Split TCP code out into proto/tcp.c
authorMichael Brown <mcb30@etherboot.org>
Mon, 2 May 2005 12:07:47 +0000 (12:07 +0000)
committerMichael Brown <mcb30@etherboot.org>
Mon, 2 May 2005 12:07:47 +0000 (12:07 +0000)
src/core/nic.c
src/include/ip.h
src/include/tcp.h
src/proto/tcp.c [new file with mode: 0644]

index 53dac57..fc8deeb 100644 (file)
@@ -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;
index 85c299b..5c9b727 100644 (file)
@@ -18,4 +18,6 @@ struct iphdr {
        struct in_addr dest;
 } PACKED;
 
+extern uint16_t tcpudpchksum(struct iphdr *ip);
+
 #endif /* _IP_H */
index f570d8e..93e1485 100644 (file)
@@ -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 (file)
index 0000000..197bfce
--- /dev/null
@@ -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;
+}