[PATCH 1/1] net: hsr: avoid learning nodes from ordinary PRP SAN traffic

Ao Zhou
2026-04-02T14:18:36Z

From: Haoze Xie <royenheart@gmail.com>

PRP slave ports accept ordinary SAN traffic and forward it to the
master device without needing a persistent node entry. Creating one
node per previously unseen SAN source lets arbitrary source MAC floods
grow node_db until the prune timer catches up.

Keep the receive path for ordinary PRP SAN traffic, but stop learning a
new node when the frame is untagged and does not carry a PRP trailer.
Continue to deliver the frame locally and only keep node state for
actual HSR/PRP senders or nodes that have already been learned.

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>
---
net/hsr/hsr_forward.c | 14 ++++++++++----
net/hsr/hsr_framereg.c | 16 +++++++++++++---
2 files changed, 23 insertions(+), 7 deletions(-)

diff --git a/net/hsr/hsr_forward.c b/net/hsr/hsr_forward.c
index aefc9b6936ba..5fbfc42997d2 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);
@@ -699,8 +700,12 @@ static int fill_frame_info(struct hsr_frame_info *frame,

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 */
+ if (IS_ERR(frame->node_src)) {
+ ret = PTR_ERR(frame->node_src);
+ if (ret != -ENOENT)
+ return ret;
+ frame->node_src = NULL;
+ }

ethhdr = (struct ethhdr *)skb_mac_header(skb);
frame->is_vlan = false;
@@ -739,7 +744,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..13e1a0484879 100644
--- a/net/hsr/hsr_framereg.c
+++ b/net/hsr/hsr_framereg.c
@@ -230,7 +230,7 @@ struct hsr_node *hsr_get_node(struct hsr_port *port, struct list_head *node_db,
bool san = false;

if (!skb_mac_header_was_set(skb))
- return NULL;
+ return ERR_PTR(-EINVAL);

ethhdr = (struct ethhdr *)skb_mac_header(skb);

@@ -263,14 +263,24 @@ struct hsr_node *hsr_get_node(struct hsr_port *port, struct list_head *node_db,
ethhdr->h_proto == htons(ETH_P_HSR)) {
/* Check if skb contains hsr_ethhdr */
if (skb->mac_len < sizeof(struct hsr_ethhdr))
- return NULL;
+ return ERR_PTR(-EINVAL);
} else {
rct = skb_get_PRP_rct(skb);
if (!rct && rx_port != HSR_PT_MASTER)
san = true;
}

- return hsr_add_node(hsr, node_db, ethhdr->h_source, san, rx_port);
+ /* PRP accepts ordinary SAN traffic on slave ports without learning a
+ * persistent node entry.
+ */
+ if (hsr->prot_version == PRP_V1 && san)
+ return ERR_PTR(-ENOENT);
+
+ node = hsr_add_node(hsr, node_db, ethhdr->h_source, san, rx_port);
+ if (!node)
+ return ERR_PTR(-ENOMEM);
+
+ return node;
}

static bool hsr_seq_block_is_old(struct hsr_seq_block *block)
--
2.53.0