]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/netinet/in_pcblist.c
xnu-6153.11.26.tar.gz
[apple/xnu.git] / bsd / netinet / in_pcblist.c
index 09cc79674f3e563365027934f9dd8b55f35b7207..dcd59d9c3300467d0830dd7bf0f13d1d5d1ba95e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010-2013 Apple Inc. All rights reserved.
+ * Copyright (c) 2010-2018 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  *
@@ -64,6 +64,8 @@
 #include <sys/socketvar.h>
 #include <sys/protosw.h>
 #include <sys/domain.h>
+#include <sys/filedesc.h>
+#include <sys/file_internal.h>
 #include <sys/kernel.h>
 #include <sys/sysctl.h>
 #include <sys/dtrace.h>
@@ -71,6 +73,8 @@
 
 #include <net/route.h>
 #include <net/if_var.h>
+#include <net/if_ports_used.h>
+#include <net/ntstat.h>
 
 #include <netinet/in.h>
 #include <netinet/in_pcb.h>
 #include <netinet/tcp_var.h>
 #include <netinet6/in6_var.h>
 
+#include <os/log.h>
+
 #ifndef ROUNDUP64
-#define        ROUNDUP64(x) P2ROUNDUP((x), sizeof (u_int64_t))
+#define ROUNDUP64(x) P2ROUNDUP((x), sizeof (u_int64_t))
 #endif
 
 #ifndef ADVANCE64
-#define        ADVANCE64(p, n) (void*)((char *)(p) + ROUNDUP64(n))
+#define ADVANCE64(p, n) (void*)((char *)(p) + ROUNDUP64(n))
 #endif
 
-static void sotoxsocket_n(struct socket *, struct xsocket_n *);
-static void sbtoxsockbuf_n(struct sockbuf *, struct xsockbuf_n *);
-static void sbtoxsockstat_n(struct socket *, struct xsockstat_n *);
 static void inpcb_to_xinpcb_n(struct inpcb *, struct xinpcb_n *);
 static void tcpcb_to_xtcpcb_n(struct tcpcb *, struct xtcpcb_n *);
+void shutdown_sockets_on_interface(struct ifnet *ifp);
 
