Changes:
1. Separate reusable part from net_set_tcp_header to
net_set_tcp_header_common
2. Make TCP signatures reusable by receiving particular
IP agnostic TCP headers
3. Extract net_send_ip_packet6 from net_send_udp_packet6
to reuse the code
4. Expose TCP state machine related functions

This allows us to reuse TCP logic between IP and IP6 stack.

Signed-off-by: Dmitrii Merkurev <dimori...@google.com>
Cc: Ying-Chun Liu (PaulLiu) <paul....@linaro.org>
Cc: Simon Glass <s...@chromium.org>
Сс: Joe Hershberger <joe.hershber...@ni.com>
Сс: Ramon Fried <rfried....@gmail.com>
Reviewed-by: Ying-Chun Liu (PaulLiu) <paul....@linaro.org>
Reviewed-by: Simon Glass <s...@chromium.org>
---
 include/net.h     |  26 +++-
 include/net/tcp.h | 109 +++++++++++++--
 net/net.c         |  24 ++--
 net/tcp.c         | 337 ++++++++++++++++++++++++++++------------------
 4 files changed, 342 insertions(+), 154 deletions(-)

diff --git a/include/net.h b/include/net.h
index e254df7d7f..2e19d9defc 100644
--- a/include/net.h
+++ b/include/net.h
@@ -569,11 +569,29 @@ int net_eth_hdr_size(void);
 int net_set_ether(uchar *xet, const uchar *dest_ethaddr, uint prot);
 int net_update_ether(struct ethernet_hdr *et, uchar *addr, uint prot);
 
