ipv6: addrconf: use stable address generator for ARPHRD_NONE
authorBjørn Mork <bjorn@mork.no>
Wed, 16 Dec 2015 15:44:38 +0000 (16:44 +0100)
committerDavid S. Miller <davem@davemloft.net>
Fri, 18 Dec 2015 19:41:07 +0000 (14:41 -0500)
Add a new address generator mode, using the stable address generator
with an automatically generated secret. This is intended as a default
address generator mode for device types with no EUI64 implementation.
The new generator is used for ARPHRD_NONE interfaces initially, adding
default IPv6 autoconf support to e.g. tun interfaces.

If the addrgenmode is set to 'random', either by default or manually,
and no stable secret is available, then a random secret is used as
input for the stable-privacy address generator.  The secret can be
read and modified like manually configured secrets, using the proc
interface.  Modifying the secret will change the addrgen mode to
'stable-privacy' to indicate that it operates on a known secret.

Existing behaviour of the 'stable-privacy' mode is kept unchanged. If
a known secret is available when the device is created, then the mode
will default to 'stable-privacy' as before.  The mode can be manually
set to 'random' but it will behave exactly like 'stable-privacy' in
this case. The secret will not change.

Cc: Hannes Frederic Sowa <hannes@stressinduktion.org>
Cc: 吉藤英明 <hideaki.yoshifuji@miraclelinux.com>
Signed-off-by: Bjørn Mork <bjorn@mork.no>
Acked-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/uapi/linux/if_link.h
net/ipv6/addrconf.c

index 2be1dd5..a30b780 100644 (file)
@@ -218,6 +218,7 @@ enum in6_addr_gen_mode {
        IN6_ADDR_GEN_MODE_EUI64,
        IN6_ADDR_GEN_MODE_NONE,
        IN6_ADDR_GEN_MODE_STABLE_PRIVACY,
+       IN6_ADDR_GEN_MODE_RANDOM,
 };
 
 /* Bridge section */
index 233efa6..819b777 100644 (file)
@@ -2319,6 +2319,12 @@ static void manage_tempaddrs(struct inet6_dev *idev,
        }
 }
 
+static bool is_addr_mode_generate_stable(struct inet6_dev *idev)
+{
+       return idev->addr_gen_mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY ||
+              idev->addr_gen_mode == IN6_ADDR_GEN_MODE_RANDOM;
+}
+
 void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
 {
        struct prefix_info *pinfo;
@@ -2432,8 +2438,7 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
                                       in6_dev->token.s6_addr + 8, 8);
                                read_unlock_bh(&in6_dev->lock);
                                tokenized = true;
-                       } else if (in6_dev->addr_gen_mode ==
-                                  IN6_ADDR_GEN_MODE_STABLE_PRIVACY &&
+                       } else if (is_addr_mode_generate_stable(in6_dev) &&
                                   !ipv6_generate_stable_address(&addr, 0,
                                                                 in6_dev)) {
                                addr_flags |= IFA_F_STABLE_PRIVACY;
@@ -3033,6 +3038,17 @@ retry:
        return 0;
 }
 
+static void ipv6_gen_mode_random_init(struct inet6_dev *idev)
+{
+       struct ipv6_stable_secret *s = &idev->cnf.stable_secret;
+
+       if (s->initialized)
+               return;
+       s = &idev->cnf.stable_secret;
+       get_random_bytes(&s->secret, sizeof(s->secret));
+       s->initialized = true;
+}
+
 static void addrconf_addr_gen(struct inet6_dev *idev, bool prefix_route)
 {
        struct in6_addr addr;
@@ -3043,13 +3059,18 @@ static void addrconf_addr_gen(struct inet6_dev *idev, bool prefix_route)
 
        ipv6_addr_set(&addr, htonl(0xFE800000), 0, 0, 0);
 
-       if (idev->addr_gen_mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY) {
+       switch (idev->addr_gen_mode) {
+       case IN6_ADDR_GEN_MODE_RANDOM:
+               ipv6_gen_mode_random_init(idev);
+               /* fallthrough */
+       case IN6_ADDR_GEN_MODE_STABLE_PRIVACY:
                if (!ipv6_generate_stable_address(&addr, 0, idev))
                        addrconf_add_linklocal(idev, &addr,
                                               IFA_F_STABLE_PRIVACY);
                else if (prefix_route)
                        addrconf_prefix_route(&addr, 64, idev->dev, 0, 0);
-       } else if (idev->addr_gen_mode == IN6_ADDR_GEN_MODE_EUI64) {
+               break;
+       case IN6_ADDR_GEN_MODE_EUI64:
                /* addrconf_add_linklocal also adds a prefix_route and we
                 * only need to care about prefix routes if ipv6_generate_eui64
                 * couldn't generate one.
@@ -3058,6 +3079,11 @@ static void addrconf_addr_gen(struct inet6_dev *idev, bool prefix_route)
                        addrconf_add_linklocal(idev, &addr, 0);
                else if (prefix_route)
                        addrconf_prefix_route(&addr, 64, idev->dev, 0, 0);
+               break;
+       case IN6_ADDR_GEN_MODE_NONE:
+       default:
+               /* will not add any link local address */
+               break;
        }
 }
 
@@ -3073,7 +3099,8 @@ static void addrconf_dev_config(struct net_device *dev)
            (dev->type != ARPHRD_INFINIBAND) &&
            (dev->type != ARPHRD_IEEE1394) &&
            (dev->type != ARPHRD_TUNNEL6) &&
-           (dev->type != ARPHRD_6LOWPAN)) {
+           (dev->type != ARPHRD_6LOWPAN) &&
+           (dev->type != ARPHRD_NONE)) {
                /* Alas, we support only Ethernet autoconfiguration. */
                return;
        }
@@ -3082,6 +3109,11 @@ static void addrconf_dev_config(struct net_device *dev)
        if (IS_ERR(idev))
                return;
 
+       /* this device type has no EUI support */
+       if (dev->type == ARPHRD_NONE &&
+           idev->addr_gen_mode == IN6_ADDR_GEN_MODE_EUI64)
+               idev->addr_gen_mode = IN6_ADDR_GEN_MODE_RANDOM;
+
        addrconf_addr_gen(idev, false);
 }
 
@@ -4926,7 +4958,8 @@ static int inet6_set_link_af(struct net_device *dev, const struct nlattr *nla)
 
                if (mode != IN6_ADDR_GEN_MODE_EUI64 &&
                    mode != IN6_ADDR_GEN_MODE_NONE &&
-                   mode != IN6_ADDR_GEN_MODE_STABLE_PRIVACY)
+                   mode != IN6_ADDR_GEN_MODE_STABLE_PRIVACY &&
+                   mode != IN6_ADDR_GEN_MODE_RANDOM)
                        return -EINVAL;
 
                if (mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY &&