]> git.proxmox.com Git - mirror_ovs.git/blame - datapath/vport-gre.c
datapath: make skb->csum consistent with rest of networking stack.
[mirror_ovs.git] / datapath / vport-gre.c
CommitLineData
2736b84e 1/*
e0edde6f 2 * Copyright (c) 2007-2012 Nicira, Inc.
2736b84e 3 *
a9a29d22
JG
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of version 2 of the GNU General Public
6 * License as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
16 * 02110-1301, USA
2736b84e
JG
17 */
18
dfffaef1
JP
19#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
20
d1eb60cc
JG
21#include <linux/if.h>
22#include <linux/skbuff.h>
2736b84e
JG
23#include <linux/ip.h>
24#include <linux/if_tunnel.h>
25#include <linux/if_vlan.h>
26#include <linux/in.h>
2736b84e 27
2736b84e 28#include <net/icmp.h>
2736b84e 29#include <net/ip.h>
2736b84e 30#include <net/protocol.h>
2736b84e 31
2a4999f3 32#include "datapath.h"
d1eb60cc 33#include "tunnel.h"
2736b84e
JG
34#include "vport.h"
35
d1eb60cc
JG
36/*
37 * The GRE header is composed of a series of sections: a base and then a variable
38 * number of options.
39 */
2736b84e
JG
40#define GRE_HEADER_SECTION 4
41
27b6cec0
JG
42struct gre_base_hdr {
43 __be16 flags;
44 __be16 protocol;
45};
46
85c9de19 47static int gre_hdr_len(const struct ovs_key_ipv4_tunnel *tun_key)
2736b84e 48{
85c9de19 49 int len = GRE_HEADER_SECTION;
2736b84e 50
85c9de19
PS
51 if (tun_key->tun_flags & OVS_TNL_F_KEY)
52 len += GRE_HEADER_SECTION;
53 if (tun_key->tun_flags & OVS_TNL_F_CSUM)
d1eb60cc 54 len += GRE_HEADER_SECTION;
85c9de19
PS
55 return len;
56}
2736b84e 57
85c9de19
PS
58static int gre64_hdr_len(const struct ovs_key_ipv4_tunnel *tun_key)
59{
986390c3 60 /* Set key for GRE64 tunnels, even when key if is zero. */
85c9de19
PS
61 int len = GRE_HEADER_SECTION + /* GRE Hdr */
62 GRE_HEADER_SECTION + /* GRE Key */
63 GRE_HEADER_SECTION; /* GRE SEQ */
986390c3 64
85c9de19 65 if (tun_key->tun_flags & OVS_TNL_F_CSUM)
d1eb60cc 66 len += GRE_HEADER_SECTION;
85c9de19 67
d1eb60cc 68 return len;
2736b84e
JG
69}
70
b9298d3f
BP
71/* Returns the least-significant 32 bits of a __be64. */
72static __be32 be64_get_low32(__be64 x)
73{
74#ifdef __BIG_ENDIAN
8dda8c9b 75 return (__force __be32)x;
b9298d3f 76#else
8dda8c9b 77 return (__force __be32)((__force u64)x >> 32);
b9298d3f
BP
78#endif
79}
80
2de795ad
PS
81static __be32 be64_get_high32(__be64 x)
82{
83#ifdef __BIG_ENDIAN
84 return (__force __be32)((__force u64)x >> 32);
85#else
86 return (__force __be32)x;
87#endif
88}
89
85c9de19
PS
90static void __gre_build_header(struct sk_buff *skb,
91 int tunnel_hlen,
92 bool is_gre64)
842cf6f4 93{
356af50b
KM
94 const struct ovs_key_ipv4_tunnel *tun_key = OVS_CB(skb)->tun_key;
95 __be32 *options = (__be32 *)(skb_network_header(skb) + tunnel_hlen
85c9de19 96 - GRE_HEADER_SECTION);
05a9d485 97 struct gre_base_hdr *greh = (struct gre_base_hdr *) skb_transport_header(skb);
05a9d485
PS
98 greh->protocol = htons(ETH_P_TEB);
99 greh->flags = 0;
100
842cf6f4 101 /* Work backwards over the options so the checksum is last. */
85c9de19 102 if (tun_key->tun_flags & OVS_TNL_F_KEY || is_gre64) {
05a9d485 103 greh->flags |= GRE_KEY;
85c9de19 104 if (is_gre64) {
2de795ad 105 /* Set higher 32 bits to seq. */
85c9de19 106 *options = be64_get_high32(tun_key->tun_id);
2de795ad 107 options--;
05a9d485 108 greh->flags |= GRE_SEQ;
2de795ad 109 }
85c9de19 110 *options = be64_get_low32(tun_key->tun_id);
2736b84e 111 options--;
2de795ad 112 }
2736b84e 113
85c9de19 114 if (tun_key->tun_flags & OVS_TNL_F_CSUM) {
05a9d485
PS
115 greh->flags |= GRE_CSUM;
116 *options = 0;
2736b84e 117 *(__sum16 *)options = csum_fold(skb_checksum(skb,
842cf6f4
JG
118 skb_transport_offset(skb),
119 skb->len - skb_transport_offset(skb),
2736b84e 120 0));
05a9d485 121 }
85c9de19 122}
5214f5c4 123
85c9de19
PS
124static void gre_build_header(const struct vport *vport,
125 struct sk_buff *skb,
126 int tunnel_hlen)
127{
128 __gre_build_header(skb, tunnel_hlen, false);
129}
130
131static void gre64_build_header(const struct vport *vport,
132 struct sk_buff *skb,
133 int tunnel_hlen)
134{
135 __gre_build_header(skb, tunnel_hlen, true);
2736b84e
JG
136}
137
2de795ad 138static __be64 key_to_tunnel_id(__be32 key, __be32 seq)
b9298d3f
BP
139{
140#ifdef __BIG_ENDIAN
2de795ad 141 return (__force __be64)((__force u64)seq << 32 | (__force u32)key);
b9298d3f 142#else
2de795ad 143 return (__force __be64)((__force u64)key << 32 | (__force u32)seq);
b9298d3f
BP
144#endif
145}
146
2de795ad 147static int parse_header(struct iphdr *iph, __be16 *flags, __be64 *tun_id,
85c9de19 148 bool *is_gre64)
2736b84e 149{
eea2aafb 150 /* IP and ICMP protocol handlers check that the IHL is valid. */
27b6cec0
JG
151 struct gre_base_hdr *greh = (struct gre_base_hdr *)((u8 *)iph + (iph->ihl << 2));
152 __be32 *options = (__be32 *)(greh + 1);
2736b84e
JG
153 int hdr_len;
154
27b6cec0 155 *flags = greh->flags;
2736b84e 156
d1eb60cc 157 if (unlikely(greh->flags & (GRE_VERSION | GRE_ROUTING)))
2736b84e
JG
158 return -EINVAL;
159
d1eb60cc 160 if (unlikely(greh->protocol != htons(ETH_P_TEB)))
2736b84e
JG
161 return -EINVAL;
162
163 hdr_len = GRE_HEADER_SECTION;
164
27b6cec0 165 if (greh->flags & GRE_CSUM) {
2736b84e
JG
166 hdr_len += GRE_HEADER_SECTION;
167 options++;
168 }
169
27b6cec0 170 if (greh->flags & GRE_KEY) {
2de795ad
PS
171 __be32 seq;
172 __be32 gre_key;
2736b84e 173
2de795ad
PS
174 gre_key = *options;
175 hdr_len += GRE_HEADER_SECTION;
2736b84e 176 options++;
2de795ad
PS
177
178 if (greh->flags & GRE_SEQ) {
179 seq = *options;
85c9de19 180 *is_gre64 = true;
2de795ad
PS
181 } else {
182 seq = 0;
85c9de19 183 *is_gre64 = false;
2de795ad
PS
184 }
185 *tun_id = key_to_tunnel_id(gre_key, seq);
685c931c 186 } else {
2de795ad 187 *tun_id = 0;
685c931c 188 /* Ignore GRE seq if there is no key present. */
85c9de19 189 *is_gre64 = false;
685c931c 190 }
2736b84e 191
2de795ad 192 if (greh->flags & GRE_SEQ)
2736b84e
JG
193 hdr_len += GRE_HEADER_SECTION;
194
195 return hdr_len;
196}
197
d1eb60cc
JG
198static bool check_checksum(struct sk_buff *skb)
199{
200 struct iphdr *iph = ip_hdr(skb);
201 struct gre_base_hdr *greh = (struct gre_base_hdr *)(iph + 1);
202 __sum16 csum = 0;
203
204 if (greh->flags & GRE_CSUM) {
205 switch (skb->ip_summed) {
206 case CHECKSUM_COMPLETE:
207 csum = csum_fold(skb->csum);
208
209 if (!csum)
210 break;
211 /* Fall through. */
212
213 case CHECKSUM_NONE:
214 skb->csum = 0;
215 csum = __skb_checksum_complete(skb);
216 skb->ip_summed = CHECKSUM_COMPLETE;
217 break;
218 }
219 }
220
221 return (csum == 0);
222}
223
85c9de19 224static u32 gre_flags_to_tunnel_flags(__be16 gre_flags, bool is_gre64)
356af50b
KM
225{
226 u32 tunnel_flags = 0;
227
85c9de19
PS
228 if (gre_flags & GRE_KEY || is_gre64)
229 tunnel_flags = OVS_TNL_F_KEY;
356af50b
KM
230
231 if (gre_flags & GRE_CSUM)
49a4902d 232 tunnel_flags |= OVS_TNL_F_CSUM;
356af50b
KM
233
234 return tunnel_flags;
235}
236
d1eb60cc 237/* Called with rcu_read_lock and BH disabled. */
fceb2a5b 238static int gre_rcv(struct sk_buff *skb)
2736b84e 239{
85c9de19 240 struct ovs_net *ovs_net;
2736b84e 241 struct vport *vport;
2736b84e
JG
242 int hdr_len;
243 struct iphdr *iph;
356af50b 244 struct ovs_key_ipv4_tunnel tun_key;
7827e967
JG
245 __be16 gre_flags;
246 u32 tnl_flags;
b9298d3f 247 __be64 key;
85c9de19 248 bool is_gre64;
2736b84e 249
d1eb60cc 250 if (unlikely(!pskb_may_pull(skb, sizeof(struct gre_base_hdr) + ETH_HLEN)))
2736b84e 251 goto error;
d1eb60cc 252 if (unlikely(!check_checksum(skb)))
2736b84e
JG
253 goto error;
254
85c9de19 255 hdr_len = parse_header(ip_hdr(skb), &gre_flags, &key, &is_gre64);
d1eb60cc 256 if (unlikely(hdr_len < 0))
2736b84e
JG
257 goto error;
258
85c9de19
PS
259 ovs_net = net_generic(dev_net(skb->dev), ovs_net_id);
260 if (is_gre64)
261 vport = rcu_dereference(ovs_net->vport_net.gre64_vport);
262 else
263 vport = rcu_dereference(ovs_net->vport_net.gre_vport);
264 if (unlikely(!vport))
2736b84e 265 goto error;
2736b84e 266
85c9de19 267 if (unlikely(!pskb_may_pull(skb, hdr_len + ETH_HLEN)))
2736b84e 268 goto error;
2736b84e 269
85c9de19
PS
270 iph = ip_hdr(skb);
271 tnl_flags = gre_flags_to_tunnel_flags(gre_flags, is_gre64);
7827e967 272 tnl_tun_key_init(&tun_key, iph, key, tnl_flags);
2736b84e 273
3cfede14 274 skb_pull_rcsum(skb, hdr_len);
2736b84e 275
db0c3134 276 ovs_tnl_rcv(vport, skb, &tun_key);
2736b84e
JG
277 return 0;
278
279error:
280 kfree_skb(skb);
281 return 0;
282}
283
b279fccf 284static const struct net_protocol gre_protocol_handlers = {
2736b84e 285 .handler = gre_rcv,
2a4999f3
PS
286#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)
287 .netns_ok = 1,
288#endif
2736b84e
JG
289};
290
806b46ef 291static int gre_ports;
fceb2a5b 292static int gre_init(void)
2736b84e
JG
293{
294 int err;
295
806b46ef
PS
296 gre_ports++;
297 if (gre_ports > 1)
2de795ad
PS
298 return 0;
299
2736b84e 300 err = inet_add_protocol(&gre_protocol_handlers, IPPROTO_GRE);
842cf6f4 301 if (err)
dfffaef1 302 pr_warn("cannot register gre protocol handler\n");
2736b84e 303
2736b84e
JG
304 return err;
305}
306
d1eb60cc 307static void gre_exit(void)
2736b84e 308{
806b46ef
PS
309 gre_ports--;
310 if (gre_ports > 0)
2de795ad
PS
311 return;
312
d1eb60cc 313 inet_del_protocol(&gre_protocol_handlers, IPPROTO_GRE);
2736b84e
JG
314}
315
c405d282
PS
316static const char *gre_get_name(const struct vport *vport)
317{
318 return vport_priv(vport);
319}
85c9de19
PS
320
321static struct vport *gre_create(const struct vport_parms *parms)
322{
323 struct net *net = ovs_dp_get_net(parms->dp);
324 struct ovs_net *ovs_net;
325 struct vport *vport;
806b46ef
PS
326 int err;
327
328 err = gre_init();
329 if (err)
330 return ERR_PTR(err);
85c9de19
PS
331
332 ovs_net = net_generic(net, ovs_net_id);
806b46ef
PS
333 if (ovsl_dereference(ovs_net->vport_net.gre_vport)) {
334 vport = ERR_PTR(-EEXIST);
335 goto error;
336 }
85c9de19 337
c405d282
PS
338 vport = ovs_vport_alloc(IFNAMSIZ, &ovs_gre_vport_ops, parms);
339 if (IS_ERR(vport))
806b46ef 340 goto error;
85c9de19 341
c405d282 342 strncpy(vport_priv(vport), parms->name, IFNAMSIZ);
85c9de19
PS
343 rcu_assign_pointer(ovs_net->vport_net.gre_vport, vport);
344 return vport;
806b46ef
PS
345
346error:
347 gre_exit();
348 return vport;
85c9de19
PS
349}
350
351static void gre_tnl_destroy(struct vport *vport)
352{
353 struct net *net = ovs_dp_get_net(vport->dp);
354 struct ovs_net *ovs_net;
355
356 ovs_net = net_generic(net, ovs_net_id);
357
358 rcu_assign_pointer(ovs_net->vport_net.gre_vport, NULL);
c405d282 359 ovs_vport_deferred_free(vport);
806b46ef 360 gre_exit();
c405d282
PS
361}
362
363static int gre_tnl_send(struct vport *vport, struct sk_buff *skb)
364{
365 int hlen;
366
be7cd27e
PS
367 if (unlikely(!OVS_CB(skb)->tun_key))
368 return -EINVAL;
c405d282
PS
369
370 hlen = gre_hdr_len(OVS_CB(skb)->tun_key);
371 return ovs_tnl_send(vport, skb, IPPROTO_GRE, hlen, gre_build_header);
85c9de19
PS
372}
373
850b6b3b 374const struct vport_ops ovs_gre_vport_ops = {
df2c07f4 375 .type = OVS_VPORT_TYPE_GRE,
2736b84e 376 .create = gre_create,
85c9de19 377 .destroy = gre_tnl_destroy,
c405d282
PS
378 .get_name = gre_get_name,
379 .send = gre_tnl_send,
2736b84e 380};
2de795ad 381
85c9de19 382/* GRE64 vport. */
85c9de19
PS
383static struct vport *gre64_create(const struct vport_parms *parms)
384{
385 struct net *net = ovs_dp_get_net(parms->dp);
386 struct ovs_net *ovs_net;
387 struct vport *vport;
806b46ef
PS
388 int err;
389
390 err = gre_init();
391 if (err)
392 return ERR_PTR(err);
85c9de19
PS
393
394 ovs_net = net_generic(net, ovs_net_id);
806b46ef
PS
395 if (ovsl_dereference(ovs_net->vport_net.gre64_vport)) {
396 vport = ERR_PTR(-EEXIST);
397 goto error;
398 }
85c9de19 399
c405d282
PS
400 vport = ovs_vport_alloc(IFNAMSIZ, &ovs_gre64_vport_ops, parms);
401 if (IS_ERR(vport))
806b46ef 402 goto error;
85c9de19 403
c405d282 404 strncpy(vport_priv(vport), parms->name, IFNAMSIZ);
85c9de19
PS
405 rcu_assign_pointer(ovs_net->vport_net.gre64_vport, vport);
406 return vport;
806b46ef
PS
407error:
408 gre_exit();
409 return vport;
85c9de19
PS
410}
411
85c9de19
PS
412static void gre64_tnl_destroy(struct vport *vport)
413{
414 struct net *net = ovs_dp_get_net(vport->dp);
415 struct ovs_net *ovs_net;
416
417 ovs_net = net_generic(net, ovs_net_id);
418
419 rcu_assign_pointer(ovs_net->vport_net.gre64_vport, NULL);
c405d282 420 ovs_vport_deferred_free(vport);
806b46ef 421 gre_exit();
c405d282
PS
422}
423
424static int gre64_tnl_send(struct vport *vport, struct sk_buff *skb)
425{
426 int hlen;
427
be7cd27e
PS
428 if (unlikely(!OVS_CB(skb)->tun_key))
429 return -EINVAL;
c405d282
PS
430
431 hlen = gre64_hdr_len(OVS_CB(skb)->tun_key);
432 return ovs_tnl_send(vport, skb, IPPROTO_GRE, hlen, gre64_build_header);
85c9de19
PS
433}
434
2de795ad
PS
435const struct vport_ops ovs_gre64_vport_ops = {
436 .type = OVS_VPORT_TYPE_GRE64,
85c9de19
PS
437 .create = gre64_create,
438 .destroy = gre64_tnl_destroy,
c405d282
PS
439 .get_name = gre_get_name,
440 .send = gre64_tnl_send,
2de795ad 441};