blob: 2635727f47de6589022470f89faf9d3acf52bf22 [file] [log] [blame]
Ying-Chun Liu (PaulLiu)41efed12022-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)41efed12022-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
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +030027/*
28 * The start sequence number increment for the two sequently created
29 * connections within the same timer tick. This number must be:
30 * - prime (to increase the time before the same number will be generated)
31 * - larger than typical MTU (to avoid similar numbers for two sequently
32 * created connections)
33 */
34#define TCP_START_SEQ_INC 2153 /* just large prime number */
35
36#define TCP_SEND_RETRY 3
37#define TCP_SEND_TIMEOUT 2000UL
38#define TCP_RX_INACTIVE_TIMEOUT 30000UL
Mikhail Kshevetskiy30cd9f42024-12-28 13:46:36 +030039#if PKTBUFSRX != 0
40 #define TCP_RCV_WND_SIZE (PKTBUFSRX * TCP_MSS)
41#else
42 #define TCP_RCV_WND_SIZE (4 * TCP_MSS)
43#endif
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +030044
45#define TCP_PACKET_OK 0
46#define TCP_PACKET_DROP 1
47
Mikhail Kshevetskiy3e04c862024-12-28 13:46:29 +030048static struct tcp_stream tcp_stream;
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +030049
50static int (*tcp_stream_on_create)(struct tcp_stream *tcp);
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +080051
52/*
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +080053 * TCP lengths are stored as a rounded up number of 32 bit words.
54 * Add 3 to length round up, rounded, then divided into the
55 * length in 32 bit words.
56 */
57#define LEN_B_TO_DW(x) ((x) >> 2)
58#define ROUND_TCPHDR_LEN(x) (LEN_B_TO_DW((x) + 3))
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +030059#define ROUND_TCPHDR_BYTES(x) (((x) + 3) & ~3)
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +080060#define SHIFT_TO_TCPHDRLEN_FIELD(x) ((x) << 4)
61#define GET_TCP_HDR_LEN_IN_BYTES(x) ((x) >> 2)
62
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +030063#define RANDOM_PORT_START 1024
64#define RANDOM_PORT_RANGE 0x4000
65
66/**
67 * random_port() - make port a little random (1024-17407)
68 *
69 * Return: random port number from 1024 to 17407
70 *
71 * This keeps the math somewhat trivial to compute, and seems to work with
72 * all supported protocols/clients/servers
73 */
74static uint random_port(void)
75{
76 return RANDOM_PORT_START + (get_timer(0) % RANDOM_PORT_RANGE);
77}
78
Mikhail Kshevetskiy78cb2da2024-12-28 13:46:28 +030079static inline s32 tcp_seq_cmp(u32 a, u32 b)
80{
81 return (s32)(a - b);
82}
83
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +030084static inline u32 tcp_get_start_seq(void)
85{
86 static u32 tcp_seq_inc;
87 u32 tcp_seq;
88
89 tcp_seq = (get_timer(0) & 0xffffffff) + tcp_seq_inc;
90 tcp_seq_inc += TCP_START_SEQ_INC;
91
92 return tcp_seq;
93}
94
95static inline ulong msec_to_ticks(ulong msec)
96{
97 return msec * CONFIG_SYS_HZ / 1000;
98}
99
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800100/**
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +0300101 * tcp_stream_get_state() - get TCP stream state
Mikhail Kshevetskiy3e04c862024-12-28 13:46:29 +0300102 * @tcp: tcp stream
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800103 *
Mikhail Kshevetskiy3e04c862024-12-28 13:46:29 +0300104 * Return: TCP stream state
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800105 */
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +0300106enum tcp_state tcp_stream_get_state(struct tcp_stream *tcp)
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800107{
Mikhail Kshevetskiy3e04c862024-12-28 13:46:29 +0300108 return tcp->state;
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800109}
110
111/**
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +0300112 * tcp_stream_set_state() - set TCP stream state
Mikhail Kshevetskiy3e04c862024-12-28 13:46:29 +0300113 * @tcp: tcp stream
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800114 * @new_state: new TCP state
115 */
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +0300116static void tcp_stream_set_state(struct tcp_stream *tcp,
117 enum tcp_state new_state)
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800118{
Mikhail Kshevetskiy3e04c862024-12-28 13:46:29 +0300119 tcp->state = new_state;
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800120}
121
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300122/**
123 * tcp_stream_get_status() - get TCP stream status
124 * @tcp: tcp stream
125 *
126 * Return: TCP stream status
127 */
128enum tcp_status tcp_stream_get_status(struct tcp_stream *tcp)
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +0300129{
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300130 return tcp->status;
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +0300131}
132
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300133/**
134 * tcp_stream_set_status() - set TCP stream state
135 * @tcp: tcp stream
136 * @new_satus: new TCP stream status
137 */
138static void tcp_stream_set_status(struct tcp_stream *tcp,
139 enum tcp_status new_status)
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +0300140{
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300141 tcp->status = new_status;
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +0300142}
143
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300144void tcp_stream_restart_rx_timer(struct tcp_stream *tcp)
Mikhail Kshevetskiy3e04c862024-12-28 13:46:29 +0300145{
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300146 tcp->time_last_rx = get_timer(0);
147}
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +0300148
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300149static void tcp_stream_init(struct tcp_stream *tcp,
150 struct in_addr rhost, u16 rport, u16 lport)
151{
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +0300152 memset(tcp, 0, sizeof(struct tcp_stream));
153 tcp->rhost.s_addr = rhost.s_addr;
154 tcp->rport = rport;
155 tcp->lport = lport;
156 tcp->state = TCP_CLOSED;
157 tcp->lost.len = TCP_OPT_LEN_2;
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300158 tcp->rcv_wnd = TCP_RCV_WND_SIZE;
159 tcp->max_retry_count = TCP_SEND_RETRY;
160 tcp->initial_timeout = TCP_SEND_TIMEOUT;
161 tcp->rx_inactiv_timeout = TCP_RX_INACTIVE_TIMEOUT;
162 tcp_stream_restart_rx_timer(tcp);
163}
164
165static void tcp_stream_destroy(struct tcp_stream *tcp)
166{
167 if (tcp->on_closed)
168 tcp->on_closed(tcp);
169 memset(tcp, 0, sizeof(struct tcp_stream));
170}
171
172void tcp_init(void)
173{
174 static int initialized;
175 struct tcp_stream *tcp = &tcp_stream;
176
177 tcp_stream_on_create = NULL;
178 if (!initialized) {
179 initialized = 1;
180 memset(tcp, 0, sizeof(struct tcp_stream));
181 }
182
183 tcp_stream_set_state(tcp, TCP_CLOSED);
184 tcp_stream_set_status(tcp, TCP_ERR_RST);
185 tcp_stream_destroy(tcp);
186}
187
188void tcp_stream_set_on_create_handler(int (*on_create)(struct tcp_stream *))
189{
190 tcp_stream_on_create = on_create;
191}
192
193static struct tcp_stream *tcp_stream_add(struct in_addr rhost,
194 u16 rport, u16 lport)
195{
196 struct tcp_stream *tcp = &tcp_stream;
197
198 if (!tcp_stream_on_create ||
199 tcp->state != TCP_CLOSED)
200 return NULL;
201
202 tcp_stream_init(tcp, rhost, rport, lport);
203 if (!tcp_stream_on_create(tcp))
204 return NULL;
205
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +0300206 return tcp;
207}
208
209struct tcp_stream *tcp_stream_get(int is_new, struct in_addr rhost,
210 u16 rport, u16 lport)
211{
212 struct tcp_stream *tcp = &tcp_stream;
213
214 if (tcp->rhost.s_addr == rhost.s_addr &&
215 tcp->rport == rport &&
216 tcp->lport == lport)
217 return tcp;
218
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300219 return is_new ? tcp_stream_add(rhost, rport, lport) : NULL;
220}
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +0300221
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300222void tcp_stream_put(struct tcp_stream *tcp)
223{
224 if (tcp->state == TCP_CLOSED)
225 tcp_stream_destroy(tcp);
Mikhail Kshevetskiy3e04c862024-12-28 13:46:29 +0300226}
227
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300228u32 tcp_stream_rx_offs(struct tcp_stream *tcp)
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800229{
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300230 u32 ret;
231
232 switch (tcp->state) {
233 case TCP_CLOSED:
234 case TCP_SYN_SENT:
235 case TCP_SYN_RECEIVED:
236 return 0;
237 default:
238 break;
239 }
240
241 ret = tcp->rcv_nxt - tcp->irs - 1;
242 if (tcp->fin_rx && tcp->rcv_nxt == tcp->fin_rx_seq)
243 ret--;
244
245 return ret;
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800246}
247
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300248u32 tcp_stream_tx_offs(struct tcp_stream *tcp)
249{
250 u32 ret;
251
252 switch (tcp->state) {
253 case TCP_CLOSED:
254 case TCP_SYN_SENT:
255 case TCP_SYN_RECEIVED:
256 return 0;
257 default:
258 break;
259 }
260
261 ret = tcp->snd_una - tcp->iss - 1;
262 if (tcp->fin_tx && tcp->snd_una == tcp->fin_tx_seq + 1)
263 ret--;
264
265 return ret;
266}
267
268static void tcp_stream_set_time_handler(struct tcp_stream *tcp, ulong msec,
269 void (*handler)(struct tcp_stream *))
270{
271 if (!msec) {
272 tcp->time_handler = NULL;
273 return;
274 }
275
276 tcp->time_handler = handler;
277 tcp->time_start = get_timer(0);
278 tcp->time_delta = msec_to_ticks(msec);
279}
280
281static void tcp_send_packet(struct tcp_stream *tcp, u8 action,
282 u32 tcp_seq_num, u32 tcp_ack_num, u32 tx_len)
283{
284 tcp->tx_packets++;
285 net_send_tcp_packet(tx_len, tcp->rhost, tcp->rport,
286 tcp->lport, action, tcp_seq_num,
287 tcp_ack_num);
288}
289
290static void tcp_send_repeat(struct tcp_stream *tcp)
291{
292 uchar *ptr;
293 u32 tcp_opts_size;
294 int ret;
295
296 if (!tcp->retry_cnt) {
297 puts("\nTCP: send retry counter exceeded\n");
298 tcp_send_packet(tcp, TCP_RST, tcp->retry_seq_num,
299 tcp->rcv_nxt, 0);
300 tcp_stream_set_status(tcp, TCP_ERR_TOUT);
301 tcp_stream_set_state(tcp, TCP_CLOSED);
302 tcp_stream_destroy(tcp);
303 return;
304 }
305 tcp->retry_cnt--;
306 tcp->retry_timeout += tcp->initial_timeout;
307
308 if (tcp->retry_tx_len > 0) {
309 tcp_opts_size = ROUND_TCPHDR_BYTES(TCP_TSOPT_SIZE +
310 tcp->lost.len);
311 ptr = net_tx_packet + net_eth_hdr_size() +
312 IP_TCP_HDR_SIZE + tcp_opts_size;
313
314 if (tcp->retry_tx_len > TCP_MSS - tcp_opts_size)
315 tcp->retry_tx_len = TCP_MSS - tcp_opts_size;
316
317 /* refill packet data */
318 ret = tcp->tx(tcp, tcp->retry_tx_offs, ptr, tcp->retry_tx_len);
319 if (ret < 0) {
320 puts("\nTCP: send failure\n");
321 tcp_send_packet(tcp, TCP_RST, tcp->retry_seq_num,
322 tcp->rcv_nxt, 0);
323 tcp_stream_set_status(tcp, TCP_ERR_IO);
324 tcp_stream_set_state(tcp, TCP_CLOSED);
325 tcp_stream_destroy(tcp);
326 return;
327 }
328 }
329 tcp_send_packet(tcp, tcp->retry_action, tcp->retry_seq_num,
330 tcp->rcv_nxt, tcp->retry_tx_len);
331
332 tcp_stream_set_time_handler(tcp, tcp->retry_timeout, tcp_send_repeat);
333}
334
335static void tcp_send_packet_with_retry(struct tcp_stream *tcp, u8 action,
336 u32 tcp_seq_num, u32 tx_len, u32 tx_offs)
337{
338 tcp->retry_cnt = tcp->max_retry_count;
339 tcp->retry_timeout = tcp->initial_timeout;
340 tcp->retry_action = action;
341 tcp->retry_seq_num = tcp_seq_num;
342 tcp->retry_tx_len = tx_len;
343 tcp->retry_tx_offs = tx_offs;
344
345 tcp_send_packet(tcp, action, tcp_seq_num, tcp->rcv_nxt, tx_len);
346 tcp_stream_set_time_handler(tcp, tcp->retry_timeout, tcp_send_repeat);
347}
348
349static inline u8 tcp_stream_fin_needed(struct tcp_stream *tcp, u32 tcp_seq_num)
350{
351 return (tcp->fin_tx && (tcp_seq_num == tcp->fin_tx_seq)) ? TCP_FIN : 0;
352}
353
354static void tcp_steam_tx_try(struct tcp_stream *tcp)
355{
356 uchar *ptr;
357 int tx_len;
358 u32 tx_offs, tcp_opts_size;
359
360 if (tcp->state != TCP_ESTABLISHED ||
361 tcp->time_handler ||
362 !tcp->tx)
363 return;
364
365 tcp_opts_size = ROUND_TCPHDR_BYTES(TCP_TSOPT_SIZE + tcp->lost.len);
366 tx_len = TCP_MSS - tcp_opts_size;
367 if (tcp->fin_tx) {
368 /* do not try to send beyonds FIN packet limits */
369 if (tcp_seq_cmp(tcp->snd_una, tcp->fin_tx_seq) >= 0)
370 return;
371
372 tx_len = tcp->fin_tx_seq - tcp->snd_una;
373 if (tx_len > TCP_MSS - tcp_opts_size)
374 tx_len = TCP_MSS - tcp_opts_size;
375 }
376
377 tx_offs = tcp_stream_tx_offs(tcp);
378 ptr = net_tx_packet + net_eth_hdr_size() +
379 IP_TCP_HDR_SIZE + tcp_opts_size;
380
381 /* fill packet data and adjust size */
382 tx_len = tcp->tx(tcp, tx_offs, ptr, tx_len);
383 if (tx_len < 0) {
384 puts("\nTCP: send failure\n");
385 tcp_send_packet(tcp, TCP_RST, tcp->retry_seq_num,
386 tcp->rcv_nxt, 0);
387 tcp_stream_set_status(tcp, TCP_ERR_IO);
388 tcp_stream_set_state(tcp, TCP_CLOSED);
389 tcp_stream_destroy(tcp);
390 return;
391 }
392 if (!tx_len)
393 return;
394
395 if (tcp_seq_cmp(tcp->snd_una + tx_len, tcp->snd_nxt) > 0)
396 tcp->snd_nxt = tcp->snd_una + tx_len;
397
398 tcp_send_packet_with_retry(tcp, TCP_ACK | TCP_PUSH,
399 tcp->snd_una, tx_len, tx_offs);
400}
401
402static void tcp_stream_poll(struct tcp_stream *tcp, ulong time)
403{
404 ulong delta;
405 void (*handler)(struct tcp_stream *tcp);
406
407 if (tcp->state == TCP_CLOSED)
408 return;
409
410 /* handle rx inactivity timeout */
411 delta = msec_to_ticks(tcp->rx_inactiv_timeout);
412 if (time - tcp->time_last_rx >= delta) {
413 puts("\nTCP: rx inactivity timeout exceeded\n");
414 tcp_stream_reset(tcp);
415 tcp_stream_set_status(tcp, TCP_ERR_TOUT);
416 tcp_stream_destroy(tcp);
417 return;
418 }
419
420 /* handle retransmit timeout */
421 if (tcp->time_handler &&
422 time - tcp->time_start >= tcp->time_delta) {
423 handler = tcp->time_handler;
424 tcp->time_handler = NULL;
425 handler(tcp);
426 }
427
428 tcp_steam_tx_try(tcp);
429}
430
431void tcp_streams_poll(void)
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800432{
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300433 ulong time;
434 struct tcp_stream *tcp;
435
436 time = get_timer(0);
437 tcp = &tcp_stream;
438 tcp_stream_poll(tcp, time);
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800439}
440
441/**
442 * tcp_set_pseudo_header() - set TCP pseudo header
443 * @pkt: the packet
444 * @src: source IP address
445 * @dest: destinaion IP address
446 * @tcp_len: tcp length
447 * @pkt_len: packet length
448 *
449 * Return: the checksum of the packet
450 */
451u16 tcp_set_pseudo_header(uchar *pkt, struct in_addr src, struct in_addr dest,
452 int tcp_len, int pkt_len)
453{
454 union tcp_build_pkt *b = (union tcp_build_pkt *)pkt;
455 int checksum_len;
456
457 /*
458 * Pseudo header
459 *
460 * Zero the byte after the last byte so that the header checksum
461 * will always work.
462 */
463 pkt[pkt_len] = 0;
464
465 net_copy_ip((void *)&b->ph.p_src, &src);
466 net_copy_ip((void *)&b->ph.p_dst, &dest);
467 b->ph.rsvd = 0;
468 b->ph.p = IPPROTO_TCP;
469 b->ph.len = htons(tcp_len);
470 checksum_len = tcp_len + PSEUDO_HDR_SIZE;
471
472 debug_cond(DEBUG_DEV_PKT,
473 "TCP Pesudo Header (to=%pI4, from=%pI4, Len=%d)\n",
474 &b->ph.p_dst, &b->ph.p_src, checksum_len);
475
476 return compute_ip_checksum(pkt + PSEUDO_PAD_SIZE, checksum_len);
477}
478
479/**
480 * net_set_ack_options() - set TCP options in acknowledge packets
Mikhail Kshevetskiy3e04c862024-12-28 13:46:29 +0300481 * @tcp: tcp stream
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800482 * @b: the packet
483 *
484 * Return: TCP header length
485 */
Mikhail Kshevetskiy3e04c862024-12-28 13:46:29 +0300486int net_set_ack_options(struct tcp_stream *tcp, union tcp_build_pkt *b)
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800487{
488 b->sack.hdr.tcp_hlen = SHIFT_TO_TCPHDRLEN_FIELD(LEN_B_TO_DW(TCP_HDR_SIZE));
489
490 b->sack.t_opt.kind = TCP_O_TS;
491 b->sack.t_opt.len = TCP_OPT_LEN_A;
Mikhail Kshevetskiy3e04c862024-12-28 13:46:29 +0300492 b->sack.t_opt.t_snd = htons(tcp->loc_timestamp);
493 b->sack.t_opt.t_rcv = tcp->rmt_timestamp;
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800494 b->sack.sack_v.kind = TCP_1_NOP;
495 b->sack.sack_v.len = 0;
496
497 if (IS_ENABLED(CONFIG_PROT_TCP_SACK)) {
Mikhail Kshevetskiy3e04c862024-12-28 13:46:29 +0300498 if (tcp->lost.len > TCP_OPT_LEN_2) {
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800499 debug_cond(DEBUG_DEV_PKT, "TCP ack opt lost.len %x\n",
Mikhail Kshevetskiy3e04c862024-12-28 13:46:29 +0300500 tcp->lost.len);
501 b->sack.sack_v.len = tcp->lost.len;
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800502 b->sack.sack_v.kind = TCP_V_SACK;
Mikhail Kshevetskiy3e04c862024-12-28 13:46:29 +0300503 b->sack.sack_v.hill[0].l = htonl(tcp->lost.hill[0].l);
504 b->sack.sack_v.hill[0].r = htonl(tcp->lost.hill[0].r);
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800505
506 /*
507 * These SACK structures are initialized with NOPs to
508 * provide TCP header alignment padding. There are 4
509 * SACK structures used for both header padding and
510 * internally.
511 */
Mikhail Kshevetskiy3e04c862024-12-28 13:46:29 +0300512 b->sack.sack_v.hill[1].l = htonl(tcp->lost.hill[1].l);
513 b->sack.sack_v.hill[1].r = htonl(tcp->lost.hill[1].r);
514 b->sack.sack_v.hill[2].l = htonl(tcp->lost.hill[2].l);
515 b->sack.sack_v.hill[2].r = htonl(tcp->lost.hill[2].r);
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800516 b->sack.sack_v.hill[3].l = TCP_O_NOP;
517 b->sack.sack_v.hill[3].r = TCP_O_NOP;
518 }
519
520 b->sack.hdr.tcp_hlen = SHIFT_TO_TCPHDRLEN_FIELD(ROUND_TCPHDR_LEN(TCP_HDR_SIZE +
521 TCP_TSOPT_SIZE +
Mikhail Kshevetskiy3e04c862024-12-28 13:46:29 +0300522 tcp->lost.len));
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800523 } else {
524 b->sack.sack_v.kind = 0;
525 b->sack.hdr.tcp_hlen = SHIFT_TO_TCPHDRLEN_FIELD(ROUND_TCPHDR_LEN(TCP_HDR_SIZE +
526 TCP_TSOPT_SIZE));
527 }
528
529 /*
530 * This returns the actual rounded up length of the
531 * TCP header to add to the total packet length
532 */
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800533 return GET_TCP_HDR_LEN_IN_BYTES(b->sack.hdr.tcp_hlen);
534}
535
536/**
Mikhail Kshevetskiy3e04c862024-12-28 13:46:29 +0300537 * net_set_syn_options() - set TCP options in SYN packets
538 * @tcp: tcp stream
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800539 * @b: the packet
540 */
Mikhail Kshevetskiy3e04c862024-12-28 13:46:29 +0300541void net_set_syn_options(struct tcp_stream *tcp, union tcp_build_pkt *b)
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800542{
543 if (IS_ENABLED(CONFIG_PROT_TCP_SACK))
Mikhail Kshevetskiy3e04c862024-12-28 13:46:29 +0300544 tcp->lost.len = 0;
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800545
546 b->ip.hdr.tcp_hlen = 0xa0;
547
548 b->ip.mss.kind = TCP_O_MSS;
549 b->ip.mss.len = TCP_OPT_LEN_4;
550 b->ip.mss.mss = htons(TCP_MSS);
551 b->ip.scale.kind = TCP_O_SCL;
552 b->ip.scale.scale = TCP_SCALE;
553 b->ip.scale.len = TCP_OPT_LEN_3;
554 if (IS_ENABLED(CONFIG_PROT_TCP_SACK)) {
555 b->ip.sack_p.kind = TCP_P_SACK;
556 b->ip.sack_p.len = TCP_OPT_LEN_2;
557 } else {
558 b->ip.sack_p.kind = TCP_1_NOP;
559 b->ip.sack_p.len = TCP_1_NOP;
560 }
561 b->ip.t_opt.kind = TCP_O_TS;
562 b->ip.t_opt.len = TCP_OPT_LEN_A;
Mikhail Kshevetskiy3e04c862024-12-28 13:46:29 +0300563 tcp->loc_timestamp = get_ticks();
564 tcp->rmt_timestamp = 0;
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800565 b->ip.t_opt.t_snd = 0;
566 b->ip.t_opt.t_rcv = 0;
567 b->ip.end = TCP_O_END;
568}
569
Mikhail Kshevetskiy4207f6a2024-12-28 13:46:35 +0300570const char *tcpflags_to_str(char tcpflags, char *buf, int size)
571{
572 int i;
573 static const struct {
574 int bit;
575 const char *name;
576 } desc[] = {{TCP_RST, "RST"}, {TCP_SYN, "SYN"}, {TCP_PUSH, "PSH"},
577 {TCP_FIN, "FIN"}, {TCP_ACK, "ACK"}};
578
579 *buf = '\0';
580 for (i = 0; i < ARRAY_SIZE(desc); i++) {
581 if (!(tcpflags & desc[i].bit))
582 continue;
583
584 if (*buf)
585 strlcat(buf, ",", size);
586 strlcat(buf, desc[i].name, size);
587 }
588
589 return buf;
590}
591
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +0300592int tcp_set_tcp_header(struct tcp_stream *tcp, uchar *pkt, int payload_len,
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800593 u8 action, u32 tcp_seq_num, u32 tcp_ack_num)
594{
595 union tcp_build_pkt *b = (union tcp_build_pkt *)pkt;
Mikhail Kshevetskiy4207f6a2024-12-28 13:46:35 +0300596 char buf[24];
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800597 int pkt_hdr_len;
598 int pkt_len;
599 int tcp_len;
600
601 /*
602 * Header: 5 32 bit words. 4 bits TCP header Length,
603 * 4 bits reserved options
604 */
605 b->ip.hdr.tcp_flags = action;
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800606 b->ip.hdr.tcp_hlen = SHIFT_TO_TCPHDRLEN_FIELD(LEN_B_TO_DW(TCP_HDR_SIZE));
607
608 switch (action) {
609 case TCP_SYN:
610 debug_cond(DEBUG_DEV_PKT,
Mikhail Kshevetskiy4207f6a2024-12-28 13:46:35 +0300611 "TCP Hdr:%s (%pI4, %pI4, s=%u, a=%u)\n",
612 tcpflags_to_str(action, buf, sizeof(buf)),
613 &tcp->rhost, &net_ip, tcp_seq_num, tcp_ack_num);
Mikhail Kshevetskiy3e04c862024-12-28 13:46:29 +0300614 net_set_syn_options(tcp, b);
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800615 pkt_hdr_len = IP_TCP_O_SIZE;
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800616 break;
Dmitrii Merkurev3acd0542023-04-12 19:49:29 +0100617 case TCP_RST | TCP_ACK:
618 case TCP_RST:
619 debug_cond(DEBUG_DEV_PKT,
Mikhail Kshevetskiy4207f6a2024-12-28 13:46:35 +0300620 "TCP Hdr:%s (%pI4, %pI4, s=%u, a=%u)\n",
621 tcpflags_to_str(action, buf, sizeof(buf)),
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +0300622 &tcp->rhost, &net_ip, tcp_seq_num, tcp_ack_num);
Mikhail Kshevetskiy4207f6a2024-12-28 13:46:35 +0300623 pkt_hdr_len = IP_TCP_HDR_SIZE;
Dmitrii Merkurev3acd0542023-04-12 19:49:29 +0100624 break;
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800625 default:
Mikhail Kshevetskiy3e04c862024-12-28 13:46:29 +0300626 pkt_hdr_len = IP_HDR_SIZE + net_set_ack_options(tcp, b);
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800627 debug_cond(DEBUG_DEV_PKT,
Mikhail Kshevetskiy4207f6a2024-12-28 13:46:35 +0300628 "TCP Hdr:%s (%pI4, %pI4, s=%u, a=%u)\n",
629 tcpflags_to_str(action, buf, sizeof(buf)),
630 &tcp->rhost, &net_ip, tcp_seq_num, tcp_ack_num);
631 break;
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800632 }
633
634 pkt_len = pkt_hdr_len + payload_len;
635 tcp_len = pkt_len - IP_HDR_SIZE;
636
Mikhail Kshevetskiyc8485442024-12-28 13:46:31 +0300637 tcp->rcv_nxt = tcp_ack_num;
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800638 /* TCP Header */
Mikhail Kshevetskiyc8485442024-12-28 13:46:31 +0300639 b->ip.hdr.tcp_ack = htonl(tcp->rcv_nxt);
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +0300640 b->ip.hdr.tcp_src = htons(tcp->lport);
641 b->ip.hdr.tcp_dst = htons(tcp->rport);
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800642 b->ip.hdr.tcp_seq = htonl(tcp_seq_num);
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800643
644 /*
645 * TCP window size - TCP header variable tcp_win.
646 * Change tcp_win only if you have an understanding of network
647 * overrun, congestion, TCP segment sizes, TCP windows, TCP scale,
648 * queuing theory and packet buffering. If there are too few buffers,
649 * there will be data loss, recovery may work or the sending TCP,
650 * the server, could abort the stream transmission.
651 * MSS is governed by maximum Ethernet frame length.
652 * The number of buffers is governed by the desire to have a queue of
653 * full buffers to be processed at the destination to maximize
654 * throughput. Temporary memory use for the boot phase on modern
655 * SOCs is may not be considered a constraint to buffer space, if
656 * it is, then the u-boot tftp or nfs kernel netboot should be
657 * considered.
658 */
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300659 b->ip.hdr.tcp_win = htons(tcp->rcv_wnd >> TCP_SCALE);
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800660
661 b->ip.hdr.tcp_xsum = 0;
662 b->ip.hdr.tcp_ugr = 0;
663
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +0300664 b->ip.hdr.tcp_xsum = tcp_set_pseudo_header(pkt, net_ip, tcp->rhost,
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800665 tcp_len, pkt_len);
666
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +0300667 net_set_ip_header((uchar *)&b->ip, tcp->rhost, net_ip,
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800668 pkt_len, IPPROTO_TCP);
669
670 return pkt_hdr_len;
671}
672
Mikhail Kshevetskiyc8485442024-12-28 13:46:31 +0300673static void tcp_update_rcv_nxt(struct tcp_stream *tcp)
Mikhail Kshevetskiy78cb2da2024-12-28 13:46:28 +0300674{
Mikhail Kshevetskiyc8485442024-12-28 13:46:31 +0300675 if (tcp_seq_cmp(tcp->rcv_nxt, tcp->lost.hill[0].l) >= 0) {
676 tcp->rcv_nxt = tcp->lost.hill[0].r;
Mikhail Kshevetskiy78cb2da2024-12-28 13:46:28 +0300677
Mikhail Kshevetskiy3e04c862024-12-28 13:46:29 +0300678 memmove(&tcp->lost.hill[0], &tcp->lost.hill[1],
Mikhail Kshevetskiy78cb2da2024-12-28 13:46:28 +0300679 (TCP_SACK_HILLS - 1) * sizeof(struct sack_edges));
680
Mikhail Kshevetskiy3e04c862024-12-28 13:46:29 +0300681 tcp->lost.len -= TCP_OPT_LEN_8;
682 tcp->lost.hill[TCP_SACK_HILLS - 1].l = TCP_O_NOP;
683 tcp->lost.hill[TCP_SACK_HILLS - 1].r = TCP_O_NOP;
Mikhail Kshevetskiy78cb2da2024-12-28 13:46:28 +0300684 }
685}
686
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800687/**
688 * tcp_hole() - Selective Acknowledgment (Essential for fast stream transfer)
Mikhail Kshevetskiy3e04c862024-12-28 13:46:29 +0300689 * @tcp: tcp stream
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800690 * @tcp_seq_num: TCP sequence start number
691 * @len: the length of sequence numbers
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800692 */
Mikhail Kshevetskiy3e04c862024-12-28 13:46:29 +0300693void tcp_hole(struct tcp_stream *tcp, u32 tcp_seq_num, u32 len)
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800694{
Mikhail Kshevetskiy78cb2da2024-12-28 13:46:28 +0300695 int i, j, cnt, cnt_move;
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800696
Mikhail Kshevetskiy3e04c862024-12-28 13:46:29 +0300697 cnt = (tcp->lost.len - TCP_OPT_LEN_2) / TCP_OPT_LEN_8;
Mikhail Kshevetskiy78cb2da2024-12-28 13:46:28 +0300698 for (i = 0; i < cnt; i++) {
Mikhail Kshevetskiy3e04c862024-12-28 13:46:29 +0300699 if (tcp_seq_cmp(tcp->lost.hill[i].r, tcp_seq_num) < 0)
Mikhail Kshevetskiy78cb2da2024-12-28 13:46:28 +0300700 continue;
Mikhail Kshevetskiy3e04c862024-12-28 13:46:29 +0300701 if (tcp_seq_cmp(tcp->lost.hill[i].l, tcp_seq_num + len) > 0)
Mikhail Kshevetskiy78cb2da2024-12-28 13:46:28 +0300702 break;
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800703
Mikhail Kshevetskiy3e04c862024-12-28 13:46:29 +0300704 if (tcp_seq_cmp(tcp->lost.hill[i].l, tcp_seq_num) > 0)
705 tcp->lost.hill[i].l = tcp_seq_num;
706 if (tcp_seq_cmp(tcp->lost.hill[i].l, tcp_seq_num) < 0) {
707 len += tcp_seq_num - tcp->lost.hill[i].l;
708 tcp_seq_num = tcp->lost.hill[i].l;
Mikhail Kshevetskiy78cb2da2024-12-28 13:46:28 +0300709 }
Mikhail Kshevetskiy3e04c862024-12-28 13:46:29 +0300710 if (tcp_seq_cmp(tcp->lost.hill[i].r, tcp_seq_num + len) >= 0) {
Mikhail Kshevetskiyc8485442024-12-28 13:46:31 +0300711 tcp_update_rcv_nxt(tcp);
Mikhail Kshevetskiy78cb2da2024-12-28 13:46:28 +0300712 return;
713 }
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800714
Mikhail Kshevetskiy78cb2da2024-12-28 13:46:28 +0300715 /* check overlapping with next hills */
716 cnt_move = 0;
Mikhail Kshevetskiy3e04c862024-12-28 13:46:29 +0300717 tcp->lost.hill[i].r = tcp_seq_num + len;
Mikhail Kshevetskiy78cb2da2024-12-28 13:46:28 +0300718 for (j = i + 1; j < cnt; j++) {
Mikhail Kshevetskiy3e04c862024-12-28 13:46:29 +0300719 if (tcp_seq_cmp(tcp->lost.hill[j].l, tcp->lost.hill[i].r) > 0)
Mikhail Kshevetskiy78cb2da2024-12-28 13:46:28 +0300720 break;
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800721
Mikhail Kshevetskiy3e04c862024-12-28 13:46:29 +0300722 tcp->lost.hill[i].r = tcp->lost.hill[j].r;
Mikhail Kshevetskiy78cb2da2024-12-28 13:46:28 +0300723 cnt_move++;
724 }
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800725
Mikhail Kshevetskiy78cb2da2024-12-28 13:46:28 +0300726 if (cnt_move > 0) {
727 if (cnt > i + cnt_move + 1)
Mikhail Kshevetskiy3e04c862024-12-28 13:46:29 +0300728 memmove(&tcp->lost.hill[i + 1],
729 &tcp->lost.hill[i + cnt_move + 1],
Mikhail Kshevetskiy78cb2da2024-12-28 13:46:28 +0300730 cnt_move * sizeof(struct sack_edges));
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800731
Mikhail Kshevetskiy78cb2da2024-12-28 13:46:28 +0300732 cnt -= cnt_move;
Mikhail Kshevetskiy3e04c862024-12-28 13:46:29 +0300733 tcp->lost.len = TCP_OPT_LEN_2 + cnt * TCP_OPT_LEN_8;
Mikhail Kshevetskiy78cb2da2024-12-28 13:46:28 +0300734 for (j = cnt; j < TCP_SACK_HILLS; j++) {
Mikhail Kshevetskiy3e04c862024-12-28 13:46:29 +0300735 tcp->lost.hill[j].l = TCP_O_NOP;
736 tcp->lost.hill[j].r = TCP_O_NOP;
Mikhail Kshevetskiy78cb2da2024-12-28 13:46:28 +0300737 }
738 }
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800739
Mikhail Kshevetskiyc8485442024-12-28 13:46:31 +0300740 tcp_update_rcv_nxt(tcp);
Mikhail Kshevetskiy78cb2da2024-12-28 13:46:28 +0300741 return;
742 }
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800743
Mikhail Kshevetskiy78cb2da2024-12-28 13:46:28 +0300744 if (i == TCP_SACK_HILLS) {
Mikhail Kshevetskiyc8485442024-12-28 13:46:31 +0300745 tcp_update_rcv_nxt(tcp);
Mikhail Kshevetskiy78cb2da2024-12-28 13:46:28 +0300746 return;
747 }
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800748
Mikhail Kshevetskiy78cb2da2024-12-28 13:46:28 +0300749 if (cnt < TCP_SACK_HILLS) {
750 cnt_move = cnt - i;
751 cnt++;
752 } else {
753 cnt = TCP_SACK_HILLS;
754 cnt_move = TCP_SACK_HILLS - i;
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800755 }
Mikhail Kshevetskiy78cb2da2024-12-28 13:46:28 +0300756
757 if (cnt_move > 0)
Mikhail Kshevetskiy3e04c862024-12-28 13:46:29 +0300758 memmove(&tcp->lost.hill[i + 1],
759 &tcp->lost.hill[i],
Mikhail Kshevetskiy78cb2da2024-12-28 13:46:28 +0300760 cnt_move * sizeof(struct sack_edges));
761
Mikhail Kshevetskiy3e04c862024-12-28 13:46:29 +0300762 tcp->lost.hill[i].l = tcp_seq_num;
763 tcp->lost.hill[i].r = tcp_seq_num + len;
764 tcp->lost.len = TCP_OPT_LEN_2 + cnt * TCP_OPT_LEN_8;
Mikhail Kshevetskiy78cb2da2024-12-28 13:46:28 +0300765
Mikhail Kshevetskiyc8485442024-12-28 13:46:31 +0300766 tcp_update_rcv_nxt(tcp);
Mikhail Kshevetskiy78cb2da2024-12-28 13:46:28 +0300767};
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800768
769/**
770 * tcp_parse_options() - parsing TCP options
Mikhail Kshevetskiy3e04c862024-12-28 13:46:29 +0300771 * @tcp: tcp stream
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800772 * @o: pointer to the option field.
773 * @o_len: length of the option field.
774 */
Mikhail Kshevetskiy3e04c862024-12-28 13:46:29 +0300775void tcp_parse_options(struct tcp_stream *tcp, uchar *o, int o_len)
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800776{
777 struct tcp_t_opt *tsopt;
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300778 struct tcp_scale *wsopt;
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800779 uchar *p = o;
780
781 /*
782 * NOPs are options with a zero length, and thus are special.
783 * All other options have length fields.
784 */
Mikhail Kshevetskiy932ac962024-12-28 13:46:27 +0300785 for (p = o; p < (o + o_len); ) {
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800786 if (!p[1])
787 return; /* Finished processing options */
788
789 switch (p[0]) {
790 case TCP_O_END:
791 return;
792 case TCP_O_MSS:
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800793 case TCP_P_SACK:
794 case TCP_V_SACK:
795 break;
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300796 case TCP_O_SCL:
797 wsopt = (struct tcp_scale *)p;
798 tcp->rmt_win_scale = wsopt->scale;
799 break;
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800800 case TCP_O_TS:
801 tsopt = (struct tcp_t_opt *)p;
Mikhail Kshevetskiy3e04c862024-12-28 13:46:29 +0300802 tcp->rmt_timestamp = tsopt->t_snd;
Mikhail Kshevetskiy932ac962024-12-28 13:46:27 +0300803 break;
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800804 }
805
806 /* Process optional NOPs */
807 if (p[0] == TCP_O_NOP)
808 p++;
Mikhail Kshevetskiy932ac962024-12-28 13:46:27 +0300809 else
810 p += p[1];
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800811 }
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300812}
813
814static int tcp_seg_in_wnd(struct tcp_stream *tcp,
815 u32 tcp_seq_num, int payload_len)
816{
817 if (!payload_len && !tcp->rcv_wnd) {
818 if (tcp_seq_num == tcp->rcv_nxt)
819 return 1;
820 }
821 if (!payload_len && tcp->rcv_wnd > 0) {
822 if (tcp_seq_cmp(tcp->rcv_nxt, tcp_seq_num) <= 0 &&
823 tcp_seq_cmp(tcp_seq_num, tcp->rcv_nxt + tcp->rcv_wnd) < 0)
824 return 1;
825 }
826 if (payload_len > 0 && tcp->rcv_wnd > 0) {
827 if (tcp_seq_cmp(tcp->rcv_nxt, tcp_seq_num) <= 0 &&
828 tcp_seq_cmp(tcp_seq_num, tcp->rcv_nxt + tcp->rcv_wnd) < 0)
829 return 1;
830 tcp_seq_num += payload_len - 1;
831 if (tcp_seq_cmp(tcp->rcv_nxt, tcp_seq_num) <= 0 &&
832 tcp_seq_cmp(tcp_seq_num, tcp->rcv_nxt + tcp->rcv_wnd) < 0)
833 return 1;
834 }
835
836 return 0;
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800837}
838
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300839static int tcp_rx_check_ack_num(struct tcp_stream *tcp, u32 tcp_seq_num,
840 u32 tcp_ack_num, u32 tcp_win_size)
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800841{
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300842 u32 old_offs, new_offs;
843 u8 action;
844
845 switch (tcp->state) {
846 case TCP_SYN_RECEIVED:
847 if (tcp_seq_cmp(tcp->snd_una, tcp_ack_num) >= 0 ||
848 tcp_seq_cmp(tcp_ack_num, tcp->snd_nxt) > 0) {
849 // segment acknowledgment is not acceptable
850 tcp_send_packet(tcp, TCP_RST, tcp_ack_num, 0, 0);
851 return TCP_PACKET_DROP;
852 }
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800853
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300854 tcp_stream_set_state(tcp, TCP_ESTABLISHED);
855 tcp->snd_wnd = tcp_win_size;
856 tcp->snd_wl1 = tcp_seq_num;
857 tcp->snd_wl2 = tcp_ack_num;
858
859 if (tcp->on_established)
860 tcp->on_established(tcp);
861
862 fallthrough;
863
864 case TCP_ESTABLISHED:
865 case TCP_FIN_WAIT_1:
866 case TCP_FIN_WAIT_2:
867 case TCP_CLOSE_WAIT:
868 case TCP_CLOSING:
869 if (tcp_seq_cmp(tcp_ack_num, tcp->snd_nxt) > 0) {
870 // ACK acks something not yet sent
871 action = tcp_stream_fin_needed(tcp, tcp->snd_una) | TCP_ACK;
872 tcp_send_packet(tcp, action, tcp->snd_una, tcp->rcv_nxt, 0);
873 return TCP_PACKET_DROP;
874 }
875
876 if (tcp_seq_cmp(tcp->snd_una, tcp_ack_num) < 0) {
877 old_offs = tcp_stream_tx_offs(tcp);
878 tcp->snd_una = tcp_ack_num;
879 new_offs = tcp_stream_tx_offs(tcp);
880 if (tcp->time_handler &&
881 tcp_seq_cmp(tcp->snd_una, tcp->retry_seq_num) > 0) {
882 tcp_stream_set_time_handler(tcp, 0, NULL);
883 }
884 if (tcp->on_snd_una_update &&
885 old_offs != new_offs)
886 tcp->on_snd_una_update(tcp, new_offs);
887 }
888
889 if (tcp_seq_cmp(tcp->snd_una, tcp_ack_num) <= 0) {
890 if (tcp_seq_cmp(tcp->snd_wl1, tcp_seq_num) < 0 ||
891 (tcp->snd_wl1 == tcp_seq_num &&
892 tcp_seq_cmp(tcp->snd_wl2, tcp_seq_num) <= 0)) {
893 tcp->snd_wnd = tcp_win_size;
894 tcp->snd_wl1 = tcp_seq_num;
895 tcp->snd_wl2 = tcp_ack_num;
896 }
897 }
898
899 if (tcp->state == TCP_FIN_WAIT_1) {
900 if (tcp->snd_una == tcp->snd_nxt)
901 tcp_stream_set_state(tcp, TCP_FIN_WAIT_2);
902 }
903
904 if (tcp->state == TCP_CLOSING) {
905 if (tcp->snd_una == tcp->snd_nxt)
906 tcp_stream_set_state(tcp, TCP_CLOSED);
907 }
908 return TCP_PACKET_OK;
909
910 case TCP_LAST_ACK:
911 if (tcp_ack_num == tcp->snd_nxt)
912 tcp_stream_set_state(tcp, TCP_CLOSED);
913 return TCP_PACKET_OK;
914
915 default:
916 return TCP_PACKET_DROP;
917 }
918}
919
920static int tcp_rx_user_data(struct tcp_stream *tcp, u32 tcp_seq_num,
921 char *buf, int len)
922{
923 int tmp_len;
924 u32 buf_offs, old_offs, new_offs;
925 u8 action;
926
927 if (!len)
928 return TCP_PACKET_OK;
929
930 switch (tcp->state) {
931 case TCP_ESTABLISHED:
932 case TCP_FIN_WAIT_1:
933 case TCP_FIN_WAIT_2:
934 break;
935 default:
936 return TCP_PACKET_DROP;
937 }
938
939 tmp_len = len;
940 old_offs = tcp_stream_rx_offs(tcp);
941 buf_offs = tcp_seq_num - tcp->irs - 1;
942 if (tcp->rx) {
943 tmp_len = tcp->rx(tcp, buf_offs, buf, len);
944 if (tmp_len < 0) {
945 puts("\nTCP: receive failure\n");
946 tcp_send_packet(tcp, TCP_RST, tcp->snd_una,
947 tcp->rcv_nxt, 0);
948 tcp_stream_set_status(tcp, TCP_ERR_IO);
949 tcp_stream_set_state(tcp, TCP_CLOSED);
950 tcp_stream_destroy(tcp);
951 return TCP_PACKET_DROP;
952 }
953 }
954 if (tmp_len)
955 tcp_hole(tcp, tcp_seq_num, tmp_len);
956
957 new_offs = tcp_stream_rx_offs(tcp);
958 if (tcp->on_rcv_nxt_update && old_offs != new_offs)
959 tcp->on_rcv_nxt_update(tcp, new_offs);
960
961 action = tcp_stream_fin_needed(tcp, tcp->snd_una) | TCP_ACK;
962 tcp_send_packet(tcp, action, tcp->snd_una, tcp->rcv_nxt, 0);
963
964 return TCP_PACKET_OK;
965}
966
967void tcp_rx_state_machine(struct tcp_stream *tcp,
968 union tcp_build_pkt *b, unsigned int pkt_len)
969{
970 int tcp_len = pkt_len - IP_HDR_SIZE;
971 u32 tcp_seq_num, tcp_ack_num, tcp_win_size;
972 int tcp_hdr_len, payload_len;
973 u8 tcp_flags, action;
974
975 tcp_hdr_len = GET_TCP_HDR_LEN_IN_BYTES(b->ip.hdr.tcp_hlen);
976 payload_len = tcp_len - tcp_hdr_len;
977
978 if (tcp_hdr_len > TCP_HDR_SIZE)
979 tcp_parse_options(tcp, (uchar *)b + IP_TCP_HDR_SIZE,
980 tcp_hdr_len - TCP_HDR_SIZE);
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800981 /*
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300982 * Incoming sequence and ack numbers are server's view of the numbers.
983 * The app must swap the numbers when responding.
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800984 */
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300985 tcp_seq_num = ntohl(b->ip.hdr.tcp_seq);
986 tcp_ack_num = ntohl(b->ip.hdr.tcp_ack);
987 tcp_win_size = ntohs(b->ip.hdr.tcp_win) << tcp->rmt_win_scale;
988
989 tcp_flags = b->ip.hdr.tcp_flags;
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800990
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300991// printf("pkt: seq=%d, ack=%d, flags=%x, len=%d\n",
992// tcp_seq_num - tcp->irs, tcp_ack_num - tcp->iss, tcp_flags, pkt_len);
993// printf("tcp: rcv_nxt=%d, snd_una=%d, snd_nxt=%d\n\n",
994// tcp->rcv_nxt - tcp->irs, tcp->snd_una - tcp->iss, tcp->snd_nxt - tcp->iss);
995
996 switch (tcp->state) {
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +0800997 case TCP_CLOSED:
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300998 if (tcp_flags & TCP_RST)
999 return;
1000
1001 if (tcp_flags & TCP_ACK) {
1002 tcp_send_packet(tcp, TCP_RST, tcp_ack_num, 0, 0);
1003 return;
Dmitrii Merkurev3acd0542023-04-12 19:49:29 +01001004 }
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +03001005
1006 if (!(tcp_flags & TCP_SYN))
1007 return;
1008
1009 tcp->irs = tcp_seq_num;
1010 tcp->rcv_nxt = tcp->irs + 1;
1011
1012 tcp->iss = tcp_get_start_seq();
1013 tcp->snd_una = tcp->iss;
1014 tcp->snd_nxt = tcp->iss + 1;
1015 tcp->snd_wnd = tcp_win_size;
1016
1017 tcp_stream_restart_rx_timer(tcp);
1018
1019 tcp_stream_set_state(tcp, TCP_SYN_RECEIVED);
1020 tcp_send_packet_with_retry(tcp, TCP_SYN | TCP_ACK,
1021 tcp->iss, 0, 0);
1022 return;
1023
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +08001024 case TCP_SYN_SENT:
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +03001025 if (!(tcp_flags & TCP_ACK))
1026 return;
Dmitrii Merkurev3acd0542023-04-12 19:49:29 +01001027
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +03001028 if (tcp_seq_cmp(tcp_ack_num, tcp->iss) <= 0 ||
1029 tcp_seq_cmp(tcp_ack_num, tcp->snd_nxt) > 0) {
1030 if (!(tcp_flags & TCP_RST))
1031 tcp_send_packet(tcp, TCP_RST, tcp_ack_num, 0, 0);
1032 return;
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +08001033 }
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +03001034
1035 if (tcp_flags & TCP_RST) {
1036 tcp_stream_set_status(tcp, TCP_ERR_RST);
1037 tcp_stream_set_state(tcp, TCP_CLOSED);
1038 return;
1039 }
1040
1041 if (!(tcp_flags & TCP_SYN))
1042 return;
1043
1044 /* stop retransmit of SYN */
1045 tcp_stream_set_time_handler(tcp, 0, NULL);
1046
1047 tcp->irs = tcp_seq_num;
1048 tcp->rcv_nxt = tcp->irs + 1;
1049 tcp->snd_una = tcp_ack_num;
1050
1051 tcp_stream_restart_rx_timer(tcp);
1052
1053 /* our SYN has been ACKed */
1054 tcp_stream_set_state(tcp, TCP_ESTABLISHED);
1055
1056 if (tcp->on_established)
1057 tcp->on_established(tcp);
1058
1059 action = tcp_stream_fin_needed(tcp, tcp->snd_una) | TCP_ACK;
1060 tcp_send_packet(tcp, action, tcp->snd_una, tcp->rcv_nxt, 0);
1061 tcp_rx_user_data(tcp, tcp_seq_num,
1062 ((char *)b) + pkt_len - payload_len,
1063 payload_len);
1064 return;
1065
1066 case TCP_SYN_RECEIVED:
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +08001067 case TCP_ESTABLISHED:
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +03001068 case TCP_FIN_WAIT_1:
1069 case TCP_FIN_WAIT_2:
1070 case TCP_CLOSE_WAIT:
1071 case TCP_CLOSING:
1072 case TCP_LAST_ACK:
1073 if (!tcp_seg_in_wnd(tcp, tcp_seq_num, payload_len)) {
1074 if (tcp_flags & TCP_RST)
1075 return;
1076 action = tcp_stream_fin_needed(tcp, tcp->snd_una) | TCP_ACK;
1077 tcp_send_packet(tcp, action, tcp->snd_una, tcp->rcv_nxt, 0);
1078 return;
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +08001079 }
1080
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +03001081 tcp_stream_restart_rx_timer(tcp);
1082
1083 if (tcp_flags & TCP_RST) {
1084 tcp_stream_set_status(tcp, TCP_ERR_RST);
1085 tcp_stream_set_state(tcp, TCP_CLOSED);
1086 return;
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +08001087 }
1088
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +03001089 if (tcp_flags & TCP_SYN) {
1090 tcp_send_packet(tcp, TCP_RST, tcp_ack_num, 0, 0);
1091 tcp_stream_set_status(tcp, TCP_ERR_RST);
1092 tcp_stream_set_state(tcp, TCP_CLOSED);
1093 return;
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +08001094 }
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +03001095
1096 if (!(tcp_flags & TCP_ACK))
1097 return;
1098
1099 if (tcp_rx_check_ack_num(tcp, tcp_seq_num, tcp_ack_num,
1100 tcp_win_size) == TCP_PACKET_DROP) {
1101 return;
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +08001102 }
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +03001103
1104 if (tcp_rx_user_data(tcp, tcp_seq_num,
1105 ((char *)b) + pkt_len - payload_len,
1106 payload_len) == TCP_PACKET_DROP) {
1107 return;
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +08001108 }
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +03001109
1110 if (tcp_flags & TCP_FIN) {
1111 tcp->fin_rx = 1;
1112 tcp->fin_rx_seq = tcp_seq_num + payload_len + 1;
1113 tcp_hole(tcp, tcp_seq_num + payload_len, 1);
1114 action = tcp_stream_fin_needed(tcp, tcp->snd_una) | TCP_ACK;
1115 tcp_send_packet(tcp, action, tcp->snd_una, tcp->rcv_nxt, 0);
1116 }
1117
1118 if (tcp->fin_rx &&
1119 tcp->fin_rx_seq == tcp->rcv_nxt) {
1120 /* all rx data were processed */
1121 switch (tcp->state) {
1122 case TCP_ESTABLISHED:
1123 tcp_stream_set_state(tcp, TCP_LAST_ACK);
1124 tcp_send_packet_with_retry(tcp, TCP_ACK | TCP_FIN,
1125 tcp->snd_nxt, 0, 0);
1126 tcp->snd_nxt++;
1127 break;
1128
1129 case TCP_FIN_WAIT_1:
1130 if (tcp_ack_num == tcp->snd_nxt)
1131 tcp_stream_set_state(tcp, TCP_CLOSED);
1132 else
1133 tcp_stream_set_state(tcp, TCP_CLOSING);
1134 break;
1135
1136 case TCP_FIN_WAIT_2:
1137 tcp_stream_set_state(tcp, TCP_CLOSED);
1138 break;
1139
1140 default:
1141 break;
1142 }
1143 }
1144
1145 if (tcp->state == TCP_FIN_WAIT_1 &&
1146 tcp_stream_fin_needed(tcp, tcp->snd_una)) {
1147 /* all tx data were acknowledged */
1148 tcp_send_packet_with_retry(tcp, TCP_ACK | TCP_FIN,
1149 tcp->snd_una, 0, 0);
1150 }
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +08001151 }
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +08001152}
1153
1154/**
1155 * rxhand_tcp_f() - process receiving data and call data handler.
1156 * @b: the packet
1157 * @pkt_len: the length of packet.
1158 */
1159void rxhand_tcp_f(union tcp_build_pkt *b, unsigned int pkt_len)
1160{
1161 int tcp_len = pkt_len - IP_HDR_SIZE;
1162 u16 tcp_rx_xsum = b->ip.hdr.ip_sum;
Mikhail Kshevetskiy3e04c862024-12-28 13:46:29 +03001163 struct tcp_stream *tcp;
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +03001164 struct in_addr src;
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +08001165
1166 /* Verify IP header */
1167 debug_cond(DEBUG_DEV_PKT,
1168 "TCP RX in RX Sum (to=%pI4, from=%pI4, len=%d)\n",
1169 &b->ip.hdr.ip_src, &b->ip.hdr.ip_dst, pkt_len);
1170
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +03001171 /*
1172 * src IP address will be destroyed by TCP checksum verification
1173 * algorithm (see tcp_set_pseudo_header()), so remember it before
1174 * it was garbaged.
1175 */
1176 src.s_addr = b->ip.hdr.ip_src.s_addr;
1177
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +08001178 b->ip.hdr.ip_dst = net_ip;
1179 b->ip.hdr.ip_sum = 0;
1180 if (tcp_rx_xsum != compute_ip_checksum(b, IP_HDR_SIZE)) {
1181 debug_cond(DEBUG_DEV_PKT,
1182 "TCP RX IP xSum Error (%pI4, =%pI4, len=%d)\n",
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +03001183 &net_ip, &src, pkt_len);
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +08001184 return;
1185 }
1186
1187 /* Build pseudo header and verify TCP header */
1188 tcp_rx_xsum = b->ip.hdr.tcp_xsum;
1189 b->ip.hdr.tcp_xsum = 0;
1190 if (tcp_rx_xsum != tcp_set_pseudo_header((uchar *)b, b->ip.hdr.ip_src,
1191 b->ip.hdr.ip_dst, tcp_len,
1192 pkt_len)) {
1193 debug_cond(DEBUG_DEV_PKT,
1194 "TCP RX TCP xSum Error (%pI4, %pI4, len=%d)\n",
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +03001195 &net_ip, &src, tcp_len);
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +08001196 return;
1197 }
1198
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +03001199 tcp = tcp_stream_get(b->ip.hdr.tcp_flags & TCP_SYN,
1200 src,
1201 ntohs(b->ip.hdr.tcp_src),
1202 ntohs(b->ip.hdr.tcp_dst));
Mikhail Kshevetskiy3e04c862024-12-28 13:46:29 +03001203 if (!tcp)
1204 return;
1205
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +03001206 tcp->rx_packets++;
1207 tcp_rx_state_machine(tcp, b, pkt_len);
1208 tcp_stream_put(tcp);
1209}
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +08001210
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +03001211struct tcp_stream *tcp_stream_connect(struct in_addr rhost, u16 rport)
1212{
1213 struct tcp_stream *tcp;
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +08001214
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +03001215 tcp = tcp_stream_add(rhost, rport, random_port());
1216 if (!tcp)
1217 return NULL;
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +08001218
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +03001219 tcp->iss = tcp_get_start_seq();
1220 tcp->snd_una = tcp->iss;
1221 tcp->snd_nxt = tcp->iss + 1;
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +08001222
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +03001223 tcp_stream_set_state(tcp, TCP_SYN_SENT);
1224 tcp_send_packet_with_retry(tcp, TCP_SYN, tcp->snd_una, 0, 0);
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +08001225
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +03001226 return tcp;
1227}
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +08001228
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +03001229void tcp_stream_reset(struct tcp_stream *tcp)
1230{
1231 if (tcp->state == TCP_CLOSED)
1232 return;
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +08001233
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +03001234 tcp_stream_set_time_handler(tcp, 0, NULL);
1235 tcp_send_packet(tcp, TCP_RST, tcp->snd_una, 0, 0);
1236 tcp_stream_set_status(tcp, TCP_ERR_RST);
1237 tcp_stream_set_state(tcp, TCP_CLOSED);
Ying-Chun Liu (PaulLiu)41efed12022-11-08 14:17:28 +08001238}
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +03001239
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +03001240void tcp_stream_close(struct tcp_stream *tcp)
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +03001241{
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +03001242 switch (tcp->state) {
1243 case TCP_SYN_SENT:
1244 tcp_stream_reset(tcp);
1245 break;
1246 case TCP_SYN_RECEIVED:
1247 case TCP_ESTABLISHED:
1248 tcp->fin_tx = 1;
1249 tcp->fin_tx_seq = tcp->snd_nxt;
1250 if (tcp_stream_fin_needed(tcp, tcp->snd_una)) {
1251 /* all tx data were acknowledged */
1252 tcp_send_packet_with_retry(tcp, TCP_ACK | TCP_FIN,
1253 tcp->snd_una, 0, 0);
1254 }
1255 tcp_stream_set_state(tcp, TCP_FIN_WAIT_1);
1256 tcp->snd_nxt++;
1257 break;
1258 default:
1259 break;
1260 }
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +03001261}