+ /* 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) &&
+ ((tp->t_flags & TF_FASTRECOVERY) == 0)) {
+
+ 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;
+ }
+#ifdef INET6
+ if (isipv6)
+ error = ip6_output_list(pkt, cnt,
+ inp->in6p_outputopts, &ro6, flags, NULL, NULL,
+ &ip6oa);
+ else
+#endif
+ error = ip_output_list(pkt, cnt, opt, &ro, flags, NULL,
+ &ipoa);
+
+ 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.
+ *
+ * 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) {
+ int rc;
+ rc = inp_set_fc_state(inp, adv->code);
+
+ if (rc == 1)
+ DTRACE_TCP5(cc, void, NULL, struct inpcb *, inp,
+ struct tcpcb *, tp, struct tcphdr *, NULL,
+ int32_t, ((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);
+
+#ifdef INET6
+ if (isipv6) {
+ if (ro6.ro_rt != NULL && (outif = ro6.ro_rt->rt_ifp) !=
+ inp->in6p_last_outifp)
+ inp->in6p_last_outifp = outif;
+ } else
+#endif
+ if (ro.ro_rt != NULL && (outif = ro.ro_rt->rt_ifp) !=
+ inp->inp_last_outifp)
+ inp->inp_last_outifp = outif;
+
+ if ((inp->inp_flags & INP_NO_IFT_CELLULAR) && outif != NULL &&
+ outif->if_type == IFT_CELLULAR)
+ soevent(inp->inp_socket,
+ (SO_FILT_HINT_LOCKED|SO_FILT_HINT_IFDENIED));
+
+ /* Synchronize cached PCB route & options */
+#ifdef INET6
+ if (isipv6)
+ in6p_route_copyin(inp, &ro6);
+ else
+#endif
+ 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);