diff --git a/README.md b/README.md index afaa60b9..9c7d671f 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,8 @@ The Raspberry Pi emulation has the following limitations: ## Networking -The system has basic networking support. Currently, it performs DHCP discovery and request to receive an IP address on the local network, is capable of responding to Ping and ARP, and can connect to a server running on ports 8080 and 80, though it currently does nothing noteworthy. +The system provides medium level networking support. At startup, it scans for available network cards, loads the matching driver if present and creates a logical L2 interface for each device; a loopback interface is also added for both IPv4 and IPv6, though the current loopback implementation is partial and rejects all localhost packets. IPv4 networking is fully implemented, while IPv6 support is limited to interface structures and it cannot perform real communication. +Once interfaces and dispatcher are initialized, the system starts ARP, DHCP, DNS and SNTP processes. At the moment, SNTP sets the unix time and the time zone is configured manually since DHCP options 100 and 101 are not always included. +The stack supports UDP and TCP sockets, and an HTTP socket is implemented on top of TCP. ICMP is also supported with the system able to respond to ping requests. DNS resolution can be called directly by applications and is also used by sockets. +An embedded HTTP server is included and listens on port 80, serving a minimal page. Also discovery mechanism is available: the system probes the local network via UDP broadcast on port 8080, and when a responder replies it connects via HTTP on port 80, though it currently does nothing noteworthy. An implementation of the server can be found at the [RedactedOS Firmware Server Repository](https://github.com/differrari/RedactedOS_firmware_server/tree/main) - diff --git a/common.mk b/common.mk index 89305fe7..1a4097ef 100644 --- a/common.mk +++ b/common.mk @@ -10,7 +10,8 @@ BUILD_DIR := ./build COMMON_FLAGS ?= -ffreestanding -nostdlib -fno-exceptions -fno-unwind-tables \ -fno-asynchronous-unwind-tables -g -O0 -Wall -Wextra \ -Wno-unused-parameter -Wno-address-of-packed-member \ - -mcpu=cortex-a72 -Werror -Wno-error=sized-deallocation + -mcpu=cortex-a72 -Werror -Wno-error=sized-deallocation \ + -Wno-unused-function CFLAGS_BASE ?= $(COMMON_FLAGS) -std=c99 CXXFLAGS_BASE ?= $(COMMON_FLAGS) -fno-rtti diff --git a/kernel/exceptions/irq.c b/kernel/exceptions/irq.c index 7936e841..591067ca 100644 --- a/kernel/exceptions/irq.c +++ b/kernel/exceptions/irq.c @@ -9,6 +9,7 @@ #include "networking/network.h" #include "hw/hw.h" #include "audio/audio.h" +#include "networking/interface_manager.h" #include "process/syscall.h" #define IRQ_TIMER 30 @@ -46,8 +47,11 @@ void irq_init() { gic_enable_irq(IRQ_TIMER, 0x80, 0); gic_enable_irq(MSI_OFFSET + INPUT_IRQ, 0x80, 0); - gic_enable_irq(MSI_OFFSET + NET_IRQ, 0x80, 0); - gic_enable_irq(MSI_OFFSET + NET_IRQ + 1, 0x80, 0); + + for (uint32_t i = 0; i < (uint32_t)MAX_L2_INTERFACES; ++i) { + gic_enable_irq(MSI_OFFSET + NET_IRQ_BASE + (2*i), 0x80, 0); + gic_enable_irq(MSI_OFFSET + NET_IRQ_BASE + (2*i+1), 0x80, 0); + } gic_enable_irq(SLEEP_TIMER, 0x80, 0); if (RPI_BOARD != 3){ @@ -92,12 +96,12 @@ void irq_el1_handler() { wake_processes(); if (RPI_BOARD != 3) write32(GICC_BASE + 0x10, irq); process_restore(); - } else if (irq == MSI_OFFSET + NET_IRQ){ - network_handle_download_interrupt(); - if (RPI_BOARD != 3) write32(GICC_BASE + 0x10, irq); - process_restore(); - } else if (irq == MSI_OFFSET + NET_IRQ + 1){ - network_handle_upload_interrupt(); + } else if (irq >= MSI_OFFSET + NET_IRQ_BASE && irq < MSI_OFFSET + NET_IRQ_BASE + (2*MAX_L2_INTERFACES)){ + uint32_t rel = irq - (MSI_OFFSET + NET_IRQ_BASE); + uint16_t nic_id = (uint16_t)(rel >> 1); + uint8_t is_rx = (rel & 1) == 0; + if (is_rx) network_handle_download_interrupt_nic(nic_id); + else network_handle_upload_interrupt_nic(nic_id); if (RPI_BOARD != 3) write32(GICC_BASE + 0x10, irq); process_restore(); } else { diff --git a/kernel/kernel.c b/kernel/kernel.c index 78e53f60..9394bb2d 100644 --- a/kernel/kernel.c +++ b/kernel/kernel.c @@ -55,11 +55,10 @@ void kernel_main() { bool input_available = load_module(&input_module); bool network_available = false; - if (BOARD_TYPE == 1){ //TODO: disabling networking until it is refactored to prevent memory issues - network_available = false;// load_module(&net_module); - + network_available = load_module(&net_module); + load_module(&audio_module); init_audio_mixer(); diff --git a/kernel/networking/drivers/net_bus.cpp b/kernel/networking/drivers/net_bus.cpp new file mode 100644 index 00000000..60666b9b --- /dev/null +++ b/kernel/networking/drivers/net_bus.cpp @@ -0,0 +1,219 @@ +#include "net_bus.hpp" +#include "net_driver.hpp" +#include "virtio_net_pci/virtio_net_pci.hpp" +#include "pci.h" +#include "std/std.h" +#include "console/kio.h" +#include "types.h" +#include "networking/interface_manager.h" +#include "networking/network.h" + +#define kprintfv(fmt, ...) ({ if (verbose){ kprintf(fmt, ##__VA_ARGS__); } }) + +typedef struct { + NetDriver* drv; + char ifname[16]; + char hw_ifname[32]; + uint8_t mac[6]; + uint16_t mtu; + uint16_t header_size; + uint8_t kind; + uint32_t speed_mbps; + uint8_t duplex; +} net_nic_desc_t; + +static net_nic_desc_t g_nics[MAX_L2_INTERFACES]; +static size_t g_count = 0; +static int g_eth_next = 0; +static int g_wif_next = 0; +static int g_net_next = 0; +static bool g_lo_added = false; +static bool verbose = true; + +static void memzero(void* p, size_t n){ + memset(p,0,n); +} + +static size_t u32_to_dec(char* dst, size_t cap, unsigned v){ + char tmp[16]; + int n=0; + if (cap==0) return 0; + if (v==0){ if (cap>1){ dst[0]='0'; dst[1]=0; return 1; } dst[0]=0; return 0; } + while (v>0 && n<16){ tmp[n++] = (char)('0' + (v%10)); v/=10; } + size_t i=0; + while (i0){ dst[i++] = tmp[--n]; } + dst[i]=0; + return i; +} + +static void make_ifname(char* dst, size_t cap, const char* prefix){ + if (!dst || cap==0) return; + int idx=0; + if (prefix && prefix[0]=='e'){ idx = g_eth_next++; } + else if (prefix && prefix[0]=='w'){ idx = g_wif_next++; } + else { idx = g_net_next++; } + size_t j=0; + if (prefix){ j = strncpy(dst, cap, prefix); } else { dst[0]='n'; dst[1]='i'; dst[2]='c'; dst[3]=0; j=3; } + if (j= MAX_L2_INTERFACES) return; + net_nic_desc_t* d = &g_nics[g_count++]; + d->drv = nullptr; + memzero(d->ifname,sizeof(d->ifname)); + memzero(d->hw_ifname,sizeof(d->hw_ifname)); + d->ifname[0]='l'; d->ifname[1]='o'; d->ifname[2]='0'; d->ifname[3]=0; + d->hw_ifname[0]='l'; d->hw_ifname[1]='o'; d->hw_ifname[2]='o'; d->hw_ifname[3]='p'; d->hw_ifname[4]='b'; d->hw_ifname[5]='a'; d->hw_ifname[6]='c'; d->hw_ifname[7]='k'; d->hw_ifname[8]=0; + memzero(d->mac,6); + d->mtu = 65535; + d->header_size = 0; + d->kind = NET_IFK_LOCALHOST; + d->speed_mbps = 0xFFFFFFFFu; + d->duplex = NET_IFK_LOCALHOST; + g_lo_added = true; + kprintfv("[net-bus] added loopback ifname=%s",d->ifname); +} + +static bool is_virtio_net(uint16_t vendor, uint16_t device, uint8_t class_code, uint8_t subclass){ + if (vendor != 0x1AF4) return false; + if (class_code == 0x02) return true; + if (device == 0x1000) return true; + return false; +} + +int net_bus_init(){ + g_count = 0; + g_eth_next = 0; + g_wif_next = 0; + g_net_next = 0; + g_lo_added = false; + + kprintfv("[net-bus] init"); + + pci_device_info infos[64]; + size_t n = pci_enumerate(infos, 64); + kprintfv("[net-bus] pci_enumerate=%u",(unsigned)n); + + int nic_ord = 0; + + for (size_t i=0;i= MAX_L2_INTERFACES){ kprintfv("[net-bus] cap reached"); break; } + + const uint16_t ven = infos[i].vendor; + const uint16_t dev = infos[i].device; + const uint8_t cls = infos[i].class_code; + const uint8_t sub = infos[i].subclass; + + if (cls != 0x02) continue; + + kprintfv("[net-bus] net dev %u ven=%x dev=%x class=%x sub=%x prog=%x addr=%x", + (unsigned)i, ven, dev, cls, sub, infos[i].prog_if, (uintptr_t)infos[i].addr); + + const char* if_prefix = "net"; + uint8_t kind = NET_IFK_OTHER; + if (sub == 0x00){ if_prefix = "eth"; kind = NET_IFK_ETH; } + + bool matched = false; + + if (is_virtio_net(ven, dev, cls, sub)){ + VirtioNetDriver* d = new VirtioNetDriver(); + if (!d){ kprintf("[net-bus][warn] virtio alloc failed"); continue; } + + uint32_t irq_base = NET_IRQ_BASE + (uint32_t)(2*nic_ord); + if (!d->init_at(infos[i].addr, irq_base)){ + kprintf("[net-bus][warn] virtio init_at failed"); + delete d; + continue; + } + + net_nic_desc_t* e = &g_nics[g_count++]; + e->drv = d; + + d->get_mac(e->mac); + e->mtu = d->get_mtu(); + e->header_size = d->get_header_size(); + e->kind = kind; + e->speed_mbps = d->get_speed_mbps(); + e->duplex = d->get_duplex(); + + make_ifname(e->ifname, sizeof(e->ifname), if_prefix); + + const char* hw = d->hw_ifname(); + strncpy(e->hw_ifname, sizeof(e->hw_ifname), (hw && hw[0]) ? hw : "vnet"); + + kprintfv("[net-bus] added if=%s mac=%x:%x:%x:%x:%x:%x mtu=%u hdr=%u hw=%s irq_base=%u spd=%u dup=%u", + e->ifname, e->mac[0],e->mac[1],e->mac[2],e->mac[3],e->mac[4],e->mac[5], + e->mtu, e->header_size, e->hw_ifname, (unsigned)irq_base, + (unsigned)e->speed_mbps, (unsigned)e->duplex); + + nic_ord += 1; + matched = true; + } + //else if future drivers + + if (!matched){ + kprintf("[net-bus][warn] no driver for ven=%x dev=%x class=%x sub=%x", ven, dev, cls, sub); + } + } + + if (!g_lo_added && g_count < MAX_L2_INTERFACES) add_loopback(); + + kprintfv("[net-bus] total_if=%u",(unsigned)g_count); + return (int)g_count; +} + + +void net_bus_enable_verbose(){ + verbose = true; +} + +int net_bus_count(){ + return (int)g_count; +} + +NetDriver* net_bus_driver(int idx){ + if (idx < 0 || (size_t)idx >= g_count) return nullptr; + return g_nics[idx].drv; +} + +const char* net_bus_ifname(int idx){ + if (idx < 0 || (size_t)idx >= g_count) return nullptr; + return g_nics[idx].ifname; +} + +const char* net_bus_hw_ifname(int idx){ + if (idx < 0 || (size_t)idx >= g_count) return nullptr; + return g_nics[idx].hw_ifname; +} + +void net_bus_get_mac(int idx, uint8_t out_mac[6]){ + if (idx < 0 || (size_t)idx >= g_count) { memzero(out_mac,6); return; } + memcpy(out_mac, g_nics[idx].mac, 6); +} + +uint16_t net_bus_get_mtu(int idx){ + if (idx < 0 || (size_t)idx >= g_count) return 0; + return g_nics[idx].mtu; +} + +uint16_t net_bus_get_header_size(int idx){ + if (idx < 0 || (size_t)idx >= g_count) return 0; + return g_nics[idx].header_size; +} + +uint8_t net_bus_get_kind(int idx){ + if (idx < 0 || (size_t)idx >= g_count) return NET_IFK_UNKNOWN; + return g_nics[idx].kind; +} + +uint32_t net_bus_get_speed_mbps(int idx){ + if (idx < 0 || (size_t)idx >= g_count) return 0xFFFFFFFFu; + return g_nics[idx].speed_mbps; +} + +uint8_t net_bus_get_duplex(int idx){ + if (idx < 0 || (size_t)idx >= g_count) return 0xFFu; + return g_nics[idx].duplex; +} diff --git a/kernel/networking/drivers/net_bus.hpp b/kernel/networking/drivers/net_bus.hpp new file mode 100644 index 00000000..4d27bfe2 --- /dev/null +++ b/kernel/networking/drivers/net_bus.hpp @@ -0,0 +1,22 @@ +#pragma once +#include "net_driver.hpp" + +enum NetIfKind : uint8_t { + NET_IFK_ETH = 0x00, + NET_IFK_WIFI = 0x01, + NET_IFK_OTHER = 0x02, + NET_IFK_LOCALHOST = 0xFE, + NET_IFK_UNKNOWN = 0xFF +}; + +int net_bus_init(); +int net_bus_count(); +NetDriver* net_bus_driver(int idx); +const char* net_bus_ifname(int idx); +const char* net_bus_hw_ifname(int idx); +void net_bus_get_mac(int idx, uint8_t out_mac[6]); +uint16_t net_bus_get_mtu(int idx); +uint16_t net_bus_get_header_size(int idx); +uint8_t net_bus_get_kind(int idx); +uint32_t net_bus_get_speed_mbps(int idx); +uint8_t net_bus_get_duplex(int idx); \ No newline at end of file diff --git a/kernel/networking/drivers/net_driver.hpp b/kernel/networking/drivers/net_driver.hpp index df69b3d0..0b62a375 100644 --- a/kernel/networking/drivers/net_driver.hpp +++ b/kernel/networking/drivers/net_driver.hpp @@ -1,21 +1,21 @@ #pragma once - #include "types.h" #include "net/network_types.h" class NetDriver { public: NetDriver() = default; - virtual bool init() = 0; - + virtual ~NetDriver() = default; + virtual bool init_at(uint64_t pci_addr, uint32_t irq_base_vector) = 0; virtual sizedptr allocate_packet(size_t size) = 0; - virtual sizedptr handle_receive_packet(void* buffer) = 0; + virtual sizedptr handle_receive_packet() = 0; virtual void handle_sent_packet() = 0; virtual void enable_verbose() = 0; virtual void send_packet(sizedptr packet) = 0; - virtual void get_mac(net_l2l3_endpoint *context) = 0; - - virtual ~NetDriver() = default; - - uint16_t header_size; + virtual void get_mac(uint8_t out_mac[6]) const = 0; + virtual uint16_t get_mtu() const = 0; + virtual uint16_t get_header_size() const = 0; + virtual const char* hw_ifname() const = 0; + virtual uint32_t get_speed_mbps() const = 0; + virtual uint8_t get_duplex() const = 0; }; diff --git a/kernel/networking/drivers/virtio_net_pci/virtio_net_pci.cpp b/kernel/networking/drivers/virtio_net_pci/virtio_net_pci.cpp index 3eca5162..371cb58c 100644 --- a/kernel/networking/drivers/virtio_net_pci/virtio_net_pci.cpp +++ b/kernel/networking/drivers/virtio_net_pci/virtio_net_pci.cpp @@ -1,160 +1,204 @@ #include "virtio_net_pci.hpp" #include "console/kio.h" -#include "networking/network.h" #include "pci.h" #include "syscalls/syscalls.h" #include "memory/page_allocator.h" #include "std/memory.h" +#include "networking/network.h" #define RECEIVE_QUEUE 0 #define TRANSMIT_QUEUE 1 +static constexpr uint32_t RX_BUF_SIZE = PAGE_SIZE; + +static void* g_rx_pool = nullptr; +static uint16_t g_rx_qsz = 0; + #define kprintfv(fmt, ...) \ ({ \ if (verbose){\ kprintf(fmt, ##__VA_ARGS__); \ }\ }) - -typedef struct __attribute__((packed)) virtio_net_hdr_t { - uint8_t flags; - uint8_t gso_type; - uint16_t hdr_len; - uint16_t gso_size; - uint16_t csum_start; - uint16_t num_buffers; -} virtio_net_hdr_t; - -typedef struct virtio_net_config { - uint8_t mac[6]; - uint16_t status; - uint16_t max_virtqueue_pairs; - uint16_t mtu; - uint32_t speed; - uint8_t duplex; - uint8_t rss_max_key_size; - uint16_t rss_max_indirection_table_length; - uint32_t supported_hash_types; -}__attribute__((packed)) virtio_net_config; - -VirtioNetDriver* VirtioNetDriver::try_init(){ - VirtioNetDriver* driver = new VirtioNetDriver(); - if (driver->init()) - return driver; - delete driver; - return nullptr; +VirtioNetDriver::VirtioNetDriver() { + verbose = false; + header_size = sizeof(virtio_net_hdr_t); + mtu = 1500; + speed_mbps = 0xFFFFFFFFu; + duplex = LinkDuplex::Unknown; + last_used_receive_idx = 0; + last_used_sent_idx = 0; + hw_name[0] = 0; } -bool VirtioNetDriver::init(){ - uint64_t addr = find_pci_device(VIRTIO_VENDOR, VIRTIO_NET_ID); - if (!addr){ - kprintf("[VIRTIO_NET error] Virtio network device not found"); - return false; - } - - uint64_t net_device_address, net_device_size; - - kprintfv("[VIRTIO_NET] Configuring network device"); - - virtio_get_capabilities(&vnp_net_dev, addr, &net_device_address, &net_device_size); +VirtioNetDriver::~VirtioNetDriver(){} + +bool VirtioNetDriver::init_at(uint64_t addr, uint32_t irq_base_vector){ + kprintfv("[virtio-net] probing pci_addr=%x",(uintptr_t)addr); + + uint64_t mmio_addr = 0, mmio_size = 0; + virtio_get_capabilities(&vnp_net_dev, addr, &mmio_addr, &mmio_size); + kprintfv("[virtio-net] mmio=%x size=%x",(uintptr_t)mmio_addr,(uintptr_t)mmio_size); + if (vnp_net_dev.common_cfg) pci_register(((uintptr_t)vnp_net_dev.common_cfg) & ~(uintptr_t)(PAGE_SIZE-1), PAGE_SIZE); if (vnp_net_dev.device_cfg) pci_register(((uintptr_t)vnp_net_dev.device_cfg) & ~(uintptr_t)(PAGE_SIZE-1), PAGE_SIZE); if (vnp_net_dev.notify_cfg) pci_register(((uintptr_t)vnp_net_dev.notify_cfg) & ~(uintptr_t)(PAGE_SIZE-1), PAGE_SIZE); - if (vnp_net_dev.isr_cfg) pci_register(((uintptr_t)vnp_net_dev.isr_cfg) & ~(uintptr_t)(PAGE_SIZE-1), PAGE_SIZE); - - uint8_t interrupts_ok = pci_setup_interrupts(addr, NET_IRQ, 2); - switch(interrupts_ok){ - case 0: - kprintf("[VIRTIO_NET] Failed to setup interrupts"); - return false; - case 1: - kprintf("[VIRTIO_NET] Interrupts setup with MSI-X %i, %i",NET_IRQ,NET_IRQ+1); - break; - default: - kprintf("[VIRTIO_NET] Interrupts setup with MSI %i,%i",NET_IRQ,NET_IRQ+1); - break; + if (vnp_net_dev.isr_cfg) pci_register(((uintptr_t)vnp_net_dev.isr_cfg) & ~(uintptr_t)(PAGE_SIZE-1), PAGE_SIZE); + + uint8_t interrupts_ok = pci_setup_interrupts(addr, irq_base_vector, 2); + if (!interrupts_ok){ + kprintf("[virtio-net][err] pci_setup_interrupts failed"); + return false; + } + if (interrupts_ok == 1){ + kprintfv("[virtio-net] interrupts MSI-X base=%u",(unsigned)irq_base_vector); + } else { + kprintfv("[virtio-net] interrupts MSI base=%u",(unsigned)irq_base_vector); } pci_enable_device(addr); + kprintfv("[virtio-net] device enabled"); - if (!virtio_init_device(&vnp_net_dev)) { - kprintf("[VIRTIO_NET error] Failed network initialization"); + if (!virtio_init_device(&vnp_net_dev)){ + kprintf("[virtio-net][err] virtio_init_device failed"); return false; } - kprintf("[VIRTIO_NET] Device set up at %x",(uintptr_t)vnp_net_dev.device_cfg); + kprintfv("[virtio-net] common_cfg=%x device_cfg=%x", + (uintptr_t)vnp_net_dev.common_cfg,(uintptr_t)vnp_net_dev.device_cfg); select_queue(&vnp_net_dev, RECEIVE_QUEUE); - - for (uint16_t i = 0; i < 128; i++){ - void* buf = kalloc(vnp_net_dev.memory_page, MAX_PACKET_SIZE, ALIGN_64B, MEM_PRIV_KERNEL); - virtio_add_buffer(&vnp_net_dev, i, (uintptr_t)buf, MAX_PACKET_SIZE, false); + uint16_t rx_qsz = vnp_net_dev.common_cfg->queue_size; + if (!rx_qsz) return false; + g_rx_qsz = rx_qsz; + kprintfv("[virtio-net] RX qsz=%u",rx_qsz); + + if (!g_rx_pool){ + g_rx_pool = palloc((uint64_t)rx_qsz * RX_BUF_SIZE, MEM_PRIV_KERNEL, MEM_RW, true); + kprintfv("[virtio-net] rx_pool=%x",(uintptr_t)g_rx_pool); + if (!g_rx_pool) return false; + } + for (uint16_t i=0;iqueue_msix_vector = 0; - if (vnp_net_dev.common_cfg->queue_msix_vector != 0){ - kprintf("[VIRTIO_NET error] failed to set interrupts on receive queue, network will be unable to receive packets"); - return false; - } + kprintfv("[virtio-net] RX vector=%u",vnp_net_dev.common_cfg->queue_msix_vector); + if (vnp_net_dev.common_cfg->queue_msix_vector != 0) return false; select_queue(&vnp_net_dev, TRANSMIT_QUEUE); - vnp_net_dev.common_cfg->queue_msix_vector = 1; - if (vnp_net_dev.common_cfg->queue_msix_vector != 1){ - kprintf("[VIRTIO_NET error] failed to set interrupts on transmit queue, network will be unable to cleanup transmitted packets"); - return false; - } + kprintfv("[virtio-net] TX vector=%u",vnp_net_dev.common_cfg->queue_msix_vector); + if (vnp_net_dev.common_cfg->queue_msix_vector != 1) return false; + + virtio_net_config* cfg = (virtio_net_config*)vnp_net_dev.device_cfg; + + uint8_t mac[6]; get_mac(mac); + + + uint16_t dev_mtu = cfg->mtu; + if (dev_mtu != 0 && dev_mtu != 0xFFFF && dev_mtu >= 576) mtu = dev_mtu; else mtu = 1500; header_size = sizeof(virtio_net_hdr_t); + speed_mbps = cfg->speed; + switch (cfg->duplex) { + case 0: duplex = LinkDuplex::Half; break; + case 1: duplex = LinkDuplex::Full; break; + default: duplex = LinkDuplex::Unknown; break; + } + + hw_name[0] = 'v'; hw_name[1] = 'i'; hw_name[2] = 'r'; hw_name[3] = 't'; hw_name[4] = 'i'; hw_name[5] = 'o'; hw_name[6] = 0; + + const char* dpx_str = (duplex == LinkDuplex::Full) ? "full" : (duplex == LinkDuplex::Half) ? "half" : "unknown"; + if (speed_mbps != 0xFFFFFFFF) { + kprintfv("[virtio-net] mac=%x:%x:%x:%x:%x:%x mtu=%u hdr=%u speed=%uMbps duplex=%s", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], + (unsigned)mtu, (unsigned)header_size, (unsigned)speed_mbps, dpx_str); + } else { + kprintfv("[virtio-net] mac=%x:%x:%x:%x:%x:%x mtu=%u hdr=%u speed=unknown duplex=%s", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], + (unsigned)mtu, (unsigned)header_size, dpx_str); + } + return true; } -void VirtioNetDriver::get_mac(net_l2l3_endpoint *context){ + +void VirtioNetDriver::get_mac(uint8_t out_mac[6]) const { virtio_net_config* net_config = (virtio_net_config*)vnp_net_dev.device_cfg; - kprintfv("[VIRTIO_NET] %x:%x:%x:%x:%x:%x", net_config->mac[0], net_config->mac[1], net_config->mac[2], net_config->mac[3], net_config->mac[4], net_config->mac[5]); + memcpy(out_mac, net_config->mac, 6); +} - memcpy((void*)&context->mac,(void*)&net_config->mac,6); - - kprintfv("[VIRTIO_NET] %i virtqueue pairs",net_config->max_virtqueue_pairs); - kprintfv("[VIRTIO_NET] %x speed", net_config->speed); - kprintfv("[VIRTIO_NET] status = %x", net_config->status); +uint16_t VirtioNetDriver::get_mtu() const { + return mtu; +} + +uint16_t VirtioNetDriver::get_header_size() const { + return header_size; +} + +const char* VirtioNetDriver::hw_ifname() const { + return hw_name; +} + +uint32_t VirtioNetDriver::get_speed_mbps() const { return speed_mbps; } + +uint8_t VirtioNetDriver::get_duplex() const { + switch (duplex) { + case LinkDuplex::Half: return 0; + case LinkDuplex::Full: return 1; + default: return 0xFF; + } } sizedptr VirtioNetDriver::allocate_packet(size_t size){ return (sizedptr){(uintptr_t)kalloc(vnp_net_dev.memory_page, size + header_size, ALIGN_64B, MEM_PRIV_KERNEL), size + header_size}; } -sizedptr VirtioNetDriver::handle_receive_packet(void* buffer){ +sizedptr VirtioNetDriver::handle_receive_packet(){ select_queue(&vnp_net_dev, RECEIVE_QUEUE); struct virtq_used* used = (struct virtq_used*)(uintptr_t)vnp_net_dev.common_cfg->queue_device; struct virtq_desc* desc = (struct virtq_desc*)(uintptr_t)vnp_net_dev.common_cfg->queue_desc; struct virtq_avail* avail = (struct virtq_avail*)(uintptr_t)vnp_net_dev.common_cfg->queue_driver; + uint16_t qsz = vnp_net_dev.common_cfg->queue_size; uint16_t new_idx = used->idx; - if (new_idx != last_used_receive_idx) { - uint16_t used_ring_index = last_used_receive_idx % 128; - last_used_receive_idx = new_idx; - struct virtq_used_elem* e = &used->ring[used_ring_index]; - uint32_t desc_index = e->id; - uint32_t len = e->len; - kprintfv("Received network packet %i at index %i (len %i - %i)",used->idx, desc_index, len,sizeof(virtio_net_hdr_t)); - - uintptr_t packet = desc[desc_index].addr; - packet += sizeof(virtio_net_hdr_t); + if (new_idx == last_used_receive_idx) { + return (sizedptr){0,0}; + } - memcpy(buffer, (void*)packet, len); + uint16_t used_ring_index = (uint16_t)(last_used_receive_idx % qsz); + struct virtq_used_elem* e = &used->ring[used_ring_index]; + last_used_receive_idx++; - avail->ring[avail->idx % 128] = desc_index; + uint32_t desc_index = e->id; + uint32_t len = e->len; + if (desc_index >= qsz || len <= header_size) + { + avail->ring[avail->idx % qsz] = (uint16_t)desc_index; avail->idx++; - *(volatile uint16_t*)(uintptr_t)(vnp_net_dev.notify_cfg + vnp_net_dev.notify_off_multiplier * RECEIVE_QUEUE) = 0; + return (sizedptr){0,0}; + } - kfree((void*)desc[desc_index].addr, MAX_PACKET_SIZE); + uintptr_t packet_addr = desc[desc_index].addr; + uint32_t payload_len = len - header_size; - return (sizedptr){packet,len}; + void* out_buf = kalloc(vnp_net_dev.memory_page, payload_len, ALIGN_64B, MEM_PRIV_KERNEL); + if (!out_buf){ + avail->ring[avail->idx % qsz] = (uint16_t)desc_index; + avail->idx++; + *(volatile uint16_t*)(uintptr_t)(vnp_net_dev.notify_cfg + vnp_net_dev.notify_off_multiplier * RECEIVE_QUEUE) = 0; + return (sizedptr){0,0}; } + memcpy(out_buf, (void*)(packet_addr + header_size), payload_len); - return (sizedptr){0,0}; + avail->ring[avail->idx % qsz] = (uint16_t)desc_index; + avail->idx++; + *(volatile uint16_t*)(uintptr_t)(vnp_net_dev.notify_cfg + vnp_net_dev.notify_off_multiplier * RECEIVE_QUEUE) = 0; + + return (sizedptr){ (uintptr_t)out_buf, payload_len }; } void VirtioNetDriver::handle_sent_packet(){ @@ -162,29 +206,31 @@ void VirtioNetDriver::handle_sent_packet(){ struct virtq_used* used = (struct virtq_used*)(uintptr_t)vnp_net_dev.common_cfg->queue_device; struct virtq_desc* desc = (struct virtq_desc*)(uintptr_t)vnp_net_dev.common_cfg->queue_desc; + uint16_t qsz = vnp_net_dev.common_cfg->queue_size; - uint16_t new_idx = used->idx; - - if (new_idx != last_used_sent_idx) { - uint16_t used_ring_index = last_used_sent_idx % 128; - last_used_sent_idx = new_idx; + int cleaned = 0; + while (last_used_sent_idx != used->idx && cleaned < 64) { + uint16_t used_ring_index = (uint16_t)(last_used_sent_idx % qsz); + last_used_sent_idx = (uint16_t)(last_used_sent_idx + 1); struct virtq_used_elem* e = &used->ring[used_ring_index]; uint32_t desc_index = e->id; - uint32_t len = e->len; - kfree((void*)desc[desc_index].addr, len); - return; + + if (desc_index < qsz) { + kfree((void*)desc[desc_index].addr, desc[desc_index].len); + } + cleaned++; } } void VirtioNetDriver::send_packet(sizedptr packet){ select_queue(&vnp_net_dev, TRANSMIT_QUEUE); - - if (packet.ptr && packet.size) + if (packet.ptr && packet.size){ + if (header_size <= packet.size) memset((void*)packet.ptr, 0, header_size); virtio_send_1d(&vnp_net_dev, packet.ptr, packet.size); - - kprintfv("Queued new packet"); + kprintfv("[virtio-net] tx queued len=%u",(unsigned)packet.size); + } } void VirtioNetDriver::enable_verbose(){ verbose = true; -} +} \ No newline at end of file diff --git a/kernel/networking/drivers/virtio_net_pci/virtio_net_pci.hpp b/kernel/networking/drivers/virtio_net_pci/virtio_net_pci.hpp index 86550911..b683c084 100644 --- a/kernel/networking/drivers/virtio_net_pci/virtio_net_pci.hpp +++ b/kernel/networking/drivers/virtio_net_pci/virtio_net_pci.hpp @@ -1,34 +1,66 @@ -#include "../net_driver.hpp" +#pragma once +#include "types.h" +#include "networking/drivers/net_driver.hpp" #include "virtio/virtio_pci.h" -#include "net/network_types.h" +#include "std/memory.h" -#define VIRTIO_NET_ID 0x1000 +typedef struct __attribute__((packed)) virtio_net_hdr_t { + uint8_t flags; + uint8_t gso_type; + uint16_t hdr_len; + uint16_t gso_size; + uint16_t csum_start; + uint16_t csum_offset; +} virtio_net_hdr_t; + +typedef struct __attribute__((packed)) virtio_net_config { + uint8_t mac[6]; + uint16_t status; + uint16_t max_virtqueue_pairs; + uint16_t mtu; + uint32_t speed; + uint8_t duplex; + uint8_t rss_max_key_size; + uint16_t rss_max_indirection_table_length; + uint32_t supported_hash_types; +} virtio_net_config; + +enum class LinkDuplex : uint8_t { + Unknown = 0xFF, + Half = 0, + Full = 1 +}; class VirtioNetDriver : public NetDriver { public: - static VirtioNetDriver* try_init(); + VirtioNetDriver(); + ~VirtioNetDriver(); - VirtioNetDriver(){} + bool init_at(uint64_t pci_addr, uint32_t irq_base_vector); + void get_mac(uint8_t out_mac[6]) const override; + uint16_t get_mtu() const override; + uint16_t get_header_size() const override; + const char* hw_ifname() const override; + void enable_verbose() override; - bool init() override; + uint32_t get_speed_mbps() const override; + uint8_t get_duplex() const override; sizedptr allocate_packet(size_t size) override; - - sizedptr handle_receive_packet(void* buffer) override; - + sizedptr handle_receive_packet() override; void handle_sent_packet() override; - - void enable_verbose() override; - void send_packet(sizedptr packet) override; - void get_mac(net_l2l3_endpoint *context) override; - - ~VirtioNetDriver() = default; - private: - bool verbose = false; - uint16_t last_used_receive_idx = 0; - uint16_t last_used_sent_idx = 0; virtio_device vnp_net_dev; -}; \ No newline at end of file + + bool verbose; + uint16_t header_size; + uint16_t mtu; + uint32_t speed_mbps; + LinkDuplex duplex; + char hw_name[8]; + + uint16_t last_used_receive_idx; + uint16_t last_used_sent_idx; +}; diff --git a/kernel/networking/interface_manager.c b/kernel/networking/interface_manager.c new file mode 100644 index 00000000..0eac062e --- /dev/null +++ b/kernel/networking/interface_manager.c @@ -0,0 +1,797 @@ +#include "interface_manager.h" +#include "std/memory.h" +#include "net/link_layer/arp.h" +#include "net/internet_layer/ipv4_route.h" +#include "networking/port_manager.h" +#include "process/scheduler.h" +#include "memory/page_allocator.h" + +static void* g_kmem_page = NULL; + +//TODO: add network settings +static inline void mem_zero(void *p, size_t n){ if (p) memset(p,0,n); } +static inline void mem_copy(void *d, const void *s, size_t n){ if (d && s && n) memcpy(d,s,n); } + +static void copy_name(char dst[16], const char* src) { + int i = 0; + if (!src) { dst[0] = 0; return; } + while (src[i] && i < 15) { dst[i] = src[i]; i += 1; } + dst[i] = 0; +} + +static inline bool is_power2_mask_contiguous(uint32_t mask){ + if (mask == 0) return false; + uint32_t inv = ~mask; + return ((inv & (inv + 1u)) == 0); +} + +static inline bool ipv4_is_unspecified(uint32_t ip){ return ip == 0; } +static inline bool ipv4_is_loopback(uint32_t ip){ return ((ip & 0xFF000000u) == 0x7F000000u); } +static inline bool ipv4_is_multicast(uint32_t ip){ return ((ip & 0xF0000000u) == 0xE0000000u); } +static inline bool ipv4_is_network_address(uint32_t ip, uint32_t mask){ if (mask==0 || mask==0xFFFFFFFFu) return false; return ((ip & mask) == ip); } +static inline bool ipv4_is_broadcast_address(uint32_t ip, uint32_t mask){ if (mask==0 || mask==0xFFFFFFFFu) return false; return (ip == ((ip & mask) | ~mask)); } +static inline bool ipv4_is_reserved_special(uint32_t ip){ + if ((ip & 0xFF000000u) == 0x00000000u) return true; + if ((ip & 0xFFFF0000u) == 0xA9FE0000u) return true; + if ((ip & 0xF0000000u) == 0xF0000000u) return true; + return false; +} + +static inline int prefix_match(const uint8_t a[16], const uint8_t b[16]){ + int bits = 0; + for (int i=0;i<16;i++){ + uint8_t x = (uint8_t)(a[i] ^ b[i]); + if (x==0){ bits += 8; continue; } + for (int bpos=7; bpos>=0; --bpos){ + if (x & (1u<=(int)MAX_L2_INTERFACES) return -1; + if (!g_l2_used[s]) return -1; + return s; +} +static inline uint8_t make_l3_id(uint8_t ifindex, uint8_t local_slot){ return (uint8_t)((ifindex<<4) | (local_slot & 0x0F)); } +static inline uint8_t l3_ifindex_from_id(uint8_t l3_id){ return (uint8_t)((l3_id >> 4) & 0x0F); } +static inline uint8_t l3_local_slot_from_id(uint8_t l3_id){ return (uint8_t)(l3_id & 0x0F); } + +static bool v4_has_dhcp_on_l2(uint8_t ifindex){ + for (int i = 0; i < V4_POOL_SIZE; i++){ + if (!g_v4[i].used) continue; + l3_ipv4_interface_t *x = &g_v4[i].node; + if (!x->l2) continue; + if (x->l2->ifindex != ifindex) continue; + if (x->mode == IPV4_CFG_DHCP) return true; + } + return false; +} + +uint8_t l2_interface_create(const char *name, void *driver_ctx, uint16_t base_metric){ + int slot = find_free_l2_slot(); + if (slot < 0) return 0; + l2_interface_t* itf = &g_l2[slot]; + mem_zero(itf, sizeof(*itf)); + itf->ifindex = (uint8_t)(slot + 1); + copy_name(itf->name, name); + itf->driver_context = driver_ctx; + itf->base_metric = base_metric; + + + itf->arp_table = arp_table_create(); + itf->nd_table = NULL; + + g_l2_used[slot] = 1; + g_l2_count += 1; + return itf->ifindex; +} + +bool l2_interface_destroy(uint8_t ifindex){ + int slot = l2_slot_from_ifindex(ifindex); + if (slot < 0) return false; + l2_interface_t* itf = &g_l2[slot]; + if (itf->ipv4_count || itf->ipv6_count) return false; + + if (itf->arp_table) { arp_table_destroy((arp_table_t*)itf->arp_table); itf->arp_table = NULL; } + + mem_zero(&g_l2[slot], sizeof(l2_interface_t)); + g_l2_used[slot] = 0; + if (g_l2_count) g_l2_count -= 1; + return true; +} + +l2_interface_t* l2_interface_find_by_index(uint8_t ifindex) { + int slot = l2_slot_from_ifindex(ifindex); + if (slot < 0) return 0; + return &g_l2[slot]; +} + +uint8_t l2_interface_count(void) { return g_l2_count; } + +l2_interface_t* l2_interface_at(uint8_t idx) { + uint8_t seen = 0; + for (int i=0;i<(int)MAX_L2_INTERFACES;i++){ + if (!g_l2_used[i]) continue; + if (seen == idx) return &g_l2[i]; + seen++; + } + return 0; +} + +bool l2_interface_set_up(uint8_t ifindex, bool up) { + l2_interface_t* itf = l2_interface_find_by_index(ifindex); + if (!itf) return false; + itf->is_up = up; + return true; +} + +static int find_ipv4_group_index(l2_interface_t* itf, uint32_t group) { + for (int i = 0; i < (int)itf->ipv4_mcast_count; ++i) if (itf->ipv4_mcast[i] == group) return i; + return -1; +} + +bool l2_ipv4_mcast_join(uint8_t ifindex, uint32_t group) { + l2_interface_t* itf = l2_interface_find_by_index(ifindex); + if (!itf) return false; + if (!ipv4_is_multicast(group)) return false; + if (find_ipv4_group_index(itf, group) >= 0) return true; + if (itf->ipv4_mcast_count >= MAX_IPV4_MCAST_PER_INTERFACE) return false; + itf->ipv4_mcast[itf->ipv4_mcast_count++] = group; + return true; +} + +bool l2_ipv4_mcast_leave(uint8_t ifindex, uint32_t group) { + l2_interface_t* itf = l2_interface_find_by_index(ifindex); + if (!itf) return false; + int idx = find_ipv4_group_index(itf, group); + if (idx < 0) return true; + for (int i = idx + 1; i < (int)itf->ipv4_mcast_count; ++i) itf->ipv4_mcast[i-1] = itf->ipv4_mcast[i]; + if (itf->ipv4_mcast_count) itf->ipv4_mcast_count -= 1; + return true; +} + +static int find_ipv6_group_index(l2_interface_t* itf, const uint8_t group[16]) { + for (int i = 0; i < (int)itf->ipv6_mcast_count; ++i) if (cmp16(itf->ipv6_mcast[i], group) == 0) return i; + return -1; +} +bool l2_ipv6_mcast_join(uint8_t ifindex, const uint8_t group[16]) { + l2_interface_t* itf = l2_interface_find_by_index(ifindex); + if (!itf || !group) return false; + if (!ipv6_is_multicast(group)) return false; + if (find_ipv6_group_index(itf, group) >= 0) return true; + if (itf->ipv6_mcast_count >= MAX_IPV6_MCAST_PER_INTERFACE) return false; + cp16(itf->ipv6_mcast[itf->ipv6_mcast_count], group); + itf->ipv6_mcast_count += 1; + return true; +} +bool l2_ipv6_mcast_leave(uint8_t ifindex, const uint8_t group[16]) { + l2_interface_t* itf = l2_interface_find_by_index(ifindex); + if (!itf || !group) return false; + int idx = find_ipv6_group_index(itf, group); + if (idx < 0) return true; + for (int i = idx + 1; i < (int)itf->ipv6_mcast_count; ++i) cp16(itf->ipv6_mcast[i-1], itf->ipv6_mcast[i]); + if (itf->ipv6_mcast_count) itf->ipv6_mcast_count -= 1; + return true; +} + +static bool v4_ip_exists_anywhere(uint32_t ip){ + for (int i=0;il2 || x->l2->ifindex != ifindex) continue; + if (x->mode == IPV4_CFG_DISABLED) continue; + uint32_t m = (x->mask==0)?mask:((mask==0)?x->mask:((x->mask < mask)?x->mask:mask)); + if (ipv4_net(ip, m) == ipv4_net(x->ip, m)) return true; + (void)n1; + } + return false; +} + +static bool v6_ip_exists_anywhere(const uint8_t ip[16]){ + if (ipv6_is_unspecified(ip)) return false; + for (int i=0;il2 || x->l2->ifindex != ifindex) continue; + if (x->cfg == IPV6_CFG_DISABLE) continue; + if (ipv6_is_unspecified(x->ip)) continue; + uint8_t minp = (x->prefix_len < prefix_len) ? x->prefix_len : prefix_len; + int eq = 1; + int fb = minp/8, rb = minp%8; + for (int b=0;bip[b]) {eq=0;break;} } + if (eq && rb){ + uint8_t m=(uint8_t)(0xFF<<(8-rb)); + if ( (ip[fb]&m) != (x->ip[fb]&m) ) eq=0; + } + if (eq) return true; + } + return false; +} + +static int alloc_local_slot_v4(l2_interface_t *l2){ + if (!l2) return -1; + for (int s=0; sl3_v4[s] == NULL) return s; + return -1; +} + +static int alloc_local_slot_v6(l2_interface_t *l2){ + if (!l2) return -1; + for (int s=0; sl3_v6[s] == NULL) return s; + return -1; +} + +static int alloc_global_v4_slot(void){ for (int i=0;iname[0]=='l' && l2->name[1]=='o')) return 0; + if (ipv4_is_multicast(ip)) return 0; + if (ipv4_is_reserved_special(ip)) return 0; + if (ipv4_is_network_address(ip, mask)) return 0; + if (ipv4_is_broadcast_address(ip, mask)) return 0; + if (v4_ip_exists_anywhere(ip)) return 0; + if (v4_overlap_intra_l2(ifindex, ip, mask)) return 0; + } + if (l2->ipv4_count >= MAX_IPV4_PER_INTERFACE) return 0; + + int loc = alloc_local_slot_v4(l2); + int g = alloc_global_v4_slot(); + if (loc < 0 || g < 0) return 0; + + g_v4[g].used = true; + g_v4[g].slot_in_l2 = (uint8_t)loc; + + l3_ipv4_interface_t *n = &g_v4[g].node; + mem_zero(n, sizeof(*n)); + n->l2 = l2; + n->mode = mode; + n->ip = (mode==IPV4_CFG_STATIC) ? ip : 0; + n->mask = (mode==IPV4_CFG_STATIC) ? mask : 0; + n->gw = (mode==IPV4_CFG_STATIC) ? gw : 0; + n->broadcast = (mode==IPV4_CFG_STATIC) ? ipv4_broadcast_calc(ip, mask) : 0; + + mem_zero(&n->runtime_opts_v4, sizeof(n->runtime_opts_v4)); + if (runtime_opts) { + n->runtime_opts_v4 = *runtime_opts; + } + + n->routing_table = ipv4_rt_create(); + if (!n->routing_table) { + g_v4[g].used = false; + mem_zero(&g_v4[g], sizeof(g_v4[g])); + return 0; + } + ipv4_rt_ensure_basics((ipv4_rt_table_t*)n->routing_table, n->ip, n->mask, n->gw, l2->base_metric); + + n->is_localhost = (l2->name[0]=='l' && l2->name[1]=='o'); + n->l3_id = make_l3_id(l2->ifindex, (uint8_t)loc); + l2->l3_v4[loc] = n; + l2->ipv4_count++; + + if (!g_kmem_page) g_kmem_page = palloc(PAGE_SIZE, MEM_PRIV_KERNEL, MEM_RW | MEM_NORM, false); + n->port_manager = (port_manager_t*)kalloc(g_kmem_page, sizeof(port_manager_t), ALIGN_16B, MEM_PRIV_KERNEL); + if (!n->port_manager) { + l2->l3_v4[loc] = NULL; + if (l2->ipv4_count) l2->ipv4_count--; + ipv4_rt_destroy((ipv4_rt_table_t*)n->routing_table); + n->routing_table = NULL; + g_v4[g].used = false; + mem_zero(&g_v4[g], sizeof(g_v4[g])); + return 0; + } + port_manager_init(n->port_manager); + + return n->l3_id; +} + +bool l3_ipv4_update(uint8_t l3_id, uint32_t ip, uint32_t mask, uint32_t gw, ipv4_cfg_t mode, net_runtime_opts_t *runtime_opts){ + l3_ipv4_interface_t *n = l3_ipv4_find_by_id(l3_id); + if (!n) return false; + l2_interface_t *l2 = n->l2; + if (!l2) return false; + if (mode == IPV4_CFG_DHCP && n->mode != IPV4_CFG_DHCP) { + if (v4_has_dhcp_on_l2(l2->ifindex)) return false; + } + if (mode == IPV4_CFG_STATIC){ + if (ipv4_is_unspecified(ip)) return false; + if (!is_power2_mask_contiguous(mask)) return false; + if (ipv4_is_loopback(ip) && !(l2->name[0]=='l' && l2->name[1]=='o')) return false; + if (ipv4_is_multicast(ip)) return false; + if (ipv4_is_reserved_special(ip)) return false; + if (ipv4_is_network_address(ip, mask)) return false; + if (ipv4_is_broadcast_address(ip, mask)) return false; + if (ip != n->ip && v4_ip_exists_anywhere(ip)) return false; + for (int i = 0; i < V4_POOL_SIZE; i++){ + if (!g_v4[i].used) continue; + l3_ipv4_interface_t *x = &g_v4[i].node; + if (x==n) continue; + if (!x->l2 || x->l2->ifindex != l2->ifindex) continue; + if (x->mode == IPV4_CFG_DISABLED) continue; + uint32_t m = (x->mask < mask) ? x->mask : mask; + if (ipv4_net(ip, m) == ipv4_net(x->ip, m)) return false; + } + } + + n->mode = mode; + + if (runtime_opts) { + n->runtime_opts_v4 = *runtime_opts; + } + + if (mode == IPV4_CFG_STATIC || mode == IPV4_CFG_DHCP) { + n->ip = ip; + n->mask = mask; + n->gw = gw; + n->broadcast = ipv4_broadcast_calc(ip, mask); + } else { + n->ip = 0; + n->mask = 0; + n->gw = 0; + n->broadcast = 0; + } + + if (!n->routing_table) n->routing_table = ipv4_rt_create(); + ipv4_rt_sync_basics((ipv4_rt_table_t*)n->routing_table, n->ip, n->mask, n->gw, l2->base_metric); + return true; +} + +bool l3_ipv4_remove_from_interface(uint8_t l3_id){ + l3_ipv4_interface_t *n = l3_ipv4_find_by_id(l3_id); + if (!n) return false; + l2_interface_t *l2 = n->l2; + if (!l2) return false; + if (l2->ipv4_count <= 1) return false; + + int g = -1; + for (int i=0;iport_manager) { + kfree(n->port_manager, sizeof(port_manager_t)); + n->port_manager = NULL; + } + + uint8_t slot = l3_local_slot_from_id(l3_id); + if (slot < MAX_IPV4_PER_INTERFACE && l2->l3_v4[slot] == n){ + l2->l3_v4[slot] = NULL; + if (l2->ipv4_count) l2->ipv4_count--; + } + + if (n->routing_table) { + ipv4_rt_destroy((ipv4_rt_table_t*)n->routing_table); + n->routing_table = 0; + } + + g_v4[g].used = false; + mem_zero(&g_v4[g], sizeof(g_v4[g])); + return true; +} + +l3_ipv4_interface_t* l3_ipv4_find_by_id(uint8_t l3_id){ + uint8_t ifx = l3_ifindex_from_id(l3_id); + uint8_t loc = l3_local_slot_from_id(l3_id); + l2_interface_t *l2 = l2_interface_find_by_index(ifx); + if (!l2) return NULL; + if (loc >= MAX_IPV4_PER_INTERFACE) return NULL; + return l2->l3_v4[loc]; +} +l3_ipv4_interface_t* l3_ipv4_find_by_ip(uint32_t ip){ + for (int i=0;i 128) return 0; + + int i_; + int placeholder_ll = 0; + if (ip[0]==0xFE && ip[1]==0x80){ placeholder_ll = 1; for(i_=2;i_<16;i_++){ if (ip[i_]!=0){ placeholder_ll=0; break; } } } + int placeholder_gua = 0; + if (ip[0]==0x20 && ip[1]==0x00){ placeholder_gua = 1; for(i_=2;i_<16;i_++){ if (ip[i_]!=0){ placeholder_gua=0; break; } } } + + if (kind & IPV6_ADDRK_LINK_LOCAL){ + if (!(cfg & (IPV6_CFG_SLAAC | IPV6_CFG_DHCPV6))){ + if (!ipv6_is_linklocal(ip)) return 0; + } + if (!ipv6_is_unspecified(ip) && !placeholder_ll && v6_ip_exists_anywhere(ip)) return 0; + for (int i=0;iifindex != ifindex) continue; + if (ipv6_is_linklocal(g_v6[i].node.ip) && g_v6[i].node.cfg != IPV6_CFG_DISABLE) return 0; + } + } else if (kind & IPV6_ADDRK_GLOBAL){ + int is_loop = ipv6_is_loopback(ip); + + if (!(cfg & (IPV6_CFG_SLAAC | IPV6_CFG_DHCPV6))){ + if (ipv6_is_unspecified(ip)) return 0; + } + if (!ipv6_is_unspecified(ip)){ + if (ipv6_is_multicast(ip)) return 0; + if (is_loop && !(l2->name[0]=='l' && l2->name[1]=='o')) return 0; + if (!is_loop){ + if (ipv6_is_ula(ip)) return 0; + if (!placeholder_gua){ + if (v6_ip_exists_anywhere(ip)) return 0; + if (v6_overlap_intra_l2(ifindex, ip, prefix_len)) return 0; + } + } + } + if (!is_loop){ + bool has_lla=false; + for (int i=0;il2 || x->l2->ifindex != ifindex) continue; + if (ipv6_is_linklocal(x->ip) && x->cfg != IPV6_CFG_DISABLE){ has_lla=true; break; } + } + if (!has_lla) return 0; + } + } else { + return 0; + } + + if (l2->ipv6_count >= MAX_IPV6_PER_INTERFACE) return 0; + int loc = alloc_local_slot_v6(l2); + int g = alloc_global_v6_slot(); + if (loc < 0 || g < 0) return 0; + + g_v6[g].used = true; + g_v6[g].slot_in_l2 = (uint8_t)loc; + + l3_ipv6_interface_t *n = &g_v6[g].node; + mem_zero(n, sizeof(*n)); + n->l2 = l2; + n->cfg = cfg; + n->kind = kind; + cp16(n->ip, ip); + n->prefix_len = prefix_len; + cp16(n->gateway, gw); + n->is_localhost = (l2->name[0]=='l' && l2->name[1]=='o'); + n->l3_id = make_l3_id(l2->ifindex, (uint8_t)loc); + l2->l3_v6[loc] = n; + l2->ipv6_count++; + + if (!g_kmem_page) g_kmem_page = palloc(PAGE_SIZE, MEM_PRIV_KERNEL, MEM_RW | MEM_NORM, false); + n->port_manager = (port_manager_t*)kalloc(g_kmem_page, sizeof(port_manager_t), ALIGN_16B, MEM_PRIV_KERNEL); + if (!n->port_manager){ + l2->l3_v6[loc] = NULL; + if (l2->ipv6_count) l2->ipv6_count--; + g_v6[g].used = false; + mem_zero(&g_v6[g], sizeof(g_v6[g])); + return 0; + } + port_manager_init(n->port_manager); + + return n->l3_id; +} + +bool l3_ipv6_update(uint8_t l3_id, const uint8_t ip[16], uint8_t prefix_len, const uint8_t gw[16], ipv6_cfg_t cfg, uint8_t kind){ + l3_ipv6_interface_t *n = l3_ipv6_find_by_id(l3_id); + if (!n) return false; + l2_interface_t *l2 = n->l2; + if (!l2) return false; + if (prefix_len > 128) return false; + + if ((n->kind & IPV6_ADDRK_LINK_LOCAL) && cfg == IPV6_CFG_DISABLE){ + for (int i=0;il2 || x->l2->ifindex != l2->ifindex) continue; + if ((x->kind & IPV6_ADDRK_GLOBAL) && x->cfg != IPV6_CFG_DISABLE) return false; + } + } + + if (kind & IPV6_ADDRK_LINK_LOCAL){ + if (!(cfg & (IPV6_CFG_SLAAC | IPV6_CFG_DHCPV6))){ + if (!ipv6_is_linklocal(ip)) return false; + } + if (!ipv6_is_unspecified(ip) && cmp16(ip, n->ip)!=0 && v6_ip_exists_anywhere(ip)) return false; + for (int i=0;il2 || x->l2->ifindex != l2->ifindex) continue; + if (ipv6_is_linklocal(x->ip) && x->cfg != IPV6_CFG_DISABLE) return false; + } + } else if (kind & IPV6_ADDRK_GLOBAL){ + if (!(cfg & (IPV6_CFG_SLAAC | IPV6_CFG_DHCPV6))){ + if (ipv6_is_unspecified(ip)) return false; + } + if (!ipv6_is_unspecified(ip)){ + if (ipv6_is_multicast(ip) || ipv6_is_loopback(ip) || ipv6_is_ula(ip)) return false; + if (cmp16(ip,n->ip)!=0 && v6_ip_exists_anywhere(ip)) return false; + if (v6_overlap_intra_l2(l2->ifindex, ip, prefix_len)){ + for (int i=0;il2 || x->l2->ifindex != l2->ifindex) continue; + if (ipv6_is_unspecified(x->ip)) continue; + uint8_t minp = (x->prefix_len < prefix_len) ? x->prefix_len : prefix_len; + int eq = 1; + int fb=minp/8, rb=minp%8; + for (int b=0;bip[b]) {eq=0;break;} } + if (eq && rb){ + uint8_t m=(uint8_t)(0xFF<<(8-rb)); + if ( (ip[fb]&m) != (x->ip[fb]&m) ) eq=0; + } + if (eq) return false; + } + } + } + } else { + return false; + } + + n->cfg = cfg; + n->kind = kind; + cp16(n->ip, ip); + n->prefix_len = prefix_len; + cp16(n->gateway, gw); + return true; +} + +bool l3_ipv6_remove_from_interface(uint8_t l3_id){ + l3_ipv6_interface_t *n = l3_ipv6_find_by_id(l3_id); + if (!n) return false; + l2_interface_t *l2 = n->l2; + if (!l2) return false; + if ((n->kind & IPV6_ADDRK_LINK_LOCAL)){ + for (int i=0;il2 || x->l2->ifindex != l2->ifindex) continue; + if ((x->kind & IPV6_ADDRK_GLOBAL) && x->cfg != IPV6_CFG_DISABLE) return false; + } + } + if (l2->ipv6_count <= 1) return false; + + int g = -1; + for (int i=0;iport_manager) { + kfree(n->port_manager, sizeof(port_manager_t)); + n->port_manager = NULL; + } + + uint8_t slot = l3_local_slot_from_id(l3_id); + if (slot < MAX_IPV6_PER_INTERFACE && l2->l3_v6[slot] == n){ + l2->l3_v6[slot] = NULL; + if (l2->ipv6_count) l2->ipv6_count--; + } + + g_v6[g].used = false; + mem_zero(&g_v6[g], sizeof(g_v6[g])); + return true; +} + +bool l3_ipv6_set_enabled(uint8_t l3_id, bool enable){ + l3_ipv6_interface_t *n = l3_ipv6_find_by_id(l3_id); + if (!n) return false; + if (enable){ + if (n->cfg == IPV6_CFG_DISABLE) n->cfg = IPV6_CFG_STATIC; + return true; + } else { + if ((n->kind & IPV6_ADDRK_LINK_LOCAL)){ + l2_interface_t *l2 = n->l2; + for (int i=0;il2 || x->l2->ifindex != l2->ifindex) continue; + if ((x->kind & IPV6_ADDRK_GLOBAL) && x->cfg != IPV6_CFG_DISABLE) return false; + } + } + n->cfg = IPV6_CFG_DISABLE; + return true; + } +} + +l3_ipv6_interface_t* l3_ipv6_find_by_id(uint8_t l3_id){ + uint8_t ifx = l3_ifindex_from_id(l3_id); + uint8_t loc = l3_local_slot_from_id(l3_id); + l2_interface_t *l2 = l2_interface_find_by_index(ifx); + if (!l2) return NULL; + if (loc >= MAX_IPV6_PER_INTERFACE) return NULL; + return l2->l3_v6[loc]; +} +l3_ipv6_interface_t* l3_ipv6_find_by_ip(const uint8_t ip[16]){ + for (int i=0;iifindex, 0x7F000001u, 0xFF000000u, 0, IPV4_CFG_STATIC, NULL); +} + +void l3_init_localhost_ipv6(void){ + l2_interface_t *lo = NULL; + for (int i=0;i<(int)MAX_L2_INTERFACES;i++){ + if (!g_l2_used[i]) continue; + if (g_l2[i].name[0]=='l' && g_l2[i].name[1]=='o' && g_l2[i].name[2]=='0' && g_l2[i].name[3]==0) { lo = &g_l2[i]; break; } + } + if (!lo) return; + uint8_t loop6[16]={0}; loop6[15]=1; + for (int i=0;iifindex, loop6, 128, zero16, IPV6_CFG_STATIC, IPV6_ADDRK_GLOBAL); +} + +void ifmgr_autoconfig_l2(uint8_t ifindex){ + l2_interface_t *l2 = l2_interface_find_by_index(ifindex); + if (!l2) return; + + if (l2->name[0]=='l' && l2->name[1]=='o' && l2->name[2]=='0' && l2->name[3]==0){ + return; + } + + if (l2->ipv4_count == 0){ + (void)l3_ipv4_add_to_interface(ifindex, 0, 0, 0, IPV4_CFG_DHCP, NULL); + } + + bool has_lla=false; + for (int i=0;il2 || x->l2->ifindex != ifindex) continue; + if (ipv6_is_linklocal(x->ip) && x->cfg != IPV6_CFG_DISABLE){ has_lla=true; break; } + } + if (!has_lla){ + uint8_t fe80_0[16]={0}; fe80_0[0]=0xFE; fe80_0[1]=0x80; + uint8_t zero16[16]={0}; + (void)l3_ipv6_add_to_interface(ifindex, fe80_0, 64, zero16, IPV6_CFG_SLAAC, IPV6_ADDRK_LINK_LOCAL); + } + + bool has_gua=false; + for (int i=0;il2 || x->l2->ifindex != ifindex) continue; + if (!ipv6_is_linklocal(x->ip) && x->cfg != IPV6_CFG_DISABLE){ has_gua=true; break; } + } + if (!has_gua){ + uint8_t g2000_0[16]={0}; g2000_0[0]=0x20; g2000_0[1]=0x00; + uint8_t zero16[16]={0}; + (void)l3_ipv6_add_to_interface(ifindex, g2000_0, 64, zero16, IPV6_CFG_SLAAC, IPV6_ADDRK_GLOBAL); + } + + //TODO: add autoconfig settings/policy +} + +void ifmgr_autoconfig_all_l2(void){ + for (int i=0;i<(int)MAX_L2_INTERFACES;i++){ + if (!g_l2_used[i]) continue; + ifmgr_autoconfig_l2(g_l2[i].ifindex); + } +} + +ip_resolution_result_t resolve_ipv4_to_interface(uint32_t dst_ip){ + ip_resolution_result_t r; r.found=false; r.ipv4=NULL; r.ipv6=NULL; r.l2=NULL; + int best_plen = -1; + for (int i=0;il2) continue; + if (x->mode == IPV4_CFG_DISABLED) continue; + uint32_t m = x->mask; + if (m==0){ + if (x->ip == dst_ip && best_plen < 32){ best_plen = 32; r.found=true; r.ipv4=x; r.l2=x->l2; } + continue; + } + if (ipv4_net(dst_ip, m) == ipv4_net(x->ip, m)){ + int plen=0; uint32_t tmp=m; + while (tmp){ plen += (tmp & 1u); tmp >>= 1; } + if (plen > best_plen){ best_plen = plen; r.found=true; r.ipv4=x; r.l2=x->l2; } + } + } + return r; +} + +ip_resolution_result_t resolve_ipv6_to_interface(const uint8_t dst_ip[16]){ + ip_resolution_result_t r; r.found=false; r.ipv4=NULL; r.ipv6=NULL; r.l2=NULL; + int best = -1; + for (int i=0;il2) continue; + if (x->cfg == IPV6_CFG_DISABLE) continue; + if (ipv6_is_unspecified(x->ip)) continue; + int match = prefix_match(dst_ip, x->ip); + if (match >= x->prefix_len && match > best){ best = match; r.found=true; r.ipv6=x; r.l2=x->l2; } + } + return r; +} + +bool check_ipv4_overlap(uint32_t new_ip, uint32_t mask, uint8_t ifindex){ return v4_overlap_intra_l2(ifindex, new_ip, mask); } +bool check_ipv6_overlap(const uint8_t new_ip[16], uint8_t prefix_len, uint8_t ifindex){ return v6_overlap_intra_l2(ifindex, new_ip, prefix_len); } diff --git a/kernel/networking/interface_manager.h b/kernel/networking/interface_manager.h new file mode 100644 index 00000000..7247d4c5 --- /dev/null +++ b/kernel/networking/interface_manager.h @@ -0,0 +1,158 @@ +#pragma once + +#include "types.h" +#include "networking/port_manager.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define MAX_L2_INTERFACES 16 +#define MAX_IPV4_PER_INTERFACE 4 +#define MAX_IPV6_PER_INTERFACE 4 +#define MAX_IPV4_MCAST_PER_INTERFACE 12 +#define MAX_IPV6_MCAST_PER_INTERFACE 12 + +typedef enum { + IPV4_CFG_DISABLED = -1, + IPV4_CFG_DHCP = 0, + IPV4_CFG_STATIC = 1 +} ipv4_cfg_t; + +typedef enum { + IPV6_ADDRK_GLOBAL = 0x01, + IPV6_ADDRK_LINK_LOCAL = 0x02 +} ipv6_addr_kind_t; + +typedef enum { + IPV6_CFG_DISABLE = -1, + IPV6_CFG_STATIC = 0x01, + IPV6_CFG_SLAAC = 0x02, + IPV6_CFG_DHCPV6 = 0x04 +} ipv6_cfg_t; + +struct l2_interface; +struct l3_ipv4_interface; +struct l3_ipv6_interface; + +typedef struct net_runtime_opts { + uint16_t mtu; + uint32_t t1; + uint32_t t2; + uint32_t dns[2]; + uint32_t ntp[2]; + uint16_t xid; + uint32_t server_ip; + uint32_t lease; + uint32_t lease_start_time; +} net_runtime_opts_t; + +typedef struct l2_interface { + uint8_t ifindex; + char name[16]; + bool is_up; + uint16_t base_metric; + void *driver_context; + void *arp_table; + void *nd_table; + struct l3_ipv4_interface *l3_v4[MAX_IPV4_PER_INTERFACE]; + struct l3_ipv6_interface *l3_v6[MAX_IPV6_PER_INTERFACE]; + uint8_t ipv4_count; + uint8_t ipv6_count; + uint32_t ipv4_mcast[MAX_IPV4_MCAST_PER_INTERFACE]; + uint8_t ipv4_mcast_count; + uint8_t ipv6_mcast[MAX_IPV6_MCAST_PER_INTERFACE][16]; + uint8_t ipv6_mcast_count; +} l2_interface_t; + +typedef struct l3_ipv4_interface { + uint8_t l3_id; + uint32_t ip; + uint32_t mask; + uint32_t gw; + uint32_t broadcast; + ipv4_cfg_t mode; + bool is_localhost; + net_runtime_opts_t runtime_opts_v4; + void *routing_table; + port_manager_t *port_manager; + l2_interface_t *l2; +} l3_ipv4_interface_t; + +typedef struct l3_ipv6_interface { + uint8_t l3_id; + uint8_t ip[16]; + uint8_t prefix_len; + uint8_t gateway[16]; + uint8_t kind; + ipv6_cfg_t cfg; + bool is_localhost; + uint32_t valid_lifetime; + uint32_t preferred_lifetime; + uint32_t timestamp_created; + uint8_t prefix[16]; + uint8_t interface_id[8]; + port_manager_t *port_manager; + l2_interface_t *l2; +} l3_ipv6_interface_t; + +typedef struct ip_resolution_result { + bool found; + l3_ipv4_interface_t *ipv4; + l3_ipv6_interface_t *ipv6; + l2_interface_t *l2; +} ip_resolution_result_t; + +uint8_t l2_interface_create(const char *name, void *driver_ctx, uint16_t base_metric); +bool l2_interface_destroy(uint8_t ifindex); +l2_interface_t *l2_interface_find_by_index(uint8_t ifindex); +uint8_t l2_interface_count(void); +l2_interface_t *l2_interface_at(uint8_t idx); +bool l2_interface_set_up(uint8_t ifindex, bool up); + +bool l2_ipv4_mcast_join(uint8_t ifindex, uint32_t group); +bool l2_ipv4_mcast_leave(uint8_t ifindex, uint32_t group); +bool l2_ipv6_mcast_join(uint8_t ifindex, const uint8_t group[16]); +bool l2_ipv6_mcast_leave(uint8_t ifindex, const uint8_t group[16]); + +uint8_t l3_ipv4_add_to_interface(uint8_t ifindex, uint32_t ip, uint32_t mask, uint32_t gw, ipv4_cfg_t mode, net_runtime_opts_t *rt); +bool l3_ipv4_update(uint8_t l3_id, uint32_t ip, uint32_t mask, uint32_t gw, ipv4_cfg_t mode, net_runtime_opts_t *rt); +bool l3_ipv4_remove_from_interface(uint8_t l3_id); +l3_ipv4_interface_t *l3_ipv4_find_by_id(uint8_t l3_id); +l3_ipv4_interface_t *l3_ipv4_find_by_ip(uint32_t ip); + +uint8_t l3_ipv6_add_to_interface(uint8_t ifindex, const uint8_t ip[16], uint8_t prefix_len, const uint8_t gw[16], ipv6_cfg_t cfg, uint8_t kind); +bool l3_ipv6_update(uint8_t l3_id, const uint8_t ip[16], uint8_t prefix_len, const uint8_t gw[16], ipv6_cfg_t cfg, uint8_t kind); +bool l3_ipv6_remove_from_interface(uint8_t l3_id); +bool l3_ipv6_set_enabled(uint8_t l3_id, bool enable); +l3_ipv6_interface_t *l3_ipv6_find_by_id(uint8_t l3_id); +l3_ipv6_interface_t *l3_ipv6_find_by_ip(const uint8_t ip[16]); + +void l3_init_localhost_ipv4(void); +void l3_init_localhost_ipv6(void); + +void ifmgr_autoconfig_all_l2(void); +void ifmgr_autoconfig_l2(uint8_t ifindex); + +ip_resolution_result_t resolve_ipv4_to_interface(uint32_t dst_ip); +ip_resolution_result_t resolve_ipv6_to_interface(const uint8_t dst_ip[16]); + +bool check_ipv4_overlap(uint32_t new_ip, uint32_t mask, uint8_t ifindex); +bool check_ipv6_overlap(const uint8_t new_ip[16], uint8_t prefix_len, uint8_t ifindex); + +static inline uint32_t ipv4_net(uint32_t ip, uint32_t mask){ return ip & mask; } +static inline uint32_t ipv4_broadcast_calc(uint32_t ip, uint32_t mask){ return (mask==0)?0:((ip & mask) | ~mask); } + + +static inline port_manager_t* ifmgr_pm_v4(uint8_t l3_id){ + l3_ipv4_interface_t* n = l3_ipv4_find_by_id(l3_id); + return n ? n->port_manager : NULL; +} +static inline port_manager_t* ifmgr_pm_v6(uint8_t l3_id){ + l3_ipv6_interface_t* n = l3_ipv6_find_by_id(l3_id); + return n ? n->port_manager : NULL; +} + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/kernel/networking/network.cpp b/kernel/networking/network.cpp index 0a56442f..52c4f25b 100644 --- a/kernel/networking/network.cpp +++ b/kernel/networking/network.cpp @@ -2,19 +2,20 @@ #include "network_dispatch.hpp" #include "process/scheduler.h" -static NetworkDispatch *dispatch = nullptr; +static NetworkDispatch *dispatch = 0; bool network_init() { dispatch = new NetworkDispatch(); - return dispatch && dispatch->init(); + if (!dispatch) return false; + return dispatch->init(); } -void network_handle_download_interrupt() { - if (dispatch) dispatch->handle_download_interrupt(); +void network_handle_download_interrupt_nic(uint16_t nic_id) { + if (dispatch) dispatch->handle_rx_irq((size_t)nic_id); } -void network_handle_upload_interrupt() { - if (dispatch) dispatch->handle_upload_interrupt(); +void network_handle_upload_interrupt_nic(uint16_t nic_id) { + if (dispatch) dispatch->handle_tx_irq((size_t)nic_id); } int network_net_task_entry(int argc, char* argv[]) { @@ -24,19 +25,61 @@ int network_net_task_entry(int argc, char* argv[]) { int net_tx_frame(uintptr_t frame_ptr, uint32_t frame_len) { if (!dispatch || !frame_ptr || !frame_len) return -1; - return dispatch->enqueue_frame({frame_ptr, frame_len}) ? 0 : -1; + uint8_t ix = 1; //legacy + if (ix == 0xFF) return -1; + return dispatch->enqueue_frame(ix, {frame_ptr, frame_len}) ? 0 : -1; } -int net_rx_frame(sizedptr *out_frame) { - extern uint16_t get_current_proc_pid(); - if (!dispatch || !out_frame) return -1; - int sz = dispatch->dequeue_packet_for(get_current_proc_pid(), out_frame) ? (int)out_frame->size : 0; - return sz; +int net_tx_frame_on(uint16_t ifindex, uintptr_t frame_ptr, uint32_t frame_len) { + if (!dispatch || !frame_ptr || !frame_len) return -1; + return dispatch->enqueue_frame(ifindex, {frame_ptr, frame_len}) ? 0 : -1; +} + +int net_rx_frame(sizedptr* out_frame) { + if (!out_frame) return -1; + out_frame->ptr = 0; + out_frame->size = 0; + return 0; +} + +const uint8_t* network_get_local_mac() { + static uint8_t dummy[6] = {0,0,0,0,0,0}; + if (!dispatch) return dummy; + if (1 == 0xFF) return dummy; + const uint8_t* m = dispatch->mac(1); //kegacy + return m ? m : dummy; +} + +const uint8_t* network_get_mac(uint16_t ifindex) { + static uint8_t dummy[6] = {0,0,0,0,0,0}; + if (!dispatch) return dummy; + const uint8_t* m = dispatch->mac(ifindex); + return m ? m : dummy; +} + +uint16_t network_get_mtu(uint16_t ifindex) { + if (!dispatch) return 0; + return dispatch->mtu(ifindex); +} + +uint16_t network_get_header_size(uint16_t ifindex) { + if (!dispatch) return 0; + return dispatch->header_size(ifindex); +} + +const char* network_get_ifname(uint16_t ifindex) { + if (!dispatch) return 0; + return dispatch->ifname(ifindex); } -const net_l2l3_endpoint* network_get_local_endpoint() { - static net_l2l3_endpoint dummy = {}; - return dispatch ? &dispatch->get_local_ep() : &dummy; +const char* network_get_hw_ifname(uint16_t ifindex) { + if (!dispatch) return 0; + return dispatch->hw_ifname(ifindex); +} + +size_t network_nic_count() { + if (!dispatch) return 0; + return dispatch->nic_count(); } void network_net_set_pid(uint16_t pid) { @@ -47,6 +90,10 @@ uint16_t network_net_get_pid() { return dispatch ? dispatch->get_net_pid() : UINT16_MAX; } +void network_dump_interfaces() { + if (dispatch) dispatch->dump_interfaces(); +} + driver_module net_module = (driver_module){ .name = "net", .mount = "/net", diff --git a/kernel/networking/network.h b/kernel/networking/network.h index 1726d7f7..b7e82b6c 100644 --- a/kernel/networking/network.h +++ b/kernel/networking/network.h @@ -8,7 +8,7 @@ extern "C" { #include "net/network_types.h" #include "dev/driver_base.h" -#define NET_IRQ 32 +#define NET_IRQ_BASE 40 //TODO: consider using the system MTU here #define MAX_PACKET_SIZE 0x1000 @@ -16,16 +16,26 @@ void network_net_set_pid(uint16_t pid); uint16_t network_net_get_pid(); bool network_init(); -void network_handle_download_interrupt(); -void network_handle_upload_interrupt(); +void network_handle_download_interrupt_nic(uint16_t nic_id); +void network_handle_upload_interrupt_nic(uint16_t nic_id); int network_net_task_entry(int argc, char* argv[]); int net_tx_frame(uintptr_t frame_ptr, uint32_t frame_len); +int net_tx_frame_on(uint16_t ifindex, uintptr_t frame_ptr, uint32_t frame_len); int net_rx_frame(sizedptr *out_frame); -const net_l2l3_endpoint* network_get_local_endpoint(); +const uint8_t* network_get_local_mac(void); +const uint8_t* network_get_mac(uint16_t ifindex); +uint16_t network_get_mtu(uint16_t ifindex); +uint16_t network_get_header_size(uint16_t ifindex); +const char* network_get_ifname(uint16_t ifindex); +const char* network_get_hw_ifname(uint16_t ifindex); +size_t network_nic_count(void); + void network_update_local_ip(uint32_t ip); +void network_dump_interfaces(void); + extern driver_module net_module; #ifdef __cplusplus diff --git a/kernel/networking/network_dispatch.cpp b/kernel/networking/network_dispatch.cpp index ff24e3b3..11133fb3 100644 --- a/kernel/networking/network_dispatch.cpp +++ b/kernel/networking/network_dispatch.cpp @@ -1,139 +1,195 @@ #include "network_dispatch.hpp" -#include "network.h" #include "drivers/virtio_net_pci/virtio_net_pci.hpp" +#include "drivers/net_bus.hpp" #include "memory/page_allocator.h" #include "net/link_layer/eth.h" #include "net/network_types.h" #include "port_manager.h" #include "std/memory.h" +#include "std/std.h" +#include "console/kio.h" +#include "networking/interface_manager.h" +#include "syscalls/syscalls.h" #include "process/scheduler.h" -extern void sleep(uint64_t ms); -extern void* malloc(uint64_t size); -extern void free(void *ptr, uint64_t size); +#define RX_INTR_BATCH_LIMIT 32 +#define TASK_RX_BATCH_LIMIT 32 +#define TASK_TX_BATCH_LIMIT 32 -static uint16_t g_net_pid = 0xFFFF; -static uint8_t recv_buffer[MAX_PACKET_SIZE]; NetworkDispatch::NetworkDispatch() - : ports(UINT16_MAX + 1), - driver(nullptr), - tx_queue(QUEUE_CAPACITY), - rx_queue(QUEUE_CAPACITY) { - for (uint32_t i = 0; i <= UINT16_MAX; ++i) - ports[i] = UINT16_MAX; - - memset(local_mac.mac, 0, sizeof(local_mac.mac)); + nic_num = 0; + g_net_pid = 0xFFFF; + for (int i = 0; i <= (int)MAX_L2_INTERFACES; ++i) ifindex_to_nicid[i] = 0xFF; } bool NetworkDispatch::init() { - driver = VirtioNetDriver::try_init(); - if (!driver) return false; - driver->get_mac(&local_mac); - return true; + if (net_bus_init() <= 0) return false; + if (!register_all_from_bus()) return false; + + l3_init_localhost_ipv4(); + l3_init_localhost_ipv6(); + ifmgr_autoconfig_all_l2(); + + dump_interfaces(); + + for (int ix = 1; ix <= (int)MAX_L2_INTERFACES; ++ix){ + int nid = nic_for_ifindex((uint8_t)ix); + if (nid >= 0) { + const char* nm = nics[nid].ifname_str; + kprintf("[net] ifindex=%i -> nic_id=%i (%s)", ix, nid, nm ); + } + } + return nic_num > 0; } -void NetworkDispatch::handle_download_interrupt() +void NetworkDispatch::handle_rx_irq(size_t nic_id) { + if (nic_id >= nic_num) return; + NetDriver* driver = nics[nic_id].drv; if (!driver) return; - - sizedptr raw = driver->handle_receive_packet(recv_buffer); - if (raw.size < sizeof(eth_hdr_t)) { - return; + for (int i = 0; i < RX_INTR_BATCH_LIMIT; ++i) { + sizedptr raw = driver->handle_receive_packet(); + if (!raw.ptr || raw.size == 0) break; + if (raw.size < sizeof(eth_hdr_t)) { free_frame(raw); continue; } + if (!nics[nic_id].rx.enqueue(raw)) free_frame(raw); } - - sizedptr frame{0, raw.size}; - frame.ptr = reinterpret_cast( - kalloc(reinterpret_cast(get_current_heap()), raw.size, ALIGN_16B, get_current_privilege())); - if (!frame.ptr) return; - - memcpy(reinterpret_cast(frame.ptr), recv_buffer, raw.size); - - if (!rx_queue.enqueue(frame)) - free_frame(frame); } -void NetworkDispatch::handle_upload_interrupt() +void NetworkDispatch::handle_tx_irq(size_t nic_id) { - if (driver) - driver->handle_sent_packet(); + if (nic_id >= nic_num) return; + NetDriver* driver = nics[nic_id].drv; + if (!driver) return; + driver->handle_sent_packet(); } -bool NetworkDispatch::enqueue_frame(const sizedptr &frame) +bool NetworkDispatch::enqueue_frame(uint8_t ifindex, const sizedptr& frame) { + int nic_id = nic_for_ifindex(ifindex); + if (nic_id < 0) return false; + NetDriver* driver = nics[nic_id].drv; + if (!driver) return false; if (frame.size == 0) return false; sizedptr pkt = driver->allocate_packet(frame.size); if (!pkt.ptr) return false; - void* dst = reinterpret_cast(pkt.ptr + driver->header_size); - memcpy(dst, reinterpret_cast(frame.ptr), frame.size); + uint16_t hs = nics[nic_id].hdr_sz; + void* dst = (void*)(pkt.ptr + hs); + memcpy(dst, (const void*)frame.ptr, frame.size); - if (!tx_queue.enqueue(pkt)) { - free_frame(pkt); - return false; - } + if (!nics[nic_id].tx.enqueue(pkt)) { free_frame(pkt); return false; } return true; } int NetworkDispatch::net_task() { - network_net_set_pid(get_current_proc_pid()); + set_net_pid(get_current_proc_pid()); for (;;) { bool did_work = false; - sizedptr pkt{0,0}; - if (!rx_queue.is_empty() && rx_queue.dequeue(pkt)) { - did_work = true; - eth_input(pkt.ptr, pkt.size); - free_frame(pkt); + for (size_t n = 0; n < nic_num; ++n) { + for (int i = 0; i < TASK_RX_BATCH_LIMIT; ++i) { + if (nics[n].rx.is_empty()) break; + sizedptr pkt{0,0}; + if (!nics[n].rx.dequeue(pkt)) break; + did_work = true; + eth_input(nics[n].ifindex, pkt.ptr, pkt.size); + free_frame(pkt); + } } - if (!tx_queue.is_empty() && tx_queue.dequeue(pkt)) { - did_work = true; - driver->send_packet(pkt); + for (size_t n = 0; n < nic_num; ++n) { + NetDriver* driver = nics[n].drv; + if (!driver) continue; + for (int i = 0; i < TASK_TX_BATCH_LIMIT; ++i) { + if (nics[n].tx.is_empty()) break; + sizedptr pkt{0,0}; + if (!nics[n].tx.dequeue(pkt)) break; + did_work = true; + driver->send_packet(pkt); + } } - if (!did_work) - sleep(10); + if (!did_work) sleep(10);//TODO: manage it with an event } - return 0; } -bool NetworkDispatch::dequeue_packet_for(uint16_t pid, sizedptr *out) +void NetworkDispatch::set_net_pid(uint16_t pid) { - process_t *proc = get_proc_by_pid(pid); - if (!proc || !out) return false; + g_net_pid = pid; +} - auto &buf = proc->packet_buffer; - if (buf.read_index == buf.write_index) return false; +uint16_t NetworkDispatch::get_net_pid() const +{ + return g_net_pid; +} - sizedptr stored = buf.entries[buf.read_index]; - buf.read_index = (buf.read_index + 1) % PACKET_BUFFER_CAPACITY; +size_t NetworkDispatch::nic_count() const +{ + return nic_num; +} - void *dst = kalloc(reinterpret_cast(get_current_heap()), stored.size, ALIGN_16B, get_current_privilege()); - if (!dst) return false; +const char* NetworkDispatch::ifname(uint8_t ifindex) const +{ + int nic_id = nic_for_ifindex(ifindex); + return nic_id < 0 ? nullptr : nics[nic_id].ifname_str; +} - memcpy(dst, reinterpret_cast(stored.ptr), stored.size); - out->ptr = reinterpret_cast(dst); - out->size = stored.size; +const char* NetworkDispatch::hw_ifname(uint8_t ifindex) const +{ + int nic_id = nic_for_ifindex(ifindex); + return nic_id < 0 ? nullptr : nics[nic_id].hwname_str; +} - free(reinterpret_cast(stored.ptr), stored.size); - return true; +const uint8_t* NetworkDispatch::mac(uint8_t ifindex) const +{ + int nic_id = nic_for_ifindex(ifindex); + return nic_id < 0 ? nullptr : nics[nic_id].mac_addr; +} + +uint16_t NetworkDispatch::mtu(uint8_t ifindex) const +{ + int nic_id = nic_for_ifindex(ifindex); + return nic_id < 0 ? 0 : nics[nic_id].mtu_val; +} + +uint16_t NetworkDispatch::header_size(uint8_t ifindex) const +{ + int nic_id = nic_for_ifindex(ifindex); + return nic_id < 0 ? 0 : nics[nic_id].hdr_sz; } -sizedptr NetworkDispatch::make_copy(const sizedptr &in) +l2_interface_t* NetworkDispatch::l2_at(uint8_t ifindex) const { - sizedptr out{0, 0}; - void *dst = kalloc(reinterpret_cast(get_current_heap()), in.size, ALIGN_16B, get_current_privilege()); - if (!dst) return out; + return l2_interface_find_by_index(ifindex); +} + +NetDriver* NetworkDispatch::driver_at(uint8_t ifindex) const +{ + int nic_id = nic_for_ifindex(ifindex); + return nic_id < 0 ? nullptr : nics[nic_id].drv; +} + +uint32_t NetworkDispatch::speed(uint8_t ifindex) const +{ + int nic_id = nic_for_ifindex(ifindex); + return nic_id < 0 ? 0xFFFFFFFFu : nics[nic_id].speed_mbps; +} - memcpy(dst, reinterpret_cast(in.ptr), in.size); - out.ptr = reinterpret_cast(dst); - out.size = in.size; - return out; +uint8_t NetworkDispatch::duplex(uint8_t ifindex) const +{ + int nic_id = nic_for_ifindex(ifindex); + return nic_id < 0 ? 0xFFu : nics[nic_id].duplex_mode; +} + +uint8_t NetworkDispatch::kind(uint8_t ifindex) const +{ + int nic_id = nic_for_ifindex(ifindex); + return nic_id < 0 ? 0xFFu : nics[nic_id].kind_val; } void NetworkDispatch::free_frame(const sizedptr &f) @@ -141,5 +197,196 @@ void NetworkDispatch::free_frame(const sizedptr &f) if (f.ptr) free_sized(f); } -void NetworkDispatch::set_net_pid(uint16_t pid) { g_net_pid = pid; } -uint16_t NetworkDispatch::get_net_pid() const { return g_net_pid; } +bool NetworkDispatch::register_all_from_bus() { + int n = net_bus_count(); + if (n <= 0) return false; + + for (int i = 0; i < n && nic_num < MAX_NIC; ++i) { + NetDriver* drv = net_bus_driver(i); + const char* name = net_bus_ifname(i); + const char* hw = net_bus_hw_ifname(i); + uint16_t m = net_bus_get_mtu(i); + uint16_t hs = net_bus_get_header_size(i); + uint32_t sp = net_bus_get_speed_mbps(i); + uint8_t dp = net_bus_get_duplex(i); + uint8_t kd = net_bus_get_kind(i); + uint8_t macbuf[6]; net_bus_get_mac(i, macbuf); + + if (!name) continue; + + if (!drv) { + bool is_lo0 = (name[0]=='l' && name[1]=='o' && name[2]=='0' && name[3]==0); + if (!is_lo0) continue; + uint8_t ix = l2_interface_create(name, nullptr, 0); + l2_interface_set_up(ix, true); + continue; + } + + uint16_t type_cost = + (kd == NET_IFK_LOCALHOST) ? 0 : + (kd == NET_IFK_ETH) ? 20 : + (kd == NET_IFK_WIFI) ? 40 : 30; + + uint16_t speed_cost = + (sp == 0xFFFFFFFFu) ? 10 : + (sp >= 40000u) ? 0 : + (sp >= 10000u) ? 2 : + (sp >= 1000u) ? 5 : + (sp >= 100u) ? 15 : 40; + + uint16_t duplex_cost = + (dp == 1) ? 0 : + (dp == 0) ? 20 : 5; + + uint16_t mtu_cost = + (m >= 9000u) ? 0 : + (m >= 2000u) ? 3 : + (m >= 1500u) ? 5 : + (m >= 576u) ? 15 : 100; + + uint16_t base_metric = (kd == NET_IFK_LOCALHOST) ? 0 : (uint16_t)(type_cost + speed_cost + duplex_cost + mtu_cost); + + NICCtx* c = &nics[nic_num]; + c->drv = drv; + + new ((void*)&c->tx) Queue(QUEUE_CAPACITY); + new ((void*)&c->rx) Queue(QUEUE_CAPACITY); + + strncpy(c->ifname_str, (int)sizeof(c->ifname_str), name); + strncpy(c->hwname_str, (int)sizeof(c->hwname_str), hw); + memcpy(c->mac_addr, macbuf, 6); + c->mtu_val = m; + c->hdr_sz = hs; + c->speed_mbps = sp; + c->duplex_mode = dp; + c->kind_val = kd; + + uint8_t ix = l2_interface_create(c->ifname_str, (void*)drv, base_metric); + l2_interface_set_up(ix, true); + c->ifindex = ix; + + if (ix <= MAX_L2_INTERFACES) ifindex_to_nicid[ix] = (uint8_t)nic_num; + + nic_num += 1; + } + return nic_num > 0; +} + +int NetworkDispatch::nic_for_ifindex(uint8_t ifindex) const { + if (!ifindex) return -1; + if (ifindex > MAX_L2_INTERFACES) return -1; + uint8_t nic_id = ifindex_to_nicid[ifindex]; + if (nic_id == 0xFF) return -1; + if (nic_id >= nic_num) return -1; + return (int)nic_id; +} + +void NetworkDispatch::dump_interfaces() +{ + kprintf("[net]interface dump start"); + + auto ipv6_to_str = [&](const uint8_t ip[16], char out[41]){ //TODO: move this to ipv6 file + static const char HEX[] = "0123456789abcdef"; + int p = 0; + for (int g = 0; g < 8; ++g) { + uint16_t w = (uint16_t(ip[g*2]) << 8) | uint16_t(ip[g*2 + 1]); + out[p++] = HEX[(w >> 12) & 0xF]; + out[p++] = HEX[(w >> 8) & 0xF]; + out[p++] = HEX[(w >> 4) & 0xF]; + out[p++] = HEX[w & 0xF]; + if (g != 7) out[p++] = ':'; + } + out[p] = 0; + }; + + for (uint8_t ifx = 1; ifx <= (uint8_t)MAX_L2_INTERFACES; ++ifx){ + l2_interface_t* l2 = l2_interface_find_by_index(ifx); + if (!l2) continue; + + int nid = nic_for_ifindex(ifx); + kprintf("int l2 %u: name=%s up=%u ipv4_count=%u ipv6_count=%u arp=%x nd=%x mcast4=%u mcast6=%u", + (unsigned)ifx, l2->name, l2->is_up?1:0, + (unsigned)l2->ipv4_count, (unsigned)l2->ipv6_count, + (uint64_t)(uintptr_t)l2->arp_table, (uint64_t)(uintptr_t)l2->nd_table, + (unsigned)l2->ipv4_mcast_count, (unsigned)l2->ipv6_mcast_count); + + if (nid >= 0){ + char macs[18]; + { + static const char HEX[] = "0123456789abcdef"; + int p = 0; + for (int i = 0; i < 6; ++i) { + uint8_t b = nics[nid].mac_addr[i]; + macs[p++] = HEX[b >> 4]; + macs[p++] = HEX[b & 0x0F]; + if (i != 5) macs[p++] = ':'; + } + macs[p] = 0; + } + + const char* dpx = (nics[nid].duplex_mode == 0) ? "half" : + (nics[nid].duplex_mode == 1) ? "full" : "unknown"; + + kprintf(" driver: nic_id=%u ifname=%s hw=%s mtu=%u hdr=%u mac=%s drv=%x spd=%u dup=%s kind=%u", + (unsigned)nid, + nics[nid].ifname_str[0] ? nics[nid].ifname_str : "(null)", + nics[nid].hwname_str[0] ? nics[nid].hwname_str : "(null)", + (unsigned)nics[nid].mtu_val, (unsigned)nics[nid].hdr_sz, macs, + (uint64_t)(uintptr_t)nics[nid].drv, + (unsigned)nics[nid].speed_mbps, dpx, (unsigned)nics[nid].kind_val); + } else { + kprintf(" driver: none"); + } + + kprintf(" int ipv4:"); + for (int s = 0; s < (int)MAX_IPV4_PER_INTERFACE; ++s){ + l3_ipv4_interface_t* v4 = l2->l3_v4[s]; + if (!v4) continue; + + char ip[16], mask[16], gw[16], bc[16]; + ipv4_to_string(v4->ip, ip); + ipv4_to_string(v4->mask, mask); + ipv4_to_string(v4->gw, gw); + ipv4_to_string(v4->broadcast, bc); + + char dns0[16], dns1[16], ntp0[16], ntp1[16]; + if (v4->runtime_opts_v4.dns[0]) ipv4_to_string(v4->runtime_opts_v4.dns[0], dns0); else { dns0[0]='-'; dns0[1]=0; } + if (v4->runtime_opts_v4.dns[1]) ipv4_to_string(v4->runtime_opts_v4.dns[1], dns1); else { dns1[0]='-'; dns1[1]=0; } + if (v4->runtime_opts_v4.ntp[0]) ipv4_to_string(v4->runtime_opts_v4.ntp[0], ntp0); else { ntp0[0]='-'; ntp0[1]=0; } + if (v4->runtime_opts_v4.ntp[1]) ipv4_to_string(v4->runtime_opts_v4.ntp[1], ntp1); else { ntp1[0]='-'; ntp1[1]=0; } + + kprintf(" - slot=%u l3_id=%u mode=%i ip=%s mask=%s gw=%s bcast=%s " + "mtu=%u dns=[%s,%s] ntp=[%s,%s] xid=%u lease=%us t1=%us t2=%us localhost=%u", + (unsigned)s, (unsigned)v4->l3_id, (int)v4->mode, + ip, mask, gw, bc, + (unsigned)v4->runtime_opts_v4.mtu, + dns0, dns1, + ntp0, ntp1, + (unsigned)v4->runtime_opts_v4.xid, + (unsigned)v4->runtime_opts_v4.lease, + (unsigned)v4->runtime_opts_v4.t1, + (unsigned)v4->runtime_opts_v4.t2, + v4->is_localhost ? 1u : 0u); + } + + + kprintf(" int ipv6:"); + for (int s = 0; s < (int)MAX_IPV6_PER_INTERFACE; ++s){ + l3_ipv6_interface_t* v6 = l2->l3_v6[s]; + if (!v6) continue; + char ip6[41], gw6[41]; + ipv6_to_str(v6->ip, ip6); + ipv6_to_str(v6->gateway, gw6); + uint32_t llc = (v6->kind & IPV6_ADDRK_LINK_LOCAL) ? 1u : 0u; + uint32_t gua = (v6->kind & IPV6_ADDRK_GLOBAL) ? 1u : 0u; + uint32_t en = (v6->cfg != IPV6_CFG_DISABLE) ? 1u : 0u; + kprintf(" - slot=%u l3_id=%u kind=%u cfg=%i llc=%u gua=%u en=%u ip=%s/%u gw=%s vlft=%u plft=%u tsc=%u localhost=%u", + (unsigned)s, (unsigned)v6->l3_id, (unsigned)v6->kind, (int)v6->cfg, llc, gua, en, + ip6, (unsigned)v6->prefix_len, gw6, + (unsigned)v6->valid_lifetime, (unsigned)v6->preferred_lifetime, (unsigned)v6->timestamp_created, + v6->is_localhost?1:0); + } + } + + kprintf("[net]interface dump end"); +} \ No newline at end of file diff --git a/kernel/networking/network_dispatch.hpp b/kernel/networking/network_dispatch.hpp index 9c7eddc6..4aaa0ee1 100644 --- a/kernel/networking/network_dispatch.hpp +++ b/kernel/networking/network_dispatch.hpp @@ -5,44 +5,66 @@ #include "data_struct/queue.hpp" #include "net/network_types.h" #include "net/internet_layer/ipv4.h" +#include "interface_manager.h" class NetworkDispatch { public: NetworkDispatch(); - bool init(); - - void handle_download_interrupt(); - void handle_upload_interrupt(); - bool enqueue_frame(const sizedptr&); - int net_task(); - bool dequeue_packet_for(uint16_t, sizedptr*); + void handle_rx_irq(size_t nic_id); + void handle_tx_irq(size_t nic_id); + + bool enqueue_frame(uint8_t ifindex, const sizedptr&); + + int net_task(); void set_net_pid(uint16_t pid); uint16_t get_net_pid() const; + size_t nic_count() const; - const net_l2l3_endpoint& get_local_ep() const { - static net_l2l3_endpoint ep; //TODO: locking/thread safe would be good - ep = local_mac; - ep.ip = ipv4_get_cfg()->ip; - return ep; - } - + const char* ifname(uint8_t ifindex) const; + const char* hw_ifname(uint8_t ifindex) const; + const uint8_t* mac(uint8_t ifindex) const; + uint16_t mtu(uint8_t ifindex) const; + uint16_t header_size(uint8_t ifindex) const; + l2_interface_t* l2_at(uint8_t ifindex) const; + NetDriver* driver_at(uint8_t ifindex) const; - NetDriver* driver_ptr() const { return driver; } - uint16_t header_size() const { return driver ? driver->header_size : 0; } + uint32_t speed(uint8_t ifindex) const; + uint8_t duplex(uint8_t ifindex) const; + uint8_t kind(uint8_t ifindex) const; + + void dump_interfaces(); private: - static constexpr size_t QUEUE_CAPACITY = 1024; + struct NICCtx { + NetDriver* drv; + uint8_t ifindex; + char ifname_str[16]; + char hwname_str[32]; + uint8_t mac_addr[6]; + uint16_t mtu_val; + uint16_t hdr_sz; + uint32_t speed_mbps; + uint8_t duplex_mode; + uint8_t kind_val; + Queue tx; + Queue rx; + }; - IndexMap ports; //port pid map - NetDriver* driver; - net_l2l3_endpoint local_mac; + static const size_t MAX_NIC = 16; + static const size_t QUEUE_CAPACITY = 1024; - Queue tx_queue; - Queue rx_queue; + NICCtx nics[MAX_NIC]; + size_t nic_num; + uint16_t g_net_pid; + + uint8_t ifindex_to_nicid[MAX_L2_INTERFACES + 1]; - sizedptr make_copy(const sizedptr&); void free_frame(const sizedptr&); + bool register_all_from_bus(); + void copy_str(char* dst, int cap, const char* src); + + int nic_for_ifindex(uint8_t ifindex) const; }; diff --git a/kernel/networking/port_manager.c b/kernel/networking/port_manager.c index 8cda0ed2..2e8e5df7 100644 --- a/kernel/networking/port_manager.c +++ b/kernel/networking/port_manager.c @@ -1,43 +1,42 @@ -#include "port_manager.h" -#include "types.h" #include "networking/port_manager.h" -#include "net/internet_layer/ipv4.h" +#include "types.h" #include "math/rng.h" - -static port_entry_t g_port_table[PROTO_COUNT][MAX_PORTS];//tab proto/port +#include "net/network_types.h" static inline bool proto_valid(protocol_t proto) { return (uint32_t)proto < PROTO_COUNT; } -void port_manager_init(void) { +void port_manager_init(port_manager_t* pm) { + if (!pm) return; for (uint32_t pr = 0; pr < PROTO_COUNT; ++pr) { for (uint32_t p = 0; p < MAX_PORTS; ++p) { - g_port_table[pr][p].used = false; - g_port_table[pr][p].pid = PORT_FREE_OWNER; - g_port_table[pr][p].handler = NULL; + pm->tab[pr][p].used = false; + pm->tab[pr][p].pid = PORT_FREE_OWNER; + pm->tab[pr][p].handler = NULL; } } } -int port_alloc_ephemeral(protocol_t proto, +int port_alloc_ephemeral(port_manager_t* pm, + protocol_t proto, uint16_t pid, port_recv_handler_t handler) { - if (!proto_valid(proto)) return -1; + if (!pm || !proto_valid(proto)) return -1; rng_t rng; rng_init_random(&rng); uint32_t seed = rng_next32(&rng); - uint32_t minp = (uint32_t)PORT_MIN_EPHEMERAL; - uint32_t maxp = (uint32_t)PORT_MAX_EPHEMERAL; - uint32_t range = maxp - minp + 1u; - uint32_t first = minp + (seed % range); + const uint32_t minp = (uint32_t)PORT_MIN_EPHEMERAL; + const uint32_t maxp = (uint32_t)PORT_MAX_EPHEMERAL; + const uint32_t range = maxp - minp + 1u; + const uint32_t first = minp + (seed % range); for (uint32_t i = 0; i < range; ++i) { uint32_t p = minp + ((first - minp + i) % range); - port_entry_t *e = &g_port_table[proto][p]; + port_entry_t *e = &pm->tab[proto][p]; if (!e->used) { e->used = true; e->pid = pid; @@ -48,13 +47,14 @@ int port_alloc_ephemeral(protocol_t proto, return -1; } -bool port_bind_manual(protocol_t proto, +bool port_bind_manual(port_manager_t* pm, + protocol_t proto, uint16_t port, uint16_t pid, port_recv_handler_t handler) { - if (!proto_valid(proto)) return false; - port_entry_t *e = &g_port_table[proto][port]; + if (!pm || !proto_valid(proto)) return false; + port_entry_t *e = &pm->tab[proto][port]; if (e->used) return false; e->used = true; e->pid = pid; @@ -62,12 +62,13 @@ bool port_bind_manual(protocol_t proto, return true; } -bool port_unbind(protocol_t proto, +bool port_unbind(port_manager_t* pm, + protocol_t proto, uint16_t port, uint16_t pid) { - if (!proto_valid(proto)) return false; - port_entry_t *e = &g_port_table[proto][port]; + if (!pm || !proto_valid(proto)) return false; + port_entry_t *e = &pm->tab[proto][port]; if (!e->used || e->pid != pid) return false; e->used = false; e->pid = PORT_FREE_OWNER; @@ -75,10 +76,11 @@ bool port_unbind(protocol_t proto, return true; } -void port_unbind_all(uint16_t pid) { +void port_unbind_all(port_manager_t* pm, uint16_t pid) { + if (!pm) return; for (uint32_t pr = 0; pr < PROTO_COUNT; ++pr) { for (uint32_t p = 1; p < MAX_PORTS; ++p) { - port_entry_t *e = &g_port_table[pr][p]; + port_entry_t *e = &pm->tab[pr][p]; if (e->used && e->pid == pid) { e->used = false; e->pid = PORT_FREE_OWNER; @@ -88,19 +90,18 @@ void port_unbind_all(uint16_t pid) { } } -bool port_is_bound(protocol_t proto, uint16_t port) { - if (!proto_valid(proto)) return false; - return g_port_table[proto][port].used; +bool port_is_bound(const port_manager_t* pm, protocol_t proto, uint16_t port) { + if (!pm || !proto_valid(proto)) return false; + return pm->tab[proto][port].used; } -uint16_t port_owner_of(protocol_t proto, uint16_t port) { - if (!proto_valid(proto)) return PORT_FREE_OWNER; - return g_port_table[proto][port].pid; +uint16_t port_owner_of(const port_manager_t* pm, protocol_t proto, uint16_t port) { + if (!pm || !proto_valid(proto)) return PORT_FREE_OWNER; + return pm->tab[proto][port].pid; } -port_recv_handler_t port_get_handler(protocol_t proto, uint16_t port) { - if (!proto_valid(proto)) return NULL; - return g_port_table[proto][port].used - ? g_port_table[proto][port].handler - : NULL; +port_recv_handler_t port_get_handler(const port_manager_t* pm, protocol_t proto, uint16_t port) { + if (!pm || !proto_valid(proto)) return NULL; + const port_entry_t* e = &pm->tab[proto][port]; + return e->used ? e->handler : NULL; } diff --git a/kernel/networking/port_manager.h b/kernel/networking/port_manager.h index b623d909..4c29207a 100644 --- a/kernel/networking/port_manager.h +++ b/kernel/networking/port_manager.h @@ -1,5 +1,6 @@ #pragma once #include "types.h" +#include "net/network_types.h" #ifdef __cplusplus extern "C" { @@ -18,9 +19,12 @@ typedef enum { #define PROTO_COUNT 2 typedef void (*port_recv_handler_t)( + uint8_t ifindex, + ip_version_t ipver, + const void* src_ip_addr, + const void* dst_ip_addr, uintptr_t frame_ptr, uint32_t frame_len, - uint32_t src_ip, uint16_t src_port, uint16_t dst_port); @@ -30,28 +34,33 @@ typedef struct { bool used; } port_entry_t; -void port_manager_init(); - -int port_alloc_ephemeral(protocol_t proto, - uint16_t pid, - port_recv_handler_t handler); +typedef struct { + port_entry_t tab[PROTO_COUNT][MAX_PORTS]; +} port_manager_t; -bool port_bind_manual(protocol_t proto, - uint16_t port, - uint16_t pid, - port_recv_handler_t handler); +void port_manager_init(port_manager_t* pm); -bool port_unbind(protocol_t proto, - uint16_t port, - uint16_t pid); +int port_alloc_ephemeral(port_manager_t* pm, + protocol_t proto, + uint16_t pid, + port_recv_handler_t handler); -void port_unbind_all(uint16_t pid); +bool port_bind_manual(port_manager_t* pm, + protocol_t proto, + uint16_t port, + uint16_t pid, + port_recv_handler_t handler); -bool port_is_bound(protocol_t proto, uint16_t port); +bool port_unbind(port_manager_t* pm, + protocol_t proto, + uint16_t port, + uint16_t pid); -uint16_t port_owner_of(protocol_t proto, uint16_t port); +void port_unbind_all(port_manager_t* pm, uint16_t pid); -port_recv_handler_t port_get_handler(protocol_t proto, uint16_t port); +bool port_is_bound(const port_manager_t* pm, protocol_t proto, uint16_t port); +uint16_t port_owner_of(const port_manager_t* pm, protocol_t proto, uint16_t port); +port_recv_handler_t port_get_handler(const port_manager_t* pm, protocol_t proto, uint16_t port); #ifdef __cplusplus } diff --git a/kernel/networking/processes/net_proc.c b/kernel/networking/processes/net_proc.c index 24eabe0c..dc63e11d 100644 --- a/kernel/networking/processes/net_proc.c +++ b/kernel/networking/processes/net_proc.c @@ -7,6 +7,7 @@ #include "net/network_types.h" #include "networking/network.h" +#include "networking/interface_manager.h" #include "net/link_layer/arp.h" @@ -22,33 +23,31 @@ #include "net/application_layer/sntp_daemon.h" #include "exceptions/timer.h" -extern void* malloc(uint64_t size); -extern void free(void *ptr, uint64_t size); -extern void sleep(uint64_t ms); +#include "syscalls/syscalls.h" -static uint32_t pick_probe_ip() { - const net_cfg_t *cfg = ipv4_get_cfg(); - if (!cfg || cfg->mode == NET_MODE_DISABLED || cfg->ip == 0) - return 0; - if (cfg->gw) - return cfg->gw; - uint32_t bcast = ipv4_broadcast(cfg->ip, cfg->mask); - if (bcast) - return bcast; - return ipv4_first_host(cfg->ip, cfg->mask); + +static inline int ipv4_is_loopback_u32(uint32_t ip) { + return ((ip & 0xFF000000u) == 0x7F000000u); } -static int udp_probe_server(uint32_t probe_ip, uint16_t probe_port, net_l2l3_endpoint *out_l2, net_l4_endpoint *out_l4) { - const net_l2l3_endpoint *local = network_get_local_endpoint(); - if (!local) - return 0; +static uint32_t pick_probe_ip_v4(const l3_ipv4_interface_t *ifv4) { + if (ifv4->ip && ifv4->mask) return ipv4_broadcast_calc(ifv4->ip, ifv4->mask); + return 0; +} - socket_handle_t sock = udp_socket_create(0, 0); +static int udp_probe_server(uint32_t probe_ip, uint16_t probe_port, net_l4_endpoint *out_l4) { + socket_handle_t sock = udp_socket_create(SOCK_ROLE_CLIENT, (uint16_t)get_current_proc_pid()); if (!sock) return 0; - const char greeting[] = "hello"; - if (socket_sendto_udp(sock, probe_ip, probe_port, greeting, sizeof(greeting)) < 0) { + net_l4_endpoint dst = (net_l4_endpoint){0}; + dst.ver = IP_VER4; + memcpy(dst.ip, &probe_ip, 4); + dst.port = probe_port; + + static const char greeting[] = "hello"; + if (socket_sendto_udp_ex(sock, DST_ENDPOINT, &dst, 0, greeting, sizeof(greeting)) < 0) { + socket_close_udp(sock); socket_destroy_udp(sock); return 0; } @@ -58,62 +57,51 @@ static int udp_probe_server(uint32_t probe_ip, uint16_t probe_port, net_l2l3_end const uint32_t TIMEOUT_MS = 2000; const uint32_t INTERVAL_MS = 50; int64_t recvd = 0; - uint32_t resp_ip = 0; - uint16_t resp_port = 0; + net_l4_endpoint src = (net_l4_endpoint){0}; while (waited < TIMEOUT_MS) { - recvd = socket_recvfrom_udp(sock, recv_buf, sizeof(recv_buf), &resp_ip, &resp_port); + recvd = socket_recvfrom_udp_ex(sock, recv_buf, sizeof(recv_buf), &src); if (recvd > 0) break; sleep(INTERVAL_MS); waited += INTERVAL_MS; } - if (recvd <= 0) { - socket_close_udp(sock); - socket_destroy_udp(sock); - return 0; - } - socket_close_udp(sock); socket_destroy_udp(sock); - memcpy(out_l2->mac, local->mac, 6); - out_l2->ip = resp_ip; - out_l4->ip = resp_ip; - out_l4->port = resp_port; - - return resp_ip; + if (recvd <= 0) return 0; + if (out_l4) *out_l4 = src; + return 1; } static void free_request(HTTPRequestMsg *req) { if (req->path.mem_length) free(req->path.data, req->path.mem_length); - - for (uint32_t i = 0; i < req->extra_header_count; ++i) { + for (uint32_t i = 0; i < req->extra_header_count; i++) { HTTPHeader *h = &req->extra_headers[i]; if (h->key.mem_length) free(h->key.data, h->key.mem_length); if (h->value.mem_length) free(h->value.data, h->value.mem_length); } - if (req->extra_headers) free(req->extra_headers, req->extra_header_count * sizeof(HTTPHeader)); - if (req->body.ptr && req->body.size) free((void*)req->body.ptr, req->body.size); } static void run_http_server() { + kprintf("[HTTP] server bootstrap"); uint16_t pid = get_current_proc_pid(); http_server_handle_t srv = http_server_create(pid); if (!srv) { stop_current_process(1); return; } - - if (http_server_bind(srv, 80) < 0) { + struct SockBindSpec spec = {0}; + spec.kind = BIND_ANY; + if (http_server_bind_ex(srv, &spec, 80) < 0) { http_server_destroy(srv); stop_current_process(2); return; @@ -145,9 +133,7 @@ static void run_http_server() { http_connection_handle_t conn = http_server_accept(srv); if (!conn) continue; - HTTPRequestMsg req = http_server_recv_request(srv, conn); - if (req.path.length) { char tmp[128] = {0}; uint32_t n = req.path.length < sizeof(tmp) - 1 ? req.path.length : sizeof(tmp) - 1; @@ -155,7 +141,7 @@ static void run_http_server() { kprintf("[HTTP] GET %s", tmp); } - HTTPResponseMsg res = {0}; + HTTPResponseMsg res = (HTTPResponseMsg){0}; if (req.path.length == 1 && req.path.data[0] == '/') { res.status_code = HTTP_OK; @@ -181,29 +167,50 @@ static void run_http_server() { } } -static void test_http(uint32_t ip) { - char ip_str[16]; - ipv4_to_string(ip, ip_str); +static void test_http(const net_l4_endpoint* ep) { + if (ep->ver == IP_VER4) { + uint32_t ip_u32; + memcpy(&ip_u32, ep->ip, 4); + char ip_str[16]; + ipv4_to_string(ip_u32, ip_str); + + kprintf("[HTTP] GET %s:80", ip_str); + } - kprintf("[HTTP] GET %s:80\n", ip_str); uint16_t pid = get_current_proc_pid(); http_client_handle_t cli = http_client_create(pid); - if (!cli) - return; + if (!cli) { + kprintf("[HTTP] http_client_create FAIL"); + return; + } - if (http_client_connect(cli, ip, 80) < 0) { + net_l4_endpoint e = {0}; + e.ver = IP_VER4; + memcpy(e.ip, ep->ip, 4); + e.port = 80; + + int rc = http_client_connect_ex(cli, DST_ENDPOINT, &e, 0); + if (rc < 0) { http_client_destroy(cli); return; } - HTTPRequestMsg req = {0}; + HTTPRequestMsg req = (HTTPRequestMsg){0}; req.method = HTTP_METHOD_GET; req.path = string_from_const("/"); req.headers_common.connection = string_from_const("close"); HTTPResponseMsg resp = http_client_send_request(cli, &req); - free(req.path.data, req.path.mem_length); - free(req.headers_common.connection.data, req.headers_common.connection.mem_length); + + //free(req.path.data, req.path.mem_length); + //free(req.headers_common.connection.data, req.headers_common.connection.mem_length); + + if ((int)resp.status_code < 0) { + kprintf("[HTTP] request FAIL status=%i", (int)resp.status_code); + http_client_close(cli); + http_client_destroy(cli); + return; + } if (resp.body.ptr && resp.body.size > 0) { char *body_str = (char*)malloc(resp.body.size + 1); @@ -234,74 +241,130 @@ static void test_http(uint32_t ip) { free(resp.extra_headers, resp.extra_header_count * sizeof(HTTPHeader)); } -static void print_info() { - if (!sntp_is_running()) { - create_kernel_process("sntpd", sntp_daemon_entry, 0, 0); - while (!timer_is_synchronised()); - - const net_cfg_t *cfg = ipv4_get_cfg(); - if (cfg && cfg->mode != NET_MODE_DISABLED && cfg->ip != 0) { - char ip_str[16], mask_str[16], gw_str[16]; - ipv4_to_string(cfg->ip, ip_str); - ipv4_to_string(cfg->mask, mask_str); - ipv4_to_string(cfg->gw, gw_str); - kprintf("[NET] IP: %s MASK: %s GW: %s", ip_str, mask_str, gw_str); - } - kprintf("[NET] PIDs -- NET: %i ARP: %i DHCP: %i DNS: %i SNTP: %i", - network_net_get_pid(), - arp_get_pid(), - dhcp_get_pid(), - dns_get_pid(), - sntp_get_pid()); +static void print_info_for_ifv4(const l3_ipv4_interface_t* ifv4) { + if (!ifv4 || !ifv4->ip) return; + if (ifv4->is_localhost) return; + if (ipv4_is_loopback_u32(ifv4->ip)) return; + char ip_str[16]; + char mask_str[16]; + char gw_str[16]; + ipv4_to_string(ifv4->ip, ip_str); + ipv4_to_string(ifv4->mask, mask_str); + ipv4_to_string(ifv4->gw, gw_str); + kprintf("[NET] IF l3_id=%u IP: %s MASK: %s GW: %s", (unsigned)ifv4->l3_id, ip_str, mask_str, gw_str); +} - timer_set_timezone_minutes(120); - kprintf("[TIME]timezone offset %i minutes", (int32_t)timer_get_timezone_minutes()); +static int ifv4_is_ready_nonlocal(const l3_ipv4_interface_t* ifv4) { + if (!ifv4) return 0; + if (ifv4->mode == IPV4_CFG_DISABLED) return 0; + if (!ifv4->ip) return 0; + if (ifv4->is_localhost) return 0; + if (ipv4_is_loopback_u32(ifv4->ip)) return 0; + return 1; +} - DateTime now_dt_utc, now_dt_loc; - if (timer_now_datetime(&now_dt_utc, 0)) { - char s[20]; - timer_datetime_to_string(&now_dt_utc, s, sizeof s); - kprintf("[TIME] UTC: %s", s); - } - if (timer_now_datetime(&now_dt_loc, 1)) { - char s[20]; - timer_datetime_to_string(&now_dt_loc, s, sizeof s); - kprintf("[TIME] LOCAL: %s (TZ %i min)", s, (int32_t)timer_get_timezone_minutes()); +static int any_ipv4_ready(void) { + uint8_t n_if = l2_interface_count(); + for (uint8_t i = 0; i < n_if; i++) { + l2_interface_t* l2 = l2_interface_at(i); + if (!l2 || !l2->is_up) continue; + for (uint8_t j = 0; j < MAX_IPV4_PER_INTERFACE; j++) { + l3_ipv4_interface_t* ifv4 = l2->l3_v4[j]; + if (ifv4_is_ready_nonlocal(ifv4)) return 1; } } + return 0; } -static void test_net() { - const net_cfg_t *cfg = ipv4_get_cfg(); - net_l2l3_endpoint l2 = {0}; - net_l4_endpoint srv = {0}; - - if (cfg && cfg->mode != NET_MODE_DISABLED && cfg->ip != 0) { - print_info(); - - uint32_t bcast = ipv4_broadcast(cfg->ip, cfg->mask); - char bcast_str[16]; - ipv4_to_string(bcast, bcast_str); +static void print_info() { + network_dump_interfaces(); + uint8_t n_if = l2_interface_count(); + for (uint8_t i = 0; i < n_if; i++) { + l2_interface_t* l2 = l2_interface_at(i); + if (!l2 || !l2->is_up) continue; + for (uint8_t j = 0; j < MAX_IPV4_PER_INTERFACE; j++) { + l3_ipv4_interface_t* ifv4 = l2->l3_v4[j]; + if (!ifv4_is_ready_nonlocal(ifv4)) continue; + print_info_for_ifv4(ifv4); + } + } + if (!sntp_is_running()) { + kprintf("[TIME] starting SNTP..."); + create_kernel_process("sntpd", sntp_daemon_entry, 0, 0); + uint32_t waited = 0; + const uint32_t step = 200; + const uint32_t timeout = 10000; + while (!timer_is_synchronised() && waited < timeout) { + if ((waited % 1000) == 0) kprintf("[TIME] waiting SNTP sync..."); + sleep(step); + waited += step; - kprintf("[NET] probing broadcast %s", bcast_str); + } + if (!timer_is_synchronised()) kprintf("[TIME] SNTP sync timeout, continuing"); + } + kprintf("[NET] PIDs -- NET: %i ARP: %i DHCP: %i DNS: %i SNTP: %i", + network_net_get_pid(), + arp_get_pid(), + dhcp_get_pid(), + dns_get_pid(), + sntp_get_pid()); - if (udp_probe_server(bcast, 8080, &l2, &srv)) - test_http(srv.ip); + timer_set_timezone_minutes(120); + kprintf("[TIME]timezone offset %i minutes", (int32_t)timer_get_timezone_minutes()); - sleep(2000); - run_http_server(); - return; + DateTime now_dt_utc, now_dt_loc; + if (timer_now_datetime(&now_dt_utc, 0)) { + char s[20]; + timer_datetime_to_string(&now_dt_utc, s, sizeof s); + kprintf("[TIME] UTC: %s", s); + } + if (timer_now_datetime(&now_dt_loc, 1)) { + char s[20]; + timer_datetime_to_string(&now_dt_loc, s, sizeof s); + kprintf("[TIME] LOCAL: %s (TZ %i min)", s, (int32_t)timer_get_timezone_minutes()); } +} - uint32_t fallback = pick_probe_ip(); - if (!fallback) - fallback = (192<<24)|(168<<16)|(1<<8)|255; +static void test_net_for_interface(l3_ipv4_interface_t* ifv4) { + if (!ifv4_is_ready_nonlocal(ifv4)) return; + print_info_for_ifv4(ifv4); + uint32_t probe_ip = pick_probe_ip_v4(ifv4); + if (!probe_ip) return; + char probe_str[16]; + ipv4_to_string(probe_ip, probe_str); + kprintf("[NET] probing %s (l3_id=%u)", probe_str, (unsigned)ifv4->l3_id); + net_l4_endpoint srv = (net_l4_endpoint){0}; + if (udp_probe_server(probe_ip, 8080, &srv)) { + test_http(&srv); + } else { + kprintf("[NET] no UDP responder at %s:8080 (l3_id=%u)", probe_str, (unsigned)ifv4->l3_id); + } +} - if (udp_probe_server(fallback, 8080, &l2, &srv)) - test_http(srv.ip); - else - kprintf("[NET] could not find update server\n"); +static void test_net() { + print_info(); + uint8_t n_if = l2_interface_count(); + int tested_any = 0; + for (uint8_t i = 0; i < n_if; i++) { + l2_interface_t* l2 = l2_interface_at(i); + if (!l2 || !l2->is_up) continue; + for (uint8_t j = 0; j < MAX_IPV4_PER_INTERFACE; j++) { + l3_ipv4_interface_t* ifv4 = l2->l3_v4[j]; + if (!ifv4_is_ready_nonlocal(ifv4)) continue; + test_net_for_interface(ifv4); + tested_any = 1; + } + } + run_http_server(); + if (!tested_any) { + net_l4_endpoint srv = (net_l4_endpoint){0}; + uint32_t fallback = (192<<24)|(168<<16)|(1<<8)|255; + if (udp_probe_server(fallback, 8080, &srv)) + test_http(&srv); + else + kprintf("[NET] could not find update server"); + } } static int net_test_entry(int argc, char* argv[]) { @@ -312,31 +375,29 @@ static int net_test_entry(int argc, char* argv[]) { static int ip_waiter_entry(int argc, char* argv[]) { (void)argc; (void)argv; - for (;;) { - const net_cfg_t *cfg = ipv4_get_cfg(); - if (cfg && cfg->mode != NET_MODE_DISABLED && cfg->ip != 0) { - create_kernel_process("net_test", net_test_entry, 0, 0); - break; - } + uint32_t waited = 0; + while (!any_ipv4_ready()) { + if ((waited % 1000) == 0) kprintf("[NET] ip_waiter: waiting for ipv4..."); sleep(200); + waited += 200; } + create_kernel_process("net_test", net_test_entry, 0, 0); return 0; } - process_t* launch_net_process() { - const net_cfg_t *cfg = ipv4_get_cfg(); - create_kernel_process("net_net", network_net_task_entry, 0, 0); create_kernel_process("arp_daemon", arp_daemon_entry, 0, 0); create_kernel_process("dhcp_daemon", dhcp_daemon_entry, 0, 0); + create_kernel_process("dns_daemon", dns_deamon_entry, 0, 0); - if (cfg && cfg->mode != NET_MODE_DISABLED && cfg->ip != 0) { + if (any_ipv4_ready()) { + kprintf("[NET] ipv4 ready, starting net_test"); create_kernel_process("net_test", net_test_entry, 0, 0); return NULL; } create_kernel_process("ip_waiter", ip_waiter_entry, 0, 0); return NULL; -} \ No newline at end of file +} diff --git a/kernel/pci.c b/kernel/pci.c index 3b589c82..b74d243c 100644 --- a/kernel/pci.c +++ b/kernel/pci.c @@ -84,6 +84,9 @@ struct acpi_mcfg_t { #define RSDP_SEARCH_END 0x00100000 bool initialized; +static pci_device_info g_pci_cache[256]; +static size_t g_pci_count = 0; +static bool g_pci_scanned = false; void* find_rsdp() { for (uint64_t addr = RSDP_SEARCH_START; addr < RSDP_SEARCH_END; addr += 16) { @@ -111,20 +114,52 @@ void* find_rsdp() { return 0x0; } +uint64_t pci_make_addr(uint32_t bus, uint32_t slot, uint32_t func, uint32_t offset){ + return PCI_BASE + | (((uint64_t)bus) << 20) + | (((uint64_t)slot) << 15) + | (((uint64_t)func) << 12) + | ((uint64_t)(offset & 0xFFF)); +} + void find_pci(){ if (!initialized){ initialized = true; for (uint64_t addr = PCI_BASE; addr < PCI_BASE + 0x10000000; addr += GRANULE_2MB) register_device_memory_2mb(addr, addr); } -} -uint64_t pci_make_addr(uint32_t bus, uint32_t slot, uint32_t func, uint32_t offset){ - return PCI_BASE - | (((uint64_t)bus) << 20) - | (((uint64_t)slot) << 15) - | (((uint64_t)func) << 12) - | ((uint64_t)(offset & 0xFFF)); + if (!g_pci_scanned){ + g_pci_count = 0; + + for (uint32_t bus = 0; bus < 256 && g_pci_count < (sizeof(g_pci_cache)/sizeof(g_pci_cache[0])); ++bus) { + for (uint32_t slot = 0; slot < 32 && g_pci_count < (sizeof(g_pci_cache)/sizeof(g_pci_cache[0])); ++slot) { + for (uint32_t func = 0; func < 8 && g_pci_count < (sizeof(g_pci_cache)/sizeof(g_pci_cache[0])); ++func) { + uint64_t addr = pci_make_addr(bus, slot, func, 0x00); + uint16_t vendor = read16(addr + 0x00); + if (vendor == 0xFFFF) continue; + + uint16_t device = read16(addr + 0x02); + uint8_t class_code = read8 (addr + 0x0B); + uint8_t subclass = read8 (addr + 0x0A); + uint8_t prog_if = read8 (addr + 0x09); + + g_pci_cache[g_pci_count].addr = addr; + g_pci_cache[g_pci_count].vendor = vendor; + g_pci_cache[g_pci_count].device = device; + g_pci_cache[g_pci_count].class_code = class_code; + g_pci_cache[g_pci_count].subclass = subclass; + g_pci_cache[g_pci_count].prog_if = prog_if; + ++g_pci_count; + + kprintfv("[PCI] device %u: bus=%u slot=%u func=%u ven=%x dev=%x", (unsigned)(g_pci_count-1), bus, slot, func, vendor, device); + } + } + } + + kprintfv("[PCI] devices cached: %u", (unsigned)g_pci_count); + g_pci_scanned = true; + } } uint64_t pci_get_bar_address(uint64_t base, uint8_t offset, uint8_t index){ @@ -240,13 +275,15 @@ void dump_pci_config(uint64_t base) { } void pci_enable_device(uint64_t pci_addr){ - uint32_t cmd = read16(pci_addr + PCI_COMMAND_REGISTER); - cmd |= PCI_COMMAND_MEMORY | PCI_COMMAND_REGISTER; + uint16_t cmd = read16(pci_addr + PCI_COMMAND_REGISTER); + cmd |= (uint16_t)(PCI_COMMAND_MEMORY | PCI_COMMAND_BUS); write16(pci_addr + PCI_COMMAND_REGISTER,cmd); } void pci_register(uint64_t mmio_addr, uint64_t mmio_size){ - for (uint64_t addr = mmio_addr; addr <= mmio_addr + mmio_size; addr += GRANULE_4KB) + uint64_t start = mmio_addr & ~(GRANULE_4KB - 1); + uint64_t end = (mmio_addr + mmio_size + GRANULE_4KB - 1) & ~(GRANULE_4KB - 1); + for (uint64_t addr = start; addr < end; addr += GRANULE_4KB) register_device_memory(addr,addr); } @@ -387,9 +424,9 @@ bool pci_setup_msix(uint64_t pci_addr, msix_irq_line* irq_lines, uint8_t line_si uint8_t cap_id = read8(pci_addr + cap_ptr); if (cap_id == PCI_CAPABILITY_MSIX) { // MSI-X uint16_t msg_ctrl = read16(pci_addr + cap_ptr + 0x2); - msg_ctrl &= ~(1 << 15); // Clear MSI-X Enable bit + msg_ctrl &= (uint16_t)~(1u << 15); // Clear MSI-X Enable bit write16(pci_addr + cap_ptr + 0x2, msg_ctrl); - uint16_t table_size = (msg_ctrl & 0x07FF) +1; // takes the 11 rightmost bits, its value is N-1, so add 1 to it for the full size + uint16_t table_size = (uint16_t)((msg_ctrl & 0x07FF) +1); // takes the 11 rightmost bits, its value is N-1, so add 1 to it for the full size if(line_size > table_size){ kprintf("[PCI] MSI-X only supports %i interrupts, but you tried to add %i interrupts", table_size, line_size); @@ -397,41 +434,41 @@ bool pci_setup_msix(uint64_t pci_addr, msix_irq_line* irq_lines, uint8_t line_si } uint32_t table_offset = read32(pci_addr + cap_ptr + 0x4); - uint8_t bir = table_offset & 0x7; - uint32_t table_addr_offset = table_offset & ~0x7; + uint8_t bir = (uint8_t)(table_offset & 0x7); + uint32_t table_addr_offset = (table_offset & ~0x7); uint64_t table_addr = pci_read_address_bar(pci_addr, bir); if(!table_addr){ - uint64_t bar_size; + uint64_t bar_size = 0; pci_setup_bar(pci_addr, bir, &table_addr, &bar_size); kprintf("Setting up new bar for MSI-X %x + %x",table_addr, table_addr_offset); } else kprintf("Bar %i setup at %x + %x",bir, table_addr, table_addr_offset); - msix_table_entry *msix_start = (msix_table_entry *)(uintptr_t)(table_addr + table_addr_offset); + volatile msix_table_entry *msix_start = (volatile msix_table_entry *)(uintptr_t)(table_addr + table_addr_offset); - for (uint32_t i = 0; i < line_size; i++){ - msix_table_entry *msix_entry = msix_start + i; + for (uint32_t i = 0; i < line_size; ++i){ + volatile msix_table_entry *msix_entry = msix_start + i; msix_irq_line irq_line = irq_lines[i]; uint64_t addr_full = 0x8020040 + irq_line.addr_offset; - msix_entry->msg_addr_low = addr_full & 0xFFFFFFFF; - msix_entry->msg_addr_high = addr_full >> 32; - msix_entry->msg_data = MSI_OFFSET + irq_line.irq_num; + msix_entry->msg_addr_low = (uint32_t)(addr_full & 0xFFFFFFFFu); + msix_entry->msg_addr_high = (uint32_t)(addr_full >> 32); + msix_entry->msg_data = (uint32_t)(MSI_OFFSET + irq_line.irq_num); msix_entry->vector_control = msix_entry->vector_control & ~0x1; // all bits other then the last one are reserved, so don't chnage it, just set the last bit to 0 } - msg_ctrl |= (1 << 15); // MSI-X Enable - msg_ctrl &= ~(1 << 14); // Clear Function Mask + msg_ctrl |= (uint16_t)(1u << 15); // MSI-X Enable + msg_ctrl &= ~(uint16_t)(1u << 14); // Clear Function Mask write16(pci_addr + cap_ptr + 0x2, msg_ctrl); - break; + return true; } cap_ptr = read8(pci_addr + cap_ptr + 1); } - return true; + return false; } uint8_t pci_setup_interrupts(uint64_t pci_addr, uint8_t irq_line, uint8_t amount){ @@ -453,4 +490,17 @@ uint8_t pci_setup_interrupts(uint64_t pci_addr, uint8_t irq_line, uint8_t amount } return 0; -} \ No newline at end of file +} +size_t pci_enumerate(pci_device_info* out, size_t max) { + if (!out || max == 0) return 0; + + if (!initialized || !g_pci_scanned) + find_pci(); + + size_t to_copy = g_pci_count < max ? g_pci_count : max; + for (size_t i = 0; i < to_copy; ++i) + out[i] = g_pci_cache[i]; + + kprintf("[PCI] (enumerate) served %u/ cached %u", (unsigned)to_copy, (unsigned)g_pci_count); + return to_copy; +} diff --git a/kernel/pci.h b/kernel/pci.h index 3c800afc..5b44e6fd 100644 --- a/kernel/pci.h +++ b/kernel/pci.h @@ -11,6 +11,16 @@ typedef struct { uint64_t size; } pci_device_mmio; +typedef struct { + uint64_t addr; + uint16_t vendor; + uint16_t device; + uint8_t class_code; + uint8_t subclass; + uint8_t prog_if; +} pci_device_info; + +size_t pci_enumerate(pci_device_info* out, size_t max); uint64_t find_pci_device(uint32_t vendor_id, uint32_t device_id); uint64_t pci_get_bar_address(uint64_t base, uint8_t offset, uint8_t index); uint64_t pci_setup_bar(uint64_t pci_addr, uint32_t bar_index, uint64_t *mmio_start, uint64_t *mmio_size); diff --git a/kernel/process/syscall.c b/kernel/process/syscall.c index c3491513..feef9645 100644 --- a/kernel/process/syscall.c +++ b/kernel/process/syscall.c @@ -18,6 +18,7 @@ #include "graph/tres.h" #include "memory/mmu.h" #include "loading/process_loader.h" +#include "networking/interface_manager.h" #include "bin/bin_mod.h" int syscall_depth = 0; @@ -123,22 +124,31 @@ uint64_t syscall_get_time(process_t *ctx){ } uint64_t syscall_bind_port(process_t *ctx){ - kprintf("[SYSCALL implementation error] syscall %s not implemented",__func__); - // uint16_t port = (uint16_t)ctx->PROC_X0; - // port_recv_handler_t handler = (port_recv_handler_t)ctx->PROC_X1; - // protocol_t proto = (protocol_t)ctx->PROC_X2; - // uint16_t pid = get_current_proc_pid(); - // return port_bind_manual(port, pid, proto, handler); - return 0; + + ip_version_t ipver= (ip_version_t)ctx->PROC_X0; + uint8_t l3_id = (uint8_t)ctx->PROC_X1; + uint16_t port = (uint16_t)ctx->PROC_X2; + port_recv_handler_t handler = (port_recv_handler_t)ctx->PROC_X3; + protocol_t proto = (protocol_t)ctx->PROC_X4; + uint16_t pid = get_current_proc_pid(); + + port_manager_t* pm = (ipver == IP_VER6) ? ifmgr_pm_v6(l3_id) : ifmgr_pm_v4(l3_id); + if (!pm) return 0; + + return port_bind_manual(pm, proto, port, pid, handler); } uint64_t syscall_unbind_port(process_t *ctx){ - kprintf("[SYSCALL implementation error] syscall %s not implemented",__func__); - // uint16_t port = (uint16_t)ctx->PROC_X0; - // protocol_t proto = (protocol_t)ctx->PROC_X2; - // uint16_t pid = get_current_proc_pid(); - // return port_unbind(port, proto, pid); - return 0; + ip_version_t ipver = (ip_version_t)ctx->PROC_X0; + uint8_t l3_id = (uint8_t)ctx->PROC_X1; + uint16_t port = (uint16_t)ctx->PROC_X2; + protocol_t proto = (protocol_t)ctx->PROC_X3; + uint16_t pid = get_current_proc_pid(); + + port_manager_t* pm = (ipver == IP_VER6) ? ifmgr_pm_v6(l3_id) : ifmgr_pm_v4(l3_id); + if (!pm) return 0; + + return port_unbind(pm, proto, port, pid); } uint64_t syscall_send_packet(process_t *ctx){ diff --git a/kernel/virtio/virtio_pci.c b/kernel/virtio/virtio_pci.c index 496f8ed0..f4b324bd 100644 --- a/kernel/virtio/virtio_pci.c +++ b/kernel/virtio/virtio_pci.c @@ -123,18 +123,26 @@ bool virtio_init_device(virtio_device *dev) { uint32_t queue_index = 0; uint32_t size; while ((size = select_queue(dev,queue_index))){ - uint64_t base = (uintptr_t)kalloc(dev->memory_page, 16 * size, ALIGN_4KB, MEM_PRIV_KERNEL); - uint64_t avail = (uintptr_t)kalloc(dev->memory_page, 4 + (2 * size), ALIGN_4KB, MEM_PRIV_KERNEL); - uint64_t used = (uintptr_t)kalloc(dev->memory_page, sizeof(uint16_t) * (2 + size), ALIGN_4KB, MEM_PRIV_KERNEL); - - kprintfv("[VIRTIO QUEUE %i] Device base %x",queue_index,base); - kprintfv("[VIRTIO QUEUE %i] Device avail %x",queue_index,avail); - kprintfv("[VIRTIO QUEUE %i] Device used %x",queue_index,used); - - cfg->queue_desc = base; - cfg->queue_driver = avail; - cfg->queue_device = used; - cfg->queue_enable = 1; + uint64_t desc_sz = 16ULL * size; + uint64_t avail_sz = 4ULL + 2ULL * size; + uint64_t used_sz = 4ULL + 8ULL * size; + uint64_t base = (uintptr_t)kalloc(dev->memory_page, desc_sz, ALIGN_4KB, MEM_PRIV_KERNEL); + uint64_t avail = (uintptr_t)kalloc(dev->memory_page, avail_sz, ALIGN_4KB, MEM_PRIV_KERNEL); + uint64_t used = (uintptr_t)kalloc(dev->memory_page, used_sz, ALIGN_4KB, MEM_PRIV_KERNEL); + + dev->common_cfg->queue_desc = base; + dev->common_cfg->queue_driver = avail; + dev->common_cfg->queue_device = used; + + volatile struct virtq_avail* A = (volatile struct virtq_avail*)(uintptr_t)avail; + A->flags = 0; + A->idx = 0; + + volatile struct virtq_used* U = (volatile struct virtq_used*)(uintptr_t)used; + U->flags = 0; + U->idx = 0; + + dev->common_cfg->queue_enable = 1; queue_index++; } diff --git a/run_virt b/run_virt index 553c5dda..af264e64 100755 --- a/run_virt +++ b/run_virt @@ -33,7 +33,8 @@ elif [ "$OS_TYPE" = "Linux" ]; then NETDEV="-netdev user,id=net0" PRIVILEGE="" DISPLAY_MODE="sdl" - SELECTED_GPU="virtio-gpu-gl$VIRTIO_GPU_VARS" + SELECTED_GPU="virtio-gpu-pci$VIRTIO_GPU_VARS" + GL="off" else echo "Unknown OS: $OS_TYPE" >&2 exit 1 @@ -53,10 +54,11 @@ $PRIVILEGE qemu-system-aarch64 \ -cpu cortex-a72 \ -m 512M \ -kernel kernel.elf \ + -device $SELECTED_GPU \ + -display $DISPLAY_MODE${GL:+,gl=$GL} \ -device ramfb \ - -display $DISPLAY_MODE,gl=$GL \ $NETDEV \ - -device virtio-net-pci,netdev=net0,mac=52:54:00:12:34:56 \ + -device virtio-net-pci,netdev=net0,mac=52:54:00:12:34:56,speed=10000,duplex=full \ $DUMP \ -serial mon:stdio \ -drive file=disk.img,if=none,format=raw,id=hd0 \ @@ -70,4 +72,5 @@ $PRIVILEGE qemu-system-aarch64 \ -trace file=./trace \ -audiodev ${AUDIO_BACKEND},id=audio \ -d guest_errors \ + #-netdev tap,id=net1,ifname=tap1,script=no,downscript=no,vnet_hdr=off \ $ARGS diff --git a/shared/net/application_layer/csocket_http_client.cpp b/shared/net/application_layer/csocket_http_client.cpp index 975227b8..10688b4f 100644 --- a/shared/net/application_layer/csocket_http_client.cpp +++ b/shared/net/application_layer/csocket_http_client.cpp @@ -1,12 +1,8 @@ #include "csocket_http_client.h" #include "socket_http_client.hpp" #include "net/transport_layer/socket_tcp.hpp" +#include "net/transport_layer/socket.hpp" -extern "C" { - extern void* malloc(uint64_t size); - extern void free(void *ptr, uint64_t size); - extern void sleep(uint64_t ms); -} extern "C" { @@ -22,19 +18,14 @@ void http_client_destroy(http_client_handle_t h) { delete cli; } -int32_t http_client_connect(http_client_handle_t h, - uint32_t ip, - uint16_t port) -{ - if (!h) return (int32_t)SOCK_ERR_INVAL; +int32_t http_client_connect_ex(http_client_handle_t h, uint8_t dst_kind, const void* dst, uint16_t port) { + if (!h || !dst) return (int32_t)SOCK_ERR_INVAL; HTTPClient *cli = reinterpret_cast(h); - return cli->connect(ip, port); + return cli->connect(static_cast(dst_kind), dst, port); } -HTTPResponseMsg http_client_send_request(http_client_handle_t h, - const HTTPRequestMsg *req) -{ - HTTPResponseMsg empty; +HTTPResponseMsg http_client_send_request(http_client_handle_t h, const HTTPRequestMsg *req) { + HTTPResponseMsg empty{}; if (!h || !req) { empty.status_code = (HttpError)SOCK_ERR_INVAL; return empty; diff --git a/shared/net/application_layer/csocket_http_client.h b/shared/net/application_layer/csocket_http_client.h index 93919f21..8c7357c8 100644 --- a/shared/net/application_layer/csocket_http_client.h +++ b/shared/net/application_layer/csocket_http_client.h @@ -1,8 +1,6 @@ #pragma once - #include "http.h" -#include "std/string.h" -#include "std/memory.h" +#include "net/transport_layer/socket_types.h" #ifdef __cplusplus extern "C" { @@ -13,12 +11,9 @@ typedef void* http_client_handle_t; http_client_handle_t http_client_create(uint16_t pid); void http_client_destroy(http_client_handle_t h); -int32_t http_client_connect(http_client_handle_t h, - uint32_t ip, - uint16_t port); +int32_t http_client_connect_ex(http_client_handle_t h, uint8_t dst_kind, const void *dst, uint16_t port); -HTTPResponseMsg http_client_send_request(http_client_handle_t h, - const HTTPRequestMsg *req); +HTTPResponseMsg http_client_send_request(http_client_handle_t h, const HTTPRequestMsg *req); int32_t http_client_close(http_client_handle_t h); diff --git a/shared/net/application_layer/csocket_http_server.cpp b/shared/net/application_layer/csocket_http_server.cpp index 9dc0589f..d037c7c1 100644 --- a/shared/net/application_layer/csocket_http_server.cpp +++ b/shared/net/application_layer/csocket_http_server.cpp @@ -1,11 +1,8 @@ #include "csocket_http_server.h" #include "socket_http_server.hpp" +#include "net/transport_layer/socket_tcp.hpp" +#include "net/transport_layer/socket.hpp" -extern "C" { - extern void* malloc(uint64_t size); - extern void free(void *ptr, uint64_t size); - extern void sleep(uint64_t ms); -} extern "C" { @@ -21,10 +18,10 @@ void http_server_destroy(http_server_handle_t h) { delete srv; } -int32_t http_server_bind(http_server_handle_t h, uint16_t port) { - if (!h) return (int32_t)SOCK_ERR_INVAL; +int32_t http_server_bind_ex(http_server_handle_t h, const SockBindSpec *spec, uint16_t port) { + if (!h || !spec) return (int32_t)SOCK_ERR_INVAL; HTTPServer* srv = reinterpret_cast(h); - return srv->bind(port); + return srv->bind(*spec, port); } int32_t http_server_listen(http_server_handle_t h, int backlog) { @@ -40,22 +37,15 @@ http_connection_handle_t http_server_accept(http_server_handle_t h) { return reinterpret_cast(cli); } -HTTPRequestMsg http_server_recv_request(http_server_handle_t h, - http_connection_handle_t c) -{ +HTTPRequestMsg http_server_recv_request(http_server_handle_t h, http_connection_handle_t c) { HTTPRequestMsg empty{}; - if (!h || !c) { - return empty; - } + if (!h || !c) return empty; HTTPServer* srv = reinterpret_cast(h); TCPSocket* conn = reinterpret_cast(c); return srv->recv_request(conn); } -int32_t http_server_send_response(http_server_handle_t h, - http_connection_handle_t c, - const HTTPResponseMsg* res) -{ +int32_t http_server_send_response(http_server_handle_t h, http_connection_handle_t c, const HTTPResponseMsg *res) { if (!h || !c || !res) return (int32_t)SOCK_ERR_INVAL; HTTPServer* srv = reinterpret_cast(h); TCPSocket* conn = reinterpret_cast(c); diff --git a/shared/net/application_layer/csocket_http_server.h b/shared/net/application_layer/csocket_http_server.h index e46d221e..6d1beb6a 100644 --- a/shared/net/application_layer/csocket_http_server.h +++ b/shared/net/application_layer/csocket_http_server.h @@ -1,6 +1,7 @@ #pragma once #include "types.h" #include "http.h" +#include "net/transport_layer/socket_types.h" #ifdef __cplusplus extern "C" { @@ -10,26 +11,19 @@ typedef void* http_server_handle_t; typedef void* http_connection_handle_t; http_server_handle_t http_server_create(uint16_t pid); - void http_server_destroy(http_server_handle_t srv); -int32_t http_server_bind(http_server_handle_t srv, - uint16_t port); +int32_t http_server_bind_ex(http_server_handle_t srv, const struct SockBindSpec *spec, uint16_t port); -int32_t http_server_listen(http_server_handle_t srv, - int backlog); +int32_t http_server_listen(http_server_handle_t srv, int backlog); http_connection_handle_t http_server_accept(http_server_handle_t srv); -HTTPRequestMsg http_server_recv_request(http_server_handle_t srv, - http_connection_handle_t conn); +HTTPRequestMsg http_server_recv_request(http_server_handle_t srv, http_connection_handle_t conn); -int32_t http_server_send_response(http_server_handle_t srv, - http_connection_handle_t conn, - const HTTPResponseMsg* res); +int32_t http_server_send_response(http_server_handle_t srv, http_connection_handle_t conn, const HTTPResponseMsg* res); int32_t http_connection_close(http_connection_handle_t conn); - int32_t http_server_close(http_server_handle_t srv); #ifdef __cplusplus diff --git a/shared/net/application_layer/dhcp.c b/shared/net/application_layer/dhcp.c index 665f051f..a8ac3ad4 100644 --- a/shared/net/application_layer/dhcp.c +++ b/shared/net/application_layer/dhcp.c @@ -3,47 +3,70 @@ #include "net/transport_layer/udp.h" #include "net/internet_layer/ipv4.h" #include "types.h" -#include "net/transport_layer/csocket_udp.h" - -extern void* malloc(uint64_t size); -extern void free(void *ptr, uint64_t size); -extern void sleep(uint64_t ms); -sizedptr dhcp_build_packet(const dhcp_request *req, - uint8_t msg_type, - uint32_t xid) -{ +#include "syscalls/syscalls.h" + +static size_t dhcp_options_write_param_req_list(uint8_t *opt, size_t idx) { + static const uint8_t prl[] = { 1, 3, 6, 15, 42, 26, 51, 58, 59, 119 }; + size_t n = sizeof(prl); + if (idx + 2 + n >= 312) return idx; + opt[idx++] = 55; + opt[idx++] = (uint8_t)n; + for (size_t i = 0; i < n; i++) opt[idx++] = prl[i]; + return idx; +} + +sizedptr dhcp_build_packet(const dhcp_request *req, uint8_t msg_type, uint32_t xid, dhcp_req_kind kind, bool broadcast) { + dhcp_packet p; memset(&p, 0, sizeof(p)); size_t idx = 0; - p.op = 1; p.htype = 1; p.hlen = 6; p.hops = 0; - p.xid = xid; p.secs = 0; - p.flags = __builtin_bswap16(0x8000); - p.ciaddr = 0; p.yiaddr = 0; p.siaddr = 0; p.giaddr = 0; + p.op = 1; + p.htype = 1; + p.hlen = 6; + p.hops = 0; + p.xid = xid; + p.secs = 0; + p.flags = broadcast ? bswap16(0x8000) : 0; + p.ciaddr = 0; + p.yiaddr = 0; + p.siaddr = 0; + p.giaddr = 0; memcpy(p.chaddr, req->mac, 6); - p.options[idx++] = 0x63; p.options[idx++] = 0x82; - p.options[idx++] = 0x53; p.options[idx++] = 0x63; + if (msg_type == DHCPINFORM) p.ciaddr = req->offered_ip; + if (msg_type == DHCPREQUEST && (kind == DHCPK_RENEW || kind == DHCPK_REBIND)) p.ciaddr = req->offered_ip; - p.options[idx++] = 53; p.options[idx++] = 1; + p.options[idx++] = DHCP_MAGIC_COOKIE_0; + p.options[idx++] = DHCP_MAGIC_COOKIE_1; + p.options[idx++] = DHCP_MAGIC_COOKIE_2; + p.options[idx++] = DHCP_MAGIC_COOKIE_3; + + p.options[idx++] = 53; + p.options[idx++] = 1; p.options[idx++] = msg_type; - if (msg_type == DHCPREQUEST) { - p.options[idx++] = 50; p.options[idx++] = 4; - memcpy(&p.options[idx], &req->offered_ip, 4); idx += 4; + if (msg_type == DHCPREQUEST && kind == DHCPK_SELECT) { + p.options[idx++] = 50; + p.options[idx++] = 4; + memcpy(&p.options[idx], &req->offered_ip, 4); + idx += 4; if (req->server_ip) { - p.options[idx++] = 54; p.options[idx++] = 4; - memcpy(&p.options[idx], &req->server_ip, 4); idx += 4; + p.options[idx++] = 54; + p.options[idx++] = 4; + memcpy(&p.options[idx], &req->server_ip, 4); + idx += 4; } } + idx = dhcp_options_write_param_req_list(p.options, idx); + p.options[idx++] = 255; size_t dhcp_len = sizeof(dhcp_packet) - (sizeof(p.options) - idx); - uintptr_t buf = (uintptr_t)malloc(dhcp_len); - memcpy((void*)buf, &p, dhcp_len); + memcpy((void*)buf, &p, dhcp_len); return (sizedptr){ .ptr = buf, .size = (uint32_t)dhcp_len }; } @@ -51,18 +74,29 @@ dhcp_packet* dhcp_parse_frame_payload(uintptr_t frame_ptr) { return (dhcp_packet*)frame_ptr; } -uint16_t dhcp_parse_option(const dhcp_packet *p, uint16_t wanted) { +bool dhcp_has_valid_cookie(const dhcp_packet *p) { + return p->options[0] == DHCP_MAGIC_COOKIE_0 && + p->options[1] == DHCP_MAGIC_COOKIE_1 && + p->options[2] == DHCP_MAGIC_COOKIE_2 && + p->options[3] == DHCP_MAGIC_COOKIE_3; +} + +uint16_t dhcp_parse_option_bounded(const dhcp_packet *p, uint32_t payload_len, uint8_t wanted) { + if (payload_len < sizeof(dhcp_packet) - sizeof(p->options)) return UINT16_MAX; + uint32_t header_sz = sizeof(dhcp_packet) - sizeof(p->options); + if (payload_len < header_sz + 4) return UINT16_MAX; const uint8_t *opt = p->options; - size_t i= 4; - while (i < sizeof(p->options)) { + uint32_t i= 4; + uint32_t max = payload_len - header_sz; + while (i < max) { uint8_t code = opt[i++]; if (code == 0) continue; if (code == 255) break; - if (i >= sizeof(p->options)) break; + if (i >= max) break; uint8_t len = opt[i++]; - if (code == wanted) { + if ((uint32_t)i + len > max) break; + if (code == wanted) return (uint16_t)(i - 2); - } i += len; } return UINT16_MAX; @@ -70,8 +104,6 @@ uint16_t dhcp_parse_option(const dhcp_packet *p, uint16_t wanted) { uint8_t dhcp_option_len(const dhcp_packet *p, uint16_t idx) { size_t opt_size = sizeof(p->options); - if (idx == 0 || (size_t)idx + 1 >= opt_size) return 0; return p->options[idx + 1]; -} - +} \ No newline at end of file diff --git a/shared/net/application_layer/dhcp.h b/shared/net/application_layer/dhcp.h index 90c29697..caf12228 100644 --- a/shared/net/application_layer/dhcp.h +++ b/shared/net/application_layer/dhcp.h @@ -22,6 +22,10 @@ enum { }; #define DHCP_FRAME_MAX ( sizeof(eth_hdr_t) + sizeof(ipv4_hdr_t) + sizeof(udp_hdr_t) + sizeof(dhcp_packet) ) +#define DHCP_MAGIC_COOKIE_0 0x63 +#define DHCP_MAGIC_COOKIE_1 0x82 +#define DHCP_MAGIC_COOKIE_2 0x53 +#define DHCP_MAGIC_COOKIE_3 0x63 typedef struct __attribute__((packed)) { uint8_t op; @@ -47,13 +51,21 @@ typedef struct { uint32_t offered_ip; } dhcp_request; -sizedptr dhcp_build_packet(const dhcp_request *req, - uint8_t msg_type, - uint32_t xid); +typedef enum { + DHCPK_SELECT = 0, + DHCPK_RENEW = 1, + DHCPK_REBIND = 2, + DHCPK_INFORM = 3, + DHCPK_DISCOVER = 4 +} dhcp_req_kind; + +sizedptr dhcp_build_packet(const dhcp_request *req, uint8_t msg_type, uint32_t xid, dhcp_req_kind kind, bool broadcast); dhcp_packet* dhcp_parse_frame_payload(uintptr_t frame_ptr); -uint16_t dhcp_parse_option(const dhcp_packet *p, uint16_t wanted); +bool dhcp_has_valid_cookie(const dhcp_packet *p); + +uint16_t dhcp_parse_option_bounded(const dhcp_packet *p, uint32_t payload_len, uint8_t wanted); uint8_t dhcp_option_len(const dhcp_packet *p, uint16_t idx); diff --git a/shared/net/application_layer/dhcp_daemon.c b/shared/net/application_layer/dhcp_daemon.c index f71776b0..c62338c7 100644 --- a/shared/net/application_layer/dhcp_daemon.c +++ b/shared/net/application_layer/dhcp_daemon.c @@ -3,33 +3,21 @@ #include "console/kio.h" #include "std/memory.h" #include "process/scheduler.h" -#include "math/math.h" #include "math/rng.h" -#include "networking/network.h" #include "net/application_layer/dhcp.h" #include "net/internet_layer/ipv4.h" #include "net/network_types.h" #include "net/link_layer/arp.h" +#include "net/transport_layer/udp.h" #include "net/transport_layer/csocket_udp.h" -extern void sleep(uint64_t ms); -extern void* malloc(uint64_t size); -extern void free(void *ptr, uint64_t size); - -#ifndef SOCK_ROLE_SERVER -#define SOCK_ROLE_SERVER 1 -#endif - -#define DHCPDISCOVER 1 -#define DHCPOFFER 2 -#define DHCPREQUEST 3 -#define DHCPDECLINE 4 -#define DHCPACK 5 -#define DHCPNAK 6 -#define DHCPRELEASE 7 -#define DHCPINFORM 8 +#include "types.h" +#include "networking/interface_manager.h" +#include "networking/network.h" +#include "net/internet_layer/ipv4_route.h" +#include "syscalls/syscalls.h" typedef enum { DHCP_S_INIT = 0, @@ -40,326 +28,570 @@ typedef enum { DHCP_S_REBINDING } dhcp_state_t; -static dhcp_state_t g_state = DHCP_S_INIT; -static net_l2l3_endpoint g_local_ep = {0}; +typedef struct { + uint8_t ifindex; + uint8_t l3_id; + dhcp_state_t state; + uint32_t t1_left_ms; + uint32_t t2_left_ms; + uint32_t lease_left_ms; + uint32_t last_xid; + uint32_t trans_xid; + uint32_t server_ip_net; + uint8_t mac[6]; + bool mac_ok; + bool needs_inform; + socket_handle_t sock; + uint32_t retry_left_ms; + uint32_t backoff_ms; +} dhcp_if_state_t; + static volatile bool g_force_renew = false; -static uint32_t g_t1_left_ms = 0; -static uint32_t g_t2_left_ms = 0; static uint16_t g_pid_dhcpd = 0xFFFF; - -static socket_handle_t g_sock = 0; +static dhcp_if_state_t g_if[MAX_L2_INTERFACES * MAX_IPV4_PER_INTERFACE]; +static int g_if_count = 0; uint16_t dhcp_get_pid() { return g_pid_dhcpd; } bool dhcp_is_running() { return g_pid_dhcpd != 0xFFFF; } void dhcp_set_pid(uint16_t p){ g_pid_dhcpd = p; } void dhcp_force_renew() { g_force_renew = true; } -static void log_state_change(dhcp_state_t old, dhcp_state_t now){ - kprintf("[DHCP] state %i -> %i", old, now); +static uint32_t dhcp_next_backoff_ms(dhcp_if_state_t* st) { + if (st->backoff_ms == 0) st->backoff_ms = 4000; + else { + uint64_t v = (uint64_t)st->backoff_ms * 2u; + if (v > 64000u) v = 64000u; + st->backoff_ms = (uint32_t)v; + } + rng_t rng; + rng_init_random(&rng); + uint32_t jitter = (uint32_t)(rng_next32(&rng) % 2000u); + int32_t signed_jitter = (int32_t)jitter - 1000; + int64_t val = (int64_t)st->backoff_ms + signed_jitter; + if (val < 1000) val = 1000; + return (uint32_t)val; } -static void dhcp_apply_offer(dhcp_packet *p, dhcp_request *req, uint32_t xid); - -static void dhcp_tx_packet(const dhcp_request *req, - uint8_t msg_type, - uint32_t xid, - uint32_t dst_ip) -{ - sizedptr pkt = dhcp_build_packet(req, msg_type, xid); - socket_sendto_udp(g_sock, dst_ip, 67, (const void*)pkt.ptr, pkt.size); - free((void*)pkt.ptr, pkt.size); +static void dhcp_reset_backoff(dhcp_if_state_t* st) { + st->backoff_ms = 0; + st->retry_left_ms = 0; } -static void dhcp_send_discover(uint32_t xid){ - kprintf("[DHCP] discover xid=%i", xid); - dhcp_request req = {0}; - memcpy(req.mac, g_local_ep.mac, 6); - dhcp_tx_packet(&req, DHCPDISCOVER, xid, 0xFFFFFFFFu); +static bool find_state(uint8_t l3_id, int* idx) { + for (int i = 0; i < g_if_count; i++) if (g_if[i].l3_id == l3_id) { *idx = i; return true; } + return false; } -static void dhcp_send_request(const dhcp_request *req, - uint32_t xid, - bool broadcast) -{ - uint32_t dst = broadcast ? 0xFFFFFFFFu : __builtin_bswap32(req->server_ip); - //kprintf("[DHCP] request xid=%i dst=%x\n", (uint64_t)xid, (uint64_t)dst); - dhcp_tx_packet(req, DHCPREQUEST, xid, dst); +static void remove_state_at(int i) { + if (g_if[i].sock) { + socket_close_udp(g_if[i].sock); + socket_destroy_udp(g_if[i].sock); + g_if[i].sock = 0; + } + if (i < g_if_count - 1) g_if[i] = g_if[g_if_count - 1]; + g_if_count--; } -static void dhcp_send_renew(uint32_t xid) { - const net_cfg_t *cfg = ipv4_get_cfg(); - dhcp_request req = {0}; - memcpy(req.mac, g_local_ep.mac, 6); - req.offered_ip = __builtin_bswap32(cfg->ip); - req.server_ip = cfg->rt ? cfg->rt->server_ip : 0; - uint32_t dst = req.server_ip ? __builtin_bswap32(req.server_ip) : 0xFFFFFFFFu; - kprintf("[DHCP] renew xid=%i dst=%x", xid, dst); - dhcp_tx_packet(&req, DHCPREQUEST, xid, dst); +static void ensure_inventory() { + for (int i = 0; i < g_if_count;) { + uint8_t l3id = g_if[i].l3_id; + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(l3id); + if (!v4 || v4->mode == IPV4_CFG_DISABLED || !v4->l2 || !v4->l2->is_up) { remove_state_at(i); continue; } + i++; + } + uint8_t n = l2_interface_count(); + for (uint8_t ix = 0; ix < n; ix++) { + l2_interface_t* l2 = l2_interface_at(ix); + if (!l2 || !l2->is_up) continue; + for (int s = 0; s < MAX_IPV4_PER_INTERFACE; s++) { + l3_ipv4_interface_t* v4 = l2->l3_v4[s]; + if (!v4) continue; + if (v4->mode == IPV4_CFG_DISABLED) continue; + int idx; + if (find_state(v4->l3_id, &idx)) continue; + dhcp_if_state_t st; + memset(&st, 0, sizeof(st)); + st.ifindex = l2->ifindex; + st.l3_id = v4->l3_id; + st.state = DHCP_S_INIT; + st.t1_left_ms = 0; + st.t2_left_ms = 0; + st.lease_left_ms = 0; + st.last_xid = 0; + st.trans_xid = 0; + st.server_ip_net = 0; + const uint8_t* m = network_get_mac(st.ifindex); + if (m) { memcpy(st.mac, m, 6); st.mac_ok = true; } + st.needs_inform = (v4->mode == IPV4_CFG_STATIC && v4->ip != 0); + st.sock = udp_socket_create(SOCK_ROLE_SERVER, g_pid_dhcpd); + SockBindSpec spec; + memset(&spec, 0, sizeof(spec)); + spec.kind = BIND_L3; + spec.l3_id = st.l3_id; + if (socket_bind_udp_ex(st.sock, &spec, 68) != SOCK_OK) { + socket_destroy_udp(st.sock); + continue; + } + st.retry_left_ms = 0; + st.backoff_ms = 0; + g_if[g_if_count++] = st; + } + } } -static void dhcp_send_rebind(uint32_t xid) { - const net_cfg_t *cfg = ipv4_get_cfg(); - dhcp_request req = {0}; - memcpy(req.mac, g_local_ep.mac, 6); - req.offered_ip = __builtin_bswap32(cfg->ip); - req.server_ip = 0; - kprintf("[DHCP] rebind xid=%i", xid); - dhcp_tx_packet(&req, DHCPREQUEST, xid, 0xFFFFFFFFu); +static bool packet_mac_matches(const dhcp_packet *p, const uint8_t mac[6]) { + if (!mac) return true; + for (int i = 0; i < 6; i++) if (p->chaddr[i] != mac[i]) return false; + return true; } -static bool dhcp_wait_for_type(uint8_t wanted, - dhcp_packet **outp, - sizedptr *outsp, - uint32_t timeout_ms) -{ +static bool udp_wait_for_type_on(socket_handle_t sock, uint8_t wanted, uint32_t expect_xid, const uint8_t mac[6], dhcp_packet **outp, sizedptr *outsp, uint32_t timeout_ms) { uint32_t waited = 0; while(waited < timeout_ms){ uint8_t buf[1024]; - uint32_t sip; uint16_t sport; - int64_t r = socket_recvfrom_udp(g_sock, buf, sizeof(buf), &sip, &sport); - if(r > 0){ + net_l4_endpoint src; + memset(&src, 0, sizeof(src)); + int64_t r = socket_recvfrom_udp_ex(sock, buf, sizeof(buf), &src); + if (r > 0) { + if (src.port != 67) { continue; } + if ((size_t)r < sizeof(dhcp_packet) - sizeof(((dhcp_packet*)0)->options) + 4) { continue; } dhcp_packet *p = (dhcp_packet*)buf; - uint16_t idx= dhcp_parse_option(p, 53); - if (idx != UINT16_MAX && p->options[idx + 2] == wanted){ - uintptr_t copy = (uintptr_t)malloc((uint32_t)r); - memcpy((void*)copy, buf, (size_t)r); - if (outp) *outp= (dhcp_packet*)copy; - if (outsp) *outsp = (sizedptr){ copy, (uint32_t)r }; - return true; - } + + if (p->htype != 1) { continue; } + if (p->hlen != 6) { continue; } + if (!dhcp_has_valid_cookie(p)) { continue; } + if (expect_xid && p->xid != expect_xid) { continue; } + if (mac && !packet_mac_matches(p, mac)) { continue; } + uint16_t idx = dhcp_parse_option_bounded(p, (uint32_t)r, 53); + if (idx == UINT16_MAX) { continue; } + uint8_t len = p->options[idx+1]; + if (len < 1) { continue; } + if (p->options[idx+2] != wanted) { continue; } + uintptr_t copy = (uintptr_t)malloc((uint32_t)r); + memcpy((void*)copy, buf, (size_t)r); + if (outp) *outp= (dhcp_packet*)copy; + if (outsp) *outsp = (sizedptr){ copy, (uint32_t)r }; + return true; } else { sleep(50); waited += 50; } } - kprintf("[DHCP] wait timeout type=%i", wanted); return false; } -static void dhcp_fsm_once() -{ - //TODO: use a syscall for the rng - rng_t rng; - rng_init_random(&rng); - uint32_t xid_seed = rng_next32(&rng); - dhcp_state_t old = g_state; - - switch (g_state) { - - case DHCP_S_INIT: { - const net_l2l3_endpoint *le = network_get_local_endpoint(); - memcpy(g_local_ep.mac, le->mac, 6); - g_local_ep.ip = 0; - xid_seed += 0x1111; - dhcp_send_discover(xid_seed); - g_state = DHCP_S_SELECTING; - } break; - - case DHCP_S_SELECTING: { - dhcp_packet *offer = NULL; sizedptr sp = {0}; - if (!dhcp_wait_for_type(DHCPOFFER, &offer, &sp, 5000)) { - g_state = DHCP_S_INIT; - break; - } - dhcp_request req = {0}; - memcpy(req.mac, g_local_ep.mac, 6); - dhcp_apply_offer(offer, &req, xid_seed); - free((void*)sp.ptr, sp.size); - - xid_seed += 0x0101; - dhcp_send_request(&req, xid_seed, true); - g_state = DHCP_S_REQUESTING; - } break; - - case DHCP_S_REQUESTING: { - dhcp_packet *ack = NULL; sizedptr sp = {0}; - if (!dhcp_wait_for_type(DHCPACK, &ack, &sp, 5000)) { - g_state = DHCP_S_INIT; - } else { - dhcp_request dummy = {0}; - memcpy(dummy.mac, g_local_ep.mac, 6); - dhcp_apply_offer(ack, &dummy, xid_seed); - free((void*)sp.ptr, sp.size); - g_state = DHCP_S_BOUND; - } - } break; - - case DHCP_S_BOUND: { - if (g_force_renew) { - g_force_renew = false; - xid_seed += 0x2222; - dhcp_send_renew(xid_seed); - g_state = DHCP_S_RENEWING; - - } else if (g_t2_left_ms == 0) { - xid_seed += 0x3333; - dhcp_send_rebind(xid_seed); - g_state = DHCP_S_REBINDING; - - } else if (g_t1_left_ms == 0) { - xid_seed += 0x2222; - dhcp_send_renew(xid_seed); - g_state = DHCP_S_RENEWING; - } - } break; - - case DHCP_S_RENEWING: { - dhcp_packet *p = NULL; sizedptr sp = {0}; - if (dhcp_wait_for_type(DHCPACK, &p, &sp, 2000)) { - dhcp_request dummy = {0}; - memcpy(dummy.mac, g_local_ep.mac, 6); - dhcp_apply_offer(p, &dummy, xid_seed); - free((void*)sp.ptr, sp.size); - g_state = DHCP_S_BOUND; - } else { - xid_seed += 0x3333; - dhcp_send_rebind(xid_seed); - g_state = DHCP_S_REBINDING; - } - } break; - - case DHCP_S_REBINDING: { - dhcp_packet *p = NULL; sizedptr sp = {0}; - if (dhcp_wait_for_type(DHCPACK, &p, &sp, 2000)) { - dhcp_request dummy = {0}; - memcpy(dummy.mac, g_local_ep.mac, 6); - dhcp_apply_offer(p, &dummy, xid_seed); - free((void*)sp.ptr, sp.size); - g_state = DHCP_S_BOUND; +static bool udp_wait_for_ack_or_nak(socket_handle_t sock, uint32_t expect_xid, const uint8_t mac[6], dhcp_packet **outp, sizedptr *outsp, uint32_t timeout_ms, uint8_t *out_msg_type) { + uint32_t waited = 0; + while (waited < timeout_ms) { + uint8_t buf[1024]; + net_l4_endpoint src; + memset(&src, 0, sizeof(src)); + int64_t r = socket_recvfrom_udp_ex(sock, buf, sizeof(buf), &src); + if (r > 0) { + if (src.port != 67) { continue; } + if ((size_t)r < sizeof(dhcp_packet) - sizeof(((dhcp_packet*)0)->options) + 4) { continue; } + dhcp_packet* p = (dhcp_packet*)buf; + if (p->htype != 1 || p->hlen != 6) { continue; } + if (!dhcp_has_valid_cookie(p)) { continue; } + if (expect_xid && p->xid != expect_xid) { continue; } + if (mac && !packet_mac_matches(p, mac)) { continue; } + uint16_t idx = dhcp_parse_option_bounded(p, (uint32_t)r, 53); + if (idx == UINT16_MAX || p->options[idx+1] < 1) { continue; } + uint8_t mtype = p->options[idx+2]; + if (mtype != DHCPACK && mtype != DHCPNAK) { continue; } + uintptr_t copy = (uintptr_t)malloc((uint32_t)r); + memcpy((void*)copy, buf, (size_t)r); + if (outp) *outp = (dhcp_packet*)copy; + if (outsp) *outsp = (sizedptr){ copy, (uint32_t)r }; + if (out_msg_type) *out_msg_type = mtype; + return true; } else { - net_cfg_t g_net_cfg; - g_net_cfg.ip = 0; - g_net_cfg.mode = NET_MODE_DISABLED; - ipv4_set_cfg(&g_net_cfg); - g_state = DHCP_S_INIT; - } - } break; - } - - if (old != g_state) log_state_change(old, g_state); -} - -int dhcp_daemon_entry(int argc, char* argv[]){ - (void)argc; (void)argv; - g_pid_dhcpd = (uint16_t)get_current_proc_pid(); - dhcp_set_pid(g_pid_dhcpd); - g_sock = udp_socket_create(SOCK_ROLE_SERVER, g_pid_dhcpd); - if(socket_bind_udp(g_sock, 68) != 0){ - kprint("[DHCP] bind failed"); - return 1; - } - - for(;;){ - dhcp_fsm_once(); - sleep(100); - - if(g_state == DHCP_S_BOUND){ - if(g_t1_left_ms > 100) g_t1_left_ms -= 100; else g_t1_left_ms = 0; - if(g_t2_left_ms > 100) g_t2_left_ms -= 100; else g_t2_left_ms = 0; + sleep(50); + waited += 50; } } + return false; } -static void dhcp_apply_offer(dhcp_packet *p, dhcp_request *req, uint32_t xid) { - const net_cfg_t *current = ipv4_get_cfg(); - net_cfg_t cfg_local = *current; - static net_runtime_opts_t rt_static; - memset(&rt_static, 0, sizeof(rt_static)); - cfg_local.rt = &rt_static; - cfg_local.rt->xid = (uint16_t)xid; - cfg_local.mode = NET_MODE_DHCP; - +static void apply_offer_to_l3(uint8_t ifindex, uint8_t l3_id, dhcp_packet *p, sizedptr sp, uint32_t xid, dhcp_if_state_t* st) { + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(l3_id); + if (!v4) return; + net_runtime_opts_t rt_local; + memset(&rt_local, 0, sizeof(rt_local)); uint32_t yi_net = p->yiaddr; - cfg_local.ip = __builtin_bswap32(yi_net); - req->offered_ip = yi_net; - + uint32_t ip_host = bswap32(yi_net); uint16_t idx; uint8_t len; - - idx = dhcp_parse_option(p, 1); + uint32_t mask_host = 0; + + idx = dhcp_parse_option_bounded(p, sp.size, 1); if (idx != UINT16_MAX && (len = p->options[idx+1]) >= 4) { uint32_t mask_net; memcpy(&mask_net, &p->options[idx+2], 4); - cfg_local.mask = __builtin_bswap32(mask_net); + mask_host = bswap32(mask_net); } - idx = dhcp_parse_option(p, 3); + uint32_t gw_host = 0; + idx = dhcp_parse_option_bounded(p, sp.size, 3); if (idx != UINT16_MAX && (len = p->options[idx+1]) >= 4) { uint32_t gw_net; memcpy(&gw_net, &p->options[idx+2], 4); - cfg_local.gw = __builtin_bswap32(gw_net); + gw_host = bswap32(gw_net); } - idx = dhcp_parse_option(p, 6); + idx = dhcp_parse_option_bounded(p, sp.size, 6); if (idx != UINT16_MAX) { len = p->options[idx+1]; - for (int i = 0; i < 2 && (i*4 + 4) <= len; ++i) { + for (int i = 0; i < 2 && (i*4 + 4) <= len; i++) { uint32_t dns_net; memcpy(&dns_net, &p->options[idx+2 + i*4], 4); - cfg_local.rt->dns[i] = __builtin_bswap32(dns_net); + rt_local.dns[i] = bswap32(dns_net); } } - idx = dhcp_parse_option(p, 42); + idx = dhcp_parse_option_bounded(p, sp.size, 42); if (idx != UINT16_MAX) { len = p->options[idx+1]; - for (int i = 0; i < 2 && (i*4 + 4) <= len; ++i) { + for (int i = 0; i < 2 && (i*4 + 4) <= len; i++) { uint32_t ntp_net; memcpy(&ntp_net, &p->options[idx+2 + i*4], 4); - cfg_local.rt->ntp[i] = __builtin_bswap32(ntp_net); + rt_local.ntp[i] = bswap32(ntp_net); } } - idx = dhcp_parse_option(p, 26); + idx = dhcp_parse_option_bounded(p, sp.size, 26); if (idx != UINT16_MAX && p->options[idx+1] == 2) { uint16_t mtu_net; memcpy(&mtu_net, &p->options[idx+2], 2); - cfg_local.rt->mtu = __builtin_bswap16(mtu_net); + rt_local.mtu = bswap16(mtu_net); } - idx = dhcp_parse_option(p, 51); + uint32_t lease_s = 0; + idx = dhcp_parse_option_bounded(p, sp.size, 51); if (idx != UINT16_MAX && p->options[idx+1] >= 4) { uint32_t lease_net; memcpy(&lease_net, &p->options[idx+2], 4); - cfg_local.rt->lease = __builtin_bswap32(lease_net); + lease_s = bswap32(lease_net); + rt_local.lease = lease_s; } - idx = dhcp_parse_option(p, 58); + uint32_t t1_s = 0; + idx = dhcp_parse_option_bounded(p, sp.size, 58); if (idx != UINT16_MAX && p->options[idx+1] >= 4) { uint32_t t1_net; memcpy(&t1_net, &p->options[idx+2], 4); - cfg_local.rt->t1 = __builtin_bswap32(t1_net); - }else { - cfg_local.rt->t1 = cfg_local.rt->lease / 2; + t1_s = bswap32(t1_net); + } else { + if (lease_s) t1_s = lease_s / 2; } - - idx = dhcp_parse_option(p, 59); + rt_local.t1 = t1_s; + + uint32_t t2_s = 0; + idx = dhcp_parse_option_bounded(p, sp.size, 59); if (idx != UINT16_MAX && p->options[idx+1] >= 4) { uint32_t t2_net; memcpy(&t2_net, &p->options[idx+2], 4); - cfg_local.rt->t2 = __builtin_bswap32(t2_net); + t2_s = bswap32(t2_net); } else { - cfg_local.rt->t2 = (cfg_local.rt->lease / 8) * 7; + if (lease_s) t2_s = (lease_s / 8) * 7; } + rt_local.t2 = t2_s; - idx = dhcp_parse_option(p, 54); + idx = dhcp_parse_option_bounded(p, sp.size, 54); if (idx != UINT16_MAX && p->options[idx+1] >= 4) { - uint32_t srv_net; - memcpy(&srv_net, &p->options[idx+2], 4); - cfg_local.rt->server_ip = __builtin_bswap32(srv_net); - req->server_ip = srv_net; + memcpy(&st->server_ip_net, &p->options[idx+2], 4); + } + + if (rt_local.dns[0] == 0 && gw_host != 0) rt_local.dns[0] = gw_host; + rt_local.xid = (uint16_t)xid; + + l3_ipv4_update(l3_id, ip_host, mask_host, gw_host, IPV4_CFG_DHCP, &rt_local); + + l3_ipv4_interface_t* v4u = l3_ipv4_find_by_id(l3_id); + if (v4u) { + if (!v4u->routing_table) { + v4u->routing_table = ipv4_rt_create(); + } else { + ipv4_rt_clear(v4u->routing_table); + } + if (v4u->routing_table) { + if (ip_host && mask_host) { + uint32_t net = ip_host & mask_host; + ipv4_rt_add_in(v4u->routing_table, net, mask_host, 0, 10); + } + if (gw_host) { + ipv4_rt_add_in(v4u->routing_table, 0, 0, gw_host, 11); + } + } } - if (cfg_local.rt->dns[0] == 0 && cfg_local.gw != 0) { - cfg_local.rt->dns[0] = cfg_local.gw; + st->t1_left_ms = t1_s * 1000; + st->t2_left_ms = t2_s * 1000; + st->lease_left_ms = lease_s * 1000; +} + +static void dhcp_send_discover_for(dhcp_if_state_t* st) { + rng_t rng; + rng_init_random(&rng); + uint32_t xid = rng_next32(&rng); + st->trans_xid = xid; + dhcp_request req; + memset(&req, 0, sizeof(req)); + if (st->mac_ok) memcpy(req.mac, st->mac, 6); + sizedptr pkt = dhcp_build_packet(&req, DHCPDISCOVER, xid, DHCPK_DISCOVER, true); + net_l4_endpoint dst; + memset(&dst, 0, sizeof(dst)); + dst.ver = IP_VER4; + uint32_t bcast = 0xFFFFFFFFu; + memcpy(dst.ip, &bcast, 4); + dst.port = 67; + socket_sendto_udp_ex(st->sock, 0, &dst, 0, (const void*)pkt.ptr, pkt.size); + free((void*)pkt.ptr, pkt.size); +} + +static void dhcp_send_request_select_for(dhcp_if_state_t* st, const dhcp_request* base) { + sizedptr pkt = dhcp_build_packet(base, DHCPREQUEST, st->trans_xid, DHCPK_SELECT, true); + net_l4_endpoint dst; + memset(&dst, 0, sizeof(dst)); + dst.ver = IP_VER4; + uint32_t dip = 0xFFFFFFFFu; + memcpy(dst.ip, &dip, 4); + dst.port = 67; + socket_sendto_udp_ex(st->sock, 0, &dst, 0, (const void*)pkt.ptr, pkt.size); + free((void*)pkt.ptr, pkt.size); +} + +static void dhcp_send_renew_for(dhcp_if_state_t* st) { + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(st->l3_id); + if (!v4) return; + dhcp_request req; + memset(&req, 0, sizeof(req)); + if (st->mac_ok) memcpy(req.mac, st->mac, 6); + uint32_t ip_net = bswap32(v4->ip); + req.offered_ip = ip_net; + req.server_ip = st->server_ip_net; + rng_t rng; + rng_init_random(&rng); + st->trans_xid = rng_next32(&rng); + sizedptr pkt = dhcp_build_packet(&req, DHCPREQUEST, st->trans_xid, DHCPK_RENEW, st->server_ip_net == 0); + net_l4_endpoint dst; + memset(&dst, 0, sizeof(dst)); + dst.ver = IP_VER4; + uint32_t dip = st->server_ip_net ? st->server_ip_net : 0xFFFFFFFFu; + memcpy(dst.ip, &dip, 4); + dst.port = 67; + socket_sendto_udp_ex(st->sock, 0, &dst, 0, (const void*)pkt.ptr, pkt.size); + free((void*)pkt.ptr, pkt.size); +} + +static void dhcp_send_rebind_for(dhcp_if_state_t* st) { + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(st->l3_id); + if (!v4) return; + dhcp_request req; + memset(&req, 0, sizeof(req)); + if (st->mac_ok) memcpy(req.mac, st->mac, 6); + uint32_t ip_net = bswap32(v4->ip); + req.offered_ip = ip_net; + req.server_ip = 0; + rng_t rng; + rng_init_random(&rng); + st->trans_xid = rng_next32(&rng); + sizedptr pkt = dhcp_build_packet(&req, DHCPREQUEST, st->trans_xid, DHCPK_REBIND, true); + net_l4_endpoint dst; + memset(&dst, 0, sizeof(dst)); + dst.ver = IP_VER4; + uint32_t dip = 0xFFFFFFFFu; + memcpy(dst.ip, &dip, 4); + dst.port = 67; + socket_sendto_udp_ex(st->sock, 0, &dst, 0, (const void*)pkt.ptr, pkt.size); + free((void*)pkt.ptr, pkt.size); +} + +static void dhcp_send_inform_for(dhcp_if_state_t* st) { + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(st->l3_id); + if (!v4 || !v4->ip) return; + dhcp_request req; + memset(&req, 0, sizeof(req)); + if (st->mac_ok) memcpy(req.mac, st->mac, 6); + uint32_t ip_net = bswap32(v4->ip); + req.offered_ip = ip_net; + req.server_ip = 0; + rng_t rng; + rng_init_random(&rng); + uint32_t xid = rng_next32(&rng); + sizedptr pkt = dhcp_build_packet(&req, DHCPINFORM, xid, DHCPK_INFORM, true); + net_l4_endpoint dst; + memset(&dst, 0, sizeof(dst)); + dst.ver = IP_VER4; + uint32_t dip = 0xFFFFFFFFu; + memcpy(dst.ip, &dip, 4); + dst.port = 67; + socket_sendto_udp_ex(st->sock, 0, &dst, 0, (const void*)pkt.ptr, pkt.size); + free((void*)pkt.ptr, pkt.size); +} + +static void schedule_retry(dhcp_if_state_t* st) { + st->retry_left_ms = dhcp_next_backoff_ms(st); +} + +static void fsm_once_for(dhcp_if_state_t* st) { + dhcp_state_t old = st->state; + switch (st->state) { + case DHCP_S_INIT: { + if (st->retry_left_ms != 0) break; + dhcp_send_discover_for(st); + st->last_xid = st->trans_xid; + st->state = DHCP_S_SELECTING; + } break; + case DHCP_S_SELECTING: { + dhcp_packet* offer = NULL; sizedptr sp = (sizedptr){0,0}; + if (!udp_wait_for_type_on(st->sock, DHCPOFFER, st->last_xid, st->mac_ok ? st->mac : NULL, &offer, &sp, 5000)) { + st->state = DHCP_S_INIT; + schedule_retry(st); + break; + } + dhcp_request req; + memset(&req, 0, sizeof(req)); + if (st->mac_ok) memcpy(req.mac, st->mac, 6); + uint16_t idx54 = dhcp_parse_option_bounded(offer, sp.size, 54); + if (idx54 != UINT16_MAX && offer->options[idx54+1] >= 4) memcpy(&st->server_ip_net, &offer->options[idx54+2], 4); + memcpy(&req.offered_ip, &offer->yiaddr, 4); + req.server_ip = st->server_ip_net; + free((void*)sp.ptr, sp.size); + dhcp_send_request_select_for(st, &req); + st->state = DHCP_S_REQUESTING; + dhcp_reset_backoff(st); + } break; + case DHCP_S_REQUESTING: { + dhcp_packet* resp = NULL; sizedptr sp = (sizedptr){0,0}; uint8_t mtype = 0; + if (!udp_wait_for_ack_or_nak(st->sock, st->last_xid, st->mac_ok ? st->mac : NULL, &resp, &sp, 5000, &mtype)) { + st->state = DHCP_S_INIT; + schedule_retry(st); + } else { + if (mtype == DHCPACK) { + apply_offer_to_l3(st->ifindex, st->l3_id, resp, sp, st->last_xid, st); + free((void*)sp.ptr, sp.size); + st->state = DHCP_S_BOUND; + dhcp_reset_backoff(st); + } else { + free((void*)sp.ptr, sp.size); + l3_ipv4_update(st->l3_id, 0, 0, 0, IPV4_CFG_DHCP, NULL); + st->t1_left_ms = 0; + st->t2_left_ms = 0; + st->lease_left_ms = 0; + st->server_ip_net = 0; + st->state = DHCP_S_INIT; + schedule_retry(st); + } + } + } break; + case DHCP_S_BOUND: { + if (g_force_renew) { + g_force_renew = false; + dhcp_send_renew_for(st); + st->last_xid = st->trans_xid; + st->state = DHCP_S_RENEWING; + } else if (st->t2_left_ms == 0 && st->lease_left_ms != 0) { + dhcp_send_rebind_for(st); + st->last_xid = st->trans_xid; + st->state = DHCP_S_REBINDING; + } else if (st->t1_left_ms == 0 && st->lease_left_ms != 0) { + dhcp_send_renew_for(st); + st->last_xid = st->trans_xid; + st->state = DHCP_S_RENEWING; + } + } break; + case DHCP_S_RENEWING: { + dhcp_packet* p = NULL; sizedptr sp = (sizedptr){0,0}; uint8_t mtype = 0; + if (udp_wait_for_ack_or_nak(st->sock, st->last_xid, st->mac_ok ? st->mac : NULL, &p, &sp, 2000, &mtype)) { + if (mtype == DHCPACK) { + apply_offer_to_l3(st->ifindex, st->l3_id, p, sp, st->last_xid, st); + free((void*)sp.ptr, sp.size); + st->state = DHCP_S_BOUND; + dhcp_reset_backoff(st); + } else { + free((void*)sp.ptr, sp.size); + l3_ipv4_update(st->l3_id, 0, 0, 0, IPV4_CFG_DHCP, NULL); + st->t1_left_ms = 0; + st->t2_left_ms = 0; + st->lease_left_ms = 0; + st->server_ip_net = 0; + st->state = DHCP_S_INIT; + schedule_retry(st); + } + } else { + dhcp_send_rebind_for(st); + st->last_xid = st->trans_xid; + st->state = DHCP_S_REBINDING; + } + } break; + case DHCP_S_REBINDING: { + dhcp_packet* p = NULL; sizedptr sp = (sizedptr){0,0}; uint8_t mtype = 0; + if (udp_wait_for_ack_or_nak(st->sock, st->last_xid, st->mac_ok ? st->mac : NULL, &p, &sp, 2000, &mtype)) { + if (mtype == DHCPACK) { + apply_offer_to_l3(st->ifindex, st->l3_id, p, sp, st->last_xid, st); + free((void*)sp.ptr, sp.size); + st->state = DHCP_S_BOUND; + dhcp_reset_backoff(st); + } else { + free((void*)sp.ptr, sp.size); + l3_ipv4_update(st->l3_id, 0, 0, 0, IPV4_CFG_DHCP, NULL); + st->t1_left_ms = 0; + st->t2_left_ms = 0; + st->lease_left_ms = 0; + st->server_ip_net = 0; + st->state = DHCP_S_INIT; + schedule_retry(st); + } + } else { + l3_ipv4_update(st->l3_id, 0, 0, 0, IPV4_CFG_DHCP, NULL); + st->t1_left_ms = 0; + st->t2_left_ms = 0; + st->lease_left_ms = 0; + st->server_ip_net = 0; + st->state = DHCP_S_INIT; + schedule_retry(st); + } + } break; } + if (old != st->state) kprintf("[DHCP] ifx=%i l3=%i state %i -> %i", st->ifindex, st->l3_id, old, st->state); +} - uint32_t bcast = ipv4_broadcast(cfg_local.ip, cfg_local.mask); - static const uint8_t bmac[6] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}; - arp_table_put(bcast, bmac, 0, true); +static void tick_timers() { + for (int i = 0; i < g_if_count; i++) { + if (g_if[i].state == DHCP_S_BOUND) { + if (g_if[i].t1_left_ms > 100) g_if[i].t1_left_ms -= 100; else if (g_if[i].t1_left_ms) g_if[i].t1_left_ms = 0; + if (g_if[i].t2_left_ms > 100) g_if[i].t2_left_ms -= 100; else if (g_if[i].t2_left_ms) g_if[i].t2_left_ms = 0; + if (g_if[i].lease_left_ms > 100) g_if[i].lease_left_ms -= 100; else if (g_if[i].lease_left_ms) g_if[i].lease_left_ms = 0; + } + if (g_if[i].retry_left_ms > 100) g_if[i].retry_left_ms -= 100; else if (g_if[i].retry_left_ms) g_if[i].retry_left_ms = 0; + } +} - ipv4_set_cfg(&cfg_local); +static void maybe_send_inform() { + for (int i = 0; i < g_if_count; i++) { + if (!g_if[i].needs_inform) continue; + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(g_if[i].l3_id); + if (!v4 || v4->mode != IPV4_CFG_STATIC || !v4->ip) { g_if[i].needs_inform = false; continue; } + dhcp_send_inform_for(&g_if[i]); + g_if[i].needs_inform = false; + } +} - g_t1_left_ms = cfg_local.rt->t1 * 1000; - g_t2_left_ms = cfg_local.rt->t2 * 1000; +int dhcp_daemon_entry(int argc, char* argv[]) { + (void)argc; (void)argv; + g_pid_dhcpd = (uint16_t)get_current_proc_pid(); + dhcp_set_pid(g_pid_dhcpd); + for (;;) { + ensure_inventory(); + if (g_if_count == 0) { sleep(250); continue; } + for (int i = 0; i < g_if_count; i++) { + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(g_if[i].l3_id); + if (!v4) continue; + if (v4->mode == IPV4_CFG_DHCP) fsm_once_for(&g_if[i]); + } + maybe_send_inform(); + sleep(100); + tick_timers(); + } } diff --git a/shared/net/application_layer/dns.c b/shared/net/application_layer/dns.c index 9e27c8f3..08328544 100644 --- a/shared/net/application_layer/dns.c +++ b/shared/net/application_layer/dns.c @@ -2,11 +2,12 @@ #include "std/memory.h" #include "math/rng.h" #include "process/scheduler.h" +#include "types.h" #include "net/internet_layer/ipv4.h" +#include "networking/interface_manager.h" #include "dns_daemon.h" -#include "types.h" +#include "syscalls/syscalls.h" -extern void sleep(uint64_t ms); static uint32_t encode_dns_qname(uint8_t* dst, const char* name){ uint32_t index = 0; @@ -66,7 +67,37 @@ static dns_result_t parse_dns_a_record(uint8_t* buffer, uint32_t buffer_len, uin return DNS_ERR_NO_ANSWER; } -static dns_result_t perform_dns_query_once(socket_handle_t sock, uint32_t dns_ip_host, const char* name, uint32_t timeout_ms, uint32_t* out_ip){ +static dns_result_t parse_dns_aaaa_record(uint8_t* buffer, uint32_t buffer_len, uint16_t message_id, uint8_t out_ipv6[16]){ + if (buffer_len < 12) return DNS_ERR_FORMAT; + if (rd_be16(buffer+0) != message_id) return DNS_ERR_FORMAT; + uint16_t flags = rd_be16(buffer+2); + uint16_t question_count = rd_be16(buffer+4); + uint16_t answer_count = rd_be16(buffer+6); + if ((flags & 0x000F) == 3) return DNS_ERR_NXDOMAIN; + uint32_t offset = 12; + for (uint16_t i = 0; i < question_count; ++i){ + offset = skip_dns_name(buffer, buffer_len, offset); + if (offset + 4 > buffer_len) return DNS_ERR_FORMAT; + offset += 4; + } + for (uint16_t i = 0; i < answer_count; ++i){ + offset = skip_dns_name(buffer, buffer_len, offset); + if (offset + 10 > buffer_len) return DNS_ERR_FORMAT; + uint16_t type = rd_be16(buffer+offset+0); + uint16_t klass = rd_be16(buffer+offset+2); + uint16_t rdlength = rd_be16(buffer+offset+8); + offset += 10; + if (offset + rdlength > buffer_len) return DNS_ERR_FORMAT; + if (type == 28 && klass == 1 && rdlength == 16){ + memcpy(out_ipv6, buffer+offset, 16); + return DNS_OK; + } + offset += rdlength; + } + return DNS_ERR_NO_ANSWER; +} + +static dns_result_t perform_dns_query_once_a(socket_handle_t sock, uint32_t dns_ip_host, const char* name, uint32_t timeout_ms, uint32_t* out_ip){ uint8_t request_buffer[512]; memset(request_buffer,0,sizeof(request_buffer)); rng_t rng; rng_init_random(&rng); uint16_t message_id = (uint16_t)(rng_next32(&rng) & 0xFFFF); @@ -78,14 +109,21 @@ static dns_result_t perform_dns_query_once(socket_handle_t sock, uint32_t dns_ip wr_be16(request_buffer+offset+0, 1); wr_be16(request_buffer+offset+2, 1); offset += 4; - int64_t sent = socket_sendto_udp(sock, dns_ip_host, 53, request_buffer, offset); + + net_l4_endpoint dst = {0}; + dst.ver = IP_VER4; + memcpy(dst.ip, &dns_ip_host, 4); + dst.port = 53; + + int64_t sent = socket_sendto_udp_ex(sock, DST_ENDPOINT, &dst, 0, request_buffer, offset); if (sent < 0) return DNS_ERR_SEND; + uint32_t waited_ms = 0; while (waited_ms < timeout_ms){ uint8_t response_buffer[512]; - uint32_t source_ip; uint16_t source_port; - int64_t received = socket_recvfrom_udp(sock, response_buffer, sizeof(response_buffer), &source_ip, &source_port); - if (received > 0 && source_port == 53 && source_ip == dns_ip_host){ + net_l4_endpoint source; + int64_t received = socket_recvfrom_udp_ex(sock, response_buffer, sizeof(response_buffer), &source); + if (received > 0 && source.port == 53 && source.ver == IP_VER4 && *(uint32_t*)source.ip == dns_ip_host){ uint32_t ip_host; dns_result_t pr = parse_dns_a_record(response_buffer, (uint32_t)received, message_id, &ip_host); if (pr == DNS_OK){ *out_ip = ip_host; return DNS_OK; } @@ -98,22 +136,130 @@ static dns_result_t perform_dns_query_once(socket_handle_t sock, uint32_t dns_ip return DNS_ERR_TIMEOUT; } -dns_result_t dns_resolve_a(const char* hostname, uint32_t* out_ip, dns_server_sel_t which, uint32_t timeout_ms){ - const net_cfg_t* cfg = ipv4_get_cfg(); - if (!cfg || !cfg->rt) return DNS_ERR_NO_DNS; - uint32_t dns_primary = cfg->rt->dns[0]; - uint32_t dns_secondary = cfg->rt->dns[1]; - if (which == DNS_USE_PRIMARY && dns_primary == 0) return DNS_ERR_NO_DNS; - if (which == DNS_USE_SECONDARY && dns_secondary == 0) return DNS_ERR_NO_DNS; - if (which == DNS_USE_BOTH && dns_primary == 0 && dns_secondary == 0) return DNS_ERR_NO_DNS; +static dns_result_t perform_dns_query_once_aaaa(socket_handle_t sock, uint32_t dns_ip_host, const char* name, uint32_t timeout_ms, uint8_t out_ipv6[16]){ + uint8_t request_buffer[512]; memset(request_buffer, 0, sizeof(request_buffer)); + rng_t rng; rng_init_random(&rng); + uint16_t message_id = (uint16_t)(rng_next32(&rng) & 0xFFFF); + wr_be16(request_buffer+0, message_id); + wr_be16(request_buffer+2, 0x0100); + wr_be16(request_buffer+4, 1); + uint32_t offset = 12; + offset += encode_dns_qname(request_buffer+offset, name); + wr_be16(request_buffer+offset+0, 28); + wr_be16(request_buffer+offset+2, 1); + offset += 4; + + net_l4_endpoint dst = {0}; + dst.ver = IP_VER4; + memcpy(dst.ip, &dns_ip_host, 4); + dst.port = 53; + + int64_t sent = socket_sendto_udp_ex(sock, DST_ENDPOINT, &dst, 0, request_buffer, offset); + if (sent < 0) return DNS_ERR_SEND; + + uint32_t waited_ms = 0; + while (waited_ms < timeout_ms){ + uint8_t response_buffer[512]; + net_l4_endpoint source; + int64_t received = socket_recvfrom_udp_ex(sock, response_buffer, sizeof(response_buffer), &source); + if (received > 0 && source.port == 53 && source.ver == IP_VER4 && *(uint32_t*)source.ip == dns_ip_host){ + dns_result_t pr = parse_dns_aaaa_record(response_buffer, (uint32_t)received, message_id, out_ipv6); + if (pr == DNS_OK) return DNS_OK; + if (pr == DNS_ERR_NXDOMAIN) return pr; + } else { + sleep(50); + waited_ms += 50; + } + } + return DNS_ERR_TIMEOUT; +} + +static bool pick_dns_on_l3(uint8_t l3_id, uint32_t* out_primary, uint32_t* out_secondary){ + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(l3_id); + if (!v4) return false; + uint32_t p = v4->runtime_opts_v4.dns[0]; + uint32_t s = v4->runtime_opts_v4.dns[1]; + if (out_primary) *out_primary = p; + if (out_secondary) *out_secondary = s; + return (p != 0) || (s != 0); +} + +static bool pick_dns_first_iface(uint8_t* out_l3, uint32_t* out_primary, uint32_t* out_secondary){ + uint8_t n = l2_interface_count(); + for (uint8_t i = 0; i < n; ++i){ + l2_interface_t* l2 = l2_interface_at(i); + if (!l2) continue; + for (int s = 0; s < MAX_IPV4_PER_INTERFACE; ++s){ + l3_ipv4_interface_t* v4 = l2->l3_v4[s]; + if (!v4 || v4->mode == IPV4_CFG_DISABLED) continue; + + uint32_t p = v4->runtime_opts_v4.dns[0]; + uint32_t q = v4->runtime_opts_v4.dns[1]; + if (p || q){ + if (out_l3) *out_l3 = v4->l3_id; + if (out_primary) *out_primary = p; + if (out_secondary)*out_secondary= q; + return true; + } + } + } + return false; +} + +static dns_result_t query_with_selection_a(uint32_t primary, uint32_t secondary, dns_server_sel_t which, const char* hostname, uint32_t timeout_ms, uint32_t* out_ip){ + if (which == DNS_USE_PRIMARY && primary == 0) return DNS_ERR_NO_DNS; + if (which == DNS_USE_SECONDARY && secondary == 0) return DNS_ERR_NO_DNS; + if (which == DNS_USE_BOTH && primary == 0 && secondary == 0) return DNS_ERR_NO_DNS; + socket_handle_t sock = dns_socket_handle(); + if (sock == 0) return DNS_ERR_SOCKET; + dns_result_t res = DNS_ERR_NO_DNS; + if (which == DNS_USE_PRIMARY) res = perform_dns_query_once_a(sock, primary, hostname, timeout_ms, out_ip); + else if (which == DNS_USE_SECONDARY) res = perform_dns_query_once_a(sock, secondary, hostname, timeout_ms, out_ip); + else { + res = perform_dns_query_once_a(sock, primary ? primary : secondary, hostname, timeout_ms, out_ip); + if (res != DNS_OK && secondary && secondary != primary) res = perform_dns_query_once_a(sock, secondary, hostname, timeout_ms, out_ip); + } + return res; +} + +static dns_result_t query_with_selection_aaaa(uint32_t primary, uint32_t secondary, dns_server_sel_t which, const char* hostname, uint32_t timeout_ms, uint8_t out_ipv6[16]){ + if (which == DNS_USE_PRIMARY && primary == 0) return DNS_ERR_NO_DNS; + if (which == DNS_USE_SECONDARY && secondary == 0) return DNS_ERR_NO_DNS; + if (which == DNS_USE_BOTH && primary == 0 && secondary == 0) return DNS_ERR_NO_DNS; socket_handle_t sock = dns_socket_handle(); if (sock == 0) return DNS_ERR_SOCKET; dns_result_t res = DNS_ERR_NO_DNS; - if (which == DNS_USE_PRIMARY) res = perform_dns_query_once(sock, dns_primary, hostname, timeout_ms, out_ip); - else if (which == DNS_USE_SECONDARY) res = perform_dns_query_once(sock, dns_secondary, hostname, timeout_ms, out_ip); + if (which == DNS_USE_PRIMARY) res = perform_dns_query_once_aaaa(sock, primary, hostname, timeout_ms, out_ipv6); + else if (which == DNS_USE_SECONDARY) res = perform_dns_query_once_aaaa(sock, secondary, hostname, timeout_ms, out_ipv6); else { - res = perform_dns_query_once(sock, dns_primary ? dns_primary : dns_secondary, hostname, timeout_ms, out_ip); - if (res != DNS_OK && dns_secondary && dns_secondary != dns_primary) res = perform_dns_query_once(sock, dns_secondary, hostname, timeout_ms, out_ip); + res = perform_dns_query_once_aaaa(sock, primary ? primary : secondary, hostname, timeout_ms, out_ipv6); + if (res != DNS_OK && secondary && secondary != primary) res = perform_dns_query_once_aaaa(sock, secondary, hostname, timeout_ms, out_ipv6); } return res; } + +dns_result_t dns_resolve_a(const char* hostname, uint32_t* out_ip, dns_server_sel_t which, uint32_t timeout_ms){ + uint8_t l3 = 0; + uint32_t p = 0, s = 0; + if (!pick_dns_first_iface(&l3, &p, &s)) return DNS_ERR_NO_DNS; + return query_with_selection_a(p, s, which, hostname, timeout_ms, out_ip); +} + +dns_result_t dns_resolve_a_on_l3(uint8_t l3_id, const char* hostname, uint32_t* out_ip, dns_server_sel_t which, uint32_t timeout_ms){ + uint32_t p = 0, s = 0; + if (!pick_dns_on_l3(l3_id, &p, &s)) return DNS_ERR_NO_DNS; + return query_with_selection_a(p, s, which, hostname, timeout_ms, out_ip); +} + +dns_result_t dns_resolve_aaaa(const char* hostname, uint8_t out_ipv6[16], dns_server_sel_t which, uint32_t timeout_ms){ + uint8_t l3 = 0; + uint32_t p = 0, s = 0; + if (!pick_dns_first_iface(&l3, &p, &s)) return DNS_ERR_NO_DNS; + return query_with_selection_aaaa(p, s, which, hostname, timeout_ms, out_ipv6); +} + +dns_result_t dns_resolve_aaaa_on_l3(uint8_t l3_id, const char* hostname, uint8_t out_ipv6[16], dns_server_sel_t which, uint32_t timeout_ms){ + uint32_t p = 0, s = 0; + if (!pick_dns_on_l3(l3_id, &p, &s)) return DNS_ERR_NO_DNS; + return query_with_selection_aaaa(p, s, which, hostname, timeout_ms, out_ipv6); +} \ No newline at end of file diff --git a/shared/net/application_layer/dns.h b/shared/net/application_layer/dns.h index 8d429627..d85ce336 100644 --- a/shared/net/application_layer/dns.h +++ b/shared/net/application_layer/dns.h @@ -21,6 +21,10 @@ typedef enum { } dns_server_sel_t; dns_result_t dns_resolve_a(const char* hostname, uint32_t* out_ip, dns_server_sel_t which, uint32_t timeout_ms); +dns_result_t dns_resolve_a_on_l3(uint8_t l3_id, const char* hostname, uint32_t* out_ip, dns_server_sel_t which, uint32_t timeout_ms); +dns_result_t dns_resolve_aaaa(const char* hostname, uint8_t out_ipv6[16], dns_server_sel_t which, uint32_t timeout_ms); +dns_result_t dns_resolve_aaaa_on_l3(uint8_t l3_id, const char* hostname, uint8_t out_ipv6[16], dns_server_sel_t which, uint32_t timeout_ms); + #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/shared/net/application_layer/dns_daemon.c b/shared/net/application_layer/dns_daemon.c index b1cf796d..99e84aec 100644 --- a/shared/net/application_layer/dns_daemon.c +++ b/shared/net/application_layer/dns_daemon.c @@ -1,8 +1,7 @@ #include "dns_daemon.h" #include "process/scheduler.h" -#include "std/memory.h" - -extern void sleep(uint64_t ms); +#include "syscalls/syscalls.h" +#include "net/transport_layer/socket_types.h" static uint16_t g_pid_dnsd = 0xFFFF; static socket_handle_t g_sock = 0; @@ -15,7 +14,7 @@ socket_handle_t dns_socket_handle(void){ return g_sock; } int dns_deamon_entry(int argc, char* argv[]){ (void)argc; (void)argv; dns_set_pid(get_current_proc_pid()); - g_sock = udp_socket_create(0, g_pid_dnsd); + g_sock = udp_socket_create(SOCK_ROLE_CLIENT, g_pid_dnsd); for(;;){ sleep(250); } return 1; } diff --git a/shared/net/application_layer/http.c b/shared/net/application_layer/http.c index 8c7782d7..89ab0fc6 100644 --- a/shared/net/application_layer/http.c +++ b/shared/net/application_layer/http.c @@ -1,9 +1,8 @@ #include "http.h" #include "std/string.h" #include "std/memory.h" -extern void* malloc(uint64_t size); -extern void free(void *ptr, uint64_t size); -extern void sleep(uint64_t ms); +#include "syscalls/syscalls.h" + static inline bool is_space(char c) { return c == ' ' || c == '\t'; diff --git a/shared/net/application_layer/sntp.c b/shared/net/application_layer/sntp.c index 15367db1..8b38448b 100644 --- a/shared/net/application_layer/sntp.c +++ b/shared/net/application_layer/sntp.c @@ -2,16 +2,15 @@ #include "exceptions/timer.h" #include "std/memory.h" #include "net/internet_layer/ipv4.h" -#include "net/transport_layer/csocket_udp.h" #include "process/scheduler.h" #include "console/kio.h" #include "types.h" +#include "net/transport_layer/csocket_udp.h" +#include "syscalls/syscalls.h" #define NTP_PORT 123 #define NTP_UNIX_EPOCH_DELTA 2208988800UL -extern void sleep(uint64_t ms); - typedef struct __attribute__((packed)) { uint8_t li_vn_mode; uint8_t stratum; @@ -43,26 +42,54 @@ static uint64_t ntp64_be_to_unix_us(uint64_t ntp_be){ return sec * 1000000ULL + ((frac * 1000000ULL) >> 32); } -static sntp_result_t sntp_send_query(socket_handle_t sock, uint32_t server_ip_host, uint64_t* t1_us_out){ +static void make_v4_ep(uint32_t ip_host, uint16_t port, net_l4_endpoint* ep){ + memset(ep, 0, sizeof(*ep)); + ep->ver = IP_VER4; + memcpy(ep->ip, &ip_host, 4); + ep->port = port; +} + +static sntp_result_t sntp_send_query(socket_handle_t sock, uint32_t server_ip_host, uint64_t* t1_us_out) { ntp_packet_t p; memset(&p, 0, sizeof(p)); p.li_vn_mode = (0u<<6) | (4u<<3) | 3u; - uint64_t t1_us = timer_now_usec(); p.txTs = unix_us_to_ntp64_be(t1_us); - - int64_t sent = socket_sendto_udp(sock, server_ip_host, NTP_PORT, &p, sizeof(p)); + net_l4_endpoint dst; + make_v4_ep(server_ip_host, 0, &dst); + int64_t sent = socket_sendto_udp_ex(sock, DST_ENDPOINT, &dst, NTP_PORT, &p, sizeof(p)); if (sent < 0) return SNTP_ERR_SEND; *t1_us_out = t1_us; return SNTP_OK; } sntp_result_t sntp_poll_once(uint32_t timeout_ms){ - const net_cfg_t* cfg = ipv4_get_cfg(); - if (!cfg || !cfg->rt) return SNTP_ERR_NO_CFG; + uint32_t s0 = 0; + uint32_t s1 = 0; + + uint8_t l2n = l2_interface_count(); + for (uint8_t i = 0; i < l2n && (s0 == 0 || s1 == 0); i++) { + l2_interface_t* l2 = l2_interface_at(i); + if (!l2) continue; + for (int s = 0; s < MAX_IPV4_PER_INTERFACE && (s0 == 0 || s1 == 0); s++) { + l3_ipv4_interface_t* v4 = l2->l3_v4[s]; + if (!v4) continue; + if (v4->mode == IPV4_CFG_DISABLED) continue; + const net_runtime_opts_t* rt = &v4->runtime_opts_v4; + if (!rt) continue; + uint32_t c0 = rt->ntp[0]; + uint32_t c1 = rt->ntp[1]; + if (c0 && c0 != s0 && c0 != s1){ + if (!s0) s0 = c0; + else if (!s1) s1 = c0; + } + if (c1 && c1 != s0 && c1 != s1){ + if (!s0) s0 = c1; + else if (!s1) s1 = c1; + } + } + } - uint32_t s0 = cfg->rt->ntp[0]; - uint32_t s1 = cfg->rt->ntp[1]; if (s0 == 0 && s1 == 0) return SNTP_ERR_NO_SERVER; socket_handle_t sock = udp_socket_create(0, (uint32_t)get_current_proc_pid()); @@ -83,45 +110,46 @@ sntp_result_t sntp_poll_once(uint32_t timeout_ms){ while (waited < timeout_ms){ uint8_t buf[96]; - uint32_t rip; uint16_t rport; - int64_t n = socket_recvfrom_udp(sock, buf, sizeof(buf), &rip, &rport); - - if (n >= (int64_t)sizeof(ntp_packet_t) && rport == NTP_PORT && (rip == s0 || rip == s1)){ - ntp_packet_t* r = (ntp_packet_t*)buf; - uint64_t t4_us = timer_now_usec(); - - uint64_t T1 = ntp64_be_to_unix_us(r->origTs); - uint64_t T2 = ntp64_be_to_unix_us(r->recvTs); - uint64_t T3 = ntp64_be_to_unix_us(r->txTs); - - if (T1 != 0 && T3 != 0) { - uint64_t t1_us = (rip == s0) ? t1_0 : t1_1; - uint64_t d = (T1 > t1_us) ? (T1 - t1_us) : (t1_us - T1); - if (t1_us != 0 && d <= 1000000ULL) { - int64_t rtt = (int64_t)(t4_us - t1_us) - (int64_t)(T3 - T2); - if (rtt < 0) rtt = 0; - int64_t off = ((int64_t)(T2 - t1_us) + (int64_t)(T3 - t4_us)) / 2; - - uint64_t server_unix_us = (uint64_t)((int64_t)t4_us + off); - - const uint64_t year2000 = 946684800ULL * 1000000ULL; - bool ok_range = false; - - if (timer_is_synchronised()) { - uint64_t now_wall_ms = timer_unix_time_ms(); - uint64_t now_wall_us = now_wall_ms ? (now_wall_ms * 1000ULL) : 0; - const uint64_t plus1d = 86400ULL * 1000000ULL; - ok_range = (server_unix_us >= year2000) &&(now_wall_us == 0||server_unix_us <= now_wall_us + plus1d); - } else { - ok_range = (server_unix_us >= year2000); - } - - if (ok_range) { - - - if ((uint64_t)rtt < best_rtt_us){ - best_rtt_us = (uint64_t)rtt; - best_server_unix_us = server_unix_us; + net_l4_endpoint src; + int64_t n = socket_recvfrom_udp_ex(sock, buf, sizeof(buf), &src); + + if (n >= (int64_t)sizeof(ntp_packet_t) && src.ver == IP_VER4 && src.port == NTP_PORT){ + uint32_t rip = 0; + memcpy(&rip, src.ip, 4); + if (rip == s0 || rip == s1){ + ntp_packet_t* r = (ntp_packet_t*)buf; + uint64_t t4_us = timer_now_usec(); + + uint64_t T1 = ntp64_be_to_unix_us(r->origTs); + uint64_t T2 = ntp64_be_to_unix_us(r->recvTs); + uint64_t T3 = ntp64_be_to_unix_us(r->txTs); + + if (T1 != 0 && T3 != 0) { + uint64_t t1_us = (rip == s0) ? t1_0 : t1_1; + uint64_t d = (T1 > t1_us) ? (T1 - t1_us) : (t1_us - T1); + if (t1_us != 0 && d <= 1000000ULL) { + int64_t rtt = (int64_t)(t4_us - t1_us) - (int64_t)(T3 - T2); + if (rtt < 0) rtt = 0; + int64_t off = ((int64_t)(T2 - t1_us) + (int64_t)(T3 - t4_us)) / 2; + + uint64_t server_unix_us = (uint64_t)((int64_t)t4_us + off); + + const uint64_t year2000 = 946684800ULL * 1000000ULL; + bool ok_range = false; + + if (timer_is_synchronised()) { + uint64_t now_wall_ms = timer_unix_time_ms(); + uint64_t now_wall_us = now_wall_ms ? (now_wall_ms * 1000ULL) : 0; + const uint64_t plus1d = 86400ULL * 1000000ULL; + ok_range = (server_unix_us >= year2000) &&(now_wall_us == 0 || server_unix_us <= now_wall_us + plus1d); + } else { + ok_range = (server_unix_us >= year2000); + } + if (ok_range) { + if ((uint64_t)rtt < best_rtt_us){ + best_rtt_us = (uint64_t)rtt; + best_server_unix_us = server_unix_us; + } } } } diff --git a/shared/net/application_layer/sntp_daemon.c b/shared/net/application_layer/sntp_daemon.c index eacb061d..f37a6842 100644 --- a/shared/net/application_layer/sntp_daemon.c +++ b/shared/net/application_layer/sntp_daemon.c @@ -3,9 +3,9 @@ #include "exceptions/timer.h" #include "process/scheduler.h" #include "net/internet_layer/ipv4.h" -#include "process/scheduler.h" +#include "networking/interface_manager.h" +#include "syscalls/syscalls.h" -extern void sleep(uint64_t ms); static uint16_t g_pid_sntp = 0xFFFF; static socket_handle_t g_sock = 0; @@ -19,6 +19,26 @@ socket_handle_t sntp_socket_handle(void){ return g_sock; } #define SNTP_QUERY_TIMEOUT_MS 1200u #define SNTP_BOOTSTRAP_MAX_RETRY 5u +static inline int ipv4_is_loopback_u32(uint32_t ip){ return ((ip & 0xFF000000u) == 0x7F000000u); } + +static bool any_ipv4_configured_nonlocal(void){ + uint8_t n = l2_interface_count(); + for (uint8_t i = 0; i < n; i++) { + l2_interface_t* l2 = l2_interface_at(i); + if (!l2 || !l2->is_up) continue; + for (int s = 0; s < MAX_IPV4_PER_INTERFACE; s++) { + l3_ipv4_interface_t* v4 = l2->l3_v4[s]; + if (!v4) continue; + if (v4->mode == IPV4_CFG_DISABLED) continue; + if (!v4->ip) continue; + if (v4->is_localhost) continue; + if (ipv4_is_loopback_u32(v4->ip)) continue; + return true; + } + } + return false; +} + int sntp_daemon_entry(int argc, char* argv[]){ (void)argc; (void)argv; g_pid_sntp = (uint16_t)get_current_proc_pid(); @@ -26,12 +46,10 @@ int sntp_daemon_entry(int argc, char* argv[]){ sntp_set_pid(get_current_proc_pid()); uint32_t attempts = 0; while (attempts < SNTP_BOOTSTRAP_MAX_RETRY){ - const net_cfg_t* cfg = ipv4_get_cfg(); - if (!cfg || cfg->mode == NET_MODE_DISABLED || cfg->ip == 0){ + if (!any_ipv4_configured_nonlocal()){ sleep(500); continue; } - sntp_result_t r = sntp_poll_once(SNTP_QUERY_TIMEOUT_MS); if (r == SNTP_OK){ break; @@ -41,10 +59,9 @@ int sntp_daemon_entry(int argc, char* argv[]){ uint32_t jitter = backoff_ms / 10u; sleep(backoff_ms + (jitter / 2u)); } - for(;;){ sntp_poll_once(SNTP_QUERY_TIMEOUT_MS); sleep(SNTP_POLL_INTERVAL_MS); } return 1; -} +} \ No newline at end of file diff --git a/shared/net/application_layer/socket_http_client.hpp b/shared/net/application_layer/socket_http_client.hpp index b3251b9e..58eeb6c4 100644 --- a/shared/net/application_layer/socket_http_client.hpp +++ b/shared/net/application_layer/socket_http_client.hpp @@ -4,136 +4,120 @@ #include "http.h" #include "std/string.h" #include "std/memory.h" +#include "net/transport_layer/socket_types.h" class HTTPClient { private: TCPSocket sock; public: - explicit HTTPClient(uint16_t pid); - ~HTTPClient(); - int32_t connect(uint32_t ip, uint16_t port); - HTTPResponseMsg send_request(const HTTPRequestMsg &req); - int32_t close(); -}; - -HTTPClient::HTTPClient(uint16_t pid) - : sock(SOCK_ROLE_CLIENT, pid) -{} - -HTTPClient::~HTTPClient() { - sock.close(); -} + explicit HTTPClient(uint16_t pid) : sock(SOCK_ROLE_CLIENT, pid){} + ~HTTPClient() {sock.close();} -int32_t HTTPClient::connect(uint32_t ip, uint16_t port) { - return sock.connect(ip, port); -} - -HTTPResponseMsg HTTPClient::send_request(const HTTPRequestMsg &req) { - string out = http_request_builder(&req); - int64_t sent = sock.send(out.data, out.length); - free(out.data, out.mem_length); + int32_t connect(SockDstKind kind, const void* dst, uint16_t port) { return sock.connect(kind, dst, port); } - HTTPResponseMsg resp{}; - if (sent < 0) { - resp.status_code = (HttpError)sent; - return resp; - } + HTTPResponseMsg send_request(const HTTPRequestMsg &req) { + string out = http_request_builder(&req); + int64_t sent = sock.send(out.data, out.length); + free(out.data, out.mem_length); - string buf = string_repeat('\0', 0); - char tmp[512]; - int attempts = 0; - int hdr_end = -1; - while (true) { - int64_t r = sock.recv(tmp, sizeof(tmp)); - if (r < 0) { - free(buf.data, buf.mem_length); - resp.status_code = (HttpError)SOCK_ERR_SYS; + HTTPResponseMsg resp{}; + if (sent < 0) { + resp.status_code = (HttpError)sent; return resp; } - if (r > 0) { - string_append_bytes(&buf, tmp, (uint32_t)r); - } - hdr_end = find_crlfcrlf(buf.data, buf.length); - if (hdr_end >= 0) break; - if (++attempts > 50) { - free(buf.data, buf.mem_length); - resp.status_code = (HttpError)SOCK_ERR_PROTO; - return resp; - } - sleep(10); - } - { - uint32_t i = 0; - while (i < (uint32_t)hdr_end && buf.data[i] != ' ') i++; - uint32_t code = 0, j = i+1; - while (j < (uint32_t)hdr_end && buf.data[j] >= '0' && buf.data[j] <= '9') { - code = code*10 + (buf.data[j]-'0'); ++j; + string buf = string_repeat('\0', 0); + char tmp[512]; + int attempts = 0; + int hdr_end = -1; + while (true) { + int64_t r = sock.recv(tmp, sizeof(tmp)); + if (r < 0) { + free(buf.data, buf.mem_length); + resp.status_code = (HttpError)SOCK_ERR_SYS; + return resp; + } + if (r > 0) { + string_append_bytes(&buf, tmp, (uint32_t)r); + } + hdr_end = find_crlfcrlf(buf.data, buf.length); + if (hdr_end >= 0) break; + if (++attempts > 50) { + free(buf.data, buf.mem_length); + resp.status_code = (HttpError)SOCK_ERR_PROTO; + return resp; + } + sleep(10); } - resp.status_code = (HttpError)code; - while (j < (uint32_t)hdr_end && buf.data[j]==' ') ++j; - if (j < (uint32_t)hdr_end) { - uint32_t rlen = hdr_end - j; - resp.reason = string_repeat('\0', 0); - string_append_bytes(&resp.reason, buf.data+j, rlen); + + { + uint32_t i = 0; + while (i < (uint32_t)hdr_end && buf.data[i] != ' ') i++; + uint32_t code = 0, j = i+1; + while (j < (uint32_t)hdr_end && buf.data[j] >= '0' && buf.data[j] <= '9') { + code = code*10 + (buf.data[j]-'0'); ++j; + } + resp.status_code = (HttpError)code; + while (j < (uint32_t)hdr_end && buf.data[j]==' ') ++j; + if (j < (uint32_t)hdr_end) { + uint32_t rlen = hdr_end - j; + resp.reason = string_repeat('\0', 0); + string_append_bytes(&resp.reason, buf.data+j, rlen); + } } - } - HTTPHeader *extras = nullptr; - uint32_t extra_count = 0; - int status_line_end = strindex((char*)buf.data, "\r\n"); - http_header_parser( - (char*)buf.data + status_line_end + 2, - buf.length - (uint32_t)(status_line_end + 2), - &resp.headers_common, - &extras, - &extra_count); - resp.extra_headers = extras; - resp.extra_header_count = extra_count; + HTTPHeader *extras = nullptr; + uint32_t extra_count = 0; + int status_line_end = strindex((char*)buf.data, "\r\n"); + http_header_parser( + (char*)buf.data + status_line_end + 2, + buf.length - (uint32_t)(status_line_end + 2), + &resp.headers_common, + &extras, + &extra_count); + resp.extra_headers = extras; + resp.extra_header_count = extra_count; - uint32_t body_start = hdr_end + 4; - uint32_t have = (buf.length > body_start) - ? buf.length - body_start - : 0; + uint32_t body_start = hdr_end + 4; + uint32_t have = (buf.length > body_start) ? buf.length - body_start : 0; - uint32_t need = resp.headers_common.length; - if (need > 0) { - while (have < need) { - int64_t r = sock.recv(tmp, sizeof(tmp)); - if (r <= 0) break; - string_append_bytes(&buf, tmp, (uint32_t)r); - have += (uint32_t)r; - } - } else { - int idle = 0; - while (idle < 5) { - int64_t r = sock.recv(tmp, sizeof(tmp)); - if (r > 0) { + uint32_t need = resp.headers_common.length; + if (need > 0) { + while (have < need) { + int64_t r = sock.recv(tmp, sizeof(tmp)); + if (r <= 0) break; string_append_bytes(&buf, tmp, (uint32_t)r); have += (uint32_t)r; - idle = 0; - } else { - ++idle; - sleep(20); + } + } else { + int idle = 0; + while (idle < 5) { + int64_t r = sock.recv(tmp, sizeof(tmp)); + if (r > 0) { + string_append_bytes(&buf, tmp, (uint32_t)r); + have += (uint32_t)r; + idle = 0; + } else { + ++idle; + sleep(20); + } } } - } - if (have > 0) { - char *body_copy = (char*)malloc(have + 1); - if (body_copy) { - memcpy(body_copy, - buf.data + body_start, - have); - body_copy[have] = '\0'; - resp.body.ptr = (uintptr_t)body_copy; - resp.body.size = have; + if (have > 0) { + char *body_copy = (char*)malloc(have + 1); + if (body_copy) { + memcpy(body_copy, buf.data + body_start, have); + body_copy[have] = '\0'; + resp.body.ptr = (uintptr_t)body_copy; + resp.body.size = have; + } } + free(buf.data, buf.mem_length); + return resp; } - free(buf.data, buf.mem_length); - return resp; -} -int32_t HTTPClient::close() { + int32_t close() { return sock.close(); } +}; diff --git a/shared/net/application_layer/socket_http_server.hpp b/shared/net/application_layer/socket_http_server.hpp index 73ea8e41..a1cb8ff0 100644 --- a/shared/net/application_layer/socket_http_server.hpp +++ b/shared/net/application_layer/socket_http_server.hpp @@ -3,6 +3,7 @@ #include "http.h" #include "std/string.h" #include "std/memory.h" +#include "net/transport_layer/socket_types.h" class HTTPServer { private: @@ -13,7 +14,7 @@ class HTTPServer { ~HTTPServer() { close(); } - int32_t bind(uint16_t port) { return sock.bind(port); } + int32_t bind(const SockBindSpec& spec, uint16_t port) { return sock.bind(spec, port); } int32_t listen(int backlog = 4) { return sock.listen(backlog); } TCPSocket* accept() { return sock.accept(); } diff --git a/shared/net/internet_layer/icmp.c b/shared/net/internet_layer/icmp.c index 6565a11c..da9f00da 100644 --- a/shared/net/internet_layer/icmp.c +++ b/shared/net/internet_layer/icmp.c @@ -1,15 +1,10 @@ #include "icmp.h" -#include "net/internet_layer/ipv4.h" -#include "net/network_types.h" #include "net/checksums.h" #include "std/memory.h" -#include "console/kio.h" -#include "ipv4.h" -#include "networking/network.h" - -extern void* malloc(uint64_t size); -extern void free(void *ptr, uint64_t size); -extern void sleep(uint64_t ms); +#include "networking/interface_manager.h" +#include "net/internet_layer/ipv4.h" +#include "net/internet_layer/ipv4_route.h" +#include "syscalls/syscalls.h" #define MAX_PENDING 16 #define POLL_MS 1 @@ -43,15 +38,13 @@ static void free_slot(int i){ } void create_icmp_packet(uintptr_t p, - const net_l2l3_endpoint *src, - const net_l2l3_endpoint *dst, const icmp_data *d) { icmp_packet *pkt = (icmp_packet*)p; pkt->type = d->response ? ICMP_ECHO_REPLY : ICMP_ECHO_REQUEST; pkt->code = 0; - pkt->id = __builtin_bswap16(d->id); - pkt->seq = __builtin_bswap16(d->seq); + pkt->id = bswap16(d->id); + pkt->seq = bswap16(d->seq); memset(pkt->payload, 0, sizeof(pkt->payload)); memcpy(pkt->payload, d->payload, sizeof(pkt->payload)); @@ -73,12 +66,11 @@ void icmp_send_echo(uint32_t dst_ip, uintptr_t buf = (uintptr_t)malloc(icmp_len); if(!buf) return; - const net_l2l3_endpoint *local = network_get_local_endpoint(); - create_icmp_packet(buf, local, NULL, &d); + create_icmp_packet(buf, &d); ((icmp_packet*)buf)->checksum = checksum16((uint16_t*)buf, icmp_len); - ipv4_send_segment(local->ip, dst_ip, 1, (sizedptr){ buf, icmp_len }); + ipv4_send_packet(dst_ip, 1, (sizedptr){ buf, icmp_len}, NULL); free((void*)buf, icmp_len); } @@ -88,22 +80,22 @@ void icmp_input(uintptr_t ptr, uint32_t src_ip, uint32_t dst_ip) { - if(len < 8) return; + if (len < 8) return; icmp_packet *pkt = (icmp_packet*)ptr; uint16_t recv_ck = pkt->checksum; pkt->checksum = 0; - if(checksum16((uint16_t*)pkt, len) != recv_ck) return; + if (checksum16((uint16_t*)pkt, len) != recv_ck) return; pkt->checksum = recv_ck; uint8_t type = pkt->type; - uint16_t id = __builtin_bswap16(pkt->id); - uint16_t sq = __builtin_bswap16(pkt->seq); + uint16_t id = bswap16(pkt->id); + uint16_t sq = bswap16(pkt->seq); uint32_t pay = len - 8; - if(pay > 56) pay = 56; + if (pay > 56) pay = 56; - if(type == ICMP_ECHO_REQUEST){ - icmp_data d = { .response=true, .id=id, .seq=sq }; + if (type == ICMP_ECHO_REQUEST) { + icmp_data d = { .response = true, .id = id, .seq = sq }; memcpy(d.payload, pkt->payload, pay); memset(d.payload + pay, 0, 56 - pay); @@ -111,19 +103,30 @@ void icmp_input(uintptr_t ptr, uintptr_t buf = (uintptr_t)malloc(reply_len); if(!buf) return; - const net_l2l3_endpoint *local = network_get_local_endpoint(); - create_icmp_packet(buf, local, NULL, &d); + create_icmp_packet(buf, &d); ((icmp_packet*)buf)->checksum = checksum16((uint16_t*)buf, reply_len); - ipv4_send_segment(local->ip, src_ip, 1, (sizedptr){ buf, reply_len }); + l3_ipv4_interface_t *l3 = l3_ipv4_find_by_ip(dst_ip); + if (l3 && l3->l2) { + ipv4_tx_opts_t o = { .index = l3->l3_id, .scope = IPV4_TX_BOUND_L2 }; + + char sbuf[16], dbuf[16]; + ipv4_to_string(src_ip, sbuf); + ipv4_to_string(dst_ip, dbuf); + + ipv4_send_packet(src_ip, 1, (sizedptr){ buf, reply_len }, &o); + } + free((void*)buf, reply_len); return; } - if(type == ICMP_ECHO_REPLY) + if(type == ICMP_ECHO_REPLY) { mark_received(id, sq); + } } + bool icmp_ping(uint32_t dst_ip, uint16_t id, uint16_t seq, diff --git a/shared/net/internet_layer/icmp.h b/shared/net/internet_layer/icmp.h index 387eb477..31c9ed7d 100644 --- a/shared/net/internet_layer/icmp.h +++ b/shared/net/internet_layer/icmp.h @@ -26,9 +26,7 @@ typedef struct { } icmp_data; void create_icmp_packet(uintptr_t p, - const net_l2l3_endpoint *src, - const net_l2l3_endpoint *dst, - const icmp_data *data); + const icmp_data *d); void icmp_input(uintptr_t ptr, uint32_t len, diff --git a/shared/net/internet_layer/ipv4.c b/shared/net/internet_layer/ipv4.c index c415cdfa..b40c2fe0 100644 --- a/shared/net/internet_layer/ipv4.c +++ b/shared/net/internet_layer/ipv4.c @@ -1,63 +1,13 @@ #include "ipv4.h" -#include "console/kio.h" -#include "std/memory.h" -#include "networking/network.h" +#include "ipv4_route.h" #include "net/link_layer/arp.h" -#include "net/transport_layer/udp.h" -#include "net/transport_layer/tcp.h" -#include "icmp.h" +#include "net/internet_layer/icmp.h" +#include "std/memory.h" #include "std/string.h" -#include "types.h" -#include "ipv4_route.h" - -extern void* malloc(uint64_t size); -extern void free(void *ptr, uint64_t size); -extern void sleep(uint64_t ms); - -static net_runtime_opts_t g_rt_opts; -net_cfg_t g_net_cfg = { - .ip = 0, - .mask = 0, - .gw = 0, - .mode = NET_MODE_DHCP, - .rt = &g_rt_opts -}; - -void ipv4_cfg_init() { - memset(&g_rt_opts, 0, sizeof(g_rt_opts)); - g_net_cfg.ip = 0; - g_net_cfg.mask = 0; - g_net_cfg.gw = 0; - g_net_cfg.mode = NET_MODE_DHCP; - g_net_cfg.rt = &g_rt_opts; - ipv4_rt_init(); -} - -void ipv4_set_cfg(const net_cfg_t *src) { - if (!src) return; - g_net_cfg.ip = src->ip; - g_net_cfg.mask = src->mask; - g_net_cfg.gw = src->gw; - g_net_cfg.mode = src->mode; - if (src->rt) { - g_rt_opts = *src->rt; - } else { - memset(&g_rt_opts, 0, sizeof(g_rt_opts)); - } - if(g_net_cfg.ip != 0){ - uint8_t bmac[6] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}; - arp_table_put(ipv4_broadcast(g_net_cfg.ip, g_net_cfg.mask), bmac, 0, true); - } - g_net_cfg.rt = &g_rt_opts; - ipv4_rt_init(); - if (g_net_cfg.gw) { - ipv4_rt_add(0, 0, g_net_cfg.gw); - } -} - -const net_cfg_t* ipv4_get_cfg() { - return &g_net_cfg; -} +#include "net/transport_layer/tcp.h" +#include "net/transport_layer/udp.h" +#include "console/kio.h" +#include "syscalls/syscalls.h" static char* u8_to_str(uint8_t val, char* out) { if (val >= 100) { @@ -74,10 +24,258 @@ static char* u8_to_str(uint8_t val, char* out) { return out; } +#define IP_IHL_NOOPTS 5 +#define IP_VERSION_4 4 +#define IP_TTL_DEFAULT 64 + +static uint16_t g_ip_ident = 1; + +static int mask_prefix_len(uint32_t m) { + int n = 0; + while (m & 0x80000000u) { n++; m <<= 1; } + return n; +} + +static inline bool is_lbcast(uint32_t ip) { return ip == 0xFFFFFFFFu; } +static inline bool is_mcast(uint32_t ip) { return (ip & 0xF0000000u) == 0xE0000000u; } +static inline bool is_directed_bcast_for(const l3_ipv4_interface_t* v4, uint32_t dst) { + if (!v4) return false; + if (!v4->mask) return false; + uint32_t b = ipv4_broadcast_calc(v4->ip, v4->mask); + return b == dst; +} +static l3_ipv4_interface_t* best_v4_on_l2_for_dst(l2_interface_t* l2, uint32_t dst) { + l3_ipv4_interface_t* best = NULL; + int best_pl = -1; + for (int s = 0; s < MAX_IPV4_PER_INTERFACE; s++) { + l3_ipv4_interface_t* v4 = l2->l3_v4[s]; + if (!v4) continue; + if (v4->mode == IPV4_CFG_DISABLED) continue; + if (!v4->ip) continue; + uint32_t m = v4->mask; + if (m && ((dst & m) == (v4->ip & m))) { + int pl = mask_prefix_len(m); + if (pl > best_pl) { best_pl = pl; best = v4; } + } else if (!best) { + best = v4; + } + } + return best; +} + +static bool lookup_route_in_tables(uint32_t dst, uint32_t* out_nh, uint8_t* out_ifx, uint32_t* out_src) { + int best_pl = -1; + int best_metric = 0x7FFF; + uint32_t best_nh = 0; + uint8_t best_ifx = 0; + uint32_t best_src = 0; + + uint8_t cnt = l2_interface_count(); + for (uint8_t i = 0; i < cnt; i++) { + l2_interface_t* l2 = l2_interface_at(i); + if (!l2) continue; + for (int s = 0; s < MAX_IPV4_PER_INTERFACE; s++) { + l3_ipv4_interface_t* v4 = l2->l3_v4[s]; + if (!v4) continue; + if (v4->mode == IPV4_CFG_DISABLED) continue; + if (!v4->routing_table) continue; + + uint32_t nh = 0; int pl = -1; int metric = 0; + if (ipv4_rt_lookup_in(v4->routing_table, dst, &nh, &pl, &metric)) { + if (pl > best_pl || (pl == best_pl && metric < best_metric)) { + best_pl = pl; + best_metric = metric; + best_nh = nh ? nh : dst; + best_ifx = l2->ifindex; + best_src = v4->ip; + } + } + } + } + + if (best_pl >= 0) { + if (out_nh) *out_nh = best_nh; + if (out_ifx) *out_ifx = best_ifx; + if (out_src) *out_src = best_src; + return true; + } + return false; +} + +static bool pick_broadcast_bound_l3(uint8_t l3_id, uint8_t* out_ifx, uint32_t* out_src, uint32_t* out_nh) { + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(l3_id); + if (!v4 || !v4->l2) return false; + if (v4->mode == IPV4_CFG_DISABLED) return false; + + if (out_ifx) *out_ifx = v4->l2->ifindex; + if (out_src) *out_src = v4->ip; + if (!v4->ip && v4->mode == IPV4_CFG_DHCP && out_src) *out_src = 0; + if (out_nh) *out_nh = 0xFFFFFFFFu; + return true; +} + +static bool pick_broadcast_bound_l2(uint8_t ifindex, uint8_t* out_ifx, uint32_t* out_src, uint32_t* out_nh) { + l2_interface_t* l2 = l2_interface_find_by_index(ifindex); + if (!l2) return false; + + l3_ipv4_interface_t* chosen = NULL; + l3_ipv4_interface_t* dhcp = NULL; + for (int s = 0; s < MAX_IPV4_PER_INTERFACE; s++) { + l3_ipv4_interface_t* v4 = l2->l3_v4[s]; + if (!v4) continue; + if (v4->mode == IPV4_CFG_DISABLED) continue; + if (v4->ip) { chosen = v4; break; } + if (!v4->ip && v4->mode == IPV4_CFG_DHCP && !dhcp) dhcp = v4; + } + if (!chosen) chosen = dhcp; + + if (out_ifx) *out_ifx = l2->ifindex; + if (out_src) *out_src = (chosen && chosen->ip) ? chosen->ip : 0; + if (out_nh) *out_nh = 0xFFFFFFFFu; + return true; +} + +static bool pick_broadcast_global(uint8_t* out_ifx, uint32_t* out_src, uint32_t* out_nh) { + l3_ipv4_interface_t* static_cand = NULL; + l3_ipv4_interface_t* dhcp_cand = NULL; + l2_interface_t* l2_s = NULL; + l2_interface_t* l2_d = NULL; + + uint8_t n = l2_interface_count(); + for (uint8_t i = 0; i < n; i++) { + l2_interface_t* l2 = l2_interface_at(i); + if (!l2) continue; + for (int s = 0; s < MAX_IPV4_PER_INTERFACE; s++) { + l3_ipv4_interface_t* v4 = l2->l3_v4[s]; + if (!v4) continue; + if (v4->mode == IPV4_CFG_DISABLED) continue; + if (v4->ip && !static_cand) { static_cand = v4; l2_s = l2; } + if (!v4->ip && v4->mode == IPV4_CFG_DHCP && !dhcp_cand) { dhcp_cand = v4; l2_d = l2; } + } + } + + l3_ipv4_interface_t* pick = static_cand ? static_cand : dhcp_cand; + l2_interface_t* l2 = static_cand ? l2_s : l2_d; + if (!l2) return false; + + if (out_ifx) *out_ifx = l2->ifindex; + if (out_src) *out_src = (pick && pick->ip) ? pick->ip : 0; + if (out_nh) *out_nh = 0xFFFFFFFFu; + return true; +} + +static bool pick_route_global(uint32_t dst, uint8_t* out_ifx, uint32_t* out_src, uint32_t* out_nh) { + if (is_lbcast(dst)) return pick_broadcast_global(out_ifx, out_src, out_nh); + + ip_resolution_result_t r = resolve_ipv4_to_interface(dst); + if (r.found && r.ipv4 && r.l2) { + uint32_t m = r.ipv4->mask; + if (m && ((dst & m) == (r.ipv4->ip & m))) { + if (out_ifx) *out_ifx = r.l2->ifindex; + if (out_src) *out_src = r.ipv4->ip; + if (out_nh) *out_nh = dst; + return true; + } + if (r.ipv4->gw) { + if (out_ifx) *out_ifx = r.l2->ifindex; + if (out_src) *out_src = r.ipv4->ip; + if (out_nh) *out_nh = r.ipv4->gw; + return true; + } + if (r.ipv4->routing_table) { + uint32_t nh = 0; int pl = -1; int metric = 0; + if (ipv4_rt_lookup_in(r.ipv4->routing_table, dst, &nh, &pl, &metric)) { + if (out_ifx) *out_ifx = r.l2->ifindex; + if (out_src) *out_src = r.ipv4->ip; + if (out_nh) *out_nh = nh ? nh : dst; + return true; + } + } + } + return lookup_route_in_tables(dst, out_nh, out_ifx, out_src); +} + +static bool pick_route_bound_l3(uint8_t l3_id, uint32_t dst, uint8_t* out_ifx, uint32_t* out_src, uint32_t* out_nh) { + if (is_lbcast(dst)) return pick_broadcast_bound_l3(l3_id, out_ifx, out_src, out_nh); + + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(l3_id); + if (!v4 || !v4->l2) return false; + if (v4->mode == IPV4_CFG_DISABLED) return false; + if (!v4->ip) return false; + + uint32_t m = v4->mask; + if (m && ((dst & m) == (v4->ip & m))) { + if (out_ifx) *out_ifx = v4->l2->ifindex; + if (out_src) *out_src = v4->ip; + if (out_nh) *out_nh = dst; + return true; + } + if (v4->gw) { + if (out_ifx) *out_ifx = v4->l2->ifindex; + if (out_src) *out_src = v4->ip; + if (out_nh) *out_nh = v4->gw; + return true; + } + if (v4->routing_table) { + uint32_t nh = 0; int pl = -1; int metric = 0; + if (ipv4_rt_lookup_in(v4->routing_table, dst, &nh, &pl, &metric)) { + if (out_ifx) *out_ifx = v4->l2->ifindex; + if (out_src) *out_src = v4->ip; + if (out_nh) *out_nh = nh ? nh : dst; + return true; + } + } + return false; +} + +static bool pick_route_bound_l2(uint8_t ifindex, uint32_t dst, uint8_t* out_ifx, uint32_t* out_src, uint32_t* out_nh) { + if (is_lbcast(dst)) return pick_broadcast_bound_l2(ifindex, out_ifx, out_src, out_nh); + + l2_interface_t* l2 = l2_interface_find_by_index(ifindex); + if (!l2) return false; + + l3_ipv4_interface_t* v4 = best_v4_on_l2_for_dst(l2, dst); + if (!v4) return false; + if (v4->mode == IPV4_CFG_DISABLED) return false; + + uint32_t m = v4->mask; + if (m && ((dst & m) == (v4->ip & m))) { + if (out_ifx) *out_ifx = l2->ifindex; + if (out_src) *out_src = v4->ip; + if (out_nh) *out_nh = dst; + return true; + } + if (v4->gw) { + if (out_ifx) *out_ifx = l2->ifindex; + if (out_src) *out_src = v4->ip; + if (out_nh) *out_nh = v4->gw; + return true; + } + if (v4->routing_table) { + uint32_t nh = 0; int pl = -1; int metric = 0; + if (ipv4_rt_lookup_in(v4->routing_table, dst, &nh, &pl, &metric)) { + if (out_ifx) *out_ifx = l2->ifindex; + if (out_src) *out_src = v4->ip; + if (out_nh) *out_nh = nh ? nh : dst; + return true; + } + } + return false; +} + +static bool pick_route(uint32_t dst, const ipv4_tx_opts_t* opts, uint8_t* out_ifx, uint32_t* out_src, uint32_t* out_nh) { + if (opts) { + if (opts->scope == IPV4_TX_BOUND_L3) return pick_route_bound_l3(opts->index, dst, out_ifx, out_src, out_nh); + if (opts->scope == IPV4_TX_BOUND_L2) return pick_route_bound_l2(opts->index, dst, out_ifx, out_src, out_nh); + return pick_route_global(dst, out_ifx, out_src, out_nh); + } + return pick_route_global(dst, out_ifx, out_src, out_nh); +} + void ipv4_to_string(uint32_t ip, char* buf) { uint8_t a = (ip >> 24) & 0xFF; uint8_t b = (ip >> 16) & 0xFF; - uint8_t c = (ip >> 8) & 0xFF; + uint8_t c = (ip >> 8) & 0xFF; uint8_t d = ip & 0xFF; char* p = buf; @@ -88,115 +286,192 @@ void ipv4_to_string(uint32_t ip, char* buf) { *p = '\0'; } -static uint16_t ipv4_checksum(const void *buf, size_t len) { - const uint16_t *data = buf; - uint32_t sum = 0; - for (; len > 1; len -= 2) { - sum += *data++; - } - if (len) { - sum += *(const uint8_t*)data; +void ipv4_send_packet(uint32_t dst_ip, uint8_t proto, sizedptr segment, const ipv4_tx_opts_t* opts) { + if (!segment.ptr || !segment.size) return; + + uint8_t ifx = 0; + uint32_t src_ip = 0; + uint32_t nh = 0; + if (!pick_route(dst_ip, opts, &ifx, &src_ip, &nh)) return; + + uint8_t dst_mac[6]; + bool is_dbcast = false; + l2_interface_t* l2 = l2_interface_find_by_index(ifx); + if (l2) { + for (int s = 0; s < MAX_IPV4_PER_INTERFACE; ++s) { + l3_ipv4_interface_t* v4 = l2->l3_v4[s]; + if (!v4 || v4->mode == IPV4_CFG_DISABLED) continue; + if (is_directed_bcast_for(v4, dst_ip)) { is_dbcast = true; break; } + } } - while (sum >> 16) { - sum = (sum & 0xFFFF) + (sum >> 16); + + if (is_dbcast) { + memset(dst_mac, 0xFF, 6); + } else { + if (!arp_resolve_on(ifx, nh, dst_mac, 200)) return; } - return (uint16_t)~sum; + + uint32_t hdr_len = IP_IHL_NOOPTS * 4; + uint32_t total = hdr_len + (uint32_t)segment.size; + + uintptr_t buf = (uintptr_t)malloc(total); + if (!buf) return; + + ipv4_hdr_t* ip = (ipv4_hdr_t*)buf; + ip->version_ihl = (uint8_t)((IP_VERSION_4 << 4) | IP_IHL_NOOPTS); + ip->dscp_ecn = 0; + ip->total_length = bswap16((uint16_t)total); + ip->identification = bswap16(g_ip_ident++); + ip->flags_frag_offset = bswap16(0); + ip->ttl = IP_TTL_DEFAULT; + ip->protocol = proto; + ip->header_checksum = 0; + ip->src_ip = bswap32(src_ip); + ip->dst_ip = bswap32(dst_ip); + + memcpy((void*)(buf + hdr_len), (const void*)segment.ptr, segment.size); + ip->header_checksum = checksum16((const uint16_t*)ip, hdr_len / 2); + + sizedptr payload = { buf, total }; + eth_send_frame_on(ifx, ETHERTYPE_IPV4, dst_mac, payload); + + free((void*)buf, total); } -void ip_input(uintptr_t ip_ptr, - uint32_t ip_len, - const uint8_t src_mac[6]) -{ +void ipv4_input(uint16_t ifindex, uintptr_t ip_ptr, uint32_t ip_len, const uint8_t src_mac[6]) { if (ip_len < sizeof(ipv4_hdr_t)) return; - ipv4_hdr_t *hdr = (ipv4_hdr_t*)ip_ptr; - uint8_t version = hdr->version_ihl >> 4; - uint8_t ihl = hdr->version_ihl & 0x0F; - if (version != 4 || ihl < 5) return; - - uint32_t header_bytes = ihl * 4; - if (ip_len < header_bytes) return; - - if (hdr->header_checksum != 0) { - uint16_t recv_ck = hdr->header_checksum; - hdr->header_checksum = 0; - if (ipv4_checksum(hdr, header_bytes) != recv_ck) return; - hdr->header_checksum = recv_ck; - } - - uint32_t sip = __builtin_bswap32(hdr->src_ip); - arp_table_put(sip, src_mac, 60000, false); - - uint32_t dip = __builtin_bswap32(hdr->dst_ip); - //TODO manage special ip - - uintptr_t payload_ptr = ip_ptr + header_bytes; - uint32_t payload_len = __builtin_bswap16(hdr->total_length) - header_bytes; - switch (hdr->protocol) { - case 1://icmp - icmp_input(payload_ptr, payload_len, sip, dip); - break; - case 6://tcp - tcp_input(payload_ptr, payload_len, sip, dip); - break; - case 17://udp - udp_input(payload_ptr, payload_len, sip, dip); - break; - default: - //everything else - break; + + ipv4_hdr_t* ip = (ipv4_hdr_t*)ip_ptr; + uint8_t ver = (uint8_t)(ip->version_ihl >> 4); + uint8_t ihl = (uint8_t)(ip->version_ihl & 0x0F); + if (ver != IP_VERSION_4) return; + if (ihl < IP_IHL_NOOPTS) return; + + uint32_t hdr_len = (uint32_t)ihl * 4; + if (ip_len < hdr_len) return; + + uint16_t saved = ip->header_checksum; + ip->header_checksum = 0; + if (checksum16((const uint16_t*)ip, hdr_len / 2) != saved) { + ip->header_checksum = saved; + return; } -} + ip->header_checksum = saved; -void ipv4_send_segment(uint32_t src_ip, - uint32_t dst_ip, - uint8_t proto, - sizedptr segment) -{ - uint32_t nh_ip; - if (!ipv4_rt_lookup(dst_ip, &nh_ip)) { - const net_cfg_t *cfg = ipv4_get_cfg(); - if (cfg && ((dst_ip & cfg->mask) == (cfg->ip & cfg->mask))) { - nh_ip = dst_ip; + uint16_t ip_totlen = bswap16(ip->total_length); + if (ip_totlen < hdr_len) return; + if (ip_len < ip_totlen) return; + + uintptr_t l4 = ip_ptr + hdr_len; + uint32_t l4_len = (uint32_t)ip_totlen - hdr_len; + + uint32_t src = bswap32(ip->src_ip); + uint32_t dst = bswap32(ip->dst_ip); + + if (ifindex && src) { + uint8_t mac_old[6]; + bool had = arp_table_get_for_l2((uint8_t)ifindex, src, mac_old); + if (!had || memcmp(mac_old, src_mac, 6) != 0) { + arp_table_put_for_l2((uint8_t)ifindex, src, src_mac, 180000, false); } else { - nh_ip = cfg ? cfg->gw : dst_ip; + arp_table_put_for_l2((uint8_t)ifindex, src, mac_old, 180000, false); } } - uint8_t dst_mac[6]; - bool ok = arp_resolve(nh_ip, dst_mac, 200); - if (!ok) { - memset(dst_mac, 0xFF, sizeof(dst_mac)); - } + uint8_t proto = ip->protocol; - uint32_t total = sizeof(eth_hdr_t) - + sizeof(ipv4_hdr_t) - + segment.size; - uintptr_t buf = (uintptr_t)malloc(total); - if (!buf) return; + l2_interface_t* l2 = l2_interface_find_by_index((uint8_t)ifindex); + if (!l2) return; - const net_l2l3_endpoint *local = network_get_local_endpoint(); - uintptr_t ptr = create_eth_packet(buf, local->mac, dst_mac, 0x0800); + l3_ipv4_interface_t* cand[MAX_IPV4_PER_INTERFACE]; + int ccount = 0; + for (int s = 0; s < MAX_IPV4_PER_INTERFACE; ++s) { + l3_ipv4_interface_t* v4 = l2->l3_v4[s]; + if (!v4) continue; + if (v4->mode == IPV4_CFG_DISABLED) continue; + cand[ccount++] = v4; + } + if (ccount == 0) return; - ipv4_hdr_t *ip = (ipv4_hdr_t *)ptr; - ip->version_ihl = (4 << 4) | (sizeof(*ip)/4); - ip->dscp_ecn = 0; - ip->total_length = __builtin_bswap16(sizeof(*ip) + segment.size); - ip->identification = 0; - ip->flags_frag_offset = __builtin_bswap16(0x4000); - ip->ttl = 64; - ip->protocol = proto; - ip->src_ip = __builtin_bswap32(src_ip); - ip->dst_ip = __builtin_bswap32(dst_ip); - ip->header_checksum = 0; - ip->header_checksum = ipv4_checksum(ip, sizeof(*ip)); + if (is_mcast(dst)) return; - ptr += sizeof(*ip); + if (is_lbcast(dst)) { + if (ccount == 1) { + uint8_t l3id = cand[0]->l3_id; + switch (proto) { + case 1: icmp_input(l4, l4_len, src, dst); break; + case 6: tcp_input(IP_VER4, &src, &dst, l3id, l4, l4_len); break; + case 17: udp_input(IP_VER4, &src, &dst, l3id, l4, l4_len); break; + default: break; + } + return; + } else { + for (int i = 0; i < ccount; ++i) { + uint8_t l3id = cand[i]->l3_id; + switch (proto) { + case 1: icmp_input(l4, l4_len, src, dst); break; + case 6: tcp_input(IP_VER4, &src, &dst, l3id, l4, l4_len); break; + case 17: udp_input(IP_VER4, &src, &dst, l3id, l4, l4_len); break; + default: break; + } + } + return; + } + } - if (segment.size) { - memcpy((void*)ptr, (void*)segment.ptr, segment.size); + int match_count = 0; + uint8_t match_l3id = 0; + for (int i = 0; i < ccount; ++i) { + if (cand[i]->ip && cand[i]->ip == dst) { + match_count++; + match_l3id = cand[i]->l3_id; + } + } + if (match_count == 1) { + switch (proto) { + case 1: icmp_input(l4, l4_len, src, dst); break; + case 6: tcp_input(IP_VER4, &src, &dst, match_l3id, l4, l4_len); break; + case 17: udp_input(IP_VER4, &src, &dst, match_l3id, l4, l4_len); break; + default: break; + } + return; + } + if (match_count > 1) { + for (int i = 0; i < ccount; ++i) { + if (cand[i]->ip == dst) { + uint8_t l3id = cand[i]->l3_id; + switch (proto) { + case 1: icmp_input(l4, l4_len, src, dst); break; + case 6: tcp_input(IP_VER4, &src, &dst, l3id, l4, l4_len); break; + case 17: udp_input(IP_VER4, &src, &dst, l3id, l4, l4_len); break; + default: break; + } + } + } + return; } - eth_send_frame(buf, total); + int any_dbcast = 0; + for (uint8_t i = 0, n = l2_interface_count(); i < n; ++i) { + l2_interface_t* l2x = l2_interface_at(i); + if (!l2x) continue; + for (int s = 0; s < MAX_IPV4_PER_INTERFACE; ++s) { + l3_ipv4_interface_t* v4 = l2x->l3_v4[s]; + if (!v4) continue; + if (v4->mode == IPV4_CFG_DISABLED) continue; + if (is_directed_bcast_for(v4, dst)) { + any_dbcast = 1; + uint8_t l3id = v4->l3_id; + switch (proto) { + case 1: icmp_input(l4, l4_len, src, dst); break; + case 6: tcp_input(IP_VER4, &src, &dst, l3id, l4, l4_len); break; + case 17: udp_input(IP_VER4, &src, &dst, l3id, l4, l4_len); break; + default: break; + } + } + } + } + if (any_dbcast) return; - free((void*)buf, total); -} + return; +} \ No newline at end of file diff --git a/shared/net/internet_layer/ipv4.h b/shared/net/internet_layer/ipv4.h index b005ef9f..006c49fe 100644 --- a/shared/net/internet_layer/ipv4.h +++ b/shared/net/internet_layer/ipv4.h @@ -4,31 +4,11 @@ #include "net/link_layer/eth.h" #include "net/network_types.h" #include "net/checksums.h" +#include "networking/interface_manager.h" + #ifdef __cplusplus extern "C" { #endif -#define NET_MODE_DISABLED ((int8_t)-1) -#define NET_MODE_DHCP 0 -#define NET_MODE_STATIC 1 - -typedef struct net_runtime_opts { - uint16_t mtu; - uint32_t t1; - uint32_t t2; - uint32_t dns[2]; - uint32_t ntp[2]; - uint16_t xid; - uint32_t server_ip; - uint32_t lease; -} net_runtime_opts_t; - -typedef struct net_cfg { - uint32_t ip; - uint32_t mask; - uint32_t gw; - int8_t mode; - net_runtime_opts_t *rt; -} net_cfg_t; typedef struct __attribute__((packed)) ipv4_hdr_t { uint8_t version_ihl; @@ -43,29 +23,27 @@ typedef struct __attribute__((packed)) ipv4_hdr_t { uint32_t dst_ip; } ipv4_hdr_t; -void ipv4_cfg_init(); -void ipv4_set_cfg(const net_cfg_t *src); -const net_cfg_t* ipv4_get_cfg(); +typedef enum { IPV4_TX_AUTO=0, + IPV4_TX_BOUND_L2=1, + IPV4_TX_BOUND_L3=2 +} ipv4_tx_scope_t; -void ipv4_to_string(uint32_t ip, char* buf); - -void ipv4_send_segment(uint32_t src_ip, - uint32_t dst_ip, - uint8_t proto, - sizedptr segment); +typedef struct { + uint8_t index; + ipv4_tx_scope_t scope; +} ipv4_tx_opts_t; -void ip_input(uintptr_t ip_ptr, - uint32_t ip_len, - const uint8_t src_mac[6]); +void ipv4_to_string(uint32_t ip, char* buf); -static inline uint32_t ipv4_network(uint32_t ip, uint32_t mask){ return ip & mask; } -static inline uint32_t ipv4_broadcast(uint32_t ip, uint32_t mask){ return (ip & mask) | ~mask; } -static inline uint32_t ipv4_first_host(uint32_t ip, uint32_t mask){ return (ip & mask) + 1; } -static inline uint32_t ipv4_last_host(uint32_t ip, uint32_t mask){ return ((ip & mask) | ~mask) - 1; } +void ipv4_send_packet(uint32_t dst_ip, + uint8_t proto, + sizedptr segment, + const ipv4_tx_opts_t* opts); + +void ipv4_input(uint16_t ifindex, uintptr_t ip_ptr, + uint32_t ip_len, + const uint8_t src_mac[6]); -void ipv4_cfg_init(); -void ipv4_set_cfg(const net_cfg_t *src); -const net_cfg_t* ipv4_get_cfg(); #ifdef __cplusplus } #endif diff --git a/shared/net/internet_layer/ipv4_route.c b/shared/net/internet_layer/ipv4_route.c index 68ac553e..1b84c98a 100644 --- a/shared/net/internet_layer/ipv4_route.c +++ b/shared/net/internet_layer/ipv4_route.c @@ -1,66 +1,149 @@ #include "ipv4_route.h" #include "std/memory.h" +#include "networking/interface_manager.h" +#include "syscalls/syscalls.h" -static ipv4_rt_entry_t g_rt[IPV4_RT_MAX]; -static int g_rt_len = 0; +struct ipv4_rt_table { + ipv4_rt_entry_t e[IPV4_RT_PER_IF_MAX]; + int len; +}; -void ipv4_rt_init() { - g_rt_len = 0; - memset(g_rt, 0, sizeof(g_rt)); +static int prefix_len(uint32_t m) { + int n = 0; + while (m & 0x80000000u) { n++; m <<= 1; } + return n; } -bool ipv4_rt_add(uint32_t network, uint32_t mask, uint32_t gateway) -{ - if (g_rt_len >= IPV4_RT_MAX) return false; +ipv4_rt_table_t* ipv4_rt_create(void) { + ipv4_rt_table_t* t = (ipv4_rt_table_t*)malloc(sizeof(ipv4_rt_table_t)); + if (!t) return 0; + memset(t, 0, sizeof(*t)); + return t; +} + +void ipv4_rt_destroy(ipv4_rt_table_t* t) { + if (!t) return; + free(t, sizeof(*t)); +} - for (int i = 0; i < g_rt_len; ++i) { - if (g_rt[i].network == network && g_rt[i].mask == mask) { - g_rt[i].gateway = gateway; +void ipv4_rt_clear(ipv4_rt_table_t* t) { + if (!t) return; + t->len = 0; + memset(t->e, 0, sizeof(t->e)); +} + +bool ipv4_rt_add_in(ipv4_rt_table_t* t, uint32_t network, uint32_t mask, uint32_t gateway, uint16_t metric) { + if (!t) return false; + for (int i = 0; i < t->len; i++) { + if (t->e[i].network == network && t->e[i].mask == mask) { + t->e[i].gateway = gateway; + t->e[i].metric = metric; return true; } } - g_rt[g_rt_len++] = (ipv4_rt_entry_t){ network, mask, gateway }; + if (t->len >= IPV4_RT_PER_IF_MAX) return false; + t->e[t->len++] = (ipv4_rt_entry_t){ network, mask, gateway, metric }; return true; } -bool ipv4_rt_del(uint32_t network, uint32_t mask) -{ - for (int i = 0; i < g_rt_len; ++i) { - if (g_rt[i].network == network && g_rt[i].mask == mask) { - g_rt[i] = g_rt[--g_rt_len]; - memset(&g_rt[g_rt_len], 0, sizeof(g_rt[0])); +bool ipv4_rt_del_in(ipv4_rt_table_t* t, uint32_t network, uint32_t mask) { + if (!t) return false; + for (int i = 0; i < t->len; i++) { + if (t->e[i].network == network && t->e[i].mask == mask) { + t->e[i] = t->e[--t->len]; + memset(&t->e[t->len], 0, sizeof(t->e[0])); return true; } } return false; } -static inline int prefix_len(uint32_t mask) -{ - int len = 0; - while (mask & 0x80000000U) { ++len; mask <<= 1; } - return len; -} - -bool ipv4_rt_lookup(uint32_t dst, uint32_t *next_hop) -{ - int best_len = -1; +bool ipv4_rt_lookup_in(const ipv4_rt_table_t* t, uint32_t dst, uint32_t* next_hop, int* out_prefix_len, int* out_metric) { + if (!t) return false; + int best_pl = -1; + int best_metric = 0x7FFF; uint32_t best_nh = 0; - for (int i = 0; i < g_rt_len; ++i) { - uint32_t net = g_rt[i].network; - uint32_t mask = g_rt[i].mask; - if (mask && (dst & mask) == net) { - int l = prefix_len(mask); - if (l > best_len) { - best_len = l; - best_nh = g_rt[i].gateway ? g_rt[i].gateway : dst; + for (int i = 0; i < t->len; i++) { + uint32_t net = t->e[i].network; + uint32_t mask = t->e[i].mask; + if (mask == 0 || ((dst & mask) == net)) { + int pl = prefix_len(mask); + int met = t->e[i].metric; + if (pl > best_pl || (pl == best_pl && met < best_metric)) { + best_pl = pl; + best_metric = met; + best_nh = t->e[i].gateway ? t->e[i].gateway : dst; } } } - if (best_len >= 0) { - if (next_hop) *next_hop = best_nh; - return true; + + if (best_pl < 0) return false; + if (next_hop) *next_hop = best_nh; + if (out_prefix_len) *out_prefix_len = best_pl; + if (out_metric) *out_metric = best_metric; + return true; +} + +void ipv4_rt_ensure_basics(ipv4_rt_table_t* t, uint32_t ip, uint32_t mask, uint32_t gw, uint16_t base_metric) { + if (!t) return; + if (ip && mask) { + uint32_t net = ip & mask; + (void)ipv4_rt_add_in(t, net, mask, 0, base_metric); } - return false; + if (gw) { + (void)ipv4_rt_add_in(t, 0, 0, gw, (uint16_t)(base_metric + 1)); + } +} + +void ipv4_rt_sync_basics(ipv4_rt_table_t* t, uint32_t ip, uint32_t mask, uint32_t gw, uint16_t base_metric) { + if (!t) return; + if (gw) { + (void)ipv4_rt_add_in(t, 0, 0, gw, (uint16_t)(base_metric + 1)); + } else { + (void)ipv4_rt_del_in(t, 0, 0); + } + if (ip && mask) { + uint32_t net = ip & mask; + (void)ipv4_rt_add_in(t, net, mask, 0, base_metric); + } +} +bool ipv4_rt_pick_best_l3_in(const uint8_t* l3_ids, int n_ids, uint32_t dst, uint8_t* out_l3){ + int best_pl = -1; + int best_cost = 0x7FFFFFFF; + uint8_t best_l3 = 0; + for (int i=0;il2) continue; + if (x->mode == IPV4_CFG_DISABLED) continue; + int l2base = (int)x->l2->base_metric; + int pl_conn = -1; + if (x->mask){ + uint32_t netx = x->ip & x->mask; + if ((dst & x->mask) == netx) pl_conn = prefix_len(x->mask); + } + int pl_tab = -1, met_tab = 0x7FFF; + if (x->routing_table){ + int out_pl = -1, out_met = 0x7FFF; + uint32_t nh; + if (ipv4_rt_lookup_in((const ipv4_rt_table_t*)x->routing_table, dst, &nh, &out_pl, &out_met)){ + pl_tab = out_pl; + met_tab = out_met; + } + } + int cand_pl = pl_conn; + int cand_cost = l2base; + if (pl_tab > cand_pl || (pl_tab == cand_pl && (l2base + met_tab) < cand_cost)){ + cand_pl = pl_tab; + cand_cost = l2base + met_tab; + } + if (cand_pl > best_pl || (cand_pl == best_pl && cand_cost < best_cost) || (cand_pl == best_pl && cand_cost == best_cost && l3_ids[i] < best_l3)){ + best_pl = cand_pl; + best_cost = cand_cost; + best_l3 = l3_ids[i]; + } + } + if (best_pl < 0) return false; + if (out_l3) *out_l3 = best_l3; + return true; } diff --git a/shared/net/internet_layer/ipv4_route.h b/shared/net/internet_layer/ipv4_route.h index e030427b..59fd03df 100644 --- a/shared/net/internet_layer/ipv4_route.h +++ b/shared/net/internet_layer/ipv4_route.h @@ -1,15 +1,35 @@ #pragma once #include "types.h" -#define IPV4_RT_MAX 8 +#ifdef __cplusplus +extern "C" { +#endif + +#define IPV4_RT_PER_IF_MAX 32 typedef struct { uint32_t network; uint32_t mask; uint32_t gateway; + uint16_t metric; } ipv4_rt_entry_t; -void ipv4_rt_init(); -bool ipv4_rt_add(uint32_t network, uint32_t mask, uint32_t gateway); -bool ipv4_rt_del(uint32_t network, uint32_t mask); -bool ipv4_rt_lookup(uint32_t dst, uint32_t *next_hop); +typedef struct ipv4_rt_table ipv4_rt_table_t; + +ipv4_rt_table_t* ipv4_rt_create(void); +void ipv4_rt_destroy(ipv4_rt_table_t* t); +void ipv4_rt_clear(ipv4_rt_table_t* t); + +bool ipv4_rt_add_in(ipv4_rt_table_t* t, uint32_t network, uint32_t mask, uint32_t gateway, uint16_t metric); +bool ipv4_rt_del_in(ipv4_rt_table_t* t, uint32_t network, uint32_t mask); + +bool ipv4_rt_lookup_in(const ipv4_rt_table_t* t, uint32_t dst, uint32_t *next_hop, int* out_prefix_len, int* out_metric); + +void ipv4_rt_ensure_basics(ipv4_rt_table_t* t, uint32_t ip, uint32_t mask, uint32_t gw, uint16_t base_metric); +void ipv4_rt_sync_basics(ipv4_rt_table_t* t, uint32_t ip, uint32_t mask, uint32_t gw, uint16_t base_metric); + +bool ipv4_rt_pick_best_l3_in(const uint8_t* l3_ids, int n_ids, uint32_t dst, uint8_t* out_l3); + +#ifdef __cplusplus +} +#endif diff --git a/shared/net/link_layer/arp.c b/shared/net/link_layer/arp.c index a70c8849..7641748e 100644 --- a/shared/net/link_layer/arp.c +++ b/shared/net/link_layer/arp.c @@ -1,209 +1,239 @@ #include "arp.h" #include "eth.h" -#include "console/kio.h" #include "std/memory.h" -#include "net/internet_layer/ipv4.h" -#include "networking/network.h" -#include "process/scheduler.h" -#include "types.h" #include "std/string.h" #include "networking/network.h" +#include "process/scheduler.h" +#include "console/kio.h" +#include "net/internet_layer/ipv4.h" +#include "syscalls/syscalls.h" - -#define ARP_OPCODE_REQUEST 1 -#define ARP_OPCODE_REPLY 2 - -extern void sleep(uint64_t ms); -extern void* malloc(uint64_t size); -extern void free(void *ptr, uint64_t size); +typedef struct arp_table { + arp_entry_t entries[ARP_TABLE_MAX]; + uint8_t init; +} arp_table_t; static uint16_t g_arp_pid = 0xFFFF; -static arp_entry_t g_arp_table[ARP_TABLE_MAX]; -static bool init = false; -void arp_set_pid(uint16_t pid) { g_arp_pid = pid; } -uint16_t arp_get_pid() { return g_arp_pid; } +static inline arp_table_t* l2_arp(uint8_t ifindex){ + l2_interface_t* l2 = l2_interface_find_by_index(ifindex); + return l2 ? (arp_table_t*)l2->arp_table : 0; +} -void arp_table_init() { - memset(g_arp_table, 0, sizeof(g_arp_table)); - init = true; - arp_table_init_static_defaults(); +arp_table_t* arp_table_create(void){ + arp_table_t* t = (arp_table_t*)malloc(sizeof(arp_table_t)); + if (!t) return 0; + memset(t, 0, sizeof(*t)); + t->init = 1; + arp_table_init_static_defaults(t); + return t; } -void arp_table_init_static_defaults() { - uint8_t bmac[6] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}; - arp_table_put(0xFFFFFFFF, bmac, 0, true); +void arp_table_destroy(arp_table_t* t){ + if (t) free(t, sizeof(*t)); } -static int arp_table_find_slot(uint32_t ip) { - for (int i = 0; i < ARP_TABLE_MAX; i++) { - if (g_arp_table[i].ip == ip) return i; - } +void arp_table_init_static_defaults(arp_table_t* t){ + if (!t) return; + t->entries[0].ip = 0xFFFFFFFFu; + t->entries[0].mac[0] = 0xFF; + t->entries[0].mac[1] = 0xFF; + t->entries[0].mac[2] = 0xFF; + t->entries[0].mac[3] = 0xFF; + t->entries[0].mac[4] = 0xFF; + t->entries[0].mac[5] = 0xFF; + t->entries[0].ttl_ms = 0; + t->entries[0].static_entry = 1; +} + +static int arp_find_slot(arp_table_t* t, uint32_t ip){ + if (!t) return -1; + for (int i=0;ientries[i].ip == ip) return i; return -1; } -static int arp_table_find_free() { - for (int i = 0; i < ARP_TABLE_MAX; i++) { - if (g_arp_table[i].ip == 0) return i; - } +static int arp_find_free(arp_table_t* t){ + if (!t) return -1; + for (int i=0;ientries[i].ip == 0) return i; return -1; } -void arp_table_put(uint32_t ip, const uint8_t mac[6], uint32_t ttl_ms, bool is_static) { - int idx = arp_table_find_slot(ip); - if (idx < 0) idx = arp_table_find_free(); +void arp_table_put_for_l2(uint8_t ifindex, uint32_t ip, const uint8_t mac[6], uint32_t ttl_ms, bool is_static){ + arp_table_t* t = l2_arp(ifindex); + if (!t) return; + int idx = arp_find_slot(t, ip); + if (idx < 0) idx = arp_find_free(t); if (idx < 0) idx = 0; - - g_arp_table[idx].ip = ip; - memcpy(g_arp_table[idx].mac, mac, 6); - g_arp_table[idx].ttl_ms = is_static ? 0 : ttl_ms; - g_arp_table[idx].static_entry = is_static ? 1 : 0; + t->entries[idx].ip = ip; + memcpy(t->entries[idx].mac, mac, 6); + t->entries[idx].ttl_ms = is_static ? 0 : ttl_ms; + t->entries[idx].static_entry = is_static ? 1 : 0; } -bool arp_table_get(uint32_t ip, uint8_t mac_out[6]) { - int idx = arp_table_find_slot(ip); - if (idx < 0) return false; - memcpy(mac_out, g_arp_table[idx].mac, 6); - return true; +bool arp_table_get_for_l2(uint8_t ifindex, uint32_t ip, uint8_t mac_out[6]){ + arp_table_t* t = l2_arp(ifindex); + if (!t) return false; + for (int i=0;ientries[i].ip == ip){ + memcpy(mac_out, t->entries[i].mac, 6); + return true; + } + } + return false; } -void arp_table_tick(uint32_t ms) { - for (int i = 0; i < ARP_TABLE_MAX; i++) { - if (g_arp_table[i].ip == 0 || g_arp_table[i].static_entry) - continue; - if (g_arp_table[i].ttl_ms <= ms) { - memset(&g_arp_table[i], 0, sizeof(arp_entry_t)); +void arp_table_tick_for_l2(uint8_t ifindex, uint32_t ms){ + arp_table_t* t = l2_arp(ifindex); + if (!t) return; + for (int i=0;ientries[i].ip == 0 || t->entries[i].static_entry) continue; + if (t->entries[i].ttl_ms <= ms){ + memset(&t->entries[i], 0, sizeof(arp_entry_t)); } else { - g_arp_table[i].ttl_ms -= ms; + t->entries[i].ttl_ms -= ms; } } } -bool arp_resolve(uint32_t ip, uint8_t mac_out[6], uint32_t timeout_ms) { - - if (arp_table_get(ip, mac_out)) return true; - if (ip == 0xFFFFFFFF) { +void arp_tick_all(uint32_t ms){ + for (uint8_t i=1;i<=MAX_L2_INTERFACES;i++){ + l2_interface_t* l2 = l2_interface_find_by_index(i); + if (!l2) continue; + if (!l2->arp_table) continue; + arp_table_tick_for_l2(i, ms); + } +} + +static uint32_t pick_spa_for_l2(uint8_t ifindex, uint32_t target_ip){ + l2_interface_t* l2 = l2_interface_find_by_index(ifindex); + if (!l2) return 0; + for (int s=0;sl3_v4[s]; + if (!v4) continue; + if (v4->mode == IPV4_CFG_DISABLED) continue; + if (v4->ip && v4->mask){ + uint32_t a = v4->ip & v4->mask; + uint32_t b = target_ip & v4->mask; + if (a == b) return v4->ip; + } + } + for (int s=0;sl3_v4[s]; + if (!v4) continue; + if (v4->mode == IPV4_CFG_DISABLED) continue; + if (v4->ip) return v4->ip; + } + return 0; +} + +bool arp_resolve_on(uint8_t ifindex, uint32_t ip, uint8_t mac_out[6], uint32_t timeout_ms){ + if (ip == 0xFFFFFFFFu){ memset(mac_out, 0xFF, 6); return true; } - arp_send_request(ip); - + if (arp_table_get_for_l2(ifindex, ip, mac_out)) return true; + arp_send_request_on(ifindex, ip); uint32_t waited = 0; const uint32_t POLL_MS = 100; while (waited < timeout_ms) { - arp_table_tick(POLL_MS); - if (arp_table_get(ip, mac_out)) return true; + arp_table_tick_for_l2(ifindex, POLL_MS); + if (arp_table_get_for_l2(ifindex, ip, mac_out)) return true; sleep(POLL_MS); waited += POLL_MS; } return false; } -void arp_send_request(uint32_t target_ip) { - const net_l2l3_endpoint *ep = network_get_local_endpoint(); - uint8_t dst_mac[6]; +void arp_send_request_on(uint8_t ifindex, uint32_t target_ip){ + const uint8_t* local_mac = network_get_mac(ifindex); + if (!local_mac) return; + uint32_t spa = pick_spa_for_l2(ifindex, target_ip); + uint8_t dst_mac[6] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}; arp_hdr_t hdr; - uintptr_t buf; - uint32_t len; - - memset(dst_mac, 0xFF, sizeof(dst_mac)); - memset(hdr.target_mac, 0x00, sizeof(hdr.target_mac)); - - hdr.htype = __builtin_bswap16(1); - hdr.ptype = __builtin_bswap16(0x0800); + memset(&hdr, 0, sizeof(hdr)); + hdr.htype = bswap16(1); + hdr.ptype = bswap16(ETHERTYPE_IPV4); hdr.hlen = 6; hdr.plen = 4; - hdr.opcode = __builtin_bswap16(1); - memcpy(hdr.sender_mac, ep->mac, 6); - hdr.sender_ip = __builtin_bswap32(ep->ip); - hdr.target_ip = __builtin_bswap32(target_ip); - - len = sizeof(eth_hdr_t) + sizeof(arp_hdr_t); - buf = (uintptr_t)malloc(len); + hdr.opcode = bswap16(ARP_OPCODE_REQUEST); + memcpy(hdr.sender_mac, local_mac, 6); + hdr.sender_ip = bswap32(spa); + hdr.target_ip = bswap32(target_ip); + uintptr_t buf = (uintptr_t)malloc(sizeof(hdr)); if (!buf) return; - - uintptr_t ptr = create_eth_packet(buf, ep->mac, dst_mac, 0x0806); - memcpy((void*)ptr, &hdr, sizeof(arp_hdr_t)); - - eth_send_frame(buf, len); - free((void*)buf, len); + memcpy((void*)buf, &hdr, sizeof(hdr)); + sizedptr payload = { buf, sizeof(hdr) }; + (void)eth_send_frame_on(ifindex, ETHERTYPE_ARP, dst_mac, payload); + free((void*)buf, sizeof(hdr)); } -bool arp_should_handle(const arp_hdr_t *arp, uint32_t my_ip) { - return __builtin_bswap32(arp->target_ip) == my_ip; -} - -void arp_populate_response(net_l2l3_endpoint *ep, const arp_hdr_t *arp) { - memcpy(ep->mac, arp->sender_mac, 6); - ep->ip = __builtin_bswap32(arp->sender_ip); -} - -bool arp_can_reply() { - const net_cfg_t *cfg = ipv4_get_cfg(); - return (cfg && cfg->ip != 0 && cfg->mode != NET_MODE_DISABLED); -} - - -int arp_daemon_entry(int argc, char* argv[]){ - (void)argc; (void)argv; - arp_set_pid(get_current_proc_pid()); - while (1){ - const net_cfg_t *cfg = ipv4_get_cfg(); - if(cfg && cfg->ip != 0 && cfg->mode != NET_MODE_DISABLED) break; - sleep(200); - } - arp_table_init(); - - while (1) { - arp_table_tick(1000); - sleep(1000); +static bool l2_has_ip(uint8_t ifindex, uint32_t ip){ + l2_interface_t* l2 = l2_interface_find_by_index(ifindex); + if (!l2) return false; + for (int s=0;sl3_v4[s]; + if (!v4) continue; + if (v4->mode == IPV4_CFG_DISABLED) continue; + if (v4->ip == ip) return true; } + return false; } -static void arp_send_reply(const arp_hdr_t *in_arp, - const uint8_t in_src_mac[6], - uint32_t frame_len) { - const net_l2l3_endpoint *ep = network_get_local_endpoint(); - - uint32_t len = sizeof(eth_hdr_t) + sizeof(arp_hdr_t); - uintptr_t buf = (uintptr_t)malloc(len); - if (!buf) return; - - uintptr_t ptr = create_eth_packet(buf, - ep->mac, - in_src_mac, - 0x0806); +static void arp_send_reply_on(uint8_t ifindex, const arp_hdr_t* in_arp, const uint8_t in_src_mac[6]){ + const uint8_t* local_mac = network_get_mac(ifindex); + if (!local_mac) return; + uint32_t spa = pick_spa_for_l2(ifindex, bswap32(in_arp->sender_ip)); + if (!spa) return; arp_hdr_t reply = *in_arp; memcpy(reply.target_mac, in_arp->sender_mac, 6); - memcpy(reply.sender_mac, ep->mac, 6); + memcpy(reply.sender_mac, local_mac, 6); reply.target_ip = in_arp->sender_ip; - reply.sender_ip = __builtin_bswap32(ep->ip); - reply.opcode = __builtin_bswap16(ARP_OPCODE_REPLY); + reply.sender_ip = bswap32(spa); + reply.opcode = bswap16(ARP_OPCODE_REPLY); + uintptr_t buf = (uintptr_t)malloc(sizeof(reply)); + if (!buf) return; + memcpy((void*)buf, &reply, sizeof(reply)); + sizedptr payload = { buf, sizeof(reply) }; + (void)eth_send_frame_on(ifindex, ETHERTYPE_ARP, in_src_mac, payload); + free((void*)buf, sizeof(reply)); +} - memcpy((void*)ptr, &reply, sizeof(reply)); +void arp_input(uint16_t ifindex, uintptr_t frame_ptr, uint32_t frame_len){ + if (frame_len < sizeof(eth_hdr_t) + sizeof(arp_hdr_t)) return; - eth_send_frame(buf, len); - free((void*)buf, len); -} + const eth_hdr_t* eth = (const eth_hdr_t*)frame_ptr; + const uint8_t* src_mac = eth->src_mac; + const arp_hdr_t* hdr = (const arp_hdr_t*)(frame_ptr + sizeof(eth_hdr_t)); + uint16_t op = bswap16(hdr->opcode); + uint32_t sender_ip = bswap32(hdr->sender_ip); + uint32_t target_ip = bswap32(hdr->target_ip); + arp_table_put_for_l2((uint8_t)ifindex, sender_ip, hdr->sender_mac, 180000, false); -void arp_input(uintptr_t frame_ptr, uint32_t frame_len) { - if (frame_len < sizeof(eth_hdr_t) + sizeof(arp_hdr_t)) return; + if (op == ARP_OPCODE_REQUEST) { + char tbuf[16], abuf[16]; + ipv4_to_string(target_ip, tbuf); - if(!init) return; + bool has = l2_has_ip((uint8_t)ifindex, target_ip); - arp_hdr_t *hdr = (arp_hdr_t*)(frame_ptr + sizeof(eth_hdr_t)); - uint32_t sender_ip = __builtin_bswap32(hdr->sender_ip); + uint32_t spa_guess = pick_spa_for_l2((uint8_t)ifindex, sender_ip); + ipv4_to_string(spa_guess, abuf); + if (has || (spa_guess == target_ip)) { + arp_send_reply_on((uint8_t)ifindex, hdr, src_mac); + } + } +} - arp_table_put(sender_ip, hdr->sender_mac, 180000, false); - const net_l2l3_endpoint *ep = network_get_local_endpoint(); - if (__builtin_bswap16(hdr->opcode) == ARP_OPCODE_REQUEST && - arp_should_handle(hdr, ep->ip) && - arp_can_reply()) - { - const arp_hdr_t *hdr = (arp_hdr_t*)(frame_ptr + sizeof(eth_hdr_t)); - const uint8_t *src_mac = hdr->sender_mac; - arp_send_reply(hdr, src_mac, frame_len); +void arp_set_pid(uint16_t pid){ g_arp_pid = pid; } +uint16_t arp_get_pid(void){ return g_arp_pid; } + +int arp_daemon_entry(int argc, char* argv[]){ + (void)argc; (void)argv; + arp_set_pid(get_current_proc_pid()); + const uint32_t tick_ms = 10000; + while (1){ + arp_tick_all(tick_ms); + sleep(tick_ms); } } \ No newline at end of file diff --git a/shared/net/link_layer/arp.h b/shared/net/link_layer/arp.h index 0543b2fe..0f3e11ba 100644 --- a/shared/net/link_layer/arp.h +++ b/shared/net/link_layer/arp.h @@ -1,11 +1,15 @@ #pragma once #include "types.h" -#include "net/network_types.h" +#include "networking/interface_manager.h" #ifdef __cplusplus extern "C" { #endif +#define ARP_TABLE_MAX 64 +#define ARP_OPCODE_REQUEST 1 +#define ARP_OPCODE_REPLY 2 + typedef struct __attribute__((packed)) arp_hdr_t { uint16_t htype; uint16_t ptype; @@ -18,12 +22,6 @@ typedef struct __attribute__((packed)) arp_hdr_t { uint32_t target_ip; } arp_hdr_t; -bool arp_should_handle(const arp_hdr_t *arp, uint32_t my_ip); -void arp_populate_response(net_l2l3_endpoint *ep, const arp_hdr_t *arp); -bool arp_resolve(uint32_t ip, uint8_t mac_out[6], uint32_t timeout_ms); - -#define ARP_TABLE_MAX 64 - typedef struct arp_entry { uint32_t ip; uint8_t mac[6]; @@ -31,23 +29,26 @@ typedef struct arp_entry { uint8_t static_entry;//1 static, 0 dynamic } arp_entry_t; -void arp_table_init(); - -void arp_table_put(uint32_t ip, const uint8_t mac[6], uint32_t ttl_ms, bool is_static); +typedef struct arp_table arp_table_t; -bool arp_table_get(uint32_t ip, uint8_t mac_out[6]); +arp_table_t* arp_table_create(void); +void arp_table_destroy(arp_table_t* t); +void arp_table_init_static_defaults(arp_table_t* t); -void arp_table_tick(uint32_t ms); +void arp_table_put_for_l2(uint8_t ifindex, uint32_t ip, const uint8_t mac[6], uint32_t ttl_ms, bool is_static); +bool arp_table_get_for_l2(uint8_t ifindex, uint32_t ip, uint8_t mac_out[6]); +void arp_table_tick_for_l2(uint8_t ifindex, uint32_t ms); +void arp_tick_all(uint32_t ms); -void arp_table_init_static_defaults(); +bool arp_resolve_on(uint8_t ifindex, uint32_t ip, uint8_t mac_out[6], uint32_t timeout_ms); +void arp_send_request_on(uint8_t ifindex, uint32_t target_ip); -void arp_send_request(uint32_t target_ip); +void arp_input(uint16_t ifindex, uintptr_t frame_ptr, uint32_t frame_len); -int arp_daemon_entry(int argc, char* argv[]); -bool arp_can_reply(); void arp_set_pid(uint16_t pid); -uint16_t arp_get_pid(); -void arp_input(uintptr_t frame_ptr, uint32_t frame_len); +uint16_t arp_get_pid(void); +int arp_daemon_entry(int argc, char* argv[]); + #ifdef __cplusplus } -#endif +#endif \ No newline at end of file diff --git a/shared/net/link_layer/eth.c b/shared/net/link_layer/eth.c index a2fcc7f4..68288217 100644 --- a/shared/net/link_layer/eth.c +++ b/shared/net/link_layer/eth.c @@ -1,8 +1,12 @@ #include "eth.h" -#include "arp.h" #include "std/memory.h" +#include "networking/network.h" +#include "arp.h" #include "net/internet_layer/ipv4.h" -extern int net_tx_frame(uintptr_t frame_ptr, uint32_t frame_len); +//#include "net/internet_layer/ipv6.h" +#include "console/kio.h" +#include "syscalls/syscalls.h" + uintptr_t create_eth_packet(uintptr_t p, const uint8_t src_mac[6], @@ -10,46 +14,68 @@ uintptr_t create_eth_packet(uintptr_t p, uint16_t type) { eth_hdr_t* eth =(eth_hdr_t*)p; - memcpy(eth->src_mac, src_mac, 6); + memcpy(eth->dst_mac, dst_mac, 6); - eth->ethertype = __builtin_bswap16(type); + memcpy(eth->src_mac, src_mac, 6); + eth->ethertype = bswap16(type); + return p + sizeof(eth_hdr_t); } -uint16_t eth_parse_packet_type(uintptr_t ptr) { +uint16_t eth_parse_type(uintptr_t ptr){ const eth_hdr_t* eth = (const eth_hdr_t*)ptr; - return __builtin_bswap16(eth->ethertype); + return bswap16(eth->ethertype); } -const uint8_t* eth_get_source_mac(uintptr_t ptr) { +const uint8_t* eth_src(uintptr_t ptr){ const eth_hdr_t* eth = (const eth_hdr_t*)ptr; return eth->src_mac; } -const uint8_t* eth_get_source(uintptr_t ptr){ + +const uint8_t* eth_dst(uintptr_t ptr){ const eth_hdr_t* eth = (const eth_hdr_t*)ptr; - return eth->src_mac; + return eth->dst_mac; } -bool eth_send_frame(uintptr_t frame_ptr, uint32_t frame_len){ - return net_tx_frame(frame_ptr, frame_len); +bool eth_send_frame_on(uint16_t ifindex, uint16_t ethertype, const uint8_t dst_mac[6], sizedptr payload){ + const uint8_t* src_mac = network_get_mac(ifindex); + if (!src_mac || !dst_mac) return false; + + uint32_t total = (uint32_t)sizeof(eth_hdr_t) + (uint32_t)payload.size; + uintptr_t buf = (uintptr_t)malloc(total); + if (!buf) return false; + + uintptr_t ptr = create_eth_packet(buf, src_mac, dst_mac, ethertype); + + if (payload.size) memcpy((void*)ptr, (const void*)payload.ptr, payload.size); + + bool ok = (net_tx_frame_on(ifindex, buf, total) == 0); + free((void*)buf, total); + return ok; } -void eth_input(uintptr_t frame_ptr, uint32_t frame_len) { +void eth_input(uint16_t ifindex, uintptr_t frame_ptr, uint32_t frame_len){ + if (frame_len < sizeof(eth_hdr_t)) return; - uint16_t type = eth_parse_packet_type(frame_ptr); - const uint8_t* src_mac = eth_get_source_mac(frame_ptr); + uint16_t type = eth_parse_type(frame_ptr); + const uint8_t* src_mac = eth_src(frame_ptr); uintptr_t payload_ptr = frame_ptr + sizeof(eth_hdr_t); - uint32_t payload_len = frame_len - sizeof(eth_hdr_t); - + uint32_t payload_len = frame_len - (uint32_t)sizeof(eth_hdr_t); switch (type) { - case 0x0806: - arp_input(frame_ptr, frame_len); - break; - case 0x0800: - ip_input(payload_ptr, payload_len, src_mac); - break; - default: - break; + case ETHERTYPE_ARP: + arp_input(ifindex, frame_ptr, frame_len); + break; + case ETHERTYPE_IPV4: + ipv4_input(ifindex, payload_ptr, payload_len, src_mac); + break; + case ETHERTYPE_IPV6: //TODO IPV6 + break; + case ETHERTYPE_VLAN1Q: //TODO vlan + break; + case ETHERTYPE_VLAN1AD: //TODO vlan + break; + default: + break; } } diff --git a/shared/net/link_layer/eth.h b/shared/net/link_layer/eth.h index c3eacb8c..2ffdedd4 100644 --- a/shared/net/link_layer/eth.h +++ b/shared/net/link_layer/eth.h @@ -6,23 +6,25 @@ extern "C" { #endif +#define ETHERTYPE_IPV4 0x0800 +#define ETHERTYPE_ARP 0x0806 +#define ETHERTYPE_VLAN1Q 0x8100 +#define ETHERTYPE_VLAN1AD 0x88A8 +#define ETHERTYPE_IPV6 0x86DD + typedef struct __attribute__((packed)) eth_hdr_t { uint8_t dst_mac[6]; uint8_t src_mac[6]; uint16_t ethertype; } eth_hdr_t; -uintptr_t create_eth_packet(uintptr_t ptr, - const uint8_t src_mac[6], - const uint8_t dst_mac[6], - uint16_t type); - -uint16_t eth_parse_packet_type(uintptr_t ptr); +uint16_t eth_parse_type(uintptr_t frame_ptr); +const uint8_t* eth_src(uintptr_t frame_ptr); +const uint8_t* eth_dst(uintptr_t frame_ptr); -const uint8_t* eth_get_source(uintptr_t ptr); +bool eth_send_frame_on(uint16_t ifindex, uint16_t ethertype, const uint8_t dst_mac[6], sizedptr payload); -bool eth_send_frame(uintptr_t frame_ptr, uint32_t frame_len); -void eth_input(uintptr_t frame_ptr, uint32_t frame_len); +void eth_input(uint16_t ifindex, uintptr_t frame_ptr, uint32_t frame_len); #ifdef __cplusplus } diff --git a/shared/net/network_types.h b/shared/net/network_types.h index 57b706f6..629fcf22 100644 --- a/shared/net/network_types.h +++ b/shared/net/network_types.h @@ -6,23 +6,17 @@ extern "C" { #include "types.h" -typedef enum NetProtocol { - UDP, - DHCP, - ARP, - TCP, - ICMP -} NetProtocol; - -typedef struct net_l2l3_endpoint { - uint8_t mac[6]; - uint32_t ip; //rn ipv4 only -} net_l2l3_endpoint; +typedef enum { + IP_VER4 = 4, + IP_VER6 = 6 +} ip_version_t; typedef struct net_l4_endpoint { - uint32_t ip; + ip_version_t ver; + uint8_t ip[16]; uint16_t port; } net_l4_endpoint; + #ifdef __cplusplus } #endif \ No newline at end of file diff --git a/shared/net/transport_layer/csocket_tcp.cpp b/shared/net/transport_layer/csocket_tcp.cpp index ceb86119..6ea7b868 100644 --- a/shared/net/transport_layer/csocket_tcp.cpp +++ b/shared/net/transport_layer/csocket_tcp.cpp @@ -8,66 +8,81 @@ socket_handle_t socket_tcp_create(uint8_t role, uint32_t pid) { return reinterpret_cast(new TCPSocket(role, pid)); } -int32_t socket_bind_tcp(socket_handle_t sh, uint16_t port) { - return reinterpret_cast(sh)->bind(port); +int32_t socket_bind_tcp_ex(socket_handle_t sh, const SockBindSpec* spec, uint16_t port) { + if (!sh || !spec) return SOCK_ERR_INVAL; + return reinterpret_cast(sh)->bind(*spec, port); } int32_t socket_listen_tcp(socket_handle_t sh, int32_t backlog) { + if (!sh) return SOCK_ERR_INVAL; return reinterpret_cast(sh)->listen(backlog); } socket_handle_t socket_accept_tcp(socket_handle_t sh) { + if (!sh) return nullptr; TCPSocket* srv = reinterpret_cast(sh); TCPSocket* client = srv->accept(); return reinterpret_cast(client); } -int32_t socket_connect_tcp(socket_handle_t sh, uint32_t ip, uint16_t port) { - return reinterpret_cast(sh)->connect(ip, port); +int32_t socket_connect_tcp_ex(socket_handle_t sh, uint8_t dst_kind, const void* dst, uint16_t port) { + if (!sh || !dst) return SOCK_ERR_INVAL; + return reinterpret_cast(sh)->connect(static_cast(dst_kind), dst, port); } int64_t socket_send_tcp(socket_handle_t sh, const void* buf, uint64_t len) { + if (!sh) return SOCK_ERR_INVAL; return reinterpret_cast(sh)->send(buf, len); } int64_t socket_recv_tcp(socket_handle_t sh, void* buf, uint64_t len) { + if (!sh) return SOCK_ERR_INVAL; return reinterpret_cast(sh)->recv(buf, len); } int32_t socket_close_tcp(socket_handle_t sh) { - return reinterpret_cast(sh)->close(); + if (!sh) return SOCK_ERR_INVAL; + return reinterpret_cast(sh)->close(); } void socket_destroy_tcp(socket_handle_t sh) { - delete reinterpret_cast(sh); + if (!sh) return; + delete reinterpret_cast(sh); } uint16_t socket_get_local_port_tcp(socket_handle_t sh) { - return reinterpret_cast(sh)->get_local_port(); + if (!sh) return 0; + return reinterpret_cast(sh)->get_local_port(); } -uint32_t socket_get_remote_ip_tcp(socket_handle_t sh) { - return reinterpret_cast(sh)->get_remote_ip(); +uint16_t socket_get_remote_port_tcp(socket_handle_t sh) { + if (!sh) return 0; + return reinterpret_cast(sh)->get_remote_port(); } -uint16_t socket_get_remote_port_tcp(socket_handle_t sh) { - return reinterpret_cast(sh)->get_remote_port(); +void socket_get_remote_ep_tcp(socket_handle_t sh, net_l4_endpoint* out) { + if (!sh || !out) return; + *out = reinterpret_cast(sh)->get_remote_ep(); } uint8_t socket_get_protocol_tcp(socket_handle_t sh) { - return reinterpret_cast(sh)->get_protocol(); + if (!sh) return 0xFF; + return reinterpret_cast(sh)->get_protocol(); } uint8_t socket_get_role_tcp(socket_handle_t sh) { - return reinterpret_cast(sh)->get_role(); + if (!sh) return 0xFF; + return reinterpret_cast(sh)->get_role(); } bool socket_is_bound_tcp(socket_handle_t sh) { - return reinterpret_cast(sh)->is_bound(); + if (!sh) return false; + return reinterpret_cast(sh)->is_bound(); } bool socket_is_connected_tcp(socket_handle_t sh) { - return reinterpret_cast(sh)->is_connected(); + if (!sh) return false; + return reinterpret_cast(sh)->is_connected(); } } diff --git a/shared/net/transport_layer/csocket_tcp.h b/shared/net/transport_layer/csocket_tcp.h index 300ae33a..eb815ef2 100644 --- a/shared/net/transport_layer/csocket_tcp.h +++ b/shared/net/transport_layer/csocket_tcp.h @@ -1,6 +1,8 @@ #pragma once - #include "types.h" +#include "net/network_types.h" +#include "socket.hpp" +#include "net/transport_layer/socket_types.h" #ifdef __cplusplus extern "C" { @@ -9,18 +11,18 @@ extern "C" { typedef void* socket_handle_t; socket_handle_t socket_tcp_create(uint8_t role, uint32_t pid); -int32_t socket_bind_tcp(socket_handle_t sh, uint16_t port); +int32_t socket_bind_tcp_ex(socket_handle_t sh, const SockBindSpec* spec, uint16_t port); int32_t socket_listen_tcp(socket_handle_t sh, int32_t backlog); socket_handle_t socket_accept_tcp(socket_handle_t sh); -int32_t socket_connect_tcp(socket_handle_t sh, uint32_t ip, uint16_t port); +int32_t socket_connect_tcp_ex(socket_handle_t sh, uint8_t dst_kind, const void* dst, uint16_t port); int64_t socket_send_tcp(socket_handle_t sh, const void* buf, uint64_t len); int64_t socket_recv_tcp(socket_handle_t sh, void* buf, uint64_t len); int32_t socket_close_tcp(socket_handle_t sh); void socket_destroy_tcp(socket_handle_t sh); uint16_t socket_get_local_port_tcp(socket_handle_t sh); -uint32_t socket_get_remote_ip_tcp(socket_handle_t sh); uint16_t socket_get_remote_port_tcp(socket_handle_t sh); +void socket_get_remote_ep_tcp(socket_handle_t sh, net_l4_endpoint* out); uint8_t socket_get_protocol_tcp(socket_handle_t sh); uint8_t socket_get_role_tcp(socket_handle_t sh); bool socket_is_bound_tcp(socket_handle_t sh); diff --git a/shared/net/transport_layer/csocket_udp.cpp b/shared/net/transport_layer/csocket_udp.cpp index 4dae93c3..0663e5f4 100644 --- a/shared/net/transport_layer/csocket_udp.cpp +++ b/shared/net/transport_layer/csocket_udp.cpp @@ -6,56 +6,62 @@ extern "C" socket_handle_t udp_socket_create(uint8_t role, uint32_t pid) { return reinterpret_cast(new UDPSocket(role, pid)); } -extern "C" int32_t socket_bind_udp(socket_handle_t sh, uint16_t port) { - return reinterpret_cast(sh)->bind(port); +extern "C" int32_t socket_bind_udp_ex(socket_handle_t sh, const SockBindSpec* spec, uint16_t port) { + if (!sh || !spec) return SOCK_ERR_INVAL; + return reinterpret_cast(sh)->bind(*spec, port); } -extern "C" int64_t socket_sendto_udp(socket_handle_t sh, - uint32_t ip, uint16_t port, - const void* buf, uint64_t len) { - auto sock = reinterpret_cast(sh); - return sock->sendto(ip, port, buf, len); +extern "C" int64_t socket_sendto_udp_ex(socket_handle_t sh, uint8_t dst_kind, const void* dst, uint16_t port, const void* buf, uint64_t len) { + if (!sh || !dst || !buf || !len) return SOCK_ERR_INVAL; + return reinterpret_cast(sh)->sendto(static_cast(dst_kind), dst, port, buf, len); } -extern "C" int64_t socket_recvfrom_udp(socket_handle_t sh, - void* buf, uint64_t len, - uint32_t* out_ip, uint16_t* out_port) { - auto sock = reinterpret_cast(sh); - return sock->recvfrom(buf, len, out_ip, out_port); +extern "C" int64_t socket_recvfrom_udp_ex(socket_handle_t sh, void* buf, uint64_t len, net_l4_endpoint* out_src) { + if (!sh || !buf || !len) return 0; + return reinterpret_cast(sh)->recvfrom(buf, len, out_src); } extern "C" int32_t socket_close_udp(socket_handle_t sh) { + if (!sh) return SOCK_ERR_INVAL; return reinterpret_cast(sh)->close(); } extern "C" void socket_destroy_udp(socket_handle_t sh) { + if (!sh) return; delete reinterpret_cast(sh); } extern "C" uint16_t socket_get_local_port_udp(socket_handle_t sh) { + if (!sh) return 0; return reinterpret_cast(sh)->get_local_port(); } extern "C" uint16_t socket_get_remote_port_udp(socket_handle_t sh) { + if (!sh) return 0; return reinterpret_cast(sh)->get_remote_port(); } -extern "C" uint32_t socket_get_remote_ip_udp(socket_handle_t sh) { - return reinterpret_cast(sh)->get_remote_ip(); +extern "C" void socket_get_remote_ep_udp(socket_handle_t sh, net_l4_endpoint* out) { + if (!sh || !out) return; + *out = reinterpret_cast(sh)->get_remote_ep(); } extern "C" uint8_t socket_get_protocol_udp(socket_handle_t sh) { + if (!sh) return 0xFF; return reinterpret_cast(sh)->get_protocol(); } extern "C" uint8_t socket_get_role_udp(socket_handle_t sh) { + if (!sh) return 0xFF; return reinterpret_cast(sh)->get_role(); } extern "C" bool socket_is_bound_udp(socket_handle_t sh) { + if (!sh) return false; return reinterpret_cast(sh)->is_bound(); } extern "C" bool socket_is_connected_udp(socket_handle_t sh) { + if (!sh) return false; return reinterpret_cast(sh)->is_connected(); } diff --git a/shared/net/transport_layer/csocket_udp.h b/shared/net/transport_layer/csocket_udp.h index dd37c1ec..b0440d38 100644 --- a/shared/net/transport_layer/csocket_udp.h +++ b/shared/net/transport_layer/csocket_udp.h @@ -1,5 +1,8 @@ #pragma once #include "types.h" +#include "net/network_types.h" +#include "net/transport_layer/socket.hpp" +#include "net/transport_layer/socket_types.h" #ifdef __cplusplus extern "C" { @@ -8,34 +11,21 @@ extern "C" { typedef void* socket_handle_t; socket_handle_t udp_socket_create(uint8_t role, uint32_t pid); - -int32_t socket_bind_udp(socket_handle_t sh, uint16_t port); - -int64_t socket_sendto_udp(socket_handle_t sh, - uint32_t ip, uint16_t port, - const void* buf, uint64_t len); - -int64_t socket_recvfrom_udp(socket_handle_t sh, - void* buf, uint64_t len, - uint32_t* out_ip, uint16_t* out_port); - +int32_t socket_bind_udp_ex(socket_handle_t sh, const SockBindSpec* spec, uint16_t port); +int64_t socket_sendto_udp_ex(socket_handle_t sh, uint8_t dst_kind, const void* dst, uint16_t port, const void* buf, uint64_t len); +int64_t socket_recvfrom_udp_ex(socket_handle_t sh, void* buf, uint64_t len, net_l4_endpoint* out_src); int32_t socket_close_udp(socket_handle_t sh); - void socket_destroy_udp(socket_handle_t sh); uint16_t socket_get_local_port_udp(socket_handle_t sh); - uint16_t socket_get_remote_port_udp(socket_handle_t sh); - -uint32_t socket_get_remote_ip_udp(socket_handle_t sh); +void socket_get_remote_ep_udp(socket_handle_t sh, net_l4_endpoint* out); uint8_t socket_get_protocol_udp(socket_handle_t sh); - uint8_t socket_get_role_udp(socket_handle_t sh); - bool socket_is_bound_udp(socket_handle_t sh); - bool socket_is_connected_udp(socket_handle_t sh); + #ifdef __cplusplus } #endif diff --git a/shared/net/transport_layer/socket.hpp b/shared/net/transport_layer/socket.hpp index 0454a409..4c9988dd 100644 --- a/shared/net/transport_layer/socket.hpp +++ b/shared/net/transport_layer/socket.hpp @@ -1,20 +1,15 @@ #pragma once - #include "types.h" #include "net/network_types.h" #include "networking/port_manager.h" #include "tcp.h" #include "udp.h" +#include "net/transport_layer/socket_types.h" #ifdef __cplusplus extern "C" { #endif -//protos -#define PROTO_TCP 1 -#define PROTO_UDP 2 - -//roles #define SOCK_ROLE_CLIENT 0 #define SOCK_ROLE_SERVER 1 @@ -27,54 +22,73 @@ extern "C" { #define SOCK_ERR_SYS -6 #define SOCK_ERR_PROTO -7 #define SOCK_ERR_STATE -8 +#define SOCK_ERR_DNS -9 #ifdef __cplusplus } #endif - #ifdef __cplusplus class Socket { protected: - uint16_t localPort = 0; - uint32_t remoteIP = 0; - uint16_t remotePort = 0; - uint8_t proto; - uint8_t role; - bool bound = false; - bool connected = false; - uint16_t pid = 0; - - Socket(uint8_t protocol, uint8_t r) - : proto(protocol), role(r) {} + static constexpr int SOCK_MAX_L3 = 32; + + uint16_t localPort = 0; + net_l4_endpoint remoteEP = { IP_VER4, {0}, 0 }; + + uint8_t proto = 0; + uint8_t role = 0; + bool bound = false; + bool connected = false; + uint16_t pid = 0; + + uint8_t bound_l3[SOCK_MAX_L3] = {0}; + int bound_l3_count = 0; + + Socket(uint8_t protocol, uint8_t r) : proto(protocol), role(r) {} + + virtual void do_unbind_one(uint8_t l3_id, uint16_t port, uint16_t pid) = 0; + + void clear_bound_l3() { bound_l3_count = 0; } + bool add_bound_l3(uint8_t l3_id) { if (bound_l3_count >= SOCK_MAX_L3) return false; bound_l3[bound_l3_count++] = l3_id; return true; } + void set_remote_endpoint(const net_l4_endpoint& ep) { remoteEP = ep; } public: virtual ~Socket() { close(); } - virtual int32_t bind(uint16_t port) = 0; + virtual int32_t bind(const SockBindSpec& spec, uint16_t port) = 0; virtual int32_t close() { if (bound) { - if (proto == PROTO_UDP) { - udp_unbind(localPort, pid); - } else if (proto == PROTO_TCP) { - tcp_unbind(localPort, pid); + for (int i = 0; i < bound_l3_count; ++i) { + do_unbind_one(bound_l3[i], localPort, pid); } - bound = false; + bound = false; localPort = 0; + clear_bound_l3(); } connected = false; + remoteEP.port = 0; + remoteEP.ver = IP_VER4; + memset(remoteEP.ip, 0, 16); return SOCK_OK; } uint16_t get_local_port() const { return localPort; } - uint16_t get_remote_port() const { return remotePort; } - uint32_t get_remote_ip() const { return remoteIP; } + uint16_t get_remote_port() const { return remoteEP.port; } uint8_t get_protocol() const { return proto; } uint8_t get_role() const { return role; } + uint16_t get_pid() const { return pid; } bool is_bound() const { return bound; } bool is_connected() const { return connected; } + + ip_version_t get_remote_ip_version() const { return remoteEP.ver; } + const uint8_t* get_remote_ip_bytes() const { return remoteEP.ip; } + const net_l4_endpoint& get_remote_endpoint() const { return remoteEP; } + + int get_bound_l3_count() const { return bound_l3_count; } + uint8_t get_bound_l3_id(int idx) const { return (idx >= 0 && idx < bound_l3_count) ? bound_l3[idx] : 0; } }; #endif diff --git a/shared/net/transport_layer/socket_tcp.hpp b/shared/net/transport_layer/socket_tcp.hpp index bdbccba9..48ef7c62 100644 --- a/shared/net/transport_layer/socket_tcp.hpp +++ b/shared/net/transport_layer/socket_tcp.hpp @@ -1,25 +1,24 @@ #pragma once -#include "console/kio.h" -#include "std/string.h" -#include "net/internet_layer/ipv4.h" #include "std/memory.h" +#include "std/string.h" #include "socket.hpp" #include "net/transport_layer/tcp.h" +#include "net/internet_layer/ipv4.h" +#include "net/application_layer/dns.h" #include "types.h" #include "data_struct/ring_buffer.hpp" +#include "net/transport_layer/socket_types.h" +#include "net/internet_layer/ipv4_route.h" +#include "syscalls/syscalls.h" -extern "C" { - void sleep(uint64_t ms); - void* malloc(uint64_t size); - void free(void *ptr, uint64_t size); -} static constexpr int TCP_MAX_BACKLOG = 8; +static constexpr dns_server_sel_t TCP_DNS_SEL = DNS_USE_BOTH; +static constexpr uint32_t TCP_DNS_TIMEOUT_MS = 3000; class TCPSocket : public Socket { - inline static TCPSocket* s_by_port[MAX_PORTS] = { nullptr }; inline static TCPSocket* s_list_head = nullptr; static constexpr int TCP_RING_CAP = 1024; @@ -29,50 +28,140 @@ class TCPSocket : public Socket { TCPSocket* pending[TCP_MAX_BACKLOG] = { nullptr }; int backlogCap = 0; int backlogLen = 0; + TCPSocket* next = nullptr; - static void dispatch(uintptr_t ptr, - uint32_t len, - uint32_t src_ip, - uint16_t src_port, - uint16_t dst_port) - { - if (len == 0) { - TCPSocket* srv = s_by_port[dst_port]; - if (!srv || srv->role != SOCK_ROLE_SERVER) return; - if (srv->backlogLen >= srv->backlogCap) return; - - TCPSocket* child = new TCPSocket(); - child->localPort = dst_port; - child->remoteIP = src_ip; - child->remotePort = src_port; - child->bound = true; - child->connected = true; - child->flow = tcp_get_ctx(dst_port, src_ip, src_port); - child->pid = srv->pid; - child->insert_in_global_list(); - srv->pending[srv->backlogLen++] = child; - return; + static bool contains_id(const uint8_t* arr, int n, uint8_t id){ + for (int i=0;il2) return false; + if (!v4->l2->is_up) return false; + if (v4->is_localhost) return false; + if (v4->mode == IPV4_CFG_DISABLED) return false; + if (v4->ip == 0) return false; + if (!v4->port_manager) return false; + return true; + } + + static bool socket_matches_dst(TCPSocket* s, uint8_t ifx, ip_version_t ver, const void* dst_ip_addr, uint16_t dst_port){ + if (!s->bound) return false; + if (s->localPort != dst_port) return false; + for (int i=0;ibound_l3_count;++i){ + uint8_t id = s->bound_l3[i]; + if (ver == IP_VER4){ + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(id); + if (!is_valid_v4_l3_for_bind(v4)) continue; + if (v4->l2->ifindex != ifx) continue; + if (v4->ip == *(const uint32_t*)dst_ip_addr) return true; + } else if (ver == IP_VER6){ + l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(id); + if (!v6 || !v6->l2 || !v6->l2->is_up || v6->is_localhost || v6->cfg == IPV6_CFG_DISABLE) continue; + if (v6->l2->ifindex != ifx) continue; + if (memcmp(v6->ip, dst_ip_addr, 16) == 0) return true; + } } + return false; + } - for (TCPSocket* s = s_list_head; s; s = s->next) { - if (s->connected && - s->localPort == dst_port && - s->remoteIP == src_ip && - s->remotePort == src_port) - { - s->on_receive(ptr, len); - return; + static uint8_t find_matching_l3(TCPSocket* s, uint8_t ifx, ip_version_t ver, const void* dst_ip_addr){ + for (int i=0;ibound_l3_count;++i){ + uint8_t id = s->bound_l3[i]; + if (ver == IP_VER4){ + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(id); + if (!is_valid_v4_l3_for_bind(v4)) continue; + if (v4->l2->ifindex != ifx) continue; + if (v4->ip == *(const uint32_t*)dst_ip_addr) return id; + } else if (ver == IP_VER6){ + l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(id); + if (!v6 || !v6->l2 || !v6->l2->is_up || v6->is_localhost || v6->cfg == IPV6_CFG_DISABLE) continue; + if (v6->l2->ifindex != ifx) continue; + if (memcmp(v6->ip, dst_ip_addr, 16) == 0) return id; + } + } + if (ver == IP_VER4){ + uint32_t v4dst; memcpy(&v4dst, dst_ip_addr, 4); + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_ip(v4dst); + if (is_valid_v4_l3_for_bind(v4) && v4->l2 && v4->l2->ifindex == ifx) return v4->l3_id; + } + return 0; + } + + static bool socket_matches_flow(TCPSocket* s, uint8_t ifx, ip_version_t ver, const void* dst_ip_addr, uint16_t dst_port, const void* src_ip_addr, uint16_t src_port){ + (void)ifx; + if (!s->connected) return false; + if (s->localPort != dst_port) return false; + if (s->remoteEP.port != src_port) return false; + if (s->remoteEP.ver != ver) return false; + if (ver == IP_VER4){ + if (*(const uint32_t*)s->remoteEP.ip != *(const uint32_t*)src_ip_addr) return false; + } else { + if (memcmp(s->remoteEP.ip, src_ip_addr, 16) != 0) return false; + } + return true; + } + + static void dispatch(uint8_t ifindex, ip_version_t ipver, const void* src_ip_addr, const void* dst_ip_addr, + uintptr_t frame_ptr, uint32_t frame_len, uint16_t src_port, uint16_t dst_port) + { + if (frame_len == 0){ + for (TCPSocket* srv = s_list_head; srv; srv = srv->next){ + if (srv->role != SOCK_ROLE_SERVER) continue; + if (!socket_matches_dst(srv, ifindex, ipver, dst_ip_addr, dst_port)) continue; + if (srv->backlogLen >= srv->backlogCap) break; + TCPSocket* child = new TCPSocket(SOCK_ROLE_CLIENT, srv->pid); + child->localPort = dst_port; + child->connected = true; + child->remoteEP.ver = ipver; + memset(child->remoteEP.ip, 0, 16); + if (ipver == IP_VER4) memcpy(child->remoteEP.ip, src_ip_addr, 4); + else memcpy(child->remoteEP.ip, src_ip_addr, 16); + child->remoteEP.port = src_port; + uint8_t l3id = find_matching_l3(srv, ifindex, ipver, dst_ip_addr); + child->clear_bound_l3(); + if (l3id) { + child->add_bound_l3(l3id); + } else if (ipver == IP_VER4){ + uint32_t v4dst; memcpy(&v4dst, dst_ip_addr, 4); + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_ip(v4dst); + if (is_valid_v4_l3_for_bind(v4) && v4->l2 && v4->l2->ifindex == ifindex){ + child->add_bound_l3(v4->l3_id); + } else { + for (int i=0;ibound_l3_count;i++){ + l3_ipv4_interface_t* sv4 = l3_ipv4_find_by_id(srv->bound_l3[i]); + if (sv4 && sv4->l2 && sv4->l2->is_up && sv4->l2->ifindex == ifindex){ + child->add_bound_l3(sv4->l3_id); + } + } + } + } + child->flow = tcp_get_ctx(dst_port, ipver, child->remoteEP.ip, src_port); + if (!child->flow){ + child->close(); + delete child; + break; + } + srv->pending[srv->backlogLen++] = child; + break; } + return; + } + for (TCPSocket* s = s_list_head; s; s = s->next){ + if (!socket_matches_flow(s, ifindex, ipver, dst_ip_addr, dst_port, src_ip_addr, src_port)) continue; + s->on_receive(frame_ptr, frame_len); + return; + } + if (frame_ptr && frame_len){ + free((void*)frame_ptr, frame_len); } } void on_receive(uintptr_t ptr, uint32_t len) { - auto data = reinterpret_cast(malloc(len)); + uint8_t* data = (uint8_t*)malloc(len); if (!data) return; - memcpy(data, (void*)ptr, len); - sizedptr packet = { (uintptr_t)data, len }; - + sizedptr packet { (uintptr_t)data, len }; if (!ring.push(packet)) { sizedptr dropped; ring.pop(dropped); @@ -81,80 +170,233 @@ class TCPSocket : public Socket { } } - void insert_in_global_list() { + void insert_in_list() { + for (TCPSocket* it = s_list_head; it; it = it->next){ + if (it == this) { + return; + } + } next = s_list_head; s_list_head = this; } - void remove_from_global_list() { + void remove_from_list() { TCPSocket** cur = &s_list_head; while (*cur) { - if (*cur == this) { - *cur = (*cur)->next; - break; - } + if (*cur == this) { *cur = (*cur)->next; break; } cur = &((*cur)->next); } + next = nullptr; + } + + void do_unbind_one(uint8_t l3_id, uint16_t port, uint16_t pid_) override { + (void)pid_; + if (role != SOCK_ROLE_SERVER) return; + (void)tcp_unbind_l3(l3_id, port, pid); } - TCPSocket* next =nullptr; + bool add_all_l3_on_l2(uint8_t ifindex, uint8_t* tmp_ids, int& n){ + l2_interface_t* l2 = l2_interface_find_by_index(ifindex); + if (!l2 || !l2->is_up) return false; + for (int s=0; sl3_v4[s]; + if (is_valid_v4_l3_for_bind(v4)){ if (n < SOCK_MAX_L3) tmp_ids[n++] = v4->l3_id; } + } + return n > 0; + } -public: - explicit TCPSocket(uint8_t r = SOCK_ROLE_CLIENT, uint32_t pid_ = 0) - : Socket(PROTO_TCP, r) - { - if (pid_ != 0) { - pid = pid_; - insert_in_global_list(); + bool add_all_l3_any(uint8_t* tmp_ids, int& n){ + uint8_t cnt = l2_interface_count(); + for (uint8_t i=0;iis_up) continue; + for (int s=0; sl3_v4[s]; + if (is_valid_v4_l3_for_bind(v4)){ if (n < SOCK_MAX_L3) tmp_ids[n++] = v4->l3_id; } + } } + return n > 0; + } + + static bool is_zero_ip16(const uint8_t ip[16]){ + for (int i=0;i<16;++i) if (ip[i]) return false; + return true; + } + +public: + explicit TCPSocket(uint8_t r = SOCK_ROLE_CLIENT, uint32_t pid_ = 0) : Socket(PROTO_TCP, r){ + pid = pid_; + insert_in_list(); } ~TCPSocket() override { close(); - remove_from_global_list(); + remove_from_list(); } - int32_t bind(uint16_t port) override { + int32_t bind(const SockBindSpec& spec_in, uint16_t port) override { if (role != SOCK_ROLE_SERVER) return SOCK_ERR_PERM; if (bound) return SOCK_ERR_BOUND; - if (!tcp_bind(port, pid, dispatch)) - return SOCK_ERR_SYS; - s_by_port[port] = this; - localPort = port; bound = true; + + SockBindSpec spec = spec_in; + bool empty = spec.kind == BIND_L3 && spec.l3_id==0 && spec.ifindex==0 && spec.ver==0 && is_zero_ip16(spec.ip); + if (empty) spec.kind = BIND_ANY; + + uint8_t ids[SOCK_MAX_L3]; + int n = 0; + + if (spec.kind == BIND_L3){ + if (spec.l3_id) ids[n++] = spec.l3_id; + if (n==0) return SOCK_ERR_INVAL; + } else if (spec.kind == BIND_L2){ + if (!add_all_l3_on_l2(spec.ifindex, ids, n)) return SOCK_ERR_INVAL; + } else if (spec.kind == BIND_IP){ + if (spec.ver == IP_VER4){ + uint32_t v4; memcpy(&v4, spec.ip, 4); + l3_ipv4_interface_t* ipif = l3_ipv4_find_by_ip(v4); + if (!is_valid_v4_l3_for_bind(ipif)) return SOCK_ERR_INVAL; + ids[n++] = ipif->l3_id; + } else if (spec.ver == IP_VER6){ + return SOCK_ERR_PROTO; + } else return SOCK_ERR_INVAL; + } else if (spec.kind == BIND_ANY){ + if (!add_all_l3_any(ids, n)) return SOCK_ERR_INVAL; + } else return SOCK_ERR_INVAL; + + if (n==0) return SOCK_ERR_INVAL; + + uint8_t dedup[SOCK_MAX_L3]; int m=0; + for (int i=0;i TCP_MAX_BACKLOG ? TCP_MAX_BACKLOG : max_backlog; + backlogLen = 0; return SOCK_OK; } - int32_t connect(uint32_t ip, uint16_t port) { + TCPSocket* accept(){ + const int max_iters = 100; + int iter = 0; + while (backlogLen == 0){ + if (++iter > max_iters) return nullptr; + sleep(10); + } + TCPSocket* client = pending[0]; + for (int i=1;iis_up) continue; + for (int s = 0; s < MAX_IPV4_PER_INTERFACE && n < SOCK_MAX_L3; ++s){ + l3_ipv4_interface_t* v4 = l2->l3_v4[s]; + if (!is_valid_v4_l3_for_bind(v4)) continue; + ids[n++] = v4->l3_id; + } + } + if (n == 0) return SOCK_ERR_SYS; + uint32_t dip; memcpy(&dip, d.ip, 4); + if (!ipv4_rt_pick_best_l3_in(ids, n, dip, &chosen_l3)) return SOCK_ERR_SYS; + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(chosen_l3); + if (!is_valid_v4_l3_for_bind(v4)) return SOCK_ERR_SYS; + int p = tcp_alloc_ephemeral_l3(chosen_l3, pid, dispatch); + if (p < 0) return SOCK_ERR_NO_PORT; + localPort = (uint16_t)p; + clear_bound_l3(); + add_bound_l3(chosen_l3); + } else if (bound_l3_count == 1){ + chosen_l3 = bound_l3[0]; + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(chosen_l3); + if (!is_valid_v4_l3_for_bind(v4)) return SOCK_ERR_SYS; + if (localPort == 0){ + int p = tcp_alloc_ephemeral_l3(chosen_l3, pid, dispatch); + if (p < 0) return SOCK_ERR_NO_PORT; + localPort = (uint16_t)p; + } + } else { + uint32_t dip; memcpy(&dip, d.ip, 4); + if (!ipv4_rt_pick_best_l3_in(bound_l3, bound_l3_count, dip, &chosen_l3)) return SOCK_ERR_SYS; + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(chosen_l3); + if (!is_valid_v4_l3_for_bind(v4)) return SOCK_ERR_SYS; + if (localPort == 0){ + int p = tcp_alloc_ephemeral_l3(chosen_l3, pid, dispatch); + if (p < 0) return SOCK_ERR_NO_PORT; + localPort = (uint16_t)p; + } + } tcp_data ctx_copy{}; - net_l4_endpoint dst{ip, port}; - if (!tcp_handshake(p, &dst, &ctx_copy, 0)) return SOCK_ERR_SYS; + if (!tcp_handshake_l3(chosen_l3, localPort, &d, &ctx_copy, pid)) return SOCK_ERR_SYS; - flow = tcp_get_ctx(p, ip, port); + flow = tcp_get_ctx(localPort, d.ver, (const void*)d.ip, d.port); if (!flow) return SOCK_ERR_SYS; - remoteIP = ip; - remotePort = port; + remoteEP = d; connected = true; return SOCK_OK; } - int64_t send(const void* buf, uint64_t len) { + int64_t send(const void* buf, uint64_t len){ if (!connected || !flow) return SOCK_ERR_STATE; flow->payload = { (uintptr_t)buf, (uint32_t)len }; - flow->flags = (1<flags = (1u<(len) : static_cast(res); + return (res == TCP_OK) ? (int64_t)len : (int64_t)res; } - int64_t recv(void* buf, uint64_t len) { + int64_t recv(void* buf, uint64_t len){ sizedptr p; if (!ring.pop(p)) return 0; uint32_t tocpy = p.size < (uint32_t)len ? p.size : (uint32_t)len; @@ -163,65 +405,26 @@ class TCPSocket : public Socket { return tocpy; } - int32_t listen(int max_backlog) { - if (!bound || role != SOCK_ROLE_SERVER) return SOCK_ERR_STATE; - backlogCap = max_backlog > TCP_MAX_BACKLOG ? TCP_MAX_BACKLOG : max_backlog; - backlogLen = 0; - return SOCK_OK; - } - - TCPSocket* accept() { - const int max_iters = 100; - int iter = 0; - while (backlogLen == 0) { - if (++iter > max_iters) return nullptr; - sleep(10); - } - TCPSocket* client = pending[0]; - for (int i = 1; i < backlogLen; ++i) - pending[i - 1] = pending[i]; - pending[--backlogLen] = nullptr; - return client; - } - - int32_t close_client() { - - if (connected && flow) { + int32_t close() override { + if (connected && flow){ tcp_flow_close(flow); connected = false; - } - if (bound) { - if (s_by_port[localPort] == this) { - tcp_unbind(localPort, pid); - s_by_port[localPort] = nullptr; - } - bound = false; + flow = nullptr; } sizedptr pkt; - while (ring.pop(pkt)) { - free((void*)pkt.ptr, pkt.size); - } - return SOCK_OK; - } + while (ring.pop(pkt)){ free((void*)pkt.ptr, pkt.size); } + for (int i=0;iremotePort != 0 && sock->remotePort != src_port)return; + static bool is_lbcast_ip(uint32_t ip) { + return ip == 0xFFFFFFFFu; + } - //if (sock->remoteIP != 0 && sock->remoteIP != src_ip)return; + static bool is_dbcast_for(const l3_ipv4_interface_t* v4, uint32_t dst) { + if (!v4 || !v4->mask) return false; + uint32_t b = ipv4_broadcast_calc(v4->ip, v4->mask); + return b == dst; + } - sock->on_receive(ptr, len, src_ip, src_port); + static bool socket_matches_dst(UDPSocket* s, uint8_t ifx, ip_version_t ver, const void* dst_ip_addr, uint16_t dst_port) { + if (!s->bound) return false; + if (s->localPort != dst_port) return false; + if (ver == IP_VER4) { + uint32_t dip = *(const uint32_t*)dst_ip_addr; + bool lb = is_lbcast_ip(dip); + for (int i = 0; i < s->bound_l3_count; ++i) { + uint8_t id = s->bound_l3[i]; + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(id); + if (!v4 || !v4->l2) continue; + if (v4->l2->ifindex != ifx) continue; + if (lb) return true; + if (is_dbcast_for(v4, dip)) return true; + if (v4->ip && v4->ip == dip) return true; + } + return false; + } else if (ver == IP_VER6) { + for (int i = 0; i < s->bound_l3_count; ++i) { + uint8_t id = s->bound_l3[i]; + l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(id); + if (!v6 || !v6->l2) continue; + if (v6->l2->ifindex != ifx) continue; + if (memcmp(v6->ip, dst_ip_addr, 16) == 0) return true; + } + return false; + } else { + return false; + } } - void on_receive(uintptr_t ptr, - uint32_t len, - uint32_t src_ip, - uint16_t src_port) - { - this->remoteIP = src_ip; - this->remotePort = src_port; + static void dispatch(uint8_t ifindex, ip_version_t ipver, const void* src_ip_addr, const void* dst_ip_addr, uintptr_t frame_ptr, uint32_t frame_len, uint16_t src_port, uint16_t dst_port) { + for (UDPSocket* s = s_list_head; s; s = s->next) { + if (socket_matches_dst(s, ifindex, ipver, dst_ip_addr, dst_port)) { + s->on_receive(ipver, src_ip_addr, src_port, frame_ptr, frame_len); + return; + } + } + if (frame_ptr && frame_len) free((void*)frame_ptr, frame_len); + } + void on_receive(ip_version_t ver, const void* src_ip_addr, uint16_t src_port, uintptr_t ptr, uint32_t len) { uintptr_t copy = (uintptr_t)malloc(len); + if (!copy) { - free((void*)ptr, len); + if (ptr && len) free((void*)ptr, len); return; } memcpy((void*)copy, (void*)ptr, len); - free((void*)ptr, len); + if (ptr && len) free((void*)ptr, len); - int next = (r_tail + 1) % UDP_RING_CAP; - if (next == r_head) { + int nexti = (r_tail + 1) % UDP_RING_CAP; + if (nexti == r_head) { free((void*)ring[r_head].ptr, ring[r_head].size); r_head = (r_head + 1) % UDP_RING_CAP; } ring[r_tail] = { (uintptr_t)copy, len }; - src_ips[r_tail] = src_ip; - src_ports[r_tail] = src_port; - r_tail = next; + src_eps[r_tail].ver = ver; + memset(src_eps[r_tail].ip, 0, 16); + if (ver == IP_VER4) { + uint32_t v4 = *(const uint32_t*)src_ip_addr; + memcpy(src_eps[r_tail].ip, &v4, 4); + } else if (ver == IP_VER6) { + memcpy(src_eps[r_tail].ip, src_ip_addr, 16); + } + src_eps[r_tail].port = src_port; + r_tail = nexti; + remoteEP = src_eps[(r_tail + UDP_RING_CAP - 1) % UDP_RING_CAP]; } -public: - UDPSocket(uint8_t r, uint32_t pid_) : Socket(PROTO_UDP, r) { - this->pid = pid_; - this->role = r; - this->proto = PROTO_UDP; - if (this->role == SOCK_ROLE_CLIENT) { - int p = udp_alloc_ephemeral(pid_, dispatch); - if (p >= 0) { - s_by_port[p] = this; - this->localPort = p; - this->bound = true; + void insert_in_list() { + next = s_list_head; + s_list_head = this; + } + + void remove_from_list() { + UDPSocket** cur = &s_list_head; + while (*cur) { + if (*cur == this) { + *cur = (*cur)->next; + break; + } + cur = &((*cur)->next); + } + next = nullptr; + } + + bool add_all_l3_on_l2(uint8_t ifindex, uint8_t* tmp_ids, int& n) { + l2_interface_t* l2 = l2_interface_find_by_index(ifindex); + if (!l2) return false; + for (int s = 0; s < MAX_IPV4_PER_INTERFACE; ++s) { + l3_ipv4_interface_t* v4 = l2->l3_v4[s]; + if (v4 && v4->mode != IPV4_CFG_DISABLED) { + if (n < SOCK_MAX_L3) tmp_ids[n++] = v4->l3_id; + } + } + for (int s = 0; s < MAX_IPV6_PER_INTERFACE; ++s) { + l3_ipv6_interface_t* v6 = l2->l3_v6[s]; + if (v6 && v6->cfg != IPV6_CFG_DISABLE) { + if (n < SOCK_MAX_L3) tmp_ids[n++] = v6->l3_id; + } + } + return n > 0; + } + + bool add_all_l3_any(uint8_t* tmp_ids, int& n) { + uint8_t cnt = l2_interface_count(); + for (uint8_t i = 0; i < cnt; ++i) { + l2_interface_t* l2 = l2_interface_at(i); + if (!l2) continue; + for (int s = 0; s < MAX_IPV4_PER_INTERFACE; ++s) { + l3_ipv4_interface_t* v4 = l2->l3_v4[s]; + if (v4 && v4->mode != IPV4_CFG_DISABLED) { + if (n < SOCK_MAX_L3) tmp_ids[n++] = v4->l3_id; + } + } + for (int s = 0; s < MAX_IPV6_PER_INTERFACE; ++s) { + l3_ipv6_interface_t* v6 = l2->l3_v6[s]; + if (v6 && v6->cfg != IPV6_CFG_DISABLE) { + if (n < SOCK_MAX_L3) tmp_ids[n++] = v6->l3_id; + } + } + } + return n > 0; + } + + void do_unbind_one(uint8_t l3_id, uint16_t port, uint16_t pid_) override { + (void)pid_; + udp_unbind_l3(l3_id, port, pid); + } + + static bool contains_id(const uint8_t* arr, int n, uint8_t id) { + for (int i = 0; i < n; ++i) if (arr[i] == id) return true; + return false; + } + + static bool is_lbcast(uint32_t ip) { + return ip == 0xFFFFFFFFu; + } + + static bool is_dbcast(uint32_t ip, uint8_t* out_l3) { + uint8_t cnt = l2_interface_count(); + for (uint8_t i = 0; i < cnt; ++i) { + l2_interface_t* l2 = l2_interface_at(i); + if (!l2) continue; + for (int s = 0; s < MAX_IPV4_PER_INTERFACE; ++s) { + l3_ipv4_interface_t* v4 = l2->l3_v4[s]; + if (!v4) continue; + if (v4->mode == IPV4_CFG_DISABLED) continue; + if (!v4->ip || !v4->mask) continue; + uint32_t b = ipv4_broadcast_calc(v4->ip, v4->mask); + if (b == ip) { if (out_l3) *out_l3 = v4->l3_id; return true; } } } + return false; + } + +public: + UDPSocket(uint8_t r, uint32_t pid_) : Socket(PROTO_UDP, r) { + pid = pid_; + insert_in_list(); } - - ~UDPSocket() override { close(); } - int32_t bind(uint16_t port) override { + ~UDPSocket() override { + close(); + remove_from_list(); + } + + int32_t bind(const SockBindSpec& spec, uint16_t port) override { if (role != SOCK_ROLE_SERVER) return SOCK_ERR_PERM; - if (bound) return SOCK_ERR_BOUND; - if (!udp_bind(port, pid, dispatch)) - return SOCK_ERR_SYS; - s_by_port[port] = this; - this->localPort = port; - this->bound = true; + if (bound) return SOCK_ERR_BOUND; + uint8_t ids[SOCK_MAX_L3]; + int n = 0; + if (spec.kind == BIND_L3) { + if (spec.l3_id) { ids[n++] = spec.l3_id; } + } else if (spec.kind == BIND_L2) { + if (!add_all_l3_on_l2(spec.ifindex, ids, n)) return SOCK_ERR_INVAL; + } else if (spec.kind == BIND_IP) { + if (spec.ver == IP_VER4) { + uint32_t v4; + memcpy(&v4, spec.ip, 4); + ip_resolution_result_t r = resolve_ipv4_to_interface(v4); + if (!r.found || !r.ipv4) return SOCK_ERR_INVAL; + ids[n++] = r.ipv4->l3_id; + } else if (spec.ver == IP_VER6) { + ip_resolution_result_t r6 = resolve_ipv6_to_interface(spec.ip); + if (!r6.found || !r6.ipv6) return SOCK_ERR_INVAL; + ids[n++] = r6.ipv6->l3_id; + } else { + return SOCK_ERR_INVAL; + } + } else if (spec.kind == BIND_ANY) { + if (!add_all_l3_any(ids, n)) return SOCK_ERR_INVAL; + } else { + return SOCK_ERR_INVAL; + } + uint8_t dedup[SOCK_MAX_L3]; + int m = 0; + for (int i = 0; i < n; ++i) { if (!contains_id(dedup, m, ids[i])) dedup[m++] = ids[i]; } + int bdone = 0; + for (int i = 0; i < m; ++i) { + uint8_t id = dedup[i]; + bool ok = udp_bind_l3(id, port, pid, dispatch); + if (!ok) { + for (int j = 0; j < bdone; ++j) { + uint8_t rid = dedup[j]; + udp_unbind_l3(rid, port, pid); + } + return SOCK_ERR_SYS; + } + bdone++; + } + clear_bound_l3(); + for (int i = 0; i < m; ++i) add_bound_l3(dedup[i]); + localPort = port; + bound = true; return SOCK_OK; } - - int64_t sendto(uint32_t ip, uint16_t port, const void* buf, uint64_t len) { - if (!bound) return SOCK_ERR_NOT_BOUND; - net_l4_endpoint src{ ipv4_get_cfg()->ip, localPort }; - net_l4_endpoint dst{ ip, port }; - sizedptr pay{ (uintptr_t)buf, (uint32_t)len }; - udp_send_segment(&src, &dst, pay); - this->remoteIP = ip; - this->remotePort = port; - return (int64_t)len; - } - - int64_t recvfrom(void* buf, uint64_t len, //b UDPSocket::recvfrom - uint32_t* src_ip, - uint16_t* src_port) - { - if (r_head == r_tail)return 0; - - auto p = ring[r_head]; - uint32_t ip = src_ips[r_head]; - uint16_t pt = src_ports[r_head]; - r_head = (r_head + 1) % UDP_RING_CAP; - uint32_t tocpy = p.size < len ? p.size : (uint32_t)len; - memcpy(buf, (void*)p.ptr, tocpy); + int64_t sendto(SockDstKind kind, const void* dst, uint16_t port, const void* buf, uint64_t len) { + if (!dst || !buf || len == 0) return SOCK_ERR_INVAL; + net_l4_endpoint d{}; + if (kind == DST_ENDPOINT) { + const net_l4_endpoint* ed = (const net_l4_endpoint*)dst; + d = *ed; + if (!d.port && port) d.port = port; + if (!d.port) return SOCK_ERR_INVAL; + } else if (kind == DST_DOMAIN) { + const char* host = (const char*)dst; + if (!port) return SOCK_ERR_INVAL; + uint32_t a4 = 0; + dns_result_t dr = dns_resolve_a(host, &a4, UDP_DNS_SEL, UDP_DNS_TIMEOUT_MS); + if (dr != DNS_OK) return SOCK_ERR_DNS; + d.ver = IP_VER4; + memset(d.ip, 0, 16); + memcpy(d.ip, &a4, 4); + d.port = port; + } else { + return SOCK_ERR_INVAL; + } - if (src_ip) *src_ip = ip; - if (src_port) *src_port = pt; + if (d.ver == IP_VER4) { + uint32_t dip; memcpy(&dip, d.ip, 4); + uint8_t chosen_l3 = 0; + bool is_bcast = false; + uint8_t db_l3 = 0; + if (is_lbcast(dip)) { + is_bcast = true; + } else if (is_dbcast(dip, &db_l3)) { + is_bcast = true; + chosen_l3 = db_l3; + } - this->remoteIP = ip; - this->remotePort = pt; + if (is_bcast) { + if (dip == 0xFFFFFFFFu) { + if (bound_l3_count == 0) return SOCK_ERR_SYS; + for (int i = 0; i < bound_l3_count; ++i) { + uint8_t bl3 = bound_l3[i]; + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(bl3); + if (!v4 || !v4->l2) continue; + if (!bound) { + int p = udp_alloc_ephemeral_l3(bl3, pid, dispatch); + if (p < 0) continue; + localPort = (uint16_t)p; + add_bound_l3(bl3); + bound = true; + } else { + bool present = false; + for (int k = 0; k < bound_l3_count; ++k) if (bound_l3[k] == bl3) { present = true; break; } + if (!present) { + if (!udp_bind_l3(bl3, localPort, pid, dispatch)) continue; + add_bound_l3(bl3); + } + } + net_l4_endpoint src; + src.ver = IP_VER4; + memset(src.ip, 0, 16); + memcpy(src.ip, &v4->ip, 4); + src.port = localPort; + sizedptr pay{ (uintptr_t)buf, (uint32_t)len }; + ipv4_tx_opts_t tx; tx.scope = IPV4_TX_BOUND_L3; tx.index = bl3; + udp_send_segment(&src, &d, pay, &tx); + } + remoteEP = d; + return (int64_t)len; + } else { + if (!chosen_l3) return SOCK_ERR_SYS; + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(chosen_l3); + if (!v4 || !v4->l2) return SOCK_ERR_SYS; + if (!bound) { + int p = udp_alloc_ephemeral_l3(chosen_l3, pid, dispatch); + if (p < 0) return SOCK_ERR_NO_PORT; + localPort = (uint16_t)p; + add_bound_l3(chosen_l3); + bound = true; + } else { + bool present = false; + for (int i = 0; i < bound_l3_count; ++i) if (bound_l3[i] == chosen_l3) { present = true; break; } + if (!present) { + if (!udp_bind_l3(chosen_l3, localPort, pid, dispatch)) return SOCK_ERR_SYS; + add_bound_l3(chosen_l3); + } + } + net_l4_endpoint src; + src.ver = IP_VER4; + memset(src.ip, 0, 16); + memcpy(src.ip, &v4->ip, 4); + src.port = localPort; + sizedptr pay{ (uintptr_t)buf, (uint32_t)len }; + ipv4_tx_opts_t tx; tx.scope = IPV4_TX_BOUND_L3; tx.index = chosen_l3; + udp_send_segment(&src, &d, pay, &tx); + remoteEP = d; + return (int64_t)len; + } + } else { + if (bound_l3_count == 0) { + uint8_t ids[SOCK_MAX_L3]; int n = 0; + uint8_t cnt = l2_interface_count(); + for (uint8_t i = 0; i < cnt; ++i) { + l2_interface_t* l2 = l2_interface_at(i); + if (!l2 || !l2->is_up) continue; + for (int s = 0; s < MAX_IPV4_PER_INTERFACE && n < SOCK_MAX_L3; ++s) { + l3_ipv4_interface_t* v4 = l2->l3_v4[s]; + if (!v4) continue; + if (v4->mode == IPV4_CFG_DISABLED) continue; + ids[n++] = v4->l3_id; + } + } + if (n == 0) return SOCK_ERR_SYS; + if (!ipv4_rt_pick_best_l3_in(ids, n, dip, &chosen_l3)) return SOCK_ERR_SYS; + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(chosen_l3); + if (!v4 || !v4->l2) return SOCK_ERR_SYS; + int p = udp_alloc_ephemeral_l3(chosen_l3, pid, dispatch); + if (p < 0) return SOCK_ERR_NO_PORT; + localPort = (uint16_t)p; + add_bound_l3(chosen_l3); + bound = true; + net_l4_endpoint src; + src.ver = IP_VER4; + memset(src.ip, 0, 16); + memcpy(src.ip, &v4->ip, 4); + src.port = localPort; + sizedptr pay{ (uintptr_t)buf, (uint32_t)len }; + const ipv4_tx_opts_t* txp = nullptr; + udp_send_segment(&src, &d, pay, txp); + remoteEP = d; + return (int64_t)len; + } else if (bound_l3_count == 1) { + chosen_l3 = bound_l3[0]; + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(chosen_l3); + if (!v4 || !v4->l2) return SOCK_ERR_SYS; + net_l4_endpoint src; + src.ver = IP_VER4; + memset(src.ip, 0, 16); + memcpy(src.ip, &v4->ip, 4); + src.port = localPort; + sizedptr pay{ (uintptr_t)buf, (uint32_t)len }; + const ipv4_tx_opts_t* txp = nullptr; + udp_send_segment(&src, &d, pay, txp); + remoteEP = d; + return (int64_t)len; + } else { + if (!ipv4_rt_pick_best_l3_in(bound_l3, bound_l3_count, dip, &chosen_l3)) return SOCK_ERR_SYS; + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(chosen_l3); + if (!v4 || !v4->l2) return SOCK_ERR_SYS; + bool present = false; + for (int i = 0; i < bound_l3_count; ++i) if (bound_l3[i] == chosen_l3) { present = true; break; } + if (!present) { + if (!udp_bind_l3(chosen_l3, localPort, pid, dispatch)) return SOCK_ERR_SYS; + add_bound_l3(chosen_l3); + } + net_l4_endpoint src; + src.ver = IP_VER4; + memset(src.ip, 0, 16); + memcpy(src.ip, &v4->ip, 4); + src.port = localPort; + sizedptr pay{ (uintptr_t)buf, (uint32_t)len }; + ipv4_tx_opts_t tx; tx.scope = IPV4_TX_BOUND_L3; tx.index = chosen_l3; + udp_send_segment(&src, &d, pay, &tx); + remoteEP = d; + return (int64_t)len; + } + } + } else if (d.ver == IP_VER6) { + return SOCK_ERR_PROTO; + } else { + return SOCK_ERR_INVAL; + } + } + int64_t recvfrom(void* buf, uint64_t len, net_l4_endpoint* src) { + if (r_head == r_tail) return 0; + sizedptr p = ring[r_head]; + net_l4_endpoint se = src_eps[r_head]; + r_head = (r_head + 1) % UDP_RING_CAP; + uint32_t tocpy = p.size < len ? p.size : (uint32_t)len; + memcpy(buf, (void*)p.ptr, tocpy); + if (src) *src = se; free((void*)p.ptr, p.size); + remoteEP = se; return tocpy; } int32_t close() override { - while (r_head != r_tail) { - free((void*)ring[r_head].ptr, - ring[r_head].size); - r_head = (r_head + 1) % UDP_RING_CAP; + while (r_head != r_tail) { + free((void*)ring[r_head].ptr, ring[r_head].size); + r_head = (r_head + 1) % UDP_RING_CAP; + } + return Socket::close(); } - udp_unbind(localPort, pid); - s_by_port[localPort] = nullptr; - bound = connected = false; - return Socket::close(); -} -}; -UDPSocket* UDPSocket::s_by_port[MAX_PORTS] = { nullptr }; + net_l4_endpoint get_remote_ep() const { + return remoteEP; + } +}; diff --git a/shared/net/transport_layer/tcp.c b/shared/net/transport_layer/tcp.c index a9614dc1..e5013134 100644 --- a/shared/net/transport_layer/tcp.c +++ b/shared/net/transport_layer/tcp.c @@ -4,56 +4,33 @@ #include "net/internet_layer/ipv4.h" #include "std/memory.h" #include "math/rng.h" +#include "syscalls/syscalls.h" //TODO: add mtu check and fragmentation. also fragment rebuild -extern void* malloc(uint64_t size); -extern void free(void *ptr, uint64_t size); -extern void sleep(uint64_t ms); + static tcp_flow_t tcp_flows[MAX_TCP_FLOWS]; -static inline uint16_t htons(uint16_t x) { - return (uint16_t)((x << 8) | (x >> 8)); -} -static inline uint16_t ntohs(uint16_t x) { - return htons(x); -} -static inline uint32_t htonl(uint32_t x) { - return ((x & 0x000000FFU) << 24) | - ((x & 0x0000FF00U) << 8) | - ((x & 0x00FF0000U) >> 8) | - ((x & 0xFF000000U) >> 24); -} -static inline uint32_t ntohl(uint32_t x) { - return htonl(x); -} -tcp_data* tcp_get_ctx(uint16_t local_port, - uint32_t remote_ip, - uint16_t remote_port) -{ - int idx = find_flow(local_port, remote_ip, remote_port); - if (idx < 0) - return NULL; - return &tcp_flows[idx].ctx; +static inline int ip_len(ip_version_t ver){ return ver==IP_VER6 ? 16 : 4; } +static inline uint32_t v4_u32_from_ptr(const void *p){ return *(const uint32_t*)p; } + +tcp_data* tcp_get_ctx(uint16_t local_port, ip_version_t ver, const void *remote_ip, uint16_t remote_port){ + int idx = find_flow(local_port, ver, remote_ip, remote_port); + return (idx < 0) ? NULL : &tcp_flows[idx].ctx; } -static uint32_t checksum_add(uint32_t sum, uint16_t val) { + +static uint32_t checksum_add(uint32_t sum, uint16_t val){ sum += val; - if (sum > 0xFFFF) { - sum = (sum & 0xFFFF) + 1; - } + if (sum > 0xFFFF) sum = (sum & 0xFFFF) + 1; return sum; } -uint16_t tcp_compute_checksum(const void *segment, - uint16_t seg_len, - uint32_t src_ip, - uint32_t dst_ip) -{ - const uint8_t *seg = (const uint8_t *)segment; + +static uint16_t tcp_compute_checksum_v4(const void *segment, uint16_t seg_len, uint32_t src_ip, uint32_t dst_ip){ + const uint8_t *seg = (const uint8_t*)segment; const uint64_t total_len = 12 + seg_len; uintptr_t raw = (uintptr_t)malloc(total_len); - if (!raw) { - return 0; - } + if (!raw) return 0; + uint8_t *buf = (uint8_t *)raw; buf[0] = (src_ip >> 24) & 0xFF; @@ -77,31 +54,33 @@ uint16_t tcp_compute_checksum(const void *segment, uint16_t word = (uint16_t)buf[i] << 8 | buf[i + 1]; sum = checksum_add(sum, word); } - if (total_len & 1) { uint16_t word = (uint16_t)buf[total_len - 1] << 8; sum = checksum_add(sum, word); } + uint16_t res = (uint16_t)(~sum & 0xFFFF); free((void *)raw, total_len); + return bswap16(res); +} - return htons((uint16_t)(~sum & 0xFFFF)); +static uint16_t tcp_compute_checksum_v6(const void*, uint16_t, const uint8_t[16], const uint8_t[16]) { + //TODO IPV6 + return 0; } -int find_flow(uint16_t local_port, uint32_t remote_ip, uint16_t remote_port) { +int find_flow(uint16_t local_port, ip_version_t ver, const void *remote_ip, uint16_t remote_port) { for (int i = 0; i < MAX_TCP_FLOWS; ++i) { tcp_flow_t *f = &tcp_flows[i]; - if (f->state != TCP_STATE_CLOSED) { - if (f->local_port == local_port) { - if (f->state == TCP_LISTEN) { - if (remote_ip == 0 && remote_port == 0) { - return i; - } - } - if (f->remote.ip == remote_ip && f->remote.port == remote_port) { - return i; - } - } + if (f->state == TCP_STATE_CLOSED) continue; + if (f->local_port != local_port) continue; + + if (f->state == TCP_LISTEN){ + if (!remote_ip && remote_port == 0) return i; + continue; + } + if (f->remote.ver == ver && f->remote.port == remote_port && remote_ip) { + if (memcmp(f->remote.ip, remote_ip, (uint64_t)ip_len(ver)) == 0) return i; } } return -1; @@ -110,7 +89,6 @@ int find_flow(uint16_t local_port, uint32_t remote_ip, uint16_t remote_port) { static int allocate_flow_entry() { for (int i = 0; i < MAX_TCP_FLOWS; ++i) { if (tcp_flows[i].state == TCP_STATE_CLOSED) { - tcp_flows[i].state = TCP_STATE_CLOSED; tcp_flows[i].retries = 0; return i; } @@ -119,123 +97,188 @@ static int allocate_flow_entry() { } static void free_flow_entry(int idx) { - if (idx >= 0 && idx < MAX_TCP_FLOWS) { - tcp_flows[idx].state = TCP_STATE_CLOSED; - tcp_flows[idx].local_port = 0; - tcp_flows[idx].remote.ip = 0; - tcp_flows[idx].remote.port = 0; - tcp_flows[idx].ctx.sequence = 0; - tcp_flows[idx].ctx.ack = 0; - tcp_flows[idx].ctx.flags = 0; - tcp_flows[idx].ctx.window = 0; - tcp_flows[idx].ctx.options.ptr = 0; - tcp_flows[idx].ctx.options.size = 0; - tcp_flows[idx].ctx.payload.ptr = 0; - tcp_flows[idx].ctx.payload.size = 0; - tcp_flows[idx].ctx.expected_ack = 0; - tcp_flows[idx].ctx.ack_received = 0; - tcp_flows[idx].retries = 0; + if (idx < 0 || idx >= MAX_TCP_FLOWS) return; + tcp_flow_t *f = &tcp_flows[idx]; + + f->state = TCP_STATE_CLOSED; + f->local_port = 0; + + f->local.ver = 0; + memset(f->local.ip, 0, 16); + f->local.port = 0; + f->remote.ver = 0; + memset(f->remote.ip, 0, 16); + f->remote.port = 0; + + f->ctx.sequence = 0; + f->ctx.ack = 0; + f->ctx.flags = 0; + f->ctx.window = 0; + f->ctx.options.ptr = 0; + f->ctx.options.size = 0; + f->ctx.payload.ptr = 0; + f->ctx.payload.size = 0; + f->ctx.expected_ack = 0; + f->ctx.ack_received = 0; + f->retries = 0; +} + +static inline port_manager_t* pm_for_l3(uint8_t l3_id){ + if (l3_ipv4_find_by_id(l3_id)) return ifmgr_pm_v4(l3_id); + if (l3_ipv6_find_by_id(l3_id)) return ifmgr_pm_v6(l3_id); + return NULL; +} + +static bool build_tx_opts_from_local_v4(const void* src_ip_addr, ipv4_tx_opts_t* out){ + if (!out) return false; + uint32_t lip = v4_u32_from_ptr(src_ip_addr); + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_ip(lip); + if (v4){ + out->scope = IPV4_TX_BOUND_L3; + out->index = v4->l3_id; + } else { + out->scope = IPV4_TX_AUTO; + out->index = 0; } + return true; } -static bool send_tcp_segment(uint32_t src_ip, uint32_t dst_ip, tcp_hdr_t *hdr, const uint8_t *payload, uint16_t payload_len) { +static bool build_tx_opts_from_l3(uint8_t l3_id, ipv4_tx_opts_t* out){ + if (!out) return false; + out->scope = IPV4_TX_BOUND_L3; + out->index = l3_id; + return true; +} + +static bool send_tcp_segment(ip_version_t ver, const void *src_ip_addr, const void *dst_ip_addr, tcp_hdr_t *hdr, const uint8_t *payload, uint16_t payload_len, const ipv4_tx_opts_t* txp){ uint8_t header_words = sizeof(tcp_hdr_t) / 4; - hdr->data_offset_reserved = (header_words << 4) | 0x0; - hdr->window = htons(hdr->window); - uint16_t tcp_len = sizeof(tcp_hdr_t) + payload_len; + hdr->data_offset_reserved = (uint8_t)((header_words << 4) | 0x0); + hdr->window = bswap16(hdr->window); + + uint16_t tcp_len = (uint16_t)(sizeof(tcp_hdr_t) + payload_len); uint8_t *segment = (uint8_t*) malloc(tcp_len); - if (!segment) { - return false; - } + if (!segment) return false; + memcpy(segment, hdr, sizeof(tcp_hdr_t)); - if (payload_len > 0) { - memcpy(segment + sizeof(tcp_hdr_t), payload, payload_len); - } - tcp_hdr_t *hdr_on_buf = (tcp_hdr_t*) segment; + if (payload_len) memcpy(segment + sizeof(tcp_hdr_t), payload, payload_len); + tcp_hdr_t *hdr_on_buf = (tcp_hdr_t*)segment; hdr_on_buf->checksum = 0; - uint16_t csum = tcp_compute_checksum(segment, tcp_len, src_ip, dst_ip); - hdr_on_buf->checksum = csum; - ipv4_send_segment(src_ip, dst_ip, 6, (sizedptr){ .ptr = (uintptr_t)segment, .size = tcp_len }); + + if (ver == IP_VER4){ + uint32_t s = v4_u32_from_ptr(src_ip_addr); + uint32_t d = v4_u32_from_ptr(dst_ip_addr); + hdr_on_buf->checksum = tcp_compute_checksum_v4(segment, tcp_len, s, d); + ipv4_send_packet(d, 6, (sizedptr){ (uintptr_t)segment, tcp_len }, txp); + } else { + free(segment, tcp_len); + return false; + } + free(segment, tcp_len); return true; } -static void send_reset(uint32_t src_ip, uint32_t dst_ip, - uint16_t src_port, uint16_t dst_port, - uint32_t seq, uint32_t ack, bool ack_valid) { +static void send_reset(ip_version_t ver, const void *src_ip_addr, const void *dst_ip_addr, + uint16_t src_port, uint16_t dst_port, uint32_t seq, uint32_t ack, bool ack_valid) { tcp_hdr_t rst_hdr; - rst_hdr.src_port = htons(src_port); - rst_hdr.dst_port = htons(dst_port); - if (ack_valid) { - rst_hdr.sequence = htonl(0); - rst_hdr.ack = htonl(seq + 1); + rst_hdr.src_port = bswap16(src_port); + rst_hdr.dst_port = bswap16(dst_port); + if (ack_valid){ + rst_hdr.sequence = bswap32(0); + rst_hdr.ack = bswap32(seq + 1); rst_hdr.flags = (1 << RST_F) | (1 << ACK_F); } else { - rst_hdr.sequence = htonl(ack); - rst_hdr.ack = htonl(0); + rst_hdr.sequence = bswap32(ack); + rst_hdr.ack = bswap32(0); rst_hdr.flags = (1 << RST_F); } rst_hdr.window = 0; rst_hdr.urgent_ptr = 0; - - send_tcp_segment(src_ip, dst_ip, &rst_hdr, NULL, 0); + ipv4_tx_opts_t tx; build_tx_opts_from_local_v4(src_ip_addr, &tx); + send_tcp_segment(ver, src_ip_addr, dst_ip_addr, &rst_hdr, NULL, 0, (ver==IP_VER4? &tx : NULL)); } -bool tcp_bind(uint16_t port, uint16_t pid, port_recv_handler_t handler) { - if (!port_bind_manual(PROTO_TCP, port, pid, handler)) { - return false; - } +bool tcp_bind_l3(uint8_t l3_id, uint16_t port, uint16_t pid, port_recv_handler_t handler) { + port_manager_t* pm = pm_for_l3(l3_id); + if (!pm) return false; + if (!port_bind_manual(pm, PROTO_TCP, port, pid, handler)) return false; int idx = allocate_flow_entry(); - if (idx >= 0) { - tcp_flows[idx].local_port = port; - tcp_flows[idx].remote.ip = 0; - tcp_flows[idx].remote.port = 0; - tcp_flows[idx].state = TCP_LISTEN; - tcp_flows[idx].ctx.sequence = 0; - tcp_flows[idx].ctx.ack = 0; - tcp_flows[idx].ctx.flags = 0; - tcp_flows[idx].ctx.window = 0xFFFF; - tcp_flows[idx].ctx.options.ptr = 0; - tcp_flows[idx].ctx.options.size = 0; - tcp_flows[idx].ctx.payload.ptr = 0; - tcp_flows[idx].ctx.payload.size = 0; - tcp_flows[idx].ctx.expected_ack = 0; - tcp_flows[idx].ctx.ack_received = 0; - tcp_flows[idx].retries = 0; + if (idx >= 0){ + tcp_flow_t *f = &tcp_flows[idx]; + f->local_port = port; + + f->local.ver = 0; + memset(f->local.ip, 0, 16); + f->local.port = 0; + f->remote.ver = 0; + memset(f->remote.ip, 0, 16); + f->remote.port = 0; + f->state = TCP_LISTEN; + f->ctx.sequence = 0; + f->ctx.ack = 0; + f->ctx.flags = 0; + f->ctx.window = 0xFFFF; + f->ctx.options.ptr = 0; + f->ctx.options.size = 0; + f->ctx.payload.ptr = 0; + f->ctx.payload.size = 0; + f->ctx.expected_ack = 0; + f->ctx.ack_received = 0; + f->retries = 0; } return true; } -int tcp_alloc_ephemeral(uint16_t pid, port_recv_handler_t handler) { - int port = port_alloc_ephemeral(PROTO_TCP, pid, handler); - return port; +int tcp_alloc_ephemeral_l3(uint8_t l3_id, uint16_t pid, port_recv_handler_t handler) { + port_manager_t* pm = pm_for_l3(l3_id); + if (!pm) return -1; + return port_alloc_ephemeral(pm, PROTO_TCP, pid, handler); } -bool tcp_unbind(uint16_t port, uint16_t pid) { - bool res = port_unbind(PROTO_TCP, port, pid); +bool tcp_unbind_l3(uint8_t l3_id, uint16_t port, uint16_t pid) { + port_manager_t* pm = pm_for_l3(l3_id); + if (!pm) return false; + + bool res = port_unbind(pm, PROTO_TCP, port, pid); if (res) { for (int i = 0; i < MAX_TCP_FLOWS; ++i) { if (tcp_flows[i].local_port == port) { - free_flow_entry(i); + if (tcp_flows[i].state == TCP_LISTEN) free_flow_entry(i); } } } return res; } -bool tcp_handshake(uint16_t local_port, net_l4_endpoint *dst, tcp_data *flow_ctx, uint16_t pid) { +bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, tcp_data *flow_ctx, uint16_t pid) { int idx = allocate_flow_entry(); - if (idx < 0) { - return false; - } + if (idx < 0) return false; + tcp_flow_t *flow = &tcp_flows[idx]; flow->local_port = local_port; - flow->remote.ip = dst->ip; + + flow->remote.ver = dst->ver; + memcpy(flow->remote.ip, dst->ip, (uint64_t)ip_len(dst->ver)); flow->remote.port = dst->port; + + if (dst->ver == IP_VER4){ + l3_ipv4_interface_t *v4 = l3_ipv4_find_by_id(l3_id); + if (!v4 || !v4->ip) { free_flow_entry(idx); return false; } + flow->local.ver = IP_VER4; + memset(flow->local.ip, 0, 16); + memcpy(flow->local.ip, &v4->ip, 4); + flow->local.port = local_port; + } else { + memset(flow->local.ip, 0, 16); + flow->local.ver = IP_VER6; + flow->local.port = local_port; + } + flow->state = TCP_SYN_SENT; flow->retries = TCP_SYN_RETRIES; - uint32_t iss = 1; + + const uint32_t iss = 1; flow->ctx.sequence = iss; flow->ctx.ack = 0; flow->ctx.window = 0xFFFF; @@ -243,84 +286,83 @@ bool tcp_handshake(uint16_t local_port, net_l4_endpoint *dst, tcp_data *flow_ctx flow->ctx.options.size = 0; flow->ctx.payload.ptr = 0; flow->ctx.payload.size = 0; - flow->ctx.flags = (1 << SYN_F); + flow->ctx.flags = (uint8_t)(1 << SYN_F); flow->ctx.expected_ack = iss + 1; flow->ctx.ack_received = 0; + tcp_hdr_t syn_hdr; - syn_hdr.src_port = htons(local_port); - syn_hdr.dst_port = htons(dst->port); - syn_hdr.sequence = htonl(flow->ctx.sequence); - syn_hdr.ack = htonl(0); - syn_hdr.flags = (1 << SYN_F); + syn_hdr.src_port = bswap16(local_port); + syn_hdr.dst_port = bswap16(dst->port); + syn_hdr.sequence = bswap32(flow->ctx.sequence); + syn_hdr.ack = bswap32(0); + syn_hdr.flags = (uint8_t)(1<ctx.window; syn_hdr.urgent_ptr = 0; - uint32_t src_ip = ipv4_get_cfg()->ip; + bool sent = false; - while (flow->retries-- > 0) { - sent = send_tcp_segment(src_ip, dst->ip, &syn_hdr, NULL, 0); - if (!sent) { - break; - } - uint64_t wait_ms = TCP_RETRY_TIMEOUT_MS; - uint64_t elapsed = 0; - const uint64_t interval = 50; - while (elapsed < wait_ms) { - if (flow->state == TCP_ESTABLISHED) { - *flow_ctx = flow->ctx; - return true; + if (dst->ver == IP_VER4) { + ipv4_tx_opts_t tx; build_tx_opts_from_l3(l3_id, &tx); + while (flow->retries-- > 0) { + sent = send_tcp_segment(IP_VER4, flow->local.ip, flow->remote.ip, &syn_hdr, NULL, 0, &tx); + if (!sent) { + break; } - if (flow->state == TCP_STATE_CLOSED) { - free_flow_entry(idx); - return false; + uint64_t wait_ms = TCP_RETRY_TIMEOUT_MS, elapsed = 0; + const uint64_t interval = 50; + while (elapsed < wait_ms) { + if (flow->state == TCP_ESTABLISHED) { + *flow_ctx = flow->ctx; + return true; + } + if (flow->state == TCP_STATE_CLOSED) { + free_flow_entry(idx); + return false; + } + sleep(interval); + elapsed += interval; } - sleep(interval); - elapsed += interval; } + } else { + //TODO IPV6 } + free_flow_entry(idx); return false; } tcp_result_t tcp_flow_send(tcp_data *flow_ctx) { - if (!flow_ctx) { - return TCP_INVALID; - } + if (!flow_ctx) return TCP_INVALID; + tcp_flow_t *flow = NULL; for (int i = 0; i < MAX_TCP_FLOWS; ++i) { - if (&tcp_flows[i].ctx == flow_ctx) { - flow = &tcp_flows[i]; - break; - } - } - if (!flow) { - return TCP_INVALID; + if (&tcp_flows[i].ctx == flow_ctx) { flow = &tcp_flows[i]; break; } } - - uint8_t flags = flow_ctx->flags; + if (!flow) return TCP_INVALID; + + const uint8_t flags = flow_ctx->flags; uint8_t *payload_ptr = (uint8_t*) flow_ctx->payload.ptr; uint16_t payload_len = flow_ctx->payload.size; if (flow->state != TCP_ESTABLISHED && !(flags & (1<state == TCP_CLOSE_WAIT && (flags & (1<state == TCP_CLOSE_WAIT && (flags & (1<local_port); - hdr.dst_port = htons(flow->remote.port); - hdr.sequence = htonl(flow_ctx->sequence); - hdr.ack = htonl(flow_ctx->ack); + hdr.src_port = bswap16(flow->local_port); + hdr.dst_port = bswap16(flow->remote.port); + hdr.sequence = bswap32(flow_ctx->sequence); + hdr.ack = bswap32(flow_ctx->ack); hdr.flags = flags; hdr.window = flow_ctx->window ? flow_ctx->window : 0xFFFF; hdr.urgent_ptr = 0; - uint32_t src_ip = ipv4_get_cfg()->ip; - uint32_t dst_ip = flow->remote.ip; - - bool sent = send_tcp_segment(src_ip, dst_ip, &hdr, payload_ptr, payload_len); - if (!sent) { - return TCP_RESET; + bool sent = false; + if (flow->remote.ver == IP_VER4) { + ipv4_tx_opts_t tx; build_tx_opts_from_local_v4(flow->local.ip, &tx); + sent = send_tcp_segment(IP_VER4, flow->local.ip, flow->remote.ip, &hdr, payload_ptr, payload_len, &tx); + } else { + sent = false; //TODO IPV6 } + if (!sent) return TCP_RESET; uint32_t seq_incr = payload_len; if (flags & (1<ack_received >= flow_ctx->expected_ack) { - break; - } - if (flow->state == TCP_STATE_CLOSED) { - return TCP_RESET; - } + if (flow_ctx->ack_received >= flow_ctx->expected_ack) break; + if (flow->state == TCP_STATE_CLOSED) return TCP_RESET; sleep(interval); elapsed += interval; } - if (flow_ctx->ack_received >= flow_ctx->expected_ack) { - break; - } - - if (flow->state >= TCP_CLOSING || flow->state == TCP_STATE_CLOSED) { - break; - } + if (flow_ctx->ack_received >= flow_ctx->expected_ack) break; + if (flow->state >= TCP_CLOSING || flow->state == TCP_STATE_CLOSED) break; flow_ctx->sequence -= seq_incr; - send_tcp_segment(src_ip, dst_ip, &hdr, payload_ptr, payload_len); + if (flow->remote.ver == IP_VER4) { + ipv4_tx_opts_t tx; build_tx_opts_from_local_v4(flow->local.ip, &tx); + send_tcp_segment(IP_VER4, flow->local.ip, flow->remote.ip, &hdr, payload_ptr, payload_len, &tx); + } flow_ctx->sequence += seq_incr; } if (flow_ctx->ack_received < flow_ctx->expected_ack) { @@ -374,17 +410,19 @@ tcp_result_t tcp_flow_close(tcp_data *flow_ctx) { if (!flow) return TCP_INVALID; if (flow->state == TCP_ESTABLISHED || flow->state == TCP_CLOSE_WAIT) { + flow_ctx->sequence = flow->ctx.sequence; + flow_ctx->ack = flow->ctx.ack; + flow_ctx->window = flow->ctx.window ? flow->ctx.window : 0xFFFF; flow_ctx->payload.ptr = 0; flow_ctx->payload.size = 0; - flow_ctx->flags = (1u << FIN_F) | (1u << ACK_F); + flow_ctx->flags = (uint8_t)((1u << FIN_F) | (1u << ACK_F)); tcp_result_t res = tcp_flow_send(flow_ctx); if (res != TCP_OK) return res; - if (flow->state == TCP_ESTABLISHED) { - flow->state = TCP_FIN_WAIT_1; - } else { flow->state = TCP_LAST_ACK; } + if (flow->state == TCP_ESTABLISHED) flow->state = TCP_FIN_WAIT_1; + else flow->state = TCP_LAST_ACK; const uint64_t max_wait = 2000; const uint64_t interval = 100; @@ -399,37 +437,44 @@ tcp_result_t tcp_flow_close(tcp_data *flow_ctx) { free_flow_entry(idx); return TCP_OK; } - return TCP_INVALID; } +void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_addr, uint8_t l3_id, uintptr_t ptr, uint32_t len) { + if (len < sizeof(tcp_hdr_t)) return; -void tcp_input(uintptr_t ptr, uint32_t len, uint32_t src_ip, uint32_t dst_ip) { - if (len < sizeof(tcp_hdr_t)) { - return; - } tcp_hdr_t *hdr = (tcp_hdr_t*) ptr; - uint16_t recv_checksum = hdr->checksum; - hdr->checksum = 0; - uint16_t calc_checksum = tcp_compute_checksum((uint8_t*)hdr, (uint16_t)len, src_ip, dst_ip); - hdr->checksum = recv_checksum; - if (recv_checksum != calc_checksum) return; + if (ipver == IP_VER4){ + uint16_t recv_checksum = hdr->checksum; + hdr->checksum = 0; + uint16_t calc = tcp_compute_checksum_v4((uint8_t*)hdr, (uint16_t)len, v4_u32_from_ptr(src_ip_addr), v4_u32_from_ptr(dst_ip_addr)); + hdr->checksum = recv_checksum; + if (recv_checksum != calc) return; + } else { + //TODO IPV6 + } - uint16_t src_port = ntohs(hdr->src_port); - uint16_t dst_port = ntohs(hdr->dst_port); - uint32_t seq = ntohl(hdr->sequence); - uint32_t ack = ntohl(hdr->ack); + uint16_t src_port = bswap16(hdr->src_port); + uint16_t dst_port = bswap16(hdr->dst_port); + uint32_t seq = bswap32(hdr->sequence); + uint32_t ack = bswap32(hdr->ack); uint8_t flags = hdr->flags; - uint16_t window = ntohs(hdr->window); - int idx = find_flow(dst_port, src_ip, src_port); + uint16_t window = bswap16(hdr->window); + + int idx = find_flow(dst_port, ipver, src_ip_addr, src_port); tcp_flow_t *flow = (idx >= 0 ? &tcp_flows[idx] : NULL); - if (flow) { - flow->ctx.window = window; - } + if (flow) flow->ctx.window = window; + + l3_ipv4_interface_t *v4 = l3_ipv4_find_by_id(l3_id); + l3_ipv6_interface_t *v6 = v4 ? NULL : l3_ipv6_find_by_id(l3_id); + port_manager_t *pm = v4 ? ifmgr_pm_v4(l3_id) : (v6 ? ifmgr_pm_v6(l3_id) : NULL); + if (!pm) return; + + uint8_t ifx = v4 ? (v4->l2 ? v4->l2->ifindex : 0) : (v6 && v6->l2 ? v6->l2->ifindex : 0); if (!flow) { - int listen_idx = find_flow(dst_port, 0, 0); + int listen_idx = find_flow(dst_port, IP_VER4, NULL, 0); if ((flags & (1u<= 0) { rng_t rng; rng_init_random(&rng); @@ -440,8 +485,17 @@ void tcp_input(uintptr_t ptr, uint32_t len, uint32_t src_ip, uint32_t dst_ip) { flow = &tcp_flows[new_idx]; flow->local_port = dst_port; - flow->remote.ip = src_ip; + + flow->remote.ver = ipver; + memset(flow->remote.ip, 0, 16); + memcpy(flow->remote.ip, src_ip_addr, (uint64_t)ip_len(ipver)); flow->remote.port = src_port; + + flow->local.ver = ipver; + memset(flow->local.ip, 0, 16); + if (ipver == IP_VER4 && v4) memcpy(flow->local.ip, &v4->ip, 4); + flow->local.port = dst_port; + flow->state = TCP_SYN_RECEIVED; flow->retries = TCP_SYN_RETRIES; @@ -458,22 +512,25 @@ void tcp_input(uintptr_t ptr, uint32_t len, uint32_t src_ip, uint32_t dst_ip) { flow->ctx.ack_received = 0; tcp_hdr_t synack_hdr; - synack_hdr.src_port = htons(dst_port); - synack_hdr.dst_port = htons(src_port); - synack_hdr.sequence = htonl(iss); - synack_hdr.ack = htonl(seq + 1); - synack_hdr.flags = (uint8_t)((1u<ctx.window); + synack_hdr.src_port = bswap16(dst_port); + synack_hdr.dst_port = bswap16(src_port); + synack_hdr.sequence = bswap32(iss); + synack_hdr.ack = bswap32(seq + 1); + synack_hdr.flags = (uint8_t)((1u<ctx.window; synack_hdr.urgent_ptr = 0; - uint32_t src_ip_local = ipv4_get_cfg()->ip; - send_tcp_segment(src_ip_local, src_ip, &synack_hdr, NULL, 0); + + if (ipver == IP_VER4 && v4) { + ipv4_tx_opts_t tx; build_tx_opts_from_l3(l3_id, &tx); + send_tcp_segment(IP_VER4, flow->local.ip, src_ip_addr, &synack_hdr, NULL, 0, &tx); + } return; } else { if (!(flags & (1u<ctx.expected_ack) { - flow->ctx.ack = seq + 1; flow->ctx.ack_received = ack; flow->ctx.sequence += 1; tcp_hdr_t final_ack; - final_ack.src_port = htons(flow->local_port); - final_ack.dst_port = htons(flow->remote.port); - final_ack.sequence = htonl(flow->ctx.sequence + 1); - final_ack.ack = htonl(flow->ctx.ack); + final_ack.src_port = bswap16(flow->local_port); + final_ack.dst_port = bswap16(flow->remote.port); + final_ack.sequence = bswap32(flow->ctx.sequence); + final_ack.ack = bswap32(flow->ctx.ack); final_ack.flags = (1<ctx.window; final_ack.urgent_ptr = 0; - uint32_t src_ip_local = ipv4_get_cfg()->ip; - send_tcp_segment(src_ip_local, flow->remote.ip, &final_ack, NULL, 0); + if (flow->remote.ver == IP_VER4){ + ipv4_tx_opts_t tx; build_tx_opts_from_local_v4(flow->local.ip, &tx); + (void)send_tcp_segment(IP_VER4, flow->local.ip, flow->remote.ip, &final_ack, NULL, 0, &tx); + } flow->state = TCP_ESTABLISHED; } } else if (flags & (1<ctx.sequence += 1; flow->state = TCP_ESTABLISHED; flow->ctx.ack_received = ack; - port_recv_handler_t h = port_get_handler(PROTO_TCP, dst_port); - if (h) { - h(0, 0, src_ip, src_port, dst_port); + + if (pm){ + port_recv_handler_t h = port_get_handler(pm, PROTO_TCP, dst_port); + if (h) h(ifx, ipver, src_ip_addr, dst_ip_addr, 0, 0, src_port, dst_port); } } - } else if (flags & (1<data_offset_reserved >> 4) * 4); - if (len < hdr_len) return; - uint32_t data_len = len - hdr_len; - const bool is_ack_only = - ( (flags & (1u << ACK_F)) != 0 ) && - ( (flags & ((1u << SYN_F) | (1u << FIN_F) | (1u << RST_F))) == 0 ) && - ( data_len == 0 ) && - ( seq + 1u == flow->ctx.ack ); - - if (is_ack_only) { - tcp_hdr_t ack_hdr = (tcp_hdr_t){ - .src_port = htons(flow->local_port), - .dst_port = htons(flow->remote.port), - .sequence = htonl(flow->ctx.sequence), - .ack = htonl(flow->ctx.ack), - .flags = (uint8_t)(1u << ACK_F), - .window = htons(flow->ctx.window), - .urgent_ptr = 0 - }; - send_tcp_segment( - ipv4_get_cfg()->ip, - flow->remote.ip, - &ack_hdr, NULL, 0 - ); - return; - } - - __attribute__((fallthrough)); - } + case TCP_ESTABLISHED: case TCP_FIN_WAIT_1: case TCP_FIN_WAIT_2: case TCP_CLOSE_WAIT: case TCP_CLOSING: case TCP_LAST_ACK: { - if (flags & (1 << RST_F)) { - free_flow_entry(idx); - return; - } - uint8_t hdr_len =(hdr->data_offset_reserved >> 4) * 4; - if (len < hdr_len) - return; - uint32_t data_len = len - hdr_len; - bool fin_set = (flags & (1 << FIN_F)) != 0; - bool fin_inseq = fin_set && (seq == flow->ctx.ack); - - if (data_len && seq == flow->ctx.ack) { - flow->ctx.ack += data_len; - port_recv_handler_t h = port_get_handler(PROTO_TCP, dst_port); - if (h) h(ptr + hdr_len, data_len, src_ip, src_port, dst_port); - } + if (flags & (1 << RST_F)) { + free_flow_entry(idx); + return; + } - if (fin_inseq) { - flow->ctx.ack += 1; - } + uint8_t hdr_len = (uint8_t)((hdr->data_offset_reserved >> 4) * 4); + if (len < hdr_len) return; + uint32_t data_len = len - hdr_len; - if ((data_len && seq == flow->ctx.ack - data_len) || fin_inseq) { - tcp_hdr_t ackhdr = { - .src_port = htons(flow->local_port), - .dst_port = htons(flow->remote.port), - .sequence = htonl(flow->ctx.sequence), - .ack = htonl(flow->ctx.ack), - .flags = (1 << ACK_F), - .window = flow->ctx.window, - .urgent_ptr = 0 - }; - send_tcp_segment(ipv4_get_cfg()->ip, - flow->remote.ip, &ackhdr, NULL, 0); - } + if ((flags & (1u << ACK_F)) && ack > flow->ctx.ack_received) { + flow->ctx.ack_received = ack; + if (flow->state == TCP_FIN_WAIT_1 && ack == flow->ctx.expected_ack){ + flow->state = TCP_FIN_WAIT_2; + } else if ((flow->state == TCP_LAST_ACK || flow->state == TCP_CLOSING) && ack == flow->ctx.expected_ack) { + free_flow_entry(idx); + return; + } + } - if ((flags & (1 << ACK_F)) && - ack > flow->ctx.ack_received) { - flow->ctx.ack_received = ack; + uint32_t rcv_next_old = flow->ctx.ack; + uint32_t rcv_next_new = rcv_next_old; - if (flow->state == TCP_FIN_WAIT_1 && - ack == flow->ctx.expected_ack) { - flow->state = TCP_FIN_WAIT_2; - } else if ((flow->state == TCP_LAST_ACK || - flow->state == TCP_CLOSING) && - ack == flow->ctx.expected_ack) { - free_flow_entry(idx); - return; + bool data_inseq = (data_len > 0) && (seq == rcv_next_old); + if (data_inseq) { + rcv_next_new += data_len; + if (pm){ + port_recv_handler_t h = port_get_handler(pm, PROTO_TCP, dst_port); + if (h) h(ifx, ipver, src_ip_addr, dst_ip_addr, ptr + hdr_len, data_len, src_port, dst_port); + } } - } - if (fin_inseq) { - if (flow->state == TCP_ESTABLISHED) - flow->state = TCP_CLOSE_WAIT; - else if (flow->state == TCP_FIN_WAIT_1) - flow->state = TCP_CLOSING; - else if (flow->state == TCP_FIN_WAIT_2) - flow->state = TCP_TIME_WAIT; - else if (flow->state == TCP_CLOSING) - flow->state = TCP_TIME_WAIT; - else if (flow->state == TCP_LAST_ACK) - flow->state = TCP_TIME_WAIT; + bool fin_set = (flags & (1<ctx.ack = rcv_next_new; + + tcp_hdr_t ackhdr = { + .src_port = bswap16(flow->local_port), + .dst_port = bswap16(flow->remote.port), + .sequence = bswap32(flow->ctx.sequence), + .ack = bswap32(flow->ctx.ack), + .flags = (uint8_t)(1<ctx.window ? flow->ctx.window : 0xFFFF, + .urgent_ptr = 0 + }; + if (flow->remote.ver == IP_VER4){ + ipv4_tx_opts_t tx; build_tx_opts_from_local_v4(flow->local.ip, &tx); + (void)send_tcp_segment(IP_VER4, flow->local.ip, flow->remote.ip, &ackhdr, NULL, 0, &tx); + } + } + + if (fin_inseq){ + tcp_state_t old = flow->state; + if (old == TCP_ESTABLISHED) flow->state = TCP_CLOSE_WAIT; + else if (old == TCP_FIN_WAIT_1) flow->state = TCP_CLOSING; + else if (old == TCP_FIN_WAIT_2) flow->state = TCP_TIME_WAIT; + else if (old == TCP_CLOSING) flow->state = TCP_TIME_WAIT; + else if (old == TCP_LAST_ACK) flow->state = TCP_TIME_WAIT; + } + return; } - return; -} + default: break; } diff --git a/shared/net/transport_layer/tcp.h b/shared/net/transport_layer/tcp.h index dcfca186..222215d7 100644 --- a/shared/net/transport_layer/tcp.h +++ b/shared/net/transport_layer/tcp.h @@ -70,8 +70,9 @@ typedef enum { typedef struct { uint16_t local_port; + net_l4_endpoint local; net_l4_endpoint remote; - tcp_state_t state; + tcp_state_t state; tcp_data ctx; uint8_t retries; } tcp_flow_t; @@ -81,36 +82,19 @@ typedef struct { #define TCP_DATA_RETRIES 5 #define TCP_RETRY_TIMEOUT_MS 1000 -int find_flow(uint16_t local_port, uint32_t remote_ip, uint16_t remote_port); +int find_flow(uint16_t local_port, ip_version_t ver, const void *remote_ip, uint16_t remote_port); +tcp_data* tcp_get_ctx(uint16_t local_port, ip_version_t ver, const void *remote_ip, uint16_t remote_port); -tcp_data* tcp_get_ctx(uint16_t local_port, - uint32_t remote_ip, - uint16_t remote_port); - -bool tcp_bind(uint16_t port, - uint16_t pid, - port_recv_handler_t handler); - -int tcp_alloc_ephemeral(uint16_t pid, - port_recv_handler_t handler); - -bool tcp_unbind(uint16_t port, - uint16_t pid); - -bool tcp_handshake(uint16_t local_port, - net_l4_endpoint *dst, - tcp_data *flow_ctx, - uint16_t pid); +bool tcp_bind_l3(uint8_t l3_id, uint16_t port, uint16_t pid, port_recv_handler_t handler); +int tcp_alloc_ephemeral_l3(uint8_t l3_id, uint16_t pid, port_recv_handler_t handler); +bool tcp_unbind_l3(uint8_t l3_id, uint16_t port, uint16_t pid); +bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, tcp_data *flow_ctx, uint16_t pid); tcp_result_t tcp_flow_send(tcp_data *flow_ctx); - tcp_result_t tcp_flow_close(tcp_data *flow_ctx); -void tcp_input(uintptr_t ptr, - uint32_t len, - uint32_t src_ip, - uint32_t dst_ip); +void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_addr, uint8_t l3_id, uintptr_t ptr, uint32_t len); #ifdef __cplusplus } -#endif +#endif \ No newline at end of file diff --git a/shared/net/transport_layer/udp.c b/shared/net/transport_layer/udp.c index 829095c2..64f01d79 100644 --- a/shared/net/transport_layer/udp.c +++ b/shared/net/transport_layer/udp.c @@ -1,80 +1,64 @@ #include "udp.h" #include "net/checksums.h" #include "net/internet_layer/ipv4.h" -#include "networking/port_manager.h" +#include "networking/port_manager.h" #include "std/memory.h" #include "types.h" +#include "syscalls/syscalls.h" -extern void sleep(uint64_t ms); -extern void* malloc(uint64_t size); -extern void free(void *ptr, uint64_t size); - -bool udp_bind(uint16_t port, - uint16_t pid, - port_recv_handler_t handler) -{ - return port_bind_manual(PROTO_UDP, port, pid, handler); -} - -int udp_alloc_ephemeral(uint16_t pid, - port_recv_handler_t handler) -{ - return port_alloc_ephemeral(PROTO_UDP, pid, handler); +static inline uint32_t v4_u32_from_arr(const uint8_t ip16[16]) { + uint32_t v = 0; + memcpy(&v, ip16, 4); + return v; } -bool udp_unbind(uint16_t port, - uint16_t pid) -{ - return port_unbind(PROTO_UDP, port, pid); -} - - -size_t create_udp_segment(uintptr_t buf, - const net_l4_endpoint *src, - const net_l4_endpoint *dst, - sizedptr payload) -{ - udp_hdr_t *udp = (udp_hdr_t*)buf; +size_t create_udp_segment(uintptr_t buf, const net_l4_endpoint *src, const net_l4_endpoint *dst, sizedptr payload) { + udp_hdr_t *udp = (udp_hdr_t *)buf; udp->src_port = bswap16(src->port); udp->dst_port = bswap16(dst->port); - uint16_t full_len = sizeof(*udp) + payload.size; - udp->length= bswap16(full_len); + uint16_t full_len = (uint16_t)(sizeof(*udp) + payload.size); + udp->length = bswap16(full_len); udp->checksum = 0; - memcpy((void*)(buf + sizeof(*udp)), (void*)payload.ptr, payload.size); + memcpy((void *)(buf + sizeof(*udp)), (void *)payload.ptr, payload.size); + + if (src->ver == IP_VER4) { + uint32_t s = v4_u32_from_arr(src->ip); + uint32_t d = v4_u32_from_arr(dst->ip); + uint16_t csum = checksum16_pipv4(s, d, 0x11, (const uint8_t *)udp, full_len); + udp->checksum = bswap16(csum); + } else if (src->ver == IP_VER6) { + //TODO IPV6 + udp->checksum = 0; + } - uint16_t csum = checksum16_pipv4( src->ip, dst->ip, 0x11, (const uint8_t*)udp, full_len); - udp->checksum = bswap16(csum); return full_len; } -void udp_send_segment(const net_l4_endpoint *src, - const net_l4_endpoint *dst, - sizedptr payload) -{ - uint32_t udp_max = sizeof(udp_hdr_t) + payload.size; - uint32_t ip_max = sizeof(ipv4_hdr_t) + udp_max; - uint32_t eth_total = sizeof(eth_hdr_t) + ip_max; - - uintptr_t buf = (uintptr_t)malloc(eth_total); - if (!buf) return; +void udp_send_segment(const net_l4_endpoint *src, const net_l4_endpoint *dst, sizedptr payload, const ipv4_tx_opts_t* tx_opts) { + if (src->ver == IP_VER4) { + uint32_t udp_len = (uint32_t)(sizeof(udp_hdr_t) + payload.size); + uintptr_t buf = (uintptr_t)malloc(udp_len); + if (!buf) return; - uintptr_t udp_buf = buf + sizeof(eth_hdr_t) + sizeof(ipv4_hdr_t); - size_t udp_len = create_udp_segment(udp_buf, src, dst, payload); + size_t written = create_udp_segment(buf, src, dst, payload); + uint32_t dst_ip = v4_u32_from_arr(dst->ip); - ipv4_send_segment(src->ip, dst->ip, 0x11,(sizedptr){ udp_buf, (uint32_t)udp_len }); - - free((void*)buf, eth_total); + ipv4_send_packet(dst_ip, 0x11, (sizedptr){ buf, (uint32_t)written }, tx_opts); + free((void *)buf, udp_len); + } else if (src->ver == IP_VER6) { + //TODO IPV6 + } } sizedptr udp_strip_header(uintptr_t ptr, uint32_t len) { if (len < sizeof(udp_hdr_t)) { - return (sizedptr){0,0}; + return (sizedptr){ 0, 0 }; } - udp_hdr_t *hdr = (udp_hdr_t*)ptr; + udp_hdr_t *hdr = (udp_hdr_t *)ptr; uint16_t total = bswap16(hdr->length); if (total < sizeof(udp_hdr_t) || total > len) { - return (sizedptr){0,0}; + return (sizedptr){ 0, 0 }; } return (sizedptr){ .ptr = ptr + sizeof(udp_hdr_t), @@ -82,31 +66,66 @@ sizedptr udp_strip_header(uintptr_t ptr, uint32_t len) { }; } -void udp_input(uintptr_t ptr, - uint32_t len, - uint32_t src_ip, - uint32_t dst_ip) -{ +void udp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_addr, uint8_t l3_id, uintptr_t ptr, uint32_t len) { sizedptr pl = udp_strip_header(ptr, len); if (!pl.ptr) return; - udp_hdr_t *hdr = (udp_hdr_t*)ptr; + udp_hdr_t *hdr = (udp_hdr_t *)ptr; + if (hdr->checksum) { - uint16_t recv = hdr->checksum; - hdr->checksum = 0; - uint16_t calc = checksum16_pipv4( - src_ip, dst_ip, 0x11, - (const uint8_t*)hdr, - pl.size + sizeof(*hdr) - ); - hdr->checksum = recv; - if (calc != bswap16(recv)) return; + if (ipver == IP_VER4) { + uint16_t recv = hdr->checksum; + hdr->checksum = 0; + uint16_t calc = checksum16_pipv4( + *(const uint32_t *)src_ip_addr, *(const uint32_t *)dst_ip_addr, 0x11, + (const uint8_t *)hdr, (uint16_t)(pl.size + sizeof(*hdr)) + ); + hdr->checksum = recv; + if (calc != bswap16(recv)) return; + } else if (ipver == IP_VER6) { + //TODO IPV& + } } uint16_t dst_port = bswap16(hdr->dst_port); uint16_t src_port = bswap16(hdr->src_port); - port_recv_handler_t handler = port_get_handler(PROTO_UDP, dst_port); + + l3_ipv4_interface_t *v4 = l3_ipv4_find_by_id(l3_id); + l3_ipv6_interface_t *v6 = v4 ? NULL : l3_ipv6_find_by_id(l3_id); + port_manager_t *pm = v4 ? ifmgr_pm_v4(l3_id) : (v6 ? ifmgr_pm_v6(l3_id) : NULL); + if (!pm) return; + + port_recv_handler_t handler = port_get_handler(pm, PROTO_UDP, dst_port); if (handler) { - handler(pl.ptr, pl.size, src_ip, src_port, dst_port); + uintptr_t copy = (uintptr_t)malloc(pl.size); + if (!copy) return; + memcpy((void*)copy, (const void*)pl.ptr, pl.size); + + uint8_t ifx = v4 ? v4->l2->ifindex : (v6 && v6->l2 ? v6->l2->ifindex : 0); + handler(ifx, ipver, src_ip_addr, dst_ip_addr, copy, pl.size, src_port, dst_port); } +} + +static inline port_manager_t* pm_for_l3(uint8_t l3_id) { + if (l3_ipv4_find_by_id(l3_id)) return ifmgr_pm_v4(l3_id); + if (l3_ipv6_find_by_id(l3_id)) return ifmgr_pm_v6(l3_id); + return NULL; +} + +bool udp_bind_l3(uint8_t l3_id, uint16_t port, uint16_t pid, port_recv_handler_t handler) { + port_manager_t* pm = pm_for_l3(l3_id); + if (!pm) return false; + return port_bind_manual(pm, PROTO_UDP, port, pid, handler); +} + +bool udp_unbind_l3(uint8_t l3_id, uint16_t port, uint16_t pid) { + port_manager_t* pm = pm_for_l3(l3_id); + if (!pm) return false; + return port_unbind(pm, PROTO_UDP, port, pid); +} + +int udp_alloc_ephemeral_l3(uint8_t l3_id, uint16_t pid, port_recv_handler_t handler) { + port_manager_t* pm = pm_for_l3(l3_id); + if (!pm) return -1; + return port_alloc_ephemeral(pm, PROTO_UDP, pid, handler); } \ No newline at end of file diff --git a/shared/net/transport_layer/udp.h b/shared/net/transport_layer/udp.h index eb5da1d7..5132ed61 100644 --- a/shared/net/transport_layer/udp.h +++ b/shared/net/transport_layer/udp.h @@ -2,6 +2,7 @@ #include "types.h" #include "net/network_types.h" #include "networking/port_manager.h" +#include "net/internet_layer/ipv4.h" #ifdef __cplusplus extern "C" { @@ -19,25 +20,19 @@ size_t create_udp_segment(uintptr_t buf, const net_l4_endpoint *dst, sizedptr payload); +void udp_send_segment(const net_l4_endpoint *src, const net_l4_endpoint *dst, sizedptr payload, const ipv4_tx_opts_t* tx_opts); -void udp_send_segment(const net_l4_endpoint *src, - const net_l4_endpoint *dst, - sizedptr payload); +void udp_input(ip_version_t ipver, + const void *src_ip_addr, + const void *dst_ip_addr, + uint8_t l3_id, + uintptr_t ptr, + uint32_t len); -void udp_input(uintptr_t ptr, - uint32_t len, - uint32_t src_ip, - uint32_t dst_ip); - -bool udp_bind(uint16_t port, - uint16_t pid, - port_recv_handler_t handler); - -int udp_alloc_ephemeral(uint16_t pid, port_recv_handler_t handler); - -bool udp_unbind(uint16_t port, - uint16_t pid); +bool udp_bind_l3(uint8_t l3_id, uint16_t port, uint16_t pid, port_recv_handler_t handler); +bool udp_unbind_l3(uint8_t l3_id, uint16_t port, uint16_t pid); +int udp_alloc_ephemeral_l3(uint8_t l3_id, uint16_t pid, port_recv_handler_t handler); #ifdef __cplusplus } -#endif +#endif \ No newline at end of file diff --git a/shared/std/allocator.cpp b/shared/std/allocator.cpp index 47dbda1e..346ae0c6 100644 --- a/shared/std/allocator.cpp +++ b/shared/std/allocator.cpp @@ -14,11 +14,19 @@ void* operator new[](size_t size) { return (void*)malloc(size); } -//TODO: We'll need to implement an unsized version of these, and keep track of size ourselves +//TODO: properly implement these +void operator delete(void* ptr) noexcept { + free(ptr, 0); +} + +void operator delete[](void* ptr) noexcept { + free(ptr, 0); +} void operator delete(void* ptr, size_t size) noexcept { - free(ptr,size); + free(ptr, size); } + void operator delete[](void* ptr, size_t size) noexcept { - free(ptr,size); + free(ptr, size); } \ No newline at end of file diff --git a/shared/std/allocator.hpp b/shared/std/allocator.hpp index b3d93fd7..6ec46ac1 100644 --- a/shared/std/allocator.hpp +++ b/shared/std/allocator.hpp @@ -1,9 +1,12 @@ -#pragma once - +#pragma once #include "types.h" void* operator new(size_t size); void* operator new(size_t size, void* ptr) noexcept; void* operator new[](size_t size); + +void operator delete(void* ptr) noexcept; +void operator delete[](void* ptr) noexcept; + void operator delete(void* ptr, size_t size) noexcept; -void operator delete[](void* ptr, size_t size) noexcept; \ No newline at end of file +void operator delete[](void* ptr, size_t size) noexcept; diff --git a/shared/std/memory.c b/shared/std/memory.c index ada8ed49..5405c800 100644 --- a/shared/std/memory.c +++ b/shared/std/memory.c @@ -9,7 +9,7 @@ int memcmp(const void *s1, const void *s2, unsigned long count) { return 0; } -void* memset(void* dest, uint32_t val, size_t count) { +void* memset32(void* dest, uint32_t val, size_t count) { uint8_t *d8 = (uint8_t *)dest; uint64_t pattern = ((uint64_t)val << 32) | val; @@ -63,6 +63,51 @@ void* memset(void* dest, uint32_t val, size_t count) { return dest; } +void* memset(void* dest, uint8_t byte, size_t count) { + uint8_t *d8 = (uint8_t *)dest; + + while (((uintptr_t)d8 & 7) && count > 0) { + *d8++ = byte; + count--; + } + + if (count >= 8) { + uint64_t pattern = 0; + for (int i = 0; i < 8; i++) { + pattern <<= 8; + pattern |= byte; + } + size_t blocks = count / 8; + for (size_t i = 0; i < blocks; i++) { + *((uint64_t *)d8) = pattern; + d8 += 8; + } + count %= 8; + } + + if (count >= 4) { + uint32_t pattern32 = byte; + pattern32 |= pattern32 << 8; + pattern32 |= pattern32 << 16; + *((uint32_t *)d8) = pattern32; + d8 += 4; + count -= 4; + } + + if (count >= 2) { + uint16_t pattern16 = (uint16_t)byte | ((uint16_t)byte << 8); + *((uint16_t *)d8) = pattern16; + d8 += 2; + count -= 2; + } + + if (count == 1) { + *d8 = byte; + } + + return dest; +} + void* memcpy(void *dest, const void *src, uint64_t count) { uint8_t *d8 = (uint8_t *)dest; const uint8_t *s8 = (const uint8_t *)src; diff --git a/shared/std/memory.h b/shared/std/memory.h index f8bb15b7..012859a5 100644 --- a/shared/std/memory.h +++ b/shared/std/memory.h @@ -5,7 +5,8 @@ extern "C" { #include "types.h" int memcmp(const void *s1, const void *s2, unsigned long count); -void* memset(void* dest, uint32_t val, size_t count); +void* memset(void* dest, uint8_t byte, size_t count); +void* memset32(void* dest, uint32_t val, size_t count); void* memcpy(void *dest, const void *src, uint64_t count); #ifdef __cplusplus diff --git a/shared/std/string.c b/shared/std/string.c index 4d6e29b3..15560fc5 100644 --- a/shared/std/string.c +++ b/shared/std/string.c @@ -199,6 +199,20 @@ size_t string_format_va_buf(const char *fmt, char *buf, va_list args){ for (int j = temp_len - 1; j >= 0 && len < STRING_MAX_LEN - 1; j--){ buf[len++] = temp[j]; } + + } else if (fmt[i] == 'u') { + uint64_t val = va_arg(args, unsigned int); + char temp[21]; + uint32_t temp_len = 0; + + do { + temp[temp_len++] = '0' + (val % 10); + val /= 10; + } while (val && temp_len < 20); + + for (int j = temp_len - 1; j >= 0 && len < STRING_MAX_LEN - 1; j--) { + buf[len++] = temp[j]; + } } else if (fmt[i] == 'f' || fmt[i] == 'd') { double val = va_arg(args, double); if (val < 0){ @@ -446,4 +460,12 @@ const char* seek_to(const char *string, char character){ string++; string++; return string; +} + +size_t strncpy(char* dst, size_t cap, const char* src){ + size_t i=0; + if (!dst || !src || cap==0) return 0; + while (ifb, color, ctx->stride * ctx->height); + memset32(ctx->fb, color, ctx->stride * ctx->height); ctx->full_redraw = true; } @@ -73,10 +73,10 @@ void fb_draw_pixel(draw_ctx *ctx, uint32_t x, uint32_t y, color color){ void fb_fill_rect(draw_ctx *ctx, uint32_t x, uint32_t y, uint32_t width, uint32_t height, color color){ if (x + width >= ctx->width || y + height >= ctx->height) return; if (width == ctx->width && x == 0){ - memset(ctx->fb + (y * (ctx->width)) , color, ctx->stride * height); + memset32(ctx->fb + (y * (ctx->width)) , color, ctx->stride * height); } else { for (uint32_t dy = 0; dy < height; dy++) - memset(ctx->fb + ((y+dy) * (ctx->width)) + x, color, width*4); + memset32(ctx->fb + ((y+dy) * (ctx->width)) + x, color, width*4); } mark_dirty(ctx, x,y,width,height); }