/*
- * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2003-2008 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
#include <kern/kern_types.h>
#include <kern/zalloc.h>
-#include "faith.h"
-#if defined(NFAITH) && NFAITH > 0
-#include <net/if_faith.h>
-#endif
-
#if IPSEC
#include <netinet6/ipsec.h>
#if INET6
#include <netinet6/ah6.h>
#endif
#include <netkey/key.h>
-extern lck_mtx_t *sadb_mutex;
#endif /* IPSEC */
struct in6_addr zeroin6_addr;
+/*
+ in6_pcblookup_local_and_cleanup does everything
+ in6_pcblookup_local does but it checks for a socket
+ that's going away. Since we know that the lock is
+ held read+write when this function is called, we
+ can safely dispose of this socket like the slow
+ timer would usually do and return NULL. This is
+ great for bind.
+*/
+static struct inpcb*
+in6_pcblookup_local_and_cleanup(
+ struct inpcbinfo *pcbinfo,
+ struct in6_addr *laddr,
+ u_int lport_arg,
+ int wild_okay)
+{
+ struct inpcb *inp;
+
+ /* Perform normal lookup */
+ inp = in6_pcblookup_local(pcbinfo, laddr, lport_arg, wild_okay);
+
+ /* Check if we found a match but it's waiting to be disposed */
+ if (inp && inp->inp_wantcnt == WNT_STOPUSING) {
+ struct socket *so = inp->inp_socket;
+
+ lck_mtx_lock(inp->inpcb_mtx);
+
+ if (so->so_usecount == 0) {
+ if (inp->inp_state != INPCB_STATE_DEAD)
+ in6_pcbdetach(inp);
+ in_pcbdispose(inp);
+ inp = NULL;
+ }
+ else {
+ lck_mtx_unlock(inp->inpcb_mtx);
+ }
+ }
+
+ return inp;
+}
int
in6_pcbbind(
struct inpcb *inp,
struct inpcb *t;
/* GROSS */
- if (ntohs(lport) < IPV6PORT_RESERVED && p &&
+ if (ntohs(lport) < IPV6PORT_RESERVED &&
((so->so_state & SS_PRIV) == 0)) {
lck_rw_done(pcbinfo->mtx);
socket_lock(so, 0);
if (so->so_uid &&
!IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) {
- t = in6_pcblookup_local(pcbinfo,
+ t = in6_pcblookup_local_and_cleanup(pcbinfo,
&sin6->sin6_addr, lport,
INPLOOKUP_WILDCARD);
if (t &&
!IN6_IS_ADDR_UNSPECIFIED(&t->in6p_laddr) ||
(t->inp_socket->so_options &
SO_REUSEPORT) == 0) &&
- so->so_uid != t->inp_socket->so_uid) {
+ (so->so_uid != t->inp_socket->so_uid) &&
+ ((t->inp_socket->so_flags & SOF_REUSESHAREUID) == 0)) {
lck_rw_done(pcbinfo->mtx);
socket_lock(so, 0);
return (EADDRINUSE);
struct sockaddr_in sin;
in6_sin6_2_sin(&sin, sin6);
- t = in_pcblookup_local(pcbinfo,
+ t = in_pcblookup_local_and_cleanup(pcbinfo,
sin.sin_addr, lport,
INPLOOKUP_WILDCARD);
- if (t &&
+ if (t && (t->inp_socket->so_options & SO_REUSEPORT) == 0 &&
(so->so_uid !=
t->inp_socket->so_uid) &&
(ntohl(t->inp_laddr.s_addr) !=
}
}
}
- t = in6_pcblookup_local(pcbinfo, &sin6->sin6_addr,
+ t = in6_pcblookup_local_and_cleanup(pcbinfo, &sin6->sin6_addr,
lport, wild);
if (t && (reuseport & t->inp_socket->so_options) == 0) {
lck_rw_done(pcbinfo->mtx);
struct sockaddr_in sin;
in6_sin6_2_sin(&sin, sin6);
- t = in_pcblookup_local(pcbinfo, sin.sin_addr,
+ t = in_pcblookup_local_and_cleanup(pcbinfo, sin.sin_addr,
lport, wild);
if (t &&
(reuseport & t->inp_socket->so_options)
}
}
lck_rw_done(pcbinfo->mtx);
+ sflt_notify(so, sock_evt_bound, NULL);
return(0);
}
*/
}
+ /* XXX: what is the point in doing this? */
if (inp->in6p_route.ro_rt)
ifp = inp->in6p_route.ro_rt->rt_ifp;
inp->inp_lport, 0, NULL);
socket_lock(inp->inp_socket, 0);
if (pcb != NULL) {
- in_pcb_checkstate(pcb, WNT_RELEASE, 0);
+ in_pcb_checkstate(pcb, WNT_RELEASE, pcb == inp ? 1 : 0);
return (EADDRINUSE);
}
if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)) {
return (0);
}
-#if 0
-/*
- * Return an IPv6 address, which is the most appropriate for given
- * destination and user specified options.
- * If necessary, this function lookups the routing table and return
- * an entry to the caller for later use.
- */
-struct in6_addr *
-in6_selectsrc(
- struct sockaddr_in6 *dstsock,
- struct ip6_pktopts *opts,
- struct ip6_moptions *mopts,
- struct route_in6 *ro,
- struct in6_addr *laddr,
- struct in6_addr *src_storage,
- int *errorp)
-{
- struct in6_addr *dst;
- struct in6_ifaddr *ia6 = 0;
- struct in6_pktinfo *pi = NULL;
-
- dst = &dstsock->sin6_addr;
- *errorp = 0;
-
- /*
- * If the source address is explicitly specified by the caller,
- * use it.
- */
- if (opts && (pi = opts->ip6po_pktinfo) &&
- !IN6_IS_ADDR_UNSPECIFIED(&pi->ipi6_addr))
- return(&pi->ipi6_addr);
-
- /*
- * If the source address is not specified but the socket(if any)
- * is already bound, use the bound address.
- */
- if (laddr && !IN6_IS_ADDR_UNSPECIFIED(laddr))
- return(laddr);
-
- /*
- * If the caller doesn't specify the source address but
- * the outgoing interface, use an address associated with
- * the interface.
- */
- if (pi && pi->ipi6_ifindex) {
- /* XXX boundary check is assumed to be already done. */
- ia6 = in6_ifawithscope(ifindex2ifnet[pi->ipi6_ifindex],
- dst);
- if (ia6 == 0) {
- *errorp = EADDRNOTAVAIL;
- return(0);
- }
- *src_storage = satosin6(&ia6->ia_addr)->sin6_addr;
- ifafree(&ia6->ia_ifa);
- return(src_storage);
- }
-
- /*
- * If the destination address is a link-local unicast address or
- * a multicast address, and if the outgoing interface is specified
- * by the sin6_scope_id filed, use an address associated with the
- * interface.
- * XXX: We're now trying to define more specific semantics of
- * sin6_scope_id field, so this part will be rewritten in
- * the near future.
- */
- if ((IN6_IS_ADDR_LINKLOCAL(dst) || IN6_IS_ADDR_MULTICAST(dst)) &&
- dstsock->sin6_scope_id) {
- /*
- * I'm not sure if boundary check for scope_id is done
- * somewhere...
- */
- if (dstsock->sin6_scope_id < 0 ||
- if_index < dstsock->sin6_scope_id) {
- *errorp = ENXIO; /* XXX: better error? */
- return(0);
- }
- ia6 = in6_ifawithscope(ifindex2ifnet[dstsock->sin6_scope_id],
- dst);
- if (ia6 == 0) {
- *errorp = EADDRNOTAVAIL;
- return(0);
- }
- *src_storage = satosin6(&ia6->ia_addr)->sin6_addr;
- ifafree(&ia6->ia_ifa);
- return(src_storage);
- }
-
- /*
- * If the destination address is a multicast address and
- * the outgoing interface for the address is specified
- * by the caller, use an address associated with the interface.
- * There is a sanity check here; if the destination has node-local
- * scope, the outgoing interfacde should be a loopback address.
- * Even if the outgoing interface is not specified, we also
- * choose a loopback interface as the outgoing interface.
- */
- if (IN6_IS_ADDR_MULTICAST(dst)) {
- struct ifnet *ifp = mopts ? mopts->im6o_multicast_ifp : NULL;
-
- if (ifp == NULL && IN6_IS_ADDR_MC_NODELOCAL(dst)) {
- ifp = &loif[0];
- }
-
- if (ifp) {
- ia6 = in6_ifawithscope(ifp, dst);
- if (ia6 == 0) {
- *errorp = EADDRNOTAVAIL;
- return(0);
- }
- *src_storage = ia6->ia_addr.sin6_addr;
- ifafree(&ia6->ia_ifa);
- return(src_storage);
- }
- }
-
- /*
- * If the next hop address for the packet is specified
- * by caller, use an address associated with the route
- * to the next hop.
- */
- {
- struct sockaddr_in6 *sin6_next;
- struct rtentry *rt;
-
- if (opts && opts->ip6po_nexthop) {
- sin6_next = satosin6(opts->ip6po_nexthop);
- rt = nd6_lookup(&sin6_next->sin6_addr, 1, NULL, 0);
- if (rt) {
- ia6 = in6_ifawithscope(rt->rt_ifp, dst);
- if (ia6 == 0) {
- ifaref(&rt->rt_ifa);
- ia6 = ifatoia6(rt->rt_ifa);
- }
- }
- if (ia6 == 0) {
- *errorp = EADDRNOTAVAIL;
- return(0);
- }
- *src_storage = satosin6(&ia6->ia_addr)->sin6_addr;
- ifaref(&rt->rt_ifa);
- return(src_storage);
- }
- }
-
- /*
- * If route is known or can be allocated now,
- * our src addr is taken from the i/f, else punt.
- */
- if (ro) {
- if (ro->ro_rt &&
- !IN6_ARE_ADDR_EQUAL(&satosin6(&ro->ro_dst)->sin6_addr, dst)) {
- rtfree(ro->ro_rt);
- ro->ro_rt = (struct rtentry *)0;
- }
- if (ro->ro_rt == (struct rtentry *)0 ||
- ro->ro_rt->rt_ifp == (struct ifnet *)0) {
- struct sockaddr_in6 *dst6;
-
- /* No route yet, so try to acquire one */
- bzero(&ro->ro_dst, sizeof(struct sockaddr_in6));
- dst6 = (struct sockaddr_in6 *)&ro->ro_dst;
- dst6->sin6_family = AF_INET6;
- dst6->sin6_len = sizeof(struct sockaddr_in6);
- dst6->sin6_addr = *dst;
- if (IN6_IS_ADDR_MULTICAST(dst)) {
- ro->ro_rt = rtalloc1(&((struct route *)ro)
- ->ro_dst, 0, 0UL);
- } else {
- rtalloc((struct route *)ro);
- }
- }
-
- /*
- * in_pcbconnect() checks out IFF_LOOPBACK to skip using
- * the address. But we don't know why it does so.
- * It is necessary to ensure the scope even for lo0
- * so doesn't check out IFF_LOOPBACK.
- */
-
- if (ro->ro_rt) {
- ia6 = in6_ifawithscope(ro->ro_rt->rt_ifa->ifa_ifp, dst);
- if (ia6 == 0) { /* xxx scope error ?*/
- ifaref(ro->ro_rt->rt_ifa);
- ia6 = ifatoia6(ro->ro_rt->rt_ifa);
- }
- }
- if (ia6 == 0) {
- *errorp = EHOSTUNREACH; /* no route */
- return(0);
- }
- *src_storage = satosin6(&ia6->ia_addr)->sin6_addr;
- ifaref(&rt->rt_ifa);
- return(src_storage);
- }
-
- *errorp = EADDRNOTAVAIL;
- return(0);
-}
-
-/*
- * Default hop limit selection. The precedence is as follows:
- * 1. Hoplimit valued specified via ioctl.
- * 2. (If the outgoing interface is detected) the current
- * hop limit of the interface specified by router advertisement.
- * 3. The system default hoplimit.
-*/
-int
-in6_selecthlim(
- struct in6pcb *in6p,
- struct ifnet *ifp)
-{
- if (in6p && in6p->in6p_hops >= 0)
- return(in6p->in6p_hops);
- else if (ifp)
- return(nd_ifinfo[ifp->if_index].chlim);
- else
- return(ip6_defhlim);
-}
-#endif
-
void
in6_pcbdisconnect(inp)
struct inpcb *inp;
#if IPSEC
if (inp->in6p_sp != NULL) {
- lck_mtx_lock(sadb_mutex);
ipsec6_delete_pcbpolicy(inp);
- lck_mtx_unlock(sadb_mutex);
}
#endif /* IPSEC */
if (in_pcb_checkstate(inp, WNT_STOPUSING, 1) != WNT_STOPUSING)
- printf("in6_pcbdetach so=%x can't be marked dead ok\n", so);
+ printf("in6_pcbdetach so=%p can't be marked dead ok\n", so);
inp->inp_state = INPCB_STATE_DEAD;
m_freem(inp->in6p_options);
ip6_freepcbopts(inp->in6p_outputopts);
ip6_freemoptions(inp->in6p_moptions);
- if (inp->in6p_route.ro_rt)
+ if (inp->in6p_route.ro_rt) {
rtfree(inp->in6p_route.ro_rt);
+ inp->in6p_route.ro_rt = NULL;
+ }
/* Check and free IPv4 related resources in case of mapped addr */
if (inp->inp_options)
(void)m_free(inp->inp_options);
struct sockaddr_in6 *sin6;
MALLOC(sin6, struct sockaddr_in6 *, sizeof *sin6, M_SONAME, M_WAITOK);
+ if (sin6 == NULL)
+ return NULL;
bzero(sin6, sizeof *sin6);
sin6->sin6_family = AF_INET6;
sin6->sin6_len = sizeof(*sin6);
MALLOC(sin6_p, struct sockaddr_in6 *, sizeof *sin6_p, M_SONAME,
M_WAITOK);
+ if (sin6_p == NULL)
+ return NULL;
in6_sin_2_v4mapsin6(&sin, sin6_p);
return (struct sockaddr *)sin6_p;
addr = inp->in6p_laddr;
*nam = in6_sockaddr(port, &addr);
+ if (*nam == NULL)
+ return ENOBUFS;
return 0;
}
addr = inp->in6p_faddr;
*nam = in6_sockaddr(port, &addr);
+ if (*nam == NULL)
+ return ENOBUFS;
return 0;
}
if (inp->inp_vflag & INP_IPV4) {
error = in_setsockaddr(so, nam);
if (error == 0)
- in6_sin_2_v4mapsin6_in_sock(nam);
- } else
- /* scope issues will be handled in in6_setsockaddr(). */
- error = in6_setsockaddr(so, nam);
-
+ error = in6_sin_2_v4mapsin6_in_sock(nam);
+ } else {
+ /* scope issues will be handled in in6_setsockaddr(). */
+ error = in6_setsockaddr(so, nam);
+ }
return error;
}
if (inp->inp_vflag & INP_IPV4) {
error = in_setpeeraddr(so, nam);
if (error == 0)
- in6_sin_2_v4mapsin6_in_sock(nam);
- } else
- /* scope issues will be handled in in6_setpeeraddr(). */
- error = in6_setpeeraddr(so, nam);
-
+ error = in6_sin_2_v4mapsin6_in_sock(nam);
+ } else {
+ /* scope issues will be handled in in6_setpeeraddr(). */
+ error = in6_setpeeraddr(so, nam);
+ }
return error;
}
struct rt_addrinfo info;
if ((rt = in6p->in6p_route.ro_rt) != NULL) {
- in6p->in6p_route.ro_rt = 0;
+ in6p->in6p_route.ro_rt = NULL;
+ RT_LOCK(rt);
bzero((caddr_t)&info, sizeof(info));
info.rti_info[RTAX_DST] =
(struct sockaddr *)&in6p->in6p_route.ro_dst;
info.rti_info[RTAX_GATEWAY] = rt->rt_gateway;
info.rti_info[RTAX_NETMASK] = rt_mask(rt);
- lck_mtx_lock(rt_mtx);
rt_missmsg(RTM_LOSING, &info, rt->rt_flags, 0);
- if (rt->rt_flags & RTF_DYNAMIC)
- (void)rtrequest_locked(RTM_DELETE, rt_key(rt),
- rt->rt_gateway, rt_mask(rt), rt->rt_flags,
- (struct rtentry **)0);
- else
+ if (rt->rt_flags & RTF_DYNAMIC) {
+ /*
+ * Prevent another thread from modifying rt_key,
+ * rt_gateway via rt_setgate() after the rt_lock
+ * is dropped by marking the route as defunct.
+ */
+ rt->rt_flags |= RTF_CONDEMNED;
+ RT_UNLOCK(rt);
+ (void) rtrequest(RTM_DELETE, rt_key(rt),
+ rt->rt_gateway, rt_mask(rt), rt->rt_flags, NULL);
+ } else {
+ RT_UNLOCK(rt);
+ }
/*
* A new route can be allocated
* the next time output is attempted.
*/
- rtfree_locked(rt);
- lck_mtx_unlock(rt_mtx);
+ rtfree(rt);
}
}
void
in6_rtchange(
struct inpcb *inp,
- int errno)
+ __unused int errno)
{
if (inp->in6p_route.ro_rt) {
rtfree(inp->in6p_route.ro_rt);
struct in6_addr *laddr,
u_int lport_arg,
int wildcard,
- struct ifnet *ifp)
+ __unused struct ifnet *ifp)
{
struct inpcbhead *head;
struct inpcb *inp;