+ return 0;
+}
+
+static int
+shutdown_sockets_on_interface_proc_callout(proc_t p, void *arg)
+{
+ struct fileproc *fp;
+ struct ifnet *ifp = (struct ifnet *)arg;
+
+ if (ifp == NULL) {
+ return PROC_RETURNED;
+ }
+
+ fdt_foreach(fp, p) {
+ struct fileglob *fg = fp->fp_glob;
+ struct socket *so;
+ struct inpcb *inp;
+ struct ifnet *inp_ifp;
+ int error;
+
+ if (FILEGLOB_DTYPE(fg) != DTYPE_SOCKET) {
+ continue;
+ }
+
+ so = (struct socket *)fp->fp_glob->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);
+}
+
+__private_extern__ int
+inp_limit_companion_link(struct inpcbinfo *pcbinfo, u_int32_t limit)
+{
+ struct inpcb *inp;
+ struct socket *so = NULL;
+
+ lck_rw_lock_shared(pcbinfo->ipi_lock);
+ inp_gen_t gencnt = pcbinfo->ipi_gencnt;
+ 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;
+
+ if ((so->so_state & SS_DEFUNCT) || so->so_state & SS_ISDISCONNECTED ||
+ SOCK_PROTO(so) != IPPROTO_TCP || inp->inp_last_outifp == NULL ||
+ !IFNET_IS_COMPANION_LINK(inp->inp_last_outifp)) {
+ continue;
+ }
+ so->so_snd.sb_flags &= ~SB_LIMITED;
+ u_int32_t new_size = MAX(MIN(limit, so->so_snd.sb_lowat), so->so_snd.sb_cc);
+ sbreserve(&so->so_snd, new_size);
+ so->so_snd.sb_flags |= SB_LIMITED;
+ }
+ }
+ lck_rw_done(pcbinfo->ipi_lock);
+ return 0;
+}
+
+__private_extern__ int
+inp_recover_companion_link(struct inpcbinfo *pcbinfo)
+{
+ struct inpcb *inp;
+ inp_gen_t gencnt = pcbinfo->ipi_gencnt;
+ struct socket *so = NULL;
+
+ 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;
+
+ if (SOCK_PROTO(so) != IPPROTO_TCP || inp->inp_last_outifp == NULL ||
+ !(so->so_snd.sb_flags & SB_LIMITED)) {
+ continue;
+ }
+
+ so->so_snd.sb_flags &= ~SB_LIMITED;
+ }
+ }
+ lck_rw_done(pcbinfo->ipi_lock);
+ return 0;