Skip to content

Commit bee88cd

Browse files
nbd168Paolo Abeni
authored andcommitted
net: add support for segmenting TCP fraglist GSO packets
Preparation for adding TCP fraglist GRO support. It expects packets to be combined in a similar way as UDP fraglist GSO packets. For IPv4 packets, NAT is handled in the same way as UDP fraglist GSO. Acked-by: Paolo Abeni <pabeni@redhat.com> Reviewed-by: Eric Dumazet <edumazet@google.com> Signed-off-by: Felix Fietkau <nbd@nbd.name> Reviewed-by: David Ahern <dsahern@kernel.org> Reviewed-by: Willem de Bruijn <willemb@google.com> Signed-off-by: Paolo Abeni <pabeni@redhat.com>
1 parent 8928756 commit bee88cd

File tree

2 files changed

+125
-0
lines changed

2 files changed

+125
-0
lines changed

net/ipv4/tcp_offload.c

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,70 @@ static void tcp_gso_tstamp(struct sk_buff *skb, unsigned int ts_seq,
2828
}
2929
}
3030

31+
static void __tcpv4_gso_segment_csum(struct sk_buff *seg,
32+
__be32 *oldip, __be32 newip,
33+
__be16 *oldport, __be16 newport)
34+
{
35+
struct tcphdr *th;
36+
struct iphdr *iph;
37+
38+
if (*oldip == newip && *oldport == newport)
39+
return;
40+
41+
th = tcp_hdr(seg);
42+
iph = ip_hdr(seg);
43+
44+
inet_proto_csum_replace4(&th->check, seg, *oldip, newip, true);
45+
inet_proto_csum_replace2(&th->check, seg, *oldport, newport, false);
46+
*oldport = newport;
47+
48+
csum_replace4(&iph->check, *oldip, newip);
49+
*oldip = newip;
50+
}
51+
52+
static struct sk_buff *__tcpv4_gso_segment_list_csum(struct sk_buff *segs)
53+
{
54+
const struct tcphdr *th;
55+
const struct iphdr *iph;
56+
struct sk_buff *seg;
57+
struct tcphdr *th2;
58+
struct iphdr *iph2;
59+
60+
seg = segs;
61+
th = tcp_hdr(seg);
62+
iph = ip_hdr(seg);
63+
th2 = tcp_hdr(seg->next);
64+
iph2 = ip_hdr(seg->next);
65+
66+
if (!(*(const u32 *)&th->source ^ *(const u32 *)&th2->source) &&
67+
iph->daddr == iph2->daddr && iph->saddr == iph2->saddr)
68+
return segs;
69+
70+
while ((seg = seg->next)) {
71+
th2 = tcp_hdr(seg);
72+
iph2 = ip_hdr(seg);
73+
74+
__tcpv4_gso_segment_csum(seg,
75+
&iph2->saddr, iph->saddr,
76+
&th2->source, th->source);
77+
__tcpv4_gso_segment_csum(seg,
78+
&iph2->daddr, iph->daddr,
79+
&th2->dest, th->dest);
80+
}
81+
82+
return segs;
83+
}
84+
85+
static struct sk_buff *__tcp4_gso_segment_list(struct sk_buff *skb,
86+
netdev_features_t features)
87+
{
88+
skb = skb_segment_list(skb, features, skb_mac_header_len(skb));
89+
if (IS_ERR(skb))
90+
return skb;
91+
92+
return __tcpv4_gso_segment_list_csum(skb);
93+
}
94+
3195
static struct sk_buff *tcp4_gso_segment(struct sk_buff *skb,
3296
netdev_features_t features)
3397
{
@@ -37,6 +101,9 @@ static struct sk_buff *tcp4_gso_segment(struct sk_buff *skb,
37101
if (!pskb_may_pull(skb, sizeof(struct tcphdr)))
38102
return ERR_PTR(-EINVAL);
39103

104+
if (skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST)
105+
return __tcp4_gso_segment_list(skb, features);
106+
40107
if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) {
41108
const struct iphdr *iph = ip_hdr(skb);
42109
struct tcphdr *th = tcp_hdr(skb);

net/ipv6/tcpv6_offload.c

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,61 @@ INDIRECT_CALLABLE_SCOPE int tcp6_gro_complete(struct sk_buff *skb, int thoff)
4040
return 0;
4141
}
4242

43+
static void __tcpv6_gso_segment_csum(struct sk_buff *seg,
44+
__be16 *oldport, __be16 newport)
45+
{
46+
struct tcphdr *th;
47+
48+
if (*oldport == newport)
49+
return;
50+
51+
th = tcp_hdr(seg);
52+
inet_proto_csum_replace2(&th->check, seg, *oldport, newport, false);
53+
*oldport = newport;
54+
}
55+
56+
static struct sk_buff *__tcpv6_gso_segment_list_csum(struct sk_buff *segs)
57+
{
58+
const struct tcphdr *th;
59+
const struct ipv6hdr *iph;
60+
struct sk_buff *seg;
61+
struct tcphdr *th2;
62+
struct ipv6hdr *iph2;
63+
64+
seg = segs;
65+
th = tcp_hdr(seg);
66+
iph = ipv6_hdr(seg);
67+
th2 = tcp_hdr(seg->next);
68+
iph2 = ipv6_hdr(seg->next);
69+
70+
if (!(*(const u32 *)&th->source ^ *(const u32 *)&th2->source) &&
71+
ipv6_addr_equal(&iph->saddr, &iph2->saddr) &&
72+
ipv6_addr_equal(&iph->daddr, &iph2->daddr))
73+
return segs;
74+
75+
while ((seg = seg->next)) {
76+
th2 = tcp_hdr(seg);
77+
iph2 = ipv6_hdr(seg);
78+
79+
iph2->saddr = iph->saddr;
80+
iph2->daddr = iph->daddr;
81+
__tcpv6_gso_segment_csum(seg, &th2->source, th->source);
82+
__tcpv6_gso_segment_csum(seg, &th2->dest, th->dest);
83+
}
84+
85+
return segs;
86+
}
87+
88+
static struct sk_buff *__tcp6_gso_segment_list(struct sk_buff *skb,
89+
netdev_features_t features)
90+
{
91+
skb = skb_segment_list(skb, features, skb_mac_header_len(skb));
92+
if (IS_ERR(skb))
93+
return skb;
94+
95+
return __tcpv6_gso_segment_list_csum(skb);
96+
}
97+
4398
static struct sk_buff *tcp6_gso_segment(struct sk_buff *skb,
4499
netdev_features_t features)
45100
{
@@ -51,6 +106,9 @@ static struct sk_buff *tcp6_gso_segment(struct sk_buff *skb,
51106
if (!pskb_may_pull(skb, sizeof(*th)))
52107
return ERR_PTR(-EINVAL);
53108

109+
if (skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST)
110+
return __tcp6_gso_segment_list(skb, features);
111+
54112
if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) {
55113
const struct ipv6hdr *ipv6h = ipv6_hdr(skb);
56114
struct tcphdr *th = tcp_hdr(skb);

0 commit comments

Comments
 (0)