From abf9cf5bec2712465400417cc8232fee2d1cce28 Mon Sep 17 00:00:00 2001
From: Anton Kling <anton@kling.gg>
Date: Tue, 11 Jun 2024 13:33:01 +0200
Subject: TCP stuff

---
 kernel/lib/relist.c       |   7 ++
 kernel/lib/relist.h       |   1 +
 kernel/network/ethernet.c |   2 +-
 kernel/network/ipv4.c     |   8 +-
 kernel/network/tcp.c      | 269 +++++++++++++++++++++++++++++++++-------------
 kernel/network/tcp.h      |   7 +-
 kernel/network/udp.c      |   3 +-
 kernel/network/udp.h      |   2 +-
 kernel/socket.c           |  37 +++++--
 kernel/socket.h           |   6 ++
 10 files changed, 253 insertions(+), 89 deletions(-)

(limited to 'kernel')

diff --git a/kernel/lib/relist.c b/kernel/lib/relist.c
index 01af884..2e45429 100644
--- a/kernel/lib/relist.c
+++ b/kernel/lib/relist.c
@@ -5,6 +5,7 @@
 #include <string.h>
 
 void relist_init(struct relist *list) {
+  list->num_entries = 0;
   list->bitmap_capacity = 1;
   list->bitmap = kcalloc(sizeof(u64), list->bitmap_capacity);
   if (!list->bitmap) {
@@ -28,6 +29,7 @@ relist_init_error:
 }
 
 void relist_reset(struct relist *list) {
+  list->num_entries = 0;
   memset(list->bitmap, 0, list->bitmap_capacity * sizeof(u64));
 }
 
@@ -43,12 +45,14 @@ int relist_clone(struct relist *in, struct relist *out) {
     kfree(out->bitmap);
     goto relist_clone_error;
   }
+  out->num_entries = in->num_entries;
   memcpy(out->bitmap, in->bitmap, sizeof(u64) * out->bitmap_capacity);
   memcpy(out->entries, in->entries,
          sizeof(void *) * sizeof(u64) * 8 * out->bitmap_capacity);
   return 1;
 
 relist_clone_error:
+  out->num_entries = 0;
   out->bitmap_capacity = 0;
   out->entries = NULL;
   out->bitmap = NULL;
@@ -100,6 +104,7 @@ int relist_add(struct relist *list, void *value, u32 *index) {
   if (index) {
     *index = entry;
   }
+  list->num_entries++;
   list->bitmap[entry / 64] |= ((u64)1 << (entry % 64));
   return 1;
 }
@@ -114,6 +119,7 @@ int relist_remove(struct relist *list, u32 index) {
     assert(0);
     return 0;
   }
+  list->num_entries--;
   list->bitmap[index / 64] &= ~((u64)1 << (index % 64));
   return 1;
 }
@@ -148,6 +154,7 @@ int relist_get(const struct relist *list, u32 index, void **out, int *end) {
 }
 
 void relist_free(struct relist *list) {
+  list->num_entries = 0;
   list->bitmap_capacity = 0;
   kfree(list->entries);
   kfree(list->bitmap);
diff --git a/kernel/lib/relist.h b/kernel/lib/relist.h
index 734e5dc..409032d 100644
--- a/kernel/lib/relist.h
+++ b/kernel/lib/relist.h
@@ -5,6 +5,7 @@
 struct relist {
   void **entries;
   u32 bitmap_capacity;
+  u32 num_entries;
   u64 *bitmap;
 };
 
diff --git a/kernel/network/ethernet.c b/kernel/network/ethernet.c
index cf2e047..1bda07f 100644
--- a/kernel/network/ethernet.c
+++ b/kernel/network/ethernet.c
@@ -62,7 +62,7 @@ void handle_ethernet(const u8 *packet, u64 packet_length) {
     handle_ipv4(payload, packet_length - sizeof(struct ETHERNET_HEADER) - 4);
     break;
   default:
-    kprintf("Can't handle ethernet type\n");
+    kprintf("Can't handle ethernet type 0x%x\n", type);
     break;
   }
 }
