+static int
+tcp_ip_output(struct socket *so, struct tcpcb *tp, struct mbuf *pkt,
+ int cnt, struct mbuf *opt, int flags, int sack_in_progress, boolean_t isipv6)
+{
+ int error = 0;
+ boolean_t chain;
+ boolean_t unlocked = FALSE;
+ boolean_t ifdenied = FALSE;
+ struct inpcb *inp = tp->t_inpcb;
+ struct ip_out_args ipoa;
+ struct route ro;
+ struct ifnet *outif = NULL;
+
+ bzero(&ipoa, sizeof(ipoa));
+ ipoa.ipoa_boundif = IFSCOPE_NONE;
+ ipoa.ipoa_flags = IPOAF_SELECT_SRCIF | IPOAF_BOUND_SRCADDR;
+ ipoa.ipoa_sotc = SO_TC_UNSPEC;
+ ipoa.ipoa_netsvctype = _NET_SERVICE_TYPE_UNSPEC;
+#if INET6
+ struct ip6_out_args ip6oa;
+ struct route_in6 ro6;
+
+ bzero(&ip6oa, sizeof(ip6oa));
+ ip6oa.ip6oa_boundif = IFSCOPE_NONE;
+ ip6oa.ip6oa_flags = IP6OAF_SELECT_SRCIF | IP6OAF_BOUND_SRCADDR;
+ ip6oa.ip6oa_sotc = SO_TC_UNSPEC;
+ ip6oa.ip6oa_netsvctype = _NET_SERVICE_TYPE_UNSPEC;
+
+ struct flowadv *adv =
+ (isipv6 ? &ip6oa.ip6oa_flowadv : &ipoa.ipoa_flowadv);
+#else /* INET6 */
+ struct flowadv *adv = &ipoa.ipoa_flowadv;
+#endif /* !INET6 */
+
+ /* If socket was bound to an ifindex, tell ip_output about it */
+ if (inp->inp_flags & INP_BOUND_IF) {
+#if INET6
+ if (isipv6) {
+ ip6oa.ip6oa_boundif = inp->inp_boundifp->if_index;
+ ip6oa.ip6oa_flags |= IP6OAF_BOUND_IF;
+ } else
+#endif /* INET6 */
+ {
+ ipoa.ipoa_boundif = inp->inp_boundifp->if_index;
+ ipoa.ipoa_flags |= IPOAF_BOUND_IF;
+ }
+ }
+
+ if (INP_NO_CELLULAR(inp)) {
+#if INET6
+ if (isipv6)
+ ip6oa.ip6oa_flags |= IP6OAF_NO_CELLULAR;
+ else
+#endif /* INET6 */
+ ipoa.ipoa_flags |= IPOAF_NO_CELLULAR;
+ }
+ if (INP_NO_EXPENSIVE(inp)) {
+#if INET6
+ if (isipv6)
+ ip6oa.ip6oa_flags |= IP6OAF_NO_EXPENSIVE;
+ else
+#endif /* INET6 */
+ ipoa.ipoa_flags |= IPOAF_NO_EXPENSIVE;
+
+ }
+ if (INP_AWDL_UNRESTRICTED(inp)) {
+#if INET6
+ if (isipv6)
+ ip6oa.ip6oa_flags |= IP6OAF_AWDL_UNRESTRICTED;
+ else
+#endif /* INET6 */
+ ipoa.ipoa_flags |= IPOAF_AWDL_UNRESTRICTED;
+
+ }
+#if INET6
+ if (INP_INTCOPROC_ALLOWED(inp) && isipv6) {
+ ip6oa.ip6oa_flags |= IP6OAF_INTCOPROC_ALLOWED;
+ }
+ if (isipv6) {
+ ip6oa.ip6oa_sotc = so->so_traffic_class;
+ ip6oa.ip6oa_netsvctype = so->so_netsvctype;
+ } else
+#endif /* INET6 */
+ {
+ ipoa.ipoa_sotc = so->so_traffic_class;
+ ipoa.ipoa_netsvctype = so->so_netsvctype;
+ }
+ if ((so->so_flags1 & SOF1_QOSMARKING_ALLOWED)) {
+#if INET6
+ if (isipv6)
+ ip6oa.ip6oa_flags |= IP6OAF_QOSMARKING_ALLOWED;
+ else
+#endif /* INET6 */
+ ipoa.ipoa_flags |= IPOAF_QOSMARKING_ALLOWED;
+ }
+#if INET6
+ if (isipv6)
+ flags |= IPV6_OUTARGS;
+ else
+#endif /* INET6 */
+ flags |= IP_OUTARGS;
+
+ /* Copy the cached route and take an extra reference */
+#if INET6
+ if (isipv6)
+ in6p_route_copyout(inp, &ro6);
+ else
+#endif /* INET6 */
+ inp_route_copyout(inp, &ro);
+
+ /*
+ * Make sure ACK/DELACK conditions are cleared before
+ * we unlock the socket.
+ */
+ tp->last_ack_sent = tp->rcv_nxt;
+ tp->t_flags &= ~(TF_ACKNOW | TF_DELACK);
+ tp->t_timer[TCPT_DELACK] = 0;
+ tp->t_unacksegs = 0;
+
+ /* 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 INET6
+ if (isipv6) {
+ 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 &&
+ so->so_snd.sb_cc > 0) {
+ /* Update the send byte count */
+ if (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)))
+ 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);
+}
+
+int tcptv_persmin_val = TCPTV_PERSMIN;
+