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:
parent
869de95743
commit
a1f7e5f8ee
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=148385
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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))
|
||||
|
@ -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));
|
||||
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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];
|
||||
|
@ -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]);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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, "");
|
||||
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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_ */
|
||||
|
@ -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) {
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user