xnu-4903.221.2.tar.gz
[apple/xnu.git] / bsd / netinet6 / in6_pcb.c
index 54b9555f60a775950e423c9e893fd646d2810737..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,18 +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) {
@@ -264,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);
@@ -289,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);
@@ -301,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,
@@ -525,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.
@@ -584,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);
@@ -618,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.
@@ -665,7 +695,7 @@ in6_pcbdetach(struct inpcb *inp)
                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);
 
                /*
@@ -752,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;
@@ -761,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);
 }
 
@@ -794,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)
 {
@@ -924,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
@@ -1340,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)
@@ -1354,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)