diff options
author | Jun-ichiro itojun Hagino <itojun@cvs.openbsd.org> | 2002-06-08 21:22:04 +0000 |
---|---|---|
committer | Jun-ichiro itojun Hagino <itojun@cvs.openbsd.org> | 2002-06-08 21:22:04 +0000 |
commit | 08134b843eeef6fd346701d831841ed9d464657e (patch) | |
tree | 4e94be7c894a1e772978b71f3e70486d80aba904 /sys/netinet6/in6.c | |
parent | 0fc8d906ee05a54d73bd80f1177a009e116cf60d (diff) |
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.
Diffstat (limited to 'sys/netinet6/in6.c')
-rw-r--r-- | sys/netinet6/in6.c | 1252 |
1 files changed, 897 insertions, 355 deletions
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... |