Skip to content

Commit ee80d1e

Browse files
wdebruijdavem330
authored andcommitted
udp: add udp gso
Implement generic segmentation offload support for udp datagrams. A follow-up patch adds support to the protocol stack to generate such packets. UDP GSO is not UFO. UFO fragments a single large datagram. GSO splits a large payload into a number of discrete UDP datagrams. The implementation adds a GSO type SKB_UDP_GSO_L4 to differentiate it from UFO (SKB_UDP_GSO). IPPROTO_UDPLITE is excluded, as that protocol has no gso handler registered. [ Export __udp_gso_segment for ipv6. -DaveM ] Signed-off-by: Willem de Bruijn <willemb@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent 1cd7884 commit ee80d1e

File tree

6 files changed

+82
-4
lines changed

6 files changed

+82
-4
lines changed

include/linux/skbuff.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,8 @@ enum {
573573
SKB_GSO_ESP = 1 << 15,
574574

575575
SKB_GSO_UDP = 1 << 16,
576+
577+
SKB_GSO_UDP_L4 = 1 << 17,
576578
};
577579

578580
#if BITS_PER_LONG > 32

include/net/udp.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,10 @@ struct sk_buff **udp_gro_receive(struct sk_buff **head, struct sk_buff *skb,
174174
struct udphdr *uh, udp_lookup_t lookup);
175175
int udp_gro_complete(struct sk_buff *skb, int nhoff, udp_lookup_t lookup);
176176

177+
struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb,
178+
netdev_features_t features,
179+
unsigned int mss, __sum16 check);
180+
177181
static inline struct udphdr *udp_gro_udphdr(struct sk_buff *skb)
178182
{
179183
struct udphdr *uh;

net/core/skbuff.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4940,6 +4940,8 @@ static unsigned int skb_gso_transport_seglen(const struct sk_buff *skb)
49404940
thlen = tcp_hdrlen(skb);
49414941
} else if (unlikely(skb_is_gso_sctp(skb))) {
49424942
thlen = sizeof(struct sctphdr);
4943+
} else if (shinfo->gso_type & SKB_GSO_UDP_L4) {
4944+
thlen = sizeof(struct udphdr);
49434945
}
49444946
/* UFO sets gso_size to the size of the fragmentation
49454947
* payload, i.e. the size of the L4 (UDP) header is already

net/ipv4/udp_offload.c

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,54 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
187187
}
188188
EXPORT_SYMBOL(skb_udp_tunnel_segment);
189189

190+
struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb,
191+
netdev_features_t features,
192+
unsigned int mss, __sum16 check)
193+
{
194+
struct sk_buff *segs, *seg;
195+
unsigned int hdrlen;
196+
struct udphdr *uh;
197+
198+
if (gso_skb->len <= sizeof(*uh) + mss)
199+
return ERR_PTR(-EINVAL);
200+
201+
hdrlen = gso_skb->data - skb_mac_header(gso_skb);
202+
skb_pull(gso_skb, sizeof(*uh));
203+
204+
segs = skb_segment(gso_skb, features);
205+
if (unlikely(IS_ERR_OR_NULL(segs)))
206+
return segs;
207+
208+
for (seg = segs; seg; seg = seg->next) {
209+
uh = udp_hdr(seg);
210+
uh->len = htons(seg->len - hdrlen);
211+
uh->check = check;
212+
213+
/* last packet can be partial gso_size */
214+
if (!seg->next)
215+
csum_replace2(&uh->check, htons(mss),
216+
htons(seg->len - hdrlen - sizeof(*uh)));
217+
}
218+
219+
return segs;
220+
}
221+
EXPORT_SYMBOL_GPL(__udp_gso_segment);
222+
223+
static struct sk_buff *__udp4_gso_segment(struct sk_buff *gso_skb,
224+
netdev_features_t features)
225+
{
226+
const struct iphdr *iph = ip_hdr(gso_skb);
227+
unsigned int mss = skb_shinfo(gso_skb)->gso_size;
228+
229+
if (!can_checksum_protocol(features, htons(ETH_P_IP)))
230+
return ERR_PTR(-EIO);
231+
232+
return __udp_gso_segment(gso_skb, features, mss,
233+
udp_v4_check(sizeof(struct udphdr) + mss,
234+
iph->saddr, iph->daddr, 0));
235+
}
236+
EXPORT_SYMBOL_GPL(__udp4_gso_segment);
237+
190238
static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb,
191239
netdev_features_t features)
192240
{
@@ -203,12 +251,15 @@ static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb,
203251
goto out;
204252
}
205253

206-
if (!(skb_shinfo(skb)->gso_type & SKB_GSO_UDP))
254+
if (!(skb_shinfo(skb)->gso_type & (SKB_GSO_UDP | SKB_GSO_UDP_L4)))
207255
goto out;
208256

209257
if (!pskb_may_pull(skb, sizeof(struct udphdr)))
210258
goto out;
211259

260+
if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4)
261+
return __udp4_gso_segment(skb, features);
262+
212263
mss = skb_shinfo(skb)->gso_size;
213264
if (unlikely(skb->len <= mss))
214265
goto out;

net/ipv6/ip6_offload.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,11 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb,
8888

8989
if (skb->encapsulation &&
9090
skb_shinfo(skb)->gso_type & (SKB_GSO_IPXIP4 | SKB_GSO_IPXIP6))
91-
udpfrag = proto == IPPROTO_UDP && encap;
91+
udpfrag = proto == IPPROTO_UDP && encap &&
92+
(skb_shinfo(skb)->gso_type & SKB_GSO_UDP);
9293
else
93-
udpfrag = proto == IPPROTO_UDP && !skb->encapsulation;
94+
udpfrag = proto == IPPROTO_UDP && !skb->encapsulation &&
95+
(skb_shinfo(skb)->gso_type & SKB_GSO_UDP);
9496

9597
ops = rcu_dereference(inet6_offloads[proto]);
9698
if (likely(ops && ops->callbacks.gso_segment)) {

net/ipv6/udp_offload.c

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,20 @@
1717
#include <net/ip6_checksum.h>
1818
#include "ip6_offload.h"
1919

20+
static struct sk_buff *__udp6_gso_segment(struct sk_buff *gso_skb,
21+
netdev_features_t features)
22+
{
23+
const struct ipv6hdr *ip6h = ipv6_hdr(gso_skb);
24+
unsigned int mss = skb_shinfo(gso_skb)->gso_size;
25+
26+
if (!can_checksum_protocol(features, htons(ETH_P_IPV6)))
27+
return ERR_PTR(-EIO);
28+
29+
return __udp_gso_segment(gso_skb, features, mss,
30+
udp_v6_check(sizeof(struct udphdr) + mss,
31+
&ip6h->saddr, &ip6h->daddr, 0));
32+
}
33+
2034
static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb,
2135
netdev_features_t features)
2236
{
@@ -42,12 +56,15 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb,
4256
const struct ipv6hdr *ipv6h;
4357
struct udphdr *uh;
4458

45-
if (!(skb_shinfo(skb)->gso_type & SKB_GSO_UDP))
59+
if (!(skb_shinfo(skb)->gso_type & (SKB_GSO_UDP | SKB_GSO_UDP_L4)))
4660
goto out;
4761

4862
if (!pskb_may_pull(skb, sizeof(struct udphdr)))
4963
goto out;
5064

65+
if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4)
66+
return __udp6_gso_segment(skb, features);
67+
5168
/* Do software UFO. Complete and fill in the UDP checksum as HW cannot
5269
* do checksum of UDP packets sent as multiple IP fragments.
5370
*/

0 commit comments

Comments
 (0)