X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/55e303ae13a4cf49d70f2294092726f2fffb9ef2..8ad349bb6ed4a0be06e34c92be0d98b92e078db4:/bsd/netinet/in.c diff --git a/bsd/netinet/in.c b/bsd/netinet/in.c index ac87e8ac5..02cf2a24c 100644 --- a/bsd/netinet/in.c +++ b/bsd/netinet/in.c @@ -1,26 +1,31 @@ /* - * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ + * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved. * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. + * @APPLE_LICENSE_OSREFERENCE_HEADER_START@ * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this + * 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. + * + * 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, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and + * + * 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, 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_LICENSE_OSREFERENCE_HEADER_END@ */ /* * Copyright (c) 1982, 1986, 1991, 1993 @@ -68,6 +73,7 @@ #include #include #include +#include #include #include @@ -89,14 +95,14 @@ #include -static int in_mask2len __P((struct in_addr *)); -static void in_len2mask __P((struct in_addr *, int)); -static int in_lifaddr_ioctl __P((struct socket *, u_long, caddr_t, - struct ifnet *, struct proc *)); +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 void in_socktrim __P((struct sockaddr_in *)); -static int in_ifinit __P((struct ifnet *, - struct in_ifaddr *, struct sockaddr_in *, 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, @@ -104,7 +110,10 @@ SYSCTL_INT(_net_inet_ip, OID_AUTO, subnets_are_local, CTLFLAG_RW, struct in_multihead in_multihead; /* XXX BSS initialization */ -extern void arp_rtrequest(); +extern lck_mtx_t *rt_mtx; + +/* Track whether or not the SIOCARPIPLL ioctl has been called */ +__private_extern__ u_int32_t ipv4_ll_arp_aware = 0; /* * Return 1 if an internet address is for a ``local'' host @@ -116,19 +125,27 @@ int in_localaddr(in) struct in_addr in; { - register u_long i = ntohl(in.s_addr); - register struct in_ifaddr *ia; + u_long i = ntohl(in.s_addr); + struct in_ifaddr *ia; if (subnetsarelocal) { + lck_mtx_lock(rt_mtx); for (ia = in_ifaddrhead.tqh_first; ia; ia = ia->ia_link.tqe_next) - if ((i & ia->ia_netmask) == ia->ia_net) + if ((i & ia->ia_netmask) == ia->ia_net) { + lck_mtx_unlock(rt_mtx); return (1); + } + lck_mtx_unlock(rt_mtx); } else { + lck_mtx_lock(rt_mtx); for (ia = in_ifaddrhead.tqh_first; ia; ia = ia->ia_link.tqe_next) - if ((i & ia->ia_subnetmask) == ia->ia_subnet) + if ((i & ia->ia_subnetmask) == ia->ia_subnet) { + lck_mtx_unlock(rt_mtx); return (1); + } + lck_mtx_unlock(rt_mtx); } return (0); } @@ -142,8 +159,8 @@ int in_canforward(in) struct in_addr in; { - register u_long i = ntohl(in.s_addr); - register u_long net; + u_long i = ntohl(in.s_addr); + u_long net; if (IN_EXPERIMENTAL(i) || IN_MULTICAST(i)) return (0); @@ -162,8 +179,8 @@ static void in_socktrim(ap) struct sockaddr_in *ap; { - register char *cplim = (char *) &ap->sin_addr; - register char *cp = (char *) (&ap->sin_addr + 1); + char *cplim = (char *) &ap->sin_addr; + char *cp = (char *) (&ap->sin_addr + 1); ap->sin_len = 0; while (--cp >= cplim) @@ -219,21 +236,21 @@ static int in_interfaces; /* number of external internet interfaces */ */ /* ARGSUSED */ int -in_control(so, cmd, data, ifp, p) - struct socket *so; - u_long cmd; - caddr_t data; - register struct ifnet *ifp; - struct proc *p; +in_control( + struct socket *so, + u_long cmd, + caddr_t data, + struct ifnet *ifp, + struct proc *p) { - register struct ifreq *ifr = (struct ifreq *)data; - register struct in_ifaddr *ia = 0, *iap; - register struct ifaddr *ifa; + struct ifreq *ifr = (struct ifreq *)data; + struct in_ifaddr *ia = 0, *iap; + struct ifaddr *ifa; struct in_ifaddr *oia; struct in_aliasreq *ifra = (struct in_aliasreq *)data; struct sockaddr_in oldaddr; - int error, hostIsNew, maskIsNew, s; - u_long i, dl_tag; + int error, hostIsNew, maskIsNew; + u_long i; struct kev_msg ev_msg; struct kev_in_data in_event_data; @@ -241,7 +258,7 @@ in_control(so, cmd, data, ifp, p) switch (cmd) { case SIOCALIFADDR: case SIOCDLIFADDR: - if (p && (error = suser(p->p_ucred, &p->p_acflag)) != 0) + if (p && (error = proc_suser(p)) != 0) return error; /*fall through*/ case SIOCGLIFADDR: @@ -256,7 +273,8 @@ in_control(so, cmd, data, ifp, p) * If an alias address was specified, find that one instead of * the first one on the interface. */ - if (ifp) + 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) { @@ -270,10 +288,12 @@ in_control(so, cmd, data, ifp, p) break; } } - + lck_mtx_unlock(rt_mtx); + } switch (cmd) { case SIOCAUTOADDR: - if (p && (error = suser(p->p_ucred, &p->p_acflag)) != 0) + case SIOCARPIPLL: + if (p && (error = proc_suser(p)) != 0) return error; break; @@ -282,12 +302,14 @@ in_control(so, cmd, data, ifp, p) if (ifp == 0) return (EADDRNOTAVAIL); if (ifra->ifra_addr.sin_family == AF_INET) { + lck_mtx_lock(rt_mtx); for (oia = ia; ia; ia = ia->ia_link.tqe_next) { if (ia->ia_ifp == ifp && ia->ia_addr.sin_addr.s_addr == ifra->ifra_addr.sin_addr.s_addr) break; } + lck_mtx_unlock(rt_mtx); if ((ifp->if_flags & IFF_POINTOPOINT) && (cmd == SIOCAIFADDR) && (ifra->ifra_dstaddr.sin_addr.s_addr @@ -303,13 +325,8 @@ in_control(so, cmd, data, ifp, p) case SIOCSIFADDR: case SIOCSIFNETMASK: case SIOCSIFDSTADDR: -#ifdef __APPLE__ if ((so->so_state & SS_PRIV) == 0) return (EPERM); -#else - if (p && (error = suser(p)) != 0) - return error; -#endif if (ifp == 0) return (EADDRNOTAVAIL); @@ -325,29 +342,14 @@ in_control(so, cmd, data, ifp, p) * Protect from ipintr() traversing address list * while we're modifying it. */ - s = splnet(); - TAILQ_INSERT_TAIL(&in_ifaddrhead, ia, ia_link); ifa = &ia->ia_ifa; - TAILQ_INSERT_TAIL(&ifp->if_addrhead, ifa, ifa_link); - -/* - * Temorary code for protocol attachment XXX - */ - - /* Generic protocol plumbing */ - - if (error = dlil_plumb_protocol(PF_INET, ifp, &dl_tag)) { - 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 */ - } -/* End of temp code */ 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_family = AF_INET; @@ -355,13 +357,27 @@ in_control(so, cmd, data, ifp, p) ia->ia_ifp = ifp; if (!(ifp->if_flags & IFF_LOOPBACK)) in_interfaces++; - splx(s); + if_attach_ifa(ifp, ifa); + ifnet_lock_done(ifp); + + lck_mtx_lock(rt_mtx); + 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 */ + } + } break; case SIOCPROTOATTACH: case SIOCPROTODETACH: - if (p && (error = suser(p->p_ucred, &p->p_acflag)) != 0) + if (p && (error = proc_suser(p)) != 0) return error; if (ifp == 0) return (EADDRNOTAVAIL); @@ -389,10 +405,24 @@ in_control(so, cmd, data, ifp, p) case SIOCAUTOADDR: if (ifp == 0) return (EADDRNOTAVAIL); - if (ifr->ifr_data) + ifnet_lock_exclusive(ifp); + if (ifr->ifr_intval) ifp->if_eflags |= IFEF_AUTOCONFIGURING; else ifp->if_eflags &= ~IFEF_AUTOCONFIGURING; + ifnet_lock_done(ifp); + break; + + case SIOCARPIPLL: + if (ifp == 0) + return (EADDRNOTAVAIL); + ipv4_ll_arp_aware = 1; + ifnet_lock_exclusive(ifp); + if (ifr->ifr_data) + ifp->if_eflags |= IFEF_ARPLL; + else + ifp->if_eflags &= ~IFEF_ARPLL; + ifnet_lock_done(ifp); break; case SIOCGIFADDR: @@ -507,16 +537,20 @@ in_control(so, cmd, data, ifp, p) (struct sockaddr_in *) &ifr->ifr_addr, 1)); case SIOCPROTOATTACH: - error = dlil_plumb_protocol(PF_INET, ifp, &dl_tag); + error = dlil_plumb_protocol(PF_INET, ifp); if (error) return(error); break; case SIOCPROTODETACH: // if an ip address is still present, refuse to detach - TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) - if (ifa->ifa_addr->sa_family == AF_INET) - return EBUSY; + ifnet_lock_shared(ifp); + TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) + if (ifa->ifa_addr->sa_family == AF_INET) + break; + ifnet_lock_done(ifp); + if (ifa != 0) + return EBUSY; error = dlil_unplumb_protocol(PF_INET, ifp); if (error) @@ -570,7 +604,7 @@ in_control(so, cmd, data, ifp, p) hostIsNew = 0; } if (ifra->ifra_mask.sin_len) { - in_ifscrub(ifp, ia); + in_ifscrub(ifp, ia, 0); ia->ia_sockmask = ifra->ifra_mask; ia->ia_subnetmask = ntohl(ia->ia_sockmask.sin_addr.s_addr); @@ -578,7 +612,7 @@ in_control(so, cmd, data, ifp, p) } if ((ifp->if_flags & IFF_POINTOPOINT) && (ifra->ifra_dstaddr.sin_family == AF_INET)) { - in_ifscrub(ifp, ia); + in_ifscrub(ifp, ia, 0); ia->ia_dstaddr = ifra->ifra_dstaddr; maskIsNew = 1; /* We lie; but the effect's the same */ } @@ -630,12 +664,13 @@ in_control(so, cmd, data, ifp, p) return (error); case SIOCDIFADDR: - error = dlil_ioctl(PF_INET, ifp, SIOCDIFADDR, (caddr_t)ia); - if (error == EOPNOTSUPP) - error = 0; + error = dlil_ioctl(PF_INET, ifp, SIOCDIFADDR, (caddr_t)ia); + if (error == EOPNOTSUPP) + error = 0; if (error) return error; + /* 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; @@ -659,47 +694,23 @@ in_control(so, cmd, data, ifp, p) 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[0].data_length = sizeof(struct kev_in_data); ev_msg.dv[1].data_length = 0; - kev_post_msg(&ev_msg); - + lck_mtx_lock(rt_mtx); + TAILQ_REMOVE(&in_ifaddrhead, ia, ia_link); /* * in_ifscrub kills the interface route. */ - in_ifscrub(ifp, ia); -#ifndef __APPLE__ - /* - * in_ifadown gets rid of all the rest of - * the routes. This is not quite the right - * thing to do, but at least if we are running - * a routing process they will come back. - */ - in_ifadown(&ia->ia_ifa, 1); - /* - * XXX horrible hack to detect that we are being called - * from if_detach() - */ - if (!ifnet_addrs[ifp->if_index - 1]) { - in_pcbpurgeif0(LIST_FIRST(ripcbinfo.listhead), ifp); - in_pcbpurgeif0(LIST_FIRST(udbinfo.listhead), ifp); - } -#endif - - /* - * Protect from ipintr() traversing address list - * while we're modifying it. - */ - s = splnet(); - + in_ifscrub(ifp, ia, 1); ifa = &ia->ia_ifa; - TAILQ_REMOVE(&ifp->if_addrhead, ifa, ifa_link); - oia = ia; - TAILQ_REMOVE(&in_ifaddrhead, oia, ia_link); - ifafree(&oia->ia_ifa); + lck_mtx_unlock(rt_mtx); + ifnet_lock_exclusive(ifp); + 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. */ @@ -714,12 +725,16 @@ in_control(so, cmd, data, ifp, p) if (ifa == 0) { addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP); IN_LOOKUP_MULTI(addr, ifp, inm); - if (inm) - in_delmulti(inm); } - } + ifnet_lock_done(ifp); + if (inm) + in_delmulti(&inm); + } else + ifnet_lock_done(ifp); #endif - splx(s); + + /* Post the kernel event */ + kev_post_msg(&ev_msg); break; #ifdef __APPLE__ @@ -729,27 +744,23 @@ in_control(so, cmd, data, ifp, p) * Special ioctl for OpenTransport sockets */ struct inpcb *inp, *cloned_inp; - int error = 0; + int error2 = 0; int cloned_fd = *(int *)data; - s = splnet(); /* XXX */ inp = sotoinpcb(so); if (inp == NULL) { - splx(s); break; } /* let's make sure it's either -1 or a valid file descriptor */ if (cloned_fd != -1) { struct socket *cloned_so; - struct file *cloned_fp; - error = getsock(p->p_fd, cloned_fd, &cloned_fp); - if (error){ - splx(s); + error2 = file_socket(cloned_fd, &cloned_so); + if (error2){ break; } - cloned_so = (struct socket *)cloned_fp->f_data; cloned_inp = sotoinpcb(cloned_so); + file_drop(cloned_fd); } else { cloned_inp = NULL; } @@ -771,7 +782,7 @@ in_control(so, cmd, data, ifp, p) /* Multicast options */ if (cloned_inp->inp_moptions != NULL) { - int i; + int i; struct ip_moptions *cloned_imo = cloned_inp->inp_moptions; struct ip_moptions *imo = inp->inp_moptions; @@ -780,14 +791,12 @@ in_control(so, cmd, data, ifp, p) * No multicast option buffer attached to the pcb; * allocate one. */ - splx(); imo = (struct ip_moptions*) _MALLOC(sizeof(*imo), M_IPMOPTS, M_WAITOK); if (imo == NULL) { - error = ENOBUFS; + error2 = ENOBUFS; break; } - s = splnet(); /* XXX */ inp->inp_moptions = imo; } imo->imo_multicast_ifp = cloned_imo->imo_multicast_ifp; @@ -800,29 +809,25 @@ in_control(so, cmd, data, ifp, p) in_addmulti(&cloned_imo->imo_membership[i]->inm_addr, cloned_imo->imo_membership[i]->inm_ifp); if (imo->imo_membership[i] == NULL) { - error = ENOBUFS; + error2 = ENOBUFS; break; } } if (i < cloned_imo->imo_num_memberships) { /* Failed, perform cleanup */ for (i--; i >= 0; i--) - in_delmulti(imo->imo_membership[i]); + in_delmulti(&imo->imo_membership[i]); + imo->imo_num_memberships = 0; break; } } } - splx(s); break; } #endif /* __APPLE__ */ default: return EOPNOTSUPP; - /* Darwin: dlil_ioctl called from ifioctl */ -#ifndef __APPLE__ - return ((*ifp->if_ioctl)(ifp, cmd, data)); -#endif } return (0); } @@ -844,12 +849,12 @@ in_control(so, cmd, data, ifp, p) * other values may be returned from in_ioctl() */ static int -in_lifaddr_ioctl(so, cmd, data, ifp, p) - struct socket *so; - u_long cmd; - caddr_t data; - struct ifnet *ifp; - struct proc *p; +in_lifaddr_ioctl( + struct socket *so, + u_long cmd, + caddr_t data, + struct ifnet *ifp, + struct proc *p) { struct if_laddrreq *iflr = (struct if_laddrreq *)data; struct ifaddr *ifa; @@ -949,6 +954,7 @@ in_lifaddr_ioctl(so, cmd, data, ifp, p) } } + ifnet_lock_shared(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; @@ -959,6 +965,7 @@ in_lifaddr_ioctl(so, cmd, data, ifp, p) if (candidate.s_addr == match.s_addr) break; } + ifnet_lock_done(ifp); if (!ifa) return EADDRNOTAVAIL; ia = (struct in_ifaddr *)ifa; @@ -1009,18 +1016,23 @@ in_lifaddr_ioctl(so, cmd, data, ifp, p) * Delete any existing route for an interface. */ void -in_ifscrub(ifp, ia) - register struct ifnet *ifp; - register struct in_ifaddr *ia; +in_ifscrub( + struct ifnet *ifp, + struct in_ifaddr *ia, + int locked) { if ((ia->ia_flags & IFA_ROUTE) == 0) return; + if (!locked) + lck_mtx_lock(rt_mtx); if (ifp->if_flags & (IFF_LOOPBACK|IFF_POINTOPOINT)) - rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST); + rtinit_locked(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST); else - rtinit(&(ia->ia_ifa), (int)RTM_DELETE, 0); + rtinit_locked(&(ia->ia_ifa), (int)RTM_DELETE, 0); ia->ia_flags &= ~IFA_ROUTE; + if (!locked) + lck_mtx_unlock(rt_mtx); } /* @@ -1028,15 +1040,15 @@ in_ifscrub(ifp, ia) * and routing table entry. */ static int -in_ifinit(ifp, ia, sin, scrub) - register struct ifnet *ifp; - register 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) { - register u_long i = ntohl(sin->sin_addr.s_addr); + u_long i = ntohl(sin->sin_addr.s_addr); struct sockaddr_in oldaddr; - int s = splimp(), flags = RTF_UP, error; + int flags = RTF_UP, error; oldaddr = ia->ia_addr; ia->ia_addr = *sin; @@ -1050,14 +1062,12 @@ in_ifinit(ifp, ia, sin, scrub) if (error == EOPNOTSUPP) error = 0; if (error) { - splx(s); ia->ia_addr = oldaddr; return (error); } - splx(s); if (scrub) { ia->ia_ifa.ifa_addr = (struct sockaddr *)&oldaddr; - in_ifscrub(ifp, ia); + in_ifscrub(ifp, ia, 0); ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; } if (IN_CLASSA(i)) @@ -1111,7 +1121,9 @@ in_ifinit(ifp, ia, sin, scrub) struct in_addr addr; 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); } @@ -1123,11 +1135,11 @@ in_ifinit(ifp, ia, sin, scrub) * Return 1 if the address might be a local broadcast address. */ int -in_broadcast(in, ifp) - struct in_addr in; - struct ifnet *ifp; +in_broadcast( + struct in_addr in, + struct ifnet *ifp) { - register struct ifaddr *ifa; + struct ifaddr *ifa; u_long t; if (in.s_addr == INADDR_BROADCAST || @@ -1141,10 +1153,12 @@ in_broadcast(in, ifp) * with a broadcast address. */ #define ia ((struct in_ifaddr *)ifa) - for (ifa = ifp->if_addrhead.tqh_first; ifa; - ifa = ifa->ifa_link.tqe_next) { - if (ifa->ifa_addr == NULL) + ifnet_lock_shared(ifp); + TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { + if (ifa->ifa_addr == NULL) { + ifnet_lock_done(ifp); return (0); + } 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 || @@ -1157,25 +1171,45 @@ in_broadcast(in, ifp) * only exist when an interface gets a secondary * address. */ - ia->ia_subnetmask != (u_long)0xffffffff) - return 1; + ia->ia_subnetmask != (u_long)0xffffffff) { + ifnet_lock_done(ifp); + return 1; + } } + ifnet_lock_done(ifp); return (0); #undef ia } + +static void +in_free_inm( + void* ifma_protospec) +{ + struct in_multi *inm = ifma_protospec; + + /* + * No remaining claims to this record; let IGMP know that + * we are leaving the multicast group. + */ + igmp_leavegroup(inm); + lck_mtx_lock(rt_mtx); + LIST_REMOVE(inm, inm_link); + lck_mtx_unlock(rt_mtx); + FREE(inm, M_IPMADDR); +} + /* * Add an address to the list of IP multicast addresses for a given interface. */ struct in_multi * -in_addmulti(ap, ifp) - register struct in_addr *ap; - register struct ifnet *ifp; +in_addmulti( + struct in_addr *ap, + struct ifnet *ifp) { - register struct in_multi *inm; + struct in_multi *inm; int error; struct sockaddr_in sin; struct ifmultiaddr *ifma; - int s = splnet(); /* * Call generic routine to add membership or increment @@ -1188,7 +1222,6 @@ in_addmulti(ap, ifp) sin.sin_addr = *ap; error = if_addmulti(ifp, (struct sockaddr *)&sin, &ifma); if (error) { - splx(s); return 0; } @@ -1197,13 +1230,11 @@ in_addmulti(ap, ifp) * a new record. Otherwise, we are done. */ if (ifma->ifma_protospec != 0) { - splx(s); return ifma->ifma_protospec; } inm = (struct in_multi *) _MALLOC(sizeof(*inm), M_IPMADDR, M_WAITOK); if (inm == NULL) { - splx(s); return (NULL); } @@ -1211,20 +1242,37 @@ in_addmulti(ap, ifp) inm->inm_addr = *ap; inm->inm_ifp = ifp; inm->inm_ifma = ifma; - ifma->ifma_protospec = inm; - LIST_INSERT_HEAD(&in_multihead, inm, inm_link); + 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; + } /* * Let IGMP know that we have joined a new IP multicast group. */ error = igmp_joingroup(inm); if (error) { - if_delmultiaddr(ifma); - LIST_REMOVE(inm, inm_link); - _FREE(inm, M_IPMADDR); - inm = NULL; + 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); } - splx(s); + return (inm); } @@ -1232,26 +1280,49 @@ in_addmulti(ap, ifp) * Delete a multicast address record. */ void -in_delmulti(inm) - register struct in_multi *inm; +in_delmulti( + struct in_multi **inm) { - struct ifmultiaddr *ifma = inm->inm_ifma; - int s = splnet(); + struct in_multi *inm2; + + lck_mtx_lock(rt_mtx); + LIST_FOREACH(inm2, &in_multihead, inm_link) { + if (inm2 == *inm) + break; + } + if (inm2 != *inm) { + lck_mtx_unlock(rt_mtx); + printf("in_delmulti - ignorning invalid inm (0x%x)\n", *inm); + return; + } + lck_mtx_unlock(rt_mtx); /* We intentionally do this a bit differently than BSD */ - - if (ifma && ifma->ifma_refcount == 1) { - /* - * No remaining claims to this record; let IGMP know that - * we are leaving the multicast group. - */ - igmp_leavegroup(inm); - ifma->ifma_protospec = 0; - LIST_REMOVE(inm, inm_link); - FREE(inm, M_IPMADDR); + if ((*inm)->inm_ifma) { + if_delmultiaddr((*inm)->inm_ifma, 0); + ifma_release((*inm)->inm_ifma); } - /* XXX - should be separate API for when we have an ifma? */ - if (ifma) - if_delmultiaddr(ifma); - splx(s); + *inm = NULL; } + +#if !NFSCLIENT +int +inet_aton(char * cp, struct in_addr * pin) +{ + u_char * b = (char *)pin; + int i; + char * p; + + 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); +} +#endif