+
+#if NECP
+static void
+inp_update_necp_want_app_policy(struct inpcb *inp, boolean_t set)
+{
+ struct socket *so = inp->inp_socket;
+ int before, after;
+
+ VERIFY(so != NULL);
+ VERIFY(inp->inp_state != INPCB_STATE_DEAD);
+
+ before = (inp->inp_flags2 & INP2_WANT_APP_POLICY);
+ if (set) {
+ inp_set_want_app_policy(inp);
+ } else {
+ inp_clear_want_app_policy(inp);
+ }
+ after = (inp->inp_flags2 & INP2_WANT_APP_POLICY);
+ if (net_io_policy_log && (before != after)) {
+ static const char *wanted = "WANTED";
+ static const char *unwanted = "UNWANTED";
+ uuid_string_t euuid_buf;
+ pid_t epid;
+
+ if (so->so_flags & SOF_DELEGATED) {
+ uuid_unparse(so->e_uuid, euuid_buf);
+ epid = so->e_pid;
+ } else {
+ uuid_unparse(so->last_uuid, euuid_buf);
+ epid = so->last_pid;
+ }
+
+ log(LOG_DEBUG, "%s: so 0x%llx [%d,%d] epid %d "
+ "euuid %s%s %s->%s\n", __func__,
+ (uint64_t)VM_KERNEL_ADDRPERM(so), SOCK_DOM(so),
+ SOCK_TYPE(so), epid, euuid_buf,
+ (so->so_flags & SOF_DELEGATED) ?
+ " [delegated]" : "",
+ ((before < after) ? unwanted : wanted),
+ ((before < after) ? wanted : unwanted));
+ }
+}
+#endif /* NECP */
+#endif /* !CONFIG_PROC_UUID_POLICY */
+
+#if NECP
+void
+inp_update_necp_policy(struct inpcb *inp, struct sockaddr *override_local_addr, struct sockaddr *override_remote_addr, u_int override_bound_interface)
+{
+ necp_socket_find_policy_match(inp, override_local_addr, override_remote_addr, override_bound_interface);
+ if (necp_socket_should_rescope(inp) &&
+ inp->inp_lport == 0 &&
+ inp->inp_laddr.s_addr == INADDR_ANY &&
+ IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)) {
+ // If we should rescope, and the socket is not yet bound
+ inp_bindif(inp, necp_socket_get_rescope_if_index(inp), NULL);
+ }
+}
+#endif /* NECP */
+
+int
+inp_update_policy(struct inpcb *inp)
+{
+#if CONFIG_PROC_UUID_POLICY
+ struct socket *so = inp->inp_socket;
+ uint32_t pflags = 0;
+ int32_t ogencnt;
+ int err = 0;
+
+ if (!net_io_policy_uuid ||
+ so == NULL || inp->inp_state == INPCB_STATE_DEAD)
+ return (0);
+
+ /*
+ * Kernel-created sockets that aren't delegating other sockets
+ * are currently exempted from UUID policy checks.
+ */
+ if (so->last_pid == 0 && !(so->so_flags & SOF_DELEGATED))
+ return (0);
+
+ ogencnt = so->so_policy_gencnt;
+ err = proc_uuid_policy_lookup(((so->so_flags & SOF_DELEGATED) ?
+ so->e_uuid : so->last_uuid), &pflags, &so->so_policy_gencnt);
+
+ /*
+ * Discard cached generation count if the entry is gone (ENOENT),
+ * so that we go thru the checks below.
+ */
+ if (err == ENOENT && ogencnt != 0)
+ so->so_policy_gencnt = 0;
+
+ /*
+ * If the generation count has changed, inspect the policy flags
+ * and act accordingly. If a policy flag was previously set and
+ * the UUID is no longer present in the table (ENOENT), treat it
+ * as if the flag has been cleared.
+ */
+ if ((err == 0 || err == ENOENT) && ogencnt != so->so_policy_gencnt) {
+ /* update cellular policy for this socket */
+ if (err == 0 && (pflags & PROC_UUID_NO_CELLULAR)) {
+ inp_update_cellular_policy(inp, TRUE);
+ } else if (!(pflags & PROC_UUID_NO_CELLULAR)) {
+ inp_update_cellular_policy(inp, FALSE);
+ }
+#if NECP
+ /* update necp want app policy for this socket */
+ if (err == 0 && (pflags & PROC_UUID_NECP_APP_POLICY)) {
+ inp_update_necp_want_app_policy(inp, TRUE);
+ } else if (!(pflags & PROC_UUID_NECP_APP_POLICY)) {
+ inp_update_necp_want_app_policy(inp, FALSE);
+ }
+#endif /* NECP */
+ }
+
+ return ((err == ENOENT) ? 0 : err);
+#else /* !CONFIG_PROC_UUID_POLICY */
+#pragma unused(inp)
+ return (0);
+#endif /* !CONFIG_PROC_UUID_POLICY */
+}
+
+static unsigned int log_restricted;
+SYSCTL_DECL(_net_inet);
+SYSCTL_INT(_net_inet, OID_AUTO, log_restricted,
+ CTLFLAG_RW | CTLFLAG_LOCKED, &log_restricted, 0,
+ "Log network restrictions");
+/*
+ * Called when we need to enforce policy restrictions in the input path.
+ *
+ * Returns TRUE if we're not allowed to receive data, otherwise FALSE.
+ */
+static boolean_t
+_inp_restricted_recv(struct inpcb *inp, struct ifnet *ifp)
+{
+ VERIFY(inp != NULL);
+
+ /*
+ * Inbound restrictions.
+ */
+ if (!sorestrictrecv)
+ return (FALSE);
+
+ if (ifp == NULL)
+ return (FALSE);
+
+ if (IFNET_IS_CELLULAR(ifp) && INP_NO_CELLULAR(inp))
+ return (TRUE);
+
+ if (IFNET_IS_EXPENSIVE(ifp) && INP_NO_EXPENSIVE(inp))
+ return (TRUE);
+
+ if (IFNET_IS_AWDL_RESTRICTED(ifp) && !INP_AWDL_UNRESTRICTED(inp))
+ return (TRUE);
+
+ if (!(ifp->if_eflags & IFEF_RESTRICTED_RECV))
+ return (FALSE);
+
+ if (inp->inp_flags & INP_RECV_ANYIF)
+ return (FALSE);
+
+ if ((inp->inp_flags & INP_BOUND_IF) && inp->inp_boundifp == ifp)
+ return (FALSE);
+
+ if (IFNET_IS_INTCOPROC(ifp) && !INP_INTCOPROC_ALLOWED(inp))
+ return (TRUE);
+
+ return (TRUE);
+}
+
+boolean_t
+inp_restricted_recv(struct inpcb *inp, struct ifnet *ifp)
+{
+ boolean_t ret;
+
+ ret = _inp_restricted_recv(inp, ifp);
+ if (ret == TRUE && log_restricted) {
+ printf("pid %d (%s) is unable to receive packets on %s\n",
+ current_proc()->p_pid, proc_best_name(current_proc()),
+ ifp->if_xname);
+ }
+ return (ret);
+}
+
+/*
+ * Called when we need to enforce policy restrictions in the output path.
+ *
+ * Returns TRUE if we're not allowed to send data out, otherwise FALSE.
+ */
+static boolean_t
+_inp_restricted_send(struct inpcb *inp, struct ifnet *ifp)
+{
+ VERIFY(inp != NULL);
+
+ /*
+ * Outbound restrictions.
+ */
+ if (!sorestrictsend)
+ return (FALSE);
+
+ if (ifp == NULL)
+ return (FALSE);
+
+ if (IFNET_IS_CELLULAR(ifp) && INP_NO_CELLULAR(inp))
+ return (TRUE);
+
+ if (IFNET_IS_EXPENSIVE(ifp) && INP_NO_EXPENSIVE(inp))
+ return (TRUE);
+
+ if (IFNET_IS_AWDL_RESTRICTED(ifp) && !INP_AWDL_UNRESTRICTED(inp))
+ return (TRUE);
+
+ if (IFNET_IS_INTCOPROC(ifp) && !INP_INTCOPROC_ALLOWED(inp))
+ return (TRUE);
+
+ return (FALSE);
+}
+
+boolean_t
+inp_restricted_send(struct inpcb *inp, struct ifnet *ifp)
+{
+ boolean_t ret;
+
+ ret = _inp_restricted_send(inp, ifp);
+ if (ret == TRUE && log_restricted) {
+ printf("pid %d (%s) is unable to transmit packets on %s\n",
+ current_proc()->p_pid, proc_best_name(current_proc()),
+ ifp->if_xname);
+ }
+ return (ret);
+}
+
+inline void
+inp_count_sndbytes(struct inpcb *inp, u_int32_t th_ack)
+{
+ struct ifnet *ifp = inp->inp_last_outifp;
+ struct socket *so = inp->inp_socket;
+ if (ifp != NULL && !(so->so_flags & SOF_MP_SUBFLOW) &&
+ (ifp->if_type == IFT_CELLULAR ||
+ ifp->if_subfamily == IFNET_SUBFAMILY_WIFI)) {
+ int32_t unsent;
+
+ so->so_snd.sb_flags |= SB_SNDBYTE_CNT;
+
+ /*
+ * There can be data outstanding before the connection
+ * becomes established -- TFO case
+ */
+ if (so->so_snd.sb_cc > 0)
+ inp_incr_sndbytes_total(so, so->so_snd.sb_cc);
+
+ unsent = inp_get_sndbytes_allunsent(so, th_ack);
+ if (unsent > 0)
+ inp_incr_sndbytes_unsent(so, unsent);
+ }
+}
+
+inline void
+inp_incr_sndbytes_total(struct socket *so, int32_t len)
+{
+ struct inpcb *inp = (struct inpcb *)so->so_pcb;
+ struct ifnet *ifp = inp->inp_last_outifp;
+
+ if (ifp != NULL) {
+ VERIFY(ifp->if_sndbyte_total >= 0);
+ OSAddAtomic64(len, &ifp->if_sndbyte_total);
+ }
+}
+
+inline void
+inp_decr_sndbytes_total(struct socket *so, int32_t len)
+{
+ struct inpcb *inp = (struct inpcb *)so->so_pcb;
+ struct ifnet *ifp = inp->inp_last_outifp;
+
+ if (ifp != NULL) {
+ VERIFY(ifp->if_sndbyte_total >= len);
+ OSAddAtomic64(-len, &ifp->if_sndbyte_total);
+ }
+}
+
+inline void
+inp_incr_sndbytes_unsent(struct socket *so, int32_t len)
+{
+ struct inpcb *inp = (struct inpcb *)so->so_pcb;
+ struct ifnet *ifp = inp->inp_last_outifp;
+
+ if (ifp != NULL) {
+ VERIFY(ifp->if_sndbyte_unsent >= 0);
+ OSAddAtomic64(len, &ifp->if_sndbyte_unsent);
+ }
+}
+
+inline void
+inp_decr_sndbytes_unsent(struct socket *so, int32_t len)
+{
+ struct inpcb *inp = (struct inpcb *)so->so_pcb;
+ struct ifnet *ifp = inp->inp_last_outifp;
+
+ if (so == NULL || !(so->so_snd.sb_flags & SB_SNDBYTE_CNT))
+ return;
+
+ if (ifp != NULL) {
+ if (ifp->if_sndbyte_unsent >= len)
+ OSAddAtomic64(-len, &ifp->if_sndbyte_unsent);
+ else
+ ifp->if_sndbyte_unsent = 0;
+ }
+}
+
+inline void
+inp_decr_sndbytes_allunsent(struct socket *so, u_int32_t th_ack)
+{
+ int32_t len;
+
+ if (so == NULL || !(so->so_snd.sb_flags & SB_SNDBYTE_CNT))
+ return;
+
+ len = inp_get_sndbytes_allunsent(so, th_ack);
+ inp_decr_sndbytes_unsent(so, len);
+}
+
+
+inline void
+inp_set_activity_bitmap(struct inpcb *inp)
+{
+ in_stat_set_activity_bitmap(&inp->inp_nw_activity, net_uptime());
+}
+
+inline void
+inp_get_activity_bitmap(struct inpcb *inp, activity_bitmap_t *ab)
+{
+ bcopy(&inp->inp_nw_activity, ab, sizeof (*ab));
+}