ipv6 sit: 6rd (IPv6 Rapid Deployment) Support.
authorYOSHIFUJI Hideaki / 吉藤英明 <yoshfuji@linux-ipv6.org>
Tue, 22 Sep 2009 23:43:14 +0000 (23:43 +0000)
committerDavid S. Miller <davem@davemloft.net>
Wed, 7 Oct 2009 08:07:37 +0000 (01:07 -0700)
IPv6 Rapid Deployment (6rd; draft-ietf-softwire-ipv6-6rd) builds upon
mechanisms of 6to4 (RFC3056) to enable a service provider to rapidly
deploy IPv6 unicast service to IPv4 sites to which it provides
customer premise equipment.  Like 6to4, it utilizes stateless IPv6 in
IPv4 encapsulation in order to transit IPv4-only network
infrastructure.  Unlike 6to4, a 6rd service provider uses an IPv6
prefix of its own in place of the fixed 6to4 prefix.

With this option enabled, the SIT driver offers 6rd functionality by
providing additional ioctl API to configure the IPv6 Prefix for in
stead of static 2002::/16 for 6to4.

Original patch was done by Alexandre Cassen <acassen@freebox.fr>
based on old Internet-Draft.

Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/linux/if_tunnel.h
include/net/ipip.h
net/ipv6/Kconfig
net/ipv6/sit.c

index 5a9aae4..c53c8e0 100644 (file)
 #define SIOCADDPRL      (SIOCDEVPRIVATE + 5)
 #define SIOCDELPRL      (SIOCDEVPRIVATE + 6)
 #define SIOCCHGPRL      (SIOCDEVPRIVATE + 7)
+#define SIOCGET6RD      (SIOCDEVPRIVATE + 8)
+#define SIOCADD6RD      (SIOCDEVPRIVATE + 9)
+#define SIOCDEL6RD      (SIOCDEVPRIVATE + 10)
+#define SIOCCHG6RD      (SIOCDEVPRIVATE + 11)
 
 #define GRE_CSUM       __cpu_to_be16(0x8000)
 #define GRE_ROUTING    __cpu_to_be16(0x4000)