-/* Set IP header */
-void net_set_ip_header(uchar *pkt, struct in_addr dest, struct in_addr source,
+/**
+ * net_set_ip_header() - Initialize IP header for the given packet
+ *
+ * @pkt_ip_hdr: Pointer to IP header
+ * @dest:       IPv4 destination address
+ * @source:     IPv4 source address
+ * @pkt_len:    Length of ip_header + tcp_header + payload
+ * @proto:      Transport level protocol (e.g tcp/udp/etc)
+ */
+void net_set_ip_header(uchar *pkt_ip_hdr, struct in_addr dest, struct in_addr 
source,
                       u16 pkt_len, u8 proto);
-void net_set_udp_header(uchar *pkt, struct in_addr dest, int dport,
-                               int sport, int len);
+
+/**
+ * net_set_udp_header() - Initialize IP and UDP header for the given packet
+ *
+ * @pkt_ip_hdr: Pointer to IP header
+ * @dest:       IPv4 destination address
+ * @dport:      Destination port
+ * @sport:      Source port
+ * @len:        Payload length
+ */
+void net_set_udp_header(uchar *pkt_ip_hdr, struct in_addr dest, int dport,
+                       int sport, int len);
 
 /**
  * compute_ip_checksum() - Compute IP checksum
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 93ed728dfe..344b4be2a4 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -8,10 +8,20 @@
 #ifndef __TCP_H__
 #define __TCP_H__
 
+#include <net.h>
+
 #define TCP_ACTIVITY 127               /* Number of packets received   */
                                        /* before console progress mark */
 
+/*
+ * TCP lengths are stored as a rounded up number of 32 bit words.
+ * Add 3 to length round up, rounded, then divided into the
+ * length in 32 bit words.
+ */
+#define LEN_B_TO_DW(x) ((x) >> 2)
+#define ROUND_TCPHDR_LEN(x) (LEN_B_TO_DW((x) + 3))
 #define GET_TCP_HDR_LEN_IN_BYTES(x) ((x) >> 2)
+#define SHIFT_TO_TCPHDRLEN_FIELD(x) ((x) << 4)
 
 /**
  * struct tcp_hdr - TCP header
@@ -24,7 +34,7 @@
  * @tcp_win: TCP windows size
  * @tcp_xsum: Checksum
  * @tcp_ugr: Pointer to urgent data
-*/
+ */
 struct tcp_hdr {
        u16             tcp_src;
        u16             tcp_dst;
@@ -163,18 +173,14 @@ struct tcp_t_opt {
  */
 
 /**
- * struct ip_tcp_hdr_o - IP + TCP header + TCP options
- * @ip_hdr: IP + TCP header
- * @tcp_hdr: TCP header
+ * struct tcp_hdr_o - TCP options
  * @mss: TCP MSS Option
  * @scale: TCP Windows Scale Option
  * @sack_p: TCP Sack-Permitted Option
  * @t_opt: TCP Timestamp Option
  * @end: end of options
  */
-struct ip_tcp_hdr_o {
-       struct  ip_hdr     ip_hdr;
-       struct  tcp_hdr    tcp_hdr;
+struct tcp_hdr_o {
        struct  tcp_mss    mss;
        struct  tcp_scale  scale;
        struct  tcp_sack_p sack_p;
@@ -182,6 +188,22 @@ struct ip_tcp_hdr_o {
        u8      end;
 } __packed;
 
+#define TCP_O_SIZE (sizeof(struct tcp_hdr_o))
+
+/**
+ * struct ip_tcp_hdr_o - IP + TCP header + TCP options
+ * @ip_hdr: IP + TCP header
+ * @tcp_hdr: TCP header
+ * @tcp_o: TCP options
+ * @end: end of IP/TCP header
+ */
+struct ip_tcp_hdr_o {
+       struct  ip_hdr     ip_hdr;
+       struct  tcp_hdr    tcp_hdr;
+       struct  tcp_hdr_o  tcp_o;
+       u8      end;
+} __packed;
+
 #define IP_TCP_O_SIZE (sizeof(struct ip_tcp_hdr_o))
 
 /**
@@ -209,7 +231,7 @@ struct ip_tcp_hdr_s {
 
 /**
  * struct pseudo_hdr - Pseudo Header
- * @padding: pseudo hdr size = ip_tcp hdr size
+ * @padding: pseudo hdr size = ip hdr size
  * @p_src: Source IP address
  * @p_dst: Destination IP address
  * @rsvd: reserved
@@ -236,7 +258,6 @@ struct pseudo_hdr {
  *
  * Build Pseudo header in packed buffer
  * first, calculate TCP checksum, then build IP header in packed buffer.
- *
  */
 union tcp_build_pkt {
        struct pseudo_hdr ph;
@@ -269,9 +290,77 @@ enum tcp_state {
 
 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,
+
+/**
+ * net_set_tcp_header_common() - IP version agnostic TCP header building 
implementation
+ *
+ * @tcp_hdr: pointer to TCP header struct
+ * @tcp_o: pointer to TCP options header struct
+ * @sack_t_opt: pointer to TCP sack options header struct
+ * @sack_v: pointer to TCP sack header struct
+ * @dport: destination TCP port
+ * @sport: source TCP port
+ * @payload_len: TCP payload len
+ * @action: TCP action (SYN, ACK, FIN, etc)
+ * @tcp_seq_num: TCP sequential number
+ * @tcp_ack_num: TCP acknowledgment number
+ *
+ * returns TCP header
+ */
+int net_set_tcp_header_common(struct tcp_hdr *tcp_hdr, struct tcp_hdr_o *tcp_o,
+                             struct tcp_t_opt *sack_t_opt, struct tcp_sack_v 
*sack_v,
+                             u16 dport, u16 sport, int payload_len, u8 action,
+                             u32 tcp_seq_num, u32 tcp_ack_num);
+
+/**
+ * net_set_tcp_header() - IPv4 TCP header bulding implementation
+ *
+ * @pkt: pointer to the IP header
+ * @dport: destination TCP port
+ * @sport: source TCP port
+ * @payload_len: TCP payload len
+ * @action: TCP action (SYN, ACK, FIN, etc)
+ * @tcp_seq_num: TCP sequential number
+ * @tcp_ack_num: TCP acknowledgment number
+ *
+ * returns TCP header
+ */
+int net_set_tcp_header(uchar *pkt, u16 dport, u16 sport, int payload_len,
                       u8 action, u32 tcp_seq_num, u32 tcp_ack_num);
 
+/**
+ * tcp_parse_options() - parsing TCP options
+ *
+ * @o: pointer to the option field.
+ * @o_len: length of the option field.
+ */
+void tcp_parse_options(uchar *o, int o_len);
+
+/**
+ * tcp_state_machine() - update TCP state in a reaction to incoming packet
+ *
+ * @tcp_flags: TCP action (SYN, ACK, FIN, etc)
+ * @tcp_seq_num: TCP sequential number
+ * @tcp_seq_num_out: TCP sequential number we expect to answer with
+ * @tcp_ack_num_out: TCP acknowledgment number we expect to answer with
+ * @payload_len: TCP payload len
+ *
+ * returns TCP action we expect to answer with
+ */
+u8 tcp_state_machine(u8 tcp_flags, u32 tcp_seq_num, u32 *tcp_seq_num_out,
+                    u32 *tcp_ack_num_out, int payload_len);
+
+/**
+ * tcp_sent_state_machine() - update TCP state in a reaction to outcoming 
packet
+ *
+ * @action: TCP action (SYN, ACK, FIN, etc)
+ * @tcp_seq_num: TCP sequential number
+ * @tcp_ack_num: TCP acknowledgment number
+ *
+ * returns TCP action we expect to answer with
+ */
+u8 tcp_sent_state_machine(u8 action, u32 *tcp_seq_num, u32 *tcp_ack_num);
+
 /**
  * rxhand_tcp() - An incoming packet handler.
  * @pkt: pointer to the application packet
diff --git a/net/net.c b/net/net.c
index 43abbac7c3..75a2bbaee3 100644
--- a/net/net.c
+++ b/net/net.c
@@ -914,8 +914,13 @@ int net_send_ip_packet(uchar *ether, struct in_addr dest, 
int dport, int sport,
                       int payload_len, int proto, u8 action, u32 tcp_seq_num,
                       u32 tcp_ack_num)
 {
+       if ((proto == IPPROTO_UDP && !IS_ENABLED(CONFIG_PROT_UDP)) ||
+           (proto == IPPROTO_TCP && !IS_ENABLED(CONFIG_PROT_TCP)))
+               return -EINVAL;
+
        uchar *pkt;
        int eth_hdr_size;
+       int ip_tcp_hdr_size;
        int pkt_hdr_size;
 
        /* make sure the net_tx_packet is initialized (net_init() was called) */
@@ -934,21 +939,24 @@ int net_send_ip_packet(uchar *ether, struct in_addr dest, 
int dport, int sport,
        pkt = (uchar *)net_tx_packet;
 
        eth_hdr_size = net_set_ether(pkt, ether, PROT_IP);
+       pkt_hdr_size = eth_hdr_size;
+       pkt += eth_hdr_size;
 
        switch (proto) {
        case IPPROTO_UDP:
-               net_set_udp_header(pkt + eth_hdr_size, dest, dport, sport,
+               net_set_udp_header(pkt, dest, dport, sport,
                                   payload_len);
-               pkt_hdr_size = eth_hdr_size + IP_UDP_HDR_SIZE;
+               pkt_hdr_size += IP_UDP_HDR_SIZE;
                break;
-#if defined(CONFIG_PROT_TCP)
        case IPPROTO_TCP:
-               pkt_hdr_size = eth_hdr_size
-                       + tcp_set_tcp_header(pkt + eth_hdr_size, dport, sport,
-                                            payload_len, action, tcp_seq_num,
-                                            tcp_ack_num);
+               ip_tcp_hdr_size = IP_HDR_SIZE;
+               ip_tcp_hdr_size += net_set_tcp_header(pkt, dport, sport,
+                                                     payload_len, action, 
tcp_seq_num,
+                                                     tcp_ack_num);
+               net_set_ip_header(pkt, net_server_ip, net_ip,
+                                 ip_tcp_hdr_size + payload_len, IPPROTO_TCP);
+               pkt_hdr_size += ip_tcp_hdr_size;
                break;
-#endif
        default:
                return -EINVAL;
        }
diff --git a/net/tcp.c b/net/tcp.c
index 10ce799814..483c03a595 100644
--- a/net/tcp.c
+++ b/net/tcp.c
@@ -54,16 +54,6 @@ 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
- * length in 32 bit words.
- */
-#define LEN_B_TO_DW(x) ((x) >> 2)
-#define ROUND_TCPHDR_LEN(x) (LEN_B_TO_DW((x) + 3))
-#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;
 
@@ -149,29 +139,32 @@ 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
- * @b: the packet
+ * @tcp_hdr: pointer to TCP header struct
+ * @t_opt: pointer to TCP t opt header struct
+ * @sack_v: pointer to TCP sack header struct
  *
  * Return: TCP header length
  */
-int net_set_ack_options(union tcp_build_pkt *b)
+int net_set_ack_options(struct tcp_hdr *tcp_hdr, struct tcp_t_opt *t_opt,
+                       struct tcp_sack_v *sack_v)
 {
-       b->sack.tcp_hdr.tcp_hlen = 
SHIFT_TO_TCPHDRLEN_FIELD(LEN_B_TO_DW(TCP_HDR_SIZE));
+       tcp_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.sack_v.kind = TCP_1_NOP;
-       b->sack.sack_v.len = 0;
+       t_opt->kind = TCP_O_TS;
+       t_opt->len = TCP_OPT_LEN_A;
+       t_opt->t_snd = htons(loc_timestamp);
+       t_opt->t_rcv = rmt_timestamp;
+       sack_v->kind = TCP_1_NOP;
+       sack_v->len = 0;
 
        if (IS_ENABLED(CONFIG_PROT_TCP_SACK)) {
                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;
-                       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);
+                       sack_v->len = tcp_lost.len;
+                       sack_v->kind = TCP_V_SACK;
+                       sack_v->hill[0].l = htonl(tcp_lost.hill[0].l);
+                       sack_v->hill[0].r = htonl(tcp_lost.hill[0].r);
 
                        /*
                         * These SACK structures are initialized with NOPs to
@@ -179,21 +172,21 @@ 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[3].l = TCP_O_NOP;
-                       b->sack.sack_v.hill[3].r = TCP_O_NOP;
+                       sack_v->hill[1].l = htonl(tcp_lost.hill[1].l);
+                       sack_v->hill[1].r = htonl(tcp_lost.hill[1].r);
+                       sack_v->hill[2].l = htonl(tcp_lost.hill[2].l);
+                       sack_v->hill[2].r = htonl(tcp_lost.hill[2].r);
+                       sack_v->hill[3].l = TCP_O_NOP;
+                       sack_v->hill[3].r = TCP_O_NOP;
                }
 
-               b->sack.tcp_hdr.tcp_hlen = 
SHIFT_TO_TCPHDRLEN_FIELD(ROUND_TCPHDR_LEN(TCP_HDR_SIZE +
-                                                                               
 TCP_TSOPT_SIZE +
-                                                                               
 tcp_lost.len));
+               tcp_hdr->tcp_hlen = 
SHIFT_TO_TCPHDRLEN_FIELD(ROUND_TCPHDR_LEN(TCP_HDR_SIZE +
+                                                                             
TCP_TSOPT_SIZE +
+                                                                             
tcp_lost.len));
        } else {
-               b->sack.sack_v.kind = 0;
-               b->sack.tcp_hdr.tcp_hlen = 
SHIFT_TO_TCPHDRLEN_FIELD(ROUND_TCPHDR_LEN(TCP_HDR_SIZE +
-                                                                               
 TCP_TSOPT_SIZE));
+               sack_v->kind = 0;
+               tcp_hdr->tcp_hlen = 
SHIFT_TO_TCPHDRLEN_FIELD(ROUND_TCPHDR_LEN(TCP_HDR_SIZE +
+                                                                             
TCP_TSOPT_SIZE));
        }
 
        /*
@@ -201,69 +194,61 @@ int net_set_ack_options(union tcp_build_pkt *b)
         * TCP header to add to the total packet length
         */
 
-       return GET_TCP_HDR_LEN_IN_BYTES(b->sack.tcp_hdr.tcp_hlen);
+       return GET_TCP_HDR_LEN_IN_BYTES(tcp_hdr->tcp_hlen);
 }
 
 /**
  * net_set_ack_options() - set TCP options in SYN packets
- * @b: the packet
+ * @tcp_hdr: pointer to TCP header struct
+ * @options: pointer to TCP header options struct
  */
-void net_set_syn_options(union tcp_build_pkt *b)
+void net_set_syn_options(struct tcp_hdr *tcp_hdr, struct tcp_hdr_o *options)
 {
        if (IS_ENABLED(CONFIG_PROT_TCP_SACK))
                tcp_lost.len = 0;
 
-       b->ip.tcp_hdr.tcp_hlen = 0xa0;
+       tcp_hdr->tcp_hlen = 0xa0;
 
-       b->ip.mss.kind = TCP_O_MSS;
-       b->ip.mss.len = TCP_OPT_LEN_4;
-       b->ip.mss.mss = htons(TCP_MSS);
-       b->ip.scale.kind = TCP_O_SCL;
-       b->ip.scale.scale = TCP_SCALE;
-       b->ip.scale.len = TCP_OPT_LEN_3;
+       options->mss.kind = TCP_O_MSS;
+       options->mss.len = TCP_OPT_LEN_4;
+       options->mss.mss = htons(TCP_MSS);
+       options->scale.kind = TCP_O_SCL;
+       options->scale.scale = TCP_SCALE;
+       options->scale.len = TCP_OPT_LEN_3;
        if (IS_ENABLED(CONFIG_PROT_TCP_SACK)) {
-               b->ip.sack_p.kind = TCP_P_SACK;
-               b->ip.sack_p.len = TCP_OPT_LEN_2;
+               options->sack_p.kind = TCP_P_SACK;
+               options->sack_p.len = TCP_OPT_LEN_2;
        } else {
-               b->ip.sack_p.kind = TCP_1_NOP;
-               b->ip.sack_p.len = TCP_1_NOP;
+               options->sack_p.kind = TCP_1_NOP;
+               options->sack_p.len = TCP_1_NOP;
        }
-       b->ip.t_opt.kind = TCP_O_TS;
-       b->ip.t_opt.len = TCP_OPT_LEN_A;
+       options->t_opt.kind = TCP_O_TS;
+       options->t_opt.len = TCP_OPT_LEN_A;
        loc_timestamp = get_ticks();
        rmt_timestamp = 0;
-       b->ip.t_opt.t_snd = 0;
-       b->ip.t_opt.t_rcv = 0;
-       b->ip.end = TCP_O_END;
+
+       options->t_opt.t_snd = 0;
+       options->t_opt.t_rcv = 0;
 }
 
-int tcp_set_tcp_header(uchar *pkt, int dport, int sport, int payload_len,
-                      u8 action, u32 tcp_seq_num, u32 tcp_ack_num)
+/**
+ * tcp_sent_state_machine() - update TCP state in a reaction to outcoming 
packet
+ *
+ * @action: TCP action (SYN, ACK, FIN, etc)
+ * @tcp_seq_num: TCP sequential number
+ * @tcp_ack_num: TCP acknowledgment number
+ *
+ * returns TCP action we expect to answer with
+ */
+u8 tcp_sent_state_machine(u8 action, u32 *tcp_seq_num, u32 *tcp_ack_num)
 {
-       union tcp_build_pkt *b = (union tcp_build_pkt *)pkt;
-       int pkt_hdr_len;
-       int pkt_len;
-       int tcp_len;
-
-       /*
-        * Header: 5 32 bit words. 4 bits TCP header Length,
-        *         4 bits reserved options
-        */
-       b->ip.tcp_hdr.tcp_flags = action;
-       pkt_hdr_len = IP_TCP_HDR_SIZE;
-       b->ip.tcp_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",
-                          &net_server_ip, &net_ip,
-                          tcp_seq_num, tcp_ack_num);
+                          "TCP Hdr:SYN (sq=%u, ak=%u)\n", *tcp_seq_num, 
*tcp_ack_num);
                tcp_activity_count = 0;
-               net_set_syn_options(b);
-               tcp_seq_num = 0;
-               tcp_ack_num = 0;
-               pkt_hdr_len = IP_TCP_O_SIZE;
+               *tcp_seq_num = 0;
+               *tcp_ack_num = 0;
                if (current_tcp_state == TCP_SYN_SENT) {  /* Too many SYNs */
                        action = TCP_FIN;
                        current_tcp_state = TCP_FIN_WAIT_1;
@@ -273,57 +258,93 @@ int tcp_set_tcp_header(uchar *pkt, int dport, int sport, 
int payload_len,
                break;
        case TCP_SYN | TCP_ACK:
        case TCP_ACK:
-               pkt_hdr_len = IP_HDR_SIZE + net_set_ack_options(b);
-               b->ip.tcp_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,
-                          action);
+                          "TCP Hdr:ACK (s=%u, a=%u, A=%x)\n",
+                          *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);
-               payload_len = 0;
-               pkt_hdr_len = IP_TCP_HDR_SIZE;
+                          "TCP Hdr:FIN  (s=%u, a=%u)\n", *tcp_seq_num, 
*tcp_ack_num);
                current_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);
+                          "TCP Hdr:RST  (s=%u, a=%u)\n", *tcp_seq_num, 
*tcp_ack_num);
                current_tcp_state = TCP_CLOSED;
                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(s=%u, a=%u, A=%x)\n",
+                          *tcp_seq_num, *tcp_ack_num, action);
                if (current_tcp_state == TCP_CLOSE_WAIT)
                        current_tcp_state = TCP_CLOSING;
-
-               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_seq_num, tcp_ack_num, action);
                fallthrough;
        default:
-               pkt_hdr_len = IP_HDR_SIZE + net_set_ack_options(b);
-               b->ip.tcp_hdr.tcp_flags = action | TCP_PUSH | TCP_ACK;
+               action = 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_seq_num, tcp_ack_num, action);
+                          "TCP Hdr:dft  (s=%u, a=%u, A=%x)\n",
+                          *tcp_seq_num, *tcp_ack_num, action);
        }
 
