aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/ndisc.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6/ndisc.c')
-rw-r--r--net/ipv6/ndisc.c42
1 files changed, 19 insertions, 23 deletions
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index 9da6e02eaaeb..44e5b7f2a6c1 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -370,17 +370,14 @@ static int ndisc_constructor(struct neighbour *neigh)
370 struct neigh_parms *parms; 370 struct neigh_parms *parms;
371 int is_multicast = ipv6_addr_is_multicast(addr); 371 int is_multicast = ipv6_addr_is_multicast(addr);
372 372
373 rcu_read_lock();
374 in6_dev = in6_dev_get(dev); 373 in6_dev = in6_dev_get(dev);
375 if (in6_dev == NULL) { 374 if (in6_dev == NULL) {
376 rcu_read_unlock();
377 return -EINVAL; 375 return -EINVAL;
378 } 376 }
379 377
380 parms = in6_dev->nd_parms; 378 parms = in6_dev->nd_parms;
381 __neigh_parms_put(neigh->parms); 379 __neigh_parms_put(neigh->parms);
382 neigh->parms = neigh_parms_clone(parms); 380 neigh->parms = neigh_parms_clone(parms);
383 rcu_read_unlock();
384 381
385 neigh->type = is_multicast ? RTN_MULTICAST : RTN_UNICAST; 382 neigh->type = is_multicast ? RTN_MULTICAST : RTN_UNICAST;
386 if (!dev->header_ops) { 383 if (!dev->header_ops) {
@@ -533,7 +530,8 @@ void ndisc_send_skb(struct sk_buff *skb,
533 530
534 skb_dst_set(skb, dst); 531 skb_dst_set(skb, dst);
535 532
536 idev = in6_dev_get(dst->dev); 533 rcu_read_lock();
534 idev = __in6_dev_get(dst->dev);
537 IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len); 535 IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len);
538 536
539 err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, skb, NULL, dst->dev, 537 err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, skb, NULL, dst->dev,
@@ -543,8 +541,7 @@ void ndisc_send_skb(struct sk_buff *skb,
543 ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS); 541 ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS);
544 } 542 }
545 543
546 if (likely(idev != NULL)) 544 rcu_read_unlock();
547 in6_dev_put(idev);
548} 545}
549 546
550EXPORT_SYMBOL(ndisc_send_skb); 547EXPORT_SYMBOL(ndisc_send_skb);
@@ -1039,7 +1036,7 @@ static void ndisc_recv_rs(struct sk_buff *skb)
1039 if (skb->len < sizeof(*rs_msg)) 1036 if (skb->len < sizeof(*rs_msg))
1040 return; 1037 return;
1041 1038
1042 idev = in6_dev_get(skb->dev); 1039 idev = __in6_dev_get(skb->dev);
1043 if (!idev) { 1040 if (!idev) {
1044 if (net_ratelimit()) 1041 if (net_ratelimit())
1045 ND_PRINTK1("ICMP6 RS: can't find in6 device\n"); 1042 ND_PRINTK1("ICMP6 RS: can't find in6 device\n");
@@ -1080,7 +1077,7 @@ static void ndisc_recv_rs(struct sk_buff *skb)
1080 neigh_release(neigh); 1077 neigh_release(neigh);
1081 } 1078 }
1082out: 1079out:
1083 in6_dev_put(idev); 1080 return;
1084} 1081}
1085 1082
1086static void ndisc_ra_useropt(struct sk_buff *ra, struct nd_opt_hdr *opt) 1083static void ndisc_ra_useropt(struct sk_buff *ra, struct nd_opt_hdr *opt)
@@ -1179,7 +1176,7 @@ static void ndisc_router_discovery(struct sk_buff *skb)
1179 * set the RA_RECV flag in the interface 1176 * set the RA_RECV flag in the interface
1180 */ 1177 */
1181 1178
1182 in6_dev = in6_dev_get(skb->dev); 1179 in6_dev = __in6_dev_get(skb->dev);
1183 if (in6_dev == NULL) { 1180 if (in6_dev == NULL) {
1184 ND_PRINTK0(KERN_ERR 1181 ND_PRINTK0(KERN_ERR
1185 "ICMPv6 RA: can't find inet6 device for %s.\n", 1182 "ICMPv6 RA: can't find inet6 device for %s.\n",
@@ -1188,7 +1185,6 @@ static void ndisc_router_discovery(struct sk_buff *skb)
1188 } 1185 }
1189 1186
1190 if (!ndisc_parse_options(opt, optlen, &ndopts)) { 1187 if (!ndisc_parse_options(opt, optlen, &ndopts)) {
1191 in6_dev_put(in6_dev);
1192 ND_PRINTK2(KERN_WARNING 1188 ND_PRINTK2(KERN_WARNING
1193 "ICMP6 RA: invalid ND options\n"); 1189 "ICMP6 RA: invalid ND options\n");
1194 return; 1190 return;
@@ -1225,6 +1221,9 @@ static void ndisc_router_discovery(struct sk_buff *skb)
1225 if (!in6_dev->cnf.accept_ra_defrtr) 1221 if (!in6_dev->cnf.accept_ra_defrtr)
1226 goto skip_defrtr; 1222 goto skip_defrtr;
1227 1223
1224 if (ipv6_chk_addr(dev_net(in6_dev->dev), &ipv6_hdr(skb)->saddr, NULL, 0))
1225 goto skip_defrtr;
1226
1228 lifetime = ntohs(ra_msg->icmph.icmp6_rt_lifetime); 1227 lifetime = ntohs(ra_msg->icmph.icmp6_rt_lifetime);
1229 1228
1230#ifdef CONFIG_IPV6_ROUTER_PREF 1229#ifdef CONFIG_IPV6_ROUTER_PREF
@@ -1255,7 +1254,6 @@ static void ndisc_router_discovery(struct sk_buff *skb)
1255 ND_PRINTK0(KERN_ERR 1254 ND_PRINTK0(KERN_ERR
1256 "ICMPv6 RA: %s() failed to add default route.\n", 1255 "ICMPv6 RA: %s() failed to add default route.\n",
1257 __func__); 1256 __func__);
1258 in6_dev_put(in6_dev);
1259 return; 1257 return;
1260 } 1258 }
1261 1259
@@ -1265,7 +1263,6 @@ static void ndisc_router_discovery(struct sk_buff *skb)
1265 "ICMPv6 RA: %s() got default router without neighbour.\n", 1263 "ICMPv6 RA: %s() got default router without neighbour.\n",
1266 __func__); 1264 __func__);
1267 dst_release(&rt->dst); 1265 dst_release(&rt->dst);
1268 in6_dev_put(in6_dev);
1269 return; 1266 return;
1270 } 1267 }
1271 neigh->flags |= NTF_ROUTER; 1268 neigh->flags |= NTF_ROUTER;
@@ -1349,6 +1346,9 @@ skip_linkparms:
1349 goto out; 1346 goto out;
1350 1347
1351#ifdef CONFIG_IPV6_ROUTE_INFO 1348#ifdef CONFIG_IPV6_ROUTE_INFO
1349 if (ipv6_chk_addr(dev_net(in6_dev->dev), &ipv6_hdr(skb)->saddr, NULL, 0))
1350 goto skip_routeinfo;
1351
1352 if (in6_dev->cnf.accept_ra_rtr_pref && ndopts.nd_opts_ri) { 1352 if (in6_dev->cnf.accept_ra_rtr_pref && ndopts.nd_opts_ri) {
1353 struct nd_opt_hdr *p; 1353 struct nd_opt_hdr *p;
1354 for (p = ndopts.nd_opts_ri; 1354 for (p = ndopts.nd_opts_ri;
@@ -1366,6 +1366,8 @@ skip_linkparms:
1366 &ipv6_hdr(skb)->saddr); 1366 &ipv6_hdr(skb)->saddr);
1367 } 1367 }
1368 } 1368 }
1369
1370skip_routeinfo:
1369#endif 1371#endif
1370 1372
1371#ifdef CONFIG_IPV6_NDISC_NODETYPE 1373#ifdef CONFIG_IPV6_NDISC_NODETYPE
@@ -1422,7 +1424,6 @@ out:
1422 dst_release(&rt->dst); 1424 dst_release(&rt->dst);
1423 else if (neigh) 1425 else if (neigh)
1424 neigh_release(neigh); 1426 neigh_release(neigh);
1425 in6_dev_put(in6_dev);
1426} 1427}
1427 1428
1428static void ndisc_redirect_rcv(struct sk_buff *skb) 1429static void ndisc_redirect_rcv(struct sk_buff *skb)
@@ -1481,13 +1482,11 @@ static void ndisc_redirect_rcv(struct sk_buff *skb)
1481 return; 1482 return;
1482 } 1483 }
1483 1484
1484 in6_dev = in6_dev_get(skb->dev); 1485 in6_dev = __in6_dev_get(skb->dev);
1485 if (!in6_dev) 1486 if (!in6_dev)
1486 return; 1487 return;
1487 if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects) { 1488 if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects)
1488 in6_dev_put(in6_dev);
1489 return; 1489 return;
1490 }
1491 1490
1492 /* RFC2461 8.1: 1491 /* RFC2461 8.1:
1493 * The IP source address of the Redirect MUST be the same as the current 1492 * The IP source address of the Redirect MUST be the same as the current
@@ -1497,7 +1496,6 @@ static void ndisc_redirect_rcv(struct sk_buff *skb)
1497 if (!ndisc_parse_options((u8*)(dest + 1), optlen, &ndopts)) { 1496 if (!ndisc_parse_options((u8*)(dest + 1), optlen, &ndopts)) {
1498 ND_PRINTK2(KERN_WARNING 1497 ND_PRINTK2(KERN_WARNING
1499 "ICMPv6 Redirect: invalid ND options\n"); 1498 "ICMPv6 Redirect: invalid ND options\n");
1500 in6_dev_put(in6_dev);
1501 return; 1499 return;
1502 } 1500 }
1503 if (ndopts.nd_opts_tgt_lladdr) { 1501 if (ndopts.nd_opts_tgt_lladdr) {
@@ -1506,7 +1504,6 @@ static void ndisc_redirect_rcv(struct sk_buff *skb)
1506 if (!lladdr) { 1504 if (!lladdr) {
1507 ND_PRINTK2(KERN_WARNING 1505 ND_PRINTK2(KERN_WARNING
1508 "ICMPv6 Redirect: invalid link-layer address length\n"); 1506 "ICMPv6 Redirect: invalid link-layer address length\n");
1509 in6_dev_put(in6_dev);
1510 return; 1507 return;
1511 } 1508 }
1512 } 1509 }
@@ -1518,7 +1515,6 @@ static void ndisc_redirect_rcv(struct sk_buff *skb)
1518 on_link); 1515 on_link);
1519 neigh_release(neigh); 1516 neigh_release(neigh);
1520 } 1517 }
1521 in6_dev_put(in6_dev);
1522} 1518}
1523 1519
1524void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh, 1520void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
@@ -1651,7 +1647,8 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
1651 csum_partial(icmph, len, 0)); 1647 csum_partial(icmph, len, 0));
1652 1648
1653 skb_dst_set(buff, dst); 1649 skb_dst_set(buff, dst);
1654 idev = in6_dev_get(dst->dev); 1650 rcu_read_lock();
1651 idev = __in6_dev_get(dst->dev);
1655 IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len); 1652 IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len);
1656 err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, buff, NULL, dst->dev, 1653 err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, buff, NULL, dst->dev,
1657 dst_output); 1654 dst_output);
@@ -1660,8 +1657,7 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
1660 ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS); 1657 ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS);
1661 } 1658 }
1662 1659
1663 if (likely(idev != NULL)) 1660 rcu_read_unlock();
1664 in6_dev_put(idev);
1665 return; 1661 return;
1666 1662
1667release: 1663release: