X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/6601e61aa18bf4f09af135ff61fc7f4771d23b06..bd504ef0e0b883cdd7917b73b3574eb9ce669905:/bsd/netinet/in.c diff --git a/bsd/netinet/in.c b/bsd/netinet/in.c index 6fa1e7ab8..1df980df6 100644 --- a/bsd/netinet/in.c +++ b/bsd/netinet/in.c @@ -1,23 +1,29 @@ /* - * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2000-2011 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) 1982, 1986, 1991, 1993 @@ -66,10 +72,15 @@ #include #include #include +#include +#include + +#include #include #include #include +#include #include #include @@ -86,27 +97,125 @@ #include +#if PF +#include +#endif /* PF */ static int in_mask2len(struct in_addr *); static void in_len2mask(struct in_addr *, int); -static int in_lifaddr_ioctl(struct socket *, u_long, caddr_t, - struct ifnet *, struct proc *); +static int in_lifaddr_ioctl(struct socket *, u_long, struct if_laddrreq *, + struct ifnet *, struct proc *); +static int in_setrouter(struct ifnet *, int); static void in_socktrim(struct sockaddr_in *); static int in_ifinit(struct ifnet *, struct in_ifaddr *, struct sockaddr_in *, int); -static int subnetsarelocal = 0; -SYSCTL_INT(_net_inet_ip, OID_AUTO, subnets_are_local, CTLFLAG_RW, - &subnetsarelocal, 0, ""); +#define IA_HASH_INIT(ia) { \ + (ia)->ia_hash.tqe_next = (void *)(uintptr_t)-1; \ + (ia)->ia_hash.tqe_prev = (void *)(uintptr_t)-1; \ +} -struct in_multihead in_multihead; /* XXX BSS initialization */ +#define IA_IS_HASHED(ia) \ + (!((ia)->ia_hash.tqe_next == (void *)(uintptr_t)-1 || \ + (ia)->ia_hash.tqe_prev == (void *)(uintptr_t)-1)) -extern lck_mtx_t *rt_mtx; +static void in_iahash_remove(struct in_ifaddr *); +static void in_iahash_insert(struct in_ifaddr *); +static void in_iahash_insert_ptp(struct in_ifaddr *); +static struct in_ifaddr *in_ifaddr_alloc(int); +static void in_ifaddr_attached(struct ifaddr *); +static void in_ifaddr_detached(struct ifaddr *); +static void in_ifaddr_free(struct ifaddr *); +static void in_ifaddr_trace(struct ifaddr *, int); + +static int subnetsarelocal = 0; +SYSCTL_INT(_net_inet_ip, OID_AUTO, subnets_are_local, CTLFLAG_RW | CTLFLAG_LOCKED, + &subnetsarelocal, 0, ""); /* Track whether or not the SIOCARPIPLL ioctl has been called */ __private_extern__ u_int32_t ipv4_ll_arp_aware = 0; +#define INIFA_TRACE_HIST_SIZE 32 /* size of trace history */ + +/* For gdb */ +__private_extern__ unsigned int inifa_trace_hist_size = INIFA_TRACE_HIST_SIZE; + +struct in_ifaddr_dbg { + struct in_ifaddr inifa; /* in_ifaddr */ + struct in_ifaddr inifa_old; /* saved in_ifaddr */ + u_int16_t inifa_refhold_cnt; /* # of IFA_ADDREF */ + u_int16_t inifa_refrele_cnt; /* # of IFA_REMREF */ + /* + * Alloc and free callers. + */ + ctrace_t inifa_alloc; + ctrace_t inifa_free; + /* + * Circular lists of IFA_ADDREF and IFA_REMREF callers. + */ + ctrace_t inifa_refhold[INIFA_TRACE_HIST_SIZE]; + ctrace_t inifa_refrele[INIFA_TRACE_HIST_SIZE]; + /* + * Trash list linkage + */ + TAILQ_ENTRY(in_ifaddr_dbg) inifa_trash_link; +}; + +/* List of trash in_ifaddr entries protected by inifa_trash_lock */ +static TAILQ_HEAD(, in_ifaddr_dbg) inifa_trash_head; +static decl_lck_mtx_data(, inifa_trash_lock); + +#if DEBUG +static unsigned int inifa_debug = 1; /* debugging (enabled) */ +#else +static unsigned int inifa_debug; /* debugging (disabled) */ +#endif /* !DEBUG */ +static unsigned int inifa_size; /* size of zone element */ +static struct zone *inifa_zone; /* zone for in_ifaddr */ + +#define INIFA_ZONE_MAX 64 /* maximum elements in zone */ +#define INIFA_ZONE_NAME "in_ifaddr" /* zone name */ + +/* + * Return 1 if the address is + * - loopback + * - unicast or multicast link local + * - routed via a link level gateway + * - belongs to a directly connected (sub)net + */ +int +inaddr_local(struct in_addr in) +{ + struct rtentry *rt; + struct sockaddr_in sin; + int local = 0; + + if (ntohl(in.s_addr) == INADDR_LOOPBACK || IN_LINKLOCAL(ntohl(in.s_addr))) { + local = 1; + } else if (ntohl(in.s_addr) >= INADDR_UNSPEC_GROUP && + ntohl(in.s_addr) <= INADDR_MAX_LOCAL_GROUP) { + local = 1; + } else { + sin.sin_family = AF_INET; + sin.sin_len = sizeof (sin); + sin.sin_addr = in; + rt = rtalloc1((struct sockaddr *)&sin, 0, 0); + + if (rt != NULL) { + RT_LOCK_SPIN(rt); + if (rt->rt_gateway->sa_family == AF_LINK || + (rt->rt_ifp->if_flags & IFF_LOOPBACK)) + local = 1; + RT_UNLOCK(rt); + rtfree(rt); + } else { + local = in_localaddr(in); + } + } + return (local); +} + /* * Return 1 if an internet address is for a ``local'' host * (one to which we have a connection). If subnetsarelocal @@ -114,30 +223,37 @@ __private_extern__ u_int32_t ipv4_ll_arp_aware = 0; * Otherwise, it includes only the directly-connected (sub)nets. */ int -in_localaddr(in) - struct in_addr in; +in_localaddr(struct in_addr in) { - u_long i = ntohl(in.s_addr); + u_int32_t i = ntohl(in.s_addr); struct in_ifaddr *ia; if (subnetsarelocal) { - lck_mtx_lock(rt_mtx); + lck_rw_lock_shared(in_ifaddr_rwlock); for (ia = in_ifaddrhead.tqh_first; ia; - ia = ia->ia_link.tqe_next) + ia = ia->ia_link.tqe_next) { + IFA_LOCK(&ia->ia_ifa); if ((i & ia->ia_netmask) == ia->ia_net) { - lck_mtx_unlock(rt_mtx); + IFA_UNLOCK(&ia->ia_ifa); + lck_rw_done(in_ifaddr_rwlock); return (1); } - lck_mtx_unlock(rt_mtx); + IFA_UNLOCK(&ia->ia_ifa); + } + lck_rw_done(in_ifaddr_rwlock); } else { - lck_mtx_lock(rt_mtx); + lck_rw_lock_shared(in_ifaddr_rwlock); for (ia = in_ifaddrhead.tqh_first; ia; - ia = ia->ia_link.tqe_next) + ia = ia->ia_link.tqe_next) { + IFA_LOCK(&ia->ia_ifa); if ((i & ia->ia_subnetmask) == ia->ia_subnet) { - lck_mtx_unlock(rt_mtx); + IFA_UNLOCK(&ia->ia_ifa); + lck_rw_done(in_ifaddr_rwlock); return (1); } - lck_mtx_unlock(rt_mtx); + IFA_UNLOCK(&ia->ia_ifa); + } + lck_rw_done(in_ifaddr_rwlock); } return (0); } @@ -148,11 +264,10 @@ in_localaddr(in) * may be forwarded. */ int -in_canforward(in) - struct in_addr in; +in_canforward(struct in_addr in) { - u_long i = ntohl(in.s_addr); - u_long net; + u_int32_t i = ntohl(in.s_addr); + u_int32_t net; if (IN_EXPERIMENTAL(i) || IN_MULTICAST(i)) return (0); @@ -168,8 +283,7 @@ in_canforward(in) * Trim a mask in a sockaddr */ static void -in_socktrim(ap) -struct sockaddr_in *ap; +in_socktrim(struct sockaddr_in *ap) { char *cplim = (char *) &ap->sin_addr; char *cp = (char *) (&ap->sin_addr + 1); @@ -183,10 +297,9 @@ struct sockaddr_in *ap; } static int -in_mask2len(mask) - struct in_addr *mask; +in_mask2len(struct in_addr *mask) { - int x, y; + size_t x, y; u_char *p; p = (u_char *)mask; @@ -205,9 +318,7 @@ in_mask2len(mask) } static void -in_len2mask(mask, len) - struct in_addr *mask; - int len; +in_len2mask(struct in_addr *mask, int len) { int i; u_char *p; @@ -222,41 +333,71 @@ in_len2mask(mask, len) static int in_interfaces; /* number of external internet interfaces */ +static int +in_domifattach(struct ifnet *ifp) +{ + int error; + + if ((error = proto_plumb(PF_INET, ifp)) && error != EEXIST) + log(LOG_ERR, "%s: proto_plumb returned %d if=%s%d\n", + __func__, error, ifp->if_name, ifp->if_unit); + + return (error); +} + /* * Generic internet control operations (ioctl's). * Ifp is 0 if not an interface-specific ioctl. + * + * Returns: 0 Success + * EINVAL + * EADDRNOTAVAIL + * EDESTADDRREQ + * EPERM + * ENOBUFS + * EBUSY + * EOPNOTSUPP + * proc_suser:EPERM + * suser:EPERM + * in_lifaddr_ioctl:??? + * dlil_ioctl:??? + * in_ifinit:??? + * dlil_plumb_protocol:??? + * dlil_unplumb_protocol:??? */ /* ARGSUSED */ int -in_control( - struct socket *so, - u_long cmd, - caddr_t data, - struct ifnet *ifp, - struct proc *p) +in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, + struct proc *p) { - struct ifreq *ifr = (struct ifreq *)data; - struct in_ifaddr *ia = 0, *iap; + struct in_ifaddr *ia = NULL; struct ifaddr *ifa; - struct in_ifaddr *oia; - struct in_aliasreq *ifra = (struct in_aliasreq *)data; struct sockaddr_in oldaddr; - int error, hostIsNew, maskIsNew; - u_long i; - struct kev_msg ev_msg; - struct kev_in_data in_event_data; + int error = 0; + int hostIsNew, maskIsNew; + struct kev_msg ev_msg; + struct kev_in_data in_event_data; + bzero(&in_event_data, sizeof (struct kev_in_data)); + bzero(&ev_msg, sizeof (struct kev_msg)); switch (cmd) { - case SIOCALIFADDR: - case SIOCDLIFADDR: - if (p && (error = proc_suser(p)) != 0) - return error; - /*fall through*/ - case SIOCGLIFADDR: - if (!ifp) - return EINVAL; - return in_lifaddr_ioctl(so, cmd, data, ifp, p); + case SIOCALIFADDR: /* struct if_laddrreq */ + case SIOCDLIFADDR: /* struct if_laddrreq */ + if ((error = proc_suser(p)) != 0) + return (error); + /* FALLTHRU */ + case SIOCGLIFADDR: { /* struct if_laddrreq */ + struct if_laddrreq iflr; + + if (ifp == NULL) + return (EINVAL); + + bcopy(data, &iflr, sizeof (iflr)); + error = in_lifaddr_ioctl(so, cmd, &iflr, ifp, p); + bcopy(&iflr, data, sizeof (iflr)); + return (error); + } } /* @@ -265,203 +406,361 @@ in_control( * If an alias address was specified, find that one instead of * the first one on the interface. */ - if (ifp) { - lck_mtx_lock(rt_mtx); - for (iap = in_ifaddrhead.tqh_first; iap; - iap = iap->ia_link.tqe_next) - if (iap->ia_ifp == ifp) { - if (((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr.s_addr == - iap->ia_addr.sin_addr.s_addr) { - ia = iap; + if (ifp != NULL) { + struct in_ifaddr *iap; + struct sockaddr_in sin; + + bcopy(&((struct ifreq *)(void *)data)->ifr_addr, + &sin, sizeof (sin)); + + lck_rw_lock_shared(in_ifaddr_rwlock); + for (iap = in_ifaddrhead.tqh_first; iap != NULL; + iap = iap->ia_link.tqe_next) { + if (iap->ia_ifp != ifp) + continue; + + IFA_LOCK(&iap->ia_ifa); + if (sin.sin_addr.s_addr == + iap->ia_addr.sin_addr.s_addr) { + ia = iap; + IFA_UNLOCK(&iap->ia_ifa); + break; + } else if (ia == NULL) { + ia = iap; + if (sin.sin_family != AF_INET) { + IFA_UNLOCK(&iap->ia_ifa); break; - } else if (ia == NULL) { - ia = iap; - if (ifr->ifr_addr.sa_family != AF_INET) - break; } } - lck_mtx_unlock(rt_mtx); + IFA_UNLOCK(&iap->ia_ifa); + } + /* take a reference on ia before releasing lock */ + if (ia != NULL) + IFA_ADDREF(&ia->ia_ifa); + lck_rw_done(in_ifaddr_rwlock); } + switch (cmd) { - case SIOCAUTOADDR: - case SIOCARPIPLL: - if (p && (error = proc_suser(p)) != 0) - return error; + case SIOCAUTOADDR: /* struct ifreq */ + case SIOCARPIPLL: /* struct ifreq */ + case SIOCSETROUTERMODE: /* struct ifreq */ + if ((error = proc_suser(p)) != 0) { + goto done; + } + if (ifp == NULL) { + error = EADDRNOTAVAIL; + goto done; + } break; - case SIOCAIFADDR: - case SIOCDIFADDR: - if (ifp == 0) - return (EADDRNOTAVAIL); - if (ifra->ifra_addr.sin_family == AF_INET) { - lck_mtx_lock(rt_mtx); + case SIOCAIFADDR: /* struct ifaliasreq */ + case SIOCDIFADDR: { /* struct ifreq */ + struct sockaddr_in addr, dstaddr; + + if (ifp == NULL) { + error = EADDRNOTAVAIL; + goto done; + } + + if (cmd == SIOCAIFADDR) { + bcopy(&((struct in_aliasreq *)(void *)data)-> + ifra_addr, &addr, sizeof (addr)); + bcopy(&((struct in_aliasreq *)(void *)data)-> + ifra_dstaddr, &dstaddr, sizeof (dstaddr)); + } else { + VERIFY(cmd == SIOCDIFADDR); + bcopy(&((struct ifreq *)(void *)data)->ifr_addr, + &addr, sizeof (addr)); + bzero(&dstaddr, sizeof (dstaddr)); + } + + if (addr.sin_family == AF_INET) { + struct in_ifaddr *oia; + + lck_rw_lock_shared(in_ifaddr_rwlock); for (oia = ia; ia; ia = ia->ia_link.tqe_next) { + IFA_LOCK(&ia->ia_ifa); if (ia->ia_ifp == ifp && ia->ia_addr.sin_addr.s_addr == - ifra->ifra_addr.sin_addr.s_addr) + addr.sin_addr.s_addr) { + IFA_ADDREF_LOCKED(&ia->ia_ifa); + IFA_UNLOCK(&ia->ia_ifa); break; + } + IFA_UNLOCK(&ia->ia_ifa); } - lck_mtx_unlock(rt_mtx); - if ((ifp->if_flags & IFF_POINTOPOINT) - && (cmd == SIOCAIFADDR) - && (ifra->ifra_dstaddr.sin_addr.s_addr - == INADDR_ANY)) { - return EDESTADDRREQ; + lck_rw_done(in_ifaddr_rwlock); + if (oia != NULL) + IFA_REMREF(&oia->ia_ifa); + if ((ifp->if_flags & IFF_POINTOPOINT) && + (cmd == SIOCAIFADDR) && + (dstaddr.sin_addr.s_addr == INADDR_ANY)) { + error = EDESTADDRREQ; + goto done; } + } else if (cmd == SIOCAIFADDR) { + error = EINVAL; + goto done; + } + if (cmd == SIOCDIFADDR && ia == NULL) { + error = EADDRNOTAVAIL; + goto done; } - else if (cmd == SIOCAIFADDR) - return (EINVAL); - if (cmd == SIOCDIFADDR && ia == 0) - return (EADDRNOTAVAIL); /* FALLTHROUGH */ - case SIOCSIFADDR: - case SIOCSIFNETMASK: - case SIOCSIFDSTADDR: - if ((so->so_state & SS_PRIV) == 0) - return (EPERM); - - if (ifp == 0) - return (EADDRNOTAVAIL); - if (ifra->ifra_addr.sin_family != AF_INET && cmd == SIOCSIFADDR) - return (EINVAL); - if (ia == (struct in_ifaddr *)0) { - ia = (struct in_ifaddr *) - _MALLOC(sizeof *ia, M_IFADDR, M_WAITOK); - if (ia == (struct in_ifaddr *)NULL) - return (ENOBUFS); - bzero((caddr_t)ia, sizeof *ia); - /* - * Protect from ipintr() traversing address list - * while we're modifying it. - */ - - ifa = &ia->ia_ifa; + } + case SIOCSIFADDR: /* struct ifreq */ + case SIOCSIFNETMASK: /* struct ifreq */ + case SIOCSIFDSTADDR: { /* struct ifreq */ + struct sockaddr_in addr; + + if (cmd == SIOCAIFADDR) { + /* fell thru from above; just repeat it */ + bcopy(&((struct in_aliasreq *)(void *)data)-> + ifra_addr, &addr, sizeof (addr)); + } else { + VERIFY(cmd == SIOCDIFADDR || cmd == SIOCSIFADDR || + cmd == SIOCSIFNETMASK || cmd == SIOCSIFDSTADDR); + bcopy(&((struct ifreq *)(void *)data)->ifr_addr, + &addr, sizeof (addr)); + } + /* socket is NULL if called from in_purgeaddrs() */ + if (so != NULL && (so->so_state & SS_PRIV) == 0) { + error = EPERM; + goto done; + } + /* in case it's NULL, make sure it came from the kernel */ + if (so == NULL && p != kernproc) { + error = EPERM; + goto done; + } + if (ifp == NULL) { + error = EADDRNOTAVAIL; + goto done; + } + if (addr.sin_family != AF_INET && cmd == SIOCSIFADDR) { + error = EINVAL; + goto done; + } + if (ia == NULL) { + ia = in_ifaddr_alloc(M_WAITOK); + if (ia == NULL) { + error = ENOBUFS; + goto done; + } + ifnet_lock_exclusive(ifp); + ifa = &ia->ia_ifa; + IFA_LOCK(ifa); + /* Hold a reference for this routine */ + IFA_ADDREF_LOCKED(ifa); + IA_HASH_INIT(ia); ifa->ifa_addr = (struct sockaddr *)&ia->ia_addr; ifa->ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr; ifa->ifa_netmask = (struct sockaddr *)&ia->ia_sockmask; ia->ia_sockmask.sin_len = 8; - ifnet_lock_exclusive(ifp); if (ifp->if_flags & IFF_BROADCAST) { - ia->ia_broadaddr.sin_len = sizeof(ia->ia_addr); + ia->ia_broadaddr.sin_len = sizeof (ia->ia_addr); ia->ia_broadaddr.sin_family = AF_INET; } ia->ia_ifp = ifp; if (!(ifp->if_flags & IFF_LOOPBACK)) in_interfaces++; + /* if_attach_ifa() holds a reference for ifa_link */ if_attach_ifa(ifp, ifa); + /* + * If we have to go through in_ifinit(), make sure + * to avoid installing route(s) based on this address + * via PFC_IFUP event, before the link resolver (ARP) + * initializes it. + */ + if (cmd == SIOCAIFADDR || cmd == SIOCSIFADDR) + ifa->ifa_debug |= IFD_NOTREADY; + IFA_UNLOCK(ifa); ifnet_lock_done(ifp); - - lck_mtx_lock(rt_mtx); + lck_rw_lock_exclusive(in_ifaddr_rwlock); + /* Hold a reference for ia_link */ + IFA_ADDREF(ifa); TAILQ_INSERT_TAIL(&in_ifaddrhead, ia, ia_link); - lck_mtx_unlock(rt_mtx); - - /* Generic protocol plumbing */ - - if (error = dlil_plumb_protocol(PF_INET, ifp)) { - kprintf("in.c: warning can't plumb proto if=%s%n type %d error=%d\n", - ifp->if_name, ifp->if_unit, ifp->if_type, error); - error = 0; /*discard error, can be cold with unsupported interfaces */ - } - + lck_rw_done(in_ifaddr_rwlock); + /* discard error */ + (void) in_domifattach(ifp); + error = 0; } break; + } - case SIOCPROTOATTACH: - case SIOCPROTODETACH: - if (p && (error = proc_suser(p)) != 0) - return error; - if (ifp == 0) - return (EADDRNOTAVAIL); + case SIOCPROTOATTACH: /* struct ifreq */ + case SIOCPROTODETACH: /* struct ifreq */ + if ((error = proc_suser(p)) != 0) { + goto done; + } + if (ifp == NULL) { + error = EADDRNOTAVAIL; + goto done; + } break; - - case SIOCSIFBRDADDR: -#ifdef __APPLE__ - if ((so->so_state & SS_PRIV) == 0) - return (EPERM); -#else - if (p && (error = suser(p)) != 0) - return error; -#endif - /* FALLTHROUGH */ - case SIOCGIFADDR: - case SIOCGIFNETMASK: - case SIOCGIFDSTADDR: - case SIOCGIFBRDADDR: - if (ia == (struct in_ifaddr *)0) - return (EADDRNOTAVAIL); + case SIOCSIFBRDADDR: /* struct ifreq */ + if ((so->so_state & SS_PRIV) == 0) { + error = EPERM; + goto done; + } + /* FALLTHROUGH */ + case SIOCGIFADDR: /* struct ifreq */ + case SIOCGIFNETMASK: /* struct ifreq */ + case SIOCGIFDSTADDR: /* struct ifreq */ + case SIOCGIFBRDADDR: /* struct ifreq */ + if (ia == NULL) { + error = EADDRNOTAVAIL; + goto done; + } break; } + switch (cmd) { - case SIOCAUTOADDR: - if (ifp == 0) - return (EADDRNOTAVAIL); + case SIOCAUTOADDR: { /* struct ifreq */ + int intval; + + VERIFY(ifp != NULL); + bcopy(&((struct ifreq *)(void *)data)->ifr_intval, + &intval, sizeof (intval)); + ifnet_lock_exclusive(ifp); - if (ifr->ifr_intval) - ifp->if_eflags |= IFEF_AUTOCONFIGURING; - else + if (intval) { + /* + * An interface in IPv4 router mode implies that it + * is configured with a static IP address and should + * not act as a DHCP client; prevent SIOCAUTOADDR from + * being set in that mode. + */ + if (ifp->if_eflags & IFEF_IPV4_ROUTER) { + intval = 0; /* be safe; clear flag if set */ + error = EBUSY; + } else { + ifp->if_eflags |= IFEF_AUTOCONFIGURING; + } + } + if (!intval) ifp->if_eflags &= ~IFEF_AUTOCONFIGURING; ifnet_lock_done(ifp); break; - - case SIOCARPIPLL: - if (ifp == 0) - return (EADDRNOTAVAIL); + } + + case SIOCARPIPLL: { /* struct ifreq */ + int intval; + + VERIFY(ifp != NULL); + bcopy(&((struct ifreq *)(void *)data)->ifr_intval, + &intval, sizeof (intval)); ipv4_ll_arp_aware = 1; + ifnet_lock_exclusive(ifp); - if (ifr->ifr_data) - ifp->if_eflags |= IFEF_ARPLL; - else + if (intval) { + /* + * An interface in IPv4 router mode implies that it + * is configured with a static IP address and should + * not have to deal with IPv4 Link-Local Address; + * prevent SIOCARPIPLL from being set in that mode. + */ + if (ifp->if_eflags & IFEF_IPV4_ROUTER) { + intval = 0; /* be safe; clear flag if set */ + error = EBUSY; + } else { + ifp->if_eflags |= IFEF_ARPLL; + } + } + if (!intval) ifp->if_eflags &= ~IFEF_ARPLL; ifnet_lock_done(ifp); break; + } - case SIOCGIFADDR: - *((struct sockaddr_in *)&ifr->ifr_addr) = ia->ia_addr; + case SIOCGIFADDR: /* struct ifreq */ + VERIFY(ia != NULL); + IFA_LOCK(&ia->ia_ifa); + bcopy(&ia->ia_addr, &((struct ifreq *)(void *)data)->ifr_addr, + sizeof (struct sockaddr_in)); + IFA_UNLOCK(&ia->ia_ifa); break; - case SIOCGIFBRDADDR: - if ((ifp->if_flags & IFF_BROADCAST) == 0) - return (EINVAL); - *((struct sockaddr_in *)&ifr->ifr_dstaddr) = ia->ia_broadaddr; + case SIOCGIFBRDADDR: /* struct ifreq */ + VERIFY(ia != NULL); + if ((ifp->if_flags & IFF_BROADCAST) == 0) { + error = EINVAL; + break; + } + IFA_LOCK(&ia->ia_ifa); + bcopy(&ia->ia_broadaddr, + &((struct ifreq *)(void *)data)->ifr_broadaddr, + sizeof (struct sockaddr_in)); + IFA_UNLOCK(&ia->ia_ifa); break; - case SIOCGIFDSTADDR: - if ((ifp->if_flags & IFF_POINTOPOINT) == 0) - return (EINVAL); - *((struct sockaddr_in *)&ifr->ifr_dstaddr) = ia->ia_dstaddr; + case SIOCGIFDSTADDR: /* struct ifreq */ + VERIFY(ia != NULL); + if ((ifp->if_flags & IFF_POINTOPOINT) == 0) { + error = EINVAL; + break; + } + IFA_LOCK(&ia->ia_ifa); + bcopy(&ia->ia_dstaddr, + &((struct ifreq *)(void *)data)->ifr_dstaddr, + sizeof (struct sockaddr_in)); + IFA_UNLOCK(&ia->ia_ifa); break; - case SIOCGIFNETMASK: - *((struct sockaddr_in *)&ifr->ifr_addr) = ia->ia_sockmask; + case SIOCGIFNETMASK: /* struct ifreq */ + VERIFY(ia != NULL); + IFA_LOCK(&ia->ia_ifa); + bcopy(&ia->ia_sockmask, + &((struct ifreq *)(void *)data)->ifr_addr, + sizeof (struct sockaddr_in)); + IFA_UNLOCK(&ia->ia_ifa); break; - case SIOCSIFDSTADDR: - if ((ifp->if_flags & IFF_POINTOPOINT) == 0) - return (EINVAL); + case SIOCSIFDSTADDR: /* struct ifreq */ + VERIFY(ifp != NULL && ia != NULL); + if ((ifp->if_flags & IFF_POINTOPOINT) == 0) { + error = EINVAL; + break; + } + IFA_LOCK(&ia->ia_ifa); oldaddr = ia->ia_dstaddr; - ia->ia_dstaddr = *(struct sockaddr_in *)&ifr->ifr_dstaddr; - error = dlil_ioctl(PF_INET, ifp, SIOCSIFDSTADDR, (caddr_t)ia); - if (error == EOPNOTSUPP) - error = 0; - + bcopy(&((struct ifreq *)(void *)data)->ifr_dstaddr, + &ia->ia_dstaddr, sizeof (struct sockaddr_in)); + if (ia->ia_dstaddr.sin_family == AF_INET) + ia->ia_dstaddr.sin_len = sizeof (struct sockaddr_in); + IFA_UNLOCK(&ia->ia_ifa); + /* + * NOTE: SIOCSIFDSTADDR is defined with struct ifreq + * as parameter, but here we are sending it down + * to the interface with a pointer to struct ifaddr, + * for legacy reasons. + */ + error = ifnet_ioctl(ifp, PF_INET, SIOCSIFDSTADDR, ia); + IFA_LOCK(&ia->ia_ifa); + if (error == EOPNOTSUPP) { + error = 0; + } if (error) { - ia->ia_dstaddr = oldaddr; - return error; + ia->ia_dstaddr = oldaddr; + IFA_UNLOCK(&ia->ia_ifa); + break; } + IFA_LOCK_ASSERT_HELD(&ia->ia_ifa); ev_msg.vendor_code = KEV_VENDOR_APPLE; ev_msg.kev_class = KEV_NETWORK_CLASS; ev_msg.kev_subclass = KEV_INET_SUBCLASS; - + ev_msg.event_code = KEV_INET_SIFDSTADDR; - if (ia->ia_ifa.ifa_dstaddr) - in_event_data.ia_dstaddr = - ((struct sockaddr_in *)ia->ia_ifa.ifa_dstaddr)->sin_addr; - else - in_event_data.ia_dstaddr.s_addr = 0; + if (ia->ia_ifa.ifa_dstaddr) { + in_event_data.ia_dstaddr = ((struct sockaddr_in *) + (void *)ia->ia_ifa.ifa_dstaddr)->sin_addr; + } else { + in_event_data.ia_dstaddr.s_addr = INADDR_ANY; + } in_event_data.ia_addr = ia->ia_addr.sin_addr; in_event_data.ia_net = ia->ia_net; @@ -469,359 +768,461 @@ in_control( in_event_data.ia_subnet = ia->ia_subnet; in_event_data.ia_subnetmask = ia->ia_subnetmask; in_event_data.ia_netbroadcast = ia->ia_netbroadcast; - strncpy(&in_event_data.link_data.if_name[0], ifp->if_name, IFNAMSIZ); + IFA_UNLOCK(&ia->ia_ifa); + (void) strncpy(&in_event_data.link_data.if_name[0], + ifp->if_name, IFNAMSIZ); in_event_data.link_data.if_family = ifp->if_family; - in_event_data.link_data.if_unit = (unsigned long) ifp->if_unit; + in_event_data.link_data.if_unit = (u_int32_t) ifp->if_unit; ev_msg.dv[0].data_ptr = &in_event_data; - ev_msg.dv[0].data_length = sizeof(struct kev_in_data); + ev_msg.dv[0].data_length = sizeof (struct kev_in_data); ev_msg.dv[1].data_length = 0; kev_post_msg(&ev_msg); - + lck_mtx_lock(rnh_lock); + IFA_LOCK(&ia->ia_ifa); if (ia->ia_flags & IFA_ROUTE) { ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&oldaddr; - rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST); + IFA_UNLOCK(&ia->ia_ifa); + rtinit_locked(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST); + IFA_LOCK(&ia->ia_ifa); ia->ia_ifa.ifa_dstaddr = - (struct sockaddr *)&ia->ia_dstaddr; - rtinit(&(ia->ia_ifa), (int)RTM_ADD, RTF_HOST|RTF_UP); + (struct sockaddr *)&ia->ia_dstaddr; + IFA_UNLOCK(&ia->ia_ifa); + rtinit_locked(&(ia->ia_ifa), (int)RTM_ADD, + RTF_HOST|RTF_UP); + } else { + IFA_UNLOCK(&ia->ia_ifa); } + lck_mtx_unlock(rnh_lock); break; - case SIOCSIFBRDADDR: - if ((ifp->if_flags & IFF_BROADCAST) == 0) - return (EINVAL); - ia->ia_broadaddr = *(struct sockaddr_in *)&ifr->ifr_broadaddr; + case SIOCSIFBRDADDR: /* struct ifreq */ + VERIFY(ia != NULL); + if ((ifp->if_flags & IFF_BROADCAST) == 0) { + error = EINVAL; + break; + } + IFA_LOCK(&ia->ia_ifa); + bcopy(&((struct ifreq *)(void *)data)->ifr_broadaddr, + &ia->ia_broadaddr, sizeof (struct sockaddr_in)); ev_msg.vendor_code = KEV_VENDOR_APPLE; ev_msg.kev_class = KEV_NETWORK_CLASS; ev_msg.kev_subclass = KEV_INET_SUBCLASS; - - ev_msg.event_code = KEV_INET_SIFBRDADDR; - if (ia->ia_ifa.ifa_dstaddr) - in_event_data.ia_dstaddr = - ((struct sockaddr_in *)ia->ia_ifa.ifa_dstaddr)->sin_addr; - else - in_event_data.ia_dstaddr.s_addr = 0; + ev_msg.event_code = KEV_INET_SIFBRDADDR; + if (ia->ia_ifa.ifa_dstaddr) { + in_event_data.ia_dstaddr = ((struct sockaddr_in *) + (void *)ia->ia_ifa.ifa_dstaddr)->sin_addr; + } else { + in_event_data.ia_dstaddr.s_addr = INADDR_ANY; + } in_event_data.ia_addr = ia->ia_addr.sin_addr; in_event_data.ia_net = ia->ia_net; in_event_data.ia_netmask = ia->ia_netmask; in_event_data.ia_subnet = ia->ia_subnet; in_event_data.ia_subnetmask = ia->ia_subnetmask; in_event_data.ia_netbroadcast = ia->ia_netbroadcast; - strncpy(&in_event_data.link_data.if_name[0], ifp->if_name, IFNAMSIZ); + IFA_UNLOCK(&ia->ia_ifa); + (void) strncpy(&in_event_data.link_data.if_name[0], + ifp->if_name, IFNAMSIZ); in_event_data.link_data.if_family = ifp->if_family; - in_event_data.link_data.if_unit = (unsigned long) ifp->if_unit; + in_event_data.link_data.if_unit = (u_int32_t) ifp->if_unit; ev_msg.dv[0].data_ptr = &in_event_data; - ev_msg.dv[0].data_length = sizeof(struct kev_in_data); + ev_msg.dv[0].data_length = sizeof (struct kev_in_data); ev_msg.dv[1].data_length = 0; kev_post_msg(&ev_msg); + break; + + case SIOCSIFADDR: { /* struct ifreq */ + struct sockaddr_in addr; + + VERIFY(ifp != NULL && ia != NULL); + bcopy(&((struct ifreq *)(void *)data)->ifr_addr, + &addr, sizeof (addr)); + /* + * If this is a new address, the reference count for the + * hash table has been taken at creation time above. + */ + error = in_ifinit(ifp, ia, &addr, 1); +#if PF + if (!error) + (void) pf_ifaddr_hook(ifp, cmd); +#endif /* PF */ + break; + } + case SIOCPROTOATTACH: /* struct ifreq */ + VERIFY(ifp != NULL); + error = in_domifattach(ifp); break; - case SIOCSIFADDR: - return (in_ifinit(ifp, ia, - (struct sockaddr_in *) &ifr->ifr_addr, 1)); - - case SIOCPROTOATTACH: - error = dlil_plumb_protocol(PF_INET, ifp); - if (error) - return(error); - break; - - case SIOCPROTODETACH: - // if an ip address is still present, refuse to detach + case SIOCPROTODETACH: /* struct ifreq */ + VERIFY(ifp != NULL); + /* + * If an IPv4 address is still present, refuse to detach. + */ ifnet_lock_shared(ifp); - TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) - if (ifa->ifa_addr->sa_family == AF_INET) + TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { + IFA_LOCK(ifa); + if (ifa->ifa_addr->sa_family == AF_INET) { + IFA_UNLOCK(ifa); break; + } + IFA_UNLOCK(ifa); + } ifnet_lock_done(ifp); - if (ifa != 0) - return EBUSY; + if (ifa != NULL) { + error = EBUSY; + break; + } - error = dlil_unplumb_protocol(PF_INET, ifp); - if (error) - return(error); + error = proto_unplumb(PF_INET, ifp); break; - - case SIOCSIFNETMASK: - i = ifra->ifra_addr.sin_addr.s_addr; + case SIOCSETROUTERMODE: { /* struct ifreq */ + int intval; + + VERIFY(ifp != NULL); + bcopy(&((struct ifreq *)(void *)data)->ifr_intval, + &intval, sizeof (intval)); + + error = in_setrouter(ifp, intval); + break; + } + + case SIOCSIFNETMASK: { /* struct ifreq */ + struct sockaddr_in addr; + in_addr_t i; + + VERIFY(ifp != NULL && ia != NULL); + bcopy(&((struct ifreq *)(void *)data)->ifr_addr, + &addr, sizeof (addr)); + i = addr.sin_addr.s_addr; + + IFA_LOCK(&ia->ia_ifa); ia->ia_subnetmask = ntohl(ia->ia_sockmask.sin_addr.s_addr = i); ev_msg.vendor_code = KEV_VENDOR_APPLE; ev_msg.kev_class = KEV_NETWORK_CLASS; ev_msg.kev_subclass = KEV_INET_SUBCLASS; - - ev_msg.event_code = KEV_INET_SIFNETMASK; - if (ia->ia_ifa.ifa_dstaddr) - in_event_data.ia_dstaddr = - ((struct sockaddr_in *)ia->ia_ifa.ifa_dstaddr)->sin_addr; - else - in_event_data.ia_dstaddr.s_addr = 0; + ev_msg.event_code = KEV_INET_SIFNETMASK; + if (ia->ia_ifa.ifa_dstaddr) { + in_event_data.ia_dstaddr = ((struct sockaddr_in *) + (void *)ia->ia_ifa.ifa_dstaddr)->sin_addr; + } else { + in_event_data.ia_dstaddr.s_addr = INADDR_ANY; + } in_event_data.ia_addr = ia->ia_addr.sin_addr; in_event_data.ia_net = ia->ia_net; in_event_data.ia_netmask = ia->ia_netmask; in_event_data.ia_subnet = ia->ia_subnet; in_event_data.ia_subnetmask = ia->ia_subnetmask; in_event_data.ia_netbroadcast = ia->ia_netbroadcast; - strncpy(&in_event_data.link_data.if_name[0], ifp->if_name, IFNAMSIZ); + IFA_UNLOCK(&ia->ia_ifa); + (void) strncpy(&in_event_data.link_data.if_name[0], + ifp->if_name, IFNAMSIZ); in_event_data.link_data.if_family = ifp->if_family; - in_event_data.link_data.if_unit = (unsigned long) ifp->if_unit; + in_event_data.link_data.if_unit = (u_int32_t) ifp->if_unit; ev_msg.dv[0].data_ptr = &in_event_data; - ev_msg.dv[0].data_length = sizeof(struct kev_in_data); + ev_msg.dv[0].data_length = sizeof (struct kev_in_data); ev_msg.dv[1].data_length = 0; kev_post_msg(&ev_msg); - break; + } + + case SIOCAIFADDR: { /* struct ifaliasreq */ + struct sockaddr_in addr, broadaddr, mask; + + VERIFY(ifp != NULL && ia != NULL); + bcopy(&((struct ifaliasreq *)(void *)data)->ifra_addr, + &addr, sizeof (addr)); + bcopy(&((struct ifaliasreq *)(void *)data)->ifra_broadaddr, + &broadaddr, sizeof (broadaddr)); + bcopy(&((struct ifaliasreq *)(void *)data)->ifra_mask, + &mask, sizeof (mask)); - case SIOCAIFADDR: maskIsNew = 0; hostIsNew = 1; error = 0; + + IFA_LOCK(&ia->ia_ifa); if (ia->ia_addr.sin_family == AF_INET) { - if (ifra->ifra_addr.sin_len == 0) { - ifra->ifra_addr = ia->ia_addr; + if (addr.sin_len == 0) { + addr = ia->ia_addr; hostIsNew = 0; - } else if (ifra->ifra_addr.sin_addr.s_addr == - ia->ia_addr.sin_addr.s_addr) + } else if (addr.sin_addr.s_addr == + ia->ia_addr.sin_addr.s_addr) { hostIsNew = 0; + } } - if (ifra->ifra_mask.sin_len) { + if (mask.sin_len) { + IFA_UNLOCK(&ia->ia_ifa); in_ifscrub(ifp, ia, 0); - ia->ia_sockmask = ifra->ifra_mask; + IFA_LOCK(&ia->ia_ifa); + ia->ia_sockmask = mask; ia->ia_subnetmask = ntohl(ia->ia_sockmask.sin_addr.s_addr); maskIsNew = 1; } if ((ifp->if_flags & IFF_POINTOPOINT) && - (ifra->ifra_dstaddr.sin_family == AF_INET)) { + (broadaddr.sin_family == AF_INET)) { + IFA_UNLOCK(&ia->ia_ifa); in_ifscrub(ifp, ia, 0); - ia->ia_dstaddr = ifra->ifra_dstaddr; + IFA_LOCK(&ia->ia_ifa); + ia->ia_dstaddr = broadaddr; + ia->ia_dstaddr.sin_len = sizeof (struct sockaddr_in); maskIsNew = 1; /* We lie; but the effect's the same */ } - if (ifra->ifra_addr.sin_family == AF_INET && - (hostIsNew || maskIsNew)) { - error = in_ifinit(ifp, ia, &ifra->ifra_addr, 0); + if (addr.sin_family == AF_INET && (hostIsNew || maskIsNew)) { + IFA_UNLOCK(&ia->ia_ifa); + error = in_ifinit(ifp, ia, &addr, 0); + } else { + IFA_UNLOCK(&ia->ia_ifa); } +#if PF + if (!error) + (void) pf_ifaddr_hook(ifp, cmd); +#endif /* PF */ + IFA_LOCK(&ia->ia_ifa); if ((ifp->if_flags & IFF_BROADCAST) && - (ifra->ifra_broadaddr.sin_family == AF_INET)) - ia->ia_broadaddr = ifra->ifra_broadaddr; + (broadaddr.sin_family == AF_INET)) + ia->ia_broadaddr = broadaddr; /* * Report event. */ - if ((error == 0) || (error == EEXIST)) { - ev_msg.vendor_code = KEV_VENDOR_APPLE; - ev_msg.kev_class = KEV_NETWORK_CLASS; - ev_msg.kev_subclass = KEV_INET_SUBCLASS; - - if (hostIsNew) - ev_msg.event_code = KEV_INET_NEW_ADDR; - else - ev_msg.event_code = KEV_INET_CHANGED_ADDR; - - if (ia->ia_ifa.ifa_dstaddr) - in_event_data.ia_dstaddr = - ((struct sockaddr_in *)ia->ia_ifa.ifa_dstaddr)->sin_addr; - else - in_event_data.ia_dstaddr.s_addr = 0; - - in_event_data.ia_addr = ia->ia_addr.sin_addr; - in_event_data.ia_net = ia->ia_net; - in_event_data.ia_netmask = ia->ia_netmask; - in_event_data.ia_subnet = ia->ia_subnet; - in_event_data.ia_subnetmask = ia->ia_subnetmask; - in_event_data.ia_netbroadcast = ia->ia_netbroadcast; - strncpy(&in_event_data.link_data.if_name[0], ifp->if_name, IFNAMSIZ); - in_event_data.link_data.if_family = ifp->if_family; - in_event_data.link_data.if_unit = (unsigned long) ifp->if_unit; - - ev_msg.dv[0].data_ptr = &in_event_data; - ev_msg.dv[0].data_length = sizeof(struct kev_in_data); - ev_msg.dv[1].data_length = 0; - - kev_post_msg(&ev_msg); + ev_msg.vendor_code = KEV_VENDOR_APPLE; + ev_msg.kev_class = KEV_NETWORK_CLASS; + ev_msg.kev_subclass = KEV_INET_SUBCLASS; + + if (hostIsNew) + ev_msg.event_code = KEV_INET_NEW_ADDR; + else + ev_msg.event_code = KEV_INET_CHANGED_ADDR; + + if (ia->ia_ifa.ifa_dstaddr) { + in_event_data.ia_dstaddr = + ((struct sockaddr_in *)(void *)ia-> + ia_ifa.ifa_dstaddr)->sin_addr; + } else { + in_event_data.ia_dstaddr.s_addr = INADDR_ANY; + } + in_event_data.ia_addr = ia->ia_addr.sin_addr; + in_event_data.ia_net = ia->ia_net; + in_event_data.ia_netmask = ia->ia_netmask; + in_event_data.ia_subnet = ia->ia_subnet; + in_event_data.ia_subnetmask = ia->ia_subnetmask; + in_event_data.ia_netbroadcast = ia->ia_netbroadcast; + IFA_UNLOCK(&ia->ia_ifa); + (void) strncpy(&in_event_data.link_data.if_name[0], + ifp->if_name, IFNAMSIZ); + in_event_data.link_data.if_family = ifp->if_family; + in_event_data.link_data.if_unit = ifp->if_unit; + + ev_msg.dv[0].data_ptr = &in_event_data; + ev_msg.dv[0].data_length = sizeof (struct kev_in_data); + ev_msg.dv[1].data_length = 0; + + kev_post_msg(&ev_msg); + } else { + IFA_UNLOCK(&ia->ia_ifa); } + break; + } - return (error); - - case SIOCDIFADDR: - error = dlil_ioctl(PF_INET, ifp, SIOCDIFADDR, (caddr_t)ia); + case SIOCDIFADDR: /* struct ifreq */ + VERIFY(ifp != NULL && ia != NULL); + error = ifnet_ioctl(ifp, PF_INET, SIOCDIFADDR, ia); if (error == EOPNOTSUPP) error = 0; - if (error) - return error; + if (error != 0) { + break; + } /* Fill out the kernel event information */ ev_msg.vendor_code = KEV_VENDOR_APPLE; ev_msg.kev_class = KEV_NETWORK_CLASS; ev_msg.kev_subclass = KEV_INET_SUBCLASS; - - ev_msg.event_code = KEV_INET_ADDR_DELETED; - if (ia->ia_ifa.ifa_dstaddr) - in_event_data.ia_dstaddr = - ((struct sockaddr_in *)ia->ia_ifa.ifa_dstaddr)->sin_addr; - else - in_event_data.ia_dstaddr.s_addr = 0; + ev_msg.event_code = KEV_INET_ADDR_DELETED; + IFA_LOCK(&ia->ia_ifa); + if (ia->ia_ifa.ifa_dstaddr) { + in_event_data.ia_dstaddr = ((struct sockaddr_in *) + (void *)ia->ia_ifa.ifa_dstaddr)->sin_addr; + } else { + in_event_data.ia_dstaddr.s_addr = INADDR_ANY; + } in_event_data.ia_addr = ia->ia_addr.sin_addr; in_event_data.ia_net = ia->ia_net; in_event_data.ia_netmask = ia->ia_netmask; in_event_data.ia_subnet = ia->ia_subnet; in_event_data.ia_subnetmask = ia->ia_subnetmask; in_event_data.ia_netbroadcast = ia->ia_netbroadcast; - strncpy(&in_event_data.link_data.if_name[0], ifp->if_name, IFNAMSIZ); + IFA_UNLOCK(&ia->ia_ifa); + (void) strncpy(&in_event_data.link_data.if_name[0], + ifp->if_name, IFNAMSIZ); in_event_data.link_data.if_family = ifp->if_family; - in_event_data.link_data.if_unit = (unsigned long) ifp->if_unit; + in_event_data.link_data.if_unit = (u_int32_t) ifp->if_unit; ev_msg.dv[0].data_ptr = &in_event_data; ev_msg.dv[0].data_length = sizeof(struct kev_in_data); ev_msg.dv[1].data_length = 0; - lck_mtx_lock(rt_mtx); + ifa = &ia->ia_ifa; + lck_rw_lock_exclusive(in_ifaddr_rwlock); + /* Release ia_link reference */ + IFA_REMREF(ifa); TAILQ_REMOVE(&in_ifaddrhead, ia, ia_link); + IFA_LOCK(ifa); + if (IA_IS_HASHED(ia)) + in_iahash_remove(ia); + IFA_UNLOCK(ifa); + lck_rw_done(in_ifaddr_rwlock); + /* * in_ifscrub kills the interface route. */ - in_ifscrub(ifp, ia, 1); - ifa = &ia->ia_ifa; - lck_mtx_unlock(rt_mtx); + in_ifscrub(ifp, ia, 0); ifnet_lock_exclusive(ifp); + IFA_LOCK(ifa); + /* if_detach_ifa() releases ifa_link reference */ if_detach_ifa(ifp, ifa); - ifafree(&ia->ia_ifa); - -#ifdef __APPLE__ - /* - * If the interface supports multicast, and no address is left, - * remove the "all hosts" multicast group from that interface. - */ - if (ifp->if_flags & IFF_MULTICAST) { - struct in_addr addr; - struct in_multi *inm; - - TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) - if (ifa->ifa_addr->sa_family == AF_INET) + /* Our reference to this address is dropped at the bottom */ + IFA_UNLOCK(ifa); + + /* + * If the interface supports multicast, and no address is left, + * remove the "all hosts" multicast group from that interface. + */ + if ((ifp->if_flags & IFF_MULTICAST) != 0 || + ifp->if_allhostsinm != NULL ) { + + TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { + IFA_LOCK(ifa); + if (ifa->ifa_addr->sa_family == AF_INET) { + IFA_UNLOCK(ifa); break; - - if (ifa == 0) { - addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP); - IN_LOOKUP_MULTI(addr, ifp, inm); + } + IFA_UNLOCK(ifa); } ifnet_lock_done(ifp); - if (inm) - in_delmulti(&inm); - } else + + lck_mtx_lock(&ifp->if_addrconfig_lock); + if (ifa == NULL && ifp->if_allhostsinm != NULL) { + struct in_multi *inm = ifp->if_allhostsinm; + ifp->if_allhostsinm = NULL; + + in_delmulti(inm); + /* release the reference for allhostsinm */ + INM_REMREF(inm); + } + lck_mtx_unlock(&ifp->if_addrconfig_lock); + } else { ifnet_lock_done(ifp); -#endif + } /* Post the kernel event */ kev_post_msg(&ev_msg); + + /* + * See if there is any IPV4 address left and if so, + * reconfigure KDP to use current primary address. + */ + ifa = ifa_ifpgetprimary(ifp, AF_INET); + if (ifa != NULL) { + /* + * NOTE: SIOCSIFADDR is defined with struct ifreq + * as parameter, but here we are sending it down + * to the interface with a pointer to struct ifaddr, + * for legacy reasons. + */ + error = ifnet_ioctl(ifp, PF_INET, SIOCSIFADDR, ifa); + if (error == EOPNOTSUPP) + error = 0; + + /* Release reference from ifa_ifpgetprimary() */ + IFA_REMREF(ifa); + } +#if PF + (void) pf_ifaddr_hook(ifp, cmd); +#endif /* PF */ break; #ifdef __APPLE__ - case SIOCSETOT: { - /* - * Inspiration from tcp_ctloutput() and ip_ctloutput() - * Special ioctl for OpenTransport sockets - */ - struct inpcb *inp, *cloned_inp; - int error2 = 0; - int cloned_fd = *(int *)data; - - inp = sotoinpcb(so); - if (inp == NULL) { - break; - } - - /* let's make sure it's either -1 or a valid file descriptor */ - if (cloned_fd != -1) { - struct socket *cloned_so; - error2 = file_socket(cloned_fd, &cloned_so); - if (error2){ - break; - } - cloned_inp = sotoinpcb(cloned_so); + case SIOCSETOT: { /* int */ + /* + * Inspiration from tcp_ctloutput() and ip_ctloutput() + * Special ioctl for OpenTransport sockets + */ + struct inpcb *inp, *cloned_inp; + int error2 = 0; + int cloned_fd; + + bcopy(data, &cloned_fd, sizeof (cloned_fd)); + + inp = sotoinpcb(so); + if (inp == NULL) { + break; + } + + /* let's make sure it's either -1 or a valid file descriptor */ + if (cloned_fd != -1) { + struct socket *cloned_so; + error2 = file_socket(cloned_fd, &cloned_so); + if (error2) { + break; + } + cloned_inp = sotoinpcb(cloned_so); file_drop(cloned_fd); - } else { - cloned_inp = NULL; - } - - if (cloned_inp == NULL) { - /* OT always uses IP_PORTRANGE_HIGH */ - inp->inp_flags &= ~(INP_LOWPORT); - inp->inp_flags |= INP_HIGHPORT; - /* For UDP, OT allows broadcast by default */ - if (so->so_type == SOCK_DGRAM) - so->so_options |= SO_BROADCAST; - /* For TCP we want to see MSG_OOB when receive urgent data */ - else if (so->so_type == SOCK_STREAM) - so->so_options |= SO_WANTOOBFLAG; - } else { - inp->inp_ip_tos = cloned_inp->inp_ip_tos; - inp->inp_ip_ttl = cloned_inp->inp_ip_ttl; - inp->inp_flags = cloned_inp->inp_flags; - - /* Multicast options */ - if (cloned_inp->inp_moptions != NULL) { - int i; - struct ip_moptions *cloned_imo = cloned_inp->inp_moptions; - struct ip_moptions *imo = inp->inp_moptions; - - if (imo == NULL) { - /* - * No multicast option buffer attached to the pcb; - * allocate one. - */ - imo = (struct ip_moptions*) - _MALLOC(sizeof(*imo), M_IPMOPTS, M_WAITOK); - if (imo == NULL) { - error2 = ENOBUFS; - break; - } - inp->inp_moptions = imo; - } - imo->imo_multicast_ifp = cloned_imo->imo_multicast_ifp; - imo->imo_multicast_vif = cloned_imo->imo_multicast_vif; - imo->imo_multicast_ttl = cloned_imo->imo_multicast_ttl; - imo->imo_multicast_loop = cloned_imo->imo_multicast_loop; - imo->imo_num_memberships = cloned_imo->imo_num_memberships; - for (i = 0; i < cloned_imo->imo_num_memberships; i++) { - imo->imo_membership[i] = - in_addmulti(&cloned_imo->imo_membership[i]->inm_addr, - cloned_imo->imo_membership[i]->inm_ifp); - if (imo->imo_membership[i] == NULL) { - error2 = ENOBUFS; - break; - } - } - if (i < cloned_imo->imo_num_memberships) { - /* Failed, perform cleanup */ - for (i--; i >= 0; i--) - in_delmulti(&imo->imo_membership[i]); - imo->imo_num_memberships = 0; - break; - } - } - } - break; - } + } else { + cloned_inp = NULL; + } + + if (cloned_inp == NULL) { + /* OT always uses IP_PORTRANGE_HIGH */ + inp->inp_flags &= ~(INP_LOWPORT); + inp->inp_flags |= INP_HIGHPORT; + /* + * For UDP, OT allows broadcast by default; + * for TCP we want to see MSG_OOB when we + * receive urgent data. + */ + if (so->so_type == SOCK_DGRAM) + so->so_options |= SO_BROADCAST; + else if (so->so_type == SOCK_STREAM) + so->so_options |= SO_WANTOOBFLAG; + } else { + inp->inp_ip_tos = cloned_inp->inp_ip_tos; + inp->inp_ip_ttl = cloned_inp->inp_ip_ttl; + inp->inp_flags = cloned_inp->inp_flags; + + /* Multicast options */ + if (cloned_inp->inp_moptions != NULL) + error2 = imo_clone(cloned_inp, inp); + } + break; + } #endif /* __APPLE__ */ default: - return EOPNOTSUPP; + error = EOPNOTSUPP; } - return (0); + done: + if (ia != NULL) { + IFA_REMREF(&ia->ia_ifa); + } + return (error); } /* @@ -841,21 +1242,12 @@ in_control( * other values may be returned from in_ioctl() */ static int -in_lifaddr_ioctl( - struct socket *so, - u_long cmd, - caddr_t data, - struct ifnet *ifp, - struct proc *p) +in_lifaddr_ioctl(struct socket *so, u_long cmd, struct if_laddrreq *iflr, + struct ifnet *ifp, struct proc *p) { - struct if_laddrreq *iflr = (struct if_laddrreq *)data; struct ifaddr *ifa; - /* sanity checks */ - if (!data || !ifp) { - panic("invalid argument to in_lifaddr_ioctl"); - /*NOTRECHED*/ - } + VERIFY(ifp != NULL); switch (cmd) { case SIOCGLIFADDR: @@ -914,7 +1306,8 @@ in_lifaddr_ioctl( case SIOCDLIFADDR: { struct in_ifaddr *ia; - struct in_addr mask, candidate, match; + struct in_addr mask, candidate; + struct in_addr match = { 0 }; struct sockaddr_in *sin; int cmp; @@ -948,21 +1341,30 @@ in_lifaddr_ioctl( ifnet_lock_shared(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { - if (ifa->ifa_addr->sa_family != AF_INET6) + IFA_LOCK(ifa); + if (ifa->ifa_addr->sa_family != AF_INET6) { + IFA_UNLOCK(ifa); continue; - if (!cmp) + } + if (!cmp) { + IFA_UNLOCK(ifa); break; + } candidate.s_addr = ((struct sockaddr_in *)&ifa->ifa_addr)->sin_addr.s_addr; candidate.s_addr &= mask.s_addr; + IFA_UNLOCK(ifa); if (candidate.s_addr == match.s_addr) break; } + if (ifa != NULL) + IFA_ADDREF(ifa); ifnet_lock_done(ifp); if (!ifa) return EADDRNOTAVAIL; ia = (struct in_ifaddr *)ifa; if (cmd == SIOCGLIFADDR) { + IFA_LOCK(ifa); /* fill in the if_laddrreq structure */ bcopy(&ia->ia_addr, &iflr->addr, ia->ia_addr.sin_len); @@ -977,6 +1379,8 @@ in_lifaddr_ioctl( iflr->flags = 0; /*XXX*/ + IFA_UNLOCK(ifa); + IFA_REMREF(ifa); return 0; } else { struct in_aliasreq ifra; @@ -986,6 +1390,7 @@ in_lifaddr_ioctl( bcopy(iflr->iflr_name, ifra.ifra_name, sizeof(ifra.ifra_name)); + IFA_LOCK(ifa); bcopy(&ia->ia_addr, &ifra.ifra_addr, ia->ia_addr.sin_len); if ((ifp->if_flags & IFF_POINTOPOINT) != 0) { @@ -994,7 +1399,8 @@ in_lifaddr_ioctl( } bcopy(&ia->ia_sockmask, &ifra.ifra_dstaddr, ia->ia_sockmask.sin_len); - + IFA_UNLOCK(ifa); + IFA_REMREF(ifa); return in_control(so, SIOCDIFADDR, (caddr_t)&ifra, ifp, p); } @@ -1004,27 +1410,151 @@ in_lifaddr_ioctl( return EOPNOTSUPP; /*just for safety*/ } +/* + * Handle SIOCSETROUTERMODE to set or clear the IPv4 router mode flag on + * the interface. When in this mode, IPv4 Link-Local Address support is + * disabled in ARP, and DHCP client support is disabled in IP input; turning + * any of them on would cause an error to be returned. Entering or exiting + * this mode will result in the removal of IPv4 addresses currently configured + * on the interface. + */ +static int +in_setrouter(struct ifnet *ifp, int enable) +{ + if (ifp->if_flags & IFF_LOOPBACK) + return (ENODEV); + + ifnet_lock_exclusive(ifp); + if (enable) { + ifp->if_eflags |= IFEF_IPV4_ROUTER; + ifp->if_eflags &= ~(IFEF_ARPLL | IFEF_AUTOCONFIGURING); + } else { + ifp->if_eflags &= ~IFEF_IPV4_ROUTER; + } + ifnet_lock_done(ifp); + + /* purge all IPv4 addresses configured on this interface */ + in_purgeaddrs(ifp); + + return (0); +} + /* * Delete any existing route for an interface. */ void -in_ifscrub( - struct ifnet *ifp, - struct in_ifaddr *ia, - int locked) +in_ifscrub(struct ifnet *ifp, struct in_ifaddr *ia, int locked) { - - if ((ia->ia_flags & IFA_ROUTE) == 0) + IFA_LOCK(&ia->ia_ifa); + if ((ia->ia_flags & IFA_ROUTE) == 0) { + IFA_UNLOCK(&ia->ia_ifa); return; + } + IFA_UNLOCK(&ia->ia_ifa); if (!locked) - lck_mtx_lock(rt_mtx); + lck_mtx_lock(rnh_lock); if (ifp->if_flags & (IFF_LOOPBACK|IFF_POINTOPOINT)) rtinit_locked(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST); else rtinit_locked(&(ia->ia_ifa), (int)RTM_DELETE, 0); + IFA_LOCK(&ia->ia_ifa); ia->ia_flags &= ~IFA_ROUTE; + IFA_UNLOCK(&ia->ia_ifa); if (!locked) - lck_mtx_unlock(rt_mtx); + lck_mtx_unlock(rnh_lock); +} + +/* + * Caller must hold in_ifaddr_rwlock as writer. + */ +static void +in_iahash_remove(struct in_ifaddr *ia) +{ + lck_rw_assert(in_ifaddr_rwlock, LCK_RW_ASSERT_EXCLUSIVE); + IFA_LOCK_ASSERT_HELD(&ia->ia_ifa); + + if (!IA_IS_HASHED(ia)) { + panic("attempt to remove wrong ia %p from hash table\n", ia); + /* NOTREACHED */ + } + TAILQ_REMOVE(INADDR_HASH(ia->ia_addr.sin_addr.s_addr), ia, ia_hash); + IA_HASH_INIT(ia); + if (IFA_REMREF_LOCKED(&ia->ia_ifa) == NULL) { + panic("%s: unexpected (missing) refcnt ifa=%p", __func__, + &ia->ia_ifa); + /* NOTREACHED */ + } +} + +/* + * Caller must hold in_ifaddr_rwlock as writer. + */ +static void +in_iahash_insert(struct in_ifaddr *ia) +{ + lck_rw_assert(in_ifaddr_rwlock, LCK_RW_ASSERT_EXCLUSIVE); + IFA_LOCK_ASSERT_HELD(&ia->ia_ifa); + + if (ia->ia_addr.sin_family != AF_INET) { + panic("attempt to insert wrong ia %p into hash table\n", ia); + /* NOTREACHED */ + } else if (IA_IS_HASHED(ia)) { + panic("attempt to double-insert ia %p into hash table\n", ia); + /* NOTREACHED */ + } + TAILQ_INSERT_HEAD(INADDR_HASH(ia->ia_addr.sin_addr.s_addr), ia, ia_hash); + IFA_ADDREF_LOCKED(&ia->ia_ifa); +} + +/* + * Some point to point interfaces that are tunnels + * borrow the address from an underlying interface (e.g. + * VPN server). In order for source address selection logic to + * find the underlying interface first, we add the address + * of borrowing point to point interfaces at the end of the list. + * (see rdar://6733789) + * + * Caller must hold in_ifaddr_rwlock as writer. + */ +static void +in_iahash_insert_ptp(struct in_ifaddr *ia) +{ + struct in_ifaddr *tmp_ifa; + struct ifnet *tmp_ifp; + + lck_rw_assert(in_ifaddr_rwlock, LCK_RW_ASSERT_EXCLUSIVE); + IFA_LOCK_ASSERT_HELD(&ia->ia_ifa); + + if (ia->ia_addr.sin_family != AF_INET) { + panic("attempt to insert wrong ia %p into hash table\n", ia); + /* NOTREACHED */ + } else if (IA_IS_HASHED(ia)) { + panic("attempt to double-insert ia %p into hash table\n", ia); + /* NOTREACHED */ + } + IFA_UNLOCK(&ia->ia_ifa); + TAILQ_FOREACH(tmp_ifa, INADDR_HASH(ia->ia_addr.sin_addr.s_addr), + ia_hash) { + IFA_LOCK(&tmp_ifa->ia_ifa); + /* ia->ia_addr won't change, so check without lock */ + if (IA_SIN(tmp_ifa)->sin_addr.s_addr == + ia->ia_addr.sin_addr.s_addr) { + IFA_UNLOCK(&tmp_ifa->ia_ifa); + break; + } + IFA_UNLOCK(&tmp_ifa->ia_ifa); + } + tmp_ifp = (tmp_ifa == NULL) ? NULL : tmp_ifa->ia_ifp; + + IFA_LOCK(&ia->ia_ifa); + if (tmp_ifp == NULL) { + TAILQ_INSERT_HEAD(INADDR_HASH(ia->ia_addr.sin_addr.s_addr), + ia, ia_hash); + } else { + TAILQ_INSERT_TAIL(INADDR_HASH(ia->ia_addr.sin_addr.s_addr), + ia, ia_hash); + } + IFA_ADDREF_LOCKED(&ia->ia_ifa); } /* @@ -1038,30 +1568,99 @@ in_ifinit( struct sockaddr_in *sin, int scrub) { - u_long i = ntohl(sin->sin_addr.s_addr); + u_int32_t i = ntohl(sin->sin_addr.s_addr); struct sockaddr_in oldaddr; int flags = RTF_UP, error; + struct ifaddr *ifa0; + unsigned int cmd; + int oldremoved = 0; + + /* Take an extra reference for this routine */ + IFA_ADDREF(&ia->ia_ifa); + lck_rw_lock_exclusive(in_ifaddr_rwlock); + IFA_LOCK(&ia->ia_ifa); oldaddr = ia->ia_addr; + if (IA_IS_HASHED(ia)) { + oldremoved = 1; + in_iahash_remove(ia); + } ia->ia_addr = *sin; + ia->ia_addr.sin_len = sizeof (*sin); + if ((ifp->if_flags & IFF_POINTOPOINT)) + in_iahash_insert_ptp(ia); + else + in_iahash_insert(ia); + IFA_UNLOCK(&ia->ia_ifa); + lck_rw_done(in_ifaddr_rwlock); /* - * Give the interface a chance to initialize - * if this is its first address, - * and to validate the address if necessary. + * Give the interface a chance to initialize if this is its first + * address, and to validate the address if necessary. Send down + * SIOCSIFADDR for first address, and SIOCAIFADDR for alias(es). + * We find the first IPV4 address assigned to it and check if this + * is the same as the one passed into this routine. */ - error = dlil_ioctl(PF_INET, ifp, SIOCSIFADDR, (caddr_t)ia); + ifa0 = ifa_ifpgetprimary(ifp, AF_INET); + cmd = (&ia->ia_ifa == ifa0) ? SIOCSIFADDR : SIOCAIFADDR; + error = ifnet_ioctl(ifp, PF_INET, cmd, ia); if (error == EOPNOTSUPP) - error = 0; + error = 0; + /* + * If we've just sent down SIOCAIFADDR, send another ioctl down + * for SIOCSIFADDR for the first IPV4 address of the interface, + * because an address change on one of the addresses will result + * in the removal of the previous first IPV4 address. KDP needs + * be reconfigured with the current primary IPV4 address. + */ + if (error == 0 && cmd == SIOCAIFADDR) { + /* + * NOTE: SIOCSIFADDR is defined with struct ifreq + * as parameter, but here we are sending it down + * to the interface with a pointer to struct ifaddr, + * for legacy reasons. + */ + error = ifnet_ioctl(ifp, PF_INET, SIOCSIFADDR, ifa0); + if (error == EOPNOTSUPP) + error = 0; + } + + /* Release reference from ifa_ifpgetprimary() */ + IFA_REMREF(ifa0); + if (error) { + lck_rw_lock_exclusive(in_ifaddr_rwlock); + IFA_LOCK(&ia->ia_ifa); + if (IA_IS_HASHED(ia)) + in_iahash_remove(ia); ia->ia_addr = oldaddr; + if (oldremoved) { + if ((ifp->if_flags & IFF_POINTOPOINT)) + in_iahash_insert_ptp(ia); + else + in_iahash_insert(ia); + } + IFA_UNLOCK(&ia->ia_ifa); + lck_rw_done(in_ifaddr_rwlock); + /* Release extra reference taken above */ + IFA_REMREF(&ia->ia_ifa); return (error); } + lck_mtx_lock(rnh_lock); + IFA_LOCK(&ia->ia_ifa); + /* + * Address has been initialized by the link resolver (ARP) + * via ifnet_ioctl() above; it may now generate route(s). + */ + ia->ia_ifa.ifa_debug &= ~IFD_NOTREADY; if (scrub) { ia->ia_ifa.ifa_addr = (struct sockaddr *)&oldaddr; - in_ifscrub(ifp, ia, 0); + IFA_UNLOCK(&ia->ia_ifa); + in_ifscrub(ifp, ia, 1); + IFA_LOCK(&ia->ia_ifa); ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; } + IFA_LOCK_ASSERT_HELD(&ia->ia_ifa); if (IN_CLASSA(i)) ia->ia_netmask = IN_CLASSA_NET; else if (IN_CLASSB(i)) @@ -1094,12 +1693,24 @@ in_ifinit( ia->ia_ifa.ifa_dstaddr = ia->ia_ifa.ifa_addr; flags |= RTF_HOST; } else if (ifp->if_flags & IFF_POINTOPOINT) { - if (ia->ia_dstaddr.sin_family != AF_INET) + if (ia->ia_dstaddr.sin_family != AF_INET) { + IFA_UNLOCK(&ia->ia_ifa); + lck_mtx_unlock(rnh_lock); + /* Release extra reference taken above */ + IFA_REMREF(&ia->ia_ifa); return (0); + } + ia->ia_dstaddr.sin_len = sizeof (*sin); flags |= RTF_HOST; } - if ((error = rtinit(&(ia->ia_ifa), (int)RTM_ADD, flags)) == 0) + IFA_UNLOCK(&ia->ia_ifa); + if ((error = rtinit_locked(&(ia->ia_ifa), (int)RTM_ADD, flags)) == 0) { + IFA_LOCK(&ia->ia_ifa); ia->ia_flags |= IFA_ROUTE; + IFA_UNLOCK(&ia->ia_ifa); + } + lck_mtx_unlock(rnh_lock); + /* XXX check if the subnet route points to the same interface */ if (error == EEXIST) error = 0; @@ -1109,16 +1720,29 @@ in_ifinit( * multicast group on that interface. */ if (ifp->if_flags & IFF_MULTICAST) { - struct in_multi *inm; struct in_addr addr; + lck_mtx_lock(&ifp->if_addrconfig_lock); addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP); - ifnet_lock_shared(ifp); - IN_LOOKUP_MULTI(addr, ifp, inm); - ifnet_lock_done(ifp); - if (inm == 0) - in_addmulti(&addr, ifp); + if (ifp->if_allhostsinm == NULL) { + struct in_multi *inm; + inm = in_addmulti(&addr, ifp); + + if (inm != NULL) { + /* keep the reference on inm added by + * in_addmulti above for storing the + * pointer in allhostsinm + */ + ifp->if_allhostsinm = inm; + } else { + printf("Failed to add membership to all-hosts multicast address on interface %s%d\n", ifp->if_name, ifp->if_unit); + } + } + lck_mtx_unlock(&ifp->if_addrconfig_lock); } + + /* Release extra reference taken above */ + IFA_REMREF(&ia->ia_ifa); return (error); } @@ -1127,18 +1751,15 @@ in_ifinit( * Return 1 if the address might be a local broadcast address. */ int -in_broadcast( - struct in_addr in, - struct ifnet *ifp) +in_broadcast(struct in_addr in, struct ifnet *ifp) { struct ifaddr *ifa; - u_long t; + u_int32_t t; - if (in.s_addr == INADDR_BROADCAST || - in.s_addr == INADDR_ANY) - return 1; + if (in.s_addr == INADDR_BROADCAST || in.s_addr == INADDR_ANY) + return (1); if ((ifp->if_flags & IFF_BROADCAST) == 0) - return 0; + return (0); t = ntohl(in.s_addr); /* * Look through the list of addresses for a match @@ -1147,10 +1768,7 @@ in_broadcast( #define ia ((struct in_ifaddr *)ifa) ifnet_lock_shared(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { - if (ifa->ifa_addr == NULL) { - ifnet_lock_done(ifp); - return (0); - } + IFA_LOCK(ifa); if (ifa->ifa_addr->sa_family == AF_INET && (in.s_addr == ia->ia_broadaddr.sin_addr.s_addr || in.s_addr == ia->ia_netbroadcast.s_addr || @@ -1163,158 +1781,258 @@ in_broadcast( * only exist when an interface gets a secondary * address. */ - ia->ia_subnetmask != (u_long)0xffffffff) { + ia->ia_subnetmask != (u_int32_t)0xffffffff) { + IFA_UNLOCK(ifa); ifnet_lock_done(ifp); - return 1; + return (1); } + IFA_UNLOCK(ifa); } ifnet_lock_done(ifp); return (0); #undef ia } -static void -in_free_inm( - void* ifma_protospec) +void +in_purgeaddrs(struct ifnet *ifp) { - struct in_multi *inm = ifma_protospec; - + struct ifaddr **ifap; + int err, i; + /* - * No remaining claims to this record; let IGMP know that - * we are leaving the multicast group. + * Be nice, and try the civilized way first. If we can't get + * rid of them this way, then do it the rough way. We must + * only get here during detach time, after the ifnet has been + * removed from the global list and arrays. */ - igmp_leavegroup(inm); - lck_mtx_lock(rt_mtx); - LIST_REMOVE(inm, inm_link); - lck_mtx_unlock(rt_mtx); - FREE(inm, M_IPMADDR); + err = ifnet_get_address_list_family_internal(ifp, &ifap, AF_INET, 1, + M_WAITOK); + if (err == 0 && ifap != NULL) { + for (i = 0; ifap[i] != NULL; i++) { + struct ifaliasreq ifr; + struct ifaddr *ifa; + + ifa = ifap[i]; + bzero(&ifr, sizeof (ifr)); + IFA_LOCK(ifa); + ifr.ifra_addr = *ifa->ifa_addr; + if (ifa->ifa_dstaddr != NULL) + ifr.ifra_broadaddr = *ifa->ifa_dstaddr; + IFA_UNLOCK(ifa); + err = in_control(NULL, SIOCDIFADDR, (caddr_t)&ifr, ifp, + kernproc); + /* if we lost the race, ignore it */ + if (err == EADDRNOTAVAIL) + err = 0; + if (err != 0) { + char s_addr[MAX_IPv4_STR_LEN]; + char s_dstaddr[MAX_IPv4_STR_LEN]; + struct in_addr *s, *d; + + IFA_LOCK(ifa); + s = &((struct sockaddr_in *) + (void *)ifa->ifa_addr)->sin_addr; + d = &((struct sockaddr_in *) + (void *)ifa->ifa_dstaddr)->sin_addr; + (void) inet_ntop(AF_INET, &s->s_addr, s_addr, + sizeof (s_addr)); + (void) inet_ntop(AF_INET, &d->s_addr, s_dstaddr, + sizeof (s_dstaddr)); + IFA_UNLOCK(ifa); + + printf("%s: SIOCDIFADDR ifp=%p ifa_addr=%s " + "ifa_dstaddr=%s (err=%d)\n", __func__, ifp, + s_addr, s_dstaddr, err); + } + } + ifnet_free_address_list(ifap); + } else if (err != 0 && err != ENXIO) { + printf("%s: error retrieving list of AF_INET addresses for " + "ifp=%p (err=%d)\n", __func__, ifp, err); + } +} + +int inet_aton(char *cp, struct in_addr *pin); +int +inet_aton(char * cp, struct in_addr * pin) +{ + u_char * b = (unsigned char *)pin; + int i; + char * p; + + for (p = cp, i = 0; i < 4; i++) { + u_int32_t l = strtoul(p, 0, 0); + if (l > 255) + return (FALSE); + b[i] = l; + p = strchr(p, '.'); + if (i < 3 && p == NULL) + return (FALSE); + p++; + } + return (TRUE); +} + +int inet_ntoa2(struct in_addr * pin, char * cp, const int len); +int inet_ntoa2(struct in_addr * pin, char * cp, const int len) +{ + int ret; + + /* address is in network byte order */ + ret = snprintf(cp, len, "%u.%u.%u.%u", pin->s_addr & 0xFF, + (pin->s_addr >> 8) & 0xFF, (pin->s_addr >> 16) & 0xFF, + (pin->s_addr >> 24) & 0xFF); + + return ret < len ? TRUE : FALSE; } /* - * Add an address to the list of IP multicast addresses for a given interface. + * Called as part of ip_init */ -struct in_multi * -in_addmulti( - struct in_addr *ap, - struct ifnet *ifp) +void +in_ifaddr_init(void) { - struct in_multi *inm; - int error; - struct sockaddr_in sin; - struct ifmultiaddr *ifma; + in_multi_init(); - /* - * Call generic routine to add membership or increment - * refcount. It wants addresses in the form of a sockaddr, - * so we build one here (being careful to zero the unused bytes). - */ - bzero(&sin, sizeof sin); - sin.sin_family = AF_INET; - sin.sin_len = sizeof sin; - sin.sin_addr = *ap; - error = if_addmulti(ifp, (struct sockaddr *)&sin, &ifma); - if (error) { - return 0; - } + PE_parse_boot_argn("ifa_debug", &inifa_debug, sizeof (inifa_debug)); - /* - * If ifma->ifma_protospec is null, then if_addmulti() created - * a new record. Otherwise, we are done. - */ - if (ifma->ifma_protospec != 0) { - return ifma->ifma_protospec; - } + inifa_size = (inifa_debug == 0) ? sizeof (struct in_ifaddr) : + sizeof (struct in_ifaddr_dbg); - inm = (struct in_multi *) _MALLOC(sizeof(*inm), M_IPMADDR, M_WAITOK); - if (inm == NULL) { - return (NULL); + inifa_zone = zinit(inifa_size, INIFA_ZONE_MAX * inifa_size, + 0, INIFA_ZONE_NAME); + if (inifa_zone == NULL) { + panic("%s: failed allocating %s", __func__, INIFA_ZONE_NAME); + /* NOTREACHED */ } + zone_change(inifa_zone, Z_EXPAND, TRUE); + zone_change(inifa_zone, Z_CALLERACCT, FALSE); - bzero(inm, sizeof *inm); - inm->inm_addr = *ap; - inm->inm_ifp = ifp; - inm->inm_ifma = ifma; - lck_mtx_lock(rt_mtx); - if (ifma->ifma_protospec == NULL) { - ifma->ifma_protospec = inm; - ifma->ifma_free = in_free_inm; - LIST_INSERT_HEAD(&in_multihead, inm, inm_link); - } - lck_mtx_unlock(rt_mtx); - - if (ifma->ifma_protospec != inm) { - _FREE(inm, M_IPMADDR); - return ifma->ifma_protospec; + lck_mtx_init(&inifa_trash_lock, ifa_mtx_grp, ifa_mtx_attr); + TAILQ_INIT(&inifa_trash_head); +} + +static struct in_ifaddr * +in_ifaddr_alloc(int how) +{ + struct in_ifaddr *inifa; + + inifa = (how == M_WAITOK) ? zalloc(inifa_zone) : + zalloc_noblock(inifa_zone); + if (inifa != NULL) { + bzero(inifa, inifa_size); + inifa->ia_ifa.ifa_free = in_ifaddr_free; + inifa->ia_ifa.ifa_debug |= IFD_ALLOC; + ifa_lock_init(&inifa->ia_ifa); + if (inifa_debug != 0) { + struct in_ifaddr_dbg *inifa_dbg = + (struct in_ifaddr_dbg *)inifa; + inifa->ia_ifa.ifa_debug |= IFD_DEBUG; + inifa->ia_ifa.ifa_trace = in_ifaddr_trace; + inifa->ia_ifa.ifa_attached = in_ifaddr_attached; + inifa->ia_ifa.ifa_detached = in_ifaddr_detached; + ctrace_record(&inifa_dbg->inifa_alloc); + } } + return (inifa); +} - /* - * Let IGMP know that we have joined a new IP multicast group. - */ - error = igmp_joingroup(inm); - if (error) { - char addrbuf[16]; - - /* - * We can't free the inm because someone else may already be - * using it. Once we put it in to ifma->ifma_protospec, it - * must exist as long as the ifma does. Might be nice to flag - * the error so we can try igmp_joingroup the next time through. - */ - log(LOG_ERR, "igmp_joingroup error %d joining multicast %s on %s%d\n", - error, inet_ntop(AF_INET, &sin.sin_addr, addrbuf, sizeof(addrbuf)), - ifp->if_name, ifp->if_unit); +static void +in_ifaddr_free(struct ifaddr *ifa) +{ + IFA_LOCK_ASSERT_HELD(ifa); + + if (ifa->ifa_refcnt != 0) { + panic("%s: ifa %p bad ref cnt", __func__, ifa); + /* NOTREACHED */ + } if (!(ifa->ifa_debug & IFD_ALLOC)) { + panic("%s: ifa %p cannot be freed", __func__, ifa); + /* NOTREACHED */ } - - return (inm); + if (ifa->ifa_debug & IFD_DEBUG) { + struct in_ifaddr_dbg *inifa_dbg = (struct in_ifaddr_dbg *)ifa; + ctrace_record(&inifa_dbg->inifa_free); + bcopy(&inifa_dbg->inifa, &inifa_dbg->inifa_old, + sizeof (struct in_ifaddr)); + if (ifa->ifa_debug & IFD_TRASHED) { + /* Become a regular mutex, just in case */ + IFA_CONVERT_LOCK(ifa); + lck_mtx_lock(&inifa_trash_lock); + TAILQ_REMOVE(&inifa_trash_head, inifa_dbg, + inifa_trash_link); + lck_mtx_unlock(&inifa_trash_lock); + ifa->ifa_debug &= ~IFD_TRASHED; + } + } + IFA_UNLOCK(ifa); + ifa_lock_destroy(ifa); + bzero(ifa, sizeof (struct in_ifaddr)); + zfree(inifa_zone, ifa); } -/* - * Delete a multicast address record. - */ -void -in_delmulti( - struct in_multi **inm) +static void +in_ifaddr_attached(struct ifaddr *ifa) { - struct in_multi *inm2; - - lck_mtx_lock(rt_mtx); - LIST_FOREACH(inm2, &in_multihead, inm_link) { - if (inm2 == *inm) - break; + struct in_ifaddr_dbg *inifa_dbg = (struct in_ifaddr_dbg *)ifa; + + IFA_LOCK_ASSERT_HELD(ifa); + + if (!(ifa->ifa_debug & IFD_DEBUG)) { + panic("%s: ifa %p has no debug structure", __func__, ifa); + /* NOTREACHED */ } - if (inm2 != *inm) { - lck_mtx_unlock(rt_mtx); - printf("in_delmulti - ignorning invalid inm (0x%x)\n", *inm); - return; + if (ifa->ifa_debug & IFD_TRASHED) { + /* Become a regular mutex, just in case */ + IFA_CONVERT_LOCK(ifa); + lck_mtx_lock(&inifa_trash_lock); + TAILQ_REMOVE(&inifa_trash_head, inifa_dbg, inifa_trash_link); + lck_mtx_unlock(&inifa_trash_lock); + ifa->ifa_debug &= ~IFD_TRASHED; } - lck_mtx_unlock(rt_mtx); - - /* We intentionally do this a bit differently than BSD */ - if ((*inm)->inm_ifma) { - if_delmultiaddr((*inm)->inm_ifma, 0); - ifma_release((*inm)->inm_ifma); +} + +static void +in_ifaddr_detached(struct ifaddr *ifa) +{ + struct in_ifaddr_dbg *inifa_dbg = (struct in_ifaddr_dbg *)ifa; + + IFA_LOCK_ASSERT_HELD(ifa); + + if (!(ifa->ifa_debug & IFD_DEBUG)) { + panic("%s: ifa %p has no debug structure", __func__, ifa); + /* NOTREACHED */ + } else if (ifa->ifa_debug & IFD_TRASHED) { + panic("%s: ifa %p is already in trash list", __func__, ifa); + /* NOTREACHED */ } - *inm = NULL; + ifa->ifa_debug |= IFD_TRASHED; + /* Become a regular mutex, just in case */ + IFA_CONVERT_LOCK(ifa); + lck_mtx_lock(&inifa_trash_lock); + TAILQ_INSERT_TAIL(&inifa_trash_head, inifa_dbg, inifa_trash_link); + lck_mtx_unlock(&inifa_trash_lock); } -#if !NFSCLIENT -int -inet_aton(char * cp, struct in_addr * pin) +static void +in_ifaddr_trace(struct ifaddr *ifa, int refhold) { - u_char * b = (char *)pin; - int i; - char * p; + struct in_ifaddr_dbg *inifa_dbg = (struct in_ifaddr_dbg *)ifa; + ctrace_t *tr; + u_int32_t idx; + u_int16_t *cnt; + + if (!(ifa->ifa_debug & IFD_DEBUG)) { + panic("%s: ifa %p has no debug structure", __func__, ifa); + /* NOTREACHED */ + } + if (refhold) { + cnt = &inifa_dbg->inifa_refhold_cnt; + tr = inifa_dbg->inifa_refhold; + } else { + cnt = &inifa_dbg->inifa_refrele_cnt; + tr = inifa_dbg->inifa_refrele; + } - for (p = cp, i = 0; i < 4; i++) { - u_long l = strtoul(p, 0, 0); - if (l > 255) - return (FALSE); - b[i] = l; - p = strchr(p, '.'); - if (i < 3 && p == NULL) - return (FALSE); - p++; - } - return (TRUE); + idx = atomic_add_16_ov(cnt, 1) % INIFA_TRACE_HIST_SIZE; + ctrace_record(&tr[idx]); } -#endif