aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVlad Yasevich <vyasevic@redhat.com>2012-11-15 03:49:18 -0500
committerDavid S. Miller <davem@davemloft.net>2012-11-15 17:36:18 -0500
commit5edbb07dc9474b7d4cd4391a2e6551ad067a0f96 (patch)
tree911a801d6101d81fb6d54dd6dde18258efb8066b
parent8663e02aba154e04679c9bb1665af52021d32547 (diff)
ipv6: Separate out UDP offload functionality
Pull UDP GSO code into a separate file in preparation for moving the code out of the module. Signed-off-by: Vlad Yasevich <vyasevic@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--net/ipv6/Makefile2
-rw-r--r--net/ipv6/ip6_offload.h3
-rw-r--r--net/ipv6/udp.c104
-rw-r--r--net/ipv6/udp_offload.c122
4 files changed, 130 insertions, 101 deletions
diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile
index f47ad9f6ea2c..04b5c9657f83 100644
--- a/net/ipv6/Makefile
+++ b/net/ipv6/Makefile
@@ -10,7 +10,7 @@ ipv6-objs := af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o \
10 raw.o protocol.o icmp.o mcast.o reassembly.o tcp_ipv6.o \ 10 raw.o protocol.o icmp.o mcast.o reassembly.o tcp_ipv6.o \
11 exthdrs.o datagram.o ip6_flowlabel.o inet6_connection_sock.o 11 exthdrs.o datagram.o ip6_flowlabel.o inet6_connection_sock.o
12 12
13ipv6-offload := ip6_offload.o tcpv6_offload.o 13ipv6-offload := ip6_offload.o tcpv6_offload.o udp_offload.o
14 14
15ipv6-$(CONFIG_SYSCTL) = sysctl_net_ipv6.o 15ipv6-$(CONFIG_SYSCTL) = sysctl_net_ipv6.o
16ipv6-$(CONFIG_IPV6_MROUTE) += ip6mr.o 16ipv6-$(CONFIG_IPV6_MROUTE) += ip6mr.o
diff --git a/net/ipv6/ip6_offload.h b/net/ipv6/ip6_offload.h
index 1891946ceedb..dff79362ad04 100644
--- a/net/ipv6/ip6_offload.h
+++ b/net/ipv6/ip6_offload.h
@@ -11,6 +11,9 @@
11#ifndef __ip6_offload_h 11#ifndef __ip6_offload_h
12#define __ip6_offload_h 12#define __ip6_offload_h
13 13
14int udp_offload_init(void);
15void udp_offload_cleanup(void);
16
14int tcpv6_offload_init(void); 17int tcpv6_offload_init(void);
15void tcpv6_offload_cleanup(void); 18void tcpv6_offload_cleanup(void);
16 19
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index e4cc1f41012d..013fef740d51 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -50,6 +50,7 @@
50#include <linux/seq_file.h> 50#include <linux/seq_file.h>
51#include <trace/events/skb.h> 51#include <trace/events/skb.h>
52#include "udp_impl.h" 52#include "udp_impl.h"
53#include "ip6_offload.h"
53 54
54int ipv6_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2) 55int ipv6_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2)
55{ 56{
@@ -1343,109 +1344,12 @@ int compat_udpv6_getsockopt(struct sock *sk, int level, int optname,
1343} 1344}
1344#endif 1345#endif
1345 1346
1346static int udp6_ufo_send_check(struct sk_buff *skb)
1347{
1348 const struct ipv6hdr *ipv6h;
1349 struct udphdr *uh;
1350
1351 if (!pskb_may_pull(skb, sizeof(*uh)))
1352 return -EINVAL;
1353
1354 ipv6h = ipv6_hdr(skb);
1355 uh = udp_hdr(skb);
1356
1357 uh->check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr, skb->len,
1358 IPPROTO_UDP, 0);
1359 skb->csum_start = skb_transport_header(skb) - skb->head;
1360 skb->csum_offset = offsetof(struct udphdr, check);
1361 skb->ip_summed = CHECKSUM_PARTIAL;
1362 return 0;
1363}
1364
1365static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb,
1366 netdev_features_t features)
1367{
1368 struct sk_buff *segs = ERR_PTR(-EINVAL);
1369 unsigned int mss;
1370 unsigned int unfrag_ip6hlen, unfrag_len;
1371 struct frag_hdr *fptr;
1372 u8 *mac_start, *prevhdr;
1373 u8 nexthdr;
1374 u8 frag_hdr_sz = sizeof(struct frag_hdr);
1375 int offset;
1376 __wsum csum;
1377
1378 mss = skb_shinfo(skb)->gso_size;
1379 if (unlikely(skb->len <= mss))
1380 goto out;
1381
1382 if (skb_gso_ok(skb, features | NETIF_F_GSO_ROBUST)) {
1383 /* Packet is from an untrusted source, reset gso_segs. */
1384 int type = skb_shinfo(skb)->gso_type;
1385
1386 if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY) ||
1387 !(type & (SKB_GSO_UDP))))
1388 goto out;
1389
1390 skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(skb->len, mss);
1391
1392 segs = NULL;
1393 goto out;
1394 }
1395
1396 /* Do software UFO. Complete and fill in the UDP checksum as HW cannot
1397 * do checksum of UDP packets sent as multiple IP fragments.
1398 */
1399 offset = skb_checksum_start_offset(skb);
1400 csum = skb_checksum(skb, offset, skb->len - offset, 0);
1401 offset += skb->csum_offset;
1402 *(__sum16 *)(skb->data + offset) = csum_fold(csum);
1403 skb->ip_summed = CHECKSUM_NONE;
1404
1405 /* Check if there is enough headroom to insert fragment header. */
1406 if ((skb_mac_header(skb) < skb->head + frag_hdr_sz) &&
1407 pskb_expand_head(skb, frag_hdr_sz, 0, GFP_ATOMIC))
1408 goto out;
1409
1410 /* Find the unfragmentable header and shift it left by frag_hdr_sz
1411 * bytes to insert fragment header.
1412 */
1413 unfrag_ip6hlen = ip6_find_1stfragopt(skb, &prevhdr);
1414 nexthdr = *prevhdr;
1415 *prevhdr = NEXTHDR_FRAGMENT;
1416 unfrag_len = skb_network_header(skb) - skb_mac_header(skb) +
1417 unfrag_ip6hlen;
1418 mac_start = skb_mac_header(skb);
1419 memmove(mac_start-frag_hdr_sz, mac_start, unfrag_len);
1420
1421 skb->mac_header -= frag_hdr_sz;
1422 skb->network_header -= frag_hdr_sz;
1423
1424 fptr = (struct frag_hdr *)(skb_network_header(skb) + unfrag_ip6hlen);
1425 fptr->nexthdr = nexthdr;
1426 fptr->reserved = 0;
1427 ipv6_select_ident(fptr, (struct rt6_info *)skb_dst(skb));
1428
1429 /* Fragment the skb. ipv6 header and the remaining fields of the
1430 * fragment header are updated in ipv6_gso_segment()
1431 */
1432 segs = skb_segment(skb, features);
1433
1434out:
1435 return segs;
1436}
1437
1438static const struct inet6_protocol udpv6_protocol = { 1347static const struct inet6_protocol udpv6_protocol = {
1439 .handler = udpv6_rcv, 1348 .handler = udpv6_rcv,
1440 .err_handler = udpv6_err, 1349 .err_handler = udpv6_err,
1441 .flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL, 1350 .flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL,
1442}; 1351};
1443 1352
1444static const struct net_offload udpv6_offload = {
1445 .gso_send_check = udp6_ufo_send_check,
1446 .gso_segment = udp6_ufo_fragment,
1447};
1448
1449/* ------------------------------------------------------------------------ */ 1353/* ------------------------------------------------------------------------ */
1450#ifdef CONFIG_PROC_FS 1354#ifdef CONFIG_PROC_FS
1451 1355
@@ -1568,7 +1472,7 @@ int __init udpv6_init(void)
1568{ 1472{
1569 int ret; 1473 int ret;
1570 1474
1571 ret = inet6_add_offload(&udpv6_offload, IPPROTO_UDP); 1475 ret = udp_offload_init();
1572 if (ret) 1476 if (ret)
1573 goto out; 1477 goto out;
1574 1478
@@ -1585,7 +1489,7 @@ out:
1585out_udpv6_protocol: 1489out_udpv6_protocol:
1586 inet6_del_protocol(&udpv6_protocol, IPPROTO_UDP); 1490 inet6_del_protocol(&udpv6_protocol, IPPROTO_UDP);
1587out_offload: 1491out_offload:
1588 inet6_del_offload(&udpv6_offload, IPPROTO_UDP); 1492 udp_offload_cleanup();
1589 goto out; 1493 goto out;
1590} 1494}
1591 1495
@@ -1593,5 +1497,5 @@ void udpv6_exit(void)
1593{ 1497{
1594 inet6_unregister_protosw(&udpv6_protosw); 1498 inet6_unregister_protosw(&udpv6_protosw);
1595 inet6_del_protocol(&udpv6_protocol, IPPROTO_UDP); 1499 inet6_del_protocol(&udpv6_protocol, IPPROTO_UDP);
1596 inet6_del_offload(&udpv6_offload, IPPROTO_UDP); 1500 udp_offload_cleanup();
1597} 1501}
diff --git a/net/ipv6/udp_offload.c b/net/ipv6/udp_offload.c
new file mode 100644
index 000000000000..f964d2b366c8
--- /dev/null
+++ b/net/ipv6/udp_offload.c
@@ -0,0 +1,122 @@
1/*
2 * IPV6 GSO/GRO offload support
3 * Linux INET6 implementation
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version
8 * 2 of the License, or (at your option) any later version.
9 *
10 * UDPv6 GSO support
11 */
12#include <linux/skbuff.h>
13#include <net/protocol.h>
14#include <net/ipv6.h>
15#include <net/udp.h>
16#include "ip6_offload.h"
17
18static int udp6_ufo_send_check(struct sk_buff *skb)
19{
20 const struct ipv6hdr *ipv6h;
21 struct udphdr *uh;
22
23 if (!pskb_may_pull(skb, sizeof(*uh)))
24 return -EINVAL;
25
26 ipv6h = ipv6_hdr(skb);
27 uh = udp_hdr(skb);
28
29 uh->check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr, skb->len,
30 IPPROTO_UDP, 0);
31 skb->csum_start = skb_transport_header(skb) - skb->head;
32 skb->csum_offset = offsetof(struct udphdr, check);
33 skb->ip_summed = CHECKSUM_PARTIAL;
34 return 0;
35}
36
37static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb,
38 netdev_features_t features)
39{
40 struct sk_buff *segs = ERR_PTR(-EINVAL);
41 unsigned int mss;
42 unsigned int unfrag_ip6hlen, unfrag_len;
43 struct frag_hdr *fptr;
44 u8 *mac_start, *prevhdr;
45 u8 nexthdr;
46 u8 frag_hdr_sz = sizeof(struct frag_hdr);
47 int offset;
48 __wsum csum;
49
50 mss = skb_shinfo(skb)->gso_size;
51 if (unlikely(skb->len <= mss))
52 goto out;
53
54 if (skb_gso_ok(skb, features | NETIF_F_GSO_ROBUST)) {
55 /* Packet is from an untrusted source, reset gso_segs. */
56 int type = skb_shinfo(skb)->gso_type;
57
58 if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY) ||
59 !(type & (SKB_GSO_UDP))))
60 goto out;
61
62 skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(skb->len, mss);
63
64 segs = NULL;
65 goto out;
66 }
67
68 /* Do software UFO. Complete and fill in the UDP checksum as HW cannot
69 * do checksum of UDP packets sent as multiple IP fragments.
70 */
71 offset = skb_checksum_start_offset(skb);
72 csum = skb_checksum(skb, offset, skb->len - offset, 0);
73 offset += skb->csum_offset;
74 *(__sum16 *)(skb->data + offset) = csum_fold(csum);
75 skb->ip_summed = CHECKSUM_NONE;
76
77 /* Check if there is enough headroom to insert fragment header. */
78 if ((skb_mac_header(skb) < skb->head + frag_hdr_sz) &&
79 pskb_expand_head(skb, frag_hdr_sz, 0, GFP_ATOMIC))
80 goto out;
81
82 /* Find the unfragmentable header and shift it left by frag_hdr_sz
83 * bytes to insert fragment header.
84 */
85 unfrag_ip6hlen = ip6_find_1stfragopt(skb, &prevhdr);
86 nexthdr = *prevhdr;
87 *prevhdr = NEXTHDR_FRAGMENT;
88 unfrag_len = skb_network_header(skb) - skb_mac_header(skb) +
89 unfrag_ip6hlen;
90 mac_start = skb_mac_header(skb);
91 memmove(mac_start-frag_hdr_sz, mac_start, unfrag_len);
92
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 ipv6_select_ident(fptr, (struct rt6_info *)skb_dst(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
106out:
107 return segs;
108}
109static const struct net_offload udpv6_offload = {
110 .gso_send_check = udp6_ufo_send_check,
111 .gso_segment = udp6_ufo_fragment,
112};
113
114int __init udp_offload_init(void)
115{
116 return inet6_add_offload(&udpv6_offload, IPPROTO_UDP);
117}
118
119void udp_offload_cleanup(void)
120{
121 inet6_del_offload(&udpv6_offload, IPPROTO_UDP);
122}