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

net: hsr: Provide RedBox support (HSR-SAN)

Introduce RedBox support (HSR-SAN to be more precise) for HSR networks.
Following traffic reduction optimizations have been implemented:
- Do not send HSR supervisory frames to Port C (interlink)
- Do not forward to HSR ring frames addressed to Port C
- Do not forward to Port C frames from HSR ring
- Do not send duplicate HSR frame to HSR ring when destination is Port C

The corresponding patch to modify iptable2 sources has already been sent:
https://lore.kernel.org/netdev/20240308145729.490863-1-lukma@denx.de/T/

Testing procedure (veth and netns):
-----------------------------------
One shall run:
linux-vanila/tools/testing/selftests/net/hsr/hsr_redbox.sh
(Detailed description of the setup one can find in the test
script file).

Testing procedure (real hardware):
----------------------------------
The EVB-KSZ9477 has been used for testing on net-next branch
(SHA1: 5fc68320c1fb3c7d456ddcae0b4757326a043e6f).

Ports 4/5 were used for SW managed HSR (hsr1) as first hsr0 for ports 1/2
(with HW offloading for ksz9477) was created. Port 3 has been used as
interlink port (single USB-ETH dongle).

Configuration - RedBox (EVB-KSZ9477):
if link set lan1 down;ip link set lan2 down
ip link add name hsr0 type hsr slave1 lan1 slave2 lan2 supervision 45 version 1
ip link add name hsr1 type hsr slave1 lan4 slave2 lan5 interlink lan3 supervision 45 version 1
ip link set lan4 up;ip link set lan5 up
ip link set lan3 up
ip addr add 192.168.0.11/24 dev hsr1
ip link set hsr1 up

Configuration - DAN-H (EVB-KSZ9477):

ip link set lan1 down;ip link set lan2 down
ip link add name hsr0 type hsr slave1 lan1 slave2 lan2 supervision 45 version 1
ip link add name hsr1 type hsr slave1 lan4 slave2 lan5 supervision 45 version 1
ip link set lan4 up;ip link set lan5 up
ip addr add 192.168.0.12/24 dev hsr1
ip link set hsr1 up

This approach uses only SW based HSR devices (hsr1).

-------------- ----------------- ------------
DAN-H Port5 | <------> | Port5 | |
Port4 | <------> | Port4 Port3 | <---> | PC
| | (RedBox) | | (USB-ETH)
EVB-KSZ9477 | | EVB-KSZ9477 | |
-------------- ----------------- ------------

Signed-off-by: Lukasz Majewski <lukma@denx.de>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>

authored by

Lukasz Majewski and committed by
Paolo Abeni
5055cccf af0cb3fa

