Branch data Line data Source code
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 <net/ip6_checksum.h>
17 : : #include "ip6_offload.h"
18 : :
19 : 0 : static int udp6_ufo_send_check(struct sk_buff *skb)
20 : : {
21 : : const struct ipv6hdr *ipv6h;
22 : : struct udphdr *uh;
23 : :
24 [ # # ]: 0 : if (!pskb_may_pull(skb, sizeof(*uh)))
25 : : return -EINVAL;
26 : :
27 [ # # ]: 0 : if (likely(!skb->encapsulation)) {
28 : : ipv6h = ipv6_hdr(skb);
29 : : uh = udp_hdr(skb);
30 : :
31 : 0 : uh->check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr, skb->len,
32 : : IPPROTO_UDP, 0);
33 : 0 : skb->csum_start = skb_transport_header(skb) - skb->head;
34 : 0 : skb->csum_offset = offsetof(struct udphdr, check);
35 : 0 : skb->ip_summed = CHECKSUM_PARTIAL;
36 : : }
37 : :
38 : : return 0;
39 : : }
40 : :
41 : 0 : static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb,
42 : : netdev_features_t features)
43 : : {
44 : : struct sk_buff *segs = ERR_PTR(-EINVAL);
45 : : unsigned int mss;
46 : : unsigned int unfrag_ip6hlen, unfrag_len;
47 : : struct frag_hdr *fptr;
48 : : u8 *packet_start, *prevhdr;
49 : : u8 nexthdr;
50 : : u8 frag_hdr_sz = sizeof(struct frag_hdr);
51 : : int offset;
52 : : __wsum csum;
53 : : int tnl_hlen;
54 : :
55 : 0 : mss = skb_shinfo(skb)->gso_size;
56 [ # # ]: 0 : if (unlikely(skb->len <= mss))
57 : : goto out;
58 : :
59 [ # # ]: 0 : if (skb_gso_ok(skb, features | NETIF_F_GSO_ROBUST)) {
60 : : /* Packet is from an untrusted source, reset gso_segs. */
61 : : int type = skb_shinfo(skb)->gso_type;
62 : :
63 [ # # ]: 0 : if (unlikely(type & ~(SKB_GSO_UDP |
64 : : SKB_GSO_DODGY |
65 : : SKB_GSO_UDP_TUNNEL |
66 : : SKB_GSO_GRE |
67 : : SKB_GSO_IPIP |
68 : : SKB_GSO_SIT |
69 : : SKB_GSO_MPLS) ||
70 : : !(type & (SKB_GSO_UDP))))
71 : : goto out;
72 : :
73 : 0 : skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(skb->len, mss);
74 : :
75 : : segs = NULL;
76 : 0 : goto out;
77 : : }
78 : :
79 [ # # ][ # # ]: 0 : if (skb->encapsulation && skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL)
80 : 0 : segs = skb_udp_tunnel_segment(skb, features);
81 : : else {
82 : : /* Do software UFO. Complete and fill in the UDP checksum as HW cannot
83 : : * do checksum of UDP packets sent as multiple IP fragments.
84 : : */
85 : : offset = skb_checksum_start_offset(skb);
86 : 0 : csum = skb_checksum(skb, offset, skb->len - offset, 0);
87 : 0 : offset += skb->csum_offset;
88 : 0 : *(__sum16 *)(skb->data + offset) = csum_fold(csum);
89 : 0 : skb->ip_summed = CHECKSUM_NONE;
90 : :
91 : : /* Check if there is enough headroom to insert fragment header. */
92 : : tnl_hlen = skb_tnl_header_len(skb);
93 [ # # ]: 0 : if (skb->mac_header < (tnl_hlen + frag_hdr_sz)) {
94 [ # # ]: 0 : if (gso_pskb_expand_head(skb, tnl_hlen + frag_hdr_sz))
95 : : goto out;
96 : : }
97 : :
98 : : /* Find the unfragmentable header and shift it left by frag_hdr_sz
99 : : * bytes to insert fragment header.
100 : : */
101 : 0 : unfrag_ip6hlen = ip6_find_1stfragopt(skb, &prevhdr);
102 : 0 : nexthdr = *prevhdr;
103 : 0 : *prevhdr = NEXTHDR_FRAGMENT;
104 : 0 : unfrag_len = (skb_network_header(skb) - skb_mac_header(skb)) +
105 : 0 : unfrag_ip6hlen + tnl_hlen;
106 : 0 : packet_start = (u8 *) skb->head + SKB_GSO_CB(skb)->mac_offset;
107 : 0 : memmove(packet_start-frag_hdr_sz, packet_start, unfrag_len);
108 : :
109 : 0 : SKB_GSO_CB(skb)->mac_offset -= frag_hdr_sz;
110 : 0 : skb->mac_header -= frag_hdr_sz;
111 : 0 : skb->network_header -= frag_hdr_sz;
112 : :
113 : 0 : fptr = (struct frag_hdr *)(skb_network_header(skb) + unfrag_ip6hlen);
114 : 0 : fptr->nexthdr = nexthdr;
115 : 0 : fptr->reserved = 0;
116 : 0 : fptr->identification = skb_shinfo(skb)->ip6_frag_id;
117 : :
118 : : /* Fragment the skb. ipv6 header and the remaining fields of the
119 : : * fragment header are updated in ipv6_gso_segment()
120 : : */
121 : 0 : segs = skb_segment(skb, features);
122 : : }
123 : :
124 : : out:
125 : 0 : return segs;
126 : : }
127 : : static const struct net_offload udpv6_offload = {
128 : : .callbacks = {
129 : : .gso_send_check = udp6_ufo_send_check,
130 : : .gso_segment = udp6_ufo_fragment,
131 : : },
132 : : };
133 : :
134 : 0 : int __init udp_offload_init(void)
135 : : {
136 : 0 : return inet6_add_offload(&udpv6_offload, IPPROTO_UDP);
137 : : }
|