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 | ||
