summaryrefslogtreecommitdiff
path: root/net/tcp.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/tcp.c')
-rw-r--r--net/tcp.c891
1 files changed, 691 insertions, 200 deletions
diff --git a/net/tcp.c b/net/tcp.c
index 7c713a5ca56..a2941951834 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_status 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;
+ }
}