aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/addrconf.c
diff options
context:
space:
mode:
authorJiri Pirko <jiri@resnulli.us>2014-07-11 15:10:18 -0400
committerDavid S. Miller <davem@davemloft.net>2014-07-11 18:05:45 -0400
commitbc91b0f07ada5535427373a4e2050877bcc12218 (patch)
tree9987f86cc3c8a141d595fc8d29c04a67debd0723 /net/ipv6/addrconf.c
parent279f64b7a771d84cbdea51ac2f794becfb06bcd4 (diff)
ipv6: addrconf: implement address generation modes
This patch introduces a possibility for userspace to set various (so far two) modes of generating addresses. This is useful for example for NetworkManager because it can set the mode to NONE and take care of link local addresses itself. That allow it to have the interface up, monitoring carrier but still don't have any addresses on it. One more use-case by Dan Williams: <quote> WWAN devices often have their LL address provided by the firmware of the device, which sometimes refuses to respond to incorrect LL addresses when doing DHCPv6 or IPv6 ND. The kernel cannot generate the correct LL address for two reasons: 1) WWAN pseudo-ethernet interfaces often construct a fake MAC address, or read a meaningless MAC address from the firmware. Thus the EUI64 and the IPv6LL address the kernel assigns will be wrong. The real LL address is often retrieved from the firmware with AT or proprietary commands. 2) WWAN PPP interfaces receive their LL address from IPV6CP, not from kernel assignments. Only after IPV6CP has completed do we know the LL address of the PPP interface and its peer. But the kernel has already assigned an incorrect LL address to the interface. So being able to suppress the kernel LL address generation and assign the one retrieved from the firmware is less complicated and more robust. </quote> Signed-off-by: Jiri Pirko <jiri@resnulli.us> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6/addrconf.c')
-rw-r--r--net/ipv6/addrconf.c56
1 files changed, 38 insertions, 18 deletions
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 358edd2272ac..4c03c2843094 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -2730,9 +2730,25 @@ static void addrconf_add_linklocal(struct inet6_dev *idev, const struct in6_addr
2730 } 2730 }
2731} 2731}
2732 2732
2733static void addrconf_addr_gen(struct inet6_dev *idev, bool prefix_route)
2734{
2735 if (idev->addr_gen_mode == IN6_ADDR_GEN_MODE_EUI64) {
2736 struct in6_addr addr;
2737
2738 ipv6_addr_set(&addr, htonl(0xFE800000), 0, 0, 0);
2739 /* addrconf_add_linklocal also adds a prefix_route and we
2740 * only need to care about prefix routes if ipv6_generate_eui64
2741 * couldn't generate one.
2742 */
2743 if (ipv6_generate_eui64(addr.s6_addr + 8, idev->dev) == 0)
2744 addrconf_add_linklocal(idev, &addr);
2745 else if (prefix_route)
2746 addrconf_prefix_route(&addr, 64, idev->dev, 0, 0);
2747 }
2748}
2749
2733static void addrconf_dev_config(struct net_device *dev) 2750static void addrconf_dev_config(struct net_device *dev)
2734{ 2751{
2735 struct in6_addr addr;
2736 struct inet6_dev *idev; 2752 struct inet6_dev *idev;
2737 2753
2738 ASSERT_RTNL(); 2754 ASSERT_RTNL();
@@ -2753,11 +2769,7 @@ static void addrconf_dev_config(struct net_device *dev)
2753 if (IS_ERR(idev)) 2769 if (IS_ERR(idev))
2754 return; 2770 return;
2755 2771
2756 memset(&addr, 0, sizeof(struct in6_addr)); 2772 addrconf_addr_gen(idev, false);
2757 addr.s6_addr32[0] = htonl(0xFE800000);
2758
2759 if (ipv6_generate_eui64(addr.s6_addr + 8, dev) == 0)
2760 addrconf_add_linklocal(idev, &addr);
2761} 2773}
2762 2774
2763#if IS_ENABLED(CONFIG_IPV6_SIT) 2775#if IS_ENABLED(CONFIG_IPV6_SIT)
@@ -2779,11 +2791,7 @@ static void addrconf_sit_config(struct net_device *dev)
2779 } 2791 }
2780 2792
2781 if (dev->priv_flags & IFF_ISATAP) { 2793 if (dev->priv_flags & IFF_ISATAP) {
2782 struct in6_addr addr; 2794 addrconf_addr_gen(idev, false);
2783
2784 ipv6_addr_set(&addr, htonl(0xFE800000), 0, 0, 0);
2785 if (!ipv6_generate_eui64(addr.s6_addr + 8, dev))
2786 addrconf_add_linklocal(idev, &addr);
2787 return; 2795 return;
2788 } 2796 }
2789 2797
@@ -2798,7 +2806,6 @@ static void addrconf_sit_config(struct net_device *dev)
2798static void addrconf_gre_config(struct net_device *dev) 2806static void addrconf_gre_config(struct net_device *dev)
2799{ 2807{
2800 struct inet6_dev *idev; 2808 struct inet6_dev *idev;
2801 struct in6_addr addr;
2802 2809
2803 ASSERT_RTNL(); 2810 ASSERT_RTNL();
2804 2811
@@ -2807,11 +2814,7 @@ static void addrconf_gre_config(struct net_device *dev)
2807 return; 2814 return;
2808 } 2815 }
2809 2816
2810 ipv6_addr_set(&addr, htonl(0xFE800000), 0, 0, 0); 2817 addrconf_addr_gen(idev, true);
2811 if (!ipv6_generate_eui64(addr.s6_addr + 8, dev))
2812 addrconf_add_linklocal(idev, &addr);
2813 else
2814 addrconf_prefix_route(&addr, 64, dev, 0, 0);
2815} 2818}
2816#endif 2819#endif
2817 2820
@@ -4423,6 +4426,10 @@ static int inet6_fill_ifla6_attrs(struct sk_buff *skb, struct inet6_dev *idev)
4423 nla = nla_reserve(skb, IFLA_INET6_TOKEN, sizeof(struct in6_addr)); 4426 nla = nla_reserve(skb, IFLA_INET6_TOKEN, sizeof(struct in6_addr));
4424 if (nla == NULL) 4427 if (nla == NULL)
4425 goto nla_put_failure; 4428 goto nla_put_failure;
4429
4430 if (nla_put_u8(skb, IFLA_INET6_ADDR_GEN_MODE, idev->addr_gen_mode))
4431 goto nla_put_failure;
4432
4426 read_lock_bh(&idev->lock); 4433 read_lock_bh(&idev->lock);
4427 memcpy(nla_data(nla), idev->token.s6_addr, nla_len(nla)); 4434 memcpy(nla_data(nla), idev->token.s6_addr, nla_len(nla));
4428 read_unlock_bh(&idev->lock); 4435 read_unlock_bh(&idev->lock);
@@ -4527,8 +4534,21 @@ static int inet6_set_link_af(struct net_device *dev, const struct nlattr *nla)
4527 if (nla_parse_nested(tb, IFLA_INET6_MAX, nla, NULL) < 0) 4534 if (nla_parse_nested(tb, IFLA_INET6_MAX, nla, NULL) < 0)
4528 BUG(); 4535 BUG();
4529 4536
4530 if (tb[IFLA_INET6_TOKEN]) 4537 if (tb[IFLA_INET6_TOKEN]) {
4531 err = inet6_set_iftoken(idev, nla_data(tb[IFLA_INET6_TOKEN])); 4538 err = inet6_set_iftoken(idev, nla_data(tb[IFLA_INET6_TOKEN]));
4539 if (err)
4540 return err;
4541 }
4542
4543 if (tb[IFLA_INET6_ADDR_GEN_MODE]) {
4544 u8 mode = nla_get_u8(tb[IFLA_INET6_ADDR_GEN_MODE]);
4545
4546 if (mode != IN6_ADDR_GEN_MODE_EUI64 &&
4547 mode != IN6_ADDR_GEN_MODE_NONE)
4548 return -EINVAL;
4549 idev->addr_gen_mode = mode;
4550 err = 0;
4551 }
4532 4552
4533 return err; 4553 return err;
4534} 4554}