sit: allow to configure 6rd tunnels via netlink
authorNicolas Dichtel <nicolas.dichtel@6wind.com>
Mon, 19 Nov 2012 22:41:45 +0000 (22:41 +0000)
committerDavid S. Miller <davem@davemloft.net>
Tue, 20 Nov 2012 18:43:28 +0000 (13:43 -0500)
This patch add the support of 6RD tunnels management via netlink.
Note that netdev_state_change() is now called when 6RD parameters are updated.

6RD parameters are updated only if there is at least one 6RD attribute.

Signed-off-by: Nicolas Dichtel <nicolas.dichtel@6wind.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/uapi/linux/if_tunnel.h
net/ipv6/sit.c

index 5ab0c8ddc2bc2cd755fd589aea14016dbfb6c279..aee73d0611fb9bbb72f3ab52a0b91c09ba861c12 100644 (file)
@@ -49,6 +49,10 @@ enum {
        IFLA_IPTUN_FLAGS,
        IFLA_IPTUN_PROTO,
        IFLA_IPTUN_PMTUDISC,
+       IFLA_IPTUN_6RD_PREFIX,
+       IFLA_IPTUN_6RD_RELAY_PREFIX,
+       IFLA_IPTUN_6RD_PREFIXLEN,
+       IFLA_IPTUN_6RD_RELAY_PREFIXLEN,
        __IFLA_IPTUN_MAX,
 };
 #define IFLA_IPTUN_MAX (__IFLA_IPTUN_MAX - 1)
index fee21c6c3ebff01c5ab43c065e010d421cdf1268..80cb3829831ce1ead743992c0febb9ec2bfefc13 100644 (file)
@@ -936,6 +936,38 @@ static void ipip6_tunnel_update(struct ip_tunnel *t, struct ip_tunnel_parm *p)
        netdev_state_change(t->dev);
 }
 
+#ifdef CONFIG_IPV6_SIT_6RD
+static int ipip6_tunnel_update_6rd(struct ip_tunnel *t,
+                                  struct ip_tunnel_6rd *ip6rd)
+{
+       struct in6_addr prefix;
+       __be32 relay_prefix;
+
+       if (ip6rd->relay_prefixlen > 32 ||
+           ip6rd->prefixlen + (32 - ip6rd->relay_prefixlen) > 64)
+               return -EINVAL;
+
+       ipv6_addr_prefix(&prefix, &ip6rd->prefix, ip6rd->prefixlen);
+       if (!ipv6_addr_equal(&prefix, &ip6rd->prefix))
+               return -EINVAL;
+       if (ip6rd->relay_prefixlen)
+               relay_prefix = ip6rd->relay_prefix &
+                              htonl(0xffffffffUL <<
+                                    (32 - ip6rd->relay_prefixlen));
+       else
+               relay_prefix = 0;
+       if (relay_prefix != ip6rd->relay_prefix)
+               return -EINVAL;
+
+       t->ip6rd.prefix = prefix;
+       t->ip6rd.relay_prefix = relay_prefix;
+       t->ip6rd.prefixlen = ip6rd->prefixlen;
+       t->ip6rd.relay_prefixlen = ip6rd->relay_prefixlen;
+       netdev_state_change(t->dev);
+       return 0;
+}
+#endif
+
 static int
 ipip6_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
 {
@@ -1105,31 +1137,9 @@ ipip6_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
                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))
+                       err = ipip6_tunnel_update_6rd(t, &ip6rd);
+                       if (err < 0)
                                goto done;
-                       if (ip6rd.relay_prefixlen)
-                               relay_prefix = ip6rd.relay_prefix &
-                                              htonl(0xffffffffUL <<
-                                                    (32 - ip6rd.relay_prefixlen));
-                       else
-                               relay_prefix = 0;
-                       if (relay_prefix != ip6rd.relay_prefix)
-                               goto done;
-
-                       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(dev, sitn);
 
@@ -1261,11 +1271,53 @@ static void ipip6_netlink_parms(struct nlattr *data[],
                parms->i_flags = nla_get_be16(data[IFLA_IPTUN_FLAGS]);
 }
 
