Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: hsr: Fix PRP duplicate detection

Add PRP specific function for handling duplicate
packets. This is needed because of potential
L2 802.1p prioritization done by network switches.

The L2 prioritization can re-order the PRP packets
from a node causing the existing implementation to
discard the frame(s) that have been received 'late'
because the sequence number is before the previous
received packet. This can happen if the node is
sending multiple frames back-to-back with different
priority.

Signed-off-by: Jaakko Karrenpalo <jkarrenpalo@gmail.com>
Reviewed-by: Simon Horman <horms@kernel.org>
Link: https://patch.msgid.link/20250307161700.1045-1-jkarrenpalo@gmail.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>

authored by

Jaakko Karrenpalo and committed by
Paolo Abeni
05fd00e5 676cc91e

+104 -7
+2
net/hsr/hsr_device.c
··· 616 616 .drop_frame = hsr_drop_frame, 617 617 .fill_frame_info = hsr_fill_frame_info, 618 618 .invalid_dan_ingress_frame = hsr_invalid_dan_ingress_frame, 619 + .register_frame_out = hsr_register_frame_out, 619 620 }; 620 621 621 622 static struct hsr_proto_ops prp_ops = { ··· 627 626 .fill_frame_info = prp_fill_frame_info, 628 627 .handle_san_frame = prp_handle_san_frame, 629 628 .update_san_info = prp_update_san_info, 629 + .register_frame_out = prp_register_frame_out, 630 630 }; 631 631 632 632 void hsr_dev_setup(struct net_device *dev)
+2 -2
net/hsr/hsr_forward.c
··· 536 536 * Also for SAN, this shouldn't be done. 537 537 */ 538 538 if (!frame->is_from_san && 539 - hsr_register_frame_out(port, frame->node_src, 540 - frame->sequence_nr)) 539 + hsr->proto_ops->register_frame_out && 540 + hsr->proto_ops->register_frame_out(port, frame)) 541 541 continue; 542 542 543 543 if (frame->is_supervision && port->type == HSR_PT_MASTER &&
+92 -3
net/hsr/hsr_framereg.c
··· 35 35 36 36 #define seq_nr_before(a, b) seq_nr_after((b), (a)) 37 37 #define seq_nr_before_or_eq(a, b) (!seq_nr_after((a), (b))) 38 + #define PRP_DROP_WINDOW_LEN 32768 38 39 39 40 bool hsr_addr_is_redbox(struct hsr_priv *hsr, unsigned char *addr) 40 41 { ··· 177 176 new_node->time_in[i] = now; 178 177 new_node->time_out[i] = now; 179 178 } 180 - for (i = 0; i < HSR_PT_PORTS; i++) 179 + for (i = 0; i < HSR_PT_PORTS; i++) { 181 180 new_node->seq_out[i] = seq_out; 181 + new_node->seq_expected[i] = seq_out + 1; 182 + new_node->seq_start[i] = seq_out + 1; 183 + } 182 184 183 185 if (san && hsr->proto_ops->handle_san_frame) 184 186 hsr->proto_ops->handle_san_frame(san, rx_port, new_node); ··· 486 482 * 0 otherwise, or 487 483 * negative error code on error 488 484 */ 489 - int hsr_register_frame_out(struct hsr_port *port, struct hsr_node *node, 490 - u16 sequence_nr) 485 + int hsr_register_frame_out(struct hsr_port *port, struct hsr_frame_info *frame) 491 486 { 487 + struct hsr_node *node = frame->node_src; 488 + u16 sequence_nr = frame->sequence_nr; 489 + 492 490 spin_lock_bh(&node->seq_out_lock); 493 491 if (seq_nr_before_or_eq(sequence_nr, node->seq_out[port->type]) && 494 492 time_is_after_jiffies(node->time_out[port->type] + ··· 498 492 spin_unlock_bh(&node->seq_out_lock); 499 493 return 1; 500 494 } 495 + 496 + node->time_out[port->type] = jiffies; 497 + node->seq_out[port->type] = sequence_nr; 498 + spin_unlock_bh(&node->seq_out_lock); 499 + return 0; 500 + } 501 + 502 + /* Adaptation of the PRP duplicate discard algorithm described in wireshark 503 + * wiki (https://wiki.wireshark.org/PRP) 504 + * 505 + * A drop window is maintained for both LANs with start sequence set to the 506 + * first sequence accepted on the LAN that has not been seen on the other LAN, 507 + * and expected sequence set to the latest received sequence number plus one. 508 + * 509 + * When a frame is received on either LAN it is compared against the received 510 + * frames on the other LAN. If it is outside the drop window of the other LAN 511 + * the frame is accepted and the drop window is updated. 512 + * The drop window for the other LAN is reset. 513 + * 514 + * 'port' is the outgoing interface 515 + * 'frame' is the frame to be sent 516 + * 517 + * Return: 518 + * 1 if frame can be shown to have been sent recently on this interface, 519 + * 0 otherwise 520 + */ 521 + int prp_register_frame_out(struct hsr_port *port, struct hsr_frame_info *frame) 522 + { 523 + enum hsr_port_type other_port; 524 + enum hsr_port_type rcv_port; 525 + struct hsr_node *node; 526 + u16 sequence_diff; 527 + u16 sequence_exp; 528 + u16 sequence_nr; 529 + 530 + /* out-going frames are always in order 531 + * and can be checked the same way as for HSR 532 + */ 533 + if (frame->port_rcv->type == HSR_PT_MASTER) 534 + return hsr_register_frame_out(port, frame); 535 + 536 + /* for PRP we should only forward frames from the slave ports 537 + * to the master port 538 + */ 539 + if (port->type != HSR_PT_MASTER) 540 + return 1; 541 + 542 + node = frame->node_src; 543 + sequence_nr = frame->sequence_nr; 544 + sequence_exp = sequence_nr + 1; 545 + rcv_port = frame->port_rcv->type; 546 + other_port = rcv_port == HSR_PT_SLAVE_A ? HSR_PT_SLAVE_B : 547 + HSR_PT_SLAVE_A; 548 + 549 + spin_lock_bh(&node->seq_out_lock); 550 + if (time_is_before_jiffies(node->time_out[port->type] + 551 + msecs_to_jiffies(HSR_ENTRY_FORGET_TIME)) || 552 + (node->seq_start[rcv_port] == node->seq_expected[rcv_port] && 553 + node->seq_start[other_port] == node->seq_expected[other_port])) { 554 + /* the node hasn't been sending for a while 555 + * or both drop windows are empty, forward the frame 556 + */ 557 + node->seq_start[rcv_port] = sequence_nr; 558 + } else if (seq_nr_before(sequence_nr, node->seq_expected[other_port]) && 559 + seq_nr_before_or_eq(node->seq_start[other_port], sequence_nr)) { 560 + /* drop the frame, update the drop window for the other port 561 + * and reset our drop window 562 + */ 563 + node->seq_start[other_port] = sequence_exp; 564 + node->seq_expected[rcv_port] = sequence_exp; 565 + node->seq_start[rcv_port] = node->seq_expected[rcv_port]; 566 + spin_unlock_bh(&node->seq_out_lock); 567 + return 1; 568 + } 569 + 570 + /* update the drop window for the port where this frame was received 571 + * and clear the drop window for the other port 572 + */ 573 + node->seq_start[other_port] = node->seq_expected[other_port]; 574 + node->seq_expected[rcv_port] = sequence_exp; 575 + sequence_diff = sequence_exp - node->seq_start[rcv_port]; 576 + if (sequence_diff > PRP_DROP_WINDOW_LEN) 577 + node->seq_start[rcv_port] = sequence_exp - PRP_DROP_WINDOW_LEN; 501 578 502 579 node->time_out[port->type] = jiffies; 503 580 node->seq_out[port->type] = sequence_nr;
+6 -2
net/hsr/hsr_framereg.h
··· 44 44 45 45 void hsr_register_frame_in(struct hsr_node *node, struct hsr_port *port, 46 46 u16 sequence_nr); 47 - int hsr_register_frame_out(struct hsr_port *port, struct hsr_node *node, 48 - u16 sequence_nr); 47 + int hsr_register_frame_out(struct hsr_port *port, struct hsr_frame_info *frame); 49 48 50 49 void hsr_prune_nodes(struct timer_list *t); 51 50 void hsr_prune_proxy_nodes(struct timer_list *t); ··· 72 73 bool hsr_is_node_in_db(struct list_head *node_db, 73 74 const unsigned char addr[ETH_ALEN]); 74 75 76 + int prp_register_frame_out(struct hsr_port *port, struct hsr_frame_info *frame); 77 + 75 78 struct hsr_node { 76 79 struct list_head mac_list; 77 80 /* Protect R/W access to seq_out */ ··· 90 89 bool san_b; 91 90 u16 seq_out[HSR_PT_PORTS]; 92 91 bool removed; 92 + /* PRP specific duplicate handling */ 93 + u16 seq_expected[HSR_PT_PORTS]; 94 + u16 seq_start[HSR_PT_PORTS]; 93 95 struct rcu_head rcu_head; 94 96 }; 95 97
+2
net/hsr/hsr_main.h
··· 175 175 struct hsr_frame_info *frame); 176 176 bool (*invalid_dan_ingress_frame)(__be16 protocol); 177 177 void (*update_san_info)(struct hsr_node *node, bool is_sup); 178 + int (*register_frame_out)(struct hsr_port *port, 179 + struct hsr_frame_info *frame); 178 180 }; 179 181 180 182 struct hsr_self_node {