[PATCH v13 00/10] net: tcp: improve tcp support in legacy stack

Legacy TCP stack is bad. Here are some of the known issues: * tcp packet from other connection can break a current one * tcp send sequence always starts from zero * bad tcp options processing * strange assumptions on packet size for selective acknowledge * tcp interface assumes one of the two scenarios: - data downloading from remote host to a board - request-response exchange with a small packets so it's not possible to upload large amount of data from the board to remote host. * wget test generate bad tcp stream, test should fail but it passes instead
This series of patches fixes all of the above issues.
The benefits: * A lot of bug was fixed * Better and more reliable TCP state machine * Tcp clients becomes smaller/simpler * Data uploading was fixed (now it's possible to transmit a huge amount of data from the board to remote host)
Modification was verified with * firmware downloading via u-boot wget command * fastboot over tcp * netcat linux client * Firefox/Chrome/Edge using example web-server implementation
Changes v2: * cover letter was added * some patches were removed
Changes v3: * better cover letter
Changes v4: * fix bug in debug output * add more comments * code style fixes
Changes v5: * old patches were occasionally sent with v4 * add back web-server patches * fix bug in debug output * add more comments * code style fixes
Changes v6: * fix the wget test * improve description of "simplify tcp header filling code" patch
Changes v7: * fix include ordering * improve option descriptions * fix a lot of extra brackets and comparisons against NULL / 0 * add empty lines before final returns * fixed a bug with zero size httpd uploads
Changes v8: * [tcp] add function to restart rx inactivity timeout. This may help in the cases where remote peer waits for u-boot data, but u-boot is busy with long executing action * [httpd] add function returning underlying tcp stream from request id. This (with the above change) allows avoid connection break for the long processing post requests.
Changes v9: * rebase all changes on top of origin/next * update test/cmd/wget patch description (bad tcp ack/seq issue was already fixed in origin/next) * tcp: add rx/tx packet counters to a tcp_stream structure * wget: use tcp rx packet counter instead of own one * netcat: use tcp rx/tx packet counters instead of own one
Changes v10: * netcat: fix bug appeared after rx/tx packet counter addition * rebased on top of v2024.10
Changes v11: * fix spelling in cover letter
Changes v12: * rebase on top of master branch (3df6145db0e) * slightly change tcp->rx() & tcp->tx() callback declarations, this allow us to terminate a connection if something goes wrong in rx/tx paths. * set net_boot_file_size variable in httpd_upload * set filesize env variable to a proper value netcat and httpd_upload
Changes v13: * rebase on top of master branch (8573ea41058) * remove netcat support * remove example httpd server implementation
Mikhail Kshevetskiy (10): net/tcp: fix TCP options processing net/tcp: fix selective acknowledge net/tcp: put connection specific data into a tcp_stream structure net/tcp: add connection info to tcp_stream structure net/tcp: rename ack_edge and seq_init to more common rcv_nxt and irs net/tcp: improve tcp framework, use better state machine test/cmd/wget: fix the test net/tcp: simplify tcp header filling code net/tcp: define a fallback value for rcv_wnd size net/net: fix include ordering
arch/sandbox/include/asm/eth.h | 4 + include/net-legacy.h | 5 +- include/net/tcp.h | 258 ++++++- include/net/wget.h | 8 - net/fastboot_tcp.c | 193 ++--- net/net.c | 45 +- net/tcp.c | 1297 ++++++++++++++++++++++---------- net/wget.c | 465 ++++-------- test/cmd/wget.c | 58 +- 9 files changed, 1444 insertions(+), 889 deletions(-)

Current TCP code may miss an option if TCP_O_NOP option was used before it for proper aligning.
Signed-off-by: Mikhail Kshevetskiy mikhail.kshevetskiy@iopsys.eu Reviewed-by: Simon Glass sjg@chromium.org --- net/tcp.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/net/tcp.c b/net/tcp.c index b0cc8a1fe3e..3e3118de450 100644 --- a/net/tcp.c +++ b/net/tcp.c @@ -475,7 +475,7 @@ void tcp_parse_options(uchar *o, int o_len) * NOPs are options with a zero length, and thus are special. * All other options have length fields. */ - for (p = o; p < (o + o_len); p = p + p[1]) { + for (p = o; p < (o + o_len); ) { if (!p[1]) return; /* Finished processing options */
@@ -490,12 +490,14 @@ void tcp_parse_options(uchar *o, int o_len) case TCP_O_TS: tsopt = (struct tcp_t_opt *)p; rmt_timestamp = tsopt->t_snd; - return; + break; }
/* Process optional NOPs */ if (p[0] == TCP_O_NOP) p++; + else + p += p[1]; } }