@@ -51,6 +55,13 @@ struct ip_tunnel_prl {
 /* PRL flags */
 #define        PRL_DEFAULT             0x0001
 
+struct ip_tunnel_6rd {
+       struct in6_addr         prefix;
+       __be32                  relay_prefix;
+       __u16                   prefixlen;
+       __u16                   relay_prefixlen;
+};
+
 enum
 {
        IFLA_GRE_UNSPEC,
index 0159221..86f1c8b 100644 (file)
@@ -7,6 +7,15 @@
 /* Keep error state on tunnel for 30 sec */
 #define IPTUNNEL_ERR_TIMEO     (30*HZ)
 
+/* 6rd prefix/relay information */
+struct ip_tunnel_6rd_parm
+{
+       struct in6_addr         prefix;
+       __be32                  relay_prefix;
+       u16                     prefixlen;
+       u16                     relay_prefixlen;
+};
+
 struct ip_tunnel
 {
        struct ip_tunnel        *next;
@@ -23,6 +32,10 @@ struct ip_tunnel
 
        struct ip_tunnel_parm   parms;
 
+       /* for SIT */
+#ifdef CONFIG_IPV6_SIT_6RD
+       struct ip_tunnel_6rd_parm       ip6rd;
+#endif
        struct ip_tunnel_prl_entry      *prl;           /* potential router list */
        unsigned int                    prl_count;      /* # of entries in PRL */
 };
index ead6c7a..f561998 100644 (file)
@@ -170,6 +170,25 @@ config IPV6_SIT
 
          Saying M here will produce a module called sit. If unsure, say Y.
 
+config IPV6_SIT_6RD
+       bool "IPv6: IPv6 Rapid Development (6RD) (EXPERIMENTAL)"
+       depends on IPV6_SIT && EXPERIMENTAL
+       default n
+       ---help---
+         IPv6 Rapid Deployment (6rd; draft-ietf-softwire-ipv6-6rd) builds upon
+         mechanisms of 6to4 (RFC3056) to enable a service provider to rapidly
+         deploy IPv6 unicast service to IPv4 sites to which it provides
+         customer premise equipment.  Like 6to4, it utilizes stateless IPv6 in
+         IPv4 encapsulation in order to transit IPv4-only network
+         infrastructure.  Unlike 6to4, a 6rd service provider uses an IPv6
+         prefix of its own in place of the fixed 6to4 prefix.
+
+         With this option enabled, the SIT driver offers 6rd functionality by
+         providing additional ioctl API to configure the IPv6 Prefix for in
+         stead of static 2002::/16 for 6to4.
+
+         If unsure, say N.
+
 config IPV6_NDISC_NODETYPE
        bool
 
index 99da272..6955654 100644 (file)
@@ -161,6 +161,21 @@ static void ipip6_tunnel_link(struct sit_net *sitn, struct ip_tunnel *t)
        write_unlock_bh(&ipip6_lock);
 }
 
+static void ipip6_tunnel_clone_6rd(struct ip_tunnel *t, struct sit_net *sitn)
+{
+#ifdef CONFIG_IPV6_SIT_6RD
+       if (t->dev == sitn->fb_tunnel_dev) {
+               ipv6_addr_set(&t->ip6rd.prefix, htonl(0x20020000), 0, 0, 0);
+               t->ip6rd.relay_prefix = 0;
+               t->ip6rd.prefixlen = 16;
+               t->ip6rd.relay_prefixlen = 0;
+       } else {
+               struct ip_tunnel *t0 = netdev_priv(sitn->fb_tunnel_dev);
+               memcpy(&t->ip6rd, &t0->ip6rd, sizeof(t->ip6rd));
+       }
+#endif
+}
+
 static struct ip_tunnel * ipip6_tunnel_locate(struct net *net,
                struct ip_tunnel_parm *parms, int create)
 {
@@ -213,6 +228,8 @@ static struct ip_tunnel * ipip6_tunnel_locate(struct net *net,
 
        dev_hold(dev);
 
+       ipip6_tunnel_clone_6rd(t, sitn);
+
        ipip6_tunnel_link(sitn, nt);
        return nt;
 
@@ -532,17 +549,41 @@ out:
        return 0;
 }
 
-/* Returns the embedded IPv4 address if the IPv6 address
-   comes from 6to4 (RFC 3056) addr space */
-
-static inline __be32 try_6to4(struct in6_addr *v6dst)
+/*
+ * Returns the embedded IPv4 address if the IPv6 address
+ * comes from 6rd / 6to4 (RFC 3056) addr space.
+ */
+static inline
+__be32 try_6rd(struct in6_addr *v6dst, struct ip_tunnel *tunnel)
 {
        __be32 dst = 0;
 
+#ifdef CONFIG_IPV6_SIT_6RD
+       if (ipv6_prefix_equal(v6dst, &tunnel->ip6rd.prefix,
+                             tunnel->ip6rd.prefixlen)) {
+               unsigned pbw0, pbi0;
+               int pbi1;
+               u32 d;
+
+               pbw0 = tunnel->ip6rd.prefixlen >> 5;
+               pbi0 = tunnel->ip6rd.prefixlen & 0x1f;
+
+               d = (ntohl(tunnel->ip6rd.prefix.s6_addr32[pbw0]) << pbi0) >>
+                   tunnel->ip6rd.relay_prefixlen;
+
+               pbi1 = pbi0 - tunnel->ip6rd.relay_prefixlen;
+               if (pbi1 > 0)
+                       d |= ntohl(tunnel->ip6rd.prefix.s6_addr32[pbw0 + 1]) >>
+                            (32 - pbi1);
+
+               dst = tunnel->ip6rd.relay_prefix | htonl(d);
+       }
+#else
        if (v6dst->s6_addr16[0] == htons(0x2002)) {
                /* 6to4 v6 addr has 16 bits prefix, 32 v4addr, 16 SLA, ... */
                memcpy(&dst, &v6dst->s6_addr16[1], 4);
        }
+#endif
        return dst;
 }
 
@@ -596,7 +637,7 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb,
        }
 
        if (!dst)
-               dst = try_6to4(&iph6->daddr);
+               dst = try_6rd(&iph6->daddr, tunnel);
 
        if (!dst) {
                struct neighbour *neigh = NULL;
@@ -786,9 +827,15 @@ ipip6_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
        struct ip_tunnel *t;
        struct net *net = dev_net(dev);
        struct sit_net *sitn = net_generic(net, sit_net_id);
+#ifdef CONFIG_IPV6_SIT_6RD
+       struct ip_tunnel_6rd ip6rd;
+#endif
 
        switch (cmd) {
        case SIOCGETTUNNEL:
+#ifdef CONFIG_IPV6_SIT_6RD
+       case SIOCGET6RD:
+#endif
                t = NULL;
                if (dev == sitn->fb_tunnel_dev) {
                        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)
                }
                if (t == NULL)
                        t = netdev_priv(dev);
-               memcpy(&p, &t->parms, sizeof(p));
-               if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof(p)))
-                       err = -EFAULT;
+
+               err = -EFAULT;
+               if (cmd == SIOCGETTUNNEL) {
+                       memcpy(&p, &t->parms, sizeof(p));
+                       if (copy_to_user(ifr->ifr_ifru.ifru_data, &p,
+                                        sizeof(p)))
+                               goto done;
+#ifdef CONFIG_IPV6_SIT_6RD
+               } else {
+                       ipv6_addr_copy(&ip6rd.prefix, &t->ip6rd.prefix);
+                       ip6rd.relay_prefix = t->ip6rd.relay_prefix;
+                       ip6rd.prefixlen = t->ip6rd.prefixlen;
+                       ip6rd.relay_prefixlen = t->ip6rd.relay_prefixlen;
+                       if (copy_to_user(ifr->ifr_ifru.ifru_data, &ip6rd,
+                                        sizeof(ip6rd)))
+                               goto done;
+#endif
+               }
+               err = 0;
                break;
 
        case SIOCADDTUNNEL:
