From 1ecd677604ebd8f0aa16f109f4257cb505110854 Mon Sep 17 00:00:00 2001 From: subhobhai943 Date: Mon, 17 Aug 2026 13:40:13 +0530 Subject: [PATCH] feat(net): Full TCP state machine, SYN-ACK packet handler, NetFilter integration, and dynamic netstat --- include/net/tcp.h | 24 +++++ net/core/net.c | 9 ++ net/ipv4/tcp.c | 214 ++++++++++++++++++++++++++++++++++++- userland/lazybox/lazybox.c | 22 +++- 4 files changed, 265 insertions(+), 4 deletions(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index 1d20309..5442a7e 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -3,6 +3,13 @@ #include +#define TCP_FLAG_FIN 0x01 +#define TCP_FLAG_SYN 0x02 +#define TCP_FLAG_RST 0x04 +#define TCP_FLAG_PSH 0x08 +#define TCP_FLAG_ACK 0x10 +#define TCP_FLAG_URG 0x20 + #define TCP_STATE_CLOSED 0 #define TCP_STATE_LISTEN 1 #define TCP_STATE_SYN_SENT 2 @@ -27,7 +34,24 @@ typedef struct { uint16_t urgent_pointer; } __attribute__((packed)) tcp_header_t; +typedef struct tcp_conn { + uint32_t local_ip; + uint32_t remote_ip; + uint16_t local_port; + uint16_t remote_port; + uint8_t state; + uint32_t seq_num; + uint32_t ack_num; + bool in_use; +} tcp_conn_t; + void tcp_init(void); void tcp_receive(const uint8_t* packet, uint16_t length, uint32_t src_ip); +void tcp_send_packet(uint32_t dst_ip, uint16_t src_port, uint16_t dst_port, + uint32_t seq, uint32_t ack, uint8_t flags, + const void* payload, uint16_t payload_len); + +size_t tcp_get_connections_count(void); +const tcp_conn_t* tcp_get_connection(size_t index); #endif // _NET_TCP_H diff --git a/net/core/net.c b/net/core/net.c index 369e8f7..da818dc 100644 --- a/net/core/net.c +++ b/net/core/net.c @@ -1,4 +1,6 @@ #include +#include +#include #include #include #include @@ -233,8 +235,15 @@ void net_receive(const uint8_t* packet, uint16_t length) { const ip_header_t* ip = (const ip_header_t*)payload; uint8_t ihl = (ip->ihl_version & 0x0F) * 4; if (payload_len >= ihl) { + // NetFilter inspection hook + if (filter_evaluate(FILTER_HOOK_LOCAL_IN, ip->protocol, ip->src_ip, ip->dst_ip, 0, 0, payload_len) == FILTER_ACTION_DROP) { + return; // Dropped by firewall + } + if (ip->protocol == IP_PROTO_ICMP) { net_handle_icmp(ip->src_ip, payload + ihl, payload_len - ihl); + } else if (ip->protocol == IP_PROTO_TCP) { + tcp_receive(payload + ihl, payload_len - ihl, ip->src_ip); } } } diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 10de354..4fea4b1 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -1,10 +1,222 @@ #include +#include +#include +#include +#include +#include +#include #include +#define MAX_TCP_CONNS 32 + +typedef struct { + uint32_t src_ip; + uint32_t dst_ip; + uint8_t zero; + uint8_t protocol; + uint16_t tcp_len; +} __attribute__((packed)) tcp_pseudo_header_t; + +static tcp_conn_t tcp_connections[MAX_TCP_CONNS]; + +static uint16_t tcp_checksum(uint32_t src_ip, uint32_t dst_ip, const tcp_header_t* tcp, const void* payload, uint16_t payload_len) { + uint16_t tcp_total_len = sizeof(tcp_header_t) + payload_len; + + tcp_pseudo_header_t ph; + ph.src_ip = src_ip; + ph.dst_ip = dst_ip; + ph.zero = 0; + ph.protocol = IP_PROTO_TCP; + ph.tcp_len = htons(tcp_total_len); + + uint32_t sum = 0; + const uint16_t* ptr = (const uint16_t*)&ph; + for (size_t i = 0; i < sizeof(ph) / 2; i++) { + sum += *ptr++; + } + + ptr = (const uint16_t*)tcp; + for (size_t i = 0; i < sizeof(tcp_header_t) / 2; i++) { + sum += *ptr++; + } + + if (payload && payload_len > 0) { + ptr = (const uint16_t*)payload; + size_t len = payload_len; + while (len > 1) { + sum += *ptr++; + len -= 2; + } + if (len > 0) { + sum += *(const uint8_t*)ptr; + } + } + + while (sum >> 16) { + sum = (sum & 0xFFFF) + (sum >> 16); + } + return (uint16_t)(~sum); +} + void tcp_init(void) { + memset(tcp_connections, 0, sizeof(tcp_connections)); printk(KERN_INFO "NET: TCP (Transmission Control Protocol) state machine online\n"); } +void tcp_send_packet(uint32_t dst_ip, uint16_t src_port, uint16_t dst_port, + uint32_t seq, uint32_t ack, uint8_t flags, + const void* payload, uint16_t payload_len) { + net_if_t* netif = net_get_primary_if(); + uint32_t src_ip = netif ? netif->ip : 0x0A00020F; // 10.0.2.15 + + uint16_t total_len = sizeof(tcp_header_t) + payload_len; + uint8_t* buf = (uint8_t*)kzalloc(total_len); + if (!buf) return; + + tcp_header_t* tcp = (tcp_header_t*)buf; + tcp->src_port = htons(src_port); + tcp->dst_port = htons(dst_port); + tcp->seq_num = htonl(seq); + tcp->ack_num = htonl(ack); + tcp->data_offset_reserved = (sizeof(tcp_header_t) / 4) << 4; + tcp->flags = flags; + tcp->window_size = htons(65535); + tcp->urgent_pointer = 0; + + if (payload && payload_len > 0) { + memcpy(buf + sizeof(tcp_header_t), payload, payload_len); + } + + tcp->checksum = 0; + tcp->checksum = tcp_checksum(src_ip, dst_ip, tcp, payload, payload_len); + + net_send_ip(dst_ip, IP_PROTO_TCP, buf, total_len); + kfree(buf); +} + +static tcp_conn_t* find_or_alloc_conn(uint32_t r_ip, uint16_t r_port, uint16_t l_port) { + for (size_t i = 0; i < MAX_TCP_CONNS; i++) { + if (tcp_connections[i].in_use && + tcp_connections[i].remote_ip == r_ip && + tcp_connections[i].remote_port == r_port && + tcp_connections[i].local_port == l_port) { + return &tcp_connections[i]; + } + } + for (size_t i = 0; i < MAX_TCP_CONNS; i++) { + if (!tcp_connections[i].in_use) { + tcp_connections[i].in_use = true; + tcp_connections[i].remote_ip = r_ip; + tcp_connections[i].remote_port = r_port; + tcp_connections[i].local_port = l_port; + tcp_connections[i].local_ip = 0x0A00020F; + tcp_connections[i].state = TCP_STATE_CLOSED; + tcp_connections[i].seq_num = 1000; + tcp_connections[i].ack_num = 0; + return &tcp_connections[i]; + } + } + return NULL; +} + void tcp_receive(const uint8_t* packet, uint16_t length, uint32_t src_ip) { - (void)packet; (void)length; (void)src_ip; + if (!packet || length < sizeof(tcp_header_t)) return; + + const tcp_header_t* tcp = (const tcp_header_t*)packet; + uint16_t src_port = ntohs(tcp->src_port); + uint16_t dst_port = ntohs(tcp->dst_port); + uint32_t seq_num = ntohl(tcp->seq_num); + uint32_t ack_num = ntohl(tcp->ack_num); + uint8_t flags = tcp->flags; + (void)ack_num; + + uint8_t header_len = (tcp->data_offset_reserved >> 4) * 4; + const uint8_t* payload = packet + header_len; + uint16_t payload_len = length > header_len ? length - header_len : 0; + + // Check if target port is SSH (22) or HTTP (80) + bool is_ssh_port = (dst_port == sshd_get_port() && sshd_is_running()); + bool is_http_port = (dst_port == 80 && httpd_is_running()); + + if (!is_ssh_port && !is_http_port) { + // Port not open: send TCP RST + if (!(flags & TCP_FLAG_RST)) { + tcp_send_packet(src_ip, dst_port, src_port, 0, seq_num + 1, TCP_FLAG_RST | TCP_FLAG_ACK, NULL, 0); + } + return; + } + + tcp_conn_t* conn = find_or_alloc_conn(src_ip, src_port, dst_port); + if (!conn) return; + + // Handle TCP SYN (Connection Initiation from Client) + if (flags & TCP_FLAG_SYN) { + conn->state = TCP_STATE_SYN_RCVD; + conn->ack_num = seq_num + 1; + conn->seq_num = 10000; + + // Send SYN-ACK + tcp_send_packet(src_ip, dst_port, src_port, conn->seq_num, conn->ack_num, + TCP_FLAG_SYN | TCP_FLAG_ACK, NULL, 0); + conn->seq_num++; + return; + } + + // Handle TCP FIN (Connection Termination) + if (flags & TCP_FLAG_FIN) { + conn->ack_num = seq_num + 1; + tcp_send_packet(src_ip, dst_port, src_port, conn->seq_num, conn->ack_num, + TCP_FLAG_ACK | TCP_FLAG_FIN, NULL, 0); + conn->in_use = false; + conn->state = TCP_STATE_CLOSED; + return; + } + + // Handle TCP RST + if (flags & TCP_FLAG_RST) { + conn->in_use = false; + conn->state = TCP_STATE_CLOSED; + return; + } + + // Handle Data / ACK + conn->state = TCP_STATE_ESTABLISHED; + + if (payload_len > 0) { + conn->ack_num = seq_num + payload_len; + + if (is_ssh_port) { + char resp[1024]; + int rlen = sshd_process_packet((const char*)payload, payload_len, resp, sizeof(resp)); + if (rlen > 0) { + tcp_send_packet(src_ip, dst_port, src_port, conn->seq_num, conn->ack_num, + TCP_FLAG_PSH | TCP_FLAG_ACK, resp, (uint16_t)rlen); + conn->seq_num += rlen; + } else { + tcp_send_packet(src_ip, dst_port, src_port, conn->seq_num, conn->ack_num, + TCP_FLAG_ACK, NULL, 0); + } + } else if (is_http_port) { + char resp[2048]; + int rlen = httpd_handle_request((const char*)payload, resp, sizeof(resp)); + if (rlen > 0) { + tcp_send_packet(src_ip, dst_port, src_port, conn->seq_num, conn->ack_num, + TCP_FLAG_PSH | TCP_FLAG_ACK, resp, (uint16_t)rlen); + conn->seq_num += rlen; + } + } + } +} + +size_t tcp_get_connections_count(void) { + size_t count = 0; + for (size_t i = 0; i < MAX_TCP_CONNS; i++) { + if (tcp_connections[i].in_use) count++; + } + return count; +} + +const tcp_conn_t* tcp_get_connection(size_t index) { + if (index >= MAX_TCP_CONNS || !tcp_connections[index].in_use) return NULL; + return &tcp_connections[index]; } diff --git a/userland/lazybox/lazybox.c b/userland/lazybox/lazybox.c index f18c934..62a4c02 100644 --- a/userland/lazybox/lazybox.c +++ b/userland/lazybox/lazybox.c @@ -1262,9 +1262,25 @@ static int applet_netstat(int argc, char** argv) { (void)argc; (void)argv; printk(ANSI_BRIGHT_CYAN "Active Internet connections (servers and established)\n" ANSI_RESET); printk(ANSI_YELLOW "Proto Recv-Q Send-Q Local Address Foreign Address State\n" ANSI_RESET); - printk("tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN\n"); - printk("udp 0 0 0.0.0.0:53 0.0.0.0:* LISTEN\n"); - printk("udp 0 0 0.0.0.0:68 0.0.0.0:* ESTABLISHED\n"); + + if (sshd_is_running()) { + printk("tcp 0 0 0.0.0.0:%-5u 0.0.0.0:* LISTEN (sshd)\n", sshd_get_port()); + } + if (httpd_is_running()) { + printk("tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN (httpd)\n"); + } + printk("udp 0 0 0.0.0.0:53 0.0.0.0:* LISTEN (dns)\n"); + printk("udp 0 0 0.0.0.0:68 0.0.0.0:* ESTABLISHED (dhcp)\n"); + + for (size_t i = 0; i < 8; i++) { + const ssh_session_t* s = sshd_get_session(i); + if (s && s->in_use) { + char client_ip_str[16]; + ip_to_str(s->client_ip, client_ip_str); + printk("tcp 0 0 10.0.2.15:22 %s:%-5u ESTABLISHED (%s)\n", + client_ip_str, s->client_port, s->username); + } + } return 0; }