]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/netinet6/in6_pcb.c
xnu-4903.221.2.tar.gz
[apple/xnu.git] / bsd / netinet6 / in6_pcb.c
index 63beb9a91d689c698a4ddb88644748bc29c93b97..db72c5c35c402b26f016de7469c3ca00772948ab 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003-2013 Apple Inc. All rights reserved.
+ * Copyright (c) 2003-2017 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  *
@@ -163,7 +163,7 @@ in6_pcblookup_local_and_cleanup(struct inpcbinfo *pcbinfo,
        if (inp != NULL && inp->inp_wantcnt == WNT_STOPUSING) {
                struct socket *so = inp->inp_socket;
 
-               lck_mtx_lock(&inp->inpcb_mtx);
+               socket_lock(so, 0);
 
                if (so->so_usecount == 0) {
                        if (inp->inp_state != INPCB_STATE_DEAD)
@@ -171,7 +171,7 @@ in6_pcblookup_local_and_cleanup(struct inpcbinfo *pcbinfo,
                        in_pcbdispose(inp);     /* will unlock & destroy */
                        inp = NULL;
                } else {
-                       lck_mtx_unlock(&inp->inpcb_mtx);
+                       socket_unlock(so, 0);
                }
        }
 
@@ -191,17 +191,24 @@ in6_pcbbind(struct inpcb *inp, struct sockaddr *nam, struct proc *p)
        int wild = 0, reuseport = (so->so_options & SO_REUSEPORT);
        struct ifnet *outif = NULL;
        struct sockaddr_in6 sin6;
+#if !CONFIG_EMBEDDED
        int error;
        kauth_cred_t cred;
+#endif /* !CONFIG_EMBEDDED */
 
        if (!in6_ifaddrs) /* XXX broken! */
                return (EADDRNOTAVAIL);
-       if (inp->inp_lport || !IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr))
-               return (EINVAL);
        if (!(so->so_options & (SO_REUSEADDR|SO_REUSEPORT)))
                wild = 1;
+
        socket_unlock(so, 0); /* keep reference */
        lck_rw_lock_exclusive(pcbinfo->ipi_lock);
+       if (inp->inp_lport || !IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)) {
+               /* another thread completed the bind */
+               lck_rw_done(pcbinfo->ipi_lock);
+               socket_lock(so, 0);
+               return (EINVAL);
+       }
 
        bzero(&sin6, sizeof (sin6));
        if (nam != NULL) {
@@ -263,8 +270,8 @@ in6_pcbbind(struct inpcb *inp, struct sockaddr *nam, struct proc *p)
                                 */
                                IFA_LOCK_SPIN(ifa);
                                if (((struct in6_ifaddr *)ifa)->ia6_flags &
-                                   (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY|
-                                   IN6_IFF_DETACHED)) {
+                                   (IN6_IFF_ANYCAST | IN6_IFF_NOTREADY|
+                                   IN6_IFF_DETACHED | IN6_IFF_CLAT46)) {
                                        IFA_UNLOCK(ifa);
                                        IFA_REMREF(ifa);
                                        lck_rw_done(pcbinfo->ipi_lock);
@@ -288,8 +295,9 @@ in6_pcbbind(struct inpcb *inp, struct sockaddr *nam, struct proc *p)
                        struct inpcb *t;
                        uid_t u;
 
-                       /* GROSS */
-                       if (ntohs(lport) < IPV6PORT_RESERVED) {
+#if !CONFIG_EMBEDDED
+                       if (ntohs(lport) < IPV6PORT_RESERVED &&
+                               !IN6_IS_ADDR_UNSPECIFIED(&sin6.sin6_addr)) {
                                cred = kauth_cred_proc_ref(p);
                                error = priv_check_cred(cred,
                                    PRIV_NETINET_RESERVEDPORT, 0);
@@ -300,6 +308,7 @@ in6_pcbbind(struct inpcb *inp, struct sockaddr *nam, struct proc *p)
                                        return (EACCES);
                                }
                        }
+#endif /* !CONFIG_EMBEDDED */
                        if (!IN6_IS_ADDR_MULTICAST(&sin6.sin6_addr) &&
                            (u = kauth_cred_getuid(so->so_cred)) != 0) {
                                t = in6_pcblookup_local_and_cleanup(pcbinfo,
@@ -367,6 +376,16 @@ in6_pcbbind(struct inpcb *inp, struct sockaddr *nam, struct proc *p)
        }
 
        socket_lock(so, 0);
+       /*
+        * We unlocked socket's protocol lock for a long time.
+        * The socket might have been dropped/defuncted.
+        * Checking if world has changed since.
+        */
+       if (inp->inp_state == INPCB_STATE_DEAD) {
+               lck_rw_done(pcbinfo->ipi_lock);
+               return (ECONNABORTED);
+       }
+
        /* check if the socket got bound when the lock was released */
        if (inp->inp_lport || !IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)) {
                lck_rw_done(pcbinfo->ipi_lock);
@@ -514,6 +533,17 @@ in6_pcbconnect(struct inpcb *inp, struct sockaddr *nam, struct proc *p)
        struct ifnet *outif = NULL;
        struct socket *so = inp->inp_socket;
 
+#if CONTENT_FILTER
+       if (so)
+               so->so_state_change_cnt++;
+#endif
+
+       if (so->so_proto->pr_protocol == IPPROTO_UDP &&
+           sin6->sin6_port == htons(53) && !(so->so_flags1 & SOF1_DNS_COUNTED)) {
+               so->so_flags1 |= SOF1_DNS_COUNTED;
+               INC_ATOMIC_INT64_LIM(net_api_stats.nas_socket_inet_dgram_dns);
+       }
+
        /*
         * Call inner routine, to assign local interface address.
         * in6_pcbladdr() may automatically fill in sin6_scope_id.
@@ -573,6 +603,11 @@ in6_pcbdisconnect(struct inpcb *inp)
 {
        struct socket *so = inp->inp_socket;
 
+#if CONTENT_FILTER
+       if (so)
+               so->so_state_change_cnt++;
+#endif
+
        if (!lck_rw_try_lock_exclusive(inp->inp_pcbinfo->ipi_lock)) {
                /* lock inversion issue, mostly with udp multicast packets */
                socket_unlock(so, 0);
@@ -607,13 +642,19 @@ in6_pcbdetach(struct inpcb *inp)
                    inp, so, SOCK_PROTO(so));
                /* NOTREACHED */
        }
-       
+
 #if IPSEC
        if (inp->in6p_sp != NULL) {
                (void) ipsec6_delete_pcbpolicy(inp);
        }
 #endif /* IPSEC */
 
+       if (inp->inp_stat != NULL && SOCK_PROTO(so) == IPPROTO_UDP) {
+               if (inp->inp_stat->rxpackets == 0 && inp->inp_stat->txpackets == 0) {
+                       INC_ATOMIC_INT64_LIM(net_api_stats.nas_socket_inet6_dgram_no_data);
+               }
+       }
+
        /*
         * Let NetworkStatistics know this PCB is going away
         * before we detach it.
@@ -646,19 +687,28 @@ in6_pcbdetach(struct inpcb *inp)
                }
                im6o = inp->in6p_moptions;
                inp->in6p_moptions = NULL;
-               if (im6o != NULL)
-                       IM6O_REMREF(im6o);
 
                imo = inp->inp_moptions;
                inp->inp_moptions = NULL;
-               if (imo != NULL)
-                       IMO_REMREF(imo);
+
                sofreelastref(so, 0);
                inp->inp_state = INPCB_STATE_DEAD;
                /* makes sure we're not called twice from so_close */
                so->so_flags |= SOF_PCBCLEARING;
+
                inpcb_gc_sched(inp->inp_pcbinfo, INPCB_TIMER_FAST);
+
+               /*
+                * See inp_join_group() for why we need to unlock
+                */
+               if (im6o != NULL || imo != NULL) {
+                       socket_unlock(so, 0);
+                       if (im6o != NULL)
+                               IM6O_REMREF(im6o);
+                       if (imo != NULL)
+                               IMO_REMREF(imo);
+                       socket_lock(so, 0);
+               }
        }
 }
 
