]> git.ipfire.org Git - thirdparty/openwrt.git/blob
90de1ab061969def2c05e54c7e50aaeeb3fd3060
[thirdparty/openwrt.git] /
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
5
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.
9
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>
16 ---
17 net/ipv4/tcp_offload.c | 67 ++++++++++++++++++++++++++++++++++++++++
18 net/ipv6/tcpv6_offload.c | 58 ++++++++++++++++++++++++++++++++++
19 2 files changed, 125 insertions(+)
20
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
24 }
25 }
26
27 +static void __tcpv4_gso_segment_csum(struct sk_buff *seg,
28 + __be32 *oldip, __be32 newip,
29 + __be16 *oldport, __be16 newport)
30 +{
31 + struct tcphdr *th;
32 + struct iphdr *iph;
33 +
34 + if (*oldip == newip && *oldport == newport)
35 + return;
36 +
37 + th = tcp_hdr(seg);
38 + iph = ip_hdr(seg);
39 +
40 + inet_proto_csum_replace4(&th->check, seg, *oldip, newip, true);
41 + inet_proto_csum_replace2(&th->check, seg, *oldport, newport, false);
42 + *oldport = newport;
43 +
44 + csum_replace4(&iph->check, *oldip, newip);
45 + *oldip = newip;
46 +}
47 +
48 +static struct sk_buff *__tcpv4_gso_segment_list_csum(struct sk_buff *segs)
49 +{
50 + const struct tcphdr *th;
51 + const struct iphdr *iph;
52 + struct sk_buff *seg;
53 + struct tcphdr *th2;
54 + struct iphdr *iph2;
55 +
56 + seg = segs;
57 + th = tcp_hdr(seg);
58 + iph = ip_hdr(seg);
59 + th2 = tcp_hdr(seg->next);
60 + iph2 = ip_hdr(seg->next);
61 +
62 + if (!(*(const u32 *)&th->source ^ *(const u32 *)&th2->source) &&
63 + iph->daddr == iph2->daddr && iph->saddr == iph2->saddr)
64 + return segs;
65 +
66 + while ((seg = seg->next)) {
67 + th2 = tcp_hdr(seg);
68 + iph2 = ip_hdr(seg);
69 +
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);
76 + }
77 +
78 + return segs;
79 +}
80 +
81 +static struct sk_buff *__tcp4_gso_segment_list(struct sk_buff *skb,
82 + netdev_features_t features)
83 +{
84 + skb = skb_segment_list(skb, features, skb_mac_header_len(skb));
85 + if (IS_ERR(skb))
86 + return skb;
87 +
88 + return __tcpv4_gso_segment_list_csum(skb);
89 +}
90 +
91 static struct sk_buff *tcp4_gso_segment(struct sk_buff *skb,
92 netdev_features_t features)
93 {
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);
97
98 + if (skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST)
99 + return __tcp4_gso_segment_list(skb, features);
100 +
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
107 return 0;
108 }
109
110 +static void __tcpv6_gso_segment_csum(struct sk_buff *seg,
111 + __be16 *oldport, __be16 newport)
112 +{
113 + struct tcphdr *th;
114 +
115 + if (*oldport == newport)
116 + return;
117 +
118 + th = tcp_hdr(seg);
119 + inet_proto_csum_replace2(&th->check, seg, *oldport, newport, false);
120 + *oldport = newport;
121 +}
122 +
123 +static struct sk_buff *__tcpv6_gso_segment_list_csum(struct sk_buff *segs)
124 +{
125 + const struct tcphdr *th;
126 + const struct ipv6hdr *iph;
127 + struct sk_buff *seg;
128 + struct tcphdr *th2;
129 + struct ipv6hdr *iph2;
130 +
131 + seg = segs;
132 + th = tcp_hdr(seg);
133 + iph = ipv6_hdr(seg);
134 + th2 = tcp_hdr(seg->next);
135 + iph2 = ipv6_hdr(seg->next);
136 +
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))
140 + return segs;
141 +
142 + while ((seg = seg->next)) {
143 + th2 = tcp_hdr(seg);
144 + iph2 = ipv6_hdr(seg);
145 +
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);
150 + }
151 +
152 + return segs;
153 +}
154 +
155 +static struct sk_buff *__tcp6_gso_segment_list(struct sk_buff *skb,
156 + netdev_features_t features)
157 +{
158 + skb = skb_segment_list(skb, features, skb_mac_header_len(skb));
159 + if (IS_ERR(skb))
160 + return skb;
161 +
162 + return __tcpv6_gso_segment_list_csum(skb);
163 +}
164 +
165 static struct sk_buff *tcp6_gso_segment(struct sk_buff *skb,
166 netdev_features_t features)
167 {
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);
171
172 + if (skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST)
173 + return __tcp6_gso_segment_list(skb, features);
174 +
175 if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) {
176 const struct ipv6hdr *ipv6h = ipv6_hdr(skb);
177 struct tcphdr *th = tcp_hdr(skb);