bring in recent KAME changes (only important and stable ones, as usual).

- remove net.inet6.ip6.nd6_proxyall.  introduce proxy NDP code works
  just like "arp -s".
- revise source address selection.
  be more careful about use of yet-to-be-valid addresses as source.
- as router, transmit ICMP6_DST_UNREACH_BEYONDSCOPE against out-of-scope
  packet forwarding attempt.
- path MTU discovery takes care of routing header properly.
- be more strict about mbuf chain parsing.
This commit is contained in:
itojun 2000-02-26 08:39:18 +00:00
parent 2925a0d65b
commit 1450d6e643
11 changed files with 952 additions and 407 deletions

View File

@ -1,4 +1,5 @@
/* $NetBSD: icmp6.h,v 1.4 2000/02/06 11:11:30 itojun Exp $ */
/* $NetBSD: icmp6.h,v 1.5 2000/02/26 08:39:18 itojun Exp $ */
/* $KAME: icmp6.h,v 1.6 2000/02/24 16:34:46 itojun Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@ -469,12 +470,12 @@ struct icmp6_filter {
#ifdef _KERNEL
#define ICMP6_FILTER_SETPASSALL(filterp) \
{ \
do { \
int i; u_char *p; \
p = (u_char *)filterp; \
for (i = 0; i < sizeof(struct icmp6_filter); i++) \
p[i] = 0xff; \
}
} while (0)
#define ICMP6_FILTER_SETBLOCKALL(filterp) \
bzero(filterp, sizeof(struct icmp6_filter))
#else /* _KERNEL */
@ -525,7 +526,7 @@ struct icmp6stat {
#define ICMPV6CTL_ND6_UMAXTRIES 9
#define ICMPV6CTL_ND6_MMAXTRIES 10
#define ICMPV6CTL_ND6_USELOOPBACK 11
#define ICMPV6CTL_ND6_PROXYALL 12
/*#define ICMPV6CTL_ND6_PROXYALL 12 obsoleted, do not reuse here */
#define ICMPV6CTL_NODEINFO 13
#define ICMPV6CTL_MAXID 14
@ -542,7 +543,7 @@ struct icmp6stat {
{ "nd6_umaxtries", CTLTYPE_INT }, \
{ "nd6_mmaxtries", CTLTYPE_INT }, \
{ "nd6_useloopback", CTLTYPE_INT }, \
{ "nd6_proxyall", CTLTYPE_INT }, \
{ 0, 0 }, \
{ "nodeinfo", CTLTYPE_INT }, \
}
@ -560,7 +561,7 @@ struct icmp6stat {
&nd6_umaxtries, \
&nd6_mmaxtries, \
&nd6_useloopback, \
&nd6_proxyall, \
0, \
&icmp6_nodeinfo, \
}

View File

@ -1,4 +1,5 @@
/* $NetBSD: icmp6.c,v 1.22 2000/02/17 10:59:38 darrenr Exp $ */
/* $NetBSD: icmp6.c,v 1.23 2000/02/26 08:39:19 itojun Exp $ */
/* $KAME: icmp6.c,v 1.70 2000/02/26 07:01:11 itojun Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@ -117,6 +118,8 @@ static struct rttimer_queue *icmp6_mtudisc_timeout_q = NULL;
extern int pmtu_expire;
static int icmp6_rip6_input __P((struct mbuf **, int));
static void icmp6_mtudisc_update __P((struct in6_addr *, struct icmp6_hdr *,
struct mbuf *));
static int icmp6_ratelimit __P((const struct in6_addr *, const int, const int));
static const char *icmp6_redirect_diag __P((struct in6_addr *,
struct in6_addr *, struct in6_addr *));
@ -155,10 +158,10 @@ icmp6_error(m, type, code, param)
icmp6stat.icp6s_error++;
#ifdef M_DECRYPTED /*not openbsd*/
if (m->m_flags & M_DECRYPTED)
if (m->m_flags & M_DECRYPTED) {
icmp6stat.icp6s_canterror++;
goto freeit;
#endif
}
#ifndef PULLDOWN_TEST
IP6_EXTHDR_CHECK(m, 0, sizeof(struct ip6_hdr), );
@ -408,12 +411,21 @@ icmp6_input(mp, offp, proto)
break;
case ICMP6_DST_UNREACH_ADMIN:
icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_adminprohib);
case ICMP6_DST_UNREACH_ADDR:
code = PRC_UNREACH_HOST;
code = PRC_UNREACH_PROTOCOL; /* is this a good code? */
break;
case ICMP6_DST_UNREACH_ADDR:
code = PRC_HOSTDEAD;
break;
#ifdef COMPAT_RFC1885
case ICMP6_DST_UNREACH_NOTNEIGHBOR:
code = PRC_UNREACH_SRCFAIL;
break;
#else
case ICMP6_DST_UNREACH_BEYONDSCOPE:
/* I mean "source address was incorrect." */
code = PRC_PARAMPROB;
break;
#endif
case ICMP6_DST_UNREACH_NOPORT:
code = PRC_UNREACH_PORT;
break;
@ -427,55 +439,14 @@ icmp6_input(mp, offp, proto)
icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_pkttoobig);
if (code != 0)
goto badcode;
{
u_int mtu = ntohl(icmp6->icmp6_mtu);
struct rtentry *rt = NULL;
struct sockaddr_in6 sin6;
if (icmp6len < sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr)) {
icmp6stat.icp6s_tooshort++;
goto freeit;
}
#ifndef PULLDOWN_TEST
IP6_EXTHDR_CHECK(m, off,
sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr),
IPPROTO_DONE);
icmp6 = (struct icmp6_hdr *)(mtod(m, caddr_t) + off);
#else
IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off,
sizeof(*icmp6) + sizeof(struct ip6_hdr));
if (icmp6 == NULL) {
icmp6stat.icp6s_tooshort++;
return IPPROTO_DONE;
}
#endif
code = PRC_MSGSIZE;
bzero(&sin6, sizeof(sin6));
sin6.sin6_family = PF_INET6;
sin6.sin6_len = sizeof(struct sockaddr_in6);
sin6.sin6_addr = ((struct ip6_hdr *)(icmp6 + 1))->ip6_dst;
rt = rtalloc1((struct sockaddr *)&sin6, 1); /*clone*/
if (!rt || (rt->rt_flags & RTF_HOST) == 0) {
if (rt)
RTFREE(rt);
rt = icmp6_mtudisc_clone((struct sockaddr *)&sin6);
}
if (rt && (rt->rt_flags & RTF_HOST)
&& !(rt->rt_rmx.rmx_locks & RTV_MTU)) {
if (mtu < IPV6_MMTU) {
/* xxx */
rt->rt_rmx.rmx_locks |= RTV_MTU;
} else if (mtu < rt->rt_ifp->if_mtu &&
rt->rt_rmx.rmx_mtu > mtu) {
rt->rt_rmx.rmx_mtu = mtu;
}
}
if (rt)
RTFREE(rt);
/*
* Updating the path MTU will be done after examining
* intermediate extension headers.
*/
goto deliver;
}
break;
case ICMP6_TIME_EXCEEDED:
@ -518,12 +489,28 @@ icmp6_input(mp, offp, proto)
if ((n->m_flags & M_EXT) != 0
|| n->m_len < off + sizeof(struct icmp6_hdr)) {
struct mbuf *n0 = n;
const int maxlen = sizeof(*nip6) + sizeof(*nicmp6);
/*
* Prepare an internal mbuf. m_pullup() doesn't
* always copy the length we specified.
*/
if (maxlen >= MCLBYTES) {
#ifdef DIAGNOSTIC
printf("MCLBYTES too small\n");
#endif
/* Give up remote */
m_freem(n0);
break;
}
MGETHDR(n, M_DONTWAIT, n0->m_type);
if (n && maxlen >= MHLEN) {
MCLGET(n, M_DONTWAIT);
if ((n->m_flags & M_EXT) == 0) {
m_free(n);
n = NULL;
}
}
if (n == NULL) {
/* Give up remote */
m_freem(n0);
@ -576,8 +563,13 @@ icmp6_input(mp, offp, proto)
icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mldquery);
else
icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mldreport);
IP6_EXTHDR_CHECK(m, off, icmp6len, IPPROTO_DONE);
mld6_input(m, off);
if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) {
/* give up local */
mld6_input(m, off);
m = NULL;
goto freeit;
}
mld6_input(n, off);
/* m stays. */
break;
@ -591,7 +583,7 @@ icmp6_input(mp, offp, proto)
case MLD6_MTRACE:
/* XXX: these two are experimental. not officially defind. */
/* XXX: per-interface statistics? */
break; /* just pass it to the userland daemon */
break; /* just pass it to applications */
case ICMP6_WRUREQUEST: /* ICMP6_FQDN_QUERY */
{
@ -621,12 +613,32 @@ icmp6_input(mp, offp, proto)
noff = sizeof(struct ip6_hdr);
} else {
u_char *p;
int maxlen, maxhlen;
maxlen = sizeof(*nip6) + sizeof(*nicmp6) + 4;
if (maxlen >= MCLBYTES) {
#ifdef DIAGNOSTIC
printf("MCLBYTES too small\n");
#endif
/* Give up remote */
break;
}
MGETHDR(n, M_DONTWAIT, m->m_type);
if (n && maxlen > MHLEN) {
MCLGET(n, M_DONTWAIT);
if ((n->m_flags & M_EXT) == 0) {
m_free(n);
n = NULL;
}
}
if (n == NULL) {
/* Give up remote */
break;
}
n->m_len = 0;
maxhlen = M_TRAILINGSPACE(n) - maxlen;
if (maxhlen > hostnamelen)
maxhlen = hostnamelen;
/*
* Copy IPv6 and ICMPv6 only.
*/
@ -636,11 +648,11 @@ icmp6_input(mp, offp, proto)
bcopy(icmp6, nicmp6, sizeof(struct icmp6_hdr));
p = (u_char *)(nicmp6 + 1);
bzero(p, 4);
bcopy(hostname, p + 4, hostnamelen);
bcopy(hostname, p + 4, maxhlen);
noff = sizeof(struct ip6_hdr);
M_COPY_PKTHDR(n, m); /* just for recvif */
n->m_pkthdr.len = n->m_len = sizeof(struct ip6_hdr) +
sizeof(struct icmp6_hdr) + 4 + hostnamelen;
sizeof(struct icmp6_hdr) + 4 + maxhlen;
nicmp6->icmp6_type = ICMP6_WRUREPLY;
nicmp6->icmp6_code = 0;
}
@ -664,8 +676,13 @@ icmp6_input(mp, offp, proto)
goto badcode;
if (icmp6len < sizeof(struct nd_router_solicit))
goto badlen;
IP6_EXTHDR_CHECK(m, off, icmp6len, IPPROTO_DONE);
nd6_rs_input(m, off, icmp6len);
if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) {
/* give up local */
nd6_rs_input(m, off, icmp6len);
m = NULL;
goto freeit;
}
nd6_rs_input(n, off, icmp6len);
/* m stays. */
break;
@ -675,8 +692,13 @@ icmp6_input(mp, offp, proto)
goto badcode;
if (icmp6len < sizeof(struct nd_router_advert))
goto badlen;
IP6_EXTHDR_CHECK(m, off, icmp6len, IPPROTO_DONE);
nd6_ra_input(m, off, icmp6len);
if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) {
/* give up local */
nd6_ra_input(m, off, icmp6len);
m = NULL;
goto freeit;
}
nd6_ra_input(n, off, icmp6len);
/* m stays. */
break;
@ -686,8 +708,13 @@ icmp6_input(mp, offp, proto)
goto badcode;
if (icmp6len < sizeof(struct nd_neighbor_solicit))
goto badlen;
IP6_EXTHDR_CHECK(m, off, icmp6len, IPPROTO_DONE);
nd6_ns_input(m, off, icmp6len);
if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) {
/* give up local */
nd6_ns_input(m, off, icmp6len);
m = NULL;
goto freeit;
}
nd6_ns_input(n, off, icmp6len);
/* m stays. */
break;
@ -697,8 +724,13 @@ icmp6_input(mp, offp, proto)
goto badcode;
if (icmp6len < sizeof(struct nd_neighbor_advert))
goto badlen;
IP6_EXTHDR_CHECK(m, off, icmp6len, IPPROTO_DONE);
nd6_na_input(m, off, icmp6len);
if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) {
/* give up local */
nd6_na_input(m, off, icmp6len);
m = NULL;
goto freeit;
}
nd6_na_input(n, off, icmp6len);
/* m stays. */
break;
@ -708,7 +740,13 @@ icmp6_input(mp, offp, proto)
goto badcode;
if (icmp6len < sizeof(struct nd_redirect))
goto badlen;
icmp6_redirect_input(m, off);
if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) {
/* give up local */
icmp6_redirect_input(m, off);
m = NULL;
goto freeit;
}
icmp6_redirect_input(n, off);
/* m stays. */
break;
@ -764,19 +802,20 @@ icmp6_input(mp, offp, proto)
int eoff = off + sizeof(struct icmp6_hdr) +
sizeof(struct ip6_hdr);
struct ip6ctlparam ip6cp;
struct in6_addr *finaldst = NULL;
int icmp6type = icmp6->icmp6_type;
struct ip6_frag *fh;
struct ip6_rthdr *rth;
struct ip6_rthdr0 *rth0;
int rthlen;
while (1) { /* XXX: should avoid inf. loop explicitly? */
struct ip6_ext *eh;
switch(nxt) {
case IPPROTO_ESP:
case IPPROTO_NONE:
goto passit;
case IPPROTO_HOPOPTS:
case IPPROTO_DSTOPTS:
case IPPROTO_ROUTING:
case IPPROTO_AH:
case IPPROTO_FRAGMENT:
#ifndef PULLDOWN_TEST
IP6_EXTHDR_CHECK(m, 0, eoff +
sizeof(struct ip6_ext),
@ -791,15 +830,105 @@ icmp6_input(mp, offp, proto)
return IPPROTO_DONE;
}
#endif
if (nxt == IPPROTO_AH)
eoff += (eh->ip6e_len + 2) << 2;
else if (nxt == IPPROTO_FRAGMENT)
eoff += sizeof(struct ip6_frag);
else
eoff += (eh->ip6e_len + 1) << 3;
nxt = eh->ip6e_nxt;
break;
case IPPROTO_ROUTING:
/*
* When the erroneous packet contains a
* routing header, we should examine the
* header to determine the final destination.
* Otherwise, we can't properly update
* information that depends on the final
* destination (e.g. path MTU).
*/
#ifndef PULLDOWN_TEST
IP6_EXTHDR_CHECK(m, 0, eoff + sizeof(*rth),
IPPROTO_DONE);
rth = (struct ip6_rthdr *)(mtod(m, caddr_t)
+ eoff);
#else
IP6_EXTHDR_GET(rth, struct ip6_rthdr *, m,
eoff, sizeof(*rth));
if (rth == NULL) {
icmp6stat.icp6s_tooshort++;
return IPPROTO_DONE;
}
#endif
rthlen = (rth->ip6r_len + 1) << 3;
/*
* XXX: currently there is no
* officially defined type other
* than type-0.
* Note that if the segment left field
* is 0, all intermediate hops must
* have been passed.
*/
if (rth->ip6r_segleft &&
rth->ip6r_type == IPV6_RTHDR_TYPE_0) {
int hops;
#ifndef PULLDOWN_TEST
IP6_EXTHDR_CHECK(m, 0, eoff + rthlen,
IPPROTO_DONE);
rth0 = (struct ip6_rthdr0 *)(mtod(m, caddr_t) + eoff);
#else
IP6_EXTHDR_GET(rth0,
struct ip6_rthdr0 *, m,
eoff, rthlen);
if (rth0 == NULL) {
icmp6stat.icp6s_tooshort++;
return IPPROTO_DONE;
}
#endif
/* just ignore a bogus header */
if ((rth0->ip6r0_len % 2) == 0 &&
(hops = rth0->ip6r0_len/2))
finaldst = (struct in6_addr *)(rth0 + 1) + (hops - 1);
}
eoff += rthlen;
nxt = rth->ip6r_nxt;
break;
case IPPROTO_FRAGMENT:
#ifndef PULLDOWN_TEST
IP6_EXTHDR_CHECK(m, 0, eoff +
sizeof(struct ip6_frag),
IPPROTO_DONE);
fh = (struct ip6_frag *)(mtod(m, caddr_t)
+ eoff);
#else
IP6_EXTHDR_GET(fh, struct ip6_frag *, m,
eoff, sizeof(*fh));
if (fh == NULL) {
icmp6stat.icp6s_tooshort++;
return IPPROTO_DONE;
}
#endif
/*
* Data after a fragment header is meaningless
* unless it is the first fragment, but
* we'll go to the notify label for path MTU
* discovery.
*/
if (fh->ip6f_offlg & IP6F_OFF_MASK)
goto notify;
eoff += sizeof(struct ip6_frag);
nxt = fh->ip6f_nxt;
break;
default:
/*
* This case includes ESP and the No Next
* Header. In such cases going to the notify
* label does not have any meaning
* (i.e. ctlfunc will be NULL), but we go
* anyway since we might have to update
* path MTU information.
*/
goto notify;
}
}
@ -814,6 +943,12 @@ icmp6_input(mp, offp, proto)
return IPPROTO_DONE;
}
#endif
if (icmp6type == ICMP6_PACKET_TOO_BIG) {
if (finaldst == NULL)
finaldst = &((struct ip6_hdr *)(icmp6 + 1))->ip6_dst;
icmp6_mtudisc_update(finaldst, icmp6, m);
}
ctlfunc = (void (*) __P((int, struct sockaddr *, void *)))
(inet6sw[ip6_protox[nxt]].pr_ctlinput);
if (ctlfunc) {
@ -834,7 +969,6 @@ icmp6_input(mp, offp, proto)
break;
}
passit:
icmp6_rip6_input(&m, *offp);
return IPPROTO_DONE;
@ -843,6 +977,42 @@ icmp6_input(mp, offp, proto)
return IPPROTO_DONE;
}
static void
icmp6_mtudisc_update(dst, icmp6, m)
struct in6_addr *dst;
struct icmp6_hdr *icmp6;/* we can assume the validity of the pointer */
struct mbuf *m; /* currently unused but added for scoped addrs */
{
u_int mtu = ntohl(icmp6->icmp6_mtu);
struct rtentry *rt = NULL;
struct sockaddr_in6 sin6;
bzero(&sin6, sizeof(sin6));
sin6.sin6_family = PF_INET6;
sin6.sin6_len = sizeof(struct sockaddr_in6);
sin6.sin6_addr = *dst;
/* sin6.sin6_scope_id = XXX: should be set if DST is a scoped addr */
rt = rtalloc1((struct sockaddr *)&sin6, 1); /*clone*/
if (!rt || (rt->rt_flags & RTF_HOST) == 0) {
if (rt)
RTFREE(rt);
rt = icmp6_mtudisc_clone((struct sockaddr *)&sin6);
}
if (rt && (rt->rt_flags & RTF_HOST)
&& !(rt->rt_rmx.rmx_locks & RTV_MTU)) {
if (mtu < IPV6_MMTU) {
/* xxx */
rt->rt_rmx.rmx_locks |= RTV_MTU;
} else if (mtu < rt->rt_ifp->if_mtu &&
rt->rt_rmx.rmx_mtu > mtu) {
rt->rt_rmx.rmx_mtu = mtu;
}
}
if (rt)
RTFREE(rt);
}
/*
* Process a Node Information Query
*/
@ -1156,8 +1326,16 @@ icmp6_rip6_input(mp, off)
struct icmp6_hdr *icmp6;
struct mbuf *opts = NULL;
#ifndef PULLDOWN_TEST
/* this is assumed to be safe. */
icmp6 = (struct icmp6_hdr *)((caddr_t)ip6 + off);
#else
IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off, sizeof(*icmp6));
if (icmp6 == NULL) {
/* m is already reclaimed */
return IPPROTO_DONE;
}
#endif
bzero(&rip6src, sizeof(rip6src));
rip6src.sin6_len = sizeof(struct sockaddr_in6);
@ -1338,10 +1516,13 @@ icmp6_reflect(m, off)
/*
* 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 would be VERY rare, but is possible when
* (for example) when we encounter an error while forwarding procedure
* destined to a duplicated address of ours.
*/
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) == 0) {
(ia->ia6_flags & (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY)) == 0) {
src = &t;
break;
}
@ -1355,9 +1536,11 @@ icmp6_reflect(m, off)
if (src == 0)
/*
* We have not multicast routing yet. So this case matches
* to our multicast, our anycast or not to our unicast.
* Select a source address which has the same scope.
* This case matches to multicasts, our anycast, or unicasts
* that we do not own. Select a source address which has the
* same scope.
* XXX: for (non link-local) multicast addresses, this might
* not be a good choice.
*/
if ((ia = in6_ifawithscope(m->m_pkthdr.rcvif, &t)) != 0)
src = &IA6_SIN6(ia)->sin6_addr;
@ -1429,7 +1612,7 @@ icmp6_redirect_input(m, off)
{
struct ifnet *ifp = m->m_pkthdr.rcvif;
struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
struct nd_redirect *nd_rd = (struct nd_redirect *)((caddr_t)ip6 + off);
struct nd_redirect *nd_rd;
int icmp6len = ntohs(ip6->ip6_plen);
char *lladdr = NULL;
int lladdrlen = 0;
@ -1439,8 +1622,8 @@ icmp6_redirect_input(m, off)
int is_router;
int is_onlink;
struct in6_addr src6 = ip6->ip6_src;
struct in6_addr redtgt6 = nd_rd->nd_rd_target;
struct in6_addr reddst6 = nd_rd->nd_rd_dst;
struct in6_addr redtgt6;
struct in6_addr reddst6;
union nd_opts ndopts;
if (!m || !ifp)
@ -1448,28 +1631,41 @@ icmp6_redirect_input(m, off)
/* XXX if we are router, we don't update route by icmp6 redirect */
if (ip6_forwarding)
return;
goto freeit;
if (!icmp6_rediraccept)
return;
goto freeit;
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);
#ifndef PULLDOWN_TEST
IP6_EXTHDR_CHECK(m, off, icmp6len,);
nd_rd = (struct nd_redirect *)((caddr_t)ip6 + off);
#else
IP6_EXTHDR_GET(nd_rd, struct nd_redirect *, m, off, icmp6len);
if (nd_rd == NULL) {
icmp6stat.icp6s_tooshort++;
return;
}
#endif
redtgt6 = nd_rd->nd_rd_target;
reddst6 = nd_rd->nd_rd_dst;
/* validation */
if (!IN6_IS_ADDR_LINKLOCAL(&src6)) {
log(LOG_ERR,
"ICMP6 redirect sent from %s rejected; "
"must be from linklocal\n", ip6_sprintf(&src6));
return;
goto freeit;
}
if (ip6->ip6_hlim != 255) {
log(LOG_ERR,
"ICMP6 redirect sent from %s rejected; "
"hlim=%d (must be 255)\n",
ip6_sprintf(&src6), ip6->ip6_hlim);
return;
goto freeit;
}
{
/* ip6->ip6_src must be equal to gw for icmp6->icmp6_reddst */
@ -1480,8 +1676,7 @@ icmp6_redirect_input(m, off)
sin6.sin6_family = AF_INET6;
sin6.sin6_len = sizeof(struct sockaddr_in6);
bcopy(&reddst6, &sin6.sin6_addr, sizeof(reddst6));
rt = rtalloc1((struct sockaddr *)&sin6, 0
);
rt = rtalloc1((struct sockaddr *)&sin6, 0);
if (rt) {
gw6 = &(((struct sockaddr_in6 *)rt->rt_gateway)->sin6_addr);
if (bcmp(&src6, gw6, sizeof(struct in6_addr)) != 0) {
@ -1492,14 +1687,14 @@ icmp6_redirect_input(m, off)
ip6_sprintf(gw6),
icmp6_redirect_diag(&src6, &reddst6, &redtgt6));
RTFREE(rt);
return;
goto freeit;
}
} else {
log(LOG_ERR,
"ICMP6 redirect rejected; "
"no route found for redirect dst: %s\n",
icmp6_redirect_diag(&src6, &reddst6, &redtgt6));
return;
goto freeit;
}
RTFREE(rt);
rt = NULL;
@ -1509,7 +1704,7 @@ icmp6_redirect_input(m, off)
"ICMP6 redirect rejected; "
"redirect dst must be unicast: %s\n",
icmp6_redirect_diag(&src6, &reddst6, &redtgt6));
return;
goto freeit;
}
is_router = is_onlink = 0;
@ -1522,7 +1717,7 @@ icmp6_redirect_input(m, off)
"ICMP6 redirect rejected; "
"neither router case nor onlink case: %s\n",
icmp6_redirect_diag(&src6, &reddst6, &redtgt6));
return;
goto freeit;
}
/* validation passed */
@ -1532,7 +1727,7 @@ icmp6_redirect_input(m, off)
log(LOG_INFO, "icmp6_redirect_input: "
"invalid ND option, rejected: %s\n",
icmp6_redirect_diag(&src6, &reddst6, &redtgt6));
return;
goto freeit;
}
if (ndopts.nd_opts_tgt_lladdr) {
@ -1610,6 +1805,9 @@ icmp6_redirect_input(m, off)
key_sa_routechange((struct sockaddr *)&sdst);
#endif
}
freeit:
m_freem(m);
}
void
@ -1657,21 +1855,28 @@ icmp6_redirect_output(m0, rt)
* we almost always ask for an mbuf cluster for simplicity.
* (MHLEN < IPV6_MMTU is almost always true)
*/
#if IPV6_MMTU >= MCLBYTES
# error assumption failed about IPV6_MMTU and MCLBYTES
#endif
MGETHDR(m, M_DONTWAIT, MT_HEADER);
if (m && IPV6_MMTU >= MHLEN)
MCLGET(m, M_DONTWAIT);
if (!m)
goto fail;
if (MHLEN < IPV6_MMTU)
MCLGET(m, M_DONTWAIT);
maxlen = (m->m_flags & M_EXT) ? MCLBYTES : MHLEN;
maxlen = min(IPV6_MMTU, maxlen);
/* just for safety */
if (maxlen < sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr))
if (maxlen < sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr) +
((sizeof(struct nd_opt_hdr) + ifp->if_addrlen + 7) & ~7)) {
goto fail;
}
{
/* get ip6 linklocal address for ifp(my outgoing interface). */
struct in6_ifaddr *ia = in6ifa_ifpforlinklocal(ifp);
if (ia == NULL)
struct in6_ifaddr *ia;
if ((ia = in6ifa_ifpforlinklocal(ifp,
IN6_IFF_NOTREADY|
IN6_IFF_ANYCAST)) == NULL)
goto fail;
ifp_ll6 = &ia->ia_addr.sin6_addr;
}
@ -1738,6 +1943,11 @@ icmp6_redirect_output(m0, rt)
rt_router = nd6_lookup(router_ll6, 0, ifp);
if (!rt_router)
goto nolladdropt;
len = sizeof(*nd_opt) + ifp->if_addrlen;
len = (len + 7) & ~7; /*round by 8*/
/* safety check */
if (len + (p - (u_char *)ip6) > maxlen)
goto nolladdropt;
if (!(rt_router->rt_flags & RTF_GATEWAY) &&
(rt_router->rt_flags & RTF_LLINFO) &&
(rt_router->rt_gateway->sa_family == AF_LINK) &&
@ -1745,12 +1955,10 @@ icmp6_redirect_output(m0, rt)
sdl->sdl_alen) {
nd_opt = (struct nd_opt_hdr *)p;
nd_opt->nd_opt_type = ND_OPT_TARGET_LINKADDR;
len = 2 + ifp->if_addrlen;
len = (len + 7) & ~7; /*round by 8*/
nd_opt->nd_opt_len = len >> 3;
p += len;
lladdr = (char *)(nd_opt + 1);
bcopy(LLADDR(sdl), lladdr, ifp->if_addrlen);
p += len;
}
}
nolladdropt:;
@ -1758,10 +1966,10 @@ nolladdropt:;
m->m_pkthdr.len = m->m_len = p - (u_char *)ip6;
/* just to be safe */
#ifdef M_DECRYPTED /*not openbsd*/
if (m0->m_flags & M_DECRYPTED)
goto noredhdropt;
#endif
if (p - (u_char *)ip6 > maxlen)
goto noredhdropt;
{
/* redirected header option */
@ -1832,9 +2040,7 @@ nolladdropt:;
m->m_next = m0;
m->m_pkthdr.len = m->m_len + m0->m_len;
}
#ifdef M_DECRYPTED /*not openbsd*/
noredhdropt:;
#endif
if (IN6_IS_ADDR_LINKLOCAL(&sip6->ip6_src))
sip6->ip6_src.s6_addr16[1] = 0;
@ -2087,8 +2293,6 @@ icmp6_sysctl(name, namelen, oldp, oldlenp, newp, newlen)
case ICMPV6CTL_ND6_USELOOPBACK:
return sysctl_int(oldp, oldlenp, newp, newlen,
&nd6_useloopback);
case ICMPV6CTL_ND6_PROXYALL:
return sysctl_int(oldp, oldlenp, newp, newlen, &nd6_proxyall);
case ICMPV6CTL_NODEINFO:
return sysctl_int(oldp, oldlenp, newp, newlen, &icmp6_nodeinfo);
default:

