Commit | Line | Data |
---|---|---|
76ee76df BJ |
1 | /* tcp_output.c 4.1 81/10/30 */ |
2 | ||
3 | #include "../h/param.h" | |
4 | #include "../h/systm.h" | |
5 | #include "../h/mbuf.h" | |
6 | #include "../h/socket.h" | |
7 | #include "../inet/inet.h" | |
8 | #include "../inet/inet_host.h" | |
9 | #include "../inet/inet_systm.h" | |
10 | #include "../inet/imp.h" | |
11 | #include "../inet/ip.h" | |
12 | #include "../inet/tcp.h" | |
13 | #include "../inet/tcp_fsm.h" | |
14 | ||
15 | send(tp) /* send data */ | |
16 | register struct tcb *tp; | |
17 | { | |
18 | register struct ucb *up; | |
19 | register unsigned long last, wind; | |
20 | struct mbuf *m; | |
21 | int flags = 0, forced, sent; | |
22 | struct mbuf *tcp_sndcopy(); | |
23 | int len; | |
24 | ||
25 | COUNT(SEND); | |
26 | up = tp->t_ucb; | |
27 | tp->snd_lst = tp->snd_nxt; | |
28 | forced = 0; | |
29 | m = NULL; | |
30 | ||
31 | if (tp->snd_nxt == tp->iss) { /* first data to be sent */ | |
32 | flags |= TH_SYN; | |
33 | tp->snd_lst++; | |
34 | } | |
35 | ||
36 | /* get seq # of last datum in send buffer */ | |
37 | ||
38 | last = tp->snd_off; | |
39 | for (m = up->uc_sbuf; m != NULL; m = m->m_next) | |
40 | last += m->m_len; | |
41 | ||
42 | /* no data to send in buffer */ | |
43 | ||
44 | if (tp->snd_nxt > last) { | |
45 | ||
46 | /* should we send FIN? don't unless haven't already sent one */ | |
47 | ||
48 | if ((tp->tc_flags&TC_SND_FIN) && | |
49 | (tp->seq_fin == tp->iss || tp->snd_nxt <= tp->seq_fin)) { | |
50 | ||
51 | flags |= TH_FIN; | |
52 | tp->seq_fin = tp->snd_lst++; | |
53 | } | |
54 | ||
55 | } else { /* there is data to send */ | |
56 | ||
57 | /* send data only if there is a window defined */ | |
58 | ||
59 | if (tp->tc_flags&TC_SYN_ACKED) { | |
60 | ||
61 | wind = tp->snd_una + tp->snd_wnd; | |
62 | ||
63 | /* use window to limit send */ | |
64 | ||
65 | tp->snd_lst = min(last, wind); | |
66 | ||
67 | /* make sure we don't do ip fragmentation */ | |
68 | ||
69 | if ((len = tp->snd_lst - tp->snd_nxt) > 1024) | |
70 | tp->snd_lst -= len - 1024; | |
71 | ||
72 | /* set persist timer */ | |
73 | ||
74 | if (tp->snd_lst >= wind) | |
75 | tp->t_persist = T_PERS; | |
76 | } | |
77 | ||
78 | /* check if window is closed and must force a byte out */ | |
79 | ||
80 | if ((tp->tc_flags&TC_FORCE_ONE) && (tp->snd_lst == wind)) { | |
81 | tp->snd_lst = tp->snd_nxt + 1; | |
82 | forced = 1; | |
83 | } | |
84 | ||
85 | /* copy data to send from send buffer */ | |
86 | ||
87 | m = tcp_sndcopy(tp, max(tp->iss+1,tp->snd_nxt), tp->snd_lst); | |
88 | ||
89 | /* see if EOL should be sent */ | |
90 | ||
91 | if (tp->snd_end > tp->iss && tp->snd_end <= tp->snd_lst) | |
92 | flags |= TH_EOL; | |
93 | ||
94 | /* must send FIN and no more data left to send after this */ | |
95 | ||
96 | if ((tp->tc_flags&TC_SND_FIN) && !forced && | |
97 | tp->snd_lst == last && | |
98 | (tp->seq_fin == tp->iss || tp->snd_nxt <= tp->seq_fin)) { | |
99 | ||
100 | flags |= TH_FIN; | |
101 | tp->seq_fin = tp->snd_lst++; | |
102 | } | |
103 | } | |
104 | ||
105 | if (tp->snd_nxt < tp->snd_lst) { /* something to send */ | |
106 | ||
107 | if (tp->tc_flags & TC_SND_URG) | |
108 | flags |= TH_URG; | |
109 | sent = tcp_output(tp, flags, tp->snd_lst - tp->snd_nxt, m); | |
110 | /* set timers for retransmission if necessary */ | |
111 | ||
112 | if (!forced) { | |
113 | tp->t_rexmt = tp->t_xmtime; | |
114 | tp->t_rexmt_val = tp->snd_lst; | |
115 | ||
116 | if ((tp->tc_flags&TC_REXMT) == 0) { | |
117 | tp->t_rexmttl = T_REXMTTL; | |
118 | tp->t_rtl_val = tp->snd_lst; | |
119 | } | |
120 | ||
121 | } | |
122 | ||
123 | /* update seq for next send if this one got out */ | |
124 | ||
125 | if (sent) | |
126 | tp->snd_nxt = tp->snd_lst; | |
127 | ||
128 | ||
129 | /* if last timed message has been acked, start timing | |
130 | this one */ | |
131 | ||
132 | if ((tp->tc_flags&TC_SYN_ACKED) && tp->snd_una > tp->t_xmt_val) { | |
133 | tp->t_xmt = 0; | |
134 | tp->t_xmt_val = tp->snd_lst; | |
135 | } | |
136 | ||
137 | tp->tc_flags &= ~(TC_ACK_DUE|TC_REXMT|TC_FORCE_ONE); | |
138 | tp->snd_hi = max(tp->snd_nxt, tp->snd_hi); | |
139 | return (1); | |
140 | } | |
141 | ||
142 | return(0); | |
143 | } | |
144 | ||
145 | tcp_sndctl(tp) /* send a control msg */ | |
146 | struct tcb *tp; | |
147 | { | |
148 | COUNT(SEND_CTL); | |
149 | if (!send(tp)) { | |
150 | tcp_sndnull(tp); | |
151 | return(0); | |
152 | } | |
153 | return(1); | |
154 | } | |
155 | ||
156 | int printhave = 0; | |
157 | ||
158 | tcp_sndwin(tp) | |
159 | struct tcb *tp; | |
160 | { | |
161 | int ihave; | |
162 | int hehas; | |
163 | ||
164 | if (tp->rcv_adv) { | |
165 | /* figure out window we would advertise */ | |
166 | ihave = tp->t_ucb->uc_rhiwat - | |
167 | (tp->t_ucb->uc_rcc + tp->seqcnt); | |
168 | hehas = tp->rcv_adv - tp->rcv_nxt; | |
169 | if (printhave) | |
170 | printf("ihave %d, hehas %d\n", ihave, hehas); | |
171 | if (hehas > 32 && | |
172 | (100*(ihave-hehas)/tp->t_ucb->uc_rhiwat) < 35) | |
173 | return; | |
174 | if (printhave) | |
175 | printf("update him\n"); | |
176 | } | |
177 | if (send(tp)) | |
178 | return (1); | |
179 | tcp_sndnull(tp); | |
180 | return (0); | |
181 | } | |
182 | tcp_sndnull(tp) /* send only control information */ | |
183 | register struct tcb *tp; | |
184 | { | |
185 | COUNT(SEND_NULL); | |
186 | ||
187 | tcp_output(tp, 0, 0, (struct mbuf *)0); | |
188 | tp->tc_flags &= ~TC_ACK_DUE; | |
189 | } | |
190 | ||
191 | tcp_sndrst(tp, n) /* send a reset */ | |
192 | register struct tcb *tp; | |
193 | register struct th *n; | |
194 | { | |
195 | COUNT(SEND_RST); | |
196 | /* don't send a reset in response to a reset */ | |
197 | ||
198 | if (n->th_flags&TH_RST) | |
199 | return; | |
200 | ||
201 | tp->tc_flags |= TC_SND_RST; | |
202 | ||
203 | if (n->th_flags&TH_ACK) | |
204 | tp->snd_nxt = n->t_ackno; | |
205 | ||
206 | tp->tc_flags &= ~TC_SYN_RCVD; | |
207 | tcp_sndnull(tp); | |
208 | tp->tc_flags &= ~TC_SND_RST; | |
209 | } | |
210 | ||
211 | /* | |
212 | * Create template to be used to send tcp packets on a connection. | |
213 | * Call after host entry created, allocates an mbuf and fills | |
214 | * in a skeletal tcp/ip header, minimizing the amount of work | |
215 | * necessary when the connection is used. | |
216 | */ | |
217 | struct th * | |
218 | tcp_template(tp) | |
219 | struct tcb *tp; | |
220 | { | |
221 | register struct host *h = tp->t_ucb->uc_host; | |
222 | register struct mbuf *m; | |
223 | register struct th *n; | |
224 | register struct ip *ip; | |
225 | ||
226 | if (h == 0) | |
227 | return (0); | |
228 | m = m_get(1); | |
229 | if (m == 0) | |
230 | return (0); | |
231 | m->m_off = MMAXOFF - sizeof (struct th); | |
232 | m->m_len = sizeof (struct th); | |
233 | n = mtod(m, struct th *); | |
234 | n->t_next = n->t_prev = 0; | |
235 | n->t_x1 = 0; | |
236 | n->t_pr = TCPROTO; | |
237 | n->t_len = htons(sizeof (struct th) - sizeof (struct ip)); | |
238 | n->t_s.s_addr = n_lhost.s_addr; | |
239 | n->t_d.s_addr = h->h_addr.s_addr; | |
240 | n->t_src = htons(tp->t_lport); | |
241 | n->t_dst = htons(tp->t_fport); | |
242 | n->t_seq = 0; | |
243 | n->t_ackno = 0; | |
244 | n->t_x2 = 0; | |
245 | n->t_off = 5; | |
246 | n->th_flags = 0; | |
247 | n->t_win = 0; | |
248 | n->t_sum = 0; | |
249 | n->t_urp = 0; | |
250 | return (n); | |
251 | } | |
252 | ||
253 | tcp_output(tp, flags, len, dat) | |
254 | register struct tcb *tp; | |
255 | register int flags; | |
256 | int len; | |
257 | struct mbuf *dat; | |
258 | { | |
259 | register struct mbuf *m; | |
260 | register struct th *t; | |
261 | register struct ip *ip; | |
262 | int i; | |
263 | #ifdef TCPDEBUG | |
264 | struct tcp_debug tdb; | |
265 | #endif | |
266 | COUNT(SEND_TCP); | |
267 | ||
268 | if ((t = tp->t_ucb->uc_template) == 0) | |
269 | return (0); | |
270 | MGET(m, 0); | |
271 | if (m == 0) | |
272 | return (0); | |
273 | m->m_off = MMAXOFF - sizeof(struct th); | |
274 | m->m_len = sizeof (struct th); | |
275 | m->m_next = dat; | |
276 | if (flags & TH_SYN) | |
277 | len--; | |
278 | if (flags & TH_FIN) | |
279 | len--; | |
280 | bcopy((caddr_t)t, mtod(m, caddr_t), sizeof (struct th)); | |
281 | t = mtod(m, struct th *); | |
282 | if (tp->tc_flags&TC_SND_RST) { | |
283 | flags &= ~TH_SYN; | |
284 | flags |= TH_RST; | |
285 | } | |
286 | if (tp->tc_flags&TC_SYN_RCVD) | |
287 | flags |= TH_ACK; | |
288 | t->th_flags = flags; | |
289 | if (flags & TH_URG) | |
290 | t->t_urp = htons(tp->snd_urp); | |
291 | t->t_win = | |
292 | tp->t_ucb->uc_rhiwat - (tp->t_ucb->uc_rcc + tp->seqcnt); | |
293 | if (tp->rcv_nxt + t->t_win > tp->rcv_adv) | |
294 | tp->rcv_adv = tp->rcv_nxt + t->t_win; | |
295 | if (len) | |
296 | t->t_len = htons(len + TCPSIZE); | |
297 | t->t_win = htons(t->t_win); | |
298 | #ifdef TCPDEBUG | |
299 | if ((tp->t_ucb->uc_flags & UDEBUG) || tcpconsdebug) { | |
300 | t->t_seq = tp->snd_nxt; | |
301 | t->t_ackno = tp->rcv_nxt; | |
302 | tdb_setup(tp, t, INSEND, &tdb); | |
303 | tdb_stuff(&tdb, -2); | |
304 | } | |
305 | #endif | |
306 | t->t_seq = htonl(tp->snd_nxt); | |
307 | t->t_ackno = htonl(tp->rcv_nxt); | |
308 | t->t_sum = cksum(m, len + sizeof(struct th)); | |
309 | ip = (struct ip *)t; | |
310 | ip->ip_v = IPVERSION; | |
311 | ip->ip_hl = 5; | |
312 | ip->ip_tos = 0; | |
313 | ip->ip_len = len + sizeof(struct th); | |
314 | ip->ip_id = ip_id++; | |
315 | ip->ip_off = 0; | |
316 | ip->ip_ttl = MAXTTL; | |
317 | i = ip_send(ip); | |
318 | #ifdef notdef | |
319 | if (tp->t_ucb->uc_flags & UDEBUG) { | |
320 | w.w_dat = (char *)t; | |
321 | w.w_stype = i; | |
322 | tcp_debug(tp, &w, INRECV, -1); | |
323 | } | |
324 | #endif | |
325 | return(i); | |
326 | } | |
327 | ||
328 | firstempty(tp) | |
329 | register struct tcb *tp; | |
330 | { | |
331 | register struct th *p, *q; | |
332 | COUNT(FIRSTEMPTY); | |
333 | ||
334 | if ((p = tp->t_rcv_next) == (struct th *)tp || tp->rcv_nxt < p->t_seq) | |
335 | return (tp->rcv_nxt); | |
336 | while ((q = p->t_next) != (struct th *)tp && | |
337 | (t_end(p) + 1) == q->t_seq) | |
338 | p = q; | |
339 | return (t_end(p) + 1); | |
340 | } | |
341 | ||
342 | /* SHOULD BE A MACRO, AFTER KEEP TRACK OF ASS Q SPACE */ | |
343 | ||
344 | struct mbuf * | |
345 | tcp_sndcopy(tp, start, end) | |
346 | struct tcb *tp; | |
347 | u_long start, end; | |
348 | { | |
349 | register struct mbuf *m, *n, **np; | |
350 | u_long off; | |
351 | register int len; | |
352 | int adj; | |
353 | struct mbuf *top, *p; | |
354 | COUNT(SND_COPY); | |
355 | ||
356 | /* | |
357 | printf("st %x end %x off %x\n", start, end, tp->snd_off); | |
358 | */ | |
359 | if (start >= end) | |
360 | return(NULL); | |
361 | off = tp->snd_off; | |
362 | m = tp->t_ucb->uc_sbuf; | |
363 | while (m != NULL && start >= (off + m->m_len)) { | |
364 | off += m->m_len; | |
365 | m = m->m_next; | |
366 | } | |
367 | np = ⊤ | |
368 | top = 0; | |
369 | adj = start - off; | |
370 | len = end - start; | |
371 | while (m && len > 0) { | |
372 | MGET(n, 1); | |
373 | *np = n; | |
374 | if (n == 0) | |
375 | goto nospace; | |
376 | n->m_len = MIN(len, m->m_len - adj); | |
377 | if (m->m_off > MMAXOFF) { | |
378 | p = mtod(m, struct mbuf *); | |
379 | n->m_off = ((int)p - (int)n) + adj; | |
380 | mprefcnt[mtopf(p)]++; | |
381 | } else { | |
382 | n->m_off = MMINOFF; | |
383 | bcopy(mtod(m, caddr_t)+adj, mtod(n, caddr_t), | |
384 | n->m_len); | |
385 | } | |
386 | len -= n->m_len; | |
387 | adj = 0; | |
388 | m = m->m_next; | |
389 | /* SHOULD TRY PACKING INTO SMALL MBUFS HERE */ | |
390 | np = &n->m_next; | |
391 | } | |
392 | /* SHOULD NEVER RUN OUT OF m WHEN LEN */ | |
393 | if (len) | |
394 | printf("snd_copy: m %x len %d\n", m, len); | |
395 | return (top); | |
396 | nospace: | |
397 | printf("snd_copy: no space\n"); | |
398 | m_freem(top); | |
399 | return (0); | |
400 | } | |
401 | ||
402 | tcp_enq(p, prev) | |
403 | register struct th *p; | |
404 | register struct th *prev; | |
405 | { | |
406 | ||
407 | p->t_prev = prev; | |
408 | p->t_next = prev->t_next; | |
409 | prev->t_next->t_prev = p; | |
410 | prev->t_next = p; | |
411 | } | |
412 | ||
413 | tcp_deq(p) | |
414 | register struct th *p; | |
415 | { | |
416 | ||
417 | p->t_prev->t_next = p->t_next; | |
418 | p->t_next->t_prev = p->t_prev; | |
419 | } |