diff options
author | 2002-06-08 21:22:02 +0000 | |
---|---|---|
committer | 2002-06-08 21:22:02 +0000 | |
commit | d8a7e3a701bd700a8a8835b69db2e106f957907c (patch) | |
tree | 6257fbf0d620979209b57e383f146582085a81e4 | |
parent | comment out paragraph that does not fit to openbsd-current (diff) | |
download | wireguard-openbsd-d8a7e3a701bd700a8a8835b69db2e106f957907c.tar.xz wireguard-openbsd-d8a7e3a701bd700a8a8835b69db2e106f957907c.zip |
sync with latest KAME in6_ifaddr/prefix/default router manipulation.
behavior changes:
- two iocts used by ndp(8) are now obsolete (backward compat provided).
use sysctl path instead.
- lo0 does not get ::1 automatically. it will get ::1 when lo0 comes up.
-rw-r--r-- | sys/conf/files | 3 | ||||
-rw-r--r-- | sys/netinet/icmp6.h | 10 | ||||
-rw-r--r-- | sys/netinet/in_pcb.h | 11 | ||||
-rw-r--r-- | sys/netinet6/icmp6.c | 5 | ||||
-rw-r--r-- | sys/netinet6/in6.c | 1252 | ||||
-rw-r--r-- | sys/netinet6/in6.h | 11 | ||||
-rw-r--r-- | sys/netinet6/in6_ifattach.c | 545 | ||||
-rw-r--r-- | sys/netinet6/in6_ifattach.h | 3 | ||||
-rw-r--r-- | sys/netinet6/in6_prefix.c | 1185 | ||||
-rw-r--r-- | sys/netinet6/in6_prefix.h | 91 | ||||
-rw-r--r-- | sys/netinet6/in6_var.h | 42 | ||||
-rw-r--r-- | sys/netinet6/ip6_forward.c | 4 | ||||
-rw-r--r-- | sys/netinet6/ip6_input.c | 89 | ||||
-rw-r--r-- | sys/netinet6/ip6_output.c | 4 | ||||
-rw-r--r-- | sys/netinet6/ip6_var.h | 13 | ||||
-rw-r--r-- | sys/netinet6/mld6.c | 73 | ||||
-rw-r--r-- | sys/netinet6/nd6.c | 816 | ||||
-rw-r--r-- | sys/netinet6/nd6.h | 110 | ||||
-rw-r--r-- | sys/netinet6/nd6_nbr.c | 296 | ||||
-rw-r--r-- | sys/netinet6/nd6_rtr.c | 1639 |
20 files changed, 3090 insertions, 3112 deletions
diff --git a/sys/conf/files b/sys/conf/files index d0911d76416..b0e26ae8d7f 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1,4 +1,4 @@ -# $OpenBSD: files,v 1.248 2002/05/16 16:16:51 provos Exp $ +# $OpenBSD: files,v 1.249 2002/06/08 21:22:02 itojun Exp $ # $NetBSD: files,v 1.87 1996/05/19 17:17:50 jonathan Exp $ # @(#)files.newconf 7.5 (Berkeley) 5/10/93 @@ -859,7 +859,6 @@ file netinet6/in6_ifattach.c inet6 file netinet6/in6_cksum.c inet6 #file netinet6/in6_pcb.c inet6 file netinet6/in6_src.c inet6 -file netinet6/in6_prefix.c inet6 file netinet6/in6_proto.c inet6 file netinet6/dest6.c inet6 file netinet6/frag6.c inet6 diff --git a/sys/netinet/icmp6.h b/sys/netinet/icmp6.h index c98bc568887..a2d7000440c 100644 --- a/sys/netinet/icmp6.h +++ b/sys/netinet/icmp6.h @@ -1,5 +1,5 @@ -/* $OpenBSD: icmp6.h,v 1.22 2002/05/29 02:59:12 itojun Exp $ */ -/* $KAME: icmp6.h,v 1.39 2001/02/06 03:48:06 itojun Exp $ */ +/* $OpenBSD: icmp6.h,v 1.23 2002/06/08 21:22:02 itojun Exp $ */ +/* $KAME: icmp6.h,v 1.71 2002/05/27 04:18:29 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -570,7 +570,9 @@ struct icmp6stat { #define ICMPV6CTL_MTUDISC_HIWAT 16 #define ICMPV6CTL_MTUDISC_LOWAT 17 #define ICMPV6CTL_ND6_DEBUG 18 -#define ICMPV6CTL_MAXID 19 +#define ICMPV6CTL_ND6_DRLIST 19 +#define ICMPV6CTL_ND6_PRLIST 20 +#define ICMPV6CTL_MAXID 21 #define ICMPV6CTL_NAMES { \ { 0, 0 }, \ @@ -592,6 +594,8 @@ struct icmp6stat { { "mtudisc_hiwat", CTLTYPE_INT }, \ { "mtudisc_lowat", CTLTYPE_INT }, \ { "nd6_debug", CTLTYPE_INT }, \ + { 0, 0 }, \ + { 0, 0 }, \ } #define RTF_PROBEMTU RTF_PROTO1 diff --git a/sys/netinet/in_pcb.h b/sys/netinet/in_pcb.h index 1bea173ba59..6bca529057c 100644 --- a/sys/netinet/in_pcb.h +++ b/sys/netinet/in_pcb.h @@ -1,4 +1,4 @@ -/* $OpenBSD: in_pcb.h,v 1.41 2002/05/31 02:39:25 angelos Exp $ */ +/* $OpenBSD: in_pcb.h,v 1.42 2002/06/08 21:22:02 itojun Exp $ */ /* $NetBSD: in_pcb.h,v 1.14 1996/02/13 23:42:00 christos Exp $ */ /* @@ -270,13 +270,8 @@ struct rtentry * /* INET6 stuff */ int in6_pcbnotify(struct inpcbtable *, struct sockaddr *, - u_int, struct sockaddr *, u_int, int, void *, - void (*)(struct inpcb *, int)); -struct in6_addr *in6_selectsrc(struct sockaddr_in6 *, - struct ip6_pktopts *, - struct ip6_moptions *, - struct route_in6 *, - struct in6_addr *, int *); + u_int, struct sockaddr *, u_int, int, void *, + void (*)(struct inpcb *, int)); int in6_selecthlim(struct inpcb *, struct ifnet *); int in6_pcbsetport(struct in6_addr *, struct inpcb *); #endif /* _KERNEL */ diff --git a/sys/netinet6/icmp6.c b/sys/netinet6/icmp6.c index 39e40fdf37d..686364a29aa 100644 --- a/sys/netinet6/icmp6.c +++ b/sys/netinet6/icmp6.c @@ -1,4 +1,4 @@ -/* $OpenBSD: icmp6.c,v 1.59 2002/05/31 04:27:00 itojun Exp $ */ +/* $OpenBSD: icmp6.c,v 1.60 2002/06/08 21:22:02 itojun Exp $ */ /* $KAME: icmp6.c,v 1.217 2001/06/20 15:03:29 jinmei Exp $ */ /* @@ -2987,6 +2987,9 @@ icmp6_sysctl(name, namelen, oldp, oldlenp, newp, newlen) &icmp6_mtudisc_lowat); case ICMPV6CTL_ND6_DEBUG: return sysctl_int(oldp, oldlenp, newp, newlen, &nd6_debug); + case ICMPV6CTL_ND6_DRLIST: + case ICMPV6CTL_ND6_PRLIST: + return nd6_sysctl(name[0], oldp, oldlenp, newp, newlen); default: return ENOPROTOOPT; } diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c index d2bfe420ee7..fc9ac96ce99 100644 --- a/sys/netinet6/in6.c +++ b/sys/netinet6/in6.c @@ -1,4 +1,4 @@ -/* $OpenBSD: in6.c,v 1.41 2002/06/08 00:06:58 itojun Exp $ */ +/* $OpenBSD: in6.c,v 1.42 2002/06/08 21:22:02 itojun Exp $ */ /* $KAME: in6.c,v 1.198 2001/07/18 09:12:38 itojun Exp $ */ /* @@ -117,6 +117,9 @@ const struct in6_addr in6mask128 = IN6MASK128; static int in6_lifaddr_ioctl(struct socket *, u_long, caddr_t, struct ifnet *, struct proc *); +static int in6_ifinit(struct ifnet *, struct in6_ifaddr *, + struct sockaddr_in6 *, int); +static void in6_unlink_ifa(struct in6_ifaddr *, struct ifnet *); const struct sockaddr_in6 sa6_any = {sizeof(sa6_any), AF_INET6, 0, 0, IN6ADDR_ANY_INIT, 0}; @@ -134,17 +137,6 @@ struct multi6_kludge { }; /* - * Check if the loopback entry will be automatically generated. - * if 0 returned, will not be automatically generated. - * if 1 returned, will be automatically generated. - */ -static int -in6_is_ifloop_auto(struct ifaddr *ifa) -{ - return 0; -} - -/* * Subroutine for in6_ifaddloop() and in6_ifremloop(). * This routine does actual work. */ @@ -153,27 +145,33 @@ in6_ifloop_request(int cmd, struct ifaddr *ifa) { struct sockaddr_in6 lo_sa; struct sockaddr_in6 all1_sa; - struct rtentry *nrt = NULL, **nrtp = NULL; + struct rtentry *nrt = NULL; + int e; bzero(&lo_sa, sizeof(lo_sa)); bzero(&all1_sa, sizeof(all1_sa)); - lo_sa.sin6_family = AF_INET6; - lo_sa.sin6_len = sizeof(struct sockaddr_in6); - all1_sa = lo_sa; + lo_sa.sin6_family = all1_sa.sin6_family = AF_INET6; + lo_sa.sin6_len = all1_sa.sin6_len = sizeof(struct sockaddr_in6); lo_sa.sin6_addr = in6addr_loopback; all1_sa.sin6_addr = in6mask128; /* - * So we add or remove static loopback entry, here. - * This request for deletion could fail, e.g. when we remove - * an address right after adding it. + * We specify the address itself as the gateway, and set the + * RTF_LLINFO flag, so that the corresponding host route would have + * the flag, and thus applications that assume traditional behavior + * would be happy. Note that we assume the caller of the function + * (probably implicitly) set nd6_rtrequest() to ifa->ifa_rtrequest, + * which changes the outgoing interface to the loopback interface. */ - if (cmd == RTM_ADD) - nrtp = &nrt; - rtrequest(cmd, ifa->ifa_addr, - (struct sockaddr *)&lo_sa, - (struct sockaddr *)&all1_sa, - RTF_UP|RTF_HOST, nrtp); + e = rtrequest(cmd, ifa->ifa_addr, ifa->ifa_addr, + (struct sockaddr *)&all1_sa, RTF_UP|RTF_HOST|RTF_LLINFO, &nrt); + if (e != 0) { + log(LOG_ERR, "in6_ifloop_request: " + "%s operation failed for %s (errno=%d)\n", + cmd == RTM_ADD ? "ADD" : "DELETE", + ip6_sprintf(&((struct in6_ifaddr *)ifa)->ia_addr.sin6_addr), + e); + } /* * Make sure rt_ifa be equal to IFA, the second argument of the @@ -187,28 +185,47 @@ in6_ifloop_request(int cmd, struct ifaddr *ifa) ifa->ifa_refcnt++; nrt->rt_ifa = ifa; } - if (nrt) - nrt->rt_refcnt--; + + /* + * Report the addition/removal of the address to the routing socket. + * XXX: since we called rtinit for a p2p interface with a destination, + * we end up reporting twice in such a case. Should we rather + * omit the second report? + */ + if (nrt) { + rt_newaddrmsg(cmd, ifa, e, nrt); + if (cmd == RTM_DELETE) { + if (nrt->rt_refcnt <= 0) { + /* XXX: we should free the entry ourselves. */ + nrt->rt_refcnt++; + rtfree(nrt); + } + } else { + /* the cmd must be RTM_ADD here */ + nrt->rt_refcnt--; + } + } } /* - * Add ownaddr as loopback rtentry, if necessary(ex. on p2p link). - * Because, KAME needs loopback rtentry for ownaddr check in - * ip6_input(). + * Add ownaddr as loopback rtentry. We previously add the route only if + * necessary (ex. on a p2p link). However, since we now manage addresses + * separately from prefixes, we should always add the route. We can't + * rely on the cloning mechanism from the corresponding interface route + * any more. */ static void in6_ifaddloop(struct ifaddr *ifa) { - if (!in6_is_ifloop_auto(ifa)) { - struct rtentry *rt; - - /* If there is no loopback entry, allocate one. */ - rt = rtalloc1(ifa->ifa_addr, 0); - if (rt == 0 || (rt->rt_ifp->if_flags & IFF_LOOPBACK) == 0) - in6_ifloop_request(RTM_ADD, ifa); - if (rt) - rt->rt_refcnt--; - } + struct rtentry *rt; + + /* If there is no loopback entry, allocate one. */ + rt = rtalloc1(ifa->ifa_addr, 0); + if (rt == NULL || (rt->rt_flags & RTF_HOST) == 0 || + (rt->rt_ifp->if_flags & IFF_LOOPBACK) == 0) + in6_ifloop_request(RTM_ADD, ifa); + if (rt) + rt->rt_refcnt--; } /* @@ -218,21 +235,48 @@ in6_ifaddloop(struct ifaddr *ifa) static void in6_ifremloop(struct ifaddr *ifa) { - if (!in6_is_ifloop_auto(ifa)) { - struct in6_ifaddr *ia; - int ia_count = 0; - - /* If only one ifa for the loopback entry, delete it. */ - for (ia = in6_ifaddr; ia; ia = ia->ia_next) { - if (IN6_ARE_ADDR_EQUAL(IFA_IN6(ifa), - &ia->ia_addr.sin6_addr)) { - ia_count++; - if (ia_count > 1) - break; - } + struct in6_ifaddr *ia; + struct rtentry *rt; + int ia_count = 0; + + /* + * Some of BSD variants do not remove cloned routes + * from an interface direct route, when removing the direct route + * (see comments in net/net_osdep.h). Even for variants that do remove + * cloned routes, they could fail to remove the cloned routes when + * we handle multple addresses that share a common prefix. + * So, we should remove the route corresponding to the deleted address. + */ + + /* + * Delete the entry only if exact one ifa exists. More than one ifa + * can exist if we assign a same single address to multiple + * (probably p2p) interfaces. + * XXX: we should avoid such a configuration in IPv6... + */ + for (ia = in6_ifaddr; ia; ia = ia->ia_next) { + if (IN6_ARE_ADDR_EQUAL(IFA_IN6(ifa), &ia->ia_addr.sin6_addr)) { + ia_count++; + if (ia_count > 1) + break; } - if (ia_count == 1) + } + + if (ia_count == 1) { + /* + * Before deleting, check if a corresponding loopbacked host + * route surely exists. With this check, we can avoid to + * delete an interface direct route whose destination is same + * as the address being removed. This can happen when removing + * a subnet-router anycast address on an interface attahced + * to a shared medium. + */ + rt = rtalloc1(ifa->ifa_addr, 0); + if (rt != NULL && (rt->rt_flags & RTF_HOST) != 0 && + (rt->rt_ifp->if_flags & IFF_LOOPBACK) != 0) { + rt->rt_refcnt--; in6_ifloop_request(RTM_DELETE, ifa); + } } } @@ -261,22 +305,40 @@ in6_ifindex2scopeid(idx) } int -in6_mask2len(mask) +in6_mask2len(mask, lim0) struct in6_addr *mask; + u_char *lim0; { - int x, y; - - for (x = 0; x < sizeof(*mask); x++) { - if (mask->s6_addr8[x] != 0xff) + int x = 0, y; + u_char *lim = lim0, *p; + + /* ignore the scope_id part */ + if (lim0 == NULL || lim0 - (u_char *)mask > sizeof(*mask)) + lim = (u_char *)mask + sizeof(*mask); + for (p = (u_char *)mask; p < lim; x++, p++) { + if (*p != 0xff) break; } y = 0; - if (x < sizeof(*mask)) { + if (p < lim) { for (y = 0; y < 8; y++) { - if ((mask->s6_addr8[x] & (0x80 >> y)) == 0) + if ((*p & (0x80 >> y)) == 0) break; } } + + /* + * when the limit pointer is given, do a stricter check on the + * remaining bits. + */ + if (p < lim) { + if (y != 0 && (*p & (0x00ff >> y)) != 0) + return(-1); + for (p = p + 1; p < lim; p++) + if (*p != 0) + return(-1); + } + return x * 8 + y; } @@ -292,12 +354,9 @@ in6_control(so, cmd, data, ifp, p) struct proc *p; { struct in6_ifreq *ifr = (struct in6_ifreq *)data; - struct in6_ifaddr *ia, *oia; + struct in6_ifaddr *ia = NULL; struct in6_aliasreq *ifra = (struct in6_aliasreq *)data; struct sockaddr_in6 *sa6; - struct sockaddr_in6 oldaddr; - int error = 0, hostIsNew, prefixIsNew; - int newifaddr; time_t time_second = (time_t)time.tv_sec; int privileged; @@ -311,7 +370,7 @@ in6_control(so, cmd, data, ifp, p) return (mrt6_ioctl(cmd, data)); } - if (ifp == 0) + if (ifp == NULL) return(EOPNOTSUPP); switch (cmd) { @@ -447,56 +506,7 @@ in6_control(so, cmd, data, ifp, p) return(EAFNOSUPPORT); if (!privileged) return(EPERM); - if (ia == NULL) { - ia = (struct in6_ifaddr *) - malloc(sizeof(*ia), M_IFADDR, M_WAITOK); - 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); - if (ifp->if_flags & IFF_POINTOPOINT) { - ia->ia_ifa.ifa_dstaddr - = (struct sockaddr *)&ia->ia_dstaddr; - ia->ia_dstaddr.sin6_family = AF_INET6; - ia->ia_dstaddr.sin6_len = sizeof(ia->ia_dstaddr); - } else { - ia->ia_ifa.ifa_dstaddr = NULL; - bzero(&ia->ia_dstaddr, sizeof(ia->ia_dstaddr)); - } - ia->ia_ifa.ifa_netmask - = (struct sockaddr *)&ia->ia_prefixmask; - ia->ia_ifp = ifp; - if ((oia = in6_ifaddr) != NULL) { - for ( ; oia->ia_next; oia = oia->ia_next) - continue; - oia->ia_next = ia; - } else - in6_ifaddr = ia; - ia->ia_ifa.ifa_refcnt++; - - TAILQ_INSERT_TAIL(&ifp->if_addrlist, - (struct ifaddr *)ia, ifa_list); - ia->ia_ifa.ifa_refcnt++; - - newifaddr = 1; - } else - newifaddr = 0; - - if (cmd == SIOCAIFADDR_IN6) { - /* sanity for overflow - beware unsigned */ - struct in6_addrlifetime *lt; - lt = &ifra->ifra_lifetime; - if (lt->ia6t_vltime != ND6_INFINITE_LIFETIME - && lt->ia6t_vltime + time_second < time_second) { - return EINVAL; - } - if (lt->ia6t_pltime != ND6_INFINITE_LIFETIME - && lt->ia6t_pltime + time_second < time_second) { - return EINVAL; - } - } break; case SIOCGIFADDR_IN6: @@ -541,6 +551,10 @@ in6_control(so, cmd, data, ifp, p) case SIOCGIFDSTADDR_IN6: if ((ifp->if_flags & IFF_POINTOPOINT) == 0) return(EINVAL); + /* + * XXX: should we check if ifa_dstaddr is NULL and return + * an error? + */ ifr->ifr_dstaddr = ia->ia_dstaddr; break; @@ -572,6 +586,42 @@ in6_control(so, cmd, data, ifp, p) case SIOCGIFALIFETIME_IN6: ifr->ifr_ifru.ifru_lifetime = ia->ia6_lifetime; + if (ia->ia6_lifetime.ia6t_vltime != ND6_INFINITE_LIFETIME) { + time_t maxexpire; + struct in6_addrlifetime *retlt = + &ifr->ifr_ifru.ifru_lifetime; + + /* + * XXX: adjust expiration time assuming time_t is + * signed. + */ + maxexpire = (-1) & + ~(1 << ((sizeof(maxexpire) * 8) - 1)); + if (ia->ia6_lifetime.ia6t_vltime < + maxexpire - ia->ia6_updatetime) { + retlt->ia6t_expire = ia->ia6_updatetime + + ia->ia6_lifetime.ia6t_vltime; + } else + retlt->ia6t_expire = maxexpire; + } + if (ia->ia6_lifetime.ia6t_pltime != ND6_INFINITE_LIFETIME) { + time_t maxexpire; + struct in6_addrlifetime *retlt = + &ifr->ifr_ifru.ifru_lifetime; + + /* + * XXX: adjust expiration time assuming time_t is + * signed. + */ + maxexpire = (-1) & + ~(1 << ((sizeof(maxexpire) * 8) - 1)); + if (ia->ia6_lifetime.ia6t_pltime < + maxexpire - ia->ia6_updatetime) { + retlt->ia6t_preferred = ia->ia6_updatetime + + ia->ia6_lifetime.ia6t_pltime; + } else + retlt->ia6t_preferred = maxexpire; + } break; case SIOCSIFALIFETIME_IN6: @@ -590,170 +640,655 @@ in6_control(so, cmd, data, ifp, p) break; case SIOCAIFADDR_IN6: - prefixIsNew = 0; - hostIsNew = 1; + { + int i, error = 0; + struct nd_prefix pr0, *pr; - if (ifra->ifra_addr.sin6_len == 0) { - ifra->ifra_addr = ia->ia_addr; - hostIsNew = 0; - } else if (IN6_ARE_ADDR_EQUAL(&ifra->ifra_addr.sin6_addr, - &ia->ia_addr.sin6_addr)) - hostIsNew = 0; + /* + * first, make or update the interface address structure, + * and link it to the list. + */ + if ((error = in6_update_ifa(ifp, ifra, ia)) != 0) + return(error); + if ((ia = in6ifa_ifpwithaddr(ifp, &ifra->ifra_addr.sin6_addr)) + == NULL) { + /* + * this can happen when the user specify the 0 valid + * lifetime. + */ + break; + } - /* Validate address families: */ /* - * The destination address for a p2p link must have a family - * of AF_UNSPEC or AF_INET6. + * then, make the prefix on-link on the interface. + * XXX: we'd rather create the prefix before the address, but + * we need at least one address to install the corresponding + * interface route, so we configure the address first. */ - 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. + * convert mask to prefix length (prefixmask has already + * been validated in in6_update_ifa(). */ - if (ifra->ifra_prefixmask.sin6_family != AF_INET6 && - ifra->ifra_prefixmask.sin6_family != AF_UNSPEC) - return(EAFNOSUPPORT); + bzero(&pr0, sizeof(pr0)); + pr0.ndpr_ifp = ifp; + pr0.ndpr_plen = in6_mask2len(&ifra->ifra_prefixmask.sin6_addr, + NULL); + if (pr0.ndpr_plen == 128) { + break; /* we don't need to install a host route. */ + } + pr0.ndpr_prefix = ifra->ifra_addr; + pr0.ndpr_mask = ifra->ifra_prefixmask.sin6_addr; + /* apply the mask for safety. */ + for (i = 0; i < 4; i++) { + pr0.ndpr_prefix.sin6_addr.s6_addr32[i] &= + ifra->ifra_prefixmask.sin6_addr.s6_addr32[i]; + } + /* + * XXX: since we don't have an API to set prefix (not address) + * lifetimes, we just use the same lifetimes as addresses. + * The (temporarily) installed lifetimes can be overridden by + * later advertised RAs (when accept_rtadv is non 0), which is + * an intended behavior. + */ + pr0.ndpr_raf_onlink = 1; /* should be configurable? */ + pr0.ndpr_raf_auto = + ((ifra->ifra_flags & IN6_IFF_AUTOCONF) != 0); + pr0.ndpr_vltime = ifra->ifra_lifetime.ia6t_vltime; + pr0.ndpr_pltime = ifra->ifra_lifetime.ia6t_pltime; + + /* add the prefix if there's one. */ + if ((pr = nd6_prefix_lookup(&pr0)) == NULL) { + /* + * nd6_prelist_add will install the corresponding + * interface route. + */ + if ((error = nd6_prelist_add(&pr0, NULL, &pr)) != 0) + return(error); + if (pr == NULL) { + log(LOG_ERR, "nd6_prelist_add succeeded but " + "no prefix\n"); + return(EINVAL); /* XXX panic here? */ + } + } + if ((ia->ia6_flags & IN6_IFF_AUTOCONF) && + ia->ia6_ndpr == NULL) { /* new autoconfed addr */ + ia->ia6_ndpr = pr; + pr->ndpr_refcnt++; + } - if (ifra->ifra_prefixmask.sin6_len) { - in6_ifscrub(ifp, ia); - ia->ia_prefixmask = ifra->ifra_prefixmask; - prefixIsNew = 1; + /* + * this might affect the status of autoconfigured addresses, + * that is, this address might make other addresses detached. + */ + pfxlist_onlink_check(); + + break; + } + + case SIOCDIFADDR_IN6: + { + int i = 0, purgeprefix = 0; + struct nd_prefix pr0, *pr = NULL; + + /* + * If the address being deleted is the only one that owns + * the corresponding prefix, expire the prefix as well. + * XXX: theoretically, we don't have to worry about such + * relationship, since we separate the address management + * and the prefix management. We do this, however, to provide + * as much backward compatibility as possible in terms of + * the ioctl operation. + */ + bzero(&pr0, sizeof(pr0)); + pr0.ndpr_ifp = ifp; + pr0.ndpr_plen = in6_mask2len(&ia->ia_prefixmask.sin6_addr, + NULL); + if (pr0.ndpr_plen == 128) + goto purgeaddr; + pr0.ndpr_prefix = ia->ia_addr; + pr0.ndpr_mask = ia->ia_prefixmask.sin6_addr; + for (i = 0; i < 4; i++) { + pr0.ndpr_prefix.sin6_addr.s6_addr32[i] &= + ia->ia_prefixmask.sin6_addr.s6_addr32[i]; } - if ((ifp->if_flags & IFF_POINTOPOINT) && - (ifra->ifra_dstaddr.sin6_family == AF_INET6)) { - in6_ifscrub(ifp, ia); - oldaddr = ia->ia_dstaddr; - ia->ia_dstaddr = ifra->ifra_dstaddr; - /* link-local index check: should be a separate function? */ - if (IN6_IS_ADDR_LINKLOCAL(&ia->ia_dstaddr.sin6_addr)) { - if (ia->ia_dstaddr.sin6_addr.s6_addr16[1] == 0) { - /* - * interface ID is not embedded by - * the user - */ - ia->ia_dstaddr.sin6_addr.s6_addr16[1] - = htons(ifp->if_index); - } else if (ia->ia_dstaddr.sin6_addr.s6_addr16[1] != - htons(ifp->if_index)) { - ia->ia_dstaddr = oldaddr; - return(EINVAL); /* ifid contradicts */ - } + /* + * The logic of the following condition is a bit complicated. + * We expire the prefix when + * 1. the address obeys autoconfiguration and it is the + * only owner of the associated prefix, or + * 2. the address does not obey autoconf and there is no + * other owner of the prefix. + */ + if ((pr = nd6_prefix_lookup(&pr0)) != NULL && + (((ia->ia6_flags & IN6_IFF_AUTOCONF) != 0 && + pr->ndpr_refcnt == 1) || + ((ia->ia6_flags & IN6_IFF_AUTOCONF) == 0 && + pr->ndpr_refcnt == 0))) + purgeprefix = 1; + + purgeaddr: + in6_purgeaddr(&ia->ia_ifa); + if (pr && purgeprefix) + prelist_remove(pr); + break; + } + + default: + if (ifp == NULL || ifp->if_ioctl == 0) + return(EOPNOTSUPP); + return((*ifp->if_ioctl)(ifp, cmd, data)); + } + + return(0); +} + +/* + * Update parameters of an IPv6 interface address. + * If necessary, a new entry is created and linked into address chains. + * This function is separated from in6_control(). + * XXX: should this be performed under splnet()? + */ +int +in6_update_ifa(ifp, ifra, ia) + struct ifnet *ifp; + struct in6_aliasreq *ifra; + struct in6_ifaddr *ia; +{ + int error = 0, hostIsNew = 0, plen = -1; + struct in6_ifaddr *oia; + struct sockaddr_in6 dst6; + struct in6_addrlifetime *lt; + struct in6_multi_mship *imm; + time_t time_second = (time_t)time.tv_sec; + struct rtentry *rt; + + /* Validate parameters */ + if (ifp == NULL || ifra == NULL) /* this maybe redundant */ + return(EINVAL); + + /* + * 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); + /* + * validate ifra_prefixmask. don't check sin6_family, netmask + * does not carry fields other than sin6_len. + */ + if (ifra->ifra_prefixmask.sin6_len > sizeof(struct sockaddr_in6)) + return(EINVAL); + /* + * Because the IPv6 address architecture is classless, we require + * users to specify a (non 0) prefix length (mask) for a new address. + * We also require the prefix (when specified) mask is valid, and thus + * reject a non-consecutive mask. + */ + if (ia == NULL && ifra->ifra_prefixmask.sin6_len == 0) + return(EINVAL); + if (ifra->ifra_prefixmask.sin6_len != 0) { + plen = in6_mask2len(&ifra->ifra_prefixmask.sin6_addr, + (u_char *)&ifra->ifra_prefixmask + + ifra->ifra_prefixmask.sin6_len); + if (plen <= 0) + return(EINVAL); + } else { + /* + * In this case, ia must not be NULL. We just use its prefix + * length. + */ + plen = in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL); + } + /* + * If the destination address on a p2p interface is specified, + * and the address is a scoped one, validate/set the scope + * zone identifier. + */ + dst6 = ifra->ifra_dstaddr; + if ((ifp->if_flags & (IFF_POINTOPOINT|IFF_LOOPBACK)) != 0 && + (dst6.sin6_family == AF_INET6)) { + /* link-local index check: should be a separate function? */ + if (IN6_IS_ADDR_LINKLOCAL(&dst6.sin6_addr)) { + if (dst6.sin6_addr.s6_addr16[1] == 0) { + /* + * interface ID is not embedded by + * the user + */ + dst6.sin6_addr.s6_addr16[1] = + htons(ifp->if_index); + } else if (dst6.sin6_addr.s6_addr16[1] != + htons(ifp->if_index)) { + return(EINVAL); /* ifid contradicts */ } - prefixIsNew = 1; /* We lie; but effect's the same */ } - if (hostIsNew || prefixIsNew) { - error = in6_ifinit(ifp, ia, &ifra->ifra_addr, 0); -#if 0 - if (error) - goto undo; + } + /* + * The destination address can be specified only for a p2p or a + * loopback interface. If specified, the corresponding prefix length + * must be 128. + */ + if (ifra->ifra_dstaddr.sin6_family == AF_INET6) { +#ifdef FORCE_P2PPLEN + int i; #endif - } - if (hostIsNew && (ifp->if_flags & IFF_MULTICAST)) { - int error_local = 0; + if ((ifp->if_flags & (IFF_POINTOPOINT|IFF_LOOPBACK)) == 0) { + /* XXX: noisy message */ + log(LOG_INFO, "in6_update_ifa: a destination can be " + "specified for a p2p or a loopback IF only\n"); + return(EINVAL); + } + if (plen != 128) { + log(LOG_INFO, "in6_update_ifa: prefixlen should be " + "128 when dstaddr is specified\n"); +#ifdef FORCE_P2PPLEN /* - * join solicited multicast addr for new host id + * To be compatible with old configurations, + * such as ifconfig gif0 inet6 2001::1 2001::2 + * prefixlen 126, we override the specified + * prefixmask as if the prefix length was 128. */ - struct in6_addr llsol; - bzero(&llsol, sizeof(struct in6_addr)); - llsol.s6_addr16[0] = htons(0xff02); - llsol.s6_addr16[1] = htons(ifp->if_index); - llsol.s6_addr32[1] = 0; - llsol.s6_addr32[2] = htonl(1); - llsol.s6_addr32[3] = - ifra->ifra_addr.sin6_addr.s6_addr32[3]; - llsol.s6_addr8[12] = 0xff; - (void)in6_addmulti(&llsol, ifp, &error_local); - if (error == 0) - error = error_local; + ifra->ifra_prefixmask.sin6_len = + sizeof(struct sockaddr_in6); + for (i = 0; i < 4; i++) + ifra->ifra_prefixmask.sin6_addr.s6_addr32[i] = + 0xffffffff; + plen = 128; +#else + return(EINVAL); +#endif } + } + /* lifetime consistency check */ + lt = &ifra->ifra_lifetime; + if (lt->ia6t_pltime > lt->ia6t_vltime) + return(EINVAL); + if (lt->ia6t_vltime == 0) { + /* + * the following log might be noisy, but this is a typical + * configuration mistake or a tool's bug. + */ + log(LOG_INFO, + "in6_update_ifa: valid lifetime is 0 for %s\n", + ip6_sprintf(&ifra->ifra_addr.sin6_addr)); - ia->ia6_flags = ifra->ifra_flags; - ia->ia6_flags &= ~IN6_IFF_DUPLICATED; /*safety*/ + if (ia == NULL) + return(0); /* there's nothing to do */ + } - ia->ia6_lifetime = ifra->ifra_lifetime; - /* for sanity */ - if (ia->ia6_lifetime.ia6t_vltime != ND6_INFINITE_LIFETIME) { - ia->ia6_lifetime.ia6t_expire = - time_second + ia->ia6_lifetime.ia6t_vltime; - } else - ia->ia6_lifetime.ia6t_expire = 0; - if (ia->ia6_lifetime.ia6t_pltime != ND6_INFINITE_LIFETIME) { - ia->ia6_lifetime.ia6t_preferred = - time_second + ia->ia6_lifetime.ia6t_pltime; + /* + * If this is a new address, allocate a new ifaddr and link it + * into chains. + */ + if (ia == NULL) { + hostIsNew = 1; + /* + * When in6_update_ifa() is called in a process of a received + * RA, it is called under an interrupt context. So, we should + * call malloc with M_NOWAIT. + */ + ia = (struct in6_ifaddr *) malloc(sizeof(*ia), M_IFADDR, + M_NOWAIT); + if (ia == NULL) + return (ENOBUFS); + bzero((caddr_t)ia, sizeof(*ia)); + LIST_INIT(&ia->ia6_memberships); + /* Initialize the address and masks, and put time stamp */ + 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->ia6_createtime = ia->ia6_updatetime = time_second; + if ((ifp->if_flags & (IFF_POINTOPOINT | IFF_LOOPBACK)) != 0) { + /* + * XXX: some functions expect that ifa_dstaddr is not + * NULL for p2p interfaces. + */ + ia->ia_ifa.ifa_dstaddr = + (struct sockaddr *)&ia->ia_dstaddr; + } else { + ia->ia_ifa.ifa_dstaddr = NULL; + } + ia->ia_ifa.ifa_netmask = + (struct sockaddr *)&ia->ia_prefixmask; + + ia->ia_ifp = ifp; + if ((oia = in6_ifaddr) != NULL) { + for ( ; oia->ia_next; oia = oia->ia_next) + continue; + oia->ia_next = ia; } else - ia->ia6_lifetime.ia6t_preferred = 0; + in6_ifaddr = ia; + TAILQ_INSERT_TAIL(&ifp->if_addrlist, &ia->ia_ifa, + ifa_list); + } + /* set prefix mask */ + if (ifra->ifra_prefixmask.sin6_len) { /* - * Perform DAD, if needed. + * We prohibit changing the prefix length of an existing + * address, because + * + such an operation should be rare in IPv6, and + * + the operation would confuse prefix management. */ - if (in6if_do_dad(ifp)) { - ia->ia6_flags |= IN6_IFF_TENTATIVE; - nd6_dad_start((struct ifaddr *)ia, NULL); + if (ia->ia_prefixmask.sin6_len && + in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL) != plen) { + log(LOG_INFO, "in6_update_ifa: the prefix length of an" + " existing (%s) address should not be changed\n", + ip6_sprintf(&ia->ia_addr.sin6_addr)); + error = EINVAL; + goto unlink; } + ia->ia_prefixmask = ifra->ifra_prefixmask; + } + + /* + * If a new destination address is specified, scrub the old one and + * install the new destination. Note that the interface must be + * p2p or loopback (see the check above.) + */ + if (dst6.sin6_family == AF_INET6 && + !IN6_ARE_ADDR_EQUAL(&dst6.sin6_addr, &ia->ia_dstaddr.sin6_addr)) { + int e; + + if ((ia->ia_flags & IFA_ROUTE) != 0 && + (e = rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST)) != 0) { + log(LOG_ERR, "in6_update_ifa: failed to remove " + "a route to the old destination: %s\n", + ip6_sprintf(&ia->ia_addr.sin6_addr)); + /* proceed anyway... */ + } else + ia->ia_flags &= ~IFA_ROUTE; + ia->ia_dstaddr = dst6; + } + + /* + * Set lifetimes. We do not refer to ia6t_expire and ia6t_preferred + * to see if the address is deprecated or invalidated, but initialize + * these members for applications. + */ + ia->ia6_lifetime = ifra->ifra_lifetime; + if (ia->ia6_lifetime.ia6t_vltime != ND6_INFINITE_LIFETIME) { + ia->ia6_lifetime.ia6t_expire = + time_second + ia->ia6_lifetime.ia6t_vltime; + } else + ia->ia6_lifetime.ia6t_expire = 0; + if (ia->ia6_lifetime.ia6t_pltime != ND6_INFINITE_LIFETIME) { + ia->ia6_lifetime.ia6t_preferred = + time_second + ia->ia6_lifetime.ia6t_pltime; + } else + ia->ia6_lifetime.ia6t_preferred = 0; + + /* reset the interface and routing table appropriately. */ + if ((error = in6_ifinit(ifp, ia, &ifra->ifra_addr, hostIsNew)) != 0) + goto unlink; + + /* + * Make the address tentative before joining multicast addresses, + * so that corresponding MLD responses would not have a tentative + * source address. + */ + ia->ia6_flags = ifra->ifra_flags; + ia->ia6_flags &= ~IN6_IFF_DUPLICATED; /* safety */ + if (hostIsNew && in6if_do_dad(ifp)) + ia->ia6_flags |= IN6_IFF_TENTATIVE; + + /* + * Beyond this point, we should call in6_purgeaddr upon an error, + * not just go to unlink. + */ + + if ((ifp->if_flags & IFF_MULTICAST) != 0) { + struct sockaddr_in6 mltaddr, mltmask; +#ifndef SCOPEDROUTING + u_int32_t zoneid = 0; +#endif if (hostIsNew) { - int iilen; - int error_local = 0; - - iilen = (sizeof(ia->ia_prefixmask.sin6_addr) << 3) - - in6_mask2len(&ia->ia_prefixmask.sin6_addr); - error_local = in6_prefix_add_ifid(iilen, ia); - if (error == 0) - error = error_local; + /* join solicited multicast addr for new host id */ + struct sockaddr_in6 llsol; + + bzero(&llsol, sizeof(llsol)); + llsol.sin6_family = AF_INET6; + llsol.sin6_len = sizeof(llsol); + llsol.sin6_addr.s6_addr16[0] = htons(0xff02); + llsol.sin6_addr.s6_addr16[1] = htons(ifp->if_index); + llsol.sin6_addr.s6_addr32[1] = 0; + llsol.sin6_addr.s6_addr32[2] = htonl(1); + llsol.sin6_addr.s6_addr32[3] = + ifra->ifra_addr.sin6_addr.s6_addr32[3]; + llsol.sin6_addr.s6_addr8[12] = 0xff; + imm = in6_joingroup(ifp, &llsol.sin6_addr, &error); + if (imm) { + LIST_INSERT_HEAD(&ia->ia6_memberships, imm, + i6mm_chain); + } else { + log(LOG_ERR, "in6_update_ifa: addmulti " + "failed for %s on %s (errno=%d)\n", + ip6_sprintf(&llsol.sin6_addr), + ifp->if_xname, error); + goto cleanup; + } } - return(error); + bzero(&mltmask, sizeof(mltmask)); + mltmask.sin6_len = sizeof(struct sockaddr_in6); + mltmask.sin6_family = AF_INET6; + mltmask.sin6_addr = in6mask32; - case SIOCDIFADDR_IN6: - in6_purgeaddr(&ia->ia_ifa, ifp); - break; + /* + * join link-local all-nodes address + */ + bzero(&mltaddr, sizeof(mltaddr)); + mltaddr.sin6_len = sizeof(struct sockaddr_in6); + mltaddr.sin6_family = AF_INET6; + mltaddr.sin6_addr = in6addr_linklocal_allnodes; + mltaddr.sin6_addr.s6_addr16[1] = htons(ifp->if_index); - default: - if (ifp == NULL || ifp->if_ioctl == 0) - return(EOPNOTSUPP); - return((*ifp->if_ioctl)(ifp, cmd, data)); + /* + * XXX: do we really need this automatic routes? + * We should probably reconsider this stuff. Most applications + * actually do not need the routes, since they usually specify + * the outgoing interface. + */ + rt = rtalloc1((struct sockaddr *)&mltaddr, 0); + if (rt) { + /* + * 32bit came from "mltmask" + * XXX: only works in !SCOPEDROUTING case. + */ + if (memcmp(&mltaddr.sin6_addr, + &((struct sockaddr_in6 *)rt_key(rt))->sin6_addr, + 32 / 8)) { + RTFREE(rt); + rt = NULL; + } + } + if (!rt) { + struct rt_addrinfo info; + + bzero(&info, sizeof(info)); + info.rti_info[RTAX_DST] = (struct sockaddr *)&mltaddr; + info.rti_info[RTAX_GATEWAY] = + (struct sockaddr *)&ia->ia_addr; + info.rti_info[RTAX_NETMASK] = + (struct sockaddr *)&mltmask; + info.rti_info[RTAX_IFA] = + (struct sockaddr *)&ia->ia_addr; + /* XXX: we need RTF_CLONING to fake nd6_rtrequest */ + info.rti_flags = RTF_UP | RTF_CLONING; + error = rtrequest1(RTM_ADD, &info, NULL); + if (error) + goto cleanup; + } else { + RTFREE(rt); + } +#ifndef SCOPEDROUTING + mltaddr.sin6_scope_id = zoneid; /* XXX */ +#endif + imm = in6_joingroup(ifp, &mltaddr.sin6_addr, &error); + if (imm) { + LIST_INSERT_HEAD(&ia->ia6_memberships, imm, + i6mm_chain); + } else { + log(LOG_WARNING, + "in6_update_ifa: addmulti failed for " + "%s on %s (errno=%d)\n", + ip6_sprintf(&mltaddr.sin6_addr), + ifp->if_xname, error); + goto cleanup; + } + + /* + * join node information group address + */ + if (in6_nigroup(ifp, hostname, hostnamelen, &mltaddr) == 0) { + imm = in6_joingroup(ifp, &mltaddr.sin6_addr, &error); + if (imm) { + LIST_INSERT_HEAD(&ia->ia6_memberships, imm, + i6mm_chain); + } else { + log(LOG_WARNING, "in6_update_ifa: " + "addmulti failed for %s on %s (errno=%d)\n", + ip6_sprintf(&mltaddr.sin6_addr), + ifp->if_xname, error); + /* XXX not very fatal, go on... */ + } + } + + if (ifp->if_flags & IFF_LOOPBACK) { + /* + * join node-local all-nodes address, on loopback. + * (ff01::1%ifN, and ff01::%ifN/32) + */ + mltaddr.sin6_addr = in6addr_nodelocal_allnodes; + + /* XXX: again, do we really need the route? */ + rt = rtalloc1((struct sockaddr *)&mltaddr, 0); + if (rt) { + /* 32bit came from "mltmask" */ + if (memcmp(&mltaddr.sin6_addr, + &((struct sockaddr_in6 *)rt_key(rt))->sin6_addr, + 32 / 8)) { + RTFREE(rt); + rt = NULL; + } + } + if (!rt) { + struct rt_addrinfo info; + + bzero(&info, sizeof(info)); + info.rti_info[RTAX_DST] = (struct sockaddr *)&mltaddr; + info.rti_info[RTAX_GATEWAY] = + (struct sockaddr *)&ia->ia_addr; + info.rti_info[RTAX_NETMASK] = + (struct sockaddr *)&mltmask; + info.rti_info[RTAX_IFA] = + (struct sockaddr *)&ia->ia_addr; + info.rti_flags = RTF_UP | RTF_CLONING; + error = rtrequest1(RTM_ADD, &info, NULL); + if (error) + goto cleanup; + } else { + RTFREE(rt); + } + imm = in6_joingroup(ifp, &mltaddr.sin6_addr, &error); + if (imm) { + LIST_INSERT_HEAD(&ia->ia6_memberships, imm, + i6mm_chain); + } else { + log(LOG_WARNING, "in6_update_ifa: " + "addmulti failed for %s on %s " + "(errno=%d)\n", + ip6_sprintf(&mltaddr.sin6_addr), + ifp->if_xname, error); + goto cleanup; + } + } } - return(0); + /* + * Perform DAD, if needed. + * XXX It may be of use, if we can administratively + * disable DAD. + */ + if (hostIsNew && in6if_do_dad(ifp) && + (ifra->ifra_flags & IN6_IFF_NODAD) == 0) + { + nd6_dad_start((struct ifaddr *)ia, NULL); + } + + return(error); + + unlink: + /* + * XXX: if a change of an existing address failed, keep the entry + * anyway. + */ + if (hostIsNew) + in6_unlink_ifa(ia, ifp); + return(error); + + cleanup: + in6_purgeaddr(&ia->ia_ifa); + return error; } void -in6_purgeaddr(ifa, ifp) +in6_purgeaddr(ifa) struct ifaddr *ifa; - struct ifnet *ifp; { - struct in6_ifaddr *oia, *ia = (void *) ifa; + struct ifnet *ifp = ifa->ifa_ifp; + struct in6_ifaddr *ia = (struct in6_ifaddr *) ifa; + struct in6_multi_mship *imm; /* stop DAD processing */ nd6_dad_stop(ifa); - in6_ifscrub(ifp, ia); + /* + * delete route to the destination of the address being purged. + * The interface must be p2p or loopback in this case. + */ + if ((ia->ia_flags & IFA_ROUTE) != 0 && ia->ia_dstaddr.sin6_len != 0) { + int e; + + if ((e = rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST)) + != 0) { + log(LOG_ERR, "in6_purgeaddr: failed to remove " + "a route to the p2p destination: %s on %s, " + "errno=%d\n", + ip6_sprintf(&ia->ia_addr.sin6_addr), ifp->if_xname, + e); + /* proceed anyway... */ + } else + ia->ia_flags &= ~IFA_ROUTE; + } - if (ifp->if_flags & IFF_MULTICAST) { - /* - * delete solicited multicast addr for deleting host id - */ - struct in6_multi *in6m; - struct in6_addr llsol; - bzero(&llsol, sizeof(struct in6_addr)); - llsol.s6_addr16[0] = htons(0xff02); - llsol.s6_addr16[1] = htons(ifp->if_index); - llsol.s6_addr32[1] = 0; - llsol.s6_addr32[2] = htonl(1); - llsol.s6_addr32[3] = - ia->ia_addr.sin6_addr.s6_addr32[3]; - llsol.s6_addr8[12] = 0xff; - - IN6_LOOKUP_MULTI(llsol, ifp, in6m); - if (in6m) - in6_delmulti(in6m); + /* Remove ownaddr's loopback rtentry, if it exists. */ + in6_ifremloop(&(ia->ia_ifa)); + + /* + * leave from multicast groups we have joined for the interface + */ + while ((imm = ia->ia6_memberships.lh_first) != NULL) { + LIST_REMOVE(imm, i6mm_chain); + in6_leavegroup(imm); } + in6_unlink_ifa(ia, ifp); +} + +static void +in6_unlink_ifa(ia, ifp) + struct in6_ifaddr *ia; + struct ifnet *ifp; +{ + struct in6_ifaddr *oia; + int s = splnet(); + TAILQ_REMOVE(&ifp->if_addrlist, &ia->ia_ifa, ifa_list); - IFAFREE(&ia->ia_ifa); oia = ia; if (oia == (ia = in6_ifaddr)) @@ -763,20 +1298,59 @@ in6_purgeaddr(ifa, ifp) ia = ia->ia_next; if (ia->ia_next) ia->ia_next = oia->ia_next; - else - printf("Didn't unlink in6_ifaddr from list\n"); + else { + /* search failed */ + printf("Couldn't unlink in6_ifaddr from in6_ifaddr\n"); + } } - { - int iilen; - iilen = (sizeof(oia->ia_prefixmask.sin6_addr) << 3) - - in6_mask2len(&oia->ia_prefixmask.sin6_addr); - in6_prefix_remove_ifid(iilen, oia); - } - if (oia->ia6_multiaddrs.lh_first != NULL) + if (oia->ia6_multiaddrs.lh_first != NULL) { in6_savemkludge(oia); + } + + /* + * When an autoconfigured address is being removed, release the + * reference to the base prefix. Also, since the release might + * affect the status of other (detached) addresses, call + * pfxlist_onlink_check(). + */ + if ((oia->ia6_flags & IN6_IFF_AUTOCONF) != 0) { + if (oia->ia6_ndpr == NULL) { + log(LOG_NOTICE, "in6_unlink_ifa: autoconf'ed address " + "%p has no prefix\n", oia); + } else { + oia->ia6_ndpr->ndpr_refcnt--; + oia->ia6_flags &= ~IN6_IFF_AUTOCONF; + oia->ia6_ndpr = NULL; + } + pfxlist_onlink_check(); + } + + /* + * release another refcnt for the link from in6_ifaddr. + * Note that we should decrement the refcnt at least once for all *BSD. + */ IFAFREE(&oia->ia_ifa); + + splx(s); +} + +void +in6_purgeif(ifp) + struct ifnet *ifp; +{ + struct ifaddr *ifa, *nifa; + + for (ifa = TAILQ_FIRST(&ifp->if_addrlist); ifa != NULL; ifa = nifa) + { + nifa = TAILQ_NEXT(ifa, ifa_list); + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + in6_purgeaddr(ifa); + } + + in6_ifdetach(ifp); } /* @@ -888,37 +1462,34 @@ in6_lifaddr_ioctl(so, cmd, data, ifp, p) /* copy args to in6_aliasreq, perform ioctl(SIOCAIFADDR_IN6). */ bzero(&ifra, sizeof(ifra)); - bcopy(iflr->iflr_name, ifra.ifra_name, - sizeof(ifra.ifra_name)); + bcopy(iflr->iflr_name, ifra.ifra_name, sizeof(ifra.ifra_name)); bcopy(&iflr->addr, &ifra.ifra_addr, - ((struct sockaddr *)&iflr->addr)->sa_len); + ((struct sockaddr *)&iflr->addr)->sa_len); if (hostid) { /* fill in hostid part */ ifra.ifra_addr.sin6_addr.s6_addr32[2] = - hostid->s6_addr32[2]; + hostid->s6_addr32[2]; ifra.ifra_addr.sin6_addr.s6_addr32[3] = - hostid->s6_addr32[3]; + hostid->s6_addr32[3]; } if (((struct sockaddr *)&iflr->dstaddr)->sa_family) { /*XXX*/ bcopy(&iflr->dstaddr, &ifra.ifra_dstaddr, - ((struct sockaddr *)&iflr->dstaddr)->sa_len); + ((struct sockaddr *)&iflr->dstaddr)->sa_len); if (hostid) { ifra.ifra_dstaddr.sin6_addr.s6_addr32[2] = - hostid->s6_addr32[2]; + hostid->s6_addr32[2]; ifra.ifra_dstaddr.sin6_addr.s6_addr32[3] = - hostid->s6_addr32[3]; + hostid->s6_addr32[3]; } } - ifra.ifra_prefixmask.sin6_family = AF_INET6; ifra.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6); in6_prefixlen2mask(&ifra.ifra_prefixmask.sin6_addr, prefixlen); ifra.ifra_flags = iflr->flags & ~IFLR_PREFIX; - return in6_control(so, SIOCAIFADDR_IN6, (caddr_t)&ifra, - ifp, p); + return in6_control(so, SIOCAIFADDR_IN6, (caddr_t)&ifra, ifp, p); } case SIOCGLIFADDR: case SIOCDLIFADDR: @@ -948,7 +1519,7 @@ in6_lifaddr_ioctl(so, cmd, data, ifp, p) } else { if (cmd == SIOCGLIFADDR) { /* on getting an address, take the 1st match */ - cmp = 0; /*XXX*/ + cmp = 0; /* XXX */ } else { /* on deleting an address, do exact match */ in6_prefixlen2mask(&mask, 128); @@ -967,6 +1538,7 @@ in6_lifaddr_ioctl(so, cmd, data, ifp, p) continue; if (!cmp) break; + bcopy(IFA_IN6(ifa), &candidate, sizeof(candidate)); candidate.s6_addr32[0] &= mask.s6_addr32[0]; candidate.s6_addr32[1] &= mask.s6_addr32[1]; @@ -982,15 +1554,14 @@ in6_lifaddr_ioctl(so, cmd, data, ifp, p) if (cmd == SIOCGLIFADDR) { /* fill in the if_laddrreq structure */ bcopy(&ia->ia_addr, &iflr->addr, ia->ia_addr.sin6_len); - if ((ifp->if_flags & IFF_POINTOPOINT) != 0) { bcopy(&ia->ia_dstaddr, &iflr->dstaddr, - ia->ia_dstaddr.sin6_len); + ia->ia_dstaddr.sin6_len); } else bzero(&iflr->dstaddr, sizeof(iflr->dstaddr)); iflr->prefixlen = - in6_mask2len(&ia->ia_prefixmask.sin6_addr); + in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL); iflr->flags = ia->ia6_flags; /*XXX*/ @@ -1001,126 +1572,93 @@ in6_lifaddr_ioctl(so, cmd, data, ifp, p) /* fill in6_aliasreq and do ioctl(SIOCDIFADDR_IN6) */ bzero(&ifra, sizeof(ifra)); bcopy(iflr->iflr_name, ifra.ifra_name, - sizeof(ifra.ifra_name)); + sizeof(ifra.ifra_name)); bcopy(&ia->ia_addr, &ifra.ifra_addr, - ia->ia_addr.sin6_len); + ia->ia_addr.sin6_len); if ((ifp->if_flags & IFF_POINTOPOINT) != 0) { bcopy(&ia->ia_dstaddr, &ifra.ifra_dstaddr, - ia->ia_dstaddr.sin6_len); + ia->ia_dstaddr.sin6_len); } else { bzero(&ifra.ifra_dstaddr, sizeof(ifra.ifra_dstaddr)); } bcopy(&ia->ia_prefixmask, &ifra.ifra_dstaddr, - ia->ia_prefixmask.sin6_len); + ia->ia_prefixmask.sin6_len); ifra.ifra_flags = ia->ia6_flags; - return in6_control(so, SIOCDIFADDR_IN6, (caddr_t)&ifra, - ifp, p); + ifp, p); } } } - return EOPNOTSUPP; /*just for safety*/ -} - -/* - * Delete any existing route for an interface. - */ -void -in6_ifscrub(ifp, ia) - struct ifnet *ifp; - struct in6_ifaddr *ia; -{ - if ((ia->ia_flags & IFA_ROUTE) == 0) - return; - /* - * We should check the existence of dstaddr, because link-local - * addresses can be configured without particular destinations - * even on point-to-point or loopback interfaces. - * In this case, kernel would panic in rtinit()... - */ - if (ifp->if_flags & (IFF_LOOPBACK | IFF_POINTOPOINT) && - (ia->ia_ifa.ifa_dstaddr != NULL)) - rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST); - else - rtinit(&(ia->ia_ifa), (int)RTM_DELETE, 0); - ia->ia_flags &= ~IFA_ROUTE; - - /* Remove ownaddr's loopback rtentry, if it exists. */ - in6_ifremloop(&(ia->ia_ifa)); + return EOPNOTSUPP; /* just for safety */ } /* * Initialize an interface's intetnet6 address * and routing table entry. */ -int -in6_ifinit(ifp, ia, sin6, scrub) +static int +in6_ifinit(ifp, ia, sin6, newhost) struct ifnet *ifp; struct in6_ifaddr *ia; struct sockaddr_in6 *sin6; - int scrub; + int newhost; { - struct sockaddr_in6 oldaddr; - int error, flags = RTF_UP; + int error = 0, plen, ifacount = 0; int s = splimp(); + struct ifaddr *ifa; - oldaddr = ia->ia_addr; - ia->ia_addr = *sin6; /* * Give the interface a chance to initialize * if this is its first address, * and to validate the address if necessary. */ - if (ifp->if_ioctl && - (error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia))) { + for (ifa = ifp->if_addrlist.tqh_first; ifa; + ifa = ifa->ifa_list.tqe_next) + { + if (ifa->ifa_addr == NULL) + continue; /* just for safety */ + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + ifacount++; + } + + ia->ia_addr = *sin6; + + if (ifacount <= 1 && ifp->if_ioctl && + (error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia))) { splx(s); - ia->ia_addr = oldaddr; return(error); } + splx(s); - switch (ifp->if_type) { - case IFT_ARCNET: - case IFT_ETHER: - case IFT_FDDI: - ia->ia_ifa.ifa_rtrequest = nd6_rtrequest; - ia->ia_ifa.ifa_flags |= RTF_CLONING; - break; - case IFT_PPP: - ia->ia_ifa.ifa_rtrequest = nd6_p2p_rtrequest; - ia->ia_ifa.ifa_flags |= RTF_CLONING; - break; - } + ia->ia_ifa.ifa_metric = ifp->if_metric; + + /* we could do in(6)_socktrim here, but just omit it at this moment. */ - splx(s); - if (scrub) { - ia->ia_ifa.ifa_addr = (struct sockaddr *)&oldaddr; - in6_ifscrub(ifp, ia); - ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; - } - /* xxx - * in_socktrim - */ /* - * Add route for the network. + * Special case: + * If the destination address is specified for a point-to-point + * interface, install a route to the destination as an interface + * direct route. */ - ia->ia_ifa.ifa_metric = ifp->if_metric; - if (ifp->if_flags & IFF_LOOPBACK) { - ia->ia_ifa.ifa_dstaddr = ia->ia_ifa.ifa_addr; - flags |= RTF_HOST; - } else if (ifp->if_flags & IFF_POINTOPOINT) { - if (ia->ia_dstaddr.sin6_family != AF_INET6) - return(0); - flags |= RTF_HOST; - } - if ((error = rtinit(&(ia->ia_ifa), (int)RTM_ADD, flags)) == 0) + plen = in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL); /* XXX */ + if (plen == 128 && ia->ia_dstaddr.sin6_family == AF_INET6) { + if ((error = rtinit(&(ia->ia_ifa), (int)RTM_ADD, + RTF_UP | RTF_HOST)) != 0) + return(error); ia->ia_flags |= IFA_ROUTE; + } - /* Add ownaddr as loopback rtentry, if necessary(ex. on p2p link). */ - in6_ifaddloop(&(ia->ia_ifa)); + /* Add ownaddr as loopback rtentry, if necessary (ex. on p2p link). */ + if (newhost) { + /* set the rtrequest function to create llinfo */ + ia->ia_ifa.ifa_rtrequest = nd6_rtrequest; + in6_ifaddloop(&(ia->ia_ifa)); + } if (ifp->if_flags & IFF_MULTICAST) in6_restoremkludge(ia, ifp); @@ -1312,7 +1850,7 @@ in6_addmulti(maddr6, ifp, errorp) *errorp = ENXIO; /* XXX: appropriate? */ else *errorp = (*ifp->if_ioctl)(ifp, SIOCADDMULTI, - (caddr_t)&ifr); + (caddr_t)&ifr); if (*errorp) { LIST_REMOVE(in6m, in6m_entry); free(in6m, M_IPMADDR); @@ -1512,23 +2050,6 @@ ip6_sprintf(addr) return(ip6buf[ip6round]); } -int -in6_localaddr(in6) - struct in6_addr *in6; -{ - struct in6_ifaddr *ia; - - if (IN6_IS_ADDR_LOOPBACK(in6) || IN6_IS_ADDR_LINKLOCAL(in6)) - return 1; - - for (ia = in6_ifaddr; ia; ia = ia->ia_next) - if (IN6_ARE_MASKED_ADDR_EQUAL(in6, &ia->ia_addr.sin6_addr, - &ia->ia_prefixmask.sin6_addr)) - return 1; - - return (0); -} - /* * Get a scope of the address. Node-local, link-local, site-local or global. */ @@ -1611,6 +2132,27 @@ in6_addr2scopeid(ifp, addr) } } +int +in6_is_addr_deprecated(sa6) + struct sockaddr_in6 *sa6; +{ + struct in6_ifaddr *ia; + + for (ia = in6_ifaddr; ia; ia = ia->ia_next) { + if (IN6_ARE_ADDR_EQUAL(&ia->ia_addr.sin6_addr, + &sa6->sin6_addr) && +#ifdef SCOPEDROUTING + ia->ia_addr.sin6_scope_id == sa6->sin6_scope_id && +#endif + (ia->ia6_flags & IN6_IFF_DEPRECATED) != 0) + return(1); /* true */ + + /* XXX: do we still have to go thru the rest of the list? */ + } + + return(0); /* false */ +} + /* * return length of part which dst and src are equal * hard coding... diff --git a/sys/netinet6/in6.h b/sys/netinet6/in6.h index 42ede9a2fb3..d5958ab2598 100644 --- a/sys/netinet6/in6.h +++ b/sys/netinet6/in6.h @@ -1,4 +1,4 @@ -/* $OpenBSD: in6.h,v 1.29 2002/06/07 15:32:42 itojun Exp $ */ +/* $OpenBSD: in6.h,v 1.30 2002/06/08 21:22:02 itojun Exp $ */ /* $KAME: in6.h,v 1.83 2001/03/29 02:55:07 jinmei Exp $ */ /* @@ -356,6 +356,15 @@ extern const struct in6_addr in6addr_linklocal_allrouters; #define IN6_IS_SCOPE_LINKLOCAL(a) \ ((IN6_IS_ADDR_LINKLOCAL(a)) || \ (IN6_IS_ADDR_MC_LINKLOCAL(a))) + +#define IFA6_IS_DEPRECATED(a) \ + ((a)->ia6_lifetime.ia6t_pltime != ND6_INFINITE_LIFETIME && \ + (u_int32_t)((time.tv_sec - (a)->ia6_updatetime)) > \ + (a)->ia6_lifetime.ia6t_pltime) +#define IFA6_IS_INVALID(a) \ + ((a)->ia6_lifetime.ia6t_vltime != ND6_INFINITE_LIFETIME && \ + (u_int32_t)((time.tv_sec - (a)->ia6_updatetime)) > \ + (a)->ia6_lifetime.ia6t_vltime) #endif /* diff --git a/sys/netinet6/in6_ifattach.c b/sys/netinet6/in6_ifattach.c index 3124aaca96c..42feb0daa43 100644 --- a/sys/netinet6/in6_ifattach.c +++ b/sys/netinet6/in6_ifattach.c @@ -1,4 +1,4 @@ -/* $OpenBSD: in6_ifattach.c,v 1.26 2002/06/07 04:34:45 itojun Exp $ */ +/* $OpenBSD: in6_ifattach.c,v 1.27 2002/06/08 21:22:02 itojun Exp $ */ /* $KAME: in6_ifattach.c,v 1.124 2001/07/18 08:32:51 jinmei Exp $ */ /* @@ -56,10 +56,11 @@ unsigned long in6_maxmtu = 0; +int ip6_auto_linklocal = 1; /* enable by default */ + static int get_rand_ifid(struct ifnet *, struct in6_addr *); static int get_hw_ifid(struct ifnet *, struct in6_addr *); static int get_ifid(struct ifnet *, struct ifnet *, struct in6_addr *); -static int in6_ifattach_addaddr(struct ifnet *, struct in6_ifaddr *); static int in6_ifattach_linklocal(struct ifnet *, struct ifnet *); static int in6_ifattach_loopback(struct ifnet *); @@ -152,13 +153,25 @@ found: addr = LLADDR(sdl); addrlen = sdl->sdl_alen; + switch (ifp->if_type) { + case IFT_IEEE1394: + case IFT_IEEE80211: + /* IEEE1394 uses 16byte length address starting with EUI64 */ + if (addrlen > 8) + addrlen = 8; + break; + default: + break; + } + /* get EUI64 */ switch (ifp->if_type) { + /* IEEE802/EUI64 cases - what others? */ case IFT_ETHER: case IFT_FDDI: case IFT_ATM: - /* IEEE802/EUI64 cases - what others? */ - + case IFT_IEEE1394: + case IFT_IEEE80211: /* look at IEEE802/EUI64 only */ if (addrlen != 8 && addrlen != 6) return -1; @@ -305,189 +318,135 @@ success: return 0; } -/* - * configure IPv6 interface address. XXX code duplicated with in.c - */ static int -in6_ifattach_addaddr(ifp, ia) +in6_ifattach_linklocal(ifp, altifp) struct ifnet *ifp; - struct in6_ifaddr *ia; + struct ifnet *altifp; /*secondary EUI64 source*/ { - struct in6_ifaddr *oia; - struct ifaddr *ifa; - int error; - int rtflag; - struct in6_addr llsol; - - /* - * initialize if_addrlist, if we are the very first one - */ - ifa = TAILQ_FIRST(&ifp->if_addrlist); - if (ifa == NULL) { - TAILQ_INIT(&ifp->if_addrlist); - } - - /* - * link the interface address to global list - */ - TAILQ_INSERT_TAIL(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list); - ia->ia_ifa.ifa_refcnt++; + struct in6_ifaddr *ia; + struct in6_aliasreq ifra; + struct nd_prefix pr0; + int i, error; /* - * Also link into the IPv6 address chain beginning with in6_ifaddr. - * kazu opposed it, but itojun & jinmei wanted. + * configure link-local address. */ - if ((oia = in6_ifaddr) != NULL) { - for (; oia->ia_next; oia = oia->ia_next) - continue; - oia->ia_next = ia; - } else - in6_ifaddr = ia; - ia->ia_ifa.ifa_refcnt++; + bzero(&ifra, sizeof(ifra)); /* - * give the interface a chance to initialize, in case this - * is the first address to be added. + * in6_update_ifa() does not use ifra_name, but we accurately set it + * for safety. */ - if (ifp->if_ioctl != NULL) { - int s; - s = splimp(); - error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia); - splx(s); - } else - error = 0; - if (error) { - switch (error) { - case EAFNOSUPPORT: - printf("%s: IPv6 not supported\n", ifp->if_xname); - break; - default: - printf("%s: SIOCSIFADDR error %d\n", ifp->if_xname, - error); - break; + strncpy(ifra.ifra_name, ifp->if_xname, sizeof(ifra.ifra_name)); + + ifra.ifra_addr.sin6_family = AF_INET6; + ifra.ifra_addr.sin6_len = sizeof(struct sockaddr_in6); + ifra.ifra_addr.sin6_addr.s6_addr16[0] = htons(0xfe80); + ifra.ifra_addr.sin6_addr.s6_addr16[1] = htons(ifp->if_index); + ifra.ifra_addr.sin6_addr.s6_addr32[1] = 0; + if ((ifp->if_flags & IFF_LOOPBACK) != 0) { + ifra.ifra_addr.sin6_addr.s6_addr32[2] = 0; + ifra.ifra_addr.sin6_addr.s6_addr32[3] = htonl(1); + } else { + if (get_ifid(ifp, altifp, &ifra.ifra_addr.sin6_addr) != 0) { + nd6log((LOG_ERR, + "%s: no ifid available\n", ifp->if_xname)); + return(-1); } - - /* undo changes */ - TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list); - IFAFREE(&ia->ia_ifa); - if (oia) - oia->ia_next = ia->ia_next; - else - in6_ifaddr = ia->ia_next; - IFAFREE(&ia->ia_ifa); - return -1; } - /* configure link-layer address resolution */ - rtflag = 0; - if (IN6_ARE_ADDR_EQUAL(&ia->ia_prefixmask.sin6_addr, &in6mask128)) - rtflag = RTF_HOST; - else { - switch (ifp->if_type) { - case IFT_LOOP: -#ifdef IFT_STF - case IFT_STF: + ifra.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6); + ifra.ifra_prefixmask.sin6_family = AF_INET6; + ifra.ifra_prefixmask.sin6_addr = in6mask64; +#ifdef SCOPEDROUTING + /* take into account the sin6_scope_id field for routing */ + ifra.ifra_prefixmask.sin6_scope_id = 0xffffffff; #endif - rtflag = 0; - break; - default: - ia->ia_ifa.ifa_rtrequest = nd6_rtrequest; - ia->ia_ifa.ifa_flags |= RTF_CLONING; - rtflag = RTF_CLONING; - break; - } - } - - /* add route to the interface. */ - rtrequest(RTM_ADD, - (struct sockaddr *)&ia->ia_addr, - (struct sockaddr *)&ia->ia_addr, - (struct sockaddr *)&ia->ia_prefixmask, - RTF_UP | rtflag, - (struct rtentry **)0); - ia->ia_flags |= IFA_ROUTE; + /* link-local addresses should NEVER expire. */ + ifra.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME; + ifra.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME; - if ((rtflag & RTF_CLONING) != 0 && - (ifp->if_flags & IFF_MULTICAST) != 0) { - /* Restore saved multicast addresses (if any). */ - in6_restoremkludge(ia, ifp); + /* + * Do not let in6_update_ifa() do DAD, since we need a random delay + * before sending an NS at the first time the interface becomes up. + * Instead, in6_if_up() will start DAD with a proper random delay. + */ + ifra.ifra_flags |= IN6_IFF_NODAD; + /* + * Now call in6_update_ifa() to do a bunch of procedures to configure + * a link-local address. We can set NULL to the 3rd argument, because + * we know there's no other link-local address on the interface + * and therefore we are adding one (instead of updating one). + */ + if ((error = in6_update_ifa(ifp, &ifra, NULL)) != 0) { /* - * join solicited multicast address + * XXX: When the interface does not support IPv6, this call + * would fail in the SIOCSIFADDR ioctl. I believe the + * notification is rather confusing in this case, so just + * suppress it. (jinmei@kame.net 20010130) */ - bzero(&llsol, sizeof(llsol)); - llsol.s6_addr16[0] = htons(0xff02); - llsol.s6_addr16[1] = htons(ifp->if_index); - llsol.s6_addr32[1] = 0; - llsol.s6_addr32[2] = htonl(1); - llsol.s6_addr32[3] = ia->ia_addr.sin6_addr.s6_addr32[3]; - llsol.s6_addr8[12] = 0xff; - (void)in6_addmulti(&llsol, ifp, &error); - - /* XXX should we run DAD on other interface types? */ - if (in6if_do_dad(ifp)) { - /* mark the address TENTATIVE, if needed. */ - ia->ia6_flags |= IN6_IFF_TENTATIVE; - /* nd6_dad_start() will be called in in6_if_up */ - } + if (error != EAFNOSUPPORT) + log(LOG_NOTICE, "in6_ifattach_linklocal: failed to " + "configure a link-local address on %s " + "(errno=%d)\n", + ifp->if_xname, error); + return(-1); } - return 0; -} - -static int -in6_ifattach_linklocal(ifp, altifp) - struct ifnet *ifp; - struct ifnet *altifp; /*secondary EUI64 source*/ -{ - struct in6_ifaddr *ia; - /* - * configure link-local address + * Adjust ia6_flags so that in6_if_up will perform DAD. + * XXX: Some P2P interfaces seem not to send packets just after + * becoming up, so we skip p2p interfaces for safety. */ - ia = (struct in6_ifaddr *)malloc(sizeof(*ia), M_IFADDR, M_WAITOK); - bzero((caddr_t)ia, sizeof(*ia)); - ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; - if (ifp->if_flags & IFF_POINTOPOINT) - ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr; - else - ia->ia_ifa.ifa_dstaddr = NULL; - ia->ia_ifa.ifa_netmask = (struct sockaddr *)&ia->ia_prefixmask; - ia->ia_ifp = ifp; - - bzero(&ia->ia_prefixmask, sizeof(ia->ia_prefixmask)); - ia->ia_prefixmask.sin6_len = sizeof(struct sockaddr_in6); - ia->ia_prefixmask.sin6_family = AF_INET6; - ia->ia_prefixmask.sin6_addr = in6mask64; - - /* just in case */ - bzero(&ia->ia_dstaddr, sizeof(ia->ia_dstaddr)); - ia->ia_dstaddr.sin6_len = sizeof(struct sockaddr_in6); - ia->ia_dstaddr.sin6_family = AF_INET6; - - bzero(&ia->ia_addr, sizeof(ia->ia_addr)); - ia->ia_addr.sin6_len = sizeof(struct sockaddr_in6); - ia->ia_addr.sin6_family = AF_INET6; - ia->ia_addr.sin6_addr.s6_addr16[0] = htons(0xfe80); - ia->ia_addr.sin6_addr.s6_addr16[1] = htons(ifp->if_index); - ia->ia_addr.sin6_addr.s6_addr32[1] = 0; - if (ifp->if_flags & IFF_LOOPBACK) { - ia->ia_addr.sin6_addr.s6_addr32[2] = 0; - ia->ia_addr.sin6_addr.s6_addr32[3] = htonl(1); - } else { - if (get_ifid(ifp, altifp, &ia->ia_addr.sin6_addr) != 0) { - nd6log((LOG_ERR, - "%s: no ifid available\n", ifp->if_xname)); - free(ia, M_IFADDR); - return -1; - } + ia = in6ifa_ifpforlinklocal(ifp, 0); /* ia must not be NULL */ +#ifdef DIAGNOSTIC + if (!ia) { + panic("ia == NULL in in6_ifattach_linklocal"); + /* NOTREACHED */ + } +#endif + if (in6if_do_dad(ifp) && (ifp->if_flags & IFF_POINTOPOINT) == 0) { + ia->ia6_flags &= ~IN6_IFF_NODAD; + ia->ia6_flags |= IN6_IFF_TENTATIVE; } - ia->ia_ifa.ifa_metric = ifp->if_metric; - - if (in6_ifattach_addaddr(ifp, ia) != 0) { - /* ia will be freed on failure */ - return -1; + /* + * Make the link-local prefix (fe80::/64%link) as on-link. + * Since we'd like to manage prefixes separately from addresses, + * we make an ND6 prefix structure for the link-local prefix, + * and add it to the prefix list as a never-expire prefix. + * XXX: this change might affect some existing code base... + */ + bzero(&pr0, sizeof(pr0)); + pr0.ndpr_ifp = ifp; + /* this should be 64 at this moment. */ + pr0.ndpr_plen = in6_mask2len(&ifra.ifra_prefixmask.sin6_addr, NULL); + pr0.ndpr_mask = ifra.ifra_prefixmask.sin6_addr; + pr0.ndpr_prefix = ifra.ifra_addr; + /* apply the mask for safety. (nd6_prelist_add will apply it again) */ + for (i = 0; i < 4; i++) { + pr0.ndpr_prefix.sin6_addr.s6_addr32[i] &= + in6mask64.s6_addr32[i]; + } + /* + * Initialize parameters. The link-local prefix must always be + * on-link, and its lifetimes never expire. + */ + pr0.ndpr_raf_onlink = 1; + pr0.ndpr_raf_auto = 1; /* probably meaningless */ + pr0.ndpr_vltime = ND6_INFINITE_LIFETIME; + pr0.ndpr_pltime = ND6_INFINITE_LIFETIME; + /* + * Since there is no other link-local addresses, nd6_prefix_lookup() + * probably returns NULL. However, we cannot always expect the result. + * For example, if we first remove the (only) existing link-local + * address, and then reconfigure another one, the prefix is still + * valid with referring to the old link-local address. + */ + if (nd6_prefix_lookup(&pr0) == NULL) { + if ((error = nd6_prelist_add(&pr0, NULL, NULL)) != 0) + return(error); } return 0; @@ -497,49 +456,106 @@ static int in6_ifattach_loopback(ifp) struct ifnet *ifp; /* must be IFT_LOOP */ { - struct in6_ifaddr *ia; + struct in6_aliasreq ifra; + int error; + + bzero(&ifra, sizeof(ifra)); /* - * configure link-local address + * in6_update_ifa() does not use ifra_name, but we accurately set it + * for safety. */ - ia = (struct in6_ifaddr *)malloc(sizeof(*ia), M_IFADDR, M_WAITOK); - bzero((caddr_t)ia, sizeof(*ia)); - ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; - ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr; - ia->ia_ifa.ifa_netmask = (struct sockaddr *)&ia->ia_prefixmask; - ia->ia_ifp = ifp; - - bzero(&ia->ia_prefixmask, sizeof(ia->ia_prefixmask)); - ia->ia_prefixmask.sin6_len = sizeof(struct sockaddr_in6); - ia->ia_prefixmask.sin6_family = AF_INET6; - ia->ia_prefixmask.sin6_addr = in6mask128; + strncpy(ifra.ifra_name, ifp->if_xname, sizeof(ifra.ifra_name)); + + ifra.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6); + ifra.ifra_prefixmask.sin6_family = AF_INET6; + ifra.ifra_prefixmask.sin6_addr = in6mask128; /* * Always initialize ia_dstaddr (= broadcast address) to loopback - * address, to make getifaddr happier. - * - * For BSDI, it is mandatory. The BSDI version of - * ifa_ifwithroute() rejects to add a route to the loopback - * interface. Even for other systems, loopback looks somewhat - * special. + * address. Follows IPv4 practice - see in_ifinit(). */ - bzero(&ia->ia_dstaddr, sizeof(ia->ia_dstaddr)); - ia->ia_dstaddr.sin6_len = sizeof(struct sockaddr_in6); - ia->ia_dstaddr.sin6_family = AF_INET6; - ia->ia_dstaddr.sin6_addr = in6addr_loopback; + ifra.ifra_dstaddr.sin6_len = sizeof(struct sockaddr_in6); + ifra.ifra_dstaddr.sin6_family = AF_INET6; + ifra.ifra_dstaddr.sin6_addr = in6addr_loopback; - bzero(&ia->ia_addr, sizeof(ia->ia_addr)); - ia->ia_addr.sin6_len = sizeof(struct sockaddr_in6); - ia->ia_addr.sin6_family = AF_INET6; - ia->ia_addr.sin6_addr = in6addr_loopback; + ifra.ifra_addr.sin6_len = sizeof(struct sockaddr_in6); + ifra.ifra_addr.sin6_family = AF_INET6; + ifra.ifra_addr.sin6_addr = in6addr_loopback; - ia->ia_ifa.ifa_metric = ifp->if_metric; + /* the loopback address should NEVER expire. */ + ifra.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME; + ifra.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME; + + /* we don't need to perform DAD on loopback interfaces. */ + ifra.ifra_flags |= IN6_IFF_NODAD; + + /* + * We are sure that this is a newly assigned address, so we can set + * NULL to the 3rd arg. + */ + if ((error = in6_update_ifa(ifp, &ifra, NULL)) != 0) { + log(LOG_ERR, "in6_ifattach_loopback: failed to configure " + "the loopback address on %s (errno=%d)\n", + ifp->if_xname, error); + return(-1); + } + + return 0; +} - if (in6_ifattach_addaddr(ifp, ia) != 0) { - /* ia will be freed on failure */ +/* + * compute NI group address, based on the current hostname setting. + * see draft-ietf-ipngwg-icmp-name-lookup-* (04 and later). + * + * when ifp == NULL, the caller is responsible for filling scopeid. + */ +int +in6_nigroup(ifp, name, namelen, sa6) + struct ifnet *ifp; + const char *name; + int namelen; + struct sockaddr_in6 *sa6; +{ + const char *p; + u_char *q; + MD5_CTX ctxt; + u_int8_t digest[16]; + char l; + char n[64]; /* a single label must not exceed 63 chars */ + + if (!namelen || !name) return -1; + + p = name; + while (p && *p && *p != '.' && p - name < namelen) + p++; + if (p - name > sizeof(n) - 1) + return -1; /* label too long */ + l = p - name; + strncpy(n, name, l); + n[(int)l] = '\0'; + for (q = n; *q; q++) { + if ('A' <= *q && *q <= 'Z') + *q = *q - 'A' + 'a'; } + /* generate 8 bytes of pseudo-random value. */ + bzero(&ctxt, sizeof(ctxt)); + MD5Init(&ctxt); + MD5Update(&ctxt, &l, sizeof(l)); + MD5Update(&ctxt, n, l); + MD5Final(digest, &ctxt); + + bzero(sa6, sizeof(*sa6)); + sa6->sin6_family = AF_INET6; + sa6->sin6_len = sizeof(*sa6); + sa6->sin6_addr.s6_addr16[0] = htons(0xff02); + sa6->sin6_addr.s6_addr16[1] = htons(ifp->if_index); + sa6->sin6_addr.s6_addr8[11] = 2; + bcopy(digest, &sa6->sin6_addr.s6_addr32[3], + sizeof(sa6->sin6_addr.s6_addr32[3])); + return 0; } @@ -553,10 +569,6 @@ in6_ifattach(ifp, altifp) struct ifnet *ifp; struct ifnet *altifp; /* secondary EUI64 source */ { - struct sockaddr_in6 mltaddr; - struct sockaddr_in6 mltmask; - struct sockaddr_in6 gate; - struct sockaddr_in6 mask; struct in6_ifaddr *ia; struct in6_addr in6; @@ -614,114 +626,27 @@ in6_ifattach(ifp, altifp) } /* - * assign link-local address, if there's none - */ - ia = in6ifa_ifpforlinklocal(ifp, 0); - if (ia == NULL) { - if (in6_ifattach_linklocal(ifp, altifp) != 0) - return; - ia = in6ifa_ifpforlinklocal(ifp, 0); - - if (ia == NULL) { - printf("%s: failed to add link-local address\n", - ifp->if_xname); - - /* we can't initialize multicasts without link-local */ - return; - } - } - - if (ifp->if_flags & IFF_POINTOPOINT) { - /* - * route local address to loopback - */ - bzero(&gate, sizeof(gate)); - gate.sin6_len = sizeof(struct sockaddr_in6); - gate.sin6_family = AF_INET6; - gate.sin6_addr = in6addr_loopback; - bzero(&mask, sizeof(mask)); - mask.sin6_len = sizeof(struct sockaddr_in6); - mask.sin6_family = AF_INET6; - mask.sin6_addr = in6mask64; - rtrequest(RTM_ADD, - (struct sockaddr *)&ia->ia_addr, - (struct sockaddr *)&gate, - (struct sockaddr *)&mask, - RTF_UP|RTF_HOST, - (struct rtentry **)0); - } - - /* * assign loopback address for loopback interface * XXX multiple loopback interface case */ - in6 = in6addr_loopback; - if (ifp->if_flags & IFF_LOOPBACK) { + if ((ifp->if_flags & IFF_LOOPBACK) != 0) { + in6 = in6addr_loopback; if (in6ifa_ifpwithaddr(ifp, &in6) == NULL) { if (in6_ifattach_loopback(ifp) != 0) return; } } -#ifdef DIAGNOSTIC - if (!ia) { - panic("ia == NULL in in6_ifattach"); - /*NOTREACHED*/ - } -#endif - /* - * join multicast + * assign a link-local address, if there's none. */ - if (ifp->if_flags & IFF_MULTICAST) { - int error; /* not used */ - struct in6_multi *in6m; - - /* Restore saved multicast addresses(if any). */ - in6_restoremkludge(ia, ifp); - - bzero(&mltmask, sizeof(mltmask)); - mltmask.sin6_len = sizeof(struct sockaddr_in6); - mltmask.sin6_family = AF_INET6; - mltmask.sin6_addr = in6mask32; - - /* - * join link-local all-nodes address - */ - bzero(&mltaddr, sizeof(mltaddr)); - mltaddr.sin6_len = sizeof(struct sockaddr_in6); - mltaddr.sin6_family = AF_INET6; - mltaddr.sin6_addr = in6addr_linklocal_allnodes; - mltaddr.sin6_addr.s6_addr16[1] = htons(ifp->if_index); - - IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m); - if (in6m == NULL) { - rtrequest(RTM_ADD, - (struct sockaddr *)&mltaddr, - (struct sockaddr *)&ia->ia_addr, - (struct sockaddr *)&mltmask, - RTF_UP|RTF_CLONING, /* xxx */ - (struct rtentry **)0); - (void)in6_addmulti(&mltaddr.sin6_addr, ifp, &error); - } - - if (ifp->if_flags & IFF_LOOPBACK) { - in6 = in6addr_loopback; - ia = in6ifa_ifpwithaddr(ifp, &in6); - /* - * join node-local all-nodes address, on loopback - */ - mltaddr.sin6_addr = in6addr_nodelocal_allnodes; - - IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m); - if (in6m == NULL && ia != NULL) { - rtrequest(RTM_ADD, - (struct sockaddr *)&mltaddr, - (struct sockaddr *)&ia->ia_addr, - (struct sockaddr *)&mltmask, - RTF_UP, - (struct rtentry **)0); - (void)in6_addmulti(&mltaddr.sin6_addr, ifp, &error); + if (ip6_auto_linklocal) { + ia = in6ifa_ifpforlinklocal(ifp, 0); + if (ia == NULL) { + if (in6_ifattach_linklocal(ifp, altifp) == 0) { + /* linklocal address assigned */ + } else { + /* failed to assign linklocal address. bark? */ } } } @@ -739,10 +664,7 @@ in6_ifdetach(ifp) struct rtentry *rt; short rtflags; struct sockaddr_in6 sin6; - struct in6_multi *in6m; - - /* nuke prefix list. this may try to remove some of ifaddrs as well */ - in6_purgeprefix(ifp); + struct in6_multi_mship *imm; /* remove neighbor management table */ nd6_purge(ifp); @@ -753,7 +675,7 @@ in6_ifdetach(ifp) next = ifa->ifa_list.tqe_next; if (ifa->ifa_addr->sa_family != AF_INET6) continue; - in6_purgeaddr(ifa, ifp); + in6_purgeaddr(ifa); } /* undo everything done by in6_ifattach(), just in case */ @@ -766,20 +688,23 @@ in6_ifdetach(ifp) ia = (struct in6_ifaddr *)ifa; - /* leave from all multicast groups joined */ - while ((in6m = LIST_FIRST(&ia->ia6_multiaddrs)) != NULL) - in6_delmulti(in6m); + /* + * leave from multicast groups we have joined for the interface + */ + while ((imm = ia->ia6_memberships.lh_first) != NULL) { + LIST_REMOVE(imm, i6mm_chain); + in6_leavegroup(imm); + } /* remove from the routing table */ - if ((ia->ia_flags & IFA_ROUTE) - && (rt = rtalloc1((struct sockaddr *)&ia->ia_addr, 0))) { + if ((ia->ia_flags & IFA_ROUTE) && + (rt = rtalloc1((struct sockaddr *)&ia->ia_addr, 0))) { rtflags = rt->rt_flags; rtfree(rt); - rtrequest(RTM_DELETE, - (struct sockaddr *)&ia->ia_addr, - (struct sockaddr *)&ia->ia_addr, - (struct sockaddr *)&ia->ia_prefixmask, - rtflags, (struct rtentry **)0); + rtrequest(RTM_DELETE, (struct sockaddr *)&ia->ia_addr, + (struct sockaddr *)&ia->ia_addr, + (struct sockaddr *)&ia->ia_prefixmask, + rtflags, (struct rtentry **)0); } /* remove from the linked list */ diff --git a/sys/netinet6/in6_ifattach.h b/sys/netinet6/in6_ifattach.h index b04dda6a4f0..b567235e921 100644 --- a/sys/netinet6/in6_ifattach.h +++ b/sys/netinet6/in6_ifattach.h @@ -1,4 +1,4 @@ -/* $OpenBSD: in6_ifattach.h,v 1.3 2002/03/14 01:27:12 millert Exp $ */ +/* $OpenBSD: in6_ifattach.h,v 1.4 2002/06/08 21:22:02 itojun Exp $ */ /* $KAME: in6_ifattach.h,v 1.9 2000/04/12 05:35:48 itojun Exp $ */ /* @@ -36,6 +36,7 @@ #ifdef _KERNEL void in6_ifattach(struct ifnet *, struct ifnet *); void in6_ifdetach(struct ifnet *); +int in6_nigroup(struct ifnet *, const char *, int, struct sockaddr_in6 *); #endif /* _KERNEL */ #endif /* _NETINET6_IN6_IFATTACH_H_ */ diff --git a/sys/netinet6/in6_prefix.c b/sys/netinet6/in6_prefix.c deleted file mode 100644 index 94f36028733..00000000000 --- a/sys/netinet6/in6_prefix.c +++ /dev/null @@ -1,1185 +0,0 @@ -/* $OpenBSD: in6_prefix.c,v 1.13 2002/03/14 01:27:12 millert Exp $ */ -/* $KAME: in6_prefix.c,v 1.47 2001/03/25 08:41:39 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: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 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 - * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -/* - * Copyright (c) 1982, 1986, 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University 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 REGENTS 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 - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)in.c 8.2 (Berkeley) 11/15/93 - */ - -#include <sys/param.h> -#include <sys/ioctl.h> -#include <sys/malloc.h> -#include <sys/kernel.h> -#include <sys/socket.h> -#include <sys/socketvar.h> -#include <sys/sockio.h> -#include <sys/systm.h> -#include <sys/syslog.h> -#include <sys/proc.h> - -#include <net/if.h> - -#include <netinet/in.h> -#include <netinet/in_var.h> -#include <netinet/ip6.h> -#include <netinet6/in6_prefix.h> -#include <netinet6/ip6_var.h> - -struct rr_prhead rr_prefix; - -struct timeout in6_rr_timer_ch; - -static void add_each_addr(struct socket *so, struct rr_prefix *rpp, - struct rp_addr *rap); -static int create_ra_entry(struct rp_addr **rapp); -static int add_each_prefix(struct socket *so, struct rr_prefix *rpp); -static void free_rp_entries(struct rr_prefix *rpp); -static int link_stray_ia6s(struct rr_prefix *rpp); -static void rp_remove(struct rr_prefix *rpp); - -/* - * Copy bits from src to tgt, from off bit for len bits. - * Caller must specify collect tgtsize and srcsize. - */ -static void -bit_copy(char *tgt, u_int tgtsize, char *src, u_int srcsize, - u_int off, u_int len) -{ - char *sp, *tp; - - /* arg values check */ - if (srcsize < off || srcsize < (off + len) || - tgtsize < off || tgtsize < (off + len)) { - log(LOG_ERR, - "in6_prefix.c: bit_copy: invalid args: srcsize %d,\n" - "tgtsize %d, off %d, len %d\n", srcsize, tgtsize, off, - len); - return; - } - - /* search start point */ - for (sp = src, tp = tgt; off >= 8; sp++, tp++) - off-=8; - /* copy starting bits */ - if (off) { - char setbit; - int startbits; - - startbits = min((8 - off), len); - - for (setbit = (0x80 >> off); startbits; - setbit >>= 1, startbits--, len--) - *tp |= (setbit & *sp); - tp++; - sp++; - } - /* copy midium bits */ - for (; len >= 8; sp++, tp++) { - *tp = *sp; - len-=8; - } - /* copy ending bits */ - if (len) { - char setbit; - - for (setbit = 0x80; len; setbit >>= 1, len--) - *tp |= (setbit & *sp); - } -} - -static struct ifprefix * -in6_prefixwithifp(struct ifnet *ifp, int plen, struct in6_addr *dst) -{ - struct ifprefix *ifpr; - - /* search matched prefix */ - for (ifpr = ifp->if_prefixlist; ifpr; ifpr = ifpr->ifpr_next) - { - if (ifpr->ifpr_prefix->sa_family != AF_INET6 || - ifpr->ifpr_type != IN6_PREFIX_RR) - continue; - if (plen <= in6_matchlen(dst, IFPR_IN6(ifpr))) - break; - } - return (ifpr); -} - -/* - * Search prefix which matches arg prefix as specified in - * draft-ietf-ipngwg-router-renum-08.txt - */ -static struct rr_prefix * -search_matched_prefix(struct ifnet *ifp, struct in6_prefixreq *ipr) -{ - struct ifprefix *ifpr; - struct ifaddr *ifa; - struct rr_prefix *rpp; - - /* search matched prefix */ - ifpr = in6_prefixwithifp(ifp, ipr->ipr_plen, - &ipr->ipr_prefix.sin6_addr); - if (ifpr != NULL) - return ifpr2rp(ifpr); - - /* - * search matched addr, and then search prefix - * which matches the addr - */ - - for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next) - { - if (ifa->ifa_addr->sa_family != AF_INET6) - continue; - if (ipr->ipr_plen <= - in6_matchlen(&ipr->ipr_prefix.sin6_addr, IFA_IN6(ifa))) - break; - } - if (ifa == NULL) - return NULL; - - rpp = ifpr2rp(((struct in6_ifaddr *)ifa)->ia6_ifpr); - if (rpp != 0) - return rpp; - - for (ifpr = ifp->if_prefixlist; ifpr; ifpr = ifpr->ifpr_next) - { - if (ifpr->ifpr_prefix->sa_family != AF_INET6 || - ifpr->ifpr_type != IN6_PREFIX_RR) - continue; - if (ifpr->ifpr_plen <= in6_matchlen(IFA_IN6(ifa), - IFPR_IN6(ifpr))) - break; - } - if (ifpr != NULL) - log(LOG_ERR, "in6_prefix.c: search_matched_prefix: addr %s" - "has no pointer to prefix %s\n", ip6_sprintf(IFA_IN6(ifa)), - ip6_sprintf(IFPR_IN6(ifpr))); - return ifpr2rp(ifpr); -} - -/* - * Search prefix which matches arg prefix as specified in - * draft-ietf-ipngwg-router-renum-08.txt, and mark it if exists. - * Return 1 if anything matched, and 0 if nothing matched. - */ -static int -mark_matched_prefixes(u_long cmd, struct ifnet *ifp, struct in6_rrenumreq *irr) -{ - struct ifprefix *ifpr; - struct ifaddr *ifa; - int matchlen, matched = 0; - - /* search matched prefixes */ - for (ifpr = ifp->if_prefixlist; ifpr; ifpr = ifpr->ifpr_next) - { - if (ifpr->ifpr_prefix->sa_family != AF_INET6 || - ifpr->ifpr_type != IN6_PREFIX_RR) - continue; - matchlen = in6_matchlen(&irr->irr_matchprefix.sin6_addr, - IFPR_IN6(ifpr)); - if (irr->irr_m_minlen > ifpr->ifpr_plen || - irr->irr_m_maxlen < ifpr->ifpr_plen || - irr->irr_m_len > matchlen) - continue; - matched = 1; - ifpr2rp(ifpr)->rp_statef_addmark = 1; - if (cmd == SIOCCIFPREFIX_IN6) - ifpr2rp(ifpr)->rp_statef_delmark = 1; - } - - /* - * search matched addr, and then search prefixes - * which matche the addr - */ - for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next) - { - struct rr_prefix *rpp; - - if (ifa->ifa_addr->sa_family != AF_INET6) - continue; - matchlen = in6_matchlen(&irr->irr_matchprefix.sin6_addr, - IFA_IN6(ifa)); - if (irr->irr_m_minlen > matchlen || - irr->irr_m_maxlen < matchlen || irr->irr_m_len > matchlen) - continue; - rpp = ifpr2rp(((struct in6_ifaddr *)ifa)->ia6_ifpr); - if (rpp != 0) { - matched = 1; - rpp->rp_statef_addmark = 1; - if (cmd == SIOCCIFPREFIX_IN6) - rpp->rp_statef_delmark = 1; - } else - log(LOG_WARNING, "in6_prefix.c: mark_matched_prefixes:" - "no back pointer to ifprefix for %s. " - "ND autoconfigured addr?\n", - ip6_sprintf(IFA_IN6(ifa))); - } - return matched; -} - -/* - * Mark global prefixes as to be deleted. - */ -static void -delmark_global_prefixes(struct ifnet *ifp, struct in6_rrenumreq *irr) -{ - struct ifprefix *ifpr; - - /* search matched prefixes */ - for (ifpr = ifp->if_prefixlist; ifpr; ifpr = ifpr->ifpr_next) - { - if (ifpr->ifpr_prefix->sa_family != AF_INET6 || - ifpr->ifpr_type != IN6_PREFIX_RR) - continue; - /* mark delete global prefix */ - if (in6_addrscope(RP_IN6(ifpr2rp(ifpr))) == - IPV6_ADDR_SCOPE_GLOBAL) - ifpr2rp(ifpr)->rp_statef_delmark = 1; - } -} - -/* Unmark prefixes */ -static void -unmark_prefixes(struct ifnet *ifp) -{ - struct ifprefix *ifpr; - - /* unmark all prefix */ - for (ifpr = ifp->if_prefixlist; ifpr; ifpr = ifpr->ifpr_next) - { - if (ifpr->ifpr_prefix->sa_family != AF_INET6 || - ifpr->ifpr_type != IN6_PREFIX_RR) - continue; - /* unmark prefix */ - ifpr2rp(ifpr)->rp_statef_addmark = 0; - ifpr2rp(ifpr)->rp_statef_delmark = 0; - } -} - -static void -init_prefix_ltimes(struct rr_prefix *rpp) -{ - long time_second = time.tv_sec; - - if (rpp->rp_pltime == RR_INFINITE_LIFETIME || - rpp->rp_rrf_decrprefd == 0) - rpp->rp_preferred = 0; - else - rpp->rp_preferred = time_second + rpp->rp_pltime; - if (rpp->rp_vltime == RR_INFINITE_LIFETIME || - rpp->rp_rrf_decrvalid == 0) - rpp->rp_expire = 0; - else - rpp->rp_expire = time_second + rpp->rp_vltime; -} - -static int -rr_are_ifid_equal(struct in6_addr *ii1, struct in6_addr *ii2, int ii_len) -{ - int ii_bytelen, ii_bitlen; - int p_bytelen, p_bitlen; - - /* sanity check */ - if (1 > ii_len || - ii_len > 124) { /* as RFC2373, prefix is at least 4 bit */ - log(LOG_ERR, "rr_are_ifid_equal: invalid ifid length(%d)\n", - ii_len); - return(0); - } - - ii_bytelen = ii_len / 8; - ii_bitlen = ii_len % 8; - - p_bytelen = sizeof(struct in6_addr) - ii_bytelen - 1; - p_bitlen = 8 - ii_bitlen; - - if (bcmp(ii1->s6_addr + p_bytelen + 1, ii2->s6_addr + p_bytelen + 1, - ii_bytelen)) - return(0); - if (((ii1->s6_addr[p_bytelen] << p_bitlen) & 0xff) != - ((ii2->s6_addr[p_bytelen] << p_bitlen) & 0xff)) - return(0); - - return(1); -} - -static struct rp_addr * -search_ifidwithprefix(struct rr_prefix *rpp, struct in6_addr *ifid) -{ - struct rp_addr *rap; - - for (rap = rpp->rp_addrhead.lh_first; rap != NULL; - rap = rap->ra_entry.le_next) - { - if (rr_are_ifid_equal(ifid, &rap->ra_ifid, - (sizeof(struct in6_addr) << 3) - - rpp->rp_plen)) - break; - } - return rap; -} - -static int -assign_ra_entry(struct rr_prefix *rpp, int iilen, struct in6_ifaddr *ia) -{ - int error = 0; - struct rp_addr *rap; - int s; - - if ((error = create_ra_entry(&rap)) != 0) - return error; - - /* copy interface id part */ - bit_copy((caddr_t)&rap->ra_ifid, sizeof(rap->ra_ifid) << 3, - (caddr_t)IA6_IN6(ia), - sizeof(*IA6_IN6(ia)) << 3, rpp->rp_plen, iilen); - /* link to ia, and put into list */ - rap->ra_addr = ia; - rap->ra_addr->ia_ifa.ifa_refcnt++; -#if 0 /* Can't do this now, because rpp may be on th stack. should fix it? */ - ia->ia6_ifpr = rp2ifpr(rpp); -#endif - s = splnet(); - LIST_INSERT_HEAD(&rpp->rp_addrhead, rap, ra_entry); - splx(s); - - return 0; -} - -/* - * add a link-local address to an interface. we will add new interface address - * (prefix database + new interface id). - */ -static int -in6_prefix_add_llifid(int iilen, struct in6_ifaddr *ia) -{ - struct rr_prefix *rpp; - struct rp_addr *rap; - struct socket so; - int error, s; - - if ((error = create_ra_entry(&rap)) != 0) - return(error); - /* copy interface id part */ - bit_copy((caddr_t)&rap->ra_ifid, sizeof(rap->ra_ifid) << 3, - (caddr_t)IA6_IN6(ia), sizeof(*IA6_IN6(ia)) << 3, - 64, (sizeof(rap->ra_ifid) << 3) - 64); - /* XXX: init dummy so */ - bzero(&so, sizeof(so)); - /* insert into list */ - for (rpp = LIST_FIRST(&rr_prefix); rpp; rpp = LIST_NEXT(rpp, rp_entry)) - { - /* - * do not attempt to add an address, if ifp does not match - */ - if (rpp->rp_ifp != ia->ia_ifp) - continue; - - s = splnet(); - LIST_INSERT_HEAD(&rpp->rp_addrhead, rap, ra_entry); - splx(s); - add_each_addr(&so, rpp, rap); - } - return 0; -} - -/* - * add an address to an interface. if the interface id portion is new, - * we will add new interface address (prefix database + new interface id). - */ -int -in6_prefix_add_ifid(int iilen, struct in6_ifaddr *ia) -{ - int plen = (sizeof(*IA6_IN6(ia)) << 3) - iilen; - struct ifprefix *ifpr; - struct rp_addr *rap; - int error = 0; - - if (IN6_IS_ADDR_LINKLOCAL(IA6_IN6(ia))) - return(in6_prefix_add_llifid(iilen, ia)); - ifpr = in6_prefixwithifp(ia->ia_ifp, plen, IA6_IN6(ia)); - if (ifpr == NULL) { - struct rr_prefix rp; - struct socket so; - int pplen = (plen == 128) ? 64 : plen; - - /* allocate a prefix for ia, with default properties */ - - /* init rp */ - bzero(&rp, sizeof(rp)); - rp.rp_type = IN6_PREFIX_RR; - rp.rp_ifp = ia->ia_ifp; - rp.rp_plen = pplen; - rp.rp_prefix.sin6_len = sizeof(rp.rp_prefix); - rp.rp_prefix.sin6_family = AF_INET6; - bit_copy((char *)RP_IN6(&rp), sizeof(*RP_IN6(&rp)) << 3, - (char *)&ia->ia_addr.sin6_addr, - sizeof(ia->ia_addr.sin6_addr) << 3, - 0, pplen); - rp.rp_vltime = rp.rp_pltime = RR_INFINITE_LIFETIME; - rp.rp_raf_onlink = 1; - rp.rp_raf_auto = 1; - /* Is some FlagMasks for rrf necessary? */ - rp.rp_rrf_decrvalid = rp.rp_rrf_decrprefd = 0; - rp.rp_origin = PR_ORIG_RR; /* can be renumbered */ - - /* create ra_entry */ - error = link_stray_ia6s(&rp); - if (error != 0) { - free_rp_entries(&rp); - return error; - } - - /* XXX: init dummy so */ - bzero(&so, sizeof(so)); - so.so_state |= SS_PRIV; - - error = add_each_prefix(&so, &rp); - - /* free each rp_addr entry */ - free_rp_entries(&rp); - - if (error != 0) - return error; - - /* search again */ - ifpr = in6_prefixwithifp(ia->ia_ifp, pplen, IA6_IN6(ia)); - if (ifpr == NULL) - return 0; - } - rap = search_ifidwithprefix(ifpr2rp(ifpr), IA6_IN6(ia)); - if (rap != NULL) { - if (rap->ra_addr == NULL) { - rap->ra_addr = ia; - rap->ra_addr->ia_ifa.ifa_refcnt++; - } else if (rap->ra_addr != ia) { - /* There may be some inconsistencies between addrs. */ - log(LOG_ERR, "ip6_prefix.c: addr %s/%d matched prefix" - " already has another ia %p(%s) on its ifid list\n", - ip6_sprintf(IA6_IN6(ia)), plen, - rap->ra_addr, - ip6_sprintf(IA6_IN6(rap->ra_addr))); - return EADDRINUSE /* XXX */; - } - ia->ia6_ifpr = ifpr; - return 0; - } - error = assign_ra_entry(ifpr2rp(ifpr), iilen, ia); - if (error == 0) - ia->ia6_ifpr = ifpr; - return (error); -} - -void -in6_prefix_remove_ifid(int iilen, struct in6_ifaddr *ia) -{ - struct rp_addr *rap; - - if (ia->ia6_ifpr == NULL) - return; - rap = search_ifidwithprefix(ifpr2rp(ia->ia6_ifpr), IA6_IN6(ia)); - if (rap != NULL) { - int s = splnet(); - LIST_REMOVE(rap, ra_entry); - splx(s); - if (rap->ra_addr) - IFAFREE(&rap->ra_addr->ia_ifa); - free(rap, M_RR_ADDR); - } - if (LIST_FIRST(&ifpr2rp(ia->ia6_ifpr)->rp_addrhead) == NULL) - rp_remove(ifpr2rp(ia->ia6_ifpr)); -} - -void -in6_purgeprefix(ifp) - struct ifnet *ifp; -{ - struct ifprefix *ifpr, *nextifpr; - - /* delete prefixes before ifnet goes away */ - for (ifpr = ifp->if_prefixlist; ifpr; ifpr = nextifpr) - { - nextifpr = ifpr->ifpr_next; - if (ifpr->ifpr_prefix->sa_family != AF_INET6 || - ifpr->ifpr_type != IN6_PREFIX_RR) - continue; - (void)delete_each_prefix(ifpr2rp(ifpr), PR_ORIG_KERNEL); - } -} - -static void -add_each_addr(struct socket *so, struct rr_prefix *rpp, struct rp_addr *rap) -{ - struct in6_ifaddr *ia6; - struct in6_aliasreq ifra; - int error; - - /* init ifra */ - bzero(&ifra, sizeof(ifra)); - strncpy(ifra.ifra_name, rpp->rp_ifp->if_xname, sizeof(ifra.ifra_name)); - ifra.ifra_addr.sin6_family = ifra.ifra_prefixmask.sin6_family = - AF_INET6; - ifra.ifra_addr.sin6_len = ifra.ifra_prefixmask.sin6_len = - sizeof(ifra.ifra_addr); - /* copy prefix part */ - bit_copy((char *)&ifra.ifra_addr.sin6_addr, - sizeof(ifra.ifra_addr.sin6_addr) << 3, - (char *)RP_IN6(rpp), sizeof(*RP_IN6(rpp)) << 3, - 0, rpp->rp_plen); - /* copy interface id part */ - bit_copy((char *)&ifra.ifra_addr.sin6_addr, - sizeof(ifra.ifra_addr.sin6_addr) << 3, - (char *)&rap->ra_ifid, sizeof(rap->ra_ifid) << 3, - rpp->rp_plen, (sizeof(rap->ra_ifid) << 3) - rpp->rp_plen); - in6_prefixlen2mask(&ifra.ifra_prefixmask.sin6_addr, rpp->rp_plen); - /* don't care ifra_flags for now */ - - ia6 = in6ifa_ifpwithaddr(rpp->rp_ifp, &ifra.ifra_addr.sin6_addr); - if (ia6 != NULL) { - if (ia6->ia6_ifpr == NULL) { - /* link this addr and the prefix each other */ - if (rap->ra_addr) - IFAFREE(&rap->ra_addr->ia_ifa); - rap->ra_addr = ia6; - rap->ra_addr->ia_ifa.ifa_refcnt++; - ia6->ia6_ifpr = rp2ifpr(rpp); - return; - } - if (ia6->ia6_ifpr == rp2ifpr(rpp)) { - if (rap->ra_addr) - IFAFREE(&rap->ra_addr->ia_ifa); - rap->ra_addr = ia6; - rap->ra_addr->ia_ifa.ifa_refcnt++; - return; - } - /* - * The addr is already assigned to other - * prefix. - * There may be some inconsistencies between - * prefixes. - * e.g. overraped prefixes with common starting - * part and different plefixlen. - * Or, completely duplicated prefixes? - * log it and return. - */ - log(LOG_ERR, "in6_prefix.c: add_each_addr: addition of an addr " - "%s/%d failed because there is already another addr %s/%d\n", - ip6_sprintf(&ifra.ifra_addr.sin6_addr), rpp->rp_plen, - ip6_sprintf(IA6_IN6(ia6)), - in6_mask2len(&ia6->ia_prefixmask.sin6_addr)); - return; - } - /* propagate ANYCAST flag if it is set for ancestor addr */ - if (rap->ra_flags.anycast != 0) - ifra.ifra_flags |= IN6_IFF_ANYCAST; - error = in6_control(so, SIOCAIFADDR_IN6, (caddr_t)&ifra, rpp->rp_ifp - , curproc); - if (error != 0) { - log(LOG_ERR, "in6_prefix.c: add_each_addr: addition of an addr" - "%s/%d failed because in6_control failed for error %d\n", - ip6_sprintf(&ifra.ifra_addr.sin6_addr), rpp->rp_plen, - error); - return; - } - - /* - * link beween this addr and the prefix will be done - * in in6_prefix_add_ifid - */ -} - -static int -rrpr_update(struct socket *so, struct rr_prefix *new) -{ - struct rr_prefix *rpp; - struct ifprefix *ifpr; - struct rp_addr *rap; - int s; - - /* search existing prefix */ - for (ifpr = new->rp_ifp->if_prefixlist; ifpr; ifpr = ifpr->ifpr_next) - { - if (ifpr->ifpr_prefix->sa_family != AF_INET6 || - ifpr->ifpr_type != IN6_PREFIX_RR) - continue; - if (ifpr->ifpr_plen == new->rp_plen && - in6_are_prefix_equal(IFPR_IN6(ifpr), RP_IN6(new), - ifpr->ifpr_plen)) - break; - } - rpp = ifpr2rp(ifpr); - if (rpp != NULL) { - /* - * We got a prefix which we have seen in the past. - */ - /* - * If the origin of the already-installed prefix is more - * preferable than the new one, ignore installation request. - */ - if (rpp->rp_origin > new->rp_origin) - return(EPERM); - - /* update prefix information */ - rpp->rp_flags.prf_ra = new->rp_flags.prf_ra; - if (rpp->rp_origin >= PR_ORIG_RR) - rpp->rp_flags.prf_rr = new->rp_flags.prf_rr; - rpp->rp_vltime = new->rp_vltime; - rpp->rp_pltime = new->rp_pltime; - rpp->rp_expire = new->rp_expire; - rpp->rp_preferred = new->rp_preferred; - rpp->rp_statef_delmark = 0; /* cancel deletion */ - /* - * Interface id related update. - * add rp_addr entries in new into rpp, if they have not - * been already included in rpp. - */ - while (new->rp_addrhead.lh_first != NULL) - { - rap = LIST_FIRST(&new->rp_addrhead); - LIST_REMOVE(rap, ra_entry); - if (search_ifidwithprefix(rpp, &rap->ra_ifid) - != NULL) { - if (rap->ra_addr) - IFAFREE(&rap->ra_addr->ia_ifa); - free(rap, M_RR_ADDR); - continue; - } - s = splnet(); - LIST_INSERT_HEAD(&rpp->rp_addrhead, rap, ra_entry); - splx(s); - } - } else { - /* - * We got a fresh prefix. - */ - /* create new prefix */ - rpp = (struct rr_prefix *)malloc(sizeof(*rpp), M_IP6RR, - M_NOWAIT); - if (rpp == NULL) { - log(LOG_ERR, "in6_prefix.c: rrpr_update:%d" - ": ENOBUFS for rr_prefix\n", __LINE__); - return(ENOBUFS); - } - /* initilization */ - *rpp = *new; - LIST_INIT(&rpp->rp_addrhead); - /* move rp_addr entries of new to rpp */ - while (new->rp_addrhead.lh_first != NULL) - { - rap = LIST_FIRST(&new->rp_addrhead); - LIST_REMOVE(rap, ra_entry); - LIST_INSERT_HEAD(&rpp->rp_addrhead, rap, ra_entry); - } - - /* let rp_ifpr.ifpr_prefix point rr_prefix. */ - rpp->rp_ifpr.ifpr_prefix = (struct sockaddr *)&rpp->rp_prefix; - /* link rr_prefix entry to if_prefixlist */ - { - struct ifnet *ifp = rpp->rp_ifp; - struct ifprefix *ifpr; - - if ((ifpr = ifp->if_prefixlist) != NULL) { - for ( ; ifpr->ifpr_next; - ifpr = ifpr->ifpr_next) - continue; - ifpr->ifpr_next = rp2ifpr(rpp); - } else - ifp->if_prefixlist = rp2ifpr(rpp); - rp2ifpr(rpp)->ifpr_type = IN6_PREFIX_RR; - } - /* link rr_prefix entry to rr_prefix list */ - s = splnet(); - LIST_INSERT_HEAD(&rr_prefix, rpp, rp_entry); - splx(s); - } - - if (!new->rp_raf_auto) - return 0; - - /* - * Add an address for each interface id, if it is not yet - * If it existed but not pointing to the prefix yet, - * init the prefix pointer. - */ - for (rap = rpp->rp_addrhead.lh_first; rap != NULL; - rap = rap->ra_entry.le_next) - { - if (rap->ra_addr != NULL) { - if (rap->ra_addr->ia6_ifpr == NULL) - rap->ra_addr->ia6_ifpr = rp2ifpr(rpp); - continue; - } - add_each_addr(so, rpp, rap); - } - return 0; -} - -static int -add_each_prefix(struct socket *so, struct rr_prefix *rpp) -{ - init_prefix_ltimes(rpp); - return(rrpr_update(so, rpp)); -} - -static void -rp_remove(struct rr_prefix *rpp) -{ - int s; - - s = splnet(); - /* unlink rp_entry from if_prefixlist */ - { - struct ifnet *ifp = rpp->rp_ifp; - struct ifprefix *ifpr; - - if ((ifpr = ifp->if_prefixlist) == rp2ifpr(rpp)) - ifp->if_prefixlist = ifpr->ifpr_next; - else { - while (ifpr->ifpr_next && - (ifpr->ifpr_next != rp2ifpr(rpp))) - ifpr = ifpr->ifpr_next; - if (ifpr->ifpr_next) - ifpr->ifpr_next = rp2ifpr(rpp)->ifpr_next; - else - printf("Couldn't unlink rr_prefix from ifp\n"); - } - } - /* unlink rp_entry from rr_prefix list */ - LIST_REMOVE(rpp, rp_entry); - splx(s); - free(rpp, M_IP6RR); -} - -static int -create_ra_entry(struct rp_addr **rapp) -{ - *rapp = (struct rp_addr *)malloc(sizeof(struct rp_addr), M_RR_ADDR, - M_NOWAIT); - if (*rapp == NULL) { - log(LOG_ERR, "in6_prefix.c: init_newprefix:%d: ENOBUFS" - "for rp_addr\n", __LINE__); - return ENOBUFS; - } - bzero(*rapp, sizeof(*(*rapp))); - - return 0; -} - -static int -init_newprefix(struct in6_rrenumreq *irr, struct ifprefix *ifpr, - struct rr_prefix *rpp) -{ - struct rp_addr *orap; - - /* init rp */ - bzero(rpp, sizeof(*rpp)); - rpp->rp_type = IN6_PREFIX_RR; - rpp->rp_ifp = ifpr->ifpr_ifp; - rpp->rp_plen = ifpr->ifpr_plen; - rpp->rp_prefix.sin6_len = sizeof(rpp->rp_prefix); - rpp->rp_prefix.sin6_family = AF_INET6; - bit_copy((char *)RP_IN6(rpp), sizeof(*RP_IN6(rpp)) << 3, - (char *)&irr->irr_useprefix.sin6_addr, - sizeof(irr->irr_useprefix.sin6_addr) << 3, - 0, irr->irr_u_uselen); - /* copy keeplen part if necessary as necessary len */ - if (irr->irr_u_uselen < ifpr->ifpr_plen) - bit_copy((char *)RP_IN6(rpp), sizeof(*RP_IN6(rpp)) << 3, - (char *)IFPR_IN6(ifpr), sizeof(*IFPR_IN6(ifpr)) << 3, - irr->irr_u_uselen, - min(ifpr->ifpr_plen - irr->irr_u_uselen, - irr->irr_u_keeplen)); - for (orap = (ifpr2rp(ifpr)->rp_addrhead).lh_first; orap != NULL; - orap = orap->ra_entry.le_next) - { - struct rp_addr *rap; - int error = 0; - - if ((error = create_ra_entry(&rap)) != 0) - return error; - rap->ra_ifid = orap->ra_ifid; - rap->ra_flags.anycast = (orap->ra_addr != NULL && - (orap->ra_addr->ia6_flags & - IN6_IFF_ANYCAST) != 0) ? 1 : 0; - LIST_INSERT_HEAD(&rpp->rp_addrhead, rap, ra_entry); - } - rpp->rp_vltime = irr->irr_vltime; - rpp->rp_pltime = irr->irr_pltime; - rpp->rp_raf_onlink = irr->irr_raf_mask_onlink ? irr->irr_raf_onlink : - ifpr2rp(ifpr)->rp_raf_onlink; - rpp->rp_raf_auto = irr->irr_raf_mask_auto ? irr->irr_raf_auto : - ifpr2rp(ifpr)->rp_raf_auto; - /* Is some FlagMasks for rrf necessary? */ - rpp->rp_rrf = irr->irr_rrf; - rpp->rp_origin = irr->irr_origin; - - return 0; -} - -static void -free_rp_entries(struct rr_prefix *rpp) -{ - /* - * This func is only called with rpp on stack(not on list). - * So no splnet() here - */ - while (rpp->rp_addrhead.lh_first != NULL) - { - struct rp_addr *rap; - - rap = LIST_FIRST(&rpp->rp_addrhead); - LIST_REMOVE(rap, ra_entry); - if (rap->ra_addr) - IFAFREE(&rap->ra_addr->ia_ifa); - free(rap, M_RR_ADDR); - } -} - -static int -add_useprefixes(struct socket *so, struct ifnet *ifp, - struct in6_rrenumreq *irr) -{ - struct ifprefix *ifpr, *nextifpr; - struct rr_prefix rp; - int error = 0; - - /* add prefixes to each of marked prefix */ - for (ifpr = ifp->if_prefixlist; ifpr; ifpr = nextifpr) - { - nextifpr = ifpr->ifpr_next; - if (ifpr->ifpr_prefix->sa_family != AF_INET6 || - ifpr->ifpr_type != IN6_PREFIX_RR) - continue; - if (ifpr2rp(ifpr)->rp_statef_addmark) { - if ((error = init_newprefix(irr, ifpr, &rp)) != 0) - break; - error = add_each_prefix(so, &rp); - } - } - /* free each rp_addr entry */ - free_rp_entries(&rp); - - return error; -} - -static void -unprefer_prefix(struct rr_prefix *rpp) -{ - struct rp_addr *rap; - long time_second = time.tv_sec; - - for (rap = rpp->rp_addrhead.lh_first; rap != NULL; - rap = rap->ra_entry.le_next) { - if (rap->ra_addr == NULL) - continue; - rap->ra_addr->ia6_lifetime.ia6t_preferred = time_second; - rap->ra_addr->ia6_lifetime.ia6t_pltime = 0; - } -} - -int -delete_each_prefix(struct rr_prefix *rpp, u_char origin) -{ - int error = 0; - - if (rpp->rp_origin > origin) - return(EPERM); - - while (rpp->rp_addrhead.lh_first != NULL) { - struct rp_addr *rap; - int s; - - s = splnet(); - rap = LIST_FIRST(&rpp->rp_addrhead); - if (rap == NULL) { - splx(s); - break; - } - LIST_REMOVE(rap, ra_entry); - splx(s); - if (rap->ra_addr == NULL) { - free(rap, M_RR_ADDR); - continue; - } - rap->ra_addr->ia6_ifpr = NULL; - - in6_purgeaddr(&rap->ra_addr->ia_ifa, rpp->rp_ifp); - IFAFREE(&rap->ra_addr->ia_ifa); - free(rap, M_RR_ADDR); - } - rp_remove(rpp); - - return error; -} - -static void -delete_prefixes(struct ifnet *ifp, u_char origin) -{ - struct ifprefix *ifpr, *nextifpr; - - /* delete prefixes marked as tobe deleted */ - for (ifpr = ifp->if_prefixlist; ifpr; ifpr = nextifpr) - { - nextifpr = ifpr->ifpr_next; - if (ifpr->ifpr_prefix->sa_family != AF_INET6 || - ifpr->ifpr_type != IN6_PREFIX_RR) - continue; - if (ifpr2rp(ifpr)->rp_statef_delmark) - (void)delete_each_prefix(ifpr2rp(ifpr), origin); - } -} - -static int -link_stray_ia6s(struct rr_prefix *rpp) -{ - struct ifaddr *ifa; - - for (ifa = rpp->rp_ifp->if_addrlist.tqh_first; ifa; - ifa = ifa->ifa_list.tqe_next) - { - struct rp_addr *rap; - struct rr_prefix *orpp; - int error = 0; - - if (ifa->ifa_addr->sa_family != AF_INET6) - continue; - if (rpp->rp_plen > in6_matchlen(RP_IN6(rpp), IFA_IN6(ifa))) - continue; - - orpp = ifpr2rp(((struct in6_ifaddr *)ifa)->ia6_ifpr); - if (orpp != NULL) { - if (!in6_are_prefix_equal(RP_IN6(orpp), RP_IN6(rpp), - rpp->rp_plen)) - log(LOG_ERR, "in6_prefix.c: link_stray_ia6s:" - "addr %s/%d already linked to a prefix" - "and it matches also %s/%d\n", - ip6_sprintf(IFA_IN6(ifa)), orpp->rp_plen, - ip6_sprintf(RP_IN6(rpp)), - rpp->rp_plen); - continue; - } - if ((error = assign_ra_entry(rpp, - (sizeof(rap->ra_ifid) << 3) - - rpp->rp_plen, - (struct in6_ifaddr *)ifa)) != 0) - return error; - } - return 0; -} - -/* XXX assumes that permission is already checked by the caller */ -int -in6_prefix_ioctl(struct socket *so, u_long cmd, caddr_t data, - struct ifnet *ifp) -{ - struct rr_prefix *rpp, rp_tmp; - struct rp_addr *rap; - struct in6_prefixreq *ipr = (struct in6_prefixreq *)data; - struct in6_rrenumreq *irr = (struct in6_rrenumreq *)data; - struct ifaddr *ifa; - int error = 0; - - /* - * Failsafe for errneous address config program. - * Let's hope rrenumd don't make a mistakes. - */ - if (ipr->ipr_origin <= PR_ORIG_RA) - ipr->ipr_origin = PR_ORIG_STATIC; - - switch (cmd) { - case SIOCSGIFPREFIX_IN6: - delmark_global_prefixes(ifp, irr); - /* FALL THROUGH */ - case SIOCAIFPREFIX_IN6: - case SIOCCIFPREFIX_IN6: - /* check if preferred lifetime > valid lifetime */ - if (irr->irr_pltime > irr->irr_vltime) { - log(LOG_NOTICE, - "in6_prefix_ioctl: preferred lifetime" - "(%ld) is greater than valid lifetime(%ld)\n", - (u_long)irr->irr_pltime, (u_long)irr->irr_vltime); - error = EINVAL; - break; - } - if (mark_matched_prefixes(cmd, ifp, irr)) { - if (irr->irr_u_uselen != 0) - if ((error = add_useprefixes(so, ifp, irr)) - != 0) - goto failed; - if (cmd != SIOCAIFPREFIX_IN6) - delete_prefixes(ifp, irr->irr_origin); - } else - return (EADDRNOTAVAIL); - failed: - unmark_prefixes(ifp); - break; - case SIOCGIFPREFIX_IN6: - rpp = search_matched_prefix(ifp, ipr); - if (rpp == NULL || ifp != rpp->rp_ifp) - return (EADDRNOTAVAIL); - - ipr->ipr_origin = rpp->rp_origin; - ipr->ipr_plen = rpp->rp_plen; - ipr->ipr_vltime = rpp->rp_vltime; - ipr->ipr_pltime = rpp->rp_pltime; - ipr->ipr_flags = rpp->rp_flags; - ipr->ipr_prefix = rpp->rp_prefix; - - break; - case SIOCSIFPREFIX_IN6: - /* check if preferred lifetime > valid lifetime */ - if (ipr->ipr_pltime > ipr->ipr_vltime) { - log(LOG_NOTICE, - "in6_prefix_ioctl: preferred lifetime" - "(%ld) is greater than valid lifetime(%ld)\n", - (u_long)ipr->ipr_pltime, (u_long)ipr->ipr_vltime); - error = EINVAL; - break; - } - - /* init rp_tmp */ - bzero((caddr_t)&rp_tmp, sizeof(rp_tmp)); - rp_tmp.rp_ifp = ifp; - rp_tmp.rp_plen = ipr->ipr_plen; - rp_tmp.rp_prefix = ipr->ipr_prefix; - rp_tmp.rp_vltime = ipr->ipr_vltime; - rp_tmp.rp_pltime = ipr->ipr_pltime; - rp_tmp.rp_flags = ipr->ipr_flags; - rp_tmp.rp_origin = ipr->ipr_origin; - - /* create rp_addr entries, usually at least for lladdr */ - if ((error = link_stray_ia6s(&rp_tmp)) != 0) { - free_rp_entries(&rp_tmp); - break; - } - for (ifa = ifp->if_addrlist.tqh_first; - ifa; - ifa = ifa->ifa_list.tqe_next) - { - if (ifa->ifa_addr == NULL) - continue; /* just for safety */ - if (ifa->ifa_addr->sa_family != AF_INET6) - continue; - if (IN6_IS_ADDR_LINKLOCAL(IFA_IN6(ifa)) == 0) - continue; - - if ((error = create_ra_entry(&rap)) != 0) { - free_rp_entries(&rp_tmp); - goto bad; - } - /* copy interface id part */ - bit_copy((caddr_t)&rap->ra_ifid, - sizeof(rap->ra_ifid) << 3, - (caddr_t)IFA_IN6(ifa), - sizeof(*IFA_IN6(ifa)) << 3, - rp_tmp.rp_plen, - (sizeof(rap->ra_ifid) << 3) - rp_tmp.rp_plen); - /* insert into list */ - LIST_INSERT_HEAD(&rp_tmp.rp_addrhead, rap, ra_entry); - } - - error = add_each_prefix(so, &rp_tmp); - - /* free each rp_addr entry */ - free_rp_entries(&rp_tmp); - - break; - case SIOCDIFPREFIX_IN6: - rpp = search_matched_prefix(ifp, ipr); - if (rpp == NULL || ifp != rpp->rp_ifp) - return (EADDRNOTAVAIL); - - error = delete_each_prefix(rpp, ipr->ipr_origin); - break; - } - bad: - return error; -} - -void -in6_rr_timer(void *ignored_arg) -{ - int s; - struct rr_prefix *rpp; - long time_second = time.tv_sec; - - timeout_set(&in6_rr_timer_ch, in6_rr_timer, NULL); - timeout_add(&in6_rr_timer_ch, ip6_rr_prune * hz); - - s = splnet(); - /* expire */ - rpp = LIST_FIRST(&rr_prefix); - while (rpp) { - if (rpp->rp_expire && rpp->rp_expire < time_second) { - struct rr_prefix *next_rpp; - - next_rpp = LIST_NEXT(rpp, rp_entry); - delete_each_prefix(rpp, PR_ORIG_KERNEL); - rpp = next_rpp; - continue; - } - if (rpp->rp_preferred && rpp->rp_preferred < time_second) - unprefer_prefix(rpp); - rpp = LIST_NEXT(rpp, rp_entry); - } - splx(s); -} diff --git a/sys/netinet6/in6_prefix.h b/sys/netinet6/in6_prefix.h deleted file mode 100644 index acaf4e1059f..00000000000 --- a/sys/netinet6/in6_prefix.h +++ /dev/null @@ -1,91 +0,0 @@ -/* $OpenBSD: in6_prefix.h,v 1.5 2002/03/14 01:27:12 millert Exp $ */ -/* $KAME: in6_prefix.h,v 1.10 2001/02/08 16:30:30 itojun Exp $ */ - -/* - * Copyright (C) 1995, 1996, 1997, 1998 and 1999 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: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 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 - * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include <sys/timeout.h> - -struct rr_prefix { - struct ifprefix rp_ifpr; - LIST_ENTRY(rr_prefix) rp_entry; - LIST_HEAD(rp_addrhead, rp_addr) rp_addrhead; - struct sockaddr_in6 rp_prefix; /* prefix */ - u_int32_t rp_vltime; /* advertised valid lifetime */ - u_int32_t rp_pltime; /* advertised preferred lifetime */ - time_t rp_expire; /* expiration time of the prefix */ - time_t rp_preferred; /* preferred time of the prefix */ - struct in6_prflags rp_flags; - u_char rp_origin; /* from where this prefix info is obtained */ - struct rp_stateflags { - /* if some prefix should be added to this prefix */ - u_char addmark : 1; - u_char delmark : 1; /* if this prefix will be deleted */ - } rp_stateflags; -}; - -#define rp_type rp_ifpr.ifpr_type -#define rp_ifp rp_ifpr.ifpr_ifp -#define rp_plen rp_ifpr.ifpr_plen - -#define rp_raf rp_flags.prf_ra -#define rp_raf_onlink rp_flags.prf_ra.onlink -#define rp_raf_auto rp_flags.prf_ra.autonomous - -#define rp_statef_addmark rp_stateflags.addmark -#define rp_statef_delmark rp_stateflags.delmark - -#define rp_rrf rp_flags.prf_rr -#define rp_rrf_decrvalid rp_flags.prf_rr.decrvalid -#define rp_rrf_decrprefd rp_flags.prf_rr.decrprefd - -struct rp_addr { - LIST_ENTRY(rp_addr) ra_entry; - struct in6_addr ra_ifid; - struct in6_ifaddr *ra_addr; - struct ra_flags { - u_char anycast : 1; - } ra_flags; -}; - -#define ifpr2rp(ifpr) ((struct rr_prefix *)(ifpr)) -#define rp2ifpr(rp) ((struct ifprefix *)(rp)) - -#define RP_IN6(rp) (&(rp)->rp_prefix.sin6_addr) - -#define RR_INFINITE_LIFETIME 0xffffffff - - -LIST_HEAD(rr_prhead, rr_prefix); - -extern struct rr_prhead rr_prefix; - -void in6_rr_timer(void *); -extern struct timeout in6_rr_timer_ch; -int delete_each_prefix(struct rr_prefix *rpp, u_char origin); diff --git a/sys/netinet6/in6_var.h b/sys/netinet6/in6_var.h index 71aa8830f4c..b25ee7a5826 100644 --- a/sys/netinet6/in6_var.h +++ b/sys/netinet6/in6_var.h @@ -1,4 +1,4 @@ -/* $OpenBSD: in6_var.h,v 1.19 2002/06/08 00:06:58 itojun Exp $ */ +/* $OpenBSD: in6_var.h,v 1.20 2002/06/08 21:22:02 itojun Exp $ */ /* $KAME: in6_var.h,v 1.55 2001/02/16 12:49:45 itojun Exp $ */ /* @@ -80,7 +80,7 @@ * hour rule for hosts). they should never be modified by nd6_timeout or * anywhere else. * userland -> kernel: accept pltime/vltime - * kernel -> userland: throuw up everything + * kernel -> userland: throw up everything * in kernel: modify preferred/expire only */ struct in6_addrlifetime { @@ -111,8 +111,17 @@ struct in6_ifaddr { /* list of multicast addresses */ int ia6_flags; - struct in6_addrlifetime ia6_lifetime; /* NULL = infty */ - struct ifprefix *ia6_ifpr; /* back pointer to ifprefix */ + struct in6_addrlifetime ia6_lifetime; + time_t ia6_createtime; /* the creation time of this address, which is + * currently used for temporary addresses only. + */ + time_t ia6_updatetime; + + /* back pointer to the ND prefix (for autoconfigured addresses only) */ + struct nd_prefix *ia6_ndpr; + + /* multicast addresses joined from the kernel */ + LIST_HEAD(, in6_multi_mship) ia6_memberships; }; /* @@ -270,7 +279,8 @@ struct in6_prflags { struct prf_ra { u_char onlink : 1; u_char autonomous : 1; - u_char reserved : 6; + u_char router : 1; + u_char reserved : 5; } prf_ra; u_char prf_reserved1; u_short prf_reserved2; @@ -352,8 +362,6 @@ struct in6_rrenumreq { #define IFA_IN6(x) (&((struct sockaddr_in6 *)((x)->ifa_addr))->sin6_addr) #define IFA_DSTIN6(x) (&((struct sockaddr_in6 *)((x)->ifa_dstaddr))->sin6_addr) -#define IFPR_IN6(x) (&((struct sockaddr_in6 *)((x)->ifpr_prefix))->sin6_addr) - #ifdef _KERNEL #define IN6_ARE_MASKED_ADDR_EQUAL(d, a, m) ( \ (((d)->s6_addr32[0] ^ (a)->s6_addr32[0]) & (m)->s6_addr32[0]) == 0 && \ @@ -426,6 +434,10 @@ struct in6_rrenumreq { #define IN6_IFF_DUPLICATED 0x04 /* DAD detected duplicate */ #define IN6_IFF_DETACHED 0x08 /* may be detached from the link */ #define IN6_IFF_DEPRECATED 0x10 /* deprecated address */ +#define IN6_IFF_NODAD 0x20 /* don't perform DAD on this address + * (used only at first SIOC* call) + */ +#define IN6_IFF_AUTOCONF 0x40 /* autoconfigurable address. */ /* do not input/output */ #define IN6_IFF_NOTREADY (IN6_IFF_TENTATIVE|IN6_IFF_DUPLICATED) @@ -491,7 +503,7 @@ struct in6_multi { #ifdef _KERNEL /* * Structure used by macros below to remember position when stepping through - * all of eht in6_multi records. + * all of the in6_multi records. */ struct in6_multistep { struct in6_ifaddr *i_ia; @@ -555,19 +567,19 @@ do { \ IN6_NEXT_MULTI((step), (in6m)); \ } while (0) -int in6_ifinit(struct ifnet *, struct in6_ifaddr *, struct sockaddr_in6 *, - int); struct in6_multi *in6_addmulti(struct in6_addr *, struct ifnet *, int *); void in6_delmulti(struct in6_multi *); struct in6_multi_mship *in6_joingroup(struct ifnet *, struct in6_addr *, int *); int in6_leavegroup(struct in6_multi_mship *); -void in6_ifscrub(struct ifnet *, struct in6_ifaddr *); int in6_ifindex2scopeid(int); -int in6_mask2len(struct in6_addr *); +int in6_mask2len(struct in6_addr *, u_char *); int in6_control(struct socket *, u_long, caddr_t, struct ifnet *, struct proc *); -void in6_purgeaddr(struct ifaddr *, struct ifnet *); +int in6_update_ifa(struct ifnet *, struct in6_aliasreq *, + struct in6_ifaddr *); +void in6_purgeaddr(struct ifaddr *); int in6if_do_dad(struct ifnet *); +void in6_purgeif(struct ifnet *); void in6_savemkludge(struct in6_ifaddr *); void in6_setmaxmtu(void); void *in6_domifattach(struct ifnet *); @@ -582,11 +594,9 @@ int in6_addr2scopeid(struct ifnet *, struct in6_addr *); int in6_matchlen(struct in6_addr *, struct in6_addr *); int in6_are_prefix_equal(struct in6_addr *, struct in6_addr *, int); void in6_prefixlen2mask(struct in6_addr *, int); -int in6_prefix_ioctl(struct socket *, u_long, caddr_t, struct ifnet *); -int in6_prefix_add_ifid(int, struct in6_ifaddr *); -void in6_prefix_remove_ifid(int, struct in6_ifaddr *); void in6_purgeprefix(struct ifnet *); +int in6_is_addr_deprecated(struct sockaddr_in6 *); struct inpcb; int in6_embedscope(struct in6_addr *, const struct sockaddr_in6 *, struct inpcb *, struct ifnet **); diff --git a/sys/netinet6/ip6_forward.c b/sys/netinet6/ip6_forward.c index 05195b20eed..cc0bcdaff5e 100644 --- a/sys/netinet6/ip6_forward.c +++ b/sys/netinet6/ip6_forward.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ip6_forward.c,v 1.22 2002/06/07 04:13:10 itojun Exp $ */ +/* $OpenBSD: ip6_forward.c,v 1.23 2002/06/08 21:22:03 itojun Exp $ */ /* $KAME: ip6_forward.c,v 1.75 2001/06/29 12:42:13 jinmei Exp $ */ /* @@ -519,12 +519,10 @@ senderr: return; switch (error) { case 0: -#if 1 if (type == ND_REDIRECT) { icmp6_redirect_output(mcopy, rt); return; } -#endif goto freecopy; case EMSGSIZE: diff --git a/sys/netinet6/ip6_input.c b/sys/netinet6/ip6_input.c index f8834b95f9e..5da3722e605 100644 --- a/sys/netinet6/ip6_input.c +++ b/sys/netinet6/ip6_input.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ip6_input.c,v 1.43 2002/06/07 15:27:58 itojun Exp $ */ +/* $OpenBSD: ip6_input.c,v 1.44 2002/06/08 21:22:03 itojun Exp $ */ /* $KAME: ip6_input.c,v 1.188 2001/03/29 05:34:31 itojun Exp $ */ /* @@ -102,7 +102,6 @@ #include <netinet/icmp6.h> #include <netinet6/in6_ifattach.h> #include <netinet6/nd6.h> -#include <netinet6/in6_prefix.h> #include <netinet6/ip6protosw.h> @@ -167,25 +166,10 @@ ip6_init2(dummy) void *dummy; { -#if 1 - /* - * to route local address of p2p link to loopback, - * assign loopback address first. - */ - in6_ifattach(lo0ifp, NULL); -#else - /* you MUST bring lo0 up manually, in rc script. */ -#endif - /* nd6_timer_init */ bzero(&nd6_timer_ch, sizeof(nd6_timer_ch)); timeout_set(&nd6_timer_ch, nd6_timer, NULL); timeout_add(&nd6_timer_ch, hz); - - /* router renumbering prefix list maintenance */ - bzero(&in6_rr_timer_ch, sizeof(in6_rr_timer_ch)); - timeout_set(&in6_rr_timer_ch, in6_rr_timer, (caddr_t)0); - timeout_add(&in6_rr_timer_ch, hz); } /* @@ -311,7 +295,7 @@ ip6_input(m) * support IPv4-less kernel compilation, we cannot support SIIT * environment at all. So, it makes more sense for us to reject any * malicious packets for non-SIIT environment, than try to do a - * partical support for SIIT environment. + * partial support for SIIT environment. */ if (IN6_IS_ADDR_V4MAPPED(&ip6->ip6_src) || IN6_IS_ADDR_V4MAPPED(&ip6->ip6_dst)) { @@ -461,7 +445,7 @@ ip6_input(m) * already done through looking up the routing table. */ IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, - &rt6_key(ip6_forward_rt.ro_rt)->sin6_addr) && + &rt6_key(ip6_forward_rt.ro_rt)->sin6_addr) && #endif ip6_forward_rt.ro_rt->rt_ifp->if_type == IFT_LOOP) { struct in6_ifaddr *ia6 = @@ -489,7 +473,7 @@ ip6_input(m) } /* - * FAITH(Firewall Aided Internet Translator) + * FAITH (Firewall Aided Internet Translator) */ #if defined(NFAITH) && 0 < NFAITH if (ip6_keepfaith) { @@ -748,6 +732,10 @@ ip6_hopopts_input(plenp, rtalertp, mp, offp) * This function is separate from ip6_hopopts_input() in order to * handle a case where the sending node itself process its hop-by-hop * options header. In such a case, the function is called from ip6_output(). + * + * The function assumes that hbh header is located right after the IPv6 header + * (RFC2460 p7), opthead is pointer into data content in m, and opthead to + * opthead + hbhlen is located in continuous memory region. */ int ip6_process_hopopts(m, opthead, hbhlen, rtalertp, plenp) @@ -762,6 +750,7 @@ ip6_process_hopopts(m, opthead, hbhlen, rtalertp, plenp) u_int8_t *opt = opthead; u_int16_t rtalert_val; u_int32_t jumboplen; + const int erroff = sizeof(struct ip6_hdr) + sizeof(struct ip6_hbh); for (; hbhlen > 0; hbhlen -= optlen, opt += optlen) { switch (*opt) { @@ -781,10 +770,13 @@ ip6_process_hopopts(m, opthead, hbhlen, rtalertp, plenp) ip6stat.ip6s_toosmall++; goto bad; } - if (*(opt + 1) != IP6OPT_RTALERT_LEN - 2) - /* XXX: should we discard the packet? */ - log(LOG_ERR, "length of router alert opt is inconsitent(%d)", - *(opt + 1)); + if (*(opt + 1) != IP6OPT_RTALERT_LEN - 2) { + /* XXX stat */ + icmp6_error(m, ICMP6_PARAM_PROB, + ICMP6_PARAMPROB_HEADER, + erroff + opt + 1 - opthead); + return (-1); + } optlen = IP6OPT_RTALERT_LEN; bcopy((caddr_t)(opt + 2), (caddr_t)&rtalert_val, 2); *rtalertp = ntohs(rtalert_val); @@ -795,11 +787,13 @@ ip6_process_hopopts(m, opthead, hbhlen, rtalertp, plenp) ip6stat.ip6s_toosmall++; goto bad; } - if (*(opt + 1) != IP6OPT_JUMBO_LEN - 2) - /* XXX: should we discard the packet? */ - log(LOG_ERR, "length of jumbopayload opt " - "is inconsistent(%d)\n", - *(opt + 1)); + if (*(opt + 1) != IP6OPT_JUMBO_LEN - 2) { + /* XXX stat */ + icmp6_error(m, ICMP6_PARAM_PROB, + ICMP6_PARAMPROB_HEADER, + erroff + opt + 1 - opthead); + return (-1); + } optlen = IP6OPT_JUMBO_LEN; /* @@ -810,11 +804,9 @@ ip6_process_hopopts(m, opthead, hbhlen, rtalertp, plenp) if (ip6->ip6_plen) { ip6stat.ip6s_badoptions++; icmp6_error(m, ICMP6_PARAM_PROB, - ICMP6_PARAMPROB_HEADER, - sizeof(struct ip6_hdr) + - sizeof(struct ip6_hbh) + - opt - opthead); - return(-1); + ICMP6_PARAMPROB_HEADER, + erroff + opt - opthead); + return (-1); } /* @@ -836,11 +828,9 @@ ip6_process_hopopts(m, opthead, hbhlen, rtalertp, plenp) if (*plenp != 0) { ip6stat.ip6s_badoptions++; icmp6_error(m, ICMP6_PARAM_PROB, - ICMP6_PARAMPROB_HEADER, - sizeof(struct ip6_hdr) + - sizeof(struct ip6_hbh) + - opt + 2 - opthead); - return(-1); + ICMP6_PARAMPROB_HEADER, + erroff + opt + 2 - opthead); + return (-1); } #endif @@ -850,11 +840,9 @@ ip6_process_hopopts(m, opthead, hbhlen, rtalertp, plenp) if (jumboplen <= IPV6_MAXPACKET) { ip6stat.ip6s_badoptions++; icmp6_error(m, ICMP6_PARAM_PROB, - ICMP6_PARAMPROB_HEADER, - sizeof(struct ip6_hdr) + - sizeof(struct ip6_hbh) + - opt + 2 - opthead); - return(-1); + ICMP6_PARAMPROB_HEADER, + erroff + opt + 2 - opthead); + return (-1); } *plenp = jumboplen; @@ -864,21 +852,20 @@ ip6_process_hopopts(m, opthead, hbhlen, rtalertp, plenp) ip6stat.ip6s_toosmall++; goto bad; } - if ((optlen = ip6_unknown_opt(opt, m, - sizeof(struct ip6_hdr) + - sizeof(struct ip6_hbh) + - opt - opthead)) == -1) - return(-1); + optlen = ip6_unknown_opt(opt, m, + erroff + opt - opthead); + if (optlen == -1) + return (-1); optlen += 2; break; } } - return(0); + return (0); bad: m_freem(m); - return(-1); + return (-1); } /* diff --git a/sys/netinet6/ip6_output.c b/sys/netinet6/ip6_output.c index 23510d0f23d..822cc034763 100644 --- a/sys/netinet6/ip6_output.c +++ b/sys/netinet6/ip6_output.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ip6_output.c,v 1.65 2002/06/07 21:47:44 itojun Exp $ */ +/* $OpenBSD: ip6_output.c,v 1.66 2002/06/08 21:22:03 itojun Exp $ */ /* $KAME: ip6_output.c,v 1.172 2001/03/25 09:55:56 itojun Exp $ */ /* @@ -427,7 +427,7 @@ ip6_output(m0, opt, ro, flags, im6o, ifpp) /* Source address validation */ if (IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src) && - (flags & IPV6_DADOUTPUT) == 0) { + (flags & IPV6_UNSPECSRC) == 0) { error = EOPNOTSUPP; ip6stat.ip6s_badscope++; goto bad; diff --git a/sys/netinet6/ip6_var.h b/sys/netinet6/ip6_var.h index 55753fa0aa4..ab4738b5e83 100644 --- a/sys/netinet6/ip6_var.h +++ b/sys/netinet6/ip6_var.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ip6_var.h,v 1.18 2002/06/07 21:47:44 itojun Exp $ */ +/* $OpenBSD: ip6_var.h,v 1.19 2002/06/08 21:22:03 itojun Exp $ */ /* $KAME: ip6_var.h,v 1.33 2000/06/11 14:59:20 jinmei Exp $ */ /* @@ -107,7 +107,7 @@ struct ip6asfrag { u_int16_t ip6af_mff; /* more fragment bit in frag off */ }; -#define IP6_REASS_MBUF(ip6af) (*(struct mbuf **)&((ip6af)->ip6af_m)) +#define IP6_REASS_MBUF(ip6af) ((ip6af)->ip6af_m) struct ip6_moptions { struct ifnet *im6o_multicast_ifp; /* ifp for outgoing multicasts */ @@ -202,7 +202,7 @@ struct ip6stat { #ifdef _KERNEL /* flags passed to ip6_output as last parameter */ -#define IPV6_DADOUTPUT 0x01 /* DAD */ +#define IPV6_UNSPECSRC 0x01 /* allow :: as the source address */ #define IPV6_FORWARDING 0x02 /* most of IPv6 header exists */ #define IPV6_MINMTU 0x04 /* use minimum MTU (IPV6_USE_MIN_MTU) */ @@ -215,7 +215,8 @@ extern int ip6_forward_srcrt; /* forward src-routed? */ extern int ip6_use_deprecated; /* allow deprecated addr as source */ extern int ip6_rr_prune; /* router renumbering prefix * walk list every 5 sec. */ -extern const int ip6_v6only; +extern const int ip6_v6only; + extern struct socket *ip6_mrouter; /* multicast routing daemon */ extern int ip6_sendredirects; /* send IP redirects when forwarding? */ extern int ip6_maxfragpackets; /* Maximum packets in reassembly queue */ @@ -231,6 +232,7 @@ extern int ip6_dad_count; /* DupAddrDetectionTransmits */ extern u_int32_t ip6_flow_seq; extern int ip6_auto_flowlabel; +extern int ip6_auto_linklocal; struct in6pcb; struct inpcb; @@ -280,6 +282,9 @@ int rip6_usrreq(struct socket *, int dest6_input(struct mbuf **, int *, int); int none_input(struct mbuf **, int *, int); + +struct in6_addr *in6_selectsrc(struct sockaddr_in6 *, struct ip6_pktopts *, + struct ip6_moptions *, struct route_in6 *, struct in6_addr *, int *); #endif /* _KERNEL */ #endif /* !_NETINET6_IP6_VAR_H_ */ diff --git a/sys/netinet6/mld6.c b/sys/netinet6/mld6.c index cc53bf4e7c4..81ba71a236b 100644 --- a/sys/netinet6/mld6.c +++ b/sys/netinet6/mld6.c @@ -1,4 +1,4 @@ -/* $OpenBSD: mld6.c,v 1.13 2002/03/14 01:27:12 millert Exp $ */ +/* $OpenBSD: mld6.c,v 1.14 2002/06/08 21:22:03 itojun Exp $ */ /* $KAME: mld6.c,v 1.26 2001/02/16 14:50:35 itojun Exp $ */ /* @@ -144,15 +144,16 @@ mld6_start_listening(in6m) * (reserved) or 1 (node-local). */ mld6_all_nodes_linklocal.s6_addr16[1] = - htons(in6m->in6m_ifp->if_index); /* XXX */ + htons(in6m->in6m_ifp->if_index); /* XXX */ if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &mld6_all_nodes_linklocal) || IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) < IPV6_ADDR_SCOPE_LINKLOCAL) { in6m->in6m_timer = 0; in6m->in6m_state = MLD6_OTHERLISTENER; } else { mld6_sendpkt(in6m, MLD6_LISTENER_REPORT, NULL); - in6m->in6m_timer = MLD6_RANDOM_DELAY( - MLD6_UNSOLICITED_REPORT_INTERVAL * PR_FASTHZ); + in6m->in6m_timer = + MLD6_RANDOM_DELAY(MLD6_UNSOLICITED_REPORT_INTERVAL * + PR_FASTHZ); in6m->in6m_state = MLD6_IREPORTEDLAST; mld6_timers_are_running = 1; } @@ -164,15 +165,15 @@ mld6_stop_listening(in6m) struct in6_multi *in6m; { mld6_all_nodes_linklocal.s6_addr16[1] = - htons(in6m->in6m_ifp->if_index); /* XXX */ + htons(in6m->in6m_ifp->if_index); /* XXX */ mld6_all_routers_linklocal.s6_addr16[1] = - htons(in6m->in6m_ifp->if_index); /* XXX: necessary when mrouting */ + htons(in6m->in6m_ifp->if_index); /* XXX: necessary when mrouting */ if (in6m->in6m_state == MLD6_IREPORTEDLAST && (!IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &mld6_all_nodes_linklocal)) && IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) > IPV6_ADDR_SCOPE_NODELOCAL) mld6_sendpkt(in6m, MLD6_LISTENER_DONE, - &mld6_all_routers_linklocal); + &mld6_all_routers_linklocal); } void @@ -237,7 +238,7 @@ mld6_input(m, off) break; /* print error or log stat? */ if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld6_addr)) mldh->mld6_addr.s6_addr16[1] = - htons(ifp->if_index); /* XXX */ + htons(ifp->if_index); /* XXX */ /* * - Start the timers in all of our membership records @@ -282,14 +283,13 @@ mld6_input(m, off) if (timer == 0) { /* send a report immediately */ mld6_sendpkt(in6m, MLD6_LISTENER_REPORT, - NULL); + NULL); in6m->in6m_timer = 0; /* reset timer */ in6m->in6m_state = MLD6_IREPORTEDLAST; - } - else if (in6m->in6m_timer == 0 || /* idle */ + } else if (in6m->in6m_timer == 0 || /* idle */ in6m->in6m_timer > timer) { in6m->in6m_timer = - MLD6_RANDOM_DELAY(timer); + MLD6_RANDOM_DELAY(timer); mld6_timers_are_running = 1; } } @@ -332,6 +332,11 @@ mld6_input(m, off) break; default: /* this is impossible */ #if 0 + /* + * this case should be impossible because of filtering in + * icmp6_input(). But we explicitly disabled this part + * just in case. + */ log(LOG_ERR, "mld6_input: illegal type(%d)", mldh->mld6_type); #endif break; @@ -355,7 +360,6 @@ mld6_fasttimeo() return; s = splnet(); - mld6_timers_are_running = 0; IN6_FIRST_MULTI(step, in6m); while (in6m != NULL) { @@ -384,15 +388,19 @@ mld6_sendpkt(in6m, type, dst) struct ip6_moptions im6o; struct in6_ifaddr *ia; struct ifnet *ifp = in6m->in6m_ifp; - struct ifnet *outif = NULL; + int ignflags; /* * At first, find a link local address on the outgoing interface * to use as the source address of the MLD packet. + * We do not reject tentative addresses for MLD report to deal with + * the case where we first join a link-local address. */ - if ((ia = in6ifa_ifpforlinklocal(ifp, IN6_IFF_NOTREADY|IN6_IFF_ANYCAST)) - == NULL) + ignflags = (IN6_IFF_NOTREADY|IN6_IFF_ANYCAST) & ~IN6_IFF_TENTATIVE; + if ((ia = in6ifa_ifpforlinklocal(ifp, ignflags)) == NULL) return; + if ((ia->ia6_flags & IN6_IFF_TENTATIVE)) + ia = NULL; /* * Allocate mbufs to store ip6 header and MLD header. @@ -409,6 +417,7 @@ mld6_sendpkt(in6m, type, dst) } mh->m_next = md; + mh->m_pkthdr.rcvif = NULL; mh->m_pkthdr.len = sizeof(struct ip6_hdr) + sizeof(struct mld6_hdr); mh->m_len = sizeof(struct ip6_hdr); MH_ALIGN(mh, sizeof(struct ip6_hdr)); @@ -421,7 +430,7 @@ mld6_sendpkt(in6m, type, dst) /* ip6_plen will be set later */ ip6->ip6_nxt = IPPROTO_ICMPV6; /* ip6_hlim will be set by im6o.im6o_multicast_hlim */ - ip6->ip6_src = ia->ia_addr.sin6_addr; + ip6->ip6_src = ia ? ia->ia_addr.sin6_addr : in6addr_any; ip6->ip6_dst = dst ? *dst : in6m->in6m_addr; /* fill in the MLD header */ @@ -437,7 +446,7 @@ mld6_sendpkt(in6m, type, dst) if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld6_addr)) mldh->mld6_addr.s6_addr16[1] = 0; /* XXX */ mldh->mld6_cksum = in6_cksum(mh, IPPROTO_ICMPV6, sizeof(struct ip6_hdr), - sizeof(struct mld6_hdr)); + sizeof(struct mld6_hdr)); /* construct multicast option */ bzero(&im6o, sizeof(im6o)); @@ -452,20 +461,18 @@ mld6_sendpkt(in6m, type, dst) /* increment output statictics */ icmp6stat.icp6s_outhist[type]++; - - ip6_output(mh, &ip6_opts, NULL, 0, &im6o, &outif); - if (outif) { - icmp6_ifstat_inc(outif, ifs6_out_msg); - switch (type) { - case MLD6_LISTENER_QUERY: - icmp6_ifstat_inc(outif, ifs6_out_mldquery); - break; - case MLD6_LISTENER_REPORT: - icmp6_ifstat_inc(outif, ifs6_out_mldreport); - break; - case MLD6_LISTENER_DONE: - icmp6_ifstat_inc(outif, ifs6_out_mlddone); - break; - } + icmp6_ifstat_inc(ifp, ifs6_out_msg); + switch (type) { + case MLD6_LISTENER_QUERY: + icmp6_ifstat_inc(ifp, ifs6_out_mldquery); + break; + case MLD6_LISTENER_REPORT: + icmp6_ifstat_inc(ifp, ifs6_out_mldreport); + break; + case MLD6_LISTENER_DONE: + icmp6_ifstat_inc(ifp, ifs6_out_mlddone); + break; } + + ip6_output(mh, &ip6_opts, NULL, ia ? 0 : IPV6_UNSPECSRC, &im6o, NULL); } diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c index b4f788b40a1..04d8d52d50f 100644 --- a/sys/netinet6/nd6.c +++ b/sys/netinet6/nd6.c @@ -1,5 +1,5 @@ -/* $OpenBSD: nd6.c,v 1.47 2002/06/07 17:04:19 itojun Exp $ */ -/* $KAME: nd6.c,v 1.151 2001/06/19 14:24:41 sumikawa Exp $ */ +/* $OpenBSD: nd6.c,v 1.48 2002/06/08 21:22:03 itojun Exp $ */ +/* $KAME: nd6.c,v 1.280 2002/06/08 19:52:07 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -60,7 +60,6 @@ #include <netinet/ip6.h> #include <netinet6/ip6_var.h> #include <netinet6/nd6.h> -#include <netinet6/in6_prefix.h> #include <netinet/icmp6.h> #define ND6_SLOWTIMER_INTERVAL (60 * 60) /* 1 hour */ @@ -100,11 +99,15 @@ static struct sockaddr_in6 all1_sa; static void nd6_setmtu0(struct ifnet *, struct nd_ifinfo *); static void nd6_slowtimo(void *); +static struct llinfo_nd6 *nd6_free(struct rtentry *, int); struct timeout nd6_slowtimo_ch; struct timeout nd6_timer_ch; extern struct timeout in6_tmpaddrtimer_ch; +static int fill_drlist(void *, size_t *, size_t); +static int fill_prlist(void *, size_t *, size_t); + void nd6_init() { @@ -146,7 +149,12 @@ nd6_ifattach(ifp) nd->basereachable = REACHABLE_TIME; nd->reachable = ND_COMPUTE_RTIME(nd->basereachable); nd->retrans = RETRANS_TIMER; - nd->flags = ND6_IFF_PERFORMNUD; + /* + * Note that the default value of ip6_accept_rtadv is 0, which means + * we won't accept RAs by default even if we set ND6_IFF_ACCEPT_RTADV + * here. + */ + nd->flags = (ND6_IFF_PERFORMNUD | ND6_IFF_ACCEPT_RTADV); /* XXX: we cannot call nd6_setmtu since ifp is not fully initialized */ nd6_setmtu0(ifp, nd); @@ -194,7 +202,7 @@ nd6_setmtu0(ifp, ndi) * Decreasing the interface MTU under IPV6 minimum MTU may cause * undesirable situation. We thus notify the operator of the change * explicitly. The check for omaxmtu is necessary to restrict the - * log to the case of changing the MTU, not initializing it. + * log to the case of changing the MTU, not initializing it. */ if (omaxmtu >= IPV6_MMTU && ndi->maxmtu < IPV6_MMTU) { log(LOG_NOTICE, "nd6_setmtu0: " @@ -368,17 +376,17 @@ nd6_timer(ignored_arg) struct nd_defrouter *dr; struct nd_prefix *pr; long time_second = time.tv_sec; - - s = splnet(); + struct ifnet *ifp; + struct in6_ifaddr *ia6, *nia6; + struct in6_addrlifetime *lt6; + s = splnet(); timeout_set(&nd6_timer_ch, nd6_timer, NULL); timeout_add(&nd6_timer_ch, nd6_prune * hz); ln = llinfo_nd6.ln_next; - /* XXX BSD/OS separates this code -- itojun */ while (ln && ln != &llinfo_nd6) { struct rtentry *rt; - struct ifnet *ifp; struct sockaddr_in6 *dst; struct llinfo_nd6 *next = ln->ln_next; /* XXX: used for the DELAY case only: */ @@ -416,25 +424,24 @@ nd6_timer(ignored_arg) ln->ln_expire = time_second + ND6_RETRANS_SEC(ND_IFINFO(ifp)->retrans); nd6_ns_output(ifp, NULL, &dst->sin6_addr, - ln, 0); + ln, 0); } else { struct mbuf *m = ln->ln_hold; if (m) { - if (rt->rt_ifp) { - /* - * Fake rcvif to make ICMP error - * more helpful in diagnosing - * for the receiver. - * XXX: should we consider - * older rcvif? - */ - m->m_pkthdr.rcvif = rt->rt_ifp; - } + /* + * Fake rcvif to make the ICMP error + * more helpful in diagnosing for the + * receiver. + * XXX: should we consider + * older rcvif? + */ + m->m_pkthdr.rcvif = rt->rt_ifp; + icmp6_error(m, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADDR, 0); ln->ln_hold = NULL; } - next = nd6_free(rt); + next = nd6_free(rt, 0); } break; case ND6_LLINFO_REACHABLE: @@ -447,7 +454,7 @@ nd6_timer(ignored_arg) case ND6_LLINFO_STALE: /* Garbage Collection(RFC 2461 5.3) */ if (ln->ln_expire) - next = nd6_free(rt); + next = nd6_free(rt, 1); break; case ND6_LLINFO_DELAY: @@ -458,8 +465,7 @@ nd6_timer(ignored_arg) ln->ln_expire = time_second + ND6_RETRANS_SEC(ndi->retrans); nd6_ns_output(ifp, &dst->sin6_addr, - &dst->sin6_addr, - ln, 0); + &dst->sin6_addr, ln, 0); } else { ln->ln_state = ND6_LLINFO_STALE; /* XXX */ ln->ln_expire = time_second + nd6_gctimer; @@ -471,14 +477,15 @@ nd6_timer(ignored_arg) ln->ln_expire = time_second + ND6_RETRANS_SEC(ND_IFINFO(ifp)->retrans); nd6_ns_output(ifp, &dst->sin6_addr, - &dst->sin6_addr, ln, 0); - } else - next = nd6_free(rt); + &dst->sin6_addr, ln, 0); + } else { + next = nd6_free(rt, 0); + } break; } ln = next; } - + /* expire default router list */ dr = TAILQ_FIRST(&nd_defrouter); while (dr) { @@ -487,49 +494,51 @@ nd6_timer(ignored_arg) t = TAILQ_NEXT(dr, dr_entry); defrtrlist_del(dr); dr = t; - } else + } else { dr = TAILQ_NEXT(dr, dr_entry); + } } - pr = nd_prefix.lh_first; - while (pr) { - struct in6_ifaddr *ia6; - struct in6_addrlifetime *lt6; - if (IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr)) - ia6 = NULL; - else - ia6 = in6ifa_ifpwithaddr(pr->ndpr_ifp, &pr->ndpr_addr); - - if (ia6) { - /* check address lifetime */ - lt6 = &ia6->ia6_lifetime; - if (lt6->ia6t_preferred && lt6->ia6t_preferred < time_second) - ia6->ia6_flags |= IN6_IFF_DEPRECATED; - if (lt6->ia6t_expire && lt6->ia6t_expire < time_second) { - if (!IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr)) - in6_ifdel(pr->ndpr_ifp, &pr->ndpr_addr); - /* xxx ND_OPT_PI_FLAG_ONLINK processing */ - } + /* + * expire interface addresses. + * in the past the loop was inside prefix expiry processing. + * However, from a stricter speci-confrmance standpoint, we should + * rather separate address lifetimes and prefix lifetimes. + */ + for (ia6 = in6_ifaddr; ia6; ia6 = nia6) { + nia6 = ia6->ia_next; + /* check address lifetime */ + lt6 = &ia6->ia6_lifetime; + if (IFA6_IS_INVALID(ia6)) { + in6_purgeaddr(&ia6->ia_ifa); } + if (IFA6_IS_DEPRECATED(ia6)) { + ia6->ia6_flags |= IN6_IFF_DEPRECATED; + } else { + /* + * A new RA might have made a deprecated address + * preferred. + */ + ia6->ia6_flags &= ~IN6_IFF_DEPRECATED; + } + } + /* expire prefix list */ + pr = nd_prefix.lh_first; + while (pr) { /* * check prefix lifetime. * since pltime is just for autoconf, pltime processing for * prefix is not necessary. - * - * we offset expire time by NDPR_KEEP_EXPIRE, so that we - * can use the old prefix information to validate the - * next prefix information to come. See prelist_update() - * for actual validation. */ - if (pr->ndpr_expire - && pr->ndpr_expire + NDPR_KEEP_EXPIRED < time_second) { + if (pr->ndpr_vltime != ND6_INFINITE_LIFETIME && + time_second - pr->ndpr_lastupdate > pr->ndpr_vltime) { struct nd_prefix *t; t = pr->ndpr_next; /* * address expiration and prefix expiration are - * separate. NEVER perform in6_ifdel here. + * separate. NEVER perform in6_purgeaddr here. */ prelist_remove(pr); @@ -549,21 +558,28 @@ nd6_purge(ifp) struct ifnet *ifp; { struct llinfo_nd6 *ln, *nln; - struct nd_defrouter *dr, *ndr, drany; + struct nd_defrouter *dr, *ndr; struct nd_prefix *pr, *npr; - /* Nuke default router list entries toward ifp */ - 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 = TAILQ_NEXT(dr, dr_entry); dr; dr = ndr) { - ndr = TAILQ_NEXT(dr, dr_entry); - if (dr->ifp == ifp) - defrtrlist_del(dr); - } - dr = TAILQ_FIRST(&nd_defrouter); + /* + * Nuke default router list entries toward ifp. + * We defer removal of default router list entries that is installed + * in the routing table, in order to keep additional side effects as + * small as possible. + */ + for (dr = TAILQ_FIRST(&nd_defrouter); dr; dr = ndr) { + ndr = TAILQ_NEXT(dr, dr_entry); + if (dr->installed) + continue; + + if (dr->ifp == ifp) + defrtrlist_del(dr); + } + for (dr = TAILQ_FIRST(&nd_defrouter); dr; dr = ndr) { + ndr = TAILQ_NEXT(dr, dr_entry); + if (!dr->installed) + continue; + if (dr->ifp == ifp) defrtrlist_del(dr); } @@ -572,8 +588,14 @@ nd6_purge(ifp) for (pr = nd_prefix.lh_first; pr; pr = npr) { npr = pr->ndpr_next; if (pr->ndpr_ifp == ifp) { - if (!IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr)) - in6_ifdel(pr->ndpr_ifp, &pr->ndpr_addr); + /* + * Previously, pr->ndpr_addr is removed as well, + * but I strongly believe we don't have to do it. + * nd6_purge() is only called from in6_ifdetach(), + * which removes all the associated interface addresses + * by itself. + * (jinmei@kame.net 20010129) + */ prelist_remove(pr); } } @@ -584,8 +606,6 @@ nd6_purge(ifp) if (!ip6_forwarding && ip6_accept_rtadv) { /* XXX: too restrictive? */ /* refresh default router list */ - bzero(&drany, sizeof(drany)); - defrouter_delreq(&drany, 0); defrouter_select(); } @@ -606,7 +626,7 @@ nd6_purge(ifp) rt->rt_gateway->sa_family == AF_LINK) { sdl = (struct sockaddr_dl *)rt->rt_gateway; if (sdl->sdl_index == ifp->if_index) - nln = nd6_free(rt); + nln = nd6_free(rt, 0); } ln = nln; } @@ -625,6 +645,7 @@ nd6_lookup(addr6, create, ifp) sin6.sin6_len = sizeof(struct sockaddr_in6); sin6.sin6_family = AF_INET6; sin6.sin6_addr = *addr6; + rt = rtalloc1((struct sockaddr *)&sin6, create); if (rt && (rt->rt_flags & RTF_LLINFO) == 0) { /* @@ -650,7 +671,7 @@ nd6_lookup(addr6, create, ifp) * be covered by our own prefix. */ struct ifaddr *ifa = - ifaof_ifpforaddr((struct sockaddr *)&sin6, ifp); + ifaof_ifpforaddr((struct sockaddr *)&sin6, ifp); if (ifa == NULL) return(NULL); @@ -661,21 +682,22 @@ nd6_lookup(addr6, create, ifp) * called in rtrequest via ifa->ifa_rtrequest. */ if ((e = rtrequest(RTM_ADD, (struct sockaddr *)&sin6, - ifa->ifa_addr, - (struct sockaddr *)&all1_sa, - (ifa->ifa_flags | - RTF_HOST | RTF_LLINFO) & - ~RTF_CLONING, - &rt)) != 0) + ifa->ifa_addr, (struct sockaddr *)&all1_sa, + (ifa->ifa_flags | RTF_HOST | RTF_LLINFO) & + ~RTF_CLONING, &rt)) != 0) { +#if 0 log(LOG_ERR, "nd6_lookup: failed to add route for a " "neighbor(%s), errno=%d\n", ip6_sprintf(addr6), e); +#endif + return(NULL); + } if (rt == NULL) return(NULL); if (rt->rt_llinfo) { struct llinfo_nd6 *ln = - (struct llinfo_nd6 *)rt->rt_llinfo; + (struct llinfo_nd6 *)rt->rt_llinfo; ln->ln_state = ND6_LLINFO_NOSTATE; } } else @@ -684,20 +706,25 @@ nd6_lookup(addr6, create, ifp) rt->rt_refcnt--; /* * Validation for the entry. + * Note that the check for rt_llinfo is necessary because a cloned + * route from a parent route that has the L flag (e.g. the default + * route to a p2p interface) may have the flag, too, while the + * destination is not actually a neighbor. * XXX: we can't use rt->rt_ifp to check for the interface, since * it might be the loopback interface if the entry is for our * own address on a non-loopback interface. Instead, we should - * use rt->rt_ifa->ifa_ifp, which would specify the REAL interface. + * use rt->rt_ifa->ifa_ifp, which would specify the REAL + * interface. */ if ((rt->rt_flags & RTF_GATEWAY) || (rt->rt_flags & RTF_LLINFO) == 0 || - rt->rt_gateway->sa_family != AF_LINK || + rt->rt_gateway->sa_family != AF_LINK || rt->rt_llinfo == NULL || (ifp && rt->rt_ifa->ifa_ifp != ifp)) { if (create) { - log(LOG_DEBUG, "nd6_lookup: failed to lookup %s (if = %s)\n", + log(LOG_DEBUG, + "nd6_lookup: failed to lookup %s (if = %s)\n", ip6_sprintf(addr6), ifp ? ifp->if_xname : "unspec"); - /* xxx more logs... kazu */ } - return(0); + return(NULL); } return(rt); } @@ -711,38 +738,43 @@ nd6_is_addr_neighbor(addr, ifp) struct sockaddr_in6 *addr; struct ifnet *ifp; { - struct ifaddr *ifa; - int i; - -#define IFADDR6(a) ((((struct in6_ifaddr *)(a))->ia_addr).sin6_addr) -#define IFMASK6(a) ((((struct in6_ifaddr *)(a))->ia_prefixmask).sin6_addr) + struct nd_prefix *pr; + struct rtentry *rt; /* * A link-local address is always a neighbor. * XXX: we should use the sin6_scope_id field rather than the embedded * interface index. + * XXX: a link does not necessarily specify a single interface. */ if (IN6_IS_ADDR_LINKLOCAL(&addr->sin6_addr) && ntohs(*(u_int16_t *)&addr->sin6_addr.s6_addr[2]) == ifp->if_index) return(1); /* - * If the address matches one of our addresses, - * it should be a neighbor. + * If the address matches one of our on-link prefixes, it should be a + * neighbor. */ - for (ifa = ifp->if_addrlist.tqh_first; - ifa; - ifa = ifa->ifa_list.tqe_next) - { - if (ifa->ifa_addr->sa_family != AF_INET6) - next: continue; - - for (i = 0; i < 4; i++) { - if ((IFADDR6(ifa).s6_addr32[i] ^ - addr->sin6_addr.s6_addr32[i]) & - IFMASK6(ifa).s6_addr32[i]) - goto next; - } + for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) { + if (pr->ndpr_ifp != ifp) + continue; + + if (!(pr->ndpr_stateflags & NDPRF_ONLINK)) + continue; + + if (IN6_ARE_MASKED_ADDR_EQUAL(&pr->ndpr_prefix.sin6_addr, + &addr->sin6_addr, &pr->ndpr_mask)) + return (1); + } + + /* + * If the default router list is empty, all addresses are regarded + * as on-link, and thus, as a neighbor. + * XXX: we restrict the condition to hosts, because routers usually do + * not have the "default router list". + */ + if (!ip6_forwarding && TAILQ_FIRST(&nd_defrouter) == NULL && + nd6_defifindex == ifp->if_index) { return(1); } @@ -750,36 +782,57 @@ nd6_is_addr_neighbor(addr, ifp) * Even if the address matches none of our addresses, it might be * in the neighbor cache. */ - if (nd6_lookup(&addr->sin6_addr, 0, ifp)) + if ((rt = nd6_lookup(&addr->sin6_addr, 0, ifp)) != NULL) return(1); return(0); -#undef IFADDR6 -#undef IFMASK6 } /* * Free an nd6 llinfo entry. + * Since the function would cause significant changes in the kernel, DO NOT + * make it global, unless you have a strong reason for the change, and are sure + * that the change is safe. */ -struct llinfo_nd6 * -nd6_free(rt) +static struct llinfo_nd6 * +nd6_free(rt, gc) struct rtentry *rt; + int gc; { struct llinfo_nd6 *ln = (struct llinfo_nd6 *)rt->rt_llinfo, *next; 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? + * we used to have pfctlinput(PRC_HOSTDEAD) here. + * even though it is not harmful, it was not really necessary. */ - 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, - rt->rt_ifp); + rt->rt_ifp); + + if (dr != NULL && dr->expire && + ln->ln_state == ND6_LLINFO_STALE && gc) { + /* + * If the reason for the deletion is just garbage + * collection, and the neighbor is an active default + * router, do not delete it. Instead, reset the GC + * timer using the router's lifetime. + * Simply deleting the entry would affect default + * router selection, which is not necessarily a good + * thing, especially when we're using router preference + * values. + * XXX: the check for ln_state would be redundant, + * but we intentionally keep it just in case. + */ + ln->ln_expire = dr->expire; + splx(s); + return(ln->ln_next); + } + if (ln->ln_router || dr) { /* * rt6_flush must be called whether or not the neighbor @@ -805,20 +858,18 @@ nd6_free(rt) */ 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(); - } + /* + * Since defrouter_select() does not affect the + * on-link determination and MIP6 needs the check + * before the default router selection, we perform + * the check now. + */ pfxlist_onlink_check(); + + /* + * refresh default router list + */ + defrouter_select(); } splx(s); } @@ -837,7 +888,7 @@ nd6_free(rt) * cached routes. */ rtrequest(RTM_DELETE, rt_key(rt), (struct sockaddr *)0, - rt_mask(rt), 0, (struct rtentry **)0); + rt_mask(rt), 0, (struct rtentry **)0); return(next); } @@ -906,9 +957,42 @@ nd6_rtrequest(req, rt, info) struct ifnet *ifp = rt->rt_ifp; struct ifaddr *ifa; long time_second = time.tv_sec; + int mine = 0; + + if ((rt->rt_flags & RTF_GATEWAY) != 0) + return; + + if (nd6_need_cache(ifp) == 0 && (rt->rt_flags & RTF_HOST) == 0) { + /* + * This is probably an interface direct route for a link + * which does not need neighbor caches (e.g. fe80::%lo0/64). + * We do not need special treatment below for such a route. + * Moreover, the RTF_LLINFO flag which would be set below + * would annoy the ndp(8) command. + */ + return; + } - if (rt->rt_flags & RTF_GATEWAY) + if (req == RTM_RESOLVE && + (nd6_need_cache(ifp) == 0 || /* stf case */ + !nd6_is_addr_neighbor((struct sockaddr_in6 *)rt_key(rt), ifp))) { + /* + * FreeBSD and BSD/OS often make a cloned host route based + * on a less-specific route (e.g. the default route). + * If the less specific route does not have a "gateway" + * (this is the case when the route just goes to a p2p or an + * stf interface), we'll mistakenly make a neighbor cache for + * the host route, and will see strange neighbor solicitation + * for the corresponding destination. In order to avoid the + * confusion, we check if the destination of the route is + * a neighbor in terms of neighbor discovery, and stop the + * process if not. Additionally, we remove the LLINFO flag + * so that ndp(8) will not try to get the neighbor information + * of the destination. + */ + rt->rt_flags &= ~RTF_LLINFO; return; + } switch (req) { case RTM_ADD: @@ -937,13 +1021,13 @@ nd6_rtrequest(req, rt, info) if (ln && ln->ln_expire == 0) { /* kludge for desktops */ #if 0 - printf("nd6_request: time.tv_sec is zero; " + printf("nd6_rtrequest: time.tv_sec is zero; " "treat it as 1\n"); #endif ln->ln_expire = 1; } #endif - if (rt->rt_flags & RTF_CLONING) + if ((rt->rt_flags & RTF_CLONING) != 0) break; } /* @@ -975,7 +1059,7 @@ nd6_rtrequest(req, rt, info) #endif /* FALLTHROUGH */ case RTM_RESOLVE: - if ((ifp->if_flags & IFF_POINTOPOINT) == 0) { + if ((ifp->if_flags & (IFF_POINTOPOINT | IFF_LOOPBACK)) == 0) { /* * Address resolution isn't necessary for a point to * point link, so we can skip this test for a p2p link. @@ -1034,12 +1118,13 @@ nd6_rtrequest(req, rt, info) * to the interface. */ ifa = (struct ifaddr *)in6ifa_ifpwithaddr(rt->rt_ifp, - &SIN6(rt_key(rt))->sin6_addr); + &SIN6(rt_key(rt))->sin6_addr); if (ifa) { caddr_t macp = nd6_ifptomac(ifp); ln->ln_expire = 0; ln->ln_state = ND6_LLINFO_REACHABLE; ln->ln_byhint = 0; + mine = 1; if (macp) { Bcopy(macp, LLADDR(SDL(gate)), ifp->if_addrlen); SDL(gate)->sdl_alen = ifp->if_addrlen; @@ -1077,10 +1162,11 @@ nd6_rtrequest(req, rt, info) 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); + if (in6_addmulti(&llsol, ifp, &error)) { + nd6log((LOG_ERR, "%s: failed to join " + "%s (errno=%d)\n", ifp->if_xname, + ip6_sprintf(&llsol), error)); + } } } break; @@ -1117,65 +1203,6 @@ nd6_rtrequest(req, rt, info) } } -void -nd6_p2p_rtrequest(req, rt, info) - int req; - struct rtentry *rt; - struct rt_addrinfo *info; /* xxx unused */ -{ - struct sockaddr *gate = rt->rt_gateway; - static struct sockaddr_dl null_sdl = {sizeof(null_sdl), AF_LINK}; - struct ifnet *ifp = rt->rt_ifp; - struct ifaddr *ifa; - - if (rt->rt_flags & RTF_GATEWAY) - return; - - switch (req) { - case RTM_ADD: - /* - * There is no backward compatibility :) - * - * if ((rt->rt_flags & RTF_HOST) == 0 && - * SIN(rt_mask(rt))->sin_addr.s_addr != 0xffffffff) - * rt->rt_flags |= RTF_CLONING; - */ - if (rt->rt_flags & RTF_CLONING) { - /* - * Case 1: This route should come from - * a route to interface. - */ - rt_setgate(rt, rt_key(rt), - (struct sockaddr *)&null_sdl); - gate = rt->rt_gateway; - SDL(gate)->sdl_type = ifp->if_type; - SDL(gate)->sdl_index = ifp->if_index; - break; - } - /* Announce a new entry if requested. */ - 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, NULL); - /* FALLTHROUGH */ - case RTM_RESOLVE: - /* - * check if rt_key(rt) is one of my address assigned - * to the interface. - */ - ifa = (struct ifaddr *)in6ifa_ifpwithaddr(rt->rt_ifp, - &SIN6(rt_key(rt))->sin6_addr); - if (ifa) { - if (nd6_useloopback) { - rt->rt_ifp = lo0ifp; /*XXX*/ - } - } - break; - } -} - int nd6_ioctl(cmd, data, ifp) u_long cmd; @@ -1183,11 +1210,11 @@ nd6_ioctl(cmd, data, ifp) struct ifnet *ifp; { struct in6_drlist *drl = (struct in6_drlist *)data; - struct in6_prlist *prl = (struct in6_prlist *)data; + struct in6_oprlist *oprl = (struct in6_oprlist *)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_defrouter *dr; struct nd_prefix *pr; struct rtentry *rt; int i = 0, error = 0; @@ -1195,6 +1222,9 @@ nd6_ioctl(cmd, data, ifp) switch (cmd) { case SIOCGDRLST_IN6: + /* + * obsolete API, use sysctl under net.inet6.icmp6 + */ bzero(drl, sizeof(*drl)); s = splnet(); dr = TAILQ_FIRST(&nd_defrouter); @@ -1220,30 +1250,37 @@ nd6_ioctl(cmd, data, ifp) break; case SIOCGPRLST_IN6: /* + * obsolete API, use sysctl under net.inet6.icmp6 + * + * XXX the structure in6_prlist was changed in backward- + * incompatible manner. in6_oprlist is used for SIOCGPRLST_IN6, + * in6_prlist is used for nd6_sysctl() - fill_prlist(). + */ + /* * 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)); + bzero(oprl, sizeof(*oprl)); s = splnet(); pr = nd_prefix.lh_first; while (pr && i < PRLSTSIZ) { struct nd_pfxrouter *pfr; int j; - prl->prefix[i].prefix = pr->ndpr_prefix.sin6_addr; - prl->prefix[i].raflags = pr->ndpr_raf; - prl->prefix[i].prefixlen = pr->ndpr_plen; - prl->prefix[i].vltime = pr->ndpr_vltime; - prl->prefix[i].pltime = pr->ndpr_pltime; - prl->prefix[i].if_index = pr->ndpr_ifp->if_index; - prl->prefix[i].expire = pr->ndpr_expire; + oprl->prefix[i].prefix = pr->ndpr_prefix.sin6_addr; + oprl->prefix[i].raflags = pr->ndpr_raf; + oprl->prefix[i].prefixlen = pr->ndpr_plen; + oprl->prefix[i].vltime = pr->ndpr_vltime; + oprl->prefix[i].pltime = pr->ndpr_pltime; + oprl->prefix[i].if_index = pr->ndpr_ifp->if_index; + oprl->prefix[i].expire = pr->ndpr_expire; pfr = pr->ndpr_advrtrs.lh_first; j = 0; while(pfr) { if (j < DRLSTSIZ) { -#define RTRADDR prl->prefix[i].advrtr[j] +#define RTRADDR oprl->prefix[i].advrtr[j] RTRADDR = pfr->router->rtaddr; if (IN6_IS_ADDR_LINKLOCAL(&RTRADDR)) { /* XXX: hack for KAME */ @@ -1259,31 +1296,12 @@ nd6_ioctl(cmd, data, ifp) j++; pfr = pfr->pfr_next; } - prl->prefix[i].advrtrs = j; - prl->prefix[i].origin = PR_ORIG_RA; + oprl->prefix[i].advrtrs = j; + oprl->prefix[i].origin = PR_ORIG_RA; i++; pr = pr->ndpr_next; } - { - struct rr_prefix *rpp; - - for (rpp = LIST_FIRST(&rr_prefix); rpp; - rpp = LIST_NEXT(rpp, rp_entry)) { - if (i >= PRLSTSIZ) - break; - prl->prefix[i].prefix = rpp->rp_prefix.sin6_addr; - prl->prefix[i].raflags = rpp->rp_raf; - prl->prefix[i].prefixlen = rpp->rp_plen; - prl->prefix[i].vltime = rpp->rp_vltime; - prl->prefix[i].pltime = rpp->rp_pltime; - 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; @@ -1306,54 +1324,61 @@ nd6_ioctl(cmd, data, ifp) ND_IFINFO(ifp)->flags = ndi->ndi.flags; break; case SIOCSNDFLUSH_IN6: /* XXX: the ioctl name is confusing... */ - /* flush default router list */ - /* - * xxx sumikawa: should not delete route if default - * route equals to the top of default router list - */ - bzero(&any, sizeof(any)); - defrouter_delreq(&any, 0); + /* sync kernel routing table with the default router list */ + defrouter_reset(); defrouter_select(); - /* xxx sumikawa: flush prefix list */ break; case SIOCSPFXFLUSH_IN6: - { + { /* flush all the prefix advertised by routers */ struct nd_prefix *pr, *next; +#ifdef __NetBSD__ + s = splsoftnet(); +#else s = splnet(); - +#endif for (pr = nd_prefix.lh_first; pr; pr = next) { + struct in6_ifaddr *ia, *ia_next; + next = pr->ndpr_next; - if (!IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr)) - in6_ifdel(pr->ndpr_ifp, &pr->ndpr_addr); + + if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr)) + continue; /* XXX */ + + /* do we really have to remove addresses as well? */ + for (ia = in6_ifaddr; ia; ia = ia_next) { + /* ia might be removed. keep the next ptr. */ + ia_next = ia->ia_next; + + if ((ia->ia6_flags & IN6_IFF_AUTOCONF) == 0) + continue; + + if (ia->ia6_ndpr == pr) + in6_purgeaddr(&ia->ia_ifa); + } prelist_remove(pr); } splx(s); break; - } + } case SIOCSRTRFLUSH_IN6: - { + { /* flush all the default routers */ struct nd_defrouter *dr, *next; s = splnet(); - 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 = TAILQ_NEXT(dr, dr_entry); dr; dr = next) { - next = TAILQ_NEXT(dr, dr_entry); - defrtrlist_del(dr); - } - defrtrlist_del(TAILQ_FIRST(&nd_defrouter)); + defrouter_reset(); + for (dr = TAILQ_FIRST(&nd_defrouter); dr; dr = next) { + next = TAILQ_NEXT(dr, dr_entry); + defrtrlist_del(dr); } + defrouter_select(); splx(s); break; - } + } case SIOCGNBRINFO_IN6: - { + { struct llinfo_nd6 *ln; struct in6_addr nb_addr = nbi->addr; /* make local for safety */ @@ -1370,21 +1395,20 @@ nd6_ioctl(cmd, data, ifp) } s = splnet(); - - if ((rt = nd6_lookup(&nb_addr, 0, ifp)) == NULL) { + if ((rt = nd6_lookup(&nb_addr, 0, ifp)) == NULL || + (ln = (struct llinfo_nd6 *)rt->rt_llinfo) == NULL) { error = EINVAL; splx(s); break; } - ln = (struct llinfo_nd6 *)rt->rt_llinfo; nbi->state = ln->ln_state; nbi->asked = ln->ln_asked; nbi->isrouter = ln->ln_router; nbi->expire = ln->ln_expire; splx(s); - + break; - } + } case SIOCGDEFIFACE_IN6: /* XXX: should be implemented as a sysctl? */ ndif->ifindex = nd6_defifindex; break; @@ -1458,7 +1482,7 @@ nd6_cache_lladdr(ifp, from, lladdr, lladdrlen, type, code) return NULL; if ((rt->rt_flags & (RTF_GATEWAY | RTF_LLINFO)) != RTF_LLINFO) { fail: - (void)nd6_free(rt); + (void)nd6_free(rt, 0); return NULL; } ln = (struct llinfo_nd6 *)rt->rt_llinfo; @@ -1500,8 +1524,8 @@ fail: } if (!is_newentry) { - if ((!olladdr && lladdr) /* (3) */ - || (olladdr && lladdr && llchange)) { /* (5) */ + if ((!olladdr && lladdr) || /* (3) */ + (olladdr && lladdr && llchange)) { /* (5) */ do_update = 1; newstate = ND6_LLINFO_STALE; } else /* (1-2,4) */ @@ -1535,8 +1559,7 @@ fail: * set the 2nd argument as the 1st one. */ nd6_output(ifp, ifp, ln->ln_hold, - (struct sockaddr_in6 *)rt_key(rt), - rt); + (struct sockaddr_in6 *)rt_key(rt), rt); ln->ln_hold = NULL; } } else if (ln->ln_state == ND6_LLINFO_INCOMPLETE) { @@ -1603,8 +1626,8 @@ fail: /* * Mark an entry with lladdr as a router. */ - if ((!is_newentry && (olladdr || lladdr)) /* (2-5) */ - || (is_newentry && lladdr)) { /* (7) */ + if ((!is_newentry && (olladdr || lladdr)) || /* (2-5) */ + (is_newentry && lladdr)) { /* (7) */ ln->ln_router = 1; } break; @@ -1621,6 +1644,9 @@ fail: * defrtrlist_update called the function as well. However, I believe * we can compromise the overhead, since it only happens the first * time. + * XXX: although defrouter_select() should not have a bad effect + * for those are not autoconfigured hosts, we explicitly avoid such + * cases for safety. */ if (do_update && ln->ln_router && !ip6_forwarding && ip6_accept_rtadv) defrouter_select(); @@ -1678,36 +1704,22 @@ nd6_output(ifp, origifp, m0, dst, rt0) if (IN6_IS_ADDR_MULTICAST(&dst->sin6_addr)) goto sendpkt; - /* - * XXX: we currently do not make neighbor cache on any interface - * other than ARCnet, Ethernet, FDDI and GIF. - * - * RFC2893 says: - * - unidirectional tunnels needs no ND - */ - switch (ifp->if_type) { - case IFT_ARCNET: - case IFT_ETHER: - case IFT_FDDI: - case IFT_GIF: /* XXX need more cases? */ - break; - default: + if (nd6_need_cache(ifp) == 0) goto sendpkt; - } /* * next hop determination. This routine is derived from ether_outpout. */ if (rt) { if ((rt->rt_flags & RTF_UP) == 0) { - if ((rt0 = rt = rtalloc1((struct sockaddr *)dst, 1)) != - NULL) + if ((rt0 = rt = rtalloc1((struct sockaddr *)dst, + 1)) != NULL) { rt->rt_refcnt--; if (rt->rt_ifp != ifp) { /* XXX: loop care? */ return nd6_output(ifp, origifp, m0, - dst, rt); + dst, rt); } } else senderr(EHOSTUNREACH); @@ -1719,7 +1731,7 @@ nd6_output(ifp, origifp, m0, dst, rt0) /* * We skip link-layer address resolution and NUD * if the gateway is not a neighbor from ND point - * of view, regardless the value of nd_ifinfo.flags. + * of view, regardless of the value of nd_ifinfo.flags. * The second condition is a bit tricky; we skip * if the gateway is our own address, which is * sometimes used to install a route to a p2p link. @@ -1741,7 +1753,8 @@ nd6_output(ifp, origifp, m0, dst, rt0) goto lookup; if (((rt = rt->rt_gwroute)->rt_flags & RTF_UP) == 0) { rtfree(rt); rt = rt0; - lookup: rt->rt_gwroute = rtalloc1(rt->rt_gateway, 1); + lookup: + rt->rt_gwroute = rtalloc1(rt->rt_gateway, 1); if ((rt = rt->rt_gwroute) == 0) senderr(EHOSTUNREACH); } @@ -1827,7 +1840,7 @@ nd6_output(ifp, origifp, m0, dst, rt0) * solicitation issued in nd6_timer() may be less than the specified * retransmission time. This should not be a problem from a practical * point of view, because we'll typically see an immediate response - * from the neighbor, which suppresses the succeeding solicitations. + * from the neighbor, which suppresses the succeeding solicitations. */ if (ln->ln_expire && ln->ln_asked == 0) { ln->ln_asked++; @@ -1836,7 +1849,7 @@ nd6_output(ifp, origifp, m0, dst, rt0) nd6_ns_output(ifp, NULL, &dst->sin6_addr, ln, 0); } return(0); - + sendpkt: #ifdef IPSEC /* @@ -1874,10 +1887,36 @@ nd6_output(ifp, origifp, m0, dst, rt0) if (m) m_freem(m); return (error); -} +} #undef senderr int +nd6_need_cache(ifp) + struct ifnet *ifp; +{ + /* + * XXX: we currently do not make neighbor cache on any interface + * other than ARCnet, Ethernet, FDDI and GIF. + * + * RFC2893 says: + * - unidirectional tunnels needs no ND + */ + switch (ifp->if_type) { + case IFT_ARCNET: + case IFT_ETHER: + case IFT_FDDI: + case IFT_IEEE1394: + case IFT_PROPVIRTUAL: + case IFT_L2VLAN: + case IFT_IEEE80211: + case IFT_GIF: /* XXX need more cases? */ + return(1); + default: + return(0); + } +} + +int nd6_storelladdr(ifp, rt, m, dst, desten) struct ifnet *ifp; struct rtentry *rt; @@ -1890,7 +1929,7 @@ nd6_storelladdr(ifp, rt, m, dst, desten) if (m->m_flags & M_MCAST) { switch (ifp->if_type) { case IFT_ETHER: - case IFT_FDDI: + case IFT_FDDI: ETHER_MAP_IPV6_MULTICAST(&SIN6(dst)->sin6_addr, desten); return(1); @@ -1917,7 +1956,8 @@ nd6_storelladdr(ifp, rt, m, dst, desten) sdl = SDL(rt->rt_gateway); if (sdl->sdl_alen == 0) { /* this should be impossible, but we bark here for debugging */ - printf("nd6_storelladdr: sdl_alen == 0\n"); + printf("nd6_storelladdr: sdl_alen == 0, dst=%s, if=%s\n", + ip6_sprintf(&SIN6(dst)->sin6_addr), ifp->if_xname); m_freem(m); return(0); } @@ -1925,3 +1965,193 @@ nd6_storelladdr(ifp, rt, m, dst, desten) bcopy(LLADDR(sdl), desten, sdl->sdl_alen); return(1); } + +int +nd6_sysctl(name, oldp, oldlenp, newp, newlen) + int name; + void *oldp; + size_t *oldlenp; + void *newp; + size_t newlen; +{ + size_t ol, l; + int error; + + error = 0; + l = 0; + + if (newp) + return EPERM; + if (oldp && !oldlenp) + return EINVAL; + ol = oldlenp ? *oldlenp : 0; + + switch (name) { + case ICMPV6CTL_ND6_DRLIST: + error = fill_drlist(oldp, oldlenp, ol); + break; + + case ICMPV6CTL_ND6_PRLIST: + error = fill_prlist(oldp, oldlenp, ol); + break; + + default: + error = ENOPROTOOPT; + break; + } + + return(error); +} + +static int +fill_drlist(oldp, oldlenp, ol) + void *oldp; + size_t *oldlenp, ol; +{ + int error = 0, s; + struct in6_defrouter *d = NULL, *de = NULL; + struct nd_defrouter *dr; + size_t l; + + s = splnet(); + + if (oldp) { + d = (struct in6_defrouter *)oldp; + de = (struct in6_defrouter *)((caddr_t)oldp + *oldlenp); + } + l = 0; + + for (dr = TAILQ_FIRST(&nd_defrouter); dr; + dr = TAILQ_NEXT(dr, dr_entry)) { + + if (oldp && d + 1 <= de) { + bzero(d, sizeof(*d)); + d->rtaddr.sin6_family = AF_INET6; + d->rtaddr.sin6_len = sizeof(struct sockaddr_in6); + d->rtaddr.sin6_addr = dr->rtaddr; + in6_recoverscope(&d->rtaddr, &d->rtaddr.sin6_addr, + dr->ifp); + d->flags = dr->flags; + d->rtlifetime = dr->rtlifetime; + d->expire = dr->expire; + d->if_index = dr->ifp->if_index; + } + + l += sizeof(*d); + if (d) + d++; + } + + if (oldp) { + *oldlenp = l; /* (caddr_t)d - (caddr_t)oldp */ + if (l > ol) + error = ENOMEM; + } else + *oldlenp = l; + + splx(s); + + return(error); +} + +static int +fill_prlist(oldp, oldlenp, ol) + void *oldp; + size_t *oldlenp, ol; +{ + int error = 0, s; + struct nd_prefix *pr; + struct in6_prefix *p = NULL; + struct in6_prefix *pe = NULL; + size_t l; + + s = splnet(); + + if (oldp) { + p = (struct in6_prefix *)oldp; + pe = (struct in6_prefix *)((caddr_t)oldp + *oldlenp); + } + l = 0; + + for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) { + u_short advrtrs; + size_t advance; + struct sockaddr_in6 *sin6; + struct sockaddr_in6 *s6; + struct nd_pfxrouter *pfr; + + if (oldp && p + 1 <= pe) + { + bzero(p, sizeof(*p)); + sin6 = (struct sockaddr_in6 *)(p + 1); + + p->prefix = pr->ndpr_prefix; + if (in6_recoverscope(&p->prefix, + &p->prefix.sin6_addr, pr->ndpr_ifp) != 0) + log(LOG_ERR, + "scope error in prefix list (%s)\n", + ip6_sprintf(&p->prefix.sin6_addr)); + p->raflags = pr->ndpr_raf; + p->prefixlen = pr->ndpr_plen; + p->vltime = pr->ndpr_vltime; + p->pltime = pr->ndpr_pltime; + p->if_index = pr->ndpr_ifp->if_index; + if (pr->ndpr_vltime == ND6_INFINITE_LIFETIME) + p->expire = 0; + else { + time_t maxexpire; + + /* XXX: we assume time_t is signed. */ + maxexpire = (-1) & + ~(1 << ((sizeof(maxexpire) * 8) - 1)); + if (pr->ndpr_vltime < + maxexpire - pr->ndpr_lastupdate) { + p->expire = pr->ndpr_lastupdate + + pr->ndpr_vltime; + } else + p->expire = maxexpire; + } + p->refcnt = pr->ndpr_refcnt; + p->flags = pr->ndpr_stateflags; + p->origin = PR_ORIG_RA; + advrtrs = 0; + for (pfr = pr->ndpr_advrtrs.lh_first; pfr; + pfr = pfr->pfr_next) { + if ((void *)&sin6[advrtrs + 1] > (void *)pe) { + advrtrs++; + continue; + } + s6 = &sin6[advrtrs]; + s6->sin6_family = AF_INET6; + s6->sin6_len = sizeof(struct sockaddr_in6); + s6->sin6_addr = pfr->router->rtaddr; + in6_recoverscope(s6, &pfr->router->rtaddr, + pfr->router->ifp); + advrtrs++; + } + p->advrtrs = advrtrs; + } + else { + advrtrs = 0; + for (pfr = pr->ndpr_advrtrs.lh_first; pfr; + pfr = pfr->pfr_next) + advrtrs++; + } + + advance = sizeof(*p) + sizeof(*sin6) * advrtrs; + l += advance; + if (p) + p = (struct in6_prefix *)((caddr_t)p + advance); + } + + if (oldp) { + *oldlenp = l; /* (caddr_t)d - (caddr_t)oldp */ + if (l > ol) + error = ENOMEM; + } else + *oldlenp = l; + + splx(s); + + return(error); +} diff --git a/sys/netinet6/nd6.h b/sys/netinet6/nd6.h index 1e46bbfc22a..efdc22adf8a 100644 --- a/sys/netinet6/nd6.h +++ b/sys/netinet6/nd6.h @@ -1,5 +1,5 @@ -/* $OpenBSD: nd6.h,v 1.21 2002/06/07 02:33:04 itojun Exp $ */ -/* $KAME: nd6.h,v 1.93 2002/06/05 00:56:22 itojun Exp $ */ +/* $OpenBSD: nd6.h,v 1.22 2002/06/08 21:22:03 itojun Exp $ */ +/* $KAME: nd6.h,v 1.95 2002/06/08 11:31:06 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -93,6 +93,7 @@ struct nd_ifinfo { }; #define ND6_IFF_PERFORMNUD 0x1 +#define ND6_IFF_ACCEPT_RTADV 0x2 #ifdef _KERNEL #define ND_IFINFO(ifp) \ @@ -126,6 +127,32 @@ struct in6_drlist { } defrouter[DRLSTSIZ]; }; +struct in6_defrouter { + struct sockaddr_in6 rtaddr; + u_char flags; + u_short rtlifetime; + u_long expire; + u_short if_index; +}; + +#ifdef _KERNEL +struct in6_oprlist { + char ifname[IFNAMSIZ]; + struct { + struct in6_addr prefix; + struct prf_ra raflags; + u_char prefixlen; + u_char origin; + u_long vltime; + u_long pltime; + u_long expire; + u_short if_index; + u_short advrtrs; /* number of advertisement routers */ + struct in6_addr advrtr[DRLSTSIZ]; /* XXX: explicit limit */ + } prefix[PRLSTSIZ]; +}; +#endif + struct in6_prlist { char ifname[IFNAMSIZ]; struct { @@ -133,15 +160,30 @@ struct in6_prlist { struct prf_ra raflags; u_char prefixlen; u_char origin; - u_long vltime; - u_long pltime; - u_long expire; + u_int32_t vltime; + u_int32_t pltime; + time_t expire; u_short if_index; u_short advrtrs; /* number of advertisement routers */ struct in6_addr advrtr[DRLSTSIZ]; /* XXX: explicit limit */ } prefix[PRLSTSIZ]; }; +struct in6_prefix { + struct sockaddr_in6 prefix; + struct prf_ra raflags; + u_char prefixlen; + u_char origin; + u_int32_t vltime; + u_int32_t pltime; + time_t expire; + u_int32_t flags; + int refcnt; + u_short if_index; + u_short advrtrs; /* number of advertisement routers */ + /* struct sockaddr_in6 advrtr[] */ +}; + #ifdef _KERNEL struct in6_ondireq { char ifname[IFNAMSIZ]; @@ -169,6 +211,10 @@ struct in6_ndifreq { u_long ifindex; }; +/* Prefix status */ +#define NDPRF_ONLINK 0x1 +#define NDPRF_DETACHED 0x2 +#define NDPRF_HOME 0x4 /* protocol constants */ #define MAX_RTR_SOLICITATION_DELAY 1 /*1sec*/ @@ -192,10 +238,11 @@ TAILQ_HEAD(nd_drhead, nd_defrouter); struct nd_defrouter { TAILQ_ENTRY(nd_defrouter) dr_entry; struct in6_addr rtaddr; - u_char flags; + u_char flags; /* flags on RA message */ u_short rtlifetime; u_long expire; struct ifnet *ifp; + int installed; /* is installed into kernel routing table */ }; struct nd_prefix { @@ -203,19 +250,20 @@ struct nd_prefix { LIST_ENTRY(nd_prefix) ndpr_entry; struct sockaddr_in6 ndpr_prefix; /* prefix */ struct in6_addr ndpr_mask; /* netmask derived from the prefix */ - struct in6_addr ndpr_addr; /* address that is derived from the prefix */ + u_int32_t ndpr_vltime; /* advertised valid lifetime */ u_int32_t ndpr_pltime; /* advertised preferred lifetime */ + time_t ndpr_expire; /* expiration time of the prefix */ time_t ndpr_preferred; /* preferred time of the prefix */ + time_t ndpr_lastupdate; /* reception time of last advertisement */ + struct prf_ra ndpr_flags; + u_int32_t ndpr_stateflags; /* actual state flags */ /* list of routers that advertise the prefix: */ LIST_HEAD(pr_rtrhead, nd_pfxrouter) ndpr_advrtrs; u_char ndpr_plen; - struct ndpr_stateflags { - /* if this prefix can be regarded as on-link */ - u_char onlink : 1; - } ndpr_stateflags; + int ndpr_refcnt; /* reference couter from addresses */ }; #define ndpr_next ndpr_entry.le_next @@ -223,15 +271,7 @@ struct nd_prefix { #define ndpr_raf ndpr_flags #define ndpr_raf_onlink ndpr_flags.onlink #define ndpr_raf_auto ndpr_flags.autonomous - -#define ndpr_statef_onlink ndpr_stateflags.onlink -#define ndpr_statef_addmark ndpr_stateflags.addmark - -/* - * We keep expired prefix for certain amount of time, for validation purposes. - * 1800s = MaxRtrAdvInterval - */ -#define NDPR_KEEP_EXPIRED (1800 * 2) +#define ndpr_raf_router ndpr_flags.router /* * Message format for use in obtaining information about prefixes @@ -259,9 +299,6 @@ struct inet6_ndpr_msghdr { #define prm_rrf_decrvalid prm_flags.prf_rr.decrvalid #define prm_rrf_decrprefd prm_flags.prf_rr.decrprefd -#define ifpr2ndpr(ifpr) ((struct nd_prefix *)(ifpr)) -#define ndpr2ifpr(ndpr) ((struct ifprefix *)(ndpr)) - struct nd_pfxrouter { LIST_ENTRY(nd_pfxrouter) pfr_entry; #define pfr_next pfr_entry.le_next @@ -288,7 +325,6 @@ extern int nd6_debug; extern struct timeout nd6_timer_ch; /* nd6_rtr.c */ -extern struct ifnet *nd6_defifp; /* XXXYYY */ extern int nd6_defifindex; union nd_opts { @@ -329,10 +365,9 @@ struct rtentry *nd6_lookup(struct in6_addr *, int, struct ifnet *); void nd6_setmtu(struct ifnet *); void nd6_timer(void *); void nd6_purge(struct ifnet *); -struct llinfo_nd6 *nd6_free(struct rtentry *); void nd6_nud_hint(struct rtentry *, struct in6_addr *, int); int nd6_resolve(struct ifnet *, struct rtentry *, - struct mbuf *, struct sockaddr *, u_char *); + struct mbuf *, struct sockaddr *, u_char *); void nd6_rtrequest(int, struct rtentry *, struct rt_addrinfo *); void nd6_p2p_rtrequest(int, struct rtentry *, struct rt_addrinfo *); int nd6_ioctl(u_long, caddr_t, struct ifnet *); @@ -340,17 +375,19 @@ struct rtentry *nd6_cache_lladdr(struct ifnet *, struct in6_addr *, char *, int, int, int); /* for test */ int nd6_output(struct ifnet *, struct ifnet *, struct mbuf *, - struct sockaddr_in6 *, struct rtentry *); + struct sockaddr_in6 *, struct rtentry *); int nd6_storelladdr(struct ifnet *, struct rtentry *, struct mbuf *, - struct sockaddr *, u_char *); + struct sockaddr *, u_char *); +int nd6_sysctl(int, void *, size_t *, void *, size_t); +int nd6_need_cache(struct ifnet *); /* nd6_nbr.c */ void nd6_na_input(struct mbuf *, int, int); void nd6_na_output(struct ifnet *, struct in6_addr *, - struct in6_addr *, u_long, int, struct sockaddr *); + struct in6_addr *, u_long, int, struct sockaddr *); void nd6_ns_input(struct mbuf *, int, int); void nd6_ns_output(struct ifnet *, struct in6_addr *, - struct in6_addr *, struct llinfo_nd6 *, int); + struct in6_addr *, struct llinfo_nd6 *, int); caddr_t nd6_ifptomac(struct ifnet *); void nd6_dad_start(struct ifaddr *, int *); void nd6_dad_stop(struct ifaddr *); @@ -361,15 +398,18 @@ void nd6_rs_input(struct mbuf *, int, int); void nd6_ra_input(struct mbuf *, int, int); void prelist_del(struct nd_prefix *); void defrouter_addreq(struct nd_defrouter *); -void defrouter_delreq(struct nd_defrouter *, int); +void defrouter_reset(void); void defrouter_select(void); void defrtrlist_del(struct nd_defrouter *); void prelist_remove(struct nd_prefix *); -int prelist_update(struct nd_prefix *, struct nd_defrouter *, - struct mbuf *); +int prelist_update(struct nd_prefix *, struct nd_defrouter *, struct mbuf *); +int nd6_prelist_add(struct nd_prefix *, struct nd_defrouter *, + struct nd_prefix **); +int nd6_prefix_onlink(struct nd_prefix *); +int nd6_prefix_offlink(struct nd_prefix *); void pfxlist_onlink_check(void); -struct nd_defrouter *defrouter_lookup(struct in6_addr *, - struct ifnet *); +struct nd_defrouter *defrouter_lookup(struct in6_addr *, struct ifnet *); +struct nd_prefix *nd6_prefix_lookup(struct nd_prefix *); int in6_ifdel(struct ifnet *, struct in6_addr *); int in6_init_prefix_ltimes(struct nd_prefix *ndpr); void rt6_flush(struct in6_addr *, struct ifnet *); diff --git a/sys/netinet6/nd6_nbr.c b/sys/netinet6/nd6_nbr.c index e3819b9d051..9c1fe1f1408 100644 --- a/sys/netinet6/nd6_nbr.c +++ b/sys/netinet6/nd6_nbr.c @@ -1,4 +1,4 @@ -/* $OpenBSD: nd6_nbr.c,v 1.23 2002/05/29 07:54:59 itojun Exp $ */ +/* $OpenBSD: nd6_nbr.c,v 1.24 2002/06/08 21:22:03 itojun Exp $ */ /* $KAME: nd6_nbr.c,v 1.61 2001/02/10 16:06:14 jinmei Exp $ */ /* @@ -95,6 +95,7 @@ nd6_ns_input(m, off, icmp6len) struct ifaddr *ifa; int lladdrlen = 0; int anycast = 0, proxy = 0, tentative = 0; + int router = ip6_forwarding; int tlladdr; union nd_opts ndopts; struct sockaddr_dl *proxydl = NULL; @@ -122,15 +123,15 @@ nd6_ns_input(m, off, icmp6len) if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) { /* dst has to be solicited node multicast address. */ - if (daddr6.s6_addr16[0] == IPV6_ADDR_INT16_MLL - /* don't check ifindex portion */ - && daddr6.s6_addr32[1] == 0 - && daddr6.s6_addr32[2] == IPV6_ADDR_INT32_ONE - && daddr6.s6_addr8[12] == 0xff) { + /* don't check ifindex portion */ + if (daddr6.s6_addr16[0] == IPV6_ADDR_INT16_MLL && + daddr6.s6_addr32[1] == 0 && + daddr6.s6_addr32[2] == IPV6_ADDR_INT32_ONE && + daddr6.s6_addr8[12] == 0xff) { ; /*good*/ } else { nd6log((LOG_INFO, "nd6_ns_input: bad DAD packet " - "(wrong ip6 dst)\n")); + "(wrong ip6 dst)\n")); goto bad; } } @@ -153,7 +154,7 @@ nd6_ns_input(m, off, icmp6len) } if (ndopts.nd_opts_src_lladdr) { - lladdr = (char *)(ndopts.nd_opts_src_lladdr +1); + lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1); lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3; } @@ -214,6 +215,7 @@ nd6_ns_input(m, off, icmp6len) if (ifa) { proxy = 1; proxydl = SDL(rt->rt_gateway); + router = 0; /* XXX */ } } if (rt) @@ -234,16 +236,14 @@ nd6_ns_input(m, off, icmp6len) goto freeit; if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { - nd6log((LOG_INFO, - "nd6_ns_input: lladdrlen mismatch for %s " + nd6log((LOG_INFO, "nd6_ns_input: lladdrlen mismatch for %s " "(if %d, NS packet %d)\n", - ip6_sprintf(&taddr6), ifp->if_addrlen, lladdrlen - 2)); + ip6_sprintf(&taddr6), ifp->if_addrlen, lladdrlen - 2)); goto bad; } if (IN6_ARE_ADDR_EQUAL(&myaddr6, &saddr6)) { - log(LOG_INFO, - "nd6_ns_input: duplicate IP6 address %s\n", + log(LOG_INFO, "nd6_ns_input: duplicate IP6 address %s\n", ip6_sprintf(&saddr6)); goto freeit; } @@ -286,20 +286,18 @@ nd6_ns_input(m, off, icmp6len) saddr6 = in6addr_linklocal_allnodes; saddr6.s6_addr16[1] = htons(ifp->if_index); nd6_na_output(ifp, &saddr6, &taddr6, - ((anycast || proxy || !tlladdr) - ? 0 : ND_NA_FLAG_OVERRIDE) - | (ip6_forwarding ? ND_NA_FLAG_ROUTER : 0), - tlladdr, (struct sockaddr *)proxydl); + ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) | + (router ? ND_NA_FLAG_ROUTER : 0), + tlladdr, (struct sockaddr *)proxydl); goto freeit; } nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_NEIGHBOR_SOLICIT, 0); nd6_na_output(ifp, &saddr6, &taddr6, - ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) - | (ip6_forwarding ? ND_NA_FLAG_ROUTER : 0) - | ND_NA_FLAG_SOLICITED, - tlladdr, (struct sockaddr *)proxydl); + ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) | + (router ? ND_NA_FLAG_ROUTER : 0) | ND_NA_FLAG_SOLICITED, + tlladdr, (struct sockaddr *)proxydl); freeit: m_freem(m); return; @@ -331,12 +329,14 @@ nd6_ns_output(ifp, daddr6, taddr6, ln, dad) struct mbuf *m; struct ip6_hdr *ip6; struct nd_neighbor_solicit *nd_ns; - struct in6_ifaddr *ia = NULL; + struct sockaddr_in6 src_sa, dst_sa; struct ip6_moptions im6o; int icmp6len; int maxlen; caddr_t mac; - struct ifnet *outif = NULL; + struct route_in6 ro; + + bzero(&ro, sizeof(ro)); if (IN6_IS_ADDR_MULTICAST(taddr6)) return; @@ -344,13 +344,14 @@ nd6_ns_output(ifp, daddr6, taddr6, ln, dad) /* estimate the size of message */ maxlen = sizeof(*ip6) + sizeof(*nd_ns); maxlen += (sizeof(struct nd_opt_hdr) + ifp->if_addrlen + 7) & ~7; - if (max_linkhdr + maxlen >= MCLBYTES) { #ifdef DIAGNOSTIC + if (max_linkhdr + maxlen >= MCLBYTES) { printf("nd6_ns_output: max_linkhdr + maxlen >= MCLBYTES " "(%d + %d > %d)\n", max_linkhdr, maxlen, MCLBYTES); -#endif - return; + panic("nd6_ns_output: insufficient MCLBYTES"); + /* NOTREACHED */ } +#endif MGETHDR(m, M_DONTWAIT, MT_DATA); if (m && max_linkhdr + maxlen >= MHLEN) { @@ -362,6 +363,7 @@ nd6_ns_output(ifp, daddr6, taddr6, ln, dad) } if (m == NULL) return; + m->m_pkthdr.rcvif = NULL; if (daddr6 == NULL || IN6_IS_ADDR_MULTICAST(daddr6)) { m->m_flags |= M_MCAST; @@ -382,31 +384,23 @@ nd6_ns_output(ifp, daddr6, taddr6, ln, dad) /* ip6->ip6_plen will be set later */ ip6->ip6_nxt = IPPROTO_ICMPV6; ip6->ip6_hlim = 255; + /* determine the source and destination addresses */ + bzero(&src_sa, sizeof(src_sa)); + bzero(&dst_sa, sizeof(dst_sa)); + src_sa.sin6_family = dst_sa.sin6_family = AF_INET6; + src_sa.sin6_len = dst_sa.sin6_len = sizeof(struct sockaddr_in6); if (daddr6) - ip6->ip6_dst = *daddr6; + dst_sa.sin6_addr = *daddr6; else { - ip6->ip6_dst.s6_addr16[0] = IPV6_ADDR_INT16_MLL; - ip6->ip6_dst.s6_addr16[1] = htons(ifp->if_index); - ip6->ip6_dst.s6_addr32[1] = 0; - ip6->ip6_dst.s6_addr32[2] = IPV6_ADDR_INT32_ONE; - ip6->ip6_dst.s6_addr32[3] = taddr6->s6_addr32[3]; - ip6->ip6_dst.s6_addr8[12] = 0xff; + dst_sa.sin6_addr.s6_addr16[0] = IPV6_ADDR_INT16_MLL; + dst_sa.sin6_addr.s6_addr16[1] = htons(ifp->if_index); + dst_sa.sin6_addr.s6_addr32[1] = 0; + dst_sa.sin6_addr.s6_addr32[2] = IPV6_ADDR_INT32_ONE; + dst_sa.sin6_addr.s6_addr32[3] = taddr6->s6_addr32[3]; + dst_sa.sin6_addr.s6_addr8[12] = 0xff; } + ip6->ip6_dst = dst_sa.sin6_addr; if (!dad) { -#if 0 /* KAME way, exact address scope match */ - /* - * Select a source whose scope is the same as that of the dest. - * Typically, the dest is link-local solicitation multicast - * (i.e. neighbor discovery) or link-local/global unicast - * (i.e. neighbor un-reachability detection). - */ - ia = in6_ifawithifp(ifp, &ip6->ip6_dst); - if (ia == NULL) { - m_freem(m); - return; - } - ip6->ip6_src = ia->ia_addr.sin6_addr; -#else /* spec-wise correct */ /* * RFC2461 7.2.2: * "If the source address of the packet prompting the @@ -420,7 +414,7 @@ nd6_ns_output(ifp, daddr6, taddr6, ln, dad) * (saddr6), if: * - saddr6 is given from the caller (by giving "ln"), and * - saddr6 belongs to the outgoing interface. - * Otherwise, we perform a scope-wise match. + * Otherwise, we perform the source address selection as usual. */ struct ip6_hdr *hip6; /* hold ip6 */ struct in6_addr *saddr6; @@ -435,23 +429,34 @@ nd6_ns_output(ifp, daddr6, taddr6, ln, dad) } else saddr6 = NULL; if (saddr6 && in6ifa_ifpwithaddr(ifp, saddr6)) - bcopy(saddr6, &ip6->ip6_src, sizeof(*saddr6)); + src_sa.sin6_addr = *saddr6; else { - ia = in6_ifawithifp(ifp, &ip6->ip6_dst); - if (ia == NULL) { - m_freem(m); - return; + struct in6_addr *src0; + int error; + + bcopy(&dst_sa, &ro.ro_dst, sizeof(dst_sa)); + src0 = in6_selectsrc(&dst_sa, NULL, NULL, &ro, NULL, + &error); + if (src0 == NULL) { + nd6log((LOG_DEBUG, + "nd6_ns_output: source can't be " + "determined: dst=%s, error=%d\n", + ip6_sprintf(&dst_sa.sin6_addr), error)); + goto bad; } - ip6->ip6_src = ia->ia_addr.sin6_addr; + src_sa.sin6_addr = *src0; } -#endif } else { /* * Source address for DAD packet must always be IPv6 * unspecified address. (0::0) + * We actually don't have to 0-clear the address (we did it + * above), but we do so here explicitly to make the intention + * clearer. */ - bzero(&ip6->ip6_src, sizeof(ip6->ip6_src)); + bzero(&src_sa.sin6_addr, sizeof(src_sa.sin6_addr)); } + ip6->ip6_src = src_sa.sin6_addr; nd_ns = (struct nd_neighbor_solicit *)(ip6 + 1); nd_ns->nd_ns_type = ND_NEIGHBOR_SOLICIT; nd_ns->nd_ns_code = 0; @@ -490,15 +495,25 @@ nd6_ns_output(ifp, daddr6, taddr6, ln, dad) ip6->ip6_plen = htons((u_short)icmp6len); nd_ns->nd_ns_cksum = 0; - nd_ns->nd_ns_cksum - = in6_cksum(m, IPPROTO_ICMPV6, sizeof(*ip6), icmp6len); + nd_ns->nd_ns_cksum = + in6_cksum(m, IPPROTO_ICMPV6, sizeof(*ip6), icmp6len); - ip6_output(m, NULL, NULL, dad ? IPV6_DADOUTPUT : 0, &im6o, &outif); - if (outif) { - icmp6_ifstat_inc(outif, ifs6_out_msg); - icmp6_ifstat_inc(outif, ifs6_out_neighborsolicit); - } + ip6_output(m, NULL, &ro, dad ? IPV6_UNSPECSRC : 0, &im6o, NULL); + icmp6_ifstat_inc(ifp, ifs6_out_msg); + icmp6_ifstat_inc(ifp, ifs6_out_neighborsolicit); icmp6stat.icp6s_outhist[ND_NEIGHBOR_SOLICIT]++; + + if (ro.ro_rt) { /* we don't cache this route. */ + RTFREE(ro.ro_rt); + } + return; + + bad: + if (ro.ro_rt) { + RTFREE(ro.ro_rt); + } + m_freem(m); + return; } /* @@ -535,6 +550,7 @@ nd6_na_input(m, off, icmp6len) struct rtentry *rt; struct sockaddr_dl *sdl; union nd_opts ndopts; + long time_second = time.tv_sec; if (ip6->ip6_hlim != 255) { nd6log((LOG_ERR, @@ -569,12 +585,11 @@ nd6_na_input(m, off, icmp6len) ip6_sprintf(&taddr6))); goto bad; } - if (IN6_IS_ADDR_MULTICAST(&daddr6)) - if (is_solicited) { - nd6log((LOG_ERR, - "nd6_na_input: a solicited adv is multicasted\n")); - goto bad; - } + if (is_solicited && IN6_IS_ADDR_MULTICAST(&daddr6)) { + nd6log((LOG_ERR, + "nd6_na_input: a solicited adv is multicasted\n")); + goto bad; + } icmp6len -= sizeof(*nd_na); nd6_option_init(nd_na + 1, icmp6len, &ndopts); @@ -616,15 +631,15 @@ nd6_na_input(m, off, icmp6len) } if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { - nd6log((LOG_INFO, - "nd6_na_input: lladdrlen mismatch for %s " - "(if %d, NA packet %d)\n", - ip6_sprintf(&taddr6), ifp->if_addrlen, lladdrlen - 2)); + nd6log((LOG_INFO, "nd6_na_input: lladdrlen mismatch for %s " + "(if %d, NA packet %d)\n", ip6_sprintf(&taddr6), + ifp->if_addrlen, lladdrlen - 2)); goto bad; } /* - * If no neighbor cache entry is found, NA SHOULD silently be discarded. + * If no neighbor cache entry is found, NA SHOULD silently be + * discarded. */ rt = nd6_lookup(&taddr6, 0, ifp); if ((rt == NULL) || @@ -649,13 +664,20 @@ nd6_na_input(m, off, icmp6len) ln->ln_state = ND6_LLINFO_REACHABLE; ln->ln_byhint = 0; if (ln->ln_expire) - ln->ln_expire = time.tv_sec + + ln->ln_expire = time_second + ND_IFINFO(rt->rt_ifp)->reachable; } else { ln->ln_state = ND6_LLINFO_STALE; - ln->ln_expire = time.tv_sec + nd6_gctimer; + ln->ln_expire = time_second + nd6_gctimer; + } + if ((ln->ln_router = is_router) != 0) { + /* + * This means a router's state has changed from + * non-reachable to probably reachable, and might + * affect the status of associated prefixes.. + */ + pfxlist_onlink_check(); } - ln->ln_router = is_router; } else { int llchange; @@ -700,7 +722,7 @@ nd6_na_input(m, off, icmp6len) */ if (ln->ln_state == ND6_LLINFO_REACHABLE) { ln->ln_state = ND6_LLINFO_STALE; - ln->ln_expire = time.tv_sec + nd6_gctimer; + ln->ln_expire = time_second + nd6_gctimer; } goto freeit; } else if (is_override /* (2a) */ @@ -723,13 +745,13 @@ nd6_na_input(m, off, icmp6len) ln->ln_state = ND6_LLINFO_REACHABLE; ln->ln_byhint = 0; if (ln->ln_expire) { - ln->ln_expire = time.tv_sec + + ln->ln_expire = time_second + ND_IFINFO(ifp)->reachable; } } else { if (lladdr && llchange) { ln->ln_state = ND6_LLINFO_STALE; - ln->ln_expire = time.tv_sec + nd6_gctimer; + ln->ln_expire = time_second + nd6_gctimer; } } } @@ -746,8 +768,13 @@ nd6_na_input(m, off, icmp6len) in6 = &((struct sockaddr_in6 *)rt_key(rt))->sin6_addr; + /* + * Lock to protect the default router list. + * XXX: this might be unnecessary, since this function + * is only called under the network software interrupt + * context. However, we keep it just for safety. + */ s = splnet(); - dr = defrouter_lookup(in6, rt->rt_ifp); if (dr) defrtrlist_del(dr); @@ -769,7 +796,7 @@ nd6_na_input(m, off, icmp6len) ln->ln_asked = 0; if (ln->ln_hold) { /* - * we assume ifp is not a p2p here, so just set the 2nd + * we assume ifp is not a loopback here, so just set the 2nd * argument as the 1st one. */ nd6_output(ifp, ifp, ln->ln_hold, @@ -806,23 +833,27 @@ nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr, sdl0) struct mbuf *m; struct ip6_hdr *ip6; struct nd_neighbor_advert *nd_na; - struct in6_ifaddr *ia = NULL; struct ip6_moptions im6o; - int icmp6len; - int maxlen; + struct sockaddr_in6 src_sa, dst_sa; + struct in6_addr *src0; + int icmp6len, maxlen, error; caddr_t mac; - struct ifnet *outif = NULL; + struct route_in6 ro; + + mac = NULL; + bzero(&ro, sizeof(ro)); /* estimate the size of message */ maxlen = sizeof(*ip6) + sizeof(*nd_na); maxlen += (sizeof(struct nd_opt_hdr) + ifp->if_addrlen + 7) & ~7; - if (max_linkhdr + maxlen >= MCLBYTES) { #ifdef DIAGNOSTIC + if (max_linkhdr + maxlen >= MCLBYTES) { printf("nd6_na_output: max_linkhdr + maxlen >= MCLBYTES " "(%d + %d > %d)\n", max_linkhdr, maxlen, MCLBYTES); -#endif - return; + panic("nd6_na_output: insufficient MCLBYTES"); + /* NOTREACHED */ } +#endif MGETHDR(m, M_DONTWAIT, MT_DATA); if (m && max_linkhdr + maxlen >= MHLEN) { @@ -834,6 +865,7 @@ nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr, sdl0) } if (m == NULL) return; + m->m_pkthdr.rcvif = NULL; if (IN6_IS_ADDR_MULTICAST(daddr6)) { m->m_flags |= M_MCAST; @@ -844,7 +876,7 @@ nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr, sdl0) icmp6len = sizeof(*nd_na); m->m_pkthdr.len = m->m_len = sizeof(struct ip6_hdr) + icmp6len; - m->m_data += max_linkhdr; /*or MH_ALIGN() equivalent?*/ + m->m_data += max_linkhdr; /* or MH_ALIGN() equivalent? */ /* fill neighbor advertisement packet */ ip6 = mtod(m, struct ip6_hdr *); @@ -853,26 +885,36 @@ nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr, sdl0) ip6->ip6_vfc |= IPV6_VERSION; ip6->ip6_nxt = IPPROTO_ICMPV6; ip6->ip6_hlim = 255; + bzero(&src_sa, sizeof(src_sa)); + bzero(&dst_sa, sizeof(dst_sa)); + src_sa.sin6_len = dst_sa.sin6_len = sizeof(struct sockaddr_in6); + src_sa.sin6_family = dst_sa.sin6_family = AF_INET6; + dst_sa.sin6_addr = *daddr6; if (IN6_IS_ADDR_UNSPECIFIED(daddr6)) { /* reply to DAD */ - ip6->ip6_dst.s6_addr16[0] = IPV6_ADDR_INT16_MLL; - ip6->ip6_dst.s6_addr16[1] = htons(ifp->if_index); - ip6->ip6_dst.s6_addr32[1] = 0; - ip6->ip6_dst.s6_addr32[2] = 0; - ip6->ip6_dst.s6_addr32[3] = IPV6_ADDR_INT32_ONE; + dst_sa.sin6_addr.s6_addr16[0] = IPV6_ADDR_INT16_MLL; + dst_sa.sin6_addr.s6_addr16[1] = htons(ifp->if_index); + dst_sa.sin6_addr.s6_addr32[1] = 0; + dst_sa.sin6_addr.s6_addr32[2] = 0; + dst_sa.sin6_addr.s6_addr32[3] = IPV6_ADDR_INT32_ONE; + flags &= ~ND_NA_FLAG_SOLICITED; - } else - ip6->ip6_dst = *daddr6; + } + ip6->ip6_dst = dst_sa.sin6_addr; /* * Select a source whose scope is the same as that of the dest. */ - ia = in6_ifawithifp(ifp, &ip6->ip6_dst); - if (ia == NULL) { - m_freem(m); - return; + bcopy(&dst_sa, &ro.ro_dst, sizeof(dst_sa)); + src0 = in6_selectsrc(&dst_sa, NULL, NULL, &ro, NULL, &error); + if (src0 == NULL) { + nd6log((LOG_DEBUG, "nd6_na_output: source can't be " + "determined: dst=%s, error=%d\n", + ip6_sprintf(&dst_sa.sin6_addr), error)); + goto bad; } - ip6->ip6_src = ia->ia_addr.sin6_addr; + src_sa.sin6_addr = *src0; + ip6->ip6_src = src_sa.sin6_addr; nd_na = (struct nd_neighbor_advert *)(ip6 + 1); nd_na->nd_na_type = ND_NEIGHBOR_ADVERT; nd_na->nd_na_code = 0; @@ -887,7 +929,6 @@ nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr, sdl0) * target lladdr option SHOULD NOT be included. */ 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 @@ -923,14 +964,25 @@ nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr, sdl0) nd_na->nd_na_flags_reserved = flags; nd_na->nd_na_cksum = 0; nd_na->nd_na_cksum = - in6_cksum(m, IPPROTO_ICMPV6, sizeof(struct ip6_hdr), icmp6len); + in6_cksum(m, IPPROTO_ICMPV6, sizeof(struct ip6_hdr), icmp6len); - ip6_output(m, NULL, NULL, 0, &im6o, &outif); - if (outif) { - icmp6_ifstat_inc(outif, ifs6_out_msg); - icmp6_ifstat_inc(outif, ifs6_out_neighboradvert); - } + ip6_output(m, NULL, &ro, 0, &im6o, NULL); + + icmp6_ifstat_inc(ifp, ifs6_out_msg); + icmp6_ifstat_inc(ifp, ifs6_out_neighboradvert); icmp6stat.icp6s_outhist[ND_NEIGHBOR_ADVERT]++; + + if (ro.ro_rt) { /* we don't cache this route. */ + RTFREE(ro.ro_rt); + } + return; + + bad: + if (ro.ro_rt) { + RTFREE(ro.ro_rt); + } + m_freem(m); + return; } caddr_t @@ -941,6 +993,10 @@ nd6_ifptomac(ifp) case IFT_ARCNET: case IFT_ETHER: case IFT_FDDI: + case IFT_IEEE1394: + case IFT_PROPVIRTUAL: + case IFT_L2VLAN: + case IFT_IEEE80211: return ((caddr_t)(ifp + 1)); break; default: @@ -1064,13 +1120,13 @@ nd6_dad_start(ifa, tick) * (re)initialization. */ dp->dad_ifa = ifa; - ifa->ifa_refcnt++; /*just for safety*/ + ifa->ifa_refcnt++; /* just for safety */ dp->dad_count = ip6_dad_count; dp->dad_ns_icount = dp->dad_na_icount = 0; dp->dad_ns_ocount = dp->dad_ns_tcount = 0; if (tick == NULL) { nd6_dad_ns_output(dp, ifa); - nd6_dad_starttimer(dp, + nd6_dad_starttimer(dp, ND6_RETRANS_SEC(ND_IFINFO(ifa->ifa_ifp)->retrans) * hz); } else { int ntick; @@ -1117,7 +1173,7 @@ nd6_dad_timer(ifa) struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa; struct dadq *dp; - s = splnet(); /*XXX*/ + s = splnet(); /* XXX */ /* Sanity check */ if (ia == NULL) { @@ -1162,7 +1218,7 @@ nd6_dad_timer(ifa) * We have more NS to go. Send NS packet for DAD. */ nd6_dad_ns_output(dp, ifa); - nd6_dad_starttimer(dp, + nd6_dad_starttimer(dp, ND6_RETRANS_SEC(ND_IFINFO(ifa->ifa_ifp)->retrans) * hz); } else { /* @@ -1251,10 +1307,10 @@ nd6_dad_duplicated(ifa) return; } - log(LOG_ERR, "%s: DAD detected duplicate IPv6 address %s: %d NS, " - "%d NA\n", ifa->ifa_ifp->if_xname, - ip6_sprintf(&ia->ia_addr.sin6_addr), - dp->dad_ns_icount, dp->dad_na_icount); + log(LOG_ERR, "%s: DAD detected duplicate IPv6 address %s: " + "NS in/out=%d/%d, NA in=%d\n", + ifa->ifa_ifp->if_xname, ip6_sprintf(&ia->ia_addr.sin6_addr), + dp->dad_ns_icount, dp->dad_ns_ocount, dp->dad_na_icount); ia->ia6_flags &= ~IN6_IFF_TENTATIVE; ia->ia6_flags |= IN6_IFF_DUPLICATED; @@ -1318,12 +1374,6 @@ nd6_dad_ns_input(ifa) duplicate = 0; dp = nd6_dad_find(ifa); - /* - * If it is from myself, ignore this. - */ - if (ifp && (ifp->if_flags & IFF_LOOPBACK)) - return; - /* Quickhack - completely ignore DAD NS packets */ if (dad_ignore_ns) { nd6log((LOG_INFO, diff --git a/sys/netinet6/nd6_rtr.c b/sys/netinet6/nd6_rtr.c index db6da5fb51e..e31df3d4205 100644 --- a/sys/netinet6/nd6_rtr.c +++ b/sys/netinet6/nd6_rtr.c @@ -1,4 +1,4 @@ -/* $OpenBSD: nd6_rtr.c,v 1.16 2002/06/08 00:06:58 itojun Exp $ */ +/* $OpenBSD: nd6_rtr.c,v 1.17 2002/06/08 21:22:03 itojun Exp $ */ /* $KAME: nd6_rtr.c,v 1.97 2001/02/07 11:09:13 itojun Exp $ */ /* @@ -58,30 +58,29 @@ #define SDL(s) ((struct sockaddr_dl *)s) +static int rtpref(struct nd_defrouter *); static struct nd_defrouter *defrtrlist_update(struct nd_defrouter *); -static int prelist_add(struct nd_prefix *, struct nd_defrouter *); -static struct nd_prefix *prefix_lookup(struct nd_prefix *); -static struct in6_ifaddr *in6_ifadd(struct ifnet *, struct in6_addr *, - struct in6_addr *, int); +static struct in6_ifaddr *in6_ifadd(struct nd_prefix *); static struct nd_pfxrouter *pfxrtr_lookup(struct nd_prefix *, - struct nd_defrouter *); + struct nd_defrouter *); static void pfxrtr_add(struct nd_prefix *, struct nd_defrouter *); static void pfxrtr_del(struct nd_pfxrouter *); static struct nd_pfxrouter *find_pfxlist_reachable_router(struct nd_prefix *); -static void nd6_detach_prefix(struct nd_prefix *); -static void nd6_attach_prefix(struct nd_prefix *); +static void defrouter_delreq(struct nd_defrouter *); static void defrouter_addifreq(struct ifnet *); +static void defrouter_delifreq(void); +static void nd6_rtmsg(int, struct rtentry *); -static void in6_init_address_ltimes(struct nd_prefix *ndpr, - struct in6_addrlifetime *lt6, - int update_vltime); +static void in6_init_address_ltimes(struct nd_prefix *, + struct in6_addrlifetime *); static int rt6_deleteroute(struct radix_node *, void *); extern int nd6_recalc_reachtm_interval; -struct ifnet *nd6_defifp; +static struct ifnet *nd6_defifp; int nd6_defifindex; +static struct ifaddr *nd6_defif_installed = NULL; /* * Receive Router Solicitation Message - just for routers. @@ -161,7 +160,7 @@ nd6_rs_input(m, off, icmp6len) nd6log((LOG_INFO, "nd6_rs_input: lladdrlen mismatch for %s " "(if %d, RS packet %d)\n", - ip6_sprintf(&saddr6), ifp->if_addrlen, lladdrlen - 2)); + ip6_sprintf(&saddr6), ifp->if_addrlen, lladdrlen - 2)); goto bad; } @@ -202,8 +201,15 @@ nd6_ra_input(m, off, icmp6len) union nd_opts ndopts; struct nd_defrouter *dr; + /* + * We only accept RAs only when + * the system-wide variable allows the acceptance, and + * per-interface variable allows RAs on the receiving interface. + */ if (ip6_accept_rtadv == 0) goto freeit; + if (!(ndi->flags & ND6_IFF_ACCEPT_RTADV)) + goto freeit; if (ip6->ip6_hlim != 255) { nd6log((LOG_ERR, @@ -245,6 +251,7 @@ nd6_ra_input(m, off, icmp6len) u_int32_t advreachable = nd_ra->nd_ra_reachable; long time_second = time.tv_sec; + Bzero(&dr0, sizeof(dr0)); dr0.rtaddr = saddr6; dr0.flags = nd_ra->nd_ra_flags_reserved; dr0.rtlifetime = ntohs(nd_ra->nd_ra_router_lifetime); @@ -272,8 +279,9 @@ nd6_ra_input(m, off, icmp6len) */ if (ndopts.nd_opts_pi) { struct nd_opt_hdr *pt; - struct nd_opt_prefix_info *pi; + struct nd_opt_prefix_info *pi = NULL; struct nd_prefix pr; + long time_second = time.tv_sec; for (pt = (struct nd_opt_hdr *)ndopts.nd_opts_pi; pt <= (struct nd_opt_hdr *)ndopts.nd_opts_pi_end; @@ -326,13 +334,13 @@ nd6_ra_input(m, off, icmp6len) pr.ndpr_ifp = (struct ifnet *)m->m_pkthdr.rcvif; pr.ndpr_raf_onlink = (pi->nd_opt_pi_flags_reserved & - ND_OPT_PI_FLAG_ONLINK) ? 1 : 0; + ND_OPT_PI_FLAG_ONLINK) ? 1 : 0; pr.ndpr_raf_auto = (pi->nd_opt_pi_flags_reserved & - ND_OPT_PI_FLAG_AUTO) ? 1 : 0; + ND_OPT_PI_FLAG_AUTO) ? 1 : 0; pr.ndpr_plen = pi->nd_opt_pi_prefix_len; pr.ndpr_vltime = ntohl(pi->nd_opt_pi_valid_time); - pr.ndpr_pltime = - ntohl(pi->nd_opt_pi_preferred_time); + pr.ndpr_pltime = ntohl(pi->nd_opt_pi_preferred_time); + pr.ndpr_lastupdate = time_second; if (in6_init_prefix_ltimes(&pr)) continue; /* prefix lifetime init failed */ @@ -345,13 +353,15 @@ nd6_ra_input(m, off, icmp6len) * MTU */ if (ndopts.nd_opts_mtu && ndopts.nd_opts_mtu->nd_opt_mtu_len == 1) { - u_int32_t mtu = ntohl(ndopts.nd_opts_mtu->nd_opt_mtu_mtu); + u_long mtu; u_long maxmtu; + mtu = ntohl(ndopts.nd_opts_mtu->nd_opt_mtu_mtu); + /* lower bound */ if (mtu < IPV6_MMTU) { nd6log((LOG_INFO, "nd6_ra_input: bogus mtu option " - "mtu=%d sent from %s, ignoring\n", + "mtu=%lu sent from %s, ignoring\n", mtu, ip6_sprintf(&ip6->ip6_src))); goto skip; } @@ -367,7 +377,7 @@ nd6_ra_input(m, off, icmp6len) in6_setmaxmtu(); } else { nd6log((LOG_INFO, "nd6_ra_input: bogus mtu " - "mtu=%d sent from %s; " + "mtu=%lu sent from %s; " "exceeds maxmtu %lu, ignoring\n", mtu, ip6_sprintf(&ip6->ip6_src), maxmtu)); } @@ -417,26 +427,57 @@ nd6_ra_input(m, off, icmp6len) /* * default router list proccessing sub routines */ + +/* tell the change to user processes watching the routing socket. */ +static void +nd6_rtmsg(cmd, rt) + int cmd; + struct rtentry *rt; +{ + struct rt_addrinfo info; + + bzero((caddr_t)&info, sizeof(info)); + info.rti_info[RTAX_DST] = rt_key(rt); + info.rti_info[RTAX_GATEWAY] = rt->rt_gateway; + info.rti_info[RTAX_NETMASK] = rt_mask(rt); + info.rti_info[RTAX_IFP] = + (struct sockaddr *)TAILQ_FIRST(&rt->rt_ifp->if_addrlist); + info.rti_info[RTAX_IFA] = rt->rt_ifa->ifa_addr; + + rt_missmsg(cmd, &info, rt->rt_flags, 0); +} + void defrouter_addreq(new) struct nd_defrouter *new; { struct sockaddr_in6 def, mask, gate; + struct rtentry *newrt = NULL; int s; + int error; Bzero(&def, sizeof(def)); Bzero(&mask, sizeof(mask)); - Bzero(&gate, sizeof(gate)); + Bzero(&gate, sizeof(gate)); /* for safety */ - def.sin6_len = mask.sin6_len = gate.sin6_len - = sizeof(struct sockaddr_in6); + def.sin6_len = mask.sin6_len = gate.sin6_len = + sizeof(struct sockaddr_in6); def.sin6_family = mask.sin6_family = gate.sin6_family = AF_INET6; gate.sin6_addr = new->rtaddr; +#ifndef SCOPEDROUTING + gate.sin6_scope_id = 0; /* XXX */ +#endif s = splnet(); - (void)rtrequest(RTM_ADD, (struct sockaddr *)&def, - (struct sockaddr *)&gate, (struct sockaddr *)&mask, - RTF_GATEWAY, NULL); + error = rtrequest(RTM_ADD, (struct sockaddr *)&def, + (struct sockaddr *)&gate, (struct sockaddr *)&mask, + RTF_GATEWAY, &newrt); + if (newrt) { + nd6_rtmsg(RTM_ADD, newrt); /* tell user process */ + newrt->rt_refcnt--; + } + if (error == 0) + new->installed = 1; splx(s); return; } @@ -448,7 +489,13 @@ defrouter_addifreq(ifp) { struct sockaddr_in6 def, mask; struct ifaddr *ifa; + struct rtentry *newrt = NULL; int error, flags; + struct rt_addrinfo info; + + /* remove one if we have already installed one */ + if (nd6_defif_installed) + defrouter_delifreq(); bzero(&def, sizeof(def)); bzero(&mask, sizeof(mask)); @@ -463,21 +510,72 @@ defrouter_addifreq(ifp) if ((ifa = ifaof_ifpforaddr((struct sockaddr *)&def, ifp)) == NULL) { nd6log((LOG_ERR, /* better error? */ "defrouter_addifreq: failed to find an ifaddr " - "to install a route to interface %s\n", ifp->if_xname)); + "to install a route to interface %s\n", + ifp->if_xname)); return; } - 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) { + /* RTF_CLONING is necessary to make sure to perform ND */ + flags = ifa->ifa_flags | RTF_CLONING; + bzero(&info, sizeof(info)); + info.rti_info[RTAX_DST] = (struct sockaddr *)&def; + info.rti_info[RTAX_GATEWAY] = (struct sockaddr *)ifa->ifa_addr; + info.rti_info[RTAX_NETMASK] = (struct sockaddr *)&mask; + info.rti_info[RTAX_IFA] = (struct sockaddr *)ifa->ifa_addr; + info.rti_flags = flags; + error = rtrequest1(RTM_ADD, &info, &newrt); + if (error != 0) { nd6log((LOG_ERR, "defrouter_addifreq: failed to install a route to " "interface %s (errno = %d)\n", ifp->if_xname, error)); + + if (newrt) /* maybe unnecessary, but do it for safety */ + newrt->rt_refcnt--; + } else { + if (newrt) { + nd6_rtmsg(RTM_ADD, newrt); + newrt->rt_refcnt--; + } } + + nd6_defif_installed = ifa; + ifa->ifa_refcnt++; +} + +/* Remove a default route points to interface */ +static void +defrouter_delifreq() +{ + struct sockaddr_in6 def, mask; + struct rtentry *oldrt = NULL; + + if (!nd6_defif_installed) + return; + + 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; + + rtrequest(RTM_DELETE, (struct sockaddr *)&def, + (struct sockaddr *)nd6_defif_installed->ifa_addr, + (struct sockaddr *)&mask, RTF_GATEWAY, &oldrt); + if (oldrt) { + nd6_rtmsg(RTM_DELETE, oldrt); + if (oldrt->rt_refcnt <= 0) { + /* + * XXX: borrowed from the RTM_DELETE case of + * rtrequest(). + */ + oldrt->rt_refcnt++; + rtfree(oldrt); + } + } + + IFAFREE(nd6_defif_installed); + nd6_defif_installed = NULL; } struct nd_defrouter * @@ -489,39 +587,15 @@ defrouter_lookup(addr, ifp) for (dr = TAILQ_FIRST(&nd_defrouter); dr; dr = TAILQ_NEXT(dr, dr_entry)) { - if (dr->ifp == ifp && IN6_ARE_ADDR_EQUAL(addr, &dr->rtaddr)) + if (dr->ifp == ifp && IN6_ARE_ADDR_EQUAL(addr, &dr->rtaddr)) { return(dr); + } } return(NULL); /* search failed */ } void -defrouter_delreq(dr, dofree) - struct nd_defrouter *dr; - int dofree; -{ - struct sockaddr_in6 def, mask, gate; - - Bzero(&def, sizeof(def)); - Bzero(&mask, sizeof(mask)); - Bzero(&gate, sizeof(gate)); - - def.sin6_len = mask.sin6_len = gate.sin6_len - = sizeof(struct sockaddr_in6); - def.sin6_family = mask.sin6_family = gate.sin6_family = AF_INET6; - gate.sin6_addr = dr->rtaddr; - - rtrequest(RTM_DELETE, (struct sockaddr *)&def, - (struct sockaddr *)&gate, - (struct sockaddr *)&mask, - RTF_GATEWAY, (struct rtentry **)0); - - if (dofree) /* XXX: necessary? */ - free(dr, M_IP6NDP); -} - -void defrtrlist_del(dr) struct nd_defrouter *dr; { @@ -532,14 +606,13 @@ defrtrlist_del(dr) * Flush all the routing table entries that use the router * as a next hop. */ - if (!ip6_forwarding && ip6_accept_rtadv) { - /* above is a good condition? */ + if (!ip6_forwarding && ip6_accept_rtadv) /* XXX: better condition? */ rt6_flush(&dr->rtaddr, dr->ifp); - } - - if (dr == TAILQ_FIRST(&nd_defrouter)) - deldr = dr; /* The router is primary. */ + if (dr->installed) { + deldr = dr; + defrouter_delreq(dr); + } TAILQ_REMOVE(&nd_defrouter, dr, dr_entry); /* @@ -564,87 +637,242 @@ defrtrlist_del(dr) } /* - * Default Router Selection according to Section 6.3.6 of RFC 2461: - * 1) Routers that are reachable or probably reachable should be - * preferred. + * Remove the default route for a given router. + * This is just a subroutine function for defrouter_select(), and should + * not be called from anywhere else. + */ +static void +defrouter_delreq(dr) + struct nd_defrouter *dr; +{ + struct sockaddr_in6 def, mask, gw; + struct rtentry *oldrt = NULL; + + Bzero(&def, sizeof(def)); + Bzero(&mask, sizeof(mask)); + Bzero(&gw, sizeof(gw)); /* for safety */ + + def.sin6_len = mask.sin6_len = gw.sin6_len = + sizeof(struct sockaddr_in6); + def.sin6_family = mask.sin6_family = gw.sin6_family = AF_INET6; + gw.sin6_addr = dr->rtaddr; +#ifndef SCOPEDROUTING + gw.sin6_scope_id = 0; /* XXX */ +#endif + + rtrequest(RTM_DELETE, (struct sockaddr *)&def, + dr ? (struct sockaddr *)&gw : NULL, + (struct sockaddr *)&mask, RTF_GATEWAY, &oldrt); + if (oldrt) { + nd6_rtmsg(RTM_DELETE, oldrt); + if (oldrt->rt_refcnt <= 0) { + /* + * XXX: borrowed from the RTM_DELETE case of + * rtrequest(). + */ + oldrt->rt_refcnt++; + rtfree(oldrt); + } + } + + if (dr) + dr->installed = 0; +} + +/* + * remove all default routes from default router list + */ +void +defrouter_reset() +{ + struct nd_defrouter *dr; + + for (dr = TAILQ_FIRST(&nd_defrouter); dr; + dr = TAILQ_NEXT(dr, dr_entry)) + defrouter_delreq(dr); + defrouter_delifreq(); + + /* + * XXX should we also nuke any default routers in the kernel, by + * going through them by rtalloc1()? + */ +} + +/* + * Default Router Selection according to Section 6.3.6 of RFC 2461 and + * draft-ietf-ipngwg-router-selection: + * 1) Routers that are reachable or probably reachable should be preferred. + * If we have more than one (probably) reachable router, prefer ones + * with the highest router preference. * 2) When no routers on the list are known to be reachable or * probably reachable, routers SHOULD be selected in a round-robin - * fashion. + * fashion, regardless of router preference values. * 3) If the Default Router List is empty, assume that all * destinations are on-link. + * + * We assume nd_defrouter is sorted by router preference value. + * Since the code below covers both with and without router preference cases, + * we do not need to classify the cases by ifdef. + * + * At this moment, we do not try to install more than one default router, + * even when the multipath routing is available, because we're not sure about + * the benefits for stub hosts comparing to the risk of making the code + * complicated and the possibility of introducing bugs. */ void defrouter_select() { int s = splnet(); - struct nd_defrouter *dr, anydr; + struct nd_defrouter *dr, *selected_dr = NULL, *installed_dr = NULL; struct rtentry *rt = NULL; struct llinfo_nd6 *ln = NULL; /* - * Search for a (probably) reachable router from the list. + * This function should be called only when acting as an autoconfigured + * host. Although the remaining part of this function is not effective + * if the node is not an autoconfigured host, we explicitly exclude + * such cases here for safety. */ - 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 (ip6_forwarding || !ip6_accept_rtadv) { + nd6log((LOG_WARNING, + "defrouter_select: called unexpectedly (forwarding=%d, " + "accept_rtadv=%d)\n", ip6_forwarding, ip6_accept_rtadv)); + splx(s); + return; } - 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 { + /* + * Let's handle easy case (3) first: + * If default router list is empty, we should probably install + * an interface route and assume that all destinations are on-link. + */ + if (!TAILQ_FIRST(&nd_defrouter)) { /* - * 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. + * This test is meaningless due to a test at the beginning of + * the function, but we intentionally keep it to make the note + * clear. */ 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. - * XXX: we enable this for host only, because - * this may override a default route installed - * a user process (e.g. routing daemon) in a - * router case. */ defrouter_addifreq(nd6_defifp); } else { + /* + * purge the existing route. + * XXX: is this really correct? + */ + defrouter_delifreq(); nd6log((LOG_INFO, "defrouter_select: " "there's no default router and no default" " interface\n")); } } + splx(s); + return; + } + + /* + * If we have a default route for the default interface, delete it. + * Note that the existence of the route is checked in the delete + * function. + */ + defrouter_delifreq(); + + /* + * Search for a (probably) reachable router from the list. + * We just pick up the first reachable one (if any), assuming that + * the ordering rule of the list described in defrtrlist_update(). + */ + for (dr = TAILQ_FIRST(&nd_defrouter); dr; + dr = TAILQ_NEXT(dr, dr_entry)) { + if (!selected_dr && + (rt = nd6_lookup(&dr->rtaddr, 0, dr->ifp)) && + (ln = (struct llinfo_nd6 *)rt->rt_llinfo) && + ND6_IS_LLINFO_PROBREACH(ln)) { + selected_dr = dr; + } + + if (dr->installed && !installed_dr) + installed_dr = dr; + else if (dr->installed && installed_dr) { + /* this should not happen. warn for diagnosis. */ + log(LOG_ERR, "defrouter_select: more than one router" + " is installed\n"); + } + } + /* + * If none of the default routers was found to be reachable, + * round-robin the list regardless of preference. + * Otherwise, if we have an installed router, check if the selected + * (reachable) router should really be preferred to the installed one. + * We only prefer the new router when the old one is not reachable + * or when the new one has a really higher preference value. + */ + if (!selected_dr) { + if (!installed_dr || !TAILQ_NEXT(installed_dr, dr_entry)) + selected_dr = TAILQ_FIRST(&nd_defrouter); + else + selected_dr = TAILQ_NEXT(installed_dr, dr_entry); + } else if (installed_dr && + (rt = nd6_lookup(&installed_dr->rtaddr, 0, installed_dr->ifp)) && + (ln = (struct llinfo_nd6 *)rt->rt_llinfo) && + ND6_IS_LLINFO_PROBREACH(ln) && + rtpref(selected_dr) <= rtpref(installed_dr)) { + selected_dr = installed_dr; + } + + /* + * If the selected router is different than the installed one, + * remove the installed router and install the selected one. + * Note that the selected router is never NULL here. + */ + if (installed_dr != selected_dr) { + if (installed_dr) + defrouter_delreq(installed_dr); + defrouter_addreq(selected_dr); } splx(s); return; } +/* + * for default router selection + * regards router-preference field as a 2-bit signed integer + */ +static int +rtpref(struct nd_defrouter *dr) +{ +#ifdef RTPREF + switch (dr->flags & ND_RA_FLAG_RTPREF_MASK) { + case ND_RA_FLAG_RTPREF_HIGH: + return RTPREF_HIGH; + case ND_RA_FLAG_RTPREF_MEDIUM: + case ND_RA_FLAG_RTPREF_RSV: + return RTPREF_MEDIUM; + case ND_RA_FLAG_RTPREF_LOW: + return RTPREF_LOW; + default: + /* + * This case should never happen. If it did, it would mean a + * serious bug of kernel internal. We thus always bark here. + * Or, can we even panic? + */ + log(LOG_ERR, "rtpref: impossible RA flag %x", dr->flags); + return RTPREF_INVALID; + } + /* NOTREACHED */ +#else + return 0; +#endif +} + static struct nd_defrouter * defrtrlist_update(new) struct nd_defrouter *new; @@ -658,10 +886,34 @@ defrtrlist_update(new) defrtrlist_del(dr); dr = NULL; } else { + int oldpref = rtpref(dr); + /* override */ dr->flags = new->flags; /* xxx flag check */ dr->rtlifetime = new->rtlifetime; dr->expire = new->expire; + + /* + * If the preference does not change, there's no need + * to sort the entries. + */ + if (rtpref(new) == oldpref) { + splx(s); + return(dr); + } + + /* + * preferred router may be changed, so relocate + * this router. + * XXX: calling TAILQ_REMOVE directly is a bad manner. + * However, since defrtrlist_del() has many side + * effects, we intentionally do so here. + * defrouter_select() below will handle routing + * changes later. + */ + TAILQ_REMOVE(&nd_defrouter, dr, dr_entry); + n = dr; + goto insert; } splx(s); return(dr); @@ -681,14 +933,27 @@ defrtrlist_update(new) bzero(n, sizeof(*n)); *n = *new; +insert: /* - * 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. + * Insert the new router in the Default Router List; + * The Default Router List should be in the descending order + * of router-preferece. Routers with the same preference are + * sorted in the arriving time order. */ - TAILQ_INSERT_TAIL(&nd_defrouter, n, dr_entry); - if (TAILQ_FIRST(&nd_defrouter) == n) - defrouter_select(); + + /* insert at the end of the group */ + for (dr = TAILQ_FIRST(&nd_defrouter); dr; + dr = TAILQ_NEXT(dr, dr_entry)) { + if (rtpref(n) > rtpref(dr)) + break; + } + if (dr) + TAILQ_INSERT_BEFORE(dr, n, dr_entry); + else + TAILQ_INSERT_TAIL(&nd_defrouter, n, dr_entry); + + defrouter_select(); + splx(s); return(n); @@ -735,8 +1000,8 @@ pfxrtr_del(pfr) free(pfr, M_IP6NDP); } -static struct nd_prefix * -prefix_lookup(pr) +struct nd_prefix * +nd6_prefix_lookup(pr) struct nd_prefix *pr; { struct nd_prefix *search; @@ -745,9 +1010,7 @@ prefix_lookup(pr) if (pr->ndpr_ifp == search->ndpr_ifp && pr->ndpr_plen == search->ndpr_plen && in6_are_prefix_equal(&pr->ndpr_prefix.sin6_addr, - &search->ndpr_prefix.sin6_addr, - pr->ndpr_plen) - ) { + &search->ndpr_prefix.sin6_addr, pr->ndpr_plen)) { break; } } @@ -755,12 +1018,12 @@ prefix_lookup(pr) return(search); } -static int -prelist_add(pr, dr) - struct nd_prefix *pr; +int +nd6_prelist_add(pr, dr, newp) + struct nd_prefix *pr, **newp; struct nd_defrouter *dr; { - struct nd_prefix *new; + struct nd_prefix *new = NULL; int i, s; new = (struct nd_prefix *)malloc(sizeof(*new), M_IP6NDP, M_NOWAIT); @@ -768,24 +1031,35 @@ prelist_add(pr, dr) return ENOMEM; bzero(new, sizeof(*new)); *new = *pr; + if (newp != NULL) + *newp = new; /* initilization */ - new->ndpr_statef_onlink = pr->ndpr_statef_onlink; LIST_INIT(&new->ndpr_advrtrs); in6_prefixlen2mask(&new->ndpr_mask, new->ndpr_plen); /* make prefix in the canonical form */ for (i = 0; i < 4; i++) new->ndpr_prefix.sin6_addr.s6_addr32[i] &= - new->ndpr_mask.s6_addr32[i]; - - /* xxx ND_OPT_PI_FLAG_ONLINK processing */ + new->ndpr_mask.s6_addr32[i]; s = splnet(); - /* link ndpr_entry to nd_prefix list */ LIST_INSERT_HEAD(&nd_prefix, new, ndpr_entry); splx(s); + /* ND_OPT_PI_FLAG_ONLINK processing */ + if (new->ndpr_raf_onlink) { + int e; + + if ((e = nd6_prefix_onlink(new)) != 0) { + nd6log((LOG_ERR, "nd6_prelist_add: failed to make " + "the prefix %s/%d on-link on %s (errno=%d)\n", + ip6_sprintf(&pr->ndpr_prefix.sin6_addr), + pr->ndpr_plen, pr->ndpr_ifp->if_xname, e)); + /* proceed anyway. XXX: is it correct? */ + } + } + if (dr) pfxrtr_add(new, dr); @@ -797,9 +1071,36 @@ prelist_remove(pr) struct nd_prefix *pr; { struct nd_pfxrouter *pfr, *next; - int s; + int e, s; + /* make sure to invalidate the prefix until it is really freed. */ + pr->ndpr_vltime = 0; + pr->ndpr_pltime = 0; +#if 0 + /* + * Though these flags are now meaningless, we'd rather keep the value + * not to confuse users when executing "ndp -p". + */ + pr->ndpr_raf_onlink = 0; + pr->ndpr_raf_auto = 0; +#endif + if ((pr->ndpr_stateflags & NDPRF_ONLINK) != 0 && + (e = nd6_prefix_offlink(pr)) != 0) { + nd6log((LOG_ERR, "prelist_remove: failed to make %s/%d offlink " + "on %s, errno=%d\n", + ip6_sprintf(&pr->ndpr_prefix.sin6_addr), + pr->ndpr_plen, pr->ndpr_ifp->if_xname, e)); + /* what should we do? */ + } + + if (pr->ndpr_refcnt > 0) + return; /* notice here? */ + +#ifdef __NetBSD__ + s = splsoftnet(); +#else s = splnet(); +#endif /* unlink ndpr_entry from nd_prefix list */ LIST_REMOVE(pr, ndpr_entry); @@ -817,25 +1118,21 @@ prelist_remove(pr) pfxlist_onlink_check(); } -/* - * NOTE: We set address lifetime to keep - * address lifetime <= prefix lifetime - * invariant. This is to simplify on-link determination code. - * If onlink determination is udated, this routine may have to be updated too. - */ int prelist_update(new, dr, m) struct nd_prefix *new; struct nd_defrouter *dr; /* may be NULL */ struct mbuf *m; { - struct in6_ifaddr *ia6 = NULL; + struct in6_ifaddr *ia6 = NULL, *ia6_match = NULL; + struct ifaddr *ifa; + struct ifnet *ifp = new->ndpr_ifp; struct nd_prefix *pr; int s = splnet(); - int error = 0; + int newprefix = 0; int auth; - struct in6_addrlifetime *lt6; + struct in6_addrlifetime lt6_tmp; auth = 0; if (m) { @@ -849,165 +1146,230 @@ prelist_update(new, dr, m) #endif } - if ((pr = prefix_lookup(new)) != NULL) { - if (pr->ndpr_ifp != new->ndpr_ifp) { - error = EADDRNOTAVAIL; - goto end; - } - /* update prefix information */ - pr->ndpr_flags = new->ndpr_flags; - pr->ndpr_vltime = new->ndpr_vltime; - pr->ndpr_pltime = new->ndpr_pltime; - pr->ndpr_preferred = new->ndpr_preferred; - pr->ndpr_expire = new->ndpr_expire; + if ((pr = nd6_prefix_lookup(new)) != NULL) { + /* + * nd6_prefix_lookup() ensures that pr and new have the same + * prefix on a same interface. + */ /* - * RFC 2462 5.5.3 (d) or (e) - * We got a prefix which we have seen in the past. + * Update prefix information. Note that the on-link (L) bit + * and the autonomous (A) bit should NOT be changed from 1 + * to 0. */ - if (!new->ndpr_raf_auto) - goto noautoconf1; + if (new->ndpr_raf_onlink == 1) + pr->ndpr_raf_onlink = 1; + if (new->ndpr_raf_auto == 1) + pr->ndpr_raf_auto = 1; + if (new->ndpr_raf_onlink) { + pr->ndpr_vltime = new->ndpr_vltime; + pr->ndpr_pltime = new->ndpr_pltime; + pr->ndpr_preferred = new->ndpr_preferred; + pr->ndpr_expire = new->ndpr_expire; + pr->ndpr_lastupdate = new->ndpr_lastupdate; + } - if (IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr)) - ia6 = NULL; - else - ia6 = in6ifa_ifpwithaddr(pr->ndpr_ifp, &pr->ndpr_addr); + if (new->ndpr_raf_onlink && + (pr->ndpr_stateflags & NDPRF_ONLINK) == 0) { + int e; - if (ia6 == NULL) { - /* - * Special case: - * (1) We have seen the prefix advertised before, but - * we have never performed autoconfig for this prefix. - * This is because Autonomous bit was 0 previously, or - * autoconfig failed due to some other reasons. - * (2) We have seen the prefix advertised before and - * we have performed autoconfig in the past, but - * we seem to have no interface address right now. - * This is because the interface address have expired. - * - * This prefix is fresh, with respect to autoconfig - * process. - * - * Add an address based on RFC 2462 5.5.3 (d). - */ - ia6 = in6_ifadd(pr->ndpr_ifp, - &pr->ndpr_prefix.sin6_addr, &pr->ndpr_addr, - new->ndpr_plen); - if (!ia6) { - error = EADDRNOTAVAIL; + if ((e = nd6_prefix_onlink(pr)) != 0) { nd6log((LOG_ERR, - "prelist_update: failed to add a " - "new address\n")); - goto noautoconf1; + "prelist_update: failed to make " + "the prefix %s/%d on-link on %s " + "(errno=%d)\n", + ip6_sprintf(&pr->ndpr_prefix.sin6_addr), + pr->ndpr_plen, pr->ndpr_ifp->if_xname, e)); + /* proceed anyway. XXX: is it correct? */ } + } - lt6 = &ia6->ia6_lifetime; + if (dr && pfxrtr_lookup(pr, dr) == NULL) + pfxrtr_add(pr, dr); + } else { + struct nd_prefix *newpr = NULL; - /* address lifetime <= prefix lifetime */ - lt6->ia6t_vltime = new->ndpr_vltime; - lt6->ia6t_pltime = new->ndpr_pltime; - in6_init_address_ltimes(new, lt6, 1); - } else { -#define TWOHOUR (120*60) - /* - * We have seen the prefix before, and we have added - * interface address in the past. We still have - * the interface address assigned. - * - * update address lifetime based on RFC 2462 - * 5.5.3 (e). - */ - int update = 0; - - lt6 = &ia6->ia6_lifetime; - -#if 0 /* RFC 2462 5.5.3 (e) */ - lt6->ia6t_pltime = new->ndpr_pltime; - if (TWOHOUR < new->ndpr_vltime - || lt6pr->nd < new->ndpr_vltime) { - lt6->ia6t_vltime = new->ndpr_vltime; - update++; - } else if (auth - && lt6->ia6t_vltime <= TWOHOUR0 - && new->ndpr_vltime <= lt6->ia6t_vltime) { - lt6->ia6t_vltime = new->ndpr_vltime; - update++; - } else { - lt6->ia6t_vltime = TWOHOUR; - update++; - } + newprefix = 1; - /* 2 hour rule is not imposed for pref lifetime */ - new->ndpr_apltime = new->ndpr_pltime; - lt6->ia6t_pltime = new->ndpr_pltime; -#else /* update from Jim Bound, (ipng 6712) */ - if (TWOHOUR < new->ndpr_vltime - || lt6->ia6t_vltime < new->ndpr_vltime) { - lt6->ia6t_vltime = new->ndpr_vltime; - update++; - } else if (auth) { - lt6->ia6t_vltime = new->ndpr_vltime; - update++; - } + if (new->ndpr_vltime == 0) + goto end; + if (new->ndpr_raf_onlink == 0 && new->ndpr_raf_auto == 0) + goto end; - /* jim bound rule is not imposed for pref lifetime */ - lt6->ia6t_pltime = new->ndpr_pltime; -#endif - in6_init_address_ltimes(new, lt6, update); + error = nd6_prelist_add(new, dr, &newpr); + if (error != 0 || newpr == NULL) { + nd6log((LOG_NOTICE, "prelist_update: " + "nd6_prelist_add failed for %s/%d on %s " + "errno=%d, returnpr=%p\n", + ip6_sprintf(&new->ndpr_prefix.sin6_addr), + new->ndpr_plen, new->ndpr_ifp->if_xname, + error, newpr)); + goto end; /* we should just give up in this case. */ + } + + /* + * XXX: from the ND point of view, we can ignore a prefix + * with the on-link bit being zero. However, we need a + * prefix structure for references from autoconfigured + * addresses. Thus, we explicitly make sure that the prefix + * itself expires now. + */ + if (newpr->ndpr_raf_onlink == 0) { + newpr->ndpr_vltime = 0; + newpr->ndpr_pltime = 0; + in6_init_prefix_ltimes(newpr); } - noautoconf1: + pr = newpr; + } -#if 0 - /* address lifetime expire processing, RFC 2462 5.5.4. */ - if (pr->ndpr_preferred && pr->ndpr_preferred < time_second) { - struct in6_ifaddr *ia6; + /* + * Address autoconfiguration based on Section 5.5.3 of RFC 2462. + * Note that pr must be non NULL at this point. + */ - ia6 = in6ifa_ifpwithaddr(pr->ndpr_ifp, &pr->ndpr_addr); - if (ia6) - ia6->ia6_flags &= ~IN6_IFF_DEPRECATED; - } -#endif + /* 5.5.3 (a). Ignore the prefix without the A bit set. */ + if (!new->ndpr_raf_auto) + goto end; - if (dr && pfxrtr_lookup(pr, dr) == NULL) - pfxrtr_add(pr, dr); - } else { - int error_tmp; + /* + * 5.5.3 (b). the link-local prefix should have been ignored in + * nd6_ra_input. + */ + + /* + * 5.5.3 (c). Consistency check on lifetimes: pltime <= vltime. + * This should have been done in nd6_ra_input. + */ + + /* + * 5.5.3 (d). If the prefix advertised does not match the prefix of an + * address already in the list, and the Valid Lifetime is not 0, + * form an address. Note that even a manually configured address + * should reject autoconfiguration of a new address. + */ + for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next) + { + struct in6_ifaddr *ifa6; + int ifa_plen; + u_int32_t storedlifetime; + long time_second = time.tv_sec; - if (new->ndpr_vltime == 0) goto end; + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; - bzero(&new->ndpr_addr, sizeof(struct in6_addr)); + ifa6 = (struct in6_ifaddr *)ifa; /* - * RFC 2462 5.5.3 (d) - * We got a fresh prefix. Perform some sanity checks - * and add an interface address by appending interface ID - * to the advertised prefix. + * Spec is not clear here, but I believe we should concentrate + * on unicast (i.e. not anycast) addresses. + * XXX: other ia6_flags? detached or duplicated? */ - if (!new->ndpr_raf_auto) - goto noautoconf2; - - ia6 = in6_ifadd(new->ndpr_ifp, &new->ndpr_prefix.sin6_addr, - &new->ndpr_addr, new->ndpr_plen); - if (!ia6) { - error = EADDRNOTAVAIL; - nd6log((LOG_ERR, "prelist_update: " - "failed to add a new address\n")); - goto noautoconf2; + if ((ifa6->ia6_flags & IN6_IFF_ANYCAST) != 0) + continue; + + ifa_plen = in6_mask2len(&ifa6->ia_prefixmask.sin6_addr, NULL); + if (ifa_plen != new->ndpr_plen || + !in6_are_prefix_equal(&ifa6->ia_addr.sin6_addr, + &new->ndpr_prefix.sin6_addr, ifa_plen)) + continue; + + if (ia6_match == NULL) /* remember the first one */ + ia6_match = ifa6; + + if ((ifa6->ia6_flags & IN6_IFF_AUTOCONF) == 0) + continue; + + /* + * An already autoconfigured address matched. Now that we + * are sure there is at least one matched address, we can + * proceed to 5.5.3. (e): update the lifetimes according to the + * "two hours" rule and the privacy extension. + */ +#define TWOHOUR (120*60) + /* + * RFC2462 introduces the notion of StoredLifetime to the + * "two hours" rule as follows: + * the Lifetime associated with the previously autoconfigured + * address. + * Our interpretation of this definition is "the remaining + * lifetime to expiration at the evaluation time". One might + * be wondering if this interpretation is really conform to the + * RFC, because the text can read that "Lifetimes" are never + * decreased, and our definition of the "storedlifetime" below + * essentially reduces the "Valid Lifetime" advertised in the + * previous RA. But, this is due to the wording of the text, + * and our interpretation is the same as an author's intention. + * See the discussion in the IETF ipngwg ML in August 2001, + * with the Subject "StoredLifetime in RFC 2462". + */ + lt6_tmp = ifa6->ia6_lifetime; + if (lt6_tmp.ia6t_vltime == ND6_INFINITE_LIFETIME) + storedlifetime = ND6_INFINITE_LIFETIME; + else if (time_second - ifa6->ia6_updatetime > + lt6_tmp.ia6t_vltime) { + /* + * The case of "invalid" address. We should usually + * not see this case. + */ + storedlifetime = 0; + } else + storedlifetime = lt6_tmp.ia6t_vltime - + (time_second - ifa6->ia6_updatetime); + if (TWOHOUR < new->ndpr_vltime || + storedlifetime < new->ndpr_vltime) { + lt6_tmp.ia6t_vltime = new->ndpr_vltime; + } else if (storedlifetime <= TWOHOUR +#if 0 + /* + * This condition is logically redundant, so we just + * omit it. + * See IPng 6712, 6717, and 6721. + */ + && new->ndpr_vltime <= storedlifetime +#endif + ) { + if (auth) { + lt6_tmp.ia6t_vltime = new->ndpr_vltime; + } + } else { + /* + * new->ndpr_vltime <= TWOHOUR && + * TWOHOUR < storedlifetime + */ + lt6_tmp.ia6t_vltime = TWOHOUR; } - /* set onlink bit if an interface route is configured */ - new->ndpr_statef_onlink = (ia6->ia_flags & IFA_ROUTE) ? 1 : 0; - lt6 = &ia6->ia6_lifetime; + /* The 2 hour rule is not imposed for preferred lifetime. */ + lt6_tmp.ia6t_pltime = new->ndpr_pltime; - /* address lifetime <= prefix lifetime */ - lt6->ia6t_vltime = new->ndpr_vltime; - lt6->ia6t_pltime = new->ndpr_pltime; - in6_init_address_ltimes(new, lt6, 1); + in6_init_address_ltimes(pr, <6_tmp); - noautoconf2: - error_tmp = prelist_add(new, dr); - error = error_tmp ? error_tmp : error; + ifa6->ia6_lifetime = lt6_tmp; + ifa6->ia6_updatetime = time_second; + } + if (ia6_match == NULL && new->ndpr_vltime) { + /* + * No address matched and the valid lifetime is non-zero. + * Create a new address. + */ + if ((ia6 = in6_ifadd(new)) != NULL) { + /* + * note that we should use pr (not new) for reference. + */ + pr->ndpr_refcnt++; + ia6->ia6_ndpr = pr; + + /* + * A newly added address might affect the status + * of other addresses, so we check and update it. + * XXX: what if address duplication happens? + */ + pfxlist_onlink_check(); + } else { + /* just set an error. do not bark here. */ + error = EADDRNOTAVAIL; /* XXX: might be unused. */ + } } end: @@ -1031,26 +1393,25 @@ find_pfxlist_reachable_router(pr) 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)) && + 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 (A router is "available" if its neighbor cache - * entry has reachable or probably reachable). + * that advertised the prefix (a router is "available" if its neighbor cache + * entry is 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 available router, we still regards - * all the prefixes as on-link. This is because we can't tell if all the + * 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 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. */ @@ -1058,162 +1419,387 @@ void pfxlist_onlink_check() { struct nd_prefix *pr; + struct in6_ifaddr *ifa; /* * 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)) + if (pr->ndpr_raf_onlink && find_pfxlist_reachable_router(pr)) break; } - - if (pr) { + if (pr != NULL || TAILQ_FIRST(&nd_defrouter) != NULL) { /* - * 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. + * There is at least one prefix that has a reachable router, + * or at least a router which probably does not advertise + * any prefixes. The latter would be the case when we move + * to a new link where we have a router that does not provide + * prefixes and we configure an address by hand. + * Detach prefixes which have no reachable advertising + * router, and attach other prefixes. */ for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) { - if (find_pfxlist_reachable_router(pr) == NULL && - pr->ndpr_statef_onlink) { - pr->ndpr_statef_onlink = 0; - nd6_detach_prefix(pr); - } + /* XXX: a link-local prefix should never be detached */ + if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr)) + continue; + + /* + * we aren't interested in prefixes without the L bit + * set. + */ + if (pr->ndpr_raf_onlink == 0) + continue; + + if ((pr->ndpr_stateflags & NDPRF_DETACHED) == 0 && + find_pfxlist_reachable_router(pr) == NULL) + pr->ndpr_stateflags |= NDPRF_DETACHED; + if ((pr->ndpr_stateflags & NDPRF_DETACHED) != 0 && + find_pfxlist_reachable_router(pr) != 0) + pr->ndpr_stateflags &= ~NDPRF_DETACHED; } + } else { + /* there is no prefix that has a reachable router */ for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) { - if (find_pfxlist_reachable_router(pr) && - pr->ndpr_statef_onlink == 0) - nd6_attach_prefix(pr); + if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr)) + continue; + + if (pr->ndpr_raf_onlink == 0) + continue; + + if ((pr->ndpr_stateflags & NDPRF_DETACHED) != 0) + pr->ndpr_stateflags &= ~NDPRF_DETACHED; + } + } + + /* + * Remove each interface route associated with a (just) detached + * prefix, and reinstall the interface route for a (just) attached + * prefix. Note that all attempt of reinstallation does not + * necessarily success, when a same prefix is shared among multiple + * interfaces. Such cases will be handled in nd6_prefix_onlink, + * so we don't have to care about them. + */ + for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) { + int e; + + if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr)) + continue; + + if (pr->ndpr_raf_onlink == 0) + continue; + + if ((pr->ndpr_stateflags & NDPRF_DETACHED) != 0 && + (pr->ndpr_stateflags & NDPRF_ONLINK) != 0) { + if ((e = nd6_prefix_offlink(pr)) != 0) { + nd6log((LOG_ERR, + "pfxlist_onlink_check: failed to " + "make %s/%d offlink, errno=%d\n", + ip6_sprintf(&pr->ndpr_prefix.sin6_addr), + pr->ndpr_plen, e)); + } + } + if ((pr->ndpr_stateflags & NDPRF_DETACHED) == 0 && + (pr->ndpr_stateflags & NDPRF_ONLINK) == 0 && + pr->ndpr_raf_onlink) { + if ((e = nd6_prefix_onlink(pr)) != 0) { + nd6log((LOG_ERR, + "pfxlist_onlink_check: failed to " + "make %s/%d offlink, errno=%d\n", + ip6_sprintf(&pr->ndpr_prefix.sin6_addr), + pr->ndpr_plen, e)); + } + } + } + + /* + * Changes on the prefix status might affect address status as well. + * Make sure that all addresses derived from an attached prefix are + * attached, and that all addresses derived from a detached prefix are + * detached. Note, however, that a manually configured address should + * always be attached. + * The precise detection logic is same as the one for prefixes. + */ + for (ifa = in6_ifaddr; ifa; ifa = ifa->ia_next) { + if (!(ifa->ia6_flags & IN6_IFF_AUTOCONF)) + continue; + + if (ifa->ia6_ndpr == NULL) { + /* + * This can happen when we first configure the address + * (i.e. the address exists, but the prefix does not). + * XXX: complicated relationships... + */ + continue; + } + + if (find_pfxlist_reachable_router(ifa->ia6_ndpr)) + break; + } + if (ifa) { + for (ifa = in6_ifaddr; ifa; ifa = ifa->ia_next) { + if ((ifa->ia6_flags & IN6_IFF_AUTOCONF) == 0) + continue; + + if (ifa->ia6_ndpr == NULL) /* XXX: see above. */ + continue; + + if (find_pfxlist_reachable_router(ifa->ia6_ndpr)) + ifa->ia6_flags &= ~IN6_IFF_DETACHED; + else + ifa->ia6_flags |= IN6_IFF_DETACHED; } } else { - /* 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); + for (ifa = in6_ifaddr; ifa; ifa = ifa->ia_next) { + if ((ifa->ia6_flags & IN6_IFF_AUTOCONF) == 0) + continue; + + ifa->ia6_flags &= ~IN6_IFF_DETACHED; + } } } -static void -nd6_detach_prefix(pr) +int +nd6_prefix_onlink(pr) struct nd_prefix *pr; { - struct in6_ifaddr *ia6; - struct sockaddr_in6 sa6, mask6; + struct ifaddr *ifa; + struct ifnet *ifp = pr->ndpr_ifp; + struct sockaddr_in6 mask6; + struct nd_prefix *opr; + u_long rtflags; + int error = 0; + struct rtentry *rt = NULL; + + /* sanity check */ + if ((pr->ndpr_stateflags & NDPRF_ONLINK) != 0) { + nd6log((LOG_ERR, + "nd6_prefix_onlink: %s/%d is already on-link\n", + ip6_sprintf(&pr->ndpr_prefix.sin6_addr), pr->ndpr_plen); + return(EEXIST)); + } /* - * Delete the interface route associated with the prefix. + * Add the interface route associated with the prefix. Before + * installing the route, check if there's the same prefix on another + * interface, and the prefix has already installed the interface route. + * Although such a configuration is expected to be rare, we explicitly + * allow it. */ - bzero(&sa6, sizeof(sa6)); - sa6.sin6_family = AF_INET6; - sa6.sin6_len = sizeof(sa6); - bcopy(&pr->ndpr_prefix.sin6_addr, &sa6.sin6_addr, - sizeof(struct in6_addr)); - bzero(&mask6, sizeof(mask6)); - mask6.sin6_family = AF_INET6; - mask6.sin6_len = sizeof(sa6); - bcopy(&pr->ndpr_mask, &mask6.sin6_addr, sizeof(struct in6_addr)); - { - int e; + for (opr = nd_prefix.lh_first; opr; opr = opr->ndpr_next) { + if (opr == pr) + continue; + + if ((opr->ndpr_stateflags & NDPRF_ONLINK) == 0) + continue; + + if (opr->ndpr_plen == pr->ndpr_plen && + in6_are_prefix_equal(&pr->ndpr_prefix.sin6_addr, + &opr->ndpr_prefix.sin6_addr, pr->ndpr_plen)) + return(0); + } - e = rtrequest(RTM_DELETE, (struct sockaddr *)&sa6, NULL, - (struct sockaddr *)&mask6, 0, NULL); - if (e) { - nd6log((LOG_ERR, - "nd6_detach_prefix: failed to delete route: " - "%s/%d (errno = %d)\n", - ip6_sprintf(&sa6.sin6_addr), - pr->ndpr_plen, - e)); + /* + * We prefer link-local addresses as the associated interface address. + */ + /* search for a link-local addr */ + ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp, + IN6_IFF_NOTREADY | IN6_IFF_ANYCAST); + if (ifa == NULL) { + /* XXX: freebsd does not have ifa_ifwithaf */ + for (ifa = ifp->if_addrlist.tqh_first; + ifa; + ifa = ifa->ifa_list.tqe_next) + { + if (ifa->ifa_addr->sa_family == AF_INET6) + break; } + /* should we care about ia6_flags? */ + } + if (ifa == NULL) { + /* + * This can still happen, when, for example, we receive an RA + * containing a prefix with the L bit set and the A bit clear, + * after removing all IPv6 addresses on the receiving + * interface. This should, of course, be rare though. + */ + nd6log((LOG_NOTICE, + "nd6_prefix_onlink: failed to find any ifaddr" + " to add route for a prefix(%s/%d) on %s\n", + ip6_sprintf(&pr->ndpr_prefix.sin6_addr), + pr->ndpr_plen, ifp->if_xname)); + return(0); } /* - * Mark the address derived from the prefix detached so that - * it won't be used as a source address for a new connection. + * in6_ifinit() sets nd6_rtrequest to ifa_rtrequest for all ifaddrs. + * ifa->ifa_rtrequest = nd6_rtrequest; */ - if (IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr)) - ia6 = NULL; - else - ia6 = in6ifa_ifpwithaddr(pr->ndpr_ifp, &pr->ndpr_addr); - if (ia6) - ia6->ia6_flags |= IN6_IFF_DETACHED; + bzero(&mask6, sizeof(mask6)); + mask6.sin6_len = sizeof(mask6); + mask6.sin6_addr = pr->ndpr_mask; + /* rtrequest() will probably set RTF_UP, but we're not sure. */ + rtflags = ifa->ifa_flags | RTF_UP; + if (nd6_need_cache(ifp)) { + /* explicitly set in case ifa_flags does not set the flag. */ + rtflags |= RTF_CLONING; + } else { + /* + * explicitly clear the cloning bit in case ifa_flags sets it. + */ + rtflags &= ~RTF_CLONING; + } + error = rtrequest(RTM_ADD, (struct sockaddr *)&pr->ndpr_prefix, + ifa->ifa_addr, (struct sockaddr *)&mask6, rtflags, &rt); + if (error == 0) { + if (rt != NULL) /* this should be non NULL, though */ + nd6_rtmsg(RTM_ADD, rt); + pr->ndpr_stateflags |= NDPRF_ONLINK; + } else { + nd6log((LOG_ERR, "nd6_prefix_onlink: failed to add route for a" + " prefix (%s/%d) on %s, gw=%s, mask=%s, flags=%lx " + "errno = %d\n", + ip6_sprintf(&pr->ndpr_prefix.sin6_addr), + pr->ndpr_plen, ifp->if_xname, + ip6_sprintf(&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr), + ip6_sprintf(&mask6.sin6_addr), rtflags, error)); + } + + if (rt != NULL) + rt->rt_refcnt--; + + return(error); } -static void -nd6_attach_prefix(pr) +int +nd6_prefix_offlink(pr) struct nd_prefix *pr; { - struct ifaddr *ifa; - struct in6_ifaddr *ia6; + int error = 0; + struct ifnet *ifp = pr->ndpr_ifp; + struct nd_prefix *opr; + struct sockaddr_in6 sa6, mask6; + struct rtentry *rt = NULL; - /* - * Add the interface route associated with the prefix(if necessary) - * Should we consider if the L bit is set in pr->ndpr_flags? - */ - ifa = ifaof_ifpforaddr((struct sockaddr *)&pr->ndpr_prefix, - pr->ndpr_ifp); - if (ifa == NULL) { + /* sanity check */ + if ((pr->ndpr_stateflags & NDPRF_ONLINK) == 0) { nd6log((LOG_ERR, - "nd6_attach_prefix: failed to find any ifaddr" - " to add route for a prefix(%s/%d)\n", - ip6_sprintf(&pr->ndpr_addr), pr->ndpr_plen)); + "nd6_prefix_offlink: %s/%d is already off-link\n", + ip6_sprintf(&pr->ndpr_prefix.sin6_addr), pr->ndpr_plen)); + return(EEXIST); } - else { - int e; - struct sockaddr_in6 mask6; - - bzero(&mask6, sizeof(mask6)); - mask6.sin6_family = AF_INET6; - mask6.sin6_len = sizeof(mask6); - mask6.sin6_addr = pr->ndpr_mask; - e = rtrequest(RTM_ADD, (struct sockaddr *)&pr->ndpr_prefix, - ifa->ifa_addr, (struct sockaddr *)&mask6, - ifa->ifa_flags, NULL); - if (e == 0) - pr->ndpr_statef_onlink = 1; - else { - nd6log((LOG_ERR, - "nd6_attach_prefix: failed to add route for" - " a prefix(%s/%d), errno = %d\n", - ip6_sprintf(&pr->ndpr_addr), pr->ndpr_plen, e)); + + bzero(&sa6, sizeof(sa6)); + sa6.sin6_family = AF_INET6; + sa6.sin6_len = sizeof(sa6); + bcopy(&pr->ndpr_prefix.sin6_addr, &sa6.sin6_addr, + sizeof(struct in6_addr)); + bzero(&mask6, sizeof(mask6)); + mask6.sin6_family = AF_INET6; + mask6.sin6_len = sizeof(sa6); + bcopy(&pr->ndpr_mask, &mask6.sin6_addr, sizeof(struct in6_addr)); + error = rtrequest(RTM_DELETE, (struct sockaddr *)&sa6, NULL, + (struct sockaddr *)&mask6, 0, &rt); + if (error == 0) { + pr->ndpr_stateflags &= ~NDPRF_ONLINK; + + /* report the route deletion to the routing socket. */ + if (rt != NULL) + nd6_rtmsg(RTM_DELETE, rt); + + /* + * There might be the same prefix on another interface, + * the prefix which could not be on-link just because we have + * the interface route (see comments in nd6_prefix_onlink). + * If there's one, try to make the prefix on-link on the + * interface. + */ + for (opr = nd_prefix.lh_first; opr; opr = opr->ndpr_next) { + if (opr == pr) + continue; + + if ((opr->ndpr_stateflags & NDPRF_ONLINK) != 0) + continue; + + /* + * KAME specific: detached prefixes should not be + * on-link. + */ + if ((opr->ndpr_stateflags & NDPRF_DETACHED) != 0) + continue; + + if (opr->ndpr_plen == pr->ndpr_plen && + in6_are_prefix_equal(&pr->ndpr_prefix.sin6_addr, + &opr->ndpr_prefix.sin6_addr, pr->ndpr_plen)) { + int e; + + if ((e = nd6_prefix_onlink(opr)) != 0) { + nd6log((LOG_ERR, + "nd6_prefix_offlink: failed to " + "recover a prefix %s/%d from %s " + "to %s (errno = %d)\n", + ip6_sprintf(&opr->ndpr_prefix.sin6_addr), + opr->ndpr_plen, ifp->if_xname, + opr->ndpr_ifp->if_xname, e)); + } + } } + } else { + /* XXX: can we still set the NDPRF_ONLINK flag? */ + nd6log((LOG_ERR, + "nd6_prefix_offlink: failed to delete route: " + "%s/%d on %s (errno = %d)\n", + ip6_sprintf(&sa6.sin6_addr), pr->ndpr_plen, ifp->if_xname, + error)); } - /* - * Now the address derived from the prefix can be used as a source - * for a new connection, so clear the detached flag. - */ - if (IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr)) - ia6 = NULL; - else - ia6 = in6ifa_ifpwithaddr(pr->ndpr_ifp, &pr->ndpr_addr); - if (ia6) { - ia6->ia6_flags &= ~IN6_IFF_DETACHED; - if (pr->ndpr_statef_onlink) - ia6->ia_flags |= IFA_ROUTE; + if (rt != NULL) { + if (rt->rt_refcnt <= 0) { + /* XXX: we should free the entry ourselves. */ + rt->rt_refcnt++; + rtfree(rt); + } } + + return(error); } static struct in6_ifaddr * -in6_ifadd(ifp, in6, addr, prefixlen) - struct ifnet *ifp; - struct in6_addr *in6; - struct in6_addr *addr; - int prefixlen; /* prefix len of the new prefix in "in6" */ +in6_ifadd(pr) + struct nd_prefix *pr; { + struct ifnet *ifp = pr->ndpr_ifp; struct ifaddr *ifa; - struct in6_ifaddr *ia, *ib, *oia; - int s, error; + struct in6_aliasreq ifra; + struct in6_ifaddr *ia, *ib; + int error, plen0; struct in6_addr mask; + int prefixlen = pr->ndpr_plen; in6_prefixlen2mask(&mask, prefixlen); - /* find link-local address (will be interface ID) */ - ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp, 0);/* 0 is OK? */ + /* + * find a link-local address (will be interface ID). + * Is it really mandatory? Theoretically, a global or a site-local + * address can be configured without a link-local address, if we + * have a unique interface identifier... + * + * it is not mandatory to have a link-local address, we can generate + * interface identifier on the fly. we do this because: + * (1) it should be the easiest way to find interface identifier. + * (2) RFC2462 5.4 suggesting the use of the same interface identifier + * for multiple addresses on a single interface, and possible shortcut + * of DAD. we omitted DAD for this reason in the past. + * (3) a user can prevent autoconfiguration of global address + * by removing link-local address by hand (this is partly because we + * don't have other way to control the use of IPv6 on a interface. + * this has been our design choice - cf. NRL's "ifconfig auto"). + * (4) it is easier to manage when an interface has addresses + * with the same interface identifier, than to have multiple addresses + * with different interface identifiers. + */ + ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp, 0); /* 0 is OK? */ if (ifa) ib = (struct in6_ifaddr *)ifa; else @@ -1228,199 +1814,72 @@ in6_ifadd(ifp, in6, addr, prefixlen) #endif /* prefixlen + ifidlen must be equal to 128 */ - if (prefixlen != in6_mask2len(&ib->ia_prefixmask.sin6_addr)) { - nd6log((LOG_ERR, "in6_ifadd: wrong prefixlen for %s" - "(prefix=%d ifid=%d)\n", ifp->if_xname, - prefixlen, - 128 - in6_mask2len(&ib->ia_prefixmask.sin6_addr))); + plen0 = in6_mask2len(&ib->ia_prefixmask.sin6_addr, NULL); + if (prefixlen != plen0) { + nd6log((LOG_INFO, "in6_ifadd: wrong prefixlen for %s " + "(prefix=%d ifid=%d)\n", + ifp->if_xname, prefixlen, 128 - plen0)); return NULL; } /* make ifaddr */ - ia = (struct in6_ifaddr *)malloc(sizeof(*ia), M_IFADDR, M_DONTWAIT); - if (ia == NULL) { - printf("ENOBUFS in in6_ifadd %d\n", __LINE__); - return NULL; - } - bzero((caddr_t)ia, sizeof(*ia)); - ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; - if (ifp->if_flags & IFF_POINTOPOINT) - ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr; - else - ia->ia_ifa.ifa_dstaddr = NULL; - ia->ia_ifa.ifa_netmask = (struct sockaddr *)&ia->ia_prefixmask; - ia->ia_ifp = ifp; - - /* link to in6_ifaddr */ - if ((oia = in6_ifaddr) != NULL) { - for( ; oia->ia_next; oia = oia->ia_next) - continue; - oia->ia_next = ia; - } else { - /* - * This should be impossible, since we have at least one - * link-local address (see the beginning of this function). - * XXX: should we rather panic here? - */ - printf("in6_ifadd: in6_ifaddr is NULL (impossible!)\n"); - in6_ifaddr = ia; - } - /* gain a refcnt for the link from in6_ifaddr */ - ia->ia_ifa.ifa_refcnt++; - - /* link to if_addrlist */ - TAILQ_INSERT_TAIL(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list); - /* gain another refcnt for the link from if_addrlist */ - ia->ia_ifa.ifa_refcnt++; - - /* new address */ - ia->ia_addr.sin6_len = sizeof(struct sockaddr_in6); - ia->ia_addr.sin6_family = AF_INET6; + bzero(&ifra, sizeof(ifra)); + /* + * in6_update_ifa() does not use ifra_name, but we accurately set it + * for safety. + */ + strncpy(ifra.ifra_name, ifp->if_xname, sizeof(ifra.ifra_name)); + ifra.ifra_addr.sin6_family = AF_INET6; + ifra.ifra_addr.sin6_len = sizeof(struct sockaddr_in6); /* prefix */ - bcopy(in6, &ia->ia_addr.sin6_addr, sizeof(ia->ia_addr.sin6_addr)); - ia->ia_addr.sin6_addr.s6_addr32[0] &= mask.s6_addr32[0]; - ia->ia_addr.sin6_addr.s6_addr32[1] &= mask.s6_addr32[1]; - ia->ia_addr.sin6_addr.s6_addr32[2] &= mask.s6_addr32[2]; - ia->ia_addr.sin6_addr.s6_addr32[3] &= mask.s6_addr32[3]; - /* interface ID */ - ia->ia_addr.sin6_addr.s6_addr32[0] - |= (ib->ia_addr.sin6_addr.s6_addr32[0] & ~mask.s6_addr32[0]); - ia->ia_addr.sin6_addr.s6_addr32[1] - |= (ib->ia_addr.sin6_addr.s6_addr32[1] & ~mask.s6_addr32[1]); - ia->ia_addr.sin6_addr.s6_addr32[2] - |= (ib->ia_addr.sin6_addr.s6_addr32[2] & ~mask.s6_addr32[2]); - ia->ia_addr.sin6_addr.s6_addr32[3] - |= (ib->ia_addr.sin6_addr.s6_addr32[3] & ~mask.s6_addr32[3]); - - /* new prefix */ - ia->ia_prefixmask.sin6_len = sizeof(struct sockaddr_in6); - ia->ia_prefixmask.sin6_family = AF_INET6; - bcopy(&mask, &ia->ia_prefixmask.sin6_addr, - sizeof(ia->ia_prefixmask.sin6_addr)); - - /* same routine */ - ia->ia_ifa.ifa_rtrequest = - (ifp->if_type == IFT_PPP) ? nd6_p2p_rtrequest : nd6_rtrequest; - ia->ia_ifa.ifa_flags |= RTF_CLONING; - ia->ia_ifa.ifa_metric = ifp->if_metric; - - /* add interface route */ - if ((error = rtinit(&(ia->ia_ifa), (int)RTM_ADD, RTF_UP|RTF_CLONING))) { - nd6log((LOG_NOTICE, - "in6_ifadd: failed to add an interface route " - "for %s/%d on %s, errno = %d\n", - ip6_sprintf(&ia->ia_addr.sin6_addr), prefixlen, - ifp->if_xname, error)); - } - else - ia->ia_flags |= IFA_ROUTE; - - *addr = ia->ia_addr.sin6_addr; - - if (ifp->if_flags & IFF_MULTICAST) { - int error; /* not used */ - struct in6_addr sol6; + bcopy(&pr->ndpr_prefix.sin6_addr, &ifra.ifra_addr.sin6_addr, + sizeof(ifra.ifra_addr.sin6_addr)); + ifra.ifra_addr.sin6_addr.s6_addr32[0] &= mask.s6_addr32[0]; + ifra.ifra_addr.sin6_addr.s6_addr32[1] &= mask.s6_addr32[1]; + ifra.ifra_addr.sin6_addr.s6_addr32[2] &= mask.s6_addr32[2]; + ifra.ifra_addr.sin6_addr.s6_addr32[3] &= mask.s6_addr32[3]; - /* Restore saved multicast addresses(if any). */ - in6_restoremkludge(ia, ifp); - - /* join solicited node multicast address */ - bzero(&sol6, sizeof(sol6)); - sol6.s6_addr16[0] = htons(0xff02); - sol6.s6_addr16[1] = htons(ifp->if_index); - sol6.s6_addr32[1] = 0; - sol6.s6_addr32[2] = htonl(1); - sol6.s6_addr32[3] = ia->ia_addr.sin6_addr.s6_addr32[3]; - sol6.s6_addr8[12] = 0xff; - (void)in6_addmulti(&sol6, ifp, &error); - } - - ia->ia6_flags |= IN6_IFF_TENTATIVE; + /* interface ID */ + ifra.ifra_addr.sin6_addr.s6_addr32[0] |= + (ib->ia_addr.sin6_addr.s6_addr32[0] & ~mask.s6_addr32[0]); + ifra.ifra_addr.sin6_addr.s6_addr32[1] |= + (ib->ia_addr.sin6_addr.s6_addr32[1] & ~mask.s6_addr32[1]); + ifra.ifra_addr.sin6_addr.s6_addr32[2] |= + (ib->ia_addr.sin6_addr.s6_addr32[2] & ~mask.s6_addr32[2]); + ifra.ifra_addr.sin6_addr.s6_addr32[3] |= + (ib->ia_addr.sin6_addr.s6_addr32[3] & ~mask.s6_addr32[3]); + + /* new prefix mask. */ + ifra.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6); + ifra.ifra_prefixmask.sin6_family = AF_INET6; + bcopy(&mask, &ifra.ifra_prefixmask.sin6_addr, + sizeof(ifra.ifra_prefixmask.sin6_addr)); /* - * To make the interface up. Only AF_INET6 in ia is used... + * lifetime. + * XXX: in6_init_address_ltimes would override these values later. + * We should reconsider this logic. */ - s = splimp(); - if (ifp->if_ioctl && (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia)) { - splx(s); - return NULL; - } - splx(s); + ifra.ifra_lifetime.ia6t_vltime = pr->ndpr_vltime; + ifra.ifra_lifetime.ia6t_pltime = pr->ndpr_pltime; - /* Perform DAD, if needed. */ - nd6_dad_start((struct ifaddr *)ia, NULL); + /* XXX: scope zone ID? */ - return ia; -} + ifra.ifra_flags |= IN6_IFF_AUTOCONF; /* obey autoconf */ -int -in6_ifdel(ifp, in6) - struct ifnet *ifp; - struct in6_addr *in6; -{ - struct in6_ifaddr *ia = (struct in6_ifaddr *)NULL; - struct in6_ifaddr *oia = (struct in6_ifaddr *)NULL; - - if (!ifp) - return -1; + /* allocate ifaddr structure, link into chain, etc. */ + if ((error = in6_update_ifa(ifp, &ifra, NULL)) != 0) { + nd6log((LOG_ERR, + "in6_ifadd: failed to make ifaddr %s on %s (errno=%d)\n", + ip6_sprintf(&ifra.ifra_addr.sin6_addr), ifp->if_xname, + error)); + return(NULL); /* ifaddr must not have been allocated. */ + } - ia = in6ifa_ifpwithaddr(ifp, in6); - if (!ia) - return -1; + ia = in6ifa_ifpwithaddr(ifp, &ifra.ifra_addr.sin6_addr); - if (ifp->if_flags & IFF_MULTICAST) { - /* - * delete solicited multicast addr for deleting host id - */ - struct in6_multi *in6m; - struct in6_addr llsol; - bzero(&llsol, sizeof(struct in6_addr)); - llsol.s6_addr16[0] = htons(0xff02); - llsol.s6_addr16[1] = htons(ifp->if_index); - llsol.s6_addr32[1] = 0; - llsol.s6_addr32[2] = htonl(1); - llsol.s6_addr32[3] = - ia->ia_addr.sin6_addr.s6_addr32[3]; - llsol.s6_addr8[12] = 0xff; - - IN6_LOOKUP_MULTI(llsol, ifp, in6m); - if (in6m) - in6_delmulti(in6m); - } - - if (ia->ia_flags & IFA_ROUTE) { - rtinit(&(ia->ia_ifa), (int)RTM_DELETE, 0); - ia->ia_flags &= ~IFA_ROUTE; - } - - TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list); - IFAFREE(&ia->ia_ifa); - - /* lladdr is never deleted */ - oia = ia; - if (oia == (ia = in6_ifaddr)) - in6_ifaddr = ia->ia_next; - else { - while (ia->ia_next && (ia->ia_next != oia)) - ia = ia->ia_next; - if (ia->ia_next) - ia->ia_next = oia->ia_next; - else - return -1; - } - - in6_savemkludge(oia); - IFAFREE((&oia->ia_ifa)); -/* xxx - rtrequest(RTM_DELETE, - (struct sockaddr *)&ia->ia_addr, - (struct sockaddr *)0 - (struct sockaddr *)&ia->ia_prefixmask, - RTF_UP|RTF_CLONING, - (struct rtentry **)0); -*/ - return 0; + return(ia); /* this is always non-NULL */ } int @@ -1428,7 +1887,7 @@ in6_init_prefix_ltimes(struct nd_prefix *ndpr) { long time_second = time.tv_sec; - /* check if preferred lifetime > valid lifetime */ + /* check if preferred lifetime > valid lifetime. RFC2462 5.5.3 (c) */ if (ndpr->ndpr_pltime > ndpr->ndpr_vltime) { nd6log((LOG_INFO, "in6_init_prefix_ltimes: preferred lifetime" "(%d) is greater than valid lifetime(%d)\n", @@ -1448,25 +1907,17 @@ in6_init_prefix_ltimes(struct nd_prefix *ndpr) } static void -in6_init_address_ltimes(struct nd_prefix *new, - struct in6_addrlifetime *lt6, - int update_vltime) +in6_init_address_ltimes(struct nd_prefix *new, struct in6_addrlifetime *lt6) { long time_second = time.tv_sec; /* Valid lifetime must not be updated unless explicitly specified. */ - if (update_vltime) { - /* init ia6t_expire */ - if (lt6->ia6t_vltime == ND6_INFINITE_LIFETIME) - lt6->ia6t_expire = 0; - else { - lt6->ia6t_expire = time_second; - lt6->ia6t_expire += lt6->ia6t_vltime; - } - /* Ensure addr lifetime <= prefix lifetime. */ - if (new->ndpr_expire && lt6->ia6t_expire && - new->ndpr_expire < lt6->ia6t_expire) - lt6->ia6t_expire = new->ndpr_expire; + /* init ia6t_expire */ + if (lt6->ia6t_vltime == ND6_INFINITE_LIFETIME) + lt6->ia6t_expire = 0; + else { + lt6->ia6t_expire = time_second; + lt6->ia6t_expire += lt6->ia6t_vltime; } /* init ia6t_preferred */ @@ -1476,10 +1927,6 @@ in6_init_address_ltimes(struct nd_prefix *new, lt6->ia6t_preferred = time_second; lt6->ia6t_preferred += lt6->ia6t_pltime; } - /* Ensure addr lifetime <= prefix lifetime. */ - if (new->ndpr_preferred && lt6->ia6t_preferred - && new->ndpr_preferred < lt6->ia6t_preferred) - lt6->ia6t_preferred = new->ndpr_preferred; } /* @@ -1523,14 +1970,22 @@ rt6_deleteroute(rn, arg) return(0); /* + * Do not delete a static route. + * XXX: this seems to be a bit ad-hoc. Should we consider the + * 'cloned' bit instead? + */ + if ((rt->rt_flags & RTF_STATIC) != 0) + return(0); + + /* * We delete only host route. This means, in particular, we don't * delete default route. */ if ((rt->rt_flags & RTF_HOST) == 0) return(0); - return(rtrequest(RTM_DELETE, rt_key(rt), - rt->rt_gateway, rt_mask(rt), rt->rt_flags, 0)); + return(rtrequest(RTM_DELETE, rt_key(rt), rt->rt_gateway, + rt_mask(rt), rt->rt_flags, 0)); #undef SIN6 } @@ -1545,21 +2000,15 @@ nd6_setdefaultiface(ifindex) if (nd6_defifindex != ifindex) { nd6_defifindex = ifindex; - if (nd6_defifindex > 0) + if (nd6_defifindex > 0) { nd6_defifp = ifindex2ifnet[nd6_defifindex]; - else + } 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. + * Rescan default router list, refresh default route(s). */ - if (TAILQ_FIRST(&nd_defrouter) == NULL) - defrouter_select(); + defrouter_select(); } return(error); |