diff options
| -rw-r--r-- | net/ipv6/ndisc.c | 171 |
1 files changed, 60 insertions, 111 deletions
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index d0b59590b252..dd2cb637da34 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -707,8 +707,10 @@ static void ndisc_recv_ns(struct sk_buff *skb) struct ndisc_options ndopts; struct net_device *dev = skb->dev; struct inet6_ifaddr *ifp; + struct inet6_dev *idev = NULL; struct neighbour *neigh; int addr_type = ipv6_addr_type(saddr); + int inc; if (ipv6_addr_is_multicast(&msg->target)) { if (net_ratelimit()) @@ -757,6 +759,8 @@ static void ndisc_recv_ns(struct sk_buff *skb) } } + inc = ipv6_addr_is_multicast(daddr); + if ((ifp = ipv6_get_ifaddr(&msg->target, dev, 1)) != NULL) { if (ifp->flags & IFA_F_TENTATIVE) { /* Address is tentative. If the source @@ -784,127 +788,72 @@ static void ndisc_recv_ns(struct sk_buff *skb) addrconf_dad_failure(ifp); return; } - - if (addr_type == IPV6_ADDR_ANY) { - struct in6_addr maddr; - - ipv6_addr_all_nodes(&maddr); - ndisc_send_na(dev, NULL, &maddr, &ifp->addr, - ifp->idev->cnf.forwarding, 0, - 1, 1); - in6_ifa_put(ifp); - return; - } - - if (addr_type & IPV6_ADDR_UNICAST) { - if (ipv6_addr_is_multicast(daddr)) - nd_tbl.stats.rcv_probes_mcast++; - else - nd_tbl.stats.rcv_probes_ucast++; - /* - * update / create cache entry - * for the source address - */ - - neigh = neigh_event_ns(&nd_tbl, lladdr, saddr, dev); - - if (neigh || !dev->hard_header) { - ndisc_send_na(dev, neigh, saddr, &ifp->addr, - ifp->idev->cnf.forwarding, 1, - 1, 1); - if (neigh) - neigh_release(neigh); - } - } - in6_ifa_put(ifp); - } else if (ipv6_chk_acast_addr(dev, &msg->target)) { - struct inet6_dev *idev = in6_dev_get(dev); - - /* anycast */ - + idev = ifp->idev; + } else { + idev = in6_dev_get(dev); if (!idev) { /* XXX: count this drop? */ return; } - - if (addr_type == IPV6_ADDR_ANY) { - struct in6_addr maddr; - - ipv6_addr_all_nodes(&maddr); - ndisc_send_na(dev, NULL, &maddr, &msg->target, - idev->cnf.forwarding, 0, 0, 1); - in6_dev_put(idev); - return; - } - - if (addr_type & IPV6_ADDR_UNICAST) { - int inc = ipv6_addr_is_multicast(daddr); - if (inc) - nd_tbl.stats.rcv_probes_mcast++; - else - nd_tbl.stats.rcv_probes_ucast++; - - /* - * update / create cache entry - * for the source address - */ - neigh = neigh_event_ns(&nd_tbl, lladdr, saddr, skb->dev); - - if (neigh || !dev->hard_header) { - ndisc_send_na(dev, neigh, saddr, - &msg->target, - idev->cnf.forwarding, 1, 0, inc); - if (neigh) - neigh_release(neigh); - } - } - in6_dev_put(idev); - } else { - struct inet6_dev *in6_dev = in6_dev_get(dev); - - if (in6_dev && in6_dev->cnf.forwarding && - (addr_type & IPV6_ADDR_UNICAST || - addr_type == IPV6_ADDR_ANY) && - pneigh_lookup(&nd_tbl, &msg->target, dev, 0)) { - int inc = ipv6_addr_is_multicast(daddr); - - if (skb->stamp.tv_sec == 0 || - skb->pkt_type == PACKET_HOST || - inc == 0 || - in6_dev->nd_parms->proxy_delay == 0) { - if (inc) - nd_tbl.stats.rcv_probes_mcast++; - else - nd_tbl.stats.rcv_probes_ucast++; - - if (addr_type & IPV6_ADDR_UNICAST) { - neigh = neigh_event_ns(&nd_tbl, lladdr, saddr, dev); - - if (neigh) { - ndisc_send_na(dev, neigh, saddr, &msg->target, - 0, 1, 0, 1); - neigh_release(neigh); - } - } else { - /* proxy should also protect against DAD */ - struct in6_addr maddr; - ipv6_addr_all_nodes(&maddr); - ndisc_send_na(dev, NULL, &maddr, &msg->target, - 0, 0, 0, 1); - } - } else { + if (ipv6_chk_acast_addr(dev, &msg->target) || + (idev->cnf.forwarding && + pneigh_lookup(&nd_tbl, &msg->target, dev, 0))) { + if (skb->stamp.tv_sec != 0 && + skb->pkt_type != PACKET_HOST && + inc != 0 && + idev->nd_parms->proxy_delay != 0) { + /* + * for anycast or proxy, + * sender should delay its response + * by a random time between 0 and + * MAX_ANYCAST_DELAY_TIME seconds. + * (RFC2461) -- yoshfuji + */ struct sk_buff *n = skb_clone(skb, GFP_ATOMIC); if (n) - pneigh_enqueue(&nd_tbl, in6_dev->nd_parms, n); - in6_dev_put(in6_dev); - return; + pneigh_enqueue(&nd_tbl, idev->nd_parms, n); + goto out; } - } - if (in6_dev) - in6_dev_put(in6_dev); + } else + goto out; } + + if (addr_type == IPV6_ADDR_ANY) { + struct in6_addr maddr; + + ipv6_addr_all_nodes(&maddr); + ndisc_send_na(dev, NULL, &maddr, &msg->target, + idev->cnf.forwarding, 0, (ifp != NULL), 1); + goto out; + } + + if (inc) + nd_tbl.stats.rcv_probes_mcast++; + else + nd_tbl.stats.rcv_probes_ucast++; + + /* + * update / create cache entry + * for the source address + */ + neigh = neigh_event_ns(&nd_tbl, lladdr, saddr, dev); + + if (neigh || !dev->hard_header) { + ndisc_send_na(dev, neigh, saddr, &msg->target, + idev->cnf.forwarding, + 1, (ifp != NULL && inc), inc); + if (neigh) + neigh_release(neigh); + } + +out: + if (ifp) + in6_ifa_put(ifp); + else + in6_dev_put(idev); + return; } |
