diff options
author | Yasuyuki Kozakai <yasuyuki.kozakai@toshiba.co.jp> | 2007-02-14 10:43:16 -0500 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2007-04-26 01:23:40 -0400 |
commit | c4d3efafcc933fd2ffd169d7dc4f980393a13796 (patch) | |
tree | 01ecdc7128ae03c29acbd605589a56420bcf95ec | |
parent | 61ec2aec28ba8de09f76a558a5d6d3893b1d2e47 (diff) |
[IPV6] IP6TUNNEL: Add support to IPv4 over IPv6 tunnel.
Some notes
- Protocol number IPPROTO_IPIP is used for IPv4 over IPv6 packets.
- If IP6_TNL_F_USE_ORIG_TCLASS is set, TOS in IPv4 header is copied to
Traffic Class in outer IPv6 header on xmit.
- IP6_TNL_F_USE_ORIG_FLOWLABEL is ignored on xmit of IPv4 packets, because
IPv4 header does not have flow label.
- Kernel sends ICMP error if IPv4 packet is too big on xmit, even if
DF flag is not set.
Signed-off-by: Yasuyuki Kozakai <yasuyuki.kozakai@toshiba.co.jp>
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | net/ipv6/ip6_tunnel.c | 190 |
1 files changed, 185 insertions, 5 deletions
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index 4546bb923a20..a6541495deab 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c | |||
@@ -1,14 +1,15 @@ | |||
1 | /* | 1 | /* |
2 | * IPv6 over IPv6 tunnel device | 2 | * IPv6 tunneling device |
3 | * Linux INET6 implementation | 3 | * Linux INET6 implementation |
4 | * | 4 | * |
5 | * Authors: | 5 | * Authors: |
6 | * Ville Nuorvala <vnuorval@tcs.hut.fi> | 6 | * Ville Nuorvala <vnuorval@tcs.hut.fi> |
7 | * Yasuyuki Kozakai <kozakai@linux-ipv6.org> | ||
7 | * | 8 | * |
8 | * $Id$ | 9 | * $Id$ |
9 | * | 10 | * |
10 | * Based on: | 11 | * Based on: |
11 | * linux/net/ipv6/sit.c | 12 | * linux/net/ipv6/sit.c and linux/net/ipv4/ipip.c |
12 | * | 13 | * |
13 | * RFC 2473 | 14 | * RFC 2473 |
14 | * | 15 | * |
@@ -24,6 +25,7 @@ | |||
24 | #include <linux/errno.h> | 25 | #include <linux/errno.h> |
25 | #include <linux/types.h> | 26 | #include <linux/types.h> |
26 | #include <linux/sockios.h> | 27 | #include <linux/sockios.h> |
28 | #include <linux/icmp.h> | ||
27 | #include <linux/if.h> | 29 | #include <linux/if.h> |
28 | #include <linux/in.h> | 30 | #include <linux/in.h> |
29 | #include <linux/ip.h> | 31 | #include <linux/ip.h> |
@@ -41,6 +43,7 @@ | |||
41 | #include <asm/uaccess.h> | 43 | #include <asm/uaccess.h> |
42 | #include <asm/atomic.h> | 44 | #include <asm/atomic.h> |
43 | 45 | ||
46 | #include <net/icmp.h> | ||
44 | #include <net/ip.h> | 47 | #include <net/ip.h> |
45 | #include <net/ipv6.h> | 48 | #include <net/ipv6.h> |
46 | #include <net/ip6_route.h> | 49 | #include <net/ip6_route.h> |
@@ -51,7 +54,7 @@ | |||
51 | #include <net/inet_ecn.h> | 54 | #include <net/inet_ecn.h> |
52 | 55 | ||
53 | MODULE_AUTHOR("Ville Nuorvala"); | 56 | MODULE_AUTHOR("Ville Nuorvala"); |
54 | MODULE_DESCRIPTION("IPv6-in-IPv6 tunnel"); | 57 | MODULE_DESCRIPTION("IPv6 tunneling device"); |
55 | MODULE_LICENSE("GPL"); | 58 | MODULE_LICENSE("GPL"); |
56 | 59 | ||
57 | #define IPV6_TLV_TEL_DST_SIZE 8 | 60 | #define IPV6_TLV_TEL_DST_SIZE 8 |
@@ -63,6 +66,7 @@ MODULE_LICENSE("GPL"); | |||
63 | #endif | 66 | #endif |
64 | 67 | ||
65 | #define IPV6_TCLASS_MASK (IPV6_FLOWINFO_MASK & ~IPV6_FLOWLABEL_MASK) | 68 | #define IPV6_TCLASS_MASK (IPV6_FLOWINFO_MASK & ~IPV6_FLOWLABEL_MASK) |
69 | #define IPV6_TCLASS_SHIFT 20 | ||
66 | 70 | ||
67 | #define HASH_SIZE 32 | 71 | #define HASH_SIZE 32 |
68 | 72 | ||
@@ -470,6 +474,104 @@ out: | |||
470 | } | 474 | } |
471 | 475 | ||
472 | static int | 476 | static int |
477 | ip4ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, | ||
478 | int type, int code, int offset, __u32 info) | ||
479 | { | ||
480 | int rel_msg = 0; | ||
481 | int rel_type = type; | ||
482 | int rel_code = code; | ||
483 | __u32 rel_info = info; | ||
484 | int err; | ||
485 | struct sk_buff *skb2; | ||
486 | struct iphdr *eiph; | ||
487 | struct flowi fl; | ||
488 | struct rtable *rt; | ||
489 | |||
490 | err = ip6_tnl_err(skb, opt, &rel_type, &rel_code, &rel_msg, &rel_info, | ||
491 | offset); | ||
492 | if (err < 0) | ||
493 | return err; | ||
494 | |||
495 | if (rel_msg == 0) | ||
496 | return 0; | ||
497 | |||
498 | switch (rel_type) { | ||
499 | case ICMPV6_DEST_UNREACH: | ||
500 | if (rel_code != ICMPV6_ADDR_UNREACH) | ||
501 | return 0; | ||
502 | rel_type = ICMP_DEST_UNREACH; | ||
503 | rel_code = ICMP_HOST_UNREACH; | ||
504 | break; | ||
505 | case ICMPV6_PKT_TOOBIG: | ||
506 | if (rel_code != 0) | ||
507 | return 0; | ||
508 | rel_type = ICMP_DEST_UNREACH; | ||
509 | rel_code = ICMP_FRAG_NEEDED; | ||
510 | break; | ||
511 | default: | ||
512 | return 0; | ||
513 | } | ||
514 | |||
515 | if (!pskb_may_pull(skb, offset + sizeof(struct iphdr))) | ||
516 | return 0; | ||
517 | |||
518 | skb2 = skb_clone(skb, GFP_ATOMIC); | ||
519 | if (!skb2) | ||
520 | return 0; | ||
521 | |||
522 | dst_release(skb2->dst); | ||
523 | skb2->dst = NULL; | ||
524 | skb_pull(skb2, offset); | ||
525 | skb2->nh.raw = skb2->data; | ||
526 | eiph = skb2->nh.iph; | ||
527 | |||
528 | /* Try to guess incoming interface */ | ||
529 | memset(&fl, 0, sizeof(fl)); | ||
530 | fl.fl4_dst = eiph->saddr; | ||
531 | fl.fl4_tos = RT_TOS(eiph->tos); | ||
532 | fl.proto = IPPROTO_IPIP; | ||
533 | if (ip_route_output_key(&rt, &fl)) | ||
534 | goto out; | ||
535 | |||
536 | skb2->dev = rt->u.dst.dev; | ||
537 | |||
538 | /* route "incoming" packet */ | ||
539 | if (rt->rt_flags & RTCF_LOCAL) { | ||
540 | ip_rt_put(rt); | ||
541 | rt = NULL; | ||
542 | fl.fl4_dst = eiph->daddr; | ||
543 | fl.fl4_src = eiph->saddr; | ||
544 | fl.fl4_tos = eiph->tos; | ||
545 | if (ip_route_output_key(&rt, &fl) || | ||
546 | rt->u.dst.dev->type != ARPHRD_TUNNEL) { | ||
547 | ip_rt_put(rt); | ||
548 | goto out; | ||
549 | } | ||
550 | } else { | ||
551 | ip_rt_put(rt); | ||
552 | if (ip_route_input(skb2, eiph->daddr, eiph->saddr, eiph->tos, | ||
553 | skb2->dev) || | ||
554 | skb2->dst->dev->type != ARPHRD_TUNNEL) | ||
555 | goto out; | ||
556 | } | ||
557 | |||
558 | /* change mtu on this route */ | ||
559 | if (rel_type == ICMP_DEST_UNREACH && rel_code == ICMP_FRAG_NEEDED) { | ||
560 | if (rel_info > dst_mtu(skb2->dst)) | ||
561 | goto out; | ||
562 | |||
563 | skb2->dst->ops->update_pmtu(skb2->dst, rel_info); | ||
564 | rel_info = htonl(rel_info); | ||
565 | } | ||
566 | |||
567 | icmp_send(skb2, rel_type, rel_code, rel_info); | ||
568 | |||
569 | out: | ||
570 | kfree_skb(skb2); | ||
571 | return 0; | ||
572 | } | ||
573 | |||
574 | static int | ||
473 | ip6ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, | 575 | ip6ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, |
474 | int type, int code, int offset, __u32 info) | 576 | int type, int code, int offset, __u32 info) |
475 | { | 577 | { |
@@ -513,6 +615,19 @@ ip6ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, | |||
513 | return 0; | 615 | return 0; |
514 | } | 616 | } |
515 | 617 | ||
618 | static void ip4ip6_dscp_ecn_decapsulate(struct ip6_tnl *t, | ||
619 | struct ipv6hdr *ipv6h, | ||
620 | struct sk_buff *skb) | ||
621 | { | ||
622 | __u8 dsfield = ipv6_get_dsfield(ipv6h) & ~INET_ECN_MASK; | ||
623 | |||
624 | if (t->parms.flags & IP6_TNL_F_RCV_DSCP_COPY) | ||
625 | ipv4_change_dsfield(skb->nh.iph, INET_ECN_MASK, dsfield); | ||
626 | |||
627 | if (INET_ECN_is_ce(dsfield)) | ||
628 | IP_ECN_set_ce(skb->nh.iph); | ||
629 | } | ||
630 | |||
516 | static void ip6ip6_dscp_ecn_decapsulate(struct ip6_tnl *t, | 631 | static void ip6ip6_dscp_ecn_decapsulate(struct ip6_tnl *t, |
517 | struct ipv6hdr *ipv6h, | 632 | struct ipv6hdr *ipv6h, |
518 | struct sk_buff *skb) | 633 | struct sk_buff *skb) |
@@ -605,6 +720,11 @@ discard: | |||
605 | return 0; | 720 | return 0; |
606 | } | 721 | } |
607 | 722 | ||
723 | static int ip4ip6_rcv(struct sk_buff *skb) | ||
724 | { | ||
725 | return ip6_tnl_rcv(skb, ETH_P_IP, ip4ip6_dscp_ecn_decapsulate); | ||
726 | } | ||
727 | |||
608 | static int ip6ip6_rcv(struct sk_buff *skb) | 728 | static int ip6ip6_rcv(struct sk_buff *skb) |
609 | { | 729 | { |
610 | return ip6_tnl_rcv(skb, ETH_P_IPV6, ip6ip6_dscp_ecn_decapsulate); | 730 | return ip6_tnl_rcv(skb, ETH_P_IPV6, ip6ip6_dscp_ecn_decapsulate); |
@@ -691,7 +811,7 @@ static inline int ip6_tnl_xmit_ctl(struct ip6_tnl *t) | |||
691 | * it. | 811 | * it. |
692 | * | 812 | * |
693 | * Return: | 813 | * Return: |
694 | * 0 | 814 | * 0 on success |
695 | * -1 fail | 815 | * -1 fail |
696 | * %-EMSGSIZE message too big. return mtu in this case. | 816 | * %-EMSGSIZE message too big. return mtu in this case. |
697 | **/ | 817 | **/ |
@@ -809,6 +929,44 @@ tx_err_dst_release: | |||
809 | } | 929 | } |
810 | 930 | ||
811 | static inline int | 931 | static inline int |
932 | ip4ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev) | ||
933 | { | ||
934 | struct ip6_tnl *t = netdev_priv(dev); | ||
935 | struct iphdr *iph = skb->nh.iph; | ||
936 | int encap_limit = -1; | ||
937 | struct flowi fl; | ||
938 | __u8 dsfield; | ||
939 | __u32 mtu; | ||
940 | int err; | ||
941 | |||
942 | if (!ip6_tnl_xmit_ctl(t)) | ||
943 | return -1; | ||
944 | |||
945 | if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT)) | ||
946 | encap_limit = t->parms.encap_limit; | ||
947 | |||
948 | memcpy(&fl, &t->fl, sizeof (fl)); | ||
949 | fl.proto = IPPROTO_IPIP; | ||
950 | |||
951 | dsfield = ipv4_get_dsfield(iph); | ||
952 | |||
953 | if ((t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS)) | ||
954 | fl.fl6_flowlabel |= ntohl(((__u32)iph->tos << IPV6_TCLASS_SHIFT) | ||
955 | & IPV6_TCLASS_MASK); | ||
956 | |||
957 | err = ip6_tnl_xmit2(skb, dev, dsfield, &fl, encap_limit, &mtu); | ||
958 | if (err != 0) { | ||
959 | /* XXX: send ICMP error even if DF is not set. */ | ||
960 | if (err == -EMSGSIZE) | ||
961 | icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, | ||
962 | htonl(mtu)); | ||
963 | return -1; | ||
964 | } | ||
965 | |||
966 | return 0; | ||
967 | } | ||
968 | |||
969 | static inline int | ||
812 | ip6ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev) | 970 | ip6ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev) |
813 | { | 971 | { |
814 | struct ip6_tnl *t = netdev_priv(dev); | 972 | struct ip6_tnl *t = netdev_priv(dev); |
@@ -867,6 +1025,9 @@ ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev) | |||
867 | } | 1025 | } |
868 | 1026 | ||
869 | switch (skb->protocol) { | 1027 | switch (skb->protocol) { |
1028 | case __constant_htons(ETH_P_IP): | ||
1029 | ret = ip4ip6_tnl_xmit(skb, dev); | ||
1030 | break; | ||
870 | case __constant_htons(ETH_P_IPV6): | 1031 | case __constant_htons(ETH_P_IPV6): |
871 | ret = ip6ip6_tnl_xmit(skb, dev); | 1032 | ret = ip6ip6_tnl_xmit(skb, dev); |
872 | break; | 1033 | break; |
@@ -1199,6 +1360,12 @@ ip6ip6_fb_tnl_dev_init(struct net_device *dev) | |||
1199 | return 0; | 1360 | return 0; |
1200 | } | 1361 | } |
1201 | 1362 | ||
1363 | static struct xfrm6_tunnel ip4ip6_handler = { | ||
1364 | .handler = ip4ip6_rcv, | ||
1365 | .err_handler = ip4ip6_err, | ||
1366 | .priority = 1, | ||
1367 | }; | ||
1368 | |||
1202 | static struct xfrm6_tunnel ip6ip6_handler = { | 1369 | static struct xfrm6_tunnel ip6ip6_handler = { |
1203 | .handler = ip6ip6_rcv, | 1370 | .handler = ip6ip6_rcv, |
1204 | .err_handler = ip6ip6_err, | 1371 | .err_handler = ip6ip6_err, |
@@ -1215,9 +1382,16 @@ static int __init ip6_tunnel_init(void) | |||
1215 | { | 1382 | { |
1216 | int err; | 1383 | int err; |
1217 | 1384 | ||
1385 | if (xfrm6_tunnel_register(&ip4ip6_handler, AF_INET)) { | ||
1386 | printk(KERN_ERR "ip4ip6 init: can't register tunnel\n"); | ||
1387 | err = -EAGAIN; | ||
1388 | goto out; | ||
1389 | } | ||
1390 | |||
1218 | if (xfrm6_tunnel_register(&ip6ip6_handler, AF_INET6)) { | 1391 | if (xfrm6_tunnel_register(&ip6ip6_handler, AF_INET6)) { |
1219 | printk(KERN_ERR "ip6ip6 init: can't register tunnel\n"); | 1392 | printk(KERN_ERR "ip6ip6 init: can't register tunnel\n"); |
1220 | return -EAGAIN; | 1393 | err = -EAGAIN; |
1394 | goto unreg_ip4ip6; | ||
1221 | } | 1395 | } |
1222 | ip6ip6_fb_tnl_dev = alloc_netdev(sizeof(struct ip6_tnl), "ip6tnl0", | 1396 | ip6ip6_fb_tnl_dev = alloc_netdev(sizeof(struct ip6_tnl), "ip6tnl0", |
1223 | ip6ip6_tnl_dev_setup); | 1397 | ip6ip6_tnl_dev_setup); |
@@ -1235,6 +1409,9 @@ static int __init ip6_tunnel_init(void) | |||
1235 | return 0; | 1409 | return 0; |
1236 | fail: | 1410 | fail: |
1237 | xfrm6_tunnel_deregister(&ip6ip6_handler, AF_INET6); | 1411 | xfrm6_tunnel_deregister(&ip6ip6_handler, AF_INET6); |
1412 | unreg_ip4ip6: | ||
1413 | xfrm6_tunnel_deregister(&ip4ip6_handler, AF_INET); | ||
1414 | out: | ||
1238 | return err; | 1415 | return err; |
1239 | } | 1416 | } |
1240 | 1417 | ||
@@ -1258,6 +1435,9 @@ static void __exit ip6ip6_destroy_tunnels(void) | |||
1258 | 1435 | ||
1259 | static void __exit ip6_tunnel_cleanup(void) | 1436 | static void __exit ip6_tunnel_cleanup(void) |
1260 | { | 1437 | { |
1438 | if (xfrm6_tunnel_deregister(&ip4ip6_handler, AF_INET)) | ||
1439 | printk(KERN_INFO "ip4ip6 close: can't deregister tunnel\n"); | ||
1440 | |||
1261 | if (xfrm6_tunnel_deregister(&ip6ip6_handler, AF_INET6)) | 1441 | if (xfrm6_tunnel_deregister(&ip6ip6_handler, AF_INET6)) |
1262 | printk(KERN_INFO "ip6ip6 close: can't deregister tunnel\n"); | 1442 | printk(KERN_INFO "ip6ip6 close: can't deregister tunnel\n"); |
1263 | 1443 | ||