X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/8f6c56a50524aa785f7e596d52dddfb331e18961..0b4c1975fb5e4eccf1012a35081f7e7799b81046:/bsd/netinet6/in6_pcb.c diff --git a/bsd/netinet6/in6_pcb.c b/bsd/netinet6/in6_pcb.c index 3c28774df..20f39a34d 100644 --- a/bsd/netinet6/in6_pcb.c +++ b/bsd/netinet6/in6_pcb.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2003-2008 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * @@ -121,11 +121,6 @@ #include #include -#include "faith.h" -#if defined(NFAITH) && NFAITH > 0 -#include -#endif - #if IPSEC #include #if INET6 @@ -136,11 +131,50 @@ #include #endif #include -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, @@ -228,7 +262,7 @@ in6_pcbbind( 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); @@ -237,7 +271,7 @@ in6_pcbbind( 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 && @@ -245,7 +279,8 @@ in6_pcbbind( !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); @@ -255,10 +290,10 @@ in6_pcbbind( 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) != @@ -272,7 +307,7 @@ in6_pcbbind( } } } - 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); @@ -284,7 +319,7 @@ in6_pcbbind( 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) @@ -319,6 +354,7 @@ in6_pcbbind( } } lck_rw_done(pcbinfo->mtx); + sflt_notify(so, sock_evt_bound, NULL); return(0); } @@ -389,6 +425,7 @@ in6_pcbladdr( */ } + /* XXX: what is the point in doing this? */ if (inp->in6p_route.ro_rt) ifp = inp->in6p_route.ro_rt->rt_ifp; @@ -427,7 +464,7 @@ in6_pcbconnect(inp, nam, p) 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)) { @@ -457,227 +494,6 @@ in6_pcbconnect(inp, nam, p) 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; @@ -707,14 +523,12 @@ in6_pcbdetach(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; @@ -726,8 +540,10 @@ in6_pcbdetach(inp) 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); @@ -745,6 +561,8 @@ in6_sockaddr(port, addr_p) 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); @@ -776,6 +594,8 @@ in6_v4mapsin6_sockaddr(port, addr_p) 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; @@ -808,6 +628,8 @@ in6_setsockaddr(so, nam) addr = inp->in6p_laddr; *nam = in6_sockaddr(port, &addr); + if (*nam == NULL) + return ENOBUFS; return 0; } @@ -828,6 +650,8 @@ in6_setpeeraddr(so, nam) addr = inp->in6p_faddr; *nam = in6_sockaddr(port, &addr); + if (*nam == NULL) + return ENOBUFS; return 0; } @@ -842,11 +666,11 @@ in6_mapped_sockaddr(struct socket *so, struct sockaddr **nam) 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; } @@ -861,11 +685,11 @@ in6_mapped_peeraddr(struct socket *so, struct sockaddr **nam) 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; } @@ -1115,25 +939,32 @@ in6_losing(in6p) 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); } } @@ -1144,7 +975,7 @@ in6_losing(in6p) void in6_rtchange( struct inpcb *inp, - int errno) + __unused int errno) { if (inp->in6p_route.ro_rt) { rtfree(inp->in6p_route.ro_rt); @@ -1167,7 +998,7 @@ in6_pcblookup_hash( struct in6_addr *laddr, u_int lport_arg, int wildcard, - struct ifnet *ifp) + __unused struct ifnet *ifp) { struct inpcbhead *head; struct inpcb *inp;