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@xxxxxxxxx> > > --- > > 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 | > >