Current code assume that all (except last) packets are of the same size. This is definitely wrong. Replace SACK code with a new one, that does not rely on this assumption. Also this code uses less memory.
Signed-off-by: Mikhail Kshevetskiy mikhail.kshevetskiy@iopsys.eu Reviewed-by: Simon Glass sjg@chromium.org --- net/tcp.c | 200 +++++++++++++++++++++++------------------------------- 1 file changed, 86 insertions(+), 114 deletions(-)
diff --git a/net/tcp.c b/net/tcp.c index 3e3118de450..724536cb352 100644 --- a/net/tcp.c +++ b/net/tcp.c @@ -38,21 +38,6 @@ static u32 tcp_ack_edge;
static int tcp_activity_count;
-/* - * Search for TCP_SACK and review the comments before the code section - * TCP_SACK is the number of packets at the front of the stream - */ - -enum pkt_state {PKT, NOPKT}; -struct sack_r { - struct sack_edges se; - enum pkt_state st; -}; - -static struct sack_r edge_a[TCP_SACK]; -static unsigned int sack_idx; -static unsigned int prev_len; - /* * TCP lengths are stored as a rounded up number of 32 bit words. * Add 3 to length round up, rounded, then divided into the @@ -69,6 +54,11 @@ static enum tcp_state current_tcp_state; /* Current TCP RX packet handler */ static rxhand_tcp *tcp_packet_handler;
+static inline s32 tcp_seq_cmp(u32 a, u32 b) +{ + return (s32)(a - b); +} + /** * tcp_get_tcp_state() - get current TCP state * @@ -267,6 +257,7 @@ int tcp_set_tcp_header(uchar *pkt, int dport, int sport, int payload_len, action = TCP_FIN; current_tcp_state = TCP_FIN_WAIT_1; } else { + tcp_lost.len = TCP_OPT_LEN_2; current_tcp_state = TCP_SYN_SENT; } break; @@ -353,6 +344,20 @@ int tcp_set_tcp_header(uchar *pkt, int dport, int sport, int payload_len, return pkt_hdr_len; }
+static void tcp_update_ack_edge(void) +{ + if (tcp_seq_cmp(tcp_ack_edge, tcp_lost.hill[0].l) >= 0) { + tcp_ack_edge = tcp_lost.hill[0].r; + + memmove(&tcp_lost.hill[0], &tcp_lost.hill[1], + (TCP_SACK_HILLS - 1) * sizeof(struct sack_edges)); + + tcp_lost.len -= TCP_OPT_LEN_8; + tcp_lost.hill[TCP_SACK_HILLS - 1].l = TCP_O_NOP; + tcp_lost.hill[TCP_SACK_HILLS - 1].r = TCP_O_NOP; + } +} + /** * tcp_hole() - Selective Acknowledgment (Essential for fast stream transfer) * @tcp_seq_num: TCP sequence start number @@ -360,106 +365,79 @@ int tcp_set_tcp_header(uchar *pkt, int dport, int sport, int payload_len, */ void tcp_hole(u32 tcp_seq_num, u32 len) { - u32 idx_sack, sack_in; - u32 sack_end = TCP_SACK - 1; - u32 hill = 0; - enum pkt_state expect = PKT; - u32 seq = tcp_seq_num - tcp_seq_init; - u32 hol_l = tcp_ack_edge - tcp_seq_init; - u32 hol_r = 0; - - /* Place new seq number in correct place in receive array */ - if (prev_len == 0) - prev_len = len; - - idx_sack = sack_idx + ((tcp_seq_num - tcp_ack_edge) / prev_len); - if (idx_sack < TCP_SACK) { - edge_a[idx_sack].se.l = tcp_seq_num; - edge_a[idx_sack].se.r = tcp_seq_num + len; - edge_a[idx_sack].st = PKT; + int i, j, cnt, cnt_move;
- /* - * The fin (last) packet is not the same length as data - * packets, and if it's length is recorded and used for - * array index calculation, calculation breaks. - */ - if (prev_len < len) - prev_len = len; - } + cnt = (tcp_lost.len - TCP_OPT_LEN_2) / TCP_OPT_LEN_8; + for (i = 0; i < cnt; i++) { + if (tcp_seq_cmp(tcp_lost.hill[i].r, tcp_seq_num) < 0) + continue; + if (tcp_seq_cmp(tcp_lost.hill[i].l, tcp_seq_num + len) > 0) + break;
- debug_cond(DEBUG_DEV_PKT, - "TCP 1 seq %d, edg %d, len %d, sack_idx %d, sack_end %d\n", - seq, hol_l, len, sack_idx, sack_end); + if (tcp_seq_cmp(tcp_lost.hill[i].l, tcp_seq_num) > 0) + tcp_lost.hill[i].l = tcp_seq_num; + if (tcp_seq_cmp(tcp_lost.hill[i].l, tcp_seq_num) < 0) { + len += tcp_seq_num - tcp_lost.hill[i].l; + tcp_seq_num = tcp_lost.hill[i].l; + } + if (tcp_seq_cmp(tcp_lost.hill[i].r, tcp_seq_num + len) >= 0) { + tcp_update_ack_edge(); + return; + }
- /* Right edge of contiguous stream, is the left edge of first hill */ - hol_l = tcp_seq_num - tcp_seq_init; - hol_r = hol_l + len; + /* check overlapping with next hills */ + cnt_move = 0; + tcp_lost.hill[i].r = tcp_seq_num + len; + for (j = i + 1; j < cnt; j++) { + if (tcp_seq_cmp(tcp_lost.hill[j].l, tcp_lost.hill[i].r) > 0) + break;
- if (IS_ENABLED(CONFIG_PROT_TCP_SACK)) - tcp_lost.len = TCP_OPT_LEN_2; + tcp_lost.hill[i].r = tcp_lost.hill[j].r; + cnt_move++; + }
- debug_cond(DEBUG_DEV_PKT, - "TCP 1 in %d, seq %d, pkt_l %d, pkt_r %d, sack_idx %d, sack_end %d\n", - idx_sack, seq, hol_l, hol_r, sack_idx, sack_end); - - for (sack_in = sack_idx; sack_in < sack_end && hill < TCP_SACK_HILLS; - sack_in++) { - switch (expect) { - case NOPKT: - switch (edge_a[sack_in].st) { - case NOPKT: - debug_cond(DEBUG_INT_STATE, "N"); - break; - case PKT: - debug_cond(DEBUG_INT_STATE, "n"); - if (IS_ENABLED(CONFIG_PROT_TCP_SACK)) { - tcp_lost.hill[hill].l = - edge_a[sack_in].se.l; - tcp_lost.hill[hill].r = - edge_a[sack_in].se.r; - } - expect = PKT; - break; - } - break; - case PKT: - switch (edge_a[sack_in].st) { - case NOPKT: - debug_cond(DEBUG_INT_STATE, "p"); - if (sack_in > sack_idx && - hill < TCP_SACK_HILLS) { - hill++; - if (IS_ENABLED(CONFIG_PROT_TCP_SACK)) - tcp_lost.len += TCP_OPT_LEN_8; - } - expect = NOPKT; - break; - case PKT: - debug_cond(DEBUG_INT_STATE, "P"); - - if (tcp_ack_edge == edge_a[sack_in].se.l) { - tcp_ack_edge = edge_a[sack_in].se.r; - edge_a[sack_in].st = NOPKT; - sack_idx++; - } else { - if (IS_ENABLED(CONFIG_PROT_TCP_SACK) && - hill < TCP_SACK_HILLS) - tcp_lost.hill[hill].r = - edge_a[sack_in].se.r; - if (IS_ENABLED(CONFIG_PROT_TCP_SACK) && - sack_in == sack_end - 1) - tcp_lost.hill[hill].r = - edge_a[sack_in].se.r; - } - break; + if (cnt_move > 0) { + if (cnt > i + cnt_move + 1) + memmove(&tcp_lost.hill[i + 1], + &tcp_lost.hill[i + cnt_move + 1], + cnt_move * sizeof(struct sack_edges)); + + cnt -= cnt_move; + tcp_lost.len = TCP_OPT_LEN_2 + cnt * TCP_OPT_LEN_8; + for (j = cnt; j < TCP_SACK_HILLS; j++) { + tcp_lost.hill[j].l = TCP_O_NOP; + tcp_lost.hill[j].r = TCP_O_NOP; } - break; } + + tcp_update_ack_edge(); + return; } - debug_cond(DEBUG_INT_STATE, "\n"); - if (!IS_ENABLED(CONFIG_PROT_TCP_SACK) || tcp_lost.len <= TCP_OPT_LEN_2) - sack_idx = 0; -} + + if (i == TCP_SACK_HILLS) { + tcp_update_ack_edge(); + return; + } + + if (cnt < TCP_SACK_HILLS) { + cnt_move = cnt - i; + cnt++; + } else { + cnt = TCP_SACK_HILLS; + cnt_move = TCP_SACK_HILLS - i; + } + + if (cnt_move > 0) + memmove(&tcp_lost.hill[i + 1], + &tcp_lost.hill[i], + cnt_move * sizeof(struct sack_edges)); + + tcp_lost.hill[i].l = tcp_seq_num; + tcp_lost.hill[i].r = tcp_seq_num + len; + tcp_lost.len = TCP_OPT_LEN_2 + cnt * TCP_OPT_LEN_8; + + tcp_update_ack_edge(); +};
/** * tcp_parse_options() - parsing TCP options @@ -509,7 +487,6 @@ static u8 tcp_state_machine(u8 tcp_flags, u32 tcp_seq_num, int payload_len) u8 tcp_push = tcp_flags & TCP_PUSH; u8 tcp_ack = tcp_flags & TCP_ACK; u8 action = TCP_DATA; - int i;
/* * tcp_flags are examined to determine TX action in a given state @@ -536,6 +513,7 @@ static u8 tcp_state_machine(u8 tcp_flags, u32 tcp_seq_num, int payload_len) action = TCP_SYN | TCP_ACK; tcp_seq_init = tcp_seq_num; tcp_ack_edge = tcp_seq_num + 1; + tcp_lost.len = TCP_OPT_LEN_2; current_tcp_state = TCP_SYN_RECEIVED; } else if (tcp_ack || tcp_fin) { action = TCP_DATA; @@ -552,13 +530,7 @@ static u8 tcp_state_machine(u8 tcp_flags, u32 tcp_seq_num, int payload_len) action |= TCP_ACK; tcp_seq_init = tcp_seq_num; tcp_ack_edge = tcp_seq_num + 1; - sack_idx = 0; - edge_a[sack_idx].se.l = tcp_ack_edge; - edge_a[sack_idx].se.r = tcp_ack_edge; - prev_len = 0; current_tcp_state = TCP_ESTABLISHED; - for (i = 0; i < TCP_SACK; i++) - edge_a[i].st = NOPKT;
if (tcp_syn && tcp_ack) action |= TCP_PUSH;

no functional changes
Signed-off-by: Mikhail Kshevetskiy mikhail.kshevetskiy@iopsys.eu Reviewed-by: Simon Glass sjg@chromium.org --- include/net/tcp.h | 37 +++++++- net/net.c | 11 ++- net/tcp.c | 231 +++++++++++++++++++++++----------------------- net/wget.c | 3 +- 4 files changed, 163 insertions(+), 119 deletions(-)
diff --git a/include/net/tcp.h b/include/net/tcp.h index c29d4ce24a7..14aee64cb1c 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -277,9 +277,40 @@ enum tcp_state { TCP_FIN_WAIT_2 };
-enum tcp_state tcp_get_tcp_state(void); -void tcp_set_tcp_state(enum tcp_state new_state); -int tcp_set_tcp_header(uchar *pkt, int dport, int sport, int payload_len, +/** + * struct tcp_stream - TCP data stream structure + * + * @state: TCP connection state + * + * @seq_init: Initial receive sequence number + * @ack_edge: Receive next + * + * @loc_timestamp: Local timestamp + * @rmt_timestamp: Remote timestamp + * + * @lost: Used for SACK + */ +struct tcp_stream { + /* TCP connection state */ + enum tcp_state state; + + u32 seq_init; + u32 ack_edge; + + /* TCP option timestamp */ + u32 loc_timestamp; + u32 rmt_timestamp; + + /* TCP sliding window control used to request re-TX */ + struct tcp_sack_v lost; +}; + +struct tcp_stream *tcp_stream_get(void); + +enum tcp_state tcp_get_tcp_state(struct tcp_stream *tcp); +void tcp_set_tcp_state(struct tcp_stream *tcp, enum tcp_state new_state); +int tcp_set_tcp_header(struct tcp_stream *tcp, uchar *pkt, int dport, + int sport, int payload_len, u8 action, u32 tcp_seq_num, u32 tcp_ack_num);
/** diff --git a/net/net.c b/net/net.c index f47e9fbe33a..d9e143f0ecc 100644 --- a/net/net.c +++ b/net/net.c @@ -420,7 +420,7 @@ int net_init(void) /* Only need to setup buffer pointers once. */ first_call = 0; if (IS_ENABLED(CONFIG_PROT_TCP)) - tcp_set_tcp_state(TCP_CLOSED); + tcp_set_tcp_state(tcp_stream_get(), TCP_CLOSED); }
return net_init_loop(); @@ -924,6 +924,9 @@ int net_send_ip_packet(uchar *ether, struct in_addr dest, int dport, int sport, uchar *pkt; int eth_hdr_size; int pkt_hdr_size; +#if defined(CONFIG_PROT_TCP) + struct tcp_stream *tcp; +#endif
/* make sure the net_tx_packet is initialized (net_init() was called) */ assert(net_tx_packet != NULL); @@ -950,8 +953,12 @@ int net_send_ip_packet(uchar *ether, struct in_addr dest, int dport, int sport, break; #if defined(CONFIG_PROT_TCP) case IPPROTO_TCP: + tcp = tcp_stream_get(); + if (tcp == NULL) + return -EINVAL; + pkt_hdr_size = eth_hdr_size - + tcp_set_tcp_header(pkt + eth_hdr_size, dport, sport, + + tcp_set_tcp_header(tcp, pkt + eth_hdr_size, dport, sport, payload_len, action, tcp_seq_num, tcp_ack_num); break; diff --git a/net/tcp.c b/net/tcp.c index 724536cb352..6646f171b83 100644 --- a/net/tcp.c +++ b/net/tcp.c @@ -24,19 +24,8 @@ #include <net.h> #include <net/tcp.h>
-/* - * TCP sliding window control used by us to request re-TX - */ -static struct tcp_sack_v tcp_lost; - -/* TCP option timestamp */ -static u32 loc_timestamp; -static u32 rmt_timestamp; - -static u32 tcp_seq_init; -static u32 tcp_ack_edge; - static int tcp_activity_count; +static struct tcp_stream tcp_stream;
/* * TCP lengths are stored as a rounded up number of 32 bit words. @@ -48,9 +37,6 @@ static int tcp_activity_count; #define SHIFT_TO_TCPHDRLEN_FIELD(x) ((x) << 4) #define GET_TCP_HDR_LEN_IN_BYTES(x) ((x) >> 2)
-/* TCP connection state */ -static enum tcp_state current_tcp_state; - /* Current TCP RX packet handler */ static rxhand_tcp *tcp_packet_handler;
@@ -60,22 +46,30 @@ static inline s32 tcp_seq_cmp(u32 a, u32 b) }
/** - * tcp_get_tcp_state() - get current TCP state + * tcp_get_tcp_state() - get TCP stream state + * @tcp: tcp stream * - * Return: Current TCP state + * Return: TCP stream state */ -enum tcp_state tcp_get_tcp_state(void) +enum tcp_state tcp_get_tcp_state(struct tcp_stream *tcp) { - return current_tcp_state; + return tcp->state; }
/** - * tcp_set_tcp_state() - set current TCP state + * tcp_set_tcp_state() - set TCP stream state + * @tcp: tcp stream * @new_state: new TCP state */ -void tcp_set_tcp_state(enum tcp_state new_state) +void tcp_set_tcp_state(struct tcp_stream *tcp, + enum tcp_state new_state) { - current_tcp_state = new_state; + tcp->state = new_state; +} + +struct tcp_stream *tcp_stream_get(void) +{ + return &tcp_stream; }
static void dummy_handler(uchar *pkt, u16 dport, @@ -138,29 +132,30 @@ u16 tcp_set_pseudo_header(uchar *pkt, struct in_addr src, struct in_addr dest,
/** * net_set_ack_options() - set TCP options in acknowledge packets + * @tcp: tcp stream * @b: the packet * * Return: TCP header length */ -int net_set_ack_options(union tcp_build_pkt *b) +int net_set_ack_options(struct tcp_stream *tcp, union tcp_build_pkt *b) { b->sack.hdr.tcp_hlen = SHIFT_TO_TCPHDRLEN_FIELD(LEN_B_TO_DW(TCP_HDR_SIZE));
b->sack.t_opt.kind = TCP_O_TS; b->sack.t_opt.len = TCP_OPT_LEN_A; - b->sack.t_opt.t_snd = htons(loc_timestamp); - b->sack.t_opt.t_rcv = rmt_timestamp; + b->sack.t_opt.t_snd = htons(tcp->loc_timestamp); + b->sack.t_opt.t_rcv = tcp->rmt_timestamp; b->sack.sack_v.kind = TCP_1_NOP; b->sack.sack_v.len = 0;
if (IS_ENABLED(CONFIG_PROT_TCP_SACK)) { - if (tcp_lost.len > TCP_OPT_LEN_2) { + if (tcp->lost.len > TCP_OPT_LEN_2) { debug_cond(DEBUG_DEV_PKT, "TCP ack opt lost.len %x\n", - tcp_lost.len); - b->sack.sack_v.len = tcp_lost.len; + tcp->lost.len); + b->sack.sack_v.len = tcp->lost.len; b->sack.sack_v.kind = TCP_V_SACK; - b->sack.sack_v.hill[0].l = htonl(tcp_lost.hill[0].l); - b->sack.sack_v.hill[0].r = htonl(tcp_lost.hill[0].r); + b->sack.sack_v.hill[0].l = htonl(tcp->lost.hill[0].l); + b->sack.sack_v.hill[0].r = htonl(tcp->lost.hill[0].r);
/* * These SACK structures are initialized with NOPs to @@ -168,17 +163,17 @@ int net_set_ack_options(union tcp_build_pkt *b) * SACK structures used for both header padding and * internally. */ - b->sack.sack_v.hill[1].l = htonl(tcp_lost.hill[1].l); - b->sack.sack_v.hill[1].r = htonl(tcp_lost.hill[1].r); - b->sack.sack_v.hill[2].l = htonl(tcp_lost.hill[2].l); - b->sack.sack_v.hill[2].r = htonl(tcp_lost.hill[2].r); + b->sack.sack_v.hill[1].l = htonl(tcp->lost.hill[1].l); + b->sack.sack_v.hill[1].r = htonl(tcp->lost.hill[1].r); + b->sack.sack_v.hill[2].l = htonl(tcp->lost.hill[2].l); + b->sack.sack_v.hill[2].r = htonl(tcp->lost.hill[2].r); b->sack.sack_v.hill[3].l = TCP_O_NOP; b->sack.sack_v.hill[3].r = TCP_O_NOP; }
b->sack.hdr.tcp_hlen = SHIFT_TO_TCPHDRLEN_FIELD(ROUND_TCPHDR_LEN(TCP_HDR_SIZE + TCP_TSOPT_SIZE + - tcp_lost.len)); + tcp->lost.len)); } else { b->sack.sack_v.kind = 0; b->sack.hdr.tcp_hlen = SHIFT_TO_TCPHDRLEN_FIELD(ROUND_TCPHDR_LEN(TCP_HDR_SIZE + @@ -194,13 +189,14 @@ int net_set_ack_options(union tcp_build_pkt *b) }
/** - * net_set_ack_options() - set TCP options in SYN packets + * net_set_syn_options() - set TCP options in SYN packets + * @tcp: tcp stream * @b: the packet */ -void net_set_syn_options(union tcp_build_pkt *b) +void net_set_syn_options(struct tcp_stream *tcp, union tcp_build_pkt *b) { if (IS_ENABLED(CONFIG_PROT_TCP_SACK)) - tcp_lost.len = 0; + tcp->lost.len = 0;
b->ip.hdr.tcp_hlen = 0xa0;
@@ -219,14 +215,15 @@ void net_set_syn_options(union tcp_build_pkt *b) } b->ip.t_opt.kind = TCP_O_TS; b->ip.t_opt.len = TCP_OPT_LEN_A; - loc_timestamp = get_ticks(); - rmt_timestamp = 0; + tcp->loc_timestamp = get_ticks(); + tcp->rmt_timestamp = 0; b->ip.t_opt.t_snd = 0; b->ip.t_opt.t_rcv = 0; b->ip.end = TCP_O_END; }
-int tcp_set_tcp_header(uchar *pkt, int dport, int sport, int payload_len, +int tcp_set_tcp_header(struct tcp_stream *tcp, uchar *pkt, int dport, + int sport, int payload_len, u8 action, u32 tcp_seq_num, u32 tcp_ack_num) { union tcp_build_pkt *b = (union tcp_build_pkt *)pkt; @@ -249,21 +246,21 @@ int tcp_set_tcp_header(uchar *pkt, int dport, int sport, int payload_len, &net_server_ip, &net_ip, tcp_seq_num, tcp_ack_num); tcp_activity_count = 0; - net_set_syn_options(b); + net_set_syn_options(tcp, b); tcp_seq_num = 0; tcp_ack_num = 0; pkt_hdr_len = IP_TCP_O_SIZE; - if (current_tcp_state == TCP_SYN_SENT) { /* Too many SYNs */ + if (tcp->state == TCP_SYN_SENT) { /* Too many SYNs */ action = TCP_FIN; - current_tcp_state = TCP_FIN_WAIT_1; + tcp->state = TCP_FIN_WAIT_1; } else { - tcp_lost.len = TCP_OPT_LEN_2; - current_tcp_state = TCP_SYN_SENT; + tcp->lost.len = TCP_OPT_LEN_2; + tcp->state = TCP_SYN_SENT; } break; case TCP_SYN | TCP_ACK: case TCP_ACK: - pkt_hdr_len = IP_HDR_SIZE + net_set_ack_options(b); + pkt_hdr_len = IP_HDR_SIZE + net_set_ack_options(tcp, b); b->ip.hdr.tcp_flags = action; debug_cond(DEBUG_DEV_PKT, "TCP Hdr:ACK (%pI4, %pI4, s=%u, a=%u, A=%x)\n", @@ -276,20 +273,20 @@ int tcp_set_tcp_header(uchar *pkt, int dport, int sport, int payload_len, &net_server_ip, &net_ip, tcp_seq_num, tcp_ack_num); payload_len = 0; pkt_hdr_len = IP_TCP_HDR_SIZE; - current_tcp_state = TCP_FIN_WAIT_1; + tcp->state = TCP_FIN_WAIT_1; break; case TCP_RST | TCP_ACK: case TCP_RST: debug_cond(DEBUG_DEV_PKT, "TCP Hdr:RST (%pI4, %pI4, s=%u, a=%u)\n", &net_server_ip, &net_ip, tcp_seq_num, tcp_ack_num); - current_tcp_state = TCP_CLOSED; + tcp->state = TCP_CLOSED; break; /* Notify connection closing */ case (TCP_FIN | TCP_ACK): case (TCP_FIN | TCP_ACK | TCP_PUSH): - if (current_tcp_state == TCP_CLOSE_WAIT) - current_tcp_state = TCP_CLOSING; + if (tcp->state == TCP_CLOSE_WAIT) + tcp->state = TCP_CLOSING;
debug_cond(DEBUG_DEV_PKT, "TCP Hdr:FIN ACK PSH(%pI4, %pI4, s=%u, a=%u, A=%x)\n", @@ -297,7 +294,7 @@ int tcp_set_tcp_header(uchar *pkt, int dport, int sport, int payload_len, tcp_seq_num, tcp_ack_num, action); fallthrough; default: - pkt_hdr_len = IP_HDR_SIZE + net_set_ack_options(b); + pkt_hdr_len = IP_HDR_SIZE + net_set_ack_options(tcp, b); b->ip.hdr.tcp_flags = action | TCP_PUSH | TCP_ACK; debug_cond(DEBUG_DEV_PKT, "TCP Hdr:dft (%pI4, %pI4, s=%u, a=%u, A=%x)\n", @@ -308,9 +305,9 @@ int tcp_set_tcp_header(uchar *pkt, int dport, int sport, int payload_len, pkt_len = pkt_hdr_len + payload_len; tcp_len = pkt_len - IP_HDR_SIZE;
- tcp_ack_edge = tcp_ack_num; + tcp->ack_edge = tcp_ack_num; /* TCP Header */ - b->ip.hdr.tcp_ack = htonl(tcp_ack_edge); + b->ip.hdr.tcp_ack = htonl(tcp->ack_edge); b->ip.hdr.tcp_src = htons(sport); b->ip.hdr.tcp_dst = htons(dport); b->ip.hdr.tcp_seq = htonl(tcp_seq_num); @@ -344,78 +341,79 @@ int tcp_set_tcp_header(uchar *pkt, int dport, int sport, int payload_len, return pkt_hdr_len; }
-static void tcp_update_ack_edge(void) +static void tcp_update_ack_edge(struct tcp_stream *tcp) { - if (tcp_seq_cmp(tcp_ack_edge, tcp_lost.hill[0].l) >= 0) { - tcp_ack_edge = tcp_lost.hill[0].r; + if (tcp_seq_cmp(tcp->ack_edge, tcp->lost.hill[0].l) >= 0) { + tcp->ack_edge = tcp->lost.hill[0].r;
- memmove(&tcp_lost.hill[0], &tcp_lost.hill[1], + memmove(&tcp->lost.hill[0], &tcp->lost.hill[1], (TCP_SACK_HILLS - 1) * sizeof(struct sack_edges));
- tcp_lost.len -= TCP_OPT_LEN_8; - tcp_lost.hill[TCP_SACK_HILLS - 1].l = TCP_O_NOP; - tcp_lost.hill[TCP_SACK_HILLS - 1].r = TCP_O_NOP; + tcp->lost.len -= TCP_OPT_LEN_8; + tcp->lost.hill[TCP_SACK_HILLS - 1].l = TCP_O_NOP; + tcp->lost.hill[TCP_SACK_HILLS - 1].r = TCP_O_NOP; } }
/** * tcp_hole() - Selective Acknowledgment (Essential for fast stream transfer) + * @tcp: tcp stream * @tcp_seq_num: TCP sequence start number * @len: the length of sequence numbers */ -void tcp_hole(u32 tcp_seq_num, u32 len) +void tcp_hole(struct tcp_stream *tcp, u32 tcp_seq_num, u32 len) { int i, j, cnt, cnt_move;
- cnt = (tcp_lost.len - TCP_OPT_LEN_2) / TCP_OPT_LEN_8; + cnt = (tcp->lost.len - TCP_OPT_LEN_2) / TCP_OPT_LEN_8; for (i = 0; i < cnt; i++) { - if (tcp_seq_cmp(tcp_lost.hill[i].r, tcp_seq_num) < 0) + if (tcp_seq_cmp(tcp->lost.hill[i].r, tcp_seq_num) < 0) continue; - if (tcp_seq_cmp(tcp_lost.hill[i].l, tcp_seq_num + len) > 0) + if (tcp_seq_cmp(tcp->lost.hill[i].l, tcp_seq_num + len) > 0) break;
- if (tcp_seq_cmp(tcp_lost.hill[i].l, tcp_seq_num) > 0) - tcp_lost.hill[i].l = tcp_seq_num; - if (tcp_seq_cmp(tcp_lost.hill[i].l, tcp_seq_num) < 0) { - len += tcp_seq_num - tcp_lost.hill[i].l; - tcp_seq_num = tcp_lost.hill[i].l; + if (tcp_seq_cmp(tcp->lost.hill[i].l, tcp_seq_num) > 0) + tcp->lost.hill[i].l = tcp_seq_num; + if (tcp_seq_cmp(tcp->lost.hill[i].l, tcp_seq_num) < 0) { + len += tcp_seq_num - tcp->lost.hill[i].l; + tcp_seq_num = tcp->lost.hill[i].l; } - if (tcp_seq_cmp(tcp_lost.hill[i].r, tcp_seq_num + len) >= 0) { - tcp_update_ack_edge(); + if (tcp_seq_cmp(tcp->lost.hill[i].r, tcp_seq_num + len) >= 0) { + tcp_update_ack_edge(tcp); return; }
/* check overlapping with next hills */ cnt_move = 0; - tcp_lost.hill[i].r = tcp_seq_num + len; + tcp->lost.hill[i].r = tcp_seq_num + len; for (j = i + 1; j < cnt; j++) { - if (tcp_seq_cmp(tcp_lost.hill[j].l, tcp_lost.hill[i].r) > 0) + if (tcp_seq_cmp(tcp->lost.hill[j].l, tcp->lost.hill[i].r) > 0) break;
- tcp_lost.hill[i].r = tcp_lost.hill[j].r; + tcp->lost.hill[i].r = tcp->lost.hill[j].r; cnt_move++; }
if (cnt_move > 0) { if (cnt > i + cnt_move + 1) - memmove(&tcp_lost.hill[i + 1], - &tcp_lost.hill[i + cnt_move + 1], + memmove(&tcp->lost.hill[i + 1], + &tcp->lost.hill[i + cnt_move + 1], cnt_move * sizeof(struct sack_edges));
cnt -= cnt_move; - tcp_lost.len = TCP_OPT_LEN_2 + cnt * TCP_OPT_LEN_8; + tcp->lost.len = TCP_OPT_LEN_2 + cnt * TCP_OPT_LEN_8; for (j = cnt; j < TCP_SACK_HILLS; j++) { - tcp_lost.hill[j].l = TCP_O_NOP; - tcp_lost.hill[j].r = TCP_O_NOP; + tcp->lost.hill[j].l = TCP_O_NOP; + tcp->lost.hill[j].r = TCP_O_NOP; } }
- tcp_update_ack_edge(); + tcp_update_ack_edge(tcp); return; }
if (i == TCP_SACK_HILLS) { - tcp_update_ack_edge(); + tcp_update_ack_edge(tcp); return; }
@@ -428,23 +426,24 @@ void tcp_hole(u32 tcp_seq_num, u32 len) }
if (cnt_move > 0) - memmove(&tcp_lost.hill[i + 1], - &tcp_lost.hill[i], + memmove(&tcp->lost.hill[i + 1], + &tcp->lost.hill[i], cnt_move * sizeof(struct sack_edges));
- tcp_lost.hill[i].l = tcp_seq_num; - tcp_lost.hill[i].r = tcp_seq_num + len; - tcp_lost.len = TCP_OPT_LEN_2 + cnt * TCP_OPT_LEN_8; + tcp->lost.hill[i].l = tcp_seq_num; + tcp->lost.hill[i].r = tcp_seq_num + len; + tcp->lost.len = TCP_OPT_LEN_2 + cnt * TCP_OPT_LEN_8;
- tcp_update_ack_edge(); + tcp_update_ack_edge(tcp); };
/** * tcp_parse_options() - parsing TCP options + * @tcp: tcp stream * @o: pointer to the option field. * @o_len: length of the option field. */ -void tcp_parse_options(uchar *o, int o_len) +void tcp_parse_options(struct tcp_stream *tcp, uchar *o, int o_len) { struct tcp_t_opt *tsopt; uchar *p = o; @@ -467,7 +466,7 @@ void tcp_parse_options(uchar *o, int o_len) break; case TCP_O_TS: tsopt = (struct tcp_t_opt *)p; - rmt_timestamp = tsopt->t_snd; + tcp->rmt_timestamp = tsopt->t_snd; break; }
@@ -479,7 +478,8 @@ void tcp_parse_options(uchar *o, int o_len) } }
-static u8 tcp_state_machine(u8 tcp_flags, u32 tcp_seq_num, int payload_len) +static u8 tcp_state_machine(struct tcp_stream *tcp, u8 tcp_flags, + u32 tcp_seq_num, int payload_len) { u8 tcp_fin = tcp_flags & TCP_FIN; u8 tcp_syn = tcp_flags & TCP_SYN; @@ -500,21 +500,21 @@ static u8 tcp_state_machine(u8 tcp_flags, u32 tcp_seq_num, int payload_len) debug_cond(DEBUG_INT_STATE, "TCP STATE ENTRY %x\n", action); if (tcp_rst) { action = TCP_DATA; - current_tcp_state = TCP_CLOSED; + tcp->state = TCP_CLOSED; net_set_state(NETLOOP_FAIL); debug_cond(DEBUG_INT_STATE, "TCP Reset %x\n", tcp_flags); return TCP_RST; }
- switch (current_tcp_state) { + switch (tcp->state) { case TCP_CLOSED: debug_cond(DEBUG_INT_STATE, "TCP CLOSED %x\n", tcp_flags); if (tcp_syn) { action = TCP_SYN | TCP_ACK; - tcp_seq_init = tcp_seq_num; - tcp_ack_edge = tcp_seq_num + 1; - tcp_lost.len = TCP_OPT_LEN_2; - current_tcp_state = TCP_SYN_RECEIVED; + tcp->seq_init = tcp_seq_num; + tcp->ack_edge = tcp_seq_num + 1; + tcp->lost.len = TCP_OPT_LEN_2; + tcp->state = TCP_SYN_RECEIVED; } else if (tcp_ack || tcp_fin) { action = TCP_DATA; } @@ -525,12 +525,12 @@ static u8 tcp_state_machine(u8 tcp_flags, u32 tcp_seq_num, int payload_len) tcp_flags, tcp_seq_num); if (tcp_fin) { action = action | TCP_PUSH; - current_tcp_state = TCP_CLOSE_WAIT; + tcp->state = TCP_CLOSE_WAIT; } else if (tcp_ack || (tcp_syn && tcp_ack)) { action |= TCP_ACK; - tcp_seq_init = tcp_seq_num; - tcp_ack_edge = tcp_seq_num + 1; - current_tcp_state = TCP_ESTABLISHED; + tcp->seq_init = tcp_seq_num; + tcp->ack_edge = tcp_seq_num + 1; + tcp->state = TCP_ESTABLISHED;
if (tcp_syn && tcp_ack) action |= TCP_PUSH; @@ -541,15 +541,15 @@ static u8 tcp_state_machine(u8 tcp_flags, u32 tcp_seq_num, int payload_len) case TCP_ESTABLISHED: debug_cond(DEBUG_INT_STATE, "TCP_ESTABLISHED %x\n", tcp_flags); if (payload_len > 0) { - tcp_hole(tcp_seq_num, payload_len); + tcp_hole(tcp, tcp_seq_num, payload_len); tcp_fin = TCP_DATA; /* cause standalone FIN */ }
if ((tcp_fin) && (!IS_ENABLED(CONFIG_PROT_TCP_SACK) || - tcp_lost.len <= TCP_OPT_LEN_2)) { + tcp->lost.len <= TCP_OPT_LEN_2)) { action = action | TCP_FIN | TCP_PUSH | TCP_ACK; - current_tcp_state = TCP_CLOSE_WAIT; + tcp->state = TCP_CLOSE_WAIT; } else if (tcp_ack) { action = TCP_DATA; } @@ -567,7 +567,7 @@ static u8 tcp_state_machine(u8 tcp_flags, u32 tcp_seq_num, int payload_len) debug_cond(DEBUG_INT_STATE, "TCP_FIN_WAIT_2 (%x)\n", tcp_flags); if (tcp_ack) { action = TCP_PUSH | TCP_ACK; - current_tcp_state = TCP_CLOSED; + tcp->state = TCP_CLOSED; puts("\n"); } else if (tcp_syn) { action = TCP_DATA; @@ -578,20 +578,20 @@ static u8 tcp_state_machine(u8 tcp_flags, u32 tcp_seq_num, int payload_len) case TCP_FIN_WAIT_1: debug_cond(DEBUG_INT_STATE, "TCP_FIN_WAIT_1 (%x)\n", tcp_flags); if (tcp_fin) { - tcp_ack_edge++; + tcp->ack_edge++; action = TCP_ACK | TCP_FIN; - current_tcp_state = TCP_FIN_WAIT_2; + tcp->state = TCP_FIN_WAIT_2; } if (tcp_syn) action = TCP_RST; if (tcp_ack) - current_tcp_state = TCP_CLOSED; + tcp->state = TCP_CLOSED; break; case TCP_CLOSING: debug_cond(DEBUG_INT_STATE, "TCP_CLOSING (%x)\n", tcp_flags); if (tcp_ack) { action = TCP_PUSH; - current_tcp_state = TCP_CLOSED; + tcp->state = TCP_CLOSED; puts("\n"); } else if (tcp_syn) { action = TCP_RST; @@ -615,6 +615,7 @@ void rxhand_tcp_f(union tcp_build_pkt *b, unsigned int pkt_len) u8 tcp_action = TCP_DATA; u32 tcp_seq_num, tcp_ack_num; int tcp_hdr_len, payload_len; + struct tcp_stream *tcp;
/* Verify IP header */ debug_cond(DEBUG_DEV_PKT, @@ -643,11 +644,15 @@ void rxhand_tcp_f(union tcp_build_pkt *b, unsigned int pkt_len) return; }
+ tcp = tcp_stream_get(); + if (tcp == NULL) + return; + tcp_hdr_len = GET_TCP_HDR_LEN_IN_BYTES(b->ip.hdr.tcp_hlen); payload_len = tcp_len - tcp_hdr_len;
if (tcp_hdr_len > TCP_HDR_SIZE) - tcp_parse_options((uchar *)b + IP_TCP_HDR_SIZE, + tcp_parse_options(tcp, (uchar *)b + IP_TCP_HDR_SIZE, tcp_hdr_len - TCP_HDR_SIZE); /* * Incoming sequence and ack numbers are server's view of the numbers. @@ -657,7 +662,7 @@ void rxhand_tcp_f(union tcp_build_pkt *b, unsigned int pkt_len) tcp_ack_num = ntohl(b->ip.hdr.tcp_ack);
/* Packets are not ordered. Send to app as received. */ - tcp_action = tcp_state_machine(b->ip.hdr.tcp_flags, + tcp_action = tcp_state_machine(tcp, b->ip.hdr.tcp_flags, tcp_seq_num, payload_len);
tcp_activity_count++; @@ -678,7 +683,7 @@ void rxhand_tcp_f(union tcp_build_pkt *b, unsigned int pkt_len) } else if (tcp_action != TCP_DATA) { debug_cond(DEBUG_DEV_PKT, "TCP Action (action=%x,Seq=%u,Ack=%u,Pay=%d)\n", - tcp_action, tcp_ack_num, tcp_ack_edge, payload_len); + tcp_action, tcp_ack_num, tcp->ack_edge, payload_len);
/* * Warning: Incoming Ack & Seq sequence numbers are transposed @@ -687,6 +692,6 @@ void rxhand_tcp_f(union tcp_build_pkt *b, unsigned int pkt_len) net_send_tcp_packet(0, ntohs(b->ip.hdr.tcp_src), ntohs(b->ip.hdr.tcp_dst), (tcp_action & (~TCP_PUSH)), - tcp_ack_num, tcp_ack_edge); + tcp_ack_num, tcp->ack_edge); } } diff --git a/net/wget.c b/net/wget.c index 361817ace65..07992613c91 100644 --- a/net/wget.c +++ b/net/wget.c @@ -304,7 +304,8 @@ static void wget_handler(uchar *pkt, u16 dport, u32 tcp_seq_num, u32 tcp_ack_num, u8 action, unsigned int len) { - enum tcp_state wget_tcp_state = tcp_get_tcp_state(); + struct tcp_stream *tcp = tcp_stream_get(); + enum tcp_state wget_tcp_state = tcp_get_tcp_state(tcp);
net_set_timeout_handler(wget_timeout, wget_timeout_handler); packets++;

Changes: * Avoid use net_server_ip in tcp code, use tcp_stream data instead * Ignore packets from other connections if connection already created. This prevents us from connection break caused by other tcp stream.
Signed-off-by: Mikhail Kshevetskiy mikhail.kshevetskiy@iopsys.eu Reviewed-by: Simon Glass sjg@chromium.org --- include/net-legacy.h | 5 +- include/net/tcp.h | 57 ++++++++++++++++--- net/fastboot_tcp.c | 50 +++++++++-------- net/net.c | 12 ++-- net/tcp.c | 131 +++++++++++++++++++++++++++++++++---------- net/wget.c | 52 ++++++----------- 6 files changed, 204 insertions(+), 103 deletions(-)
diff --git a/include/net-legacy.h b/include/net-legacy.h index ca1efd17af7..d6902508ebd 100644 --- a/include/net-legacy.h +++ b/include/net-legacy.h @@ -414,6 +414,7 @@ int net_send_ip_packet(uchar *ether, struct in_addr dest, int dport, int sport, /** * net_send_tcp_packet() - Transmit TCP packet. * @payload_len: length of payload + * @dhost: Destination host * @dport: Destination TCP port * @sport: Source TCP port * @action: TCP action to be performed @@ -422,8 +423,8 @@ int net_send_ip_packet(uchar *ether, struct in_addr dest, int dport, int sport, * * Return: 0 on success, other value on failure */ -int net_send_tcp_packet(int payload_len, int dport, int sport, u8 action, - u32 tcp_seq_num, u32 tcp_ack_num); +int net_send_tcp_packet(int payload_len, struct in_addr dhost, int dport, + int sport, u8 action, u32 tcp_seq_num, u32 tcp_ack_num); int net_send_udp_packet(uchar *ether, struct in_addr dest, int dport, int sport, int payload_len);
diff --git a/include/net/tcp.h b/include/net/tcp.h index 14aee64cb1c..f224d0cae2f 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -279,6 +279,9 @@ enum tcp_state {
/** * struct tcp_stream - TCP data stream structure + * @rhost: Remote host, network byte order + * @rport: Remote port, host byte order + * @lport: Local port, host byte order * * @state: TCP connection state * @@ -291,6 +294,10 @@ enum tcp_state { * @lost: Used for SACK */ struct tcp_stream { + struct in_addr rhost; + u16 rport; + u16 lport; + /* TCP connection state */ enum tcp_state state;
@@ -305,16 +312,53 @@ struct tcp_stream { struct tcp_sack_v lost; };
-struct tcp_stream *tcp_stream_get(void); +void tcp_init(void); + +typedef int tcp_incoming_filter(struct in_addr rhost, + u16 rport, u16 sport); + +/* + * This function sets user callback used to accept/drop incoming + * connections. Callback should: + * + Check TCP stream endpoint and make connection verdict + * - return non-zero value to accept connection + * - return zero to drop connection + * + * WARNING: If callback is NOT defined, all incoming connections + * will be dropped. + */ +void tcp_set_incoming_filter(tcp_incoming_filter *filter); + +/* + * tcp_stream_get -- Get or create TCP stream + * @is_new: if non-zero and no stream found, then create a new one + * @rhost: Remote host, network byte order + * @rport: Remote port, host byte order + * @lport: Local port, host byte order + * + * Returns: TCP stream structure or NULL (if not found/created) + */ +struct tcp_stream *tcp_stream_get(int is_new, struct in_addr rhost, + u16 rport, u16 lport); + +/* + * tcp_stream_connect -- Create new TCP stream for remote connection. + * @rhost: Remote host, network byte order + * @rport: Remote port, host byte order + * + * Returns: TCP new stream structure or NULL (if not created). + * Random local port will be used. + */ +struct tcp_stream *tcp_stream_connect(struct in_addr rhost, u16 rport); + +enum tcp_state tcp_stream_get_state(struct tcp_stream *tcp);
-enum tcp_state tcp_get_tcp_state(struct tcp_stream *tcp); -void tcp_set_tcp_state(struct tcp_stream *tcp, enum tcp_state new_state); -int tcp_set_tcp_header(struct tcp_stream *tcp, uchar *pkt, int dport, - int sport, int payload_len, +int tcp_set_tcp_header(struct tcp_stream *tcp, uchar *pkt, int payload_len, u8 action, u32 tcp_seq_num, u32 tcp_ack_num);
/** * rxhand_tcp() - An incoming packet handler. + * @tcp: TCP stream * @pkt: pointer to the application packet * @dport: destination TCP port * @sip: source IP address @@ -324,8 +368,7 @@ int tcp_set_tcp_header(struct tcp_stream *tcp, uchar *pkt, int dport, * @action: TCP action (SYN, ACK, FIN, etc) * @len: packet length */ -typedef void rxhand_tcp(uchar *pkt, u16 dport, - struct in_addr sip, u16 sport, +typedef void rxhand_tcp(struct tcp_stream *tcp, uchar *pkt, u32 tcp_seq_num, u32 tcp_ack_num, u8 action, unsigned int len); void tcp_set_tcp_handler(rxhand_tcp *f); diff --git a/net/fastboot_tcp.c b/net/fastboot_tcp.c index d1fccbc7238..4d34fdc5a45 100644 --- a/net/fastboot_tcp.c +++ b/net/fastboot_tcp.c @@ -8,14 +8,14 @@ #include <net/fastboot_tcp.h> #include <net/tcp.h>
-static char command[FASTBOOT_COMMAND_LEN] = {0}; -static char response[FASTBOOT_RESPONSE_LEN] = {0}; +#define FASTBOOT_TCP_PORT 5554 + +static char command[FASTBOOT_COMMAND_LEN]; +static char response[FASTBOOT_RESPONSE_LEN];
static const unsigned short handshake_length = 4; static const uchar *handshake = "FB01";
-static u16 curr_sport; -static u16 curr_dport; static u32 curr_tcp_seq_num; static u32 curr_tcp_ack_num; static unsigned int curr_request_len; @@ -25,34 +25,37 @@ static enum fastboot_tcp_state { FASTBOOT_DISCONNECTING } state = FASTBOOT_CLOSED;
-static void fastboot_tcp_answer(u8 action, unsigned int len) +static void fastboot_tcp_answer(struct tcp_stream *tcp, u8 action, + unsigned int len) { const u32 response_seq_num = curr_tcp_ack_num; const u32 response_ack_num = curr_tcp_seq_num + (curr_request_len > 0 ? curr_request_len : 1);
- net_send_tcp_packet(len, htons(curr_sport), htons(curr_dport), + net_send_tcp_packet(len, tcp->rhost, tcp->rport, tcp->lport, action, response_seq_num, response_ack_num); }
-static void fastboot_tcp_reset(void) +static void fastboot_tcp_reset(struct tcp_stream *tcp) { - fastboot_tcp_answer(TCP_RST, 0); + fastboot_tcp_answer(tcp, TCP_RST, 0); state = FASTBOOT_CLOSED; }
-static void fastboot_tcp_send_packet(u8 action, const uchar *data, unsigned int len) +static void fastboot_tcp_send_packet(struct tcp_stream *tcp, u8 action, + const uchar *data, unsigned int len) { uchar *pkt = net_get_async_tx_pkt_buf();
memset(pkt, '\0', PKTSIZE); pkt += net_eth_hdr_size() + IP_TCP_HDR_SIZE + TCP_TSOPT_SIZE + 2; memcpy(pkt, data, len); - fastboot_tcp_answer(action, len); + fastboot_tcp_answer(tcp, action, len); memset(pkt, '\0', PKTSIZE); }
-static void fastboot_tcp_send_message(const char *message, unsigned int len) +static void fastboot_tcp_send_message(struct tcp_stream *tcp, + const char *message, unsigned int len) { __be64 len_be = __cpu_to_be64(len); uchar *pkt = net_get_async_tx_pkt_buf(); @@ -63,12 +66,11 @@ static void fastboot_tcp_send_message(const char *message, unsigned int len) memcpy(pkt, &len_be, 8); pkt += 8; memcpy(pkt, message, len); - fastboot_tcp_answer(TCP_ACK | TCP_PUSH, len + 8); + fastboot_tcp_answer(tcp, TCP_ACK | TCP_PUSH, len + 8); memset(pkt, '\0', PKTSIZE); }
-static void fastboot_tcp_handler_ipv4(uchar *pkt, u16 dport, - struct in_addr sip, u16 sport, +static void fastboot_tcp_handler_ipv4(struct tcp_stream *tcp, uchar *pkt, u32 tcp_seq_num, u32 tcp_ack_num, u8 action, unsigned int len) { @@ -77,8 +79,6 @@ static void fastboot_tcp_handler_ipv4(uchar *pkt, u16 dport, u8 tcp_fin = action & TCP_FIN; u8 tcp_push = action & TCP_PUSH;
- curr_sport = sport; - curr_dport = dport; curr_tcp_seq_num = tcp_seq_num; curr_tcp_ack_num = tcp_ack_num; curr_request_len = len; @@ -89,17 +89,17 @@ static void fastboot_tcp_handler_ipv4(uchar *pkt, u16 dport, if (len != handshake_length || strlen(pkt) != handshake_length || memcmp(pkt, handshake, handshake_length) != 0) { - fastboot_tcp_reset(); + fastboot_tcp_reset(tcp); break; } - fastboot_tcp_send_packet(TCP_ACK | TCP_PUSH, + fastboot_tcp_send_packet(tcp, TCP_ACK | TCP_PUSH, handshake, handshake_length); state = FASTBOOT_CONNECTED; } break; case FASTBOOT_CONNECTED: if (tcp_fin) { - fastboot_tcp_answer(TCP_FIN | TCP_ACK, 0); + fastboot_tcp_answer(tcp, TCP_FIN | TCP_ACK, 0); state = FASTBOOT_DISCONNECTING; break; } @@ -111,12 +111,12 @@ static void fastboot_tcp_handler_ipv4(uchar *pkt, u16 dport,
// Only single packet messages are supported ATM if (strlen(pkt) != command_size) { - fastboot_tcp_reset(); + fastboot_tcp_reset(tcp); break; } strlcpy(command, pkt, len + 1); fastboot_command_id = fastboot_handle_command(command, response); - fastboot_tcp_send_message(response, strlen(response)); + fastboot_tcp_send_message(tcp, response, strlen(response)); fastboot_handle_boot(fastboot_command_id, strncmp("OKAY", response, 4) == 0); } @@ -129,17 +129,21 @@ static void fastboot_tcp_handler_ipv4(uchar *pkt, u16 dport,
memset(command, 0, FASTBOOT_COMMAND_LEN); memset(response, 0, FASTBOOT_RESPONSE_LEN); - curr_sport = 0; - curr_dport = 0; curr_tcp_seq_num = 0; curr_tcp_ack_num = 0; curr_request_len = 0; }
+static int incoming_filter(struct in_addr rhost, u16 rport, u16 lport) +{ + return (lport == FASTBOOT_TCP_PORT); +} + void fastboot_tcp_start_server(void) { printf("Using %s device\n", eth_get_name()); printf("Listening for fastboot command on tcp %pI4\n", &net_ip);
+ tcp_set_incoming_filter(incoming_filter); tcp_set_tcp_handler(fastboot_tcp_handler_ipv4); } diff --git a/net/net.c b/net/net.c index d9e143f0ecc..e5caf1d9334 100644 --- a/net/net.c +++ b/net/net.c @@ -420,7 +420,7 @@ int net_init(void) /* Only need to setup buffer pointers once. */ first_call = 0; if (IS_ENABLED(CONFIG_PROT_TCP)) - tcp_set_tcp_state(tcp_stream_get(), TCP_CLOSED); + tcp_init(); }
return net_init_loop(); @@ -908,10 +908,10 @@ int net_send_udp_packet(uchar *ether, struct in_addr dest, int dport, int sport, }
#if defined(CONFIG_PROT_TCP) -int net_send_tcp_packet(int payload_len, int dport, int sport, u8 action, - u32 tcp_seq_num, u32 tcp_ack_num) +int net_send_tcp_packet(int payload_len, struct in_addr dhost, int dport, + int sport, u8 action, u32 tcp_seq_num, u32 tcp_ack_num) { - return net_send_ip_packet(net_server_ethaddr, net_server_ip, dport, + return net_send_ip_packet(net_server_ethaddr, dhost, dport, sport, payload_len, IPPROTO_TCP, action, tcp_seq_num, tcp_ack_num); } @@ -953,12 +953,12 @@ int net_send_ip_packet(uchar *ether, struct in_addr dest, int dport, int sport, break; #if defined(CONFIG_PROT_TCP) case IPPROTO_TCP: - tcp = tcp_stream_get(); + tcp = tcp_stream_get(0, dest, dport, sport); if (tcp == NULL) return -EINVAL;
pkt_hdr_size = eth_hdr_size - + tcp_set_tcp_header(tcp, pkt + eth_hdr_size, dport, sport, + + tcp_set_tcp_header(tcp, pkt + eth_hdr_size, payload_len, action, tcp_seq_num, tcp_ack_num); break; diff --git a/net/tcp.c b/net/tcp.c index 6646f171b83..0c32c5d7c92 100644 --- a/net/tcp.c +++ b/net/tcp.c @@ -26,6 +26,7 @@
static int tcp_activity_count; static struct tcp_stream tcp_stream; +static tcp_incoming_filter *incoming_filter;
/* * TCP lengths are stored as a rounded up number of 32 bit words. @@ -40,40 +41,95 @@ static struct tcp_stream tcp_stream; /* Current TCP RX packet handler */ static rxhand_tcp *tcp_packet_handler;
+#define RANDOM_PORT_START 1024 +#define RANDOM_PORT_RANGE 0x4000 + +/** + * random_port() - make port a little random (1024-17407) + * + * Return: random port number from 1024 to 17407 + * + * This keeps the math somewhat trivial to compute, and seems to work with + * all supported protocols/clients/servers + */ +static uint random_port(void) +{ + return RANDOM_PORT_START + (get_timer(0) % RANDOM_PORT_RANGE); +} + static inline s32 tcp_seq_cmp(u32 a, u32 b) { return (s32)(a - b); }
/** - * tcp_get_tcp_state() - get TCP stream state + * tcp_stream_get_state() - get TCP stream state * @tcp: tcp stream * * Return: TCP stream state */ -enum tcp_state tcp_get_tcp_state(struct tcp_stream *tcp) +enum tcp_state tcp_stream_get_state(struct tcp_stream *tcp) { return tcp->state; }
/** - * tcp_set_tcp_state() - set TCP stream state + * tcp_stream_set_state() - set TCP stream state * @tcp: tcp stream * @new_state: new TCP state */ -void tcp_set_tcp_state(struct tcp_stream *tcp, - enum tcp_state new_state) +static void tcp_stream_set_state(struct tcp_stream *tcp, + enum tcp_state new_state) { tcp->state = new_state; }
-struct tcp_stream *tcp_stream_get(void) +void tcp_init(void) +{ + incoming_filter = NULL; + tcp_stream.state = TCP_CLOSED; +} + +void tcp_set_incoming_filter(tcp_incoming_filter *filter) +{ + incoming_filter = filter; +} + +static struct tcp_stream *tcp_stream_add(struct in_addr rhost, + u16 rport, u16 lport) +{ + struct tcp_stream *tcp = &tcp_stream; + + if (tcp->state != TCP_CLOSED) + return NULL; + + memset(tcp, 0, sizeof(struct tcp_stream)); + tcp->rhost.s_addr = rhost.s_addr; + tcp->rport = rport; + tcp->lport = lport; + tcp->state = TCP_CLOSED; + tcp->lost.len = TCP_OPT_LEN_2; + return tcp; +} + +struct tcp_stream *tcp_stream_get(int is_new, struct in_addr rhost, + u16 rport, u16 lport) { - return &tcp_stream; + struct tcp_stream *tcp = &tcp_stream; + + if (tcp->rhost.s_addr == rhost.s_addr && + tcp->rport == rport && + tcp->lport == lport) + return tcp; + + if (!is_new || !incoming_filter) || + !incoming_filter(rhost, rport, lport)) + return NULL; + + return tcp_stream_add(rhost, rport, lport); }
-static void dummy_handler(uchar *pkt, u16 dport, - struct in_addr sip, u16 sport, +static void dummy_handler(struct tcp_stream *tcp, uchar *pkt, u32 tcp_seq_num, u32 tcp_ack_num, u8 action, unsigned int len) { @@ -222,8 +278,7 @@ void net_set_syn_options(struct tcp_stream *tcp, union tcp_build_pkt *b) b->ip.end = TCP_O_END; }
-int tcp_set_tcp_header(struct tcp_stream *tcp, uchar *pkt, int dport, - int sport, int payload_len, +int tcp_set_tcp_header(struct tcp_stream *tcp, uchar *pkt, int payload_len, u8 action, u32 tcp_seq_num, u32 tcp_ack_num) { union tcp_build_pkt *b = (union tcp_build_pkt *)pkt; @@ -243,7 +298,7 @@ int tcp_set_tcp_header(struct tcp_stream *tcp, uchar *pkt, int dport, case TCP_SYN: debug_cond(DEBUG_DEV_PKT, "TCP Hdr:SYN (%pI4, %pI4, sq=%u, ak=%u)\n", - &net_server_ip, &net_ip, + &tcp->rhost, &net_ip, tcp_seq_num, tcp_ack_num); tcp_activity_count = 0; net_set_syn_options(tcp, b); @@ -264,13 +319,13 @@ int tcp_set_tcp_header(struct tcp_stream *tcp, uchar *pkt, int dport, b->ip.hdr.tcp_flags = action; debug_cond(DEBUG_DEV_PKT, "TCP Hdr:ACK (%pI4, %pI4, s=%u, a=%u, A=%x)\n", - &net_server_ip, &net_ip, tcp_seq_num, tcp_ack_num, + &tcp->rhost, &net_ip, tcp_seq_num, tcp_ack_num, action); break; case TCP_FIN: debug_cond(DEBUG_DEV_PKT, "TCP Hdr:FIN (%pI4, %pI4, s=%u, a=%u)\n", - &net_server_ip, &net_ip, tcp_seq_num, tcp_ack_num); + &tcp->rhost, &net_ip, tcp_seq_num, tcp_ack_num); payload_len = 0; pkt_hdr_len = IP_TCP_HDR_SIZE; tcp->state = TCP_FIN_WAIT_1; @@ -279,7 +334,7 @@ int tcp_set_tcp_header(struct tcp_stream *tcp, uchar *pkt, int dport, case TCP_RST: debug_cond(DEBUG_DEV_PKT, "TCP Hdr:RST (%pI4, %pI4, s=%u, a=%u)\n", - &net_server_ip, &net_ip, tcp_seq_num, tcp_ack_num); + &tcp->rhost, &net_ip, tcp_seq_num, tcp_ack_num); tcp->state = TCP_CLOSED; break; /* Notify connection closing */ @@ -290,7 +345,7 @@ int tcp_set_tcp_header(struct tcp_stream *tcp, uchar *pkt, int dport,
debug_cond(DEBUG_DEV_PKT, "TCP Hdr:FIN ACK PSH(%pI4, %pI4, s=%u, a=%u, A=%x)\n", - &net_server_ip, &net_ip, + &tcp->rhost, &net_ip, tcp_seq_num, tcp_ack_num, action); fallthrough; default: @@ -298,7 +353,7 @@ int tcp_set_tcp_header(struct tcp_stream *tcp, uchar *pkt, int dport, b->ip.hdr.tcp_flags = action | TCP_PUSH | TCP_ACK; debug_cond(DEBUG_DEV_PKT, "TCP Hdr:dft (%pI4, %pI4, s=%u, a=%u, A=%x)\n", - &net_server_ip, &net_ip, + &tcp->rhost, &net_ip, tcp_seq_num, tcp_ack_num, action); }
@@ -308,8 +363,8 @@ int tcp_set_tcp_header(struct tcp_stream *tcp, uchar *pkt, int dport, tcp->ack_edge = tcp_ack_num; /* TCP Header */ b->ip.hdr.tcp_ack = htonl(tcp->ack_edge); - b->ip.hdr.tcp_src = htons(sport); - b->ip.hdr.tcp_dst = htons(dport); + b->ip.hdr.tcp_src = htons(tcp->lport); + b->ip.hdr.tcp_dst = htons(tcp->rport); b->ip.hdr.tcp_seq = htonl(tcp_seq_num);
/* @@ -332,10 +387,10 @@ int tcp_set_tcp_header(struct tcp_stream *tcp, uchar *pkt, int dport, b->ip.hdr.tcp_xsum = 0; b->ip.hdr.tcp_ugr = 0;
- b->ip.hdr.tcp_xsum = tcp_set_pseudo_header(pkt, net_ip, net_server_ip, + b->ip.hdr.tcp_xsum = tcp_set_pseudo_header(pkt, net_ip, tcp->rhost, tcp_len, pkt_len);
- net_set_ip_header((uchar *)&b->ip, net_server_ip, net_ip, + net_set_ip_header((uchar *)&b->ip, tcp->rhost, net_ip, pkt_len, IPPROTO_TCP);
return pkt_hdr_len; @@ -616,19 +671,26 @@ void rxhand_tcp_f(union tcp_build_pkt *b, unsigned int pkt_len) u32 tcp_seq_num, tcp_ack_num; int tcp_hdr_len, payload_len; struct tcp_stream *tcp; + struct in_addr src;
/* Verify IP header */ debug_cond(DEBUG_DEV_PKT, "TCP RX in RX Sum (to=%pI4, from=%pI4, len=%d)\n", &b->ip.hdr.ip_src, &b->ip.hdr.ip_dst, pkt_len);
- b->ip.hdr.ip_src = net_server_ip; + /* + * src IP address will be destroyed by TCP checksum verification + * algorithm (see tcp_set_pseudo_header()), so remember it before + * it was garbaged. + */ + src.s_addr = b->ip.hdr.ip_src.s_addr; + b->ip.hdr.ip_dst = net_ip; b->ip.hdr.ip_sum = 0; if (tcp_rx_xsum != compute_ip_checksum(b, IP_HDR_SIZE)) { debug_cond(DEBUG_DEV_PKT, "TCP RX IP xSum Error (%pI4, =%pI4, len=%d)\n", - &net_ip, &net_server_ip, pkt_len); + &net_ip, &src, pkt_len); return; }
@@ -640,12 +702,15 @@ void rxhand_tcp_f(union tcp_build_pkt *b, unsigned int pkt_len) pkt_len)) { debug_cond(DEBUG_DEV_PKT, "TCP RX TCP xSum Error (%pI4, %pI4, len=%d)\n", - &net_ip, &net_server_ip, tcp_len); + &net_ip, &src, tcp_len); return; }
- tcp = tcp_stream_get(); - if (tcp == NULL) + tcp = tcp_stream_get(b->ip.hdr.tcp_flags & TCP_SYN, + src, + ntohs(b->ip.hdr.tcp_src), + ntohs(b->ip.hdr.tcp_dst)); + if (!tcp) return;
tcp_hdr_len = GET_TCP_HDR_LEN_IN_BYTES(b->ip.hdr.tcp_hlen); @@ -676,9 +741,9 @@ void rxhand_tcp_f(union tcp_build_pkt *b, unsigned int pkt_len) "TCP Notify (action=%x, Seq=%u,Ack=%u,Pay%d)\n", tcp_action, tcp_seq_num, tcp_ack_num, payload_len);
- (*tcp_packet_handler) ((uchar *)b + pkt_len - payload_len, b->ip.hdr.tcp_dst, - b->ip.hdr.ip_src, b->ip.hdr.tcp_src, tcp_seq_num, - tcp_ack_num, tcp_action, payload_len); + (*tcp_packet_handler) (tcp, (uchar *)b + pkt_len - payload_len, + tcp_seq_num, tcp_ack_num, tcp_action, + payload_len);
} else if (tcp_action != TCP_DATA) { debug_cond(DEBUG_DEV_PKT, @@ -689,9 +754,13 @@ void rxhand_tcp_f(union tcp_build_pkt *b, unsigned int pkt_len) * Warning: Incoming Ack & Seq sequence numbers are transposed * here to outgoing Seq & Ack sequence numbers */ - net_send_tcp_packet(0, ntohs(b->ip.hdr.tcp_src), - ntohs(b->ip.hdr.tcp_dst), + net_send_tcp_packet(0, tcp->rhost, tcp->rport, tcp->lport, (tcp_action & (~TCP_PUSH)), tcp_ack_num, tcp->ack_edge); } } + +struct tcp_stream *tcp_stream_connect(struct in_addr rhost, u16 rport) +{ + return tcp_stream_add(rhost, rport, random_port()); +} diff --git a/net/wget.c b/net/wget.c index 07992613c91..3638cbf9373 100644 --- a/net/wget.c +++ b/net/wget.c @@ -27,9 +27,8 @@ static const char bootfile3[] = " HTTP/1.0\r\n\r\n"; static const char http_eom[] = "\r\n\r\n"; static const char http_ok[] = "200"; static const char linefeed[] = "\r\n"; -static struct in_addr web_server_ip; -static int our_port; static int wget_timeout_count; +struct tcp_stream *tcp;
struct pkt_qd { uchar *pkt; @@ -108,22 +107,19 @@ static void wget_send_stored(void) int len = retry_len; unsigned int tcp_ack_num = retry_tcp_seq_num + (len == 0 ? 1 : len); unsigned int tcp_seq_num = retry_tcp_ack_num; - unsigned int server_port; uchar *ptr, *offset;
- server_port = env_get_ulong("httpdstp", 10, SERVER_PORT) & 0xffff; - switch (current_wget_state) { case WGET_CLOSED: debug_cond(DEBUG_WGET, "wget: send SYN\n"); current_wget_state = WGET_CONNECTING; - net_send_tcp_packet(0, server_port, our_port, action, + net_send_tcp_packet(0, tcp->rhost, tcp->rport, tcp->lport, action, tcp_seq_num, tcp_ack_num); packets = 0; break; case WGET_CONNECTING: pkt_q_idx = 0; - net_send_tcp_packet(0, server_port, our_port, action, + net_send_tcp_packet(0, tcp->rhost, tcp->rport, tcp->lport, action, tcp_seq_num, tcp_ack_num);
ptr = net_tx_packet + net_eth_hdr_size() + @@ -138,14 +134,14 @@ static void wget_send_stored(void)
memcpy(offset, &bootfile3, strlen(bootfile3)); offset += strlen(bootfile3); - net_send_tcp_packet((offset - ptr), server_port, our_port, + net_send_tcp_packet((offset - ptr), tcp->rhost, tcp->rport, tcp->lport, TCP_PUSH, tcp_seq_num, tcp_ack_num); current_wget_state = WGET_CONNECTED; break; case WGET_CONNECTED: case WGET_TRANSFERRING: case WGET_TRANSFERRED: - net_send_tcp_packet(0, server_port, our_port, action, + net_send_tcp_packet(0, tcp->rhost, tcp->rport, tcp->lport, action, tcp_seq_num, tcp_ack_num); break; } @@ -287,10 +283,8 @@ static void wget_connected(uchar *pkt, unsigned int tcp_seq_num,
/** * wget_handler() - TCP handler of wget + * @tcp: TCP stream * @pkt: pointer to the application packet - * @dport: destination TCP port - * @sip: source IP address - * @sport: source TCP port * @tcp_seq_num: TCP sequential number * @tcp_ack_num: TCP acknowledgment number * @action: TCP action (SYN, ACK, FIN, etc) @@ -299,13 +293,11 @@ static void wget_connected(uchar *pkt, unsigned int tcp_seq_num, * In the "application push" invocation, the TCP header with all * its information is pointed to by the packet pointer. */ -static void wget_handler(uchar *pkt, u16 dport, - struct in_addr sip, u16 sport, +static void wget_handler(struct tcp_stream *tcp, uchar *pkt, u32 tcp_seq_num, u32 tcp_ack_num, u8 action, unsigned int len) { - struct tcp_stream *tcp = tcp_stream_get(); - enum tcp_state wget_tcp_state = tcp_get_tcp_state(tcp); + enum tcp_state wget_tcp_state = tcp_stream_get_state(tcp);
net_set_timeout_handler(wget_timeout, wget_timeout_handler); packets++; @@ -393,26 +385,13 @@ static void wget_handler(uchar *pkt, u16 dport, } }
-#define RANDOM_PORT_START 1024 -#define RANDOM_PORT_RANGE 0x4000 - -/** - * random_port() - make port a little random (1024-17407) - * - * Return: random port number from 1024 to 17407 - * - * This keeps the math somewhat trivial to compute, and seems to work with - * all supported protocols/clients/servers - */ -static unsigned int random_port(void) -{ - return RANDOM_PORT_START + (get_timer(0) % RANDOM_PORT_RANGE); -} - #define BLOCKSIZE 512
void wget_start(void) { + struct in_addr web_server_ip; + unsigned int server_port; + image_url = strchr(net_boot_file_name, ':'); if (image_url > 0) { web_server_ip = string_to_ip(net_boot_file_name); @@ -456,8 +435,6 @@ void wget_start(void) wget_timeout_count = 0; current_wget_state = WGET_CLOSED;
- our_port = random_port(); - /* * Zero out server ether to force arp resolution in case * the server ip for the previous u-boot command, for example dns @@ -466,6 +443,13 @@ void wget_start(void)
memset(net_server_ethaddr, 0, 6);
+ server_port = env_get_ulong("httpdstp", 10, SERVER_PORT) & 0xffff; + tcp = tcp_stream_connect(web_server_ip, server_port); + if (tcp == NULL) { + net_set_state(NETLOOP_FAIL); + return; + } + wget_send(TCP_SYN, 0, 0, 0); }

Use the names from RFC 9293
Signed-off-by: Mikhail Kshevetskiy mikhail.kshevetskiy@iopsys.eu Reviewed-by: Simon Glass sjg@chromium.org --- include/net/tcp.h | 8 ++++---- net/tcp.c | 32 ++++++++++++++++---------------- 2 files changed, 20 insertions(+), 20 deletions(-)
diff --git a/include/net/tcp.h b/include/net/tcp.h index f224d0cae2f..0694af9d5b1 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -285,8 +285,8 @@ enum tcp_state { * * @state: TCP connection state * - * @seq_init: Initial receive sequence number - * @ack_edge: Receive next + * @irs: Initial receive sequence number + * @rcv_nxt: Receive next * * @loc_timestamp: Local timestamp * @rmt_timestamp: Remote timestamp @@ -301,8 +301,8 @@ struct tcp_stream { /* TCP connection state */ enum tcp_state state;
- u32 seq_init; - u32 ack_edge; + u32 irs; + u32 rcv_nxt;
/* TCP option timestamp */ u32 loc_timestamp; diff --git a/net/tcp.c b/net/tcp.c index 0c32c5d7c92..7e445eaffd6 100644 --- a/net/tcp.c +++ b/net/tcp.c @@ -360,9 +360,9 @@ int tcp_set_tcp_header(struct tcp_stream *tcp, uchar *pkt, int payload_len, pkt_len = pkt_hdr_len + payload_len; tcp_len = pkt_len - IP_HDR_SIZE;
- tcp->ack_edge = tcp_ack_num; + tcp->rcv_nxt = tcp_ack_num; /* TCP Header */ - b->ip.hdr.tcp_ack = htonl(tcp->ack_edge); + b->ip.hdr.tcp_ack = htonl(tcp->rcv_nxt); b->ip.hdr.tcp_src = htons(tcp->lport); b->ip.hdr.tcp_dst = htons(tcp->rport); b->ip.hdr.tcp_seq = htonl(tcp_seq_num); @@ -396,10 +396,10 @@ int tcp_set_tcp_header(struct tcp_stream *tcp, uchar *pkt, int payload_len, return pkt_hdr_len; }
-static void tcp_update_ack_edge(struct tcp_stream *tcp) +static void tcp_update_rcv_nxt(struct tcp_stream *tcp) { - if (tcp_seq_cmp(tcp->ack_edge, tcp->lost.hill[0].l) >= 0) { - tcp->ack_edge = tcp->lost.hill[0].r; + if (tcp_seq_cmp(tcp->rcv_nxt, tcp->lost.hill[0].l) >= 0) { + tcp->rcv_nxt = tcp->lost.hill[0].r;
memmove(&tcp->lost.hill[0], &tcp->lost.hill[1], (TCP_SACK_HILLS - 1) * sizeof(struct sack_edges)); @@ -434,7 +434,7 @@ void tcp_hole(struct tcp_stream *tcp, u32 tcp_seq_num, u32 len) tcp_seq_num = tcp->lost.hill[i].l; } if (tcp_seq_cmp(tcp->lost.hill[i].r, tcp_seq_num + len) >= 0) { - tcp_update_ack_edge(tcp); + tcp_update_rcv_nxt(tcp); return; }
@@ -463,12 +463,12 @@ void tcp_hole(struct tcp_stream *tcp, u32 tcp_seq_num, u32 len) } }
- tcp_update_ack_edge(tcp); + tcp_update_rcv_nxt(tcp); return; }
if (i == TCP_SACK_HILLS) { - tcp_update_ack_edge(tcp); + tcp_update_rcv_nxt(tcp); return; }
@@ -489,7 +489,7 @@ void tcp_hole(struct tcp_stream *tcp, u32 tcp_seq_num, u32 len) tcp->lost.hill[i].r = tcp_seq_num + len; tcp->lost.len = TCP_OPT_LEN_2 + cnt * TCP_OPT_LEN_8;
- tcp_update_ack_edge(tcp); + tcp_update_rcv_nxt(tcp); };
/** @@ -566,8 +566,8 @@ static u8 tcp_state_machine(struct tcp_stream *tcp, u8 tcp_flags, debug_cond(DEBUG_INT_STATE, "TCP CLOSED %x\n", tcp_flags); if (tcp_syn) { action = TCP_SYN | TCP_ACK; - tcp->seq_init = tcp_seq_num; - tcp->ack_edge = tcp_seq_num + 1; + tcp->irs = tcp_seq_num; + tcp->rcv_nxt = tcp_seq_num + 1; tcp->lost.len = TCP_OPT_LEN_2; tcp->state = TCP_SYN_RECEIVED; } else if (tcp_ack || tcp_fin) { @@ -583,8 +583,8 @@ static u8 tcp_state_machine(struct tcp_stream *tcp, u8 tcp_flags, tcp->state = TCP_CLOSE_WAIT; } else if (tcp_ack || (tcp_syn && tcp_ack)) { action |= TCP_ACK; - tcp->seq_init = tcp_seq_num; - tcp->ack_edge = tcp_seq_num + 1; + tcp->irs = tcp_seq_num; + tcp->rcv_nxt = tcp_seq_num + 1; tcp->state = TCP_ESTABLISHED;
if (tcp_syn && tcp_ack) @@ -633,7 +633,7 @@ static u8 tcp_state_machine(struct tcp_stream *tcp, u8 tcp_flags, case TCP_FIN_WAIT_1: debug_cond(DEBUG_INT_STATE, "TCP_FIN_WAIT_1 (%x)\n", tcp_flags); if (tcp_fin) { - tcp->ack_edge++; + tcp->rcv_nxt++; action = TCP_ACK | TCP_FIN; tcp->state = TCP_FIN_WAIT_2; } @@ -748,7 +748,7 @@ void rxhand_tcp_f(union tcp_build_pkt *b, unsigned int pkt_len) } else if (tcp_action != TCP_DATA) { debug_cond(DEBUG_DEV_PKT, "TCP Action (action=%x,Seq=%u,Ack=%u,Pay=%d)\n", - tcp_action, tcp_ack_num, tcp->ack_edge, payload_len); + tcp_action, tcp_ack_num, tcp->rcv_nxt, payload_len);
/* * Warning: Incoming Ack & Seq sequence numbers are transposed @@ -756,7 +756,7 @@ void rxhand_tcp_f(union tcp_build_pkt *b, unsigned int pkt_len) */ net_send_tcp_packet(0, tcp->rhost, tcp->rport, tcp->lport, (tcp_action & (~TCP_PUSH)), - tcp_ack_num, tcp->ack_edge); + tcp_ack_num, tcp->rcv_nxt); } }

Changes: * Fix initial send sequence always zero issue * Use state machine close to RFC 9293. This should make TCP transfers more reliable (now we can upload a huge array of data from the board to external server) * Improve TCP framework a lot. This should make tcp client code much more simple. * rewrite wget with new tcp stack * rewrite fastboot_tcp with new tcp stack
It's quite hard to fix the initial send sequence (ISS) issue with the separate patch. A naive attempt to fix an issue inside the tcp_set_tcp_header() function will break tcp packet retransmit logic in wget and other clients.
Example: Wget stores tcp_seq_num value before tcp_set_tcp_header() will be called and (on failure) retransmit the packet with the stored tcp_seq_num value. Thus: * the same ISS must allways be used (current case) * or tcp clients needs to generate a proper ISS when required.
A proper ISS fix will require a big redesing comparable with a this one.
Signed-off-by: Mikhail Kshevetskiy mikhail.kshevetskiy@iopsys.eu Reviewed-by: Simon Glass sjg@chromium.org --- include/net/tcp.h | 202 ++++++++-- include/net/wget.h | 8 - net/fastboot_tcp.c | 193 +++++----- net/net.c | 4 + net/tcp.c | 891 +++++++++++++++++++++++++++++++++++---------- net/wget.c | 450 +++++++---------------- 6 files changed, 1088 insertions(+), 660 deletions(-)
diff --git a/include/net/tcp.h b/include/net/tcp.h index 0694af9d5b1..55541e1b90e 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -265,6 +265,7 @@ union tcp_build_pkt { * @TCP_CLOSING: Rec FIN, sent FIN, ACK waiting for ACK * @TCP_FIN_WAIT_1: Sent FIN waiting for response * @TCP_FIN_WAIT_2: Rec ACK from FIN sent, waiting for FIN + * @TCP_LAST_ACK: Waiting for ACK of the connection termination */ enum tcp_state { TCP_CLOSED, @@ -274,7 +275,22 @@ enum tcp_state { TCP_CLOSE_WAIT, TCP_CLOSING, TCP_FIN_WAIT_1, - TCP_FIN_WAIT_2 + TCP_FIN_WAIT_2, + TCP_LAST_ACK, +}; + +/** + * enum tcp_status - TCP stream status for connection + * @TCP_ERR_OK: no rx/tx errors + * @TCP_ERR_TOUT: rx/tx timeout happened + * @TCP_ERR_RST: connection was reset + * @TCP_ERR_IO: input/output error + */ +enum tcp_status { + TCP_ERR_OK = 0, + TCP_ERR_TOUT, + TCP_ERR_RST, + TCP_ERR_IO };
/** @@ -283,51 +299,156 @@ enum tcp_state { * @rport: Remote port, host byte order * @lport: Local port, host byte order * + * @priv: User private data (not used by tcp module) + * + * @max_retry_count: Maximum retransmit attempts (default 3) + * @initial_timeout: Timeout from initial TX to reTX (default 2 sec) + * @rx_inactiv_timeout: Maximum time from last rx till connection drop + * (default 30 sec) + * + * @on_closed: User callback, called just before destroying TCP stream + * @on_established: User callback, called when TCP stream enters + * TCP_ESTABLISHED state + * @on_rcv_nxt_update: User callback, called when all data in the segment + * [0..rx_bytes - 1] was received + * @on_snd_una_update: User callback, called when all data in the segment + * [0..tx_bytes - 1] were transferred and acknowledged + * @rx: User callback, called on receive of segment + * [rx_offs..rx_offs+len-1]. If NULL -- all incoming data + * will be ignored. User SHOULD store the segment and + * return the number of accepted bytes or negative value + * on error. + * WARNING: Previous segmengs may not be received yet + * @tx: User callback, called on transmit/retransmit of segment + * [tx_offs..tx_offs+maxlen-1]. If NULL -- no data will + * be transmitted. User SHOULD fill provided buffer and + * return the number of bytes in the buffer or negative + * value on error. + * WARNING: do not use tcp_stream_close() from this + * callback (it will break stream). Better use + * on_snd_una_update() callback for such purposes. + * + * @time_last_rx: Arrival time of last valid incoming package (ticks) + * @time_start: Timeout start time (ticks) + * @time_delta: Timeout duration (ticks) + * @time_handler Timeout handler for a stream + * * @state: TCP connection state + * @status: TCP stream status (OK or ERR) + * @rx_packets: total number of received packets + * @tx_packets: total number of transmitted packets + * + * @fin_rx: Non-zero if TCP_FIN was received + * @fin_rx_seq: TCP sequence of rx FIN bit + * @fin_tx: Non-zero if TCP_FIN was sent (or planned to send) + * @fin_tx_seq: TCP sequence of tx FIN bit + * + * @iss: Initial send sequence number + * @snd_una: Send unacknowledged + * @snd_nxt: Send next + * @snd_wnd: Send window (in bytes) + * @snd_wl1: Segment sequence number used for last window update + * @snd_wl2: Segment acknowledgment number used for last window update * * @irs: Initial receive sequence number * @rcv_nxt: Receive next + * @rcv_wnd: Receive window (in bytes) * * @loc_timestamp: Local timestamp * @rmt_timestamp: Remote timestamp * + * @rmt_win_scale: Remote window scale factor + * * @lost: Used for SACK + * + * @retry_cnt: Number of retry attempts remaining. Only SYN, FIN + * or DATA segments are tried to retransmit. + * @retry_timeout: Current retry timeout (ms) + * @retry_action: TCP flags used for sending + * @retry_seq_num: TCP sequence for retransmit + * retry_tx_len: Number of data to transmit + * @retry_tx_offs: Position in the TX stream */ struct tcp_stream { struct in_addr rhost; u16 rport; u16 lport;
- /* TCP connection state */ + void *priv; + + int max_retry_count; + int initial_timeout; + int rx_inactiv_timeout; + + void (*on_closed)(struct tcp_stream *tcp); + void (*on_established)(struct tcp_stream *tcp); + void (*on_rcv_nxt_update)(struct tcp_stream *tcp, u32 rx_bytes); + void (*on_snd_una_update)(struct tcp_stream *tcp, u32 tx_bytes); + int (*rx)(struct tcp_stream *tcp, u32 rx_offs, void *buf, int len); + int (*tx)(struct tcp_stream *tcp, u32 tx_offs, void *buf, int maxlen); + + ulong time_last_rx; + ulong time_start; + ulong time_delta; + void (*time_handler)(struct tcp_stream *tcp); + enum tcp_state state; + enum tcp_status status; + u32 rx_packets; + u32 tx_packets; + + int fin_rx; + u32 fin_rx_seq; + + int fin_tx; + u32 fin_tx_seq; + + u32 iss; + u32 snd_una; + u32 snd_nxt; + u32 snd_wnd; + u32 snd_wl1; + u32 snd_wl2;
u32 irs; u32 rcv_nxt; + u32 rcv_wnd;
/* TCP option timestamp */ u32 loc_timestamp; u32 rmt_timestamp;
+ /* TCP window scale */ + u8 rmt_win_scale; + /* TCP sliding window control used to request re-TX */ struct tcp_sack_v lost; + + /* used for data retransmission */ + int retry_cnt; + int retry_timeout; + u8 retry_action; + u32 retry_seq_num; + u32 retry_tx_len; + u32 retry_tx_offs; };
void tcp_init(void);
-typedef int tcp_incoming_filter(struct in_addr rhost, - u16 rport, u16 sport); - /* - * This function sets user callback used to accept/drop incoming - * connections. Callback should: + * This function sets user callback called on TCP stream creation. + * Callback should: * + Check TCP stream endpoint and make connection verdict * - return non-zero value to accept connection * - return zero to drop connection + * + Setup TCP stream callbacks like: on_closed(), on_established(), + * n_rcv_nxt_update(), on_snd_una_update(), rx() and tx(). + * + Setup other stream related data * - * WARNING: If callback is NOT defined, all incoming connections - * will be dropped. + * WARNING: User MUST setup TCP stream on_create handler. Without it + * no connection (including outgoung) will be created. */ -void tcp_set_incoming_filter(tcp_incoming_filter *filter); +void tcp_stream_set_on_create_handler(int (*on_create)(struct tcp_stream *));
/* * tcp_stream_get -- Get or create TCP stream @@ -351,27 +472,52 @@ struct tcp_stream *tcp_stream_get(int is_new, struct in_addr rhost, */ struct tcp_stream *tcp_stream_connect(struct in_addr rhost, u16 rport);
-enum tcp_state tcp_stream_get_state(struct tcp_stream *tcp); +/* + * tcp_stream_put -- Return stream to a TCP subsystem. Subsystem will + * check stream and destroy it (if stream was already + * closed). Otherwize no stream change will happen. + * @tcp: TCP stream to put + */ +void tcp_stream_put(struct tcp_stream *tcp);
-int tcp_set_tcp_header(struct tcp_stream *tcp, uchar *pkt, int payload_len, - u8 action, u32 tcp_seq_num, u32 tcp_ack_num); +/* + * tcp_stream_restart_rx_timer -- Restart RX inactivity timer. Usually there + * is no needs to call this function. Timer + * will be restarted on receiving of any valid + * tcp packet belonging to a stream. + * + * This function may be used to prevent connection + * break in the following case: + * - u-boot is busy with very long data processing + * - remote side waits for u-boot reply + * + * @tcp: TCP stream to put + */ +void tcp_stream_restart_rx_timer(struct tcp_stream *tcp);
-/** - * rxhand_tcp() - An incoming packet handler. - * @tcp: TCP stream - * @pkt: pointer to the application packet - * @dport: destination TCP port - * @sip: source IP address - * @sport: source TCP port - * @tcp_seq_num: TCP sequential number - * @tcp_ack_num: TCP acknowledgment number - * @action: TCP action (SYN, ACK, FIN, etc) - * @len: packet length +enum tcp_state tcp_stream_get_state(struct tcp_stream *tcp); +enum tcp_status tcp_stream_get_status(struct tcp_stream *tcp); + + +/* + * tcp_stream_rx_offs(), + * tcp_stream_tx_offs() -- Returns offset of first unacknowledged byte + * in receive/transmit stream correspondingly. + * The result is NOT affected by sin/fin flags. + * @tcp: TCP stream */ -typedef void rxhand_tcp(struct tcp_stream *tcp, uchar *pkt, - u32 tcp_seq_num, u32 tcp_ack_num, - u8 action, unsigned int len); -void tcp_set_tcp_handler(rxhand_tcp *f); +u32 tcp_stream_rx_offs(struct tcp_stream *tcp); +u32 tcp_stream_tx_offs(struct tcp_stream *tcp); + +/* reset tcp stream */ +void tcp_stream_reset(struct tcp_stream *tcp); +/* force TCP stream closing, do NOT use from tcp->tx callback */ +void tcp_stream_close(struct tcp_stream *tcp); + +void tcp_streams_poll(void); + +int tcp_set_tcp_header(struct tcp_stream *tcp, uchar *pkt, int payload_len, + u8 action, u32 tcp_seq_num, u32 tcp_ack_num);
void rxhand_tcp_f(union tcp_build_pkt *b, unsigned int len);
diff --git a/include/net/wget.h b/include/net/wget.h index 6714f7ea573..9a423b30414 100644 --- a/include/net/wget.h +++ b/include/net/wget.h @@ -8,14 +8,6 @@ */ void wget_start(void);
-enum wget_state { - WGET_CLOSED, - WGET_CONNECTING, - WGET_CONNECTED, - WGET_TRANSFERRING, - WGET_TRANSFERRED -}; - #define DEBUG_WGET 0 /* Set to 1 for debug messages */ #define WGET_RETRY_COUNT 30 #define WGET_TIMEOUT 2000UL diff --git a/net/fastboot_tcp.c b/net/fastboot_tcp.c index 4d34fdc5a45..3ea25c997fc 100644 --- a/net/fastboot_tcp.c +++ b/net/fastboot_tcp.c @@ -10,140 +10,109 @@
#define FASTBOOT_TCP_PORT 5554
-static char command[FASTBOOT_COMMAND_LEN]; -static char response[FASTBOOT_RESPONSE_LEN]; - static const unsigned short handshake_length = 4; static const uchar *handshake = "FB01";
-static u32 curr_tcp_seq_num; -static u32 curr_tcp_ack_num; -static unsigned int curr_request_len; -static enum fastboot_tcp_state { - FASTBOOT_CLOSED, - FASTBOOT_CONNECTED, - FASTBOOT_DISCONNECTING -} state = FASTBOOT_CLOSED; - -static void fastboot_tcp_answer(struct tcp_stream *tcp, u8 action, - unsigned int len) -{ - const u32 response_seq_num = curr_tcp_ack_num; - const u32 response_ack_num = curr_tcp_seq_num + - (curr_request_len > 0 ? curr_request_len : 1); +static char rxbuf[sizeof(u64) + FASTBOOT_COMMAND_LEN + 1]; +static char txbuf[sizeof(u64) + FASTBOOT_RESPONSE_LEN + 1];
- net_send_tcp_packet(len, tcp->rhost, tcp->rport, tcp->lport, - action, response_seq_num, response_ack_num); -} +static u32 data_read; +static u32 tx_last_offs, tx_last_len;
-static void fastboot_tcp_reset(struct tcp_stream *tcp) +static void tcp_stream_on_rcv_nxt_update(struct tcp_stream *tcp, u32 rx_bytes) { - fastboot_tcp_answer(tcp, TCP_RST, 0); - state = FASTBOOT_CLOSED; -} + u64 cmd_size; + __be64 len_be; + char saved; + int fastboot_command_id, len; + + if (!data_read && rx_bytes >= handshake_length) { + if (memcmp(rxbuf, handshake, handshake_length)) { + printf("fastboot: bad handshake\n"); + tcp_stream_close(tcp); + return; + }
-static void fastboot_tcp_send_packet(struct tcp_stream *tcp, u8 action, - const uchar *data, unsigned int len) -{ - uchar *pkt = net_get_async_tx_pkt_buf(); + tx_last_offs = 0; + tx_last_len = handshake_length; + memcpy(txbuf, handshake, handshake_length); + + data_read += handshake_length; + rx_bytes -= handshake_length; + if (rx_bytes > 0) + memmove(rxbuf, rxbuf + handshake_length, rx_bytes); + return; + }
- memset(pkt, '\0', PKTSIZE); - pkt += net_eth_hdr_size() + IP_TCP_HDR_SIZE + TCP_TSOPT_SIZE + 2; - memcpy(pkt, data, len); - fastboot_tcp_answer(tcp, action, len); - memset(pkt, '\0', PKTSIZE); + if (rx_bytes < sizeof(u64)) + return; + + memcpy(&cmd_size, rxbuf, sizeof(u64)); + cmd_size = __be64_to_cpu(cmd_size); + if (rx_bytes < sizeof(u64) + cmd_size) + return; + + saved = rxbuf[sizeof(u64) + cmd_size]; + rxbuf[sizeof(u64) + cmd_size] = '\0'; + fastboot_command_id = fastboot_handle_command(rxbuf + sizeof(u64), + txbuf + sizeof(u64)); + fastboot_handle_boot(fastboot_command_id, + strncmp("OKAY", txbuf + sizeof(u64), 4) != 0); + rxbuf[sizeof(u64) + cmd_size] = saved; + + len = strlen(txbuf + sizeof(u64)); + len_be = __cpu_to_be64(len); + memcpy(txbuf, &len_be, sizeof(u64)); + + tx_last_offs += tx_last_len; + tx_last_len = len + sizeof(u64); + + data_read += sizeof(u64) + cmd_size; + rx_bytes -= sizeof(u64) + cmd_size; + if (rx_bytes > 0) + memmove(rxbuf, rxbuf + sizeof(u64) + cmd_size, rx_bytes); }
-static void fastboot_tcp_send_message(struct tcp_stream *tcp, - const char *message, unsigned int len) +static int tcp_stream_rx(struct tcp_stream *tcp, u32 rx_offs, void *buf, int len) { - __be64 len_be = __cpu_to_be64(len); - uchar *pkt = net_get_async_tx_pkt_buf(); - - memset(pkt, '\0', PKTSIZE); - pkt += net_eth_hdr_size() + IP_TCP_HDR_SIZE + TCP_TSOPT_SIZE + 2; - // Put first 8 bytes as a big endian message length - memcpy(pkt, &len_be, 8); - pkt += 8; - memcpy(pkt, message, len); - fastboot_tcp_answer(tcp, TCP_ACK | TCP_PUSH, len + 8); - memset(pkt, '\0', PKTSIZE); + memcpy(rxbuf + rx_offs - data_read, buf, len); + + return len; }
-static void fastboot_tcp_handler_ipv4(struct tcp_stream *tcp, uchar *pkt, - u32 tcp_seq_num, u32 tcp_ack_num, - u8 action, unsigned int len) +static int tcp_stream_tx(struct tcp_stream *tcp, u32 tx_offs, void *buf, int maxlen) { - int fastboot_command_id; - u64 command_size; - u8 tcp_fin = action & TCP_FIN; - u8 tcp_push = action & TCP_PUSH; - - curr_tcp_seq_num = tcp_seq_num; - curr_tcp_ack_num = tcp_ack_num; - curr_request_len = len; - - switch (state) { - case FASTBOOT_CLOSED: - if (tcp_push) { - if (len != handshake_length || - strlen(pkt) != handshake_length || - memcmp(pkt, handshake, handshake_length) != 0) { - fastboot_tcp_reset(tcp); - break; - } - fastboot_tcp_send_packet(tcp, TCP_ACK | TCP_PUSH, - handshake, handshake_length); - state = FASTBOOT_CONNECTED; - } - break; - case FASTBOOT_CONNECTED: - if (tcp_fin) { - fastboot_tcp_answer(tcp, TCP_FIN | TCP_ACK, 0); - state = FASTBOOT_DISCONNECTING; - break; - } - if (tcp_push) { - // First 8 bytes is big endian message length - command_size = __be64_to_cpu(*(u64 *)pkt); - len -= 8; - pkt += 8; - - // Only single packet messages are supported ATM - if (strlen(pkt) != command_size) { - fastboot_tcp_reset(tcp); - break; - } - strlcpy(command, pkt, len + 1); - fastboot_command_id = fastboot_handle_command(command, response); - fastboot_tcp_send_message(tcp, response, strlen(response)); - fastboot_handle_boot(fastboot_command_id, - strncmp("OKAY", response, 4) == 0); - } - break; - case FASTBOOT_DISCONNECTING: - if (tcp_push) - state = FASTBOOT_CLOSED; - break; - } + /* by design: tx_offs >= tx_last_offs */ + if (tx_offs >= tx_last_offs + tx_last_len) + return 0;
- memset(command, 0, FASTBOOT_COMMAND_LEN); - memset(response, 0, FASTBOOT_RESPONSE_LEN); - curr_tcp_seq_num = 0; - curr_tcp_ack_num = 0; - curr_request_len = 0; + maxlen = tx_last_offs + tx_last_len - tx_offs; + memcpy(buf, txbuf + (tx_offs - tx_last_offs), maxlen); + + return maxlen; }
-static int incoming_filter(struct in_addr rhost, u16 rport, u16 lport) +static int tcp_stream_on_create(struct tcp_stream *tcp) { - return (lport == FASTBOOT_TCP_PORT); + if (tcp->lport != FASTBOOT_TCP_PORT) + return 0; + + data_read = 0; + tx_last_offs = 0; + tx_last_len = 0; + + tcp->on_rcv_nxt_update = tcp_stream_on_rcv_nxt_update; + tcp->rx = tcp_stream_rx; + tcp->tx = tcp_stream_tx; + + return 1; }
void fastboot_tcp_start_server(void) { + memset(net_server_ethaddr, 0, 6); + tcp_stream_set_on_create_handler(tcp_stream_on_create); + printf("Using %s device\n", eth_get_name()); printf("Listening for fastboot command on tcp %pI4\n", &net_ip); - - tcp_set_incoming_filter(incoming_filter); - tcp_set_tcp_handler(fastboot_tcp_handler_ipv4); } diff --git a/net/net.c b/net/net.c index e5caf1d9334..aa1cb0b51cb 100644 --- a/net/net.c +++ b/net/net.c @@ -652,6 +652,9 @@ restart: * errors that may have happened. */ eth_rx(); +#if defined(CONFIG_PROT_TCP) + tcp_streams_poll(); +#endif
/* * Abort if ctrl-c was pressed. @@ -961,6 +964,7 @@ int net_send_ip_packet(uchar *ether, struct in_addr dest, int dport, int sport, + tcp_set_tcp_header(tcp, pkt + eth_hdr_size, payload_len, action, tcp_seq_num, tcp_ack_num); + tcp_stream_put(tcp); break; #endif default: diff --git a/net/tcp.c b/net/tcp.c index 7e445eaffd6..5a4ade01c77 100644 --- a/net/tcp.c +++ b/net/tcp.c @@ -24,9 +24,26 @@ #include <net.h> #include <net/tcp.h>
-static int tcp_activity_count; +/* + * The start sequence number increment for the two sequently created + * connections within the same timer tick. This number must be: + * - prime (to increase the time before the same number will be generated) + * - larger than typical MTU (to avoid similar numbers for two sequently + * created connections) + */ +#define TCP_START_SEQ_INC 2153 /* just large prime number */ + +#define TCP_SEND_RETRY 3 +#define TCP_SEND_TIMEOUT 2000UL +#define TCP_RX_INACTIVE_TIMEOUT 30000UL +#define TCP_RCV_WND_SIZE (PKTBUFSRX * TCP_MSS) + +#define TCP_PACKET_OK 0 +#define TCP_PACKET_DROP 1 + static struct tcp_stream tcp_stream; -static tcp_incoming_filter *incoming_filter; + +static int (*tcp_stream_on_create)(struct tcp_stream *tcp);
/* * TCP lengths are stored as a rounded up number of 32 bit words. @@ -35,12 +52,10 @@ static tcp_incoming_filter *incoming_filter; */ #define LEN_B_TO_DW(x) ((x) >> 2) #define ROUND_TCPHDR_LEN(x) (LEN_B_TO_DW((x) + 3)) +#define ROUND_TCPHDR_BYTES(x) (((x) + 3) & ~3) #define SHIFT_TO_TCPHDRLEN_FIELD(x) ((x) << 4) #define GET_TCP_HDR_LEN_IN_BYTES(x) ((x) >> 2)
-/* Current TCP RX packet handler */ -static rxhand_tcp *tcp_packet_handler; - #define RANDOM_PORT_START 1024 #define RANDOM_PORT_RANGE 0x4000
@@ -62,6 +77,22 @@ static inline s32 tcp_seq_cmp(u32 a, u32 b) return (s32)(a - b); }
+static inline u32 tcp_get_start_seq(void) +{ + static u32 tcp_seq_inc; + u32 tcp_seq; + + tcp_seq = (get_timer(0) & 0xffffffff) + tcp_seq_inc; + tcp_seq_inc += TCP_START_SEQ_INC; + + return tcp_seq; +} + +static inline ulong msec_to_ticks(ulong msec) +{ + return msec * CONFIG_SYS_HZ / 1000; +} + /** * tcp_stream_get_state() - get TCP stream state * @tcp: tcp stream @@ -84,31 +115,90 @@ static void tcp_stream_set_state(struct tcp_stream *tcp, tcp->state = new_state; }
-void tcp_init(void) +/** + * tcp_stream_get_status() - get TCP stream status + * @tcp: tcp stream + * + * Return: TCP stream status + */ +enum tcp_status tcp_stream_get_status(struct tcp_stream *tcp) { - incoming_filter = NULL; - tcp_stream.state = TCP_CLOSED; + return tcp->status; }
-void tcp_set_incoming_filter(tcp_incoming_filter *filter) +/** + * tcp_stream_set_status() - set TCP stream state + * @tcp: tcp stream + * @new_satus: new TCP stream status + */ +static void tcp_stream_set_status(struct tcp_stream *tcp, + enum tcp_state new_status) { - incoming_filter = filter; + tcp->status = new_status; }
-static struct tcp_stream *tcp_stream_add(struct in_addr rhost, - u16 rport, u16 lport) +void tcp_stream_restart_rx_timer(struct tcp_stream *tcp) { - struct tcp_stream *tcp = &tcp_stream; - - if (tcp->state != TCP_CLOSED) - return NULL; + tcp->time_last_rx = get_timer(0); +}
+static void tcp_stream_init(struct tcp_stream *tcp, + struct in_addr rhost, u16 rport, u16 lport) +{ memset(tcp, 0, sizeof(struct tcp_stream)); tcp->rhost.s_addr = rhost.s_addr; tcp->rport = rport; tcp->lport = lport; tcp->state = TCP_CLOSED; tcp->lost.len = TCP_OPT_LEN_2; + tcp->rcv_wnd = TCP_RCV_WND_SIZE; + tcp->max_retry_count = TCP_SEND_RETRY; + tcp->initial_timeout = TCP_SEND_TIMEOUT; + tcp->rx_inactiv_timeout = TCP_RX_INACTIVE_TIMEOUT; + tcp_stream_restart_rx_timer(tcp); +} + +static void tcp_stream_destroy(struct tcp_stream *tcp) +{ + if (tcp->on_closed) + tcp->on_closed(tcp); + memset(tcp, 0, sizeof(struct tcp_stream)); +} + +void tcp_init(void) +{ + static int initialized; + struct tcp_stream *tcp = &tcp_stream; + + tcp_stream_on_create = NULL; + if (!initialized) { + initialized = 1; + memset(tcp, 0, sizeof(struct tcp_stream)); + } + + tcp_stream_set_state(tcp, TCP_CLOSED); + tcp_stream_set_status(tcp, TCP_ERR_RST); + tcp_stream_destroy(tcp); +} + +void tcp_stream_set_on_create_handler(int (*on_create)(struct tcp_stream *)) +{ + tcp_stream_on_create = on_create; +} + +static struct tcp_stream *tcp_stream_add(struct in_addr rhost, + u16 rport, u16 lport) +{ + struct tcp_stream *tcp = &tcp_stream; + + if (!tcp_stream_on_create || + tcp->state != TCP_CLOSED) + return NULL; + + tcp_stream_init(tcp, rhost, rport, lport); + if (!tcp_stream_on_create(tcp)) + return NULL; + return tcp; }
@@ -122,30 +212,226 @@ struct tcp_stream *tcp_stream_get(int is_new, struct in_addr rhost, tcp->lport == lport) return tcp;
- if (!is_new || !incoming_filter) || - !incoming_filter(rhost, rport, lport)) - return NULL; + return is_new ? tcp_stream_add(rhost, rport, lport) : NULL; +}
- return tcp_stream_add(rhost, rport, lport); +void tcp_stream_put(struct tcp_stream *tcp) +{ + if (tcp->state == TCP_CLOSED) + tcp_stream_destroy(tcp); }
-static void dummy_handler(struct tcp_stream *tcp, uchar *pkt, - u32 tcp_seq_num, u32 tcp_ack_num, - u8 action, unsigned int len) +u32 tcp_stream_rx_offs(struct tcp_stream *tcp) { + u32 ret; + + switch (tcp->state) { + case TCP_CLOSED: + case TCP_SYN_SENT: + case TCP_SYN_RECEIVED: + return 0; + default: + break; + } + + ret = tcp->rcv_nxt - tcp->irs - 1; + if (tcp->fin_rx && (tcp->rcv_nxt == tcp->fin_rx_seq)) + ret--; + + return ret; }
-/** - * tcp_set_tcp_handler() - set a handler to receive data - * @f: handler - */ -void tcp_set_tcp_handler(rxhand_tcp *f) +u32 tcp_stream_tx_offs(struct tcp_stream *tcp) +{ + u32 ret; + + switch (tcp->state) { + case TCP_CLOSED: + case TCP_SYN_SENT: + case TCP_SYN_RECEIVED: + return 0; + default: + break; + } + + ret = tcp->snd_una - tcp->iss - 1; + if (tcp->fin_tx && (tcp->snd_una == tcp->fin_tx_seq + 1)) + ret--; + + return ret; +} + +static void tcp_stream_set_time_handler(struct tcp_stream *tcp, ulong msec, + void (*handler)(struct tcp_stream *)) +{ + if (!msec) { + tcp->time_handler = NULL; + return; + } + + tcp->time_handler = handler; + tcp->time_start = get_timer(0); + tcp->time_delta = msec_to_ticks(msec); +} + +static void tcp_send_packet(struct tcp_stream *tcp, u8 action, + u32 tcp_seq_num, u32 tcp_ack_num, u32 tx_len) +{ + tcp->tx_packets++; + net_send_tcp_packet(tx_len, tcp->rhost, tcp->rport, + tcp->lport, action, tcp_seq_num, + tcp_ack_num); +} + +static void tcp_send_repeat(struct tcp_stream *tcp) +{ + uchar *ptr; + u32 tcp_opts_size; + int ret; + + if (!tcp->retry_cnt) { + puts("\nTCP: send retry counter exceeded\n"); + tcp_send_packet(tcp, TCP_RST, tcp->retry_seq_num, + tcp->rcv_nxt, 0); + tcp_stream_set_status(tcp, TCP_ERR_TOUT); + tcp_stream_set_state(tcp, TCP_CLOSED); + tcp_stream_destroy(tcp); + return; + } + tcp->retry_cnt--; + tcp->retry_timeout += tcp->initial_timeout; + + if (tcp->retry_tx_len > 0) { + tcp_opts_size = ROUND_TCPHDR_BYTES(TCP_TSOPT_SIZE + + tcp->lost.len); + ptr = net_tx_packet + net_eth_hdr_size() + + IP_TCP_HDR_SIZE + tcp_opts_size; + + if (tcp->retry_tx_len > TCP_MSS - tcp_opts_size) + tcp->retry_tx_len = TCP_MSS - tcp_opts_size; + + /* refill packet data */ + ret = tcp->tx(tcp, tcp->retry_tx_offs, ptr, tcp->retry_tx_len); + if (ret < 0) { + puts("\nTCP: send failure\n"); + tcp_send_packet(tcp, TCP_RST, tcp->retry_seq_num, + tcp->rcv_nxt, 0); + tcp_stream_set_status(tcp, TCP_ERR_IO); + tcp_stream_set_state(tcp, TCP_CLOSED); + tcp_stream_destroy(tcp); + return; + } + } + tcp_send_packet(tcp, tcp->retry_action, tcp->retry_seq_num, + tcp->rcv_nxt, tcp->retry_tx_len); + + tcp_stream_set_time_handler(tcp, tcp->retry_timeout, tcp_send_repeat); +} + +static void tcp_send_packet_with_retry(struct tcp_stream *tcp, u8 action, + u32 tcp_seq_num, u32 tx_len, u32 tx_offs) +{ + tcp->retry_cnt = tcp->max_retry_count; + tcp->retry_timeout = tcp->initial_timeout; + tcp->retry_action = action; + tcp->retry_seq_num = tcp_seq_num; + tcp->retry_tx_len = tx_len; + tcp->retry_tx_offs = tx_offs; + + tcp_send_packet(tcp, action, tcp_seq_num, tcp->rcv_nxt, tx_len); + tcp_stream_set_time_handler(tcp, tcp->retry_timeout, tcp_send_repeat); +} + +static inline u8 tcp_stream_fin_needed(struct tcp_stream *tcp, u32 tcp_seq_num) +{ + return (tcp->fin_tx && (tcp_seq_num == tcp->fin_tx_seq)) ? TCP_FIN : 0; +} + +static void tcp_steam_tx_try(struct tcp_stream *tcp) { - debug_cond(DEBUG_INT_STATE, "--- net_loop TCP handler set (%p)\n", f); - if (!f) - tcp_packet_handler = dummy_handler; - else - tcp_packet_handler = f; + uchar *ptr; + int tx_len; + u32 tx_offs, tcp_opts_size; + + if (tcp->state != TCP_ESTABLISHED || + tcp->time_handler || + !tcp->tx) + return; + + tcp_opts_size = ROUND_TCPHDR_BYTES(TCP_TSOPT_SIZE + tcp->lost.len); + tx_len = TCP_MSS - tcp_opts_size; + if (tcp->fin_tx) { + /* do not try to send beyonds FIN packet limits */ + if (tcp_seq_cmp(tcp->snd_una, tcp->fin_tx_seq) >= 0) + return; + + tx_len = tcp->fin_tx_seq - tcp->snd_una; + if (tx_len > TCP_MSS - tcp_opts_size) + tx_len = TCP_MSS - tcp_opts_size; + } + + tx_offs = tcp_stream_tx_offs(tcp); + ptr = net_tx_packet + net_eth_hdr_size() + + IP_TCP_HDR_SIZE + tcp_opts_size; + + /* fill packet data and adjust size */ + tx_len = tcp->tx(tcp, tx_offs, ptr, tx_len); + if (tx_len < 0) { + puts("\nTCP: send failure\n"); + tcp_send_packet(tcp, TCP_RST, tcp->retry_seq_num, + tcp->rcv_nxt, 0); + tcp_stream_set_status(tcp, TCP_ERR_IO); + tcp_stream_set_state(tcp, TCP_CLOSED); + tcp_stream_destroy(tcp); + return; + } + if (!tx_len) + return; + + if (tcp_seq_cmp(tcp->snd_una + tx_len, tcp->snd_nxt) > 0) + tcp->snd_nxt = tcp->snd_una + tx_len; + + tcp_send_packet_with_retry(tcp, TCP_ACK | TCP_PUSH, + tcp->snd_una, tx_len, tx_offs); +} + +static void tcp_stream_poll(struct tcp_stream *tcp, ulong time) +{ + ulong delta; + void (*handler)(struct tcp_stream *tcp); + + if (tcp->state == TCP_CLOSED) + return; + + /* handle rx inactivity timeout */ + delta = msec_to_ticks(tcp->rx_inactiv_timeout); + if (time - tcp->time_last_rx >= delta) { + puts("\nTCP: rx inactivity timeout exceeded\n"); + tcp_stream_reset(tcp); + tcp_stream_set_status(tcp, TCP_ERR_TOUT); + tcp_stream_destroy(tcp); + return; + } + + /* handle retransmit timeout */ + if (tcp->time_handler && + time - tcp->time_start >= tcp->time_delta) { + handler = tcp->time_handler; + tcp->time_handler = NULL; + handler(tcp); + } + + tcp_steam_tx_try(tcp); +} + +void tcp_streams_poll(void) +{ + ulong time; + struct tcp_stream *tcp; + + time = get_timer(0); + tcp = &tcp_stream; + tcp_stream_poll(tcp, time); }
/** @@ -240,7 +526,6 @@ int net_set_ack_options(struct tcp_stream *tcp, union tcp_build_pkt *b) * This returns the actual rounded up length of the * TCP header to add to the total packet length */ - return GET_TCP_HDR_LEN_IN_BYTES(b->sack.hdr.tcp_hlen); }
@@ -300,18 +585,8 @@ int tcp_set_tcp_header(struct tcp_stream *tcp, uchar *pkt, int payload_len, "TCP Hdr:SYN (%pI4, %pI4, sq=%u, ak=%u)\n", &tcp->rhost, &net_ip, tcp_seq_num, tcp_ack_num); - tcp_activity_count = 0; net_set_syn_options(tcp, b); - tcp_seq_num = 0; - tcp_ack_num = 0; pkt_hdr_len = IP_TCP_O_SIZE; - if (tcp->state == TCP_SYN_SENT) { /* Too many SYNs */ - action = TCP_FIN; - tcp->state = TCP_FIN_WAIT_1; - } else { - tcp->lost.len = TCP_OPT_LEN_2; - tcp->state = TCP_SYN_SENT; - } break; case TCP_SYN | TCP_ACK: case TCP_ACK: @@ -328,21 +603,16 @@ int tcp_set_tcp_header(struct tcp_stream *tcp, uchar *pkt, int payload_len, &tcp->rhost, &net_ip, tcp_seq_num, tcp_ack_num); payload_len = 0; pkt_hdr_len = IP_TCP_HDR_SIZE; - tcp->state = TCP_FIN_WAIT_1; break; case TCP_RST | TCP_ACK: case TCP_RST: debug_cond(DEBUG_DEV_PKT, "TCP Hdr:RST (%pI4, %pI4, s=%u, a=%u)\n", &tcp->rhost, &net_ip, tcp_seq_num, tcp_ack_num); - tcp->state = TCP_CLOSED; break; /* Notify connection closing */ case (TCP_FIN | TCP_ACK): case (TCP_FIN | TCP_ACK | TCP_PUSH): - if (tcp->state == TCP_CLOSE_WAIT) - tcp->state = TCP_CLOSING; - debug_cond(DEBUG_DEV_PKT, "TCP Hdr:FIN ACK PSH(%pI4, %pI4, s=%u, a=%u, A=%x)\n", &tcp->rhost, &net_ip, @@ -382,7 +652,7 @@ int tcp_set_tcp_header(struct tcp_stream *tcp, uchar *pkt, int payload_len, * it is, then the u-boot tftp or nfs kernel netboot should be * considered. */ - b->ip.hdr.tcp_win = htons(PKTBUFSRX * TCP_MSS >> TCP_SCALE); + b->ip.hdr.tcp_win = htons(tcp->rcv_wnd >> TCP_SCALE);
b->ip.hdr.tcp_xsum = 0; b->ip.hdr.tcp_ugr = 0; @@ -501,6 +771,7 @@ void tcp_hole(struct tcp_stream *tcp, u32 tcp_seq_num, u32 len) void tcp_parse_options(struct tcp_stream *tcp, uchar *o, int o_len) { struct tcp_t_opt *tsopt; + struct tcp_scale *wsopt; uchar *p = o;
/* @@ -515,10 +786,13 @@ void tcp_parse_options(struct tcp_stream *tcp, uchar *o, int o_len) case TCP_O_END: return; case TCP_O_MSS: - case TCP_O_SCL: case TCP_P_SACK: case TCP_V_SACK: break; + case TCP_O_SCL: + wsopt = (struct tcp_scale *)p; + tcp->rmt_win_scale = wsopt->scale; + break; case TCP_O_TS: tsopt = (struct tcp_t_opt *)p; tcp->rmt_timestamp = tsopt->t_snd; @@ -533,129 +807,344 @@ void tcp_parse_options(struct tcp_stream *tcp, uchar *o, int o_len) } }
-static u8 tcp_state_machine(struct tcp_stream *tcp, u8 tcp_flags, - u32 tcp_seq_num, int payload_len) +static int tcp_seg_in_wnd(struct tcp_stream *tcp, + u32 tcp_seq_num, int payload_len) { - u8 tcp_fin = tcp_flags & TCP_FIN; - u8 tcp_syn = tcp_flags & TCP_SYN; - u8 tcp_rst = tcp_flags & TCP_RST; - u8 tcp_push = tcp_flags & TCP_PUSH; - u8 tcp_ack = tcp_flags & TCP_ACK; - u8 action = TCP_DATA; + if (!payload_len && !tcp->rcv_wnd) { + if (tcp_seq_num == tcp->rcv_nxt) + return 1; + } + if (!payload_len && tcp->rcv_wnd > 0) { + if (tcp_seq_cmp(tcp->rcv_nxt, tcp_seq_num) <= 0 && + tcp_seq_cmp(tcp_seq_num, tcp->rcv_nxt + tcp->rcv_wnd) < 0) + return 1; + } + if (payload_len > 0 && tcp->rcv_wnd > 0) { + if (tcp_seq_cmp(tcp->rcv_nxt, tcp_seq_num) <= 0 && + tcp_seq_cmp(tcp_seq_num, tcp->rcv_nxt + tcp->rcv_wnd) < 0) + return 1; + tcp_seq_num += payload_len - 1; + if (tcp_seq_cmp(tcp->rcv_nxt, tcp_seq_num) <= 0 && + tcp_seq_cmp(tcp_seq_num, tcp->rcv_nxt + tcp->rcv_wnd) < 0) + return 1; + } + + return 0; +} + +static int tcp_rx_check_ack_num(struct tcp_stream *tcp, u32 tcp_seq_num, + u32 tcp_ack_num, u32 tcp_win_size) +{ + u32 old_offs, new_offs; + u8 action; + + switch (tcp->state) { + case TCP_SYN_RECEIVED: + if (tcp_seq_cmp(tcp->snd_una, tcp_ack_num) >= 0 || + tcp_seq_cmp(tcp_ack_num, tcp->snd_nxt) > 0) { + // segment acknowledgment is not acceptable + tcp_send_packet(tcp, TCP_RST, tcp_ack_num, 0, 0); + return TCP_PACKET_DROP; + }
+ tcp_stream_set_state(tcp, TCP_ESTABLISHED); + tcp->snd_wnd = tcp_win_size; + tcp->snd_wl1 = tcp_seq_num; + tcp->snd_wl2 = tcp_ack_num; + + if (tcp->on_established) + tcp->on_established(tcp); + + fallthrough; + + case TCP_ESTABLISHED: + case TCP_FIN_WAIT_1: + case TCP_FIN_WAIT_2: + case TCP_CLOSE_WAIT: + case TCP_CLOSING: + if (tcp_seq_cmp(tcp_ack_num, tcp->snd_nxt) > 0) { + // ACK acks something not yet sent + action = tcp_stream_fin_needed(tcp, tcp->snd_una) | TCP_ACK; + tcp_send_packet(tcp, action, tcp->snd_una, tcp->rcv_nxt, 0); + return TCP_PACKET_DROP; + } + + if (tcp_seq_cmp(tcp->snd_una, tcp_ack_num) < 0) { + old_offs = tcp_stream_tx_offs(tcp); + tcp->snd_una = tcp_ack_num; + new_offs = tcp_stream_tx_offs(tcp); + if (tcp->time_handler && + tcp_seq_cmp(tcp->snd_una, tcp->retry_seq_num) > 0) { + tcp_stream_set_time_handler(tcp, 0, NULL); + } + if (tcp->on_snd_una_update && + old_offs != new_offs) + tcp->on_snd_una_update(tcp, new_offs); + } + + if (tcp_seq_cmp(tcp->snd_una, tcp_ack_num) <= 0) { + if (tcp_seq_cmp(tcp->snd_wl1, tcp_seq_num) < 0 || + (tcp->snd_wl1 == tcp_seq_num && + tcp_seq_cmp(tcp->snd_wl2, tcp_seq_num) <= 0)) { + tcp->snd_wnd = tcp_win_size; + tcp->snd_wl1 = tcp_seq_num; + tcp->snd_wl2 = tcp_ack_num; + } + } + + if (tcp->state == TCP_FIN_WAIT_1) { + if (tcp->snd_una == tcp->snd_nxt) + tcp_stream_set_state(tcp, TCP_FIN_WAIT_2); + } + + if (tcp->state == TCP_CLOSING) { + if (tcp->snd_una == tcp->snd_nxt) + tcp_stream_set_state(tcp, TCP_CLOSED); + } + return TCP_PACKET_OK; + + case TCP_LAST_ACK: + if (tcp_ack_num == tcp->snd_nxt) + tcp_stream_set_state(tcp, TCP_CLOSED); + return TCP_PACKET_OK; + + default: + return TCP_PACKET_DROP; + } +} + +static int tcp_rx_user_data(struct tcp_stream *tcp, u32 tcp_seq_num, + char *buf, int len) +{ + int tmp_len; + u32 buf_offs, old_offs, new_offs; + u8 action; + + if (!len) + return TCP_PACKET_OK; + + switch (tcp->state) { + case TCP_ESTABLISHED: + case TCP_FIN_WAIT_1: + case TCP_FIN_WAIT_2: + break; + default: + return TCP_PACKET_DROP; + } + + tmp_len = len; + old_offs = tcp_stream_rx_offs(tcp); + buf_offs = tcp_seq_num - tcp->irs - 1; + if (tcp->rx) { + tmp_len = tcp->rx(tcp, buf_offs, buf, len); + if (tmp_len < 0) { + puts("\nTCP: receive failure\n"); + tcp_send_packet(tcp, TCP_RST, tcp->snd_una, + tcp->rcv_nxt, 0); + tcp_stream_set_status(tcp, TCP_ERR_IO); + tcp_stream_set_state(tcp, TCP_CLOSED); + tcp_stream_destroy(tcp); + return TCP_PACKET_DROP; + } + } + if (tmp_len) + tcp_hole(tcp, tcp_seq_num, tmp_len); + + new_offs = tcp_stream_rx_offs(tcp); + if (tcp->on_rcv_nxt_update && old_offs != new_offs) + tcp->on_rcv_nxt_update(tcp, new_offs); + + action = tcp_stream_fin_needed(tcp, tcp->snd_una) | TCP_ACK; + tcp_send_packet(tcp, action, tcp->snd_una, tcp->rcv_nxt, 0); + + return TCP_PACKET_OK; +} + +void tcp_rx_state_machine(struct tcp_stream *tcp, + union tcp_build_pkt *b, unsigned int pkt_len) +{ + int tcp_len = pkt_len - IP_HDR_SIZE; + u32 tcp_seq_num, tcp_ack_num, tcp_win_size; + int tcp_hdr_len, payload_len; + u8 tcp_flags, action; + + tcp_hdr_len = GET_TCP_HDR_LEN_IN_BYTES(b->ip.hdr.tcp_hlen); + payload_len = tcp_len - tcp_hdr_len; + + if (tcp_hdr_len > TCP_HDR_SIZE) + tcp_parse_options(tcp, (uchar *)b + IP_TCP_HDR_SIZE, + tcp_hdr_len - TCP_HDR_SIZE); /* - * tcp_flags are examined to determine TX action in a given state - * tcp_push is interpreted to mean "inform the app" - * urg, ece, cer and nonce flags are not supported. - * - * exe and crw are use to signal and confirm knowledge of congestion. - * This TCP only sends a file request and acks. If it generates - * congestion, the network is broken. + * Incoming sequence and ack numbers are server's view of the numbers. + * The app must swap the numbers when responding. */ - debug_cond(DEBUG_INT_STATE, "TCP STATE ENTRY %x\n", action); - if (tcp_rst) { - action = TCP_DATA; - tcp->state = TCP_CLOSED; - net_set_state(NETLOOP_FAIL); - debug_cond(DEBUG_INT_STATE, "TCP Reset %x\n", tcp_flags); - return TCP_RST; - } + tcp_seq_num = ntohl(b->ip.hdr.tcp_seq); + tcp_ack_num = ntohl(b->ip.hdr.tcp_ack); + tcp_win_size = ntohs(b->ip.hdr.tcp_win) << tcp->rmt_win_scale;
- switch (tcp->state) { + tcp_flags = b->ip.hdr.tcp_flags; + +// printf("pkt: seq=%d, ack=%d, flags=%x, len=%d\n", +// tcp_seq_num - tcp->irs, tcp_ack_num - tcp->iss, tcp_flags, pkt_len); +// printf("tcp: rcv_nxt=%d, snd_una=%d, snd_nxt=%d\n\n", +// tcp->rcv_nxt - tcp->irs, tcp->snd_una - tcp->iss, tcp->snd_nxt - tcp->iss); + + switch (tcp->state) { case TCP_CLOSED: - debug_cond(DEBUG_INT_STATE, "TCP CLOSED %x\n", tcp_flags); - if (tcp_syn) { - action = TCP_SYN | TCP_ACK; - tcp->irs = tcp_seq_num; - tcp->rcv_nxt = tcp_seq_num + 1; - tcp->lost.len = TCP_OPT_LEN_2; - tcp->state = TCP_SYN_RECEIVED; - } else if (tcp_ack || tcp_fin) { - action = TCP_DATA; + if (tcp_flags & TCP_RST) + return; + + if (tcp_flags & TCP_ACK) { + tcp_send_packet(tcp, TCP_RST, tcp_ack_num, 0, 0); + return; } - break; - case TCP_SYN_RECEIVED: + + if (!(tcp_flags & TCP_SYN)) + return; + + tcp->irs = tcp_seq_num; + tcp->rcv_nxt = tcp->irs + 1; + + tcp->iss = tcp_get_start_seq(); + tcp->snd_una = tcp->iss; + tcp->snd_nxt = tcp->iss + 1; + tcp->snd_wnd = tcp_win_size; + + tcp_stream_restart_rx_timer(tcp); + + tcp_stream_set_state(tcp, TCP_SYN_RECEIVED); + tcp_send_packet_with_retry(tcp, TCP_SYN | TCP_ACK, + tcp->iss, 0, 0); + return; + case TCP_SYN_SENT: - debug_cond(DEBUG_INT_STATE, "TCP_SYN_SENT | TCP_SYN_RECEIVED %x, %u\n", - tcp_flags, tcp_seq_num); - if (tcp_fin) { - action = action | TCP_PUSH; - tcp->state = TCP_CLOSE_WAIT; - } else if (tcp_ack || (tcp_syn && tcp_ack)) { - action |= TCP_ACK; - tcp->irs = tcp_seq_num; - tcp->rcv_nxt = tcp_seq_num + 1; - tcp->state = TCP_ESTABLISHED; - - if (tcp_syn && tcp_ack) - action |= TCP_PUSH; - } else { - action = TCP_DATA; + if (!(tcp_flags & TCP_ACK)) + return; + + if (tcp_seq_cmp(tcp_ack_num, tcp->iss) <= 0 || + tcp_seq_cmp(tcp_ack_num, tcp->snd_nxt) > 0) { + if (!(tcp_flags & TCP_RST)) + tcp_send_packet(tcp, TCP_RST, tcp_ack_num, 0, 0); + return; } - break; + + if (tcp_flags & TCP_RST) { + tcp_stream_set_status(tcp, TCP_ERR_RST); + tcp_stream_set_state(tcp, TCP_CLOSED); + return; + } + + if (!(tcp_flags & TCP_SYN)) + return; + + /* stop retransmit of SYN */ + tcp_stream_set_time_handler(tcp, 0, NULL); + + tcp->irs = tcp_seq_num; + tcp->rcv_nxt = tcp->irs + 1; + tcp->snd_una = tcp_ack_num; + + tcp_stream_restart_rx_timer(tcp); + + /* our SYN has been ACKed */ + tcp_stream_set_state(tcp, TCP_ESTABLISHED); + + if (tcp->on_established) + tcp->on_established(tcp); + + action = tcp_stream_fin_needed(tcp, tcp->snd_una) | TCP_ACK; + tcp_send_packet(tcp, action, tcp->snd_una, tcp->rcv_nxt, 0); + tcp_rx_user_data(tcp, tcp_seq_num, + ((char *)b) + pkt_len - payload_len, + payload_len); + return; + + case TCP_SYN_RECEIVED: case TCP_ESTABLISHED: - debug_cond(DEBUG_INT_STATE, "TCP_ESTABLISHED %x\n", tcp_flags); - if (payload_len > 0) { - tcp_hole(tcp, tcp_seq_num, payload_len); - tcp_fin = TCP_DATA; /* cause standalone FIN */ + case TCP_FIN_WAIT_1: + case TCP_FIN_WAIT_2: + case TCP_CLOSE_WAIT: + case TCP_CLOSING: + case TCP_LAST_ACK: + if (!tcp_seg_in_wnd(tcp, tcp_seq_num, payload_len)) { + if (tcp_flags & TCP_RST) + return; + action = tcp_stream_fin_needed(tcp, tcp->snd_una) | TCP_ACK; + tcp_send_packet(tcp, action, tcp->snd_una, tcp->rcv_nxt, 0); + return; }
- if ((tcp_fin) && - (!IS_ENABLED(CONFIG_PROT_TCP_SACK) || - tcp->lost.len <= TCP_OPT_LEN_2)) { - action = action | TCP_FIN | TCP_PUSH | TCP_ACK; - tcp->state = TCP_CLOSE_WAIT; - } else if (tcp_ack) { - action = TCP_DATA; + tcp_stream_restart_rx_timer(tcp); + + if (tcp_flags & TCP_RST) { + tcp_stream_set_status(tcp, TCP_ERR_RST); + tcp_stream_set_state(tcp, TCP_CLOSED); + return; }
- if (tcp_syn) - action = TCP_ACK + TCP_RST; - else if (tcp_push) - action = action | TCP_PUSH; - break; - case TCP_CLOSE_WAIT: - debug_cond(DEBUG_INT_STATE, "TCP_CLOSE_WAIT (%x)\n", tcp_flags); - action = TCP_DATA; - break; - case TCP_FIN_WAIT_2: - debug_cond(DEBUG_INT_STATE, "TCP_FIN_WAIT_2 (%x)\n", tcp_flags); - if (tcp_ack) { - action = TCP_PUSH | TCP_ACK; - tcp->state = TCP_CLOSED; - puts("\n"); - } else if (tcp_syn) { - action = TCP_DATA; - } else if (tcp_fin) { - action = TCP_DATA; + if (tcp_flags & TCP_SYN) { + tcp_send_packet(tcp, TCP_RST, tcp_ack_num, 0, 0); + tcp_stream_set_status(tcp, TCP_ERR_RST); + tcp_stream_set_state(tcp, TCP_CLOSED); + return; } - break; - case TCP_FIN_WAIT_1: - debug_cond(DEBUG_INT_STATE, "TCP_FIN_WAIT_1 (%x)\n", tcp_flags); - if (tcp_fin) { - tcp->rcv_nxt++; - action = TCP_ACK | TCP_FIN; - tcp->state = TCP_FIN_WAIT_2; + + if (!(tcp_flags & TCP_ACK)) + return; + + if (tcp_rx_check_ack_num(tcp, tcp_seq_num, tcp_ack_num, + tcp_win_size) == TCP_PACKET_DROP) { + return; } - if (tcp_syn) - action = TCP_RST; - if (tcp_ack) - tcp->state = TCP_CLOSED; - break; - case TCP_CLOSING: - debug_cond(DEBUG_INT_STATE, "TCP_CLOSING (%x)\n", tcp_flags); - if (tcp_ack) { - action = TCP_PUSH; - tcp->state = TCP_CLOSED; - puts("\n"); - } else if (tcp_syn) { - action = TCP_RST; - } else if (tcp_fin) { - action = TCP_DATA; + + if (tcp_rx_user_data(tcp, tcp_seq_num, + ((char *)b) + pkt_len - payload_len, + payload_len) == TCP_PACKET_DROP) { + return; + } + + if (tcp_flags & TCP_FIN) { + tcp->fin_rx = 1; + tcp->fin_rx_seq = tcp_seq_num + payload_len + 1; + tcp_hole(tcp, tcp_seq_num + payload_len, 1); + action = tcp_stream_fin_needed(tcp, tcp->snd_una) | TCP_ACK; + tcp_send_packet(tcp, action, tcp->snd_una, tcp->rcv_nxt, 0); + } + + if (tcp->fin_rx && + tcp->fin_rx_seq == tcp->rcv_nxt) { + /* all rx data were processed */ + switch (tcp->state) { + case TCP_ESTABLISHED: + tcp_stream_set_state(tcp, TCP_LAST_ACK); + tcp_send_packet_with_retry(tcp, TCP_ACK | TCP_FIN, + tcp->snd_nxt, 0, 0); + tcp->snd_nxt++; + break; + + case TCP_FIN_WAIT_1: + if (tcp_ack_num == tcp->snd_nxt) + tcp_stream_set_state(tcp, TCP_CLOSED); + else + tcp_stream_set_state(tcp, TCP_CLOSING); + break; + + case TCP_FIN_WAIT_2: + tcp_stream_set_state(tcp, TCP_CLOSED); + break; + + default: + break; + } + } + + if (tcp->state == TCP_FIN_WAIT_1 && + tcp_stream_fin_needed(tcp, tcp->snd_una)) { + /* all tx data were acknowledged */ + tcp_send_packet_with_retry(tcp, TCP_ACK | TCP_FIN, + tcp->snd_una, 0, 0); } - break; } - return action; }
/** @@ -667,9 +1156,6 @@ void rxhand_tcp_f(union tcp_build_pkt *b, unsigned int pkt_len) { int tcp_len = pkt_len - IP_HDR_SIZE; u16 tcp_rx_xsum = b->ip.hdr.ip_sum; - u8 tcp_action = TCP_DATA; - u32 tcp_seq_num, tcp_ack_num; - int tcp_hdr_len, payload_len; struct tcp_stream *tcp; struct in_addr src;
@@ -713,54 +1199,59 @@ void rxhand_tcp_f(union tcp_build_pkt *b, unsigned int pkt_len) if (!tcp) return;
- tcp_hdr_len = GET_TCP_HDR_LEN_IN_BYTES(b->ip.hdr.tcp_hlen); - payload_len = tcp_len - tcp_hdr_len; + tcp->rx_packets++; + tcp_rx_state_machine(tcp, b, pkt_len); + tcp_stream_put(tcp); +}
- if (tcp_hdr_len > TCP_HDR_SIZE) - tcp_parse_options(tcp, (uchar *)b + IP_TCP_HDR_SIZE, - tcp_hdr_len - TCP_HDR_SIZE); - /* - * Incoming sequence and ack numbers are server's view of the numbers. - * The app must swap the numbers when responding. - */ - tcp_seq_num = ntohl(b->ip.hdr.tcp_seq); - tcp_ack_num = ntohl(b->ip.hdr.tcp_ack); +struct tcp_stream *tcp_stream_connect(struct in_addr rhost, u16 rport) +{ + struct tcp_stream *tcp;
- /* Packets are not ordered. Send to app as received. */ - tcp_action = tcp_state_machine(tcp, b->ip.hdr.tcp_flags, - tcp_seq_num, payload_len); + tcp = tcp_stream_add(rhost, rport, random_port()); + if (!tcp) + return NULL;
- tcp_activity_count++; - if (tcp_activity_count > TCP_ACTIVITY) { - puts("| "); - tcp_activity_count = 0; - } + tcp->iss = tcp_get_start_seq(); + tcp->snd_una = tcp->iss; + tcp->snd_nxt = tcp->iss + 1;
- if ((tcp_action & TCP_PUSH) || payload_len > 0) { - debug_cond(DEBUG_DEV_PKT, - "TCP Notify (action=%x, Seq=%u,Ack=%u,Pay%d)\n", - tcp_action, tcp_seq_num, tcp_ack_num, payload_len); + tcp_stream_set_state(tcp, TCP_SYN_SENT); + tcp_send_packet_with_retry(tcp, TCP_SYN, tcp->snd_una, 0, 0);
- (*tcp_packet_handler) (tcp, (uchar *)b + pkt_len - payload_len, - tcp_seq_num, tcp_ack_num, tcp_action, - payload_len); + return tcp; +}
- } else if (tcp_action != TCP_DATA) { - debug_cond(DEBUG_DEV_PKT, - "TCP Action (action=%x,Seq=%u,Ack=%u,Pay=%d)\n", - tcp_action, tcp_ack_num, tcp->rcv_nxt, payload_len); - - /* - * Warning: Incoming Ack & Seq sequence numbers are transposed - * here to outgoing Seq & Ack sequence numbers - */ - net_send_tcp_packet(0, tcp->rhost, tcp->rport, tcp->lport, - (tcp_action & (~TCP_PUSH)), - tcp_ack_num, tcp->rcv_nxt); - } +void tcp_stream_reset(struct tcp_stream *tcp) +{ + if (tcp->state == TCP_CLOSED) + return; + + tcp_stream_set_time_handler(tcp, 0, NULL); + tcp_send_packet(tcp, TCP_RST, tcp->snd_una, 0, 0); + tcp_stream_set_status(tcp, TCP_ERR_RST); + tcp_stream_set_state(tcp, TCP_CLOSED); }
-struct tcp_stream *tcp_stream_connect(struct in_addr rhost, u16 rport) +void tcp_stream_close(struct tcp_stream *tcp) { - return tcp_stream_add(rhost, rport, random_port()); + switch (tcp->state) { + case TCP_SYN_SENT: + tcp_stream_reset(tcp); + break; + case TCP_SYN_RECEIVED: + case TCP_ESTABLISHED: + tcp->fin_tx = 1; + tcp->fin_tx_seq = tcp->snd_nxt; + if (tcp_stream_fin_needed(tcp, tcp->snd_una)) { + /* all tx data were acknowledged */ + tcp_send_packet_with_retry(tcp, TCP_ACK | TCP_FIN, + tcp->snd_una, 0, 0); + } + tcp_stream_set_state(tcp, TCP_FIN_WAIT_1); + tcp->snd_nxt++; + break; + default: + break; + } } diff --git a/net/wget.c b/net/wget.c index 3638cbf9373..c114c4238c2 100644 --- a/net/wget.c +++ b/net/wget.c @@ -22,46 +22,25 @@ DECLARE_GLOBAL_DATA_PTR; /* The default, change with environment variable 'httpdstp' */ #define SERVER_PORT 80
+#define HASHES_PER_LINE 65 + +#define HTTP_MAX_HDR_LEN 2048 + static const char bootfile1[] = "GET "; static const char bootfile3[] = " HTTP/1.0\r\n\r\n"; static const char http_eom[] = "\r\n\r\n"; -static const char http_ok[] = "200"; +static const char http_ok[] = " 200 "; +static const char content_len[] = "Content-Length:"; static const char linefeed[] = "\r\n"; -static int wget_timeout_count; -struct tcp_stream *tcp; - -struct pkt_qd { - uchar *pkt; - unsigned int tcp_seq_num; - unsigned int len; -}; - -/* - * This is a control structure for out of order packets received. - * The actual packet bufers are in the kernel space, and are - * expected to be overwritten by the downloaded image. - */ -#define PKTQ_SZ (PKTBUFSRX / 4) -static struct pkt_qd pkt_q[PKTQ_SZ]; -static int pkt_q_idx; -static unsigned int packets; - -static unsigned int initial_data_seq_num; -static unsigned int next_data_seq_num; - -static enum wget_state current_wget_state; +static struct in_addr web_server_ip; +static unsigned int server_port; +static unsigned long content_length; +static u32 http_hdr_size; +static int wget_tsize_num_hash;
static char *image_url; -static unsigned int wget_timeout = WGET_TIMEOUT; - static enum net_loop_state wget_loop_state;
-/* Timeout retry parameters */ -static u8 retry_action; /* actions for TCP retry */ -static unsigned int retry_tcp_ack_num; /* TCP retry acknowledge number*/ -static unsigned int retry_tcp_seq_num; /* TCP retry sequence number */ -static int retry_len; /* TCP retry length */ - /** * store_block() - store block in memory * @src: source of data @@ -71,7 +50,6 @@ static int retry_len; /* TCP retry length */ static inline int store_block(uchar *src, unsigned int offset, unsigned int len) { ulong store_addr = image_load_addr + offset; - ulong newsize = offset + len; uchar *ptr;
if (CONFIG_IS_ENABLED(LMB)) { @@ -87,310 +65,158 @@ static inline int store_block(uchar *src, unsigned int offset, unsigned int len) memcpy(ptr, src, len); unmap_sysmem(ptr);
- if (net_boot_file_size < (offset + len)) - net_boot_file_size = newsize; - return 0; }
-/** - * wget_send_stored() - wget response dispatcher - * - * WARNING, This, and only this, is the place in wget.c where - * SEQUENCE NUMBERS are swapped between incoming (RX) - * and outgoing (TX). - * Procedure wget_handler() is correct for RX traffic. - */ -static void wget_send_stored(void) +static void show_block_marker(u32 packets) { - u8 action = retry_action; - int len = retry_len; - unsigned int tcp_ack_num = retry_tcp_seq_num + (len == 0 ? 1 : len); - unsigned int tcp_seq_num = retry_tcp_ack_num; - uchar *ptr, *offset; - - switch (current_wget_state) { - case WGET_CLOSED: - debug_cond(DEBUG_WGET, "wget: send SYN\n"); - current_wget_state = WGET_CONNECTING; - net_send_tcp_packet(0, tcp->rhost, tcp->rport, tcp->lport, action, - tcp_seq_num, tcp_ack_num); - packets = 0; - break; - case WGET_CONNECTING: - pkt_q_idx = 0; - net_send_tcp_packet(0, tcp->rhost, tcp->rport, tcp->lport, action, - tcp_seq_num, tcp_ack_num); - - ptr = net_tx_packet + net_eth_hdr_size() + - IP_TCP_HDR_SIZE + TCP_TSOPT_SIZE + 2; - offset = ptr; - - memcpy(offset, &bootfile1, strlen(bootfile1)); - offset += strlen(bootfile1); - - memcpy(offset, image_url, strlen(image_url)); - offset += strlen(image_url); - - memcpy(offset, &bootfile3, strlen(bootfile3)); - offset += strlen(bootfile3); - net_send_tcp_packet((offset - ptr), tcp->rhost, tcp->rport, tcp->lport, - TCP_PUSH, tcp_seq_num, tcp_ack_num); - current_wget_state = WGET_CONNECTED; - break; - case WGET_CONNECTED: - case WGET_TRANSFERRING: - case WGET_TRANSFERRED: - net_send_tcp_packet(0, tcp->rhost, tcp->rport, tcp->lport, action, - tcp_seq_num, tcp_ack_num); - break; + int cnt; + + if (content_length != -1) { + if (net_boot_file_size > content_length) + content_length = net_boot_file_size; + + cnt = net_boot_file_size * 50 / content_length; + while (wget_tsize_num_hash < cnt) { + putc('#'); + wget_tsize_num_hash++; + } + } else { + if ((packets % 10) == 0) + putc('#'); + else if (((packets + 1) % (10 * HASHES_PER_LINE)) == 0) + puts("\n"); } }
-static void wget_send(u8 action, unsigned int tcp_seq_num, - unsigned int tcp_ack_num, int len) +static void tcp_stream_on_closed(struct tcp_stream *tcp) { - retry_action = action; - retry_tcp_ack_num = tcp_ack_num; - retry_tcp_seq_num = tcp_seq_num; - retry_len = len; + if (tcp->status != TCP_ERR_OK) + wget_loop_state = NETLOOP_FAIL;
- wget_send_stored(); -} + net_set_state(wget_loop_state); + if (wget_loop_state != NETLOOP_SUCCESS) { + printf("\nwget: Transfer Fail, TCP status - %d\n", tcp->status); + return; + }
-void wget_fail(char *error_message, unsigned int tcp_seq_num, - unsigned int tcp_ack_num, u8 action) -{ - printf("wget: Transfer Fail - %s\n", error_message); - net_set_timeout_handler(0, NULL); - wget_send(action, tcp_seq_num, tcp_ack_num, 0); + printf("\nPackets received %d, Transfer Successful\n", tcp->rx_packets); + efi_set_bootdev("Net", "", image_url, + map_sysmem(image_load_addr, 0), + net_boot_file_size); + env_set_hex("filesize", net_boot_file_size); }
-/* - * Interfaces of U-BOOT - */ -static void wget_timeout_handler(void) +static void tcp_stream_on_rcv_nxt_update(struct tcp_stream *tcp, u32 rx_bytes) { - if (++wget_timeout_count > WGET_RETRY_COUNT) { - puts("\nRetry count exceeded; starting again\n"); - wget_send(TCP_RST, 0, 0, 0); - net_start_again(); - } else { - puts("T "); - net_set_timeout_handler(wget_timeout + - WGET_TIMEOUT * wget_timeout_count, - wget_timeout_handler); - wget_send_stored(); - } -} + char *pos, *tail; + uchar saved, *ptr; + int i;
-#define PKT_QUEUE_OFFSET 0x20000 -#define PKT_QUEUE_PACKET_SIZE 0x800 + if (http_hdr_size) { + net_boot_file_size = rx_bytes - http_hdr_size; + show_block_marker(tcp->rx_packets); + return; + }
-static void wget_connected(uchar *pkt, unsigned int tcp_seq_num, - u8 action, unsigned int tcp_ack_num, unsigned int len) -{ - uchar *pkt_in_q; - char *pos; - int hlen, i; - uchar *ptr1; + ptr = map_sysmem(image_load_addr, rx_bytes + 1);
- pkt[len] = '\0'; - pos = strstr((char *)pkt, http_eom); + saved = ptr[rx_bytes]; + ptr[rx_bytes] = '\0'; + pos = strstr((char *)ptr, http_eom); + ptr[rx_bytes] = saved;
if (!pos) { - debug_cond(DEBUG_WGET, - "wget: Connected, data before Header %p\n", pkt); - pkt_in_q = (void *)image_load_addr + PKT_QUEUE_OFFSET + - (pkt_q_idx * PKT_QUEUE_PACKET_SIZE); - - ptr1 = map_sysmem((ulong)pkt_in_q, len); - memcpy(ptr1, pkt, len); - unmap_sysmem(ptr1); + if (rx_bytes < HTTP_MAX_HDR_LEN && + tcp->state == TCP_ESTABLISHED) + goto end; + + printf("ERROR: misssed HTTP header\n"); + tcp_stream_close(tcp); + wget_loop_state = NETLOOP_FAIL; + goto end; + }
- pkt_q[pkt_q_idx].pkt = pkt_in_q; - pkt_q[pkt_q_idx].tcp_seq_num = tcp_seq_num; - pkt_q[pkt_q_idx].len = len; - pkt_q_idx++; + http_hdr_size = pos - (char *)ptr + strlen(http_eom); + *pos = '\0'; + + pos = strstr((char *)ptr, linefeed); + if (pos) + i = pos - (char *)ptr; + else + i = http_hdr_size - strlen(http_eom); + printf("%.*s\n", i, ptr); + + if (!strstr((char *)ptr, http_ok)) { + debug_cond(DEBUG_WGET, "wget: Connected Bad Xfer\n"); + tcp_stream_close(tcp); + wget_loop_state = NETLOOP_FAIL; + goto end; + }
- if (pkt_q_idx >= PKTQ_SZ) { - printf("wget: Fatal error, queue overrun!\n"); - net_set_state(NETLOOP_FAIL); + debug_cond(DEBUG_WGET, "wget: Connctd pkt %p hlen %x\n", + ptr, http_hdr_size);
- return; - } + pos = strstr((char *)ptr, content_len); + if (!pos) { + content_length = -1; } else { - debug_cond(DEBUG_WGET, "wget: Connected HTTP Header %p\n", pkt); - /* sizeof(http_eom) - 1 is the string length of (http_eom) */ - hlen = pos - (char *)pkt + sizeof(http_eom) - 1; - pos = strstr((char *)pkt, linefeed); - if (pos > 0) - i = pos - (char *)pkt; - else - i = hlen; - printf("%.*s", i, pkt); - - current_wget_state = WGET_TRANSFERRING; - - initial_data_seq_num = tcp_seq_num + hlen; - next_data_seq_num = tcp_seq_num + len; - - if (strstr((char *)pkt, http_ok) == 0) { - debug_cond(DEBUG_WGET, - "wget: Connected Bad Xfer\n"); - wget_loop_state = NETLOOP_FAIL; - wget_send(action, tcp_seq_num, tcp_ack_num, len); - } else { - debug_cond(DEBUG_WGET, - "wget: Connected Pkt %p hlen %x\n", - pkt, hlen); - - net_boot_file_size = 0; - - if (len > hlen) { - if (store_block(pkt + hlen, 0, len - hlen) != 0) { - wget_loop_state = NETLOOP_FAIL; - wget_fail("wget: store error\n", tcp_seq_num, tcp_ack_num, action); - net_set_state(NETLOOP_FAIL); - return; - } - } - - for (i = 0; i < pkt_q_idx; i++) { - int err; - - ptr1 = map_sysmem((ulong)pkt_q[i].pkt, - pkt_q[i].len); - err = store_block(ptr1, - pkt_q[i].tcp_seq_num - - initial_data_seq_num, - pkt_q[i].len); - unmap_sysmem(ptr1); - debug_cond(DEBUG_WGET, - "wget: Conncted pkt Q %p len %x\n", - pkt_q[i].pkt, pkt_q[i].len); - if (err) { - wget_loop_state = NETLOOP_FAIL; - wget_fail("wget: store error\n", tcp_seq_num, tcp_ack_num, action); - net_set_state(NETLOOP_FAIL); - return; - } - } - } + pos += strlen(content_len) + 1; + content_length = simple_strtoul(pos, &tail, 10); + if (*tail != '\r' && *tail != '\n' && *tail != '\0') + content_length = -1; + printf("%s %d\n", content_len, (int)content_length); } - wget_send(action, tcp_seq_num, tcp_ack_num, len); + + net_boot_file_size = rx_bytes - http_hdr_size; + memmove(ptr, ptr + http_hdr_size, net_boot_file_size); + +end: + unmap_sysmem(ptr); + + wget_loop_state = NETLOOP_SUCCESS; }
-/** - * wget_handler() - TCP handler of wget - * @tcp: TCP stream - * @pkt: pointer to the application packet - * @tcp_seq_num: TCP sequential number - * @tcp_ack_num: TCP acknowledgment number - * @action: TCP action (SYN, ACK, FIN, etc) - * @len: packet length - * - * In the "application push" invocation, the TCP header with all - * its information is pointed to by the packet pointer. - */ -static void wget_handler(struct tcp_stream *tcp, uchar *pkt, - u32 tcp_seq_num, u32 tcp_ack_num, - u8 action, unsigned int len) +static int tcp_stream_rx(struct tcp_stream *tcp, u32 rx_offs, void *buf, int len) { - enum tcp_state wget_tcp_state = tcp_stream_get_state(tcp); - - net_set_timeout_handler(wget_timeout, wget_timeout_handler); - packets++; - - switch (current_wget_state) { - case WGET_CLOSED: - debug_cond(DEBUG_WGET, "wget: Handler: Error!, State wrong\n"); - break; - case WGET_CONNECTING: - debug_cond(DEBUG_WGET, - "wget: Connecting In len=%x, Seq=%u, Ack=%u\n", - len, tcp_seq_num, tcp_ack_num); - if (!len) { - if (wget_tcp_state == TCP_ESTABLISHED) { - debug_cond(DEBUG_WGET, - "wget: Cting, send, len=%x\n", len); - wget_send(action, tcp_seq_num, tcp_ack_num, - len); - } else { - printf("%.*s", len, pkt); - wget_fail("wget: Handler Connected Fail\n", - tcp_seq_num, tcp_ack_num, action); - } - } - break; - case WGET_CONNECTED: - debug_cond(DEBUG_WGET, "wget: Connected seq=%u, len=%x\n", - tcp_seq_num, len); - if (!len) { - wget_fail("Image not found, no data returned\n", - tcp_seq_num, tcp_ack_num, action); - } else { - wget_connected(pkt, tcp_seq_num, action, tcp_ack_num, len); - } - break; - case WGET_TRANSFERRING: - debug_cond(DEBUG_WGET, - "wget: Transferring, seq=%x, ack=%x,len=%x\n", - tcp_seq_num, tcp_ack_num, len); - - if (next_data_seq_num != tcp_seq_num) { - debug_cond(DEBUG_WGET, "wget: seq=%x packet was lost\n", next_data_seq_num); - return; - } - next_data_seq_num = tcp_seq_num + len; + store_block(buf, rx_offs - http_hdr_size, len);
- if (store_block(pkt, tcp_seq_num - initial_data_seq_num, len) != 0) { - wget_fail("wget: store error\n", - tcp_seq_num, tcp_ack_num, action); - net_set_state(NETLOOP_FAIL); - return; - } + return len; +}
- switch (wget_tcp_state) { - case TCP_FIN_WAIT_2: - wget_send(TCP_ACK, tcp_seq_num, tcp_ack_num, len); - fallthrough; - case TCP_SYN_SENT: - case TCP_SYN_RECEIVED: - case TCP_CLOSING: - case TCP_FIN_WAIT_1: - case TCP_CLOSED: - net_set_state(NETLOOP_FAIL); - break; - case TCP_ESTABLISHED: - wget_send(TCP_ACK, tcp_seq_num, tcp_ack_num, - len); - wget_loop_state = NETLOOP_SUCCESS; - break; - case TCP_CLOSE_WAIT: /* End of transfer */ - current_wget_state = WGET_TRANSFERRED; - wget_send(action | TCP_ACK | TCP_FIN, - tcp_seq_num, tcp_ack_num, len); - break; - } - break; - case WGET_TRANSFERRED: - printf("Packets received %d, Transfer Successful\n", packets); - net_set_state(wget_loop_state); - efi_set_bootdev("Net", "", image_url, - map_sysmem(image_load_addr, 0), - net_boot_file_size); - env_set_hex("filesize", net_boot_file_size); - break; - } +static int tcp_stream_tx(struct tcp_stream *tcp, u32 tx_offs, void *buf, int maxlen) +{ + int ret; + + if (tx_offs) + return 0; + + ret = snprintf(buf, maxlen, "%s%s%s", bootfile1, image_url, bootfile3); + + return ret; +} + +static int tcp_stream_on_create(struct tcp_stream *tcp) +{ + if (tcp->rhost.s_addr != web_server_ip.s_addr || + tcp->rport != server_port) + return 0; + + tcp->max_retry_count = WGET_RETRY_COUNT; + tcp->initial_timeout = WGET_TIMEOUT; + tcp->on_closed = tcp_stream_on_closed; + tcp->on_rcv_nxt_update = tcp_stream_on_rcv_nxt_update; + tcp->rx = tcp_stream_rx; + tcp->tx = tcp_stream_tx; + + return 1; }
#define BLOCKSIZE 512
void wget_start(void) { - struct in_addr web_server_ip; - unsigned int server_port; + struct tcp_stream *tcp;
image_url = strchr(net_boot_file_name, ':'); if (image_url > 0) { @@ -429,12 +255,6 @@ void wget_start(void) debug_cond(DEBUG_WGET, "\nwget:Load address: 0x%lx\nLoading: *\b", image_load_addr);
- net_set_timeout_handler(wget_timeout, wget_timeout_handler); - tcp_set_tcp_handler(wget_handler); - - wget_timeout_count = 0; - current_wget_state = WGET_CLOSED; - /* * Zero out server ether to force arp resolution in case * the server ip for the previous u-boot command, for example dns @@ -443,14 +263,20 @@ void wget_start(void)
memset(net_server_ethaddr, 0, 6);
+ net_boot_file_size = 0; + http_hdr_size = 0; + wget_tsize_num_hash = 0; + wget_loop_state = NETLOOP_FAIL; + server_port = env_get_ulong("httpdstp", 10, SERVER_PORT) & 0xffff; + tcp_stream_set_on_create_handler(tcp_stream_on_create); tcp = tcp_stream_connect(web_server_ip, server_port); - if (tcp == NULL) { + if (!tcp) { + printf("No free tcp streams\n"); net_set_state(NETLOOP_FAIL); return; } - - wget_send(TCP_SYN, 0, 0, 0); + tcp_stream_put(tcp); }
#if (IS_ENABLED(CONFIG_CMD_DNS))

Changes: * update to new tcp stack * fix zero values for ISS and IRS issue (see RFC 9293)
Previously this patch also fix incorrect values for tcp_ack & tcp_seq, but the issue was fixed in * dbb6b5a: sandbox: fix wget test failure after fixing wget issue
Signed-off-by: Mikhail Kshevetskiy mikhail.kshevetskiy@iopsys.eu Reviewed-by: Simon Glass sjg@chromium.org --- arch/sandbox/include/asm/eth.h | 4 +++ test/cmd/wget.c | 58 ++++++++++++++++------------------ 2 files changed, 32 insertions(+), 30 deletions(-)
diff --git a/arch/sandbox/include/asm/eth.h b/arch/sandbox/include/asm/eth.h index f042a5f3b92..083a7371a3f 100644 --- a/arch/sandbox/include/asm/eth.h +++ b/arch/sandbox/include/asm/eth.h @@ -77,6 +77,8 @@ typedef int sandbox_eth_tx_hand_f(struct udevice *dev, void *pkt, * fake_host_hwaddr - MAC address of mocked machine * fake_host_ipaddr - IP address of mocked machine * disabled - Will not respond + * irs - tcp initial receive sequence + * iss - tcp initial send sequence * recv_packet_buffer - buffers of the packet returned as received * recv_packet_length - lengths of the packet returned as received * recv_packets - number of packets returned @@ -87,6 +89,8 @@ struct eth_sandbox_priv { uchar fake_host_hwaddr[ARP_HLEN]; struct in_addr fake_host_ipaddr; bool disabled; + u32 irs; + u32 iss; uchar * recv_packet_buffer[PKTBUFSRX]; int recv_packet_length[PKTBUFSRX]; int recv_packets; diff --git a/test/cmd/wget.c b/test/cmd/wget.c index fe26fee54c9..a90ca4fad8c 100644 --- a/test/cmd/wget.c +++ b/test/cmd/wget.c @@ -25,8 +25,7 @@
#define SHIFT_TO_TCPHDRLEN_FIELD(x) ((x) << 4) #define LEN_B_TO_DW(x) ((x) >> 2) - -int net_set_ack_options(union tcp_build_pkt *b); +#define GET_TCP_HDR_LEN_IN_BYTES(x) ((x) >> 2)
static int sb_arp_handler(struct udevice *dev, void *packet, unsigned int len) @@ -64,12 +63,14 @@ static int sb_syn_handler(struct udevice *dev, void *packet, eth_send = (void *)priv->recv_packet_buffer[priv->recv_packets]; memcpy(eth_send->et_dest, eth->et_src, ARP_HLEN); memcpy(eth_send->et_src, priv->fake_host_hwaddr, ARP_HLEN); + priv->irs = ntohl(tcp->tcp_seq); + priv->iss = ~priv->irs; /* just to differ from irs */ eth_send->et_protlen = htons(PROT_IP); tcp_send = (void *)eth_send + ETHER_HDR_SIZE; tcp_send->tcp_src = tcp->tcp_dst; tcp_send->tcp_dst = tcp->tcp_src; - tcp_send->tcp_seq = htonl(0); - tcp_send->tcp_ack = htonl(ntohl(tcp->tcp_seq) + 1); + tcp_send->tcp_seq = htonl(priv->iss); + tcp_send->tcp_ack = htonl(priv->irs + 1); tcp_send->tcp_hlen = SHIFT_TO_TCPHDRLEN_FIELD(LEN_B_TO_DW(TCP_HDR_SIZE)); tcp_send->tcp_flags = TCP_SYN | TCP_ACK; tcp_send->tcp_win = htons(PKTBUFSRX * TCP_MSS >> TCP_SCALE); @@ -104,13 +105,11 @@ static int sb_ack_handler(struct udevice *dev, void *packet, void *data; int pkt_len; int payload_len = 0; + u32 tcp_seq, tcp_ack; + int tcp_data_len; const char *payload1 = "HTTP/1.1 200 OK\r\n" "Content-Length: 30\r\n\r\n\r\n" "<html><body>Hi</body></html>\r\n"; - union tcp_build_pkt *b = (union tcp_build_pkt *)tcp; - const int recv_payload_len = len - net_set_ack_options(b) - IP_HDR_SIZE - ETHER_HDR_SIZE; - static int next_seq; - const int bottom_payload_len = 10;
/* Don't allow the buffer to overrun */ if (priv->recv_packets >= PKTBUFSRX) @@ -125,35 +124,32 @@ static int sb_ack_handler(struct udevice *dev, void *packet, tcp_send->tcp_dst = tcp->tcp_src; data = (void *)tcp_send + IP_TCP_HDR_SIZE;
- if (ntohl(tcp->tcp_seq) == 1 && ntohl(tcp->tcp_ack) == 1 && recv_payload_len == 0) { - // ignore ACK for three-way handshaking - return 0; - } else if (ntohl(tcp->tcp_seq) == 1 && ntohl(tcp->tcp_ack) == 1) { - // recv HTTP request message and reply top half data - tcp_send->tcp_seq = htonl(ntohl(tcp->tcp_ack)); - tcp_send->tcp_ack = htonl(ntohl(tcp->tcp_seq) + recv_payload_len); + tcp_seq = ntohl(tcp->tcp_seq) - priv->irs; + tcp_ack = ntohl(tcp->tcp_ack) - priv->iss; + tcp_data_len = len - ETHER_HDR_SIZE - IP_HDR_SIZE - GET_TCP_HDR_LEN_IN_BYTES(tcp->tcp_hlen);
- payload_len = strlen(payload1) - bottom_payload_len; - memcpy(data, payload1, payload_len); - tcp_send->tcp_flags = TCP_ACK; + if (tcp->tcp_flags & TCP_FIN) + tcp_data_len++;
- next_seq = ntohl(tcp_send->tcp_seq) + payload_len; - } else if (ntohl(tcp->tcp_ack) == next_seq) { - // reply bottom half data - const int top_payload_len = strlen(payload1) - bottom_payload_len; + tcp_send->tcp_seq = htonl(ntohl(tcp->tcp_ack)); + tcp_send->tcp_ack = htonl(ntohl(tcp->tcp_seq) + tcp_data_len);
- tcp_send->tcp_seq = htonl(next_seq); - tcp_send->tcp_ack = htonl(ntohl(tcp->tcp_seq) + recv_payload_len); + if (tcp_seq == 1 && tcp_ack == 1) { + if (tcp_data_len == 0) { + /* no data, wait for GET request */ + return -1; + }
- payload_len = bottom_payload_len; - memcpy(data, payload1 + top_payload_len, payload_len); + /* reply to GET request */ + payload_len = strlen(payload1); + memcpy(data, payload1, payload_len); tcp_send->tcp_flags = TCP_ACK; - } else { - // close connection - tcp_send->tcp_seq = htonl(ntohl(tcp->tcp_ack)); - tcp_send->tcp_ack = htonl(ntohl(tcp->tcp_seq) + 1); + } else if (tcp_ack == 1 + strlen(payload1)) { payload_len = 0; tcp_send->tcp_flags = TCP_ACK | TCP_FIN; + } else if (tcp_ack == 2 + strlen(payload1)) { + payload_len = 0; + tcp_send->tcp_flags = TCP_ACK; }
tcp_send->tcp_hlen = SHIFT_TO_TCPHDRLEN_FIELD(LEN_B_TO_DW(TCP_HDR_SIZE)); @@ -214,6 +210,8 @@ static int net_test_wget(struct unit_test_state *uts) env_set("loadaddr", "0x20000"); ut_assertok(run_command("wget ${loadaddr} 1.1.2.2:/index.html", 0)); ut_assert_nextline("HTTP/1.1 200 OK"); + ut_assert_nextline("Content-Length: 30"); + ut_assert_nextline(""); ut_assert_nextline("Packets received 5, Transfer Successful"); ut_assert_nextline("Bytes transferred = 32 (20 hex)");

This patch: * remove useless code, * use a special function for pretty printing of tcp flags, * simplify the code
The behavior should not be changed.
Signed-off-by: Mikhail Kshevetskiy mikhail.kshevetskiy@iopsys.eu Reviewed-by: Simon Glass sjg@chromium.org --- net/tcp.c | 66 +++++++++++++++++++++++++++---------------------------- 1 file changed, 33 insertions(+), 33 deletions(-)
diff --git a/net/tcp.c b/net/tcp.c index 5a4ade01c77..153f5183b25 100644 --- a/net/tcp.c +++ b/net/tcp.c @@ -563,10 +563,33 @@ void net_set_syn_options(struct tcp_stream *tcp, union tcp_build_pkt *b) b->ip.end = TCP_O_END; }
+const char *tcpflags_to_str(char tcpflags, char *buf, int size) +{ + int i; + static const struct { + int bit; + const char *name; + } desc[] = {{TCP_RST, "RST"}, {TCP_SYN, "SYN"}, {TCP_PUSH, "PSH"}, + {TCP_FIN, "FIN"}, {TCP_ACK, "ACK"}}; + + *buf = '\0'; + for (i = 0; i < ARRAY_SIZE(desc); i++) { + if (!(tcpflags & desc[i].bit)) + continue; + + if (*buf) + strlcat(buf, ",", size); + strlcat(buf, desc[i].name, size); + } + + return buf; +} + int tcp_set_tcp_header(struct tcp_stream *tcp, uchar *pkt, int payload_len, u8 action, u32 tcp_seq_num, u32 tcp_ack_num) { union tcp_build_pkt *b = (union tcp_build_pkt *)pkt; + char buf[24]; int pkt_hdr_len; int pkt_len; int tcp_len; @@ -576,55 +599,32 @@ int tcp_set_tcp_header(struct tcp_stream *tcp, uchar *pkt, int payload_len, * 4 bits reserved options */ b->ip.hdr.tcp_flags = action; - pkt_hdr_len = IP_TCP_HDR_SIZE; b->ip.hdr.tcp_hlen = SHIFT_TO_TCPHDRLEN_FIELD(LEN_B_TO_DW(TCP_HDR_SIZE));
switch (action) { case TCP_SYN: debug_cond(DEBUG_DEV_PKT, - "TCP Hdr:SYN (%pI4, %pI4, sq=%u, ak=%u)\n", - &tcp->rhost, &net_ip, - tcp_seq_num, tcp_ack_num); + "TCP Hdr:%s (%pI4, %pI4, s=%u, a=%u)\n", + tcpflags_to_str(action, buf, sizeof(buf)), + &tcp->rhost, &net_ip, tcp_seq_num, tcp_ack_num); net_set_syn_options(tcp, b); pkt_hdr_len = IP_TCP_O_SIZE; break; - case TCP_SYN | TCP_ACK: - case TCP_ACK: - pkt_hdr_len = IP_HDR_SIZE + net_set_ack_options(tcp, b); - b->ip.hdr.tcp_flags = action; - debug_cond(DEBUG_DEV_PKT, - "TCP Hdr:ACK (%pI4, %pI4, s=%u, a=%u, A=%x)\n", - &tcp->rhost, &net_ip, tcp_seq_num, tcp_ack_num, - action); - break; - case TCP_FIN: - debug_cond(DEBUG_DEV_PKT, - "TCP Hdr:FIN (%pI4, %pI4, s=%u, a=%u)\n", - &tcp->rhost, &net_ip, tcp_seq_num, tcp_ack_num); - payload_len = 0; - pkt_hdr_len = IP_TCP_HDR_SIZE; - break; case TCP_RST | TCP_ACK: case TCP_RST: debug_cond(DEBUG_DEV_PKT, - "TCP Hdr:RST (%pI4, %pI4, s=%u, a=%u)\n", + "TCP Hdr:%s (%pI4, %pI4, s=%u, a=%u)\n", + tcpflags_to_str(action, buf, sizeof(buf)), &tcp->rhost, &net_ip, tcp_seq_num, tcp_ack_num); + pkt_hdr_len = IP_TCP_HDR_SIZE; break; - /* Notify connection closing */ - case (TCP_FIN | TCP_ACK): - case (TCP_FIN | TCP_ACK | TCP_PUSH): - debug_cond(DEBUG_DEV_PKT, - "TCP Hdr:FIN ACK PSH(%pI4, %pI4, s=%u, a=%u, A=%x)\n", - &tcp->rhost, &net_ip, - tcp_seq_num, tcp_ack_num, action); - fallthrough; default: pkt_hdr_len = IP_HDR_SIZE + net_set_ack_options(tcp, b); - b->ip.hdr.tcp_flags = action | TCP_PUSH | TCP_ACK; debug_cond(DEBUG_DEV_PKT, - "TCP Hdr:dft (%pI4, %pI4, s=%u, a=%u, A=%x)\n", - &tcp->rhost, &net_ip, - tcp_seq_num, tcp_ack_num, action); + "TCP Hdr:%s (%pI4, %pI4, s=%u, a=%u)\n", + tcpflags_to_str(action, buf, sizeof(buf)), + &tcp->rhost, &net_ip, tcp_seq_num, tcp_ack_num); + break; }
pkt_len = pkt_hdr_len + payload_len;

Some driver implements it's own network packet pool, so PKTBUFSRX is zero. This results in zero-size TCP receive window, so data transfer doesn't work. Avoid it by setting a reasonable fallback value.
Signed-off-by: Mikhail Kshevetskiy mikhail.kshevetskiy@iopsys.eu Reviewed-by: Simon Glass sjg@chromium.org --- net/tcp.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/net/tcp.c b/net/tcp.c index 153f5183b25..5e76b1a24bc 100644 --- a/net/tcp.c +++ b/net/tcp.c @@ -36,7 +36,11 @@ #define TCP_SEND_RETRY 3 #define TCP_SEND_TIMEOUT 2000UL #define TCP_RX_INACTIVE_TIMEOUT 30000UL -#define TCP_RCV_WND_SIZE (PKTBUFSRX * TCP_MSS) +#if PKTBUFSRX != 0 + #define TCP_RCV_WND_SIZE (PKTBUFSRX * TCP_MSS) +#else + #define TCP_RCV_WND_SIZE (4 * TCP_MSS) +#endif
#define TCP_PACKET_OK 0 #define TCP_PACKET_DROP 1

fix include ordering to follow https://docs.u-boot.org/en/latest/develop/codingstyle.html#include-files
Signed-off-by: Mikhail Kshevetskiy mikhail.kshevetskiy@iopsys.eu Reviewed-by: Simon Glass sjg@chromium.org --- net/net.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-)
diff --git a/net/net.c b/net/net.c index aa1cb0b51cb..39bc3ce8903 100644 --- a/net/net.c +++ b/net/net.c @@ -89,41 +89,43 @@ #include <image.h> #include <led.h> #include <log.h> +#if defined(CONFIG_LED_STATUS) +#include <miiphy.h> +#endif #include <net.h> #include <net6.h> #include <ndisc.h> +#if defined(CONFIG_LED_STATUS) +#include <status_led.h> +#endif +#include <watchdog.h> +#include <linux/compiler.h> #include <net/fastboot_udp.h> #include <net/fastboot_tcp.h> -#include <net/tftp.h> #include <net/ncsi.h> #if defined(CONFIG_CMD_PCAP) #include <net/pcap.h> #endif -#include <net/udp.h> -#if defined(CONFIG_LED_STATUS) -#include <miiphy.h> -#include <status_led.h> -#endif -#include <watchdog.h> -#include <linux/compiler.h> -#include <test/test.h> #include <net/tcp.h> +#include <net/tftp.h> +#include <net/udp.h> #include <net/wget.h> +#include <test/test.h> #include "arp.h" #include "bootp.h" #include "cdp.h" +#include "dhcpv6.h" #if defined(CONFIG_CMD_DNS) #include "dns.h" #endif #include "link_local.h" +#include "net_rand.h" #include "nfs.h" #include "ping.h" #include "rarp.h" #if defined(CONFIG_CMD_WOL) #include "wol.h" #endif -#include "dhcpv6.h" -#include "net_rand.h"
/** BOOTP EXTENTIONS **/

On Thu, Nov 14, 2024 at 06:36:33AM +0300, Mikhail Kshevetskiy wrote:
Legacy TCP stack is bad. Here are some of the known issues:
- tcp packet from other connection can break a current one
- tcp send sequence always starts from zero
- bad tcp options processing
- strange assumptions on packet size for selective acknowledge
- tcp interface assumes one of the two scenarios:
so it's not possible to upload large amount of data from the board to remote host.
- data downloading from remote host to a board
- request-response exchange with a small packets
- wget test generate bad tcp stream, test should fail but it passes instead
This series of patches fixes all of the above issues.
Can you please work with CI to get everything there to pass? Thanks. See https://docs.u-boot.org/en/latest/develop/ci_testing.html if needed for help on starting that.

On 16.11.2024 03:31, Tom Rini wrote:
On Thu, Nov 14, 2024 at 06:36:33AM +0300, Mikhail Kshevetskiy wrote:
Legacy TCP stack is bad. Here are some of the known issues:
- tcp packet from other connection can break a current one
- tcp send sequence always starts from zero
- bad tcp options processing
- strange assumptions on packet size for selective acknowledge
- tcp interface assumes one of the two scenarios:
so it's not possible to upload large amount of data from the board to remote host.
- data downloading from remote host to a board
- request-response exchange with a small packets
- wget test generate bad tcp stream, test should fail but it passes instead
This series of patches fixes all of the above issues.
Can you please work with CI to get everything there to pass? Thanks. See https://docs.u-boot.org/en/latest/develop/ci_testing.html if needed for help on starting that.
Hello Tom, Could you look to https://github.com/u-boot/u-boot/pull/692 ? It fails 2 times on ut_lib_test_udelay, but this seems unrelated to my changes.
Mikhail

On Sat, Nov 16, 2024 at 05:49:06PM +0300, Mikhail Kshevetskiy wrote:
On 16.11.2024 03:31, Tom Rini wrote:
On Thu, Nov 14, 2024 at 06:36:33AM +0300, Mikhail Kshevetskiy wrote:
Legacy TCP stack is bad. Here are some of the known issues:
- tcp packet from other connection can break a current one
- tcp send sequence always starts from zero
- bad tcp options processing
- strange assumptions on packet size for selective acknowledge
- tcp interface assumes one of the two scenarios:
so it's not possible to upload large amount of data from the board to remote host.
- data downloading from remote host to a board
- request-response exchange with a small packets
- wget test generate bad tcp stream, test should fail but it passes instead
This series of patches fixes all of the above issues.
Can you please work with CI to get everything there to pass? Thanks. See https://docs.u-boot.org/en/latest/develop/ci_testing.html if needed for help on starting that.
Hello Tom, Could you look to https://github.com/u-boot/u-boot/pull/692 ? It fails 2 times on ut_lib_test_udelay, but this seems unrelated to my changes.
Yes, unfortunately Simon's recent moving of the tests caused that one to fail more frequently now. I've hit re-run, if that's not something available to you, just now.

Hi Tom,
On Sat, 16 Nov 2024 at 09:29, Tom Rini trini@konsulko.com wrote:
On Sat, Nov 16, 2024 at 05:49:06PM +0300, Mikhail Kshevetskiy wrote:
On 16.11.2024 03:31, Tom Rini wrote:
On Thu, Nov 14, 2024 at 06:36:33AM +0300, Mikhail Kshevetskiy wrote:
Legacy TCP stack is bad. Here are some of the known issues:
- tcp packet from other connection can break a current one
- tcp send sequence always starts from zero
- bad tcp options processing
- strange assumptions on packet size for selective acknowledge
- tcp interface assumes one of the two scenarios:
so it's not possible to upload large amount of data from the board to remote host.
- data downloading from remote host to a board
- request-response exchange with a small packets
- wget test generate bad tcp stream, test should fail but it passes instead
This series of patches fixes all of the above issues.
Can you please work with CI to get everything there to pass? Thanks. See https://docs.u-boot.org/en/latest/develop/ci_testing.html if needed for help on starting that.
Hello Tom, Could you look to https://github.com/u-boot/u-boot/pull/692 ? It fails 2 times on ut_lib_test_udelay, but this seems unrelated to my changes.
Yes, unfortunately Simon's recent moving of the tests caused that one to fail more frequently now. I've hit re-run, if that's not something available to you, just now.
Perhaps we should skip that test (return -EAGAIN) for sandbox?
Regards, Simon

On Sun, Nov 17, 2024 at 12:45:12PM -0700, Simon Glass wrote:
Hi Tom,
On Sat, 16 Nov 2024 at 09:29, Tom Rini trini@konsulko.com wrote:
On Sat, Nov 16, 2024 at 05:49:06PM +0300, Mikhail Kshevetskiy wrote:
On 16.11.2024 03:31, Tom Rini wrote:
On Thu, Nov 14, 2024 at 06:36:33AM +0300, Mikhail Kshevetskiy wrote:
Legacy TCP stack is bad. Here are some of the known issues:
- tcp packet from other connection can break a current one
- tcp send sequence always starts from zero
- bad tcp options processing
- strange assumptions on packet size for selective acknowledge
- tcp interface assumes one of the two scenarios:
so it's not possible to upload large amount of data from the board to remote host.
- data downloading from remote host to a board
- request-response exchange with a small packets
- wget test generate bad tcp stream, test should fail but it passes instead
This series of patches fixes all of the above issues.
Can you please work with CI to get everything there to pass? Thanks. See https://docs.u-boot.org/en/latest/develop/ci_testing.html if needed for help on starting that.
Hello Tom, Could you look to https://github.com/u-boot/u-boot/pull/692 ? It fails 2 times on ut_lib_test_udelay, but this seems unrelated to my changes.
Yes, unfortunately Simon's recent moving of the tests caused that one to fail more frequently now. I've hit re-run, if that's not something available to you, just now.
Perhaps we should skip that test (return -EAGAIN) for sandbox?
It would be good to understand why: - if (abs(error) > 100) - return -EINVAL; + ut_assert(abs(error) <= 100);
is not actually functionally equivalent, seemingly.

Hi Tom,
On Sun, 17 Nov 2024 at 12:52, Tom Rini trini@konsulko.com wrote:
On Sun, Nov 17, 2024 at 12:45:12PM -0700, Simon Glass wrote:
Hi Tom,
On Sat, 16 Nov 2024 at 09:29, Tom Rini trini@konsulko.com wrote:
On Sat, Nov 16, 2024 at 05:49:06PM +0300, Mikhail Kshevetskiy wrote:
On 16.11.2024 03:31, Tom Rini wrote:
On Thu, Nov 14, 2024 at 06:36:33AM +0300, Mikhail Kshevetskiy wrote:
Legacy TCP stack is bad. Here are some of the known issues:
- tcp packet from other connection can break a current one
- tcp send sequence always starts from zero
- bad tcp options processing
- strange assumptions on packet size for selective acknowledge
- tcp interface assumes one of the two scenarios:
so it's not possible to upload large amount of data from the board to remote host.
- data downloading from remote host to a board
- request-response exchange with a small packets
- wget test generate bad tcp stream, test should fail but it passes instead
This series of patches fixes all of the above issues.
Can you please work with CI to get everything there to pass? Thanks. See https://docs.u-boot.org/en/latest/develop/ci_testing.html if needed for help on starting that.
Hello Tom, Could you look to https://github.com/u-boot/u-boot/pull/692 ? It fails 2 times on ut_lib_test_udelay, but this seems unrelated to my changes.
Yes, unfortunately Simon's recent moving of the tests caused that one to fail more frequently now. I've hit re-run, if that's not something available to you, just now.
Perhaps we should skip that test (return -EAGAIN) for sandbox?
It would be good to understand why:
if (abs(error) > 100)
return -EINVAL;
ut_assert(abs(error) <= 100);
is not actually functionally equivalent, seemingly.
-EINVAL is consider an error
-EAGAIN is considered a request to skip the test
Regards, Simon
participants (3)
-
Mikhail Kshevetskiy
-
Simon Glass
-
Tom Rini