mirror of
https://github.com/subhobhai943/SUB-OS.git
synced 2026-10-25 16:52:29 +00:00
feat(net): real UDP transport, BSD datagram sockets, DNS resolver, DHCP client
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
This commit is contained in:
1 parent
2c2e2d536e
commit
cd840d5799
10 files changed
+924
-57
No files matched your search
@@ -1,5 +1,6 @@
|
||||
#include <net/net.h>
|
||||
#include <net/tcp.h>
|
||||
#include <net/udp.h>
|
||||
#include <net/filter.h>
|
||||
#include <drivers/e1000.h>
|
||||
#include <lib/string.h>
|
||||
@@ -247,6 +248,8 @@ void net_receive(const uint8_t* packet, uint16_t length) {
|
||||
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);
|
||||
} else if (ip->protocol == IP_PROTO_UDP) {
|
||||
udp_receive(payload + ihl, payload_len - ihl, ip->src_ip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+170
-18
@@ -1,24 +1,91 @@
|
||||
// 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
|
||||
static socket_t socket_table[MAX_SOCKETS];
|
||||
#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;
|
||||
socket_table[i].protocol = protocol ? protocol :
|
||||
(type == SOCK_DGRAM ? IPPROTO_UDP : IPPROTO_TCP);
|
||||
socket_table[i].state = 0;
|
||||
return i;
|
||||
}
|
||||
@@ -26,37 +93,122 @@ int sys_socket(int domain, int type, int protocol) {
|
||||
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 (sockfd < 0 || sockfd >= MAX_SOCKETS || !socket_table[sockfd].in_use || !addr) return -1;
|
||||
socket_table[sockfd].local_ip = addr->sin_addr;
|
||||
socket_table[sockfd].local_port = addr->sin_port;
|
||||
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 (sockfd < 0 || sockfd >= MAX_SOCKETS || !socket_table[sockfd].in_use || !addr) return -1;
|
||||
socket_table[sockfd].remote_ip = addr->sin_addr;
|
||||
socket_table[sockfd].remote_port = addr->sin_port;
|
||||
socket_table[sockfd].state = 1; // Connected
|
||||
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_send(int sockfd, const void* buf, size_t len, int flags) {
|
||||
(void)flags;
|
||||
if (sockfd < 0 || sockfd >= MAX_SOCKETS || !socket_table[sockfd].in_use || !buf) return -1;
|
||||
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_recv(int sockfd, void* buf, size_t len, int flags) {
|
||||
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 (sockfd < 0 || sockfd >= MAX_SOCKETS || !socket_table[sockfd].in_use || !buf) return -1;
|
||||
return 0;
|
||||
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 (sockfd < 0 || sockfd >= MAX_SOCKETS || !socket_table[sockfd].in_use) return -1;
|
||||
memset(&socket_table[sockfd], 0, sizeof(socket_t));
|
||||
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;
|
||||
}
|
||||
+209
-2
@@ -1,13 +1,220 @@
|
||||
// DHCP (Dynamic Host Configuration Protocol) client for SUB-OS.
|
||||
//
|
||||
// Performs the classic four-way handshake over UDP: broadcast DISCOVER, receive
|
||||
// OFFER, broadcast REQUEST, receive ACK. On ACK the offered IP address, subnet
|
||||
// mask, gateway and DNS server are applied to the primary interface. Each phase
|
||||
// is bounded by a timeout so an absent server degrades to the static fallback
|
||||
// configuration rather than stalling boot.
|
||||
#include <net/dhcp.h>
|
||||
#include <net/udp.h>
|
||||
#include <net/net.h>
|
||||
#include <mm/kmalloc.h>
|
||||
#include <lib/string.h>
|
||||
#include <lib/printf.h>
|
||||
#include <kernel/printk.h>
|
||||
#include <arch/arch.h>
|
||||
|
||||
#define DHCP_SERVER_PORT 67
|
||||
#define DHCP_CLIENT_PORT 68
|
||||
#define DHCP_MAGIC 0x63825363u
|
||||
#define DHCP_TIMEOUT_MS 2500
|
||||
|
||||
// BOOTP message ops and DHCP message types (option 53).
|
||||
#define BOOTREQUEST 1
|
||||
#define BOOTREPLY 2
|
||||
#define DHCPDISCOVER 1
|
||||
#define DHCPOFFER 2
|
||||
#define DHCPREQUEST 3
|
||||
#define DHCPACK 5
|
||||
#define DHCPNAK 6
|
||||
|
||||
typedef struct {
|
||||
uint8_t op;
|
||||
uint8_t htype;
|
||||
uint8_t hlen;
|
||||
uint8_t hops;
|
||||
uint32_t xid;
|
||||
uint16_t secs;
|
||||
uint16_t flags;
|
||||
uint32_t ciaddr;
|
||||
uint32_t yiaddr;
|
||||
uint32_t siaddr;
|
||||
uint32_t giaddr;
|
||||
uint8_t chaddr[16];
|
||||
uint8_t sname[64];
|
||||
uint8_t file[128];
|
||||
uint32_t cookie;
|
||||
uint8_t options[312];
|
||||
} __attribute__((packed)) dhcp_packet_t;
|
||||
|
||||
static dhcp_lease_t g_lease;
|
||||
|
||||
// Filled from UDP receive context on OFFER/ACK.
|
||||
static volatile bool reply_ready = false;
|
||||
static volatile uint8_t reply_type = 0;
|
||||
static volatile uint32_t reply_xid = 0;
|
||||
static volatile uint32_t off_yiaddr = 0;
|
||||
static volatile uint32_t off_mask = 0;
|
||||
static volatile uint32_t off_router = 0;
|
||||
static volatile uint32_t off_dns = 0;
|
||||
static volatile uint32_t off_server = 0;
|
||||
static volatile uint32_t off_lease = 0;
|
||||
|
||||
void dhcp_init(void) {
|
||||
memset(&g_lease, 0, sizeof(g_lease));
|
||||
reply_ready = false;
|
||||
printk(KERN_INFO "DHCP: Dynamic Host Configuration Protocol client ready\n");
|
||||
}
|
||||
|
||||
const dhcp_lease_t* dhcp_get_lease(void) { return &g_lease; }
|
||||
|
||||
// Append a TLV option; returns the new write offset.
|
||||
static int opt_put(uint8_t* opts, int o, uint8_t code, uint8_t len, const void* val) {
|
||||
opts[o++] = code;
|
||||
opts[o++] = len;
|
||||
if (len && val) { memcpy(&opts[o], val, len); o += len; }
|
||||
return o;
|
||||
}
|
||||
|
||||
static void dhcp_udp_cb(uint32_t src_ip, uint16_t src_port, uint16_t dst_port,
|
||||
const uint8_t* data, uint16_t len, void* ctx) {
|
||||
(void)src_ip; (void)src_port; (void)dst_port; (void)ctx;
|
||||
if (len < 240) return;
|
||||
const dhcp_packet_t* p = (const dhcp_packet_t*)data;
|
||||
if (p->op != BOOTREPLY) return;
|
||||
if (ntohl(p->cookie) != DHCP_MAGIC) return;
|
||||
|
||||
uint32_t mask = 0, router = 0, dns = 0, server = 0, lease = 0;
|
||||
uint8_t mtype = 0;
|
||||
|
||||
int optlen = len - (int)((const uint8_t*)p->options - (const uint8_t*)p);
|
||||
int i = 0;
|
||||
while (i < optlen) {
|
||||
uint8_t code = p->options[i++];
|
||||
if (code == 0xFF) break; // end
|
||||
if (code == 0x00) continue; // pad
|
||||
if (i >= optlen) break;
|
||||
uint8_t l = p->options[i++];
|
||||
if (i + l > optlen) break;
|
||||
const uint8_t* v = &p->options[i];
|
||||
switch (code) {
|
||||
case 53: mtype = v[0]; break;
|
||||
case 1: if (l >= 4) memcpy(&mask, v, 4); break;
|
||||
case 3: if (l >= 4) memcpy(&router, v, 4); break;
|
||||
case 6: if (l >= 4) memcpy(&dns, v, 4); break;
|
||||
case 54: if (l >= 4) memcpy(&server, v, 4); break;
|
||||
case 51: if (l >= 4) { uint32_t t; memcpy(&t, v, 4); lease = ntohl(t); } break;
|
||||
default: break;
|
||||
}
|
||||
i += l;
|
||||
}
|
||||
|
||||
reply_type = mtype;
|
||||
reply_xid = ntohl(p->xid);
|
||||
off_yiaddr = p->yiaddr;
|
||||
off_mask = mask;
|
||||
off_router = router;
|
||||
off_dns = dns;
|
||||
off_server = server;
|
||||
off_lease = lease;
|
||||
reply_ready = true;
|
||||
}
|
||||
|
||||
// Build a DISCOVER or REQUEST and broadcast it. requested_ip/server are set for
|
||||
// REQUEST (0 for DISCOVER).
|
||||
static void dhcp_send(uint8_t msgtype, uint32_t xid, const uint8_t* mac,
|
||||
uint32_t requested_ip, uint32_t server_id) {
|
||||
dhcp_packet_t* p = (dhcp_packet_t*)kzalloc(sizeof(dhcp_packet_t));
|
||||
if (!p) return;
|
||||
|
||||
p->op = BOOTREQUEST;
|
||||
p->htype = 1; // Ethernet
|
||||
p->hlen = 6;
|
||||
p->xid = htonl(xid);
|
||||
p->flags = htons(0x8000); // ask the server to broadcast its reply
|
||||
memcpy(p->chaddr, mac, 6);
|
||||
p->cookie = htonl(DHCP_MAGIC);
|
||||
|
||||
int o = 0;
|
||||
o = opt_put(p->options, o, 53, 1, &msgtype);
|
||||
if (msgtype == DHCPREQUEST) {
|
||||
if (requested_ip) o = opt_put(p->options, o, 50, 4, &requested_ip);
|
||||
if (server_id) o = opt_put(p->options, o, 54, 4, &server_id);
|
||||
}
|
||||
uint8_t plist[4] = {1, 3, 6, 15}; // mask, router, dns, domain
|
||||
o = opt_put(p->options, o, 55, sizeof(plist), plist);
|
||||
p->options[o++] = 0xFF; // end
|
||||
|
||||
int fixed = (int)((uint8_t*)p->options - (uint8_t*)p);
|
||||
udp_send_broadcast(DHCP_CLIENT_PORT, DHCP_SERVER_PORT, p, (uint16_t)(fixed + o));
|
||||
kfree(p);
|
||||
}
|
||||
|
||||
static bool wait_reply(uint32_t xid, uint8_t want_type) {
|
||||
uint64_t t0 = pit_get_ticks();
|
||||
uint64_t wait_ticks = DHCP_TIMEOUT_MS / 10;
|
||||
while (pit_get_ticks() - t0 < wait_ticks) {
|
||||
if (reply_ready && reply_xid == xid && reply_type == want_type) return true;
|
||||
arch_halt();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int dhcp_request_lease(void) {
|
||||
net_if_t* nif = net_get_primary_if();
|
||||
if (!nif) return -1;
|
||||
|
||||
printk(KERN_INFO "DHCP: Discovering network configuration via broadcast...\n");
|
||||
printk(KERN_INFO "DHCP: Lease granted: IP 10.0.2.15, Mask 255.255.255.0, GW 10.0.2.2, DNS 10.0.2.3\n");
|
||||
return 0;
|
||||
|
||||
if (udp_bind(DHCP_CLIENT_PORT, dhcp_udp_cb, NULL) != 0) {
|
||||
printk(ANSI_YELLOW "DHCP: client port 68 unavailable\n" ANSI_RESET);
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint32_t xid = (uint32_t)(0x53554200u ^ pit_get_ticks());
|
||||
int rc = -1;
|
||||
|
||||
// DISCOVER -> OFFER
|
||||
reply_ready = false;
|
||||
dhcp_send(DHCPDISCOVER, xid, nif->mac, 0, 0);
|
||||
if (wait_reply(xid, DHCPOFFER)) {
|
||||
uint32_t offered = off_yiaddr;
|
||||
uint32_t server = off_server;
|
||||
char ips[16]; ip_to_str(offered, ips);
|
||||
printk(KERN_INFO "DHCP: OFFER received: %s\n", ips);
|
||||
|
||||
// REQUEST -> ACK
|
||||
reply_ready = false;
|
||||
dhcp_send(DHCPREQUEST, xid, nif->mac, offered, server);
|
||||
if (wait_reply(xid, DHCPACK)) {
|
||||
nif->ip = off_yiaddr;
|
||||
if (off_mask) nif->subnet = off_mask;
|
||||
if (off_router) nif->gateway = off_router;
|
||||
if (off_dns) nif->dns = off_dns;
|
||||
|
||||
g_lease.ip = nif->ip;
|
||||
g_lease.mask = nif->subnet;
|
||||
g_lease.gateway = nif->gateway;
|
||||
g_lease.dns = nif->dns;
|
||||
g_lease.server = off_server;
|
||||
g_lease.lease_secs = off_lease ? off_lease : 86400;
|
||||
g_lease.bound = true;
|
||||
|
||||
char a[16], m[16], gw[16], d[16];
|
||||
ip_to_str(nif->ip, a); ip_to_str(nif->subnet, m);
|
||||
ip_to_str(nif->gateway, gw); ip_to_str(nif->dns, d);
|
||||
printk(ANSI_BRIGHT_GREEN
|
||||
"DHCP: Lease granted: IP %s, Mask %s, GW %s, DNS %s (lease %us)\n"
|
||||
ANSI_RESET, a, m, gw, d, g_lease.lease_secs);
|
||||
rc = 0;
|
||||
}
|
||||
}
|
||||
|
||||
udp_unbind(DHCP_CLIENT_PORT);
|
||||
|
||||
if (rc != 0) {
|
||||
char a[16]; ip_to_str(nif->ip, a);
|
||||
printk(ANSI_YELLOW "DHCP: no response; keeping static config %s\n" ANSI_RESET, a);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
+172
-6
@@ -1,17 +1,183 @@
|
||||
// DNS (Domain Name System) stub resolver for SUB-OS.
|
||||
//
|
||||
// Resolves A records by sending a real DNS query over UDP/53 to the interface's
|
||||
// configured nameserver and parsing the response. Results are cached (with TTL)
|
||||
// via the in-kernel DNS cache; the resolver first consults the cache, then a
|
||||
// small built-in hosts table, and only then hits the wire. Every network wait
|
||||
// is bounded so a missing or silent server degrades to a timeout, never a hang.
|
||||
#include <net/dns.h>
|
||||
#include <net/net.h>
|
||||
#include <net/udp.h>
|
||||
#include <net/dns_cache.h>
|
||||
#include <mm/kmalloc.h>
|
||||
#include <lib/string.h>
|
||||
#include <lib/printf.h>
|
||||
#include <kernel/printk.h>
|
||||
#include <arch/arch.h>
|
||||
|
||||
#define DNS_PORT 53
|
||||
#define DNS_TIMEOUT_MS 2000
|
||||
#define DNS_MAX_MSG 512
|
||||
|
||||
typedef struct {
|
||||
uint16_t id;
|
||||
uint16_t flags;
|
||||
uint16_t qdcount;
|
||||
uint16_t ancount;
|
||||
uint16_t nscount;
|
||||
uint16_t arcount;
|
||||
} __attribute__((packed)) dns_header_t;
|
||||
|
||||
// Filled from UDP receive context when a matching reply arrives.
|
||||
static volatile bool dns_reply_ready = false;
|
||||
static volatile uint16_t dns_reply_id = 0;
|
||||
static volatile uint32_t dns_reply_ip = 0;
|
||||
static volatile uint32_t dns_reply_ttl = 0;
|
||||
|
||||
void dns_init(void) {
|
||||
dns_reply_ready = false;
|
||||
printk(KERN_INFO "DNS: Domain Name System Resolver client ready (Nameserver: 10.0.2.3)\n");
|
||||
}
|
||||
|
||||
// Encode "www.example.com" as DNS labels: 3www7example3com0.
|
||||
static int dns_encode_name(const char* host, uint8_t* out, int max) {
|
||||
int o = 0;
|
||||
const char* seg = host;
|
||||
while (*seg) {
|
||||
const char* dot = seg;
|
||||
while (*dot && *dot != '.') dot++;
|
||||
int seglen = (int)(dot - seg);
|
||||
if (seglen <= 0 || seglen > 63 || o + seglen + 1 >= max) return -1;
|
||||
out[o++] = (uint8_t)seglen;
|
||||
for (int i = 0; i < seglen; i++) out[o++] = (uint8_t)seg[i];
|
||||
seg = (*dot == '.') ? dot + 1 : dot;
|
||||
}
|
||||
if (o + 1 >= max) return -1;
|
||||
out[o++] = 0; // root label
|
||||
return o;
|
||||
}
|
||||
|
||||
// Skip a (possibly compressed) name in the answer section, returning the offset
|
||||
// just past it. Compression pointers (0xC0) end the name in two bytes.
|
||||
static int dns_skip_name(const uint8_t* msg, int off, int len) {
|
||||
while (off < len) {
|
||||
uint8_t b = msg[off];
|
||||
if (b == 0) return off + 1;
|
||||
if ((b & 0xC0) == 0xC0) return off + 2;
|
||||
off += b + 1;
|
||||
}
|
||||
return off;
|
||||
}
|
||||
|
||||
static void dns_udp_cb(uint32_t src_ip, uint16_t src_port, uint16_t dst_port,
|
||||
const uint8_t* data, uint16_t len, void* ctx) {
|
||||
(void)src_ip; (void)src_port; (void)dst_port; (void)ctx;
|
||||
if (len < sizeof(dns_header_t)) return;
|
||||
|
||||
const dns_header_t* h = (const dns_header_t*)data;
|
||||
uint16_t id = ntohs(h->id);
|
||||
uint16_t ancount = ntohs(h->ancount);
|
||||
uint16_t qdcount = ntohs(h->qdcount);
|
||||
if (ancount == 0) { dns_reply_id = id; dns_reply_ready = true; return; }
|
||||
|
||||
int off = sizeof(dns_header_t);
|
||||
// Skip the echoed question(s): name + qtype(2) + qclass(2).
|
||||
for (uint16_t q = 0; q < qdcount; q++) {
|
||||
off = dns_skip_name(data, off, len);
|
||||
off += 4;
|
||||
}
|
||||
|
||||
for (uint16_t a = 0; a < ancount && off + 12 <= len; a++) {
|
||||
off = dns_skip_name(data, off, len);
|
||||
if (off + 10 > len) break;
|
||||
uint16_t type = ntohs(*(const uint16_t*)(data + off));
|
||||
uint32_t ttl = ntohl(*(const uint32_t*)(data + off + 4));
|
||||
uint16_t rdlen = ntohs(*(const uint16_t*)(data + off + 8));
|
||||
off += 10;
|
||||
if (off + rdlen > len) break;
|
||||
if (type == 1 && rdlen == 4) { // A record
|
||||
uint32_t ip;
|
||||
memcpy(&ip, data + off, 4); // already network byte order == our storage
|
||||
dns_reply_ip = ip;
|
||||
dns_reply_ttl = ttl ? ttl : 300;
|
||||
dns_reply_id = id;
|
||||
dns_reply_ready = true;
|
||||
return;
|
||||
}
|
||||
off += rdlen;
|
||||
}
|
||||
dns_reply_id = id;
|
||||
dns_reply_ready = true; // answered, but no A record found
|
||||
}
|
||||
|
||||
// Built-in hosts fallback for when the wire query yields nothing.
|
||||
static uint32_t dns_static_host(const char* host) {
|
||||
if (strcmp(host, "localhost") == 0) return ip_parse("127.0.0.1");
|
||||
if (strcmp(host, "gateway") == 0) return ip_parse("10.0.2.2");
|
||||
if (strcmp(host, "dns.google") == 0) return 0x08080808;
|
||||
if (strcmp(host, "google.com") == 0) return ip_parse("142.250.190.46");
|
||||
if (strcmp(host, "github.com") == 0) return ip_parse("140.82.121.4");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t dns_resolve(const char* hostname) {
|
||||
if (!hostname) return 0;
|
||||
if (strcmp(hostname, "localhost") == 0) return ip_parse("127.0.0.1");
|
||||
if (strcmp(hostname, "gateway") == 0) return ip_parse("10.0.2.2");
|
||||
if (strcmp(hostname, "google.com") == 0) return ip_parse("142.250.190.46");
|
||||
if (strcmp(hostname, "github.com") == 0) return ip_parse("140.82.121.4");
|
||||
return ip_parse("10.0.2.15");
|
||||
if (!hostname || !*hostname) return 0;
|
||||
|
||||
// Dotted-quad literal: no lookup needed.
|
||||
{
|
||||
bool numeric = true;
|
||||
for (const char* p = hostname; *p; p++) {
|
||||
if (!((*p >= '0' && *p <= '9') || *p == '.')) { numeric = false; break; }
|
||||
}
|
||||
if (numeric) return ip_parse(hostname);
|
||||
}
|
||||
|
||||
// 1) Cache.
|
||||
uint32_t cached;
|
||||
if (dns_cache_lookup(hostname, &cached)) return cached;
|
||||
|
||||
// 2) Wire query.
|
||||
net_if_t* nif = net_get_primary_if();
|
||||
uint32_t server = (nif && nif->dns) ? nif->dns : ip_parse("10.0.2.3");
|
||||
uint16_t sport = udp_alloc_ephemeral_port();
|
||||
if (sport && udp_bind(sport, dns_udp_cb, NULL) == 0) {
|
||||
uint8_t query[DNS_MAX_MSG];
|
||||
dns_header_t* h = (dns_header_t*)query;
|
||||
uint16_t qid = (uint16_t)(0x5300u ^ (pit_get_ticks() & 0xFF));
|
||||
h->id = htons(qid);
|
||||
h->flags = htons(0x0100); // standard query, recursion desired
|
||||
h->qdcount = htons(1);
|
||||
h->ancount = 0; h->nscount = 0; h->arcount = 0;
|
||||
|
||||
int off = sizeof(dns_header_t);
|
||||
int nl = dns_encode_name(hostname, query + off, DNS_MAX_MSG - off - 4);
|
||||
if (nl > 0) {
|
||||
off += nl;
|
||||
*(uint16_t*)(query + off) = htons(1); off += 2; // QTYPE A
|
||||
*(uint16_t*)(query + off) = htons(1); off += 2; // QCLASS IN
|
||||
|
||||
dns_reply_ready = false;
|
||||
dns_reply_ip = 0;
|
||||
uint64_t t0 = pit_get_ticks();
|
||||
udp_send(server, sport, DNS_PORT, query, (uint16_t)off);
|
||||
|
||||
uint64_t wait_ticks = DNS_TIMEOUT_MS / 10;
|
||||
while (!dns_reply_ready && (pit_get_ticks() - t0 < wait_ticks)) {
|
||||
arch_halt();
|
||||
}
|
||||
|
||||
if (dns_reply_ready && dns_reply_id == qid && dns_reply_ip != 0) {
|
||||
uint32_t ip = dns_reply_ip;
|
||||
udp_unbind(sport);
|
||||
dns_cache_insert(hostname, ip, dns_reply_ttl);
|
||||
return ip;
|
||||
}
|
||||
}
|
||||
udp_unbind(sport);
|
||||
}
|
||||
|
||||
// 3) Static hosts fallback.
|
||||
uint32_t st = dns_static_host(hostname);
|
||||
if (st) { dns_cache_insert(hostname, st, 300); return st; }
|
||||
return 0;
|
||||
}
|
||||
+15
-12
@@ -1,5 +1,11 @@
|
||||
// DNS Resolver Cache with TTL Management and Metrics for SUB-OS
|
||||
// DNS Resolver Cache with TTL Management and Metrics for SUB-OS.
|
||||
//
|
||||
// IPs are stored in the same convention as the rest of the network stack:
|
||||
// network byte order in memory (the first octet in the lowest byte), exactly as
|
||||
// produced by ip_parse() and consumed by ip_to_str(). Storing them any other
|
||||
// way would hand dns_resolve() callers a byte-swapped address.
|
||||
#include <net/dns_cache.h>
|
||||
#include <net/net.h>
|
||||
#include <lib/string.h>
|
||||
#include <lib/printf.h>
|
||||
#include <kernel/printk.h>
|
||||
@@ -10,12 +16,12 @@ static uint64_t total_hits = 0;
|
||||
|
||||
void dns_cache_init(void) {
|
||||
memset(dns_table, 0, sizeof(dns_table));
|
||||
// Seed common local and well-known hosts
|
||||
dns_cache_insert("localhost", 0x7F000001, 86400); // 127.0.0.1
|
||||
dns_cache_insert("gateway.local", 0x0A000202, 3600); // 10.0.2.2
|
||||
dns_cache_insert("dns.google", 0x08080808, 7200); // 8.8.8.8
|
||||
dns_cache_insert("one.one.one.one", 0x01010101, 7200); // 1.1.1.1
|
||||
dns_cache_insert("github.com", 0x8C527964, 300); // 140.82.121.4
|
||||
// Seed common local and well-known hosts (network byte order via ip_parse).
|
||||
dns_cache_insert("localhost", ip_parse("127.0.0.1"), 86400);
|
||||
dns_cache_insert("gateway.local", ip_parse("10.0.2.2"), 3600);
|
||||
dns_cache_insert("dns.google", ip_parse("8.8.8.8"), 7200);
|
||||
dns_cache_insert("one.one.one.one", ip_parse("1.1.1.1"), 7200);
|
||||
dns_cache_insert("github.com", ip_parse("140.82.121.4"), 300);
|
||||
|
||||
printk(KERN_INFO "DNS: High-performance DNS Cache table initialized (Capacity: %d)\n", DNS_CACHE_MAX_ENTRIES);
|
||||
}
|
||||
@@ -63,7 +69,7 @@ void dns_cache_insert(const char* hostname, uint32_t ip, uint32_t ttl) {
|
||||
|
||||
void dns_cache_flush(void) {
|
||||
memset(dns_table, 0, sizeof(dns_table));
|
||||
dns_cache_insert("localhost", 0x7F000001, 86400);
|
||||
dns_cache_insert("localhost", ip_parse("127.0.0.1"), 86400);
|
||||
printk(ANSI_YELLOW "DNS: Resolver cache flushed.\n" ANSI_RESET);
|
||||
}
|
||||
|
||||
@@ -78,11 +84,8 @@ void dns_cache_dump(void) {
|
||||
|
||||
for (int i = 0; i < DNS_CACHE_MAX_ENTRIES; i++) {
|
||||
if (dns_table[i].active) {
|
||||
uint32_t ip = dns_table[i].ip;
|
||||
char ip_str[20];
|
||||
snprintf(ip_str, sizeof(ip_str), "%u.%u.%u.%u",
|
||||
(ip >> 24) & 0xFF, (ip >> 16) & 0xFF,
|
||||
(ip >> 8) & 0xFF, ip & 0xFF);
|
||||
ip_to_str(dns_table[i].ip, ip_str);
|
||||
|
||||
printk("%-28s " ANSI_BRIGHT_YELLOW "%-16s" ANSI_RESET " %8u %8llu\n",
|
||||
dns_table[i].hostname, ip_str,
|
||||
|
||||
+209
-9
@@ -1,26 +1,226 @@
|
||||
// UDP (User Datagram Protocol) transport for SUB-OS.
|
||||
//
|
||||
// Provides connectionless datagram delivery on top of the IPv4 layer. Inbound
|
||||
// datagrams are demultiplexed by destination port to a small table of bound
|
||||
// handlers (used by the socket layer, the DNS resolver and the DHCP client);
|
||||
// unclaimed ports are counted as drops. Outbound datagrams are checksummed with
|
||||
// the standard IPv4 pseudo-header.
|
||||
#include <net/udp.h>
|
||||
#include <mm/kmalloc.h>
|
||||
#include <lib/string.h>
|
||||
#include <lib/printf.h>
|
||||
#include <kernel/printk.h>
|
||||
|
||||
#define UDP_MAX_BINDINGS 32
|
||||
#define UDP_EPHEMERAL_LO 49152u
|
||||
#define UDP_EPHEMERAL_HI 65535u
|
||||
|
||||
typedef struct {
|
||||
uint16_t port;
|
||||
udp_handler_t handler;
|
||||
void* ctx;
|
||||
bool active;
|
||||
} udp_binding_t;
|
||||
|
||||
// Pseudo-header used only for checksum computation (never transmitted).
|
||||
typedef struct {
|
||||
uint32_t src_ip;
|
||||
uint32_t dst_ip;
|
||||
uint8_t zero;
|
||||
uint8_t protocol;
|
||||
uint16_t udp_len;
|
||||
} __attribute__((packed)) udp_pseudo_header_t;
|
||||
|
||||
static udp_binding_t bindings[UDP_MAX_BINDINGS];
|
||||
static uint16_t next_ephemeral = UDP_EPHEMERAL_LO;
|
||||
|
||||
static volatile uint64_t stat_rx = 0;
|
||||
static volatile uint64_t stat_tx = 0;
|
||||
static volatile uint64_t stat_drop = 0;
|
||||
|
||||
static uint16_t udp_checksum(uint32_t src_ip, uint32_t dst_ip,
|
||||
const udp_header_t* udp, const void* payload,
|
||||
uint16_t payload_len) {
|
||||
udp_pseudo_header_t ph;
|
||||
ph.src_ip = src_ip;
|
||||
ph.dst_ip = dst_ip;
|
||||
ph.zero = 0;
|
||||
ph.protocol = IP_PROTO_UDP;
|
||||
ph.udp_len = htons((uint16_t)(sizeof(udp_header_t) + payload_len));
|
||||
|
||||
uint32_t sum = 0;
|
||||
const uint16_t* p = (const uint16_t*)&ph;
|
||||
for (size_t i = 0; i < sizeof(ph) / 2; i++) sum += *p++;
|
||||
|
||||
p = (const uint16_t*)udp;
|
||||
for (size_t i = 0; i < sizeof(udp_header_t) / 2; i++) sum += *p++;
|
||||
|
||||
if (payload && payload_len > 0) {
|
||||
p = (const uint16_t*)payload;
|
||||
size_t len = payload_len;
|
||||
while (len > 1) { sum += *p++; len -= 2; }
|
||||
if (len > 0) sum += *(const uint8_t*)p;
|
||||
}
|
||||
|
||||
while (sum >> 16) sum = (sum & 0xFFFF) + (sum >> 16);
|
||||
uint16_t cksum = (uint16_t)(~sum);
|
||||
// A zero checksum means "not computed" on the wire; send 0xFFFF instead.
|
||||
return cksum ? cksum : 0xFFFF;
|
||||
}
|
||||
|
||||
void udp_init(void) {
|
||||
memset(bindings, 0, sizeof(bindings));
|
||||
next_ephemeral = UDP_EPHEMERAL_LO;
|
||||
stat_rx = stat_tx = stat_drop = 0;
|
||||
printk(KERN_INFO "NET: UDP (User Datagram Protocol) transport layer online\n");
|
||||
}
|
||||
|
||||
void udp_receive(const uint8_t* packet, uint16_t length, uint32_t src_ip) {
|
||||
(void)packet; (void)length; (void)src_ip;
|
||||
int udp_bind(uint16_t port, udp_handler_t handler, void* ctx) {
|
||||
if (!handler || port == 0) return -1;
|
||||
for (int i = 0; i < UDP_MAX_BINDINGS; i++) {
|
||||
if (bindings[i].active && bindings[i].port == port) return -1;
|
||||
}
|
||||
for (int i = 0; i < UDP_MAX_BINDINGS; i++) {
|
||||
if (!bindings[i].active) {
|
||||
bindings[i].port = port;
|
||||
bindings[i].handler = handler;
|
||||
bindings[i].ctx = ctx;
|
||||
bindings[i].active = true;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void udp_send(uint32_t dst_ip, uint16_t src_port, uint16_t dst_port, const void* data, uint16_t len) {
|
||||
uint8_t buffer[1500];
|
||||
udp_header_t* udp = (udp_header_t*)buffer;
|
||||
void udp_unbind(uint16_t port) {
|
||||
for (int i = 0; i < UDP_MAX_BINDINGS; i++) {
|
||||
if (bindings[i].active && bindings[i].port == port) {
|
||||
bindings[i].active = false;
|
||||
bindings[i].handler = NULL;
|
||||
bindings[i].ctx = NULL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t udp_alloc_ephemeral_port(void) {
|
||||
for (uint32_t tries = 0; tries <= (UDP_EPHEMERAL_HI - UDP_EPHEMERAL_LO); tries++) {
|
||||
uint16_t candidate = next_ephemeral;
|
||||
if (next_ephemeral >= UDP_EPHEMERAL_HI) next_ephemeral = UDP_EPHEMERAL_LO;
|
||||
else next_ephemeral++;
|
||||
|
||||
bool taken = false;
|
||||
for (int i = 0; i < UDP_MAX_BINDINGS; i++) {
|
||||
if (bindings[i].active && bindings[i].port == candidate) { taken = true; break; }
|
||||
}
|
||||
if (!taken) return candidate;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void udp_receive(const uint8_t* packet, uint16_t length, uint32_t src_ip) {
|
||||
if (!packet || length < sizeof(udp_header_t)) { stat_drop++; return; }
|
||||
|
||||
const udp_header_t* udp = (const udp_header_t*)packet;
|
||||
uint16_t dst_port = ntohs(udp->dst_port);
|
||||
uint16_t src_port = ntohs(udp->src_port);
|
||||
uint16_t udp_len = ntohs(udp->length);
|
||||
|
||||
if (udp_len < sizeof(udp_header_t) || udp_len > length) { stat_drop++; return; }
|
||||
|
||||
const uint8_t* data = packet + sizeof(udp_header_t);
|
||||
uint16_t data_len = udp_len - sizeof(udp_header_t);
|
||||
|
||||
stat_rx++;
|
||||
|
||||
for (int i = 0; i < UDP_MAX_BINDINGS; i++) {
|
||||
if (bindings[i].active && bindings[i].port == dst_port && bindings[i].handler) {
|
||||
bindings[i].handler(src_ip, src_port, dst_port, data, data_len, bindings[i].ctx);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// No listener on this port.
|
||||
stat_drop++;
|
||||
}
|
||||
|
||||
void udp_send(uint32_t dst_ip, uint16_t src_port, uint16_t dst_port,
|
||||
const void* data, uint16_t len) {
|
||||
uint16_t total = (uint16_t)(sizeof(udp_header_t) + len);
|
||||
uint8_t* buf = (uint8_t*)kzalloc(total);
|
||||
if (!buf) return;
|
||||
|
||||
udp_header_t* udp = (udp_header_t*)buf;
|
||||
udp->src_port = htons(src_port);
|
||||
udp->dst_port = htons(dst_port);
|
||||
udp->length = htons((uint16_t)(sizeof(udp_header_t) + len));
|
||||
udp->length = htons(total);
|
||||
udp->checksum = 0;
|
||||
|
||||
if (data && len > 0) {
|
||||
memcpy(buffer + sizeof(udp_header_t), data, len);
|
||||
if (data && len > 0) memcpy(buf + sizeof(udp_header_t), data, len);
|
||||
|
||||
net_if_t* nif = net_get_primary_if();
|
||||
uint32_t src_ip = nif ? nif->ip : 0;
|
||||
udp->checksum = udp_checksum(src_ip, dst_ip, udp, data, len);
|
||||
|
||||
net_send_ip(dst_ip, IP_PROTO_UDP, buf, total);
|
||||
stat_tx++;
|
||||
kfree(buf);
|
||||
}
|
||||
|
||||
void udp_send_broadcast(uint16_t src_port, uint16_t dst_port,
|
||||
const void* data, uint16_t len) {
|
||||
// Build IPv4 + UDP by hand: src 0.0.0.0, dst 255.255.255.255, sent to the
|
||||
// Ethernet broadcast address without ARP (the interface has no IP yet).
|
||||
static const uint8_t bcast_mac[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
||||
uint16_t udp_total = (uint16_t)(sizeof(udp_header_t) + len);
|
||||
uint16_t ip_total = (uint16_t)(sizeof(ip_header_t) + udp_total);
|
||||
|
||||
uint8_t* buf = (uint8_t*)kzalloc(ip_total);
|
||||
if (!buf) return;
|
||||
|
||||
ip_header_t* ip = (ip_header_t*)buf;
|
||||
ip->ihl_version = 0x45;
|
||||
ip->tos = 0;
|
||||
ip->total_length = htons(ip_total);
|
||||
ip->id = htons(0x5342); // 'SB'
|
||||
ip->flags_fragment = 0;
|
||||
ip->ttl = 64;
|
||||
ip->protocol = IP_PROTO_UDP;
|
||||
ip->checksum = 0;
|
||||
ip->src_ip = 0x00000000;
|
||||
ip->dst_ip = 0xFFFFFFFF;
|
||||
|
||||
// IPv4 header checksum.
|
||||
{
|
||||
uint32_t sum = 0;
|
||||
const uint16_t* p = (const uint16_t*)ip;
|
||||
for (size_t i = 0; i < sizeof(ip_header_t) / 2; i++) sum += *p++;
|
||||
while (sum >> 16) sum = (sum & 0xFFFF) + (sum >> 16);
|
||||
ip->checksum = (uint16_t)(~sum);
|
||||
}
|
||||
|
||||
net_send_ip(dst_ip, IP_PROTO_UDP, buffer, (uint16_t)(sizeof(udp_header_t) + len));
|
||||
udp_header_t* udp = (udp_header_t*)(buf + sizeof(ip_header_t));
|
||||
udp->src_port = htons(src_port);
|
||||
udp->dst_port = htons(dst_port);
|
||||
udp->length = htons(udp_total);
|
||||
udp->checksum = 0;
|
||||
if (data && len > 0) memcpy((uint8_t*)udp + sizeof(udp_header_t), data, len);
|
||||
udp->checksum = udp_checksum(0x00000000, 0xFFFFFFFF, udp, data, len);
|
||||
|
||||
net_send_eth(bcast_mac, ETHERTYPE_IPV4, buf, ip_total);
|
||||
stat_tx++;
|
||||
kfree(buf);
|
||||
}
|
||||
|
||||
void udp_get_stats(uint64_t* rx, uint64_t* tx, uint64_t* drop) {
|
||||
if (rx) *rx = stat_rx;
|
||||
if (tx) *tx = stat_tx;
|
||||
if (drop) *drop = stat_drop;
|
||||
}
|
||||
|
||||
int udp_get_bindings(uint16_t* ports_out, int max) {
|
||||
int n = 0;
|
||||
for (int i = 0; i < UDP_MAX_BINDINGS && n < max; i++) {
|
||||
if (bindings[i].active) ports_out[n++] = bindings[i].port;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
Reference in new issue
Block a user