[RFC PATCH 3/5] net: Add simple TCP support

Sascha Hauer sha at pengutronix.de
Fri Aug 12 00:04:48 PDT 2022


Hi Jules,

On Tue, Aug 09, 2022 at 03:20:19PM +0200, Jules Maselbas wrote:
> This is a very simple TCP implementation that only support connecting
> to servers (passive open and listen are not supported).
> 
> This also doesn't handle multiples segments and the TCP window size is
> smaller than the MTU, this will hopefully make the sender only send one
> segment at a time.

I am impressed how simple TCP support can be. I never even considered
trying to write TCP support from scratch.
My plan for TCP support was always to integrate some existing stack like
lwip into barebox to get a stack that has proven to work elsewhere. Also
IPv6 support is still a pending feature which is becoming more and more
interesting and we would get that for free with integrating an existing
IP stack.
Given that I am sceptical if we really want to follow the approach of
developping TCP from scratch.
Nevertheless it's really cool to see how simple it could be ;) Nice
stuff!

Sascha


> 
> Signed-off-by: Jules Maselbas <jmaselbas at kalray.eu>
> ---
>  common/misc.c |  12 +-
>  include/net.h | 118 +++++++++++++++
>  net/net.c     | 406 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 531 insertions(+), 5 deletions(-)
> 
> diff --git a/common/misc.c b/common/misc.c
> index 0f6de3e9e5..30e94f5a8b 100644
> --- a/common/misc.c
> +++ b/common/misc.c
> @@ -57,6 +57,13 @@ const char *strerror(int errnum)
>  	case	EPROBE_DEFER	: str = "Requested probe deferral"; break;
>  	case	ELOOP		: str = "Too many symbolic links encountered"; break;
>  	case	ENODATA		: str = "No data available"; break;
> +#ifdef CONFIG_NET
> +	case	ENETRESET	: str = "Network dropped connection because of reset"; break;
> +	case	ECONNABORTED	: str = "Software caused connection abort"; break;
> +	case	ECONNRESET	: str = "Connection reset by peer"; break;
> +	case	ENOBUFS		: str = "No buffer space available"; break;
> +	case	ECONNREFUSED	: str = "Connection refused"; break;
> +#endif
>  #if 0 /* These are probably not needed */
>  	case	ENOTBLK		: str = "Block device required"; break;
>  	case	EFBIG		: str = "File too large"; break;
> @@ -79,11 +86,6 @@ const char *strerror(int errnum)
>  	case	EAFNOSUPPORT	: str = "Address family not supported by protocol"; break;
>  	case	EADDRINUSE	: str = "Address already in use"; break;
>  	case	EADDRNOTAVAIL	: str = "Cannot assign requested address"; break;
> -	case	ENETRESET	: str = "Network dropped connection because of reset"; break;
> -	case	ECONNABORTED	: str = "Software caused connection abort"; break;
> -	case	ECONNRESET	: str = "Connection reset by peer"; break;
> -	case	ENOBUFS		: str = "No buffer space available"; break;
> -	case	ECONNREFUSED	: str = "Connection refused"; break;
>  	case	EHOSTDOWN	: str = "Host is down"; break;
>  	case	EALREADY	: str = "Operation already in progress"; break;
>  	case	EINPROGRESS	: str = "Operation now in progress"; break;
> diff --git a/include/net.h b/include/net.h
> index b50b6e76c8..76b64ccb21 100644
> --- a/include/net.h
> +++ b/include/net.h
> @@ -143,6 +143,7 @@ struct ethernet {
>  #define PROT_VLAN	0x8100		/* IEEE 802.1q protocol		*/
>  
>  #define IPPROTO_ICMP	 1	/* Internet Control Message Protocol	*/
> +#define IPPROTO_TCP	 6	/* Transmission Control Protocol	*/
>  #define IPPROTO_UDP	17	/* User Datagram Protocol		*/
>  
>  #define IP_BROADCAST    0xffffffff /* Broadcast IP aka 255.255.255.255 */
> @@ -171,6 +172,67 @@ struct udphdr {
>  	uint16_t	uh_sum;		/* udp checksum */
>  } __attribute__ ((packed));
>  
> +/* pseudo header for checksum */
> +struct psdhdr {
> +	uint32_t	saddr;
> +	uint32_t	daddr;
> +	uint16_t	proto;
> +	uint16_t	ttlen;
> +} __attribute__ ((packed));
> +
> +struct tcphdr {
> +	uint16_t	src;	/* source port */
> +	uint16_t	dst;	/* destination port */
> +	uint32_t	seq;	/* sequence number */
> +	uint32_t	ack;	/* acknowledge number */
> +	uint16_t 	doff_flag;	/* data offset and flags */
> +#define TCP_DOFF_MASK	0xf
> +#define TCP_DOFF_SHIFT	12
> +#define TCP_FLAG_FIN	BIT(0)
> +#define TCP_FLAG_SYN	BIT(1)
> +#define TCP_FLAG_RST	BIT(2)
> +#define TCP_FLAG_PSH	BIT(3)
> +#define TCP_FLAG_ACK	BIT(4)
> +#define TCP_FLAG_URG	BIT(5)
> +#define TCP_FLAG_ECE	BIT(6)
> +#define TCP_FLAG_CWR	BIT(7)
> +#define TCP_FLAG_NS	BIT(8)
> +#define TCP_FLAG_MASK	0x1ff
> +	uint16_t	wnd;	/* window size */
> +	uint16_t	sum;	/* header and data checksum */
> +	uint16_t	urp;	/* urgent pointer (if URG is set) */
> +	/* The options start here. */
> +} __attribute__ ((packed));
> +
> +enum tcp_state {
> +	TCP_CLOSED = 0,
> +	TCP_LISTEN,
> +	TCP_SYN_SENT,
> +	TCP_SYN_RECV,
> +	TCP_ESTABLISHED,
> +	TCP_FIN_WAIT1,
> +	TCP_FIN_WAIT2,
> +	TCP_TIME_WAIT,
> +	TCP_CLOSE_WAIT,
> +	TCP_LAST_ACK,
> +	TCP_CLOSING,
> +};
> +
> +/* Transmission Control Block */
> +struct tcb {
> +	uint32_t snd_una;
> +	uint32_t snd_nxt;
> +	uint32_t snd_wnd;
> +	uint16_t snd_urp;
> +	uint32_t snd_wl1;
> +	uint32_t snd_wl2;
> +	uint32_t rcv_nxt;
> +	uint32_t rcv_wnd;
> +	uint16_t rcv_urp;
> +	uint32_t iss;
> +	uint32_t irs;
> +};
> +
>  /*
>   *	Address Resolution Protocol (ARP) header.
>   */
> @@ -264,6 +326,13 @@ struct eth_device *net_route(IPaddr_t ip);
>  /* Do the work */
>  void net_poll(void);
>  
> +static inline size_t net_tcp_data_offset(struct tcphdr *tcp)
> +{
> +	uint16_t doff;
> +	doff = ntohs(tcp->doff_flag) >> TCP_DOFF_SHIFT;
> +	return doff * sizeof(uint32_t);
> +}
> +
>  static inline struct iphdr *net_eth_to_iphdr(char *pkt)
>  {
>  	return (struct iphdr *)(pkt + ETHER_HDR_SIZE);
> @@ -274,6 +343,11 @@ static inline struct udphdr *net_eth_to_udphdr(char *pkt)
>  	return (struct udphdr *)(net_eth_to_iphdr(pkt) + 1);
>  }
>  
> +static inline struct tcphdr *net_eth_to_tcphdr(char *pkt)
> +{
> +	return (struct tcphdr *)(net_eth_to_iphdr(pkt) + 1);
> +}
> +
>  static inline struct icmphdr *net_eth_to_icmphdr(char *pkt)
>  {
>  	return (struct icmphdr *)(net_eth_to_iphdr(pkt) + 1);
> @@ -295,8 +369,28 @@ static inline int net_eth_to_udplen(char *pkt)
>  	return ntohs(udp->uh_ulen) - 8;
>  }
>  
> +static inline char *net_eth_to_tcp_payload(char *pkt)
> +{
> +	struct tcphdr *tcp = net_eth_to_tcphdr(pkt);
> +	return ((char *)tcp) + net_tcp_data_offset(tcp);
> +}
> +
> +static inline int net_eth_to_iplen(char *pkt)
> +{
> +	struct iphdr *ip = net_eth_to_iphdr(pkt);
> +	return ntohs(ip->tot_len) - sizeof(struct iphdr);
> +}
> +
> +static inline int net_eth_to_tcplen(char *pkt)
> +{
> +	struct tcphdr *tcp = net_eth_to_tcphdr(pkt);
> +	return net_eth_to_iplen(pkt) - net_tcp_data_offset(tcp);
> +}
> +
>  int net_checksum_ok(unsigned char *, int);	/* Return true if cksum OK	*/
>  uint16_t net_checksum(unsigned char *, int);	/* Calculate the checksum	*/
> +int tcp_checksum_ok(struct iphdr *ip, struct tcphdr *tcp, int len);
> +uint16_t tcp_checksum(struct iphdr *ip, struct tcphdr *tcp, int len);
>  
>  /*
>   * The following functions are a bit ugly, but necessary to deal with
> @@ -459,12 +553,18 @@ struct net_connection {
>  	struct ethernet *et;
>  	struct iphdr *ip;
>  	struct udphdr *udp;
> +	struct tcphdr *tcp;
>  	struct eth_device *edev;
>  	struct icmphdr *icmp;
>  	unsigned char *packet;
>  	struct list_head list;
>  	rx_handler_f *handler;
>  	int proto;
> +	int state;
> +	int ret;
> +	union {
> +		struct tcb tcb;
> +	};
>  	void *priv;
>  };
>  
> @@ -480,6 +580,13 @@ struct net_connection *net_udp_eth_new(struct eth_device *edev, IPaddr_t dest,
>                                         uint16_t dport, rx_handler_f *handler,
>                                         void *ctx);
>  
> +struct net_connection *net_tcp_new(IPaddr_t dest, uint16_t dport,
> +		rx_handler_f *handler, void *ctx);
> +
> +struct net_connection *net_tcp_eth_new(struct eth_device *edev, IPaddr_t dest,
> +                                       uint16_t dport, rx_handler_f *handler,
> +                                       void *ctx);
> +
>  struct net_connection *net_icmp_new(IPaddr_t dest, rx_handler_f *handler,
>  		void *ctx);
>  
> @@ -497,6 +604,17 @@ static inline void *net_udp_get_payload(struct net_connection *con)
>  		sizeof(struct udphdr);
>  }
>  
> +static inline void *net_tcp_get_payload(struct net_connection *con)
> +{
> +	return con->packet + sizeof(struct ethernet) + sizeof(struct iphdr) +
> +		net_tcp_data_offset(con->tcp);
> +}
> +
> +int net_tcp_listen(struct net_connection *con);
> +int net_tcp_open(struct net_connection *con);
> +int net_tcp_send(struct net_connection *con, int len);
> +int net_tcp_close(struct net_connection *con);
> +
>  int net_udp_send(struct net_connection *con, int len);
>  int net_icmp_send(struct net_connection *con, int len);
>  
> diff --git a/net/net.c b/net/net.c
> index 9f799f252d..855bb8e4c2 100644
> --- a/net/net.c
> +++ b/net/net.c
> @@ -47,6 +47,8 @@ static struct net_connection *net_ip_get_con(int proto, uint16_t port)
>  			continue;
>  		if (con->proto == IPPROTO_UDP && ntohs(con->udp->uh_sport) == port)
>  			return con;
> +		if (con->proto == IPPROTO_TCP && ntohs(con->tcp->src) == port)
> +			return con;
>  	}
>  
>  	return NULL;
> @@ -99,6 +101,31 @@ uint16_t net_checksum(unsigned char *ptr, int len)
>  	return xsum & 0xffff;
>  }
>  
> +uint16_t tcp_checksum(struct iphdr *ip, struct tcphdr *tcp, int len)
> +{
> +	uint32_t xsum;
> +	struct psdhdr pseudo;
> +	size_t hdrsize = net_tcp_data_offset(tcp);
> +
> +	pseudo.saddr = ip->saddr;
> +	pseudo.daddr = ip->daddr;
> +	pseudo.proto = htons(ip->protocol);
> +	pseudo.ttlen = htons(hdrsize + len);
> +
> +	xsum = net_checksum((void *)&pseudo, sizeof(struct psdhdr));
> +	xsum += net_checksum((void *)tcp, hdrsize + len);
> +
> +	while (xsum > 0xffff)
> +		xsum = (xsum & 0xffff) + (xsum >> 16);
> +
> +	return xsum;
> +}
> +
> +int tcp_checksum_ok(struct iphdr *ip, struct tcphdr *tcp, int len)
> +{
> +	return tcp_checksum(ip, tcp, len) == 0xffff;
> +}
> +
>  IPaddr_t getenv_ip(const char *name)
>  {
>  	IPaddr_t ip;
> @@ -335,6 +362,11 @@ static uint16_t net_udp_new_localport(void)
>  	return net_new_localport(IPPROTO_UDP);
>  }
>  
> +static uint16_t net_tcp_new_localport(void)
> +{
> +	return net_new_localport(IPPROTO_TCP);
> +}
> +
>  IPaddr_t net_get_serverip(void)
>  {
>  	IPaddr_t ip;
> @@ -422,6 +454,7 @@ static struct net_connection *net_new(struct eth_device *edev, IPaddr_t dest,
>  	con->et = (struct ethernet *)con->packet;
>  	con->ip = (struct iphdr *)(con->packet + ETHER_HDR_SIZE);
>  	con->udp = (struct udphdr *)(con->packet + ETHER_HDR_SIZE + sizeof(struct iphdr));
> +	con->tcp = (struct tcphdr *)(con->packet + ETHER_HDR_SIZE + sizeof(struct iphdr));
>  	con->icmp = (struct icmphdr *)(con->packet + ETHER_HDR_SIZE + sizeof(struct iphdr));
>  	con->handler = handler;
>  
> @@ -452,6 +485,30 @@ out:
>  	return ERR_PTR(ret);
>  }
>  
> +struct net_connection *net_tcp_eth_new(struct eth_device *edev, IPaddr_t dest,
> +				       uint16_t dport, rx_handler_f *handler,
> +				       void *ctx)
> +{
> +	struct net_connection *con = net_new(edev, dest, handler, ctx);
> +	uint16_t doff;
> +
> +	if (IS_ERR(con))
> +		return con;
> +
> +	con->proto = IPPROTO_TCP;
> +	con->state = TCP_CLOSED;
> +	con->tcp->src = htons(net_tcp_new_localport());
> +	con->tcp->dst = htons(dport);
> +	con->tcp->seq = 0;
> +	con->tcp->ack = 0;
> +	doff = sizeof(struct tcphdr) / sizeof(uint32_t);
> +	con->tcp->doff_flag = htons(doff << TCP_DOFF_SHIFT);
> +	con->tcp->urp = 0;
> +	con->ip->protocol = IPPROTO_TCP;
> +
> +	return con;
> +}
> +
>  struct net_connection *net_udp_eth_new(struct eth_device *edev, IPaddr_t dest,
>  				       uint16_t dport, rx_handler_f *handler,
>  				       void *ctx)
> @@ -475,6 +532,12 @@ struct net_connection *net_udp_new(IPaddr_t dest, uint16_t dport,
>  	return net_udp_eth_new(NULL, dest, dport, handler, ctx);
>  }
>  
> +struct net_connection *net_tcp_new(IPaddr_t dest, uint16_t dport,
> +		rx_handler_f *handler, void *ctx)
> +{
> +	return net_tcp_eth_new(NULL, dest, dport, handler, ctx);
> +}
> +
>  struct net_connection *net_icmp_new(IPaddr_t dest, rx_handler_f *handler,
>  		void *ctx)
>  {
> @@ -514,6 +577,167 @@ int net_udp_send(struct net_connection *con, int len)
>  	return net_ip_send(con, sizeof(struct udphdr) + len);
>  }
>  
> +static int tcp_send(struct net_connection *con, int len, uint16_t flags)
> +{
> +	size_t hdr_size = net_tcp_data_offset(con->tcp);
> +
> +	con->tcp->doff_flag &= ~htons(TCP_FLAG_MASK);
> +	con->tcp->doff_flag |= htons(flags);
> +	con->tcp->sum = 0;
> +	con->tcp->sum = ~tcp_checksum(con->ip, con->tcp, len);
> +
> +	return net_ip_send(con, hdr_size + len);
> +}
> +
> +int net_tcp_send(struct net_connection *con, int len)
> +{
> +	struct tcb *tcb = &con->tcb;
> +	uint16_t flag = 0;
> +
> +	if (con->proto != IPPROTO_TCP)
> +		return -EPROTOTYPE;
> +	switch (con->state) {
> +	case TCP_CLOSED:
> +		return -ENOTCONN;
> +	case TCP_LISTEN:
> +		/* TODO: proceed as open */
> +		break;
> +	case TCP_SYN_SENT:
> +	case TCP_SYN_RECV:
> +		/* queue request or "error:  insufficient resources". */
> +		break;
> +	case TCP_ESTABLISHED:
> +	case TCP_CLOSE_WAIT:
> +		/* proceed */
> +		break;
> +	case TCP_FIN_WAIT1:
> +	case TCP_FIN_WAIT2:
> +	case TCP_TIME_WAIT:
> +	case TCP_LAST_ACK:
> +	case TCP_CLOSING:
> +		return -ESHUTDOWN;
> +	}
> +
> +	con->tcp->seq = htonl(tcb->snd_nxt);
> +	tcb->snd_nxt += len;
> +	flag |= TCP_FLAG_PSH;
> +	if (1 || ntohl(con->tcp->ack) < con->tcb.rcv_nxt) {
> +		flag |= TCP_FLAG_ACK;
> +		con->tcp->ack = htonl(con->tcb.rcv_nxt);
> +	} else {
> +		con->tcp->ack = 0;
> +	}
> +
> +	return tcp_send(con, len, flag);
> +}
> +
> +int net_tcp_listen(struct net_connection *con)
> +{
> +	if (con->proto != IPPROTO_TCP)
> +		return -EPROTOTYPE;
> +
> +	con->state = TCP_LISTEN;
> +	return -1;
> +}
> +
> +int net_tcp_open(struct net_connection *con)
> +{
> +	struct tcphdr *tcp = net_eth_to_tcphdr(con->packet);
> +	struct tcb *tcb = &con->tcb;
> +	int ret;
> +
> +	if (con->proto != IPPROTO_TCP)
> +		return -EPROTOTYPE;
> +	switch (con->state) {
> +	case TCP_CLOSED:
> +	case TCP_LISTEN:
> +		break;
> +	case TCP_SYN_SENT:
> +	case TCP_SYN_RECV:
> +	case TCP_ESTABLISHED:
> +	case TCP_FIN_WAIT1:
> +	case TCP_FIN_WAIT2:
> +	case TCP_TIME_WAIT:
> +	case TCP_CLOSE_WAIT:
> +	case TCP_LAST_ACK:
> +	case TCP_CLOSING:
> +		return -EISCONN;
> +	}
> +
> +	/* use a window smaller than the MTU, as only one tcp segment packet
> +	 * can be received at time */
> +	tcb->rcv_wnd = 1024;
> +	tcb->snd_wnd = 0;
> +	tcb->iss = random32() + (get_time_ns() >> 10);
> +	con->state = TCP_SYN_SENT;
> +
> +	tcp->wnd = htons(tcb->rcv_wnd);
> +	tcp->seq = htonl(tcb->iss);
> +	tcb->snd_una = tcb->iss;
> +	tcb->snd_nxt = tcb->iss + 1;
> +	ret = tcp_send(con, 0, TCP_FLAG_SYN);
> +	if (ret)
> +		return ret;
> +
> +	ret = wait_on_timeout(6000 * MSECOND, con->state == TCP_ESTABLISHED);
> +	if (ret)
> +		return -ETIMEDOUT;
> +
> +	return con->ret; // TODO: return 0 ?
> +}
> +
> +int net_tcp_close(struct net_connection *con)
> +{
> +	struct tcphdr *tcp = net_eth_to_tcphdr(con->packet);
> +	struct tcb *tcb = &con->tcb;
> +	int ret;
> +
> +	if (con->proto != IPPROTO_TCP)
> +		return -EPROTOTYPE;
> +	switch (con->state) {
> +	case TCP_CLOSED:
> +		return -ENOTCONN;
> +	case TCP_LISTEN:
> +	case TCP_SYN_SENT:
> +		con->state = TCP_CLOSED;
> +		return 0;
> +		break;
> +	case TCP_SYN_RECV:
> +	case TCP_ESTABLISHED:
> +		/* wait for pending send */
> +		con->state = TCP_FIN_WAIT1;
> +		break;
> +	case TCP_FIN_WAIT1:
> +	case TCP_FIN_WAIT2:
> +		/* error: connection closing */
> +		return -1;
> +	case TCP_TIME_WAIT:
> +	case TCP_LAST_ACK:
> +	case TCP_CLOSING:
> +		/* error: connection closing */
> +		return -1;
> +	case TCP_CLOSE_WAIT:
> +		/* queue close request after pending sends */
> +		con->state = TCP_LAST_ACK;
> +		break;
> +	}
> +
> +	tcp->seq = htonl(tcb->snd_nxt);
> +	tcp->ack = htonl(tcb->rcv_nxt);
> +	tcb->snd_nxt += 1;
> +	ret = tcp_send(con, 0, TCP_FLAG_FIN | TCP_FLAG_ACK);
> +	if (ret)
> +		return ret;
> +
> +	ret = wait_on_timeout(1000 * MSECOND, con->state == TCP_CLOSED);
> +	if (ret)
> +		return -ETIMEDOUT;
> +
> +	net_unregister(con);
> +
> +	return con->ret; // TODO: return 0 ?
> +}
> +
>  int net_icmp_send(struct net_connection *con, int len)
>  {
>  	con->icmp->checksum = ~net_checksum((unsigned char *)con->icmp,
> @@ -624,6 +848,186 @@ static int net_handle_udp(unsigned char *pkt, int len)
>  	return -EINVAL;
>  }
>  
> +static int net_handle_tcp(unsigned char *pkt, int len)
> +{
> +	size_t min_size = ETHER_HDR_SIZE + sizeof(struct iphdr);
> +	struct net_connection *con;
> +	struct iphdr *ip = net_eth_to_iphdr(pkt);
> +	struct tcphdr *tcp = net_eth_to_tcphdr(pkt);
> +	struct tcb *tcb;
> +	uint16_t flag;
> +	uint16_t doff;
> +	uint32_t tcp_len;
> +	uint32_t seg_len;
> +	uint32_t seg_ack;
> +	uint32_t seg_seq;
> +	uint32_t seg_last;
> +	uint32_t rcv_wnd;
> +	uint32_t rcv_nxt;
> +	int seg_accept = 0;
> +
> +	if (len < (min_size + sizeof(struct tcphdr)))
> +		goto bad;
> +	flag = ntohs(tcp->doff_flag) & TCP_FLAG_MASK;
> +	doff = net_tcp_data_offset(tcp);
> +	if (doff < sizeof(struct tcphdr))
> +		goto bad;
> +	if (len < (min_size + doff))
> +		goto bad;
> +
> +	seg_ack = ntohl(tcp->ack);
> +	seg_seq = ntohl(tcp->seq);
> +	tcp_len = net_eth_to_tcplen(pkt);
> +	seg_len = tcp_len;
> +	seg_len += !!(flag & TCP_FLAG_FIN);
> +	seg_len += !!(flag & TCP_FLAG_SYN);
> +
> +	if (!tcp_checksum_ok(ip, tcp, tcp_len))
> +		goto bad;
> +
> +	con = net_ip_get_con(IPPROTO_TCP, ntohs(tcp->dst));
> +	if (con == NULL)
> +		goto bad;
> +	tcb = &con->tcb;
> +
> +	/* segment arrives */
> +	seg_last = seg_seq + seg_len - 1;
> +	rcv_wnd = tcb->rcv_wnd;
> +	rcv_nxt = tcb->rcv_nxt;
> +
> +	if (seg_len == 0 && rcv_wnd == 0)
> +		seg_accept = seg_seq == rcv_nxt;
> +	if (seg_len == 0 && rcv_wnd > 0)
> +		seg_accept = rcv_nxt <= seg_seq && seg_seq < (rcv_nxt + rcv_wnd);
> +	if (seg_len > 0 && rcv_wnd == 0)
> +		seg_accept = 0; /* not acceptable */
> +	if (seg_len > 0 && rcv_wnd > 0)
> +		seg_accept = (rcv_nxt <= seg_seq && seg_seq < (rcv_nxt + rcv_wnd))
> +			|| (rcv_nxt <= seg_last && seg_last < (rcv_nxt + rcv_wnd));
> +
> +	switch (con->state) {
> +	case TCP_CLOSED:
> +		if (flag & TCP_FLAG_RST) {
> +			goto drop;
> +		}
> +		if (flag & TCP_FLAG_ACK) {
> +			con->tcp->seq = 0;
> +			con->tcp->ack = htonl(seg_seq + seg_len);
> +			con->ret = tcp_send(con, 0, TCP_FLAG_RST | TCP_FLAG_ACK);
> +		} else  {
> +			con->tcp->seq = htonl(seg_ack);
> +			con->ret = tcp_send(con, 0, TCP_FLAG_RST);
> +		}
> +		break;
> +	case TCP_LISTEN:
> +		/* TODO */
> +		break;
> +	case TCP_SYN_SENT:
> +		if (flag & TCP_FLAG_ACK) {
> +			if (seg_ack <= tcb->iss || seg_ack > tcb->snd_nxt) {
> +				if (flag & TCP_FLAG_RST)
> +					goto drop;
> +				con->tcp->seq = htonl(seg_ack);
> +				return tcp_send(con, 0, TCP_FLAG_RST);
> +			}
> +			if (tcb->snd_una > seg_ack || seg_ack > tcb->snd_nxt)
> +				goto drop; /* unacceptable */
> +		}
> +		if (flag & TCP_FLAG_RST) {
> +			con->state = TCP_CLOSED;
> +			con->ret = -ENETRESET;
> +			break;
> +		}
> +		if ((flag & TCP_FLAG_SYN) && !(flag & TCP_FLAG_RST)) {
> +			tcb->irs = seg_seq;
> +			tcb->rcv_nxt = seg_seq + 1;
> +			if (flag & TCP_FLAG_ACK)
> +				tcb->snd_una = seg_ack;
> +			if (tcb->snd_una > tcb->iss) {
> +				con->state = TCP_ESTABLISHED;
> +				con->tcp->seq = htonl(tcb->snd_nxt);
> +				con->tcp->ack = htonl(tcb->rcv_nxt);
> +				con->ret = tcp_send(con, 0, TCP_FLAG_ACK);
> +			} else {
> +				con->state = TCP_SYN_RECV;
> +				tcb->snd_nxt = tcb->iss + 1;
> +				con->tcp->seq = htonl(tcb->iss);
> +				con->tcp->ack = htonl(tcb->rcv_nxt);
> +				con->ret = tcp_send(con, 0, TCP_FLAG_SYN | TCP_FLAG_ACK);
> +			}
> +		}
> +		break;
> +	case TCP_SYN_RECV:
> +	case TCP_ESTABLISHED:
> +		if (flag & TCP_FLAG_RST) {
> +			/* TODO: if passive open then return to LISTEN */
> +			con->state = TCP_CLOSED;
> +			con->ret = -ECONNREFUSED;
> +			break;
> +		}
> +		if (!seg_accept) {
> +			/* segment is not acceptable, send an ack unless RST bit
> +			 * is set (done above) */
> +			con->tcp->seq = htonl(tcb->snd_nxt);
> +			con->tcp->ack = htonl(tcb->rcv_nxt);
> +			con->ret = tcp_send(con, 0, TCP_FLAG_ACK);
> +			goto drop;
> +		}
> +		if (flag & TCP_FLAG_FIN && flag & TCP_FLAG_ACK)
> +			con->state = TCP_CLOSE_WAIT;
> +
> +		if (flag & TCP_FLAG_ACK)
> +			tcb->snd_una = seg_ack;
> +
> +		tcb->rcv_nxt += seg_len;
> +		con->tcp->seq = htonl(tcb->snd_nxt);
> +		if (seg_len) {
> +			con->tcp->ack = htonl(tcb->rcv_nxt);
> +			con->ret = tcp_send(con, 0, TCP_FLAG_ACK |
> +					    /* send FIN+ACK if FIN is set */
> +					    (flag & TCP_FLAG_FIN));
> +		}
> +		con->handler(con->priv, pkt, len);
> +		break;
> +	case TCP_FIN_WAIT1:
> +		if (flag & TCP_FLAG_FIN)
> +			con->state = TCP_CLOSING;
> +		if (flag & TCP_FLAG_ACK)
> +			tcb->snd_una = seg_ack;
> +		/* fall-through */
> +	case TCP_FIN_WAIT2:
> +		tcb->rcv_nxt += seg_len;
> +		con->tcp->seq = htonl(tcb->snd_nxt);
> +		if (seg_len) {
> +			con->tcp->ack = htonl(tcb->rcv_nxt);
> +			con->ret = tcp_send(con, 0, TCP_FLAG_ACK);
> +		}
> +	case TCP_CLOSE_WAIT:
> +		/* all segment queues should be flushed */
> +		if (flag & TCP_FLAG_RST) {
> +			con->state = TCP_CLOSED;
> +			con->ret = -ENETRESET;
> +			break;
> +		}
> +		break;
> +	case TCP_CLOSING:
> +		con->state = TCP_TIME_WAIT;
> +	case TCP_LAST_ACK:
> +	case TCP_TIME_WAIT:
> +		if (flag & TCP_FLAG_RST) {
> +			con->state = TCP_CLOSED;
> +			con->ret = 0;
> +		}
> +		break;
> +	}
> +	return con->ret;
> +drop:
> +	return 0;
> +bad:
> +	net_bad_packet(pkt, len);
> +	return 0;
> +}
> +
>  static int ping_reply(struct eth_device *edev, unsigned char *pkt, int len)
>  {
>  	struct ethernet *et = (struct ethernet *)pkt;
> @@ -711,6 +1115,8 @@ static int net_handle_ip(struct eth_device *edev, unsigned char *pkt, int len)
>  		return net_handle_icmp(edev, pkt, len);
>  	case IPPROTO_UDP:
>  		return net_handle_udp(pkt, len);
> +	case IPPROTO_TCP:
> +		return net_handle_tcp(pkt, len);
>  	}
>  
>  	return 0;
> -- 
> 2.17.1
> 
> 
> 

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |



More information about the barebox mailing list