[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