diff options
Diffstat (limited to 'net/ipv6/ndisc.c')
-rw-r--r-- | net/ipv6/ndisc.c | 53 |
1 files changed, 41 insertions, 12 deletions
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index b50055b9278d..0304b5fe8d6a 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c | |||
@@ -62,6 +62,7 @@ | |||
62 | #include <linux/sysctl.h> | 62 | #include <linux/sysctl.h> |
63 | #endif | 63 | #endif |
64 | 64 | ||
65 | #include <linux/if_addr.h> | ||
65 | #include <linux/if_arp.h> | 66 | #include <linux/if_arp.h> |
66 | #include <linux/ipv6.h> | 67 | #include <linux/ipv6.h> |
67 | #include <linux/icmpv6.h> | 68 | #include <linux/icmpv6.h> |
@@ -411,7 +412,8 @@ static void pndisc_destructor(struct pneigh_entry *n) | |||
411 | */ | 412 | */ |
412 | 413 | ||
413 | static inline void ndisc_flow_init(struct flowi *fl, u8 type, | 414 | static inline void ndisc_flow_init(struct flowi *fl, u8 type, |
414 | struct in6_addr *saddr, struct in6_addr *daddr) | 415 | struct in6_addr *saddr, struct in6_addr *daddr, |
416 | int oif) | ||
415 | { | 417 | { |
416 | memset(fl, 0, sizeof(*fl)); | 418 | memset(fl, 0, sizeof(*fl)); |
417 | ipv6_addr_copy(&fl->fl6_src, saddr); | 419 | ipv6_addr_copy(&fl->fl6_src, saddr); |
@@ -419,6 +421,8 @@ static inline void ndisc_flow_init(struct flowi *fl, u8 type, | |||
419 | fl->proto = IPPROTO_ICMPV6; | 421 | fl->proto = IPPROTO_ICMPV6; |
420 | fl->fl_icmp_type = type; | 422 | fl->fl_icmp_type = type; |
421 | fl->fl_icmp_code = 0; | 423 | fl->fl_icmp_code = 0; |
424 | fl->oif = oif; | ||
425 | security_sk_classify_flow(ndisc_socket->sk, fl); | ||
422 | } | 426 | } |
423 | 427 | ||
424 | static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh, | 428 | static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh, |
@@ -450,7 +454,8 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh, | |||
450 | src_addr = &tmpaddr; | 454 | src_addr = &tmpaddr; |
451 | } | 455 | } |
452 | 456 | ||
453 | ndisc_flow_init(&fl, NDISC_NEIGHBOUR_ADVERTISEMENT, src_addr, daddr); | 457 | ndisc_flow_init(&fl, NDISC_NEIGHBOUR_ADVERTISEMENT, src_addr, daddr, |
458 | dev->ifindex); | ||
454 | 459 | ||
455 | dst = ndisc_dst_alloc(dev, neigh, daddr, ip6_output); | 460 | dst = ndisc_dst_alloc(dev, neigh, daddr, ip6_output); |
456 | if (!dst) | 461 | if (!dst) |
@@ -491,7 +496,7 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh, | |||
491 | msg->icmph.icmp6_unused = 0; | 496 | msg->icmph.icmp6_unused = 0; |
492 | msg->icmph.icmp6_router = router; | 497 | msg->icmph.icmp6_router = router; |
493 | msg->icmph.icmp6_solicited = solicited; | 498 | msg->icmph.icmp6_solicited = solicited; |
494 | msg->icmph.icmp6_override = !!override; | 499 | msg->icmph.icmp6_override = override; |
495 | 500 | ||
496 | /* Set the target address. */ | 501 | /* Set the target address. */ |
497 | ipv6_addr_copy(&msg->target, solicited_addr); | 502 | ipv6_addr_copy(&msg->target, solicited_addr); |
@@ -540,7 +545,8 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh, | |||
540 | saddr = &addr_buf; | 545 | saddr = &addr_buf; |
541 | } | 546 | } |
542 | 547 | ||
543 | ndisc_flow_init(&fl, NDISC_NEIGHBOUR_SOLICITATION, saddr, daddr); | 548 | ndisc_flow_init(&fl, NDISC_NEIGHBOUR_SOLICITATION, saddr, daddr, |
549 | dev->ifindex); | ||
544 | 550 | ||
545 | dst = ndisc_dst_alloc(dev, neigh, daddr, ip6_output); | 551 | dst = ndisc_dst_alloc(dev, neigh, daddr, ip6_output); |
546 | if (!dst) | 552 | if (!dst) |
@@ -615,7 +621,8 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr, | |||
615 | int len; | 621 | int len; |
616 | int err; | 622 | int err; |
617 | 623 | ||
618 | ndisc_flow_init(&fl, NDISC_ROUTER_SOLICITATION, saddr, daddr); | 624 | ndisc_flow_init(&fl, NDISC_ROUTER_SOLICITATION, saddr, daddr, |
625 | dev->ifindex); | ||
619 | 626 | ||
620 | dst = ndisc_dst_alloc(dev, NULL, daddr, ip6_output); | 627 | dst = ndisc_dst_alloc(dev, NULL, daddr, ip6_output); |
621 | if (!dst) | 628 | if (!dst) |
@@ -729,8 +736,10 @@ static void ndisc_recv_ns(struct sk_buff *skb) | |||
729 | struct inet6_ifaddr *ifp; | 736 | struct inet6_ifaddr *ifp; |
730 | struct inet6_dev *idev = NULL; | 737 | struct inet6_dev *idev = NULL; |
731 | struct neighbour *neigh; | 738 | struct neighbour *neigh; |
739 | struct pneigh_entry *pneigh = NULL; | ||
732 | int dad = ipv6_addr_any(saddr); | 740 | int dad = ipv6_addr_any(saddr); |
733 | int inc; | 741 | int inc; |
742 | int is_router; | ||
734 | 743 | ||
735 | if (ipv6_addr_is_multicast(&msg->target)) { | 744 | if (ipv6_addr_is_multicast(&msg->target)) { |
736 | ND_PRINTK2(KERN_WARNING | 745 | ND_PRINTK2(KERN_WARNING |
@@ -815,7 +824,9 @@ static void ndisc_recv_ns(struct sk_buff *skb) | |||
815 | 824 | ||
816 | if (ipv6_chk_acast_addr(dev, &msg->target) || | 825 | if (ipv6_chk_acast_addr(dev, &msg->target) || |
817 | (idev->cnf.forwarding && | 826 | (idev->cnf.forwarding && |
818 | pneigh_lookup(&nd_tbl, &msg->target, dev, 0))) { | 827 | (ipv6_devconf.proxy_ndp || idev->cnf.proxy_ndp) && |
828 | (pneigh = pneigh_lookup(&nd_tbl, | ||
829 | &msg->target, dev, 0)) != NULL)) { | ||
819 | if (!(NEIGH_CB(skb)->flags & LOCALLY_ENQUEUED) && | 830 | if (!(NEIGH_CB(skb)->flags & LOCALLY_ENQUEUED) && |
820 | skb->pkt_type != PACKET_HOST && | 831 | skb->pkt_type != PACKET_HOST && |
821 | inc != 0 && | 832 | inc != 0 && |
@@ -836,12 +847,14 @@ static void ndisc_recv_ns(struct sk_buff *skb) | |||
836 | goto out; | 847 | goto out; |
837 | } | 848 | } |
838 | 849 | ||
850 | is_router = !!(pneigh ? pneigh->flags & NTF_ROUTER : idev->cnf.forwarding); | ||
851 | |||
839 | if (dad) { | 852 | if (dad) { |
840 | struct in6_addr maddr; | 853 | struct in6_addr maddr; |
841 | 854 | ||
842 | ipv6_addr_all_nodes(&maddr); | 855 | ipv6_addr_all_nodes(&maddr); |
843 | ndisc_send_na(dev, NULL, &maddr, &msg->target, | 856 | ndisc_send_na(dev, NULL, &maddr, &msg->target, |
844 | idev->cnf.forwarding, 0, (ifp != NULL), 1); | 857 | is_router, 0, (ifp != NULL), 1); |
845 | goto out; | 858 | goto out; |
846 | } | 859 | } |
847 | 860 | ||
@@ -862,7 +875,7 @@ static void ndisc_recv_ns(struct sk_buff *skb) | |||
862 | NEIGH_UPDATE_F_OVERRIDE); | 875 | NEIGH_UPDATE_F_OVERRIDE); |
863 | if (neigh || !dev->hard_header) { | 876 | if (neigh || !dev->hard_header) { |
864 | ndisc_send_na(dev, neigh, saddr, &msg->target, | 877 | ndisc_send_na(dev, neigh, saddr, &msg->target, |
865 | idev->cnf.forwarding, | 878 | is_router, |
866 | 1, (ifp != NULL && inc), inc); | 879 | 1, (ifp != NULL && inc), inc); |
867 | if (neigh) | 880 | if (neigh) |
868 | neigh_release(neigh); | 881 | neigh_release(neigh); |
@@ -945,6 +958,20 @@ static void ndisc_recv_na(struct sk_buff *skb) | |||
945 | if (neigh->nud_state & NUD_FAILED) | 958 | if (neigh->nud_state & NUD_FAILED) |
946 | goto out; | 959 | goto out; |
947 | 960 | ||
961 | /* | ||
962 | * Don't update the neighbor cache entry on a proxy NA from | ||
963 | * ourselves because either the proxied node is off link or it | ||
964 | * has already sent a NA to us. | ||
965 | */ | ||
966 | if (lladdr && !memcmp(lladdr, dev->dev_addr, dev->addr_len) && | ||
967 | ipv6_devconf.forwarding && ipv6_devconf.proxy_ndp && | ||
968 | pneigh_lookup(&nd_tbl, &msg->target, dev, 0)) { | ||
969 | /* XXX: idev->cnf.prixy_ndp */ | ||
970 | WARN_ON(skb->dst != NULL && | ||
971 | ((struct rt6_info *)skb->dst)->rt6i_idev); | ||
972 | goto out; | ||
973 | } | ||
974 | |||
948 | neigh_update(neigh, lladdr, | 975 | neigh_update(neigh, lladdr, |
949 | msg->icmph.icmp6_solicited ? NUD_REACHABLE : NUD_STALE, | 976 | msg->icmph.icmp6_solicited ? NUD_REACHABLE : NUD_STALE, |
950 | NEIGH_UPDATE_F_WEAK_OVERRIDE| | 977 | NEIGH_UPDATE_F_WEAK_OVERRIDE| |
@@ -959,7 +986,7 @@ static void ndisc_recv_na(struct sk_buff *skb) | |||
959 | struct rt6_info *rt; | 986 | struct rt6_info *rt; |
960 | rt = rt6_get_dflt_router(saddr, dev); | 987 | rt = rt6_get_dflt_router(saddr, dev); |
961 | if (rt) | 988 | if (rt) |
962 | ip6_del_rt(rt, NULL, NULL, NULL); | 989 | ip6_del_rt(rt); |
963 | } | 990 | } |
964 | 991 | ||
965 | out: | 992 | out: |
@@ -1112,7 +1139,7 @@ static void ndisc_router_discovery(struct sk_buff *skb) | |||
1112 | 1139 | ||
1113 | if (rt && lifetime == 0) { | 1140 | if (rt && lifetime == 0) { |
1114 | neigh_clone(neigh); | 1141 | neigh_clone(neigh); |
1115 | ip6_del_rt(rt, NULL, NULL, NULL); | 1142 | ip6_del_rt(rt); |
1116 | rt = NULL; | 1143 | rt = NULL; |
1117 | } | 1144 | } |
1118 | 1145 | ||
@@ -1344,7 +1371,8 @@ static void ndisc_redirect_rcv(struct sk_buff *skb) | |||
1344 | 1371 | ||
1345 | neigh = __neigh_lookup(&nd_tbl, target, skb->dev, 1); | 1372 | neigh = __neigh_lookup(&nd_tbl, target, skb->dev, 1); |
1346 | if (neigh) { | 1373 | if (neigh) { |
1347 | rt6_redirect(dest, &skb->nh.ipv6h->saddr, neigh, lladdr, | 1374 | rt6_redirect(dest, &skb->nh.ipv6h->daddr, |
1375 | &skb->nh.ipv6h->saddr, neigh, lladdr, | ||
1348 | on_link); | 1376 | on_link); |
1349 | neigh_release(neigh); | 1377 | neigh_release(neigh); |
1350 | } | 1378 | } |
@@ -1380,7 +1408,8 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh, | |||
1380 | return; | 1408 | return; |
1381 | } | 1409 | } |
1382 | 1410 | ||
1383 | ndisc_flow_init(&fl, NDISC_REDIRECT, &saddr_buf, &skb->nh.ipv6h->saddr); | 1411 | ndisc_flow_init(&fl, NDISC_REDIRECT, &saddr_buf, &skb->nh.ipv6h->saddr, |
1412 | dev->ifindex); | ||
1384 | 1413 | ||
1385 | dst = ip6_route_output(NULL, &fl); | 1414 | dst = ip6_route_output(NULL, &fl); |
1386 | if (dst == NULL) | 1415 | if (dst == NULL) |