}
 }
 
+static void addrconf_addr_gen(struct inet6_dev *idev, bool prefix_route)
+{
+       if (idev->addr_gen_mode == IN6_ADDR_GEN_MODE_EUI64) {
+               struct in6_addr addr;
+
+               ipv6_addr_set(&addr,  htonl(0xFE800000), 0, 0, 0);
+               /* 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.
+                */
+               if (ipv6_generate_eui64(addr.s6_addr + 8, idev->dev) == 0)
+                       addrconf_add_linklocal(idev, &addr);
+               else if (prefix_route)
+                       addrconf_prefix_route(&addr, 64, idev->dev, 0, 0);
+       }
+}
+
 static void addrconf_dev_config(struct net_device *dev)
 {
-       struct in6_addr addr;
        struct inet6_dev *idev;
 
        ASSERT_RTNL();
        if (IS_ERR(idev))
                return;
 
-       memset(&addr, 0, sizeof(struct in6_addr));
-       addr.s6_addr32[0] = htonl(0xFE800000);
-
-       if (ipv6_generate_eui64(addr.s6_addr + 8, dev) == 0)
-               addrconf_add_linklocal(idev, &addr);
+       addrconf_addr_gen(idev, false);
 }
 
 #if IS_ENABLED(CONFIG_IPV6_SIT)
        }
 
        if (dev->priv_flags & IFF_ISATAP) {
-               struct in6_addr addr;
-
-               ipv6_addr_set(&addr,  htonl(0xFE800000), 0, 0, 0);
-               if (!ipv6_generate_eui64(addr.s6_addr + 8, dev))
-                       addrconf_add_linklocal(idev, &addr);
+               addrconf_addr_gen(idev, false);
                return;
        }
 
 static void addrconf_gre_config(struct net_device *dev)
 {
        struct inet6_dev *idev;
-       struct in6_addr addr;
 
        ASSERT_RTNL();
 
                return;
        }
 
-       ipv6_addr_set(&addr,  htonl(0xFE800000), 0, 0, 0);
-       if (!ipv6_generate_eui64(addr.s6_addr + 8, dev))
-               addrconf_add_linklocal(idev, &addr);
-       else
-               addrconf_prefix_route(&addr, 64, dev, 0, 0);
+       addrconf_addr_gen(idev, true);
 }
 #endif
 
        nla = nla_reserve(skb, IFLA_INET6_TOKEN, sizeof(struct in6_addr));
        if (nla == NULL)
                goto nla_put_failure;
+
+       if (nla_put_u8(skb, IFLA_INET6_ADDR_GEN_MODE, idev->addr_gen_mode))
+               goto nla_put_failure;
+
        read_lock_bh(&idev->lock);
        memcpy(nla_data(nla), idev->token.s6_addr, nla_len(nla));
        read_unlock_bh(&idev->lock);
        if (nla_parse_nested(tb, IFLA_INET6_MAX, nla, NULL) < 0)
                BUG();
 
-       if (tb[IFLA_INET6_TOKEN])
+       if (tb[IFLA_INET6_TOKEN]) {
                err = inet6_set_iftoken(idev, nla_data(tb[IFLA_INET6_TOKEN]));
+               if (err)
+                       return err;
+       }
+
+       if (tb[IFLA_INET6_ADDR_GEN_MODE]) {
+               u8 mode = nla_get_u8(tb[IFLA_INET6_ADDR_GEN_MODE]);
+
+               if (mode != IN6_ADDR_GEN_MODE_EUI64 &&
+                   mode != IN6_ADDR_GEN_MODE_NONE)
+                       return -EINVAL;
+               idev->addr_gen_mode = mode;
+               err = 0;
+       }
 
        return err;
 }