blob: b0cc8a1fe3ed6d65ab244f497ac338ed2bb7a58a [file] [log] [blame]
Ying-Chun Liu (PaulLiu)a3bf1932022-11-08 14:17:28 +08001// 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 */
Ying-Chun Liu (PaulLiu)a3bf1932022-11-08 14:17:28 +080020#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 */
30static struct tcp_sack_v tcp_lost;
31
32/* TCP option timestamp */
33static u32 loc_timestamp;
34static u32 rmt_timestamp;
35
36static u32 tcp_seq_init;
37static u32 tcp_ack_edge;
Ying-Chun Liu (PaulLiu)a3bf1932022-11-08 14:17:28 +080038
39static 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
46enum pkt_state {PKT, NOPKT};
47struct sack_r {
48 struct sack_edges se;
49 enum pkt_state st;
50};
51
52static struct sack_r edge_a[TCP_SACK];
53static unsigned int sack_idx;
54static 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 */
67static enum tcp_state current_tcp_state;
68
69/* Current TCP RX packet handler */
70static rxhand_tcp *tcp_packet_handler;
71
72/**
73 * tcp_get_tcp_state() - get current TCP state
74 *
75 * Return: Current TCP state
76 */
77enum 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 */
86void tcp_set_tcp_state(enum tcp_state new_state)
87{
88 current_tcp_state = new_state;
89}
90
Dmitrii Merkurev08fb8da2023-04-12 19:49:29 +010091static 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)
Ying-Chun Liu (PaulLiu)a3bf1932022-11-08 14:17:28 +080095{
96}
97
98/**
99 * tcp_set_tcp_handler() - set a handler to receive data
100 * @f: handler
101 */
102void 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 */
121u16 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 */
155int 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 */
210void 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
239int 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,
Dmitrii Merkurev08fb8da2023-04-12 19:49:29 +0100258 "TCP Hdr:SYN (%pI4, %pI4, sq=%u, ak=%u)\n",
Ying-Chun Liu (PaulLiu)a3bf1932022-11-08 14:17:28 +0800259 &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;
Dmitrii Merkurev08fb8da2023-04-12 19:49:29 +0100273 case TCP_SYN | TCP_ACK:
Ying-Chun Liu (PaulLiu)a3bf1932022-11-08 14:17:28 +0800274 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,
Dmitrii Merkurev08fb8da2023-04-12 19:49:29 +0100278 "TCP Hdr:ACK (%pI4, %pI4, s=%u, a=%u, A=%x)\n",
Ying-Chun Liu (PaulLiu)a3bf1932022-11-08 14:17:28 +0800279 &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,
Dmitrii Merkurev08fb8da2023-04-12 19:49:29 +0100284 "TCP Hdr:FIN (%pI4, %pI4, s=%u, a=%u)\n",
Ying-Chun Liu (PaulLiu)a3bf1932022-11-08 14:17:28 +0800285 &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;
Dmitrii Merkurev08fb8da2023-04-12 19:49:29 +0100290 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;
Ying-Chun Liu (PaulLiu)a3bf1932022-11-08 14:17:28 +0800297 /* Notify connection closing */
Ying-Chun Liu (PaulLiu)a3bf1932022-11-08 14:17:28 +0800298 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
Ying-Chun Liu (PaulLiu)a3bf1932022-11-08 14:17:28 +0800303 debug_cond(DEBUG_DEV_PKT,
Dmitrii Merkurev08fb8da2023-04-12 19:49:29 +0100304 "TCP Hdr:FIN ACK PSH(%pI4, %pI4, s=%u, a=%u, A=%x)\n",
Ying-Chun Liu (PaulLiu)a3bf1932022-11-08 14:17:28 +0800305 &net_server_ip, &net_ip,
Dmitrii Merkurev08fb8da2023-04-12 19:49:29 +0100306 tcp_seq_num, tcp_ack_num, action);
Ying-Chun Liu (PaulLiu)a3bf1932022-11-08 14:17:28 +0800307 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,
Dmitrii Merkurev08fb8da2023-04-12 19:49:29 +0100312 "TCP Hdr:dft (%pI4, %pI4, s=%u, a=%u, A=%x)\n",
Ying-Chun Liu (PaulLiu)a3bf1932022-11-08 14:17:28 +0800313 &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
Dmitrii Merkurev08fb8da2023-04-12 19:49:29 +0100320 tcp_ack_edge = tcp_ack_num;
Ying-Chun Liu (PaulLiu)a3bf1932022-11-08 14:17:28 +0800321 /* 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);
Ying-Chun Liu (PaulLiu)a3bf1932022-11-08 14:17:28 +0800326
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
Ying-Chun Liu (PaulLiu)a3bf1932022-11-08 14:17:28 +0800360 */
Dmitrii Merkurev08fb8da2023-04-12 19:49:29 +0100361void tcp_hole(u32 tcp_seq_num, u32 len)
Ying-Chun Liu (PaulLiu)a3bf1932022-11-08 14:17:28 +0800362{
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 */
469void 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
Dmitrii Merkurev08fb8da2023-04-12 19:49:29 +0100502static u8 tcp_state_machine(u8 tcp_flags, u32 tcp_seq_num, int payload_len)
Ying-Chun Liu (PaulLiu)a3bf1932022-11-08 14:17:28 +0800503{
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);
Dmitrii Merkurev08fb8da2023-04-12 19:49:29 +0100533 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) {
Ying-Chun Liu (PaulLiu)a3bf1932022-11-08 14:17:28 +0800539 action = TCP_DATA;
Dmitrii Merkurev08fb8da2023-04-12 19:49:29 +0100540 }
Ying-Chun Liu (PaulLiu)a3bf1932022-11-08 14:17:28 +0800541 break;
Dmitrii Merkurev08fb8da2023-04-12 19:49:29 +0100542 case TCP_SYN_RECEIVED:
Ying-Chun Liu (PaulLiu)a3bf1932022-11-08 14:17:28 +0800543 case TCP_SYN_SENT:
Dmitrii Merkurev08fb8da2023-04-12 19:49:29 +0100544 debug_cond(DEBUG_INT_STATE, "TCP_SYN_SENT | TCP_SYN_RECEIVED %x, %u\n",
545 tcp_flags, tcp_seq_num);
Ying-Chun Liu (PaulLiu)a3bf1932022-11-08 14:17:28 +0800546 if (tcp_fin) {
547 action = action | TCP_PUSH;
548 current_tcp_state = TCP_CLOSE_WAIT;
Dmitrii Merkurev08fb8da2023-04-12 19:49:29 +0100549 } 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 {
Ying-Chun Liu (PaulLiu)a3bf1932022-11-08 14:17:28 +0800564 action = TCP_DATA;
565 }
Ying-Chun Liu (PaulLiu)a3bf1932022-11-08 14:17:28 +0800566 break;
567 case TCP_ESTABLISHED:
568 debug_cond(DEBUG_INT_STATE, "TCP_ESTABLISHED %x\n", tcp_flags);
Ying-Chun Liu (PaulLiu)a3bf1932022-11-08 14:17:28 +0800569 if (payload_len > 0) {
Dmitrii Merkurev08fb8da2023-04-12 19:49:29 +0100570 tcp_hole(tcp_seq_num, payload_len);
Ying-Chun Liu (PaulLiu)a3bf1932022-11-08 14:17:28 +0800571 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) {
Dmitrii Merkurev08fb8da2023-04-12 19:49:29 +0100607 tcp_ack_edge++;
Ying-Chun Liu (PaulLiu)a3bf1932022-11-08 14:17:28 +0800608 action = TCP_ACK | TCP_FIN;
609 current_tcp_state = TCP_FIN_WAIT_2;
610 }
611 if (tcp_syn)
612 action = TCP_RST;
Dmitrii Merkurev08fb8da2023-04-12 19:49:29 +0100613 if (tcp_ack)
Ying-Chun Liu (PaulLiu)a3bf1932022-11-08 14:17:28 +0800614 current_tcp_state = TCP_CLOSED;
Ying-Chun Liu (PaulLiu)a3bf1932022-11-08 14:17:28 +0800615 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 */
637void 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;
Ying-Chun Liu (PaulLiu)a3bf1932022-11-08 14:17:28 +0800643 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,
Dmitrii Merkurev08fb8da2023-04-12 19:49:29 +0100687 tcp_seq_num, payload_len);
Ying-Chun Liu (PaulLiu)a3bf1932022-11-08 14:17:28 +0800688
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,
Dmitrii Merkurev08fb8da2023-04-12 19:49:29 +0100697 "TCP Notify (action=%x, Seq=%u,Ack=%u,Pay%d)\n",
Ying-Chun Liu (PaulLiu)a3bf1932022-11-08 14:17:28 +0800698 tcp_action, tcp_seq_num, tcp_ack_num, payload_len);
699
Dmitrii Merkurev08fb8da2023-04-12 19:49:29 +0100700 (*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);
Ying-Chun Liu (PaulLiu)a3bf1932022-11-08 14:17:28 +0800703
704 } else if (tcp_action != TCP_DATA) {
705 debug_cond(DEBUG_DEV_PKT,
Dmitrii Merkurev08fb8da2023-04-12 19:49:29 +0100706 "TCP Action (action=%x,Seq=%u,Ack=%u,Pay=%d)\n",
707 tcp_action, tcp_ack_num, tcp_ack_edge, payload_len);
Ying-Chun Liu (PaulLiu)a3bf1932022-11-08 14:17:28 +0800708
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)),
Dmitrii Merkurev08fb8da2023-04-12 19:49:29 +0100716 tcp_ack_num, tcp_ack_edge);
Ying-Chun Liu (PaulLiu)a3bf1932022-11-08 14:17:28 +0800717 }
718}