scope cleanup. with this change

- most of the kernel code will not care about the actual encoding of
  scope zone IDs and won't touch "s6_addr16[1]" directly.
- similarly, most of the kernel code will not care about link-local
  scoped addresses as a special case.
- scope boundary check will be stricter.  For example, the current
  *BSD code allows a packet with src=::1 and dst=(some global IPv6
  address) to be sent outside of the node, if the application do:
    s = socket(AF_INET6);
    bind(s, "::1");
    sendto(s, some_global_IPv6_addr);
  This is clearly wrong, since ::1 is only meaningful within a single
  node, but the current implementation of the *BSD kernel cannot
  reject this attempt.

Submitted by:	JINMEI Tatuya <jinmei__at__isl.rdc.toshiba.co.jp>
Obtained from:	KAME
This commit is contained in:
Hajimu UMEMOTO 2005-07-25 12:31:43 +00:00
parent 869de95743
commit a1f7e5f8ee
34 changed files with 1420 additions and 1240 deletions

View File

@ -74,6 +74,7 @@
#include <netinet6/in6_var.h> #include <netinet6/in6_var.h>
#include <netinet/ip6.h> #include <netinet/ip6.h>
#include <netinet6/ip6_var.h> #include <netinet6/ip6_var.h>
#include <netinet6/scope6_var.h>
#include <netinet6/in6_gif.h> #include <netinet6/in6_gif.h>
#include <netinet6/ip6protosw.h> #include <netinet6/ip6protosw.h>
#endif /* INET6 */ #endif /* INET6 */
@ -679,6 +680,13 @@ gif_ioctl(ifp, cmd, data)
if (src->sa_len > size) if (src->sa_len > size)
return EINVAL; return EINVAL;
bcopy((caddr_t)src, (caddr_t)dst, src->sa_len); 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; break;
case SIOCGIFPDSTADDR: case SIOCGIFPDSTADDR:
@ -711,6 +719,13 @@ gif_ioctl(ifp, cmd, data)
if (src->sa_len > size) if (src->sa_len > size)
return EINVAL; return EINVAL;
bcopy((caddr_t)src, (caddr_t)dst, src->sa_len); 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; break;
case SIOCGLIFPHYADDR: case SIOCGLIFPHYADDR:
@ -832,6 +847,16 @@ gif_set_tunnel(ifp, src, dst)
#endif #endif
#ifdef INET6 #ifdef INET6
case AF_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); error = in6_gif_attach(sc);
break; break;
#endif #endif

View File