View File

@ -1,4 +1,4 @@
/* $NetBSD: in6.c,v 1.20 2000/02/25 05:13:05 itojun Exp $ */
/* $NetBSD: in6.c,v 1.21 2000/02/26 08:39:19 itojun Exp $ */
/* $KAME: in6.c,v 1.55 2000/02/25 00:32:23 itojun Exp $ */
/*
@ -138,22 +138,6 @@ struct multi6_kludge {
struct in6_multihead mk_head;
};
/*
* Determine whether an IP6 address is in a reserved set of addresses
* that may not be forwarded, or whether datagrams to that destination
* may be forwarded.
*/
int
in6_canforward(src, dst)
struct in6_addr *src, *dst;
{
if (IN6_IS_ADDR_LINKLOCAL(src) ||
IN6_IS_ADDR_LINKLOCAL(dst) ||
IN6_IS_ADDR_MULTICAST(dst))
return(0);
return(1);
}
/*
* Check if the loopback entry will be automatically generated.
* if 0 returned, will not be automatically generated.
@ -267,90 +251,6 @@ in6_ifremloop(struct ifaddr *ifa)
}
}
/*
* Subroutine for in6_ifaddproxy() and in6_ifremproxy().
* This routine does actual work.
* call in6_addmulti() when cmd == 1.
* call in6_delmulti() when cmd == 2.
*/
static int
in6_ifproxy_request(int cmd, struct in6_ifaddr *ia)
{
int error = 0;
/*
* If we have an IPv6 dstaddr on adding p2p interface,
* join dstaddr's solicited multicast on necessary interface.
*/
if ((ia->ia_ifp->if_flags & IFF_POINTOPOINT) &&
ia->ia_dstaddr.sin6_family == AF_INET6 &&
!IN6_IS_ADDR_LINKLOCAL(&ia->ia_dstaddr.sin6_addr)) {
struct in6_ifaddr *ia_lan;
/*
* TODO: Join only on some specified interfaces by some
* configuration.
* Unsolicited Neighbor Advertisements will be also necessary.
*
* Now, join on interfaces which meets following.
* -IFF_BROADCAST and IFF_MULTICAST
* (NBMA is out of scope)
* -the prefix value is same as p2p dstaddr
*/
for (ia_lan = in6_ifaddr; ia_lan; ia_lan = ia_lan->ia_next) {
struct in6_addr llsol;
if ((ia_lan->ia_ifp->if_flags &
(IFF_BROADCAST|IFF_MULTICAST)) !=
(IFF_BROADCAST|IFF_MULTICAST))
continue;
if (!IN6_ARE_MASKED_ADDR_EQUAL(IA6_IN6(ia),
IA6_IN6(ia_lan),
IA6_MASKIN6(ia_lan)))
continue;
if (ia_lan->ia_ifp == ia->ia_ifp)
continue;
/* init llsol */
bzero(&llsol, sizeof(struct in6_addr));
llsol.s6_addr16[0] = htons(0xff02);
llsol.s6_addr16[1] = htons(ia_lan->ia_ifp->if_index);
llsol.s6_addr32[1] = 0;
llsol.s6_addr32[2] = htonl(1);
llsol.s6_addr32[3] =
ia->ia_dstaddr.sin6_addr.s6_addr32[3];
llsol.s6_addr8[12] = 0xff;
if (cmd == 1)
(void)in6_addmulti(&llsol,
ia_lan->ia_ifp,
&error);
else if (cmd == 2) {
struct in6_multi *in6m;
IN6_LOOKUP_MULTI(llsol,
ia_lan->ia_ifp,
in6m);
if (in6m)
in6_delmulti(in6m);
}
}
}
return error;
}
static int
in6_ifaddproxy(struct in6_ifaddr *ia)
{
return(in6_ifproxy_request(1, ia));
}
static void
in6_ifremproxy(struct in6_ifaddr *ia)
{
in6_ifproxy_request(2, ia);
}
int
in6_ifindex2scopeid(idx)
int idx;
@ -837,14 +737,6 @@ in6_control(so, cmd, data, ifp, p)
if (error == 0)
error = error_local;
}
/* Join dstaddr's solicited multicast if necessary. */
if (nd6_proxyall && hostIsNew) {
int error_local;
error_local = in6_ifaddproxy(ia);
if (error == 0)
error = error_local;
}
ia->ia6_flags = ifra->ifra_flags;
ia->ia6_flags &= ~IN6_IFF_DUPLICATED; /*safety*/
@ -939,9 +831,6 @@ in6_purgeaddr(ifa, ifp)
if (in6m)
in6_delmulti(in6m);
}
/* Leave dstaddr's solicited multicast if necessary. */
if (nd6_proxyall)
in6_ifremproxy(ia);
TAILQ_REMOVE(&ifp->if_addrlist, &ia->ia_ifa, ifa_list);
IFAFREE(&ia->ia_ifa);
@ -1086,7 +975,7 @@ in6_lifaddr_ioctl(so, cmd, data, ifp, p)
* address. hostid points to the first link-local
* address attached to the interface.
*/
ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp);
ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp, 0);
if (!ifa)
return EADDRNOTAVAIL;
hostid = IFA_IN6(ifa);
@ -1554,8 +1443,9 @@ in6_delmulti(in6m)
* Find an IPv6 interface link-local address specific to an interface.
*/
struct in6_ifaddr *
in6ifa_ifpforlinklocal(ifp)
in6ifa_ifpforlinklocal(ifp, ignoreflags)
struct ifnet *ifp;
int ignoreflags;
{
register struct ifaddr *ifa;
@ -1565,8 +1455,12 @@ in6ifa_ifpforlinklocal(ifp)
continue; /* just for safety */
if (ifa->ifa_addr->sa_family != AF_INET6)
continue;
if (IN6_IS_ADDR_LINKLOCAL(IFA_IN6(ifa)))
if (IN6_IS_ADDR_LINKLOCAL(IFA_IN6(ifa))) {
if ((((struct in6_ifaddr *)ifa)->ia6_flags &
ignoreflags) != 0)
continue;
break;
}
}
return((struct in6_ifaddr *)ifa);
@ -1726,6 +1620,29 @@ struct in6_addr *addr;
return IPV6_ADDR_SCOPE_GLOBAL;
}
int
in6_addr2scopeid(ifp, addr)
struct ifnet *ifp; /* must not be NULL */
struct in6_addr *addr; /* must not be NULL */
{
int scope = in6_addrscope(addr);
switch(scope) {
case IPV6_ADDR_SCOPE_NODELOCAL:
return(-1); /* XXX: is this an appropriate value? */
case IPV6_ADDR_SCOPE_LINKLOCAL:
/* XXX: we do not distinguish between a link and an I/F. */
return(ifp->if_index);
case IPV6_ADDR_SCOPE_SITELOCAL:
return(0); /* XXX: invalid. */
default:
return(0); /* XXX: treat as global. */
}
}
/*
* return length of part which dst and src are equal
* hard coding...
@ -1804,86 +1721,258 @@ in6_prefixlen2mask(maskp, len)
/*
* return the best address out of the same scope
*/
struct in6_ifaddr *
in6_ifawithscope(ifp, dst)
register struct ifnet *ifp;
in6_ifawithscope(oifp, dst)
register struct ifnet *oifp;
register struct in6_addr *dst;
{
int dst_scope = in6_addrscope(dst), blen = -1, tlen;
int dst_scope = in6_addrscope(dst), src_scope, best_scope;
int blen = -1;
struct ifaddr *ifa;
struct in6_ifaddr *besta = NULL, *ia;
struct in6_ifaddr *dep[2]; /*last-resort: deprecated*/
dep[0] = dep[1] = NULL;
struct ifnet *ifp;
struct in6_ifaddr *ifa_best = NULL;
if (oifp == NULL) {
printf("in6_ifawithscope: output interface is not specified\n");
return(NULL);
}
/*
* We first look for addresses in the same scope.
* If there is one, return it.
* If two or more, return one which matches the dst longest.
* If none, return one of global addresses assigned other ifs.
* We search for all addresses on all interfaces from the beginning.
* Comparing an interface with the outgoing interface will be done
* only at the final stage of tiebreaking.
*/
for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
for (ifp = ifnet; ifp; ifp = ifp->if_next)
#else
for (ifp = TAILQ_FIRST(&ifnet); ifp; ifp = TAILQ_NEXT(ifp, if_list))
#endif
{
if (ifa->ifa_addr->sa_family != AF_INET6)
/*
* We can never take an address that breaks the scope zone
* of the destination.
*/
if (in6_addr2scopeid(ifp, dst) != in6_addr2scopeid(oifp, dst))
continue;
if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST)
continue; /* XXX: is there any case to allow anycast? */
if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_NOTREADY)
continue; /* don't use this interface */
if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DETACHED)
continue;
if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DEPRECATED) {
if (ip6_use_deprecated)
dep[0] = (struct in6_ifaddr *)ifa;
continue;
}
if (dst_scope == in6_addrscope(IFA_IN6(ifa))) {
#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
#else
for (ifa = ifp->if_addrlist.tqh_first; ifa;
ifa = ifa->ifa_list.tqe_next)
#endif
{
int tlen = -1, dscopecmp, bscopecmp, matchcmp;
if (ifa->ifa_addr->sa_family != AF_INET6)
continue;
src_scope = in6_addrscope(IFA_IN6(ifa));
#ifdef ADDRSELECT_DEBUG /* should be removed after stabilization */
dscopecmp = IN6_ARE_SCOPE_CMP(src_scope, dst_scope);
printf("in6_ifawithscope: dst=%s bestaddr=%s, "
"newaddr=%s, scope=%x, dcmp=%d, bcmp=%d, "
"matchlen=%d, flgs=%x\n",
ip6_sprintf(dst),
ifa_best ? ip6_sprintf(&ifa_best->ia_addr.sin6_addr) : "none",
ip6_sprintf(IFA_IN6(ifa)), src_scope,
dscopecmp,
ifa_best ? IN6_ARE_SCOPE_CMP(src_scope, best_scope) : -1,
in6_matchlen(IFA_IN6(ifa), dst),
((struct in6_ifaddr *)ifa)->ia6_flags);
#endif
/*
* call in6_matchlen() as few as possible
* Don't use an address before completing DAD
* nor a duplicated address.
*/
if (besta) {
if (blen == -1)
blen = in6_matchlen(&besta->ia_addr.sin6_addr, dst);
tlen = in6_matchlen(IFA_IN6(ifa), dst);
if (tlen > blen) {
blen = tlen;
besta = (struct in6_ifaddr *)ifa;
}
} else
besta = (struct in6_ifaddr *)ifa;
if (((struct in6_ifaddr *)ifa)->ia6_flags &
IN6_IFF_NOTREADY)
continue;
/* XXX: is there any case to allow anycasts? */
if (((struct in6_ifaddr *)ifa)->ia6_flags &
IN6_IFF_ANYCAST)
continue;
if (((struct in6_ifaddr *)ifa)->ia6_flags &
IN6_IFF_DETACHED)
continue;
/*
* If this is the first address we find,
* keep it anyway.
*/
if (ifa_best == NULL)
goto replace;
/*
* ifa_best is never NULL beyond this line except
* within the block labeled "replace".
*/
/*
* If ifa_best has a smaller scope than dst and
* the current address has a larger one than
* (or equal to) dst, always replace ifa_best.
* Also, if the current address has a smaller scope
* than dst, ignore it unless ifa_best also has a
* smaller scope.
*/
if (IN6_ARE_SCOPE_CMP(best_scope, dst_scope) < 0 &&
IN6_ARE_SCOPE_CMP(src_scope, dst_scope) >= 0)
goto replace;
if (IN6_ARE_SCOPE_CMP(src_scope, dst_scope) < 0 &&
IN6_ARE_SCOPE_CMP(best_scope, dst_scope) >= 0)
continue;
/*
* A deprecated address SHOULD NOT be used in new
* communications if an alternate (non-deprecated)
* address is available and has sufficient scope.
* RFC 2462, Section 5.5.4.
*/
if (((struct in6_ifaddr *)ifa)->ia6_flags &
IN6_IFF_DEPRECATED) {
/*
* Ignore any deprecated addresses if
* specified by configuration.
*/
if (!ip6_use_deprecated)
continue;
/*
* If we have already found a non-deprecated
* candidate, just ignore deprecated addresses.
*/
if ((ifa_best->ia6_flags & IN6_IFF_DEPRECATED)
== 0)
continue;
}
/*
* A non-deprecated address is always preferred
* to a deprecated one regardless of scopes and
* address matching.
*/
if ((ifa_best->ia6_flags & IN6_IFF_DEPRECATED) &&
(((struct in6_ifaddr *)ifa)->ia6_flags &
IN6_IFF_DEPRECATED) == 0)
goto replace;
/*
* At this point, we have two cases:
* 1. we are looking at a non-deprecated address,
* and ifa_best is also non-deprecated.
* 2. we are looking at a deprecated address,
* and ifa_best is also deprecated.
* Also, we do not have to consider a case where
* the scope of if_best is larger(smaller) than dst and
* the scope of the current address is smaller(larger)
* than dst. Such a case has already been covered.
* Tiebreaking is done according to the following
* items:
* - the scope comparison between the address and
* dst (dscopecmp)
* - the scope comparison between the address and
* ifa_best (bscopecmp)
* - if the address match dst longer than ifa_best
* (matchcmp)
* - if the address is on the outgoing I/F (outI/F)
*
* Roughly speaking, the selection policy is
* - the most important item is scope. The same scope
* is best. Then search for a larger scope.
* Smaller scopes are the last resort.
* - A deprecated address is chosen only when we have
* no address that has an enough scope, but is
* prefered to any addresses of smaller scopes.
* - Longest address match against dst is considered
* only for addresses that has the same scope of dst.
* - If there is no other reasons to choose one,
* addresses on the outgoing I/F are preferred.
*
* The precise decision table is as follows:
* dscopecmp bscopecmp matchcmp outI/F | replace?
* !equal equal N/A Yes | Yes (1)
* !equal equal N/A No | No (2)
* larger larger N/A N/A | No (3)
* larger smaller N/A N/A | Yes (4)
* smaller larger N/A N/A | Yes (5)
* smaller smaller N/A N/A | No (6)
* equal smaller N/A N/A | Yes (7)
* equal larger (already done)
* equal equal larger N/A | Yes (8)
* equal equal smaller N/A | No (9)
* equal equal equal Yes | Yes (a)
* eaual eqaul equal No | No (b)
*/
dscopecmp = IN6_ARE_SCOPE_CMP(src_scope, dst_scope);
bscopecmp = IN6_ARE_SCOPE_CMP(src_scope, best_scope);
if (dscopecmp && bscopecmp == 0) {
if (oifp == ifp) /* (1) */
goto replace;
continue; /* (2) */
}
if (dscopecmp > 0) {
if (bscopecmp > 0) /* (3) */
continue;
goto replace; /* (4) */
}
if (dscopecmp < 0) {
if (bscopecmp > 0) /* (5) */
goto replace;
continue; /* (6) */
}
/* now dscopecmp must be 0 */
if (bscopecmp < 0)
goto replace; /* (7) */
/*
* At last both dscopecmp and bscopecmp must be 0.
* We need address matching against dst for
* tiebreaking.
*/
tlen = in6_matchlen(IFA_IN6(ifa), dst);
matchcmp = tlen - blen;
if (matchcmp > 0) /* (8) */
goto replace;
if (matchcmp < 0) /* (9) */
continue;
if (oifp == ifp) /* (a) */
goto replace;
continue; /* (b) */
replace:
ifa_best = (struct in6_ifaddr *)ifa;
blen = tlen >= 0 ? tlen :
in6_matchlen(IFA_IN6(ifa), dst);
best_scope = in6_addrscope(&ifa_best->ia_addr.sin6_addr);
}
}
if (besta)
return besta;
for (ia = in6_ifaddr; ia; ia = ia->ia_next) {
if (IPV6_ADDR_SCOPE_GLOBAL !=
in6_addrscope(&(ia->ia_addr.sin6_addr)))
continue;
/* XXX: is there any case to allow anycast? */
if ((ia->ia6_flags & IN6_IFF_ANYCAST) != 0)
continue;
if ((ia->ia6_flags & IN6_IFF_NOTREADY) != 0)
continue;
if ((ia->ia6_flags & IN6_IFF_DETACHED) != 0)
continue;
if ((ia->ia6_flags & IN6_IFF_DEPRECATED) != 0) {
if (ip6_use_deprecated)
dep[1] = (struct in6_ifaddr *)ifa;
continue;
}
return ia;
/* count statistics for future improvements */
if (ifa_best == NULL)
ip6stat.ip6s_sources_none++;
else {
if (oifp == ifa_best->ia_ifp)
ip6stat.ip6s_sources_sameif[best_scope]++;
else
ip6stat.ip6s_sources_otherif[best_scope]++;
if (best_scope == dst_scope)
ip6stat.ip6s_sources_samescope[best_scope]++;
else
ip6stat.ip6s_sources_otherscope[best_scope]++;
if ((ifa_best->ia6_flags & IN6_IFF_DEPRECATED) != 0)
ip6stat.ip6s_sources_deprecated[best_scope]++;
}
/* use the last-resort values, that are, deprecated addresses */
if (dep[0])
return dep[0];
if (dep[1])
return dep[1];
return NULL;
return(ifa_best);
}
/*

View File

@ -1,5 +1,5 @@
/* $NetBSD: in6_var.h,v 1.11 2000/02/25 05:13:06 itojun Exp $ */
/* $KAME: in6_var.h,v 1.28 2000/02/25 00:32:22 itojun Exp $ */
/* $NetBSD: in6_var.h,v 1.12 2000/02/26 08:39:20 itojun Exp $ */
/* $KAME: in6_var.h,v 1.29 2000/02/25 05:20:58 itojun Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@ -570,10 +570,11 @@ void in6_savemkludge __P((struct in6_ifaddr *));
void in6_setmaxmtu __P((void));
void in6_restoremkludge __P((struct in6_ifaddr *, struct ifnet *));
void in6_purgemkludge __P((struct ifnet *));
struct in6_ifaddr *in6ifa_ifpforlinklocal __P((struct ifnet *));
struct in6_ifaddr *in6ifa_ifpforlinklocal __P((struct ifnet *, int));
struct in6_ifaddr *in6ifa_ifpwithaddr __P((struct ifnet *,
struct in6_addr *));
char *ip6_sprintf __P((struct in6_addr *));
int in6_addr2scopeid __P((struct ifnet *, struct in6_addr *));
int in6_matchlen __P((struct in6_addr *, struct in6_addr *));
int in6_are_prefix_equal __P((struct in6_addr *p1, struct in6_addr *p2,
int len));

View File

@ -1,4 +1,5 @@
/* $NetBSD: ip6_forward.c,v 1.8 2000/02/06 12:49:45 itojun Exp $ */
/* $NetBSD: ip6_forward.c,v 1.9 2000/02/26 08:39:20 itojun Exp $ */
/* $KAME: ip6_forward.c,v 1.28 2000/02/22 14:04:20 itojun Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@ -109,19 +110,17 @@ ip6_forward(m, srcrt)
}
#endif /*IPSEC_IPV6FWD*/
if (m->m_flags & (M_BCAST|M_MCAST) ||
in6_canforward(&ip6->ip6_src, &ip6->ip6_dst) == 0) {
if ((m->m_flags & (M_BCAST|M_MCAST)) != 0 ||
IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
ip6stat.ip6s_cantforward++;
ip6stat.ip6s_badscope++;
/* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */
if (ip6_log_time + ip6_log_interval < time_second) {
char addr[INET6_ADDRSTRLEN];
ip6_log_time = time_second;
strncpy(addr, ip6_sprintf(&ip6->ip6_src), sizeof(addr));
log(LOG_DEBUG,
"cannot forward "
"from %s to %s nxt %d received on %s\n",
addr, ip6_sprintf(&ip6->ip6_dst),
ip6_sprintf(&ip6->ip6_src),
ip6_sprintf(&ip6->ip6_dst),
ip6->ip6_nxt,
if_name(m->m_pkthdr.rcvif));
}
@ -324,6 +323,37 @@ ip6_forward(m, srcrt)
}
}
rt = ip6_forward_rt.ro_rt;
/*
* 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-00.txt, Section 3.1]
*/
if (in6_addr2scopeid(m->m_pkthdr.rcvif, &ip6->ip6_src) !=
in6_addr2scopeid(rt->rt_ifp, &ip6->ip6_src)) {
ip6stat.ip6s_cantforward++;
ip6stat.ip6s_badscope++;
in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard);
if (ip6_log_time + ip6_log_interval < time_second) {
ip6_log_time = time_second;
log(LOG_DEBUG,
"cannot forward "
"src %s, dst %s, nxt %d, rcvif %s, outif %s\n",
ip6_sprintf(&ip6->ip6_src),
ip6_sprintf(&ip6->ip6_dst),
ip6->ip6_nxt,
if_name(m->m_pkthdr.rcvif), if_name(rt->rt_ifp));
}
if (mcopy)
icmp6_error(mcopy, ICMP6_DST_UNREACH,
ICMP6_DST_UNREACH_BEYONDSCOPE, 0);
m_freem(m);
return;
}
if (m->m_pkthdr.len > rt->rt_ifp->if_mtu) {
in6_ifstat_inc(rt->rt_ifp, ifs6_in_toobig);
if (mcopy) {

View File

@ -1,4 +1,5 @@
/* $NetBSD: ip6_var.h,v 1.9 2000/02/03 18:13:02 itojun Exp $ */
/* $NetBSD: ip6_var.h,v 1.10 2000/02/26 08:39:20 itojun Exp $ */
/* $KAME: ip6_var.h,v 1.27 2000/02/22 14:04:22 itojun Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@ -170,9 +171,43 @@ struct ip6stat {
u_quad_t ip6s_nogif; /* no match gif found */
u_quad_t ip6s_toomanyhdr; /* discarded due to too many headers */
/* XXX the following two items are not really AF_INET6 thing */
u_quad_t ip6s_exthdrget; /* # of calls to IP6_EXTHDR_GET */
u_quad_t ip6s_exthdrget0; /* # of calls to IP6_EXTHDR_GET0 */
u_quad_t ip6s_pulldown; /* # of calls to m_pulldown */
u_quad_t ip6s_pulldown_copy; /* # of mbuf copies in m_pulldown */
u_quad_t ip6s_pulldown_alloc; /* # of mbuf allocs in m_pulldown */
u_quad_t ip6s_pullup; /* # of calls to m_pullup */
u_quad_t ip6s_pullup_copy; /* # of possible m_pullup copies */
u_quad_t ip6s_pullup_alloc; /* # of possible m_pullup mallocs */
u_quad_t ip6s_pullup_fail; /* # of possible m_pullup failures */
u_quad_t ip6s_pullup2; /* # of calls to m_pullup2 */
u_quad_t ip6s_pullup2_copy; /* # of possible m_pullup2 copies */
u_quad_t ip6s_pullup2_alloc; /* # of possible m_pullup2 mallocs */
u_quad_t ip6s_pullup2_fail; /* # of possible m_pullup2 failures */
/*
* statistics for improvement of the source address selection
* algorithm:
* XXX: hardcoded 16 = # of ip6 multicast scope types + 1
*/
/* number of times that address selection fails */
u_quad_t ip6s_sources_none;
/* number of times that an address on the outgoing I/F is chosen */
u_quad_t ip6s_sources_sameif[16];
/* number of times that an address on a non-outgoing I/F is chosen */
u_quad_t ip6s_sources_otherif[16];
/*
* number of times that an address that has the same scope
* from the destination is chosen.
*/
u_quad_t ip6s_sources_samescope[16];
/*
* number of times that an address that has a different scope
* from the destination is chosen.
*/
u_quad_t ip6s_sources_otherscope[16];
/* number of times that an deprecated address is chosen */
u_quad_t ip6s_sources_deprecated[16];
};
#ifdef _KERNEL

View File

@ -1,4 +1,5 @@
/* $NetBSD: mld6.c,v 1.10 2000/02/06 12:49:47 itojun Exp $ */
/* $NetBSD: mld6.c,v 1.11 2000/02/26 08:39:20 itojun Exp $ */
/* $KAME: mld6.c,v 1.16 2000/02/22 14:04:27 itojun Exp $ */
/*
* Copyright (C) 1998 WIDE Project.
@ -119,7 +120,7 @@ mld6_init()
mld6_timers_are_running = 0;
/* ip6h_nxt will be fill in later */
hbh->ip6h_len = 0; /* (8 >> 3) - 1*/
hbh->ip6h_len = 0; /* (8 >> 3) - 1 */
/* XXX: grotty hard coding... */
hbh_buf[2] = IP6OPT_PADN; /* 2 byte padding */
@ -140,7 +141,7 @@ mld6_start_listening(in6m)
int s = splsoftnet();
/*
* (draft-ietf-ipngwg-mld, page 10)
* RFC2710 page 10:
* The node never sends a Report or Done for the link-scope all-nodes
* address.
* MLD messages are never sent for multicast addresses whose scope is 0
@ -184,7 +185,7 @@ mld6_input(m, off)
int off;
{
struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
struct mld6_hdr *mldh = (struct mld6_hdr *)(mtod(m, caddr_t) + off);
struct mld6_hdr *mldh;
struct ifnet *ifp = m->m_pkthdr.rcvif;
struct in6_multi *in6m;
struct in6_ifaddr *ia;
@ -196,13 +197,25 @@ mld6_input(m, off)
"mld6_input: src %s is not link-local\n",
ip6_sprintf(&ip6->ip6_src));
/*
* spec(draft-ietf-ipngwg-mld) does not explicitly
* spec (RFC2710) does not explicitly
* specify to discard the packet from a non link-local
* source address. But we believe it's expected to do so.
*/
m_freem(m);
return;
}
#ifndef PULLDOWN_TEST
IP6_EXTHDR_CHECK(m, off, sizeof(*mldh),);
mldh = (struct mld6_hdr *)(mtod(m, caddr_t) + off);
#else
IP6_EXTHDR_GET(mldh, struct mld6_hdr *, m, off, sizeof(*mldh));
if (mldh == NULL) {
icmp6stat.icp6s_tooshort++;
return;
}
#endif
/*
* In the MLD6 specification, there are 3 states and a flag.
*
@ -227,25 +240,25 @@ mld6_input(m, off)
htons(ifp->if_index); /* XXX */
/*
* - Start the timers in all of our membership records
* that the query applies to for the interface on
* which the query arrived excl. those that belong
* to the "all-nodes" group (ff02::1).
* - Restart any timer that is already running but has
* A value longer than the requested timeout.
* - Use the value specified in the query message as
* the maximum timeout.
*/
* - Start the timers in all of our membership records
* that the query applies to for the interface on
* which the query arrived excl. those that belong
* to the "all-nodes" group (ff02::1).
* - Restart any timer that is already running but has
* A value longer than the requested timeout.
* - Use the value specified in the query message as
* the maximum timeout.
*/
IFP_TO_IA6(ifp, ia);
if (ia == NULL)
break;
/*
* XXX: System timer resolution is too low to handle Max
* Response Delay, so set 1 to the internal timer even if
* the calculated value equals to zero when Max Response
* Delay is positive.
*/
* XXX: System timer resolution is too low to handle Max
* Response Delay, so set 1 to the internal timer even if
* the calculated value equals to zero when Max Response
* Delay is positive.
*/
timer = ntohs(mldh->mld6_maxdelay)*PR_FASTHZ/MLD6_TIMER_SCALE;
if (timer == 0 && mldh->mld6_maxdelay)
timer = 1;
@ -287,14 +300,14 @@ mld6_input(m, off)
break;
case MLD6_LISTENER_REPORT:
/*
* For fast leave to work, we have to know that we are the
* last person to send a report for this group. Reports
* can potentially get looped back if we are a multicast
* router, so discard reports sourced by me.
* Note that it is impossible to check IFF_LOOPBACK flag of
* ifp for this purpose, since ip6_mloopback pass the physical
* interface to looutput.
*/
* For fast leave to work, we have to know that we are the
* last person to send a report for this group. Reports
* can potentially get looped back if we are a multicast
* router, so discard reports sourced by me.
* Note that it is impossible to check IFF_LOOPBACK flag of
* ifp for this purpose, since ip6_mloopback pass the physical
* interface to looutput.
*/
if (m->m_flags & M_LOOP) /* XXX: grotty flag, but efficient */
break;
@ -305,9 +318,9 @@ mld6_input(m, off)
mldh->mld6_addr.s6_addr16[1] =
htons(ifp->if_index); /* XXX */
/*
* If we belong to the group being reported, stop
* our timer for that group.
*/
* If we belong to the group being reported, stop
* our timer for that group.
*/
IN6_LOOKUP_MULTI(mldh->mld6_addr, ifp, in6m);
if (in6m) {
in6m->in6m_timer = 0; /* transit to idle state */
@ -321,6 +334,8 @@ mld6_input(m, off)
log(LOG_ERR, "mld6_input: illegal type(%d)", mldh->mld6_type);
break;
}
m_freem(m);
}
void
@ -372,7 +387,8 @@ mld6_sendpkt(in6m, type, dst)
* At first, find a link local address on the outgoing interface
* to use as the source address of the MLD packet.
*/
if ((ia = in6ifa_ifpforlinklocal(ifp)) == NULL)
if ((ia = in6ifa_ifpforlinklocal(ifp, IN6_IFF_NOTREADY|IN6_IFF_ANYCAST))
== NULL)
return;
/*

View File

@ -1,4 +1,5 @@
/* $NetBSD: nd6.c,v 1.17 2000/02/06 12:49:47 itojun Exp $ */
/* $NetBSD: nd6.c,v 1.18 2000/02/26 08:39:20 itojun Exp $ */
/* $KAME: nd6.c,v 1.41 2000/02/24 16:34:50 itojun Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@ -45,6 +46,7 @@
#include <sys/sockio.h>
#include <sys/time.h>
#include <sys/kernel.h>
#include <sys/protosw.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/syslog.h>
@ -84,7 +86,6 @@ int nd6_delay = 5; /* delay first probe time 5 second */
int nd6_umaxtries = 3; /* maximum unicast query */
int nd6_mmaxtries = 3; /* maximum multicast query */
int nd6_useloopback = 1; /* use loopback interface for local traffic */
int nd6_proxyall = 0; /* enable Proxy Neighbor Advertisement */
/* preventing too many loops in ND option parsing */
int nd6_maxndopt = 10; /* max # of ND options allowed */
@ -431,9 +432,8 @@ nd6_timer(ignored_arg)
}
break;
case ND6_LLINFO_REACHABLE:
if (ln->ln_expire) {
if (ln->ln_expire)
ln->ln_state = ND6_LLINFO_STALE;
}
break;
/*
* ND6_LLINFO_STALE state requires nothing for timer
@ -578,7 +578,7 @@ nd6_purge(ifp)
* Nuke neighbor cache entries for the ifp.
* Note that rt->rt_ifp may not be the same as ifp,
* due to KAME goto ours hack. See RTM_RESOLVE case in
* nd6_rtrequest(), and ip6_input()).
* nd6_rtrequest(), and ip6_input().
*/
ln = llinfo_nd6.ln_next;
while (ln && ln != &llinfo_nd6) {
@ -597,7 +597,8 @@ nd6_purge(ifp)
}
/*
* Interface route will be retained by nd6_free(). Nuke it.
* Neighbor cache entry for interface route will be retained
* with ND6_LLINFO_WAITDELETE state, by nd6_free(). Nuke it.
*/
ln = llinfo_nd6.ln_next;
while (ln && ln != &llinfo_nd6) {
@ -768,9 +769,19 @@ nd6_free(rt)
struct in6_addr in6 = ((struct sockaddr_in6 *)rt_key(rt))->sin6_addr;
struct nd_defrouter *dr;
/*
* Clear all destination cache entries for the neighbor.
* XXX: is it better to restrict this to hosts?
*/
pfctlinput(PRC_HOSTDEAD, rt_key(rt));
if (!ip6_forwarding && ip6_accept_rtadv) { /* XXX: too restrictive? */
int s;
#ifdef __NetBSD__
s = splsoftnet();
#else
s = splnet();
#endif
dr = defrouter_lookup(&((struct sockaddr_in6 *)rt_key(rt))->sin6_addr,
rt->rt_ifp);
if (ln->ln_router || dr) {
@ -993,7 +1004,7 @@ nd6_rtrequest(req, rt, sa)
* SIN(rt_mask(rt))->sin_addr.s_addr != 0xffffffff)
* rt->rt_flags |= RTF_CLONING;
*/
if (rt->rt_flags & RTF_CLONING || rt->rt_flags & RTF_LLINFO) {
if (rt->rt_flags & (RTF_CLONING | RTF_LLINFO)) {
/*
* Case 1: This route should come from
* a route to interface. RTF_LLINFO flag is set
@ -1020,17 +1031,37 @@ nd6_rtrequest(req, rt, sa)
if (rt->rt_flags & RTF_CLONING)
break;
}
/* Announce a new entry if requested. */
/*
* In IPv4 code, we try to annonuce new RTF_ANNOUNCE entry here.
* We don't do that here since llinfo is not ready yet.
*
* There are also couple of other things to be discussed:
* - unsolicited NA code needs improvement beforehand
* - RFC2461 says we MAY send multicast unsolicited NA
* (7.2.6 paragraph 4), however, it also says that we
* SHOULD provide a mechanism to prevent multicast NA storm.
* we don't have anything like it right now.
* note that the mechanism need a mutual agreement
* between proxies, which means that we need to implement
* a new protocol, or new kludge.
* - from RFC2461 6.2.4, host MUST NOT send unsolicited NA.
* we need to check ip6forwarding before sending it.
* (or should we allow proxy ND configuration only for
* routers? there's no mention about proxy ND from hosts)
*/
#if 0
/* XXX it does not work */
if (rt->rt_flags & RTF_ANNOUNCE)
nd6_na_output(ifp,
&SIN6(rt_key(rt))->sin6_addr,
&SIN6(rt_key(rt))->sin6_addr,
ip6_forwarding ? ND_NA_FLAG_ROUTER : 0,
1);
&SIN6(rt_key(rt))->sin6_addr,
&SIN6(rt_key(rt))->sin6_addr,
ip6_forwarding ? ND_NA_FLAG_ROUTER : 0,
1, NULL);
#endif
/* FALLTHROUGH */
case RTM_RESOLVE:
if (gate->sa_family != AF_LINK ||
gate->sa_len < sizeof(null_sdl)) {
gate->sa_len < sizeof(null_sdl)) {
log(LOG_DEBUG, "nd6_rtrequest: bad gateway value\n");
break;
}
@ -1104,12 +1135,50 @@ nd6_rtrequest(req, rt, sa)
rt->rt_ifa = ifa;
}
}
} else if (rt->rt_flags & RTF_ANNOUNCE) {
ln->ln_expire = 0;
ln->ln_state = ND6_LLINFO_REACHABLE;
/* join solicited node multicast for proxy ND */
if (ifp->if_flags & IFF_MULTICAST) {
struct in6_addr llsol;
int error;
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;
(void)in6_addmulti(&llsol, ifp, &error);
if (error)
printf(
"nd6_rtrequest: could not join solicited node multicast (errno=%d)\n", error);
}
}
break;
case RTM_DELETE:
if (!ln)
break;
/* leave from solicited node multicast for proxy ND */
if ((rt->rt_flags & RTF_ANNOUNCE) != 0 &&
(ifp->if_flags & IFF_MULTICAST) != 0) {
struct in6_addr llsol;
struct in6_multi *in6m;
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);
}
nd6_inuse--;
ln->ln_next->ln_prev = ln->ln_prev;
ln->ln_prev->ln_next = ln->ln_next;
@ -1163,7 +1232,7 @@ nd6_p2p_rtrequest(req, rt, sa)
&SIN6(rt_key(rt))->sin6_addr,
&SIN6(rt_key(rt))->sin6_addr,
ip6_forwarding ? ND_NA_FLAG_ROUTER : 0,
1);
1, NULL);
/* FALLTHROUGH */
case RTM_RESOLVE:
/*
@ -1225,6 +1294,11 @@ nd6_ioctl(cmd, data, ifp)
splx(s);
break;
case SIOCGPRLST_IN6:
/*
* XXX meaning of fields, especialy "raflags", is very
* differnet between RA prefix list and RR/static prefix list.
* how about separating ioctls into two?
*/
bzero(prl, sizeof(*prl));
s = splsoftnet();
pr = nd_prefix.lh_first;
@ -1262,11 +1336,11 @@ nd6_ioctl(cmd, data, ifp)
pfr = pfr->pfr_next;
}
prl->prefix[i].advrtrs = j;
prl->prefix[i].origin = PR_ORIG_RA;
i++;
pr = pr->ndpr_next;
}
splx(s);
{
struct rr_prefix *rpp;
@ -1282,9 +1356,11 @@ nd6_ioctl(cmd, data, ifp)
prl->prefix[i].if_index = rpp->rp_ifp->if_index;
prl->prefix[i].expire = rpp->rp_expire;
prl->prefix[i].advrtrs = 0;
prl->prefix[i].origin = rpp->rp_origin;
i++;
}
}
splx(s);
break;
case SIOCGIFINFO_IN6:

