aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorPravin B Shelar <pshelar@nicira.com>2013-03-07 08:21:51 -0500
committerDavid S. Miller <davem@davemloft.net>2013-03-09 16:09:17 -0500
commit731362674580cb0c696cd1b1a03d8461a10cf90a (patch)
tree29c6e106fbe52b31cacf454d4adc50750694a7b1 /net
parentaefbd2b3c2a9c657605e4337f9919d6c6273e8e6 (diff)
tunneling: Add generic Tunnel segmentation.
Adds generic tunneling offloading support for IPv4-UDP based tunnels. GSO type is added to request this offload for a skb. netdev feature NETIF_F_UDP_TUNNEL is added for hardware offloaded udp-tunnel support. Currently no device supports this feature, software offload is used. This can be used by tunneling protocols like VXLAN. CC: Jesse Gross <jesse@nicira.com> Signed-off-by: Pravin B Shelar <pshelar@nicira.com> Acked-by: Stephen Hemminger <stephen@networkplumber.org> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/core/ethtool.c1
-rw-r--r--net/ipv4/af_inet.c6
-rw-r--r--net/ipv4/tcp.c1
-rw-r--r--net/ipv4/udp.c115
-rw-r--r--net/ipv6/ip6_offload.c1
-rw-r--r--net/ipv6/udp_offload.c8
6 files changed, 105 insertions, 27 deletions
diff --git a/net/core/ethtool.c b/net/core/ethtool.c
index 3e9b2c3e30f0..adc1351e6873 100644
--- a/net/core/ethtool.c
+++ b/net/core/ethtool.c
@@ -78,6 +78,7 @@ static const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN]
78 [NETIF_F_TSO6_BIT] = "tx-tcp6-segmentation", 78 [NETIF_F_TSO6_BIT] = "tx-tcp6-segmentation",
79 [NETIF_F_FSO_BIT] = "tx-fcoe-segmentation", 79 [NETIF_F_FSO_BIT] = "tx-fcoe-segmentation",
80 [NETIF_F_GSO_GRE_BIT] = "tx-gre-segmentation", 80 [NETIF_F_GSO_GRE_BIT] = "tx-gre-segmentation",
81 [NETIF_F_GSO_UDP_TUNNEL_BIT] = "tx-udp_tnl-segmentation",
81 82
82 [NETIF_F_FCOE_CRC_BIT] = "tx-checksum-fcoe-crc", 83 [NETIF_F_FCOE_CRC_BIT] = "tx-checksum-fcoe-crc",
83 [NETIF_F_SCTP_CSUM_BIT] = "tx-checksum-sctp", 84 [NETIF_F_SCTP_CSUM_BIT] = "tx-checksum-sctp",
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index dc3f677360a5..9e5882caf8a7 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -1283,6 +1283,7 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb,
1283 int ihl; 1283 int ihl;
1284 int id; 1284 int id;
1285 unsigned int offset = 0; 1285 unsigned int offset = 0;
1286 bool tunnel;
1286 1287
1287 if (unlikely(skb_shinfo(skb)->gso_type & 1288 if (unlikely(skb_shinfo(skb)->gso_type &
1288 ~(SKB_GSO_TCPV4 | 1289 ~(SKB_GSO_TCPV4 |
@@ -1290,6 +1291,7 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb,
1290 SKB_GSO_DODGY | 1291 SKB_GSO_DODGY |
1291 SKB_GSO_TCP_ECN | 1292 SKB_GSO_TCP_ECN |
1292 SKB_GSO_GRE | 1293 SKB_GSO_GRE |
1294 SKB_GSO_UDP_TUNNEL |
1293 0))) 1295 0)))
1294 goto out; 1296 goto out;
1295 1297
@@ -1304,6 +1306,8 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb,
1304 if (unlikely(!pskb_may_pull(skb, ihl))) 1306 if (unlikely(!pskb_may_pull(skb, ihl)))
1305 goto out; 1307 goto out;
1306 1308
1309 tunnel = !!skb->encapsulation;
1310
1307 __skb_pull(skb, ihl); 1311 __skb_pull(skb, ihl);
1308 skb_reset_transport_header(skb); 1312 skb_reset_transport_header(skb);
1309 iph = ip_hdr(skb); 1313 iph = ip_hdr(skb);
@@ -1323,7 +1327,7 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb,
1323 skb = segs; 1327 skb = segs;
1324 do { 1328 do {
1325 iph = ip_hdr(skb); 1329 iph = ip_hdr(skb);
1326 if (proto == IPPROTO_UDP) { 1330 if (!tunnel && proto == IPPROTO_UDP) {
1327 iph->id = htons(id); 1331 iph->id = htons(id);
1328 iph->frag_off = htons(offset >> 3); 1332 iph->frag_off = htons(offset >> 3);
1329 if (skb->next != NULL) 1333 if (skb->next != NULL)
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 47e854fcae24..8d14573ade77 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -3044,6 +3044,7 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb,
3044 SKB_GSO_TCP_ECN | 3044 SKB_GSO_TCP_ECN |
3045 SKB_GSO_TCPV6 | 3045 SKB_GSO_TCPV6 |
3046 SKB_GSO_GRE | 3046 SKB_GSO_GRE |
3047 SKB_GSO_UDP_TUNNEL |
3047 0) || 3048 0) ||
3048 !(type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6)))) 3049 !(type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))))
3049 goto out; 3050 goto out;
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 265c42cf963c..41760e043bf5 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -2272,31 +2272,88 @@ void __init udp_init(void)
2272 2272
2273int udp4_ufo_send_check(struct sk_buff *skb) 2273int udp4_ufo_send_check(struct sk_buff *skb)
2274{ 2274{
2275 const struct iphdr *iph; 2275 if (!pskb_may_pull(skb, sizeof(struct udphdr)))
2276 struct udphdr *uh;
2277
2278 if (!pskb_may_pull(skb, sizeof(*uh)))
2279 return -EINVAL; 2276 return -EINVAL;
2280 2277
2281 iph = ip_hdr(skb); 2278 if (likely(!skb->encapsulation)) {
2282 uh = udp_hdr(skb); 2279 const struct iphdr *iph;
2280 struct udphdr *uh;
2283 2281
2284 uh->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, skb->len, 2282 iph = ip_hdr(skb);
2285 IPPROTO_UDP, 0); 2283 uh = udp_hdr(skb);
2286 skb->csum_start = skb_transport_header(skb) - skb->head; 2284
2287 skb->csum_offset = offsetof(struct udphdr, check); 2285 uh->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, skb->len,
2288 skb->ip_summed = CHECKSUM_PARTIAL; 2286 IPPROTO_UDP, 0);
2287 skb->csum_start = skb_transport_header(skb) - skb->head;
2288 skb->csum_offset = offsetof(struct udphdr, check);
2289 skb->ip_summed = CHECKSUM_PARTIAL;
2290 }
2289 return 0; 2291 return 0;
2290} 2292}
2291 2293
2294static struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
2295 netdev_features_t features)
2296{
2297 struct sk_buff *segs = ERR_PTR(-EINVAL);
2298 int mac_len = skb->mac_len;
2299 int tnl_hlen = skb_inner_mac_header(skb) - skb_transport_header(skb);
2300 int outer_hlen;
2301 netdev_features_t enc_features;
2302
2303 if (unlikely(!pskb_may_pull(skb, tnl_hlen)))
2304 goto out;
2305
2306 skb->encapsulation = 0;
2307 __skb_pull(skb, tnl_hlen);
2308 skb_reset_mac_header(skb);
2309 skb_set_network_header(skb, skb_inner_network_offset(skb));
2310 skb->mac_len = skb_inner_network_offset(skb);
2311
2312 /* segment inner packet. */
2313 enc_features = skb->dev->hw_enc_features & netif_skb_features(skb);
2314 segs = skb_mac_gso_segment(skb, enc_features);
2315 if (!segs || IS_ERR(segs))
2316 goto out;
2317
2318 outer_hlen = skb_tnl_header_len(skb);
2319 skb = segs;
2320 do {
2321 struct udphdr *uh;
2322 int udp_offset = outer_hlen - tnl_hlen;
2323
2324 skb->mac_len = mac_len;
2325
2326 skb_push(skb, outer_hlen);
2327 skb_reset_mac_header(skb);
2328 skb_set_network_header(skb, mac_len);
2329 skb_set_transport_header(skb, udp_offset);
2330 uh = udp_hdr(skb);
2331 uh->len = htons(skb->len - udp_offset);
2332
2333 /* csum segment if tunnel sets skb with csum. */
2334 if (unlikely(uh->check)) {
2335 struct iphdr *iph = ip_hdr(skb);
2336
2337 uh->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr,
2338 skb->len - udp_offset,
2339 IPPROTO_UDP, 0);
2340 uh->check = csum_fold(skb_checksum(skb, udp_offset,
2341 skb->len - udp_offset, 0));
2342 if (uh->check == 0)
2343 uh->check = CSUM_MANGLED_0;
2344
2345 }
2346 skb->ip_summed = CHECKSUM_NONE;
2347 } while ((skb = skb->next));
2348out:
2349 return segs;
2350}
2351
2292struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb, 2352struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb,
2293 netdev_features_t features) 2353 netdev_features_t features)
2294{ 2354{
2295 struct sk_buff *segs = ERR_PTR(-EINVAL); 2355 struct sk_buff *segs = ERR_PTR(-EINVAL);
2296 unsigned int mss; 2356 unsigned int mss;
2297 int offset;
2298 __wsum csum;
2299
2300 mss = skb_shinfo(skb)->gso_size; 2357 mss = skb_shinfo(skb)->gso_size;
2301 if (unlikely(skb->len <= mss)) 2358 if (unlikely(skb->len <= mss))
2302 goto out; 2359 goto out;
@@ -2306,6 +2363,7 @@ struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb,
2306 int type = skb_shinfo(skb)->gso_type; 2363 int type = skb_shinfo(skb)->gso_type;
2307 2364
2308 if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY | 2365 if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY |
2366 SKB_GSO_UDP_TUNNEL |
2309 SKB_GSO_GRE) || 2367 SKB_GSO_GRE) ||
2310 !(type & (SKB_GSO_UDP)))) 2368 !(type & (SKB_GSO_UDP))))
2311 goto out; 2369 goto out;
@@ -2316,20 +2374,27 @@ struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb,
2316 goto out; 2374 goto out;
2317 } 2375 }
2318 2376
2319 /* Do software UFO. Complete and fill in the UDP checksum as HW cannot
2320 * do checksum of UDP packets sent as multiple IP fragments.
2321 */
2322 offset = skb_checksum_start_offset(skb);
2323 csum = skb_checksum(skb, offset, skb->len - offset, 0);
2324 offset += skb->csum_offset;
2325 *(__sum16 *)(skb->data + offset) = csum_fold(csum);
2326 skb->ip_summed = CHECKSUM_NONE;
2327
2328 /* Fragment the skb. IP headers of the fragments are updated in 2377 /* Fragment the skb. IP headers of the fragments are updated in
2329 * inet_gso_segment() 2378 * inet_gso_segment()
2330 */ 2379 */
2331 segs = skb_segment(skb, features); 2380 if (skb->encapsulation && skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL)
2381 segs = skb_udp_tunnel_segment(skb, features);
2382 else {
2383 int offset;
2384 __wsum csum;
2385
2386 /* Do software UFO. Complete and fill in the UDP checksum as
2387 * HW cannot do checksum of UDP packets sent as multiple
2388 * IP fragments.
2389 */
2390 offset = skb_checksum_start_offset(skb);
2391 csum = skb_checksum(skb, offset, skb->len - offset, 0);
2392 offset += skb->csum_offset;
2393 *(__sum16 *)(skb->data + offset) = csum_fold(csum);
2394 skb->ip_summed = CHECKSUM_NONE;
2395
2396 segs = skb_segment(skb, features);
2397 }
2332out: 2398out:
2333 return segs; 2399 return segs;
2334} 2400}
2335
diff --git a/net/ipv6/ip6_offload.c b/net/ipv6/ip6_offload.c
index 7a0d25a5479c..71b766ee821d 100644
--- a/net/ipv6/ip6_offload.c
+++ b/net/ipv6/ip6_offload.c
@@ -97,6 +97,7 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb,
97 SKB_GSO_DODGY | 97 SKB_GSO_DODGY |
98 SKB_GSO_TCP_ECN | 98 SKB_GSO_TCP_ECN |
99 SKB_GSO_GRE | 99 SKB_GSO_GRE |
100 SKB_GSO_UDP_TUNNEL |
100 SKB_GSO_TCPV6 | 101 SKB_GSO_TCPV6 |
101 0))) 102 0)))
102 goto out; 103 goto out;
diff --git a/net/ipv6/udp_offload.c b/net/ipv6/udp_offload.c
index cf05cf073c51..3bb3a891a424 100644
--- a/net/ipv6/udp_offload.c
+++ b/net/ipv6/udp_offload.c
@@ -21,6 +21,10 @@ static int udp6_ufo_send_check(struct sk_buff *skb)
21 const struct ipv6hdr *ipv6h; 21 const struct ipv6hdr *ipv6h;
22 struct udphdr *uh; 22 struct udphdr *uh;
23 23
24 /* UDP Tunnel offload on ipv6 is not yet supported. */
25 if (skb->encapsulation)
26 return -EINVAL;
27
24 if (!pskb_may_pull(skb, sizeof(*uh))) 28 if (!pskb_may_pull(skb, sizeof(*uh)))
25 return -EINVAL; 29 return -EINVAL;
26 30
@@ -56,7 +60,9 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb,
56 /* Packet is from an untrusted source, reset gso_segs. */ 60 /* Packet is from an untrusted source, reset gso_segs. */
57 int type = skb_shinfo(skb)->gso_type; 61 int type = skb_shinfo(skb)->gso_type;
58 62
59 if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY | 63 if (unlikely(type & ~(SKB_GSO_UDP |
64 SKB_GSO_DODGY |
65 SKB_GSO_UDP_TUNNEL |
60 SKB_GSO_GRE) || 66 SKB_GSO_GRE) ||
61 !(type & (SKB_GSO_UDP)))) 67 !(type & (SKB_GSO_UDP))))
62 goto out; 68 goto out;