-       pkt_len = pkt_hdr_len + payload_len;
-       tcp_len = pkt_len - IP_HDR_SIZE;
+       return action;
+}
+
+/**
+ * net_set_tcp_header_common() - IP version agnostic TCP header building 
implementation
+ *
+ * @tcp_hdr: pointer to TCP header struct
+ * @tcp_o: pointer to TCP options header struct
+ * @sack_t_opt: pointer to TCP sack options header struct
+ * @sack_v: pointer to TCP sack header struct
+ * @dport: destination TCP port
+ * @sport: source TCP port
+ * @payload_len: TCP payload len
+ * @action: TCP action (SYN, ACK, FIN, etc)
+ * @tcp_seq_num: TCP sequential number
+ * @tcp_ack_num: TCP acknowledgment number
+ *
+ * returns TCP header size
+ */
+int net_set_tcp_header_common(struct tcp_hdr *tcp_hdr, struct tcp_hdr_o *tcp_o,
+                             struct tcp_t_opt *sack_t_opt, struct tcp_sack_v 
*sack_v,
+                             u16 dport, u16 sport, int payload_len, u8 action,
+                             u32 tcp_seq_num, u32 tcp_ack_num)
+{
+       u8  tcp_action = TCP_DATA;
+       int tcp_hdr_len;
+
+       /*
+        * Header: 5 32 bit words. 4 bits TCP header Length,
+        *         4 bits reserved options
+        */
+       tcp_hdr_len = TCP_HDR_SIZE;
+       tcp_hdr->tcp_hlen = SHIFT_TO_TCPHDRLEN_FIELD(LEN_B_TO_DW(TCP_HDR_SIZE));
+
+       switch (action) {
+       case TCP_SYN:
+               net_set_syn_options(tcp_hdr, tcp_o);
+               tcp_hdr_len = TCP_HDR_SIZE + TCP_O_SIZE;
+               break;
+       case TCP_RST | TCP_ACK:
+       case TCP_RST:
+       case TCP_FIN:
+               payload_len = 0;
+               break;
+       default:
+               tcp_hdr_len = net_set_ack_options(tcp_hdr, sack_t_opt, sack_v);
+       }
+
+       tcp_action = tcp_sent_state_machine(action, &tcp_seq_num, &tcp_ack_num);
+       tcp_hdr->tcp_flags = tcp_action;
 
        tcp_ack_edge = tcp_ack_num;
-       /* TCP Header */
-       b->ip.tcp_hdr.tcp_ack = htonl(tcp_ack_edge);
-       b->ip.tcp_hdr.tcp_src = htons(sport);
-       b->ip.tcp_hdr.tcp_dst = htons(dport);
-       b->ip.tcp_hdr.tcp_seq = htonl(tcp_seq_num);
+       tcp_hdr->tcp_ack = htonl(tcp_ack_edge);
+       tcp_hdr->tcp_seq = htonl(tcp_seq_num);
+       tcp_hdr->tcp_src = htons(sport);
+       tcp_hdr->tcp_dst = htons(dport);
 
        /*
         * TCP window size - TCP header variable tcp_win.
@@ -340,18 +361,46 @@ int tcp_set_tcp_header(uchar *pkt, int dport, int sport, 
int payload_len,
         * it is, then the u-boot tftp or nfs kernel netboot should be
         * considered.
         */
-       b->ip.tcp_hdr.tcp_win = htons(PKTBUFSRX * TCP_MSS >> TCP_SCALE);
+       tcp_hdr->tcp_win = htons(PKTBUFSRX * TCP_MSS >> TCP_SCALE);
 
-       b->ip.tcp_hdr.tcp_xsum = 0;
-       b->ip.tcp_hdr.tcp_ugr = 0;
+       tcp_hdr->tcp_xsum = 0;
+       tcp_hdr->tcp_ugr = 0;
 
-       b->ip.tcp_hdr.tcp_xsum = tcp_set_pseudo_header(pkt, net_ip, 
net_server_ip,
-                                                      tcp_len, pkt_len);
+       return tcp_hdr_len;
+}
 
