diff options
author | 2000-02-28 11:55:20 +0000 | |
---|---|---|
committer | 2000-02-28 11:55:20 +0000 | |
commit | f4f4d166a5b90d7c5fa648ad3f10dd92cbac4be0 (patch) | |
tree | d121e05d91dcb4b1567306e66de7147463f78fae | |
parent | Add back the OpenBSDism that a #commented realm means Kerberos is disabled. (diff) | |
download | wireguard-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.h | 28 | ||||
-rw-r--r-- | sys/netinet6/icmp6.c | 400 | ||||
-rw-r--r-- | sys/netinet6/in6.c | 584 | ||||
-rw-r--r-- | sys/netinet6/in6.h | 46 | ||||
-rw-r--r-- | sys/netinet6/in6_cksum.c | 2 | ||||
-rw-r--r-- | sys/netinet6/in6_pcb.c | 2 | ||||
-rw-r--r-- | sys/netinet6/in6_proto.c | 2 | ||||
-rw-r--r-- | sys/netinet6/in6_src.c | 5 | ||||
-rw-r--r-- | sys/netinet6/in6_var.h | 24 | ||||
-rw-r--r-- | sys/netinet6/ip6_forward.c | 86 | ||||
-rw-r--r-- | sys/netinet6/ip6_var.h | 37 | ||||
-rw-r--r-- | sys/netinet6/mld6.c | 78 | ||||
-rw-r--r-- | sys/netinet6/nd6.c | 216 | ||||
-rw-r--r-- | sys/netinet6/nd6.h | 32 | ||||
-rw-r--r-- | sys/netinet6/nd6_nbr.c | 151 | ||||
-rw-r--r-- | sys/netinet6/nd6_rtr.c | 371 | ||||
-rw-r--r-- | sys/netinet6/raw_ipv6.c | 5 | ||||
-rw-r--r-- | sys/netinet6/tcpipv6.h | 2 |
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 |