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

Jules Maselbas jmaselbas at kalray.eu
Fri Aug 12 09:17:18 PDT 2022


Hi Sascha,

On Fri, Aug 12, 2022 at 09:04:48AM +0200, Sascha Hauer wrote:
> 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.
Yes... I agree that integrating a "proven" and field-tested TCP
implementation (including IPv6) is the saner solution, over rolling out
a custom implementation.

This is more a PoC and it was an excuse to take a deeper look at how TCP
works and what it requires.

> 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