X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/8ae6c0891d11b5aa1f590d9185be474a72d0d9ad..fcfe450e4ae7bcb3ff4d45b10c8a7c30aa1b3c16:/usr/src/sys/netinet/tcp_output.c diff --git a/usr/src/sys/netinet/tcp_output.c b/usr/src/sys/netinet/tcp_output.c index f75fda289c..b166b98451 100644 --- a/usr/src/sys/netinet/tcp_output.c +++ b/usr/src/sys/netinet/tcp_output.c @@ -1,4 +1,4 @@ -/* tcp_output.c 4.35 82/03/20 */ +/* tcp_output.c 4.46 82/10/09 */ #include "../h/param.h" #include "../h/systm.h" @@ -6,33 +6,28 @@ #include "../h/protosw.h" #include "../h/socket.h" #include "../h/socketvar.h" -#include "../net/in.h" -#include "../net/in_pcb.h" -#include "../net/in_systm.h" -#include "../net/ip.h" -#include "../net/ip_var.h" -#include "../net/tcp.h" +#include "../netinet/in.h" +#include "../net/route.h" +#include "../netinet/in_pcb.h" +#include "../netinet/in_systm.h" +#include "../netinet/ip.h" +#include "../netinet/ip_var.h" +#include "../netinet/tcp.h" #define TCPOUTFLAGS -#include "../net/tcp_fsm.h" -#include "../net/tcp_seq.h" -#include "../net/tcp_timer.h" -#include "../net/tcp_var.h" -#include "../net/tcpip.h" -#include "../net/tcp_debug.h" -#include "../errno.h" +#include "../netinet/tcp_fsm.h" +#include "../netinet/tcp_seq.h" +#include "../netinet/tcp_timer.h" +#include "../netinet/tcp_var.h" +#include "../netinet/tcpip.h" +#include "../netinet/tcp_debug.h" +#include char *tcpstates[]; /* XXX */ /* - * Initial options: indicate max segment length 1/2 of space - * allocated for receive; if TCPTRUEOOB is defined, indicate - * willingness to do true out-of-band. + * Initial options. */ -#ifndef TCPTRUEOOB u_char tcp_initopt[4] = { TCPOPT_MAXSEG, 4, 0x0, 0x0, }; -#else -u_char tcp_initopt[6] = { TCPOPT_MAXSEG, 4, 0x0, 0x0, TCPOPT_WILLOOB, 2 }; -#endif /* * Tcp output routine: figure out what should be sent and send it. @@ -43,13 +38,13 @@ tcp_output(tp) register struct socket *so = tp->t_inpcb->inp_socket; register int len; struct mbuf *m0; - int off, flags, win; + int off, flags, win, error; register struct mbuf *m; register struct tcpiphdr *ti; u_char *opt; unsigned optlen = 0; + int sendalot; -COUNT(TCP_OUTPUT); /* * Determine length of data that should be transmitted, @@ -57,12 +52,16 @@ COUNT(TCP_OUTPUT); * If there is some data or critical controls (SYN, RST) * to send, then transmit; otherwise, investigate further. */ +again: + sendalot = 0; off = tp->snd_nxt - tp->snd_una; len = MIN(so->so_snd.sb_cc, tp->snd_wnd+tp->t_force) - off; if (len < 0) - return (0); /* past FIN */ - if (len > tp->t_maxseg) + return (0); /* ??? */ /* past FIN */ + if (len > tp->t_maxseg) { len = tp->t_maxseg; + sendalot = 1; + } flags = tcp_outflags[tp->t_state]; if (tp->snd_nxt + len < tp->snd_una + so->so_snd.sb_cc) @@ -92,13 +91,6 @@ COUNT(TCP_OUTPUT); if (tp->t_flags&TF_ACKNOW) goto send; -#ifdef TCPTRUEOOB - /* - * Send if an out of band data or ack should be transmitted. - */ - if (tp->t_oobflags&(TCPOOB_OWEACK|TCPOOB_NEEDACK))) - goto send; -#endif /* * Calculate available window in i, and also amount @@ -111,6 +103,33 @@ COUNT(TCP_OUTPUT); ((100*(win-(tp->rcv_adv-tp->rcv_nxt))/so->so_rcv.sb_hiwat) >= 35)) goto send; + /* + * TCP window updates are not reliable, rather a polling protocol + * using ``persist'' packets is used to insure receipt of window + * updates. The three ``states'' for the output side are: + * idle not doing retransmits or persists + * persisting to move a zero window + * (re)transmitting and thereby not persisting + * + * tp->t_timer[TCPT_PERSIST] + * is set when we are in persist state. + * tp->t_force + * is set when we are called to send a persist packet. + * tp->t_timer[TCPT_REXMT] + * is set when we are retransmitting + * The output side is idle when both timers are zero. + * + * If send window is closed, there is data to transmit, and no + * retransmit or persist is pending, then go to persist state, + * arranging to force out a byte to get more current window information + * if nothing happens soon. + */ + if (tp->snd_wnd == 0 && so->so_snd.sb_cc && + tp->t_timer[TCPT_REXMT] == 0 && tp->t_timer[TCPT_PERSIST] == 0) { + tp->t_rxtshift = 0; + tcp_setpersist(tp); + } + /* * No reason to send a segment, just return. */ @@ -124,7 +143,7 @@ send: */ MGET(m, 0); if (m == 0) - return (0); + return (ENOBUFS); m->m_off = MMAXOFF - sizeof (struct tcpiphdr); m->m_len = sizeof (struct tcpiphdr); if (len) { @@ -156,7 +175,7 @@ send: goto noopt; opt = tcp_initopt; optlen = sizeof (tcp_initopt); - *(u_short *)(opt + 2) = so->so_rcv.sb_hiwat / 2; + *(u_short *)(opt + 2) = MIN(so->so_rcv.sb_hiwat / 2, 1024); #if vax *(u_short *)(opt + 2) = htons(*(u_short *)(opt + 2)); #endif @@ -166,50 +185,19 @@ send: opt = mtod(tp->t_tcpopt, u_char *); optlen = tp->t_tcpopt->m_len; } -#ifndef TCPTRUEOOB - if (opt) -#else - if (opt || (tp->t_oobflags&(TCPOOB_OWEACK|TCPOOB_NEEDACK))) -#endif - { + if (opt) { m0 = m->m_next; m->m_next = m_get(M_DONTWAIT); if (m->m_next == 0) { (void) m_free(m); m_freem(m0); - return (0); + return (ENOBUFS); } m->m_next->m_next = m0; m0 = m->m_next; - m0->m_off = MMINOFF; m0->m_len = optlen; bcopy((caddr_t)opt, mtod(m0, caddr_t), optlen); opt = (u_char *)(mtod(m0, caddr_t) + optlen); -#ifdef TCPTRUEOOB - if (tp->t_oobflags&TCPOOB_OWEACK) { -printf("tp %x send OOBACK for %x\n", tp->t_iobseq); - *opt++ = TCPOPT_OOBACK; - *opt++ = 3; - *opt++ = tp->t_iobseq; - m0->m_len += 3; - tp->t_oobflags &= ~TCPOOB_OWEACK; - /* sender should rexmt oob to force ack repeat */ - } - if (tp->t_oobflags&TCPOOB_NEEDACK) { -printf("tp %x send OOBDATA seq %x data %x\n", tp->t_oobseq, tp->t_oobc); - *opt++ = TCPOPT_OOBDATA; - *opt++ = 8; - *opt++ = tp->t_oobseq; - *opt++ = tp->t_oobc; - *(tcp_seq *)opt = tp->t_oobmark - tp->snd_nxt; -#ifdef vax - *(tcp_seq *)opt = htonl((unsigned)*(tcp_seq *)opt); -#endif - m0->m_len += 8; - TCPT_RANGESET(tp->t_timer[TCPT_OOBREXMT], - tcp_beta * tp->t_srtt, TCPTV_MIN, TCPTV_MAX); - } -#endif while (m0->m_len & 0x3) { *opt++ = TCPOPT_EOL; m0->m_len++; @@ -242,7 +230,13 @@ noopt: * number wraparound. */ tp->snd_up = tp->snd_una; /* drag it along */ - /* PUSH */ + /* + * If anything to send and we can send it all, set PUSH. + * (This will keep happy those implementations which only + * give data to the user when a buffer fills or a PUSH comes in. + */ + if (len && off+len == so->so_snd.sb_cc) + ti->ti_flags |= TH_PUSH; /* * Put TCP length in extended header, and then @@ -257,49 +251,52 @@ noopt: ti->ti_sum = in_cksum(m, sizeof (struct tcpiphdr) + (int)optlen + len); /* - * Advance snd_nxt over sequence space of this segment - */ - if (flags & (TH_SYN|TH_FIN)) - tp->snd_nxt++; - tp->snd_nxt += len; - - /* - * If this transmission closes the window, - * start persistance timer at 2 round trip times - * but at least TCPTV_PERSMIN ticks. + * In transmit state, time the transmission and arrange for + * the retransmit. In persist state, reset persist time for + * next persist. */ - if (TCPS_HAVERCVDSYN(tp->t_state) && - SEQ_GEQ(tp->snd_nxt, tp->snd_una+tp->snd_wnd) && - tp->t_timer[TCPT_PERSIST] == 0) - TCPT_RANGESET(tp->t_timer[TCPT_PERSIST], - 2 * tp->t_srtt, TCPTV_PERSMIN, TCPTV_MAX); + if (tp->t_force == 0) { + /* + * Advance snd_nxt over sequence space of this segment. + */ + if (flags & (TH_SYN|TH_FIN)) + tp->snd_nxt++; + tp->snd_nxt += len; + if (SEQ_GT(tp->snd_nxt, tp->snd_max)) + tp->snd_max = tp->snd_nxt; - /* - * Time this transmission if not a retransmission and - * not currently timing anything. - */ - if (SEQ_GT(tp->snd_nxt, tp->snd_max) && tp->t_rtt == 0) { - tp->t_rtt = 1; - tp->t_rtseq = tp->snd_nxt - len; - } + /* + * Time this transmission if not a retransmission and + * not currently timing anything. + */ + if (SEQ_GT(tp->snd_nxt, tp->snd_max) && tp->t_rtt == 0) { + tp->t_rtt = 1; + tp->t_rtseq = tp->snd_nxt - len; + } - /* - * Set retransmit timer if not currently set. - * Initial value for retransmit timer to tcp_beta*tp->t_srtt. - * Initialize shift counter which is used for exponential - * backoff of retransmit time. - */ - if (tp->t_timer[TCPT_REXMT] == 0 && tp->snd_nxt != tp->snd_una) { - TCPT_RANGESET(tp->t_timer[TCPT_REXMT], - tcp_beta * tp->t_srtt, TCPTV_MIN, TCPTV_MAX); - tp->t_rtt = 0; - tp->t_rxtshift = 0; + /* + * Set retransmit timer if not currently set. + * Initial value for retransmit timer to tcp_beta*tp->t_srtt. + * Initialize shift counter which is used for exponential + * backoff of retransmit time. + */ + if (tp->t_timer[TCPT_REXMT] == 0 && + tp->snd_nxt != tp->snd_una) { + TCPT_RANGESET(tp->t_timer[TCPT_REXMT], + tcp_beta * tp->t_srtt, TCPTV_MIN, TCPTV_MAX); + tp->t_rtt = 0; + tp->t_rxtshift = 0; + } + tp->t_timer[TCPT_PERSIST] = 0; + } else { + if (SEQ_GT(tp->snd_una+1, tp->snd_max)) + tp->snd_max = tp->snd_una+1; } /* * Trace. */ - if (tp->t_inpcb->inp_socket->so_options & SO_DEBUG) + if (so->so_options & SO_DEBUG) tcp_trace(TA_OUTPUT, tp->t_state, tp, ti, 0); /* @@ -308,8 +305,9 @@ noopt: */ ((struct ip *)ti)->ip_len = sizeof (struct tcpiphdr) + optlen + len; ((struct ip *)ti)->ip_ttl = TCP_TTL; - if (ip_output(m, tp->t_ipopt, 0) == 0) - return (0); + if (error = ip_output(m, tp->t_ipopt, (so->so_options & SO_DONTROUTE) ? + &routetoif : &tp->t_inpcb->inp_route, 0)) + return (error); /* * Data sent (as far as we can tell). @@ -320,7 +318,24 @@ noopt: if (win > 0 && SEQ_GT(tp->rcv_nxt+win, tp->rcv_adv)) tp->rcv_adv = tp->rcv_nxt + win; tp->t_flags &= ~(TF_ACKNOW|TF_DELACK); - if (SEQ_GT(tp->snd_nxt, tp->snd_max)) - tp->snd_max = tp->snd_nxt; - return (1); + if (sendalot && tp->t_force == 0) + goto again; + return (0); +} + +tcp_setpersist(tp) + register struct tcpcb *tp; +{ + + if (tp->t_timer[TCPT_REXMT]) + panic("tcp_output REXMT"); + /* + * Start/restart persistance timer. + */ + TCPT_RANGESET(tp->t_timer[TCPT_PERSIST], + ((int)(tcp_beta * tp->t_srtt)) << tp->t_rxtshift, + TCPTV_PERSMIN, TCPTV_MAX); + tp->t_rxtshift++; + if (tp->t_rxtshift >= TCP_MAXRXTSHIFT) + tp->t_rxtshift = 0; }