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