net/tcp: improve tcp framework, use better state machine
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>
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 0694af9..5022fa9 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -265,6 +265,7 @@
* @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,60 +275,180 @@
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
+};
+
+/**
* 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
*
+ * @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;
+ void *priv;
+
- /* TCP connection state */
+ 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,51 @@
*/
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);