diff --git a/kernel/network/ipv4.c b/kernel/network/ipv4.c
index 4fe3799..cceee8e 100644
--- a/kernel/network/ipv4.c
+++ b/kernel/network/ipv4.c
@@ -84,15 +84,17 @@ void handle_ipv4(const u8 *payload, u32 packet_length) {
   assert(ipv4_total_length <= packet_length);
 
   ipv4_t src_ip;
-  memcpy(&src_ip, payload + 12, sizeof(u8[4]));
+  memcpy(&src_ip.d, payload + 12, sizeof(u8[4]));
+  ipv4_t dst_ip;
+  memcpy(&dst_ip.d, payload + 16, sizeof(u8[4]));
 
   u8 protocol = *(payload + 9);
   switch (protocol) {
   case 0x6:
-    handle_tcp(src_ip, payload + 20, ipv4_total_length - 20);
+    handle_tcp(src_ip, dst_ip, payload + 20, ipv4_total_length - 20);
     break;
   case 0x11:
-    handle_udp(src_ip, payload + 20, ipv4_total_length - 20);
+    handle_udp(src_ip, dst_ip, payload + 20, ipv4_total_length - 20);
     break;
   default:
     kprintf("Protocol given in IPv4 header not handeld: %x\n", protocol);
diff --git a/kernel/network/tcp.c b/kernel/network/tcp.c
index aeabbf2..fc8d97c 100644
--- a/kernel/network/tcp.c
+++ b/kernel/network/tcp.c
@@ -1,10 +1,13 @@
 #include <assert.h>
 #include <cpu/arch_inst.h>
 #include <drivers/pit.h>
+#include <fs/vfs.h>
+#include <math.h>
 #include <network/arp.h>
 #include <network/bytes.h>
 #include <network/ipv4.h>
 #include <network/udp.h>
+#include <random.h>
 
 #define CWR (1 << 7)
 #define ECE (1 << 6)
@@ -15,6 +18,8 @@
 #define SYN (1 << 1)
 #define FIN (1 << 0)
 
+#define MSS 536
+
 // FIXME: This should be dynamic
 #define WINDOW_SIZE 4096
 
@@ -73,28 +78,82 @@ u16 tcp_checksum(u16 *buffer, int size) {
   return (u16)(~cksum);
 }
 
-void tcp_calculate_checksum(ipv4_t src_ip, u32 dst_ip, const u8 *payload,
-                            u16 payload_length, struct TCP_HEADER *header) {
-  struct PSEUDO_TCP_HEADER ps;
-  memset(&ps, 0, sizeof(ps));
-  memcpy(&ps.src_addr, &src_ip.d, sizeof(u32));
-  memcpy(&ps.dst_addr, &dst_ip, sizeof(u32));
-  ps.protocol = 6;
-  ps.tcp_length = htons(20 + payload_length);
-  ps.src_port = header->src_port;
-  ps.dst_port = header->dst_port;
-  ps.seq_num = header->seq_num;
-  ps.ack_num = header->ack_num;
-  ps.data_offset = header->data_offset;
-  ps.reserved = header->reserved;
-  ps.flags = header->flags;
-  ps.window_size = header->window_size;
-  ps.urgent_pointer = header->urgent_pointer;
-  int buffer_length = sizeof(ps) + payload_length;
+u16 tcp_calculate_checksum(ipv4_t src_ip, u32 dst_ip, const u8 *payload,
+                           u16 payload_length, const struct TCP_HEADER *header,
+                           int total) {
+  if (total < header->data_offset + payload_length) {
+    return 0;
+  }
+
+  int pseudo = sizeof(u32) * 2 + sizeof(u16) + sizeof(u8) * 2;
+
+  int buffer_length =
+      pseudo + header->data_offset * sizeof(u32) + payload_length;
   u8 buffer[buffer_length];
-  memcpy(buffer, &ps, sizeof(ps));
-  memcpy(buffer + sizeof(ps), payload, payload_length);
-  header->checksum = tcp_checksum((u16 *)buffer, buffer_length);
+  u8 *ptr = buffer;
+  memcpy(ptr, &src_ip.d, sizeof(u32));
+  ptr += sizeof(u32);
+  memcpy(ptr, &dst_ip, sizeof(u32));
+  ptr += sizeof(u32);
+  *ptr = 0;
+  ptr += sizeof(u8);
+  *ptr = 6;
+  ptr += sizeof(u8);
+  *(u16 *)ptr = htons(header->data_offset * sizeof(u32) + payload_length);
+  ptr += sizeof(u16);
+  memcpy(ptr, header, header->data_offset * sizeof(u32));
+  memset(ptr + 16, 0, sizeof(u16)); // set checksum to zero
+  ptr += header->data_offset * sizeof(u32);
+  memcpy(ptr, payload, payload_length);
+
+  return tcp_checksum((u16 *)buffer, buffer_length);
+}
+
+struct TcpPacket {
+  u32 time;
+  u32 seq_num;
+  u16 payload_length;
+  u8 *buffer;
+  u16 length;
+};
+
+static void tcp_send(struct TcpConnection *con, u8 *buffer, u16 length,
+                     u32 seq_num, u32 payload_length) {
+  if (payload_length > 0) {
+    struct TcpPacket *packet = kmalloc(sizeof(struct TcpPacket));
+    assert(packet);
+    packet->time = pit_num_ms();
+    packet->seq_num = seq_num;
+    packet->buffer = buffer;
+    packet->length = length;
+    packet->payload_length = payload_length;
+
+    assert(relist_add(&con->inflight, packet, NULL));
+  }
+  send_ipv4_packet((ipv4_t){.d = con->outgoing_ip}, 6, buffer, length);
+}
+
+void tcp_resend_packets(struct TcpConnection *con) {
+  if (con->inflight.num_entries > 0) {
+    for (u32 i = 0;; i++) {
+      struct TcpPacket *packet;
+      int end;
+      if (!relist_get(&con->inflight, i, (void *)&packet, &end)) {
+        if (end) {
+          break;
+        }
+        continue;
+      }
+      if (packet->time + 200 > pit_num_ms()) {
+        continue;
+      }
+      // resend the packet
+      relist_remove(&con->inflight, i);
+      tcp_send(con, packet->buffer, packet->length, packet->seq_num,
+               packet->payload_length);
+      kfree(packet);
+    }
+  }
 }
 
 void tcp_send_empty_payload(struct TcpConnection *con, u8 flags) {
@@ -111,14 +170,15 @@ void tcp_send_empty_payload(struct TcpConnection *con, u8 flags) {
 
   u8 payload[0];
   u16 payload_length = 0;
-  tcp_calculate_checksum(ip_address, con->outgoing_ip, (const u8 *)payload,
-                         payload_length, &header);
+  header.checksum = tcp_calculate_checksum(
+      ip_address, con->outgoing_ip, (const u8 *)payload, payload_length,
+      &header, sizeof(struct TCP_HEADER) + payload_length);
   int send_len = sizeof(header) + payload_length;
-  u8 send_buffer[send_len];
+  u8 *send_buffer = kmalloc(send_len);
   memcpy(send_buffer, &header, sizeof(header));
   memcpy(send_buffer + sizeof(header), payload, payload_length);
 
-  send_ipv4_packet((ipv4_t){.d = con->outgoing_ip}, 6, send_buffer, send_len);
+  tcp_send(con, send_buffer, send_len, con->seq, 0);
 }
 
 void tcp_close_connection(struct TcpConnection *con) {
@@ -134,12 +194,30 @@ void tcp_send_syn(struct TcpConnection *con) {
   con->seq++;
 }
 
-void send_tcp_packet(struct TcpConnection *con, const u8 *payload,
-                     u16 payload_length) {
-  if (payload_length > 1500 - 20 - sizeof(struct TCP_HEADER)) {
-    send_tcp_packet(con, payload, 1500 - 20 - sizeof(struct TCP_HEADER));
-    payload_length -= 1500 - 20 - sizeof(struct TCP_HEADER);
-    payload += 1500 - 20 - sizeof(struct TCP_HEADER);
+int tcp_can_send(struct TcpConnection *con, u16 payload_length) {
+  if (con->inflight.num_entries > 2) {
+    tcp_resend_packets(con);
+    return 0;
+  }
+  if (con->seq - con->seq_ack + payload_length > con->current_window_size) {
+    tcp_resend_packets(con);
+    return 0;
+  }
+  return 1;
+}
+
+int send_tcp_packet(struct TcpConnection *con, const u8 *payload,
+                    u16 payload_length) {
+  if (!tcp_can_send(con, payload_length)) {
+    return 0;
+  }
+
+  if (payload_length > MSS) {
+    if (0 == send_tcp_packet(con, payload, MSS)) {
+      return 0;
+    }
+    payload_length -= MSS;
+    payload += MSS;
     return send_tcp_packet(con, payload, payload_length);
   }
   struct TCP_HEADER header = {0};
@@ -152,24 +230,37 @@ void send_tcp_packet(struct TcpConnection *con, const u8 *payload,
   header.flags = PSH | ACK;
   header.window_size = htons(WINDOW_SIZE);
   header.urgent_pointer = 0;
-  tcp_calculate_checksum(ip_address, con->outgoing_ip, (const u8 *)payload,
-                         payload_length, &header);
+  header.checksum = tcp_calculate_checksum(
+      ip_address, con->outgoing_ip, (const u8 *)payload, payload_length,
+      &header, sizeof(struct TCP_HEADER) + payload_length);
   int send_len = sizeof(header) + payload_length;
-  u8 send_buffer[send_len];
+  u8 *send_buffer = kmalloc(send_len);
+  assert(send_buffer);
   memcpy(send_buffer, &header, sizeof(header));
   memcpy(send_buffer + sizeof(header), payload, payload_length);
-  send_ipv4_packet((ipv4_t){.d = con->outgoing_ip}, 6, send_buffer, send_len);
+
+  tcp_send(con, send_buffer, send_len, con->seq, payload_length);
 
   con->seq += payload_length;
+  return 1;
 }
 
-void handle_tcp(ipv4_t src_ip, const u8 *payload, u32 payload_length) {
+void handle_tcp(ipv4_t src_ip, ipv4_t dst_ip, const u8 *payload,
+                u32 payload_length) {
   const struct TCP_HEADER *header = (const struct TCP_HEADER *)payload;
-  (void)header;
+  u16 tcp_payload_length = payload_length - header->data_offset * sizeof(u32);
+  const u8 *tcp_payload = payload + header->data_offset * sizeof(u32);
+  u16 checksum =
+      tcp_calculate_checksum(src_ip, dst_ip.d, tcp_payload, tcp_payload_length,
+                             header, payload_length);
+  if (header->checksum != checksum) {
+    return;
+  }
   u16 n_src_port = header->src_port;
   u16 n_dst_port = header->dst_port;
   u32 n_seq_num = header->seq_num;
   u32 n_ack_num = header->ack_num;
+  u32 n_window_size = header->window_size;
 
   u8 flags = header->flags;
 
@@ -177,14 +268,18 @@ void handle_tcp(ipv4_t src_ip, const u8 *payload, u32 payload_length) {
   u16 dst_port = htons(n_dst_port);
   u32 seq_num = htonl(n_seq_num);
   u32 ack_num = htonl(n_ack_num);
+  u16 window_size = htons(n_window_size);
+
   (void)ack_num;
 
   if (SYN == flags) {
     struct TcpConnection *con =
         internal_tcp_incoming(src_ip.d, src_port, 0, dst_port);
-    if(!con) {
+    if (!con) {
       return;
     }
+    con->window_size = window_size;
+    con->current_window_size = MSS;
     con->ack = seq_num + 1;
     tcp_send_empty_payload(con, SYN | ACK);
     con->seq++;
@@ -195,49 +290,75 @@ void handle_tcp(ipv4_t src_ip, const u8 *payload, u32 payload_length) {
       tcp_find_connection(src_ip, src_port, dst_port);
   if (!incoming_connection) {
     kprintf("unable to find open port for incoming connection\n");
+    return;
+  }
+  incoming_connection->window_size = window_size;
+  incoming_connection->unhandled_packet = 1;
+  if (0 != (flags & RST)) {
+    klog("Requested port is closed", LOG_NOTE);
+    incoming_connection->dead = 1;
+    return;
   }
-  if (incoming_connection) {
-    incoming_connection->unhandled_packet = 1;
-    if (0 != (flags & RST)) {
-      klog("Requested port is closed", LOG_NOTE);
-      incoming_connection->dead = 1;
+  if (ACK == flags) {
+    if (0 == incoming_connection->handshake_state) {
+      // Then it is probably a response to the SYN|ACK we sent.
+      incoming_connection->handshake_state = 1;
       return;
     }
-    if (ACK == flags) {
-      if (0 == incoming_connection->handshake_state) {
-        // Then it is probably a response to the SYN|ACK we sent.
-        incoming_connection->handshake_state = 1;
-        return;
+  }
+  if ((SYN | ACK) == flags) {
+    assert(0 == incoming_connection->handshake_state);
+    incoming_connection->handshake_state = 1;
+    incoming_connection->ack = seq_num + 1;
+    tcp_send_ack(incoming_connection);
+    return;
+  }
+  if (ACK & flags) {
+    incoming_connection->seq_ack = ack_num;
+    if (incoming_connection->inflight.num_entries > 0) {
+      for (u32 i = 0;; i++) {
+        struct TcpPacket *packet;
+        int end;
+        if (!relist_get(&incoming_connection->inflight, i, (void *)&packet,
+                        &end)) {
+          if (end) {
+            break;
+          }
+          continue;
+        }
+        if (packet->seq_num + packet->payload_length != ack_num) {
+          continue;
+        }
+        relist_remove(&incoming_connection->inflight, i);
+        kfree(packet->buffer);
+        kfree(packet);
+        break;
       }
     }
-    if ((SYN | ACK) == flags) {
-      assert(0 == incoming_connection->handshake_state);
-      incoming_connection->handshake_state = 1;
-
-      incoming_connection->ack = seq_num + 1;
-
-      tcp_send_ack(incoming_connection);
-    }
-    u16 tcp_payload_length = payload_length - header->data_offset * sizeof(u32);
-    if (tcp_payload_length > 0) {
-      const u8 *tcp_payload = payload + header->data_offset * sizeof(u32);
-      u32 len = ringbuffer_write(&incoming_connection->incoming_buffer,
-                                 tcp_payload, tcp_payload_length);
-      assert(len == tcp_payload_length);
-      incoming_connection->ack += len;
-      tcp_send_ack(incoming_connection);
+    tcp_resend_packets(incoming_connection);
+    if (0 == incoming_connection->inflight.num_entries) {
+      u32 rest = incoming_connection->window_size -
+                 incoming_connection->current_window_size;
+      if (rest > 0) {
+        incoming_connection->current_window_size += min(rest, MSS);
+      }
     }
-    if (0 != (flags & FIN)) {
-      incoming_connection->ack++;
+  }
+  if (tcp_payload_length > 0) {
+    u32 len = ringbuffer_write(&incoming_connection->incoming_buffer,
+                               tcp_payload, tcp_payload_length);
+    assert(len == tcp_payload_length);
+    incoming_connection->ack += len;
+    tcp_send_ack(incoming_connection);
+  }
+  if (0 != (flags & FIN)) {
+    incoming_connection->ack++;
 
-      tcp_send_empty_payload(incoming_connection, FIN | ACK);
-      incoming_connection->seq++;
+    tcp_send_empty_payload(incoming_connection, FIN | ACK);
+    incoming_connection->seq++;
 
-      incoming_connection->dead = 1; // FIXME: It should wait for a ACK
-                                     // of the FIN before the connection
-                                     // is closed.
-    }
-  } else {
-    return;
+    incoming_connection->dead = 1; // FIXME: It should wait for a ACK
+                                   // of the FIN before the connection
+                                   // is closed.
   }
 }
diff --git a/kernel/network/tcp.h b/kernel/network/tcp.h
index 1e292b3..836a148 100644
--- a/kernel/network/tcp.h
+++ b/kernel/network/tcp.h
@@ -2,7 +2,8 @@
 #include <typedefs.h>
 void tcp_send_syn(struct TcpConnection *con);
 void tcp_wait_reply(struct TcpConnection *con);
-void handle_tcp(ipv4_t src_ip, const u8 *payload, u32 payload_length);
-void send_tcp_packet(struct TcpConnection *con, const u8 *payload,
-                     u16 payload_length);
+void handle_tcp(ipv4_t src_ip, ipv4_t dst_ip, const u8 *payload, u32 payload_length);
+int send_tcp_packet(struct TcpConnection *con, const u8 *payload,
+                    u16 payload_length);
 void tcp_close_connection(struct TcpConnection *con);
+int tcp_can_send(struct TcpConnection *con, u16 payload_length);
diff --git a/kernel/network/udp.c b/kernel/network/udp.c
index 724c97a..0c8d6e9 100644
--- a/kernel/network/udp.c
+++ b/kernel/network/udp.c
@@ -24,7 +24,8 @@ void send_udp_packet(struct sockaddr_in *src, const struct sockaddr_in *dst,
   kfree(packet);
 }
 
-void handle_udp(ipv4_t src_ip, const u8 *payload, u32 packet_length) {
+void handle_udp(ipv4_t src_ip, ipv4_t dst_ip, const u8 *payload, u32 packet_length) {
+  (void)dst_ip;
   if (packet_length < sizeof(u16[4])) {
     return;
   }
diff --git a/kernel/network/udp.h b/kernel/network/udp.h
index bcf31bc..af532ec 100644
--- a/kernel/network/udp.h
+++ b/kernel/network/udp.h
@@ -1,6 +1,6 @@
 #include <socket.h>
 #include <typedefs.h>
 
-void handle_udp(ipv4_t src_ip, const u8 *payload, u32 packet_length);
+void handle_udp(ipv4_t src_ip, ipv4_t dst_ip, const u8 *payload, u32 packet_length);
 void send_udp_packet(struct sockaddr_in *src, const struct sockaddr_in *dst,
                      const u8 *payload, u16 payload_length);
diff --git a/kernel/socket.c b/kernel/socket.c
index fa37efd..8b6e12d 100644
--- a/kernel/socket.c
+++ b/kernel/socket.c
@@ -89,6 +89,7 @@ struct TcpConnection *internal_tcp_incoming(u32 src_ip, u16 src_port,
   int connection_id;
   list_add(&open_tcp_connections, con, &connection_id);
 
+  con->current_window_size = 536;
   con->outgoing_ip = src_ip;
   con->outgoing_port = src_port;
   con->incoming_ip = dst_ip;
@@ -97,6 +98,7 @@ struct TcpConnection *internal_tcp_incoming(u32 src_ip, u16 src_port,
 
   ringbuffer_init(&con->incoming_buffer, 8192);
   ringbuffer_init(&con->outgoing_buffer, 8192);
+  relist_init(&con->inflight);
   stack_push(&listen->incoming_connections, con);
   return con;
 }
@@ -105,7 +107,7 @@ int tcp_sync_buffer(vfs_fd_t *fd) {
   struct TcpConnection *con = fd->inode->internal_object;
   assert(con);
   if (con->dead) {
-    return -EBADF; // TODO: Check if this is correct.
+    return 0;
   }
 
   struct ringbuffer *rb = &con->outgoing_buffer;
@@ -113,11 +115,16 @@ int tcp_sync_buffer(vfs_fd_t *fd) {
   if (0 == send_buffer_len) {
     return 0;
   }
+
+  if (!tcp_can_send(con, send_buffer_len)) {
+    return 0;
+  }
+
   char *send_buffer = kmalloc(send_buffer_len);
   assert(ringbuffer_read(rb, send_buffer, send_buffer_len) == send_buffer_len);
   send_tcp_packet(con, send_buffer, send_buffer_len);
   kfree(send_buffer);
-  return 0;
+  return 1;
 }
 
 void tcp_close(vfs_fd_t *fd) {
@@ -151,14 +158,20 @@ int tcp_write(u8 *buffer, u64 offset, u64 len, vfs_fd_t *fd) {
   }
 
   if (con->no_delay) {
-    send_tcp_packet(con, buffer, len);
+    if (!send_tcp_packet(con, buffer, len)) {
+      return -EWOULDBLOCK;
+    }
     return len;
   }
 
   struct ringbuffer *rb = &con->outgoing_buffer;
   if (ringbuffer_unused(rb) < len) {
-    tcp_sync_buffer(fd);
-    send_tcp_packet(con, buffer, len);
+    if (!tcp_sync_buffer(fd)) {
+      return -EWOULDBLOCK;
+    }
+    if (!send_tcp_packet(con, buffer, len)) {
+      return -EWOULDBLOCK;
+    }
     len = 0;
   } else {
     assert(ringbuffer_write(rb, buffer, len) == len);
@@ -171,6 +184,15 @@ int tcp_has_data(vfs_inode_t *inode) {
   return !(ringbuffer_isempty(&con->incoming_buffer));
 }
 
+int tcp_can_write(vfs_inode_t *inode) {
+  struct TcpConnection *con = inode->internal_object;
+  if (con->no_delay) {
+    return tcp_can_send(con, 256);
+  }
+  return (ringbuffer_unused(&con->outgoing_buffer) > 0) ||
+         (tcp_can_send(con, 250));
+}
+
 int udp_recvfrom(vfs_fd_t *fd, void *buffer, size_t len, int flags,
                  struct sockaddr *src_addr, socklen_t *addrlen) {
   struct UdpConnection *con = fd->inode->internal_object;
@@ -289,10 +311,12 @@ int tcp_connect(vfs_fd_t *fd, const struct sockaddr *addr, socklen_t addrlen) {
   con->incoming_port = 1337; // TODO
   con->outgoing_ip = in_addr->sin_addr.s_addr;
   con->outgoing_port = ntohs(in_addr->sin_port);
+  con->current_window_size = 536;
 
   ringbuffer_init(&con->incoming_buffer, 8192);
   ringbuffer_init(&con->outgoing_buffer, 8192);
   list_add(&open_tcp_connections, con, NULL);
+  relist_init(&con->inflight);
 
   tcp_send_syn(con);
 
@@ -307,6 +331,7 @@ int tcp_connect(vfs_fd_t *fd, const struct sockaddr *addr, socklen_t addrlen) {
   }
 
   fd->inode->_has_data = tcp_has_data;
+  fd->inode->_can_write = tcp_can_write;
   fd->inode->write = tcp_write;
   fd->inode->read = tcp_read;
   fd->inode->close = tcp_close;
@@ -426,7 +451,7 @@ int accept(int socket, struct sockaddr *address, socklen_t *address_len) {
         stack_pop(&tcp_listen->incoming_connections);
     assert(connection);
     vfs_inode_t *inode = vfs_create_inode(
-        0 /*inode_num*/, FS_TYPE_UNIX_SOCKET, tcp_has_data, always_can_write, 1,
+        0 /*inode_num*/, FS_TYPE_UNIX_SOCKET, tcp_has_data, tcp_can_write, 1,
         connection, 0 /*file_size*/, NULL /*open*/, NULL /*create_file*/,
         tcp_read, tcp_write, tcp_close /*close*/, NULL /*create_directory*/,
         NULL /*get_vm_object*/, NULL /*truncate*/, NULL /*stat*/,
diff --git a/kernel/socket.h b/kernel/socket.h
index ef5863d..f41e9a1 100644
--- a/kernel/socket.h
+++ b/kernel/socket.h
@@ -5,6 +5,7 @@ typedef int socklen_t;
 #include <fs/fifo.h>
 #include <fs/vfs.h>
 #include <lib/buffered_write.h>
+#include <lib/relist.h>
 #include <lib/ringbuffer.h>
 #include <lib/stack.h>
 #include <stddef.h>
@@ -51,9 +52,14 @@ struct TcpConnection {
   struct ringbuffer incoming_buffer;
   struct ringbuffer outgoing_buffer;
 
+  struct relist inflight;
+
   int no_delay;
 
+  u32 current_window_size;
+  u32 window_size;
   u32 seq;
+  u32 seq_ack;
   u32 ack;
 
   int handshake_state;
-- 
cgit v1.2.3