-       net_set_ip_header((uchar *)&b->ip, net_server_ip, net_ip,
-                         pkt_len, IPPROTO_TCP);
+/**
+ * net_set_tcp_header() - IPv4 TCP header bulding implementation
+ *
+ * @pkt: pointer to the IP header
+ * @dport: destination TCP port
+ * @sport: source TCP port
+ * @payload_len: TCP payload len
+ * @action: TCP action (SYN, ACK, FIN, etc)
+ * @tcp_seq_num: TCP sequential number
+ * @tcp_ack_num: TCP acknowledgment number
+ *
+ * returns TCP header + payload size
+ */
+int net_set_tcp_header(uchar *pkt, u16 dport, u16 sport, int payload_len,
+                      u8 action, u32 tcp_seq_num, u32 tcp_ack_num)
+{
+       union tcp_build_pkt *b = (union tcp_build_pkt *)pkt;
+       int tcp_hdr_len;
+       int pkt_len;
 
-       return pkt_hdr_len;
+       pkt_len = IP_HDR_SIZE;
+       tcp_hdr_len = net_set_tcp_header_common(&b->ip.tcp_hdr, &b->ip.tcp_o,
+                                               &b->sack.t_opt, &b->sack.sack_v,
+                                               dport, sport, payload_len, 
action,
+                                               tcp_seq_num, tcp_ack_num);
+       pkt_len += tcp_hdr_len;
+       pkt_len += payload_len;
+
+       b->ip.tcp_hdr.tcp_xsum = tcp_set_pseudo_header(pkt, net_ip, 
net_server_ip,
+                                                      tcp_hdr_len + 
payload_len, pkt_len);
+
+       return tcp_hdr_len;
 }
 
 /**
@@ -500,7 +549,31 @@ 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 void init_sack_options(u32 tcp_seq_num, u32 tcp_ack_num)
+{
+       tcp_seq_init = tcp_seq_num;
+       tcp_ack_edge = tcp_ack_num;
+       sack_idx = 0;
+       edge_a[sack_idx].se.l = tcp_ack_edge;
+       edge_a[sack_idx].se.r = tcp_ack_edge;
+       prev_len = 0;
+       for (int i = 0; i < TCP_SACK; i++)
+               edge_a[i].st = NOPKT;
+}
+
+/**
+ * tcp_state_machine() - update TCP state in a reaction to incoming request
+ *
+ * @tcp_flags: TCP action (SYN, ACK, FIN, etc)
+ * @tcp_seq_num: TCP sequential number
+ * @tcp_seq_num_out: TCP sequential number we expect to answer with
+ * @tcp_ack_num_out: TCP acknowledgment number we expect to answer with
+ * @payload_len: TCP payload len
+ *
+ * returns TCP action we expect to answer with
+ */
+u8 tcp_state_machine(u8 tcp_flags, u32 tcp_seq_num, u32 *tcp_seq_num_out,
+                    u32 *tcp_ack_num_out, int payload_len)
 {
        u8 tcp_fin = tcp_flags & TCP_FIN;
        u8 tcp_syn = tcp_flags & TCP_SYN;
@@ -508,7 +581,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
@@ -533,34 +605,29 @@ static u8 tcp_state_machine(u8 tcp_flags, u32 
tcp_seq_num, int payload_len)
                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;
+                       init_sack_options(tcp_seq_num, tcp_seq_num + 1);
                        current_tcp_state = TCP_SYN_RECEIVED;
                } else if (tcp_ack || tcp_fin) {
                        action = TCP_DATA;
                }
                break;
        case TCP_SYN_RECEIVED:
