[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