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