Merge tag 'for-4.5' of git://git.osdn.jp/gitroot/uclinux-h8/linux
[cascardo/linux.git] / net / ipv6 / addrconf.c
index 1f21087..38eedde 100644 (file)
@@ -70,7 +70,7 @@
 #include <net/sock.h>
 #include <net/snmp.h>
 
-#include <net/af_ieee802154.h>
+#include <net/6lowpan.h>
 #include <net/firewire.h>
 #include <net/ipv6.h>
 #include <net/protocol.h>
@@ -1772,12 +1772,13 @@ struct inet6_ifaddr *ipv6_get_ifaddr(struct net *net, const struct in6_addr *add
 
 static void addrconf_dad_stop(struct inet6_ifaddr *ifp, int dad_failed)
 {
+       if (dad_failed)
+               ifp->flags |= IFA_F_DADFAILED;
+
        if (ifp->flags&IFA_F_PERMANENT) {
                spin_lock_bh(&ifp->lock);
                addrconf_del_dad_work(ifp);
                ifp->flags |= IFA_F_TENTATIVE;
-               if (dad_failed)
-                       ifp->flags |= IFA_F_DADFAILED;
                spin_unlock_bh(&ifp->lock);
                if (dad_failed)
                        ipv6_ifa_notify(0, ifp);
@@ -1953,9 +1954,9 @@ static void addrconf_leave_anycast(struct inet6_ifaddr *ifp)
 
 static int addrconf_ifid_eui64(u8 *eui, struct net_device *dev)
 {
-       if (dev->addr_len != IEEE802154_ADDR_LEN)
+       if (dev->addr_len != EUI64_ADDR_LEN)
                return -1;
-       memcpy(eui, dev->dev_addr, 8);
+       memcpy(eui, dev->dev_addr, EUI64_ADDR_LEN);
        eui[0] ^= 2;
        return 0;
 }
@@ -2047,7 +2048,6 @@ static int ipv6_generate_eui64(u8 *eui, struct net_device *dev)
        case ARPHRD_IPGRE:
                return addrconf_ifid_gre(eui, dev);
        case ARPHRD_6LOWPAN:
-       case ARPHRD_IEEE802154:
                return addrconf_ifid_eui64(eui, dev);
        case ARPHRD_IEEE1394:
                return addrconf_ifid_ieee1394(eui, dev);
@@ -2320,6 +2320,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;
@@ -2433,8 +2439,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;
@@ -3034,6 +3039,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;
@@ -3044,13 +3060,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.
@@ -3059,6 +3080,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;
        }
 }
 
@@ -3072,10 +3098,10 @@ static void addrconf_dev_config(struct net_device *dev)
            (dev->type != ARPHRD_FDDI) &&
            (dev->type != ARPHRD_ARCNET) &&
            (dev->type != ARPHRD_INFINIBAND) &&
-           (dev->type != ARPHRD_IEEE802154) &&
            (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;
        }
@@ -3084,6 +3110,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);
 }
 
@@ -3293,7 +3324,8 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
 
        case NETDEV_PRE_TYPE_CHANGE:
        case NETDEV_POST_TYPE_CHANGE:
-               addrconf_type_change(dev, event);
+               if (idev)
+                       addrconf_type_change(dev, event);
                break;
        }
 
@@ -4927,7 +4959,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 &&
@@ -5205,6 +5238,20 @@ int addrconf_sysctl_forward(struct ctl_table *ctl, int write,
        return ret;
 }
 
+static
+int addrconf_sysctl_hop_limit(struct ctl_table *ctl, int write,
+                              void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+       struct ctl_table lctl;
+       int min_hl = 1, max_hl = 255;
+
+       lctl = *ctl;
+       lctl.extra1 = &min_hl;
+       lctl.extra2 = &max_hl;
+
+       return proc_dointvec_minmax(&lctl, write, buffer, lenp, ppos);
+}
+
 static
 int addrconf_sysctl_mtu(struct ctl_table *ctl, int write,
                        void __user *buffer, size_t *lenp, loff_t *ppos)
@@ -5457,7 +5504,7 @@ static struct addrconf_sysctl_table
                        .data           = &ipv6_devconf.hop_limit,
                        .maxlen         = sizeof(int),
                        .mode           = 0644,
-                       .proc_handler   = proc_dointvec,
+                       .proc_handler   = addrconf_sysctl_hop_limit,
                },
                {
                        .procname       = "mtu",