diff options
Diffstat (limited to 'net/ipv6/addrconf.c')
-rw-r--r-- | net/ipv6/addrconf.c | 83 |
1 files changed, 74 insertions, 9 deletions
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 147588f4c7c..2ec73e62202 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c | |||
@@ -6,8 +6,6 @@ | |||
6 | * Pedro Roque <roque@di.fc.ul.pt> | 6 | * Pedro Roque <roque@di.fc.ul.pt> |
7 | * Alexey Kuznetsov <kuznet@ms2.inr.ac.ru> | 7 | * Alexey Kuznetsov <kuznet@ms2.inr.ac.ru> |
8 | * | 8 | * |
9 | * $Id: addrconf.c,v 1.69 2001/10/31 21:55:54 davem Exp $ | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or | 9 | * This program is free software; you can redistribute it and/or |
12 | * modify it under the terms of the GNU General Public License | 10 | * modify it under the terms of the GNU General Public License |
13 | * as published by the Free Software Foundation; either version | 11 | * as published by the Free Software Foundation; either version |
@@ -121,6 +119,7 @@ static void ipv6_regen_rndid(unsigned long data); | |||
121 | static int desync_factor = MAX_DESYNC_FACTOR * HZ; | 119 | static int desync_factor = MAX_DESYNC_FACTOR * HZ; |
122 | #endif | 120 | #endif |
123 | 121 | ||
122 | static int ipv6_generate_eui64(u8 *eui, struct net_device *dev); | ||
124 | static int ipv6_count_addresses(struct inet6_dev *idev); | 123 | static int ipv6_count_addresses(struct inet6_dev *idev); |
125 | 124 | ||
126 | /* | 125 | /* |
@@ -185,6 +184,8 @@ struct ipv6_devconf ipv6_devconf __read_mostly = { | |||
185 | #endif | 184 | #endif |
186 | .proxy_ndp = 0, | 185 | .proxy_ndp = 0, |
187 | .accept_source_route = 0, /* we do not accept RH0 by default. */ | 186 | .accept_source_route = 0, /* we do not accept RH0 by default. */ |
187 | .disable_ipv6 = 0, | ||
188 | .accept_dad = 1, | ||
188 | }; | 189 | }; |
189 | 190 | ||
190 | static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { | 191 | static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { |
@@ -217,6 +218,8 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { | |||
217 | #endif | 218 | #endif |
218 | .proxy_ndp = 0, | 219 | .proxy_ndp = 0, |
219 | .accept_source_route = 0, /* we do not accept RH0 by default. */ | 220 | .accept_source_route = 0, /* we do not accept RH0 by default. */ |
221 | .disable_ipv6 = 0, | ||
222 | .accept_dad = 1, | ||
220 | }; | 223 | }; |
221 | 224 | ||
222 | /* IPv6 Wildcard Address and Loopback Address defined by RFC2553 */ | 225 | /* IPv6 Wildcard Address and Loopback Address defined by RFC2553 */ |
@@ -231,6 +234,12 @@ static inline int addrconf_qdisc_ok(struct net_device *dev) | |||
231 | return (dev->qdisc != &noop_qdisc); | 234 | return (dev->qdisc != &noop_qdisc); |
232 | } | 235 | } |
233 | 236 | ||
237 | /* Check if a route is valid prefix route */ | ||
238 | static inline int addrconf_is_prefix_route(const struct rt6_info *rt) | ||
239 | { | ||
240 | return ((rt->rt6i_flags & (RTF_GATEWAY | RTF_DEFAULT)) == 0); | ||
241 | } | ||
242 | |||
234 | static void addrconf_del_timer(struct inet6_ifaddr *ifp) | 243 | static void addrconf_del_timer(struct inet6_ifaddr *ifp) |
235 | { | 244 | { |
236 | if (del_timer(&ifp->timer)) | 245 | if (del_timer(&ifp->timer)) |
@@ -344,6 +353,8 @@ static struct inet6_dev * ipv6_add_dev(struct net_device *dev) | |||
344 | kfree(ndev); | 353 | kfree(ndev); |
345 | return NULL; | 354 | return NULL; |
346 | } | 355 | } |
356 | if (ndev->cnf.forwarding) | ||
357 | dev_disable_lro(dev); | ||
347 | /* We refer to the device */ | 358 | /* We refer to the device */ |
348 | dev_hold(dev); | 359 | dev_hold(dev); |
349 | 360 | ||
@@ -372,6 +383,9 @@ static struct inet6_dev * ipv6_add_dev(struct net_device *dev) | |||
372 | */ | 383 | */ |
373 | in6_dev_hold(ndev); | 384 | in6_dev_hold(ndev); |
374 | 385 | ||
386 | if (dev->flags & (IFF_NOARP | IFF_LOOPBACK)) | ||
387 | ndev->cnf.accept_dad = -1; | ||
388 | |||
375 | #if defined(CONFIG_IPV6_SIT) || defined(CONFIG_IPV6_SIT_MODULE) | 389 | #if defined(CONFIG_IPV6_SIT) || defined(CONFIG_IPV6_SIT_MODULE) |
376 | if (dev->type == ARPHRD_SIT && (dev->priv_flags & IFF_ISATAP)) { | 390 | if (dev->type == ARPHRD_SIT && (dev->priv_flags & IFF_ISATAP)) { |
377 | printk(KERN_INFO | 391 | printk(KERN_INFO |
@@ -438,6 +452,8 @@ static void dev_forward_change(struct inet6_dev *idev) | |||
438 | if (!idev) | 452 | if (!idev) |
439 | return; | 453 | return; |
440 | dev = idev->dev; | 454 | dev = idev->dev; |
455 | if (idev->cnf.forwarding) | ||
456 | dev_disable_lro(dev); | ||
441 | if (dev && (dev->flags & IFF_MULTICAST)) { | 457 | if (dev && (dev->flags & IFF_MULTICAST)) { |
442 | if (idev->cnf.forwarding) | 458 | if (idev->cnf.forwarding) |
443 | ipv6_dev_mc_inc(dev, &in6addr_linklocal_allrouters); | 459 | ipv6_dev_mc_inc(dev, &in6addr_linklocal_allrouters); |
@@ -483,12 +499,14 @@ static void addrconf_fixup_forwarding(struct ctl_table *table, int *p, int old) | |||
483 | if (p == &net->ipv6.devconf_dflt->forwarding) | 499 | if (p == &net->ipv6.devconf_dflt->forwarding) |
484 | return; | 500 | return; |
485 | 501 | ||
502 | rtnl_lock(); | ||
486 | if (p == &net->ipv6.devconf_all->forwarding) { | 503 | if (p == &net->ipv6.devconf_all->forwarding) { |
487 | __s32 newf = net->ipv6.devconf_all->forwarding; | 504 | __s32 newf = net->ipv6.devconf_all->forwarding; |
488 | net->ipv6.devconf_dflt->forwarding = newf; | 505 | net->ipv6.devconf_dflt->forwarding = newf; |
489 | addrconf_forward_change(net, newf); | 506 | addrconf_forward_change(net, newf); |
490 | } else if ((!*p) ^ (!old)) | 507 | } else if ((!*p) ^ (!old)) |
491 | dev_forward_change((struct inet6_dev *)table->extra1); | 508 | dev_forward_change((struct inet6_dev *)table->extra1); |
509 | rtnl_unlock(); | ||
492 | 510 | ||
493 | if (*p) | 511 | if (*p) |
494 | rt6_purge_dflt_routers(net); | 512 | rt6_purge_dflt_routers(net); |
@@ -568,6 +586,13 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen, | |||
568 | struct rt6_info *rt; | 586 | struct rt6_info *rt; |
569 | int hash; | 587 | int hash; |
570 | int err = 0; | 588 | int err = 0; |
589 | int addr_type = ipv6_addr_type(addr); | ||
590 | |||
591 | if (addr_type == IPV6_ADDR_ANY || | ||
592 | addr_type & IPV6_ADDR_MULTICAST || | ||
593 | (!(idev->dev->flags & IFF_LOOPBACK) && | ||
594 | addr_type & IPV6_ADDR_LOOPBACK)) | ||
595 | return ERR_PTR(-EADDRNOTAVAIL); | ||
571 | 596 | ||
572 | rcu_read_lock_bh(); | 597 | rcu_read_lock_bh(); |
573 | if (idev->dead) { | 598 | if (idev->dead) { |
@@ -777,7 +802,7 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp) | |||
777 | ipv6_addr_prefix(&prefix, &ifp->addr, ifp->prefix_len); | 802 | ipv6_addr_prefix(&prefix, &ifp->addr, ifp->prefix_len); |
778 | rt = rt6_lookup(net, &prefix, NULL, ifp->idev->dev->ifindex, 1); | 803 | rt = rt6_lookup(net, &prefix, NULL, ifp->idev->dev->ifindex, 1); |
779 | 804 | ||
780 | if (rt && ((rt->rt6i_flags & (RTF_GATEWAY | RTF_DEFAULT)) == 0)) { | 805 | if (rt && addrconf_is_prefix_route(rt)) { |
781 | if (onlink == 0) { | 806 | if (onlink == 0) { |
782 | ip6_del_rt(rt); | 807 | ip6_del_rt(rt); |
783 | rt = NULL; | 808 | rt = NULL; |
@@ -958,7 +983,8 @@ static inline int ipv6_saddr_preferred(int type) | |||
958 | return 0; | 983 | return 0; |
959 | } | 984 | } |
960 | 985 | ||
961 | static int ipv6_get_saddr_eval(struct ipv6_saddr_score *score, | 986 | static int ipv6_get_saddr_eval(struct net *net, |
987 | struct ipv6_saddr_score *score, | ||
962 | struct ipv6_saddr_dst *dst, | 988 | struct ipv6_saddr_dst *dst, |
963 | int i) | 989 | int i) |
964 | { | 990 | { |
@@ -1037,7 +1063,8 @@ static int ipv6_get_saddr_eval(struct ipv6_saddr_score *score, | |||
1037 | break; | 1063 | break; |
1038 | case IPV6_SADDR_RULE_LABEL: | 1064 | case IPV6_SADDR_RULE_LABEL: |
1039 | /* Rule 6: Prefer matching label */ | 1065 | /* Rule 6: Prefer matching label */ |
1040 | ret = ipv6_addr_label(&score->ifa->addr, score->addr_type, | 1066 | ret = ipv6_addr_label(net, |
1067 | &score->ifa->addr, score->addr_type, | ||
1041 | score->ifa->idev->dev->ifindex) == dst->label; | 1068 | score->ifa->idev->dev->ifindex) == dst->label; |
1042 | break; | 1069 | break; |
1043 | #ifdef CONFIG_IPV6_PRIVACY | 1070 | #ifdef CONFIG_IPV6_PRIVACY |
@@ -1091,7 +1118,7 @@ int ipv6_dev_get_saddr(struct net_device *dst_dev, | |||
1091 | dst.addr = daddr; | 1118 | dst.addr = daddr; |
1092 | dst.ifindex = dst_dev ? dst_dev->ifindex : 0; | 1119 | dst.ifindex = dst_dev ? dst_dev->ifindex : 0; |
1093 | dst.scope = __ipv6_addr_src_scope(dst_type); | 1120 | dst.scope = __ipv6_addr_src_scope(dst_type); |
1094 | dst.label = ipv6_addr_label(daddr, dst_type, dst.ifindex); | 1121 | dst.label = ipv6_addr_label(net, daddr, dst_type, dst.ifindex); |
1095 | dst.prefs = prefs; | 1122 | dst.prefs = prefs; |
1096 | 1123 | ||
1097 | hiscore->rule = -1; | 1124 | hiscore->rule = -1; |
@@ -1159,8 +1186,8 @@ int ipv6_dev_get_saddr(struct net_device *dst_dev, | |||
1159 | for (i = 0; i < IPV6_SADDR_RULE_MAX; i++) { | 1186 | for (i = 0; i < IPV6_SADDR_RULE_MAX; i++) { |
1160 | int minihiscore, miniscore; | 1187 | int minihiscore, miniscore; |
1161 | 1188 | ||
1162 | minihiscore = ipv6_get_saddr_eval(hiscore, &dst, i); | 1189 | minihiscore = ipv6_get_saddr_eval(net, hiscore, &dst, i); |
1163 | miniscore = ipv6_get_saddr_eval(score, &dst, i); | 1190 | miniscore = ipv6_get_saddr_eval(net, score, &dst, i); |
1164 | 1191 | ||
1165 | if (minihiscore > miniscore) { | 1192 | if (minihiscore > miniscore) { |
1166 | if (i == IPV6_SADDR_RULE_SCOPE && | 1193 | if (i == IPV6_SADDR_RULE_SCOPE && |
@@ -1400,6 +1427,20 @@ static void addrconf_dad_stop(struct inet6_ifaddr *ifp) | |||
1400 | 1427 | ||
1401 | void addrconf_dad_failure(struct inet6_ifaddr *ifp) | 1428 | void addrconf_dad_failure(struct inet6_ifaddr *ifp) |
1402 | { | 1429 | { |
1430 | struct inet6_dev *idev = ifp->idev; | ||
1431 | if (idev->cnf.accept_dad > 1 && !idev->cnf.disable_ipv6) { | ||
1432 | struct in6_addr addr; | ||
1433 | |||
1434 | addr.s6_addr32[0] = htonl(0xfe800000); | ||
1435 | addr.s6_addr32[1] = 0; | ||
1436 | |||
1437 | if (!ipv6_generate_eui64(addr.s6_addr + 8, idev->dev) && | ||
1438 | ipv6_addr_equal(&ifp->addr, &addr)) { | ||
1439 | /* DAD failed for link-local based on MAC address */ | ||
1440 | idev->cnf.disable_ipv6 = 1; | ||
1441 | } | ||
1442 | } | ||
1443 | |||
1403 | if (net_ratelimit()) | 1444 | if (net_ratelimit()) |
1404 | printk(KERN_INFO "%s: duplicate address detected!\n", ifp->idev->dev->name); | 1445 | printk(KERN_INFO "%s: duplicate address detected!\n", ifp->idev->dev->name); |
1405 | addrconf_dad_stop(ifp); | 1446 | addrconf_dad_stop(ifp); |
@@ -1788,7 +1829,7 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len) | |||
1788 | rt = rt6_lookup(dev_net(dev), &pinfo->prefix, NULL, | 1829 | rt = rt6_lookup(dev_net(dev), &pinfo->prefix, NULL, |
1789 | dev->ifindex, 1); | 1830 | dev->ifindex, 1); |
1790 | 1831 | ||
1791 | if (rt && ((rt->rt6i_flags & (RTF_GATEWAY | RTF_DEFAULT)) == 0)) { | 1832 | if (rt && addrconf_is_prefix_route(rt)) { |
1792 | /* Autoconf prefix route */ | 1833 | /* Autoconf prefix route */ |
1793 | if (valid_lft == 0) { | 1834 | if (valid_lft == 0) { |
1794 | ip6_del_rt(rt); | 1835 | ip6_del_rt(rt); |
@@ -2732,6 +2773,7 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags) | |||
2732 | spin_lock_bh(&ifp->lock); | 2773 | spin_lock_bh(&ifp->lock); |
2733 | 2774 | ||
2734 | if (dev->flags&(IFF_NOARP|IFF_LOOPBACK) || | 2775 | if (dev->flags&(IFF_NOARP|IFF_LOOPBACK) || |
2776 | idev->cnf.accept_dad < 1 || | ||
2735 | !(ifp->flags&IFA_F_TENTATIVE) || | 2777 | !(ifp->flags&IFA_F_TENTATIVE) || |
2736 | ifp->flags & IFA_F_NODAD) { | 2778 | ifp->flags & IFA_F_NODAD) { |
2737 | ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC); | 2779 | ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC); |
@@ -2779,6 +2821,11 @@ static void addrconf_dad_timer(unsigned long data) | |||
2779 | read_unlock_bh(&idev->lock); | 2821 | read_unlock_bh(&idev->lock); |
2780 | goto out; | 2822 | goto out; |
2781 | } | 2823 | } |
2824 | if (idev->cnf.accept_dad > 1 && idev->cnf.disable_ipv6) { | ||
2825 | read_unlock_bh(&idev->lock); | ||
2826 | addrconf_dad_failure(ifp); | ||
2827 | return; | ||
2828 | } | ||
2782 | spin_lock_bh(&ifp->lock); | 2829 | spin_lock_bh(&ifp->lock); |
2783 | if (ifp->probes == 0) { | 2830 | if (ifp->probes == 0) { |
2784 | /* | 2831 | /* |
@@ -3638,6 +3685,8 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf, | |||
3638 | #ifdef CONFIG_IPV6_MROUTE | 3685 | #ifdef CONFIG_IPV6_MROUTE |
3639 | array[DEVCONF_MC_FORWARDING] = cnf->mc_forwarding; | 3686 | array[DEVCONF_MC_FORWARDING] = cnf->mc_forwarding; |
3640 | #endif | 3687 | #endif |
3688 | array[DEVCONF_DISABLE_IPV6] = cnf->disable_ipv6; | ||
3689 | array[DEVCONF_ACCEPT_DAD] = cnf->accept_dad; | ||
3641 | } | 3690 | } |
3642 | 3691 | ||
3643 | static inline size_t inet6_if_nlmsg_size(void) | 3692 | static inline size_t inet6_if_nlmsg_size(void) |
@@ -4197,6 +4246,22 @@ static struct addrconf_sysctl_table | |||
4197 | }, | 4246 | }, |
4198 | #endif | 4247 | #endif |
4199 | { | 4248 | { |
4249 | .ctl_name = CTL_UNNUMBERED, | ||
4250 | .procname = "disable_ipv6", | ||
4251 | .data = &ipv6_devconf.disable_ipv6, | ||
4252 | .maxlen = sizeof(int), | ||
4253 | .mode = 0644, | ||
4254 | .proc_handler = &proc_dointvec, | ||
4255 | }, | ||
4256 | { | ||
4257 | .ctl_name = CTL_UNNUMBERED, | ||
4258 | .procname = "accept_dad", | ||
4259 | .data = &ipv6_devconf.accept_dad, | ||
4260 | .maxlen = sizeof(int), | ||
4261 | .mode = 0644, | ||
4262 | .proc_handler = &proc_dointvec, | ||
4263 | }, | ||
4264 | { | ||
4200 | .ctl_name = 0, /* sentinel */ | 4265 | .ctl_name = 0, /* sentinel */ |
4201 | } | 4266 | } |
4202 | }, | 4267 | }, |