From: Maxim Mikityanskiy From: Maxim Mikityanskiy skb_segment is called for partial GSO, when netif_needs_gso returns true in validate_xmit_skb. Partial GSO is needed, for example, when segmentation of tunneled traffic is offloaded to a NIC that only supports inner checksum offload. Currently, skb_segment clamps the segment length to 65534 bytes, because gso_size == 65535 is a special value GSO_BY_FRAGS, and we don't want to accidentally assign mss = 65535, as it would fall into the GSO_BY_FRAGS check further in the function. This implementation, however, artificially blocks len > 65534, which is possible since the introduction of BIG TCP. To allow bigger lengths and avoid resegmentation of BIG TCP packets, store the gso_by_frags flag in the beginning and don't use a special value of mss for this purpose after mss was modified. Signed-off-by: Maxim Mikityanskiy --- net/core/skbuff.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/net/core/skbuff.c b/net/core/skbuff.c index d331e607edfb..2ebacf5fa09a 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -4699,6 +4699,7 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb, struct sk_buff *tail = NULL; struct sk_buff *list_skb = skb_shinfo(head_skb)->frag_list; unsigned int mss = skb_shinfo(head_skb)->gso_size; + bool gso_by_frags = mss == GSO_BY_FRAGS; unsigned int doffset = head_skb->data - skb_mac_header(head_skb); unsigned int offset = doffset; unsigned int tnl_hlen = skb_tnl_header_len(head_skb); @@ -4714,7 +4715,7 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb, int nfrags, pos; if ((skb_shinfo(head_skb)->gso_type & SKB_GSO_DODGY) && - mss != GSO_BY_FRAGS && mss != skb_headlen(head_skb)) { + !gso_by_frags && mss != skb_headlen(head_skb)) { struct sk_buff *check_skb; for (check_skb = list_skb; check_skb; check_skb = check_skb->next) { @@ -4742,7 +4743,7 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb, sg = !!(features & NETIF_F_SG); csum = !!can_checksum_protocol(features, proto); - if (sg && csum && (mss != GSO_BY_FRAGS)) { + if (sg && csum && !gso_by_frags) { if (!(features & NETIF_F_GSO_PARTIAL)) { struct sk_buff *iter; unsigned int frag_len; @@ -4776,9 +4777,8 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb, /* GSO partial only requires that we trim off any excess that * doesn't fit into an MSS sized block, so take care of that * now. - * Cap len to not accidentally hit GSO_BY_FRAGS. */ - partial_segs = min(len, GSO_BY_FRAGS - 1) / mss; + partial_segs = len / mss; if (partial_segs > 1) mss *= partial_segs; else @@ -4802,7 +4802,7 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb, int hsize; int size; - if (unlikely(mss == GSO_BY_FRAGS)) { + if (unlikely(gso_by_frags)) { len = list_skb->len; } else { len = head_skb->len - offset; -- 2.50.1