@@ -732,7 +782,7 @@ in6_getsockaddr(struct socket *so, struct sockaddr **nam)
 }
 
 int
-in6_getsockaddr_s(struct socket *so, struct sockaddr_storage *ss)
+in6_getsockaddr_s(struct socket *so, struct sockaddr_in6 *ss)
 {
        struct inpcb *inp;
        struct in6_addr addr;
@@ -741,17 +791,13 @@ in6_getsockaddr_s(struct socket *so, struct sockaddr_storage *ss)
        VERIFY(ss != NULL);
        bzero(ss, sizeof (*ss));
 
-       if ((inp = sotoinpcb(so)) == NULL
-#if NECP
-               || (necp_socket_should_use_flow_divert(inp))
-#endif /* NECP */
-               )
-               return (inp == NULL ? EINVAL : EPROTOTYPE);
+       if ((inp = sotoinpcb(so)) == NULL)
+               return (EINVAL);
 
        port = inp->inp_lport;
        addr = inp->in6p_laddr;
 
-       in6_sockaddr_s(port, &addr, SIN6(ss));
+       in6_sockaddr_s(port, &addr, ss);
        return (0);
 }
 
@@ -774,30 +820,6 @@ in6_getpeeraddr(struct socket *so, struct sockaddr **nam)
        return (0);
 }
 
