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 <netinet/ip6.h>
#include <netinet6/ip6_var.h>
#include <netinet6/scope6_var.h>
#include <netinet6/in6_gif.h>
#include <netinet6/ip6protosw.h>
#endif /* INET6 */
@ -679,6 +680,13 @@ gif_ioctl(ifp, cmd, data)
if (src->sa_len > size)
return EINVAL;
bcopy((caddr_t)src, (caddr_t)dst, src->sa_len);
#ifdef INET6
if (dst->sa_family == AF_INET6) {
error = sa6_recoverscope((struct sockaddr_in6 *)dst);
if (error != 0)
return (error);
}
#endif
break;
case SIOCGIFPDSTADDR:
@ -711,6 +719,13 @@ gif_ioctl(ifp, cmd, data)
if (src->sa_len > size)
return EINVAL;
bcopy((caddr_t)src, (caddr_t)dst, src->sa_len);
#ifdef INET6
if (dst->sa_family == AF_INET6) {
error = sa6_recoverscope((struct sockaddr_in6 *)dst);
if (error != 0)
return (error);
}
#endif
break;
case SIOCGLIFPHYADDR:
@ -832,6 +847,16 @@ gif_set_tunnel(ifp, src, dst)
#endif
#ifdef INET6
case AF_INET6:
/*
* Check validity of the scope zone ID of the addresses, and
* convert it into the kernel internal form if necessary.
*/
error = sa6_embedscope((struct sockaddr_in6 *)sc->gif_psrc, 0);
if (error != 0)
break;
error = sa6_embedscope((struct sockaddr_in6 *)sc->gif_pdst, 0);
if (error != 0)
break;
error = in6_gif_attach(sc);
break;
#endif

View File

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

View File

@ -632,6 +632,7 @@ struct in6_multi;
void icmp6_init(void);
void icmp6_paramerror(struct mbuf *, int);
void icmp6_error(struct mbuf *, int, int, int);
void icmp6_error2(struct mbuf *, int, int, int, struct ifnet *);
int icmp6_input(struct mbuf **, int *, int);
void icmp6_fasttimo(void);
void icmp6_reflect(struct mbuf *, size_t);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -442,6 +442,8 @@ SYSCTL_STRUCT(_net_inet6_ip6, IPV6CTL_RIP6STATS, rip6stats, CTLFLAG_RD,
&rip6stat, rip6stat, "");
SYSCTL_INT(_net_inet6_ip6, IPV6CTL_PREFER_TEMPADDR,
prefer_tempaddr, CTLFLAG_RW, &ip6_prefer_tempaddr, 0, "");
SYSCTL_INT(_net_inet6_ip6, IPV6CTL_USE_DEFAULTZONE,
use_defaultzone, CTLFLAG_RW, &ip6_use_defzone, 0,"");
SYSCTL_INT(_net_inet6_ip6, IPV6CTL_MAXFRAGS,
maxfrags, CTLFLAG_RW, &ip6_maxfrags, 0, "");

View File

