diff options
Diffstat (limited to 'net')
| -rw-r--r-- | net/ipv6/Kconfig | 19 | ||||
| -rw-r--r-- | net/ipv6/sit.c | 124 |
2 files changed, 135 insertions, 8 deletions
diff --git a/net/ipv6/Kconfig b/net/ipv6/Kconfig index ead6c7a42f44..f56199827452 100644 --- a/net/ipv6/Kconfig +++ b/net/ipv6/Kconfig | |||
| @@ -170,6 +170,25 @@ config IPV6_SIT | |||
| 170 | 170 | ||
| 171 | Saying M here will produce a module called sit. If unsure, say Y. | 171 | Saying M here will produce a module called sit. If unsure, say Y. |
| 172 | 172 | ||
| 173 | config IPV6_SIT_6RD | ||
| 174 | bool "IPv6: IPv6 Rapid Development (6RD) (EXPERIMENTAL)" | ||
| 175 | depends on IPV6_SIT && EXPERIMENTAL | ||
| 176 | default n | ||
| 177 | ---help--- | ||
| 178 | IPv6 Rapid Deployment (6rd; draft-ietf-softwire-ipv6-6rd) builds upon | ||
| 179 | mechanisms of 6to4 (RFC3056) to enable a service provider to rapidly | ||
| 180 | deploy IPv6 unicast service to IPv4 sites to which it provides | ||
| 181 | customer premise equipment. Like 6to4, it utilizes stateless IPv6 in | ||
| 182 | IPv4 encapsulation in order to transit IPv4-only network | ||
| 183 | infrastructure. Unlike 6to4, a 6rd service provider uses an IPv6 | ||
| 184 | prefix of its own in place of the fixed 6to4 prefix. | ||
| 185 | |||
| 186 | With this option enabled, the SIT driver offers 6rd functionality by | ||
| 187 | providing additional ioctl API to configure the IPv6 Prefix for in | ||
| 188 | stead of static 2002::/16 for 6to4. | ||
| 189 | |||
| 190 | If unsure, say N. | ||
| 191 | |||
| 173 | config IPV6_NDISC_NODETYPE | 192 | config IPV6_NDISC_NODETYPE |
| 174 | bool | 193 | bool |
| 175 | 194 | ||
diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index 99da272951dc..6955654262a5 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c | |||
| @@ -161,6 +161,21 @@ static void ipip6_tunnel_link(struct sit_net *sitn, struct ip_tunnel *t) | |||
| 161 | write_unlock_bh(&ipip6_lock); | 161 | write_unlock_bh(&ipip6_lock); |
| 162 | } | 162 | } |
| 163 | 163 | ||
| 164 | static void ipip6_tunnel_clone_6rd(struct ip_tunnel *t, struct sit_net *sitn) | ||
| 165 | { | ||
| 166 | #ifdef CONFIG_IPV6_SIT_6RD | ||
| 167 | if (t->dev == sitn->fb_tunnel_dev) { | ||
| 168 | ipv6_addr_set(&t->ip6rd.prefix, htonl(0x20020000), 0, 0, 0); | ||
| 169 | t->ip6rd.relay_prefix = 0; | ||
| 170 | t->ip6rd.prefixlen = 16; | ||
| 171 | t->ip6rd.relay_prefixlen = 0; | ||
| 172 | } else { | ||
| 173 | struct ip_tunnel *t0 = netdev_priv(sitn->fb_tunnel_dev); | ||
| 174 | memcpy(&t->ip6rd, &t0->ip6rd, sizeof(t->ip6rd)); | ||
| 175 | } | ||
| 176 | #endif | ||
| 177 | } | ||
| 178 | |||
| 164 | static struct ip_tunnel * ipip6_tunnel_locate(struct net *net, | 179 | static struct ip_tunnel * ipip6_tunnel_locate(struct net *net, |
| 165 | struct ip_tunnel_parm *parms, int create) | 180 | struct ip_tunnel_parm *parms, int create) |
| 166 | { | 181 | { |
| @@ -213,6 +228,8 @@ static struct ip_tunnel * ipip6_tunnel_locate(struct net *net, | |||
| 213 | 228 | ||
| 214 | dev_hold(dev); | 229 | dev_hold(dev); |
| 215 | 230 | ||
| 231 | ipip6_tunnel_clone_6rd(t, sitn); | ||
| 232 | |||
| 216 | ipip6_tunnel_link(sitn, nt); | 233 | ipip6_tunnel_link(sitn, nt); |
| 217 | return nt; | 234 | return nt; |
| 218 | 235 | ||
| @@ -532,17 +549,41 @@ out: | |||
| 532 | return 0; | 549 | return 0; |
| 533 | } | 550 | } |
| 534 | 551 | ||
| 535 | /* Returns the embedded IPv4 address if the IPv6 address | 552 | /* |
| 536 | comes from 6to4 (RFC 3056) addr space */ | 553 | * Returns the embedded IPv4 address if the IPv6 address |
| 537 | 554 | * comes from 6rd / 6to4 (RFC 3056) addr space. | |
| 538 | static inline __be32 try_6to4(struct in6_addr *v6dst) | 555 | */ |
| 556 | static inline | ||
| 557 | __be32 try_6rd(struct in6_addr *v6dst, struct ip_tunnel *tunnel) | ||
| 539 | { | 558 | { |
| 540 | __be32 dst = 0; | 559 | __be32 dst = 0; |
| 541 | 560 | ||
| 561 | #ifdef CONFIG_IPV6_SIT_6RD | ||
| 562 | if (ipv6_prefix_equal(v6dst, &tunnel->ip6rd.prefix, | ||
| 563 | tunnel->ip6rd.prefixlen)) { | ||
| 564 | unsigned pbw0, pbi0; | ||
| 565 | int pbi1; | ||
| 566 | u32 d; | ||
| 567 | |||
| 568 | pbw0 = tunnel->ip6rd.prefixlen >> 5; | ||
| 569 | pbi0 = tunnel->ip6rd.prefixlen & 0x1f; | ||
| 570 | |||
| 571 | d = (ntohl(tunnel->ip6rd.prefix.s6_addr32[pbw0]) << pbi0) >> | ||
| 572 | tunnel->ip6rd.relay_prefixlen; | ||
| 573 | |||
| 574 | pbi1 = pbi0 - tunnel->ip6rd.relay_prefixlen; | ||
| 575 | if (pbi1 > 0) | ||
| 576 | d |= ntohl(tunnel->ip6rd.prefix.s6_addr32[pbw0 + 1]) >> | ||
| 577 | (32 - pbi1); | ||
| 578 | |||
| 579 | dst = tunnel->ip6rd.relay_prefix | htonl(d); | ||
| 580 | } | ||
| 581 | #else | ||
| 542 | if (v6dst->s6_addr16[0] == htons(0x2002)) { | 582 | if (v6dst->s6_addr16[0] == htons(0x2002)) { |
| 543 | /* 6to4 v6 addr has 16 bits prefix, 32 v4addr, 16 SLA, ... */ | 583 | /* 6to4 v6 addr has 16 bits prefix, 32 v4addr, 16 SLA, ... */ |
| 544 | memcpy(&dst, &v6dst->s6_addr16[1], 4); | 584 | memcpy(&dst, &v6dst->s6_addr16[1], 4); |
| 545 | } | 585 | } |
| 586 | #endif | ||
| 546 | return dst; | 587 | return dst; |
| 547 | } | 588 | } |
| 548 | 589 | ||
| @@ -596,7 +637,7 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb, | |||
| 596 | } | 637 | } |
| 597 | 638 | ||
| 598 | if (!dst) | 639 | if (!dst) |
| 599 | dst = try_6to4(&iph6->daddr); | 640 | dst = try_6rd(&iph6->daddr, tunnel); |
| 600 | 641 | ||
| 601 | if (!dst) { | 642 | if (!dst) { |
| 602 | struct neighbour *neigh = NULL; | 643 | struct neighbour *neigh = NULL; |
| @@ -786,9 +827,15 @@ ipip6_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd) | |||
| 786 | struct ip_tunnel *t; | 827 | struct ip_tunnel *t; |
| 787 | struct net *net = dev_net(dev); | 828 | struct net *net = dev_net(dev); |
| 788 | struct sit_net *sitn = net_generic(net, sit_net_id); | 829 | struct sit_net *sitn = net_generic(net, sit_net_id); |
| 830 | #ifdef CONFIG_IPV6_SIT_6RD | ||
| 831 | struct ip_tunnel_6rd ip6rd; | ||
| 832 | #endif | ||
| 789 | 833 | ||
| 790 | switch (cmd) { | 834 | switch (cmd) { |
| 791 | case SIOCGETTUNNEL: | 835 | case SIOCGETTUNNEL: |
| 836 | #ifdef CONFIG_IPV6_SIT_6RD | ||
| 837 | case SIOCGET6RD: | ||
| 838 | #endif | ||
| 792 | t = NULL; | 839 | t = NULL; |
| 793 | if (dev == sitn->fb_tunnel_dev) { | 840 | if (dev == sitn->fb_tunnel_dev) { |
| 794 | if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p))) { | 841 | if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p))) { |
| @@ -799,9 +846,25 @@ ipip6_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd) | |||
| 799 | } | 846 | } |
| 800 | if (t == NULL) | 847 | if (t == NULL) |
| 801 | t = netdev_priv(dev); | 848 | t = netdev_priv(dev); |
| 802 | memcpy(&p, &t->parms, sizeof(p)); | 849 | |
| 803 | if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof(p))) | 850 | err = -EFAULT; |
| 804 | err = -EFAULT; | 851 | if (cmd == SIOCGETTUNNEL) { |
| 852 | memcpy(&p, &t->parms, sizeof(p)); | ||
| 853 | if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, | ||
| 854 | sizeof(p))) | ||
| 855 | goto done; | ||
| 856 | #ifdef CONFIG_IPV6_SIT_6RD | ||
| 857 | } else { | ||
| 858 | ipv6_addr_copy(&ip6rd.prefix, &t->ip6rd.prefix); | ||
| 859 | ip6rd.relay_prefix = t->ip6rd.relay_prefix; | ||
| 860 | ip6rd.prefixlen = t->ip6rd.prefixlen; | ||
| 861 | ip6rd.relay_prefixlen = t->ip6rd.relay_prefixlen; | ||
| 862 | if (copy_to_user(ifr->ifr_ifru.ifru_data, &ip6rd, | ||
| 863 | sizeof(ip6rd))) | ||
| 864 | goto done; | ||
| 865 | #endif | ||
| 866 | } | ||
| 867 | err = 0; | ||
| 805 | break; | 868 | break; |
| 806 | 869 | ||
| 807 | case SIOCADDTUNNEL: | 870 | case SIOCADDTUNNEL: |
| @@ -922,6 +985,51 @@ ipip6_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd) | |||
| 922 | netdev_state_change(dev); | 985 | netdev_state_change(dev); |
| 923 | break; | 986 | break; |
| 924 | 987 | ||
| 988 | #ifdef CONFIG_IPV6_SIT_6RD | ||
| 989 | case SIOCADD6RD: | ||
| 990 | case SIOCCHG6RD: | ||
| 991 | case SIOCDEL6RD: | ||
| 992 | err = -EPERM; | ||
| 993 | if (!capable(CAP_NET_ADMIN)) | ||
| 994 | goto done; | ||
| 995 | |||
| 996 | err = -EFAULT; | ||
| 997 | if (copy_from_user(&ip6rd, ifr->ifr_ifru.ifru_data, | ||
| 998 | sizeof(ip6rd))) | ||
| 999 | goto done; | ||
| 1000 | |||
| 1001 | t = netdev_priv(dev); | ||
| 1002 | |||
| 1003 | if (cmd != SIOCDEL6RD) { | ||
| 1004 | struct in6_addr prefix; | ||
| 1005 | __be32 relay_prefix; | ||
| 1006 | |||
| 1007 | err = -EINVAL; | ||
| 1008 | if (ip6rd.relay_prefixlen > 32 || | ||
| 1009 | ip6rd.prefixlen + (32 - ip6rd.relay_prefixlen) > 64) | ||
| 1010 | goto done; | ||
| 1011 | |||
| 1012 | ipv6_addr_prefix(&prefix, &ip6rd.prefix, | ||
| 1013 | ip6rd.prefixlen); | ||
| 1014 | if (!ipv6_addr_equal(&prefix, &ip6rd.prefix)) | ||
| 1015 | goto done; | ||
| 1016 | relay_prefix = ip6rd.relay_prefix & | ||
| 1017 | htonl(0xffffffffUL << | ||
| 1018 | (32 - ip6rd.relay_prefixlen)); | ||
| 1019 | if (relay_prefix != ip6rd.relay_prefix) | ||
| 1020 | goto done; | ||
| 1021 | |||
| 1022 | ipv6_addr_copy(&t->ip6rd.prefix, &prefix); | ||
| 1023 | t->ip6rd.relay_prefix = relay_prefix; | ||
| 1024 | t->ip6rd.prefixlen = ip6rd.prefixlen; | ||
| 1025 | t->ip6rd.relay_prefixlen = ip6rd.relay_prefixlen; | ||
| 1026 | } else | ||
| 1027 | ipip6_tunnel_clone_6rd(t, sitn); | ||
| 1028 | |||
| 1029 | err = 0; | ||
| 1030 | break; | ||
| 1031 | #endif | ||
| 1032 | |||
| 925 | default: | 1033 | default: |
| 926 | err = -EINVAL; | 1034 | err = -EINVAL; |
| 927 | } | 1035 | } |