@@ -922,6 +985,51 @@ ipip6_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
                netdev_state_change(dev);
                break;
 
+#ifdef CONFIG_IPV6_SIT_6RD
+       case SIOCADD6RD:
+       case SIOCCHG6RD:
+       case SIOCDEL6RD:
+               err = -EPERM;
+               if (!capable(CAP_NET_ADMIN))
+                       goto done;
+
+               err = -EFAULT;
+               if (copy_from_user(&ip6rd, ifr->ifr_ifru.ifru_data,
+                                  sizeof(ip6rd)))
+                       goto done;
+
+               t = netdev_priv(dev);
+
+               if (cmd != SIOCDEL6RD) {
+                       struct in6_addr prefix;
+                       __be32 relay_prefix;
+
+                       err = -EINVAL;
+                       if (ip6rd.relay_prefixlen > 32 ||
+                           ip6rd.prefixlen + (32 - ip6rd.relay_prefixlen) > 64)
+                               goto done;
+
+                       ipv6_addr_prefix(&prefix, &ip6rd.prefix,
+                                        ip6rd.prefixlen);
+                       if (!ipv6_addr_equal(&prefix, &ip6rd.prefix))
+                               goto done;
+                       relay_prefix = ip6rd.relay_prefix &
+                                      htonl(0xffffffffUL <<
+                                            (32 - ip6rd.relay_prefixlen));
+                       if (relay_prefix != ip6rd.relay_prefix)
+                               goto done;
+
+                       ipv6_addr_copy(&t->ip6rd.prefix, &prefix);
+                       t->ip6rd.relay_prefix = relay_prefix;
+                       t->ip6rd.prefixlen = ip6rd.prefixlen;
+                       t->ip6rd.relay_prefixlen = ip6rd.relay_prefixlen;
+               } else
+                       ipip6_tunnel_clone_6rd(t, sitn);
+
+               err = 0;
+               break;
+#endif
+
        default:
                err = -EINVAL;
        }