@ -89,6 +89,7 @@
#include <netinet/ip6.h>
#include <netinet6/in6_pcb.h>
#include <netinet6/ip6_var.h>
#include <netinet6/scope6_var.h>
#include <netinet6/nd6.h>
#ifdef ENABLE_DEFAULT_SCOPE
#include <netinet6/scope6_var.h>
@ -107,6 +108,9 @@ struct in6_addrpolicy defaultaddrpolicy;
int ip6_prefer_tempaddr = 0;
static int selectroute __P((struct sockaddr_in6 *, struct ip6_pktopts *,
struct ip6_moptions *, struct route_in6 *, struct ifnet **,
struct rtentry **, int, int));
static int in6_selectif __P((struct sockaddr_in6 *, struct ip6_pktopts *,
struct ip6_moptions *, struct route_in6 *ro, struct ifnet **));
@ -148,15 +152,16 @@ static struct in6_addrpolicy *match_addrsel_policy __P((struct sockaddr_in6 *));
} while(0)
struct in6_addr *
in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp)
in6_selectsrc(dstsock, opts, mopts, ro, laddr, ifpp, errorp)
struct sockaddr_in6 *dstsock;
struct ip6_pktopts *opts;
struct ip6_moptions *mopts;
struct route_in6 *ro;
struct in6_addr *laddr;
struct ifnet **ifpp;
int *errorp;
{
struct in6_addr *dst;
struct in6_addr dst;
struct ifnet *ifp = NULL;
struct in6_ifaddr *ia = NULL, *ia_best = NULL;
struct in6_pktinfo *pi = NULL;
@ -164,30 +169,11 @@ in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp)
struct in6_addrpolicy *dst_policy = NULL, *best_policy = NULL;
u_int32_t odstzone;
int prefer_tempaddr;
struct sockaddr_in6 dstsock0;
dstsock0 = *dstsock;
if (IN6_IS_SCOPE_LINKLOCAL(&dstsock0.sin6_addr) ||
IN6_IS_ADDR_MC_INTFACELOCAL(&dstsock0.sin6_addr)) {
/* KAME assumption: link id == interface id */
if (opts && opts->ip6po_pktinfo &&
opts->ip6po_pktinfo->ipi6_ifindex) {
ifp = ifnet_byindex(opts->ip6po_pktinfo->ipi6_ifindex);
dstsock0.sin6_addr.s6_addr16[1] =
htons(opts->ip6po_pktinfo->ipi6_ifindex);
} else if (mopts &&
IN6_IS_ADDR_MULTICAST(&dstsock0.sin6_addr) &&
mopts->im6o_multicast_ifp) {
ifp = mopts->im6o_multicast_ifp;
dstsock0.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
} else if ((*errorp = in6_embedscope(&dstsock0.sin6_addr,
&dstsock0, NULL, NULL)) != 0)
return (NULL);
}
dstsock = &dstsock0;
dst = &dstsock->sin6_addr;
dst = dstsock->sin6_addr; /* make a copy for local operation */
*errorp = 0;
if (ifpp)
*ifpp = NULL;
/*
* If the source address is explicitly specified by the caller,
@ -209,23 +195,20 @@ in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp)
/*
* determine the appropriate zone id of the source based on
* the zone of the destination and the outgoing interface.
* If the specified address is ambiguous wrt the scope zone,
* the interface must be specified; otherwise, ifa_ifwithaddr()
* will fail matching the address.
*/
bzero(&srcsock, sizeof(srcsock));
srcsock.sin6_family = AF_INET6;
srcsock.sin6_len = sizeof(srcsock);
srcsock.sin6_addr = pi->ipi6_addr;
if (ifp) {
if (in6_addr2zoneid(ifp, &pi->ipi6_addr,
&srcsock.sin6_scope_id)) {
*errorp = EINVAL; /* XXX */
*errorp = in6_setscope(&srcsock.sin6_addr, ifp, NULL);
if (*errorp != 0)
return (NULL);
}
}
if ((*errorp = in6_embedscope(&srcsock.sin6_addr, &srcsock,
NULL, NULL)) != 0) {
return (NULL);
}
srcsock.sin6_scope_id = 0; /* XXX: ifa_ifwithaddr expects 0 */
ia6 = (struct in6_ifaddr *)ifa_ifwithaddr((struct sockaddr *)(&srcsock));
if (ia6 == NULL ||
(ia6->ia6_flags & (IN6_IFF_ANYCAST | IN6_IFF_NOTREADY))) {
@ -233,6 +216,8 @@ in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp)
return (NULL);
}
pi->ipi6_addr = srcsock.sin6_addr; /* XXX: this overrides pi */
if (ifpp)
*ifpp = ifp;
return (&ia6->ia_addr.sin6_addr);
}
@ -254,14 +239,15 @@ in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp)
if (ifp == NULL) /* this should not happen */
panic("in6_selectsrc: NULL ifp");
#endif
if (in6_addr2zoneid(ifp, dst, &odstzone)) { /* impossible */
*errorp = EIO; /* XXX */
*errorp = in6_setscope(&dst, ifp, &odstzone);
if (*errorp != 0)
return (NULL);
}
for (ia = in6_ifaddr; ia; ia = ia->ia_next) {
int new_scope = -1, new_matchlen = -1;
struct in6_addrpolicy *new_policy = NULL;
u_int32_t srczone, osrczone, dstzone;
struct in6_addr src;
struct ifnet *ifp1 = ia->ia_ifp;
/*
@ -270,12 +256,13 @@ in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp)
* does not contain the outgoing interface.
* XXX: we should probably use sin6_scope_id here.
*/
if (in6_addr2zoneid(ifp1, dst, &dstzone) ||
if (in6_setscope(&dst, ifp1, &dstzone) ||
odstzone != dstzone) {
continue;
}
if (in6_addr2zoneid(ifp, &ia->ia_addr.sin6_addr, &osrczone) ||
in6_addr2zoneid(ifp1, &ia->ia_addr.sin6_addr, &srczone) ||
src = ia->ia_addr.sin6_addr;
if (in6_setscope(&src, ifp, &osrczone) ||
in6_setscope(&src, ifp1, &srczone) ||
osrczone != srczone) {
continue;
}
@ -289,7 +276,7 @@ in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp)
continue;
/* Rule 1: Prefer same address */
if (IN6_ARE_ADDR_EQUAL(dst, &ia->ia_addr.sin6_addr)) {
if (IN6_ARE_ADDR_EQUAL(&dst, &ia->ia_addr.sin6_addr)) {
ia_best = ia;
BREAK(1); /* there should be no better candidate */
}
@ -299,7 +286,7 @@ in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp)
/* Rule 2: Prefer appropriate scope */
if (dst_scope < 0)
dst_scope = in6_addrscope(dst);
dst_scope = in6_addrscope(&dst);
new_scope = in6_addrscope(&ia->ia_addr.sin6_addr);
if (IN6_ARE_SCOPE_CMP(best_scope, new_scope) < 0) {
if (IN6_ARE_SCOPE_CMP(best_scope, dst_scope) < 0)
@ -396,7 +383,7 @@ in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp)
* a large number so that it is easy to assign smaller numbers
* to more preferred rules.
*/
new_matchlen = in6_matchlen(&ia->ia_addr.sin6_addr, dst);
new_matchlen = in6_matchlen(&ia->ia_addr.sin6_addr, &dst);
if (best_matchlen < new_matchlen)
REPLACE(14);
if (new_matchlen < best_matchlen)
@ -418,7 +405,7 @@ in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp)
lookup_addrsel_policy(&ia_best->ia_addr));
best_matchlen = (new_matchlen >= 0 ? new_matchlen :
in6_matchlen(&ia_best->ia_addr.sin6_addr,
dst));
&dst));
next:
continue;
@ -432,75 +419,14 @@ in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp)
return (NULL);
}
if (ifpp)
*ifpp = ifp;
return (&ia->ia_addr.sin6_addr);
}
static int
in6_selectif(dstsock, opts, mopts, ro, retifp)
struct sockaddr_in6 *dstsock;
struct ip6_pktopts *opts;
struct ip6_moptions *mopts;
struct route_in6 *ro;
struct ifnet **retifp;
{
int error;
struct route_in6 sro;
struct rtentry *rt = NULL;
if (ro == NULL) {
bzero(&sro, sizeof(sro));
ro = &sro;
}
if ((error = in6_selectroute(dstsock, opts, mopts, ro, retifp,
&rt, 0)) != 0) {
if (rt && rt == sro.ro_rt)
RTFREE(rt);
return (error);
}
/*
* do not use a rejected or black hole route.
* XXX: this check should be done in the L2 output routine.
* However, if we skipped this check here, we'd see the following
* scenario:
* - install a rejected route for a scoped address prefix
* (like fe80::/10)
* - send a packet to a destination that matches the scoped prefix,
* with ambiguity about the scope zone.
* - pick the outgoing interface from the route, and disambiguate the
* scope zone with the interface.
* - ip6_output() would try to get another route with the "new"
* destination, which may be valid.
* - we'd see no error on output.
* Although this may not be very harmful, it should still be confusing.
* We thus reject the case here.
*/
if (rt && (rt->rt_flags & (RTF_REJECT | RTF_BLACKHOLE))) {
int flags = (rt->rt_flags & RTF_HOST ? EHOSTUNREACH : ENETUNREACH);
if (rt && rt == sro.ro_rt)
RTFREE(rt);
return (flags);
}
/*
* Adjust the "outgoing" interface. If we're going to loop the packet
* back to ourselves, the ifp would be the loopback interface.
* However, we'd rather know the interface associated to the
* destination address (which should probably be one of our own
* addresses.)
*/
if (rt && rt->rt_ifa && rt->rt_ifa->ifa_ifp)
*retifp = rt->rt_ifa->ifa_ifp;
if (rt && rt == sro.ro_rt)
RTFREE(rt);
return (0);
}
int
in6_selectroute(dstsock, opts, mopts, ro, retifp, retrt, clone)
selectroute(dstsock, opts, mopts, ro, retifp, retrt, clone, norouteok)
struct sockaddr_in6 *dstsock;
struct ip6_pktopts *opts;
struct ip6_moptions *mopts;
@ -508,6 +434,7 @@ in6_selectroute(dstsock, opts, mopts, ro, retifp, retrt, clone)
struct ifnet **retifp;
struct rtentry **retrt;
int clone; /* meaningful only for bsdi and freebsd. */
int norouteok;
{
int error = 0;
struct ifnet *ifp = NULL;
@ -534,7 +461,8 @@ in6_selectroute(dstsock, opts, mopts, ro, retifp, retrt, clone)
/* XXX boundary check is assumed to be already done. */
ifp = ifnet_byindex(pi->ipi6_ifindex);
if (ifp != NULL &&
(retrt == NULL || IN6_IS_ADDR_MULTICAST(dst))) {
(norouteok || retrt == NULL ||
IN6_IS_ADDR_MULTICAST(dst))) {
/*
* we do not have to check nor get the route for
* multicast.
@ -697,6 +625,84 @@ in6_selectroute(dstsock, opts, mopts, ro, retifp, retrt, clone)
return (error);
}
static int
in6_selectif(dstsock, opts, mopts, ro, retifp)
struct sockaddr_in6 *dstsock;
struct ip6_pktopts *opts;
struct ip6_moptions *mopts;
struct route_in6 *ro;
struct ifnet **retifp;
{
int error;
struct route_in6 sro;
struct rtentry *rt = NULL;
if (ro == NULL) {
bzero(&sro, sizeof(sro));
ro = &sro;
}
if ((error = selectroute(dstsock, opts, mopts, ro, retifp,
&rt, 0, 1)) != 0) {
if (rt && rt == sro.ro_rt)
RTFREE(rt);
return (error);
}
/*
* do not use a rejected or black hole route.
* XXX: this check should be done in the L2 output routine.
* However, if we skipped this check here, we'd see the following
* scenario:
* - install a rejected route for a scoped address prefix
* (like fe80::/10)
* - send a packet to a destination that matches the scoped prefix,
* with ambiguity about the scope zone.
* - pick the outgoing interface from the route, and disambiguate the
* scope zone with the interface.
* - ip6_output() would try to get another route with the "new"
* destination, which may be valid.
* - we'd see no error on output.
* Although this may not be very harmful, it should still be confusing.
* We thus reject the case here.
*/
if (rt && (rt->rt_flags & (RTF_REJECT | RTF_BLACKHOLE))) {
int flags = (rt->rt_flags & RTF_HOST ? EHOSTUNREACH : ENETUNREACH);
if (rt && rt == sro.ro_rt)
RTFREE(rt);
return (flags);
}
/*
* Adjust the "outgoing" interface. If we're going to loop the packet
* back to ourselves, the ifp would be the loopback interface.
* However, we'd rather know the interface associated to the
* destination address (which should probably be one of our own
* addresses.)
*/
if (rt && rt->rt_ifa && rt->rt_ifa->ifa_ifp)
*retifp = rt->rt_ifa->ifa_ifp;
if (rt && rt == sro.ro_rt)
RTFREE(rt);
return (0);
}
int
in6_selectroute(dstsock, opts, mopts, ro, retifp, retrt, clone)
struct sockaddr_in6 *dstsock;
struct ip6_pktopts *opts;
struct ip6_moptions *mopts;
struct route_in6 *ro;
struct ifnet **retifp;
struct rtentry **retrt;
int clone; /* meaningful only for bsdi and freebsd. */
{
return (selectroute(dstsock, opts, mopts, ro, retifp,
retrt, clone, 0));
}
/*
* Default hop limit selection. The precedence is as follows:
* 1. Hoplimit value specified via ioctl.
@ -830,128 +836,6 @@ in6_pcbsetport(laddr, inp, cred)
return (0);
}
/*
* Generate kernel-internal form (scopeid embedded into s6_addr16[1]).
* If the address scope of is link-local, embed the interface index in the
* address. The routine determines our precedence
* between advanced API scope/interface specification and basic API
* specification.
*
* This function should be nuked in the future, when we get rid of embedded
* scopeid thing.
*
* XXX actually, it is over-specification to return ifp against sin6_scope_id.
* there can be multiple interfaces that belong to a particular scope zone
* (in specification, we have 1:N mapping between a scope zone and interfaces).
* we may want to change the function to return something other than ifp.
*/
int
in6_embedscope(in6, sin6, in6p, ifpp)
struct in6_addr *in6;
const struct sockaddr_in6 *sin6;
struct in6pcb *in6p;
struct ifnet **ifpp;
{
struct ifnet *ifp = NULL;
u_int32_t zoneid = sin6->sin6_scope_id;
*in6 = sin6->sin6_addr;
if (ifpp)
*ifpp = NULL;
/*
* don't try to read sin6->sin6_addr beyond here, since the caller may
* ask us to overwrite existing sockaddr_in6
*/
#ifdef ENABLE_DEFAULT_SCOPE
if (zoneid == 0)
zoneid = scope6_addr2default(in6);
#endif
if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6)) {
struct in6_pktinfo *pi;
/* KAME assumption: link id == interface id */
if (in6p && in6p->in6p_outputopts &&
(pi = in6p->in6p_outputopts->ip6po_pktinfo) &&
pi->ipi6_ifindex) {
ifp = ifnet_byindex(pi->ipi6_ifindex);
in6->s6_addr16[1] = htons(pi->ipi6_ifindex);
} else if (in6p && IN6_IS_ADDR_MULTICAST(in6) &&
in6p->in6p_moptions &&
in6p->in6p_moptions->im6o_multicast_ifp) {
ifp = in6p->in6p_moptions->im6o_multicast_ifp;
in6->s6_addr16[1] = htons(ifp->if_index);
} else if (zoneid) {
if (if_index < zoneid)
return (ENXIO); /* XXX EINVAL? */
ifp = ifnet_byindex(zoneid);
/* XXX assignment to 16bit from 32bit variable */
in6->s6_addr16[1] = htons(zoneid & 0xffff);
}
if (ifpp)
*ifpp = ifp;
}
return 0;
}
/*
* generate standard sockaddr_in6 from embedded form.
* touches sin6_addr and sin6_scope_id only.
*
* this function should be nuked in the future, when we get rid of
* embedded scopeid thing.
*/
int
in6_recoverscope(sin6, in6, ifp)
struct sockaddr_in6 *sin6;
const struct in6_addr *in6;
struct ifnet *ifp;
{
u_int32_t zoneid;
sin6->sin6_addr = *in6;
/*
* don't try to read *in6 beyond here, since the caller may
* ask us to overwrite existing sockaddr_in6
*/
sin6->sin6_scope_id = 0;
if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6)) {
/*
* KAME assumption: link id == interface id
*/
zoneid = ntohs(sin6->sin6_addr.s6_addr16[1]);
if (zoneid) {
/* sanity check */
if (zoneid < 0 || if_index < zoneid)
return ENXIO;
if (ifp && ifp->if_index != zoneid)
return ENXIO;
sin6->sin6_addr.s6_addr16[1] = 0;
sin6->sin6_scope_id = zoneid;
}
}
return 0;
}
/*
* just clear the embedded scope identifier.
*/
void
in6_clearscope(addr)
struct in6_addr *addr;
{
if (IN6_IS_SCOPE_LINKLOCAL(addr) || IN6_IS_ADDR_MC_INTFACELOCAL(addr))
addr->s6_addr16[1] = 0;
}
void
addrsel_policy_init()
{

View File

@ -611,11 +611,6 @@ void in6_ifaddloop(struct ifaddr *);
int in6_is_addr_deprecated __P((struct sockaddr_in6 *));
struct inpcb;
int in6_embedscope __P((struct in6_addr *, const struct sockaddr_in6 *,
struct inpcb *, struct ifnet **));
int in6_recoverscope __P((struct sockaddr_in6 *, const struct in6_addr *,
struct ifnet *));
void in6_clearscope __P((struct in6_addr *));
int in6_src_ioctl __P((u_long, caddr_t));
#endif /* _KERNEL */

View File

@ -59,6 +59,7 @@
#include <netinet6/in6_var.h>
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
#include <netinet6/scope6_var.h>
#include <netinet/icmp6.h>
#include <netinet6/nd6.h>
@ -111,7 +112,8 @@ ip6_forward(m, srcrt)
int error, type = 0, code = 0;
struct mbuf *mcopy = NULL;
struct ifnet *origifp; /* maybe unnecessary */
u_int32_t srczone, dstzone;
u_int32_t inzone, outzone;
struct in6_addr src_in6, dst_in6;
#ifdef IPSEC
struct secpolicy *sp = NULL;
int ipsecrt = 0;
@ -400,21 +402,29 @@ ip6_forward(m, srcrt)
#endif
/*
* Scope check: if a packet can't be delivered to its destination
* for the reason that the destination is beyond the scope of the
* source address, discard the packet and return an icmp6 destination
* unreachable error with Code 2 (beyond scope of source address).
* [draft-ietf-ipngwg-icmp-v3-02.txt, Section 3.1]
* Source scope check: if a packet can't be delivered to its
* destination for the reason that the destination is beyond the scope
* of the source address, discard the packet and return an icmp6
* destination unreachable error with Code 2 (beyond scope of source
* address). We use a local copy of ip6_src, since in6_setscope()
* will possibly modify its first argument.
* [draft-ietf-ipngwg-icmp-v3-04.txt, Section 3.1]
*/
if (in6_addr2zoneid(m->m_pkthdr.rcvif, &ip6->ip6_src, &srczone) ||
in6_addr2zoneid(rt->rt_ifp, &ip6->ip6_src, &dstzone)) {
src_in6 = ip6->ip6_src;
if (in6_setscope(&src_in6, rt->rt_ifp, &outzone)) {
/* XXX: this should not happen */
ip6stat.ip6s_cantforward++;
ip6stat.ip6s_badscope++;
m_freem(m);
return;
}
if (srczone != dstzone
if (in6_setscope(&src_in6, m->m_pkthdr.rcvif, &inzone)) {
ip6stat.ip6s_cantforward++;
ip6stat.ip6s_badscope++;
m_freem(m);
return;
}
if (inzone != outzone
#ifdef IPSEC
&& !ipsecrt
#endif
@ -440,6 +450,23 @@ ip6_forward(m, srcrt)
return;
}
/*
* Destination scope check: if a packet is going to break the scope
* zone of packet's destination address, discard it. This case should
* usually be prevented by appropriately-configured routing table, but
* we need an explicit check because we may mistakenly forward the
* packet to a different zone by (e.g.) a default route.
*/
dst_in6 = ip6->ip6_dst;
if (in6_setscope(&dst_in6, m->m_pkthdr.rcvif, &inzone) != 0 ||
in6_setscope(&dst_in6, rt->rt_ifp, &outzone) != 0 ||
inzone != outzone) {
ip6stat.ip6s_cantforward++;
ip6stat.ip6s_badscope++;
m_freem(m);
return;
}
if (m->m_pkthdr.len > IN6_LINKMTU(rt->rt_ifp)) {
in6_ifstat_inc(rt->rt_ifp, ifs6_in_toobig);
if (mcopy) {

View File

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

View File

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

View File

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

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
in the source address selection */
extern int ip6_use_defzone; /* whether to use the default scope zone
when unspecified */
extern struct pfil_head inet6_pfil_hook; /* packet filter hooks */
extern struct pr_usrreqs rip6_usrreqs;
@ -391,7 +394,7 @@ int none_input __P((struct mbuf **, int *, int));
struct in6_addr *in6_selectsrc __P((struct sockaddr_in6 *,
struct ip6_pktopts *, struct ip6_moptions *, struct route_in6 *,
struct in6_addr *, int *));
struct in6_addr *, struct ifnet **, int *));
int in6_selectroute __P((struct sockaddr_in6 *, struct ip6_pktopts *,
struct ip6_moptions *, struct route_in6 *, struct ifnet **,
struct rtentry **, int));

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -75,6 +75,7 @@
#include <sys/socketvar.h>
#include <sys/sx.h>
#include <sys/systm.h>
#include <sys/syslog.h>
#include <net/if.h>
#include <net/if_types.h>
@ -92,9 +93,7 @@
#include <netinet6/ip6_var.h>
#include <netinet6/nd6.h>
#include <netinet6/raw_ip6.h>
#ifdef ENABLE_DEFAULT_SCOPE
#include <netinet6/scope6_var.h>
#endif
#ifdef IPSEC
#include <netinet6/ipsec.h>
@ -328,6 +327,7 @@ rip6_output(m, va_alist)
struct ifnet *oifp = NULL;
int type = 0, code = 0; /* for ICMPv6 output statistics only */
int priv = 0;
int scope_ambiguous = 0;
struct in6_addr *in6a;
va_list ap;
@ -354,6 +354,17 @@ rip6_output(m, va_alist)
} else
optp = in6p->in6p_outputopts;
/*
* Check and convert scope zone ID into internal form.
* XXX: we may still need to determine the zone later.
*/
if (!(so->so_state & SS_ISCONNECTED)) {
if (dstsock->sin6_scope_id == 0 && !ip6_use_defzone)
scope_ambiguous = 1;
if ((error = sa6_embedscope(dstsock, ip6_use_defzone)) != 0)
goto bad;
}
/*
* For an ICMPv6 packet, we should know its type and code
* to update statistics.
@ -377,56 +388,33 @@ rip6_output(m, va_alist)
}
ip6 = mtod(m, struct ip6_hdr *);
/*
* Next header might not be ICMP6 but use its pseudo header anyway.
*/
ip6->ip6_dst = *dst;
/*
* If the scope of the destination is link-local, embed the interface
* index in the address.
*
* XXX advanced-api value overrides sin6_scope_id
*/
if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) {
struct in6_pktinfo *pi;
/*
* XXX Boundary check is assumed to be already done in
* ip6_setpktopts().
*/
if (optp &&
(pi = optp->ip6po_pktinfo) &&
pi->ipi6_ifindex) {
ip6->ip6_dst.s6_addr16[1] = htons(pi->ipi6_ifindex);
oifp = ifnet_byindex(pi->ipi6_ifindex);
} else if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) &&
in6p->in6p_moptions &&
in6p->in6p_moptions->im6o_multicast_ifp) {
oifp = in6p->in6p_moptions->im6o_multicast_ifp;
ip6->ip6_dst.s6_addr16[1] = htons(oifp->if_index);
} else if (dstsock->sin6_scope_id) {
/* boundary check */
if (dstsock->sin6_scope_id < 0 ||
if_index < dstsock->sin6_scope_id) {
error = ENXIO; /* XXX EINVAL? */
goto bad;
}
ip6->ip6_dst.s6_addr16[1] =
htons(dstsock->sin6_scope_id & 0xffff); /* XXX */
}
}
/*
* Source address selection.
*/
if ((in6a = in6_selectsrc(dstsock, optp, in6p->in6p_moptions, NULL,
&in6p->in6p_laddr, &error)) == 0) {
&in6p->in6p_laddr, &oifp, &error)) == NULL) {
if (error == 0)
error = EADDRNOTAVAIL;
goto bad;
}
ip6->ip6_src = *in6a;
if (oifp && scope_ambiguous) {
/*
* Application should provide a proper zone ID or the use of
* default zone IDs should be enabled. Unfortunately, some
* applications do not behave as it should, so we need a
* workaround. Even if an appropriate ID is not determined
* (when it's required), if we can determine the outgoing
* interface. determine the zone ID based on the interface.
*/
error = in6_setscope(&dstsock->sin6_addr, oifp, NULL);
if (error != 0)
goto bad;
}
ip6->ip6_dst = dstsock->sin6_addr;
/* fill in the rest of the IPv6 header fields */
ip6->ip6_flow = (ip6->ip6_flow & ~IPV6_FLOWINFO_MASK) |
(in6p->in6p_flowinfo & IPV6_FLOWINFO_MASK);
ip6->ip6_vfc = (ip6->ip6_vfc & ~IPV6_VERSION_MASK) |
@ -648,16 +636,15 @@ rip6_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
struct inpcb *inp = sotoinpcb(so);
struct sockaddr_in6 *addr = (struct sockaddr_in6 *)nam;
struct ifaddr *ia = NULL;
int error = 0;
if (nam->sa_len != sizeof(*addr))
return EINVAL;
if (TAILQ_EMPTY(&ifnet) || addr->sin6_family != AF_INET6)
return EADDRNOTAVAIL;
#ifdef ENABLE_DEFAULT_SCOPE
if (addr->sin6_scope_id == 0) { /* not change if specified */
addr->sin6_scope_id = scope6_addr2default(&addr->sin6_addr);
}
#endif
if ((error = sa6_embedscope(addr, ip6_use_defzone)) != 0)
return(error);
if (!IN6_IS_ADDR_UNSPECIFIED(&addr->sin6_addr) &&
(ia = ifa_ifwithaddr((struct sockaddr *)addr)) == 0)
return EADDRNOTAVAIL;
@ -681,10 +668,8 @@ rip6_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
struct inpcb *inp = sotoinpcb(so);
struct sockaddr_in6 *addr = (struct sockaddr_in6 *)nam;
struct in6_addr *in6a = NULL;
int error = 0;
#ifdef ENABLE_DEFAULT_SCOPE
struct sockaddr_in6 tmp;
#endif
struct ifnet *ifp = NULL;
int error = 0, scope_ambiguous = 0;
if (nam->sa_len != sizeof(*addr))
return EINVAL;
@ -692,27 +677,41 @@ rip6_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
return EADDRNOTAVAIL;
if (addr->sin6_family != AF_INET6)
return EAFNOSUPPORT;
#ifdef ENABLE_DEFAULT_SCOPE
if (addr->sin6_scope_id == 0) { /* not change if specified */
/* avoid overwrites */
tmp = *addr;
addr = &tmp;
addr->sin6_scope_id = scope6_addr2default(&addr->sin6_addr);
}
#endif
/*
* Application should provide a proper zone ID or the use of
* default zone IDs should be enabled. Unfortunately, some
* applications do not behave as it should, so we need a
* workaround. Even if an appropriate ID is not determined,
* we'll see if we can determine the outgoing interface. If we
* can, determine the zone ID based on the interface below.
*/
if (addr->sin6_scope_id == 0 && !ip6_use_defzone)
scope_ambiguous = 1;
if ((error = sa6_embedscope(addr, ip6_use_defzone)) != 0)
return(error);
INP_INFO_WLOCK(&ripcbinfo);
INP_LOCK(inp);
/* Source address selection. XXX: need pcblookup? */
in6a = in6_selectsrc(addr, inp->in6p_outputopts,
inp->in6p_moptions, NULL,
&inp->in6p_laddr, &error);
&inp->in6p_laddr, &ifp, &error);
if (in6a == NULL) {
INP_UNLOCK(inp);
INP_INFO_WUNLOCK(&ripcbinfo);
return (error ? error : EADDRNOTAVAIL);
}
inp->in6p_laddr = *in6a;
/* XXX: see above */
if (ifp && scope_ambiguous &&
(error = in6_setscope(&addr->sin6_addr, ifp, NULL)) != 0) {
INP_UNLOCK(inp);
INP_INFO_WUNLOCK(&ripcbinfo);
return(error);
}
inp->in6p_faddr = addr->sin6_addr;
inp->in6p_laddr = *in6a;
soisconnected(so);
INP_UNLOCK(inp);
INP_INFO_WUNLOCK(&ripcbinfo);
@ -764,14 +763,29 @@ rip6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam,
m_freem(m);
return ENOTCONN;
}
if (nam->sa_len != sizeof(struct sockaddr_in6)) {
INP_INFO_WUNLOCK(&ripcbinfo);
m_freem(m);
return(EINVAL);
}
tmp = *(struct sockaddr_in6 *)nam;
dst = &tmp;
if (dst->sin6_family == AF_UNSPEC) {
/*
* XXX: we allow this case for backward
* compatibility to buggy applications that
* rely on old (and wrong) kernel behavior.
*/
log(LOG_INFO, "rip6 SEND: address family is "
"unspec. Assume AF_INET6\n");
dst->sin6_family = AF_INET6;
} else if (dst->sin6_family != AF_INET6) {
INP_INFO_WUNLOCK(&ripcbinfo);
m_freem(m);
return(EAFNOSUPPORT);
}
}
#ifdef ENABLE_DEFAULT_SCOPE
if (dst->sin6_scope_id == 0) { /* not change if specified */
dst->sin6_scope_id = scope6_addr2default(&dst->sin6_addr);
}
#endif
ret = rip6_output(m, so, dst, control);
INP_INFO_WUNLOCK(&ripcbinfo);
return (ret);

