Commit | Line | Data |
---|---|---|
d52566dd | 1 | /* tcp_output.c 4.10 81/11/08 */ |
76ee76df BJ |
2 | |
3 | #include "../h/param.h" | |
4 | #include "../h/systm.h" | |
5 | #include "../h/mbuf.h" | |
6 | #include "../h/socket.h" | |
d52566dd BJ |
7 | #include "../h/socketvar.h" |
8 | #include "../net/inet_cksum.h" | |
9 | #include "../net/inet.h" | |
10 | #include "../net/inet_host.h" | |
11 | #include "../net/inet_systm.h" | |
12 | #include "../net/imp.h" | |
13 | #include "../net/ip.h" | |
14 | #include "../net/tcp.h" | |
15 | #include "../net/tcp_var.h" | |
16 | #include "../net/tcp_fsm.h" | |
17 | #include "/usr/include/errno.h" | |
76ee76df | 18 | |
ea727f86 BJ |
19 | /* |
20 | * Special routines to send control messages. | |
21 | */ | |
22 | tcp_sndctl(tp) | |
23 | struct tcb *tp; | |
24 | { | |
25 | COUNT(TCP_SNDCTL); | |
26 | ||
27 | if (tcp_send(tp)) | |
28 | return (1); | |
29 | tcp_sndnull(tp); | |
d52566dd | 30 | return (0); |
ea727f86 BJ |
31 | } |
32 | ||
33 | tcp_sndwin(tp) | |
34 | struct tcb *tp; | |
35 | { | |
36 | int ihave, hehas; | |
d52566dd | 37 | register struct socket *so = tp->t_socket; |
ea727f86 BJ |
38 | COUNT(TCP_SNDWIN); |
39 | ||
40 | if (tp->rcv_adv) { | |
d52566dd BJ |
41 | ihave = so->so_rcv.sb_hiwat - |
42 | (so->so_rcv.sb_cc + tp->seqcnt); | |
ea727f86 | 43 | hehas = tp->rcv_adv - tp->rcv_nxt; |
d52566dd | 44 | if ((100*(ihave-hehas)/so->so_rcv.sb_hiwat) < 35) |
ea727f86 BJ |
45 | return; |
46 | } | |
47 | if (tcp_send(tp)) | |
48 | return (1); | |
49 | tcp_sndnull(tp); | |
50 | return (0); | |
51 | } | |
52 | ||
53 | tcp_sndnull(tp) | |
54 | register struct tcb *tp; | |
55 | { | |
56 | COUNT(TCP_SNDNULL); | |
57 | ||
58 | tcp_output(tp, 0, 0, (struct mbuf *)0); | |
59 | tp->tc_flags &= ~TC_ACK_DUE; | |
60 | } | |
61 | ||
62 | tcp_sndrst(tp, n) | |
63 | register struct tcb *tp; | |
64 | register struct th *n; | |
65 | { | |
66 | COUNT(TCP_SNDRST); | |
67 | ||
68 | /* don't send a reset in response to a reset */ | |
69 | if (n->th_flags&TH_RST) | |
70 | return; | |
71 | tp->tc_flags |= TC_SND_RST; | |
72 | if (n->th_flags&TH_ACK) | |
73 | tp->snd_nxt = n->t_ackno; | |
74 | tp->tc_flags &= ~TC_SYN_RCVD; | |
75 | tcp_sndnull(tp); | |
76 | tp->tc_flags &= ~TC_SND_RST; | |
77 | } | |
78 | ||
79 | /* | |
80 | * Tcp segment output routine. | |
81 | */ | |
82 | tcp_send(tp) | |
76ee76df BJ |
83 | register struct tcb *tp; |
84 | { | |
d52566dd | 85 | register struct socket *so; |
76ee76df BJ |
86 | register unsigned long last, wind; |
87 | struct mbuf *m; | |
88 | int flags = 0, forced, sent; | |
89 | struct mbuf *tcp_sndcopy(); | |
90 | int len; | |
91 | ||
ea727f86 | 92 | COUNT(TCP_SEND); |
d52566dd | 93 | so = tp->t_socket; |
76ee76df BJ |
94 | tp->snd_lst = tp->snd_nxt; |
95 | forced = 0; | |
96 | m = NULL; | |
ea727f86 | 97 | if (tp->snd_nxt == tp->iss) { |
76ee76df BJ |
98 | flags |= TH_SYN; |
99 | tp->snd_lst++; | |
100 | } | |
76ee76df | 101 | last = tp->snd_off; |
d52566dd | 102 | for (m = so->so_snd.sb_mb; m != NULL; m = m->m_next) |
76ee76df | 103 | last += m->m_len; |
76ee76df | 104 | if (tp->snd_nxt > last) { |
76ee76df BJ |
105 | if ((tp->tc_flags&TC_SND_FIN) && |
106 | (tp->seq_fin == tp->iss || tp->snd_nxt <= tp->seq_fin)) { | |
107 | ||
108 | flags |= TH_FIN; | |
109 | tp->seq_fin = tp->snd_lst++; | |
110 | } | |
ea727f86 | 111 | } else { |
76ee76df | 112 | if (tp->tc_flags&TC_SYN_ACKED) { |
76ee76df | 113 | wind = tp->snd_una + tp->snd_wnd; |
76ee76df | 114 | tp->snd_lst = min(last, wind); |
76ee76df BJ |
115 | if ((len = tp->snd_lst - tp->snd_nxt) > 1024) |
116 | tp->snd_lst -= len - 1024; | |
76ee76df BJ |
117 | if (tp->snd_lst >= wind) |
118 | tp->t_persist = T_PERS; | |
119 | } | |
76ee76df BJ |
120 | if ((tp->tc_flags&TC_FORCE_ONE) && (tp->snd_lst == wind)) { |
121 | tp->snd_lst = tp->snd_nxt + 1; | |
122 | forced = 1; | |
ddfd844e | 123 | } else if (tp->snd_nxt >= tp->snd_lst && (tp->tc_flags&TC_SND_FIN) == 0) |
7be5ba08 BJ |
124 | return (0); |
125 | m = tcp_sndcopy(tp, MAX(tp->iss+1,tp->snd_nxt), tp->snd_lst); | |
76ee76df BJ |
126 | if (tp->snd_end > tp->iss && tp->snd_end <= tp->snd_lst) |
127 | flags |= TH_EOL; | |
76ee76df BJ |
128 | if ((tp->tc_flags&TC_SND_FIN) && !forced && |
129 | tp->snd_lst == last && | |
130 | (tp->seq_fin == tp->iss || tp->snd_nxt <= tp->seq_fin)) { | |
76ee76df BJ |
131 | flags |= TH_FIN; |
132 | tp->seq_fin = tp->snd_lst++; | |
133 | } | |
134 | } | |
ddfd844e BJ |
135 | if (tp->snd_nxt >= tp->snd_lst) |
136 | return (0); | |
ea727f86 BJ |
137 | if (tp->tc_flags & TC_SND_URG) |
138 | flags |= TH_URG; | |
139 | sent = tcp_output(tp, flags, tp->snd_lst - tp->snd_nxt, m); | |
140 | if (!forced) { | |
141 | tp->t_rexmt = tp->t_xmtime; | |
142 | tp->t_rexmt_val = tp->snd_lst; | |
143 | if ((tp->tc_flags&TC_REXMT) == 0) { | |
144 | tp->t_rexmttl = T_REXMTTL; | |
145 | tp->t_rtl_val = tp->snd_lst; | |
76ee76df | 146 | } |
76ee76df | 147 | } |
ea727f86 BJ |
148 | if (sent) |
149 | tp->snd_nxt = tp->snd_lst; | |
150 | if ((tp->tc_flags&TC_SYN_ACKED) && | |
151 | tp->snd_una > tp->t_xmt_val) { | |
152 | tp->t_xmt = 0; | |
153 | tp->t_xmt_val = tp->snd_lst; | |
76ee76df | 154 | } |
ea727f86 | 155 | tp->tc_flags &= ~(TC_ACK_DUE|TC_REXMT|TC_FORCE_ONE); |
7be5ba08 | 156 | tp->snd_hi = MAX(tp->snd_nxt, tp->snd_hi); |
ea727f86 | 157 | return (1); |
76ee76df BJ |
158 | } |
159 | ||
160 | /* | |
161 | * Create template to be used to send tcp packets on a connection. | |
162 | * Call after host entry created, allocates an mbuf and fills | |
163 | * in a skeletal tcp/ip header, minimizing the amount of work | |
164 | * necessary when the connection is used. | |
165 | */ | |
166 | struct th * | |
167 | tcp_template(tp) | |
168 | struct tcb *tp; | |
169 | { | |
d52566dd | 170 | register struct host *h = tp->t_host; |
76ee76df BJ |
171 | register struct mbuf *m; |
172 | register struct th *n; | |
173 | register struct ip *ip; | |
174 | ||
175 | if (h == 0) | |
176 | return (0); | |
177 | m = m_get(1); | |
178 | if (m == 0) | |
179 | return (0); | |
180 | m->m_off = MMAXOFF - sizeof (struct th); | |
181 | m->m_len = sizeof (struct th); | |
182 | n = mtod(m, struct th *); | |
183 | n->t_next = n->t_prev = 0; | |
184 | n->t_x1 = 0; | |
e1506033 | 185 | n->t_pr = IPPROTO_TCP; |
76ee76df BJ |
186 | n->t_len = htons(sizeof (struct th) - sizeof (struct ip)); |
187 | n->t_s.s_addr = n_lhost.s_addr; | |
188 | n->t_d.s_addr = h->h_addr.s_addr; | |
189 | n->t_src = htons(tp->t_lport); | |
190 | n->t_dst = htons(tp->t_fport); | |
191 | n->t_seq = 0; | |
192 | n->t_ackno = 0; | |
193 | n->t_x2 = 0; | |
194 | n->t_off = 5; | |
195 | n->th_flags = 0; | |
196 | n->t_win = 0; | |
197 | n->t_sum = 0; | |
198 | n->t_urp = 0; | |
199 | return (n); | |
200 | } | |
201 | ||
202 | tcp_output(tp, flags, len, dat) | |
203 | register struct tcb *tp; | |
204 | register int flags; | |
205 | int len; | |
206 | struct mbuf *dat; | |
207 | { | |
6d6a2c70 | 208 | register struct th *t; /* known to be r9 */ |
76ee76df | 209 | register struct mbuf *m; |
76ee76df BJ |
210 | register struct ip *ip; |
211 | int i; | |
212 | #ifdef TCPDEBUG | |
213 | struct tcp_debug tdb; | |
214 | #endif | |
3552a746 | 215 | COUNT(TCP_OUTPUT); |
76ee76df | 216 | |
e1506033 | 217 | if ((t = tp->t_template) == 0) |
76ee76df BJ |
218 | return (0); |
219 | MGET(m, 0); | |
220 | if (m == 0) | |
221 | return (0); | |
222 | m->m_off = MMAXOFF - sizeof(struct th); | |
223 | m->m_len = sizeof (struct th); | |
224 | m->m_next = dat; | |
225 | if (flags & TH_SYN) | |
226 | len--; | |
227 | if (flags & TH_FIN) | |
228 | len--; | |
229 | bcopy((caddr_t)t, mtod(m, caddr_t), sizeof (struct th)); | |
230 | t = mtod(m, struct th *); | |
231 | if (tp->tc_flags&TC_SND_RST) { | |
232 | flags &= ~TH_SYN; | |
233 | flags |= TH_RST; | |
234 | } | |
235 | if (tp->tc_flags&TC_SYN_RCVD) | |
236 | flags |= TH_ACK; | |
237 | t->th_flags = flags; | |
238 | if (flags & TH_URG) | |
239 | t->t_urp = htons(tp->snd_urp); | |
240 | t->t_win = | |
d52566dd BJ |
241 | tp->t_socket->so_rcv.sb_hiwat - |
242 | (tp->t_socket->so_rcv.sb_cc + tp->seqcnt); | |
76ee76df BJ |
243 | if (tp->rcv_nxt + t->t_win > tp->rcv_adv) |
244 | tp->rcv_adv = tp->rcv_nxt + t->t_win; | |
245 | if (len) | |
246 | t->t_len = htons(len + TCPSIZE); | |
247 | t->t_win = htons(t->t_win); | |
248 | #ifdef TCPDEBUG | |
d52566dd | 249 | if ((tp->t_socket->so_options & SO_DEBUG) || tcpconsdebug) { |
76ee76df BJ |
250 | t->t_seq = tp->snd_nxt; |
251 | t->t_ackno = tp->rcv_nxt; | |
252 | tdb_setup(tp, t, INSEND, &tdb); | |
253 | tdb_stuff(&tdb, -2); | |
254 | } | |
255 | #endif | |
256 | t->t_seq = htonl(tp->snd_nxt); | |
257 | t->t_ackno = htonl(tp->rcv_nxt); | |
6d6a2c70 BJ |
258 | t->t_sum = 0; /* gratuitous? */ |
259 | CKSUM_TCPSET(m, t, r9, sizeof (struct th) + len); | |
76ee76df BJ |
260 | ip = (struct ip *)t; |
261 | ip->ip_v = IPVERSION; | |
262 | ip->ip_hl = 5; | |
263 | ip->ip_tos = 0; | |
264 | ip->ip_len = len + sizeof(struct th); | |
265 | ip->ip_id = ip_id++; | |
266 | ip->ip_off = 0; | |
267 | ip->ip_ttl = MAXTTL; | |
268 | i = ip_send(ip); | |
d52566dd | 269 | return (i); |
76ee76df BJ |
270 | } |
271 | ||
272 | firstempty(tp) | |
273 | register struct tcb *tp; | |
274 | { | |
275 | register struct th *p, *q; | |
276 | COUNT(FIRSTEMPTY); | |
277 | ||
d52566dd BJ |
278 | if ((p = tp->tcb_hd.seg_next) == (struct th *)tp || |
279 | tp->rcv_nxt < p->t_seq) | |
76ee76df BJ |
280 | return (tp->rcv_nxt); |
281 | while ((q = p->t_next) != (struct th *)tp && | |
282 | (t_end(p) + 1) == q->t_seq) | |
283 | p = q; | |
284 | return (t_end(p) + 1); | |
285 | } | |
286 | ||
76ee76df BJ |
287 | struct mbuf * |
288 | tcp_sndcopy(tp, start, end) | |
289 | struct tcb *tp; | |
290 | u_long start, end; | |
291 | { | |
292 | register struct mbuf *m, *n, **np; | |
293 | u_long off; | |
294 | register int len; | |
295 | int adj; | |
296 | struct mbuf *top, *p; | |
3552a746 | 297 | COUNT(TCP_SNDCOPY); |
76ee76df | 298 | |
76ee76df | 299 | if (start >= end) |
d52566dd | 300 | return (NULL); |
76ee76df | 301 | off = tp->snd_off; |
d52566dd | 302 | m = tp->t_socket->so_snd.sb_mb; |
76ee76df BJ |
303 | while (m != NULL && start >= (off + m->m_len)) { |
304 | off += m->m_len; | |
305 | m = m->m_next; | |
306 | } | |
307 | np = ⊤ | |
308 | top = 0; | |
309 | adj = start - off; | |
310 | len = end - start; | |
311 | while (m && len > 0) { | |
312 | MGET(n, 1); | |
313 | *np = n; | |
314 | if (n == 0) | |
315 | goto nospace; | |
316 | n->m_len = MIN(len, m->m_len - adj); | |
317 | if (m->m_off > MMAXOFF) { | |
318 | p = mtod(m, struct mbuf *); | |
319 | n->m_off = ((int)p - (int)n) + adj; | |
320 | mprefcnt[mtopf(p)]++; | |
321 | } else { | |
322 | n->m_off = MMINOFF; | |
323 | bcopy(mtod(m, caddr_t)+adj, mtod(n, caddr_t), | |
324 | n->m_len); | |
325 | } | |
326 | len -= n->m_len; | |
327 | adj = 0; | |
328 | m = m->m_next; | |
329 | /* SHOULD TRY PACKING INTO SMALL MBUFS HERE */ | |
330 | np = &n->m_next; | |
331 | } | |
332 | /* SHOULD NEVER RUN OUT OF m WHEN LEN */ | |
333 | if (len) | |
334 | printf("snd_copy: m %x len %d\n", m, len); | |
335 | return (top); | |
336 | nospace: | |
337 | printf("snd_copy: no space\n"); | |
338 | m_freem(top); | |
339 | return (0); | |
340 | } | |
d52566dd BJ |
341 | |
342 | tcp_fasttimo() | |
343 | { | |
344 | ||
345 | } |