+ return (space);
+}
+
+/*
+ * If this socket has priority queues, check if there is enough
+ * space in the priority queue for this msg.
+ */
+int
+msgq_sbspace(struct socket *so, struct mbuf *control)
+{
+ int space = 0, error;
+ u_int32_t msgpri;
+ VERIFY(so->so_type == SOCK_STREAM &&
+ SOCK_PROTO(so) == IPPROTO_TCP);
+ if (control != NULL) {
+ error = tcp_get_msg_priority(control, &msgpri);
+ if (error)
+ return (0);
+ } else {
+ msgpri = MSG_PRI_0;
+ }
+ space = (so->so_snd.sb_idealsize / MSG_PRI_COUNT) -
+ so->so_msg_state->msg_priq[msgpri].msgq_bytes;
+ if (space < 0)
+ space = 0;
+ return (space);
+}
+
+/* do we have to send all at once on a socket? */
+int
+sosendallatonce(struct socket *so)
+{
+ return (so->so_proto->pr_flags & PR_ATOMIC);
+}
+
+/* can we read something from so? */
+int
+soreadable(struct socket *so)
+{
+ return (so->so_rcv.sb_cc >= so->so_rcv.sb_lowat ||
+ ((so->so_state & SS_CANTRCVMORE)
+#if CONTENT_FILTER
+ && cfil_sock_data_pending(&so->so_rcv) == 0
+#endif /* CONTENT_FILTER */
+ ) ||
+ so->so_comp.tqh_first || so->so_error);
+}
+
+/* can we write something to so? */
+
+int
+sowriteable(struct socket *so)
+{
+ if ((so->so_state & SS_CANTSENDMORE) ||
+ so->so_error > 0)
+ return (1);
+ if (so_wait_for_if_feedback(so) || !socanwrite(so))
+ return (0);
+ if (so->so_flags1 & SOF1_PRECONNECT_DATA)
+ return(1);
+
+ if (sbspace(&(so)->so_snd) >= (so)->so_snd.sb_lowat) {
+ if (so->so_flags & SOF_NOTSENT_LOWAT) {
+ if ((SOCK_DOM(so) == PF_INET6 ||
+ SOCK_DOM(so) == PF_INET) &&
+ so->so_type == SOCK_STREAM) {
+ return (tcp_notsent_lowat_check(so));
+ }
+#if MPTCP
+ else if ((SOCK_DOM(so) == PF_MULTIPATH) &&
+ (SOCK_PROTO(so) == IPPROTO_TCP)) {
+ return (mptcp_notsent_lowat_check(so));
+ }
+#endif
+ else {
+ return (1);
+ }
+ } else {
+ return (1);
+ }
+ }
+ return (0);
+}
+
+/* adjust counters in sb reflecting allocation of m */
+
+void
+sballoc(struct sockbuf *sb, struct mbuf *m)
+{
+ u_int32_t cnt = 1;
+ sb->sb_cc += m->m_len;
+ if (m->m_type != MT_DATA && m->m_type != MT_HEADER &&
+ m->m_type != MT_OOBDATA)
+ sb->sb_ctl += m->m_len;
+ sb->sb_mbcnt += MSIZE;
+
+ if (m->m_flags & M_EXT) {
+ sb->sb_mbcnt += m->m_ext.ext_size;
+ cnt += (m->m_ext.ext_size >> MSIZESHIFT);
+ }
+ OSAddAtomic(cnt, &total_sbmb_cnt);
+ VERIFY(total_sbmb_cnt > 0);
+ if (total_sbmb_cnt > total_sbmb_cnt_peak)
+ total_sbmb_cnt_peak = total_sbmb_cnt;
+
+ /*
+ * If data is being appended to the send socket buffer,
+ * update the send byte count
+ */
+ if (!(sb->sb_flags & SB_RECV))
+ OSAddAtomic(cnt, &total_snd_byte_count);
+}
+
+/* adjust counters in sb reflecting freeing of m */
+void
+sbfree(struct sockbuf *sb, struct mbuf *m)
+{
+ int cnt = -1;
+
+ sb->sb_cc -= m->m_len;
+ if (m->m_type != MT_DATA && m->m_type != MT_HEADER &&
+ m->m_type != MT_OOBDATA)
+ sb->sb_ctl -= m->m_len;
+ sb->sb_mbcnt -= MSIZE;
+ if (m->m_flags & M_EXT) {
+ sb->sb_mbcnt -= m->m_ext.ext_size;
+ cnt -= (m->m_ext.ext_size >> MSIZESHIFT);
+ }
+ OSAddAtomic(cnt, &total_sbmb_cnt);
+ VERIFY(total_sbmb_cnt >= 0);
+
+ /*
+ * If data is being removed from the send socket buffer,
+ * update the send byte count
+ */
+ if (!(sb->sb_flags & SB_RECV)) {
+ OSAddAtomic(cnt, &total_snd_byte_count);
+ }
+}
+
+/*
+ * Set lock on sockbuf sb; sleep if lock is already held.
+ * Unless SB_NOINTR is set on sockbuf, sleep is interruptible.
+ * Returns error without lock if sleep is interrupted.
+ */
+int
+sblock(struct sockbuf *sb, uint32_t flags)
+{
+ boolean_t nointr = ((sb->sb_flags & SB_NOINTR) || (flags & SBL_NOINTR));
+ void *lr_saved = __builtin_return_address(0);
+ struct socket *so = sb->sb_so;
+ void * wchan;
+ int error = 0;
+ thread_t tp = current_thread();
+
+ VERIFY((flags & SBL_VALID) == flags);
+
+ /* so_usecount may be 0 if we get here from sofreelastref() */
+ if (so == NULL) {
+ panic("%s: null so, sb=%p sb_flags=0x%x lr=%p\n",
+ __func__, sb, sb->sb_flags, lr_saved);
+ /* NOTREACHED */
+ } else if (so->so_usecount < 0) {
+ panic("%s: sb=%p sb_flags=0x%x sb_so=%p usecount=%d lr=%p "
+ "lrh= %s\n", __func__, sb, sb->sb_flags, so,
+ so->so_usecount, lr_saved, solockhistory_nr(so));
+ /* NOTREACHED */
+ }
+
+ /*
+ * The content filter thread must hold the sockbuf lock
+ */
+ if ((so->so_flags & SOF_CONTENT_FILTER) && sb->sb_cfil_thread == tp) {
+ /*
+ * Don't panic if we are defunct because SB_LOCK has
+ * been cleared by sodefunct()
+ */
+ if (!(so->so_flags & SOF_DEFUNCT) && !(sb->sb_flags & SB_LOCK))
+ panic("%s: SB_LOCK not held for %p\n",
+ __func__, sb);
+
+ /* Keep the sockbuf locked */
+ return (0);
+ }
+
+ if ((sb->sb_flags & SB_LOCK) && !(flags & SBL_WAIT))
+ return (EWOULDBLOCK);
+ /*
+ * We may get here from sorflush(), in which case "sb" may not
+ * point to the real socket buffer. Use the actual socket buffer
+ * address from the socket instead.
+ */
+ wchan = (sb->sb_flags & SB_RECV) ?
+ &so->so_rcv.sb_flags : &so->so_snd.sb_flags;
+
+ /*
+ * A content filter thread has exclusive access to the sockbuf
+ * until it clears the
+ */
+ while ((sb->sb_flags & SB_LOCK) ||
+ ((so->so_flags & SOF_CONTENT_FILTER) &&
+ sb->sb_cfil_thread != NULL)) {
+ lck_mtx_t *mutex_held;
+
+ /*
+ * XXX: This code should be moved up above outside of this loop;
+ * however, we may get here as part of sofreelastref(), and
+ * at that time pr_getlock() may no longer be able to return
+ * us the lock. This will be fixed in future.
+ */
+ if (so->so_proto->pr_getlock != NULL)
+ mutex_held = (*so->so_proto->pr_getlock)(so, 0);
+ else
+ mutex_held = so->so_proto->pr_domain->dom_mtx;
+
+ lck_mtx_assert(mutex_held, LCK_MTX_ASSERT_OWNED);
+
+ sb->sb_wantlock++;
+ VERIFY(sb->sb_wantlock != 0);
+
+ error = msleep(wchan, mutex_held,
+ nointr ? PSOCK : PSOCK | PCATCH,
+ nointr ? "sb_lock_nointr" : "sb_lock", NULL);
+
+ VERIFY(sb->sb_wantlock != 0);
+ sb->sb_wantlock--;
+
+ if (error == 0 && (so->so_flags & SOF_DEFUNCT) &&
+ !(flags & SBL_IGNDEFUNCT)) {
+ error = EBADF;
+ SODEFUNCTLOG(("%s[%d]: defunct so 0x%llx [%d,%d] "
+ "(%d)\n", __func__, proc_selfpid(),
+ (uint64_t)VM_KERNEL_ADDRPERM(so),
+ SOCK_DOM(so), SOCK_TYPE(so), error));
+ }
+
+ if (error != 0)
+ return (error);
+ }
+ sb->sb_flags |= SB_LOCK;
+ return (0);
+}
+
+/*
+ * Release lock on sockbuf sb
+ */
+void
+sbunlock(struct sockbuf *sb, boolean_t keeplocked)
+{
+ void *lr_saved = __builtin_return_address(0);
+ struct socket *so = sb->sb_so;
+ thread_t tp = current_thread();
+
+ /* so_usecount may be 0 if we get here from sofreelastref() */
+ if (so == NULL) {
+ panic("%s: null so, sb=%p sb_flags=0x%x lr=%p\n",
+ __func__, sb, sb->sb_flags, lr_saved);
+ /* NOTREACHED */
+ } else if (so->so_usecount < 0) {
+ panic("%s: sb=%p sb_flags=0x%x sb_so=%p usecount=%d lr=%p "
+ "lrh= %s\n", __func__, sb, sb->sb_flags, so,
+ so->so_usecount, lr_saved, solockhistory_nr(so));
+ /* NOTREACHED */
+ }
+
+ /*
+ * The content filter thread must hold the sockbuf lock
+ */
+ if ((so->so_flags & SOF_CONTENT_FILTER) && sb->sb_cfil_thread == tp) {
+ /*
+ * Don't panic if we are defunct because SB_LOCK has
+ * been cleared by sodefunct()
+ */
+ if (!(so->so_flags & SOF_DEFUNCT) &&
+ !(sb->sb_flags & SB_LOCK) &&
+ !(so->so_state & SS_DEFUNCT) &&
+ !(so->so_flags1 & SOF1_DEFUNCTINPROG)) {
+ panic("%s: SB_LOCK not held for %p\n",
+ __func__, sb);
+ }
+ /* Keep the sockbuf locked and proceed */
+ } else {
+ VERIFY((sb->sb_flags & SB_LOCK) ||
+ (so->so_state & SS_DEFUNCT) ||
+ (so->so_flags1 & SOF1_DEFUNCTINPROG));
+
+ sb->sb_flags &= ~SB_LOCK;
+
+ if (sb->sb_wantlock > 0) {
+ /*
+ * We may get here from sorflush(), in which case "sb"
+ * may not point to the real socket buffer. Use the
+ * actual socket buffer address from the socket instead.
+ */
+ wakeup((sb->sb_flags & SB_RECV) ? &so->so_rcv.sb_flags :
+ &so->so_snd.sb_flags);
+ }
+ }
+
+ if (!keeplocked) { /* unlock on exit */
+ lck_mtx_t *mutex_held;
+
+ if (so->so_proto->pr_getlock != NULL)
+ mutex_held = (*so->so_proto->pr_getlock)(so, 0);
+ else
+ mutex_held = so->so_proto->pr_domain->dom_mtx;
+
+ lck_mtx_assert(mutex_held, LCK_MTX_ASSERT_OWNED);
+
+ VERIFY(so->so_usecount != 0);
+ so->so_usecount--;
+ so->unlock_lr[so->next_unlock_lr] = lr_saved;
+ so->next_unlock_lr = (so->next_unlock_lr + 1) % SO_LCKDBG_MAX;
+ lck_mtx_unlock(mutex_held);
+ }
+}
+
+void
+sorwakeup(struct socket *so)
+{
+ if (sb_notify(&so->so_rcv))
+ sowakeup(so, &so->so_rcv);
+}
+
+void
+sowwakeup(struct socket *so)
+{
+ if (sb_notify(&so->so_snd))
+ sowakeup(so, &so->so_snd);
+}
+
+void
+soevent(struct socket *so, long hint)
+{
+ if (so->so_flags & SOF_KNOTE)
+ KNOTE(&so->so_klist, hint);
+
+ soevupcall(so, hint);
+
+ /*
+ * Don't post an event if this a subflow socket or
+ * the app has opted out of using cellular interface
+ */
+ if ((hint & SO_FILT_HINT_IFDENIED) &&
+ !(so->so_flags & SOF_MP_SUBFLOW) &&
+ !(so->so_restrictions & SO_RESTRICT_DENY_CELLULAR) &&
+ !(so->so_restrictions & SO_RESTRICT_DENY_EXPENSIVE))
+ soevent_ifdenied(so);
+}
+
+void
+soevupcall(struct socket *so, u_int32_t hint)
+{
+ if (so->so_event != NULL) {
+ caddr_t so_eventarg = so->so_eventarg;
+ int locked = hint & SO_FILT_HINT_LOCKED;
+
+ hint &= so->so_eventmask;
+ if (hint != 0) {
+ if (locked)
+ socket_unlock(so, 0);
+
+ so->so_event(so, so_eventarg, hint);
+
+ if (locked)
+ socket_lock(so, 0);
+ }
+ }
+}
+
+static void
+soevent_ifdenied(struct socket *so)
+{
+ struct kev_netpolicy_ifdenied ev_ifdenied;
+
+ bzero(&ev_ifdenied, sizeof (ev_ifdenied));
+ /*
+ * The event consumer is interested about the effective {upid,pid,uuid}
+ * info which can be different than the those related to the process
+ * that recently performed a system call on the socket, i.e. when the
+ * socket is delegated.
+ */
+ if (so->so_flags & SOF_DELEGATED) {
+ ev_ifdenied.ev_data.eupid = so->e_upid;
+ ev_ifdenied.ev_data.epid = so->e_pid;
+ uuid_copy(ev_ifdenied.ev_data.euuid, so->e_uuid);
+ } else {
+ ev_ifdenied.ev_data.eupid = so->last_upid;
+ ev_ifdenied.ev_data.epid = so->last_pid;
+ uuid_copy(ev_ifdenied.ev_data.euuid, so->last_uuid);
+ }
+
+ if (++so->so_ifdenied_notifies > 1) {
+ /*
+ * Allow for at most one kernel event to be generated per
+ * socket; so_ifdenied_notifies is reset upon changes in
+ * the UUID policy. See comments in inp_update_policy.
+ */
+ if (net_io_policy_log) {
+ uuid_string_t buf;
+
+ uuid_unparse(ev_ifdenied.ev_data.euuid, buf);
+ log(LOG_DEBUG, "%s[%d]: so 0x%llx [%d,%d] epid %d "
+ "euuid %s%s has %d redundant events supressed\n",
+ __func__, so->last_pid,
+ (uint64_t)VM_KERNEL_ADDRPERM(so), SOCK_DOM(so),
+ SOCK_TYPE(so), ev_ifdenied.ev_data.epid, buf,
+ ((so->so_flags & SOF_DELEGATED) ?
+ " [delegated]" : ""), so->so_ifdenied_notifies);
+ }
+ } else {
+ if (net_io_policy_log) {
+ uuid_string_t buf;
+
+ uuid_unparse(ev_ifdenied.ev_data.euuid, buf);
+ log(LOG_DEBUG, "%s[%d]: so 0x%llx [%d,%d] epid %d "
+ "euuid %s%s event posted\n", __func__,
+ so->last_pid, (uint64_t)VM_KERNEL_ADDRPERM(so),
+ SOCK_DOM(so), SOCK_TYPE(so),
+ ev_ifdenied.ev_data.epid, buf,
+ ((so->so_flags & SOF_DELEGATED) ?
+ " [delegated]" : ""));
+ }
+ netpolicy_post_msg(KEV_NETPOLICY_IFDENIED, &ev_ifdenied.ev_data,
+ sizeof (ev_ifdenied));
+ }
+}