aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWillem de Bruijn <willemb@google.com>2017-11-21 10:22:25 -0500
committerDavid S. Miller <davem@davemloft.net>2017-11-23 11:37:35 -0500
commit0c19f846d582af919db66a5914a0189f9f92c936 (patch)
tree327b6d3dad4895621dd6fb712f7b59bbf7b922dd
parent9e77d7a5549dc4d4999a60676373ab3fd1dae4db (diff)
net: accept UFO datagrams from tuntap and packet
Tuntap and similar devices can inject GSO packets. Accept type VIRTIO_NET_HDR_GSO_UDP, even though not generating UFO natively. Processes are expected to use feature negotiation such as TUNSETOFFLOAD to detect supported offload types and refrain from injecting other packets. This process breaks down with live migration: guest kernels do not renegotiate flags, so destination hosts need to expose all features that the source host does. Partially revert the UFO removal from 182e0b6b5846~1..d9d30adf5677. This patch introduces nearly(*) no new code to simplify verification. It brings back verbatim tuntap UFO negotiation, VIRTIO_NET_HDR_GSO_UDP insertion and software UFO segmentation. It does not reinstate protocol stack support, hardware offload (NETIF_F_UFO), SKB_GSO_UDP tunneling in SKB_GSO_SOFTWARE or reception of VIRTIO_NET_HDR_GSO_UDP packets in tuntap. To support SKB_GSO_UDP reappearing in the stack, also reinstate logic in act_csum and openvswitch. Achieve equivalence with v4.13 HEAD by squashing in commit 939912216fa8 ("net: skb_needs_check() removes CHECKSUM_UNNECESSARY check for tx.") and reverting commit 8d63bee643f1 ("net: avoid skb_warn_bad_offload false positives on UFO"). (*) To avoid having to bring back skb_shinfo(skb)->ip6_frag_id, ipv6_proxy_select_ident is changed to return a __be32 and this is assigned directly to the frag_hdr. Also, SKB_GSO_UDP is inserted at the end of the enum to minimize code churn. Tested Booted a v4.13 guest kernel with QEMU. On a host kernel before this patch `ethtool -k eth0` shows UFO disabled. After the patch, it is enabled, same as on a v4.13 host kernel. A UFO packet sent from the guest appears on the tap device: host: nc -l -p -u 8000 & tcpdump -n -i tap0 guest: dd if=/dev/zero of=payload.txt bs=1 count=2000 nc -u 192.16.1.1 8000 < payload.txt Direct tap to tap transmission of VIRTIO_NET_HDR_GSO_UDP succeeds, packets arriving fragmented: ./with_tap_pair.sh ./tap_send_ufo tap0 tap1 (from https://github.com/wdebruij/kerneltools/tree/master/tests) Changes v1 -> v2 - simplified set_offload change (review comment) - documented test procedure Link: http://lkml.kernel.org/r/<CAF=yD-LuUeDuL9YWPJD9ykOZ0QCjNeznPDr6whqZ9NGMNF12Mw@mail.gmail.com> Fixes: fb652fdfe837 ("macvlan/macvtap: Remove NETIF_F_UFO advertisement.") Reported-by: Michal Kubecek <mkubecek@suse.cz> Signed-off-by: Willem de Bruijn <willemb@google.com> Acked-by: Jason Wang <jasowang@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/tap.c2
-rw-r--r--drivers/net/tun.c2
-rw-r--r--include/linux/netdev_features.h4
-rw-r--r--include/linux/netdevice.h1
-rw-r--r--include/linux/skbuff.h2
-rw-r--r--include/linux/virtio_net.h5
-rw-r--r--include/net/ipv6.h1
-rw-r--r--net/core/dev.c3
-rw-r--r--net/ipv4/af_inet.c12
-rw-r--r--net/ipv4/udp_offload.c49
-rw-r--r--net/ipv6/output_core.c31
-rw-r--r--net/ipv6/udp_offload.c85
-rw-r--r--net/openvswitch/datapath.c14
-rw-r--r--net/openvswitch/flow.c6
-rw-r--r--net/sched/act_csum.c6
15 files changed, 209 insertions, 14 deletions
diff --git a/drivers/net/tap.c b/drivers/net/tap.c
index b13890953ebb..e9489b88407c 100644
--- a/drivers/net/tap.c
+++ b/drivers/net/tap.c
@@ -1077,7 +1077,7 @@ static long tap_ioctl(struct file *file, unsigned int cmd,
1077 case TUNSETOFFLOAD: 1077 case TUNSETOFFLOAD:
1078 /* let the user check for future flags */ 1078 /* let the user check for future flags */
1079 if (arg & ~(TUN_F_CSUM | TUN_F_TSO4 | TUN_F_TSO6 | 1079 if (arg & ~(TUN_F_CSUM | TUN_F_TSO4 | TUN_F_TSO6 |
1080 TUN_F_TSO_ECN)) 1080 TUN_F_TSO_ECN | TUN_F_UFO))
1081 return -EINVAL; 1081 return -EINVAL;
1082 1082
1083 rtnl_lock(); 1083 rtnl_lock();
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 5a2ea78a008f..6a7bde9bc4b2 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -2370,6 +2370,8 @@ static int set_offload(struct tun_struct *tun, unsigned long arg)
2370 features |= NETIF_F_TSO6; 2370 features |= NETIF_F_TSO6;
2371 arg &= ~(TUN_F_TSO4|TUN_F_TSO6); 2371 arg &= ~(TUN_F_TSO4|TUN_F_TSO6);
2372 } 2372 }
2373
2374 arg &= ~TUN_F_UFO;
2373 } 2375 }
2374 2376
2375 /* This gives the user a way to test for new features in future by 2377 /* This gives the user a way to test for new features in future by
diff --git a/include/linux/netdev_features.h b/include/linux/netdev_features.h
index dc8b4896b77b..b1b0ca7ccb2b 100644
--- a/include/linux/netdev_features.h
+++ b/include/linux/netdev_features.h
@@ -54,8 +54,9 @@ enum {
54 NETIF_F_GSO_TUNNEL_REMCSUM_BIT, /* ... TUNNEL with TSO & REMCSUM */ 54 NETIF_F_GSO_TUNNEL_REMCSUM_BIT, /* ... TUNNEL with TSO & REMCSUM */
55 NETIF_F_GSO_SCTP_BIT, /* ... SCTP fragmentation */ 55 NETIF_F_GSO_SCTP_BIT, /* ... SCTP fragmentation */
56 NETIF_F_GSO_ESP_BIT, /* ... ESP with TSO */ 56 NETIF_F_GSO_ESP_BIT, /* ... ESP with TSO */
57 NETIF_F_GSO_UDP_BIT, /* ... UFO, deprecated except tuntap */
57 /**/NETIF_F_GSO_LAST = /* last bit, see GSO_MASK */ 58 /**/NETIF_F_GSO_LAST = /* last bit, see GSO_MASK */
58 NETIF_F_GSO_ESP_BIT, 59 NETIF_F_GSO_UDP_BIT,
59 60
60 NETIF_F_FCOE_CRC_BIT, /* FCoE CRC32 */ 61 NETIF_F_FCOE_CRC_BIT, /* FCoE CRC32 */
61 NETIF_F_SCTP_CRC_BIT, /* SCTP checksum offload */ 62 NETIF_F_SCTP_CRC_BIT, /* SCTP checksum offload */
@@ -132,6 +133,7 @@ enum {
132#define NETIF_F_GSO_TUNNEL_REMCSUM __NETIF_F(GSO_TUNNEL_REMCSUM) 133#define NETIF_F_GSO_TUNNEL_REMCSUM __NETIF_F(GSO_TUNNEL_REMCSUM)
133#define NETIF_F_GSO_SCTP __NETIF_F(GSO_SCTP) 134#define NETIF_F_GSO_SCTP __NETIF_F(GSO_SCTP)
134#define NETIF_F_GSO_ESP __NETIF_F(GSO_ESP) 135#define NETIF_F_GSO_ESP __NETIF_F(GSO_ESP)
136#define NETIF_F_GSO_UDP __NETIF_F(GSO_UDP)
135#define NETIF_F_HW_VLAN_STAG_FILTER __NETIF_F(HW_VLAN_STAG_FILTER) 137#define NETIF_F_HW_VLAN_STAG_FILTER __NETIF_F(HW_VLAN_STAG_FILTER)
136#define NETIF_F_HW_VLAN_STAG_RX __NETIF_F(HW_VLAN_STAG_RX) 138#define NETIF_F_HW_VLAN_STAG_RX __NETIF_F(HW_VLAN_STAG_RX)
137#define NETIF_F_HW_VLAN_STAG_TX __NETIF_F(HW_VLAN_STAG_TX) 139#define NETIF_F_HW_VLAN_STAG_TX __NETIF_F(HW_VLAN_STAG_TX)
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 6b274bfe489f..ef789e1d679e 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -4140,6 +4140,7 @@ static inline bool net_gso_ok(netdev_features_t features, int gso_type)
4140 BUILD_BUG_ON(SKB_GSO_TUNNEL_REMCSUM != (NETIF_F_GSO_TUNNEL_REMCSUM >> NETIF_F_GSO_SHIFT)); 4140 BUILD_BUG_ON(SKB_GSO_TUNNEL_REMCSUM != (NETIF_F_GSO_TUNNEL_REMCSUM >> NETIF_F_GSO_SHIFT));
4141 BUILD_BUG_ON(SKB_GSO_SCTP != (NETIF_F_GSO_SCTP >> NETIF_F_GSO_SHIFT)); 4141 BUILD_BUG_ON(SKB_GSO_SCTP != (NETIF_F_GSO_SCTP >> NETIF_F_GSO_SHIFT));
4142 BUILD_BUG_ON(SKB_GSO_ESP != (NETIF_F_GSO_ESP >> NETIF_F_GSO_SHIFT)); 4142 BUILD_BUG_ON(SKB_GSO_ESP != (NETIF_F_GSO_ESP >> NETIF_F_GSO_SHIFT));
4143 BUILD_BUG_ON(SKB_GSO_UDP != (NETIF_F_GSO_UDP >> NETIF_F_GSO_SHIFT));
4143 4144
4144 return (features & feature) == feature; 4145 return (features & feature) == feature;
4145} 4146}
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index ed06e1c28fc7..bc486ef23f20 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -568,6 +568,8 @@ enum {
568 SKB_GSO_SCTP = 1 << 14, 568 SKB_GSO_SCTP = 1 << 14,
569 569
570 SKB_GSO_ESP = 1 << 15, 570 SKB_GSO_ESP = 1 << 15,
571
572 SKB_GSO_UDP = 1 << 16,
571}; 573};
572 574
573#if BITS_PER_LONG > 32 575#if BITS_PER_LONG > 32
diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h
index 210034c896e3..f144216febc6 100644
--- a/include/linux/virtio_net.h
+++ b/include/linux/virtio_net.h
@@ -9,7 +9,7 @@ static inline int virtio_net_hdr_to_skb(struct sk_buff *skb,
9 const struct virtio_net_hdr *hdr, 9 const struct virtio_net_hdr *hdr,
10 bool little_endian) 10 bool little_endian)
11{ 11{
12 unsigned short gso_type = 0; 12 unsigned int gso_type = 0;
13 13
14 if (hdr->gso_type != VIRTIO_NET_HDR_GSO_NONE) { 14 if (hdr->gso_type != VIRTIO_NET_HDR_GSO_NONE) {
15 switch (hdr->gso_type & ~VIRTIO_NET_HDR_GSO_ECN) { 15 switch (hdr->gso_type & ~VIRTIO_NET_HDR_GSO_ECN) {
@@ -19,6 +19,9 @@ static inline int virtio_net_hdr_to_skb(struct sk_buff *skb,
19 case VIRTIO_NET_HDR_GSO_TCPV6: 19 case VIRTIO_NET_HDR_GSO_TCPV6:
20 gso_type = SKB_GSO_TCPV6; 20 gso_type = SKB_GSO_TCPV6;
21 break; 21 break;
22 case VIRTIO_NET_HDR_GSO_UDP:
23 gso_type = SKB_GSO_UDP;
24 break;
22 default: 25 default:
23 return -EINVAL; 26 return -EINVAL;
24 } 27 }
diff --git a/include/net/ipv6.h b/include/net/ipv6.h
index ec14f0d5a3a1..f73797e2fa60 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -767,6 +767,7 @@ static inline int ipv6_addr_diff(const struct in6_addr *a1, const struct in6_add
767__be32 ipv6_select_ident(struct net *net, 767__be32 ipv6_select_ident(struct net *net,
768 const struct in6_addr *daddr, 768 const struct in6_addr *daddr,
769 const struct in6_addr *saddr); 769 const struct in6_addr *saddr);
770__be32 ipv6_proxy_select_ident(struct net *net, struct sk_buff *skb);
770 771
771int ip6_dst_hoplimit(struct dst_entry *dst); 772int ip6_dst_hoplimit(struct dst_entry *dst);
772 773
diff --git a/net/core/dev.c b/net/core/dev.c
index 8ee29f4f5fa9..bbba19112f02 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -2746,7 +2746,8 @@ EXPORT_SYMBOL(skb_mac_gso_segment);
2746static inline bool skb_needs_check(struct sk_buff *skb, bool tx_path) 2746static inline bool skb_needs_check(struct sk_buff *skb, bool tx_path)
2747{ 2747{
2748 if (tx_path) 2748 if (tx_path)
2749 return skb->ip_summed != CHECKSUM_PARTIAL; 2749 return skb->ip_summed != CHECKSUM_PARTIAL &&
2750 skb->ip_summed != CHECKSUM_UNNECESSARY;
2750 2751
2751 return skb->ip_summed == CHECKSUM_NONE; 2752 return skb->ip_summed == CHECKSUM_NONE;
2752} 2753}
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index ce4aa827be05..f00499a46927 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -1223,9 +1223,10 @@ EXPORT_SYMBOL(inet_sk_rebuild_header);
1223struct sk_buff *inet_gso_segment(struct sk_buff *skb, 1223struct sk_buff *inet_gso_segment(struct sk_buff *skb,
1224 netdev_features_t features) 1224 netdev_features_t features)
1225{ 1225{
1226 bool fixedid = false, gso_partial, encap; 1226 bool udpfrag = false, fixedid = false, gso_partial, encap;
1227 struct sk_buff *segs = ERR_PTR(-EINVAL); 1227 struct sk_buff *segs = ERR_PTR(-EINVAL);
1228 const struct net_offload *ops; 1228 const struct net_offload *ops;
1229 unsigned int offset = 0;
1229 struct iphdr *iph; 1230 struct iphdr *iph;
1230 int proto, tot_len; 1231 int proto, tot_len;
1231 int nhoff; 1232 int nhoff;
@@ -1260,6 +1261,7 @@ struct sk_buff *inet_gso_segment(struct sk_buff *skb,
1260 segs = ERR_PTR(-EPROTONOSUPPORT); 1261 segs = ERR_PTR(-EPROTONOSUPPORT);
1261 1262
1262 if (!skb->encapsulation || encap) { 1263 if (!skb->encapsulation || encap) {
1264 udpfrag = !!(skb_shinfo(skb)->gso_type & SKB_GSO_UDP);
1263 fixedid = !!(skb_shinfo(skb)->gso_type & SKB_GSO_TCP_FIXEDID); 1265 fixedid = !!(skb_shinfo(skb)->gso_type & SKB_GSO_TCP_FIXEDID);
1264 1266
1265 /* fixed ID is invalid if DF bit is not set */ 1267 /* fixed ID is invalid if DF bit is not set */
@@ -1279,7 +1281,13 @@ struct sk_buff *inet_gso_segment(struct sk_buff *skb,
1279 skb = segs; 1281 skb = segs;
1280 do { 1282 do {
1281 iph = (struct iphdr *)(skb_mac_header(skb) + nhoff); 1283 iph = (struct iphdr *)(skb_mac_header(skb) + nhoff);
1282 if (skb_is_gso(skb)) { 1284 if (udpfrag) {
1285 iph->frag_off = htons(offset >> 3);
1286 if (skb->next)
1287 iph->frag_off |= htons(IP_MF);
1288 offset += skb->len - nhoff - ihl;
1289 tot_len = skb->len - nhoff;
1290 } else if (skb_is_gso(skb)) {
1283 if (!fixedid) { 1291 if (!fixedid) {
1284 iph->id = htons(id); 1292 iph->id = htons(id);
1285 id += skb_shinfo(skb)->gso_segs; 1293 id += skb_shinfo(skb)->gso_segs;
diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c
index e360d55be555..01801b77bd0d 100644
--- a/net/ipv4/udp_offload.c
+++ b/net/ipv4/udp_offload.c
@@ -187,16 +187,57 @@ out_unlock:
187} 187}
188EXPORT_SYMBOL(skb_udp_tunnel_segment); 188EXPORT_SYMBOL(skb_udp_tunnel_segment);
189 189
190static struct sk_buff *udp4_tunnel_segment(struct sk_buff *skb, 190static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb,
191 netdev_features_t features) 191 netdev_features_t features)
192{ 192{
193 struct sk_buff *segs = ERR_PTR(-EINVAL); 193 struct sk_buff *segs = ERR_PTR(-EINVAL);
194 unsigned int mss;
195 __wsum csum;
196 struct udphdr *uh;
197 struct iphdr *iph;
194 198
195 if (skb->encapsulation && 199 if (skb->encapsulation &&
196 (skb_shinfo(skb)->gso_type & 200 (skb_shinfo(skb)->gso_type &
197 (SKB_GSO_UDP_TUNNEL|SKB_GSO_UDP_TUNNEL_CSUM))) 201 (SKB_GSO_UDP_TUNNEL|SKB_GSO_UDP_TUNNEL_CSUM))) {
198 segs = skb_udp_tunnel_segment(skb, features, false); 202 segs = skb_udp_tunnel_segment(skb, features, false);
203 goto out;
204 }
205
206 if (!pskb_may_pull(skb, sizeof(struct udphdr)))
207 goto out;
208
209 mss = skb_shinfo(skb)->gso_size;
210 if (unlikely(skb->len <= mss))
211 goto out;
212
213 /* Do software UFO. Complete and fill in the UDP checksum as
214 * HW cannot do checksum of UDP packets sent as multiple
215 * IP fragments.
216 */
199 217
218 uh = udp_hdr(skb);
219 iph = ip_hdr(skb);
220
221 uh->check = 0;
222 csum = skb_checksum(skb, 0, skb->len, 0);
223 uh->check = udp_v4_check(skb->len, iph->saddr, iph->daddr, csum);
224 if (uh->check == 0)
225 uh->check = CSUM_MANGLED_0;
226
227 skb->ip_summed = CHECKSUM_UNNECESSARY;
228
229 /* If there is no outer header we can fake a checksum offload
230 * due to the fact that we have already done the checksum in
231 * software prior to segmenting the frame.
232 */
233 if (!skb->encap_hdr_csum)
234 features |= NETIF_F_HW_CSUM;
235
236 /* Fragment the skb. IP headers of the fragments are updated in
237 * inet_gso_segment()
238 */
239 segs = skb_segment(skb, features);
240out:
200 return segs; 241 return segs;
201} 242}
202 243
@@ -330,7 +371,7 @@ static int udp4_gro_complete(struct sk_buff *skb, int nhoff)
330 371
331static const struct net_offload udpv4_offload = { 372static const struct net_offload udpv4_offload = {
332 .callbacks = { 373 .callbacks = {
333 .gso_segment = udp4_tunnel_segment, 374 .gso_segment = udp4_ufo_fragment,
334 .gro_receive = udp4_gro_receive, 375 .gro_receive = udp4_gro_receive,
335 .gro_complete = udp4_gro_complete, 376 .gro_complete = udp4_gro_complete,
336 }, 377 },
diff --git a/net/ipv6/output_core.c b/net/ipv6/output_core.c
index 4a7e5ffa5108..4fe7c90962dd 100644
--- a/net/ipv6/output_core.c
+++ b/net/ipv6/output_core.c
@@ -31,6 +31,37 @@ static u32 __ipv6_select_ident(struct net *net, u32 hashrnd,
31 return id; 31 return id;
32} 32}
33 33
34/* This function exists only for tap drivers that must support broken
35 * clients requesting UFO without specifying an IPv6 fragment ID.
36 *
37 * This is similar to ipv6_select_ident() but we use an independent hash
38 * seed to limit information leakage.
39 *
40 * The network header must be set before calling this.
41 */
42__be32 ipv6_proxy_select_ident(struct net *net, struct sk_buff *skb)
43{
44 static u32 ip6_proxy_idents_hashrnd __read_mostly;
45 struct in6_addr buf[2];
46 struct in6_addr *addrs;
47 u32 id;
48
49 addrs = skb_header_pointer(skb,
50 skb_network_offset(skb) +
51 offsetof(struct ipv6hdr, saddr),
52 sizeof(buf), buf);
53 if (!addrs)
54 return 0;
55
56 net_get_random_once(&ip6_proxy_idents_hashrnd,
57 sizeof(ip6_proxy_idents_hashrnd));
58
59 id = __ipv6_select_ident(net, ip6_proxy_idents_hashrnd,
60 &addrs[1], &addrs[0]);
61 return htonl(id);
62}
63EXPORT_SYMBOL_GPL(ipv6_proxy_select_ident);
64
34__be32 ipv6_select_ident(struct net *net, 65__be32 ipv6_select_ident(struct net *net,
35 const struct in6_addr *daddr, 66 const struct in6_addr *daddr,
36 const struct in6_addr *saddr) 67 const struct in6_addr *saddr)
diff --git a/net/ipv6/udp_offload.c b/net/ipv6/udp_offload.c
index 455fd4e39333..a0f89ad76f9d 100644
--- a/net/ipv6/udp_offload.c
+++ b/net/ipv6/udp_offload.c
@@ -17,15 +17,94 @@
17#include <net/ip6_checksum.h> 17#include <net/ip6_checksum.h>
18#include "ip6_offload.h" 18#include "ip6_offload.h"
19 19
20static struct sk_buff *udp6_tunnel_segment(struct sk_buff *skb, 20static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb,
21 netdev_features_t features) 21 netdev_features_t features)
22{ 22{
23 struct sk_buff *segs = ERR_PTR(-EINVAL); 23 struct sk_buff *segs = ERR_PTR(-EINVAL);
24 unsigned int mss;
25 unsigned int unfrag_ip6hlen, unfrag_len;
26 struct frag_hdr *fptr;
27 u8 *packet_start, *prevhdr;
28 u8 nexthdr;
29 u8 frag_hdr_sz = sizeof(struct frag_hdr);
30 __wsum csum;
31 int tnl_hlen;
32 int err;
33
34 mss = skb_shinfo(skb)->gso_size;
35 if (unlikely(skb->len <= mss))
36 goto out;
24 37
25 if (skb->encapsulation && skb_shinfo(skb)->gso_type & 38 if (skb->encapsulation && skb_shinfo(skb)->gso_type &
26 (SKB_GSO_UDP_TUNNEL|SKB_GSO_UDP_TUNNEL_CSUM)) 39 (SKB_GSO_UDP_TUNNEL|SKB_GSO_UDP_TUNNEL_CSUM))
27 segs = skb_udp_tunnel_segment(skb, features, true); 40 segs = skb_udp_tunnel_segment(skb, features, true);
41 else {
42 const struct ipv6hdr *ipv6h;
43 struct udphdr *uh;
44
45 if (!pskb_may_pull(skb, sizeof(struct udphdr)))
46 goto out;
47
48 /* Do software UFO. Complete and fill in the UDP checksum as HW cannot
49 * do checksum of UDP packets sent as multiple IP fragments.
50 */
51
52 uh = udp_hdr(skb);
53 ipv6h = ipv6_hdr(skb);
54
55 uh->check = 0;
56 csum = skb_checksum(skb, 0, skb->len, 0);
57 uh->check = udp_v6_check(skb->len, &ipv6h->saddr,
58 &ipv6h->daddr, csum);
59 if (uh->check == 0)
60 uh->check = CSUM_MANGLED_0;
61
62 skb->ip_summed = CHECKSUM_UNNECESSARY;
63
64 /* If there is no outer header we can fake a checksum offload
65 * due to the fact that we have already done the checksum in
66 * software prior to segmenting the frame.
67 */
68 if (!skb->encap_hdr_csum)
69 features |= NETIF_F_HW_CSUM;
70
71 /* Check if there is enough headroom to insert fragment header. */
72 tnl_hlen = skb_tnl_header_len(skb);
73 if (skb->mac_header < (tnl_hlen + frag_hdr_sz)) {
74 if (gso_pskb_expand_head(skb, tnl_hlen + frag_hdr_sz))
75 goto out;
76 }
77
78 /* Find the unfragmentable header and shift it left by frag_hdr_sz
79 * bytes to insert fragment header.
80 */
81 err = ip6_find_1stfragopt(skb, &prevhdr);
82 if (err < 0)
83 return ERR_PTR(err);
84 unfrag_ip6hlen = err;
85 nexthdr = *prevhdr;
86 *prevhdr = NEXTHDR_FRAGMENT;
87 unfrag_len = (skb_network_header(skb) - skb_mac_header(skb)) +
88 unfrag_ip6hlen + tnl_hlen;
89 packet_start = (u8 *) skb->head + SKB_GSO_CB(skb)->mac_offset;
90 memmove(packet_start-frag_hdr_sz, packet_start, unfrag_len);
91
92 SKB_GSO_CB(skb)->mac_offset -= frag_hdr_sz;
93 skb->mac_header -= frag_hdr_sz;
94 skb->network_header -= frag_hdr_sz;
95
96 fptr = (struct frag_hdr *)(skb_network_header(skb) + unfrag_ip6hlen);
97 fptr->nexthdr = nexthdr;
98 fptr->reserved = 0;
99 fptr->identification = ipv6_proxy_select_ident(dev_net(skb->dev), skb);
100
101 /* Fragment the skb. ipv6 header and the remaining fields of the
102 * fragment header are updated in ipv6_gso_segment()
103 */
104 segs = skb_segment(skb, features);
105 }
28 106
107out:
29 return segs; 108 return segs;
30} 109}
31 110
@@ -75,7 +154,7 @@ static int udp6_gro_complete(struct sk_buff *skb, int nhoff)
75 154
76static const struct net_offload udpv6_offload = { 155static const struct net_offload udpv6_offload = {
77 .callbacks = { 156 .callbacks = {
78 .gso_segment = udp6_tunnel_segment, 157 .gso_segment = udp6_ufo_fragment,
79 .gro_receive = udp6_gro_receive, 158 .gro_receive = udp6_gro_receive,
80 .gro_complete = udp6_gro_complete, 159 .gro_complete = udp6_gro_complete,
81 }, 160 },
diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c
index 0dab33fb9844..99cfafc2a139 100644
--- a/net/openvswitch/datapath.c
+++ b/net/openvswitch/datapath.c
@@ -308,6 +308,8 @@ static int queue_gso_packets(struct datapath *dp, struct sk_buff *skb,
308 const struct dp_upcall_info *upcall_info, 308 const struct dp_upcall_info *upcall_info,
309 uint32_t cutlen) 309 uint32_t cutlen)
310{ 310{
311 unsigned short gso_type = skb_shinfo(skb)->gso_type;
312 struct sw_flow_key later_key;
311 struct sk_buff *segs, *nskb; 313 struct sk_buff *segs, *nskb;
312 int err; 314 int err;
313 315
@@ -318,9 +320,21 @@ static int queue_gso_packets(struct datapath *dp, struct sk_buff *skb,
318 if (segs == NULL) 320 if (segs == NULL)
319 return -EINVAL; 321 return -EINVAL;
320 322
323 if (gso_type & SKB_GSO_UDP) {
324 /* The initial flow key extracted by ovs_flow_key_extract()
325 * in this case is for a first fragment, so we need to
326 * properly mark later fragments.
327 */
328 later_key = *key;
329 later_key.ip.frag = OVS_FRAG_TYPE_LATER;
330 }
331
321 /* Queue all of the segments. */ 332 /* Queue all of the segments. */
322 skb = segs; 333 skb = segs;
323 do { 334 do {
335 if (gso_type & SKB_GSO_UDP && skb != segs)
336 key = &later_key;
337
324 err = queue_userspace_packet(dp, skb, key, upcall_info, cutlen); 338 err = queue_userspace_packet(dp, skb, key, upcall_info, cutlen);
325 if (err) 339 if (err)
326 break; 340 break;
diff --git a/net/openvswitch/flow.c b/net/openvswitch/flow.c
index 864ddb1e3642..dbe2379329c5 100644
--- a/net/openvswitch/flow.c
+++ b/net/openvswitch/flow.c
@@ -631,7 +631,8 @@ static int key_extract(struct sk_buff *skb, struct sw_flow_key *key)
631 key->ip.frag = OVS_FRAG_TYPE_LATER; 631 key->ip.frag = OVS_FRAG_TYPE_LATER;
632 return 0; 632 return 0;
633 } 633 }
634 if (nh->frag_off & htons(IP_MF)) 634 if (nh->frag_off & htons(IP_MF) ||
635 skb_shinfo(skb)->gso_type & SKB_GSO_UDP)
635 key->ip.frag = OVS_FRAG_TYPE_FIRST; 636 key->ip.frag = OVS_FRAG_TYPE_FIRST;
636 else 637 else
637 key->ip.frag = OVS_FRAG_TYPE_NONE; 638 key->ip.frag = OVS_FRAG_TYPE_NONE;
@@ -747,6 +748,9 @@ static int key_extract(struct sk_buff *skb, struct sw_flow_key *key)
747 748
748 if (key->ip.frag == OVS_FRAG_TYPE_LATER) 749 if (key->ip.frag == OVS_FRAG_TYPE_LATER)
749 return 0; 750 return 0;
751 if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP)
752 key->ip.frag = OVS_FRAG_TYPE_FIRST;
753
750 /* Transport layer. */ 754 /* Transport layer. */
751 if (key->ip.proto == NEXTHDR_TCP) { 755 if (key->ip.proto == NEXTHDR_TCP) {
752 if (tcphdr_ok(skb)) { 756 if (tcphdr_ok(skb)) {
diff --git a/net/sched/act_csum.c b/net/sched/act_csum.c
index 1c40caadcff9..d836f998117b 100644
--- a/net/sched/act_csum.c
+++ b/net/sched/act_csum.c
@@ -229,6 +229,9 @@ static int tcf_csum_ipv4_udp(struct sk_buff *skb, unsigned int ihl,
229 const struct iphdr *iph; 229 const struct iphdr *iph;
230 u16 ul; 230 u16 ul;
231 231
232 if (skb_is_gso(skb) && skb_shinfo(skb)->gso_type & SKB_GSO_UDP)
233 return 1;
234
232 /* 235 /*
233 * Support both UDP and UDPLITE checksum algorithms, Don't use 236 * Support both UDP and UDPLITE checksum algorithms, Don't use
234 * udph->len to get the real length without any protocol check, 237 * udph->len to get the real length without any protocol check,
@@ -282,6 +285,9 @@ static int tcf_csum_ipv6_udp(struct sk_buff *skb, unsigned int ihl,
282 const struct ipv6hdr *ip6h; 285 const struct ipv6hdr *ip6h;
283 u16 ul; 286 u16 ul;
284 287
288 if (skb_is_gso(skb) && skb_shinfo(skb)->gso_type & SKB_GSO_UDP)
289 return 1;
290
285 /* 291 /*
286 * Support both UDP and UDPLITE checksum algorithms, Don't use 292 * Support both UDP and UDPLITE checksum algorithms, Don't use
287 * udph->len to get the real length without any protocol check, 293 * udph->len to get the real length without any protocol check,