X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/d7e50217d7adf6e52786a38bcaa4cd698cb9a79e..13f56ec4e58bf8687e2a68032c093c0213dd519b:/bsd/net/if.c diff --git a/bsd/net/if.c b/bsd/net/if.c index 1f10ed96f..595fcaea9 100644 --- a/bsd/net/if.c +++ b/bsd/net/if.c @@ -1,16 +1,19 @@ /* - * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2000-2010 Apple Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ - * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER @@ -20,7 +23,7 @@ * Please see the License for the specific language governing rights and * limitations under the License. * - * @APPLE_LICENSE_HEADER_END@ + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ /* * Copyright (c) 1980, 1986, 1993 @@ -57,6 +60,14 @@ * @(#)if.c 8.3 (Berkeley) 1/4/94 * $FreeBSD: src/sys/net/if.c,v 1.85.2.9 2001/07/24 19:10:17 brooks Exp $ */ +/* + * NOTICE: This file was modified by SPARTA, Inc. in 2006 to introduce + * support for mandatory and extensible security protections. This notice + * is included in support of clause 2.2 (b) of the Apple Public License, + * Version 2.0. + */ + +#include #include #include @@ -70,53 +81,165 @@ #include #include #include +#include +#include + +#include + +#include #include #include #include #include #include +#include +#include + #include #include #ifdef __APPLE__ #include //#include #include +#include #endif -#if defined(INET) || defined(INET6) +#if INET || INET6 /*XXX*/ #include #include +#include +#include #if INET6 #include #include +#include #endif #endif +#if CONFIG_MACF_NET +#include +#endif + + /* * System initialization */ -static int ifconf __P((u_long, caddr_t)); -static void if_qflush __P((struct ifqueue *)); -static void link_rtrequest __P((int, struct rtentry *, struct sockaddr *)); +/* Lock group and attribute for ifaddr lock */ +lck_attr_t *ifa_mtx_attr; +lck_grp_t *ifa_mtx_grp; +static lck_grp_attr_t *ifa_mtx_grp_attr; + +static int ifconf(u_long cmd, user_addr_t ifrp, int * ret_space); +static void if_qflush(struct ifqueue *); +__private_extern__ void link_rtrequest(int, struct rtentry *, struct sockaddr *); +void if_rtproto_del(struct ifnet *ifp, int protocol); + +static int if_addmulti_common(struct ifnet *, const struct sockaddr *, + struct ifmultiaddr **, int); +static int if_delmulti_common(struct ifmultiaddr *, struct ifnet *, + const struct sockaddr *, int); + +static int if_rtmtu(struct radix_node *, void *); +static void if_rtmtu_update(struct ifnet *); + +#if IF_CLONE_LIST +static int if_clone_list(int count, int * total, user_addr_t dst); +#endif /* IF_CLONE_LIST */ MALLOC_DEFINE(M_IFADDR, "ifaddr", "interface address"); -MALLOC_DEFINE(M_IFMADDR, "ether_multi", "link-level multicast address"); int ifqmaxlen = IFQ_MAXLEN; -struct ifnethead ifnet; /* depend on static init XXX */ +struct ifnethead ifnet_head = TAILQ_HEAD_INITIALIZER(ifnet_head); + +static int if_cloners_count; +LIST_HEAD(, if_clone) if_cloners = LIST_HEAD_INITIALIZER(if_cloners); + +static struct ifaddr *ifa_ifwithnet_common(const struct sockaddr *, + unsigned int); +static void if_attach_ifa_common(struct ifnet *, struct ifaddr *, int); +static void if_detach_ifa_common(struct ifnet *, struct ifaddr *, int); + +static void if_attach_ifma(struct ifnet *, struct ifmultiaddr *, int); +static int if_detach_ifma(struct ifnet *, struct ifmultiaddr *, int); + +static struct ifmultiaddr *ifma_alloc(int); +static void ifma_free(struct ifmultiaddr *); +static void ifma_trace(struct ifmultiaddr *, int); + +#if DEBUG +static unsigned int ifma_debug = 1; /* debugging (enabled) */ +#else +static unsigned int ifma_debug; /* debugging (disabled) */ +#endif /* !DEBUG */ +static unsigned int ifma_size; /* size of zone element */ +static struct zone *ifma_zone; /* zone for ifmultiaddr */ + +#define IFMA_TRACE_HIST_SIZE 32 /* size of trace history */ + +/* For gdb */ +__private_extern__ unsigned int ifma_trace_hist_size = IFMA_TRACE_HIST_SIZE; + +struct ifmultiaddr_dbg { + struct ifmultiaddr ifma; /* ifmultiaddr */ + u_int16_t ifma_refhold_cnt; /* # of ref */ + u_int16_t ifma_refrele_cnt; /* # of rele */ + /* + * Circular lists of IFA_ADDREF and IFA_REMREF callers. + */ + ctrace_t ifma_refhold[IFMA_TRACE_HIST_SIZE]; + ctrace_t ifma_refrele[IFMA_TRACE_HIST_SIZE]; + /* + * Trash list linkage + */ + TAILQ_ENTRY(ifmultiaddr_dbg) ifma_trash_link; +}; + +/* List of trash ifmultiaddr entries protected by ifma_trash_lock */ +static TAILQ_HEAD(, ifmultiaddr_dbg) ifma_trash_head; +static decl_lck_mtx_data(, ifma_trash_lock); + +#define IFMA_ZONE_MAX 64 /* maximum elements in zone */ +#define IFMA_ZONE_NAME "ifmultiaddr" /* zone name */ #if INET6 /* * XXX: declare here to avoid to include many inet6 related files.. * should be more generalized? */ -extern void nd6_setmtu __P((struct ifnet *)); -extern int ip6_auto_on; +extern void nd6_setmtu(struct ifnet *); +extern lck_mtx_t *nd6_mutex; #endif + +void +ifa_init(void) +{ + /* Setup lock group and attribute for ifaddr */ + ifa_mtx_grp_attr = lck_grp_attr_alloc_init(); + ifa_mtx_grp = lck_grp_alloc_init("ifaddr", ifa_mtx_grp_attr); + ifa_mtx_attr = lck_attr_alloc_init(); + + PE_parse_boot_argn("ifa_debug", &ifma_debug, sizeof (ifma_debug)); + + ifma_size = (ifma_debug == 0) ? sizeof (struct ifmultiaddr) : + sizeof (struct ifmultiaddr_dbg); + + ifma_zone = zinit(ifma_size, IFMA_ZONE_MAX * ifma_size, 0, + IFMA_ZONE_NAME); + if (ifma_zone == NULL) { + panic("%s: failed allocating %s", __func__, IFMA_ZONE_NAME); + /* NOTREACHED */ + } + zone_change(ifma_zone, Z_EXPAND, TRUE); + zone_change(ifma_zone, Z_CALLERACCT, FALSE); + + lck_mtx_init(&ifma_trash_lock, ifa_mtx_grp, ifa_mtx_attr); + TAILQ_INIT(&ifma_trash_head); +} + /* * Network interface utility routines. * @@ -124,428 +247,1069 @@ extern int ip6_auto_on; * parameters. */ -int if_index = 0; +int if_index; struct ifaddr **ifnet_addrs; -struct ifnet **ifindex2ifnet = NULL; +struct ifnet **ifindex2ifnet; - -/* - * Attach an interface to the - * list of "active" interfaces. - */ -void -old_if_attach(ifp) - struct ifnet *ifp; +__private_extern__ void +if_attach_ifa(struct ifnet *ifp, struct ifaddr *ifa) { - unsigned socksize, ifasize; - int namelen, masklen; - char workbuf[64]; - register struct sockaddr_dl *sdl; - register struct ifaddr *ifa; - static int if_indexlim = 8; - static int inited; + if_attach_ifa_common(ifp, ifa, 0); +} - if (ifp->if_snd.ifq_maxlen == 0) - ifp->if_snd.ifq_maxlen = ifqmaxlen; +__private_extern__ void +if_attach_link_ifa(struct ifnet *ifp, struct ifaddr *ifa) +{ + if_attach_ifa_common(ifp, ifa, 1); +} - if (!inited) { - TAILQ_INIT(&ifnet); - inited = 1; +static void +if_attach_ifa_common(struct ifnet *ifp, struct ifaddr *ifa, int link) +{ + ifnet_lock_assert(ifp, IFNET_LCK_ASSERT_EXCLUSIVE); + IFA_LOCK_ASSERT_HELD(ifa); + + if (ifa->ifa_ifp != ifp) { + panic("%s: Mismatch ifa_ifp=%p != ifp=%p", __func__, + ifa->ifa_ifp, ifp); + /* NOTREACHED */ + } else if (ifa->ifa_debug & IFD_ATTACHED) { + panic("%s: Attempt to attach an already attached ifa=%p", + __func__, ifa); + /* NOTREACHED */ + } else if (link && !(ifa->ifa_debug & IFD_LINK)) { + panic("%s: Unexpected non-link address ifa=%p", __func__, ifa); + /* NOTREACHED */ + } else if (!link && (ifa->ifa_debug & IFD_LINK)) { + panic("%s: Unexpected link address ifa=%p", __func__, ifa); + /* NOTREACHED */ } + IFA_ADDREF_LOCKED(ifa); + ifa->ifa_debug |= IFD_ATTACHED; + if (link) + TAILQ_INSERT_HEAD(&ifp->if_addrhead, ifa, ifa_link); + else + TAILQ_INSERT_TAIL(&ifp->if_addrhead, ifa, ifa_link); - TAILQ_INSERT_TAIL(&ifnet, ifp, if_link); - ifp->if_index = ++if_index; - /* - * XXX - - * The old code would work if the interface passed a pre-existing - * chain of ifaddrs to this code. We don't trust our callers to - * properly initialize the tailq, however, so we no longer allow - * this unlikely case. - */ - TAILQ_INIT(&ifp->if_addrhead); - TAILQ_INIT(&ifp->if_prefixhead); - LIST_INIT(&ifp->if_multiaddrs); - getmicrotime(&ifp->if_lastchange); - if (ifnet_addrs == 0 || if_index >= if_indexlim) { - unsigned n = (if_indexlim <<= 1) * sizeof(ifa); - struct ifaddr **q = (struct ifaddr **) - _MALLOC(n, M_IFADDR, M_WAITOK); - bzero((caddr_t)q, n); - if (ifnet_addrs) { - bcopy((caddr_t)ifnet_addrs, (caddr_t)q, n/2); - FREE((caddr_t)ifnet_addrs, M_IFADDR); - } - ifnet_addrs = (struct ifaddr **)q; - - /* grow ifindex2ifnet */ - n = if_indexlim * sizeof(struct ifaddr *); - q = (struct ifaddr **)_MALLOC(n, M_IFADDR, M_WAITOK); - bzero(q, n); - if (ifindex2ifnet) { - bcopy((caddr_t)ifindex2ifnet, q, n/2); - _FREE((caddr_t)ifindex2ifnet, M_IFADDR); - } - ifindex2ifnet = (struct ifnet **)q; - } + if (ifa->ifa_attached != NULL) + (*ifa->ifa_attached)(ifa); +} - ifindex2ifnet[if_index] = ifp; +__private_extern__ void +if_detach_ifa(struct ifnet *ifp, struct ifaddr *ifa) +{ + if_detach_ifa_common(ifp, ifa, 0); +} - /* - * create a Link Level name for this device - */ - namelen = snprintf(workbuf, sizeof(workbuf), - "%s%d", ifp->if_name, ifp->if_unit); -#define _offsetof(t, m) ((int)((caddr_t)&((t *)0)->m)) - masklen = _offsetof(struct sockaddr_dl, sdl_data[0]) + namelen; - socksize = masklen + ifp->if_addrlen; -#define ROUNDUP(a) (1 + (((a) - 1) | (sizeof(long) - 1))) - if (socksize < sizeof(*sdl)) - socksize = sizeof(*sdl); - socksize = ROUNDUP(socksize); - ifasize = sizeof(*ifa) + 2 * socksize; - ifa = (struct ifaddr *) _MALLOC(ifasize, M_IFADDR, M_WAITOK); - if (ifa) { - bzero((caddr_t)ifa, ifasize); - sdl = (struct sockaddr_dl *)(ifa + 1); - sdl->sdl_len = socksize; - sdl->sdl_family = AF_LINK; - bcopy(workbuf, sdl->sdl_data, namelen); - sdl->sdl_nlen = namelen; - sdl->sdl_index = ifp->if_index; - sdl->sdl_type = ifp->if_type; - ifnet_addrs[if_index - 1] = ifa; - ifa->ifa_ifp = ifp; - ifa->ifa_rtrequest = link_rtrequest; - ifa->ifa_addr = (struct sockaddr *)sdl; - sdl = (struct sockaddr_dl *)(socksize + (caddr_t)sdl); - ifa->ifa_netmask = (struct sockaddr *)sdl; - sdl->sdl_len = masklen; - while (namelen != 0) - sdl->sdl_data[--namelen] = 0xff; - TAILQ_INSERT_HEAD(&ifp->if_addrhead, ifa, ifa_link); - } +__private_extern__ void +if_detach_link_ifa(struct ifnet *ifp, struct ifaddr *ifa) +{ + if_detach_ifa_common(ifp, ifa, 1); } -/* - * Locate an interface based on a complete address. - */ -/*ARGSUSED*/ -struct ifaddr * -ifa_ifwithaddr(addr) - register struct sockaddr *addr; -{ - register struct ifnet *ifp; - register struct ifaddr *ifa; - -#define equal(a1, a2) \ - (bcmp((caddr_t)(a1), (caddr_t)(a2), ((struct sockaddr *)(a1))->sa_len) == 0) - for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_link.tqe_next) - for (ifa = ifp->if_addrhead.tqh_first; ifa; - ifa = ifa->ifa_link.tqe_next) { - if (ifa->ifa_addr->sa_family != addr->sa_family) - continue; - if (equal(addr, ifa->ifa_addr)) - return (ifa); - if ((ifp->if_flags & IFF_BROADCAST) && ifa->ifa_broadaddr && - /* IP6 doesn't have broadcast */ - ifa->ifa_broadaddr->sa_len != 0 && - equal(ifa->ifa_broadaddr, addr)) - return (ifa); +static void +if_detach_ifa_common(struct ifnet *ifp, struct ifaddr *ifa, int link) +{ + ifnet_lock_assert(ifp, IFNET_LCK_ASSERT_EXCLUSIVE); + IFA_LOCK_ASSERT_HELD(ifa); + + if (link && !(ifa->ifa_debug & IFD_LINK)) { + panic("%s: Unexpected non-link address ifa=%p", __func__, ifa); + /* NOTREACHED */ + } else if (link && ifa != TAILQ_FIRST(&ifp->if_addrhead)) { + panic("%s: Link address ifa=%p not first", __func__, ifa); + /* NOTREACHED */ + } else if (!link && (ifa->ifa_debug & IFD_LINK)) { + panic("%s: Unexpected link address ifa=%p", __func__, ifa); + /* NOTREACHED */ + } else if (!(ifa->ifa_debug & IFD_ATTACHED)) { + panic("%s: Attempt to detach an unattached address ifa=%p", + __func__, ifa); + /* NOTREACHED */ + } else if (ifa->ifa_ifp != ifp) { + panic("%s: Mismatch ifa_ifp=%p, ifp=%p", __func__, + ifa->ifa_ifp, ifp); + /* NOTREACHED */ + } else if (ifa->ifa_debug & IFD_DEBUG) { + struct ifaddr *ifa2; + TAILQ_FOREACH(ifa2, &ifp->if_addrhead, ifa_link) { + if (ifa2 == ifa) + break; + } + if (ifa2 != ifa) { + panic("%s: Attempt to detach a stray address ifa=%p", + __func__, ifa); + /* NOTREACHED */ + } + } + TAILQ_REMOVE(&ifp->if_addrhead, ifa, ifa_link); + /* This must not be the last reference to the ifaddr */ + if (IFA_REMREF_LOCKED(ifa) == NULL) { + panic("%s: unexpected (missing) refcnt ifa=%p", __func__, ifa); + /* NOTREACHED */ } - return ((struct ifaddr *)0); + ifa->ifa_debug &= ~IFD_ATTACHED; + + if (ifa->ifa_detached != NULL) + (*ifa->ifa_detached)(ifa); } + +#define INITIAL_IF_INDEXLIM 8 + /* - * Locate the point to point interface with a given destination address. + * Function: if_next_index + * Purpose: + * Return the next available interface index. + * Grow the ifnet_addrs[] and ifindex2ifnet[] arrays to accomodate the + * added entry when necessary. + * + * Note: + * ifnet_addrs[] is indexed by (if_index - 1), whereas + * ifindex2ifnet[] is indexed by ifp->if_index. That requires us to + * always allocate one extra element to hold ifindex2ifnet[0], which + * is unused. */ -/*ARGSUSED*/ -struct ifaddr * -ifa_ifwithdstaddr(addr) - register struct sockaddr *addr; +int if_next_index(void); + +__private_extern__ int +if_next_index(void) { - register struct ifnet *ifp; - register struct ifaddr *ifa; + static int if_indexlim = 0; + int new_index; + + new_index = ++if_index; + if (if_index > if_indexlim) { + unsigned n; + int new_if_indexlim; + caddr_t new_ifnet_addrs; + caddr_t new_ifindex2ifnet; + caddr_t old_ifnet_addrs; + + old_ifnet_addrs = (caddr_t)ifnet_addrs; + if (ifnet_addrs == NULL) { + new_if_indexlim = INITIAL_IF_INDEXLIM; + } else { + new_if_indexlim = if_indexlim << 1; + } - for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_link.tqe_next) - if (ifp->if_flags & IFF_POINTOPOINT) - for (ifa = ifp->if_addrhead.tqh_first; ifa; - ifa = ifa->ifa_link.tqe_next) { - if (ifa->ifa_addr->sa_family != addr->sa_family) - continue; - if (ifa->ifa_dstaddr && equal(addr, ifa->ifa_dstaddr)) - return (ifa); + /* allocate space for the larger arrays */ + n = (2 * new_if_indexlim + 1) * sizeof(caddr_t); + new_ifnet_addrs = _MALLOC(n, M_IFADDR, M_WAITOK); + if (new_ifnet_addrs == NULL) { + --if_index; + return -1; + } + + new_ifindex2ifnet = new_ifnet_addrs + + new_if_indexlim * sizeof(caddr_t); + bzero(new_ifnet_addrs, n); + if (ifnet_addrs != NULL) { + /* copy the existing data */ + bcopy((caddr_t)ifnet_addrs, new_ifnet_addrs, + if_indexlim * sizeof(caddr_t)); + bcopy((caddr_t)ifindex2ifnet, + new_ifindex2ifnet, + (if_indexlim + 1) * sizeof(caddr_t)); + } + + /* switch to the new tables and size */ + ifnet_addrs = (struct ifaddr **)new_ifnet_addrs; + ifindex2ifnet = (struct ifnet **)new_ifindex2ifnet; + if_indexlim = new_if_indexlim; + + /* release the old data */ + if (old_ifnet_addrs != NULL) { + _FREE((caddr_t)old_ifnet_addrs, M_IFADDR); + } } - return ((struct ifaddr *)0); + return (new_index); } /* - * Find an interface on a specific network. If many, choice - * is most specific found. + * Create a clone network interface. */ -struct ifaddr * -ifa_ifwithnet(addr) - struct sockaddr *addr; +static int +if_clone_create(char *name, int len, void *params) { - register struct ifnet *ifp; - register struct ifaddr *ifa; - struct ifaddr *ifa_maybe = (struct ifaddr *) 0; - u_int af = addr->sa_family; - char *addr_data = addr->sa_data, *cplim; - + struct if_clone *ifc; + char *dp; + int wildcard; + u_int32_t bytoff, bitoff; + u_int32_t unit; + int err; + + ifc = if_clone_lookup(name, &unit); + if (ifc == NULL) + return (EINVAL); + + if (ifunit(name) != NULL) + return (EEXIST); + + bytoff = bitoff = 0; + wildcard = (unit == UINT32_MAX); /* - * AF_LINK addresses can be looked up directly by their index number, - * so do that if we can. + * Find a free unit if none was given. */ - if (af == AF_LINK) { - register struct sockaddr_dl *sdl = (struct sockaddr_dl *)addr; - if (sdl->sdl_index && sdl->sdl_index <= if_index) - return (ifnet_addrs[sdl->sdl_index - 1]); + if (wildcard) { + while ((bytoff < ifc->ifc_bmlen) + && (ifc->ifc_units[bytoff] == 0xff)) + bytoff++; + if (bytoff >= ifc->ifc_bmlen) + return (ENOSPC); + while ((ifc->ifc_units[bytoff] & (1 << bitoff)) != 0) + bitoff++; + unit = (bytoff << 3) + bitoff; } - /* - * Scan though each interface, looking for ones that have - * addresses in this address family. - */ - for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_link.tqe_next) { - for (ifa = ifp->if_addrhead.tqh_first; ifa; - ifa = ifa->ifa_link.tqe_next) { - register char *cp, *cp2, *cp3; + if (unit > ifc->ifc_maxunit) + return (ENXIO); - if (ifa->ifa_addr->sa_family != af) -next: continue; -#ifndef __APPLE__ -/* This breaks tunneling application trying to install a route with - * a specific subnet and the local address as the destination - * It's breaks binary compatibility with previous version of MacOS X - */ - if ( - -#if INET6 /* XXX: for maching gif tunnel dst as routing entry gateway */ - addr->sa_family != AF_INET6 && -#endif - ifp->if_flags & IFF_POINTOPOINT) { - /* - * This is a bit broken as it doesn't - * take into account that the remote end may - * be a single node in the network we are - * looking for. - * The trouble is that we don't know the - * netmask for the remote end. - */ - if (ifa->ifa_dstaddr != 0 - && equal(addr, ifa->ifa_dstaddr)) - return (ifa); - } else -#endif /* __APPLE__*/ - { - /* - * if we have a special address handler, - * then use it instead of the generic one. - */ - if (ifa->ifa_claim_addr) { - if ((*ifa->ifa_claim_addr)(ifa, addr)) { - return (ifa); - } else { - continue; - } - } + err = (*ifc->ifc_create)(ifc, unit, params); + if (err != 0) + return (err); - /* - * Scan all the bits in the ifa's address. - * If a bit dissagrees with what we are - * looking for, mask it with the netmask - * to see if it really matters. - * (A byte at a time) - */ - if (ifa->ifa_netmask == 0) - continue; - cp = addr_data; - cp2 = ifa->ifa_addr->sa_data; - cp3 = ifa->ifa_netmask->sa_data; - cplim = ifa->ifa_netmask->sa_len - + (char *)ifa->ifa_netmask; - while (cp3 < cplim) - if ((*cp++ ^ *cp2++) & *cp3++) - goto next; /* next address! */ - /* - * If the netmask of what we just found - * is more specific than what we had before - * (if we had one) then remember the new one - * before continuing to search - * for an even better one. - */ - if (ifa_maybe == 0 || - rn_refines((caddr_t)ifa->ifa_netmask, - (caddr_t)ifa_maybe->ifa_netmask)) - ifa_maybe = ifa; - } - } + if (!wildcard) { + bytoff = unit >> 3; + bitoff = unit - (bytoff << 3); } - return (ifa_maybe); -} - -/* - * Find an interface address specific to an interface best matching - * a given address. - */ -struct ifaddr * -ifaof_ifpforaddr(addr, ifp) - struct sockaddr *addr; - register struct ifnet *ifp; -{ - register struct ifaddr *ifa; - register char *cp, *cp2, *cp3; - register char *cplim; - struct ifaddr *ifa_maybe = 0; - u_int af = addr->sa_family; - if (af >= AF_MAX) - return (0); - for (ifa = ifp->if_addrhead.tqh_first; ifa; - ifa = ifa->ifa_link.tqe_next) { - if (ifa->ifa_addr->sa_family != af) - continue; - if (ifa_maybe == 0) - ifa_maybe = ifa; - if (ifa->ifa_netmask == 0) { - if (equal(addr, ifa->ifa_addr) || - (ifa->ifa_dstaddr && equal(addr, ifa->ifa_dstaddr))) - return (ifa); - continue; - } - if (ifp->if_flags & IFF_POINTOPOINT) { - if (equal(addr, ifa->ifa_dstaddr)) - return (ifa); - } else { - cp = addr->sa_data; - cp2 = ifa->ifa_addr->sa_data; - cp3 = ifa->ifa_netmask->sa_data; - cplim = ifa->ifa_netmask->sa_len + (char *)ifa->ifa_netmask; - for (; cp3 < cplim; cp3++) - if ((*cp++ ^ *cp2++) & *cp3) - break; - if (cp3 == cplim) - return (ifa); + /* + * Allocate the unit in the bitmap. + */ + KASSERT((ifc->ifc_units[bytoff] & (1 << bitoff)) == 0, + ("%s: bit is already set", __func__)); + ifc->ifc_units[bytoff] |= (1 << bitoff); + + /* In the wildcard case, we need to update the name. */ + if (wildcard) { + for (dp = name; *dp != '\0'; dp++); + if (snprintf(dp, len - (dp-name), "%d", unit) > + len - (dp-name) - 1) { + /* + * This can only be a programmer error and + * there's no straightforward way to recover if + * it happens. + */ + panic("%s: interface name too long", __func__); + /* NOTREACHED */ } + } - return (ifa_maybe); -} -#include + return (0); +} /* - * Default action when installing a route with a Link Level gateway. - * Lookup an appropriate real ifa to point to. - * This should be moved to /sys/net/link.c eventually. + * Destroy a clone network interface. */ -static void -link_rtrequest(cmd, rt, sa) - int cmd; - register struct rtentry *rt; - struct sockaddr *sa; +static int +if_clone_destroy(const char *name) { - register struct ifaddr *ifa; - struct sockaddr *dst; + struct if_clone *ifc; struct ifnet *ifp; + int bytoff, bitoff; + u_int32_t unit; - if (cmd != RTM_ADD || ((ifa = rt->rt_ifa) == 0) || - ((ifp = ifa->ifa_ifp) == 0) || ((dst = rt_key(rt)) == 0)) - return; - ifa = ifaof_ifpforaddr(dst, ifp); - if (ifa) { - rtsetifa(rt, ifa); - if (ifa->ifa_rtrequest && ifa->ifa_rtrequest != link_rtrequest) - ifa->ifa_rtrequest(cmd, rt, sa); - } -} + ifc = if_clone_lookup(name, &unit); + if (ifc == NULL) + return (EINVAL); -/* - * Mark an interface down and notify protocols of - * the transition. - * NOTE: must be called at splnet or eqivalent. - */ -void -if_unroute(ifp, flag, fam) - register struct ifnet *ifp; - int flag, fam; -{ - register struct ifaddr *ifa; - - ifp->if_flags &= ~flag; - getmicrotime(&ifp->if_lastchange); - TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) - if (fam == PF_UNSPEC || (fam == ifa->ifa_addr->sa_family)) - pfctlinput(PRC_IFDOWN, ifa->ifa_addr); - if_qflush(&ifp->if_snd); - rt_ifmsg(ifp); + if (unit < ifc->ifc_minifs) + return (EINVAL); + + ifp = ifunit(name); + if (ifp == NULL) + return (ENXIO); + + if (ifc->ifc_destroy == NULL) + return (EOPNOTSUPP); + + (*ifc->ifc_destroy)(ifp); + + /* + * Compute offset in the bitmap and deallocate the unit. + */ + bytoff = unit >> 3; + bitoff = unit - (bytoff << 3); + KASSERT((ifc->ifc_units[bytoff] & (1 << bitoff)) != 0, + ("%s: bit is already cleared", __func__)); + ifc->ifc_units[bytoff] &= ~(1 << bitoff); + return (0); } /* - * Mark an interface up and notify protocols of - * the transition. - * NOTE: must be called at splnet or eqivalent. + * Look up a network interface cloner. */ -void -if_route(ifp, flag, fam) - register struct ifnet *ifp; - int flag, fam; + +__private_extern__ struct if_clone * +if_clone_lookup(const char *name, u_int32_t *unitp) { - register struct ifaddr *ifa; + struct if_clone *ifc; + const char *cp; + size_t i; - ifp->if_flags |= flag; - getmicrotime(&ifp->if_lastchange); - TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) - if (fam == PF_UNSPEC || (fam == ifa->ifa_addr->sa_family)) - pfctlinput(PRC_IFUP, ifa->ifa_addr); - rt_ifmsg(ifp); + for (ifc = LIST_FIRST(&if_cloners); ifc != NULL;) { + for (cp = name, i = 0; i < ifc->ifc_namelen; i++, cp++) { + if (ifc->ifc_name[i] != *cp) + goto next_ifc; + } + goto found_name; + next_ifc: + ifc = LIST_NEXT(ifc, ifc_list); + } -#if INET6 - if (ip6_auto_on) /* Only if IPv6 is on on configured on on all ifs */ - in6_if_up(ifp); -#endif + /* No match. */ + return ((struct if_clone *)NULL); + + found_name: + if (*cp == '\0') { + i = 0xffff; + } else { + for (i = 0; *cp != '\0'; cp++) { + if (*cp < '0' || *cp > '9') { + /* Bogus unit number. */ + return (NULL); + } + i = (i * 10) + (*cp - '0'); + } + } + + if (unitp != NULL) + *unitp = i; + return (ifc); } /* - * Mark an interface down and notify protocols of - * the transition. - * NOTE: must be called at splnet or eqivalent. + * Register a network interface cloner. */ -void -if_down(ifp) - register struct ifnet *ifp; +int +if_clone_attach(struct if_clone *ifc) { + int bytoff, bitoff; + int err; + int len, maxclone; + u_int32_t unit; + + KASSERT(ifc->ifc_minifs - 1 <= ifc->ifc_maxunit, + ("%s: %s requested more units then allowed (%d > %d)", + __func__, ifc->ifc_name, ifc->ifc_minifs, + ifc->ifc_maxunit + 1)); + /* + * Compute bitmap size and allocate it. + */ + maxclone = ifc->ifc_maxunit + 1; + len = maxclone >> 3; + if ((len << 3) < maxclone) + len++; + ifc->ifc_units = _MALLOC(len, M_CLONE, M_WAITOK | M_ZERO); + if (ifc->ifc_units == NULL) + return ENOBUFS; + bzero(ifc->ifc_units, len); + ifc->ifc_bmlen = len; + + LIST_INSERT_HEAD(&if_cloners, ifc, ifc_list); + if_cloners_count++; + + for (unit = 0; unit < ifc->ifc_minifs; unit++) { + err = (*ifc->ifc_create)(ifc, unit, NULL); + KASSERT(err == 0, + ("%s: failed to create required interface %s%d", + __func__, ifc->ifc_name, unit)); + + /* Allocate the unit in the bitmap. */ + bytoff = unit >> 3; + bitoff = unit - (bytoff << 3); + ifc->ifc_units[bytoff] |= (1 << bitoff); + } - if_unroute(ifp, IFF_UP, AF_UNSPEC); + return 0; } /* - * Mark an interface up and notify protocols of - * the transition. - * NOTE: must be called at splnet or eqivalent. + * Unregister a network interface cloner. */ void -if_up(ifp) - register struct ifnet *ifp; +if_clone_detach(struct if_clone *ifc) { - if_route(ifp, IFF_UP, AF_UNSPEC); + LIST_REMOVE(ifc, ifc_list); + FREE(ifc->ifc_units, M_CLONE); + if_cloners_count--; } +#if IF_CLONE_LIST /* - * Flush an interface queue. + * Provide list of interface cloners to userspace. */ -static void -if_qflush(ifq) - register struct ifqueue *ifq; +static int +if_clone_list(int count, int * total, user_addr_t dst) { - register struct mbuf *m, *n; + char outbuf[IFNAMSIZ]; + struct if_clone *ifc; + int error = 0; + + *total = if_cloners_count; + if (dst == USER_ADDR_NULL) { + /* Just asking how many there are. */ + return (0); + } + + if (count < 0) + return (EINVAL); + + count = (if_cloners_count < count) ? if_cloners_count : count; + + for (ifc = LIST_FIRST(&if_cloners); ifc != NULL && count != 0; + ifc = LIST_NEXT(ifc, ifc_list), count--, dst += IFNAMSIZ) { + strlcpy(outbuf, ifc->ifc_name, IFNAMSIZ); + error = copyout(outbuf, dst, IFNAMSIZ); + if (error) + break; + } + + return (error); +} +#endif /* IF_CLONE_LIST */ + +/* + * Similar to ifa_ifwithaddr, except that this is IPv4 specific + * and that it matches only the local (not broadcast) address. + */ +__private_extern__ struct in_ifaddr * +ifa_foraddr(unsigned int addr) +{ + return (ifa_foraddr_scoped(addr, IFSCOPE_NONE)); +} + +/* + * Similar to ifa_foraddr, except with the added interface scope + * constraint (unless the caller passes in IFSCOPE_NONE in which + * case there is no scope restriction). + */ +__private_extern__ struct in_ifaddr * +ifa_foraddr_scoped(unsigned int addr, unsigned int scope) +{ + struct in_ifaddr *ia = NULL; + + lck_rw_lock_shared(in_ifaddr_rwlock); + TAILQ_FOREACH(ia, INADDR_HASH(addr), ia_hash) { + IFA_LOCK_SPIN(&ia->ia_ifa); + if (ia->ia_addr.sin_addr.s_addr == addr && + (scope == IFSCOPE_NONE || ia->ia_ifp->if_index == scope)) { + IFA_ADDREF_LOCKED(&ia->ia_ifa); /* for caller */ + IFA_UNLOCK(&ia->ia_ifa); + break; + } + IFA_UNLOCK(&ia->ia_ifa); + } + lck_rw_done(in_ifaddr_rwlock); + return (ia); +} + +#if INET6 +/* + * Similar to ifa_foraddr, except that this for IPv6. + */ +__private_extern__ struct in6_ifaddr * +ifa_foraddr6(struct in6_addr *addr6) +{ + return (ifa_foraddr6_scoped(addr6, IFSCOPE_NONE)); +} + +__private_extern__ struct in6_ifaddr * +ifa_foraddr6_scoped(struct in6_addr *addr6, unsigned int scope) +{ + struct in6_ifaddr *ia = NULL; + + lck_rw_lock_shared(&in6_ifaddr_rwlock); + for (ia = in6_ifaddrs; ia; ia = ia->ia_next) { + IFA_LOCK(&ia->ia_ifa); + if (IN6_ARE_ADDR_EQUAL(&ia->ia_addr.sin6_addr, addr6) && + (scope == IFSCOPE_NONE || ia->ia_ifp->if_index == scope)) { + IFA_ADDREF_LOCKED(&ia->ia_ifa); /* for caller */ + IFA_UNLOCK(&ia->ia_ifa); + break; + } + IFA_UNLOCK(&ia->ia_ifa); + } + lck_rw_done(&in6_ifaddr_rwlock); + + return (ia); +} +#endif /* INET6 */ + +/* + * Return the first (primary) address of a given family on an interface. + */ +__private_extern__ struct ifaddr * +ifa_ifpgetprimary(struct ifnet *ifp, int family) +{ + struct ifaddr *ifa; + + ifnet_lock_shared(ifp); + TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { + IFA_LOCK_SPIN(ifa); + if (ifa->ifa_addr->sa_family == family) { + IFA_ADDREF_LOCKED(ifa); /* for caller */ + IFA_UNLOCK(ifa); + break; + } + IFA_UNLOCK(ifa); + } + ifnet_lock_done(ifp); + + return (ifa); +} + +/* + * Locate an interface based on a complete address. + */ +/*ARGSUSED*/ +struct ifaddr * +ifa_ifwithaddr(const struct sockaddr *addr) +{ + struct ifnet *ifp; + struct ifaddr *ifa; + struct ifaddr *result = NULL; + +#define equal(a1, a2) \ + (bcmp((const void*)(a1), (const void*)(a2), \ + ((const struct sockaddr *)(a1))->sa_len) == 0) + + ifnet_head_lock_shared(); + for (ifp = ifnet_head.tqh_first; ifp && !result; + ifp = ifp->if_link.tqe_next) { + ifnet_lock_shared(ifp); + for (ifa = ifp->if_addrhead.tqh_first; ifa; + ifa = ifa->ifa_link.tqe_next) { + IFA_LOCK_SPIN(ifa); + if (ifa->ifa_addr->sa_family != addr->sa_family) { + IFA_UNLOCK(ifa); + continue; + } + if (equal(addr, ifa->ifa_addr)) { + result = ifa; + IFA_ADDREF_LOCKED(ifa); /* for caller */ + IFA_UNLOCK(ifa); + break; + } + if ((ifp->if_flags & IFF_BROADCAST) && + ifa->ifa_broadaddr != NULL && + /* IP6 doesn't have broadcast */ + ifa->ifa_broadaddr->sa_len != 0 && + equal(ifa->ifa_broadaddr, addr)) { + result = ifa; + IFA_ADDREF_LOCKED(ifa); /* for caller */ + IFA_UNLOCK(ifa); + break; + } + IFA_UNLOCK(ifa); + } + ifnet_lock_done(ifp); + } + ifnet_head_done(); + + return (result); +} +/* + * Locate the point to point interface with a given destination address. + */ +/*ARGSUSED*/ +struct ifaddr * +ifa_ifwithdstaddr(const struct sockaddr *addr) +{ + struct ifnet *ifp; + struct ifaddr *ifa; + struct ifaddr *result = NULL; + + ifnet_head_lock_shared(); + for (ifp = ifnet_head.tqh_first; ifp && !result; + ifp = ifp->if_link.tqe_next) { + if ((ifp->if_flags & IFF_POINTOPOINT)) { + ifnet_lock_shared(ifp); + for (ifa = ifp->if_addrhead.tqh_first; ifa; + ifa = ifa->ifa_link.tqe_next) { + IFA_LOCK_SPIN(ifa); + if (ifa->ifa_addr->sa_family != + addr->sa_family) { + IFA_UNLOCK(ifa); + continue; + } + if (ifa->ifa_dstaddr && + equal(addr, ifa->ifa_dstaddr)) { + result = ifa; + IFA_ADDREF_LOCKED(ifa); /* for caller */ + IFA_UNLOCK(ifa); + break; + } + IFA_UNLOCK(ifa); + } + ifnet_lock_done(ifp); + } + } + ifnet_head_done(); + return (result); +} + +/* + * Locate the source address of an interface based on a complete address. + */ +struct ifaddr * +ifa_ifwithaddr_scoped(const struct sockaddr *addr, unsigned int ifscope) +{ + struct ifaddr *result = NULL; + struct ifnet *ifp; + + if (ifscope == IFSCOPE_NONE) + return (ifa_ifwithaddr(addr)); + + ifnet_head_lock_shared(); + if (ifscope > (unsigned int)if_index) { + ifnet_head_done(); + return (NULL); + } + + ifp = ifindex2ifnet[ifscope]; + if (ifp != NULL) { + struct ifaddr *ifa = NULL; + + /* + * This is suboptimal; there should be a better way + * to search for a given address of an interface + * for any given address family. + */ + ifnet_lock_shared(ifp); + for (ifa = ifp->if_addrhead.tqh_first; ifa != NULL; + ifa = ifa->ifa_link.tqe_next) { + IFA_LOCK_SPIN(ifa); + if (ifa->ifa_addr->sa_family != addr->sa_family) { + IFA_UNLOCK(ifa); + continue; + } + if (equal(addr, ifa->ifa_addr)) { + result = ifa; + IFA_ADDREF_LOCKED(ifa); /* for caller */ + IFA_UNLOCK(ifa); + break; + } + if ((ifp->if_flags & IFF_BROADCAST) && + ifa->ifa_broadaddr != NULL && + /* IP6 doesn't have broadcast */ + ifa->ifa_broadaddr->sa_len != 0 && + equal(ifa->ifa_broadaddr, addr)) { + result = ifa; + IFA_ADDREF_LOCKED(ifa); /* for caller */ + IFA_UNLOCK(ifa); + break; + } + IFA_UNLOCK(ifa); + } + ifnet_lock_done(ifp); + } + ifnet_head_done(); + + return (result); +} + +struct ifaddr * +ifa_ifwithnet(const struct sockaddr *addr) +{ + return (ifa_ifwithnet_common(addr, IFSCOPE_NONE)); +} + +struct ifaddr * +ifa_ifwithnet_scoped(const struct sockaddr *addr, unsigned int ifscope) +{ + return (ifa_ifwithnet_common(addr, ifscope)); +} + +/* + * Find an interface on a specific network. If many, choice + * is most specific found. + */ +static struct ifaddr * +ifa_ifwithnet_common(const struct sockaddr *addr, unsigned int ifscope) +{ + struct ifnet *ifp; + struct ifaddr *ifa = NULL; + struct ifaddr *ifa_maybe = NULL; + u_int af = addr->sa_family; + const char *addr_data = addr->sa_data, *cplim; + +#if INET6 + if ((af != AF_INET && af != AF_INET6) || + (af == AF_INET && !ip_doscopedroute) || + (af == AF_INET6 && !ip6_doscopedroute)) +#else + if (af != AF_INET || !ip_doscopedroute) +#endif /* !INET6 */ + ifscope = IFSCOPE_NONE; + + ifnet_head_lock_shared(); + /* + * AF_LINK addresses can be looked up directly by their index number, + * so do that if we can. + */ + if (af == AF_LINK) { + const struct sockaddr_dl *sdl = (const struct sockaddr_dl *)addr; + if (sdl->sdl_index && sdl->sdl_index <= if_index) { + ifa = ifnet_addrs[sdl->sdl_index - 1]; + if (ifa != NULL) + IFA_ADDREF(ifa); + + ifnet_head_done(); + return (ifa); + } + } + + /* + * Scan though each interface, looking for ones that have + * addresses in this address family. + */ + for (ifp = ifnet_head.tqh_first; ifp; ifp = ifp->if_link.tqe_next) { + ifnet_lock_shared(ifp); + for (ifa = ifp->if_addrhead.tqh_first; ifa; + ifa = ifa->ifa_link.tqe_next) { + const char *cp, *cp2, *cp3; + + IFA_LOCK(ifa); + if (ifa->ifa_addr == NULL || + ifa->ifa_addr->sa_family != af) { +next: + IFA_UNLOCK(ifa); + continue; + } +#ifndef __APPLE__ +/* This breaks tunneling application trying to install a route with + * a specific subnet and the local address as the destination + * It's breaks binary compatibility with previous version of MacOS X + */ + if ( +#if INET6 /* XXX: for maching gif tunnel dst as routing entry gateway */ + addr->sa_family != AF_INET6 && +#endif + ifp->if_flags & IFF_POINTOPOINT) { + /* + * This is a bit broken as it doesn't + * take into account that the remote end may + * be a single node in the network we are + * looking for. + * The trouble is that we don't know the + * netmask for the remote end. + */ + if (ifa->ifa_dstaddr != 0 && + equal(addr, ifa->ifa_dstaddr)) { + IFA_ADDREF_LOCKED(ifa); + IFA_UNLOCK(ifa); + break; + } + IFA_UNLOCK(ifa); + } else +#endif /* __APPLE__*/ + { + /* + * If we're looking up with a scope, + * find using a matching interface. + */ + if (ifscope != IFSCOPE_NONE && + ifp->if_index != ifscope) { + IFA_UNLOCK(ifa); + continue; + } + + /* + * Scan all the bits in the ifa's address. + * If a bit dissagrees with what we are + * looking for, mask it with the netmask + * to see if it really matters. + * (A byte at a time) + */ + if (ifa->ifa_netmask == 0) { + IFA_UNLOCK(ifa); + continue; + } + cp = addr_data; + cp2 = ifa->ifa_addr->sa_data; + cp3 = ifa->ifa_netmask->sa_data; + cplim = ifa->ifa_netmask->sa_len + + (char *)ifa->ifa_netmask; + while (cp3 < cplim) + if ((*cp++ ^ *cp2++) & *cp3++) + goto next; /* next address! */ + /* + * If the netmask of what we just found + * is more specific than what we had before + * (if we had one) then remember the new one + * before continuing to search + * for an even better one. + */ + if (ifa_maybe == NULL || + rn_refines((caddr_t)ifa->ifa_netmask, + (caddr_t)ifa_maybe->ifa_netmask)) { + IFA_ADDREF_LOCKED(ifa); /* ifa_maybe */ + IFA_UNLOCK(ifa); + if (ifa_maybe != NULL) + IFA_REMREF(ifa_maybe); + ifa_maybe = ifa; + } else { + IFA_UNLOCK(ifa); + } + } + IFA_LOCK_ASSERT_NOTHELD(ifa); + } + ifnet_lock_done(ifp); + + if (ifa != NULL) + break; + } + ifnet_head_done(); + + if (ifa == NULL) + ifa = ifa_maybe; + else if (ifa_maybe != NULL) + IFA_REMREF(ifa_maybe); + + return (ifa); +} + +/* + * Find an interface address specific to an interface best matching + * a given address. + */ +struct ifaddr * +ifaof_ifpforaddr(const struct sockaddr *addr, struct ifnet *ifp) +{ + struct ifaddr *ifa = NULL; + const char *cp, *cp2, *cp3; + char *cplim; + struct ifaddr *ifa_maybe = NULL; + struct ifaddr *better_ifa_maybe = NULL; + u_int af = addr->sa_family; + + if (af >= AF_MAX) + return (NULL); + + ifnet_lock_shared(ifp); + for (ifa = ifp->if_addrhead.tqh_first; ifa; + ifa = ifa->ifa_link.tqe_next) { + IFA_LOCK(ifa); + if (ifa->ifa_addr->sa_family != af) { + IFA_UNLOCK(ifa); + continue; + } + if (ifa_maybe == NULL) { + IFA_ADDREF_LOCKED(ifa); /* for ifa_maybe */ + ifa_maybe = ifa; + } + if (ifa->ifa_netmask == 0) { + if (equal(addr, ifa->ifa_addr) || (ifa->ifa_dstaddr && + equal(addr, ifa->ifa_dstaddr))) { + IFA_ADDREF_LOCKED(ifa); /* for caller */ + IFA_UNLOCK(ifa); + break; + } + IFA_UNLOCK(ifa); + continue; + } + if (ifp->if_flags & IFF_POINTOPOINT) { + if (ifa->ifa_dstaddr && equal(addr, ifa->ifa_dstaddr)) { + IFA_ADDREF_LOCKED(ifa); /* for caller */ + IFA_UNLOCK(ifa); + break; + } + } else { + if (equal(addr, ifa->ifa_addr)) { + /* exact match */ + IFA_ADDREF_LOCKED(ifa); /* for caller */ + IFA_UNLOCK(ifa); + break; + } + cp = addr->sa_data; + cp2 = ifa->ifa_addr->sa_data; + cp3 = ifa->ifa_netmask->sa_data; + cplim = ifa->ifa_netmask->sa_len + + (char *)ifa->ifa_netmask; + for (; cp3 < cplim; cp3++) + if ((*cp++ ^ *cp2++) & *cp3) + break; + if (cp3 == cplim) { + /* subnet match */ + if (better_ifa_maybe == NULL) { + /* for better_ifa_maybe */ + IFA_ADDREF_LOCKED(ifa); + better_ifa_maybe = ifa; + } + } + } + IFA_UNLOCK(ifa); + } + + if (ifa == NULL) { + if (better_ifa_maybe != NULL) { + ifa = better_ifa_maybe; + better_ifa_maybe = NULL; + } else { + ifa = ifa_maybe; + ifa_maybe = NULL; + } + } + + ifnet_lock_done(ifp); + + if (better_ifa_maybe != NULL) + IFA_REMREF(better_ifa_maybe); + if (ifa_maybe != NULL) + IFA_REMREF(ifa_maybe); + + return (ifa); +} + +#include + +/* + * Default action when installing a route with a Link Level gateway. + * Lookup an appropriate real ifa to point to. + * This should be moved to /sys/net/link.c eventually. + */ +void +link_rtrequest(int cmd, struct rtentry *rt, struct sockaddr *sa) +{ + struct ifaddr *ifa; + struct sockaddr *dst; + struct ifnet *ifp; + void (*ifa_rtrequest)(int, struct rtentry *, struct sockaddr *); + + lck_mtx_assert(rnh_lock, LCK_MTX_ASSERT_OWNED); + RT_LOCK_ASSERT_HELD(rt); + + if (cmd != RTM_ADD || ((ifa = rt->rt_ifa) == 0) || + ((ifp = ifa->ifa_ifp) == 0) || ((dst = rt_key(rt)) == 0)) + return; + + /* Become a regular mutex, just in case */ + RT_CONVERT_LOCK(rt); + + ifa = ifaof_ifpforaddr(dst, ifp); + if (ifa) { + rtsetifa(rt, ifa); + IFA_LOCK_SPIN(ifa); + ifa_rtrequest = ifa->ifa_rtrequest; + IFA_UNLOCK(ifa); + if (ifa_rtrequest != NULL && ifa_rtrequest != link_rtrequest) + ifa_rtrequest(cmd, rt, sa); + IFA_REMREF(ifa); + } +} + +/* + * if_updown will set the interface up or down. It will + * prevent other up/down events from occurring until this + * up/down event has completed. + * + * Caller must lock ifnet. This function will drop the + * lock. This allows ifnet_set_flags to set the rest of + * the flags after we change the up/down state without + * dropping the interface lock between setting the + * up/down state and updating the rest of the flags. + */ +__private_extern__ void +if_updown( + struct ifnet *ifp, + int up) +{ + int i; + struct ifaddr **ifa; + struct timespec tv; + + /* Wait until no one else is changing the up/down state */ + while ((ifp->if_eflags & IFEF_UPDOWNCHANGE) != 0) { + tv.tv_sec = 0; + tv.tv_nsec = NSEC_PER_SEC / 10; + ifnet_lock_done(ifp); + msleep(&ifp->if_eflags, NULL, 0, "if_updown", &tv); + ifnet_lock_exclusive(ifp); + } + + /* Verify that the interface isn't already in the right state */ + if ((!up && (ifp->if_flags & IFF_UP) == 0) || + (up && (ifp->if_flags & IFF_UP) == IFF_UP)) { + return; + } + + /* Indicate that the up/down state is changing */ + ifp->if_eflags |= IFEF_UPDOWNCHANGE; + + /* Mark interface up or down */ + if (up) { + ifp->if_flags |= IFF_UP; + } + else { + ifp->if_flags &= ~IFF_UP; + } + + ifnet_touch_lastchange(ifp); + + /* Drop the lock to notify addresses and route */ + ifnet_lock_done(ifp); + if (ifnet_get_address_list(ifp, &ifa) == 0) { + for (i = 0; ifa[i] != 0; i++) { + pfctlinput(up ? PRC_IFUP : PRC_IFDOWN, ifa[i]->ifa_addr); + } + ifnet_free_address_list(ifa); + } + rt_ifmsg(ifp); + + /* Aquire the lock to clear the changing flag and flush the send queue */ + ifnet_lock_exclusive(ifp); + if (!up) + if_qflush(&ifp->if_snd); + ifp->if_eflags &= ~IFEF_UPDOWNCHANGE; + wakeup(&ifp->if_eflags); + + return; +} + +/* + * Mark an interface down and notify protocols of + * the transition. + */ +void +if_down( + struct ifnet *ifp) +{ + ifnet_lock_exclusive(ifp); + if_updown(ifp, 0); + ifnet_lock_done(ifp); +} + +/* + * Mark an interface up and notify protocols of + * the transition. + */ +void +if_up( + struct ifnet *ifp) +{ + ifnet_lock_exclusive(ifp); + if_updown(ifp, 1); + ifnet_lock_done(ifp); +} + +/* + * Flush an interface queue. + */ +static void +if_qflush(struct ifqueue *ifq) +{ + struct mbuf *m, *n; n = ifq->ifq_head; while ((m = n) != 0) { n = m->m_act; m_freem(m); } - ifq->ifq_head = 0; - ifq->ifq_tail = 0; + ifq->ifq_head = NULL; + ifq->ifq_tail = NULL; ifq->ifq_len = 0; } @@ -565,19 +1329,19 @@ ifunit(const char *name) len = strlen(name); if (len < 2 || len > IFNAMSIZ) - return NULL; + return (NULL); cp = name + len - 1; c = *cp; if (c < '0' || c > '9') - return NULL; /* trailing garbage */ + return (NULL); /* trailing garbage */ unit = 0; m = 1; do { if (cp == name) - return NULL; /* no interface name */ + return (NULL); /* no interface name */ unit += (c - '0') * m; if (unit > 1000000) - return NULL; /* number is unreasonable */ + return (NULL); /* number is unreasonable */ m *= 10; c = *--cp; } while (c >= '0' && c <= '9'); @@ -587,12 +1351,14 @@ ifunit(const char *name) /* * Now search all the interfaces for this name/number */ - for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_link.tqe_next) { - if (strcmp(ifp->if_name, namebuf)) + ifnet_head_lock_shared(); + TAILQ_FOREACH(ifp, &ifnet_head, if_link) { + if (strncmp(ifp->if_name, namebuf, len)) continue; if (unit == ifp->if_unit) break; } + ifnet_head_done(); return (ifp); } @@ -602,15 +1368,14 @@ ifunit(const char *name) * interface structure pointer. */ struct ifnet * -if_withname(sa) - struct sockaddr *sa; +if_withname(struct sockaddr *sa) { char ifname[IFNAMSIZ+1]; struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa; if ( (sa->sa_family != AF_LINK) || (sdl->sdl_nlen == 0) || (sdl->sdl_nlen > IFNAMSIZ) ) - return NULL; + return (NULL); /* * ifunit wants a null-terminated name. It may not be null-terminated @@ -621,7 +1386,7 @@ if_withname(sa) bcopy(sdl->sdl_data, ifname, sdl->sdl_nlen); ifname[sdl->sdl_nlen] = '\0'; - return ifunit(ifname); + return (ifunit(ifname)); } @@ -629,272 +1394,390 @@ if_withname(sa) * Interface ioctls. */ int -ifioctl(so, cmd, data, p) - struct socket *so; - u_long cmd; - caddr_t data; - struct proc *p; -{ - register struct ifnet *ifp; - register struct ifreq *ifr; +ifioctl(struct socket *so, u_long cmd, caddr_t data, struct proc *p) +{ + struct ifnet *ifp; + struct ifreq *ifr; struct ifstat *ifs; int error = 0; short oif_flags; struct kev_msg ev_msg; struct net_event_data ev_data; + bzero(&ev_data, sizeof(struct net_event_data)); + bzero(&ev_msg, sizeof(struct kev_msg)); switch (cmd) { - - case SIOCGIFCONF: - case OSIOCGIFCONF: - return (ifconf(cmd, data)); + case OSIOCGIFCONF32: + case SIOCGIFCONF32: { + struct ifconf32 *ifc = (struct ifconf32 *)data; + return (ifconf(cmd, CAST_USER_ADDR_T(ifc->ifc_req), + &ifc->ifc_len)); + /* NOTREACHED */ + } + case SIOCGIFCONF64: + case OSIOCGIFCONF64: { + struct ifconf64 *ifc = (struct ifconf64 *)data; + return (ifconf(cmd, ifc->ifc_req, &ifc->ifc_len)); + /* NOTREACHED */ + } } ifr = (struct ifreq *)data; + switch (cmd) { + case SIOCIFCREATE: + case SIOCIFCREATE2: + error = proc_suser(p); + if (error) + return (error); + return if_clone_create(ifr->ifr_name, sizeof(ifr->ifr_name), + cmd == SIOCIFCREATE2 ? ifr->ifr_data : NULL); + case SIOCIFDESTROY: + error = proc_suser(p); + if (error) + return (error); + return if_clone_destroy(ifr->ifr_name); +#if IF_CLONE_LIST + case SIOCIFGCLONERS32: { + struct if_clonereq32 *ifcr = (struct if_clonereq32 *)data; + return (if_clone_list(ifcr->ifcr_count, &ifcr->ifcr_total, + CAST_USER_ADDR_T(ifcr->ifcru_buffer))); + /* NOTREACHED */ + + } + case SIOCIFGCLONERS64: { + struct if_clonereq64 *ifcr = (struct if_clonereq64 *)data; + return (if_clone_list(ifcr->ifcr_count, &ifcr->ifcr_total, + ifcr->ifcru_buffer)); + /* NOTREACHED */ + } +#endif /* IF_CLONE_LIST */ + } + + /* + * ioctls which require ifp. Note that we acquire dlil_ifnet_lock + * here to ensure that the ifnet, if found, has been fully attached. + */ + dlil_if_lock(); ifp = ifunit(ifr->ifr_name); - if (ifp == 0) + dlil_if_unlock(); + if (ifp == NULL) return (ENXIO); - switch (cmd) { + switch (cmd) { case SIOCGIFFLAGS: + ifnet_lock_shared(ifp); ifr->ifr_flags = ifp->if_flags; + ifnet_lock_done(ifp); break; + case SIOCGIFCAP: + ifnet_lock_shared(ifp); + ifr->ifr_reqcap = ifp->if_capabilities; + ifr->ifr_curcap = ifp->if_capenable; + ifnet_lock_done(ifp); + break; + +#if CONFIG_MACF_NET + case SIOCGIFMAC: + error = mac_ifnet_label_get(kauth_cred_get(), ifr, ifp); + break; +#endif case SIOCGIFMETRIC: + ifnet_lock_shared(ifp); ifr->ifr_metric = ifp->if_metric; + ifnet_lock_done(ifp); break; case SIOCGIFMTU: + ifnet_lock_shared(ifp); ifr->ifr_mtu = ifp->if_mtu; + ifnet_lock_done(ifp); break; case SIOCGIFPHYS: + ifnet_lock_shared(ifp); ifr->ifr_phys = ifp->if_physical; + ifnet_lock_done(ifp); break; case SIOCSIFFLAGS: - error = suser(p->p_ucred, &p->p_acflag); - if (error) - return (error); -#ifndef __APPLE__ - if (ifp->if_flags & IFF_SMART) { - /* Smart drivers twiddle their own routes */ - } else -#endif - if (ifp->if_flags & IFF_UP && - (ifr->ifr_flags & IFF_UP) == 0) { - int s = splimp(); - if_down(ifp); - splx(s); - } else if (ifr->ifr_flags & IFF_UP && - (ifp->if_flags & IFF_UP) == 0) { - int s = splimp(); - if_up(ifp); - splx(s); - } - ifp->if_flags = (ifp->if_flags & IFF_CANTCHANGE) | - (ifr->ifr_flags &~ IFF_CANTCHANGE); + error = proc_suser(p); + if (error != 0) + break; - error = dlil_ioctl(so->so_proto->pr_domain->dom_family, - ifp, cmd, (caddr_t) data); + (void) ifnet_set_flags(ifp, ifr->ifr_flags, + (u_int16_t)~IFF_CANTCHANGE); - if (error == 0) { - ev_msg.vendor_code = KEV_VENDOR_APPLE; - ev_msg.kev_class = KEV_NETWORK_CLASS; - ev_msg.kev_subclass = KEV_DL_SUBCLASS; - - ev_msg.event_code = KEV_DL_SIFFLAGS; - strncpy(&ev_data.if_name[0], ifp->if_name, IFNAMSIZ); - ev_data.if_family = ifp->if_family; - ev_data.if_unit = (unsigned long) ifp->if_unit; - ev_msg.dv[0].data_length = sizeof(struct net_event_data); - ev_msg.dv[0].data_ptr = &ev_data; - ev_msg.dv[1].data_length = 0; - kev_post_msg(&ev_msg); + /* + * Note that we intentionally ignore any error from below + * for the SIOCSIFFLAGS case. + */ + (void) ifnet_ioctl(ifp, so->so_proto->pr_domain->dom_family, + cmd, data); + + /* + * Send the event even upon error from the driver because + * we changed the flags. + */ + ev_msg.vendor_code = KEV_VENDOR_APPLE; + ev_msg.kev_class = KEV_NETWORK_CLASS; + ev_msg.kev_subclass = KEV_DL_SUBCLASS; + + ev_msg.event_code = KEV_DL_SIFFLAGS; + strlcpy(&ev_data.if_name[0], ifp->if_name, IFNAMSIZ); + ev_data.if_family = ifp->if_family; + ev_data.if_unit = (u_int32_t) ifp->if_unit; + ev_msg.dv[0].data_length = sizeof(struct net_event_data); + ev_msg.dv[0].data_ptr = &ev_data; + ev_msg.dv[1].data_length = 0; + kev_post_msg(&ev_msg); + + ifnet_touch_lastchange(ifp); + break; + + case SIOCSIFCAP: + error = proc_suser(p); + if (error != 0) + break; + + if ((ifr->ifr_reqcap & ~ifp->if_capabilities)) { + error = EINVAL; + break; } - getmicrotime(&ifp->if_lastchange); + error = ifnet_ioctl(ifp, so->so_proto->pr_domain->dom_family, + cmd, data); + + ifnet_touch_lastchange(ifp); break; +#if CONFIG_MACF_NET + case SIOCSIFMAC: + error = mac_ifnet_label_set(kauth_cred_get(), ifr, ifp); + break; +#endif case SIOCSIFMETRIC: - error = suser(p->p_ucred, &p->p_acflag); - if (error) - return (error); - ifp->if_metric = ifr->ifr_metric; + error = proc_suser(p); + if (error != 0) + break; + ifp->if_metric = ifr->ifr_metric; ev_msg.vendor_code = KEV_VENDOR_APPLE; ev_msg.kev_class = KEV_NETWORK_CLASS; ev_msg.kev_subclass = KEV_DL_SUBCLASS; - + ev_msg.event_code = KEV_DL_SIFMETRICS; - strncpy(&ev_data.if_name[0], ifp->if_name, IFNAMSIZ); + strlcpy(&ev_data.if_name[0], ifp->if_name, IFNAMSIZ); + ev_data.if_family = ifp->if_family; + ev_data.if_unit = (u_int32_t) ifp->if_unit; + ev_msg.dv[0].data_length = sizeof(struct net_event_data); + ev_msg.dv[0].data_ptr = &ev_data; + + ev_msg.dv[1].data_length = 0; + kev_post_msg(&ev_msg); + + ifnet_touch_lastchange(ifp); + break; + + case SIOCSIFPHYS: + error = proc_suser(p); + if (error != 0) + break; + + error = ifnet_ioctl(ifp, so->so_proto->pr_domain->dom_family, + cmd, data); + if (error != 0) + break; + + ev_msg.vendor_code = KEV_VENDOR_APPLE; + ev_msg.kev_class = KEV_NETWORK_CLASS; + ev_msg.kev_subclass = KEV_DL_SUBCLASS; + + ev_msg.event_code = KEV_DL_SIFPHYS; + strlcpy(&ev_data.if_name[0], ifp->if_name, IFNAMSIZ); ev_data.if_family = ifp->if_family; - ev_data.if_unit = (unsigned long) ifp->if_unit; + ev_data.if_unit = (u_int32_t) ifp->if_unit; ev_msg.dv[0].data_length = sizeof(struct net_event_data); ev_msg.dv[0].data_ptr = &ev_data; - ev_msg.dv[1].data_length = 0; kev_post_msg(&ev_msg); - getmicrotime(&ifp->if_lastchange); + ifnet_touch_lastchange(ifp); break; - case SIOCSIFPHYS: - error = suser(p->p_ucred, &p->p_acflag); - if (error) - return error; + case SIOCSIFMTU: + { + u_int32_t oldmtu = ifp->if_mtu; - error = dlil_ioctl(so->so_proto->pr_domain->dom_family, - ifp, cmd, (caddr_t) data); + error = proc_suser(p); + if (error != 0) + break; - if (error == 0) { - ev_msg.vendor_code = KEV_VENDOR_APPLE; - ev_msg.kev_class = KEV_NETWORK_CLASS; - ev_msg.kev_subclass = KEV_DL_SUBCLASS; - - ev_msg.event_code = KEV_DL_SIFPHYS; - strncpy(&ev_data.if_name[0], ifp->if_name, IFNAMSIZ); - ev_data.if_family = ifp->if_family; - ev_data.if_unit = (unsigned long) ifp->if_unit; - ev_msg.dv[0].data_length = sizeof(struct net_event_data); - ev_msg.dv[0].data_ptr = &ev_data; - ev_msg.dv[1].data_length = 0; - kev_post_msg(&ev_msg); - - getmicrotime(&ifp->if_lastchange); + if (ifp->if_ioctl == NULL) { + error = EOPNOTSUPP; + break; } - return(error); + if (ifr->ifr_mtu < IF_MINMTU || ifr->ifr_mtu > IF_MAXMTU) { + error = EINVAL; + break; + } + error = ifnet_ioctl(ifp, so->so_proto->pr_domain->dom_family, + cmd, data); + if (error != 0) + break; - case SIOCSIFMTU: - { - u_long oldmtu = ifp->if_mtu; + ev_msg.vendor_code = KEV_VENDOR_APPLE; + ev_msg.kev_class = KEV_NETWORK_CLASS; + ev_msg.kev_subclass = KEV_DL_SUBCLASS; - error = suser(p->p_ucred, &p->p_acflag); - if (error) - return (error); - if (ifp->if_ioctl == NULL) - return (EOPNOTSUPP); - if (ifr->ifr_mtu < IF_MINMTU || ifr->ifr_mtu > IF_MAXMTU) - return (EINVAL); + ev_msg.event_code = KEV_DL_SIFMTU; + strlcpy(&ev_data.if_name[0], ifp->if_name, IFNAMSIZ); + ev_data.if_family = ifp->if_family; + ev_data.if_unit = (u_int32_t) ifp->if_unit; + ev_msg.dv[0].data_length = sizeof(struct net_event_data); + ev_msg.dv[0].data_ptr = &ev_data; + ev_msg.dv[1].data_length = 0; + kev_post_msg(&ev_msg); - error = dlil_ioctl(so->so_proto->pr_domain->dom_family, - ifp, cmd, (caddr_t) data); + ifnet_touch_lastchange(ifp); + rt_ifmsg(ifp); - if (error == 0) { - ev_msg.vendor_code = KEV_VENDOR_APPLE; - ev_msg.kev_class = KEV_NETWORK_CLASS; - ev_msg.kev_subclass = KEV_DL_SUBCLASS; - - ev_msg.event_code = KEV_DL_SIFMTU; - strncpy(&ev_data.if_name[0], ifp->if_name, IFNAMSIZ); - ev_data.if_family = ifp->if_family; - ev_data.if_unit = (unsigned long) ifp->if_unit; - ev_msg.dv[0].data_length = sizeof(struct net_event_data); - ev_msg.dv[0].data_ptr = &ev_data; - ev_msg.dv[1].data_length = 0; - kev_post_msg(&ev_msg); - - getmicrotime(&ifp->if_lastchange); - rt_ifmsg(ifp); - } /* - * If the link MTU changed, do network layer specific procedure. + * If the link MTU changed, do network layer specific procedure + * and update all route entries associated with the interface, + * so that their MTU metric gets updated. */ if (ifp->if_mtu != oldmtu) { + if_rtmtu_update(ifp); #if INET6 nd6_setmtu(ifp); #endif } - return (error); + break; } case SIOCADDMULTI: case SIOCDELMULTI: - error = suser(p->p_ucred, &p->p_acflag); - if (error) - return (error); + error = proc_suser(p); + if (error != 0) + break; /* Don't allow group membership on non-multicast interfaces. */ - if ((ifp->if_flags & IFF_MULTICAST) == 0) - return EOPNOTSUPP; + if ((ifp->if_flags & IFF_MULTICAST) == 0) { + error = EOPNOTSUPP; + break; + } -#ifndef __APPLE__ /* Don't let users screw up protocols' entries. */ - if (ifr->ifr_addr.sa_family != AF_LINK) - return EINVAL; -#endif + if (ifr->ifr_addr.sa_family != AF_UNSPEC && + ifr->ifr_addr.sa_family != AF_LINK) { + error = EINVAL; + break; + } + /* + * User is permitted to anonymously join a particular link + * multicast group via SIOCADDMULTI. Subsequent join requested + * for the same record which has an outstanding refcnt from a + * past if_addmulti_anon() will not result in EADDRINUSE error + * (unlike other BSDs.) Anonymously leaving a group is also + * allowed only as long as there is an outstanding refcnt held + * by a previous anonymous request, or else ENOENT (even if the + * link-layer multicast membership exists for a network-layer + * membership.) + */ if (cmd == SIOCADDMULTI) { - struct ifmultiaddr *ifma; - error = if_addmulti(ifp, &ifr->ifr_addr, &ifma); + error = if_addmulti_anon(ifp, &ifr->ifr_addr, NULL); ev_msg.event_code = KEV_DL_ADDMULTI; } else { - error = if_delmulti(ifp, &ifr->ifr_addr); + error = if_delmulti_anon(ifp, &ifr->ifr_addr); ev_msg.event_code = KEV_DL_DELMULTI; } - if (error == 0) { - ev_msg.vendor_code = KEV_VENDOR_APPLE; - ev_msg.kev_class = KEV_NETWORK_CLASS; - ev_msg.kev_subclass = KEV_DL_SUBCLASS; - strncpy(&ev_data.if_name[0], ifp->if_name, IFNAMSIZ); - - ev_data.if_family = ifp->if_family; - ev_data.if_unit = (unsigned long) ifp->if_unit; - ev_msg.dv[0].data_length = sizeof(struct net_event_data); - ev_msg.dv[0].data_ptr = &ev_data; - ev_msg.dv[1].data_length = 0; - kev_post_msg(&ev_msg); - - getmicrotime(&ifp->if_lastchange); - } - return error; + if (error != 0) + break; + + ev_msg.vendor_code = KEV_VENDOR_APPLE; + ev_msg.kev_class = KEV_NETWORK_CLASS; + ev_msg.kev_subclass = KEV_DL_SUBCLASS; + strlcpy(&ev_data.if_name[0], ifp->if_name, IFNAMSIZ); + + ev_data.if_family = ifp->if_family; + ev_data.if_unit = (u_int32_t) ifp->if_unit; + ev_msg.dv[0].data_length = sizeof(struct net_event_data); + ev_msg.dv[0].data_ptr = &ev_data; + ev_msg.dv[1].data_length = 0; + kev_post_msg(&ev_msg); + + ifnet_touch_lastchange(ifp); + break; case SIOCSIFPHYADDR: case SIOCDIFPHYADDR: -#ifdef INET6 - case SIOCSIFPHYADDR_IN6: +#if INET6 + case SIOCSIFPHYADDR_IN6_32: + case SIOCSIFPHYADDR_IN6_64: #endif case SIOCSLIFPHYADDR: case SIOCSIFMEDIA: case SIOCSIFGENERIC: case SIOCSIFLLADDR: - error = suser(p->p_ucred, &p->p_acflag); - if (error) - return (error); + case SIOCSIFALTMTU: + case SIOCSIFVLAN: + case SIOCSIFBOND: + error = proc_suser(p); + if (error != 0) + break; - error = dlil_ioctl(so->so_proto->pr_domain->dom_family, - ifp, cmd, (caddr_t) data); + error = ifnet_ioctl(ifp, so->so_proto->pr_domain->dom_family, + cmd, data); + if (error != 0) + break; - if (error == 0) - getmicrotime(&ifp->if_lastchange); - return error; + ifnet_touch_lastchange(ifp); + break; case SIOCGIFSTATUS: ifs = (struct ifstat *)data; ifs->ascii[0] = '\0'; - + case SIOCGIFPSRCADDR: case SIOCGIFPDSTADDR: case SIOCGLIFPHYADDR: - case SIOCGIFMEDIA: + case SIOCGIFMEDIA32: + case SIOCGIFMEDIA64: case SIOCGIFGENERIC: + case SIOCGIFDEVMTU: + error = ifnet_ioctl(ifp, so->so_proto->pr_domain->dom_family, + cmd, data); + break; + + case SIOCGIFVLAN: + case SIOCGIFBOND: + error = ifnet_ioctl(ifp, so->so_proto->pr_domain->dom_family, + cmd, data); + break; + + case SIOCGIFWAKEFLAGS: + ifnet_lock_shared(ifp); + ifr->ifr_wake_flags = ifnet_get_wake_flags(ifp); + ifnet_lock_done(ifp); + break; - return dlil_ioctl(so->so_proto->pr_domain->dom_family, - ifp, cmd, (caddr_t) data); + case SIOCGIFGETRTREFCNT: + ifnet_lock_shared(ifp); + ifr->ifr_route_refcnt = ifp->if_route_refcnt; + ifnet_lock_done(ifp); + break; default: oif_flags = ifp->if_flags; - if (so->so_proto == 0) - return (EOPNOTSUPP); -#if !COMPAT_43 - return ((*so->so_proto->pr_usrreqs->pru_control)(so, cmd, - data, - ifp, p)); -#else + if (so->so_proto == NULL) { + error = EOPNOTSUPP; + break; + } { - int ocmd = cmd; + u_long ocmd = cmd; switch (cmd) { - case SIOCSIFDSTADDR: case SIOCSIFADDR: case SIOCSIFBRDADDR: @@ -926,12 +1809,13 @@ ifioctl(so, cmd, data, p) case OSIOCGIFNETMASK: cmd = SIOCGIFNETMASK; } - error = ((*so->so_proto->pr_usrreqs->pru_control)(so, - cmd, - data, - ifp, p)); - switch (ocmd) { + socket_lock(so, 1); + error = ((*so->so_proto->pr_usrreqs->pru_control)(so, cmd, + data, ifp, p)); + socket_unlock(so, 1); + + switch (ocmd) { case OSIOCGIFADDR: case OSIOCGIFDSTADDR: case OSIOCGIFBRDADDR: @@ -940,58 +1824,81 @@ ifioctl(so, cmd, data, p) } } -#endif /* COMPAT_43 */ + if (cmd == SIOCSIFKPI) { + int temperr = proc_suser(p); + if (temperr != 0) + error = temperr; + } - if (error == EOPNOTSUPP) - error = dlil_ioctl(so->so_proto->pr_domain->dom_family, - ifp, cmd, (caddr_t) data); + if (error == EOPNOTSUPP || error == ENOTSUP) + error = ifnet_ioctl(ifp, + so->so_proto->pr_domain->dom_family, cmd, data); - return (error); + break; } - return (0); + return (error); } +int +ifioctllocked(struct socket *so, u_long cmd, caddr_t data, struct proc *p) +{ + int error; + + socket_unlock(so, 0); + error = ifioctl(so, cmd, data, p); + socket_lock(so, 0); + return(error); +} + /* * Set/clear promiscuous mode on interface ifp based on the truth value * of pswitch. The calls are reference counted so that only the first * "on" request actually has an effect, as does the final "off" request. * Results are undefined if the "off" and "on" requests are not matched. */ -int -ifpromisc(ifp, pswitch) - struct ifnet *ifp; - int pswitch; +errno_t +ifnet_set_promiscuous( + ifnet_t ifp, + int pswitch) { - struct ifreq ifr; - int error; - int oldflags; + int error = 0; + int oldflags = 0; + int newflags = 0; + ifnet_lock_exclusive(ifp); oldflags = ifp->if_flags; - if (pswitch) { - /* - * If the device is not configured up, we cannot put it in - * promiscuous mode. - */ - if ((ifp->if_flags & IFF_UP) == 0) - return (ENETDOWN); - if (ifp->if_pcount++ != 0) - return (0); + ifp->if_pcount += pswitch ? 1 : -1; + + if (ifp->if_pcount > 0) ifp->if_flags |= IFF_PROMISC; - log(LOG_INFO, "%s%d: promiscuous mode enabled\n", - ifp->if_name, ifp->if_unit); - } else { - if (--ifp->if_pcount > 0) - return (0); + else ifp->if_flags &= ~IFF_PROMISC; - log(LOG_INFO, "%s%d: promiscuous mode disabled\n", - ifp->if_name, ifp->if_unit); + + newflags = ifp->if_flags; + ifnet_lock_done(ifp); + + if (newflags != oldflags && (newflags & IFF_UP) != 0) { + error = ifnet_ioctl(ifp, 0, SIOCSIFFLAGS, NULL); + if (error == 0) { + rt_ifmsg(ifp); + } else { + ifnet_lock_exclusive(ifp); + // revert the flags + ifp->if_pcount -= pswitch ? 1 : -1; + if (ifp->if_pcount > 0) + ifp->if_flags |= IFF_PROMISC; + else + ifp->if_flags &= ~IFF_PROMISC; + ifnet_lock_done(ifp); + } + } + + if (newflags != oldflags) { + log(LOG_INFO, "%s%d: promiscuous mode %s%s\n", + ifp->if_name, ifp->if_unit, + (newflags & IFF_PROMISC) != 0 ? "enable" : "disable", + error != 0 ? " failed" : " succeeded"); } - ifr.ifr_flags = ifp->if_flags; - error = dlil_ioctl(0, ifp, SIOCSIFFLAGS, (caddr_t)&ifr); - if (error == 0) - rt_ifmsg(ifp); - else - ifp->if_flags = oldflags; return error; } @@ -1003,20 +1910,26 @@ ifpromisc(ifp, pswitch) */ /*ARGSUSED*/ static int -ifconf(cmd, data) - u_long cmd; - caddr_t data; -{ - register struct ifconf *ifc = (struct ifconf *)data; - register struct ifnet *ifp = ifnet.tqh_first; - register struct ifaddr *ifa; - struct ifreq ifr, *ifrp; - int space = ifc->ifc_len, error = 0; - - ifrp = ifc->ifc_req; - for (; space > sizeof (ifr) && ifp; ifp = ifp->if_link.tqe_next) { +ifconf(u_long cmd, user_addr_t ifrp, int * ret_space) +{ + struct ifnet *ifp = NULL; + struct ifaddr *ifa; + struct ifreq ifr; + int error = 0; + size_t space; + + /* + * Zero the ifr buffer to make sure we don't + * disclose the contents of the stack. + */ + bzero(&ifr, sizeof(struct ifreq)); + + space = *ret_space; + ifnet_head_lock_shared(); + for (ifp = ifnet_head.tqh_first; space > sizeof(ifr) && + ifp; ifp = ifp->if_link.tqe_next) { char workbuf[64]; - int ifnlen, addrs; + size_t ifnlen, addrs; ifnlen = snprintf(workbuf, sizeof(workbuf), "%s%d", ifp->if_name, ifp->if_unit); @@ -1024,276 +1937,799 @@ ifconf(cmd, data) error = ENAMETOOLONG; break; } else { - strcpy(ifr.ifr_name, workbuf); + strlcpy(ifr.ifr_name, workbuf, IFNAMSIZ); + } + + ifnet_lock_shared(ifp); + + addrs = 0; + ifa = ifp->if_addrhead.tqh_first; + for ( ; space > sizeof (ifr) && ifa; + ifa = ifa->ifa_link.tqe_next) { + struct sockaddr *sa; + + IFA_LOCK(ifa); + sa = ifa->ifa_addr; +#ifndef __APPLE__ + if (curproc->p_prison && prison_if(curproc, sa)) { + IFA_UNLOCK(ifa); + continue; + } +#endif + addrs++; + if (cmd == OSIOCGIFCONF32 || cmd == OSIOCGIFCONF64) { + struct osockaddr *osa = + (struct osockaddr *)&ifr.ifr_addr; + ifr.ifr_addr = *sa; + osa->sa_family = sa->sa_family; + error = copyout((caddr_t)&ifr, ifrp, + sizeof (ifr)); + ifrp += sizeof(struct ifreq); + } else if (sa->sa_len <= sizeof(*sa)) { + ifr.ifr_addr = *sa; + error = copyout((caddr_t)&ifr, ifrp, + sizeof (ifr)); + ifrp += sizeof(struct ifreq); + } else { + if (space < + sizeof (ifr) + sa->sa_len - sizeof(*sa)) { + IFA_UNLOCK(ifa); + break; + } + space -= sa->sa_len - sizeof(*sa); + error = copyout((caddr_t)&ifr, ifrp, + sizeof (ifr.ifr_name)); + if (error == 0) { + error = copyout((caddr_t)sa, (ifrp + + offsetof(struct ifreq, ifr_addr)), + sa->sa_len); + } + ifrp += (sa->sa_len + offsetof(struct ifreq, + ifr_addr)); + } + IFA_UNLOCK(ifa); + if (error) + break; + space -= sizeof (ifr); + } + ifnet_lock_done(ifp); + + if (error) + break; + if (!addrs) { + bzero((caddr_t)&ifr.ifr_addr, sizeof(ifr.ifr_addr)); + error = copyout((caddr_t)&ifr, ifrp, sizeof (ifr)); + if (error) + break; + space -= sizeof (ifr); + ifrp += sizeof(struct ifreq); + } + } + ifnet_head_done(); + *ret_space -= space; + return (error); +} + +/* + * Just like if_promisc(), but for all-multicast-reception mode. + */ +int +if_allmulti(struct ifnet *ifp, int onswitch) +{ + int error = 0; + int modified = 0; + + ifnet_lock_exclusive(ifp); + + if (onswitch) { + if (ifp->if_amcount++ == 0) { + ifp->if_flags |= IFF_ALLMULTI; + modified = 1; + } + } else { + if (ifp->if_amcount > 1) { + ifp->if_amcount--; + } else { + ifp->if_amcount = 0; + ifp->if_flags &= ~IFF_ALLMULTI; + modified = 1; } + } + ifnet_lock_done(ifp); + + if (modified) + error = ifnet_ioctl(ifp, 0, SIOCSIFFLAGS, NULL); + + if (error == 0) + rt_ifmsg(ifp); + return error; +} + +static struct ifmultiaddr * +ifma_alloc(int how) +{ + struct ifmultiaddr *ifma; + + ifma = (how == M_WAITOK) ? zalloc(ifma_zone) : + zalloc_noblock(ifma_zone); + + if (ifma != NULL) { + bzero(ifma, ifma_size); + lck_mtx_init(&ifma->ifma_lock, ifa_mtx_grp, ifa_mtx_attr); + ifma->ifma_debug |= IFD_ALLOC; + if (ifma_debug != 0) { + ifma->ifma_debug |= IFD_DEBUG; + ifma->ifma_trace = ifma_trace; + } + } + return (ifma); +} + +static void +ifma_free(struct ifmultiaddr *ifma) +{ + IFMA_LOCK(ifma); + + if (ifma->ifma_protospec != NULL) { + panic("%s: Protospec not NULL for ifma=%p", __func__, ifma); + /* NOTREACHED */ + } else if ((ifma->ifma_flags & IFMAF_ANONYMOUS) || + ifma->ifma_anoncnt != 0) { + panic("%s: Freeing ifma=%p with outstanding anon req", + __func__, ifma); + /* NOTREACHED */ + } else if (ifma->ifma_debug & IFD_ATTACHED) { + panic("%s: ifma=%p attached to ifma_ifp=%p is being freed", + __func__, ifma, ifma->ifma_ifp); + /* NOTREACHED */ + } else if (!(ifma->ifma_debug & IFD_ALLOC)) { + panic("%s: ifma %p cannot be freed", __func__, ifma); + /* NOTREACHED */ + } else if (ifma->ifma_refcount != 0) { + panic("%s: non-zero refcount ifma=%p", __func__, ifma); + /* NOTREACHED */ + } else if (ifma->ifma_reqcnt != 0) { + panic("%s: non-zero reqcnt ifma=%p", __func__, ifma); + /* NOTREACHED */ + } else if (ifma->ifma_ifp != NULL) { + panic("%s: non-NULL ifma_ifp=%p for ifma=%p", __func__, + ifma->ifma_ifp, ifma); + /* NOTREACHED */ + } else if (ifma->ifma_ll != NULL) { + panic("%s: non-NULL ifma_ll=%p for ifma=%p", __func__, + ifma->ifma_ll, ifma); + /* NOTREACHED */ + } + ifma->ifma_debug &= ~IFD_ALLOC; + if ((ifma->ifma_debug & (IFD_DEBUG | IFD_TRASHED)) == + (IFD_DEBUG | IFD_TRASHED)) { + lck_mtx_lock(&ifma_trash_lock); + TAILQ_REMOVE(&ifma_trash_head, (struct ifmultiaddr_dbg *)ifma, + ifma_trash_link); + lck_mtx_unlock(&ifma_trash_lock); + ifma->ifma_debug &= ~IFD_TRASHED; + } + IFMA_UNLOCK(ifma); + + if (ifma->ifma_addr != NULL) { + FREE(ifma->ifma_addr, M_IFADDR); + ifma->ifma_addr = NULL; + } + lck_mtx_destroy(&ifma->ifma_lock, ifa_mtx_grp); + zfree(ifma_zone, ifma); +} + +static void +ifma_trace(struct ifmultiaddr *ifma, int refhold) +{ + struct ifmultiaddr_dbg *ifma_dbg = (struct ifmultiaddr_dbg *)ifma; + ctrace_t *tr; + u_int32_t idx; + u_int16_t *cnt; + + if (!(ifma->ifma_debug & IFD_DEBUG)) { + panic("%s: ifma %p has no debug structure", __func__, ifma); + /* NOTREACHED */ + } + if (refhold) { + cnt = &ifma_dbg->ifma_refhold_cnt; + tr = ifma_dbg->ifma_refhold; + } else { + cnt = &ifma_dbg->ifma_refrele_cnt; + tr = ifma_dbg->ifma_refrele; + } + + idx = atomic_add_16_ov(cnt, 1) % IFMA_TRACE_HIST_SIZE; + ctrace_record(&tr[idx]); +} + +void +ifma_addref(struct ifmultiaddr *ifma, int locked) +{ + if (!locked) + IFMA_LOCK(ifma); + else + IFMA_LOCK_ASSERT_HELD(ifma); + + if (++ifma->ifma_refcount == 0) { + panic("%s: ifma=%p wraparound refcnt", __func__, ifma); + /* NOTREACHED */ + } else if (ifma->ifma_trace != NULL) { + (*ifma->ifma_trace)(ifma, TRUE); + } + if (!locked) + IFMA_UNLOCK(ifma); +} + +void +ifma_remref(struct ifmultiaddr *ifma) +{ + struct ifmultiaddr *ll; + + IFMA_LOCK(ifma); + + if (ifma->ifma_refcount == 0) { + panic("%s: ifma=%p negative refcnt", __func__, ifma); + /* NOTREACHED */ + } else if (ifma->ifma_trace != NULL) { + (*ifma->ifma_trace)(ifma, FALSE); + } + + --ifma->ifma_refcount; + if (ifma->ifma_refcount > 0) { + IFMA_UNLOCK(ifma); + return; + } + + ll = ifma->ifma_ll; + ifma->ifma_ifp = NULL; + ifma->ifma_ll = NULL; + IFMA_UNLOCK(ifma); + ifma_free(ifma); /* deallocate it */ + + if (ll != NULL) + IFMA_REMREF(ll); +} + +static void +if_attach_ifma(struct ifnet *ifp, struct ifmultiaddr *ifma, int anon) +{ + ifnet_lock_assert(ifp, IFNET_LCK_ASSERT_EXCLUSIVE); + IFMA_LOCK_ASSERT_HELD(ifma); + + if (ifma->ifma_ifp != ifp) { + panic("%s: Mismatch ifma_ifp=%p != ifp=%p", __func__, + ifma->ifma_ifp, ifp); + /* NOTREACHED */ + } else if (ifma->ifma_debug & IFD_ATTACHED) { + panic("%s: Attempt to attach an already attached ifma=%p", + __func__, ifma); + /* NOTREACHED */ + } else if (anon && (ifma->ifma_flags & IFMAF_ANONYMOUS)) { + panic("%s: ifma=%p unexpected IFMAF_ANONYMOUS", __func__, ifma); + /* NOTREACHED */ + } else if (ifma->ifma_debug & IFD_TRASHED) { + panic("%s: Attempt to reattach a detached ifma=%p", + __func__, ifma); + /* NOTREACHED */ + } + + ifma->ifma_reqcnt++; + VERIFY(ifma->ifma_reqcnt == 1); + IFMA_ADDREF_LOCKED(ifma); + ifma->ifma_debug |= IFD_ATTACHED; + if (anon) { + ifma->ifma_anoncnt++; + VERIFY(ifma->ifma_anoncnt == 1); + ifma->ifma_flags |= IFMAF_ANONYMOUS; + } + + LIST_INSERT_HEAD(&ifp->if_multiaddrs, ifma, ifma_link); +} + +static int +if_detach_ifma(struct ifnet *ifp, struct ifmultiaddr *ifma, int anon) +{ + ifnet_lock_assert(ifp, IFNET_LCK_ASSERT_EXCLUSIVE); + IFMA_LOCK_ASSERT_HELD(ifma); + + if (ifma->ifma_reqcnt == 0) { + panic("%s: ifma=%p negative reqcnt", __func__, ifma); + /* NOTREACHED */ + } else if (anon && !(ifma->ifma_flags & IFMAF_ANONYMOUS)) { + panic("%s: ifma=%p missing IFMAF_ANONYMOUS", __func__, ifma); + /* NOTREACHED */ + } else if (anon && ifma->ifma_anoncnt == 0) { + panic("%s: ifma=%p negative anonreqcnt", __func__, ifma); + /* NOTREACHED */ + } else if (ifma->ifma_ifp != ifp) { + panic("%s: Mismatch ifma_ifp=%p, ifp=%p", __func__, + ifma->ifma_ifp, ifp); + /* NOTREACHED */ + } + + if (anon) { + --ifma->ifma_anoncnt; + if (ifma->ifma_anoncnt > 0) + return (0); + ifma->ifma_flags &= ~IFMAF_ANONYMOUS; + } + + --ifma->ifma_reqcnt; + if (ifma->ifma_reqcnt > 0) + return (0); + + if (ifma->ifma_protospec != NULL) { + panic("%s: Protospec not NULL for ifma=%p", __func__, ifma); + /* NOTREACHED */ + } else if ((ifma->ifma_flags & IFMAF_ANONYMOUS) || + ifma->ifma_anoncnt != 0) { + panic("%s: Detaching ifma=%p with outstanding anon req", + __func__, ifma); + /* NOTREACHED */ + } else if (!(ifma->ifma_debug & IFD_ATTACHED)) { + panic("%s: Attempt to detach an unattached address ifma=%p", + __func__, ifma); + /* NOTREACHED */ + } else if (ifma->ifma_debug & IFD_TRASHED) { + panic("%s: ifma %p is already in trash list", __func__, ifma); + /* NOTREACHED */ + } + + /* + * NOTE: Caller calls IFMA_REMREF + */ + ifma->ifma_debug &= ~IFD_ATTACHED; + LIST_REMOVE(ifma, ifma_link); + if (LIST_EMPTY(&ifp->if_multiaddrs)) + ifp->if_updatemcasts = 0; + + if (ifma->ifma_debug & IFD_DEBUG) { + /* Become a regular mutex, just in case */ + IFMA_CONVERT_LOCK(ifma); + lck_mtx_lock(&ifma_trash_lock); + TAILQ_INSERT_TAIL(&ifma_trash_head, + (struct ifmultiaddr_dbg *)ifma, ifma_trash_link); + lck_mtx_unlock(&ifma_trash_lock); + ifma->ifma_debug |= IFD_TRASHED; + } + + return (1); +} + +/* + * Find an ifmultiaddr that matches a socket address on an interface. + * + * Caller is responsible for holding the ifnet_lock while calling + * this function. + */ +static int +if_addmulti_doesexist(struct ifnet *ifp, const struct sockaddr *sa, + struct ifmultiaddr **retifma, int anon) +{ + struct ifmultiaddr *ifma; - addrs = 0; - ifa = ifp->if_addrhead.tqh_first; - for ( ; space > sizeof (ifr) && ifa; - ifa = ifa->ifa_link.tqe_next) { - register struct sockaddr *sa = ifa->ifa_addr; -#ifndef __APPLE__ - if (curproc->p_prison && prison_if(curproc, sa)) - continue; -#endif - addrs++; -#ifdef COMPAT_43 - if (cmd == OSIOCGIFCONF) { - struct osockaddr *osa = - (struct osockaddr *)&ifr.ifr_addr; - ifr.ifr_addr = *sa; - osa->sa_family = sa->sa_family; - error = copyout((caddr_t)&ifr, (caddr_t)ifrp, - sizeof (ifr)); - ifrp++; - } else -#endif - if (sa->sa_len <= sizeof(*sa)) { - ifr.ifr_addr = *sa; - error = copyout((caddr_t)&ifr, (caddr_t)ifrp, - sizeof (ifr)); - ifrp++; - } else { - if (space < sizeof (ifr) + sa->sa_len - - sizeof(*sa)) - break; - space -= sa->sa_len - sizeof(*sa); - error = copyout((caddr_t)&ifr, (caddr_t)ifrp, - sizeof (ifr.ifr_name)); - if (error == 0) - error = copyout((caddr_t)sa, - (caddr_t)&ifrp->ifr_addr, sa->sa_len); - ifrp = (struct ifreq *) - (sa->sa_len + (caddr_t)&ifrp->ifr_addr); + for (ifma = LIST_FIRST(&ifp->if_multiaddrs); ifma != NULL; + ifma = LIST_NEXT(ifma, ifma_link)) { + IFMA_LOCK_SPIN(ifma); + if (!equal(sa, ifma->ifma_addr)) { + IFMA_UNLOCK(ifma); + continue; + } + if (anon) { + VERIFY(!(ifma->ifma_flags & IFMAF_ANONYMOUS) || + ifma->ifma_anoncnt != 0); + VERIFY((ifma->ifma_flags & IFMAF_ANONYMOUS) || + ifma->ifma_anoncnt == 0); + ifma->ifma_anoncnt++; + if (!(ifma->ifma_flags & IFMAF_ANONYMOUS)) { + VERIFY(ifma->ifma_anoncnt == 1); + ifma->ifma_flags |= IFMAF_ANONYMOUS; } - if (error) - break; - space -= sizeof (ifr); } - if (error) - break; - if (!addrs) { - bzero((caddr_t)&ifr.ifr_addr, sizeof(ifr.ifr_addr)); - error = copyout((caddr_t)&ifr, (caddr_t)ifrp, - sizeof (ifr)); - if (error) - break; - space -= sizeof (ifr); - ifrp++; + if (!anon || ifma->ifma_anoncnt == 1) { + ifma->ifma_reqcnt++; + VERIFY(ifma->ifma_reqcnt > 1); } + if (retifma != NULL) { + *retifma = ifma; + IFMA_ADDREF_LOCKED(ifma); + } + IFMA_UNLOCK(ifma); + return (0); } - ifc->ifc_len -= space; - return (error); + return (ENOENT); } /* - * Just like if_promisc(), but for all-multicast-reception mode. + * Radar 3642395, make sure all multicasts are in a standard format. */ -int -if_allmulti(ifp, onswitch) - struct ifnet *ifp; - int onswitch; +static struct sockaddr* +copy_and_normalize( + const struct sockaddr *original) { - int error = 0; - int s = splimp(); - - if (onswitch) { - if (ifp->if_amcount++ == 0) { - ifp->if_flags |= IFF_ALLMULTI; - error = dlil_ioctl(0, ifp, SIOCSIFFLAGS, (caddr_t) 0); + int alen = 0; + const u_char *aptr = NULL; + struct sockaddr *copy = NULL; + struct sockaddr_dl *sdl_new = NULL; + int len = 0; + + if (original->sa_family != AF_LINK && + original->sa_family != AF_UNSPEC) { + /* Just make a copy */ + MALLOC(copy, struct sockaddr*, original->sa_len, M_IFADDR, M_WAITOK); + if (copy != NULL) + bcopy(original, copy, original->sa_len); + return copy; + } + + switch (original->sa_family) { + case AF_LINK: { + const struct sockaddr_dl *sdl_original = + (const struct sockaddr_dl*)original; + + if (sdl_original->sdl_nlen + sdl_original->sdl_alen + sdl_original->sdl_slen + + offsetof(struct sockaddr_dl, sdl_data) > sdl_original->sdl_len) + return NULL; + + alen = sdl_original->sdl_alen; + aptr = CONST_LLADDR(sdl_original); } - } else { - if (ifp->if_amcount > 1) { - ifp->if_amcount--; - } else { - ifp->if_amcount = 0; - ifp->if_flags &= ~IFF_ALLMULTI; - error = dlil_ioctl(0, ifp, SIOCSIFFLAGS, (caddr_t) 0); + break; + + case AF_UNSPEC: { + if (original->sa_len < ETHER_ADDR_LEN + + offsetof(struct sockaddr, sa_data)) { + return NULL; + } + + alen = ETHER_ADDR_LEN; + aptr = (const u_char*)original->sa_data; } + break; + } + + if (alen == 0 || aptr == NULL) + return NULL; + + len = alen + offsetof(struct sockaddr_dl, sdl_data); + MALLOC(sdl_new, struct sockaddr_dl*, len, M_IFADDR, M_WAITOK); + + if (sdl_new != NULL) { + bzero(sdl_new, len); + sdl_new->sdl_len = len; + sdl_new->sdl_family = AF_LINK; + sdl_new->sdl_alen = alen; + bcopy(aptr, LLADDR(sdl_new), alen); } - splx(s); + + return (struct sockaddr*)sdl_new; +} - if (error == 0) - rt_ifmsg(ifp); - return error; +/* + * Network-layer protocol domains which hold references to the underlying + * link-layer record must use this routine. + */ +int +if_addmulti(struct ifnet *ifp, const struct sockaddr *sa, + struct ifmultiaddr **retifma) +{ + return (if_addmulti_common(ifp, sa, retifma, 0)); } /* - * Add a multicast listenership to the interface in question. - * The link layer provides a routine which converts + * Anything other than network-layer protocol domains which hold references + * to the underlying link-layer record must use this routine: SIOCADDMULTI + * ioctl, ifnet_add_multicast(), AppleTalk, if_bond. */ int -if_addmulti(ifp, sa, retifma) - struct ifnet *ifp; /* interface to manipulate */ - struct sockaddr *sa; /* address to add */ - struct ifmultiaddr **retifma; -{ - struct sockaddr *llsa = 0; - struct sockaddr *dupsa; - int error, s; - struct ifmultiaddr *ifma; - struct rslvmulti_req rsreq; +if_addmulti_anon(struct ifnet *ifp, const struct sockaddr *sa, + struct ifmultiaddr **retifma) +{ + return (if_addmulti_common(ifp, sa, retifma, 1)); +} - /* - * If the matching multicast address already exists - * then don't add a new one, just add a reference - */ - for (ifma = ifp->if_multiaddrs.lh_first; ifma; - ifma = ifma->ifma_link.le_next) { - if (equal(sa, ifma->ifma_addr)) { - ifma->ifma_refcount++; - if (retifma) - *retifma = ifma; - return 0; +/* + * Register an additional multicast address with a network interface. + * + * - If the address is already present, bump the reference count on the + * address and return. + * - If the address is not link-layer, look up a link layer address. + * - Allocate address structures for one or both addresses, and attach to the + * multicast address list on the interface. If automatically adding a link + * layer address, the protocol address will own a reference to the link + * layer address, to be freed when it is freed. + * - Notify the network device driver of an addition to the multicast address + * list. + * + * 'sa' points to caller-owned memory with the desired multicast address. + * + * 'retifma' will be used to return a pointer to the resulting multicast + * address reference, if desired. + * + * 'anon' indicates a link-layer address with no protocol address reference + * made to it. Anything other than network-layer protocol domain requests + * are considered as anonymous. + */ +static int +if_addmulti_common(struct ifnet *ifp, const struct sockaddr *sa, + struct ifmultiaddr **retifma, int anon) +{ + struct sockaddr_storage storage; + struct sockaddr *llsa = NULL; + struct sockaddr *dupsa = NULL; + int error = 0, ll_firstref = 0, lladdr; + struct ifmultiaddr *ifma = NULL; + struct ifmultiaddr *llifma = NULL; + + /* Only AF_UNSPEC/AF_LINK is allowed for an "anonymous" address */ + VERIFY(!anon || sa->sa_family == AF_UNSPEC || + sa->sa_family == AF_LINK); + + /* If sa is a AF_LINK or AF_UNSPEC, duplicate and normalize it */ + if (sa->sa_family == AF_LINK || sa->sa_family == AF_UNSPEC) { + dupsa = copy_and_normalize(sa); + if (dupsa == NULL) { + error = ENOMEM; + goto cleanup; } + sa = dupsa; + } + + ifnet_lock_exclusive(ifp); + if (!(ifp->if_flags & IFF_MULTICAST)) { + error = EADDRNOTAVAIL; + ifnet_lock_done(ifp); + goto cleanup; } + /* If the address is already present, return a new reference to it */ + error = if_addmulti_doesexist(ifp, sa, retifma, anon); + ifnet_lock_done(ifp); + if (error == 0) + goto cleanup; + /* - * Give the link layer a chance to accept/reject it, and also - * find out which AF_LINK address this maps to, if it isn't one - * already. + * The address isn't already present; give the link layer a chance + * to accept/reject it, and also find out which AF_LINK address this + * maps to, if it isn't one already. */ - rsreq.sa = sa; - rsreq.llsa = &llsa; + error = dlil_resolve_multi(ifp, sa, (struct sockaddr *)&storage, + sizeof (storage)); + if (error == 0 && storage.ss_len != 0) { + llsa = copy_and_normalize((struct sockaddr *)&storage); + if (llsa == NULL) { + error = ENOMEM; + goto cleanup; + } + + llifma = ifma_alloc(M_WAITOK); + if (llifma == NULL) { + error = ENOMEM; + goto cleanup; + } + } - error = dlil_ioctl(sa->sa_family, ifp, SIOCRSLVMULTI, (caddr_t) &rsreq); - /* to be similar to FreeBSD */ if (error == EOPNOTSUPP) error = 0; + else if (error != 0) + goto cleanup; + + /* Allocate while we aren't holding any locks */ + if (dupsa == NULL) { + dupsa = copy_and_normalize(sa); + if (dupsa == NULL) { + error = ENOMEM; + goto cleanup; + } + } + ifma = ifma_alloc(M_WAITOK); + if (ifma == NULL) { + error = ENOMEM; + goto cleanup; + } + + ifnet_lock_exclusive(ifp); + /* + * Check again for the matching multicast. + */ + error = if_addmulti_doesexist(ifp, sa, retifma, anon); + if (error == 0) { + ifnet_lock_done(ifp); + goto cleanup; + } - if (error) - return error; + if (llifma != NULL) { + VERIFY(!anon); /* must not get here if "anonymous" */ + if (if_addmulti_doesexist(ifp, llsa, &ifma->ifma_ll, 0) == 0) { + FREE(llsa, M_IFADDR); + llsa = NULL; + ifma_free(llifma); + llifma = NULL; + VERIFY(ifma->ifma_ll->ifma_ifp == ifp); + } else { + ll_firstref = 1; + llifma->ifma_addr = llsa; + llifma->ifma_ifp = ifp; + IFMA_LOCK(llifma); + if_attach_ifma(ifp, llifma, 0); + /* add extra refcnt for ifma */ + IFMA_ADDREF_LOCKED(llifma); + IFMA_UNLOCK(llifma); + ifma->ifma_ll = llifma; + } + } - MALLOC(ifma, struct ifmultiaddr *, sizeof *ifma, M_IFMADDR, M_WAITOK); - MALLOC(dupsa, struct sockaddr *, sa->sa_len, M_IFMADDR, M_WAITOK); - bcopy(sa, dupsa, sa->sa_len); + /* "anonymous" request should not result in network address */ + VERIFY(!anon || ifma->ifma_ll == NULL); ifma->ifma_addr = dupsa; - ifma->ifma_lladdr = llsa; ifma->ifma_ifp = ifp; - ifma->ifma_refcount = 1; - ifma->ifma_protospec = 0; + IFMA_LOCK(ifma); + if_attach_ifma(ifp, ifma, anon); + IFMA_ADDREF_LOCKED(ifma); /* for this routine */ + if (retifma != NULL) { + *retifma = ifma; + IFMA_ADDREF_LOCKED(*retifma); /* for caller */ + } + lladdr = (ifma->ifma_addr->sa_family == AF_UNSPEC || + ifma->ifma_addr->sa_family == AF_LINK); + IFMA_UNLOCK(ifma); + ifnet_lock_done(ifp); + rt_newmaddrmsg(RTM_NEWMADDR, ifma); + IFMA_REMREF(ifma); /* for this routine */ - /* - * Some network interfaces can scan the address list at - * interrupt time; lock them out. - */ - s = splimp(); - LIST_INSERT_HEAD(&ifp->if_multiaddrs, ifma, ifma_link); - splx(s); - if (retifma) - *retifma = ifma; - - if (llsa != 0) { - for (ifma = ifp->if_multiaddrs.lh_first; ifma; - ifma = ifma->ifma_link.le_next) { - if (equal(ifma->ifma_addr, llsa)) - break; - } - if (ifma) { - ifma->ifma_refcount++; - } else { - MALLOC(ifma, struct ifmultiaddr *, sizeof *ifma, - M_IFMADDR, M_WAITOK); - MALLOC(dupsa, struct sockaddr *, llsa->sa_len, - M_IFMADDR, M_WAITOK); - bcopy(llsa, dupsa, llsa->sa_len); - ifma->ifma_addr = dupsa; - ifma->ifma_lladdr = 0; - ifma->ifma_ifp = ifp; - ifma->ifma_refcount = 1; - s = splimp(); - LIST_INSERT_HEAD(&ifp->if_multiaddrs, ifma, ifma_link); - splx(s); - } - } /* * We are certain we have added something, so call down to the - * interface to let them know about it. + * interface to let them know about it. Do this only for newly- + * added AF_LINK/AF_UNSPEC address in the if_multiaddrs set. */ - s = splimp(); - dlil_ioctl(0, ifp, SIOCADDMULTI, (caddr_t) 0); - splx(s); + if (lladdr || ll_firstref) + (void) ifnet_ioctl(ifp, 0, SIOCADDMULTI, NULL); - return 0; + if (ifp->if_updatemcasts > 0) + ifp->if_updatemcasts = 0; + + return (0); + +cleanup: + if (ifma != NULL) + ifma_free(ifma); + if (dupsa != NULL) + FREE(dupsa, M_IFADDR); + if (llifma != NULL) + ifma_free(llifma); + if (llsa != NULL) + FREE(llsa, M_IFADDR); + + return (error); } /* - * Remove a reference to a multicast address on this interface. Yell - * if the request does not match an existing membership. + * Delete a multicast group membership by network-layer group address. + * This routine is deprecated. */ int -if_delmulti(ifp, sa) - struct ifnet *ifp; - struct sockaddr *sa; +if_delmulti(struct ifnet *ifp, const struct sockaddr *sa) { - struct ifmultiaddr *ifma; - int s; + return (if_delmulti_common(NULL, ifp, sa, 0)); +} - for (ifma = ifp->if_multiaddrs.lh_first; ifma; - ifma = ifma->ifma_link.le_next) - if (equal(sa, ifma->ifma_addr)) - break; - if (ifma == 0) - return ENOENT; +/* + * Delete a multicast group membership by group membership pointer. + * Network-layer protocol domains must use this routine. + */ +int +if_delmulti_ifma(struct ifmultiaddr *ifma) +{ + return (if_delmulti_common(ifma, NULL, NULL, 0)); +} - if (ifma->ifma_refcount > 1) { - ifma->ifma_refcount--; - return 0; - } +/* + * Anything other than network-layer protocol domains which hold references + * to the underlying link-layer record must use this routine: SIOCDELMULTI + * ioctl, ifnet_remove_multicast(), AppleTalk, if_bond. + */ +int +if_delmulti_anon(struct ifnet *ifp, const struct sockaddr *sa) +{ + return (if_delmulti_common(NULL, ifp, sa, 1)); +} - rt_newmaddrmsg(RTM_DELMADDR, ifma); - sa = ifma->ifma_lladdr; - s = splimp(); - LIST_REMOVE(ifma, ifma_link); - /* - * Make sure the interface driver is notified - * in the case of a link layer mcast group being left. - */ - if (ifma->ifma_addr->sa_family == AF_LINK && sa == 0) - dlil_ioctl(0, ifp, SIOCDELMULTI, 0); - splx(s); - FREE(ifma->ifma_addr, M_IFMADDR); - FREE(ifma, M_IFMADDR); - if (sa == 0) - return 0; +/* + * Delete a multicast group membership by network-layer group address. + * + * Returns ENOENT if the entry could not be found. + */ +static int +if_delmulti_common(struct ifmultiaddr *ifma, struct ifnet *ifp, + const struct sockaddr *sa, int anon) +{ + struct sockaddr *dupsa = NULL; + int lastref, ll_lastref = 0, lladdr; + struct ifmultiaddr *ll = NULL; + + /* sanity check for callers */ + VERIFY(ifma != NULL || (ifp != NULL && sa != NULL)); + + if (ifma != NULL) + ifp = ifma->ifma_ifp; + + if (sa != NULL && + (sa->sa_family == AF_LINK || sa->sa_family == AF_UNSPEC)) { + dupsa = copy_and_normalize(sa); + if (dupsa == NULL) + return (ENOMEM); + sa = dupsa; + } - /* - * Now look for the link-layer address which corresponds to - * this network address. It had been squirreled away in - * ifma->ifma_lladdr for this purpose (so we don't have - * to call SIOCRSLVMULTI again), and we saved that - * value in sa above. If some nasty deleted the - * link-layer address out from underneath us, we can deal because - * the address we stored was is not the same as the one which was - * in the record for the link-layer address. (So we don't complain - * in that case.) - */ - for (ifma = ifp->if_multiaddrs.lh_first; ifma; - ifma = ifma->ifma_link.le_next) - if (equal(sa, ifma->ifma_addr)) + ifnet_lock_exclusive(ifp); + if (ifma == NULL) { + for (ifma = LIST_FIRST(&ifp->if_multiaddrs); ifma != NULL; + ifma = LIST_NEXT(ifma, ifma_link)) { + IFMA_LOCK(ifma); + if (!equal(sa, ifma->ifma_addr) || + (anon && !(ifma->ifma_flags & IFMAF_ANONYMOUS))) { + VERIFY(!(ifma->ifma_flags & IFMAF_ANONYMOUS) || + ifma->ifma_anoncnt != 0); + IFMA_UNLOCK(ifma); + continue; + } + /* found; keep it locked */ break; - if (ifma == 0) - return 0; + } + if (ifma == NULL) { + if (dupsa != NULL) + FREE(dupsa, M_IFADDR); + ifnet_lock_done(ifp); + return (ENOENT); + } + } else { + IFMA_LOCK(ifma); + } + IFMA_LOCK_ASSERT_HELD(ifma); + IFMA_ADDREF_LOCKED(ifma); /* for this routine */ + lastref = if_detach_ifma(ifp, ifma, anon); + VERIFY(!lastref || (!(ifma->ifma_debug & IFD_ATTACHED) && + ifma->ifma_reqcnt == 0)); + VERIFY(!anon || ifma->ifma_ll == NULL); + ll = ifma->ifma_ll; + lladdr = (ifma->ifma_addr->sa_family == AF_UNSPEC || + ifma->ifma_addr->sa_family == AF_LINK); + IFMA_UNLOCK(ifma); + if (lastref && ll != NULL) { + IFMA_LOCK(ll); + ll_lastref = if_detach_ifma(ifp, ll, 0); + IFMA_UNLOCK(ll); + } + ifnet_lock_done(ifp); + + if (lastref) + rt_newmaddrmsg(RTM_DELMADDR, ifma); - if (ifma->ifma_refcount > 1) { - ifma->ifma_refcount--; - return 0; + if ((ll == NULL && lastref && lladdr) || ll_lastref) { + /* + * Make sure the interface driver is notified in the + * case of a link layer mcast group being left. Do + * this only for a AF_LINK/AF_UNSPEC address that has + * been removed from the if_multiaddrs set. + */ + ifnet_ioctl(ifp, 0, SIOCDELMULTI, NULL); } - s = splimp(); - LIST_REMOVE(ifma, ifma_link); - dlil_ioctl(0, ifp, SIOCDELMULTI, (caddr_t) 0); - splx(s); - FREE(ifma->ifma_addr, M_IFMADDR); - FREE(sa, M_IFMADDR); - FREE(ifma, M_IFMADDR); + if (lastref) + IFMA_REMREF(ifma); /* for if_multiaddrs list */ + if (ll_lastref) + IFMA_REMREF(ll); /* for if_multiaddrs list */ - return 0; -} + IFMA_REMREF(ifma); /* for this routine */ + if (dupsa != NULL) + FREE(dupsa, M_IFADDR); + return (0); +} /* * We don't use if_setlladdr, our interfaces are responsible for @@ -1307,40 +2743,30 @@ if_setlladdr(struct ifnet *ifp, const u_char *lladdr, int len) } #endif -struct ifmultiaddr * -ifmaof_ifpforaddr(sa, ifp) - struct sockaddr *sa; - struct ifnet *ifp; -{ - struct ifmultiaddr *ifma; - - for (ifma = ifp->if_multiaddrs.lh_first; ifma; - ifma = ifma->ifma_link.le_next) - if (equal(ifma->ifma_addr, sa)) - break; - - return ifma; -} - -SYSCTL_NODE(_net, PF_LINK, link, CTLFLAG_RW, 0, "Link layers"); -SYSCTL_NODE(_net_link, 0, generic, CTLFLAG_RW, 0, "Generic link-management"); +SYSCTL_NODE(_net, PF_LINK, link, CTLFLAG_RW|CTLFLAG_LOCKED, 0, "Link layers"); +SYSCTL_NODE(_net_link, 0, generic, CTLFLAG_RW|CTLFLAG_LOCKED, 0, "Generic link-management"); /* * Shutdown all network activity. Used boot() when halting * system. */ -int if_down_all(void) +int +if_down_all(void) { - struct ifnet *ifp; - int s; - - s = splnet(); - TAILQ_FOREACH(ifp, &ifnet, if_link) - if_down(ifp); + struct ifnet **ifp; + u_int32_t count; + u_int32_t i; + + if (ifnet_list_get_all(IFNET_FAMILY_ANY, &ifp, &count) == 0) { + for (i = 0; i < count; i++) { + if_down(ifp[i]); + dlil_proto_unplumb_all(ifp[i]); + } + ifnet_list_free(ifp); + } - splx(s); - return(0); /* Sheesh */ + return 0; } /* @@ -1359,44 +2785,310 @@ int if_down_all(void) * */ static int -if_rtdel(rn, arg) - struct radix_node *rn; - void *arg; +if_rtdel(struct radix_node *rn, void *arg) { struct rtentry *rt = (struct rtentry *)rn; struct ifnet *ifp = arg; int err; - if (rt != NULL && rt->rt_ifp == ifp) { - + if (rt == NULL) + return (0); + /* + * Checking against RTF_UP protects against walktree + * recursion problems with cloned routes. + */ + RT_LOCK(rt); + if (rt->rt_ifp == ifp && (rt->rt_flags & RTF_UP)) { /* - * Protect (sorta) against walktree recursion problems - * with cloned routes + * Safe to drop rt_lock and use rt_key, rt_gateway, + * since holding rnh_lock here prevents another thread + * from calling rt_setgate() on this route. */ - if ((rt->rt_flags & RTF_UP) == 0) - return (0); - - err = rtrequest(RTM_DELETE, rt_key(rt), rt->rt_gateway, - rt_mask(rt), rt->rt_flags, - (struct rtentry **) NULL); + RT_UNLOCK(rt); + err = rtrequest_locked(RTM_DELETE, rt_key(rt), rt->rt_gateway, + rt_mask(rt), rt->rt_flags, NULL); if (err) { log(LOG_WARNING, "if_rtdel: error %d\n", err); } + } else { + RT_UNLOCK(rt); } - return (0); } /* - * Removes routing table reference to a given interfacei + * Removes routing table reference to a given interface * for a given protocol family */ +void +if_rtproto_del(struct ifnet *ifp, int protocol) +{ + struct radix_node_head *rnh; + + if (use_routegenid) + routegenid_update(); + if ((protocol <= AF_MAX) && (protocol >= 0) && + ((rnh = rt_tables[protocol]) != NULL) && (ifp != NULL)) { + lck_mtx_lock(rnh_lock); + (void) rnh->rnh_walktree(rnh, if_rtdel, ifp); + lck_mtx_unlock(rnh_lock); + } +} + +static int +if_rtmtu(struct radix_node *rn, void *arg) +{ + struct rtentry *rt = (struct rtentry *)rn; + struct ifnet *ifp = arg; + + RT_LOCK(rt); + if (rt->rt_ifp == ifp) { + /* + * Update the MTU of this entry only if the MTU + * has not been locked (RTV_MTU is not set) and + * if it was non-zero to begin with. + */ + if (!(rt->rt_rmx.rmx_locks & RTV_MTU) && rt->rt_rmx.rmx_mtu) + rt->rt_rmx.rmx_mtu = ifp->if_mtu; + } + RT_UNLOCK(rt); + + return (0); +} + +/* + * Update the MTU metric of all route entries in all protocol tables + * associated with a particular interface; this is called when the + * MTU of that interface has changed. + */ +static +void if_rtmtu_update(struct ifnet *ifp) +{ + struct radix_node_head *rnh; + int p; + + for (p = 0; p < AF_MAX + 1; p++) { + if ((rnh = rt_tables[p]) == NULL) + continue; + + lck_mtx_lock(rnh_lock); + (void) rnh->rnh_walktree(rnh, if_rtmtu, ifp); + lck_mtx_unlock(rnh_lock); + } + + if (use_routegenid) + routegenid_update(); +} -void if_rtproto_del(struct ifnet *ifp, int protocol) +__private_extern__ void +if_data_internal_to_if_data(struct ifnet *ifp, + const struct if_data_internal *if_data_int, struct if_data *if_data) { +#pragma unused(ifp) +#define COPYFIELD(fld) if_data->fld = if_data_int->fld +#define COPYFIELD32(fld) if_data->fld = (u_int32_t)(if_data_int->fld) +/* compiler will cast down to 32-bit */ +#define COPYFIELD32_ATOMIC(fld) do { \ + atomic_get_64(if_data->fld, \ + (u_int64_t *)(void *)(uintptr_t)&if_data_int->fld); \ +} while (0) + + COPYFIELD(ifi_type); + COPYFIELD(ifi_typelen); + COPYFIELD(ifi_physical); + COPYFIELD(ifi_addrlen); + COPYFIELD(ifi_hdrlen); + COPYFIELD(ifi_recvquota); + COPYFIELD(ifi_xmitquota); + if_data->ifi_unused1 = 0; + COPYFIELD(ifi_mtu); + COPYFIELD(ifi_metric); + if (if_data_int->ifi_baudrate & 0xFFFFFFFF00000000LL) { + if_data->ifi_baudrate = 0xFFFFFFFF; + } else { + COPYFIELD32(ifi_baudrate); + } + + COPYFIELD32_ATOMIC(ifi_ipackets); + COPYFIELD32_ATOMIC(ifi_ierrors); + COPYFIELD32_ATOMIC(ifi_opackets); + COPYFIELD32_ATOMIC(ifi_oerrors); + COPYFIELD32_ATOMIC(ifi_collisions); + COPYFIELD32_ATOMIC(ifi_ibytes); + COPYFIELD32_ATOMIC(ifi_obytes); + COPYFIELD32_ATOMIC(ifi_imcasts); + COPYFIELD32_ATOMIC(ifi_omcasts); + COPYFIELD32_ATOMIC(ifi_iqdrops); + COPYFIELD32_ATOMIC(ifi_noproto); + + COPYFIELD(ifi_recvtiming); + COPYFIELD(ifi_xmittiming); - struct radix_node_head *rnh; + if_data->ifi_lastchange.tv_sec = if_data_int->ifi_lastchange.tv_sec; + if_data->ifi_lastchange.tv_usec = if_data_int->ifi_lastchange.tv_usec; - if ((protocol <= AF_MAX) && ((rnh = rt_tables[protocol]) != NULL) && (ifp != NULL)) - (void) rnh->rnh_walktree(rnh, if_rtdel, ifp); +#if IF_LASTCHANGEUPTIME + if_data->ifi_lastchange.tv_sec += boottime_sec(); +#endif + + if_data->ifi_unused2 = 0; + COPYFIELD(ifi_hwassist); + if_data->ifi_reserved1 = 0; + if_data->ifi_reserved2 = 0; +#undef COPYFIELD32_ATOMIC +#undef COPYFIELD32 +#undef COPYFIELD +} + +__private_extern__ void +if_data_internal_to_if_data64(struct ifnet *ifp, + const struct if_data_internal *if_data_int, + struct if_data64 *if_data64) +{ +#pragma unused(ifp) +#define COPYFIELD64(fld) if_data64->fld = if_data_int->fld +#define COPYFIELD64_ATOMIC(fld) do { \ + atomic_get_64(if_data64->fld, \ + (u_int64_t *)(void *)(uintptr_t)&if_data_int->fld); \ +} while (0) + + COPYFIELD64(ifi_type); + COPYFIELD64(ifi_typelen); + COPYFIELD64(ifi_physical); + COPYFIELD64(ifi_addrlen); + COPYFIELD64(ifi_hdrlen); + COPYFIELD64(ifi_recvquota); + COPYFIELD64(ifi_xmitquota); + if_data64->ifi_unused1 = 0; + COPYFIELD64(ifi_mtu); + COPYFIELD64(ifi_metric); + COPYFIELD64(ifi_baudrate); + + COPYFIELD64_ATOMIC(ifi_ipackets); + COPYFIELD64_ATOMIC(ifi_ierrors); + COPYFIELD64_ATOMIC(ifi_opackets); + COPYFIELD64_ATOMIC(ifi_oerrors); + COPYFIELD64_ATOMIC(ifi_collisions); + COPYFIELD64_ATOMIC(ifi_ibytes); + COPYFIELD64_ATOMIC(ifi_obytes); + COPYFIELD64_ATOMIC(ifi_imcasts); + COPYFIELD64_ATOMIC(ifi_omcasts); + COPYFIELD64_ATOMIC(ifi_iqdrops); + COPYFIELD64_ATOMIC(ifi_noproto); + + /* Note these two fields are actually 32 bit, so doing COPYFIELD64_ATOMIC will + * cause them to be misaligned + */ + COPYFIELD64(ifi_recvtiming); + COPYFIELD64(ifi_xmittiming); + + if_data64->ifi_lastchange.tv_sec = if_data_int->ifi_lastchange.tv_sec; + if_data64->ifi_lastchange.tv_usec = if_data_int->ifi_lastchange.tv_usec; + +#if IF_LASTCHANGEUPTIME + if_data64->ifi_lastchange.tv_sec += boottime_sec(); +#endif + +#undef COPYFIELD64 +} + +__private_extern__ void +if_copy_traffic_class(struct ifnet *ifp, + struct if_traffic_class *if_tc) +{ +#define COPY_IF_TC_FIELD64_ATOMIC(fld) do { \ + atomic_get_64(if_tc->fld, \ + (u_int64_t *)(void *)(uintptr_t)&ifp->if_tc.fld); \ +} while (0) + + COPY_IF_TC_FIELD64_ATOMIC(ifi_ibkpackets); + COPY_IF_TC_FIELD64_ATOMIC(ifi_ibkbytes); + COPY_IF_TC_FIELD64_ATOMIC(ifi_obkpackets); + COPY_IF_TC_FIELD64_ATOMIC(ifi_obkbytes); + COPY_IF_TC_FIELD64_ATOMIC(ifi_ivipackets); + COPY_IF_TC_FIELD64_ATOMIC(ifi_ivibytes); + COPY_IF_TC_FIELD64_ATOMIC(ifi_ovipackets); + COPY_IF_TC_FIELD64_ATOMIC(ifi_ovibytes); + COPY_IF_TC_FIELD64_ATOMIC(ifi_ivopackets); + COPY_IF_TC_FIELD64_ATOMIC(ifi_ivobytes); + COPY_IF_TC_FIELD64_ATOMIC(ifi_ovopackets); + COPY_IF_TC_FIELD64_ATOMIC(ifi_ovobytes); + +#undef COPY_IF_TC_FIELD64_ATOMIC +} + + +struct ifaddr * +ifa_remref(struct ifaddr *ifa, int locked) +{ + if (!locked) + IFA_LOCK_SPIN(ifa); + else + IFA_LOCK_ASSERT_HELD(ifa); + + if (ifa->ifa_refcnt == 0) + panic("%s: ifa %p negative refcnt\n", __func__, ifa); + else if (ifa->ifa_trace != NULL) + (*ifa->ifa_trace)(ifa, FALSE); + if (--ifa->ifa_refcnt == 0) { + if (ifa->ifa_debug & IFD_ATTACHED) + panic("ifa %p attached to ifp is being freed\n", ifa); + /* + * Some interface addresses are allocated either statically + * or carved out of a larger block; e.g. AppleTalk addresses. + * Only free it if it was allocated via MALLOC or via the + * corresponding per-address family allocator. Otherwise, + * leave it alone. + */ + if (ifa->ifa_debug & IFD_ALLOC) { + if (ifa->ifa_free == NULL) { + IFA_UNLOCK(ifa); + FREE(ifa, M_IFADDR); + } else { + /* Become a regular mutex */ + IFA_CONVERT_LOCK(ifa); + /* callee will unlock */ + (*ifa->ifa_free)(ifa); + } + } else { + IFA_UNLOCK(ifa); + } + ifa = NULL; + } + + if (!locked && ifa != NULL) + IFA_UNLOCK(ifa); + + return (ifa); +} + +void +ifa_addref(struct ifaddr *ifa, int locked) +{ + if (!locked) + IFA_LOCK_SPIN(ifa); + else + IFA_LOCK_ASSERT_HELD(ifa); + + if (++ifa->ifa_refcnt == 0) { + panic("%s: ifa %p wraparound refcnt\n", __func__, ifa); + /* NOTREACHED */ + } else if (ifa->ifa_trace != NULL) { + (*ifa->ifa_trace)(ifa, TRUE); + } + if (!locked) + IFA_UNLOCK(ifa); +} + +void +ifa_lock_init(struct ifaddr *ifa) +{ + lck_mtx_init(&ifa->ifa_lock, ifa_mtx_grp, ifa_mtx_attr); +} + +void +ifa_lock_destroy(struct ifaddr *ifa) +{ + IFA_LOCK_ASSERT_NOTHELD(ifa); + lck_mtx_destroy(&ifa->ifa_lock, ifa_mtx_grp); }