View File

@ -1,4 +1,5 @@
/* $NetBSD: nd6.h,v 1.8 2000/02/04 14:34:28 itojun Exp $ */
/* $NetBSD: nd6.h,v 1.9 2000/02/26 08:39:20 itojun Exp $ */
/* $KAME: nd6.h,v 1.16 2000/02/24 16:34:51 itojun Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@ -32,6 +33,11 @@
#ifndef _NETINET6_ND6_H_
#define _NETINET6_ND6_H_
/* see net/route.h, or net/if_inarp.h */
#ifndef RTF_ANNOUNCE
#define RTF_ANNOUNCE RTF_PROTO2
#endif
#include <sys/queue.h>
struct llinfo_nd6 {
@ -94,6 +100,7 @@ struct in6_prlist {
struct in6_addr prefix;
struct prf_ra raflags;
u_char prefixlen;
u_char origin;
u_long vltime;
u_long pltime;
u_long expire;
@ -220,7 +227,6 @@ extern int nd6_delay;
extern int nd6_umaxtries;
extern int nd6_mmaxtries;
extern int nd6_useloopback;
extern int nd6_proxyall;
extern struct llinfo_nd6 llinfo_nd6;
extern struct nd_ifinfo *nd_ifinfo;
extern struct nd_drhead nd_defrouter;
@ -284,7 +290,7 @@ int nd6_storelladdr __P((struct ifnet *, struct rtentry *, struct mbuf *,
/* nd6_nbr.c */
void nd6_na_input __P((struct mbuf *, int, int));
void nd6_na_output __P((struct ifnet *, struct in6_addr *,
struct in6_addr *, u_long, int));
struct in6_addr *, u_long, int, struct sockaddr *));
void nd6_ns_input __P((struct mbuf *, int, int));
void nd6_ns_output __P((struct ifnet *, struct in6_addr *,
struct in6_addr *, struct llinfo_nd6 *, int));

View File

@ -1,4 +1,5 @@
/* $NetBSD: nd6_nbr.c,v 1.15 2000/02/07 05:42:28 itojun Exp $ */
/* $NetBSD: nd6_nbr.c,v 1.16 2000/02/26 08:39:20 itojun Exp $ */
/* $KAME: nd6_nbr.c,v 1.28 2000/02/26 06:53:11 itojun Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@ -77,8 +78,6 @@ static int dad_maxtry = 15; /* max # of *tries* to transmit DAD packet */
*
* Based on RFC 2461
* Based on RFC 2462 (duplicated address detection)
*
* XXX proxy advertisement
*/
void
nd6_ns_input(m, off, icmp6len)
@ -87,11 +86,10 @@ nd6_ns_input(m, off, icmp6len)
{
struct ifnet *ifp = m->m_pkthdr.rcvif;
struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
struct nd_neighbor_solicit *nd_ns
= (struct nd_neighbor_solicit *)((caddr_t)ip6 + off);
struct nd_neighbor_solicit *nd_ns;
struct in6_addr saddr6 = ip6->ip6_src;
struct in6_addr daddr6 = ip6->ip6_dst;
struct in6_addr taddr6 = nd_ns->nd_ns_target;
struct in6_addr taddr6;
struct in6_addr myaddr6;
char *lladdr = NULL;
struct ifaddr *ifa;
@ -99,11 +97,12 @@ nd6_ns_input(m, off, icmp6len)
int anycast = 0, proxy = 0, tentative = 0;
int tlladdr;
union nd_opts ndopts;
struct sockaddr_dl *proxydl = NULL;
if (ip6->ip6_hlim != 255) {
log(LOG_ERR,
"nd6_ns_input: invalid hlim %d\n", ip6->ip6_hlim);
return;
goto freeit;
}
if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) {
@ -121,6 +120,18 @@ nd6_ns_input(m, off, icmp6len)
}
}
#ifndef PULLDOWN_TEST
IP6_EXTHDR_CHECK(m, off, icmp6len,);
nd_ns = (struct nd_neighbor_solicit *)((caddr_t)ip6 + off);
#else
IP6_EXTHDR_GET(nd_ns, struct nd_neighbor_solicit *, m, off, icmp6len);
if (nd_ns == NULL) {
icmp6stat.icp6s_tooshort++;
return;
}
#endif
taddr6 = nd_ns->nd_ns_target;
if (IN6_IS_ADDR_MULTICAST(&taddr6)) {
log(LOG_INFO, "nd6_ns_input: bad NS target (multicast)\n");
goto bad;
@ -178,7 +189,7 @@ nd6_ns_input(m, off, icmp6len)
ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &taddr6);
/* (2) check. */
if (!ifa && nd6_proxyall) {
if (!ifa) {
struct rtentry *rt;
struct sockaddr_in6 tsin6;
@ -187,17 +198,25 @@ nd6_ns_input(m, off, icmp6len)
tsin6.sin6_family = AF_INET6;
tsin6.sin6_addr = taddr6;
rt = rtalloc1((struct sockaddr *)&tsin6, 0);
if (rt && rt->rt_ifp != ifp) {
rt = rtalloc1((struct sockaddr *)&tsin6, 0
#ifdef __FreeBSD__
, 0
#endif /* __FreeBSD__ */
);
if (rt && (rt->rt_flags & RTF_ANNOUNCE) != 0 &&
rt->rt_gateway->sa_family == AF_LINK) {
/*
* search link local addr for ifp, and use it for
* proxy NA.
* proxy NDP for single entry
*/
ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp);
if (ifa)
ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp,
IN6_IFF_NOTREADY|IN6_IFF_ANYCAST);
if (ifa) {
proxy = 1;
proxydl = SDL(rt->rt_gateway);
}
}
rtfree(rt);
if (rt)
rtfree(rt);
}
if (!ifa) {
/*
@ -205,13 +224,13 @@ nd6_ns_input(m, off, icmp6len)
* assigned for us. We MUST silently ignore it.
* See RFC2461 7.2.3.
*/
return;
goto freeit;
}
myaddr6 = *IFA_IN6(ifa);
anycast = ((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST;
tentative = ((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_TENTATIVE;
if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DUPLICATED)
return;
goto freeit;
if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) {
log(LOG_INFO,
@ -224,7 +243,7 @@ nd6_ns_input(m, off, icmp6len)
log(LOG_INFO,
"nd6_ns_input: duplicate IP6 address %s\n",
ip6_sprintf(&saddr6));
return;
goto freeit;
}
/*
@ -250,7 +269,7 @@ nd6_ns_input(m, off, icmp6len)
if (IN6_IS_ADDR_UNSPECIFIED(&saddr6))
nd6_dad_ns_input(ifa);
return;
goto freeit;
}
/*
@ -268,8 +287,8 @@ nd6_ns_input(m, off, icmp6len)
((anycast || proxy || !tlladdr)
? 0 : ND_NA_FLAG_OVERRIDE)
| (ip6_forwarding ? ND_NA_FLAG_ROUTER : 0),
tlladdr);
return;
tlladdr, (struct sockaddr *)proxydl);
goto freeit;
}
nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_NEIGHBOR_SOLICIT, 0);
@ -278,14 +297,16 @@ nd6_ns_input(m, off, icmp6len)
((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE)
| (ip6_forwarding ? ND_NA_FLAG_ROUTER : 0)
| ND_NA_FLAG_SOLICITED,
tlladdr);
tlladdr, (struct sockaddr *)proxydl);
freeit:
m_freem(m);
return;
bad:
log(LOG_ERR, "nd6_ns_input: src=%s\n", ip6_sprintf(&saddr6));
log(LOG_ERR, "nd6_ns_input: dst=%s\n", ip6_sprintf(&daddr6));
log(LOG_ERR, "nd6_ns_input: tgt=%s\n", ip6_sprintf(&taddr6));
return;
m_freem(m);
}
/*
@ -485,6 +506,10 @@ nd6_ns_output(ifp, daddr6, taddr6, ln, dad)
*
* Based on RFC 2461
* Based on RFC 2462 (duplicated address detection)
*
* the following items are not implemented yet:
* - proxy advertisement delay rule (RFC2461 7.2.8, last paragraph, SHOULD)
* - anycast advertisement delay rule (RFC2461 7.2.7, SHOULD)
*/
void
nd6_na_input(m, off, icmp6len)
@ -493,17 +518,16 @@ nd6_na_input(m, off, icmp6len)
{
struct ifnet *ifp = m->m_pkthdr.rcvif;
struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
struct nd_neighbor_advert *nd_na
= (struct nd_neighbor_advert *)((caddr_t)ip6 + off);
struct nd_neighbor_advert *nd_na;
#if 0
struct in6_addr saddr6 = ip6->ip6_src;
#endif
struct in6_addr daddr6 = ip6->ip6_dst;
struct in6_addr taddr6 = nd_na->nd_na_target;
int flags = nd_na->nd_na_flags_reserved;
int is_router = ((flags & ND_NA_FLAG_ROUTER) != 0);
int is_solicited = ((flags & ND_NA_FLAG_SOLICITED) != 0);
int is_override = ((flags & ND_NA_FLAG_OVERRIDE) != 0);
struct in6_addr taddr6;
int flags;
int is_router;
int is_solicited;
int is_override;
char *lladdr = NULL;
int lladdrlen = 0;
struct ifaddr *ifa;
@ -515,8 +539,24 @@ nd6_na_input(m, off, icmp6len)
if (ip6->ip6_hlim != 255) {
log(LOG_ERR,
"nd6_na_input: invalid hlim %d\n", ip6->ip6_hlim);
goto freeit;
}
#ifndef PULLDOWN_TEST
IP6_EXTHDR_CHECK(m, off, icmp6len,);
nd_na = (struct nd_neighbor_advert *)((caddr_t)ip6 + off);
#else
IP6_EXTHDR_GET(nd_na, struct nd_neighbor_advert *, m, off, icmp6len);
if (nd_na == NULL) {
icmp6stat.icp6s_tooshort++;
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);
@ -525,20 +565,20 @@ nd6_na_input(m, off, icmp6len)
log(LOG_ERR,
"nd6_na_input: invalid target address %s\n",
ip6_sprintf(&taddr6));
return;
goto freeit;
}
if (IN6_IS_ADDR_MULTICAST(&daddr6))
if (is_solicited) {
log(LOG_ERR,
"nd6_na_input: a solicited adv is multicasted\n");
return;
goto freeit;
}
icmp6len -= sizeof(*nd_na);
nd6_option_init(nd_na + 1, icmp6len, &ndopts);
if (nd6_options(&ndopts) < 0) {
log(LOG_INFO, "nd6_na_input: invalid ND option, ignored\n");
return;
goto freeit;
}
if (ndopts.nd_opts_tgt_lladdr) {
@ -560,7 +600,7 @@ nd6_na_input(m, off, icmp6len)
if (ifa
&& (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_TENTATIVE)) {
nd6_dad_na_input(ifa);
return;
goto freeit;
}
/* Just for safety, maybe unnecessery. */
@ -568,7 +608,7 @@ nd6_na_input(m, off, icmp6len)
log(LOG_ERR,
"nd6_na_input: duplicate IP6 address %s\n",
ip6_sprintf(&taddr6));
return;
goto freeit;
}
if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) {
@ -585,7 +625,7 @@ nd6_na_input(m, off, icmp6len)
if ((rt == NULL) ||
((ln = (struct llinfo_nd6 *)rt->rt_llinfo) == NULL) ||
((sdl = SDL(rt->rt_gateway)) == NULL))
return;
goto freeit;
if (ln->ln_state == ND6_LLINFO_INCOMPLETE) {
/*
@ -593,7 +633,7 @@ nd6_na_input(m, off, icmp6len)
* discard the packet.
*/
if (ifp->if_addrlen && !lladdr)
return;
goto freeit;
/*
* Record link-layer address, and update the state.
@ -652,7 +692,7 @@ nd6_na_input(m, off, icmp6len)
*/
if (ln->ln_state == ND6_LLINFO_REACHABLE)
ln->ln_state = ND6_LLINFO_STALE;
return;
goto freeit;
} else if (is_override /* (2a) */
|| (!is_override && (lladdr && !llchange)) /* (2b) */
|| !lladdr) { /* (2c) */
@ -721,6 +761,9 @@ nd6_na_input(m, off, icmp6len)
#endif
ln->ln_hold = 0;
}
freeit:
m_freem(m);
}
/*
@ -728,16 +771,17 @@ nd6_na_input(m, off, icmp6len)
*
* Based on RFC 2461
*
* XXX NA delay for anycast address is not implemented yet
* (RFC 2461 7.2.7)
* XXX proxy advertisement?
* the following items are not implemented yet:
* - proxy advertisement delay rule (RFC2461 7.2.8, last paragraph, SHOULD)
* - anycast advertisement delay rule (RFC2461 7.2.7, SHOULD)
*/
void
nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr)
nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr, sdl0)
struct ifnet *ifp;
struct in6_addr *daddr6, *taddr6;
u_long flags;
int tlladdr; /* 1 if include target link-layer address */
int tlladdr; /* 1 if include target link-layer address */
struct sockaddr *sdl0; /* sockaddr_dl (= proxy NA) or NULL */
{
struct mbuf *m;
struct ip6_hdr *ip6;
@ -822,7 +866,23 @@ nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr)
* Basically, if NS packet is sent to unicast/anycast addr,
* target lladdr option SHOULD NOT be included.
*/
if (tlladdr && (mac = nd6_ifptomac(ifp))) {
if (tlladdr) {
mac = NULL;
/*
* sdl0 != NULL indicates proxy NA. If we do proxy, use
* lladdr in sdl0. If we are not proxying (sending NA for
* my address) use lladdr configured for the interface.
*/
if (sdl0 == NULL)
mac = nd6_ifptomac(ifp);
else if (sdl0->sa_family == AF_LINK) {
struct sockaddr_dl *sdl;
sdl = (struct sockaddr_dl *)sdl0;
if (sdl->sdl_alen == ifp->if_addrlen)
mac = LLADDR(sdl);
}
}
if (tlladdr && mac) {
int optlen = sizeof(struct nd_opt_hdr) + ifp->if_addrlen;
struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)(nd_na + 1);

View File

@ -1,4 +1,5 @@
/* $NetBSD: nd6_rtr.c,v 1.11 2000/02/06 12:49:48 itojun Exp $ */
/* $NetBSD: nd6_rtr.c,v 1.12 2000/02/26 08:39:21 itojun Exp $ */
/* $KAME: nd6_rtr.c,v 1.27 2000/02/26 06:53:11 itojun Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@ -97,8 +98,7 @@ nd6_rs_input(m, off, icmp6len)
{
struct ifnet *ifp = m->m_pkthdr.rcvif;
struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
struct nd_router_solicit *nd_rs
= (struct nd_router_solicit *)((caddr_t)ip6 + off);
struct nd_router_solicit *nd_rs;
struct in6_addr saddr6 = ip6->ip6_src;
#if 0
struct in6_addr daddr6 = ip6->ip6_dst;
@ -115,13 +115,13 @@ nd6_rs_input(m, off, icmp6len)
/* If I'm not a router, ignore it. */
if (ip6_accept_rtadv != 0 || ip6_forwarding != 1)
return;
goto freeit;
/* Sanity checks */
if (ip6->ip6_hlim != 255) {
log(LOG_ERR,
"nd6_rs_input: invalid hlim %d\n", ip6->ip6_hlim);
return;
goto freeit;
}
/*
@ -129,13 +129,24 @@ nd6_rs_input(m, off, icmp6len)
* This indicates that the src has no IP address assigned yet.
*/
if (IN6_IS_ADDR_UNSPECIFIED(&saddr6))
goto freeit;
#ifndef PULLDOWN_TEST
IP6_EXTHDR_CHECK(m, off, icmp6len,);
nd_rs = (struct nd_router_solicit *)((caddr_t)ip6 + off);
#else
IP6_EXTHDR_GET(nd_rs, struct nd_router_solicit *, m, off, icmp6len);
if (nd_rs == NULL) {
icmp6stat.icp6s_tooshort++;
return;
}
#endif
icmp6len -= sizeof(*nd_rs);
nd6_option_init(nd_rs + 1, icmp6len, &ndopts);
if (nd6_options(&ndopts) < 0) {
log(LOG_INFO, "nd6_rs_input: invalid ND option, ignored\n");
return;
goto freeit;
}
if (ndopts.nd_opts_src_lladdr) {
@ -151,6 +162,9 @@ nd6_rs_input(m, off, icmp6len)
}
nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_ROUTER_SOLICIT, 0);
freeit:
m_freem(m);
}
/*
@ -168,12 +182,11 @@ nd6_ra_input(m, off, icmp6len)
struct ifnet *ifp = m->m_pkthdr.rcvif;
struct nd_ifinfo *ndi = &nd_ifinfo[ifp->if_index];
struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
struct nd_router_advert *nd_ra =
(struct nd_router_advert *)((caddr_t)ip6 + off);
struct nd_router_advert *nd_ra;
struct in6_addr saddr6 = ip6->ip6_src;
#if 0
struct in6_addr daddr6 = ip6->ip6_dst;
int flags = nd_ra->nd_ra_flags_reserved;
int flags; /* = nd_ra->nd_ra_flags_reserved; */
int is_managed = ((flags & ND_RA_FLAG_MANAGED) != 0);
int is_other = ((flags & ND_RA_FLAG_OTHER) != 0);
#endif
@ -181,26 +194,37 @@ nd6_ra_input(m, off, icmp6len)
struct nd_defrouter *dr;
if (ip6_accept_rtadv == 0)
return;
goto freeit;
if (ip6->ip6_hlim != 255) {
log(LOG_ERR,
"nd6_ra_input: invalid hlim %d\n", ip6->ip6_hlim);
return;
goto freeit;
}
if (!IN6_IS_ADDR_LINKLOCAL(&saddr6)) {
log(LOG_ERR,
"nd6_ra_input: src %s is not link-local\n",
ip6_sprintf(&saddr6));
goto freeit;
}
#ifndef PULLDOWN_TEST
IP6_EXTHDR_CHECK(m, off, icmp6len,);
nd_ra = (struct nd_router_advert *)((caddr_t)ip6 + off);
#else
IP6_EXTHDR_GET(nd_ra, struct nd_router_advert *, m, off, icmp6len);
if (nd_ra == NULL) {
icmp6stat.icp6s_tooshort++;
return;
}
#endif
icmp6len -= sizeof(*nd_ra);
nd6_option_init(nd_ra + 1, icmp6len, &ndopts);
if (nd6_options(&ndopts) < 0) {
log(LOG_INFO, "nd6_ra_input: invalid ND option, ignored\n");
return;
goto freeit;
}
{
@ -367,6 +391,9 @@ nd6_ra_input(m, off, icmp6len)
*/
pfxlist_onlink_check();
}
freeit:
m_freem(m);
}
/*
@ -1160,7 +1187,7 @@ in6_ifadd(ifp, in6, addr, prefixlen)
in6_len2mask(&mask, prefixlen);
/* find link-local address (will be interface ID) */
ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp);
ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp, 0);/* 0 is OK? */
if (ifa)
ib = (struct in6_ifaddr *)ifa;
else
@ -1341,7 +1368,7 @@ in6_ifdel(ifp, in6)
}
TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list);
IFAFREE((struct ifaddr *)ia);
IFAFREE(&ia->ia_ifa);
/* lladdr is never deleted */
oia = ia;