+ /* Increment the count of outstanding send operations */
+ inp->inp_sndinprog_cnt++;
+
+ /*
+ * If allowed, unlock TCP socket while in IP
+ * but only if the connection is established and
+ * in a normal mode where reentrancy on the tcpcb won't be
+ * an issue:
+ * - there is no SACK episode
+ * - we're not in Fast Recovery mode
+ * - if we're not sending from an upcall.
+ */
+ if (tcp_output_unlocked && !so->so_upcallusecount &&
+ (tp->t_state == TCPS_ESTABLISHED) && (sack_in_progress == 0) &&
+ !IN_FASTRECOVERY(tp) && !(so->so_flags & SOF_MP_SUBFLOW)) {
+
+ unlocked = TRUE;
+ socket_unlock(so, 0);
+ }
+
+ /*
+ * Don't send down a chain of packets when:
+ * - TCP chaining is disabled
+ * - there is an IPsec rule set
+ * - there is a non default rule set for the firewall
+ */
+
+ chain = tcp_packet_chaining > 1
+#if IPSEC
+ && ipsec_bypass
+#endif
+#if IPFIREWALL
+ && (fw_enable == 0 || fw_bypass)
+#endif
+ ; // I'm important, not extraneous
+
+ while (pkt != NULL) {
+ struct mbuf *npkt = pkt->m_nextpkt;
+
+ if (!chain) {
+ pkt->m_nextpkt = NULL;
+ /*
+ * If we are not chaining, make sure to set the packet
+ * list count to 0 so that IP takes the right path;
+ * this is important for cases such as IPsec where a
+ * single mbuf might result in multiple mbufs as part
+ * of the encapsulation. If a non-zero count is passed
+ * down to IP, the head of the chain might change and
+ * we could end up skipping it (thus generating bogus
+ * packets). Fixing it in IP would be desirable, but
+ * for now this would do it.
+ */
+ cnt = 0;
+ }
+#if INET6
+ if (isipv6) {
+ error = ip6_output_list(pkt, cnt,
+ inp->in6p_outputopts, &ro6, flags, NULL, NULL,
+ &ip6oa);
+ ifdenied = (ip6oa.ip6oa_retflags & IP6OARF_IFDENIED);
+ } else {
+#endif /* INET6 */
+ error = ip_output_list(pkt, cnt, opt, &ro, flags, NULL,
+ &ipoa);
+ ifdenied = (ipoa.ipoa_retflags & IPOARF_IFDENIED);
+ }
+
+ if (chain || error) {
+ /*
+ * If we sent down a chain then we are done since
+ * the callee had taken care of everything; else
+ * we need to free the rest of the chain ourselves.
+ */
+ if (!chain)
+ m_freem_list(npkt);
+ break;
+ }
+ pkt = npkt;
+ }
+
+ if (unlocked)
+ socket_lock(so, 0);
+
+ /*
+ * Enter flow controlled state if the connection is established
+ * and is not in recovery. Flow control is allowed only if there
+ * is outstanding data.
+ *
+ * A connection will enter suspended state even if it is in
+ * recovery.
+ */
+ if (((adv->code == FADV_FLOW_CONTROLLED && !IN_FASTRECOVERY(tp)) ||
+ adv->code == FADV_SUSPENDED) &&
+ !(tp->t_flags & TF_CLOSING) &&
+ tp->t_state == TCPS_ESTABLISHED &&
+ SEQ_GT(tp->snd_max, tp->snd_una)) {
+ int rc;
+ rc = inp_set_fc_state(inp, adv->code);
+
+ if (rc == 1)
+ tcp_ccdbg_trace(tp, NULL,
+ ((adv->code == FADV_FLOW_CONTROLLED) ?
+ TCP_CC_FLOW_CONTROL : TCP_CC_SUSPEND));
+ }
+
+ /*
+ * When an interface queue gets suspended, some of the
+ * packets are dropped. Return ENOBUFS, to update the
+ * pcb state.
+ */
+ if (adv->code == FADV_SUSPENDED)
+ error = ENOBUFS;
+
+ VERIFY(inp->inp_sndinprog_cnt > 0);
+ if ( --inp->inp_sndinprog_cnt == 0) {
+ inp->inp_flags &= ~(INP_FC_FEEDBACK);
+ if (inp->inp_sndingprog_waiters > 0) {
+ wakeup(&inp->inp_sndinprog_cnt);
+ }
+ }
+
+#if INET6
+ if (isipv6) {
+ /*
+ * When an NECP IP tunnel policy forces the outbound interface,
+ * ip6_output_list() informs the transport layer what is the actual
+ * outgoing interface
+ */
+ if (ip6oa.ip6oa_flags & IP6OAF_BOUND_IF) {
+ outif = ifindex2ifnet[ip6oa.ip6oa_boundif];
+ } else if (ro6.ro_rt != NULL) {
+ outif = ro6.ro_rt->rt_ifp;
+ }
+ } else
+#endif /* INET6 */
+ if (ro.ro_rt != NULL)
+ outif = ro.ro_rt->rt_ifp;
+
+ if (outif != NULL && outif != inp->inp_last_outifp) {
+ /* Update the send byte count */
+ if (so->so_snd.sb_cc > 0 && so->so_snd.sb_flags & SB_SNDBYTE_CNT) {
+ inp_decr_sndbytes_total(so, so->so_snd.sb_cc);
+ inp_decr_sndbytes_allunsent(so, tp->snd_una);
+ so->so_snd.sb_flags &= ~SB_SNDBYTE_CNT;
+ }
+ inp->inp_last_outifp = outif;
+
+ }
+
+ if (error != 0 && ifdenied &&
+ (INP_NO_CELLULAR(inp) || INP_NO_EXPENSIVE(inp) || INP_NO_CONSTRAINED(inp)))
+ soevent(so,
+ (SO_FILT_HINT_LOCKED|SO_FILT_HINT_IFDENIED));
+
+ /* Synchronize cached PCB route & options */
+#if INET6
+ if (isipv6)
+ in6p_route_copyin(inp, &ro6);
+ else
+#endif /* INET6 */
+ inp_route_copyin(inp, &ro);
+
+ if (tp->t_state < TCPS_ESTABLISHED && tp->t_rxtshift == 0 &&
+ tp->t_inpcb->inp_route.ro_rt != NULL) {
+ /* If we found the route and there is an rtt on it
+ * reset the retransmit timer
+ */
+ tcp_getrt_rtt(tp, tp->t_inpcb->in6p_route.ro_rt);
+ tp->t_timer[TCPT_REXMT] = OFFSET_FROM_START(tp, tp->t_rxtcur);
+ }
+ return error;