mirror of
https://github.com/subhobhai943/SUB-OS.git
synced 2026-10-25 16:52:29 +00:00
Turn the networking stubs into a working transport/application stack on top of
the existing L2/L3 (Ethernet/ARP/IPv4/ICMP) core.
UDP (net/ipv4/udp.c)
- Port-demux table with bound receive handlers, pseudo-header checksum,
ephemeral-port allocation, limited-broadcast send (0.0.0.0 -> 255.255.255.255
for DHCP before the interface has an address), and rx/tx/drop counters.
- Wired into the IPv4 receive path (IP_PROTO_UDP dispatch in net/core/net.c).
BSD sockets (net/core/socket.c)
- Real SOCK_DGRAM sockets backed by UDP: bind installs a UDP handler that fills
a per-socket receive ring; send/sendto and recv/recvfrom move datagrams;
ephemeral auto-bind, connect() memorises the peer. Socket table + I/O stats
exposed for introspection.
DNS resolver (net/ipv4/dns.c)
- Builds and parses real A-record queries over UDP/53 (label encoding,
compression-pointer aware answer walk), cache-first with TTL, static-hosts
and dotted-quad fast paths, and a bounded timeout that degrades to fallback
instead of hanging.
DHCP client (net/ipv4/dhcp.c)
- Full DISCOVER/OFFER/REQUEST/ACK handshake over UDP broadcast, option parsing
(mask/router/dns/lease/server-id), applies the lease to the primary
interface, bounded timeouts keep the static config on no response.
Fix (net/ipv4/dns_cache.c): the cache stored IPs as 0xAABBCCDD numeric while the
rest of the stack uses network-byte-order-in-memory (ip_parse/ip_to_str), so a
resolved address came back byte-swapped (nslookup github.com -> 100.121.82.140).
Seeds and dump now use ip_parse/ip_to_str; verified 140.82.121.4.
Userland (lazybox): live ifconfig (real interface + NIC counters, loopback),
netstat with live UDP bindings and transport stats, new `sockstat`, richer
nslookup. Verified in QEMU: boot clean on x86_64/aarch64/armv8i, login as SUB,
ping/nslookup/dhclient/netstat/sockstat all run; DHCP/DNS wire replies are
blocked by the sandbox's slirp but time out cleanly and fall back as designed.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Fh5z6V7nGPbHvqVG5p4CXX
215 lines
6.9 KiB
C
215 lines
6.9 KiB
C
// BSD-style socket layer for SUB-OS.
|
|
//
|
|
// Implements connectionless (SOCK_DGRAM) sockets on top of the UDP transport.
|
|
// Binding a datagram socket registers a UDP handler that appends inbound
|
|
// datagrams to the socket's fixed-depth receive ring; sys_recv/sys_recvfrom pop
|
|
// the oldest datagram. Stream (SOCK_STREAM) sockets are accepted and tracked so
|
|
// the API is uniform, but their data path is served by the in-kernel TCP engine.
|
|
#include <net/socket.h>
|
|
#include <net/udp.h>
|
|
#include <net/net.h>
|
|
#include <lib/string.h>
|
|
#include <kernel/printk.h>
|
|
|
|
#define MAX_SOCKETS 32
|
|
#define RX_RING_DEPTH 8
|
|
#define DGRAM_MAX 1472 // 1500 MTU - 20 IP - 8 UDP
|
|
|
|
typedef struct {
|
|
uint32_t src_ip;
|
|
uint16_t src_port;
|
|
uint16_t len;
|
|
uint8_t data[DGRAM_MAX];
|
|
} dgram_t;
|
|
|
|
// Per-socket receive ring, kept parallel to the public socket_t so the exported
|
|
// struct stays small.
|
|
typedef struct {
|
|
dgram_t ring[RX_RING_DEPTH];
|
|
int head; // next slot to read
|
|
int count; // queued datagrams
|
|
bool bound; // a UDP binding is installed for local_port
|
|
} sock_priv_t;
|
|
|
|
static socket_t socket_table[MAX_SOCKETS];
|
|
static sock_priv_t socket_priv[MAX_SOCKETS];
|
|
|
|
static volatile uint64_t stat_tx = 0;
|
|
static volatile uint64_t stat_rx = 0;
|
|
static volatile uint64_t stat_drop = 0;
|
|
|
|
void socket_subsystem_init(void) {
|
|
memset(socket_table, 0, sizeof(socket_table));
|
|
memset(socket_priv, 0, sizeof(socket_priv));
|
|
stat_tx = stat_rx = stat_drop = 0;
|
|
printk(KERN_INFO "NET: BSD Sockets API Layer initialized (AF_INET support)\n");
|
|
}
|
|
|
|
// Runs in UDP receive context: enqueue the datagram, dropping the oldest if the
|
|
// ring is full so a stalled reader never wedges delivery.
|
|
static void socket_udp_cb(uint32_t src_ip, uint16_t src_port, uint16_t dst_port,
|
|
const uint8_t* data, uint16_t len, void* ctx) {
|
|
(void)dst_port;
|
|
sock_priv_t* pv = (sock_priv_t*)ctx;
|
|
if (!pv) return;
|
|
if (len > DGRAM_MAX) len = DGRAM_MAX;
|
|
|
|
int slot;
|
|
if (pv->count == RX_RING_DEPTH) {
|
|
// Overwrite the oldest.
|
|
pv->head = (pv->head + 1) % RX_RING_DEPTH;
|
|
slot = (pv->head + pv->count - 1) % RX_RING_DEPTH;
|
|
stat_drop++;
|
|
} else {
|
|
slot = (pv->head + pv->count) % RX_RING_DEPTH;
|
|
pv->count++;
|
|
}
|
|
|
|
dgram_t* d = &pv->ring[slot];
|
|
d->src_ip = src_ip;
|
|
d->src_port = src_port;
|
|
d->len = len;
|
|
if (data && len) memcpy(d->data, data, len);
|
|
stat_rx++;
|
|
}
|
|
|
|
int sys_socket(int domain, int type, int protocol) {
|
|
if (domain != AF_INET) return -1;
|
|
if (type != SOCK_DGRAM && type != SOCK_STREAM && type != SOCK_RAW) return -1;
|
|
|
|
for (int i = 0; i < MAX_SOCKETS; i++) {
|
|
if (!socket_table[i].in_use) {
|
|
memset(&socket_table[i], 0, sizeof(socket_t));
|
|
memset(&socket_priv[i], 0, sizeof(sock_priv_t));
|
|
socket_table[i].in_use = true;
|
|
socket_table[i].domain = domain;
|
|
socket_table[i].type = type;
|
|
socket_table[i].protocol = protocol ? protocol :
|
|
(type == SOCK_DGRAM ? IPPROTO_UDP : IPPROTO_TCP);
|
|
socket_table[i].state = 0;
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static bool valid_fd(int fd) {
|
|
return fd >= 0 && fd < MAX_SOCKETS && socket_table[fd].in_use;
|
|
}
|
|
|
|
// Install the UDP binding for a datagram socket's local port (idempotent).
|
|
static int dgram_ensure_bound(int fd) {
|
|
socket_t* s = &socket_table[fd];
|
|
sock_priv_t* pv = &socket_priv[fd];
|
|
if (pv->bound) return 0;
|
|
if (s->local_port == 0) {
|
|
s->local_port = udp_alloc_ephemeral_port();
|
|
if (s->local_port == 0) return -1;
|
|
}
|
|
if (udp_bind(s->local_port, socket_udp_cb, pv) != 0) return -1;
|
|
pv->bound = true;
|
|
return 0;
|
|
}
|
|
|
|
int sys_bind(int sockfd, const struct sockaddr_in* addr, size_t addrlen) {
|
|
(void)addrlen;
|
|
if (!valid_fd(sockfd) || !addr) return -1;
|
|
socket_t* s = &socket_table[sockfd];
|
|
s->local_ip = addr->sin_addr;
|
|
s->local_port = ntohs(addr->sin_port);
|
|
if (s->type == SOCK_DGRAM) return dgram_ensure_bound(sockfd);
|
|
return 0;
|
|
}
|
|
|
|
int sys_connect(int sockfd, const struct sockaddr_in* addr, size_t addrlen) {
|
|
(void)addrlen;
|
|
if (!valid_fd(sockfd) || !addr) return -1;
|
|
socket_t* s = &socket_table[sockfd];
|
|
s->remote_ip = addr->sin_addr;
|
|
s->remote_port = ntohs(addr->sin_port);
|
|
s->state = 1; // "connected" (address memorised for send/recv)
|
|
if (s->type == SOCK_DGRAM) return dgram_ensure_bound(sockfd);
|
|
return 0;
|
|
}
|
|
|
|
ssize_t sys_sendto(int sockfd, const void* buf, size_t len, int flags,
|
|
const struct sockaddr_in* dest, size_t addrlen) {
|
|
(void)flags; (void)addrlen;
|
|
if (!valid_fd(sockfd) || !buf) return -1;
|
|
socket_t* s = &socket_table[sockfd];
|
|
if (s->type != SOCK_DGRAM) return -1;
|
|
if (len > DGRAM_MAX) len = DGRAM_MAX;
|
|
|
|
uint32_t dip;
|
|
uint16_t dport;
|
|
if (dest) { dip = dest->sin_addr; dport = ntohs(dest->sin_port); }
|
|
else if (s->state == 1) { dip = s->remote_ip; dport = s->remote_port; }
|
|
else return -1;
|
|
|
|
if (dgram_ensure_bound(sockfd) != 0) return -1;
|
|
udp_send(dip, s->local_port, dport, buf, (uint16_t)len);
|
|
stat_tx++;
|
|
return (ssize_t)len;
|
|
}
|
|
|
|
ssize_t sys_send(int sockfd, const void* buf, size_t len, int flags) {
|
|
return sys_sendto(sockfd, buf, len, flags, NULL, 0);
|
|
}
|
|
|
|
ssize_t sys_recvfrom(int sockfd, void* buf, size_t len, int flags,
|
|
struct sockaddr_in* src, size_t* addrlen) {
|
|
(void)flags;
|
|
if (!valid_fd(sockfd) || !buf) return -1;
|
|
socket_t* s = &socket_table[sockfd];
|
|
if (s->type != SOCK_DGRAM) return 0;
|
|
|
|
sock_priv_t* pv = &socket_priv[sockfd];
|
|
if (pv->count == 0) return 0; // non-blocking: nothing queued
|
|
|
|
dgram_t* d = &pv->ring[pv->head];
|
|
size_t n = d->len < len ? d->len : len;
|
|
memcpy(buf, d->data, n);
|
|
|
|
if (src) {
|
|
src->sin_family = AF_INET;
|
|
src->sin_addr = d->src_ip;
|
|
src->sin_port = htons(d->src_port);
|
|
if (addrlen) *addrlen = sizeof(struct sockaddr_in);
|
|
}
|
|
|
|
pv->head = (pv->head + 1) % RX_RING_DEPTH;
|
|
pv->count--;
|
|
return (ssize_t)n;
|
|
}
|
|
|
|
ssize_t sys_recv(int sockfd, void* buf, size_t len, int flags) {
|
|
return sys_recvfrom(sockfd, buf, len, flags, NULL, NULL);
|
|
}
|
|
|
|
int sys_close_socket(int sockfd) {
|
|
if (!valid_fd(sockfd)) return -1;
|
|
socket_t* s = &socket_table[sockfd];
|
|
sock_priv_t* pv = &socket_priv[sockfd];
|
|
if (pv->bound && s->local_port) udp_unbind(s->local_port);
|
|
memset(s, 0, sizeof(socket_t));
|
|
memset(pv, 0, sizeof(sock_priv_t));
|
|
return 0;
|
|
}
|
|
|
|
int socket_get_count(void) {
|
|
int n = 0;
|
|
for (int i = 0; i < MAX_SOCKETS; i++) if (socket_table[i].in_use) n++;
|
|
return n;
|
|
}
|
|
|
|
const socket_t* socket_get(int idx) {
|
|
if (idx < 0 || idx >= MAX_SOCKETS || !socket_table[idx].in_use) return NULL;
|
|
return &socket_table[idx];
|
|
}
|
|
|
|
void socket_get_stats(uint64_t* tx_dgrams, uint64_t* rx_dgrams, uint64_t* drops) {
|
|
if (tx_dgrams) *tx_dgrams = stat_tx;
|
|
if (rx_dgrams) *rx_dgrams = stat_rx;
|
|
if (drops) *drops = stat_drop;
|
|
}
|