+204 -16
+1
include/uapi/linux/if_link.h
··· 1771 1771 IFLA_HSR_PROTOCOL, /* Indicate different protocol than 1772 1772 * HSR. For example PRP. 1773 1773 */ 1774 + IFLA_HSR_INTERLINK, /* HSR interlink network device */ 1774 1775 __IFLA_HSR_MAX, 1775 1776 }; 1776 1777
+34 -2
net/hsr/hsr_device.c
··· 146 146 case HSR_PT_SLAVE_B: 147 147 designation = "Slave B"; 148 148 break; 149 + case HSR_PT_INTERLINK: 150 + designation = "Interlink"; 151 + break; 149 152 default: 150 153 designation = "Unknown"; 151 154 } ··· 288 285 struct hsr_priv *hsr = master->hsr; 289 286 __u8 type = HSR_TLV_LIFE_CHECK; 290 287 struct hsr_sup_payload *hsr_sp; 288 + struct hsr_sup_tlv *hsr_stlv; 291 289 struct hsr_sup_tag *hsr_stag; 292 290 struct sk_buff *skb; 293 291 ··· 327 323 /* Payload: MacAddressA */ 328 324 hsr_sp = skb_put(skb, sizeof(struct hsr_sup_payload)); 329 325 ether_addr_copy(hsr_sp->macaddress_A, master->dev->dev_addr); 326 + 327 + if (hsr->redbox) { 328 + hsr_stlv = skb_put(skb, sizeof(struct hsr_sup_tlv)); 329 + hsr_stlv->HSR_TLV_type = PRP_TLV_REDBOX_MAC; 330 + hsr_stlv->HSR_TLV_length = sizeof(struct hsr_sup_payload); 331 + 332 + /* Payload: MacAddressRedBox */ 333 + hsr_sp = skb_put(skb, sizeof(struct hsr_sup_payload)); 334 + ether_addr_copy(hsr_sp->macaddress_A, hsr->macaddress_redbox); 335 + } 330 336 331 337 if (skb_put_padto(skb, ETH_ZLEN)) { 332 338 spin_unlock_bh(&hsr->seqnr_lock); ··· 416 402 hsr_del_port(port); 417 403 418 404 port = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_B); 405 + if (port) 406 + hsr_del_port(port); 407 + 408 + port = hsr_port_get_hsr(hsr, HSR_PT_INTERLINK); 419 409 if (port) 420 410 hsr_del_port(port); 421 411 ··· 552 534 }; 553 535 554 536 int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2], 555 - unsigned char multicast_spec, u8 protocol_version, 556 - struct netlink_ext_ack *extack) 537 + struct net_device *interlink, unsigned char multicast_spec, 538 + u8 protocol_version, struct netlink_ext_ack *extack) 557 539 { 558 540 bool unregister = false; 559 541 struct hsr_priv *hsr; ··· 562 544 hsr = netdev_priv(hsr_dev); 563 545 INIT_LIST_HEAD(&hsr->ports); 564 546 INIT_LIST_HEAD(&hsr->node_db); 547 + INIT_LIST_HEAD(&hsr->proxy_node_db); 565 548 spin_lock_init(&hsr->list_lock); 566 549 567 550 eth_hw_addr_set(hsr_dev, slave[0]->dev_addr); ··· 588 569 /* Overflow soon to find bugs easier: */ 589 570 hsr->sequence_nr = HSR_SEQNR_START; 590 571 hsr->sup_sequence_nr = HSR_SUP_SEQNR_START; 572 + hsr->interlink_sequence_nr = HSR_SEQNR_START; 591 573 592 574 timer_setup(&hsr->announce_timer, hsr_announce, 0); 593 575 timer_setup(&hsr->prune_timer, hsr_prune_nodes, 0); ··· 623 603 res = hsr_add_port(hsr, slave[1], HSR_PT_SLAVE_B, extack); 624 604 if (res) 625 605 goto err_unregister; 606 + 607 + if (interlink) { 608 + res = hsr_add_port(hsr, interlink, HSR_PT_INTERLINK, extack); 609 + if (res) 610 + goto err_unregister; 611 + 612 + hsr->redbox = true; 613 + ether_addr_copy(hsr->macaddress_redbox, interlink->dev_addr); 614 + timer_setup(&hsr->prune_proxy_timer, hsr_prune_proxy_nodes, 0); 615 + mod_timer(&hsr->prune_proxy_timer, 616 + jiffies + msecs_to_jiffies(PRUNE_PROXY_PERIOD)); 617 + } 626 618 627 619 hsr_debugfs_init(hsr, hsr_dev); 628 620 mod_timer(&hsr->prune_timer, jiffies + msecs_to_jiffies(PRUNE_PERIOD));
+2 -2
net/hsr/hsr_device.h
··· 16 16 void hsr_del_ports(struct hsr_priv *hsr); 17 17 void hsr_dev_setup(struct net_device *dev); 18 18 int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2], 19 - unsigned char multicast_spec, u8 protocol_version, 20 - struct netlink_ext_ack *extack); 19 + struct net_device *interlink, unsigned char multicast_spec, 20 + u8 protocol_version, struct netlink_ext_ack *extack); 21 21 void hsr_check_carrier_and_operstate(struct hsr_priv *hsr); 22 22 int hsr_get_max_mtu(struct hsr_priv *hsr); 23 23 #endif /* __HSR_DEVICE_H */
+76 -9
net/hsr/hsr_forward.c
··· 377 377 */ 378 378 ether_addr_copy(eth_hdr(skb)->h_source, port->dev->dev_addr); 379 379 } 380 + 381 + /* When HSR node is used as RedBox - the frame received from HSR ring 382 + * requires source MAC address (SA) replacement to one which can be 383 + * recognized by SAN devices (otherwise, frames are dropped by switch) 384 + */ 385 + if (port->type == HSR_PT_INTERLINK) 386 + ether_addr_copy(eth_hdr(skb)->h_source, 387 + port->hsr->macaddress_redbox); 388 + 380 389 return dev_queue_xmit(skb); 381 390 } 382 391 ··· 399 390 400 391 bool hsr_drop_frame(struct hsr_frame_info *frame, struct hsr_port *port) 401 392 { 393 + struct sk_buff *skb; 394 + 402 395 if (port->dev->features & NETIF_F_HW_HSR_FWD) 403 396 return prp_drop_frame(frame, port); 397 + 398 + /* RedBox specific frames dropping policies 399 + * 400 + * Do not send HSR supervisory frames to SAN devices 401 + */ 402 + if (frame->is_supervision && port->type == HSR_PT_INTERLINK) 403 + return true; 404 + 405 + /* Do not forward to other HSR port (A or B) unicast frames which 406 + * are addressed to interlink port (and are in the ProxyNodeTable). 407 + */ 408 + skb = frame->skb_hsr; 409 + if (skb && prp_drop_frame(frame, port) && 410 + is_unicast_ether_addr(eth_hdr(skb)->h_dest) && 411 + hsr_is_node_in_db(&port->hsr->proxy_node_db, 412 + eth_hdr(skb)->h_dest)) { 413 + return true; 414 + } 415 + 416 + /* Do not forward to port C (Interlink) frames from nodes A and B 417 + * if DA is in NodeTable. 418 + */ 419 + if ((frame->port_rcv->type == HSR_PT_SLAVE_A || 420 + frame->port_rcv->type == HSR_PT_SLAVE_B) && 421 + port->type == HSR_PT_INTERLINK) { 422 + skb = frame->skb_hsr; 423 + if (skb && is_unicast_ether_addr(eth_hdr(skb)->h_dest) && 424 + hsr_is_node_in_db(&port->hsr->node_db, 425 + eth_hdr(skb)->h_dest)) { 426 + return true; 427 + } 428 + } 429 + 430 + /* Do not forward to port A and B unicast frames received on the 431 + * interlink port if it is addressed to one of nodes registered in 432 + * the ProxyNodeTable. 433 + */ 434 + if ((port->type == HSR_PT_SLAVE_A || port->type == HSR_PT_SLAVE_B) && 435 + frame->port_rcv->type == HSR_PT_INTERLINK) { 436 + skb = frame->skb_std; 437 + if (skb && is_unicast_ether_addr(eth_hdr(skb)->h_dest) && 438 + hsr_is_node_in_db(&port->hsr->proxy_node_db, 439 + eth_hdr(skb)->h_dest)) { 440 + return true; 441 + } 442 + } 404 443 405 444 return false; 406 445 } ··· 505 448 } 506 449 507 450 /* Check if frame is to be dropped. Eg. for PRP no forward 508 - * between ports. 451 + * between ports, or sending HSR supervision to RedBox. 509 452 */ 510 453 if (hsr->proto_ops->drop_frame && 511 454 hsr->proto_ops->drop_frame(frame, port)) 512 455 continue; 513 456 514 - if (port->type != HSR_PT_MASTER) 457 + if (port->type == HSR_PT_SLAVE_A || 458 + port->type == HSR_PT_SLAVE_B) 515 459 skb = hsr->proto_ops->create_tagged_frame(frame, port); 516 460 else 517 461 skb = hsr->proto_ops->get_untagged_frame(frame, port); ··· 527 469 hsr_deliver_master(skb, port->dev, frame->node_src); 528 470 } else { 529 471 if (!hsr_xmit(skb, port, frame)) 530 - sent = true; 472 + if (port->type == HSR_PT_SLAVE_A || 473 + port->type == HSR_PT_SLAVE_B) 474 + sent = true; 531 475 } 532 476 } 533 477 } ··· 563 503 frame->skb_prp = NULL; 564 504 frame->skb_std = skb; 565 505 566 - if (port->type != HSR_PT_MASTER) { 506 + if (port->type != HSR_PT_MASTER) 567 507 frame->is_from_san = true; 568 - } else { 569 - /* Sequence nr for the master node */ 508 + 509 + if (port->type == HSR_PT_MASTER || 510 + port->type == HSR_PT_INTERLINK) { 511 + /* Sequence nr for the master/interlink node */ 570 512 lockdep_assert_held(&hsr->seqnr_lock); 571 513 frame->sequence_nr = hsr->sequence_nr; 572 514 hsr->sequence_nr++; ··· 626 564 { 627 565 struct hsr_priv *hsr = port->hsr; 628 566 struct hsr_vlan_ethhdr *vlan_hdr; 567 + struct list_head *n_db; 629 568 struct ethhdr *ethhdr; 630 569 __be16 proto; 631 570 int ret; ··· 637 574 638 575 memset(frame, 0, sizeof(*frame)); 639 576 frame->is_supervision = is_supervision_frame(port->hsr, skb); 640 - frame->node_src = hsr_get_node(port, &hsr->node_db, skb, 641 - frame->is_supervision, 642 - port->type); 577 + 578 + n_db = &hsr->node_db; 579 + if (port->type == HSR_PT_INTERLINK) 580 + n_db = &hsr->proxy_node_db; 581 + 582 + frame->node_src = hsr_get_node(port, n_db, skb, 583 + frame->is_supervision, port->type); 643 584 if (!frame->node_src) 644 585 return -1; /* Unknown node and !is_supervision, or no mem */ 645 586
+52
net/hsr/hsr_framereg.c
··· 71 71 return NULL; 72 72 } 73 73 74 + /* Check if node for a given MAC address is already present in data base 75 + */ 76 + bool hsr_is_node_in_db(struct list_head *node_db, 77 + const unsigned char addr[ETH_ALEN]) 78 + { 79 + return !!find_node_by_addr_A(node_db, addr); 80 + } 81 + 74 82 /* Helper for device init; the self_node is used in hsr_rcv() to recognize 75 83 * frames from self that's been looped over the HSR ring. 76 84 */ ··· 225 217 return node; 226 218 } 227 219 if (ether_addr_equal(node->macaddress_B, ethhdr->h_source)) { 220 + if (hsr->proto_ops->update_san_info) 221 + hsr->proto_ops->update_san_info(node, is_sup); 222 + return node; 223 + } 224 + } 225 + 226 + /* Check if required node is not in proxy nodes table */ 227 + list_for_each_entry_rcu(node, &hsr->proxy_node_db, mac_list) { 228 + if (ether_addr_equal(node->macaddress_A, ethhdr->h_source)) { 228 229 if (hsr->proto_ops->update_san_info) 229 230 hsr->proto_ops->update_san_info(node, is_sup); 230 231 return node; ··· 435 418 436 419 node_dst = find_node_by_addr_A(&port->hsr->node_db, 437 420 eth_hdr(skb)->h_dest); 421 + if (!node_dst && port->hsr->redbox) 422 + node_dst = find_node_by_addr_A(&port->hsr->proxy_node_db, 423 + eth_hdr(skb)->h_dest); 424 + 438 425 if (!node_dst) { 439 426 if (port->hsr->prot_version != PRP_V1 && net_ratelimit()) 440 427 netdev_err(skb->dev, "%s: Unknown node\n", __func__); ··· 580 559 /* Restart timer */ 581 560 mod_timer(&hsr->prune_timer, 582 561 jiffies + msecs_to_jiffies(PRUNE_PERIOD)); 562 + } 563 + 564 + void hsr_prune_proxy_nodes(struct timer_list *t) 565 + { 566 + struct hsr_priv *hsr = from_timer(hsr, t, prune_proxy_timer); 567 + unsigned long timestamp; 568 + struct hsr_node *node; 569 + struct hsr_node *tmp; 570 + 571 + spin_lock_bh(&hsr->list_lock); 572 + list_for_each_entry_safe(node, tmp, &hsr->proxy_node_db, mac_list) { 573 + timestamp = node->time_in[HSR_PT_INTERLINK]; 574 + 575 + /* Prune old entries */ 576 + if (time_is_before_jiffies(timestamp + 577 + msecs_to_jiffies(HSR_PROXY_NODE_FORGET_TIME))) { 578 + hsr_nl_nodedown(hsr, node->macaddress_A); 579 + if (!node->removed) { 580 + list_del_rcu(&node->mac_list); 581 + node->removed = true; 582 + /* Note that we need to free this entry later: */ 583 + kfree_rcu(node, rcu_head); 584 + } 585 + } 586 + } 587 + 588 + spin_unlock_bh(&hsr->list_lock); 589 + 590 + /* Restart timer */ 591 + mod_timer(&hsr->prune_proxy_timer, 592 + jiffies + msecs_to_jiffies(PRUNE_PROXY_PERIOD)); 583 593 } 584 594 585 595 void *hsr_get_next_node(struct hsr_priv *hsr, void *_pos,
+4
net/hsr/hsr_framereg.h
··· 46 46 u16 sequence_nr); 47 47 48 48 void hsr_prune_nodes(struct timer_list *t); 49 + void hsr_prune_proxy_nodes(struct timer_list *t); 49 50 50 51 int hsr_create_self_node(struct hsr_priv *hsr, 51 52 const unsigned char addr_a[ETH_ALEN], ··· 67 66 void prp_handle_san_frame(bool san, enum hsr_port_type port, 68 67 struct hsr_node *node); 69 68 void prp_update_san_info(struct hsr_node *node, bool is_sup); 69 + 70 + bool hsr_is_node_in_db(struct list_head *node_db, 71 + const unsigned char addr[ETH_ALEN]); 70 72 71 73 struct hsr_node { 72 74 struct list_head mac_list;
+7
net/hsr/hsr_main.h
··· 21 21 */ 22 22 #define HSR_LIFE_CHECK_INTERVAL 2000 /* ms */ 23 23 #define HSR_NODE_FORGET_TIME 60000 /* ms */ 24 + #define HSR_PROXY_NODE_FORGET_TIME 60000 /* ms */ 24 25 #define HSR_ANNOUNCE_INTERVAL 100 /* ms */ 25 26 #define HSR_ENTRY_FORGET_TIME 400 /* ms */ 26 27 ··· 36 35 * HSR_NODE_FORGET_TIME? 37 36 */ 38 37 #define PRUNE_PERIOD 3000 /* ms */ 38 + #define PRUNE_PROXY_PERIOD 3000 /* ms */ 39 39 #define HSR_TLV_EOT 0 /* End of TLVs */ 40 40 #define HSR_TLV_ANNOUNCE 22 41 41 #define HSR_TLV_LIFE_CHECK 23 ··· 194 192 struct rcu_head rcu_head; 195 193 struct list_head ports; 196 194 struct list_head node_db; /* Known HSR nodes */ 195 + struct list_head proxy_node_db; /* RedBox HSR proxy nodes */ 197 196 struct hsr_self_node __rcu *self_node; /* MACs of slaves */ 198 197 struct timer_list announce_timer; /* Supervision frame dispatch */ 199 198 struct timer_list prune_timer; 199 + struct timer_list prune_proxy_timer; 200 200 int announce_count; 201 201 u16 sequence_nr; 202 + u16 interlink_sequence_nr; /* Interlink port seq_nr */ 202 203 u16 sup_sequence_nr; /* For HSRv1 separate seq_nr for supervision */ 203 204 enum hsr_version prot_version; /* Indicate if HSRv0, HSRv1 or PRPv1 */ 204 205 spinlock_t seqnr_lock; /* locking for sequence_nr */ ··· 214 209 * of lan_id 215 210 */ 216 211 bool fwd_offloaded; /* Forwarding offloaded to HW */ 212 + bool redbox; /* Device supports HSR RedBox */ 213 + unsigned char macaddress_redbox[ETH_ALEN]; 217 214 unsigned char sup_multicast_addr[ETH_ALEN] __aligned(sizeof(u16)); 218 215 /* Align to u16 boundary to avoid unaligned access 219 216 * in ether_addr_equal
+27 -3
net/hsr/hsr_netlink.c
··· 23 23 [IFLA_HSR_SUPERVISION_ADDR] = { .len = ETH_ALEN }, 24 24 [IFLA_HSR_SEQ_NR] = { .type = NLA_U16 }, 25 25 [IFLA_HSR_PROTOCOL] = { .type = NLA_U8 }, 26 + [IFLA_HSR_INTERLINK] = { .type = NLA_U32 }, 26 27 }; 27 28 28 29 /* Here, it seems a netdevice has already been allocated for us, and the ··· 36 35 enum hsr_version proto_version; 37 36 unsigned char multicast_spec; 38 37 u8 proto = HSR_PROTOCOL_HSR; 39 - struct net_device *link[2]; 40 38 39 + struct net_device *link[2], *interlink = NULL; 41 40 if (!data) { 42 41 NL_SET_ERR_MSG_MOD(extack, "No slave devices specified"); 43 42 return -EINVAL; ··· 65 64 66 65 if (link[0] == link[1]) { 67 66 NL_SET_ERR_MSG_MOD(extack, "Slave1 and Slave2 are same"); 67 + return -EINVAL; 68 + } 69 + 70 + if (data[IFLA_HSR_INTERLINK]) 71 + interlink = __dev_get_by_index(src_net, 72 + nla_get_u32(data[IFLA_HSR_INTERLINK])); 73 + 74 + if (interlink && interlink == link[0]) { 75 + NL_SET_ERR_MSG_MOD(extack, "Interlink and Slave1 are the same"); 76 + return -EINVAL; 77 + } 78 + 79 + if (interlink && interlink == link[1]) { 80 + NL_SET_ERR_MSG_MOD(extack, "Interlink and Slave2 are the same"); 68 81 return -EINVAL; 69 82 } 70 83 ··· 111 96 } 112 97 } 113 98 114 - if (proto == HSR_PROTOCOL_PRP) 99 + if (proto == HSR_PROTOCOL_PRP) { 115 100 proto_version = PRP_V1; 101 + if (interlink) { 102 + NL_SET_ERR_MSG_MOD(extack, 103 + "Interlink only works with HSR"); 104 + return -EINVAL; 105 + } 106 + } 116 107 117 - return hsr_dev_finalize(dev, link, multicast_spec, proto_version, extack); 108 + return hsr_dev_finalize(dev, link, interlink, multicast_spec, 109 + proto_version, extack); 118 110 } 119 111 120 112 static void hsr_dellink(struct net_device *dev, struct list_head *head) ··· 129 107 struct hsr_priv *hsr = netdev_priv(dev); 130 108 131 109 del_timer_sync(&hsr->prune_timer); 110 + del_timer_sync(&hsr->prune_proxy_timer); 132 111 del_timer_sync(&hsr->announce_timer); 133 112 134 113 hsr_debugfs_term(hsr); ··· 137 114 138 115 hsr_del_self_node(hsr); 139 116 hsr_del_nodes(&hsr->node_db); 117 + hsr_del_nodes(&hsr->proxy_node_db); 140 118 141 119 unregister_netdevice_queue(dev, head); 142 120 }
+1
net/hsr/hsr_slave.c
··· 55 55 protocol = eth_hdr(skb)->h_proto; 56 56 57 57 if (!(port->dev->features & NETIF_F_HW_HSR_TAG_RM) && 58 + port->type != HSR_PT_INTERLINK && 58 59 hsr->proto_ops->invalid_dan_ingress_frame && 59 60 hsr->proto_ops->invalid_dan_ingress_frame(protocol)) 60 61 goto finish_pass;