@ -77,6 +77,10 @@
#include <netinet/tcp.h> #include <netinet/tcp.h>
#endif #endif
#ifdef INET6
#include <netinet6/scope6_var.h>
#endif
#if defined (__FreeBSD__) || defined (__OpenBSD__) #if defined (__FreeBSD__) || defined (__OpenBSD__)
# include <netinet/if_ether.h> # include <netinet/if_ether.h>
#else #else
@ -3571,7 +3575,7 @@ sppp_ipv6cp_RCR(struct sppp *sp, struct lcp_header *h, int len)
nohisaddr = IN6_IS_ADDR_UNSPECIFIED(&desiredaddr); nohisaddr = IN6_IS_ADDR_UNSPECIFIED(&desiredaddr);
desiredaddr.s6_addr16[0] = htons(0xfe80); 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) { if (!collision && !nohisaddr) {
/* no collision, hisaddr known - Conf-Ack */ /* no collision, hisaddr known - Conf-Ack */
@ -3714,7 +3718,7 @@ sppp_ipv6cp_RCN_nak(struct sppp *sp, struct lcp_header *h, int len)
break; break;
bzero(&suggestaddr, sizeof(suggestaddr)); bzero(&suggestaddr, sizeof(suggestaddr));
suggestaddr.s6_addr16[0] = htons(0xfe80); 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); bcopy(&p[2], &suggestaddr.s6_addr[8], 8);
sp->ipv6cp.opts |= (1 << IPV6CP_OPT_IFID); sp->ipv6cp.opts |= (1 << IPV6CP_OPT_IFID);

View File

@ -632,6 +632,7 @@ struct in6_multi;
void icmp6_init(void); void icmp6_init(void);
void icmp6_paramerror(struct mbuf *, int); void icmp6_paramerror(struct mbuf *, int);
void icmp6_error(struct mbuf *, int, int, 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); int icmp6_input(struct mbuf **, int *, int);
void icmp6_fasttimo(void); void icmp6_fasttimo(void);
void icmp6_reflect(struct mbuf *, size_t); void icmp6_reflect(struct mbuf *, size_t);

View File

@ -268,8 +268,7 @@ carp_hmac_prepare(struct carp_softc *sc)
TAILQ_FOREACH(ifa, &SC2IFP(sc)->if_addrlist, ifa_list) { TAILQ_FOREACH(ifa, &SC2IFP(sc)->if_addrlist, ifa_list) {
if (ifa->ifa_addr->sa_family == AF_INET6) { if (ifa->ifa_addr->sa_family == AF_INET6) {
in6 = ifatoia6(ifa)->ia_addr.sin6_addr; in6 = ifatoia6(ifa)->ia_addr.sin6_addr;
if (IN6_IS_ADDR_LINKLOCAL(&in6)) in6_clearscope(&in6);
in6.s6_addr16[1] = 0;
SHA1Update(&sc->sc_sha1, (void *)&in6, sizeof(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 in6_ifaddr *ia, *ia_if;
struct ip6_moptions *im6o = &sc->sc_im6o; struct ip6_moptions *im6o = &sc->sc_im6o;
struct in6_multi_mship *imm; struct in6_multi_mship *imm;
struct sockaddr_in6 addr; struct in6_addr in6;
int own, error; int own, error;
if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { 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; im6o->im6o_multicast_ifp = ifp;
/* join CARP multicast address */ /* join CARP multicast address */
bzero(&addr, sizeof(addr)); bzero(&in6, sizeof(in6));
addr.sin6_family = AF_INET6; in6.s6_addr16[0] = htons(0xff02);
addr.sin6_len = sizeof(addr); in6.s6_addr8[15] = 0x12;
addr.sin6_addr.s6_addr16[0] = htons(0xff02); if (in6_setscope(&in6, ifp, NULL) != 0)
addr.sin6_addr.s6_addr16[1] = htons(ifp->if_index); goto cleanup;
addr.sin6_addr.s6_addr8[15] = 0x12; if ((imm = in6_joingroup(ifp, &in6, &error)) == NULL)
if ((imm = in6_joingroup(ifp, &addr.sin6_addr, &error)) == NULL)
goto cleanup; goto cleanup;
LIST_INSERT_HEAD(&im6o->im6o_memberships, imm, i6mm_chain); LIST_INSERT_HEAD(&im6o->im6o_memberships, imm, i6mm_chain);
/* join solicited multicast address */ /* join solicited multicast address */
bzero(&addr.sin6_addr, sizeof(addr.sin6_addr)); bzero(&in6, sizeof(in6));
addr.sin6_addr.s6_addr16[0] = htons(0xff02); in6.s6_addr16[0] = htons(0xff02);
addr.sin6_addr.s6_addr16[1] = htons(ifp->if_index); in6.s6_addr32[1] = 0;
addr.sin6_addr.s6_addr32[1] = 0; in6.s6_addr32[2] = htonl(1);
addr.sin6_addr.s6_addr32[2] = htonl(1); in6.s6_addr32[3] = sin6->sin6_addr.s6_addr32[3];
addr.sin6_addr.s6_addr32[3] = sin6->sin6_addr.s6_addr32[3]; in6.s6_addr8[12] = 0xff;
addr.sin6_addr.s6_addr8[12] = 0xff; if (in6_setscope(&in6, ifp, NULL) != 0)
if ((imm = in6_joingroup(ifp, &addr.sin6_addr, &error)) == NULL) goto cleanup;
if ((imm = in6_joingroup(ifp, &in6, &error)) == NULL)
goto cleanup; goto cleanup;
LIST_INSERT_HEAD(&im6o->im6o_memberships, imm, i6mm_chain); LIST_INSERT_HEAD(&im6o->im6o_memberships, imm, i6mm_chain);
} }

View File

@ -74,6 +74,7 @@
#include <netinet/ip_var.h> #include <netinet/ip_var.h>
#ifdef INET6 #ifdef INET6
#include <netinet6/ip6_var.h> #include <netinet6/ip6_var.h>
#include <netinet6/scope6_var.h>
#include <netinet6/nd6.h> #include <netinet6/nd6.h>
#endif #endif
#include <netinet/ip_icmp.h> #include <netinet/ip_icmp.h>
@ -1044,19 +1045,17 @@ tcp6_getcred(SYSCTL_HANDLER_ARGS)
error = SYSCTL_IN(req, addrs, sizeof(addrs)); error = SYSCTL_IN(req, addrs, sizeof(addrs));
if (error) if (error)
return (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[0].sin6_addr)) {
if (IN6_IS_ADDR_V4MAPPED(&addrs[1].sin6_addr)) if (IN6_IS_ADDR_V4MAPPED(&addrs[1].sin6_addr))
mapped = 1; mapped = 1;
else else
return (EINVAL); 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); INP_INFO_RLOCK(&tcbinfo);
if (mapped == 1) if (mapped == 1)
inp = in_pcblookup_hash(&tcbinfo, inp = in_pcblookup_hash(&tcbinfo,
@ -2191,12 +2190,11 @@ sysctl_drop(SYSCTL_HANDLER_ARGS)
lin = (struct sockaddr_in *)&addrs[1]; lin = (struct sockaddr_in *)&addrs[1];
break; break;
} }
error = in6_embedscope(&f6, fin6, NULL, NULL); error = sa6_embedscope(fin6, ip6_use_defzone);
if (error) if (error)
return (EINVAL); return (error);
error = in6_embedscope(&l6, lin6, NULL, NULL); error = sa6_embedscope(lin6, ip6_use_defzone);
if (error) return (error);
return (EINVAL);
break; break;
#endif #endif
case AF_INET: case AF_INET:

View File

@ -74,6 +74,7 @@
#include <netinet/ip_var.h> #include <netinet/ip_var.h>
#ifdef INET6 #ifdef INET6
#include <netinet6/ip6_var.h> #include <netinet6/ip6_var.h>
#include <netinet6/scope6_var.h>
#include <netinet6/nd6.h> #include <netinet6/nd6.h>
#endif #endif
#include <netinet/ip_icmp.h> #include <netinet/ip_icmp.h>
@ -1044,19 +1045,17 @@ tcp6_getcred(SYSCTL_HANDLER_ARGS)
error = SYSCTL_IN(req, addrs, sizeof(addrs)); error = SYSCTL_IN(req, addrs, sizeof(addrs));
if (error) if (error)
return (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[0].sin6_addr)) {
if (IN6_IS_ADDR_V4MAPPED(&addrs[1].sin6_addr)) if (IN6_IS_ADDR_V4MAPPED(&addrs[1].sin6_addr))
mapped = 1; mapped = 1;
else else
return (EINVAL); 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); INP_INFO_RLOCK(&tcbinfo);
if (mapped == 1) if (mapped == 1)
inp = in_pcblookup_hash(&tcbinfo, inp = in_pcblookup_hash(&tcbinfo,
@ -2191,12 +2190,11 @@ sysctl_drop(SYSCTL_HANDLER_ARGS)
lin = (struct sockaddr_in *)&addrs[1]; lin = (struct sockaddr_in *)&addrs[1];
break; break;
} }
error = in6_embedscope(&f6, fin6, NULL, NULL); error = sa6_embedscope(fin6, ip6_use_defzone);
if (error) if (error)
return (EINVAL); return (error);
error = in6_embedscope(&l6, lin6, NULL, NULL); error = sa6_embedscope(lin6, ip6_use_defzone);
if (error) return (error);
return (EINVAL);
break; break;
#endif #endif
case AF_INET: case AF_INET:

View File

@ -66,6 +66,7 @@
#include <netinet/ip_var.h> #include <netinet/ip_var.h>
#ifdef INET6 #ifdef INET6
#include <netinet6/ip6_var.h> #include <netinet6/ip6_var.h>
#include <netinet6/scope6_var.h>
#endif #endif
#include <netinet/tcp.h> #include <netinet/tcp.h>
#include <netinet/tcp_fsm.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 * Cannot simply call in_pcbconnect, because there might be an
* earlier incarnation of this same connection still in * earlier incarnation of this same connection still in
* TIME_WAIT state, creating an ADDRINUSE error. * TIME_WAIT state, creating an ADDRINUSE error.
* in6_pcbladdr() also handles scope zone IDs.
*/ */
error = in6_pcbladdr(inp, nam, &addr6); error = in6_pcbladdr(inp, nam, &addr6);
if (error) if (error)

View File

@ -63,6 +63,7 @@
#ifdef INET6 #ifdef INET6
#include <netinet/ip6.h> #include <netinet/ip6.h>
#include <netinet6/ip6_var.h> #include <netinet6/ip6_var.h>
#include <netinet6/scope6_var.h>
#include <netinet/icmp6.h> #include <netinet/icmp6.h>
#endif #endif

View File

@ -95,6 +95,7 @@
#include <netinet6/in6_pcb.h> #include <netinet6/in6_pcb.h>
#include <netinet6/ip6protosw.h> #include <netinet6/ip6protosw.h>
#include <netinet6/ip6_var.h> #include <netinet6/ip6_var.h>
#include <netinet6/scope6_var.h>
#include <netinet6/mld6_var.h> #include <netinet6/mld6_var.h>
#include <netinet6/nd6.h> #include <netinet6/nd6.h>
@ -204,6 +205,41 @@ icmp6_errcount(stat, type, code)
stat->icp6errs_unknown++; stat->icp6errs_unknown++;
} }
/*
* 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. * Generate an error packet of type error in response to bad IP6 packet.
*/ */
@ -1024,37 +1060,16 @@ icmp6_notify_error(mp, off, icmp6len, code)
icmp6dst.sin6_addr = eip6->ip6_dst; icmp6dst.sin6_addr = eip6->ip6_dst;
else else
icmp6dst.sin6_addr = *finaldst; icmp6dst.sin6_addr = *finaldst;
if (in6_addr2zoneid(m->m_pkthdr.rcvif, &icmp6dst.sin6_addr, if (in6_setscope(&icmp6dst.sin6_addr, m->m_pkthdr.rcvif, NULL))
&icmp6dst.sin6_scope_id))
goto freeit; goto freeit;
if (in6_embedscope(&icmp6dst.sin6_addr, &icmp6dst,
NULL, NULL)) {
/* should be impossible */
nd6log((LOG_DEBUG,
"icmp6_notify_error: in6_embedscope failed\n"));
goto freeit;
}
/*
* retrieve parameters from the inner IPv6 header, and convert
* them into sockaddr structures.
*/
bzero(&icmp6src, sizeof(icmp6src)); bzero(&icmp6src, sizeof(icmp6src));
icmp6src.sin6_len = sizeof(struct sockaddr_in6); icmp6src.sin6_len = sizeof(struct sockaddr_in6);
icmp6src.sin6_family = AF_INET6; icmp6src.sin6_family = AF_INET6;
icmp6src.sin6_addr = eip6->ip6_src; icmp6src.sin6_addr = eip6->ip6_src;
if (in6_addr2zoneid(m->m_pkthdr.rcvif, &icmp6src.sin6_addr, if (in6_setscope(&icmp6src.sin6_addr, m->m_pkthdr.rcvif, NULL))
&icmp6src.sin6_scope_id)) {
goto freeit; goto freeit;
} icmp6src.sin6_flowinfo =
if (in6_embedscope(&icmp6src.sin6_addr, &icmp6src, (eip6->ip6_flow & IPV6_FLOWLABEL_MASK);
NULL, NULL)) {
/* should be impossible */
nd6log((LOG_DEBUG,
"icmp6_notify_error: in6_embedscope failed\n"));
goto freeit;
}
icmp6src.sin6_flowinfo = (eip6->ip6_flow & IPV6_FLOWLABEL_MASK);
if (finaldst == NULL) if (finaldst == NULL)
finaldst = &eip6->ip6_dst; finaldst = &eip6->ip6_dst;
@ -1124,11 +1139,8 @@ icmp6_mtudisc_update(ip6cp, validated)
bzero(&inc, sizeof(inc)); bzero(&inc, sizeof(inc));
inc.inc_flags = 1; /* IPv6 */ inc.inc_flags = 1; /* IPv6 */
inc.inc6_faddr = *dst; inc.inc6_faddr = *dst;
/* XXX normally, this won't happen */ if (in6_setscope(&inc.inc6_faddr, m->m_pkthdr.rcvif, NULL))
if (IN6_IS_ADDR_LINKLOCAL(dst)) { return;
inc.inc6_faddr.s6_addr16[1] =
htons(m->m_pkthdr.rcvif->if_index);
}
if (mtu < tcp_maxmtu6(&inc)) { if (mtu < tcp_maxmtu6(&inc)) {
tcp_hc_updatemtu(&inc, mtu); tcp_hc_updatemtu(&inc, mtu);
@ -1161,7 +1173,7 @@ ni6_input(m, off)
struct ni_reply_fqdn *fqdn; struct ni_reply_fqdn *fqdn;
int addrs; /* for NI_QTYPE_NODEADDR */ int addrs; /* for NI_QTYPE_NODEADDR */
struct ifnet *ifp = NULL; /* 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; struct ip6_hdr *ip6;
int oldfqdn = 0; /* if 1, return pascal string (03 draft) */ int oldfqdn = 0; /* if 1, return pascal string (03 draft) */
char *subj = NULL; char *subj = NULL;
@ -1252,22 +1264,13 @@ ni6_input(m, off)
* We do not do proxy at this moment. * We do not do proxy at this moment.
*/ */
/* m_pulldown instead of copy? */ /* 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), m_copydata(m, off + sizeof(struct icmp6_nodeinfo),
subjlen, (caddr_t)&sin6_sbj.sin6_addr); subjlen, (caddr_t)&in6_subj);
if (in6_addr2zoneid(m->m_pkthdr.rcvif, if (in6_setscope(&in6_subj, m->m_pkthdr.rcvif, NULL))
&sin6_sbj.sin6_addr, &sin6_sbj.sin6_scope_id)) {
goto bad; goto bad;
}
if (in6_embedscope(&sin6_sbj.sin6_addr, &sin6_sbj,
NULL, NULL))
goto bad; /* XXX should not happen */
subj = (char *)&sin6_sbj; subj = (char *)&in6_subj;
if (IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, if (IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &in6_subj))
&sin6_sbj.sin6_addr))
break; break;
/* /*
@ -1884,11 +1887,18 @@ icmp6_rip6_input(mp, off)
} }
#endif #endif
/*
* XXX: the address may have embedded scope zone ID, which should be
* hidden from applications.
*/
bzero(&fromsa, sizeof(fromsa)); bzero(&fromsa, sizeof(fromsa));
fromsa.sin6_len = sizeof(struct sockaddr_in6);
fromsa.sin6_family = AF_INET6; fromsa.sin6_family = AF_INET6;
/* KAME hack: recover scopeid */ fromsa.sin6_len = sizeof(struct sockaddr_in6);
(void)in6_recoverscope(&fromsa, &ip6->ip6_src, m->m_pkthdr.rcvif); fromsa.sin6_addr = ip6->ip6_src;
if (sa6_recoverscope(&fromsa)) {
m_freem(m);
return (IPPROTO_DONE);
}
INP_INFO_RLOCK(&ripcbinfo); INP_INFO_RLOCK(&ripcbinfo);
LIST_FOREACH(in6p, &ripcb, inp_list) { LIST_FOREACH(in6p, &ripcb, inp_list) {
@ -2019,11 +2029,10 @@ icmp6_reflect(m, off)
struct ip6_hdr *ip6; struct ip6_hdr *ip6;
struct icmp6_hdr *icmp6; struct icmp6_hdr *icmp6;
struct in6_ifaddr *ia; struct in6_ifaddr *ia;
struct in6_addr t, *src = 0;
int plen; int plen;
int type, code; int type, code;
struct ifnet *outif = NULL; struct ifnet *outif = NULL;
struct sockaddr_in6 sa6_src, sa6_dst; struct in6_addr origdst, *src = NULL;
#ifdef COMPAT_RFC1885 #ifdef COMPAT_RFC1885
int mtu = IPV6_MMTU; int mtu = IPV6_MMTU;
struct sockaddr_in6 *sin6 = &icmp6_reflect_rt.ro_dst; 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 */ type = icmp6->icmp6_type; /* keep type for statistics */
code = icmp6->icmp6_code; /* ditto. */ code = icmp6->icmp6_code; /* ditto. */
t = ip6->ip6_dst; origdst = ip6->ip6_dst;
/* /*
* ip6_input() drops a packet if its src is multicast. * ip6_input() drops a packet if its src is multicast.
* So, the src is never multicast. * So, the src is never multicast.
*/ */
ip6->ip6_dst = ip6->ip6_src; 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 #ifdef COMPAT_RFC1885
/* /*
* xxx guess MTU * xxx guess MTU
@ -2136,29 +2123,42 @@ icmp6_reflect(m, off)
m_adj(m, mtu - m->m_pkthdr.len); m_adj(m, mtu - m->m_pkthdr.len);
} }
#endif #endif
/* /*
* If the incoming packet was addressed directly to us (i.e. unicast), * If the incoming packet was addressed directly to us (i.e. unicast),
* use dst as the src for the reply. * use dst as the src for the reply.
* The IN6_IFF_NOTREADY case should be VERY rare, but is possible * The IN6_IFF_NOTREADY case should be VERY rare, but is possible
* (for example) when we encounter an error while forwarding procedure * (for example) when we encounter an error while forwarding procedure
* destined to a duplicated address of ours. * 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_IS_ADDR_MULTICAST(&origdst)) {
if (IN6_ARE_ADDR_EQUAL(&t, &ia->ia_addr.sin6_addr) && if ((ia = ip6_getdstifaddr(m))) {
(ia->ia6_flags & (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY)) == 0) { if (!(ia->ia6_flags &
src = &t; (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY)))
break; 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; int e;
struct sockaddr_in6 sin6;
struct route_in6 ro; struct route_in6 ro;
/* /*
@ -2166,21 +2166,25 @@ icmp6_reflect(m, off)
* that we do not own. Select a source address based on the * that we do not own. Select a source address based on the
* source address of the erroneous packet. * 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)); 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) if (ro.ro_rt)
RTFREE(ro.ro_rt); /* XXX: we could use this */ RTFREE(ro.ro_rt); /* XXX: we could use this */
if (src == NULL) { if (src == NULL) {
nd6log((LOG_DEBUG, nd6log((LOG_DEBUG,
"icmp6_reflect: source can't be determined: " "icmp6_reflect: source can't be determined: "
"dst=%s, error=%d\n", "dst=%s, error=%d\n",
ip6_sprintf(&sa6_src.sin6_addr), e)); ip6_sprintf(&sin6.sin6_addr), e));
goto bad; goto bad;
} }
} }
ip6->ip6_src = *src; ip6->ip6_src = *src;
ip6->ip6_flow = 0; ip6->ip6_flow = 0;
ip6->ip6_vfc &= ~IPV6_VERSION_MASK; ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
ip6->ip6_vfc |= IPV6_VERSION; ip6->ip6_vfc |= IPV6_VERSION;
@ -2280,10 +2284,10 @@ icmp6_redirect_input(m, off)
redtgt6 = nd_rd->nd_rd_target; redtgt6 = nd_rd->nd_rd_target;
reddst6 = nd_rd->nd_rd_dst; reddst6 = nd_rd->nd_rd_dst;
if (IN6_IS_ADDR_LINKLOCAL(&redtgt6)) if (in6_setscope(&redtgt6, m->m_pkthdr.rcvif, NULL) ||
redtgt6.s6_addr16[1] = htons(ifp->if_index); in6_setscope(&reddst6, m->m_pkthdr.rcvif, NULL)) {
if (IN6_IS_ADDR_LINKLOCAL(&reddst6)) goto freeit;
reddst6.s6_addr16[1] = htons(ifp->if_index); }
/* validation */ /* validation */
if (!IN6_IS_ADDR_LINKLOCAL(&src6)) { if (!IN6_IS_ADDR_LINKLOCAL(&src6)) {
@ -2477,9 +2481,6 @@ icmp6_redirect_output(m0, rt)
src_sa.sin6_family = AF_INET6; src_sa.sin6_family = AF_INET6;
src_sa.sin6_len = sizeof(src_sa); src_sa.sin6_len = sizeof(src_sa);
src_sa.sin6_addr = sip6->ip6_src; 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) if (nd6_is_addr_neighbor(&src_sa, ifp) == 0)
goto fail; goto fail;
if (IN6_IS_ADDR_MULTICAST(&sip6->ip6_dst)) if (IN6_IS_ADDR_MULTICAST(&sip6->ip6_dst))

View File

@ -330,7 +330,7 @@ in6_control(so, cmd, data, ifp, td)
struct in6_ifreq *ifr = (struct in6_ifreq *)data; struct in6_ifreq *ifr = (struct in6_ifreq *)data;
struct in6_ifaddr *ia = NULL; struct in6_ifaddr *ia = NULL;
struct in6_aliasreq *ifra = (struct in6_aliasreq *)data; struct in6_aliasreq *ifra = (struct in6_aliasreq *)data;
int privileged; int error, privileged;
privileged = 0; privileged = 0;
if (td == NULL || !suser(td)) if (td == NULL || !suser(td))
@ -412,25 +412,15 @@ in6_control(so, cmd, data, ifp, td)
* Find address for this interface, if it exists. * Find address for this interface, if it exists.
*/ */
if (ifra->ifra_addr.sin6_family == AF_INET6) { /* XXX */ if (ifra->ifra_addr.sin6_family == AF_INET6) { /* XXX */
struct sockaddr_in6 *sa6 = int error = 0;
(struct sockaddr_in6 *)&ifra->ifra_addr;
if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr)) { if (ifra->ifra_addr.sin6_scope_id != 0)
if (sa6->sin6_addr.s6_addr16[1] == 0) { error = sa6_embedscope(&ifra->ifra_addr, 0);
/* link ID is not embedded by the user */ else
sa6->sin6_addr.s6_addr16[1] = error = in6_setscope(&ifra->ifra_addr.sin6_addr,
htons(ifp->if_index); ifp, NULL);
} else if (sa6->sin6_addr.s6_addr16[1] != if (error != 0)
htons(ifp->if_index)) { return (error);
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? */
}
}
ia = in6ifa_ifpwithaddr(ifp, &ifra->ifra_addr.sin6_addr); ia = in6ifa_ifpwithaddr(ifp, &ifra->ifra_addr.sin6_addr);
} }
@ -507,6 +497,8 @@ in6_control(so, cmd, data, ifp, td)
case SIOCGIFADDR_IN6: case SIOCGIFADDR_IN6:
ifr->ifr_addr = ia->ia_addr; ifr->ifr_addr = ia->ia_addr;
if ((error = sa6_recoverscope(&ifr->ifr_addr)) != 0)
return (error);
break; break;
case SIOCGIFDSTADDR_IN6: case SIOCGIFDSTADDR_IN6:
@ -517,6 +509,8 @@ in6_control(so, cmd, data, ifp, td)
* an error? * an error?
*/ */
ifr->ifr_dstaddr = ia->ia_dstaddr; ifr->ifr_dstaddr = ia->ia_dstaddr;
if ((error = sa6_recoverscope(&ifr->ifr_dstaddr)) != 0)
return (error);
break; break;
case SIOCGIFNETMASK_IN6: case SIOCGIFNETMASK_IN6:
@ -741,6 +735,7 @@ in6_update_ifa(ifp, ifra, ia)
struct in6_ifaddr *oia; struct in6_ifaddr *oia;
struct sockaddr_in6 dst6; struct sockaddr_in6 dst6;
struct in6_addrlifetime *lt; struct in6_addrlifetime *lt;
struct rtentry *rt;
/* Validate parameters */ /* Validate parameters */
if (ifp == NULL || ifra == NULL) /* this maybe redundant */ if (ifp == NULL || ifra == NULL) /* this maybe redundant */
@ -789,21 +784,22 @@ in6_update_ifa(ifp, ifra, ia)
dst6 = ifra->ifra_dstaddr; dst6 = ifra->ifra_dstaddr;
if ((ifp->if_flags & (IFF_POINTOPOINT|IFF_LOOPBACK)) != 0 && if ((ifp->if_flags & (IFF_POINTOPOINT|IFF_LOOPBACK)) != 0 &&
(dst6.sin6_family == AF_INET6)) { (dst6.sin6_family == AF_INET6)) {
struct in6_addr in6_tmp;
u_int32_t zoneid; u_int32_t zoneid;
if ((error = in6_recoverscope(&dst6, in6_tmp = dst6.sin6_addr;
&ifra->ifra_dstaddr.sin6_addr, ifp)) != 0) if (in6_setscope(&in6_tmp, ifp, &zoneid))
return (error); return (EINVAL); /* XXX: should be impossible */
if (in6_addr2zoneid(ifp, &dst6.sin6_addr, &zoneid))
return (EINVAL); if (dst6.sin6_scope_id != 0) {
if (dst6.sin6_scope_id == 0) /* user omit to specify the ID. */ if (dst6.sin6_scope_id != zoneid)
return (EINVAL);
} else /* user omit to specify the ID. */
dst6.sin6_scope_id = zoneid; dst6.sin6_scope_id = zoneid;
else if (dst6.sin6_scope_id != zoneid)
return (EINVAL); /* scope ID mismatch. */ /* convert into the internal form */
if ((error = in6_embedscope(&dst6.sin6_addr, &dst6, NULL, NULL)) if (sa6_embedscope(&dst6, 0))
!= 0) return (EINVAL); /* XXX: should be impossible */
return (error);
dst6.sin6_scope_id = 0; /* XXX */
} }
/* /*
* The destination address can be specified only for a p2p or a * The destination address can be specified only for a p2p or a
@ -927,10 +923,59 @@ in6_update_ifa(ifp, ifra, ia)
ia->ia_dstaddr = dst6; 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. */ /* reset the interface and routing table appropriately. */
if ((error = in6_ifinit(ifp, ia, &ifra->ifra_addr, hostIsNew)) != 0) if ((error = in6_ifinit(ifp, ia, &ifra->ifra_addr, hostIsNew)) != 0)
goto unlink; 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, * Beyond this point, we should call in6_purgeaddr upon an error,
* not just go to unlink. * not just go to unlink.
@ -939,29 +984,29 @@ in6_update_ifa(ifp, ifra, ia)
if ((ifp->if_flags & IFF_MULTICAST) != 0) { if ((ifp->if_flags & IFF_MULTICAST) != 0) {
struct sockaddr_in6 mltaddr, mltmask; struct sockaddr_in6 mltaddr, mltmask;
struct in6_multi *in6m; struct in6_multi *in6m;
struct in6_addr llsol;
if (hostIsNew) { /* join solicited multicast addr for new host id */
/* join solicited multicast addr for new host id */ bzero(&llsol, sizeof(struct in6_addr));
struct in6_addr llsol; llsol.s6_addr16[0] = htons(0xff02);
llsol.s6_addr32[1] = 0;
bzero(&llsol, sizeof(struct in6_addr)); llsol.s6_addr32[2] = htonl(1);
llsol.s6_addr16[0] = htons(0xff02); llsol.s6_addr32[3] = ifra->ifra_addr.sin6_addr.s6_addr32[3];
llsol.s6_addr16[1] = htons(ifp->if_index); llsol.s6_addr8[12] = 0xff;
llsol.s6_addr32[1] = 0; if ((error = in6_setscope(&llsol, ifp, NULL)) != 0) {
llsol.s6_addr32[2] = htonl(1); /* XXX: should not happen */
llsol.s6_addr32[3] = log(LOG_ERR, "in6_update_ifa: "
ifra->ifra_addr.sin6_addr.s6_addr32[3]; "in6_setscope failed\n");
llsol.s6_addr8[12] = 0xff; goto cleanup;
(void)in6_addmulti(&llsol, ifp, &error); }
if (error != 0) { (void)in6_addmulti(&llsol, ifp, &error);
nd6log((LOG_WARNING, if (error != 0) {
"in6_update_ifa: addmulti failed for " nd6log((LOG_WARNING,
"%s on %s (errno=%d)\n", "in6_update_ifa: addmulti failed for "
ip6_sprintf(&llsol), if_name(ifp), "%s on %s (errno=%d)\n",
error)); ip6_sprintf(&llsol), if_name(ifp),
in6_purgeaddr((struct ifaddr *)ia); error));
return (error); goto cleanup;
}
} }
bzero(&mltmask, sizeof(mltmask)); bzero(&mltmask, sizeof(mltmask));
@ -976,16 +1021,41 @@ in6_update_ifa(ifp, ifra, ia)
mltaddr.sin6_len = sizeof(struct sockaddr_in6); mltaddr.sin6_len = sizeof(struct sockaddr_in6);
mltaddr.sin6_family = AF_INET6; mltaddr.sin6_family = AF_INET6;
mltaddr.sin6_addr = in6addr_linklocal_allnodes; 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); IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m);
if (in6m == NULL) { 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); (void)in6_addmulti(&mltaddr.sin6_addr, ifp, &error);
if (error != 0) { if (error != 0) {
nd6log((LOG_WARNING, nd6log((LOG_WARNING,
@ -993,6 +1063,7 @@ in6_update_ifa(ifp, ifra, ia)
"%s on %s (errno=%d)\n", "%s on %s (errno=%d)\n",
ip6_sprintf(&mltaddr.sin6_addr), ip6_sprintf(&mltaddr.sin6_addr),
if_name(ifp), error)); 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) if (in6_nigroup(ifp, hostname, hostnamelen, &mltaddr.sin6_addr)
== 0) { == 0) {
IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m); IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m);
if (in6m == NULL && ia != NULL) { if (in6m == NULL) {
(void)in6_addmulti(&mltaddr.sin6_addr, (void)in6_addmulti(&mltaddr.sin6_addr,
ifp, &error); ifp, &error);
if (error != 0) { if (error != 0) {
@ -1012,71 +1083,53 @@ in6_update_ifa(ifp, ifra, ia)
"%s on %s (errno=%d)\n", "%s on %s (errno=%d)\n",
ip6_sprintf(&mltaddr.sin6_addr), ip6_sprintf(&mltaddr.sin6_addr),
if_name(ifp), error)); if_name(ifp), error));
goto cleanup;
} }
} }
} }
#undef hostnamelen #undef hostnamelen
/* /*
* join node-local all-nodes address, on loopback. * join interface-local all-nodes address.
* XXX: since "node-local" is obsoleted by interface-local, * (ff01::1%ifN, and ff01::%ifN/32)
* we have to join the group on every interface with
* some interface-boundary restriction.
*/ */
if (ifp->if_flags & IFF_LOOPBACK) { mltaddr.sin6_addr = in6addr_nodelocal_allnodes;
struct in6_ifaddr *ia_loop; if ((error = in6_setscope(&mltaddr.sin6_addr, ifp, NULL))
!= 0)
struct in6_addr loop6 = in6addr_loopback; goto cleanup; /* XXX: should not fail */
ia_loop = in6ifa_ifpwithaddr(ifp, &loop6); /* XXX: again, do we really need the route? */
rt = rtalloc1((struct sockaddr *)&mltaddr, 0, 0UL);
mltaddr.sin6_addr = in6addr_nodelocal_allnodes; if (rt) {
/* 32bit came from "mltmask" */
IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m); if (memcmp(&mltaddr.sin6_addr,
if (in6m == NULL && ia_loop != NULL) { &((struct sockaddr_in6 *)rt_key(rt))->sin6_addr,
rtrequest(RTM_ADD, 32 / 8)) {
(struct sockaddr *)&mltaddr, RTFREE_LOCKED(rt);
(struct sockaddr *)&ia_loop->ia_addr, rt = NULL;
(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));
}
} }
} }
} 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);
ia->ia6_flags = ifra->ifra_flags; IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m);
ia->ia6_flags &= ~IN6_IFF_DUPLICATED; /*safety*/ if (in6m == NULL) {
ia->ia6_flags &= ~IN6_IFF_NODAD; /* Mobile IPv6 */ (void)in6_addmulti(&mltaddr.sin6_addr, ifp, &error);
if (error != 0) {
ia->ia6_lifetime = ifra->ifra_lifetime; nd6log((LOG_WARNING, "in6_update_ifa: "
/* for sanity */ "addmulti failed for %s on %s "
if (ia->ia6_lifetime.ia6t_vltime != ND6_INFINITE_LIFETIME) { "(errno=%d)\n",
ia->ia6_lifetime.ia6t_expire = ip6_sprintf(&mltaddr.sin6_addr),
time_second + ia->ia6_lifetime.ia6t_vltime; if_name(ifp), error));
} else goto cleanup;
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;
/*
* 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);
} }
return (error); return (error);
@ -1089,6 +1142,10 @@ in6_update_ifa(ifp, ifra, ia)
if (hostIsNew) if (hostIsNew)
in6_unlink_ifa(ia, ifp); in6_unlink_ifa(ia, ifp);
return (error); return (error);
cleanup:
in6_purgeaddr(&ia->ia_ifa);
return error;
} }
void void
@ -1131,12 +1188,12 @@ in6_purgeaddr(ifa)
struct in6_addr llsol; struct in6_addr llsol;
bzero(&llsol, sizeof(struct in6_addr)); bzero(&llsol, sizeof(struct in6_addr));
llsol.s6_addr16[0] = htons(0xff02); llsol.s6_addr16[0] = htons(0xff02);
llsol.s6_addr16[1] = htons(ifp->if_index);
llsol.s6_addr32[1] = 0; llsol.s6_addr32[1] = 0;
llsol.s6_addr32[2] = htonl(1); llsol.s6_addr32[2] = htonl(1);
llsol.s6_addr32[3] = llsol.s6_addr32[3] =
ia->ia_addr.sin6_addr.s6_addr32[3]; ia->ia_addr.sin6_addr.s6_addr32[3];
llsol.s6_addr8[12] = 0xff; llsol.s6_addr8[12] = 0xff;
(void)in6_setscope(&llsol, ifp, NULL); /* XXX proceed anyway */
IN6_LOOKUP_MULTI(llsol, ifp, in6m); IN6_LOOKUP_MULTI(llsol, ifp, in6m);
if (in6m) if (in6m)
@ -1397,14 +1454,13 @@ in6_lifaddr_ioctl(so, cmd, data, ifp, td)
if (!cmp) if (!cmp)
break; break;
bcopy(IFA_IN6(ifa), &candidate, sizeof(candidate));
/* /*
* XXX: this is adhoc, but is necessary to allow * XXX: this is adhoc, but is necessary to allow
* a user to specify fe80::/64 (not /10) for a * a user to specify fe80::/64 (not /10) for a
* link-local address. * link-local address.
*/ */
if (IN6_IS_ADDR_LINKLOCAL(&candidate)) bcopy(IFA_IN6(ifa), &candidate, sizeof(candidate));
candidate.s6_addr16[1] = 0; in6_clearscope(&candidate);
candidate.s6_addr32[0] &= mask.s6_addr32[0]; candidate.s6_addr32[0] &= mask.s6_addr32[0];
candidate.s6_addr32[1] &= mask.s6_addr32[1]; candidate.s6_addr32[1] &= mask.s6_addr32[1];
candidate.s6_addr32[2] &= mask.s6_addr32[2]; candidate.s6_addr32[2] &= mask.s6_addr32[2];
@ -1417,27 +1473,22 @@ in6_lifaddr_ioctl(so, cmd, data, ifp, td)
ia = ifa2ia6(ifa); ia = ifa2ia6(ifa);
if (cmd == SIOCGLIFADDR) { if (cmd == SIOCGLIFADDR) {
struct sockaddr_in6 *s6; int error;
/* fill in the if_laddrreq structure */ /* fill in the if_laddrreq structure */
bcopy(&ia->ia_addr, &iflr->addr, ia->ia_addr.sin6_len); bcopy(&ia->ia_addr, &iflr->addr, ia->ia_addr.sin6_len);
s6 = (struct sockaddr_in6 *)&iflr->addr; error = sa6_recoverscope(
if (IN6_IS_ADDR_LINKLOCAL(&s6->sin6_addr)) { (struct sockaddr_in6 *)&iflr->addr);
s6->sin6_addr.s6_addr16[1] = 0; if (error != 0)
if (in6_addr2zoneid(ifp, &s6->sin6_addr, return (error);
&s6->sin6_scope_id))
return (EINVAL); /* XXX */
}
if ((ifp->if_flags & IFF_POINTOPOINT) != 0) { if ((ifp->if_flags & IFF_POINTOPOINT) != 0) {
bcopy(&ia->ia_dstaddr, &iflr->dstaddr, bcopy(&ia->ia_dstaddr, &iflr->dstaddr,
ia->ia_dstaddr.sin6_len); ia->ia_dstaddr.sin6_len);
s6 = (struct sockaddr_in6 *)&iflr->dstaddr; error = sa6_recoverscope(
if (IN6_IS_ADDR_LINKLOCAL(&s6->sin6_addr)) { (struct sockaddr_in6 *)&iflr->dstaddr);
s6->sin6_addr.s6_addr16[1] = 0; if (error != 0)
if (in6_addr2zoneid(ifp, return (error);
&s6->sin6_addr, &s6->sin6_scope_id))
return (EINVAL); /* EINVAL */
}
} else } else
bzero(&iflr->dstaddr, sizeof(iflr->dstaddr)); bzero(&iflr->dstaddr, sizeof(iflr->dstaddr));

View File

@ -590,6 +590,7 @@ struct ip6_mtuinfo {
#define IPV6CTL_RIP6STATS 36 /* raw_ip6 stats */ #define IPV6CTL_RIP6STATS 36 /* raw_ip6 stats */
#define IPV6CTL_PREFER_TEMPADDR 37 /* prefer temporary addr as src */ #define IPV6CTL_PREFER_TEMPADDR 37 /* prefer temporary addr as src */
#define IPV6CTL_ADDRCTLPOLICY 38 /* get/set address selection policy */ #define IPV6CTL_ADDRCTLPOLICY 38 /* get/set address selection policy */
#define IPV6CTL_USE_DEFAULTZONE 39 /* use default scope zone */
#define IPV6CTL_MAXFRAGS 41 /* max fragments */ #define IPV6CTL_MAXFRAGS 41 /* max fragments */

View File

@ -66,6 +66,7 @@
#include <sys/systm.h> #include <sys/systm.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <netinet/ip6.h> #include <netinet/ip6.h>
#include <netinet6/scope6_var.h>
#include <net/net_osdep.h> #include <net/net_osdep.h>
@ -97,6 +98,7 @@ in6_cksum(m, nxt, off, len)
int mlen = 0; int mlen = 0;
int byte_swapped = 0; int byte_swapped = 0;
struct ip6_hdr *ip6; struct ip6_hdr *ip6;
struct in6_addr in6;
union { union {
u_int16_t phs[4]; u_int16_t phs[4];
struct { struct {
@ -126,22 +128,27 @@ in6_cksum(m, nxt, off, len)
* First create IP6 pseudo header and calculate a summary. * First create IP6 pseudo header and calculate a summary.
*/ */
ip6 = mtod(m, struct ip6_hdr *); ip6 = mtod(m, struct ip6_hdr *);
w = (u_int16_t *)&ip6->ip6_src;
uph.ph.ph_len = htonl(len); uph.ph.ph_len = htonl(len);
uph.ph.ph_nxt = nxt; uph.ph.ph_nxt = nxt;
/* IPv6 source address */ /*
sum += w[0]; * IPv6 source address.
if (!IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) * XXX: we'd like to avoid copying the address, but we can't due to
sum += w[1]; * the possibly embedded scope zone ID.
sum += w[2]; sum += w[3]; sum += w[4]; sum += w[5]; */
sum += w[6]; sum += w[7]; 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 */ /* IPv6 destination address */
sum += w[8]; in6 = ip6->ip6_dst;
if (!IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) in6_clearscope(&in6);
sum += w[9]; w = (u_int16_t *)&in6;
sum += w[10]; sum += w[11]; sum += w[12]; sum += w[13]; sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
sum += w[14]; sum += w[15]; sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
/* Payload length and upper layer identifier */ /* Payload length and upper layer identifier */
sum += uph.phs[0]; sum += uph.phs[1]; sum += uph.phs[0]; sum += uph.phs[1];
sum += uph.phs[2]; sum += uph.phs[3]; sum += uph.phs[2]; sum += uph.phs[3];

View File

@ -436,8 +436,7 @@ in6_ifattach_linklocal(ifp, altifp)
ifra.ifra_addr.sin6_family = AF_INET6; ifra.ifra_addr.sin6_family = AF_INET6;
ifra.ifra_addr.sin6_len = sizeof(struct sockaddr_in6); 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_addr32[0] = htonl(0xfe800000);
ifra.ifra_addr.sin6_addr.s6_addr16[1] = htons(ifp->if_index); /* XXX */
ifra.ifra_addr.sin6_addr.s6_addr32[1] = 0; ifra.ifra_addr.sin6_addr.s6_addr32[1] = 0;
if ((ifp->if_flags & IFF_LOOPBACK) != 0) { if ((ifp->if_flags & IFF_LOOPBACK) != 0) {
ifra.ifra_addr.sin6_addr.s6_addr32[2] = 0; ifra.ifra_addr.sin6_addr.s6_addr32[2] = 0;
@ -449,6 +448,8 @@ in6_ifattach_linklocal(ifp, altifp)
return (-1); 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_len = sizeof(struct sockaddr_in6);
ifra.ifra_prefixmask.sin6_family = AF_INET6; ifra.ifra_prefixmask.sin6_family = AF_INET6;
@ -643,10 +644,10 @@ in6_nigroup(ifp, name, namelen, in6)
bzero(in6, sizeof(*in6)); bzero(in6, sizeof(*in6));
in6->s6_addr16[0] = htons(0xff02); in6->s6_addr16[0] = htons(0xff02);
if (ifp)
in6->s6_addr16[1] = htons(ifp->if_index);
in6->s6_addr8[11] = 2; in6->s6_addr8[11] = 2;
bcopy(digest, &in6->s6_addr32[3], sizeof(in6->s6_addr32[3])); 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; return 0;
} }
@ -841,7 +842,9 @@ in6_ifdetach(ifp)
sin6.sin6_len = sizeof(struct sockaddr_in6); sin6.sin6_len = sizeof(struct sockaddr_in6);
sin6.sin6_family = AF_INET6; sin6.sin6_family = AF_INET6;
sin6.sin6_addr = in6addr_linklocal_allnodes; 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 */ /* XXX grab lock first to avoid LOR */
if (rt_tables[AF_INET6] != NULL) { if (rt_tables[AF_INET6] != NULL) {
RADIX_NODE_HEAD_LOCK(rt_tables[AF_INET6]); RADIX_NODE_HEAD_LOCK(rt_tables[AF_INET6]);

View File

@ -96,6 +96,7 @@
#include <netinet6/nd6.h> #include <netinet6/nd6.h>
#include <netinet/in_pcb.h> #include <netinet/in_pcb.h>
#include <netinet6/in6_pcb.h> #include <netinet6/in6_pcb.h>
#include <netinet6/scope6_var.h>
#ifdef IPSEC #ifdef IPSEC
#include <netinet6/ipsec.h> #include <netinet6/ipsec.h>
@ -139,6 +140,8 @@ in6_pcbbind(inp, nam, cred)
if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0) if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0)
wild = 1; wild = 1;
if (nam) { if (nam) {
int error;
sin6 = (struct sockaddr_in6 *)nam; sin6 = (struct sockaddr_in6 *)nam;
if (nam->sa_len != sizeof(*sin6)) if (nam->sa_len != sizeof(*sin6))
return (EINVAL); return (EINVAL);
@ -148,11 +151,8 @@ in6_pcbbind(inp, nam, cred)
if (nam->sa_family != AF_INET6) if (nam->sa_family != AF_INET6)
return (EAFNOSUPPORT); return (EAFNOSUPPORT);
/* KAME hack: embed scopeid */ if ((error = sa6_embedscope(sin6, ip6_use_defzone)) != 0)
if (in6_embedscope(&sin6->sin6_addr, sin6, inp, NULL) != 0) return(error);
return EINVAL;
/* this must be cleared for ifa_ifwithaddr() */
sin6->sin6_scope_id = 0;
lport = sin6->sin6_port; lport = sin6->sin6_port;
if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) { if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) {
@ -292,8 +292,9 @@ in6_pcbladdr(inp, nam, plocal_addr6)
struct in6_addr **plocal_addr6; struct in6_addr **plocal_addr6;
{ {
register struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)nam; register struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)nam;
struct ifnet *ifp = NULL;
int error = 0; int error = 0;
struct ifnet *ifp = NULL;
int scope_ambiguous = 0;
if (nam->sa_len != sizeof (*sin6)) if (nam->sa_len != sizeof (*sin6))
return (EINVAL); return (EINVAL);
@ -302,13 +303,14 @@ in6_pcbladdr(inp, nam, plocal_addr6)
if (sin6->sin6_port == 0) if (sin6->sin6_port == 0)
return (EADDRNOTAVAIL); 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_INFO_WLOCK_ASSERT(inp->inp_pcbinfo);
INP_LOCK_ASSERT(inp); INP_LOCK_ASSERT(inp);
/* KAME hack: embed scopeid */
if (in6_embedscope(&sin6->sin6_addr, sin6, inp, &ifp) != 0)
return EINVAL;
if (in6_ifaddr) { if (in6_ifaddr) {
/* /*
* If the destination address is UNSPECIFIED addr, * 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)) if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr))
sin6->sin6_addr = in6addr_loopback; sin6->sin6_addr = in6addr_loopback;
} }
{
/* /*
* XXX: in6_selectsrc might replace the bound local address * XXX: in6_selectsrc might replace the bound local address
* with the address specified by setsockopt(IPV6_PKTINFO). * with the address specified by setsockopt(IPV6_PKTINFO).
* Is it the intended behavior? * Is it the intended behavior?
*/ */
*plocal_addr6 = in6_selectsrc(sin6, inp->in6p_outputopts, *plocal_addr6 = in6_selectsrc(sin6, inp->in6p_outputopts,
inp->in6p_moptions, NULL, inp->in6p_moptions, NULL,
&inp->in6p_laddr, &error); &inp->in6p_laddr, &ifp, &error);
if (*plocal_addr6 == 0) { if (ifp && scope_ambiguous &&
if (error == 0) (error = in6_setscope(&sin6->sin6_addr, ifp, NULL)) != 0) {
error = EADDRNOTAVAIL; return(error);
return (error);
}
/*
* Don't do pcblookup call here; return interface in
* plocal_addr6
* and exit to caller, that will do the lookup.
*/
} }
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); return (0);
} }
@ -466,12 +473,7 @@ in6_sockaddr(port, addr_p)
sin6->sin6_len = sizeof(*sin6); sin6->sin6_len = sizeof(*sin6);
sin6->sin6_port = port; sin6->sin6_port = port;
sin6->sin6_addr = *addr_p; sin6->sin6_addr = *addr_p;
if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr)) (void)sa6_recoverscope(sin6); /* XXX: should catch errors */
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;
return (struct sockaddr *)sin6; return (struct sockaddr *)sin6;
} }
@ -953,11 +955,8 @@ init_sin6(struct sockaddr_in6 *sin6, struct mbuf *m)
sin6->sin6_len = sizeof(*sin6); sin6->sin6_len = sizeof(*sin6);
sin6->sin6_family = AF_INET6; sin6->sin6_family = AF_INET6;
sin6->sin6_addr = ip->ip6_src; sin6->sin6_addr = ip->ip6_src;
if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr))
sin6->sin6_addr.s6_addr16[1] = 0; (void)sa6_recoverscope(sin6); /* XXX: should catch errors... */
sin6->sin6_scope_id =
(m->m_pkthdr.rcvif && IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr))
? m->m_pkthdr.rcvif->if_index : 0;
return; return;
} }