+               if (tcp_ack) {
+                       action = TCP_DATA;
+                       init_sack_options(tcp_seq_num, tcp_seq_num + 1);
+                       current_tcp_state = TCP_ESTABLISHED;
+               }
+               break;
        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;
                        current_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;
-                       sack_idx = 0;
-                       edge_a[sack_idx].se.l = tcp_ack_edge;
-                       edge_a[sack_idx].se.r = tcp_ack_edge;
-                       prev_len = 0;
+               } else if (tcp_syn && tcp_ack) {
+                       action |= TCP_ACK | TCP_PUSH;
+                       init_sack_options(tcp_seq_num, tcp_seq_num + 1);
                        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;
                } else {
                        action = TCP_DATA;
                }
@@ -627,6 +694,10 @@ static u8 tcp_state_machine(u8 tcp_flags, u32 tcp_seq_num, 
int payload_len)
                }
                break;
        }
+
+       *tcp_seq_num_out = tcp_seq_num;
+       *tcp_ack_num_out = tcp_ack_edge;
+
        return action;
 }
 
@@ -641,6 +712,7 @@ void rxhand_tcp_f(union tcp_build_pkt *b, unsigned int 
pkt_len)
        u16 tcp_rx_xsum = b->ip.ip_hdr.ip_sum;
        u8  tcp_action = TCP_DATA;
        u32 tcp_seq_num, tcp_ack_num;
