#include <sys/kernel.h>
#include <sys/syslog.h>
#include <sys/kern_event.h>
+#include <kern/lock.h>
#include <net/if.h>
#include <net/if_types.h>
const struct sockaddr_in6 sa6_any = {sizeof(sa6_any), AF_INET6,
0, 0, IN6ADDR_ANY_INIT, 0};
-static int in6_lifaddr_ioctl __P((struct socket *, u_long, caddr_t,
- struct ifnet *, struct proc *));
-static int in6_ifinit __P((struct ifnet *, struct in6_ifaddr *,
- struct sockaddr_in6 *, int));
-static void in6_unlink_ifa __P((struct in6_ifaddr *, struct ifnet *));
+static int in6_lifaddr_ioctl(struct socket *, u_long, caddr_t,
+ struct ifnet *, struct proc *);
+static int in6_ifinit(struct ifnet *, struct in6_ifaddr *,
+ struct sockaddr_in6 *, int);
+static void in6_unlink_ifa(struct in6_ifaddr *, struct ifnet *, int);
struct in6_multihead in6_multihead; /* XXX BSS initialization */
+extern struct lck_mtx_t *nd6_mutex;
/*
* Subroutine for in6_ifaddloop() and in6_ifremloop().
* (probably implicitly) set nd6_rtrequest() to ifa->ifa_rtrequest,
* which changes the outgoing interface to the loopback interface.
*/
- e = rtrequest(cmd, ifa->ifa_addr, ifa->ifa_addr,
+ e = rtrequest_locked(cmd, ifa->ifa_addr, ifa->ifa_addr,
(struct sockaddr *)&all1_sa,
RTF_UP|RTF_HOST|RTF_LLINFO, &nrt);
if (e != 0) {
if (nrt->rt_refcnt <= 0) {
/* XXX: we should free the entry ourselves. */
rtref(nrt);
- rtfree(nrt);
+ rtfree_locked(nrt);
}
} else {
/* the cmd must be RTM_ADD here */
{
struct rtentry *rt;
+ lck_mtx_lock(rt_mtx);
/* If there is no loopback entry, allocate one. */
- rt = rtalloc1(ifa->ifa_addr, 0, 0);
+ rt = rtalloc1_locked(ifa->ifa_addr, 0, 0);
if (rt == NULL || (rt->rt_flags & RTF_HOST) == 0 ||
(rt->rt_ifp->if_flags & IFF_LOOPBACK) == 0)
in6_ifloop_request(RTM_ADD, ifa);
if (rt)
rt->rt_refcnt--;
+ lck_mtx_unlock(rt_mtx);
}
/*
* if it exists.
*/
static void
-in6_ifremloop(struct ifaddr *ifa)
+in6_ifremloop(struct ifaddr *ifa, int locked)
{
struct in6_ifaddr *ia;
struct rtentry *rt;
* (probably p2p) interfaces.
* XXX: we should avoid such a configuration in IPv6...
*/
- for (ia = in6_ifaddr; ia; ia = ia->ia_next) {
+ if (!locked)
+ lck_mtx_lock(nd6_mutex);
+ for (ia = in6_ifaddrs; ia; ia = ia->ia_next) {
if (IN6_ARE_ADDR_EQUAL(IFA_IN6(ifa), &ia->ia_addr.sin6_addr)) {
ia_count++;
if (ia_count > 1)
break;
}
}
+ if (!locked)
+ lck_mtx_unlock(nd6_mutex);
if (ia_count == 1) {
/*
* a subnet-router anycast address on an interface attahced
* to a shared medium.
*/
- rt = rtalloc1(ifa->ifa_addr, 0, 0);
+ lck_mtx_lock(rt_mtx);
+ rt = rtalloc1_locked(ifa->ifa_addr, 0, 0);
if (rt != NULL && (rt->rt_flags & RTF_HOST) != 0 &&
(rt->rt_ifp->if_flags & IFF_LOOPBACK) != 0) {
rt->rt_refcnt--;
in6_ifloop_request(RTM_DELETE, ifa);
}
+ lck_mtx_unlock(rt_mtx);
}
}
if (idx < 0 || if_index < idx)
return -1;
+
+ ifnet_head_lock_shared();
ifp = ifindex2ifnet[idx];
+ ifnet_head_done();
+ ifnet_lock_shared(ifp);
TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list)
{
if (ifa->ifa_addr->sa_family != AF_INET6)
continue;
sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
- if (IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr))
+ if (IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr)) {
+ ifnet_lock_done(ifp);
return sin6->sin6_scope_id & 0xffff;
+ }
}
+ ifnet_lock_done(ifp);
return -1;
}
struct in6_ifaddr *ia = NULL;
struct in6_aliasreq *ifra = (struct in6_aliasreq *)data;
int privileged, error = 0;
- u_long dl_tag;
+ int index;
+ struct timeval timenow;
+
+ getmicrotime(&timenow);
privileged = 0;
#ifdef __APPLE__
- if (p == NULL || !suser(p->p_ucred, &p->p_acflag))
+ if (p == NULL || !proc_suser(p))
#else
if (p == NULL || !suser(p))
#endif
return(EOPNOTSUPP);
switch (cmd) {
+ case SIOCAUTOCONF_START:
+ case SIOCAUTOCONF_STOP:
+ case SIOCLL_START:
+ case SIOCLL_STOP:
+ case SIOCPROTOATTACH_IN6:
+ case SIOCPROTODETACH_IN6:
+ if (!privileged)
+ return(EPERM);
+ break;
case SIOCSNDFLUSH_IN6:
case SIOCSPFXFLUSH_IN6:
case SIOCSRTRFLUSH_IN6:
switch (cmd) {
case SIOCAUTOCONF_START:
+ ifnet_lock_exclusive(ifp);
ifp->if_eflags |= IFEF_ACCEPT_RTADVD;
+ ifnet_lock_done(ifp);
return (0);
case SIOCAUTOCONF_STOP:
{
- struct ifaddr *ifa, *nifa = NULL;
-
+ struct in6_ifaddr *ia, *nia = NULL;
+
+ ifnet_lock_exclusive(ifp);
ifp->if_eflags &= ~IFEF_ACCEPT_RTADVD;
+ ifnet_lock_done(ifp);
- /* nuke prefix list. this may try to remove some of ifaddrs as well */
+ /* nuke prefix list. this may try to remove some ifaddrs as well */
in6_purgeprefix(ifp);
/* removed autoconfigured address from interface */
-
- for (ifa = TAILQ_FIRST(&ifp->if_addrlist); ifa != NULL; ifa = nifa)
- {
- nifa = TAILQ_NEXT(ifa, ifa_list);
- if (ifa->ifa_addr == NULL || ifa->ifa_addr->sa_family != AF_INET6)
+ lck_mtx_lock(nd6_mutex);
+ for (ia = in6_ifaddrs; ia != NULL; ia = nia) {
+ nia = ia->ia_next;
+ if (ia->ia_ifa.ifa_ifp != ifp)
continue;
- if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_AUTOCONF)
- in6_purgeaddr(ifa);
+ if (ia->ia6_flags & IN6_IFF_AUTOCONF)
+ in6_purgeaddr(&ia->ia_ifa, 1);
}
+ lck_mtx_unlock(nd6_mutex);
return (0);
}
case SIOCLL_STOP:
{
- struct ifaddr *ifa, *nifa = NULL;
-
+ struct in6_ifaddr *ia, *nia = NULL;
+
/* removed link local addresses from interface */
- for (ifa = TAILQ_FIRST(&ifp->if_addrlist); ifa != NULL; ifa = nifa)
- {
- nifa = TAILQ_NEXT(ifa, ifa_list);
- if (ifa->ifa_addr == NULL || ifa->ifa_addr->sa_family != AF_INET6)
+ lck_mtx_lock(nd6_mutex);
+ for (ia = in6_ifaddrs; ia != NULL; ia = nia) {
+ nia = ia->ia_next;
+ if (ia->ia_ifa.ifa_ifp != ifp)
continue;
- if (IN6_IS_ADDR_LINKLOCAL(IFA_IN6(ifa)))
- in6_purgeaddr(ifa);
+ if (IN6_IS_ADDR_LINKLOCAL(&ia->ia_addr.sin6_addr))
+ in6_purgeaddr(&ia->ia_ifa, 1);
}
+ lck_mtx_unlock(nd6_mutex);
return (0);
}
case SIOCPROTOATTACH_IN6:
-
+
switch (ifp->if_type) {
#if IFT_BRIDGE /*OpenBSD 2.8*/
/* some of the interfaces are inherently not IPv6 capable */
#endif
default:
- if (error = dlil_plumb_protocol(PF_INET6, ifp, &dl_tag))
+ if (error = dlil_plumb_protocol(PF_INET6, ifp))
printf("SIOCPROTOATTACH_IN6: %s error=%d\n",
if_name(ifp), error);
break;
* and should be unused.
*/
/* we decided to obsolete this command (20000704) */
- return(EINVAL);
+ error = EINVAL;
+ goto ioctl_cleanup;
case SIOCDIFADDR_IN6:
/*
* address from the day one, we consider "remove the first one"
* semantics to be not preferable.
*/
- if (ia == NULL)
- return(EADDRNOTAVAIL);
+ if (ia == NULL) {
+ error = EADDRNOTAVAIL;
+ goto ioctl_cleanup;
+ }
+
/* FALLTHROUGH */
case SIOCAIFADDR_IN6:
/*
* the corresponding operation.
*/
if (ifra->ifra_addr.sin6_family != AF_INET6 ||
- ifra->ifra_addr.sin6_len != sizeof(struct sockaddr_in6))
- return(EAFNOSUPPORT);
- if (!privileged)
- return(EPERM);
+ ifra->ifra_addr.sin6_len != sizeof(struct sockaddr_in6)) {
+ error = EAFNOSUPPORT;
+ goto ioctl_cleanup;
+ }
+ if (!privileged) {
+ error = EPERM;
+ goto ioctl_cleanup;
+ }
break;
case SIOCGIFDSTADDR_IN6:
case SIOCGIFALIFETIME_IN6:
/* must think again about its semantics */
- if (ia == NULL)
- return(EADDRNOTAVAIL);
+ if (ia == NULL) {
+ error = EADDRNOTAVAIL;
+ goto ioctl_cleanup;
+ }
break;
case SIOCSIFALIFETIME_IN6:
{
struct in6_addrlifetime *lt;
- if (!privileged)
- return(EPERM);
- if (ia == NULL)
- return(EADDRNOTAVAIL);
+ if (!privileged) {
+ error = EPERM;
+ goto ioctl_cleanup;
+ }
+ if (ia == NULL) {
+ error = EADDRNOTAVAIL;
+ goto ioctl_cleanup;
+ }
/* sanity for overflow - beware unsigned */
lt = &ifr->ifr_ifru.ifru_lifetime;
if (lt->ia6t_vltime != ND6_INFINITE_LIFETIME
- && lt->ia6t_vltime + time_second < time_second) {
- return EINVAL;
+ && lt->ia6t_vltime + timenow.tv_sec < timenow.tv_sec) {
+ error = EINVAL;
+ goto ioctl_cleanup;
}
if (lt->ia6t_pltime != ND6_INFINITE_LIFETIME
- && lt->ia6t_pltime + time_second < time_second) {
- return EINVAL;
+ && lt->ia6t_pltime + timenow.tv_sec < timenow.tv_sec) {
+ error = EINVAL;
+ goto ioctl_cleanup;
}
break;
}
break;
case SIOCGIFDSTADDR_IN6:
- if ((ifp->if_flags & IFF_POINTOPOINT) == 0)
- return(EINVAL);
+ if ((ifp->if_flags & IFF_POINTOPOINT) == 0) {
+ error = EINVAL;
+ goto ioctl_cleanup;
+ }
/*
* XXX: should we check if ifa_dstaddr is NULL and return
* an error?
break;
case SIOCGIFSTAT_IN6:
- if (ifp == NULL)
- return EINVAL;
- if (in6_ifstat == NULL || ifp->if_index >= in6_ifstatmax
- || in6_ifstat[ifp->if_index] == NULL) {
+ if (ifp == NULL) {
+ error = EINVAL;
+ goto ioctl_cleanup;
+ }
+ index = ifp->if_index;
+ if (in6_ifstat == NULL || index >= in6_ifstatmax
+ || in6_ifstat[index] == NULL) {
/* return EAFNOSUPPORT? */
bzero(&ifr->ifr_ifru.ifru_stat,
sizeof(ifr->ifr_ifru.ifru_stat));
} else
- ifr->ifr_ifru.ifru_stat = *in6_ifstat[ifp->if_index];
+ ifr->ifr_ifru.ifru_stat = *in6_ifstat[index];
break;
case SIOCGIFSTAT_ICMP6:
- if (ifp == NULL)
- return EINVAL;
- if (icmp6_ifstat == NULL || ifp->if_index >= icmp6_ifstatmax ||
- icmp6_ifstat[ifp->if_index] == NULL) {
+ if (ifp == NULL) {
+ error = EINVAL;
+ goto ioctl_cleanup;
+ }
+ index = ifp->if_index;
+ if (icmp6_ifstat == NULL || index >= icmp6_ifstatmax ||
+ icmp6_ifstat[index] == NULL) {
/* return EAFNOSUPPORT? */
bzero(&ifr->ifr_ifru.ifru_stat,
sizeof(ifr->ifr_ifru.ifru_icmp6stat));
} else
ifr->ifr_ifru.ifru_icmp6stat =
- *icmp6_ifstat[ifp->if_index];
+ *icmp6_ifstat[index];
break;
case SIOCGIFALIFETIME_IN6:
/* for sanity */
if (ia->ia6_lifetime.ia6t_vltime != ND6_INFINITE_LIFETIME) {
ia->ia6_lifetime.ia6t_expire =
- time_second + ia->ia6_lifetime.ia6t_vltime;
+ timenow.tv_sec + ia->ia6_lifetime.ia6t_vltime;
} else
ia->ia6_lifetime.ia6t_expire = 0;
if (ia->ia6_lifetime.ia6t_pltime != ND6_INFINITE_LIFETIME) {
ia->ia6_lifetime.ia6t_preferred =
- time_second + ia->ia6_lifetime.ia6t_pltime;
+ timenow.tv_sec + ia->ia6_lifetime.ia6t_pltime;
} else
ia->ia6_lifetime.ia6t_preferred = 0;
break;
case SIOCAIFADDR_IN6:
{
- int i, error = 0;
+ int i;
struct nd_prefix pr0, *pr;
- if (dlil_find_dltag(ifp->if_family, ifp->if_unit, PF_INET6, &dl_tag) == EPROTONOSUPPORT) {
- /* Address is added without previous IPv6 configurator support (gif, stf etc...) */
- if (error = dlil_plumb_protocol(PF_INET6, ifp, &dl_tag)) {
+ /* Attempt to attache the protocol, in case it isn't attached */
+ error = dlil_plumb_protocol(PF_INET6, ifp);
+ if (error) {
+ if (error != EEXIST) {
printf("SIOCAIFADDR_IN6: %s can't plumb protocol error=%d\n",
if_name(ifp), error);
- return (error);
+ goto ioctl_cleanup;
}
+
+ /* Ignore, EEXIST */
+ error = 0;
+ }
+ else {
+ /* PF_INET6 wasn't previously attached */
in6_if_up(ifp, NULL);
}
-
/*
* first, make or update the interface address structure,
* and link it to the list.
*/
if ((error = in6_update_ifa(ifp, ifra, ia)) != 0)
- return(error);
+ goto ioctl_cleanup;
/*
* then, make the prefix on-link on the interface.
* interface route.
*/
if ((error = nd6_prelist_add(&pr0, NULL, &pr)) != 0)
- return(error);
+ goto ioctl_cleanup;
if (pr == NULL) {
log(LOG_ERR, "nd6_prelist_add succedded but "
"no prefix\n");
- return(EINVAL); /* XXX panic here? */
+ error = EINVAL;
+ goto ioctl_cleanup;
}
}
if ((ia = in6ifa_ifpwithaddr(ifp, &ifra->ifra_addr.sin6_addr))
* addresses, that is, this address might make
* other addresses detached.
*/
- pfxlist_onlink_check();
- in6_post_msg(ifp, KEV_INET6_NEW_USER_ADDR, ia);
+ pfxlist_onlink_check(0);
}
break;
}
purgeaddr:
- in6_purgeaddr(&ia->ia_ifa);
+ in6_purgeaddr(&ia->ia_ifa, 0);
break;
}
default:
#ifdef __APPLE__
- error = dlil_ioctl(PF_INET6, ifp, cmd, (caddr_t)data);
- return error;
-
+ error = dlil_ioctl(PF_INET6, ifp, cmd, (caddr_t)data);
+ goto ioctl_cleanup;
#else
if (ifp == NULL || ifp->if_ioctl == 0)
return(EOPNOTSUPP);
return((*ifp->if_ioctl)(ifp, cmd, data));
#endif
}
-
- return(0);
+ioctl_cleanup:
+ return error;
}
/*
struct in6_ifaddr *oia;
struct sockaddr_in6 dst6;
struct in6_addrlifetime *lt;
+ struct timeval timenow;
+
+ lck_mtx_assert(nd6_mutex, LCK_MTX_ASSERT_NOTOWNED);
/* Validate parameters */
if (ifp == NULL || ifra == NULL) /* this maybe redundant */
return(EINVAL);
*/
if (ifra->ifra_prefixmask.sin6_len > sizeof(struct sockaddr_in6))
return(EINVAL);
+ /*
+ * Set the address family value for the mask if it was not set.
+ * Radar 3899482.
+ */
+ if (ifra->ifra_prefixmask.sin6_len == sizeof(struct sockaddr_in6) &&
+ ifra->ifra_prefixmask.sin6_family == 0) {
+ ifra->ifra_prefixmask.sin6_family = AF_INET6;
+ }
/*
* Because the IPv6 address architecture is classless, we require
* users to specify a (non 0) prefix length (mask) for a new address.
}
}
/* lifetime consistency check */
+
+ getmicrotime(&timenow);
lt = &ifra->ifra_lifetime;
if (lt->ia6t_vltime != ND6_INFINITE_LIFETIME
- && lt->ia6t_vltime + time_second < time_second) {
+ && lt->ia6t_vltime + timenow.tv_sec < timenow.tv_sec) {
return EINVAL;
}
if (lt->ia6t_vltime == 0) {
ip6_sprintf(&ifra->ifra_addr.sin6_addr));
}
if (lt->ia6t_pltime != ND6_INFINITE_LIFETIME
- && lt->ia6t_pltime + time_second < time_second) {
+ && lt->ia6t_pltime + timenow.tv_sec < timenow.tv_sec) {
return EINVAL;
}
ia = (struct in6_ifaddr *)
_MALLOC(sizeof(*ia), M_IFADDR, M_NOWAIT);
if (ia == NULL)
- return (ENOBUFS);
+ return ENOBUFS;
bzero((caddr_t)ia, sizeof(*ia));
/* Initialize the address and masks */
ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr;
= (struct sockaddr *)&ia->ia_prefixmask;
ia->ia_ifp = ifp;
- if ((oia = in6_ifaddr) != NULL) {
+ lck_mtx_lock(nd6_mutex);
+ if ((oia = in6_ifaddrs) != NULL) {
for ( ; oia->ia_next; oia = oia->ia_next)
continue;
oia->ia_next = ia;
} else
- in6_ifaddr = ia;
+ in6_ifaddrs = ia;
+ lck_mtx_unlock(nd6_mutex);
- TAILQ_INSERT_TAIL(&ifp->if_addrlist, &ia->ia_ifa,
- ifa_list);
+ ifnet_lock_exclusive(ifp);
+ if_attach_ifa(ifp, &ia->ia_ifa);
+ ifnet_lock_done(ifp);
}
/* set prefix mask */
iilen = (sizeof(ia->ia_prefixmask.sin6_addr) << 3) - plen;
if ((error = in6_prefix_add_ifid(iilen, ia)) != 0) {
- in6_purgeaddr((struct ifaddr *)ia);
+ in6_purgeaddr((struct ifaddr *)ia, 0);
return(error);
}
}
llsol.s6_addr32[3] =
ifra->ifra_addr.sin6_addr.s6_addr32[3];
llsol.s6_addr8[12] = 0xff;
- (void)in6_addmulti(&llsol, ifp, &error);
+ (void)in6_addmulti(&llsol, ifp, &error, 0);
if (error != 0) {
log(LOG_WARNING,
"in6_update_ifa: addmulti failed for "
"%s on %s (errno=%d)\n",
ip6_sprintf(&llsol), if_name(ifp),
error);
- in6_purgeaddr((struct ifaddr *)ia);
+ in6_purgeaddr((struct ifaddr *)ia, 0);
return(error);
}
}
mltaddr.sin6_addr = in6addr_linklocal_allnodes;
mltaddr.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
+ ifnet_lock_shared(ifp);
IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m);
+ ifnet_lock_done(ifp);
if (in6m == NULL) {
rtrequest(RTM_ADD,
(struct sockaddr *)&mltaddr,
(struct sockaddr *)&mltmask,
RTF_UP|RTF_CLONING, /* xxx */
(struct rtentry **)0);
- (void)in6_addmulti(&mltaddr.sin6_addr, ifp, &error);
+ (void)in6_addmulti(&mltaddr.sin6_addr, ifp, &error, 0);
if (error != 0) {
log(LOG_WARNING,
"in6_update_ifa: addmulti failed for "
#define hostnamelen strlen(hostname)
if (in6_nigroup(ifp, hostname, hostnamelen, &mltaddr.sin6_addr)
== 0) {
+ ifnet_lock_shared(ifp);
IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m);
+ ifnet_lock_done(ifp);
if (in6m == NULL && ia != NULL) {
(void)in6_addmulti(&mltaddr.sin6_addr,
- ifp, &error);
+ ifp, &error, 0);
if (error != 0) {
log(LOG_WARNING, "in6_update_ifa: "
"addmulti failed for "
mltaddr.sin6_addr = in6addr_nodelocal_allnodes;
+ ifnet_lock_shared(ifp);
IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m);
+ ifnet_lock_done(ifp);
if (in6m == NULL && ia_loop != NULL) {
rtrequest(RTM_ADD,
(struct sockaddr *)&mltaddr,
RTF_UP,
(struct rtentry **)0);
(void)in6_addmulti(&mltaddr.sin6_addr, ifp,
- &error);
+ &error, 0);
if (error != 0) {
log(LOG_WARNING, "in6_update_ifa: "
"addmulti failed for %s on %s "
/* for sanity */
if (ia->ia6_lifetime.ia6t_vltime != ND6_INFINITE_LIFETIME) {
ia->ia6_lifetime.ia6t_expire =
- time_second + ia->ia6_lifetime.ia6t_vltime;
+ timenow.tv_sec + ia->ia6_lifetime.ia6t_vltime;
} else
ia->ia6_lifetime.ia6t_expire = 0;
if (ia->ia6_lifetime.ia6t_pltime != ND6_INFINITE_LIFETIME) {
ia->ia6_lifetime.ia6t_preferred =
- time_second + ia->ia6_lifetime.ia6t_pltime;
+ timenow.tv_sec + ia->ia6_lifetime.ia6t_pltime;
} else
ia->ia6_lifetime.ia6t_preferred = 0;
* anyway.
*/
if (hostIsNew)
- in6_unlink_ifa(ia, ifp);
+ in6_unlink_ifa(ia, ifp, 0);
return(error);
}
void
-in6_purgeaddr(ifa)
- struct ifaddr *ifa;
+in6_purgeaddr(
+ struct ifaddr *ifa, int nd6_locked)
{
struct ifnet *ifp = ifa->ifa_ifp;
struct in6_ifaddr *ia = (struct in6_ifaddr *) ifa;
}
/* Remove ownaddr's loopback rtentry, if it exists. */
- in6_ifremloop(&(ia->ia_ifa));
+ in6_ifremloop(&(ia->ia_ifa), nd6_locked);
if (ifp->if_flags & IFF_MULTICAST) {
/*
ia->ia_addr.sin6_addr.s6_addr32[3];
llsol.s6_addr8[12] = 0xff;
+ ifnet_lock_shared(ifp);
IN6_LOOKUP_MULTI(llsol, ifp, in6m);
+ ifnet_lock_done(ifp);
if (in6m)
- in6_delmulti(in6m);
+ in6_delmulti(in6m, nd6_locked);
}
+ in6_unlink_ifa(ia, ifp, nd6_locked);
in6_post_msg(ifp, KEV_INET6_ADDR_DELETED, ia);
- in6_unlink_ifa(ia, ifp);
}
static void
-in6_unlink_ifa(ia, ifp)
+in6_unlink_ifa(ia, ifp, nd6_locked)
struct in6_ifaddr *ia;
struct ifnet *ifp;
+ int nd6_locked;
{
int plen, iilen;
struct in6_ifaddr *oia;
- int s = splnet();
- TAILQ_REMOVE(&ifp->if_addrlist, &ia->ia_ifa, ifa_list);
+ ifnet_lock_exclusive(ifp);
+ if_detach_ifa(ifp, &ia->ia_ifa);
+ ifnet_lock_done(ifp);
+ if (!nd6_locked)
+ lck_mtx_lock(nd6_mutex);
oia = ia;
- if (oia == (ia = in6_ifaddr))
- in6_ifaddr = ia->ia_next;
+ if (oia == (ia = in6_ifaddrs))
+ in6_ifaddrs = ia->ia_next;
else {
while (ia->ia_next && (ia->ia_next != oia))
ia = ia->ia_next;
printf("Couldn't unlink in6_ifaddr from in6_ifaddr\n");
}
}
-
if (oia->ia6_ifpr) { /* check for safety */
plen = in6_mask2len(&oia->ia_prefixmask.sin6_addr, NULL);
iilen = (sizeof(oia->ia_prefixmask.sin6_addr) << 3) - plen;
oia->ia6_ndpr = NULL;
}
- pfxlist_onlink_check();
+ pfxlist_onlink_check(1);
}
+ if (!nd6_locked)
+ lck_mtx_unlock(nd6_mutex);
+
/*
- * release another refcnt for the link from in6_ifaddr.
+ * release another refcnt for the link from in6_ifaddrs.
* Note that we should decrement the refcnt at least once for all *BSD.
*/
ifafree(&oia->ia_ifa);
- splx(s);
}
void
in6_purgeif(ifp)
struct ifnet *ifp;
{
- struct ifaddr *ifa, *nifa = NULL;
+ struct in6_ifaddr *ia, *nia = NULL;
if (ifp == NULL || &ifp->if_addrlist == NULL)
return;
- for (ifa = TAILQ_FIRST(&ifp->if_addrlist); ifa != NULL; ifa = nifa)
+ lck_mtx_lock(nd6_mutex);
+ for (ia = in6_ifaddrs; ia != NULL; ia = nia)
{
- nifa = TAILQ_NEXT(ifa, ifa_list);
- if (ifa->ifa_addr == NULL)
+ nia = ia->ia_next;
+ if (ia->ia_ifa.ifa_ifp != ifp)
continue;
- if (ifa->ifa_addr->sa_family != AF_INET6)
- continue;
- in6_purgeaddr(ifa);
+ in6_purgeaddr(&ia->ia_ifa, 1);
}
+ lck_mtx_unlock(nd6_mutex);
in6_ifdetach(ifp);
}
case SIOCALIFADDR:
{
struct in6_aliasreq ifra;
- struct in6_addr *hostid = NULL;
+ struct in6_addr hostid;
int prefixlen;
+ int hostid_found = 0;
if ((iflr->flags & IFLR_PREFIX) != 0) {
struct sockaddr_in6 *sin6;
ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp, 0);
if (!ifa)
return EADDRNOTAVAIL;
- hostid = IFA_IN6(ifa);
+ hostid = *IFA_IN6(ifa);
+ hostid_found = 1;
/* prefixlen must be <= 64. */
if (64 < iflr->prefixlen)
bcopy(&iflr->addr, &ifra.ifra_addr,
((struct sockaddr *)&iflr->addr)->sa_len);
- if (hostid) {
+ if (hostid_found) {
/* fill in hostid part */
ifra.ifra_addr.sin6_addr.s6_addr32[2] =
- hostid->s6_addr32[2];
+ hostid.s6_addr32[2];
ifra.ifra_addr.sin6_addr.s6_addr32[3] =
- hostid->s6_addr32[3];
+ hostid.s6_addr32[3];
}
if (((struct sockaddr *)&iflr->dstaddr)->sa_family) { /*XXX*/
bcopy(&iflr->dstaddr, &ifra.ifra_dstaddr,
((struct sockaddr *)&iflr->dstaddr)->sa_len);
- if (hostid) {
+ if (hostid_found) {
ifra.ifra_dstaddr.sin6_addr.s6_addr32[2] =
- hostid->s6_addr32[2];
+ hostid.s6_addr32[2];
ifra.ifra_dstaddr.sin6_addr.s6_addr32[3] =
- hostid->s6_addr32[3];
+ hostid.s6_addr32[3];
}
}
}
}
+ ifnet_lock_shared(ifp);
TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list)
{
if (ifa->ifa_addr->sa_family != AF_INET6)
if (IN6_ARE_ADDR_EQUAL(&candidate, &match))
break;
}
+ ifnet_lock_done(ifp);
if (!ifa)
return EADDRNOTAVAIL;
ia = ifa2ia6(ifa);
int newhost;
{
int error = 0, plen, ifacount = 0;
- int s = splimp();
struct ifaddr *ifa;
/*
* if this is its first address,
* and to validate the address if necessary.
*/
+ ifnet_lock_shared(ifp);
TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list)
{
if (ifa->ifa_addr == NULL)
continue;
ifacount++;
}
+ ifnet_lock_done(ifp);
ia->ia_addr = *sin6;
if (ifacount <= 1 &&
-#ifdef __APPLE__
(error = dlil_ioctl(PF_INET6, ifp, SIOCSIFADDR, (caddr_t)ia))) {
if (error) {
- splx(s);
return(error);
}
}
-#else
- ifp->if_ioctl && (error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia))) {
- splx(s);
- return(error);
- }
-#endif
- splx(s);
ia->ia_ifa.ifa_metric = ifp->if_metric;
* given interface.
*/
struct in6_multi *
-in6_addmulti(maddr6, ifp, errorp)
+in6_addmulti(maddr6, ifp, errorp, nd6_locked)
struct in6_addr *maddr6;
struct ifnet *ifp;
int *errorp;
+ int nd6_locked;
{
struct in6_multi *in6m;
struct sockaddr_in6 sin6;
struct ifmultiaddr *ifma;
- int s = splnet();
*errorp = 0;
sin6.sin6_addr = *maddr6;
*errorp = if_addmulti(ifp, (struct sockaddr *)&sin6, &ifma);
if (*errorp) {
- splx(s);
return 0;
}
at interrupt time? If so, need to fix if_addmulti. XXX */
in6m = (struct in6_multi *)_MALLOC(sizeof(*in6m), M_IPMADDR, M_NOWAIT);
if (in6m == NULL) {
- splx(s);
return (NULL);
}
in6m->in6m_ifp = ifp;
in6m->in6m_ifma = ifma;
ifma->ifma_protospec = in6m;
+ if (nd6_locked == 0)
+ lck_mtx_lock(nd6_mutex);
LIST_INSERT_HEAD(&in6_multihead, in6m, in6m_entry);
+ if (nd6_locked == 0)
+ lck_mtx_unlock(nd6_mutex);
/*
* Let MLD6 know that we have joined a new IP6 multicast
* group.
*/
mld6_start_listening(in6m);
- splx(s);
return(in6m);
}
* Delete a multicast address record.
*/
void
-in6_delmulti(in6m)
- struct in6_multi *in6m;
+in6_delmulti(
+ struct in6_multi *in6m, int nd6locked)
{
struct ifmultiaddr *ifma = in6m->in6m_ifma;
- int s = splnet();
- if (ifma && ifma->ifma_refcount == 1) {
+ if (ifma && ifma->ifma_usecount == 1) {
/*
* No remaining claims to this record; let MLD6 know
* that we are leaving the multicast group.
*/
mld6_stop_listening(in6m);
ifma->ifma_protospec = 0;
+ if (nd6locked == 0)
+ lck_mtx_lock(nd6_mutex);
LIST_REMOVE(in6m, in6m_entry);
+ if (nd6locked == 0)
+ lck_mtx_unlock(nd6_mutex);
FREE(in6m, M_IPMADDR);
}
/* XXX - should be separate API for when we have an ifma? */
- if (ifma)
- if_delmultiaddr(ifma);
- splx(s);
+ if (ifma) {
+ if_delmultiaddr(ifma, 0);
+ ifma_release(ifma);
+ }
}
/*
{
struct ifaddr *ifa;
+ ifnet_lock_shared(ifp);
TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list)
{
if (ifa->ifa_addr == NULL)
break;
}
}
+ ifnet_lock_done(ifp);
return((struct in6_ifaddr *)ifa);
}
-
/*
* find the internet address corresponding to a given interface and address.
*/
{
struct ifaddr *ifa;
+ ifnet_lock_shared(ifp);
TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list)
{
if (ifa->ifa_addr == NULL)
if (IN6_ARE_ADDR_EQUAL(addr, IFA_IN6(ifa)))
break;
}
+ ifnet_lock_done(ifp);
return((struct in6_ifaddr *)ifa);
}
if (IN6_IS_ADDR_LOOPBACK(in6) || IN6_IS_ADDR_LINKLOCAL(in6))
return 1;
- for (ia = in6_ifaddr; ia; ia = ia->ia_next)
+ lck_mtx_lock(nd6_mutex);
+ for (ia = in6_ifaddrs; ia; ia = ia->ia_next)
if (IN6_ARE_MASKED_ADDR_EQUAL(in6, &ia->ia_addr.sin6_addr,
- &ia->ia_prefixmask.sin6_addr))
+ &ia->ia_prefixmask.sin6_addr)) {
+ lck_mtx_unlock(nd6_mutex);
return 1;
+ }
+ lck_mtx_unlock(nd6_mutex);
return (0);
}
{
struct in6_ifaddr *ia;
- for (ia = in6_ifaddr; ia; ia = ia->ia_next) {
+ lck_mtx_lock(nd6_mutex);
+ for (ia = in6_ifaddrs; ia; ia = ia->ia_next) {
if (IN6_ARE_ADDR_EQUAL(&ia->ia_addr.sin6_addr,
&sa6->sin6_addr) &&
#if SCOPEDROUTING
ia->ia_addr.sin6_scope_id == sa6->sin6_scope_id &&
#endif
- (ia->ia6_flags & IN6_IFF_DEPRECATED) != 0)
+ (ia->ia6_flags & IN6_IFF_DEPRECATED) != 0) {
+ lck_mtx_unlock(nd6_mutex);
return(1); /* true */
+ }
/* XXX: do we still have to go thru the rest of the list? */
}
+ lck_mtx_unlock(nd6_mutex);
return(0); /* false */
}
if (bcmp(&p1->s6_addr, &p2->s6_addr, bytelen))
return(0);
- if (p1->s6_addr[bytelen] >> (8 - bitlen) !=
+ if (bitlen != 0 &&
+ p1->s6_addr[bytelen] >> (8 - bitlen) !=
p2->s6_addr[bytelen] >> (8 - bitlen))
return(0);
* return the best address out of the same scope
*/
struct in6_ifaddr *
-in6_ifawithscope(oifp, dst)
- struct ifnet *oifp;
- struct in6_addr *dst;
+in6_ifawithscope(
+ struct ifnet *oifp,
+ struct in6_addr *dst)
{
int dst_scope = in6_addrscope(dst), src_scope, best_scope = 0;
int blen = -1;
* Comparing an interface with the outgoing interface will be done
* only at the final stage of tiebreaking.
*/
- for (ifp = TAILQ_FIRST(&ifnet); ifp; ifp = TAILQ_NEXT(ifp, if_list))
- {
+ ifnet_head_lock_shared();
+ TAILQ_FOREACH(ifp, &ifnet_head, if_list) {
/*
* We can never take an address that breaks the scope zone
* of the destination.
if (in6_addr2scopeid(ifp, dst) != in6_addr2scopeid(oifp, dst))
continue;
+ ifnet_lock_shared(ifp);
TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list)
{
int tlen = -1, dscopecmp, bscopecmp, matchcmp;
goto replace; /* (9) */
replace:
+ ifaref(ifa);
+ if (ifa_best)
+ ifafree(&ifa_best->ia_ifa);
ifa_best = (struct in6_ifaddr *)ifa;
blen = tlen >= 0 ? tlen :
in6_matchlen(IFA_IN6(ifa), dst);
best_scope = in6_addrscope(&ifa_best->ia_addr.sin6_addr);
}
+ ifnet_lock_done(ifp);
}
+ ifnet_head_done();
/* count statistics for future improvements */
if (ifa_best == NULL)
* found, return the first valid address from designated IF.
*/
struct in6_ifaddr *
-in6_ifawithifp(ifp, dst)
- struct ifnet *ifp;
- struct in6_addr *dst;
+in6_ifawithifp(
+ struct ifnet *ifp,
+ struct in6_addr *dst)
{
int dst_scope = in6_addrscope(dst), blen = -1, tlen;
struct ifaddr *ifa;
* If two or more, return one which matches the dst longest.
* If none, return one of global addresses assigned other ifs.
*/
+ ifnet_lock_shared(ifp);
TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list)
{
if (ifa->ifa_addr->sa_family != AF_INET6)
besta = (struct in6_ifaddr *)ifa;
}
}
- if (besta)
+ if (besta) {
+ ifnet_lock_done(ifp);
return(besta);
+ }
TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list)
{
dep[1] = (struct in6_ifaddr *)ifa;
continue;
}
-
+
+ ifnet_lock_done(ifp);
return (struct in6_ifaddr *)ifa;
}
+ ifnet_lock_done(ifp);
/* use the last-resort values, that are, deprecated addresses */
if (dep[0])
* perform DAD when interface becomes IFF_UP.
*/
void
-in6_if_up(ifp, ifra)
- struct ifnet *ifp;
- struct in6_aliasreq *ifra;
+in6_if_up(
+ struct ifnet *ifp,
+ struct in6_aliasreq *ifra)
{
struct ifaddr *ifa;
struct in6_ifaddr *ia;
in6_ifattach(ifp, NULL, ifra);
dad_delay = 0;
+ ifnet_lock_exclusive(ifp);
TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list)
{
if (ifa->ifa_addr->sa_family != AF_INET6)
if (ia->ia6_flags & IN6_IFF_TENTATIVE)
nd6_dad_start(ifa, &dad_delay);
}
+ ifnet_lock_done(ifp);
}
int
-in6if_do_dad(ifp)
- struct ifnet *ifp;
+in6if_do_dad(
+ struct ifnet *ifp)
{
if ((ifp->if_flags & IFF_LOOPBACK) != 0)
return(0);
unsigned long maxmtu = 0;
struct ifnet *ifp;
- for (ifp = TAILQ_FIRST(&ifnet); ifp; ifp = TAILQ_NEXT(ifp, if_list))
- {
+ ifnet_head_lock_shared();
+ TAILQ_FOREACH(ifp, &ifnet_head, if_list) {
if ((ifp->if_flags & IFF_LOOPBACK) == 0 &&
nd_ifinfo[ifp->if_index].linkmtu > maxmtu)
maxmtu = nd_ifinfo[ifp->if_index].linkmtu;
}
+ ifnet_head_done();
if (maxmtu) /* update only when maxmtu is positive */
in6_maxmtu = maxmtu;
}