aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/udp_offload.c
diff options
context:
space:
mode:
authorCong Wang <amwang@redhat.com>2013-08-31 01:44:37 -0400
committerDavid S. Miller <davem@davemloft.net>2013-08-31 22:30:01 -0400
commitd949d826c09fb65e230f55868ff70dc581ec06fa (patch)
tree51ec4e719474e4748834e6f9c36e824370630929 /net/ipv6/udp_offload.c
parentf564f45c451809aa3b74f577754528520d315ac1 (diff)
ipv6: Add generic UDP Tunnel segmentation
Similar to commit 731362674580cb0c696cd1b1a03d8461a10cf90a (tunneling: Add generic Tunnel segmentation) This patch adds generic tunneling offloading support for IPv6-UDP based tunnels. This can be used by tunneling protocols like VXLAN. Cc: Jesse Gross <jesse@nicira.com> Cc: Pravin B Shelar <pshelar@nicira.com> Cc: Stephen Hemminger <stephen@networkplumber.org> Cc: David S. Miller <davem@davemloft.net> Signed-off-by: Cong Wang <amwang@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6/udp_offload.c')
-rw-r--r--net/ipv6/udp_offload.c159
1 files changed, 108 insertions, 51 deletions
diff --git a/net/ipv6/udp_offload.c b/net/ipv6/udp_offload.c
index 5d1b8d7ac993..7e5e5acc9619 100644
--- a/net/ipv6/udp_offload.c
+++ b/net/ipv6/udp_offload.c
@@ -21,26 +21,79 @@ 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
28 if (!pskb_may_pull(skb, sizeof(*uh))) 24 if (!pskb_may_pull(skb, sizeof(*uh)))
29 return -EINVAL; 25 return -EINVAL;
30 26
31 ipv6h = ipv6_hdr(skb); 27 if (likely(!skb->encapsulation)) {
32 uh = udp_hdr(skb); 28 ipv6h = ipv6_hdr(skb);
29 uh = udp_hdr(skb);
30
31 uh->check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr, skb->len,
32 IPPROTO_UDP, 0);
33 skb->csum_start = skb_transport_header(skb) - skb->head;
34 skb->csum_offset = offsetof(struct udphdr, check);
35 skb->ip_summed = CHECKSUM_PARTIAL;
36 }
33 37
34 uh->check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr, skb->len,
35 IPPROTO_UDP, 0);
36 skb->csum_start = skb_transport_header(skb) - skb->head;
37 skb->csum_offset = offsetof(struct udphdr, check);
38 skb->ip_summed = CHECKSUM_PARTIAL;
39 return 0; 38 return 0;
40} 39}
41 40
41static struct sk_buff *skb_udp6_tunnel_segment(struct sk_buff *skb,
42 netdev_features_t features)
43{
44 struct sk_buff *segs = ERR_PTR(-EINVAL);
45 int mac_len = skb->mac_len;
46 int tnl_hlen = skb_inner_mac_header(skb) - skb_transport_header(skb);
47 int outer_hlen;
48 netdev_features_t enc_features;
49
50 if (unlikely(!pskb_may_pull(skb, tnl_hlen)))
51 goto out;
52
53 skb->encapsulation = 0;
54 __skb_pull(skb, tnl_hlen);
55 skb_reset_mac_header(skb);
56 skb_set_network_header(skb, skb_inner_network_offset(skb));
57 skb->mac_len = skb_inner_network_offset(skb);
58
59 /* segment inner packet. */
60 enc_features = skb->dev->hw_enc_features & netif_skb_features(skb);
61 segs = skb_mac_gso_segment(skb, enc_features);
62 if (!segs || IS_ERR(segs))
63 goto out;
64
65 outer_hlen = skb_tnl_header_len(skb);
66 skb = segs;
67 do {
68 struct udphdr *uh;
69 struct ipv6hdr *ipv6h;
70 int udp_offset = outer_hlen - tnl_hlen;
71 u32 len;
72
73 skb->mac_len = mac_len;
74
75 skb_push(skb, outer_hlen);
76 skb_reset_mac_header(skb);
77 skb_set_network_header(skb, mac_len);
78 skb_set_transport_header(skb, udp_offset);
79 uh = udp_hdr(skb);
80 uh->len = htons(skb->len - udp_offset);
81 ipv6h = ipv6_hdr(skb);
82 len = skb->len - udp_offset;
83
84 uh->check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr,
85 len, IPPROTO_UDP, 0);
86 uh->check = csum_fold(skb_checksum(skb, udp_offset, len, 0));
87 if (uh->check == 0)
88 uh->check = CSUM_MANGLED_0;
89 skb->ip_summed = CHECKSUM_NONE;
90 } while ((skb = skb->next));
91out:
92 return segs;
93}
94
42static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb, 95static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb,
43 netdev_features_t features) 96 netdev_features_t features)
44{ 97{
45 struct sk_buff *segs = ERR_PTR(-EINVAL); 98 struct sk_buff *segs = ERR_PTR(-EINVAL);
46 unsigned int mss; 99 unsigned int mss;
@@ -75,47 +128,51 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb,
75 goto out; 128 goto out;
76 } 129 }
77 130
78 /* Do software UFO. Complete and fill in the UDP checksum as HW cannot 131 if (skb->encapsulation && skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL)
79 * do checksum of UDP packets sent as multiple IP fragments. 132 segs = skb_udp6_tunnel_segment(skb, features);
80 */ 133 else {
81 offset = skb_checksum_start_offset(skb); 134 /* Do software UFO. Complete and fill in the UDP checksum as HW cannot
82 csum = skb_checksum(skb, offset, skb->len - offset, 0); 135 * do checksum of UDP packets sent as multiple IP fragments.
83 offset += skb->csum_offset; 136 */
84 *(__sum16 *)(skb->data + offset) = csum_fold(csum); 137 offset = skb_checksum_start_offset(skb);
85 skb->ip_summed = CHECKSUM_NONE; 138 csum = skb_checksum(skb, offset, skb->len - offset, 0);
86 139 offset += skb->csum_offset;
87 /* Check if there is enough headroom to insert fragment header. */ 140 *(__sum16 *)(skb->data + offset) = csum_fold(csum);
88 tnl_hlen = skb_tnl_header_len(skb); 141 skb->ip_summed = CHECKSUM_NONE;
89 if (skb_headroom(skb) < (tnl_hlen + frag_hdr_sz)) { 142
90 if (gso_pskb_expand_head(skb, tnl_hlen + frag_hdr_sz)) 143 /* Check if there is enough headroom to insert fragment header. */
91 goto out; 144 tnl_hlen = skb_tnl_header_len(skb);
145 if (skb_headroom(skb) < (tnl_hlen + frag_hdr_sz)) {
146 if (gso_pskb_expand_head(skb, tnl_hlen + frag_hdr_sz))
147 goto out;
148 }
149
150 /* Find the unfragmentable header and shift it left by frag_hdr_sz
151 * bytes to insert fragment header.
152 */
153 unfrag_ip6hlen = ip6_find_1stfragopt(skb, &prevhdr);
154 nexthdr = *prevhdr;
155 *prevhdr = NEXTHDR_FRAGMENT;
156 unfrag_len = (skb_network_header(skb) - skb_mac_header(skb)) +
157 unfrag_ip6hlen + tnl_hlen;
158 packet_start = (u8 *) skb->head + SKB_GSO_CB(skb)->mac_offset;
159 memmove(packet_start-frag_hdr_sz, packet_start, unfrag_len);
160
161 SKB_GSO_CB(skb)->mac_offset -= frag_hdr_sz;
162 skb->mac_header -= frag_hdr_sz;
163 skb->network_header -= frag_hdr_sz;
164
165 fptr = (struct frag_hdr *)(skb_network_header(skb) + unfrag_ip6hlen);
166 fptr->nexthdr = nexthdr;
167 fptr->reserved = 0;
168 ipv6_select_ident(fptr, (struct rt6_info *)skb_dst(skb));
169
170 /* Fragment the skb. ipv6 header and the remaining fields of the
171 * fragment header are updated in ipv6_gso_segment()
172 */
173 segs = skb_segment(skb, features);
92 } 174 }
93 175
94 /* Find the unfragmentable header and shift it left by frag_hdr_sz
95 * bytes to insert fragment header.
96 */
97 unfrag_ip6hlen = ip6_find_1stfragopt(skb, &prevhdr);
98 nexthdr = *prevhdr;
99 *prevhdr = NEXTHDR_FRAGMENT;
100 unfrag_len = (skb_network_header(skb) - skb_mac_header(skb)) +
101 unfrag_ip6hlen + tnl_hlen;
102 packet_start = (u8 *) skb->head + SKB_GSO_CB(skb)->mac_offset;
103 memmove(packet_start-frag_hdr_sz, packet_start, unfrag_len);
104
105 SKB_GSO_CB(skb)->mac_offset -= frag_hdr_sz;
106 skb->mac_header -= frag_hdr_sz;
107 skb->network_header -= frag_hdr_sz;
108
109 fptr = (struct frag_hdr *)(skb_network_header(skb) + unfrag_ip6hlen);
110 fptr->nexthdr = nexthdr;
111 fptr->reserved = 0;
112 ipv6_select_ident(fptr, (struct rt6_info *)skb_dst(skb));
113
114 /* Fragment the skb. ipv6 header and the remaining fields of the
115 * fragment header are updated in ipv6_gso_segment()
116 */
117 segs = skb_segment(skb, features);
118
119out: 176out:
120 return segs; 177 return segs;
121} 178}