X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/316670eb35587141e969394ae8537d66b9211e80..ecc0ceb4089d506a0b8d16686a95817b331af9cb:/bsd/netinet/in.c diff --git a/bsd/netinet/in.c b/bsd/netinet/in.c index 1df980df6..9f65560f8 100644 --- a/bsd/netinet/in.c +++ b/bsd/netinet/in.c @@ -1,8 +1,8 @@ /* - * Copyright (c) 2000-2011 Apple Inc. All rights reserved. + * Copyright (c) 2000-2015 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ - * + * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in @@ -11,10 +11,10 @@ * unlawful or unlicensed copies of an Apple operating system, or to * circumvent, violate, or enable the circumvention or violation of, any * terms of an Apple operating system software license agreement. - * + * * Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this file. - * + * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, @@ -22,7 +22,7 @@ * 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_OSREFERENCE_LICENSE_HEADER_END@ */ /* @@ -58,7 +58,6 @@ * SUCH DAMAGE. * * @(#)in.c 8.4 (Berkeley) 1/9/95 - * $FreeBSD: src/sys/netinet/in.c,v 1.44.2.5 2001/08/13 16:26:17 ume Exp $ */ #include @@ -73,43 +72,48 @@ #include #include #include -#include +#include +#include +#include #include #include #include #include #include +#include +#if PF +#include +#endif /* PF */ #include #include #include - #include -#include - #include - #include #include #include -#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, 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 inctl_associd(struct socket *, u_long, caddr_t); +static int inctl_connid(struct socket *, u_long, caddr_t); +static int inctl_conninfo(struct socket *, u_long, caddr_t); +static int inctl_autoaddr(struct ifnet *, struct ifreq *); +static int inctl_arpipll(struct ifnet *, struct ifreq *); +static int inctl_setrouter(struct ifnet *, struct ifreq *); +static int inctl_ifaddr(struct ifnet *, struct in_ifaddr *, u_long, + struct ifreq *); +static int inctl_ifdstaddr(struct ifnet *, struct in_ifaddr *, u_long, + struct ifreq *); +static int inctl_ifbrdaddr(struct ifnet *, struct in_ifaddr *, u_long, + struct ifreq *); +static int inctl_ifnetmask(struct ifnet *, struct in_ifaddr *, u_long, + struct ifreq *); + +static void in_socktrim(struct sockaddr_in *); +static int in_ifinit(struct ifnet *, struct in_ifaddr *, + struct sockaddr_in *, int); #define IA_HASH_INIT(ia) { \ (ia)->ia_hash.tqe_next = (void *)(uintptr_t)-1; \ @@ -129,12 +133,18 @@ static void in_ifaddr_detached(struct ifaddr *); static void in_ifaddr_free(struct ifaddr *); static void in_ifaddr_trace(struct ifaddr *, int); +static int in_getassocids(struct socket *, uint32_t *, user_addr_t); +static int in_getconnids(struct socket *, sae_associd_t, uint32_t *, user_addr_t); +static int in_getconninfo(struct socket *, sae_connid_t, uint32_t *, + uint32_t *, int32_t *, user_addr_t, socklen_t *, user_addr_t, socklen_t *, + uint32_t *, user_addr_t, uint32_t *); + static int subnetsarelocal = 0; -SYSCTL_INT(_net_inet_ip, OID_AUTO, subnets_are_local, CTLFLAG_RW | CTLFLAG_LOCKED, - &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; +u_int32_t ipv4_ll_arp_aware = 0; #define INIFA_TRACE_HIST_SIZE 32 /* size of trace history */ @@ -177,6 +187,10 @@ 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 */ +static const unsigned int in_extra_size = sizeof (struct in_ifextra); +static const unsigned int in_extra_bufsize = in_extra_size + + sizeof (void *) + sizeof (uint64_t); + /* * Return 1 if the address is * - loopback @@ -191,10 +205,11 @@ inaddr_local(struct in_addr in) struct sockaddr_in sin; int local = 0; - if (ntohl(in.s_addr) == INADDR_LOOPBACK || IN_LINKLOCAL(ntohl(in.s_addr))) { + 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) { + ntohl(in.s_addr) <= INADDR_MAX_LOCAL_GROUP) { local = 1; } else { sin.sin_family = AF_INET; @@ -230,8 +245,8 @@ in_localaddr(struct in_addr in) if (subnetsarelocal) { lck_rw_lock_shared(in_ifaddr_rwlock); - for (ia = in_ifaddrhead.tqh_first; ia; - ia = ia->ia_link.tqe_next) { + for (ia = in_ifaddrhead.tqh_first; ia != NULL; + ia = ia->ia_link.tqe_next) { IFA_LOCK(&ia->ia_ifa); if ((i & ia->ia_netmask) == ia->ia_net) { IFA_UNLOCK(&ia->ia_ifa); @@ -243,8 +258,8 @@ in_localaddr(struct in_addr in) lck_rw_done(in_ifaddr_rwlock); } else { lck_rw_lock_shared(in_ifaddr_rwlock); - for (ia = in_ifaddrhead.tqh_first; ia; - ia = ia->ia_link.tqe_next) { + for (ia = in_ifaddrhead.tqh_first; ia != NULL; + ia = ia->ia_link.tqe_next) { IFA_LOCK(&ia->ia_ifa); if ((i & ia->ia_subnetmask) == ia->ia_subnet) { IFA_UNLOCK(&ia->ia_ifa); @@ -263,20 +278,20 @@ in_localaddr(struct in_addr in) * that may not be forwarded, or whether datagrams to that destination * may be forwarded. */ -int +boolean_t in_canforward(struct in_addr in) { u_int32_t i = ntohl(in.s_addr); u_int32_t net; if (IN_EXPERIMENTAL(i) || IN_MULTICAST(i)) - return (0); + return (FALSE); if (IN_CLASSA(i)) { net = i & IN_CLASSA_NET; if (net == 0 || net == (IN_LOOPBACKNET << IN_CLASSA_NSHIFT)) - return (0); + return (FALSE); } - return (1); + return (TRUE); } /* @@ -285,50 +300,15 @@ in_canforward(struct in_addr in) static void in_socktrim(struct sockaddr_in *ap) { - char *cplim = (char *) &ap->sin_addr; - char *cp = (char *) (&ap->sin_addr + 1); - - ap->sin_len = 0; - while (--cp >= cplim) - if (*cp) { - (ap)->sin_len = cp - (char *) (ap) + 1; - break; - } -} - -static int -in_mask2len(struct in_addr *mask) -{ - size_t x, y; - u_char *p; + char *cplim = (char *)&ap->sin_addr; + char *cp = (char *)(&ap->sin_addr + 1); - p = (u_char *)mask; - for (x = 0; x < sizeof(*mask); x++) { - if (p[x] != 0xff) + ap->sin_len = 0; + while (--cp >= cplim) + if (*cp) { + (ap)->sin_len = cp - (char *)(ap) + 1; break; - } - y = 0; - if (x < sizeof(*mask)) { - for (y = 0; y < 8; y++) { - if ((p[x] & (0x80 >> y)) == 0) - break; } - } - return x * 8 + y; -} - -static void -in_len2mask(struct in_addr *mask, int len) -{ - int i; - u_char *p; - - p = (u_char *)mask; - bzero(mask, sizeof(*mask)); - for (i = 0; i < len / 8; i++) - p[i] = 0xff; - if (len % 8) - p[i] = (0xff00 >> (len % 8)) & 0xff; } static int in_interfaces; /* number of external internet interfaces */ @@ -338,396 +318,585 @@ 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); + VERIFY(ifp != NULL); + + if ((error = proto_plumb(PF_INET, ifp)) && error != EEXIST) { + log(LOG_ERR, "%s: proto_plumb returned %d if=%s\n", + __func__, error, if_name(ifp)); + } else if (error == 0 && ifp->if_inetdata == NULL) { + void **pbuf, *base; + struct in_ifextra *ext; + int errorx; + + if ((ext = (struct in_ifextra *)_MALLOC(in_extra_bufsize, + M_IFADDR, M_WAITOK|M_ZERO)) == NULL) { + error = ENOMEM; + errorx = proto_unplumb(PF_INET, ifp); + if (errorx != 0) { + log(LOG_ERR, + "%s: proto_unplumb returned %d if=%s%d\n", + __func__, errorx, ifp->if_name, + ifp->if_unit); + } + goto done; + } + /* Align on 64-bit boundary */ + base = (void *)P2ROUNDUP((intptr_t)ext + sizeof (uint64_t), + sizeof (uint64_t)); + VERIFY(((intptr_t)base + in_extra_size) <= + ((intptr_t)ext + in_extra_bufsize)); + pbuf = (void **)((intptr_t)base - sizeof (void *)); + *pbuf = ext; + ifp->if_inetdata = base; + VERIFY(IS_P2ALIGNED(ifp->if_inetdata, sizeof (uint64_t))); + } +done: + if (error == 0 && ifp->if_inetdata != NULL) { + /* + * Since the structure is never freed, we need to + * zero out its contents to avoid reusing stale data. + * A little redundant with allocation above, but it + * keeps the code simpler for all cases. + */ + bzero(ifp->if_inetdata, in_extra_size); + } 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) +static __attribute__((noinline)) int +inctl_associd(struct socket *so, u_long cmd, caddr_t data) { - struct in_ifaddr *ia = NULL; - struct ifaddr *ifa; - struct sockaddr_in oldaddr; int error = 0; - int hostIsNew, maskIsNew; - struct kev_msg ev_msg; - struct kev_in_data in_event_data; + union { + struct so_aidreq32 a32; + struct so_aidreq64 a64; + } u; - bzero(&in_event_data, sizeof (struct kev_in_data)); - bzero(&ev_msg, sizeof (struct kev_msg)); + VERIFY(so != NULL); switch (cmd) { - 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; + case SIOCGASSOCIDS32: /* struct so_aidreq32 */ + bcopy(data, &u.a32, sizeof (u.a32)); + error = in_getassocids(so, &u.a32.sar_cnt, u.a32.sar_aidp); + if (error == 0) + bcopy(&u.a32, data, sizeof (u.a32)); + break; - if (ifp == NULL) - return (EINVAL); + case SIOCGASSOCIDS64: /* struct so_aidreq64 */ + bcopy(data, &u.a64, sizeof (u.a64)); + error = in_getassocids(so, &u.a64.sar_cnt, u.a64.sar_aidp); + if (error == 0) + bcopy(&u.a64, data, sizeof (u.a64)); + break; - bcopy(data, &iflr, sizeof (iflr)); - error = in_lifaddr_ioctl(so, cmd, &iflr, ifp, p); - bcopy(&iflr, data, sizeof (iflr)); - return (error); - } + default: + VERIFY(0); + /* NOTREACHED */ } - /* - * Find address for this interface, if it exists. - * - * If an alias address was specified, find that one instead of - * the first one on the interface. - */ - if (ifp != NULL) { - struct in_ifaddr *iap; - struct sockaddr_in sin; + return (error); +} - bcopy(&((struct ifreq *)(void *)data)->ifr_addr, - &sin, sizeof (sin)); +static __attribute__((noinline)) int +inctl_connid(struct socket *so, u_long cmd, caddr_t data) +{ + int error = 0; + union { + struct so_cidreq32 c32; + struct so_cidreq64 c64; + } u; - 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; + VERIFY(so != NULL); - 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; - } - } - 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 SIOCGCONNIDS32: /* struct so_cidreq32 */ + bcopy(data, &u.c32, sizeof (u.c32)); + error = in_getconnids(so, u.c32.scr_aid, &u.c32.scr_cnt, + u.c32.scr_cidp); + if (error == 0) + bcopy(&u.c32, data, sizeof (u.c32)); + break; + + case SIOCGCONNIDS64: /* struct so_cidreq64 */ + bcopy(data, &u.c64, sizeof (u.c64)); + error = in_getconnids(so, u.c64.scr_aid, &u.c64.scr_cnt, + u.c64.scr_cidp); + if (error == 0) + bcopy(&u.c64, data, sizeof (u.c64)); + break; + + default: + VERIFY(0); + /* NOTREACHED */ } + return (error); +} + +static __attribute__((noinline)) int +inctl_conninfo(struct socket *so, u_long cmd, caddr_t data) +{ + int error = 0; + union { + struct so_cinforeq32 ci32; + struct so_cinforeq64 ci64; + } u; + + VERIFY(so != NULL); + switch (cmd) { - 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; - } + case SIOCGCONNINFO32: /* struct so_cinforeq32 */ + bcopy(data, &u.ci32, sizeof (u.ci32)); + error = in_getconninfo(so, u.ci32.scir_cid, &u.ci32.scir_flags, + &u.ci32.scir_ifindex, &u.ci32.scir_error, u.ci32.scir_src, + &u.ci32.scir_src_len, u.ci32.scir_dst, &u.ci32.scir_dst_len, + &u.ci32.scir_aux_type, u.ci32.scir_aux_data, + &u.ci32.scir_aux_len); + if (error == 0) + bcopy(&u.ci32, data, sizeof (u.ci32)); break; - case SIOCAIFADDR: /* struct ifaliasreq */ - case SIOCDIFADDR: { /* struct ifreq */ - struct sockaddr_in addr, dstaddr; + case SIOCGCONNINFO64: /* struct so_cinforeq64 */ + bcopy(data, &u.ci64, sizeof (u.ci64)); + error = in_getconninfo(so, u.ci64.scir_cid, &u.ci64.scir_flags, + &u.ci64.scir_ifindex, &u.ci64.scir_error, u.ci64.scir_src, + &u.ci64.scir_src_len, u.ci64.scir_dst, &u.ci64.scir_dst_len, + &u.ci64.scir_aux_type, u.ci64.scir_aux_data, + &u.ci64.scir_aux_len); + if (error == 0) + bcopy(&u.ci64, data, sizeof (u.ci64)); + break; - if (ifp == NULL) { - error = EADDRNOTAVAIL; - goto done; - } + default: + VERIFY(0); + /* NOTREACHED */ + } - 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)); - } + return (error); +} - if (addr.sin_family == AF_INET) { - struct in_ifaddr *oia; +/* + * Caller passes in the ioctl data pointer directly via "ifr", with the + * expectation that this routine always uses bcopy() or other byte-aligned + * memory accesses. + */ +static __attribute__((noinline)) int +inctl_autoaddr(struct ifnet *ifp, struct ifreq *ifr) +{ + int error = 0, intval; - 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 == - addr.sin_addr.s_addr) { - IFA_ADDREF_LOCKED(&ia->ia_ifa); - IFA_UNLOCK(&ia->ia_ifa); - break; - } - IFA_UNLOCK(&ia->ia_ifa); - } - 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; + VERIFY(ifp != NULL); + + bcopy(&ifr->ifr_intval, &intval, sizeof (intval)); + + ifnet_lock_exclusive(ifp); + 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; } - /* FALLTHROUGH */ } - case SIOCSIFADDR: /* struct ifreq */ - case SIOCSIFNETMASK: /* struct ifreq */ - case SIOCSIFDSTADDR: { /* struct ifreq */ - struct sockaddr_in addr; + if (!intval) + ifp->if_eflags &= ~IFEF_AUTOCONFIGURING; + ifnet_lock_done(ifp); - if (cmd == SIOCAIFADDR) { - /* fell thru from above; just repeat it */ - bcopy(&((struct in_aliasreq *)(void *)data)-> - ifra_addr, &addr, sizeof (addr)); + return (error); +} + +/* + * Caller passes in the ioctl data pointer directly via "ifr", with the + * expectation that this routine always uses bcopy() or other byte-aligned + * memory accesses. + */ +static __attribute__((noinline)) int +inctl_arpipll(struct ifnet *ifp, struct ifreq *ifr) +{ + int error = 0, intval; + + VERIFY(ifp != NULL); + + bcopy(&ifr->ifr_intval, &intval, sizeof (intval)); + ipv4_ll_arp_aware = 1; + + ifnet_lock_exclusive(ifp); + 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 { - VERIFY(cmd == SIOCDIFADDR || cmd == SIOCSIFADDR || - cmd == SIOCSIFNETMASK || cmd == SIOCSIFDSTADDR); - bcopy(&((struct ifreq *)(void *)data)->ifr_addr, - &addr, sizeof (addr)); + ifp->if_eflags |= IFEF_ARPLL; } + } + if (!intval) + ifp->if_eflags &= ~IFEF_ARPLL; + ifnet_lock_done(ifp); - /* 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; - if (ifp->if_flags & IFF_BROADCAST) { - 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_rw_lock_exclusive(in_ifaddr_rwlock); - /* Hold a reference for ia_link */ - IFA_ADDREF(ifa); - TAILQ_INSERT_TAIL(&in_ifaddrhead, ia, ia_link); - lck_rw_done(in_ifaddr_rwlock); - /* discard error */ - (void) in_domifattach(ifp); - error = 0; + return (error); +} + +/* + * 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. + * + * Caller passes in the ioctl data pointer directly via "ifr", with the + * expectation that this routine always uses bcopy() or other byte-aligned + * memory accesses. + */ +static __attribute__((noinline)) int +inctl_setrouter(struct ifnet *ifp, struct ifreq *ifr) +{ + int error = 0, intval; + + VERIFY(ifp != NULL); + + /* Router mode isn't valid for loopback */ + if (ifp->if_flags & IFF_LOOPBACK) + return (ENODEV); + + bcopy(&ifr->ifr_intval, &intval, sizeof (intval)); + + ifnet_lock_exclusive(ifp); + if (intval) { + 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 (error); +} + +/* + * Caller passes in the ioctl data pointer directly via "ifr", with the + * expectation that this routine always uses bcopy() or other byte-aligned + * memory accesses. + */ +static __attribute__((noinline)) int +inctl_ifaddr(struct ifnet *ifp, struct in_ifaddr *ia, u_long cmd, + struct ifreq *ifr) +{ + struct kev_in_data in_event_data; + struct kev_msg ev_msg; + struct sockaddr_in addr; + struct ifaddr *ifa; + int error = 0; + + VERIFY(ifp != NULL); + + bzero(&in_event_data, sizeof (struct kev_in_data)); + bzero(&ev_msg, sizeof (struct kev_msg)); + + switch (cmd) { + case SIOCGIFADDR: /* struct ifreq */ + if (ia == NULL) { + error = EADDRNOTAVAIL; + break; } + IFA_LOCK(&ia->ia_ifa); + bcopy(&ia->ia_addr, &ifr->ifr_addr, sizeof (addr)); + IFA_UNLOCK(&ia->ia_ifa); break; - } - case SIOCPROTOATTACH: /* struct ifreq */ - case SIOCPROTODETACH: /* struct ifreq */ - if ((error = proc_suser(p)) != 0) { - goto done; - } - if (ifp == NULL) { - error = EADDRNOTAVAIL; - goto done; + case SIOCSIFADDR: /* struct ifreq */ + VERIFY(ia != NULL); + bcopy(&ifr->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 (error == 0) { + (void) ifnet_notify_address(ifp, AF_INET); } break; - case SIOCSIFBRDADDR: /* struct ifreq */ - if ((so->so_state & SS_PRIV) == 0) { - error = EPERM; - goto done; + case SIOCAIFADDR: { /* struct {if,in_}aliasreq */ + struct in_aliasreq *ifra = (struct in_aliasreq *)ifr; + struct sockaddr_in broadaddr, mask; + int hostIsNew, maskIsNew; + + VERIFY(ia != NULL); + bcopy(&ifra->ifra_addr, &addr, sizeof (addr)); + bcopy(&ifra->ifra_broadaddr, &broadaddr, sizeof (broadaddr)); + bcopy(&ifra->ifra_mask, &mask, sizeof (mask)); + + maskIsNew = 0; + hostIsNew = 1; + error = 0; + + IFA_LOCK(&ia->ia_ifa); + if (ia->ia_addr.sin_family == AF_INET) { + if (addr.sin_len == 0) { + addr = ia->ia_addr; + hostIsNew = 0; + } else if (addr.sin_addr.s_addr == + ia->ia_addr.sin_addr.s_addr) { + hostIsNew = 0; + } } - /* FALLTHROUGH */ - case SIOCGIFADDR: /* struct ifreq */ - case SIOCGIFNETMASK: /* struct ifreq */ - case SIOCGIFDSTADDR: /* struct ifreq */ - case SIOCGIFBRDADDR: /* struct ifreq */ - if (ia == NULL) { - error = EADDRNOTAVAIL; - goto done; + if (mask.sin_len) { + IFA_UNLOCK(&ia->ia_ifa); + in_ifscrub(ifp, ia, 0); + IFA_LOCK(&ia->ia_ifa); + ia->ia_sockmask = mask; + ia->ia_subnetmask = + ntohl(ia->ia_sockmask.sin_addr.s_addr); + maskIsNew = 1; } - break; - } + if ((ifp->if_flags & IFF_POINTOPOINT) && + (broadaddr.sin_family == AF_INET)) { + IFA_UNLOCK(&ia->ia_ifa); + in_ifscrub(ifp, ia, 0); + 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 (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 (error == 0) { + (void) ifnet_notify_address(ifp, AF_INET); + } + IFA_LOCK(&ia->ia_ifa); + if ((ifp->if_flags & IFF_BROADCAST) && + (broadaddr.sin_family == AF_INET)) + ia->ia_broadaddr = broadaddr; - switch (cmd) { - case SIOCAUTOADDR: { /* struct ifreq */ - int intval; + /* + * 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; - VERIFY(ifp != NULL); - bcopy(&((struct ifreq *)(void *)data)->ifr_intval, - &intval, sizeof (intval)); + if (hostIsNew) + ev_msg.event_code = KEV_INET_NEW_ADDR; + else + ev_msg.event_code = KEV_INET_CHANGED_ADDR; - ifnet_lock_exclusive(ifp); - 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; + if (ia->ia_ifa.ifa_dstaddr) { + in_event_data.ia_dstaddr = + ((struct sockaddr_in *)(void *)ia-> + ia_ifa.ifa_dstaddr)->sin_addr; } else { - ifp->if_eflags |= IFEF_AUTOCONFIGURING; + 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) strlcpy(&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); } - if (!intval) - ifp->if_eflags &= ~IFEF_AUTOCONFIGURING; - ifnet_lock_done(ifp); break; } - case SIOCARPIPLL: { /* struct ifreq */ - int intval; + case SIOCDIFADDR: /* struct ifreq */ + VERIFY(ia != NULL); + error = ifnet_ioctl(ifp, PF_INET, SIOCDIFADDR, ia); + if (error == EOPNOTSUPP) + error = 0; + 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; + + 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; + IFA_UNLOCK(&ia->ia_ifa); + (void) strlcpy(&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 = (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; - VERIFY(ifp != NULL); - bcopy(&((struct ifreq *)(void *)data)->ifr_intval, - &intval, sizeof (intval)); - ipv4_ll_arp_aware = 1; + 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, 0); ifnet_lock_exclusive(ifp); - if (intval) { + IFA_LOCK(ifa); + /* if_detach_ifa() releases ifa_link reference */ + if_detach_ifa(ifp, ifa); + /* Our reference to this address is dropped at the bottom */ + IFA_UNLOCK(ifa); + + /* invalidate route caches */ + routegenid_inet_update(); + + /* + * 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) || + 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; + } + IFA_UNLOCK(ifa); + } + ifnet_lock_done(ifp); + + 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); + } + + /* 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) { /* - * 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. + * 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. */ - if (ifp->if_eflags & IFEF_IPV4_ROUTER) { - intval = 0; /* be safe; clear flag if set */ - error = EBUSY; - } else { - ifp->if_eflags |= IFEF_ARPLL; - } + error = ifnet_ioctl(ifp, PF_INET, SIOCSIFADDR, ifa); + if (error == EOPNOTSUPP) + error = 0; + + /* Release reference from ifa_ifpgetprimary() */ + IFA_REMREF(ifa); } - if (!intval) - ifp->if_eflags &= ~IFEF_ARPLL; - ifnet_lock_done(ifp); + (void) ifnet_notify_address(ifp, AF_INET); break; + + default: + VERIFY(0); + /* NOTREACHED */ } - 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; + return (error); +} - 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; +/* + * Caller passes in the ioctl data pointer directly via "ifr", with the + * expectation that this routine always uses bcopy() or other byte-aligned + * memory accesses. + */ +static __attribute__((noinline)) int +inctl_ifdstaddr(struct ifnet *ifp, struct in_ifaddr *ia, u_long cmd, + struct ifreq *ifr) +{ + struct kev_in_data in_event_data; + struct kev_msg ev_msg; + struct sockaddr_in dstaddr; + int error = 0; + VERIFY(ifp != NULL); + + if (!(ifp->if_flags & IFF_POINTOPOINT)) + return (EINVAL); + + bzero(&in_event_data, sizeof (struct kev_in_data)); + bzero(&ev_msg, sizeof (struct kev_msg)); + + switch (cmd) { case SIOCGIFDSTADDR: /* struct ifreq */ - VERIFY(ia != NULL); - if ((ifp->if_flags & IFF_POINTOPOINT) == 0) { - error = EINVAL; + if (ia == NULL) { + error = EADDRNOTAVAIL; 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 ifreq */ - VERIFY(ia != NULL); - IFA_LOCK(&ia->ia_ifa); - bcopy(&ia->ia_sockmask, - &((struct ifreq *)(void *)data)->ifr_addr, - sizeof (struct sockaddr_in)); + bcopy(&ia->ia_dstaddr, &ifr->ifr_dstaddr, sizeof (dstaddr)); IFA_UNLOCK(&ia->ia_ifa); break; case SIOCSIFDSTADDR: /* struct ifreq */ - VERIFY(ifp != NULL && ia != NULL); - if ((ifp->if_flags & IFF_POINTOPOINT) == 0) { - error = EINVAL; - break; - } + VERIFY(ia != NULL); IFA_LOCK(&ia->ia_ifa); - oldaddr = ia->ia_dstaddr; - bcopy(&((struct ifreq *)(void *)data)->ifr_dstaddr, - &ia->ia_dstaddr, sizeof (struct sockaddr_in)); + dstaddr = ia->ia_dstaddr; + bcopy(&ifr->ifr_dstaddr, &ia->ia_dstaddr, sizeof (dstaddr)); if (ia->ia_dstaddr.sin_family == AF_INET) ia->ia_dstaddr.sin_len = sizeof (struct sockaddr_in); IFA_UNLOCK(&ia->ia_ifa); @@ -739,21 +908,20 @@ in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, */ error = ifnet_ioctl(ifp, PF_INET, SIOCSIFDSTADDR, ia); IFA_LOCK(&ia->ia_ifa); - if (error == EOPNOTSUPP) { + if (error == EOPNOTSUPP) error = 0; - } - if (error) { - ia->ia_dstaddr = oldaddr; + if (error != 0) { + ia->ia_dstaddr = dstaddr; 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.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; + ev_msg.event_code = KEV_INET_SIFDSTADDR; if (ia->ia_ifa.ifa_dstaddr) { in_event_data.ia_dstaddr = ((struct sockaddr_in *) @@ -762,17 +930,17 @@ in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, 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; + 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], + (void) strlcpy(&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 = (u_int32_t) 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); @@ -783,7 +951,7 @@ in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, lck_mtx_lock(rnh_lock); IFA_LOCK(&ia->ia_ifa); if (ia->ia_flags & IFA_ROUTE) { - ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&oldaddr; + ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&dstaddr; IFA_UNLOCK(&ia->ia_ifa); rtinit_locked(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST); IFA_LOCK(&ia->ia_ifa); @@ -798,645 +966,514 @@ in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, lck_mtx_unlock(rnh_lock); break; - 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 *) - (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 = (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; - - 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 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) { - IFA_LOCK(ifa); - if (ifa->ifa_addr->sa_family == AF_INET) { - IFA_UNLOCK(ifa); - break; - } - IFA_UNLOCK(ifa); - } - ifnet_lock_done(ifp); - if (ifa != NULL) { - error = EBUSY; - break; - } - - error = proto_unplumb(PF_INET, ifp); - break; - - 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; + default: + VERIFY(0); + /* NOTREACHED */ } - 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 *) - (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 = (u_int32_t) ifp->if_unit; + return (error); +} - 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; +/* + * Caller passes in the ioctl data pointer directly via "ifr", with the + * expectation that this routine always uses bcopy() or other byte-aligned + * memory accesses. + */ +static __attribute__((noinline)) int +inctl_ifbrdaddr(struct ifnet *ifp, struct in_ifaddr *ia, u_long cmd, + struct ifreq *ifr) +{ + struct kev_in_data in_event_data; + struct kev_msg ev_msg; + int error = 0; - kev_post_msg(&ev_msg); - break; - } + VERIFY(ifp != NULL); - case SIOCAIFADDR: { /* struct ifaliasreq */ - struct sockaddr_in addr, broadaddr, mask; + if (ia == NULL) + return (EADDRNOTAVAIL); - 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)); + if (!(ifp->if_flags & IFF_BROADCAST)) + return (EINVAL); - maskIsNew = 0; - hostIsNew = 1; - error = 0; + bzero(&in_event_data, sizeof (struct kev_in_data)); + bzero(&ev_msg, sizeof (struct kev_msg)); + switch (cmd) { + case SIOCGIFBRDADDR: /* struct ifreq */ IFA_LOCK(&ia->ia_ifa); - if (ia->ia_addr.sin_family == AF_INET) { - if (addr.sin_len == 0) { - addr = ia->ia_addr; - hostIsNew = 0; - } else if (addr.sin_addr.s_addr == - ia->ia_addr.sin_addr.s_addr) { - hostIsNew = 0; - } - } - if (mask.sin_len) { - IFA_UNLOCK(&ia->ia_ifa); - in_ifscrub(ifp, ia, 0); - 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) && - (broadaddr.sin_family == AF_INET)) { - IFA_UNLOCK(&ia->ia_ifa); - in_ifscrub(ifp, ia, 0); - 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 (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) && - (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 *)(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); - } + bcopy(&ia->ia_broadaddr, &ifr->ifr_broadaddr, + sizeof (struct sockaddr_in)); + IFA_UNLOCK(&ia->ia_ifa); break; - } - case SIOCDIFADDR: /* struct ifreq */ - VERIFY(ifp != NULL && ia != NULL); - error = ifnet_ioctl(ifp, PF_INET, SIOCDIFADDR, ia); - if (error == EOPNOTSUPP) - error = 0; - if (error != 0) { - break; - } + case SIOCSIFBRDADDR: /* struct ifreq */ + IFA_LOCK(&ia->ia_ifa); + bcopy(&ifr->ifr_broadaddr, &ia->ia_broadaddr, + sizeof (struct sockaddr_in)); - /* 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.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; + ev_msg.event_code = KEV_INET_SIFBRDADDR; - 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; + 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; + 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], + (void) strlcpy(&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 = (u_int32_t) 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; - 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, 0); - ifnet_lock_exclusive(ifp); - IFA_LOCK(ifa); - /* if_detach_ifa() releases ifa_link reference */ - if_detach_ifa(ifp, ifa); - /* 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 ) { + kev_post_msg(&ev_msg); + break; - 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); + default: + VERIFY(0); + /* NOTREACHED */ + } - 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; + return (error); +} - in_delmulti(inm); - /* release the reference for allhostsinm */ - INM_REMREF(inm); - } - lck_mtx_unlock(&ifp->if_addrconfig_lock); - } else { - ifnet_lock_done(ifp); - } +/* + * Caller passes in the ioctl data pointer directly via "ifr", with the + * expectation that this routine always uses bcopy() or other byte-aligned + * memory accesses. + */ +static __attribute__((noinline)) int +inctl_ifnetmask(struct ifnet *ifp, struct in_ifaddr *ia, u_long cmd, + struct ifreq *ifr) +{ + struct kev_in_data in_event_data; + struct kev_msg ev_msg; + struct sockaddr_in mask; + int error = 0; - /* Post the kernel event */ - kev_post_msg(&ev_msg); + VERIFY(ifp != NULL); - /* - * 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; + bzero(&in_event_data, sizeof (struct kev_in_data)); + bzero(&ev_msg, sizeof (struct kev_msg)); - /* Release reference from ifa_ifpgetprimary() */ - IFA_REMREF(ifa); + switch (cmd) { + case SIOCGIFNETMASK: /* struct ifreq */ + if (ia == NULL) { + error = EADDRNOTAVAIL; + break; } -#if PF - (void) pf_ifaddr_hook(ifp, cmd); -#endif /* PF */ + IFA_LOCK(&ia->ia_ifa); + bcopy(&ia->ia_sockmask, &ifr->ifr_addr, sizeof (mask)); + IFA_UNLOCK(&ia->ia_ifa); break; -#ifdef __APPLE__ - 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; + case SIOCSIFNETMASK: { /* struct ifreq */ + in_addr_t i; - bcopy(data, &cloned_fd, sizeof (cloned_fd)); + bcopy(&ifr->ifr_addr, &mask, sizeof (mask)); + i = mask.sin_addr.s_addr; - inp = sotoinpcb(so); - if (inp == NULL) { - break; - } + VERIFY(ia != NULL); + 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; - /* 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); + 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 { - cloned_inp = NULL; + 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) strlcpy(&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 = (u_int32_t)ifp->if_unit; - 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; + 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; - /* Multicast options */ - if (cloned_inp->inp_moptions != NULL) - error2 = imo_clone(cloned_inp, inp); - } + kev_post_msg(&ev_msg); break; } -#endif /* __APPLE__ */ default: - error = EOPNOTSUPP; - } - done: - if (ia != NULL) { - IFA_REMREF(&ia->ia_ifa); + VERIFY(0); + /* NOTREACHED */ } + return (error); } /* - * SIOC[GAD]LIFADDR. - * SIOCGLIFADDR: get first address. (?!?) - * SIOCGLIFADDR with IFLR_PREFIX: - * get first address that matches the specified prefix. - * SIOCALIFADDR: add the specified address. - * SIOCALIFADDR with IFLR_PREFIX: - * EINVAL since we can't deduce hostid part of the address. - * SIOCDLIFADDR: delete the specified address. - * SIOCDLIFADDR with IFLR_PREFIX: - * delete the first address that matches the specified prefix. - * return values: - * EINVAL on invalid parameters - * EADDRNOTAVAIL on prefix match failed/specified address not found - * other values may be returned from in_ioctl() + * Generic INET control operations (ioctl's). + * + * ifp is NULL if not an interface-specific ioctl. + * + * Most of the routines called to handle the ioctls would end up being + * tail-call optimized, which unfortunately causes this routine to + * consume too much stack space; this is the reason for the "noinline" + * attribute used on those routines. + * + * If called directly from within the networking stack (as opposed to via + * pru_control), the socket parameter may be NULL. */ -static int -in_lifaddr_ioctl(struct socket *so, u_long cmd, struct if_laddrreq *iflr, - struct ifnet *ifp, struct proc *p) +int +in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, + struct proc *p) { + struct ifreq *ifr = (struct ifreq *)(void *)data; + struct sockaddr_in addr, dstaddr; + struct sockaddr_in sin, *sa = NULL; + boolean_t privileged = (proc_suser(p) == 0); + boolean_t so_unlocked = FALSE; + struct in_ifaddr *ia = NULL; struct ifaddr *ifa; + int error = 0; - VERIFY(ifp != NULL); + /* In case it's NULL, make sure it came from the kernel */ + VERIFY(so != NULL || p == kernproc); + /* + * ioctls which don't require ifp, but require socket. + */ switch (cmd) { - case SIOCGLIFADDR: - /* address must be specified on GET with IFLR_PREFIX */ - if ((iflr->flags & IFLR_PREFIX) == 0) - break; - /*FALLTHROUGH*/ - case SIOCALIFADDR: - case SIOCDLIFADDR: - /* address must be specified on ADD and DELETE */ - if (iflr->addr.ss_family != AF_INET) - return EINVAL; - if (iflr->addr.ss_len != sizeof(struct sockaddr_in)) - return EINVAL; - /* XXX need improvement */ - if (iflr->dstaddr.ss_family - && iflr->dstaddr.ss_family != AF_INET) - return EINVAL; - if (iflr->dstaddr.ss_family - && iflr->dstaddr.ss_len != sizeof(struct sockaddr_in)) - return EINVAL; - break; - default: /*shouldn't happen*/ - return EOPNOTSUPP; + case SIOCGASSOCIDS32: /* struct so_aidreq32 */ + case SIOCGASSOCIDS64: /* struct so_aidreq64 */ + return (inctl_associd(so, cmd, data)); + /* NOTREACHED */ + + case SIOCGCONNIDS32: /* struct so_cidreq32 */ + case SIOCGCONNIDS64: /* struct so_cidreq64 */ + return (inctl_connid(so, cmd, data)); + /* NOTREACHED */ + + case SIOCGCONNINFO32: /* struct so_cinforeq32 */ + case SIOCGCONNINFO64: /* struct so_cinforeq64 */ + return (inctl_conninfo(so, cmd, data)); + /* NOTREACHED */ + } + + /* + * The rest of ioctls require ifp; reject if we don't have one; + * return ENXIO to be consistent with ifioctl(). + */ + if (ifp == NULL) + return (ENXIO); + + /* + * ioctls which require ifp but not interface address. + */ + switch (cmd) { + case SIOCAUTOADDR: /* struct ifreq */ + if (!privileged) + return (EPERM); + return (inctl_autoaddr(ifp, ifr)); + /* NOTREACHED */ + + case SIOCARPIPLL: /* struct ifreq */ + if (!privileged) + return (EPERM); + return (inctl_arpipll(ifp, ifr)); + /* NOTREACHED */ + + case SIOCSETROUTERMODE: /* struct ifreq */ + if (!privileged) + return (EPERM); + return (inctl_setrouter(ifp, ifr)); + /* NOTREACHED */ + + case SIOCPROTOATTACH: /* struct ifreq */ + if (!privileged) + return (EPERM); + return (in_domifattach(ifp)); + /* NOTREACHED */ + + case SIOCPROTODETACH: /* struct ifreq */ + if (!privileged) + return (EPERM); + + /* + * If an IPv4 address is still present, refuse to detach. + */ + ifnet_lock_shared(ifp); + 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); + return ((ifa == NULL) ? proto_unplumb(PF_INET, ifp) : EBUSY); + /* NOTREACHED */ } - if (sizeof(struct in_addr) * 8 < iflr->prefixlen) - return EINVAL; + /* + * ioctls which require interface address; obtain sockaddr_in. + */ switch (cmd) { - case SIOCALIFADDR: - { - struct in_aliasreq ifra; + case SIOCAIFADDR: /* struct {if,in_}aliasreq */ + if (!privileged) + return (EPERM); + bcopy(&((struct in_aliasreq *)(void *)data)->ifra_addr, + &sin, sizeof (sin)); + sa = &sin; + break; + + case SIOCDIFADDR: /* struct ifreq */ + case SIOCSIFADDR: /* struct ifreq */ + case SIOCSIFDSTADDR: /* struct ifreq */ + case SIOCSIFNETMASK: /* struct ifreq */ + case SIOCSIFBRDADDR: /* struct ifreq */ + if (!privileged) + return (EPERM); + /* FALLTHRU */ + case SIOCGIFADDR: /* struct ifreq */ + case SIOCGIFDSTADDR: /* struct ifreq */ + case SIOCGIFNETMASK: /* struct ifreq */ + case SIOCGIFBRDADDR: /* struct ifreq */ + bcopy(&ifr->ifr_addr, &sin, sizeof (sin)); + sa = &sin; + break; + } + + /* + * Find address for this interface, if it exists. + * + * If an alias address was specified, find that one instead of + * the first one on the interface, if possible. + */ + VERIFY(ia == NULL); + if (sa != NULL) { + struct in_ifaddr *iap; - if (iflr->flags & IFLR_PREFIX) - return EINVAL; + /* + * Any failures from this point on must take into account + * a non-NULL "ia" with an outstanding reference count, and + * therefore requires IFA_REMREF. Jump to "done" label + * instead of calling return if "ia" is valid. + */ + lck_rw_lock_shared(in_ifaddr_rwlock); + TAILQ_FOREACH(iap, INADDR_HASH(sa->sin_addr.s_addr), ia_hash) { + IFA_LOCK(&iap->ia_ifa); + if (iap->ia_ifp == ifp && + iap->ia_addr.sin_addr.s_addr == + sa->sin_addr.s_addr) { + ia = iap; + IFA_UNLOCK(&iap->ia_ifa); + break; + } + 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); - /* copy args to in_aliasreq, perform ioctl(SIOCAIFADDR_IN6). */ - bzero(&ifra, sizeof(ifra)); - bcopy(iflr->iflr_name, ifra.ifra_name, - sizeof(ifra.ifra_name)); + if (ia == NULL) { + ifnet_lock_shared(ifp); + TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { + iap = ifatoia(ifa); + IFA_LOCK(&iap->ia_ifa); + if (iap->ia_addr.sin_family == AF_INET) { + ia = iap; + IFA_UNLOCK(&iap->ia_ifa); + break; + } + IFA_UNLOCK(&iap->ia_ifa); + } + /* take a reference on ia before releasing lock */ + if (ia != NULL) + IFA_ADDREF(&ia->ia_ifa); + ifnet_lock_done(ifp); + } + } - bcopy(&iflr->addr, &ifra.ifra_addr, iflr->addr.ss_len); + /* + * Unlock the socket since ifnet_ioctl() may be invoked by + * one of the ioctl handlers below. Socket will be re-locked + * prior to returning. + */ + if (so != NULL) { + socket_unlock(so, 0); + so_unlocked = TRUE; + } - if (iflr->dstaddr.ss_family) { /*XXX*/ - bcopy(&iflr->dstaddr, &ifra.ifra_dstaddr, - iflr->dstaddr.ss_len); + switch (cmd) { + case SIOCAIFADDR: /* struct {if,in_}aliasreq */ + case SIOCDIFADDR: /* struct ifreq */ + 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)); } - ifra.ifra_mask.sin_family = AF_INET; - ifra.ifra_mask.sin_len = sizeof(struct sockaddr_in); - in_len2mask(&ifra.ifra_mask.sin_addr, iflr->prefixlen); - - return in_control(so, SIOCAIFADDR, (caddr_t)&ifra, ifp, p); - } - case SIOCGLIFADDR: - case SIOCDLIFADDR: - { - struct in_ifaddr *ia; - struct in_addr mask, candidate; - struct in_addr match = { 0 }; - struct sockaddr_in *sin; - int cmp; - - bzero(&mask, sizeof(mask)); - if (iflr->flags & IFLR_PREFIX) { - /* lookup a prefix rather than address. */ - in_len2mask(&mask, iflr->prefixlen); - - sin = (struct sockaddr_in *)&iflr->addr; - match.s_addr = sin->sin_addr.s_addr; - match.s_addr &= mask.s_addr; - - /* if you set extra bits, that's wrong */ - if (match.s_addr != sin->sin_addr.s_addr) - return EINVAL; - - cmp = 1; - } else { - if (cmd == SIOCGLIFADDR) { - /* on getting an address, take the 1st match */ - cmp = 0; /*XXX*/ - } else { - /* on deleting an address, do exact match */ - in_len2mask(&mask, 32); - sin = (struct sockaddr_in *)&iflr->addr; - match.s_addr = sin->sin_addr.s_addr; - - cmp = 1; - } - } + if (addr.sin_family == AF_INET) { + struct in_ifaddr *oia; - ifnet_lock_shared(ifp); - TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { - IFA_LOCK(ifa); - if (ifa->ifa_addr->sa_family != AF_INET6) { - IFA_UNLOCK(ifa); - continue; + 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 == + addr.sin_addr.s_addr) { + IFA_ADDREF_LOCKED(&ia->ia_ifa); + IFA_UNLOCK(&ia->ia_ifa); + break; + } + IFA_UNLOCK(&ia->ia_ifa); } - if (!cmp) { - IFA_UNLOCK(ifa); - break; + 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; } - 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; + } else if (cmd == SIOCAIFADDR) { + error = EINVAL; + goto done; } - 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); - - if ((ifp->if_flags & IFF_POINTOPOINT) != 0) { - bcopy(&ia->ia_dstaddr, &iflr->dstaddr, - ia->ia_dstaddr.sin_len); - } else - bzero(&iflr->dstaddr, sizeof(iflr->dstaddr)); - - iflr->prefixlen = - in_mask2len(&ia->ia_sockmask.sin_addr); - - iflr->flags = 0; /*XXX*/ - - IFA_UNLOCK(ifa); - IFA_REMREF(ifa); - return 0; + if (cmd == SIOCDIFADDR && ia == NULL) { + error = EADDRNOTAVAIL; + goto done; + } + /* FALLTHROUGH */ + case SIOCSIFADDR: /* struct ifreq */ + case SIOCSIFDSTADDR: /* struct ifreq */ + case SIOCSIFNETMASK: /* struct ifreq */ + if (cmd == SIOCAIFADDR) { + /* fell thru from above; just repeat it */ + bcopy(&((struct in_aliasreq *)(void *)data)-> + ifra_addr, &addr, sizeof (addr)); } else { - struct in_aliasreq ifra; - - /* fill in_aliasreq and do ioctl(SIOCDIFADDR_IN6) */ - bzero(&ifra, sizeof(ifra)); - bcopy(iflr->iflr_name, ifra.ifra_name, - sizeof(ifra.ifra_name)); + VERIFY(cmd == SIOCDIFADDR || cmd == SIOCSIFADDR || + cmd == SIOCSIFNETMASK || cmd == SIOCSIFDSTADDR); + bcopy(&((struct ifreq *)(void *)data)->ifr_addr, + &addr, sizeof (addr)); + } + 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); - bcopy(&ia->ia_addr, &ifra.ifra_addr, - ia->ia_addr.sin_len); - if ((ifp->if_flags & IFF_POINTOPOINT) != 0) { - bcopy(&ia->ia_dstaddr, &ifra.ifra_dstaddr, - ia->ia_dstaddr.sin_len); + /* 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; + if (ifp->if_flags & IFF_BROADCAST) { + ia->ia_broadaddr.sin_len = sizeof (ia->ia_addr); + ia->ia_broadaddr.sin_family = AF_INET; } - bcopy(&ia->ia_sockmask, &ifra.ifra_dstaddr, - ia->ia_sockmask.sin_len); + 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); - IFA_REMREF(ifa); - return in_control(so, SIOCDIFADDR, (caddr_t)&ifra, - ifp, p); + ifnet_lock_done(ifp); + 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_rw_done(in_ifaddr_rwlock); + /* discard error */ + (void) in_domifattach(ifp); + error = 0; } - } + break; } - return EOPNOTSUPP; /*just for safety*/ -} + switch (cmd) { + case SIOCGIFDSTADDR: /* struct ifreq */ + case SIOCSIFDSTADDR: /* struct ifreq */ + error = inctl_ifdstaddr(ifp, ia, cmd, ifr); + break; -/* - * 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); + case SIOCGIFBRDADDR: /* struct ifreq */ + case SIOCSIFBRDADDR: /* struct ifreq */ + error = inctl_ifbrdaddr(ifp, ia, cmd, ifr); + break; - 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); + case SIOCGIFNETMASK: /* struct ifreq */ + case SIOCSIFNETMASK: /* struct ifreq */ + error = inctl_ifnetmask(ifp, ia, cmd, ifr); + break; - /* purge all IPv4 addresses configured on this interface */ - in_purgeaddrs(ifp); + case SIOCGIFADDR: /* struct ifreq */ + case SIOCSIFADDR: /* struct ifreq */ + case SIOCAIFADDR: /* struct {if,in_}aliasreq */ + case SIOCDIFADDR: /* struct ifreq */ + error = inctl_ifaddr(ifp, ia, cmd, ifr); + break; - return (0); + default: + error = EOPNOTSUPP; + break; + } +done: + if (ia != NULL) + IFA_REMREF(&ia->ia_ifa); + if (so_unlocked) + socket_lock(so, 0); + + return (error); } /* @@ -1470,7 +1507,7 @@ in_ifscrub(struct ifnet *ifp, struct in_ifaddr *ia, int locked) static void in_iahash_remove(struct in_ifaddr *ia) { - lck_rw_assert(in_ifaddr_rwlock, LCK_RW_ASSERT_EXCLUSIVE); + lck_rw_assert(in_ifaddr_rwlock, LCK_RW_ASSERT_EXCLUSIVE); IFA_LOCK_ASSERT_HELD(&ia->ia_ifa); if (!IA_IS_HASHED(ia)) { @@ -1492,7 +1529,7 @@ in_iahash_remove(struct in_ifaddr *ia) static void in_iahash_insert(struct in_ifaddr *ia) { - lck_rw_assert(in_ifaddr_rwlock, LCK_RW_ASSERT_EXCLUSIVE); + 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) { @@ -1502,15 +1539,15 @@ in_iahash_insert(struct in_ifaddr *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); + 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 + * 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) * @@ -1522,7 +1559,7 @@ 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); + 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) { @@ -1562,11 +1599,8 @@ in_iahash_insert_ptp(struct in_ifaddr *ia) * and routing table entry. */ static int -in_ifinit( - struct ifnet *ifp, - struct in_ifaddr *ia, - struct sockaddr_in *sin, - int scrub) +in_ifinit(struct ifnet *ifp, struct in_ifaddr *ia, struct sockaddr_in *sin, + int scrub) { u_int32_t i = ntohl(sin->sin_addr.s_addr); struct sockaddr_in oldaddr; @@ -1586,7 +1620,13 @@ in_ifinit( in_iahash_remove(ia); } ia->ia_addr = *sin; - ia->ia_addr.sin_len = sizeof (*sin); + /* + * Interface addresses should not contain port or sin_zero information. + */ + SIN(&ia->ia_addr)->sin_family = AF_INET; + SIN(&ia->ia_addr)->sin_len = sizeof (struct sockaddr_in); + SIN(&ia->ia_addr)->sin_port = 0; + bzero(&SIN(&ia->ia_addr)->sin_zero, sizeof (sin->sin_zero)); if ((ifp->if_flags & IFF_POINTOPOINT)) in_iahash_insert_ptp(ia); else @@ -1686,9 +1726,9 @@ in_ifinit( ia->ia_ifa.ifa_metric = ifp->if_metric; if (ifp->if_flags & IFF_BROADCAST) { ia->ia_broadaddr.sin_addr.s_addr = - htonl(ia->ia_subnet | ~ia->ia_subnetmask); + htonl(ia->ia_subnet | ~ia->ia_subnetmask); ia->ia_netbroadcast.s_addr = - htonl(ia->ia_net | ~ ia->ia_netmask); + htonl(ia->ia_net | ~ ia->ia_netmask); } else if (ifp->if_flags & IFF_LOOPBACK) { ia->ia_ifa.ifa_dstaddr = ia->ia_ifa.ifa_addr; flags |= RTF_HOST; @@ -1700,10 +1740,11 @@ in_ifinit( IFA_REMREF(&ia->ia_ifa); return (0); } - ia->ia_dstaddr.sin_len = sizeof (*sin); + ia->ia_dstaddr.sin_len = sizeof (struct sockaddr_in); flags |= RTF_HOST; } 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; @@ -1729,13 +1770,16 @@ in_ifinit( inm = in_addmulti(&addr, ifp); if (inm != NULL) { - /* keep the reference on inm added by - * in_addmulti above for storing the - * pointer in allhostsinm + /* + * 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); + printf("%s: failed to add membership to " + "all-hosts multicast address on %s\n", + __func__, if_name(ifp)); } } lck_mtx_unlock(&ifp->if_addrconfig_lock); @@ -1743,29 +1787,35 @@ in_ifinit( /* Release extra reference taken above */ IFA_REMREF(&ia->ia_ifa); + + if (error == 0) { + /* invalidate route caches */ + routegenid_inet_update(); + } + return (error); } - /* - * Return 1 if the address might be a local broadcast address. + * Return TRUE if the address might be a local broadcast address. */ -int +boolean_t in_broadcast(struct in_addr in, struct ifnet *ifp) { struct ifaddr *ifa; u_int32_t t; if (in.s_addr == INADDR_BROADCAST || in.s_addr == INADDR_ANY) - return (1); - if ((ifp->if_flags & IFF_BROADCAST) == 0) - return (0); + return (TRUE); + if (!(ifp->if_flags & IFF_BROADCAST)) + return (FALSE); t = ntohl(in.s_addr); + /* * Look through the list of addresses for a match * with a broadcast address. */ -#define ia ((struct in_ifaddr *)ifa) +#define ia ((struct in_ifaddr *)ifa) ifnet_lock_shared(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { IFA_LOCK(ifa); @@ -1784,12 +1834,12 @@ in_broadcast(struct in_addr in, struct ifnet *ifp) ia->ia_subnetmask != (u_int32_t)0xffffffff) { IFA_UNLOCK(ifa); ifnet_lock_done(ifp); - return (1); + return (TRUE); } IFA_UNLOCK(ifa); } ifnet_lock_done(ifp); - return (0); + return (FALSE); #undef ia } @@ -1799,6 +1849,8 @@ in_purgeaddrs(struct ifnet *ifp) struct ifaddr **ifap; int err, i; + VERIFY(ifp != NULL); + /* * 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 @@ -1806,18 +1858,21 @@ in_purgeaddrs(struct ifnet *ifp) * removed from the global list and arrays. */ err = ifnet_get_address_list_family_internal(ifp, &ifap, AF_INET, 1, - M_WAITOK); + M_WAITOK, 0); if (err == 0 && ifap != NULL) { + struct ifreq ifr; + + bzero(&ifr, sizeof (ifr)); + (void) snprintf(ifr.ifr_name, sizeof (ifr.ifr_name), + "%s", if_name(ifp)); + 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; + bcopy(ifa->ifa_addr, &ifr.ifr_addr, + sizeof (struct sockaddr_in)); IFA_UNLOCK(ifa); err = in_control(NULL, SIOCDIFADDR, (caddr_t)&ifr, ifp, kernproc); @@ -1840,50 +1895,130 @@ in_purgeaddrs(struct ifnet *ifp) 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); + printf("%s: SIOCDIFADDR ifp=%s ifa_addr=%s " + "ifa_dstaddr=%s (err=%d)\n", __func__, + ifp->if_xname, 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); + "ifp=%s (err=%d)\n", __func__, ifp->if_xname, err); } } -int inet_aton(char *cp, struct in_addr *pin); +/* + * Select endpoint address(es). For now just take the first matching + * address and discard the rest, if present. + */ int -inet_aton(char * cp, struct in_addr * pin) +in_selectaddrs(int af, struct sockaddr_list **src_sl, + struct sockaddr_entry **src_se, struct sockaddr_list **dst_sl, + struct sockaddr_entry **dst_se) { - 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); -} + struct sockaddr_entry *se = NULL; + struct sockaddr_entry *tse = NULL; + int error = 0; -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; + VERIFY(src_sl != NULL && dst_sl != NULL && *dst_sl != NULL); + VERIFY(src_se != NULL && dst_se != NULL); + + *src_se = *dst_se = NULL; + + /* pick a source address, if available */ + if (*src_sl != NULL) { + TAILQ_FOREACH(se, &(*src_sl)->sl_head, se_link) { + VERIFY(se->se_addr != NULL); + /* + * Take the first source address, or the first + * one with matching address family. + */ + if (af == AF_UNSPEC || se->se_addr->sa_family == af) { + sockaddrlist_remove(*src_sl, se); + *src_se = se; + break; + } + } + /* get rid of the rest */ + TAILQ_FOREACH_SAFE(se, &(*src_sl)->sl_head, se_link, tse) { + sockaddrlist_remove(*src_sl, se); + sockaddrentry_free(se); + } + if (*src_se != NULL) { + /* insert the first src address back in */ + sockaddrlist_insert(*src_sl, *src_se); + VERIFY((*src_sl)->sl_cnt == 1); + /* destination address must be of this family */ + af = (*src_se)->se_addr->sa_family; + } else { + /* no usable source address with matching family */ + VERIFY(af != AF_UNSPEC); + error = EAFNOSUPPORT; + goto out; + } + } + /* pick a (matching) destination address */ + TAILQ_FOREACH(se, &(*dst_sl)->sl_head, se_link) { + VERIFY(se->se_addr != NULL); + /* + * Take the first destination address; if source is specified, + * find one which uses the same address family. + */ + if (af == AF_UNSPEC || se->se_addr->sa_family == af) { + sockaddrlist_remove(*dst_sl, se); + *dst_se = se; + break; + } + } + /* get rid of the rest */ + TAILQ_FOREACH_SAFE(se, &(*dst_sl)->sl_head, se_link, tse) { + sockaddrlist_remove(*dst_sl, se); + sockaddrentry_free(se); + } + if (*dst_se != NULL) { + /* insert the first dst address back in */ + sockaddrlist_insert(*dst_sl, *dst_se); + VERIFY((*dst_sl)->sl_cnt == 1); + } else { + /* source and destination address families don't match */ + error = EAFNOSUPPORT; + goto out; + } + + af = (*dst_se)->se_addr->sa_family; + VERIFY(*src_se == NULL || (*src_se)->se_addr->sa_family == af); - /* 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); + /* verify address length */ + switch (af) { + case AF_INET: + if ((*dst_se)->se_addr->sa_len != + sizeof (struct sockaddr_in)) { + error = EAFNOSUPPORT; + goto out; + } + break; +#if INET6 + case AF_INET6: + if ((*dst_se)->se_addr->sa_len != + sizeof (struct sockaddr_in6)) { + error = EAFNOSUPPORT; + goto out; + } + break; +#endif /* INET6 */ + default: + error = EAFNOSUPPORT; + goto out; + } - return ret < len ? TRUE : FALSE; + /* if source address is specified, length must match destination */ + if (*src_se != NULL && (*src_se)->se_addr->sa_len != + (*dst_se)->se_addr->sa_len) { + error = EAFNOSUPPORT; + goto out; + } +out: + return (error); } /* @@ -2036,3 +2171,157 @@ in_ifaddr_trace(struct ifaddr *ifa, int refhold) idx = atomic_add_16_ov(cnt, 1) % INIFA_TRACE_HIST_SIZE; ctrace_record(&tr[idx]); } + +/* + * Handle SIOCGASSOCIDS ioctl for PF_INET domain. + */ +static int +in_getassocids(struct socket *so, uint32_t *cnt, user_addr_t aidp) +{ + struct inpcb *inp = sotoinpcb(so); + sae_associd_t aid; + + if (inp == NULL || inp->inp_state == INPCB_STATE_DEAD) + return (EINVAL); + + /* INPCB has no concept of association */ + aid = SAE_ASSOCID_ANY; + *cnt = 0; + + /* just asking how many there are? */ + if (aidp == USER_ADDR_NULL) + return (0); + + return (copyout(&aid, aidp, sizeof (aid))); +} + +/* + * Handle SIOCGCONNIDS ioctl for PF_INET domain. + */ +static int +in_getconnids(struct socket *so, sae_associd_t aid, uint32_t *cnt, + user_addr_t cidp) +{ + struct inpcb *inp = sotoinpcb(so); + sae_connid_t cid; + + if (inp == NULL || inp->inp_state == INPCB_STATE_DEAD) + return (EINVAL); + + if (aid != SAE_ASSOCID_ANY && aid != SAE_ASSOCID_ALL) + return (EINVAL); + + /* if connected, return 1 connection count */ + *cnt = ((so->so_state & SS_ISCONNECTED) ? 1 : 0); + + /* just asking how many there are? */ + if (cidp == USER_ADDR_NULL) + return (0); + + /* if INPCB is connected, assign it connid 1 */ + cid = ((*cnt != 0) ? 1 : SAE_CONNID_ANY); + + return (copyout(&cid, cidp, sizeof (cid))); +} + +/* + * Handle SIOCGCONNINFO ioctl for PF_INET domain. + */ +static int +in_getconninfo(struct socket *so, sae_connid_t cid, uint32_t *flags, + uint32_t *ifindex, int32_t *soerror, user_addr_t src, socklen_t *src_len, + user_addr_t dst, socklen_t *dst_len, uint32_t *aux_type, + user_addr_t aux_data, uint32_t *aux_len) +{ +#pragma unused(aux_data) + struct inpcb *inp = sotoinpcb(so); + struct sockaddr_in sin; + struct ifnet *ifp = NULL; + int error = 0; + u_int32_t copy_len = 0; + + /* + * Don't test for INPCB_STATE_DEAD since this may be called + * after SOF_PCBCLEARING is set, e.g. after tcp_close(). + */ + if (inp == NULL) { + error = EINVAL; + goto out; + } + + if (cid != SAE_CONNID_ANY && cid != SAE_CONNID_ALL && cid != 1) { + error = EINVAL; + goto out; + } + + ifp = inp->inp_last_outifp; + *ifindex = ((ifp != NULL) ? ifp->if_index : 0); + *soerror = so->so_error; + *flags = 0; + if (so->so_state & SS_ISCONNECTED) + *flags |= (CIF_CONNECTED | CIF_PREFERRED); + if (inp->inp_flags & INP_BOUND_IF) + *flags |= CIF_BOUND_IF; + if (!(inp->inp_flags & INP_INADDR_ANY)) + *flags |= CIF_BOUND_IP; + if (!(inp->inp_flags & INP_ANONPORT)) + *flags |= CIF_BOUND_PORT; + + bzero(&sin, sizeof (sin)); + sin.sin_len = sizeof (sin); + sin.sin_family = AF_INET; + + /* source address and port */ + sin.sin_port = inp->inp_lport; + sin.sin_addr.s_addr = inp->inp_laddr.s_addr; + if (*src_len == 0) { + *src_len = sin.sin_len; + } else { + if (src != USER_ADDR_NULL) { + copy_len = min(*src_len, sizeof (sin)); + error = copyout(&sin, src, copy_len); + if (error != 0) + goto out; + *src_len = copy_len; + } + } + + /* destination address and port */ + sin.sin_port = inp->inp_fport; + sin.sin_addr.s_addr = inp->inp_faddr.s_addr; + if (*dst_len == 0) { + *dst_len = sin.sin_len; + } else { + if (dst != USER_ADDR_NULL) { + copy_len = min(*dst_len, sizeof (sin)); + error = copyout(&sin, dst, copy_len); + if (error != 0) + goto out; + *dst_len = copy_len; + } + } + + *aux_type = 0; + *aux_len = 0; + if (SOCK_PROTO(so) == IPPROTO_TCP) { + struct conninfo_tcp tcp_ci; + + *aux_type = CIAUX_TCP; + if (*aux_len == 0) { + *aux_len = sizeof (tcp_ci); + } else { + if (aux_data != USER_ADDR_NULL) { + copy_len = min(*aux_len, sizeof (tcp_ci)); + bzero(&tcp_ci, sizeof (tcp_ci)); + tcp_getconninfo(so, &tcp_ci); + error = copyout(&tcp_ci, aux_data, copy_len); + if (error != 0) + goto out; + *aux_len = copy_len; + } + } + } + +out: + return (error); +}