aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/net/addrconf.h3
-rw-r--r--net/ipv6/addrconf.c19
-rw-r--r--net/ipv6/ndisc.c4
3 files changed, 23 insertions, 3 deletions
diff --git a/include/net/addrconf.h b/include/net/addrconf.h
index d13573bb879e..80456f72d70a 100644
--- a/include/net/addrconf.h
+++ b/include/net/addrconf.h
@@ -62,6 +62,9 @@ int addrconf_set_dstaddr(struct net *net, void __user *arg);
62 62
63int ipv6_chk_addr(struct net *net, const struct in6_addr *addr, 63int ipv6_chk_addr(struct net *net, const struct in6_addr *addr,
64 const struct net_device *dev, int strict); 64 const struct net_device *dev, int strict);
65int ipv6_chk_addr_and_flags(struct net *net, const struct in6_addr *addr,
66 const struct net_device *dev, int strict,
67 u32 banned_flags);
65 68
66#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) 69#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
67int ipv6_chk_home_addr(struct net *net, const struct in6_addr *addr); 70int ipv6_chk_home_addr(struct net *net, const struct in6_addr *addr);
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index f7c8bbeb27b7..62900aee4c58 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -1519,15 +1519,30 @@ static int ipv6_count_addresses(struct inet6_dev *idev)
1519int ipv6_chk_addr(struct net *net, const struct in6_addr *addr, 1519int ipv6_chk_addr(struct net *net, const struct in6_addr *addr,
1520 const struct net_device *dev, int strict) 1520 const struct net_device *dev, int strict)
1521{ 1521{
1522 return ipv6_chk_addr_and_flags(net, addr, dev, strict, IFA_F_TENTATIVE);
1523}
1524EXPORT_SYMBOL(ipv6_chk_addr);
1525
1526int ipv6_chk_addr_and_flags(struct net *net, const struct in6_addr *addr,
1527 const struct net_device *dev, int strict,
1528 u32 banned_flags)
1529{
1522 struct inet6_ifaddr *ifp; 1530 struct inet6_ifaddr *ifp;
1523 unsigned int hash = inet6_addr_hash(addr); 1531 unsigned int hash = inet6_addr_hash(addr);
1532 u32 ifp_flags;
1524 1533
1525 rcu_read_lock_bh(); 1534 rcu_read_lock_bh();
1526 hlist_for_each_entry_rcu(ifp, &inet6_addr_lst[hash], addr_lst) { 1535 hlist_for_each_entry_rcu(ifp, &inet6_addr_lst[hash], addr_lst) {
1527 if (!net_eq(dev_net(ifp->idev->dev), net)) 1536 if (!net_eq(dev_net(ifp->idev->dev), net))
1528 continue; 1537 continue;
1538 /* Decouple optimistic from tentative for evaluation here.
1539 * Ban optimistic addresses explicitly, when required.
1540 */
1541 ifp_flags = (ifp->flags&IFA_F_OPTIMISTIC)
1542 ? (ifp->flags&~IFA_F_TENTATIVE)
1543 : ifp->flags;
1529 if (ipv6_addr_equal(&ifp->addr, addr) && 1544 if (ipv6_addr_equal(&ifp->addr, addr) &&
1530 !(ifp->flags&IFA_F_TENTATIVE) && 1545 !(ifp_flags&banned_flags) &&
1531 (dev == NULL || ifp->idev->dev == dev || 1546 (dev == NULL || ifp->idev->dev == dev ||
1532 !(ifp->scope&(IFA_LINK|IFA_HOST) || strict))) { 1547 !(ifp->scope&(IFA_LINK|IFA_HOST) || strict))) {
1533 rcu_read_unlock_bh(); 1548 rcu_read_unlock_bh();
@@ -1538,7 +1553,7 @@ int ipv6_chk_addr(struct net *net, const struct in6_addr *addr,
1538 rcu_read_unlock_bh(); 1553 rcu_read_unlock_bh();
1539 return 0; 1554 return 0;
1540} 1555}
1541EXPORT_SYMBOL(ipv6_chk_addr); 1556EXPORT_SYMBOL(ipv6_chk_addr_and_flags);
1542 1557
1543static bool ipv6_chk_same_addr(struct net *net, const struct in6_addr *addr, 1558static bool ipv6_chk_same_addr(struct net *net, const struct in6_addr *addr,
1544 struct net_device *dev) 1559 struct net_device *dev)
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index 682866777d53..113fc6cd5a0c 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -655,7 +655,9 @@ static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb)
655 struct in6_addr *target = (struct in6_addr *)&neigh->primary_key; 655 struct in6_addr *target = (struct in6_addr *)&neigh->primary_key;
656 int probes = atomic_read(&neigh->probes); 656 int probes = atomic_read(&neigh->probes);
657 657
658 if (skb && ipv6_chk_addr(dev_net(dev), &ipv6_hdr(skb)->saddr, dev, 1)) 658 if (skb && ipv6_chk_addr_and_flags(dev_net(dev), &ipv6_hdr(skb)->saddr,
659 dev, 1,
660 IFA_F_TENTATIVE|IFA_F_OPTIMISTIC))
659 saddr = &ipv6_hdr(skb)->saddr; 661 saddr = &ipv6_hdr(skb)->saddr;
660 probes -= NEIGH_VAR(neigh->parms, UCAST_PROBES); 662 probes -= NEIGH_VAR(neigh->parms, UCAST_PROBES);
661 if (probes < 0) { 663 if (probes < 0) {