]>
Commit | Line | Data |
---|---|---|
a3bf193b YCLP |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Copyright 2017 Duncan Hare, all rights reserved. | |
4 | */ | |
5 | ||
6 | /* | |
7 | * General Desription: | |
8 | * | |
9 | * TCP support for the wget command, for fast file downloading. | |
10 | * | |
11 | * HTTP/TCP Receiver: | |
12 | * | |
13 | * Prerequisites: - own ethernet address | |
14 | * - own IP address | |
15 | * - Server IP address | |
16 | * - Server with TCP | |
17 | * - TCP application (eg wget) | |
18 | * Next Step HTTPS? | |
19 | */ | |
a3bf193b YCLP |
20 | #include <command.h> |
21 | #include <console.h> | |
22 | #include <env_internal.h> | |
23 | #include <errno.h> | |
24 | #include <net.h> | |
25 | #include <net/tcp.h> | |
26 | ||
27 | /* | |
28 | * TCP sliding window control used by us to request re-TX | |
29 | */ | |
30 | static struct tcp_sack_v tcp_lost; | |
31 | ||
32 | /* TCP option timestamp */ | |
33 | static u32 loc_timestamp; | |
34 | static u32 rmt_timestamp; | |
35 | ||
36 | static u32 tcp_seq_init; | |
37 | static u32 tcp_ack_edge; | |
a3bf193b YCLP |
38 | |
39 | static int tcp_activity_count; | |
40 | ||
41 | /* | |
42 | * Search for TCP_SACK and review the comments before the code section | |
43 | * TCP_SACK is the number of packets at the front of the stream | |
44 | */ | |
45 | ||
46 | enum pkt_state {PKT, NOPKT}; | |
47 | struct sack_r { | |
48 | struct sack_edges se; | |
49 | enum pkt_state st; | |
50 | }; | |
51 | ||
52 | static struct sack_r edge_a[TCP_SACK]; | |
53 | static unsigned int sack_idx; | |
54 | static unsigned int prev_len; | |
55 | ||
56 | /* | |
57 | * TCP lengths are stored as a rounded up number of 32 bit words. | |
58 | * Add 3 to length round up, rounded, then divided into the | |
59 | * length in 32 bit words. | |
60 | */ | |
61 | #define LEN_B_TO_DW(x) ((x) >> 2) | |
62 | #define ROUND_TCPHDR_LEN(x) (LEN_B_TO_DW((x) + 3)) | |
63 | #define SHIFT_TO_TCPHDRLEN_FIELD(x) ((x) << 4) | |
64 | #define GET_TCP_HDR_LEN_IN_BYTES(x) ((x) >> 2) | |
65 | ||
66 | /* TCP connection state */ | |
67 | static enum tcp_state current_tcp_state; | |
68 | ||
69 | /* Current TCP RX packet handler */ | |
70 | static rxhand_tcp *tcp_packet_handler; | |
71 | ||
72 | /** | |
73 | * tcp_get_tcp_state() - get current TCP state | |
74 | * | |
75 | * Return: Current TCP state | |
76 | */ | |
77 | enum tcp_state tcp_get_tcp_state(void) | |
78 | { | |
79 | return current_tcp_state; | |
80 | } | |
81 | ||
82 | /** | |
83 | * tcp_set_tcp_state() - set current TCP state | |
84 | * @new_state: new TCP state | |
85 | */ | |
86 | void tcp_set_tcp_state(enum tcp_state new_state) | |
87 | { | |
88 | current_tcp_state = new_state; | |
89 | } | |
90 | ||
08fb8da3 DM |
91 | static void dummy_handler(uchar *pkt, u16 dport, |
92 | struct in_addr sip, u16 sport, | |
93 | u32 tcp_seq_num, u32 tcp_ack_num, | |
94 | u8 action, unsigned int len) | |
a3bf193b YCLP |
95 | { |
96 | } | |
97 | ||
98 | /** | |
99 | * tcp_set_tcp_handler() - set a handler to receive data | |
100 | * @f: handler | |
101 | */ | |
102 | void tcp_set_tcp_handler(rxhand_tcp *f) | |
103 | { | |
104 | debug_cond(DEBUG_INT_STATE, "--- net_loop TCP handler set (%p)\n", f); | |
105 | if (!f) | |
106 | tcp_packet_handler = dummy_handler; | |
107 | else | |
108 | tcp_packet_handler = f; | |
109 | } | |
110 | ||
111 | /** | |
112 | * tcp_set_pseudo_header() - set TCP pseudo header | |
113 | * @pkt: the packet | |
114 | * @src: source IP address | |
115 | * @dest: destinaion IP address | |
116 | * @tcp_len: tcp length | |
117 | * @pkt_len: packet length | |
118 | * | |
119 | * Return: the checksum of the packet | |
120 | */ | |
121 | u16 tcp_set_pseudo_header(uchar *pkt, struct in_addr src, struct in_addr dest, | |
122 | int tcp_len, int pkt_len) | |
123 | { | |
124 | union tcp_build_pkt *b = (union tcp_build_pkt *)pkt; | |
125 | int checksum_len; | |
126 | ||
127 | /* | |
128 | * Pseudo header | |
129 | * | |
130 | * Zero the byte after the last byte so that the header checksum | |
131 | * will always work. | |
132 | */ | |
133 | pkt[pkt_len] = 0; | |
134 | ||
135 | net_copy_ip((void *)&b->ph.p_src, &src); | |
136 | net_copy_ip((void *)&b->ph.p_dst, &dest); | |
137 | b->ph.rsvd = 0; | |
138 | b->ph.p = IPPROTO_TCP; | |
139 | b->ph.len = htons(tcp_len); | |
140 | checksum_len = tcp_len + PSEUDO_HDR_SIZE; | |
141 | ||
142 | debug_cond(DEBUG_DEV_PKT, | |
143 | "TCP Pesudo Header (to=%pI4, from=%pI4, Len=%d)\n", | |
144 | &b->ph.p_dst, &b->ph.p_src, checksum_len); | |
145 | ||
146 | return compute_ip_checksum(pkt + PSEUDO_PAD_SIZE, checksum_len); | |
147 | } | |
148 | ||
149 | /** | |
150 | * net_set_ack_options() - set TCP options in acknowledge packets | |
151 | * @b: the packet | |
152 | * | |
153 | * Return: TCP header length | |
154 | */ | |
155 | int net_set_ack_options(union tcp_build_pkt *b) | |
156 | { | |
157 | b->sack.hdr.tcp_hlen = SHIFT_TO_TCPHDRLEN_FIELD(LEN_B_TO_DW(TCP_HDR_SIZE)); | |
158 | ||
159 | b->sack.t_opt.kind = TCP_O_TS; | |
160 | b->sack.t_opt.len = TCP_OPT_LEN_A; | |
161 | b->sack.t_opt.t_snd = htons(loc_timestamp); | |
162 | b->sack.t_opt.t_rcv = rmt_timestamp; | |
163 | b->sack.sack_v.kind = TCP_1_NOP; | |
164 | b->sack.sack_v.len = 0; | |
165 | ||
166 | if (IS_ENABLED(CONFIG_PROT_TCP_SACK)) { | |
167 | if (tcp_lost.len > TCP_OPT_LEN_2) { | |
168 | debug_cond(DEBUG_DEV_PKT, "TCP ack opt lost.len %x\n", | |
169 | tcp_lost.len); | |
170 | b->sack.sack_v.len = tcp_lost.len; | |
171 | b->sack.sack_v.kind = TCP_V_SACK; | |
172 | b->sack.sack_v.hill[0].l = htonl(tcp_lost.hill[0].l); | |
173 | b->sack.sack_v.hill[0].r = htonl(tcp_lost.hill[0].r); | |
174 | ||
175 | /* | |
176 | * These SACK structures are initialized with NOPs to | |
177 | * provide TCP header alignment padding. There are 4 | |
178 | * SACK structures used for both header padding and | |
179 | * internally. | |
180 | */ | |
181 | b->sack.sack_v.hill[1].l = htonl(tcp_lost.hill[1].l); | |
182 | b->sack.sack_v.hill[1].r = htonl(tcp_lost.hill[1].r); | |
183 | b->sack.sack_v.hill[2].l = htonl(tcp_lost.hill[2].l); | |
184 | b->sack.sack_v.hill[2].r = htonl(tcp_lost.hill[2].r); | |
185 | b->sack.sack_v.hill[3].l = TCP_O_NOP; | |
186 | b->sack.sack_v.hill[3].r = TCP_O_NOP; | |
187 | } | |
188 | ||
189 | b->sack.hdr.tcp_hlen = SHIFT_TO_TCPHDRLEN_FIELD(ROUND_TCPHDR_LEN(TCP_HDR_SIZE + | |
190 | TCP_TSOPT_SIZE + | |
191 | tcp_lost.len)); | |
192 | } else { | |
193 | b->sack.sack_v.kind = 0; | |
194 | b->sack.hdr.tcp_hlen = SHIFT_TO_TCPHDRLEN_FIELD(ROUND_TCPHDR_LEN(TCP_HDR_SIZE + | |
195 | TCP_TSOPT_SIZE)); | |
196 | } | |
197 | ||
198 | /* | |
199 | * This returns the actual rounded up length of the | |
200 | * TCP header to add to the total packet length | |
201 | */ | |
202 | ||
203 | return GET_TCP_HDR_LEN_IN_BYTES(b->sack.hdr.tcp_hlen); | |
204 | } | |
205 | ||
206 | /** | |
207 | * net_set_ack_options() - set TCP options in SYN packets | |
208 | * @b: the packet | |
209 | */ | |
210 | void net_set_syn_options(union tcp_build_pkt *b) | |
211 | { | |
212 | if (IS_ENABLED(CONFIG_PROT_TCP_SACK)) | |
213 | tcp_lost.len = 0; | |
214 | ||
215 | b->ip.hdr.tcp_hlen = 0xa0; | |
216 | ||
217 | b->ip.mss.kind = TCP_O_MSS; | |
218 | b->ip.mss.len = TCP_OPT_LEN_4; | |
219 | b->ip.mss.mss = htons(TCP_MSS); | |
220 | b->ip.scale.kind = TCP_O_SCL; | |
221 | b->ip.scale.scale = TCP_SCALE; | |
222 | b->ip.scale.len = TCP_OPT_LEN_3; | |
223 | if (IS_ENABLED(CONFIG_PROT_TCP_SACK)) { | |
224 | b->ip.sack_p.kind = TCP_P_SACK; | |
225 | b->ip.sack_p.len = TCP_OPT_LEN_2; | |
226 | } else { | |
227 | b->ip.sack_p.kind = TCP_1_NOP; | |
228 | b->ip.sack_p.len = TCP_1_NOP; | |
229 | } | |
230 | b->ip.t_opt.kind = TCP_O_TS; | |
231 | b->ip.t_opt.len = TCP_OPT_LEN_A; | |
232 | loc_timestamp = get_ticks(); | |
233 | rmt_timestamp = 0; | |
234 | b->ip.t_opt.t_snd = 0; | |
235 | b->ip.t_opt.t_rcv = 0; | |
236 | b->ip.end = TCP_O_END; | |
237 | } | |
238 | ||
239 | int tcp_set_tcp_header(uchar *pkt, int dport, int sport, int payload_len, | |
240 | u8 action, u32 tcp_seq_num, u32 tcp_ack_num) | |
241 | { | |
242 | union tcp_build_pkt *b = (union tcp_build_pkt *)pkt; | |
243 | int pkt_hdr_len; | |
244 | int pkt_len; | |
245 | int tcp_len; | |
246 | ||
247 | /* | |
248 | * Header: 5 32 bit words. 4 bits TCP header Length, | |
249 | * 4 bits reserved options | |
250 | */ | |
251 | b->ip.hdr.tcp_flags = action; | |
252 | pkt_hdr_len = IP_TCP_HDR_SIZE; | |
253 | b->ip.hdr.tcp_hlen = SHIFT_TO_TCPHDRLEN_FIELD(LEN_B_TO_DW(TCP_HDR_SIZE)); | |
254 | ||
255 | switch (action) { | |
256 | case TCP_SYN: | |
257 | debug_cond(DEBUG_DEV_PKT, | |
08fb8da3 | 258 | "TCP Hdr:SYN (%pI4, %pI4, sq=%u, ak=%u)\n", |
a3bf193b YCLP |
259 | &net_server_ip, &net_ip, |
260 | tcp_seq_num, tcp_ack_num); | |
261 | tcp_activity_count = 0; | |
262 | net_set_syn_options(b); | |
263 | tcp_seq_num = 0; | |
264 | tcp_ack_num = 0; | |
265 | pkt_hdr_len = IP_TCP_O_SIZE; | |
266 | if (current_tcp_state == TCP_SYN_SENT) { /* Too many SYNs */ | |
267 | action = TCP_FIN; | |
268 | current_tcp_state = TCP_FIN_WAIT_1; | |
269 | } else { | |
270 | current_tcp_state = TCP_SYN_SENT; | |
271 | } | |
272 | break; | |
08fb8da3 | 273 | case TCP_SYN | TCP_ACK: |
a3bf193b YCLP |
274 | case TCP_ACK: |
275 | pkt_hdr_len = IP_HDR_SIZE + net_set_ack_options(b); | |
276 | b->ip.hdr.tcp_flags = action; | |
277 | debug_cond(DEBUG_DEV_PKT, | |
08fb8da3 | 278 | "TCP Hdr:ACK (%pI4, %pI4, s=%u, a=%u, A=%x)\n", |
a3bf193b YCLP |
279 | &net_server_ip, &net_ip, tcp_seq_num, tcp_ack_num, |
280 | action); | |
281 | break; | |
282 | case TCP_FIN: | |
283 | debug_cond(DEBUG_DEV_PKT, | |
08fb8da3 | 284 | "TCP Hdr:FIN (%pI4, %pI4, s=%u, a=%u)\n", |
a3bf193b YCLP |
285 | &net_server_ip, &net_ip, tcp_seq_num, tcp_ack_num); |
286 | payload_len = 0; | |
287 | pkt_hdr_len = IP_TCP_HDR_SIZE; | |
288 | current_tcp_state = TCP_FIN_WAIT_1; | |
289 | break; | |
08fb8da3 DM |
290 | case TCP_RST | TCP_ACK: |
291 | case TCP_RST: | |
292 | debug_cond(DEBUG_DEV_PKT, | |
293 | "TCP Hdr:RST (%pI4, %pI4, s=%u, a=%u)\n", | |
294 | &net_server_ip, &net_ip, tcp_seq_num, tcp_ack_num); | |
295 | current_tcp_state = TCP_CLOSED; | |
296 | break; | |
a3bf193b | 297 | /* Notify connection closing */ |
a3bf193b YCLP |
298 | case (TCP_FIN | TCP_ACK): |
299 | case (TCP_FIN | TCP_ACK | TCP_PUSH): | |
300 | if (current_tcp_state == TCP_CLOSE_WAIT) | |
301 | current_tcp_state = TCP_CLOSING; | |
302 | ||
a3bf193b | 303 | debug_cond(DEBUG_DEV_PKT, |
08fb8da3 | 304 | "TCP Hdr:FIN ACK PSH(%pI4, %pI4, s=%u, a=%u, A=%x)\n", |
a3bf193b | 305 | &net_server_ip, &net_ip, |
08fb8da3 | 306 | tcp_seq_num, tcp_ack_num, action); |
a3bf193b YCLP |
307 | fallthrough; |
308 | default: | |
309 | pkt_hdr_len = IP_HDR_SIZE + net_set_ack_options(b); | |
310 | b->ip.hdr.tcp_flags = action | TCP_PUSH | TCP_ACK; | |
311 | debug_cond(DEBUG_DEV_PKT, | |
08fb8da3 | 312 | "TCP Hdr:dft (%pI4, %pI4, s=%u, a=%u, A=%x)\n", |
a3bf193b YCLP |
313 | &net_server_ip, &net_ip, |
314 | tcp_seq_num, tcp_ack_num, action); | |
315 | } | |
316 | ||
317 | pkt_len = pkt_hdr_len + payload_len; | |
318 | tcp_len = pkt_len - IP_HDR_SIZE; | |
319 | ||
08fb8da3 | 320 | tcp_ack_edge = tcp_ack_num; |
a3bf193b YCLP |
321 | /* TCP Header */ |
322 | b->ip.hdr.tcp_ack = htonl(tcp_ack_edge); | |
323 | b->ip.hdr.tcp_src = htons(sport); | |
324 | b->ip.hdr.tcp_dst = htons(dport); | |
325 | b->ip.hdr.tcp_seq = htonl(tcp_seq_num); | |
a3bf193b YCLP |
326 | |
327 | /* | |
328 | * TCP window size - TCP header variable tcp_win. | |
329 | * Change tcp_win only if you have an understanding of network | |
330 | * overrun, congestion, TCP segment sizes, TCP windows, TCP scale, | |
331 | * queuing theory and packet buffering. If there are too few buffers, | |
332 | * there will be data loss, recovery may work or the sending TCP, | |
333 | * the server, could abort the stream transmission. | |
334 | * MSS is governed by maximum Ethernet frame length. | |
335 | * The number of buffers is governed by the desire to have a queue of | |
336 | * full buffers to be processed at the destination to maximize | |
337 | * throughput. Temporary memory use for the boot phase on modern | |
338 | * SOCs is may not be considered a constraint to buffer space, if | |
339 | * it is, then the u-boot tftp or nfs kernel netboot should be | |
340 | * considered. | |
341 | */ | |
342 | b->ip.hdr.tcp_win = htons(PKTBUFSRX * TCP_MSS >> TCP_SCALE); | |
343 | ||
344 | b->ip.hdr.tcp_xsum = 0; | |
345 | b->ip.hdr.tcp_ugr = 0; | |
346 | ||
347 | b->ip.hdr.tcp_xsum = tcp_set_pseudo_header(pkt, net_ip, net_server_ip, | |
348 | tcp_len, pkt_len); | |
349 | ||
350 | net_set_ip_header((uchar *)&b->ip, net_server_ip, net_ip, | |
351 | pkt_len, IPPROTO_TCP); | |
352 | ||
353 | return pkt_hdr_len; | |
354 | } | |
355 | ||
356 | /** | |
357 | * tcp_hole() - Selective Acknowledgment (Essential for fast stream transfer) | |
358 | * @tcp_seq_num: TCP sequence start number | |
359 | * @len: the length of sequence numbers | |
a3bf193b | 360 | */ |
08fb8da3 | 361 | void tcp_hole(u32 tcp_seq_num, u32 len) |
a3bf193b YCLP |
362 | { |
363 | u32 idx_sack, sack_in; | |
364 | u32 sack_end = TCP_SACK - 1; | |
365 | u32 hill = 0; | |
366 | enum pkt_state expect = PKT; | |
367 | u32 seq = tcp_seq_num - tcp_seq_init; | |
368 | u32 hol_l = tcp_ack_edge - tcp_seq_init; | |
369 | u32 hol_r = 0; | |
370 | ||
371 | /* Place new seq number in correct place in receive array */ | |
372 | if (prev_len == 0) | |
373 | prev_len = len; | |
374 | ||
375 | idx_sack = sack_idx + ((tcp_seq_num - tcp_ack_edge) / prev_len); | |
376 | if (idx_sack < TCP_SACK) { | |
377 | edge_a[idx_sack].se.l = tcp_seq_num; | |
378 | edge_a[idx_sack].se.r = tcp_seq_num + len; | |
379 | edge_a[idx_sack].st = PKT; | |
380 | ||
381 | /* | |
382 | * The fin (last) packet is not the same length as data | |
383 | * packets, and if it's length is recorded and used for | |
384 | * array index calculation, calculation breaks. | |
385 | */ | |
386 | if (prev_len < len) | |
387 | prev_len = len; | |
388 | } | |
389 | ||
390 | debug_cond(DEBUG_DEV_PKT, | |
391 | "TCP 1 seq %d, edg %d, len %d, sack_idx %d, sack_end %d\n", | |
392 | seq, hol_l, len, sack_idx, sack_end); | |
393 | ||
394 | /* Right edge of contiguous stream, is the left edge of first hill */ | |
395 | hol_l = tcp_seq_num - tcp_seq_init; | |
396 | hol_r = hol_l + len; | |
397 | ||
398 | if (IS_ENABLED(CONFIG_PROT_TCP_SACK)) | |
399 | tcp_lost.len = TCP_OPT_LEN_2; | |
400 | ||
401 | debug_cond(DEBUG_DEV_PKT, | |
402 | "TCP 1 in %d, seq %d, pkt_l %d, pkt_r %d, sack_idx %d, sack_end %d\n", | |
403 | idx_sack, seq, hol_l, hol_r, sack_idx, sack_end); | |
404 | ||
405 | for (sack_in = sack_idx; sack_in < sack_end && hill < TCP_SACK_HILLS; | |
406 | sack_in++) { | |
407 | switch (expect) { | |
408 | case NOPKT: | |
409 | switch (edge_a[sack_in].st) { | |
410 | case NOPKT: | |
411 | debug_cond(DEBUG_INT_STATE, "N"); | |
412 | break; | |
413 | case PKT: | |
414 | debug_cond(DEBUG_INT_STATE, "n"); | |
415 | if (IS_ENABLED(CONFIG_PROT_TCP_SACK)) { | |
416 | tcp_lost.hill[hill].l = | |
417 | edge_a[sack_in].se.l; | |
418 | tcp_lost.hill[hill].r = | |
419 | edge_a[sack_in].se.r; | |
420 | } | |
421 | expect = PKT; | |
422 | break; | |
423 | } | |
424 | break; | |
425 | case PKT: | |
426 | switch (edge_a[sack_in].st) { | |
427 | case NOPKT: | |
428 | debug_cond(DEBUG_INT_STATE, "p"); | |
429 | if (sack_in > sack_idx && | |
430 | hill < TCP_SACK_HILLS) { | |
431 | hill++; | |
432 | if (IS_ENABLED(CONFIG_PROT_TCP_SACK)) | |
433 | tcp_lost.len += TCP_OPT_LEN_8; | |
434 | } | |
435 | expect = NOPKT; | |
436 | break; | |
437 | case PKT: | |
438 | debug_cond(DEBUG_INT_STATE, "P"); | |
439 | ||
440 | if (tcp_ack_edge == edge_a[sack_in].se.l) { | |
441 | tcp_ack_edge = edge_a[sack_in].se.r; | |
442 | edge_a[sack_in].st = NOPKT; | |
443 | sack_idx++; | |
444 | } else { | |
445 | if (IS_ENABLED(CONFIG_PROT_TCP_SACK) && | |
446 | hill < TCP_SACK_HILLS) | |
447 | tcp_lost.hill[hill].r = | |
448 | edge_a[sack_in].se.r; | |
449 | if (IS_ENABLED(CONFIG_PROT_TCP_SACK) && | |
450 | sack_in == sack_end - 1) | |
451 | tcp_lost.hill[hill].r = | |
452 | edge_a[sack_in].se.r; | |
453 | } | |
454 | break; | |
455 | } | |
456 | break; | |
457 | } | |
458 | } | |
459 | debug_cond(DEBUG_INT_STATE, "\n"); | |
460 | if (!IS_ENABLED(CONFIG_PROT_TCP_SACK) || tcp_lost.len <= TCP_OPT_LEN_2) | |
461 | sack_idx = 0; | |
462 | } | |
463 | ||
464 | /** | |
465 | * tcp_parse_options() - parsing TCP options | |
466 | * @o: pointer to the option field. | |
467 | * @o_len: length of the option field. | |
468 | */ | |
469 | void tcp_parse_options(uchar *o, int o_len) | |
470 | { | |
471 | struct tcp_t_opt *tsopt; | |
472 | uchar *p = o; | |
473 | ||
474 | /* | |
475 | * NOPs are options with a zero length, and thus are special. | |
476 | * All other options have length fields. | |
477 | */ | |
478 | for (p = o; p < (o + o_len); p = p + p[1]) { | |
479 | if (!p[1]) | |
480 | return; /* Finished processing options */ | |
481 | ||
482 | switch (p[0]) { | |
483 | case TCP_O_END: | |
484 | return; | |
485 | case TCP_O_MSS: | |
486 | case TCP_O_SCL: | |
487 | case TCP_P_SACK: | |
488 | case TCP_V_SACK: | |
489 | break; | |
490 | case TCP_O_TS: | |
491 | tsopt = (struct tcp_t_opt *)p; | |
492 | rmt_timestamp = tsopt->t_snd; | |
493 | return; | |
494 | } | |
495 | ||
496 | /* Process optional NOPs */ | |
497 | if (p[0] == TCP_O_NOP) | |
498 | p++; | |
499 | } | |
500 | } | |
501 | ||
08fb8da3 | 502 | static u8 tcp_state_machine(u8 tcp_flags, u32 tcp_seq_num, int payload_len) |
a3bf193b YCLP |
503 | { |
504 | u8 tcp_fin = tcp_flags & TCP_FIN; | |
505 | u8 tcp_syn = tcp_flags & TCP_SYN; | |
506 | u8 tcp_rst = tcp_flags & TCP_RST; | |
507 | u8 tcp_push = tcp_flags & TCP_PUSH; | |
508 | u8 tcp_ack = tcp_flags & TCP_ACK; | |
509 | u8 action = TCP_DATA; | |
510 | int i; | |
511 | ||
512 | /* | |
513 | * tcp_flags are examined to determine TX action in a given state | |
514 | * tcp_push is interpreted to mean "inform the app" | |
515 | * urg, ece, cer and nonce flags are not supported. | |
516 | * | |
517 | * exe and crw are use to signal and confirm knowledge of congestion. | |
518 | * This TCP only sends a file request and acks. If it generates | |
519 | * congestion, the network is broken. | |
520 | */ | |
521 | debug_cond(DEBUG_INT_STATE, "TCP STATE ENTRY %x\n", action); | |
522 | if (tcp_rst) { | |
523 | action = TCP_DATA; | |
524 | current_tcp_state = TCP_CLOSED; | |
525 | net_set_state(NETLOOP_FAIL); | |
526 | debug_cond(DEBUG_INT_STATE, "TCP Reset %x\n", tcp_flags); | |
527 | return TCP_RST; | |
528 | } | |
529 | ||
530 | switch (current_tcp_state) { | |
531 | case TCP_CLOSED: | |
532 | debug_cond(DEBUG_INT_STATE, "TCP CLOSED %x\n", tcp_flags); | |
08fb8da3 DM |
533 | if (tcp_syn) { |
534 | action = TCP_SYN | TCP_ACK; | |
535 | tcp_seq_init = tcp_seq_num; | |
536 | tcp_ack_edge = tcp_seq_num + 1; | |
537 | current_tcp_state = TCP_SYN_RECEIVED; | |
538 | } else if (tcp_ack || tcp_fin) { | |
a3bf193b | 539 | action = TCP_DATA; |
08fb8da3 | 540 | } |
a3bf193b | 541 | break; |
08fb8da3 | 542 | case TCP_SYN_RECEIVED: |
a3bf193b | 543 | case TCP_SYN_SENT: |
08fb8da3 DM |
544 | debug_cond(DEBUG_INT_STATE, "TCP_SYN_SENT | TCP_SYN_RECEIVED %x, %u\n", |
545 | tcp_flags, tcp_seq_num); | |
a3bf193b YCLP |
546 | if (tcp_fin) { |
547 | action = action | TCP_PUSH; | |
548 | current_tcp_state = TCP_CLOSE_WAIT; | |
08fb8da3 DM |
549 | } else if (tcp_ack || (tcp_syn && tcp_ack)) { |
550 | action |= TCP_ACK; | |
551 | tcp_seq_init = tcp_seq_num; | |
552 | tcp_ack_edge = tcp_seq_num + 1; | |
553 | sack_idx = 0; | |
554 | edge_a[sack_idx].se.l = tcp_ack_edge; | |
555 | edge_a[sack_idx].se.r = tcp_ack_edge; | |
556 | prev_len = 0; | |
557 | current_tcp_state = TCP_ESTABLISHED; | |
558 | for (i = 0; i < TCP_SACK; i++) | |
559 | edge_a[i].st = NOPKT; | |
560 | ||
561 | if (tcp_syn && tcp_ack) | |
562 | action |= TCP_PUSH; | |
563 | } else { | |
a3bf193b YCLP |
564 | action = TCP_DATA; |
565 | } | |
a3bf193b YCLP |
566 | break; |
567 | case TCP_ESTABLISHED: | |
568 | debug_cond(DEBUG_INT_STATE, "TCP_ESTABLISHED %x\n", tcp_flags); | |
a3bf193b | 569 | if (payload_len > 0) { |
08fb8da3 | 570 | tcp_hole(tcp_seq_num, payload_len); |
a3bf193b YCLP |
571 | tcp_fin = TCP_DATA; /* cause standalone FIN */ |
572 | } | |
573 | ||
574 | if ((tcp_fin) && | |
575 | (!IS_ENABLED(CONFIG_PROT_TCP_SACK) || | |
576 | tcp_lost.len <= TCP_OPT_LEN_2)) { | |
577 | action = action | TCP_FIN | TCP_PUSH | TCP_ACK; | |
578 | current_tcp_state = TCP_CLOSE_WAIT; | |
579 | } else if (tcp_ack) { | |
580 | action = TCP_DATA; | |
581 | } | |
582 | ||
583 | if (tcp_syn) | |
584 | action = TCP_ACK + TCP_RST; | |
585 | else if (tcp_push) | |
586 | action = action | TCP_PUSH; | |
587 | break; | |
588 | case TCP_CLOSE_WAIT: | |
589 | debug_cond(DEBUG_INT_STATE, "TCP_CLOSE_WAIT (%x)\n", tcp_flags); | |
590 | action = TCP_DATA; | |
591 | break; | |
592 | case TCP_FIN_WAIT_2: | |
593 | debug_cond(DEBUG_INT_STATE, "TCP_FIN_WAIT_2 (%x)\n", tcp_flags); | |
594 | if (tcp_ack) { | |
595 | action = TCP_PUSH | TCP_ACK; | |
596 | current_tcp_state = TCP_CLOSED; | |
597 | puts("\n"); | |
598 | } else if (tcp_syn) { | |
599 | action = TCP_DATA; | |
600 | } else if (tcp_fin) { | |
601 | action = TCP_DATA; | |
602 | } | |
603 | break; | |
604 | case TCP_FIN_WAIT_1: | |
605 | debug_cond(DEBUG_INT_STATE, "TCP_FIN_WAIT_1 (%x)\n", tcp_flags); | |
606 | if (tcp_fin) { | |
08fb8da3 | 607 | tcp_ack_edge++; |
a3bf193b YCLP |
608 | action = TCP_ACK | TCP_FIN; |
609 | current_tcp_state = TCP_FIN_WAIT_2; | |
610 | } | |
611 | if (tcp_syn) | |
612 | action = TCP_RST; | |
08fb8da3 | 613 | if (tcp_ack) |
a3bf193b | 614 | current_tcp_state = TCP_CLOSED; |
a3bf193b YCLP |
615 | break; |
616 | case TCP_CLOSING: | |
617 | debug_cond(DEBUG_INT_STATE, "TCP_CLOSING (%x)\n", tcp_flags); | |
618 | if (tcp_ack) { | |
619 | action = TCP_PUSH; | |
620 | current_tcp_state = TCP_CLOSED; | |
621 | puts("\n"); | |
622 | } else if (tcp_syn) { | |
623 | action = TCP_RST; | |
624 | } else if (tcp_fin) { | |
625 | action = TCP_DATA; | |
626 | } | |
627 | break; | |
628 | } | |
629 | return action; | |
630 | } | |
631 | ||
632 | /** | |
633 | * rxhand_tcp_f() - process receiving data and call data handler. | |
634 | * @b: the packet | |
635 | * @pkt_len: the length of packet. | |
636 | */ | |
637 | void rxhand_tcp_f(union tcp_build_pkt *b, unsigned int pkt_len) | |
638 | { | |
639 | int tcp_len = pkt_len - IP_HDR_SIZE; | |
640 | u16 tcp_rx_xsum = b->ip.hdr.ip_sum; | |
641 | u8 tcp_action = TCP_DATA; | |
642 | u32 tcp_seq_num, tcp_ack_num; | |
a3bf193b YCLP |
643 | int tcp_hdr_len, payload_len; |
644 | ||
645 | /* Verify IP header */ | |
646 | debug_cond(DEBUG_DEV_PKT, | |
647 | "TCP RX in RX Sum (to=%pI4, from=%pI4, len=%d)\n", | |
648 | &b->ip.hdr.ip_src, &b->ip.hdr.ip_dst, pkt_len); | |
649 | ||
650 | b->ip.hdr.ip_src = net_server_ip; | |
651 | b->ip.hdr.ip_dst = net_ip; | |
652 | b->ip.hdr.ip_sum = 0; | |
653 | if (tcp_rx_xsum != compute_ip_checksum(b, IP_HDR_SIZE)) { | |
654 | debug_cond(DEBUG_DEV_PKT, | |
655 | "TCP RX IP xSum Error (%pI4, =%pI4, len=%d)\n", | |
656 | &net_ip, &net_server_ip, pkt_len); | |
657 | return; | |
658 | } | |
659 | ||
660 | /* Build pseudo header and verify TCP header */ | |
661 | tcp_rx_xsum = b->ip.hdr.tcp_xsum; | |
662 | b->ip.hdr.tcp_xsum = 0; | |
663 | if (tcp_rx_xsum != tcp_set_pseudo_header((uchar *)b, b->ip.hdr.ip_src, | |
664 | b->ip.hdr.ip_dst, tcp_len, | |
665 | pkt_len)) { | |
666 | debug_cond(DEBUG_DEV_PKT, | |
667 | "TCP RX TCP xSum Error (%pI4, %pI4, len=%d)\n", | |
668 | &net_ip, &net_server_ip, tcp_len); | |
669 | return; | |
670 | } | |
671 | ||
672 | tcp_hdr_len = GET_TCP_HDR_LEN_IN_BYTES(b->ip.hdr.tcp_hlen); | |
673 | payload_len = tcp_len - tcp_hdr_len; | |
674 | ||
675 | if (tcp_hdr_len > TCP_HDR_SIZE) | |
676 | tcp_parse_options((uchar *)b + IP_TCP_HDR_SIZE, | |
677 | tcp_hdr_len - TCP_HDR_SIZE); | |
678 | /* | |
679 | * Incoming sequence and ack numbers are server's view of the numbers. | |
680 | * The app must swap the numbers when responding. | |
681 | */ | |
682 | tcp_seq_num = ntohl(b->ip.hdr.tcp_seq); | |
683 | tcp_ack_num = ntohl(b->ip.hdr.tcp_ack); | |
684 | ||
685 | /* Packets are not ordered. Send to app as received. */ | |
686 | tcp_action = tcp_state_machine(b->ip.hdr.tcp_flags, | |
08fb8da3 | 687 | tcp_seq_num, payload_len); |
a3bf193b YCLP |
688 | |
689 | tcp_activity_count++; | |
690 | if (tcp_activity_count > TCP_ACTIVITY) { | |
691 | puts("| "); | |
692 | tcp_activity_count = 0; | |
693 | } | |
694 | ||
695 | if ((tcp_action & TCP_PUSH) || payload_len > 0) { | |
696 | debug_cond(DEBUG_DEV_PKT, | |
08fb8da3 | 697 | "TCP Notify (action=%x, Seq=%u,Ack=%u,Pay%d)\n", |
a3bf193b YCLP |
698 | tcp_action, tcp_seq_num, tcp_ack_num, payload_len); |
699 | ||
08fb8da3 DM |
700 | (*tcp_packet_handler) ((uchar *)b + pkt_len - payload_len, b->ip.hdr.tcp_dst, |
701 | b->ip.hdr.ip_src, b->ip.hdr.tcp_src, tcp_seq_num, | |
702 | tcp_ack_num, tcp_action, payload_len); | |
a3bf193b YCLP |
703 | |
704 | } else if (tcp_action != TCP_DATA) { | |
705 | debug_cond(DEBUG_DEV_PKT, | |
08fb8da3 DM |
706 | "TCP Action (action=%x,Seq=%u,Ack=%u,Pay=%d)\n", |
707 | tcp_action, tcp_ack_num, tcp_ack_edge, payload_len); | |
a3bf193b YCLP |
708 | |
709 | /* | |
710 | * Warning: Incoming Ack & Seq sequence numbers are transposed | |
711 | * here to outgoing Seq & Ack sequence numbers | |
712 | */ | |
713 | net_send_tcp_packet(0, ntohs(b->ip.hdr.tcp_src), | |
714 | ntohs(b->ip.hdr.tcp_dst), | |
715 | (tcp_action & (~TCP_PUSH)), | |
08fb8da3 | 716 | tcp_ack_num, tcp_ack_edge); |
a3bf193b YCLP |
717 | } |
718 | } |