View File

@ -442,6 +442,8 @@ SYSCTL_STRUCT(_net_inet6_ip6, IPV6CTL_RIP6STATS, rip6stats, CTLFLAG_RD,
&rip6stat, rip6stat, ""); &rip6stat, rip6stat, "");
SYSCTL_INT(_net_inet6_ip6, IPV6CTL_PREFER_TEMPADDR, SYSCTL_INT(_net_inet6_ip6, IPV6CTL_PREFER_TEMPADDR,
prefer_tempaddr, CTLFLAG_RW, &ip6_prefer_tempaddr, 0, ""); 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, SYSCTL_INT(_net_inet6_ip6, IPV6CTL_MAXFRAGS,
maxfrags, CTLFLAG_RW, &ip6_maxfrags, 0, ""); maxfrags, CTLFLAG_RW, &ip6_maxfrags, 0, "");

View File

@ -89,6 +89,7 @@
#include <netinet/ip6.h> #include <netinet/ip6.h>
#include <netinet6/in6_pcb.h> #include <netinet6/in6_pcb.h>
#include <netinet6/ip6_var.h> #include <netinet6/ip6_var.h>
#include <netinet6/scope6_var.h>
#include <netinet6/nd6.h> #include <netinet6/nd6.h>
#ifdef ENABLE_DEFAULT_SCOPE #ifdef ENABLE_DEFAULT_SCOPE
#include <netinet6/scope6_var.h> #include <netinet6/scope6_var.h>
@ -107,6 +108,9 @@ struct in6_addrpolicy defaultaddrpolicy;
int ip6_prefer_tempaddr = 0; 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 *, static int in6_selectif __P((struct sockaddr_in6 *, struct ip6_pktopts *,
struct ip6_moptions *, struct route_in6 *ro, struct ifnet **)); 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) } while(0)
struct in6_addr * 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 sockaddr_in6 *dstsock;
struct ip6_pktopts *opts; struct ip6_pktopts *opts;
struct ip6_moptions *mopts; struct ip6_moptions *mopts;
struct route_in6 *ro; struct route_in6 *ro;
struct in6_addr *laddr; struct in6_addr *laddr;
struct ifnet **ifpp;
int *errorp; int *errorp;
{ {
struct in6_addr *dst; struct in6_addr dst;
struct ifnet *ifp = NULL; struct ifnet *ifp = NULL;
struct in6_ifaddr *ia = NULL, *ia_best = NULL; struct in6_ifaddr *ia = NULL, *ia_best = NULL;
struct in6_pktinfo *pi = 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; struct in6_addrpolicy *dst_policy = NULL, *best_policy = NULL;
u_int32_t odstzone; u_int32_t odstzone;
int prefer_tempaddr; int prefer_tempaddr;
struct sockaddr_in6 dstsock0;
dstsock0 = *dstsock; dst = dstsock->sin6_addr; /* make a copy for local operation */
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;
*errorp = 0; *errorp = 0;
if (ifpp)
*ifpp = NULL;
/* /*
* If the source address is explicitly specified by the caller, * 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 * determine the appropriate zone id of the source based on
* the zone of the destination and the outgoing interface. * 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)); bzero(&srcsock, sizeof(srcsock));
srcsock.sin6_family = AF_INET6; srcsock.sin6_family = AF_INET6;
srcsock.sin6_len = sizeof(srcsock); srcsock.sin6_len = sizeof(srcsock);
srcsock.sin6_addr = pi->ipi6_addr; srcsock.sin6_addr = pi->ipi6_addr;
if (ifp) { if (ifp) {
if (in6_addr2zoneid(ifp, &pi->ipi6_addr, *errorp = in6_setscope(&srcsock.sin6_addr, ifp, NULL);
&srcsock.sin6_scope_id)) { if (*errorp != 0)
*errorp = EINVAL; /* XXX */
return (NULL); 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)); ia6 = (struct in6_ifaddr *)ifa_ifwithaddr((struct sockaddr *)(&srcsock));
if (ia6 == NULL || if (ia6 == NULL ||
(ia6->ia6_flags & (IN6_IFF_ANYCAST | IN6_IFF_NOTREADY))) { (ia6->ia6_flags & (IN6_IFF_ANYCAST | IN6_IFF_NOTREADY))) {
@ -233,6 +216,8 @@ in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp)
return (NULL); return (NULL);
} }
pi->ipi6_addr = srcsock.sin6_addr; /* XXX: this overrides pi */ pi->ipi6_addr = srcsock.sin6_addr; /* XXX: this overrides pi */
if (ifpp)
*ifpp = ifp;
return (&ia6->ia_addr.sin6_addr); 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 */ if (ifp == NULL) /* this should not happen */
panic("in6_selectsrc: NULL ifp"); panic("in6_selectsrc: NULL ifp");
#endif #endif
if (in6_addr2zoneid(ifp, dst, &odstzone)) { /* impossible */ *errorp = in6_setscope(&dst, ifp, &odstzone);
*errorp = EIO; /* XXX */ if (*errorp != 0)
return (NULL); return (NULL);
}
for (ia = in6_ifaddr; ia; ia = ia->ia_next) { for (ia = in6_ifaddr; ia; ia = ia->ia_next) {
int new_scope = -1, new_matchlen = -1; int new_scope = -1, new_matchlen = -1;
struct in6_addrpolicy *new_policy = NULL; struct in6_addrpolicy *new_policy = NULL;
u_int32_t srczone, osrczone, dstzone; u_int32_t srczone, osrczone, dstzone;
struct in6_addr src;
struct ifnet *ifp1 = ia->ia_ifp; struct ifnet *ifp1 = ia->ia_ifp;
/* /*
@ -270,12 +256,13 @@ in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp)
* does not contain the outgoing interface. * does not contain the outgoing interface.
* XXX: we should probably use sin6_scope_id here. * XXX: we should probably use sin6_scope_id here.
*/ */
if (in6_addr2zoneid(ifp1, dst, &dstzone) || if (in6_setscope(&dst, ifp1, &dstzone) ||
odstzone != dstzone) { odstzone != dstzone) {
continue; continue;
} }
if (in6_addr2zoneid(ifp, &ia->ia_addr.sin6_addr, &osrczone) || src = ia->ia_addr.sin6_addr;
in6_addr2zoneid(ifp1, &ia->ia_addr.sin6_addr, &srczone) || if (in6_setscope(&src, ifp, &osrczone) ||
in6_setscope(&src, ifp1, &srczone) ||
osrczone != srczone) { osrczone != srczone) {
continue; continue;
} }
@ -289,7 +276,7 @@ in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp)
continue; continue;
/* Rule 1: Prefer same address */ /* 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; ia_best = ia;
BREAK(1); /* there should be no better candidate */ 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 */ /* Rule 2: Prefer appropriate scope */
if (dst_scope < 0) if (dst_scope < 0)
dst_scope = in6_addrscope(dst); dst_scope = in6_addrscope(&dst);
new_scope = in6_addrscope(&ia->ia_addr.sin6_addr); 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, new_scope) < 0) {
if (IN6_ARE_SCOPE_CMP(best_scope, dst_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 * a large number so that it is easy to assign smaller numbers
* to more preferred rules. * 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) if (best_matchlen < new_matchlen)
REPLACE(14); REPLACE(14);
if (new_matchlen < best_matchlen) if (new_matchlen < best_matchlen)
@ -418,7 +405,7 @@ in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp)
lookup_addrsel_policy(&ia_best->ia_addr)); lookup_addrsel_policy(&ia_best->ia_addr));
best_matchlen = (new_matchlen >= 0 ? new_matchlen : best_matchlen = (new_matchlen >= 0 ? new_matchlen :
in6_matchlen(&ia_best->ia_addr.sin6_addr, in6_matchlen(&ia_best->ia_addr.sin6_addr,
dst)); &dst));
next: next:
continue; continue;
@ -432,75 +419,14 @@ in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp)
return (NULL); return (NULL);
} }
if (ifpp)
*ifpp = ifp;
return (&ia->ia_addr.sin6_addr); return (&ia->ia_addr.sin6_addr);
} }
static int static int
in6_selectif(dstsock, opts, mopts, ro, retifp) selectroute(dstsock, opts, mopts, ro, retifp, retrt, clone, norouteok)
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)
struct sockaddr_in6 *dstsock; struct sockaddr_in6 *dstsock;
struct ip6_pktopts *opts; struct ip6_pktopts *opts;
struct ip6_moptions *mopts; struct ip6_moptions *mopts;
@ -508,6 +434,7 @@ in6_selectroute(dstsock, opts, mopts, ro, retifp, retrt, clone)
struct ifnet **retifp; struct ifnet **retifp;
struct rtentry **retrt; struct rtentry **retrt;
int clone; /* meaningful only for bsdi and freebsd. */ int clone; /* meaningful only for bsdi and freebsd. */
int norouteok;
{ {
int error = 0; int error = 0;
struct ifnet *ifp = NULL; 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. */ /* XXX boundary check is assumed to be already done. */
ifp = ifnet_byindex(pi->ipi6_ifindex); ifp = ifnet_byindex(pi->ipi6_ifindex);
if (ifp != NULL && 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 * we do not have to check nor get the route for
* multicast. * multicast.
@ -697,6 +625,84 @@ in6_selectroute(dstsock, opts, mopts, ro, retifp, retrt, clone)
return (error); 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: * Default hop limit selection. The precedence is as follows:
* 1. Hoplimit value specified via ioctl. * 1. Hoplimit value specified via ioctl.
@ -830,128 +836,6 @@ in6_pcbsetport(laddr, inp, cred)
return (0); 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 void
addrsel_policy_init() addrsel_policy_init()
{ {

View File

@ -611,11 +611,6 @@ void in6_ifaddloop(struct ifaddr *);
int in6_is_addr_deprecated __P((struct sockaddr_in6 *)); int in6_is_addr_deprecated __P((struct sockaddr_in6 *));
struct inpcb; 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)); int in6_src_ioctl __P((u_long, caddr_t));
#endif /* _KERNEL */ #endif /* _KERNEL */

View File

@ -59,6 +59,7 @@
#include <netinet6/in6_var.h> #include <netinet6/in6_var.h>
#include <netinet/ip6.h> #include <netinet/ip6.h>
#include <netinet6/ip6_var.h> #include <netinet6/ip6_var.h>
#include <netinet6/scope6_var.h>
#include <netinet/icmp6.h> #include <netinet/icmp6.h>
#include <netinet6/nd6.h> #include <netinet6/nd6.h>
@ -111,7 +112,8 @@ ip6_forward(m, srcrt)
int error, type = 0, code = 0; int error, type = 0, code = 0;
struct mbuf *mcopy = NULL; struct mbuf *mcopy = NULL;
struct ifnet *origifp; /* maybe unnecessary */ struct ifnet *origifp; /* maybe unnecessary */
u_int32_t srczone, dstzone; u_int32_t inzone, outzone;
struct in6_addr src_in6, dst_in6;
#ifdef IPSEC #ifdef IPSEC
struct secpolicy *sp = NULL; struct secpolicy *sp = NULL;
int ipsecrt = 0; int ipsecrt = 0;
@ -400,21 +402,29 @@ ip6_forward(m, srcrt)
#endif #endif
/* /*
* Scope check: if a packet can't be delivered to its destination * Source scope check: if a packet can't be delivered to its
* for the reason that the destination is beyond the scope of the * destination for the reason that the destination is beyond the scope
* source address, discard the packet and return an icmp6 destination * of the source address, discard the packet and return an icmp6
* unreachable error with Code 2 (beyond scope of source address). * destination unreachable error with Code 2 (beyond scope of source
* [draft-ietf-ipngwg-icmp-v3-02.txt, Section 3.1] * 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) || src_in6 = ip6->ip6_src;
in6_addr2zoneid(rt->rt_ifp, &ip6->ip6_src, &dstzone)) { if (in6_setscope(&src_in6, rt->rt_ifp, &outzone)) {
/* XXX: this should not happen */ /* XXX: this should not happen */
ip6stat.ip6s_cantforward++; ip6stat.ip6s_cantforward++;
ip6stat.ip6s_badscope++; ip6stat.ip6s_badscope++;
m_freem(m); m_freem(m);
return; 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 #ifdef IPSEC
&& !ipsecrt && !ipsecrt
#endif #endif
@ -440,6 +450,23 @@ ip6_forward(m, srcrt)
return; 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)) { if (m->m_pkthdr.len > IN6_LINKMTU(rt->rt_ifp)) {
in6_ifstat_inc(rt->rt_ifp, ifs6_in_toobig); in6_ifstat_inc(rt->rt_ifp, ifs6_in_toobig);
if (mcopy) { if (mcopy) {

View File

@ -236,8 +236,6 @@ ip6_input(m)
u_int32_t rtalert = ~0; u_int32_t rtalert = ~0;
int nxt, ours = 0; int nxt, ours = 0;
struct ifnet *deliverifp = NULL; struct ifnet *deliverifp = NULL;
struct sockaddr_in6 sa6;
u_int32_t srczone, dstzone;
struct in6_addr odst; struct in6_addr odst;
int srcrt = 0; int srcrt = 0;
@ -401,23 +399,23 @@ ip6_input(m)
#endif #endif
/* /*
* Drop packets if the link ID portion is already filled. * Disambiguate address scope zones (if there is ambiguity).
* XXX: this is technically not a good behavior. But, we internally * We first make sure that the original source or destination address
* use the field to disambiguate link-local addresses, so we cannot * is not in our internal form for scoped addresses. Such addresses
* be generous against those a bit strange 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_clearscope(&ip6->ip6_src) || in6_clearscope(&ip6->ip6_dst)) {
if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src) && ip6stat.ip6s_badscope++; /* XXX */
ip6->ip6_src.s6_addr16[1]) { goto bad;
ip6stat.ip6s_badscope++; }
goto bad; if (in6_setscope(&ip6->ip6_src, m->m_pkthdr.rcvif, NULL) ||
} in6_setscope(&ip6->ip6_dst, m->m_pkthdr.rcvif, NULL)) {
if ((IN6_IS_ADDR_MC_INTFACELOCAL(&ip6->ip6_dst) || ip6stat.ip6s_badscope++;
IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) && goto bad;
ip6->ip6_dst.s6_addr16[1]) {
ip6stat.ip6s_badscope++;
goto bad;
}
} }
/* /*
@ -456,49 +454,6 @@ ip6_input(m)
return; return;
} }
/*
* 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 * Multicast check
*/ */
@ -1363,7 +1318,8 @@ ip6_notify_pmtu(in6p, dst, mtu)
bzero(&mtuctl, sizeof(mtuctl)); /* zero-clear for safety */ bzero(&mtuctl, sizeof(mtuctl)); /* zero-clear for safety */
mtuctl.ip6m_mtu = *mtu; mtuctl.ip6m_mtu = *mtu;
mtuctl.ip6m_addr = *dst; 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), if ((m_mtu = sbcreatecontrol((caddr_t)&mtuctl, sizeof(mtuctl),
IPV6_PATHMTU, IPPROTO_IPV6)) == NULL) IPV6_PATHMTU, IPPROTO_IPV6)) == NULL)