-int
-in6_getpeeraddr_s(struct socket *so, struct sockaddr_storage *ss)
-{
-       struct inpcb *inp;
-       struct in6_addr addr;
-       in_port_t port;
-
-       VERIFY(ss != NULL);
-       bzero(ss, sizeof (*ss));
-
-       if ((inp = sotoinpcb(so)) == NULL
-#if NECP
-               || (necp_socket_should_use_flow_divert(inp))
-#endif /* NECP */
-               )
-               return (inp == NULL ? EINVAL : EPROTOTYPE);
-
-       port = inp->inp_fport;
-       addr = inp->in6p_faddr;
-
-       in6_sockaddr_s(port, &addr, SIN6(ss));
-       return (0);
-}
-
 int
 in6_mapped_sockaddr(struct socket *so, struct sockaddr **nam)
 {
@@ -857,7 +879,7 @@ in6_pcbnotify(struct inpcbinfo *pcbinfo, struct sockaddr *dst, u_int fport_arg,
        u_int32_t flowinfo;
        int errno;
 
-       if ((unsigned)cmd > PRC_NCMDS || dst->sa_family != AF_INET6)
+       if ((unsigned)cmd >= PRC_NCMDS || dst->sa_family != AF_INET6)
                return;
 
        sa6_dst = (struct sockaddr_in6 *)(void *)dst;
@@ -904,13 +926,9 @@ in6_pcbnotify(struct inpcbinfo *pcbinfo, struct sockaddr *dst, u_int fport_arg,
                 * sockets disconnected.
                 * XXX: should we avoid to notify the value to TCP sockets?
                 */
-               if (cmd == PRC_MSGSIZE && (inp->inp_flags & IN6P_MTU) != 0 &&
-                   (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr) ||
-                   IN6_ARE_ADDR_EQUAL(&inp->in6p_faddr,
-                   &sa6_dst->sin6_addr))) {
+               if (cmd == PRC_MSGSIZE)
                        ip6_notify_pmtu(inp, (struct sockaddr_in6 *)(void *)dst,
                            (u_int32_t *)cmdarg);
-               }
 
                /*
                 * Detect if we should notify the error. If no source and
@@ -1041,16 +1059,9 @@ void
 in6_losing(struct inpcb *in6p)
 {
        struct rtentry *rt;
-       struct rt_addrinfo info;
 
        if ((rt = 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);
-               rt_missmsg(RTM_LOSING, &info, rt->rt_flags, 0);
                if (rt->rt_flags & RTF_DYNAMIC) {
                        /*
                         * Prevent another thread from modifying rt_key,
@@ -1327,7 +1338,7 @@ in6p_route_copyout(struct inpcb *inp, struct route_in6 *dst)
 {
        struct route_in6 *src = &inp->in6p_route;
 
-       lck_mtx_assert(&inp->inpcb_mtx, LCK_MTX_ASSERT_OWNED);
+       socket_lock_assert_owned(inp->inp_socket);
 
        /* Minor sanity check */
        if (src->ro_rt != NULL && rt_key(src->ro_rt)->sa_family != AF_INET6)
@@ -1341,7 +1352,7 @@ in6p_route_copyin(struct inpcb *inp, struct route_in6 *src)
 {
        struct route_in6 *dst = &inp->in6p_route;
 
-       lck_mtx_assert(&inp->inpcb_mtx, LCK_MTX_ASSERT_OWNED);
+       socket_lock_assert_owned(inp->inp_socket);
 
        /* Minor sanity check */
        if (src->ro_rt != NULL && rt_key(src->ro_rt)->sa_family != AF_INET6)