summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoritojun <itojun@openbsd.org>2000-02-28 11:55:20 +0000
committeritojun <itojun@openbsd.org>2000-02-28 11:55:20 +0000
commitf4f4d166a5b90d7c5fa648ad3f10dd92cbac4be0 (patch)
treed121e05d91dcb4b1567306e66de7147463f78fae
parentAdd back the OpenBSDism that a #commented realm means Kerberos is disabled. (diff)
downloadwireguard-openbsd-f4f4d166a5b90d7c5fa648ad3f10dd92cbac4be0.tar.xz
wireguard-openbsd-f4f4d166a5b90d7c5fa648ad3f10dd92cbac4be0.zip
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. - nuke xxCTL_VARS #define, they are for BSDI. - disable SIOCSIFDSTADDR_IN6/SIOCSIFNETMASK_IN6 ioctl, they do not fit IPv6 model where multiple address on interface is normal. (kernel side supports them for a while for backward compat, the support will be nuked shortly) - introduce "default outgoing interface" (for spec conformance in very rare case)
-rw-r--r--sys/netinet/icmp6.h28
-rw-r--r--sys/netinet6/icmp6.c400
-rw-r--r--sys/netinet6/in6.c584
-rw-r--r--sys/netinet6/in6.h46
-rw-r--r--sys/netinet6/in6_cksum.c2
-rw-r--r--sys/netinet6/in6_pcb.c2
-rw-r--r--sys/netinet6/in6_proto.c2
-rw-r--r--sys/netinet6/in6_src.c5
-rw-r--r--sys/netinet6/in6_var.h24
-rw-r--r--sys/netinet6/ip6_forward.c86
-rw-r--r--sys/netinet6/ip6_var.h37
-rw-r--r--sys/netinet6/mld6.c78
-rw-r--r--sys/netinet6/nd6.c216
-rw-r--r--sys/netinet6/nd6.h32
-rw-r--r--sys/netinet6/nd6_nbr.c151
-rw-r--r--sys/netinet6/nd6_rtr.c371
-rw-r--r--sys/netinet6/raw_ipv6.c5
-rw-r--r--sys/netinet6/tcpipv6.h2
18 files changed, 1395 insertions, 676 deletions
diff --git a/sys/netinet/icmp6.h b/sys/netinet/icmp6.h
index cff11780015..fbd96f0107f 100644
--- a/sys/netinet/icmp6.h
+++ b/sys/netinet/icmp6.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: icmp6.h,v 1.2 2000/02/07 05:45:55 itojun Exp $ */
+/* $OpenBSD: icmp6.h,v 1.3 2000/02/28 11:55:20 itojun Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -469,12 +469,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 +525,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,28 +542,10 @@ struct icmp6stat {
{ "nd6_umaxtries", CTLTYPE_INT }, \
{ "nd6_mmaxtries", CTLTYPE_INT }, \
{ "nd6_useloopback", CTLTYPE_INT }, \
- { "nd6_proxyall", CTLTYPE_INT }, \
+ { 0, 0 }, \
{ "nodeinfo", CTLTYPE_INT }, \
}
-#define ICMPV6CTL_VARS { \
- 0, \
- 0, \
- &icmp6_rediraccept, \
- &icmp6_redirtimeout, \
- 0, \
- 0, \
- &icmp6errratelim, \
- &nd6_prune, \
- 0, \
- &nd6_delay, \
- &nd6_umaxtries, \
- &nd6_mmaxtries, \
- &nd6_useloopback, \
- &nd6_proxyall, \
- &icmp6_nodeinfo, \
-}
-
#define RTF_PROBEMTU RTF_PROTO1
#ifdef _KERNEL
diff --git a/sys/netinet6/icmp6.c b/sys/netinet6/icmp6.c
index 2cb30386901..7cf9f5db7fe 100644
--- a/sys/netinet6/icmp6.c
+++ b/sys/netinet6/icmp6.c
@@ -1,4 +1,5 @@
-/* $OpenBSD: icmp6.c,v 1.6 2000/02/07 06:09:09 itojun Exp $ */
+/* $OpenBSD: icmp6.c,v 1.7 2000/02/28 11:55:21 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.
@@ -112,6 +113,8 @@ extern int icmp6_nodeinfo;
static struct rttimer_queue *icmp6_mtudisc_timeout_q = NULL;
extern int pmtu_expire;
+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 *));
@@ -145,7 +148,7 @@ icmp6_error(m, type, code, param)
{
struct ip6_hdr *oip6, *nip6;
struct icmp6_hdr *icmp6;
- u_int prep;
+ u_int preplen;
int off;
u_char nxt;
@@ -194,6 +197,7 @@ icmp6_error(m, type, code, param)
case IPPROTO_UDP:
case IPPROTO_TCP:
case IPPROTO_ESP:
+ case IPPROTO_IPCOMP:
case IPPROTO_FRAGMENT:
/*
* ICMPv6 error must not be fragmented.
@@ -274,10 +278,10 @@ icmp6_error(m, type, code, param)
if (m->m_pkthdr.len >= ICMPV6_PLD_MAXLEN)
m_adj(m, ICMPV6_PLD_MAXLEN - m->m_pkthdr.len);
- prep = sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr);
- M_PREPEND(m, prep, M_DONTWAIT);
- if (m && m->m_len < prep)
- m = m_pullup(m, prep);
+ preplen = sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr);
+ M_PREPEND(m, preplen, M_DONTWAIT);
+ if (m && m->m_len < preplen)
+ m = m_pullup(m, preplen);
if (m == NULL) {
printf("ENOBUFS in icmp6_error %d\n", __LINE__);
return;
@@ -398,12 +402,21 @@ icmp6_input(mp, offp, proto)
break;
case ICMP6_DST_UNREACH_ADMIN:
icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_adminprohib);
+ code = PRC_UNREACH_PROTOCOL; /* is this a good code? */
+ break;
case ICMP6_DST_UNREACH_ADDR:
- code = PRC_UNREACH_HOST;
+ 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;
@@ -417,55 +430,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:
@@ -508,12 +480,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);
@@ -566,8 +554,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;
@@ -581,7 +574,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 */
{
@@ -611,12 +604,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.
*/
@@ -626,11 +639,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;
}
@@ -654,8 +667,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;
@@ -665,8 +683,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;
@@ -676,8 +699,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;
@@ -687,8 +715,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;
@@ -698,7 +731,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;
@@ -754,19 +793,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),
@@ -781,15 +821,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;
}
}
@@ -804,6 +934,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) {
@@ -824,7 +960,6 @@ icmp6_input(mp, offp, proto)
break;
}
- passit:
rip6_input(&m, offp, IPPROTO_ICMPV6);
return IPPROTO_DONE;
@@ -833,6 +968,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
*/
@@ -1244,10 +1415,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;
}
@@ -1261,9 +1435,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;
@@ -1335,7 +1511,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;
@@ -1345,8 +1521,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)
@@ -1354,28 +1530,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 */
@@ -1397,14 +1586,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;
@@ -1414,7 +1603,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;
@@ -1427,7 +1616,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 */
@@ -1437,7 +1626,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) {
@@ -1515,6 +1704,9 @@ icmp6_redirect_input(m, off)
key_sa_routechange((struct sockaddr *)&sdst);
#endif
}
+
+ freeit:
+ m_freem(m);
}
void
@@ -1562,21 +1754,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;
}
@@ -1643,6 +1842,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) &&
@@ -1650,12 +1854,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:;
@@ -1912,8 +2114,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:
diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c
index 5977eb44145..c4ba2ff1fa2 100644
--- a/sys/netinet6/in6.c
+++ b/sys/netinet6/in6.c
@@ -1,8 +1,10 @@
-/* $OpenBSD: in6.c,v 1.13 2000/02/07 06:05:06 itojun Exp $ */
+/* $OpenBSD: in6.c,v 1.14 2000/02/28 11:55:21 itojun Exp $ */
+/* $KAME: in6.c,v 1.55 2000/02/25 00:32:23 itojun Exp $ */
+
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
* All rights reserved.
- *
+ *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@@ -14,7 +16,7 @@
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
- *
+ *
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
@@ -100,6 +102,9 @@
#include <net/net_osdep.h>
+/* backward compatibility for a while... */
+#define COMPAT_IN6IFIOCTL
+
/*
* Definitions of some costant IP6 addresses.
*/
@@ -134,22 +139,6 @@ struct multi6_kludge {
};
/*
- * 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.
* if 1 returned, will be automatically generated.
@@ -246,90 +235,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;
@@ -389,7 +294,7 @@ in6_len2mask(mask, len)
}
#define ifa2ia6(ifa) ((struct in6_ifaddr *)(ifa))
-#define ia62ifa(ia6) ((struct ifaddr *)(ia6))
+#define ia62ifa(ia6) (&((ia6)->ia_ifa))
int
in6_control(so, cmd, data, ifp, p)
@@ -402,7 +307,10 @@ in6_control(so, cmd, data, ifp, p)
struct in6_ifreq *ifr = (struct in6_ifreq *)data;
struct in6_ifaddr *ia, *oia;
struct in6_aliasreq *ifra = (struct in6_aliasreq *)data;
- struct sockaddr_in6 oldaddr, net;
+ struct sockaddr_in6 oldaddr;
+#ifdef COMPAT_IN6IFIOCTL
+ struct sockaddr_in6 net;
+#endif
int error = 0, hostIsNew, prefixIsNew;
time_t time_second = (time_t)time.tv_sec;
int privileged;
@@ -440,6 +348,7 @@ in6_control(so, cmd, data, ifp, p)
case SIOCSNDFLUSH_IN6:
case SIOCSPFXFLUSH_IN6:
case SIOCSRTRFLUSH_IN6:
+ case SIOCSDEFIFACE_IN6:
if (!privileged)
return(EPERM);
/*fall through*/
@@ -447,6 +356,7 @@ in6_control(so, cmd, data, ifp, p)
case SIOCGDRLST_IN6:
case SIOCGPRLST_IN6:
case SIOCGNBRINFO_IN6:
+ case SIOCGDEFIFACE_IN6:
return(nd6_ioctl(cmd, data, ifp));
}
@@ -476,8 +386,7 @@ in6_control(so, cmd, data, ifp, p)
/*
* 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;
@@ -487,10 +396,10 @@ in6_control(so, cmd, data, ifp, p)
sa6->sin6_addr.s6_addr16[1] =
htons(ifp->if_index);
}
- else
- if (sa6->sin6_addr.s6_addr16[1] !=
- htons(ifp->if_index))
- return(EINVAL); /* ifid is contradict */
+ else if (sa6->sin6_addr.s6_addr16[1] !=
+ htons(ifp->if_index)) {
+ return(EINVAL); /* ifid is contradict */
+ }
if (sa6->sin6_scope_id) {
if (sa6->sin6_scope_id !=
(u_int32_t)ifp->if_index)
@@ -498,36 +407,53 @@ in6_control(so, cmd, data, ifp, p)
sa6->sin6_scope_id = 0; /* XXX: good way? */
}
}
- }
-#if 0
- if (ifra->ifra_addr.sin6_family == AF_INET6) {
ia = in6ifa_ifpwithaddr(ifp, &ifra->ifra_addr.sin6_addr);
}
-#else
- ia = in6ifa_ifpwithaddr(ifp, &ifra->ifra_addr.sin6_addr);
-#endif
switch (cmd) {
case SIOCDIFADDR_IN6:
- if (ia == 0)
+ /*
+ * for IPv4, we look for existing in6_ifaddr here to allow
+ * "ifconfig if0 delete" to remove first IPv4 address on the
+ * interface. For IPv6, as the spec allow multiple interface
+ * address from the day one, we consider "remove the first one"
+ * semantics to be not preferrable.
+ */
+ if (ia == NULL)
return(EADDRNOTAVAIL);
/* FALLTHROUGH */
case SIOCAIFADDR_IN6:
case SIOCSIFADDR_IN6:
- case SIOCSIFNETMASK_IN6:
+#ifdef COMPAT_IN6IFIOCTL
case SIOCSIFDSTADDR_IN6:
+ case SIOCSIFNETMASK_IN6:
+ /*
+ * Since IPv6 allows a node to assign multiple addresses
+ * on a single interface, SIOCSIFxxx ioctls are not suitable
+ * and should be unused.
+ */
+#endif
+ if (ifra->ifra_addr.sin6_family != AF_INET6)
+ return(EAFNOSUPPORT);
if (!privileged)
return(EPERM);
- if (ia == 0) {
+ if (ia == NULL) {
ia = (struct in6_ifaddr *)
malloc(sizeof(*ia), M_IFADDR, M_WAITOK);
if (ia == NULL)
return (ENOBUFS);
bzero((caddr_t)ia, sizeof(*ia));
+ /* Initialize the address and masks */
ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr;
+ ia->ia_addr.sin6_family = AF_INET6;
+ ia->ia_addr.sin6_len = sizeof(ia->ia_addr);
ia->ia_ifa.ifa_dstaddr
= (struct sockaddr *)&ia->ia_dstaddr;
+ if (ifp->if_flags & IFF_POINTOPOINT) {
+ ia->ia_dstaddr.sin6_family = AF_INET6;
+ ia->ia_dstaddr.sin6_len = sizeof(ia->ia_dstaddr);
+ }
ia->ia_ifa.ifa_netmask
= (struct sockaddr *)&ia->ia_prefixmask;
@@ -542,7 +468,7 @@ in6_control(so, cmd, data, ifp, p)
TAILQ_INSERT_TAIL(&ifp->if_addrlist,
(struct ifaddr *)ia, ifa_list);
- oia->ia_next = ia;
+ ia->ia_ifa.ifa_refcnt++;
}
if (cmd == SIOCAIFADDR_IN6) {
@@ -568,7 +494,7 @@ in6_control(so, cmd, data, ifp, p)
case SIOCGIFDSTADDR_IN6:
case SIOCGIFALIFETIME_IN6:
/* must think again about its semantics */
- if (ia == 0)
+ if (ia == NULL)
return(EADDRNOTAVAIL);
break;
case SIOCSIFALIFETIME_IN6:
@@ -577,7 +503,7 @@ in6_control(so, cmd, data, ifp, p)
if (!privileged)
return(EPERM);
- if (ia == 0)
+ if (ia == NULL)
return(EADDRNOTAVAIL);
/* sanity for overflow - beware unsigned */
lt = &ifr->ifr_ifru.ifru_lifetime;
@@ -612,7 +538,7 @@ in6_control(so, cmd, data, ifp, p)
case SIOCGIFAFLAG_IN6:
ifr->ifr_ifru.ifru_flags6 = ia->ia6_flags;
break;
-
+
case SIOCGIFSTAT_IN6:
if (ifp == NULL)
return EINVAL;
@@ -638,6 +564,7 @@ in6_control(so, cmd, data, ifp, p)
*icmp6_ifstat[ifp->if_index];
break;
+#ifdef COMPAT_IN6IFIOCTL /* should be unused */
case SIOCSIFDSTADDR_IN6:
if ((ifp->if_flags & IFF_POINTOPOINT) == 0)
return(EINVAL);
@@ -651,12 +578,11 @@ in6_control(so, cmd, data, ifp, p)
ia->ia_dstaddr.sin6_addr.s6_addr16[1]
= htons(ifp->if_index);
}
- else
- if (ia->ia_dstaddr.sin6_addr.s6_addr16[1] !=
+ else if (ia->ia_dstaddr.sin6_addr.s6_addr16[1] !=
htons(ifp->if_index)) {
- ia->ia_dstaddr = oldaddr;
- return(EINVAL); /* ifid is contradict */
- }
+ ia->ia_dstaddr = oldaddr;
+ return(EINVAL); /* ifid is contradict */
+ }
}
if (ifp->if_ioctl && (error = (ifp->if_ioctl)
@@ -673,6 +599,7 @@ in6_control(so, cmd, data, ifp, p)
}
break;
+#endif
case SIOCGIFALIFETIME_IN6:
ifr->ifr_ifru.ifru_lifetime = ia->ia6_lifetime;
break;
@@ -695,6 +622,7 @@ in6_control(so, cmd, data, ifp, p)
case SIOCSIFADDR_IN6:
return(in6_ifinit(ifp, ia, &ifr->ifr_addr, 1));
+#ifdef COMPAT_IN6IFIOCTL /* XXX should be unused */
case SIOCSIFNETMASK_IN6:
ia->ia_prefixmask = ifr->ifr_addr;
bzero(&net, sizeof(net));
@@ -716,6 +644,7 @@ in6_control(so, cmd, data, ifp, p)
ia->ia_prefixmask.sin6_addr.s6_addr32[3];
ia->ia_net = net;
break;
+#endif
case SIOCAIFADDR_IN6:
prefixIsNew = 0;
@@ -728,6 +657,22 @@ in6_control(so, cmd, data, ifp, p)
&ia->ia_addr.sin6_addr))
hostIsNew = 0;
+ /* Validate address families: */
+ /*
+ * The destination address for a p2p link must have a family
+ * of AF_UNSPEC or AF_INET6.
+ */
+ if ((ifp->if_flags & IFF_POINTOPOINT) != 0 &&
+ ifra->ifra_dstaddr.sin6_family != AF_INET6 &&
+ ifra->ifra_dstaddr.sin6_family != AF_UNSPEC)
+ return(EAFNOSUPPORT);
+ /*
+ * The prefixmask must have a family of AF_UNSPEC or AF_INET6.
+ */
+ if (ifra->ifra_prefixmask.sin6_family != AF_INET6 &&
+ ifra->ifra_prefixmask.sin6_family != AF_UNSPEC)
+ return(EAFNOSUPPORT);
+
if (ifra->ifra_prefixmask.sin6_len) {
in6_ifscrub(ifp, ia);
ia->ia_prefixmask = ifra->ifra_prefixmask;
@@ -747,21 +692,17 @@ in6_control(so, cmd, data, ifp, p)
*/
ia->ia_dstaddr.sin6_addr.s6_addr16[1]
= htons(ifp->if_index);
- }
- else
- if (ia->ia_dstaddr.sin6_addr.s6_addr16[1] !=
+ } else if (ia->ia_dstaddr.sin6_addr.s6_addr16[1] !=
htons(ifp->if_index)) {
- ia->ia_dstaddr = oldaddr;
- return(EINVAL); /* ifid is contradict */
- }
+ ia->ia_dstaddr = oldaddr;
+ return(EINVAL); /* ifid is contradict */
+ }
}
prefixIsNew = 1; /* We lie; but effect's the same */
}
- if (ifra->ifra_addr.sin6_family == AF_INET6 &&
- (hostIsNew || prefixIsNew))
+ if (hostIsNew || prefixIsNew)
error = in6_ifinit(ifp, ia, &ifra->ifra_addr, 0);
- if (ifra->ifra_addr.sin6_family == AF_INET6
- && hostIsNew && (ifp->if_flags & IFF_MULTICAST)) {
+ if (hostIsNew && (ifp->if_flags & IFF_MULTICAST)) {
int error_local = 0;
/*
@@ -780,14 +721,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*/
@@ -806,7 +739,7 @@ in6_control(so, cmd, data, ifp, p)
ia->ia6_lifetime.ia6t_preferred = 0;
/*
- * Perform DAD, if needed.
+ * Perform DAD, if needed.
* XXX It may be of use, if we can administratively
* disable DAD.
*/
@@ -848,7 +781,7 @@ in6_control(so, cmd, data, ifp, p)
break;
default:
- if (ifp == 0 || ifp->if_ioctl == 0)
+ if (ifp == NULL || ifp->if_ioctl == 0)
return(EOPNOTSUPP);
return((*ifp->if_ioctl)(ifp, cmd, data));
}
@@ -883,9 +816,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);
@@ -1002,7 +932,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);
@@ -1226,7 +1156,7 @@ in6_ifinit(ifp, ia, sin6, scrub)
in6_ifscrub(ifp, ia);
ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr;
}
- /* xxx
+ /* xxx
* in_socktrim
*/
/*
@@ -1350,7 +1280,7 @@ in6_purgemkludge(ifp)
}
/*
- * Add an address to the list of IP6 multicast addresses for a
+ * Add an address to the list of IP6 multicast addresses for a
* given interface.
*/
struct in6_multi *
@@ -1454,7 +1384,7 @@ in6_delmulti(in6m)
IFAFREE(&in6m->in6m_ia->ia_ifa); /* release reference */
/*
- * Notify the network driver to update its multicast
+ * Notify the network driver to update its multicast
* reception filter.
*/
bzero(&ifr.ifr_addr, sizeof(struct sockaddr_in6));
@@ -1472,8 +1402,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;
@@ -1485,8 +1416,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);
@@ -1618,7 +1553,7 @@ struct in6_addr *addr;
if (addr->s6_addr8[0] == 0xff) {
scope = addr->s6_addr8[1] & 0x0f;
- /*
+ /*
* due to other scope such as reserved,
* return scope doesn't work.
*/
@@ -1648,6 +1583,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...
@@ -1726,89 +1684,250 @@ 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)
+ for (ifp = TAILQ_FIRST(&ifnet); ifp; ifp = TAILQ_NEXT(ifp, if_list))
{
- if (ifa->ifa_addr->sa_family != AF_INET6)
- 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;
+ /*
+ * 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 (dst_scope == in6_addrscope(IFA_IN6(ifa))) {
+ for (ifa = ifp->if_addrlist.tqh_first; ifa;
+ ifa = ifa->ifa_list.tqe_next)
+ {
+ 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 (besta)
- return besta;
+ if (((struct in6_ifaddr *)ifa)->ia6_flags &
+ IN6_IFF_NOTREADY)
+ continue;
- 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;
+ /* 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);
}
- return ia;
}
- /* use the last-resort values, that are, deprecated addresses */
- if (dep[0])
- return dep[0];
- if (dep[1])
- return dep[1];
+ /* 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]++;
- return NULL;
+ 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]++;
+ }
+
+ return(ifa_best);
}
/*
@@ -1829,7 +1948,7 @@ in6_ifawithifp(ifp, dst)
dep[0] = dep[1] = NULL;
/*
- * We first look for addresses in the same scope.
+ * 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.
@@ -1841,13 +1960,12 @@ in6_ifawithifp(ifp, dst)
if (ifa->ifa_addr->sa_family != AF_INET6)
continue;
if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST)
- continue; /* XXX: is there any case to allow 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 (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DEPRECATED) {
if (ip6_use_deprecated)
dep[0] = (struct in6_ifaddr *)ifa;
continue;
@@ -1865,7 +1983,7 @@ in6_ifawithifp(ifp, dst)
blen = tlen;
besta = (struct in6_ifaddr *)ifa;
}
- } else
+ } else
besta = (struct in6_ifaddr *)ifa;
}
}
@@ -1879,13 +1997,12 @@ in6_ifawithifp(ifp, dst)
if (ifa->ifa_addr->sa_family != AF_INET6)
continue;
if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST)
- continue; /* XXX: is there any case to allow 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 (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DEPRECATED) {
if (ip6_use_deprecated)
dep[1] = (struct in6_ifaddr *)ifa;
continue;
@@ -2002,4 +2119,3 @@ in6_setmaxmtu()
if (maxmtu) /* update only when maxmtu is positive */
in6_maxmtu = maxmtu;
}
-
diff --git a/sys/netinet6/in6.h b/sys/netinet6/in6.h
index 5ffa0900e71..6a40d8c50a1 100644
--- a/sys/netinet6/in6.h
+++ b/sys/netinet6/in6.h
@@ -1,4 +1,5 @@
-/* $OpenBSD: in6.h,v 1.11 2000/02/19 17:31:40 deraadt Exp $ */
+/* $OpenBSD: in6.h,v 1.12 2000/02/28 11:55:22 itojun Exp $ */
+
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
* All rights reserved.
@@ -532,22 +533,10 @@ struct in6_pktinfo {
#define IPV6CTL_KAME_VERSION 20
#define IPV6CTL_USE_DEPRECATED 21 /* use deprecated addr (RFC2462 5.5.4) */
#define IPV6CTL_RR_PRUNE 22 /* walk timer for router renumbering */
-#ifdef MAPPED_ADDR_ENABLED
-#define IPV6CTL_MAPPED_ADDR 23
-#endif /* MAPPED_ADDR_ENABLED */
+/*#define IPV6CTL_MAPPED_ADDR 23 not for openbsd */
/* New entries should be added here from current IPV6CTL_MAXID value. */
#define IPV6CTL_MAXID 24
-#ifdef MAPPED_ADDR_ENABLED
-#define IPV6CTL_NAMES_MAPPED_ADDR "mapped_addr"
-#define IPV6CTL_TYPE_MAPPED_ADDR CTLTYPE_INT
-#define IPV6CTL_VARS_MAPPED_ADDR &ip6_mapped_addr_on
-#else /* MAPPED_ADDR_ENABLED */
-#define IPV6CTL_NAMES_MAPPED_ADDR 0
-#define IPV6CTL_TYPE_MAPPED_ADDR 0
-#define IPV6CTL_VARS_MAPPED_ADDR 0
-#endif /* MAPPED_ADDR_ENABLED */
-
#define IPV6CTL_NAMES { \
{ 0, 0 }, \
{ "forwarding", CTLTYPE_INT }, \
@@ -572,41 +561,14 @@ struct in6_pktinfo {
{ "kame_version", CTLTYPE_STRING }, \
{ "use_deprecated", CTLTYPE_INT }, \
{ "rr_prune", CTLTYPE_INT }, \
- { IPV6CTL_NAMES_MAPPED_ADDR, IPV6CTL_TYPE_MAPPED_ADDR }, \
+ { 0, 0 }, \
}
-#define IPV6CTL_VARS { \
- 0, \
- &ip6_forwarding, \
- &ip6_sendredirects, \
- &ip6_defhlim, \
- 0, \
- &ip6_forward_srcrt, \
- 0, \
- 0, \
- 0, \
- &ip6_maxfragpackets, \
- &ip6_sourcecheck, \
- &ip6_sourcecheck_interval, \
- &ip6_accept_rtadv, \
- &ip6_keepfaith, \
- &ip6_log_interval, \
- &ip6_hdrnestlimit, \
- &ip6_dad_count, \
- &ip6_auto_flowlabel, \
- &ip6_defmcasthlim, \
- &ip6_gif_hlim, \
- 0, \
- &ip6_use_deprecated, \
- &ip6_rr_prune, \
- IPV6CTL_VARS_MAPPED_ADDR, \
-}
#endif /* !_XOPEN_SOURCE */
#ifdef _KERNEL
struct cmsghdr;
-int in6_canforward __P((struct in6_addr *, struct in6_addr *));
int in6_cksum __P((struct mbuf *, u_int8_t, u_int32_t, u_int32_t));
int in6_localaddr __P((struct in6_addr *));
int in6_addrscope __P((struct in6_addr *));
diff --git a/sys/netinet6/in6_cksum.c b/sys/netinet6/in6_cksum.c
index 630283fb896..4ff14efda5c 100644
--- a/sys/netinet6/in6_cksum.c
+++ b/sys/netinet6/in6_cksum.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: in6_cksum.c,v 1.4 2000/02/07 06:09:09 itojun Exp $ */
+/* $OpenBSD: in6_cksum.c,v 1.5 2000/02/28 11:55:22 itojun Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
diff --git a/sys/netinet6/in6_pcb.c b/sys/netinet6/in6_pcb.c
index 8d955ac107c..4779e5e20b5 100644
--- a/sys/netinet6/in6_pcb.c
+++ b/sys/netinet6/in6_pcb.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: in6_pcb.c,v 1.9 2000/02/07 06:09:10 itojun Exp $ */
+/* $OpenBSD: in6_pcb.c,v 1.10 2000/02/28 11:55:22 itojun Exp $ */
/*
%%% copyright-nrl-95
diff --git a/sys/netinet6/in6_proto.c b/sys/netinet6/in6_proto.c
index c85c335bd40..85128e8563e 100644
--- a/sys/netinet6/in6_proto.c
+++ b/sys/netinet6/in6_proto.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: in6_proto.c,v 1.17 2000/02/07 06:09:10 itojun Exp $ */
+/* $OpenBSD: in6_proto.c,v 1.18 2000/02/28 11:55:22 itojun Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
diff --git a/sys/netinet6/in6_src.c b/sys/netinet6/in6_src.c
index 2d5a443c2b9..000b32d8a3e 100644
--- a/sys/netinet6/in6_src.c
+++ b/sys/netinet6/in6_src.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: in6_src.c,v 1.3 2000/02/07 06:09:10 itojun Exp $ */
+/* $OpenBSD: in6_src.c,v 1.4 2000/02/28 11:55:22 itojun Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -86,9 +86,6 @@
#include <netinet/in_pcb.h>
#include <netinet6/in6_var.h>
#include <netinet/ip6.h>
-#if 0
-#include <netinet6/in6_pcb.h>
-#endif
#include <netinet6/ip6_var.h>
#include <netinet6/nd6.h>
diff --git a/sys/netinet6/in6_var.h b/sys/netinet6/in6_var.h
index 3002c2c5e6a..20e3637da9b 100644
--- a/sys/netinet6/in6_var.h
+++ b/sys/netinet6/in6_var.h
@@ -1,4 +1,5 @@
-/* $OpenBSD: in6_var.h,v 1.8 2000/02/04 18:13:36 itojun Exp $ */
+/* $OpenBSD: in6_var.h,v 1.9 2000/02/28 11:55:22 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.
@@ -356,9 +357,17 @@ struct in6_rrenumreq {
#define SIOCSIFADDR_IN6 _IOW('i', 12, struct in6_ifreq)
#define SIOCGIFADDR_IN6 _IOWR('i', 33, struct in6_ifreq)
+
+#ifdef _KERNEL
+/*
+ * SIOCSxxx ioctls should be unused (see comments in in6.c), but
+ * we do not shift numbers for binary compatibility.
+ */
#define SIOCSIFDSTADDR_IN6 _IOW('i', 14, struct in6_ifreq)
-#define SIOCGIFDSTADDR_IN6 _IOWR('i', 34, struct in6_ifreq)
#define SIOCSIFNETMASK_IN6 _IOW('i', 22, struct in6_ifreq)
+#endif
+
+#define SIOCGIFDSTADDR_IN6 _IOWR('i', 34, struct in6_ifreq)
#define SIOCGIFNETMASK_IN6 _IOWR('i', 37, struct in6_ifreq)
#define SIOCDIFADDR_IN6 _IOW('i', 25, struct in6_ifreq)
@@ -383,6 +392,9 @@ struct in6_rrenumreq {
#define SIOCGIFSTAT_IN6 _IOWR('i', 83, struct in6_ifreq)
#define SIOCGIFSTAT_ICMP6 _IOWR('i', 84, struct in6_ifreq)
+#define SIOCSDEFIFACE_IN6 _IOWR('i', 85, struct in6_ndifreq)
+#define SIOCGDEFIFACE_IN6 _IOWR('i', 86, struct in6_ndifreq)
+
#define SIOCSIFPREFIX_IN6 _IOW('i', 100, struct in6_prefixreq) /* set */
#define SIOCGIFPREFIX_IN6 _IOWR('i', 101, struct in6_prefixreq) /* get */
#define SIOCDIFPREFIX_IN6 _IOW('i', 102, struct in6_prefixreq) /* del */
@@ -407,6 +419,11 @@ struct in6_rrenumreq {
#define IN6_IFF_NOTREADY (IN6_IFF_TENTATIVE|IN6_IFF_DUPLICATED)
#ifdef _KERNEL
+#define IN6_ARE_SCOPE_CMP(a,b) ((a)-(b))
+#define IN6_ARE_SCOPE_EQUAL(a,b) ((a)==(b))
+#endif
+
+#ifdef _KERNEL
extern struct in6_ifaddr *in6_ifaddr;
extern struct in6_ifstat **in6_ifstat;
@@ -549,10 +566,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));
diff --git a/sys/netinet6/ip6_forward.c b/sys/netinet6/ip6_forward.c
index a28ac69c199..097f868d0f7 100644
--- a/sys/netinet6/ip6_forward.c
+++ b/sys/netinet6/ip6_forward.c
@@ -1,4 +1,5 @@
-/* $OpenBSD: ip6_forward.c,v 1.3 2000/02/07 06:09:10 itojun Exp $ */
+/* $OpenBSD: ip6_forward.c,v 1.4 2000/02/28 11:55:22 itojun Exp $ */
+/* $KAME: ip6_forward.c,v 1.29 2000/02/26 18:08:38 itojun Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -46,23 +47,18 @@
#include <netinet/in.h>
#include <netinet/in_var.h>
+#include <netinet/ip_var.h>
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
#include <netinet/icmp6.h>
#include <netinet6/nd6.h>
-#undef IPSEC
-
#ifdef IPSEC_IPV6FWD
#include <netinet6/ipsec.h>
#include <netkey/key.h>
#include <netkey/key_debug.h>
#endif /* IPSEC_IPV6FWD */
-#ifdef IPV6FIREWALL
-#include <netinet6/ip6_fw.h>
-#endif
-
#include <net/net_osdep.h>
struct route_in6 ip6_forward_rt;
@@ -111,19 +107,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));
}
@@ -327,17 +321,73 @@ ip6_forward(m, srcrt)
}
}
rt = ip6_forward_rt.ro_rt;
- if (m->m_pkthdr.len > rt->rt_ifp->if_mtu){
+
+ /*
+ * 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) {
+ u_long mtu;
+#ifdef IPSEC_IPV6FWD
+ struct secpolicy *sp;
+ int ipsecerror;
+ size_t ipsechdrsiz;
+#endif
+
+ mtu = rt->rt_ifp->if_mtu;
+#ifdef IPSEC_IPV6FWD
/*
- * XXX
* When we do IPsec tunnel ingress, we need to play
* with if_mtu value (decrement IPsec header size
- * from mtu value). see ip_input().
+ * from mtu value). The code is much simpler than v4
+ * case, as we have the outgoing interface for
+ * encapsulated packet as "rt->rt_ifp".
*/
- icmp6_error(mcopy, ICMP6_PACKET_TOO_BIG, 0,
- rt->rt_ifp->if_mtu);
+ sp = ipsec6_getpolicybyaddr(mcopy, IPSEC_DIR_OUTBOUND,
+ IP_FORWARDING, &ipsecerror);
+ if (sp) {
+ ipsechdrsiz = ipsec6_hdrsiz(mcopy,
+ IPSEC_DIR_OUTBOUND, NULL);
+ if (ipsechdrsiz < mtu)
+ mtu -= ipsechdrsiz;
+ }
+
+ /*
+ * if mtu becomes less than minimum MTU,
+ * tell minimum MTU (and I'll need to fragment it).
+ */
+ if (mtu < IPV6_MMTU)
+ mtu = IPV6_MMTU;
+#endif
+ icmp6_error(mcopy, ICMP6_PACKET_TOO_BIG, 0, mtu);
}
m_freem(m);
return;
diff --git a/sys/netinet6/ip6_var.h b/sys/netinet6/ip6_var.h
index 214fe3088f8..b9293940211 100644
--- a/sys/netinet6/ip6_var.h
+++ b/sys/netinet6/ip6_var.h
@@ -1,4 +1,5 @@
-/* $OpenBSD: ip6_var.h,v 1.4 2000/02/04 18:11:38 itojun Exp $ */
+/* $OpenBSD: ip6_var.h,v 1.5 2000/02/28 11:55:22 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
diff --git a/sys/netinet6/mld6.c b/sys/netinet6/mld6.c
index fa3d929cc55..8940d654e15 100644
--- a/sys/netinet6/mld6.c
+++ b/sys/netinet6/mld6.c
@@ -1,4 +1,5 @@
-/* $OpenBSD: mld6.c,v 1.5 2000/02/07 06:09:10 itojun Exp $ */
+/* $OpenBSD: mld6.c,v 1.6 2000/02/28 11:55:22 itojun Exp $ */
+/* $KAME: mld6.c,v 1.16 2000/02/22 14:04:27 itojun Exp $ */
/*
* Copyright (C) 1998 WIDE Project.
@@ -117,7 +118,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 */
@@ -138,7 +139,7 @@ mld6_start_listening(in6m)
int s = splnet();
/*
- * (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
@@ -182,7 +183,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;
@@ -194,13 +195,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.
*
@@ -225,25 +238,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;
@@ -285,14 +298,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;
@@ -303,9 +316,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 */
@@ -319,6 +332,8 @@ mld6_input(m, off)
log(LOG_ERR, "mld6_input: illegal type(%d)", mldh->mld6_type);
break;
}
+
+ m_freem(m);
}
void
@@ -371,7 +386,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;
/*
diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c
index d2eeda40b29..c2dc4952c7f 100644
--- a/sys/netinet6/nd6.c
+++ b/sys/netinet6/nd6.c
@@ -1,4 +1,5 @@
-/* $OpenBSD: nd6.c,v 1.6 2000/02/07 06:09:10 itojun Exp $ */
+/* $OpenBSD: nd6.c,v 1.7 2000/02/28 11:55:22 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>
@@ -85,7 +87,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 */
@@ -95,13 +96,10 @@ static int nd6_inuse, nd6_allocated;
struct llinfo_nd6 llinfo_nd6 = {&llinfo_nd6, &llinfo_nd6};
struct nd_ifinfo *nd_ifinfo = NULL;
-struct nd_drhead nd_defrouter = { 0 };
+struct nd_drhead nd_defrouter;
struct nd_prhead nd_prefix = { 0 };
int nd6_recalc_reachtm_interval = ND6_RECALC_REACHTM_INTERVAL;
-#if 0
-extern int ip6_forwarding;
-#endif
static struct sockaddr_in6 all1_sa;
static void nd6_slowtimo __P((void *));
@@ -122,6 +120,9 @@ nd6_init()
for (i = 0; i < sizeof(all1_sa.sin6_addr); i++)
all1_sa.sin6_addr.s6_addr[i] = 0xff;
+ /* initialization of the default router list */
+ TAILQ_INIT(&nd_defrouter);
+
nd6_init_done = 1;
/* start timer */
@@ -433,9 +434,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
@@ -469,15 +469,15 @@ nd6_timer(ignored_arg)
}
/* expire */
- dr = nd_defrouter.lh_first;
+ dr = TAILQ_FIRST(&nd_defrouter);
while (dr) {
if (dr->expire && dr->expire < time_second) {
struct nd_defrouter *t;
- t = dr->dr_next;
+ t = TAILQ_NEXT(dr, dr_entry);
defrtrlist_del(dr);
dr = t;
} else
- dr = dr->dr_next;
+ dr = TAILQ_NEXT(dr, dr_entry);
}
pr = nd_prefix.lh_first;
while (pr) {
@@ -542,17 +542,17 @@ nd6_purge(ifp)
struct nd_prefix *pr, *npr;
/* Nuke default router list entries toward ifp */
- if ((dr = nd_defrouter.lh_first) != NULL) {
+ if ((dr = TAILQ_FIRST(&nd_defrouter)) != NULL) {
/*
* The first entry of the list may be stored in
* the routing table, so we'll delete it later.
*/
- for (dr = dr->dr_next; dr; dr = ndr) {
- ndr = dr->dr_next;
+ for (dr = TAILQ_NEXT(dr, dr_entry); dr; dr = ndr) {
+ ndr = TAILQ_NEXT(dr, dr_entry);
if (dr->ifp == ifp)
defrtrlist_del(dr);
}
- dr = nd_defrouter.lh_first;
+ dr = TAILQ_FIRST(&nd_defrouter);
if (dr->ifp == ifp)
defrtrlist_del(dr);
}
@@ -567,9 +567,14 @@ nd6_purge(ifp)
}
}
+ /* cancel default outgoing interface setting */
+ if (nd6_defifindex == ifp->if_index)
+ nd6_setdefaultiface(0);
+
/* refresh default router list */
bzero(&drany, sizeof(drany));
defrouter_delreq(&drany, 0);
+ defrouter_select();
/*
* Nuke neighbor cache entries for the ifp.
@@ -763,41 +768,74 @@ nd6_free(rt)
{
struct llinfo_nd6 *ln = (struct llinfo_nd6 *)rt->rt_llinfo;
struct sockaddr_dl *sdl;
+ struct in6_addr in6 = ((struct sockaddr_in6 *)rt_key(rt))->sin6_addr;
+ struct nd_defrouter *dr;
- if (ln->ln_router) {
- /* remove from default router list */
- struct nd_defrouter *dr;
- struct in6_addr *in6;
- int s;
- in6 = &((struct sockaddr_in6 *)rt_key(rt))->sin6_addr;
+ /*
+ * 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;
s = splnet();
-
- dr = defrouter_lookup(&((struct sockaddr_in6 *)rt_key(rt))->
- sin6_addr,
+ dr = defrouter_lookup(&((struct sockaddr_in6 *)rt_key(rt))->sin6_addr,
rt->rt_ifp);
- if (dr)
- defrtrlist_del(dr);
- else if (!ip6_forwarding && ip6_accept_rtadv) {
+ if (ln->ln_router || dr) {
+ /*
+ * rt6_flush must be called whether or not the neighbor
+ * is in the Default Router List.
+ * See a corresponding comment in nd6_na_input().
+ */
+ rt6_flush(&in6, rt->rt_ifp);
+ }
+
+ if (dr) {
/*
- * rt6_flush must be called in any case.
- * see the comment in nd6_na_input().
+ * Unreachablity of a router might affect the default
+ * router selection and on-link detection of advertised
+ * prefixes.
*/
- rt6_flush(in6, rt->rt_ifp);
+
+ /*
+ * Temporarily fake the state to choose a new default
+ * router and to perform on-link determination of
+ * prefixes coreectly.
+ * Below the state will be set correctly,
+ * or the entry itself will be deleted.
+ */
+ ln->ln_state = ND6_LLINFO_INCOMPLETE;
+
+ if (dr == TAILQ_FIRST(&nd_defrouter)) {
+ /*
+ * It is used as the current default router,
+ * so we have to move it to the end of the
+ * list and choose a new one.
+ * XXX: it is not very efficient if this is
+ * the only router.
+ */
+ TAILQ_REMOVE(&nd_defrouter, dr, dr_entry);
+ TAILQ_INSERT_TAIL(&nd_defrouter, dr, dr_entry);
+
+ defrouter_select();
+ }
+ pfxlist_onlink_check();
}
splx(s);
}
-
+
if (rt->rt_refcnt > 0 && (sdl = SDL(rt->rt_gateway)) &&
- sdl->sdl_family == AF_LINK) {
+ sdl->sdl_family == AF_LINK) {
sdl->sdl_alen = 0;
ln->ln_state = ND6_LLINFO_WAITDELETE;
ln->ln_asked = 0;
rt->rt_flags &= ~RTF_REJECT;
return;
}
- rtrequest(RTM_DELETE, rt_key(rt), (struct sockaddr *)0, rt_mask(rt),
- 0, (struct rtentry **)0);
+
+ rtrequest(RTM_DELETE, rt_key(rt), (struct sockaddr *)0,
+ rt_mask(rt), 0, (struct rtentry **)0);
}
/*
@@ -964,7 +1002,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
@@ -991,17 +1029,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;
}
@@ -1075,12 +1133,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;
@@ -1134,7 +1230,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:
/*
@@ -1162,6 +1258,7 @@ nd6_ioctl(cmd, data, ifp)
struct in6_prlist *prl = (struct in6_prlist *)data;
struct in6_ndireq *ndi = (struct in6_ndireq *)data;
struct in6_nbrinfo *nbi = (struct in6_nbrinfo *)data;
+ struct in6_ndifreq *ndif = (struct in6_ndifreq *)data;
struct nd_defrouter *dr, any;
struct nd_prefix *pr;
struct rtentry *rt;
@@ -1171,10 +1268,8 @@ nd6_ioctl(cmd, data, ifp)
switch (cmd) {
case SIOCGDRLST_IN6:
bzero(drl, sizeof(*drl));
-
s = splnet();
-
- dr = nd_defrouter.lh_first;
+ dr = TAILQ_FIRST(&nd_defrouter);
while (dr && i < DRLSTSIZ) {
drl->defrouter[i].rtaddr = dr->rtaddr;
if (IN6_IS_ADDR_LINKLOCAL(&drl->defrouter[i].rtaddr)) {
@@ -1192,15 +1287,18 @@ nd6_ioctl(cmd, data, ifp)
drl->defrouter[i].expire = dr->expire;
drl->defrouter[i].if_index = dr->ifp->if_index;
i++;
- dr = dr->dr_next;
+ dr = TAILQ_NEXT(dr, dr_entry);
}
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 = splnet();
-
pr = nd_prefix.lh_first;
while (pr && i < PRLSTSIZ) {
struct nd_pfxrouter *pfr;
@@ -1236,11 +1334,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;
@@ -1256,15 +1354,17 @@ 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:
ndi->ndi = nd_ifinfo[ifp->if_index];
break;
- case SIOCSNDFLUSH_IN6:
+ case SIOCSNDFLUSH_IN6: /* XXX: the ioctl name is confusing... */
/* flush default router list */
/*
* xxx sumikawa: should not delete route if default
@@ -1272,6 +1372,7 @@ nd6_ioctl(cmd, data, ifp)
*/
bzero(&any, sizeof(any));
defrouter_delreq(&any, 0);
+ defrouter_select();
/* xxx sumikawa: flush prefix list */
break;
case SIOCSPFXFLUSH_IN6:
@@ -1296,17 +1397,16 @@ nd6_ioctl(cmd, data, ifp)
struct nd_defrouter *dr, *next;
s = splnet();
-
- if ((dr = nd_defrouter.lh_first) != NULL) {
+ if ((dr = TAILQ_FIRST(&nd_defrouter)) != NULL) {
/*
* The first entry of the list may be stored in
* the routing table, so we'll delete it later.
*/
- for (dr = dr->dr_next; dr; dr = next) {
- next = dr->dr_next;
+ for (dr = TAILQ_NEXT(dr, dr_entry); dr; dr = next) {
+ next = TAILQ_NEXT(dr, dr_entry);
defrtrlist_del(dr);
}
- defrtrlist_del(nd_defrouter.lh_first);
+ defrtrlist_del(TAILQ_FIRST(&nd_defrouter));
}
splx(s);
break;
@@ -1344,6 +1444,12 @@ nd6_ioctl(cmd, data, ifp)
break;
}
+ case SIOCGDEFIFACE_IN6: /* XXX: should be implemented as a sysctl? */
+ ndif->ifindex = nd6_defifindex;
+ break;
+ case SIOCSDEFIFACE_IN6: /* XXX: should be implemented as a sysctl? */
+ return(nd6_setdefaultiface(ndif->ifindex));
+ break;
}
return(error);
}
diff --git a/sys/netinet6/nd6.h b/sys/netinet6/nd6.h
index 0a89dd38108..1051468ccff 100644
--- a/sys/netinet6/nd6.h
+++ b/sys/netinet6/nd6.h
@@ -1,4 +1,5 @@
-/* $OpenBSD: nd6.h,v 1.3 2000/02/04 18:13:36 itojun Exp $ */
+/* $OpenBSD: nd6.h,v 1.4 2000/02/28 11:55:22 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 {
@@ -53,6 +59,8 @@ struct llinfo_nd6 {
#define ND6_LLINFO_DELAY 3
#define ND6_LLINFO_PROBE 4
+#define ND6_IS_LLINFO_PROBREACH(n) ((n)->ln_state > ND6_LLINFO_INCOMPLETE)
+
struct nd_ifinfo {
u_int32_t linkmtu; /* LinkMTU */
u_int32_t maxmtu; /* Upper bound of LinkMTU */
@@ -92,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;
@@ -106,6 +115,12 @@ struct in6_ndireq {
struct nd_ifinfo ndi;
};
+struct in6_ndifreq {
+ char ifname[IFNAMSIZ];
+ u_long ifindex;
+};
+
+
/* protocol constants */
#define MAX_RTR_SOLICITATION_DELAY 1 /*1sec*/
#define RTR_SOLICITATION_INTERVAL 4 /*4sec*/
@@ -124,9 +139,9 @@ struct in6_ndireq {
(((MIN_RANDOM_FACTOR * (x >> 10)) + (arc4random() & \
((MAX_RANDOM_FACTOR - MIN_RANDOM_FACTOR) * (x >> 10)))) /1000)
+TAILQ_HEAD(nd_drhead, nd_defrouter);
struct nd_defrouter {
- LIST_ENTRY(nd_defrouter) dr_entry;
-#define dr_next dr_entry.le_next
+ TAILQ_ENTRY(nd_defrouter) dr_entry;
struct in6_addr rtaddr;
u_char flags;
u_short rtlifetime;
@@ -204,7 +219,6 @@ struct nd_pfxrouter {
struct nd_defrouter *router;
};
-LIST_HEAD(nd_drhead, nd_defrouter);
LIST_HEAD(nd_prhead, nd_prefix);
/* nd6.c */
@@ -213,12 +227,15 @@ 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;
extern struct nd_prhead nd_prefix;
+/* nd6_rtr.c */
+extern struct ifnet *nd6_defifp; /* XXXYYY */
+extern int nd6_defifindex;
+
union nd_opts {
struct nd_opt_hdr *nd_opt_array[9];
struct {
@@ -279,7 +296,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));
@@ -293,15 +310,18 @@ void nd6_ra_input __P((struct mbuf *, int, int));
void prelist_del __P((struct nd_prefix *));
void defrouter_addreq __P((struct nd_defrouter *));
void defrouter_delreq __P((struct nd_defrouter *, int));
+void defrouter_select __P((void));
void defrtrlist_del __P((struct nd_defrouter *));
void prelist_remove __P((struct nd_prefix *));
int prelist_update __P((struct nd_prefix *, struct nd_defrouter *,
struct mbuf *));
+void pfxlist_onlink_check __P((void));
struct nd_defrouter *defrouter_lookup __P((struct in6_addr *,
struct ifnet *));
int in6_ifdel __P((struct ifnet *, struct in6_addr *));
int in6_init_prefix_ltimes __P((struct nd_prefix *ndpr));
void rt6_flush __P((struct in6_addr *, struct ifnet *));
+int nd6_setdefaultiface __P((int));
#endif /* _KERNEL */
diff --git a/sys/netinet6/nd6_nbr.c b/sys/netinet6/nd6_nbr.c
index cb1fb521712..85fd4169c91 100644
--- a/sys/netinet6/nd6_nbr.c
+++ b/sys/netinet6/nd6_nbr.c
@@ -1,4 +1,5 @@
-/* $OpenBSD: nd6_nbr.c,v 1.8 2000/02/07 06:04:43 itojun Exp $ */
+/* $OpenBSD: nd6_nbr.c,v 1.9 2000/02/28 11:55:23 itojun Exp $ */
+/* $KAME: nd6_nbr.c,v 1.29 2000/02/26 08:20:58 itojun Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -61,10 +62,6 @@
#define SDL(s) ((struct sockaddr_dl *)s)
-#if 0
-extern struct timeval time;
-#endif
-
struct dadq;
static struct dadq *nd6_dad_find __P((struct ifaddr *));
static void nd6_dad_timer __P((struct ifaddr *));
@@ -80,8 +77,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)
@@ -90,11 +85,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;
@@ -102,11 +96,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)) {
@@ -124,6 +119,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;
@@ -181,7 +188,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;
@@ -191,16 +198,20 @@ nd6_ns_input(m, off, icmp6len)
tsin6.sin6_addr = taddr6;
rt = rtalloc1((struct sockaddr *)&tsin6, 0);
- if (rt && rt->rt_ifp != ifp) {
+ 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) {
/*
@@ -208,13 +219,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,
@@ -227,7 +238,7 @@ nd6_ns_input(m, off, icmp6len)
log(LOG_INFO,
"nd6_ns_input: duplicate IP6 address %s\n",
ip6_sprintf(&saddr6));
- return;
+ goto freeit;
}
/*
@@ -253,7 +264,7 @@ nd6_ns_input(m, off, icmp6len)
if (IN6_IS_ADDR_UNSPECIFIED(&saddr6))
nd6_dad_ns_input(ifa);
- return;
+ goto freeit;
}
/*
@@ -271,8 +282,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);
@@ -281,14 +292,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 +498,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 +510,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 +531,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 +557,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 +592,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 +600,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 +617,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 +625,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 +684,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) */
@@ -723,6 +755,9 @@ nd6_na_input(m, off, icmp6len)
#endif
ln->ln_hold = 0;
}
+
+ freeit:
+ m_freem(m);
}
/*
@@ -730,16 +765,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;
@@ -824,7 +860,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);
@@ -1102,8 +1154,9 @@ nd6_dad_timer(ifa)
ia->ia6_flags &= ~IN6_IFF_TENTATIVE;
#ifdef DEBUG
- log(LOG_INFO, "%s: DAD complete for %s - no duplicates "
- "found\n", if_name(ifa->ifa_ifp),
+ log(LOG_INFO,
+ "%s: DAD complete for %s - no duplicates found\n",
+ if_name(ifa->ifa_ifp),
ip6_sprintf(&ia->ia_addr.sin6_addr));
#endif
diff --git a/sys/netinet6/nd6_rtr.c b/sys/netinet6/nd6_rtr.c
index 868813782c9..2a1db314c7c 100644
--- a/sys/netinet6/nd6_rtr.c
+++ b/sys/netinet6/nd6_rtr.c
@@ -1,4 +1,5 @@
-/* $OpenBSD: nd6_rtr.c,v 1.4 2000/02/07 06:09:10 itojun Exp $ */
+/* $OpenBSD: nd6_rtr.c,v 1.5 2000/02/28 11:55:23 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.
@@ -68,9 +69,10 @@ static struct nd_pfxrouter *pfxrtr_lookup __P((struct nd_prefix *,
struct nd_defrouter *));
static void pfxrtr_add __P((struct nd_prefix *, struct nd_defrouter *));
static void pfxrtr_del __P((struct nd_pfxrouter *));
-static void pfxlist_onlink_check __P((void));
+static struct nd_pfxrouter *find_pfxlist_reachable_router __P((struct nd_prefix *));
static void nd6_detach_prefix __P((struct nd_prefix *));
static void nd6_attach_prefix __P((struct nd_prefix *));
+static void defrouter_addifreq __P((struct ifnet *));
static void in6_init_address_ltimes __P((struct nd_prefix *ndpr,
struct in6_addrlifetime *lt6,
@@ -78,16 +80,10 @@ static void in6_init_address_ltimes __P((struct nd_prefix *ndpr,
static int rt6_deleteroute __P((struct radix_node *, void *));
-#if 0
-extern struct timeval time;
-#endif
extern int nd6_recalc_reachtm_interval;
-#if 0
-static u_char bmask [] = {
- 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe,
-};
-#endif
+struct ifnet *nd6_defifp;
+int nd6_defifindex;
/*
* Receive Router Solicitation Message - just for routers.
@@ -103,8 +99,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;
@@ -121,13 +116,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;
}
/*
@@ -135,13 +130,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) {
@@ -157,6 +163,9 @@ nd6_rs_input(m, off, icmp6len)
}
nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_ROUTER_SOLICIT, 0);
+
+ freeit:
+ m_freem(m);
}
/*
@@ -174,12 +183,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
@@ -187,26 +195,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;
}
{
@@ -365,7 +384,17 @@ nd6_ra_input(m, off, icmp6len)
}
nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_ROUTER_ADVERT, 0);
+
+ /*
+ * Installing a link-layer address might change the state of the
+ * router's neighbor cache, which might also affect our on-link
+ * detection of adveritsed prefixes.
+ */
+ pfxlist_onlink_check();
}
+
+freeit:
+ m_freem(m);
}
/*
@@ -377,15 +406,6 @@ defrouter_addreq(new)
{
struct sockaddr_in6 def, mask, gate;
int s;
-#if 0
- register struct radix_node *rn;
- register struct radix_node_head *rnh;
- struct sockaddr *ndst;
- struct ifnet *ifp = new->ifp;
- struct ifaddr *ifa;
- struct rtentry *rt;
- extern struct pool rtentry_pool;
-#endif
Bzero(&def, sizeof(def));
Bzero(&mask, sizeof(mask));
@@ -396,51 +416,52 @@ defrouter_addreq(new)
def.sin6_family = mask.sin6_family = gate.sin6_family = AF_INET6;
gate.sin6_addr = new->rtaddr;
-#if 1
s = splnet();
-
(void)rtrequest(RTM_ADD, (struct sockaddr *)&def,
(struct sockaddr *)&gate, (struct sockaddr *)&mask,
RTF_GATEWAY, NULL);
splx(s);
return;
-#else
- ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp);
- if (!ifa)
- return;
- if ((rnh = rt_tables[AF_INET6]) == 0)
- return;
+}
- s = splnet();
- R_Malloc(rt, struct rtentry *, sizeof(*rt));
- if (!rt)
- goto bad;
- Bzero(rt, sizeof(*rt));
- rt->rt_flags = RTF_UP | RTF_GATEWAY;
- if (rt_setgate(rt, (struct sockaddr *)&def, (struct sockaddr *)&gate)){
- Free(rt);
- goto bad;
+/* Add a route to a given interface as default */
+static void
+defrouter_addifreq(ifp)
+ struct ifnet *ifp;
+{
+ struct sockaddr_in6 def, mask;
+ struct ifaddr *ifa;
+ int error, flags;
+
+ bzero(&def, sizeof(def));
+ bzero(&mask, sizeof(mask));
+
+ def.sin6_len = mask.sin6_len = sizeof(struct sockaddr_in6);
+ def.sin6_family = mask.sin6_family = AF_INET6;
+
+ /*
+ * Search for an ifaddr beloging to the specified interface.
+ * XXX: An IPv6 address are required to be assigned on the interface.
+ */
+ if ((ifa = ifaof_ifpforaddr((struct sockaddr *)&def, ifp)) == NULL) {
+ log(LOG_ERR, /* better error? */
+ "defrouter_addifreq: failed to find an ifaddr "
+ "to install a route to interface %s\n",
+ if_name(ifp));
+ return;
}
- ndst = rt_key(rt);
- Bcopy(&def, ndst, sizeof(def));
- rn = rnh->rnh_addaddr((caddr_t)ndst, (caddr_t)&mask,
- rnh, rt->rt_nodes);
- if (rn == 0) {
- Free(rt_key(rt));
- Free(rt);
- goto bad;
+
+ flags = ifa->ifa_flags;
+ if ((ifp->if_flags & IFF_POINTOPOINT) != 0)
+ flags &= ~RTF_CLONING;
+ if ((error = rtrequest(RTM_ADD, (struct sockaddr *)&def,
+ ifa->ifa_addr, (struct sockaddr *)&mask,
+ flags, NULL)) != 0) {
+ log(LOG_ERR,
+ "defrouter_addifreq: failed to install a route to "
+ "interface %s (errno = %d)\n",
+ if_name(ifp), error);
}
- ifa->ifa_refcnt++;
- rt->rt_ifa = ifa;
- rt->rt_ifp = ifp;
- rt->rt_rmx.rmx_mtu = ifa->ifa_ifp->if_mtu;
- /* xxx
- * many codes should be stolen from route.c
- */
-bad:
- splx(s);
- return;
-#endif
}
struct nd_defrouter *
@@ -450,9 +471,11 @@ defrouter_lookup(addr, ifp)
{
struct nd_defrouter *dr;
- for(dr = nd_defrouter.lh_first; dr; dr = dr->dr_next)
+ for (dr = TAILQ_FIRST(&nd_defrouter); dr;
+ dr = TAILQ_NEXT(dr, dr_entry)) {
if (dr->ifp == ifp && IN6_ARE_ADDR_EQUAL(addr, &dr->rtaddr))
return(dr);
+ }
return(NULL); /* search failed */
}
@@ -478,16 +501,8 @@ defrouter_delreq(dr, dofree)
(struct sockaddr *)&mask,
RTF_GATEWAY, (struct rtentry **)0);
- if (dofree)
+ if (dofree) /* XXX: necessary? */
free(dr, M_IP6NDP);
-
- if (nd_defrouter.lh_first)
- defrouter_addreq(nd_defrouter.lh_first);
-
- /*
- * xxx update the Destination Cache entries for all
- * destinations using that neighbor as a router (7.2.5)
- */
}
void
@@ -506,10 +521,10 @@ defrtrlist_del(dr)
rt6_flush(&dr->rtaddr, dr->ifp);
}
- if (dr == nd_defrouter.lh_first)
+ if (dr == TAILQ_FIRST(&nd_defrouter))
deldr = dr; /* The router is primary. */
- LIST_REMOVE(dr, dr_entry);
+ TAILQ_REMOVE(&nd_defrouter, dr, dr_entry);
/*
* Also delete all the pointers to the router in each prefix lists.
@@ -522,14 +537,94 @@ defrtrlist_del(dr)
pfxlist_onlink_check();
/*
- * If the router is the primary one, delete the default route
- * entry in the routing table.
+ * If the router is the primary one, choose a new one.
+ * Note that defrouter_select() will remove the current gateway
+ * from the routing table.
*/
if (deldr)
- defrouter_delreq(deldr, 0);
+ defrouter_select();
+
free(dr, M_IP6NDP);
}
+/*
+ * Default Router Selection according to Section 6.3.6 of RFC 2461:
+ * 1) Routers that are reachable or probably reachable should be
+ * preferred.
+ * 2) When no routers on the list are known to be reachable or
+ * probably reachable, routers SHOULD be selected in a round-robin
+ * fashion.
+ * 3) If the Default Router List is empty, assume that all
+ * destinations are on-link.
+ */
+void
+defrouter_select()
+{
+ int s = splnet();
+ struct nd_defrouter *dr, anydr;
+ struct rtentry *rt = NULL;
+ struct llinfo_nd6 *ln = NULL;
+
+ /*
+ * Search for a (probably) reachable router from the list.
+ */
+ for (dr = TAILQ_FIRST(&nd_defrouter); dr;
+ dr = TAILQ_NEXT(dr, dr_entry)) {
+ if ((rt = nd6_lookup(&dr->rtaddr, 0, dr->ifp)) &&
+ (ln = (struct llinfo_nd6 *)rt->rt_llinfo) &&
+ ND6_IS_LLINFO_PROBREACH(ln)) {
+ /* Got it, and move it to the head */
+ TAILQ_REMOVE(&nd_defrouter, dr, dr_entry);
+ TAILQ_INSERT_HEAD(&nd_defrouter, dr, dr_entry);
+ break;
+ }
+ }
+
+ if ((dr = TAILQ_FIRST(&nd_defrouter))) {
+ /*
+ * De-install the previous default gateway and install
+ * a new one.
+ * Note that if there is no reachable router in the list,
+ * the head entry will be used anyway.
+ * XXX: do we have to check the current routing table entry?
+ */
+ bzero(&anydr, sizeof(anydr));
+ defrouter_delreq(&anydr, 0);
+ defrouter_addreq(dr);
+ }
+ else {
+ /*
+ * The Default Router List is empty, so install the default
+ * route to an inteface.
+ * XXX: The specification does not say this mechanism should
+ * be restricted to hosts, but this would be not useful
+ * (even harmful) for routers.
+ */
+ if (!ip6_forwarding) {
+ /*
+ * De-install the current default route
+ * in advance.
+ */
+ bzero(&anydr, sizeof(anydr));
+ defrouter_delreq(&anydr, 0);
+ if (nd6_defifp) {
+ /*
+ * Install a route to the default interface
+ * as default route.
+ */
+ defrouter_addifreq(nd6_defifp);
+ }
+ else /* noisy log? */
+ log(LOG_INFO, "defrouter_select: "
+ "there's no default router and no default"
+ " interface\n");
+ }
+ }
+
+ splx(s);
+ return;
+}
+
static struct nd_defrouter *
defrtrlist_update(new)
struct nd_defrouter *new;
@@ -565,13 +660,15 @@ defrtrlist_update(new)
}
bzero(n, sizeof(*n));
*n = *new;
- if (nd_defrouter.lh_first == NULL) {
- LIST_INSERT_HEAD(&nd_defrouter, n, dr_entry);
- defrouter_addreq(n);
- } else {
- LIST_INSERT_AFTER(nd_defrouter.lh_first, n, dr_entry);
- defrouter_addreq(n);
- }
+
+ /*
+ * Insert the new router at the end of the Default Router List.
+ * If there is no other router, install it anyway. Otherwise,
+ * just continue to use the current default router.
+ */
+ TAILQ_INSERT_TAIL(&nd_defrouter, n, dr_entry);
+ if (TAILQ_FIRST(&nd_defrouter) == n)
+ defrouter_select();
splx(s);
return(n);
@@ -897,49 +994,81 @@ prelist_update(new, dr, m)
}
/*
+ * A supplement function used in the on-link detection below;
+ * detect if a given prefix has a (probably) reachable advertising router.
+ * XXX: lengthy function name...
+ */
+static struct nd_pfxrouter *
+find_pfxlist_reachable_router(pr)
+ struct nd_prefix *pr;
+{
+ struct nd_pfxrouter *pfxrtr;
+ struct rtentry *rt;
+ struct llinfo_nd6 *ln;
+
+ for (pfxrtr = LIST_FIRST(&pr->ndpr_advrtrs); pfxrtr;
+ pfxrtr = LIST_NEXT(pfxrtr, pfr_entry)) {
+ if ((rt = nd6_lookup(&pfxrtr->router->rtaddr, 0,
+ pfxrtr->router->ifp)) &&
+ (ln = (struct llinfo_nd6 *)rt->rt_llinfo) &&
+ ND6_IS_LLINFO_PROBREACH(ln))
+ break; /* found */
+ }
+
+ return(pfxrtr);
+
+}
+
+/*
* Check if each prefix in the prefix list has at least one available router
- * that advertised the prefix.
- * If the check fails, the prefix may be off-link because, for example,
+ * that advertised the prefix (A router is "available" if its neighbor cache
+ * entry has reachable or probably reachable).
+ * If the check fails, the prefix may be off-link, because, for example,
* we have moved from the network but the lifetime of the prefix has not
* been expired yet. So we should not use the prefix if there is another
* prefix that has an available router.
- * But if there is no prefix that has an availble router, we still regards
+ * But if there is no prefix that has an available router, we still regards
* all the prefixes as on-link. This is because we can't tell if all the
* routers are simply dead or if we really moved from the network and there
* is no router around us.
*/
-static void
+void
pfxlist_onlink_check()
{
struct nd_prefix *pr;
- for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next)
- if (pr->ndpr_advrtrs.lh_first) /* pr has an available router */
+ /*
+ * Check if there is a prefix that has a reachable advertising
+ * router.
+ */
+ for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) {
+ if (find_pfxlist_reachable_router(pr))
break;
+ }
if (pr) {
/*
- * There is at least one prefix that has a router. First,
- * detach prefixes which has no advertising router and then
- * attach other prefixes. The order is important since an
- * attached prefix and a detached prefix may have a same
- * interface route.
+ * There is at least one prefix that has a reachable router.
+ * First, detach prefixes which has no reachable advertising
+ * router and then attach other prefixes.
+ * The order is important since an attached prefix and a
+ * detached prefix may have a same interface route.
*/
for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) {
- if (pr->ndpr_advrtrs.lh_first == NULL &&
+ if (find_pfxlist_reachable_router(pr) == NULL &&
pr->ndpr_statef_onlink) {
pr->ndpr_statef_onlink = 0;
nd6_detach_prefix(pr);
}
}
for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) {
- if (pr->ndpr_advrtrs.lh_first &&
- pr->ndpr_statef_onlink == 0)
+ if (find_pfxlist_reachable_router(pr) &&
+ pr->ndpr_statef_onlink == 0)
nd6_attach_prefix(pr);
}
}
else {
- /* there is no prefix that has a router */
+ /* there is no prefix that has a reachable router */
for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next)
if (pr->ndpr_statef_onlink == 0)
nd6_attach_prefix(pr);
@@ -1062,7 +1191,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
@@ -1105,11 +1234,13 @@ in6_ifadd(ifp, in6, addr, prefixlen)
oia->ia_next = ia;
} else
in6_ifaddr = ia;
+ ia->ia_ifa.ifa_refcnt++;
/* link to if_addrlist */
if (ifp->if_addrlist.tqh_first != NULL) {
TAILQ_INSERT_TAIL(&ifp->if_addrlist, (struct ifaddr *)ia,
ifa_list);
+ ia->ia_ifa.ifa_refcnt++;
}
#if 0
else {
@@ -1241,6 +1372,7 @@ in6_ifdel(ifp, in6)
}
TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list);
+ IFAFREE(&ia->ia_ifa);
/* lladdr is never deleted */
oia = ia;
@@ -1378,3 +1510,34 @@ rt6_deleteroute(rn, arg)
rt->rt_gateway, rt_mask(rt), rt->rt_flags, 0));
#undef SIN6
}
+
+int
+nd6_setdefaultiface(ifindex)
+ int ifindex;
+{
+ int error = 0;
+
+ if (ifindex < 0 || if_index < ifindex)
+ return(EINVAL);
+
+ if (nd6_defifindex != ifindex) {
+ nd6_defifindex = ifindex;
+ if (nd6_defifindex > 0)
+ nd6_defifp = ifindex2ifnet[nd6_defifindex];
+ else
+ nd6_defifp = NULL;
+
+ /*
+ * If the Default Router List is empty, install a route
+ * to the specified interface as default or remove the default
+ * route when the default interface becomes canceled.
+ * The check for the queue is actually redundant, but
+ * we do this here to avoid re-install the default route
+ * if the list is NOT empty.
+ */
+ if (TAILQ_FIRST(&nd_defrouter) == NULL)
+ defrouter_select();
+ }
+
+ return(error);
+}
diff --git a/sys/netinet6/raw_ipv6.c b/sys/netinet6/raw_ipv6.c
index a2cae4bcbdb..2ab3fbd543d 100644
--- a/sys/netinet6/raw_ipv6.c
+++ b/sys/netinet6/raw_ipv6.c
@@ -1,4 +1,5 @@
-/* $OpenBSD: raw_ipv6.c,v 1.13 2000/02/07 06:09:10 itojun Exp $ */
+/* $OpenBSD: raw_ipv6.c,v 1.14 2000/02/28 11:55:23 itojun Exp $ */
+
/*
%%% copyright-nrl-95
This software is Copyright 1995-1998 by Randall Atkinson, Ronald Lee,
@@ -43,7 +44,7 @@ didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
* SUCH DAMAGE.
*
* @(#)raw_ip.c 8.7 (Berkeley) 5/15/95
- * $Id: raw_ipv6.c,v 1.13 2000/02/07 06:09:10 itojun Exp $
+ * $Id: raw_ipv6.c,v 1.14 2000/02/28 11:55:23 itojun Exp $
*/
#include <sys/param.h>
diff --git a/sys/netinet6/tcpipv6.h b/sys/netinet6/tcpipv6.h
index a9559602049..ba8453a32cf 100644
--- a/sys/netinet6/tcpipv6.h
+++ b/sys/netinet6/tcpipv6.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: tcpipv6.h,v 1.5 2000/02/07 06:09:10 itojun Exp $ */
+/* $OpenBSD: tcpipv6.h,v 1.6 2000/02/28 11:55:23 itojun Exp $ */
/*
%%% copyright-nrl-95