HSR/PRP forward frames one by one: each wire frame gets its own tag and sequence number, and duplicate discard is per frame. A GSO super-packet reaching hsr_forward_skb() breaks that per-frame semantics: it would be tagged and forwarded as a single frame. Unfold such super-packets at the forward entry with the top-level GSO dispatch: __skb_gso_segment() initializes SKB_GSO_CB() and performs the L2->L3->L4 protocol dispatch (calling the low-level skb_segment() helper directly is not allowed here -- it reads SKB_GSO_CB(skb) state that only __skb_gso_segment() sets up). features = 0 requests full software segmentation; tx_path is selected by ingress port (master = locally generated TX, interlink = RX) to get the right checksum semantics. Each segment then runs through the existing per-frame path, which is split out as hsr_forward_skb_one() so that no GSO skb can reach it. Segmentation is only offered for the ingress roles whose frames are known to be plain Ethernet: the master (locally generated) and the interlink (SAN side, untagged). A super-packet received from a LAN slave may carry per-frame HSR tags or PRP RCT trailers that software segmentation cannot recover, and an already-tagged HSR/PRP super-packet violates per-frame wire semantics; both are rejected by ingress-port policy. (ETH_P_PRP identifies supervision traffic only; a PRP data frame keeps its payload EtherType, so RCT carriage cannot be tested by protocol.) Also drop NETIF_F_GSO_MASK from the HSR master's hw_features so locally generated traffic is segmented before reaching the forward path whenever possible. Fixes: f421436a591d ("net/hsr: Add support for the High-availability Seamless Redundancy protocol (HSRv0)") Cc: stable@vger.kernel.org Signed-off-by: Xin Xie --- net/hsr/hsr_device.c | 2 +- net/hsr/hsr_forward.c | 50 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/net/hsr/hsr_device.c b/net/hsr/hsr_device.c index 3fd1762d891..248cbb142e2 100644 --- a/net/hsr/hsr_device.c +++ b/net/hsr/hsr_device.c @@ -652,7 +652,7 @@ void hsr_dev_setup(struct net_device *dev) dev->needs_free_netdev = true; dev->hw_features = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA | - NETIF_F_GSO_MASK | NETIF_F_HW_CSUM | + NETIF_F_HW_CSUM | NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_FILTER; diff --git a/net/hsr/hsr_forward.c b/net/hsr/hsr_forward.c index 8e4158a9b57..3fcdbac49c5 100644 --- a/net/hsr/hsr_forward.c +++ b/net/hsr/hsr_forward.c @@ -12,6 +12,7 @@ #include #include #include +#include #include "hsr_main.h" #include "hsr_framereg.h" @@ -732,7 +733,7 @@ static int fill_frame_info(struct hsr_frame_info *frame, } /* Must be called holding rcu read lock (because of the port parameter) */ -void hsr_forward_skb(struct sk_buff *skb, struct hsr_port *port) +static void hsr_forward_skb_one(struct sk_buff *skb, struct hsr_port *port) { struct hsr_frame_info frame; @@ -761,3 +762,50 @@ void hsr_forward_skb(struct sk_buff *skb, struct hsr_port *port) port->dev->stats.tx_dropped++; kfree_skb(skb); } + +/* GSO fan-out funnel: unfold super-packets before per-frame processing so + * each wire frame gets its own HSR/PRP tag and sequence number. + */ +void hsr_forward_skb(struct sk_buff *skb, struct hsr_port *port) +{ + struct sk_buff *segs, *next; + + if (likely(!skb_is_gso(skb))) { + hsr_forward_skb_one(skb, port); + return; + } + + /* Unfold only plain-Ethernet GSO super-packets: locally generated + * on the master, or arriving untagged from the SAN side on the + * interlink. A super-packet from a LAN slave may carry per-frame + * HSR tags / PRP RCT trailers that software segmentation cannot + * recover; an already-tagged HSR/PRP super-packet violates + * per-frame wire semantics. Drop both. + */ + if (port->type != HSR_PT_MASTER && port->type != HSR_PT_INTERLINK) + goto drop_gso; + if (skb->protocol == htons(ETH_P_HSR) || + skb->protocol == htons(ETH_P_PRP)) + goto drop_gso; + + /* features = 0: request full software segmentation. tx_path is true + * only for locally generated traffic on the master; ingress from + * the interlink follows RX checksum semantics. + */ + segs = __skb_gso_segment(skb, 0, port->type == HSR_PT_MASTER); + if (IS_ERR(segs) || unlikely(!segs)) + goto drop_gso; + + consume_skb(skb); + while (segs) { + next = segs->next; + segs->next = NULL; + hsr_forward_skb_one(segs, port); + segs = next; + } + return; + +drop_gso: + port->dev->stats.tx_dropped++; + kfree_skb(skb); +} -- 2.43.0