Index: stable/5/sys/netinet6/in6.c =================================================================== --- stable/5/sys/netinet6/in6.c (revision 170508) +++ stable/5/sys/netinet6/in6.c (revision 170509) @@ -1,2067 +1,2072 @@ /* $FreeBSD$ */ /* $KAME: in6.c,v 1.259 2002/01/21 11:37:50 keiichi 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. * 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 "opt_inet.h" #include "opt_inet6.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include MALLOC_DEFINE(M_IPMADDR, "in6_multi", "internet multicast address"); /* * Definitions of some costant IP6 addresses. */ const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT; const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT; const struct in6_addr in6addr_nodelocal_allnodes = IN6ADDR_NODELOCAL_ALLNODES_INIT; const struct in6_addr in6addr_linklocal_allnodes = IN6ADDR_LINKLOCAL_ALLNODES_INIT; const struct in6_addr in6addr_linklocal_allrouters = IN6ADDR_LINKLOCAL_ALLROUTERS_INIT; const struct in6_addr in6mask0 = IN6MASK0; const struct in6_addr in6mask32 = IN6MASK32; const struct in6_addr in6mask64 = IN6MASK64; const struct in6_addr in6mask96 = IN6MASK96; const struct in6_addr in6mask128 = IN6MASK128; const struct sockaddr_in6 sa6_any = { sizeof(sa6_any), AF_INET6, 0, 0, IN6ADDR_ANY_INIT, 0 }; static int in6_lifaddr_ioctl __P((struct socket *, u_long, caddr_t, struct ifnet *, struct thread *)); static int in6_ifinit __P((struct ifnet *, struct in6_ifaddr *, struct sockaddr_in6 *, int)); static void in6_unlink_ifa __P((struct in6_ifaddr *, struct ifnet *)); struct in6_multihead in6_multihead; /* XXX BSS initialization */ int (*faithprefix_p)(struct in6_addr *); /* * Subroutine for in6_ifaddloop() and in6_ifremloop(). * This routine does actual work. */ static void in6_ifloop_request(int cmd, struct ifaddr *ifa) { struct sockaddr_in6 all1_sa; struct rtentry *nrt = NULL; int e; bzero(&all1_sa, sizeof(all1_sa)); all1_sa.sin6_family = AF_INET6; all1_sa.sin6_len = sizeof(struct sockaddr_in6); all1_sa.sin6_addr = in6mask128; /* * 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. */ e = rtrequest(cmd, ifa->ifa_addr, ifa->ifa_addr, (struct sockaddr *)&all1_sa, RTF_UP|RTF_HOST|RTF_LLINFO, &nrt); if (e != 0) { /* XXX need more descriptive message */ 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); } if (nrt) { RT_LOCK(nrt); /* * Make sure rt_ifa be equal to IFA, the second argument of * the function. We need this because when we refer to * rt_ifa->ia6_flags in ip6_input, we assume that the rt_ifa * points to the address instead of the loopback address. */ if (cmd == RTM_ADD && ifa != nrt->rt_ifa) { IFAFREE(nrt->rt_ifa); IFAREF(ifa); nrt->rt_ifa = ifa; } /* * 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? */ rt_newaddrmsg(cmd, ifa, e, nrt); if (cmd == RTM_DELETE) { rtfree(nrt); } else { /* the cmd must be RTM_ADD here */ RT_REMREF(nrt); RT_UNLOCK(nrt); } } } /* * 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. */ void in6_ifaddloop(struct ifaddr *ifa) { struct rtentry *rt; int need_loop; /* If there is no loopback entry, allocate one. */ rt = rtalloc1(ifa->ifa_addr, 0, 0); need_loop = (rt == NULL || (rt->rt_flags & RTF_HOST) == 0 || (rt->rt_ifp->if_flags & IFF_LOOPBACK) == 0); if (rt) rtfree(rt); if (need_loop) in6_ifloop_request(RTM_ADD, ifa); } /* * Remove loopback rtentry of ownaddr generated by in6_ifaddloop(), * if it exists. */ void in6_ifremloop(struct ifaddr *ifa) { 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 * regardless of the result of in6_is_ifloop_auto(). */ /* * 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) { /* * 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, 0); if (rt != NULL) { if ((rt->rt_flags & RTF_HOST) != 0 && (rt->rt_ifp->if_flags & IFF_LOOPBACK) != 0) { rtfree(rt); in6_ifloop_request(RTM_DELETE, ifa); } else RT_UNLOCK(rt); } } } int in6_mask2len(mask, lim0) struct in6_addr *mask; u_char *lim0; { 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 (p < lim) { for (y = 0; y < 8; y++) { 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; } #define ifa2ia6(ifa) ((struct in6_ifaddr *)(ifa)) #define ia62ifa(ia6) (&((ia6)->ia_ifa)) int in6_control(so, cmd, data, ifp, td) struct socket *so; u_long cmd; caddr_t data; struct ifnet *ifp; struct thread *td; { struct in6_ifreq *ifr = (struct in6_ifreq *)data; struct in6_ifaddr *ia = NULL; struct in6_aliasreq *ifra = (struct in6_aliasreq *)data; int privileged; privileged = 0; if (td == NULL || !suser(td)) privileged++; switch (cmd) { case SIOCGETSGCNT_IN6: case SIOCGETMIFCNT_IN6: return (mrt6_ioctl(cmd, data)); } switch(cmd) { case SIOCAADDRCTL_POLICY: case SIOCDADDRCTL_POLICY: if (!privileged) return (EPERM); return (in6_src_ioctl(cmd, data)); } if (ifp == NULL) return (EOPNOTSUPP); switch (cmd) { case SIOCSNDFLUSH_IN6: case SIOCSPFXFLUSH_IN6: case SIOCSRTRFLUSH_IN6: case SIOCSDEFIFACE_IN6: case SIOCSIFINFO_FLAGS: if (!privileged) return (EPERM); /* FALLTHROUGH */ case OSIOCGIFINFO_IN6: case SIOCGIFINFO_IN6: case SIOCGDRLST_IN6: case SIOCGPRLST_IN6: case SIOCGNBRINFO_IN6: case SIOCGDEFIFACE_IN6: return (nd6_ioctl(cmd, data, ifp)); } switch (cmd) { case SIOCSIFPREFIX_IN6: case SIOCDIFPREFIX_IN6: case SIOCAIFPREFIX_IN6: case SIOCCIFPREFIX_IN6: case SIOCSGIFPREFIX_IN6: case SIOCGIFPREFIX_IN6: log(LOG_NOTICE, "prefix ioctls are now invalidated. " "please use ifconfig.\n"); return (EOPNOTSUPP); } switch (cmd) { case SIOCSSCOPE6: if (!privileged) return (EPERM); return (scope6_set(ifp, (struct scope6_id *)ifr->ifr_ifru.ifru_scope_id)); case SIOCGSCOPE6: return (scope6_get(ifp, (struct scope6_id *)ifr->ifr_ifru.ifru_scope_id)); case SIOCGSCOPE6DEF: return (scope6_get_default((struct scope6_id *) ifr->ifr_ifru.ifru_scope_id)); } switch (cmd) { case SIOCALIFADDR: case SIOCDLIFADDR: if (!privileged) return (EPERM); /* FALLTHROUGH */ case SIOCGLIFADDR: return in6_lifaddr_ioctl(so, cmd, data, ifp, td); } /* * Find address for this interface, if it exists. */ if (ifra->ifra_addr.sin6_family == AF_INET6) { /* XXX */ struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)&ifra->ifra_addr; if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr)) { if (sa6->sin6_addr.s6_addr16[1] == 0) { /* link ID is not embedded by the user */ sa6->sin6_addr.s6_addr16[1] = htons(ifp->if_index); } else if (sa6->sin6_addr.s6_addr16[1] != htons(ifp->if_index)) { return (EINVAL); /* link ID contradicts */ } if (sa6->sin6_scope_id) { if (sa6->sin6_scope_id != (u_int32_t)ifp->if_index) return (EINVAL); sa6->sin6_scope_id = 0; /* XXX: good way? */ } } ia = in6ifa_ifpwithaddr(ifp, &ifra->ifra_addr.sin6_addr); } switch (cmd) { case SIOCSIFADDR_IN6: case SIOCSIFDSTADDR_IN6: case SIOCSIFNETMASK_IN6: /* * Since IPv6 allows a node to assign multiple addresses * on a single interface, SIOCSIFxxx ioctls are not suitable * and should be unused. */ /* we decided to obsolete this command (20000704) */ return (EINVAL); case SIOCDIFADDR_IN6: /* * for IPv4, we look for existing in_ifaddr here to allow * "ifconfig if0 delete" to remove first IPv4 address on the * interface. For IPv6, as the spec allow multiple interface * address from the day one, we consider "remove the first one" * semantics to be not preferable. */ if (ia == NULL) return (EADDRNOTAVAIL); /* FALLTHROUGH */ case SIOCAIFADDR_IN6: /* * We always require users to specify a valid IPv6 address for * the corresponding operation. */ if (ifra->ifra_addr.sin6_family != AF_INET6 || ifra->ifra_addr.sin6_len != sizeof(struct sockaddr_in6)) return (EAFNOSUPPORT); if (!privileged) return (EPERM); break; case SIOCGIFADDR_IN6: /* This interface is basically deprecated. use SIOCGIFCONF. */ /* FALLTHROUGH */ case SIOCGIFAFLAG_IN6: case SIOCGIFNETMASK_IN6: case SIOCGIFDSTADDR_IN6: case SIOCGIFALIFETIME_IN6: /* must think again about its semantics */ if (ia == NULL) return (EADDRNOTAVAIL); break; case SIOCSIFALIFETIME_IN6: { struct in6_addrlifetime *lt; if (!privileged) return (EPERM); if (ia == NULL) return (EADDRNOTAVAIL); /* sanity for overflow - beware unsigned */ lt = &ifr->ifr_ifru.ifru_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; } } switch (cmd) { case SIOCGIFADDR_IN6: ifr->ifr_addr = ia->ia_addr; break; 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; case SIOCGIFNETMASK_IN6: ifr->ifr_addr = ia->ia_prefixmask; break; case SIOCGIFAFLAG_IN6: ifr->ifr_ifru.ifru_flags6 = ia->ia6_flags; break; case SIOCGIFSTAT_IN6: if (ifp == NULL) return EINVAL; bzero(&ifr->ifr_ifru.ifru_stat, sizeof(ifr->ifr_ifru.ifru_stat)); ifr->ifr_ifru.ifru_stat = *((struct in6_ifextra *)ifp->if_afdata[AF_INET6])->in6_ifstat; break; case SIOCGIFSTAT_ICMP6: if (ifp == NULL) return EINVAL; bzero(&ifr->ifr_ifru.ifru_icmp6stat, sizeof(ifr->ifr_ifru.ifru_icmp6stat)); ifr->ifr_ifru.ifru_icmp6stat = *((struct in6_ifextra *)ifp->if_afdata[AF_INET6])->icmp6_ifstat; break; case SIOCGIFALIFETIME_IN6: ifr->ifr_ifru.ifru_lifetime = ia->ia6_lifetime; break; case SIOCSIFALIFETIME_IN6: ia->ia6_lifetime = ifr->ifr_ifru.ifru_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; } else ia->ia6_lifetime.ia6t_preferred = 0; break; case SIOCAIFADDR_IN6: { int i, error = 0; struct nd_prefix pr0, *pr; /* * 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); /* * 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. */ /* * convert mask to prefix length (prefixmask has already * been validated in in6_update_ifa(). */ 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 not yet. */ 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 = in6ifa_ifpwithaddr(ifp, &ifra->ifra_addr.sin6_addr)) == NULL) { /* XXX: this should not happen! */ log(LOG_ERR, "in6_control: addition succeeded, but" " no ifaddr\n"); } else { if ((ia->ia6_flags & IN6_IFF_AUTOCONF) != 0 && ia->ia6_ndpr == NULL) { /* new autoconfed addr */ ia->ia6_ndpr = pr; pr->ndpr_refcnt++; /* * If this is the first autoconf address from * the prefix, create a temporary address * as well (when specified). */ if (ip6_use_tempaddr && pr->ndpr_refcnt == 1) { int e; if ((e = in6_tmpifadd(ia, 1)) != 0) { log(LOG_NOTICE, "in6_control: " "failed to create a " "temporary address, " "errno=%d\n", e); } } } /* * this might affect the status of autoconfigured * addresses, that is, this address might make * other addresses detached. */ pfxlist_onlink_check(); } if (error == 0 && ia) EVENTHANDLER_INVOKE(ifaddr_event, ifp); break; } case SIOCDIFADDR_IN6: { int i = 0; struct nd_prefix pr0, *pr; /* * 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]; } /* * 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))) { pr->ndpr_expire = 1; /* XXX: just for expiration */ } purgeaddr: in6_purgeaddr(&ia->ia_ifa); EVENTHANDLER_INVOKE(ifaddr_event, ifp); 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; /* 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)) { u_int32_t zoneid; if ((error = in6_recoverscope(&dst6, &ifra->ifra_dstaddr.sin6_addr, ifp)) != 0) return (error); if (in6_addr2zoneid(ifp, &dst6.sin6_addr, &zoneid)) return (EINVAL); if (dst6.sin6_scope_id == 0) /* user omit to specify the ID. */ dst6.sin6_scope_id = zoneid; else if (dst6.sin6_scope_id != zoneid) return (EINVAL); /* scope ID mismatch. */ if ((error = in6_embedscope(&dst6.sin6_addr, &dst6, NULL, NULL)) != 0) return (error); dst6.sin6_scope_id = 0; /* XXX */ } /* * 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) { if ((ifp->if_flags & (IFF_POINTOPOINT|IFF_LOOPBACK)) == 0) { /* XXX: noisy message */ nd6log((LOG_INFO, "in6_update_ifa: a destination can " "be specified for a p2p or a loopback IF only\n")); return (EINVAL); } if (plen != 128) { nd6log((LOG_INFO, "in6_update_ifa: prefixlen should " "be 128 when dstaddr is specified\n")); return (EINVAL); } } /* lifetime consistency check */ lt = &ifra->ifra_lifetime; if (lt->ia6t_vltime != ND6_INFINITE_LIFETIME && lt->ia6t_vltime + time_second < time_second) { 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. */ nd6log((LOG_INFO, "in6_update_ifa: valid lifetime is 0 for %s\n", ip6_sprintf(&ifra->ifra_addr.sin6_addr))); } if (lt->ia6t_pltime != ND6_INFINITE_LIFETIME && lt->ia6t_pltime + time_second < time_second) { return EINVAL; } /* * 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 */ IFA_LOCK_INIT(&ia->ia_ifa); 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 | 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 in6_ifaddr = ia; ia->ia_ifa.ifa_refcnt = 1; TAILQ_INSERT_TAIL(&ifp->if_addrlist, &ia->ia_ifa, ifa_list); } /* set prefix mask */ if (ifra->ifra_prefixmask.sin6_len) { /* * 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 (ia->ia_prefixmask.sin6_len && in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL) != plen) { nd6log((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) { nd6log((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; } /* reset the interface and routing table appropriately. */ if ((error = in6_ifinit(ifp, ia, &ifra->ifra_addr, hostIsNew)) != 0) goto unlink; /* + * We are done if we have simply modified an existing address. + */ + if (!hostIsNew) + return (error); + + /* * 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; struct in6_multi *in6m; - if (hostIsNew) { - /* join solicited multicast addr for new host id */ - struct in6_addr llsol; + /* join solicited multicast addr for new host id */ + 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); - if (error != 0) { - nd6log((LOG_WARNING, - "in6_update_ifa: addmulti failed for " - "%s on %s (errno=%d)\n", - ip6_sprintf(&llsol), if_name(ifp), - error)); - in6_purgeaddr((struct ifaddr *)ia); - return (error); - } + 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; + imm = in6_joingroup(ifp, &llsol, &error); + if (imm == NULL) { + nd6log((LOG_WARNING, + "in6_update_ifa: addmulti failed for " + "%s on %s (errno=%d)\n", + ip6_sprintf(&llsol), if_name(ifp), + error)); + in6_purgeaddr((struct ifaddr *)ia); + return (error); } + LIST_INSERT_HEAD(&ia->ia6_memberships, imm, i6mm_chain); 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 (error != 0) { - nd6log((LOG_WARNING, - "in6_update_ifa: addmulti failed for " - "%s on %s (errno=%d)\n", - ip6_sprintf(&mltaddr.sin6_addr), - if_name(ifp), error)); - } } + imm = in6_joingroup(ifp, &mltaddr.sin6_addr, &error); + if (imm == NULL) { + nd6log((LOG_WARNING, + "in6_update_ifa: addmulti failed for " + "%s on %s (errno=%d)\n", + ip6_sprintf(&mltaddr.sin6_addr), + if_name(ifp), error)); + in6_purgeaddr((struct ifaddr *)ia); + return (error); + } + LIST_INSERT_HEAD(&ia->ia6_memberships, imm, i6mm_chain); /* * join node information group address */ #define hostnamelen strlen(hostname) if (in6_nigroup(ifp, hostname, hostnamelen, &mltaddr.sin6_addr) == 0) { - IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m); - if (in6m == NULL && ia != NULL) { - (void)in6_addmulti(&mltaddr.sin6_addr, - ifp, &error); - if (error != 0) { - nd6log((LOG_WARNING, "in6_update_ifa: " - "addmulti failed for " - "%s on %s (errno=%d)\n", - ip6_sprintf(&mltaddr.sin6_addr), - if_name(ifp), error)); - } + imm = in6_joingroup(ifp, &mltaddr.sin6_addr, &error); + if (imm == NULL) { + nd6log((LOG_WARNING, "in6_update_ifa: " + "addmulti failed for " + "%s on %s (errno=%d)\n", + ip6_sprintf(&mltaddr.sin6_addr), + if_name(ifp), error)); + /* XXX not very fatal, go on... */ + } else { + LIST_INSERT_HEAD(&ia->ia6_memberships, + imm, i6mm_chain); } } #undef hostnamelen /* * join node-local all-nodes address, on loopback. * XXX: since "node-local" is obsoleted by interface-local, * we have to join the group on every interface with * some interface-boundary restriction. */ if (ifp->if_flags & IFF_LOOPBACK) { struct in6_ifaddr *ia_loop; struct in6_addr loop6 = in6addr_loopback; ia_loop = in6ifa_ifpwithaddr(ifp, &loop6); mltaddr.sin6_addr = in6addr_nodelocal_allnodes; IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m); if (in6m == NULL && ia_loop != NULL) { rtrequest(RTM_ADD, (struct sockaddr *)&mltaddr, (struct sockaddr *)&ia_loop->ia_addr, (struct sockaddr *)&mltmask, RTF_UP, (struct rtentry **)0); - (void)in6_addmulti(&mltaddr.sin6_addr, ifp, - &error); - if (error != 0) { - nd6log((LOG_WARNING, "in6_update_ifa: " - "addmulti failed for %s on %s " - "(errno=%d)\n", - ip6_sprintf(&mltaddr.sin6_addr), - if_name(ifp), error)); - } } + imm = in6_joingroup(ifp, &mltaddr.sin6_addr, &error); + if (imm == NULL) { + nd6log((LOG_WARNING, "in6_update_ifa: " + "addmulti failed for %s on %s " + "(errno=%d)\n", + ip6_sprintf(&mltaddr.sin6_addr), + if_name(ifp), error)); + goto cleanup; + } + LIST_INSERT_HEAD(&ia->ia6_memberships, imm, + i6mm_chain); } } ia->ia6_flags = ifra->ifra_flags; ia->ia6_flags &= ~IN6_IFF_DUPLICATED; /*safety*/ ia->ia6_flags &= ~IN6_IFF_NODAD; /* Mobile IPv6 */ 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; } else ia->ia6_lifetime.ia6t_preferred = 0; /* * Perform DAD, if needed. * XXX It may be of use, if we can administratively * disable DAD. */ if (in6if_do_dad(ifp) && (ifra->ifra_flags & IN6_IFF_NODAD) == 0) { ia->ia6_flags |= IN6_IFF_TENTATIVE; 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) struct ifaddr *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); /* * 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), if_name(ifp), e); /* proceed anyway... */ } else ia->ia_flags &= ~IFA_ROUTE; } /* Remove ownaddr's loopback rtentry, if it exists. */ in6_ifremloop(&(ia->ia_ifa)); - 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); + /* + * 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); 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 { /* search failed */ printf("Couldn't unlink in6_ifaddr from in6_ifaddr\n"); } } /* * 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) { nd6log((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); } /* * SIOC[GAD]LIFADDR. * SIOCGLIFADDR: get first address. (?) * SIOCGLIFADDR with IFLR_PREFIX: * get first address that matches the specified prefix. * SIOCALIFADDR: add the specified address. * SIOCALIFADDR with IFLR_PREFIX: * add the specified prefix, filling hostid part from * the first link-local address. prefixlen must be <= 64. * SIOCDLIFADDR: delete the specified address. * SIOCDLIFADDR with IFLR_PREFIX: * delete the first address that matches the specified prefix. * return values: * EINVAL on invalid parameters * EADDRNOTAVAIL on prefix match failed/specified address not found * other values may be returned from in6_ioctl() * * NOTE: SIOCALIFADDR(with IFLR_PREFIX set) allows prefixlen less than 64. * this is to accomodate address naming scheme other than RFC2374, * in the future. * RFC2373 defines interface id to be 64bit, but it allows non-RFC2374 * address encoding scheme. (see figure on page 8) */ static int in6_lifaddr_ioctl(so, cmd, data, ifp, td) struct socket *so; u_long cmd; caddr_t data; struct ifnet *ifp; struct thread *td; { struct if_laddrreq *iflr = (struct if_laddrreq *)data; struct ifaddr *ifa; struct sockaddr *sa; /* sanity checks */ if (!data || !ifp) { panic("invalid argument to in6_lifaddr_ioctl"); /* NOTREACHED */ } switch (cmd) { case SIOCGLIFADDR: /* address must be specified on GET with IFLR_PREFIX */ if ((iflr->flags & IFLR_PREFIX) == 0) break; /* FALLTHROUGH */ case SIOCALIFADDR: case SIOCDLIFADDR: /* address must be specified on ADD and DELETE */ sa = (struct sockaddr *)&iflr->addr; if (sa->sa_family != AF_INET6) return EINVAL; if (sa->sa_len != sizeof(struct sockaddr_in6)) return EINVAL; /* XXX need improvement */ sa = (struct sockaddr *)&iflr->dstaddr; if (sa->sa_family && sa->sa_family != AF_INET6) return EINVAL; if (sa->sa_len && sa->sa_len != sizeof(struct sockaddr_in6)) return EINVAL; break; default: /* shouldn't happen */ #if 0 panic("invalid cmd to in6_lifaddr_ioctl"); /* NOTREACHED */ #else return EOPNOTSUPP; #endif } if (sizeof(struct in6_addr) * 8 < iflr->prefixlen) return EINVAL; switch (cmd) { case SIOCALIFADDR: { struct in6_aliasreq ifra; struct in6_addr *hostid = NULL; int prefixlen; if ((iflr->flags & IFLR_PREFIX) != 0) { struct sockaddr_in6 *sin6; /* * hostid is to fill in the hostid part of the * address. hostid points to the first link-local * address attached to the interface. */ ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp, 0); if (!ifa) return EADDRNOTAVAIL; hostid = IFA_IN6(ifa); /* prefixlen must be <= 64. */ if (64 < iflr->prefixlen) return EINVAL; prefixlen = iflr->prefixlen; /* hostid part must be zero. */ sin6 = (struct sockaddr_in6 *)&iflr->addr; if (sin6->sin6_addr.s6_addr32[2] != 0 || sin6->sin6_addr.s6_addr32[3] != 0) { return EINVAL; } } else prefixlen = iflr->prefixlen; /* 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->addr, &ifra.ifra_addr, ((struct sockaddr *)&iflr->addr)->sa_len); if (hostid) { /* fill in hostid part */ ifra.ifra_addr.sin6_addr.s6_addr32[2] = hostid->s6_addr32[2]; ifra.ifra_addr.sin6_addr.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); if (hostid) { ifra.ifra_dstaddr.sin6_addr.s6_addr32[2] = hostid->s6_addr32[2]; ifra.ifra_dstaddr.sin6_addr.s6_addr32[3] = hostid->s6_addr32[3]; } } 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, td); } case SIOCGLIFADDR: case SIOCDLIFADDR: { struct in6_ifaddr *ia; struct in6_addr mask, candidate, match; struct sockaddr_in6 *sin6; int cmp; bzero(&mask, sizeof(mask)); if (iflr->flags & IFLR_PREFIX) { /* lookup a prefix rather than address. */ in6_prefixlen2mask(&mask, iflr->prefixlen); sin6 = (struct sockaddr_in6 *)&iflr->addr; bcopy(&sin6->sin6_addr, &match, sizeof(match)); match.s6_addr32[0] &= mask.s6_addr32[0]; match.s6_addr32[1] &= mask.s6_addr32[1]; match.s6_addr32[2] &= mask.s6_addr32[2]; match.s6_addr32[3] &= mask.s6_addr32[3]; /* if you set extra bits, that's wrong */ if (bcmp(&match, &sin6->sin6_addr, sizeof(match))) return EINVAL; cmp = 1; } else { if (cmd == SIOCGLIFADDR) { /* on getting an address, take the 1st match */ cmp = 0; /* XXX */ } else { /* on deleting an address, do exact match */ in6_prefixlen2mask(&mask, 128); sin6 = (struct sockaddr_in6 *)&iflr->addr; bcopy(&sin6->sin6_addr, &match, sizeof(match)); cmp = 1; } } TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; if (!cmp) break; bcopy(IFA_IN6(ifa), &candidate, sizeof(candidate)); /* * XXX: this is adhoc, but is necessary to allow * a user to specify fe80::/64 (not /10) for a * link-local address. */ if (IN6_IS_ADDR_LINKLOCAL(&candidate)) candidate.s6_addr16[1] = 0; candidate.s6_addr32[0] &= mask.s6_addr32[0]; candidate.s6_addr32[1] &= mask.s6_addr32[1]; candidate.s6_addr32[2] &= mask.s6_addr32[2]; candidate.s6_addr32[3] &= mask.s6_addr32[3]; if (IN6_ARE_ADDR_EQUAL(&candidate, &match)) break; } if (!ifa) return EADDRNOTAVAIL; ia = ifa2ia6(ifa); if (cmd == SIOCGLIFADDR) { struct sockaddr_in6 *s6; /* fill in the if_laddrreq structure */ bcopy(&ia->ia_addr, &iflr->addr, ia->ia_addr.sin6_len); s6 = (struct sockaddr_in6 *)&iflr->addr; if (IN6_IS_ADDR_LINKLOCAL(&s6->sin6_addr)) { s6->sin6_addr.s6_addr16[1] = 0; if (in6_addr2zoneid(ifp, &s6->sin6_addr, &s6->sin6_scope_id)) return (EINVAL); /* XXX */ } if ((ifp->if_flags & IFF_POINTOPOINT) != 0) { bcopy(&ia->ia_dstaddr, &iflr->dstaddr, ia->ia_dstaddr.sin6_len); s6 = (struct sockaddr_in6 *)&iflr->dstaddr; if (IN6_IS_ADDR_LINKLOCAL(&s6->sin6_addr)) { s6->sin6_addr.s6_addr16[1] = 0; if (in6_addr2zoneid(ifp, &s6->sin6_addr, &s6->sin6_scope_id)) return (EINVAL); /* EINVAL */ } } else bzero(&iflr->dstaddr, sizeof(iflr->dstaddr)); iflr->prefixlen = in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL); iflr->flags = ia->ia6_flags; /* XXX */ return 0; } else { struct in6_aliasreq ifra; /* fill in6_aliasreq and do ioctl(SIOCDIFADDR_IN6) */ bzero(&ifra, sizeof(ifra)); bcopy(iflr->iflr_name, ifra.ifra_name, sizeof(ifra.ifra_name)); bcopy(&ia->ia_addr, &ifra.ifra_addr, ia->ia_addr.sin6_len); if ((ifp->if_flags & IFF_POINTOPOINT) != 0) { bcopy(&ia->ia_dstaddr, &ifra.ifra_dstaddr, 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); ifra.ifra_flags = ia->ia6_flags; return in6_control(so, SIOCDIFADDR_IN6, (caddr_t)&ifra, ifp, td); } } } return EOPNOTSUPP; /* just for safety */ } /* * Initialize an interface's intetnet6 address * and routing table entry. */ static int in6_ifinit(ifp, ia, sin6, newhost) struct ifnet *ifp; struct in6_ifaddr *ia; struct sockaddr_in6 *sin6; int newhost; { int error = 0, plen, ifacount = 0; int s = splimp(); struct ifaddr *ifa; /* * Give the interface a chance to initialize * if this is its first address, * and to validate the address if necessary. */ TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) { 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); return (error); } splx(s); ia->ia_ifa.ifa_metric = ifp->if_metric; /* we could do in(6)_socktrim here, but just omit it at this moment. */ /* * Special case: * If a new destination address is specified for a point-to-point * interface, install a route to the destination as an interface * direct route. * XXX: the logic below rejects assigning multiple addresses on a p2p * interface that share a same destination. */ plen = in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL); /* XXX */ if (!(ia->ia_flags & IFA_ROUTE) && 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; } if (plen < 128) { /* * The RTF_CLONING flag is necessary for in6_is_ifloop_auto(). */ ia->ia_ifa.ifa_flags |= RTF_CLONING; } /* 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)); } return (error); } struct in6_multi_mship * in6_joingroup(ifp, addr, errorp) struct ifnet *ifp; struct in6_addr *addr; int *errorp; { struct in6_multi_mship *imm; imm = malloc(sizeof(*imm), M_IPMADDR, M_NOWAIT); if (!imm) { *errorp = ENOBUFS; return NULL; } imm->i6mm_maddr = in6_addmulti(addr, ifp, errorp); if (!imm->i6mm_maddr) { /* *errorp is alrady set */ free(imm, M_IPMADDR); return NULL; } return imm; } int in6_leavegroup(imm) struct in6_multi_mship *imm; { if (imm->i6mm_maddr) in6_delmulti(imm->i6mm_maddr); free(imm, M_IPMADDR); return 0; } /* * Find an IPv6 interface link-local address specific to an interface. */ struct in6_ifaddr * in6ifa_ifpforlinklocal(ifp, ignoreflags) struct ifnet *ifp; int ignoreflags; { struct ifaddr *ifa; TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) { 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))) { if ((((struct in6_ifaddr *)ifa)->ia6_flags & ignoreflags) != 0) continue; break; } } return ((struct in6_ifaddr *)ifa); } /* * find the internet address corresponding to a given interface and address. */ struct in6_ifaddr * in6ifa_ifpwithaddr(ifp, addr) struct ifnet *ifp; struct in6_addr *addr; { struct ifaddr *ifa; TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) { if (ifa->ifa_addr == NULL) continue; /* just for safety */ if (ifa->ifa_addr->sa_family != AF_INET6) continue; if (IN6_ARE_ADDR_EQUAL(addr, IFA_IN6(ifa))) break; } return ((struct in6_ifaddr *)ifa); } /* * Convert IP6 address to printable (loggable) representation. */ static char digits[] = "0123456789abcdef"; static int ip6round = 0; char * ip6_sprintf(addr) const struct in6_addr *addr; { static char ip6buf[8][48]; int i; char *cp; const u_int16_t *a = (const u_int16_t *)addr; const u_int8_t *d; int dcolon = 0; ip6round = (ip6round + 1) & 7; cp = ip6buf[ip6round]; for (i = 0; i < 8; i++) { if (dcolon == 1) { if (*a == 0) { if (i == 7) *cp++ = ':'; a++; continue; } else dcolon = 2; } if (*a == 0) { if (dcolon == 0 && *(a + 1) == 0) { if (i == 0) *cp++ = ':'; *cp++ = ':'; dcolon = 1; } else { *cp++ = '0'; *cp++ = ':'; } a++; continue; } d = (const u_char *)a; *cp++ = digits[*d >> 4]; *cp++ = digits[*d++ & 0xf]; *cp++ = digits[*d >> 4]; *cp++ = digits[*d & 0xf]; *cp++ = ':'; a++; } *--cp = 0; 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); } 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) && (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... */ int in6_matchlen(src, dst) struct in6_addr *src, *dst; { int match = 0; u_char *s = (u_char *)src, *d = (u_char *)dst; u_char *lim = s + 16, r; while (s < lim) if ((r = (*d++ ^ *s++)) != 0) { while (r < 128) { match++; r <<= 1; } break; } else match += 8; return match; } /* XXX: to be scope conscious */ int in6_are_prefix_equal(p1, p2, len) struct in6_addr *p1, *p2; int len; { int bytelen, bitlen; /* sanity check */ if (0 > len || len > 128) { log(LOG_ERR, "in6_are_prefix_equal: invalid prefix length(%d)\n", len); return (0); } bytelen = len / 8; bitlen = len % 8; if (bcmp(&p1->s6_addr, &p2->s6_addr, bytelen)) return (0); if (bitlen != 0 && p1->s6_addr[bytelen] >> (8 - bitlen) != p2->s6_addr[bytelen] >> (8 - bitlen)) return (0); return (1); } void in6_prefixlen2mask(maskp, len) struct in6_addr *maskp; int len; { u_char maskarray[8] = {0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff}; int bytelen, bitlen, i; /* sanity check */ if (0 > len || len > 128) { log(LOG_ERR, "in6_prefixlen2mask: invalid prefix length(%d)\n", len); return; } bzero(maskp, sizeof(*maskp)); bytelen = len / 8; bitlen = len % 8; for (i = 0; i < bytelen; i++) maskp->s6_addr[i] = 0xff; if (bitlen) maskp->s6_addr[bytelen] = maskarray[bitlen - 1]; } /* * return the best address out of the same scope. if no address was * found, return the first valid address from designated IF. */ struct in6_ifaddr * in6_ifawithifp(ifp, dst) struct ifnet *ifp; struct in6_addr *dst; { int dst_scope = in6_addrscope(dst), blen = -1, tlen; struct ifaddr *ifa; struct in6_ifaddr *besta = 0; struct in6_ifaddr *dep[2]; /* last-resort: deprecated */ dep[0] = dep[1] = NULL; /* * We first look for addresses in the same scope. * If there is one, return it. * If two or more, return one which matches the dst longest. * If none, return one of global addresses assigned other ifs. */ TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST) continue; /* XXX: is there any case to allow anycast? */ if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_NOTREADY) continue; /* don't use this interface */ if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DETACHED) continue; if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DEPRECATED) { if (ip6_use_deprecated) dep[0] = (struct in6_ifaddr *)ifa; continue; } if (dst_scope == in6_addrscope(IFA_IN6(ifa))) { /* * call in6_matchlen() as few as possible */ if (besta) { if (blen == -1) blen = in6_matchlen(&besta->ia_addr.sin6_addr, dst); tlen = in6_matchlen(IFA_IN6(ifa), dst); if (tlen > blen) { blen = tlen; besta = (struct in6_ifaddr *)ifa; } } else besta = (struct in6_ifaddr *)ifa; } } if (besta) return (besta); TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST) continue; /* XXX: is there any case to allow anycast? */ if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_NOTREADY) continue; /* don't use this interface */ if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DETACHED) continue; if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DEPRECATED) { if (ip6_use_deprecated) dep[1] = (struct in6_ifaddr *)ifa; continue; } return (struct in6_ifaddr *)ifa; } /* use the last-resort values, that are, deprecated addresses */ if (dep[0]) return dep[0]; if (dep[1]) return dep[1]; return NULL; } /* * perform DAD when interface becomes IFF_UP. */ void in6_if_up(ifp) struct ifnet *ifp; { struct ifaddr *ifa; struct in6_ifaddr *ia; int dad_delay; /* delay ticks before DAD output */ /* * special cases, like 6to4, are handled in in6_ifattach */ in6_ifattach(ifp, NULL); dad_delay = 0; TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; ia = (struct in6_ifaddr *)ifa; if (ia->ia6_flags & IN6_IFF_TENTATIVE) nd6_dad_start(ifa, &dad_delay); } } int in6if_do_dad(ifp) struct ifnet *ifp; { if ((ifp->if_flags & IFF_LOOPBACK) != 0) return (0); switch (ifp->if_type) { #ifdef IFT_DUMMY case IFT_DUMMY: #endif case IFT_FAITH: /* * These interfaces do not have the IFF_LOOPBACK flag, * but loop packets back. We do not have to do DAD on such * interfaces. We should even omit it, because loop-backed * NS would confuse the DAD procedure. */ return (0); default: /* * Our DAD routine requires the interface up and running. * However, some interfaces can be up before the RUNNING * status. Additionaly, users may try to assign addresses * before the interface becomes up (or running). * We simply skip DAD in such a case as a work around. * XXX: we should rather mark "tentative" on such addresses, * and do DAD after the interface becomes ready. */ if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) return (0); return (1); } } /* * Calculate max IPv6 MTU through all the interfaces and store it * to in6_maxmtu. */ void in6_setmaxmtu() { unsigned long maxmtu = 0; struct ifnet *ifp; IFNET_RLOCK(); for (ifp = TAILQ_FIRST(&ifnet); ifp; ifp = TAILQ_NEXT(ifp, if_list)) { /* this function can be called during ifnet initialization */ if (!ifp->if_afdata[AF_INET6]) continue; if ((ifp->if_flags & IFF_LOOPBACK) == 0 && IN6_LINKMTU(ifp) > maxmtu) maxmtu = IN6_LINKMTU(ifp); } IFNET_RUNLOCK(); if (maxmtu) /* update only when maxmtu is positive */ in6_maxmtu = maxmtu; } void * in6_domifattach(ifp) struct ifnet *ifp; { struct in6_ifextra *ext; ext = (struct in6_ifextra *)malloc(sizeof(*ext), M_IFADDR, M_WAITOK); bzero(ext, sizeof(*ext)); ext->in6_ifstat = (struct in6_ifstat *)malloc(sizeof(struct in6_ifstat), M_IFADDR, M_WAITOK); bzero(ext->in6_ifstat, sizeof(*ext->in6_ifstat)); ext->icmp6_ifstat = (struct icmp6_ifstat *)malloc(sizeof(struct icmp6_ifstat), M_IFADDR, M_WAITOK); bzero(ext->icmp6_ifstat, sizeof(*ext->icmp6_ifstat)); ext->nd_ifinfo = nd6_ifattach(ifp); ext->scope6_id = scope6_ifattach(ifp); return ext; } void in6_domifdetach(ifp, aux) struct ifnet *ifp; void *aux; { struct in6_ifextra *ext = (struct in6_ifextra *)aux; scope6_ifdetach(ext->scope6_id); nd6_ifdetach(ext->nd_ifinfo); free(ext->in6_ifstat, M_IFADDR); free(ext->icmp6_ifstat, M_IFADDR); free(ext, M_IFADDR); } /* * Convert sockaddr_in6 to sockaddr_in. Original sockaddr_in6 must be * v4 mapped addr or v4 compat addr */ void in6_sin6_2_sin(struct sockaddr_in *sin, struct sockaddr_in6 *sin6) { bzero(sin, sizeof(*sin)); sin->sin_len = sizeof(struct sockaddr_in); sin->sin_family = AF_INET; sin->sin_port = sin6->sin6_port; sin->sin_addr.s_addr = sin6->sin6_addr.s6_addr32[3]; } /* Convert sockaddr_in to sockaddr_in6 in v4 mapped addr format. */ void in6_sin_2_v4mapsin6(struct sockaddr_in *sin, struct sockaddr_in6 *sin6) { bzero(sin6, sizeof(*sin6)); sin6->sin6_len = sizeof(struct sockaddr_in6); sin6->sin6_family = AF_INET6; sin6->sin6_port = sin->sin_port; sin6->sin6_addr.s6_addr32[0] = 0; sin6->sin6_addr.s6_addr32[1] = 0; sin6->sin6_addr.s6_addr32[2] = IPV6_ADDR_INT32_SMP; sin6->sin6_addr.s6_addr32[3] = sin->sin_addr.s_addr; } /* Convert sockaddr_in6 into sockaddr_in. */ void in6_sin6_2_sin_in_sock(struct sockaddr *nam) { struct sockaddr_in *sin_p; struct sockaddr_in6 sin6; /* * Save original sockaddr_in6 addr and convert it * to sockaddr_in. */ sin6 = *(struct sockaddr_in6 *)nam; sin_p = (struct sockaddr_in *)nam; in6_sin6_2_sin(sin_p, &sin6); } /* Convert sockaddr_in into sockaddr_in6 in v4 mapped addr format. */ void in6_sin_2_v4mapsin6_in_sock(struct sockaddr **nam) { struct sockaddr_in *sin_p; struct sockaddr_in6 *sin6_p; MALLOC(sin6_p, struct sockaddr_in6 *, sizeof *sin6_p, M_SONAME, M_WAITOK); sin_p = (struct sockaddr_in *)*nam; in6_sin_2_v4mapsin6(sin_p, sin6_p); FREE(*nam, M_SONAME); *nam = (struct sockaddr *)sin6_p; } Index: stable/5/sys/netinet6/in6_ifattach.c =================================================================== --- stable/5/sys/netinet6/in6_ifattach.c (revision 170508) +++ stable/5/sys/netinet6/in6_ifattach.c (revision 170509) @@ -1,908 +1,916 @@ /* $FreeBSD$ */ /* $KAME: in6_ifattach.c,v 1.118 2001/05/24 07:44:00 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include unsigned long in6_maxmtu = 0; #ifdef IP6_AUTO_LINKLOCAL int ip6_auto_linklocal = IP6_AUTO_LINKLOCAL; #else int ip6_auto_linklocal = 1; /* enable by default */ #endif struct callout in6_tmpaddrtimer_ch; extern struct inpcbinfo udbinfo; extern struct inpcbinfo ripcbinfo; static int get_rand_ifid __P((struct ifnet *, struct in6_addr *)); static int generate_tmp_ifid __P((u_int8_t *, const u_int8_t *, u_int8_t *)); static int get_hw_ifid __P((struct ifnet *, struct in6_addr *)); static int get_ifid __P((struct ifnet *, struct ifnet *, struct in6_addr *)); static int in6_ifattach_linklocal __P((struct ifnet *, struct ifnet *)); static int in6_ifattach_loopback __P((struct ifnet *)); #define EUI64_GBIT 0x01 #define EUI64_UBIT 0x02 #define EUI64_TO_IFID(in6) do {(in6)->s6_addr[8] ^= EUI64_UBIT; } while (0) #define EUI64_GROUP(in6) ((in6)->s6_addr[8] & EUI64_GBIT) #define EUI64_INDIVIDUAL(in6) (!EUI64_GROUP(in6)) #define EUI64_LOCAL(in6) ((in6)->s6_addr[8] & EUI64_UBIT) #define EUI64_UNIVERSAL(in6) (!EUI64_LOCAL(in6)) #define IFID_LOCAL(in6) (!EUI64_LOCAL(in6)) #define IFID_UNIVERSAL(in6) (!EUI64_UNIVERSAL(in6)) /* * Generate a last-resort interface identifier, when the machine has no * IEEE802/EUI64 address sources. * The goal here is to get an interface identifier that is * (1) random enough and (2) does not change across reboot. * We currently use MD5(hostname) for it. */ static int get_rand_ifid(ifp, in6) struct ifnet *ifp; struct in6_addr *in6; /* upper 64bits are preserved */ { MD5_CTX ctxt; u_int8_t digest[16]; int hostnamelen = strlen(hostname); #if 0 /* we need at least several letters as seed for ifid */ if (hostnamelen < 3) return -1; #endif /* generate 8 bytes of pseudo-random value. */ bzero(&ctxt, sizeof(ctxt)); MD5Init(&ctxt); MD5Update(&ctxt, hostname, hostnamelen); MD5Final(digest, &ctxt); /* assumes sizeof(digest) > sizeof(ifid) */ bcopy(digest, &in6->s6_addr[8], 8); /* make sure to set "u" bit to local, and "g" bit to individual. */ in6->s6_addr[8] &= ~EUI64_GBIT; /* g bit to "individual" */ in6->s6_addr[8] |= EUI64_UBIT; /* u bit to "local" */ /* convert EUI64 into IPv6 interface identifier */ EUI64_TO_IFID(in6); return 0; } static int generate_tmp_ifid(seed0, seed1, ret) u_int8_t *seed0, *ret; const u_int8_t *seed1; { MD5_CTX ctxt; u_int8_t seed[16], digest[16], nullbuf[8]; u_int32_t val32; /* If there's no hisotry, start with a random seed. */ bzero(nullbuf, sizeof(nullbuf)); if (bcmp(nullbuf, seed0, sizeof(nullbuf)) == 0) { int i; for (i = 0; i < 2; i++) { val32 = arc4random(); bcopy(&val32, seed + sizeof(val32) * i, sizeof(val32)); } } else bcopy(seed0, seed, 8); /* copy the right-most 64-bits of the given address */ /* XXX assumption on the size of IFID */ bcopy(seed1, &seed[8], 8); if (0) { /* for debugging purposes only */ int i; printf("generate_tmp_ifid: new randomized ID from: "); for (i = 0; i < 16; i++) printf("%02x", seed[i]); printf(" "); } /* generate 16 bytes of pseudo-random value. */ bzero(&ctxt, sizeof(ctxt)); MD5Init(&ctxt); MD5Update(&ctxt, seed, sizeof(seed)); MD5Final(digest, &ctxt); /* * RFC 3041 3.2.1. (3) * Take the left-most 64-bits of the MD5 digest and set bit 6 (the * left-most bit is numbered 0) to zero. */ bcopy(digest, ret, 8); ret[0] &= ~EUI64_UBIT; /* * XXX: we'd like to ensure that the generated value is not zero * for simplicity. If the caclculated digest happens to be zero, * use a random non-zero value as the last resort. */ if (bcmp(nullbuf, ret, sizeof(nullbuf)) == 0) { log(LOG_INFO, "generate_tmp_ifid: computed MD5 value is zero.\n"); val32 = arc4random(); val32 = 1 + (val32 % (0xffffffff - 1)); } /* * RFC 3041 3.2.1. (4) * Take the rightmost 64-bits of the MD5 digest and save them in * stable storage as the history value to be used in the next * iteration of the algorithm. */ bcopy(&digest[8], seed0, 8); if (0) { /* for debugging purposes only */ int i; printf("to: "); for (i = 0; i < 16; i++) printf("%02x", digest[i]); printf("\n"); } return 0; } /* * Get interface identifier for the specified interface. * XXX assumes single sockaddr_dl (AF_LINK address) per an interface */ static int get_hw_ifid(ifp, in6) struct ifnet *ifp; struct in6_addr *in6; /* upper 64bits are preserved */ { struct ifaddr *ifa; struct sockaddr_dl *sdl; u_int8_t *addr; size_t addrlen; static u_int8_t allzero[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; static u_int8_t allone[8] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next) { if (ifa->ifa_addr->sa_family != AF_LINK) continue; sdl = (struct sockaddr_dl *)ifa->ifa_addr; if (sdl == NULL) continue; if (sdl->sdl_alen == 0) continue; goto found; } return -1; found: addr = LLADDR(sdl); addrlen = sdl->sdl_alen; /* get EUI64 */ switch (ifp->if_type) { case IFT_ETHER: case IFT_FDDI: case IFT_ISO88025: case IFT_ATM: case IFT_IEEE1394: #ifdef IFT_IEEE80211 case IFT_IEEE80211: #endif /* IEEE802/EUI64 cases - what others? */ /* IEEE1394 uses 16byte length address starting with EUI64 */ if (addrlen > 8) addrlen = 8; /* look at IEEE802/EUI64 only */ if (addrlen != 8 && addrlen != 6) return -1; /* * check for invalid MAC address - on bsdi, we see it a lot * since wildboar configures all-zero MAC on pccard before * card insertion. */ if (bcmp(addr, allzero, addrlen) == 0) return -1; if (bcmp(addr, allone, addrlen) == 0) return -1; /* make EUI64 address */ if (addrlen == 8) bcopy(addr, &in6->s6_addr[8], 8); else if (addrlen == 6) { in6->s6_addr[8] = addr[0]; in6->s6_addr[9] = addr[1]; in6->s6_addr[10] = addr[2]; in6->s6_addr[11] = 0xff; in6->s6_addr[12] = 0xfe; in6->s6_addr[13] = addr[3]; in6->s6_addr[14] = addr[4]; in6->s6_addr[15] = addr[5]; } break; case IFT_ARCNET: if (addrlen != 1) return -1; if (!addr[0]) return -1; bzero(&in6->s6_addr[8], 8); in6->s6_addr[15] = addr[0]; /* * due to insufficient bitwidth, we mark it local. */ in6->s6_addr[8] &= ~EUI64_GBIT; /* g bit to "individual" */ in6->s6_addr[8] |= EUI64_UBIT; /* u bit to "local" */ break; case IFT_GIF: #ifdef IFT_STF case IFT_STF: #endif /* * RFC2893 says: "SHOULD use IPv4 address as ifid source". * however, IPv4 address is not very suitable as unique * identifier source (can be renumbered). * we don't do this. */ return -1; default: return -1; } /* sanity check: g bit must not indicate "group" */ if (EUI64_GROUP(in6)) return -1; /* convert EUI64 into IPv6 interface identifier */ EUI64_TO_IFID(in6); /* * sanity check: ifid must not be all zero, avoid conflict with * subnet router anycast */ if ((in6->s6_addr[8] & ~(EUI64_GBIT | EUI64_UBIT)) == 0x00 && bcmp(&in6->s6_addr[9], allzero, 7) == 0) { return -1; } return 0; } /* * Get interface identifier for the specified interface. If it is not * available on ifp0, borrow interface identifier from other information * sources. */ static int get_ifid(ifp0, altifp, in6) struct ifnet *ifp0; struct ifnet *altifp; /* secondary EUI64 source */ struct in6_addr *in6; { struct ifnet *ifp; /* first, try to get it from the interface itself */ if (get_hw_ifid(ifp0, in6) == 0) { nd6log((LOG_DEBUG, "%s: got interface identifier from itself\n", if_name(ifp0))); goto success; } /* try secondary EUI64 source. this basically is for ATM PVC */ if (altifp && get_hw_ifid(altifp, in6) == 0) { nd6log((LOG_DEBUG, "%s: got interface identifier from %s\n", if_name(ifp0), if_name(altifp))); goto success; } /* next, try to get it from some other hardware interface */ IFNET_RLOCK(); for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_list.tqe_next) { if (ifp == ifp0) continue; if (get_hw_ifid(ifp, in6) != 0) continue; /* * to borrow ifid from other interface, ifid needs to be * globally unique */ if (IFID_UNIVERSAL(in6)) { nd6log((LOG_DEBUG, "%s: borrow interface identifier from %s\n", if_name(ifp0), if_name(ifp))); IFNET_RUNLOCK(); goto success; } } IFNET_RUNLOCK(); /* last resort: get from random number source */ if (get_rand_ifid(ifp, in6) == 0) { nd6log((LOG_DEBUG, "%s: interface identifier generated by random number\n", if_name(ifp0))); goto success; } printf("%s: failed to get interface identifier\n", if_name(ifp0)); return -1; success: nd6log((LOG_INFO, "%s: ifid: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", if_name(ifp0), in6->s6_addr[8], in6->s6_addr[9], in6->s6_addr[10], in6->s6_addr[11], in6->s6_addr[12], in6->s6_addr[13], in6->s6_addr[14], in6->s6_addr[15])); return 0; } static int in6_ifattach_linklocal(ifp, altifp) struct ifnet *ifp; struct ifnet *altifp; /* secondary EUI64 source */ { struct in6_ifaddr *ia; struct in6_aliasreq ifra; struct nd_prefix pr0; int i, error; /* * configure link-local address. */ bzero(&ifra, sizeof(ifra)); /* * in6_update_ifa() does not use ifra_name, but we accurately set it * for safety. */ strncpy(ifra.ifra_name, if_name(ifp), 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); /* XXX */ 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", if_name(ifp))); return (-1); } } ifra.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6); ifra.ifra_prefixmask.sin6_family = AF_INET6; ifra.ifra_prefixmask.sin6_addr = in6mask64; /* link-local addresses should NEVER expire. */ ifra.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME; ifra.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME; /* * 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) { /* * 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) */ if (error != EAFNOSUPPORT) log(LOG_NOTICE, "in6_ifattach_linklocal: failed to " "configure a link-local address on %s " "(errno=%d)\n", if_name(ifp), error); return (-1); } /* * 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 = 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; } /* * Make the link-local prefix (fe80::%link/64) 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; } static int in6_ifattach_loopback(ifp) struct ifnet *ifp; /* must be IFT_LOOP */ { struct in6_aliasreq ifra; int error; bzero(&ifra, sizeof(ifra)); /* * in6_update_ifa() does not use ifra_name, but we accurately set it * for safety. */ strncpy(ifra.ifra_name, if_name(ifp), 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. Follows IPv4 practice - see in_ifinit(). */ ifra.ifra_dstaddr.sin6_len = sizeof(struct sockaddr_in6); ifra.ifra_dstaddr.sin6_family = AF_INET6; ifra.ifra_dstaddr.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; /* 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; /* skip registration to the prefix list. XXX should be temporary. */ ifra.ifra_flags |= IN6_IFF_NOPFX; /* * 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", if_name(ifp), error); return (-1); } return 0; } /* * 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, in6) struct ifnet *ifp; const char *name; int namelen; struct in6_addr *in6; { 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(in6, sizeof(*in6)); in6->s6_addr16[0] = htons(0xff02); if (ifp) in6->s6_addr16[1] = htons(ifp->if_index); in6->s6_addr8[11] = 2; bcopy(digest, &in6->s6_addr32[3], sizeof(in6->s6_addr32[3])); return 0; } /* * XXX multiple loopback interface needs more care. for instance, * nodelocal address needs to be configured onto only one of them. * XXX multiple link-local address case */ void in6_ifattach(ifp, altifp) struct ifnet *ifp; struct ifnet *altifp; /* secondary EUI64 source */ { struct in6_ifaddr *ia; struct in6_addr in6; /* some of the interfaces are inherently not IPv6 capable */ switch (ifp->if_type) { case IFT_PFLOG: case IFT_PFSYNC: case IFT_CARP: return; } /* * quirks based on interface type */ switch (ifp->if_type) { #ifdef IFT_STF case IFT_STF: /* * 6to4 interface is a very special kind of beast. * no multicast, no linklocal. RFC2529 specifies how to make * linklocals for 6to4 interface, but there's no use and * it is rather harmful to have one. */ goto statinit; #endif default: break; } /* * usually, we require multicast capability to the interface */ if ((ifp->if_flags & IFF_MULTICAST) == 0) { log(LOG_INFO, "in6_ifattach: " "%s is not multicast capable, IPv6 not enabled\n", if_name(ifp)); return; } /* * assign loopback address for loopback interface. * XXX multiple loopback interface case. */ if ((ifp->if_flags & IFF_LOOPBACK) != 0) { in6 = in6addr_loopback; if (in6ifa_ifpwithaddr(ifp, &in6) == NULL) { if (in6_ifattach_loopback(ifp) != 0) return; } } /* * assign a link-local address, if there's none. */ if (ip6_auto_linklocal && ifp->if_type != IFT_BRIDGE) { 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? */ } } } #ifdef IFT_STF /* XXX */ statinit: #endif /* update dynamically. */ if (in6_maxmtu < ifp->if_mtu) in6_maxmtu = ifp->if_mtu; } /* * NOTE: in6_ifdetach() does not support loopback if at this moment. * We don't need this function in bsdi, because interfaces are never removed * from the ifnet list in bsdi. */ void in6_ifdetach(ifp) struct ifnet *ifp; { struct in6_ifaddr *ia, *oia; struct ifaddr *ifa, *next; struct rtentry *rt; short rtflags; struct sockaddr_in6 sin6; - struct in6_multi *in6m; - struct in6_multi *in6m_next; + struct in6_multi *in6m, *in6m_next; + struct in6_multi_mship *imm; /* remove neighbor management table */ nd6_purge(ifp); /* nuke any of IPv6 addresses we have */ for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = next) { next = ifa->ifa_list.tqe_next; if (ifa->ifa_addr->sa_family != AF_INET6) continue; in6_purgeaddr(ifa); } /* undo everything done by in6_ifattach(), just in case */ for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = next) { next = ifa->ifa_list.tqe_next; if (ifa->ifa_addr->sa_family != AF_INET6 || !IN6_IS_ADDR_LINKLOCAL(&satosin6(&ifa->ifa_addr)->sin6_addr)) { continue; } ia = (struct in6_ifaddr *)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); + } /* remove from the routing table */ if ((ia->ia_flags & IFA_ROUTE) && (rt = rtalloc1((struct sockaddr *)&ia->ia_addr, 0, 0UL))) { 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); } /* remove from the linked list */ TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list); IFAFREE(&ia->ia_ifa); /* also remove from the IPv6 address chain(itojun&jinmei) */ 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 { nd6log((LOG_ERR, "%s: didn't unlink in6ifaddr from list\n", if_name(ifp))); } } IFAFREE(&oia->ia_ifa); } /* leave from all multicast groups joined */ if (udbinfo.listhead != NULL) in6_pcbpurgeif0(LIST_FIRST(udbinfo.listhead), ifp); if (ripcbinfo.listhead != NULL) in6_pcbpurgeif0(LIST_FIRST(ripcbinfo.listhead), ifp); for (in6m = LIST_FIRST(&in6_multihead); in6m; in6m = in6m_next) { in6m_next = LIST_NEXT(in6m, in6m_entry); if (in6m->in6m_ifp != ifp) continue; in6_delmulti(in6m); in6m = NULL; } /* * remove neighbor management table. we call it twice just to make * sure we nuke everything. maybe we need just one call. * XXX: since the first call did not release addresses, some prefixes * might remain. We should call nd6_purge() again to release the * prefixes after removing all addresses above. * (Or can we just delay calling nd6_purge until at this point?) */ nd6_purge(ifp); /* remove route to link-local allnodes multicast (ff02::1) */ bzero(&sin6, sizeof(sin6)); sin6.sin6_len = sizeof(struct sockaddr_in6); sin6.sin6_family = AF_INET6; sin6.sin6_addr = in6addr_linklocal_allnodes; sin6.sin6_addr.s6_addr16[1] = htons(ifp->if_index); /* XXX grab lock first to avoid LOR */ if (rt_tables[AF_INET6] != NULL) { RADIX_NODE_HEAD_LOCK(rt_tables[AF_INET6]); rt = rtalloc1((struct sockaddr *)&sin6, 0, 0UL); if (rt) { if (rt->rt_ifp == ifp) rtexpunge(rt); RTFREE_LOCKED(rt); } RADIX_NODE_HEAD_UNLOCK(rt_tables[AF_INET6]); } } void in6_get_tmpifid(ifp, retbuf, baseid, generate) struct ifnet *ifp; u_int8_t *retbuf; const u_int8_t *baseid; int generate; { u_int8_t nullbuf[8]; struct nd_ifinfo *ndi = ND_IFINFO(ifp); bzero(nullbuf, sizeof(nullbuf)); if (bcmp(ndi->randomid, nullbuf, sizeof(nullbuf)) == 0) { /* we've never created a random ID. Create a new one. */ generate = 1; } if (generate) { bcopy(baseid, ndi->randomseed1, sizeof(ndi->randomseed1)); /* generate_tmp_ifid will update seedn and buf */ (void)generate_tmp_ifid(ndi->randomseed0, ndi->randomseed1, ndi->randomid); } bcopy(ndi->randomid, retbuf, 8); } void in6_tmpaddrtimer(ignored_arg) void *ignored_arg; { struct nd_ifinfo *ndi; u_int8_t nullbuf[8]; struct ifnet *ifp; int s = splnet(); callout_reset(&in6_tmpaddrtimer_ch, (ip6_temp_preferred_lifetime - ip6_desync_factor - ip6_temp_regen_advance) * hz, in6_tmpaddrtimer, NULL); bzero(nullbuf, sizeof(nullbuf)); for (ifp = TAILQ_FIRST(&ifnet); ifp; ifp = TAILQ_NEXT(ifp, if_list)) { ndi = ND_IFINFO(ifp); if (bcmp(ndi->randomid, nullbuf, sizeof(nullbuf)) != 0) { /* * We've been generating a random ID on this interface. * Create a new one. */ (void)generate_tmp_ifid(ndi->randomseed0, ndi->randomseed1, ndi->randomid); } } splx(s); } Index: stable/5/sys/netinet6/in6_var.h =================================================================== --- stable/5/sys/netinet6/in6_var.h (revision 170508) +++ stable/5/sys/netinet6/in6_var.h (revision 170509) @@ -1,624 +1,627 @@ /* $FreeBSD$ */ /* $KAME: in6_var.h,v 1.56 2001/03/29 05:34:31 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) 1985, 1986, 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. * 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_var.h 8.1 (Berkeley) 6/10/93 */ #ifndef _NETINET6_IN6_VAR_H_ #define _NETINET6_IN6_VAR_H_ /* * Interface address, Internet version. One of these structures * is allocated for each interface with an Internet address. * The ifaddr structure contains the protocol-independent part * of the structure and is assumed to be first. */ /* * pltime/vltime are just for future reference (required to implements 2 * hour rule for hosts). they should never be modified by nd6_timeout or * anywhere else. * userland -> kernel: accept pltime/vltime * kernel -> userland: throw up everything * in kernel: modify preferred/expire only */ struct in6_addrlifetime { time_t ia6t_expire; /* valid lifetime expiration time */ time_t ia6t_preferred; /* preferred lifetime expiration time */ u_int32_t ia6t_vltime; /* valid lifetime */ u_int32_t ia6t_pltime; /* prefix lifetime */ }; struct nd_ifinfo; struct scope6_id; struct in6_ifextra { struct in6_ifstat *in6_ifstat; struct icmp6_ifstat *icmp6_ifstat; struct nd_ifinfo *nd_ifinfo; struct scope6_id *scope6_id; }; struct in6_ifaddr { struct ifaddr ia_ifa; /* protocol-independent info */ #define ia_ifp ia_ifa.ifa_ifp #define ia_flags ia_ifa.ifa_flags struct sockaddr_in6 ia_addr; /* interface address */ struct sockaddr_in6 ia_net; /* network number of interface */ struct sockaddr_in6 ia_dstaddr; /* space for destination addr */ struct sockaddr_in6 ia_prefixmask; /* prefix mask */ u_int32_t ia_plen; /* prefix length */ struct in6_ifaddr *ia_next; /* next in6 list of IP6 addresses */ int ia6_flags; struct in6_addrlifetime ia6_lifetime; struct ifprefix *ia6_ifpr; /* back pointer to ifprefix */ /* 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; }; /* control structure to manage address selection policy */ struct in6_addrpolicy { struct sockaddr_in6 addr; /* prefix address */ struct sockaddr_in6 addrmask; /* prefix mask */ int preced; /* precedence */ int label; /* matching label */ u_quad_t use; /* statistics */ }; /* * IPv6 interface statistics, as defined in RFC2465 Ipv6IfStatsEntry (p12). */ struct in6_ifstat { u_quad_t ifs6_in_receive; /* # of total input datagram */ u_quad_t ifs6_in_hdrerr; /* # of datagrams with invalid hdr */ u_quad_t ifs6_in_toobig; /* # of datagrams exceeded MTU */ u_quad_t ifs6_in_noroute; /* # of datagrams with no route */ u_quad_t ifs6_in_addrerr; /* # of datagrams with invalid dst */ u_quad_t ifs6_in_protounknown; /* # of datagrams with unknown proto */ /* NOTE: increment on final dst if */ u_quad_t ifs6_in_truncated; /* # of truncated datagrams */ u_quad_t ifs6_in_discard; /* # of discarded datagrams */ /* NOTE: fragment timeout is not here */ u_quad_t ifs6_in_deliver; /* # of datagrams delivered to ULP */ /* NOTE: increment on final dst if */ u_quad_t ifs6_out_forward; /* # of datagrams forwarded */ /* NOTE: increment on outgoing if */ u_quad_t ifs6_out_request; /* # of outgoing datagrams from ULP */ /* NOTE: does not include forwrads */ u_quad_t ifs6_out_discard; /* # of discarded datagrams */ u_quad_t ifs6_out_fragok; /* # of datagrams fragmented */ u_quad_t ifs6_out_fragfail; /* # of datagrams failed on fragment */ u_quad_t ifs6_out_fragcreat; /* # of fragment datagrams */ /* NOTE: this is # after fragment */ u_quad_t ifs6_reass_reqd; /* # of incoming fragmented packets */ /* NOTE: increment on final dst if */ u_quad_t ifs6_reass_ok; /* # of reassembled packets */ /* NOTE: this is # after reass */ /* NOTE: increment on final dst if */ u_quad_t ifs6_reass_fail; /* # of reass failures */ /* NOTE: may not be packet count */ /* NOTE: increment on final dst if */ u_quad_t ifs6_in_mcast; /* # of inbound multicast datagrams */ u_quad_t ifs6_out_mcast; /* # of outbound multicast datagrams */ }; /* * ICMPv6 interface statistics, as defined in RFC2466 Ipv6IfIcmpEntry. * XXX: I'm not sure if this file is the right place for this structure... */ struct icmp6_ifstat { /* * Input statistics */ /* ipv6IfIcmpInMsgs, total # of input messages */ u_quad_t ifs6_in_msg; /* ipv6IfIcmpInErrors, # of input error messages */ u_quad_t ifs6_in_error; /* ipv6IfIcmpInDestUnreachs, # of input dest unreach errors */ u_quad_t ifs6_in_dstunreach; /* ipv6IfIcmpInAdminProhibs, # of input administratively prohibited errs */ u_quad_t ifs6_in_adminprohib; /* ipv6IfIcmpInTimeExcds, # of input time exceeded errors */ u_quad_t ifs6_in_timeexceed; /* ipv6IfIcmpInParmProblems, # of input parameter problem errors */ u_quad_t ifs6_in_paramprob; /* ipv6IfIcmpInPktTooBigs, # of input packet too big errors */ u_quad_t ifs6_in_pkttoobig; /* ipv6IfIcmpInEchos, # of input echo requests */ u_quad_t ifs6_in_echo; /* ipv6IfIcmpInEchoReplies, # of input echo replies */ u_quad_t ifs6_in_echoreply; /* ipv6IfIcmpInRouterSolicits, # of input router solicitations */ u_quad_t ifs6_in_routersolicit; /* ipv6IfIcmpInRouterAdvertisements, # of input router advertisements */ u_quad_t ifs6_in_routeradvert; /* ipv6IfIcmpInNeighborSolicits, # of input neighbor solicitations */ u_quad_t ifs6_in_neighborsolicit; /* ipv6IfIcmpInNeighborAdvertisements, # of input neighbor advertisements */ u_quad_t ifs6_in_neighboradvert; /* ipv6IfIcmpInRedirects, # of input redirects */ u_quad_t ifs6_in_redirect; /* ipv6IfIcmpInGroupMembQueries, # of input MLD queries */ u_quad_t ifs6_in_mldquery; /* ipv6IfIcmpInGroupMembResponses, # of input MLD reports */ u_quad_t ifs6_in_mldreport; /* ipv6IfIcmpInGroupMembReductions, # of input MLD done */ u_quad_t ifs6_in_mlddone; /* * Output statistics. We should solve unresolved routing problem... */ /* ipv6IfIcmpOutMsgs, total # of output messages */ u_quad_t ifs6_out_msg; /* ipv6IfIcmpOutErrors, # of output error messages */ u_quad_t ifs6_out_error; /* ipv6IfIcmpOutDestUnreachs, # of output dest unreach errors */ u_quad_t ifs6_out_dstunreach; /* ipv6IfIcmpOutAdminProhibs, # of output administratively prohibited errs */ u_quad_t ifs6_out_adminprohib; /* ipv6IfIcmpOutTimeExcds, # of output time exceeded errors */ u_quad_t ifs6_out_timeexceed; /* ipv6IfIcmpOutParmProblems, # of output parameter problem errors */ u_quad_t ifs6_out_paramprob; /* ipv6IfIcmpOutPktTooBigs, # of output packet too big errors */ u_quad_t ifs6_out_pkttoobig; /* ipv6IfIcmpOutEchos, # of output echo requests */ u_quad_t ifs6_out_echo; /* ipv6IfIcmpOutEchoReplies, # of output echo replies */ u_quad_t ifs6_out_echoreply; /* ipv6IfIcmpOutRouterSolicits, # of output router solicitations */ u_quad_t ifs6_out_routersolicit; /* ipv6IfIcmpOutRouterAdvertisements, # of output router advertisements */ u_quad_t ifs6_out_routeradvert; /* ipv6IfIcmpOutNeighborSolicits, # of output neighbor solicitations */ u_quad_t ifs6_out_neighborsolicit; /* ipv6IfIcmpOutNeighborAdvertisements, # of output neighbor advertisements */ u_quad_t ifs6_out_neighboradvert; /* ipv6IfIcmpOutRedirects, # of output redirects */ u_quad_t ifs6_out_redirect; /* ipv6IfIcmpOutGroupMembQueries, # of output MLD queries */ u_quad_t ifs6_out_mldquery; /* ipv6IfIcmpOutGroupMembResponses, # of output MLD reports */ u_quad_t ifs6_out_mldreport; /* ipv6IfIcmpOutGroupMembReductions, # of output MLD done */ u_quad_t ifs6_out_mlddone; }; struct in6_ifreq { char ifr_name[IFNAMSIZ]; union { struct sockaddr_in6 ifru_addr; struct sockaddr_in6 ifru_dstaddr; int ifru_flags; int ifru_flags6; int ifru_metric; caddr_t ifru_data; struct in6_addrlifetime ifru_lifetime; struct in6_ifstat ifru_stat; struct icmp6_ifstat ifru_icmp6stat; u_int32_t ifru_scope_id[16]; } ifr_ifru; }; struct in6_aliasreq { char ifra_name[IFNAMSIZ]; struct sockaddr_in6 ifra_addr; struct sockaddr_in6 ifra_dstaddr; struct sockaddr_in6 ifra_prefixmask; int ifra_flags; struct in6_addrlifetime ifra_lifetime; }; /* prefix type macro */ #define IN6_PREFIX_ND 1 #define IN6_PREFIX_RR 2 /* * prefix related flags passed between kernel(NDP related part) and * user land command(ifconfig) and daemon(rtadvd). */ struct in6_prflags { struct prf_ra { u_char onlink : 1; u_char autonomous : 1; u_char reserved : 6; } prf_ra; u_char prf_reserved1; u_short prf_reserved2; /* want to put this on 4byte offset */ struct prf_rr { u_char decrvalid : 1; u_char decrprefd : 1; u_char reserved : 6; } prf_rr; u_char prf_reserved3; u_short prf_reserved4; }; struct in6_prefixreq { char ipr_name[IFNAMSIZ]; u_char ipr_origin; u_char ipr_plen; u_int32_t ipr_vltime; u_int32_t ipr_pltime; struct in6_prflags ipr_flags; struct sockaddr_in6 ipr_prefix; }; #define PR_ORIG_RA 0 #define PR_ORIG_RR 1 #define PR_ORIG_STATIC 2 #define PR_ORIG_KERNEL 3 #define ipr_raf_onlink ipr_flags.prf_ra.onlink #define ipr_raf_auto ipr_flags.prf_ra.autonomous #define ipr_statef_onlink ipr_flags.prf_state.onlink #define ipr_rrf_decrvalid ipr_flags.prf_rr.decrvalid #define ipr_rrf_decrprefd ipr_flags.prf_rr.decrprefd struct in6_rrenumreq { char irr_name[IFNAMSIZ]; u_char irr_origin; u_char irr_m_len; /* match len for matchprefix */ u_char irr_m_minlen; /* minlen for matching prefix */ u_char irr_m_maxlen; /* maxlen for matching prefix */ u_char irr_u_uselen; /* uselen for adding prefix */ u_char irr_u_keeplen; /* keeplen from matching prefix */ struct irr_raflagmask { u_char onlink : 1; u_char autonomous : 1; u_char reserved : 6; } irr_raflagmask; u_int32_t irr_vltime; u_int32_t irr_pltime; struct in6_prflags irr_flags; struct sockaddr_in6 irr_matchprefix; struct sockaddr_in6 irr_useprefix; }; #define irr_raf_mask_onlink irr_raflagmask.onlink #define irr_raf_mask_auto irr_raflagmask.autonomous #define irr_raf_mask_reserved irr_raflagmask.reserved #define irr_raf_onlink irr_flags.prf_ra.onlink #define irr_raf_auto irr_flags.prf_ra.autonomous #define irr_statef_onlink irr_flags.prf_state.onlink #define irr_rrf irr_flags.prf_rr #define irr_rrf_decrvalid irr_flags.prf_rr.decrvalid #define irr_rrf_decrprefd irr_flags.prf_rr.decrprefd /* * Given a pointer to an in6_ifaddr (ifaddr), * return a pointer to the addr as a sockaddr_in6 */ #define IA6_IN6(ia) (&((ia)->ia_addr.sin6_addr)) #define IA6_DSTIN6(ia) (&((ia)->ia_dstaddr.sin6_addr)) #define IA6_MASKIN6(ia) (&((ia)->ia_prefixmask.sin6_addr)) #define IA6_SIN6(ia) (&((ia)->ia_addr)) #define IA6_DSTSIN6(ia) (&((ia)->ia_dstaddr)) #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 && \ (((d)->s6_addr32[1] ^ (a)->s6_addr32[1]) & (m)->s6_addr32[1]) == 0 && \ (((d)->s6_addr32[2] ^ (a)->s6_addr32[2]) & (m)->s6_addr32[2]) == 0 && \ (((d)->s6_addr32[3] ^ (a)->s6_addr32[3]) & (m)->s6_addr32[3]) == 0 ) #endif #define SIOCSIFADDR_IN6 _IOW('i', 12, struct in6_ifreq) #define SIOCGIFADDR_IN6 _IOWR('i', 33, struct in6_ifreq) #ifdef _KERNEL /* * SIOCSxxx ioctls should be unused (see comments in in6.c), but * we do not shift numbers for binary compatibility. */ #define SIOCSIFDSTADDR_IN6 _IOW('i', 14, struct in6_ifreq) #define SIOCSIFNETMASK_IN6 _IOW('i', 22, struct in6_ifreq) #endif #define SIOCGIFDSTADDR_IN6 _IOWR('i', 34, struct in6_ifreq) #define SIOCGIFNETMASK_IN6 _IOWR('i', 37, struct in6_ifreq) #define SIOCDIFADDR_IN6 _IOW('i', 25, struct in6_ifreq) #define SIOCAIFADDR_IN6 _IOW('i', 26, struct in6_aliasreq) #define SIOCSIFPHYADDR_IN6 _IOW('i', 70, struct in6_aliasreq) #define SIOCGIFPSRCADDR_IN6 _IOWR('i', 71, struct in6_ifreq) #define SIOCGIFPDSTADDR_IN6 _IOWR('i', 72, struct in6_ifreq) #define SIOCGIFAFLAG_IN6 _IOWR('i', 73, struct in6_ifreq) #define SIOCGDRLST_IN6 _IOWR('i', 74, struct in6_drlist) #ifdef _KERNEL /* XXX: SIOCGPRLST_IN6 is exposed in KAME but in6_oprlist is not. */ #define SIOCGPRLST_IN6 _IOWR('i', 75, struct in6_oprlist) #endif #ifdef _KERNEL #define OSIOCGIFINFO_IN6 _IOWR('i', 76, struct in6_ondireq) #endif #define SIOCGIFINFO_IN6 _IOWR('i', 108, struct in6_ndireq) #define SIOCSNDFLUSH_IN6 _IOWR('i', 77, struct in6_ifreq) #define SIOCGNBRINFO_IN6 _IOWR('i', 78, struct in6_nbrinfo) #define SIOCSPFXFLUSH_IN6 _IOWR('i', 79, struct in6_ifreq) #define SIOCSRTRFLUSH_IN6 _IOWR('i', 80, struct in6_ifreq) #define SIOCGIFALIFETIME_IN6 _IOWR('i', 81, struct in6_ifreq) #define SIOCSIFALIFETIME_IN6 _IOWR('i', 82, struct in6_ifreq) #define SIOCGIFSTAT_IN6 _IOWR('i', 83, struct in6_ifreq) #define SIOCGIFSTAT_ICMP6 _IOWR('i', 84, struct in6_ifreq) #define SIOCSDEFIFACE_IN6 _IOWR('i', 85, struct in6_ndifreq) #define SIOCGDEFIFACE_IN6 _IOWR('i', 86, struct in6_ndifreq) #define SIOCSIFINFO_FLAGS _IOWR('i', 87, struct in6_ndireq) /* XXX */ #define SIOCSSCOPE6 _IOW('i', 88, struct in6_ifreq) #define SIOCGSCOPE6 _IOWR('i', 89, struct in6_ifreq) #define SIOCGSCOPE6DEF _IOWR('i', 90, struct in6_ifreq) #define SIOCSIFPREFIX_IN6 _IOW('i', 100, struct in6_prefixreq) /* set */ #define SIOCGIFPREFIX_IN6 _IOWR('i', 101, struct in6_prefixreq) /* get */ #define SIOCDIFPREFIX_IN6 _IOW('i', 102, struct in6_prefixreq) /* del */ #define SIOCAIFPREFIX_IN6 _IOW('i', 103, struct in6_rrenumreq) /* add */ #define SIOCCIFPREFIX_IN6 _IOW('i', 104, \ struct in6_rrenumreq) /* change */ #define SIOCSGIFPREFIX_IN6 _IOW('i', 105, \ struct in6_rrenumreq) /* set global */ #define SIOCGETSGCNT_IN6 _IOWR('u', 106, \ struct sioc_sg_req6) /* get s,g pkt cnt */ #define SIOCGETMIFCNT_IN6 _IOWR('u', 107, \ struct sioc_mif_req6) /* get pkt cnt per if */ #define SIOCAADDRCTL_POLICY _IOW('u', 108, struct in6_addrpolicy) #define SIOCDADDRCTL_POLICY _IOW('u', 109, struct in6_addrpolicy) #define IN6_IFF_ANYCAST 0x01 /* anycast address */ #define IN6_IFF_TENTATIVE 0x02 /* tentative address */ #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. */ #define IN6_IFF_TEMPORARY 0x80 /* temporary (anonymous) address. */ #define IN6_IFF_NOPFX 0x8000 /* skip kernel prefix management. * XXX: this should be temporary. */ /* do not input/output */ #define IN6_IFF_NOTREADY (IN6_IFF_TENTATIVE|IN6_IFF_DUPLICATED) #ifdef _KERNEL #define IN6_ARE_SCOPE_CMP(a,b) ((a)-(b)) #define IN6_ARE_SCOPE_EQUAL(a,b) ((a)==(b)) #endif #ifdef _KERNEL extern struct in6_ifaddr *in6_ifaddr; extern struct icmp6stat icmp6stat; #define in6_ifstat_inc(ifp, tag) \ do { \ if (ifp) \ ((struct in6_ifextra *)((ifp)->if_afdata[AF_INET6]))->in6_ifstat->tag++; \ } while (/*CONSTCOND*/ 0) extern struct in6_addr zeroin6_addr; extern u_char inet6ctlerrmap[]; extern unsigned long in6_maxmtu; #ifdef MALLOC_DECLARE MALLOC_DECLARE(M_IPMADDR); #endif /* MALLOC_DECLARE */ /* * Macro for finding the internet address structure (in6_ifaddr) corresponding * to a given interface (ifnet structure). */ #define IFP_TO_IA6(ifp, ia) \ /* struct ifnet *ifp; */ \ /* struct in6_ifaddr *ia; */ \ do { \ struct ifaddr *ifa; \ for (ifa = (ifp)->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next) { \ if (!ifa->ifa_addr) \ continue; \ if (ifa->ifa_addr->sa_family == AF_INET6) \ break; \ } \ (ia) = (struct in6_ifaddr *)ifa; \ } while (/*CONSTCOND*/ 0) #endif /* _KERNEL */ /* * Multi-cast membership entry. One for each group/ifp that a PCB * belongs to. */ struct in6_multi_mship { struct in6_multi *i6mm_maddr; /* Multicast address pointer */ LIST_ENTRY(in6_multi_mship) i6mm_chain; /* multicast options chain */ }; struct in6_multi { LIST_ENTRY(in6_multi) in6m_entry; /* list glue */ struct in6_addr in6m_addr; /* IP6 multicast address */ struct ifnet *in6m_ifp; /* back pointer to ifnet */ struct ifmultiaddr *in6m_ifma; /* back pointer to ifmultiaddr */ u_int in6m_refcount; /* # membership claims by sockets */ u_int in6m_state; /* state of the membership */ u_int in6m_timer; /* MLD6 listener report timer */ }; #ifdef _KERNEL extern LIST_HEAD(in6_multihead, in6_multi) in6_multihead; /* * Structure used by macros below to remember position when stepping through * all of the in6_multi records. */ struct in6_multistep { struct in6_ifaddr *i_ia; struct in6_multi *i_in6m; }; /* * Macros for looking up the in6_multi record for a given IP6 multicast * address on a given interface. If no matching record is found, "in6m" * returns NLL. */ #define IN6_LOOKUP_MULTI(addr, ifp, in6m) \ /* struct in6_addr addr; */ \ /* struct ifnet *ifp; */ \ /* struct in6_multi *in6m; */ \ do { \ struct ifmultiaddr *ifma; \ IF_ADDR_LOCK(ifp); \ TAILQ_FOREACH(ifma, &(ifp)->if_multiaddrs, ifma_link) { \ if (ifma->ifma_addr->sa_family == AF_INET6 \ && IN6_ARE_ADDR_EQUAL(&((struct sockaddr_in6 *)ifma->ifma_addr)->sin6_addr, \ &(addr))) \ break; \ } \ (in6m) = (struct in6_multi *)(ifma ? ifma->ifma_protospec : 0); \ IF_ADDR_UNLOCK(ifp); \ } while(0) /* * Macro to step through all of the in6_multi records, one at a time. * The current position is remembered in "step", which the caller must * provide. IN6_FIRST_MULTI(), below, must be called to initialize "step" * and get the first record. Both macros return a NULL "in6m" when there * are no remaining records. */ #define IN6_NEXT_MULTI(step, in6m) \ /* struct in6_multistep step; */ \ /* struct in6_multi *in6m; */ \ do { \ if (((in6m) = (step).i_in6m) != NULL) \ (step).i_in6m = (step).i_in6m->in6m_entry.le_next; \ } while(0) #define IN6_FIRST_MULTI(step, in6m) \ /* struct in6_multistep step; */ \ /* struct in6_multi *in6m */ \ do { \ (step).i_in6m = in6_multihead.lh_first; \ IN6_NEXT_MULTI((step), (in6m)); \ } while(0) struct in6_multi *in6_addmulti __P((struct in6_addr *, struct ifnet *, int *)); void in6_delmulti __P((struct in6_multi *)); struct in6_multi_mship *in6_joingroup(struct ifnet *, struct in6_addr *, int *); int in6_leavegroup(struct in6_multi_mship *); int in6_mask2len __P((struct in6_addr *, u_char *)); int in6_control __P((struct socket *, u_long, caddr_t, struct ifnet *, struct thread *)); int in6_update_ifa __P((struct ifnet *, struct in6_aliasreq *, struct in6_ifaddr *)); void in6_purgeaddr __P((struct ifaddr *)); int in6if_do_dad __P((struct ifnet *)); void in6_purgeif __P((struct ifnet *)); void in6_savemkludge __P((struct in6_ifaddr *)); void *in6_domifattach __P((struct ifnet *)); void in6_domifdetach __P((struct ifnet *, void *)); void in6_setmaxmtu __P((void)); void in6_restoremkludge __P((struct in6_ifaddr *, struct ifnet *)); void in6_purgemkludge __P((struct ifnet *)); struct in6_ifaddr *in6ifa_ifpforlinklocal __P((struct ifnet *, int)); struct in6_ifaddr *in6ifa_ifpwithaddr __P((struct ifnet *, struct in6_addr *)); char *ip6_sprintf __P((const struct in6_addr *)); int in6_addr2zoneid __P((struct ifnet *, struct in6_addr *, u_int32_t *)); int in6_matchlen __P((struct in6_addr *, struct in6_addr *)); int in6_are_prefix_equal __P((struct in6_addr *, struct in6_addr *, int)); void in6_prefixlen2mask __P((struct in6_addr *, int)); int in6_prefix_ioctl __P((struct socket *, u_long, caddr_t, struct ifnet *)); int in6_prefix_add_ifid __P((int, struct in6_ifaddr *)); void in6_prefix_remove_ifid __P((int, struct in6_ifaddr *)); void in6_purgeprefix __P((struct ifnet *)); void in6_ifremloop(struct ifaddr *); void in6_ifaddloop(struct ifaddr *); int in6_is_addr_deprecated __P((struct sockaddr_in6 *)); struct inpcb; int in6_embedscope __P((struct in6_addr *, const struct sockaddr_in6 *, struct inpcb *, struct ifnet **)); int in6_recoverscope __P((struct sockaddr_in6 *, const struct in6_addr *, struct ifnet *)); void in6_clearscope __P((struct in6_addr *)); int in6_src_ioctl __P((u_long, caddr_t)); #endif /* _KERNEL */ #endif /* _NETINET6_IN6_VAR_H_ */