diff options
author | 1999-12-08 06:50:14 +0000 | |
---|---|---|
committer | 1999-12-08 06:50:14 +0000 | |
commit | 287546ea80ee896bda0c88b8a8c85a1dc6ff37f9 (patch) | |
tree | cef428e54b6d2bca56fb9b461aa0667c7fb5f6a2 /sys/netinet6/in6.c | |
parent | add GENERIC.v6 (IPv6 test configuration). to be integrated into GENREIC. (diff) | |
download | wireguard-openbsd-287546ea80ee896bda0c88b8a8c85a1dc6ff37f9.tar.xz wireguard-openbsd-287546ea80ee896bda0c88b8a8c85a1dc6ff37f9.zip |
bring in KAME IPv6 code, dated 19991208.
replaces NRL IPv6 layer. reuses NRL pcb layer. no IPsec-on-v6 support.
see sys/netinet6/{TODO,IMPLEMENTATION} for more details.
GENERIC configuration should work fine as before. GENERIC.v6 works fine
as well, but you'll need KAME userland tools to play with IPv6 (will be
bringed into soon).
Diffstat (limited to 'sys/netinet6/in6.c')
-rw-r--r-- | sys/netinet6/in6.c | 3009 |
1 files changed, 2166 insertions, 843 deletions
diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c index 6d2a1a0e50d..a3b74ae4157 100644 --- a/sys/netinet6/in6.c +++ b/sys/netinet6/in6.c @@ -1,942 +1,2265 @@ /* -%%% copyright-nrl-95 -This software is Copyright 1995-1998 by Randall Atkinson, Ronald Lee, -Daniel McDonald, Bao Phan, and Chris Winters. All Rights Reserved. All -rights under this copyright have been assigned to the US Naval Research -Laboratory (NRL). The NRL Copyright Notice and License Agreement Version -1.1 (January 17, 1995) applies to this software. -You should have received a copy of the license with this software. If you -didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>. + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ -*/ +/* + * Copyright (c) 1982, 1986, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)in.c 8.2 (Berkeley) 11/15/93 + */ + +#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined(__NetBSD__) +#include "opt_inet.h" +#endif #include <sys/param.h> +#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) #include <sys/ioctl.h> +#endif #include <sys/errno.h> #include <sys/malloc.h> #include <sys/socket.h> #include <sys/socketvar.h> +#include <sys/sockio.h> #include <sys/systm.h> -#if __NetBSD__ || __FreeBSD__ +#if !defined(__bsdi__) && !(defined(__FreeBSD__) && __FreeBSD__ < 3) #include <sys/proc.h> -#endif /* __NetBSD__ || __FreeBSD__ */ +#endif +#include <sys/time.h> +#include <sys/kernel.h> +#include <sys/syslog.h> #include <net/if.h> #include <net/if_types.h> -#include <net/if_dl.h> #include <net/route.h> +#include "gif.h" +#if NGIF > 0 +#include <net/if_gif.h> +#endif +#include <net/if_dl.h> #include <netinet/in.h> - -#include <netinet6/in6_var.h> -#include <netinet6/ipv6.h> -#include <netinet6/ipv6_var.h> -#include <netinet6/ipv6_icmp.h> - -#ifdef DEBUG_NRL -#include <sys/debug.h> -#else /* DEBUG_NRL */ -#if __OpenBSD__ -#include <netinet6/debug.h> -#else /* __OpenBSD__ */ -#include <sys/debug.h> -#endif /* __OpenBSD__ */ -#endif /* DEBUG_NRL */ +#include <netinet/in_var.h> +#ifdef __NetBSD__ +#include <net/if_ether.h> +#else +#include <netinet/if_ether.h> +#endif + +#include <netinet6/nd6.h> +#include <netinet6/ip6.h> +#include <netinet6/ip6_var.h> +#include <netinet6/mld6_var.h> +#include <netinet6/ip6_mroute.h> +#include <netinet6/in6_ifattach.h> + +#include <net/net_osdep.h> + +#if defined(__FreeBSD__) && __FreeBSD__ >= 3 +MALLOC_DEFINE(M_IPMADDR, "in6_multi", "internet multicast address"); +#endif /* - * Globals + * Definitions of some costant IP6 addresses. */ - -struct ifnet *mcastdefault = NULL; /* Should be changeable by sysctl(). */ - 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; + +#if !defined(__bsdi__) && !(defined(__FreeBSD__) && __FreeBSD__ < 3) +static int in6_lifaddr_ioctl __P((struct socket *, u_long, caddr_t, + struct ifnet *, struct proc *)); +#else +static int in6_lifaddr_ioctl __P((struct socket *, u_long, caddr_t, + struct ifnet *)); +#endif + +#if defined(__FreeBSD__) && __FreeBSD__ >= 3 +struct in6_multihead in6_multihead; /* XXX BSS initialization */ +#else /* - * External globals + * This structure is used to keep track of in6_multi chains which belong to + * deleted interface addresses. */ +static LIST_HEAD(, multi6_kludge) in6_mk; /* XXX BSS initialization */ -extern struct sockaddr_in6 in6_allones; -extern struct in6_ifaddr *in6_ifaddr; -extern struct in6_ifnet *in6_ifnet; -extern int ipv6forwarding; - -static void setmcastdef __P((register struct ifnet *)); -void del_in6_ifnet __P((struct ifnet *)); -struct in6_ifnet *add_in6_ifnet __P((struct ifnet *, int *)); -int in6_ifscrub __P((struct ifnet *, struct in6_ifaddr *)); -int in6_ifinit __P((register struct ifnet *, register struct in6_ifaddr *, struct sockaddr_in6 *, int, int)); -void addrconf_dad __P((struct in6_ifaddr *)); - -/*---------------------------------------------------------------------- - * Set the default multicast interface. In single-homed case, this will - * always be the non-loopback interface. In multi-homed cases, the function - * should be able to set one accordingly. The multicast route entry - * (ff00::/8) will have its rt_ifp point to this interface, and its rt_ifa - * point to whatever rtrequest() does. The rt_ifa should be more intelligently - * set eventually. - ----------------------------------------------------------------------*/ +struct multi6_kludge { + LIST_ENTRY(multi6_kludge) mk_entry; + struct ifnet *mk_ifp; + struct in6_multihead mk_head; +}; +#endif -static void -setmcastdef(ifp) - register struct ifnet *ifp; +/* + * Determine whether an IP6 address is in a reserved set of addresses + * that may not be forwarded, or whether datagrams to that destination + * may be forwarded. + */ +int +in6_canforward(src, dst) + struct in6_addr *src, *dst; { -#ifdef __FreeBSD__ -struct ifaddr *ifa = ifp->if_addrhead.tqh_first; -#else /* __FreeBSD__ */ -#if __NetBSD__ || __OpenBSD__ - struct ifaddr *ifa = ifp->if_addrlist.tqh_first; -#else /* __NetBSD__ || __OpenBSD__ */ - struct ifaddr *ifa = ifp->if_addrlist; -#endif /* __NetBSD__ || __OpenBSD__ */ -#endif /* __FreeBSD__ */ - struct sockaddr_dl lsdl; - struct sockaddr_in6 lsin6; - struct rtentry *newrt=NULL; - int s; + if (IN6_IS_ADDR_LINKLOCAL(src) || + IN6_IS_ADDR_LINKLOCAL(dst) || + IN6_IS_ADDR_MULTICAST(dst)) + return(0); + return(1); +} - if (ifp == mcastdefault) - return; +/* + * Check if the loopback entry will be automatically generated. + * if 0 returned, will not be automatically generated. + * if 1 returned, will be automatically generated. + */ +static int +in6_is_ifloop_auto(struct ifaddr *ifa) +{ +#ifdef __OpenBSD__ + return 0; +#else +#define SIN6(s) ((struct sockaddr_in6 *)s) + /* + * If RTF_CLONING is unset, or (IFF_LOOPBACK | IFF_POINTOPOINT), + * or netmask is all0 or all1, then cloning will not happen, + * then we can't rely on its loopback entry generation. + */ + if ((ifa->ifa_flags & RTF_CLONING) == 0 || + (ifa->ifa_ifp->if_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) || + (SIN6(ifa->ifa_netmask)->sin6_len == sizeof(struct sockaddr_in6) + && + IN6_ARE_ADDR_EQUAL(&SIN6(ifa->ifa_netmask)->sin6_addr, + &in6mask128)) || + ((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_len == 0) + return 0; + else + return 1; +#undef SIN6 +#endif +} - /* - * If NULL, nuke any mcast entry. - */ +/* + * 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 lo_sa; + struct sockaddr_in6 all1_sa; + struct rtentry *nrt = NULL; + + bzero(&lo_sa, sizeof(lo_sa)); + bzero(&all1_sa, sizeof(all1_sa)); + lo_sa.sin6_family = AF_INET6; + lo_sa.sin6_len = sizeof(struct sockaddr_in6); + all1_sa = lo_sa; + lo_sa.sin6_addr = in6addr_loopback; + all1_sa.sin6_addr = in6mask128; + + /* So we add or remove static loopback entry, here. */ + rtrequest(cmd, ifa->ifa_addr, + (struct sockaddr *)&lo_sa, + (struct sockaddr *)&all1_sa, + RTF_UP|RTF_HOST, &nrt); + + /* + * Make sure rt_ifa be equal to IFA, the second argument of the + * function. + * We need this because when we refer 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 && nrt && ifa != nrt->rt_ifa) { + nrt->rt_ifa->ifa_refcnt--; + ifa->ifa_refcnt++; + nrt->rt_ifa = ifa; + } + if (nrt) + nrt->rt_refcnt--; +} - /* - * Find link addr for ifp. - */ +/* + * Add ownaddr as loopback rtentry, if necessary(ex. on p2p link). + * Because, KAME needs loopback rtentry for ownaddr check in + * ip6_input(). + */ +static void +in6_ifaddloop(struct ifaddr *ifa) +{ + if (!in6_is_ifloop_auto(ifa)) { + struct rtentry *rt; - while (ifa != NULL && ifa->ifa_addr->sa_family != AF_LINK) + /* If there is no loopback entry, allocate one. */ + rt = rtalloc1(ifa->ifa_addr, 0 #ifdef __FreeBSD__ - ifa = ifa->ifa_link.tqe_next; -#else /* __FreeBSD__ */ -#if __NetBSD__ || __OpenBSD__ - ifa = ifa->ifa_list.tqe_next; -#else /* __NetBSD__ || __OpenBSD__ */ - ifa = ifa->ifa_next; -#endif /* __NetBSD__ || __OpenBSD__ */ + , 0 #endif /* __FreeBSD__ */ + ); + if (rt == 0 || (rt->rt_ifp->if_flags & IFF_LOOPBACK) == 0) + in6_ifloop_request(RTM_ADD, ifa); + if (rt) + rt->rt_refcnt--; + } +} - if (ifa == NULL) - panic("Can't find AF_LINK for new multicast default interface."); - - bcopy(ifa->ifa_addr,&lsdl,ifa->ifa_addr->sa_len); - DDO(IDL_EVENT,dump_smart_sockaddr((struct sockaddr *)&lsdl)); - lsdl.sdl_alen = 0; - lsdl.sdl_slen = 0; - lsdl.sdl_nlen = 0; - - /* - * Delete old route, and add new one. - */ - - bzero(&lsin6,sizeof(lsin6)); - lsin6.sin6_family = AF_INET6; - lsin6.sin6_len = sizeof(lsin6); - lsin6.sin6_addr.s6_addr[0]=0xff; - - /* Neat property, mask and value are identical! */ - - s = splnet(); - rtrequest(RTM_DELETE,(struct sockaddr *)&lsin6,NULL, - (struct sockaddr *)&lsin6,0,NULL); - /* - * - * NB: If we clone, we have mcast dests being on a route. - * Consider multihomed system with processes talking to the - * same mcast group, but out different interfaces. - * - * Also, the RTM_ADD will do its best to find a "source address" to stick - * in the rt_ifa field. (See ipv6_rtrequest.c for this code.) - */ - rtrequest(RTM_ADD,(struct sockaddr *)&lsin6,(struct sockaddr *)&lsdl, - (struct sockaddr *)&lsin6,0,&newrt); - if (newrt == NULL) - panic("Assigning default multicast if."); - newrt->rt_rmx.rmx_mtu = ifp->if_mtu; - newrt->rt_refcnt--; - mcastdefault = ifp; - splx(s); -} - -/*---------------------------------------------------------------------- - * Delete an "IPv6 interface". Only called inside splnet(). - ----------------------------------------------------------------------*/ +/* + * Remove loopback rtentry of ownaddr generated by in6_ifaddloop(), + * if it exists. + */ +static void +in6_ifremloop(struct ifaddr *ifa) +{ + if (!in6_is_ifloop_auto(ifa)) { + struct in6_ifaddr *ia; + int ia_count = 0; + + /* If only one ifa for the loopback entry, delete it. */ + for (ia = in6_ifaddr; ia; ia = ia->ia_next) { + if (IN6_ARE_ADDR_EQUAL(IFA_IN6(ifa), + &ia->ia_addr.sin6_addr)) { + ia_count++; + if (ia_count > 1) + break; + } + } + if (ia_count == 1) + in6_ifloop_request(RTM_DELETE, ifa); + } +} -void -del_in6_ifnet(ifp) - struct ifnet *ifp; +/* + * Subroutine for in6_ifaddproxy() and in6_ifremproxy(). + * This routine does actual work. + * call in6_addmulti() when cmd == 1. + * call in6_delmulti() when cmd == 2. + */ +static int +in6_ifproxy_request(int cmd, struct in6_ifaddr *ia) { - struct in6_ifnet *i6ifp,*prev = NULL; + int error = 0; + + /* + * If we have an IPv6 dstaddr on adding p2p interface, + * join dstaddr's solicited multicast on necessary interface. + */ + if ((ia->ia_ifp->if_flags & IFF_POINTOPOINT) && + ia->ia_dstaddr.sin6_family == AF_INET6 && + !IN6_IS_ADDR_LINKLOCAL(&ia->ia_dstaddr.sin6_addr)) { + struct in6_ifaddr *ia_lan; + + /* + * TODO: Join only on some specified interfaces by some + * configuration. + * Unsolicited Neighbor Advertisements will be also necessary. + * + * Now, join on interfaces which meets following. + * -IFF_BROADCAST and IFF_MULTICAST + * (NBMA is out of scope) + * -the prefix value is same as p2p dstaddr + */ + for (ia_lan = in6_ifaddr; ia_lan; ia_lan = ia_lan->ia_next) { + struct in6_addr llsol; + + if ((ia_lan->ia_ifp->if_flags & + (IFF_BROADCAST|IFF_MULTICAST)) != + (IFF_BROADCAST|IFF_MULTICAST)) + continue; + if (!IN6_ARE_MASKED_ADDR_EQUAL(IA6_IN6(ia), + IA6_IN6(ia_lan), + IA6_MASKIN6(ia_lan))) + continue; + if (ia_lan->ia_ifp == ia->ia_ifp) + continue; + + /* init llsol */ + bzero(&llsol, sizeof(struct in6_addr)); + llsol.s6_addr16[0] = htons(0xff02); + llsol.s6_addr16[1] = htons(ia_lan->ia_ifp->if_index); + llsol.s6_addr32[1] = 0; + llsol.s6_addr32[2] = htonl(1); + llsol.s6_addr32[3] = + ia->ia_dstaddr.sin6_addr.s6_addr32[3]; + llsol.s6_addr8[12] = 0xff; + + if (cmd == 1) + (void)in6_addmulti(&llsol, + ia_lan->ia_ifp, + &error); + else if (cmd == 2) { + struct in6_multi *in6m; + + IN6_LOOKUP_MULTI(llsol, + ia_lan->ia_ifp, + in6m); + if (in6m) + in6_delmulti(in6m); + } + } + } + return error; +} - for (i6ifp = in6_ifnet; i6ifp != NULL; i6ifp = i6ifp->i6ifp_next) - { - if (i6ifp->i6ifp_ifp == ifp) - break; - prev = i6ifp; - } +static int +in6_ifaddproxy(struct in6_ifaddr *ia) +{ + return(in6_ifproxy_request(1, ia)); +} - if (i6ifp == NULL) - panic("Ooooh boy, consistency mismatch in del_in6_ifnet!"); +static void +in6_ifremproxy(struct in6_ifaddr *ia) +{ + in6_ifproxy_request(2, ia); +} - if (--(i6ifp->i6ifp_numaddrs) == 0) - { - while (i6ifp->i6ifp_multiaddrs != NULL) - { - i6ifp->i6ifp_multiaddrs->in6m_refcount = 1; - in6_delmulti(i6ifp->i6ifp_multiaddrs); - } - if (prev == NULL) - in6_ifnet = i6ifp->i6ifp_next; - else prev->i6ifp_next = i6ifp->i6ifp_next; - free(i6ifp,M_I6IFP); - } -} - -/*---------------------------------------------------------------------- - * Add a new "IPv6 interface". Only called inside splnet(). - * Perhaps send router adverts when this gets called. For now, they - * are issued when duplicate address detection succeeds on link-locals. - * See ipv6_addrconf.c for details. - ----------------------------------------------------------------------*/ - -struct in6_ifnet * -add_in6_ifnet(ifp, new) - struct ifnet *ifp; /* Assume an in6_ifaddr with this ifp is already - allocated and linked into the master list. */ - int *new; /* XXX */ -{ - struct in6_ifnet *i6ifp; - - *new = 0; - for (i6ifp = in6_ifnet; i6ifp != NULL; i6ifp = i6ifp->i6ifp_next) - if (i6ifp->i6ifp_ifp == ifp) - break; - - if (i6ifp == NULL) - { - i6ifp = malloc(sizeof(*i6ifp),M_I6IFP,M_NOWAIT); - if (i6ifp == NULL) +int +in6_ifindex2scopeid(idx) + int idx; +{ + struct ifnet *ifp; + struct ifaddr *ifa; + struct sockaddr_in6 *sin6; + + if (idx < 0 || if_index < idx) + return -1; + ifp = ifindex2ifnet[idx]; + +#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3) + for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) +#else + for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next) +#endif { - printf("DANGER! Malloc for i6ifp failed.\n"); - return NULL; + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; + if (IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr)) + return sin6->sin6_scope_id & 0xffff; } - i6ifp->i6ifp_ifp = ifp; - i6ifp->i6ifp_multiaddrs = NULL; - i6ifp->i6ifp_numaddrs = 1; - /* Other inits... */ - i6ifp->i6ifp_next = in6_ifnet; - in6_ifnet = i6ifp; - *new = 1; - } - return i6ifp; + return -1; } -/*---------------------------------------------------------------------- - * This function is called by the PRU_CONTROL handlers in both TCP and UDP. - * (Actually raw_ipv6 might need a PRU_CONTROL handler, but raw_ip doesn't - * have one.) - ----------------------------------------------------------------------*/ +int +in6_mask2len(mask) + struct in6_addr *mask; +{ + int x, y; + + for (x = 0; x < sizeof(*mask); x++) { + if (mask->s6_addr8[x] != 0xff) + break; + } + y = 0; + if (x < sizeof(*mask)) { + for (y = 0; y < 8; y++) { + if ((mask->s6_addr8[x] & (0x80 >> y)) == 0) + break; + } + } + return x * 8 + y; +} + +void +in6_len2mask(mask, len) + struct in6_addr *mask; + int len; +{ + int i; + + bzero(mask, sizeof(*mask)); + for (i = 0; i < len / 8; i++) + mask->s6_addr8[i] = 0xff; + if (len % 8) + mask->s6_addr8[i] = (0xff00 >> (len % 8)) & 0xff; +} + +int in6_interfaces; /* number of external internet interfaces */ + +#define ifa2ia6(ifa) ((struct in6_ifaddr *)(ifa)) +#define ia62ifa(ia6) ((struct ifaddr *)(ia6)) int -#if __NetBSD__ || __FreeBSD__ -in6_control(so, cmd, data, ifp, internal, p) -#else /* __NetBSD__ || __FreeBSD__ */ -in6_control(so, cmd, data, ifp, internal) -#endif /* __NetBSD__ || __FreeBSD__ */ - struct socket *so; -#if __NetBSD__ - u_long cmd; -#else /* __NetBSD__ */ - int cmd; -#endif /* __NetBSD__ */ - caddr_t data; - register struct ifnet *ifp; - int internal; -#if __NetBSD__ || __FreeBSD__ - struct proc *p; -#endif /* __NetBSD__ || __FreeBSD__ */ -{ - register struct inet6_ifreq *ifr = (struct inet6_ifreq *)data; - register struct in6_ifaddr *i6a = 0; - struct in6_ifaddr *oi6a; - struct inet6_aliasreq *ifra = (struct inet6_aliasreq *)data; - struct sockaddr_in6 oldaddr; - int error, hostIsNew, maskIsNew, ifnetIsNew = 0; -#if !__NetBSD__ && !__OpenBSD__ && !__FreeBSD__ - struct ifaddr *ifa; -#endif /* !__NetBSD__ && !__OpenBSD__ && !__FreeBSD__ */ - - /* - * If given an interface, find first IPv6 address on that interface. - * I may want to change how this is searched. I also may want to - * discriminate between link-local, site-local, v4-compatible, etc. - * - * This is used by the SIOCGIFADDR_INET6, and other such things. - * Those ioctls() currently assume only one IPv6 address on an interface. - * This is not a good assumption, and this code will have to be modified - * to correct that assumption. - */ - if (ifp) - for (i6a = in6_ifaddr; i6a; i6a = i6a->i6a_next) - if (i6a->i6a_ifp == ifp) - break; - - switch (cmd) - { - case SIOCAIFADDR_INET6: - case SIOCDIFADDR_INET6: - case SIOCVIFADDR_INET6: - /* - * For adding and deleting an address, find an exact match for - * that address. Note that ifr_addr and ifra_addr are in the same - * place, so even though VIFADDR uses a different struct than AIFADDR, - * the match will still occur. - */ - if (ifra->ifra_addr.sin6_family == AF_INET6 && - (cmd != SIOCDIFADDR_INET6 || - !IN6_IS_ADDR_UNSPECIFIED(&ifra->ifra_addr.sin6_addr))) - for (oi6a = i6a; i6a; i6a = i6a->i6a_next) - { - if (i6a->i6a_ifp == ifp && - IN6_ARE_ADDR_EQUAL(&i6a->i6a_addr.sin6_addr, &ifra->ifra_addr.sin6_addr)) - break; /* Out of for loop. */ - } - - /* - * You can't delete what you don't have... - */ - if (cmd == SIOCDIFADDR_INET6 && i6a == 0) - return EADDRNOTAVAIL; - - /* - * User program requests verification of address. No harm done in - * letting ANY program use this ioctl(), so we put code in for it - * here. - * - * If I found the i6a, check if I'm not sure. Return EWOULDBLOCK if - * not sure, return 0 if sure. Return EADDRNOTAVAIL if not available - * (i.e. DAD failed.). - */ - if (cmd == SIOCVIFADDR_INET6) { - if (i6a == NULL) { - return EADDRNOTAVAIL; - } else { - if (i6a->i6a_addrflags & I6AF_NOTSURE) { - return EWOULDBLOCK; - } else { - return 0; - } - } - } - - /* FALLTHROUGH TO... */ - - case SIOCSIFDSTADDR_INET6: -#if __NetBSD__ || __FreeBSD__ - if (p == 0 || (error = suser(p->p_ucred, &p->p_acflag)) ) -#else /* __NetBSD__ || __FreeBSD__ */ - if ((so->so_state & SS_PRIV) == 0) -#endif /* __NetBSD__ || __FreeBSD__ */ - return EPERM; - - if (ifp==0) - panic("in6_control, ifp==0"); - if (i6a == NULL) +#if !defined(__bsdi__) && !(defined(__FreeBSD__) && __FreeBSD__ < 3) +in6_control(so, cmd, data, ifp, p) + struct socket *so; + u_long cmd; + caddr_t data; + struct ifnet *ifp; + struct proc *p; +#else +in6_control(so, cmd, data, ifp) + struct socket *so; + u_long cmd; + caddr_t data; + struct ifnet *ifp; +#endif +{ + struct in6_ifreq *ifr = (struct in6_ifreq *)data; +#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3) + struct ifaddr *ifa; +#endif + struct in6_ifaddr *ia, *oia; + struct in6_aliasreq *ifra = (struct in6_aliasreq *)data; + struct sockaddr_in6 oldaddr, net; + int error = 0, hostIsNew, prefixIsNew; +#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) + time_t time_second = (time_t)time.tv_sec; +#endif + int privileged; + + privileged = 0; +#if defined(__NetBSD__) || (defined(__FreeBSD__) && __FreeBSD__ >= 3) + if (p && !suser(p->p_ucred, &p->p_acflag)) + privileged++; +#else + if ((so->so_state & SS_PRIV) != 0) + privileged++; +#endif + + /* + * xxx should prevent processes for link-local addresses? + */ +#if NGIF > 0 + if (ifp && ifp->if_type == IFT_GIF) { + switch (cmd) { + case SIOCSIFPHYADDR_IN6: + if (!privileged) + return(EPERM); + /*fall through*/ + case SIOCGIFPSRCADDR_IN6: + case SIOCGIFPDSTADDR_IN6: + return gif_ioctl(ifp, cmd, data); + } + } +#endif + switch (cmd) { + case SIOCGETSGCNT_IN6: + case SIOCGETMIFCNT_IN6: + return (mrt6_ioctl(cmd, data)); + } + + if (ifp == 0) + return(EOPNOTSUPP); + + switch (cmd) { + case SIOCSNDFLUSH_IN6: + case SIOCSPFXFLUSH_IN6: + case SIOCSRTRFLUSH_IN6: + if (!privileged) + return(EPERM); + /*fall through*/ + case SIOCGIFINFO_IN6: + case SIOCGDRLST_IN6: + case SIOCGPRLST_IN6: + case SIOCGNBRINFO_IN6: + return(nd6_ioctl(cmd, data, ifp)); + } + + switch (cmd) { + case SIOCSIFPREFIX_IN6: + case SIOCDIFPREFIX_IN6: + case SIOCAIFPREFIX_IN6: + case SIOCCIFPREFIX_IN6: + case SIOCSGIFPREFIX_IN6: + if (!privileged) + return(EPERM); + /*fall through*/ + case SIOCGIFPREFIX_IN6: + return(in6_prefix_ioctl(so, cmd, data, ifp)); + } + + switch (cmd) { + case SIOCALIFADDR: + case SIOCDLIFADDR: + if (!privileged) + return(EPERM); + /*fall through*/ + case SIOCGLIFADDR: +#if !defined(__bsdi__) && !(defined(__FreeBSD__) && __FreeBSD__ < 3) + return in6_lifaddr_ioctl(so, cmd, data, ifp, p); +#else + return in6_lifaddr_ioctl(so, cmd, data, ifp); +#endif + } + + /* + * Find address for this interface, if it exists. + */ { - struct in6_ifaddr *tmp; - - /* - * Create new in6_ifaddr (IPv6 interface address) for additions - * and destination settings. - */ - if (!(tmp = (struct in6_ifaddr *)malloc(sizeof(struct in6_ifaddr), - M_IFADDR,M_NOWAIT))) + + 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) { + /* interface 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); /* ifid is contradict */ + 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? */ + } + } + } +#if 0 + if (ifra->ifra_addr.sin6_family == AF_INET6) { + ia = in6ifa_ifpwithaddr(ifp, &ifra->ifra_addr.sin6_addr); + } +#else + ia = in6ifa_ifpwithaddr(ifp, &ifra->ifra_addr.sin6_addr); +#endif + + switch (cmd) { + + case SIOCDIFADDR_IN6: + if (ia == 0) + return(EADDRNOTAVAIL); + /* FALLTHROUGH */ + case SIOCAIFADDR_IN6: + case SIOCSIFADDR_IN6: + case SIOCSIFNETMASK_IN6: + case SIOCSIFDSTADDR_IN6: + if (!privileged) + return(EPERM); + if (ia == 0) { + ia = (struct in6_ifaddr *) + malloc(sizeof(*ia), M_IFADDR, M_WAITOK); + if (ia == NULL) + return (ENOBUFS); + bzero((caddr_t)ia, sizeof(*ia)); + ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; + ia->ia_ifa.ifa_dstaddr + = (struct sockaddr *)&ia->ia_dstaddr; + ia->ia_ifa.ifa_netmask + = (struct sockaddr *)&ia->ia_prefixmask; + + ia->ia_ifp = ifp; + if ((oia = in6_ifaddr) != NULL) { + for ( ; oia->ia_next; oia = oia->ia_next) + continue; + oia->ia_next = ia; + } else + in6_ifaddr = ia; +#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3) + if ((ifa = ifp->if_addrlist) != NULL) { + for ( ; ifa->ifa_next; ifa = ifa->ifa_next) + continue; + ifa->ifa_next = ia62ifa(ia); + } else + ifp->if_addrlist = ia62ifa(ia); +#else + TAILQ_INSERT_TAIL(&ifp->if_addrlist, + (struct ifaddr *)ia, ifa_list); +#endif + if ((ifp->if_flags & IFF_LOOPBACK) == 0) + in6_interfaces++; /*XXX*/ + } + + if (cmd == SIOCAIFADDR_IN6) { + /* sanity for overflow - beware unsigned */ + struct in6_addrlifetime *lt; + lt = &ifra->ifra_lifetime; + if (lt->ia6t_vltime != ND6_INFINITE_LIFETIME + && lt->ia6t_vltime + time_second < time_second) { + return EINVAL; + } + if (lt->ia6t_pltime != ND6_INFINITE_LIFETIME + && lt->ia6t_pltime + time_second < time_second) { + return EINVAL; + } + } + break; + + case SIOCGIFADDR_IN6: + /* This interface is basically deprecated. use SIOCGIFCONF. */ + /* fall through */ + case SIOCGIFAFLAG_IN6: + case SIOCGIFNETMASK_IN6: + case SIOCGIFDSTADDR_IN6: + case SIOCGIFALIFETIME_IN6: + /* must think again about its semantics */ + if (ia == 0) + return(EADDRNOTAVAIL); + break; + case SIOCSIFALIFETIME_IN6: { - return ENOBUFS; + struct in6_addrlifetime *lt; + + if (!privileged) + return(EPERM); + if (ia == 0) + 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); + 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; + if (in6_ifstat == NULL || ifp->if_index >= in6_ifstatmax + || in6_ifstat[ifp->if_index] == NULL) { + /* return EAFNOSUPPORT? */ + bzero(&ifr->ifr_ifru.ifru_stat, + sizeof(ifr->ifr_ifru.ifru_stat)); + } else + ifr->ifr_ifru.ifru_stat = *in6_ifstat[ifp->if_index]; + break; + + case SIOCGIFSTAT_ICMP6: + if (ifp == NULL) + return EINVAL; + if (icmp6_ifstat == NULL || ifp->if_index >= icmp6_ifstatmax || + icmp6_ifstat[ifp->if_index] == NULL) { + /* return EAFNOSUPPORT? */ + bzero(&ifr->ifr_ifru.ifru_stat, + sizeof(ifr->ifr_ifru.ifru_icmp6stat)); + } else + ifr->ifr_ifru.ifru_icmp6stat = + *icmp6_ifstat[ifp->if_index]; + break; + + case SIOCSIFDSTADDR_IN6: + if ((ifp->if_flags & IFF_POINTOPOINT) == 0) + return(EINVAL); + oldaddr = ia->ia_dstaddr; + ia->ia_dstaddr = ifr->ifr_dstaddr; + + /* link-local index check */ + if (IN6_IS_ADDR_LINKLOCAL(&ia->ia_dstaddr.sin6_addr)) { + if (ia->ia_dstaddr.sin6_addr.s6_addr16[1] == 0) { + /* interface ID is not embedded by the user */ + ia->ia_dstaddr.sin6_addr.s6_addr16[1] + = htons(ifp->if_index); + } + else + if (ia->ia_dstaddr.sin6_addr.s6_addr16[1] != + htons(ifp->if_index)) { + ia->ia_dstaddr = oldaddr; + return(EINVAL); /* ifid is contradict */ + } + } + + if (ifp->if_ioctl && (error = (ifp->if_ioctl) + (ifp, SIOCSIFDSTADDR, (caddr_t)ia))) { + ia->ia_dstaddr = oldaddr; + return(error); + } + if (ia->ia_flags & IFA_ROUTE) { + ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&oldaddr; + rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST); + ia->ia_ifa.ifa_dstaddr = + (struct sockaddr *)&ia->ia_dstaddr; + rtinit(&(ia->ia_ifa), (int)RTM_ADD, RTF_HOST|RTF_UP); + } + 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 SIOCSIFADDR_IN6: + return(in6_ifinit(ifp, ia, &ifr->ifr_addr, 1)); + + case SIOCSIFNETMASK_IN6: + ia->ia_prefixmask = ifr->ifr_addr; + bzero(&net, sizeof(net)); + net.sin6_len = sizeof(struct sockaddr_in6); + net.sin6_family = AF_INET6; + net.sin6_port = htons(0); + net.sin6_flowinfo = htonl(0); + net.sin6_addr.s6_addr32[0] + = ia->ia_addr.sin6_addr.s6_addr32[0] & + ia->ia_prefixmask.sin6_addr.s6_addr32[0]; + net.sin6_addr.s6_addr32[1] + = ia->ia_addr.sin6_addr.s6_addr32[1] & + ia->ia_prefixmask.sin6_addr.s6_addr32[1]; + net.sin6_addr.s6_addr32[2] + = ia->ia_addr.sin6_addr.s6_addr32[2] & + ia->ia_prefixmask.sin6_addr.s6_addr32[2]; + net.sin6_addr.s6_addr32[3] + = ia->ia_addr.sin6_addr.s6_addr32[3] & + ia->ia_prefixmask.sin6_addr.s6_addr32[3]; + ia->ia_net = net; + break; + + case SIOCAIFADDR_IN6: + prefixIsNew = 0; + hostIsNew = 1; + + if (ifra->ifra_addr.sin6_len == 0) { + ifra->ifra_addr = ia->ia_addr; + hostIsNew = 0; + } else if (IN6_ARE_ADDR_EQUAL(&ifra->ifra_addr.sin6_addr, + &ia->ia_addr.sin6_addr)) + hostIsNew = 0; + + if (ifra->ifra_prefixmask.sin6_len) { + in6_ifscrub(ifp, ia); + ia->ia_prefixmask = ifra->ifra_prefixmask; + prefixIsNew = 1; + } + if ((ifp->if_flags & IFF_POINTOPOINT) && + (ifra->ifra_dstaddr.sin6_family == AF_INET6)) { + in6_ifscrub(ifp, ia); + ia->ia_dstaddr = ifra->ifra_dstaddr; + /* link-local index check: should be a separate function? */ + if (IN6_IS_ADDR_LINKLOCAL(&ia->ia_dstaddr.sin6_addr)) { + if (ia->ia_dstaddr.sin6_addr.s6_addr16[1] == 0) { + /* + * interface ID is not embedded by + * the user + */ + ia->ia_dstaddr.sin6_addr.s6_addr16[1] + = htons(ifp->if_index); + } + else + if (ia->ia_dstaddr.sin6_addr.s6_addr16[1] != + htons(ifp->if_index)) { + ia->ia_dstaddr = oldaddr; + return(EINVAL); /* ifid is contradict */ + } + } + prefixIsNew = 1; /* We lie; but effect's the same */ + } + if (ifra->ifra_addr.sin6_family == AF_INET6 && + (hostIsNew || prefixIsNew)) + error = in6_ifinit(ifp, ia, &ifra->ifra_addr, 0); + if (ifra->ifra_addr.sin6_family == AF_INET6 + && hostIsNew && (ifp->if_flags & IFF_MULTICAST)) { + int error_local = 0; + + /* + * 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_local); + if (error == 0) + error = error_local; + } + /* Join dstaddr's solicited multicast if necessary. */ + if (nd6_proxyall && hostIsNew) { + int error_local; + + error_local = in6_ifaddproxy(ia); + if (error == 0) + error = error_local; + } + + ia->ia6_flags = ifra->ifra_flags; + ia->ia6_flags &= ~IN6_IFF_DUPLICATED; /*safety*/ + + 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. + */ + switch (ifp->if_type) { + case IFT_ARCNET: + case IFT_ETHER: + case IFT_FDDI: +#if 0 + case IFT_ATM: + case IFT_SLIP: + case IFT_PPP: +#endif + ia->ia6_flags |= IN6_IFF_TENTATIVE; + nd6_dad_start((struct ifaddr *)ia, NULL); + break; + case IFT_DUMMY: + case IFT_FAITH: + case IFT_GIF: + case IFT_LOOP: + default: + break; + } + + if (hostIsNew) { + int iilen; + int error_local = 0; + + iilen = (sizeof(ia->ia_prefixmask.sin6_addr) << 3) - + in6_mask2len(&ia->ia_prefixmask.sin6_addr); + error_local = in6_prefix_add_ifid(iilen, ia); + if (error == 0) + error = error_local; + } + + return(error); + + case SIOCDIFADDR_IN6: + in6_ifscrub(ifp, ia); + + 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 dstaddr's solicited multicast if necessary. */ + if (nd6_proxyall) + in6_ifremproxy(ia); + +#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3) + if ((ifa = ifp->if_addrlist) == ia62ifa(ia)) + ifp->if_addrlist = ifa->ifa_next; + else { + while (ifa->ifa_next && + (ifa->ifa_next != ia62ifa(ia))) + ifa = ifa->ifa_next; + if (ifa->ifa_next) + ifa->ifa_next = ia62ifa(ia)->ifa_next; + else + printf("Couldn't unlink in6_ifaddr from ifp\n"); + } +#else + TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list); +#endif + 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 + printf("Didn't unlink in6_ifaddr from list\n"); + } + { + int iilen; + + iilen = (sizeof(oia->ia_prefixmask.sin6_addr) << 3) - + in6_mask2len(&oia->ia_prefixmask.sin6_addr); + in6_prefix_remove_ifid(iilen, oia); + } +#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) + if (oia->ia6_multiaddrs.lh_first == NULL) { + IFAFREE(&oia->ia_ifa); + break; + } + else + in6_savemkludge(oia); +#endif + + IFAFREE((&oia->ia_ifa)); + break; + + default: + if (ifp == 0 || ifp->if_ioctl == 0) + return(EOPNOTSUPP); + return((*ifp->if_ioctl)(ifp, cmd, data)); + } + return(0); +} + +/* + * 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 +#if !defined(__bsdi__) && !(defined(__FreeBSD__) && __FreeBSD__ < 3) +in6_lifaddr_ioctl(so, cmd, data, ifp, p) + struct socket *so; + u_long cmd; + caddr_t data; + struct ifnet *ifp; + struct proc *p; +#else +in6_lifaddr_ioctl(so, cmd, data, ifp) + struct socket *so; + u_long cmd; + caddr_t data; + struct ifnet *ifp; +#endif +{ + 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"); + /*NOTRECHED*/ + } - bzero(tmp,sizeof(struct in6_ifaddr)); - /* - * Set NOTSURE addrflag before putting in list. - */ - tmp->i6a_addrflags = I6AF_NOTSURE; - if ((i6a = in6_ifaddr)) + 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: { - for (; i6a->i6a_next; i6a=i6a->i6a_next) - ; - i6a->i6a_next = tmp; + 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); + 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_family = AF_INET6; + ifra.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6); + in6_len2mask(&ifra.ifra_prefixmask.sin6_addr, prefixlen); + + ifra.ifra_flags = iflr->flags & ~IFLR_PREFIX; +#if !defined(__bsdi__) && !(defined(__FreeBSD__) && __FreeBSD__ < 3) + return in6_control(so, SIOCAIFADDR_IN6, (caddr_t)&ifra, ifp, p); +#else + return in6_control(so, SIOCAIFADDR_IN6, (caddr_t)&ifra, ifp); +#endif } - else in6_ifaddr = tmp; - i6a = tmp; -#ifdef __FreeBSD__ - TAILQ_INSERT_TAIL(&ifp->if_addrhead, (struct ifaddr *)i6a, - ifa_link); -#else /* __FreeBSD__ */ -#if __NetBSD__ || __OpenBSD__ - TAILQ_INSERT_TAIL(&ifp->if_addrlist, (struct ifaddr *)i6a, - ifa_list); -#else /* __NetBSD__ || __OpenBSD__ */ - if (ifa = ifp->if_addrlist) + case SIOCGLIFADDR: + case SIOCDLIFADDR: { - for (; ifa->ifa_next; ifa=ifa->ifa_next) - ; - ifa->ifa_next = (struct ifaddr *)i6a; + 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_len2mask(&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_len2mask(&mask, 128); + sin6 = (struct sockaddr_in6 *)&iflr->addr; + bcopy(&sin6->sin6_addr, &match, sizeof(match)); + + cmp = 1; + } + } + +#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3) + for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) +#else + for (ifa = ifp->if_addrlist.tqh_first; + ifa; + ifa = ifa->ifa_list.tqe_next) +#endif + { + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + if (!cmp) + break; + bcopy(IFA_IN6(ifa), &candidate, sizeof(candidate)); + candidate.s6_addr32[0] &= mask.s6_addr32[0]; + candidate.s6_addr32[1] &= mask.s6_addr32[1]; + 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) { + /* fill in the if_laddrreq structure */ + bcopy(&ia->ia_addr, &iflr->addr, ia->ia_addr.sin6_len); + + if ((ifp->if_flags & IFF_POINTOPOINT) != 0) { + bcopy(&ia->ia_dstaddr, &iflr->dstaddr, + ia->ia_dstaddr.sin6_len); + } else + bzero(&iflr->dstaddr, sizeof(iflr->dstaddr)); + + iflr->prefixlen = + in6_mask2len(&ia->ia_prefixmask.sin6_addr); + + 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); + } + bcopy(&ia->ia_prefixmask, &ifra.ifra_dstaddr, + ia->ia_prefixmask.sin6_len); + + ifra.ifra_flags = ia->ia6_flags; +#if !defined(__bsdi__) && !(defined(__FreeBSD__) && __FreeBSD__ < 3) + return in6_control(so, SIOCDIFADDR_IN6, (caddr_t)&ifra, + ifp, p); +#else + return in6_control(so, SIOCDIFADDR_IN6, (caddr_t)&ifra, + ifp); +#endif + } } - else ifp->if_addrlist = (struct ifaddr *)i6a; -#endif /* __NetBSD__ || __OpenBSD__ */ -#endif /* __FreeBSD__ */ - i6a->i6a_ifa.ifa_addr = (struct sockaddr *)&i6a->i6a_addr; - i6a->i6a_ifa.ifa_dstaddr = (struct sockaddr *)&i6a->i6a_dstaddr; - i6a->i6a_ifa.ifa_netmask - = (struct sockaddr *)&i6a->i6a_sockmask; - i6a->i6a_sockmask.sin6_len = sizeof(struct sockaddr_in6); - i6a->i6a_ifp = ifp; - - /* - * Add address to IPv6 interface lists. - */ - i6a->i6a_i6ifp = add_in6_ifnet(ifp, &ifnetIsNew); - } - break; - case SIOCGIFADDR_INET6: - case SIOCGIFNETMASK_INET6: - case SIOCGIFDSTADDR_INET6: - /* - * Can't get information on what is not there... - */ - if (i6a == NULL) - return EADDRNOTAVAIL; - break; - - default: - return EOPNOTSUPP; - } - - switch (cmd) - { - /* - * The following three cases assume that there is only one address per - * interface; this is not good in IPv6-land. Unfortunately, the - * ioctl() interface, is such that I'll have to rewrite the way things - * work here, either that, or curious user programs will have to troll - * /dev/kmem (like netstat(8) does). - */ - case SIOCGIFADDR_INET6: - bcopy(&(i6a->i6a_addr),&(ifr->ifr_addr),sizeof(struct sockaddr_in6)); - break; - - case SIOCGIFDSTADDR_INET6: - if ((ifp->if_flags & IFF_POINTOPOINT) == 0) - return EINVAL; - bcopy(&(i6a->i6a_dstaddr),&(ifr->ifr_dstaddr), - sizeof(struct sockaddr_in6)); - break; - - case SIOCGIFNETMASK_INET6: - bcopy(&(i6a->i6a_sockmask),&(ifr->ifr_addr),sizeof(struct sockaddr_in6)); - break; - - case SIOCSIFDSTADDR_INET6: - i6a->i6a_addrflags &= ~I6AF_NOTSURE; - if ((ifp->if_flags & IFF_POINTOPOINT) == 0) - return EINVAL; - oldaddr = i6a->i6a_dstaddr; - i6a->i6a_dstaddr = *(struct sockaddr_in6 *)&ifr->ifr_dstaddr; - if (ifp->if_ioctl && (error = (*ifp->if_ioctl)(ifp, SIOCSIFDSTADDR, - (caddr_t)i6a))) - { - i6a->i6a_dstaddr = oldaddr; - return error; } - if (i6a->i6a_flags & IFA_ROUTE) - { - i6a->i6a_ifa.ifa_dstaddr = (struct sockaddr *)&oldaddr; - rtinit(&(i6a->i6a_ifa), RTM_DELETE, RTF_HOST); - i6a->i6a_ifa.ifa_dstaddr = (struct sockaddr *)&i6a->i6a_dstaddr; - rtinit(&(i6a->i6a_ifa), RTM_ADD, RTF_HOST|RTF_UP); - } - break; - - /* - * For adding new IPv6 addresses to an interface, I stuck to the way - * that IPv4 uses, pretty much. - */ - case SIOCAIFADDR_INET6: - maskIsNew = 0; - hostIsNew = 1; - error = 0; - if (i6a->i6a_addr.sin6_family == AF_INET6) { - if (ifra->ifra_addr.sin6_len == 0) { - { - bcopy(&(i6a->i6a_addr),&(ifra->ifra_addr), - sizeof(struct sockaddr_in6)); - hostIsNew = 0; - } + + return EOPNOTSUPP; /*just for safety*/ +} + +/* + * Delete any existing route for an interface. + */ +void +in6_ifscrub(ifp, ia) + register struct ifnet *ifp; + register struct in6_ifaddr *ia; +{ + if ((ia->ia_flags & IFA_ROUTE) == 0) + return; + if (ifp->if_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) + rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST); + else + rtinit(&(ia->ia_ifa), (int)RTM_DELETE, 0); + ia->ia_flags &= ~IFA_ROUTE; + + /* Remove ownaddr's loopback rtentry, if it exists. */ + in6_ifremloop(&(ia->ia_ifa)); +} + +/* + * Initialize an interface's intetnet6 address + * and routing table entry. + */ +int +in6_ifinit(ifp, ia, sin6, scrub) + struct ifnet *ifp; + struct in6_ifaddr *ia; + struct sockaddr_in6 *sin6; + int scrub; +{ + struct sockaddr_in6 oldaddr; + int error, flags = RTF_UP; + int s = splimp(); + + oldaddr = ia->ia_addr; + ia->ia_addr = *sin6; + /* + * Give the interface a chance to initialize + * if this is its first address, + * and to validate the address if necessary. + */ + if (ifp->if_ioctl && + (error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia))) { + splx(s); + ia->ia_addr = oldaddr; + return(error); + } + + switch (ifp->if_type) { + case IFT_ARCNET: + case IFT_ETHER: + case IFT_FDDI: + ia->ia_ifa.ifa_rtrequest = nd6_rtrequest; + ia->ia_ifa.ifa_flags |= RTF_CLONING; + break; + case IFT_PPP: + ia->ia_ifa.ifa_rtrequest = nd6_p2p_rtrequest; + ia->ia_ifa.ifa_flags |= RTF_CLONING; + break; + } + + splx(s); + if (scrub) { + ia->ia_ifa.ifa_addr = (struct sockaddr *)&oldaddr; + in6_ifscrub(ifp, ia); + ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; + } + /* xxx + * in_socktrim + */ + /* + * Add route for the network. + */ + ia->ia_ifa.ifa_metric = ifp->if_metric; + if (ifp->if_flags & IFF_LOOPBACK) { + ia->ia_ifa.ifa_dstaddr = ia->ia_ifa.ifa_addr; + flags |= RTF_HOST; + } else if (ifp->if_flags & IFF_POINTOPOINT) { + if (ia->ia_dstaddr.sin6_family != AF_INET6) + return(0); + flags |= RTF_HOST; + } + if ((error = rtinit(&(ia->ia_ifa), (int)RTM_ADD, flags)) == 0) + ia->ia_flags |= IFA_ROUTE; + + /* Add ownaddr as loopback rtentry, if necessary(ex. on p2p link). */ + in6_ifaddloop(&(ia->ia_ifa)); + +#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) + if (ifp->if_flags & IFF_MULTICAST) + in6_restoremkludge(ia, ifp); +#endif + + return(error); +} + +#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) +/* + * Multicast address kludge: + * If there were any multicast addresses attached to this interface address, + * either move them to another address on this interface, or save them until + * such time as this interface is reconfigured for IPv6. + */ +void +in6_savemkludge(oia) + struct in6_ifaddr *oia; +{ + struct in6_ifaddr *ia; + struct in6_multi *in6m, *next; + + IFP_TO_IA6(oia->ia_ifp, ia); + if (ia) { /* there is another address */ + for (in6m = oia->ia6_multiaddrs.lh_first; in6m; in6m = next){ + next = in6m->in6m_entry.le_next; + IFAFREE(&in6m->in6m_ia->ia_ifa); + ia->ia_ifa.ifa_refcnt++; + in6m->in6m_ia = ia; + LIST_INSERT_HEAD(&ia->ia6_multiaddrs, in6m, in6m_entry); + } + } else { /* last address on this if deleted, save */ + struct multi6_kludge *mk; + + mk = malloc(sizeof(*mk), M_IPMADDR, M_WAITOK); + + LIST_INIT(&mk->mk_head); + mk->mk_ifp = oia->ia_ifp; + + for (in6m = oia->ia6_multiaddrs.lh_first; in6m; in6m = next){ + next = in6m->in6m_entry.le_next; + LIST_INSERT_HEAD(&mk->mk_head, in6m, in6m_entry); + } + + if (mk->mk_head.lh_first != NULL) { + LIST_INSERT_HEAD(&in6_mk, mk, mk_entry); + } + else { + FREE(mk, M_IPMADDR); + } + } +} + +/* + * Continuation of multicast address hack: + * If there was a multicast group list previously saved for this interface, + * then we re-attach it to the first address configured on the i/f. + */ +void +in6_restoremkludge(ia, ifp) + struct in6_ifaddr *ia; + struct ifnet *ifp; +{ + struct multi6_kludge *mk; + + for (mk = in6_mk.lh_first; mk; mk = mk->mk_entry.le_next) { + if (mk->mk_ifp == ifp) { + struct in6_multi *in6m, *next; + + for (in6m = mk->mk_head.lh_first; in6m; in6m = next){ + next = in6m->in6m_entry.le_next; + LIST_INSERT_HEAD(&ia->ia6_multiaddrs, + in6m, in6m_entry); + } + LIST_REMOVE(mk, mk_entry); + free(mk, M_IPMADDR); + break; + } + } +} + +/* + * Add an address to the list of IP6 multicast addresses for a + * given interface. + */ +struct in6_multi * +in6_addmulti(maddr6, ifp, errorp) + register struct in6_addr *maddr6; + register struct ifnet *ifp; + int *errorp; +{ + struct in6_ifaddr *ia; + struct in6_ifreq ifr; + struct in6_multi *in6m; +#ifdef __NetBSD__ + int s = splsoftnet(); +#else + int s = splnet(); +#endif + + *errorp = 0; + /* + * See if address already in list. + */ + IN6_LOOKUP_MULTI(*maddr6, ifp, in6m); + if (in6m != NULL) { + /* + * Found it; just increment the refrence count. + */ + in6m->in6m_refcount++; } else { - if (IN6_ARE_ADDR_EQUAL(&ifra->ifra_addr.sin6_addr, &i6a->i6a_addr.sin6_addr)) - hostIsNew = 0; - } - } + /* + * New address; allocate a new multicast record + * and link it into the interface's multicast list. + */ + in6m = (struct in6_multi *) + malloc(sizeof(*in6m), M_IPMADDR, M_NOWAIT); + if (in6m == NULL) { + splx(s); + *errorp = ENOBUFS; + return(NULL); + } + in6m->in6m_addr = *maddr6; + in6m->in6m_ifp = ifp; + in6m->in6m_refcount = 1; + IFP_TO_IA6(ifp, ia); + if (ia == NULL) { + free(in6m, M_IPMADDR); + splx(s); + *errorp = EADDRNOTAVAIL; /* appropriate? */ + return(NULL); + } + in6m->in6m_ia = ia; + ia->ia_ifa.ifa_refcnt++; /* gain a reference */ + LIST_INSERT_HEAD(&ia->ia6_multiaddrs, in6m, in6m_entry); + + /* + * Ask the network driver to update its multicast reception + * filter appropriately for the new address. + */ + bzero(&ifr.ifr_addr, sizeof(struct sockaddr_in6)); + ifr.ifr_addr.sin6_len = sizeof(struct sockaddr_in6); + ifr.ifr_addr.sin6_family = AF_INET6; + ifr.ifr_addr.sin6_addr = *maddr6; + if (ifp->if_ioctl == NULL) + *errorp = ENXIO; /* XXX: appropriate? */ + else + *errorp = (*ifp->if_ioctl)(ifp, SIOCADDMULTI, + (caddr_t)&ifr); + if (*errorp) { + LIST_REMOVE(in6m, in6m_entry); + free(in6m, M_IPMADDR); + splx(s); + return(NULL); + } + /* + * Let MLD6 know that we have joined a new IP6 multicast + * group. + */ + mld6_start_listening(in6m); + } + splx(s); + return(in6m); +} - if (ifra->ifra_mask.sin6_len) - { - in6_ifscrub(ifp,i6a); - bcopy(&(ifra->ifra_mask),&(i6a->i6a_sockmask), - sizeof(struct sockaddr_in6)); - maskIsNew = 1; +/* + * Delete a multicast address record. + */ +void +in6_delmulti(in6m) + struct in6_multi *in6m; +{ + struct in6_ifreq ifr; +#ifdef __NetBSD__ + int s = splsoftnet(); +#else + int s = splnet(); +#endif + + if (--in6m->in6m_refcount == 0) { + /* + * No remaining claims to this record; let MLD6 know + * that we are leaving the multicast group. + */ + mld6_stop_listening(in6m); + + /* + * Unlink from list. + */ + LIST_REMOVE(in6m, in6m_entry); + IFAFREE(&in6m->in6m_ia->ia_ifa); /* release reference */ + + /* + * Notify the network driver to update its multicast + * reception filter. + */ + bzero(&ifr.ifr_addr, sizeof(struct sockaddr_in6)); + ifr.ifr_addr.sin6_len = sizeof(struct sockaddr_in6); + ifr.ifr_addr.sin6_family = AF_INET6; + ifr.ifr_addr.sin6_addr = in6m->in6m_addr; + (*in6m->in6m_ifp->if_ioctl)(in6m->in6m_ifp, + SIOCDELMULTI, (caddr_t)&ifr); + free(in6m, M_IPMADDR); + } + splx(s); +} +#else /* not FreeBSD3 */ +/* + * Add an address to the list of IP6 multicast addresses for a + * given interface. + */ +struct in6_multi * +in6_addmulti(maddr6, ifp, errorp) + register struct in6_addr *maddr6; + register struct ifnet *ifp; + int *errorp; +{ + struct in6_multi *in6m; + struct sockaddr_in6 sin6; + struct ifmultiaddr *ifma; + int s = splnet(); + + *errorp = 0; + + /* + * Call generic routine to add membership or increment + * refcount. It wants addresses in the form of a sockaddr, + * so we build one here (being careful to zero the unused bytes). + */ + bzero(&sin6, sizeof sin6); + sin6.sin6_family = AF_INET6; + sin6.sin6_len = sizeof sin6; + sin6.sin6_addr = *maddr6; + *errorp = if_addmulti(ifp, (struct sockaddr *)&sin6, &ifma); + if (*errorp) { + splx(s); + return 0; } - if ((ifp->if_flags & IFF_POINTOPOINT) && - (ifra->ifra_dstaddr.sin6_family == AF_INET6)) - { - in6_ifscrub(ifp,i6a); - bcopy(&(ifra->ifra_dstaddr),&(i6a->i6a_dstaddr), - sizeof(struct sockaddr_in6)); - maskIsNew = 1; /* We lie, simply so that in6_ifinit() will be - called to initialize the peer's address. */ + /* + * If ifma->ifma_protospec is null, then if_addmulti() created + * a new record. Otherwise, we are done. + */ + if (ifma->ifma_protospec != 0) + return ifma->ifma_protospec; + + /* XXX - if_addmulti uses M_WAITOK. Can this really be called + at interrupt time? If so, need to fix if_addmulti. XXX */ + in6m = (struct in6_multi *)malloc(sizeof(*in6m), M_IPMADDR, M_NOWAIT); + if (in6m == NULL) { + splx(s); + return (NULL); } - if (ifra->ifra_addr.sin6_family == AF_INET6 && (hostIsNew || maskIsNew)) - error = in6_ifinit(ifp,i6a,&ifra->ifra_addr,0,!internal); - /* else i6a->i6a_addrflags &= ~I6AF_NOTSURE; */ - if (error == EEXIST) /* XXX, if route exists, we should be ok */ - error = 0; + bzero(in6m, sizeof *in6m); + in6m->in6m_addr = *maddr6; + in6m->in6m_ifp = ifp; + in6m->in6m_ifma = ifma; + ifma->ifma_protospec = in6m; + LIST_INSERT_HEAD(&in6_multihead, in6m, in6m_entry); + + /* + * Let MLD6 know that we have joined a new IP6 multicast + * group. + */ + mld6_start_listening(in6m); + splx(s); + return(in6m); +} - if (hostIsNew && !ifnetIsNew /* && (!error || error == EEXIST) */) - { - if (i6a->i6a_i6ifp) - i6a->i6a_i6ifp->i6ifp_numaddrs++; - else - panic("in6_control: missing i6ifp"); +/* + * Delete a multicast address record. + */ +void +in6_delmulti(in6m) + struct in6_multi *in6m; +{ + struct ifmultiaddr *ifma = in6m->in6m_ifma; + int s = splnet(); + + if (ifma->ifma_refcount == 1) { + /* + * No remaining claims to this record; let MLD6 know + * that we are leaving the multicast group. + */ + mld6_stop_listening(in6m); + ifma->ifma_protospec = 0; + LIST_REMOVE(in6m, in6m_entry); + free(in6m, M_IPMADDR); } - return error; + /* XXX - should be separate API for when we have an ifma? */ + if_delmulti(ifma->ifma_ifp, ifma->ifma_addr); + splx(s); +} +#endif /* not FreeBSD3 */ - case SIOCDIFADDR_INET6: - in6_ifscrub(ifp, i6a); - /* - * If last address on this interface, delete IPv6 interface record. - */ - del_in6_ifnet(ifp); +/* + * Find an IPv6 interface link-local address specific to an interface. + */ +struct in6_ifaddr * +in6ifa_ifpforlinklocal(ifp) + struct ifnet *ifp; +{ + register struct ifaddr *ifa; -#ifdef __FreeBSD__ - TAILQ_REMOVE(&ifp->if_addrhead, (struct ifaddr *)i6a, ifa_link); -#else /* __FreeBSD__ */ -#if __NetBSD__ || __OpenBSD__ - TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)i6a, ifa_list); -#else /* __NetBSD__ || __OpenBSD__ */ - if ((ifa = ifp->if_addrlist) == (struct ifaddr *)i6a) - ifp->if_addrlist = ifa->ifa_next; - else +#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3) + for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) +#else + for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next) +#endif { - while (ifa->ifa_next && - (ifa->ifa_next != (struct ifaddr *)i6a)) - ifa=ifa->ifa_next; - if (ifa->ifa_next) - ifa->ifa_next = i6a->i6a_ifa.ifa_next; - else - DPRINTF(IDL_ERROR, ("Couldn't unlink in6_ifaddr from ifp!\n")); - } -#endif /* __NetBSD__ || __OpenBSD__ */ -#endif /* __FreeBSD__ */ - oi6a = i6a; - if (oi6a == (i6a = in6_ifaddr)) - in6_ifaddr = i6a->i6a_next; - else + 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))) + 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; +{ + register struct ifaddr *ifa; + +#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3) + for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) +#else + for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next) +#endif { - while (i6a->i6a_next && (i6a->i6a_next != oi6a)) - i6a = i6a->i6a_next; - if (i6a->i6a_next) - i6a->i6a_next = oi6a->i6a_next; - else - DPRINTF(IDL_ERROR, ("Didn't unlink in6_ifaddr from list.\n")); + 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; } - IFAFREE((&oi6a->i6a_ifa)); /* For the benefit of routes pointing - to this ifa. */ - break; - default: - DPRINTF(IDL_ERROR, - ("in6_control(): Default case not implemented.\n")); - return EOPNOTSUPP; - } + return((struct in6_ifaddr *)ifa); +} - return 0; +/* + * Convert IP6 address to printable (loggable) representation. + */ +static char digits[] = "0123456789abcdef"; +static int ip6round = 0; +char * +ip6_sprintf(addr) +register struct in6_addr *addr; +{ + static char ip6buf[8][48]; + register int i; + register char *cp; + register u_short *a = (u_short *)addr; + register u_char *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 = (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]); } -/*---------------------------------------------------------------------- - * in6_ifscrub: - * Delete any existing route for an IPv6 interface. - ----------------------------------------------------------------------*/ +int +in6_localaddr(in6) + struct in6_addr *in6; +{ + struct in6_ifaddr *ia; + + if (IN6_IS_ADDR_LOOPBACK(in6) || IN6_IS_ADDR_LINKLOCAL(in6)) + return 1; + + for (ia = in6_ifaddr; ia; ia = ia->ia_next) + if (IN6_ARE_MASKED_ADDR_EQUAL(in6, &ia->ia_addr.sin6_addr, + &ia->ia_prefixmask.sin6_addr)) + return 1; + return (0); +} + +/* + * Get a scope of the address. Node-local, link-local, site-local or global. + */ int -in6_ifscrub(ifp,i6a) - register struct ifnet *ifp; - register struct in6_ifaddr *i6a; +in6_addrscope (addr) +struct in6_addr *addr; { - if (!(i6a->i6a_flags & IFA_ROUTE)) - return 1; + int scope; + + if (addr->s6_addr8[0] == 0xfe) { + scope = addr->s6_addr8[1] & 0xc0; + + switch (scope) { + case 0x80: + return IPV6_ADDR_SCOPE_LINKLOCAL; + break; + case 0xc0: + return IPV6_ADDR_SCOPE_SITELOCAL; + break; + default: + return IPV6_ADDR_SCOPE_GLOBAL; /* just in case */ + break; + } + } - if (ifp->if_flags & (IFF_LOOPBACK|IFF_POINTOPOINT)) - rtinit(&(i6a->i6a_ifa), (int)RTM_DELETE, RTF_HOST); - else - rtinit(&(i6a->i6a_ifa), (int)RTM_DELETE, 0); - i6a->i6a_flags &= ~IFA_ROUTE; - return 0; + if (addr->s6_addr8[0] == 0xff) { + scope = addr->s6_addr8[1] & 0x0f; + + /* + * due to other scope such as reserved, + * return scope doesn't work. + */ + switch (scope) { + case IPV6_ADDR_SCOPE_NODELOCAL: + return IPV6_ADDR_SCOPE_NODELOCAL; + break; + case IPV6_ADDR_SCOPE_LINKLOCAL: + return IPV6_ADDR_SCOPE_LINKLOCAL; + break; + case IPV6_ADDR_SCOPE_SITELOCAL: + return IPV6_ADDR_SCOPE_SITELOCAL; + break; + default: + return IPV6_ADDR_SCOPE_GLOBAL; + break; + } + } + + if (bcmp(&in6addr_loopback, addr, sizeof(addr) - 1) == 0) { + if (addr->s6_addr8[15] == 1) /* loopback */ + return IPV6_ADDR_SCOPE_NODELOCAL; + if (addr->s6_addr8[15] == 0) /* unspecified */ + return IPV6_ADDR_SCOPE_LINKLOCAL; + } + + return IPV6_ADDR_SCOPE_GLOBAL; } -/*---------------------------------------------------------------------- - * Initialize an IPv6 address for an interface. - * - * When I get around to doing duplicate address detection, this is probably - * the place to do it. - ----------------------------------------------------------------------*/ +/* + * return length of part which dst and src are equal + * hard coding... + */ int -in6_ifinit(ifp, i6a, sin6, scrub, useDAD) - register struct ifnet *ifp; - register struct in6_ifaddr *i6a; - struct sockaddr_in6 *sin6; - int scrub; - int useDAD; -{ - int s, error, flags = RTF_UP; - struct sockaddr_in6 oldaddr; - - DPRINTF(IDL_EVENT,("Before splimp in in6_ifinit()\n")); - s = splimp(); - - bcopy(&(i6a->i6a_addr),&oldaddr,sizeof(struct sockaddr_in6)); - bcopy(sin6,&(i6a->i6a_addr),sizeof(struct sockaddr_in6)); - - /* - * Give the interface a chance to initialize - * if this is its first address, - * and to validate the address if necessary. - */ - - if (ifp->if_ioctl && (error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, - (caddr_t)i6a))) - { - bcopy(&oldaddr,&(i6a->i6a_addr),sizeof(struct sockaddr_in6)); - splx(s); - return error; - } - - /* - * IPv4 in 4.4BSD sets the RTF_CLONING flag here if it's an Ethernet. - * I delay this until later. - */ - - splx(s); - DPRINTF(IDL_EVENT,("After splx() in in6_ifinit().\n")); - - sin6->sin6_port = 0; - - if (scrub) - { - i6a->i6a_ifa.ifa_addr = (struct sockaddr *)&oldaddr; - in6_ifscrub(ifp, i6a); - i6a->i6a_ifa.ifa_addr = (struct sockaddr *)&i6a->i6a_addr; - } - - /* - * Adjust the sin6_len such that it only counts mask bytes with - * 1's in them. - */ - - { - register char *cpbase = (char *)&(i6a->i6a_sockmask.sin6_addr); - register char *cp = cpbase + sizeof(struct in6_addr); - - i6a->i6a_sockmask.sin6_len = 0; - while (--cp >=cpbase) - if (*cp) - { - i6a->i6a_sockmask.sin6_len = 1 + cp - (char *)&(i6a->i6a_sockmask); - break; - } - } - - /* - * Add route. Also, set some properties of the interface address here. - * (Properties include permanance, lifetime, etc.) - */ - - i6a->i6a_ifa.ifa_metric = ifp->if_metric; - i6a->i6a_ifa.ifa_rtrequest = ipv6_rtrequest; /* Want this to be true - for ALL IPv6 ifaddrs. */ - if (ifp->if_flags & IFF_LOOPBACK) - { - useDAD = 0; - i6a->i6a_ifa.ifa_dstaddr = i6a->i6a_ifa.ifa_addr; - flags |= RTF_HOST; - - /* Loopback is definitely a permanent address. */ - if (IN6_IS_ADDR_LOOPBACK(&i6a->i6a_addr.sin6_addr)) - i6a->i6a_addrflags |= I6AF_PERMANENT; - } - else if (ifp->if_flags & IFF_POINTOPOINT) - { - useDAD = 0; /* ??!!?? */ - if (i6a->i6a_dstaddr.sin6_family != AF_INET6) - return 0; +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; +} - flags |= RTF_HOST; - } - else - { - /* - * No b-cast in IPv6, therefore the ifa_broadaddr (concidentally the - * dest address filled in above...) should be set to NULL! - */ - i6a->i6a_ifa.ifa_broadaddr = NULL; - - if (IN6_IS_ADDR_LINKLOCAL(&i6a->i6a_addr.sin6_addr)) - { - flags |= RTF_HOST; - i6a->i6a_ifa.ifa_dstaddr = i6a->i6a_ifa.ifa_addr; - - /* - * Possibly do other stuff specific to link-local addresses, hence - * keeping this separate from IFF_LOOPBACK case above. I may move - * the link-local check to || with IFF_LOOPBACK. - * - * Other stuff includes setting i6a_preflen so when addrconf - * needs to know what part of the link-local is used for uniqueness, - * it doesn't have to gyrate. - */ - switch(i6a->i6a_ifp->if_type) - { - case IFT_ETHER: - i6a->i6a_preflen = 64; - break; - default: - DPRINTF(IDL_ERROR,("Can't set i6a_preflen for type %d.\n",\ - i6a->i6a_ifp->if_type)); - break; - } +int +in6_are_prefix_equal(p1, p2, len) + struct in6_addr *p1, *p2; + int len; +{ + int bytelen, bitlen; - i6a->i6a_addrflags |= (I6AF_LINKLOC | I6AF_PERMANENT); + /* sanity check */ + if (0 > len || len > 128) { + log(LOG_ERR, "in6_are_prefix_equal: invalid prefix length(%d)\n", + len); + return(0); } - else + + bytelen = len / 8; + bitlen = len % 8; + + if (bcmp(&p1->s6_addr, &p2->s6_addr, bytelen)) + return(0); + if (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 + */ + +struct in6_ifaddr * +in6_ifawithscope(ifp, dst) + register struct ifnet *ifp; + register struct in6_addr *dst; +{ + int dst_scope = in6_addrscope(dst), blen = -1, tlen; + struct ifaddr *ifa; + struct in6_ifaddr *besta = NULL, *ia; + 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. + */ +#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3) + for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) +#else + for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next) +#endif { - if (!(i6a->i6a_sockmask.sin6_len == sizeof(struct sockaddr_in6) && - IN6_ARE_ADDR_EQUAL(&i6a->i6a_sockmask.sin6_addr, &in6_allones.sin6_addr))) - flags |= RTF_CLONING; /* IMHO, ALL network routes - have the cloning bit set for next-hop - resolution if they aren't loopback or - pt. to pt. */ - i6a->i6a_addrflags |= I6AF_PREFIX; /* I'm a 'prefix list entry'. */ - } - } - - if ((error = rtinit(&(i6a->i6a_ifa), RTM_ADD,flags)) == 0) - { - i6a->i6a_flags |= IFA_ROUTE; - } - - /* - * If the interface supports multicast, join the appropriate - * multicast groups (all {nodes, routers}) on that interface. - * - * Also join the solicited nodes discovery multicast group for that - * destination. - */ - if (ifp->if_flags & IFF_MULTICAST) - { - struct in6_multi *rc; - - /* NOTE2: Set default multicast interface here. - Set up cloning route for ff00::0/8 */ - if (ifp->if_type != IFT_LOOP && mcastdefault == NULL) - setmcastdef(ifp); - - { - struct in6_addr addr = IN6ADDR_ALLNODES_INIT; - - rc = in6_addmulti(&addr, ifp); - }; - - /* All-routers, if forwarding */ - if (ipv6forwarding) { - struct in6_addr addr = IN6ADDR_ALLROUTERS_INIT; - - rc = in6_addmulti(&addr, ifp); - }; - - { - struct in6_addr addr = IN6ADDR_ALLHOSTS_INIT; - - rc = in6_addmulti(&addr, ifp); - }; - - /* Solicited-nodes. */ - { - struct in6_addr addr = IN6ADDR_SN_PREFIX_INIT; - - addr.s6_addr[13] = i6a->i6a_addr.sin6_addr.s6_addr32[13]; - addr.s6_addr[14] = i6a->i6a_addr.sin6_addr.s6_addr32[14]; - addr.s6_addr[15] = i6a->i6a_addr.sin6_addr.s6_addr32[15]; - - DDO(IDL_EVENT, dump_in6_addr(&addr)); - - rc=in6_addmulti(&addr, ifp); - }; + 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; + + for (ia = in6_ifaddr; ia; ia = ia->ia_next) { + if (IPV6_ADDR_SCOPE_GLOBAL != + in6_addrscope(&(ia->ia_addr.sin6_addr))) + continue; + /* XXX: is there any case to allow anycast? */ + if ((ia->ia6_flags & IN6_IFF_ANYCAST) != 0) + continue; + if ((ia->ia6_flags & IN6_IFF_NOTREADY) != 0) + continue; + if ((ia->ia6_flags & IN6_IFF_DETACHED) != 0) + continue; + if ((ia->ia6_flags & IN6_IFF_DEPRECATED) != 0) { + if (ip6_use_deprecated) + dep[1] = (struct in6_ifaddr *)ifa; + continue; + } + return ia; + } -#if 0 - addr.s6_addr32[0] = htonl(0xff020000); - addr.s6_addr32[1] = 0; - addr.s6_addr32[2] = htonl(1); - addr.s6_addr32[3] = i6a->i6a_addr.sin6_addr.s6_addr32[3] | htonl(0xff000000); -#endif /* 0 */ - } + /* use the last-resort values, that are, deprecated addresses */ + if (dep[0]) + return dep[0]; + if (dep[1]) + return dep[1]; - if (useDAD /*&& error != 0*/) - addrconf_dad(i6a); - else - i6a->i6a_addrflags &= ~I6AF_NOTSURE; + return NULL; +} + +/* + * return the best address out of the same scope. if no address was + * found, return the first valid address from designated IF. + */ - return error; +struct in6_ifaddr * +in6_ifawithifp(ifp, dst) + register struct ifnet *ifp; + register 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. + */ +#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3) + for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) +#else + for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next) +#endif + { + 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); + +#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3) + for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) +#else + for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next) +#endif + { + 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; } -/*---------------------------------------------------------------------- - * Add IPv6 multicast address. IPv6 multicast addresses are handled - * pretty much like IP multicast addresses for now. - * - * Multicast addresses hang off in6_ifaddr's. Eventually, they should hang - * off the link-local multicast address, this way, there are no ambiguities. - ----------------------------------------------------------------------*/ - -struct in6_multi *in6_addmulti(addr,ifp) - register struct in6_addr *addr; - struct ifnet *ifp; - -{ - register struct in6_multi *in6m; - struct inet6_ifreq ifr; - struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&ifr.ifr_addr; - struct in6_ifnet *i6ifp; - int s = splnet(); - - /* - * See if address is already in list.. - */ - - IN6_LOOKUP_MULTI(addr,ifp,in6m); - - if (in6m != NULL) - { - /* Increment the reference count. */ - in6m->in6m_refcount++; - } - else - { -#if __FreeBSD__ - struct ifmultiaddr *ifma; -#endif /* __FreeBSD__ */ - /* - * Otherwise, allocate a new m-cast record and link it to - * the interface's multicast list. - */ - - if ((in6m=malloc(sizeof(struct in6_multi),M_IPMADDR,M_NOWAIT)) == NULL) +/* + * perform DAD when interface becomes IFF_UP. + */ +void +in6_if_up(ifp) + struct ifnet *ifp; +{ + struct ifaddr *ifa; + struct in6_ifaddr *ia; + struct sockaddr_dl *sdl; + int type; +#ifdef __bsdi__ + u_char ea[ETHER_ADDR_LEN]; +#else + struct ether_addr ea; +#endif + int off; + int dad_delay; /* delay ticks before DAD output */ + + bzero(&ea, sizeof(ea)); + sdl = NULL; + +#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3) + for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) +#else + for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next) +#endif { - splx(s); - return NULL; - } - bzero(in6m,sizeof(struct in6_multi)); - in6m->in6m_addr = *addr; - in6m->in6m_refcount = 1; - in6m->in6m_ifp = ifp; - - for(i6ifp = in6_ifnet; i6ifp != NULL && i6ifp->i6ifp_ifp != ifp; - i6ifp = i6ifp->i6ifp_next) - ; - if (i6ifp == NULL) + if (ifa->ifa_addr->sa_family == AF_INET6 + && IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr)) { + goto dad; + } + if (ifa->ifa_addr->sa_family != AF_LINK) + continue; + sdl = (struct sockaddr_dl *)ifa->ifa_addr; + break; + } + + switch (ifp->if_type) { + case IFT_SLIP: + case IFT_PPP: + case IFT_DUMMY: + case IFT_GIF: + case IFT_FAITH: + type = IN6_IFT_P2P; + in6_ifattach(ifp, type, 0, 1); + break; + case IFT_ETHER: + case IFT_FDDI: + case IFT_ATM: + type = IN6_IFT_802; + if (sdl == NULL) + break; + off = sdl->sdl_nlen; + if (bcmp(&sdl->sdl_data[off], &ea, sizeof(ea)) != 0) + in6_ifattach(ifp, type, LLADDR(sdl), 0); + break; + case IFT_ARCNET: + type = IN6_IFT_ARCNET; + if (sdl == NULL) + break; + off = sdl->sdl_nlen; + if (sdl->sdl_data[off] != 0) /* XXX ?: */ + in6_ifattach(ifp, type, LLADDR(sdl), 0); + break; + default: + break; + } + +dad: + dad_delay = 0; +#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3) + for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) +#else + for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next) +#endif { - free(in6m,M_IPMADDR); - splx(s); - return NULL; - } - in6m->in6m_i6ifp = i6ifp; - in6m->in6m_next = i6ifp->i6ifp_multiaddrs; - i6ifp->i6ifp_multiaddrs = in6m; - - /* - * Ask the network driver to update its multicast reception - * filter appropriately for the new address. - */ - sin6->sin6_family=AF_INET6; - sin6->sin6_len=sizeof(struct sockaddr_in6); - sin6->sin6_addr = *addr; - sin6->sin6_port = 0; - sin6->sin6_flowinfo = 0; - - -#if __FreeBSD__ - if (if_addmulti(ifp, (struct sockaddr *) sin6, &ifma)) -#else /* __FreeBSD */ - if (ifp->if_ioctl == NULL || - (*ifp->if_ioctl)(ifp, SIOCADDMULTI,(caddr_t)&ifr) != 0) -#endif /* __FreeBSD__ */ + 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); + } +} + +/* + * Calculate max IPv6 MTU through all the interfaces and store it + * to in6_maxmtu. + */ +void +in6_setmaxmtu() +{ + unsigned long maxmtu = 0; + struct ifnet *ifp; + +#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3) + for (ifp = ifnet; ifp; ifp = ifp->if_next) +#else + for (ifp = TAILQ_FIRST(&ifnet); ifp; ifp = TAILQ_NEXT(ifp, if_list)) +#endif { - i6ifp->i6ifp_multiaddrs = in6m->in6m_next; - free(in6m,M_IPMADDR); - splx(s); - return NULL; + if ((ifp->if_flags & IFF_LOOPBACK) == 0 && + nd_ifinfo[ifp->if_index].linkmtu > maxmtu) + maxmtu = nd_ifinfo[ifp->if_index].linkmtu; } -#ifdef __FreeBSD__ - ifma->ifma_protospec = in6m; -#endif /* __FreeBSD__ */ - - /* Tell IGMP that we've joined a new group. */ - /*ipv6_igmp_joingroup(in6m);*/ - } - splx(s); - return in6m; + if (maxmtu) /* update only when maxmtu is positive */ + in6_maxmtu = maxmtu; } -/*---------------------------------------------------------------------- - * Delete IPv6 multicast address. - ----------------------------------------------------------------------*/ +#ifdef MAPPED_ADDR_ENABLED +/* + * 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_delmulti(in6m) - register struct in6_multi *in6m; -{ - register struct in6_multi **p; - struct inet6_ifreq ifr; - struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&(ifr.ifr_addr); - int s = splnet(); - - if (--in6m->in6m_refcount == 0) - { - /* Tell IGMP that I'm bailing this group. */ - /* ipv6_igmp_leavegroup(in6m);*/ - - /* Unlink from list. */ - for (p = &(in6m->in6m_i6ifp->i6ifp_multiaddrs); - *p != in6m; - p = &(*p)->in6m_next) - ; - *p = (*p)->in6m_next; - - /* - * Notify the network driver to update its multicast reception - * filter. - */ - sin6->sin6_family = AF_INET6; - sin6->sin6_len = sizeof(struct sockaddr_in6); - sin6->sin6_port = 0; - sin6->sin6_flowinfo = 0; - sin6->sin6_addr = in6m->in6m_addr; - (*(in6m->in6m_ifp->if_ioctl))(in6m->in6m_ifp, SIOCDELMULTI, - (caddr_t)&ifr); - - free(in6m,M_IPMADDR); - } - splx(s); +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; +} +#endif /* MAPPED_ADDR_ENABLED */ + |