]>
Commit | Line | Data |
---|---|---|
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 |
42 | struct gre_base_hdr { |
43 | __be16 flags; | |
44 | __be16 protocol; | |
45 | }; | |
46 | ||
85c9de19 | 47 | static 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 |
58 | static 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. */ |
72 | static __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 |
81 | static __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 |
90 | static 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 |
124 | static 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 | ||
131 | static 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 | 138 | static __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 | 147 | static 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 |
198 | static 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 | 224 | static 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 | 238 | static 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 | ||
279 | error: | |
280 | kfree_skb(skb); | |
281 | return 0; | |
282 | } | |
283 | ||
b279fccf | 284 | static 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 | 291 | static int gre_ports; |
fceb2a5b | 292 | static 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 | 307 | static 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 |
316 | static const char *gre_get_name(const struct vport *vport) |
317 | { | |
318 | return vport_priv(vport); | |
319 | } | |
85c9de19 PS |
320 | |
321 | static 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 | |
346 | error: | |
347 | gre_exit(); | |
348 | return vport; | |
85c9de19 PS |
349 | } |
350 | ||
351 | static 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 | ||
363 | static 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 | 374 | const 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 |
383 | static 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 |
407 | error: |
408 | gre_exit(); | |
409 | return vport; | |
85c9de19 PS |
410 | } |
411 | ||
85c9de19 PS |
412 | static 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 | ||
424 | static 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 |
435 | const 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 | }; |