Implement UDP and TCP stack (AI)

This commit is contained in:
AI
2026-02-24 07:43:45 +00:00
parent d7ce0d5856
commit e6929438a0
8 changed files with 1005 additions and 12 deletions

View File

@@ -74,7 +74,7 @@ Once a task is completed, it should be checked off.
- [x] Create a IPv4 stack. Create the `ip` app that shows curernt IPv4 configuration. It should read this information from `/sys`
- [x] Create a ARP subsystem. Create the `arp` command that shows current ARP tables. Again, this info should be found in `/sys`
- [x] Create a DHCP subsystem. Create the `dhcp` command to show current DHCP status information.
- [ ] Create a UDP and TCP stack.
- [x] Create a UDP and TCP stack.
- [ ] Implement a simple version of `ftp` and `wget`.
- [ ] Create a graphics subsystem. It should provide functionality to switch between the normal text mode, and a graphics mode.
- [ ] Create a simple game of pool. It should use graphics mode to render the game.

View File

@@ -1,6 +1,3 @@
-- Configuring done (0.1s)
-- Generating done (0.2s)
-- Build files have been written to: /workspaces/claude-os/build
[ 2%] Building user-mode applications
Building app: arp
Built: /workspaces/claude-os/build/apps_bin/arp (214 bytes)
@@ -38,12 +35,9 @@ Building app: sh
1 warning generated.
Built: /workspaces/claude-os/build/apps_bin/sh (3428 bytes)
[ 2%] Built target apps
[ 5%] Generating CPIO initial ramdisk
Generated initrd: 24768 bytes
[ 5%] Built target initrd
[ 7%] Building C object src/CMakeFiles/kernel.dir/kernel.c.o
[ 10%] Building C object src/CMakeFiles/kernel.dir/dhcp.c.o
[ 13%] Linking C executable ../bin/kernel
[ 7%] Building C object src/CMakeFiles/kernel.dir/tcp.c.o
[ 10%] Linking C executable ../bin/kernel
[ 97%] Built target kernel
[100%] Generating bootable ISO image
xorriso 1.5.6 : RockRidge filesystem manipulator, libburnia project.
@@ -52,14 +46,14 @@ Drive current: -outdev 'stdio:/workspaces/claude-os/release/claude-os.iso'
Media current: stdio file, overwriteable
Media status : is blank
Media summary: 0 sessions, 0 data blocks, 0 data, 126g free
Added to ISO image: directory '/'='/tmp/grub.dJBlkG'
Added to ISO image: directory '/'='/tmp/grub.onhBjG'
xorriso : UPDATE : 581 files added in 1 seconds
Added to ISO image: directory '/'='/workspaces/claude-os/build/isodir'
xorriso : UPDATE : 586 files added in 1 seconds
xorriso : NOTE : Copying to System Area: 512 bytes from file '/usr/lib/grub/i386-pc/boot_hybrid.img'
xorriso : UPDATE : Thank you for being patient. Working since 0 seconds.
ISO image produced: 6009 sectors
Written to medium : 6009 sectors at LBA 0
ISO image produced: 6027 sectors
Written to medium : 6027 sectors at LBA 0
Writing to 'stdio:/workspaces/claude-os/release/claude-os.iso' completed successfully.
[100%] Built target iso

View File

