diff --git a/sys/netinet/icmp6.h b/sys/netinet/icmp6.h index 994b7b4ef96c..e64048a653c1 100644 --- a/sys/netinet/icmp6.h +++ b/sys/netinet/icmp6.h @@ -1,4 +1,5 @@ -/* $NetBSD: icmp6.h,v 1.4 2000/02/06 11:11:30 itojun Exp $ */ +/* $NetBSD: icmp6.h,v 1.5 2000/02/26 08:39:18 itojun Exp $ */ +/* $KAME: icmp6.h,v 1.6 2000/02/24 16:34:46 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -469,12 +470,12 @@ struct icmp6_filter { #ifdef _KERNEL #define ICMP6_FILTER_SETPASSALL(filterp) \ - { \ +do { \ int i; u_char *p; \ p = (u_char *)filterp; \ for (i = 0; i < sizeof(struct icmp6_filter); i++) \ p[i] = 0xff; \ - } +} while (0) #define ICMP6_FILTER_SETBLOCKALL(filterp) \ bzero(filterp, sizeof(struct icmp6_filter)) #else /* _KERNEL */ @@ -525,7 +526,7 @@ struct icmp6stat { #define ICMPV6CTL_ND6_UMAXTRIES 9 #define ICMPV6CTL_ND6_MMAXTRIES 10 #define ICMPV6CTL_ND6_USELOOPBACK 11 -#define ICMPV6CTL_ND6_PROXYALL 12 +/*#define ICMPV6CTL_ND6_PROXYALL 12 obsoleted, do not reuse here */ #define ICMPV6CTL_NODEINFO 13 #define ICMPV6CTL_MAXID 14 @@ -542,7 +543,7 @@ struct icmp6stat { { "nd6_umaxtries", CTLTYPE_INT }, \ { "nd6_mmaxtries", CTLTYPE_INT }, \ { "nd6_useloopback", CTLTYPE_INT }, \ - { "nd6_proxyall", CTLTYPE_INT }, \ + { 0, 0 }, \ { "nodeinfo", CTLTYPE_INT }, \ } @@ -560,7 +561,7 @@ struct icmp6stat { &nd6_umaxtries, \ &nd6_mmaxtries, \ &nd6_useloopback, \ - &nd6_proxyall, \ + 0, \ &icmp6_nodeinfo, \ } diff --git a/sys/netinet6/icmp6.c b/sys/netinet6/icmp6.c index c344d1205f76..19e2ab784b90 100644 --- a/sys/netinet6/icmp6.c +++ b/sys/netinet6/icmp6.c @@ -1,4 +1,5 @@ -/* $NetBSD: icmp6.c,v 1.22 2000/02/17 10:59:38 darrenr Exp $ */ +/* $NetBSD: icmp6.c,v 1.23 2000/02/26 08:39:19 itojun Exp $ */ +/* $KAME: icmp6.c,v 1.70 2000/02/26 07:01:11 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -117,6 +118,8 @@ static struct rttimer_queue *icmp6_mtudisc_timeout_q = NULL; extern int pmtu_expire; static int icmp6_rip6_input __P((struct mbuf **, int)); +static void icmp6_mtudisc_update __P((struct in6_addr *, struct icmp6_hdr *, + struct mbuf *)); static int icmp6_ratelimit __P((const struct in6_addr *, const int, const int)); static const char *icmp6_redirect_diag __P((struct in6_addr *, struct in6_addr *, struct in6_addr *)); @@ -155,10 +158,10 @@ icmp6_error(m, type, code, param) icmp6stat.icp6s_error++; -#ifdef M_DECRYPTED /*not openbsd*/ - if (m->m_flags & M_DECRYPTED) + if (m->m_flags & M_DECRYPTED) { + icmp6stat.icp6s_canterror++; goto freeit; -#endif + } #ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, 0, sizeof(struct ip6_hdr), ); @@ -408,12 +411,21 @@ icmp6_input(mp, offp, proto) break; case ICMP6_DST_UNREACH_ADMIN: icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_adminprohib); - case ICMP6_DST_UNREACH_ADDR: - code = PRC_UNREACH_HOST; + code = PRC_UNREACH_PROTOCOL; /* is this a good code? */ break; + case ICMP6_DST_UNREACH_ADDR: + code = PRC_HOSTDEAD; + break; +#ifdef COMPAT_RFC1885 case ICMP6_DST_UNREACH_NOTNEIGHBOR: code = PRC_UNREACH_SRCFAIL; break; +#else + case ICMP6_DST_UNREACH_BEYONDSCOPE: + /* I mean "source address was incorrect." */ + code = PRC_PARAMPROB; + break; +#endif case ICMP6_DST_UNREACH_NOPORT: code = PRC_UNREACH_PORT; break; @@ -427,55 +439,14 @@ icmp6_input(mp, offp, proto) icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_pkttoobig); if (code != 0) goto badcode; - { - u_int mtu = ntohl(icmp6->icmp6_mtu); - struct rtentry *rt = NULL; - struct sockaddr_in6 sin6; - if (icmp6len < sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr)) { - icmp6stat.icp6s_tooshort++; - goto freeit; - } -#ifndef PULLDOWN_TEST - IP6_EXTHDR_CHECK(m, off, - sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr), - IPPROTO_DONE); - icmp6 = (struct icmp6_hdr *)(mtod(m, caddr_t) + off); -#else - IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off, - sizeof(*icmp6) + sizeof(struct ip6_hdr)); - if (icmp6 == NULL) { - icmp6stat.icp6s_tooshort++; - return IPPROTO_DONE; - } -#endif code = PRC_MSGSIZE; - bzero(&sin6, sizeof(sin6)); - sin6.sin6_family = PF_INET6; - sin6.sin6_len = sizeof(struct sockaddr_in6); - sin6.sin6_addr = ((struct ip6_hdr *)(icmp6 + 1))->ip6_dst; - rt = rtalloc1((struct sockaddr *)&sin6, 1); /*clone*/ - if (!rt || (rt->rt_flags & RTF_HOST) == 0) { - if (rt) - RTFREE(rt); - rt = icmp6_mtudisc_clone((struct sockaddr *)&sin6); - } - - if (rt && (rt->rt_flags & RTF_HOST) - && !(rt->rt_rmx.rmx_locks & RTV_MTU)) { - if (mtu < IPV6_MMTU) { - /* xxx */ - rt->rt_rmx.rmx_locks |= RTV_MTU; - } else if (mtu < rt->rt_ifp->if_mtu && - rt->rt_rmx.rmx_mtu > mtu) { - rt->rt_rmx.rmx_mtu = mtu; - } - } - if (rt) - RTFREE(rt); + /* + * Updating the path MTU will be done after examining + * intermediate extension headers. + */ goto deliver; - } break; case ICMP6_TIME_EXCEEDED: @@ -518,12 +489,28 @@ icmp6_input(mp, offp, proto) if ((n->m_flags & M_EXT) != 0 || n->m_len < off + sizeof(struct icmp6_hdr)) { struct mbuf *n0 = n; + const int maxlen = sizeof(*nip6) + sizeof(*nicmp6); /* * Prepare an internal mbuf. m_pullup() doesn't * always copy the length we specified. */ + if (maxlen >= MCLBYTES) { +#ifdef DIAGNOSTIC + printf("MCLBYTES too small\n"); +#endif + /* Give up remote */ + m_freem(n0); + break; + } MGETHDR(n, M_DONTWAIT, n0->m_type); + if (n && maxlen >= MHLEN) { + MCLGET(n, M_DONTWAIT); + if ((n->m_flags & M_EXT) == 0) { + m_free(n); + n = NULL; + } + } if (n == NULL) { /* Give up remote */ m_freem(n0); @@ -576,8 +563,13 @@ icmp6_input(mp, offp, proto) icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mldquery); else icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mldreport); - IP6_EXTHDR_CHECK(m, off, icmp6len, IPPROTO_DONE); - mld6_input(m, off); + if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) { + /* give up local */ + mld6_input(m, off); + m = NULL; + goto freeit; + } + mld6_input(n, off); /* m stays. */ break; @@ -591,7 +583,7 @@ icmp6_input(mp, offp, proto) case MLD6_MTRACE: /* XXX: these two are experimental. not officially defind. */ /* XXX: per-interface statistics? */ - break; /* just pass it to the userland daemon */ + break; /* just pass it to applications */ case ICMP6_WRUREQUEST: /* ICMP6_FQDN_QUERY */ { @@ -621,12 +613,32 @@ icmp6_input(mp, offp, proto) noff = sizeof(struct ip6_hdr); } else { u_char *p; + int maxlen, maxhlen; + maxlen = sizeof(*nip6) + sizeof(*nicmp6) + 4; + if (maxlen >= MCLBYTES) { +#ifdef DIAGNOSTIC + printf("MCLBYTES too small\n"); +#endif + /* Give up remote */ + break; + } MGETHDR(n, M_DONTWAIT, m->m_type); + if (n && maxlen > MHLEN) { + MCLGET(n, M_DONTWAIT); + if ((n->m_flags & M_EXT) == 0) { + m_free(n); + n = NULL; + } + } if (n == NULL) { /* Give up remote */ break; } + n->m_len = 0; + maxhlen = M_TRAILINGSPACE(n) - maxlen; + if (maxhlen > hostnamelen) + maxhlen = hostnamelen; /* * Copy IPv6 and ICMPv6 only. */ @@ -636,11 +648,11 @@ icmp6_input(mp, offp, proto) bcopy(icmp6, nicmp6, sizeof(struct icmp6_hdr)); p = (u_char *)(nicmp6 + 1); bzero(p, 4); - bcopy(hostname, p + 4, hostnamelen); + bcopy(hostname, p + 4, maxhlen); noff = sizeof(struct ip6_hdr); M_COPY_PKTHDR(n, m); /* just for recvif */ n->m_pkthdr.len = n->m_len = sizeof(struct ip6_hdr) + - sizeof(struct icmp6_hdr) + 4 + hostnamelen; + sizeof(struct icmp6_hdr) + 4 + maxhlen; nicmp6->icmp6_type = ICMP6_WRUREPLY; nicmp6->icmp6_code = 0; } @@ -664,8 +676,13 @@ icmp6_input(mp, offp, proto) goto badcode; if (icmp6len < sizeof(struct nd_router_solicit)) goto badlen; - IP6_EXTHDR_CHECK(m, off, icmp6len, IPPROTO_DONE); - nd6_rs_input(m, off, icmp6len); + if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) { + /* give up local */ + nd6_rs_input(m, off, icmp6len); + m = NULL; + goto freeit; + } + nd6_rs_input(n, off, icmp6len); /* m stays. */ break; @@ -675,8 +692,13 @@ icmp6_input(mp, offp, proto) goto badcode; if (icmp6len < sizeof(struct nd_router_advert)) goto badlen; - IP6_EXTHDR_CHECK(m, off, icmp6len, IPPROTO_DONE); - nd6_ra_input(m, off, icmp6len); + if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) { + /* give up local */ + nd6_ra_input(m, off, icmp6len); + m = NULL; + goto freeit; + } + nd6_ra_input(n, off, icmp6len); /* m stays. */ break; @@ -686,8 +708,13 @@ icmp6_input(mp, offp, proto) goto badcode; if (icmp6len < sizeof(struct nd_neighbor_solicit)) goto badlen; - IP6_EXTHDR_CHECK(m, off, icmp6len, IPPROTO_DONE); - nd6_ns_input(m, off, icmp6len); + if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) { + /* give up local */ + nd6_ns_input(m, off, icmp6len); + m = NULL; + goto freeit; + } + nd6_ns_input(n, off, icmp6len); /* m stays. */ break; @@ -697,8 +724,13 @@ icmp6_input(mp, offp, proto) goto badcode; if (icmp6len < sizeof(struct nd_neighbor_advert)) goto badlen; - IP6_EXTHDR_CHECK(m, off, icmp6len, IPPROTO_DONE); - nd6_na_input(m, off, icmp6len); + if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) { + /* give up local */ + nd6_na_input(m, off, icmp6len); + m = NULL; + goto freeit; + } + nd6_na_input(n, off, icmp6len); /* m stays. */ break; @@ -708,7 +740,13 @@ icmp6_input(mp, offp, proto) goto badcode; if (icmp6len < sizeof(struct nd_redirect)) goto badlen; - icmp6_redirect_input(m, off); + if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) { + /* give up local */ + icmp6_redirect_input(m, off); + m = NULL; + goto freeit; + } + icmp6_redirect_input(n, off); /* m stays. */ break; @@ -764,19 +802,20 @@ icmp6_input(mp, offp, proto) int eoff = off + sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr); struct ip6ctlparam ip6cp; + struct in6_addr *finaldst = NULL; + int icmp6type = icmp6->icmp6_type; + struct ip6_frag *fh; + struct ip6_rthdr *rth; + struct ip6_rthdr0 *rth0; + int rthlen; while (1) { /* XXX: should avoid inf. loop explicitly? */ struct ip6_ext *eh; switch(nxt) { - case IPPROTO_ESP: - case IPPROTO_NONE: - goto passit; case IPPROTO_HOPOPTS: case IPPROTO_DSTOPTS: - case IPPROTO_ROUTING: case IPPROTO_AH: - case IPPROTO_FRAGMENT: #ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, 0, eoff + sizeof(struct ip6_ext), @@ -791,15 +830,105 @@ icmp6_input(mp, offp, proto) return IPPROTO_DONE; } #endif + if (nxt == IPPROTO_AH) eoff += (eh->ip6e_len + 2) << 2; - else if (nxt == IPPROTO_FRAGMENT) - eoff += sizeof(struct ip6_frag); else eoff += (eh->ip6e_len + 1) << 3; nxt = eh->ip6e_nxt; break; + case IPPROTO_ROUTING: + /* + * When the erroneous packet contains a + * routing header, we should examine the + * header to determine the final destination. + * Otherwise, we can't properly update + * information that depends on the final + * destination (e.g. path MTU). + */ +#ifndef PULLDOWN_TEST + IP6_EXTHDR_CHECK(m, 0, eoff + sizeof(*rth), + IPPROTO_DONE); + rth = (struct ip6_rthdr *)(mtod(m, caddr_t) + + eoff); +#else + IP6_EXTHDR_GET(rth, struct ip6_rthdr *, m, + eoff, sizeof(*rth)); + if (rth == NULL) { + icmp6stat.icp6s_tooshort++; + return IPPROTO_DONE; + } +#endif + rthlen = (rth->ip6r_len + 1) << 3; + /* + * XXX: currently there is no + * officially defined type other + * than type-0. + * Note that if the segment left field + * is 0, all intermediate hops must + * have been passed. + */ + if (rth->ip6r_segleft && + rth->ip6r_type == IPV6_RTHDR_TYPE_0) { + int hops; + +#ifndef PULLDOWN_TEST + IP6_EXTHDR_CHECK(m, 0, eoff + rthlen, + IPPROTO_DONE); + rth0 = (struct ip6_rthdr0 *)(mtod(m, caddr_t) + eoff); +#else + IP6_EXTHDR_GET(rth0, + struct ip6_rthdr0 *, m, + eoff, rthlen); + if (rth0 == NULL) { + icmp6stat.icp6s_tooshort++; + return IPPROTO_DONE; + } +#endif + /* just ignore a bogus header */ + if ((rth0->ip6r0_len % 2) == 0 && + (hops = rth0->ip6r0_len/2)) + finaldst = (struct in6_addr *)(rth0 + 1) + (hops - 1); + } + eoff += rthlen; + nxt = rth->ip6r_nxt; + break; + case IPPROTO_FRAGMENT: +#ifndef PULLDOWN_TEST + IP6_EXTHDR_CHECK(m, 0, eoff + + sizeof(struct ip6_frag), + IPPROTO_DONE); + fh = (struct ip6_frag *)(mtod(m, caddr_t) + + eoff); +#else + IP6_EXTHDR_GET(fh, struct ip6_frag *, m, + eoff, sizeof(*fh)); + if (fh == NULL) { + icmp6stat.icp6s_tooshort++; + return IPPROTO_DONE; + } +#endif + /* + * Data after a fragment header is meaningless + * unless it is the first fragment, but + * we'll go to the notify label for path MTU + * discovery. + */ + if (fh->ip6f_offlg & IP6F_OFF_MASK) + goto notify; + + eoff += sizeof(struct ip6_frag); + nxt = fh->ip6f_nxt; + break; default: + /* + * This case includes ESP and the No Next + * Header. In such cases going to the notify + * label does not have any meaning + * (i.e. ctlfunc will be NULL), but we go + * anyway since we might have to update + * path MTU information. + */ goto notify; } } @@ -814,6 +943,12 @@ icmp6_input(mp, offp, proto) return IPPROTO_DONE; } #endif + if (icmp6type == ICMP6_PACKET_TOO_BIG) { + if (finaldst == NULL) + finaldst = &((struct ip6_hdr *)(icmp6 + 1))->ip6_dst; + icmp6_mtudisc_update(finaldst, icmp6, m); + } + ctlfunc = (void (*) __P((int, struct sockaddr *, void *))) (inet6sw[ip6_protox[nxt]].pr_ctlinput); if (ctlfunc) { @@ -834,7 +969,6 @@ icmp6_input(mp, offp, proto) break; } - passit: icmp6_rip6_input(&m, *offp); return IPPROTO_DONE; @@ -843,6 +977,42 @@ icmp6_input(mp, offp, proto) return IPPROTO_DONE; } +static void +icmp6_mtudisc_update(dst, icmp6, m) + struct in6_addr *dst; + struct icmp6_hdr *icmp6;/* we can assume the validity of the pointer */ + struct mbuf *m; /* currently unused but added for scoped addrs */ +{ + u_int mtu = ntohl(icmp6->icmp6_mtu); + struct rtentry *rt = NULL; + struct sockaddr_in6 sin6; + + bzero(&sin6, sizeof(sin6)); + sin6.sin6_family = PF_INET6; + sin6.sin6_len = sizeof(struct sockaddr_in6); + sin6.sin6_addr = *dst; + /* sin6.sin6_scope_id = XXX: should be set if DST is a scoped addr */ + rt = rtalloc1((struct sockaddr *)&sin6, 1); /*clone*/ + if (!rt || (rt->rt_flags & RTF_HOST) == 0) { + if (rt) + RTFREE(rt); + rt = icmp6_mtudisc_clone((struct sockaddr *)&sin6); + } + + if (rt && (rt->rt_flags & RTF_HOST) + && !(rt->rt_rmx.rmx_locks & RTV_MTU)) { + if (mtu < IPV6_MMTU) { + /* xxx */ + rt->rt_rmx.rmx_locks |= RTV_MTU; + } else if (mtu < rt->rt_ifp->if_mtu && + rt->rt_rmx.rmx_mtu > mtu) { + rt->rt_rmx.rmx_mtu = mtu; + } + } + if (rt) + RTFREE(rt); +} + /* * Process a Node Information Query */ @@ -1156,8 +1326,16 @@ icmp6_rip6_input(mp, off) struct icmp6_hdr *icmp6; struct mbuf *opts = NULL; +#ifndef PULLDOWN_TEST /* this is assumed to be safe. */ icmp6 = (struct icmp6_hdr *)((caddr_t)ip6 + off); +#else + IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off, sizeof(*icmp6)); + if (icmp6 == NULL) { + /* m is already reclaimed */ + return IPPROTO_DONE; + } +#endif bzero(&rip6src, sizeof(rip6src)); rip6src.sin6_len = sizeof(struct sockaddr_in6); @@ -1338,10 +1516,13 @@ icmp6_reflect(m, off) /* * If the incoming packet was addressed directly to us(i.e. unicast), * use dst as the src for the reply. + * The IN6_IFF_NOTREADY case would be VERY rare, but is possible when + * (for example) when we encounter an error while forwarding procedure + * destined to a duplicated address of ours. */ for (ia = in6_ifaddr; ia; ia = ia->ia_next) if (IN6_ARE_ADDR_EQUAL(&t, &ia->ia_addr.sin6_addr) && - (ia->ia6_flags & IN6_IFF_ANYCAST) == 0) { + (ia->ia6_flags & (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY)) == 0) { src = &t; break; } @@ -1355,9 +1536,11 @@ icmp6_reflect(m, off) if (src == 0) /* - * We have not multicast routing yet. So this case matches - * to our multicast, our anycast or not to our unicast. - * Select a source address which has the same scope. + * This case matches to multicasts, our anycast, or unicasts + * that we do not own. Select a source address which has the + * same scope. + * XXX: for (non link-local) multicast addresses, this might + * not be a good choice. */ if ((ia = in6_ifawithscope(m->m_pkthdr.rcvif, &t)) != 0) src = &IA6_SIN6(ia)->sin6_addr; @@ -1429,7 +1612,7 @@ icmp6_redirect_input(m, off) { struct ifnet *ifp = m->m_pkthdr.rcvif; struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); - struct nd_redirect *nd_rd = (struct nd_redirect *)((caddr_t)ip6 + off); + struct nd_redirect *nd_rd; int icmp6len = ntohs(ip6->ip6_plen); char *lladdr = NULL; int lladdrlen = 0; @@ -1439,8 +1622,8 @@ icmp6_redirect_input(m, off) int is_router; int is_onlink; struct in6_addr src6 = ip6->ip6_src; - struct in6_addr redtgt6 = nd_rd->nd_rd_target; - struct in6_addr reddst6 = nd_rd->nd_rd_dst; + struct in6_addr redtgt6; + struct in6_addr reddst6; union nd_opts ndopts; if (!m || !ifp) @@ -1448,28 +1631,41 @@ icmp6_redirect_input(m, off) /* XXX if we are router, we don't update route by icmp6 redirect */ if (ip6_forwarding) - return; + goto freeit; if (!icmp6_rediraccept) - return; + goto freeit; if (IN6_IS_ADDR_LINKLOCAL(&redtgt6)) redtgt6.s6_addr16[1] = htons(ifp->if_index); if (IN6_IS_ADDR_LINKLOCAL(&reddst6)) reddst6.s6_addr16[1] = htons(ifp->if_index); +#ifndef PULLDOWN_TEST + IP6_EXTHDR_CHECK(m, off, icmp6len,); + nd_rd = (struct nd_redirect *)((caddr_t)ip6 + off); +#else + IP6_EXTHDR_GET(nd_rd, struct nd_redirect *, m, off, icmp6len); + if (nd_rd == NULL) { + icmp6stat.icp6s_tooshort++; + return; + } +#endif + redtgt6 = nd_rd->nd_rd_target; + reddst6 = nd_rd->nd_rd_dst; + /* validation */ if (!IN6_IS_ADDR_LINKLOCAL(&src6)) { log(LOG_ERR, "ICMP6 redirect sent from %s rejected; " "must be from linklocal\n", ip6_sprintf(&src6)); - return; + goto freeit; } if (ip6->ip6_hlim != 255) { log(LOG_ERR, "ICMP6 redirect sent from %s rejected; " "hlim=%d (must be 255)\n", ip6_sprintf(&src6), ip6->ip6_hlim); - return; + goto freeit; } { /* ip6->ip6_src must be equal to gw for icmp6->icmp6_reddst */ @@ -1480,8 +1676,7 @@ icmp6_redirect_input(m, off) sin6.sin6_family = AF_INET6; sin6.sin6_len = sizeof(struct sockaddr_in6); bcopy(&reddst6, &sin6.sin6_addr, sizeof(reddst6)); - rt = rtalloc1((struct sockaddr *)&sin6, 0 - ); + rt = rtalloc1((struct sockaddr *)&sin6, 0); if (rt) { gw6 = &(((struct sockaddr_in6 *)rt->rt_gateway)->sin6_addr); if (bcmp(&src6, gw6, sizeof(struct in6_addr)) != 0) { @@ -1492,14 +1687,14 @@ icmp6_redirect_input(m, off) ip6_sprintf(gw6), icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); RTFREE(rt); - return; + goto freeit; } } else { log(LOG_ERR, "ICMP6 redirect rejected; " "no route found for redirect dst: %s\n", icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); - return; + goto freeit; } RTFREE(rt); rt = NULL; @@ -1509,7 +1704,7 @@ icmp6_redirect_input(m, off) "ICMP6 redirect rejected; " "redirect dst must be unicast: %s\n", icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); - return; + goto freeit; } is_router = is_onlink = 0; @@ -1522,7 +1717,7 @@ icmp6_redirect_input(m, off) "ICMP6 redirect rejected; " "neither router case nor onlink case: %s\n", icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); - return; + goto freeit; } /* validation passed */ @@ -1532,7 +1727,7 @@ icmp6_redirect_input(m, off) log(LOG_INFO, "icmp6_redirect_input: " "invalid ND option, rejected: %s\n", icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); - return; + goto freeit; } if (ndopts.nd_opts_tgt_lladdr) { @@ -1610,6 +1805,9 @@ icmp6_redirect_input(m, off) key_sa_routechange((struct sockaddr *)&sdst); #endif } + + freeit: + m_freem(m); } void @@ -1657,21 +1855,28 @@ icmp6_redirect_output(m0, rt) * we almost always ask for an mbuf cluster for simplicity. * (MHLEN < IPV6_MMTU is almost always true) */ +#if IPV6_MMTU >= MCLBYTES +# error assumption failed about IPV6_MMTU and MCLBYTES +#endif MGETHDR(m, M_DONTWAIT, MT_HEADER); + if (m && IPV6_MMTU >= MHLEN) + MCLGET(m, M_DONTWAIT); if (!m) goto fail; - if (MHLEN < IPV6_MMTU) - MCLGET(m, M_DONTWAIT); maxlen = (m->m_flags & M_EXT) ? MCLBYTES : MHLEN; maxlen = min(IPV6_MMTU, maxlen); /* just for safety */ - if (maxlen < sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr)) + if (maxlen < sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr) + + ((sizeof(struct nd_opt_hdr) + ifp->if_addrlen + 7) & ~7)) { goto fail; + } { /* get ip6 linklocal address for ifp(my outgoing interface). */ - struct in6_ifaddr *ia = in6ifa_ifpforlinklocal(ifp); - if (ia == NULL) + struct in6_ifaddr *ia; + if ((ia = in6ifa_ifpforlinklocal(ifp, + IN6_IFF_NOTREADY| + IN6_IFF_ANYCAST)) == NULL) goto fail; ifp_ll6 = &ia->ia_addr.sin6_addr; } @@ -1738,6 +1943,11 @@ icmp6_redirect_output(m0, rt) rt_router = nd6_lookup(router_ll6, 0, ifp); if (!rt_router) goto nolladdropt; + len = sizeof(*nd_opt) + ifp->if_addrlen; + len = (len + 7) & ~7; /*round by 8*/ + /* safety check */ + if (len + (p - (u_char *)ip6) > maxlen) + goto nolladdropt; if (!(rt_router->rt_flags & RTF_GATEWAY) && (rt_router->rt_flags & RTF_LLINFO) && (rt_router->rt_gateway->sa_family == AF_LINK) && @@ -1745,12 +1955,10 @@ icmp6_redirect_output(m0, rt) sdl->sdl_alen) { nd_opt = (struct nd_opt_hdr *)p; nd_opt->nd_opt_type = ND_OPT_TARGET_LINKADDR; - len = 2 + ifp->if_addrlen; - len = (len + 7) & ~7; /*round by 8*/ nd_opt->nd_opt_len = len >> 3; - p += len; lladdr = (char *)(nd_opt + 1); bcopy(LLADDR(sdl), lladdr, ifp->if_addrlen); + p += len; } } nolladdropt:; @@ -1758,10 +1966,10 @@ nolladdropt:; m->m_pkthdr.len = m->m_len = p - (u_char *)ip6; /* just to be safe */ -#ifdef M_DECRYPTED /*not openbsd*/ if (m0->m_flags & M_DECRYPTED) goto noredhdropt; -#endif + if (p - (u_char *)ip6 > maxlen) + goto noredhdropt; { /* redirected header option */ @@ -1832,9 +2040,7 @@ nolladdropt:; m->m_next = m0; m->m_pkthdr.len = m->m_len + m0->m_len; } -#ifdef M_DECRYPTED /*not openbsd*/ noredhdropt:; -#endif if (IN6_IS_ADDR_LINKLOCAL(&sip6->ip6_src)) sip6->ip6_src.s6_addr16[1] = 0; @@ -2087,8 +2293,6 @@ icmp6_sysctl(name, namelen, oldp, oldlenp, newp, newlen) case ICMPV6CTL_ND6_USELOOPBACK: return sysctl_int(oldp, oldlenp, newp, newlen, &nd6_useloopback); - case ICMPV6CTL_ND6_PROXYALL: - return sysctl_int(oldp, oldlenp, newp, newlen, &nd6_proxyall); case ICMPV6CTL_NODEINFO: return sysctl_int(oldp, oldlenp, newp, newlen, &icmp6_nodeinfo); default: diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c index 28b3e59d3020..c39fb9a2dc1d 100644 --- a/sys/netinet6/in6.c +++ b/sys/netinet6/in6.c @@ -1,4 +1,4 @@ -/* $NetBSD: in6.c,v 1.20 2000/02/25 05:13:05 itojun Exp $ */ +/* $NetBSD: in6.c,v 1.21 2000/02/26 08:39:19 itojun Exp $ */ /* $KAME: in6.c,v 1.55 2000/02/25 00:32:23 itojun Exp $ */ /* @@ -138,22 +138,6 @@ struct multi6_kludge { struct in6_multihead mk_head; }; -/* - * Determine whether an IP6 address is in a reserved set of addresses - * that may not be forwarded, or whether datagrams to that destination - * may be forwarded. - */ -int -in6_canforward(src, dst) - struct in6_addr *src, *dst; -{ - if (IN6_IS_ADDR_LINKLOCAL(src) || - IN6_IS_ADDR_LINKLOCAL(dst) || - IN6_IS_ADDR_MULTICAST(dst)) - return(0); - return(1); -} - /* * Check if the loopback entry will be automatically generated. * if 0 returned, will not be automatically generated. @@ -267,90 +251,6 @@ in6_ifremloop(struct ifaddr *ifa) } } -/* - * Subroutine for in6_ifaddproxy() and in6_ifremproxy(). - * This routine does actual work. - * call in6_addmulti() when cmd == 1. - * call in6_delmulti() when cmd == 2. - */ -static int -in6_ifproxy_request(int cmd, struct in6_ifaddr *ia) -{ - int error = 0; - - /* - * If we have an IPv6 dstaddr on adding p2p interface, - * join dstaddr's solicited multicast on necessary interface. - */ - if ((ia->ia_ifp->if_flags & IFF_POINTOPOINT) && - ia->ia_dstaddr.sin6_family == AF_INET6 && - !IN6_IS_ADDR_LINKLOCAL(&ia->ia_dstaddr.sin6_addr)) { - struct in6_ifaddr *ia_lan; - - /* - * TODO: Join only on some specified interfaces by some - * configuration. - * Unsolicited Neighbor Advertisements will be also necessary. - * - * Now, join on interfaces which meets following. - * -IFF_BROADCAST and IFF_MULTICAST - * (NBMA is out of scope) - * -the prefix value is same as p2p dstaddr - */ - for (ia_lan = in6_ifaddr; ia_lan; ia_lan = ia_lan->ia_next) { - struct in6_addr llsol; - - if ((ia_lan->ia_ifp->if_flags & - (IFF_BROADCAST|IFF_MULTICAST)) != - (IFF_BROADCAST|IFF_MULTICAST)) - continue; - if (!IN6_ARE_MASKED_ADDR_EQUAL(IA6_IN6(ia), - IA6_IN6(ia_lan), - IA6_MASKIN6(ia_lan))) - continue; - if (ia_lan->ia_ifp == ia->ia_ifp) - continue; - - /* init llsol */ - bzero(&llsol, sizeof(struct in6_addr)); - llsol.s6_addr16[0] = htons(0xff02); - llsol.s6_addr16[1] = htons(ia_lan->ia_ifp->if_index); - llsol.s6_addr32[1] = 0; - llsol.s6_addr32[2] = htonl(1); - llsol.s6_addr32[3] = - ia->ia_dstaddr.sin6_addr.s6_addr32[3]; - llsol.s6_addr8[12] = 0xff; - - if (cmd == 1) - (void)in6_addmulti(&llsol, - ia_lan->ia_ifp, - &error); - else if (cmd == 2) { - struct in6_multi *in6m; - - IN6_LOOKUP_MULTI(llsol, - ia_lan->ia_ifp, - in6m); - if (in6m) - in6_delmulti(in6m); - } - } - } - return error; -} - -static int -in6_ifaddproxy(struct in6_ifaddr *ia) -{ - return(in6_ifproxy_request(1, ia)); -} - -static void -in6_ifremproxy(struct in6_ifaddr *ia) -{ - in6_ifproxy_request(2, ia); -} - int in6_ifindex2scopeid(idx) int idx; @@ -837,14 +737,6 @@ in6_control(so, cmd, data, ifp, p) if (error == 0) error = error_local; } - /* Join dstaddr's solicited multicast if necessary. */ - if (nd6_proxyall && hostIsNew) { - int error_local; - - error_local = in6_ifaddproxy(ia); - if (error == 0) - error = error_local; - } ia->ia6_flags = ifra->ifra_flags; ia->ia6_flags &= ~IN6_IFF_DUPLICATED; /*safety*/ @@ -939,9 +831,6 @@ in6_purgeaddr(ifa, ifp) if (in6m) in6_delmulti(in6m); } - /* Leave dstaddr's solicited multicast if necessary. */ - if (nd6_proxyall) - in6_ifremproxy(ia); TAILQ_REMOVE(&ifp->if_addrlist, &ia->ia_ifa, ifa_list); IFAFREE(&ia->ia_ifa); @@ -1086,7 +975,7 @@ in6_lifaddr_ioctl(so, cmd, data, ifp, p) * address. hostid points to the first link-local * address attached to the interface. */ - ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp); + ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp, 0); if (!ifa) return EADDRNOTAVAIL; hostid = IFA_IN6(ifa); @@ -1554,8 +1443,9 @@ in6_delmulti(in6m) * Find an IPv6 interface link-local address specific to an interface. */ struct in6_ifaddr * -in6ifa_ifpforlinklocal(ifp) +in6ifa_ifpforlinklocal(ifp, ignoreflags) struct ifnet *ifp; + int ignoreflags; { register struct ifaddr *ifa; @@ -1565,8 +1455,12 @@ in6ifa_ifpforlinklocal(ifp) continue; /* just for safety */ if (ifa->ifa_addr->sa_family != AF_INET6) continue; - if (IN6_IS_ADDR_LINKLOCAL(IFA_IN6(ifa))) + if (IN6_IS_ADDR_LINKLOCAL(IFA_IN6(ifa))) { + if ((((struct in6_ifaddr *)ifa)->ia6_flags & + ignoreflags) != 0) + continue; break; + } } return((struct in6_ifaddr *)ifa); @@ -1726,6 +1620,29 @@ struct in6_addr *addr; return IPV6_ADDR_SCOPE_GLOBAL; } +int +in6_addr2scopeid(ifp, addr) + struct ifnet *ifp; /* must not be NULL */ + struct in6_addr *addr; /* must not be NULL */ +{ + int scope = in6_addrscope(addr); + + switch(scope) { + case IPV6_ADDR_SCOPE_NODELOCAL: + return(-1); /* XXX: is this an appropriate value? */ + + case IPV6_ADDR_SCOPE_LINKLOCAL: + /* XXX: we do not distinguish between a link and an I/F. */ + return(ifp->if_index); + + case IPV6_ADDR_SCOPE_SITELOCAL: + return(0); /* XXX: invalid. */ + + default: + return(0); /* XXX: treat as global. */ + } +} + /* * return length of part which dst and src are equal * hard coding... @@ -1804,86 +1721,258 @@ in6_prefixlen2mask(maskp, len) /* * return the best address out of the same scope */ - struct in6_ifaddr * -in6_ifawithscope(ifp, dst) - register struct ifnet *ifp; +in6_ifawithscope(oifp, dst) + register struct ifnet *oifp; register struct in6_addr *dst; { - int dst_scope = in6_addrscope(dst), blen = -1, tlen; + int dst_scope = in6_addrscope(dst), src_scope, best_scope; + int blen = -1; struct ifaddr *ifa; - struct in6_ifaddr *besta = NULL, *ia; - struct in6_ifaddr *dep[2]; /*last-resort: deprecated*/ - - dep[0] = dep[1] = NULL; + struct ifnet *ifp; + struct in6_ifaddr *ifa_best = NULL; + + if (oifp == NULL) { + printf("in6_ifawithscope: output interface is not specified\n"); + return(NULL); + } /* - * We first look for addresses in the same scope. - * If there is one, return it. - * If two or more, return one which matches the dst longest. - * If none, return one of global addresses assigned other ifs. + * We search for all addresses on all interfaces from the beginning. + * Comparing an interface with the outgoing interface will be done + * only at the final stage of tiebreaking. */ - for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next) +#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3) + for (ifp = ifnet; ifp; ifp = ifp->if_next) +#else + for (ifp = TAILQ_FIRST(&ifnet); ifp; ifp = TAILQ_NEXT(ifp, if_list)) +#endif { - if (ifa->ifa_addr->sa_family != AF_INET6) + /* + * We can never take an address that breaks the scope zone + * of the destination. + */ + if (in6_addr2scopeid(ifp, dst) != in6_addr2scopeid(oifp, dst)) continue; - if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST) - continue; /* XXX: is there any case to allow anycast? */ - if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_NOTREADY) - continue; /* don't use this interface */ - if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DETACHED) - continue; - if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DEPRECATED) { - if (ip6_use_deprecated) - dep[0] = (struct in6_ifaddr *)ifa; - continue; - } - if (dst_scope == in6_addrscope(IFA_IN6(ifa))) { +#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3) + for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) +#else + for (ifa = ifp->if_addrlist.tqh_first; ifa; + ifa = ifa->ifa_list.tqe_next) +#endif + { + int tlen = -1, dscopecmp, bscopecmp, matchcmp; + + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + + src_scope = in6_addrscope(IFA_IN6(ifa)); + +#ifdef ADDRSELECT_DEBUG /* should be removed after stabilization */ + dscopecmp = IN6_ARE_SCOPE_CMP(src_scope, dst_scope); + printf("in6_ifawithscope: dst=%s bestaddr=%s, " + "newaddr=%s, scope=%x, dcmp=%d, bcmp=%d, " + "matchlen=%d, flgs=%x\n", + ip6_sprintf(dst), + ifa_best ? ip6_sprintf(&ifa_best->ia_addr.sin6_addr) : "none", + ip6_sprintf(IFA_IN6(ifa)), src_scope, + dscopecmp, + ifa_best ? IN6_ARE_SCOPE_CMP(src_scope, best_scope) : -1, + in6_matchlen(IFA_IN6(ifa), dst), + ((struct in6_ifaddr *)ifa)->ia6_flags); +#endif + /* - * call in6_matchlen() as few as possible + * Don't use an address before completing DAD + * nor a duplicated address. */ - if (besta) { - if (blen == -1) - blen = in6_matchlen(&besta->ia_addr.sin6_addr, dst); - tlen = in6_matchlen(IFA_IN6(ifa), dst); - if (tlen > blen) { - blen = tlen; - besta = (struct in6_ifaddr *)ifa; - } - } else - besta = (struct in6_ifaddr *)ifa; + if (((struct in6_ifaddr *)ifa)->ia6_flags & + IN6_IFF_NOTREADY) + continue; + + /* XXX: is there any case to allow anycasts? */ + if (((struct in6_ifaddr *)ifa)->ia6_flags & + IN6_IFF_ANYCAST) + continue; + + if (((struct in6_ifaddr *)ifa)->ia6_flags & + IN6_IFF_DETACHED) + continue; + + /* + * If this is the first address we find, + * keep it anyway. + */ + if (ifa_best == NULL) + goto replace; + + /* + * ifa_best is never NULL beyond this line except + * within the block labeled "replace". + */ + + /* + * If ifa_best has a smaller scope than dst and + * the current address has a larger one than + * (or equal to) dst, always replace ifa_best. + * Also, if the current address has a smaller scope + * than dst, ignore it unless ifa_best also has a + * smaller scope. + */ + if (IN6_ARE_SCOPE_CMP(best_scope, dst_scope) < 0 && + IN6_ARE_SCOPE_CMP(src_scope, dst_scope) >= 0) + goto replace; + if (IN6_ARE_SCOPE_CMP(src_scope, dst_scope) < 0 && + IN6_ARE_SCOPE_CMP(best_scope, dst_scope) >= 0) + continue; + + /* + * A deprecated address SHOULD NOT be used in new + * communications if an alternate (non-deprecated) + * address is available and has sufficient scope. + * RFC 2462, Section 5.5.4. + */ + if (((struct in6_ifaddr *)ifa)->ia6_flags & + IN6_IFF_DEPRECATED) { + /* + * Ignore any deprecated addresses if + * specified by configuration. + */ + if (!ip6_use_deprecated) + continue; + + /* + * If we have already found a non-deprecated + * candidate, just ignore deprecated addresses. + */ + if ((ifa_best->ia6_flags & IN6_IFF_DEPRECATED) + == 0) + continue; + } + + /* + * A non-deprecated address is always preferred + * to a deprecated one regardless of scopes and + * address matching. + */ + if ((ifa_best->ia6_flags & IN6_IFF_DEPRECATED) && + (((struct in6_ifaddr *)ifa)->ia6_flags & + IN6_IFF_DEPRECATED) == 0) + goto replace; + + /* + * At this point, we have two cases: + * 1. we are looking at a non-deprecated address, + * and ifa_best is also non-deprecated. + * 2. we are looking at a deprecated address, + * and ifa_best is also deprecated. + * Also, we do not have to consider a case where + * the scope of if_best is larger(smaller) than dst and + * the scope of the current address is smaller(larger) + * than dst. Such a case has already been covered. + * Tiebreaking is done according to the following + * items: + * - the scope comparison between the address and + * dst (dscopecmp) + * - the scope comparison between the address and + * ifa_best (bscopecmp) + * - if the address match dst longer than ifa_best + * (matchcmp) + * - if the address is on the outgoing I/F (outI/F) + * + * Roughly speaking, the selection policy is + * - the most important item is scope. The same scope + * is best. Then search for a larger scope. + * Smaller scopes are the last resort. + * - A deprecated address is chosen only when we have + * no address that has an enough scope, but is + * prefered to any addresses of smaller scopes. + * - Longest address match against dst is considered + * only for addresses that has the same scope of dst. + * - If there is no other reasons to choose one, + * addresses on the outgoing I/F are preferred. + * + * The precise decision table is as follows: + * dscopecmp bscopecmp matchcmp outI/F | replace? + * !equal equal N/A Yes | Yes (1) + * !equal equal N/A No | No (2) + * larger larger N/A N/A | No (3) + * larger smaller N/A N/A | Yes (4) + * smaller larger N/A N/A | Yes (5) + * smaller smaller N/A N/A | No (6) + * equal smaller N/A N/A | Yes (7) + * equal larger (already done) + * equal equal larger N/A | Yes (8) + * equal equal smaller N/A | No (9) + * equal equal equal Yes | Yes (a) + * eaual eqaul equal No | No (b) + */ + dscopecmp = IN6_ARE_SCOPE_CMP(src_scope, dst_scope); + bscopecmp = IN6_ARE_SCOPE_CMP(src_scope, best_scope); + + if (dscopecmp && bscopecmp == 0) { + if (oifp == ifp) /* (1) */ + goto replace; + continue; /* (2) */ + } + if (dscopecmp > 0) { + if (bscopecmp > 0) /* (3) */ + continue; + goto replace; /* (4) */ + } + if (dscopecmp < 0) { + if (bscopecmp > 0) /* (5) */ + goto replace; + continue; /* (6) */ + } + + /* now dscopecmp must be 0 */ + if (bscopecmp < 0) + goto replace; /* (7) */ + + /* + * At last both dscopecmp and bscopecmp must be 0. + * We need address matching against dst for + * tiebreaking. + */ + tlen = in6_matchlen(IFA_IN6(ifa), dst); + matchcmp = tlen - blen; + if (matchcmp > 0) /* (8) */ + goto replace; + if (matchcmp < 0) /* (9) */ + continue; + if (oifp == ifp) /* (a) */ + goto replace; + continue; /* (b) */ + + replace: + ifa_best = (struct in6_ifaddr *)ifa; + blen = tlen >= 0 ? tlen : + in6_matchlen(IFA_IN6(ifa), dst); + best_scope = in6_addrscope(&ifa_best->ia_addr.sin6_addr); } } - if (besta) - return besta; - for (ia = in6_ifaddr; ia; ia = ia->ia_next) { - if (IPV6_ADDR_SCOPE_GLOBAL != - in6_addrscope(&(ia->ia_addr.sin6_addr))) - continue; - /* XXX: is there any case to allow anycast? */ - if ((ia->ia6_flags & IN6_IFF_ANYCAST) != 0) - continue; - if ((ia->ia6_flags & IN6_IFF_NOTREADY) != 0) - continue; - if ((ia->ia6_flags & IN6_IFF_DETACHED) != 0) - continue; - if ((ia->ia6_flags & IN6_IFF_DEPRECATED) != 0) { - if (ip6_use_deprecated) - dep[1] = (struct in6_ifaddr *)ifa; - continue; - } - return ia; + /* count statistics for future improvements */ + if (ifa_best == NULL) + ip6stat.ip6s_sources_none++; + else { + if (oifp == ifa_best->ia_ifp) + ip6stat.ip6s_sources_sameif[best_scope]++; + else + ip6stat.ip6s_sources_otherif[best_scope]++; + + if (best_scope == dst_scope) + ip6stat.ip6s_sources_samescope[best_scope]++; + else + ip6stat.ip6s_sources_otherscope[best_scope]++; + + if ((ifa_best->ia6_flags & IN6_IFF_DEPRECATED) != 0) + ip6stat.ip6s_sources_deprecated[best_scope]++; } - /* use the last-resort values, that are, deprecated addresses */ - if (dep[0]) - return dep[0]; - if (dep[1]) - return dep[1]; - - return NULL; + return(ifa_best); } /* diff --git a/sys/netinet6/in6_var.h b/sys/netinet6/in6_var.h index 7a3a5b682739..0cac8b86bc34 100644 --- a/sys/netinet6/in6_var.h +++ b/sys/netinet6/in6_var.h @@ -1,5 +1,5 @@ -/* $NetBSD: in6_var.h,v 1.11 2000/02/25 05:13:06 itojun Exp $ */ -/* $KAME: in6_var.h,v 1.28 2000/02/25 00:32:22 itojun Exp $ */ +/* $NetBSD: in6_var.h,v 1.12 2000/02/26 08:39:20 itojun Exp $ */ +/* $KAME: in6_var.h,v 1.29 2000/02/25 05:20:58 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -570,10 +570,11 @@ void in6_savemkludge __P((struct in6_ifaddr *)); void in6_setmaxmtu __P((void)); void in6_restoremkludge __P((struct in6_ifaddr *, struct ifnet *)); void in6_purgemkludge __P((struct ifnet *)); -struct in6_ifaddr *in6ifa_ifpforlinklocal __P((struct ifnet *)); +struct in6_ifaddr *in6ifa_ifpforlinklocal __P((struct ifnet *, int)); struct in6_ifaddr *in6ifa_ifpwithaddr __P((struct ifnet *, struct in6_addr *)); char *ip6_sprintf __P((struct in6_addr *)); +int in6_addr2scopeid __P((struct ifnet *, struct in6_addr *)); int in6_matchlen __P((struct in6_addr *, struct in6_addr *)); int in6_are_prefix_equal __P((struct in6_addr *p1, struct in6_addr *p2, int len)); diff --git a/sys/netinet6/ip6_forward.c b/sys/netinet6/ip6_forward.c index 21d99b1b8a15..4f2e48b28e53 100644 --- a/sys/netinet6/ip6_forward.c +++ b/sys/netinet6/ip6_forward.c @@ -1,4 +1,5 @@ -/* $NetBSD: ip6_forward.c,v 1.8 2000/02/06 12:49:45 itojun Exp $ */ +/* $NetBSD: ip6_forward.c,v 1.9 2000/02/26 08:39:20 itojun Exp $ */ +/* $KAME: ip6_forward.c,v 1.28 2000/02/22 14:04:20 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -109,19 +110,17 @@ ip6_forward(m, srcrt) } #endif /*IPSEC_IPV6FWD*/ - if (m->m_flags & (M_BCAST|M_MCAST) || - in6_canforward(&ip6->ip6_src, &ip6->ip6_dst) == 0) { + if ((m->m_flags & (M_BCAST|M_MCAST)) != 0 || + IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { ip6stat.ip6s_cantforward++; - ip6stat.ip6s_badscope++; /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */ if (ip6_log_time + ip6_log_interval < time_second) { - char addr[INET6_ADDRSTRLEN]; ip6_log_time = time_second; - strncpy(addr, ip6_sprintf(&ip6->ip6_src), sizeof(addr)); log(LOG_DEBUG, "cannot forward " "from %s to %s nxt %d received on %s\n", - addr, ip6_sprintf(&ip6->ip6_dst), + ip6_sprintf(&ip6->ip6_src), + ip6_sprintf(&ip6->ip6_dst), ip6->ip6_nxt, if_name(m->m_pkthdr.rcvif)); } @@ -324,6 +323,37 @@ ip6_forward(m, srcrt) } } rt = ip6_forward_rt.ro_rt; + + /* + * Scope check: if a packet can't be delivered to its destination + * for the reason that the destination is beyond the scope of the + * source address, discard the packet and return an icmp6 destination + * unreachable error with Code 2 (beyond scope of source address). + * [draft-ietf-ipngwg-icmp-v3-00.txt, Section 3.1] + */ + if (in6_addr2scopeid(m->m_pkthdr.rcvif, &ip6->ip6_src) != + in6_addr2scopeid(rt->rt_ifp, &ip6->ip6_src)) { + ip6stat.ip6s_cantforward++; + ip6stat.ip6s_badscope++; + in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard); + + if (ip6_log_time + ip6_log_interval < time_second) { + ip6_log_time = time_second; + log(LOG_DEBUG, + "cannot forward " + "src %s, dst %s, nxt %d, rcvif %s, outif %s\n", + ip6_sprintf(&ip6->ip6_src), + ip6_sprintf(&ip6->ip6_dst), + ip6->ip6_nxt, + if_name(m->m_pkthdr.rcvif), if_name(rt->rt_ifp)); + } + if (mcopy) + icmp6_error(mcopy, ICMP6_DST_UNREACH, + ICMP6_DST_UNREACH_BEYONDSCOPE, 0); + m_freem(m); + return; + } + if (m->m_pkthdr.len > rt->rt_ifp->if_mtu) { in6_ifstat_inc(rt->rt_ifp, ifs6_in_toobig); if (mcopy) { diff --git a/sys/netinet6/ip6_var.h b/sys/netinet6/ip6_var.h index e59e72caa2a0..e9a34a2bfa9c 100644 --- a/sys/netinet6/ip6_var.h +++ b/sys/netinet6/ip6_var.h @@ -1,4 +1,5 @@ -/* $NetBSD: ip6_var.h,v 1.9 2000/02/03 18:13:02 itojun Exp $ */ +/* $NetBSD: ip6_var.h,v 1.10 2000/02/26 08:39:20 itojun Exp $ */ +/* $KAME: ip6_var.h,v 1.27 2000/02/22 14:04:22 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -170,9 +171,43 @@ struct ip6stat { u_quad_t ip6s_nogif; /* no match gif found */ u_quad_t ip6s_toomanyhdr; /* discarded due to too many headers */ /* XXX the following two items are not really AF_INET6 thing */ + u_quad_t ip6s_exthdrget; /* # of calls to IP6_EXTHDR_GET */ + u_quad_t ip6s_exthdrget0; /* # of calls to IP6_EXTHDR_GET0 */ u_quad_t ip6s_pulldown; /* # of calls to m_pulldown */ u_quad_t ip6s_pulldown_copy; /* # of mbuf copies in m_pulldown */ u_quad_t ip6s_pulldown_alloc; /* # of mbuf allocs in m_pulldown */ + u_quad_t ip6s_pullup; /* # of calls to m_pullup */ + u_quad_t ip6s_pullup_copy; /* # of possible m_pullup copies */ + u_quad_t ip6s_pullup_alloc; /* # of possible m_pullup mallocs */ + u_quad_t ip6s_pullup_fail; /* # of possible m_pullup failures */ + u_quad_t ip6s_pullup2; /* # of calls to m_pullup2 */ + u_quad_t ip6s_pullup2_copy; /* # of possible m_pullup2 copies */ + u_quad_t ip6s_pullup2_alloc; /* # of possible m_pullup2 mallocs */ + u_quad_t ip6s_pullup2_fail; /* # of possible m_pullup2 failures */ + + /* + * statistics for improvement of the source address selection + * algorithm: + * XXX: hardcoded 16 = # of ip6 multicast scope types + 1 + */ + /* number of times that address selection fails */ + u_quad_t ip6s_sources_none; + /* number of times that an address on the outgoing I/F is chosen */ + u_quad_t ip6s_sources_sameif[16]; + /* number of times that an address on a non-outgoing I/F is chosen */ + u_quad_t ip6s_sources_otherif[16]; + /* + * number of times that an address that has the same scope + * from the destination is chosen. + */ + u_quad_t ip6s_sources_samescope[16]; + /* + * number of times that an address that has a different scope + * from the destination is chosen. + */ + u_quad_t ip6s_sources_otherscope[16]; + /* number of times that an deprecated address is chosen */ + u_quad_t ip6s_sources_deprecated[16]; }; #ifdef _KERNEL diff --git a/sys/netinet6/mld6.c b/sys/netinet6/mld6.c index 3ec093c61583..b7f1a18629b2 100644 --- a/sys/netinet6/mld6.c +++ b/sys/netinet6/mld6.c @@ -1,4 +1,5 @@ -/* $NetBSD: mld6.c,v 1.10 2000/02/06 12:49:47 itojun Exp $ */ +/* $NetBSD: mld6.c,v 1.11 2000/02/26 08:39:20 itojun Exp $ */ +/* $KAME: mld6.c,v 1.16 2000/02/22 14:04:27 itojun Exp $ */ /* * Copyright (C) 1998 WIDE Project. @@ -119,7 +120,7 @@ mld6_init() mld6_timers_are_running = 0; /* ip6h_nxt will be fill in later */ - hbh->ip6h_len = 0; /* (8 >> 3) - 1*/ + hbh->ip6h_len = 0; /* (8 >> 3) - 1 */ /* XXX: grotty hard coding... */ hbh_buf[2] = IP6OPT_PADN; /* 2 byte padding */ @@ -140,7 +141,7 @@ mld6_start_listening(in6m) int s = splsoftnet(); /* - * (draft-ietf-ipngwg-mld, page 10) + * RFC2710 page 10: * The node never sends a Report or Done for the link-scope all-nodes * address. * MLD messages are never sent for multicast addresses whose scope is 0 @@ -184,7 +185,7 @@ mld6_input(m, off) int off; { struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); - struct mld6_hdr *mldh = (struct mld6_hdr *)(mtod(m, caddr_t) + off); + struct mld6_hdr *mldh; struct ifnet *ifp = m->m_pkthdr.rcvif; struct in6_multi *in6m; struct in6_ifaddr *ia; @@ -196,13 +197,25 @@ mld6_input(m, off) "mld6_input: src %s is not link-local\n", ip6_sprintf(&ip6->ip6_src)); /* - * spec(draft-ietf-ipngwg-mld) does not explicitly + * spec (RFC2710) does not explicitly * specify to discard the packet from a non link-local * source address. But we believe it's expected to do so. */ + m_freem(m); return; } +#ifndef PULLDOWN_TEST + IP6_EXTHDR_CHECK(m, off, sizeof(*mldh),); + mldh = (struct mld6_hdr *)(mtod(m, caddr_t) + off); +#else + IP6_EXTHDR_GET(mldh, struct mld6_hdr *, m, off, sizeof(*mldh)); + if (mldh == NULL) { + icmp6stat.icp6s_tooshort++; + return; + } +#endif + /* * In the MLD6 specification, there are 3 states and a flag. * @@ -227,25 +240,25 @@ mld6_input(m, off) htons(ifp->if_index); /* XXX */ /* - * - Start the timers in all of our membership records - * that the query applies to for the interface on - * which the query arrived excl. those that belong - * to the "all-nodes" group (ff02::1). - * - Restart any timer that is already running but has - * A value longer than the requested timeout. - * - Use the value specified in the query message as - * the maximum timeout. - */ + * - Start the timers in all of our membership records + * that the query applies to for the interface on + * which the query arrived excl. those that belong + * to the "all-nodes" group (ff02::1). + * - Restart any timer that is already running but has + * A value longer than the requested timeout. + * - Use the value specified in the query message as + * the maximum timeout. + */ IFP_TO_IA6(ifp, ia); if (ia == NULL) break; /* - * XXX: System timer resolution is too low to handle Max - * Response Delay, so set 1 to the internal timer even if - * the calculated value equals to zero when Max Response - * Delay is positive. - */ + * XXX: System timer resolution is too low to handle Max + * Response Delay, so set 1 to the internal timer even if + * the calculated value equals to zero when Max Response + * Delay is positive. + */ timer = ntohs(mldh->mld6_maxdelay)*PR_FASTHZ/MLD6_TIMER_SCALE; if (timer == 0 && mldh->mld6_maxdelay) timer = 1; @@ -287,14 +300,14 @@ mld6_input(m, off) break; case MLD6_LISTENER_REPORT: /* - * For fast leave to work, we have to know that we are the - * last person to send a report for this group. Reports - * can potentially get looped back if we are a multicast - * router, so discard reports sourced by me. - * Note that it is impossible to check IFF_LOOPBACK flag of - * ifp for this purpose, since ip6_mloopback pass the physical - * interface to looutput. - */ + * For fast leave to work, we have to know that we are the + * last person to send a report for this group. Reports + * can potentially get looped back if we are a multicast + * router, so discard reports sourced by me. + * Note that it is impossible to check IFF_LOOPBACK flag of + * ifp for this purpose, since ip6_mloopback pass the physical + * interface to looutput. + */ if (m->m_flags & M_LOOP) /* XXX: grotty flag, but efficient */ break; @@ -305,9 +318,9 @@ mld6_input(m, off) mldh->mld6_addr.s6_addr16[1] = htons(ifp->if_index); /* XXX */ /* - * If we belong to the group being reported, stop - * our timer for that group. - */ + * If we belong to the group being reported, stop + * our timer for that group. + */ IN6_LOOKUP_MULTI(mldh->mld6_addr, ifp, in6m); if (in6m) { in6m->in6m_timer = 0; /* transit to idle state */ @@ -321,6 +334,8 @@ mld6_input(m, off) log(LOG_ERR, "mld6_input: illegal type(%d)", mldh->mld6_type); break; } + + m_freem(m); } void @@ -372,7 +387,8 @@ mld6_sendpkt(in6m, type, dst) * At first, find a link local address on the outgoing interface * to use as the source address of the MLD packet. */ - if ((ia = in6ifa_ifpforlinklocal(ifp)) == NULL) + if ((ia = in6ifa_ifpforlinklocal(ifp, IN6_IFF_NOTREADY|IN6_IFF_ANYCAST)) + == NULL) return; /* diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c index 561cd79c447a..d9100d1198fb 100644 --- a/sys/netinet6/nd6.c +++ b/sys/netinet6/nd6.c @@ -1,4 +1,5 @@ -/* $NetBSD: nd6.c,v 1.17 2000/02/06 12:49:47 itojun Exp $ */ +/* $NetBSD: nd6.c,v 1.18 2000/02/26 08:39:20 itojun Exp $ */ +/* $KAME: nd6.c,v 1.41 2000/02/24 16:34:50 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -45,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -84,7 +86,6 @@ int nd6_delay = 5; /* delay first probe time 5 second */ int nd6_umaxtries = 3; /* maximum unicast query */ int nd6_mmaxtries = 3; /* maximum multicast query */ int nd6_useloopback = 1; /* use loopback interface for local traffic */ -int nd6_proxyall = 0; /* enable Proxy Neighbor Advertisement */ /* preventing too many loops in ND option parsing */ int nd6_maxndopt = 10; /* max # of ND options allowed */ @@ -431,9 +432,8 @@ nd6_timer(ignored_arg) } break; case ND6_LLINFO_REACHABLE: - if (ln->ln_expire) { + if (ln->ln_expire) ln->ln_state = ND6_LLINFO_STALE; - } break; /* * ND6_LLINFO_STALE state requires nothing for timer @@ -578,7 +578,7 @@ nd6_purge(ifp) * Nuke neighbor cache entries for the ifp. * Note that rt->rt_ifp may not be the same as ifp, * due to KAME goto ours hack. See RTM_RESOLVE case in - * nd6_rtrequest(), and ip6_input()). + * nd6_rtrequest(), and ip6_input(). */ ln = llinfo_nd6.ln_next; while (ln && ln != &llinfo_nd6) { @@ -597,7 +597,8 @@ nd6_purge(ifp) } /* - * Interface route will be retained by nd6_free(). Nuke it. + * Neighbor cache entry for interface route will be retained + * with ND6_LLINFO_WAITDELETE state, by nd6_free(). Nuke it. */ ln = llinfo_nd6.ln_next; while (ln && ln != &llinfo_nd6) { @@ -768,9 +769,19 @@ nd6_free(rt) struct in6_addr in6 = ((struct sockaddr_in6 *)rt_key(rt))->sin6_addr; struct nd_defrouter *dr; + /* + * Clear all destination cache entries for the neighbor. + * XXX: is it better to restrict this to hosts? + */ + pfctlinput(PRC_HOSTDEAD, rt_key(rt)); + if (!ip6_forwarding && ip6_accept_rtadv) { /* XXX: too restrictive? */ int s; +#ifdef __NetBSD__ s = splsoftnet(); +#else + s = splnet(); +#endif dr = defrouter_lookup(&((struct sockaddr_in6 *)rt_key(rt))->sin6_addr, rt->rt_ifp); if (ln->ln_router || dr) { @@ -993,7 +1004,7 @@ nd6_rtrequest(req, rt, sa) * SIN(rt_mask(rt))->sin_addr.s_addr != 0xffffffff) * rt->rt_flags |= RTF_CLONING; */ - if (rt->rt_flags & RTF_CLONING || rt->rt_flags & RTF_LLINFO) { + if (rt->rt_flags & (RTF_CLONING | RTF_LLINFO)) { /* * Case 1: This route should come from * a route to interface. RTF_LLINFO flag is set @@ -1020,17 +1031,37 @@ nd6_rtrequest(req, rt, sa) if (rt->rt_flags & RTF_CLONING) break; } - /* Announce a new entry if requested. */ + /* + * In IPv4 code, we try to annonuce new RTF_ANNOUNCE entry here. + * We don't do that here since llinfo is not ready yet. + * + * There are also couple of other things to be discussed: + * - unsolicited NA code needs improvement beforehand + * - RFC2461 says we MAY send multicast unsolicited NA + * (7.2.6 paragraph 4), however, it also says that we + * SHOULD provide a mechanism to prevent multicast NA storm. + * we don't have anything like it right now. + * note that the mechanism need a mutual agreement + * between proxies, which means that we need to implement + * a new protocol, or new kludge. + * - from RFC2461 6.2.4, host MUST NOT send unsolicited NA. + * we need to check ip6forwarding before sending it. + * (or should we allow proxy ND configuration only for + * routers? there's no mention about proxy ND from hosts) + */ +#if 0 + /* XXX it does not work */ if (rt->rt_flags & RTF_ANNOUNCE) nd6_na_output(ifp, - &SIN6(rt_key(rt))->sin6_addr, - &SIN6(rt_key(rt))->sin6_addr, - ip6_forwarding ? ND_NA_FLAG_ROUTER : 0, - 1); + &SIN6(rt_key(rt))->sin6_addr, + &SIN6(rt_key(rt))->sin6_addr, + ip6_forwarding ? ND_NA_FLAG_ROUTER : 0, + 1, NULL); +#endif /* FALLTHROUGH */ case RTM_RESOLVE: if (gate->sa_family != AF_LINK || - gate->sa_len < sizeof(null_sdl)) { + gate->sa_len < sizeof(null_sdl)) { log(LOG_DEBUG, "nd6_rtrequest: bad gateway value\n"); break; } @@ -1104,12 +1135,50 @@ nd6_rtrequest(req, rt, sa) rt->rt_ifa = ifa; } } + } else if (rt->rt_flags & RTF_ANNOUNCE) { + ln->ln_expire = 0; + ln->ln_state = ND6_LLINFO_REACHABLE; + + /* join solicited node multicast for proxy ND */ + if (ifp->if_flags & IFF_MULTICAST) { + struct in6_addr llsol; + int error; + + llsol = SIN6(rt_key(rt))->sin6_addr; + llsol.s6_addr16[0] = htons(0xff02); + llsol.s6_addr16[1] = htons(ifp->if_index); + llsol.s6_addr32[1] = 0; + llsol.s6_addr32[2] = htonl(1); + llsol.s6_addr8[12] = 0xff; + + (void)in6_addmulti(&llsol, ifp, &error); + if (error) + printf( +"nd6_rtrequest: could not join solicited node multicast (errno=%d)\n", error); + } } break; case RTM_DELETE: if (!ln) break; + /* leave from solicited node multicast for proxy ND */ + if ((rt->rt_flags & RTF_ANNOUNCE) != 0 && + (ifp->if_flags & IFF_MULTICAST) != 0) { + struct in6_addr llsol; + struct in6_multi *in6m; + + llsol = SIN6(rt_key(rt))->sin6_addr; + llsol.s6_addr16[0] = htons(0xff02); + llsol.s6_addr16[1] = htons(ifp->if_index); + llsol.s6_addr32[1] = 0; + llsol.s6_addr32[2] = htonl(1); + llsol.s6_addr8[12] = 0xff; + + IN6_LOOKUP_MULTI(llsol, ifp, in6m); + if (in6m) + in6_delmulti(in6m); + } nd6_inuse--; ln->ln_next->ln_prev = ln->ln_prev; ln->ln_prev->ln_next = ln->ln_next; @@ -1163,7 +1232,7 @@ nd6_p2p_rtrequest(req, rt, sa) &SIN6(rt_key(rt))->sin6_addr, &SIN6(rt_key(rt))->sin6_addr, ip6_forwarding ? ND_NA_FLAG_ROUTER : 0, - 1); + 1, NULL); /* FALLTHROUGH */ case RTM_RESOLVE: /* @@ -1225,6 +1294,11 @@ nd6_ioctl(cmd, data, ifp) splx(s); break; case SIOCGPRLST_IN6: + /* + * XXX meaning of fields, especialy "raflags", is very + * differnet between RA prefix list and RR/static prefix list. + * how about separating ioctls into two? + */ bzero(prl, sizeof(*prl)); s = splsoftnet(); pr = nd_prefix.lh_first; @@ -1262,11 +1336,11 @@ nd6_ioctl(cmd, data, ifp) pfr = pfr->pfr_next; } prl->prefix[i].advrtrs = j; + prl->prefix[i].origin = PR_ORIG_RA; i++; pr = pr->ndpr_next; } - splx(s); { struct rr_prefix *rpp; @@ -1282,9 +1356,11 @@ nd6_ioctl(cmd, data, ifp) prl->prefix[i].if_index = rpp->rp_ifp->if_index; prl->prefix[i].expire = rpp->rp_expire; prl->prefix[i].advrtrs = 0; + prl->prefix[i].origin = rpp->rp_origin; i++; } } + splx(s); break; case SIOCGIFINFO_IN6: diff --git a/sys/netinet6/nd6.h b/sys/netinet6/nd6.h index 2c49c7c7432f..4ba6eddd84eb 100644 --- a/sys/netinet6/nd6.h +++ b/sys/netinet6/nd6.h @@ -1,4 +1,5 @@ -/* $NetBSD: nd6.h,v 1.8 2000/02/04 14:34:28 itojun Exp $ */ +/* $NetBSD: nd6.h,v 1.9 2000/02/26 08:39:20 itojun Exp $ */ +/* $KAME: nd6.h,v 1.16 2000/02/24 16:34:51 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -32,6 +33,11 @@ #ifndef _NETINET6_ND6_H_ #define _NETINET6_ND6_H_ +/* see net/route.h, or net/if_inarp.h */ +#ifndef RTF_ANNOUNCE +#define RTF_ANNOUNCE RTF_PROTO2 +#endif + #include struct llinfo_nd6 { @@ -94,6 +100,7 @@ struct in6_prlist { struct in6_addr prefix; struct prf_ra raflags; u_char prefixlen; + u_char origin; u_long vltime; u_long pltime; u_long expire; @@ -220,7 +227,6 @@ extern int nd6_delay; extern int nd6_umaxtries; extern int nd6_mmaxtries; extern int nd6_useloopback; -extern int nd6_proxyall; extern struct llinfo_nd6 llinfo_nd6; extern struct nd_ifinfo *nd_ifinfo; extern struct nd_drhead nd_defrouter; @@ -284,7 +290,7 @@ int nd6_storelladdr __P((struct ifnet *, struct rtentry *, struct mbuf *, /* nd6_nbr.c */ void nd6_na_input __P((struct mbuf *, int, int)); void nd6_na_output __P((struct ifnet *, struct in6_addr *, - struct in6_addr *, u_long, int)); + struct in6_addr *, u_long, int, struct sockaddr *)); void nd6_ns_input __P((struct mbuf *, int, int)); void nd6_ns_output __P((struct ifnet *, struct in6_addr *, struct in6_addr *, struct llinfo_nd6 *, int)); diff --git a/sys/netinet6/nd6_nbr.c b/sys/netinet6/nd6_nbr.c index 77ecfbf0f7c6..320f28d5194c 100644 --- a/sys/netinet6/nd6_nbr.c +++ b/sys/netinet6/nd6_nbr.c @@ -1,4 +1,5 @@ -/* $NetBSD: nd6_nbr.c,v 1.15 2000/02/07 05:42:28 itojun Exp $ */ +/* $NetBSD: nd6_nbr.c,v 1.16 2000/02/26 08:39:20 itojun Exp $ */ +/* $KAME: nd6_nbr.c,v 1.28 2000/02/26 06:53:11 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -77,8 +78,6 @@ static int dad_maxtry = 15; /* max # of *tries* to transmit DAD packet */ * * Based on RFC 2461 * Based on RFC 2462 (duplicated address detection) - * - * XXX proxy advertisement */ void nd6_ns_input(m, off, icmp6len) @@ -87,11 +86,10 @@ nd6_ns_input(m, off, icmp6len) { struct ifnet *ifp = m->m_pkthdr.rcvif; struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); - struct nd_neighbor_solicit *nd_ns - = (struct nd_neighbor_solicit *)((caddr_t)ip6 + off); + struct nd_neighbor_solicit *nd_ns; struct in6_addr saddr6 = ip6->ip6_src; struct in6_addr daddr6 = ip6->ip6_dst; - struct in6_addr taddr6 = nd_ns->nd_ns_target; + struct in6_addr taddr6; struct in6_addr myaddr6; char *lladdr = NULL; struct ifaddr *ifa; @@ -99,11 +97,12 @@ nd6_ns_input(m, off, icmp6len) int anycast = 0, proxy = 0, tentative = 0; int tlladdr; union nd_opts ndopts; + struct sockaddr_dl *proxydl = NULL; if (ip6->ip6_hlim != 255) { log(LOG_ERR, "nd6_ns_input: invalid hlim %d\n", ip6->ip6_hlim); - return; + goto freeit; } if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) { @@ -121,6 +120,18 @@ nd6_ns_input(m, off, icmp6len) } } +#ifndef PULLDOWN_TEST + IP6_EXTHDR_CHECK(m, off, icmp6len,); + nd_ns = (struct nd_neighbor_solicit *)((caddr_t)ip6 + off); +#else + IP6_EXTHDR_GET(nd_ns, struct nd_neighbor_solicit *, m, off, icmp6len); + if (nd_ns == NULL) { + icmp6stat.icp6s_tooshort++; + return; + } +#endif + taddr6 = nd_ns->nd_ns_target; + if (IN6_IS_ADDR_MULTICAST(&taddr6)) { log(LOG_INFO, "nd6_ns_input: bad NS target (multicast)\n"); goto bad; @@ -178,7 +189,7 @@ nd6_ns_input(m, off, icmp6len) ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &taddr6); /* (2) check. */ - if (!ifa && nd6_proxyall) { + if (!ifa) { struct rtentry *rt; struct sockaddr_in6 tsin6; @@ -187,17 +198,25 @@ nd6_ns_input(m, off, icmp6len) tsin6.sin6_family = AF_INET6; tsin6.sin6_addr = taddr6; - rt = rtalloc1((struct sockaddr *)&tsin6, 0); - if (rt && rt->rt_ifp != ifp) { + rt = rtalloc1((struct sockaddr *)&tsin6, 0 +#ifdef __FreeBSD__ + , 0 +#endif /* __FreeBSD__ */ + ); + if (rt && (rt->rt_flags & RTF_ANNOUNCE) != 0 && + rt->rt_gateway->sa_family == AF_LINK) { /* - * search link local addr for ifp, and use it for - * proxy NA. + * proxy NDP for single entry */ - ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp); - if (ifa) + ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp, + IN6_IFF_NOTREADY|IN6_IFF_ANYCAST); + if (ifa) { proxy = 1; + proxydl = SDL(rt->rt_gateway); + } } - rtfree(rt); + if (rt) + rtfree(rt); } if (!ifa) { /* @@ -205,13 +224,13 @@ nd6_ns_input(m, off, icmp6len) * assigned for us. We MUST silently ignore it. * See RFC2461 7.2.3. */ - return; + goto freeit; } myaddr6 = *IFA_IN6(ifa); anycast = ((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST; tentative = ((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_TENTATIVE; if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DUPLICATED) - return; + goto freeit; if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { log(LOG_INFO, @@ -224,7 +243,7 @@ nd6_ns_input(m, off, icmp6len) log(LOG_INFO, "nd6_ns_input: duplicate IP6 address %s\n", ip6_sprintf(&saddr6)); - return; + goto freeit; } /* @@ -250,7 +269,7 @@ nd6_ns_input(m, off, icmp6len) if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) nd6_dad_ns_input(ifa); - return; + goto freeit; } /* @@ -268,8 +287,8 @@ nd6_ns_input(m, off, icmp6len) ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) | (ip6_forwarding ? ND_NA_FLAG_ROUTER : 0), - tlladdr); - return; + tlladdr, (struct sockaddr *)proxydl); + goto freeit; } nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_NEIGHBOR_SOLICIT, 0); @@ -278,14 +297,16 @@ nd6_ns_input(m, off, icmp6len) ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) | (ip6_forwarding ? ND_NA_FLAG_ROUTER : 0) | ND_NA_FLAG_SOLICITED, - tlladdr); + tlladdr, (struct sockaddr *)proxydl); + freeit: + m_freem(m); return; bad: log(LOG_ERR, "nd6_ns_input: src=%s\n", ip6_sprintf(&saddr6)); log(LOG_ERR, "nd6_ns_input: dst=%s\n", ip6_sprintf(&daddr6)); log(LOG_ERR, "nd6_ns_input: tgt=%s\n", ip6_sprintf(&taddr6)); - return; + m_freem(m); } /* @@ -485,6 +506,10 @@ nd6_ns_output(ifp, daddr6, taddr6, ln, dad) * * Based on RFC 2461 * Based on RFC 2462 (duplicated address detection) + * + * the following items are not implemented yet: + * - proxy advertisement delay rule (RFC2461 7.2.8, last paragraph, SHOULD) + * - anycast advertisement delay rule (RFC2461 7.2.7, SHOULD) */ void nd6_na_input(m, off, icmp6len) @@ -493,17 +518,16 @@ nd6_na_input(m, off, icmp6len) { struct ifnet *ifp = m->m_pkthdr.rcvif; struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); - struct nd_neighbor_advert *nd_na - = (struct nd_neighbor_advert *)((caddr_t)ip6 + off); + struct nd_neighbor_advert *nd_na; #if 0 struct in6_addr saddr6 = ip6->ip6_src; #endif struct in6_addr daddr6 = ip6->ip6_dst; - struct in6_addr taddr6 = nd_na->nd_na_target; - int flags = nd_na->nd_na_flags_reserved; - int is_router = ((flags & ND_NA_FLAG_ROUTER) != 0); - int is_solicited = ((flags & ND_NA_FLAG_SOLICITED) != 0); - int is_override = ((flags & ND_NA_FLAG_OVERRIDE) != 0); + struct in6_addr taddr6; + int flags; + int is_router; + int is_solicited; + int is_override; char *lladdr = NULL; int lladdrlen = 0; struct ifaddr *ifa; @@ -515,8 +539,24 @@ nd6_na_input(m, off, icmp6len) if (ip6->ip6_hlim != 255) { log(LOG_ERR, "nd6_na_input: invalid hlim %d\n", ip6->ip6_hlim); + goto freeit; + } + +#ifndef PULLDOWN_TEST + IP6_EXTHDR_CHECK(m, off, icmp6len,); + nd_na = (struct nd_neighbor_advert *)((caddr_t)ip6 + off); +#else + IP6_EXTHDR_GET(nd_na, struct nd_neighbor_advert *, m, off, icmp6len); + if (nd_na == NULL) { + icmp6stat.icp6s_tooshort++; return; } +#endif + taddr6 = nd_na->nd_na_target; + flags = nd_na->nd_na_flags_reserved; + is_router = ((flags & ND_NA_FLAG_ROUTER) != 0); + is_solicited = ((flags & ND_NA_FLAG_SOLICITED) != 0); + is_override = ((flags & ND_NA_FLAG_OVERRIDE) != 0); if (IN6_IS_SCOPE_LINKLOCAL(&taddr6)) taddr6.s6_addr16[1] = htons(ifp->if_index); @@ -525,20 +565,20 @@ nd6_na_input(m, off, icmp6len) log(LOG_ERR, "nd6_na_input: invalid target address %s\n", ip6_sprintf(&taddr6)); - return; + goto freeit; } if (IN6_IS_ADDR_MULTICAST(&daddr6)) if (is_solicited) { log(LOG_ERR, "nd6_na_input: a solicited adv is multicasted\n"); - return; + goto freeit; } icmp6len -= sizeof(*nd_na); nd6_option_init(nd_na + 1, icmp6len, &ndopts); if (nd6_options(&ndopts) < 0) { log(LOG_INFO, "nd6_na_input: invalid ND option, ignored\n"); - return; + goto freeit; } if (ndopts.nd_opts_tgt_lladdr) { @@ -560,7 +600,7 @@ nd6_na_input(m, off, icmp6len) if (ifa && (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_TENTATIVE)) { nd6_dad_na_input(ifa); - return; + goto freeit; } /* Just for safety, maybe unnecessery. */ @@ -568,7 +608,7 @@ nd6_na_input(m, off, icmp6len) log(LOG_ERR, "nd6_na_input: duplicate IP6 address %s\n", ip6_sprintf(&taddr6)); - return; + goto freeit; } if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { @@ -585,7 +625,7 @@ nd6_na_input(m, off, icmp6len) if ((rt == NULL) || ((ln = (struct llinfo_nd6 *)rt->rt_llinfo) == NULL) || ((sdl = SDL(rt->rt_gateway)) == NULL)) - return; + goto freeit; if (ln->ln_state == ND6_LLINFO_INCOMPLETE) { /* @@ -593,7 +633,7 @@ nd6_na_input(m, off, icmp6len) * discard the packet. */ if (ifp->if_addrlen && !lladdr) - return; + goto freeit; /* * Record link-layer address, and update the state. @@ -652,7 +692,7 @@ nd6_na_input(m, off, icmp6len) */ if (ln->ln_state == ND6_LLINFO_REACHABLE) ln->ln_state = ND6_LLINFO_STALE; - return; + goto freeit; } else if (is_override /* (2a) */ || (!is_override && (lladdr && !llchange)) /* (2b) */ || !lladdr) { /* (2c) */ @@ -721,6 +761,9 @@ nd6_na_input(m, off, icmp6len) #endif ln->ln_hold = 0; } + + freeit: + m_freem(m); } /* @@ -728,16 +771,17 @@ nd6_na_input(m, off, icmp6len) * * Based on RFC 2461 * - * XXX NA delay for anycast address is not implemented yet - * (RFC 2461 7.2.7) - * XXX proxy advertisement? + * the following items are not implemented yet: + * - proxy advertisement delay rule (RFC2461 7.2.8, last paragraph, SHOULD) + * - anycast advertisement delay rule (RFC2461 7.2.7, SHOULD) */ void -nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr) +nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr, sdl0) struct ifnet *ifp; struct in6_addr *daddr6, *taddr6; u_long flags; - int tlladdr; /* 1 if include target link-layer address */ + int tlladdr; /* 1 if include target link-layer address */ + struct sockaddr *sdl0; /* sockaddr_dl (= proxy NA) or NULL */ { struct mbuf *m; struct ip6_hdr *ip6; @@ -822,7 +866,23 @@ nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr) * Basically, if NS packet is sent to unicast/anycast addr, * target lladdr option SHOULD NOT be included. */ - if (tlladdr && (mac = nd6_ifptomac(ifp))) { + if (tlladdr) { + mac = NULL; + /* + * sdl0 != NULL indicates proxy NA. If we do proxy, use + * lladdr in sdl0. If we are not proxying (sending NA for + * my address) use lladdr configured for the interface. + */ + if (sdl0 == NULL) + mac = nd6_ifptomac(ifp); + else if (sdl0->sa_family == AF_LINK) { + struct sockaddr_dl *sdl; + sdl = (struct sockaddr_dl *)sdl0; + if (sdl->sdl_alen == ifp->if_addrlen) + mac = LLADDR(sdl); + } + } + if (tlladdr && mac) { int optlen = sizeof(struct nd_opt_hdr) + ifp->if_addrlen; struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)(nd_na + 1); diff --git a/sys/netinet6/nd6_rtr.c b/sys/netinet6/nd6_rtr.c index 248b1b579258..18ecb0cc4742 100644 --- a/sys/netinet6/nd6_rtr.c +++ b/sys/netinet6/nd6_rtr.c @@ -1,4 +1,5 @@ -/* $NetBSD: nd6_rtr.c,v 1.11 2000/02/06 12:49:48 itojun Exp $ */ +/* $NetBSD: nd6_rtr.c,v 1.12 2000/02/26 08:39:21 itojun Exp $ */ +/* $KAME: nd6_rtr.c,v 1.27 2000/02/26 06:53:11 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -97,8 +98,7 @@ nd6_rs_input(m, off, icmp6len) { struct ifnet *ifp = m->m_pkthdr.rcvif; struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); - struct nd_router_solicit *nd_rs - = (struct nd_router_solicit *)((caddr_t)ip6 + off); + struct nd_router_solicit *nd_rs; struct in6_addr saddr6 = ip6->ip6_src; #if 0 struct in6_addr daddr6 = ip6->ip6_dst; @@ -115,13 +115,13 @@ nd6_rs_input(m, off, icmp6len) /* If I'm not a router, ignore it. */ if (ip6_accept_rtadv != 0 || ip6_forwarding != 1) - return; + goto freeit; /* Sanity checks */ if (ip6->ip6_hlim != 255) { log(LOG_ERR, "nd6_rs_input: invalid hlim %d\n", ip6->ip6_hlim); - return; + goto freeit; } /* @@ -129,13 +129,24 @@ nd6_rs_input(m, off, icmp6len) * This indicates that the src has no IP address assigned yet. */ if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) + goto freeit; + +#ifndef PULLDOWN_TEST + IP6_EXTHDR_CHECK(m, off, icmp6len,); + nd_rs = (struct nd_router_solicit *)((caddr_t)ip6 + off); +#else + IP6_EXTHDR_GET(nd_rs, struct nd_router_solicit *, m, off, icmp6len); + if (nd_rs == NULL) { + icmp6stat.icp6s_tooshort++; return; + } +#endif icmp6len -= sizeof(*nd_rs); nd6_option_init(nd_rs + 1, icmp6len, &ndopts); if (nd6_options(&ndopts) < 0) { log(LOG_INFO, "nd6_rs_input: invalid ND option, ignored\n"); - return; + goto freeit; } if (ndopts.nd_opts_src_lladdr) { @@ -151,6 +162,9 @@ nd6_rs_input(m, off, icmp6len) } nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_ROUTER_SOLICIT, 0); + + freeit: + m_freem(m); } /* @@ -168,12 +182,11 @@ nd6_ra_input(m, off, icmp6len) struct ifnet *ifp = m->m_pkthdr.rcvif; struct nd_ifinfo *ndi = &nd_ifinfo[ifp->if_index]; struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); - struct nd_router_advert *nd_ra = - (struct nd_router_advert *)((caddr_t)ip6 + off); + struct nd_router_advert *nd_ra; struct in6_addr saddr6 = ip6->ip6_src; #if 0 struct in6_addr daddr6 = ip6->ip6_dst; - int flags = nd_ra->nd_ra_flags_reserved; + int flags; /* = nd_ra->nd_ra_flags_reserved; */ int is_managed = ((flags & ND_RA_FLAG_MANAGED) != 0); int is_other = ((flags & ND_RA_FLAG_OTHER) != 0); #endif @@ -181,26 +194,37 @@ nd6_ra_input(m, off, icmp6len) struct nd_defrouter *dr; if (ip6_accept_rtadv == 0) - return; + goto freeit; if (ip6->ip6_hlim != 255) { log(LOG_ERR, "nd6_ra_input: invalid hlim %d\n", ip6->ip6_hlim); - return; + goto freeit; } if (!IN6_IS_ADDR_LINKLOCAL(&saddr6)) { log(LOG_ERR, "nd6_ra_input: src %s is not link-local\n", ip6_sprintf(&saddr6)); + goto freeit; + } + +#ifndef PULLDOWN_TEST + IP6_EXTHDR_CHECK(m, off, icmp6len,); + nd_ra = (struct nd_router_advert *)((caddr_t)ip6 + off); +#else + IP6_EXTHDR_GET(nd_ra, struct nd_router_advert *, m, off, icmp6len); + if (nd_ra == NULL) { + icmp6stat.icp6s_tooshort++; return; } +#endif icmp6len -= sizeof(*nd_ra); nd6_option_init(nd_ra + 1, icmp6len, &ndopts); if (nd6_options(&ndopts) < 0) { log(LOG_INFO, "nd6_ra_input: invalid ND option, ignored\n"); - return; + goto freeit; } { @@ -367,6 +391,9 @@ nd6_ra_input(m, off, icmp6len) */ pfxlist_onlink_check(); } + + freeit: + m_freem(m); } /* @@ -1160,7 +1187,7 @@ in6_ifadd(ifp, in6, addr, prefixlen) in6_len2mask(&mask, prefixlen); /* find link-local address (will be interface ID) */ - ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp); + ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp, 0);/* 0 is OK? */ if (ifa) ib = (struct in6_ifaddr *)ifa; else @@ -1341,7 +1368,7 @@ in6_ifdel(ifp, in6) } TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list); - IFAFREE((struct ifaddr *)ia); + IFAFREE(&ia->ia_ifa); /* lladdr is never deleted */ oia = ia;