View File

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

View File

@ -36,6 +36,7 @@
#include <sys/socket.h>
#include <sys/systm.h>
#include <sys/queue.h>
#include <sys/syslog.h>
#include <net/route.h>
#include <net/if.h>
@ -45,6 +46,12 @@
#include <netinet6/in6_var.h>
#include <netinet6/scope6_var.h>
#ifdef ENABLE_DEFAULT_SCOPE
int ip6_use_defzone = 1;
#else
int ip6_use_defzone = 0;
#endif
/*
* The scope6_lock protects the global sid default stored in
* sid_default below.
@ -254,87 +261,6 @@ in6_addrscope(addr)
return IPV6_ADDR_SCOPE_GLOBAL;
}
/*
* When we introduce the "4+28" split semantics in sin6_scope_id,
* a 32bit integer is not enough to tell a large ID from an error (-1).
* So, we intentionally use a large type as the return value.
*/
int
in6_addr2zoneid(ifp, addr, ret_id)
struct ifnet *ifp; /* must not be NULL */
struct in6_addr *addr; /* must not be NULL */
u_int32_t *ret_id; /* must not be NULL */
{
int scope;
u_int32_t zoneid = 0;
struct scope6_id *sid = NULL;
IF_AFDATA_LOCK(ifp);
sid = SID(ifp);
#ifdef DIAGNOSTIC
if (sid == NULL) { /* should not happen */
panic("in6_addr2zoneid: scope array is NULL");
/* NOTREACHED */
}
if (ret_id == NULL) {
panic("in6_addr2zoneid: return ID is null");
/* NOTREACHED */
}
#endif
/*
* special case: the loopback address can only belong to a loopback
* interface.
*/
if (IN6_IS_ADDR_LOOPBACK(addr)) {
if (!(ifp->if_flags & IFF_LOOPBACK)) {
IF_AFDATA_UNLOCK(ifp);
return (-1);
} else {
*ret_id = 0; /* there's no ambiguity */
IF_AFDATA_UNLOCK(ifp);
return (0);
}
}
scope = in6_addrscope(addr);
/*
* XXX: These are all u_int32_t reads, so may not require locking.
*/
SCOPE6_LOCK();
switch (scope) {
case IPV6_ADDR_SCOPE_INTFACELOCAL: /* should be interface index */
zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL];
break;
case IPV6_ADDR_SCOPE_LINKLOCAL:
zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL];
break;
case IPV6_ADDR_SCOPE_SITELOCAL:
zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL];
break;
case IPV6_ADDR_SCOPE_ORGLOCAL:
zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL];
break;
default:
zoneid = 0; /* XXX: treat as global. */
break;
}
SCOPE6_UNLOCK();
*ret_id = zoneid;
IF_AFDATA_UNLOCK(ifp);
return (0);
}
void
scope6_setdefault(ifp)
struct ifnet *ifp; /* note that this might be NULL */
@ -392,3 +318,169 @@ scope6_addr2default(addr)
SCOPE6_UNLOCK();
return (id);
}
/*
* Validate the specified scope zone ID in the sin6_scope_id field. If the ID
* is unspecified (=0), needs to be specified, and the default zone ID can be
* used, the default value will be used.
* This routine then generates the kernel-internal form: if the address scope
* of is interface-local or link-local, embed the interface index in the
* address.
*/
int
sa6_embedscope(sin6, defaultok)
struct sockaddr_in6 *sin6;
int defaultok;
{
struct ifnet *ifp;
u_int32_t zoneid;
if ((zoneid = sin6->sin6_scope_id) == 0 && defaultok)
zoneid = scope6_addr2default(&sin6->sin6_addr);
if (zoneid != 0 &&
(IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) ||
IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr))) {
/*
* At this moment, we only check interface-local and
* link-local scope IDs, and use interface indices as the
* zone IDs assuming a one-to-one mapping between interfaces
* and links.
*/
if (if_index < zoneid)
return (ENXIO);
ifp = ifnet_byindex(zoneid);
if (ifp == NULL) /* XXX: this can happen for some OS */
return (ENXIO);
/* XXX assignment to 16bit from 32bit variable */
sin6->sin6_addr.s6_addr16[1] = htons(zoneid & 0xffff);
sin6->sin6_scope_id = 0;
}
return 0;
}
/*
* generate standard sockaddr_in6 from embedded form.
*/
int
sa6_recoverscope(sin6)
struct sockaddr_in6 *sin6;
{
u_int32_t zoneid;
if (sin6->sin6_scope_id != 0) {
log(LOG_NOTICE,
"sa6_recoverscope: assumption failure (non 0 ID): %s%%%d\n",
ip6_sprintf(&sin6->sin6_addr), sin6->sin6_scope_id);
/* XXX: proceed anyway... */
}
if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) ||
IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr)) {
/*
* KAME assumption: link id == interface id
*/
zoneid = ntohs(sin6->sin6_addr.s6_addr16[1]);
if (zoneid) {
/* sanity check */
if (zoneid < 0 || if_index < zoneid)
return (ENXIO);
if (!ifnet_byindex(zoneid))
return (ENXIO);
sin6->sin6_addr.s6_addr16[1] = 0;
sin6->sin6_scope_id = zoneid;
}
}
return 0;
}
/*
* Determine the appropriate scope zone ID for in6 and ifp. If ret_id is
* non NULL, it is set to the zone ID. If the zone ID needs to be embedded
* in the in6_addr structure, in6 will be modified.
*/
int
in6_setscope(in6, ifp, ret_id)
struct in6_addr *in6;
struct ifnet *ifp;
u_int32_t *ret_id; /* unnecessary? */
{
int scope;
u_int32_t zoneid = 0;
struct scope6_id *sid = SID(ifp);
#ifdef DIAGNOSTIC
if (sid == NULL) { /* should not happen */
panic("in6_setscope: scope array is NULL");
/* NOTREACHED */
}
#endif
/*
* special case: the loopback address can only belong to a loopback
* interface.
*/
if (IN6_IS_ADDR_LOOPBACK(in6)) {
if (!(ifp->if_flags & IFF_LOOPBACK))
return (EINVAL);
else {
if (ret_id != NULL)
*ret_id = 0; /* there's no ambiguity */
return (0);
}
}
scope = in6_addrscope(in6);
switch (scope) {
case IPV6_ADDR_SCOPE_INTFACELOCAL: /* should be interface index */
zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL];
break;
case IPV6_ADDR_SCOPE_LINKLOCAL:
zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL];
break;
case IPV6_ADDR_SCOPE_SITELOCAL:
zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL];
break;
case IPV6_ADDR_SCOPE_ORGLOCAL:
zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL];
break;
default:
zoneid = 0; /* XXX: treat as global. */
break;
}
if (ret_id != NULL)
*ret_id = zoneid;
if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6))
in6->s6_addr16[1] = htons(zoneid & 0xffff); /* XXX */
return (0);
}
/*
* Just clear the embedded scope identifier. Return 0 if the original address
* is intact; return non 0 if the address is modified.
*/
int
in6_clearscope(in6)
struct in6_addr *in6;
{
int modified = 0;
if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6)) {
if (in6->s6_addr16[1] != 0)
modified = 1;
in6->s6_addr16[1] = 0;
}
return (modified);
}