-static void
+
+__private_extern__ void
 sotoxsocket_n(struct socket *so, struct xsocket_n *xso)
 {
-       xso->xso_len = sizeof (struct xsocket_n);
+       xso->xso_len = sizeof(struct xsocket_n);
        xso->xso_kind = XSO_SOCKET;
 
-       if (so != NULL) {
-               xso->xso_so = (u_int64_t)(uintptr_t)so;
-               xso->so_type = so->so_type;
-               xso->so_options = so->so_options;
-               xso->so_linger = so->so_linger;
-               xso->so_state = so->so_state;
-               xso->so_pcb = (u_int64_t)(uintptr_t)so->so_pcb;
-               if (so->so_proto) {
-                       xso->xso_protocol = SOCK_PROTO(so);
-                       xso->xso_family = SOCK_DOM(so);
-               } else {
-                       xso->xso_protocol = xso->xso_family = 0;
-               }
-               xso->so_qlen = so->so_qlen;
-               xso->so_incqlen = so->so_incqlen;
-               xso->so_qlimit = so->so_qlimit;
-               xso->so_timeo = so->so_timeo;
-               xso->so_error = so->so_error;
-               xso->so_pgid = so->so_pgid;
-               xso->so_oobmark = so->so_oobmark;
-               xso->so_uid = kauth_cred_getuid(so->so_cred);
+       if (so == NULL) {
+               return;
+       }
+
+       xso->xso_so = (uint64_t)VM_KERNEL_ADDRPERM(so);
+       xso->so_type = so->so_type;
+       xso->so_options = so->so_options;
+       xso->so_linger = so->so_linger;
+       xso->so_state = so->so_state;
+       xso->so_pcb = (uint64_t)VM_KERNEL_ADDRPERM(so->so_pcb);
+       if (so->so_proto) {
+               xso->xso_protocol = SOCK_PROTO(so);
+               xso->xso_family = SOCK_DOM(so);
+       } else {
+               xso->xso_protocol = xso->xso_family = 0;
        }
+       xso->so_qlen = so->so_qlen;
+       xso->so_incqlen = so->so_incqlen;
+       xso->so_qlimit = so->so_qlimit;
+       xso->so_timeo = so->so_timeo;
+       xso->so_error = so->so_error;
+       xso->so_pgid = so->so_pgid;
+       xso->so_oobmark = so->so_oobmark;
+       xso->so_uid = kauth_cred_getuid(so->so_cred);
+       xso->so_last_pid = so->last_pid;
+       xso->so_e_pid = so->e_pid;
 }
 
-static void
+__private_extern__ void
 sbtoxsockbuf_n(struct sockbuf *sb, struct xsockbuf_n *xsb)
 {
-       xsb->xsb_len = sizeof (struct xsockbuf_n);
-       xsb->xsb_kind = (sb->sb_flags & SB_RECV) ? XSO_RCVBUF : XSO_SNDBUF;
+       xsb->xsb_len = sizeof(struct xsockbuf_n);
 
-       if (sb != NULL) {
-               xsb->sb_cc = sb->sb_cc;
-               xsb->sb_hiwat = sb->sb_hiwat;
-               xsb->sb_mbcnt = sb->sb_mbcnt;
-               xsb->sb_mbmax = sb->sb_mbmax;
-               xsb->sb_lowat = sb->sb_lowat;
-               xsb->sb_flags = sb->sb_flags;
-               xsb->sb_timeo = (short)(sb->sb_timeo.tv_sec * hz) +
-                   sb->sb_timeo.tv_usec / tick;
-               if (xsb->sb_timeo == 0 && sb->sb_timeo.tv_usec != 0)
-                       xsb->sb_timeo = 1;
+       if (sb == NULL) {
+               return;
+       }
+
+       xsb->xsb_kind = (sb->sb_flags & SB_RECV) ? XSO_RCVBUF : XSO_SNDBUF;
+       xsb->sb_cc = sb->sb_cc;
+       xsb->sb_hiwat = sb->sb_hiwat;
+       xsb->sb_mbcnt = sb->sb_mbcnt;
+       xsb->sb_mbmax = sb->sb_mbmax;
+       xsb->sb_lowat = sb->sb_lowat;
+       xsb->sb_flags = sb->sb_flags;
+       xsb->sb_timeo = (short)(sb->sb_timeo.tv_sec * hz) +
+           sb->sb_timeo.tv_usec / tick;
+       if (xsb->sb_timeo == 0 && sb->sb_timeo.tv_usec != 0) {
+               xsb->sb_timeo = 1;
        }
 }
 
-static void
+__private_extern__ void
 sbtoxsockstat_n(struct socket *so, struct xsockstat_n *xst)
 {
        int i;
 
-       xst->xst_len = sizeof (struct xsockstat_n);
+       xst->xst_len = sizeof(struct xsockstat_n);
        xst->xst_kind = XSO_STATS;
 
+       if (so == NULL) {
+               return;
+       }
+
        for (i = 0; i < SO_TC_STATS_MAX; i++) {
                xst->xst_tc_stats[i].rxpackets = so->so_tc_stats[i].rxpackets;
                xst->xst_tc_stats[i].rxbytes = so->so_tc_stats[i].rxbytes;
@@ -170,12 +186,12 @@ sbtoxsockstat_n(struct socket *so, struct xsockstat_n *xst)
 static void
 inpcb_to_xinpcb_n(struct inpcb *inp, struct xinpcb_n *xinp)
 {
-       xinp->xi_len = sizeof (struct xinpcb_n);
+       xinp->xi_len = sizeof(struct xinpcb_n);
        xinp->xi_kind = XSO_INPCB;
-       xinp->xi_inpp = (u_int64_t)(uintptr_t)inp;
+       xinp->xi_inpp = (uint64_t)VM_KERNEL_ADDRPERM(inp);
        xinp->inp_fport = inp->inp_fport;
        xinp->inp_lport = inp->inp_lport;
-       xinp->inp_ppcb = (u_int64_t)(uintptr_t)inp->inp_ppcb;
+       xinp->inp_ppcb = (uint64_t)VM_KERNEL_ADDRPERM(inp->inp_ppcb);
        xinp->inp_gencnt = inp->inp_gencnt;
        xinp->inp_flags = inp->inp_flags;
        xinp->inp_flow = inp->inp_flow;
@@ -190,23 +206,24 @@ inpcb_to_xinpcb_n(struct inpcb *inp, struct xinpcb_n *xinp)
        xinp->inp_depend6.inp6_ifindex = 0;
        xinp->inp_depend6.inp6_hops = inp->inp_depend6.inp6_hops;
        xinp->inp_flowhash = inp->inp_flowhash;
+       xinp->inp_flags2 = inp->inp_flags2;
 }
 
 __private_extern__ void
 tcpcb_to_xtcpcb_n(struct tcpcb *tp, struct xtcpcb_n *xt)
 {
-       int i;
-
-       xt->xt_len = sizeof (struct xtcpcb_n);
+       xt->xt_len = sizeof(struct xtcpcb_n);
        xt->xt_kind = XSO_TCPCB;
 
-       xt->t_segq = (u_int32_t)(uintptr_t)tp->t_segq.lh_first;
+       xt->t_segq = (uint32_t)VM_KERNEL_ADDRPERM(tp->t_segq.lh_first);
        xt->t_dupacks = tp->t_dupacks;
-       for (i = 0; i < TCPT_NTIMERS_EXT; i++)
-               xt->t_timer[i] = tp->t_timer[i];
+       xt->t_timer[TCPT_REXMT_EXT] = tp->t_timer[TCPT_REXMT];
+       xt->t_timer[TCPT_PERSIST_EXT] = tp->t_timer[TCPT_PERSIST];
+       xt->t_timer[TCPT_KEEP_EXT] = tp->t_timer[TCPT_KEEP];
+       xt->t_timer[TCPT_2MSL_EXT] = tp->t_timer[TCPT_2MSL];
        xt->t_state = tp->t_state;
        xt->t_flags = tp->t_flags;
-       xt->t_force = tp->t_force;
+       xt->t_force = (tp->t_flagsext & TF_FORCE) ? 1 : 0;
        xt->snd_una = tp->snd_una;
        xt->snd_max = tp->snd_max;
        xt->snd_nxt = tp->snd_nxt;
@@ -245,8 +262,8 @@ tcpcb_to_xtcpcb_n(struct tcpcb *tp, struct xtcpcb_n *xt)
        xt->ts_recent = tp->ts_recent;
        xt->ts_recent_age = tp->ts_recent_age;
        xt->last_ack_sent = tp->last_ack_sent;
-       xt->cc_send = tp->cc_send;
-       xt->cc_recv = tp->cc_recv;
+       xt->cc_send = 0;
+       xt->cc_recv = 0;
        xt->snd_recover = tp->snd_recover;
        xt->snd_cwnd_prev = tp->snd_cwnd_prev;
        xt->snd_ssthresh_prev = tp->snd_ssthresh_prev;
@@ -261,49 +278,50 @@ get_pcblist_n(short proto, struct sysctl_req *req, struct inpcbinfo *pcbinfo)
        inp_gen_t gencnt;
        struct xinpgen xig;
        void *buf = NULL;
-       size_t item_size = ROUNDUP64(sizeof (struct xinpcb_n)) +
-           ROUNDUP64(sizeof (struct xsocket_n)) +
-           2 * ROUNDUP64(sizeof (struct xsockbuf_n)) +
-           ROUNDUP64(sizeof (struct xsockstat_n));
+       size_t item_size = ROUNDUP64(sizeof(struct xinpcb_n)) +
+           ROUNDUP64(sizeof(struct xsocket_n)) +
+           2 * ROUNDUP64(sizeof(struct xsockbuf_n)) +
+           ROUNDUP64(sizeof(struct xsockstat_n));
 
-       if (proto == IPPROTO_TCP)
-               item_size += ROUNDUP64(sizeof (struct xtcpcb_n));
+       if (proto == IPPROTO_TCP) {
+               item_size += ROUNDUP64(sizeof(struct xtcpcb_n));
+       }
 
-       /*
-        * The process of preparing the PCB list is too time-consuming and
-        * resource-intensive to repeat twice on every request.
-        */
-       lck_rw_lock_exclusive(pcbinfo->ipi_lock);
        if (req->oldptr == USER_ADDR_NULL) {
                n = pcbinfo->ipi_count;
-               req->oldidx = 2 * (sizeof (xig)) + (n + n/8) * item_size;
-               goto done;
+               req->oldidx = 2 * (sizeof(xig)) + (n + n / 8 + 1) * item_size;
+               return 0;
        }
 
        if (req->newptr != USER_ADDR_NULL) {
-               error = EPERM;
-               goto done;
+               return EPERM;
        }
 
+
+       /*
+        * The process of preparing the PCB list is too time-consuming and
+        * resource-intensive to repeat twice on every request.
+        */
+       lck_rw_lock_exclusive(pcbinfo->ipi_lock);
        /*
         * OK, now we're committed to doing something.
         */
        gencnt = pcbinfo->ipi_gencnt;
        n = pcbinfo->ipi_count;
 
-       bzero(&xig, sizeof (xig));
-       xig.xig_len = sizeof (xig);
+       bzero(&xig, sizeof(xig));
+       xig.xig_len = sizeof(xig);
        xig.xig_count = n;
        xig.xig_gen = gencnt;
        xig.xig_sogen = so_gencnt;
-       error = SYSCTL_OUT(req, &xig, sizeof (xig));
+       error = SYSCTL_OUT(req, &xig, sizeof(xig));
        if (error) {
                goto done;
        }
        /*
         * We are done if there is no pcb
         */
-       if (n == 0) {
+       if (xig.xig_count == 0) {
                goto done;
        }
 
@@ -313,19 +331,28 @@ get_pcblist_n(short proto, struct sysctl_req *req, struct inpcbinfo *pcbinfo)
                goto done;
        }
 
-       inp_list = _MALLOC(n * sizeof (*inp_list), M_TEMP, M_WAITOK);
+       inp_list = _MALLOC(n * sizeof(*inp_list), M_TEMP, M_WAITOK);
        if (inp_list == NULL) {
                error = ENOMEM;
                goto done;
        }
 
-       for (inp = pcbinfo->ipi_listhead->lh_first, i = 0; inp && i < n;
-           inp = inp->inp_list.le_next) {
-               if (inp->inp_gencnt <= gencnt &&
-                   inp->inp_state != INPCB_STATE_DEAD)
-                       inp_list[i++] = inp;
+       /*
+        * Special case TCP to include the connections in time wait
+        */
+       if (proto == IPPROTO_TCP) {
+               n = get_tcp_inp_list(inp_list, n, gencnt);
+       } else {
+               for (inp = pcbinfo->ipi_listhead->lh_first, i = 0; inp && i < n;
+                   inp = inp->inp_list.le_next) {
+                       if (inp->inp_gencnt <= gencnt &&
+                           inp->inp_state != INPCB_STATE_DEAD) {
+                               inp_list[i++] = inp;
+                       }
+               }
+               n = i;
        }
-       n = i;
+
 
        error = 0;
        for (i = 0; i < n; i++) {
@@ -334,13 +361,13 @@ get_pcblist_n(short proto, struct sysctl_req *req, struct inpcbinfo *pcbinfo)
                    inp->inp_state != INPCB_STATE_DEAD) {
                        struct xinpcb_n *xi = (struct xinpcb_n *)buf;
                        struct xsocket_n *xso = (struct xsocket_n *)
-                           ADVANCE64(xi, sizeof (*xi));
+                           ADVANCE64(xi, sizeof(*xi));
                        struct xsockbuf_n *xsbrcv = (struct xsockbuf_n *)
-                           ADVANCE64(xso, sizeof (*xso));
+                           ADVANCE64(xso, sizeof(*xso));
                        struct xsockbuf_n *xsbsnd = (struct xsockbuf_n *)
-                           ADVANCE64(xsbrcv, sizeof (*xsbrcv));
+                           ADVANCE64(xsbrcv, sizeof(*xsbrcv));
                        struct xsockstat_n *xsostats = (struct xsockstat_n *)
-                           ADVANCE64(xsbsnd, sizeof (*xsbsnd));
+                           ADVANCE64(xsbsnd, sizeof(*xsbsnd));
 
                        bzero(buf, item_size);
 
@@ -353,22 +380,27 @@ get_pcblist_n(short proto, struct sysctl_req *req, struct inpcbinfo *pcbinfo)
                        sbtoxsockstat_n(inp->inp_socket, xsostats);
                        if (proto == IPPROTO_TCP) {
                                struct  xtcpcb_n *xt = (struct xtcpcb_n *)
-                                   ADVANCE64(xsostats, sizeof (*xsostats));
+                                   ADVANCE64(xsostats, sizeof(*xsostats));
 
                                /*
                                 * inp->inp_ppcb, can only be NULL on
                                 * an initialization race window.
                                 * No need to lock.
                                 */
-                               if (inp->inp_ppcb == NULL)
+                               if (inp->inp_ppcb == NULL) {
                                        continue;
+                               }
 
                                tcpcb_to_xtcpcb_n((struct tcpcb *)
                                    inp->inp_ppcb, xt);
                        }
                        error = SYSCTL_OUT(req, buf, item_size);
+                       if (error) {
+                               break;
+                       }
                }
        }
+
        if (!error) {
                /*
                 * Give the user an updated idea of our state.
@@ -377,65 +409,189 @@ get_pcblist_n(short proto, struct sysctl_req *req, struct inpcbinfo *pcbinfo)
                 * while we were processing this request, and it
                 * might be necessary to retry.
                 */
-               bzero(&xig, sizeof (xig));
-               xig.xig_len = sizeof (xig);
+               bzero(&xig, sizeof(xig));
+               xig.xig_len = sizeof(xig);
                xig.xig_gen = pcbinfo->ipi_gencnt;
                xig.xig_sogen = so_gencnt;
                xig.xig_count = pcbinfo->ipi_count;
-               error = SYSCTL_OUT(req, &xig, sizeof (xig));
+               error = SYSCTL_OUT(req, &xig, sizeof(xig));
        }
 done:
        lck_rw_done(pcbinfo->ipi_lock);
-       if (inp_list != NULL)
+
+       if (inp_list != NULL) {
                FREE(inp_list, M_TEMP);
-       if (buf != NULL)
+       }
+       if (buf != NULL) {
                FREE(buf, M_TEMP);
-       return (error);
+       }
+       return error;
 }
 
 __private_extern__ void
-inpcb_get_ports_used(uint32_t ifindex, int protocol, uint32_t wildcardok,
+inpcb_get_ports_used(uint32_t ifindex, int protocol, uint32_t flags,
     bitstr_t *bitfield, struct inpcbinfo *pcbinfo)
 {
        struct inpcb *inp;
        struct socket *so;
        inp_gen_t gencnt;
-       uint32_t iswildcard;
+       bool iswildcard, wildcardok, nowakeok;
+       bool recvanyifonly, extbgidleok;
+       bool activeonly;
+
+       wildcardok = ((flags & INPCB_GET_PORTS_USED_WILDCARDOK) != 0);
+       nowakeok = ((flags & INPCB_GET_PORTS_USED_NOWAKEUPOK) != 0);
+       recvanyifonly = ((flags & INPCB_GET_PORTS_USED_RECVANYIFONLY) != 0);
+       extbgidleok = ((flags & INPCB_GET_PORTS_USED_EXTBGIDLEONLY) != 0);
+       activeonly = ((flags & INPCB_GET_PORTS_USED_ACTIVEONLY) != 0);
 
        lck_rw_lock_shared(pcbinfo->ipi_lock);
        gencnt = pcbinfo->ipi_gencnt;
+
        for (inp = LIST_FIRST(pcbinfo->ipi_listhead); inp;
            inp = LIST_NEXT(inp, inp_list)) {
                uint16_t port;
 
                if (inp->inp_gencnt > gencnt ||
-                   inp->inp_state == INPCB_STATE_DEAD)
+                   inp->inp_state == INPCB_STATE_DEAD ||
+                   inp->inp_wantcnt == WNT_STOPUSING) {
                        continue;
+               }
 
                if ((so = inp->inp_socket) == NULL ||
-                   (so->so_state & SS_DEFUNCT))
+                   (so->so_state & SS_DEFUNCT) ||
+                   (so->so_state & SS_ISDISCONNECTED)) {
                        continue;
+               }
 
-               if (!(protocol == PF_UNSPEC ||
-                   (protocol == PF_INET && (inp->inp_vflag & INP_IPV4)) ||
-                   (protocol == PF_INET6 && (inp->inp_vflag & INP_IPV6))))
+               /*
+                * If protocol is specified, filter out inpcbs that
+                * are not relevant to the protocol family of interest.
+                */
+               if (protocol != PF_UNSPEC) {
+                       if (protocol == PF_INET) {
+                               /*
+                                * If protocol of interest is IPv4, skip the inpcb
+                                * if the family is not IPv4.
+                                * OR
+                                * If the family is IPv4, skip if the IPv4 flow is
+                                * CLAT46 translated.
+                                */
+                               if ((inp->inp_vflag & INP_IPV4) == 0 ||
+                                   (inp->inp_flags2 & INP2_CLAT46_FLOW) != 0) {
+                                       continue;
+                               }
+                       } else if (protocol == PF_INET6) {
+                               /*
+                                * If protocol of interest is IPv6, skip the inpcb
+                                * if the family is not IPv6.
+                                * AND
+                                * The flow is not a CLAT46'd flow.
+                                */
+                               if ((inp->inp_vflag & INP_IPV6) == 0 &&
+                                   (inp->inp_flags2 & INP2_CLAT46_FLOW) == 0) {
+                                       continue;
+                               }
+                       } else {
+                               /* Protocol family not supported */
+                               continue;
+                       }
+               }
+
+               if (SOCK_PROTO(inp->inp_socket) != IPPROTO_UDP &&
+                   SOCK_PROTO(inp->inp_socket) != IPPROTO_TCP) {
                        continue;
+               }
 
                iswildcard = (((inp->inp_vflag & INP_IPV4) &&
                    inp->inp_laddr.s_addr == INADDR_ANY) ||
                    ((inp->inp_vflag & INP_IPV6) &&
                    IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)));
 
-               if (!wildcardok && iswildcard)
+               if (!wildcardok && iswildcard) {
+                       continue;
+               }
+
+               if ((so->so_options & SO_NOWAKEFROMSLEEP) &&
+                   !nowakeok) {
+                       continue;
+               }
+
+               if (!(inp->inp_flags & INP_RECV_ANYIF) &&
+                   recvanyifonly) {
                        continue;
+               }
+
+               if (!(so->so_flags1 & SOF1_EXTEND_BK_IDLE_WANTED) &&
+                   extbgidleok) {
+                       continue;
+               }
 
                if (!iswildcard &&
                    !(ifindex == 0 || inp->inp_last_outifp == NULL ||
-                   ifindex == inp->inp_last_outifp->if_index))
+                   ifindex == inp->inp_last_outifp->if_index)) {
                        continue;
+               }
+
+               if (SOCK_PROTO(inp->inp_socket) == IPPROTO_UDP &&
+                   so->so_state & SS_CANTRCVMORE) {
+                       continue;
+               }
 
+               if (SOCK_PROTO(inp->inp_socket) == IPPROTO_TCP) {
+                       struct  tcpcb *tp = sototcpcb(inp->inp_socket);
+
+                       /*
+                        * Workaround race where inp_ppcb is NULL during
+                        * socket initialization
+                        */
+                       if (tp == NULL) {
+                               continue;
+                       }
+
+                       switch (tp->t_state) {
+                       case TCPS_CLOSED:
+                               continue;
+                       /* NOT REACHED */
+                       case TCPS_LISTEN:
+                       case TCPS_SYN_SENT:
+                       case TCPS_SYN_RECEIVED:
+                       case TCPS_ESTABLISHED:
+                       case TCPS_FIN_WAIT_1:
+                               /*
+                                * Note: FIN_WAIT_1 is an active state
+                                * because we need our FIN to be
+                                * acknowledged
+                                */
+                               break;
+                       case TCPS_CLOSE_WAIT:
+                       case TCPS_CLOSING:
+                       case TCPS_LAST_ACK:
+                       case TCPS_FIN_WAIT_2:
+                               /*
+                                * In the closing states, the connection
+                                * is not idle when there is outgoing
+                                * data having to be acknowledged
+                                */
+                               if (activeonly && so->so_snd.sb_cc == 0) {
+                                       continue;
+                               }
+                               break;
+                       case TCPS_TIME_WAIT:
+                               continue;
+                               /* NOT REACHED */
+                       }
+               }
+               /*
+                * Final safeguard to exclude unspecified local port
+                */
                port = ntohs(inp->inp_lport);
-               bit_set(bitfield, port);
+               if (port == 0) {
+                       continue;
+               }
+               bitstr_set(bitfield, port);
+
+               if_ports_used_add_inpcb(ifindex, inp);
        }
        lck_rw_done(pcbinfo->ipi_lock);
 }
