X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/0b4e3aa066abc0728aacb4bbeb86f53f9737156e..ff6e181ae92fc6f1e89841290f461d1f2f9badd9:/bsd/netinet/in.c diff --git a/bsd/netinet/in.c b/bsd/netinet/in.c index e8e82accd..adb49f221 100644 --- a/bsd/netinet/in.c +++ b/bsd/netinet/in.c @@ -3,19 +3,20 @@ * * @APPLE_LICENSE_HEADER_START@ * - * The contents of this file constitute Original Code as defined in and - * are subject to the Apple Public Source License Version 1.1 (the - * "License"). You may not use this file except in compliance with the - * License. Please obtain a copy of the License at - * http://www.apple.com/publicsource and read it before using this file. + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. * - * This Original Code and all software distributed under the License are - * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the - * License for the specific language governing rights and limitations - * under the License. + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ @@ -52,6 +53,7 @@ * 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 @@ -64,14 +66,11 @@ #include #include #include +#include #include -#include -#if NGIF > 0 -#include "gif.h" #include -#include -#endif +#include #include #include @@ -89,14 +88,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,9 +103,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 int ether_detach_inet(struct ifnet *ifp); +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 @@ -118,19 +118,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); } @@ -144,8 +152,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); @@ -164,8 +172,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) @@ -221,61 +229,30 @@ 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; -#if NGIF > 0 - if (ifp && ifp->if_type == IFT_GIF) { - switch (cmd) { - case SIOCSIFPHYADDR: -#if 1 - if (p && - (error = suser(p->p_ucred, &p->p_acflag)) != 0) - return(error); -#else - if ((so->so_state & SS_PRIV) == 0) - return (EPERM); -#endif - case SIOCGIFPSRCADDR: - case SIOCGIFPDSTADDR: -#if NGIF > 0 - if (strcmp(ifp->if_name, "gif") == 0) - dl_tag = gif_attach_inet(ifp); - return gif_ioctl(ifp, cmd, data); -#endif - } - } -#endif -#if NFAITH > 0 - if (ifp && ifp->if_type == IFT_FAITH) - dl_tag = faith_attach_inet(ifp); -#endif switch (cmd) { case SIOCALIFADDR: case SIOCDLIFADDR: -#if 1 - if (p && (error = suser(p->p_ucred, &p->p_acflag)) != 0) + if (p && (error = proc_suser(p)) != 0) return error; -#else - if ((so->so_state & SS_PRIV) == 0) - return (EPERM); -#endif /*fall through*/ case SIOCGLIFADDR: if (!ifp) @@ -289,7 +266,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) { @@ -303,16 +281,13 @@ in_control(so, cmd, data, ifp, p) break; } } - + lck_mtx_unlock(rt_mtx); + } switch (cmd) { case SIOCAUTOADDR: -#if 1 - if (p && (error = suser(p->p_ucred, &p->p_acflag)) != 0) + case SIOCARPIPLL: + if (p && (error = proc_suser(p)) != 0) return error; -#else - if ((so->so_state & SS_PRIV) == 0) - return (EPERM); -#endif break; case SIOCAIFADDR: @@ -320,12 +295,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 @@ -341,14 +318,8 @@ in_control(so, cmd, data, ifp, p) case SIOCSIFADDR: case SIOCSIFNETMASK: case SIOCSIFDSTADDR: - -#if ISFB31 - if (p && (error = suser(p->p_ucred, &p->p_acflag)) != 0) - return error; -#else if ((so->so_state & SS_PRIV) == 0) return (EPERM); -#endif if (ifp == 0) return (EADDRNOTAVAIL); @@ -364,28 +335,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 - */ - - if (strcmp(ifp->if_name, "en") == 0) - dl_tag = ether_attach_inet(ifp); - - if (strcmp(ifp->if_name, "lo") == 0) - dl_tag = lo_attach_inet(ifp); -/* End of temp code */ - - ifa->ifa_dlt = dl_tag; 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; @@ -393,27 +350,39 @@ 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) - return error; + if (p && (error = proc_suser(p)) != 0) + return error; if (ifp == 0) return (EADDRNOTAVAIL); - if (strcmp(ifp->if_name, "en")) - return ENODEV; - break; + break; case SIOCSIFBRDADDR: -#if ISFB31 - if (p && (error = suser(p->p_ucred, &p->p_acflag)) != 0) - return error; -#else +#ifdef __APPLE__ if ((so->so_state & SS_PRIV) == 0) return (EPERM); +#else + if (p && (error = suser(p)) != 0) + return error; #endif /* FALLTHROUGH */ @@ -429,10 +398,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: @@ -547,15 +530,26 @@ in_control(so, cmd, data, ifp, p) (struct sockaddr_in *) &ifr->ifr_addr, 1)); case SIOCPROTOATTACH: - ether_attach_inet(ifp); + 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; - return ether_detach_inet(ifp); + 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) + return(error); + break; + case SIOCSIFNETMASK: i = ifra->ifra_addr.sin_addr.s_addr; @@ -603,7 +597,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); @@ -611,7 +605,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 */ } @@ -627,7 +621,7 @@ in_control(so, cmd, data, ifp, p) * Report event. */ - if (error == 0) { + 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; @@ -663,6 +657,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; + 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; @@ -686,72 +687,73 @@ 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); - - in_ifscrub(ifp, ia); + lck_mtx_lock(rt_mtx); + TAILQ_REMOVE(&in_ifaddrhead, ia, ia_link); /* - * Protect from ipintr() traversing address list - * while we're modifying it. + * in_ifscrub kills the interface route. */ - 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); - - /* - * If the interface supports multicast, and no address is left, - * remove the "all hosts" multicast group from that interface. - */ - if (ifp->if_flags & IFF_MULTICAST) { - struct in_addr addr; - struct in_multi *inm; - - TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) - if (ifa->ifa_addr->sa_family == AF_INET) - break; - - if (ifa == 0) { - addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP); - IN_LOOKUP_MULTI(addr, ifp, inm); - if (inm) - in_delmulti(inm); - } - } - splx(s); + 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. + */ + if (ifp->if_flags & IFF_MULTICAST) { + struct in_addr addr; + struct in_multi *inm; + + TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) + if (ifa->ifa_addr->sa_family == AF_INET) + break; + + if (ifa == 0) { + addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP); + IN_LOOKUP_MULTI(addr, ifp, inm); + } + ifnet_lock_done(ifp); + if (inm) + in_delmulti(&inm); + } else + ifnet_lock_done(ifp); +#endif + + /* Post the kernel event */ + kev_post_msg(&ev_msg); break; +#ifdef __APPLE__ case SIOCSETOT: { /* * Inspiration from tcp_ctloutput() and ip_ctloutput() + * 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; } @@ -773,7 +775,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; @@ -782,14 +784,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; @@ -801,23 +801,33 @@ in_control(so, cmd, data, ifp, p) imo->imo_membership[i] = in_addmulti(&cloned_imo->imo_membership[i]->inm_addr, cloned_imo->imo_membership[i]->inm_ifp); + if (imo->imo_membership[i] == NULL) { + error2 = ENOBUFS; + break; + } + } + if (i < cloned_imo->imo_num_memberships) { + /* Failed, perform cleanup */ + for (i--; i >= 0; i--) + in_delmulti(&imo->imo_membership[i]); + imo->imo_num_memberships = 0; + break; } } } - splx(s); break; } +#endif /* __APPLE__ */ default: - return EOPNOTSUPP; - + return EOPNOTSUPP; } return (0); } /* * SIOC[GAD]LIFADDR. - * SIOCGLIFADDR: get first address. (???) + * SIOCGLIFADDR: get first address. (?!?) * SIOCGLIFADDR with IFLR_PREFIX: * get first address that matches the specified prefix. * SIOCALIFADDR: add the specified address. @@ -832,12 +842,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; @@ -870,12 +880,7 @@ in_lifaddr_ioctl(so, cmd, data, ifp, p) return EINVAL; break; default: /*shouldn't happen*/ -#if 0 - panic("invalid cmd to in_lifaddr_ioctl"); - /*NOTREACHED*/ -#else return EOPNOTSUPP; -#endif } if (sizeof(struct in_addr) * 8 < iflr->prefixlen) return EINVAL; @@ -942,6 +947,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; @@ -952,6 +958,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; @@ -1002,18 +1009,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); } /* @@ -1021,37 +1033,34 @@ 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; - u_long dl_tag; - - + int flags = RTF_UP, error; oldaddr = ia->ia_addr; ia->ia_addr = *sin; - + /* + * Give the interface a chance to initialize + * if this is its first address, + * and to validate the address if necessary. + */ error = dlil_ioctl(PF_INET, ifp, SIOCSIFADDR, (caddr_t)ia); 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)) @@ -1092,19 +1101,24 @@ in_ifinit(ifp, ia, sin, scrub) } if ((error = rtinit(&(ia->ia_ifa), (int)RTM_ADD, flags)) == 0) ia->ia_flags |= IFA_ROUTE; + /* XXX check if the subnet route points to the same interface */ + if (error == EEXIST) + error = 0; /* * If the interface supports multicast, join the "all hosts" * multicast group on that interface. */ if (ifp->if_flags & IFF_MULTICAST) { - struct in_multi *inm; + struct in_multi *inm; struct in_addr addr; addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP); - IN_LOOKUP_MULTI(addr, ifp, inm); - if (inm == 0) - in_addmulti(&addr, ifp); + ifnet_lock_shared(ifp); + IN_LOOKUP_MULTI(addr, ifp, inm); + ifnet_lock_done(ifp); + if (inm == 0) + in_addmulti(&addr, ifp); } return (error); } @@ -1114,11 +1128,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 || @@ -1132,10 +1146,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 || @@ -1148,25 +1164,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 @@ -1179,7 +1215,6 @@ in_addmulti(ap, ifp) sin.sin_addr = *ap; error = if_addmulti(ifp, (struct sockaddr *)&sin, &ifma); if (error) { - splx(s); return 0; } @@ -1187,12 +1222,12 @@ in_addmulti(ap, ifp) * If ifma->ifma_protospec is null, then if_addmulti() created * a new record. Otherwise, we are done. */ - if (ifma->ifma_protospec != 0) + if (ifma->ifma_protospec != 0) { return ifma->ifma_protospec; + } inm = (struct in_multi *) _MALLOC(sizeof(*inm), M_IPMADDR, M_WAITOK); if (inm == NULL) { - splx(s); return (NULL); } @@ -1200,14 +1235,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. */ - igmp_joingroup(inm); - splx(s); + error = igmp_joingroup(inm); + if (error) { + char addrbuf[16]; + + /* + * We can't free the inm because someone else may already be + * using it. Once we put it in to ifma->ifma_protospec, it + * must exist as long as the ifma does. Might be nice to flag + * the error so we can try igmp_joingroup the next time through. + */ + log(LOG_ERR, "igmp_joingroup error %d joining multicast %s on %s%d\n", + error, inet_ntop(AF_INET, &sin.sin_addr, addrbuf, sizeof(addrbuf)), + ifp->if_name, ifp->if_unit); + } + return (inm); } @@ -1215,25 +1273,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(); - - if (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); + struct in_multi *inm2; + + lck_mtx_lock(rt_mtx); + LIST_FOREACH(inm2, &in_multihead, inm_link) { + if (inm2 == *inm) + break; } - /* XXX - should be separate API for when we have an ifma? */ - if_delmulti(ifma->ifma_ifp, ifma->ifma_addr); - splx(s); - + 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 ((*inm)->inm_ifma) { + if_delmultiaddr((*inm)->inm_ifma, 0); + ifma_release((*inm)->inm_ifma); + } + *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