1 From bee88cd5bd83d40b8aec4d6cb729378f707f6197 Mon Sep 17 00:00:00 2001
2 From: Felix Fietkau <nbd@nbd.name>
3 Date: Thu, 2 May 2024 10:44:43 +0200
4 Subject: [PATCH 2/6] net: add support for segmenting TCP fraglist GSO packets
6 Preparation for adding TCP fraglist GRO support. It expects packets to be
7 combined in a similar way as UDP fraglist GSO packets.
8 For IPv4 packets, NAT is handled in the same way as UDP fraglist GSO.
10 Acked-by: Paolo Abeni <pabeni@redhat.com>
11 Reviewed-by: Eric Dumazet <edumazet@google.com>
12 Signed-off-by: Felix Fietkau <nbd@nbd.name>
13 Reviewed-by: David Ahern <dsahern@kernel.org>
14 Reviewed-by: Willem de Bruijn <willemb@google.com>
15 Signed-off-by: Paolo Abeni <pabeni@redhat.com>
17 net/ipv4/tcp_offload.c | 67 ++++++++++++++++++++++++++++++++++++++++
18 net/ipv6/tcpv6_offload.c | 58 ++++++++++++++++++++++++++++++++++
19 2 files changed, 125 insertions(+)
21 --- a/net/ipv4/tcp_offload.c
22 +++ b/net/ipv4/tcp_offload.c
23 @@ -31,6 +31,70 @@ static void tcp_gso_tstamp(struct sk_buf
27 +static void __tcpv4_gso_segment_csum(struct sk_buff *seg,
28 + __be32 *oldip, __be32 newip,
29 + __be16 *oldport, __be16 newport)
34 + if (*oldip == newip && *oldport == newport)
40 + inet_proto_csum_replace4(&th->check, seg, *oldip, newip, true);
41 + inet_proto_csum_replace2(&th->check, seg, *oldport, newport, false);
44 + csum_replace4(&iph->check, *oldip, newip);
48 +static struct sk_buff *__tcpv4_gso_segment_list_csum(struct sk_buff *segs)
50 + const struct tcphdr *th;
51 + const struct iphdr *iph;
52 + struct sk_buff *seg;
59 + th2 = tcp_hdr(seg->next);
60 + iph2 = ip_hdr(seg->next);
62 + if (!(*(const u32 *)&th->source ^ *(const u32 *)&th2->source) &&
63 + iph->daddr == iph2->daddr && iph->saddr == iph2->saddr)
66 + while ((seg = seg->next)) {
70 + __tcpv4_gso_segment_csum(seg,
71 + &iph2->saddr, iph->saddr,
72 + &th2->source, th->source);
73 + __tcpv4_gso_segment_csum(seg,
74 + &iph2->daddr, iph->daddr,
75 + &th2->dest, th->dest);
81 +static struct sk_buff *__tcp4_gso_segment_list(struct sk_buff *skb,
82 + netdev_features_t features)
84 + skb = skb_segment_list(skb, features, skb_mac_header_len(skb));
88 + return __tcpv4_gso_segment_list_csum(skb);
91 static struct sk_buff *tcp4_gso_segment(struct sk_buff *skb,
92 netdev_features_t features)
94 @@ -40,6 +104,9 @@ static struct sk_buff *tcp4_gso_segment(
95 if (!pskb_may_pull(skb, sizeof(struct tcphdr)))
96 return ERR_PTR(-EINVAL);
98 + if (skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST)
99 + return __tcp4_gso_segment_list(skb, features);
101 if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) {
102 const struct iphdr *iph = ip_hdr(skb);
103 struct tcphdr *th = tcp_hdr(skb);
104 --- a/net/ipv6/tcpv6_offload.c
105 +++ b/net/ipv6/tcpv6_offload.c
106 @@ -40,6 +40,61 @@ INDIRECT_CALLABLE_SCOPE int tcp6_gro_com
110 +static void __tcpv6_gso_segment_csum(struct sk_buff *seg,
111 + __be16 *oldport, __be16 newport)
115 + if (*oldport == newport)
119 + inet_proto_csum_replace2(&th->check, seg, *oldport, newport, false);
120 + *oldport = newport;
123 +static struct sk_buff *__tcpv6_gso_segment_list_csum(struct sk_buff *segs)
125 + const struct tcphdr *th;
126 + const struct ipv6hdr *iph;
127 + struct sk_buff *seg;
128 + struct tcphdr *th2;
129 + struct ipv6hdr *iph2;
133 + iph = ipv6_hdr(seg);
134 + th2 = tcp_hdr(seg->next);
135 + iph2 = ipv6_hdr(seg->next);
137 + if (!(*(const u32 *)&th->source ^ *(const u32 *)&th2->source) &&
138 + ipv6_addr_equal(&iph->saddr, &iph2->saddr) &&
139 + ipv6_addr_equal(&iph->daddr, &iph2->daddr))
142 + while ((seg = seg->next)) {
143 + th2 = tcp_hdr(seg);
144 + iph2 = ipv6_hdr(seg);
146 + iph2->saddr = iph->saddr;
147 + iph2->daddr = iph->daddr;
148 + __tcpv6_gso_segment_csum(seg, &th2->source, th->source);
149 + __tcpv6_gso_segment_csum(seg, &th2->dest, th->dest);
155 +static struct sk_buff *__tcp6_gso_segment_list(struct sk_buff *skb,
156 + netdev_features_t features)
158 + skb = skb_segment_list(skb, features, skb_mac_header_len(skb));
162 + return __tcpv6_gso_segment_list_csum(skb);
165 static struct sk_buff *tcp6_gso_segment(struct sk_buff *skb,
166 netdev_features_t features)
168 @@ -51,6 +106,9 @@ static struct sk_buff *tcp6_gso_segment(
169 if (!pskb_may_pull(skb, sizeof(*th)))
170 return ERR_PTR(-EINVAL);
172 + if (skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST)
173 + return __tcp6_gso_segment_list(skb, features);
175 if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) {
176 const struct ipv6hdr *ipv6h = ipv6_hdr(skb);
177 struct tcphdr *th = tcp_hdr(skb);