big footnotes, too.
[unix-history] / usr / src / sys / netinet / tcp_output.c
CommitLineData
fa627691 1/* tcp_output.c 4.32 82/03/12 */
76ee76df
BJ
2
3#include "../h/param.h"
4#include "../h/systm.h"
5#include "../h/mbuf.h"
405c9168 6#include "../h/protosw.h"
76ee76df 7#include "../h/socket.h"
d52566dd 8#include "../h/socketvar.h"
0974b45c
BJ
9#include "../net/in.h"
10#include "../net/in_pcb.h"
11#include "../net/in_systm.h"
d52566dd 12#include "../net/ip.h"
eb44bfb2 13#include "../net/ip_var.h"
d52566dd 14#include "../net/tcp.h"
0974b45c 15#define TCPOUTFLAGS
d52566dd 16#include "../net/tcp_fsm.h"
0974b45c
BJ
17#include "../net/tcp_seq.h"
18#include "../net/tcp_timer.h"
19#include "../net/tcp_var.h"
20#include "../net/tcpip.h"
f1dd32da 21#include "../net/tcp_debug.h"
f1b2fa5b 22#include "../errno.h"
76ee76df 23
4aed14e3 24char *tcpstates[]; /* XXX */
8b5a83bb
BJ
25
26/*
27 * Initial options: indicate max segment length 1/2 of space
28 * allocated for receive; if TCPTRUEOOB is defined, indicate
29 * willingness to do true out-of-band.
30 */
31#ifndef TCPTRUEOOB
32u_char tcp_initopt[4] = { TCPOPT_MAXSEG, 4, 0x0, 0x0, };
33#else
34u_char tcp_initopt[6] = { TCPOPT_MAXSEG, 4, 0x0, 0x0, TCPOPT_WILLOOB, 2 };
35#endif
36
ea727f86 37/*
4aed14e3 38 * Tcp output routine: figure out what should be sent and send it.
ea727f86 39 */
a6503abf 40tcp_output(tp)
53a5409e 41 register struct tcpcb *tp;
ea727f86 42{
53a5409e 43 register struct socket *so = tp->t_inpcb->inp_socket;
a6503abf
BJ
44 register int len;
45 struct mbuf *m0;
fa627691 46 int off, flags, win;
a6503abf
BJ
47 register struct mbuf *m;
48 register struct tcpiphdr *ti;
8b5a83bb
BJ
49 u_char *opt;
50 unsigned optlen = 0;
76ee76df 51
a6503abf 52COUNT(TCP_OUTPUT);
76ee76df 53
a6503abf 54 /*
0974b45c
BJ
55 * Determine length of data that can be transmitted,
56 * and flags that will be used.
57 * If there is some data or critical controls (SYN, RST)
58 * to send, then transmit; otherwise, investigate further.
a6503abf
BJ
59 */
60 off = tp->snd_nxt - tp->snd_una;
405c9168 61 len = MIN(so->so_snd.sb_cc, tp->snd_wnd+tp->t_force) - off;
3232ef09 62 if (len < 0)
fa627691 63 return (0); /* past FIN */
0974b45c
BJ
64 if (len > tp->t_maxseg)
65 len = tp->t_maxseg;
66 flags = tcp_outflags[tp->t_state];
275e05b7 67 if (tp->snd_nxt + len < tp->snd_una + so->so_snd.sb_cc)
405c9168 68 flags &= ~TH_FIN;
3232ef09 69 if (len || (flags & (TH_SYN|TH_RST|TH_FIN)))
a6503abf
BJ
70 goto send;
71
72 /*
3232ef09 73 * Send if we owe peer an ACK.
a6503abf 74 */
8b5a83bb
BJ
75 if (tp->t_flags&TF_ACKNOW)
76 goto send;
77
78#ifdef TCPTRUEOOB
79 /*
80 * Send if an out of band data or ack should be transmitted.
81 */
82 if (tp->t_oobflags&(TCPOOB_OWEACK|TCPOOB_NEEDACK)))
a6503abf 83 goto send;
8b5a83bb 84#endif
76ee76df 85
a6503abf
BJ
86 /*
87 * Calculate available window in i, and also amount
88 * of window known to peer (as advertised window less
89 * next expected input.) If this is 35% or more of the
90 * maximum possible window, then want to send a segment to peer.
91 */
0974b45c
BJ
92 win = sbspace(&so->so_rcv);
93 if (win > 0 &&
94 ((100*(win-(tp->rcv_adv-tp->rcv_nxt))/so->so_rcv.sb_hiwat) >= 35))
a6503abf
BJ
95 goto send;
96
97 /*
98 * No reason to send a segment, just return.
99 */
f1b2fa5b 100 return (0);
a6503abf
BJ
101
102send:
103 /*
104 * Grab a header mbuf, attaching a copy of data to
105 * be transmitted, and initialize the header from
106 * the template for sends on this connection.
107 */
76ee76df
BJ
108 MGET(m, 0);
109 if (m == 0)
110 return (0);
4aed14e3 111 m->m_off = MMAXOFF - sizeof (struct tcpiphdr);
53a5409e 112 m->m_len = sizeof (struct tcpiphdr);
a6503abf
BJ
113 if (len) {
114 m->m_next = m_copy(so->so_snd.sb_mb, off, len);
115 if (m->m_next == 0)
116 len = 0;
117 }
118 ti = mtod(m, struct tcpiphdr *);
119 if (tp->t_template == 0)
120 panic("tcp_output");
f1b2fa5b 121 bcopy((caddr_t)tp->t_template, (caddr_t)ti, sizeof (struct tcpiphdr));
a6503abf
BJ
122
123 /*
124 * Fill in fields, remembering maximum advertised
125 * window for use in delaying messages about window sizes.
126 */
4aed14e3
BJ
127 ti->ti_seq = tp->snd_nxt;
128 ti->ti_ack = tp->rcv_nxt;
129#if vax
130 ti->ti_seq = htonl(ti->ti_seq);
131 ti->ti_ack = htonl(ti->ti_ack);
132#endif
8b5a83bb
BJ
133 /*
134 * Before ESTABLISHED, force sending of initial options
135 * unless TCP set to not do any options.
136 */
137 if (tp->t_state < TCPS_ESTABLISHED) {
138 if (tp->t_flags&TF_NOOPT)
139 goto noopt;
140 opt = tcp_initopt;
141 optlen = sizeof (tcp_initopt);
142 *(u_short *)(opt + 2) = so->so_rcv.sb_hiwat / 2;
143#if vax
144 *(u_short *)(opt + 2) = htons(*(u_short *)(opt + 2));
145#endif
146 } else {
147 if (tp->t_tcpopt == 0)
148 goto noopt;
149 opt = mtod(tp->t_tcpopt, u_char *);
150 optlen = tp->t_tcpopt->m_len;
151 }
152#ifndef TCPTRUEOOB
153 if (opt)
154#else
155 if (opt || (tp->t_oobflags&(TCPOOB_OWEACK|TCPOOB_NEEDACK)))
156#endif
157 {
f1b2fa5b 158 m0 = m->m_next;
ef9b4258 159 m->m_next = m_get(M_DONTWAIT);
0974b45c
BJ
160 if (m->m_next == 0) {
161 (void) m_free(m);
8b5a83bb 162 m_freem(m0);
0974b45c
BJ
163 return (0);
164 }
165 m->m_next->m_next = m0;
8b5a83bb
BJ
166 m0 = m->m_next;
167 m0->m_off = MMINOFF;
168 m0->m_len = optlen;
169 bcopy(opt, mtod(m0, caddr_t), optlen);
170 opt = (u_char *)(mtod(m0, caddr_t) + optlen);
171#ifdef TCPTRUEOOB
172 if (tp->t_oobflags&TCPOOB_OWEACK) {
173printf("tp %x send OOBACK for %x\n", tp->t_iobseq);
174 *opt++ = TCPOPT_OOBACK;
175 *opt++ = 3;
176 *opt++ = tp->t_iobseq;
177 m0->m_len += 3;
178 tp->t_oobflags &= ~TCPOOB_OWEACK;
179 /* sender should rexmt oob to force ack repeat */
180 }
181 if (tp->t_oobflags&TCPOOB_NEEDACK) {
182printf("tp %x send OOBDATA seq %x data %x\n", tp->t_oobseq, tp->t_oobc);
183 *opt++ = TCPOPT_OOBDATA;
b2db9217 184 *opt++ = 8;
8b5a83bb
BJ
185 *opt++ = tp->t_oobseq;
186 *opt++ = tp->t_oobc;
b2db9217
BJ
187 *(tcp_seq *)opt = tp->t_oobmark - tp->snd_nxt;
188#ifdef vax
189 *(tcp_seq *)opt = htonl((unsigned)*(tcp_seq *)opt);
190#endif
191 m0->m_len += 8;
8b5a83bb
BJ
192 TCPT_RANGESET(tp->t_timer[TCPT_OOBREXMT],
193 tcp_beta * tp->t_srtt, TCPTV_MIN, TCPTV_MAX);
194 }
195#endif
196 while (m0->m_len & 0x3) {
197 *opt++ = TCPOPT_EOL;
198 m0->m_len++;
199 }
200 optlen = m0->m_len;
201 ti->ti_off = (sizeof (struct tcphdr) + optlen) >> 2;
0974b45c 202 }
8b5a83bb 203noopt:
0974b45c 204 ti->ti_flags = flags;
a6503abf
BJ
205 win = sbspace(&so->so_rcv);
206 if (win > 0)
f1b2fa5b 207 ti->ti_win = htons((u_short)win);
0974b45c 208 if (SEQ_GT(tp->snd_up, tp->snd_nxt)) {
5e74df82
BJ
209 ti->ti_urp = tp->snd_up - tp->snd_nxt;
210#if vax
211 ti->ti_urp = htons(ti->ti_urp);
212#endif
a6503abf
BJ
213 ti->ti_flags |= TH_URG;
214 } else
215 /*
216 * If no urgent pointer to send, then we pull
217 * the urgent pointer to the left edge of the send window
218 * so that it doesn't drift into the send window on sequence
219 * number wraparound.
220 */
0974b45c
BJ
221 tp->snd_up = tp->snd_una; /* drag it along */
222 /* PUSH */
a6503abf
BJ
223
224 /*
225 * Put TCP length in extended header, and then
226 * checksum extended header and data.
227 */
8b5a83bb
BJ
228 if (len + optlen) {
229 ti->ti_len = sizeof (struct tcphdr) + optlen + len;
230#if vax
231 ti->ti_len = htons((u_short)ti->ti_len);
232#endif
233 }
234 ti->ti_sum = in_cksum(m, sizeof (struct tcpiphdr) + optlen + len);
0974b45c
BJ
235
236 /*
237 * Advance snd_nxt over sequence space of this segment
238 */
239 if (flags & (TH_SYN|TH_FIN))
4aed14e3 240 tp->snd_nxt++;
0974b45c
BJ
241 tp->snd_nxt += len;
242
243 /*
405c9168 244 * If this transmission closes the window,
4aed14e3
BJ
245 * start persistance timer at 2 round trip times
246 * but at least TCPTV_PERSMIN ticks.
0974b45c 247 */
89413846 248 if (TCPS_HAVERCVDSYN(tp->t_state) &&
275e05b7 249 SEQ_GEQ(tp->snd_nxt, tp->snd_una+tp->snd_wnd) &&
4aed14e3
BJ
250 tp->t_timer[TCPT_PERSIST] == 0)
251 TCPT_RANGESET(tp->t_timer[TCPT_PERSIST],
252 2 * tp->t_srtt, TCPTV_PERSMIN, TCPTV_MAX);
405c9168
BJ
253
254 /*
255 * Time this transmission if not a retransmission and
256 * not currently timing anything.
257 */
258 if (SEQ_GT(tp->snd_nxt, tp->snd_max) && tp->t_rtt == 0) {
259 tp->t_rtt = 1;
260 tp->t_rtseq = tp->snd_nxt - len;
261 }
262
263 /*
264 * Set retransmit timer if not currently set.
4aed14e3 265 * Initial value for retransmit timer to tcp_beta*tp->t_srtt.
405c9168
BJ
266 * Initialize shift counter which is used for exponential
267 * backoff of retransmit time.
268 */
4aed14e3
BJ
269 if (tp->t_timer[TCPT_REXMT] == 0 && tp->snd_nxt != tp->snd_una) {
270 TCPT_RANGESET(tp->t_timer[TCPT_REXMT],
271 tcp_beta * tp->t_srtt, TCPTV_MIN, TCPTV_MAX);
275e05b7 272 tp->t_rtt = 0;
405c9168 273 tp->t_rxtshift = 0;
0974b45c 274 }
a6503abf 275
f1dd32da
BJ
276 /*
277 * Trace.
278 */
279 if (tp->t_inpcb->inp_socket->so_options & SO_DEBUG)
280 tcp_trace(TA_OUTPUT, tp->t_state, tp, ti, 0);
281
a6503abf
BJ
282 /*
283 * Fill in IP length and desired time to live and
284 * send to IP level.
285 */
8b5a83bb 286 ((struct ip *)ti)->ip_len = sizeof (struct tcpiphdr) + optlen + len;
a6503abf 287 ((struct ip *)ti)->ip_ttl = TCP_TTL;
5e74df82 288 if (ip_output(m, tp->t_ipopt) == 0)
0974b45c 289 return (0);
a6503abf
BJ
290
291 /*
292 * Data sent (as far as we can tell).
293 * If this advertises a larger window than any other segment,
4aed14e3 294 * then remember the size of the advertised window.
0974b45c 295 * Drop send for purpose of ACK requirements.
a6503abf 296 */
be43ac7f 297 if (win > 0 && SEQ_GT(tp->rcv_nxt+win, tp->rcv_adv))
a6503abf 298 tp->rcv_adv = tp->rcv_nxt + win;
0974b45c 299 tp->t_flags &= ~(TF_ACKNOW|TF_DELACK);
4aed14e3
BJ
300 if (SEQ_GT(tp->snd_nxt, tp->snd_max))
301 tp->snd_max = tp->snd_nxt;
0974b45c 302 return (1);
76ee76df 303}