summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/net/if_gif.c25
-rw-r--r--sys/net/if_spppsubr.c8
-rw-r--r--sys/netinet/icmp6.h1
-rw-r--r--sys/netinet/ip_carp.c35
-rw-r--r--sys/netinet/tcp_subr.c22
-rw-r--r--sys/netinet/tcp_timewait.c22
-rw-r--r--sys/netinet/tcp_usrreq.c2
-rw-r--r--sys/netinet6/ah_core.c1
-rw-r--r--sys/netinet6/icmp6.c189
-rw-r--r--sys/netinet6/in6.c323
-rw-r--r--sys/netinet6/in6.h1
-rw-r--r--sys/netinet6/in6_cksum.c31
-rw-r--r--sys/netinet6/in6_ifattach.c13
-rw-r--r--sys/netinet6/in6_pcb.c79
-rw-r--r--sys/netinet6/in6_proto.c2
-rw-r--r--sys/netinet6/in6_src.c346
-rw-r--r--sys/netinet6/in6_var.h5
-rw-r--r--sys/netinet6/ip6_forward.c45
-rw-r--r--sys/netinet6/ip6_input.c80
-rw-r--r--sys/netinet6/ip6_mroute.c31
-rw-r--r--sys/netinet6/ip6_output.c454
-rw-r--r--sys/netinet6/ip6_var.h5
-rw-r--r--sys/netinet6/ipsec.c26
-rw-r--r--sys/netinet6/mld6.c93
-rw-r--r--sys/netinet6/nd6.c95
-rw-r--r--sys/netinet6/nd6_nbr.c161
-rw-r--r--sys/netinet6/nd6_rtr.c2
-rw-r--r--sys/netinet6/raw_ip6.c148
-rw-r--r--sys/netinet6/route6.c23
-rw-r--r--sys/netinet6/scope6.c254
-rw-r--r--sys/netinet6/scope6_var.h4
-rw-r--r--sys/netinet6/udp6_output.c64
-rw-r--r--sys/netinet6/udp6_usrreq.c5
-rw-r--r--sys/netkey/key.c63
34 files changed, 1419 insertions, 1239 deletions
diff --git a/sys/net/if_gif.c b/sys/net/if_gif.c
index 3291321..d5c7cd7 100644
--- a/sys/net/if_gif.c
+++ b/sys/net/if_gif.c
@@ -74,6 +74,7 @@
#include <netinet6/in6_var.h>
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
+#include <netinet6/scope6_var.h>
#include <netinet6/in6_gif.h>
#include <netinet6/ip6protosw.h>
#endif /* INET6 */
@@ -679,6 +680,13 @@ gif_ioctl(ifp, cmd, data)
if (src->sa_len > size)
return EINVAL;
bcopy((caddr_t)src, (caddr_t)dst, src->sa_len);
+#ifdef INET6
+ if (dst->sa_family == AF_INET6) {
+ error = sa6_recoverscope((struct sockaddr_in6 *)dst);
+ if (error != 0)
+ return (error);
+ }
+#endif
break;
case SIOCGIFPDSTADDR:
@@ -711,6 +719,13 @@ gif_ioctl(ifp, cmd, data)
if (src->sa_len > size)
return EINVAL;
bcopy((caddr_t)src, (caddr_t)dst, src->sa_len);
+#ifdef INET6
+ if (dst->sa_family == AF_INET6) {
+ error = sa6_recoverscope((struct sockaddr_in6 *)dst);
+ if (error != 0)
+ return (error);
+ }
+#endif
break;
case SIOCGLIFPHYADDR:
@@ -832,6 +847,16 @@ gif_set_tunnel(ifp, src, dst)
#endif
#ifdef INET6
case AF_INET6:
+ /*
+ * Check validity of the scope zone ID of the addresses, and
+ * convert it into the kernel internal form if necessary.
+ */
+ error = sa6_embedscope((struct sockaddr_in6 *)sc->gif_psrc, 0);
+ if (error != 0)
+ break;
+ error = sa6_embedscope((struct sockaddr_in6 *)sc->gif_pdst, 0);
+ if (error != 0)
+ break;
error = in6_gif_attach(sc);
break;
#endif
diff --git a/sys/net/if_spppsubr.c b/sys/net/if_spppsubr.c
index b1cd8fe..b3d1df5 100644
--- a/sys/net/if_spppsubr.c
+++ b/sys/net/if_spppsubr.c
@@ -77,6 +77,10 @@
#include <netinet/tcp.h>
#endif
+#ifdef INET6
+#include <netinet6/scope6_var.h>
+#endif
+
#if defined (__FreeBSD__) || defined (__OpenBSD__)
# include <netinet/if_ether.h>
#else
@@ -3571,7 +3575,7 @@ sppp_ipv6cp_RCR(struct sppp *sp, struct lcp_header *h, int len)
nohisaddr = IN6_IS_ADDR_UNSPECIFIED(&desiredaddr);
desiredaddr.s6_addr16[0] = htons(0xfe80);
- desiredaddr.s6_addr16[1] = htons(SP2IFP(sp)->if_index);
+ (void)in6_setscope(&desiredaddr, SP2IFP(sp), NULL);
if (!collision && !nohisaddr) {
/* no collision, hisaddr known - Conf-Ack */
@@ -3714,7 +3718,7 @@ sppp_ipv6cp_RCN_nak(struct sppp *sp, struct lcp_header *h, int len)
break;
bzero(&suggestaddr, sizeof(suggestaddr));
suggestaddr.s6_addr16[0] = htons(0xfe80);
- suggestaddr.s6_addr16[1] = htons(SP2IFP(sp)->if_index);
+ (void)in6_setscope(&suggestaddr, SP2IFP(sp), NULL);
bcopy(&p[2], &suggestaddr.s6_addr[8], 8);
sp->ipv6cp.opts |= (1 << IPV6CP_OPT_IFID);
diff --git a/sys/netinet/icmp6.h b/sys/netinet/icmp6.h
index 4dee079..b35ec09 100644
--- a/sys/netinet/icmp6.h
+++ b/sys/netinet/icmp6.h
@@ -632,6 +632,7 @@ struct in6_multi;
void icmp6_init(void);
void icmp6_paramerror(struct mbuf *, int);
void icmp6_error(struct mbuf *, int, int, int);
+void icmp6_error2(struct mbuf *, int, int, int, struct ifnet *);
int icmp6_input(struct mbuf **, int *, int);
void icmp6_fasttimo(void);
void icmp6_reflect(struct mbuf *, size_t);
diff --git a/sys/netinet/ip_carp.c b/sys/netinet/ip_carp.c
index 83fb9b9..812caf3 100644
--- a/sys/netinet/ip_carp.c
+++ b/sys/netinet/ip_carp.c
@@ -268,8 +268,7 @@ carp_hmac_prepare(struct carp_softc *sc)
TAILQ_FOREACH(ifa, &SC2IFP(sc)->if_addrlist, ifa_list) {
if (ifa->ifa_addr->sa_family == AF_INET6) {
in6 = ifatoia6(ifa)->ia_addr.sin6_addr;
- if (IN6_IS_ADDR_LINKLOCAL(&in6))
- in6.s6_addr16[1] = 0;
+ in6_clearscope(&in6);
SHA1Update(&sc->sc_sha1, (void *)&in6, sizeof(in6));
}
}
@@ -1537,7 +1536,7 @@ carp_set_addr6(struct carp_softc *sc, struct sockaddr_in6 *sin6)
struct in6_ifaddr *ia, *ia_if;
struct ip6_moptions *im6o = &sc->sc_im6o;
struct in6_multi_mship *imm;
- struct sockaddr_in6 addr;
+ struct in6_addr in6;
int own, error;
if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
@@ -1586,25 +1585,25 @@ carp_set_addr6(struct carp_softc *sc, struct sockaddr_in6 *sin6)
im6o->im6o_multicast_ifp = ifp;
/* join CARP multicast address */
- bzero(&addr, sizeof(addr));
- addr.sin6_family = AF_INET6;
- addr.sin6_len = sizeof(addr);
- addr.sin6_addr.s6_addr16[0] = htons(0xff02);
- addr.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
- addr.sin6_addr.s6_addr8[15] = 0x12;
- if ((imm = in6_joingroup(ifp, &addr.sin6_addr, &error)) == NULL)
+ bzero(&in6, sizeof(in6));
+ in6.s6_addr16[0] = htons(0xff02);
+ in6.s6_addr8[15] = 0x12;
+ if (in6_setscope(&in6, ifp, NULL) != 0)
+ goto cleanup;
+ if ((imm = in6_joingroup(ifp, &in6, &error)) == NULL)
goto cleanup;
LIST_INSERT_HEAD(&im6o->im6o_memberships, imm, i6mm_chain);
/* join solicited multicast address */
- bzero(&addr.sin6_addr, sizeof(addr.sin6_addr));
- addr.sin6_addr.s6_addr16[0] = htons(0xff02);
- addr.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
- addr.sin6_addr.s6_addr32[1] = 0;
- addr.sin6_addr.s6_addr32[2] = htonl(1);
- addr.sin6_addr.s6_addr32[3] = sin6->sin6_addr.s6_addr32[3];
- addr.sin6_addr.s6_addr8[12] = 0xff;
- if ((imm = in6_joingroup(ifp, &addr.sin6_addr, &error)) == NULL)
+ bzero(&in6, sizeof(in6));
+ in6.s6_addr16[0] = htons(0xff02);
+ in6.s6_addr32[1] = 0;
+ in6.s6_addr32[2] = htonl(1);
+ in6.s6_addr32[3] = sin6->sin6_addr.s6_addr32[3];
+ in6.s6_addr8[12] = 0xff;
+ if (in6_setscope(&in6, ifp, NULL) != 0)
+ goto cleanup;
+ if ((imm = in6_joingroup(ifp, &in6, &error)) == NULL)
goto cleanup;
LIST_INSERT_HEAD(&im6o->im6o_memberships, imm, i6mm_chain);
}
diff --git a/sys/netinet/tcp_subr.c b/sys/netinet/tcp_subr.c
index 11d51e2..750c3b9 100644
--- a/sys/netinet/tcp_subr.c
+++ b/sys/netinet/tcp_subr.c
@@ -74,6 +74,7 @@
#include <netinet/ip_var.h>
#ifdef INET6
#include <netinet6/ip6_var.h>
+#include <netinet6/scope6_var.h>
#include <netinet6/nd6.h>
#endif
#include <netinet/ip_icmp.h>
@@ -1044,19 +1045,17 @@ tcp6_getcred(SYSCTL_HANDLER_ARGS)
error = SYSCTL_IN(req, addrs, sizeof(addrs));
if (error)
return (error);
+ if ((error = sa6_embedscope(&addrs[0], ip6_use_defzone)) != 0 ||
+ (error = sa6_embedscope(&addrs[1], ip6_use_defzone)) != 0) {
+ return (error);
+ }
if (IN6_IS_ADDR_V4MAPPED(&addrs[0].sin6_addr)) {
if (IN6_IS_ADDR_V4MAPPED(&addrs[1].sin6_addr))
mapped = 1;
else
return (EINVAL);
- } else {
- error = in6_embedscope(&a6[0], &addrs[0], NULL, NULL);
- if (error)
- return (EINVAL);
- error = in6_embedscope(&a6[1], &addrs[1], NULL, NULL);
- if (error)
- return (EINVAL);
}
+
INP_INFO_RLOCK(&tcbinfo);
if (mapped == 1)
inp = in_pcblookup_hash(&tcbinfo,
@@ -2191,12 +2190,11 @@ sysctl_drop(SYSCTL_HANDLER_ARGS)
lin = (struct sockaddr_in *)&addrs[1];
break;
}
- error = in6_embedscope(&f6, fin6, NULL, NULL);
+ error = sa6_embedscope(fin6, ip6_use_defzone);
if (error)
- return (EINVAL);
- error = in6_embedscope(&l6, lin6, NULL, NULL);
- if (error)
- return (EINVAL);
+ return (error);
+ error = sa6_embedscope(lin6, ip6_use_defzone);
+ return (error);
break;
#endif
case AF_INET:
diff --git a/sys/netinet/tcp_timewait.c b/sys/netinet/tcp_timewait.c
index 11d51e2..750c3b9 100644
--- a/sys/netinet/tcp_timewait.c
+++ b/sys/netinet/tcp_timewait.c
@@ -74,6 +74,7 @@
#include <netinet/ip_var.h>
#ifdef INET6
#include <netinet6/ip6_var.h>
+#include <netinet6/scope6_var.h>
#include <netinet6/nd6.h>
#endif
#include <netinet/ip_icmp.h>
@@ -1044,19 +1045,17 @@ tcp6_getcred(SYSCTL_HANDLER_ARGS)
error = SYSCTL_IN(req, addrs, sizeof(addrs));
if (error)
return (error);
+ if ((error = sa6_embedscope(&addrs[0], ip6_use_defzone)) != 0 ||
+ (error = sa6_embedscope(&addrs[1], ip6_use_defzone)) != 0) {
+ return (error);
+ }
if (IN6_IS_ADDR_V4MAPPED(&addrs[0].sin6_addr)) {
if (IN6_IS_ADDR_V4MAPPED(&addrs[1].sin6_addr))
mapped = 1;
else
return (EINVAL);
- } else {
- error = in6_embedscope(&a6[0], &addrs[0], NULL, NULL);
- if (error)
- return (EINVAL);
- error = in6_embedscope(&a6[1], &addrs[1], NULL, NULL);
- if (error)
- return (EINVAL);
}
+
INP_INFO_RLOCK(&tcbinfo);
if (mapped == 1)
inp = in_pcblookup_hash(&tcbinfo,
@@ -2191,12 +2190,11 @@ sysctl_drop(SYSCTL_HANDLER_ARGS)
lin = (struct sockaddr_in *)&addrs[1];
break;
}
- error = in6_embedscope(&f6, fin6, NULL, NULL);
+ error = sa6_embedscope(fin6, ip6_use_defzone);
if (error)
- return (EINVAL);
- error = in6_embedscope(&l6, lin6, NULL, NULL);
- if (error)
- return (EINVAL);
+ return (error);
+ error = sa6_embedscope(lin6, ip6_use_defzone);
+ return (error);
break;
#endif
case AF_INET:
diff --git a/sys/netinet/tcp_usrreq.c b/sys/netinet/tcp_usrreq.c
index 653ab6c..0ed4495 100644
--- a/sys/netinet/tcp_usrreq.c
+++ b/sys/netinet/tcp_usrreq.c
@@ -66,6 +66,7 @@
#include <netinet/ip_var.h>
#ifdef INET6
#include <netinet6/ip6_var.h>
+#include <netinet6/scope6_var.h>
#endif
#include <netinet/tcp.h>
#include <netinet/tcp_fsm.h>
@@ -925,6 +926,7 @@ tcp6_connect(tp, nam, td)
* Cannot simply call in_pcbconnect, because there might be an
* earlier incarnation of this same connection still in
* TIME_WAIT state, creating an ADDRINUSE error.
+ * in6_pcbladdr() also handles scope zone IDs.
*/
error = in6_pcbladdr(inp, nam, &addr6);
if (error)
diff --git a/sys/netinet6/ah_core.c b/sys/netinet6/ah_core.c
index cbc70ac..d24acb7 100644
--- a/sys/netinet6/ah_core.c
+++ b/sys/netinet6/ah_core.c
@@ -63,6 +63,7 @@
#ifdef INET6
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
+#include <netinet6/scope6_var.h>
#include <netinet/icmp6.h>
#endif
diff --git a/sys/netinet6/icmp6.c b/sys/netinet6/icmp6.c
index 3f3cbd6..129704e 100644
--- a/sys/netinet6/icmp6.c
+++ b/sys/netinet6/icmp6.c
@@ -95,6 +95,7 @@
#include <netinet6/in6_pcb.h>
#include <netinet6/ip6protosw.h>
#include <netinet6/ip6_var.h>
+#include <netinet6/scope6_var.h>
#include <netinet6/mld6_var.h>
#include <netinet6/nd6.h>
@@ -205,6 +206,41 @@ icmp6_errcount(stat, type, code)
}
/*
+ * A wrapper function for icmp6_error() necessary when the erroneous packet
+ * may not contain enough scope zone information.
+ */
+void
+icmp6_error2(m, type, code, param, ifp)
+ struct mbuf *m;
+ int type, code, param;
+ struct ifnet *ifp;
+{
+ struct ip6_hdr *ip6;
+
+ if (ifp == NULL)
+ return;
+
+#ifndef PULLDOWN_TEST
+ IP6_EXTHDR_CHECK(m, 0, sizeof(struct ip6_hdr), );
+#else
+ if (m->m_len < sizeof(struct ip6_hdr)) {
+ m = m_pullup(m, sizeof(struct ip6_hdr));
+ if (m == NULL)
+ return;
+ }
+#endif
+
+ ip6 = mtod(m, struct ip6_hdr *);
+
+ if (in6_setscope(&ip6->ip6_src, ifp, NULL) != 0)
+ return;
+ if (in6_setscope(&ip6->ip6_dst, ifp, NULL) != 0)
+ return;
+
+ icmp6_error(m, type, code, param);
+}
+
+/*
* Generate an error packet of type error in response to bad IP6 packet.
*/
void
@@ -1024,37 +1060,16 @@ icmp6_notify_error(mp, off, icmp6len, code)
icmp6dst.sin6_addr = eip6->ip6_dst;
else
icmp6dst.sin6_addr = *finaldst;
- if (in6_addr2zoneid(m->m_pkthdr.rcvif, &icmp6dst.sin6_addr,
- &icmp6dst.sin6_scope_id))
- goto freeit;
- if (in6_embedscope(&icmp6dst.sin6_addr, &icmp6dst,
- NULL, NULL)) {
- /* should be impossible */
- nd6log((LOG_DEBUG,
- "icmp6_notify_error: in6_embedscope failed\n"));
+ if (in6_setscope(&icmp6dst.sin6_addr, m->m_pkthdr.rcvif, NULL))
goto freeit;
- }
-
- /*
- * retrieve parameters from the inner IPv6 header, and convert
- * them into sockaddr structures.
- */
bzero(&icmp6src, sizeof(icmp6src));
icmp6src.sin6_len = sizeof(struct sockaddr_in6);
icmp6src.sin6_family = AF_INET6;
icmp6src.sin6_addr = eip6->ip6_src;
- if (in6_addr2zoneid(m->m_pkthdr.rcvif, &icmp6src.sin6_addr,
- &icmp6src.sin6_scope_id)) {
- goto freeit;
- }
- if (in6_embedscope(&icmp6src.sin6_addr, &icmp6src,
- NULL, NULL)) {
- /* should be impossible */
- nd6log((LOG_DEBUG,
- "icmp6_notify_error: in6_embedscope failed\n"));
+ if (in6_setscope(&icmp6src.sin6_addr, m->m_pkthdr.rcvif, NULL))
goto freeit;
- }
- icmp6src.sin6_flowinfo = (eip6->ip6_flow & IPV6_FLOWLABEL_MASK);
+ icmp6src.sin6_flowinfo =
+ (eip6->ip6_flow & IPV6_FLOWLABEL_MASK);
if (finaldst == NULL)
finaldst = &eip6->ip6_dst;
@@ -1124,11 +1139,8 @@ icmp6_mtudisc_update(ip6cp, validated)
bzero(&inc, sizeof(inc));
inc.inc_flags = 1; /* IPv6 */
inc.inc6_faddr = *dst;
- /* XXX normally, this won't happen */
- if (IN6_IS_ADDR_LINKLOCAL(dst)) {
- inc.inc6_faddr.s6_addr16[1] =
- htons(m->m_pkthdr.rcvif->if_index);
- }
+ if (in6_setscope(&inc.inc6_faddr, m->m_pkthdr.rcvif, NULL))
+ return;
if (mtu < tcp_maxmtu6(&inc)) {
tcp_hc_updatemtu(&inc, mtu);
@@ -1161,7 +1173,7 @@ ni6_input(m, off)
struct ni_reply_fqdn *fqdn;
int addrs; /* for NI_QTYPE_NODEADDR */
struct ifnet *ifp = NULL; /* for NI_QTYPE_NODEADDR */
- struct sockaddr_in6 sin6_sbj; /* subject address */
+ struct in6_addr in6_subj; /* subject address */
struct ip6_hdr *ip6;
int oldfqdn = 0; /* if 1, return pascal string (03 draft) */
char *subj = NULL;
@@ -1252,22 +1264,13 @@ ni6_input(m, off)
* We do not do proxy at this moment.
*/
/* m_pulldown instead of copy? */
- bzero(&sin6_sbj, sizeof(sin6_sbj));
- sin6_sbj.sin6_family = AF_INET6;
- sin6_sbj.sin6_len = sizeof(sin6_sbj);
m_copydata(m, off + sizeof(struct icmp6_nodeinfo),
- subjlen, (caddr_t)&sin6_sbj.sin6_addr);
- if (in6_addr2zoneid(m->m_pkthdr.rcvif,
- &sin6_sbj.sin6_addr, &sin6_sbj.sin6_scope_id)) {
+ subjlen, (caddr_t)&in6_subj);
+ if (in6_setscope(&in6_subj, m->m_pkthdr.rcvif, NULL))
goto bad;
- }
- if (in6_embedscope(&sin6_sbj.sin6_addr, &sin6_sbj,
- NULL, NULL))
- goto bad; /* XXX should not happen */
- subj = (char *)&sin6_sbj;
- if (IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst,
- &sin6_sbj.sin6_addr))
+ subj = (char *)&in6_subj;
+ if (IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &in6_subj))
break;
/*
@@ -1884,11 +1887,18 @@ icmp6_rip6_input(mp, off)
}
#endif
+ /*
+ * XXX: the address may have embedded scope zone ID, which should be
+ * hidden from applications.
+ */
bzero(&fromsa, sizeof(fromsa));
- fromsa.sin6_len = sizeof(struct sockaddr_in6);
fromsa.sin6_family = AF_INET6;
- /* KAME hack: recover scopeid */
- (void)in6_recoverscope(&fromsa, &ip6->ip6_src, m->m_pkthdr.rcvif);
+ fromsa.sin6_len = sizeof(struct sockaddr_in6);
+ fromsa.sin6_addr = ip6->ip6_src;
+ if (sa6_recoverscope(&fromsa)) {
+ m_freem(m);
+ return (IPPROTO_DONE);
+ }
INP_INFO_RLOCK(&ripcbinfo);
LIST_FOREACH(in6p, &ripcb, inp_list) {
@@ -2019,11 +2029,10 @@ icmp6_reflect(m, off)
struct ip6_hdr *ip6;
struct icmp6_hdr *icmp6;
struct in6_ifaddr *ia;
- struct in6_addr t, *src = 0;
int plen;
int type, code;
struct ifnet *outif = NULL;
- struct sockaddr_in6 sa6_src, sa6_dst;
+ struct in6_addr origdst, *src = NULL;
#ifdef COMPAT_RFC1885
int mtu = IPV6_MMTU;
struct sockaddr_in6 *sin6 = &icmp6_reflect_rt.ro_dst;
@@ -2074,35 +2083,13 @@ icmp6_reflect(m, off)
type = icmp6->icmp6_type; /* keep type for statistics */
code = icmp6->icmp6_code; /* ditto. */
- t = ip6->ip6_dst;
+ origdst = ip6->ip6_dst;
/*
* ip6_input() drops a packet if its src is multicast.
* So, the src is never multicast.
*/
ip6->ip6_dst = ip6->ip6_src;
- /*
- * XXX: make sure to embed scope zone information, using
- * already embedded IDs or the received interface (if any).
- * Note that rcvif may be NULL.
- * TODO: scoped routing case (XXX).
- */
- bzero(&sa6_src, sizeof(sa6_src));
- sa6_src.sin6_family = AF_INET6;
- sa6_src.sin6_len = sizeof(sa6_src);
- sa6_src.sin6_addr = ip6->ip6_dst;
- in6_recoverscope(&sa6_src, &ip6->ip6_dst, m->m_pkthdr.rcvif);
- if (in6_embedscope(&ip6->ip6_dst, &sa6_src, NULL, NULL))
- goto bad;
-
- bzero(&sa6_dst, sizeof(sa6_dst));
- sa6_dst.sin6_family = AF_INET6;
- sa6_dst.sin6_len = sizeof(sa6_dst);
- sa6_dst.sin6_addr = t;
- in6_recoverscope(&sa6_dst, &t, m->m_pkthdr.rcvif);
- if (in6_embedscope(&t, &sa6_dst, NULL, NULL))
- goto bad;
-
#ifdef COMPAT_RFC1885
/*
* xxx guess MTU
@@ -2136,29 +2123,42 @@ icmp6_reflect(m, off)
m_adj(m, mtu - m->m_pkthdr.len);
}
#endif
+
/*
* If the incoming packet was addressed directly to us (i.e. unicast),
* use dst as the src for the reply.
* The IN6_IFF_NOTREADY case should be VERY rare, but is possible
* (for example) when we encounter an error while forwarding procedure
* destined to a duplicated address of ours.
+ * Note that ip6_getdstifaddr() may fail if we are in an error handling
+ * procedure of an outgoing packet of our own, in which case we need
+ * to search in the ifaddr list.
*/
- for (ia = in6_ifaddr; ia; ia = ia->ia_next)
- if (IN6_ARE_ADDR_EQUAL(&t, &ia->ia_addr.sin6_addr) &&
- (ia->ia6_flags & (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY)) == 0) {
- src = &t;
- break;
+ if (!IN6_IS_ADDR_MULTICAST(&origdst)) {
+ if ((ia = ip6_getdstifaddr(m))) {
+ if (!(ia->ia6_flags &
+ (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY)))
+ src = &ia->ia_addr.sin6_addr;
+ } else {
+ struct sockaddr_in6 d;
+
+ bzero(&d, sizeof(d));
+ d.sin6_family = AF_INET6;
+ d.sin6_len = sizeof(d);
+ d.sin6_addr = origdst;
+ ia = (struct in6_ifaddr *)
+ ifa_ifwithaddr((struct sockaddr *)&d);
+ if (ia &&
+ !(ia->ia6_flags &
+ (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY))) {
+ src = &ia->ia_addr.sin6_addr;
+ }
}
- if (ia == NULL && IN6_IS_ADDR_LINKLOCAL(&t) && (m->m_flags & M_LOOP)) {
- /*
- * This is the case if the dst is our link-local address
- * and the sender is also ourselves.
- */
- src = &t;
}
- if (src == 0) {
+ if (src == NULL) {
int e;
+ struct sockaddr_in6 sin6;
struct route_in6 ro;
/*
@@ -2166,21 +2166,25 @@ icmp6_reflect(m, off)
* that we do not own. Select a source address based on the
* source address of the erroneous packet.
*/
+ bzero(&sin6, sizeof(sin6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_len = sizeof(sin6);
+ sin6.sin6_addr = ip6->ip6_dst; /* zone ID should be embedded */
+
bzero(&ro, sizeof(ro));
- src = in6_selectsrc(&sa6_src, NULL, NULL, &ro, NULL, &e);
+ src = in6_selectsrc(&sin6, NULL, NULL, &ro, NULL, &outif, &e);
if (ro.ro_rt)
RTFREE(ro.ro_rt); /* XXX: we could use this */
if (src == NULL) {
nd6log((LOG_DEBUG,
"icmp6_reflect: source can't be determined: "
"dst=%s, error=%d\n",
- ip6_sprintf(&sa6_src.sin6_addr), e));
+ ip6_sprintf(&sin6.sin6_addr), e));
goto bad;
}
}
ip6->ip6_src = *src;
-
ip6->ip6_flow = 0;
ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
ip6->ip6_vfc |= IPV6_VERSION;
@@ -2280,10 +2284,10 @@ icmp6_redirect_input(m, off)
redtgt6 = nd_rd->nd_rd_target;
reddst6 = nd_rd->nd_rd_dst;
- if (IN6_IS_ADDR_LINKLOCAL(&redtgt6))
- redtgt6.s6_addr16[1] = htons(ifp->if_index);
- if (IN6_IS_ADDR_LINKLOCAL(&reddst6))
- reddst6.s6_addr16[1] = htons(ifp->if_index);
+ if (in6_setscope(&redtgt6, m->m_pkthdr.rcvif, NULL) ||
+ in6_setscope(&reddst6, m->m_pkthdr.rcvif, NULL)) {
+ goto freeit;
+ }
/* validation */
if (!IN6_IS_ADDR_LINKLOCAL(&src6)) {
@@ -2477,9 +2481,6 @@ icmp6_redirect_output(m0, rt)
src_sa.sin6_family = AF_INET6;
src_sa.sin6_len = sizeof(src_sa);
src_sa.sin6_addr = sip6->ip6_src;
- /* we don't currently use sin6_scope_id, but eventually use it */
- if (in6_addr2zoneid(ifp, &sip6->ip6_src, &src_sa.sin6_scope_id))
- goto fail;
if (nd6_is_addr_neighbor(&src_sa, ifp) == 0)
goto fail;
if (IN6_IS_ADDR_MULTICAST(&sip6->ip6_dst))
diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c
index 1dc7516..a123a7c 100644
--- a/sys/netinet6/in6.c
+++ b/sys/netinet6/in6.c
@@ -330,7 +330,7 @@ in6_control(so, cmd, data, ifp, td)
struct in6_ifreq *ifr = (struct in6_ifreq *)data;
struct in6_ifaddr *ia = NULL;
struct in6_aliasreq *ifra = (struct in6_aliasreq *)data;
- int privileged;
+ int error, privileged;
privileged = 0;
if (td == NULL || !suser(td))
@@ -412,25 +412,15 @@ in6_control(so, cmd, data, ifp, td)
* Find address for this interface, if it exists.
*/
if (ifra->ifra_addr.sin6_family == AF_INET6) { /* XXX */
- struct sockaddr_in6 *sa6 =
- (struct sockaddr_in6 *)&ifra->ifra_addr;
-
- if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr)) {
- if (sa6->sin6_addr.s6_addr16[1] == 0) {
- /* link ID is not embedded by the user */
- sa6->sin6_addr.s6_addr16[1] =
- htons(ifp->if_index);
- } else if (sa6->sin6_addr.s6_addr16[1] !=
- htons(ifp->if_index)) {
- return (EINVAL); /* link ID contradicts */
- }
- if (sa6->sin6_scope_id) {
- if (sa6->sin6_scope_id !=
- (u_int32_t)ifp->if_index)
- return (EINVAL);
- sa6->sin6_scope_id = 0; /* XXX: good way? */
- }
- }
+ int error = 0;
+
+ if (ifra->ifra_addr.sin6_scope_id != 0)
+ error = sa6_embedscope(&ifra->ifra_addr, 0);
+ else
+ error = in6_setscope(&ifra->ifra_addr.sin6_addr,
+ ifp, NULL);
+ if (error != 0)
+ return (error);
ia = in6ifa_ifpwithaddr(ifp, &ifra->ifra_addr.sin6_addr);
}
@@ -507,6 +497,8 @@ in6_control(so, cmd, data, ifp, td)
case SIOCGIFADDR_IN6:
ifr->ifr_addr = ia->ia_addr;
+ if ((error = sa6_recoverscope(&ifr->ifr_addr)) != 0)
+ return (error);
break;
case SIOCGIFDSTADDR_IN6:
@@ -517,6 +509,8 @@ in6_control(so, cmd, data, ifp, td)
* an error?
*/
ifr->ifr_dstaddr = ia->ia_dstaddr;
+ if ((error = sa6_recoverscope(&ifr->ifr_dstaddr)) != 0)
+ return (error);
break;
case SIOCGIFNETMASK_IN6:
@@ -741,6 +735,7 @@ in6_update_ifa(ifp, ifra, ia)
struct in6_ifaddr *oia;
struct sockaddr_in6 dst6;
struct in6_addrlifetime *lt;
+ struct rtentry *rt;
/* Validate parameters */
if (ifp == NULL || ifra == NULL) /* this maybe redundant */
@@ -789,21 +784,22 @@ in6_update_ifa(ifp, ifra, ia)
dst6 = ifra->ifra_dstaddr;
if ((ifp->if_flags & (IFF_POINTOPOINT|IFF_LOOPBACK)) != 0 &&
(dst6.sin6_family == AF_INET6)) {
+ struct in6_addr in6_tmp;
u_int32_t zoneid;
- if ((error = in6_recoverscope(&dst6,
- &ifra->ifra_dstaddr.sin6_addr, ifp)) != 0)
- return (error);
- if (in6_addr2zoneid(ifp, &dst6.sin6_addr, &zoneid))
- return (EINVAL);
- if (dst6.sin6_scope_id == 0) /* user omit to specify the ID. */
+ in6_tmp = dst6.sin6_addr;
+ if (in6_setscope(&in6_tmp, ifp, &zoneid))
+ return (EINVAL); /* XXX: should be impossible */
+
+ if (dst6.sin6_scope_id != 0) {
+ if (dst6.sin6_scope_id != zoneid)
+ return (EINVAL);
+ } else /* user omit to specify the ID. */
dst6.sin6_scope_id = zoneid;
- else if (dst6.sin6_scope_id != zoneid)
- return (EINVAL); /* scope ID mismatch. */
- if ((error = in6_embedscope(&dst6.sin6_addr, &dst6, NULL, NULL))
- != 0)
- return (error);
- dst6.sin6_scope_id = 0; /* XXX */
+
+ /* convert into the internal form */
+ if (sa6_embedscope(&dst6, 0))
+ return (EINVAL); /* XXX: should be impossible */
}
/*
* The destination address can be specified only for a p2p or a
@@ -927,11 +923,60 @@ in6_update_ifa(ifp, ifra, ia)
ia->ia_dstaddr = dst6;
}
+ /*
+ * Set lifetimes. We do not refer to ia6t_expire and ia6t_preferred
+ * to see if the address is deprecated or invalidated, but initialize
+ * these members for applications.
+ */
+ ia->ia6_lifetime = ifra->ifra_lifetime;
+ if (ia->ia6_lifetime.ia6t_vltime != ND6_INFINITE_LIFETIME) {
+ ia->ia6_lifetime.ia6t_expire =
+ time_second + ia->ia6_lifetime.ia6t_vltime;
+ } else
+ ia->ia6_lifetime.ia6t_expire = 0;
+ if (ia->ia6_lifetime.ia6t_pltime != ND6_INFINITE_LIFETIME) {
+ ia->ia6_lifetime.ia6t_preferred =
+ time_second + ia->ia6_lifetime.ia6t_pltime;
+ } else
+ ia->ia6_lifetime.ia6t_preferred = 0;
+
/* reset the interface and routing table appropriately. */
if ((error = in6_ifinit(ifp, ia, &ifra->ifra_addr, hostIsNew)) != 0)
goto unlink;
/*
+ * configure address flags.
+ */
+ ia->ia6_flags = ifra->ifra_flags;
+ ia->ia6_flags &= ~IN6_IFF_DUPLICATED; /* safety */
+ ia->ia6_flags &= ~IN6_IFF_NODAD; /* Mobile IPv6 */
+ /*
+ * backward compatibility - if IN6_IFF_DEPRECATED is set from the
+ * userland, make it deprecated.
+ */
+ if ((ifra->ifra_flags & IN6_IFF_DEPRECATED) != 0) {
+ ia->ia6_lifetime.ia6t_pltime = 0;
+ ia->ia6_lifetime.ia6t_preferred = time_second;
+ }
+
+ /*
+ * Perform DAD, if needed.
+ * XXX It may be of use, if we can administratively
+ * disable DAD.
+ */
+ if (in6if_do_dad(ifp) && hostIsNew &&
+ (ifra->ifra_flags & IN6_IFF_NODAD) == 0) {
+ ia->ia6_flags |= IN6_IFF_TENTATIVE;
+ nd6_dad_start((struct ifaddr *)ia, NULL);
+ }
+
+ /*
+ * We are done if we have simply modified an existing address.
+ */
+ if (!hostIsNew)
+ return (error);
+
+ /*
* Beyond this point, we should call in6_purgeaddr upon an error,
* not just go to unlink.
*/
@@ -939,29 +984,29 @@ in6_update_ifa(ifp, ifra, ia)
if ((ifp->if_flags & IFF_MULTICAST) != 0) {
struct sockaddr_in6 mltaddr, mltmask;
struct in6_multi *in6m;
+ struct in6_addr llsol;
- if (hostIsNew) {
- /* join solicited multicast addr for new host id */
- struct in6_addr llsol;
-
- bzero(&llsol, sizeof(struct in6_addr));
- llsol.s6_addr16[0] = htons(0xff02);
- llsol.s6_addr16[1] = htons(ifp->if_index);
- llsol.s6_addr32[1] = 0;
- llsol.s6_addr32[2] = htonl(1);
- llsol.s6_addr32[3] =
- ifra->ifra_addr.sin6_addr.s6_addr32[3];
- llsol.s6_addr8[12] = 0xff;
- (void)in6_addmulti(&llsol, ifp, &error);
- if (error != 0) {
- nd6log((LOG_WARNING,
- "in6_update_ifa: addmulti failed for "
- "%s on %s (errno=%d)\n",
- ip6_sprintf(&llsol), if_name(ifp),
- error));
- in6_purgeaddr((struct ifaddr *)ia);
- return (error);
- }
+ /* join solicited multicast addr for new host id */
+ bzero(&llsol, sizeof(struct in6_addr));
+ llsol.s6_addr16[0] = htons(0xff02);
+ llsol.s6_addr32[1] = 0;
+ llsol.s6_addr32[2] = htonl(1);
+ llsol.s6_addr32[3] = ifra->ifra_addr.sin6_addr.s6_addr32[3];
+ llsol.s6_addr8[12] = 0xff;
+ if ((error = in6_setscope(&llsol, ifp, NULL)) != 0) {
+ /* XXX: should not happen */
+ log(LOG_ERR, "in6_update_ifa: "
+ "in6_setscope failed\n");
+ goto cleanup;
+ }
+ (void)in6_addmulti(&llsol, ifp, &error);
+ if (error != 0) {
+ nd6log((LOG_WARNING,
+ "in6_update_ifa: addmulti failed for "
+ "%s on %s (errno=%d)\n",
+ ip6_sprintf(&llsol), if_name(ifp),
+ error));
+ goto cleanup;
}
bzero(&mltmask, sizeof(mltmask));
@@ -976,16 +1021,41 @@ in6_update_ifa(ifp, ifra, ia)
mltaddr.sin6_len = sizeof(struct sockaddr_in6);
mltaddr.sin6_family = AF_INET6;
mltaddr.sin6_addr = in6addr_linklocal_allnodes;
- mltaddr.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
+ if ((error = in6_setscope(&mltaddr.sin6_addr, ifp, NULL)) !=
+ 0)
+ goto cleanup; /* XXX: should not fail */
+
+ /*
+ * XXX: do we really need this automatic routes?
+ * We should probably reconsider this stuff. Most applications
+ * actually do not need the routes, since they usually specify
+ * the outgoing interface.
+ */
+ rt = rtalloc1((struct sockaddr *)&mltaddr, 0, 0UL);
+ if (rt) {
+ /*
+ * 32bit came from "mltmask"
+ */
+ if (memcmp(&mltaddr.sin6_addr,
+ &((struct sockaddr_in6 *)rt_key(rt))->sin6_addr,
+ 32 / 8)) {
+ RTFREE_LOCKED(rt);
+ rt = NULL;
+ }
+ }
+ if (!rt) {
+ /* XXX: we need RTF_CLONING to fake nd6_rtrequest */
+ error = rtrequest(RTM_ADD, (struct sockaddr *)&mltaddr,
+ (struct sockaddr *)&ia->ia_addr,
+ (struct sockaddr *)&mltmask, RTF_UP | RTF_CLONING,
+ (struct rtentry **)0);
+ if (error)
+ goto cleanup;
+ } else
+ RTFREE_LOCKED(rt);
IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m);
if (in6m == NULL) {
- rtrequest(RTM_ADD,
- (struct sockaddr *)&mltaddr,
- (struct sockaddr *)&ia->ia_addr,
- (struct sockaddr *)&mltmask,
- RTF_UP|RTF_CLONING, /* xxx */
- (struct rtentry **)0);
(void)in6_addmulti(&mltaddr.sin6_addr, ifp, &error);
if (error != 0) {
nd6log((LOG_WARNING,
@@ -993,6 +1063,7 @@ in6_update_ifa(ifp, ifra, ia)
"%s on %s (errno=%d)\n",
ip6_sprintf(&mltaddr.sin6_addr),
if_name(ifp), error));
+ goto cleanup;
}
}
@@ -1003,7 +1074,7 @@ in6_update_ifa(ifp, ifra, ia)
if (in6_nigroup(ifp, hostname, hostnamelen, &mltaddr.sin6_addr)
== 0) {
IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m);
- if (in6m == NULL && ia != NULL) {
+ if (in6m == NULL) {
(void)in6_addmulti(&mltaddr.sin6_addr,
ifp, &error);
if (error != 0) {
@@ -1012,71 +1083,53 @@ in6_update_ifa(ifp, ifra, ia)
"%s on %s (errno=%d)\n",
ip6_sprintf(&mltaddr.sin6_addr),
if_name(ifp), error));
+ goto cleanup;
}
}
}
#undef hostnamelen
/*
- * join node-local all-nodes address, on loopback.
- * XXX: since "node-local" is obsoleted by interface-local,
- * we have to join the group on every interface with
- * some interface-boundary restriction.
+ * join interface-local all-nodes address.
+ * (ff01::1%ifN, and ff01::%ifN/32)
*/
- if (ifp->if_flags & IFF_LOOPBACK) {
- struct in6_ifaddr *ia_loop;
-
- struct in6_addr loop6 = in6addr_loopback;
- ia_loop = in6ifa_ifpwithaddr(ifp, &loop6);
-
- mltaddr.sin6_addr = in6addr_nodelocal_allnodes;
-
- IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m);
- if (in6m == NULL && ia_loop != NULL) {
- rtrequest(RTM_ADD,
- (struct sockaddr *)&mltaddr,
- (struct sockaddr *)&ia_loop->ia_addr,
- (struct sockaddr *)&mltmask,
- RTF_UP,
- (struct rtentry **)0);
- (void)in6_addmulti(&mltaddr.sin6_addr, ifp,
- &error);
- if (error != 0) {
- nd6log((LOG_WARNING, "in6_update_ifa: "
- "addmulti failed for %s on %s "
- "(errno=%d)\n",
- ip6_sprintf(&mltaddr.sin6_addr),
- if_name(ifp), error));
- }
+ mltaddr.sin6_addr = in6addr_nodelocal_allnodes;
+ if ((error = in6_setscope(&mltaddr.sin6_addr, ifp, NULL))
+ != 0)
+ goto cleanup; /* XXX: should not fail */
+ /* XXX: again, do we really need the route? */
+ rt = rtalloc1((struct sockaddr *)&mltaddr, 0, 0UL);
+ if (rt) {
+ /* 32bit came from "mltmask" */
+ if (memcmp(&mltaddr.sin6_addr,
+ &((struct sockaddr_in6 *)rt_key(rt))->sin6_addr,
+ 32 / 8)) {
+ RTFREE_LOCKED(rt);
+ rt = NULL;
}
}
- }
-
- ia->ia6_flags = ifra->ifra_flags;
- ia->ia6_flags &= ~IN6_IFF_DUPLICATED; /*safety*/
- ia->ia6_flags &= ~IN6_IFF_NODAD; /* Mobile IPv6 */
-
- ia->ia6_lifetime = ifra->ifra_lifetime;
- /* for sanity */
- if (ia->ia6_lifetime.ia6t_vltime != ND6_INFINITE_LIFETIME) {
- ia->ia6_lifetime.ia6t_expire =
- time_second + ia->ia6_lifetime.ia6t_vltime;
- } else
- ia->ia6_lifetime.ia6t_expire = 0;
- if (ia->ia6_lifetime.ia6t_pltime != ND6_INFINITE_LIFETIME) {
- ia->ia6_lifetime.ia6t_preferred =
- time_second + ia->ia6_lifetime.ia6t_pltime;
- } else
- ia->ia6_lifetime.ia6t_preferred = 0;
+ if (!rt) {
+ error = rtrequest(RTM_ADD, (struct sockaddr *)&mltaddr,
+ (struct sockaddr *)&ia->ia_addr,
+ (struct sockaddr *)&mltmask, RTF_UP | RTF_CLONING,
+ (struct rtentry **)0);
+ if (error)
+ goto cleanup;
+ } else
+ RTFREE_LOCKED(rt);
- /*
- * Perform DAD, if needed.
- * XXX It may be of use, if we can administratively
- * disable DAD.
- */
- if (in6if_do_dad(ifp) && (ifra->ifra_flags & IN6_IFF_NODAD) == 0) {
- ia->ia6_flags |= IN6_IFF_TENTATIVE;
- nd6_dad_start((struct ifaddr *)ia, NULL);
+ IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m);
+ if (in6m == NULL) {
+ (void)in6_addmulti(&mltaddr.sin6_addr, ifp, &error);
+ if (error != 0) {
+ nd6log((LOG_WARNING, "in6_update_ifa: "
+ "addmulti failed for %s on %s "
+ "(errno=%d)\n",
+ ip6_sprintf(&mltaddr.sin6_addr),
+ if_name(ifp), error));
+ goto cleanup;
+ }
+ }
}
return (error);
@@ -1089,6 +1142,10 @@ in6_update_ifa(ifp, ifra, ia)
if (hostIsNew)
in6_unlink_ifa(ia, ifp);
return (error);
+
+ cleanup:
+ in6_purgeaddr(&ia->ia_ifa);
+ return error;
}
void
@@ -1131,12 +1188,12 @@ in6_purgeaddr(ifa)
struct in6_addr llsol;
bzero(&llsol, sizeof(struct in6_addr));
llsol.s6_addr16[0] = htons(0xff02);
- llsol.s6_addr16[1] = htons(ifp->if_index);
llsol.s6_addr32[1] = 0;
llsol.s6_addr32[2] = htonl(1);
llsol.s6_addr32[3] =
ia->ia_addr.sin6_addr.s6_addr32[3];
llsol.s6_addr8[12] = 0xff;
+ (void)in6_setscope(&llsol, ifp, NULL); /* XXX proceed anyway */
IN6_LOOKUP_MULTI(llsol, ifp, in6m);
if (in6m)
@@ -1397,14 +1454,13 @@ in6_lifaddr_ioctl(so, cmd, data, ifp, td)
if (!cmp)
break;
- bcopy(IFA_IN6(ifa), &candidate, sizeof(candidate));
/*
* XXX: this is adhoc, but is necessary to allow
* a user to specify fe80::/64 (not /10) for a
* link-local address.
*/
- if (IN6_IS_ADDR_LINKLOCAL(&candidate))
- candidate.s6_addr16[1] = 0;
+ bcopy(IFA_IN6(ifa), &candidate, sizeof(candidate));
+ in6_clearscope(&candidate);
candidate.s6_addr32[0] &= mask.s6_addr32[0];
candidate.s6_addr32[1] &= mask.s6_addr32[1];
candidate.s6_addr32[2] &= mask.s6_addr32[2];
@@ -1417,27 +1473,22 @@ in6_lifaddr_ioctl(so, cmd, data, ifp, td)
ia = ifa2ia6(ifa);
if (cmd == SIOCGLIFADDR) {
- struct sockaddr_in6 *s6;
+ int error;
/* fill in the if_laddrreq structure */
bcopy(&ia->ia_addr, &iflr->addr, ia->ia_addr.sin6_len);
- s6 = (struct sockaddr_in6 *)&iflr->addr;
- if (IN6_IS_ADDR_LINKLOCAL(&s6->sin6_addr)) {
- s6->sin6_addr.s6_addr16[1] = 0;
- if (in6_addr2zoneid(ifp, &s6->sin6_addr,
- &s6->sin6_scope_id))
- return (EINVAL); /* XXX */
- }
+ error = sa6_recoverscope(
+ (struct sockaddr_in6 *)&iflr->addr);
+ if (error != 0)
+ return (error);
+
if ((ifp->if_flags & IFF_POINTOPOINT) != 0) {
bcopy(&ia->ia_dstaddr, &iflr->dstaddr,
ia->ia_dstaddr.sin6_len);
- s6 = (struct sockaddr_in6 *)&iflr->dstaddr;
- if (IN6_IS_ADDR_LINKLOCAL(&s6->sin6_addr)) {
- s6->sin6_addr.s6_addr16[1] = 0;
- if (in6_addr2zoneid(ifp,
- &s6->sin6_addr, &s6->sin6_scope_id))
- return (EINVAL); /* EINVAL */
- }
+ error = sa6_recoverscope(
+ (struct sockaddr_in6 *)&iflr->dstaddr);
+ if (error != 0)
+ return (error);
} else
bzero(&iflr->dstaddr, sizeof(iflr->dstaddr));
diff --git a/sys/netinet6/in6.h b/sys/netinet6/in6.h
index d0ebdae..05d0943 100644
--- a/sys/netinet6/in6.h
+++ b/sys/netinet6/in6.h
@@ -590,6 +590,7 @@ struct ip6_mtuinfo {
#define IPV6CTL_RIP6STATS 36 /* raw_ip6 stats */
#define IPV6CTL_PREFER_TEMPADDR 37 /* prefer temporary addr as src */
#define IPV6CTL_ADDRCTLPOLICY 38 /* get/set address selection policy */
+#define IPV6CTL_USE_DEFAULTZONE 39 /* use default scope zone */
#define IPV6CTL_MAXFRAGS 41 /* max fragments */
diff --git a/sys/netinet6/in6_cksum.c b/sys/netinet6/in6_cksum.c
index 50e14bf..1fd9451 100644
--- a/sys/netinet6/in6_cksum.c
+++ b/sys/netinet6/in6_cksum.c
@@ -66,6 +66,7 @@
#include <sys/systm.h>
#include <netinet/in.h>
#include <netinet/ip6.h>
+#include <netinet6/scope6_var.h>
#include <net/net_osdep.h>
@@ -97,6 +98,7 @@ in6_cksum(m, nxt, off, len)
int mlen = 0;
int byte_swapped = 0;
struct ip6_hdr *ip6;
+ struct in6_addr in6;
union {
u_int16_t phs[4];
struct {
@@ -126,22 +128,27 @@ in6_cksum(m, nxt, off, len)
* First create IP6 pseudo header and calculate a summary.
*/
ip6 = mtod(m, struct ip6_hdr *);
- w = (u_int16_t *)&ip6->ip6_src;
uph.ph.ph_len = htonl(len);
uph.ph.ph_nxt = nxt;
- /* IPv6 source address */
- sum += w[0];
- if (!IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src))
- sum += w[1];
- sum += w[2]; sum += w[3]; sum += w[4]; sum += w[5];
- sum += w[6]; sum += w[7];
+ /*
+ * IPv6 source address.
+ * XXX: we'd like to avoid copying the address, but we can't due to
+ * the possibly embedded scope zone ID.
+ */
+ in6 = ip6->ip6_src;
+ in6_clearscope(&in6);
+ w = (u_int16_t *)&in6;
+ sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
+ sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
+
/* IPv6 destination address */
- sum += w[8];
- if (!IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst))
- sum += w[9];
- sum += w[10]; sum += w[11]; sum += w[12]; sum += w[13];
- sum += w[14]; sum += w[15];
+ in6 = ip6->ip6_dst;
+ in6_clearscope(&in6);
+ w = (u_int16_t *)&in6;
+ sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
+ sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
+
/* Payload length and upper layer identifier */
sum += uph.phs[0]; sum += uph.phs[1];
sum += uph.phs[2]; sum += uph.phs[3];
diff --git a/sys/netinet6/in6_ifattach.c b/sys/netinet6/in6_ifattach.c
index 8837a91..7fb499a 100644
--- a/sys/netinet6/in6_ifattach.c
+++ b/sys/netinet6/in6_ifattach.c
@@ -436,8 +436,7 @@ in6_ifattach_linklocal(ifp, altifp)
ifra.ifra_addr.sin6_family = AF_INET6;
ifra.ifra_addr.sin6_len = sizeof(struct sockaddr_in6);
- ifra.ifra_addr.sin6_addr.s6_addr16[0] = htons(0xfe80);
- ifra.ifra_addr.sin6_addr.s6_addr16[1] = htons(ifp->if_index); /* XXX */
+ ifra.ifra_addr.sin6_addr.s6_addr32[0] = htonl(0xfe800000);
ifra.ifra_addr.sin6_addr.s6_addr32[1] = 0;
if ((ifp->if_flags & IFF_LOOPBACK) != 0) {
ifra.ifra_addr.sin6_addr.s6_addr32[2] = 0;
@@ -449,6 +448,8 @@ in6_ifattach_linklocal(ifp, altifp)
return (-1);
}
}
+ if (in6_setscope(&ifra.ifra_addr.sin6_addr, ifp, NULL))
+ return (-1);
ifra.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6);
ifra.ifra_prefixmask.sin6_family = AF_INET6;
@@ -643,10 +644,10 @@ in6_nigroup(ifp, name, namelen, in6)
bzero(in6, sizeof(*in6));
in6->s6_addr16[0] = htons(0xff02);
- if (ifp)
- in6->s6_addr16[1] = htons(ifp->if_index);
in6->s6_addr8[11] = 2;
bcopy(digest, &in6->s6_addr32[3], sizeof(in6->s6_addr32[3]));
+ if (in6_setscope(in6, ifp, NULL))
+ return (-1); /* XXX: should not fail */
return 0;
}
@@ -841,7 +842,9 @@ in6_ifdetach(ifp)
sin6.sin6_len = sizeof(struct sockaddr_in6);
sin6.sin6_family = AF_INET6;
sin6.sin6_addr = in6addr_linklocal_allnodes;
- sin6.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
+ if (in6_setscope(&sin6.sin6_addr, ifp, NULL))
+ /* XXX: should not fail */
+ return;
/* XXX grab lock first to avoid LOR */
if (rt_tables[AF_INET6] != NULL) {
RADIX_NODE_HEAD_LOCK(rt_tables[AF_INET6]);
diff --git a/sys/netinet6/in6_pcb.c b/sys/netinet6/in6_pcb.c
index 9aab2a0..5856512 100644
--- a/sys/netinet6/in6_pcb.c
+++ b/sys/netinet6/in6_pcb.c
@@ -96,6 +96,7 @@
#include <netinet6/nd6.h>
#include <netinet/in_pcb.h>
#include <netinet6/in6_pcb.h>
+#include <netinet6/scope6_var.h>
#ifdef IPSEC
#include <netinet6/ipsec.h>
@@ -139,6 +140,8 @@ in6_pcbbind(inp, nam, cred)
if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0)
wild = 1;
if (nam) {
+ int error;
+
sin6 = (struct sockaddr_in6 *)nam;
if (nam->sa_len != sizeof(*sin6))
return (EINVAL);
@@ -148,11 +151,8 @@ in6_pcbbind(inp, nam, cred)
if (nam->sa_family != AF_INET6)
return (EAFNOSUPPORT);
- /* KAME hack: embed scopeid */
- if (in6_embedscope(&sin6->sin6_addr, sin6, inp, NULL) != 0)
- return EINVAL;
- /* this must be cleared for ifa_ifwithaddr() */
- sin6->sin6_scope_id = 0;
+ if ((error = sa6_embedscope(sin6, ip6_use_defzone)) != 0)
+ return(error);
lport = sin6->sin6_port;
if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) {
@@ -292,8 +292,9 @@ in6_pcbladdr(inp, nam, plocal_addr6)
struct in6_addr **plocal_addr6;
{
register struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)nam;
- struct ifnet *ifp = NULL;
int error = 0;
+ struct ifnet *ifp = NULL;
+ int scope_ambiguous = 0;
if (nam->sa_len != sizeof (*sin6))
return (EINVAL);
@@ -302,13 +303,14 @@ in6_pcbladdr(inp, nam, plocal_addr6)
if (sin6->sin6_port == 0)
return (EADDRNOTAVAIL);
+ if (sin6->sin6_scope_id == 0 && !ip6_use_defzone)
+ scope_ambiguous = 1;
+ if ((error = sa6_embedscope(sin6, ip6_use_defzone)) != 0)
+ return(error);
+
INP_INFO_WLOCK_ASSERT(inp->inp_pcbinfo);
INP_LOCK_ASSERT(inp);
- /* KAME hack: embed scopeid */
- if (in6_embedscope(&sin6->sin6_addr, sin6, inp, &ifp) != 0)
- return EINVAL;
-
if (in6_ifaddr) {
/*
* If the destination address is UNSPECIFIED addr,
@@ -317,26 +319,31 @@ in6_pcbladdr(inp, nam, plocal_addr6)
if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr))
sin6->sin6_addr = in6addr_loopback;
}
- {
- /*
- * XXX: in6_selectsrc might replace the bound local address
- * with the address specified by setsockopt(IPV6_PKTINFO).
- * Is it the intended behavior?
- */
- *plocal_addr6 = in6_selectsrc(sin6, inp->in6p_outputopts,
- inp->in6p_moptions, NULL,
- &inp->in6p_laddr, &error);
- if (*plocal_addr6 == 0) {
- if (error == 0)
- error = EADDRNOTAVAIL;
- return (error);
- }
- /*
- * Don't do pcblookup call here; return interface in
- * plocal_addr6
- * and exit to caller, that will do the lookup.
- */
+
+ /*
+ * XXX: in6_selectsrc might replace the bound local address
+ * with the address specified by setsockopt(IPV6_PKTINFO).
+ * Is it the intended behavior?
+ */
+ *plocal_addr6 = in6_selectsrc(sin6, inp->in6p_outputopts,
+ inp->in6p_moptions, NULL,
+ &inp->in6p_laddr, &ifp, &error);
+ if (ifp && scope_ambiguous &&
+ (error = in6_setscope(&sin6->sin6_addr, ifp, NULL)) != 0) {
+ return(error);
+ }
+
+ if (*plocal_addr6 == 0) {
+ if (error == 0)
+ error = EADDRNOTAVAIL;
+ return (error);
}
+ /*
+ * Don't do pcblookup call here; return interface in
+ * plocal_addr6
+ * and exit to caller, that will do the lookup.
+ */
+
return (0);
}
@@ -466,12 +473,7 @@ in6_sockaddr(port, addr_p)
sin6->sin6_len = sizeof(*sin6);
sin6->sin6_port = port;
sin6->sin6_addr = *addr_p;
- if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr))
- sin6->sin6_scope_id = ntohs(sin6->sin6_addr.s6_addr16[1]);
- else
- sin6->sin6_scope_id = 0; /*XXX*/
- if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr))
- sin6->sin6_addr.s6_addr16[1] = 0;
+ (void)sa6_recoverscope(sin6); /* XXX: should catch errors */
return (struct sockaddr *)sin6;
}
@@ -953,11 +955,8 @@ init_sin6(struct sockaddr_in6 *sin6, struct mbuf *m)
sin6->sin6_len = sizeof(*sin6);
sin6->sin6_family = AF_INET6;
sin6->sin6_addr = ip->ip6_src;
- if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr))
- sin6->sin6_addr.s6_addr16[1] = 0;
- sin6->sin6_scope_id =
- (m->m_pkthdr.rcvif && IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr))
- ? m->m_pkthdr.rcvif->if_index : 0;
+
+ (void)sa6_recoverscope(sin6); /* XXX: should catch errors... */
return;
}
diff --git a/sys/netinet6/in6_proto.c b/sys/netinet6/in6_proto.c
index 7900303..796de8d 100644
--- a/sys/netinet6/in6_proto.c
+++ b/sys/netinet6/in6_proto.c
@@ -442,6 +442,8 @@ SYSCTL_STRUCT(_net_inet6_ip6, IPV6CTL_RIP6STATS, rip6stats, CTLFLAG_RD,
&rip6stat, rip6stat, "");
SYSCTL_INT(_net_inet6_ip6, IPV6CTL_PREFER_TEMPADDR,
prefer_tempaddr, CTLFLAG_RW, &ip6_prefer_tempaddr, 0, "");
+SYSCTL_INT(_net_inet6_ip6, IPV6CTL_USE_DEFAULTZONE,
+ use_defaultzone, CTLFLAG_RW, &ip6_use_defzone, 0,"");
SYSCTL_INT(_net_inet6_ip6, IPV6CTL_MAXFRAGS,
maxfrags, CTLFLAG_RW, &ip6_maxfrags, 0, "");
diff --git a/sys/netinet6/in6_src.c b/sys/netinet6/in6_src.c
index 812b03d..78e18cd 100644
--- a/sys/netinet6/in6_src.c
+++ b/sys/netinet6/in6_src.c
@@ -89,6 +89,7 @@
#include <netinet/ip6.h>
#include <netinet6/in6_pcb.h>
#include <netinet6/ip6_var.h>
+#include <netinet6/scope6_var.h>
#include <netinet6/nd6.h>
#ifdef ENABLE_DEFAULT_SCOPE
#include <netinet6/scope6_var.h>
@@ -107,6 +108,9 @@ struct in6_addrpolicy defaultaddrpolicy;
int ip6_prefer_tempaddr = 0;
+static int selectroute __P((struct sockaddr_in6 *, struct ip6_pktopts *,
+ struct ip6_moptions *, struct route_in6 *, struct ifnet **,
+ struct rtentry **, int, int));
static int in6_selectif __P((struct sockaddr_in6 *, struct ip6_pktopts *,
struct ip6_moptions *, struct route_in6 *ro, struct ifnet **));
@@ -148,15 +152,16 @@ static struct in6_addrpolicy *match_addrsel_policy __P((struct sockaddr_in6 *));
} while(0)
struct in6_addr *
-in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp)
+in6_selectsrc(dstsock, opts, mopts, ro, laddr, ifpp, errorp)
struct sockaddr_in6 *dstsock;
struct ip6_pktopts *opts;
struct ip6_moptions *mopts;
struct route_in6 *ro;
struct in6_addr *laddr;
+ struct ifnet **ifpp;
int *errorp;
{
- struct in6_addr *dst;
+ struct in6_addr dst;
struct ifnet *ifp = NULL;
struct in6_ifaddr *ia = NULL, *ia_best = NULL;
struct in6_pktinfo *pi = NULL;
@@ -164,30 +169,11 @@ in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp)
struct in6_addrpolicy *dst_policy = NULL, *best_policy = NULL;
u_int32_t odstzone;
int prefer_tempaddr;
- struct sockaddr_in6 dstsock0;
- dstsock0 = *dstsock;
- if (IN6_IS_SCOPE_LINKLOCAL(&dstsock0.sin6_addr) ||
- IN6_IS_ADDR_MC_INTFACELOCAL(&dstsock0.sin6_addr)) {
- /* KAME assumption: link id == interface id */
- if (opts && opts->ip6po_pktinfo &&
- opts->ip6po_pktinfo->ipi6_ifindex) {
- ifp = ifnet_byindex(opts->ip6po_pktinfo->ipi6_ifindex);
- dstsock0.sin6_addr.s6_addr16[1] =
- htons(opts->ip6po_pktinfo->ipi6_ifindex);
- } else if (mopts &&
- IN6_IS_ADDR_MULTICAST(&dstsock0.sin6_addr) &&
- mopts->im6o_multicast_ifp) {
- ifp = mopts->im6o_multicast_ifp;
- dstsock0.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
- } else if ((*errorp = in6_embedscope(&dstsock0.sin6_addr,
- &dstsock0, NULL, NULL)) != 0)
- return (NULL);
- }
- dstsock = &dstsock0;
-
- dst = &dstsock->sin6_addr;
+ dst = dstsock->sin6_addr; /* make a copy for local operation */
*errorp = 0;
+ if (ifpp)
+ *ifpp = NULL;
/*
* If the source address is explicitly specified by the caller,
@@ -209,23 +195,20 @@ in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp)
/*
* determine the appropriate zone id of the source based on
* the zone of the destination and the outgoing interface.
+ * If the specified address is ambiguous wrt the scope zone,
+ * the interface must be specified; otherwise, ifa_ifwithaddr()
+ * will fail matching the address.
*/
bzero(&srcsock, sizeof(srcsock));
srcsock.sin6_family = AF_INET6;
srcsock.sin6_len = sizeof(srcsock);
srcsock.sin6_addr = pi->ipi6_addr;
if (ifp) {
- if (in6_addr2zoneid(ifp, &pi->ipi6_addr,
- &srcsock.sin6_scope_id)) {
- *errorp = EINVAL; /* XXX */
+ *errorp = in6_setscope(&srcsock.sin6_addr, ifp, NULL);
+ if (*errorp != 0)
return (NULL);
- }
- }
- if ((*errorp = in6_embedscope(&srcsock.sin6_addr, &srcsock,
- NULL, NULL)) != 0) {
- return (NULL);
}
- srcsock.sin6_scope_id = 0; /* XXX: ifa_ifwithaddr expects 0 */
+
ia6 = (struct in6_ifaddr *)ifa_ifwithaddr((struct sockaddr *)(&srcsock));
if (ia6 == NULL ||
(ia6->ia6_flags & (IN6_IFF_ANYCAST | IN6_IFF_NOTREADY))) {
@@ -233,6 +216,8 @@ in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp)
return (NULL);
}
pi->ipi6_addr = srcsock.sin6_addr; /* XXX: this overrides pi */
+ if (ifpp)
+ *ifpp = ifp;
return (&ia6->ia_addr.sin6_addr);
}
@@ -254,14 +239,15 @@ in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp)
if (ifp == NULL) /* this should not happen */
panic("in6_selectsrc: NULL ifp");
#endif
- if (in6_addr2zoneid(ifp, dst, &odstzone)) { /* impossible */
- *errorp = EIO; /* XXX */
+ *errorp = in6_setscope(&dst, ifp, &odstzone);
+ if (*errorp != 0)
return (NULL);
- }
+
for (ia = in6_ifaddr; ia; ia = ia->ia_next) {
int new_scope = -1, new_matchlen = -1;
struct in6_addrpolicy *new_policy = NULL;
u_int32_t srczone, osrczone, dstzone;
+ struct in6_addr src;
struct ifnet *ifp1 = ia->ia_ifp;
/*
@@ -270,12 +256,13 @@ in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp)
* does not contain the outgoing interface.
* XXX: we should probably use sin6_scope_id here.
*/
- if (in6_addr2zoneid(ifp1, dst, &dstzone) ||
+ if (in6_setscope(&dst, ifp1, &dstzone) ||
odstzone != dstzone) {
continue;
}
- if (in6_addr2zoneid(ifp, &ia->ia_addr.sin6_addr, &osrczone) ||
- in6_addr2zoneid(ifp1, &ia->ia_addr.sin6_addr, &srczone) ||
+ src = ia->ia_addr.sin6_addr;
+ if (in6_setscope(&src, ifp, &osrczone) ||
+ in6_setscope(&src, ifp1, &srczone) ||
osrczone != srczone) {
continue;
}
@@ -289,7 +276,7 @@ in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp)
continue;
/* Rule 1: Prefer same address */
- if (IN6_ARE_ADDR_EQUAL(dst, &ia->ia_addr.sin6_addr)) {
+ if (IN6_ARE_ADDR_EQUAL(&dst, &ia->ia_addr.sin6_addr)) {
ia_best = ia;
BREAK(1); /* there should be no better candidate */
}
@@ -299,7 +286,7 @@ in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp)
/* Rule 2: Prefer appropriate scope */
if (dst_scope < 0)
- dst_scope = in6_addrscope(dst);
+ dst_scope = in6_addrscope(&dst);
new_scope = in6_addrscope(&ia->ia_addr.sin6_addr);
if (IN6_ARE_SCOPE_CMP(best_scope, new_scope) < 0) {
if (IN6_ARE_SCOPE_CMP(best_scope, dst_scope) < 0)
@@ -396,7 +383,7 @@ in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp)
* a large number so that it is easy to assign smaller numbers
* to more preferred rules.
*/
- new_matchlen = in6_matchlen(&ia->ia_addr.sin6_addr, dst);
+ new_matchlen = in6_matchlen(&ia->ia_addr.sin6_addr, &dst);
if (best_matchlen < new_matchlen)
REPLACE(14);
if (new_matchlen < best_matchlen)
@@ -418,7 +405,7 @@ in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp)
lookup_addrsel_policy(&ia_best->ia_addr));
best_matchlen = (new_matchlen >= 0 ? new_matchlen :
in6_matchlen(&ia_best->ia_addr.sin6_addr,
- dst));
+ &dst));
next:
continue;
@@ -432,75 +419,14 @@ in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp)
return (NULL);
}
+ if (ifpp)
+ *ifpp = ifp;
+
return (&ia->ia_addr.sin6_addr);
}
static int
-in6_selectif(dstsock, opts, mopts, ro, retifp)
- struct sockaddr_in6 *dstsock;
- struct ip6_pktopts *opts;
- struct ip6_moptions *mopts;
- struct route_in6 *ro;
- struct ifnet **retifp;
-{
- int error;
- struct route_in6 sro;
- struct rtentry *rt = NULL;
-
- if (ro == NULL) {
- bzero(&sro, sizeof(sro));
- ro = &sro;
- }
-
- if ((error = in6_selectroute(dstsock, opts, mopts, ro, retifp,
- &rt, 0)) != 0) {
- if (rt && rt == sro.ro_rt)
- RTFREE(rt);
- return (error);
- }
-
- /*
- * do not use a rejected or black hole route.
- * XXX: this check should be done in the L2 output routine.
- * However, if we skipped this check here, we'd see the following
- * scenario:
- * - install a rejected route for a scoped address prefix
- * (like fe80::/10)
- * - send a packet to a destination that matches the scoped prefix,
- * with ambiguity about the scope zone.
- * - pick the outgoing interface from the route, and disambiguate the
- * scope zone with the interface.
- * - ip6_output() would try to get another route with the "new"
- * destination, which may be valid.
- * - we'd see no error on output.
- * Although this may not be very harmful, it should still be confusing.
- * We thus reject the case here.
- */
- if (rt && (rt->rt_flags & (RTF_REJECT | RTF_BLACKHOLE))) {
- int flags = (rt->rt_flags & RTF_HOST ? EHOSTUNREACH : ENETUNREACH);
-
- if (rt && rt == sro.ro_rt)
- RTFREE(rt);
- return (flags);
- }
-
- /*
- * Adjust the "outgoing" interface. If we're going to loop the packet
- * back to ourselves, the ifp would be the loopback interface.
- * However, we'd rather know the interface associated to the
- * destination address (which should probably be one of our own
- * addresses.)
- */
- if (rt && rt->rt_ifa && rt->rt_ifa->ifa_ifp)
- *retifp = rt->rt_ifa->ifa_ifp;
-
- if (rt && rt == sro.ro_rt)
- RTFREE(rt);
- return (0);
-}
-
-int
-in6_selectroute(dstsock, opts, mopts, ro, retifp, retrt, clone)
+selectroute(dstsock, opts, mopts, ro, retifp, retrt, clone, norouteok)
struct sockaddr_in6 *dstsock;
struct ip6_pktopts *opts;
struct ip6_moptions *mopts;
@@ -508,6 +434,7 @@ in6_selectroute(dstsock, opts, mopts, ro, retifp, retrt, clone)
struct ifnet **retifp;
struct rtentry **retrt;
int clone; /* meaningful only for bsdi and freebsd. */
+ int norouteok;
{
int error = 0;
struct ifnet *ifp = NULL;
@@ -534,7 +461,8 @@ in6_selectroute(dstsock, opts, mopts, ro, retifp, retrt, clone)
/* XXX boundary check is assumed to be already done. */
ifp = ifnet_byindex(pi->ipi6_ifindex);
if (ifp != NULL &&
- (retrt == NULL || IN6_IS_ADDR_MULTICAST(dst))) {
+ (norouteok || retrt == NULL ||
+ IN6_IS_ADDR_MULTICAST(dst))) {
/*
* we do not have to check nor get the route for
* multicast.
@@ -697,6 +625,84 @@ in6_selectroute(dstsock, opts, mopts, ro, retifp, retrt, clone)
return (error);
}
+static int
+in6_selectif(dstsock, opts, mopts, ro, retifp)
+ struct sockaddr_in6 *dstsock;
+ struct ip6_pktopts *opts;
+ struct ip6_moptions *mopts;
+ struct route_in6 *ro;
+ struct ifnet **retifp;
+{
+ int error;
+ struct route_in6 sro;
+ struct rtentry *rt = NULL;
+
+ if (ro == NULL) {
+ bzero(&sro, sizeof(sro));
+ ro = &sro;
+ }
+
+ if ((error = selectroute(dstsock, opts, mopts, ro, retifp,
+ &rt, 0, 1)) != 0) {
+ if (rt && rt == sro.ro_rt)
+ RTFREE(rt);
+ return (error);
+ }
+
+ /*
+ * do not use a rejected or black hole route.
+ * XXX: this check should be done in the L2 output routine.
+ * However, if we skipped this check here, we'd see the following
+ * scenario:
+ * - install a rejected route for a scoped address prefix
+ * (like fe80::/10)
+ * - send a packet to a destination that matches the scoped prefix,
+ * with ambiguity about the scope zone.
+ * - pick the outgoing interface from the route, and disambiguate the
+ * scope zone with the interface.
+ * - ip6_output() would try to get another route with the "new"
+ * destination, which may be valid.
+ * - we'd see no error on output.
+ * Although this may not be very harmful, it should still be confusing.
+ * We thus reject the case here.
+ */
+ if (rt && (rt->rt_flags & (RTF_REJECT | RTF_BLACKHOLE))) {
+ int flags = (rt->rt_flags & RTF_HOST ? EHOSTUNREACH : ENETUNREACH);
+
+ if (rt && rt == sro.ro_rt)
+ RTFREE(rt);
+ return (flags);
+ }
+
+ /*
+ * Adjust the "outgoing" interface. If we're going to loop the packet
+ * back to ourselves, the ifp would be the loopback interface.
+ * However, we'd rather know the interface associated to the
+ * destination address (which should probably be one of our own
+ * addresses.)
+ */
+ if (rt && rt->rt_ifa && rt->rt_ifa->ifa_ifp)
+ *retifp = rt->rt_ifa->ifa_ifp;
+
+ if (rt && rt == sro.ro_rt)
+ RTFREE(rt);
+ return (0);
+}
+
+int
+in6_selectroute(dstsock, opts, mopts, ro, retifp, retrt, clone)
+ struct sockaddr_in6 *dstsock;
+ struct ip6_pktopts *opts;
+ struct ip6_moptions *mopts;
+ struct route_in6 *ro;
+ struct ifnet **retifp;
+ struct rtentry **retrt;
+ int clone; /* meaningful only for bsdi and freebsd. */
+{
+ return (selectroute(dstsock, opts, mopts, ro, retifp,
+ retrt, clone, 0));
+}
+
/*
* Default hop limit selection. The precedence is as follows:
* 1. Hoplimit value specified via ioctl.
@@ -830,128 +836,6 @@ in6_pcbsetport(laddr, inp, cred)
return (0);
}
-/*
- * Generate kernel-internal form (scopeid embedded into s6_addr16[1]).
- * If the address scope of is link-local, embed the interface index in the
- * address. The routine determines our precedence
- * between advanced API scope/interface specification and basic API
- * specification.
- *
- * This function should be nuked in the future, when we get rid of embedded
- * scopeid thing.
- *
- * XXX actually, it is over-specification to return ifp against sin6_scope_id.
- * there can be multiple interfaces that belong to a particular scope zone
- * (in specification, we have 1:N mapping between a scope zone and interfaces).
- * we may want to change the function to return something other than ifp.
- */
-int
-in6_embedscope(in6, sin6, in6p, ifpp)
- struct in6_addr *in6;
- const struct sockaddr_in6 *sin6;
- struct in6pcb *in6p;
- struct ifnet **ifpp;
-{
- struct ifnet *ifp = NULL;
- u_int32_t zoneid = sin6->sin6_scope_id;
-
- *in6 = sin6->sin6_addr;
- if (ifpp)
- *ifpp = NULL;
-
- /*
- * don't try to read sin6->sin6_addr beyond here, since the caller may
- * ask us to overwrite existing sockaddr_in6
- */
-
-#ifdef ENABLE_DEFAULT_SCOPE
- if (zoneid == 0)
- zoneid = scope6_addr2default(in6);
-#endif
-
- if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6)) {
- struct in6_pktinfo *pi;
-
- /* KAME assumption: link id == interface id */
- if (in6p && in6p->in6p_outputopts &&
- (pi = in6p->in6p_outputopts->ip6po_pktinfo) &&
- pi->ipi6_ifindex) {
- ifp = ifnet_byindex(pi->ipi6_ifindex);
- in6->s6_addr16[1] = htons(pi->ipi6_ifindex);
- } else if (in6p && IN6_IS_ADDR_MULTICAST(in6) &&
- in6p->in6p_moptions &&
- in6p->in6p_moptions->im6o_multicast_ifp) {
- ifp = in6p->in6p_moptions->im6o_multicast_ifp;
- in6->s6_addr16[1] = htons(ifp->if_index);
- } else if (zoneid) {
- if (if_index < zoneid)
- return (ENXIO); /* XXX EINVAL? */
- ifp = ifnet_byindex(zoneid);
-
- /* XXX assignment to 16bit from 32bit variable */
- in6->s6_addr16[1] = htons(zoneid & 0xffff);
- }
-
- if (ifpp)
- *ifpp = ifp;
- }
-
- return 0;
-}
-
-/*
- * generate standard sockaddr_in6 from embedded form.
- * touches sin6_addr and sin6_scope_id only.
- *
- * this function should be nuked in the future, when we get rid of
- * embedded scopeid thing.
- */
-int
-in6_recoverscope(sin6, in6, ifp)
- struct sockaddr_in6 *sin6;
- const struct in6_addr *in6;
- struct ifnet *ifp;
-{
- u_int32_t zoneid;
-
- sin6->sin6_addr = *in6;
-
- /*
- * don't try to read *in6 beyond here, since the caller may
- * ask us to overwrite existing sockaddr_in6
- */
-
- sin6->sin6_scope_id = 0;
- if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6)) {
- /*
- * KAME assumption: link id == interface id
- */
- zoneid = ntohs(sin6->sin6_addr.s6_addr16[1]);
- if (zoneid) {
- /* sanity check */
- if (zoneid < 0 || if_index < zoneid)
- return ENXIO;
- if (ifp && ifp->if_index != zoneid)
- return ENXIO;
- sin6->sin6_addr.s6_addr16[1] = 0;
- sin6->sin6_scope_id = zoneid;
- }
- }
-
- return 0;
-}
-
-/*
- * just clear the embedded scope identifier.
- */
-void
-in6_clearscope(addr)
- struct in6_addr *addr;
-{
- if (IN6_IS_SCOPE_LINKLOCAL(addr) || IN6_IS_ADDR_MC_INTFACELOCAL(addr))
- addr->s6_addr16[1] = 0;
-}
-
void
addrsel_policy_init()
{
diff --git a/sys/netinet6/in6_var.h b/sys/netinet6/in6_var.h
index 05ec3d5..6e2be4f 100644
--- a/sys/netinet6/in6_var.h
+++ b/sys/netinet6/in6_var.h
@@ -611,11 +611,6 @@ void in6_ifaddloop(struct ifaddr *);
int in6_is_addr_deprecated __P((struct sockaddr_in6 *));
struct inpcb;
-int in6_embedscope __P((struct in6_addr *, const struct sockaddr_in6 *,
- struct inpcb *, struct ifnet **));
-int in6_recoverscope __P((struct sockaddr_in6 *, const struct in6_addr *,
- struct ifnet *));
-void in6_clearscope __P((struct in6_addr *));
int in6_src_ioctl __P((u_long, caddr_t));
#endif /* _KERNEL */
diff --git a/sys/netinet6/ip6_forward.c b/sys/netinet6/ip6_forward.c
index f0b1cf0..e003452 100644
--- a/sys/netinet6/ip6_forward.c
+++ b/sys/netinet6/ip6_forward.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 <netinet/icmp6.h>
#include <netinet6/nd6.h>
@@ -111,7 +112,8 @@ ip6_forward(m, srcrt)
int error, type = 0, code = 0;
struct mbuf *mcopy = NULL;
struct ifnet *origifp; /* maybe unnecessary */
- u_int32_t srczone, dstzone;
+ u_int32_t inzone, outzone;
+ struct in6_addr src_in6, dst_in6;
#ifdef IPSEC
struct secpolicy *sp = NULL;
int ipsecrt = 0;
@@ -400,21 +402,29 @@ ip6_forward(m, srcrt)
#endif
/*
- * Scope check: if a packet can't be delivered to its destination
- * for the reason that the destination is beyond the scope of the
- * source address, discard the packet and return an icmp6 destination
- * unreachable error with Code 2 (beyond scope of source address).
- * [draft-ietf-ipngwg-icmp-v3-02.txt, Section 3.1]
+ * Source scope check: if a packet can't be delivered to its
+ * destination for the reason that the destination is beyond the scope
+ * of the source address, discard the packet and return an icmp6
+ * destination unreachable error with Code 2 (beyond scope of source
+ * address). We use a local copy of ip6_src, since in6_setscope()
+ * will possibly modify its first argument.
+ * [draft-ietf-ipngwg-icmp-v3-04.txt, Section 3.1]
*/
- if (in6_addr2zoneid(m->m_pkthdr.rcvif, &ip6->ip6_src, &srczone) ||
- in6_addr2zoneid(rt->rt_ifp, &ip6->ip6_src, &dstzone)) {
+ src_in6 = ip6->ip6_src;
+ if (in6_setscope(&src_in6, rt->rt_ifp, &outzone)) {
/* XXX: this should not happen */
ip6stat.ip6s_cantforward++;
ip6stat.ip6s_badscope++;
m_freem(m);
return;
}
- if (srczone != dstzone
+ if (in6_setscope(&src_in6, m->m_pkthdr.rcvif, &inzone)) {
+ ip6stat.ip6s_cantforward++;
+ ip6stat.ip6s_badscope++;
+ m_freem(m);
+ return;
+ }
+ if (inzone != outzone
#ifdef IPSEC
&& !ipsecrt
#endif
@@ -440,6 +450,23 @@ ip6_forward(m, srcrt)
return;
}
+ /*
+ * Destination scope check: if a packet is going to break the scope
+ * zone of packet's destination address, discard it. This case should
+ * usually be prevented by appropriately-configured routing table, but
+ * we need an explicit check because we may mistakenly forward the
+ * packet to a different zone by (e.g.) a default route.
+ */
+ dst_in6 = ip6->ip6_dst;
+ if (in6_setscope(&dst_in6, m->m_pkthdr.rcvif, &inzone) != 0 ||
+ in6_setscope(&dst_in6, rt->rt_ifp, &outzone) != 0 ||
+ inzone != outzone) {
+ ip6stat.ip6s_cantforward++;
+ ip6stat.ip6s_badscope++;
+ m_freem(m);
+ return;
+ }
+
if (m->m_pkthdr.len > IN6_LINKMTU(rt->rt_ifp)) {
in6_ifstat_inc(rt->rt_ifp, ifs6_in_toobig);
if (mcopy) {
diff --git a/sys/netinet6/ip6_input.c b/sys/netinet6/ip6_input.c
index d23a447..9c5ff4a 100644
--- a/sys/netinet6/ip6_input.c
+++ b/sys/netinet6/ip6_input.c
@@ -236,8 +236,6 @@ ip6_input(m)
u_int32_t rtalert = ~0;
int nxt, ours = 0;
struct ifnet *deliverifp = NULL;
- struct sockaddr_in6 sa6;
- u_int32_t srczone, dstzone;
struct in6_addr odst;
int srcrt = 0;
@@ -401,23 +399,23 @@ ip6_input(m)
#endif
/*
- * Drop packets if the link ID portion is already filled.
- * XXX: this is technically not a good behavior. But, we internally
- * use the field to disambiguate link-local addresses, so we cannot
- * be generous against those a bit strange addresses.
+ * Disambiguate address scope zones (if there is ambiguity).
+ * We first make sure that the original source or destination address
+ * is not in our internal form for scoped addresses. Such addresses
+ * are not necessarily invalid spec-wise, but we cannot accept them due
+ * to the usage conflict.
+ * in6_setscope() then also checks and rejects the cases where src or
+ * dst are the loopback address and the receiving interface
+ * is not loopback.
*/
- if ((m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) == 0) {
- if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src) &&
- ip6->ip6_src.s6_addr16[1]) {
- ip6stat.ip6s_badscope++;
- goto bad;
- }
- if ((IN6_IS_ADDR_MC_INTFACELOCAL(&ip6->ip6_dst) ||
- IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) &&
- ip6->ip6_dst.s6_addr16[1]) {
- ip6stat.ip6s_badscope++;
- goto bad;
- }
+ if (in6_clearscope(&ip6->ip6_src) || in6_clearscope(&ip6->ip6_dst)) {
+ ip6stat.ip6s_badscope++; /* XXX */
+ goto bad;
+ }
+ if (in6_setscope(&ip6->ip6_src, m->m_pkthdr.rcvif, NULL) ||
+ in6_setscope(&ip6->ip6_dst, m->m_pkthdr.rcvif, NULL)) {
+ ip6stat.ip6s_badscope++;
+ goto bad;
}
/*
@@ -457,49 +455,6 @@ passin:
}
/*
- * construct source and destination address structures with
- * disambiguating their scope zones (if there is ambiguity).
- * XXX: sin6_family and sin6_len will NOT be referred to, but we fill
- * in these fields just in case.
- */
- if (in6_addr2zoneid(m->m_pkthdr.rcvif, &ip6->ip6_src, &srczone) ||
- in6_addr2zoneid(m->m_pkthdr.rcvif, &ip6->ip6_dst, &dstzone)) {
- /*
- * Note that these generic checks cover cases that src or
- * dst are the loopback address and the receiving interface
- * is not loopback.
- */
- ip6stat.ip6s_badscope++;
- goto bad;
- }
-
- bzero(&sa6, sizeof(sa6));
- sa6.sin6_family = AF_INET6;
- sa6.sin6_len = sizeof(struct sockaddr_in6);
-
- sa6.sin6_addr = ip6->ip6_src;
- sa6.sin6_scope_id = srczone;
- if (in6_embedscope(&ip6->ip6_src, &sa6, NULL, NULL)) {
- /* XXX: should not happen */
- ip6stat.ip6s_badscope++;
- goto bad;
- }
-
- sa6.sin6_addr = ip6->ip6_dst;
- sa6.sin6_scope_id = dstzone;
- if (in6_embedscope(&ip6->ip6_dst, &sa6, NULL, NULL)) {
- /* XXX: should not happen */
- ip6stat.ip6s_badscope++;
- goto bad;
- }
-
- /* XXX: ff01::%ifN awareness is not merged, yet. */
- if (IN6_IS_ADDR_MC_INTFACELOCAL(&ip6->ip6_src))
- ip6->ip6_src.s6_addr16[1] = 0;
- if (IN6_IS_ADDR_MC_INTFACELOCAL(&ip6->ip6_dst))
- ip6->ip6_dst.s6_addr16[1] = 0;
-
- /*
* Multicast check
*/
if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
@@ -1363,7 +1318,8 @@ ip6_notify_pmtu(in6p, dst, mtu)
bzero(&mtuctl, sizeof(mtuctl)); /* zero-clear for safety */
mtuctl.ip6m_mtu = *mtu;
mtuctl.ip6m_addr = *dst;
- in6_recoverscope(&mtuctl.ip6m_addr, &mtuctl.ip6m_addr.sin6_addr, NULL);
+ if (sa6_recoverscope(&mtuctl.ip6m_addr))
+ return;
if ((m_mtu = sbcreatecontrol((caddr_t)&mtuctl, sizeof(mtuctl),
IPV6_PATHMTU, IPPROTO_IPV6)) == NULL)
diff --git a/sys/netinet6/ip6_mroute.c b/sys/netinet6/ip6_mroute.c
index 64ce311..e0d86de 100644
--- a/sys/netinet6/ip6_mroute.c
+++ b/sys/netinet6/ip6_mroute.c
@@ -109,6 +109,7 @@
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
+#include <netinet6/scope6_var.h>
#include <netinet6/nd6.h>
#include <netinet6/ip6_mroute.h>
#include <netinet6/pim6.h>
@@ -1290,7 +1291,9 @@ ip6_mdq(m, ifp, rt)
mifi_t mifi, iif;
struct mif6 *mifp;
int plen = m->m_pkthdr.len;
- u_int32_t dscopein, sscopein;
+ struct in6_addr src0, dst0; /* copies for local work */
+ u_int32_t iszone, idzone, oszone, odzone;
+ int error = 0;
/*
* Macro to send packet on mif. Since RSVP packets don't get counted on
@@ -1422,13 +1425,15 @@ ip6_mdq(m, ifp, rt)
* For each mif, forward a copy of the packet if there are group
* members downstream on the interface.
*/
- if (in6_addr2zoneid(ifp, &ip6->ip6_dst, &dscopein) ||
- in6_addr2zoneid(ifp, &ip6->ip6_src, &sscopein))
- return (EINVAL);
+ src0 = ip6->ip6_src;
+ dst0 = ip6->ip6_dst;
+ if ((error = in6_setscope(&src0, ifp, &iszone)) != 0 ||
+ (error = in6_setscope(&dst0, ifp, &idzone)) != 0) {
+ ip6stat.ip6s_badscope++;
+ return (error);
+ }
for (mifp = mif6table, mifi = 0; mifi < nummifs; mifp++, mifi++) {
if (IF_ISSET(mifi, &rt->mf6c_ifset)) {
- u_int32_t dscopeout, sscopeout;
-
/*
* check if the outgoing packet is going to break
* a scope boundary.
@@ -1438,14 +1443,12 @@ ip6_mdq(m, ifp, rt)
if (!(mif6table[rt->mf6c_parent].m6_flags &
MIFF_REGISTER) &&
!(mif6table[mifi].m6_flags & MIFF_REGISTER)) {
- if (in6_addr2zoneid(mif6table[mifi].m6_ifp,
- &ip6->ip6_dst,
- &dscopeout) ||
- in6_addr2zoneid(mif6table[mifi].m6_ifp,
- &ip6->ip6_src,
- &sscopeout) ||
- dscopein != dscopeout ||
- sscopein != sscopeout) {
+ if (in6_setscope(&src0, mif6table[mifi].m6_ifp,
+ &oszone) ||
+ in6_setscope(&dst0, mif6table[mifi].m6_ifp,
+ &odzone) ||
+ iszone != oszone ||
+ idzone != odzone) {
ip6stat.ip6s_badscope++;
continue;
}
diff --git a/sys/netinet6/ip6_output.c b/sys/netinet6/ip6_output.c
index bee369e..e2c9361 100644
--- a/sys/netinet6/ip6_output.c
+++ b/sys/netinet6/ip6_output.c
@@ -111,6 +111,7 @@
#include <net/net_osdep.h>
#include <netinet6/ip6protosw.h>
+#include <netinet6/scope6_var.h>
static MALLOC_DEFINE(M_IPMOPTS, "ip6_moptions", "internet multicast options");
@@ -168,7 +169,8 @@ ip6_output(m0, opt, ro, flags, im6o, ifpp, inp)
struct mbuf *m = m0;
int hlen, tlen, len, off;
struct route_in6 ip6route;
- struct sockaddr_in6 *dst;
+ struct rtentry *rt = NULL;
+ struct sockaddr_in6 *dst, src_sa, dst_sa;
struct in6_addr odst;
int error = 0;
struct in6_ifaddr *ia = NULL;
@@ -176,7 +178,8 @@ ip6_output(m0, opt, ro, flags, im6o, ifpp, inp)
int alwaysfrag, dontfrag;
u_int32_t optlen = 0, plen = 0, unfragpartlen = 0;
struct ip6_exthdrs exthdrs;
- struct in6_addr finaldst;
+ struct in6_addr finaldst, src0, dst0;
+ u_int32_t zone;
struct route_in6 *ro_pmtu = NULL;
int hdrsplit = 0;
int needipsec = 0;
@@ -481,18 +484,38 @@ skip_ipsec2:;
(struct ip6_rthdr *)(mtod(exthdrs.ip6e_rthdr,
struct ip6_rthdr *));
struct ip6_rthdr0 *rh0;
- struct in6_addr *addrs;
+ struct in6_addr *addr;
+ struct sockaddr_in6 sa;
switch (rh->ip6r_type) {
case IPV6_RTHDR_TYPE_0:
rh0 = (struct ip6_rthdr0 *)rh;
- addrs = (struct in6_addr *)(rh0 + 1);
-
- ip6->ip6_dst = *addrs;
- bcopy((caddr_t)(addrs + 1), (caddr_t)addrs,
- sizeof(struct in6_addr)*(rh0->ip6r0_segleft - 1)
- );
- *(addrs + rh0->ip6r0_segleft - 1) = finaldst;
+ addr = (struct in6_addr *)(rh0 + 1);
+
+ /*
+ * construct a sockaddr_in6 form of
+ * the first hop.
+ *
+ * XXX: we may not have enough
+ * information about its scope zone;
+ * there is no standard API to pass
+ * the information from the
+ * application.
+ */
+ bzero(&sa, sizeof(sa));
+ sa.sin6_family = AF_INET6;
+ sa.sin6_len = sizeof(sa);
+ sa.sin6_addr = addr[0];
+ if ((error = sa6_embedscope(&sa,
+ ip6_use_defzone)) != 0) {
+ goto bad;
+ }
+ ip6->ip6_dst = sa.sin6_addr;
+ bcopy(&addr[1], &addr[0], sizeof(struct in6_addr)
+ * (rh0->ip6r0_segleft - 1));
+ addr[rh0->ip6r0_segleft - 1] = finaldst;
+ /* XXX */
+ in6_clearscope(addr + rh0->ip6r0_segleft - 1);
break;
default: /* is it possible? */
error = EINVAL;
@@ -528,24 +551,6 @@ skip_ipsec2:;
dst = (struct sockaddr_in6 *)&ro->ro_dst;
again:
- /*
- * If there is a cached route,
- * check that it is to the same destination
- * and is still up. If not, free it and try again.
- */
- if (ro->ro_rt && ((ro->ro_rt->rt_flags & RTF_UP) == 0 ||
- dst->sin6_family != AF_INET6 ||
- !IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &ip6->ip6_dst))) {
- RTFREE(ro->ro_rt);
- ro->ro_rt = (struct rtentry *)0;
- }
- if (ro->ro_rt == 0) {
- bzero(dst, sizeof(*dst));
- dst->sin6_family = AF_INET6;
- dst->sin6_len = sizeof(struct sockaddr_in6);
- dst->sin6_addr = ip6->ip6_dst;
- }
-
/*
* if specified, try to fill in the traffic class field.
* do not override if a non-zero value is already set.
@@ -623,140 +628,116 @@ again:
}
#endif /* IPSEC */
- if (!IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
- /* Unicast */
-
-#define ifatoia6(ifa) ((struct in6_ifaddr *)(ifa))
-#define sin6tosa(sin6) ((struct sockaddr *)(sin6))
- /* xxx
- * interface selection comes here
- * if an interface is specified from an upper layer,
- * ifp must point it.
- */
- if (ro->ro_rt == 0) {
- /*
- * non-bsdi always clone routes, if parent is
- * PRF_CLONING.
- */
- rtalloc((struct route *)ro);
- }
- if (ro->ro_rt == 0) {
+ /* adjust pointer */
+ ip6 = mtod(m, struct ip6_hdr *);
+
+ 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;
+ if ((error = in6_selectroute(&dst_sa, opt, im6o, ro,
+ &ifp, &rt, 0)) != 0) {
+ switch (error) {
+ case EHOSTUNREACH:
ip6stat.ip6s_noroute++;
- error = EHOSTUNREACH;
- /* XXX in6_ifstat_inc(ifp, ifs6_out_discard); */
- goto bad;
+ break;
+ case EADDRNOTAVAIL:
+ default:
+ break; /* XXX statistics? */
}
- /* XXX rt not locked */
- ia = ifatoia6(ro->ro_rt->rt_ifa);
- ifp = ro->ro_rt->rt_ifp;
- ro->ro_rt->rt_rmx.rmx_pksent++;
- if (ro->ro_rt->rt_flags & RTF_GATEWAY)
- dst = (struct sockaddr_in6 *)ro->ro_rt->rt_gateway;
- m->m_flags &= ~(M_BCAST | M_MCAST); /* just in case */
-
- in6_ifstat_inc(ifp, ifs6_out_request);
-
+ if (ifp != NULL)
+ in6_ifstat_inc(ifp, ifs6_out_discard);
+ goto bad;
+ }
+ if (rt == NULL) {
/*
- * Check if the outgoing interface conflicts with
- * the interface specified by ifi6_ifindex (if specified).
- * Note that loopback interface is always okay.
- * (this may happen when we are sending a packet to one of
- * our own addresses.)
+ * If in6_selectroute() does not return a route entry,
+ * dst may not have been updated.
*/
- if (opt && opt->ip6po_pktinfo
- && opt->ip6po_pktinfo->ipi6_ifindex) {
- if (!(ifp->if_flags & IFF_LOOPBACK)
- && ifp->if_index != opt->ip6po_pktinfo->ipi6_ifindex) {
- ip6stat.ip6s_noroute++;
- in6_ifstat_inc(ifp, ifs6_out_discard);
- error = EHOSTUNREACH;
- goto bad;
- }
- }
-
- if (opt && opt->ip6po_hlim != -1)
- ip6->ip6_hlim = opt->ip6po_hlim & 0xff;
- } else {
- /* Multicast */
- struct in6_multi *in6m;
+ *dst = dst_sa; /* XXX */
+ }
- m->m_flags = (m->m_flags & ~M_BCAST) | M_MCAST;
+ /*
+ * then rt (for unicast) and ifp must be non-NULL valid values.
+ */
+ if ((flags & IPV6_FORWARDING) == 0) {
+ /* XXX: the FORWARDING flag can be set for mrouting. */
+ in6_ifstat_inc(ifp, ifs6_out_request);
+ }
+ if (rt != NULL) {
+ ia = (struct in6_ifaddr *)(rt->rt_ifa);
+ rt->rt_use++;
+ }
- /*
- * See if the caller provided any multicast options
- */
- ifp = NULL;
- if (im6o != NULL) {
- ip6->ip6_hlim = im6o->im6o_multicast_hlim;
- if (im6o->im6o_multicast_ifp != NULL)
- ifp = im6o->im6o_multicast_ifp;
- } else
- ip6->ip6_hlim = ip6_defmcasthlim;
+ /*
+ * The outgoing interface must be in the zone of source and
+ * destination addresses. We should use ia_ifp to support the
+ * case of sending packets to an address of our own.
+ */
+ if (ia != NULL && ia->ia_ifp)
+ origifp = ia->ia_ifp;
+ else
+ origifp = ifp;
- /*
- * See if the caller provided the outgoing interface
- * as an ancillary data.
- * Boundary check for ifindex is assumed to be already done.
- */
- if (opt && opt->ip6po_pktinfo && opt->ip6po_pktinfo->ipi6_ifindex)
- ifp = ifnet_byindex(opt->ip6po_pktinfo->ipi6_ifindex);
+ src0 = ip6->ip6_src;
+ if (in6_setscope(&src0, origifp, &zone))
+ goto badscope;
+ bzero(&src_sa, sizeof(src_sa));
+ src_sa.sin6_family = AF_INET6;
+ src_sa.sin6_len = sizeof(src_sa);
+ src_sa.sin6_addr = ip6->ip6_src;
+ if (sa6_recoverscope(&src_sa) || zone != src_sa.sin6_scope_id)
+ goto badscope;
+
+ dst0 = ip6->ip6_dst;
+ if (in6_setscope(&dst0, origifp, &zone))
+ goto badscope;
+ /* re-initialize to be sure */
+ 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;
+ if (sa6_recoverscope(&dst_sa) || zone != dst_sa.sin6_scope_id) {
+ goto badscope;
+ }
+
+ /* scope check is done. */
+ goto routefound;
+
+ badscope:
+ ip6stat.ip6s_badscope++;
+ in6_ifstat_inc(origifp, ifs6_out_discard);
+ if (error == 0)
+ error = EHOSTUNREACH; /* XXX */
+ goto bad;
- /*
- * If the destination is a node-local scope multicast,
- * the packet should be loop-backed only.
- */
- if (IN6_IS_ADDR_MC_INTFACELOCAL(&ip6->ip6_dst)) {
+ routefound:
+ if (rt && !IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
+ if (opt && opt->ip6po_nextroute.ro_rt) {
/*
- * If the outgoing interface is already specified,
- * it should be a loopback interface.
+ * The nexthop is explicitly specified by the
+ * application. We assume the next hop is an IPv6
+ * address.
*/
- if (ifp && (ifp->if_flags & IFF_LOOPBACK) == 0) {
- ip6stat.ip6s_badscope++;
- error = ENETUNREACH; /* XXX: better error? */
- /* XXX correct ifp? */
- in6_ifstat_inc(ifp, ifs6_out_discard);
- goto bad;
- } else {
- ifp = &loif[0];
- }
+ dst = (struct sockaddr_in6 *)opt->ip6po_nexthop;
}
+ else if ((rt->rt_flags & RTF_GATEWAY))
+ dst = (struct sockaddr_in6 *)rt->rt_gateway;
+ }
- if (opt && opt->ip6po_hlim != -1)
- ip6->ip6_hlim = opt->ip6po_hlim & 0xff;
+ if (!IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
+ m->m_flags &= ~(M_BCAST | M_MCAST); /* just in case */
+ } else {
+ struct in6_multi *in6m;
- /*
- * If caller did not provide an interface lookup a
- * default in the routing table. This is either a
- * default for the speicfied group (i.e. a host
- * route), or a multicast default (a route for the
- * ``net'' ff00::/8).
- */
- if (ifp == NULL) {
- if (ro->ro_rt == 0)
- ro->ro_rt = rtalloc1((struct sockaddr *)
- &ro->ro_dst, 0, 0UL);
- else
- RT_LOCK(ro->ro_rt);
- if (ro->ro_rt == 0) {
- ip6stat.ip6s_noroute++;
- error = EHOSTUNREACH;
- /* XXX in6_ifstat_inc(ifp, ifs6_out_discard) */
- goto bad;
- }
- ia = ifatoia6(ro->ro_rt->rt_ifa);
- ifp = ro->ro_rt->rt_ifp;
- ro->ro_rt->rt_rmx.rmx_pksent++;
- RT_UNLOCK(ro->ro_rt);
- }
+ m->m_flags = (m->m_flags & ~M_BCAST) | M_MCAST;
- if ((flags & IPV6_FORWARDING) == 0)
- in6_ifstat_inc(ifp, ifs6_out_request);
in6_ifstat_inc(ifp, ifs6_out_mcast);
/*
* Confirm that the outgoing interface supports multicast.
*/
- if ((ifp->if_flags & IFF_MULTICAST) == 0) {
+ if (!(ifp->if_flags & IFF_MULTICAST)) {
ip6stat.ip6s_noroute++;
in6_ifstat_inc(ifp, ifs6_out_discard);
error = ENETUNREACH;
@@ -785,6 +766,14 @@ again:
* if necessary.
*/
if (ip6_mrouter && (flags & IPV6_FORWARDING) == 0) {
+ /*
+ * XXX: ip6_mforward expects that rcvif is NULL
+ * when it is called from the originating path.
+ * However, it is not always the case, since
+ * some versions of MGETHDR() does not
+ * initialize the field.
+ */
+ m->m_pkthdr.rcvif = NULL;
if (ip6_mforward(ip6, ifp, m) != 0) {
m_freem(m);
goto done;
@@ -842,44 +831,6 @@ again:
}
}
- /* Fake scoped addresses */
- if ((ifp->if_flags & IFF_LOOPBACK) != 0) {
- /*
- * If source or destination address is a scoped address, and
- * the packet is going to be sent to a loopback interface,
- * we should keep the original interface.
- */
-
- /*
- * XXX: this is a very experimental and temporary solution.
- * We eventually have sockaddr_in6 and use the sin6_scope_id
- * field of the structure here.
- * We rely on the consistency between two scope zone ids
- * of source and destination, which should already be assured.
- * Larger scopes than link will be supported in the future.
- */
- origifp = NULL;
- if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src))
- origifp = ifnet_byindex(ntohs(ip6->ip6_src.s6_addr16[1]));
- else if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst))
- origifp = ifnet_byindex(ntohs(ip6->ip6_dst.s6_addr16[1]));
- /*
- * XXX: origifp can be NULL even in those two cases above.
- * For example, if we remove the (only) link-local address
- * from the loopback interface, and try to send a link-local
- * address without link-id information. Then the source
- * address is ::1, and the destination address is the
- * link-local address with its s6_addr16[1] being zero.
- * What is worse, if the packet goes to the loopback interface
- * by a default rejected route, the null pointer would be
- * passed to looutput, and the kernel would hang.
- * The following last resort would prevent such disaster.
- */
- if (origifp == NULL)
- origifp = ifp;
- }
- else
- origifp = ifp;
/*
* clear embedded scope identifiers if necessary.
* in6_clearscope will touch the addresses only when necessary.
@@ -2661,7 +2612,6 @@ ip6_setmoptions(optname, im6op, m)
struct ifnet *ifp;
struct ip6_moptions *im6o = *im6op;
struct route_in6 ro;
- struct sockaddr_in6 *dst;
struct in6_multi_mship *imm;
struct thread *td = curthread;
@@ -2752,6 +2702,7 @@ ip6_setmoptions(optname, im6op, m)
break;
}
mreq = mtod(m, struct ipv6_mreq *);
+
if (IN6_IS_ADDR_UNSPECIFIED(&mreq->ipv6mr_multiaddr)) {
/*
* We use the unspecified address to specify to accept
@@ -2768,44 +2719,45 @@ ip6_setmoptions(optname, im6op, m)
}
/*
- * If the interface is specified, validate it.
- */
- if (mreq->ipv6mr_interface < 0 ||
- if_index < mreq->ipv6mr_interface) {
- error = ENXIO; /* XXX EINVAL? */
- break;
- }
- /*
* If no interface was explicitly specified, choose an
* appropriate one according to the given multicast address.
*/
if (mreq->ipv6mr_interface == 0) {
+ struct sockaddr_in6 *dst;
+
/*
- * If the multicast address is in node-local scope,
- * the interface should be a loopback interface.
- * Otherwise, look up the routing table for the
+ * Look up the routing table for the
* address, and choose the outgoing interface.
* XXX: is it a good approach?
*/
- if (IN6_IS_ADDR_MC_INTFACELOCAL(&mreq->ipv6mr_multiaddr)) {
- ifp = &loif[0];
- } else {
- ro.ro_rt = NULL;
- dst = (struct sockaddr_in6 *)&ro.ro_dst;
- bzero(dst, sizeof(*dst));
- dst->sin6_len = sizeof(struct sockaddr_in6);
- dst->sin6_family = AF_INET6;
- dst->sin6_addr = mreq->ipv6mr_multiaddr;
- rtalloc((struct route *)&ro);
- if (ro.ro_rt == NULL) {
- error = EADDRNOTAVAIL;
- break;
- }
- ifp = ro.ro_rt->rt_ifp;
- RTFREE(ro.ro_rt);
+ ro.ro_rt = NULL;
+ dst = (struct sockaddr_in6 *)&ro.ro_dst;
+ bzero(dst, sizeof(*dst));
+ dst->sin6_family = AF_INET6;
+ dst->sin6_len = sizeof(*dst);
+ dst->sin6_addr = mreq->ipv6mr_multiaddr;
+ rtalloc((struct route *)&ro);
+ if (ro.ro_rt == NULL) {
+ error = EADDRNOTAVAIL;
+ break;
+ }
+ ifp = ro.ro_rt->rt_ifp;
+ RTFREE(ro.ro_rt);
+ } else {
+ /*
+ * If the interface is specified, validate it.
+ */
+ if (mreq->ipv6mr_interface < 0 ||
+ if_index < mreq->ipv6mr_interface) {
+ error = ENXIO; /* XXX EINVAL? */
+ break;
}
- } else
ifp = ifnet_byindex(mreq->ipv6mr_interface);
+ if (!ifp) {
+ error = ENXIO; /* XXX EINVAL? */
+ break;
+ }
+ }
/*
* See if we found an interface, and confirm that it
@@ -2815,14 +2767,12 @@ ip6_setmoptions(optname, im6op, m)
error = EADDRNOTAVAIL;
break;
}
- /*
- * Put interface index into the multicast address,
- * if the address has link-local scope.
- */
- if (IN6_IS_ADDR_MC_LINKLOCAL(&mreq->ipv6mr_multiaddr)) {
- mreq->ipv6mr_multiaddr.s6_addr16[1] =
- htons(ifp->if_index);
+
+ if (in6_setscope(&mreq->ipv6mr_multiaddr, ifp, NULL)) {
+ error = EADDRNOTAVAIL; /* XXX: should not happen */
+ break;
}
+
/*
* See if the membership already exists.
*/
@@ -2863,32 +2813,57 @@ ip6_setmoptions(optname, im6op, m)
break;
}
mreq = mtod(m, struct ipv6_mreq *);
- if (IN6_IS_ADDR_UNSPECIFIED(&mreq->ipv6mr_multiaddr)) {
- if (suser(td)) {
- error = EACCES;
- break;
- }
- } else if (!IN6_IS_ADDR_MULTICAST(&mreq->ipv6mr_multiaddr)) {
- error = EINVAL;
- break;
- }
+
/*
* If an interface address was specified, get a pointer
* to its ifnet structure.
*/
- if (mreq->ipv6mr_interface < 0
- || if_index < mreq->ipv6mr_interface) {
+ if (mreq->ipv6mr_interface < 0 ||
+ if_index < mreq->ipv6mr_interface) {
error = ENXIO; /* XXX EINVAL? */
break;
}
- ifp = ifnet_byindex(mreq->ipv6mr_interface);
- /*
- * Put interface index into the multicast address,
- * if the address has link-local scope.
- */
- if (IN6_IS_ADDR_MC_LINKLOCAL(&mreq->ipv6mr_multiaddr)) {
- mreq->ipv6mr_multiaddr.s6_addr16[1]
- = htons(mreq->ipv6mr_interface);
+ if (mreq->ipv6mr_interface == 0)
+ ifp = NULL;
+ else
+ ifp = ifnet_byindex(mreq->ipv6mr_interface);
+
+ /* Fill in the scope zone ID */
+ if (ifp) {
+ if (in6_setscope(&mreq->ipv6mr_multiaddr, ifp, NULL)) {
+ /* XXX: should not happen */
+ error = EADDRNOTAVAIL;
+ break;
+ }
+ } else if (mreq->ipv6mr_interface != 0) {
+ /*
+ * This case happens when the (positive) index is in
+ * the valid range, but the corresponding interface has
+ * been detached dynamically (XXX).
+ */
+ error = EADDRNOTAVAIL;
+ break;
+ } else { /* ipv6mr_interface == 0 */
+ struct sockaddr_in6 sa6_mc;
+
+ /*
+ * The API spec says as follows:
+ * If the interface index is specified as 0, the
+ * system may choose a multicast group membership to
+ * drop by matching the multicast address only.
+ * On the other hand, we cannot disambiguate the scope
+ * zone unless an interface is provided. Thus, we
+ * check if there's ambiguity with the default scope
+ * zone as the last resort.
+ */
+ bzero(&sa6_mc, sizeof(sa6_mc));
+ sa6_mc.sin6_family = AF_INET6;
+ sa6_mc.sin6_len = sizeof(sa6_mc);
+ sa6_mc.sin6_addr = mreq->ipv6mr_multiaddr;
+ error = sa6_embedscope(&sa6_mc, ip6_use_defzone);
+ if (error != 0)
+ break;
+ mreq->ipv6mr_multiaddr = sa6_mc.sin6_addr;
}
/*
@@ -3235,9 +3210,7 @@ ip6_setpktopt(optname, buf, len, opt, priv, sticky, cmsg, uproto)
case AF_INET6:
{
struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)buf;
-#if 0
int error;
-#endif
if (sa6->sin6_len != sizeof(struct sockaddr_in6))
return (EINVAL);
@@ -3246,13 +3219,10 @@ ip6_setpktopt(optname, buf, len, opt, priv, sticky, cmsg, uproto)
IN6_IS_ADDR_MULTICAST(&sa6->sin6_addr)) {
return (EINVAL);
}
-#if 0
- if ((error = scope6_check_id(sa6, ip6_use_defzone))
+ if ((error = sa6_embedscope(sa6, ip6_use_defzone))
!= 0) {
return (error);
}
-#endif
- sa6->sin6_scope_id = 0; /* XXX */
break;
}
case AF_LINK: /* should eventually be supported */
diff --git a/sys/netinet6/ip6_var.h b/sys/netinet6/ip6_var.h
index 090dc31..68faf9b 100644
--- a/sys/netinet6/ip6_var.h
+++ b/sys/netinet6/ip6_var.h
@@ -322,6 +322,9 @@ extern int ip6_use_tempaddr; /* whether to use temporary addresses. */
extern int ip6_prefer_tempaddr; /* whether to prefer temporary addresses
in the source address selection */
+extern int ip6_use_defzone; /* whether to use the default scope zone
+ when unspecified */
+
extern struct pfil_head inet6_pfil_hook; /* packet filter hooks */
extern struct pr_usrreqs rip6_usrreqs;
@@ -391,7 +394,7 @@ int none_input __P((struct mbuf **, int *, int));
struct in6_addr *in6_selectsrc __P((struct sockaddr_in6 *,
struct ip6_pktopts *, struct ip6_moptions *, struct route_in6 *,
- struct in6_addr *, int *));
+ struct in6_addr *, struct ifnet **, int *));
int in6_selectroute __P((struct sockaddr_in6 *, struct ip6_pktopts *,
struct ip6_moptions *, struct route_in6 *, struct ifnet **,
struct rtentry **, int));
diff --git a/sys/netinet6/ipsec.c b/sys/netinet6/ipsec.c
index 63e02d0..aa710a2 100644
--- a/sys/netinet6/ipsec.c
+++ b/sys/netinet6/ipsec.c
@@ -73,6 +73,7 @@
#include <netinet/ip6.h>
#ifdef INET6
#include <netinet6/ip6_var.h>
+#include <netinet6/scope6_var.h>
#endif
#include <netinet/in_pcb.h>
#ifdef INET6
@@ -1143,14 +1144,14 @@ ipsec6_setspidx_ipaddr(m, spidx)
bzero(sin6, sizeof(*sin6));
sin6->sin6_family = AF_INET6;
sin6->sin6_len = sizeof(struct sockaddr_in6);
- in6_recoverscope(sin6, &ip6->ip6_src, NULL);
+ sin6->sin6_addr = ip6->ip6_src;
spidx->prefs = sizeof(struct in6_addr) << 3;
sin6 = (struct sockaddr_in6 *)&spidx->dst;
bzero(sin6, sizeof(*sin6));
sin6->sin6_family = AF_INET6;
sin6->sin6_len = sizeof(struct sockaddr_in6);
- in6_recoverscope(sin6, &ip6->ip6_dst, NULL);
+ sin6->sin6_addr = ip6->ip6_dst;
spidx->prefd = sizeof(struct in6_addr) << 3;
return 0;
@@ -2177,9 +2178,11 @@ ipsec6_encapsulate(m, sav)
struct mbuf *m;
struct secasvar *sav;
{
+ struct sockaddr_in6 sa6;
struct ip6_hdr *oip6;
struct ip6_hdr *ip6;
size_t plen;
+ int error;
/* can't tunnel between different AFs */
if (((struct sockaddr *)&sav->sah->saidx.src)->sa_family
@@ -2237,10 +2240,17 @@ ipsec6_encapsulate(m, sav)
/* ip6->ip6_plen will be updated in ip6_output() */
}
ip6->ip6_nxt = IPPROTO_IPV6;
- in6_embedscope(&ip6->ip6_src,
- (struct sockaddr_in6 *)&sav->sah->saidx.src, NULL, NULL);
- in6_embedscope(&ip6->ip6_dst,
- (struct sockaddr_in6 *)&sav->sah->saidx.dst, NULL, NULL);
+
+ sa6 = *(struct sockaddr_in6 *)&sav->sah->saidx.src;
+ if ((error = sa6_embedscope(&sa6, 0)) != 0)
+ return (error);
+ ip6->ip6_src = sa6.sin6_addr;
+
+ sa6 = *(struct sockaddr_in6 *)&sav->sah->saidx.dst;
+ if ((error = sa6_embedscope(&sa6, 0)) != 0)
+ return (error);
+ ip6->ip6_dst = sa6.sin6_addr;
+
ip6->ip6_hlim = IPV6_DEFHLIM;
/* XXX Should ip6_src be updated later ? */
@@ -2833,14 +2843,14 @@ ipsec6_checksa(isr, state, tunnel)
sin6->sin6_len = sizeof(*sin6);
sin6->sin6_family = AF_INET6;
sin6->sin6_port = IPSEC_PORT_ANY;
- in6_recoverscope(sin6, &ip6->ip6_src, NULL);
+ sin6->sin6_addr = ip6->ip6_src;
}
sin6 = (struct sockaddr_in6 *)&saidx.dst;
if (sin6->sin6_len == 0 || tunnel) {
sin6->sin6_len = sizeof(*sin6);
sin6->sin6_family = AF_INET6;
sin6->sin6_port = IPSEC_PORT_ANY;
- in6_recoverscope(sin6, &ip6->ip6_dst, NULL);
+ sin6->sin6_addr = ip6->ip6_dst;
}
return key_checkrequest(isr, &saidx);
diff --git a/sys/netinet6/mld6.c b/sys/netinet6/mld6.c
index 5cd7cf6..4f62181 100644
--- a/sys/netinet6/mld6.c
+++ b/sys/netinet6/mld6.c
@@ -82,6 +82,7 @@
#include <netinet/in_var.h>
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
+#include <netinet6/scope6_var.h>
#include <netinet/icmp6.h>
#include <netinet6/mld6_var.h>
@@ -101,9 +102,6 @@
static struct ip6_pktopts ip6_opts;
static int mld6_timers_are_running;
-/* XXX: These are necessary for KAME's link-local hack */
-static struct in6_addr mld6_all_nodes_linklocal = IN6ADDR_LINKLOCAL_ALLNODES_INIT;
-static struct in6_addr mld6_all_routers_linklocal = IN6ADDR_LINKLOCAL_ALLROUTERS_INIT;
static void mld6_sendpkt(struct in6_multi *, int, const struct in6_addr *);
@@ -134,6 +132,7 @@ void
mld6_start_listening(in6m)
struct in6_multi *in6m;
{
+ struct in6_addr all_in6;
int s = splnet();
/*
@@ -143,10 +142,15 @@ mld6_start_listening(in6m)
* MLD messages are never sent for multicast addresses whose scope is 0
* (reserved) or 1 (node-local).
*/
- mld6_all_nodes_linklocal.s6_addr16[1] =
- htons(in6m->in6m_ifp->if_index); /* XXX */
- if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &mld6_all_nodes_linklocal) ||
- IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) < IPV6_ADDR_SCOPE_LINKLOCAL) {
+ all_in6 = in6addr_linklocal_allnodes;
+ if (in6_setscope(&all_in6, in6m->in6m_ifp, NULL)) {
+ /* XXX: this should not happen! */
+ in6m->in6m_timer = 0;
+ in6m->in6m_state = MLD_OTHERLISTENER;
+ }
+ if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &all_in6) ||
+ IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) <
+ IPV6_ADDR_SCOPE_LINKLOCAL) {
in6m->in6m_timer = 0;
in6m->in6m_state = MLD_OTHERLISTENER;
} else {
@@ -164,16 +168,24 @@ void
mld6_stop_listening(in6m)
struct in6_multi *in6m;
{
- mld6_all_nodes_linklocal.s6_addr16[1] =
- htons(in6m->in6m_ifp->if_index); /* XXX */
- mld6_all_routers_linklocal.s6_addr16[1] =
- htons(in6m->in6m_ifp->if_index); /* XXX: necessary when mrouting */
+ struct in6_addr allnode, allrouter;
+ allnode = in6addr_linklocal_allnodes;
+ if (in6_setscope(&allnode, in6m->in6m_ifp, NULL)) {
+ /* XXX: this should not happen! */
+ return;
+ }
+ allrouter = in6addr_linklocal_allrouters;
+ if (in6_setscope(&allrouter, in6m->in6m_ifp, NULL)) {
+ /* XXX impossible */
+ return;
+ }
if (in6m->in6m_state == MLD_IREPORTEDLAST &&
- (!IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &mld6_all_nodes_linklocal)) &&
- IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) > IPV6_ADDR_SCOPE_INTFACELOCAL)
- mld6_sendpkt(in6m, MLD_LISTENER_DONE,
- &mld6_all_routers_linklocal);
+ !IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &allnode) &&
+ IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) >
+ IPV6_ADDR_SCOPE_INTFACELOCAL) {
+ mld6_sendpkt(in6m, MLD_LISTENER_DONE, &allrouter);
+ }
}
void
@@ -185,6 +197,7 @@ mld6_input(m, off)
struct mld_hdr *mldh;
struct ifnet *ifp = m->m_pkthdr.rcvif;
struct in6_multi *in6m;
+ struct in6_addr mld_addr, all_in6;
struct in6_ifaddr *ia;
struct ifmultiaddr *ifma;
int timer; /* timer value in the MLD query header */
@@ -218,6 +231,16 @@ mld6_input(m, off)
}
/*
+ * make a copy for local work (in6_setscope() may modify the 1st arg)
+ */
+ mld_addr = mldh->mld_addr;
+ if (in6_setscope(&mld_addr, ifp, NULL)) {
+ /* XXX: this should not happen! */
+ m_free(m);
+ return;
+ }
+
+ /*
* In the MLD6 specification, there are 3 states and a flag.
*
* In Non-Listener state, we simply don't have a membership record.
@@ -233,12 +256,15 @@ mld6_input(m, off)
if (ifp->if_flags & IFF_LOOPBACK)
break;
- if (!IN6_IS_ADDR_UNSPECIFIED(&mldh->mld_addr) &&
- !IN6_IS_ADDR_MULTICAST(&mldh->mld_addr))
+ if (!IN6_IS_ADDR_UNSPECIFIED(&mld_addr) &&
+ !IN6_IS_ADDR_MULTICAST(&mld_addr))
break; /* print error or log stat? */
- if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld_addr))
- mldh->mld_addr.s6_addr16[1] =
- htons(ifp->if_index); /* XXX */
+
+ all_in6 = in6addr_linklocal_allnodes;
+ if (in6_setscope(&all_in6, ifp, NULL)) {
+ /* XXX: this should not happen! */
+ break;
+ }
/*
* - Start the timers in all of our membership records
@@ -263,24 +289,19 @@ mld6_input(m, off)
timer = ntohs(mldh->mld_maxdelay) * PR_FASTHZ / MLD_TIMER_SCALE;
if (timer == 0 && mldh->mld_maxdelay)
timer = 1;
- mld6_all_nodes_linklocal.s6_addr16[1] =
- htons(ifp->if_index); /* XXX */
TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
if (ifma->ifma_addr->sa_family != AF_INET6)
continue;
in6m = (struct in6_multi *)ifma->ifma_protospec;
- if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr,
- &mld6_all_nodes_linklocal) ||
+ if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &all_in6) ||
IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) <
IPV6_ADDR_SCOPE_LINKLOCAL)
continue;
- if (IN6_IS_ADDR_UNSPECIFIED(&mldh->mld_addr) ||
- IN6_ARE_ADDR_EQUAL(&mldh->mld_addr,
- &in6m->in6m_addr))
- {
+ if (IN6_IS_ADDR_UNSPECIFIED(&mld_addr) ||
+ IN6_ARE_ADDR_EQUAL(&mld_addr, &in6m->in6m_addr)) {
if (timer == 0) {
/* send a report immediately */
mld6_sendpkt(in6m, MLD_LISTENER_REPORT,
@@ -296,9 +317,6 @@ mld6_input(m, off)
}
}
}
-
- if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld_addr))
- mldh->mld_addr.s6_addr16[1] = 0; /* XXX */
break;
case MLD_LISTENER_REPORT:
@@ -314,24 +332,18 @@ mld6_input(m, off)
if (m->m_flags & M_LOOP) /* XXX: grotty flag, but efficient */
break;
- if (!IN6_IS_ADDR_MULTICAST(&mldh->mld_addr))
+ if (!IN6_IS_ADDR_MULTICAST(&mld_addr))
break;
- if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld_addr))
- mldh->mld_addr.s6_addr16[1] =
- htons(ifp->if_index); /* XXX */
/*
* If we belong to the group being reported, stop
* our timer for that group.
*/
- IN6_LOOKUP_MULTI(mldh->mld_addr, ifp, in6m);
+ IN6_LOOKUP_MULTI(mld_addr, ifp, in6m);
if (in6m) {
in6m->in6m_timer = 0; /* transit to idle state */
in6m->in6m_state = MLD_OTHERLISTENER; /* clear flag */
}
-
- if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld_addr))
- mldh->mld_addr.s6_addr16[1] = 0; /* XXX */
break;
default: /* this is impossible */
log(LOG_ERR, "mld6_input: illegal type(%d)", mldh->mld_type);
@@ -437,8 +449,7 @@ mld6_sendpkt(in6m, type, dst)
mldh->mld_maxdelay = 0;
mldh->mld_reserved = 0;
mldh->mld_addr = in6m->in6m_addr;
- if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld_addr))
- mldh->mld_addr.s6_addr16[1] = 0; /* XXX */
+ in6_clearscope(&mldh->mld_addr); /* XXX */
mldh->mld_cksum = in6_cksum(mh, IPPROTO_ICMPV6, sizeof(struct ip6_hdr),
sizeof(struct mld_hdr));
diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c
index 7c50764..f93df61 100644
--- a/sys/netinet6/nd6.c
+++ b/sys/netinet6/nd6.c
@@ -63,6 +63,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>
@@ -444,19 +445,14 @@ nd6_timer(ignored_arg)
} else {
struct mbuf *m = ln->ln_hold;
if (m) {
- if (rt->rt_ifp) {
- /*
- * Fake rcvif to make ICMP error
- * more helpful in diagnosing
- * for the receiver.
- * XXX: should we consider
- * older rcvif?
- */
- m->m_pkthdr.rcvif = rt->rt_ifp;
- }
- icmp6_error(m, ICMP6_DST_UNREACH,
- ICMP6_DST_UNREACH_ADDR, 0);
+ /*
+ * assuming every packet in ln_hold has
+ * the same IP header
+ */
ln->ln_hold = NULL;
+ icmp6_error2(m, ICMP6_DST_UNREACH,
+ ICMP6_DST_UNREACH_ADDR, 0,
+ rt->rt_ifp);
}
next = nd6_free(rt);
}
@@ -869,13 +865,26 @@ nd6_is_new_addr_neighbor(addr, ifp)
/*
* A link-local address is always a neighbor.
- * XXX: we should use the sin6_scope_id field rather than the embedded
- * interface index.
* XXX: a link does not necessarily specify a single interface.
*/
- if (IN6_IS_ADDR_LINKLOCAL(&addr->sin6_addr) &&
- ntohs(*(u_int16_t *)&addr->sin6_addr.s6_addr[2]) == ifp->if_index)
- return (1);
+ if (IN6_IS_ADDR_LINKLOCAL(&addr->sin6_addr)) {
+ struct sockaddr_in6 sin6_copy;
+ u_int32_t zone;
+
+ /*
+ * We need sin6_copy since sa6_recoverscope() may modify the
+ * content (XXX).
+ */
+ sin6_copy = *addr;
+ if (sa6_recoverscope(&sin6_copy))
+ return (0); /* XXX: should be impossible */
+ if (in6_setscope(&sin6_copy.sin6_addr, ifp, &zone))
+ return (0);
+ if (sin6_copy.sin6_scope_id == zone)
+ return (1);
+ else
+ return (0);
+ }
/*
* If the address matches one of our addresses,
@@ -1282,11 +1291,11 @@ nd6_rtrequest(req, rt, info)
llsol = SIN6(rt_key(rt))->sin6_addr;
llsol.s6_addr16[0] = htons(0xff02);
- llsol.s6_addr16[1] = htons(ifp->if_index);
llsol.s6_addr32[1] = 0;
llsol.s6_addr32[2] = htonl(1);
llsol.s6_addr8[12] = 0xff;
-
+ if (in6_setscope(&llsol, ifp, NULL))
+ break;
if (!in6_addmulti(&llsol, ifp, &error)) {
nd6log((LOG_ERR, "%s: failed to join "
"%s (errno=%d)\n", if_name(ifp),
@@ -1307,14 +1316,15 @@ nd6_rtrequest(req, rt, info)
llsol = SIN6(rt_key(rt))->sin6_addr;
llsol.s6_addr16[0] = htons(0xff02);
- llsol.s6_addr16[1] = htons(ifp->if_index);
llsol.s6_addr32[1] = 0;
llsol.s6_addr32[2] = htonl(1);
llsol.s6_addr8[12] = 0xff;
-
- IN6_LOOKUP_MULTI(llsol, ifp, in6m);
- if (in6m)
- in6_delmulti(in6m);
+ if (in6_setscope(&llsol, ifp, NULL) == 0) {
+ IN6_LOOKUP_MULTI(llsol, ifp, in6m);
+ if (in6m)
+ in6_delmulti(in6m);
+ } else
+ ; /* XXX: should not happen. bark here? */
}
nd6_inuse--;
ln->ln_next->ln_prev = ln->ln_prev;
@@ -1386,8 +1396,7 @@ nd6_ioctl(cmd, data, ifp)
struct nd_pfxrouter *pfr;
int j;
- (void)in6_embedscope(&oprl->prefix[i].prefix,
- &pr->ndpr_prefix, NULL, NULL);
+ oprl->prefix[i].prefix = pr->ndpr_prefix.sin6_addr;
oprl->prefix[i].raflags = pr->ndpr_raf;
oprl->prefix[i].prefixlen = pr->ndpr_plen;
oprl->prefix[i].vltime = pr->ndpr_vltime;
@@ -1501,17 +1510,8 @@ nd6_ioctl(cmd, data, ifp)
struct llinfo_nd6 *ln;
struct in6_addr nb_addr = nbi->addr; /* make local for safety */
- /*
- * XXX: KAME specific hack for scoped addresses
- * XXXX: for other scopes than link-local?
- */
- if (IN6_IS_ADDR_LINKLOCAL(&nbi->addr) ||
- IN6_IS_ADDR_MC_LINKLOCAL(&nbi->addr)) {
- u_int16_t *idp = (u_int16_t *)&nb_addr.s6_addr[2];
-
- if (*idp == 0)
- *idp = htons(ifp->if_index);
- }
+ if ((error = in6_setscope(&nb_addr, ifp, NULL)) != 0)
+ return (error);
s = splnet();
if ((rt = nd6_lookup(&nb_addr, 0, ifp)) == NULL) {
@@ -2122,12 +2122,13 @@ nd6_sysctl_drlist(SYSCTL_HANDLER_ARGS)
bzero(d, sizeof(*d));
d->rtaddr.sin6_family = AF_INET6;
d->rtaddr.sin6_len = sizeof(d->rtaddr);
- if (in6_recoverscope(&d->rtaddr, &dr->rtaddr,
- dr->ifp) != 0)
+ d->rtaddr.sin6_addr = dr->rtaddr;
+ if (sa6_recoverscope(&d->rtaddr)) {
log(LOG_ERR,
- "scope error in "
- "default router list (%s)\n",
- ip6_sprintf(&dr->rtaddr));
+ "scope error in router list (%s)\n",
+ ip6_sprintf(&d->rtaddr.sin6_addr));
+ /* XXX: press on... */
+ }
d->flags = dr->flags;
d->rtlifetime = dr->rtlifetime;
d->expire = dr->expire;
@@ -2169,11 +2170,12 @@ nd6_sysctl_prlist(SYSCTL_HANDLER_ARGS)
sin6 = (struct sockaddr_in6 *)(p + 1);
p->prefix = pr->ndpr_prefix;
- if (in6_recoverscope(&p->prefix,
- &p->prefix.sin6_addr, pr->ndpr_ifp) != 0)
+ if (sa6_recoverscope(&p->prefix)) {
log(LOG_ERR,
"scope error in prefix list (%s)\n",
ip6_sprintf(&p->prefix.sin6_addr));
+ /* XXX: press on... */
+ }
p->raflags = pr->ndpr_raf;
p->prefixlen = pr->ndpr_plen;
p->vltime = pr->ndpr_vltime;
@@ -2194,12 +2196,13 @@ nd6_sysctl_prlist(SYSCTL_HANDLER_ARGS)
bzero(s6, sizeof(*s6));
s6->sin6_family = AF_INET6;
s6->sin6_len = sizeof(*sin6);
- if (in6_recoverscope(s6, &pfr->router->rtaddr,
- pfr->router->ifp) != 0)
+ s6->sin6_addr = pfr->router->rtaddr;
+ if (sa6_recoverscope(s6)) {
log(LOG_ERR,
"scope error in "
"prefix list (%s)\n",
ip6_sprintf(&pfr->router->rtaddr));
+ }
advrtrs++;
}
p->advrtrs = advrtrs;
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
diff --git a/sys/netinet6/nd6_rtr.c b/sys/netinet6/nd6_rtr.c
index e0bc008..1b0792d 100644
--- a/sys/netinet6/nd6_rtr.c
+++ b/sys/netinet6/nd6_rtr.c
@@ -1875,8 +1875,6 @@ rt6_flush(gateway, ifp)
splx(s);
return;
}
- /* XXX: hack for KAME's link-local address kludge */
- gateway->s6_addr16[1] = htons(ifp->if_index);
RADIX_NODE_HEAD_LOCK(rnh);
rnh->rnh_walktree(rnh, rt6_deleteroute, (void *)gateway);
diff --git a/sys/netinet6/raw_ip6.c b/sys/netinet6/raw_ip6.c
index 64304ac..475d7fa 100644
--- a/sys/netinet6/raw_ip6.c
+++ b/sys/netinet6/raw_ip6.c
@@ -75,6 +75,7 @@
#include <sys/socketvar.h>
#include <sys/sx.h>
#include <sys/systm.h>
+#include <sys/syslog.h>
#include <net/if.h>
#include <net/if_types.h>
@@ -92,9 +93,7 @@
#include <netinet6/ip6_var.h>
#include <netinet6/nd6.h>
#include <netinet6/raw_ip6.h>
-#ifdef ENABLE_DEFAULT_SCOPE
#include <netinet6/scope6_var.h>
-#endif
#ifdef IPSEC
#include <netinet6/ipsec.h>
@@ -328,6 +327,7 @@ rip6_output(m, va_alist)
struct ifnet *oifp = NULL;
int type = 0, code = 0; /* for ICMPv6 output statistics only */
int priv = 0;
+ int scope_ambiguous = 0;
struct in6_addr *in6a;
va_list ap;
@@ -355,6 +355,17 @@ rip6_output(m, va_alist)
optp = in6p->in6p_outputopts;
/*
+ * Check and convert scope zone ID into internal form.
+ * XXX: we may still need to determine the zone later.
+ */
+ if (!(so->so_state & SS_ISCONNECTED)) {
+ if (dstsock->sin6_scope_id == 0 && !ip6_use_defzone)
+ scope_ambiguous = 1;
+ if ((error = sa6_embedscope(dstsock, ip6_use_defzone)) != 0)
+ goto bad;
+ }
+
+ /*
* For an ICMPv6 packet, we should know its type and code
* to update statistics.
*/
@@ -378,55 +389,32 @@ rip6_output(m, va_alist)
ip6 = mtod(m, struct ip6_hdr *);
/*
- * Next header might not be ICMP6 but use its pseudo header anyway.
- */
- ip6->ip6_dst = *dst;
-
- /*
- * If the scope of the destination is link-local, embed the interface
- * index in the address.
- *
- * XXX advanced-api value overrides sin6_scope_id
- */
- if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) {
- struct in6_pktinfo *pi;
-
- /*
- * XXX Boundary check is assumed to be already done in
- * ip6_setpktopts().
- */
- if (optp &&
- (pi = optp->ip6po_pktinfo) &&
- pi->ipi6_ifindex) {
- ip6->ip6_dst.s6_addr16[1] = htons(pi->ipi6_ifindex);
- oifp = ifnet_byindex(pi->ipi6_ifindex);
- } else if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) &&
- in6p->in6p_moptions &&
- in6p->in6p_moptions->im6o_multicast_ifp) {
- oifp = in6p->in6p_moptions->im6o_multicast_ifp;
- ip6->ip6_dst.s6_addr16[1] = htons(oifp->if_index);
- } else if (dstsock->sin6_scope_id) {
- /* boundary check */
- if (dstsock->sin6_scope_id < 0 ||
- if_index < dstsock->sin6_scope_id) {
- error = ENXIO; /* XXX EINVAL? */
- goto bad;
- }
- ip6->ip6_dst.s6_addr16[1] =
- htons(dstsock->sin6_scope_id & 0xffff); /* XXX */
- }
- }
-
- /*
* Source address selection.
*/
if ((in6a = in6_selectsrc(dstsock, optp, in6p->in6p_moptions, NULL,
- &in6p->in6p_laddr, &error)) == 0) {
+ &in6p->in6p_laddr, &oifp, &error)) == NULL) {
if (error == 0)
error = EADDRNOTAVAIL;
goto bad;
}
ip6->ip6_src = *in6a;
+
+ if (oifp && scope_ambiguous) {
+ /*
+ * Application should provide a proper zone ID or the use of
+ * default zone IDs should be enabled. Unfortunately, some
+ * applications do not behave as it should, so we need a
+ * workaround. Even if an appropriate ID is not determined
+ * (when it's required), if we can determine the outgoing
+ * interface. determine the zone ID based on the interface.
+ */
+ error = in6_setscope(&dstsock->sin6_addr, oifp, NULL);
+ if (error != 0)
+ goto bad;
+ }
+ ip6->ip6_dst = dstsock->sin6_addr;
+
+ /* fill in the rest of the IPv6 header fields */
ip6->ip6_flow = (ip6->ip6_flow & ~IPV6_FLOWINFO_MASK) |
(in6p->in6p_flowinfo & IPV6_FLOWINFO_MASK);
ip6->ip6_vfc = (ip6->ip6_vfc & ~IPV6_VERSION_MASK) |
@@ -648,16 +636,15 @@ rip6_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
struct inpcb *inp = sotoinpcb(so);
struct sockaddr_in6 *addr = (struct sockaddr_in6 *)nam;
struct ifaddr *ia = NULL;
+ int error = 0;
if (nam->sa_len != sizeof(*addr))
return EINVAL;
if (TAILQ_EMPTY(&ifnet) || addr->sin6_family != AF_INET6)
return EADDRNOTAVAIL;
-#ifdef ENABLE_DEFAULT_SCOPE
- if (addr->sin6_scope_id == 0) { /* not change if specified */
- addr->sin6_scope_id = scope6_addr2default(&addr->sin6_addr);
- }
-#endif
+ if ((error = sa6_embedscope(addr, ip6_use_defzone)) != 0)
+ return(error);
+
if (!IN6_IS_ADDR_UNSPECIFIED(&addr->sin6_addr) &&
(ia = ifa_ifwithaddr((struct sockaddr *)addr)) == 0)
return EADDRNOTAVAIL;
@@ -681,10 +668,8 @@ rip6_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
struct inpcb *inp = sotoinpcb(so);
struct sockaddr_in6 *addr = (struct sockaddr_in6 *)nam;
struct in6_addr *in6a = NULL;
- int error = 0;
-#ifdef ENABLE_DEFAULT_SCOPE
- struct sockaddr_in6 tmp;
-#endif
+ struct ifnet *ifp = NULL;
+ int error = 0, scope_ambiguous = 0;
if (nam->sa_len != sizeof(*addr))
return EINVAL;
@@ -692,27 +677,41 @@ rip6_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
return EADDRNOTAVAIL;
if (addr->sin6_family != AF_INET6)
return EAFNOSUPPORT;
-#ifdef ENABLE_DEFAULT_SCOPE
- if (addr->sin6_scope_id == 0) { /* not change if specified */
- /* avoid overwrites */
- tmp = *addr;
- addr = &tmp;
- addr->sin6_scope_id = scope6_addr2default(&addr->sin6_addr);
- }
-#endif
+
+ /*
+ * Application should provide a proper zone ID or the use of
+ * default zone IDs should be enabled. Unfortunately, some
+ * applications do not behave as it should, so we need a
+ * workaround. Even if an appropriate ID is not determined,
+ * we'll see if we can determine the outgoing interface. If we
+ * can, determine the zone ID based on the interface below.
+ */
+ if (addr->sin6_scope_id == 0 && !ip6_use_defzone)
+ scope_ambiguous = 1;
+ if ((error = sa6_embedscope(addr, ip6_use_defzone)) != 0)
+ return(error);
+
INP_INFO_WLOCK(&ripcbinfo);
INP_LOCK(inp);
/* Source address selection. XXX: need pcblookup? */
in6a = in6_selectsrc(addr, inp->in6p_outputopts,
inp->in6p_moptions, NULL,
- &inp->in6p_laddr, &error);
+ &inp->in6p_laddr, &ifp, &error);
if (in6a == NULL) {
INP_UNLOCK(inp);
INP_INFO_WUNLOCK(&ripcbinfo);
return (error ? error : EADDRNOTAVAIL);
}
- inp->in6p_laddr = *in6a;
+
+ /* XXX: see above */
+ if (ifp && scope_ambiguous &&
+ (error = in6_setscope(&addr->sin6_addr, ifp, NULL)) != 0) {
+ INP_UNLOCK(inp);
+ INP_INFO_WUNLOCK(&ripcbinfo);
+ return(error);
+ }
inp->in6p_faddr = addr->sin6_addr;
+ inp->in6p_laddr = *in6a;
soisconnected(so);
INP_UNLOCK(inp);
INP_INFO_WUNLOCK(&ripcbinfo);
@@ -764,14 +763,29 @@ rip6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam,
m_freem(m);
return ENOTCONN;
}
+ if (nam->sa_len != sizeof(struct sockaddr_in6)) {
+ INP_INFO_WUNLOCK(&ripcbinfo);
+ m_freem(m);
+ return(EINVAL);
+ }
tmp = *(struct sockaddr_in6 *)nam;
dst = &tmp;
+
+ if (dst->sin6_family == AF_UNSPEC) {
+ /*
+ * XXX: we allow this case for backward
+ * compatibility to buggy applications that
+ * rely on old (and wrong) kernel behavior.
+ */
+ log(LOG_INFO, "rip6 SEND: address family is "
+ "unspec. Assume AF_INET6\n");
+ dst->sin6_family = AF_INET6;
+ } else if (dst->sin6_family != AF_INET6) {
+ INP_INFO_WUNLOCK(&ripcbinfo);
+ m_freem(m);
+ return(EAFNOSUPPORT);
+ }
}
-#ifdef ENABLE_DEFAULT_SCOPE
- if (dst->sin6_scope_id == 0) { /* not change if specified */
- dst->sin6_scope_id = scope6_addr2default(&dst->sin6_addr);
- }
-#endif
ret = rip6_output(m, so, dst, control);
INP_INFO_WUNLOCK(&ripcbinfo);
return (ret);
diff --git a/sys/netinet6/route6.c b/sys/netinet6/route6.c
index aa25440..c082e33 100644
--- a/sys/netinet6/route6.c
+++ b/sys/netinet6/route6.c
@@ -45,6 +45,7 @@
#include <netinet6/in6_var.h>
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
+#include <netinet6/scope6_var.h>
#include <netinet/icmp6.h>
@@ -143,6 +144,7 @@ ip6_rthdr0(m, ip6, rh0)
{
int addrs, index;
struct in6_addr *nextaddr, tmpaddr;
+ struct in6_ifaddr *ifa;
if (rh0->ip6r0_segleft == 0)
return (0);
@@ -197,15 +199,24 @@ ip6_rthdr0(m, ip6, rh0)
}
/*
+ * Determine the scope zone of the next hop, based on the interface
+ * of the current hop. [RFC4007, Section 9]
+ * Then disambiguate the scope zone for the next hop (if necessary).
+ */
+ if ((ifa = ip6_getdstifaddr(m)) == NULL)
+ goto bad;
+ if (in6_setscope(nextaddr, ifa->ia_ifp, NULL) != 0) {
+ ip6stat.ip6s_badscope++;
+ goto bad;
+ }
+
+ /*
* Swap the IPv6 destination address and nextaddr. Forward the packet.
*/
tmpaddr = *nextaddr;
*nextaddr = ip6->ip6_dst;
- if (IN6_IS_ADDR_LINKLOCAL(nextaddr))
- nextaddr->s6_addr16[1] = 0;
+ in6_clearscope(nextaddr); /* XXX */
ip6->ip6_dst = tmpaddr;
- if (IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst))
- ip6->ip6_dst.s6_addr16[1] = htons(m->m_pkthdr.rcvif->if_index);
#ifdef COMPAT_RFC1883
if (rh0->ip6r0_slmap[index / 8] & (1 << (7 - (index % 8))))
@@ -217,4 +228,8 @@ ip6_rthdr0(m, ip6, rh0)
#endif
return (-1); /* m would be freed in ip6_forward() */
+
+ bad:
+ m_freem(m);
+ return (-1);
}
diff --git a/sys/netinet6/scope6.c b/sys/netinet6/scope6.c
index 7b6c473..ca31098 100644
--- a/sys/netinet6/scope6.c
+++ b/sys/netinet6/scope6.c
@@ -36,6 +36,7 @@
#include <sys/socket.h>
#include <sys/systm.h>
#include <sys/queue.h>
+#include <sys/syslog.h>
#include <net/route.h>
#include <net/if.h>
@@ -45,6 +46,12 @@
#include <netinet6/in6_var.h>
#include <netinet6/scope6_var.h>
+#ifdef ENABLE_DEFAULT_SCOPE
+int ip6_use_defzone = 1;
+#else
+int ip6_use_defzone = 0;
+#endif
+
/*
* The scope6_lock protects the global sid default stored in
* sid_default below.
@@ -254,87 +261,6 @@ in6_addrscope(addr)
return IPV6_ADDR_SCOPE_GLOBAL;
}
-/*
- * When we introduce the "4+28" split semantics in sin6_scope_id,
- * a 32bit integer is not enough to tell a large ID from an error (-1).
- * So, we intentionally use a large type as the return value.
- */
-int
-in6_addr2zoneid(ifp, addr, ret_id)
- struct ifnet *ifp; /* must not be NULL */
- struct in6_addr *addr; /* must not be NULL */
- u_int32_t *ret_id; /* must not be NULL */
-{
- int scope;
- u_int32_t zoneid = 0;
- struct scope6_id *sid = NULL;
-
- IF_AFDATA_LOCK(ifp);
-
- sid = SID(ifp);
-
-#ifdef DIAGNOSTIC
- if (sid == NULL) { /* should not happen */
- panic("in6_addr2zoneid: scope array is NULL");
- /* NOTREACHED */
- }
- if (ret_id == NULL) {
- panic("in6_addr2zoneid: return ID is null");
- /* NOTREACHED */
- }
-#endif
-
- /*
- * special case: the loopback address can only belong to a loopback
- * interface.
- */
- if (IN6_IS_ADDR_LOOPBACK(addr)) {
- if (!(ifp->if_flags & IFF_LOOPBACK)) {
- IF_AFDATA_UNLOCK(ifp);
- return (-1);
- } else {
- *ret_id = 0; /* there's no ambiguity */
- IF_AFDATA_UNLOCK(ifp);
- return (0);
- }
- }
-
- scope = in6_addrscope(addr);
-
- /*
- * XXX: These are all u_int32_t reads, so may not require locking.
- */
- SCOPE6_LOCK();
- switch (scope) {
- case IPV6_ADDR_SCOPE_INTFACELOCAL: /* should be interface index */
- zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL];
- break;
-
- case IPV6_ADDR_SCOPE_LINKLOCAL:
- zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL];
- break;
-
- case IPV6_ADDR_SCOPE_SITELOCAL:
- zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL];
- break;
-
- case IPV6_ADDR_SCOPE_ORGLOCAL:
- zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL];
- break;
-
- default:
- zoneid = 0; /* XXX: treat as global. */
- break;
- }
- SCOPE6_UNLOCK();
-
- *ret_id = zoneid;
-
- IF_AFDATA_UNLOCK(ifp);
-
- return (0);
-}
-
void
scope6_setdefault(ifp)
struct ifnet *ifp; /* note that this might be NULL */
@@ -392,3 +318,169 @@ scope6_addr2default(addr)
SCOPE6_UNLOCK();
return (id);
}
+
+/*
+ * Validate the specified scope zone ID in the sin6_scope_id field. If the ID
+ * is unspecified (=0), needs to be specified, and the default zone ID can be
+ * used, the default value will be used.
+ * This routine then generates the kernel-internal form: if the address scope
+ * of is interface-local or link-local, embed the interface index in the
+ * address.
+ */
+int
+sa6_embedscope(sin6, defaultok)
+ struct sockaddr_in6 *sin6;
+ int defaultok;
+{
+ struct ifnet *ifp;
+ u_int32_t zoneid;
+
+ if ((zoneid = sin6->sin6_scope_id) == 0 && defaultok)
+ zoneid = scope6_addr2default(&sin6->sin6_addr);
+
+ if (zoneid != 0 &&
+ (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) ||
+ IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr))) {
+ /*
+ * At this moment, we only check interface-local and
+ * link-local scope IDs, and use interface indices as the
+ * zone IDs assuming a one-to-one mapping between interfaces
+ * and links.
+ */
+ if (if_index < zoneid)
+ return (ENXIO);
+ ifp = ifnet_byindex(zoneid);
+ if (ifp == NULL) /* XXX: this can happen for some OS */
+ return (ENXIO);
+
+ /* XXX assignment to 16bit from 32bit variable */
+ sin6->sin6_addr.s6_addr16[1] = htons(zoneid & 0xffff);
+
+ sin6->sin6_scope_id = 0;
+ }
+
+ return 0;
+}
+
+/*
+ * generate standard sockaddr_in6 from embedded form.
+ */
+int
+sa6_recoverscope(sin6)
+ struct sockaddr_in6 *sin6;
+{
+ u_int32_t zoneid;
+
+ if (sin6->sin6_scope_id != 0) {
+ log(LOG_NOTICE,
+ "sa6_recoverscope: assumption failure (non 0 ID): %s%%%d\n",
+ ip6_sprintf(&sin6->sin6_addr), sin6->sin6_scope_id);
+ /* XXX: proceed anyway... */
+ }
+ if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) ||
+ IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr)) {
+ /*
+ * KAME assumption: link id == interface id
+ */
+ zoneid = ntohs(sin6->sin6_addr.s6_addr16[1]);
+ if (zoneid) {
+ /* sanity check */
+ if (zoneid < 0 || if_index < zoneid)
+ return (ENXIO);
+ if (!ifnet_byindex(zoneid))
+ return (ENXIO);
+ sin6->sin6_addr.s6_addr16[1] = 0;
+ sin6->sin6_scope_id = zoneid;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Determine the appropriate scope zone ID for in6 and ifp. If ret_id is
+ * non NULL, it is set to the zone ID. If the zone ID needs to be embedded
+ * in the in6_addr structure, in6 will be modified.
+ */
+int
+in6_setscope(in6, ifp, ret_id)
+ struct in6_addr *in6;
+ struct ifnet *ifp;
+ u_int32_t *ret_id; /* unnecessary? */
+{
+ int scope;
+ u_int32_t zoneid = 0;
+ struct scope6_id *sid = SID(ifp);
+
+#ifdef DIAGNOSTIC
+ if (sid == NULL) { /* should not happen */
+ panic("in6_setscope: scope array is NULL");
+ /* NOTREACHED */
+ }
+#endif
+
+ /*
+ * special case: the loopback address can only belong to a loopback
+ * interface.
+ */
+ if (IN6_IS_ADDR_LOOPBACK(in6)) {
+ if (!(ifp->if_flags & IFF_LOOPBACK))
+ return (EINVAL);
+ else {
+ if (ret_id != NULL)
+ *ret_id = 0; /* there's no ambiguity */
+ return (0);
+ }
+ }
+
+ scope = in6_addrscope(in6);
+
+ switch (scope) {
+ case IPV6_ADDR_SCOPE_INTFACELOCAL: /* should be interface index */
+ zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL];
+ break;
+
+ case IPV6_ADDR_SCOPE_LINKLOCAL:
+ zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL];
+ break;
+
+ case IPV6_ADDR_SCOPE_SITELOCAL:
+ zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL];
+ break;
+
+ case IPV6_ADDR_SCOPE_ORGLOCAL:
+ zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL];
+ break;
+
+ default:
+ zoneid = 0; /* XXX: treat as global. */
+ break;
+ }
+
+ if (ret_id != NULL)
+ *ret_id = zoneid;
+
+ if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6))
+ in6->s6_addr16[1] = htons(zoneid & 0xffff); /* XXX */
+
+ return (0);
+}
+
+/*
+ * Just clear the embedded scope identifier. Return 0 if the original address
+ * is intact; return non 0 if the address is modified.
+ */
+int
+in6_clearscope(in6)
+ struct in6_addr *in6;
+{
+ int modified = 0;
+
+ if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6)) {
+ if (in6->s6_addr16[1] != 0)
+ modified = 1;
+ in6->s6_addr16[1] = 0;
+ }
+
+ return (modified);
+}
diff --git a/sys/netinet6/scope6_var.h b/sys/netinet6/scope6_var.h
index 0baaa21..3dbdcd9 100644
--- a/sys/netinet6/scope6_var.h
+++ b/sys/netinet6/scope6_var.h
@@ -51,6 +51,10 @@ void scope6_setdefault __P((struct ifnet *));
int scope6_get_default __P((struct scope6_id *));
u_int32_t scope6_in6_addrscope __P((struct in6_addr *));
u_int32_t scope6_addr2default __P((struct in6_addr *));
+int sa6_embedscope __P((struct sockaddr_in6 *, int));
+int sa6_recoverscope __P((struct sockaddr_in6 *));
+int in6_setscope __P((struct in6_addr *, struct ifnet *, u_int32_t *));
+int in6_clearscope __P((struct in6_addr *));
#endif /* _KERNEL */
#endif /* _NETINET6_SCOPE6_VAR_H_ */
diff --git a/sys/netinet6/udp6_output.c b/sys/netinet6/udp6_output.c
index 933e3b3..0cd4cde 100644
--- a/sys/netinet6/udp6_output.c
+++ b/sys/netinet6/udp6_output.c
@@ -96,6 +96,7 @@
#include <netinet6/udp6_var.h>
#include <netinet/icmp6.h>
#include <netinet6/ip6protosw.h>
+#include <netinet6/scope6_var.h>
#ifdef IPSEC
#include <netinet6/ipsec.h>
@@ -128,6 +129,9 @@ udp6_output(in6p, m, addr6, control, td)
struct ip6_hdr *ip6;
struct udphdr *udp6;
struct in6_addr *laddr, *faddr;
+ struct sockaddr_in6 *sin6 = NULL;
+ struct ifnet *oifp = NULL;
+ int scope_ambiguous = 0;
u_short fport;
int error = 0;
struct ip6_pktopts *optp, opt;
@@ -139,6 +143,29 @@ udp6_output(in6p, m, addr6, control, td)
priv = 0;
if (td && !suser(td))
priv = 1;
+
+ if (addr6) {
+ /* addr6 has been validated in udp6_send(). */
+ sin6 = (struct sockaddr_in6 *)addr6;
+
+ /* protect *sin6 from overwrites */
+ tmp = *sin6;
+ sin6 = &tmp;
+
+ /*
+ * Application should provide a proper zone ID or the use of
+ * default zone IDs should be enabled. Unfortunately, some
+ * applications do not behave as it should, so we need a
+ * workaround. Even if an appropriate ID is not determined,
+ * we'll see if we can determine the outgoing interface. If we
+ * can, determine the zone ID based on the interface below.
+ */
+ if (sin6->sin6_scope_id == 0 && !ip6_use_defzone)
+ scope_ambiguous = 1;
+ if ((error = sa6_embedscope(sin6, ip6_use_defzone)) != 0)
+ return (error);
+ }
+
if (control) {
if ((error = ip6_setpktopts(control, &opt,
in6p->in6p_outputopts, priv, IPPROTO_UDP)) != 0)
@@ -147,7 +174,9 @@ udp6_output(in6p, m, addr6, control, td)
} else
optp = in6p->in6p_outputopts;
- if (addr6) {
+ if (sin6) {
+ faddr = &sin6->sin6_addr;
+
/*
* IPv4 version of udp_output calls in_pcbconnect in this case,
* which needs splnet and affects performance.
@@ -156,7 +185,6 @@ udp6_output(in6p, m, addr6, control, td)
* and in6_pcbsetport in order to fill in the local address
* and the local port.
*/
- struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)addr6;
if (sin6->sin6_port == 0) {
error = EADDRNOTAVAIL;
goto release;
@@ -168,11 +196,6 @@ udp6_output(in6p, m, addr6, control, td)
goto release;
}
- /* protect *sin6 from overwrites */
- tmp = *sin6;
- sin6 = &tmp;
-
- faddr = &sin6->sin6_addr;
fport = sin6->sin6_port; /* allow 0 port */
if (IN6_IS_ADDR_V4MAPPED(faddr)) {
@@ -189,19 +212,30 @@ udp6_output(in6p, m, addr6, control, td)
*/
error = EINVAL;
goto release;
- } else
- af = AF_INET;
- }
+ }
+ if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr) &&
+ !IN6_IS_ADDR_V4MAPPED(&in6p->in6p_laddr)) {
+ /*
+ * when remote addr is an IPv4-mapped address,
+ * local addr should not be an IPv6 address,
+ * since you cannot determine how to map IPv6
+ * source address to IPv4.
+ */
+ error = EINVAL;
+ goto release;
+ }
- /* KAME hack: embed scopeid */
- if (in6_embedscope(&sin6->sin6_addr, sin6, in6p, NULL) != 0) {
- error = EINVAL;
- goto release;
+ af = AF_INET;
}
if (!IN6_IS_ADDR_V4MAPPED(faddr)) {
laddr = in6_selectsrc(sin6, optp, in6p->in6p_moptions,
- NULL, &in6p->in6p_laddr, &error);
+ NULL, &in6p->in6p_laddr, &oifp, &error);
+ if (oifp && scope_ambiguous &&
+ (error = in6_setscope(&sin6->sin6_addr,
+ oifp, NULL))) {
+ goto release;
+ }
} else
laddr = &in6p->in6p_laddr; /* XXX */
if (laddr == NULL) {
diff --git a/sys/netinet6/udp6_usrreq.c b/sys/netinet6/udp6_usrreq.c
index 9c6517d..b8ca0fe 100644
--- a/sys/netinet6/udp6_usrreq.c
+++ b/sys/netinet6/udp6_usrreq.c
@@ -99,6 +99,7 @@
#include <netinet6/ip6_var.h>
#include <netinet6/in6_pcb.h>
#include <netinet6/udp6_var.h>
+#include <netinet6/scope6_var.h>
#ifdef IPSEC
#include <netinet6/ipsec.h>
@@ -472,6 +473,10 @@ udp6_getcred(SYSCTL_HANDLER_ARGS)
error = SYSCTL_IN(req, addrs, sizeof(addrs));
if (error)
return (error);
+ if ((error = sa6_embedscope(&addrs[0], ip6_use_defzone)) != 0 ||
+ (error = sa6_embedscope(&addrs[1], ip6_use_defzone)) != 0) {
+ return (error);
+ }
s = splnet();
inp = in6_pcblookup_hash(&udbinfo, &addrs[1].sin6_addr,
addrs[1].sin6_port,
diff --git a/sys/netkey/key.c b/sys/netkey/key.c
index 1a26c95..03cb075 100644
--- a/sys/netkey/key.c
+++ b/sys/netkey/key.c
@@ -69,6 +69,7 @@ __FBSDID("$FreeBSD$");
#include <netinet/ip6.h>
#include <netinet6/in6_var.h>
#include <netinet6/ip6_var.h>
+#include <netinet6/scope6_var.h>
#endif /* INET6 */
#ifdef INET
@@ -955,6 +956,13 @@ key_allocsa(family, src, dst, proto, spi)
arraysize = _ARRAYLEN(saorder_state_valid_prefer_new);
}
+ bzero(&sin, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_len = sizeof(sin);
+ bzero(&sin6, sizeof(sin6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_len = sizeof(sin6);
+
/*
* searching SAD.
* XXX: to be checked internal IP header somewhere. Also when
@@ -991,27 +999,17 @@ key_allocsa(family, src, dst, proto, spi)
/* check src address */
switch (family) {
case AF_INET:
- bzero(&sin, sizeof(sin));
- sin.sin_family = AF_INET;
- sin.sin_len = sizeof(sin);
- bcopy(src, &sin.sin_addr,
- sizeof(sin.sin_addr));
+ bcopy(src, &sin.sin_addr, sizeof(sin.sin_addr));
if (key_sockaddrcmp((struct sockaddr*)&sin,
(struct sockaddr *)&sav->sah->saidx.src, 0) != 0)
continue;
break;
case AF_INET6:
- bzero(&sin6, sizeof(sin6));
- sin6.sin6_family = AF_INET6;
- sin6.sin6_len = sizeof(sin6);
bcopy(src, &sin6.sin6_addr, sizeof(sin6.sin6_addr));
- if (IN6_IS_SCOPE_LINKLOCAL(&sin6.sin6_addr)) {
- /* kame fake scopeid */
- sin6.sin6_scope_id =
- ntohs(sin6.sin6_addr.s6_addr16[1]);
- sin6.sin6_addr.s6_addr16[1] = 0;
- }
+ sin6.sin6_scope_id = 0;
+ if (sa6_recoverscope(&sin6))
+ continue;
if (key_sockaddrcmp((struct sockaddr *)&sin6,
(struct sockaddr *)&sav->sah->saidx.src, 0) != 0)
continue;
@@ -1027,27 +1025,17 @@ key_allocsa(family, src, dst, proto, spi)
/* check dst address */
switch (family) {
case AF_INET:
- bzero(&sin, sizeof(sin));
- sin.sin_family = AF_INET;
- sin.sin_len = sizeof(sin);
- bcopy(dst, &sin.sin_addr,
- sizeof(sin.sin_addr));
+ bcopy(dst, &sin.sin_addr, sizeof(sin.sin_addr));
if (key_sockaddrcmp((struct sockaddr*)&sin,
(struct sockaddr *)&sav->sah->saidx.dst, 0) != 0)
continue;
break;
case AF_INET6:
- bzero(&sin6, sizeof(sin6));
- sin6.sin6_family = AF_INET6;
- sin6.sin6_len = sizeof(sin6);
bcopy(dst, &sin6.sin6_addr, sizeof(sin6.sin6_addr));
- if (IN6_IS_SCOPE_LINKLOCAL(&sin6.sin6_addr)) {
- /* kame fake scopeid */
- sin6.sin6_scope_id =
- ntohs(sin6.sin6_addr.s6_addr16[1]);
- sin6.sin6_addr.s6_addr16[1] = 0;
- }
+ sin6.sin6_scope_id = 0;
+ if (sa6_recoverscope(&sin6))
+ continue;
if (key_sockaddrcmp((struct sockaddr *)&sin6,
(struct sockaddr *)&sav->sah->saidx.dst, 0) != 0)
continue;
@@ -3975,6 +3963,9 @@ key_ismyaddr6(sin6)
struct in6_ifaddr *ia;
struct in6_multi *in6m;
+ if (sa6_embedscope(sin6, 0) != 0)
+ return 0;
+
for (ia = in6_ifaddr; ia; ia = ia->ia_next) {
if (key_sockaddrcmp((struct sockaddr *)&sin6,
(struct sockaddr *)&ia->ia_addr, 0) == 0)
@@ -6899,6 +6890,9 @@ key_parse(m, so)
u_int orglen;
int error;
int target;
+#ifdef INET6
+ struct sockaddr_in6 *sin6;
+#endif
/* sanity check */
if (m == NULL || so == NULL)
@@ -7087,6 +7081,19 @@ key_parse(m, so)
error = EINVAL;
goto senderror;
}
+#ifdef INET6
+ /*
+ * Check validity of the scope zone ID of the
+ * addresses, and embed the zone ID into the address
+ * if necessary.
+ */
+ sin6 = (struct sockaddr_in6 *)PFKEY_ADDR_SADDR(src0);
+ if ((error = sa6_embedscope(sin6, 0)) != 0)
+ goto senderror;
+ sin6 = (struct sockaddr_in6 *)PFKEY_ADDR_SADDR(dst0);
+ if ((error = sa6_embedscope(sin6, 0)) != 0)
+ goto senderror;
+#endif
break;
default:
ipseclog((LOG_DEBUG,
OpenPOWER on IntegriCloud