@@ -474,12 +630,12 @@ inpcb_count_opportunistic(unsigned int ifindex, struct inpcbinfo *pcbinfo,
                                            (SO_FILT_HINT_LOCKED |
                                            SO_FILT_HINT_RESUME));
                                }
-                               SOTHROTTLELOG(("throttle[%d]: so 0x%llx "
+                               SOTHROTTLELOG("throttle[%d]: so 0x%llx "
                                    "[%d,%d] %s\n", so->last_pid,
                                    (uint64_t)VM_KERNEL_ADDRPERM(so),
                                    SOCK_DOM(so), SOCK_TYPE(so),
                                    (so->so_flags & SOF_SUSPENDED) ?
-                                   "SUSPENDED" : "RESUMED"));
+                                   "SUSPENDED" : "RESUMED");
                                socket_unlock(so, 1);
                        }
                }
@@ -487,7 +643,7 @@ inpcb_count_opportunistic(unsigned int ifindex, struct inpcbinfo *pcbinfo,
 
        lck_rw_done(pcbinfo->ipi_lock);
 
-       return (opportunistic);
+       return opportunistic;
 }
 
 __private_extern__ uint32_t
@@ -500,39 +656,128 @@ inpcb_find_anypcb_byaddr(struct ifaddr *ifa, struct inpcbinfo *pcbinfo)
 
        if ((ifa->ifa_addr->sa_family != AF_INET) &&
            (ifa->ifa_addr->sa_family != AF_INET6)) {
-               return (0);
+               return 0;
        }
 
        lck_rw_lock_shared(pcbinfo->ipi_lock);
        for (inp = LIST_FIRST(pcbinfo->ipi_listhead);
            inp != NULL; inp = LIST_NEXT(inp, inp_list)) {
-
                if (inp->inp_gencnt <= gencnt &&
                    inp->inp_state != INPCB_STATE_DEAD &&
                    inp->inp_socket != NULL) {
                        so = inp->inp_socket;
                        af = SOCK_DOM(so);
-                       if (af != ifa->ifa_addr->sa_family)
+                       if (af != ifa->ifa_addr->sa_family) {
                                continue;
-                       if (inp->inp_last_outifp != ifa->ifa_ifp)
+                       }
+                       if (inp->inp_last_outifp != ifa->ifa_ifp) {
                                continue;
+                       }
 
                        if (af == AF_INET) {
                                if (inp->inp_laddr.s_addr ==
                                    (satosin(ifa->ifa_addr))->sin_addr.s_addr) {
                                        lck_rw_done(pcbinfo->ipi_lock);
-                                       return (1);
+                                       return 1;
                                }
                        }
                        if (af == AF_INET6) {
                                if (IN6_ARE_ADDR_EQUAL(IFA_IN6(ifa),
                                    &inp->in6p_laddr)) {
                                        lck_rw_done(pcbinfo->ipi_lock);
-                                       return (1);
+                                       return 1;
                                }
                        }
                }
        }
        lck_rw_done(pcbinfo->ipi_lock);
