diff options
Diffstat (limited to 'net/ipv6/sit.c')
-rw-r--r-- | net/ipv6/sit.c | 124 |
1 files changed, 116 insertions, 8 deletions
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 | } |