]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/netinet/tcp_output.c
xnu-792.25.20.tar.gz
[apple/xnu.git] / bsd / netinet / tcp_output.c
index d3e5558d3b0bffd068c7a4320238411df2ddfe1b..b336fa6d877e3a677b4698304ffdf2c05f1a2efe 100644 (file)
@@ -113,7 +113,7 @@ int ss_fltsz = 1;
 SYSCTL_INT(_net_inet_tcp, OID_AUTO, slowstart_flightsize, CTLFLAG_RW,
        &ss_fltsz, 1, "Slow start flight size");
 
-int ss_fltsz_local = TCP_MAXWIN;               /* something large */
+int ss_fltsz_local = 4; /* starts with four segments max */
 SYSCTL_INT(_net_inet_tcp, OID_AUTO, local_slowstart_flightsize, CTLFLAG_RW,
        &ss_fltsz_local, 1, "Slow start flight size for local networks");
 
@@ -121,7 +121,14 @@ int     tcp_do_newreno = 0;
 SYSCTL_INT(_net_inet_tcp, OID_AUTO, newreno, CTLFLAG_RW, &tcp_do_newreno,
         0, "Enable NewReno Algorithms");
 
-struct mbuf *m_copym_with_hdrs __P((struct mbuf*, int, int, int, struct mbuf**, int*));
+int    tcp_packet_chaining = 50;
+SYSCTL_INT(_net_inet_tcp, OID_AUTO, packetchain, CTLFLAG_RW, &tcp_packet_chaining,
+        0, "Enable TCP output packet chaining");
+
+struct mbuf *m_copym_with_hdrs(struct mbuf*, int, int, int, struct mbuf**, int*);
+static long packchain_newlist = 0;
+static long packchain_looped = 0;
+static long packchain_sent = 0;
 
 
 /* temporary: for testing */