+       u32 res_tcp_seq_num, res_tcp_ack_num;
        int tcp_hdr_len, payload_len;
 
        /* Verify IP header */
@@ -685,7 +757,8 @@ void rxhand_tcp_f(union tcp_build_pkt *b, unsigned int 
pkt_len)
 
        /* Packets are not ordered. Send to app as received. */
        tcp_action = tcp_state_machine(b->ip.tcp_hdr.tcp_flags,
-                                      tcp_seq_num, payload_len);
+                                      tcp_seq_num, &res_tcp_seq_num,
+                                      &res_tcp_ack_num, payload_len);
 
        tcp_activity_count++;
        if (tcp_activity_count > TCP_ACTIVITY) {
@@ -705,7 +778,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, res_tcp_seq_num, res_tcp_ack_num, 
payload_len);
 
                /*
                 * Warning: Incoming Ack & Seq sequence numbers are transposed
@@ -714,6 +787,6 @@ void rxhand_tcp_f(union tcp_build_pkt *b, unsigned int 
pkt_len)
                net_send_tcp_packet(0, ntohs(b->ip.tcp_hdr.tcp_src),
                                    ntohs(b->ip.tcp_hdr.tcp_dst),
                                    (tcp_action & (~TCP_PUSH)),
-                                   tcp_ack_num, tcp_ack_edge);
+                                   res_tcp_seq_num, res_tcp_ack_num);
        }
 }
-- 
2.41.0.487.g6d72f3e995-goog

Reply via email to