View File

@ -109,6 +109,7 @@
#include <netinet/ip6.h> #include <netinet/ip6.h>
#include <netinet6/ip6_var.h> #include <netinet6/ip6_var.h>
#include <netinet6/scope6_var.h>
#include <netinet6/nd6.h> #include <netinet6/nd6.h>
#include <netinet6/ip6_mroute.h> #include <netinet6/ip6_mroute.h>
#include <netinet6/pim6.h> #include <netinet6/pim6.h>
@ -1290,7 +1291,9 @@ ip6_mdq(m, ifp, rt)
mifi_t mifi, iif; mifi_t mifi, iif;
struct mif6 *mifp; struct mif6 *mifp;
int plen = m->m_pkthdr.len; 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 * 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 * For each mif, forward a copy of the packet if there are group
* members downstream on the interface. * members downstream on the interface.
*/ */
if (in6_addr2zoneid(ifp, &ip6->ip6_dst, &dscopein) || src0 = ip6->ip6_src;
in6_addr2zoneid(ifp, &ip6->ip6_src, &sscopein)) dst0 = ip6->ip6_dst;
return (EINVAL); 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++) { for (mifp = mif6table, mifi = 0; mifi < nummifs; mifp++, mifi++) {
if (IF_ISSET(mifi, &rt->mf6c_ifset)) { if (IF_ISSET(mifi, &rt->mf6c_ifset)) {
u_int32_t dscopeout, sscopeout;
/* /*
* check if the outgoing packet is going to break * check if the outgoing packet is going to break
* a scope boundary. * a scope boundary.
@ -1438,14 +1443,12 @@ ip6_mdq(m, ifp, rt)
if (!(mif6table[rt->mf6c_parent].m6_flags & if (!(mif6table[rt->mf6c_parent].m6_flags &
MIFF_REGISTER) && MIFF_REGISTER) &&
!(mif6table[mifi].m6_flags & MIFF_REGISTER)) { !(mif6table[mifi].m6_flags & MIFF_REGISTER)) {
if (in6_addr2zoneid(mif6table[mifi].m6_ifp, if (in6_setscope(&src0, mif6table[mifi].m6_ifp,
&ip6->ip6_dst, &oszone) ||
&dscopeout) || in6_setscope(&dst0, mif6table[mifi].m6_ifp,
in6_addr2zoneid(mif6table[mifi].m6_ifp, &odzone) ||
&ip6->ip6_src, iszone != oszone ||
&sscopeout) || idzone != odzone) {
dscopein != dscopeout ||
sscopein != sscopeout) {
ip6stat.ip6s_badscope++; ip6stat.ip6s_badscope++;
continue; continue;
} }

View File

@ -111,6 +111,7 @@
#include <net/net_osdep.h> #include <net/net_osdep.h>
#include <netinet6/ip6protosw.h> #include <netinet6/ip6protosw.h>
#include <netinet6/scope6_var.h>
static MALLOC_DEFINE(M_IPMOPTS, "ip6_moptions", "internet multicast options"); 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; struct mbuf *m = m0;
int hlen, tlen, len, off; int hlen, tlen, len, off;
struct route_in6 ip6route; struct route_in6 ip6route;
struct sockaddr_in6 *dst; struct rtentry *rt = NULL;
struct sockaddr_in6 *dst, src_sa, dst_sa;
struct in6_addr odst; struct in6_addr odst;
int error = 0; int error = 0;
struct in6_ifaddr *ia = NULL; struct in6_ifaddr *ia = NULL;
@ -176,7 +178,8 @@ ip6_output(m0, opt, ro, flags, im6o, ifpp, inp)
int alwaysfrag, dontfrag; int alwaysfrag, dontfrag;
u_int32_t optlen = 0, plen = 0, unfragpartlen = 0; u_int32_t optlen = 0, plen = 0, unfragpartlen = 0;
struct ip6_exthdrs exthdrs; struct ip6_exthdrs exthdrs;
struct in6_addr finaldst; struct in6_addr finaldst, src0, dst0;
u_int32_t zone;
struct route_in6 *ro_pmtu = NULL; struct route_in6 *ro_pmtu = NULL;
int hdrsplit = 0; int hdrsplit = 0;
int needipsec = 0; int needipsec = 0;
@ -481,18 +484,38 @@ skip_ipsec2:;
(struct ip6_rthdr *)(mtod(exthdrs.ip6e_rthdr, (struct ip6_rthdr *)(mtod(exthdrs.ip6e_rthdr,
struct ip6_rthdr *)); struct ip6_rthdr *));
struct ip6_rthdr0 *rh0; struct ip6_rthdr0 *rh0;
struct in6_addr *addrs; struct in6_addr *addr;
struct sockaddr_in6 sa;
switch (rh->ip6r_type) { switch (rh->ip6r_type) {
case IPV6_RTHDR_TYPE_0: case IPV6_RTHDR_TYPE_0:
rh0 = (struct ip6_rthdr0 *)rh; rh0 = (struct ip6_rthdr0 *)rh;
addrs = (struct in6_addr *)(rh0 + 1); addr = (struct in6_addr *)(rh0 + 1);
ip6->ip6_dst = *addrs; /*
bcopy((caddr_t)(addrs + 1), (caddr_t)addrs, * construct a sockaddr_in6 form of
sizeof(struct in6_addr)*(rh0->ip6r0_segleft - 1) * the first hop.
); *
*(addrs + rh0->ip6r0_segleft - 1) = finaldst; * 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; break;
default: /* is it possible? */ default: /* is it possible? */
error = EINVAL; error = EINVAL;
@ -528,24 +551,6 @@ skip_ipsec2:;
dst = (struct sockaddr_in6 *)&ro->ro_dst; dst = (struct sockaddr_in6 *)&ro->ro_dst;
again: 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. * if specified, try to fill in the traffic class field.
* do not override if a non-zero value is already set. * do not override if a non-zero value is already set.
@ -623,140 +628,116 @@ skip_ipsec2:;
} }
#endif /* IPSEC */ #endif /* IPSEC */
if (!IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { /* adjust pointer */
/* Unicast */ ip6 = mtod(m, struct ip6_hdr *);
#define ifatoia6(ifa) ((struct in6_ifaddr *)(ifa)) bzero(&dst_sa, sizeof(dst_sa));
#define sin6tosa(sin6) ((struct sockaddr *)(sin6)) dst_sa.sin6_family = AF_INET6;
/* xxx dst_sa.sin6_len = sizeof(dst_sa);
* interface selection comes here dst_sa.sin6_addr = ip6->ip6_dst;
* if an interface is specified from an upper layer, if ((error = in6_selectroute(&dst_sa, opt, im6o, ro,
* ifp must point it. &ifp, &rt, 0)) != 0) {
*/ switch (error) {
if (ro->ro_rt == 0) { case EHOSTUNREACH:
/*
* non-bsdi always clone routes, if parent is
* PRF_CLONING.
*/
rtalloc((struct route *)ro);
}
if (ro->ro_rt == 0) {
ip6stat.ip6s_noroute++; ip6stat.ip6s_noroute++;
error = EHOSTUNREACH; break;
/* XXX in6_ifstat_inc(ifp, ifs6_out_discard); */ case EADDRNOTAVAIL:
goto bad; default:
break; /* XXX statistics? */
} }
/* XXX rt not locked */ if (ifp != NULL)
ia = ifatoia6(ro->ro_rt->rt_ifa); in6_ifstat_inc(ifp, ifs6_out_discard);
ifp = ro->ro_rt->rt_ifp; goto bad;
ro->ro_rt->rt_rmx.rmx_pksent++; }
if (ro->ro_rt->rt_flags & RTF_GATEWAY) if (rt == NULL) {
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);
/* /*
* Check if the outgoing interface conflicts with * If in6_selectroute() does not return a route entry,
* the interface specified by ifi6_ifindex (if specified). * dst may not have been updated.
* Note that loopback interface is always okay.
* (this may happen when we are sending a packet to one of
* our own addresses.)
*/ */
if (opt && opt->ip6po_pktinfo *dst = dst_sa; /* XXX */
&& 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; * 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++;
}
/*
* 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;
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;
routefound:
if (rt && !IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
if (opt && opt->ip6po_nextroute.ro_rt) {
/*
* The nexthop is explicitly specified by the
* application. We assume the next hop is an IPv6
* address.
*/
dst = (struct sockaddr_in6 *)opt->ip6po_nexthop;
}
else if ((rt->rt_flags & RTF_GATEWAY))
dst = (struct sockaddr_in6 *)rt->rt_gateway;
}
if (!IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
m->m_flags &= ~(M_BCAST | M_MCAST); /* just in case */
} else { } else {
/* Multicast */
struct in6_multi *in6m; struct in6_multi *in6m;
m->m_flags = (m->m_flags & ~M_BCAST) | M_MCAST; m->m_flags = (m->m_flags & ~M_BCAST) | M_MCAST;
/*
* 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;
/*
* 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);
/*
* 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)) {
/*
* If the outgoing interface is already specified,
* it should be a loopback interface.
*/
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];
}
}
if (opt && opt->ip6po_hlim != -1)
ip6->ip6_hlim = opt->ip6po_hlim & 0xff;
/*
* 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);
}
if ((flags & IPV6_FORWARDING) == 0)
in6_ifstat_inc(ifp, ifs6_out_request);
in6_ifstat_inc(ifp, ifs6_out_mcast); in6_ifstat_inc(ifp, ifs6_out_mcast);
/* /*
* Confirm that the outgoing interface supports multicast. * Confirm that the outgoing interface supports multicast.
*/ */
if ((ifp->if_flags & IFF_MULTICAST) == 0) { if (!(ifp->if_flags & IFF_MULTICAST)) {
ip6stat.ip6s_noroute++; ip6stat.ip6s_noroute++;
in6_ifstat_inc(ifp, ifs6_out_discard); in6_ifstat_inc(ifp, ifs6_out_discard);
error = ENETUNREACH; error = ENETUNREACH;
@ -785,6 +766,14 @@ skip_ipsec2:;
* if necessary. * if necessary.
*/ */
if (ip6_mrouter && (flags & IPV6_FORWARDING) == 0) { 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) { if (ip6_mforward(ip6, ifp, m) != 0) {
m_freem(m); m_freem(m);
goto done; goto done;
@ -842,44 +831,6 @@ skip_ipsec2:;
} }
} }
/* 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. * clear embedded scope identifiers if necessary.
* in6_clearscope will touch the addresses only when necessary. * in6_clearscope will touch the addresses only when necessary.
@ -2661,7 +2612,6 @@ ip6_setmoptions(optname, im6op, m)
struct ifnet *ifp; struct ifnet *ifp;
struct ip6_moptions *im6o = *im6op; struct ip6_moptions *im6o = *im6op;
struct route_in6 ro; struct route_in6 ro;
struct sockaddr_in6 *dst;
struct in6_multi_mship *imm; struct in6_multi_mship *imm;
struct thread *td = curthread; struct thread *td = curthread;
@ -2752,6 +2702,7 @@ ip6_setmoptions(optname, im6op, m)
break; break;
} }
mreq = mtod(m, struct ipv6_mreq *); mreq = mtod(m, struct ipv6_mreq *);
if (IN6_IS_ADDR_UNSPECIFIED(&mreq->ipv6mr_multiaddr)) { if (IN6_IS_ADDR_UNSPECIFIED(&mreq->ipv6mr_multiaddr)) {
/* /*
* We use the unspecified address to specify to accept * We use the unspecified address to specify to accept
@ -2767,45 +2718,46 @@ ip6_setmoptions(optname, im6op, m)
break; break;
} }
/*
* 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 * If no interface was explicitly specified, choose an
* appropriate one according to the given multicast address. * appropriate one according to the given multicast address.
*/ */
if (mreq->ipv6mr_interface == 0) { if (mreq->ipv6mr_interface == 0) {
struct sockaddr_in6 *dst;
/* /*
* If the multicast address is in node-local scope, * Look up the routing table for the
* the interface should be a loopback interface.
* Otherwise, look up the routing table for the
* address, and choose the outgoing interface. * address, and choose the outgoing interface.
* XXX: is it a good approach? * XXX: is it a good approach?
*/ */
if (IN6_IS_ADDR_MC_INTFACELOCAL(&mreq->ipv6mr_multiaddr)) { ro.ro_rt = NULL;
ifp = &loif[0]; dst = (struct sockaddr_in6 *)&ro.ro_dst;
} else { bzero(dst, sizeof(*dst));
ro.ro_rt = NULL; dst->sin6_family = AF_INET6;
dst = (struct sockaddr_in6 *)&ro.ro_dst; dst->sin6_len = sizeof(*dst);
bzero(dst, sizeof(*dst)); dst->sin6_addr = mreq->ipv6mr_multiaddr;
dst->sin6_len = sizeof(struct sockaddr_in6); rtalloc((struct route *)&ro);
dst->sin6_family = AF_INET6; if (ro.ro_rt == NULL) {
dst->sin6_addr = mreq->ipv6mr_multiaddr; error = EADDRNOTAVAIL;
rtalloc((struct route *)&ro); break;
if (ro.ro_rt == NULL) { }
error = EADDRNOTAVAIL; ifp = ro.ro_rt->rt_ifp;
break; RTFREE(ro.ro_rt);
} } else {
ifp = ro.ro_rt->rt_ifp; /*
RTFREE(ro.ro_rt); * 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); ifp = ifnet_byindex(mreq->ipv6mr_interface);
if (!ifp) {
error = ENXIO; /* XXX EINVAL? */
break;
}
}
/* /*
* See if we found an interface, and confirm that it * See if we found an interface, and confirm that it
@ -2815,14 +2767,12 @@ ip6_setmoptions(optname, im6op, m)
error = EADDRNOTAVAIL; error = EADDRNOTAVAIL;
break; break;
} }
/*
* Put interface index into the multicast address, if (in6_setscope(&mreq->ipv6mr_multiaddr, ifp, NULL)) {
* if the address has link-local scope. error = EADDRNOTAVAIL; /* XXX: should not happen */
*/ break;
if (IN6_IS_ADDR_MC_LINKLOCAL(&mreq->ipv6mr_multiaddr)) {
mreq->ipv6mr_multiaddr.s6_addr16[1] =
htons(ifp->if_index);
} }
/* /*
* See if the membership already exists. * See if the membership already exists.
*/ */
@ -2863,32 +2813,57 @@ ip6_setmoptions(optname, im6op, m)
break; break;
} }
mreq = mtod(m, struct ipv6_mreq *); 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 * If an interface address was specified, get a pointer
* to its ifnet structure. * to its ifnet structure.
*/ */
if (mreq->ipv6mr_interface < 0 if (mreq->ipv6mr_interface < 0 ||
|| if_index < mreq->ipv6mr_interface) { if_index < mreq->ipv6mr_interface) {
error = ENXIO; /* XXX EINVAL? */ error = ENXIO; /* XXX EINVAL? */
break; break;
} }
ifp = ifnet_byindex(mreq->ipv6mr_interface); if (mreq->ipv6mr_interface == 0)
/* ifp = NULL;
* Put interface index into the multicast address, else
* if the address has link-local scope. ifp = ifnet_byindex(mreq->ipv6mr_interface);
*/
if (IN6_IS_ADDR_MC_LINKLOCAL(&mreq->ipv6mr_multiaddr)) { /* Fill in the scope zone ID */
mreq->ipv6mr_multiaddr.s6_addr16[1] if (ifp) {
= htons(mreq->ipv6mr_interface); 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: case AF_INET6:
{ {
struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)buf; struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)buf;
#if 0
int error; int error;
#endif
if (sa6->sin6_len != sizeof(struct sockaddr_in6)) if (sa6->sin6_len != sizeof(struct sockaddr_in6))
return (EINVAL); return (EINVAL);
@ -3246,13 +3219,10 @@ ip6_setpktopt(optname, buf, len, opt, priv, sticky, cmsg, uproto)
IN6_IS_ADDR_MULTICAST(&sa6->sin6_addr)) { IN6_IS_ADDR_MULTICAST(&sa6->sin6_addr)) {
return (EINVAL); return (EINVAL);
} }
#if 0 if ((error = sa6_embedscope(sa6, ip6_use_defzone))
if ((error = scope6_check_id(sa6, ip6_use_defzone))
!= 0) { != 0) {
return (error); return (error);
} }
#endif
sa6->sin6_scope_id = 0; /* XXX */
break; break;
} }
case AF_LINK: /* should eventually be supported */ case AF_LINK: /* should eventually be supported */