+#ifdef CONFIG_IPV6_SIT_6RD
+/* This function returns true when 6RD attributes are present in the nl msg */
+static bool ipip6_netlink_6rd_parms(struct nlattr *data[],
+                                   struct ip_tunnel_6rd *ip6rd)
+{
+       bool ret = false;
+       memset(ip6rd, 0, sizeof(*ip6rd));
+
+       if (!data)
+               return ret;
+
+       if (data[IFLA_IPTUN_6RD_PREFIX]) {
+               ret = true;
+               nla_memcpy(&ip6rd->prefix, data[IFLA_IPTUN_6RD_PREFIX],
+                          sizeof(struct in6_addr));
+       }
+
+       if (data[IFLA_IPTUN_6RD_RELAY_PREFIX]) {
+               ret = true;
+               ip6rd->relay_prefix =
+                       nla_get_be32(data[IFLA_IPTUN_6RD_RELAY_PREFIX]);
+       }
+
+       if (data[IFLA_IPTUN_6RD_PREFIXLEN]) {
+               ret = true;
+               ip6rd->prefixlen = nla_get_u16(data[IFLA_IPTUN_6RD_PREFIXLEN]);
+       }
+
+       if (data[IFLA_IPTUN_6RD_RELAY_PREFIXLEN]) {
+               ret = true;
+               ip6rd->relay_prefixlen =
+                       nla_get_u16(data[IFLA_IPTUN_6RD_RELAY_PREFIXLEN]);
+       }
+
+       return ret;
+}
+#endif
+
 static int ipip6_newlink(struct net *src_net, struct net_device *dev,
                         struct nlattr *tb[], struct nlattr *data[])
 {
        struct net *net = dev_net(dev);
        struct ip_tunnel *nt;
+#ifdef CONFIG_IPV6_SIT_6RD
+       struct ip_tunnel_6rd ip6rd;
+#endif
+       int err;
 
        nt = netdev_priv(dev);
        ipip6_netlink_parms(data, &nt->parms);
@@ -1273,7 +1325,16 @@ static int ipip6_newlink(struct net *src_net, struct net_device *dev,
        if (ipip6_tunnel_locate(net, &nt->parms, 0))
                return -EEXIST;
 
-       return ipip6_tunnel_create(dev);
+       err = ipip6_tunnel_create(dev);
+       if (err < 0)
+               return err;
+
+#ifdef CONFIG_IPV6_SIT_6RD
+       if (ipip6_netlink_6rd_parms(data, &ip6rd))
+               err = ipip6_tunnel_update_6rd(nt, &ip6rd);
+#endif
+
+       return err;
 }
 
 static int ipip6_changelink(struct net_device *dev, struct nlattr *tb[],
@@ -1283,6 +1344,9 @@ static int ipip6_changelink(struct net_device *dev, struct nlattr *tb[],
        struct ip_tunnel_parm p;
        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
 
        if (dev == sitn->fb_tunnel_dev)
                return -EINVAL;
@@ -1302,6 +1366,12 @@ static int ipip6_changelink(struct net_device *dev, struct nlattr *tb[],
                t = netdev_priv(dev);
 
        ipip6_tunnel_update(t, &p);
+
+#ifdef CONFIG_IPV6_SIT_6RD
+       if (ipip6_netlink_6rd_parms(data, &ip6rd))
+               return ipip6_tunnel_update_6rd(t, &ip6rd);
+#endif
+
        return 0;
 }
 
@@ -1322,6 +1392,16 @@ static size_t ipip6_get_size(const struct net_device *dev)
                nla_total_size(1) +
                /* IFLA_IPTUN_FLAGS */
                nla_total_size(2) +
+#ifdef CONFIG_IPV6_SIT_6RD
+               /* IFLA_IPTUN_6RD_PREFIX */
+               nla_total_size(sizeof(struct in6_addr)) +
+               /* IFLA_IPTUN_6RD_RELAY_PREFIX */
+               nla_total_size(4) +
+               /* IFLA_IPTUN_6RD_PREFIXLEN */
+               nla_total_size(2) +
+               /* IFLA_IPTUN_6RD_RELAY_PREFIXLEN */
+               nla_total_size(2) +
+#endif
                0;
 }
 
@@ -1339,6 +1419,19 @@ static int ipip6_fill_info(struct sk_buff *skb, const struct net_device *dev)
                       !!(parm->iph.frag_off & htons(IP_DF))) ||
            nla_put_be16(skb, IFLA_IPTUN_FLAGS, parm->i_flags))
                goto nla_put_failure;
+
+#ifdef CONFIG_IPV6_SIT_6RD
+       if (nla_put(skb, IFLA_IPTUN_6RD_PREFIX, sizeof(struct in6_addr),
+                   &tunnel->ip6rd.prefix) ||
+           nla_put_be32(skb, IFLA_IPTUN_6RD_RELAY_PREFIX,
+                        tunnel->ip6rd.relay_prefix) ||
+           nla_put_u16(skb, IFLA_IPTUN_6RD_PREFIXLEN,
+                       tunnel->ip6rd.prefixlen) ||
+           nla_put_u16(skb, IFLA_IPTUN_6RD_RELAY_PREFIXLEN,
+                       tunnel->ip6rd.relay_prefixlen))
+               goto nla_put_failure;
+#endif
+
        return 0;
 
 nla_put_failure:
@@ -1353,6 +1446,12 @@ static const struct nla_policy ipip6_policy[IFLA_IPTUN_MAX + 1] = {
        [IFLA_IPTUN_TOS]                = { .type = NLA_U8 },
        [IFLA_IPTUN_PMTUDISC]           = { .type = NLA_U8 },
        [IFLA_IPTUN_FLAGS]              = { .type = NLA_U16 },
+#ifdef CONFIG_IPV6_SIT_6RD
+       [IFLA_IPTUN_6RD_PREFIX]         = { .len = sizeof(struct in6_addr) },
+       [IFLA_IPTUN_6RD_RELAY_PREFIX]   = { .type = NLA_U32 },
+       [IFLA_IPTUN_6RD_PREFIXLEN]      = { .type = NLA_U16 },
+       [IFLA_IPTUN_6RD_RELAY_PREFIXLEN] = { .type = NLA_U16 },
+#endif
 };
 
 static struct rtnl_link_ops sit_link_ops __read_mostly = {