X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/e5568f75972dfc723778653c11cb6b4dc825716a..4a3eedf9ecc9bbe3f3a5c6ce5e53ad199d639d32:/bsd/net/if.c diff --git a/bsd/net/if.c b/bsd/net/if.c index e5f361d70..499b4790c 100644 --- a/bsd/net/if.c +++ b/bsd/net/if.c @@ -1,23 +1,29 @@ /* - * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2000-2007 Apple Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * - * The contents of this file constitute Original Code as defined in and - * are subject to the Apple Public Source License Version 1.1 (the - * "License"). You may not use this file except in compliance with the - * License. Please obtain a copy of the License at - * http://www.apple.com/publicsource and read it before using this file. + * 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. 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. * - * This Original Code and all software distributed under the License are - * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * 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 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the - * License for the specific language governing rights and limitations - * under the License. + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * 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 @@ -54,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 @@ -73,15 +87,19 @@ #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 @@ -91,23 +109,37 @@ #endif #endif +extern u_long route_generation; +extern int use_routegenid; +extern int dlil_multithreaded_input; +extern struct dlil_threading_info *dlil_lo_thread_ptr; + +#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 *)); +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_rtmtu(struct radix_node *, void *); +static void if_rtmtu_update(struct ifnet *); static struct if_clone *if_clone_lookup(const char *, int *); -static int if_clone_list(struct if_clonereq *); +#ifdef IF_CLONE_LIST +static int if_clone_list(int count, int * total, user_addr_t dst); +#endif MALLOC_DEFINE(M_IFADDR, "ifaddr", "interface address"); MALLOC_DEFINE(M_IFMADDR, "ether_multi", "link-level multicast address"); int ifqmaxlen = IFQ_MAXLEN; -struct ifnethead ifnet = TAILQ_HEAD_INITIALIZER(ifnet); -struct ifmultihead ifma_lostlist = LIST_HEAD_INITIALIZER(ifma_lostlist); +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); @@ -117,7 +149,7 @@ LIST_HEAD(, if_clone) if_cloners = LIST_HEAD_INITIALIZER(if_cloners); * XXX: declare here to avoid to include many inet6 related files.. * should be more generalized? */ -extern void nd6_setmtu __P((struct ifnet *)); +extern void nd6_setmtu(struct ifnet *); #endif #define M_CLONE M_IFADDR @@ -133,6 +165,48 @@ int if_index; struct ifaddr **ifnet_addrs; struct ifnet **ifindex2ifnet; +__private_extern__ void +if_attach_ifa( + struct ifnet *ifp, + struct ifaddr *ifa) +{ + ifnet_lock_assert(ifp, LCK_MTX_ASSERT_OWNED); + if (ifa->ifa_debug & IFA_ATTACHED) { + panic("if_attach_ifa: Attempted to attach address that's already attached!\n"); + } + ifaref(ifa); + ifa->ifa_debug |= IFA_ATTACHED; + TAILQ_INSERT_TAIL(&ifp->if_addrhead, ifa, ifa_link); +} + +__private_extern__ void +if_detach_ifa( + struct ifnet *ifp, + struct ifaddr *ifa) +{ + ifnet_lock_assert(ifp, LCK_MTX_ASSERT_OWNED); +#if 1 + /* Debugging code */ + if ((ifa->ifa_debug & IFA_ATTACHED) == 0) { + printf("if_detach_ifa: ifa is not attached to any interface! flags=%lu\n", ifa->ifa_debug); + return; + } + else { + struct ifaddr *ifa2; + TAILQ_FOREACH(ifa2, &ifp->if_addrhead, ifa_link) { + if (ifa2 == ifa) + break; + } + if (ifa2 != ifa) { + printf("if_detach_ifa: Attempted to detach IFA that was not attached!\n"); + } + } +#endif + TAILQ_REMOVE(&ifp->if_addrhead, ifa, ifa_link); + ifa->ifa_debug &= ~IFA_ATTACHED; + ifafree(ifa); +} + #define INITIAL_IF_INDEXLIM 8 /* @@ -148,17 +222,14 @@ struct ifnet **ifindex2ifnet; * always allocate one extra element to hold ifindex2ifnet[0], which * is unused. */ -static int +int if_next_index(void); + +__private_extern__ int if_next_index(void) { static int if_indexlim = 0; - static int if_list_growing = 0; int new_index; - while (if_list_growing) { - /* wait until list is done growing */ - (void)tsleep((caddr_t)&ifnet_addrs, PZERO, "if_next_index", 0); - } new_index = ++if_index; if (if_index > if_indexlim) { unsigned n; @@ -167,9 +238,6 @@ if_next_index(void) caddr_t new_ifindex2ifnet; caddr_t old_ifnet_addrs; - /* mark list as growing */ - if_list_growing = 1; - old_ifnet_addrs = (caddr_t)ifnet_addrs; if (ifnet_addrs == NULL) { new_if_indexlim = INITIAL_IF_INDEXLIM; @@ -201,89 +269,8 @@ if_next_index(void) if (old_ifnet_addrs != NULL) { _FREE((caddr_t)old_ifnet_addrs, M_IFADDR); } - - /* wake up others that might be blocked */ - if_list_growing = 0; - wakeup((caddr_t)&ifnet_addrs); } return (new_index); - -} - -/* - * Attach an interface to the - * list of "active" interfaces. - */ -void -old_if_attach(ifp) - struct ifnet *ifp; -{ - unsigned socksize, ifasize; - int namelen, masklen; - char workbuf[64]; - register struct sockaddr_dl *sdl; - register struct ifaddr *ifa; - - if (ifp->if_snd.ifq_maxlen == 0) - ifp->if_snd.ifq_maxlen = ifqmaxlen; - - /* - * 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 ((ifp->if_eflags & IFEF_REUSE) == 0 || ifp->if_index == 0) { - /* allocate a new entry */ - ifp->if_index = if_next_index(); - ifindex2ifnet[ifp->if_index] = ifp; - - /* - * 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[ifp->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; - } - } else { - ifa = ifnet_addrs[ifp->if_index - 1]; - } - if (ifa != NULL) { - TAILQ_INSERT_HEAD(&ifp->if_addrhead, ifa, ifa_link); - } - TAILQ_INSERT_TAIL(&ifnet, ifp, if_link); } /* @@ -361,7 +348,7 @@ if_clone_create(char *name, int len) /* * Destroy a clone network interface. */ -int +static int if_clone_destroy(const char *name) { struct if_clone *ifc; @@ -405,7 +392,7 @@ if_clone_lookup(const char *name, int *unitp) { struct if_clone *ifc; const char *cp; - int i; + size_t i; for (ifc = LIST_FIRST(&if_cloners); ifc != NULL;) { for (cp = name, i = 0; i < ifc->ifc_namelen; i++, cp++) { @@ -492,31 +479,31 @@ if_clone_detach(struct if_clone *ifc) if_cloners_count--; } +#ifdef IF_CLONE_LIST /* * Provide list of interface cloners to userspace. */ static int -if_clone_list(struct if_clonereq *ifcr) +if_clone_list(int count, int * total, user_addr_t dst) { - char outbuf[IFNAMSIZ], *dst; + char outbuf[IFNAMSIZ]; struct if_clone *ifc; - int count, error = 0; + int error = 0; - ifcr->ifcr_total = if_cloners_count; - if ((dst = ifcr->ifcr_buffer) == NULL) { + *total = if_cloners_count; + if (dst == USER_ADDR_NULL) { /* Just asking how many there are. */ return (0); } - if (ifcr->ifcr_count < 0) + if (count < 0) return (EINVAL); - count = (if_cloners_count < ifcr->ifcr_count) ? - if_cloners_count : ifcr->ifcr_count; + 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) { - strncpy(outbuf, ifc->ifc_name, IFNAMSIZ - 1); + strlcpy(outbuf, ifc->ifc_name, IFNAMSIZ); error = copyout(outbuf, dst, IFNAMSIZ); if (error) break; @@ -524,27 +511,58 @@ if_clone_list(struct if_clonereq *ifcr) return (error); } +#endif IF_CLONE_LIST __private_extern__ int -ifa_foraddr(addr) - unsigned int addr; +ifa_foraddr( + unsigned int addr) { - register struct ifnet *ifp; - register struct ifaddr *ifa; - register unsigned int addr2; + struct ifnet *ifp; + struct ifaddr *ifa; + unsigned int addr2; + int result = 0; - - for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_link.tqe_next) + 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) { - if (ifa->ifa_addr->sa_family != AF_INET) - continue; - addr2 = IA_SIN(ifa)->sin_addr.s_addr; + if (ifa->ifa_addr->sa_family != AF_INET) + continue; + addr2 = IA_SIN(ifa)->sin_addr.s_addr; + + if (addr == addr2) { + result = 1; + break; + } + } + ifnet_lock_done(ifp); + } + ifnet_head_done(); + + return result; +} - if (addr == addr2) - return (1); +/* + * 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 *ifa0 = NULL, *ifa; + + ifnet_lock_shared(ifp); + TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { + if (ifa->ifa_addr->sa_family == family && ifa0 == NULL) { + ifa0 = ifa; + break; + } } - return (0); + if (ifa0 != NULL) + ifaref(ifa0); + ifnet_lock_done(ifp); + + return (ifa0); } /* @@ -552,50 +570,75 @@ ifa_foraddr(addr) */ /*ARGSUSED*/ struct ifaddr * -ifa_ifwithaddr(addr) - register struct sockaddr *addr; +ifa_ifwithaddr( + const struct sockaddr *addr) { - register struct ifnet *ifp; - register struct ifaddr *ifa; + struct ifnet *ifp; + struct ifaddr *ifa; + struct ifaddr *result = NULL; #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); - } - return ((struct ifaddr *)0); + (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) { + if (ifa->ifa_addr->sa_family != addr->sa_family) + continue; + if (equal(addr, ifa->ifa_addr)) { + result = ifa; + break; + } + 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)) { + result = ifa; + break; + } + } + if (result) + ifaref(result); + 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(addr) - register struct sockaddr *addr; +ifa_ifwithdstaddr( + const struct sockaddr *addr) { - register struct ifnet *ifp; - register struct ifaddr *ifa; - - 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); + 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) { + if (ifa->ifa_addr->sa_family != addr->sa_family) + continue; + if (ifa->ifa_dstaddr && equal(addr, ifa->ifa_dstaddr)) { + result = ifa; + break; + } + } + if (result) + ifaref(result); + ifnet_lock_done(ifp); + } } - return ((struct ifaddr *)0); + ifnet_head_done(); + return result; } /* @@ -603,33 +646,42 @@ ifa_ifwithdstaddr(addr) * is most specific found. */ struct ifaddr * -ifa_ifwithnet(addr) - struct sockaddr *addr; +ifa_ifwithnet( + const struct sockaddr *addr) { - register struct ifnet *ifp; - register struct ifaddr *ifa; + struct ifnet *ifp; + struct ifaddr *ifa = NULL; struct ifaddr *ifa_maybe = (struct ifaddr *) 0; u_int af = addr->sa_family; char *addr_data = addr->sa_data, *cplim; + 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) { - 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]); + 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) + ifaref(ifa); + + ifnet_head_done(); + return ifa; + } } /* * 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 (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) { - register char *cp, *cp2, *cp3; + char *cp, *cp2, *cp3; if (ifa->ifa_addr->sa_family != af) next: continue; @@ -653,8 +705,9 @@ next: continue; * netmask for the remote end. */ if (ifa->ifa_dstaddr != 0 - && equal(addr, ifa->ifa_dstaddr)) - return (ifa); + && equal(addr, ifa->ifa_dstaddr)) { + break; + } } else #endif /* __APPLE__*/ { @@ -663,8 +716,8 @@ next: continue; * then use it instead of the generic one. */ if (ifa->ifa_claim_addr) { - if ((*ifa->ifa_claim_addr)(ifa, addr)) { - return (ifa); + if (ifa->ifa_claim_addr(ifa, addr)) { + break; } else { continue; } @@ -696,12 +749,38 @@ next: continue; */ if (ifa_maybe == 0 || rn_refines((caddr_t)ifa->ifa_netmask, - (caddr_t)ifa_maybe->ifa_netmask)) + (caddr_t)ifa_maybe->ifa_netmask)) { + ifaref(ifa); + if (ifa_maybe) + ifafree(ifa_maybe); ifa_maybe = ifa; + } } } + + if (ifa) { + ifaref(ifa); + } + + /* + * ifa is set if we found an exact match. + * take a reference to the ifa before + * releasing the ifp lock + */ + ifnet_lock_done(ifp); + + if (ifa) { + break; + } } - return (ifa_maybe); + ifnet_head_done(); + if (!ifa) + ifa = ifa_maybe; + else if (ifa_maybe) { + ifafree(ifa_maybe); + ifa_maybe = NULL; + } + return ifa; } /* @@ -709,18 +788,21 @@ next: continue; * a given address. */ struct ifaddr * -ifaof_ifpforaddr(addr, ifp) - struct sockaddr *addr; - register struct ifnet *ifp; +ifaof_ifpforaddr( + const struct sockaddr *addr, + struct ifnet *ifp) { - register struct ifaddr *ifa; - register char *cp, *cp2, *cp3; - register char *cplim; - struct ifaddr *ifa_maybe = 0; + 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 (0); + return (NULL); + + ifnet_lock_shared(ifp); for (ifa = ifp->if_addrhead.tqh_first; ifa; ifa = ifa->ifa_link.tqe_next) { if (ifa->ifa_addr->sa_family != af) @@ -730,13 +812,17 @@ ifaof_ifpforaddr(addr, ifp) if (ifa->ifa_netmask == 0) { if (equal(addr, ifa->ifa_addr) || (ifa->ifa_dstaddr && equal(addr, ifa->ifa_dstaddr))) - return (ifa); + break; continue; } if (ifp->if_flags & IFF_POINTOPOINT) { if (equal(addr, ifa->ifa_dstaddr)) - return (ifa); + break; } else { + if (equal(addr, ifa->ifa_addr)) { + /* exact match */ + break; + } cp = addr->sa_data; cp2 = ifa->ifa_addr->sa_data; cp3 = ifa->ifa_netmask->sa_data; @@ -744,11 +830,26 @@ ifaof_ifpforaddr(addr, ifp) for (; cp3 < cplim; cp3++) if ((*cp++ ^ *cp2++) & *cp3) break; - if (cp3 == cplim) - return (ifa); + if (cp3 == cplim) { + /* subnet match */ + if (better_ifa_maybe == NULL) { + better_ifa_maybe = ifa; + } + } + } + } + + if (ifa == NULL) { + if (better_ifa_maybe != NULL) { + ifa = better_ifa_maybe; + } else { + ifa = ifa_maybe; } } - return (ifa_maybe); + if (ifa) ifaref(ifa); + + ifnet_lock_done(ifp); + return ifa; } #include @@ -758,13 +859,10 @@ ifaof_ifpforaddr(addr, ifp) * Lookup an appropriate real ifa to point to. * This should be moved to /sys/net/link.c eventually. */ -static void -link_rtrequest(cmd, rt, sa) - int cmd; - register struct rtentry *rt; - struct sockaddr *sa; +void +link_rtrequest(int cmd, struct rtentry *rt, struct sockaddr *sa) { - register struct ifaddr *ifa; + struct ifaddr *ifa; struct sockaddr *dst; struct ifnet *ifp; @@ -776,93 +874,119 @@ link_rtrequest(cmd, rt, sa) rtsetifa(rt, ifa); if (ifa->ifa_rtrequest && ifa->ifa_rtrequest != link_rtrequest) ifa->ifa_rtrequest(cmd, rt, sa); + ifafree(ifa); } } /* - * 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); -} - -/* - * Mark an interface up and notify protocols of - * the transition. - * NOTE: must be called at splnet or eqivalent. + * 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. */ -void -if_route(ifp, flag, fam) - register struct ifnet *ifp; - int flag, fam; +__private_extern__ void +if_updown( + struct ifnet *ifp, + int up) { - 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_IFUP, ifa->ifa_addr); + 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. - * NOTE: must be called at splnet or eqivalent. */ void -if_down(ifp) - register struct ifnet *ifp; +if_down( + struct ifnet *ifp) { - - if_unroute(ifp, IFF_UP, AF_UNSPEC); + ifnet_lock_exclusive(ifp); + if_updown(ifp, 0); + ifnet_lock_done(ifp); } /* * Mark an interface up and notify protocols of * the transition. - * NOTE: must be called at splnet or eqivalent. */ void -if_up(ifp) - register struct ifnet *ifp; +if_up( + struct ifnet *ifp) { - - if_route(ifp, IFF_UP, AF_UNSPEC); + ifnet_lock_exclusive(ifp); + if_updown(ifp, 1); + ifnet_lock_done(ifp); } /* * Flush an interface queue. */ static void -if_qflush(ifq) - register struct ifqueue *ifq; +if_qflush(struct ifqueue *ifq) { - register struct mbuf *m, *n; + 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; } @@ -904,12 +1028,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); } @@ -919,8 +1045,7 @@ 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; @@ -946,14 +1071,10 @@ if_withname(sa) * Interface ioctls. */ int -ifioctl(so, cmd, data, p) - struct socket *so; - u_long cmd; - caddr_t data; - struct proc *p; +ifioctl(struct socket *so, u_long cmd, caddr_t data, struct proc *p) { - register struct ifnet *ifp; - register struct ifreq *ifr; + struct ifnet *ifp; + struct ifreq *ifr; struct ifstat *ifs; int error = 0; short oif_flags; @@ -961,26 +1082,41 @@ ifioctl(so, cmd, data, p) struct net_event_data ev_data; switch (cmd) { - case SIOCGIFCONF: case OSIOCGIFCONF: - return (ifconf(cmd, data)); + case SIOCGIFCONF64: + { + struct ifconf64 * ifc = (struct ifconf64 *)data; + user_addr_t user_addr; + + user_addr = proc_is64bit(p) + ? ifc->ifc_req64 : CAST_USER_ADDR_T(ifc->ifc_req); + return (ifconf(cmd, user_addr, &ifc->ifc_len)); + } + break; } ifr = (struct ifreq *)data; - switch (cmd) { case SIOCIFCREATE: case SIOCIFDESTROY: - error = suser(p->p_ucred, &p->p_acflag); + error = proc_suser(p); if (error) return (error); return ((cmd == SIOCIFCREATE) ? if_clone_create(ifr->ifr_name, sizeof(ifr->ifr_name)) : if_clone_destroy(ifr->ifr_name)); -#if 0 +#if IF_CLONE_LIST case SIOCIFGCLONERS: - return (if_clone_list((struct if_clonereq *)data)); -#endif 0 + case SIOCIFGCLONERS64: + { + struct if_clonereq64 * ifcr = (struct if_clonereq64 *)data; + user_addr = proc_is64bit(p) + ? ifcr->ifcr_ifcru.ifcru_buffer64 + : CAST_USER_ADDR_T(ifcr->ifcr_ifcru.ifcru_buffer32); + return (if_clone_list(ifcr->ifcr_count, &ifcr->ifcr_total, + user_data)); + } +#endif IF_CLONE_LIST } ifp = ifunit(ifr->ifr_name); @@ -989,46 +1125,45 @@ ifioctl(so, cmd, data, p) switch (cmd) { case SIOCGIFFLAGS: + ifnet_lock_shared(ifp); ifr->ifr_flags = ifp->if_flags; + ifnet_lock_done(ifp); break; +#if CONFIG_MACF_NET + case SIOCGIFMAC: + error = mac_ifnet_label_get(kauth_cred_get(), ifr, ifp); + if (error) + return (error); + 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); + error = proc_suser(p); 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 = dlil_ioctl(so->so_proto->pr_domain->dom_family, - ifp, cmd, (caddr_t) data); + ifnet_set_flags(ifp, ifr->ifr_flags, (u_int16_t)~IFF_CANTCHANGE); + + error = ifnet_ioctl(ifp, so->so_proto->pr_domain->dom_family, + cmd, data); if (error == 0) { ev_msg.vendor_code = KEV_VENDOR_APPLE; @@ -1036,7 +1171,7 @@ ifioctl(so, cmd, data, p) ev_msg.kev_subclass = KEV_DL_SUBCLASS; ev_msg.event_code = KEV_DL_SIFFLAGS; - 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 = (unsigned long) ifp->if_unit; ev_msg.dv[0].data_length = sizeof(struct net_event_data); @@ -1044,11 +1179,18 @@ ifioctl(so, cmd, data, p) ev_msg.dv[1].data_length = 0; kev_post_msg(&ev_msg); } - getmicrotime(&ifp->if_lastchange); + ifnet_touch_lastchange(ifp); break; +#if CONFIG_MACF_NET + case SIOCSIFMAC: + error = mac_ifnet_label_set(kauth_cred_get(), ifr, ifp); + if (error) + return (error); + break; +#endif case SIOCSIFMETRIC: - error = suser(p->p_ucred, &p->p_acflag); + error = proc_suser(p); if (error) return (error); ifp->if_metric = ifr->ifr_metric; @@ -1059,7 +1201,7 @@ ifioctl(so, cmd, data, p) 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 = (unsigned long) ifp->if_unit; ev_msg.dv[0].data_length = sizeof(struct net_event_data); @@ -1068,16 +1210,16 @@ ifioctl(so, cmd, data, p) 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); + error = proc_suser(p); if (error) return error; - 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) { ev_msg.vendor_code = KEV_VENDOR_APPLE; @@ -1085,7 +1227,7 @@ ifioctl(so, cmd, data, p) ev_msg.kev_subclass = KEV_DL_SUBCLASS; ev_msg.event_code = KEV_DL_SIFPHYS; - 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 = (unsigned long) ifp->if_unit; ev_msg.dv[0].data_length = sizeof(struct net_event_data); @@ -1093,7 +1235,7 @@ ifioctl(so, cmd, data, p) ev_msg.dv[1].data_length = 0; kev_post_msg(&ev_msg); - getmicrotime(&ifp->if_lastchange); + ifnet_touch_lastchange(ifp); } return(error); @@ -1101,7 +1243,7 @@ ifioctl(so, cmd, data, p) { u_long oldmtu = ifp->if_mtu; - error = suser(p->p_ucred, &p->p_acflag); + error = proc_suser(p); if (error) return (error); if (ifp->if_ioctl == NULL) @@ -1109,8 +1251,8 @@ ifioctl(so, cmd, data, p) if (ifr->ifr_mtu < IF_MINMTU || ifr->ifr_mtu > IF_MAXMTU) return (EINVAL); - 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) { ev_msg.vendor_code = KEV_VENDOR_APPLE; @@ -1118,7 +1260,7 @@ ifioctl(so, cmd, data, p) ev_msg.kev_subclass = KEV_DL_SUBCLASS; ev_msg.event_code = KEV_DL_SIFMTU; - 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 = (unsigned long) ifp->if_unit; ev_msg.dv[0].data_length = sizeof(struct net_event_data); @@ -1126,13 +1268,16 @@ ifioctl(so, cmd, data, p) ev_msg.dv[1].data_length = 0; kev_post_msg(&ev_msg); - getmicrotime(&ifp->if_lastchange); + ifnet_touch_lastchange(ifp); 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 (error == 0 && ifp->if_mtu != oldmtu) { + if_rtmtu_update(ifp); #if INET6 nd6_setmtu(ifp); #endif @@ -1142,7 +1287,7 @@ ifioctl(so, cmd, data, p) case SIOCADDMULTI: case SIOCDELMULTI: - error = suser(p->p_ucred, &p->p_acflag); + error = proc_suser(p); if (error) return (error); @@ -1157,8 +1302,7 @@ ifioctl(so, cmd, data, p) #endif if (cmd == SIOCADDMULTI) { - struct ifmultiaddr *ifma; - error = if_addmulti(ifp, &ifr->ifr_addr, &ifma); + error = if_addmulti(ifp, &ifr->ifr_addr, NULL); ev_msg.event_code = KEV_DL_ADDMULTI; } else { error = if_delmulti(ifp, &ifr->ifr_addr); @@ -1168,7 +1312,7 @@ ifioctl(so, cmd, data, p) 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); + 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; @@ -1177,32 +1321,31 @@ ifioctl(so, cmd, data, p) ev_msg.dv[1].data_length = 0; kev_post_msg(&ev_msg); - getmicrotime(&ifp->if_lastchange); + ifnet_touch_lastchange(ifp); } return error; - case SIOCSETVLAN: - if (ifp->if_type != IFT_L2VLAN) { - return (EOPNOTSUPP); - } case SIOCSIFPHYADDR: case SIOCDIFPHYADDR: -#ifdef INET6 +#if INET6 case SIOCSIFPHYADDR_IN6: #endif case SIOCSLIFPHYADDR: case SIOCSIFMEDIA: case SIOCSIFGENERIC: case SIOCSIFLLADDR: - error = suser(p->p_ucred, &p->p_acflag); + case SIOCSIFALTMTU: + case SIOCSIFVLAN: + case SIOCSIFBOND: + error = proc_suser(p); if (error) return (error); - 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) - getmicrotime(&ifp->if_lastchange); + ifnet_touch_lastchange(ifp); return error; case SIOCGIFSTATUS: @@ -1214,25 +1357,18 @@ ifioctl(so, cmd, data, p) case SIOCGLIFPHYADDR: case SIOCGIFMEDIA: case SIOCGIFGENERIC: - - return dlil_ioctl(so->so_proto->pr_domain->dom_family, - ifp, cmd, (caddr_t) data); - case SIOCGETVLAN: - if (ifp->if_type != IFT_L2VLAN) { - return (EOPNOTSUPP); - } - return dlil_ioctl(so->so_proto->pr_domain->dom_family, - ifp, cmd, (caddr_t) data); + case SIOCGIFDEVMTU: + return ifnet_ioctl(ifp, so->so_proto->pr_domain->dom_family, + cmd, data); + case SIOCGIFVLAN: + case SIOCGIFBOND: + return ifnet_ioctl(ifp, so->so_proto->pr_domain->dom_family, + cmd, data); 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 { int ocmd = cmd; @@ -1269,10 +1405,10 @@ ifioctl(so, cmd, data, p) case OSIOCGIFNETMASK: cmd = SIOCGIFNETMASK; } - error = ((*so->so_proto->pr_usrreqs->pru_control)(so, - cmd, - data, - ifp, p)); + 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: @@ -1283,58 +1419,85 @@ 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); } return (0); } +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 error = 0; int oldflags; + int locked = 0; + int changed = 0; + ifnet_lock_exclusive(ifp); + locked = 1; 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); + if ((ifp->if_flags & IFF_UP) == 0) { + error = ENETDOWN; + goto done; + } + if (ifp->if_pcount++ != 0) { + goto done; + } 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); + goto done; ifp->if_flags &= ~IFF_PROMISC; - log(LOG_INFO, "%s%d: promiscuous mode disabled\n", - ifp->if_name, ifp->if_unit); } ifr.ifr_flags = ifp->if_flags; - error = dlil_ioctl(0, ifp, SIOCSIFFLAGS, (caddr_t)&ifr); + locked = 0; + ifnet_lock_done(ifp); + error = ifnet_ioctl(ifp, 0, SIOCSIFFLAGS, &ifr); if (error == 0) rt_ifmsg(ifp); else ifp->if_flags = oldflags; +done: + if (locked) ifnet_lock_done(ifp); + if (changed) { + log(LOG_INFO, "%s%d: promiscuous mode %s\n", + ifp->if_name, ifp->if_unit, + pswitch != 0 ? "enabled" : "disabled"); + } return error; } @@ -1346,20 +1509,25 @@ ifpromisc(ifp, pswitch) */ /*ARGSUSED*/ static int -ifconf(cmd, data) - u_long cmd; - caddr_t data; +ifconf(u_long cmd, user_addr_t ifrp, int * ret_space) { - 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) { + 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); @@ -1367,65 +1535,63 @@ 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) { - register struct sockaddr *sa = ifa->ifa_addr; + 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)) { + 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, (caddr_t)ifrp, - sizeof (ifr)); - ifrp++; + error = copyout((caddr_t)&ifr, ifrp, sizeof(ifr)); + ifrp += sizeof(struct ifreq); } else { - if (space < sizeof (ifr) + sa->sa_len - - sizeof(*sa)) + 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)&ifr, 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); + (ifrp + offsetof(struct ifreq, ifr_addr)), + sa->sa_len); + } + ifrp += (sa->sa_len + offsetof(struct ifreq, ifr_addr)); } 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, (caddr_t)ifrp, - sizeof (ifr)); + error = copyout((caddr_t)&ifr, ifrp, sizeof (ifr)); if (error) break; space -= sizeof (ifr); - ifrp++; + ifrp += sizeof(struct ifreq); } } - ifc->ifc_len -= space; + ifnet_head_done(); + *ret_space -= space; return (error); } @@ -1433,17 +1599,17 @@ ifconf(cmd, data) * Just like if_promisc(), but for all-multicast-reception mode. */ int -if_allmulti(ifp, onswitch) - struct ifnet *ifp; - int onswitch; +if_allmulti(struct ifnet *ifp, int onswitch) { int error = 0; - int s = splimp(); + int modified = 0; + + ifnet_lock_exclusive(ifp); if (onswitch) { if (ifp->if_amcount++ == 0) { ifp->if_flags |= IFF_ALLMULTI; - error = dlil_ioctl(0, ifp, SIOCSIFFLAGS, (caddr_t) 0); + modified = 1; } } else { if (ifp->if_amcount > 1) { @@ -1451,207 +1617,351 @@ if_allmulti(ifp, onswitch) } else { ifp->if_amcount = 0; ifp->if_flags &= ~IFF_ALLMULTI; - error = dlil_ioctl(0, ifp, SIOCSIFFLAGS, (caddr_t) 0); + modified = 1; } } - splx(s); + ifnet_lock_done(ifp); + + if (modified) + error = ifnet_ioctl(ifp, 0, SIOCSIFFLAGS, NULL); if (error == 0) rt_ifmsg(ifp); return error; } -/* - * Add a multicast listenership to the interface in question. - * The link layer provides a routine which converts - */ -int -if_addmulti(ifp, sa, retifma) - struct ifnet *ifp; /* interface to manipulate */ - struct sockaddr *sa; /* address to add */ - struct ifmultiaddr **retifma; +void +ifma_reference( + struct ifmultiaddr *ifma) { - struct sockaddr *llsa = 0; - struct sockaddr *dupsa; - int error, s; - struct ifmultiaddr *ifma; - struct rslvmulti_req rsreq; + if (OSIncrementAtomic((SInt32 *)&ifma->ifma_refcount) <= 0) + panic("ifma_reference: ifma already released or invalid\n"); +} - /* - * If the matching multicast address already exists - * then don't add a new one, just add a reference - */ +void +ifma_release( + struct ifmultiaddr *ifma) +{ + while (ifma) { + struct ifmultiaddr *next; + int32_t prevValue = OSDecrementAtomic((SInt32 *)&ifma->ifma_refcount); + if (prevValue < 1) + panic("ifma_release: ifma already released or invalid\n"); + if (prevValue != 1) + break; + + /* Allow the allocator of the protospec to free it */ + if (ifma->ifma_protospec && ifma->ifma_free) { + ifma->ifma_free(ifma->ifma_protospec); + } + + next = ifma->ifma_ll; + FREE(ifma->ifma_addr, M_IFMADDR); + FREE(ifma, M_IFMADDR); + ifma = next; + } +} + + /* + * 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) +{ + struct ifmultiaddr *ifma; 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) + ifma->ifma_usecount++; + if (retifma) { *retifma = ifma; + ifma_reference(*retifma); + } return 0; } } + + return ENOENT; +} + +/* + * Radar 3642395, make sure all multicasts are in a standard format. + */ +static struct sockaddr* +copy_and_normalize( + const struct sockaddr *original) +{ + 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); + } + 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); + } + + return (struct sockaddr*)sdl_new; +} + +/* + * Add a multicast listenership to the interface in question. + * The link layer provides a routine which converts + */ +int +if_addmulti( + struct ifnet *ifp, /* interface to manipulate */ + const struct sockaddr *sa, /* address to add */ + struct ifmultiaddr **retifma) +{ + struct sockaddr_storage storage; + struct sockaddr *llsa = NULL; + struct sockaddr *dupsa = NULL; + int error = 0; + struct ifmultiaddr *ifma = NULL; + struct ifmultiaddr *llifma = NULL; + + /* 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) { + return ENOMEM; + } + sa = dupsa; + } + + ifnet_lock_exclusive(ifp); + error = if_addmulti_doesexist(ifp, sa, retifma); + 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. */ - rsreq.sa = sa; - rsreq.llsa = &llsa; - - error = dlil_ioctl(sa->sa_family, ifp, SIOCRSLVMULTI, (caddr_t) &rsreq); + 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; + } + + MALLOC(llifma, struct ifmultiaddr *, sizeof *llifma, M_IFMADDR, M_WAITOK); + if (llifma == NULL) { + error = ENOMEM; + goto cleanup; + } + } /* to be similar to FreeBSD */ - if (error == EOPNOTSUPP) + if (error == EOPNOTSUPP) { error = 0; + } + else if (error) { + goto cleanup; + } - if (error) - return error; - + /* Allocate while we aren't holding any locks */ + if (dupsa == NULL) { + dupsa = copy_and_normalize(sa); + if (dupsa == NULL) { + error = ENOMEM; + goto cleanup; + } + } 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); + if (ifma == NULL) { + error = ENOMEM; + goto cleanup; + } + + ifnet_lock_exclusive(ifp); + /* + * Check again for the matching multicast. + */ + if ((error = if_addmulti_doesexist(ifp, sa, retifma)) == 0) { + ifnet_lock_done(ifp); + goto cleanup; + } + bzero(ifma, sizeof(*ifma)); ifma->ifma_addr = dupsa; - ifma->ifma_lladdr = llsa; ifma->ifma_ifp = ifp; + ifma->ifma_usecount = 1; ifma->ifma_refcount = 1; - ifma->ifma_protospec = 0; - rt_newmaddrmsg(RTM_NEWMADDR, ifma); - - /* - * 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++; + + if (llifma != 0) { + if (if_addmulti_doesexist(ifp, llsa, &ifma->ifma_ll) == 0) { + FREE(llsa, M_IFMADDR); + FREE(llifma, M_IFMADDR); } 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); + bzero(llifma, sizeof(*llifma)); + llifma->ifma_addr = llsa; + llifma->ifma_ifp = ifp; + llifma->ifma_usecount = 1; + llifma->ifma_refcount = 1; + LIST_INSERT_HEAD(&ifp->if_multiaddrs, llifma, ifma_link); + + ifma->ifma_ll = llifma; + ifma_reference(ifma->ifma_ll); } } + + LIST_INSERT_HEAD(&ifp->if_multiaddrs, ifma, ifma_link); + + if (retifma) { + *retifma = ifma; + ifma_reference(*retifma); + } + + ifnet_lock_done(ifp); + + if (llsa != 0) + rt_newmaddrmsg(RTM_NEWMADDR, ifma); + /* * We are certain we have added something, so call down to the * interface to let them know about it. */ - s = splimp(); - dlil_ioctl(0, ifp, SIOCADDMULTI, (caddr_t) 0); - splx(s); - + ifnet_ioctl(ifp, 0, SIOCADDMULTI, NULL); + return 0; + +cleanup: + if (ifma) + FREE(ifma, M_IFADDR); + if (dupsa) + FREE(dupsa, M_IFADDR); + if (llifma) + FREE(llifma, M_IFADDR); + if (llsa) + FREE(llsa, M_IFADDR); + + return error; } int -if_delmultiaddr(struct ifmultiaddr *ifma) +if_delmultiaddr( + struct ifmultiaddr *ifma, + int locked) { - struct sockaddr *sa; struct ifnet *ifp; + int do_del_multi = 0; - /* Verify ifma is valid */ - { - struct ifmultiaddr *match = NULL; - for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_link.tqe_next) { - for (match = ifp->if_multiaddrs.lh_first; match; match = match->ifma_link.le_next) { - if (match->ifma_ifp != ifp) { - printf("if_delmultiaddr: ifma (%x) on ifp i(%s) is stale\n", - match, if_name(ifp)); - return (0) ; /* swallow error ? */ - } - if (match == ifma) - break; - } - if (match == ifma) - break; + ifp = ifma->ifma_ifp; + + if (!locked && ifp) { + ifnet_lock_exclusive(ifp); + } + + while (ifma != NULL) { + struct ifmultiaddr *ll_ifma; + + if (ifma->ifma_usecount > 1) { + ifma->ifma_usecount--; + break; } - if (match != ifma) { - for (match = ifma_lostlist.lh_first; match; match = match->ifma_link.le_next) { - if (match->ifma_ifp != NULL) { - printf("if_delmultiaddr: item on lost list (%x) contains non-null ifp=%s\n", - match, if_name(match->ifma_ifp)); - return (0) ; /* swallow error ? */ - } - if (match == ifma) - break; - } + + if (ifp) + LIST_REMOVE(ifma, ifma_link); + + ll_ifma = ifma->ifma_ll; + + if (ll_ifma) { /* send a routing msg for network addresses only */ + if (ifp) + ifnet_lock_done(ifp); + rt_newmaddrmsg(RTM_DELMADDR, ifma); + if (ifp) + ifnet_lock_exclusive(ifp); } - if (match != ifma) { - printf("if_delmultiaddr: ifma 0x%X is invalid\n", ifma); - return 0; + /* + * Make sure the interface driver is notified + * in the case of a link layer mcast group being left. + */ + if (ll_ifma == 0) { + if (ifp && ifma->ifma_addr->sa_family == AF_LINK) + do_del_multi = 1; + break; } - } + + if (ifp) + ifma_release(ifma); - if (ifma->ifma_refcount > 1) { - ifma->ifma_refcount--; - return 0; + ifma = ll_ifma; } - - sa = ifma->ifma_lladdr; - - if (sa) /* send a routing msg for network addresses only */ - rt_newmaddrmsg(RTM_DELMADDR, ifma); - - ifp = ifma->ifma_ifp; - LIST_REMOVE(ifma, ifma_link); - /* - * Make sure the interface driver is notified - * in the case of a link layer mcast group being left. - */ - if (ifp && ifma->ifma_addr->sa_family == AF_LINK && sa == 0) - dlil_ioctl(0, ifp, SIOCDELMULTI, 0); - FREE(ifma->ifma_addr, M_IFMADDR); - FREE(ifma, M_IFMADDR); - if (sa == 0) - return 0; - - /* - * 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.) - */ - if (ifp) - ifma = ifp->if_multiaddrs.lh_first; - else - ifma = ifma_lostlist.lh_first; - for (; ifma; ifma = ifma->ifma_link.le_next) - if (equal(sa, ifma->ifma_addr)) - break; + if (!locked && ifp) { + /* This wasn't initially locked, we should unlock it */ + ifnet_lock_done(ifp); + } - FREE(sa, M_IFMADDR); - if (ifma == 0) { - return 0; + if (do_del_multi) { + if (locked) + ifnet_lock_done(ifp); + ifnet_ioctl(ifp, 0, SIOCDELMULTI, NULL); + if (locked) + ifnet_lock_exclusive(ifp); } - - return if_delmultiaddr(ifma); + + return 0; } /* @@ -1659,20 +1969,40 @@ if_delmultiaddr(struct ifmultiaddr *ifma) * if the request does not match an existing membership. */ int -if_delmulti(ifp, sa) - struct ifnet *ifp; - struct sockaddr *sa; +if_delmulti( + struct ifnet *ifp, + const struct sockaddr *sa) { - struct ifmultiaddr *ifma; - + struct ifmultiaddr *ifma; + struct sockaddr *dupsa = NULL; + int retval = 0; + + if (sa->sa_family == AF_LINK || sa->sa_family == AF_UNSPEC) { + dupsa = copy_and_normalize(sa); + if (dupsa == NULL) { + return ENOMEM; + } + sa = dupsa; + } + + ifnet_lock_exclusive(ifp); for (ifma = ifp->if_multiaddrs.lh_first; ifma; ifma = ifma->ifma_link.le_next) if (equal(sa, ifma->ifma_addr)) break; - if (ifma == 0) + if (ifma == 0) { + ifnet_lock_done(ifp); + if (dupsa) + FREE(dupsa, M_IFADDR); return ENOENT; + } + + retval = if_delmultiaddr(ifma, 1); + ifnet_lock_done(ifp); + if (dupsa) + FREE(dupsa, M_IFADDR); - return if_delmultiaddr(ifma); + return retval; } @@ -1689,39 +2019,44 @@ if_setlladdr(struct ifnet *ifp, const u_char *lladdr, int len) #endif struct ifmultiaddr * -ifmaof_ifpforaddr(sa, ifp) - struct sockaddr *sa; - struct ifnet *ifp; +ifmaof_ifpforaddr(const struct sockaddr *sa, struct ifnet *ifp) { struct ifmultiaddr *ifma; + ifnet_lock_shared(ifp); for (ifma = ifp->if_multiaddrs.lh_first; ifma; ifma = ifma->ifma_link.le_next) if (equal(ifma->ifma_addr, sa)) break; + ifnet_lock_done(ifp); 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; } /* @@ -1740,9 +2075,9 @@ 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; @@ -1757,7 +2092,7 @@ if_rtdel(rn, arg) if ((rt->rt_flags & RTF_UP) == 0) return (0); - err = rtrequest(RTM_DELETE, rt_key(rt), rt->rt_gateway, + err = rtrequest_locked(RTM_DELETE, rt_key(rt), rt->rt_gateway, rt_mask(rt), rt->rt_flags, (struct rtentry **) NULL); if (err) { @@ -1772,12 +2107,163 @@ if_rtdel(rn, arg) * Removes routing table reference to a given interfacei * for a given protocol family */ - void if_rtproto_del(struct ifnet *ifp, int protocol) { + struct radix_node_head *rnh; + if (use_routegenid) + route_generation++; + if ((protocol <= AF_MAX) && (protocol >= 0) && + ((rnh = rt_tables[protocol]) != NULL) && (ifp != NULL)) { + lck_mtx_lock(rt_mtx); + (void) rnh->rnh_walktree(rnh, if_rtdel, ifp); + lck_mtx_unlock(rt_mtx); + } +} + +static int +if_rtmtu(struct radix_node *rn, void *arg) +{ + struct rtentry *rt = (struct rtentry *)rn; + struct ifnet *ifp = arg; + + 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; + } + + 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(rt_mtx); + (void) rnh->rnh_walktree(rnh, if_rtmtu, ifp); + lck_mtx_unlock(rt_mtx); + } + + if (use_routegenid) + route_generation++; +} + +__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) +{ + struct dlil_threading_info *thread; + if ((thread = ifp->if_input_thread) == NULL || (dlil_multithreaded_input == 0)) + thread = dlil_lo_thread_ptr; + +#define COPYFIELD(fld) if_data->fld = if_data_int->fld +#define COPYFIELD32(fld) if_data->fld = (u_int32_t)(if_data_int->fld) + 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); + } - struct radix_node_head *rnh; + lck_mtx_lock(thread->input_lck); + COPYFIELD32(ifi_ipackets); + COPYFIELD32(ifi_ierrors); + COPYFIELD32(ifi_opackets); + COPYFIELD32(ifi_oerrors); + COPYFIELD32(ifi_collisions); + COPYFIELD32(ifi_ibytes); + COPYFIELD32(ifi_obytes); + COPYFIELD32(ifi_imcasts); + COPYFIELD32(ifi_omcasts); + COPYFIELD32(ifi_iqdrops); + COPYFIELD32(ifi_noproto); + COPYFIELD32(ifi_recvtiming); + COPYFIELD32(ifi_xmittiming); + COPYFIELD(ifi_lastchange); + lck_mtx_unlock(thread->input_lck); + +#if IF_LASTCHANGEUPTIME + if_data->ifi_lastchange.tv_sec += boottime_sec(); +#endif - if ((protocol <= AF_MAX) && ((rnh = rt_tables[protocol]) != NULL) && (ifp != NULL)) - (void) rnh->rnh_walktree(rnh, if_rtdel, ifp); + if_data->ifi_unused2 = 0; + COPYFIELD(ifi_hwassist); + if_data->ifi_reserved1 = 0; + if_data->ifi_reserved2 = 0; +#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) +{ + struct dlil_threading_info *thread; + if ((thread = ifp->if_input_thread) == NULL || (dlil_multithreaded_input == 0)) + thread = dlil_lo_thread_ptr; + +#define COPYFIELD(fld) if_data64->fld = if_data_int->fld + COPYFIELD(ifi_type); + COPYFIELD(ifi_typelen); + COPYFIELD(ifi_physical); + COPYFIELD(ifi_addrlen); + COPYFIELD(ifi_hdrlen); + COPYFIELD(ifi_recvquota); + COPYFIELD(ifi_xmitquota); + if_data64->ifi_unused1 = 0; + COPYFIELD(ifi_mtu); + COPYFIELD(ifi_metric); + COPYFIELD(ifi_baudrate); + + lck_mtx_lock(thread->input_lck); + COPYFIELD(ifi_ipackets); + COPYFIELD(ifi_ierrors); + COPYFIELD(ifi_opackets); + COPYFIELD(ifi_oerrors); + COPYFIELD(ifi_collisions); + COPYFIELD(ifi_ibytes); + COPYFIELD(ifi_obytes); + COPYFIELD(ifi_imcasts); + COPYFIELD(ifi_omcasts); + COPYFIELD(ifi_iqdrops); + COPYFIELD(ifi_noproto); + COPYFIELD(ifi_recvtiming); + COPYFIELD(ifi_xmittiming); + COPYFIELD(ifi_lastchange); + lck_mtx_unlock(thread->input_lck); + +#if IF_LASTCHANGEUPTIME + if_data64->ifi_lastchange.tv_sec += boottime_sec(); +#endif + +#undef COPYFIELD }