diff options
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, |