diff options
Diffstat (limited to 'net/ipv6/ndisc.c')
-rw-r--r-- | net/ipv6/ndisc.c | 140 |
1 files changed, 93 insertions, 47 deletions
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 58841c4ae947..7596f071d308 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c | |||
@@ -91,7 +91,9 @@ | |||
91 | #include <linux/netfilter.h> | 91 | #include <linux/netfilter.h> |
92 | #include <linux/netfilter_ipv6.h> | 92 | #include <linux/netfilter_ipv6.h> |
93 | 93 | ||
94 | static u32 ndisc_hash(const void *pkey, const struct net_device *dev); | 94 | static u32 ndisc_hash(const void *pkey, |
95 | const struct net_device *dev, | ||
96 | __u32 rnd); | ||
95 | static int ndisc_constructor(struct neighbour *neigh); | 97 | static int ndisc_constructor(struct neighbour *neigh); |
96 | static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb); | 98 | static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb); |
97 | static void ndisc_error_report(struct neighbour *neigh, struct sk_buff *skb); | 99 | static void ndisc_error_report(struct neighbour *neigh, struct sk_buff *skb); |
@@ -139,18 +141,18 @@ struct neigh_table nd_tbl = { | |||
139 | .proxy_redo = pndisc_redo, | 141 | .proxy_redo = pndisc_redo, |
140 | .id = "ndisc_cache", | 142 | .id = "ndisc_cache", |
141 | .parms = { | 143 | .parms = { |
142 | .tbl = &nd_tbl, | 144 | .tbl = &nd_tbl, |
143 | .base_reachable_time = 30 * HZ, | 145 | .base_reachable_time = ND_REACHABLE_TIME, |
144 | .retrans_time = 1 * HZ, | 146 | .retrans_time = ND_RETRANS_TIMER, |
145 | .gc_staletime = 60 * HZ, | 147 | .gc_staletime = 60 * HZ, |
146 | .reachable_time = 30 * HZ, | 148 | .reachable_time = ND_REACHABLE_TIME, |
147 | .delay_probe_time = 5 * HZ, | 149 | .delay_probe_time = 5 * HZ, |
148 | .queue_len = 3, | 150 | .queue_len = 3, |
149 | .ucast_probes = 3, | 151 | .ucast_probes = 3, |
150 | .mcast_probes = 3, | 152 | .mcast_probes = 3, |
151 | .anycast_delay = 1 * HZ, | 153 | .anycast_delay = 1 * HZ, |
152 | .proxy_delay = (8 * HZ) / 10, | 154 | .proxy_delay = (8 * HZ) / 10, |
153 | .proxy_qlen = 64, | 155 | .proxy_qlen = 64, |
154 | }, | 156 | }, |
155 | .gc_interval = 30 * HZ, | 157 | .gc_interval = 30 * HZ, |
156 | .gc_thresh1 = 128, | 158 | .gc_thresh1 = 128, |
@@ -228,12 +230,12 @@ static struct nd_opt_hdr *ndisc_next_option(struct nd_opt_hdr *cur, | |||
228 | do { | 230 | do { |
229 | cur = ((void *)cur) + (cur->nd_opt_len << 3); | 231 | cur = ((void *)cur) + (cur->nd_opt_len << 3); |
230 | } while(cur < end && cur->nd_opt_type != type); | 232 | } while(cur < end && cur->nd_opt_type != type); |
231 | return (cur <= end && cur->nd_opt_type == type ? cur : NULL); | 233 | return cur <= end && cur->nd_opt_type == type ? cur : NULL; |
232 | } | 234 | } |
233 | 235 | ||
234 | static inline int ndisc_is_useropt(struct nd_opt_hdr *opt) | 236 | static inline int ndisc_is_useropt(struct nd_opt_hdr *opt) |
235 | { | 237 | { |
236 | return (opt->nd_opt_type == ND_OPT_RDNSS); | 238 | return opt->nd_opt_type == ND_OPT_RDNSS; |
237 | } | 239 | } |
238 | 240 | ||
239 | static struct nd_opt_hdr *ndisc_next_useropt(struct nd_opt_hdr *cur, | 241 | static struct nd_opt_hdr *ndisc_next_useropt(struct nd_opt_hdr *cur, |
@@ -244,7 +246,7 @@ static struct nd_opt_hdr *ndisc_next_useropt(struct nd_opt_hdr *cur, | |||
244 | do { | 246 | do { |
245 | cur = ((void *)cur) + (cur->nd_opt_len << 3); | 247 | cur = ((void *)cur) + (cur->nd_opt_len << 3); |
246 | } while(cur < end && !ndisc_is_useropt(cur)); | 248 | } while(cur < end && !ndisc_is_useropt(cur)); |
247 | return (cur <= end && ndisc_is_useropt(cur) ? cur : NULL); | 249 | return cur <= end && ndisc_is_useropt(cur) ? cur : NULL; |
248 | } | 250 | } |
249 | 251 | ||
250 | static struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len, | 252 | static struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len, |
@@ -319,10 +321,10 @@ static inline u8 *ndisc_opt_addr_data(struct nd_opt_hdr *p, | |||
319 | int prepad = ndisc_addr_option_pad(dev->type); | 321 | int prepad = ndisc_addr_option_pad(dev->type); |
320 | if (lladdrlen != NDISC_OPT_SPACE(dev->addr_len + prepad)) | 322 | if (lladdrlen != NDISC_OPT_SPACE(dev->addr_len + prepad)) |
321 | return NULL; | 323 | return NULL; |
322 | return (lladdr + prepad); | 324 | return lladdr + prepad; |
323 | } | 325 | } |
324 | 326 | ||
325 | int ndisc_mc_map(struct in6_addr *addr, char *buf, struct net_device *dev, int dir) | 327 | int ndisc_mc_map(const struct in6_addr *addr, char *buf, struct net_device *dev, int dir) |
326 | { | 328 | { |
327 | switch (dev->type) { | 329 | switch (dev->type) { |
328 | case ARPHRD_ETHER: | 330 | case ARPHRD_ETHER: |
@@ -339,6 +341,8 @@ int ndisc_mc_map(struct in6_addr *addr, char *buf, struct net_device *dev, int d | |||
339 | case ARPHRD_INFINIBAND: | 341 | case ARPHRD_INFINIBAND: |
340 | ipv6_ib_mc_map(addr, dev->broadcast, buf); | 342 | ipv6_ib_mc_map(addr, dev->broadcast, buf); |
341 | return 0; | 343 | return 0; |
344 | case ARPHRD_IPGRE: | ||
345 | return ipv6_ipgre_mc_map(addr, dev->broadcast, buf); | ||
342 | default: | 346 | default: |
343 | if (dir) { | 347 | if (dir) { |
344 | memcpy(buf, dev->broadcast, dev->addr_len); | 348 | memcpy(buf, dev->broadcast, dev->addr_len); |
@@ -350,7 +354,9 @@ int ndisc_mc_map(struct in6_addr *addr, char *buf, struct net_device *dev, int d | |||
350 | 354 | ||
351 | EXPORT_SYMBOL(ndisc_mc_map); | 355 | EXPORT_SYMBOL(ndisc_mc_map); |
352 | 356 | ||
353 | static u32 ndisc_hash(const void *pkey, const struct net_device *dev) | 357 | static u32 ndisc_hash(const void *pkey, |
358 | const struct net_device *dev, | ||
359 | __u32 hash_rnd) | ||
354 | { | 360 | { |
355 | const u32 *p32 = pkey; | 361 | const u32 *p32 = pkey; |
356 | u32 addr_hash, i; | 362 | u32 addr_hash, i; |
@@ -359,7 +365,7 @@ static u32 ndisc_hash(const void *pkey, const struct net_device *dev) | |||
359 | for (i = 0; i < (sizeof(struct in6_addr) / sizeof(u32)); i++) | 365 | for (i = 0; i < (sizeof(struct in6_addr) / sizeof(u32)); i++) |
360 | addr_hash ^= *p32++; | 366 | addr_hash ^= *p32++; |
361 | 367 | ||
362 | return jhash_2words(addr_hash, dev->ifindex, nd_tbl.hash_rnd); | 368 | return jhash_2words(addr_hash, dev->ifindex, hash_rnd); |
363 | } | 369 | } |
364 | 370 | ||
365 | static int ndisc_constructor(struct neighbour *neigh) | 371 | static int ndisc_constructor(struct neighbour *neigh) |
@@ -507,7 +513,7 @@ void ndisc_send_skb(struct sk_buff *skb, | |||
507 | const struct in6_addr *saddr, | 513 | const struct in6_addr *saddr, |
508 | struct icmp6hdr *icmp6h) | 514 | struct icmp6hdr *icmp6h) |
509 | { | 515 | { |
510 | struct flowi fl; | 516 | struct flowi6 fl6; |
511 | struct dst_entry *dst; | 517 | struct dst_entry *dst; |
512 | struct net *net = dev_net(dev); | 518 | struct net *net = dev_net(dev); |
513 | struct sock *sk = net->ipv6.ndisc_sk; | 519 | struct sock *sk = net->ipv6.ndisc_sk; |
@@ -517,7 +523,7 @@ void ndisc_send_skb(struct sk_buff *skb, | |||
517 | 523 | ||
518 | type = icmp6h->icmp6_type; | 524 | type = icmp6h->icmp6_type; |
519 | 525 | ||
520 | icmpv6_flow_init(sk, &fl, type, saddr, daddr, dev->ifindex); | 526 | icmpv6_flow_init(sk, &fl6, type, saddr, daddr, dev->ifindex); |
521 | 527 | ||
522 | dst = icmp6_dst_alloc(dev, neigh, daddr); | 528 | dst = icmp6_dst_alloc(dev, neigh, daddr); |
523 | if (!dst) { | 529 | if (!dst) { |
@@ -525,8 +531,8 @@ void ndisc_send_skb(struct sk_buff *skb, | |||
525 | return; | 531 | return; |
526 | } | 532 | } |
527 | 533 | ||
528 | err = xfrm_lookup(net, &dst, &fl, NULL, 0); | 534 | dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0); |
529 | if (err < 0) { | 535 | if (IS_ERR(dst)) { |
530 | kfree_skb(skb); | 536 | kfree_skb(skb); |
531 | return; | 537 | return; |
532 | } | 538 | } |
@@ -605,6 +611,29 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh, | |||
605 | inc_opt ? ND_OPT_TARGET_LL_ADDR : 0); | 611 | inc_opt ? ND_OPT_TARGET_LL_ADDR : 0); |
606 | } | 612 | } |
607 | 613 | ||
614 | static void ndisc_send_unsol_na(struct net_device *dev) | ||
615 | { | ||
616 | struct inet6_dev *idev; | ||
617 | struct inet6_ifaddr *ifa; | ||
618 | struct in6_addr mcaddr; | ||
619 | |||
620 | idev = in6_dev_get(dev); | ||
621 | if (!idev) | ||
622 | return; | ||
623 | |||
624 | read_lock_bh(&idev->lock); | ||
625 | list_for_each_entry(ifa, &idev->addr_list, if_list) { | ||
626 | addrconf_addr_solict_mult(&ifa->addr, &mcaddr); | ||
627 | ndisc_send_na(dev, NULL, &mcaddr, &ifa->addr, | ||
628 | /*router=*/ !!idev->cnf.forwarding, | ||
629 | /*solicited=*/ false, /*override=*/ true, | ||
630 | /*inc_opt=*/ true); | ||
631 | } | ||
632 | read_unlock_bh(&idev->lock); | ||
633 | |||
634 | in6_dev_put(idev); | ||
635 | } | ||
636 | |||
608 | void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh, | 637 | void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh, |
609 | const struct in6_addr *solicit, | 638 | const struct in6_addr *solicit, |
610 | const struct in6_addr *daddr, const struct in6_addr *saddr) | 639 | const struct in6_addr *daddr, const struct in6_addr *saddr) |
@@ -719,8 +748,8 @@ static int pndisc_is_router(const void *pkey, | |||
719 | static void ndisc_recv_ns(struct sk_buff *skb) | 748 | static void ndisc_recv_ns(struct sk_buff *skb) |
720 | { | 749 | { |
721 | struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb); | 750 | struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb); |
722 | struct in6_addr *saddr = &ipv6_hdr(skb)->saddr; | 751 | const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr; |
723 | struct in6_addr *daddr = &ipv6_hdr(skb)->daddr; | 752 | const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr; |
724 | u8 *lladdr = NULL; | 753 | u8 *lladdr = NULL; |
725 | u32 ndoptlen = skb->tail - (skb->transport_header + | 754 | u32 ndoptlen = skb->tail - (skb->transport_header + |
726 | offsetof(struct nd_msg, opt)); | 755 | offsetof(struct nd_msg, opt)); |
@@ -895,8 +924,8 @@ out: | |||
895 | static void ndisc_recv_na(struct sk_buff *skb) | 924 | static void ndisc_recv_na(struct sk_buff *skb) |
896 | { | 925 | { |
897 | struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb); | 926 | struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb); |
898 | struct in6_addr *saddr = &ipv6_hdr(skb)->saddr; | 927 | const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr; |
899 | struct in6_addr *daddr = &ipv6_hdr(skb)->daddr; | 928 | const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr; |
900 | u8 *lladdr = NULL; | 929 | u8 *lladdr = NULL; |
901 | u32 ndoptlen = skb->tail - (skb->transport_header + | 930 | u32 ndoptlen = skb->tail - (skb->transport_header + |
902 | offsetof(struct nd_msg, opt)); | 931 | offsetof(struct nd_msg, opt)); |
@@ -939,9 +968,10 @@ static void ndisc_recv_na(struct sk_buff *skb) | |||
939 | } | 968 | } |
940 | ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1); | 969 | ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1); |
941 | if (ifp) { | 970 | if (ifp) { |
942 | if (ifp->flags & IFA_F_TENTATIVE) { | 971 | if (skb->pkt_type != PACKET_LOOPBACK |
943 | addrconf_dad_failure(ifp); | 972 | && (ifp->flags & IFA_F_TENTATIVE)) { |
944 | return; | 973 | addrconf_dad_failure(ifp); |
974 | return; | ||
945 | } | 975 | } |
946 | /* What should we make now? The advertisement | 976 | /* What should we make now? The advertisement |
947 | is invalid, but ndisc specs say nothing | 977 | is invalid, but ndisc specs say nothing |
@@ -1008,7 +1038,7 @@ static void ndisc_recv_rs(struct sk_buff *skb) | |||
1008 | unsigned long ndoptlen = skb->len - sizeof(*rs_msg); | 1038 | unsigned long ndoptlen = skb->len - sizeof(*rs_msg); |
1009 | struct neighbour *neigh; | 1039 | struct neighbour *neigh; |
1010 | struct inet6_dev *idev; | 1040 | struct inet6_dev *idev; |
1011 | struct in6_addr *saddr = &ipv6_hdr(skb)->saddr; | 1041 | const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr; |
1012 | struct ndisc_options ndopts; | 1042 | struct ndisc_options ndopts; |
1013 | u8 *lladdr = NULL; | 1043 | u8 *lladdr = NULL; |
1014 | 1044 | ||
@@ -1105,6 +1135,18 @@ errout: | |||
1105 | rtnl_set_sk_err(net, RTNLGRP_ND_USEROPT, err); | 1135 | rtnl_set_sk_err(net, RTNLGRP_ND_USEROPT, err); |
1106 | } | 1136 | } |
1107 | 1137 | ||
1138 | static inline int accept_ra(struct inet6_dev *in6_dev) | ||
1139 | { | ||
1140 | /* | ||
1141 | * If forwarding is enabled, RA are not accepted unless the special | ||
1142 | * hybrid mode (accept_ra=2) is enabled. | ||
1143 | */ | ||
1144 | if (in6_dev->cnf.forwarding && in6_dev->cnf.accept_ra < 2) | ||
1145 | return 0; | ||
1146 | |||
1147 | return in6_dev->cnf.accept_ra; | ||
1148 | } | ||
1149 | |||
1108 | static void ndisc_router_discovery(struct sk_buff *skb) | 1150 | static void ndisc_router_discovery(struct sk_buff *skb) |
1109 | { | 1151 | { |
1110 | struct ra_msg *ra_msg = (struct ra_msg *)skb_transport_header(skb); | 1152 | struct ra_msg *ra_msg = (struct ra_msg *)skb_transport_header(skb); |
@@ -1158,8 +1200,7 @@ static void ndisc_router_discovery(struct sk_buff *skb) | |||
1158 | return; | 1200 | return; |
1159 | } | 1201 | } |
1160 | 1202 | ||
1161 | /* skip route and link configuration on routers */ | 1203 | if (!accept_ra(in6_dev)) |
1162 | if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_ra) | ||
1163 | goto skip_linkparms; | 1204 | goto skip_linkparms; |
1164 | 1205 | ||
1165 | #ifdef CONFIG_IPV6_NDISC_NODETYPE | 1206 | #ifdef CONFIG_IPV6_NDISC_NODETYPE |
@@ -1244,7 +1285,8 @@ static void ndisc_router_discovery(struct sk_buff *skb) | |||
1244 | if (ra_msg->icmph.icmp6_hop_limit) { | 1285 | if (ra_msg->icmph.icmp6_hop_limit) { |
1245 | in6_dev->cnf.hop_limit = ra_msg->icmph.icmp6_hop_limit; | 1286 | in6_dev->cnf.hop_limit = ra_msg->icmph.icmp6_hop_limit; |
1246 | if (rt) | 1287 | if (rt) |
1247 | rt->dst.metrics[RTAX_HOPLIMIT-1] = ra_msg->icmph.icmp6_hop_limit; | 1288 | dst_metric_set(&rt->dst, RTAX_HOPLIMIT, |
1289 | ra_msg->icmph.icmp6_hop_limit); | ||
1248 | } | 1290 | } |
1249 | 1291 | ||
1250 | skip_defrtr: | 1292 | skip_defrtr: |
@@ -1309,8 +1351,7 @@ skip_linkparms: | |||
1309 | NEIGH_UPDATE_F_ISROUTER); | 1351 | NEIGH_UPDATE_F_ISROUTER); |
1310 | } | 1352 | } |
1311 | 1353 | ||
1312 | /* skip route and link configuration on routers */ | 1354 | if (!accept_ra(in6_dev)) |
1313 | if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_ra) | ||
1314 | goto out; | 1355 | goto out; |
1315 | 1356 | ||
1316 | #ifdef CONFIG_IPV6_ROUTE_INFO | 1357 | #ifdef CONFIG_IPV6_ROUTE_INFO |
@@ -1363,7 +1404,7 @@ skip_linkparms: | |||
1363 | in6_dev->cnf.mtu6 = mtu; | 1404 | in6_dev->cnf.mtu6 = mtu; |
1364 | 1405 | ||
1365 | if (rt) | 1406 | if (rt) |
1366 | rt->dst.metrics[RTAX_MTU-1] = mtu; | 1407 | dst_metric_set(&rt->dst, RTAX_MTU, mtu); |
1367 | 1408 | ||
1368 | rt6_mtu_change(skb->dev, mtu); | 1409 | rt6_mtu_change(skb->dev, mtu); |
1369 | } | 1410 | } |
@@ -1394,8 +1435,8 @@ static void ndisc_redirect_rcv(struct sk_buff *skb) | |||
1394 | { | 1435 | { |
1395 | struct inet6_dev *in6_dev; | 1436 | struct inet6_dev *in6_dev; |
1396 | struct icmp6hdr *icmph; | 1437 | struct icmp6hdr *icmph; |
1397 | struct in6_addr *dest; | 1438 | const struct in6_addr *dest; |
1398 | struct in6_addr *target; /* new first hop to destination */ | 1439 | const struct in6_addr *target; /* new first hop to destination */ |
1399 | struct neighbour *neigh; | 1440 | struct neighbour *neigh; |
1400 | int on_link = 0; | 1441 | int on_link = 0; |
1401 | struct ndisc_options ndopts; | 1442 | struct ndisc_options ndopts; |
@@ -1428,7 +1469,7 @@ static void ndisc_redirect_rcv(struct sk_buff *skb) | |||
1428 | } | 1469 | } |
1429 | 1470 | ||
1430 | icmph = icmp6_hdr(skb); | 1471 | icmph = icmp6_hdr(skb); |
1431 | target = (struct in6_addr *) (icmph + 1); | 1472 | target = (const struct in6_addr *) (icmph + 1); |
1432 | dest = target + 1; | 1473 | dest = target + 1; |
1433 | 1474 | ||
1434 | if (ipv6_addr_is_multicast(dest)) { | 1475 | if (ipv6_addr_is_multicast(dest)) { |
@@ -1500,7 +1541,7 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh, | |||
1500 | struct rt6_info *rt; | 1541 | struct rt6_info *rt; |
1501 | struct dst_entry *dst; | 1542 | struct dst_entry *dst; |
1502 | struct inet6_dev *idev; | 1543 | struct inet6_dev *idev; |
1503 | struct flowi fl; | 1544 | struct flowi6 fl6; |
1504 | u8 *opt; | 1545 | u8 *opt; |
1505 | int rd_len; | 1546 | int rd_len; |
1506 | int err; | 1547 | int err; |
@@ -1520,15 +1561,15 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh, | |||
1520 | return; | 1561 | return; |
1521 | } | 1562 | } |
1522 | 1563 | ||
1523 | icmpv6_flow_init(sk, &fl, NDISC_REDIRECT, | 1564 | icmpv6_flow_init(sk, &fl6, NDISC_REDIRECT, |
1524 | &saddr_buf, &ipv6_hdr(skb)->saddr, dev->ifindex); | 1565 | &saddr_buf, &ipv6_hdr(skb)->saddr, dev->ifindex); |
1525 | 1566 | ||
1526 | dst = ip6_route_output(net, NULL, &fl); | 1567 | dst = ip6_route_output(net, NULL, &fl6); |
1527 | if (dst == NULL) | 1568 | if (dst == NULL) |
1528 | return; | 1569 | return; |
1529 | 1570 | ||
1530 | err = xfrm_lookup(net, &dst, &fl, NULL, 0); | 1571 | dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0); |
1531 | if (err) | 1572 | if (IS_ERR(dst)) |
1532 | return; | 1573 | return; |
1533 | 1574 | ||
1534 | rt = (struct rt6_info *) dst; | 1575 | rt = (struct rt6_info *) dst; |
@@ -1538,7 +1579,9 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh, | |||
1538 | "ICMPv6 Redirect: destination is not a neighbour.\n"); | 1579 | "ICMPv6 Redirect: destination is not a neighbour.\n"); |
1539 | goto release; | 1580 | goto release; |
1540 | } | 1581 | } |
1541 | if (!xrlim_allow(dst, 1*HZ)) | 1582 | if (!rt->rt6i_peer) |
1583 | rt6_bind_peer(rt, 1); | ||
1584 | if (inet_peer_xrlim_allow(rt->rt6i_peer, 1*HZ)) | ||
1542 | goto release; | 1585 | goto release; |
1543 | 1586 | ||
1544 | if (dev->addr_len) { | 1587 | if (dev->addr_len) { |
@@ -1703,6 +1746,9 @@ static int ndisc_netdev_event(struct notifier_block *this, unsigned long event, | |||
1703 | neigh_ifdown(&nd_tbl, dev); | 1746 | neigh_ifdown(&nd_tbl, dev); |
1704 | fib6_run_gc(~0UL, net); | 1747 | fib6_run_gc(~0UL, net); |
1705 | break; | 1748 | break; |
1749 | case NETDEV_NOTIFY_PEERS: | ||
1750 | ndisc_send_unsol_na(dev); | ||
1751 | break; | ||
1706 | default: | 1752 | default: |
1707 | break; | 1753 | break; |
1708 | } | 1754 | } |