@@ -129,15 +136,36 @@ struct    mbuf *m_copym_with_hdrs __P((struct mbuf*, int, int, int, struct mbuf**,
 extern int ipsec_bypass;
 #endif
 
+extern int slowlink_wsize;     /* window correction for slow links */
+extern u_long  route_generation;
+extern int fw_enable;          /* firewall is on: disable packet chaining */
+extern int ipsec_bypass;
+
+extern vm_size_t       so_cache_zone_element_size;
+
+static __inline__ u_int16_t
+get_socket_id(struct socket * s)
+{
+       u_int16_t               val;
+
+       if (so_cache_zone_element_size == 0) {
+               return (0);
+       }
+       val = (u_int16_t)(((u_int32_t)s) / so_cache_zone_element_size);
+       if (val == 0) {
+               val = 0xffff;
+       }
+       return (val);
+}
+
 /*
  * Tcp output routine: figure out what should be sent and send it.
  */
 int
-tcp_output(tp)
-       register struct tcpcb *tp;
+tcp_output(struct tcpcb *tp)
 {
-       register struct socket *so = tp->t_inpcb->inp_socket;
-       register long len, win;
+       struct socket *so = tp->t_inpcb->inp_socket;
+       long len, recwin, sendwin;
        int off, flags, error;
        register struct mbuf *m;
        struct ip *ip = NULL;
@@ -148,59 +176,43 @@ tcp_output(tp)
        register struct tcphdr *th;
        u_char opt[TCP_MAXOLEN];
        unsigned ipoptlen, optlen, hdrlen;
-       int idle, sendalot;
+       int idle, sendalot, howmuchsent = 0;
+       int i, sack_rxmit;
+       int sack_bytes_rxmt;
+       struct sackhole *p;
+
        int maxburst = TCP_MAXBURST;
        struct rmxp_tao *taop;
        struct rmxp_tao tao_noncached;
-#if INET6
-       int isipv6;
-#endif
-       int    last_off;
+       int    last_off = 0;
        int    m_off;
        struct mbuf *m_last = 0;
        struct mbuf *m_head = 0;
-
-
-       KERNEL_DEBUG(DBG_FNC_TCP_OUTPUT | DBG_FUNC_START, 0,0,0,0,0);
+       struct mbuf *packetlist = 0;
+       struct mbuf *lastpacket = 0;
 #if INET6
-       if (isipv6 = ((tp->t_inpcb->inp_vflag & INP_IPV6) != 0)) {
-       
-               KERNEL_DEBUG(DBG_LAYER_BEG,
-                    ((tp->t_inpcb->inp_fport << 16) | tp->t_inpcb->inp_lport),
-                    (((tp->t_inpcb->in6p_laddr.s6_addr16[0] & 0xffff) << 16) |
-                     (tp->t_inpcb->in6p_faddr.s6_addr16[0] & 0xffff)),
-                    0,0,0);
-       }
-       else
+       int isipv6 = tp->t_inpcb->inp_vflag & INP_IPV6 ;
 #endif
+       short packchain_listadd = 0;
+       u_int16_t       socket_id = get_socket_id(so);
+
 
-       {
-               KERNEL_DEBUG(DBG_LAYER_BEG,
-                    ((tp->t_inpcb->inp_fport << 16) | tp->t_inpcb->inp_lport),
-                    (((tp->t_inpcb->inp_laddr.s_addr & 0xffff) << 16) |
-                     (tp->t_inpcb->inp_faddr.s_addr & 0xffff)),
-                    0,0,0);
-       }
        /*
         * Determine length of data that should be transmitted,
         * and flags that will be used.
         * If there is some data or critical controls (SYN, RST)
         * to send, then transmit; otherwise, investigate further.
         */
-       idle = (tp->snd_max == tp->snd_una);
-#ifdef __APPLE__
+       idle = (tp->t_flags & TF_LASTIDLE) || (tp->snd_max == tp->snd_una);
        if (idle && tp->t_rcvtime >= tp->t_rxtcur) {
-#else
-       if (idle && (ticks - tp->t_rcvtime) >= tp->t_rxtcur) {
-#endif
                /*
                 * We have been idle for "a while" and no acks are
                 * expected to clock out any data we send --
                 * slow start to get ack "clock" running again.
-                *       
+                *
                 * Set the slow-start flight size depending on whether
                 * this is a local network or not.
-                */      
+                */
                if (
 #if INET6
                    (isipv6 && in6_localaddr(&tp->t_inpcb->in6p_faddr)) ||
@@ -215,12 +227,144 @@ tcp_output(tp)
                else     
                        tp->snd_cwnd = tp->t_maxseg * ss_fltsz;
        }
+       tp->t_flags &= ~TF_LASTIDLE;
+       if (idle) {
+               if (tp->t_flags & TF_MORETOCOME) {
+                       tp->t_flags |= TF_LASTIDLE;
+                       idle = 0;
+               }
+       }
 again:
+       KERNEL_DEBUG(DBG_FNC_TCP_OUTPUT | DBG_FUNC_START, 0,0,0,0,0);
+
+#if INET6
+       if (isipv6) {
+       
+               KERNEL_DEBUG(DBG_LAYER_BEG,
+                    ((tp->t_inpcb->inp_fport << 16) | tp->t_inpcb->inp_lport),
+                    (((tp->t_inpcb->in6p_laddr.s6_addr16[0] & 0xffff) << 16) |
+                     (tp->t_inpcb->in6p_faddr.s6_addr16[0] & 0xffff)),
+                    sendalot,0,0);
+       }
+       else
+#endif
+
+       {
+               KERNEL_DEBUG(DBG_LAYER_BEG,
+                    ((tp->t_inpcb->inp_fport << 16) | tp->t_inpcb->inp_lport),
+                    (((tp->t_inpcb->inp_laddr.s_addr & 0xffff) << 16) |
+                     (tp->t_inpcb->inp_faddr.s_addr & 0xffff)),
+                    sendalot,0,0);
+       /*
+        * If the route generation id changed, we need to check that our
+        * local (source) IP address is still valid. If it isn't either
+        * return error or silently do nothing (assuming the address will
+        * come back before the TCP connection times out).
+        */
+
+      if ((tp->t_inpcb->inp_route.ro_rt != NULL &&
+           (tp->t_inpcb->inp_route.ro_rt->generation_id != route_generation)) || (tp->t_inpcb->inp_route.ro_rt == NULL)) {
+               /* check that the source address is still valid */
+               if (ifa_foraddr(tp->t_inpcb->inp_laddr.s_addr) == 0) {
+                       if (tp->t_state >= TCPS_CLOSE_WAIT) {
+                               tcp_close(tp);
+                               return(EADDRNOTAVAIL);
+                       }
+
+                       /* set Retransmit  timer if it wasn't set
+                        * reset Persist timer and shift register as the
+                        * adversed peer window may not be valid anymore
+                        */
+
+                        if (!tp->t_timer[TCPT_REXMT]) {
+                                tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
+                               if (tp->t_timer[TCPT_PERSIST]) {
+                                       tp->t_timer[TCPT_PERSIST] = 0;
+                                       tp->t_rxtshift = 0;
+                               }
+                       }
+
+                       if (packetlist) {
+                               error = ip_output_list(packetlist, packchain_listadd, tp->t_inpcb->inp_options, &tp->t_inpcb->inp_route,
+                                       (so->so_options & SO_DONTROUTE), 0);
+                               tp->t_lastchain = 0;
+                       }
+                       if (so->so_flags & SOF_NOADDRAVAIL)
+                               return(EADDRNOTAVAIL);
+                       else
+                               return(0); /* silently ignore and keep data in socket */
+               }
+        }
+       }
+
+       /*
+        * If we've recently taken a timeout, snd_max will be greater than
+        * snd_nxt.  There may be SACK information that allows us to avoid
+        * resending already delivered data.  Adjust snd_nxt accordingly.
+        */
+       if (tp->sack_enable && SEQ_LT(tp->snd_nxt, tp->snd_max))
+               tcp_sack_adjust(tp);
        sendalot = 0;
        off = tp->snd_nxt - tp->snd_una;
-       win = min(tp->snd_wnd, tp->snd_cwnd);
+       sendwin = min(tp->snd_wnd, tp->snd_cwnd);
+
+       if (tp->t_flags & TF_SLOWLINK && slowlink_wsize > 0)
+               sendwin = min(sendwin, slowlink_wsize);
 
        flags = tcp_outflags[tp->t_state];
+       /*
+        * Send any SACK-generated retransmissions.  If we're explicitly trying
+        * to send out new data (when sendalot is 1), bypass this function.
+        * If we retransmit in fast recovery mode, decrement snd_cwnd, since
+        * we're replacing a (future) new transmission with a retransmission
+        * now, and we previously incremented snd_cwnd in tcp_input().
+        */
+       /*
+        * Still in sack recovery , reset rxmit flag to zero.
+        */
+       sack_rxmit = 0;
+       sack_bytes_rxmt = 0;
+       len = 0;
+       p = NULL;
+       if (tp->sack_enable && IN_FASTRECOVERY(tp) &&
+           (p = tcp_sack_output(tp, &sack_bytes_rxmt))) {
+               long cwin;
+               
+               cwin = min(tp->snd_wnd, tp->snd_cwnd) - sack_bytes_rxmt;
+               if (cwin < 0)
+                       cwin = 0;
+               /* Do not retransmit SACK segments beyond snd_recover */
+               if (SEQ_GT(p->end, tp->snd_recover)) {
+                       /*
+                        * (At least) part of sack hole extends beyond
+                        * snd_recover. Check to see if we can rexmit data
+                        * for this hole.
+                        */
+                       if (SEQ_GEQ(p->rxmit, tp->snd_recover)) {
+                               /*
+                                * Can't rexmit any more data for this hole.
+                                * That data will be rexmitted in the next
+                                * sack recovery episode, when snd_recover
+                                * moves past p->rxmit.
+                                */
+                               p = NULL;
+                               goto after_sack_rexmit;
+                       } else
+                               /* Can rexmit part of the current hole */
+                               len = ((long)ulmin(cwin,
+                                                  tp->snd_recover - p->rxmit));
+               } else
+                       len = ((long)ulmin(cwin, p->end - p->rxmit));
+               off = p->rxmit - tp->snd_una;
+               if (len > 0) {
+                       sack_rxmit = 1;
+                       sendalot = 1;
+                       tcpstat.tcps_sack_rexmits++;
+                       tcpstat.tcps_sack_rexmit_bytes +=
+                           min(len, tp->t_maxseg);
+               }
+       }
+after_sack_rexmit:
        /*
         * Get standard flags, and add SYN or FIN if requested by 'hidden'
         * state flags.
@@ -237,7 +381,7 @@ again:
         * and go to transmit state.
         */
        if (tp->t_force) {
-               if (win == 0) {
+               if (sendwin == 0) {
                        /*
                         * If we still have some data to send, then
                         * clear the FIN bit.  Usually this would
@@ -256,18 +400,58 @@ again:
                         */
                        if (off < so->so_snd.sb_cc)
                                flags &= ~TH_FIN;
-                       win = 1;
+                       sendwin = 1;
                } else {
                        tp->t_timer[TCPT_PERSIST] = 0;
                        tp->t_rxtshift = 0;
                }
        }
 
-       len = (long)ulmin(so->so_snd.sb_cc, win) - off;
-
-       if ((taop = tcp_gettaocache(tp->t_inpcb)) == NULL) {
-               taop = &tao_noncached;
-               bzero(taop, sizeof(*taop));
+       /*
+        * If snd_nxt == snd_max and we have transmitted a FIN, the
+        * offset will be > 0 even if so_snd.sb_cc is 0, resulting in
+        * a negative length.  This can also occur when TCP opens up
+        * its congestion window while receiving additional duplicate
+        * acks after fast-retransmit because TCP will reset snd_nxt
+        * to snd_max after the fast-retransmit.
+        *
+        * In the normal retransmit-FIN-only case, however, snd_nxt will
+        * be set to snd_una, the offset will be 0, and the length may
+        * wind up 0.
+        *
+        * If sack_rxmit is true we are retransmitting from the scoreboard
+        * in which case len is already set.
+        */
+       if (sack_rxmit == 0) {
+               if (sack_bytes_rxmt == 0)
+                       len = ((long)ulmin(so->so_snd.sb_cc, sendwin) - off);
+               else {
+                       long cwin;
+
+                        /*
+                        * We are inside of a SACK recovery episode and are
+                        * sending new data, having retransmitted all the
+                        * data possible in the scoreboard.
+                        */
+                       len = ((long)ulmin(so->so_snd.sb_cc, tp->snd_wnd) 
+                              - off);
+                       /*
+                        * Don't remove this (len > 0) check !
+                        * We explicitly check for len > 0 here (although it 
+                        * isn't really necessary), to work around a gcc 
+                        * optimization issue - to force gcc to compute
+                        * len above. Without this check, the computation
+                        * of len is bungled by the optimizer.
+                        */
+                       if (len > 0) {
+                               cwin = tp->snd_cwnd - 
+                                       (tp->snd_nxt - tp->sack_newdata) -
+                                       sack_bytes_rxmt;
+                               if (cwin < 0)
+                                       cwin = 0;
+                               len = lmin(len, cwin);
+                       }
+               }
        }
 
        /*
@@ -278,22 +462,23 @@ again:
        if ((flags & TH_SYN) && SEQ_GT(tp->snd_nxt, tp->snd_una)) {
                flags &= ~TH_SYN;
                off--, len++;
-               if (len > 0 && tp->t_state == TCPS_SYN_SENT &&
-                   taop->tao_ccsent == 0) {
+               if (len > 0 && tp->t_state == TCPS_SYN_SENT) {
+                       if (packetlist) {
+                               error = ip_output_list(packetlist, packchain_listadd, tp->t_inpcb->inp_options, &tp->t_inpcb->inp_route,
+                               (so->so_options & SO_DONTROUTE), 0);
+                               tp->t_lastchain = 0;
+                       }
                  KERNEL_DEBUG(DBG_FNC_TCP_OUTPUT | DBG_FUNC_END, 0,0,0,0,0);
                  return 0;
                }
        }
 
        /*
-        * Be careful not to send data and/or FIN on SYN segments
-        * in cases when no CC option will be sent.
+        * Be careful not to send data and/or FIN on SYN segments.
         * This measure is needed to prevent interoperability problems
         * with not fully conformant TCP implementations.
         */
-       if ((flags & TH_SYN) &&
-           ((tp->t_flags & TF_NOOPT) || !(tp->t_flags & TF_REQ_CC) ||
-            ((flags & TH_ACK) && !(tp->t_flags & TF_RCVD_CC)))) {
+       if ((flags & TH_SYN) && (tp->t_flags & TF_NOOPT)) {
                len = 0;
                flags &= ~TH_FIN;
        }
@@ -302,7 +487,7 @@ again:
                /*
                 * If FIN has been sent but not acked,
                 * but we haven't been called to retransmit,
-                * len will be -1.  Otherwise, window shrank
+                * len will be < 0.  Otherwise, window shrank
                 * after we sent into it.  If window shrank to 0,
                 * cancel pending retransmit, pull snd_nxt back
                 * to (closed) window, and set the persist timer
@@ -310,7 +495,7 @@ again:
                 * close completely, just wait for an ACK.
                 */
                len = 0;
-               if (win == 0) {
+               if (sendwin == 0) {
                        tp->t_timer[TCPT_REXMT] = 0;
                        tp->t_rxtshift = 0;
                        tp->snd_nxt = tp->snd_una;
@@ -318,24 +503,41 @@ again:
                                tcp_setpersist(tp);
                }
        }
+
+       /*
+        * len will be >= 0 after this point.  Truncate to the maximum
+        * segment length and ensure that FIN is removed if the length
+        * no longer contains the last data byte.
+        */
        if (len > tp->t_maxseg) {
                len = tp->t_maxseg;
+               howmuchsent += len;
                sendalot = 1;
        }
-       if (SEQ_LT(tp->snd_nxt + len, tp->snd_una + so->so_snd.sb_cc))
-               flags &= ~TH_FIN;
+       if (sack_rxmit) {
+               if (SEQ_LT(p->rxmit + len, tp->snd_una + so->so_snd.sb_cc))
+                       flags &= ~TH_FIN;
+       } else {
+               if (SEQ_LT(tp->snd_nxt + len, tp->snd_una + so->so_snd.sb_cc))
+                       flags &= ~TH_FIN;
+       }
 
-       win = sbspace(&so->so_rcv);
+       if (tp->t_flags & TF_SLOWLINK && slowlink_wsize > 0 )   /* Clips window size for slow links */
+               recwin = min(sbspace(&so->so_rcv), slowlink_wsize);
+       else
+               recwin = sbspace(&so->so_rcv);
 
        /*
-        * Sender silly window avoidance.  If connection is idle
-        * and can send all data, a maximum segment,
-        * at least a maximum default-size segment do it,
-        * or are forced, do it; otherwise don't bother.
-        * If peer's buffer is tiny, then send
-        * when window is at least half open.
-        * If retransmitting (possibly after persist timer forced us
-        * to send into a small window), then must resend.
+        * Sender silly window avoidance.   We transmit under the following
+        * conditions when len is non-zero:
+        *
+        *      - We have a full segment
+        *      - This is the last buffer in a write()/send() and we are
+        *        either idle or running NODELAY
+        *      - we've timed out (e.g. persist timer)
+        *      - we have more then 1/2 the maximum send window's worth of
+        *        data (receiver may be limited the window size)
+        *      - we need to retransmit
         */
        if (len) {
                if (len == tp->t_maxseg)
@@ -349,7 +551,9 @@ again:
                        goto send;
                if (len >= tp->max_sndwnd / 2 && tp->max_sndwnd > 0)
                        goto send;
-               if (SEQ_LT(tp->snd_nxt, tp->snd_max))
+               if (SEQ_LT(tp->snd_nxt, tp->snd_max))   /* retransmit case */
+                       goto send;
+               if (sack_rxmit)
                        goto send;
        }
 
@@ -359,14 +563,15 @@ again:
         * next expected input).  If the difference is at least two
         * max size segments, or at least 50% of the maximum possible
         * window, then want to send a window update to peer.
+        * Skip this if the connection is in T/TCP half-open state.
         */
-       if (win > 0) {
+       if (recwin > 0 && !(tp->t_flags & TF_NEEDSYN)) {
                /*
                 * "adv" is the amount we can increase the window,
                 * taking into account that we are limited by
                 * TCP_MAXWIN << tp->rcv_scale.
                 */
-               long adv = min(win, (long)TCP_MAXWIN << tp->rcv_scale) -
+               long adv = min(recwin, (long)TCP_MAXWIN << tp->rcv_scale) -
                        (tp->rcv_adv - tp->rcv_nxt);
 
                if (adv >= (long) (2 * tp->t_maxseg))
@@ -376,7 +581,8 @@ again:
        }
 
        /*
-        * Send if we owe peer an ACK.
+        * Send if we owe the peer an ACK, RST, SYN, or urgent data.  ACKNOW
+        * is also a catch-all for the retransmit timer timeout case.
         */
        if (tp->t_flags & TF_ACKNOW)
                goto send;
@@ -387,13 +593,22 @@ again:
                goto send;
        /*
         * If our state indicates that FIN should be sent
-        * and we have not yet done so, or we're retransmitting the FIN,
-        * then we need to send.
+        * and we have not yet done so, then we need to send.
         */
        if (flags & TH_FIN &&
            ((tp->t_flags & TF_SENTFIN) == 0 || tp->snd_nxt == tp->snd_una))
                goto send;
-
+       /*
+        * In SACK, it is possible for tcp_output to fail to send a segment
+        * after the retransmission timer has been turned off.  Make sure
+        * that the retransmission timer is set.
+        */
+       if (tp->sack_enable && SEQ_GT(tp->snd_max, tp->snd_una) &&
+               tp->t_timer[TCPT_REXMT] == 0 &&
+           tp->t_timer[TCPT_PERSIST] == 0) {
+                       tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
+                       goto just_return;
+       } 
        /*
         * TCP window updates are not reliable, rather a polling protocol
         * using ``persist'' packets is used to insure receipt of window
@@ -421,10 +636,15 @@ again:
                tp->t_rxtshift = 0;
                tcp_setpersist(tp);
        }
-
+just_return:
        /*
-        * No reason to send a segment, just return.
+        * If there is no reason to send a segment, just return.
+        * but if there is some packets left in the packet list, send them now.
         */
+       if (packetlist) {
+               error = ip_output_list(packetlist, packchain_listadd, tp->t_inpcb->inp_options, &tp->t_inpcb->inp_route,
+                       (so->so_options & SO_DONTROUTE), 0);
+       }
        KERNEL_DEBUG(DBG_FNC_TCP_OUTPUT | DBG_FUNC_END, 0,0,0,0,0);
        return (0);
 
@@ -486,78 +706,92 @@ send:
                optlen += TCPOLEN_TSTAMP_APPA;
        }
 
-       /*
-        * Send `CC-family' options if our side wants to use them (TF_REQ_CC),
-        * options are allowed (!TF_NOOPT) and it's not a RST.
-        */
-       if ((tp->t_flags & (TF_REQ_CC|TF_NOOPT)) == TF_REQ_CC &&
-            (flags & TH_RST) == 0) {
-               switch (flags & (TH_SYN|TH_ACK)) {
-               /*
-                * This is a normal ACK, send CC if we received CC before
-                * from our peer.
+       if (tp->sack_enable && ((tp->t_flags & TF_NOOPT) == 0)) {
+               /* 
+                * Tack on the SACK permitted option *last*.
+                * And do padding of options after tacking this on.
+                * This is because of MSS, TS, WinScale and Signatures are
+                * all present, we have just 2 bytes left for the SACK
+                * permitted option, which is just enough.
                 */
-               case TH_ACK:
-                       if (!(tp->t_flags & TF_RCVD_CC))
-                               break;
-                       /*FALLTHROUGH*/
-
                /*
-                * We can only get here in T/TCP's SYN_SENT* state, when
-                * we're a sending a non-SYN segment without waiting for
-                * the ACK of our SYN.  A check above assures that we only
-                * do this if our peer understands T/TCP.
+                * If this is the first SYN of connection (not a SYN
+                * ACK), include SACK permitted option.  If this is a
+                * SYN ACK, include SACK permitted option if peer has
+                * already done so. This is only for active connect,
+                * since the syncache takes care of the passive connect.
                 */
-               case 0:
-                       opt[optlen++] = TCPOPT_NOP;
-                       opt[optlen++] = TCPOPT_NOP;
-                       opt[optlen++] = TCPOPT_CC;
-                       opt[optlen++] = TCPOLEN_CC;
-                       *(u_int32_t *)&opt[optlen] = htonl(tp->cc_send);
-
-                       optlen += 4;
-                       break;
+               if ((flags & TH_SYN) &&
+                   (!(flags & TH_ACK) || (tp->t_flags & TF_SACK_PERMIT))) {
+                       u_char *bp;
+                       bp = (u_char *)opt + optlen;
+
+                       *bp++ = TCPOPT_SACK_PERMITTED;
+                       *bp++ = TCPOLEN_SACK_PERMITTED;
+                       optlen += TCPOLEN_SACK_PERMITTED;
+               }
 
                /*
-                * This is our initial SYN, check whether we have to use
-                * CC or CC.new.
+                * Send SACKs if necessary.  This should be the last
+                * option processed.  Only as many SACKs are sent as
+                * are permitted by the maximum options size.
+                *
+                * In general, SACK blocks consume 8*n+2 bytes.
+                * So a full size SACK blocks option is 34 bytes
+                * (to generate 4 SACK blocks).  At a minimum,
+                * we need 10 bytes (to generate 1 SACK block).
+                * If TCP Timestamps (12 bytes) and TCP Signatures
+                * (18 bytes) are both present, we'll just have
+                * 10 bytes for SACK options 40 - (12 + 18).
                 */
-               case TH_SYN:
-                       opt[optlen++] = TCPOPT_NOP;
-                       opt[optlen++] = TCPOPT_NOP;
-                       opt[optlen++] = tp->t_flags & TF_SENDCCNEW ?
-                                               TCPOPT_CCNEW : TCPOPT_CC;
-                       opt[optlen++] = TCPOLEN_CC;
-                       *(u_int32_t *)&opt[optlen] = htonl(tp->cc_send);
-                       optlen += 4;
-                       break;
+               if (TCPS_HAVEESTABLISHED(tp->t_state) &&
+                   (tp->t_flags & TF_SACK_PERMIT) && tp->rcv_numsacks > 0 &&
+                   MAX_TCPOPTLEN - optlen - 2 >= TCPOLEN_SACK) {
+                       int nsack, sackoptlen, padlen;
+                       u_char *bp = (u_char *)opt + optlen;
+                       u_int32_t *lp;
 
-               /*
-                * This is a SYN,ACK; send CC and CC.echo if we received
-                * CC from our peer.
-                */
-               case (TH_SYN|TH_ACK):
-                       if (tp->t_flags & TF_RCVD_CC) {
-                               opt[optlen++] = TCPOPT_NOP;
-                               opt[optlen++] = TCPOPT_NOP;
-                               opt[optlen++] = TCPOPT_CC;
-                               opt[optlen++] = TCPOLEN_CC;
-                               *(u_int32_t *)&opt[optlen] =
-                                       htonl(tp->cc_send);
-                               optlen += 4;
-                               opt[optlen++] = TCPOPT_NOP;
-                               opt[optlen++] = TCPOPT_NOP;
-                               opt[optlen++] = TCPOPT_CCECHO;
-                               opt[optlen++] = TCPOLEN_CC;
-                               *(u_int32_t *)&opt[optlen] =
-                                       htonl(tp->cc_recv);
-                               optlen += 4;
+                       nsack = (MAX_TCPOPTLEN - optlen - 2) / TCPOLEN_SACK;
+                       nsack = min(nsack, tp->rcv_numsacks);
+                       sackoptlen = (2 + nsack * TCPOLEN_SACK);
+
+                       /*
+                        * First we need to pad options so that the
+                        * SACK blocks can start at a 4-byte boundary
+                        * (sack option and length are at a 2 byte offset).
+                        */
+                       padlen = (MAX_TCPOPTLEN - optlen - sackoptlen) % 4;
+                       optlen += padlen;
+                       while (padlen-- > 0)
+                               *bp++ = TCPOPT_NOP;
+
+                       tcpstat.tcps_sack_send_blocks++;
+                       *bp++ = TCPOPT_SACK;
+                       *bp++ = sackoptlen;
+                       lp = (u_int32_t *)bp;
+                       for (i = 0; i < nsack; i++) {
+                               struct sackblk sack = tp->sackblks[i];
+                               *lp++ = htonl(sack.start);
+                               *lp++ = htonl(sack.end);
                        }
-                       break;
+                       optlen += sackoptlen;
                }
-       }
+       }
+
+       /* Pad TCP options to a 4 byte boundary */
+       if (optlen < MAX_TCPOPTLEN && (optlen % sizeof(u_int32_t))) {
+               int pad = sizeof(u_int32_t) - (optlen % sizeof(u_int32_t));
+               u_char *bp = (u_char *)opt + optlen;
+
+               optlen += pad;
+               while (pad) {
+                       *bp++ = TCPOPT_EOL;
+                       pad--;
+               }
+       }
+
+       hdrlen += optlen;
 
-       hdrlen += optlen;
 #if INET6
        if (isipv6)
                ipoptlen = ip6_optlen(tp->t_inpcb);
@@ -567,9 +801,8 @@ send:
                if (tp->t_inpcb->inp_options) {
                        ipoptlen = tp->t_inpcb->inp_options->m_len -
                                offsetof(struct ipoption, ipopt_list);
-               } else {
+               } else
                        ipoptlen = 0;
-               }
        }
 #if IPSEC
        if (ipsec_bypass == 0)
@@ -588,6 +821,7 @@ send:
                 */
                flags &= ~TH_FIN;
                len = tp->t_maxopd - optlen - ipoptlen;
+               howmuchsent += len;
                sendalot = 1;
        }
 
@@ -668,6 +902,12 @@ send:
                                m->m_data += max_linkhdr;
                                m->m_len = hdrlen;
                        }
+                       /* makes sure we still have data left to be sent at this point */
+                       if (so->so_snd.sb_mb == NULL || off == -1) {
+                               if (m != NULL)  m_freem(m);
+                               error = 0; /* should we return an error? */
+                               goto out;
+                       }
                        m_copydata(so->so_snd.sb_mb, off, (int) len,
                            mtod(m, caddr_t) + hdrlen);
                        m->m_len += len;
@@ -694,7 +934,13 @@ send:
                                        m_last = NULL;
                                last_off = off + len;
                                m_head = so->so_snd.sb_mb;
-
+       
+                               /* makes sure we still have data left to be sent at this point */
+                               if (m_head == NULL) {
+                                       error = 0; /* should we return an error? */
+                                       goto out;
+                               }
+                               
                                /*
                                 * m_copym_with_hdrs will always return the last mbuf pointer and the offset into it that
                                 * it acted on to fullfill the current request, whether a valid 'hint' was passed in or not
@@ -740,7 +986,7 @@ send:
                m->m_data += max_linkhdr;
                m->m_len = hdrlen;
        }
-       m->m_pkthdr.rcvif = (struct ifnet *)0;
+       m->m_pkthdr.rcvif = 0;
 #if INET6
        if (isipv6) {
                ip6 = mtod(m, struct ip6_hdr *);
@@ -777,10 +1023,16 @@ send:
         * case, since we know we aren't doing a retransmission.
         * (retransmit and persist are mutually exclusive...)
         */
-       if (len || (flags & (TH_SYN|TH_FIN)) || tp->t_timer[TCPT_PERSIST])
-               th->th_seq = htonl(tp->snd_nxt);
-       else
-               th->th_seq = htonl(tp->snd_max);
+       if (sack_rxmit == 0) {
+               if (len || (flags & (TH_SYN|TH_FIN)) || tp->t_timer[TCPT_PERSIST])
+                       th->th_seq = htonl(tp->snd_nxt);
+               else
+                       th->th_seq = htonl(tp->snd_max);
+       } else {
+               th->th_seq = htonl(p->rxmit);
+               p->rxmit += len;
+               tp->sackhint.sack_bytes_rexmit += len;
+       }
        th->th_ack = htonl(tp->rcv_nxt);
        if (optlen) {
                bcopy(opt, th + 1, optlen);
@@ -791,13 +1043,33 @@ send:
         * Calculate receive window.  Don't shrink window,
         * but avoid silly window syndrome.
         */
-       if (win < (long)(so->so_rcv.sb_hiwat / 4) && win < (long)tp->t_maxseg)
-               win = 0;
-       if (win < (long)(tp->rcv_adv - tp->rcv_nxt))
-               win = (long)(tp->rcv_adv - tp->rcv_nxt);
-       if (win > (long)TCP_MAXWIN << tp->rcv_scale)
-               win = (long)TCP_MAXWIN << tp->rcv_scale;
-       th->th_win = htons((u_short) (win>>tp->rcv_scale));
+       if (recwin < (long)(so->so_rcv.sb_hiwat / 4) && recwin < (long)tp->t_maxseg)
+               recwin = 0;
+       if (recwin < (long)(tp->rcv_adv - tp->rcv_nxt))
+               recwin = (long)(tp->rcv_adv - tp->rcv_nxt);
+       if (tp->t_flags & TF_SLOWLINK && slowlink_wsize > 0) {
+               if (recwin > (long)slowlink_wsize) 
+                       recwin = slowlink_wsize;
+                       th->th_win = htons((u_short) (recwin>>tp->rcv_scale));
+       }
+       else {
+               if (recwin > (long)TCP_MAXWIN << tp->rcv_scale)
+                       recwin = (long)TCP_MAXWIN << tp->rcv_scale;
+               th->th_win = htons((u_short) (recwin>>tp->rcv_scale));
+       }
+
+       /*
+        * Adjust the RXWIN0SENT flag - indicate that we have advertised
+        * a 0 window.  This may cause the remote transmitter to stall.  This
+        * flag tells soreceive() to disable delayed acknowledgements when
+        * draining the buffer.  This can occur if the receiver is attempting
+        * to read more data then can be buffered prior to transmitting on
+        * the connection.
+        */
+       if (recwin == 0)
+               tp->t_flags |= TF_RXWIN0SENT;
+       else
+               tp->t_flags &= ~TF_RXWIN0SENT;
        if (SEQ_GT(tp->snd_up, tp->snd_nxt)) {
                th->th_urp = htons((u_short)(tp->snd_up - tp->snd_nxt));
                th->th_flags |= TH_URG;
@@ -831,10 +1103,6 @@ send:
                if (len + optlen)
                        th->th_sum = in_addword(th->th_sum, 
                                htons((u_short)(optlen + len)));
-
-               /* IP version must be set here for ipv4/ipv6 checking later */
-               KASSERT(ip->ip_v == IPVERSION,
-                       ("%s: IP version incorrect: %d", __FUNCTION__, ip->ip_v));
       }
 
        /*
@@ -855,6 +1123,8 @@ send:
                                tp->t_flags |= TF_SENTFIN;
                        }
                }
+               if (sack_rxmit)
+                       goto timer;
                tp->snd_nxt += len;
                if (SEQ_GT(tp->snd_nxt, tp->snd_max)) {
                        tp->snd_max = tp->snd_nxt;
@@ -877,17 +1147,31 @@ send:
                 * Initialize shift counter which is used for backoff
                 * of retransmit time.
                 */
+timer:
                if (tp->t_timer[TCPT_REXMT] == 0 &&
-                   tp->snd_nxt != tp->snd_una) {
-                       tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
+                   ((sack_rxmit && tp->snd_nxt != tp->snd_max) ||
+                       tp->snd_nxt != tp->snd_una)) {
                        if (tp->t_timer[TCPT_PERSIST]) {
                                tp->t_timer[TCPT_PERSIST] = 0;
                                tp->t_rxtshift = 0;
                        }
+                       tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
                }
-       } else
-               if (SEQ_GT(tp->snd_nxt + len, tp->snd_max))
+       } else {
+               /*
+                * Persist case, update snd_max but since we are in
+                * persist mode (no window) we do not update snd_nxt.
+                */
+               int xlen = len;
+               if (flags & TH_SYN)
+                       ++xlen;
+               if (flags & TH_FIN) {
+                       ++xlen;
+                       tp->t_flags |= TF_SENTFIN;
+               }
+               if (SEQ_GT(tp->snd_nxt + xlen, tp->snd_max))
                        tp->snd_max = tp->snd_nxt + len;
+       }
 
 #if TCPDEBUG
        /*
@@ -928,17 +1212,18 @@ send:
                        goto out;
                }
 #endif /*IPSEC*/
+               m->m_pkthdr.socket_id = socket_id;
                error = ip6_output(m,
                            tp->t_inpcb->in6p_outputopts,
                            &tp->t_inpcb->in6p_route,
-                           (so->so_options & SO_DONTROUTE), NULL, NULL);
+                           (so->so_options & SO_DONTROUTE), NULL, NULL, 0);
        } else
 #endif /* INET6 */
     {
        struct rtentry *rt;
        ip->ip_len = m->m_pkthdr.len;
 #if INET6
-       if (INP_CHECK_SOCKAF(so, AF_INET6))
+       if (isipv6)
                ip->ip_ttl = in6_selecthlim(tp->t_inpcb,
                                            tp->t_inpcb->in6p_route.ro_rt ?
                                            tp->t_inpcb->in6p_route.ro_rt->rt_ifp
@@ -984,29 +1269,78 @@ send:
        if (ipsec_bypass == 0)
                ipsec_setsocket(m, so);
 #endif /*IPSEC*/
-       error = ip_output(m, tp->t_inpcb->inp_options, &tp->t_inpcb->inp_route,
-           (so->so_options & SO_DONTROUTE), 0);
-    }
+
+       /*
+        * The socket is kept locked while sending out packets in ip_output, even if packet chaining is not active.
+        */
+
+       m->m_pkthdr.socket_id = socket_id;
+       if (packetlist) {
+               m->m_nextpkt = NULL;
+               lastpacket->m_nextpkt = m;
+               lastpacket = m;
+               packchain_listadd++;
+       }
+       else {
+               m->m_nextpkt = NULL;
+               packchain_newlist++;
+               packetlist = lastpacket = m;
+               packchain_listadd=0;
+       }
+
+       if ((ipsec_bypass == 0) || fw_enable || sendalot == 0 || (tp->t_state != TCPS_ESTABLISHED) || 
+                     (tp->snd_cwnd <= (tp->snd_wnd / 4)) || 
+                     (tp->t_flags & (TH_PUSH | TF_ACKNOW)) || tp->t_force != 0 ||
+                     packchain_listadd >= tcp_packet_chaining) {
+               lastpacket->m_nextpkt = 0;
+               error = ip_output_list(packetlist, packchain_listadd, tp->t_inpcb->inp_options, &tp->t_inpcb->inp_route,
+                       (so->so_options & SO_DONTROUTE), 0);
+               tp->t_lastchain = packchain_listadd;
+               packchain_sent++;
+               packetlist = NULL;
+               if (error == 0)
+                       howmuchsent = 0;
+       }
+       else {
+               error = 0;
+               packchain_looped++;
+               tcpstat.tcps_sndtotal++;
+               if (recwin > 0 && SEQ_GT(tp->rcv_nxt+recwin, tp->rcv_adv))
+                       tp->rcv_adv = tp->rcv_nxt + recwin;
+               tp->last_ack_sent = tp->rcv_nxt;
+               tp->t_flags &= ~(TF_ACKNOW|TF_DELACK);
+               goto again;
+       }
+   }
        if (error) {
 
                /*
                 * We know that the packet was lost, so back out the
                 * sequence number advance, if any.
                 */
-               if (tp->t_force == 0 || !tp->t_timer[TCPT_PERSIST]) {
+               if (tp->t_force == 0 || tp->t_timer[TCPT_PERSIST] == 0) {
                        /*
                         * No need to check for TH_FIN here because
                         * the TF_SENTFIN flag handles that case.
                         */
-                       if ((flags & TH_SYN) == 0)
-                               tp->snd_nxt -= len;
+                       if ((flags & TH_SYN) == 0) {
+                               if (sack_rxmit) {
+                                       p->rxmit -= howmuchsent;
+                                       tp->sackhint.sack_bytes_rexmit -= howmuchsent;
+                               } else
+                                       tp->snd_nxt -= howmuchsent;
+                       }
                }
+               howmuchsent = 0;
 out:
                if (error == ENOBUFS) {
                         if (!tp->t_timer[TCPT_REXMT] &&
                              !tp->t_timer[TCPT_PERSIST])
                                 tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
                        tcp_quench(tp->t_inpcb, 0);
+                       if (packetlist)
+                               m_freem_list(packetlist);
+                       tp->t_lastchain = 0;
                        KERNEL_DEBUG(DBG_FNC_TCP_OUTPUT | DBG_FUNC_END, 0,0,0,0,0);
                        return (0);
                }
@@ -1018,18 +1352,28 @@ out:
                         * not do so here.
                         */
                        tcp_mtudisc(tp->t_inpcb, 0);
+                       if (packetlist)
+                               m_freem_list(packetlist);
+                       tp->t_lastchain = 0;
                        KERNEL_DEBUG(DBG_FNC_TCP_OUTPUT | DBG_FUNC_END, 0,0,0,0,0);
                        return 0;
                }
                if ((error == EHOSTUNREACH || error == ENETDOWN)
                    && TCPS_HAVERCVDSYN(tp->t_state)) {
                        tp->t_softerror = error;
+                       if (packetlist)
+                               m_freem_list(packetlist);
+                       tp->t_lastchain = 0;
                        KERNEL_DEBUG(DBG_FNC_TCP_OUTPUT | DBG_FUNC_END, 0,0,0,0,0);
                        return (0);
                }
+               if (packetlist)
+                       m_freem_list(packetlist);
+               tp->t_lastchain = 0;
                KERNEL_DEBUG(DBG_FNC_TCP_OUTPUT | DBG_FUNC_END, 0,0,0,0,0);
                return (error);
        }
+sentit:
        tcpstat.tcps_sndtotal++;
 
        /*
@@ -1038,13 +1382,14 @@ out:
         * then remember the size of the advertised window.
         * Any pending ACK has now been sent.
         */
-       if (win > 0 && SEQ_GT(tp->rcv_nxt+win, tp->rcv_adv))
-               tp->rcv_adv = tp->rcv_nxt + win;
+       if (recwin > 0 && SEQ_GT(tp->rcv_nxt+recwin, tp->rcv_adv))
+               tp->rcv_adv = tp->rcv_nxt + recwin;
        tp->last_ack_sent = tp->rcv_nxt;
        tp->t_flags &= ~(TF_ACKNOW|TF_DELACK);
-       if (sendalot)
+
+       KERNEL_DEBUG(DBG_FNC_TCP_OUTPUT | DBG_FUNC_END,0,0,0,0,0);
+       if (sendalot && (!tcp_do_newreno || --maxburst))
                goto again;
-       KERNEL_DEBUG(DBG_FNC_TCP_OUTPUT | DBG_FUNC_END, 0,0,0,0,0);
        return (0);
 }
 
@@ -1053,7 +1398,6 @@ tcp_setpersist(tp)
        register struct tcpcb *tp;
 {
        int t = ((tp->t_srtt >> 2) + tp->t_rttvar) >> 1;
-       int tt;
 
        if (tp->t_timer[TCPT_REXMT])
                panic("tcp_setpersist: retransmit pending");