in6.c revision 53541
153541Sshin/* 253541Sshin * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 353541Sshin * All rights reserved. 453541Sshin * 553541Sshin * Redistribution and use in source and binary forms, with or without 653541Sshin * modification, are permitted provided that the following conditions 753541Sshin * are met: 853541Sshin * 1. Redistributions of source code must retain the above copyright 953541Sshin * notice, this list of conditions and the following disclaimer. 1053541Sshin * 2. Redistributions in binary form must reproduce the above copyright 1153541Sshin * notice, this list of conditions and the following disclaimer in the 1253541Sshin * documentation and/or other materials provided with the distribution. 1353541Sshin * 3. Neither the name of the project nor the names of its contributors 1453541Sshin * may be used to endorse or promote products derived from this software 1553541Sshin * without specific prior written permission. 1653541Sshin * 1753541Sshin * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 1853541Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1953541Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2053541Sshin * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 2153541Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2253541Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2353541Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2453541Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2553541Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2653541Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2753541Sshin * SUCH DAMAGE. 2853541Sshin * 2953541Sshin * $FreeBSD: head/sys/netinet6/in6.c 53541 1999-11-22 02:45:11Z shin $ 3053541Sshin */ 3153541Sshin 3253541Sshin/* 3353541Sshin * Copyright (c) 1982, 1986, 1991, 1993 3453541Sshin * The Regents of the University of California. All rights reserved. 3553541Sshin * 3653541Sshin * Redistribution and use in source and binary forms, with or without 3753541Sshin * modification, are permitted provided that the following conditions 3853541Sshin * are met: 3953541Sshin * 1. Redistributions of source code must retain the above copyright 4053541Sshin * notice, this list of conditions and the following disclaimer. 4153541Sshin * 2. Redistributions in binary form must reproduce the above copyright 4253541Sshin * notice, this list of conditions and the following disclaimer in the 4353541Sshin * documentation and/or other materials provided with the distribution. 4453541Sshin * 3. All advertising materials mentioning features or use of this software 4553541Sshin * must display the following acknowledgement: 4653541Sshin * This product includes software developed by the University of 4753541Sshin * California, Berkeley and its contributors. 4853541Sshin * 4. Neither the name of the University nor the names of its contributors 4953541Sshin * may be used to endorse or promote products derived from this software 5053541Sshin * without specific prior written permission. 5153541Sshin * 5253541Sshin * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 5353541Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 5453541Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 5553541Sshin * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 5653541Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 5753541Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 5853541Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 5953541Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 6053541Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 6153541Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 6253541Sshin * SUCH DAMAGE. 6353541Sshin * 6453541Sshin * @(#)in.c 8.2 (Berkeley) 11/15/93 6553541Sshin */ 6653541Sshin 6753541Sshin#include "opt_inet.h" 6853541Sshin 6953541Sshin#include <sys/param.h> 7053541Sshin#include <sys/errno.h> 7153541Sshin#include <sys/malloc.h> 7253541Sshin#include <sys/socket.h> 7353541Sshin#include <sys/socketvar.h> 7453541Sshin#include <sys/sockio.h> 7553541Sshin#include <sys/systm.h> 7653541Sshin#include <sys/proc.h> 7753541Sshin#include <sys/time.h> 7853541Sshin#include <sys/kernel.h> 7953541Sshin#include <sys/syslog.h> 8053541Sshin 8153541Sshin#include <net/if.h> 8253541Sshin#include <net/if_types.h> 8353541Sshin#include <net/route.h> 8453541Sshin/* #include "gif.h" */ 8553541Sshin#if NGIF > 0 8653541Sshin#include <net/if_gif.h> 8753541Sshin#endif 8853541Sshin#include <net/if_dl.h> 8953541Sshin 9053541Sshin#include <netinet/in.h> 9153541Sshin#include <netinet/in_var.h> 9253541Sshin#include <netinet/if_ether.h> 9353541Sshin 9453541Sshin#include <netinet6/nd6.h> 9553541Sshin#include <netinet6/ip6.h> 9653541Sshin#include <netinet6/ip6_var.h> 9753541Sshin#include <netinet6/mld6_var.h> 9853541Sshin#include <netinet6/in6_ifattach.h> 9953541Sshin 10053541Sshin#include <net/net_osdep.h> 10153541Sshin 10253541SshinMALLOC_DEFINE(M_IPMADDR, "in6_multi", "internet multicast address"); 10353541Sshin 10453541Sshin/* 10553541Sshin * Definitions of some costant IP6 addresses. 10653541Sshin */ 10753541Sshinconst struct in6_addr in6addr_any = IN6ADDR_ANY_INIT; 10853541Sshinconst struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT; 10953541Sshinconst struct in6_addr in6addr_nodelocal_allnodes = 11053541Sshin IN6ADDR_NODELOCAL_ALLNODES_INIT; 11153541Sshinconst struct in6_addr in6addr_linklocal_allnodes = 11253541Sshin IN6ADDR_LINKLOCAL_ALLNODES_INIT; 11353541Sshinconst struct in6_addr in6addr_linklocal_allrouters = 11453541Sshin IN6ADDR_LINKLOCAL_ALLROUTERS_INIT; 11553541Sshin 11653541Sshinconst struct in6_addr in6mask0 = IN6MASK0; 11753541Sshinconst struct in6_addr in6mask32 = IN6MASK32; 11853541Sshinconst struct in6_addr in6mask64 = IN6MASK64; 11953541Sshinconst struct in6_addr in6mask96 = IN6MASK96; 12053541Sshinconst struct in6_addr in6mask128 = IN6MASK128; 12153541Sshin 12253541Sshinstatic int in6_lifaddr_ioctl __P((struct socket *, u_long, caddr_t, 12353541Sshin struct ifnet *, struct proc *)); 12453541Sshinstruct in6_multihead in6_multihead; /* XXX BSS initialization */ 12553541Sshin 12653541Sshin/* 12753541Sshin * Determine whether an IP6 address is in a reserved set of addresses 12853541Sshin * that may not be forwarded, or whether datagrams to that destination 12953541Sshin * may be forwarded. 13053541Sshin */ 13153541Sshinint 13253541Sshinin6_canforward(src, dst) 13353541Sshin struct in6_addr *src, *dst; 13453541Sshin{ 13553541Sshin if (IN6_IS_ADDR_LINKLOCAL(src) || 13653541Sshin IN6_IS_ADDR_LINKLOCAL(dst) || 13753541Sshin IN6_IS_ADDR_MULTICAST(dst)) 13853541Sshin return(0); 13953541Sshin return(1); 14053541Sshin} 14153541Sshin 14253541Sshin/* 14353541Sshin * Check if the loopback entry will be automatically generated. 14453541Sshin * if 0 returned, will not be automatically generated. 14553541Sshin * if 1 returned, will be automatically generated. 14653541Sshin */ 14753541Sshinstatic int 14853541Sshinin6_is_ifloop_auto(struct ifaddr *ifa) 14953541Sshin{ 15053541Sshin#define SIN6(s) ((struct sockaddr_in6 *)s) 15153541Sshin /* 15253541Sshin * If RTF_CLONING is unset, or (IFF_LOOPBACK | IFF_POINTOPOINT), 15353541Sshin * or netmask is all0 or all1, then cloning will not happen, 15453541Sshin * then we can't rely on its loopback entry generation. 15553541Sshin */ 15653541Sshin if ((ifa->ifa_flags & RTF_CLONING) == 0 || 15753541Sshin (ifa->ifa_ifp->if_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) || 15853541Sshin (SIN6(ifa->ifa_netmask)->sin6_len == sizeof(struct sockaddr_in6) 15953541Sshin && 16053541Sshin IN6_ARE_ADDR_EQUAL(&SIN6(ifa->ifa_netmask)->sin6_addr, 16153541Sshin &in6mask128)) || 16253541Sshin ((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_len == 0) 16353541Sshin return 0; 16453541Sshin else 16553541Sshin return 1; 16653541Sshin#undef SIN6 16753541Sshin} 16853541Sshin 16953541Sshin/* 17053541Sshin * Subroutine for in6_ifaddloop() and in6_ifremloop(). 17153541Sshin * This routine does actual work. 17253541Sshin */ 17353541Sshinstatic void 17453541Sshinin6_ifloop_request(int cmd, struct ifaddr *ifa) 17553541Sshin{ 17653541Sshin struct sockaddr_in6 lo_sa; 17753541Sshin struct sockaddr_in6 all1_sa; 17853541Sshin struct rtentry *nrt = NULL; 17953541Sshin 18053541Sshin bzero(&lo_sa, sizeof(lo_sa)); 18153541Sshin bzero(&all1_sa, sizeof(all1_sa)); 18253541Sshin lo_sa.sin6_family = AF_INET6; 18353541Sshin lo_sa.sin6_len = sizeof(struct sockaddr_in6); 18453541Sshin all1_sa = lo_sa; 18553541Sshin lo_sa.sin6_addr = in6addr_loopback; 18653541Sshin all1_sa.sin6_addr = in6mask128; 18753541Sshin 18853541Sshin /* So we add or remove static loopback entry, here. */ 18953541Sshin rtrequest(cmd, ifa->ifa_addr, 19053541Sshin (struct sockaddr *)&lo_sa, 19153541Sshin (struct sockaddr *)&all1_sa, 19253541Sshin RTF_UP|RTF_HOST, &nrt); 19353541Sshin 19453541Sshin /* 19553541Sshin * Make sure rt_ifa be equal to IFA, the second argument of the 19653541Sshin * function. 19753541Sshin * We need this because when we refer rt_ifa->ia6_flags in ip6_input, 19853541Sshin * we assume that the rt_ifa points to the address instead of the 19953541Sshin * loopback address. 20053541Sshin */ 20153541Sshin if (cmd == RTM_ADD && nrt && ifa != nrt->rt_ifa) { 20253541Sshin nrt->rt_ifa->ifa_refcnt--; 20353541Sshin ifa->ifa_refcnt++; 20453541Sshin nrt->rt_ifa = ifa; 20553541Sshin } 20653541Sshin if (nrt) 20753541Sshin nrt->rt_refcnt--; 20853541Sshin} 20953541Sshin 21053541Sshin/* 21153541Sshin * Add ownaddr as loopback rtentry, if necessary(ex. on p2p link). 21253541Sshin * Because, KAME needs loopback rtentry for ownaddr check in 21353541Sshin * ip6_input(). 21453541Sshin */ 21553541Sshinstatic void 21653541Sshinin6_ifaddloop(struct ifaddr *ifa) 21753541Sshin{ 21853541Sshin if (!in6_is_ifloop_auto(ifa)) { 21953541Sshin struct rtentry *rt; 22053541Sshin 22153541Sshin /* If there is no loopback entry, allocate one. */ 22253541Sshin rt = rtalloc1(ifa->ifa_addr, 0, 0); 22353541Sshin if (rt == 0 || (rt->rt_ifp->if_flags & IFF_LOOPBACK) == 0) 22453541Sshin in6_ifloop_request(RTM_ADD, ifa); 22553541Sshin if (rt) 22653541Sshin rt->rt_refcnt--; 22753541Sshin } 22853541Sshin} 22953541Sshin 23053541Sshin/* 23153541Sshin * Remove loopback rtentry of ownaddr generated by in6_ifaddloop(), 23253541Sshin * if it exists. 23353541Sshin */ 23453541Sshinstatic void 23553541Sshinin6_ifremloop(struct ifaddr *ifa) 23653541Sshin{ 23753541Sshin if (!in6_is_ifloop_auto(ifa)) { 23853541Sshin struct in6_ifaddr *ia; 23953541Sshin int ia_count = 0; 24053541Sshin 24153541Sshin /* If only one ifa for the loopback entry, delete it. */ 24253541Sshin for (ia = in6_ifaddr; ia; ia = ia->ia_next) { 24353541Sshin if (IN6_ARE_ADDR_EQUAL(IFA_IN6(ifa), 24453541Sshin &ia->ia_addr.sin6_addr)) { 24553541Sshin ia_count++; 24653541Sshin if (ia_count > 1) 24753541Sshin break; 24853541Sshin } 24953541Sshin } 25053541Sshin if (ia_count == 1) 25153541Sshin in6_ifloop_request(RTM_DELETE, ifa); 25253541Sshin } 25353541Sshin} 25453541Sshin 25553541Sshin/* 25653541Sshin * Subroutine for in6_ifaddproxy() and in6_ifremproxy(). 25753541Sshin * This routine does actual work. 25853541Sshin * call in6_addmulti() when cmd == 1. 25953541Sshin * call in6_delmulti() when cmd == 2. 26053541Sshin */ 26153541Sshinstatic int 26253541Sshinin6_ifproxy_request(int cmd, struct in6_ifaddr *ia) 26353541Sshin{ 26453541Sshin int error = 0; 26553541Sshin 26653541Sshin /* 26753541Sshin * If we have an IPv6 dstaddr on adding p2p interface, 26853541Sshin * join dstaddr's solicited multicast on necessary interface. 26953541Sshin */ 27053541Sshin if ((ia->ia_ifp->if_flags & IFF_POINTOPOINT) && 27153541Sshin ia->ia_dstaddr.sin6_family == AF_INET6 && 27253541Sshin !IN6_IS_ADDR_LINKLOCAL(&ia->ia_dstaddr.sin6_addr)) { 27353541Sshin struct in6_ifaddr *ia_lan; 27453541Sshin 27553541Sshin /* 27653541Sshin * TODO: Join only on some specified interfaces by some 27753541Sshin * configuration. 27853541Sshin * Unsolicited Neighbor Advertisements will be also necessary. 27953541Sshin * 28053541Sshin * Now, join on interfaces which meets following. 28153541Sshin * -IFF_BROADCAST and IFF_MULTICAST 28253541Sshin * (NBMA is out of scope) 28353541Sshin * -the prefix value is same as p2p dstaddr 28453541Sshin */ 28553541Sshin for (ia_lan = in6_ifaddr; ia_lan; ia_lan = ia_lan->ia_next) { 28653541Sshin struct in6_addr llsol; 28753541Sshin 28853541Sshin if ((ia_lan->ia_ifp->if_flags & 28953541Sshin (IFF_BROADCAST|IFF_MULTICAST)) != 29053541Sshin (IFF_BROADCAST|IFF_MULTICAST)) 29153541Sshin continue; 29253541Sshin if (!IN6_ARE_MASKED_ADDR_EQUAL(IA6_IN6(ia), 29353541Sshin IA6_IN6(ia_lan), 29453541Sshin IA6_MASKIN6(ia_lan))) 29553541Sshin continue; 29653541Sshin if (ia_lan->ia_ifp == ia->ia_ifp) 29753541Sshin continue; 29853541Sshin 29953541Sshin /* init llsol */ 30053541Sshin bzero(&llsol, sizeof(struct in6_addr)); 30153541Sshin llsol.s6_addr16[0] = htons(0xff02); 30253541Sshin llsol.s6_addr16[1] = htons(ia_lan->ia_ifp->if_index); 30353541Sshin llsol.s6_addr32[1] = 0; 30453541Sshin llsol.s6_addr32[2] = htonl(1); 30553541Sshin llsol.s6_addr32[3] = 30653541Sshin ia->ia_dstaddr.sin6_addr.s6_addr32[3]; 30753541Sshin llsol.s6_addr8[12] = 0xff; 30853541Sshin 30953541Sshin if (cmd == 1) 31053541Sshin (void)in6_addmulti(&llsol, 31153541Sshin ia_lan->ia_ifp, 31253541Sshin &error); 31353541Sshin else if (cmd == 2) { 31453541Sshin struct in6_multi *in6m; 31553541Sshin 31653541Sshin IN6_LOOKUP_MULTI(llsol, 31753541Sshin ia_lan->ia_ifp, 31853541Sshin in6m); 31953541Sshin if (in6m) 32053541Sshin in6_delmulti(in6m); 32153541Sshin } 32253541Sshin } 32353541Sshin } 32453541Sshin return error; 32553541Sshin} 32653541Sshin 32753541Sshinstatic int 32853541Sshinin6_ifaddproxy(struct in6_ifaddr *ia) 32953541Sshin{ 33053541Sshin return(in6_ifproxy_request(1, ia)); 33153541Sshin} 33253541Sshin 33353541Sshinstatic void 33453541Sshinin6_ifremproxy(struct in6_ifaddr *ia) 33553541Sshin{ 33653541Sshin in6_ifproxy_request(2, ia); 33753541Sshin} 33853541Sshin 33953541Sshinint 34053541Sshinin6_ifindex2scopeid(idx) 34153541Sshin int idx; 34253541Sshin{ 34353541Sshin struct ifnet *ifp; 34453541Sshin struct ifaddr *ifa; 34553541Sshin struct sockaddr_in6 *sin6; 34653541Sshin 34753541Sshin if (idx < 0 || if_index < idx) 34853541Sshin return -1; 34953541Sshin ifp = ifindex2ifnet[idx]; 35053541Sshin 35153541Sshin for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next) 35253541Sshin { 35353541Sshin if (ifa->ifa_addr->sa_family != AF_INET6) 35453541Sshin continue; 35553541Sshin sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; 35653541Sshin if (IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr)) 35753541Sshin return sin6->sin6_scope_id & 0xffff; 35853541Sshin } 35953541Sshin 36053541Sshin return -1; 36153541Sshin} 36253541Sshin 36353541Sshinint 36453541Sshinin6_mask2len(mask) 36553541Sshin struct in6_addr *mask; 36653541Sshin{ 36753541Sshin int x, y; 36853541Sshin 36953541Sshin for (x = 0; x < sizeof(*mask); x++) { 37053541Sshin if (mask->s6_addr8[x] != 0xff) 37153541Sshin break; 37253541Sshin } 37353541Sshin y = 0; 37453541Sshin if (x < sizeof(*mask)) { 37553541Sshin for (y = 0; y < 8; y++) { 37653541Sshin if ((mask->s6_addr8[x] & (0x80 >> y)) == 0) 37753541Sshin break; 37853541Sshin } 37953541Sshin } 38053541Sshin return x * 8 + y; 38153541Sshin} 38253541Sshin 38353541Sshinvoid 38453541Sshinin6_len2mask(mask, len) 38553541Sshin struct in6_addr *mask; 38653541Sshin int len; 38753541Sshin{ 38853541Sshin int i; 38953541Sshin 39053541Sshin bzero(mask, sizeof(*mask)); 39153541Sshin for (i = 0; i < len / 8; i++) 39253541Sshin mask->s6_addr8[i] = 0xff; 39353541Sshin if (len % 8) 39453541Sshin mask->s6_addr8[i] = (0xff00 >> (len % 8)) & 0xff; 39553541Sshin} 39653541Sshin 39753541Sshinint in6_interfaces; /* number of external internet interfaces */ 39853541Sshin 39953541Sshin#define ifa2ia6(ifa) ((struct in6_ifaddr *)(ifa)) 40053541Sshin#define ia62ifa(ia6) ((struct ifaddr *)(ia6)) 40153541Sshin 40253541Sshinint 40353541Sshinin6_control(so, cmd, data, ifp, p) 40453541Sshin struct socket *so; 40553541Sshin u_long cmd; 40653541Sshin caddr_t data; 40753541Sshin struct ifnet *ifp; 40853541Sshin struct proc *p; 40953541Sshin{ 41053541Sshin struct in6_ifreq *ifr = (struct in6_ifreq *)data; 41153541Sshin struct in6_ifaddr *ia, *oia; 41253541Sshin struct in6_aliasreq *ifra = (struct in6_aliasreq *)data; 41353541Sshin struct sockaddr_in6 oldaddr, net; 41453541Sshin int error = 0, hostIsNew, prefixIsNew; 41553541Sshin int privileged; 41653541Sshin 41753541Sshin privileged = 0; 41853541Sshin if (p && !suser(p)) 41953541Sshin privileged++; 42053541Sshin 42153541Sshin /* 42253541Sshin * xxx should prevent processes for link-local addresses? 42353541Sshin */ 42453541Sshin#if NGIF > 0 42553541Sshin if (ifp && ifp->if_type == IFT_GIF) { 42653541Sshin switch (cmd) { 42753541Sshin case SIOCSIFPHYADDR_IN6: 42853541Sshin if (!privileged) 42953541Sshin return(EPERM); 43053541Sshin /*fall through*/ 43153541Sshin case SIOCGIFPSRCADDR_IN6: 43253541Sshin case SIOCGIFPDSTADDR_IN6: 43353541Sshin return gif_ioctl(ifp, cmd, data); 43453541Sshin } 43553541Sshin } 43653541Sshin#endif 43753541Sshin 43853541Sshin if (ifp == 0) 43953541Sshin return(EOPNOTSUPP); 44053541Sshin 44153541Sshin switch (cmd) { 44253541Sshin case SIOCSNDFLUSH_IN6: 44353541Sshin case SIOCSPFXFLUSH_IN6: 44453541Sshin case SIOCSRTRFLUSH_IN6: 44553541Sshin if (!privileged) 44653541Sshin return(EPERM); 44753541Sshin /*fall through*/ 44853541Sshin case SIOCGIFINFO_IN6: 44953541Sshin case SIOCGDRLST_IN6: 45053541Sshin case SIOCGPRLST_IN6: 45153541Sshin case SIOCGNBRINFO_IN6: 45253541Sshin return(nd6_ioctl(cmd, data, ifp)); 45353541Sshin } 45453541Sshin 45553541Sshin switch (cmd) { 45653541Sshin case SIOCSIFPREFIX_IN6: 45753541Sshin case SIOCDIFPREFIX_IN6: 45853541Sshin case SIOCAIFPREFIX_IN6: 45953541Sshin case SIOCCIFPREFIX_IN6: 46053541Sshin case SIOCSGIFPREFIX_IN6: 46153541Sshin if (!privileged) 46253541Sshin return(EPERM); 46353541Sshin /*fall through*/ 46453541Sshin case SIOCGIFPREFIX_IN6: 46553541Sshin return(in6_prefix_ioctl(so, cmd, data, ifp)); 46653541Sshin } 46753541Sshin 46853541Sshin switch (cmd) { 46953541Sshin case SIOCALIFADDR: 47053541Sshin case SIOCDLIFADDR: 47153541Sshin if (!privileged) 47253541Sshin return(EPERM); 47353541Sshin /*fall through*/ 47453541Sshin case SIOCGLIFADDR: 47553541Sshin return in6_lifaddr_ioctl(so, cmd, data, ifp, p); 47653541Sshin } 47753541Sshin 47853541Sshin /* 47953541Sshin * Find address for this interface, if it exists. 48053541Sshin */ 48153541Sshin { 48253541Sshin 48353541Sshin struct sockaddr_in6 *sa6 = 48453541Sshin (struct sockaddr_in6 *)&ifra->ifra_addr; 48553541Sshin 48653541Sshin if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr)) { 48753541Sshin if (sa6->sin6_addr.s6_addr16[1] == 0) { 48853541Sshin /* interface ID is not embedded by the user */ 48953541Sshin sa6->sin6_addr.s6_addr16[1] = 49053541Sshin htons(ifp->if_index); 49153541Sshin } else 49253541Sshin if (sa6->sin6_addr.s6_addr16[1] != 49353541Sshin htons(ifp->if_index)) 49453541Sshin return(EINVAL); /* ifid is contradict */ 49553541Sshin if (sa6->sin6_scope_id) { 49653541Sshin if (sa6->sin6_scope_id != 49753541Sshin (u_int32_t)ifp->if_index) 49853541Sshin return(EINVAL); 49953541Sshin sa6->sin6_scope_id = 0; /* XXX: good way? */ 50053541Sshin } 50153541Sshin } 50253541Sshin } 50353541Sshin ia = in6ifa_ifpwithaddr(ifp, &ifra->ifra_addr.sin6_addr); 50453541Sshin 50553541Sshin switch (cmd) { 50653541Sshin 50753541Sshin case SIOCDIFADDR_IN6: 50853541Sshin if (ia == 0) 50953541Sshin return(EADDRNOTAVAIL); 51053541Sshin /* FALLTHROUGH */ 51153541Sshin case SIOCAIFADDR_IN6: 51253541Sshin case SIOCSIFADDR_IN6: 51353541Sshin case SIOCSIFNETMASK_IN6: 51453541Sshin case SIOCSIFDSTADDR_IN6: 51553541Sshin if (!privileged) 51653541Sshin return(EPERM); 51753541Sshin if (ia == 0) { 51853541Sshin ia = (struct in6_ifaddr *) 51953541Sshin malloc(sizeof(*ia), M_IFADDR, M_WAITOK); 52053541Sshin if (ia == NULL) 52153541Sshin return (ENOBUFS); 52253541Sshin bzero((caddr_t)ia, sizeof(*ia)); 52353541Sshin ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; 52453541Sshin ia->ia_ifa.ifa_dstaddr 52553541Sshin = (struct sockaddr *)&ia->ia_dstaddr; 52653541Sshin ia->ia_ifa.ifa_netmask 52753541Sshin = (struct sockaddr *)&ia->ia_prefixmask; 52853541Sshin 52953541Sshin ia->ia_ifp = ifp; 53053541Sshin if ((oia = in6_ifaddr) != NULL) { 53153541Sshin for ( ; oia->ia_next; oia = oia->ia_next) 53253541Sshin continue; 53353541Sshin oia->ia_next = ia; 53453541Sshin } else 53553541Sshin in6_ifaddr = ia; 53653541Sshin TAILQ_INSERT_TAIL(&ifp->if_addrlist, 53753541Sshin (struct ifaddr *)ia, ifa_list); 53853541Sshin if ((ifp->if_flags & IFF_LOOPBACK) == 0) 53953541Sshin in6_interfaces++; /*XXX*/ 54053541Sshin } 54153541Sshin 54253541Sshin if (cmd == SIOCAIFADDR_IN6) { 54353541Sshin /* sanity for overflow - beware unsigned */ 54453541Sshin struct in6_addrlifetime *lt; 54553541Sshin lt = &ifra->ifra_lifetime; 54653541Sshin if (lt->ia6t_vltime != ND6_INFINITE_LIFETIME 54753541Sshin && lt->ia6t_vltime + time_second < time_second) { 54853541Sshin return EINVAL; 54953541Sshin } 55053541Sshin if (lt->ia6t_pltime != ND6_INFINITE_LIFETIME 55153541Sshin && lt->ia6t_pltime + time_second < time_second) { 55253541Sshin return EINVAL; 55353541Sshin } 55453541Sshin } 55553541Sshin break; 55653541Sshin 55753541Sshin case SIOCGIFADDR_IN6: 55853541Sshin /* This interface is basically deprecated. use SIOCGIFCONF. */ 55953541Sshin /* fall through */ 56053541Sshin case SIOCGIFAFLAG_IN6: 56153541Sshin case SIOCGIFNETMASK_IN6: 56253541Sshin case SIOCGIFDSTADDR_IN6: 56353541Sshin case SIOCGIFALIFETIME_IN6: 56453541Sshin /* must think again about its semantics */ 56553541Sshin if (ia == 0) 56653541Sshin return(EADDRNOTAVAIL); 56753541Sshin break; 56853541Sshin case SIOCSIFALIFETIME_IN6: 56953541Sshin { 57053541Sshin struct in6_addrlifetime *lt; 57153541Sshin 57253541Sshin if (!privileged) 57353541Sshin return(EPERM); 57453541Sshin if (ia == 0) 57553541Sshin return(EADDRNOTAVAIL); 57653541Sshin /* sanity for overflow - beware unsigned */ 57753541Sshin lt = &ifr->ifr_ifru.ifru_lifetime; 57853541Sshin if (lt->ia6t_vltime != ND6_INFINITE_LIFETIME 57953541Sshin && lt->ia6t_vltime + time_second < time_second) { 58053541Sshin return EINVAL; 58153541Sshin } 58253541Sshin if (lt->ia6t_pltime != ND6_INFINITE_LIFETIME 58353541Sshin && lt->ia6t_pltime + time_second < time_second) { 58453541Sshin return EINVAL; 58553541Sshin } 58653541Sshin break; 58753541Sshin } 58853541Sshin } 58953541Sshin 59053541Sshin switch (cmd) { 59153541Sshin 59253541Sshin case SIOCGIFADDR_IN6: 59353541Sshin ifr->ifr_addr = ia->ia_addr; 59453541Sshin break; 59553541Sshin 59653541Sshin case SIOCGIFDSTADDR_IN6: 59753541Sshin if ((ifp->if_flags & IFF_POINTOPOINT) == 0) 59853541Sshin return(EINVAL); 59953541Sshin ifr->ifr_dstaddr = ia->ia_dstaddr; 60053541Sshin break; 60153541Sshin 60253541Sshin case SIOCGIFNETMASK_IN6: 60353541Sshin ifr->ifr_addr = ia->ia_prefixmask; 60453541Sshin break; 60553541Sshin 60653541Sshin case SIOCGIFAFLAG_IN6: 60753541Sshin ifr->ifr_ifru.ifru_flags6 = ia->ia6_flags; 60853541Sshin break; 60953541Sshin 61053541Sshin case SIOCGIFSTAT_IN6: 61153541Sshin if (ifp == NULL) 61253541Sshin return EINVAL; 61353541Sshin if (in6_ifstat == NULL || ifp->if_index >= in6_ifstatmax 61453541Sshin || in6_ifstat[ifp->if_index] == NULL) { 61553541Sshin /* return EAFNOSUPPORT? */ 61653541Sshin bzero(&ifr->ifr_ifru.ifru_stat, 61753541Sshin sizeof(ifr->ifr_ifru.ifru_stat)); 61853541Sshin } else 61953541Sshin ifr->ifr_ifru.ifru_stat = *in6_ifstat[ifp->if_index]; 62053541Sshin break; 62153541Sshin 62253541Sshin case SIOCGIFSTAT_ICMP6: 62353541Sshin if (ifp == NULL) 62453541Sshin return EINVAL; 62553541Sshin if (icmp6_ifstat == NULL || ifp->if_index >= icmp6_ifstatmax || 62653541Sshin icmp6_ifstat[ifp->if_index] == NULL) { 62753541Sshin /* return EAFNOSUPPORT? */ 62853541Sshin bzero(&ifr->ifr_ifru.ifru_stat, 62953541Sshin sizeof(ifr->ifr_ifru.ifru_icmp6stat)); 63053541Sshin } else 63153541Sshin ifr->ifr_ifru.ifru_icmp6stat = 63253541Sshin *icmp6_ifstat[ifp->if_index]; 63353541Sshin break; 63453541Sshin 63553541Sshin case SIOCSIFDSTADDR_IN6: 63653541Sshin if ((ifp->if_flags & IFF_POINTOPOINT) == 0) 63753541Sshin return(EINVAL); 63853541Sshin oldaddr = ia->ia_dstaddr; 63953541Sshin ia->ia_dstaddr = ifr->ifr_dstaddr; 64053541Sshin 64153541Sshin /* link-local index check */ 64253541Sshin if (IN6_IS_ADDR_LINKLOCAL(&ia->ia_dstaddr.sin6_addr)) { 64353541Sshin if (ia->ia_dstaddr.sin6_addr.s6_addr16[1] == 0) { 64453541Sshin /* interface ID is not embedded by the user */ 64553541Sshin ia->ia_dstaddr.sin6_addr.s6_addr16[1] 64653541Sshin = htons(ifp->if_index); 64753541Sshin } else 64853541Sshin if (ia->ia_dstaddr.sin6_addr.s6_addr16[1] != 64953541Sshin htons(ifp->if_index)) { 65053541Sshin ia->ia_dstaddr = oldaddr; 65153541Sshin return(EINVAL); /* ifid is contradict */ 65253541Sshin } 65353541Sshin } 65453541Sshin 65553541Sshin if (ifp->if_ioctl && (error = (ifp->if_ioctl) 65653541Sshin (ifp, SIOCSIFDSTADDR, (caddr_t)ia))) { 65753541Sshin ia->ia_dstaddr = oldaddr; 65853541Sshin return(error); 65953541Sshin } 66053541Sshin if (ia->ia_flags & IFA_ROUTE) { 66153541Sshin ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&oldaddr; 66253541Sshin rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST); 66353541Sshin ia->ia_ifa.ifa_dstaddr = 66453541Sshin (struct sockaddr *)&ia->ia_dstaddr; 66553541Sshin rtinit(&(ia->ia_ifa), (int)RTM_ADD, RTF_HOST|RTF_UP); 66653541Sshin } 66753541Sshin break; 66853541Sshin 66953541Sshin case SIOCGIFALIFETIME_IN6: 67053541Sshin ifr->ifr_ifru.ifru_lifetime = ia->ia6_lifetime; 67153541Sshin break; 67253541Sshin 67353541Sshin case SIOCSIFALIFETIME_IN6: 67453541Sshin ia->ia6_lifetime = ifr->ifr_ifru.ifru_lifetime; 67553541Sshin /* for sanity */ 67653541Sshin if (ia->ia6_lifetime.ia6t_vltime != ND6_INFINITE_LIFETIME) { 67753541Sshin ia->ia6_lifetime.ia6t_expire = 67853541Sshin time_second + ia->ia6_lifetime.ia6t_vltime; 67953541Sshin } else 68053541Sshin ia->ia6_lifetime.ia6t_expire = 0; 68153541Sshin if (ia->ia6_lifetime.ia6t_pltime != ND6_INFINITE_LIFETIME) { 68253541Sshin ia->ia6_lifetime.ia6t_preferred = 68353541Sshin time_second + ia->ia6_lifetime.ia6t_pltime; 68453541Sshin } else 68553541Sshin ia->ia6_lifetime.ia6t_preferred = 0; 68653541Sshin break; 68753541Sshin 68853541Sshin case SIOCSIFADDR_IN6: 68953541Sshin return(in6_ifinit(ifp, ia, &ifr->ifr_addr, 1)); 69053541Sshin 69153541Sshin case SIOCSIFNETMASK_IN6: 69253541Sshin ia->ia_prefixmask = ifr->ifr_addr; 69353541Sshin bzero(&net, sizeof(net)); 69453541Sshin net.sin6_len = sizeof(struct sockaddr_in6); 69553541Sshin net.sin6_family = AF_INET6; 69653541Sshin net.sin6_port = htons(0); 69753541Sshin net.sin6_flowinfo = htonl(0); 69853541Sshin net.sin6_addr.s6_addr32[0] 69953541Sshin = ia->ia_addr.sin6_addr.s6_addr32[0] & 70053541Sshin ia->ia_prefixmask.sin6_addr.s6_addr32[0]; 70153541Sshin net.sin6_addr.s6_addr32[1] 70253541Sshin = ia->ia_addr.sin6_addr.s6_addr32[1] & 70353541Sshin ia->ia_prefixmask.sin6_addr.s6_addr32[1]; 70453541Sshin net.sin6_addr.s6_addr32[2] 70553541Sshin = ia->ia_addr.sin6_addr.s6_addr32[2] & 70653541Sshin ia->ia_prefixmask.sin6_addr.s6_addr32[2]; 70753541Sshin net.sin6_addr.s6_addr32[3] 70853541Sshin = ia->ia_addr.sin6_addr.s6_addr32[3] & 70953541Sshin ia->ia_prefixmask.sin6_addr.s6_addr32[3]; 71053541Sshin ia->ia_net = net; 71153541Sshin break; 71253541Sshin 71353541Sshin case SIOCAIFADDR_IN6: 71453541Sshin prefixIsNew = 0; 71553541Sshin hostIsNew = 1; 71653541Sshin 71753541Sshin if (ifra->ifra_addr.sin6_len == 0) { 71853541Sshin ifra->ifra_addr = ia->ia_addr; 71953541Sshin hostIsNew = 0; 72053541Sshin } else if (IN6_ARE_ADDR_EQUAL(&ifra->ifra_addr.sin6_addr, 72153541Sshin &ia->ia_addr.sin6_addr)) 72253541Sshin hostIsNew = 0; 72353541Sshin 72453541Sshin if (ifra->ifra_prefixmask.sin6_len) { 72553541Sshin in6_ifscrub(ifp, ia); 72653541Sshin ia->ia_prefixmask = ifra->ifra_prefixmask; 72753541Sshin prefixIsNew = 1; 72853541Sshin } 72953541Sshin if ((ifp->if_flags & IFF_POINTOPOINT) && 73053541Sshin (ifra->ifra_dstaddr.sin6_family == AF_INET6)) { 73153541Sshin in6_ifscrub(ifp, ia); 73253541Sshin ia->ia_dstaddr = ifra->ifra_dstaddr; 73353541Sshin /* link-local index check: should be a separate function? */ 73453541Sshin if (IN6_IS_ADDR_LINKLOCAL(&ia->ia_dstaddr.sin6_addr)) { 73553541Sshin if (ia->ia_dstaddr.sin6_addr.s6_addr16[1] == 0) { 73653541Sshin /* 73753541Sshin * interface ID is not embedded by 73853541Sshin * the user 73953541Sshin */ 74053541Sshin ia->ia_dstaddr.sin6_addr.s6_addr16[1] 74153541Sshin = htons(ifp->if_index); 74253541Sshin } else 74353541Sshin if (ia->ia_dstaddr.sin6_addr.s6_addr16[1] != 74453541Sshin htons(ifp->if_index)) { 74553541Sshin ia->ia_dstaddr = oldaddr; 74653541Sshin return(EINVAL); /* ifid is contradict */ 74753541Sshin } 74853541Sshin } 74953541Sshin prefixIsNew = 1; /* We lie; but effect's the same */ 75053541Sshin } 75153541Sshin if (ifra->ifra_addr.sin6_family == AF_INET6 && 75253541Sshin (hostIsNew || prefixIsNew)) 75353541Sshin error = in6_ifinit(ifp, ia, &ifra->ifra_addr, 0); 75453541Sshin if (ifra->ifra_addr.sin6_family == AF_INET6 75553541Sshin && hostIsNew && (ifp->if_flags & IFF_MULTICAST)) { 75653541Sshin int error_local = 0; 75753541Sshin 75853541Sshin /* 75953541Sshin * join solicited multicast addr for new host id 76053541Sshin */ 76153541Sshin struct in6_addr llsol; 76253541Sshin bzero(&llsol, sizeof(struct in6_addr)); 76353541Sshin llsol.s6_addr16[0] = htons(0xff02); 76453541Sshin llsol.s6_addr16[1] = htons(ifp->if_index); 76553541Sshin llsol.s6_addr32[1] = 0; 76653541Sshin llsol.s6_addr32[2] = htonl(1); 76753541Sshin llsol.s6_addr32[3] = 76853541Sshin ifra->ifra_addr.sin6_addr.s6_addr32[3]; 76953541Sshin llsol.s6_addr8[12] = 0xff; 77053541Sshin (void)in6_addmulti(&llsol, ifp, &error_local); 77153541Sshin if (error == 0) 77253541Sshin error = error_local; 77353541Sshin } 77453541Sshin /* Join dstaddr's solicited multicast if necessary. */ 77553541Sshin if (nd6_proxyall && hostIsNew) { 77653541Sshin int error_local; 77753541Sshin 77853541Sshin error_local = in6_ifaddproxy(ia); 77953541Sshin if (error == 0) 78053541Sshin error = error_local; 78153541Sshin } 78253541Sshin 78353541Sshin ia->ia6_flags = ifra->ifra_flags; 78453541Sshin ia->ia6_flags &= ~IN6_IFF_DUPLICATED; /*safety*/ 78553541Sshin 78653541Sshin ia->ia6_lifetime = ifra->ifra_lifetime; 78753541Sshin /* for sanity */ 78853541Sshin if (ia->ia6_lifetime.ia6t_vltime != ND6_INFINITE_LIFETIME) { 78953541Sshin ia->ia6_lifetime.ia6t_expire = 79053541Sshin time_second + ia->ia6_lifetime.ia6t_vltime; 79153541Sshin } else 79253541Sshin ia->ia6_lifetime.ia6t_expire = 0; 79353541Sshin if (ia->ia6_lifetime.ia6t_pltime != ND6_INFINITE_LIFETIME) { 79453541Sshin ia->ia6_lifetime.ia6t_preferred = 79553541Sshin time_second + ia->ia6_lifetime.ia6t_pltime; 79653541Sshin } else 79753541Sshin ia->ia6_lifetime.ia6t_preferred = 0; 79853541Sshin 79953541Sshin /* 80053541Sshin * Perform DAD, if needed. 80153541Sshin * XXX It may be of use, if we can administratively 80253541Sshin * disable DAD. 80353541Sshin */ 80453541Sshin switch (ifp->if_type) { 80553541Sshin case IFT_ARCNET: 80653541Sshin case IFT_ETHER: 80753541Sshin case IFT_FDDI: 80853541Sshin ia->ia6_flags |= IN6_IFF_TENTATIVE; 80953541Sshin nd6_dad_start((struct ifaddr *)ia, NULL); 81053541Sshin break; 81153541Sshin#ifdef IFT_DUMMY 81253541Sshin case IFT_DUMMY: 81353541Sshin#endif 81453541Sshin case IFT_FAITH: 81553541Sshin case IFT_GIF: 81653541Sshin case IFT_LOOP: 81753541Sshin default: 81853541Sshin break; 81953541Sshin } 82053541Sshin 82153541Sshin if (hostIsNew) { 82253541Sshin int iilen; 82353541Sshin int error_local = 0; 82453541Sshin 82553541Sshin iilen = (sizeof(ia->ia_prefixmask.sin6_addr) << 3) - 82653541Sshin in6_mask2len(&ia->ia_prefixmask.sin6_addr); 82753541Sshin error_local = in6_prefix_add_ifid(iilen, ia); 82853541Sshin if (error == 0) 82953541Sshin error = error_local; 83053541Sshin } 83153541Sshin 83253541Sshin return(error); 83353541Sshin 83453541Sshin case SIOCDIFADDR_IN6: 83553541Sshin in6_ifscrub(ifp, ia); 83653541Sshin 83753541Sshin if (ifp->if_flags & IFF_MULTICAST) { 83853541Sshin /* 83953541Sshin * delete solicited multicast addr for deleting host id 84053541Sshin */ 84153541Sshin struct in6_multi *in6m; 84253541Sshin struct in6_addr llsol; 84353541Sshin bzero(&llsol, sizeof(struct in6_addr)); 84453541Sshin llsol.s6_addr16[0] = htons(0xff02); 84553541Sshin llsol.s6_addr16[1] = htons(ifp->if_index); 84653541Sshin llsol.s6_addr32[1] = 0; 84753541Sshin llsol.s6_addr32[2] = htonl(1); 84853541Sshin llsol.s6_addr32[3] = 84953541Sshin ia->ia_addr.sin6_addr.s6_addr32[3]; 85053541Sshin llsol.s6_addr8[12] = 0xff; 85153541Sshin 85253541Sshin IN6_LOOKUP_MULTI(llsol, ifp, in6m); 85353541Sshin if (in6m) 85453541Sshin in6_delmulti(in6m); 85553541Sshin } 85653541Sshin /* Leave dstaddr's solicited multicast if necessary. */ 85753541Sshin if (nd6_proxyall) 85853541Sshin in6_ifremproxy(ia); 85953541Sshin 86053541Sshin TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list); 86153541Sshin oia = ia; 86253541Sshin if (oia == (ia = in6_ifaddr)) 86353541Sshin in6_ifaddr = ia->ia_next; 86453541Sshin else { 86553541Sshin while (ia->ia_next && (ia->ia_next != oia)) 86653541Sshin ia = ia->ia_next; 86753541Sshin if (ia->ia_next) 86853541Sshin ia->ia_next = oia->ia_next; 86953541Sshin else 87053541Sshin printf("Didn't unlink in6_ifaddr from list\n"); 87153541Sshin } 87253541Sshin { 87353541Sshin int iilen; 87453541Sshin 87553541Sshin iilen = (sizeof(oia->ia_prefixmask.sin6_addr) << 3) - 87653541Sshin in6_mask2len(&oia->ia_prefixmask.sin6_addr); 87753541Sshin in6_prefix_remove_ifid(iilen, oia); 87853541Sshin } 87953541Sshin IFAFREE((&oia->ia_ifa)); 88053541Sshin break; 88153541Sshin 88253541Sshin default: 88353541Sshin if (ifp == 0 || ifp->if_ioctl == 0) 88453541Sshin return(EOPNOTSUPP); 88553541Sshin return((*ifp->if_ioctl)(ifp, cmd, data)); 88653541Sshin } 88753541Sshin return(0); 88853541Sshin} 88953541Sshin 89053541Sshin/* 89153541Sshin * SIOC[GAD]LIFADDR. 89253541Sshin * SIOCGLIFADDR: get first address. (???) 89353541Sshin * SIOCGLIFADDR with IFLR_PREFIX: 89453541Sshin * get first address that matches the specified prefix. 89553541Sshin * SIOCALIFADDR: add the specified address. 89653541Sshin * SIOCALIFADDR with IFLR_PREFIX: 89753541Sshin * add the specified prefix, filling hostid part from 89853541Sshin * the first link-local address. prefixlen must be <= 64. 89953541Sshin * SIOCDLIFADDR: delete the specified address. 90053541Sshin * SIOCDLIFADDR with IFLR_PREFIX: 90153541Sshin * delete the first address that matches the specified prefix. 90253541Sshin * return values: 90353541Sshin * EINVAL on invalid parameters 90453541Sshin * EADDRNOTAVAIL on prefix match failed/specified address not found 90553541Sshin * other values may be returned from in6_ioctl() 90653541Sshin * 90753541Sshin * NOTE: SIOCALIFADDR(with IFLR_PREFIX set) allows prefixlen less than 64. 90853541Sshin * this is to accomodate address naming scheme other than RFC2374, 90953541Sshin * in the future. 91053541Sshin * RFC2373 defines interface id to be 64bit, but it allows non-RFC2374 91153541Sshin * address encoding scheme. (see figure on page 8) 91253541Sshin */ 91353541Sshinstatic int 91453541Sshinin6_lifaddr_ioctl(so, cmd, data, ifp, p) 91553541Sshin struct socket *so; 91653541Sshin u_long cmd; 91753541Sshin caddr_t data; 91853541Sshin struct ifnet *ifp; 91953541Sshin struct proc *p; 92053541Sshin{ 92153541Sshin struct if_laddrreq *iflr = (struct if_laddrreq *)data; 92253541Sshin struct ifaddr *ifa; 92353541Sshin 92453541Sshin /* sanity checks */ 92553541Sshin if (!data || !ifp) { 92653541Sshin panic("invalid argument to in6_lifaddr_ioctl"); 92753541Sshin /*NOTRECHED*/ 92853541Sshin } 92953541Sshin 93053541Sshin switch (cmd) { 93153541Sshin case SIOCGLIFADDR: 93253541Sshin /* address must be specified on GET with IFLR_PREFIX */ 93353541Sshin if ((iflr->flags & IFLR_PREFIX) == 0) 93453541Sshin break; 93553541Sshin /*FALLTHROUGH*/ 93653541Sshin case SIOCALIFADDR: 93753541Sshin case SIOCDLIFADDR: 93853541Sshin /* address must be specified on ADD and DELETE */ 93953541Sshin if (iflr->addr.__ss_family != AF_INET6) 94053541Sshin return EINVAL; 94153541Sshin if (iflr->addr.__ss_len != sizeof(struct sockaddr_in6)) 94253541Sshin return EINVAL; 94353541Sshin /* XXX need improvement */ 94453541Sshin if (iflr->dstaddr.__ss_family 94553541Sshin && iflr->dstaddr.__ss_family != AF_INET6) 94653541Sshin return EINVAL; 94753541Sshin if (iflr->dstaddr.__ss_family 94853541Sshin && iflr->dstaddr.__ss_len != sizeof(struct sockaddr_in6)) 94953541Sshin return EINVAL; 95053541Sshin break; 95153541Sshin default: /*shouldn't happen*/ 95253541Sshin return EOPNOTSUPP; 95353541Sshin } 95453541Sshin if (sizeof(struct in6_addr) * 8 < iflr->prefixlen) 95553541Sshin return EINVAL; 95653541Sshin 95753541Sshin switch (cmd) { 95853541Sshin case SIOCALIFADDR: 95953541Sshin { 96053541Sshin struct in6_aliasreq ifra; 96153541Sshin struct in6_addr *hostid = NULL; 96253541Sshin int prefixlen; 96353541Sshin 96453541Sshin if ((iflr->flags & IFLR_PREFIX) != 0) { 96553541Sshin struct sockaddr_in6 *sin6; 96653541Sshin 96753541Sshin /* 96853541Sshin * hostid is to fill in the hostid part of the 96953541Sshin * address. hostid points to the first link-local 97053541Sshin * address attached to the interface. 97153541Sshin */ 97253541Sshin ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp); 97353541Sshin if (!ifa) 97453541Sshin return EADDRNOTAVAIL; 97553541Sshin hostid = IFA_IN6(ifa); 97653541Sshin 97753541Sshin /* prefixlen must be <= 64. */ 97853541Sshin if (64 < iflr->prefixlen) 97953541Sshin return EINVAL; 98053541Sshin prefixlen = iflr->prefixlen; 98153541Sshin 98253541Sshin /* hostid part must be zero. */ 98353541Sshin sin6 = (struct sockaddr_in6 *)&iflr->addr; 98453541Sshin if (sin6->sin6_addr.s6_addr32[2] != 0 98553541Sshin || sin6->sin6_addr.s6_addr32[3] != 0) { 98653541Sshin return EINVAL; 98753541Sshin } 98853541Sshin } else 98953541Sshin prefixlen = iflr->prefixlen; 99053541Sshin 99153541Sshin /* copy args to in6_aliasreq, perform ioctl(SIOCAIFADDR_IN6). */ 99253541Sshin bzero(&ifra, sizeof(ifra)); 99353541Sshin bcopy(iflr->iflr_name, ifra.ifra_name, 99453541Sshin sizeof(ifra.ifra_name)); 99553541Sshin 99653541Sshin bcopy(&iflr->addr, &ifra.ifra_addr, iflr->addr.__ss_len); 99753541Sshin if (hostid) { 99853541Sshin /* fill in hostid part */ 99953541Sshin ifra.ifra_addr.sin6_addr.s6_addr32[2] = 100053541Sshin hostid->s6_addr32[2]; 100153541Sshin ifra.ifra_addr.sin6_addr.s6_addr32[3] = 100253541Sshin hostid->s6_addr32[3]; 100353541Sshin } 100453541Sshin 100553541Sshin if (iflr->dstaddr.__ss_family) { /*XXX*/ 100653541Sshin bcopy(&iflr->dstaddr, &ifra.ifra_dstaddr, 100753541Sshin iflr->dstaddr.__ss_len); 100853541Sshin if (hostid) { 100953541Sshin ifra.ifra_dstaddr.sin6_addr.s6_addr32[2] = 101053541Sshin hostid->s6_addr32[2]; 101153541Sshin ifra.ifra_dstaddr.sin6_addr.s6_addr32[3] = 101253541Sshin hostid->s6_addr32[3]; 101353541Sshin } 101453541Sshin } 101553541Sshin 101653541Sshin ifra.ifra_prefixmask.sin6_family = AF_INET6; 101753541Sshin ifra.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6); 101853541Sshin in6_len2mask(&ifra.ifra_prefixmask.sin6_addr, prefixlen); 101953541Sshin 102053541Sshin ifra.ifra_flags = iflr->flags & ~IFLR_PREFIX; 102153541Sshin return in6_control(so, SIOCAIFADDR_IN6, (caddr_t)&ifra, ifp, p); 102253541Sshin } 102353541Sshin case SIOCGLIFADDR: 102453541Sshin case SIOCDLIFADDR: 102553541Sshin { 102653541Sshin struct in6_ifaddr *ia; 102753541Sshin struct in6_addr mask, candidate, match; 102853541Sshin struct sockaddr_in6 *sin6; 102953541Sshin int cmp; 103053541Sshin 103153541Sshin bzero(&mask, sizeof(mask)); 103253541Sshin if (iflr->flags & IFLR_PREFIX) { 103353541Sshin /* lookup a prefix rather than address. */ 103453541Sshin in6_len2mask(&mask, iflr->prefixlen); 103553541Sshin 103653541Sshin sin6 = (struct sockaddr_in6 *)&iflr->addr; 103753541Sshin bcopy(&sin6->sin6_addr, &match, sizeof(match)); 103853541Sshin match.s6_addr32[0] &= mask.s6_addr32[0]; 103953541Sshin match.s6_addr32[1] &= mask.s6_addr32[1]; 104053541Sshin match.s6_addr32[2] &= mask.s6_addr32[2]; 104153541Sshin match.s6_addr32[3] &= mask.s6_addr32[3]; 104253541Sshin 104353541Sshin /* if you set extra bits, that's wrong */ 104453541Sshin if (bcmp(&match, &sin6->sin6_addr, sizeof(match))) 104553541Sshin return EINVAL; 104653541Sshin 104753541Sshin cmp = 1; 104853541Sshin } else { 104953541Sshin if (cmd == SIOCGLIFADDR) { 105053541Sshin /* on getting an address, take the 1st match */ 105153541Sshin cmp = 0; /*XXX*/ 105253541Sshin } else { 105353541Sshin /* on deleting an address, do exact match */ 105453541Sshin in6_len2mask(&mask, 128); 105553541Sshin sin6 = (struct sockaddr_in6 *)&iflr->addr; 105653541Sshin bcopy(&sin6->sin6_addr, &match, sizeof(match)); 105753541Sshin 105853541Sshin cmp = 1; 105953541Sshin } 106053541Sshin } 106153541Sshin 106253541Sshin for (ifa = ifp->if_addrlist.tqh_first; 106353541Sshin ifa; 106453541Sshin ifa = ifa->ifa_list.tqe_next) 106553541Sshin { 106653541Sshin if (ifa->ifa_addr->sa_family != AF_INET6) 106753541Sshin continue; 106853541Sshin if (!cmp) 106953541Sshin break; 107053541Sshin bcopy(IFA_IN6(ifa), &candidate, sizeof(candidate)); 107153541Sshin candidate.s6_addr32[0] &= mask.s6_addr32[0]; 107253541Sshin candidate.s6_addr32[1] &= mask.s6_addr32[1]; 107353541Sshin candidate.s6_addr32[2] &= mask.s6_addr32[2]; 107453541Sshin candidate.s6_addr32[3] &= mask.s6_addr32[3]; 107553541Sshin if (IN6_ARE_ADDR_EQUAL(&candidate, &match)) 107653541Sshin break; 107753541Sshin } 107853541Sshin if (!ifa) 107953541Sshin return EADDRNOTAVAIL; 108053541Sshin ia = ifa2ia6(ifa); 108153541Sshin 108253541Sshin if (cmd == SIOCGLIFADDR) { 108353541Sshin /* fill in the if_laddrreq structure */ 108453541Sshin bcopy(&ia->ia_addr, &iflr->addr, ia->ia_addr.sin6_len); 108553541Sshin 108653541Sshin if ((ifp->if_flags & IFF_POINTOPOINT) != 0) { 108753541Sshin bcopy(&ia->ia_dstaddr, &iflr->dstaddr, 108853541Sshin ia->ia_dstaddr.sin6_len); 108953541Sshin } else 109053541Sshin bzero(&iflr->dstaddr, sizeof(iflr->dstaddr)); 109153541Sshin 109253541Sshin iflr->prefixlen = 109353541Sshin in6_mask2len(&ia->ia_prefixmask.sin6_addr); 109453541Sshin 109553541Sshin iflr->flags = ia->ia6_flags; /*XXX*/ 109653541Sshin 109753541Sshin return 0; 109853541Sshin } else { 109953541Sshin struct in6_aliasreq ifra; 110053541Sshin 110153541Sshin /* fill in6_aliasreq and do ioctl(SIOCDIFADDR_IN6) */ 110253541Sshin bzero(&ifra, sizeof(ifra)); 110353541Sshin bcopy(iflr->iflr_name, ifra.ifra_name, 110453541Sshin sizeof(ifra.ifra_name)); 110553541Sshin 110653541Sshin bcopy(&ia->ia_addr, &ifra.ifra_addr, 110753541Sshin ia->ia_addr.sin6_len); 110853541Sshin if ((ifp->if_flags & IFF_POINTOPOINT) != 0) { 110953541Sshin bcopy(&ia->ia_dstaddr, &ifra.ifra_dstaddr, 111053541Sshin ia->ia_dstaddr.sin6_len); 111153541Sshin } 111253541Sshin bcopy(&ia->ia_prefixmask, &ifra.ifra_dstaddr, 111353541Sshin ia->ia_prefixmask.sin6_len); 111453541Sshin 111553541Sshin ifra.ifra_flags = ia->ia6_flags; 111653541Sshin return in6_control(so, SIOCDIFADDR_IN6, (caddr_t)&ifra, 111753541Sshin ifp, p); 111853541Sshin } 111953541Sshin } 112053541Sshin } 112153541Sshin 112253541Sshin return EOPNOTSUPP; /*just for safety*/ 112353541Sshin} 112453541Sshin 112553541Sshin/* 112653541Sshin * Delete any existing route for an interface. 112753541Sshin */ 112853541Sshinvoid 112953541Sshinin6_ifscrub(ifp, ia) 113053541Sshin register struct ifnet *ifp; 113153541Sshin register struct in6_ifaddr *ia; 113253541Sshin{ 113353541Sshin if ((ia->ia_flags & IFA_ROUTE) == 0) 113453541Sshin return; 113553541Sshin if (ifp->if_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) 113653541Sshin rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST); 113753541Sshin else 113853541Sshin rtinit(&(ia->ia_ifa), (int)RTM_DELETE, 0); 113953541Sshin ia->ia_flags &= ~IFA_ROUTE; 114053541Sshin 114153541Sshin /* Remove ownaddr's loopback rtentry, if it exists. */ 114253541Sshin in6_ifremloop(&(ia->ia_ifa)); 114353541Sshin} 114453541Sshin 114553541Sshin/* 114653541Sshin * Initialize an interface's intetnet6 address 114753541Sshin * and routing table entry. 114853541Sshin */ 114953541Sshinint 115053541Sshinin6_ifinit(ifp, ia, sin6, scrub) 115153541Sshin struct ifnet *ifp; 115253541Sshin struct in6_ifaddr *ia; 115353541Sshin struct sockaddr_in6 *sin6; 115453541Sshin int scrub; 115553541Sshin{ 115653541Sshin struct sockaddr_in6 oldaddr; 115753541Sshin int error, flags = RTF_UP; 115853541Sshin int s = splimp(); 115953541Sshin 116053541Sshin oldaddr = ia->ia_addr; 116153541Sshin ia->ia_addr = *sin6; 116253541Sshin /* 116353541Sshin * Give the interface a chance to initialize 116453541Sshin * if this is its first address, 116553541Sshin * and to validate the address if necessary. 116653541Sshin */ 116753541Sshin if (ifp->if_ioctl && 116853541Sshin (error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia))) { 116953541Sshin splx(s); 117053541Sshin ia->ia_addr = oldaddr; 117153541Sshin return(error); 117253541Sshin } 117353541Sshin 117453541Sshin switch (ifp->if_type) { 117553541Sshin case IFT_ARCNET: 117653541Sshin case IFT_ETHER: 117753541Sshin case IFT_FDDI: 117853541Sshin ia->ia_ifa.ifa_rtrequest = nd6_rtrequest; 117953541Sshin ia->ia_ifa.ifa_flags |= RTF_CLONING; 118053541Sshin break; 118153541Sshin case IFT_PPP: 118253541Sshin ia->ia_ifa.ifa_rtrequest = nd6_p2p_rtrequest; 118353541Sshin ia->ia_ifa.ifa_flags |= RTF_CLONING; 118453541Sshin break; 118553541Sshin } 118653541Sshin 118753541Sshin splx(s); 118853541Sshin if (scrub) { 118953541Sshin ia->ia_ifa.ifa_addr = (struct sockaddr *)&oldaddr; 119053541Sshin in6_ifscrub(ifp, ia); 119153541Sshin ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; 119253541Sshin } 119353541Sshin /* xxx 119453541Sshin * in_socktrim 119553541Sshin */ 119653541Sshin /* 119753541Sshin * Add route for the network. 119853541Sshin */ 119953541Sshin ia->ia_ifa.ifa_metric = ifp->if_metric; 120053541Sshin if (ifp->if_flags & IFF_LOOPBACK) { 120153541Sshin ia->ia_ifa.ifa_dstaddr = ia->ia_ifa.ifa_addr; 120253541Sshin flags |= RTF_HOST; 120353541Sshin } else if (ifp->if_flags & IFF_POINTOPOINT) { 120453541Sshin if (ia->ia_dstaddr.sin6_family != AF_INET6) 120553541Sshin return(0); 120653541Sshin flags |= RTF_HOST; 120753541Sshin } 120853541Sshin if ((error = rtinit(&(ia->ia_ifa), (int)RTM_ADD, flags)) == 0) 120953541Sshin ia->ia_flags |= IFA_ROUTE; 121053541Sshin 121153541Sshin /* Add ownaddr as loopback rtentry, if necessary(ex. on p2p link). */ 121253541Sshin in6_ifaddloop(&(ia->ia_ifa)); 121353541Sshin 121453541Sshin return(error); 121553541Sshin} 121653541Sshin 121753541Sshin/* 121853541Sshin * Add an address to the list of IP6 multicast addresses for a 121953541Sshin * given interface. 122053541Sshin */ 122153541Sshinstruct in6_multi * 122253541Sshinin6_addmulti(maddr6, ifp, errorp) 122353541Sshin register struct in6_addr *maddr6; 122453541Sshin register struct ifnet *ifp; 122553541Sshin int *errorp; 122653541Sshin{ 122753541Sshin struct in6_multi *in6m; 122853541Sshin struct sockaddr_in6 sin6; 122953541Sshin struct ifmultiaddr *ifma; 123053541Sshin int s = splnet(); 123153541Sshin 123253541Sshin *errorp = 0; 123353541Sshin 123453541Sshin /* 123553541Sshin * Call generic routine to add membership or increment 123653541Sshin * refcount. It wants addresses in the form of a sockaddr, 123753541Sshin * so we build one here (being careful to zero the unused bytes). 123853541Sshin */ 123953541Sshin bzero(&sin6, sizeof sin6); 124053541Sshin sin6.sin6_family = AF_INET6; 124153541Sshin sin6.sin6_len = sizeof sin6; 124253541Sshin sin6.sin6_addr = *maddr6; 124353541Sshin *errorp = if_addmulti(ifp, (struct sockaddr *)&sin6, &ifma); 124453541Sshin if (*errorp) { 124553541Sshin splx(s); 124653541Sshin return 0; 124753541Sshin } 124853541Sshin 124953541Sshin /* 125053541Sshin * If ifma->ifma_protospec is null, then if_addmulti() created 125153541Sshin * a new record. Otherwise, we are done. 125253541Sshin */ 125353541Sshin if (ifma->ifma_protospec != 0) 125453541Sshin return ifma->ifma_protospec; 125553541Sshin 125653541Sshin /* XXX - if_addmulti uses M_WAITOK. Can this really be called 125753541Sshin at interrupt time? If so, need to fix if_addmulti. XXX */ 125853541Sshin in6m = (struct in6_multi *)malloc(sizeof(*in6m), M_IPMADDR, M_NOWAIT); 125953541Sshin if (in6m == NULL) { 126053541Sshin splx(s); 126153541Sshin return (NULL); 126253541Sshin } 126353541Sshin 126453541Sshin bzero(in6m, sizeof *in6m); 126553541Sshin in6m->in6m_addr = *maddr6; 126653541Sshin in6m->in6m_ifp = ifp; 126753541Sshin in6m->in6m_ifma = ifma; 126853541Sshin ifma->ifma_protospec = in6m; 126953541Sshin LIST_INSERT_HEAD(&in6_multihead, in6m, in6m_entry); 127053541Sshin 127153541Sshin /* 127253541Sshin * Let MLD6 know that we have joined a new IP6 multicast 127353541Sshin * group. 127453541Sshin */ 127553541Sshin mld6_start_listening(in6m); 127653541Sshin splx(s); 127753541Sshin return(in6m); 127853541Sshin} 127953541Sshin 128053541Sshin/* 128153541Sshin * Delete a multicast address record. 128253541Sshin */ 128353541Sshinvoid 128453541Sshinin6_delmulti(in6m) 128553541Sshin struct in6_multi *in6m; 128653541Sshin{ 128753541Sshin struct ifmultiaddr *ifma = in6m->in6m_ifma; 128853541Sshin int s = splnet(); 128953541Sshin 129053541Sshin if (ifma->ifma_refcount == 1) { 129153541Sshin /* 129253541Sshin * No remaining claims to this record; let MLD6 know 129353541Sshin * that we are leaving the multicast group. 129453541Sshin */ 129553541Sshin mld6_stop_listening(in6m); 129653541Sshin ifma->ifma_protospec = 0; 129753541Sshin LIST_REMOVE(in6m, in6m_entry); 129853541Sshin free(in6m, M_IPMADDR); 129953541Sshin } 130053541Sshin /* XXX - should be separate API for when we have an ifma? */ 130153541Sshin if_delmulti(ifma->ifma_ifp, ifma->ifma_addr); 130253541Sshin splx(s); 130353541Sshin} 130453541Sshin 130553541Sshin/* 130653541Sshin * Find an IPv6 interface link-local address specific to an interface. 130753541Sshin */ 130853541Sshinstruct in6_ifaddr * 130953541Sshinin6ifa_ifpforlinklocal(ifp) 131053541Sshin struct ifnet *ifp; 131153541Sshin{ 131253541Sshin register struct ifaddr *ifa; 131353541Sshin 131453541Sshin for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next) 131553541Sshin { 131653541Sshin if (ifa->ifa_addr == NULL) 131753541Sshin continue; /* just for safety */ 131853541Sshin if (ifa->ifa_addr->sa_family != AF_INET6) 131953541Sshin continue; 132053541Sshin if (IN6_IS_ADDR_LINKLOCAL(IFA_IN6(ifa))) 132153541Sshin break; 132253541Sshin } 132353541Sshin 132453541Sshin return((struct in6_ifaddr *)ifa); 132553541Sshin} 132653541Sshin 132753541Sshin 132853541Sshin/* 132953541Sshin * find the internet address corresponding to a given interface and address. 133053541Sshin */ 133153541Sshinstruct in6_ifaddr * 133253541Sshinin6ifa_ifpwithaddr(ifp, addr) 133353541Sshin struct ifnet *ifp; 133453541Sshin struct in6_addr *addr; 133553541Sshin{ 133653541Sshin register struct ifaddr *ifa; 133753541Sshin 133853541Sshin for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next) 133953541Sshin { 134053541Sshin if (ifa->ifa_addr == NULL) 134153541Sshin continue; /* just for safety */ 134253541Sshin if (ifa->ifa_addr->sa_family != AF_INET6) 134353541Sshin continue; 134453541Sshin if (IN6_ARE_ADDR_EQUAL(addr, IFA_IN6(ifa))) 134553541Sshin break; 134653541Sshin } 134753541Sshin 134853541Sshin return((struct in6_ifaddr *)ifa); 134953541Sshin} 135053541Sshin 135153541Sshin/* 135253541Sshin * Convert IP6 address to printable (loggable) representation. 135353541Sshin */ 135453541Sshinstatic char digits[] = "0123456789abcdef"; 135553541Sshinstatic int ip6round = 0; 135653541Sshinchar * 135753541Sshinip6_sprintf(addr) 135853541Sshinregister struct in6_addr *addr; 135953541Sshin{ 136053541Sshin static char ip6buf[8][48]; 136153541Sshin register int i; 136253541Sshin register char *cp; 136353541Sshin register u_short *a = (u_short *)addr; 136453541Sshin register u_char *d; 136553541Sshin int dcolon = 0; 136653541Sshin 136753541Sshin ip6round = (ip6round + 1) & 7; 136853541Sshin cp = ip6buf[ip6round]; 136953541Sshin 137053541Sshin for (i = 0; i < 8; i++) { 137153541Sshin if (dcolon == 1) { 137253541Sshin if (*a == 0) { 137353541Sshin if (i == 7) 137453541Sshin *cp++ = ':'; 137553541Sshin a++; 137653541Sshin continue; 137753541Sshin } else 137853541Sshin dcolon = 2; 137953541Sshin } 138053541Sshin if (*a == 0) { 138153541Sshin if (dcolon == 0 && *(a + 1) == 0) { 138253541Sshin if (i == 0) 138353541Sshin *cp++ = ':'; 138453541Sshin *cp++ = ':'; 138553541Sshin dcolon = 1; 138653541Sshin } else { 138753541Sshin *cp++ = '0'; 138853541Sshin *cp++ = ':'; 138953541Sshin } 139053541Sshin a++; 139153541Sshin continue; 139253541Sshin } 139353541Sshin d = (u_char *)a; 139453541Sshin *cp++ = digits[*d >> 4]; 139553541Sshin *cp++ = digits[*d++ & 0xf]; 139653541Sshin *cp++ = digits[*d >> 4]; 139753541Sshin *cp++ = digits[*d & 0xf]; 139853541Sshin *cp++ = ':'; 139953541Sshin a++; 140053541Sshin } 140153541Sshin *--cp = 0; 140253541Sshin return(ip6buf[ip6round]); 140353541Sshin} 140453541Sshin 140553541Sshinint 140653541Sshinin6_localaddr(in6) 140753541Sshin struct in6_addr *in6; 140853541Sshin{ 140953541Sshin struct in6_ifaddr *ia; 141053541Sshin 141153541Sshin if (IN6_IS_ADDR_LOOPBACK(in6) || IN6_IS_ADDR_LINKLOCAL(in6)) 141253541Sshin return 1; 141353541Sshin 141453541Sshin for (ia = in6_ifaddr; ia; ia = ia->ia_next) 141553541Sshin if (IN6_ARE_MASKED_ADDR_EQUAL(in6, &ia->ia_addr.sin6_addr, 141653541Sshin &ia->ia_prefixmask.sin6_addr)) 141753541Sshin return 1; 141853541Sshin 141953541Sshin return (0); 142053541Sshin} 142153541Sshin 142253541Sshin/* 142353541Sshin * Get a scope of the address. Node-local, link-local, site-local or global. 142453541Sshin */ 142553541Sshinint 142653541Sshinin6_addrscope (addr) 142753541Sshinstruct in6_addr *addr; 142853541Sshin{ 142953541Sshin int scope; 143053541Sshin 143153541Sshin if (addr->s6_addr8[0] == 0xfe) { 143253541Sshin scope = addr->s6_addr8[1] & 0xc0; 143353541Sshin 143453541Sshin switch (scope) { 143553541Sshin case 0x80: 143653541Sshin return IPV6_ADDR_SCOPE_LINKLOCAL; 143753541Sshin break; 143853541Sshin case 0xc0: 143953541Sshin return IPV6_ADDR_SCOPE_SITELOCAL; 144053541Sshin break; 144153541Sshin default: 144253541Sshin return IPV6_ADDR_SCOPE_GLOBAL; /* just in case */ 144353541Sshin break; 144453541Sshin } 144553541Sshin } 144653541Sshin 144753541Sshin 144853541Sshin if (addr->s6_addr8[0] == 0xff) { 144953541Sshin scope = addr->s6_addr8[1] & 0x0f; 145053541Sshin 145153541Sshin /* 145253541Sshin * due to other scope such as reserved, 145353541Sshin * return scope doesn't work. 145453541Sshin */ 145553541Sshin switch (scope) { 145653541Sshin case IPV6_ADDR_SCOPE_NODELOCAL: 145753541Sshin return IPV6_ADDR_SCOPE_NODELOCAL; 145853541Sshin break; 145953541Sshin case IPV6_ADDR_SCOPE_LINKLOCAL: 146053541Sshin return IPV6_ADDR_SCOPE_LINKLOCAL; 146153541Sshin break; 146253541Sshin case IPV6_ADDR_SCOPE_SITELOCAL: 146353541Sshin return IPV6_ADDR_SCOPE_SITELOCAL; 146453541Sshin break; 146553541Sshin default: 146653541Sshin return IPV6_ADDR_SCOPE_GLOBAL; 146753541Sshin break; 146853541Sshin } 146953541Sshin } 147053541Sshin 147153541Sshin if (bcmp(&in6addr_loopback, addr, sizeof(addr) - 1) == 0) { 147253541Sshin if (addr->s6_addr8[15] == 1) /* loopback */ 147353541Sshin return IPV6_ADDR_SCOPE_NODELOCAL; 147453541Sshin if (addr->s6_addr8[15] == 0) /* unspecified */ 147553541Sshin return IPV6_ADDR_SCOPE_LINKLOCAL; 147653541Sshin } 147753541Sshin 147853541Sshin return IPV6_ADDR_SCOPE_GLOBAL; 147953541Sshin} 148053541Sshin 148153541Sshin/* 148253541Sshin * return length of part which dst and src are equal 148353541Sshin * hard coding... 148453541Sshin */ 148553541Sshin 148653541Sshinint 148753541Sshinin6_matchlen(src, dst) 148853541Sshinstruct in6_addr *src, *dst; 148953541Sshin{ 149053541Sshin int match = 0; 149153541Sshin u_char *s = (u_char *)src, *d = (u_char *)dst; 149253541Sshin u_char *lim = s + 16, r; 149353541Sshin 149453541Sshin while (s < lim) 149553541Sshin if ((r = (*d++ ^ *s++)) != 0) { 149653541Sshin while (r < 128) { 149753541Sshin match++; 149853541Sshin r <<= 1; 149953541Sshin } 150053541Sshin break; 150153541Sshin } else 150253541Sshin match += 8; 150353541Sshin return match; 150453541Sshin} 150553541Sshin 150653541Sshinint 150753541Sshinin6_are_prefix_equal(p1, p2, len) 150853541Sshin struct in6_addr *p1, *p2; 150953541Sshin int len; 151053541Sshin{ 151153541Sshin int bytelen, bitlen; 151253541Sshin 151353541Sshin /* sanity check */ 151453541Sshin if (0 > len || len > 128) { 151553541Sshin log(LOG_ERR, "in6_are_prefix_equal: invalid prefix length(%d)\n", 151653541Sshin len); 151753541Sshin return(0); 151853541Sshin } 151953541Sshin 152053541Sshin bytelen = len / 8; 152153541Sshin bitlen = len % 8; 152253541Sshin 152353541Sshin if (bcmp(&p1->s6_addr, &p2->s6_addr, bytelen)) 152453541Sshin return(0); 152553541Sshin if (p1->s6_addr[bytelen] >> (8 - bitlen) != 152653541Sshin p2->s6_addr[bytelen] >> (8 - bitlen)) 152753541Sshin return(0); 152853541Sshin 152953541Sshin return(1); 153053541Sshin} 153153541Sshin 153253541Sshinvoid 153353541Sshinin6_prefixlen2mask(maskp, len) 153453541Sshin struct in6_addr *maskp; 153553541Sshin int len; 153653541Sshin{ 153753541Sshin u_char maskarray[8] = {0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff}; 153853541Sshin int bytelen, bitlen, i; 153953541Sshin 154053541Sshin /* sanity check */ 154153541Sshin if (0 > len || len > 128) { 154253541Sshin log(LOG_ERR, "in6_prefixlen2mask: invalid prefix length(%d)\n", 154353541Sshin len); 154453541Sshin return; 154553541Sshin } 154653541Sshin 154753541Sshin bzero(maskp, sizeof(*maskp)); 154853541Sshin bytelen = len / 8; 154953541Sshin bitlen = len % 8; 155053541Sshin for (i = 0; i < bytelen; i++) 155153541Sshin maskp->s6_addr[i] = 0xff; 155253541Sshin if (bitlen) 155353541Sshin maskp->s6_addr[bytelen] = maskarray[bitlen - 1]; 155453541Sshin} 155553541Sshin 155653541Sshin/* 155753541Sshin * return the best address out of the same scope 155853541Sshin */ 155953541Sshin 156053541Sshinstruct in6_ifaddr * 156153541Sshinin6_ifawithscope(ifp, dst) 156253541Sshin register struct ifnet *ifp; 156353541Sshin register struct in6_addr *dst; 156453541Sshin{ 156553541Sshin int dst_scope = in6_addrscope(dst), blen = -1, tlen; 156653541Sshin struct ifaddr *ifa; 156753541Sshin struct in6_ifaddr *besta = NULL, *ia; 156853541Sshin struct in6_ifaddr *dep[2]; /*last-resort: deprecated*/ 156953541Sshin 157053541Sshin dep[0] = dep[1] = NULL; 157153541Sshin 157253541Sshin /* 157353541Sshin * We first look for addresses in the same scope. 157453541Sshin * If there is one, return it. 157553541Sshin * If two or more, return one which matches the dst longest. 157653541Sshin * If none, return one of global addresses assigned other ifs. 157753541Sshin */ 157853541Sshin for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next) 157953541Sshin { 158053541Sshin if (ifa->ifa_addr->sa_family != AF_INET6) 158153541Sshin continue; 158253541Sshin if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST) 158353541Sshin continue; /* XXX: is there any case to allow anycast? */ 158453541Sshin if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_NOTREADY) 158553541Sshin continue; /* don't use this interface */ 158653541Sshin if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DETACHED) 158753541Sshin continue; 158853541Sshin if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DEPRECATED) { 158953541Sshin if (ip6_use_deprecated) 159053541Sshin dep[0] = (struct in6_ifaddr *)ifa; 159153541Sshin continue; 159253541Sshin } 159353541Sshin 159453541Sshin if (dst_scope == in6_addrscope(IFA_IN6(ifa))) { 159553541Sshin /* 159653541Sshin * call in6_matchlen() as few as possible 159753541Sshin */ 159853541Sshin if (besta) { 159953541Sshin if (blen == -1) 160053541Sshin blen = in6_matchlen(&besta->ia_addr.sin6_addr, dst); 160153541Sshin tlen = in6_matchlen(IFA_IN6(ifa), dst); 160253541Sshin if (tlen > blen) { 160353541Sshin blen = tlen; 160453541Sshin besta = (struct in6_ifaddr *)ifa; 160553541Sshin } 160653541Sshin } else 160753541Sshin besta = (struct in6_ifaddr *)ifa; 160853541Sshin } 160953541Sshin } 161053541Sshin if (besta) 161153541Sshin return besta; 161253541Sshin 161353541Sshin for (ia = in6_ifaddr; ia; ia = ia->ia_next) { 161453541Sshin if (IPV6_ADDR_SCOPE_GLOBAL != 161553541Sshin in6_addrscope(&(ia->ia_addr.sin6_addr))) 161653541Sshin continue; 161753541Sshin /* XXX: is there any case to allow anycast? */ 161853541Sshin if ((ia->ia6_flags & IN6_IFF_ANYCAST) != 0) 161953541Sshin continue; 162053541Sshin if ((ia->ia6_flags & IN6_IFF_NOTREADY) != 0) 162153541Sshin continue; 162253541Sshin if ((ia->ia6_flags & IN6_IFF_DETACHED) != 0) 162353541Sshin continue; 162453541Sshin if ((ia->ia6_flags & IN6_IFF_DEPRECATED) != 0) { 162553541Sshin if (ip6_use_deprecated) 162653541Sshin dep[1] = (struct in6_ifaddr *)ifa; 162753541Sshin continue; 162853541Sshin } 162953541Sshin return ia; 163053541Sshin } 163153541Sshin 163253541Sshin /* use the last-resort values, that are, deprecated addresses */ 163353541Sshin if (dep[0]) 163453541Sshin return dep[0]; 163553541Sshin if (dep[1]) 163653541Sshin return dep[1]; 163753541Sshin 163853541Sshin return NULL; 163953541Sshin} 164053541Sshin 164153541Sshin/* 164253541Sshin * return the best address out of the same scope. if no address was 164353541Sshin * found, return the first valid address from designated IF. 164453541Sshin */ 164553541Sshin 164653541Sshinstruct in6_ifaddr * 164753541Sshinin6_ifawithifp(ifp, dst) 164853541Sshin register struct ifnet *ifp; 164953541Sshin register struct in6_addr *dst; 165053541Sshin{ 165153541Sshin int dst_scope = in6_addrscope(dst), blen = -1, tlen; 165253541Sshin struct ifaddr *ifa; 165353541Sshin struct in6_ifaddr *besta = 0; 165453541Sshin struct in6_ifaddr *dep[2]; /*last-resort: deprecated*/ 165553541Sshin 165653541Sshin dep[0] = dep[1] = NULL; 165753541Sshin 165853541Sshin /* 165953541Sshin * We first look for addresses in the same scope. 166053541Sshin * If there is one, return it. 166153541Sshin * If two or more, return one which matches the dst longest. 166253541Sshin * If none, return one of global addresses assigned other ifs. 166353541Sshin */ 166453541Sshin for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next) 166553541Sshin { 166653541Sshin if (ifa->ifa_addr->sa_family != AF_INET6) 166753541Sshin continue; 166853541Sshin if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST) 166953541Sshin continue; /* XXX: is there any case to allow anycast? */ 167053541Sshin if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_NOTREADY) 167153541Sshin continue; /* don't use this interface */ 167253541Sshin if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DETACHED) 167353541Sshin continue; 167453541Sshin if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DEPRECATED) { 167553541Sshin if (ip6_use_deprecated) 167653541Sshin dep[0] = (struct in6_ifaddr *)ifa; 167753541Sshin continue; 167853541Sshin } 167953541Sshin 168053541Sshin if (dst_scope == in6_addrscope(IFA_IN6(ifa))) { 168153541Sshin /* 168253541Sshin * call in6_matchlen() as few as possible 168353541Sshin */ 168453541Sshin if (besta) { 168553541Sshin if (blen == -1) 168653541Sshin blen = in6_matchlen(&besta->ia_addr.sin6_addr, dst); 168753541Sshin tlen = in6_matchlen(IFA_IN6(ifa), dst); 168853541Sshin if (tlen > blen) { 168953541Sshin blen = tlen; 169053541Sshin besta = (struct in6_ifaddr *)ifa; 169153541Sshin } 169253541Sshin } else 169353541Sshin besta = (struct in6_ifaddr *)ifa; 169453541Sshin } 169553541Sshin } 169653541Sshin if (besta) 169753541Sshin return(besta); 169853541Sshin 169953541Sshin for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next) 170053541Sshin { 170153541Sshin if (ifa->ifa_addr->sa_family != AF_INET6) 170253541Sshin continue; 170353541Sshin if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST) 170453541Sshin continue; /* XXX: is there any case to allow anycast? */ 170553541Sshin if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_NOTREADY) 170653541Sshin continue; /* don't use this interface */ 170753541Sshin if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DETACHED) 170853541Sshin continue; 170953541Sshin if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DEPRECATED) { 171053541Sshin if (ip6_use_deprecated) 171153541Sshin dep[1] = (struct in6_ifaddr *)ifa; 171253541Sshin continue; 171353541Sshin } 171453541Sshin 171553541Sshin return (struct in6_ifaddr *)ifa; 171653541Sshin } 171753541Sshin 171853541Sshin /* use the last-resort values, that are, deprecated addresses */ 171953541Sshin if (dep[0]) 172053541Sshin return dep[0]; 172153541Sshin if (dep[1]) 172253541Sshin return dep[1]; 172353541Sshin 172453541Sshin return NULL; 172553541Sshin} 172653541Sshin 172753541Sshin/* 172853541Sshin * perform DAD when interface becomes IFF_UP. 172953541Sshin */ 173053541Sshinvoid 173153541Sshinin6_if_up(ifp) 173253541Sshin struct ifnet *ifp; 173353541Sshin{ 173453541Sshin struct ifaddr *ifa; 173553541Sshin struct in6_ifaddr *ia; 173653541Sshin struct sockaddr_dl *sdl; 173753541Sshin int type; 173853541Sshin struct ether_addr ea; 173953541Sshin int off; 174053541Sshin int dad_delay; /* delay ticks before DAD output */ 174153541Sshin 174253541Sshin bzero(&ea, sizeof(ea)); 174353541Sshin sdl = NULL; 174453541Sshin 174553541Sshin for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next) 174653541Sshin { 174753541Sshin if (ifa->ifa_addr->sa_family == AF_INET6 174853541Sshin && IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr)) { 174953541Sshin goto dad; 175053541Sshin } 175153541Sshin if (ifa->ifa_addr->sa_family != AF_LINK) 175253541Sshin continue; 175353541Sshin sdl = (struct sockaddr_dl *)ifa->ifa_addr; 175453541Sshin break; 175553541Sshin } 175653541Sshin 175753541Sshin switch (ifp->if_type) { 175853541Sshin case IFT_SLIP: 175953541Sshin case IFT_PPP: 176053541Sshin#ifdef IFT_DUMMY 176153541Sshin case IFT_DUMMY: 176253541Sshin#endif 176353541Sshin case IFT_GIF: 176453541Sshin case IFT_FAITH: 176553541Sshin type = IN6_IFT_P2P; 176653541Sshin in6_ifattach(ifp, type, 0, 1); 176753541Sshin break; 176853541Sshin case IFT_ETHER: 176953541Sshin case IFT_FDDI: 177053541Sshin case IFT_ATM: 177153541Sshin type = IN6_IFT_802; 177253541Sshin if (sdl == NULL) 177353541Sshin break; 177453541Sshin off = sdl->sdl_nlen; 177553541Sshin if (bcmp(&sdl->sdl_data[off], &ea, sizeof(ea)) != 0) 177653541Sshin in6_ifattach(ifp, type, LLADDR(sdl), 0); 177753541Sshin break; 177853541Sshin case IFT_ARCNET: 177953541Sshin type = IN6_IFT_ARCNET; 178053541Sshin if (sdl == NULL) 178153541Sshin break; 178253541Sshin off = sdl->sdl_nlen; 178353541Sshin if (sdl->sdl_data[off] != 0) /* XXX ?: */ 178453541Sshin in6_ifattach(ifp, type, LLADDR(sdl), 0); 178553541Sshin break; 178653541Sshin default: 178753541Sshin break; 178853541Sshin } 178953541Sshin 179053541Sshindad: 179153541Sshin dad_delay = 0; 179253541Sshin for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next) 179353541Sshin { 179453541Sshin if (ifa->ifa_addr->sa_family != AF_INET6) 179553541Sshin continue; 179653541Sshin ia = (struct in6_ifaddr *)ifa; 179753541Sshin if (ia->ia6_flags & IN6_IFF_TENTATIVE) 179853541Sshin nd6_dad_start(ifa, &dad_delay); 179953541Sshin } 180053541Sshin} 180153541Sshin 180253541Sshin/* 180353541Sshin * Calculate max IPv6 MTU through all the interfaces and store it 180453541Sshin * to in6_maxmtu. 180553541Sshin */ 180653541Sshinvoid 180753541Sshinin6_setmaxmtu() 180853541Sshin{ 180953541Sshin unsigned long maxmtu = 0; 181053541Sshin struct ifnet *ifp; 181153541Sshin 181253541Sshin for (ifp = TAILQ_FIRST(&ifnet); ifp; ifp = TAILQ_NEXT(ifp, if_list)) 181353541Sshin { 181453541Sshin if ((ifp->if_flags & IFF_LOOPBACK) == 0 && 181553541Sshin nd_ifinfo[ifp->if_index].linkmtu > maxmtu) 181653541Sshin maxmtu = nd_ifinfo[ifp->if_index].linkmtu; 181753541Sshin } 181853541Sshin if (maxmtu) /* update only when maxmtu is positive */ 181953541Sshin in6_maxmtu = maxmtu; 182053541Sshin} 182153541Sshin 182253541Sshin/* 182353541Sshin * Convert sockaddr_in6 to sockaddr_in. Original sockaddr_in6 must be 182453541Sshin * v4 mapped addr or v4 compat addr 182553541Sshin */ 182653541Sshinvoid 182753541Sshinin6_sin6_2_sin(struct sockaddr_in *sin, struct sockaddr_in6 *sin6) 182853541Sshin{ 182953541Sshin bzero(sin, sizeof(*sin)); 183053541Sshin sin->sin_len = sizeof(struct sockaddr_in); 183153541Sshin sin->sin_family = AF_INET; 183253541Sshin sin->sin_port = sin6->sin6_port; 183353541Sshin sin->sin_addr.s_addr = sin6->sin6_addr.s6_addr32[3]; 183453541Sshin} 183553541Sshin 183653541Sshin/* Convert sockaddr_in to sockaddr_in6 in v4 mapped addr format. */ 183753541Sshinvoid 183853541Sshinin6_sin_2_v4mapsin6(struct sockaddr_in *sin, struct sockaddr_in6 *sin6) 183953541Sshin{ 184053541Sshin bzero(sin6, sizeof(*sin6)); 184153541Sshin sin6->sin6_len = sizeof(struct sockaddr_in6); 184253541Sshin sin6->sin6_family = AF_INET6; 184353541Sshin sin6->sin6_port = sin->sin_port; 184453541Sshin sin6->sin6_addr.s6_addr32[0] = 0; 184553541Sshin sin6->sin6_addr.s6_addr32[1] = 0; 184653541Sshin sin6->sin6_addr.s6_addr32[2] = IPV6_ADDR_INT32_SMP; 184753541Sshin sin6->sin6_addr.s6_addr32[3] = sin->sin_addr.s_addr; 184853541Sshin} 184953541Sshin 185053541Sshin/* Convert sockaddr_in6 into sockaddr_in. */ 185153541Sshinvoid 185253541Sshinin6_sin6_2_sin_in_sock(struct sockaddr *nam) 185353541Sshin{ 185453541Sshin struct sockaddr_in *sin_p; 185553541Sshin struct sockaddr_in6 sin6; 185653541Sshin 185753541Sshin /* 185853541Sshin * Save original sockaddr_in6 addr and convert it 185953541Sshin * to sockaddr_in. 186053541Sshin */ 186153541Sshin sin6 = *(struct sockaddr_in6 *)nam; 186253541Sshin sin_p = (struct sockaddr_in *)nam; 186353541Sshin in6_sin6_2_sin(sin_p, &sin6); 186453541Sshin} 186553541Sshin 186653541Sshin/* Convert sockaddr_in into sockaddr_in6 in v4 mapped addr format. */ 186753541Sshinvoid 186853541Sshinin6_sin_2_v4mapsin6_in_sock(struct sockaddr **nam) 186953541Sshin{ 187053541Sshin struct sockaddr_in *sin_p; 187153541Sshin struct sockaddr_in6 *sin6_p; 187253541Sshin 187353541Sshin MALLOC(sin6_p, struct sockaddr_in6 *, sizeof *sin6_p, M_SONAME, 187453541Sshin M_WAITOK); 187553541Sshin sin_p = (struct sockaddr_in *)*nam; 187653541Sshin in6_sin_2_v4mapsin6(sin_p, sin6_p); 187753541Sshin FREE(*nam, M_SONAME); 187853541Sshin *nam = (struct sockaddr *)sin6_p; 187953541Sshin} 1880