summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/core/nic.c298
-rw-r--r--src/include/ip.h2
-rw-r--r--src/include/tcp.h10
-rw-r--r--src/proto/tcp.c286
4 files changed, 295 insertions, 301 deletions
diff --git a/src/core/nic.c b/src/core/nic.c
index 53dac57e..fc8deebe 100644
--- a/src/core/nic.c
+++ b/src/core/nic.c
@@ -219,7 +219,6 @@ static int rarp(void);
#else
static int bootp(void);
#endif
-static unsigned short tcpudpchksum(struct iphdr *ip);
/*
@@ -320,9 +319,6 @@ static int nic_load ( struct type_dev *type_dev,
char *kernel;
/* Now use TFTP to load file */
-#ifdef DOWNLOAD_PROTO_NFS
- rpc_init();
-#endif
kernel = KERNEL_BUF[0] == '\0' ?
#ifdef DEFAULT_BOOTFILE
DEFAULT_BOOTFILE
@@ -529,29 +525,6 @@ void build_udp_hdr(unsigned long destip,
udp->chksum = 0xffff;
}
-#ifdef DOWNLOAD_PROTO_HTTP
-void build_tcp_hdr(unsigned long destip, unsigned int srcsock,
- unsigned int destsock, long send_seq, long recv_seq,
- int window, int flags, int ttl, int len, const void *buf)
-{
- struct iphdr *ip;
- struct tcphdr *tcp;
- ip = (struct iphdr *)buf;
- build_ip_hdr(destip, ttl, IP_TCP, 0, len, buf);
- tcp = (struct tcphdr *)(ip + 1);
- tcp->src = htons(srcsock);
- tcp->dst = htons(destsock);
- tcp->seq = htonl(send_seq);
- tcp->ack = htonl(recv_seq);
- tcp->ctrl = htons(flags + (5 << 12)); /* No TCP options */
- tcp->window = htons(window);
- tcp->chksum = 0;
- if ((tcp->chksum = tcpudpchksum(ip)) == 0)
- tcp->chksum = 0xffff;
-}
-#endif
-
-
/**************************************************************************
UDP_TRANSMIT - Send an UDP datagram
**************************************************************************/
@@ -562,37 +535,6 @@ int udp_transmit(unsigned long destip, unsigned int srcsock,
return ip_transmit(len, buf);
}
-/**************************************************************************
-TCP_TRANSMIT - Send a TCP packet
-**************************************************************************/
-#ifdef DOWNLOAD_PROTO_HTTP
-int tcp_transmit(unsigned long destip, unsigned int srcsock,
- unsigned int destsock, long send_seq, long recv_seq,
- int window, int flags, int len, const void *buf)
-{
- build_tcp_hdr(destip, srcsock, destsock, send_seq, recv_seq,
- window, flags, 60, len, buf);
- return ip_transmit(len, buf);
-}
-
-int tcp_reset(struct iphdr *ip) {
- struct tcphdr *tcp = (struct tcphdr *)(ip + 1);
- char buf[sizeof(struct iphdr) + sizeof(struct tcphdr)];
-
- if (!(tcp->ctrl & htons(RST))) {
- long seq = ntohl(tcp->seq) + ntohs(ip->len) -
- sizeof(struct iphdr) -
- ((ntohs(tcp->ctrl) >> 10) & 0x3C);
- if (tcp->ctrl & htons(SYN|FIN))
- seq++;
- return tcp_transmit(ntohl(ip->src.s_addr),
- ntohs(tcp->dst), ntohs(tcp->src),
- tcp->ctrl&htons(ACK) ? ntohl(tcp->ack) : 0,
- seq, TCP_MAX_WINDOW, RST, sizeof(buf), buf);
- }
- return (1);
-}
-#endif
/**************************************************************************
QDRAIN - clear the nic's receive queue
@@ -616,9 +558,6 @@ void rx_qdrain(void)
await_reply(await_qdrain, 0, NULL, 0);
}
-#ifdef DOWNLOAD_PROTO_TFTP
-#endif /* DOWNLOAD_PROTO_TFTP */
-
#ifdef RARP_NOT_BOOTP
/**************************************************************************
RARP - Get my IP address and load information
@@ -876,7 +815,7 @@ static int bootp(void)
}
#endif /* RARP_NOT_BOOTP */
-static uint16_t tcpudpchksum(struct iphdr *ip)
+uint16_t tcpudpchksum(struct iphdr *ip)
{
struct udp_pseudo_hdr pseudo;
uint16_t checksum;
@@ -1034,239 +973,6 @@ void join_group(int slot, unsigned long group)
#include "proto_eth_slow.c"
-/**************************************************************************
-TCP - Simple-minded TCP stack. Can only send data once and then
- receive the response. The algorithm for computing window
- sizes and delaying ack's is currently broken, and thus
- disabled. Performance would probably improve a little, if
- this gets fixed. FIXME
-**************************************************************************/
-#ifdef DOWNLOAD_PROTO_HTTP
-static int await_tcp(int ival, void *ptr, unsigned short ptype __unused,
- struct iphdr *ip, struct udphdr *udp __unused,
- struct tcphdr *tcp)
-{
- if (!tcp) {
- return 0;
- }
- if (arptable[ARP_CLIENT].ipaddr.s_addr != ip->dest.s_addr)
- return 0;
- if (ntohs(tcp->dst) != ival) {
- tcp_reset(ip);
- return 0;
- }
- *(void **)ptr = tcp;
- return 1;
-}
-
-int tcp_transaction(unsigned long destip, unsigned int destsock, void *ptr,
- int (*send)(int len, void *buf, void *ptr),
- int (*recv)(int len, const void *buf, void *ptr)) {
- static uint16_t srcsock = 0;
- int rc = 1;
- long send_seq = currticks();
- long recv_seq = 0;
- int can_send = 0;
- int sent_all = 0;
- struct iphdr *ip;
- struct tcphdr *tcp;
- int ctrl = SYN;
- char buf[128]; /* Small outgoing buffer */
- long payload;
- int header_size;
- int window = 3*TCP_MIN_WINDOW;
- long last_ack = 0;
- long last_sent = 0;
- long rtt = 0;
- long srtt = 0;
- long rto = TCP_INITIAL_TIMEOUT;
- int retry = TCP_MAX_TIMEOUT/TCP_INITIAL_TIMEOUT;
- enum { CLOSED, SYN_RCVD, ESTABLISHED,
- FIN_WAIT_1, FIN_WAIT_2 } state = CLOSED;
-
- if (!srcsock) {
- srcsock = currticks();
- }
- if (++srcsock < 1024)
- srcsock += 1024;
-
- await_reply(await_qdrain, 0, NULL, 0);
-
- send_data:
- if (ctrl & ACK)
- last_ack = recv_seq;
- if (!tcp_transmit(destip, srcsock, destsock, send_seq,
- recv_seq, window, ctrl,
- sizeof(struct iphdr) + sizeof(struct tcphdr)+
- can_send, buf)) {
- return (0);
- }
- last_sent = currticks();
-
- recv_data:
- if (!await_reply(await_tcp, srcsock, &tcp,
- (state == ESTABLISHED && !can_send)
- ? TCP_MAX_TIMEOUT : rto)) {
- if (state == ESTABLISHED) {
- close:
- ctrl = FIN|ACK;
- state = FIN_WAIT_1;
- rc = 0;
- goto send_data;
- }
-
- if (state == FIN_WAIT_1 || state == FIN_WAIT_2)
- return (rc);
-
- if (--retry <= 0) {
- /* time out */
- if (state == SYN_RCVD) {
- tcp_transmit(destip, srcsock, destsock,
- send_seq, 0, window, RST,
- sizeof(struct iphdr) +
- sizeof(struct tcphdr), buf);
- }
- return (0);
- }
- /* retransmit */
- goto send_data;
- }
- got_data:
- retry = TCP_MAX_RETRY;
-
- if (tcp->ctrl & htons(ACK) ) {
- char *data;
- int syn_ack, consumed;
-
- if (state == FIN_WAIT_1 || state == FIN_WAIT_2) {
- state = FIN_WAIT_2;
- ctrl = ACK;
- goto consume_data;
- }
- syn_ack = state == CLOSED || state == SYN_RCVD;
- consumed = ntohl(tcp->ack) - send_seq - syn_ack;
- if (consumed < 0 || consumed > can_send) {
- tcp_reset((struct iphdr *)&nic.packet[ETH_HLEN]);
- goto recv_data;
- }
-
- rtt = currticks() - last_sent;
- srtt = !srtt ? rtt : (srtt*4 + rtt)/5;
- rto = srtt + srtt/2;
- if (rto < TCP_MIN_TIMEOUT)
- rto = TCP_MIN_TIMEOUT;
- else if (rto > TCP_MAX_TIMEOUT)
- rto = TCP_MAX_TIMEOUT;
-
- can_send -= consumed;
- send_seq += consumed + syn_ack;
- data = buf + sizeof(struct iphdr) + sizeof(struct tcphdr);
- if (can_send) {
- memmove(data, data + consumed, can_send);
- }
- if (!sent_all) {
- int more_data;
- data += can_send;
- more_data = buf + sizeof(buf) - data;
- if (more_data > 0) {
- more_data = send(more_data, data, ptr);
- can_send += more_data;
- }
- sent_all = !more_data;
- }
- if (state == SYN_RCVD) {
- state = ESTABLISHED;
- ctrl = PSH|ACK;
- goto consume_data;
- }
- if (tcp->ctrl & htons(RST))
- return (0);
- } else if (tcp->ctrl & htons(RST)) {
- if (state == CLOSED)
- goto recv_data;
- return (0);
- }
-
- consume_data:
- ip = (struct iphdr *)&nic.packet[ETH_HLEN];
- header_size = sizeof(struct iphdr) + ((ntohs(tcp->ctrl)>>10)&0x3C);
- payload = ntohs(ip->len) - header_size;
- if (payload > 0 && state == ESTABLISHED) {
- int old_bytes = recv_seq - (long)ntohl(tcp->seq);
- if (old_bytes >= 0 && payload - old_bytes > 0) {
- recv_seq += payload - old_bytes;
- if (state != FIN_WAIT_1 && state != FIN_WAIT_2 &&
- !recv(payload - old_bytes,
- &nic.packet[ETH_HLEN+header_size+old_bytes],
- ptr)) {
- goto close;
- }
- if ((state == ESTABLISHED || state == SYN_RCVD) &&
- !(tcp->ctrl & htons(FIN))) {
- int in_window = window - 2*TCP_MIN_WINDOW >
- recv_seq - last_ack;
- ctrl = can_send ? PSH|ACK : ACK;
- if (!can_send && in_window) {
-/* Window scaling is broken right now, just fall back to acknowledging every */
-/* packet immediately and unconditionally. FIXME */ /***/
-/* if (await_reply(await_tcp, srcsock,
- &tcp, rto))
- goto got_data;
- else */
- goto send_data;
- }
- if (!in_window) {
- window += TCP_MIN_WINDOW;
- if (window > TCP_MAX_WINDOW)
- window = TCP_MAX_WINDOW;
- }
- goto send_data;
- }
- } else {
- /* saw old data again, must have lost packets */
- window /= 2;
- if (window < 2*TCP_MIN_WINDOW)
- window = 2*TCP_MIN_WINDOW;
- }
- }
-
- if (tcp->ctrl & htons(FIN)) {
- if (state == ESTABLISHED) {
- ctrl = FIN|ACK;
- } else if (state == FIN_WAIT_1 || state == FIN_WAIT_2) {
- ctrl = ACK;
- } else {
- ctrl = RST;
- }
- return (tcp_transmit(destip, srcsock, destsock,
- send_seq, recv_seq + 1, window, ctrl,
- sizeof(struct iphdr) +
- sizeof(struct tcphdr), buf) &&
- (state == ESTABLISHED ||
- state == FIN_WAIT_1 || state == FIN_WAIT_2) &&
- !can_send);
- }
-
- if (state == CLOSED) {
- if (tcp->ctrl & htons(SYN)) {
- recv_seq = ntohl(tcp->seq) + 1;
- if (!(tcp->ctrl & htons(ACK))) {
- state = SYN_RCVD;
- ctrl = SYN|ACK|PSH;
- goto send_data;
- } else {
- state = ESTABLISHED;
- ctrl = PSH|ACK;
- }
- }
- }
-
- if (can_send || payload) {
- goto send_data;
- }
- goto recv_data;
-}
-#endif
/**************************************************************************
AWAIT_REPLY - Wait until we get a response for our request
@@ -1361,7 +1067,6 @@ int await_reply(reply_t reply, int ival, void *ptr, long timeout)
}
}
tcp = 0;
-#ifdef DOWNLOAD_PROTO_HTTP
if (ip && (ip->protocol == IP_TCP) &&
(nic.packetlen >=
ETH_HLEN + sizeof(struct iphdr) + sizeof(struct tcphdr))){
@@ -1377,7 +1082,6 @@ int await_reply(reply_t reply, int ival, void *ptr, long timeout)
}
}
-#endif
result = reply(ival, ptr, ptype, ip, udp, tcp);
if (result > 0) {
return result;
diff --git a/src/include/ip.h b/src/include/ip.h
index 85c299ba..5c9b7271 100644
--- a/src/include/ip.h
+++ b/src/include/ip.h
@@ -18,4 +18,6 @@ struct iphdr {
struct in_addr dest;
} PACKED;
+extern uint16_t tcpudpchksum(struct iphdr *ip);
+
#endif /* _IP_H */
diff --git a/src/include/tcp.h b/src/include/tcp.h
index f570d8ef..93e1485e 100644
--- a/src/include/tcp.h
+++ b/src/include/tcp.h
@@ -9,10 +9,6 @@
#define TCP_MIN_WINDOW (1500-TCP_MAX_HEADER)
#define TCP_MAX_WINDOW (65535-TCP_MAX_HEADER)
-
-#define MAX_URL 80
-
-
#define FIN 1
#define SYN 2
#define RST 4
@@ -32,4 +28,10 @@ struct tcphdr {
uint16_t urgent;
};
+extern int tcp_transaction ( unsigned long destip, unsigned int destsock,
+ void *ptr,
+ int (*send)(int len, void *buf, void *ptr),
+ int (*recv)(int len, const void *buf, void *ptr));
+
+
#endif /* _TCP_H */
diff --git a/src/proto/tcp.c b/src/proto/tcp.c
new file mode 100644
index 00000000..197bfce2
--- /dev/null
+++ b/src/proto/tcp.c
@@ -0,0 +1,286 @@
+#include "etherboot.h"
+#include "ip.h"
+#include "tcp.h"
+
+
+void build_tcp_hdr(unsigned long destip, unsigned int srcsock,
+ unsigned int destsock, long send_seq, long recv_seq,
+ int window, int flags, int ttl, int len, const void *buf)
+{
+ struct iphdr *ip;
+ struct tcphdr *tcp;
+ ip = (struct iphdr *)buf;
+ build_ip_hdr(destip, ttl, IP_TCP, 0, len, buf);
+ tcp = (struct tcphdr *)(ip + 1);
+ tcp->src = htons(srcsock);
+ tcp->dst = htons(destsock);
+ tcp->seq = htonl(send_seq);
+ tcp->ack = htonl(recv_seq);
+ tcp->ctrl = htons(flags + (5 << 12)); /* No TCP options */
+ tcp->window = htons(window);
+ tcp->chksum = 0;
+ if ((tcp->chksum = tcpudpchksum(ip)) == 0)
+ tcp->chksum = 0xffff;
+}
+
+/**************************************************************************
+TCP_TRANSMIT - Send a TCP packet
+**************************************************************************/
+int tcp_transmit(unsigned long destip, unsigned int srcsock,
+ unsigned int destsock, long send_seq, long recv_seq,
+ int window, int flags, int len, const void *buf)
+{
+ build_tcp_hdr(destip, srcsock, destsock, send_seq, recv_seq,
+ window, flags, 60, len, buf);
+ return ip_transmit(len, buf);
+}
+
+int tcp_reset(struct iphdr *ip) {
+ struct tcphdr *tcp = (struct tcphdr *)(ip + 1);
+ char buf[sizeof(struct iphdr) + sizeof(struct tcphdr)];
+
+ if (!(tcp->ctrl & htons(RST))) {
+ long seq = ntohl(tcp->seq) + ntohs(ip->len) -
+ sizeof(struct iphdr) -
+ ((ntohs(tcp->ctrl) >> 10) & 0x3C);
+ if (tcp->ctrl & htons(SYN|FIN))
+ seq++;
+ return tcp_transmit(ntohl(ip->src.s_addr),
+ ntohs(tcp->dst), ntohs(tcp->src),
+ tcp->ctrl&htons(ACK) ? ntohl(tcp->ack) : 0,
+ seq, TCP_MAX_WINDOW, RST, sizeof(buf), buf);
+ }
+ return (1);
+}
+
+/**************************************************************************
+TCP - Simple-minded TCP stack. Can only send data once and then
+ receive the response. The algorithm for computing window
+ sizes and delaying ack's is currently broken, and thus
+ disabled. Performance would probably improve a little, if
+ this gets fixed. FIXME
+**************************************************************************/
+static int await_tcp(int ival, void *ptr, unsigned short ptype __unused,
+ struct iphdr *ip, struct udphdr *udp __unused,
+ struct tcphdr *tcp)
+{
+ if (!tcp) {
+ return 0;
+ }
+ if (arptable[ARP_CLIENT].ipaddr.s_addr != ip->dest.s_addr)
+ return 0;
+ if (ntohs(tcp->dst) != ival) {
+ tcp_reset(ip);
+ return 0;
+ }
+ *(void **)ptr = tcp;
+ return 1;
+}
+
+int tcp_transaction(unsigned long destip, unsigned int destsock, void *ptr,
+ int (*send)(int len, void *buf, void *ptr),
+ int (*recv)(int len, const void *buf, void *ptr)) {
+ static uint16_t srcsock = 0;
+ int rc = 1;
+ long send_seq = currticks();
+ long recv_seq = 0;
+ int can_send = 0;
+ int sent_all = 0;
+ struct iphdr *ip;
+ struct tcphdr *tcp;
+ int ctrl = SYN;
+ char buf[128]; /* Small outgoing buffer */
+ long payload;
+ int header_size;
+ int window = 3*TCP_MIN_WINDOW;
+ long last_ack = 0;
+ long last_sent = 0;
+ long rtt = 0;
+ long srtt = 0;
+ long rto = TCP_INITIAL_TIMEOUT;
+ int retry = TCP_MAX_TIMEOUT/TCP_INITIAL_TIMEOUT;
+ enum { CLOSED, SYN_RCVD, ESTABLISHED,
+ FIN_WAIT_1, FIN_WAIT_2 } state = CLOSED;
+
+ if (!srcsock) {
+ srcsock = currticks();
+ }
+ if (++srcsock < 1024)
+ srcsock += 1024;
+
+ rx_qdrain();
+
+ send_data:
+ if (ctrl & ACK)
+ last_ack = recv_seq;
+ if (!tcp_transmit(destip, srcsock, destsock, send_seq,
+ recv_seq, window, ctrl,
+ sizeof(struct iphdr) + sizeof(struct tcphdr)+
+ can_send, buf)) {
+ return (0);
+ }
+ last_sent = currticks();
+
+ recv_data:
+ if (!await_reply(await_tcp, srcsock, &tcp,
+ (state == ESTABLISHED && !can_send)
+ ? TCP_MAX_TIMEOUT : rto)) {
+ if (state == ESTABLISHED) {
+ close:
+ ctrl = FIN|ACK;
+ state = FIN_WAIT_1;
+ rc = 0;
+ goto send_data;
+ }
+
+ if (state == FIN_WAIT_1 || state == FIN_WAIT_2)
+ return (rc);
+
+ if (--retry <= 0) {
+ /* time out */
+ if (state == SYN_RCVD) {
+ tcp_transmit(destip, srcsock, destsock,
+ send_seq, 0, window, RST,
+ sizeof(struct iphdr) +
+ sizeof(struct tcphdr), buf);
+ }
+ return (0);
+ }
+ /* retransmit */
+ goto send_data;
+ }
+ /* got_data: */
+ retry = TCP_MAX_RETRY;
+
+ if (tcp->ctrl & htons(ACK) ) {
+ char *data;
+ int syn_ack, consumed;
+
+ if (state == FIN_WAIT_1 || state == FIN_WAIT_2) {
+ state = FIN_WAIT_2;
+ ctrl = ACK;
+ goto consume_data;
+ }
+ syn_ack = state == CLOSED || state == SYN_RCVD;
+ consumed = ntohl(tcp->ack) - send_seq - syn_ack;
+ if (consumed < 0 || consumed > can_send) {
+ tcp_reset((struct iphdr *)&nic.packet[ETH_HLEN]);
+ goto recv_data;
+ }
+
+ rtt = currticks() - last_sent;
+ srtt = !srtt ? rtt : (srtt*4 + rtt)/5;
+ rto = srtt + srtt/2;
+ if (rto < TCP_MIN_TIMEOUT)
+ rto = TCP_MIN_TIMEOUT;
+ else if (rto > TCP_MAX_TIMEOUT)
+ rto = TCP_MAX_TIMEOUT;
+
+ can_send -= consumed;
+ send_seq += consumed + syn_ack;
+ data = buf + sizeof(struct iphdr) + sizeof(struct tcphdr);
+ if (can_send) {
+ memmove(data, data + consumed, can_send);
+ }
+ if (!sent_all) {
+ int more_data;
+ data += can_send;
+ more_data = buf + sizeof(buf) - data;
+ if (more_data > 0) {
+ more_data = send(more_data, data, ptr);
+ can_send += more_data;
+ }
+ sent_all = !more_data;
+ }
+ if (state == SYN_RCVD) {
+ state = ESTABLISHED;
+ ctrl = PSH|ACK;
+ goto consume_data;
+ }
+ if (tcp->ctrl & htons(RST))
+ return (0);
+ } else if (tcp->ctrl & htons(RST)) {
+ if (state == CLOSED)
+ goto recv_data;
+ return (0);
+ }
+
+ consume_data:
+ ip = (struct iphdr *)&nic.packet[ETH_HLEN];
+ header_size = sizeof(struct iphdr) + ((ntohs(tcp->ctrl)>>10)&0x3C);
+ payload = ntohs(ip->len) - header_size;
+ if (payload > 0 && state == ESTABLISHED) {
+ int old_bytes = recv_seq - (long)ntohl(tcp->seq);
+ if (old_bytes >= 0 && payload - old_bytes > 0) {
+ recv_seq += payload - old_bytes;
+ if (state != FIN_WAIT_1 && state != FIN_WAIT_2 &&
+ !recv(payload - old_bytes,
+ &nic.packet[ETH_HLEN+header_size+old_bytes],
+ ptr)) {
+ goto close;
+ }
+ if ((state == ESTABLISHED || state == SYN_RCVD) &&
+ !(tcp->ctrl & htons(FIN))) {
+ int in_window = window - 2*TCP_MIN_WINDOW >
+ recv_seq - last_ack;
+ ctrl = can_send ? PSH|ACK : ACK;
+ if (!can_send && in_window) {
+/* Window scaling is broken right now, just fall back to acknowledging every */
+/* packet immediately and unconditionally. FIXME */ /***/
+/* if (await_reply(await_tcp, srcsock,
+ &tcp, rto))
+ goto got_data;
+ else */
+ goto send_data;
+ }
+ if (!in_window) {
+ window += TCP_MIN_WINDOW;
+ if (window > TCP_MAX_WINDOW)
+ window = TCP_MAX_WINDOW;
+ }
+ goto send_data;
+ }
+ } else {
+ /* saw old data again, must have lost packets */
+ window /= 2;
+ if (window < 2*TCP_MIN_WINDOW)
+ window = 2*TCP_MIN_WINDOW;
+ }
+ }
+
+ if (tcp->ctrl & htons(FIN)) {
+ if (state == ESTABLISHED) {
+ ctrl = FIN|ACK;
+ } else if (state == FIN_WAIT_1 || state == FIN_WAIT_2) {
+ ctrl = ACK;
+ } else {
+ ctrl = RST;
+ }
+ return (tcp_transmit(destip, srcsock, destsock,
+ send_seq, recv_seq + 1, window, ctrl,
+ sizeof(struct iphdr) +
+ sizeof(struct tcphdr), buf) &&
+ (state == ESTABLISHED ||
+ state == FIN_WAIT_1 || state == FIN_WAIT_2) &&
+ !can_send);
+ }
+
+ if (state == CLOSED) {
+ if (tcp->ctrl & htons(SYN)) {
+ recv_seq = ntohl(tcp->seq) + 1;
+ if (!(tcp->ctrl & htons(ACK))) {
+ state = SYN_RCVD;
+ ctrl = SYN|ACK|PSH;
+ goto send_data;
+ } else {
+ state = ESTABLISHED;
+ ctrl = PSH|ACK;
+ }
+ }
+ }
+
+ if (can_send || payload) {
+ goto send_data;
+ }
+ goto recv_data;
+}