View File

@ -51,6 +51,10 @@ void scope6_setdefault __P((struct ifnet *));
int scope6_get_default __P((struct scope6_id *));
u_int32_t scope6_in6_addrscope __P((struct in6_addr *));
u_int32_t scope6_addr2default __P((struct in6_addr *));
int sa6_embedscope __P((struct sockaddr_in6 *, int));
int sa6_recoverscope __P((struct sockaddr_in6 *));
int in6_setscope __P((struct in6_addr *, struct ifnet *, u_int32_t *));
int in6_clearscope __P((struct in6_addr *));
#endif /* _KERNEL */
#endif /* _NETINET6_SCOPE6_VAR_H_ */

View File

@ -96,6 +96,7 @@
#include <netinet6/udp6_var.h>
#include <netinet/icmp6.h>
#include <netinet6/ip6protosw.h>
#include <netinet6/scope6_var.h>
#ifdef IPSEC
#include <netinet6/ipsec.h>
@ -128,6 +129,9 @@ udp6_output(in6p, m, addr6, control, td)
struct ip6_hdr *ip6;
struct udphdr *udp6;
struct in6_addr *laddr, *faddr;
struct sockaddr_in6 *sin6 = NULL;
struct ifnet *oifp = NULL;
int scope_ambiguous = 0;
u_short fport;
int error = 0;
struct ip6_pktopts *optp, opt;
@ -139,6 +143,29 @@ udp6_output(in6p, m, addr6, control, td)
priv = 0;
if (td && !suser(td))
priv = 1;
if (addr6) {
/* addr6 has been validated in udp6_send(). */
sin6 = (struct sockaddr_in6 *)addr6;
/* protect *sin6 from overwrites */
tmp = *sin6;
sin6 = &tmp;
/*
* Application should provide a proper zone ID or the use of
* default zone IDs should be enabled. Unfortunately, some
* applications do not behave as it should, so we need a
* workaround. Even if an appropriate ID is not determined,
* we'll see if we can determine the outgoing interface. If we
* can, determine the zone ID based on the interface below.
*/
if (sin6->sin6_scope_id == 0 && !ip6_use_defzone)
scope_ambiguous = 1;
if ((error = sa6_embedscope(sin6, ip6_use_defzone)) != 0)
return (error);
}
if (control) {
if ((error = ip6_setpktopts(control, &opt,
in6p->in6p_outputopts, priv, IPPROTO_UDP)) != 0)
@ -147,7 +174,9 @@ udp6_output(in6p, m, addr6, control, td)
} else
optp = in6p->in6p_outputopts;
if (addr6) {
if (sin6) {
faddr = &sin6->sin6_addr;
/*
* IPv4 version of udp_output calls in_pcbconnect in this case,
* which needs splnet and affects performance.
@ -156,7 +185,6 @@ udp6_output(in6p, m, addr6, control, td)
* and in6_pcbsetport in order to fill in the local address
* and the local port.
*/
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)addr6;
if (sin6->sin6_port == 0) {
error = EADDRNOTAVAIL;
goto release;
@ -168,11 +196,6 @@ udp6_output(in6p, m, addr6, control, td)
goto release;
}
/* protect *sin6 from overwrites */
tmp = *sin6;
sin6 = &tmp;
faddr = &sin6->sin6_addr;
fport = sin6->sin6_port; /* allow 0 port */
if (IN6_IS_ADDR_V4MAPPED(faddr)) {
@ -189,19 +212,30 @@ udp6_output(in6p, m, addr6, control, td)
*/
error = EINVAL;
goto release;
} else
af = AF_INET;
}
}
if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr) &&
!IN6_IS_ADDR_V4MAPPED(&in6p->in6p_laddr)) {
/*
* when remote addr is an IPv4-mapped address,
* local addr should not be an IPv6 address,
* since you cannot determine how to map IPv6
* source address to IPv4.
*/
error = EINVAL;
goto release;
}
/* KAME hack: embed scopeid */
if (in6_embedscope(&sin6->sin6_addr, sin6, in6p, NULL) != 0) {
error = EINVAL;
goto release;
af = AF_INET;
}
if (!IN6_IS_ADDR_V4MAPPED(faddr)) {
laddr = in6_selectsrc(sin6, optp, in6p->in6p_moptions,
NULL, &in6p->in6p_laddr, &error);
NULL, &in6p->in6p_laddr, &oifp, &error);
if (oifp && scope_ambiguous &&
(error = in6_setscope(&sin6->sin6_addr,
oifp, NULL))) {
goto release;
}
} else
laddr = &in6p->in6p_laddr; /* XXX */
if (laddr == NULL) {

View File

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

View File

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