summaryrefslogtreecommitdiffstats
path: root/sys/netinet6/nd6_nbr.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/netinet6/nd6_nbr.c')
-rw-r--r--sys/netinet6/nd6_nbr.c161
1 files changed, 109 insertions, 52 deletions
diff --git a/sys/netinet6/nd6_nbr.c b/sys/netinet6/nd6_nbr.c
index b2b3c30..4a49302 100644
--- a/sys/netinet6/nd6_nbr.c
+++ b/sys/netinet6/nd6_nbr.c
@@ -59,6 +59,7 @@
#include <netinet6/in6_var.h>
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
+#include <netinet6/scope6_var.h>
#include <netinet6/nd6.h>
#include <netinet/icmp6.h>
@@ -120,6 +121,8 @@ nd6_ns_input(m, off, icmp6len)
#endif
ip6 = mtod(m, struct ip6_hdr *); /* adjust pointer for safety */
taddr6 = nd_ns->nd_ns_target;
+ if (in6_setscope(&taddr6, ifp, NULL) != 0)
+ goto bad;
if (ip6->ip6_hlim != 255) {
nd6log((LOG_ERR,
@@ -149,9 +152,6 @@ nd6_ns_input(m, off, icmp6len)
goto bad;
}
- if (IN6_IS_SCOPE_LINKLOCAL(&taddr6))
- taddr6.s6_addr16[1] = htons(ifp->if_index);
-
icmp6len -= sizeof(*nd_ns);
nd6_option_init(nd_ns + 1, icmp6len, &ndopts);
if (nd6_options(&ndopts) < 0) {
@@ -300,9 +300,12 @@ nd6_ns_input(m, off, icmp6len)
* S bit ("solicited") must be zero.
*/
if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) {
- saddr6 = in6addr_linklocal_allnodes;
- saddr6.s6_addr16[1] = htons(ifp->if_index);
- nd6_na_output(ifp, &saddr6, &taddr6,
+ struct in6_addr in6_all;
+
+ in6_all = in6addr_linklocal_allnodes;
+ if (in6_setscope(&in6_all, ifp, NULL) != 0)
+ goto bad;
+ nd6_na_output(ifp, &in6_all, &taddr6,
((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) |
(ip6_forwarding ? ND_NA_FLAG_ROUTER : 0),
tlladdr, (struct sockaddr *)proxydl);
@@ -347,12 +350,14 @@ nd6_ns_output(ifp, daddr6, taddr6, ln, dad)
struct mbuf *m;
struct ip6_hdr *ip6;
struct nd_neighbor_solicit *nd_ns;
- struct in6_ifaddr *ia = NULL;
+ struct in6_addr *src, src_in;
struct ip6_moptions im6o;
int icmp6len;
int maxlen;
caddr_t mac;
- struct ifnet *outif = NULL;
+ struct route_in6 ro;
+
+ bzero(&ro, sizeof(ro));
if (IN6_IS_ADDR_MULTICAST(taddr6))
return;
@@ -403,11 +408,13 @@ nd6_ns_output(ifp, daddr6, taddr6, ln, dad)
ip6->ip6_dst = *daddr6;
else {
ip6->ip6_dst.s6_addr16[0] = IPV6_ADDR_INT16_MLL;
- ip6->ip6_dst.s6_addr16[1] = htons(ifp->if_index);
+ ip6->ip6_dst.s6_addr16[1] = 0;
ip6->ip6_dst.s6_addr32[1] = 0;
ip6->ip6_dst.s6_addr32[2] = IPV6_ADDR_INT32_ONE;
ip6->ip6_dst.s6_addr32[3] = taddr6->s6_addr32[3];
ip6->ip6_dst.s6_addr8[12] = 0xff;
+ if (in6_setscope(&ip6->ip6_dst, ifp, NULL) != 0)
+ goto bad;
}
if (!dad) {
/*
@@ -423,37 +430,52 @@ nd6_ns_output(ifp, daddr6, taddr6, ln, dad)
* (saddr6), if:
* - saddr6 is given from the caller (by giving "ln"), and
* - saddr6 belongs to the outgoing interface.
- * Otherwise, we perform a scope-wise match.
+ * Otherwise, we perform the source address selection as usual.
*/
struct ip6_hdr *hip6; /* hold ip6 */
- struct in6_addr *saddr6;
+ struct in6_addr *hsrc = NULL;
if (ln && ln->ln_hold) {
hip6 = mtod(ln->ln_hold, struct ip6_hdr *);
/* XXX pullup? */
if (sizeof(*hip6) < ln->ln_hold->m_len)
- saddr6 = &hip6->ip6_src;
+ hsrc = &hip6->ip6_src;
else
- saddr6 = NULL;
- } else
- saddr6 = NULL;
- if (saddr6 && in6ifa_ifpwithaddr(ifp, saddr6))
- bcopy(saddr6, &ip6->ip6_src, sizeof(*saddr6));
+ hsrc = NULL;
+ }
+ if (hsrc && in6ifa_ifpwithaddr(ifp, hsrc))
+ src = hsrc;
else {
- ia = in6_ifawithifp(ifp, &ip6->ip6_dst);
- if (ia == NULL) {
- m_freem(m);
- return;
+ int error;
+ struct sockaddr_in6 dst_sa;
+
+ bzero(&dst_sa, sizeof(dst_sa));
+ dst_sa.sin6_family = AF_INET6;
+ dst_sa.sin6_len = sizeof(dst_sa);
+ dst_sa.sin6_addr = ip6->ip6_dst;
+
+ src = in6_selectsrc(&dst_sa, NULL,
+ NULL, &ro, NULL, NULL, &error);
+ if (src == NULL) {
+ nd6log((LOG_DEBUG,
+ "nd6_ns_output: source can't be "
+ "determined: dst=%s, error=%d\n",
+ ip6_sprintf(&dst_sa.sin6_addr), error));
+ goto bad;
}
- ip6->ip6_src = ia->ia_addr.sin6_addr;
}
} else {
/*
* Source address for DAD packet must always be IPv6
* unspecified address. (0::0)
+ * We actually don't have to 0-clear the address (we did it
+ * above), but we do so here explicitly to make the intention
+ * clearer.
*/
- bzero(&ip6->ip6_src, sizeof(ip6->ip6_src));
+ bzero(&src_in, sizeof(src_in));
+ src = &src_in;
}
+ ip6->ip6_src = *src;
nd_ns = (struct nd_neighbor_solicit *)(ip6 + 1);
nd_ns->nd_ns_type = ND_NEIGHBOR_SOLICIT;
nd_ns->nd_ns_code = 0;
@@ -493,12 +515,22 @@ nd6_ns_output(ifp, daddr6, taddr6, ln, dad)
nd_ns->nd_ns_cksum =
in6_cksum(m, IPPROTO_ICMPV6, sizeof(*ip6), icmp6len);
- ip6_output(m, NULL, NULL, dad ? IPV6_DADOUTPUT : 0, &im6o, &outif, NULL);
- if (outif) {
- icmp6_ifstat_inc(outif, ifs6_out_msg);
- icmp6_ifstat_inc(outif, ifs6_out_neighborsolicit);
- }
+ ip6_output(m, NULL, &ro, dad ? IPV6_DADOUTPUT : 0, &im6o, NULL, NULL);
+ icmp6_ifstat_inc(ifp, ifs6_out_msg);
+ icmp6_ifstat_inc(ifp, ifs6_out_neighborsolicit);
icmp6stat.icp6s_outhist[ND_NEIGHBOR_SOLICIT]++;
+
+ if (ro.ro_rt) { /* we don't cache this route. */
+ RTFREE(ro.ro_rt);
+ }
+ return;
+
+ bad:
+ if (ro.ro_rt) {
+ RTFREE(ro.ro_rt);
+ }
+ m_freem(m);
+ return;
}
/*
@@ -551,14 +583,15 @@ nd6_na_input(m, off, icmp6len)
return;
}
#endif
- taddr6 = nd_na->nd_na_target;
+
flags = nd_na->nd_na_flags_reserved;
is_router = ((flags & ND_NA_FLAG_ROUTER) != 0);
is_solicited = ((flags & ND_NA_FLAG_SOLICITED) != 0);
is_override = ((flags & ND_NA_FLAG_OVERRIDE) != 0);
- if (IN6_IS_SCOPE_LINKLOCAL(&taddr6))
- taddr6.s6_addr16[1] = htons(ifp->if_index);
+ taddr6 = nd_na->nd_na_target;
+ if (in6_setscope(&taddr6, ifp, NULL))
+ return; /* XXX: impossible */
if (IN6_IS_ADDR_MULTICAST(&taddr6)) {
nd6log((LOG_ERR,
@@ -806,9 +839,9 @@ nd6_na_input(m, off, icmp6len)
* - anycast advertisement delay rule (RFC2461 7.2.7, SHOULD)
*/
void
-nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr, sdl0)
+nd6_na_output(ifp, daddr6_0, taddr6, flags, tlladdr, sdl0)
struct ifnet *ifp;
- const struct in6_addr *daddr6, *taddr6;
+ const struct in6_addr *daddr6_0, *taddr6;
u_long flags;
int tlladdr; /* 1 if include target link-layer address */
struct sockaddr *sdl0; /* sockaddr_dl (= proxy NA) or NULL */
@@ -816,12 +849,16 @@ nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr, sdl0)
struct mbuf *m;
struct ip6_hdr *ip6;
struct nd_neighbor_advert *nd_na;
- struct in6_ifaddr *ia = NULL;
struct ip6_moptions im6o;
- int icmp6len;
- int maxlen;
+ struct in6_addr *src, daddr6;
+ struct sockaddr_in6 dst_sa;
+ int icmp6len, maxlen, error;
caddr_t mac = NULL;
- struct ifnet *outif = NULL;
+ struct route_in6 ro;
+
+ bzero(&ro, sizeof(ro));
+
+ daddr6 = *daddr6_0; /* make a local copy for modification */
/* estimate the size of message */
maxlen = sizeof(*ip6) + sizeof(*nd_na);
@@ -846,7 +883,7 @@ nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr, sdl0)
return;
m->m_pkthdr.rcvif = NULL;
- if (IN6_IS_ADDR_MULTICAST(daddr6)) {
+ if (IN6_IS_ADDR_MULTICAST(&daddr6)) {
m->m_flags |= M_MCAST;
im6o.im6o_multicast_ifp = ifp;
im6o.im6o_multicast_hlim = 255;
@@ -864,26 +901,36 @@ nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr, sdl0)
ip6->ip6_vfc |= IPV6_VERSION;
ip6->ip6_nxt = IPPROTO_ICMPV6;
ip6->ip6_hlim = 255;
- if (IN6_IS_ADDR_UNSPECIFIED(daddr6)) {
+ if (IN6_IS_ADDR_UNSPECIFIED(&daddr6)) {
/* reply to DAD */
ip6->ip6_dst.s6_addr16[0] = IPV6_ADDR_INT16_MLL;
- ip6->ip6_dst.s6_addr16[1] = htons(ifp->if_index);
+ ip6->ip6_dst.s6_addr16[1] = 0;
ip6->ip6_dst.s6_addr32[1] = 0;
ip6->ip6_dst.s6_addr32[2] = 0;
ip6->ip6_dst.s6_addr32[3] = IPV6_ADDR_INT32_ONE;
+ if (in6_setscope(&daddr6, ifp, NULL))
+ goto bad;
+
flags &= ~ND_NA_FLAG_SOLICITED;
- } else
- ip6->ip6_dst = *daddr6;
+ }
+ ip6->ip6_dst = daddr6;
+ bzero(&dst_sa, sizeof(struct sockaddr_in6));
+ dst_sa.sin6_family = AF_INET6;
+ dst_sa.sin6_len = sizeof(struct sockaddr_in6);
+ dst_sa.sin6_addr = daddr6;
/*
* Select a source whose scope is the same as that of the dest.
*/
- ia = in6_ifawithifp(ifp, &ip6->ip6_dst);
- if (ia == NULL) {
- m_freem(m);
- return;
+ bcopy(&dst_sa, &ro.ro_dst, sizeof(dst_sa));
+ src = in6_selectsrc(&dst_sa, NULL, NULL, &ro, NULL, NULL, &error);
+ if (src == NULL) {
+ nd6log((LOG_DEBUG, "nd6_na_output: source can't be "
+ "determined: dst=%s, error=%d\n",
+ ip6_sprintf(&dst_sa.sin6_addr), error));
+ goto bad;
}
- ip6->ip6_src = ia->ia_addr.sin6_addr;
+ ip6->ip6_src = *src;
nd_na = (struct nd_neighbor_advert *)(ip6 + 1);
nd_na->nd_na_type = ND_NEIGHBOR_ADVERT;
nd_na->nd_na_code = 0;
@@ -941,12 +988,22 @@ nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr, sdl0)
nd_na->nd_na_cksum =
in6_cksum(m, IPPROTO_ICMPV6, sizeof(struct ip6_hdr), icmp6len);
- ip6_output(m, NULL, NULL, 0, &im6o, &outif, NULL);
- if (outif) {
- icmp6_ifstat_inc(outif, ifs6_out_msg);
- icmp6_ifstat_inc(outif, ifs6_out_neighboradvert);
- }
+ ip6_output(m, NULL, &ro, 0, &im6o, NULL, NULL);
+ icmp6_ifstat_inc(ifp, ifs6_out_msg);
+ icmp6_ifstat_inc(ifp, ifs6_out_neighboradvert);
icmp6stat.icp6s_outhist[ND_NEIGHBOR_ADVERT]++;
+
+ if (ro.ro_rt) { /* we don't cache this route. */
+ RTFREE(ro.ro_rt);
+ }
+ return;
+
+ bad:
+ if (ro.ro_rt) {
+ RTFREE(ro.ro_rt);
+ }
+ m_freem(m);
+ return;
}
caddr_t
OpenPOWER on IntegriCloud