[PATCH net v2 1/1] net: hsr: avoid learning unknown senders for local delivery

Ao Zhou
2026-04-04T11:31:13Z

From: Haoze Xie <royenheart@gmail.com>

Traffic that is directly addressed to the local HSR/PRP master can be
delivered locally without creating a persistent node entry. Learning one
node per previously unseen source MAC lets forged sender floods grow
node_db until the prune timer catches up.

Determine whether a frame is locally exclusive before node lookup and
skip learning for unknown senders in that case. When no node state
exists, also skip duplicate discard checks that depend on it.

This keeps locally-destined traffic reachable while avoiding node table
growth from source-MAC floods in both the PRP SAN path and the HSR/PRP
tagged sender paths.

Fixes: f421436a591d ("net/hsr: Add support for the High-availability Seamless Redundancy protocol (HSRv0)")
Fixes: 451d8123f897 ("net: prp: add packet handling support")
Reported-by: Yifan Wu <yifanwucs@gmail.com>
Reported-by: Juefei Pu <tomapufckgml@gmail.com>
Co-developed-by: Yuan Tan <yuantan098@gmail.com>
Signed-off-by: Yuan Tan <yuantan098@gmail.com>
Suggested-by: Xin Liu <bird@lzu.edu.cn>
Tested-by: Yuqi Xu <xuyuqiabc@gmail.com>
Signed-off-by: Haoze Xie <royenheart@gmail.com>
Signed-off-by: Ao Zhou <n05ec@lzu.edu.cn>
---
changes in v2:
- generalize the fix beyond PRP SAN traffic and cover HSR/PRP tagged sender floods
- decide whether learning is needed from local-exclusive delivery instead of protocol-specific SAN checks
- use the normal NULL return semantics from hsr_get_node() instead of ERR_PTR-based error plumbing
- skip duplicate-discard state checks when no node state exists

net/hsr/hsr_forward.c | 23 +++++++++++++----------
net/hsr/hsr_framereg.c | 5 ++++-
net/hsr/hsr_framereg.h | 2 +-
3 files changed, 18 insertions(+), 12 deletions(-)

diff --git a/net/hsr/hsr_forward.c b/net/hsr/hsr_forward.c
index aefc9b6936ba..15bd17b4ee17 100644
--- a/net/hsr/hsr_forward.c
+++ b/net/hsr/hsr_forward.c
@@ -403,7 +403,8 @@ static void hsr_deliver_master(struct sk_buff *skb, struct net_device *dev,
int res, recv_len;

was_multicast_frame = (skb->pkt_type == PACKET_MULTICAST);
- hsr_addr_subst_source(node_src, skb);
+ if (node_src)
+ hsr_addr_subst_source(node_src, skb);
skb_pull(skb, ETH_HLEN);
recv_len = skb->len;
res = netif_rx(skb);
@@ -545,7 +546,7 @@ static void hsr_forward_do(struct hsr_frame_info *frame)
/* Don't send frame over port where it has been sent before.
* Also for SAN, this shouldn't be done.
*/
- if (!frame->is_from_san &&
+ if (frame->node_src && !frame->is_from_san &&
hsr->proto_ops->register_frame_out &&
hsr->proto_ops->register_frame_out(port, frame))
continue;
@@ -688,21 +689,25 @@ static int fill_frame_info(struct hsr_frame_info *frame,
return -EINVAL;

memset(frame, 0, sizeof(*frame));
+ frame->port_rcv = port;
frame->is_supervision = is_supervision_frame(port->hsr, skb);
if (frame->is_supervision && hsr->redbox)
frame->is_proxy_supervision =
is_proxy_supervision_frame(port->hsr, skb);

+ ethhdr = (struct ethhdr *)skb_mac_header(skb);
+ check_local_dest(port->hsr, skb, frame);
+
n_db = &hsr->node_db;
if (port->type == HSR_PT_INTERLINK)
n_db = &hsr->proxy_node_db;

frame->node_src = hsr_get_node(port, n_db, skb,
- frame->is_supervision, port->type);
- if (!frame->node_src)
- return -1; /* Unknown node and !is_supervision, or no mem */
+ frame->is_supervision, port->type,
+ !frame->is_local_exclusive);
+ if (!frame->node_src && !frame->is_local_exclusive)
+ return -1;

- ethhdr = (struct ethhdr *)skb_mac_header(skb);
frame->is_vlan = false;
proto = ethhdr->h_proto;

@@ -720,13 +725,10 @@ static int fill_frame_info(struct hsr_frame_info *frame,
}

frame->is_from_san = false;
- frame->port_rcv = port;
ret = hsr->proto_ops->fill_frame_info(proto, skb, frame);
if (ret)
return ret;

- check_local_dest(port->hsr, skb, frame);
-
return 0;
}

@@ -739,7 +741,8 @@ void hsr_forward_skb(struct sk_buff *skb, struct hsr_port *port)
if (fill_frame_info(&frame, skb, port) < 0)
goto out_drop;

- hsr_register_frame_in(frame.node_src, port, frame.sequence_nr);
+ if (frame.node_src)
+ hsr_register_frame_in(frame.node_src, port, frame.sequence_nr);
hsr_forward_do(&frame);
rcu_read_unlock();
/* Gets called for ingress frames as well as egress from master port.
diff --git a/net/hsr/hsr_framereg.c b/net/hsr/hsr_framereg.c
index 50996f4de7f9..2bc6f8f154c2 100644
--- a/net/hsr/hsr_framereg.c
+++ b/net/hsr/hsr_framereg.c
@@ -221,7 +221,7 @@ void prp_update_san_info(struct hsr_node *node, bool is_sup)
*/
struct hsr_node *hsr_get_node(struct hsr_port *port, struct list_head *node_db,
struct sk_buff *skb, bool is_sup,
- enum hsr_port_type rx_port)
+ enum hsr_port_type rx_port, bool learn)
{
struct hsr_priv *hsr = port->hsr;
struct hsr_node *node;
@@ -270,6 +270,9 @@ struct hsr_node *hsr_get_node(struct hsr_port *port, struct list_head *node_db,
san = true;
}

+ if (!learn)
+ return NULL;
+
return hsr_add_node(hsr, node_db, ethhdr->h_source, san, rx_port);
}

diff --git a/net/hsr/hsr_framereg.h b/net/hsr/hsr_framereg.h
index c65ecb925734..3d9c88e83090 100644
--- a/net/hsr/hsr_framereg.h
+++ b/net/hsr/hsr_framereg.h
@@ -33,7 +33,7 @@ void hsr_del_self_node(struct hsr_priv *hsr);
void hsr_del_nodes(struct list_head *node_db);
struct hsr_node *hsr_get_node(struct hsr_port *port, struct list_head *node_db,
struct sk_buff *skb, bool is_sup,
- enum hsr_port_type rx_port);
+ enum hsr_port_type rx_port, bool learn);
void hsr_handle_sup_frame(struct hsr_frame_info *frame);
bool hsr_addr_is_self(struct hsr_priv *hsr, unsigned char *addr);
bool hsr_addr_is_redbox(struct hsr_priv *hsr, unsigned char *addr);
--
2.53.0