-       return (0);
+       return 0;
+}
+
+static int
+shutdown_sockets_on_interface_proc_callout(proc_t p, void *arg)
+{
+       struct filedesc *fdp;
+       int i;
+       struct ifnet *ifp = (struct ifnet *)arg;
+
+       if (ifp == NULL) {
+               return PROC_RETURNED;
+       }
+
+       proc_fdlock(p);
+       fdp = p->p_fd;
+       for (i = 0; i < fdp->fd_nfiles; i++) {
+               struct fileproc *fp = fdp->fd_ofiles[i];
+               struct fileglob *fg;
+               struct socket *so;
+               struct inpcb *inp;
+               struct ifnet *inp_ifp;
+               int error;
+
+               if (fp == NULL || (fdp->fd_ofileflags[i] & UF_RESERVED) != 0) {
+                       continue;
+               }
+
+               fg = fp->f_fglob;
+               if (FILEGLOB_DTYPE(fg) != DTYPE_SOCKET) {
+                       continue;
+               }
+
+               so = (struct socket *)fp->f_fglob->fg_data;
+               if (SOCK_DOM(so) != PF_INET && SOCK_DOM(so) != PF_INET6) {
+                       continue;
+               }
+
+               inp = (struct inpcb *)so->so_pcb;
+
+               if (in_pcb_checkstate(inp, WNT_ACQUIRE, 0) == WNT_STOPUSING) {
+                       continue;
+               }
+
+               socket_lock(so, 1);
+
+               if (in_pcb_checkstate(inp, WNT_RELEASE, 1) == WNT_STOPUSING) {
+                       socket_unlock(so, 1);
+                       continue;
+               }
+
+               if (inp->inp_boundifp != NULL) {
+                       inp_ifp = inp->inp_boundifp;
+               } else if (inp->inp_last_outifp != NULL) {
+                       inp_ifp = inp->inp_last_outifp;
+               } else {
+                       socket_unlock(so, 1);
+                       continue;
+               }
+
+               if (inp_ifp != ifp && inp_ifp->if_delegated.ifp != ifp) {
+                       socket_unlock(so, 1);
+                       continue;
+               }
+               error = sosetdefunct(p, so, 0, TRUE);
+               if (error != 0) {
+                       log(LOG_ERR, "%s: sosetdefunct() error %d",
+                           __func__, error);
+               } else {
+                       error = sodefunct(p, so, 0);
+                       if (error != 0) {
+                               log(LOG_ERR, "%s: sodefunct() error %d",
+                                   __func__, error);
+                       }
+               }
+
+               socket_unlock(so, 1);
+       }
+       proc_fdunlock(p);
+
+       return PROC_RETURNED;
+}
+
+void
+shutdown_sockets_on_interface(struct ifnet *ifp)
+{
+       proc_iterate(PROC_ALLPROCLIST,
+           shutdown_sockets_on_interface_proc_callout,
+           ifp, NULL, NULL);
 }