diff mbox series

[v2,2/6] net: prepare existing TCP stack to be reused by IP6

Message ID 20230510165959.2228978-2-dimorinny@google.com
State Superseded
Delegated to: Ramon Fried
Headers show
Series [v2,1/6] net: split IP_TCP header into separate IP/IP6 and TCP headers | expand

Commit Message

Dmitrii Merkurev May 10, 2023, 4:59 p.m. UTC
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 <dimorinny@google.com>
Cc: Ying-Chun Liu (PaulLiu) <paul.liu@linaro.org>
Cc: Simon Glass <sjg@chromium.org>
Сс: Joe Hershberger <joe.hershberger@ni.com>
Сс: Ramon Fried <rfried.dev@gmail.com>
---
 include/net/tcp.h | 109 +++++++++++++--
 net/net.c         |  18 ++-
 net/net6.c        |  78 ++++++++---
 net/tcp.c         | 337 ++++++++++++++++++++++++++++------------------
 4 files changed, 372 insertions(+), 170 deletions(-)

Comments

Paul Liu May 24, 2023, 9:38 p.m. UTC | #1
On 2023/5/11 00:59, Dmitrii Merkurev wrote:
> 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 <dimorinny@google.com>
> Cc: Ying-Chun Liu (PaulLiu) <paul.liu@linaro.org>
> Cc: Simon Glass <sjg@chromium.org>
> Сс: Joe Hershberger <joe.hershberger@ni.com>
> Сс: Ramon Fried <rfried.dev@gmail.com>
> ---
>   include/net/tcp.h | 109 +++++++++++++--
>   net/net.c         |  18 ++-
>   net/net6.c        |  78 ++++++++---
>   net/tcp.c         | 337 ++++++++++++++++++++++++++++------------------
>   4 files changed, 372 insertions(+), 170 deletions(-)
> 
> 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..0b68bf7b13 100644
> --- a/net/net.c
> +++ b/net/net.c
> @@ -916,6 +916,7 @@ int net_send_ip_packet(uchar *ether, struct in_addr dest, int dport, int sport,
>   {
>   	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,19 +935,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:
> diff --git a/net/net6.c b/net/net6.c
> index 2dd64c0e16..e395b930b0 100644
> --- a/net/net6.c
> +++ b/net/net6.c
> @@ -324,15 +324,13 @@ int ip6_add_hdr(uchar *xip, struct in6_addr *src, struct in6_addr *dest,
>   	return sizeof(struct ip6_hdr);
>   }
>   
> -int net_send_udp_packet6(uchar *ether, struct in6_addr *dest, int dport,
> -			 int sport, int len)
> +int udp6_add_hdr(uchar *xip, struct in6_addr *dest, int dport, int sport,
> +		 int len)
>   {
> -	uchar *pkt;
>   	struct udp_hdr *udp;
>   	u16 csum_p;
>   
> -	udp = (struct udp_hdr *)((uchar *)net_tx_packet + net_eth_hdr_size() +
> -			IP6_HDR_SIZE);
> +	udp = (struct udp_hdr *)xip;
>   
>   	udp->udp_dst = htons(dport);
>   	udp->udp_src = htons(sport);
> @@ -344,39 +342,75 @@ int net_send_udp_packet6(uchar *ether, struct in6_addr *dest, int dport,
>   	udp->udp_xsum = csum_ipv6_magic(&net_ip6, dest, len + UDP_HDR_SIZE,
>   					IPPROTO_UDP, csum_p);
>   
> +	return sizeof(struct udp_hdr);
> +}
> +
> +int net_send_ip_packet6(uchar *ether, struct in6_addr *dest, int dport, int sport,
> +			int payload_len, int proto, u8 action, u32 tcp_seq_num,
> +			u32 tcp_ack_num)
> +{
> +	uchar *pkt;
> +	int eth_hdr_size;
> +	int ip_hdr_size;
> +	int udp_hdr_size;
> +	int tcp_hdr_size;
> +	int pkt_hdr_size;
> +
> +	if (!net_tx_packet)
> +		return -1;
> +
> +	pkt = (uchar *)net_tx_packet;
> +
> +	eth_hdr_size = net_set_ether(pkt, ether, PROT_IP6);
> +	pkt_hdr_size += eth_hdr_size;
> +	pkt = eth_hdr_size;
> +
> +	switch (proto) {
> +#if defined(CONFIG_PROT_UDP)
> +	case IPPROTO_UDP:
> +		ip_hdr_size = ip6_add_hdr(pkt, &net_ip6, dest, IPPROTO_UDP, 64,
> +					  payload_len + UDP_HDR_SIZE);
> +		pkt_hdr_size += ip_hdr_size;
> +		pkt += ip_hdr_size;
> +
> +		udp_hdr_size = udp6_add_hdr(pkt, dest, dport, sport, payload_len);
> +		pkt_hdr_size += udp_hdr_size;
> +		pkt += udp_hdr_size;
> +		break;
> +#endif
> +	default:
> +		return -EINVAL;
> +	}
> +
>   	/* if MAC address was not discovered yet, save the packet and do
>   	 * neighbour discovery
>   	 */
> -	if (!memcmp(ether, net_null_ethaddr, 6)) {
> +	if (memcmp(ether, net_null_ethaddr, 6) == 0) {
> +		memcpy((uchar *)net_nd_tx_packet,
> +		       (uchar *)net_tx_packet, pkt_hdr_size + payload_len);
> +		memset((uchar *)net_tx_packet, 0, pkt_hdr_size + payload_len);
> +
>   		net_copy_ip6(&net_nd_sol_packet_ip6, dest);
>   		net_nd_packet_mac = ether;
> -
> -		pkt = net_nd_tx_packet;
> -		pkt += net_set_ether(pkt, net_nd_packet_mac, PROT_IP6);
> -		pkt += ip6_add_hdr(pkt, &net_ip6, dest, IPPROTO_UDP, 64,
> -				len + UDP_HDR_SIZE);
> -		memcpy(pkt, (uchar *)udp, len + UDP_HDR_SIZE);
> -
>   		/* size of the waiting packet */
> -		net_nd_tx_packet_size = (pkt - net_nd_tx_packet) +
> -			UDP_HDR_SIZE + len;
> -
> -		/* and do the neighbor solicitation */
> +		net_nd_tx_packet_size = pkt_hdr_size + payload_len;
>   		net_nd_try = 1;
>   		net_nd_timer_start = get_timer(0);
>   		ndisc_request();
>   		return 1;	/* waiting */
>   	}
>   
> -	pkt = (uchar *)net_tx_packet;
> -	pkt += net_set_ether(pkt, ether, PROT_IP6);
> -	pkt += ip6_add_hdr(pkt, &net_ip6, dest, IPPROTO_UDP, 64,
> -			len + UDP_HDR_SIZE);
> -	(void)eth_send(net_tx_packet, pkt - net_tx_packet + UDP_HDR_SIZE + len);
> +	(void)eth_send(net_tx_packet, pkt_hdr_size + payload_len);
>   
>   	return 0;	/* transmitted */
>   }
>   
> +int net_send_udp_packet6(uchar *ether, struct in6_addr *dest, int dport,
> +			 int sport, int len)
> +{
> +	return net_send_ip_packet6(ether, dest, dport, sport, len, IPPROTO_UDP, 0, 0, 0);
> +}
> +
>   int net_ip6_handler(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len)
>   {
>   	struct in_addr zero_ip = {.s_addr = 0 };
> 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);
>   	}
>   }

Reviewed-by: Ying-Chun Liu (PaulLiu) <paul.liu@linaro.org>
Simon Glass July 23, 2023, 3:48 a.m. UTC | #2
Hi Dmitrii,

On Wed, 10 May 2023 at 11:00, Dmitrii Merkurev <dimorinny@google.com> wrote:
>
> 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 <dimorinny@google.com>
> Cc: Ying-Chun Liu (PaulLiu) <paul.liu@linaro.org>
> Cc: Simon Glass <sjg@chromium.org>
> Сс: Joe Hershberger <joe.hershberger@ni.com>
> Сс: Ramon Fried <rfried.dev@gmail.com>
> ---
>  include/net/tcp.h | 109 +++++++++++++--
>  net/net.c         |  18 ++-
>  net/net6.c        |  78 ++++++++---
>  net/tcp.c         | 337 ++++++++++++++++++++++++++++------------------
>  4 files changed, 372 insertions(+), 170 deletions(-)

Reviewed-by: Simon Glass <sjg@chromium.org>

While you are here, or in a follow-up patch, can you please add full
comments to net_set_ip_header(), e,g. what is the pkt param exactly?
The whole packet, before any headers?
Dmitrii Merkurev July 25, 2023, 10:01 p.m. UTC | #3
>
> While you are here, or in a follow-up patch, can you please add full
> comments to net_set_ip_header(), e,g. what is the pkt param exactly?
> The whole packet, before any headers?


It's an IP header. Added comment and renamed the argument on this one and
net_set_udp_header.


On Sun, Jul 23, 2023 at 4:48 AM Simon Glass <sjg@chromium.org> wrote:

> Hi Dmitrii,
>
> On Wed, 10 May 2023 at 11:00, Dmitrii Merkurev <dimorinny@google.com>
> wrote:
> >
> > 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 <dimorinny@google.com>
> > Cc: Ying-Chun Liu (PaulLiu) <paul.liu@linaro.org>
> > Cc: Simon Glass <sjg@chromium.org>
> > Сс: Joe Hershberger <joe.hershberger@ni.com>
> > Сс: Ramon Fried <rfried.dev@gmail.com>
> > ---
> >  include/net/tcp.h | 109 +++++++++++++--
> >  net/net.c         |  18 ++-
> >  net/net6.c        |  78 ++++++++---
> >  net/tcp.c         | 337 ++++++++++++++++++++++++++++------------------
> >  4 files changed, 372 insertions(+), 170 deletions(-)
>
> Reviewed-by: Simon Glass <sjg@chromium.org>
>
> While you are here, or in a follow-up patch, can you please add full
> comments to net_set_ip_header(), e,g. what is the pkt param exactly?
> The whole packet, before any headers?
>
diff mbox series

Patch

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..0b68bf7b13 100644
--- a/net/net.c
+++ b/net/net.c
@@ -916,6 +916,7 @@  int net_send_ip_packet(uchar *ether, struct in_addr dest, int dport, int sport,
 {
 	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,19 +935,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:
diff --git a/net/net6.c b/net/net6.c
index 2dd64c0e16..e395b930b0 100644
--- a/net/net6.c
+++ b/net/net6.c
@@ -324,15 +324,13 @@  int ip6_add_hdr(uchar *xip, struct in6_addr *src, struct in6_addr *dest,
 	return sizeof(struct ip6_hdr);
 }
 
-int net_send_udp_packet6(uchar *ether, struct in6_addr *dest, int dport,
-			 int sport, int len)
+int udp6_add_hdr(uchar *xip, struct in6_addr *dest, int dport, int sport,
+		 int len)
 {
-	uchar *pkt;
 	struct udp_hdr *udp;
 	u16 csum_p;
 
-	udp = (struct udp_hdr *)((uchar *)net_tx_packet + net_eth_hdr_size() +
-			IP6_HDR_SIZE);
+	udp = (struct udp_hdr *)xip;
 
 	udp->udp_dst = htons(dport);
 	udp->udp_src = htons(sport);
@@ -344,39 +342,75 @@  int net_send_udp_packet6(uchar *ether, struct in6_addr *dest, int dport,
 	udp->udp_xsum = csum_ipv6_magic(&net_ip6, dest, len + UDP_HDR_SIZE,
 					IPPROTO_UDP, csum_p);
 
+	return sizeof(struct udp_hdr);
+}
+
+int net_send_ip_packet6(uchar *ether, struct in6_addr *dest, int dport, int sport,
+			int payload_len, int proto, u8 action, u32 tcp_seq_num,
+			u32 tcp_ack_num)
+{
+	uchar *pkt;
+	int eth_hdr_size;
+	int ip_hdr_size;
+	int udp_hdr_size;
+	int tcp_hdr_size;
+	int pkt_hdr_size;
+
+	if (!net_tx_packet)
+		return -1;
+
+	pkt = (uchar *)net_tx_packet;
+
+	eth_hdr_size = net_set_ether(pkt, ether, PROT_IP6);
+	pkt_hdr_size += eth_hdr_size;
+	pkt = eth_hdr_size;
+
+	switch (proto) {
+#if defined(CONFIG_PROT_UDP)
+	case IPPROTO_UDP:
+		ip_hdr_size = ip6_add_hdr(pkt, &net_ip6, dest, IPPROTO_UDP, 64,
+					  payload_len + UDP_HDR_SIZE);
+		pkt_hdr_size += ip_hdr_size;
+		pkt += ip_hdr_size;
+
+		udp_hdr_size = udp6_add_hdr(pkt, dest, dport, sport, payload_len);
+		pkt_hdr_size += udp_hdr_size;
+		pkt += udp_hdr_size;
+		break;
+#endif
+	default:
+		return -EINVAL;
+	}
+
 	/* if MAC address was not discovered yet, save the packet and do
 	 * neighbour discovery
 	 */
-	if (!memcmp(ether, net_null_ethaddr, 6)) {
+	if (memcmp(ether, net_null_ethaddr, 6) == 0) {
+		memcpy((uchar *)net_nd_tx_packet,
+		       (uchar *)net_tx_packet, pkt_hdr_size + payload_len);
+		memset((uchar *)net_tx_packet, 0, pkt_hdr_size + payload_len);
+
 		net_copy_ip6(&net_nd_sol_packet_ip6, dest);
 		net_nd_packet_mac = ether;
-
-		pkt = net_nd_tx_packet;
-		pkt += net_set_ether(pkt, net_nd_packet_mac, PROT_IP6);
-		pkt += ip6_add_hdr(pkt, &net_ip6, dest, IPPROTO_UDP, 64,
-				len + UDP_HDR_SIZE);
-		memcpy(pkt, (uchar *)udp, len + UDP_HDR_SIZE);
-
 		/* size of the waiting packet */
-		net_nd_tx_packet_size = (pkt - net_nd_tx_packet) +
-			UDP_HDR_SIZE + len;
-
-		/* and do the neighbor solicitation */
+		net_nd_tx_packet_size = pkt_hdr_size + payload_len;
 		net_nd_try = 1;
 		net_nd_timer_start = get_timer(0);
 		ndisc_request();
 		return 1;	/* waiting */
 	}
 
-	pkt = (uchar *)net_tx_packet;
-	pkt += net_set_ether(pkt, ether, PROT_IP6);
-	pkt += ip6_add_hdr(pkt, &net_ip6, dest, IPPROTO_UDP, 64,
-			len + UDP_HDR_SIZE);
-	(void)eth_send(net_tx_packet, pkt - net_tx_packet + UDP_HDR_SIZE + len);
+	(void)eth_send(net_tx_packet, pkt_hdr_size + payload_len);
 
 	return 0;	/* transmitted */
 }
 
+int net_send_udp_packet6(uchar *ether, struct in6_addr *dest, int dport,
+			 int sport, int len)
+{
+	return net_send_ip_packet6(ether, dest, dport, sport, len, IPPROTO_UDP, 0, 0, 0);
+}
+
 int net_ip6_handler(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len)
 {
 	struct in_addr zero_ip = {.s_addr = 0 };
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);
 	}
 }