summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--net/hsr/hsr_framereg.c216
-rw-r--r--net/hsr/hsr_framereg.h20
-rw-r--r--net/hsr/prp_dup_discard_test.c38
3 files changed, 131 insertions, 143 deletions
diff --git a/net/hsr/hsr_framereg.c b/net/hsr/hsr_framereg.c
index 7e21e607efc6..7d87f304ded4 100644
--- a/net/hsr/hsr_framereg.c
+++ b/net/hsr/hsr_framereg.c
@@ -20,23 +20,6 @@
#include "hsr_framereg.h"
#include "hsr_netlink.h"
-/* seq_nr_after(a, b) - return true if a is after (higher in sequence than) b,
- * false otherwise.
- */
-static bool seq_nr_after(u16 a, u16 b)
-{
- /* Remove inconsistency where
- * seq_nr_after(a, b) == seq_nr_before(a, b)
- */
- if ((int)b - a == 32768)
- return false;
-
- return (((s16)(b - a)) < 0);
-}
-
-#define seq_nr_before(a, b) seq_nr_after((b), (a))
-#define seq_nr_before_or_eq(a, b) (!seq_nr_after((a), (b)))
-
bool hsr_addr_is_redbox(struct hsr_priv *hsr, unsigned char *addr)
{
if (!hsr->redbox || !is_valid_ether_addr(hsr->macaddress_redbox))
@@ -164,18 +147,16 @@ void prp_handle_san_frame(bool san, enum hsr_port_type port,
node->san_b = true;
}
-/* Allocate an hsr_node and add it to node_db. 'addr' is the node's address_A;
- * seq_out is used to initialize filtering of outgoing duplicate frames
- * originating from the newly added node.
+/* Allocate an hsr_node and add it to node_db. 'addr' is the node's address_A.
*/
static struct hsr_node *hsr_add_node(struct hsr_priv *hsr,
struct list_head *node_db,
- unsigned char addr[],
- u16 seq_out, bool san,
+ unsigned char addr[], bool san,
enum hsr_port_type rx_port)
{
struct hsr_node *new_node, *node = NULL;
unsigned long now;
+ size_t block_sz;
int i;
new_node = kzalloc(sizeof(*new_node), GFP_ATOMIC);
@@ -185,9 +166,13 @@ static struct hsr_node *hsr_add_node(struct hsr_priv *hsr,
ether_addr_copy(new_node->macaddress_A, addr);
spin_lock_init(&new_node->seq_out_lock);
- new_node->block_buf = kcalloc(HSR_MAX_SEQ_BLOCKS,
- sizeof(struct hsr_seq_block),
- GFP_ATOMIC);
+ if (hsr->prot_version == PRP_V1)
+ new_node->seq_port_cnt = 1;
+ else
+ new_node->seq_port_cnt = HSR_PT_PORTS - 1;
+
+ block_sz = hsr_seq_block_size(new_node);
+ new_node->block_buf = kcalloc(HSR_MAX_SEQ_BLOCKS, block_sz, GFP_ATOMIC);
if (!new_node->block_buf)
goto free;
@@ -199,8 +184,6 @@ static struct hsr_node *hsr_add_node(struct hsr_priv *hsr,
now = jiffies;
for (i = 0; i < HSR_PT_PORTS; i++) {
new_node->time_in[i] = now;
- new_node->time_out[i] = now;
- new_node->seq_out[i] = seq_out;
}
if (san && hsr->proto_ops->handle_san_frame)
@@ -245,7 +228,6 @@ struct hsr_node *hsr_get_node(struct hsr_port *port, struct list_head *node_db,
struct ethhdr *ethhdr;
struct prp_rct *rct;
bool san = false;
- u16 seq_out;
if (!skb_mac_header_was_set(skb))
return NULL;
@@ -282,24 +264,13 @@ struct hsr_node *hsr_get_node(struct hsr_port *port, struct list_head *node_db,
/* Check if skb contains hsr_ethhdr */
if (skb->mac_len < sizeof(struct hsr_ethhdr))
return NULL;
-
- /* Use the existing sequence_nr from the tag as starting point
- * for filtering duplicate frames.
- */
- seq_out = hsr_get_skb_sequence_nr(skb) - 1;
} else {
rct = skb_get_PRP_rct(skb);
- if (rct && prp_check_lsdu_size(skb, rct, is_sup)) {
- seq_out = prp_get_skb_sequence_nr(rct);
- } else {
- if (rx_port != HSR_PT_MASTER)
- san = true;
- seq_out = HSR_SEQNR_START;
- }
+ if (!rct && rx_port != HSR_PT_MASTER)
+ san = true;
}
- return hsr_add_node(hsr, node_db, ethhdr->h_source, seq_out,
- san, rx_port);
+ return hsr_add_node(hsr, node_db, ethhdr->h_source, san, rx_port);
}
static bool hsr_seq_block_is_old(struct hsr_seq_block *block)
@@ -328,6 +299,7 @@ VISIBLE_IF_KUNIT struct hsr_seq_block *hsr_get_seq_block(struct hsr_node *node,
u16 block_idx)
{
struct hsr_seq_block *block, *res;
+ size_t block_sz;
block = xa_load(&node->seq_blocks, block_idx);
@@ -337,10 +309,11 @@ VISIBLE_IF_KUNIT struct hsr_seq_block *hsr_get_seq_block(struct hsr_node *node,
}
if (!block) {
- block = &node->block_buf[node->next_block];
+ block_sz = hsr_seq_block_size(node);
+ block = node->block_buf + node->next_block * block_sz;
hsr_forget_seq_block(node, block);
- memset(block, 0, sizeof(*block));
+ memset(block, 0, block_sz);
block->time = jiffies;
block->block_idx = block_idx;
@@ -420,8 +393,7 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame)
if (!node_real)
/* No frame received from AddrA of this node yet */
node_real = hsr_add_node(hsr, node_db, hsr_sp->macaddress_A,
- HSR_SEQNR_START - 1, true,
- port_rcv->type);
+ true, port_rcv->type);
if (!node_real)
goto done; /* No mem */
if (node_real == node_curr)
@@ -468,8 +440,6 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame)
node_real->time_in_stale[i] =
node_curr->time_in_stale[i];
}
- if (seq_nr_after(node_curr->seq_out[i], node_real->seq_out[i]))
- node_real->seq_out[i] = node_curr->seq_out[i];
}
xa_for_each(&node_curr->seq_blocks, idx, src_blk) {
@@ -480,8 +450,10 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame)
if (!merge_blk)
continue;
merge_blk->time = min(merge_blk->time, src_blk->time);
- bitmap_or(merge_blk->seq_nrs, merge_blk->seq_nrs,
- src_blk->seq_nrs, HSR_SEQ_BLOCK_SIZE);
+ for (i = 0; i < node_real->seq_port_cnt; i++) {
+ bitmap_or(merge_blk->seq_nrs[i], merge_blk->seq_nrs[i],
+ src_blk->seq_nrs[i], HSR_SEQ_BLOCK_SIZE);
+ }
}
spin_unlock_bh(&node_real->seq_out_lock);
node_real->addr_B_port = port_rcv->type;
@@ -558,60 +530,28 @@ void hsr_addr_subst_dest(struct hsr_node *node_src, struct sk_buff *skb,
void hsr_register_frame_in(struct hsr_node *node, struct hsr_port *port,
u16 sequence_nr)
{
- /* Don't register incoming frames without a valid sequence number. This
- * ensures entries of restarted nodes gets pruned so that they can
- * re-register and resume communications.
- */
- if (!(port->dev->features & NETIF_F_HW_HSR_TAG_RM) &&
- seq_nr_before(sequence_nr, node->seq_out[port->type]))
- return;
-
node->time_in[port->type] = jiffies;
node->time_in_stale[port->type] = false;
}
-/* 'skb' is a HSR Ethernet frame (with a HSR tag inserted), with a valid
- * ethhdr->h_source address and skb->mac_header set.
- *
- * Return:
- * 1 if frame can be shown to have been sent recently on this interface,
- * 0 otherwise, or
- * negative error code on error
- */
-int hsr_register_frame_out(struct hsr_port *port, struct hsr_frame_info *frame)
-{
- struct hsr_node *node = frame->node_src;
- u16 sequence_nr = frame->sequence_nr;
-
- spin_lock_bh(&node->seq_out_lock);
- if (seq_nr_before_or_eq(sequence_nr, node->seq_out[port->type]) &&
- time_is_after_jiffies(node->time_out[port->type] +
- msecs_to_jiffies(HSR_ENTRY_FORGET_TIME))) {
- spin_unlock_bh(&node->seq_out_lock);
- return 1;
- }
-
- node->time_out[port->type] = jiffies;
- node->seq_out[port->type] = sequence_nr;
- spin_unlock_bh(&node->seq_out_lock);
- return 0;
-}
-
-/* PRP duplicate discard algorithm: we maintain a bitmap where we set a bit for
+/* Duplicate discard algorithm: we maintain a bitmap where we set a bit for
* every seen sequence number. The bitmap is split into blocks and the block
* management is detailed in hsr_get_seq_block(). In any case, we err on the
* side of accepting a packet, as the specification requires the algorithm to
* be "designed such that it never rejects a legitimate frame, while occasional
* acceptance of a duplicate can be tolerated." (IEC 62439-3:2021, 4.1.10.3).
+ * While this requirement is explicit for PRP, applying it to HSR does no harm
+ * either.
*
- * 'port' is the outgoing interface
* 'frame' is the frame to be sent
+ * 'port_type' is the type of the outgoing interface
*
* Return:
* 1 if frame can be shown to have been sent recently on this interface,
* 0 otherwise
*/
-int prp_register_frame_out(struct hsr_port *port, struct hsr_frame_info *frame)
+static int hsr_check_duplicate(struct hsr_frame_info *frame,
+ unsigned int port_type)
{
u16 sequence_nr, seq_bit, block_idx;
struct hsr_seq_block *block;
@@ -620,20 +560,8 @@ int prp_register_frame_out(struct hsr_port *port, struct hsr_frame_info *frame)
node = frame->node_src;
sequence_nr = frame->sequence_nr;
- /* out-going frames are always in order */
- if (frame->port_rcv->type == HSR_PT_MASTER) {
- spin_lock_bh(&node->seq_out_lock);
- node->time_out[port->type] = jiffies;
- node->seq_out[port->type] = sequence_nr;
- spin_unlock_bh(&node->seq_out_lock);
+ if (WARN_ON_ONCE(port_type >= node->seq_port_cnt))
return 0;
- }
-
- /* for PRP we should only forward frames from the slave ports
- * to the master port
- */
- if (port->type != HSR_PT_MASTER)
- return 1;
spin_lock_bh(&node->seq_out_lock);
@@ -643,11 +571,9 @@ int prp_register_frame_out(struct hsr_port *port, struct hsr_frame_info *frame)
goto out_new;
seq_bit = hsr_seq_block_bit(sequence_nr);
- if (__test_and_set_bit(seq_bit, block->seq_nrs))
+ if (__test_and_set_bit(seq_bit, block->seq_nrs[port_type]))
goto out_seen;
- node->time_out[port->type] = jiffies;
- node->seq_out[port->type] = sequence_nr;
out_new:
spin_unlock_bh(&node->seq_out_lock);
return 0;
@@ -656,6 +582,49 @@ out_seen:
spin_unlock_bh(&node->seq_out_lock);
return 1;
}
+
+/* HSR duplicate discard: we check if the same frame has already been sent on
+ * this outgoing interface. The check follows the general duplicate discard
+ * algorithm.
+ *
+ * 'port' is the outgoing interface
+ * 'frame' is the frame to be sent
+ *
+ * Return:
+ * 1 if frame can be shown to have been sent recently on this interface,
+ * 0 otherwise
+ */
+int hsr_register_frame_out(struct hsr_port *port, struct hsr_frame_info *frame)
+{
+ return hsr_check_duplicate(frame, port->type - 1);
+}
+
+/* PRP duplicate discard: we only consider frames that are received on port A
+ * or port B and should go to the master port. For those, we check if they have
+ * already been received by the host, i.e., master port. The check uses the
+ * general duplicate discard algorithm, but without tracking multiple ports.
+ *
+ * 'port' is the outgoing interface
+ * 'frame' is the frame to be sent
+ *
+ * Return:
+ * 1 if frame can be shown to have been sent recently on this interface,
+ * 0 otherwise
+ */
+int prp_register_frame_out(struct hsr_port *port, struct hsr_frame_info *frame)
+{
+ /* out-going frames are always in order */
+ if (frame->port_rcv->type == HSR_PT_MASTER)
+ return 0;
+
+ /* for PRP we should only forward frames from the slave ports
+ * to the master port
+ */
+ if (port->type != HSR_PT_MASTER)
+ return 1;
+
+ return hsr_check_duplicate(frame, 0);
+}
EXPORT_SYMBOL_IF_KUNIT(prp_register_frame_out);
static struct hsr_port *get_late_port(struct hsr_priv *hsr,
@@ -806,6 +775,39 @@ void *hsr_get_next_node(struct hsr_priv *hsr, void *_pos,
return NULL;
}
+/* Fill the last sequence number that has been received from node on if1 by
+ * finding the last sequence number sent on port B; accordingly get the last
+ * received sequence number for if2 using sent sequence numbers on port A.
+ */
+static void fill_last_seq_nrs(struct hsr_node *node, u16 *if1_seq, u16 *if2_seq)
+{
+ struct hsr_seq_block *block;
+ unsigned int block_off;
+ size_t block_sz;
+ u16 seq_bit;
+
+ spin_lock_bh(&node->seq_out_lock);
+
+ /* Get last inserted block */
+ block_off = (node->next_block - 1) & (HSR_MAX_SEQ_BLOCKS - 1);
+ block_sz = hsr_seq_block_size(node);
+ block = node->block_buf + block_off * block_sz;
+
+ if (!bitmap_empty(block->seq_nrs[HSR_PT_SLAVE_B - 1],
+ HSR_SEQ_BLOCK_SIZE)) {
+ seq_bit = find_last_bit(block->seq_nrs[HSR_PT_SLAVE_B - 1],
+ HSR_SEQ_BLOCK_SIZE);
+ *if1_seq = (block->block_idx << HSR_SEQ_BLOCK_SHIFT) | seq_bit;
+ }
+ if (!bitmap_empty(block->seq_nrs[HSR_PT_SLAVE_A - 1],
+ HSR_SEQ_BLOCK_SIZE)) {
+ seq_bit = find_last_bit(block->seq_nrs[HSR_PT_SLAVE_A - 1],
+ HSR_SEQ_BLOCK_SIZE);
+ *if2_seq = (block->block_idx << HSR_SEQ_BLOCK_SHIFT) | seq_bit;
+ }
+ spin_unlock_bh(&node->seq_out_lock);
+}
+
int hsr_get_node_data(struct hsr_priv *hsr,
const unsigned char *addr,
unsigned char addr_b[ETH_ALEN],
@@ -846,8 +848,10 @@ int hsr_get_node_data(struct hsr_priv *hsr,
*if2_age = jiffies_to_msecs(tdiff);
/* Present sequence numbers as if they were incoming on interface */
- *if1_seq = node->seq_out[HSR_PT_SLAVE_B];
- *if2_seq = node->seq_out[HSR_PT_SLAVE_A];
+ *if1_seq = 0;
+ *if2_seq = 0;
+ if (hsr->prot_version != PRP_V1)
+ fill_last_seq_nrs(node, if1_seq, if2_seq);
if (node->addr_B_port != HSR_PT_NONE) {
port = hsr_port_get_hsr(hsr, node->addr_B_port);
diff --git a/net/hsr/hsr_framereg.h b/net/hsr/hsr_framereg.h
index 686f2a595400..c65ecb925734 100644
--- a/net/hsr/hsr_framereg.h
+++ b/net/hsr/hsr_framereg.h
@@ -89,12 +89,15 @@ struct hsr_seq_block *hsr_get_seq_block(struct hsr_node *node, u16 block_idx);
struct hsr_seq_block {
unsigned long time;
u16 block_idx;
- DECLARE_BITMAP(seq_nrs, HSR_SEQ_BLOCK_SIZE);
+ /* Should be a flexible array member of what DECLARE_BITMAP() would
+ * produce.
+ */
+ unsigned long seq_nrs[][BITS_TO_LONGS(HSR_SEQ_BLOCK_SIZE)];
};
struct hsr_node {
struct list_head mac_list;
- /* Protect R/W access to seq_out and seq_blocks */
+ /* Protect R/W access seq_blocks */
spinlock_t seq_out_lock;
unsigned char macaddress_A[ETH_ALEN];
unsigned char macaddress_B[ETH_ALEN];
@@ -102,17 +105,22 @@ struct hsr_node {
enum hsr_port_type addr_B_port;
unsigned long time_in[HSR_PT_PORTS];
bool time_in_stale[HSR_PT_PORTS];
- unsigned long time_out[HSR_PT_PORTS];
/* if the node is a SAN */
bool san_a;
bool san_b;
- u16 seq_out[HSR_PT_PORTS];
bool removed;
- /* PRP specific duplicate handling */
+ /* Duplicate detection */
struct xarray seq_blocks;
- struct hsr_seq_block *block_buf;
+ void *block_buf;
unsigned int next_block;
+ unsigned int seq_port_cnt;
struct rcu_head rcu_head;
};
+static inline size_t hsr_seq_block_size(struct hsr_node *node)
+{
+ WARN_ON_ONCE(node->seq_port_cnt == 0);
+ return struct_size_t(struct hsr_seq_block, seq_nrs, node->seq_port_cnt);
+}
+
#endif /* __HSR_FRAMEREG_H */
diff --git a/net/hsr/prp_dup_discard_test.c b/net/hsr/prp_dup_discard_test.c
index bfa3d318ec04..b028e71e6a0f 100644
--- a/net/hsr/prp_dup_discard_test.c
+++ b/net/hsr/prp_dup_discard_test.c
@@ -15,12 +15,15 @@ struct prp_test_data {
static struct prp_test_data *build_prp_test_data(struct kunit *test)
{
+ size_t block_sz;
+
struct prp_test_data *data = kunit_kzalloc(test,
sizeof(struct prp_test_data), GFP_USER);
KUNIT_EXPECT_NOT_ERR_OR_NULL(test, data);
- data->node.block_buf = kunit_kcalloc(test, HSR_MAX_SEQ_BLOCKS,
- sizeof(struct hsr_seq_block),
+ data->node.seq_port_cnt = 1;
+ block_sz = hsr_seq_block_size(&data->node);
+ data->node.block_buf = kunit_kcalloc(test, HSR_MAX_SEQ_BLOCKS, block_sz,
GFP_ATOMIC);
KUNIT_EXPECT_NOT_ERR_OR_NULL(test, data->node.block_buf);
@@ -30,8 +33,6 @@ static struct prp_test_data *build_prp_test_data(struct kunit *test)
data->frame.node_src = &data->node;
data->frame.port_rcv = &data->port_rcv;
data->port_rcv.type = HSR_PT_SLAVE_A;
- data->node.seq_out[HSR_PT_MASTER] = 0;
- data->node.time_out[HSR_PT_MASTER] = jiffies;
data->port.type = HSR_PT_MASTER;
return data;
@@ -48,7 +49,7 @@ static void check_prp_frame_seen(struct kunit *test, struct prp_test_data *data,
KUNIT_EXPECT_NOT_NULL(test, block);
seq_bit = sequence_nr & HSR_SEQ_BLOCK_MASK;
- KUNIT_EXPECT_TRUE(test, test_bit(seq_bit, block->seq_nrs));
+ KUNIT_EXPECT_TRUE(test, test_bit(seq_bit, block->seq_nrs[0]));
}
static void check_prp_frame_unseen(struct kunit *test,
@@ -62,7 +63,7 @@ static void check_prp_frame_unseen(struct kunit *test,
KUNIT_EXPECT_NOT_NULL(test, block);
seq_bit = sequence_nr & HSR_SEQ_BLOCK_MASK;
- KUNIT_EXPECT_FALSE(test, test_bit(seq_bit, block->seq_nrs));
+ KUNIT_EXPECT_FALSE(test, test_bit(seq_bit, block->seq_nrs[0]));
}
static void prp_dup_discard_forward(struct kunit *test)
@@ -73,30 +74,20 @@ static void prp_dup_discard_forward(struct kunit *test)
data->frame.sequence_nr = 2;
KUNIT_EXPECT_EQ(test, 0,
prp_register_frame_out(&data->port, &data->frame));
- KUNIT_EXPECT_EQ(test, data->frame.sequence_nr,
- data->node.seq_out[HSR_PT_MASTER]);
- KUNIT_EXPECT_EQ(test, jiffies, data->node.time_out[HSR_PT_MASTER]);
check_prp_frame_seen(test, data, data->frame.sequence_nr);
}
static void prp_dup_discard_drop_duplicate(struct kunit *test)
{
struct prp_test_data *data = build_prp_test_data(test);
- unsigned long time = jiffies - 10;
data->frame.sequence_nr = 2;
KUNIT_EXPECT_EQ(test, 0,
prp_register_frame_out(&data->port, &data->frame));
- KUNIT_EXPECT_EQ(test, data->frame.sequence_nr,
- data->node.seq_out[HSR_PT_MASTER]);
check_prp_frame_seen(test, data, data->frame.sequence_nr);
- data->node.time_out[HSR_PT_MASTER] = time;
KUNIT_EXPECT_EQ(test, 1,
prp_register_frame_out(&data->port, &data->frame));
- KUNIT_EXPECT_EQ(test, data->frame.sequence_nr,
- data->node.seq_out[HSR_PT_MASTER]);
- KUNIT_EXPECT_EQ(test, time, data->node.time_out[HSR_PT_MASTER]);
check_prp_frame_seen(test, data, data->frame.sequence_nr);
}
@@ -123,9 +114,6 @@ static void prp_dup_discard_entry_timeout(struct kunit *test)
KUNIT_EXPECT_EQ(test, 0,
prp_register_frame_out(&data->port, &data->frame));
- KUNIT_EXPECT_EQ(test, data->frame.sequence_nr,
- data->node.seq_out[HSR_PT_MASTER]);
- KUNIT_EXPECT_EQ(test, jiffies, data->node.time_out[HSR_PT_MASTER]);
check_prp_frame_seen(test, data, data->frame.sequence_nr);
check_prp_frame_unseen(test, data, 7);
}
@@ -139,16 +127,12 @@ static void prp_dup_discard_out_of_sequence(struct kunit *test)
data->frame.sequence_nr = 9;
KUNIT_EXPECT_EQ(test, 0,
prp_register_frame_out(&data->port, &data->frame));
- KUNIT_EXPECT_EQ(test, data->frame.sequence_nr,
- data->node.seq_out[HSR_PT_MASTER]);
check_prp_frame_seen(test, data, data->frame.sequence_nr);
/* 1st old frame, should be accepted */
data->frame.sequence_nr = 8;
KUNIT_EXPECT_EQ(test, 0,
prp_register_frame_out(&data->port, &data->frame));
- KUNIT_EXPECT_EQ(test, data->frame.sequence_nr,
- data->node.seq_out[HSR_PT_MASTER]);
check_prp_frame_seen(test, data, data->frame.sequence_nr);
/* 2nd frame should be dropped */
@@ -162,8 +146,6 @@ static void prp_dup_discard_out_of_sequence(struct kunit *test)
data->port_rcv.type = HSR_PT_SLAVE_A;
KUNIT_EXPECT_EQ(test, 0,
prp_register_frame_out(&data->port, &data->frame));
- KUNIT_EXPECT_EQ(test, data->frame.sequence_nr,
- data->node.seq_out[HSR_PT_MASTER]);
check_prp_frame_seen(test, data, data->frame.sequence_nr);
/* and next one is dropped */
@@ -178,20 +160,14 @@ static void prp_dup_discard_lan_b_late(struct kunit *test)
/* LAN B is behind */
struct prp_test_data *data = build_prp_test_data(test);
- data->node.seq_out[HSR_PT_MASTER] = 8;
-
data->frame.sequence_nr = 9;
KUNIT_EXPECT_EQ(test, 0,
prp_register_frame_out(&data->port, &data->frame));
- KUNIT_EXPECT_EQ(test, data->frame.sequence_nr,
- data->node.seq_out[HSR_PT_MASTER]);
check_prp_frame_seen(test, data, data->frame.sequence_nr);
data->frame.sequence_nr = 10;
KUNIT_EXPECT_EQ(test, 0,
prp_register_frame_out(&data->port, &data->frame));
- KUNIT_EXPECT_EQ(test, data->frame.sequence_nr,
- data->node.seq_out[HSR_PT_MASTER]);
check_prp_frame_seen(test, data, data->frame.sequence_nr);
data->frame.sequence_nr = 9;