View File

@ -322,6 +322,9 @@ extern int ip6_use_tempaddr; /* whether to use temporary addresses. */
extern int ip6_prefer_tempaddr; /* whether to prefer temporary addresses extern int ip6_prefer_tempaddr; /* whether to prefer temporary addresses
in the source address selection */ 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 pfil_head inet6_pfil_hook; /* packet filter hooks */
extern struct pr_usrreqs rip6_usrreqs; 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 in6_addr *in6_selectsrc __P((struct sockaddr_in6 *,
struct ip6_pktopts *, struct ip6_moptions *, struct route_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 *, int in6_selectroute __P((struct sockaddr_in6 *, struct ip6_pktopts *,
struct ip6_moptions *, struct route_in6 *, struct ifnet **, struct ip6_moptions *, struct route_in6 *, struct ifnet **,
struct rtentry **, int)); struct rtentry **, int));

View File

@ -73,6 +73,7 @@
#include <netinet/ip6.h> #include <netinet/ip6.h>
#ifdef INET6 #ifdef INET6
#include <netinet6/ip6_var.h> #include <netinet6/ip6_var.h>
#include <netinet6/scope6_var.h>
#endif #endif
#include <netinet/in_pcb.h> #include <netinet/in_pcb.h>
#ifdef INET6 #ifdef INET6
@ -1143,14 +1144,14 @@ ipsec6_setspidx_ipaddr(m, spidx)
bzero(sin6, sizeof(*sin6)); bzero(sin6, sizeof(*sin6));
sin6->sin6_family = AF_INET6; sin6->sin6_family = AF_INET6;
sin6->sin6_len = sizeof(struct sockaddr_in6); 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; spidx->prefs = sizeof(struct in6_addr) << 3;
sin6 = (struct sockaddr_in6 *)&spidx->dst; sin6 = (struct sockaddr_in6 *)&spidx->dst;
bzero(sin6, sizeof(*sin6)); bzero(sin6, sizeof(*sin6));
sin6->sin6_family = AF_INET6; sin6->sin6_family = AF_INET6;
sin6->sin6_len = sizeof(struct sockaddr_in6); 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; spidx->prefd = sizeof(struct in6_addr) << 3;
return 0; return 0;
@ -2177,9 +2178,11 @@ ipsec6_encapsulate(m, sav)
struct mbuf *m; struct mbuf *m;
struct secasvar *sav; struct secasvar *sav;
{ {
struct sockaddr_in6 sa6;
struct ip6_hdr *oip6; struct ip6_hdr *oip6;
struct ip6_hdr *ip6; struct ip6_hdr *ip6;
size_t plen; size_t plen;
int error;
/* can't tunnel between different AFs */ /* can't tunnel between different AFs */
if (((struct sockaddr *)&sav->sah->saidx.src)->sa_family 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_plen will be updated in ip6_output() */
} }
ip6->ip6_nxt = IPPROTO_IPV6; ip6->ip6_nxt = IPPROTO_IPV6;
in6_embedscope(&ip6->ip6_src,
(struct sockaddr_in6 *)&sav->sah->saidx.src, NULL, NULL); sa6 = *(struct sockaddr_in6 *)&sav->sah->saidx.src;
in6_embedscope(&ip6->ip6_dst, if ((error = sa6_embedscope(&sa6, 0)) != 0)
(struct sockaddr_in6 *)&sav->sah->saidx.dst, NULL, NULL); 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; ip6->ip6_hlim = IPV6_DEFHLIM;
/* XXX Should ip6_src be updated later ? */ /* XXX Should ip6_src be updated later ? */
@ -2833,14 +2843,14 @@ ipsec6_checksa(isr, state, tunnel)
sin6->sin6_len = sizeof(*sin6); sin6->sin6_len = sizeof(*sin6);
sin6->sin6_family = AF_INET6; sin6->sin6_family = AF_INET6;
sin6->sin6_port = IPSEC_PORT_ANY; 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; sin6 = (struct sockaddr_in6 *)&saidx.dst;
if (sin6->sin6_len == 0 || tunnel) { if (sin6->sin6_len == 0 || tunnel) {
sin6->sin6_len = sizeof(*sin6); sin6->sin6_len = sizeof(*sin6);
sin6->sin6_family = AF_INET6; sin6->sin6_family = AF_INET6;
sin6->sin6_port = IPSEC_PORT_ANY; sin6->sin6_port = IPSEC_PORT_ANY;
in6_recoverscope(sin6, &ip6->ip6_dst, NULL); sin6->sin6_addr = ip6->ip6_dst;
} }
return key_checkrequest(isr, &saidx); return key_checkrequest(isr, &saidx);

View File

@ -82,6 +82,7 @@
#include <netinet/in_var.h> #include <netinet/in_var.h>
#include <netinet/ip6.h> #include <netinet/ip6.h>
#include <netinet6/ip6_var.h> #include <netinet6/ip6_var.h>
#include <netinet6/scope6_var.h>
#include <netinet/icmp6.h> #include <netinet/icmp6.h>
#include <netinet6/mld6_var.h> #include <netinet6/mld6_var.h>
@ -101,9 +102,6 @@
static struct ip6_pktopts ip6_opts; static struct ip6_pktopts ip6_opts;
static int mld6_timers_are_running; 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 *); static void mld6_sendpkt(struct in6_multi *, int, const struct in6_addr *);
@ -134,6 +132,7 @@ void
mld6_start_listening(in6m) mld6_start_listening(in6m)
struct in6_multi *in6m; struct in6_multi *in6m;
{ {
struct in6_addr all_in6;
int s = splnet(); int s = splnet();
/* /*
@ -143,10 +142,15 @@ mld6_start_listening(in6m)
* MLD messages are never sent for multicast addresses whose scope is 0 * MLD messages are never sent for multicast addresses whose scope is 0
* (reserved) or 1 (node-local). * (reserved) or 1 (node-local).
*/ */
mld6_all_nodes_linklocal.s6_addr16[1] = all_in6 = in6addr_linklocal_allnodes;
htons(in6m->in6m_ifp->if_index); /* XXX */ if (in6_setscope(&all_in6, in6m->in6m_ifp, NULL)) {
if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &mld6_all_nodes_linklocal) || /* XXX: this should not happen! */
IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) < IPV6_ADDR_SCOPE_LINKLOCAL) { 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_timer = 0;
in6m->in6m_state = MLD_OTHERLISTENER; in6m->in6m_state = MLD_OTHERLISTENER;
} else { } else {
@ -164,16 +168,24 @@ void
mld6_stop_listening(in6m) mld6_stop_listening(in6m)
struct in6_multi *in6m; struct in6_multi *in6m;
{ {
mld6_all_nodes_linklocal.s6_addr16[1] = struct in6_addr allnode, allrouter;
htons(in6m->in6m_ifp->if_index); /* XXX */
mld6_all_routers_linklocal.s6_addr16[1] =
htons(in6m->in6m_ifp->if_index); /* XXX: necessary when mrouting */
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 && if (in6m->in6m_state == MLD_IREPORTEDLAST &&
(!IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &mld6_all_nodes_linklocal)) && !IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &allnode) &&
IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) > IPV6_ADDR_SCOPE_INTFACELOCAL) IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) >
mld6_sendpkt(in6m, MLD_LISTENER_DONE, IPV6_ADDR_SCOPE_INTFACELOCAL) {
&mld6_all_routers_linklocal); mld6_sendpkt(in6m, MLD_LISTENER_DONE, &allrouter);
}
} }
void void
@ -185,6 +197,7 @@ mld6_input(m, off)
struct mld_hdr *mldh; struct mld_hdr *mldh;
struct ifnet *ifp = m->m_pkthdr.rcvif; struct ifnet *ifp = m->m_pkthdr.rcvif;
struct in6_multi *in6m; struct in6_multi *in6m;
struct in6_addr mld_addr, all_in6;
struct in6_ifaddr *ia; struct in6_ifaddr *ia;
struct ifmultiaddr *ifma; struct ifmultiaddr *ifma;
int timer; /* timer value in the MLD query header */ int timer; /* timer value in the MLD query header */
@ -217,6 +230,16 @@ mld6_input(m, off)
return; return;
} }
/*
* 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 the MLD6 specification, there are 3 states and a flag.
* *
@ -233,12 +256,15 @@ mld6_input(m, off)
if (ifp->if_flags & IFF_LOOPBACK) if (ifp->if_flags & IFF_LOOPBACK)
break; break;
if (!IN6_IS_ADDR_UNSPECIFIED(&mldh->mld_addr) && if (!IN6_IS_ADDR_UNSPECIFIED(&mld_addr) &&
!IN6_IS_ADDR_MULTICAST(&mldh->mld_addr)) !IN6_IS_ADDR_MULTICAST(&mld_addr))
break; /* print error or log stat? */ break; /* print error or log stat? */
if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld_addr))
mldh->mld_addr.s6_addr16[1] = all_in6 = in6addr_linklocal_allnodes;
htons(ifp->if_index); /* XXX */ if (in6_setscope(&all_in6, ifp, NULL)) {
/* XXX: this should not happen! */
break;
}
/* /*
* - Start the timers in all of our membership records * - 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; timer = ntohs(mldh->mld_maxdelay) * PR_FASTHZ / MLD_TIMER_SCALE;
if (timer == 0 && mldh->mld_maxdelay) if (timer == 0 && mldh->mld_maxdelay)
timer = 1; timer = 1;
mld6_all_nodes_linklocal.s6_addr16[1] =
htons(ifp->if_index); /* XXX */
TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
if (ifma->ifma_addr->sa_family != AF_INET6) if (ifma->ifma_addr->sa_family != AF_INET6)
continue; continue;
in6m = (struct in6_multi *)ifma->ifma_protospec; in6m = (struct in6_multi *)ifma->ifma_protospec;
if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &all_in6) ||
&mld6_all_nodes_linklocal) ||
IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) < IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) <
IPV6_ADDR_SCOPE_LINKLOCAL) IPV6_ADDR_SCOPE_LINKLOCAL)
continue; continue;
if (IN6_IS_ADDR_UNSPECIFIED(&mldh->mld_addr) || if (IN6_IS_ADDR_UNSPECIFIED(&mld_addr) ||
IN6_ARE_ADDR_EQUAL(&mldh->mld_addr, IN6_ARE_ADDR_EQUAL(&mld_addr, &in6m->in6m_addr)) {
&in6m->in6m_addr))
{
if (timer == 0) { if (timer == 0) {
/* send a report immediately */ /* send a report immediately */
mld6_sendpkt(in6m, MLD_LISTENER_REPORT, 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; break;
case MLD_LISTENER_REPORT: case MLD_LISTENER_REPORT:
@ -314,24 +332,18 @@ mld6_input(m, off)
if (m->m_flags & M_LOOP) /* XXX: grotty flag, but efficient */ if (m->m_flags & M_LOOP) /* XXX: grotty flag, but efficient */
break; break;
if (!IN6_IS_ADDR_MULTICAST(&mldh->mld_addr)) if (!IN6_IS_ADDR_MULTICAST(&mld_addr))
break; 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 * If we belong to the group being reported, stop
* our timer for that group. * our timer for that group.
*/ */
IN6_LOOKUP_MULTI(mldh->mld_addr, ifp, in6m); IN6_LOOKUP_MULTI(mld_addr, ifp, in6m);
if (in6m) { if (in6m) {
in6m->in6m_timer = 0; /* transit to idle state */ in6m->in6m_timer = 0; /* transit to idle state */
in6m->in6m_state = MLD_OTHERLISTENER; /* clear flag */ 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; break;
default: /* this is impossible */ default: /* this is impossible */
log(LOG_ERR, "mld6_input: illegal type(%d)", mldh->mld_type); 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_maxdelay = 0;
mldh->mld_reserved = 0; mldh->mld_reserved = 0;
mldh->mld_addr = in6m->in6m_addr; mldh->mld_addr = in6m->in6m_addr;
if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld_addr)) in6_clearscope(&mldh->mld_addr); /* XXX */
mldh->mld_addr.s6_addr16[1] = 0; /* XXX */
mldh->mld_cksum = in6_cksum(mh, IPPROTO_ICMPV6, sizeof(struct ip6_hdr), mldh->mld_cksum = in6_cksum(mh, IPPROTO_ICMPV6, sizeof(struct ip6_hdr),
sizeof(struct mld_hdr)); sizeof(struct mld_hdr));

View File

@ -63,6 +63,7 @@
#include <netinet6/in6_var.h> #include <netinet6/in6_var.h>
#include <netinet/ip6.h> #include <netinet/ip6.h>
#include <netinet6/ip6_var.h> #include <netinet6/ip6_var.h>
#include <netinet6/scope6_var.h>
#include <netinet6/nd6.h> #include <netinet6/nd6.h>
#include <netinet/icmp6.h> #include <netinet/icmp6.h>
@ -444,19 +445,14 @@ nd6_timer(ignored_arg)
} else { } else {
struct mbuf *m = ln->ln_hold; struct mbuf *m = ln->ln_hold;
if (m) { if (m) {
if (rt->rt_ifp) { /*
/* * assuming every packet in ln_hold has
* Fake rcvif to make ICMP error * the same IP header
* 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);
ln->ln_hold = NULL; ln->ln_hold = NULL;
icmp6_error2(m, ICMP6_DST_UNREACH,
ICMP6_DST_UNREACH_ADDR, 0,
rt->rt_ifp);
} }
next = nd6_free(rt); next = nd6_free(rt);
} }
@ -869,13 +865,26 @@ nd6_is_new_addr_neighbor(addr, ifp)
/* /*
* A link-local address is always a neighbor. * 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. * XXX: a link does not necessarily specify a single interface.
*/ */
if (IN6_IS_ADDR_LINKLOCAL(&addr->sin6_addr) && if (IN6_IS_ADDR_LINKLOCAL(&addr->sin6_addr)) {
ntohs(*(u_int16_t *)&addr->sin6_addr.s6_addr[2]) == ifp->if_index) struct sockaddr_in6 sin6_copy;
return (1); 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, * 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 = SIN6(rt_key(rt))->sin6_addr;
llsol.s6_addr16[0] = htons(0xff02); llsol.s6_addr16[0] = htons(0xff02);
llsol.s6_addr16[1] = htons(ifp->if_index);
llsol.s6_addr32[1] = 0; llsol.s6_addr32[1] = 0;
llsol.s6_addr32[2] = htonl(1); llsol.s6_addr32[2] = htonl(1);
llsol.s6_addr8[12] = 0xff; llsol.s6_addr8[12] = 0xff;
if (in6_setscope(&llsol, ifp, NULL))
break;
if (!in6_addmulti(&llsol, ifp, &error)) { if (!in6_addmulti(&llsol, ifp, &error)) {
nd6log((LOG_ERR, "%s: failed to join " nd6log((LOG_ERR, "%s: failed to join "
"%s (errno=%d)\n", if_name(ifp), "%s (errno=%d)\n", if_name(ifp),
@ -1307,14 +1316,15 @@ nd6_rtrequest(req, rt, info)
llsol = SIN6(rt_key(rt))->sin6_addr; llsol = SIN6(rt_key(rt))->sin6_addr;
llsol.s6_addr16[0] = htons(0xff02); llsol.s6_addr16[0] = htons(0xff02);
llsol.s6_addr16[1] = htons(ifp->if_index);
llsol.s6_addr32[1] = 0; llsol.s6_addr32[1] = 0;
llsol.s6_addr32[2] = htonl(1); llsol.s6_addr32[2] = htonl(1);
llsol.s6_addr8[12] = 0xff; llsol.s6_addr8[12] = 0xff;
if (in6_setscope(&llsol, ifp, NULL) == 0) {
IN6_LOOKUP_MULTI(llsol, ifp, in6m); IN6_LOOKUP_MULTI(llsol, ifp, in6m);
if (in6m) if (in6m)
in6_delmulti(in6m); in6_delmulti(in6m);
} else
; /* XXX: should not happen. bark here? */
} }
nd6_inuse--; nd6_inuse--;
ln->ln_next->ln_prev = ln->ln_prev; ln->ln_next->ln_prev = ln->ln_prev;
@ -1386,8 +1396,7 @@ nd6_ioctl(cmd, data, ifp)
struct nd_pfxrouter *pfr; struct nd_pfxrouter *pfr;
int j; int j;
(void)in6_embedscope(&oprl->prefix[i].prefix, oprl->prefix[i].prefix = pr->ndpr_prefix.sin6_addr;
&pr->ndpr_prefix, NULL, NULL);
oprl->prefix[i].raflags = pr->ndpr_raf; oprl->prefix[i].raflags = pr->ndpr_raf;
oprl->prefix[i].prefixlen = pr->ndpr_plen; oprl->prefix[i].prefixlen = pr->ndpr_plen;
oprl->prefix[i].vltime = pr->ndpr_vltime; oprl->prefix[i].vltime = pr->ndpr_vltime;
@ -1501,17 +1510,8 @@ nd6_ioctl(cmd, data, ifp)
struct llinfo_nd6 *ln; struct llinfo_nd6 *ln;
struct in6_addr nb_addr = nbi->addr; /* make local for safety */ struct in6_addr nb_addr = nbi->addr; /* make local for safety */
/* if ((error = in6_setscope(&nb_addr, ifp, NULL)) != 0)
* XXX: KAME specific hack for scoped addresses return (error);
* 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);
}
s = splnet(); s = splnet();
if ((rt = nd6_lookup(&nb_addr, 0, ifp)) == NULL) { if ((rt = nd6_lookup(&nb_addr, 0, ifp)) == NULL) {
@ -2122,12 +2122,13 @@ nd6_sysctl_drlist(SYSCTL_HANDLER_ARGS)
bzero(d, sizeof(*d)); bzero(d, sizeof(*d));
d->rtaddr.sin6_family = AF_INET6; d->rtaddr.sin6_family = AF_INET6;
d->rtaddr.sin6_len = sizeof(d->rtaddr); d->rtaddr.sin6_len = sizeof(d->rtaddr);
if (in6_recoverscope(&d->rtaddr, &dr->rtaddr, d->rtaddr.sin6_addr = dr->rtaddr;
dr->ifp) != 0) if (sa6_recoverscope(&d->rtaddr)) {
log(LOG_ERR, log(LOG_ERR,
"scope error in " "scope error in router list (%s)\n",
"default router list (%s)\n", ip6_sprintf(&d->rtaddr.sin6_addr));
ip6_sprintf(&dr->rtaddr)); /* XXX: press on... */
}
d->flags = dr->flags; d->flags = dr->flags;
d->rtlifetime = dr->rtlifetime; d->rtlifetime = dr->rtlifetime;
d->expire = dr->expire; d->expire = dr->expire;
@ -2169,11 +2170,12 @@ nd6_sysctl_prlist(SYSCTL_HANDLER_ARGS)
sin6 = (struct sockaddr_in6 *)(p + 1); sin6 = (struct sockaddr_in6 *)(p + 1);
p->prefix = pr->ndpr_prefix; p->prefix = pr->ndpr_prefix;
if (in6_recoverscope(&p->prefix, if (sa6_recoverscope(&p->prefix)) {
&p->prefix.sin6_addr, pr->ndpr_ifp) != 0)
log(LOG_ERR, log(LOG_ERR,
"scope error in prefix list (%s)\n", "scope error in prefix list (%s)\n",
ip6_sprintf(&p->prefix.sin6_addr)); ip6_sprintf(&p->prefix.sin6_addr));
/* XXX: press on... */
}
p->raflags = pr->ndpr_raf; p->raflags = pr->ndpr_raf;
p->prefixlen = pr->ndpr_plen; p->prefixlen = pr->ndpr_plen;
p->vltime = pr->ndpr_vltime; p->vltime = pr->ndpr_vltime;
@ -2194,12 +2196,13 @@ nd6_sysctl_prlist(SYSCTL_HANDLER_ARGS)
bzero(s6, sizeof(*s6)); bzero(s6, sizeof(*s6));
s6->sin6_family = AF_INET6; s6->sin6_family = AF_INET6;
s6->sin6_len = sizeof(*sin6); s6->sin6_len = sizeof(*sin6);
if (in6_recoverscope(s6, &pfr->router->rtaddr, s6->sin6_addr = pfr->router->rtaddr;
pfr->router->ifp) != 0) if (sa6_recoverscope(s6)) {
log(LOG_ERR, log(LOG_ERR,
"scope error in " "scope error in "
"prefix list (%s)\n", "prefix list (%s)\n",
ip6_sprintf(&pfr->router->rtaddr)); ip6_sprintf(&pfr->router->rtaddr));
}
advrtrs++; advrtrs++;
} }
p->advrtrs = advrtrs; p->advrtrs = advrtrs;

View File

@ -59,6 +59,7 @@
#include <netinet6/in6_var.h> #include <netinet6/in6_var.h>
#include <netinet/ip6.h> #include <netinet/ip6.h>
#include <netinet6/ip6_var.h> #include <netinet6/ip6_var.h>
#include <netinet6/scope6_var.h>
#include <netinet6/nd6.h> #include <netinet6/nd6.h>
#include <netinet/icmp6.h> #include <netinet/icmp6.h>
@ -120,6 +121,8 @@ nd6_ns_input(m, off, icmp6len)
#endif #endif
ip6 = mtod(m, struct ip6_hdr *); /* adjust pointer for safety */ ip6 = mtod(m, struct ip6_hdr *); /* adjust pointer for safety */
taddr6 = nd_ns->nd_ns_target; taddr6 = nd_ns->nd_ns_target;
if (in6_setscope(&taddr6, ifp, NULL) != 0)
goto bad;
if (ip6->ip6_hlim != 255) { if (ip6->ip6_hlim != 255) {
nd6log((LOG_ERR, nd6log((LOG_ERR,
@ -149,9 +152,6 @@ nd6_ns_input(m, off, icmp6len)
goto bad; goto bad;
} }
if (IN6_IS_SCOPE_LINKLOCAL(&taddr6))
taddr6.s6_addr16[1] = htons(ifp->if_index);
icmp6len -= sizeof(*nd_ns); icmp6len -= sizeof(*nd_ns);
nd6_option_init(nd_ns + 1, icmp6len, &ndopts); nd6_option_init(nd_ns + 1, icmp6len, &ndopts);
if (nd6_options(&ndopts) < 0) { if (nd6_options(&ndopts) < 0) {
@ -300,9 +300,12 @@ nd6_ns_input(m, off, icmp6len)
* S bit ("solicited") must be zero. * S bit ("solicited") must be zero.
*/ */
if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) { if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) {
saddr6 = in6addr_linklocal_allnodes; struct in6_addr in6_all;
saddr6.s6_addr16[1] = htons(ifp->if_index);
nd6_na_output(ifp, &saddr6, &taddr6, 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) | ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) |
(ip6_forwarding ? ND_NA_FLAG_ROUTER : 0), (ip6_forwarding ? ND_NA_FLAG_ROUTER : 0),
tlladdr, (struct sockaddr *)proxydl); tlladdr, (struct sockaddr *)proxydl);
@ -347,12 +350,14 @@ nd6_ns_output(ifp, daddr6, taddr6, ln, dad)
struct mbuf *m; struct mbuf *m;
struct ip6_hdr *ip6; struct ip6_hdr *ip6;
struct nd_neighbor_solicit *nd_ns; struct nd_neighbor_solicit *nd_ns;
struct in6_ifaddr *ia = NULL; struct in6_addr *src, src_in;
struct ip6_moptions im6o; struct ip6_moptions im6o;
int icmp6len; int icmp6len;
int maxlen; int maxlen;
caddr_t mac; caddr_t mac;
struct ifnet *outif = NULL; struct route_in6 ro;
bzero(&ro, sizeof(ro));
if (IN6_IS_ADDR_MULTICAST(taddr6)) if (IN6_IS_ADDR_MULTICAST(taddr6))
return; return;
@ -403,11 +408,13 @@ nd6_ns_output(ifp, daddr6, taddr6, ln, dad)
ip6->ip6_dst = *daddr6; ip6->ip6_dst = *daddr6;
else { else {
ip6->ip6_dst.s6_addr16[0] = IPV6_ADDR_INT16_MLL; 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[1] = 0;
ip6->ip6_dst.s6_addr32[2] = IPV6_ADDR_INT32_ONE; ip6->ip6_dst.s6_addr32[2] = IPV6_ADDR_INT32_ONE;
ip6->ip6_dst.s6_addr32[3] = taddr6->s6_addr32[3]; ip6->ip6_dst.s6_addr32[3] = taddr6->s6_addr32[3];
ip6->ip6_dst.s6_addr8[12] = 0xff; ip6->ip6_dst.s6_addr8[12] = 0xff;
if (in6_setscope(&ip6->ip6_dst, ifp, NULL) != 0)
goto bad;
} }
if (!dad) { if (!dad) {
/* /*
@ -423,37 +430,52 @@ nd6_ns_output(ifp, daddr6, taddr6, ln, dad)
* (saddr6), if: * (saddr6), if:
* - saddr6 is given from the caller (by giving "ln"), and * - saddr6 is given from the caller (by giving "ln"), and
* - saddr6 belongs to the outgoing interface. * - 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 ip6_hdr *hip6; /* hold ip6 */
struct in6_addr *saddr6; struct in6_addr *hsrc = NULL;
if (ln && ln->ln_hold) { if (ln && ln->ln_hold) {
hip6 = mtod(ln->ln_hold, struct ip6_hdr *); hip6 = mtod(ln->ln_hold, struct ip6_hdr *);
/* XXX pullup? */ /* XXX pullup? */
if (sizeof(*hip6) < ln->ln_hold->m_len) if (sizeof(*hip6) < ln->ln_hold->m_len)
saddr6 = &hip6->ip6_src; hsrc = &hip6->ip6_src;
else else
saddr6 = NULL; hsrc = NULL;
} else }
saddr6 = NULL; if (hsrc && in6ifa_ifpwithaddr(ifp, hsrc))
if (saddr6 && in6ifa_ifpwithaddr(ifp, saddr6)) src = hsrc;
bcopy(saddr6, &ip6->ip6_src, sizeof(*saddr6));
else { else {
ia = in6_ifawithifp(ifp, &ip6->ip6_dst); int error;
if (ia == NULL) { struct sockaddr_in6 dst_sa;
m_freem(m);
return; 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 { } else {
/* /*
* Source address for DAD packet must always be IPv6 * Source address for DAD packet must always be IPv6
* unspecified address. (0::0) * 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 = (struct nd_neighbor_solicit *)(ip6 + 1);
nd_ns->nd_ns_type = ND_NEIGHBOR_SOLICIT; nd_ns->nd_ns_type = ND_NEIGHBOR_SOLICIT;
nd_ns->nd_ns_code = 0; nd_ns->nd_ns_code = 0;
@ -493,12 +515,22 @@ nd6_ns_output(ifp, daddr6, taddr6, ln, dad)
nd_ns->nd_ns_cksum = nd_ns->nd_ns_cksum =
in6_cksum(m, IPPROTO_ICMPV6, sizeof(*ip6), icmp6len); in6_cksum(m, IPPROTO_ICMPV6, sizeof(*ip6), icmp6len);
ip6_output(m, NULL, NULL, dad ? IPV6_DADOUTPUT : 0, &im6o, &outif, NULL); ip6_output(m, NULL, &ro, dad ? IPV6_DADOUTPUT : 0, &im6o, NULL, NULL);
if (outif) { icmp6_ifstat_inc(ifp, ifs6_out_msg);
icmp6_ifstat_inc(outif, ifs6_out_msg); icmp6_ifstat_inc(ifp, ifs6_out_neighborsolicit);
icmp6_ifstat_inc(outif, ifs6_out_neighborsolicit);
}
icmp6stat.icp6s_outhist[ND_NEIGHBOR_SOLICIT]++; 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; return;
} }
#endif #endif
taddr6 = nd_na->nd_na_target;
flags = nd_na->nd_na_flags_reserved; flags = nd_na->nd_na_flags_reserved;
is_router = ((flags & ND_NA_FLAG_ROUTER) != 0); is_router = ((flags & ND_NA_FLAG_ROUTER) != 0);
is_solicited = ((flags & ND_NA_FLAG_SOLICITED) != 0); is_solicited = ((flags & ND_NA_FLAG_SOLICITED) != 0);
is_override = ((flags & ND_NA_FLAG_OVERRIDE) != 0); is_override = ((flags & ND_NA_FLAG_OVERRIDE) != 0);
if (IN6_IS_SCOPE_LINKLOCAL(&taddr6)) taddr6 = nd_na->nd_na_target;
taddr6.s6_addr16[1] = htons(ifp->if_index); if (in6_setscope(&taddr6, ifp, NULL))
return; /* XXX: impossible */
if (IN6_IS_ADDR_MULTICAST(&taddr6)) { if (IN6_IS_ADDR_MULTICAST(&taddr6)) {
nd6log((LOG_ERR, nd6log((LOG_ERR,
@ -806,9 +839,9 @@ nd6_na_input(m, off, icmp6len)
* - anycast advertisement delay rule (RFC2461 7.2.7, SHOULD) * - anycast advertisement delay rule (RFC2461 7.2.7, SHOULD)
*/ */
void void
nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr, sdl0) nd6_na_output(ifp, daddr6_0, taddr6, flags, tlladdr, sdl0)
struct ifnet *ifp; struct ifnet *ifp;
const struct in6_addr *daddr6, *taddr6; const struct in6_addr *daddr6_0, *taddr6;
u_long flags; u_long flags;
int tlladdr; /* 1 if include target link-layer address */ int tlladdr; /* 1 if include target link-layer address */
struct sockaddr *sdl0; /* sockaddr_dl (= proxy NA) or NULL */ 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 mbuf *m;
struct ip6_hdr *ip6; struct ip6_hdr *ip6;
struct nd_neighbor_advert *nd_na; struct nd_neighbor_advert *nd_na;
struct in6_ifaddr *ia = NULL;
struct ip6_moptions im6o; struct ip6_moptions im6o;
int icmp6len; struct in6_addr *src, daddr6;
int maxlen; struct sockaddr_in6 dst_sa;
int icmp6len, maxlen, error;
caddr_t mac = NULL; 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 */ /* estimate the size of message */
maxlen = sizeof(*ip6) + sizeof(*nd_na); maxlen = sizeof(*ip6) + sizeof(*nd_na);
@ -846,7 +883,7 @@ nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr, sdl0)
return; return;
m->m_pkthdr.rcvif = NULL; m->m_pkthdr.rcvif = NULL;
if (IN6_IS_ADDR_MULTICAST(daddr6)) { if (IN6_IS_ADDR_MULTICAST(&daddr6)) {
m->m_flags |= M_MCAST; m->m_flags |= M_MCAST;
im6o.im6o_multicast_ifp = ifp; im6o.im6o_multicast_ifp = ifp;
im6o.im6o_multicast_hlim = 255; 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_vfc |= IPV6_VERSION;
ip6->ip6_nxt = IPPROTO_ICMPV6; ip6->ip6_nxt = IPPROTO_ICMPV6;
ip6->ip6_hlim = 255; ip6->ip6_hlim = 255;
if (IN6_IS_ADDR_UNSPECIFIED(daddr6)) { if (IN6_IS_ADDR_UNSPECIFIED(&daddr6)) {
/* reply to DAD */ /* reply to DAD */
ip6->ip6_dst.s6_addr16[0] = IPV6_ADDR_INT16_MLL; 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[1] = 0;
ip6->ip6_dst.s6_addr32[2] = 0; ip6->ip6_dst.s6_addr32[2] = 0;
ip6->ip6_dst.s6_addr32[3] = IPV6_ADDR_INT32_ONE; ip6->ip6_dst.s6_addr32[3] = IPV6_ADDR_INT32_ONE;
if (in6_setscope(&daddr6, ifp, NULL))
goto bad;
flags &= ~ND_NA_FLAG_SOLICITED; 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. * Select a source whose scope is the same as that of the dest.
*/ */
ia = in6_ifawithifp(ifp, &ip6->ip6_dst); bcopy(&dst_sa, &ro.ro_dst, sizeof(dst_sa));
if (ia == NULL) { src = in6_selectsrc(&dst_sa, NULL, NULL, &ro, NULL, NULL, &error);
m_freem(m); if (src == NULL) {
return; 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 = (struct nd_neighbor_advert *)(ip6 + 1);
nd_na->nd_na_type = ND_NEIGHBOR_ADVERT; nd_na->nd_na_type = ND_NEIGHBOR_ADVERT;
nd_na->nd_na_code = 0; nd_na->nd_na_code = 0;
@ -941,12 +988,22 @@ nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr, sdl0)
nd_na->nd_na_cksum = nd_na->nd_na_cksum =
in6_cksum(m, IPPROTO_ICMPV6, sizeof(struct ip6_hdr), icmp6len); in6_cksum(m, IPPROTO_ICMPV6, sizeof(struct ip6_hdr), icmp6len);
ip6_output(m, NULL, NULL, 0, &im6o, &outif, NULL); ip6_output(m, NULL, &ro, 0, &im6o, NULL, NULL);
if (outif) { icmp6_ifstat_inc(ifp, ifs6_out_msg);
icmp6_ifstat_inc(outif, ifs6_out_msg); icmp6_ifstat_inc(ifp, ifs6_out_neighboradvert);
icmp6_ifstat_inc(outif, ifs6_out_neighboradvert);
}
icmp6stat.icp6s_outhist[ND_NEIGHBOR_ADVERT]++; 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 caddr_t

View File

@ -1875,8 +1875,6 @@ rt6_flush(gateway, ifp)
splx(s); splx(s);
return; return;
} }
/* XXX: hack for KAME's link-local address kludge */
gateway->s6_addr16[1] = htons(ifp->if_index);
RADIX_NODE_HEAD_LOCK(rnh); RADIX_NODE_HEAD_LOCK(rnh);
rnh->rnh_walktree(rnh, rt6_deleteroute, (void *)gateway); rnh->rnh_walktree(rnh, rt6_deleteroute, (void *)gateway);

View File

@ -75,6 +75,7 @@
#include <sys/socketvar.h> #include <sys/socketvar.h>
#include <sys/sx.h> #include <sys/sx.h>
#include <sys/systm.h> #include <sys/systm.h>
#include <sys/syslog.h>
#include <net/if.h> #include <net/if.h>
#include <net/if_types.h> #include <net/if_types.h>
@ -92,9 +93,7 @@
#include <netinet6/ip6_var.h> #include <netinet6/ip6_var.h>
#include <netinet6/nd6.h> #include <netinet6/nd6.h>
#include <netinet6/raw_ip6.h> #include <netinet6/raw_ip6.h>
#ifdef ENABLE_DEFAULT_SCOPE
#include <netinet6/scope6_var.h> #include <netinet6/scope6_var.h>
#endif
#ifdef IPSEC #ifdef IPSEC
#include <netinet6/ipsec.h> #include <netinet6/ipsec.h>
@ -328,6 +327,7 @@ rip6_output(m, va_alist)
struct ifnet *oifp = NULL; struct ifnet *oifp = NULL;
int type = 0, code = 0; /* for ICMPv6 output statistics only */ int type = 0, code = 0; /* for ICMPv6 output statistics only */
int priv = 0; int priv = 0;
int scope_ambiguous = 0;
struct in6_addr *in6a; struct in6_addr *in6a;
va_list ap; va_list ap;
@ -354,6 +354,17 @@ rip6_output(m, va_alist)
} else } else
optp = in6p->in6p_outputopts; 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 * For an ICMPv6 packet, we should know its type and code
* to update statistics. * to update statistics.
@ -377,56 +388,33 @@ rip6_output(m, va_alist)
} }
ip6 = mtod(m, struct ip6_hdr *); 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. * Source address selection.
*/ */
if ((in6a = in6_selectsrc(dstsock, optp, in6p->in6p_moptions, NULL, if ((in6a = in6_selectsrc(dstsock, optp, in6p->in6p_moptions, NULL,
&in6p->in6p_laddr, &error)) == 0) { &in6p->in6p_laddr, &oifp, &error)) == NULL) {
if (error == 0) if (error == 0)
error = EADDRNOTAVAIL; error = EADDRNOTAVAIL;
goto bad; goto bad;
} }
ip6->ip6_src = *in6a; 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) | ip6->ip6_flow = (ip6->ip6_flow & ~IPV6_FLOWINFO_MASK) |
(in6p->in6p_flowinfo & IPV6_FLOWINFO_MASK); (in6p->in6p_flowinfo & IPV6_FLOWINFO_MASK);
ip6->ip6_vfc = (ip6->ip6_vfc & ~IPV6_VERSION_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 inpcb *inp = sotoinpcb(so);
struct sockaddr_in6 *addr = (struct sockaddr_in6 *)nam; struct sockaddr_in6 *addr = (struct sockaddr_in6 *)nam;
struct ifaddr *ia = NULL; struct ifaddr *ia = NULL;
int error = 0;
if (nam->sa_len != sizeof(*addr)) if (nam->sa_len != sizeof(*addr))
return EINVAL; return EINVAL;
if (TAILQ_EMPTY(&ifnet) || addr->sin6_family != AF_INET6) if (TAILQ_EMPTY(&ifnet) || addr->sin6_family != AF_INET6)
return EADDRNOTAVAIL; return EADDRNOTAVAIL;
#ifdef ENABLE_DEFAULT_SCOPE if ((error = sa6_embedscope(addr, ip6_use_defzone)) != 0)
if (addr->sin6_scope_id == 0) { /* not change if specified */ return(error);
addr->sin6_scope_id = scope6_addr2default(&addr->sin6_addr);
}
#endif
if (!IN6_IS_ADDR_UNSPECIFIED(&addr->sin6_addr) && if (!IN6_IS_ADDR_UNSPECIFIED(&addr->sin6_addr) &&
(ia = ifa_ifwithaddr((struct sockaddr *)addr)) == 0) (ia = ifa_ifwithaddr((struct sockaddr *)addr)) == 0)
return EADDRNOTAVAIL; return EADDRNOTAVAIL;
@ -681,10 +668,8 @@ rip6_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
struct inpcb *inp = sotoinpcb(so); struct inpcb *inp = sotoinpcb(so);
struct sockaddr_in6 *addr = (struct sockaddr_in6 *)nam; struct sockaddr_in6 *addr = (struct sockaddr_in6 *)nam;
struct in6_addr *in6a = NULL; struct in6_addr *in6a = NULL;
int error = 0; struct ifnet *ifp = NULL;
#ifdef ENABLE_DEFAULT_SCOPE int error = 0, scope_ambiguous = 0;
struct sockaddr_in6 tmp;
#endif
if (nam->sa_len != sizeof(*addr)) if (nam->sa_len != sizeof(*addr))
return EINVAL; return EINVAL;
@ -692,27 +677,41 @@ rip6_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
return EADDRNOTAVAIL; return EADDRNOTAVAIL;
if (addr->sin6_family != AF_INET6) if (addr->sin6_family != AF_INET6)
return EAFNOSUPPORT; return EAFNOSUPPORT;
#ifdef ENABLE_DEFAULT_SCOPE
if (addr->sin6_scope_id == 0) { /* not change if specified */ /*
/* avoid overwrites */ * Application should provide a proper zone ID or the use of
tmp = *addr; * default zone IDs should be enabled. Unfortunately, some
addr = &tmp; * applications do not behave as it should, so we need a
addr->sin6_scope_id = scope6_addr2default(&addr->sin6_addr); * workaround. Even if an appropriate ID is not determined,
} * we'll see if we can determine the outgoing interface. If we
#endif * 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_INFO_WLOCK(&ripcbinfo);
INP_LOCK(inp); INP_LOCK(inp);
/* Source address selection. XXX: need pcblookup? */ /* Source address selection. XXX: need pcblookup? */
in6a = in6_selectsrc(addr, inp->in6p_outputopts, in6a = in6_selectsrc(addr, inp->in6p_outputopts,
inp->in6p_moptions, NULL, inp->in6p_moptions, NULL,
&inp->in6p_laddr, &error); &inp->in6p_laddr, &ifp, &error);
if (in6a == NULL) { if (in6a == NULL) {
INP_UNLOCK(inp); INP_UNLOCK(inp);
INP_INFO_WUNLOCK(&ripcbinfo); INP_INFO_WUNLOCK(&ripcbinfo);
return (error ? error : EADDRNOTAVAIL); 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_faddr = addr->sin6_addr;
inp->in6p_laddr = *in6a;
soisconnected(so); soisconnected(so);
INP_UNLOCK(inp); INP_UNLOCK(inp);
INP_INFO_WUNLOCK(&ripcbinfo); INP_INFO_WUNLOCK(&ripcbinfo);
@ -764,14 +763,29 @@ rip6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam,
m_freem(m); m_freem(m);
return ENOTCONN; return ENOTCONN;
} }
if (nam->sa_len != sizeof(struct sockaddr_in6)) {
INP_INFO_WUNLOCK(&ripcbinfo);
m_freem(m);
return(EINVAL);
}
tmp = *(struct sockaddr_in6 *)nam; tmp = *(struct sockaddr_in6 *)nam;
dst = &tmp; 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); ret = rip6_output(m, so, dst, control);
INP_INFO_WUNLOCK(&ripcbinfo); INP_INFO_WUNLOCK(&ripcbinfo);
return (ret); return (ret);

View File

@ -45,6 +45,7 @@
#include <netinet6/in6_var.h> #include <netinet6/in6_var.h>
#include <netinet/ip6.h> #include <netinet/ip6.h>
#include <netinet6/ip6_var.h> #include <netinet6/ip6_var.h>
#include <netinet6/scope6_var.h>
#include <netinet/icmp6.h> #include <netinet/icmp6.h>
@ -143,6 +144,7 @@ ip6_rthdr0(m, ip6, rh0)
{ {
int addrs, index; int addrs, index;
struct in6_addr *nextaddr, tmpaddr; struct in6_addr *nextaddr, tmpaddr;
struct in6_ifaddr *ifa;
if (rh0->ip6r0_segleft == 0) if (rh0->ip6r0_segleft == 0)
return (0); return (0);
@ -196,16 +198,25 @@ ip6_rthdr0(m, ip6, rh0)
return (-1); return (-1);
} }
/*
* 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. * Swap the IPv6 destination address and nextaddr. Forward the packet.
*/ */
tmpaddr = *nextaddr; tmpaddr = *nextaddr;
*nextaddr = ip6->ip6_dst; *nextaddr = ip6->ip6_dst;
if (IN6_IS_ADDR_LINKLOCAL(nextaddr)) in6_clearscope(nextaddr); /* XXX */
nextaddr->s6_addr16[1] = 0;
ip6->ip6_dst = tmpaddr; 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 #ifdef COMPAT_RFC1883
if (rh0->ip6r0_slmap[index / 8] & (1 << (7 - (index % 8)))) if (rh0->ip6r0_slmap[index / 8] & (1 << (7 - (index % 8))))
@ -217,4 +228,8 @@ ip6_rthdr0(m, ip6, rh0)
#endif #endif
return (-1); /* m would be freed in ip6_forward() */ return (-1); /* m would be freed in ip6_forward() */
bad:
m_freem(m);
return (-1);
} }

View File

@ -36,6 +36,7 @@
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/systm.h> #include <sys/systm.h>
#include <sys/queue.h> #include <sys/queue.h>
#include <sys/syslog.h>
#include <net/route.h> #include <net/route.h>
#include <net/if.h> #include <net/if.h>
@ -45,6 +46,12 @@
#include <netinet6/in6_var.h> #include <netinet6/in6_var.h>
#include <netinet6/scope6_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 * The scope6_lock protects the global sid default stored in
* sid_default below. * sid_default below.
@ -254,87 +261,6 @@ in6_addrscope(addr)
return IPV6_ADDR_SCOPE_GLOBAL; 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 void
scope6_setdefault(ifp) scope6_setdefault(ifp)
struct ifnet *ifp; /* note that this might be NULL */ struct ifnet *ifp; /* note that this might be NULL */
@ -392,3 +318,169 @@ scope6_addr2default(addr)
SCOPE6_UNLOCK(); SCOPE6_UNLOCK();
return (id); 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);
}

View File

@ -51,6 +51,10 @@ void scope6_setdefault __P((struct ifnet *));
int scope6_get_default __P((struct scope6_id *)); int scope6_get_default __P((struct scope6_id *));
u_int32_t scope6_in6_addrscope __P((struct in6_addr *)); u_int32_t scope6_in6_addrscope __P((struct in6_addr *));
u_int32_t scope6_addr2default __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 /* _KERNEL */
#endif /* _NETINET6_SCOPE6_VAR_H_ */ #endif /* _NETINET6_SCOPE6_VAR_H_ */

View File

@ -96,6 +96,7 @@
#include <netinet6/udp6_var.h> #include <netinet6/udp6_var.h>
#include <netinet/icmp6.h> #include <netinet/icmp6.h>
#include <netinet6/ip6protosw.h> #include <netinet6/ip6protosw.h>
#include <netinet6/scope6_var.h>
#ifdef IPSEC #ifdef IPSEC
#include <netinet6/ipsec.h> #include <netinet6/ipsec.h>
@ -128,6 +129,9 @@ udp6_output(in6p, m, addr6, control, td)
struct ip6_hdr *ip6; struct ip6_hdr *ip6;
struct udphdr *udp6; struct udphdr *udp6;
struct in6_addr *laddr, *faddr; struct in6_addr *laddr, *faddr;
struct sockaddr_in6 *sin6 = NULL;
struct ifnet *oifp = NULL;
int scope_ambiguous = 0;
u_short fport; u_short fport;
int error = 0; int error = 0;
struct ip6_pktopts *optp, opt; struct ip6_pktopts *optp, opt;
@ -139,6 +143,29 @@ udp6_output(in6p, m, addr6, control, td)
priv = 0; priv = 0;
if (td && !suser(td)) if (td && !suser(td))
priv = 1; 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 (control) {
if ((error = ip6_setpktopts(control, &opt, if ((error = ip6_setpktopts(control, &opt,
in6p->in6p_outputopts, priv, IPPROTO_UDP)) != 0) in6p->in6p_outputopts, priv, IPPROTO_UDP)) != 0)
@ -147,7 +174,9 @@ udp6_output(in6p, m, addr6, control, td)
} else } else
optp = in6p->in6p_outputopts; optp = in6p->in6p_outputopts;
if (addr6) { if (sin6) {
faddr = &sin6->sin6_addr;
/* /*
* IPv4 version of udp_output calls in_pcbconnect in this case, * IPv4 version of udp_output calls in_pcbconnect in this case,
* which needs splnet and affects performance. * 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 in6_pcbsetport in order to fill in the local address
* and the local port. * and the local port.
*/ */
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)addr6;
if (sin6->sin6_port == 0) { if (sin6->sin6_port == 0) {
error = EADDRNOTAVAIL; error = EADDRNOTAVAIL;
goto release; goto release;
@ -168,11 +196,6 @@ udp6_output(in6p, m, addr6, control, td)
goto release; goto release;
} }
/* protect *sin6 from overwrites */
tmp = *sin6;
sin6 = &tmp;
faddr = &sin6->sin6_addr;
fport = sin6->sin6_port; /* allow 0 port */ fport = sin6->sin6_port; /* allow 0 port */
if (IN6_IS_ADDR_V4MAPPED(faddr)) { if (IN6_IS_ADDR_V4MAPPED(faddr)) {
@ -189,19 +212,30 @@ udp6_output(in6p, m, addr6, control, td)
*/ */
error = EINVAL; error = EINVAL;
goto release; 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 */ af = AF_INET;
if (in6_embedscope(&sin6->sin6_addr, sin6, in6p, NULL) != 0) {
error = EINVAL;
goto release;
} }
if (!IN6_IS_ADDR_V4MAPPED(faddr)) { if (!IN6_IS_ADDR_V4MAPPED(faddr)) {
laddr = in6_selectsrc(sin6, optp, in6p->in6p_moptions, 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 } else
laddr = &in6p->in6p_laddr; /* XXX */ laddr = &in6p->in6p_laddr; /* XXX */
if (laddr == NULL) { if (laddr == NULL) {

View File

@ -99,6 +99,7 @@
#include <netinet6/ip6_var.h> #include <netinet6/ip6_var.h>
#include <netinet6/in6_pcb.h> #include <netinet6/in6_pcb.h>
#include <netinet6/udp6_var.h> #include <netinet6/udp6_var.h>
#include <netinet6/scope6_var.h>
#ifdef IPSEC #ifdef IPSEC
#include <netinet6/ipsec.h> #include <netinet6/ipsec.h>
@ -472,6 +473,10 @@ udp6_getcred(SYSCTL_HANDLER_ARGS)
error = SYSCTL_IN(req, addrs, sizeof(addrs)); error = SYSCTL_IN(req, addrs, sizeof(addrs));
if (error) if (error)
return (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(); s = splnet();
inp = in6_pcblookup_hash(&udbinfo, &addrs[1].sin6_addr, inp = in6_pcblookup_hash(&udbinfo, &addrs[1].sin6_addr,
addrs[1].sin6_port, addrs[1].sin6_port,

View File

@ -69,6 +69,7 @@ __FBSDID("$FreeBSD$");
#include <netinet/ip6.h> #include <netinet/ip6.h>
#include <netinet6/in6_var.h> #include <netinet6/in6_var.h>
#include <netinet6/ip6_var.h> #include <netinet6/ip6_var.h>
#include <netinet6/scope6_var.h>
#endif /* INET6 */ #endif /* INET6 */
#ifdef INET #ifdef INET
@ -955,6 +956,13 @@ key_allocsa(family, src, dst, proto, spi)
arraysize = _ARRAYLEN(saorder_state_valid_prefer_new); 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. * searching SAD.
* XXX: to be checked internal IP header somewhere. Also when * XXX: to be checked internal IP header somewhere. Also when
@ -991,27 +999,17 @@ key_allocsa(family, src, dst, proto, spi)
/* check src address */ /* check src address */
switch (family) { switch (family) {
case AF_INET: case AF_INET:
bzero(&sin, sizeof(sin)); bcopy(src, &sin.sin_addr, sizeof(sin.sin_addr));
sin.sin_family = AF_INET;
sin.sin_len = sizeof(sin);
bcopy(src, &sin.sin_addr,
sizeof(sin.sin_addr));
if (key_sockaddrcmp((struct sockaddr*)&sin, if (key_sockaddrcmp((struct sockaddr*)&sin,
(struct sockaddr *)&sav->sah->saidx.src, 0) != 0) (struct sockaddr *)&sav->sah->saidx.src, 0) != 0)
continue; continue;
break; break;
case AF_INET6: 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)); bcopy(src, &sin6.sin6_addr, sizeof(sin6.sin6_addr));
if (IN6_IS_SCOPE_LINKLOCAL(&sin6.sin6_addr)) { sin6.sin6_scope_id = 0;
/* kame fake scopeid */ if (sa6_recoverscope(&sin6))
sin6.sin6_scope_id = continue;
ntohs(sin6.sin6_addr.s6_addr16[1]);
sin6.sin6_addr.s6_addr16[1] = 0;
}
if (key_sockaddrcmp((struct sockaddr *)&sin6, if (key_sockaddrcmp((struct sockaddr *)&sin6,
(struct sockaddr *)&sav->sah->saidx.src, 0) != 0) (struct sockaddr *)&sav->sah->saidx.src, 0) != 0)
continue; continue;
@ -1027,27 +1025,17 @@ key_allocsa(family, src, dst, proto, spi)
/* check dst address */ /* check dst address */
switch (family) { switch (family) {
case AF_INET: case AF_INET:
bzero(&sin, sizeof(sin)); bcopy(dst, &sin.sin_addr, sizeof(sin.sin_addr));
sin.sin_family = AF_INET;
sin.sin_len = sizeof(sin);
bcopy(dst, &sin.sin_addr,
sizeof(sin.sin_addr));
if (key_sockaddrcmp((struct sockaddr*)&sin, if (key_sockaddrcmp((struct sockaddr*)&sin,
(struct sockaddr *)&sav->sah->saidx.dst, 0) != 0) (struct sockaddr *)&sav->sah->saidx.dst, 0) != 0)
continue; continue;
break; break;
case AF_INET6: 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)); bcopy(dst, &sin6.sin6_addr, sizeof(sin6.sin6_addr));
if (IN6_IS_SCOPE_LINKLOCAL(&sin6.sin6_addr)) { sin6.sin6_scope_id = 0;
/* kame fake scopeid */ if (sa6_recoverscope(&sin6))
sin6.sin6_scope_id = continue;
ntohs(sin6.sin6_addr.s6_addr16[1]);
sin6.sin6_addr.s6_addr16[1] = 0;
}
if (key_sockaddrcmp((struct sockaddr *)&sin6, if (key_sockaddrcmp((struct sockaddr *)&sin6,
(struct sockaddr *)&sav->sah->saidx.dst, 0) != 0) (struct sockaddr *)&sav->sah->saidx.dst, 0) != 0)
continue; continue;
@ -3975,6 +3963,9 @@ key_ismyaddr6(sin6)
struct in6_ifaddr *ia; struct in6_ifaddr *ia;
struct in6_multi *in6m; struct in6_multi *in6m;
if (sa6_embedscope(sin6, 0) != 0)
return 0;
for (ia = in6_ifaddr; ia; ia = ia->ia_next) { for (ia = in6_ifaddr; ia; ia = ia->ia_next) {
if (key_sockaddrcmp((struct sockaddr *)&sin6, if (key_sockaddrcmp((struct sockaddr *)&sin6,
(struct sockaddr *)&ia->ia_addr, 0) == 0) (struct sockaddr *)&ia->ia_addr, 0) == 0)
@ -6899,6 +6890,9 @@ key_parse(m, so)
u_int orglen; u_int orglen;
int error; int error;
int target; int target;
#ifdef INET6
struct sockaddr_in6 *sin6;
#endif
/* sanity check */ /* sanity check */
if (m == NULL || so == NULL) if (m == NULL || so == NULL)
@ -7087,6 +7081,19 @@ key_parse(m, so)
error = EINVAL; error = EINVAL;
goto senderror; 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; break;
default: default:
ipseclog((LOG_DEBUG, ipseclog((LOG_DEBUG,