diff options
author | Steffen Klassert <steffen.klassert@secunet.com> | 2014-03-14 02:28:08 -0400 |
---|---|---|
committer | Steffen Klassert <steffen.klassert@secunet.com> | 2014-03-14 02:28:08 -0400 |
commit | 22e1b23dafa8554ef722454e8b84645820cbbc17 (patch) | |
tree | 6fe157ae571ee70930f199a96ff1f723966d9923 /net/ipv6 | |
parent | 573ce1c11b0d93a08b988d2713ef02214404aad1 (diff) |
vti6: Support inter address family tunneling.
With this patch we can tunnel ipv4 traffic via a vti6
interface. A vti6 interface can now have an ipv4 address
and ipv4 traffic can be routed via a vti6 interface.
The resulting traffic is xfrm transformed and tunneled
through ipv6 if matching IPsec policies and states are
present.
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
Diffstat (limited to 'net/ipv6')
-rw-r--r-- | net/ipv6/ip6_vti.c | 49 |
1 files changed, 34 insertions, 15 deletions
diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c index 226854a3c392..8d189aae7c96 100644 --- a/net/ipv6/ip6_vti.c +++ b/net/ipv6/ip6_vti.c | |||
@@ -380,30 +380,22 @@ vti6_addr_conflict(const struct ip6_tnl *t, const struct ipv6hdr *hdr) | |||
380 | * vti6_xmit - send a packet | 380 | * vti6_xmit - send a packet |
381 | * @skb: the outgoing socket buffer | 381 | * @skb: the outgoing socket buffer |
382 | * @dev: the outgoing tunnel device | 382 | * @dev: the outgoing tunnel device |
383 | * @fl: the flow informations for the xfrm_lookup | ||
383 | **/ | 384 | **/ |
384 | static int vti6_xmit(struct sk_buff *skb, struct net_device *dev) | 385 | static int |
386 | vti6_xmit(struct sk_buff *skb, struct net_device *dev, struct flowi *fl) | ||
385 | { | 387 | { |
386 | struct ip6_tnl *t = netdev_priv(dev); | 388 | struct ip6_tnl *t = netdev_priv(dev); |
387 | struct net_device_stats *stats = &t->dev->stats; | 389 | struct net_device_stats *stats = &t->dev->stats; |
388 | struct dst_entry *dst = skb_dst(skb); | 390 | struct dst_entry *dst = skb_dst(skb); |
389 | struct flowi fl; | ||
390 | struct ipv6hdr *ipv6h = ipv6_hdr(skb); | ||
391 | struct net_device *tdev; | 391 | struct net_device *tdev; |
392 | int err = -1; | 392 | int err = -1; |
393 | 393 | ||
394 | if ((t->parms.proto != IPPROTO_IPV6 && t->parms.proto != 0) || | ||
395 | !ip6_tnl_xmit_ctl(t) || vti6_addr_conflict(t, ipv6h)) | ||
396 | return err; | ||
397 | |||
398 | memset(&fl, 0, sizeof(fl)); | ||
399 | skb->mark = be32_to_cpu(t->parms.o_key); | ||
400 | xfrm_decode_session(skb, &fl, AF_INET6); | ||
401 | |||
402 | if (!dst) | 394 | if (!dst) |
403 | goto tx_err_link_failure; | 395 | goto tx_err_link_failure; |
404 | 396 | ||
405 | dst_hold(dst); | 397 | dst_hold(dst); |
406 | dst = xfrm_lookup(t->net, dst, &fl, NULL, 0); | 398 | dst = xfrm_lookup(t->net, dst, fl, NULL, 0); |
407 | if (IS_ERR(dst)) { | 399 | if (IS_ERR(dst)) { |
408 | err = PTR_ERR(dst); | 400 | err = PTR_ERR(dst); |
409 | dst = NULL; | 401 | dst = NULL; |
@@ -422,12 +414,22 @@ static int vti6_xmit(struct sk_buff *skb, struct net_device *dev) | |||
422 | goto tx_err_dst_release; | 414 | goto tx_err_dst_release; |
423 | } | 415 | } |
424 | 416 | ||
425 | memset(IP6CB(skb), 0, sizeof(*IP6CB(skb))); | ||
426 | skb_scrub_packet(skb, !net_eq(t->net, dev_net(dev))); | 417 | skb_scrub_packet(skb, !net_eq(t->net, dev_net(dev))); |
427 | skb_dst_set(skb, dst); | 418 | skb_dst_set(skb, dst); |
428 | skb->dev = skb_dst(skb)->dev; | 419 | skb->dev = skb_dst(skb)->dev; |
429 | 420 | ||
430 | ip6tunnel_xmit(skb, dev); | 421 | err = dst_output(skb); |
422 | if (net_xmit_eval(err) == 0) { | ||
423 | struct pcpu_sw_netstats *tstats = this_cpu_ptr(dev->tstats); | ||
424 | |||
425 | u64_stats_update_begin(&tstats->syncp); | ||
426 | tstats->tx_bytes += skb->len; | ||
427 | tstats->tx_packets++; | ||
428 | u64_stats_update_end(&tstats->syncp); | ||
429 | } else { | ||
430 | stats->tx_errors++; | ||
431 | stats->tx_aborted_errors++; | ||
432 | } | ||
431 | 433 | ||
432 | return 0; | 434 | return 0; |
433 | tx_err_link_failure: | 435 | tx_err_link_failure: |
@@ -443,16 +445,33 @@ vti6_tnl_xmit(struct sk_buff *skb, struct net_device *dev) | |||
443 | { | 445 | { |
444 | struct ip6_tnl *t = netdev_priv(dev); | 446 | struct ip6_tnl *t = netdev_priv(dev); |
445 | struct net_device_stats *stats = &t->dev->stats; | 447 | struct net_device_stats *stats = &t->dev->stats; |
448 | struct ipv6hdr *ipv6h; | ||
449 | struct flowi fl; | ||
446 | int ret; | 450 | int ret; |
447 | 451 | ||
452 | memset(&fl, 0, sizeof(fl)); | ||
453 | skb->mark = be32_to_cpu(t->parms.o_key); | ||
454 | |||
448 | switch (skb->protocol) { | 455 | switch (skb->protocol) { |
449 | case htons(ETH_P_IPV6): | 456 | case htons(ETH_P_IPV6): |
450 | ret = vti6_xmit(skb, dev); | 457 | ipv6h = ipv6_hdr(skb); |
458 | |||
459 | if ((t->parms.proto != IPPROTO_IPV6 && t->parms.proto != 0) || | ||
460 | !ip6_tnl_xmit_ctl(t) || vti6_addr_conflict(t, ipv6h)) | ||
461 | goto tx_err; | ||
462 | |||
463 | xfrm_decode_session(skb, &fl, AF_INET6); | ||
464 | memset(IP6CB(skb), 0, sizeof(*IP6CB(skb))); | ||
465 | break; | ||
466 | case htons(ETH_P_IP): | ||
467 | xfrm_decode_session(skb, &fl, AF_INET); | ||
468 | memset(IPCB(skb), 0, sizeof(*IPCB(skb))); | ||
451 | break; | 469 | break; |
452 | default: | 470 | default: |
453 | goto tx_err; | 471 | goto tx_err; |
454 | } | 472 | } |
455 | 473 | ||
474 | ret = vti6_xmit(skb, dev, &fl); | ||
456 | if (ret < 0) | 475 | if (ret < 0) |
457 | goto tx_err; | 476 | goto tx_err; |
458 | 477 | ||