@@ -31,6 +31,8 @@ add_executable(kernel
ipv4.c
arp.c
dhcp.c
udp.c
tcp.c
env.c
keyboard.c
interrupts.S

View File

@@ -26,6 +26,8 @@
#include "ipv4.h"
#include "arp.h"
#include "dhcp.h"
#include "udp.h"
#include "tcp.h"
#include "framebuffer.h"
/* Global framebuffer info, parsed from multiboot2 tags. */
@@ -431,6 +433,12 @@ void kernel_main(uint32_t magic, uint32_t addr) {
dhcp_init();
offset_print("DHCP subsystem initialized\n");
udp_init();
offset_print("UDP stack initialized\n");
tcp_init();
offset_print("TCP stack initialized\n");
init_drivers();
EARLY_PRINT("DRV ");
offset_print("Drivers initialized\n");

512
src/tcp.c Normal file
View File

@@ -0,0 +1,512 @@
/**
* @file tcp.c
* @brief Transmission Control Protocol (TCP) implementation.
*
* Minimal TCP state machine supporting:
* - Active open (connect)
* - Data transfer (send/recv)
* - Connection teardown (close with FIN)
*
* Limitations:
* - No retransmission timer (single-attempt)
* - No congestion control
* - No passive open (listen/accept) yet
* - No out-of-order segment handling
* - Window size is static
*/
#include "tcp.h"
#include "ipv4.h"
#include "ethernet.h"
#include "kmalloc.h"
#include "string.h"
#include "vga.h"
/* ================================================================
* Internal state
* ================================================================ */
static tcp_socket_t tcp_sockets[TCP_MAX_SOCKETS];
static uint16_t tcp_next_port = 49152; /* Ephemeral port start. */
/* Simple pseudo-random ISN based on a counter. */
static uint32_t tcp_isn_counter = 0x12345678;
static uint32_t tcp_generate_isn(void) {
tcp_isn_counter = tcp_isn_counter * 1103515245 + 12345;
return tcp_isn_counter;
}
/* ================================================================
* TCP pseudo-header checksum
* ================================================================ */
/**
* Compute TCP checksum including the pseudo-header.
*/
static uint16_t tcp_checksum(uint32_t src_ip, uint32_t dst_ip,
const void *tcp_seg, uint32_t tcp_len)
{
uint32_t sum = 0;
/* Pseudo-header */
sum += (src_ip >> 16) & 0xFFFF;
sum += src_ip & 0xFFFF;
sum += (dst_ip >> 16) & 0xFFFF;
sum += dst_ip & 0xFFFF;
sum += htons(6); /* IP_PROTO_TCP */
sum += htons(tcp_len);
/* TCP segment */
const uint16_t *p = (const uint16_t *)tcp_seg;
uint32_t rem = tcp_len;
while (rem > 1) {
sum += *p++;
rem -= 2;
}
if (rem == 1) {
sum += *((const uint8_t *)p);
}
/* Fold carries */
while (sum >> 16)
sum = (sum & 0xFFFF) + (sum >> 16);
return (uint16_t)(~sum);
}
/* ================================================================
* Send a TCP segment
* ================================================================ */
static int tcp_send_segment(tcp_socket_t *sock, uint8_t flags,
const void *data, uint32_t data_len)
{
uint32_t total_len = TCP_HLEN + data_len;
uint8_t buf[TCP_HLEN + TCP_MSS];
if (total_len > sizeof(buf)) return -1;
/* Build TCP header */
tcp_header_t *hdr = (tcp_header_t *)buf;
hdr->src_port = htons(sock->local_port);
hdr->dst_port = htons(sock->remote_port);
hdr->seq_num = htonl(sock->snd_nxt);
hdr->ack_num = (flags & TCP_ACK) ? htonl(sock->rcv_nxt) : 0;
hdr->data_offset = (TCP_HLEN / 4) << 4; /* 5 words, no options */
hdr->flags = flags;
hdr->window = htons(TCP_RX_BUF_SIZE - sock->rx_count);
hdr->checksum = 0;
hdr->urgent = 0;
/* Copy payload */
if (data && data_len > 0) {
memcpy(buf + TCP_HLEN, data, data_len);
}
/* Compute local IP from first ethernet interface. */
eth_iface_t *iface = ethernet_get_iface(0);
uint32_t src_ip = iface ? iface->ip_addr : 0;
/* Compute checksum over the whole segment. */
hdr->checksum = tcp_checksum(htonl(src_ip), htonl(sock->remote_ip),
buf, total_len);
/* Advance SND.NXT for SYN, FIN, and data. */
if (flags & TCP_SYN)
sock->snd_nxt++;
if (flags & TCP_FIN)
sock->snd_nxt++;
sock->snd_nxt += data_len;
/* Send via IPv4. */
return ipv4_send(sock->remote_ip, IP_PROTO_TCP, buf, total_len);
}
/* ================================================================
* Send a RST in response to an unexpected segment
* ================================================================ */
static void tcp_send_rst(uint32_t src_ip, uint32_t dst_ip,
const tcp_header_t *in_hdr, uint32_t seg_len)
{
uint8_t buf[TCP_HLEN];
tcp_header_t *hdr = (tcp_header_t *)buf;
memset(buf, 0, TCP_HLEN);
hdr->src_port = in_hdr->dst_port; /* Already in network byte order. */
hdr->dst_port = in_hdr->src_port;
hdr->data_offset = (TCP_HLEN / 4) << 4;
if (in_hdr->flags & TCP_ACK) {
hdr->seq_num = in_hdr->ack_num;
hdr->flags = TCP_RST;
} else {
hdr->seq_num = 0;
hdr->ack_num = htonl(ntohl(in_hdr->seq_num) + seg_len);
hdr->flags = TCP_RST | TCP_ACK;
}
hdr->window = 0;
hdr->checksum = tcp_checksum(htonl(dst_ip), htonl(src_ip),
buf, TCP_HLEN);
ipv4_send(src_ip, IP_PROTO_TCP, buf, TCP_HLEN);
}
/* ================================================================
* Find a socket matching an incoming segment
* ================================================================ */
static tcp_socket_t *tcp_find_socket(uint32_t remote_ip,
uint16_t remote_port,
uint16_t local_port)
{
for (int i = 0; i < TCP_MAX_SOCKETS; i++) {
tcp_socket_t *s = &tcp_sockets[i];
if (!s->active) continue;
if (s->local_port != local_port) continue;
if (s->state == TCP_STATE_LISTEN) return s;
if (s->remote_port == remote_port && s->remote_ip == remote_ip)
return s;
}
return NULL;
}
/* ================================================================
* Ring buffer helpers
* ================================================================ */
static void rx_buf_write(tcp_socket_t *sock, const uint8_t *data, uint32_t len) {
for (uint32_t i = 0; i < len && sock->rx_count < TCP_RX_BUF_SIZE; i++) {
sock->rx_buf[sock->rx_head] = data[i];
sock->rx_head = (sock->rx_head + 1) % TCP_RX_BUF_SIZE;
sock->rx_count++;
}
}
static uint32_t rx_buf_read(tcp_socket_t *sock, uint8_t *buf, uint32_t len) {
uint32_t n = 0;
while (n < len && sock->rx_count > 0) {
buf[n++] = sock->rx_buf[sock->rx_tail];
sock->rx_tail = (sock->rx_tail + 1) % TCP_RX_BUF_SIZE;
sock->rx_count--;
}
return n;
}
/* ================================================================
* TCP input processing
* ================================================================ */
void tcp_receive(uint32_t src_ip_ho, uint32_t dst_ip_ho,
const void *data, uint32_t len, uint32_t iface_idx)
{
(void)iface_idx;
if (len < TCP_HLEN) return;
const tcp_header_t *hdr = (const tcp_header_t *)data;
uint32_t hdr_len = ((hdr->data_offset >> 4) & 0x0F) * 4;
if (hdr_len < TCP_HLEN || hdr_len > len) return;
uint16_t src_port = ntohs(hdr->src_port);
uint16_t dst_port = ntohs(hdr->dst_port);
uint32_t seq = ntohl(hdr->seq_num);
uint32_t ack = ntohl(hdr->ack_num);
uint8_t flags = hdr->flags;
const uint8_t *payload = (const uint8_t *)data + hdr_len;
uint32_t payload_len = len - hdr_len;
/* Compute segment length (SYN and FIN count as 1 byte each). */
uint32_t seg_len = payload_len;
if (flags & TCP_SYN) seg_len++;
if (flags & TCP_FIN) seg_len++;
/* Find matching socket. */
tcp_socket_t *sock = tcp_find_socket(src_ip_ho, src_port, dst_port);
if (!sock) {
/* No socket found — send RST if not already RST. */
if (!(flags & TCP_RST)) {
tcp_send_rst(src_ip_ho, dst_ip_ho, hdr, seg_len);
}
return;
}
/* Handle RST. */
if (flags & TCP_RST) {
sock->state = TCP_STATE_CLOSED;
sock->active = 0;
return;
}
switch (sock->state) {
case TCP_STATE_SYN_SENT:
/* Expecting SYN+ACK */
if ((flags & (TCP_SYN | TCP_ACK)) == (TCP_SYN | TCP_ACK)) {
if (ack == sock->snd_nxt) {
sock->rcv_nxt = seq + 1;
sock->snd_una = ack;
sock->state = TCP_STATE_ESTABLISHED;
/* Send ACK */
tcp_send_segment(sock, TCP_ACK, NULL, 0);
}
} else if (flags & TCP_SYN) {
/* Simultaneous open — simplified handling. */
sock->rcv_nxt = seq + 1;
sock->state = TCP_STATE_SYN_RECEIVED;
tcp_send_segment(sock, TCP_SYN | TCP_ACK, NULL, 0);
}
break;
case TCP_STATE_SYN_RECEIVED:
if (flags & TCP_ACK) {
if (ack == sock->snd_nxt) {
sock->snd_una = ack;
sock->state = TCP_STATE_ESTABLISHED;
}
}
break;
case TCP_STATE_ESTABLISHED:
/* Check sequence number. */
if (seq != sock->rcv_nxt) {
/* Out-of-order — send duplicate ACK. */
tcp_send_segment(sock, TCP_ACK, NULL, 0);
break;
}
/* Update SND.UNA if ACK present. */
if (flags & TCP_ACK) {
sock->snd_una = ack;
}
/* Process payload. */
if (payload_len > 0) {
rx_buf_write(sock, payload, payload_len);
sock->rcv_nxt += payload_len;
}
/* Handle FIN. */
if (flags & TCP_FIN) {
sock->rcv_nxt++;
sock->state = TCP_STATE_CLOSE_WAIT;
/* ACK the FIN. */
tcp_send_segment(sock, TCP_ACK, NULL, 0);
} else if (payload_len > 0) {
/* ACK the data. */
tcp_send_segment(sock, TCP_ACK, NULL, 0);
}
break;
case TCP_STATE_FIN_WAIT_1:
if (flags & TCP_ACK) {
sock->snd_una = ack;
}
if ((flags & TCP_FIN) && (flags & TCP_ACK) && ack == sock->snd_nxt) {
/* FIN+ACK: simultaneous close shortcut. */
sock->rcv_nxt++;
sock->state = TCP_STATE_TIME_WAIT;
tcp_send_segment(sock, TCP_ACK, NULL, 0);
} else if (flags & TCP_FIN) {
sock->rcv_nxt++;
sock->state = TCP_STATE_CLOSING;
tcp_send_segment(sock, TCP_ACK, NULL, 0);
} else if ((flags & TCP_ACK) && ack == sock->snd_nxt) {
/* Our FIN was ACKed. */
sock->state = TCP_STATE_FIN_WAIT_2;
}
/* Accept any data in FIN_WAIT_1 */
if (payload_len > 0 && seq == sock->rcv_nxt) {
rx_buf_write(sock, payload, payload_len);
sock->rcv_nxt += payload_len;
}
break;
case TCP_STATE_FIN_WAIT_2:
if (payload_len > 0 && seq == sock->rcv_nxt) {
rx_buf_write(sock, payload, payload_len);
sock->rcv_nxt += payload_len;
tcp_send_segment(sock, TCP_ACK, NULL, 0);
}
if (flags & TCP_FIN) {
sock->rcv_nxt++;
sock->state = TCP_STATE_TIME_WAIT;
tcp_send_segment(sock, TCP_ACK, NULL, 0);
}
break;
case TCP_STATE_CLOSE_WAIT:
/* Waiting for application to close. Accept ACKs. */
if (flags & TCP_ACK) {
sock->snd_una = ack;
}
break;
case TCP_STATE_CLOSING:
if ((flags & TCP_ACK) && ack == sock->snd_nxt) {
sock->state = TCP_STATE_TIME_WAIT;
}
break;
case TCP_STATE_LAST_ACK:
if ((flags & TCP_ACK) && ack == sock->snd_nxt) {
sock->state = TCP_STATE_CLOSED;
sock->active = 0;
}
break;
case TCP_STATE_TIME_WAIT:
/* In a real OS we'd wait 2*MSL. Here we just ACK and stay. */
if (flags & TCP_FIN) {
tcp_send_segment(sock, TCP_ACK, NULL, 0);
}
/* Immediately transition to CLOSED (no timer). */
sock->state = TCP_STATE_CLOSED;
sock->active = 0;
break;
default:
break;
}
}
/* ================================================================
* Public API
* ================================================================ */
void tcp_init(void)
{
memset(tcp_sockets, 0, sizeof(tcp_sockets));
ipv4_register_proto(IP_PROTO_TCP, tcp_receive);
vga_puts("[TCP] Initialized (");
vga_put_dec(TCP_MAX_SOCKETS);
vga_puts(" sockets)\n");
}
int tcp_socket_create(void)
{
for (int i = 0; i < TCP_MAX_SOCKETS; i++) {
if (!tcp_sockets[i].active) {
memset(&tcp_sockets[i], 0, sizeof(tcp_socket_t));
tcp_sockets[i].active = 1;
tcp_sockets[i].state = TCP_STATE_CLOSED;
return i;
}
}
return -1;
}
int tcp_connect(int sockfd, uint32_t remote_ip, uint16_t remote_port)
{
if (sockfd < 0 || sockfd >= TCP_MAX_SOCKETS) return -1;
tcp_socket_t *sock = &tcp_sockets[sockfd];
if (!sock->active || sock->state != TCP_STATE_CLOSED) return -1;
/* Assign ephemeral local port. */
sock->local_port = tcp_next_port++;
if (tcp_next_port == 0) tcp_next_port = 49152;
sock->remote_ip = remote_ip;
sock->remote_port = remote_port;
/* Generate ISN. */
sock->snd_nxt = tcp_generate_isn();
sock->snd_una = sock->snd_nxt;
/* Send SYN. */
sock->state = TCP_STATE_SYN_SENT;
return tcp_send_segment(sock, TCP_SYN, NULL, 0);
}
int tcp_send(int sockfd, const void *data, uint32_t len)
{
if (sockfd < 0 || sockfd >= TCP_MAX_SOCKETS) return -1;
tcp_socket_t *sock = &tcp_sockets[sockfd];
if (!sock->active || sock->state != TCP_STATE_ESTABLISHED) return -1;
/* Send in MSS-sized chunks. */
uint32_t sent = 0;
const uint8_t *p = (const uint8_t *)data;
while (sent < len) {
uint32_t chunk = len - sent;
if (chunk > TCP_MSS) chunk = TCP_MSS;
if (tcp_send_segment(sock, TCP_ACK | TCP_PSH, p + sent, chunk) < 0)
break;
sent += chunk;
}
return (int)sent;
}
int tcp_recv(int sockfd, void *buf, uint32_t bufsize)
{
if (sockfd < 0 || sockfd >= TCP_MAX_SOCKETS) return -1;
tcp_socket_t *sock = &tcp_sockets[sockfd];
if (!sock->active) return -1;
/* If connection is closed with data still in buffer, return it. */
if (sock->rx_count == 0) {
if (sock->state == TCP_STATE_CLOSE_WAIT ||
sock->state == TCP_STATE_CLOSED) {
return -1; /* EOF / connection closed. */
}
return 0; /* No data available yet. */
}
return (int)rx_buf_read(sock, (uint8_t *)buf, bufsize);
}
void tcp_close(int sockfd)
{
if (sockfd < 0 || sockfd >= TCP_MAX_SOCKETS) return;
tcp_socket_t *sock = &tcp_sockets[sockfd];
if (!sock->active) return;
switch (sock->state) {
case TCP_STATE_ESTABLISHED:
sock->state = TCP_STATE_FIN_WAIT_1;
tcp_send_segment(sock, TCP_FIN | TCP_ACK, NULL, 0);
break;
case TCP_STATE_CLOSE_WAIT:
sock->state = TCP_STATE_LAST_ACK;
tcp_send_segment(sock, TCP_FIN | TCP_ACK, NULL, 0);
break;
case TCP_STATE_SYN_SENT:
case TCP_STATE_SYN_RECEIVED:
sock->state = TCP_STATE_CLOSED;
sock->active = 0;
break;
default:
/* Already closing or closed — force close. */
sock->state = TCP_STATE_CLOSED;
sock->active = 0;
break;
}
}
uint8_t tcp_get_state(int sockfd)
{
if (sockfd < 0 || sockfd >= TCP_MAX_SOCKETS) return TCP_STATE_CLOSED;
return tcp_sockets[sockfd].state;
}
const char *tcp_state_name(uint8_t state)
{
switch (state) {
case TCP_STATE_CLOSED: return "CLOSED";
case TCP_STATE_LISTEN: return "LISTEN";
case TCP_STATE_SYN_SENT: return "SYN_SENT";
case TCP_STATE_SYN_RECEIVED: return "SYN_RECEIVED";
case TCP_STATE_ESTABLISHED: return "ESTABLISHED";
case TCP_STATE_FIN_WAIT_1: return "FIN_WAIT_1";
case TCP_STATE_FIN_WAIT_2: return "FIN_WAIT_2";
case TCP_STATE_CLOSE_WAIT: return "CLOSE_WAIT";
case TCP_STATE_CLOSING: return "CLOSING";
case TCP_STATE_LAST_ACK: return "LAST_ACK";
case TCP_STATE_TIME_WAIT: return "TIME_WAIT";
default: return "UNKNOWN";
}
}

169
src/tcp.h Normal file
View File

@@ -0,0 +1,169 @@
/**
* @file tcp.h
* @brief Transmission Control Protocol (TCP) subsystem.
*
* Provides a minimal TCP implementation supporting active open
* (client connections). Implements the core TCP state machine
* for connection setup (SYN/SYN-ACK/ACK), data transfer, and
* connection teardown (FIN/ACK).
*
* Built on top of the IPv4 stack.
*/
#ifndef TCP_H
#define TCP_H
#include <stdint.h>
/** Maximum number of TCP sockets. */
#define TCP_MAX_SOCKETS 8
/** Maximum TCP segment payload. */
#define TCP_MSS 1460 /* ETH_MTU(1500) - IP(20) - TCP(20) */
/** TCP header size (no options). */
#define TCP_HLEN 20
/** TCP receive buffer size per socket. */
#define TCP_RX_BUF_SIZE 4096
/** TCP send buffer size per socket. */
#define TCP_TX_BUF_SIZE 4096
/* ================================================================
* TCP flags
* ================================================================ */
#define TCP_FIN 0x01
#define TCP_SYN 0x02
#define TCP_RST 0x04
#define TCP_PSH 0x08
#define TCP_ACK 0x10
#define TCP_URG 0x20
/* ================================================================
* TCP states (RFC 793)
* ================================================================ */
#define TCP_STATE_CLOSED 0
#define TCP_STATE_LISTEN 1
#define TCP_STATE_SYN_SENT 2
#define TCP_STATE_SYN_RECEIVED 3
#define TCP_STATE_ESTABLISHED 4
#define TCP_STATE_FIN_WAIT_1 5
#define TCP_STATE_FIN_WAIT_2 6
#define TCP_STATE_CLOSE_WAIT 7
#define TCP_STATE_CLOSING 8
#define TCP_STATE_LAST_ACK 9
#define TCP_STATE_TIME_WAIT 10
/* ================================================================
* TCP header
* ================================================================ */
typedef struct __attribute__((packed)) tcp_header {
uint16_t src_port; /**< Source port. */
uint16_t dst_port; /**< Destination port. */
uint32_t seq_num; /**< Sequence number. */
uint32_t ack_num; /**< Acknowledgment number. */
uint8_t data_offset; /**< Data offset (upper 4 bits) + reserved. */
uint8_t flags; /**< TCP flags. */
uint16_t window; /**< Window size. */
uint16_t checksum; /**< Checksum. */
uint16_t urgent; /**< Urgent pointer. */
} tcp_header_t;
/* ================================================================
* TCP socket
* ================================================================ */
typedef struct tcp_socket {
uint8_t active; /**< 1 if in use. */
uint8_t state; /**< TCP_STATE_*. */
uint16_t local_port; /**< Local port (host byte order). */
uint16_t remote_port; /**< Remote port (host byte order). */
uint32_t remote_ip; /**< Remote IP (host byte order). */
/* Sequence numbers */
uint32_t snd_una; /**< Send unacknowledged. */
uint32_t snd_nxt; /**< Send next. */
uint32_t rcv_nxt; /**< Receive next expected. */
/* Receive buffer (ring buffer) */
uint8_t rx_buf[TCP_RX_BUF_SIZE];
uint32_t rx_head; /**< Write position. */
uint32_t rx_tail; /**< Read position. */
uint32_t rx_count; /**< Bytes available. */
} tcp_socket_t;
/* ================================================================
* Public API
* ================================================================ */
/**
* Initialize the TCP subsystem.
*/
void tcp_init(void);
/**
* Create a TCP socket.
* @return Socket index (>= 0), or -1 on failure.
*/
int tcp_socket_create(void);
/**
* Connect to a remote host (active open).
* Sends SYN and transitions to SYN_SENT state.
*
* @param sockfd Socket index.
* @param remote_ip Remote IP (host byte order).
* @param remote_port Remote port (host byte order).
* @return 0 on success (SYN sent), -1 on failure.
*/
int tcp_connect(int sockfd, uint32_t remote_ip, uint16_t remote_port);
/**
* Send data on an established connection.
*
* @param sockfd Socket index.
* @param data Data to send.
* @param len Data length.
* @return Number of bytes sent, or -1 on failure.
*/
int tcp_send(int sockfd, const void *data, uint32_t len);
/**
* Receive data from an established connection (non-blocking).
*
* @param sockfd Socket index.
* @param buf Buffer.
* @param bufsize Buffer size.
* @return Number of bytes received, 0 if no data, -1 on error/closed.
*/
int tcp_recv(int sockfd, void *buf, uint32_t bufsize);
/**
* Close a TCP connection.
*
* @param sockfd Socket index.
*/
void tcp_close(int sockfd);
/**
* Get the state of a TCP socket.
*
* @param sockfd Socket index.
* @return TCP state constant, or TCP_STATE_CLOSED for invalid sockets.
*/
uint8_t tcp_get_state(int sockfd);
/**
* Get the state name as a string.
*/
const char *tcp_state_name(uint8_t state);
/**
* Process an incoming TCP segment (called from IPv4 layer).
*/
void tcp_receive(uint32_t src_ip, uint32_t dst_ip,
const void *data, uint32_t len, uint32_t iface_idx);
#endif /* TCP_H */

180
src/udp.c Normal file
View File

@@ -0,0 +1,180 @@
/**
* @file udp.c
* @brief UDP subsystem implementation.
*
* Provides a simple socket-like interface for sending and receiving
* UDP datagrams. Registers with the IPv4 stack as protocol handler 17.
*/
#include "udp.h"
#include "ipv4.h"
#include "ethernet.h"
#include <string.h>
/* Debug print helpers */
extern void offset_print(const char *str);
extern void print_hex(uint32_t val);
/* ================================================================
* Global state
* ================================================================ */
/** UDP socket table. */
static udp_socket_t sockets[UDP_MAX_SOCKETS];
/* ================================================================
* Internal helpers
* ================================================================ */
/**
* Find a socket bound to the given local port.
*/
static udp_socket_t *find_socket_by_port(uint16_t port) {
for (int i = 0; i < UDP_MAX_SOCKETS; i++) {
if (sockets[i].active && sockets[i].bound &&
sockets[i].local_port == port) {
return &sockets[i];
}
}
return NULL;
}
/* ================================================================
* Public API
* ================================================================ */
int udp_socket_create(void) {
for (int i = 0; i < UDP_MAX_SOCKETS; i++) {
if (!sockets[i].active) {
memset(&sockets[i], 0, sizeof(udp_socket_t));
sockets[i].active = 1;
return i;
}
}
return -1;
}
int udp_bind(int sockfd, uint16_t port) {
if (sockfd < 0 || sockfd >= UDP_MAX_SOCKETS) return -1;
if (!sockets[sockfd].active) return -1;
/* Check port not already used */
if (find_socket_by_port(port)) return -1;
sockets[sockfd].local_port = port;
sockets[sockfd].bound = 1;
return 0;
}
int udp_sendto(int sockfd, uint32_t dst_ip, uint16_t dst_port,
const void *data, uint32_t len) {
if (sockfd < 0 || sockfd >= UDP_MAX_SOCKETS) return -1;
if (!sockets[sockfd].active) return -1;
if (len > UDP_MAX_PAYLOAD) return -1;
/* Build UDP datagram */
uint8_t pkt[UDP_HLEN + UDP_MAX_PAYLOAD];
udp_header_t *hdr = (udp_header_t *)pkt;
hdr->src_port = htons(sockets[sockfd].local_port);
hdr->dst_port = htons(dst_port);
hdr->length = htons((uint16_t)(UDP_HLEN + len));
hdr->checksum = 0; /* Optional in IPv4 */
memcpy(pkt + UDP_HLEN, data, len);
/* Send via IPv4 */
int ret = ipv4_send(dst_ip, IP_PROTO_UDP, pkt, UDP_HLEN + len);
return (ret == 0) ? (int)len : -1;
}
int udp_recvfrom(int sockfd, void *buf, uint32_t bufsize,
uint32_t *src_ip, uint16_t *src_port) {
if (sockfd < 0 || sockfd >= UDP_MAX_SOCKETS) return -1;
if (!sockets[sockfd].active) return -1;
udp_socket_t *sock = &sockets[sockfd];
/* Scan the receive queue for the oldest entry */
for (int i = 0; i < UDP_RX_QUEUE_SIZE; i++) {
if (sock->rx_queue[i].used) {
udp_rx_entry_t *entry = &sock->rx_queue[i];
uint32_t copy_len = entry->len;
if (copy_len > bufsize) copy_len = bufsize;
memcpy(buf, entry->data, copy_len);
if (src_ip) *src_ip = entry->src_ip;
if (src_port) *src_port = entry->src_port;
entry->used = 0; /* Free the slot */
return (int)copy_len;
}
}
return 0; /* No data available */
}
void udp_close(int sockfd) {
if (sockfd < 0 || sockfd >= UDP_MAX_SOCKETS) return;
memset(&sockets[sockfd], 0, sizeof(udp_socket_t));
}
/* ================================================================
* IPv4 protocol handler
* ================================================================ */
void udp_receive(uint32_t src_ip, uint32_t dst_ip,
const void *data, uint32_t len, uint32_t iface_idx) {
(void)dst_ip;
(void)iface_idx;
if (len < UDP_HLEN) return;
const udp_header_t *hdr = (const udp_header_t *)data;
uint16_t dst_port = ntohs(hdr->dst_port);
uint16_t src_port_val = ntohs(hdr->src_port);
uint16_t udp_len = ntohs(hdr->length);
if (udp_len > len) return;
uint32_t payload_len = udp_len - UDP_HLEN;
/* Find socket bound to this port */
udp_socket_t *sock = find_socket_by_port(dst_port);
if (!sock) return;
/* Enqueue the datagram */
for (int i = 0; i < UDP_RX_QUEUE_SIZE; i++) {
if (!sock->rx_queue[i].used) {
udp_rx_entry_t *entry = &sock->rx_queue[i];
entry->src_ip = src_ip;
entry->src_port = src_port_val;
entry->len = (uint16_t)(payload_len > UDP_RX_BUF_SIZE ?
UDP_RX_BUF_SIZE : payload_len);
memcpy(entry->data, (const uint8_t *)data + UDP_HLEN,
entry->len);
entry->used = 1;
return;
}
}
/* Queue full — drop packet */
}
/* ================================================================
* Initialization
* ================================================================ */
/**
* IPv4 callback wrapper for UDP.
*/
static void udp_ipv4_handler(uint32_t src_ip, uint32_t dst_ip,
const void *payload, uint32_t len,
uint32_t iface_idx) {
udp_receive(src_ip, dst_ip, payload, len, iface_idx);
}
void udp_init(void) {
memset(sockets, 0, sizeof(sockets));
ipv4_register_proto(IP_PROTO_UDP, udp_ipv4_handler);
offset_print(" UDP: initialized\n");
}

128
src/udp.h Normal file
View File

@@ -0,0 +1,128 @@
/**
* @file udp.h
* @brief User Datagram Protocol (UDP) subsystem.
*
* Provides a simple UDP socket interface built on top of the IPv4 stack.
* Supports connectionless datagram communication.
*/
#ifndef UDP_H
#define UDP_H
#include <stdint.h>
/** Maximum number of UDP sockets. */
#define UDP_MAX_SOCKETS 16
/** Maximum UDP payload size. */
#define UDP_MAX_PAYLOAD 1472 /* ETH_MTU(1500) - IP(20) - UDP(8) */
/** UDP header size. */
#define UDP_HLEN 8
/* ================================================================
* UDP header
* ================================================================ */
typedef struct __attribute__((packed)) udp_header {
uint16_t src_port; /**< Source port (network byte order). */
uint16_t dst_port; /**< Destination port (network byte order). */
uint16_t length; /**< Total length (header + payload, NBO). */
uint16_t checksum; /**< Checksum (0 = not computed). */
} udp_header_t;
/* ================================================================
* UDP socket
* ================================================================ */
/** Maximum number of queued received datagrams per socket. */
#define UDP_RX_QUEUE_SIZE 8
/** Maximum datagram size in the receive queue. */
#define UDP_RX_BUF_SIZE 1500
/** Received datagram in the queue. */
typedef struct udp_rx_entry {
uint32_t src_ip; /**< Source IP (host byte order). */
uint16_t src_port; /**< Source port (host byte order). */
uint16_t len; /**< Payload length. */
uint8_t data[UDP_RX_BUF_SIZE]; /**< Payload data. */
uint8_t used; /**< 1 if slot is occupied. */
} udp_rx_entry_t;
/**
* UDP socket state.
*/
typedef struct udp_socket {
uint16_t local_port; /**< Bound local port (host byte order). */
uint8_t active; /**< 1 if socket is in use. */
uint8_t bound; /**< 1 if bound to a port. */
udp_rx_entry_t rx_queue[UDP_RX_QUEUE_SIZE]; /**< Receive queue. */
uint32_t rx_head; /**< Next slot to write. */
} udp_socket_t;
/* ================================================================
* Public API
* ================================================================ */
/**
* Initialize the UDP subsystem.
* Registers as an IPv4 protocol handler.
*/
void udp_init(void);
/**
* Create a UDP socket.
* @return Socket index (>= 0) on success, -1 on failure.
*/
int udp_socket_create(void);
/**
* Bind a socket to a local port.
*
* @param sockfd Socket index.
* @param port Local port number (host byte order).
* @return 0 on success, -1 on failure.
*/
int udp_bind(int sockfd, uint16_t port);
/**
* Send a UDP datagram.
*
* @param sockfd Socket index.
* @param dst_ip Destination IP (host byte order).
* @param dst_port Destination port (host byte order).
* @param data Payload data.
* @param len Payload length (max UDP_MAX_PAYLOAD).
* @return Number of bytes sent, or -1 on failure.
*/
int udp_sendto(int sockfd, uint32_t dst_ip, uint16_t dst_port,
const void *data, uint32_t len);
/**
* Receive a UDP datagram (non-blocking).
*
* @param sockfd Socket index.
* @param buf Buffer for payload.
* @param bufsize Buffer size.
* @param src_ip Output: source IP (host byte order, can be NULL).
* @param src_port Output: source port (host byte order, can be NULL).
* @return Number of bytes received, 0 if no data, -1 on error.
*/
int udp_recvfrom(int sockfd, void *buf, uint32_t bufsize,
uint32_t *src_ip, uint16_t *src_port);
/**
* Close a UDP socket.
*
* @param sockfd Socket index.
*/
void udp_close(int sockfd);
/**
* Process an incoming UDP datagram (called from IPv4 layer).
*/
void udp_receive(uint32_t src_ip, uint32_t dst_ip,
const void *data, uint32_t len, uint32_t iface_idx);
#endif /* UDP_H */