From 83251052637665bb37c6636858242de0649ab5e5 Mon Sep 17 00:00:00 2001 From: CodeAnarchist Date: Thu, 9 Apr 2026 17:16:12 +0200 Subject: [PATCH 01/39] [gpu] fb --- kernel/fw/fw_cfg.c | 7 ++++--- kernel/memory/addr.c | 5 ++++- modules/graph/virt/ramfb.cpp | 30 ++++++++++++++++++++++----- modules/graph/virt/virtio_gpu_pci.cpp | 1 + 4 files changed, 34 insertions(+), 9 deletions(-) diff --git a/kernel/fw/fw_cfg.c b/kernel/fw/fw_cfg.c index a0c9e9c6..d5b71b7c 100644 --- a/kernel/fw/fw_cfg.c +++ b/kernel/fw/fw_cfg.c @@ -2,6 +2,7 @@ #include "console/kio.h" #include "std/memory_access.h" #include "memory/mmu.h" +#include "memory/addr.h" #include "async.h" #include "sysregs.h" #include "std/string.h" @@ -37,12 +38,12 @@ bool fw_cfg_check(){ void fw_cfg_dma_operation(void* dest, uint32_t size, uint32_t ctrl) { struct fw_cfg_dma_access access = { - .address = __builtin_bswap64(VIRT_TO_PHYS((uint64_t)dest)), + .address = __builtin_bswap64(pt_va_to_pa(dest)), .length = __builtin_bswap32(size), .control = __builtin_bswap32(ctrl), }; - write64(PHYS_TO_VIRT(FW_CFG_DMA), __builtin_bswap64(VIRT_TO_PHYS((uint64_t)&access))); + write64(PHYS_TO_VIRT(FW_CFG_DMA), __builtin_bswap64(pt_va_to_pa(&access))); __asm__("isb"); @@ -72,7 +73,7 @@ bool fw_find_file(const char* search, struct fw_cfg_file *file) { return false; uint32_t count; - fw_cfg_dma_read(VIRT_TO_PHYS_P(&count), sizeof(count), FW_LIST_DIRECTORY); + fw_cfg_dma_read(&count, sizeof(count), FW_LIST_DIRECTORY); count = __builtin_bswap32(count); diff --git a/kernel/memory/addr.c b/kernel/memory/addr.c index ae0716c1..d0d469bf 100644 --- a/kernel/memory/addr.c +++ b/kernel/memory/addr.c @@ -1,7 +1,9 @@ #include "addr.h" +#include "va_layout.h" bool kva_is_dmap(kaddr_t va){ - return ((uint64_t)va & HIGH_VA) == HIGH_VA; + uint64_t v = (uint64_t)va; + return v >= HIGH_VA && v < KERNEL_IMAGE_VA_BASE; } kaddr_t dmap_pa_to_kva(paddr_t pa){ @@ -26,5 +28,6 @@ paddr_t pt_va_to_pa(const void* va){ uint64_t sctlr = 0; asm volatile("mrs %0, sctlr_el1" : "=r"(sctlr)); if ((sctlr & 1) == 0) return (paddr_t)v; + if (v >= KERNEL_IMAGE_VA_BASE) return kimg_va_to_pa((kaddr_t)v); return dmap_kva_to_pa((kaddr_t)v); } diff --git a/modules/graph/virt/ramfb.cpp b/modules/graph/virt/ramfb.cpp index c59bb86e..218fb54a 100644 --- a/modules/graph/virt/ramfb.cpp +++ b/modules/graph/virt/ramfb.cpp @@ -7,6 +7,15 @@ #include "theme/theme.h" #include "memory/page_allocator.h" #include "sysregs.h" +#include "memory/addr.h" + +bool verbose = true; +#define kprintfv(fmt, ...) \ + ({ \ + if (verbose){\ + kprintf(fmt, ##__VA_ARGS__); \ + }\ + }) typedef struct { uint64_t addr; @@ -33,26 +42,34 @@ RamFBGPUDriver* RamFBGPUDriver::try_init(gpu_size preferred_screen_size){ bool RamFBGPUDriver::init(gpu_size preferred_screen_size){ file = {}; screen_size = preferred_screen_size; + kprintfv("[RAMFB] init requested %ix%i", preferred_screen_size.width, preferred_screen_size.height); if (!screen_size.width || !screen_size.height) return false; stride = bpp * screen_size.width; framebuffer_size = (size_t)(stride * screen_size.height); - fw_find_file("etc/ramfb", &file); - - if (file.selector == 0x0){ + kprintfv("[RAMFB] probing cfg desc"); + if (!fw_find_file("etc/ramfb", &file) || file.selector == 0x0){ kprintf("Ramfb not found"); return false; } + + kprintfv("[RAMFB] descriptor found selector=%x size=%x name=%s", file.selector, file.size, file.name); mem_page = palloc(0x1000, MEM_PRIV_KERNEL, MEM_RW | MEM_DEV, false); uint8_t* fb_block = (uint8_t*)palloc(framebuffer_size*2, MEM_PRIV_SHARED, MEM_RW, true); - if (!fb_block) return false; + if (!fb_block) { + kprintfv("[RAMFB] failed to allocate fb"); + return false; + } framebuffer = (uint32_t*)fb_block; back_framebuffer = (uint32_t*)(fb_block + framebuffer_size); + kprintfv("[RAMFB] framebuffer va=%x pa=%x", (uintptr_t)framebuffer, (uintptr_t)pt_va_to_pa(framebuffer)); + kprintfv("[RAMFB] backbuffer va=%x pa=%x", (uintptr_t)back_framebuffer, (uintptr_t)pt_va_to_pa(back_framebuffer)); + ctx = { .dirty_rects = {}, .fb = (uint32_t*)back_framebuffer, @@ -71,8 +88,9 @@ bool RamFBGPUDriver::init(gpu_size preferred_screen_size){ } void RamFBGPUDriver::update_gpu_fb(){ + paddr_t fb_pa = pt_va_to_pa(framebuffer); ramfb_structure fb = { - .addr = __builtin_bswap64(VIRT_TO_PHYS((uintptr_t)framebuffer)), + .addr = __builtin_bswap64((uint64_t)fb_pa), .fourcc = __builtin_bswap32(RGB_FORMAT_ARGB8888), .flags = __builtin_bswap32(0), .width = __builtin_bswap32(screen_size.width), @@ -80,7 +98,9 @@ void RamFBGPUDriver::update_gpu_fb(){ .stride = __builtin_bswap32(stride), }; + kprintfv("[RAMFB] writing descriptor selector=%x fb_pa=%x %ix%i stride=%x", file.selector, (uintptr_t)fb_pa, screen_size.width, screen_size.height, stride); fw_cfg_dma_write(&fb, sizeof(fb), file.selector); + kprintfv("[RAMFB] descriptor w completed"); } gpu_size RamFBGPUDriver::get_screen_size(){ diff --git a/modules/graph/virt/virtio_gpu_pci.cpp b/modules/graph/virt/virtio_gpu_pci.cpp index 302f85f0..a4c1af5e 100644 --- a/modules/graph/virt/virtio_gpu_pci.cpp +++ b/modules/graph/virt/virtio_gpu_pci.cpp @@ -6,6 +6,7 @@ #include "theme/theme.h" #include "memory/page_allocator.h" #include "sysregs.h" +#include "fw/fw_cfg.h" #define VIRTIO_GPU_CMD_GET_DISPLAY_INFO 0x0100 #define VIRTIO_GPU_CMD_RESOURCE_CREATE_2D 0x0101 From 5ce56909ff7d7dfc0c616c37642f1c1feaa73e36 Mon Sep 17 00:00:00 2001 From: CodeAnarchist Date: Sun, 12 Apr 2026 22:26:32 +0200 Subject: [PATCH 02/39] [net] alligment --- kernel/networking/internet_layer/icmp.c | 28 ++++---- kernel/networking/internet_layer/icmpv6.c | 67 ++++++++++--------- kernel/networking/internet_layer/igmp.c | 25 +++---- kernel/networking/internet_layer/ipv4.c | 33 ++++----- kernel/networking/internet_layer/ipv6.c | 60 +++++++++-------- kernel/networking/internet_layer/mld.c | 21 +++--- kernel/networking/link_layer/arp.c | 16 ++--- kernel/networking/link_layer/eth.c | 17 ++--- kernel/networking/link_layer/ndp.c | 50 +++++++------- .../networking/transport_layer/tcp/tcp_core.c | 12 ++-- .../networking/transport_layer/tcp/tcp_rx.c | 36 +++++----- .../transport_layer/tcp/tcp_utils.c | 5 +- kernel/networking/transport_layer/udp.c | 55 +++++++-------- kernel/process/loading/dwarf.c | 37 +++++----- kernel/theme/theme.c | 2 +- run_virt | 4 +- 16 files changed, 243 insertions(+), 225 deletions(-) diff --git a/kernel/networking/internet_layer/icmp.c b/kernel/networking/internet_layer/icmp.c index fc4a91fc..a93de04b 100644 --- a/kernel/networking/internet_layer/icmp.c +++ b/kernel/networking/internet_layer/icmp.c @@ -171,17 +171,17 @@ bool icmp_ping(uint32_t dst_ip, uint16_t id, uint16_t seq, uint32_t timeout_ms, void icmp_input(uintptr_t ptr, uint32_t len, uint32_t src_ip, uint32_t dst_ip) { if (len < 8) return; - icmp_packet* pkt = (icmp_packet*)ptr; - uint16_t recv_ck = pkt->checksum; - pkt->checksum = 0; - uint16_t calc = checksum16((uint16_t*)pkt, (len+1)/2); - pkt->checksum = recv_ck; - if (calc != recv_ck) return; - - uint8_t type = pkt->type; - uint8_t code = pkt->code; - uint16_t id = bswap16(pkt->id); - uint16_t sq = bswap16(pkt->seq); + const uint8_t* raw = (const uint8_t*)ptr; + uint32_t sum = 0; + for (uint32_t i = 0; i + 1 < len; i += 2) sum += (uint32_t)((raw[i] << 8) | raw[i + 1]); + if (len & 1u) sum += (uint32_t)(raw[len - 1] << 8); + while (sum >> 16) sum = (sum & 0xFFFFu) + (sum >> 16); + if ((uint16_t)sum != 0xFFFFu) return; + + uint8_t type = raw[0]; + uint8_t code = raw[1]; + uint16_t id = rd_be16(raw + 4); + uint16_t sq = rd_be16(raw + 6); uint32_t pay = len - 8; if (pay > 56) pay = 56; @@ -194,7 +194,7 @@ void icmp_input(uintptr_t ptr, uint32_t len, uint32_t src_ip, uint32_t dst_ip) { rp->id = bswap16(id); rp->seq = bswap16(sq); memset(rp->payload, 0, 56); - if (pay) memcpy(rp->payload, pkt->payload, pay); + if (pay) memcpy(rp->payload, raw + 8, pay); rp->checksum = 0; uint32_t rlen = 8 + pay; rp->checksum = checksum16((uint16_t*)rp, (rlen+1)/2); @@ -225,14 +225,14 @@ void icmp_input(uintptr_t ptr, uint32_t len, uint32_t src_ip, uint32_t dst_ip) { if (type == ICMP_TIME_EXCEEDED || type == ICMP_DEST_UNREACH || type == ICMP_PARAM_PROBLEM || type == ICMP_REDIRECT) { if (pay >= 28) { - const uint8_t *ip = pkt->payload; + const uint8_t *ip = raw + 8; uint8_t ihl = (uint8_t)(ip[0] & 0x0F); uint32_t iphdr = (uint32_t)ihl * 4; if (pay >= iphdr + 8) { uint8_t proto = ip[9]; if (proto == 1) { - const uint8_t *ic = pkt->payload + iphdr; + const uint8_t *ic = raw + 8 + iphdr; uint8_t t = ic[0]; if (t == ICMP_ECHO_REQUEST || t == ICMP_ECHO_REPLY) { uint16_t iid = (uint16_t)((ic[4] << 8) | ic[5]); diff --git a/kernel/networking/internet_layer/icmpv6.c b/kernel/networking/internet_layer/icmpv6.c index 4b1ca415..83f70f5a 100644 --- a/kernel/networking/internet_layer/icmpv6.c +++ b/kernel/networking/internet_layer/icmpv6.c @@ -74,14 +74,15 @@ bool icmpv6_send_on_l2(uint8_t ifindex, const uint8_t dst_ip[16], const uint8_t return false; } - ipv6_hdr_t *ip6 = (ipv6_hdr_t*)buf; - ip6->ver_tc_fl = bswap32((uint32_t)(6u << 28)); - ip6->payload_len = bswap16((uint16_t)icmp_len); - ip6->next_header = 58; - ip6->hop_limit = hop_limit ? hop_limit : 64; - memcpy(ip6->src, src_ip, 16); - memcpy(ip6->dst, dst_ip, 16); - + ipv6_hdr_t ip6; + ip6.ver_tc_fl = bswap32((uint32_t)(6u << 28)); + ip6.payload_len = bswap16((uint16_t)icmp_len); + ip6.next_header = 58; + ip6.hop_limit = hop_limit ? hop_limit : 64; + memcpy(ip6.src, src_ip, 16); + memcpy(ip6.dst, dst_ip, 16); + + memcpy(buf, &ip6, sizeof(ip6)); memcpy((void*)((uintptr_t)buf + sizeof(ipv6_hdr_t)), icmp, icmp_len); return eth_send_frame_on(ifindex, ETHERTYPE_IPV6, dst_mac, pkt); @@ -127,13 +128,14 @@ static bool icmpv6_send_echo_request(const uint8_t dst_ip[16], uint16_t id, uint return false; } - icmpv6_echo_t *e = (icmpv6_echo_t*)buf; - e->hdr.type = ICMPV6_ECHO_REQUEST; - e->hdr.code = 0; - e->hdr.checksum = 0; - e->id = bswap16(id); - e->seq = bswap16(seq); + icmpv6_echo_t e; + e.hdr.type = ICMPV6_ECHO_REQUEST; + e.hdr.code = 0; + e.hdr.checksum = 0; + e.id = bswap16(id); + e.seq = bswap16(seq); + memcpy(buf, &e, sizeof(e)); if (payload_len) memcpy((void*)((uintptr_t)buf + sizeof(icmpv6_echo_t)), payload, payload_len); ipv6_tx_plan_t plan; @@ -141,7 +143,8 @@ static bool icmpv6_send_echo_request(const uint8_t dst_ip[16], uint16_t id, uint netpkt_unref(pkt); return false; } - e->hdr.checksum = bswap16(checksum16_pipv6(plan.src_ip, dst_ip, 58, (const uint8_t*)buf, len)); + e.hdr.checksum = bswap16(checksum16_pipv6(plan.src_ip, dst_ip, 58, (const uint8_t*)buf, len)); + memcpy(buf, &e, sizeof(e)); ipv6_send_packet(dst_ip, 58, pkt, (const ipv6_tx_opts_t*)tx_opts_or_null, hop_limit ? hop_limit : 64, 0); return true; @@ -237,22 +240,24 @@ bool icmpv6_ping(const uint8_t dst_ip[16], uint16_t id, uint16_t seq, uint32_t t return false; } -static bool extract_echo_id_seq_from_error(const uint8_t *icmp, uint32_t icmp_len, uint16_t *out_id, uint16_t *out_seq) {//b +static bool extract_echo_id_seq_from_error(const uint8_t *icmp, uint32_t icmp_len, uint16_t *out_id, uint16_t *out_seq) { if (!icmp || icmp_len < 8u + (uint32_t)sizeof(ipv6_hdr_t) + (uint32_t)sizeof(icmpv6_echo_t)) return false; - const ipv6_hdr_t *inner = (const ipv6_hdr_t*)(icmp + 8); - uint32_t v = bswap32(inner->ver_tc_fl); + ipv6_hdr_t inner; + memcpy(&inner, icmp + 8, sizeof(inner)); + uint32_t v = bswap32(inner.ver_tc_fl); if ((v >>28) != 6) return false; - if (inner->next_header != 58) return false; + if (inner.next_header != 58) return false; - const uint8_t *inner_icmp = (const uint8_t*)(inner + 1); + const uint8_t *inner_icmp = icmp + 8u + (uint32_t)sizeof(ipv6_hdr_t); if ((uintptr_t)inner_icmp + sizeof(icmpv6_echo_t)>(uintptr_t)icmp + icmp_len) return false; - const icmpv6_echo_t *e = (const icmpv6_echo_t*)inner_icmp; - if (e->hdr.type != ICMPV6_ECHO_REQUEST) return false; + icmpv6_echo_t e; + memcpy(&e, inner_icmp, sizeof(e)); + if (e.hdr.type != ICMPV6_ECHO_REQUEST) return false; - if (out_id) *out_id = bswap16(e->id); - if (out_seq) *out_seq = bswap16(e->seq); + if (out_id) *out_id = bswap16(e.id); + if (out_seq) *out_seq = bswap16(e.seq); return true; } @@ -279,8 +284,9 @@ void icmpv6_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ if (h->type == ICMPV6_ECHO_REPLY) { if (icmp_len < sizeof(icmpv6_echo_t)) return; - const icmpv6_echo_t *e = (const icmpv6_echo_t*)icmp; - mark_received(bswap16(e->id), bswap16(e->seq), h->type, h->code, src_ip); + icmpv6_echo_t e; + memcpy(&e, icmp, sizeof(e)); + mark_received(bswap16(e.id), bswap16(e.seq), h->type, h->code, src_ip); return; } @@ -292,12 +298,13 @@ void icmpv6_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ if (h->type == ICMPV6_PACKET_TOO_BIG) { if (icmp_len >= 8u + (uint32_t)sizeof(ipv6_hdr_t)) { - uint32_t mtu = bswap32(*(const uint32_t *)(icmp + 4)); - const ipv6_hdr_t *inner = (const ipv6_hdr_t *)(icmp + 8); - uint32_t v = bswap32(inner->ver_tc_fl); + uint32_t mtu = rd_be32(icmp + 4); + ipv6_hdr_t inner; + memcpy(&inner, icmp + 8, sizeof(inner)); + uint32_t v = bswap32(inner.ver_tc_fl); if ((v >> 28) == 6 && mtu >= 1280u && mtu <= 65535u) - ipv6_pmtu_note(inner->dst, (uint16_t)mtu); + ipv6_pmtu_note(inner.dst, (uint16_t)mtu); uint16_t id = 0, seq = 0; if (extract_echo_id_seq_from_error(icmp, icmp_len, &id, &seq)) diff --git a/kernel/networking/internet_layer/igmp.c b/kernel/networking/internet_layer/igmp.c index 42833c45..5420d17e 100644 --- a/kernel/networking/internet_layer/igmp.c +++ b/kernel/networking/internet_layer/igmp.c @@ -45,8 +45,9 @@ static bool send_igmp(uint8_t ifindex, uint32_t dst, uint8_t type, uint32_t grou netpkt_t* pkt = netpkt_alloc(sizeof(igmp_hdr_t),headroom, 0); if (!pkt) return false; - igmp_hdr_t* h = (igmp_hdr_t*)netpkt_put(pkt, sizeof(igmp_hdr_t)); - if (!h) { + uint16_t igmp_words[sizeof(igmp_hdr_t)/sizeof(uint16_t)]; + igmp_hdr_t* h = (igmp_hdr_t*)igmp_words; + if (!netpkt_put(pkt, sizeof(igmp_hdr_t))) { netpkt_unref(pkt); return false; } @@ -55,7 +56,8 @@ static bool send_igmp(uint8_t ifindex, uint32_t dst, uint8_t type, uint32_t grou h->max_resp_time = 0; h->group = bswap32(group); h->checksum = 0; - h->checksum = checksum16((const uint16_t*)h, sizeof(igmp_hdr_t)/2); + h->checksum = checksum16(igmp_words, sizeof(igmp_hdr_t)/2); + memcpy((void*)netpkt_data(pkt), h, sizeof(*h)); ipv4_tx_opts_t tx; tx.scope = IP_TX_BOUND_L2; @@ -198,17 +200,16 @@ static void schedule_report(uint8_t ifindex, uint32_t group, uint32_t max_resp_d void igmp_input(uint8_t ifindex, uint32_t src, uint32_t dst, const void* l4, uint32_t l4_len) { if (!l4 || l4_len < sizeof(igmp_hdr_t)) return; - const igmp_hdr_t* h = (const igmp_hdr_t*)l4; - uint16_t saved = h->checksum; - igmp_hdr_t tmp; - memcpy(&tmp, h, sizeof(tmp)); - tmp.checksum = 0; - if (checksum16((const uint16_t*)&tmp, sizeof(tmp) / 2) != saved) return; + const uint8_t* p = (const uint8_t*)l4; + uint32_t sum = 0; + for (uint32_t i = 0; i + 1 < sizeof(igmp_hdr_t); i += 2) sum += (uint32_t)((p[i] << 8) | p[i + 1]); + while (sum >> 16) sum = (sum & 0xFFFFu) + (sum >> 16); + if ((uint16_t)sum != 0xFFFFu) return; - uint8_t type = h->type; - uint32_t group = bswap32(h->group); + uint8_t type = p[0]; + uint32_t group = rd_be32(p + 4); - uint32_t max_resp_ds = (uint32_t)h->max_resp_time; + uint32_t max_resp_ds = (uint32_t)p[1]; if (type != IGMP_TYPE_QUERY) return; diff --git a/kernel/networking/internet_layer/ipv4.c b/kernel/networking/internet_layer/ipv4.c index a8e7edf5..b13f9c89 100644 --- a/kernel/networking/internet_layer/ipv4.c +++ b/kernel/networking/internet_layer/ipv4.c @@ -310,7 +310,8 @@ void ipv4_send_packet(uint32_t dst_ip, uint8_t proto, netpkt_t* pkt, const ipv4_ netpkt_unref(pkt); return; } - ipv4_hdr_t* ip = (ipv4_hdr_t*)hdrp; + uint16_t ip_[sizeof(ipv4_hdr_t)/sizeof(uint16_t)]; + ipv4_hdr_t* ip = (ipv4_hdr_t*)ip_; ip->version_ihl = (uint8_t)((IP_VERSION_4 << 4) | IP_IHL_NOOPTS); ip->dscp_ecn = 0; ip->total_length = bswap16((uint16_t)total); @@ -323,7 +324,8 @@ void ipv4_send_packet(uint32_t dst_ip, uint8_t proto, netpkt_t* pkt, const ipv4_ ip->header_checksum = 0; ip->src_ip = bswap32(src_ip); ip->dst_ip = bswap32(dst_ip); - ip->header_checksum = checksum16((const uint16_t*)ip, hdr_len / 2); + ip->header_checksum = checksum16(ip_, hdr_len / 2); + memcpy(hdrp, ip, sizeof(*ip)); eth_send_frame_on(ifx, ETHERTYPE_IPV4, dst_mac, pkt); } @@ -334,24 +336,23 @@ void ipv4_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { uintptr_t ip_ptr = netpkt_data(pkt); if (ip_len < sizeof(ipv4_hdr_t)) return; - 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); + uint8_t first = *(const uint8_t*)ip_ptr; + uint8_t ver = (uint8_t)(first >> 4); + uint8_t ihl = (uint8_t)(first & 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; + uint16_t hdr_words[60 /sizeof(uint16_t)]; + uint8_t* hdr_copy = (uint8_t*)hdr_words; + memcpy(hdr_copy, (const void*)ip_ptr, hdr_len); + if (checksum16(hdr_words, hdr_len / 2) != 0) return; + ipv4_hdr_t ip; + memcpy(&ip, hdr_copy, sizeof(ip)); - uint16_t ip_totlen = bswap16(ip->total_length); + uint16_t ip_totlen = bswap16(ip.total_length); if (ip_totlen < hdr_len) return; if (ip_len < ip_totlen) return; (void)netpkt_trim(pkt, ip_totlen); @@ -360,8 +361,8 @@ void ipv4_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { 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); + uint32_t src = bswap32(ip.src_ip); + uint32_t dst = bswap32(ip.dst_ip); if (ifindex && src) { uint8_t mac_old[6]; @@ -373,7 +374,7 @@ void ipv4_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { } } - uint8_t proto = ip->protocol; + uint8_t proto = ip.protocol; l2_interface_t* l2 = l2_interface_find_by_index((uint8_t)ifindex); if (!l2) return; diff --git a/kernel/networking/internet_layer/ipv6.c b/kernel/networking/internet_layer/ipv6.c index 05b2cd6a..66ad0977 100644 --- a/kernel/networking/internet_layer/ipv6.c +++ b/kernel/networking/internet_layer/ipv6.c @@ -132,7 +132,7 @@ static void icmpv6_send_error(uint8_t ifindex, const uint8_t src_ip[16], const u h->code = code; h->checksum = 0; - *(uint32_t*)(buf + sizeof(icmpv6_hdr_t)) = bswap32(param32); + wr_be32(buf + sizeof(icmpv6_hdr_t), param32); memcpy(buf + base, invoking, copy); @@ -423,13 +423,14 @@ void ipv6_send_packet(const uint8_t dst[16], uint8_t next_header, netpkt_t* pkt, return; } - ipv6_hdr_t* ip6 = (ipv6_hdr_t*)hdrp; - ip6->ver_tc_fl = bswap32((uint32_t)(6u << 28)); - ip6->payload_len = bswap16((uint16_t)seg_len); - ip6->next_header = next_header; - ip6->hop_limit = hop_limit ? hop_limit : 64; - memcpy(ip6->src, src, 16); - memcpy(ip6->dst, dst, 16); + ipv6_hdr_t ip6; + ip6.ver_tc_fl = bswap32((uint32_t)(6u << 28)); + ip6.payload_len = bswap16((uint16_t)seg_len); + ip6.next_header = next_header; + ip6.hop_limit = hop_limit ? hop_limit : 64; + memcpy(ip6.src, src, 16); + memcpy(ip6.dst, dst, 16); + memcpy(hdrp, &ip6, sizeof(ip6)); eth_send_frame_on(ifx, ETHERTYPE_IPV6, dst_mac, pkt); return; @@ -479,23 +480,25 @@ void ipv6_send_packet(const uint8_t dst[16], uint8_t next_header, netpkt_t* pkt, break; } - ipv6_hdr_t* ip6 = (ipv6_hdr_t*)buf; - ip6->ver_tc_fl = bswap32((uint32_t)(6u << 28)); - ip6->payload_len = bswap16((uint16_t)payload_len); - ip6->next_header = 44; - ip6->hop_limit = hop_limit ? hop_limit : 64; - memcpy(ip6->src, src, 16); - memcpy(ip6->dst, dst, 16); + ipv6_hdr_t ip6; + ip6.ver_tc_fl = bswap32((uint32_t)(6u << 28)); + ip6.payload_len = bswap16((uint16_t)payload_len); + ip6.next_header = 44; + ip6.hop_limit = hop_limit ? hop_limit : 64; + memcpy(ip6.src, src, 16); + memcpy(ip6.dst, dst, 16); - ipv6_frag_hdr_t* fh = (ipv6_frag_hdr_t*)((uintptr_t)buf + hdr_len); - fh->next_header = next_header; - fh->reserved = 0; + memcpy(buf, &ip6, sizeof(ip6)); + ipv6_frag_hdr_t fh; + fh.next_header = next_header; + fh.reserved = 0; uint16_t off_flags = (uint16_t)(((off / 8u) & 0x1FFFu) << 3); if (more) off_flags |= 0x0001u; - fh->offset_flags = bswap16(off_flags); - fh->identification = bswap32(ident); + fh.offset_flags = bswap16(off_flags); + fh.identification = bswap32(ident); + memcpy((uint8_t*)buf + hdr_len, &fh, sizeof(fh)); - memcpy((uint8_t*)(fh + 1), data + off, chunk); + memcpy((uint8_t*)buf + hdr_len + sizeof(fh), data + off, chunk); eth_send_frame_on(ifx, ETHERTYPE_IPV6, dst_mac, fpkt); @@ -548,7 +551,9 @@ void ipv6_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { uintptr_t ip_ptr = netpkt_data(pkt); if (ip_len < sizeof(ipv6_hdr_t)) return; - ipv6_hdr_t* ip6 = (ipv6_hdr_t*)ip_ptr; + ipv6_hdr_t ip6_; + ipv6_hdr_t* ip6 = &ip6_; + memcpy(ip6, (const void*)ip_ptr, sizeof(*ip6)); uint32_t v = bswap32(ip6->ver_tc_fl); if ((v >> 28) != 6) return; uint32_t now = (uint32_t)get_time(); @@ -606,15 +611,16 @@ void ipv6_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { if (nh == 44) {//b if (l4_len < sizeof(ipv6_frag_hdr_t)) return; - const ipv6_frag_hdr_t* fh = (const ipv6_frag_hdr_t*)l4; - uint8_t inner_nh = fh->next_header; - uint16_t off_flags = bswap16(fh->offset_flags); - uint32_t ident = bswap32(fh->identification); + ipv6_frag_hdr_t fh; + memcpy(&fh, (const void*)l4, sizeof(fh)); + uint8_t inner_nh = fh.next_header; + uint16_t off_flags = bswap16(fh.offset_flags); + uint32_t ident = bswap32(fh.identification); uint32_t off = ((uint32_t)(off_flags >> 3) & 0x1FFFu) * 8u; uint8_t more = (off_flags & 0x0001u) ? 1u : 0u; - const uint8_t* frag = (const uint8_t*)(fh + 1); + const uint8_t* frag = (const uint8_t*)l4 + sizeof(ipv6_frag_hdr_t); uint32_t frag_len = l4_len-(uint32_t)sizeof(ipv6_frag_hdr_t); if (more && (frag_len & 7u)) { diff --git a/kernel/networking/internet_layer/mld.c b/kernel/networking/internet_layer/mld.c index 901a69b4..c17b8a52 100644 --- a/kernel/networking/internet_layer/mld.c +++ b/kernel/networking/internet_layer/mld.c @@ -133,22 +133,21 @@ static bool mld_send_report(uint8_t ifindex, const uint8_t group[16], uint8_t re netpkt_t* pkt = netpkt_alloc(total, headroom, 0); if(!pkt) return false; - ipv6_hdr_t* ip6 = (ipv6_hdr_t*)netpkt_put(pkt, (uint32_t)sizeof(ipv6_hdr_t)); - if(!ip6) { + void* ip6p = netpkt_put(pkt, (uint32_t)sizeof(ipv6_hdr_t)); + if(!ip6p) { netpkt_unref(pkt); return false; } - ((uint8_t*)&ip6->ver_tc_fl)[0] = 0x60; - ((uint8_t*)&ip6->ver_tc_fl)[1] = 0x00; - ((uint8_t*)&ip6->ver_tc_fl)[2] = 0x00; - ((uint8_t*)&ip6->ver_tc_fl)[3] = 0x00; + ipv6_hdr_t ip6; + ip6.ver_tc_fl = bswap32((uint32_t)(6 << 28)); - ip6->payload_len = bswap16((uint16_t)payload_len); - ip6->next_header = 0; - ip6->hop_limit = 1; - memcpy(ip6->src, src_ip, 16); - memcpy(ip6->dst, dst_ip, 16); + ip6.payload_len = bswap16((uint16_t)payload_len); + ip6.next_header = 0; + ip6.hop_limit = 1; + memcpy(ip6.src, src_ip, 16); + memcpy(ip6.dst, dst_ip, 16); + memcpy(ip6p, &ip6, sizeof(ip6)); uint8_t* hb = (uint8_t*)netpkt_put(pkt, (uint32_t)sizeof(hbh)); if(!hb) { diff --git a/kernel/networking/link_layer/arp.c b/kernel/networking/link_layer/arp.c index a1534b3c..0f980b96 100644 --- a/kernel/networking/link_layer/arp.c +++ b/kernel/networking/link_layer/arp.c @@ -211,14 +211,14 @@ void arp_input(uint16_t ifindex, netpkt_t* pkt) { uintptr_t frame_ptr = netpkt_data(pkt); if (frame_len < (uint32_t)sizeof(eth_hdr_t) + (uint32_t)sizeof(arp_hdr_t)) return; - 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); + const uint8_t* src_mac = (const uint8_t*)(frame_ptr+6); + arp_hdr_t hdr; + memcpy(&hdr, (const void*)(frame_ptr + sizeof(eth_hdr_t)), sizeof(hdr)); + 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); + arp_table_put_for_l2((uint8_t)ifindex, sender_ip, hdr.sender_mac, 180000, false); if (op == ARP_OPCODE_REQUEST) { char tbuf[16], abuf[16]; @@ -229,7 +229,7 @@ void arp_input(uint16_t ifindex, netpkt_t* pkt) { 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_send_reply_on((uint8_t)ifindex, &hdr, src_mac); } } } diff --git a/kernel/networking/link_layer/eth.c b/kernel/networking/link_layer/eth.c index e50400fc..e8ea98ba 100644 --- a/kernel/networking/link_layer/eth.c +++ b/kernel/networking/link_layer/eth.c @@ -7,28 +7,25 @@ #include "console/kio.h" #include "syscalls/syscalls.h" uintptr_t create_eth_packet(uintptr_t p, const uint8_t src_mac[6], const uint8_t dst_mac[6], uint16_t type) { - eth_hdr_t* eth =(eth_hdr_t*)p; + uint8_t* eth = (uint8_t*)p; - memcpy(eth->dst_mac, dst_mac, 6); - memcpy(eth->src_mac, src_mac, 6); - eth->ethertype = bswap16(type); + memcpy(eth, dst_mac, 6); + memcpy(eth+6, src_mac, 6); + wr_be16(eth+12, type); return p + (uint32_t)sizeof(eth_hdr_t); } uint16_t eth_parse_type(uintptr_t ptr){ - const eth_hdr_t* eth = (const eth_hdr_t*)ptr; - return bswap16(eth->ethertype); + return rd_be16((const void*)(ptr+12)); } const uint8_t* eth_src(uintptr_t ptr){ - const eth_hdr_t* eth = (const eth_hdr_t*)ptr; - return eth->src_mac; + return (const uint8_t*)(ptr+6); } const uint8_t* eth_dst(uintptr_t ptr){ - const eth_hdr_t* eth = (const eth_hdr_t*)ptr; - return eth->dst_mac; + return (const uint8_t*)ptr; } bool eth_send_frame_on(uint16_t ifindex, uint16_t ethertype, const uint8_t dst_mac[6], netpkt_t* pkt){ diff --git a/kernel/networking/link_layer/ndp.c b/kernel/networking/link_layer/ndp.c index dfa878d8..e54221e3 100644 --- a/kernel/networking/link_layer/ndp.c +++ b/kernel/networking/link_layer/ndp.c @@ -776,8 +776,9 @@ void ndp_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[ if (h->type == 135) { if (icmp_len < sizeof(icmpv6_ns_t)) return; - const icmpv6_ns_t* ns = (const icmpv6_ns_t*)icmp; - if (ipv6_is_multicast(ns->target)) return; + icmpv6_ns_t ns; + memcpy(&ns, icmp, sizeof(ns)); + if (ipv6_is_multicast(ns.target)) return; l2_interface_t* l2 = l2_interface_find_by_index((uint8_t)ifindex); if (!l2) return; @@ -789,7 +790,7 @@ void ndp_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[ if (!v6) continue; if (v6->cfg == IPV6_CFG_DISABLE) continue; - if (ipv6_cmp(v6->ip, ns->target) == 0) { + if (ipv6_cmp(v6->ip, ns.target) == 0) { self = v6; break; } @@ -819,7 +820,7 @@ void ndp_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[ if (v6->cfg == IPV6_CFG_DISABLE) continue; if (v6->dad_state != IPV6_DAD_OK) continue; - if (ipv6_cmp(v6->ip, ns->target) == 0) { + if (ipv6_cmp(v6->ip, ns.target) == 0) { ipv6_cpy(src_my, v6->ip); break; } @@ -830,15 +831,16 @@ void ndp_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[ const uint8_t* my_mac = network_get_mac((uint8_t)ifindex); if (!my_mac) return; - ndp_send_na_on((uint8_t)ifindex, src_ip, src_my, ns->target, src_mac, my_mac, 1); + ndp_send_na_on((uint8_t)ifindex, src_ip, src_my, ns.target, src_mac, my_mac, 1); return; } if (h->type == 136) { if (icmp_len < sizeof(icmpv6_na_t)) return; - const icmpv6_na_t* na = (const icmpv6_na_t*)icmp; - if (ipv6_is_multicast(na->target)) return; + icmpv6_na_t na; + memcpy(&na, icmp, sizeof(na)); + if (ipv6_is_multicast(na.target)) return; l2_interface_t* l2 = l2_interface_find_by_index((uint8_t)ifindex); @@ -846,7 +848,7 @@ void ndp_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[ for (int i = 0; i < MAX_IPV6_PER_INTERFACE; i++) { l3_ipv6_interface_t* v6 = l2->l3_v6[i]; if (!v6) continue; - if (ipv6_cmp(v6->ip, na->target) != 0) continue; + if (ipv6_cmp(v6->ip, na.target) != 0) continue; if (v6->dad_state == IPV6_DAD_IN_PROGRESS || v6->dad_requested) { v6->dad_state = IPV6_DAD_FAILED; @@ -860,7 +862,7 @@ void ndp_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[ if (ipv6_is_unspecified(src_ip)) return; - uint32_t f = bswap32(na->flags); + uint32_t f = bswap32(na.flags); uint8_t router = (uint8_t)((f >> 31) & 1u); uint8_t solicited = (uint8_t)((f >> 30) & 1u); uint8_t override = (uint8_t)((f >> 29) & 1u); @@ -868,7 +870,7 @@ void ndp_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[ ndp_table_impl_t* t = l2_ndp((uint8_t)ifindex); if (!t) return; - int idx = ndp_find_slot(t, na->target); + int idx = ndp_find_slot(t, na.target); if (idx < 0) idx = ndp_find_free(t); if (idx < 0) { @@ -900,7 +902,7 @@ void ndp_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[ memcpy(old_mac, e->mac, 6); if (e->ttl_ms == 0 && e->state == NDP_STATE_UNUSED) { - memcpy(e->ip, na->target, 16); + memcpy(e->ip, na.target, 16); memcpy(e->mac, src_mac, 6); e->ttl_ms = g_ndp_reachable_time_ms * 4; e->probes_sent = 0; @@ -965,11 +967,12 @@ void ndp_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[ if (h->type == 134) { if (icmp_len < sizeof(icmpv6_ra_t)) return; - const icmpv6_ra_t* ra = (const icmpv6_ra_t*)icmp; + icmpv6_ra_t ra; + memcpy(&ra, icmp, sizeof(ra)); - uint16_t router_lifetime = bswap16(ra->router_lifetime); - uint32_t reachable_time = bswap32(ra->reachable_time); - uint32_t retrans_timer = bswap32(ra->retrans_timer); + uint16_t router_lifetime = bswap16(ra.router_lifetime); + uint32_t reachable_time = bswap32(ra.reachable_time); + uint32_t retrans_timer = bswap32(ra.retrans_timer); uint32_t router_lifetime_ms = (uint32_t)router_lifetime * 1000u; @@ -979,7 +982,7 @@ void ndp_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[ if (reachable_time) g_ndp_reachable_time_ms = reachable_time; if (retrans_timer) g_ndp_retrans_timer_ms = retrans_timer; - const uint8_t* opt = (const uint8_t*)(ra + 1); + const uint8_t* opt = icmp + sizeof(ra); uint32_t opt_len = icmp_len - (uint32_t)sizeof(icmpv6_ra_t); uint8_t idx = (uint8_t)(ifindex - 1); @@ -998,17 +1001,18 @@ void ndp_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[ if (opt_size > opt_len) break; if (opt_type == 3&&opt_size >= (uint32_t)sizeof(ndp_opt_prefix_info_t)) { - const ndp_opt_prefix_info_t* pio = (const ndp_opt_prefix_info_t*)opt; + ndp_opt_prefix_info_t pio; + memcpy(&pio, opt, sizeof(pio)); - uint8_t pfx_len = pio->prefix_length; - uint8_t autonomous = (pio->flags & 0x40u) ? 1u : 0u; - uint32_t valid_lft = bswap32(pio->valid_lifetime); - uint32_t pref_lft = bswap32(pio->preferred_lifetime); + uint8_t pfx_len = pio.prefix_length; + uint8_t autonomous = (pio.flags & 0x40u) ? 1u : 0u; + uint32_t valid_lft = bswap32(pio.valid_lifetime); + uint32_t pref_lft = bswap32(pio.preferred_lifetime); uint8_t pfx[16]; - memcpy(pfx, pio->prefix, 16); + memcpy(pfx, pio.prefix, 16); - if (pfx_len != 0) ndp_on_ra((uint8_t)ifindex, src_ip, router_lifetime, pfx, pfx_len, valid_lft, pref_lft, autonomous, ra->flags); + if (pfx_len != 0) ndp_on_ra((uint8_t)ifindex, src_ip, router_lifetime, pfx, pfx_len, valid_lft, pref_lft, autonomous, ra.flags); } else if (opt_type == 5 && opt_size >= (uint32_t)sizeof(ndp_opt_mtu_t)) { uint32_t mtu32 = 0; memcpy(&mtu32, opt + 4, 4); diff --git a/kernel/networking/transport_layer/tcp/tcp_core.c b/kernel/networking/transport_layer/tcp/tcp_core.c index 3d6f9a3b..8927614c 100644 --- a/kernel/networking/transport_layer/tcp/tcp_core.c +++ b/kernel/networking/transport_layer/tcp/tcp_core.c @@ -176,14 +176,18 @@ bool tcp_send_segment(ip_version_t ver, const void *src_ip_addr, const void *dst if (payload_len && payload) memcpy(segment + sizeof(tcp_hdr_t) + opts_len, payload, payload_len); if (ver == IP_VER4){ - uint32_t s = *(const uint32_t *)src_ip_addr; - uint32_t d = *(const uint32_t *)dst_ip_addr; + uint32_t s = 0; + uint32_t d = 0; + memcpy(&s, src_ip_addr, sizeof(s)); + memcpy(&d, dst_ip_addr, sizeof(d)); - ((tcp_hdr_t *)segment)->checksum = tcp_checksum_ipv4(segment, tcp_len, s, d); + h.checksum = tcp_checksum_ipv4(segment, tcp_len, s, d); + memcpy(segment, &h, sizeof(h)); ipv4_send_packet(d, 6, pkt, (const ipv4_tx_opts_t *)txp, ttl, dontfrag); return true; } else if (ver == IP_VER6){ - ((tcp_hdr_t *)segment)->checksum = tcp_checksum_ipv6(segment, tcp_len, (const uint8_t *)src_ip_addr, (const uint8_t *)dst_ip_addr); + h.checksum = tcp_checksum_ipv6(segment, tcp_len, (const uint8_t *)src_ip_addr, (const uint8_t *)dst_ip_addr); + memcpy(segment, &h, sizeof(h)); ipv6_send_packet((const uint8_t *)dst_ip_addr, 6, pkt, (const ipv6_tx_opts_t *)txp, ttl, dontfrag); return true; } diff --git a/kernel/networking/transport_layer/tcp/tcp_rx.c b/kernel/networking/transport_layer/tcp/tcp_rx.c index 1d5deb66..fba40956 100644 --- a/kernel/networking/transport_layer/tcp/tcp_rx.c +++ b/kernel/networking/transport_layer/tcp/tcp_rx.c @@ -328,27 +328,27 @@ static void tcp_cc_on_dupack(tcp_flow_t *f) { 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; - tcp_hdr_t *hdr = (tcp_hdr_t *)ptr; + tcp_hdr_t hdr; + memcpy(&hdr, (const void*)ptr, sizeof(hdr)); - uint16_t recv_checksum = hdr->checksum; - hdr->checksum = 0; - - uint16_t calc; - - if (ipver == IP_VER4) calc = tcp_checksum_ipv4(hdr, (uint16_t)len, *(const uint32_t *)src_ip_addr, *(const uint32_t *)dst_ip_addr); - else calc = tcp_checksum_ipv6(hdr, (uint16_t)len, (const uint8_t *)src_ip_addr, (const uint8_t *)dst_ip_addr); - - hdr->checksum = recv_checksum; - if (recv_checksum != calc) return; + if (ipver == IP_VER4) { + uint32_t src_ip = 0; + uint32_t dst_ip = 0; + memcpy(&src_ip, src_ip_addr, sizeof(src_ip)); + memcpy(&dst_ip, dst_ip_addr, sizeof(dst_ip)); + if (tcp_checksum_ipv4((const void*)ptr, (uint16_t)len, src_ip, dst_ip) != 0) return; + } else { + if (tcp_checksum_ipv6((const void*)ptr, (uint16_t)len, (const uint8_t *)src_ip_addr, (const uint8_t *)dst_ip_addr) != 0) return; + } - 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 = bswap16(hdr->window); + 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 = bswap16(hdr.window); - uint8_t hdr_len = (uint8_t)((hdr->data_offset_reserved >> 4) * 4); + uint8_t hdr_len = (uint8_t)((hdr.data_offset_reserved >> 4) * 4); if (len < hdr_len) return; uint32_t data_len = len - hdr_len; diff --git a/kernel/networking/transport_layer/tcp/tcp_utils.c b/kernel/networking/transport_layer/tcp/tcp_utils.c index 8ce48329..56643c9a 100644 --- a/kernel/networking/transport_layer/tcp/tcp_utils.c +++ b/kernel/networking/transport_layer/tcp/tcp_utils.c @@ -1,4 +1,5 @@ #include "tcp_utils.h" +#include "std/memory.h" uint32_t tcp_calc_mss_for_l3(uint8_t l3_id, ip_version_t ver, const void *remote_ip){ uint32_t mtu = 1500; @@ -23,7 +24,9 @@ uint32_t tcp_calc_mss_for_l3(uint8_t l3_id, ip_version_t ver, const void *remote bool tcp_build_tx_opts_from_local_v4(const void *src_ip_addr, ipv4_tx_opts_t *out){ if (!out) return false; - l3_ipv4_interface_t *v4 = l3_ipv4_find_by_ip(*(const uint32_t *)src_ip_addr); + uint32_t src_ip = 0; + memcpy(&src_ip, src_ip_addr, sizeof(src_ip)); + l3_ipv4_interface_t *v4 = l3_ipv4_find_by_ip(src_ip); if (v4) { out->scope = IP_TX_BOUND_L3; out->index = v4->l3_id; diff --git a/kernel/networking/transport_layer/udp.c b/kernel/networking/transport_layer/udp.c index 36e025e4..9955a84c 100644 --- a/kernel/networking/transport_layer/udp.c +++ b/kernel/networking/transport_layer/udp.c @@ -15,25 +15,25 @@ static inline uint32_t v4_u32_from_arr(const uint8_t ip16[16]) { } 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 = (uint16_t)(sizeof(*udp) + payload.size); - udp->length = bswap16(full_len); - udp->checksum = 0; + udp_hdr_t udp; + udp.src_port = bswap16(src->port); + udp.dst_port = bswap16(dst->port); + 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, &udp, sizeof(udp)); + 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); + udp.checksum = bswap16(checksum16_pipv4(s, d, 0x11, (const uint8_t *)buf, full_len)); } else if (src->ver == IP_VER6) { - uint16_t csum = checksum16_pipv6(src->ip, dst->ip, 17, (const uint8_t *)udp, full_len); - udp->checksum = bswap16(csum); + udp.checksum = bswap16(checksum16_pipv6(src->ip, dst->ip, 17, (const uint8_t *)buf, full_len)); } + memcpy((void*)buf, &udp, sizeof(udp)); return full_len; } @@ -66,8 +66,9 @@ sizedptr udp_strip_header(uintptr_t ptr, uint32_t len) { if (len < sizeof(udp_hdr_t)) { return (sizedptr){ 0, 0 }; } - udp_hdr_t *hdr = (udp_hdr_t *)ptr; - uint16_t total = bswap16(hdr->length); + udp_hdr_t hdr; + memcpy(&hdr, (const void*)ptr, sizeof(hdr)); + uint16_t total = bswap16(hdr.length); if (total < sizeof(udp_hdr_t) || total > len) { return (sizedptr){ 0, 0 }; } @@ -81,29 +82,23 @@ void udp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a sizedptr pl = udp_strip_header(ptr, len); if (!pl.ptr) return; - udp_hdr_t *hdr = (udp_hdr_t *)ptr; + udp_hdr_t hdr; + memcpy(&hdr, (const void*)ptr, sizeof(hdr)); - if (hdr->checksum) { + if (hdr.checksum) { 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; + uint32_t src_ip = 0; + uint32_t dst_ip = 0; + memcpy(&src_ip, src_ip_addr, sizeof(src_ip)); + memcpy(&dst_ip, dst_ip_addr, sizeof(dst_ip)); + if (checksum16_pipv4(src_ip, dst_ip, 0x11, (const uint8_t*)ptr, (uint16_t)(pl.size + sizeof(hdr))) != 0) return; } else if (ipver == IP_VER6) { - uint16_t recv = hdr->checksum; - hdr->checksum = 0; - uint16_t calc = checksum16_pipv6( (const uint8_t*)src_ip_addr, (const uint8_t*)dst_ip_addr, 0x11, (const uint8_t*)hdr, (uint32_t)(pl.size + sizeof(*hdr))); - hdr->checksum = recv; - if (calc != bswap16(recv)) return; + if (checksum16_pipv6((const uint8_t*)src_ip_addr, (const uint8_t*)dst_ip_addr, 0x11, (const uint8_t*)ptr, (uint32_t)(pl.size + sizeof(hdr))) != 0) return; } } - uint16_t dst_port = bswap16(hdr->dst_port); - uint16_t src_port = bswap16(hdr->src_port); + uint16_t dst_port = bswap16(hdr.dst_port); + uint16_t src_port = bswap16(hdr.src_port); l3_ipv4_interface_t *v4 = NULL; l3_ipv6_interface_t *v6 = NULL; diff --git a/kernel/process/loading/dwarf.c b/kernel/process/loading/dwarf.c index 7d3186a3..121eac21 100644 --- a/kernel/process/loading/dwarf.c +++ b/kernel/process/loading/dwarf.c @@ -274,14 +274,15 @@ debug_line_info dwarf_decode_lines(uintptr_t ptr, size_t size, uintptr_t debug_l .discriminator = 0 }; if (ptr + sizeof(dwarf_debug_line_header) > end_section) return (debug_line_info){}; - dwarf_debug_line_header *hdr = (dwarf_debug_line_header*)ptr; - uintptr_t unit_end = (uintptr_t)&hdr->unit_length + sizeof(hdr->unit_length) + hdr->unit_length; + dwarf_debug_line_header hdr = {}; + memcpy(&hdr, (const void*)ptr, sizeof(hdr)); + uintptr_t unit_end = ptr + sizeof(hdr.unit_length) + hdr.unit_length; if (unit_end <= ptr || unit_end > end_section) return (debug_line_info){}; - if (!hdr->line_range || !hdr->opcode_base) return (debug_line_info){}; + if (!hdr.line_range || !hdr.opcode_base) return (debug_line_info){}; - state.is_stmt = hdr->default_is_stmt; + state.is_stmt = hdr.default_is_stmt; - if (hdr->version != 5) { + if (hdr.version != 5) { kprintf("Only DWARF version 5 is supported"); return (debug_line_info){}; } @@ -291,7 +292,7 @@ debug_line_info dwarf_decode_lines(uintptr_t ptr, size_t size, uintptr_t debug_l // kprintf("Header is %x bytes",sizeof(dwarf_debug_line_header)); memset(files, 0, sizeof(files)); - uintptr_t file_ptr = dwarf_decode_entries(ptr + sizeof(dwarf_debug_line_header) + hdr->opcode_base - 1, debug_line_str_base, str_size, type_codes, form_codes, 0); + uintptr_t file_ptr = dwarf_decode_entries(ptr + sizeof(dwarf_debug_line_header) + hdr.opcode_base - 1, debug_line_str_base, str_size, type_codes, form_codes, 0); if (file_ptr != ptr){ // kprintf("Now files %x",file_ptr); dwarf_decode_entries(file_ptr, debug_line_str_base, str_size, type_codes, form_codes, files); @@ -301,7 +302,7 @@ debug_line_info dwarf_decode_lines(uintptr_t ptr, size_t size, uintptr_t debug_l // if (files[i]) kprintf("File [%i] = %s",i,files[i]); // } - ptr = (uintptr_t)&hdr->header_length + sizeof(hdr->header_length) + hdr->header_length; + ptr = ptr + sizeof(hdr.unit_length) + sizeof(hdr.version) + sizeof(hdr.address_size) + sizeof(hdr.segment_selector) + sizeof(hdr.header_length) + hdr.header_length; uint8_t *end = (uint8_t*)unit_end; uint8_t *p = (uint8_t*)ptr; @@ -331,7 +332,7 @@ debug_line_info dwarf_decode_lines(uintptr_t ptr, size_t size, uintptr_t debug_l case DW_LNE_set_address: // kprintf("Address changed by DW_LNE_set_address from %x",state.address); - state.address = decode_address(&p, hdr->address_size); + state.address = decode_address(&p, hdr.address_size); // kprintf(" to %x",state.address); break; @@ -343,7 +344,7 @@ debug_line_info dwarf_decode_lines(uintptr_t ptr, size_t size, uintptr_t debug_l kprintf("[DWARF ERROR] UNKNOWN EXTENDED OPCODE %i with length %i at %x",ex_opcode, len, p); return (debug_line_info){}; } - } else if (opcode < hdr->opcode_base) { //Standard + } else if (opcode < hdr.opcode_base) { //Standard switch (opcode) { case DW_LNS_copy: emit_row = true; @@ -352,7 +353,7 @@ debug_line_info dwarf_decode_lines(uintptr_t ptr, size_t size, uintptr_t debug_l case DW_LNS_advance_pc: { uint64_t operand = decode_uleb128(&p); // kprintf("Advancing DW_LNS_advance_pc by %x",operand); - state.address += operand * hdr->minimum_instruction_length; + state.address += operand * hdr.minimum_instruction_length; break; } @@ -380,15 +381,15 @@ debug_line_info dwarf_decode_lines(uintptr_t ptr, size_t size, uintptr_t debug_l break; case DW_LNS_const_add_pc: { - uint8_t adjusted = 255 - hdr->opcode_base; - uint64_t addr_inc = (adjusted / hdr->line_range) * hdr->minimum_instruction_length; + uint8_t adjusted = 255 - hdr.opcode_base; + uint64_t addr_inc = (adjusted / hdr.line_range) * hdr.minimum_instruction_length; state.address += addr_inc; // kprintf("Advancing DW_LNS_const_add_pc by %x. New %x",addr_inc,state.address); break; } case DW_LNS_fixed_advance_pc: { - uint16_t advance = *(uint16_t *)p; + uint16_t advance = read_unaligned16((const uint16_t*)p); p += 2; state.address += advance; // kprintf("Advancing DW_LNS_fixed_advance_pc by %x. New %x",advance,state.address); @@ -413,12 +414,12 @@ debug_line_info dwarf_decode_lines(uintptr_t ptr, size_t size, uintptr_t debug_l } } else { //Special - uint8_t adj = opcode - hdr->opcode_base;//146 - 13 = 133 + uint8_t adj = opcode - hdr.opcode_base;//146 - 13 = 133 // kprintf("Special opcode %i - %i = %i",opcode) - uint8_t op_adv = adj/hdr->line_range;//47/14 = 9.xxx - state.line += hdr->line_base + (adj % hdr->line_range); + uint8_t op_adv = adj/hdr.line_range;//47/14 = 9.xxx + state.line += hdr.line_base + (adj % hdr.line_range); // kprintf("Advancing by special by %x",op_adv); - state.address += op_adv * hdr->minimum_instruction_length; + state.address += op_adv * hdr.minimum_instruction_length; state.basic_block = false; state.prologue_end = false; state.epilogue_begin = false; @@ -437,7 +438,7 @@ debug_line_info dwarf_decode_lines(uintptr_t ptr, size_t size, uintptr_t debug_l .file = 1, .line = 1, .column = 0, - .is_stmt = hdr->default_is_stmt, + .is_stmt = hdr.default_is_stmt, .basic_block = false, .end_sequence = false, .prologue_end = false, diff --git a/kernel/theme/theme.c b/kernel/theme/theme.c index 601b85f6..066a524c 100644 --- a/kernel/theme/theme.c +++ b/kernel/theme/theme.c @@ -37,7 +37,7 @@ system_config_t system_config = { .default_pwd = DEFAULT_PWD, .system_name = SYSTEM_NAME, .app_directory = "boot", - .use_net = false, + .use_net = true, }; gpu_point parse_gpu_point(char *value, size_t value_len){ diff --git a/run_virt b/run_virt index 95cd421e..56250871 100755 --- a/run_virt +++ b/run_virt @@ -31,8 +31,8 @@ AUDIO_BACKEND="sdl" VIRTIO_GPU_VARS=",xres=1920,yres=1080" GL="on" -USE_FB=true -USE_NET=false +USE_FB=false +USE_NET=true if [ "$USE_FB" == true ]; then SELECTED_GPU="ramfb" From c416e0ccdd2d8aca0e15a412d80f3a4064c037bf Mon Sep 17 00:00:00 2001 From: CodeAnarchist Date: Sat, 18 Apr 2026 00:20:21 +0200 Subject: [PATCH 03/39] [net]netpkt across l2-l4 --- kernel/networking/internet_layer/icmp.c | 52 +++-- kernel/networking/internet_layer/icmp.h | 3 +- kernel/networking/internet_layer/icmpv6.c | 54 +++-- kernel/networking/internet_layer/icmpv6.h | 3 +- kernel/networking/internet_layer/igmp.c | 45 ++++- kernel/networking/internet_layer/igmp.h | 3 +- kernel/networking/internet_layer/ipv4.c | 121 +++++++++--- kernel/networking/internet_layer/ipv4_route.c | 5 +- kernel/networking/internet_layer/ipv6.c | 184 ++++++++++++------ kernel/networking/internet_layer/ipv6_route.c | 5 +- kernel/networking/internet_layer/mld.c | 38 ++-- kernel/networking/internet_layer/mld.h | 2 +- kernel/networking/link_layer/arp.c | 16 +- kernel/networking/link_layer/arp.h | 2 +- kernel/networking/link_layer/eth.c | 45 +++-- kernel/networking/link_layer/eth.h | 6 +- kernel/networking/link_layer/ndp.c | 54 ++--- kernel/networking/link_layer/ndp.h | 3 +- kernel/networking/netpkt.c | 21 ++ kernel/networking/netpkt.h | 2 + kernel/networking/network_dispatch.cpp | 11 +- kernel/networking/port_manager.c | 22 +++ kernel/networking/port_manager.h | 15 +- .../networking/transport_layer/socket_tcp.hpp | 29 ++- .../networking/transport_layer/socket_udp.hpp | 46 ++--- kernel/networking/transport_layer/tcp.h | 3 +- .../networking/transport_layer/tcp/tcp_core.c | 10 +- .../networking/transport_layer/tcp/tcp_rx.c | 149 ++++++++++---- .../networking/transport_layer/tcp/tcp_tx.c | 2 +- kernel/networking/transport_layer/udp.c | 90 +++++---- kernel/networking/transport_layer/udp.h | 4 +- 31 files changed, 709 insertions(+), 336 deletions(-) diff --git a/kernel/networking/internet_layer/icmp.c b/kernel/networking/internet_layer/icmp.c index a93de04b..dd56198b 100644 --- a/kernel/networking/internet_layer/icmp.c +++ b/kernel/networking/internet_layer/icmp.c @@ -56,7 +56,7 @@ static void mark_received(uint16_t id, uint16_t seq, uint8_t type, uint8_t code, static uintptr_t build_echo(uint16_t id, uint16_t seq, const uint8_t* payload, uint32_t pay_len, uint32_t* out_total_len) { uint32_t len = 8 + (pay_len > 56 ? 56 : pay_len); *out_total_len = len; - uintptr_t buf = (uintptr_t)malloc(8 + 56); + uintptr_t buf = (uintptr_t)zalloc(8 + 56); if (!buf) return 0; icmp_packet *pkt = (icmp_packet*)buf; @@ -102,19 +102,19 @@ bool icmp_ping(uint32_t dst_ip, uint16_t id, uint16_t seq, uint32_t timeout_ms, uint32_t headroom = (uint32_t)sizeof(eth_hdr_t) + (uint32_t)sizeof(ipv4_hdr_t); netpkt_t* pkt = netpkt_alloc(tot_len, headroom, 0); if (!pkt) { - free_sized((void*)buf, 8 + 56); + release((void*)buf); g_pending[slot].in_use = false; return false; } void* p = netpkt_put(pkt, tot_len); if (!p) { netpkt_unref(pkt); - free_sized((void*)buf, 8 + 56); + release((void*)buf); g_pending[slot].in_use = false; return false; } memcpy(p, (const void*)buf, tot_len); - free_sized((void*)buf, 8 + 56); + release((void*)buf); ipv4_send_packet(dst_ip, 1, pkt, (const ipv4_tx_opts_t*)tx_opts_or_null, (uint8_t)ttl, 0); uint32_t start = (uint32_t)get_time(); @@ -168,26 +168,43 @@ bool icmp_ping(uint32_t dst_ip, uint16_t id, uint16_t seq, uint32_t timeout_ms, return false; } -void icmp_input(uintptr_t ptr, uint32_t len, uint32_t src_ip, uint32_t dst_ip) { - if (len < 8) return; +void icmp_input(netpkt_t* pkt, uint32_t src_ip, uint32_t dst_ip) { + if (!pkt) return; - const uint8_t* raw = (const uint8_t*)ptr; + uint32_t len = netpkt_len(pkt); + if (len < 8) { + netpkt_unref(pkt); + return; + } + + const uint8_t* raw = (const uint8_t*)netpkt_data(pkt); + uint8_t hdr[8]; + if (!netpkt_copyout(pkt, 0, hdr, sizeof(hdr))) { + netpkt_unref(pkt); + return; + } uint32_t sum = 0; for (uint32_t i = 0; i + 1 < len; i += 2) sum += (uint32_t)((raw[i] << 8) | raw[i + 1]); if (len & 1u) sum += (uint32_t)(raw[len - 1] << 8); while (sum >> 16) sum = (sum & 0xFFFFu) + (sum >> 16); - if ((uint16_t)sum != 0xFFFFu) return; + if ((uint16_t)sum != 0xFFFFu) { + netpkt_unref(pkt); + return; + } - uint8_t type = raw[0]; - uint8_t code = raw[1]; - uint16_t id = rd_be16(raw + 4); - uint16_t sq = rd_be16(raw + 6); + uint8_t type = hdr[0]; + uint8_t code = hdr[1]; + uint16_t id = rd_be16(hdr + 4); + uint16_t sq = rd_be16(hdr + 6); uint32_t pay = len - 8; if (pay > 56) pay = 56; if (type == ICMP_ECHO_REQUEST) { - uintptr_t buf = (uintptr_t)malloc(8 + 56); - if (!buf) return; + uintptr_t buf = (uintptr_t)zalloc(8 + 56); + if (!buf) { + netpkt_unref(pkt); + return; + } icmp_packet *rp = (icmp_packet*)buf; rp->type = ICMP_ECHO_REPLY; rp->code = 0; @@ -214,12 +231,14 @@ void icmp_input(uintptr_t ptr, uint32_t len, uint32_t src_ip, uint32_t dst_ip) { } } } - free_sized((void*)buf, 8 + 56); + release((void*)buf); + netpkt_unref(pkt); return; } if (type == ICMP_ECHO_REPLY) { mark_received(id, sq, type, code, src_ip); + netpkt_unref(pkt); return; } @@ -242,6 +261,9 @@ void icmp_input(uintptr_t ptr, uint32_t len, uint32_t src_ip, uint32_t dst_ip) { } } } + netpkt_unref(pkt); return; } + + netpkt_unref(pkt); } diff --git a/kernel/networking/internet_layer/icmp.h b/kernel/networking/internet_layer/icmp.h index 08ff1223..c0cf4b64 100644 --- a/kernel/networking/internet_layer/icmp.h +++ b/kernel/networking/internet_layer/icmp.h @@ -1,6 +1,7 @@ #pragma once #include "types.h" #include "net/network_types.h" +#include "networking/netpkt.h" #ifdef __cplusplus extern "C" { @@ -48,7 +49,7 @@ typedef struct { } ping_result_t; bool icmp_ping(uint32_t dst_ip, uint16_t id, uint16_t seq, uint32_t timeout_ms, const void *tx_opts_or_null, uint32_t ttl, ping_result_t *out); -void icmp_input(uintptr_t ptr, uint32_t len, uint32_t src_ip, uint32_t dst_ip); +void icmp_input(netpkt_t* pkt, uint32_t src_ip, uint32_t dst_ip); #ifdef __cplusplus } diff --git a/kernel/networking/internet_layer/icmpv6.c b/kernel/networking/internet_layer/icmpv6.c index 83f70f5a..cfa24d3e 100644 --- a/kernel/networking/internet_layer/icmpv6.c +++ b/kernel/networking/internet_layer/icmpv6.c @@ -91,7 +91,7 @@ bool icmpv6_send_on_l2(uint8_t ifindex, const uint8_t dst_ip[16], const uint8_t static bool icmpv6_send_echo_reply(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[16], const uint8_t *icmp, uint32_t icmp_len, const uint8_t src_mac[6], uint8_t hop_limit) { if (!dst_ip || !icmp || icmp_len < sizeof(icmpv6_echo_t)) return false; - uintptr_t buf = (uintptr_t)malloc(icmp_len); + uintptr_t buf = (uintptr_t)zalloc(icmp_len ? icmp_len : 1u); if (!buf) return false; memcpy((void*)buf, icmp, icmp_len); @@ -103,7 +103,7 @@ static bool icmpv6_send_echo_reply(uint16_t ifindex, const uint8_t src_ip[16], c ipv6_tx_plan_t plan; if (!ipv6_build_tx_plan(dst_ip, 0 ,0, 0, &plan)) { - free_sized((void*)buf, icmp_len); + release((void*)buf); return false; } @@ -111,7 +111,7 @@ static bool icmpv6_send_echo_reply(uint16_t ifindex, const uint8_t src_ip[16], c icmpv6_send_on_l2(ifindex, src_ip, dst_ip, src_mac, (const void*)buf, icmp_len, hop_limit ? hop_limit : 64); - free_sized((void*)buf, icmp_len); + release((void*)buf); return true; } @@ -261,37 +261,63 @@ static bool extract_echo_id_seq_from_error(const uint8_t *icmp, uint32_t icmp_le return true; } -void icmpv6_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[16], uint8_t hop_limit, const uint8_t src_mac[6], const uint8_t *icmp, uint32_t icmp_len) { - if (!ifindex || !src_ip || !dst_ip || !icmp || icmp_len < sizeof(icmpv6_hdr_t)) return; +void icmpv6_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[16], uint8_t hop_limit, const uint8_t src_mac[6], netpkt_t* pkt) { + if (!ifindex || !src_ip || !dst_ip || !pkt || netpkt_len(pkt) < sizeof(icmpv6_hdr_t)) { + if (pkt) netpkt_unref(pkt); + return; + } - const icmpv6_hdr_t *h = (const icmpv6_hdr_t*)icmp; - if (h->code != 0 && (h->type == ICMPV6_ECHO_REQUEST || h->type == ICMPV6_ECHO_REPLY)) return; + const uint8_t *icmp = (const uint8_t*)netpkt_data(pkt); + uint32_t icmp_len = netpkt_len(pkt); + icmpv6_hdr_t hdr; + if (!netpkt_copyout(pkt, 0, &hdr, sizeof(hdr))) { + netpkt_unref(pkt); + return; + } + const icmpv6_hdr_t *h = &hdr; + if (h->code != 0 && (h->type == ICMPV6_ECHO_REQUEST || h->type == ICMPV6_ECHO_REPLY)) { + netpkt_unref(pkt); + return; + } uint16_t calc = bswap16(checksum16_pipv6(src_ip, dst_ip, 58, icmp, icmp_len)); - if (calc != 0) return; + if (calc != 0) { + netpkt_unref(pkt); + return; + } - if ((h->type == 133 || h->type == 134 || h->type == 135 || h->type == 136 || h->type == 137) && hop_limit != 255) return; + if ((h->type == 133 || h->type == 134 || h->type == 135 || h->type == 136 || h->type == 137) && hop_limit != 255) { + netpkt_unref(pkt); + return; + } if (h->type == 130 || h->type == 131 || h->type == 132 || h->type == 143) { - mld_input((uint8_t)ifindex, src_ip, dst_ip, icmp, icmp_len); + mld_input((uint8_t)ifindex, src_ip, dst_ip, pkt); + netpkt_unref(pkt); return; } if (h->type == ICMPV6_ECHO_REQUEST) { icmpv6_send_echo_reply(ifindex, src_ip, dst_ip, icmp, icmp_len, src_mac, hop_limit); + netpkt_unref(pkt); return; } if (h->type == ICMPV6_ECHO_REPLY) { - if (icmp_len < sizeof(icmpv6_echo_t)) return; + if (icmp_len < sizeof(icmpv6_echo_t)) { + netpkt_unref(pkt); + return; + } icmpv6_echo_t e; memcpy(&e, icmp, sizeof(e)); mark_received(bswap16(e.id), bswap16(e.seq), h->type, h->code, src_ip); + netpkt_unref(pkt); return; } if (h->type == 133 || h->type == 134 || h->type == 135 || h->type == 136 || h->type == 137) { - ndp_input(ifindex, src_ip, dst_ip, src_mac, icmp, icmp_len); + ndp_input(ifindex, src_ip, dst_ip, src_mac, pkt); + netpkt_unref(pkt); return; } @@ -310,12 +336,16 @@ void icmpv6_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ if (extract_echo_id_seq_from_error(icmp, icmp_len, &id, &seq)) mark_received(id, seq, h->type, h->code, src_ip); } + netpkt_unref(pkt); return; } if (h->type == ICMPV6_DEST_UNREACH || h->type == ICMPV6_TIME_EXCEEDED || h->type == ICMPV6_PARAM_PROBLEM) { uint16_t id = 0, seq = 0; if (extract_echo_id_seq_from_error(icmp, icmp_len, &id, &seq)) mark_received(id, seq, h->type, h->code, src_ip); + netpkt_unref(pkt); return; } + + netpkt_unref(pkt); } \ No newline at end of file diff --git a/kernel/networking/internet_layer/icmpv6.h b/kernel/networking/internet_layer/icmpv6.h index 58c54ca4..86e12eba 100644 --- a/kernel/networking/internet_layer/icmpv6.h +++ b/kernel/networking/internet_layer/icmpv6.h @@ -3,6 +3,7 @@ #include "types.h" #include "net/network_types.h" #include "networking/internet_layer/icmp.h" +#include "networking/netpkt.h" #ifdef __cplusplus extern "C" { @@ -30,7 +31,7 @@ typedef struct { uint8_t responder_ip[16]; } ping6_result_t; -void icmpv6_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[16], uint8_t hop_limit, const uint8_t src_mac[6], const uint8_t *icmp, uint32_t icmp_len); +void icmpv6_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[16], uint8_t hop_limit, const uint8_t src_mac[6], netpkt_t* pkt); bool icmpv6_ping(const uint8_t dst_ip[16], uint16_t id, uint16_t seq, uint32_t timeout_ms, const void *tx_opts_or_null, uint8_t hop_limit, ping6_result_t *out); bool icmpv6_send_on_l2(uint8_t ifindex, const uint8_t dst_ip[16], const uint8_t src_ip[16], const uint8_t dst_mac[6], const void *icmp, uint32_t icmp_len, uint8_t hop_limit); diff --git a/kernel/networking/internet_layer/igmp.c b/kernel/networking/internet_layer/igmp.c index 5420d17e..e36bae68 100644 --- a/kernel/networking/internet_layer/igmp.c +++ b/kernel/networking/internet_layer/igmp.c @@ -198,41 +198,66 @@ static void schedule_report(uint8_t ifindex, uint32_t group, uint32_t max_resp_d igmp_daemon_kick(); } -void igmp_input(uint8_t ifindex, uint32_t src, uint32_t dst, const void* l4, uint32_t l4_len) { - if (!l4 || l4_len < sizeof(igmp_hdr_t)) return; - const uint8_t* p = (const uint8_t*)l4; +void igmp_input(uint8_t ifindex, uint32_t src, uint32_t dst, netpkt_t* pkt) { + if (!pkt) return; + uint32_t l4_len = netpkt_len(pkt); + if (l4_len < sizeof(igmp_hdr_t)) { + netpkt_unref(pkt); + return; + } + const uint8_t* p = (const uint8_t*)netpkt_data(pkt); + uint8_t hdr[sizeof(igmp_hdr_t)]; + if (!netpkt_copyout(pkt, 0, hdr, sizeof(hdr))) { + netpkt_unref(pkt); + return; + } uint32_t sum = 0; for (uint32_t i = 0; i + 1 < sizeof(igmp_hdr_t); i += 2) sum += (uint32_t)((p[i] << 8) | p[i + 1]); while (sum >> 16) sum = (sum & 0xFFFFu) + (sum >> 16); - if ((uint16_t)sum != 0xFFFFu) return; + if ((uint16_t)sum != 0xFFFFu) { + netpkt_unref(pkt); + return; + } - uint8_t type = p[0]; - uint32_t group = rd_be32(p + 4); + uint8_t type = hdr[0]; + uint32_t group = rd_be32(hdr + 4); - uint32_t max_resp_ds = (uint32_t)p[1]; + uint32_t max_resp_ds = (uint32_t)hdr[1]; - if (type != IGMP_TYPE_QUERY) return; + if (type != IGMP_TYPE_QUERY) { + netpkt_unref(pkt); + return; + } if (group == 0) { l2_interface_t* l2 = l2_interface_find_by_index(ifindex); - if (!l2) return; + if (!l2) { + netpkt_unref(pkt); + return; + } for (int i = 0; i < (int)l2->ipv4_mcast_count; ++i) { uint32_t g = l2->ipv4_mcast[i]; if (ipv4_is_multicast(g)) schedule_report(ifindex, g, max_resp_ds); } + netpkt_unref(pkt); return; } l2_interface_t* l2 = l2_interface_find_by_index(ifindex); - if (!l2) return; + if (!l2) { + netpkt_unref(pkt); + return; + } for (int i = 0; i < (int)l2->ipv4_mcast_count; ++i) { if (l2->ipv4_mcast[i] == group) { schedule_report(ifindex, group, max_resp_ds); + netpkt_unref(pkt); return; } } (void)src; (void)dst; + netpkt_unref(pkt); } \ No newline at end of file diff --git a/kernel/networking/internet_layer/igmp.h b/kernel/networking/internet_layer/igmp.h index ae56c209..9e27568c 100644 --- a/kernel/networking/internet_layer/igmp.h +++ b/kernel/networking/internet_layer/igmp.h @@ -1,5 +1,6 @@ #pragma once #include "types.h" +#include "networking/netpkt.h" #ifdef __cplusplus extern "C" { @@ -7,7 +8,7 @@ extern "C" { bool igmp_send_join(uint8_t ifindex, uint32_t group); bool igmp_send_leave(uint8_t ifindex, uint32_t group); -void igmp_input(uint8_t ifindex, uint32_t src, uint32_t dst, const void* l4, uint32_t l4_len); +void igmp_input(uint8_t ifindex, uint32_t src, uint32_t dst, netpkt_t* pkt); #ifdef __cplusplus } diff --git a/kernel/networking/internet_layer/ipv4.c b/kernel/networking/internet_layer/ipv4.c index b13f9c89..727185d2 100644 --- a/kernel/networking/internet_layer/ipv4.c +++ b/kernel/networking/internet_layer/ipv4.c @@ -333,10 +333,10 @@ void ipv4_send_packet(uint32_t dst_ip, uint8_t proto, netpkt_t* pkt, const ipv4_ void ipv4_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { if (!pkt) return; uint32_t ip_len = netpkt_len(pkt); - uintptr_t ip_ptr = netpkt_data(pkt); if (ip_len < sizeof(ipv4_hdr_t)) return; - uint8_t first = *(const uint8_t*)ip_ptr; + uint8_t first = 0; + if (!netpkt_copyout(pkt, 0, &first, sizeof(first))) return; uint8_t ver = (uint8_t)(first >> 4); uint8_t ihl = (uint8_t)(first & 0x0F); if (ver != IP_VERSION_4) return; @@ -347,7 +347,7 @@ void ipv4_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { uint16_t hdr_words[60 /sizeof(uint16_t)]; uint8_t* hdr_copy = (uint8_t*)hdr_words; - memcpy(hdr_copy, (const void*)ip_ptr, hdr_len); + if (!netpkt_copyout(pkt, 0, hdr_copy, hdr_len)) return; if (checksum16(hdr_words, hdr_len / 2) != 0) return; ipv4_hdr_t ip; memcpy(&ip, hdr_copy, sizeof(ip)); @@ -358,7 +358,6 @@ void ipv4_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { (void)netpkt_trim(pkt, ip_totlen); ip_len = ip_totlen; - uintptr_t l4 = ip_ptr + hdr_len; uint32_t l4_len = (uint32_t)ip_totlen - hdr_len; uint32_t src = bswap32(ip.src_ip); @@ -399,9 +398,19 @@ void ipv4_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { for (int i = 0; i < ccount; ++i) { uint8_t l3id = cand[i]->l3_id; switch (proto) { - case 2: igmp_input((uint8_t)ifindex, src, dst, (const void*)l4, l4_len); 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; + netpkt_t* l4pkt; + case 2: + l4pkt = netpkt_view(pkt, hdr_len, l4_len); + if (l4pkt) igmp_input((uint8_t)ifindex, src, dst, l4pkt); + break; + case 6: + l4pkt = netpkt_view(pkt, hdr_len, l4_len); + if (l4pkt) tcp_input(IP_VER4, &src, &dst, l3id, l4pkt); + break; + case 17: + l4pkt = netpkt_view(pkt, hdr_len, l4_len); + if (l4pkt) udp_input(IP_VER4, &src, &dst, l3id, l4pkt); + break; default: break; } } @@ -411,21 +420,44 @@ void ipv4_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { if (dst == 0xFFFFFFFFu) { 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; + netpkt_t* l4pkt = netpkt_view(pkt, hdr_len, l4_len); + if (l4pkt) { + switch (proto) { + case 1: + icmp_input(l4pkt, src, dst); + break; + case 2: + igmp_input((uint8_t)ifindex, src, dst, l4pkt); + break; + case 6: + tcp_input(IP_VER4, &src, &dst, cand[0]->l3_id, l4pkt); + break; + case 17: + udp_input(IP_VER4, &src, &dst, cand[0]->l3_id, l4pkt); + break; + default: + netpkt_unref(l4pkt); + 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; + netpkt_t* l4pkt; + case 1: + l4pkt = netpkt_view(pkt, hdr_len, l4_len); + if (l4pkt) icmp_input(l4pkt, src, dst); + break; + case 6: + l4pkt = netpkt_view(pkt, hdr_len, l4_len); + if (l4pkt) tcp_input(IP_VER4, &src, &dst, l3id, l4pkt); + break; + case 17: + l4pkt = netpkt_view(pkt, hdr_len, l4_len); + if (l4pkt) udp_input(IP_VER4, &src, &dst, l3id, l4pkt); + break; default: break; } } @@ -442,11 +474,26 @@ void ipv4_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { } } 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; + netpkt_t* l4pkt = netpkt_view(pkt, hdr_len, l4_len); + if (l4pkt) { + + switch (proto) { + case 1: + icmp_input(l4pkt, src, dst); + break; + case 2: + igmp_input((uint8_t)ifindex, src, dst, l4pkt); + break; + case 6: + tcp_input(IP_VER4, &src, &dst, match_l3id, l4pkt); + break; + case 17: + udp_input(IP_VER4, &src, &dst, match_l3id, l4pkt); + break; + default: + netpkt_unref(l4pkt); + break; + } } return; } @@ -455,9 +502,19 @@ void ipv4_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { 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; + netpkt_t* l4pkt; + case 1: + l4pkt = netpkt_view(pkt, hdr_len, l4_len); + if (l4pkt) icmp_input(l4pkt, src, dst); + break; + case 6: + l4pkt = netpkt_view(pkt, hdr_len, l4_len); + if (l4pkt) tcp_input(IP_VER4, &src, &dst, l3id, l4pkt); + break; + case 17: + l4pkt = netpkt_view(pkt, hdr_len, l4_len); + if (l4pkt) udp_input(IP_VER4, &src, &dst, l3id, l4pkt); + break; default: break; } } @@ -477,9 +534,19 @@ void ipv4_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { 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; + netpkt_t* l4pkt; + case 1: + l4pkt = netpkt_view(pkt, hdr_len, l4_len); + if (l4pkt) icmp_input(l4pkt, src, dst); + break; + case 6: + l4pkt = netpkt_view(pkt, hdr_len, l4_len); + if (l4pkt) tcp_input(IP_VER4, &src, &dst, l3id, l4pkt); + break; + case 17: + l4pkt = netpkt_view(pkt, hdr_len, l4_len); + if (l4pkt) udp_input(IP_VER4, &src, &dst, l3id, l4pkt); + break; default: break; } } diff --git a/kernel/networking/internet_layer/ipv4_route.c b/kernel/networking/internet_layer/ipv4_route.c index 71b67e0a..da806b72 100644 --- a/kernel/networking/internet_layer/ipv4_route.c +++ b/kernel/networking/internet_layer/ipv4_route.c @@ -91,15 +91,14 @@ static int prefix_len(uint32_t m) { } ipv4_rt_table_t* ipv4_rt_create(void) { - ipv4_rt_table_t* t = (ipv4_rt_table_t*)malloc(sizeof(ipv4_rt_table_t)); + ipv4_rt_table_t* t = (ipv4_rt_table_t*)zalloc(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_sized(t, sizeof(*t)); + release(t); } void ipv4_rt_clear(ipv4_rt_table_t* t) { diff --git a/kernel/networking/internet_layer/ipv6.c b/kernel/networking/internet_layer/ipv6.c index 66ad0977..1843467d 100644 --- a/kernel/networking/internet_layer/ipv6.c +++ b/kernel/networking/internet_layer/ipv6.c @@ -19,6 +19,8 @@ #define IPV6_MIN_MTU 1280u #define PMTU_CACHE_SIZE 16 #define REASS_SLOTS 8 +#define REASS_STEP 2048u +#define REASS_MAX_LEN (2048u * 8u) typedef struct { uint8_t used; @@ -49,6 +51,7 @@ typedef struct { uint8_t first_pkt[1280]; uint8_t *buf; + uint32_t buf_cap; uint8_t bitmap[2048]; } reass_slot_t; @@ -109,10 +112,15 @@ void ipv6_pmtu_note(const uint8_t dst[16], uint16_t mtu) { static void reass_free(reass_slot_t *s) { if (!s) return; - if (s->buf) free_sized(s->buf, 2048u * 8u); + if (s->buf) release(s->buf); memset(s, 0, sizeof(*s)); } + +static void free_release(void* ctx, uintptr_t base, uint32_t alloc_size) { + if (base) release((void*)base); +} + static void icmpv6_send_error(uint8_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[16], const uint8_t dst_mac[6], uint8_t type, uint8_t code, uint32_t param32, const uint8_t *invoking, uint32_t invoking_len) { if (!ifindex || !src_ip || !dst_ip || !dst_mac || !invoking || !invoking_len) return; @@ -124,7 +132,7 @@ static void icmpv6_send_error(uint8_t ifindex, const uint8_t src_ip[16], const u if (copy > max_invoke - base) copy = max_invoke - base; uint32_t icmp_len = base + copy; - uint8_t *buf = (uint8_t*)malloc(icmp_len); + uint8_t *buf = (uint8_t*)zalloc(icmp_len ? icmp_len : 1u); if (!buf) return; icmpv6_hdr_t *h = (icmpv6_hdr_t*)buf; @@ -140,7 +148,7 @@ static void icmpv6_send_error(uint8_t ifindex, const uint8_t src_ip[16], const u icmpv6_send_on_l2(ifindex, dst_ip, src_ip, dst_mac, buf, icmp_len, 64); - free_sized(buf, icmp_len); + release(buf); } static l3_ipv6_interface_t* best_v6_on_l2_for_dst(l2_interface_t* l2, const uint8_t dst[16]) { @@ -508,35 +516,34 @@ void ipv6_send_packet(const uint8_t dst[16], uint8_t next_header, netpkt_t* pkt, netpkt_unref(pkt); } -static bool ipv6_skip_ext_headers(uint8_t* nh, uintptr_t* l4, uint32_t* l4_len) { - if (!nh || !l4 || !l4_len) return false; +static bool ipv6_skip_ext_headers(const netpkt_t* pkt, uint8_t* nh, uint32_t* l4_off, uint32_t* l4_len) { + if (!pkt || !nh || !l4_off || !l4_len) return false; + uint32_t total_len = netpkt_len(pkt); for(;;) { + uint8_t ext[2]; + if (*l4_off > total_len || *l4_len > total_len - *l4_off) return false; uint8_t h = *nh; if (h == 44) return true; - if (h == 0 ||h == 43 || h == 60) { - if (*l4_len < 2) return false; - const uint8_t* p = (const uint8_t*)(*l4); - uint8_t next = p[0]; - uint8_t hlen = p[1]; - uint32_t bytes = (uint32_t)(hlen + 1u)*8; + if (h == 0 || h == 43 || h == 60) { + if (*l4_len < sizeof(ext)) return false; + if (!netpkt_copyout(pkt, *l4_off, ext, sizeof(ext))) return false; + uint32_t bytes = ((uint32_t)ext[1] + 1u)*8; if (bytes > *l4_len) return false; - *nh = next; - *l4 += bytes; + *nh = ext[0]; + *l4_off += bytes; *l4_len -= bytes; continue; } if (h == 51) { - if (*l4_len < 2) return false; - const uint8_t* p = (const uint8_t*)(*l4); - uint8_t next = p[0]; - uint8_t plen = p[1]; - uint32_t bytes = ((uint32_t)plen + 2u)*4; + if (*l4_len < sizeof(ext)) return false; + if (!netpkt_copyout(pkt, *l4_off, ext, sizeof(ext))) return false; + uint32_t bytes = ((uint32_t)ext[1] + 2u)*4; if (bytes > *l4_len) return false; - *nh = next; - *l4 += bytes; + *nh = ext[0]; + *l4_off += bytes; *l4_len -= bytes; continue; } @@ -548,12 +555,11 @@ static bool ipv6_skip_ext_headers(uint8_t* nh, uintptr_t* l4, uint32_t* l4_len) void ipv6_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { if (!pkt) return; uint32_t ip_len = netpkt_len(pkt); - uintptr_t ip_ptr = netpkt_data(pkt); if (ip_len < sizeof(ipv6_hdr_t)) return; ipv6_hdr_t ip6_; ipv6_hdr_t* ip6 = &ip6_; - memcpy(ip6, (const void*)ip_ptr, sizeof(*ip6)); + if (!netpkt_copyout(pkt, 0, ip6, sizeof(*ip6))) return; uint32_t v = bswap32(ip6->ver_tc_fl); if ((v >> 28) != 6) return; uint32_t now = (uint32_t)get_time(); @@ -561,7 +567,7 @@ void ipv6_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { reass_slot_t *s = &g_reass[i]; if (!s->used) continue; - if (now - s->first_rx_ms < 60000u) continue; + if (now - s->last_update_ms < 60000u) continue; if (s->have_first && s->first_pkt_len) { icmpv6_send_error(s->ifindex, s->dst, s->src, s->first_src_mac, 3, 1, 0, s->first_pkt, s->first_pkt_len); @@ -597,7 +603,7 @@ void ipv6_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { if (!dst_is_local) return; } - uintptr_t l4 = ip_ptr + sizeof(ipv6_hdr_t); + uint32_t l4_off = (uint32_t)sizeof(ipv6_hdr_t); uint32_t l4_len = (uint32_t)payload_len; if (ipv6_is_linklocal(ip6->dst) && !ipv6_is_unspecified(ip6->src) && !ipv6_is_linklocal(ip6->src)) return; @@ -606,13 +612,13 @@ void ipv6_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { uint8_t nh = ip6->next_header; - if (!ipv6_skip_ext_headers(&nh, &l4, &l4_len)) return; + if (!ipv6_skip_ext_headers(pkt, &nh, &l4_off, &l4_len)) return; if (nh == 44) {//b if (l4_len < sizeof(ipv6_frag_hdr_t)) return; ipv6_frag_hdr_t fh; - memcpy(&fh, (const void*)l4, sizeof(fh)); + if (!netpkt_copyout(pkt, l4_off, &fh, sizeof(fh))) return; uint8_t inner_nh = fh.next_header; uint16_t off_flags = bswap16(fh.offset_flags); uint32_t ident = bswap32(fh.identification); @@ -620,7 +626,7 @@ void ipv6_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { uint32_t off = ((uint32_t)(off_flags >> 3) & 0x1FFFu) * 8u; uint8_t more = (off_flags & 0x0001u) ? 1u : 0u; - const uint8_t* frag = (const uint8_t*)l4 + sizeof(ipv6_frag_hdr_t); + uint32_t frag_off = l4_off + (uint32_t)sizeof(ipv6_frag_hdr_t); uint32_t frag_len = l4_len-(uint32_t)sizeof(ipv6_frag_hdr_t); if (more && (frag_len & 7u)) { @@ -632,7 +638,7 @@ void ipv6_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { uint32_t cpy = l4_len; uint32_t max = (uint32_t)sizeof(ipv6_frag_hdr_t) + 8u; if (cpy > max) cpy = max; - memcpy(invoke_buf + sizeof(ipv6_hdr_t), (void*)l4, cpy); + if (!netpkt_copyout(pkt, l4_off, invoke_buf + sizeof(ipv6_hdr_t), cpy)) return; inv = invoke_buf; inv_len = (uint32_t)sizeof(invoke_buf); } @@ -649,7 +655,7 @@ void ipv6_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { uint32_t cpy = l4_len; uint32_t max = (uint32_t)sizeof(ipv6_frag_hdr_t) + 8u; if (cpy > max) cpy = max; - memcpy(invoke_buf + sizeof(ipv6_hdr_t), (void*)l4, cpy); + if (!netpkt_copyout(pkt, l4_off, invoke_buf + sizeof(ipv6_hdr_t), cpy)) return; inv = invoke_buf; inv_len = (uint32_t)sizeof(invoke_buf); } @@ -657,7 +663,7 @@ void ipv6_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { return; } - if (off + frag_len > 2048u * 8u) return; + if (off + frag_len > REASS_MAX_LEN) return; reass_slot_t* s = NULL; uint32_t now = (uint32_t)get_time(); @@ -671,8 +677,7 @@ void ipv6_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { if (ipv6_cmp(t->src, ip6->src) != 0) continue; if (ipv6_cmp(t->dst, ip6->dst) != 0) continue; if (now - t->last_update_ms > 60000u) { - if (t->buf) free_sized(t->buf, 2048u * 8u); - memset(t, 0, sizeof(*t)); + reass_free(t); continue; } s = t; @@ -684,8 +689,8 @@ void ipv6_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { reass_slot_t *t = &g_reass[i]; if (t->used) continue; - t->buf = (uint8_t*)malloc(2048u * 8u); - if (!t->buf) return; + t->buf = 0; + t->buf_cap = 0; t->used = 1; t->ifindex = (uint8_t)ifindex; @@ -730,17 +735,18 @@ void ipv6_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { int ok = 1; while (nh == 0 || nh == 43 || nh == 60 || nh == 51) { - const uint8_t *p = frag + ulh_off; + uint8_t ext[2]; uint32_t avail = frag_len - ulh_off; - if (avail < 2) { ok = 0; break; } + if (avail < sizeof(ext)) { ok = 0; break; } + if (!netpkt_copyout(pkt, frag_off + ulh_off, ext, sizeof(ext))) { ok = 0; break; } uint32_t hlen = 0; - if (nh == 0 || nh == 43 || nh == 60) hlen = ((uint32_t)p[1] + 1u) * 8u; - else hlen = ((uint32_t)p[1] + 2u) * 4u; + if (nh == 0 || nh == 43 || nh == 60) hlen = ((uint32_t)ext[1] + 1u) * 8u; + else hlen = ((uint32_t)ext[1] + 2u) * 4u; if (hlen > avail) { ok = 0; break; } - nh = p[0]; + nh = ext[0]; ulh_off += hlen; } @@ -762,7 +768,7 @@ void ipv6_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { uint32_t cpy = l4_len; uint32_t max = (uint32_t)sizeof(ipv6_frag_hdr_t) + 64u; if (cpy > max) cpy = max; - memcpy(invoke_buf + sizeof(ipv6_hdr_t), (void*)l4, cpy); + if (!netpkt_copyout(pkt, l4_off, invoke_buf + sizeof(ipv6_hdr_t), cpy)) return; inv = invoke_buf; inv_len = (uint32_t)sizeof(invoke_buf); } @@ -774,20 +780,48 @@ void ipv6_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { if (off == 0 && !s->have_first) { uint32_t inv_len = (uint32_t)sizeof(ipv6_hdr_t) + l4_len; if (inv_len > sizeof(s->first_pkt)) inv_len = sizeof(s->first_pkt); - memcpy(s->first_pkt, ip6, inv_len); + memcpy(s->first_pkt, ip6, sizeof(*ip6)); + if (inv_len > (uint32_t)sizeof(*ip6) && !netpkt_copyout(pkt, l4_off, s->first_pkt + sizeof(*ip6), inv_len - (uint32_t)sizeof(*ip6))) { + reass_free(s); + return; + } s->first_pkt_len = (uint16_t)inv_len; memcpy(s->first_src_mac, src_mac, 6); s->have_first = 1; } - memcpy(s->buf + off, frag, frag_len); + if (off + frag_len > REASS_MAX_LEN) { + reass_free(s); + return; + } + if (s->buf_cap < off + frag_len) { + uint32_t new_cap = (off + frag_len + (REASS_STEP - 1u)) & ~(REASS_STEP - 1u); + if (new_cap < REASS_STEP) new_cap = REASS_STEP; + if (new_cap > REASS_MAX_LEN) new_cap = REASS_MAX_LEN; + + uint8_t* new_buf = (uint8_t*)zalloc(new_cap); + if (!new_buf) { + reass_free(s); + return; + } + + if (s->buf && s->buf_cap) memcpy(new_buf, s->buf, s->buf_cap); + if (s->buf) release(s->buf); + + s->buf = new_buf; + s->buf_cap = new_cap; + } + if (!netpkt_copyout(pkt, frag_off, s->buf + off, frag_len)) { + reass_free(s); + return; + } start = off / 8u; end = (off + frag_len + 7u) / 8u; if (end > sizeof(s->bitmap)) end = sizeof(s->bitmap); for (uint32_t i = start; i < end; i++) s->bitmap[i] = 1; - s->last_update_ms = (uint32_t)get_time(); + s->last_update_ms = now; if (!more) { s->have_last = 1; @@ -808,21 +842,36 @@ void ipv6_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { if (!complete) return; - uintptr_t payload_ptr = (uintptr_t)s->buf; + uint32_t payload_off = 0; uint32_t payload_size = s->total_len; - if (!ipv6_skip_ext_headers(&inner_nh, &payload_ptr, &payload_size)) { + + netpkt_t* reassembled = netpkt_wrap((uintptr_t)s->buf, s->total_len, 0, s->total_len, free_release, 0); + if (!reassembled) { + reass_free(s); + return; + } + s->buf = 0; + + if (!ipv6_skip_ext_headers(reassembled, &inner_nh, &payload_off, &payload_size)) { + netpkt_unref(reassembled); reass_free(s); return; } if (inner_nh == 58) { - icmpv6_input(ifindex, ip6->src, ip6->dst, ip6->hop_limit, src_mac, (const uint8_t*)payload_ptr, payload_size); + netpkt_t* l4pkt = netpkt_view(reassembled, payload_off, payload_size); + if (l4pkt) icmpv6_input(ifindex, ip6->src, ip6->dst, ip6->hop_limit, src_mac, l4pkt); + netpkt_unref(reassembled); reass_free(s); return; } l2_interface_t* l2 = l2_interface_find_by_index((uint8_t)ifindex); - if (!l2) { reass_free(s); return; } + if (!l2) { + netpkt_unref(reassembled); + reass_free(s); + return; + } l3_ipv6_interface_t* cand[MAX_IPV6_PER_INTERFACE]; int ccount = 0; @@ -833,6 +882,7 @@ void ipv6_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { cand[ccount++] = v6; } if (ccount == 0) { + netpkt_unref(reassembled); reass_free(s); return; } @@ -846,6 +896,7 @@ void ipv6_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { } } if (!joined) { + netpkt_unref(reassembled); reass_free(s); return; } @@ -853,10 +904,14 @@ void ipv6_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { for (int i = 0; i < ccount; i++) { l3_ipv6_interface_t* v6 = cand[i]; if (!ipv6_is_linklocal(v6->ip) && ipv6_is_linklocal(ip6->dst)) continue; - if (inner_nh == 17) udp_input(IP_VER6, ip6->src, ip6->dst, v6->l3_id, payload_ptr, payload_size); - else if (inner_nh == 6) tcp_input(IP_VER6, ip6->src, ip6->dst, v6->l3_id, payload_ptr, payload_size); + netpkt_t* l4pkt = netpkt_view(reassembled, payload_off, payload_size); + if (!l4pkt) continue; + if (inner_nh == 17) udp_input(IP_VER6, ip6->src, ip6->dst, v6->l3_id, l4pkt); + else if (inner_nh == 6) tcp_input(IP_VER6, ip6->src, ip6->dst, v6->l3_id, l4pkt); + else netpkt_unref(l4pkt); } + netpkt_unref(reassembled); reass_free(s); return; } @@ -871,16 +926,21 @@ void ipv6_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { } if (match_count >= 1) { - if (inner_nh == 6) tcp_input(IP_VER6, ip6->src, ip6->dst, match_l3id, payload_ptr, payload_size); - else if (inner_nh == 17) udp_input(IP_VER6, ip6->src, ip6->dst, match_l3id, payload_ptr, payload_size); + netpkt_t* l4pkt = netpkt_view(reassembled, payload_off, payload_size); + if (l4pkt) { + if (inner_nh == 6) tcp_input(IP_VER6, ip6->src, ip6->dst, match_l3id, l4pkt); + else if (inner_nh == 17) udp_input(IP_VER6, ip6->src, ip6->dst, match_l3id, l4pkt); + else netpkt_unref(l4pkt); + } } - + netpkt_unref(reassembled); reass_free(s); return; } if (nh == 58) { - icmpv6_input(ifindex, ip6->src, ip6->dst, ip6->hop_limit, src_mac, (const uint8_t*)l4, l4_len); + netpkt_t* l4pkt = netpkt_view(pkt, l4_off, l4_len); + if (l4pkt) icmpv6_input(ifindex, ip6->src, ip6->dst, ip6->hop_limit, src_mac, l4pkt); return; } @@ -912,12 +972,15 @@ void ipv6_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { if (!ipv6_is_linklocal(v6->ip) && ipv6_is_linklocal(ip6->dst)) continue; - switch (ip6->next_header) { + switch (nh) { + netpkt_t* l4pkt; case 17: - udp_input(IP_VER6, ip6->src, ip6->dst, v6->l3_id, l4, l4_len); + l4pkt = netpkt_view(pkt, l4_off, l4_len); + if (l4pkt) udp_input(IP_VER6, ip6->src, ip6->dst, v6->l3_id, l4pkt); break; case 6: - tcp_input(IP_VER6, ip6->src, ip6->dst, v6->l3_id, l4, l4_len); + l4pkt = netpkt_view(pkt, l4_off, l4_len); + if (l4pkt) tcp_input(IP_VER6, ip6->src, ip6->dst, v6->l3_id, l4pkt); break; default: break; @@ -936,12 +999,15 @@ void ipv6_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { } if (match_count >= 1) { - switch (ip6->next_header) { + switch (nh) { + netpkt_t* l4pkt; case 6: - tcp_input(IP_VER6, ip6->src, ip6->dst, match_l3id, l4, l4_len); + l4pkt = netpkt_view(pkt, l4_off, l4_len); + if (l4pkt) tcp_input(IP_VER6, ip6->src, ip6->dst, match_l3id, l4pkt); break; case 17: - udp_input(IP_VER6, ip6->src, ip6->dst, match_l3id, l4, l4_len); + l4pkt = netpkt_view(pkt, l4_off, l4_len); + if (l4pkt) udp_input(IP_VER6, ip6->src, ip6->dst, match_l3id, l4pkt); break; default: break; diff --git a/kernel/networking/internet_layer/ipv6_route.c b/kernel/networking/internet_layer/ipv6_route.c index 201bcea3..f3484cd7 100644 --- a/kernel/networking/internet_layer/ipv6_route.c +++ b/kernel/networking/internet_layer/ipv6_route.c @@ -94,17 +94,16 @@ struct ipv6_rt_table { }; ipv6_rt_table_t* ipv6_rt_create(void) { - ipv6_rt_table_t* t = malloc(sizeof(*t)); + ipv6_rt_table_t* t = zalloc(sizeof(*t)); if (!t) return 0; - memset(t, 0, sizeof(*t)); return t; } void ipv6_rt_destroy(ipv6_rt_table_t* t) { if (!t) return; - free_sized(t, sizeof(*t)); + release(t); } void ipv6_rt_clear(ipv6_rt_table_t* t) { diff --git a/kernel/networking/internet_layer/mld.c b/kernel/networking/internet_layer/mld.c index c17b8a52..b5bbdefe 100644 --- a/kernel/networking/internet_layer/mld.c +++ b/kernel/networking/internet_layer/mld.c @@ -317,25 +317,29 @@ static void schedule_report(uint8_t ifindex, const uint8_t group[16], uint16_t m mld_daemon_kick(); } -void mld_input(uint8_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[16], const void* l4, uint32_t l4_len) { - if(!ifindex || !src_ip || !dst_ip || !l4) return; +void mld_input(uint8_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[16], netpkt_t* pkt) { + if(!ifindex || !src_ip || !dst_ip || !pkt) return; + uint32_t l4_len = netpkt_len(pkt); if(l4_len < 8) return; - const uint8_t* p = (const uint8_t*)l4; - uint8_t type = p[0]; + uint8_t type = 0; + if (!netpkt_copyout(pkt, 0, &type, 1)) return; if(type == MLD_TYPE_REPORT_V2) { - if(l4_len < 8) return; - uint16_t nrec = (uint16_t)((uint16_t)p[6] << 8) | (uint16_t)p[7]; + uint8_t hdr[8]; + if (!netpkt_copyout(pkt, 0, hdr, sizeof(hdr))) return; + uint16_t nrec = rd_be16(hdr + 6); uint32_t off = 8; for(uint16_t i = 0; i < nrec; i++) { - if(off + 20u > l4_len) break; - - uint8_t rtype = p[off + 0]; - uint8_t aux_words = p[off + 1]; - uint16_t nsrc = (uint16_t)((uint16_t)p[off + 2] << 8) | (uint16_t)p[off + 3]; - const uint8_t* group = p + off + 4; + uint8_t rec[20]; + uint8_t group[16]; + if(!netpkt_copyout(pkt, off, rec, sizeof(rec))) break; + + uint8_t rtype = rec[0]; + uint8_t aux_words = rec[1]; + uint16_t nsrc = rd_be16(rec + 2); + memcpy(group, rec + 4, sizeof(group)); off += 20; uint32_t src_bytes = (uint32_t)nsrc * 16u; @@ -360,9 +364,8 @@ void mld_input(uint8_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[1 } if(type == MLD_TYPE_REPORT_V1) { - if(l4_len < 24) return; uint8_t group[16]; - memcpy(group, p + 8, 16); + if (!netpkt_copyout(pkt, 8, group, sizeof(group))) return; if(ipv6_is_multicast(group)) mld_suppress_pending(ifindex, src_ip, group); return; } @@ -370,10 +373,11 @@ void mld_input(uint8_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[1 if(type != MLD_TYPE_QUERY) return; if(l4_len < 24) return; - uint16_t max_resp_ms = (uint16_t)((uint16_t)p[4] << 8) | (uint16_t)p[5]; - + uint8_t query[24]; uint8_t group[16]; - memcpy(group, p + 8, 16); + if (!netpkt_copyout(pkt, 0, query, sizeof(query))) return; + uint16_t max_resp_ms = rd_be16(query + 4); + memcpy(group, query + 8, sizeof(group)); l2_interface_t* l2 = l2_interface_find_by_index(ifindex); if(!l2) return; diff --git a/kernel/networking/internet_layer/mld.h b/kernel/networking/internet_layer/mld.h index 35269e92..64585957 100644 --- a/kernel/networking/internet_layer/mld.h +++ b/kernel/networking/internet_layer/mld.h @@ -10,7 +10,7 @@ extern "C" { bool mld_send_join(uint8_t ifindex, const uint8_t group[16]); bool mld_send_leave(uint8_t ifindex, const uint8_t group[16]); -void mld_input(uint8_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[16], const void* l4, uint32_t l4_len); +void mld_input(uint8_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[16], netpkt_t* pkt); #ifdef __cplusplus } diff --git a/kernel/networking/link_layer/arp.c b/kernel/networking/link_layer/arp.c index 0f980b96..a0f64600 100644 --- a/kernel/networking/link_layer/arp.c +++ b/kernel/networking/link_layer/arp.c @@ -22,16 +22,15 @@ static inline arp_table_t* l2_arp(uint8_t ifindex){ } arp_table_t* arp_table_create(void){ - arp_table_t* t = (arp_table_t*)malloc(sizeof(arp_table_t)); + arp_table_t* t = (arp_table_t*)zalloc(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_destroy(arp_table_t* t){ - if (t) free_sized(t, sizeof(*t)); + if (t) release(t); } void arp_table_init_static_defaults(arp_table_t* t){ @@ -205,15 +204,12 @@ static void arp_send_reply_on(uint8_t ifindex, const arp_hdr_t* in_arp, const ui (void)eth_send_frame_on(ifindex, ETHERTYPE_ARP, in_src_mac, pkt); } -void arp_input(uint16_t ifindex, netpkt_t* pkt) { - if (!pkt) return; - uint32_t frame_len = netpkt_len(pkt); - uintptr_t frame_ptr = netpkt_data(pkt); - if (frame_len < (uint32_t)sizeof(eth_hdr_t) + (uint32_t)sizeof(arp_hdr_t)) return; +void arp_input(uint16_t ifindex, const uint8_t src_mac[6], netpkt_t* pkt) { + if (!pkt || !src_mac) return; + if (netpkt_len(pkt) < (uint32_t)sizeof(arp_hdr_t)) return; - const uint8_t* src_mac = (const uint8_t*)(frame_ptr+6); arp_hdr_t hdr; - memcpy(&hdr, (const void*)(frame_ptr + sizeof(eth_hdr_t)), sizeof(hdr)); + if (!netpkt_copyout(pkt, 0, &hdr, sizeof(hdr))) return; uint16_t op = bswap16(hdr.opcode); uint32_t sender_ip = bswap32(hdr.sender_ip); uint32_t target_ip = bswap32(hdr.target_ip); diff --git a/kernel/networking/link_layer/arp.h b/kernel/networking/link_layer/arp.h index badfb6ee..7d59be70 100644 --- a/kernel/networking/link_layer/arp.h +++ b/kernel/networking/link_layer/arp.h @@ -44,7 +44,7 @@ void arp_tick_all(uint32_t ms); 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_input(uint16_t ifindex, netpkt_t* pkt); +void arp_input(uint16_t ifindex, const uint8_t src_mac[6], netpkt_t* pkt); void arp_set_pid(uint16_t pid); uint16_t arp_get_pid(void); diff --git a/kernel/networking/link_layer/eth.c b/kernel/networking/link_layer/eth.c index e8ea98ba..9d1bfbdb 100644 --- a/kernel/networking/link_layer/eth.c +++ b/kernel/networking/link_layer/eth.c @@ -16,16 +16,20 @@ uintptr_t create_eth_packet(uintptr_t p, const uint8_t src_mac[6], const uint8_t return p + (uint32_t)sizeof(eth_hdr_t); } -uint16_t eth_parse_type(uintptr_t ptr){ - return rd_be16((const void*)(ptr+12)); +uint16_t eth_parse_type(const netpkt_t* pkt){ + uint16_t type = 0; + if (!pkt || !netpkt_copyout(pkt, 12u, &type, sizeof(type))) return 0; + return rd_be16(&type); } -const uint8_t* eth_src(uintptr_t ptr){ - return (const uint8_t*)(ptr+6); +bool eth_src(const netpkt_t* pkt, uint8_t out[6]){ + if (!pkt || !out) return false; + return netpkt_copyout(pkt, 6u, out, 6); } -const uint8_t* eth_dst(uintptr_t ptr){ - return (const uint8_t*)ptr; +bool eth_dst(const netpkt_t* pkt, uint8_t out[6]){ + if (!pkt || !out) return false; + return netpkt_copyout(pkt, 0u, out, 6); } bool eth_send_frame_on(uint16_t ifindex, uint16_t ethertype, const uint8_t dst_mac[6], netpkt_t* pkt){ @@ -51,30 +55,35 @@ bool eth_send_frame_on(uint16_t ifindex, uint16_t ethertype, const uint8_t dst_m void eth_input(uint16_t ifindex, netpkt_t* pkt) { if (!pkt) return; - uint32_t frame_len = netpkt_len(pkt); - uintptr_t frame_ptr = netpkt_data(pkt); - if (frame_len < sizeof(eth_hdr_t)) return; + if (netpkt_len(pkt) < sizeof(eth_hdr_t)) return; + eth_hdr_t eth; + if (!netpkt_copyout(pkt, 0, ð, sizeof(eth))) return; - uint16_t type = eth_parse_type(frame_ptr); - const uint8_t* src_mac = eth_src(frame_ptr); + uint16_t type = bswap16(eth.ethertype); + netpkt_t* payload = netpkt_view(pkt, (uint32_t)sizeof(eth_hdr_t), netpkt_len(pkt) - (uint32_t)sizeof(eth_hdr_t)); + if (!payload) return; switch (type) { case ETHERTYPE_ARP: - arp_input(ifindex, pkt); + arp_input(ifindex, eth.src_mac, payload); + netpkt_unref(payload); break; case ETHERTYPE_IPV4: - if (!netpkt_pull(pkt, (uint32_t)sizeof(eth_hdr_t))) break; - ipv4_input(ifindex, pkt, src_mac); + ipv4_input(ifindex, payload, eth.src_mac); + netpkt_unref(payload); break; case ETHERTYPE_IPV6: - if (!netpkt_pull(pkt, (uint32_t)sizeof(eth_hdr_t))) break; - ipv6_input(ifindex, pkt, src_mac); + ipv6_input(ifindex, payload, eth.src_mac); + netpkt_unref(payload); break; - case ETHERTYPE_VLAN1Q: //TODO vlan + case ETHERTYPE_VLAN1Q: + netpkt_unref(payload); break; - case ETHERTYPE_VLAN1AD: //TODO vlan + case ETHERTYPE_VLAN1AD: + netpkt_unref(payload); break; default: + netpkt_unref(payload); break; } } diff --git a/kernel/networking/link_layer/eth.h b/kernel/networking/link_layer/eth.h index 38dc465b..7ac9d339 100644 --- a/kernel/networking/link_layer/eth.h +++ b/kernel/networking/link_layer/eth.h @@ -19,9 +19,9 @@ typedef struct __attribute__((packed)) eth_hdr_t { uint16_t ethertype; } eth_hdr_t; -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); +uint16_t eth_parse_type(const netpkt_t* pkt); +bool eth_src(const netpkt_t* pkt, uint8_t out[6]); +bool eth_dst(const netpkt_t* pkt, uint8_t out[6]); bool eth_send_frame_on(uint16_t ifindex, uint16_t ethertype, const uint8_t dst_mac[6], netpkt_t* pkt); diff --git a/kernel/networking/link_layer/ndp.c b/kernel/networking/link_layer/ndp.c index e54221e3..ada22a54 100644 --- a/kernel/networking/link_layer/ndp.c +++ b/kernel/networking/link_layer/ndp.c @@ -318,10 +318,8 @@ static void ndp_on_ra(uint8_t ifindex, const uint8_t router_ip[16], uint16_t rou } ndp_table_t* ndp_table_create(void) { - ndp_table_impl_t* t = (ndp_table_impl_t*)malloc(sizeof(ndp_table_impl_t)); + ndp_table_impl_t* t = (ndp_table_impl_t*)zalloc(sizeof(ndp_table_impl_t)); if (!t) return 0; - - memset(t, 0, sizeof(*t)); t->init = 1; return (ndp_table_t*)t; @@ -329,7 +327,7 @@ ndp_table_t* ndp_table_create(void) { void ndp_table_destroy(ndp_table_t* t) { if (!t) return; - free_sized(t, sizeof(ndp_table_impl_t)); + release(t); } static ndp_table_impl_t* l2_ndp(uint8_t ifindex) { @@ -433,7 +431,7 @@ static bool ndp_table_get_for_l2(uint8_t ifindex, const uint8_t ip[16], uint8_t static bool ndp_send_na_on(uint8_t ifindex, const uint8_t dst_ip[16], const uint8_t src_ip[16], const uint8_t target_ip[16], const uint8_t dst_mac_in[6], const uint8_t my_mac[6], uint8_t solicited) { uint32_t plen = (uint32_t)(sizeof(icmpv6_na_t) + sizeof(icmpv6_opt_lladdr_t)); - uintptr_t buf = (uintptr_t)malloc(plen); + uintptr_t buf = (uintptr_t)zalloc(plen ? plen : 1u); if (!buf) return false; icmpv6_na_t* na = (icmpv6_na_t*)buf; @@ -461,14 +459,14 @@ static bool ndp_send_na_on(uint8_t ifindex, const uint8_t dst_ip[16], const uint bool ok = icmpv6_send_on_l2(ifindex, dst_ip, src_ip, dst_mac, (const void*)buf, plen, 255); - free_sized((void*)buf, plen); + release((void*)buf); return ok; } static void ndp_send_ns_on(uint8_t ifindex, const uint8_t target_ip[16], const uint8_t src_ip[16]) { bool dad = ipv6_is_unspecified(src_ip); uint32_t plen = (uint32_t)sizeof(icmpv6_ns_t) + (dad ? 0u : (uint32_t)sizeof(icmpv6_opt_lladdr_t)); - uintptr_t buf = (uintptr_t)malloc(plen); + uintptr_t buf = (uintptr_t)zalloc(plen ? plen : 1u); if (!buf) return; icmpv6_ns_t* ns = (icmpv6_ns_t*)buf; @@ -498,7 +496,7 @@ static void ndp_send_ns_on(uint8_t ifindex, const uint8_t target_ip[16], const u ipv6_multicast_mac(dst_ip, dst_mac); icmpv6_send_on_l2(ifindex, dst_ip, src_ip, dst_mac, (const void*)buf, plen, 255); - free_sized((void*)buf, plen); + release((void*)buf); } static void ndp_send_rs_on(uint8_t ifindex) { @@ -528,7 +526,7 @@ static void ndp_send_rs_on(uint8_t ifindex) { } icmpv6_rs_t; uint32_t plen = (uint32_t)(sizeof(icmpv6_rs_t) + sizeof(icmpv6_opt_lladdr_t)); - uintptr_t buf = (uintptr_t)malloc(plen); + uintptr_t buf = (uintptr_t)zalloc(plen ? plen : 1u); if (!buf) return; icmpv6_rs_t* rs = (icmpv6_rs_t*)buf; @@ -551,7 +549,7 @@ static void ndp_send_rs_on(uint8_t ifindex) { ipv6_multicast_mac(dst_ip, dst_mac); icmpv6_send_on_l2(ifindex, dst_ip, src_ip, dst_mac, (const void*)buf, plen, 255); - free_sized((void*)buf, plen); + release((void*)buf); } static void ndp_send_probe(uint8_t ifindex, ndp_entry_t* e) { @@ -767,17 +765,20 @@ bool ndp_request_dad_on(uint8_t ifindex, const uint8_t ip[16]) { return false; } -void ndp_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[16], const uint8_t src_mac[6], const uint8_t* icmp, uint32_t icmp_len) { - if (!ifindex || !src_ip || !dst_ip || !icmp || icmp_len < sizeof(icmpv6_hdr_t)) return; +void ndp_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[16], const uint8_t src_mac[6], netpkt_t* pkt) { + if (!ifindex || !src_ip || !dst_ip || !pkt || netpkt_len(pkt) < sizeof(icmpv6_hdr_t)) return; - const icmpv6_hdr_t* h = (const icmpv6_hdr_t*)icmp; + uint32_t icmp_len = netpkt_len(pkt); + icmpv6_hdr_t hdr; + if (!netpkt_copyout(pkt, 0, &hdr, sizeof(hdr))) return; + const icmpv6_hdr_t* h = &hdr; if (h->code != 0) return; if (h->type == 135) { if (icmp_len < sizeof(icmpv6_ns_t)) return; icmpv6_ns_t ns; - memcpy(&ns, icmp, sizeof(ns)); + if (!netpkt_copyout(pkt, 0, &ns, sizeof(ns))) return; if (ipv6_is_multicast(ns.target)) return; l2_interface_t* l2 = l2_interface_find_by_index((uint8_t)ifindex); @@ -839,7 +840,7 @@ void ndp_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[ if (icmp_len < sizeof(icmpv6_na_t)) return; icmpv6_na_t na; - memcpy(&na, icmp, sizeof(na)); + if (!netpkt_copyout(pkt, 0, &na, sizeof(na))) return; if (ipv6_is_multicast(na.target)) return; l2_interface_t* l2 = l2_interface_find_by_index((uint8_t)ifindex); @@ -968,7 +969,7 @@ void ndp_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[ if (icmp_len < sizeof(icmpv6_ra_t)) return; icmpv6_ra_t ra; - memcpy(&ra, icmp, sizeof(ra)); + if (!netpkt_copyout(pkt, 0, &ra, sizeof(ra))) return; uint16_t router_lifetime = bswap16(ra.router_lifetime); uint32_t reachable_time = bswap32(ra.reachable_time); @@ -982,7 +983,7 @@ void ndp_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[ if (reachable_time) g_ndp_reachable_time_ms = reachable_time; if (retrans_timer) g_ndp_retrans_timer_ms = retrans_timer; - const uint8_t* opt = icmp + sizeof(ra); + uint32_t opt_off = (uint32_t)sizeof(icmpv6_ra_t); uint32_t opt_len = icmp_len - (uint32_t)sizeof(icmpv6_ra_t); uint8_t idx = (uint8_t)(ifindex - 1); @@ -993,8 +994,10 @@ void ndp_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[ } while (opt_len >= 2) { - uint8_t opt_type = opt[0]; - uint8_t opt_units = opt[1]; + uint8_t opt_head[2]; + if (!netpkt_copyout(pkt, opt_off, opt_head, sizeof(opt_head))) break; + uint8_t opt_type = opt_head[0]; + uint8_t opt_units = opt_head[1]; if (opt_units == 0) break; uint32_t opt_size = (uint32_t)opt_units * 8u; @@ -1002,7 +1005,7 @@ void ndp_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[ if (opt_type == 3&&opt_size >= (uint32_t)sizeof(ndp_opt_prefix_info_t)) { ndp_opt_prefix_info_t pio; - memcpy(&pio, opt, sizeof(pio)); + if (!netpkt_copyout(pkt, opt_off, &pio, sizeof(pio))) break; uint8_t pfx_len = pio.prefix_length; uint8_t autonomous = (pio.flags & 0x40u) ? 1u : 0u; @@ -1015,7 +1018,7 @@ void ndp_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[ if (pfx_len != 0) ndp_on_ra((uint8_t)ifindex, src_ip, router_lifetime, pfx, pfx_len, valid_lft, pref_lft, autonomous, ra.flags); } else if (opt_type == 5 && opt_size >= (uint32_t)sizeof(ndp_opt_mtu_t)) { uint32_t mtu32 = 0; - memcpy(&mtu32, opt + 4, 4); + if (!netpkt_copyout(pkt, opt_off + 4u, &mtu32, sizeof(mtu32))) break; mtu32= bswap32(mtu32); if (mtu32 >= 1280u && mtu32 <= 65535u) { @@ -1038,8 +1041,11 @@ void ndp_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[ uint8_t zero16[16] = {0}; - const uint8_t* a0 = (addr_count >= 1) ? (opt + 8) : zero16; - const uint8_t* a1 = (addr_count >= 2) ? (opt + 24) : zero16; + uint8_t a0[16], a1[16]; + if (addr_count >= 1 && !netpkt_copyout(pkt, opt_off + 8u, a0, sizeof(a0))) break; + else if (addr_count < 1) memcpy(a0, zero16, sizeof(a0)); + if (addr_count >= 2 && !netpkt_copyout(pkt, opt_off + 24u, a1, sizeof(a1))) break; + else if (addr_count < 2) memcpy(a1, zero16, sizeof(a1)); l3_ipv6_interface_t* slot = NULL; @@ -1091,7 +1097,7 @@ void ndp_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[ } } - opt += opt_size; + opt_off += opt_size; opt_len -= opt_size; } diff --git a/kernel/networking/link_layer/ndp.h b/kernel/networking/link_layer/ndp.h index 85b3e5ee..244ab819 100644 --- a/kernel/networking/link_layer/ndp.h +++ b/kernel/networking/link_layer/ndp.h @@ -1,6 +1,7 @@ #pragma once #include "types.h" +#include "networking/netpkt.h" #ifdef __cplusplus extern "C" { @@ -36,7 +37,7 @@ typedef struct { ndp_table_t* ndp_table_create(void); void ndp_table_destroy(ndp_table_t* t); -void ndp_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[16], const uint8_t src_mac[6], const uint8_t* icmp, uint32_t icmp_len); +void ndp_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[16], const uint8_t src_mac[6], netpkt_t* pkt); void ndp_table_put_for_l2(uint8_t ifindex, const uint8_t ip[16], const uint8_t mac[6], uint32_t ttl_ms, bool router); diff --git a/kernel/networking/netpkt.c b/kernel/networking/netpkt.c index d69695ea..4aba9168 100644 --- a/kernel/networking/netpkt.c +++ b/kernel/networking/netpkt.c @@ -546,6 +546,27 @@ uint32_t netpkt_len(const netpkt_t* p) { return p ? p->len : 0; } +bool netpkt_copyout(const netpkt_t* p, uint32_t off, void* dst, uint32_t len) { + if (!p || !dst) return false; + if (off > p->len) return false; + if (len > p->len - off) return false; + if (!len) return true; + + uintptr_t src = netpkt_data(p); + if (!src) return false; + src += (uintptr_t)off; + + if (len <= 16u) { + const uint8_t* s = (const uint8_t*)src; + uint8_t* d = (uint8_t*)dst; + for (uint32_t i = 0; i < len; i++) d[i] = s[i]; + return true; + } + + memcpy(dst, (const void*)src, len); + return true; +} + uint32_t netpkt_headroom(const netpkt_t* p) { return p ? p->head : 0; } diff --git a/kernel/networking/netpkt.h b/kernel/networking/netpkt.h index 1f03c27f..d9b8bfe6 100644 --- a/kernel/networking/netpkt.h +++ b/kernel/networking/netpkt.h @@ -22,6 +22,8 @@ void netpkt_unref(netpkt_t* p); uintptr_t netpkt_data(const netpkt_t* p); uint32_t netpkt_len(const netpkt_t* p); +//NOTE use this only for small reads to avoid unnecessary payload copies +bool netpkt_copyout(const netpkt_t* p, uint32_t off, void* dst, uint32_t len); uint32_t netpkt_headroom(const netpkt_t* p); uint32_t netpkt_tailroom(const netpkt_t* p); diff --git a/kernel/networking/network_dispatch.cpp b/kernel/networking/network_dispatch.cpp index 5118c31d..a405e2d7 100644 --- a/kernel/networking/network_dispatch.cpp +++ b/kernel/networking/network_dispatch.cpp @@ -20,6 +20,10 @@ #define TASK_RX_BATCH_LIMIT 256 #define TASK_TX_BATCH_LIMIT 256 +static void netpkt_free_rx_frame(void* ctx, uintptr_t base, uint32_t alloc_size) { + if (base) kfree((void*)base, alloc_size); +} + NetworkDispatch::NetworkDispatch() { nic_num = 0; @@ -133,12 +137,11 @@ int NetworkDispatch::net_task() if (nics[n].rx.is_empty()) break; sizedptr pkt{0,0}; if (!nics[n].rx.pop(pkt)) break; - netpkt_t* np = netpkt_wrap(pkt.ptr, pkt.size, 0 , pkt.size, NULL, 0); + netpkt_t* np = netpkt_wrap(pkt.ptr, pkt.size, 0 , pkt.size, netpkt_free_rx_frame, 0); if (np) { eth_input(nics[n].ifindex, np); netpkt_unref(np); - } - free_frame(pkt); + } else kfree((void*)pkt.ptr, pkt.size); nics[n].rx_consumed++; processed++; } @@ -243,7 +246,7 @@ uint8_t NetworkDispatch::kind(uint8_t ifindex) const void NetworkDispatch::free_frame(const sizedptr &f) { - if (f.ptr) free_sized((void*)f.ptr, f.size); + if (f.ptr) kfree((void*)f.ptr, f.size); } bool NetworkDispatch::register_all_from_bus() { diff --git a/kernel/networking/port_manager.c b/kernel/networking/port_manager.c index 0af1a7ba..a516b8c6 100644 --- a/kernel/networking/port_manager.c +++ b/kernel/networking/port_manager.c @@ -105,3 +105,25 @@ port_recv_handler_t port_get_handler(const port_manager_t* pm, protocol_t proto, const port_entry_t* e = &pm->tab[proto][port]; return e->used ? e->handler : NULL; } + +uint32_t port_deliver(const port_manager_t* pm, + protocol_t proto, + uint16_t port, + uint8_t ifindex, + ip_version_t ipver, + const void* src_ip_addr, + const void* dst_ip_addr, + netpkt_t* pkt, + uint16_t src_port, + uint16_t dst_port, + bool* handled) +{ + if (handled) *handled = false; + if (!pm || !proto_valid(proto)) return 0; + + port_recv_handler_t handler = port_get_handler(pm, proto, port); + if (!handler) return 0; + + if (handled) *handled = true; + return handler(ifindex, ipver, src_ip_addr, dst_ip_addr, pkt, src_port, dst_port); +} diff --git a/kernel/networking/port_manager.h b/kernel/networking/port_manager.h index e2cde304..e035f20d 100644 --- a/kernel/networking/port_manager.h +++ b/kernel/networking/port_manager.h @@ -1,6 +1,7 @@ #pragma once #include "types.h" #include "net/network_types.h" +#include "networking/netpkt.h" #ifdef __cplusplus extern "C" { @@ -18,8 +19,7 @@ typedef uint32_t (*port_recv_handler_t)( ip_version_t ipver, const void* src_ip_addr, const void* dst_ip_addr, - uintptr_t frame_ptr, - uint32_t frame_len, + netpkt_t* pkt, uint16_t src_port, uint16_t dst_port ); @@ -57,6 +57,17 @@ void port_unbind_all(port_manager_t* pm, uint16_t pid); 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); +uint32_t port_deliver(const port_manager_t* pm, + protocol_t proto, + uint16_t port, + uint8_t ifindex, + ip_version_t ipver, + const void* src_ip_addr, + const void* dst_ip_addr, + netpkt_t* pkt, + uint16_t src_port, + uint16_t dst_port, + bool* handled); #ifdef __cplusplus } diff --git a/kernel/networking/transport_layer/socket_tcp.hpp b/kernel/networking/transport_layer/socket_tcp.hpp index 2c749317..5f2d1162 100755 --- a/kernel/networking/transport_layer/socket_tcp.hpp +++ b/kernel/networking/transport_layer/socket_tcp.hpp @@ -53,8 +53,8 @@ class TCPSocket : public Socket { return true; } - static uint32_t 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){ + static uint32_t dispatch(uint8_t ifindex, ip_version_t ipver, const void* src_ip_addr, const void* dst_ip_addr, netpkt_t* pkt, uint16_t src_port, uint16_t dst_port) { + if (!pkt){ for (TCPSocket* srv = s_list_head; srv; srv = srv->next){ if (srv->role != SOCK_ROLE_SERVER) continue; if (!srv->bound) continue; @@ -68,7 +68,9 @@ class TCPSocket : public Socket { l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(id); if (!is_valid_v4_l3_for_bind(v4)) continue; if (v4->l2->ifindex != ifindex) continue; - if (v4->ip == *(const uint32_t*)dst_ip_addr) { + uint32_t dst_v4 = 0; + memcpy(&dst_v4, dst_ip_addr, 4); + if (v4->ip == dst_v4) { matches_dst = true; break; } @@ -106,7 +108,9 @@ class TCPSocket : public Socket { l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(id); if (!is_valid_v4_l3_for_bind(v4)) continue; if (v4->l2->ifindex != ifindex) continue; - if (v4->ip == *(const uint32_t*)dst_ip_addr) { + uint32_t dst_v4 = 0; + memcpy(&dst_v4, dst_ip_addr, 4); + if (v4->ip == dst_v4) { l3id = id; break; } @@ -191,7 +195,9 @@ class TCPSocket : public Socket { l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(id); if (!is_valid_v4_l3_for_bind(v4)) continue; if (v4->l2->ifindex != ifindex) continue; - if (v4->ip == *(const uint32_t*)dst_ip_addr) { + uint32_t dst_v4 = 0; + memcpy(&dst_v4, dst_ip_addr, 4); + if (v4->ip == dst_v4) { matches_dst = true; break; } @@ -209,18 +215,25 @@ class TCPSocket : public Socket { if (!matches_dst) continue; if (ipver == IP_VER4) { - if (*(const uint32_t*)s->remoteEP.ip != *(const uint32_t*)src_ip_addr) continue; + uint32_t remote_v4 = 0; + uint32_t src_v4 = 0; + memcpy(&remote_v4, s->remoteEP.ip, 4); + memcpy(&src_v4, src_ip_addr, 4); + if (remote_v4 != src_v4) continue; } else { if (memcmp(s->remoteEP.ip, src_ip_addr, 16) != 0) continue; } - return s->on_receive(frame_ptr, frame_len); + return s->on_receive(pkt); } return 0; } - uint32_t on_receive(uintptr_t ptr, uint32_t len) { + uint32_t on_receive(netpkt_t* pkt) { + if (!pkt) return 0; + uintptr_t ptr = netpkt_data(pkt); + uint32_t len = netpkt_len(pkt); if(!ptr || !len) return 0; uint64_t limit = ring.capacity(); diff --git a/kernel/networking/transport_layer/socket_udp.hpp b/kernel/networking/transport_layer/socket_udp.hpp index da1aede9..6a45eba1 100644 --- a/kernel/networking/transport_layer/socket_udp.hpp +++ b/kernel/networking/transport_layer/socket_udp.hpp @@ -117,53 +117,48 @@ class UDPSocket : public Socket { return false; } - static uint32_t 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) { - UDPSocket* first = nullptr; - uint32_t ret = frame_len; + static uint32_t dispatch(uint8_t ifindex, ip_version_t ipver, const void* src_ip_addr, const void* dst_ip_addr, netpkt_t* pkt, uint16_t src_port, uint16_t dst_port) { + if (!pkt) return 0; + uint32_t ret = netpkt_len(pkt); irq_flags_t irq = irq_save_disable(); for (UDPSocket* s = s_list_head; s; s = s->next) { if (!socket_matches_dst(s, ifindex, ipver, dst_ip_addr, dst_port)) continue; - if (!first) { - first = s; - continue; - } - - uintptr_t copy = (uintptr_t)malloc(frame_len); - if (!copy) continue; - - memcpy((void*)copy, (const void*)frame_ptr, frame_len); - s->on_receive(ipver, src_ip_addr, src_port, copy, frame_len); + s->on_receive(ipver, src_ip_addr, src_port, pkt); } - if (first) first->on_receive(ipver, src_ip_addr, src_port, frame_ptr, frame_len); - else if (frame_ptr && frame_len) free_sized((void*)frame_ptr, frame_len); irq_restore(irq); return ret; } - void on_receive(ip_version_t ver, const void* src_ip_addr, uint16_t src_port, uintptr_t ptr, uint32_t len) { + void on_receive(ip_version_t ver, const void* src_ip_addr, uint16_t src_port, netpkt_t* pkt) { + if (!pkt) return; + uintptr_t ptr = netpkt_data(pkt); + uint32_t len = netpkt_len(pkt); uint32_t limit = 0xFFFFFFFFu; if ((extraOpts.flags & SOCK_OPT_BUF_SIZE) && extraOpts.buf_size) limit = extraOpts.buf_size; - if (len > limit) { - if (ptr && len) free_sized((void*)ptr, len); - return; + if (len > limit) return; + uintptr_t copy = 0; + if (len) { + copy = (uintptr_t)zalloc(len); + if (!copy) return; + memcpy((void*)copy, (const void*)ptr, len); } while (rx_bytes + len > limit && r_head != r_tail) { rx_bytes -= ring[r_head].size; - free_sized((void*)ring[r_head].ptr, ring[r_head].size); + release((void*)ring[r_head].ptr); r_head = (r_head + 1) % UDP_RING_CAP; } int nexti = (r_tail + 1) % UDP_RING_CAP; if (nexti == r_head) { rx_bytes -= ring[r_head].size; - free_sized((void*)ring[r_head].ptr, ring[r_head].size); + release((void*)ring[r_head].ptr); r_head = (r_head + 1) % UDP_RING_CAP; } - ring[r_tail].ptr = ptr; + ring[r_tail].ptr = copy; ring[r_tail].size = len; rx_bytes += len; @@ -171,8 +166,7 @@ class UDPSocket : public Socket { 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); + memcpy(src_eps[r_tail].ip, src_ip_addr, 4); } else if (ver == IP_VER6) { memcpy(src_eps[r_tail].ip, src_ip_addr, 16); } @@ -751,7 +745,7 @@ class UDPSocket : public Socket { memcpy(buf, (void*)p.ptr, tocpy); if (src) *src = se; - free_sized((void*)p.ptr, p.size); + release((void*)p.ptr); remoteEP = se; return tocpy; } @@ -766,7 +760,7 @@ class UDPSocket : public Socket { netlog_socket_event(&extraOpts, &ev); while (r_head != r_tail) { rx_bytes -= ring[r_head].size; - free_sized((void*)ring[r_head].ptr, ring[r_head].size); + release((void*)ring[r_head].ptr); r_head = (r_head + 1) % UDP_RING_CAP; } return Socket::close(); diff --git a/kernel/networking/transport_layer/tcp.h b/kernel/networking/transport_layer/tcp.h index 3498771f..57be40af 100644 --- a/kernel/networking/transport_layer/tcp.h +++ b/kernel/networking/transport_layer/tcp.h @@ -3,6 +3,7 @@ #include "networking/port_manager.h" #include "networking/internet_layer/ipv4.h" #include "networking/link_layer/eth.h" +#include "networking/netpkt.h" #include "std/memory.h" #include "net/network_types.h" #include "net/socket_types.h" @@ -98,7 +99,7 @@ tcp_result_t tcp_flow_close(tcp_data *flow_ctx); void tcp_flow_window_update(tcp_data *flow_ctx); void tcp_flow_on_app_read(tcp_data *flow_ctx, uint32_t bytes_read); -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); +void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_addr, uint8_t l3_id, netpkt_t* pkt); void tcp_tick_all(uint32_t elapsed_ms); int tcp_daemon_entry(int argc, char *argv[]); diff --git a/kernel/networking/transport_layer/tcp/tcp_core.c b/kernel/networking/transport_layer/tcp/tcp_core.c index 8927614c..3b710a7f 100644 --- a/kernel/networking/transport_layer/tcp/tcp_core.c +++ b/kernel/networking/transport_layer/tcp/tcp_core.c @@ -65,7 +65,7 @@ static void clear_txq(tcp_flow_t *f){ for (int i = 0; i < TCP_MAX_TX_SEGS; i++){ tcp_tx_seg_t *s = &f->txq[i]; - if (s->used && s->buf && s->len) free_sized((void *)s->buf, s->len); + if (s->used && s->buf && s->len) release((void *)s->buf); s->used = 0; s->syn = 0; @@ -83,8 +83,7 @@ static void clear_txq(tcp_flow_t *f){ static void clear_reass(tcp_flow_t *f){ for (int i = 0; i < TCP_REASS_MAX_SEGS; i++){ if (f->reass[i].buf && f->reass[i].end > f->reass[i].seq){ - uint32_t l = f->reass[i].end - f->reass[i].seq; - free_sized((void *)f->reass[i].buf, l); + release((void *)f->reass[i].buf); } f->reass[i].seq = 0; @@ -100,9 +99,8 @@ tcp_flow_t *tcp_alloc_flow(void){ for (int i = 0; i < MAX_TCP_FLOWS; i++){ if (tcp_flows[i]) continue; - tcp_flow_t *f = (tcp_flow_t *)malloc(sizeof(tcp_flow_t)); + tcp_flow_t *f = (tcp_flow_t *)zalloc(sizeof(tcp_flow_t)); if (!f) return NULL; - memset(f, 0, sizeof(tcp_flow_t)); tcp_flows[i] = f; f->rto = TCP_INIT_RTO; @@ -144,7 +142,7 @@ void tcp_free_flow(int idx) { f->cwnd = f->mss; f->ssthresh = TCP_RECV_WINDOW; - free_sized(f, sizeof(*f)); + release(f); tcp_flows[idx] = NULL; } diff --git a/kernel/networking/transport_layer/tcp/tcp_rx.c b/kernel/networking/transport_layer/tcp/tcp_rx.c index fba40956..fd9446cd 100644 --- a/kernel/networking/transport_layer/tcp/tcp_rx.c +++ b/kernel/networking/transport_layer/tcp/tcp_rx.c @@ -22,7 +22,7 @@ static void tcp_reass_evict_tail(tcp_flow_t *flow, uint32_t need) { tcp_reass_seg_t *r = &flow->reass[idx]; uint32_t olen = r->end - r->seq; - if (r->buf && olen) free_sized((void *)r->buf, olen); + if (r->buf && olen) release((void *)r->buf); if (flow->rcv_buf_used >= olen) flow->rcv_buf_used -= olen; else flow->rcv_buf_used = 0; @@ -34,7 +34,7 @@ static void tcp_reass_evict_tail(tcp_flow_t *flow, uint32_t need) { } } -static void tcp_reass_insert(tcp_flow_t *flow, uint32_t seq, const uint8_t *data, uint32_t len) { +static void tcp_reass_insert(tcp_flow_t *flow, uint32_t seq, const netpkt_t *pkt, uint32_t data, uint32_t len) { if (!len) return; if (flow->reass_count >= TCP_REASS_MAX_SEGS) return; if (seq < flow->rcv_nxt) { @@ -71,7 +71,7 @@ static void tcp_reass_insert(tcp_flow_t *flow, uint32_t seq, const uint8_t *data if (start <= rs && end >= re) { uint32_t olen = re - rs; - if (r->buf && olen) free_sized((void *)r->buf, olen); + if (r->buf && olen) release((void *)r->buf); flow->reass[i] = flow->reass[flow->reass_count - 1]; flow->reass[flow->reass_count - 1].seq = 0; @@ -108,11 +108,14 @@ static void tcp_reass_insert(tcp_flow_t *flow, uint32_t seq, const uint8_t *data if (flow->rcv_buf_used + len > flow->rcv_wnd_max) tcp_reass_evict_tail(flow, len); if (flow->rcv_buf_used + len > flow->rcv_wnd_max) return; - uintptr_t buf = (uintptr_t)malloc(len); + uintptr_t buf = (uintptr_t)zalloc(len ? len : 1u); if (!buf) return; uint32_t offset = start - orig_seq; - memcpy((void *)buf, data + offset, len); + if (!netpkt_copyout(pkt, data + offset, (void *)buf, len)) { + release((void *)buf); + return; + } int pos = flow->reass_count; while (pos > 0 && flow->reass[pos - 1].seq > start){ @@ -130,7 +133,7 @@ static void tcp_reass_insert(tcp_flow_t *flow, uint32_t seq, const uint8_t *data (void)tcp_calc_adv_wnd_field(flow, 1); } -static void tcp_reass_drain_inseq(tcp_flow_t *flow, port_manager_t *pm, uint8_t ifx, ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_addr, uint16_t src_port, uint16_t dst_port) { +static void tcp_reass_drain_inseq(tcp_flow_t *flow, port_recv_handler_t handler, uint8_t ifx, ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_addr, uint16_t src_port, uint16_t dst_port) { uint32_t rcv_nxt = flow->rcv_nxt; for(;;){ @@ -156,16 +159,25 @@ static void tcp_reass_drain_inseq(tcp_flow_t *flow, port_manager_t *pm, uint8_t continue; } - if (pm) { - port_recv_handler_t h = port_get_handler(pm, PROTO_TCP, dst_port); + if (handler) { + bool handled = true; uint32_t accepted = seg_len; - if (h) accepted = h(ifx, ipver, src_ip_addr, dst_ip_addr, seg->buf, seg_len, src_port, dst_port); + netpkt_t* segpkt = netpkt_alloc(seg_len, 0, 0); + if (segpkt) { + void* segmem = netpkt_put(segpkt, seg_len); + if (segmem) { + memcpy(segmem, (const void*)seg->buf, seg_len); + accepted = handler(ifx, ipver, src_ip_addr, dst_ip_addr, segpkt, src_port, dst_port); + } else accepted = 0; + netpkt_unref(segpkt); + } else accepted = 0; + if (!handled) accepted = seg_len; if (accepted > seg_len) accepted = seg_len; if (accepted == 0) { if (flow->state == TCP_FIN_WAIT_1 || flow->state == TCP_FIN_WAIT_2 || flow->state == TCP_CLOSING || flow->state == TCP_LAST_ACK || flow->state == TCP_TIME_WAIT) { - if (seg->buf) free_sized((void *)seg->buf, seg_len); + if (seg->buf) release((void *)seg->buf); if (flow->rcv_buf_used >= seg_len) flow->rcv_buf_used -= seg_len; else flow->rcv_buf_used = 0; @@ -185,11 +197,11 @@ static void tcp_reass_drain_inseq(tcp_flow_t *flow, port_manager_t *pm, uint8_t if (accepted < seg_len) { uint32_t rem = seg_len - accepted; - uintptr_t newbuf = (uintptr_t)malloc(rem); + uintptr_t newbuf = (uintptr_t)zalloc(rem ? rem : 1u); if (!newbuf) break; memcpy((void *)newbuf, ((const uint8_t *)seg->buf) + accepted, rem); - if (seg->buf) free_sized((void *)seg->buf, seg_len); + if (seg->buf) release((void *)seg->buf); seg->buf = newbuf; seg->seq += accepted; @@ -209,7 +221,7 @@ static void tcp_reass_drain_inseq(tcp_flow_t *flow, port_manager_t *pm, uint8_t rcv_nxt += seg_len; - if (seg->buf) free_sized((void *)seg->buf, seg_len); + if (seg->buf) release((void *)seg->buf); if (flow->rcv_buf_used >= seg_len) flow->rcv_buf_used -= seg_len; else flow->rcv_buf_used = 0; @@ -325,20 +337,35 @@ static void tcp_cc_on_dupack(tcp_flow_t *f) { } } -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(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_addr, uint8_t l3_id, netpkt_t* pkt) { + if (!pkt) return; + const uint8_t* segment = (const uint8_t*)netpkt_data(pkt); + uint32_t len = netpkt_len(pkt); + if (len < sizeof(tcp_hdr_t)) { + netpkt_unref(pkt); + return; + } tcp_hdr_t hdr; - memcpy(&hdr, (const void*)ptr, sizeof(hdr)); + if (!netpkt_copyout(pkt, 0, &hdr, sizeof(hdr))) { + netpkt_unref(pkt); + return; + } if (ipver == IP_VER4) { uint32_t src_ip = 0; uint32_t dst_ip = 0; memcpy(&src_ip, src_ip_addr, sizeof(src_ip)); memcpy(&dst_ip, dst_ip_addr, sizeof(dst_ip)); - if (tcp_checksum_ipv4((const void*)ptr, (uint16_t)len, src_ip, dst_ip) != 0) return; + if (tcp_checksum_ipv4((const void*)segment, (uint16_t)len, src_ip, dst_ip) != 0) { + netpkt_unref(pkt); + return; + } } else { - if (tcp_checksum_ipv6((const void*)ptr, (uint16_t)len, (const uint8_t *)src_ip_addr, (const uint8_t *)dst_ip_addr) != 0) return; + if (tcp_checksum_ipv6((const void*)segment, (uint16_t)len, (const uint8_t *)src_ip_addr, (const uint8_t *)dst_ip_addr) != 0) { + netpkt_unref(pkt); + return; + } } uint16_t src_port = bswap16(hdr.src_port); @@ -349,7 +376,10 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a uint16_t window = bswap16(hdr.window); uint8_t hdr_len = (uint8_t)((hdr.data_offset_reserved >> 4) * 4); - if (len < hdr_len) return; + if (len < hdr_len) { + netpkt_unref(pkt); + return; + } uint32_t data_len = len - hdr_len; @@ -363,17 +393,28 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a if (ipver == IP_VER4) { l3_ipv4_interface_t *v4 = l3_ipv4_find_by_id(l3_id); - if (!v4 || !v4->l2) return; + if (!v4 || !v4->l2) { + netpkt_unref(pkt); + return; + } pm = ifmgr_pm_v4(l3_id); ifx = v4->l2->ifindex; } else { l3_ipv6_interface_t *v6 = l3_ipv6_find_by_id(l3_id); - if (!v6 || !v6->l2) return; + if (!v6 || !v6->l2) { + netpkt_unref(pkt); + return; + } pm = ifmgr_pm_v6(l3_id); ifx = v6->l2->ifindex; } - if (!pm) return; + if (!pm) { + netpkt_unref(pkt); + return; + } + + port_recv_handler_t port_handler = port_get_handler(pm, PROTO_TCP, dst_port); if (!flow){ int listen_idx = find_flow(dst_port, ipver, dst_ip_addr, NULL, 0); @@ -395,11 +436,17 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a syn_total++; if (f->local_port == dst_port && f->l3_id == l3_id) syn_port++; } - if (syn_total >= (MAX_TCP_FLOWS / 4) || syn_port >= 32) return; + if (syn_total >= (MAX_TCP_FLOWS / 4) || syn_port >= 32) { + netpkt_unref(pkt); + return; + } tcp_flow_t *lf = tcp_flows[listen_idx]; tcp_flow_t *nf = tcp_alloc_flow(); - if (!nf) return; + if (!nf) { + netpkt_unref(pkt); + return; + } flow = nf; for (int k = 0; k < MAX_TCP_FLOWS; k++) { @@ -426,7 +473,14 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a flow->retries = TCP_SYN_RETRIES; tcp_parsed_opts_t pop; - tcp_parse_options((const uint8_t *)(ptr + sizeof(tcp_hdr_t)), (uint32_t)(hdr_len > sizeof(tcp_hdr_t) ? hdr_len - sizeof(tcp_hdr_t) : 0), &pop); + uint8_t rx_opts_buf[40]; + uint32_t rx_opts_len = (uint32_t)(hdr_len > sizeof(tcp_hdr_t) ? hdr_len - sizeof(tcp_hdr_t) : 0); + if (rx_opts_len && (!netpkt_copyout(pkt, sizeof(tcp_hdr_t), rx_opts_buf, rx_opts_len))) { + tcp_free_flow(idx); + netpkt_unref(pkt); + return; + } + tcp_parse_options(rx_opts_len ? rx_opts_buf : NULL, rx_opts_len, &pop); flow->ws_send = lf->ws_send; flow->ws_recv = 0; @@ -522,6 +576,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a } tcp_daemon_kick(); + netpkt_unref(pkt); return; } @@ -538,11 +593,15 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a } } + netpkt_unref(pkt); return; } if (flow->state == TCP_TIME_WAIT){ - if (flags & (1u << RST_F)) return; + if (flags & (1u << RST_F)) { + netpkt_unref(pkt); + return; + } uint32_t seg_len = data_len; @@ -556,6 +615,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a tcp_send_ack_now(flow); } + netpkt_unref(pkt); return; } uint32_t new_wnd = window; @@ -590,7 +650,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a if (s_end <= ack){ if (s->rtt_sample && s->retransmit_cnt == 0) tcp_rtt_update(flow, s->timer_ms); - if (s->buf && s->len) free_sized((void *)s->buf, s->len); + if (s->buf && s->len) release((void *)s->buf); s->used = 0; s->buf = 0; @@ -606,6 +666,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a tcp_daemon_kick(); } else if ((flow->state == TCP_LAST_ACK || flow->state == TCP_CLOSING) && ack >= flow->ctx.expected_ack){ tcp_free_flow(idx); + netpkt_unref(pkt); return; } } else if (ack == flow->snd_una && data_len == 0 && !fin){ @@ -630,7 +691,13 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a flow->ctx.flags = 0; tcp_parsed_opts_t pop; - tcp_parse_options((const uint8_t *)(ptr + sizeof(tcp_hdr_t)), (uint32_t)(hdr_len > sizeof(tcp_hdr_t) ? hdr_len - sizeof(tcp_hdr_t) : 0), &pop); + uint8_t ack_opts_buf[40]; + uint32_t ack_opts_len = (uint32_t)(hdr_len > sizeof(tcp_hdr_t) ? hdr_len - sizeof(tcp_hdr_t) : 0); + if (ack_opts_len && (!netpkt_copyout(pkt, sizeof(tcp_hdr_t), ack_opts_buf, ack_opts_len))) { + netpkt_unref(pkt); + return; + } + tcp_parse_options(ack_opts_len ? ack_opts_buf : NULL, ack_opts_len, &pop); flow->ws_recv = pop.has_wscale ? pop.wscale : 0; if (flow->ws_recv > 14) flow->ws_recv = 14; @@ -686,6 +753,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a flow->state = TCP_STATE_CLOSED; } + netpkt_unref(pkt); return; case TCP_SYN_RECEIVED: @@ -698,14 +766,14 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a flow->delayed_ack_timer_ms = 0; flow->ctx.ack_received = ack; - port_recv_handler_t h = port_get_handler(pm, PROTO_TCP, dst_port); - if (h) (void)h(ifx, ipver, src_ip_addr, dst_ip_addr, 0, 0, src_port, dst_port); + if (port_handler) port_handler(ifx, ipver, src_ip_addr, dst_ip_addr, 0, src_port, dst_port); tcp_daemon_kick(); } else if (flags & (1u << RST_F)){ tcp_free_flow(idx); } + netpkt_unref(pkt); return; default: @@ -714,6 +782,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a if (flags & (1u << RST_F)) { tcp_free_flow(idx); + netpkt_unref(pkt); return; } @@ -738,7 +807,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a if (fin_seq < rcv_nxt || fin_seq >= wnd_end) fin_in = 0; } - const uint8_t *payload = (const uint8_t *)(ptr + hdr_len); + uint32_t payload = hdr_len; if (seg_seq < rcv_nxt) { uint32_t d = rcv_nxt - seg_seq; @@ -765,16 +834,20 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a if (data_len){ uint32_t free_space = (flow->rcv_buf_used < flow->rcv_wnd_max) ? (flow->rcv_wnd_max - flow->rcv_buf_used) : 0; - port_recv_handler_t h = port_get_handler(pm, PROTO_TCP, dst_port); - uint32_t offer = data_len; if (offer > free_space) offer = free_space; uint32_t accepted = 0; - if (offer && h) accepted = h(ifx, ipver, src_ip_addr, dst_ip_addr, (uintptr_t)payload, offer, src_port, dst_port); + if (offer && port_handler) { + netpkt_t* datapkt = netpkt_view(pkt, payload, offer); + if (datapkt) { + accepted = port_handler(ifx, ipver, src_ip_addr, dst_ip_addr, datapkt, src_port, dst_port); + netpkt_unref(datapkt); + } else port_handler = 0; + } if (accepted > offer) accepted = offer; - if (h && accepted == 0 && data_len) { + if (port_handler && accepted == 0 && data_len) { (void)tcp_calc_adv_wnd_field(flow, 1); need_ack = 1; ack_immediate = 1; @@ -816,7 +889,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a } } - tcp_reass_drain_inseq(flow, pm, ifx, ipver, src_ip_addr, dst_ip_addr, src_port, dst_port); + tcp_reass_drain_inseq(flow, port_handler, ifx, ipver, src_ip_addr, dst_ip_addr, src_port, dst_port); if (flow->fin_pending && flow->fin_seq == flow->rcv_nxt){ flow->fin_pending = 0; @@ -841,7 +914,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a if (!ack_immediate && data_len) ack_defer = 1; need_ack = 1; } else { - if (!(flow->state == TCP_FIN_WAIT_1 || flow->state == TCP_FIN_WAIT_2 || flow->state == TCP_CLOSING || flow->state == TCP_LAST_ACK || flow->state == TCP_TIME_WAIT) && data_len) tcp_reass_insert(flow, seg_seq, payload, data_len); + if (!(flow->state == TCP_FIN_WAIT_1 || flow->state == TCP_FIN_WAIT_2 || flow->state == TCP_CLOSING || flow->state == TCP_LAST_ACK || flow->state == TCP_TIME_WAIT) && data_len) tcp_reass_insert(flow, seg_seq, pkt, payload, data_len); if (fin_in){ flow->fin_pending = 1; @@ -912,7 +985,7 @@ void tcp_flow_on_app_read(tcp_data *flow_ctx, uint32_t bytes_read){ } if (pm) { - tcp_reass_drain_inseq(flow, pm, ifx, flow->local.ver, flow->remote.ip, flow->local.ip, flow->remote.port, flow->local_port); + tcp_reass_drain_inseq(flow, port_get_handler(pm, PROTO_TCP, flow->local_port), ifx, flow->local.ver, flow->remote.ip, flow->local.ip, flow->remote.port, flow->local_port); } if (flow->state != TCP_STATE_CLOSED && flow->state != TCP_TIME_WAIT) { diff --git a/kernel/networking/transport_layer/tcp/tcp_tx.c b/kernel/networking/transport_layer/tcp/tcp_tx.c index 7f453581..fbb55d0b 100644 --- a/kernel/networking/transport_layer/tcp/tcp_tx.c +++ b/kernel/networking/transport_layer/tcp/tcp_tx.c @@ -227,7 +227,7 @@ tcp_result_t tcp_flow_send(tcp_data *flow_ctx){ uintptr_t buf = 0; if (seg_len) { - buf = (uintptr_t)malloc(seg_len); + buf = (uintptr_t)zalloc(seg_len); if (!buf) { seg->used = 0; break; } memcpy((void *)buf, payload_ptr + sent_bytes, seg_len); } diff --git a/kernel/networking/transport_layer/udp.c b/kernel/networking/transport_layer/udp.c index 9955a84c..71ab7fc6 100644 --- a/kernel/networking/transport_layer/udp.c +++ b/kernel/networking/transport_layer/udp.c @@ -8,12 +8,6 @@ #include "syscalls/syscalls.h" #include "networking/internet_layer/ipv4_utils.h" -static inline uint32_t v4_u32_from_arr(const uint8_t ip16[16]) { - uint32_t v = 0; - memcpy(&v, ip16, 4); - return v; -} - 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.src_port = bswap16(src->port); @@ -26,8 +20,10 @@ size_t create_udp_segment(uintptr_t buf, const net_l4_endpoint *src, const net_l 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); + uint32_t s = 0; + uint32_t d = 0; + memcpy(&s, src->ip, 4); + memcpy(&d, dst->ip, 4); udp.checksum = bswap16(checksum16_pipv4(s, d, 0x11, (const uint8_t *)buf, full_len)); } else if (src->ver == IP_VER6) { udp.checksum = bswap16(checksum16_pipv6(src->ip, dst->ip, 17, (const uint8_t *)buf, full_len)); @@ -51,7 +47,8 @@ void udp_send_segment(const net_l4_endpoint *src, const net_l4_endpoint *dst, si size_t written = create_udp_segment((uintptr_t)buf, src, dst, payload); if (src->ver == IP_VER4) { - uint32_t dst_ip = v4_u32_from_arr(dst->ip); + uint32_t dst_ip = 0; + memcpy(&dst_ip, dst->ip, 4); (void)netpkt_trim(pkt, (uint32_t)written); ipv4_send_packet(dst_ip, 0x11, pkt, (const ipv4_tx_opts_t*)tx_opts, ttl, dontfrag); } else if (src->ver == IP_VER6) { @@ -62,28 +59,28 @@ void udp_send_segment(const net_l4_endpoint *src, const net_l4_endpoint *dst, si } } -sizedptr udp_strip_header(uintptr_t ptr, uint32_t len) { - if (len < sizeof(udp_hdr_t)) { - return (sizedptr){ 0, 0 }; - } - udp_hdr_t hdr; - memcpy(&hdr, (const void*)ptr, sizeof(hdr)); - uint16_t total = bswap16(hdr.length); - if (total < sizeof(udp_hdr_t) || total > len) { - return (sizedptr){ 0, 0 }; - } - return (sizedptr){ - .ptr = ptr + sizeof(udp_hdr_t), - .size = total - sizeof(udp_hdr_t) - }; +bool udp_strip_header(const netpkt_t* pkt, udp_hdr_t* hdr, uint32_t* payload_off, uint32_t* payload_len) { + if (!pkt || !hdr || !payload_off || !payload_len) return false; + uint32_t len = netpkt_len(pkt); + if (len < sizeof(udp_hdr_t)) return false; + if (!netpkt_copyout(pkt, 0, hdr, sizeof(*hdr))) return false; + + uint16_t total = bswap16(hdr->length); + if (total < sizeof(udp_hdr_t) || total > len) return false; + *payload_off = (uint32_t)sizeof(udp_hdr_t); + *payload_len = total - (uint32_t)sizeof(udp_hdr_t); + return true; } -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; - +void udp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_addr, uint8_t l3_id, netpkt_t* pkt) { + if (!pkt) return; udp_hdr_t hdr; - memcpy(&hdr, (const void*)ptr, sizeof(hdr)); + uint32_t payload_off = 0; + uint32_t payload_len = 0; + if (!udp_strip_header(pkt, &hdr, &payload_off, &payload_len)) { + netpkt_unref(pkt); + return; + } if (hdr.checksum) { if (ipver == IP_VER4) { @@ -91,9 +88,15 @@ void udp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a uint32_t dst_ip = 0; memcpy(&src_ip, src_ip_addr, sizeof(src_ip)); memcpy(&dst_ip, dst_ip_addr, sizeof(dst_ip)); - if (checksum16_pipv4(src_ip, dst_ip, 0x11, (const uint8_t*)ptr, (uint16_t)(pl.size + sizeof(hdr))) != 0) return; + if (checksum16_pipv4(src_ip, dst_ip, 0x11, (const uint8_t*)netpkt_data(pkt), (uint16_t)(payload_len + sizeof(hdr))) != 0) { + netpkt_unref(pkt); + return; + } } else if (ipver == IP_VER6) { - if (checksum16_pipv6((const uint8_t*)src_ip_addr, (const uint8_t*)dst_ip_addr, 0x11, (const uint8_t*)ptr, (uint32_t)(pl.size + sizeof(hdr))) != 0) return; + if (checksum16_pipv6((const uint8_t*)src_ip_addr, (const uint8_t*)dst_ip_addr, 0x11, (const uint8_t*)netpkt_data(pkt), (uint32_t)(payload_len + sizeof(hdr))) != 0) { + netpkt_unref(pkt); + return; + } } } @@ -112,20 +115,25 @@ void udp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a if (v6) pm = ifmgr_pm_v6(l3_id); } - if (!pm) return; + if (!pm) { + netpkt_unref(pkt); + return; + } - port_recv_handler_t handler = port_get_handler(pm, PROTO_UDP, dst_port); - if (handler) { - uintptr_t copy = (uintptr_t)malloc(pl.size); - if (!copy) return; - memcpy((void*)copy, (const void*)pl.ptr, pl.size); + netpkt_t* plpkt = netpkt_view(pkt, payload_off, payload_len); + if (!plpkt) { + netpkt_unref(pkt); + return; + } - uint8_t ifx = 0; - if (v4 && v4->l2) ifx = v4->l2->ifindex; - else if (v6 && v6->l2) ifx = v6->l2->ifindex; + uint8_t ifx = 0; + if (v4 && v4->l2) ifx = v4->l2->ifindex; + else if (v6 && v6->l2) ifx = v6->l2->ifindex; - handler(ifx, ipver, src_ip_addr, dst_ip_addr, copy, pl.size, src_port, dst_port); - } + port_recv_handler_t handler = port_get_handler(pm, PROTO_UDP, dst_port); + if (handler) handler(ifx, ipver, src_ip_addr, dst_ip_addr, plpkt, src_port, dst_port); + netpkt_unref(plpkt); + netpkt_unref(pkt); } static inline port_manager_t* pm_for_l3(uint8_t l3_id) { diff --git a/kernel/networking/transport_layer/udp.h b/kernel/networking/transport_layer/udp.h index 074012bf..58bbaae3 100644 --- a/kernel/networking/transport_layer/udp.h +++ b/kernel/networking/transport_layer/udp.h @@ -3,6 +3,7 @@ #include "net/network_types.h" #include "networking/port_manager.h" #include "networking/internet_layer/ipv4.h" +#include "networking/netpkt.h" #ifdef __cplusplus extern "C" { @@ -26,8 +27,7 @@ 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); + netpkt_t* pkt); 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); From 854ad12fc7b6a6bb3c11bf137416b9ba0821f9fd Mon Sep 17 00:00:00 2001 From: CodeAnarchist Date: Tue, 21 Apr 2026 22:05:12 +0200 Subject: [PATCH 04/39] [TCP] track passive SYN-ACK and reject orphan accepts --- .../networking/transport_layer/socket_tcp.hpp | 7 +- .../networking/transport_layer/tcp/tcp_core.c | 28 +------ .../transport_layer/tcp/tcp_internal.h | 2 + .../networking/transport_layer/tcp/tcp_rx.c | 73 ++++++++++++------- .../networking/transport_layer/tcp/tcp_tx.c | 7 +- 5 files changed, 61 insertions(+), 56 deletions(-) diff --git a/kernel/networking/transport_layer/socket_tcp.hpp b/kernel/networking/transport_layer/socket_tcp.hpp index 5f2d1162..f7108254 100755 --- a/kernel/networking/transport_layer/socket_tcp.hpp +++ b/kernel/networking/transport_layer/socket_tcp.hpp @@ -89,9 +89,9 @@ class TCPSocket : public Socket { if (srv->backlogLen >= srv->backlogCap) break; TCPSocket* child = new TCPSocket(SOCK_ROLE_CLIENT, srv->pid, &srv->extraOpts); + if (!child) break; child->localPort = dst_port; - child->connected = true; child->remoteEP.ver = ipver; memset(child->remoteEP.ip, 0, 16); @@ -168,15 +168,14 @@ class TCPSocket : public Socket { child->flow = tcp_get_ctx(dst_port, ipver, dst_ip_addr, child->remoteEP.ip, src_port); if (!child->flow){ - child->close(); delete child; break; } - child->insert_in_list(); + child->connected = true; srv->pending[srv->backlogLen++] = child; - break; + return 1; } return 0; } diff --git a/kernel/networking/transport_layer/tcp/tcp_core.c b/kernel/networking/transport_layer/tcp/tcp_core.c index 3b710a7f..07994e34 100644 --- a/kernel/networking/transport_layer/tcp/tcp_core.c +++ b/kernel/networking/transport_layer/tcp/tcp_core.c @@ -72,6 +72,8 @@ static void clear_txq(tcp_flow_t *f){ s->fin = 0; s->rtt_sample = 0; s->retransmit_cnt = 0; + s->opts_len = 0; + memset(s->opts, 0, sizeof(s->opts)); s->seq = 0; s->len = 0; s->buf = 0; @@ -489,31 +491,9 @@ bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, seg->timer_ms = 0; seg->timeout_ms = flow->rto ? flow->rto : TCP_INIT_RTO; - tcp_hdr_t syn_hdr; + seg->opts_len = tcp_build_syn_options(seg->opts, (uint16_t)flow->mss, flow->rcv_wnd_max > 65535u ? flow->ws_send : 0xffu, flow->sack_ok); - syn_hdr.src_port = bswap16(local_port); - syn_hdr.dst_port = bswap16(dst->port); - syn_hdr.sequence = bswap32(flow->snd_nxt); - syn_hdr.ack = bswap32(0); - syn_hdr.flags = (uint8_t)(1u << SYN_F); - syn_hdr.window = flow->ctx.window; - syn_hdr.urgent_ptr = 0; - - uint8_t syn_opts[40]; - uint8_t syn_opts_len = tcp_build_syn_options(syn_opts, (uint16_t)flow->mss, flow->rcv_wnd_max > 65535u ? flow->ws_send : 0xffu, flow->sack_ok); - - if (dst->ver == IP_VER4){ - ipv4_tx_opts_t tx; - - tcp_build_tx_opts_from_l3(l3_id, &tx); - tcp_send_segment(IP_VER4, flow->local.ip, flow->remote.ip, &syn_hdr, syn_opts, syn_opts_len, NULL, 0, (const ip_tx_opts_t *)&tx, flow->ip_ttl, flow->ip_dontfrag); - } else{ - ipv6_tx_opts_t tx; - - tx.scope = IP_TX_BOUND_L3; - tx.index = l3_id; - tcp_send_segment(IP_VER6, flow->local.ip, flow->remote.ip, &syn_hdr, syn_opts, syn_opts_len, NULL, 0, (const ip_tx_opts_t *)&tx, flow->ip_ttl, flow->ip_dontfrag); - } + tcp_send_from_seg(flow, seg); flow->snd_nxt += 1; flow->ctx.sequence = flow->snd_nxt; diff --git a/kernel/networking/transport_layer/tcp/tcp_internal.h b/kernel/networking/transport_layer/tcp/tcp_internal.h index b11bb212..c40464d7 100644 --- a/kernel/networking/transport_layer/tcp/tcp_internal.h +++ b/kernel/networking/transport_layer/tcp/tcp_internal.h @@ -32,6 +32,8 @@ typedef struct { uint8_t fin; uint8_t rtt_sample; uint8_t retransmit_cnt; + uint8_t opts_len; + uint8_t opts[40]; uint32_t seq; uint64_t len; uintptr_t buf; diff --git a/kernel/networking/transport_layer/tcp/tcp_rx.c b/kernel/networking/transport_layer/tcp/tcp_rx.c index fd9446cd..16c46292 100644 --- a/kernel/networking/transport_layer/tcp/tcp_rx.c +++ b/kernel/networking/transport_layer/tcp/tcp_rx.c @@ -533,7 +533,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a flow->rcv_wnd_max = lf->rcv_wnd_max; flow->rcv_buf_used = 0; - uint16_t synack_wnd = tcp_calc_adv_wnd_field(flow, flow->ws_ok ? 1 : 0); + tcp_calc_adv_wnd_field(flow, flow->ws_ok ? 1 : 0); flow->ip_ttl = lf->ip_ttl; flow->ip_dontfrag = lf->ip_dontfrag; @@ -551,29 +551,26 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a flow->time_wait_ms = 0; flow->fin_wait2_ms = 0; - tcp_hdr_t synack_hdr; - 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 << SYN_F) | (1u << ACK_F)); - synack_hdr.window = synack_wnd; - synack_hdr.urgent_ptr = 0; + tcp_tx_seg_t *seg = tcp_alloc_tx_seg(flow); + if (!seg) { + tcp_free_flow(idx); + netpkt_unref(pkt); + return; + } - uint8_t syn_opts[40]; - uint8_t syn_opts_len = tcp_build_syn_options(syn_opts, (uint16_t)flow->mss, flow->ws_ok ? flow->ws_send : 0xffu, flow->sack_ok); + seg->syn = 1; + seg->fin = 0; + seg->rtt_sample = 1; + seg->retransmit_cnt = 0; + seg->seq = iss; + seg->len = 0; + seg->buf = 0; + seg->timer_ms = 0; + seg->timeout_ms = flow->rto ? flow->rto : TCP_INIT_RTO; + seg->opts_len = tcp_build_syn_options(seg->opts, (uint16_t)flow->mss, flow->ws_ok ? flow->ws_send : 0xff, flow->sack_ok); + tcp_send_from_seg(flow, seg); - if (ipver == IP_VER4) { - ipv4_tx_opts_t tx; - tx.scope = IP_TX_BOUND_L3; - tx.index = l3_id; - tcp_send_segment(IP_VER4, flow->local.ip, src_ip_addr, &synack_hdr, syn_opts, syn_opts_len, NULL, 0, (const ip_tx_opts_t *)&tx, flow->ip_ttl, flow->ip_dontfrag); - } else { - ipv6_tx_opts_t tx; - tx.scope = IP_TX_BOUND_L3; - tx.index = l3_id; - tcp_send_segment(IP_VER6, flow->local.ip, src_ip_addr, &synack_hdr, syn_opts, syn_opts_len, NULL, 0, (const ip_tx_opts_t *)&tx, flow->ip_ttl, flow->ip_dontfrag); - } + flow->snd_nxt = iss + 1; tcp_daemon_kick(); netpkt_unref(pkt); @@ -758,16 +755,40 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a case TCP_SYN_RECEIVED: if ((flags & (1u << ACK_F)) && !(flags & (1u << SYN_F)) && !(flags & (1u << RST_F)) && ack == flow->ctx.expected_ack){ - flow->ctx.sequence += 1; + uint32_t queued = 0; + if (port_handler) queued = port_handler(ifx, ipver, src_ip_addr, dst_ip_addr, 0, src_port, dst_port); + if (!queued) { + tcp_hdr_t rst_hdr; + rst_hdr.src_port = bswap16(flow->local_port); + rst_hdr.dst_port = bswap16(flow->remote.port); + rst_hdr.sequence = bswap32(flow->snd_nxt); + rst_hdr.ack = bswap32(flow->ctx.ack); + rst_hdr.flags = (uint8_t)((1 << RST_F) | (1 << ACK_F)); + rst_hdr.window = 0; + rst_hdr.urgent_ptr = 0; + + if (flow->local.ver == IP_VER4) { + ipv4_tx_opts_t tx; + tcp_build_tx_opts_from_local_v4(flow->local.ip, &tx); + (void)tcp_send_segment(IP_VER4, flow->local.ip, flow->remote.ip, &rst_hdr, NULL, 0, NULL, 0, (const ip_tx_opts_t *)&tx, flow->ip_ttl, flow->ip_dontfrag); + } else if (flow->local.ver == IP_VER6) { + ipv6_tx_opts_t tx; + tcp_build_tx_opts_from_local_v6(flow->local.ip, &tx); + (void)tcp_send_segment(IP_VER6, flow->local.ip, flow->remote.ip, &rst_hdr, NULL, 0, NULL, 0, (const ip_tx_opts_t *)&tx, flow->ip_ttl, flow->ip_dontfrag); + } + + tcp_free_flow(idx); + netpkt_unref(pkt); + return; + } + + flow->ctx.sequence = flow->snd_nxt; flow->snd_una = ack; - flow->snd_nxt = flow->ctx.sequence; flow->state = TCP_ESTABLISHED; flow->delayed_ack_pending = 0; flow->delayed_ack_timer_ms = 0; flow->ctx.ack_received = ack; - if (port_handler) port_handler(ifx, ipver, src_ip_addr, dst_ip_addr, 0, src_port, dst_port); - tcp_daemon_kick(); } else if (flags & (1u << RST_F)){ tcp_free_flow(idx); diff --git a/kernel/networking/transport_layer/tcp/tcp_tx.c b/kernel/networking/transport_layer/tcp/tcp_tx.c index fbb55d0b..598038eb 100644 --- a/kernel/networking/transport_layer/tcp/tcp_tx.c +++ b/kernel/networking/transport_layer/tcp/tcp_tx.c @@ -54,6 +54,8 @@ tcp_tx_seg_t *tcp_alloc_tx_seg(tcp_flow_t *flow){ s->fin = 0; s->rtt_sample = 0; s->retransmit_cnt = 0; + s->opts_len = 0; + memset(s->opts, 0, sizeof(s->opts)); s->seq = 0; s->len = 0; s->buf = 0; @@ -83,15 +85,16 @@ void tcp_send_from_seg(tcp_flow_t *flow, tcp_tx_seg_t *seg){ hdr.window = tcp_calc_adv_wnd_field(flow, seg->syn ? 0 : 1); hdr.urgent_ptr = 0; + const uint8_t *opts = seg->opts_len ? seg->opts : NULL; if (flow->local.ver == IP_VER4) { ipv4_tx_opts_t tx; tcp_build_tx_opts_from_local_v4(flow->local.ip, &tx); - (void)tcp_send_segment(IP_VER4, flow->local.ip, flow->remote.ip, &hdr, NULL, 0, seg->buf ? (const uint8_t *)seg->buf : NULL, seg->len, (const ip_tx_opts_t *)&tx, flow->ip_ttl, flow->ip_dontfrag); + (void)tcp_send_segment(IP_VER4, flow->local.ip, flow->remote.ip, &hdr, opts, seg->opts_len, seg->buf ? (const uint8_t *)seg->buf : NULL, seg->len, (const ip_tx_opts_t *)&tx, flow->ip_ttl, flow->ip_dontfrag); } else if (flow->local.ver == IP_VER6) { ipv6_tx_opts_t tx; tcp_build_tx_opts_from_local_v6(flow->local.ip, &tx); - (void)tcp_send_segment(IP_VER6, flow->local.ip, flow->remote.ip, &hdr, NULL, 0, seg->buf ? (const uint8_t *)seg->buf : NULL, seg->len, (const ip_tx_opts_t *)&tx, flow->ip_ttl, flow->ip_dontfrag); + (void)tcp_send_segment(IP_VER6, flow->local.ip, flow->remote.ip, &hdr, opts, seg->opts_len, seg->buf ? (const uint8_t *)seg->buf : NULL, seg->len, (const ip_tx_opts_t *)&tx, flow->ip_ttl, flow->ip_dontfrag); } tcp_daemon_kick(); From 486385ddcc6bf099fcad41c9eaf49e18793d6056 Mon Sep 17 00:00:00 2001 From: CodeAnarchist Date: Fri, 1 May 2026 22:22:54 +0200 Subject: [PATCH 05/39] [tcp] unify rx buffer and fix window managing --- .../networking/transport_layer/socket_tcp.hpp | 127 ++--- kernel/networking/transport_layer/tcp.h | 3 + .../networking/transport_layer/tcp/tcp_core.c | 70 +-- .../transport_layer/tcp/tcp_internal.h | 6 +- .../networking/transport_layer/tcp/tcp_rx.c | 485 +++++++++--------- .../networking/transport_layer/tcp/tcp_tx.c | 14 +- 6 files changed, 296 insertions(+), 409 deletions(-) diff --git a/kernel/networking/transport_layer/socket_tcp.hpp b/kernel/networking/transport_layer/socket_tcp.hpp index f7108254..452ef2b9 100755 --- a/kernel/networking/transport_layer/socket_tcp.hpp +++ b/kernel/networking/transport_layer/socket_tcp.hpp @@ -7,7 +7,6 @@ #include "networking/internet_layer/ipv4.h" #include "networking/application_layer/dns/dns.h" #include "types.h" -#include "data/struct/ring_buffer.hpp" #include "net/socket_types.h" #include "networking/internet_layer/ipv4_route.h" #include "networking/internet_layer/ipv6_route.h" @@ -21,9 +20,8 @@ static constexpr uint32_t TCP_DNS_TIMEOUT_MS = 3000; class TCPSocket : public Socket { inline static TCPSocket* s_list_head = nullptr; + static constexpr uint32_t TCP_DEFAULT_SOCKET_BUF = 256 * 1024; - static constexpr uint32_t TCP_RING_CAP = 256 * 1024; - RingBuffer ring; tcp_data* flow = nullptr; TCPSocket* pending[TCP_MAX_BACKLOG] = { nullptr }; @@ -180,83 +178,8 @@ class TCPSocket : public Socket { return 0; } - for (TCPSocket* s = s_list_head; s; s = s->next) { - if (!s->connected) continue; - if (s->localPort != dst_port) continue; - if (s->remoteEP.port != src_port) continue; - if (s->remoteEP.ver != ipver) continue; - - bool matches_dst = (s->bound_l3_count == 0); - for (int i = 0; !matches_dst && i < s->bound_l3_count; ++i) { - uint8_t id = s->bound_l3[i]; - - if (ipver == 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 != ifindex) continue; - uint32_t dst_v4 = 0; - memcpy(&dst_v4, dst_ip_addr, 4); - if (v4->ip == dst_v4) { - matches_dst = true; - break; - } - } else { - l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(id); - if (!is_valid_v6_l3_for_bind(v6)) continue; - if (v6->l2->ifindex != ifindex) continue; - if (memcmp(v6->ip, dst_ip_addr, 16) == 0) { - matches_dst = true; - break; - } - } - } - - if (!matches_dst) continue; - - if (ipver == IP_VER4) { - uint32_t remote_v4 = 0; - uint32_t src_v4 = 0; - memcpy(&remote_v4, s->remoteEP.ip, 4); - memcpy(&src_v4, src_ip_addr, 4); - if (remote_v4 != src_v4) continue; - } else { - if (memcmp(s->remoteEP.ip, src_ip_addr, 16) != 0) continue; - } - - return s->on_receive(pkt); - } - return 0; } - - uint32_t on_receive(netpkt_t* pkt) { - if (!pkt) return 0; - uintptr_t ptr = netpkt_data(pkt); - uint32_t len = netpkt_len(pkt); - if(!ptr || !len) return 0; - - uint64_t limit = ring.capacity(); - if ((extraOpts.flags & SOCK_OPT_BUF_SIZE) && extraOpts.buf_size) { - uint64_t m = extraOpts.buf_size; - if (m < limit) limit = m; - } - if (!limit) return 0; - - const uint8_t* src = (const uint8_t*)ptr; - uint32_t pushed = 0; - - uint64_t sz = ring.size(); - if (sz < limit) { - uint64_t free = limit - sz; - - uint32_t accept = len; - if((uint64_t)accept > free) accept = (uint32_t)free; - - pushed = (uint32_t)ring.push_buf(src, accept); - } - - return pushed; - } void insert_in_list() { for (TCPSocket* it = s_list_head; it; it = it->next){ @@ -344,16 +267,27 @@ class TCPSocket : public Socket { return n > 0; } + + TCPSocket* pop_pending_at(int idx) { + if (idx < 0 || idx >= backlogLen) return nullptr; + + TCPSocket* client = pending[idx]; + for (int i = idx + 1; i < backlogLen; ++i) pending[i-1] = pending[i]; + + pending[--backlogLen] = nullptr; + return client; + } + public: explicit TCPSocket(uint8_t r = SOCK_ROLE_CLIENT, uint32_t pid_ = 0, const SocketExtraOptions* extra = nullptr) : Socket(PROTO_TCP, r, extra) { pid = pid_; if (!(extraOpts.flags & SOCK_OPT_BUF_SIZE)) { extraOpts.flags |= SOCK_OPT_BUF_SIZE; - extraOpts.buf_size = TCP_RING_CAP; + extraOpts.buf_size = TCP_DEFAULT_SOCKET_BUF; } - if (!extraOpts.buf_size) extraOpts.buf_size = TCP_RING_CAP; - if (extraOpts.buf_size > TCP_RING_CAP) extraOpts.buf_size = TCP_RING_CAP; + if (!extraOpts.buf_size) extraOpts.buf_size = TCP_DEFAULT_SOCKET_BUF; + if (extraOpts.buf_size > TCP_DEFAULT_SOCKET_BUF) extraOpts.buf_size = TCP_DEFAULT_SOCKET_BUF; insert_in_list(); } @@ -488,12 +422,19 @@ class TCPSocket : public Socket { msleep(10); } - TCPSocket* client = pending[0]; - - for (int i = 1; i < backlogLen; ++i) pending[i - 1] = pending[i]; - pending[--backlogLen] = nullptr; + //TODO temporary http workaround + //without socket events accept() can pick an idle preconnect and block in recv() while a later ipv4/6 request is already ready + iter = 0; + while (1) { + for (int i = 0; i < backlogLen; ++i) { + TCPSocket* client = pending[i]; + if (!client) continue; + if (!client->flow || tcp_flow_readable(client->flow) || tcp_flow_recv_closed(client->flow)) return pop_pending_at(i); + } - return client; + if (++iter > max_iters) return nullptr; + msleep(10); + } } int32_t connect(SockDstKind kind, const void* dst, uint16_t port) { @@ -697,16 +638,11 @@ class TCPSocket : public Socket { ev.remote_ep = remoteEP; netlog_socket_event(&extraOpts, &ev); if (!buf || !len) return 0; + if (!connected || !flow) return connected ? TCP_WOULDBLOCK : 0; - uint8_t* out = (uint8_t*)buf; - uint64_t n = 0; - - n = ring.pop_buf(out, len); - - if (n) { - if (flow) tcp_flow_on_app_read(flow, (uint32_t)n); - return (int64_t)n; - } + int64_t n = tcp_flow_read(flow, buf, len); + if (n != 0) return n; + if (tcp_flow_recv_closed(flow)) return 0; if (connected) return TCP_WOULDBLOCK; return 0; } @@ -725,7 +661,6 @@ class TCPSocket : public Socket { flow = nullptr; } - ring.clear(); for (int i = 0; i < backlogLen; ++i) delete pending[i]; backlogLen = 0; diff --git a/kernel/networking/transport_layer/tcp.h b/kernel/networking/transport_layer/tcp.h index 57be40af..182afc7f 100644 --- a/kernel/networking/transport_layer/tcp.h +++ b/kernel/networking/transport_layer/tcp.h @@ -95,6 +95,9 @@ bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, tcp_result_t tcp_flow_send(tcp_data *flow_ctx); tcp_result_t tcp_flow_close(tcp_data *flow_ctx); +int64_t tcp_flow_read(tcp_data *flow_ctx, void *buf, uint64_t len); +uint32_t tcp_flow_readable(tcp_data *flow_ctx); +bool tcp_flow_recv_closed(tcp_data *flow_ctx); void tcp_flow_window_update(tcp_data *flow_ctx); void tcp_flow_on_app_read(tcp_data *flow_ctx, uint32_t bytes_read); diff --git a/kernel/networking/transport_layer/tcp/tcp_core.c b/kernel/networking/transport_layer/tcp/tcp_core.c index 07994e34..6e11c668 100644 --- a/kernel/networking/transport_layer/tcp/tcp_core.c +++ b/kernel/networking/transport_layer/tcp/tcp_core.c @@ -61,42 +61,6 @@ tcp_data *tcp_get_ctx(uint16_t local_port, ip_version_t ver, const void *local_i return &tcp_flows[idx]->ctx; } -static void clear_txq(tcp_flow_t *f){ - for (int i = 0; i < TCP_MAX_TX_SEGS; i++){ - tcp_tx_seg_t *s = &f->txq[i]; - - if (s->used && s->buf && s->len) release((void *)s->buf); - - s->used = 0; - s->syn = 0; - s->fin = 0; - s->rtt_sample = 0; - s->retransmit_cnt = 0; - s->opts_len = 0; - memset(s->opts, 0, sizeof(s->opts)); - s->seq = 0; - s->len = 0; - s->buf = 0; - s->timer_ms = 0; - s->timeout_ms = 0; -} -} - -static void clear_reass(tcp_flow_t *f){ - for (int i = 0; i < TCP_REASS_MAX_SEGS; i++){ - if (f->reass[i].buf && f->reass[i].end > f->reass[i].seq){ - release((void *)f->reass[i].buf); - } - - f->reass[i].seq = 0; - f->reass[i].end = 0; - f->reass[i].buf = 0; - } - - f->reass_count = 0; - f->rcv_buf_used = 0; -} - tcp_flow_t *tcp_alloc_flow(void){ for (int i = 0; i < MAX_TCP_FLOWS; i++){ if (tcp_flows[i]) continue; @@ -112,10 +76,6 @@ tcp_flow_t *tcp_alloc_flow(void){ f->mss = TCP_DEFAULT_MSS; f->cwnd = f->mss; f->ssthresh = TCP_RECV_WINDOW; - - clear_reass(f); - clear_txq(f); - return f; } @@ -127,9 +87,12 @@ void tcp_free_flow(int idx) { tcp_flow_t *f = tcp_flows[idx]; if (!f) return; + for (int i = 0; i < TCP_MAX_TX_SEGS; i++){ + tcp_tx_seg_t *s = &f->txq[i]; + if (s->used && s->buf && s->len) release((void *)s->buf); + } - clear_txq(f); - clear_reass(f); + if (f->rcv_buf) release((void *)f->rcv_buf); memset(f, 0, sizeof(*f)); @@ -298,7 +261,9 @@ bool tcp_bind_l3(uint8_t l3_id, uint16_t port, uint16_t pid, port_recv_handler_t f->rcv_wnd_max = TCP_DEFAULT_RCV_BUF; if (extra && (extra->flags & SOCK_OPT_BUF_SIZE) && extra->buf_size) f->rcv_wnd_max = extra->buf_size; - f->rcv_buf_used = 0; + f->rcv_base = 0; + f->rcv_data_nxt = 0; + f->rcv_ooo_used = 0; f->rcv_adv_edge = 0; f->ip_ttl = extra && (extra->flags & SOCK_OPT_TTL) ? extra->ttl : 0; @@ -308,7 +273,7 @@ bool tcp_bind_l3(uint8_t l3_id, uint16_t port, uint16_t pid, port_recv_handler_t f->keepalive_idle_ms = 0; f->mss = TCP_DEFAULT_MSS; - if (f->rcv_wnd_max > 65535u) { + if (f->rcv_wnd_max > 65535u) { f->ws_send = 8; f->ws_recv = 0; f->ws_ok = 1; @@ -319,7 +284,6 @@ bool tcp_bind_l3(uint8_t l3_id, uint16_t port, uint16_t pid, port_recv_handler_t } f->sack_ok = 1; - (void)tcp_calc_adv_wnd_field(f, 1); f->ctx.options.ptr = 0; f->ctx.options.size = 0; @@ -422,7 +386,9 @@ bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, flow->ctx.ack = 0; flow->rcv_nxt = 0; - flow->rcv_buf_used = 0; + flow->rcv_base = 0; + flow->rcv_data_nxt = 0; + flow->rcv_ooo_used = 0; flow->rcv_wnd_max = TCP_DEFAULT_RCV_BUF; if (extra && (extra->flags & SOCK_OPT_BUF_SIZE) && extra->buf_size) flow->rcv_wnd_max = extra->buf_size; flow->rcv_adv_edge = 0; @@ -439,7 +405,14 @@ bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, flow->ws_ok = 0; } flow->sack_ok = 1; - + flow->rcv_buf = (uintptr_t)zalloc(flow->rcv_wnd_max); + if (!flow->rcv_buf) { + flow->rcv_wnd = 0; + flow->rcv_adv_edge = flow->rcv_nxt; + flow->ctx.window = 0; + tcp_free_flow(idx); + return false; + } (void)tcp_calc_adv_wnd_field(flow, 1); flow->ip_ttl = extra && (extra->flags & SOCK_OPT_TTL) ? extra->ttl : 0; @@ -471,9 +444,6 @@ bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, flow->time_wait_ms = 0; flow->fin_wait2_ms = 0; - clear_reass(flow); - clear_txq(flow); - tcp_tx_seg_t *seg = tcp_alloc_tx_seg(flow); if (!seg){ diff --git a/kernel/networking/transport_layer/tcp/tcp_internal.h b/kernel/networking/transport_layer/tcp/tcp_internal.h index c40464d7..b187a525 100644 --- a/kernel/networking/transport_layer/tcp/tcp_internal.h +++ b/kernel/networking/transport_layer/tcp/tcp_internal.h @@ -44,7 +44,6 @@ typedef struct { typedef struct { uint32_t seq; uint32_t end; - uintptr_t buf; } tcp_reass_seg_t; typedef struct { @@ -66,7 +65,10 @@ typedef struct { uint32_t fin_wait2_ms; uint32_t rcv_nxt; - uint32_t rcv_buf_used; + uint32_t rcv_base; + uint32_t rcv_data_nxt; + uintptr_t rcv_buf; + uint32_t rcv_ooo_used; uint32_t rcv_wnd; uint32_t rcv_wnd_max; uint32_t rcv_adv_edge; diff --git a/kernel/networking/transport_layer/tcp/tcp_rx.c b/kernel/networking/transport_layer/tcp/tcp_rx.c index 16c46292..a881c106 100644 --- a/kernel/networking/transport_layer/tcp/tcp_rx.c +++ b/kernel/networking/transport_layer/tcp/tcp_rx.c @@ -9,233 +9,100 @@ #include "syscalls/syscalls.h" #include "../tcp.h" -static void tcp_reass_evict_tail(tcp_flow_t *flow, uint32_t need) { - while (flow->reass_count && flow->rcv_buf_used + need > flow->rcv_wnd_max) { - int idx = 0; - uint32_t best = flow->reass[0].seq; - for (int i = 1; i < flow->reass_count; i++) { - if (flow->reass[i].seq > best) { - best = flow->reass[i].seq; - idx = i; - } - } - - tcp_reass_seg_t *r = &flow->reass[idx]; - uint32_t olen = r->end - r->seq; - if (r->buf && olen) release((void *)r->buf); - if (flow->rcv_buf_used >= olen) flow->rcv_buf_used -= olen; - else flow->rcv_buf_used = 0; - - flow->reass[idx] = flow->reass[flow->reass_count - 1]; - flow->reass[flow->reass_count - 1].seq = 0; - flow->reass[flow->reass_count - 1].end = 0; - flow->reass[flow->reass_count - 1].buf = 0; - flow->reass_count--; - } -} - -static void tcp_reass_insert(tcp_flow_t *flow, uint32_t seq, const netpkt_t *pkt, uint32_t data, uint32_t len) { - if (!len) return; - if (flow->reass_count >= TCP_REASS_MAX_SEGS) return; - if (seq < flow->rcv_nxt) { - uint32_t d = flow->rcv_nxt - seq; - if (d >= len) return; - seq += d; - data += d; - len -= d; +static tcp_flow_t *tcp_flow_from_ctx(tcp_data *flow_ctx) { + if (!flow_ctx) return NULL; + for (int i = 0; i < MAX_TCP_FLOWS; i++) { + tcp_flow_t *flow = tcp_flows[i]; + if (!flow)continue; + if (&flow->ctx == flow_ctx) return flow; } - uint32_t wnd_end = flow->rcv_nxt + flow->rcv_wnd; - if (seq >= wnd_end) return; - if (seq + len > wnd_end) len = wnd_end - seq; - if (!len) return; - - if (flow->rcv_buf_used + len > flow->rcv_wnd_max) tcp_reass_evict_tail(flow, len); - if (flow->rcv_buf_used + len > flow->rcv_wnd_max) return; - - uint32_t orig_seq = seq; - uint32_t start = seq; - uint32_t end = seq + len; - - for(;;){ - int changed = 0; - - for (int i = 0; i < flow->reass_count; i++){ - tcp_reass_seg_t *r = &flow->reass[i]; - uint32_t rs = r->seq; - uint32_t re = r->end; - - if (end <= rs || start >= re) continue; - if (start >= rs && end <= re) return; - - if (start <= rs && end >= re) { - uint32_t olen = re - rs; - - if (r->buf && olen) release((void *)r->buf); - - flow->reass[i] = flow->reass[flow->reass_count - 1]; - flow->reass[flow->reass_count - 1].seq = 0; - flow->reass[flow->reass_count - 1].end = 0; - flow->reass[flow->reass_count - 1].buf = 0; - flow->reass_count--; - - flow->rcv_buf_used -= olen; - changed = 1; - break; - } - - if (start < rs && end > rs && end <= re) { - end = rs; - len = end - start; - changed = 1; - break; - } - - if (start >= rs && start < re && end > re){ - start = re; - len = end - start; - changed = 1; - break; - } - } - - if (!changed) break; - if (!len) return; - } - - if (!len) return; - if (flow->reass_count >= TCP_REASS_MAX_SEGS) return; - if (flow->rcv_buf_used + len > flow->rcv_wnd_max) tcp_reass_evict_tail(flow, len); - if (flow->rcv_buf_used + len > flow->rcv_wnd_max) return; - - uintptr_t buf = (uintptr_t)zalloc(len ? len : 1u); - if (!buf) return; - - uint32_t offset = start - orig_seq; - if (!netpkt_copyout(pkt, data + offset, (void *)buf, len)) { - release((void *)buf); - return; - } + return NULL; +} - int pos = flow->reass_count; - while (pos > 0 && flow->reass[pos - 1].seq > start){ - flow->reass[pos] = flow->reass[pos - 1]; - pos--; - } +static void tcp_reass_remove(tcp_flow_t *flow, int idx) { + uint32_t len = flow->reass[idx].end - flow->reass[idx].seq; - flow->reass[pos].seq = start; - flow->reass[pos].end = start + len; - flow->reass[pos].buf = buf; - flow->reass_count++; + if (flow->rcv_ooo_used >= len) flow->rcv_ooo_used -= len; + else flow->rcv_ooo_used = 0; - flow->rcv_buf_used += len; + for (int i = idx; i + 1 < flow->reass_count; i++) flow->reass[i] = flow->reass[i+1]; - (void)tcp_calc_adv_wnd_field(flow, 1); + if (flow->reass_count) flow->reass_count--; + flow->reass[flow->reass_count].seq = 0; + flow->reass[flow->reass_count].end = 0; } -static void tcp_reass_drain_inseq(tcp_flow_t *flow, port_recv_handler_t handler, uint8_t ifx, ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_addr, uint16_t src_port, uint16_t dst_port) { - uint32_t rcv_nxt = flow->rcv_nxt; +static int tcp_reass_drain_inseq(tcp_flow_t *flow) { + int advanced = 0; for(;;){ int idx = -1; for (int i = 0; i < flow->reass_count; i++){ - if (flow->reass[i].seq != rcv_nxt) continue; + if (flow->reass[i].seq != flow->rcv_nxt) continue; idx = i; break; } if (idx < 0) break; - tcp_reass_seg_t *seg = &flow->reass[idx]; - uint32_t seg_len = seg->end - seg->seq; - - if (!seg_len) { - flow->reass[idx] = flow->reass[flow->reass_count - 1]; - flow->reass[flow->reass_count - 1].seq = 0; - flow->reass[flow->reass_count - 1].end = 0; - flow->reass[flow->reass_count - 1].buf = 0; - flow->reass_count--; + uint32_t end = flow->reass[idx].end; + if (end <= flow->rcv_nxt) { + tcp_reass_remove(flow, idx); continue; } - if (handler) { - bool handled = true; - uint32_t accepted = seg_len; - - netpkt_t* segpkt = netpkt_alloc(seg_len, 0, 0); - if (segpkt) { - void* segmem = netpkt_put(segpkt, seg_len); - if (segmem) { - memcpy(segmem, (const void*)seg->buf, seg_len); - accepted = handler(ifx, ipver, src_ip_addr, dst_ip_addr, segpkt, src_port, dst_port); - } else accepted = 0; - netpkt_unref(segpkt); - } else accepted = 0; - if (!handled) accepted = seg_len; - if (accepted > seg_len) accepted = seg_len; - - if (accepted == 0) { - if (flow->state == TCP_FIN_WAIT_1 || flow->state == TCP_FIN_WAIT_2 || flow->state == TCP_CLOSING || flow->state == TCP_LAST_ACK || flow->state == TCP_TIME_WAIT) { - if (seg->buf) release((void *)seg->buf); - - if (flow->rcv_buf_used >= seg_len) flow->rcv_buf_used -= seg_len; - else flow->rcv_buf_used = 0; - - rcv_nxt += seg_len; - - flow->reass[idx] = flow->reass[flow->reass_count - 1]; - flow->reass[flow->reass_count - 1].seq = 0; - flow->reass[flow->reass_count - 1].end = 0; - flow->reass[flow->reass_count - 1].buf = 0; - flow->reass_count--; - continue; - } - - break; - } - - if (accepted < seg_len) { - uint32_t rem = seg_len - accepted; - uintptr_t newbuf = (uintptr_t)zalloc(rem ? rem : 1u); - if (!newbuf) break; - - memcpy((void *)newbuf, ((const uint8_t *)seg->buf) + accepted, rem); - if (seg->buf) release((void *)seg->buf); + flow->rcv_nxt = end; + flow->rcv_data_nxt = end; + flow->ctx.ack = flow->rcv_nxt; + tcp_reass_remove(flow, idx); + advanced = 1; + } - seg->buf = newbuf; - seg->seq += accepted; + if (advanced) tcp_calc_adv_wnd_field(flow, 1); + return advanced; +} - if (flow->rcv_buf_used >= accepted) flow->rcv_buf_used -= accepted; - else flow->rcv_buf_used = 0; +int64_t tcp_flow_read(tcp_data *flow_ctx, void *buf, uint64_t len) { + if (!buf || !len) return 0; - rcv_nxt += accepted; + tcp_flow_t *flow = tcp_flow_from_ctx(flow_ctx); + if (!flow) return TCP_DISCONNECT; - flow->rcv_nxt = rcv_nxt; - flow->ctx.ack = rcv_nxt; + if (!flow->rcv_buf || !flow->rcv_wnd_max) return 0; + if (flow->rcv_data_nxt < flow->rcv_base) return 0; - (void)tcp_calc_adv_wnd_field(flow, 1); - continue; - } - } + uint32_t n = flow->rcv_data_nxt - flow->rcv_base; + if (!n) return 0; + if (len < n) n = (uint32_t)len; - rcv_nxt += seg_len; + uint8_t *rx = (uint8_t*)flow->rcv_buf; + uint8_t *dst = (uint8_t*)buf; + uint32_t pos = flow->rcv_base % flow->rcv_wnd_max; + uint32_t first = flow->rcv_wnd_max - pos; - if (seg->buf) release((void *)seg->buf); - if (flow->rcv_buf_used >= seg_len) flow->rcv_buf_used -= seg_len; - else flow->rcv_buf_used = 0; + if (first > n) first = n; + if (first) memcpy(dst, rx + pos, first); + if (n > first) memcpy(dst + first, rx, n - first); - flow->reass[idx] = flow->reass[flow->reass_count - 1]; - flow->reass[flow->reass_count - 1].seq = 0; - flow->reass[flow->reass_count - 1].end = 0; - flow->reass[flow->reass_count - 1].buf = 0; - flow->reass_count--; - } + flow->rcv_base += n; + tcp_flow_on_app_read(flow_ctx, n); + return n; +} - flow->rcv_nxt = rcv_nxt; - flow->ctx.ack = rcv_nxt; +uint32_t tcp_flow_readable(tcp_data *flow_ctx) { + tcp_flow_t *flow = tcp_flow_from_ctx(flow_ctx); + if (!flow || !flow->rcv_buf || !flow->rcv_wnd_max) return 0; + if (flow->rcv_data_nxt < flow->rcv_base) return 0; + return flow->rcv_data_nxt - flow->rcv_base; +} - (void)tcp_calc_adv_wnd_field(flow, 1); +bool tcp_flow_recv_closed(tcp_data *flow_ctx) { + tcp_flow_t *flow = tcp_flow_from_ctx(flow_ctx); + if (!flow) return true; + if (flow->state == TCP_CLOSE_WAIT || flow->state == TCP_STATE_CLOSED || flow->state == TCP_TIME_WAIT) return true; + return false; } tcp_tx_seg_t *tcp_find_first_unacked(tcp_flow_t *flow) { @@ -532,7 +399,18 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a flow->delayed_ack_timer_ms = 0; flow->rcv_wnd_max = lf->rcv_wnd_max; - flow->rcv_buf_used = 0; + flow->rcv_base = flow->rcv_nxt; + flow->rcv_data_nxt = flow->rcv_nxt; + flow->rcv_ooo_used = 0; + flow->rcv_buf = (uintptr_t)zalloc(flow->rcv_wnd_max); + if (!flow->rcv_buf) { + flow->rcv_wnd = 0; + flow->rcv_adv_edge = flow->rcv_nxt; + flow->ctx.window = 0; + tcp_free_flow(idx); + netpkt_unref(pkt); + return; + } tcp_calc_adv_wnd_field(flow, flow->ws_ok ? 1 : 0); flow->ip_ttl = lf->ip_ttl; @@ -721,6 +599,16 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a if (flow->ws_ok && flow->ws_recv) new_wnd <<= flow->ws_recv; flow->snd_wnd = new_wnd; + flow->rcv_base = flow->rcv_nxt; + flow->rcv_data_nxt = flow->rcv_nxt; + flow->rcv_ooo_used = 0; + if (!flow->rcv_buf || !flow->rcv_wnd_max) { + flow->rcv_wnd = 0; + flow->rcv_adv_edge = flow->rcv_nxt; + flow->ctx.window = 0; + netpkt_unref(pkt); + return; + } (void)tcp_calc_adv_wnd_field(flow, 1); tcp_hdr_t final_ack; @@ -853,39 +741,41 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a ack_immediate = 1; } else if (seg_seq == flow->rcv_nxt) { if (data_len){ - uint32_t free_space = (flow->rcv_buf_used < flow->rcv_wnd_max) ? (flow->rcv_wnd_max - flow->rcv_buf_used) : 0; - uint32_t offer = data_len; - if (offer > free_space) offer = free_space; + uint32_t right_edge = flow->rcv_adv_edge; + uint32_t hard_edge = flow->rcv_buf && flow->rcv_wnd_max ? flow->rcv_base + flow->rcv_wnd_max : flow->rcv_nxt; + if (right_edge > hard_edge) right_edge = hard_edge; + if (seg_seq >= right_edge) offer = 0; + else if (seg_seq + offer > right_edge) offer = right_edge - seg_seq; uint32_t accepted = 0; - if (offer && port_handler) { - netpkt_t* datapkt = netpkt_view(pkt, payload, offer); - if (datapkt) { - accepted = port_handler(ifx, ipver, src_ip_addr, dst_ip_addr, datapkt, src_port, dst_port); - netpkt_unref(datapkt); - } else port_handler = 0; + uint32_t stored = 0; + if (offer && (flow->state == TCP_FIN_WAIT_1 || flow->state == TCP_FIN_WAIT_2 || flow->state == TCP_CLOSING || flow->state == TCP_LAST_ACK || flow->state == TCP_TIME_WAIT)) accepted = offer; + else if (offer && flow->rcv_buf && flow->rcv_wnd_max) { + uint8_t *rx = (uint8_t *)flow->rcv_buf; + uint32_t cap = flow->rcv_wnd_max; + uint32_t pos = seg_seq % cap; + uint32_t first = cap - pos; + + if (first > offer) first = offer; + if (first && !netpkt_copyout(pkt, payload, rx + pos, first)) stored = 0; + else if (offer > first && !netpkt_copyout(pkt, payload + first, rx, offer - first)) stored = 0; + else stored = offer; + + accepted = stored; } - if (accepted > offer) accepted = offer; - if (port_handler && accepted == 0 && data_len) { + if (accepted) { + flow->rcv_nxt += accepted; + if (stored) flow->rcv_data_nxt = flow->rcv_nxt; + flow->ctx.ack = flow->rcv_nxt; + } else if (data_len) { (void)tcp_calc_adv_wnd_field(flow, 1); need_ack = 1; ack_immediate = 1; } - if (!accepted && offer && (flow->state == TCP_FIN_WAIT_1 || flow->state == TCP_FIN_WAIT_2 || flow->state == TCP_CLOSING || flow->state == TCP_LAST_ACK || flow->state == TCP_TIME_WAIT)) { - flow->rcv_nxt += offer; - flow->ctx.ack = flow->rcv_nxt; - accepted = offer; - } else if (accepted) { - flow->rcv_nxt += accepted; - flow->ctx.ack = flow->rcv_nxt; - flow->rcv_buf_used += accepted; - } - if (accepted < data_len) { - ack_immediate = 1; - } + if (accepted < data_len) ack_immediate = 1; } if (fin_in) { @@ -910,7 +800,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a } } - tcp_reass_drain_inseq(flow, port_handler, ifx, ipver, src_ip_addr, dst_ip_addr, src_port, dst_port); + if (tcp_reass_drain_inseq(flow)) ack_immediate = 1; if (flow->fin_pending && flow->fin_seq == flow->rcv_nxt){ flow->fin_pending = 0; @@ -935,7 +825,113 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a if (!ack_immediate && data_len) ack_defer = 1; need_ack = 1; } else { - if (!(flow->state == TCP_FIN_WAIT_1 || flow->state == TCP_FIN_WAIT_2 || flow->state == TCP_CLOSING || flow->state == TCP_LAST_ACK || flow->state == TCP_TIME_WAIT) && data_len) tcp_reass_insert(flow, seg_seq, pkt, payload, data_len); + if (!(flow->state == TCP_FIN_WAIT_1 || flow->state == TCP_FIN_WAIT_2 || flow->state == TCP_CLOSING || flow->state == TCP_LAST_ACK || flow->state == TCP_TIME_WAIT) && data_len && flow->rcv_buf && flow->rcv_wnd_max) { + uint32_t ooo_seq = seg_seq; + uint32_t ooo_data = payload; + uint32_t ooo_len = data_len; + + if (ooo_seq rcv_nxt) { + uint32_t d = flow->rcv_nxt - ooo_seq; + if (d >= ooo_len) ooo_len= 0; + else { + ooo_seq += d; + ooo_data += d; + ooo_len -= d; + } + } + + if (ooo_len) { + uint32_t right_edge = flow->rcv_adv_edge; + uint32_t hard_edge = flow->rcv_base + flow->rcv_wnd_max; + if (right_edge > hard_edge) right_edge = hard_edge; + if (ooo_seq >= right_edge) ooo_len = 0; + else if (ooo_seq + ooo_len > right_edge) ooo_len = right_edge - ooo_seq; + } + + if (ooo_len) { + uint32_t orig_seq = ooo_seq; + uint32_t start = ooo_seq; + uint32_t end = ooo_seq + ooo_len; + + for (;;) { + int changed = 0; + for (int i = 0; i < flow->reass_count; i++) { + tcp_reass_seg_t *r = &flow->reass[i]; + uint32_t rs = r->seq; + uint32_t re = r->end; + + if (end <= rs || start >= re) continue; + if (start >= rs && end <= re) { + ooo_len = 0; + break; + } + + if (start <= rs && end >= re) { + tcp_reass_remove(flow, i); + changed = 1; + break; + } + + if (start < rs && end > rs && end <= re) { + end = rs; + ooo_len = end - start; + changed = 1; + break; + } + + if (start >= rs && start < re && end > re) { + start = re; + ooo_len = end - start; + changed = 1; + break; + } + } + + if (!changed || !ooo_len) break; + } + + if (ooo_len && flow->reass_count>= TCP_REASS_MAX_SEGS) { + int drop_idx = 0; + uint32_t best = flow->reass[0].seq; + + for (int i = 1; i < flow->reass_count; i++) { + if (flow->reass[i].seq <= best) continue; + best = flow->reass[i].seq; + drop_idx = i; + } + + if (start >= best) ooo_len = 0; + else tcp_reass_remove(flow, drop_idx); + } + + if (ooo_len && flow->reass_count < TCP_REASS_MAX_SEGS) { + uint8_t *rx = (uint8_t *)flow->rcv_buf; + uint32_t cap = flow->rcv_wnd_max; + uint32_t offset = start - orig_seq; + uint32_t pos = start % cap; + uint32_t first = cap - pos; + int copied = 1; + + if (first > ooo_len) first = ooo_len; + if (first && !netpkt_copyout(pkt, ooo_data + offset, rx + pos, first)) copied = 0; + if (copied && ooo_len > first && !netpkt_copyout(pkt, ooo_data + offset + first, rx, ooo_len - first)) copied = 0; + + if (copied) { + int pos_idx = flow->reass_count; + while (pos_idx > 0 &&flow->reass[pos_idx - 1].seq > start) { + flow->reass[pos_idx] = flow->reass[pos_idx - 1]; + pos_idx--; + } + + flow->reass[pos_idx].seq = start; + flow->reass[pos_idx].end = start + ooo_len; + flow->reass_count++; + flow->rcv_ooo_used += ooo_len; + tcp_calc_adv_wnd_field(flow, 1); + } + } + } + } if (fin_in){ flow->fin_pending = 1; @@ -969,48 +965,29 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a } } } + netpkt_unref(pkt); } void tcp_flow_on_app_read(tcp_data *flow_ctx, uint32_t bytes_read){ if (!flow_ctx || bytes_read == 0) return; - tcp_flow_t *flow = NULL; - for (int i = 0; i < MAX_TCP_FLOWS; ++i) { - tcp_flow_t *f = tcp_flows[i]; - if (!f) continue; - if (&f->ctx == flow_ctx) { - flow = f; - break; - } - } + tcp_flow_t *flow = tcp_flow_from_ctx(flow_ctx); if (!flow) return; - if (bytes_read > flow->rcv_buf_used) bytes_read = flow->rcv_buf_used; - flow->rcv_buf_used -= bytes_read; + uint32_t old_edge = flow->rcv_adv_edge; + uint32_t old_wnd = flow->rcv_wnd; + uint32_t old_nxt = flow->rcv_nxt; - port_manager_t *pm = NULL; - uint8_t ifx = 0; + int advanced = tcp_reass_drain_inseq(flow); + tcp_calc_adv_wnd_field(flow, 1); - if (flow->local.ver == IP_VER4) { - l3_ipv4_interface_t *v4 = l3_ipv4_find_by_id(flow->l3_id); - if (v4 && v4->l2) { - pm = ifmgr_pm_v4(flow->l3_id); - ifx = v4->l2->ifindex; - } - } else if (flow->local.ver == IP_VER6) { - l3_ipv6_interface_t *v6 = l3_ipv6_find_by_id(flow->l3_id); - if (v6 && v6->l2) { - pm = ifmgr_pm_v6(flow->l3_id); - ifx = v6->l2->ifindex; - } - } + if (flow->state == TCP_STATE_CLOSED || flow->state == TCP_TIME_WAIT) return; - if (pm) { - tcp_reass_drain_inseq(flow, port_get_handler(pm, PROTO_TCP, flow->local_port), ifx, flow->local.ver, flow->remote.ip, flow->local.ip, flow->remote.port, flow->local_port); - } + uint32_t threshold = flow->mss ? flow->mss : TCP_DEFAULT_MSS; + uint32_t half = flow->rcv_wnd_max >> 1; + if (half && half < threshold) threshold = half; + if (!threshold) threshold = 1; - if (flow->state != TCP_STATE_CLOSED && flow->state != TCP_TIME_WAIT) { - (void)tcp_calc_adv_wnd_field(flow, 1); - tcp_send_ack_now(flow); - } + uint32_t delta = flow->rcv_adv_edge > old_edge ? flow->rcv_adv_edge - old_edge : 0; + if (advanced || flow->rcv_nxt != old_nxt || (!old_wnd && flow->rcv_wnd) || delta >= threshold) tcp_send_ack_now(flow); } diff --git a/kernel/networking/transport_layer/tcp/tcp_tx.c b/kernel/networking/transport_layer/tcp/tcp_tx.c index 598038eb..f630534d 100644 --- a/kernel/networking/transport_layer/tcp/tcp_tx.c +++ b/kernel/networking/transport_layer/tcp/tcp_tx.c @@ -6,17 +6,17 @@ uint16_t tcp_calc_adv_wnd_field(tcp_flow_t *flow, uint8_t apply_scale) { uint32_t quantum = 1; if (apply_scale && flow->ws_ok && flow->ws_send) quantum = 1u << flow->ws_send; - uint32_t maxw = flow->rcv_wnd_max; - uint32_t used = flow->rcv_buf_used; - uint32_t freew = maxw > used ? maxw - used: 0; - - uint32_t free_q = quantum == 1 ? freew : (freew & ~(quantum - 1)); + uint32_t edge = flow->rcv_nxt; + if (flow->rcv_buf && flow->rcv_wnd_max) { + edge = flow->rcv_base + flow->rcv_wnd_max; + if (edge < flow->rcv_nxt) edge = flow->rcv_nxt; + } if (flow->rcv_adv_edge < flow->rcv_nxt) flow->rcv_adv_edge = flow->rcv_nxt; - uint32_t candidate_edge = flow->rcv_nxt + free_q; - if (candidate_edge > flow->rcv_adv_edge) flow->rcv_adv_edge = candidate_edge; + if (edge > flow->rcv_adv_edge) flow->rcv_adv_edge = edge; uint32_t adv = flow->rcv_adv_edge - flow->rcv_nxt; + if (quantum > 1) adv &= ~(quantum - 1); uint32_t field = adv; if (!apply_scale || !flow->ws_ok || flow->ws_send == 0) { From f51e53f765faaa61c11e770284becb7672bd8f66 Mon Sep 17 00:00:00 2001 From: CodeAnarchist Date: Wed, 6 May 2026 01:03:57 +0200 Subject: [PATCH 06/39] [NET] TCP retransmission, SACK handling, HTTP cont len --- kernel/networking/application_layer/http.c | 75 ++++-- kernel/networking/application_layer/http.h | 2 + .../application_layer/socket_http_client.hpp | 74 ++++-- .../application_layer/socket_http_server.hpp | 77 ++++-- kernel/networking/processes/net_proc.c | 8 +- .../networking/transport_layer/socket_tcp.hpp | 2 +- .../networking/transport_layer/socket_udp.hpp | 18 +- .../networking/transport_layer/tcp/tcp_core.c | 4 + .../transport_layer/tcp/tcp_internal.h | 7 + .../networking/transport_layer/tcp/tcp_rx.c | 240 ++++++++++++------ .../transport_layer/tcp/tcp_timer.c | 23 +- .../networking/transport_layer/tcp/tcp_tx.c | 179 ++++++++----- .../transport_layer/tcp/tcp_utils.c | 27 +- .../transport_layer/tcp/tcp_utils.h | 8 + 14 files changed, 507 insertions(+), 237 deletions(-) diff --git a/kernel/networking/application_layer/http.c b/kernel/networking/application_layer/http.c index eebf2698..9168a482 100644 --- a/kernel/networking/application_layer/http.c +++ b/kernel/networking/application_layer/http.c @@ -2,7 +2,7 @@ #include "std/string.h" #include "std/memory.h" #include "syscalls/syscalls.h" - +//TODO check for a maximum request body size before allocating the buffer string http_header_builder(const HTTPHeadersCommon *C, const HTTPHeader *H, uint32_t N){ string out = string_repeat('\0', 0); @@ -80,7 +80,7 @@ void http_headers_extra_free(HTTPHeader *extra, uint32_t extra_count){ if (extra[i].key.mem_length) string_free(extra[i].key); if (extra[i].value.mem_length) string_free(extra[i].value); } - free_sized(extra, extra_count * sizeof(HTTPHeader)); + release(extra); } void http_header_parser(const char *buf, uint32_t len, @@ -91,13 +91,27 @@ void http_header_parser(const char *buf, uint32_t len, *C = (HTTPHeadersCommon){0}; uint32_t max_lines = 0; - for (uint32_t i = 0; i + 1 < len; i++){ - if (buf[i]=='\r' && buf[i+1]=='\n') max_lines++; + uint32_t count_pos = 0; + while (count_pos < len) { + uint32_t eol = count_pos; + bool has_crlf = false; + while (eol + 1 < len) { + if (buf[eol] == '\r' && buf[eol+1] == '\n') { + has_crlf = true; + break; + } + eol++; + } + if (!has_crlf) eol = len; + if (eol == count_pos) break; + max_lines++; + if (!has_crlf) break; + count_pos = eol + 2; } HTTPHeader *extras = NULL; if (max_lines){ - extras = (HTTPHeader*)(uintptr_t)malloc(sizeof(*extras) * max_lines); + extras = (HTTPHeader*)zalloc(sizeof(*extras) * max_lines); if (!extras){ *out_extra = NULL; *out_extra_count = 0; @@ -110,12 +124,20 @@ void http_header_parser(const char *buf, uint32_t len, char key_tmp[64]; - while (pos + 1 < len){ + while (pos < len){ uint32_t eol = pos; - while (eol + 1 < len && !(buf[eol]=='\r' && buf[eol+1]=='\n')) eol++; + bool has_crlf = false; + while (eol + 1 < len) { + if (buf[eol] == '\r' && buf[eol+1] == '\n') { + has_crlf = true; + break; + } + eol++; + } + if (!has_crlf) eol = len; if (eol == pos){ - pos += 2; + if (has_crlf) pos += 2; break; } @@ -123,6 +145,7 @@ void http_header_parser(const char *buf, uint32_t len, while (sep < eol && buf[sep] != ':') sep++; if (sep == eol){ + if (!has_crlf) break; pos = eol + 2; continue; } @@ -140,7 +163,28 @@ void http_header_parser(const char *buf, uint32_t len, key_tmp[copy_len] = '\0'; if (copy_len == 14 && strcmp_case(key_tmp, "content-length", true) == 0){ - C->length = (uint32_t)parse_int_u64(buf + val_start, val_len); + uint32_t p = val_start; + uint32_t end = eol; + while (p < end && (buf[p] == ' ' || buf[p] == '\t')) p++; + while (end > p && (buf[end-1] == ' ' || buf[end-1] == '\t')) end--; + + bool ok = p < end; + uint64_t parsed = 0; + while (ok && p < end) { + char c = buf[p++]; + if (! is_digit(c)) { + ok = false; + break; + } + parsed = parsed * 10 + (uint64_t)(c - '0'); + if (parsed > UINT32_MAX) ok = false; + } + + if (!ok || (C->has_length && C->length != (uint32_t)parsed)) C->bad_length = 1; + else { + C->has_length = 1; + C->length = (uint32_t)parsed; + } } else if (copy_len == 12 && strcmp_case(key_tmp, "content-type", true) == 0){ C->type = string_from_literal_length((char*)(buf + val_start), val_len); @@ -169,11 +213,12 @@ void http_header_parser(const char *buf, uint32_t len, } } + if (!has_crlf) break; pos = eol + 2; } if (!extras || extra_i == 0){ - if (extras) free_sized(extras, sizeof(*extras) * max_lines); + if (extras) release(extras); *out_extra = NULL; *out_extra_count = 0; return; @@ -185,10 +230,10 @@ void http_header_parser(const char *buf, uint32_t len, return; } - HTTPHeader *shr = (HTTPHeader*)(uintptr_t)malloc(sizeof(*shr) * extra_i); + HTTPHeader *shr = (HTTPHeader*)zalloc(sizeof(*shr) * extra_i); if (shr){ memcpy(shr, extras, sizeof(*shr) * extra_i); - free_sized(extras, sizeof(*extras) * max_lines); + release(extras); *out_extra = shr; *out_extra_count = extra_i; return; @@ -198,7 +243,7 @@ void http_header_parser(const char *buf, uint32_t len, if (extras[i].key.mem_length) string_free(extras[i].key); if (extras[i].value.mem_length) string_free(extras[i].value); } - free_sized(extras, sizeof(*extras) * max_lines); + release(extras); *out_extra = NULL; *out_extra_count = 0; } @@ -215,9 +260,7 @@ string http_request_builder(const HTTPRequestMsg *R){ string_free(hdrs); if (R->body.ptr && R->body.size){ - string body = string_from_literal_length((char*)R->body.ptr, R->body.size); - string_append_bytes(&out, body.data, body.length); - string_free(body); + string_append_bytes(&out, (char*)R->body.ptr, (uint32_t)R->body.size); } return out; diff --git a/kernel/networking/application_layer/http.h b/kernel/networking/application_layer/http.h index 06163a76..e3f461e8 100644 --- a/kernel/networking/application_layer/http.h +++ b/kernel/networking/application_layer/http.h @@ -33,6 +33,8 @@ typedef struct { typedef struct { uint32_t length; + uint8_t has_length; + uint8_t bad_length; string type; string date; string connection; diff --git a/kernel/networking/application_layer/socket_http_client.hpp b/kernel/networking/application_layer/socket_http_client.hpp index be410252..fbf872b8 100644 --- a/kernel/networking/application_layer/socket_http_client.hpp +++ b/kernel/networking/application_layer/socket_http_client.hpp @@ -4,7 +4,7 @@ #include "http.h" #include "std/std.h" #include "net/socket_types.h" - +//TODO chunked; handle http bodies without Content-Length class HTTPClient { private: uint16_t pid; @@ -18,7 +18,7 @@ class HTTPClient { const SocketExtraOptions* tcp_ptr = extra; if (extra && (log_opts.flags & SOCK_OPT_DEBUG)) { - tcp_extra = (SocketExtraOptions*)malloc(sizeof(SocketExtraOptions)); + tcp_extra = (SocketExtraOptions*)zalloc(sizeof(SocketExtraOptions)); if (tcp_extra) { *tcp_extra = *extra; tcp_extra->flags &= ~SOCK_OPT_DEBUG; @@ -26,7 +26,7 @@ class HTTPClient { } } - sock = (TCPSocket*)malloc(sizeof(TCPSocket)); + sock = (TCPSocket*)zalloc(sizeof(TCPSocket)); if (sock) new (sock) TCPSocket(SOCK_ROLE_CLIENT, pid, tcp_ptr); } @@ -152,7 +152,7 @@ class HTTPClient { 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), + (uint32_t)hdr_end - (uint32_t)(status_line_end + 2), &resp.headers_common, &extras, &extra_count); @@ -162,23 +162,57 @@ class HTTPClient { 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 == TCP_WOULDBLOCK) { msleep(10); continue; } - if (r < 0) break; - if (r == 0) break; - string_append_bytes(&buf, tmp, (uint32_t)r); - have += (uint32_t)r; - } + if (resp.headers_common.bad_length) { + if (resp.reason.mem_length) string_free(resp.reason); + resp.reason = string{}; + http_headers_common_free(&resp.headers_common); + http_headers_extra_free(resp.extra_headers, resp.extra_header_count); + resp.extra_headers = nullptr; + resp.extra_header_count = 0; + string_free(buf); + sock->close(); + resp.status_code = (HttpError)SOCK_ERR_PROTO; + return resp; } - if (have > 0) { - char *body_copy = (char*)malloc(have); + + uint32_t need = resp.headers_common.has_length ? resp.headers_common.length : 0; + uint32_t body_len = resp.headers_common.has_length ? need : have; + char *body_copy = nullptr; + + if (body_len > 0) { + body_copy = (char*)zalloc(body_len); if (body_copy) { - memcpy(body_copy, buf.data + body_start, have); + uint32_t copied = have < body_len ? have : body_len; + if (copied) memcpy(body_copy, buf.data + body_start, copied); + + if (resp.headers_common.has_length) { + while (copied < need) { + int64_t r = sock->recv(body_copy + copied, need - copied); + if (r == TCP_WOULDBLOCK) { + msleep(1); + continue; + } + if (r <= 0) break; + copied += (uint32_t)r; + } + + if (copied < need) { + release(body_copy); + if (resp.reason.mem_length) string_free(resp.reason); + resp.reason = string{}; + http_headers_common_free(&resp.headers_common); + http_headers_extra_free(resp.extra_headers, resp.extra_header_count); + resp.extra_headers = nullptr; + resp.extra_header_count = 0; + string_free(buf); + sock->close(); + resp.status_code = (HttpError)SOCK_ERR_PROTO; + return resp; + } + } + resp.body.ptr = (uintptr_t)body_copy; - resp.body.size = have; + resp.body.size = body_len; } } @@ -214,10 +248,10 @@ class HTTPClient { netlog_socket_event(&log_opts, &ev); if (sock) sock->~TCPSocket(); - if (sock) free_sized(sock, sizeof(TCPSocket)); + if (sock) release(sock); sock = nullptr; - if (tcp_extra) free_sized(tcp_extra, sizeof(SocketExtraOptions)); + if (tcp_extra) release(tcp_extra); tcp_extra = nullptr; log_opts.flags &= ~SOCK_OPT_DEBUG; diff --git a/kernel/networking/application_layer/socket_http_server.hpp b/kernel/networking/application_layer/socket_http_server.hpp index 9e888288..6465c2c1 100644 --- a/kernel/networking/application_layer/socket_http_server.hpp +++ b/kernel/networking/application_layer/socket_http_server.hpp @@ -19,7 +19,7 @@ class HTTPServer { const SocketExtraOptions* tcp_ptr = extra; if (extra && (log_opts.flags & SOCK_OPT_DEBUG)) { - tcp_extra = (SocketExtraOptions*)malloc(sizeof(SocketExtraOptions)); + tcp_extra = (SocketExtraOptions*)zalloc(sizeof(SocketExtraOptions)); if (tcp_extra) { *tcp_extra = *extra; tcp_extra->flags &= ~SOCK_OPT_DEBUG; @@ -27,7 +27,7 @@ class HTTPServer { } } - sock = (TCPSocket*)malloc(sizeof(TCPSocket)); + sock = (TCPSocket*)zalloc(sizeof(TCPSocket)); if (sock) new (sock) TCPSocket(SOCK_ROLE_SERVER, pid, tcp_ptr); } @@ -81,7 +81,7 @@ class HTTPServer { if (!client) return req; string buf = string_repeat('\0', 0); - char tmp[512]; + char tmp[2048]; int hdr_end = -1; while (hdr_end < 0) { @@ -158,29 +158,58 @@ class HTTPServer { uint32_t body_start = hdr_end + 4; uint32_t have = buf.length > body_start ? buf.length - body_start : 0; - uint32_t need = req.headers_common.length; - - if (need > 0) { - while (have < need) { - int64_t r = client->recv(tmp, sizeof(tmp)); - if (r == TCP_WOULDBLOCK) { - msleep(10); - continue; + uint32_t need = req.headers_common.has_length ? req.headers_common.length : 0; + bool bad_request = req.headers_common.bad_length != 0; + char* body_copy = nullptr; + + if (!bad_request && need > 0) { + body_copy = (char*)zalloc(need); + if (!body_copy) bad_request = true; + else { + uint32_t copied = have < need ? have : need; + if (copied) memcpy(body_copy, buf.data + body_start, copied); + + while (copied < need) { + int64_t r = client->recv(body_copy + copied, need - copied); + if (r == TCP_WOULDBLOCK) { + msleep(5); + continue; + } + if (r <= 0) { + bad_request = true; + break; + } + copied += (uint32_t)r; } - if (r < 0) break; - if (r == 0) break; - string_append_bytes(&buf, tmp, (uint32_t)r); - have += (uint32_t)r; } } - if (have > 0) { - char* body_copy = (char*)malloc(have); - if (body_copy) { - memcpy(body_copy, buf.data + body_start, have); - req.body.ptr = (uintptr_t)body_copy; - req.body.size = have; - } + if (bad_request) { + if (body_copy) release(body_copy); + static const char BAD_BODY[] = "bad request\n"; + static const char BAD_REASON[] = "Bad Request"; + static const char BAD_TYPE[] = "text/plain"; + static const char BAD_CONN[] = "close"; + HTTPResponseMsg res{}; + res.status_code = HTTP_BAD_REQUEST; + res.reason = (string) {(char*)BAD_REASON, sizeof(BAD_REASON)-1, 0}; + res.headers_common.length = sizeof(BAD_BODY)-1; + res.headers_common.type = (string){(char*)BAD_TYPE, sizeof(BAD_TYPE)-1, 0}; + res.headers_common.connection = (string){(char*)BAD_CONN, sizeof(BAD_CONN)-1, 0}; + res.body.ptr = (uintptr_t)BAD_BODY; + res.body.size = sizeof(BAD_BODY)-1; + send_response(client, res); + + if (req.path.mem_length) string_free(req.path); + http_headers_common_free(&req.headers_common); + http_headers_extra_free(req.extra_headers, req.extra_header_count); + string_free(buf); + return HTTPRequestMsg{}; + } + + if (body_copy) { + req.body.ptr = (uintptr_t)body_copy; + req.body.size = need; } netlog_socket_event_t ev{}; @@ -258,10 +287,10 @@ class HTTPServer { netlog_socket_event(&log_opts, &ev); if (sock) sock->~TCPSocket(); - if (sock) free_sized(sock, sizeof(TCPSocket)); + if (sock) release(sock); sock = nullptr; - if (tcp_extra) free_sized(tcp_extra, sizeof(SocketExtraOptions)); + if (tcp_extra) release(tcp_extra); tcp_extra = nullptr; log_opts.flags &= ~SOCK_OPT_DEBUG; diff --git a/kernel/networking/processes/net_proc.c b/kernel/networking/processes/net_proc.c index c9554089..644e5fea 100644 --- a/kernel/networking/processes/net_proc.c +++ b/kernel/networking/processes/net_proc.c @@ -88,7 +88,7 @@ static void free_request(HTTPRequestMsg *req) { req->extra_headers = NULL; req->extra_header_count = 0; - if (req->body.ptr && req->body.size) free_sized((void*)req->body.ptr, req->body.size); + if (req->body.ptr && req->body.size) release((void*)req->body.ptr); req->path = (string){0}; req->body = (sizedptr){0}; @@ -224,20 +224,20 @@ static void test_http(const net_l4_endpoint* ep) { } if (resp.body.ptr && resp.body.size > 0) { - char *body_str = (char*)malloc(resp.body.size + 1); + char *body_str = (char*)zalloc(resp.body.size + 1); if (body_str) { memcpy(body_str, (void*)resp.body.ptr, resp.body.size); body_str[resp.body.size] = '\0'; kprintf("[HTTP] %i %i bytes of body", resp.status_code, resp.body.size); kprintf("%s", body_str); - free_sized(body_str, resp.body.size + 1); + release(body_str); } } http_client_close(cli); http_client_destroy(cli); - if (resp.body.ptr && resp.body.size) free_sized((void*)resp.body.ptr, resp.body.size); + if (resp.body.ptr && resp.body.size) release((void*)resp.body.ptr); http_headers_common_free(&resp.headers_common); diff --git a/kernel/networking/transport_layer/socket_tcp.hpp b/kernel/networking/transport_layer/socket_tcp.hpp index 452ef2b9..1451738a 100755 --- a/kernel/networking/transport_layer/socket_tcp.hpp +++ b/kernel/networking/transport_layer/socket_tcp.hpp @@ -14,7 +14,7 @@ #include "networking/transport_layer/trans_utils.h" #include "syscalls/syscalls.h" -static constexpr int TCP_MAX_BACKLOG = 8; +static constexpr int TCP_MAX_BACKLOG = 32; static constexpr dns_server_sel_t TCP_DNS_SEL = DNS_USE_BOTH; static constexpr uint32_t TCP_DNS_TIMEOUT_MS = 3000; diff --git a/kernel/networking/transport_layer/socket_udp.hpp b/kernel/networking/transport_layer/socket_udp.hpp index 6a45eba1..17b92de5 100644 --- a/kernel/networking/transport_layer/socket_udp.hpp +++ b/kernel/networking/transport_layer/socket_udp.hpp @@ -138,6 +138,11 @@ class UDPSocket : public Socket { uint32_t limit = 0xFFFFFFFFu; if ((extraOpts.flags & SOCK_OPT_BUF_SIZE) && extraOpts.buf_size) limit = extraOpts.buf_size; if (len > limit) return; + if (rx_bytes > limit - len) return; + + int nexti = (r_tail + 1) % UDP_RING_CAP; + if (nexti == r_head) return; + uintptr_t copy = 0; if (len) { copy = (uintptr_t)zalloc(len); @@ -145,19 +150,6 @@ class UDPSocket : public Socket { memcpy((void*)copy, (const void*)ptr, len); } - while (rx_bytes + len > limit && r_head != r_tail) { - rx_bytes -= ring[r_head].size; - release((void*)ring[r_head].ptr); - r_head = (r_head + 1) % UDP_RING_CAP; - } - - int nexti = (r_tail + 1) % UDP_RING_CAP; - if (nexti == r_head) { - rx_bytes -= ring[r_head].size; - release((void*)ring[r_head].ptr); - r_head = (r_head + 1) % UDP_RING_CAP; - } - ring[r_tail].ptr = copy; ring[r_tail].size = len; rx_bytes += len; diff --git a/kernel/networking/transport_layer/tcp/tcp_core.c b/kernel/networking/transport_layer/tcp/tcp_core.c index 6e11c668..51ba53a8 100644 --- a/kernel/networking/transport_layer/tcp/tcp_core.c +++ b/kernel/networking/transport_layer/tcp/tcp_core.c @@ -264,6 +264,8 @@ bool tcp_bind_l3(uint8_t l3_id, uint16_t port, uint16_t pid, port_recv_handler_t f->rcv_base = 0; f->rcv_data_nxt = 0; f->rcv_ooo_used = 0; + f->sack_recent_left = 0; + f->sack_recent_right = 0; f->rcv_adv_edge = 0; f->ip_ttl = extra && (extra->flags & SOCK_OPT_TTL) ? extra->ttl : 0; @@ -389,6 +391,8 @@ bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, flow->rcv_base = 0; flow->rcv_data_nxt = 0; flow->rcv_ooo_used = 0; + flow->sack_recent_left = 0; + flow->sack_recent_right = 0; flow->rcv_wnd_max = TCP_DEFAULT_RCV_BUF; if (extra && (extra->flags & SOCK_OPT_BUF_SIZE) && extra->buf_size) flow->rcv_wnd_max = extra->buf_size; flow->rcv_adv_edge = 0; diff --git a/kernel/networking/transport_layer/tcp/tcp_internal.h b/kernel/networking/transport_layer/tcp/tcp_internal.h index b187a525..e14d6139 100644 --- a/kernel/networking/transport_layer/tcp/tcp_internal.h +++ b/kernel/networking/transport_layer/tcp/tcp_internal.h @@ -18,6 +18,7 @@ extern "C" { #endif #define TCP_REASS_MAX_SEGS 32 +#define TCP_REASS_MAX_BYTES (64u * 1024u) #define TCP_DEFAULT_MSS 1460 #define TCP_DEFAULT_RCV_BUF (256u * 1024u) #define TCP_PERSIST_PROBE_BUFSZ 1 @@ -33,6 +34,8 @@ typedef struct { uint8_t rtt_sample; uint8_t retransmit_cnt; uint8_t opts_len; + uint8_t sacked; + uint8_t sack_retransmitted; uint8_t opts[40]; uint32_t seq; uint64_t len; @@ -69,6 +72,8 @@ typedef struct { uint32_t rcv_data_nxt; uintptr_t rcv_buf; uint32_t rcv_ooo_used; + uint32_t sack_recent_left; + uint32_t sack_recent_right; uint32_t rcv_wnd; uint32_t rcv_wnd_max; uint32_t rcv_adv_edge; @@ -98,6 +103,7 @@ typedef struct { uint8_t reass_count; tcp_tx_seg_t txq[TCP_MAX_TX_SEGS]; uint8_t fin_pending; + uint8_t fin_tx_pending; uint32_t fin_seq; uint8_t ip_ttl; @@ -117,6 +123,7 @@ void tcp_rtt_update(tcp_flow_t *flow, uint32_t sample_ms); tcp_tx_seg_t *tcp_alloc_tx_seg(tcp_flow_t *flow); void tcp_send_from_seg(tcp_flow_t *flow, tcp_tx_seg_t *seg); void tcp_send_ack_now(tcp_flow_t *flow); +int tcp_try_send_pending_fin(tcp_flow_t *flow); static inline uint16_t tcp_checksum_ipv4(const void *segment, uint16_t seg_len, uint32_t src_ip, uint32_t dst_ip) { uint16_t csum = checksum16_pipv4(src_ip, dst_ip, 6, (const uint8_t *)segment, seg_len); diff --git a/kernel/networking/transport_layer/tcp/tcp_rx.c b/kernel/networking/transport_layer/tcp/tcp_rx.c index a881c106..2c5a7e73 100644 --- a/kernel/networking/transport_layer/tcp/tcp_rx.c +++ b/kernel/networking/transport_layer/tcp/tcp_rx.c @@ -101,7 +101,12 @@ uint32_t tcp_flow_readable(tcp_data *flow_ctx) { bool tcp_flow_recv_closed(tcp_data *flow_ctx) { tcp_flow_t *flow = tcp_flow_from_ctx(flow_ctx); if (!flow) return true; - if (flow->state == TCP_CLOSE_WAIT || flow->state == TCP_STATE_CLOSED || flow->state == TCP_TIME_WAIT) return true; + if (flow->state == TCP_STATE_CLOSED || flow->state == TCP_TIME_WAIT) return true; + if (flow->state == TCP_CLOSE_WAIT) { + if (!flow->rcv_buf || !flow->rcv_wnd_max) return true; + if (flow->rcv_data_nxt < flow->rcv_base) return true; + return flow->rcv_data_nxt == flow->rcv_base; + } return false; } @@ -126,6 +131,71 @@ tcp_tx_seg_t *tcp_find_first_unacked(tcp_flow_t *flow) { return best; } +static int tcp_apply_sack_blocks(tcp_flow_t *flow, const tcp_parsed_opts_t *opts) { + if (!flow || !opts || !flow->sack_ok || !opts->sack_count) return 0; + + int changed = 0; + for (uint32_t b = 0; b < opts->sack_count; b++) { + uint32_t left = opts->sacks[b].left; + uint32_t right = opts->sacks[b].right; + + if (right <= left) continue; + if (right <= flow->snd_una) continue; + if (left >= flow->snd_nxt) continue; + + for (int i = 0; i < TCP_MAX_TX_SEGS; i++) { + tcp_tx_seg_t *s = &flow->txq[i]; + + if (!s->used) continue; + + uint32_t end = s->seq + s->len + (s->syn ? 1 : 0) + (s->fin ? 1 : 0); + if (end <= flow->snd_una) continue; + if (s->seq < left || end > right) continue; + + if (!s->sacked) changed = 1; + s->sacked = 1; + } + } + + return changed; +} + +static tcp_tx_seg_t *tcp_find_sack_retransmit(tcp_flow_t *flow) { + tcp_tx_seg_t *best = NULL; + uint32_t best_seq = 0; + uint32_t highest_sacked = flow->snd_una; + + for (int i = 0; i < TCP_MAX_TX_SEGS; i++) { + tcp_tx_seg_t *s = &flow->txq[i]; + + if (!s->used || !s->sacked) continue; + + uint32_t end = s->seq + s->len + (s->syn ? 1u : 0u) + (s->fin ? 1u : 0u); + if (end <= flow->snd_una) continue; + if (end > highest_sacked) highest_sacked = end; + } + + if (highest_sacked == flow->snd_una) return NULL; + + for (int i = 0; i < TCP_MAX_TX_SEGS; i++) { + tcp_tx_seg_t *s = &flow->txq[i]; + + if (!s->used)continue; + if (s->sacked || s->sack_retransmitted) continue; + + uint32_t end = s->seq + s->len + (s->syn ? 1 : 0) + (s->fin ? 1 : 0); + if (end <= flow->snd_una) continue; + if (s->seq >= highest_sacked) continue; + + if (!best || s->seq < best_seq) { + best = s; + best_seq = s->seq; + } + } + + return best; +} + void tcp_cc_on_timeout(tcp_flow_t *f){ uint32_t mss = f->mss ? f->mss : TCP_DEFAULT_MSS; uint32_t flight = f->snd_nxt > f->snd_una ? f->snd_nxt - f->snd_una : 0; @@ -140,6 +210,11 @@ void tcp_cc_on_timeout(tcp_flow_t *f){ f->dup_acks = 0; f->in_fast_recovery = 0; f->recover = 0; + + for (int i = 0; i < TCP_MAX_TX_SEGS; i++) { + f->txq[i].sacked = 0; + f->txq[i].sack_retransmitted = 0; + } } static void tcp_cc_on_new_ack(tcp_flow_t *f, uint32_t ack) { @@ -180,6 +255,15 @@ static void tcp_cc_on_dupack(tcp_flow_t *f) { if (f->in_fast_recovery){ f->cwnd += mss; + + tcp_tx_seg_t *s = tcp_find_sack_retransmit(f); + if (s) { + tcp_send_from_seg(f, s); + s->sack_retransmitted = 1; + s->retransmit_cnt++; + s->timer_ms = 0; + } + return; } @@ -196,9 +280,11 @@ static void tcp_cc_on_dupack(tcp_flow_t *f) { f->cwnd = f->ssthresh + 3u * mss; f->in_fast_recovery = 1; - tcp_tx_seg_t *s = tcp_find_first_unacked(f); + tcp_tx_seg_t *s = tcp_find_sack_retransmit(f); + if (!s) s = tcp_find_first_unacked(f); if (s) { tcp_send_from_seg(f, s); + s->sack_retransmitted = 1; s->retransmit_cnt++; s->timer_ms = 0; } @@ -250,6 +336,15 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a uint32_t data_len = len - hdr_len; + tcp_parsed_opts_t parsed_opts; + uint8_t parsed_opts_buf[40]; + uint32_t parsed_opts_len = (uint32_t)(hdr_len > sizeof(tcp_hdr_t) ? hdr_len - sizeof(tcp_hdr_t) : 0); + if (parsed_opts_len && !netpkt_copyout(pkt, sizeof(tcp_hdr_t), parsed_opts_buf, parsed_opts_len)) { + netpkt_unref(pkt); + return; + } + tcp_parse_options(parsed_opts_len ? parsed_opts_buf : NULL, parsed_opts_len, &parsed_opts); + int idx = find_flow(dst_port, ipver, dst_ip_addr, src_ip_addr, src_port); tcp_flow_t *flow = idx >= 0 ? tcp_flows[idx] : NULL; if (flow) flow->keepalive_idle_ms = 0; @@ -339,15 +434,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a flow->state = TCP_SYN_RECEIVED; flow->retries = TCP_SYN_RETRIES; - tcp_parsed_opts_t pop; - uint8_t rx_opts_buf[40]; - uint32_t rx_opts_len = (uint32_t)(hdr_len > sizeof(tcp_hdr_t) ? hdr_len - sizeof(tcp_hdr_t) : 0); - if (rx_opts_len && (!netpkt_copyout(pkt, sizeof(tcp_hdr_t), rx_opts_buf, rx_opts_len))) { - tcp_free_flow(idx); - netpkt_unref(pkt); - return; - } - tcp_parse_options(rx_opts_len ? rx_opts_buf : NULL, rx_opts_len, &pop); + tcp_parsed_opts_t pop = parsed_opts; flow->ws_send = lf->ws_send; flow->ws_recv = 0; @@ -402,6 +489,8 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a flow->rcv_base = flow->rcv_nxt; flow->rcv_data_nxt = flow->rcv_nxt; flow->rcv_ooo_used = 0; + flow->sack_recent_left = 0; + flow->sack_recent_right = 0; flow->rcv_buf = (uintptr_t)zalloc(flow->rcv_wnd_max); if (!flow->rcv_buf) { flow->rcv_wnd = 0; @@ -530,9 +619,21 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a s->used = 0; s->buf = 0; s->len = 0; + s->sacked = 0; + s->sack_retransmitted = 0; } } + for (int i = 0; i < TCP_MAX_TX_SEGS; i++) { + tcp_tx_seg_t *s = &flow->txq[i]; + if (!s->used) continue; + if (s->seq >= ack) continue; + + s->sacked = 0; + s->sack_retransmitted = 0; + } + + if (flow->sack_ok && parsed_opts.sack_count) tcp_apply_sack_blocks(flow, &parsed_opts); if (ack > prev_una) tcp_cc_on_new_ack(flow, ack); if (flow->state == TCP_FIN_WAIT_1 && ack >= flow->ctx.expected_ack){ @@ -545,6 +646,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a return; } } else if (ack == flow->snd_una && data_len == 0 && !fin){ + if (flow->sack_ok && parsed_opts.sack_count) tcp_apply_sack_blocks(flow, &parsed_opts); if (flow->dup_acks < UINT8_MAX) flow->dup_acks++; tcp_cc_on_dupack(flow); } else { @@ -552,6 +654,8 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a } } + if (flow->fin_tx_pending) tcp_try_send_pending_fin(flow); + uint32_t seg_seq = seq; switch (flow->state){ @@ -565,14 +669,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a flow->ctx.sequence = flow->snd_nxt; flow->ctx.flags = 0; - tcp_parsed_opts_t pop; - uint8_t ack_opts_buf[40]; - uint32_t ack_opts_len = (uint32_t)(hdr_len > sizeof(tcp_hdr_t) ? hdr_len - sizeof(tcp_hdr_t) : 0); - if (ack_opts_len && (!netpkt_copyout(pkt, sizeof(tcp_hdr_t), ack_opts_buf, ack_opts_len))) { - netpkt_unref(pkt); - return; - } - tcp_parse_options(ack_opts_len ? ack_opts_buf : NULL, ack_opts_len, &pop); + tcp_parsed_opts_t pop = parsed_opts; flow->ws_recv = pop.has_wscale ? pop.wscale : 0; if (flow->ws_recv > 14) flow->ws_recv = 14; @@ -602,6 +699,8 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a flow->rcv_base = flow->rcv_nxt; flow->rcv_data_nxt = flow->rcv_nxt; flow->rcv_ooo_used = 0; + flow->sack_recent_left = 0; + flow->sack_recent_right = 0; if (!flow->rcv_buf || !flow->rcv_wnd_max) { flow->rcv_wnd = 0; flow->rcv_adv_edge = flow->rcv_nxt; @@ -849,84 +948,77 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a } if (ooo_len) { - uint32_t orig_seq = ooo_seq; uint32_t start = ooo_seq; uint32_t end = ooo_seq + ooo_len; - - for (;;) { - int changed = 0; - for (int i = 0; i < flow->reass_count; i++) { - tcp_reass_seg_t *r = &flow->reass[i]; - uint32_t rs = r->seq; - uint32_t re = r->end; - - if (end <= rs || start >= re) continue; - if (start >= rs && end <= re) { - ooo_len = 0; - break; - } - - if (start <= rs && end >= re) { - tcp_reass_remove(flow, i); - changed = 1; - break; - } - - if (start < rs && end > rs && end <= re) { - end = rs; - ooo_len = end - start; - changed = 1; - break; - } - - if (start >= rs && start < re && end > re) { - start = re; - ooo_len = end - start; - changed = 1; - break; - } + uint32_t merged_start = start; + uint32_t merged_end = end; + uint32_t old_bytes = 0; + int16_t overlapping = 0; + int16_t covered = 0; + + for (int i = 0; i < flow->reass_count; i++) { + tcp_reass_seg_t *r = &flow->reass[i]; + if (r->seq <= start && r->end >= end) { + flow->sack_recent_left = r->seq; + flow->sack_recent_right = r->end; + covered = 1; + break; } - if (!changed || !ooo_len) break; + if (r->end < merged_start || r->seq > merged_end) continue; + + if (r->seq < merged_start) merged_start = r->seq; + if (r->end > merged_end) merged_end = r->end; + old_bytes += r->end - r->seq; + overlapping++; } - if (ooo_len && flow->reass_count>= TCP_REASS_MAX_SEGS) { - int drop_idx = 0; - uint32_t best = flow->reass[0].seq; + if (!covered) { + uint32_t merged_len = merged_end - merged_start; + uint32_t increase = merged_len > old_bytes ? merged_len - old_bytes : 0; + uint32_t ooo_limit = flow->rcv_wnd_max >> 1; - for (int i = 1; i < flow->reass_count; i++) { - if (flow->reass[i].seq <= best) continue; - best = flow->reass[i].seq; - drop_idx = i; - } + if (ooo_limit > TCP_REASS_MAX_BYTES) ooo_limit = TCP_REASS_MAX_BYTES; + if (ooo_limit < flow->mss * 4) ooo_limit = flow->mss * 4; + if (ooo_limit > flow->rcv_wnd_max) ooo_limit = flow->rcv_wnd_max; - if (start >= best) ooo_len = 0; - else tcp_reass_remove(flow, drop_idx); + if (increase && (flow->rcv_ooo_used + increase > ooo_limit || flow->reass_count - overlapping >= TCP_REASS_MAX_SEGS)) ooo_len = 0; } - if (ooo_len && flow->reass_count < TCP_REASS_MAX_SEGS) { + if (!covered && ooo_len) { uint8_t *rx = (uint8_t *)flow->rcv_buf; uint32_t cap = flow->rcv_wnd_max; - uint32_t offset = start - orig_seq; uint32_t pos = start % cap; uint32_t first = cap - pos; int copied = 1; if (first > ooo_len) first = ooo_len; - if (first && !netpkt_copyout(pkt, ooo_data + offset, rx + pos, first)) copied = 0; - if (copied && ooo_len > first && !netpkt_copyout(pkt, ooo_data + offset + first, rx, ooo_len - first)) copied = 0; + if (first && !netpkt_copyout(pkt, ooo_data, rx + pos, first)) copied = 0; + if (copied && ooo_len > first && !netpkt_copyout(pkt, ooo_data + first, rx, ooo_len - first)) copied = 0; if (copied) { + for (int i = 0; i < flow->reass_count;) { + tcp_reass_seg_t *r = &flow->reass[i]; + if (r->end < merged_start || r->seq > merged_end) { + i++; + continue; + } + + tcp_reass_remove(flow,i); + } + int pos_idx = flow->reass_count; - while (pos_idx > 0 &&flow->reass[pos_idx - 1].seq > start) { + while (pos_idx > 0 &&flow->reass[pos_idx - 1].seq > merged_start) { flow->reass[pos_idx] = flow->reass[pos_idx - 1]; pos_idx--; } - flow->reass[pos_idx].seq = start; - flow->reass[pos_idx].end = start + ooo_len; + flow->reass[pos_idx].seq = merged_start; + flow->reass[pos_idx].end = merged_end; flow->reass_count++; - flow->rcv_ooo_used += ooo_len; + flow->rcv_ooo_used += merged_end - merged_start; + flow->sack_recent_left = merged_start; + flow->sack_recent_right = merged_end; tcp_calc_adv_wnd_field(flow, 1); } } @@ -975,7 +1067,7 @@ void tcp_flow_on_app_read(tcp_data *flow_ctx, uint32_t bytes_read){ if (!flow) return; uint32_t old_edge = flow->rcv_adv_edge; - uint32_t old_wnd = flow->rcv_wnd; + uint16_t old_adv_field = flow->ctx.window; uint32_t old_nxt = flow->rcv_nxt; int advanced = tcp_reass_drain_inseq(flow); @@ -989,5 +1081,5 @@ void tcp_flow_on_app_read(tcp_data *flow_ctx, uint32_t bytes_read){ if (!threshold) threshold = 1; uint32_t delta = flow->rcv_adv_edge > old_edge ? flow->rcv_adv_edge - old_edge : 0; - if (advanced || flow->rcv_nxt != old_nxt || (!old_wnd && flow->rcv_wnd) || delta >= threshold) tcp_send_ack_now(flow); + if (advanced || flow->rcv_nxt != old_nxt || (!old_adv_field && flow->ctx.window) || delta >= threshold) tcp_send_ack_now(flow); } diff --git a/kernel/networking/transport_layer/tcp/tcp_timer.c b/kernel/networking/transport_layer/tcp/tcp_timer.c index b3a92597..0e4be14f 100644 --- a/kernel/networking/transport_layer/tcp/tcp_timer.c +++ b/kernel/networking/transport_layer/tcp/tcp_timer.c @@ -33,9 +33,10 @@ int tcp_has_pending_timers(void) { //TODO mhh this should be event driven to avo if (f->state == TCP_TIME_WAIT) return 1; if (f->state == TCP_FIN_WAIT_2) return 1; + if (f->fin_tx_pending && f->snd_wnd == 0) return 1; if (f->delayed_ack_pending) return 1; if (f->persist_active) return 1; - if (f->keepalive_on && f->state == TCP_ESTABLISHED && f->keepalive_ms) return 1; + if (!f->fin_tx_pending && f->keepalive_on && f->state == TCP_ESTABLISHED && f->keepalive_ms) return 1; for (int j = 0; j < TCP_MAX_TX_SEGS; j++) { tcp_tx_seg_t *s = &f->txq[j]; @@ -75,7 +76,7 @@ void tcp_tick_all(uint32_t elapsed_ms) { if (f->delayed_ack_timer_ms >= TCP_DELAYED_ACK_MS) tcp_send_ack_now(f); } - if (f->keepalive_on && f->state == TCP_ESTABLISHED && f->keepalive_ms) { + if (!f->fin_tx_pending && f->keepalive_on && f->state == TCP_ESTABLISHED && f->keepalive_ms) { f->keepalive_idle_ms += elapsed_ms; if (f->keepalive_idle_ms >= f->keepalive_ms) { tcp_hdr_t hdr; @@ -102,7 +103,7 @@ void tcp_tick_all(uint32_t elapsed_ms) { } } - if (f->snd_wnd == 0 && f->snd_nxt > f->snd_una) { + if (f->snd_wnd == 0 && (f->snd_nxt > f->snd_una || f->fin_tx_pending)) { if (!f->persist_active) { f->persist_active = 1; f->persist_timer_ms = 0; @@ -111,19 +112,8 @@ void tcp_tick_all(uint32_t elapsed_ms) { } else { f->persist_timer_ms += elapsed_ms; if (f->persist_timer_ms >= f->persist_timeout_ms) { - if (f->persist_probe_cnt >= TCP_MAX_PERSIST_PROBES) { - if (f->state == TCP_ESTABLISHED) { - f->ctx.flags = (uint8_t)((1u << FIN_F) | (1u << ACK_F)); - f->ctx.payload.ptr = 0; - f->ctx.payload.size = 0; - - tcp_flow_send(&f->ctx); - f->state = TCP_FIN_WAIT_1; - f->ctx.expected_ack = f->snd_nxt; - tcp_daemon_kick(); - } else { - tcp_free_flow(i); - } + if (f->fin_tx_pending && f->persist_probe_cnt >= TCP_MAX_PERSIST_PROBES) { + tcp_free_flow(i); continue; } tcp_tx_seg_t *best = tcp_find_first_unacked(f); @@ -137,6 +127,7 @@ void tcp_tick_all(uint32_t elapsed_ms) { uint16_t pl = 0; uint32_t probe_seq = f->snd_una; + if (!best && f->fin_tx_pending && f->snd_nxt == f->snd_una && f->snd_nxt) probe_seq = f->snd_nxt-1; if (best && best->buf && best->len && probe_seq >= best->seq && probe_seq < best->seq + best->len) { payload[0] = *((uint8_t *)best->buf + (probe_seq - best->seq)); diff --git a/kernel/networking/transport_layer/tcp/tcp_tx.c b/kernel/networking/transport_layer/tcp/tcp_tx.c index f630534d..a57096bf 100644 --- a/kernel/networking/transport_layer/tcp/tcp_tx.c +++ b/kernel/networking/transport_layer/tcp/tcp_tx.c @@ -6,18 +6,28 @@ uint16_t tcp_calc_adv_wnd_field(tcp_flow_t *flow, uint8_t apply_scale) { uint32_t quantum = 1; if (apply_scale && flow->ws_ok && flow->ws_send) quantum = 1u << flow->ws_send; - uint32_t edge = flow->rcv_nxt; + uint32_t hard_edge = flow->rcv_nxt; if (flow->rcv_buf && flow->rcv_wnd_max) { - edge = flow->rcv_base + flow->rcv_wnd_max; - if (edge < flow->rcv_nxt) edge = flow->rcv_nxt; + hard_edge = flow->rcv_base + flow->rcv_wnd_max; + if (hard_edge < flow->rcv_nxt) hard_edge = flow->rcv_nxt; } if (flow->rcv_adv_edge < flow->rcv_nxt) flow->rcv_adv_edge = flow->rcv_nxt; - if (edge > flow->rcv_adv_edge) flow->rcv_adv_edge = edge; + uint32_t accept_edge = flow->rcv_adv_edge; + uint32_t accept_adv = accept_edge - flow->rcv_nxt; - uint32_t adv = flow->rcv_adv_edge - flow->rcv_nxt; + uint32_t adv = accept_adv; + if (hard_edge > accept_edge) adv = hard_edge - flow->rcv_nxt; if (quantum > 1) adv &= ~(quantum - 1); + if (apply_scale && adv) { + uint32_t threshold = flow->mss ? flow->mss : TCP_DEFAULT_MSS; + uint32_t half = flow->rcv_wnd_max >> 1; + if (half && half < threshold) threshold = half; + if (!threshold) threshold = 1; + if (adv < threshold) adv = 0; + } + uint32_t field = adv; if (!apply_scale || !flow->ws_ok || flow->ws_send == 0) { if (field > 65535u) field = 65535u; @@ -28,8 +38,14 @@ uint16_t tcp_calc_adv_wnd_field(tcp_flow_t *flow, uint8_t apply_scale) { adv = field << flow->ws_send; } - flow->rcv_wnd = adv; - flow->rcv_adv_edge = flow->rcv_nxt + adv; + if (adv) { + flow->rcv_wnd = adv; + flow->rcv_adv_edge = flow->rcv_nxt + adv; + } else { + flow->rcv_wnd = accept_adv; + flow->rcv_adv_edge = accept_edge; + } + flow->ctx.window = (uint16_t)field; return (uint16_t)field; } @@ -55,6 +71,8 @@ tcp_tx_seg_t *tcp_alloc_tx_seg(tcp_flow_t *flow){ s->rtt_sample = 0; s->retransmit_cnt = 0; s->opts_len = 0; + s->sacked = 0; + s->sack_retransmitted = 0; memset(s->opts, 0, sizeof(s->opts)); s->seq = 0; s->len = 0; @@ -118,34 +136,50 @@ void tcp_send_ack_now(tcp_flow_t *flow){ opts_len = 0; if (flow->sack_ok && flow->reass_count > 0) { - uint32_t n = flow->reass_count; - if (n > 4) n = 4; + tcp_sack_block_t blocks[TCP_SACK_MAX_BLOCKS]; + uint32_t n = 0; + + if (flow->sack_recent_right > flow->sack_recent_left) { + for (uint32_t i = 0; i < flow->reass_count; i++) { + if (flow->reass[i].seq > flow->sack_recent_left) continue; + if (flow->reass[i].end < flow->sack_recent_right) continue; + + blocks[n].left = flow->reass[i].seq; + blocks[n].right = flow->reass[i].end; + n++; + break; + } + } + + for (uint32_t i = 0; i < flow->reass_count && n < TCP_SACK_MAX_BLOCKS; i++) { + uint32_t left = flow->reass[i].seq; + uint32_t right = flow->reass[i].end; + int duplicate = 0; + + for (uint32_t j = 0; j < n; j++) { + if (blocks[j].left == left && blocks[j].right == right) { + duplicate = 1; + break; + } + } + + if (duplicate) continue; + blocks[n].left = left; + blocks[n].right = right; + n++; + } uint32_t need = 2 + 8 * n; uint32_t pad = (4 - (need & 3)) & 3; - if (need + pad <= sizeof(opts)) { + if (n && need + pad <= sizeof(opts)) { opts[0] = 5; opts[1] = (uint8_t)need; uint32_t o = 2; - uint32_t idx[4]; - for (uint32_t i = 0; i < n; i++) idx[i] = i; - - for (uint32_t i = 0; i + 1 < n; i++) { - for (uint32_t j = i + 1; j < n; j++) { - if ((int32_t)(flow->reass[idx[j]].seq > flow->reass[idx[i]].seq)) { - uint32_t t = idx[i]; - idx[i] = idx[j]; - idx[j] = t; - } - } - } - for (uint32_t i = 0; i < n; i++) { - const tcp_reass_seg_t *s = &flow->reass[idx[i]]; - uint32_t left = s->seq; - uint32_t right = s->end; + uint32_t left = blocks[i].left; + uint32_t right = blocks[i].right; opts[o + 0] = (uint8_t)(left >> 24); opts[o + 1] = (uint8_t)(left >> 16); @@ -179,6 +213,43 @@ void tcp_send_ack_now(tcp_flow_t *flow){ tcp_daemon_kick(); } +int tcp_try_send_pending_fin(tcp_flow_t *flow) { + if (!flow || !flow->fin_tx_pending) return 0; + if (flow->state != TCP_ESTABLISHED&& flow->state != TCP_CLOSE_WAIT) return 0; + + uint64_t in_flight = flow->snd_nxt - flow->snd_una; + uint32_t wnd = flow->snd_wnd; + uint32_t cwnd = flow->cwnd ? flow->cwnd : (flow->mss ? flow->mss : TCP_DEFAULT_MSS); + uint32_t eff_wnd = wnd < cwnd ? wnd : cwnd; + + if (eff_wnd == 0 || in_flight >= eff_wnd) return 0; + tcp_tx_seg_t *seg = tcp_alloc_tx_seg(flow); + if (!seg) return 0; + + seg->seq = flow->snd_nxt; + seg->len = 0; + seg->buf = 0; + seg->syn = 0; + seg->fin = 1; + seg->timer_ms = 0; + seg->timeout_ms = flow->rto ? flow->rto : TCP_INIT_RTO; + seg->retransmit_cnt = 0; + seg->rtt_sample = 0; + + tcp_send_from_seg(flow, seg); + + flow->snd_nxt += 1; + flow->ctx.expected_ack = flow->snd_nxt; + flow->ctx.sequence = flow->snd_nxt; + flow->fin_tx_pending = 0; + + if (flow->state == TCP_ESTABLISHED) flow->state = TCP_FIN_WAIT_1; + else flow->state = TCP_LAST_ACK; + + tcp_daemon_kick(); + return 1; +} + tcp_result_t tcp_flow_send(tcp_data *flow_ctx){ if (!flow_ctx) return TCP_INVALID; @@ -196,12 +267,12 @@ tcp_result_t tcp_flow_send(tcp_data *flow_ctx){ uint8_t *payload_ptr = (uint8_t *)flow_ctx->payload.ptr; uint64_t payload_len = flow_ctx->payload.size; flow_ctx->payload.size = 0; + int want_fin = (flags & (1u << FIN_F)) != 0; + int fin_queued = 0; - if (flow->state != TCP_ESTABLISHED && !(flags & (1u << FIN_F))) { - if (!(flow->state == TCP_CLOSE_WAIT && (flags & (1u << FIN_F)))) return TCP_INVALID; - } + if (flow->state != TCP_ESTABLISHED && flow->state != TCP_CLOSE_WAIT) return TCP_INVALID; - if (flow->snd_wnd == 0 && !(flags & (1u << FIN_F))) { + if (flow->snd_wnd == 0 && (!want_fin || payload_len > 0)) { tcp_persist_arm(flow); return TCP_WOULDBLOCK; } @@ -211,11 +282,10 @@ tcp_result_t tcp_flow_send(tcp_data *flow_ctx){ uint32_t cwnd = flow->cwnd ? flow->cwnd : (flow->mss ? flow->mss : TCP_DEFAULT_MSS); uint32_t eff_wnd = wnd < cwnd ? wnd : cwnd; - if (eff_wnd == 0) eff_wnd = 1; - if (in_flight >= eff_wnd && !(flags & (1u << FIN_F))) return TCP_WOULDBLOCK; + if (in_flight >= eff_wnd && !want_fin) return TCP_WOULDBLOCK; - uint64_t can_send = eff_wnd - in_flight; - if (can_send == 0 && !(flags & (1u << FIN_F))) return TCP_WOULDBLOCK; + uint64_t can_send = in_flight < eff_wnd ? eff_wnd - in_flight :0; + if (can_send == 0 && !want_fin) return TCP_WOULDBLOCK; uint64_t remaining = payload_len; uint64_t sent_bytes = 0; @@ -255,24 +325,10 @@ tcp_result_t tcp_flow_send(tcp_data *flow_ctx){ first_segment = 0; } - if ((flags & (1u << FIN_F)) && remaining == 0) { - tcp_tx_seg_t *seg = tcp_alloc_tx_seg(flow); - if (!seg) return sent_bytes ? TCP_OK : TCP_WOULDBLOCK; - - seg->seq = flow->snd_nxt; - seg->len = 0; - seg->buf = 0; - seg->syn = 0; - seg->fin = 1; - seg->timer_ms = 0; - seg->timeout_ms = flow->rto ? flow->rto : TCP_INIT_RTO; - seg->retransmit_cnt = 0; - seg->rtt_sample = 0; - - tcp_send_from_seg(flow, seg); - - flow->snd_nxt += 1; - flow->ctx.expected_ack = flow->snd_nxt; + if (want_fin && remaining == 0) { + flow->fin_tx_pending = 1; + fin_queued = 1; + tcp_try_send_pending_fin(flow); } flow_ctx->sequence = flow->snd_nxt; @@ -281,7 +337,8 @@ tcp_result_t tcp_flow_send(tcp_data *flow_ctx){ tcp_daemon_kick(); flow_ctx->payload.size = sent_bytes; - return sent_bytes || (flags & (1u << FIN_F)) ? TCP_OK : TCP_WOULDBLOCK; + if (!sent_bytes && fin_queued && flow->fin_tx_pending) return TCP_WOULDBLOCK; + return (sent_bytes || fin_queued) ? TCP_OK : TCP_WOULDBLOCK; } tcp_result_t tcp_flow_close(tcp_data *flow_ctx){ @@ -298,20 +355,10 @@ 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->snd_nxt; - flow_ctx->ack = flow->ctx.ack; - flow_ctx->window = tcp_calc_adv_wnd_field(flow, 1); - flow_ctx->payload.ptr = 0; - flow_ctx->payload.size = 0; - flow_ctx->flags = (uint8_t)((1u << FIN_F) | (1u << ACK_F)); - - tcp_result_t res = tcp_flow_send(flow_ctx); - if (res == TCP_OK || res == TCP_WOULDBLOCK) { - if (flow->state == TCP_ESTABLISHED) flow->state = TCP_FIN_WAIT_1; - else flow->state = TCP_LAST_ACK; - } + flow->fin_tx_pending = 1; + tcp_try_send_pending_fin(flow); tcp_daemon_kick(); - return res; + return TCP_OK; } return TCP_INVALID; diff --git a/kernel/networking/transport_layer/tcp/tcp_utils.c b/kernel/networking/transport_layer/tcp/tcp_utils.c index 56643c9a..2becd325 100644 --- a/kernel/networking/transport_layer/tcp/tcp_utils.c +++ b/kernel/networking/transport_layer/tcp/tcp_utils.c @@ -66,6 +66,8 @@ void tcp_parse_options(const uint8_t *opts, uint32_t len, tcp_parsed_opts_t *out out->sack_permitted = 0; out->has_mss = 0; out->has_wscale = 0; + out->sack_count = 0; + memset(out->sacks, 0, sizeof(out->sacks)); if (!opts || len == 0) return; @@ -84,13 +86,32 @@ void tcp_parse_options(const uint8_t *opts, uint32_t len, tcp_parsed_opts_t *out if (i + olen > len) break; if (kind == 2 && olen == 4) { - out->mss = (uint16_t)((opts[i + 2] << 8) | opts[i + 3]); + out->mss = rd_be16(&opts[i + 2]); out->has_mss = 1; } else if (kind == 3 && olen == 3) { - out->wscale =opts[i + 2]; - out->has_wscale = 1; + uint8_t ws =opts[i + 2]; + if (ws <= 14) { + out->wscale = ws; + out->has_wscale = 1; + } } else if (kind == 4 && olen == 2) { out->sack_permitted = 1; + } else if (kind == 5 && olen >= 10 && ((olen - 2) % 8) == 0) { + uint32_t blocks = (uint32_t)((olen - 2) / 8); + if (blocks > TCP_SACK_MAX_BLOCKS) blocks = TCP_SACK_MAX_BLOCKS; + + for (uint32_t b = 0; b < blocks; b++) { + uint32_t o = i + 2 + b*8; + uint32_t left = rd_be32(&opts[o]); + uint32_t right = rd_be32(&opts[o+4]); + if (right <= left) continue; + + uint8_t n = out->sack_count; + if (n >= TCP_SACK_MAX_BLOCKS) break; + out->sacks[n].left = left; + out->sacks[n].right = right; + out->sack_count = (uint8_t)(n+1); + } } i += olen; diff --git a/kernel/networking/transport_layer/tcp/tcp_utils.h b/kernel/networking/transport_layer/tcp/tcp_utils.h index 30e9c9b9..b79c51e2 100644 --- a/kernel/networking/transport_layer/tcp/tcp_utils.h +++ b/kernel/networking/transport_layer/tcp/tcp_utils.h @@ -11,12 +11,20 @@ extern "C" { #endif +#define TCP_SACK_MAX_BLOCKS 4 +typedef struct { + uint32_t left; + uint32_t right; +} tcp_sack_block_t; + typedef struct { uint16_t mss; uint8_t wscale; uint8_t sack_permitted; uint8_t has_mss; uint8_t has_wscale; + uint8_t sack_count; + tcp_sack_block_t sacks[TCP_SACK_MAX_BLOCKS]; } tcp_parsed_opts_t; void tcp_parse_options(const uint8_t *opts, uint32_t len, tcp_parsed_opts_t *out); From 6764f50380393fdd32c622f80430b43bc8ff30c2 Mon Sep 17 00:00:00 2001 From: CodeAnarchist Date: Fri, 8 May 2026 22:03:55 +0200 Subject: [PATCH 07/39] [tcp] nagle, minor fixes --- .../application_layer/socket_http_server.hpp | 39 ++- .../networking/transport_layer/socket_tcp.hpp | 28 +- kernel/networking/transport_layer/tcp.h | 3 + .../networking/transport_layer/tcp/tcp_core.c | 96 +++++-- .../transport_layer/tcp/tcp_internal.h | 16 +- .../networking/transport_layer/tcp/tcp_rx.c | 9 +- .../transport_layer/tcp/tcp_timer.c | 15 +- .../networking/transport_layer/tcp/tcp_tx.c | 254 ++++++++++++++---- 8 files changed, 368 insertions(+), 92 deletions(-) diff --git a/kernel/networking/application_layer/socket_http_server.hpp b/kernel/networking/application_layer/socket_http_server.hpp index 6465c2c1..c3a9ff11 100644 --- a/kernel/networking/application_layer/socket_http_server.hpp +++ b/kernel/networking/application_layer/socket_http_server.hpp @@ -4,6 +4,7 @@ #include "http.h" #include "std/std.h" #include "net/socket_types.h" +#include "syscalls/syscalls.h" class HTTPServer { @@ -83,10 +84,17 @@ class HTTPServer { string buf = string_repeat('\0', 0); char tmp[2048]; int hdr_end = -1; + uint32_t start_ms = (uint32_t)get_time(); + uint32_t last_rx_ms = start_ms; while (hdr_end < 0) { int64_t r = client->recv(tmp, sizeof(tmp)); if (r == TCP_WOULDBLOCK) { + uint32_t now = (uint32_t)get_time(); + if ((uint32_t)(now - last_rx_ms) > 1000 || (uint32_t)(now - start_ms) > 15000) { + string_free(buf); + return req; + } msleep(10); continue; } @@ -95,6 +103,11 @@ class HTTPServer { return req; } string_append_bytes(&buf, tmp, (uint32_t)r); + last_rx_ms = (uint32_t)get_time(); + if (buf.length > 16384) { + string_free(buf); + return req; + } hdr_end = find_crlfcrlf(buf.data, buf.length); } @@ -109,6 +122,7 @@ class HTTPServer { while (p + 1u < line_end && buf.data[p] == '\r' && buf.data[p + 1u] == '\n') p += 2; + bool bad_request = false; uint32_t i = p; while (i < line_end && buf.data[i] != ' ') ++i; @@ -119,13 +133,24 @@ class HTTPServer { else if (mlen == 4 && memcmp(method_tok, "POST", 4) == 0) req.method = HTTP_METHOD_POST; else if (mlen == 3 && memcmp(method_tok, "PUT", 3) == 0) req.method = HTTP_METHOD_PUT; else if (mlen == 6 && memcmp(method_tok, "DELETE", 6) == 0) req.method = HTTP_METHOD_DELETE; - else req.method = HTTP_METHOD_GET; + else bad_request = true; + if (i >= line_end) bad_request = true; uint32_t j = (i < line_end) ? (i + 1u) : line_end; + while (j < line_end && buf.data[j] == ' ') ++j; uint32_t path_start = j; while (j < line_end && buf.data[j] != ' ') ++j; + uint32_t path_len = j > path_start ? (j - path_start) : 0; + if (!path_len || j >= line_end) bad_request = true; + + uint32_t version_start = j; + while (version_start < line_end && buf.data[version_start ] == ' ') ++version_start; + uint32_t version_len = line_end > version_start ? (line_end - version_start) : 0; + if (version_len != 8) bad_request = true; + else if (memcmp(buf.data + version_start, "HTTP/1.0", 8) != 0 && memcmp(buf.data + version_start, "HTTP/1.1", 8) != 0) bad_request = true; + req.path = string_repeat('\0', 0); - string_append_bytes(&req.path, buf.data + path_start, j - path_start); + if (path_len) string_append_bytes(&req.path, buf.data + path_start, path_len); if (req.path.length >= 7 && memcmp(req.path.data, "http://", 7) == 0) { uint32_t k = 7; @@ -159,7 +184,7 @@ class HTTPServer { uint32_t body_start = hdr_end + 4; uint32_t have = buf.length > body_start ? buf.length - body_start : 0; uint32_t need = req.headers_common.has_length ? req.headers_common.length : 0; - bool bad_request = req.headers_common.bad_length != 0; + if (req.headers_common.bad_length) bad_request = true; char* body_copy = nullptr; if (!bad_request && need > 0) { @@ -169,9 +194,16 @@ class HTTPServer { uint32_t copied = have < need ? have : need; if (copied) memcpy(body_copy, buf.data + body_start, copied); + uint32_t body_start_ms = (uint32_t)get_time(); + uint32_t body_last_rx_ms = body_start_ms; while (copied < need) { int64_t r = client->recv(body_copy + copied, need - copied); if (r == TCP_WOULDBLOCK) { + uint32_t now = (uint32_t)get_time(); + if ((now - body_last_rx_ms) > 3000 || (now - body_start_ms) > 20000) { + bad_request = true; + break; + } msleep(5); continue; } @@ -180,6 +212,7 @@ class HTTPServer { break; } copied += (uint32_t)r; + body_last_rx_ms = (uint32_t)get_time(); } } } diff --git a/kernel/networking/transport_layer/socket_tcp.hpp b/kernel/networking/transport_layer/socket_tcp.hpp index 1451738a..6831947b 100755 --- a/kernel/networking/transport_layer/socket_tcp.hpp +++ b/kernel/networking/transport_layer/socket_tcp.hpp @@ -28,6 +28,7 @@ class TCPSocket : public Socket { int backlogCap = 0; int backlogLen = 0; TCPSocket* next = nullptr; + int idleAcceptSkips = 0; static bool is_valid_v4_l3_for_bind(l3_ipv4_interface_t* v4) { if (!v4 || !v4->l2) return false; @@ -414,11 +415,14 @@ class TCPSocket : public Socket { } TCPSocket* accept(){ - const int max_iters = 100; + const int max_empty_iters = 100; + const int ready_wait_iters = 3; + const int max_idle_skips = 20; int iter = 0; while (backlogLen == 0){ - if (++iter > max_iters) return nullptr; + idleAcceptSkips = 0; + if (++iter > max_empty_iters) return nullptr; msleep(10); } @@ -429,10 +433,19 @@ class TCPSocket : public Socket { for (int i = 0; i < backlogLen; ++i) { TCPSocket* client = pending[i]; if (!client) continue; - if (!client->flow || tcp_flow_readable(client->flow) || tcp_flow_recv_closed(client->flow)) return pop_pending_at(i); + if (!client->flow || tcp_flow_readable(client->flow) || tcp_flow_recv_closed(client->flow)) { + idleAcceptSkips = 0; + return pop_pending_at(i); + } } - if (++iter > max_iters) return nullptr; + if (++iter > ready_wait_iters) { + if (backlogLen >= backlogCap || ++idleAcceptSkips > max_idle_skips) { + idleAcceptSkips = 0; + return pop_pending_at(0); + } + return nullptr; + } msleep(10); } } @@ -640,6 +653,7 @@ class TCPSocket : public Socket { if (!buf || !len) return 0; if (!connected || !flow) return connected ? TCP_WOULDBLOCK : 0; + tcp_flow_flush(flow); int64_t n = tcp_flow_read(flow, buf, len); if (n != 0) return n; if (tcp_flow_recv_closed(flow)) return 0; @@ -656,7 +670,11 @@ class TCPSocket : public Socket { ev.remote_ep = remoteEP; netlog_socket_event(&extraOpts, &ev); if (connected && flow){ - tcp_flow_close(flow); + if (tcp_flow_is_closed(flow)) tcp_flow_release_closed(flow); + else { + tcp_flow_flush(flow); + tcp_flow_close(flow); + } connected = false; flow = nullptr; } diff --git a/kernel/networking/transport_layer/tcp.h b/kernel/networking/transport_layer/tcp.h index 182afc7f..483886d9 100644 --- a/kernel/networking/transport_layer/tcp.h +++ b/kernel/networking/transport_layer/tcp.h @@ -94,7 +94,10 @@ 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, const SocketExtraOptions* extra); tcp_result_t tcp_flow_send(tcp_data *flow_ctx); +tcp_result_t tcp_flow_flush(tcp_data *flow_ctx); tcp_result_t tcp_flow_close(tcp_data *flow_ctx); +bool tcp_flow_is_closed(tcp_data *flow_ctx); +tcp_result_t tcp_flow_release_closed(tcp_data *flow_ctx); int64_t tcp_flow_read(tcp_data *flow_ctx, void *buf, uint64_t len); uint32_t tcp_flow_readable(tcp_data *flow_ctx); bool tcp_flow_recv_closed(tcp_data *flow_ctx); diff --git a/kernel/networking/transport_layer/tcp/tcp_core.c b/kernel/networking/transport_layer/tcp/tcp_core.c index 51ba53a8..8f98ba5a 100644 --- a/kernel/networking/transport_layer/tcp/tcp_core.c +++ b/kernel/networking/transport_layer/tcp/tcp_core.c @@ -61,6 +61,32 @@ tcp_data *tcp_get_ctx(uint16_t local_port, ip_version_t ver, const void *local_i return &tcp_flows[idx]->ctx; } +static void tcp_clear_flow_buffers(tcp_flow_t *f) { + if (!f) return; + + for (int i = 0; i < TCP_MAX_TX_SEGS; i++) { + tcp_tx_seg_t *s = &f->txq[i]; + if (s->used && s->buf && s->len)release((void*)s->buf); + memset(s, 0, sizeof(*s)); + } + + if (f->nagle_buf) release((void*)f->nagle_buf); + f->nagle_buf = 0; + f->nagle_len = 0; + f->nagle_cap = 0; + f->nagle_timer_ms = 0; + f->nagle_flushing = 0; + f->nagle_appending = 0; + + if (f->rcv_buf) release((void*)f->rcv_buf); + f->rcv_buf = 0; + f->rcv_base = f->rcv_nxt; + f->rcv_data_nxt = f->rcv_nxt; + f->rcv_ooo_used = 0; + f->reass_count = 0; + memset(f->reass, 0, sizeof(f->reass)); +} + tcp_flow_t *tcp_alloc_flow(void){ for (int i = 0; i < MAX_TCP_FLOWS; i++){ if (tcp_flows[i]) continue; @@ -74,7 +100,7 @@ tcp_flow_t *tcp_alloc_flow(void){ f->rcv_wnd = f->rcv_wnd_max; f->mss = TCP_DEFAULT_MSS; - f->cwnd = f->mss; + f->cwnd = f->mss * TCP_INIT_CWND_SEGS; f->ssthresh = TCP_RECV_WINDOW; return f; } @@ -82,33 +108,65 @@ tcp_flow_t *tcp_alloc_flow(void){ return NULL; } -void tcp_free_flow(int idx) { +void tcp_abort_flow(int idx) { if (idx < 0 || idx >= MAX_TCP_FLOWS) return; tcp_flow_t *f = tcp_flows[idx]; if (!f) return; - for (int i = 0; i < TCP_MAX_TX_SEGS; i++){ - tcp_tx_seg_t *s = &f->txq[i]; - if (s->used && s->buf && s->len) release((void *)s->buf); - } - if (f->rcv_buf) release((void *)f->rcv_buf); + tcp_clear_flow_buffers(f); + f->state = TCP_STATE_CLOSED; + f->ctx.flags = 0; + f->ctx.payload.ptr = 0; + f->ctx.payload.size = 0; + f->ctx.options.ptr = 0; + f->ctx.options.size = 0; + f->fin_pending = 0; + f->fin_tx_pending = 0; + f->delayed_ack_pending = 0; + f->persist_active = 0; + f->keepalive_on = 0; +} + +void tcp_free_flow(int idx) { + if (idx < 0 || idx >= MAX_TCP_FLOWS) return; + + tcp_flow_t *f = tcp_flows[idx]; + if (!f) return; + tcp_clear_flow_buffers(f); memset(f, 0, sizeof(*f)); + release(f); + tcp_flows[idx] = NULL; +} - f->state = TCP_STATE_CLOSED; +bool tcp_flow_is_closed(tcp_data *flow_ctx) { + if (!flow_ctx) return true; + + for (int i = 0; i < MAX_TCP_FLOWS; i++) { + tcp_flow_t *f = tcp_flows[i]; + if (!f) continue; + if (&f->ctx != flow_ctx) continue; + return f->state == TCP_STATE_CLOSED || f->state == TCP_TIME_WAIT; + } - f->rto = TCP_INIT_RTO; + return true; +} - f->rcv_wnd_max = TCP_DEFAULT_RCV_BUF; - f->rcv_wnd = f->rcv_wnd_max; +tcp_result_t tcp_flow_release_closed(tcp_data *flow_ctx) { + if (!flow_ctx) return TCP_INVALID; - f->mss = TCP_DEFAULT_MSS; - f->cwnd = f->mss; - f->ssthresh = TCP_RECV_WINDOW; + for (int i = 0; i < MAX_TCP_FLOWS; i++) { + tcp_flow_t *f = tcp_flows[i]; + if (!f) continue; + if (&f->ctx != flow_ctx) continue; - release(f); - tcp_flows[idx] = NULL; + if (f->state != TCP_STATE_CLOSED && f->state != TCP_TIME_WAIT) return TCP_BUSY; + tcp_free_flow(i); + return TCP_OK; + } + + return TCP_INVALID; } bool tcp_send_segment(ip_version_t ver, const void *src_ip_addr, const void *dst_ip_addr, tcp_hdr_t *hdr, const uint8_t *opts, uint8_t opts_len, const uint8_t *payload, uint16_t payload_len, const ip_tx_opts_t *txp, uint8_t ttl, uint8_t dontfrag){ @@ -438,7 +496,7 @@ bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, flow->snd_nxt = iss; flow->snd_wnd = 0; - flow->cwnd = flow->mss; + flow->cwnd = flow->mss * TCP_INIT_CWND_SEGS; flow->ssthresh = TCP_RECV_WINDOW; flow->dup_acks = 0; flow->in_fast_recovery = 0; @@ -477,10 +535,10 @@ bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, uint64_t waited = 0; const uint64_t interval = 50; - const uint64_t max_wait = (uint64_t)TCP_MAX_RTO * (uint64_t)(TCP_SYN_RETRIES + 1); + const uint64_t max_wait = TCP_CONNECT_TIMEOUT_MS; while (waited < max_wait){ - if (flow->state == TCP_ESTABLISHED){ + if (flow->state == TCP_ESTABLISHED || flow->state == TCP_CLOSE_WAIT){ tcp_data *ctx = tcp_get_ctx(local_port, dst->ver, flow->local.ip, dst->ip, dst->port); if (!ctx) return false; diff --git a/kernel/networking/transport_layer/tcp/tcp_internal.h b/kernel/networking/transport_layer/tcp/tcp_internal.h index e14d6139..815e6655 100644 --- a/kernel/networking/transport_layer/tcp/tcp_internal.h +++ b/kernel/networking/transport_layer/tcp/tcp_internal.h @@ -23,9 +23,13 @@ extern "C" { #define TCP_DEFAULT_RCV_BUF (256u * 1024u) #define TCP_PERSIST_PROBE_BUFSZ 1 -#define TCP_DELAYED_ACK_MS 200 +#define TCP_DELAYED_ACK_MS 40 #define TCP_PERSIST_MIN_MS 500 #define TCP_PERSIST_MAX_MS 60000 +#define TCP_INIT_CWND_SEGS 4 +#define TCP_NAGLE_FLUSH_THRESHOLD TCP_DEFAULT_MSS +#define TCP_NAGLE_TIMEOUT_MS 10 +#define TCP_CONNECT_TIMEOUT_MS 10000 typedef struct { uint8_t used; @@ -99,6 +103,14 @@ typedef struct { uint8_t delayed_ack_pending; uint32_t delayed_ack_timer_ms; + uint8_t nagle_flushing; + uint8_t nagle_appending; + + uintptr_t nagle_buf; + uint32_t nagle_len; + uint32_t nagle_cap; + uint32_t nagle_timer_ms; + tcp_reass_seg_t reass[TCP_REASS_MAX_SEGS]; uint8_t reass_count; tcp_tx_seg_t txq[TCP_MAX_TX_SEGS]; @@ -117,6 +129,7 @@ extern tcp_flow_t *tcp_flows[MAX_TCP_FLOWS]; tcp_flow_t *tcp_alloc_flow(void); void tcp_free_flow(int idx); +void tcp_abort_flow(int idx); void tcp_rtt_update(tcp_flow_t *flow, uint32_t sample_ms); @@ -124,6 +137,7 @@ tcp_tx_seg_t *tcp_alloc_tx_seg(tcp_flow_t *flow); void tcp_send_from_seg(tcp_flow_t *flow, tcp_tx_seg_t *seg); void tcp_send_ack_now(tcp_flow_t *flow); int tcp_try_send_pending_fin(tcp_flow_t *flow); +uint64_t tcp_flush_nagle(tcp_flow_t *flow, uint8_t force); static inline uint16_t tcp_checksum_ipv4(const void *segment, uint16_t seg_len, uint32_t src_ip, uint32_t dst_ip) { uint16_t csum = checksum16_pipv4(src_ip, dst_ip, 6, (const uint8_t *)segment, seg_len); diff --git a/kernel/networking/transport_layer/tcp/tcp_rx.c b/kernel/networking/transport_layer/tcp/tcp_rx.c index 2c5a7e73..b89536a1 100644 --- a/kernel/networking/transport_layer/tcp/tcp_rx.c +++ b/kernel/networking/transport_layer/tcp/tcp_rx.c @@ -69,6 +69,7 @@ int64_t tcp_flow_read(tcp_data *flow_ctx, void *buf, uint64_t len) { tcp_flow_t *flow = tcp_flow_from_ctx(flow_ctx); if (!flow) return TCP_DISCONNECT; + if (flow->state == TCP_STATE_CLOSED) return TCP_DISCONNECT; if (!flow->rcv_buf || !flow->rcv_wnd_max) return 0; if (flow->rcv_data_nxt < flow->rcv_base) return 0; @@ -93,7 +94,7 @@ int64_t tcp_flow_read(tcp_data *flow_ctx, void *buf, uint64_t len) { uint32_t tcp_flow_readable(tcp_data *flow_ctx) { tcp_flow_t *flow = tcp_flow_from_ctx(flow_ctx); - if (!flow || !flow->rcv_buf || !flow->rcv_wnd_max) return 0; + if (!flow || flow->state == TCP_STATE_CLOSED || !flow->rcv_buf || !flow->rcv_wnd_max) return 0; if (flow->rcv_data_nxt < flow->rcv_base) return 0; return flow->rcv_data_nxt - flow->rcv_base; } @@ -508,7 +509,8 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a flow->keepalive_ms = lf->keepalive_ms; flow->keepalive_idle_ms = 0; - flow->cwnd = flow->mss; + flow->cwnd = flow->mss * TCP_INIT_CWND_SEGS; + if (flow->cwnd < flow->mss) flow->cwnd = flow->mss; flow->ssthresh = TCP_RECV_WINDOW; flow->dup_acks = 0; flow->in_fast_recovery = 0; @@ -654,6 +656,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a } } + if (flow->nagle_len) tcp_flush_nagle(flow, 0); if (flow->fin_tx_pending) tcp_try_send_pending_fin(flow); uint32_t seg_seq = seq; @@ -789,7 +792,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a } if (flags & (1u << RST_F)) { - tcp_free_flow(idx); + tcp_abort_flow(idx); netpkt_unref(pkt); return; } diff --git a/kernel/networking/transport_layer/tcp/tcp_timer.c b/kernel/networking/transport_layer/tcp/tcp_timer.c index 0e4be14f..4d775257 100644 --- a/kernel/networking/transport_layer/tcp/tcp_timer.c +++ b/kernel/networking/transport_layer/tcp/tcp_timer.c @@ -34,6 +34,7 @@ int tcp_has_pending_timers(void) { //TODO mhh this should be event driven to avo if (f->state == TCP_TIME_WAIT) return 1; if (f->state == TCP_FIN_WAIT_2) return 1; if (f->fin_tx_pending && f->snd_wnd == 0) return 1; + if (f->nagle_len) return 1; if (f->delayed_ack_pending) return 1; if (f->persist_active) return 1; if (!f->fin_tx_pending && f->keepalive_on && f->state == TCP_ESTABLISHED && f->keepalive_ms) return 1; @@ -76,6 +77,14 @@ void tcp_tick_all(uint32_t elapsed_ms) { if (f->delayed_ack_timer_ms >= TCP_DELAYED_ACK_MS) tcp_send_ack_now(f); } + if (f->nagle_len) { + if (f->snd_nxt == f->snd_una) tcp_flush_nagle(f, 1); + else { + f->nagle_timer_ms += elapsed_ms; + if (f->nagle_timer_ms >= TCP_NAGLE_TIMEOUT_MS) tcp_flush_nagle(f, 1); + } + } + if (!f->fin_tx_pending && f->keepalive_on && f->state == TCP_ESTABLISHED && f->keepalive_ms) { f->keepalive_idle_ms += elapsed_ms; if (f->keepalive_idle_ms >= f->keepalive_ms) { @@ -113,7 +122,8 @@ void tcp_tick_all(uint32_t elapsed_ms) { f->persist_timer_ms += elapsed_ms; if (f->persist_timer_ms >= f->persist_timeout_ms) { if (f->fin_tx_pending && f->persist_probe_cnt >= TCP_MAX_PERSIST_PROBES) { - tcp_free_flow(i); + if (f->state == TCP_ESTABLISHED || f->state == TCP_CLOSE_WAIT) tcp_abort_flow(i); + else tcp_free_flow(i); continue; } tcp_tx_seg_t *best = tcp_find_first_unacked(f); @@ -176,7 +186,8 @@ void tcp_tick_all(uint32_t elapsed_ms) { if (s->timer_ms < s->timeout_ms) continue; if (s->retransmit_cnt >= TCP_MAX_RETRANS) { - tcp_free_flow(i); + if (f->state == TCP_ESTABLISHED || f->state == TCP_CLOSE_WAIT) tcp_abort_flow(i); + else tcp_free_flow(i); break; } diff --git a/kernel/networking/transport_layer/tcp/tcp_tx.c b/kernel/networking/transport_layer/tcp/tcp_tx.c index a57096bf..0cd18c87 100644 --- a/kernel/networking/transport_layer/tcp/tcp_tx.c +++ b/kernel/networking/transport_layer/tcp/tcp_tx.c @@ -61,6 +61,138 @@ static void tcp_persist_arm(tcp_flow_t *flow) { tcp_daemon_kick(); } +static uint32_t tcp_nagle_threshold(tcp_flow_t *flow) { + uint32_t threshold = flow && flow->mss? flow->mss : TCP_NAGLE_FLUSH_THRESHOLD; + if (!threshold) threshold = 1; + return threshold; +} + +static uint64_t tcp_emit_data(tcp_flow_t *flow, const uint8_t *payload, uint64_t payload_len) { + if (!flow || (!payload && payload_len)) return 0; + if (flow->state != TCP_ESTABLISHED && flow->state != TCP_CLOSE_WAIT) return 0; + + uint64_t in_flight = flow->snd_nxt - flow->snd_una; + uint32_t wnd = flow->snd_wnd; + uint32_t cwnd = flow->cwnd ? flow->cwnd : (flow->mss ? flow->mss : TCP_DEFAULT_MSS); + uint32_t eff_wnd = wnd < cwnd ? wnd : cwnd; + + uint64_t can_send = 0; + if (in_flight < eff_wnd) can_send = eff_wnd - in_flight; + + uint64_t remaining = payload_len; + uint64_t sent_bytes = 0; + int first_segment = 1; + + while (remaining > 0 && can_send > 0) { + uint64_t seg_len = remaining > can_send ? can_send : remaining; + if (flow->mss && seg_len > flow->mss) seg_len = flow->mss; + + tcp_tx_seg_t *seg = tcp_alloc_tx_seg(flow); + if (!seg) break; + + uintptr_t buf = (uintptr_t)zalloc(seg_len); + if (!buf) { + seg->used = 0; + break; + } + + memcpy((void*)buf, payload + sent_bytes, seg_len); + + seg->seq = flow->snd_nxt; + seg->len = seg_len; + seg->buf = buf; + seg->syn = 0; + seg->fin = 0; + seg->timer_ms = 0; + seg->timeout_ms = flow->rto ? flow->rto : TCP_INIT_RTO; + seg->retransmit_cnt = 0; + seg->rtt_sample = 0; + + if (!flow->rtt_valid && first_segment) seg->rtt_sample = 1; + tcp_send_from_seg(flow, seg); + + flow->snd_nxt += seg_len; + sent_bytes += seg_len; + remaining -= seg_len; + can_send -= seg_len; + first_segment = 0; + } + + flow->ctx.sequence = flow->snd_nxt; + return sent_bytes; +} + +static uint64_t tcp_nagle_append(tcp_flow_t *flow, const uint8_t *payload, uint64_t payload_len) { + if (!flow || (!payload && payload_len) || !payload_len) return 0; + if (flow->nagle_flushing || flow->nagle_appending) return 0; + + flow->nagle_appending = 1; + + uint32_t cap = tcp_nagle_threshold(flow); + if (flow->nagle_len >= cap) { + flow->nagle_appending = 0; + return 0; + } + + if (!flow->nagle_buf || flow->nagle_cap < cap) { + uintptr_t nb = (uintptr_t)zalloc(cap); + if (!nb) { + flow->nagle_appending = 0; + return 0; + } + + if (flow->nagle_buf && flow->nagle_len) memcpy((void*)nb, (const void*)flow->nagle_buf, flow->nagle_len); + if (flow->nagle_buf) release((void*)flow->nagle_buf); + + flow->nagle_buf = nb; + flow->nagle_cap = cap; + } + + uint64_t n = payload_len; + uint32_t room = flow->nagle_cap - flow->nagle_len; + if (n > room)n = room; + + memcpy((void*)(flow->nagle_buf + flow->nagle_len), payload, n); + flow->nagle_len += (uint32_t)n; + flow->nagle_appending = 0; + tcp_daemon_kick(); + return n; +} + +uint64_t tcp_flush_nagle(tcp_flow_t *flow, uint8_t force) { + if (!flow || !flow->nagle_len || !flow->nagle_buf) return 0; + if (flow->nagle_flushing || flow->nagle_appending) return 0; + + uint64_t in_flight = flow->snd_nxt - flow->snd_una; + if (!force && in_flight && flow->nagle_lennagle_flushing = 1; + + uint32_t old_len = flow->nagle_len; + uint64_t sent = tcp_emit_data(flow, (const uint8_t*)flow->nagle_buf, old_len); + if (!sent) { + flow->nagle_flushing = 0; + return 0; + } + + if (sent >= old_len) { + release((void*)flow->nagle_buf); + flow->nagle_buf = 0; + flow->nagle_len = 0; + flow->nagle_cap = 0; + flow->nagle_timer_ms = 0; + flow->nagle_flushing = 0; + return sent; + } + + memmove((void*)flow->nagle_buf, (const void*)(flow->nagle_buf + sent), old_len - sent); + flow->nagle_len = old_len - (uint32_t)sent; + flow->nagle_timer_ms = 0; + flow->nagle_flushing = 0; + tcp_daemon_kick(); + return sent; +} + tcp_tx_seg_t *tcp_alloc_tx_seg(tcp_flow_t *flow){ for (int i = 0; i < TCP_MAX_TX_SEGS; i++) { if (!flow->txq[i].used) { @@ -87,6 +219,8 @@ tcp_tx_seg_t *tcp_alloc_tx_seg(tcp_flow_t *flow){ } void tcp_send_from_seg(tcp_flow_t *flow, tcp_tx_seg_t *seg){ + if (!flow || !seg) return; + if (flow->state == TCP_STATE_CLOSED) return; if (flow) flow->keepalive_idle_ms = 0; tcp_hdr_t hdr; @@ -181,14 +315,8 @@ void tcp_send_ack_now(tcp_flow_t *flow){ uint32_t left = blocks[i].left; uint32_t right = blocks[i].right; - opts[o + 0] = (uint8_t)(left >> 24); - opts[o + 1] = (uint8_t)(left >> 16); - opts[o + 2] = (uint8_t)(left >> 8); - opts[o + 3] = (uint8_t)(left); - opts[o + 4] = (uint8_t)(right >> 24); - opts[o + 5] = (uint8_t)(right >> 16); - opts[o + 6] = (uint8_t)(right >> 8); - opts[o + 7] = (uint8_t)(right); + wr_be32(&opts[o], left); + wr_be32(&opts[o + 4], right); o += 8; } @@ -217,6 +345,9 @@ int tcp_try_send_pending_fin(tcp_flow_t *flow) { if (!flow || !flow->fin_tx_pending) return 0; if (flow->state != TCP_ESTABLISHED&& flow->state != TCP_CLOSE_WAIT) return 0; + if (flow->nagle_len) tcp_flush_nagle(flow, 1); + if (flow->nagle_len) return 0; + uint64_t in_flight = flow->snd_nxt - flow->snd_una; uint32_t wnd = flow->snd_wnd; uint32_t cwnd = flow->cwnd ? flow->cwnd : (flow->mss ? flow->mss : TCP_DEFAULT_MSS); @@ -250,6 +381,32 @@ int tcp_try_send_pending_fin(tcp_flow_t *flow) { return 1; } +tcp_result_t tcp_flow_flush(tcp_data *flow_ctx){ + if (!flow_ctx) return TCP_INVALID; + + tcp_flow_t *flow = NULL; + for (int i = 0; i < MAX_TCP_FLOWS; i++) { + if (!tcp_flows[i]) continue; + if (&tcp_flows[i]->ctx == flow_ctx) { + flow = tcp_flows[i]; + break; + } + } + if (!flow) return TCP_INVALID; + + if (flow->state != TCP_ESTABLISHED && flow->state != TCP_CLOSE_WAIT && flow->state != TCP_FIN_WAIT_1 && flow->state != TCP_FIN_WAIT_2) return TCP_INVALID; + + while (flow->nagle_len) { + uint32_t before = flow->nagle_len; + uint64_t sent = tcp_flush_nagle(flow, 1); + if (!sent || flow->nagle_len == before) break; + } + + if (flow->fin_tx_pending) tcp_try_send_pending_fin(flow); + tcp_daemon_kick(); + return flow->nagle_len ? TCP_WOULDBLOCK : TCP_OK; +} + tcp_result_t tcp_flow_send(tcp_data *flow_ctx){ if (!flow_ctx) return TCP_INVALID; @@ -272,62 +429,38 @@ tcp_result_t tcp_flow_send(tcp_data *flow_ctx){ if (flow->state != TCP_ESTABLISHED && flow->state != TCP_CLOSE_WAIT) return TCP_INVALID; - if (flow->snd_wnd == 0 && (!want_fin || payload_len > 0)) { - tcp_persist_arm(flow); - return TCP_WOULDBLOCK; - } - - uint64_t in_flight = flow->snd_nxt - flow->snd_una; - uint32_t wnd = flow->snd_wnd; - uint32_t cwnd = flow->cwnd ? flow->cwnd : (flow->mss ? flow->mss : TCP_DEFAULT_MSS); - uint32_t eff_wnd = wnd < cwnd ? wnd : cwnd; - - if (in_flight >= eff_wnd && !want_fin) return TCP_WOULDBLOCK; - - uint64_t can_send = in_flight < eff_wnd ? eff_wnd - in_flight :0; - if (can_send == 0 && !want_fin) return TCP_WOULDBLOCK; - - uint64_t remaining = payload_len; - uint64_t sent_bytes = 0; - int first_segment = 1; + uint64_t accepted = 0; - while (remaining > 0 && can_send > 0) { - uint64_t seg_len = (uint64_t)(remaining > can_send ? can_send : remaining); - if (flow->mss && seg_len > flow->mss) seg_len = (uint64_t)flow->mss; + if (payload_len && flow->nagle_len) { + uint64_t n = tcp_nagle_append(flow, payload_ptr, payload_len); + accepted += n; + payload_ptr += n; + payload_len -= n; + if (flow->nagle_len >= tcp_nagle_threshold(flow)) tcp_flush_nagle(flow, 1); + } - tcp_tx_seg_t *seg = tcp_alloc_tx_seg(flow); - if (!seg) break; + if (payload_len && payload_len < tcp_nagle_threshold(flow)) { + uint64_t n = tcp_nagle_append(flow, payload_ptr, payload_len); + accepted += n; + payload_ptr += n; + payload_len -= n; + if (flow->nagle_len >= tcp_nagle_threshold(flow)) tcp_flush_nagle(flow, 1); + } - uintptr_t buf = 0; - if (seg_len) { - buf = (uintptr_t)zalloc(seg_len); - if (!buf) { seg->used = 0; break; } - memcpy((void *)buf, payload_ptr + sent_bytes, seg_len); + if (payload_len) { + if (flow->nagle_len) tcp_flush_nagle(flow, 1); + if (!flow->nagle_len) { + uint64_t n = tcp_emit_data(flow, payload_ptr, payload_len); + accepted += n; + payload_ptr += n; + payload_len -= n; } - - seg->seq = flow->snd_nxt; - seg->len = seg_len; - seg->buf = buf; - seg->syn = 0; - seg->fin = 0; - seg->timer_ms = 0; - seg->timeout_ms = flow->rto ? flow->rto : TCP_INIT_RTO; - seg->retransmit_cnt = 0; - seg->rtt_sample = 0; - if (!flow->rtt_valid && first_segment) seg->rtt_sample = 1; - - tcp_send_from_seg(flow, seg); - - flow->snd_nxt += seg_len; - sent_bytes += seg_len; - remaining -= seg_len; - can_send -= seg_len; - first_segment = 0; } - if (want_fin && remaining == 0) { + if (want_fin && payload_len == 0) { flow->fin_tx_pending = 1; fin_queued = 1; + if (flow->nagle_len) tcp_flush_nagle(flow, 1); tcp_try_send_pending_fin(flow); } @@ -336,9 +469,11 @@ tcp_result_t tcp_flow_send(tcp_data *flow_ctx){ tcp_daemon_kick(); - flow_ctx->payload.size = sent_bytes; - if (!sent_bytes && fin_queued && flow->fin_tx_pending) return TCP_WOULDBLOCK; - return (sent_bytes || fin_queued) ? TCP_OK : TCP_WOULDBLOCK; + flow_ctx->payload.size = accepted; + if (!accepted && fin_queued && flow->fin_tx_pending) return TCP_WOULDBLOCK; + if (accepted || fin_queued) return TCP_OK; + if (flow->snd_wnd == 0) tcp_persist_arm(flow); + return TCP_WOULDBLOCK; } tcp_result_t tcp_flow_close(tcp_data *flow_ctx){ @@ -356,6 +491,7 @@ tcp_result_t tcp_flow_close(tcp_data *flow_ctx){ if (flow->state == TCP_ESTABLISHED || flow->state == TCP_CLOSE_WAIT) { flow->fin_tx_pending = 1; + if (flow->nagle_len) tcp_flow_flush(flow_ctx); tcp_try_send_pending_fin(flow); tcp_daemon_kick(); return TCP_OK; From 2f9c11947c57ea745e118f208158177c32c08278 Mon Sep 17 00:00:00 2001 From: CodeAnarchist Date: Mon, 11 May 2026 01:11:13 +0200 Subject: [PATCH 08/39] [net] random crash, tx irq-safe --- .../drivers/loopback/loopback_driver.cpp | 2 - .../drivers/loopback/loopback_driver.hpp | 1 - kernel/networking/drivers/net_driver.hpp | 2 +- .../drivers/virtio_net_pci/virtio_net_pci.cpp | 145 ++++++++++++++---- .../drivers/virtio_net_pci/virtio_net_pci.hpp | 11 ++ kernel/networking/network_dispatch.cpp | 23 ++- 6 files changed, 143 insertions(+), 41 deletions(-) diff --git a/kernel/networking/drivers/loopback/loopback_driver.cpp b/kernel/networking/drivers/loopback/loopback_driver.cpp index 7314e651..ee46f8bc 100644 --- a/kernel/networking/drivers/loopback/loopback_driver.cpp +++ b/kernel/networking/drivers/loopback/loopback_driver.cpp @@ -36,8 +36,6 @@ sizedptr LoopbackDriver::handle_receive_packet(){ return p; } -void LoopbackDriver::handle_sent_packet(){} - void LoopbackDriver::enable_verbose(){ verbose = true; } bool LoopbackDriver::send_packet(sizedptr packet){ diff --git a/kernel/networking/drivers/loopback/loopback_driver.hpp b/kernel/networking/drivers/loopback/loopback_driver.hpp index c110301b..1d50490b 100644 --- a/kernel/networking/drivers/loopback/loopback_driver.hpp +++ b/kernel/networking/drivers/loopback/loopback_driver.hpp @@ -11,7 +11,6 @@ class LoopbackDriver : public NetDriver { bool init_at(uint64_t pci_addr, uint32_t irq_base_vector) override; sizedptr allocate_packet(size_t size) override; sizedptr handle_receive_packet() override; - void handle_sent_packet() override; void enable_verbose() override; bool send_packet(sizedptr packet) override; void get_mac(uint8_t out_mac[6]) const override; diff --git a/kernel/networking/drivers/net_driver.hpp b/kernel/networking/drivers/net_driver.hpp index 2d083da1..a13dbe4b 100644 --- a/kernel/networking/drivers/net_driver.hpp +++ b/kernel/networking/drivers/net_driver.hpp @@ -9,7 +9,7 @@ class NetDriver { 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() = 0; - virtual void handle_sent_packet() = 0; + virtual void handle_sent_packet() {} virtual void enable_verbose() = 0; virtual bool send_packet(sizedptr packet) = 0; virtual void get_mac(uint8_t out_mac[6]) 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 24b2cee9..7573964b 100644 --- a/kernel/networking/drivers/virtio_net_pci/virtio_net_pci.cpp +++ b/kernel/networking/drivers/virtio_net_pci/virtio_net_pci.cpp @@ -97,6 +97,13 @@ bool VirtioNetDriver::init_at(uint64_t addr, uint32_t irq_base_vector) { rx_avail = nullptr; rx_used = nullptr; rx_qsz = 0; + tx_desc = nullptr; + tx_avail = nullptr; + tx_used = nullptr; + tx_qsz = 0; + tx_next_desc = 0; + tx_pending = nullptr; + tx_pending_size = 0; memset(&vnp_net_dev, 0, sizeof(vnp_net_dev)); kprintfv("[virtio-net] probing pci_addr=%x",(uintptr_t)addr); @@ -205,6 +212,24 @@ bool VirtioNetDriver::init_at(uint64_t addr, uint32_t irq_base_vector) { if (TRANSMIT_QUEUE >= vnp_net_dev.num_queues) return false; if (!vnp_net_dev.queues[TRANSMIT_QUEUE].valid) return false; + tx_qsz = vnp_net_dev.queues[TRANSMIT_QUEUE].size; + tx_desc = vnp_net_dev.queues[TRANSMIT_QUEUE].desc; + tx_avail = vnp_net_dev.queues[TRANSMIT_QUEUE].driver; + tx_used = vnp_net_dev.queues[TRANSMIT_QUEUE].device; + if (!tx_qsz || !tx_desc || !tx_avail || !tx_used) return false; + + tx_pending_size = (size_t)tx_qsz * sizeof(sizedptr); + tx_pending = (sizedptr*)kalloc(vnp_net_dev.memory_page, tx_pending_size, ALIGN_64B, MEM_PRIV_KERNEL); + if (!tx_pending) return false; + + memset((void*)tx_desc, 0, 16 * tx_qsz); + tx_avail->flags = 0; + tx_avail->idx = 0; + tx_used->flags = 0; + tx_used->idx = 0; + last_used_sent_idx = 0; + tx_next_desc = 0; + select_queue(&vnp_net_dev, TRANSMIT_QUEUE); vnp_net_dev.common_cfg->queue_msix_vector = 1; kprintfv("[virtio-net] TX vector=%u",vnp_net_dev.common_cfg->queue_msix_vector); @@ -292,21 +317,21 @@ sizedptr VirtioNetDriver::handle_receive_packet(){ uint32_t total_len = 0; uint16_t num_buffers = 1; - disable_interrupt(); + irq_flags_t irq = irq_save_disable(); select_queue(&vnp_net_dev, RECEIVE_QUEUE); volatile virtq_used* used = rx_used; volatile virtq_desc* desc = rx_desc; volatile virtq_avail* avail = rx_avail; uint16_t qsz = rx_qsz; if (!qsz || !used || !desc || !avail) { - enable_interrupt(); + irq_restore(irq); return (sizedptr){0,0}; } asm volatile ("dmb ishld" ::: "memory"); uint16_t new_idx = used->idx; if (new_idx == last_used_receive_idx) { - enable_interrupt(); + irq_restore(irq); return (sizedptr){0,0}; } @@ -324,7 +349,7 @@ sizedptr VirtioNetDriver::handle_receive_packet(){ asm volatile ("dmb ishst" ::: "memory"); select_queue(&vnp_net_dev, RECEIVE_QUEUE); virtio_notify(&vnp_net_dev); - enable_interrupt(); + irq_restore(irq); return (sizedptr){0,0}; } @@ -341,17 +366,17 @@ sizedptr VirtioNetDriver::handle_receive_packet(){ asm volatile ("dmb ishst" ::: "memory"); select_queue(&vnp_net_dev, RECEIVE_QUEUE); virtio_notify(&vnp_net_dev); - enable_interrupt(); + irq_restore(irq); return (sizedptr){0,0}; } } - enable_interrupt(); + irq_restore(irq); uint32_t payload_len = total_len - (uint32_t)header_size; void* out_buf = kalloc(vnp_net_dev.memory_page, payload_len, ALIGN_64B, MEM_PRIV_KERNEL); if (!out_buf){ - disable_interrupt(); + irq = irq_save_disable(); uint16_t aidx = avail->idx; avail->ring[aidx % qsz] = (uint16_t)desc_index; asm volatile ("dmb ishst" ::: "memory"); @@ -359,7 +384,7 @@ sizedptr VirtioNetDriver::handle_receive_packet(){ asm volatile ("dmb ishst" ::: "memory"); select_queue(&vnp_net_dev, RECEIVE_QUEUE); virtio_notify(&vnp_net_dev); - enable_interrupt(); + irq_restore(irq); return (sizedptr){0,0}; } @@ -384,7 +409,7 @@ sizedptr VirtioNetDriver::handle_receive_packet(){ } } - disable_interrupt(); + irq = irq_save_disable(); uint16_t aidx = avail->idx; avail->ring[aidx % qsz] = (uint16_t)desc_index; asm volatile ("dmb ishst" ::: "memory"); @@ -392,7 +417,7 @@ sizedptr VirtioNetDriver::handle_receive_packet(){ asm volatile ("dmb ishst" ::: "memory"); select_queue(&vnp_net_dev, RECEIVE_QUEUE); virtio_notify(&vnp_net_dev); - enable_interrupt(); + irq_restore(irq); if (remaining != 0) { kfree(out_buf, payload_len); @@ -404,28 +429,90 @@ sizedptr VirtioNetDriver::handle_receive_packet(){ void VirtioNetDriver::handle_sent_packet(){ if (TRANSMIT_QUEUE >= vnp_net_dev.num_queues) return; - if (!vnp_net_dev.queues[TRANSMIT_QUEUE].device) return; - last_used_sent_idx = vnp_net_dev.queues[TRANSMIT_QUEUE].device->idx; + if (!tx_qsz || !tx_desc || !tx_used || !tx_pending) return; + + irq_flags_t irq = irq_save_disable(); + select_queue(&vnp_net_dev, TRANSMIT_QUEUE); + asm volatile ("dmb ishld" ::: "memory"); + + uint16_t used_idx = tx_used->idx; + while (last_used_sent_idx != used_idx) { + uint16_t ring_index = (uint16_t)(last_used_sent_idx % tx_qsz); + uint16_t desc_index = (uint16_t)tx_used->ring[ring_index].id; + last_used_sent_idx++; + + if (desc_index >= tx_qsz) continue; + + sizedptr packet = tx_pending[desc_index]; + tx_pending[desc_index] = (sizedptr){0,0}; + tx_desc[desc_index].addr = 0; + tx_desc[desc_index].len = 0; + tx_desc[desc_index].flags = 0; + tx_desc[desc_index].next = 0; + + irq_restore(irq); + if (packet.ptr) kfree((void*)packet.ptr, packet.size); + irq = irq_save_disable(); + select_queue(&vnp_net_dev, TRANSMIT_QUEUE); + asm volatile ("dmb ishld" ::: "memory"); + used_idx = tx_used->idx; + } + + irq_restore(irq); } bool VirtioNetDriver::send_packet(sizedptr packet){ if (!packet.ptr || !packet.size) return false; + if ((size_t)header_size > packet.size) return false; + if (packet.size > 0xFFFFFFFF) return false; + if (!tx_qsz || !tx_desc || !tx_avail || !tx_used || !tx_pending) return false; - disable_interrupt(); - select_queue(&vnp_net_dev, TRANSMIT_QUEUE); - - if ((size_t)header_size <= packet.size) memset((void*)packet.ptr, 0, (size_t)header_size); + memset((void*)packet.ptr, 0, (size_t)header_size); if (mrg_rxbuf) ((virtio_net_hdr_mrg_rxbuf_t*)packet.ptr)->num_buffers = 0; - virtio_buf b; - b.addr = packet.ptr; - b.len = (uint32_t)packet.size; - b.flags = 0; - bool ok = virtio_send_nd(&vnp_net_dev, &b, 1); - enable_interrupt(); - - kprintfv("[virtio-net] tx queued len=%u",(unsigned)packet.size); - if (ok) kfree((void*)packet.ptr, packet.size); - return ok; + + for (uint32_t wait = 0; wait < 1000; wait++) { + handle_sent_packet(); + + irq_flags_t irq = irq_save_disable(); + select_queue(&vnp_net_dev, TRANSMIT_QUEUE); + + bool found = false; + uint16_t desc_index = 0; + for (uint16_t i = 0; i < tx_qsz; i++) { + uint16_t current = (tx_next_desc + i) % tx_qsz; + if (tx_pending[current].ptr) continue; + desc_index = current; + tx_next_desc = (current + 1) % tx_qsz; + found = true; + break; + } + + if (found) { + tx_pending[desc_index] = packet; + tx_desc[desc_index].addr = VIRT_TO_PHYS(packet.ptr); + tx_desc[desc_index].len = (uint32_t)packet.size; + tx_desc[desc_index].flags = 0; + tx_desc[desc_index].next = 0; + + asm volatile ("dmb ishst" ::: "memory"); + uint16_t avail_idx = tx_avail->idx; + tx_avail->ring[avail_idx % tx_qsz] = desc_index; + asm volatile ("dmb ishst" ::: "memory"); + tx_avail->idx = (uint16_t)(avail_idx + 1); + asm volatile ("dmb ishst" ::: "memory"); + virtio_notify(&vnp_net_dev); + irq_restore(irq); + + kprintfv("[virtio-net] tx queued desc=%u len=%u", desc_index,(unsigned)packet.size); + return true; + } + + irq_restore(irq); + msleep(1); + } + + kprintfv("[virtio-net] tx queue full len=%u", packet.size); + return false; } bool VirtioNetDriver::sync_multicast(const uint8_t* macs, uint32_t count) { @@ -433,7 +520,7 @@ bool VirtioNetDriver::sync_multicast(const uint8_t* macs, uint32_t count) { if (!ctrl_rx) return true; if (!macs && count) return false; - disable_interrupt(); + irq_flags_t irq = irq_save_disable(); bool ok = true; @@ -449,7 +536,7 @@ bool VirtioNetDriver::sync_multicast(const uint8_t* macs, uint32_t count) { uint32_t payload_len = 8u + count * 6u; uint8_t* payload = (uint8_t*)kalloc(vnp_net_dev.memory_page, payload_len, ALIGN_16B, MEM_PRIV_KERNEL); if (!payload) { - enable_interrupt(); + irq_restore(irq); return false; } kprintfv("[virtio-net] sync_multicast ctrl_vq=%u ctrl_rx=%u count=%u",(unsigned)ctrl_vq, (unsigned)ctrl_rx, (unsigned)count); @@ -462,7 +549,7 @@ bool VirtioNetDriver::sync_multicast(const uint8_t* macs, uint32_t count) { ok = ok && virtio_net_ctrl_send(&vnp_net_dev, VIRTIO_NET_CTRL_MAC, VIRTIO_NET_CTRL_MAC_TABLE_SET, payload, payload_len); kfree(payload, payload_len); - enable_interrupt(); + irq_restore(irq); return ok; } 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 19b8b4fb..a828fba4 100644 --- a/kernel/networking/drivers/virtio_net_pci/virtio_net_pci.hpp +++ b/kernel/networking/drivers/virtio_net_pci/virtio_net_pci.hpp @@ -80,6 +80,17 @@ class VirtioNetDriver : public NetDriver { volatile virtq_used* rx_used = nullptr; uint16_t rx_qsz = 0; + volatile virtq_desc* tx_desc = nullptr; + volatile virtq_avail* tx_avail = nullptr; + volatile virtq_used* tx_used = nullptr; + uint16_t tx_qsz = 0; + uint16_t tx_next_desc = 0; + //TODO move tx own from sizedptr to netpkt. make it async + //[kfree] untracked ptr=0xffff800044342580 phys=0x44342580 size=0x42 off=0x580 + + sizedptr* tx_pending = nullptr; + size_t tx_pending_size = 0; + bool verbose = false; bool mrg_rxbuf = false; diff --git a/kernel/networking/network_dispatch.cpp b/kernel/networking/network_dispatch.cpp index a405e2d7..adc3c24f 100644 --- a/kernel/networking/network_dispatch.cpp +++ b/kernel/networking/network_dispatch.cpp @@ -15,6 +15,7 @@ #include "networking/netpkt.h" #include "networking/link_layer/link_utils.h" #include "networking/drivers/loopback/loopback_driver.hpp" +#include "exceptions/irq.h" #define RX_INTR_BATCH_LIMIT 64 #define TASK_RX_BATCH_LIMIT 256 @@ -78,9 +79,7 @@ void NetworkDispatch::handle_rx_irq(size_t nic_id) void NetworkDispatch::handle_tx_irq(size_t nic_id) { if (nic_id >= nic_num) return; - NetDriver* driver = nics[nic_id].drv; - if (!driver) return; - driver->handle_sent_packet(); + //TODO wake net_task from tx irq when the network loop becomes event driven } bool NetworkDispatch::enqueue_frame(uint8_t ifindex, const sizedptr& frame) @@ -98,12 +97,16 @@ bool NetworkDispatch::enqueue_frame(uint8_t ifindex, const sizedptr& frame) void* dst = (void*)(pkt.ptr + hs); memcpy(dst, (const void*)frame.ptr, frame.size); - if (!nics[nic_id].tx.push(pkt)) { + irq_flags_t irq = irq_save_disable(); + int pushed = nics[nic_id].tx.push(pkt); + if (pushed) nics[nic_id].tx_produced++; + irq_restore(irq); + + if (!pushed) { free_frame(pkt); nics[nic_id].tx_dropped++; return false; } - nics[nic_id].tx_produced++; return true; } @@ -116,6 +119,7 @@ int NetworkDispatch::net_task() for (size_t n = 0; n < nic_num; ++n) { NetDriver* driver = nics[n].drv; if (driver) { + driver->handle_sent_packet(); int lim = nics[n].kind_val == NET_IFK_LOCALHOST ? TASK_RX_BATCH_LIMIT : RX_INTR_BATCH_LIMIT; for (int i = 0; i < lim; ++i) { sizedptr raw = driver->handle_receive_packet(); @@ -153,14 +157,17 @@ int NetworkDispatch::net_task() if (!driver) continue; int processed = 0; 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.pop(pkt)) break; + irq_flags_t irq = irq_save_disable(); + int popped = nics[n].tx.pop(pkt); + if (popped) nics[n].tx_consumed++; + irq_restore(irq); + + if (!popped) break; if (!driver->send_packet(pkt)) { free_frame(pkt); nics[n].tx_dropped++; } - nics[n].tx_consumed++; processed++; } if (processed) did_work = true; From 8be718ddb5fb832a64406933b4d9675b4fa34d69 Mon Sep 17 00:00:00 2001 From: CodeAnarchist Date: Mon, 11 May 2026 20:46:15 +0200 Subject: [PATCH 09/39] [net/misc] L3/4 cleanup, tcp flow struct split and allocator bug fix --- kernel/exceptions/exception_handler.c | 10 + kernel/memory/page_allocator.c | 11 +- kernel/networking/internet_layer/ipv4.c | 14 +- kernel/networking/internet_layer/ipv4.h | 2 +- kernel/networking/internet_layer/ipv6.c | 44 +- kernel/networking/internet_layer/ipv6.h | 2 +- .../networking/transport_layer/socket_tcp.hpp | 141 ++-- kernel/networking/transport_layer/tcp.h | 10 +- .../networking/transport_layer/tcp/tcp_core.c | 490 ++++++------ .../transport_layer/tcp/tcp_internal.h | 84 +- .../networking/transport_layer/tcp/tcp_rx.c | 744 ++++++++++-------- .../transport_layer/tcp/tcp_timer.c | 180 ++--- .../networking/transport_layer/tcp/tcp_tx.c | 387 +++++---- .../transport_layer/tcp/tcp_utils.h | 1 - 14 files changed, 1125 insertions(+), 995 deletions(-) mode change 100755 => 100644 kernel/networking/transport_layer/socket_tcp.hpp diff --git a/kernel/exceptions/exception_handler.c b/kernel/exceptions/exception_handler.c index 102feb16..976673ad 100644 --- a/kernel/exceptions/exception_handler.c +++ b/kernel/exceptions/exception_handler.c @@ -6,8 +6,10 @@ #include "theme/theme.h" #include "std/string.h" #include "sysregs.h" +#include "process/syscall.h" static bool panic_triggered = false; +static bool panic_skip_trace = false; void set_exception_vectors(){ extern char exception_vectors[]; @@ -27,7 +29,11 @@ void handle_exception(const char* type, uint64_t info) { char buf[STRING_MAX_LEN];//no heap to avoid corruption const char *fmt = "%s \r\nESR_EL1: %llx\r\nELR_EL1: %llx\r\nFAR_EL1: %llx"; string_format_buf(buf, sizeof(buf), fmt,type,esr,elr,far); + + bool old_panic_skip_trace = panic_skip_trace; + panic_skip_trace = syscall_depth > 0; panic(buf, info); + panic_skip_trace = old_panic_skip_trace; } void fiq_el1_handler(){ handle_exception("FIQ EXCEPTION\r\n", 0); } @@ -61,6 +67,10 @@ void panic(const char* msg, uint64_t info) { uart_raw_puts("Additional info: "); uart_puthex(info); uart_raw_puts("\r\n"); + if (!old_panic_triggered && !panic_skip_trace) { + uart_raw_puts("Backtrace:\r\n"); + trace(); + } uart_raw_puts("System Halted\r\n"); if (!old_panic_triggered){ char buf[STRING_MAX_LEN]; diff --git a/kernel/memory/page_allocator.c b/kernel/memory/page_allocator.c index 8a8930a5..00f0ef47 100644 --- a/kernel/memory/page_allocator.c +++ b/kernel/memory/page_allocator.c @@ -186,6 +186,7 @@ void setup_page(uintptr_t address, uint8_t attributes){ } paddr_t palloc_inner(uint64_t size, uint8_t level, uint8_t attributes, bool full, bool map) { + if (!size) return 0; if (!alloc_max_page) page_alloc_init(); if (!page_alloc_high_va) page_alloc_enable_high_va(); uint64_t page_count = count_pages(size,PAGE_SIZE); @@ -263,10 +264,10 @@ paddr_t palloc_inner(uint64_t size, uint8_t level, uint8_t attributes, bool full uint64_t inv = ~mem_bitmap[i]; uint64_t bit = __builtin_ctzll(inv); - if (bit > (64 - page_count)){ - continue; - } - while (bit < 64) { + uint64_t max_bit = 64 - page_count; + if (bit > max_bit) continue; + + while (bit <= max_bit) { bool found = true; for (uint64_t b = bit; b < bit + page_count; b++){ if ((mem_bitmap[i] >> b) & 1ull){ @@ -277,7 +278,7 @@ paddr_t palloc_inner(uint64_t size, uint8_t level, uint8_t attributes, bool full } if (found) break; } - if (bit >= 64) continue; + if (bit > max_bit) continue; uintptr_t first_address = 0; mem_page* prev_page = 0; diff --git a/kernel/networking/internet_layer/ipv4.c b/kernel/networking/internet_layer/ipv4.c index 727185d2..5446c88b 100644 --- a/kernel/networking/internet_layer/ipv4.c +++ b/kernel/networking/internet_layer/ipv4.c @@ -247,10 +247,10 @@ static bool pick_route(uint32_t dst, const ipv4_tx_opts_t* opts, uint8_t* out_if } -void ipv4_send_packet(uint32_t dst_ip, uint8_t proto, netpkt_t* pkt, const ipv4_tx_opts_t* opts, uint8_t ttl, uint8_t dontfrag) { +bool ipv4_send_packet(uint32_t dst_ip, uint8_t proto, netpkt_t* pkt, const ipv4_tx_opts_t* opts, uint8_t ttl, uint8_t dontfrag) { if (!pkt || !netpkt_len(pkt)) { if (pkt) netpkt_unref(pkt); - return; + return false; } uint8_t ifx = 0; @@ -258,7 +258,7 @@ void ipv4_send_packet(uint32_t dst_ip, uint8_t proto, netpkt_t* pkt, const ipv4_ uint32_t nh = 0; if (!pick_route(dst_ip, opts, &ifx, &src_ip, &nh)) { netpkt_unref(pkt); - return; + return false; } uint8_t dst_mac[6]; @@ -281,7 +281,7 @@ void ipv4_send_packet(uint32_t dst_ip, uint8_t proto, netpkt_t* pkt, const ipv4_ memset(dst_mac, 0, 6); } else if (!arp_resolve_on(ifx, nh, dst_mac, 1000)) { netpkt_unref(pkt); - return; + return false; } } @@ -302,13 +302,13 @@ void ipv4_send_packet(uint32_t dst_ip, uint8_t proto, netpkt_t* pkt, const ipv4_ void* hdrp = netpkt_push(pkt, hdr_len); if (!hdrp) { netpkt_unref(pkt); - return; + return false; } uint32_t total = hdr_len + seg_len; if (dontfrag && total > (uint32_t)mtu) { netpkt_unref(pkt); - return; + return false; } uint16_t ip_[sizeof(ipv4_hdr_t)/sizeof(uint16_t)]; ipv4_hdr_t* ip = (ipv4_hdr_t*)ip_; @@ -327,7 +327,7 @@ void ipv4_send_packet(uint32_t dst_ip, uint8_t proto, netpkt_t* pkt, const ipv4_ ip->header_checksum = checksum16(ip_, hdr_len / 2); memcpy(hdrp, ip, sizeof(*ip)); - eth_send_frame_on(ifx, ETHERTYPE_IPV4, dst_mac, pkt); + return eth_send_frame_on(ifx, ETHERTYPE_IPV4, dst_mac, pkt); } void ipv4_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { diff --git a/kernel/networking/internet_layer/ipv4.h b/kernel/networking/internet_layer/ipv4.h index 54638eac..ab0fa01e 100644 --- a/kernel/networking/internet_layer/ipv4.h +++ b/kernel/networking/internet_layer/ipv4.h @@ -31,7 +31,7 @@ typedef struct __attribute__((packed)) ipv4_hdr_t { typedef ip_tx_scope_t ipv4_tx_scope_t; typedef ip_tx_opts_t ipv4_tx_opts_t; -void ipv4_send_packet(uint32_t dst_ip, uint8_t proto, netpkt_t* pkt, const ipv4_tx_opts_t* opts, uint8_t ttl, uint8_t dontfrag); +bool ipv4_send_packet(uint32_t dst_ip, uint8_t proto, netpkt_t* pkt, const ipv4_tx_opts_t* opts, uint8_t ttl, uint8_t dontfrag); void ipv4_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]); #ifdef __cplusplus diff --git a/kernel/networking/internet_layer/ipv6.c b/kernel/networking/internet_layer/ipv6.c index 1843467d..65b359e2 100644 --- a/kernel/networking/internet_layer/ipv6.c +++ b/kernel/networking/internet_layer/ipv6.c @@ -20,7 +20,8 @@ #define PMTU_CACHE_SIZE 16 #define REASS_SLOTS 8 #define REASS_STEP 2048u -#define REASS_MAX_LEN (2048u * 8u) +#define REASS_MAX_LEN 65535 +#define REASS_BITMAP_BYTES ((REASS_MAX_LEN + 7u) / 8u) typedef struct { uint8_t used; @@ -52,7 +53,7 @@ typedef struct { uint8_t *buf; uint32_t buf_cap; - uint8_t bitmap[2048]; + uint8_t bitmap[REASS_BITMAP_BYTES]; } reass_slot_t; typedef struct __attribute__((packed)) { @@ -350,10 +351,10 @@ static bool pick_route(const uint8_t dst[16], const ipv6_tx_opts_t* opts, uint8_ return pick_route_global(dst, out_ifx, out_src, out_nh); } -void ipv6_send_packet(const uint8_t dst[16], uint8_t next_header, netpkt_t* pkt, const ipv6_tx_opts_t* opts, uint8_t hop_limit, uint8_t dontfrag) { +bool ipv6_send_packet(const uint8_t dst[16], uint8_t next_header, netpkt_t* pkt, const ipv6_tx_opts_t* opts, uint8_t hop_limit, uint8_t dontfrag) { if (!dst || !pkt || !netpkt_len(pkt)) { if (pkt) netpkt_unref(pkt); - return; + return false; } uint8_t ifx = 0; @@ -364,14 +365,14 @@ void ipv6_send_packet(const uint8_t dst[16], uint8_t next_header, netpkt_t* pkt, if (!pick_route(dst, opts, &ifx, src, nh)) { netpkt_unref(pkt); - return; + return false; } if (!ipv6_is_unspecified(src)) { l2_interface_t* l2 = l2_interface_find_by_index(ifx); if (!l2) { netpkt_unref(pkt); - return; + return false; } int ok = 0; @@ -381,11 +382,11 @@ void ipv6_send_packet(const uint8_t dst[16], uint8_t next_header, netpkt_t* pkt, if (ipv6_cmp(v6->ip, src) != 0) continue; if (v6->cfg == IPV6_CFG_DISABLE) { netpkt_unref(pkt); - return; + return false; } if (v6->dad_state != IPV6_DAD_OK) { netpkt_unref(pkt); - return; + return false; } ok = 1; src_v6 = v6; @@ -393,13 +394,13 @@ void ipv6_send_packet(const uint8_t dst[16], uint8_t next_header, netpkt_t* pkt, } if (!ok) { netpkt_unref(pkt); - return; + return false; } } if (ipv6_is_linklocal(src) && !ipv6_is_linklocal(dst) && !ipv6_is_multicast(dst)) { netpkt_unref(pkt); - return; + return false; } uint8_t dst_mac[6]; @@ -408,7 +409,7 @@ void ipv6_send_packet(const uint8_t dst[16], uint8_t next_header, netpkt_t* pkt, else if (l2 && l2->kind == NET_IFK_LOCALHOST) memset(dst_mac, 0, 6); else if (!ndp_resolve_on(ifx, nh, dst_mac, 200)) { netpkt_unref(pkt); - return; + return false; } uint16_t mtu = 1500; @@ -428,7 +429,7 @@ void ipv6_send_packet(const uint8_t dst[16], uint8_t next_header, netpkt_t* pkt, void* hdrp = netpkt_push(pkt, hdr_len); if (!hdrp) { netpkt_unref(pkt); - return; + return false; } ipv6_hdr_t ip6; @@ -440,26 +441,25 @@ void ipv6_send_packet(const uint8_t dst[16], uint8_t next_header, netpkt_t* pkt, memcpy(ip6.dst, dst, 16); memcpy(hdrp, &ip6, sizeof(ip6)); - eth_send_frame_on(ifx, ETHERTYPE_IPV6, dst_mac, pkt); - return; + return eth_send_frame_on(ifx, ETHERTYPE_IPV6, dst_mac, pkt); } if (dontfrag) { netpkt_unref(pkt); - return; + return false; } uint32_t frag_hdr_len = (uint32_t)sizeof(ipv6_frag_hdr_t); if ((uint32_t)mtu < hdr_len + frag_hdr_len + 8u) { netpkt_unref(pkt); - return; + return false; } uint32_t max_chunk = (uint32_t)mtu - hdr_len - frag_hdr_len; max_chunk = (max_chunk / 8u) * 8u; if (max_chunk == 0) { netpkt_unref(pkt); - return; + return false; } rng_t rng; @@ -471,6 +471,7 @@ void ipv6_send_packet(const uint8_t dst[16], uint8_t next_header, netpkt_t* pkt, uint32_t off = 0; const uint8_t* data = (const uint8_t*)netpkt_data(pkt); uint32_t data_len = seg_len; + bool ok = true; while (off < data_len) { uint32_t remain = data_len - off; @@ -481,10 +482,14 @@ void ipv6_send_packet(const uint8_t dst[16], uint8_t next_header, netpkt_t* pkt, uint32_t frame_len = hdr_len + payload_len; netpkt_t* fpkt = netpkt_alloc(frame_len, (uint32_t)sizeof(eth_hdr_t), 0); - if (!fpkt) break; + if (!fpkt) { + ok = false; + break; + } void* buf = netpkt_put(fpkt, frame_len); if (!buf) { netpkt_unref(fpkt); + ok = false; break; } @@ -508,12 +513,13 @@ void ipv6_send_packet(const uint8_t dst[16], uint8_t next_header, netpkt_t* pkt, memcpy((uint8_t*)buf + hdr_len + sizeof(fh), data + off, chunk); - eth_send_frame_on(ifx, ETHERTYPE_IPV6, dst_mac, fpkt); + if (!eth_send_frame_on(ifx, ETHERTYPE_IPV6, dst_mac, fpkt)) ok = false; off += chunk; } netpkt_unref(pkt); + return ok && off == data_len; } static bool ipv6_skip_ext_headers(const netpkt_t* pkt, uint8_t* nh, uint32_t* l4_off, uint32_t* l4_len) { diff --git a/kernel/networking/internet_layer/ipv6.h b/kernel/networking/internet_layer/ipv6.h index a8e8fd36..e716893d 100644 --- a/kernel/networking/internet_layer/ipv6.h +++ b/kernel/networking/internet_layer/ipv6.h @@ -20,7 +20,7 @@ typedef struct __attribute__((packed)) { typedef ip_tx_scope_t ipv6_tx_scope_t; typedef ip_tx_opts_t ipv6_tx_opts_t; -void ipv6_send_packet(const uint8_t dst[16], uint8_t next_header, netpkt_t* pkt, const ipv6_tx_opts_t* opts, uint8_t hop_limit, uint8_t dontfrag); +bool ipv6_send_packet(const uint8_t dst[16], uint8_t next_header, netpkt_t* pkt, const ipv6_tx_opts_t* opts, uint8_t hop_limit, uint8_t dontfrag); void ipv6_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]); uint16_t ipv6_pmtu_get(const uint8_t dst[16]); diff --git a/kernel/networking/transport_layer/socket_tcp.hpp b/kernel/networking/transport_layer/socket_tcp.hpp old mode 100755 new mode 100644 index 6831947b..dbe6d3dc --- a/kernel/networking/transport_layer/socket_tcp.hpp +++ b/kernel/networking/transport_layer/socket_tcp.hpp @@ -14,7 +14,7 @@ #include "networking/transport_layer/trans_utils.h" #include "syscalls/syscalls.h" -static constexpr int TCP_MAX_BACKLOG = 32; +static constexpr int TCP_MAX_BACKLOG = 128; static constexpr dns_server_sel_t TCP_DNS_SEL = DNS_USE_BOTH; static constexpr uint32_t TCP_DNS_TIMEOUT_MS = 3000; @@ -22,7 +22,7 @@ class TCPSocket : public Socket { inline static TCPSocket* s_list_head = nullptr; static constexpr uint32_t TCP_DEFAULT_SOCKET_BUF = 256 * 1024; - tcp_data* flow = nullptr; + tcp_data flow = {}; TCPSocket* pending[TCP_MAX_BACKLOG] = { nullptr }; int backlogCap = 0; @@ -85,6 +85,23 @@ class TCPSocket : public Socket { } if (!matches_dst) continue; + for (int i = 0; i < srv->backlogLen;) { + TCPSocket* client = srv->pending[i]; + + if (client && client->flow.flow_generation) { + bool readable = tcp_flow_readable(&client->flow) != 0; + bool closed = tcp_flow_recv_closed(&client->flow); + + if (!closed || readable) { + i++; + continue; + } + } + + client = srv->pop_pending_at(i); + if (client) delete client; + } + if (srv->backlogLen >= srv->backlogCap) break; TCPSocket* child = new TCPSocket(SOCK_ROLE_CLIENT, srv->pid, &srv->extraOpts); @@ -165,8 +182,7 @@ class TCPSocket : public Socket { } } - child->flow = tcp_get_ctx(dst_port, ipver, dst_ip_addr, child->remoteEP.ip, src_port); - if (!child->flow){ + if (!tcp_get_ctx(dst_port, ipver, dst_ip_addr, child->remoteEP.ip, src_port, &child->flow)) { delete child; break; } @@ -410,30 +426,68 @@ class TCPSocket : public Socket { if (!bound || role != SOCK_ROLE_SERVER) return SOCK_ERR_STATE; backlogCap = max_backlog > TCP_MAX_BACKLOG ? TCP_MAX_BACKLOG : max_backlog; + if (backlogCap < 1) backlogCap = 1; backlogLen = 0; return SOCK_OK; } TCPSocket* accept(){ - const int max_empty_iters = 100; - const int ready_wait_iters = 3; + const int max_empty_iters = 200; + const int ready_wait_iters = 4; const int max_idle_skips = 20; int iter = 0; while (backlogLen == 0){ idleAcceptSkips = 0; if (++iter > max_empty_iters) return nullptr; - msleep(10); + msleep(5); } + for (int i = 0; i < backlogLen;) { + TCPSocket* client = pending[i]; + + if (client && client->flow.flow_generation) { + bool readable = tcp_flow_readable(&client->flow) != 0; + bool closed = tcp_flow_recv_closed(&client->flow); + + if (!closed || readable) { + i++; + continue; + } + } + + client = pop_pending_at(i); + if (client) delete client; + } + + if (backlogLen == 0) return nullptr; + //TODO temporary http workaround //without socket events accept() can pick an idle preconnect and block in recv() while a later ipv4/6 request is already ready iter = 0; while (1) { + for (int i = 0; i < backlogLen;) { + TCPSocket* client = pending[i]; + + if (client && client->flow.flow_generation) { + bool readable = tcp_flow_readable(&client->flow) != 0; + bool closed = tcp_flow_recv_closed(&client->flow); + + if (!closed || readable) { + i++; + continue; + } + } + + client = pop_pending_at(i); + if (client) delete client; + } + + if (backlogLen == 0) return nullptr; for (int i = 0; i < backlogLen; ++i) { TCPSocket* client = pending[i]; if (!client) continue; - if (!client->flow || tcp_flow_readable(client->flow) || tcp_flow_recv_closed(client->flow)) { + if (!client->flow.flow_generation || tcp_flow_readable(&client->flow) || tcp_flow_recv_closed(&client->flow)) { idleAcceptSkips = 0; return pop_pending_at(i); } @@ -446,7 +500,7 @@ class TCPSocket : public Socket { } return nullptr; } - msleep(10); + msleep(5); } } @@ -561,34 +615,24 @@ class TCPSocket : public Socket { add_bound_l3(chosen_l3); bound = true; - tcp_data ctx_copy{}; - if (!tcp_handshake_l3(chosen_l3, localPort, &d, &ctx_copy, pid, &extraOpts)) { + flow = tcp_data{}; + if (!tcp_handshake_l3(chosen_l3, localPort, &d, &flow, pid, &extraOpts)) { Socket::close(); return SOCK_ERR_SYS; } - uint8_t local_ip[16]; - memset(local_ip, 0, sizeof(local_ip)); if (d.ver == IP_VER4) { l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(chosen_l3); if (!is_valid_v4_l3_for_bind(v4)) { Socket::close(); return SOCK_ERR_SYS; } - memcpy(local_ip, &v4->ip, 4); } else { l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(chosen_l3); if (!is_valid_v6_l3_for_bind(v6)) { Socket::close(); return SOCK_ERR_SYS; - } - memcpy(local_ip, v6->ip, 16); - } - - flow = tcp_get_ctx(localPort, d.ver, local_ip, (const void*)d.ip, d.port); - if (!flow) { - Socket::close(); - return SOCK_ERR_SYS; + } } remoteEP = d; @@ -614,30 +658,18 @@ class TCPSocket : public Socket { ev.local_port = localPort; ev.remote_ep = remoteEP; netlog_socket_event(&extraOpts, &ev); - if (!connected || !flow) return SOCK_ERR_STATE; - - const uint8_t* p = (const uint8_t*)buf; - uint64_t sent_total = 0; - - while (sent_total < len) { - uint64_t remain = len - sent_total; - uint32_t chunk = remain > UINT32_MAX ? UINT32_MAX : (uint32_t)remain; - flow->payload.ptr = (uintptr_t)(p + sent_total); - flow->payload.size = chunk; - flow->flags = (1u<payload.size; - if (!pushed) break; - sent_total += pushed; - } - - if (sent_total) return (int64_t)sent_total; + if (!connected || !flow.flow_generation) return SOCK_ERR_STATE; + if (!buf && len) return SOCK_ERR_INVAL; + if (!len) return 0; + + uint32_t chunk = len > UINT32_MAX ? UINT32_MAX : (uint32_t)len; + flow.payload.ptr = (uintptr_t)buf; + flow.payload.size = chunk; + flow.flags = (1u<state == TCP_STATE_CLOSED) continue; - if (f->local_port != local_port) continue; + if (f->base.state == TCP_STATE_CLOSED) continue; + if (f->base.local_port != local_port) continue; - if (f->state == TCP_LISTEN){ + if (f->base.state == TCP_LISTEN){ if (remote_ip || remote_port) continue; - if (f->local.ver && f->local.ver != ver) continue; + if (f->base.local.ver && f->base.local.ver != ver) continue; if (!local_ip) return i; size_t l = (size_t)(ver == IP_VER6 ? 16 : 4); int unspec = 1; for (size_t k = 0; k < l; ++k){ - if (f->local.ip[k]){ + if (f->base.local.ip[k]){ unspec = 0; break; } } if (unspec) return i; - if (memcmp(f->local.ip, local_ip, l) == 0) return i; + if (memcmp(f->base.local.ip, local_ip, l) == 0) return i; continue; } if (!remote_ip) continue; if (!local_ip) continue; - if (f->remote.ver != ver) continue; - if (f->remote.port != remote_port) continue; + if (f->base.remote.ver != ver) continue; + if (f->base.remote.port != remote_port) continue; size_t l = (size_t)(ver == IP_VER6 ? 16 : 4); - if (memcmp(f->local.ip, local_ip, l) != 0) continue; - if (memcmp(f->remote.ip, remote_ip, l) != 0) continue; + if (memcmp(f->base.local.ip, local_ip, l) != 0) continue; + if (memcmp(f->base.remote.ip, remote_ip, l) != 0) continue; return i; } @@ -54,37 +57,52 @@ int find_flow(uint16_t local_port, ip_version_t ver, const void *local_ip, const return -1; } -tcp_data *tcp_get_ctx(uint16_t local_port, ip_version_t ver, const void *local_ip, const void *remote_ip, uint16_t remote_port){ +bool tcp_get_ctx(uint16_t local_port, ip_version_t ver, const void *local_ip, const void *remote_ip, uint16_t remote_port, tcp_data *out_ctx){ + if (!out_ctx) return false; int idx = find_flow(local_port, ver, local_ip, remote_ip, remote_port); + if (idx < 0) return false; + *out_ctx = tcp_flows[idx]->base.ctx; + return true; +} + +tcp_flow_t *tcp_flow_from_ctx(tcp_data *flow_ctx) { + if (!flow_ctx) return NULL; + if (flow_ctx->flow_index >= MAX_TCP_FLOWS) return NULL; - if (idx < 0) return NULL; - return &tcp_flows[idx]->ctx; + tcp_flow_t *flow = tcp_flows[flow_ctx->flow_index]; + if (!flow) return NULL; + if (flow->base.generation != flow_ctx->flow_generation) return NULL; + return flow; } -static void tcp_clear_flow_buffers(tcp_flow_t *f) { +void tcp_release_io_buffers(tcp_flow_t *f) { if (!f) return; for (int i = 0; i < TCP_MAX_TX_SEGS; i++) { - tcp_tx_seg_t *s = &f->txq[i]; + tcp_tx_seg_t *s = &f->tx.txq[i]; if (s->used && s->buf && s->len)release((void*)s->buf); memset(s, 0, sizeof(*s)); } - if (f->nagle_buf) release((void*)f->nagle_buf); - f->nagle_buf = 0; - f->nagle_len = 0; - f->nagle_cap = 0; - f->nagle_timer_ms = 0; - f->nagle_flushing = 0; - f->nagle_appending = 0; - - if (f->rcv_buf) release((void*)f->rcv_buf); - f->rcv_buf = 0; - f->rcv_base = f->rcv_nxt; - f->rcv_data_nxt = f->rcv_nxt; - f->rcv_ooo_used = 0; - f->reass_count = 0; - memset(f->reass, 0, sizeof(f->reass)); + if (f->tx.nagle_buf) release((void*)f->tx.nagle_buf); + f->tx.nagle_buf = 0; + f->tx.nagle_len = 0; + f->tx.nagle_cap = 0; + f->tx.nagle_timer_ms = 0; + f->tx.nagle_flushing = 0; + f->tx.nagle_appending = 0; + + if (f->rx.rcv_buf) release((void*)f->rx.rcv_buf); + f->rx.rcv_buf = 0; + f->rx.rcv_base = f->rx.rcv_nxt; + f->rx.rcv_data_nxt = f->rx.rcv_nxt; + f->rx.rcv_ooo_used = 0; + f->rx.rcv_wnd = 0; + f->rx.rcv_adv_edge = f->rx.rcv_nxt; + f->rx.fin_pending = 0; + f->base.ctx.window = 0; + f->rx.reass_count = 0; + memset(f->rx.reass, 0, sizeof(f->rx.reass)); } tcp_flow_t *tcp_alloc_flow(void){ @@ -95,78 +113,96 @@ tcp_flow_t *tcp_alloc_flow(void){ if (!f) return NULL; tcp_flows[i] = f; - f->rto = TCP_INIT_RTO; - f->rcv_wnd_max = TCP_DEFAULT_RCV_BUF; - f->rcv_wnd = f->rcv_wnd_max; - - f->mss = TCP_DEFAULT_MSS; - f->cwnd = f->mss * TCP_INIT_CWND_SEGS; - f->ssthresh = TCP_RECV_WINDOW; + if (tcp_active_count >= MAX_TCP_FLOWS) { + release(f); + tcp_flows[i] = NULL; + return NULL; + } + f->base.active_pos = tcp_active_count; + tcp_active_flows[tcp_active_count++] = (uint16_t)i; + f->base.slot = (uint16_t)i; + f->base.generation = tcp_generation_next++; + if (!tcp_generation_next) tcp_generation_next = 1; + f->base.ctx.flow_index = f->base.slot; + f->base.ctx.flow_generation = f->base.generation; + + f->tx.rto = TCP_INIT_RTO; + f->rx.rcv_wnd_max = TCP_DEFAULT_RCV_BUF; + f->rx.rcv_wnd = f->rx.rcv_wnd_max; + + f->tx.mss = TCP_DEFAULT_MSS; + f->tx.cwnd = f->tx.mss * TCP_INIT_CWND_SEGS; + f->tx.ssthresh = TCP_RECV_WINDOW; return f; } return NULL; } -void tcp_abort_flow(int idx) { +void tcp_free_flow(int idx) { if (idx < 0 || idx >= MAX_TCP_FLOWS) return; tcp_flow_t *f = tcp_flows[idx]; if (!f) return; - tcp_clear_flow_buffers(f); - f->state = TCP_STATE_CLOSED; - f->ctx.flags = 0; - f->ctx.payload.ptr = 0; - f->ctx.payload.size = 0; - f->ctx.options.ptr = 0; - f->ctx.options.size = 0; - f->fin_pending = 0; - f->fin_tx_pending = 0; - f->delayed_ack_pending = 0; - f->persist_active = 0; - f->keepalive_on = 0; -} + if (tcp_active_count != 0) { + uint16_t pos = f->base.active_pos; -void tcp_free_flow(int idx) { - if (idx < 0 || idx >= MAX_TCP_FLOWS) return; + if (pos >= tcp_active_count || tcp_active_flows[pos] != idx) for (pos = 0; pos < tcp_active_count && tcp_active_flows[pos] != idx; pos++); + if (pos < tcp_active_count) { + uint16_t last = tcp_active_flows[--tcp_active_count]; + tcp_active_flows[pos] = last; + tcp_active_flows[tcp_active_count] = 0; - tcp_flow_t *f = tcp_flows[idx]; - if (!f) return; + if (last != idx && tcp_flows[last]) tcp_flows[last]->base.active_pos = pos; + } + + f->base.active_pos = 0; + } + + for (int i = 0; i < TCP_MAX_TX_SEGS; i++) { + tcp_tx_seg_t *s =&f->tx.txq[i]; + if (s->used && s->buf && s->len)release((void*)s->buf); + memset(s, 0, sizeof(*s)); + } - tcp_clear_flow_buffers(f); + if (f->tx.nagle_buf) release((void*)f->tx.nagle_buf); + f->tx.nagle_buf = 0; + f->tx.nagle_len = 0; + f->tx.nagle_cap = 0; + f->tx.nagle_timer_ms = 0; + f->tx.nagle_flushing = 0; + f->tx.nagle_appending = 0; + + if (f->rx.rcv_buf) release((void*)f->rx.rcv_buf); + f->rx.rcv_buf = 0; + f->rx.rcv_base = f->rx.rcv_nxt; + f->rx.rcv_data_nxt = f->rx.rcv_nxt; + f->rx.rcv_ooo_used = 0; + f->rx.rcv_wnd = 0; + f->rx.rcv_adv_edge = f->rx.rcv_nxt; + f->rx.fin_pending = 0; + f->base.ctx.window = 0; + f->rx.reass_count = 0; + + memset(f->rx.reass, 0, sizeof(f->rx.reass)); memset(f, 0, sizeof(*f)); release(f); tcp_flows[idx] = NULL; } bool tcp_flow_is_closed(tcp_data *flow_ctx) { - if (!flow_ctx) return true; - - for (int i = 0; i < MAX_TCP_FLOWS; i++) { - tcp_flow_t *f = tcp_flows[i]; - if (!f) continue; - if (&f->ctx != flow_ctx) continue; - return f->state == TCP_STATE_CLOSED || f->state == TCP_TIME_WAIT; - } - - return true; + tcp_flow_t *flow = tcp_flow_from_ctx(flow_ctx); + if (!flow) return true; + return flow->base.state == TCP_STATE_CLOSED || flow->base.state == TCP_TIME_WAIT; } tcp_result_t tcp_flow_release_closed(tcp_data *flow_ctx) { - if (!flow_ctx) return TCP_INVALID; - - for (int i = 0; i < MAX_TCP_FLOWS; i++) { - tcp_flow_t *f = tcp_flows[i]; - if (!f) continue; - if (&f->ctx != flow_ctx) continue; - - if (f->state != TCP_STATE_CLOSED && f->state != TCP_TIME_WAIT) return TCP_BUSY; - tcp_free_flow(i); - return TCP_OK; - } - - return TCP_INVALID; + tcp_flow_t *flow = tcp_flow_from_ctx(flow_ctx); + if (!flow) return TCP_INVALID; + if (flow->base.state != TCP_STATE_CLOSED && flow->base.state != TCP_TIME_WAIT) return TCP_BUSY; + tcp_free_flow((int)flow->base.slot); + return TCP_OK; } bool tcp_send_segment(ip_version_t ver, const void *src_ip_addr, const void *dst_ip_addr, tcp_hdr_t *hdr, const uint8_t *opts, uint8_t opts_len, const uint8_t *payload, uint16_t payload_len, const ip_tx_opts_t *txp, uint8_t ttl, uint8_t dontfrag){ @@ -204,13 +240,11 @@ bool tcp_send_segment(ip_version_t ver, const void *src_ip_addr, const void *dst h.checksum = tcp_checksum_ipv4(segment, tcp_len, s, d); memcpy(segment, &h, sizeof(h)); - ipv4_send_packet(d, 6, pkt, (const ipv4_tx_opts_t *)txp, ttl, dontfrag); - return true; + return ipv4_send_packet(d, 6, pkt, (const ipv4_tx_opts_t *)txp, ttl, dontfrag); } else if (ver == IP_VER6){ h.checksum = tcp_checksum_ipv6(segment, tcp_len, (const uint8_t *)src_ip_addr, (const uint8_t *)dst_ip_addr); memcpy(segment, &h, sizeof(h)); - ipv6_send_packet((const uint8_t *)dst_ip_addr, 6, pkt, (const ipv6_tx_opts_t *)txp, ttl, dontfrag); - return true; + return ipv6_send_packet((const uint8_t *)dst_ip_addr, 6, pkt, (const ipv6_tx_opts_t *)txp, ttl, dontfrag); } netpkt_unref(pkt); @@ -252,35 +286,35 @@ void tcp_send_reset(ip_version_t ver, const void *src_ip_addr, const void *dst_i void tcp_rtt_update(tcp_flow_t *flow, uint32_t sample_ms){ if (sample_ms == 0) sample_ms = 1; - if (!flow->rtt_valid){ - flow->srtt = sample_ms; - flow->rttvar = sample_ms / 2; + if (!flow->tx.rtt_valid){ + flow->tx.srtt = sample_ms; + flow->tx.rttvar = sample_ms / 2; - uint32_t rto = flow->srtt + (flow->rttvar << 2); + uint32_t rto = flow->tx.srtt + (flow->tx.rttvar << 2); if (rto < TCP_MIN_RTO) rto = TCP_MIN_RTO; if (rto > TCP_MAX_RTO) rto = TCP_MAX_RTO; - flow->rto = rto; - flow->rtt_valid = 1; + flow->tx.rto = rto; + flow->tx.rtt_valid = 1; return; } - uint32_t srtt = flow->srtt; - uint32_t rttvar = flow->rttvar; + uint32_t srtt = flow->tx.srtt; + uint32_t rttvar = flow->tx.rttvar; uint32_t diff = srtt > sample_ms ? srtt - sample_ms : sample_ms - srtt; uint32_t new_rttvar = (uint32_t)((3 * (uint64_t)rttvar + (uint64_t)diff) >> 2); uint32_t new_srtt = (uint32_t)(((uint64_t)7 * srtt + sample_ms) >> 3); - flow->srtt = new_srtt; - flow->rttvar = new_rttvar; + flow->tx.srtt = new_srtt; + flow->tx.rttvar = new_rttvar; uint32_t rto = new_srtt + (new_rttvar << 2); if (rto < TCP_MIN_RTO) rto = TCP_MIN_RTO; if (rto > TCP_MAX_RTO) rto = TCP_MAX_RTO; - flow->rto = rto; + flow->tx.rto = rto; } bool tcp_bind_l3(uint8_t l3_id, uint16_t port, uint16_t pid, port_recv_handler_t handler, const SocketExtraOptions* extra){ @@ -300,61 +334,61 @@ bool tcp_bind_l3(uint8_t l3_id, uint16_t port, uint16_t pid, port_recv_handler_t return false; } if (f){ - f->local_port = port; - f->l3_id = l3_id; - - f->local.ver = l3_is_v6_from_id(l3_id) ? IP_VER6 : IP_VER4; - memset(f->local.ip, 0, sizeof(f->local.ip)); - f->local.port = port; - - f->remote.ver = 0; - memset(f->remote.ip, 0, sizeof(f->remote.ip)); - f->remote.port = 0; - - f->state = TCP_LISTEN; - - f->ctx.sequence = 0; - f->ctx.ack = 0; - f->ctx.flags = 0; - - f->rcv_wnd_max = TCP_DEFAULT_RCV_BUF; - if (extra && (extra->flags & SOCK_OPT_BUF_SIZE) && extra->buf_size) f->rcv_wnd_max = extra->buf_size; - f->rcv_base = 0; - f->rcv_data_nxt = 0; - f->rcv_ooo_used = 0; - f->sack_recent_left = 0; - f->sack_recent_right = 0; - f->rcv_adv_edge = 0; - - f->ip_ttl = extra && (extra->flags & SOCK_OPT_TTL) ? extra->ttl : 0; - f->ip_dontfrag = extra && (extra->flags & SOCK_OPT_DONTFRAG) ? 1 : 0; - f->keepalive_on = extra && (extra->flags & SOCK_OPT_KEEPALIVE) ? 1 : 0; - f->keepalive_ms = extra && (extra->flags & SOCK_OPT_KEEPALIVE) ? extra->keepalive_ms : 0; - f->keepalive_idle_ms = 0; - - f->mss = TCP_DEFAULT_MSS; - if (f->rcv_wnd_max > 65535u) { - f->ws_send = 8; - f->ws_recv = 0; - f->ws_ok = 1; + f->base.local_port = port; + f->base.l3_id = l3_id; + + f->base.local.ver = l3_is_v6_from_id(l3_id) ? IP_VER6 : IP_VER4; + memset(f->base.local.ip, 0, sizeof(f->base.local.ip)); + f->base.local.port = port; + + f->base.remote.ver = 0; + memset(f->base.remote.ip, 0, sizeof(f->base.remote.ip)); + f->base.remote.port = 0; + + f->base.state = TCP_LISTEN; + + f->base.ctx.sequence = 0; + f->base.ctx.ack = 0; + f->base.ctx.flags = 0; + + f->rx.rcv_wnd_max = TCP_DEFAULT_RCV_BUF; + if (extra && (extra->flags & SOCK_OPT_BUF_SIZE) && extra->buf_size) f->rx.rcv_wnd_max = extra->buf_size; + f->rx.rcv_base = 0; + f->rx.rcv_data_nxt = 0; + f->rx.rcv_ooo_used = 0; + f->rx.sack_recent_left = 0; + f->rx.sack_recent_right = 0; + f->rx.rcv_adv_edge = 0; + + f->ip.ttl = extra && (extra->flags & SOCK_OPT_TTL) ? extra->ttl : 0; + f->ip.dontfrag = extra && (extra->flags & SOCK_OPT_DONTFRAG) ? 1 : 0; + f->timer.keepalive_on = extra && (extra->flags & SOCK_OPT_KEEPALIVE) ? 1 : 0; + f->timer.keepalive_ms = extra && (extra->flags & SOCK_OPT_KEEPALIVE) ? extra->keepalive_ms : 0; + f->timer.keepalive_idle_ms = 0; + + f->tx.mss = TCP_DEFAULT_MSS; + if (f->rx.rcv_wnd_max > 65535u) { + f->tx.ws_send = 8; + f->tx.ws_recv = 0; + f->tx.ws_ok = 1; } else { - f->ws_send = 0; - f->ws_recv = 0; - f->ws_ok = 0; + f->tx.ws_send = 0; + f->tx.ws_recv = 0; + f->tx.ws_ok = 0; } - f->sack_ok = 1; + f->tx.sack_ok = 1; - f->ctx.options.ptr = 0; - f->ctx.options.size = 0; - f->ctx.payload.ptr = 0; - f->ctx.payload.size = 0; + f->base.ctx.options.ptr = 0; + f->base.ctx.options.size = 0; + f->base.ctx.payload.ptr = 0; + f->base.ctx.payload.size = 0; - f->ctx.expected_ack = 0; - f->ctx.ack_received = 0; + f->base.ctx.expected_ack = 0; + f->base.ctx.ack_received = 0; - f->time_wait_ms = 0; - f->fin_wait2_ms = 0; + f->timer.time_wait_ms = 0; + f->timer.fin_wait2_ms = 0; } return true; @@ -363,8 +397,6 @@ bool tcp_bind_l3(uint8_t l3_id, uint16_t port, uint16_t pid, port_recv_handler_t int tcp_alloc_ephemeral_l3(uint8_t l3_id, uint16_t pid, port_recv_handler_t handler){ port_manager_t *pm = l3_is_v6_from_id(l3_id) ? ifmgr_pm_v6(l3_id) : ifmgr_pm_v4(l3_id); - if (!pm) return -1; - if (!pm) return -1; return port_alloc_ephemeral(pm, PROTO_TCP, pid, handler); } @@ -381,7 +413,7 @@ bool tcp_unbind_l3(uint8_t l3_id, uint16_t port, uint16_t pid){ for (int i = 0; i < MAX_TCP_FLOWS; i++){ tcp_flow_t *f = tcp_flows[i]; if (!f) continue; - if (f->state==TCP_LISTEN && f->local_port==port && f->local.ver==ver) tcp_free_flow(i); + if (f->base.state==TCP_LISTEN && f->base.local_port==port && f->base.local.ver==ver) tcp_free_flow(i); } } @@ -390,25 +422,19 @@ 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, const SocketExtraOptions* extra){ (void)pid; + if (!dst || !flow_ctx) return false; tcp_flow_t *flow = tcp_alloc_flow(); if (!flow) return false; - int idx = -1; - for (int i = 0; i < MAX_TCP_FLOWS; i++) { - if (tcp_flows[i] == flow) { - idx = i; - break; - } - } - if (idx < 0) return false; + int idx = (int)flow->base.slot; - flow->local_port = local_port; - flow->l3_id = l3_id; + flow->base.local_port = local_port; + flow->base.l3_id = l3_id; - flow->remote.ver = dst->ver; - memcpy(flow->remote.ip, dst->ip, (size_t)(dst->ver == IP_VER6 ? 16 : 4)); - flow->remote.port = dst->port; + flow->base.remote.ver = dst->ver; + memcpy(flow->base.remote.ip, dst->ip, (size_t)(dst->ver == IP_VER6 ? 16 : 4)); + flow->base.remote.port = dst->port; if (dst->ver == IP_VER4){ l3_ipv4_interface_t *v4 = l3_ipv4_find_by_id(l3_id); @@ -418,7 +444,7 @@ bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, return false; } - make_ep(v4->ip, local_port, IP_VER4, &flow->local); + make_ep(v4->ip, local_port, IP_VER4, &flow->base.local); } else{ l3_ipv6_interface_t *v6 = l3_ipv6_find_by_id(l3_id); @@ -427,14 +453,14 @@ bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, return false; } - flow->local.ver = IP_VER6; - memset(flow->local.ip, 0, sizeof(flow->local.ip)); - memcpy(flow->local.ip, v6->ip, sizeof(flow->local.ip)); - flow->local.port = local_port; + flow->base.local.ver = IP_VER6; + memset(flow->base.local.ip, 0, sizeof(flow->base.local.ip)); + memcpy(flow->base.local.ip, v6->ip, sizeof(flow->base.local.ip)); + flow->base.local.port = local_port; } - flow->state = TCP_SYN_SENT; - flow->retries = TCP_SYN_RETRIES; + flow->base.state = TCP_SYN_SENT; + flow->base.retries = TCP_SYN_RETRIES; rng_t rng; uint64_t virt_timer; @@ -442,69 +468,69 @@ bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, rng_seed(&rng, virt_timer); uint32_t iss = rng_next32(&rng); - flow->ctx.sequence = iss; - flow->ctx.ack = 0; - - flow->rcv_nxt = 0; - flow->rcv_base = 0; - flow->rcv_data_nxt = 0; - flow->rcv_ooo_used = 0; - flow->sack_recent_left = 0; - flow->sack_recent_right = 0; - flow->rcv_wnd_max = TCP_DEFAULT_RCV_BUF; - if (extra && (extra->flags & SOCK_OPT_BUF_SIZE) && extra->buf_size) flow->rcv_wnd_max = extra->buf_size; - flow->rcv_adv_edge = 0; - - flow->mss = tcp_calc_mss_for_l3(l3_id, dst->ver, dst->ip); - - if (flow->rcv_wnd_max > 65535u) { - flow->ws_send = 8; - flow->ws_recv = 0; - flow->ws_ok = 1; + flow->base.ctx.sequence = iss; + flow->base.ctx.ack = 0; + + flow->rx.rcv_nxt = 0; + flow->rx.rcv_base = 0; + flow->rx.rcv_data_nxt = 0; + flow->rx.rcv_ooo_used = 0; + flow->rx.sack_recent_left = 0; + flow->rx.sack_recent_right = 0; + flow->rx.rcv_wnd_max = TCP_DEFAULT_RCV_BUF; + if (extra && (extra->flags & SOCK_OPT_BUF_SIZE) && extra->buf_size) flow->rx.rcv_wnd_max = extra->buf_size; + flow->rx.rcv_adv_edge = 0; + + flow->tx.mss = tcp_calc_mss_for_l3(l3_id, dst->ver, dst->ip); + + if (flow->rx.rcv_wnd_max > 65535u) { + flow->tx.ws_send = 8; + flow->tx.ws_recv = 0; + flow->tx.ws_ok = 1; } else { - flow->ws_send = 0; - flow->ws_recv = 0; - flow->ws_ok = 0; + flow->tx.ws_send = 0; + flow->tx.ws_recv = 0; + flow->tx.ws_ok = 0; } - flow->sack_ok = 1; - flow->rcv_buf = (uintptr_t)zalloc(flow->rcv_wnd_max); - if (!flow->rcv_buf) { - flow->rcv_wnd = 0; - flow->rcv_adv_edge = flow->rcv_nxt; - flow->ctx.window = 0; + flow->tx.sack_ok = 1; + flow->rx.rcv_buf = (uintptr_t)zalloc(flow->rx.rcv_wnd_max); + if (!flow->rx.rcv_buf) { + flow->rx.rcv_wnd = 0; + flow->rx.rcv_adv_edge = flow->rx.rcv_nxt; + flow->base.ctx.window = 0; tcp_free_flow(idx); return false; } (void)tcp_calc_adv_wnd_field(flow, 1); - flow->ip_ttl = extra && (extra->flags & SOCK_OPT_TTL) ? extra->ttl : 0; - flow->ip_dontfrag = extra && (extra->flags & SOCK_OPT_DONTFRAG) ? 1 : 0; - flow->keepalive_on = extra && (extra->flags & SOCK_OPT_KEEPALIVE) ? 1 : 0; - flow->keepalive_ms = extra && (extra->flags & SOCK_OPT_KEEPALIVE) ? extra->keepalive_ms : 0; - flow->keepalive_idle_ms = 0; + flow->ip.ttl = extra && (extra->flags & SOCK_OPT_TTL) ? extra->ttl : 0; + flow->ip.dontfrag = extra && (extra->flags & SOCK_OPT_DONTFRAG) ? 1 : 0; + flow->timer.keepalive_on = extra && (extra->flags & SOCK_OPT_KEEPALIVE) ? 1 : 0; + flow->timer.keepalive_ms = extra && (extra->flags & SOCK_OPT_KEEPALIVE) ? extra->keepalive_ms : 0; + flow->timer.keepalive_idle_ms = 0; - flow->ctx.options.ptr = 0; - flow->ctx.options.size = 0; - flow->ctx.payload.ptr = 0; - flow->ctx.payload.size = 0; + flow->base.ctx.options.ptr = 0; + flow->base.ctx.options.size = 0; + flow->base.ctx.payload.ptr = 0; + flow->base.ctx.payload.size = 0; - flow->ctx.flags = (uint8_t)(1u << SYN_F); - flow->ctx.expected_ack = iss + 1; - flow->ctx.ack_received = 0; + flow->base.ctx.flags = (uint8_t)(1u << SYN_F); + flow->base.ctx.expected_ack = iss + 1; + flow->base.ctx.ack_received = 0; - flow->snd_una = iss; - flow->snd_nxt = iss; - flow->snd_wnd = 0; + flow->tx.snd_una = iss; + flow->tx.snd_nxt = iss; + flow->tx.snd_wnd = 0; - flow->cwnd = flow->mss * TCP_INIT_CWND_SEGS; - flow->ssthresh = TCP_RECV_WINDOW; - flow->dup_acks = 0; - flow->in_fast_recovery = 0; - flow->recover = 0; - flow->cwnd_acc = 0; + flow->tx.cwnd = flow->tx.mss * TCP_INIT_CWND_SEGS; + flow->tx.ssthresh = TCP_RECV_WINDOW; + flow->tx.dup_acks = 0; + flow->tx.in_fast_recovery = 0; + flow->tx.recover = 0; + flow->tx.cwnd_acc = 0; - flow->time_wait_ms = 0; - flow->fin_wait2_ms = 0; + flow->timer.time_wait_ms = 0; + flow->timer.fin_wait2_ms = 0; tcp_tx_seg_t *seg = tcp_alloc_tx_seg(flow); @@ -517,19 +543,22 @@ bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, seg->fin = 0; seg->rtt_sample = 1; seg->retransmit_cnt = 0; - seg->seq = flow->snd_nxt; + seg->seq = flow->tx.snd_nxt; seg->len = 0; seg->buf = 0; seg->timer_ms = 0; - seg->timeout_ms = flow->rto ? flow->rto : TCP_INIT_RTO; + seg->timeout_ms = flow->tx.rto ? flow->tx.rto : TCP_INIT_RTO; - seg->opts_len = tcp_build_syn_options(seg->opts, (uint16_t)flow->mss, flow->rcv_wnd_max > 65535u ? flow->ws_send : 0xffu, flow->sack_ok); + seg->opts_len = tcp_build_syn_options(seg->opts, (uint16_t)flow->tx.mss, flow->rx.rcv_wnd_max > 65535u ? flow->tx.ws_send : 0xffu, flow->tx.sack_ok); - tcp_send_from_seg(flow, seg); + if (!tcp_send_from_seg(flow, seg)) { + tcp_free_flow(idx); + return false; + } - flow->snd_nxt += 1; - flow->ctx.sequence = flow->snd_nxt; - flow->ctx.expected_ack = flow->snd_nxt; + flow->tx.snd_nxt += 1; + flow->base.ctx.sequence = flow->tx.snd_nxt; + flow->base.ctx.expected_ack = flow->tx.snd_nxt; tcp_daemon_kick(); @@ -538,15 +567,12 @@ bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, const uint64_t max_wait = TCP_CONNECT_TIMEOUT_MS; while (waited < max_wait){ - if (flow->state == TCP_ESTABLISHED || flow->state == TCP_CLOSE_WAIT){ - tcp_data *ctx = tcp_get_ctx(local_port, dst->ver, flow->local.ip, dst->ip, dst->port); - if (!ctx) return false; - - *flow_ctx = *ctx; + if (flow->base.state == TCP_ESTABLISHED || flow->base.state == TCP_CLOSE_WAIT){ + *flow_ctx = flow->base.ctx; return true; } - if (flow->state == TCP_STATE_CLOSED){ + if (flow->base.state == TCP_STATE_CLOSED){ tcp_free_flow(idx); return false; } diff --git a/kernel/networking/transport_layer/tcp/tcp_internal.h b/kernel/networking/transport_layer/tcp/tcp_internal.h index 815e6655..127c19b4 100644 --- a/kernel/networking/transport_layer/tcp/tcp_internal.h +++ b/kernel/networking/transport_layer/tcp/tcp_internal.h @@ -23,10 +23,10 @@ extern "C" { #define TCP_DEFAULT_RCV_BUF (256u * 1024u) #define TCP_PERSIST_PROBE_BUFSZ 1 -#define TCP_DELAYED_ACK_MS 40 +#define TCP_DELAYED_ACK_MS 10 #define TCP_PERSIST_MIN_MS 500 #define TCP_PERSIST_MAX_MS 60000 -#define TCP_INIT_CWND_SEGS 4 +#define TCP_INIT_CWND_SEGS 10 #define TCP_NAGLE_FLUSH_THRESHOLD TCP_DEFAULT_MSS #define TCP_NAGLE_TIMEOUT_MS 10 #define TCP_CONNECT_TIMEOUT_MS 10000 @@ -55,12 +55,18 @@ typedef struct { typedef struct { uint16_t local_port; + uint16_t slot; + uint16_t active_pos; + uint32_t generation; net_l4_endpoint local; net_l4_endpoint remote; uint8_t l3_id; tcp_state_t state; tcp_data ctx; uint8_t retries; +} tcp_flow_base_t; + +typedef struct { uint32_t snd_wnd; uint32_t snd_una; uint32_t snd_nxt; @@ -68,19 +74,6 @@ typedef struct { uint32_t rttvar; uint32_t rto; uint8_t rtt_valid; - uint32_t time_wait_ms; - uint32_t fin_wait2_ms; - - uint32_t rcv_nxt; - uint32_t rcv_base; - uint32_t rcv_data_nxt; - uintptr_t rcv_buf; - uint32_t rcv_ooo_used; - uint32_t sack_recent_left; - uint32_t sack_recent_right; - uint32_t rcv_wnd; - uint32_t rcv_wnd_max; - uint32_t rcv_adv_edge; uint32_t cwnd; uint32_t ssthresh; @@ -95,14 +88,6 @@ typedef struct { uint32_t recover; uint32_t cwnd_acc; - uint8_t persist_active; - uint8_t persist_probe_cnt; - uint32_t persist_timer_ms; - uint32_t persist_timeout_ms; - - uint8_t delayed_ack_pending; - uint32_t delayed_ack_timer_ms; - uint8_t nagle_flushing; uint8_t nagle_appending; @@ -111,30 +96,71 @@ typedef struct { uint32_t nagle_cap; uint32_t nagle_timer_ms; + tcp_tx_seg_t txq[TCP_MAX_TX_SEGS]; + uint8_t fin_tx_pending; +} tcp_flow_tx_t; + +typedef struct { + uint32_t rcv_nxt; + uint32_t rcv_base; + uint32_t rcv_data_nxt; + uintptr_t rcv_buf; + uint32_t rcv_ooo_used; + uint32_t sack_recent_left; + uint32_t sack_recent_right; + uint32_t rcv_wnd; + uint32_t rcv_wnd_max; + uint32_t rcv_adv_edge; + tcp_reass_seg_t reass[TCP_REASS_MAX_SEGS]; uint8_t reass_count; - tcp_tx_seg_t txq[TCP_MAX_TX_SEGS]; uint8_t fin_pending; - uint8_t fin_tx_pending; uint32_t fin_seq; +} tcp_flow_rx_t; + +typedef struct { + uint32_t time_wait_ms; + uint32_t fin_wait2_ms; + + uint8_t persist_active; + uint8_t persist_probe_cnt; + uint32_t persist_timer_ms; + uint32_t persist_timeout_ms; + + uint8_t delayed_ack_pending; + uint32_t delayed_ack_timer_ms; - uint8_t ip_ttl; - uint8_t ip_dontfrag; uint8_t keepalive_on; uint32_t keepalive_ms; uint32_t keepalive_idle_ms; +} tcp_flow_timer_t; + +typedef struct { + uint8_t ttl; + uint8_t dontfrag; +} tcp_flow_ip_t; + +typedef struct { + tcp_flow_base_t base; + tcp_flow_tx_t tx; + tcp_flow_rx_t rx; + tcp_flow_timer_t timer; + tcp_flow_ip_t ip; } tcp_flow_t; extern tcp_flow_t *tcp_flows[MAX_TCP_FLOWS]; +extern uint16_t tcp_active_flows[MAX_TCP_FLOWS]; +extern uint16_t tcp_active_count; tcp_flow_t *tcp_alloc_flow(void); void tcp_free_flow(int idx); -void tcp_abort_flow(int idx); +void tcp_release_io_buffers(tcp_flow_t *f); +tcp_flow_t *tcp_flow_from_ctx(tcp_data *flow_ctx); void tcp_rtt_update(tcp_flow_t *flow, uint32_t sample_ms); tcp_tx_seg_t *tcp_alloc_tx_seg(tcp_flow_t *flow); -void tcp_send_from_seg(tcp_flow_t *flow, tcp_tx_seg_t *seg); +bool tcp_send_from_seg(tcp_flow_t *flow, tcp_tx_seg_t *seg); void tcp_send_ack_now(tcp_flow_t *flow); int tcp_try_send_pending_fin(tcp_flow_t *flow); uint64_t tcp_flush_nagle(tcp_flow_t *flow, uint8_t force); diff --git a/kernel/networking/transport_layer/tcp/tcp_rx.c b/kernel/networking/transport_layer/tcp/tcp_rx.c index b89536a1..7453a620 100644 --- a/kernel/networking/transport_layer/tcp/tcp_rx.c +++ b/kernel/networking/transport_layer/tcp/tcp_rx.c @@ -9,53 +9,65 @@ #include "syscalls/syscalls.h" #include "../tcp.h" -static tcp_flow_t *tcp_flow_from_ctx(tcp_data *flow_ctx) { - if (!flow_ctx) return NULL; - for (int i = 0; i < MAX_TCP_FLOWS; i++) { - tcp_flow_t *flow = tcp_flows[i]; - if (!flow)continue; - if (&flow->ctx == flow_ctx) return flow; +static int16_t tcp_reass_count_ok(tcp_flow_t *flow) { + if (!flow) return 0; + + if (flow->rx.reass_count <= TCP_REASS_MAX_SEGS) return 1; + + flow->rx.reass_count = 0; + flow->rx.rcv_ooo_used = 0; + flow->rx.sack_recent_left = 0; + flow->rx.sack_recent_right = 0; + + for (int i = 0; i < TCP_REASS_MAX_SEGS; i++) { + flow->rx.reass[i].seq = 0; + flow->rx.reass[i].end = 0; } - return NULL; + return 0; } static void tcp_reass_remove(tcp_flow_t *flow, int idx) { - uint32_t len = flow->reass[idx].end - flow->reass[idx].seq; + if (!tcp_reass_count_ok(flow)) return; + if (idx < 0 || idx >= flow->rx.reass_count) return; - if (flow->rcv_ooo_used >= len) flow->rcv_ooo_used -= len; - else flow->rcv_ooo_used = 0; + uint32_t len = 0; + if (flow->rx.reass[idx].end > flow->rx.reass[idx].seq) len = flow->rx.reass[idx].end - flow->rx.reass[idx].seq; - for (int i = idx; i + 1 < flow->reass_count; i++) flow->reass[i] = flow->reass[i+1]; + if (flow->rx.rcv_ooo_used >= len) flow->rx.rcv_ooo_used -= len; + else flow->rx.rcv_ooo_used = 0; - if (flow->reass_count) flow->reass_count--; - flow->reass[flow->reass_count].seq = 0; - flow->reass[flow->reass_count].end = 0; + for (int i = idx; i + 1 < flow->rx.reass_count; i++) flow->rx.reass[i] = flow->rx.reass[i+1]; + + if (flow->rx.reass_count) flow->rx.reass_count--; + flow->rx.reass[flow->rx.reass_count].seq = 0; + flow->rx.reass[flow->rx.reass_count].end = 0; } static int tcp_reass_drain_inseq(tcp_flow_t *flow) { int advanced = 0; + if (!tcp_reass_count_ok(flow)) return 0; for(;;){ int idx = -1; - for (int i = 0; i < flow->reass_count; i++){ - if (flow->reass[i].seq != flow->rcv_nxt) continue; + for (int i = 0; i < flow->rx.reass_count; i++){ + if (flow->rx.reass[i].seq != flow->rx.rcv_nxt) continue; idx = i; break; } if (idx < 0) break; - uint32_t end = flow->reass[idx].end; - if (end <= flow->rcv_nxt) { + uint32_t end = flow->rx.reass[idx].end; + if (end <= flow->rx.rcv_nxt) { tcp_reass_remove(flow, idx); continue; } - flow->rcv_nxt = end; - flow->rcv_data_nxt = end; - flow->ctx.ack = flow->rcv_nxt; + flow->rx.rcv_nxt = end; + flow->rx.rcv_data_nxt = end; + flow->base.ctx.ack = flow->rx.rcv_nxt; tcp_reass_remove(flow, idx); advanced = 1; } @@ -69,44 +81,44 @@ int64_t tcp_flow_read(tcp_data *flow_ctx, void *buf, uint64_t len) { tcp_flow_t *flow = tcp_flow_from_ctx(flow_ctx); if (!flow) return TCP_DISCONNECT; - if (flow->state == TCP_STATE_CLOSED) return TCP_DISCONNECT; + if (flow->base.state == TCP_STATE_CLOSED) return TCP_DISCONNECT; - if (!flow->rcv_buf || !flow->rcv_wnd_max) return 0; - if (flow->rcv_data_nxt < flow->rcv_base) return 0; + if (!flow->rx.rcv_buf || !flow->rx.rcv_wnd_max) return 0; + if (flow->rx.rcv_data_nxt < flow->rx.rcv_base) return 0; - uint32_t n = flow->rcv_data_nxt - flow->rcv_base; + uint32_t n = flow->rx.rcv_data_nxt - flow->rx.rcv_base; if (!n) return 0; if (len < n) n = (uint32_t)len; - uint8_t *rx = (uint8_t*)flow->rcv_buf; + uint8_t *rx = (uint8_t*)flow->rx.rcv_buf; uint8_t *dst = (uint8_t*)buf; - uint32_t pos = flow->rcv_base % flow->rcv_wnd_max; - uint32_t first = flow->rcv_wnd_max - pos; + uint32_t pos = flow->rx.rcv_base % flow->rx.rcv_wnd_max; + uint32_t first = flow->rx.rcv_wnd_max - pos; if (first > n) first = n; if (first) memcpy(dst, rx + pos, first); if (n > first) memcpy(dst + first, rx, n - first); - flow->rcv_base += n; + flow->rx.rcv_base += n; tcp_flow_on_app_read(flow_ctx, n); return n; } uint32_t tcp_flow_readable(tcp_data *flow_ctx) { tcp_flow_t *flow = tcp_flow_from_ctx(flow_ctx); - if (!flow || flow->state == TCP_STATE_CLOSED || !flow->rcv_buf || !flow->rcv_wnd_max) return 0; - if (flow->rcv_data_nxt < flow->rcv_base) return 0; - return flow->rcv_data_nxt - flow->rcv_base; + if (!flow || flow->base.state == TCP_STATE_CLOSED || !flow->rx.rcv_buf || !flow->rx.rcv_wnd_max) return 0; + if (flow->rx.rcv_data_nxt < flow->rx.rcv_base) return 0; + return flow->rx.rcv_data_nxt - flow->rx.rcv_base; } bool tcp_flow_recv_closed(tcp_data *flow_ctx) { tcp_flow_t *flow = tcp_flow_from_ctx(flow_ctx); if (!flow) return true; - if (flow->state == TCP_STATE_CLOSED || flow->state == TCP_TIME_WAIT) return true; - if (flow->state == TCP_CLOSE_WAIT) { - if (!flow->rcv_buf || !flow->rcv_wnd_max) return true; - if (flow->rcv_data_nxt < flow->rcv_base) return true; - return flow->rcv_data_nxt == flow->rcv_base; + if (flow->base.state == TCP_STATE_CLOSED || flow->base.state == TCP_TIME_WAIT) return true; + if (flow->base.state == TCP_CLOSE_WAIT) { + if (!flow->rx.rcv_buf || !flow->rx.rcv_wnd_max) return true; + if (flow->rx.rcv_data_nxt < flow->rx.rcv_base) return true; + return flow->rx.rcv_data_nxt == flow->rx.rcv_base; } return false; } @@ -116,12 +128,12 @@ tcp_tx_seg_t *tcp_find_first_unacked(tcp_flow_t *flow) { uint32_t best_seq = 0; for (int i = 0; i < TCP_MAX_TX_SEGS; i++){ - tcp_tx_seg_t *s = &flow->txq[i]; + tcp_tx_seg_t *s = &flow->tx.txq[i]; if (!s->used) continue; uint32_t end = s->seq + s->len + (s->syn ? 1u : 0u) + (s->fin ? 1u : 0u); - if (end <= flow->snd_una) continue; + if (end <= flow->tx.snd_una) continue; if (!best || s->seq < best_seq){ best = s; @@ -133,7 +145,7 @@ tcp_tx_seg_t *tcp_find_first_unacked(tcp_flow_t *flow) { } static int tcp_apply_sack_blocks(tcp_flow_t *flow, const tcp_parsed_opts_t *opts) { - if (!flow || !opts || !flow->sack_ok || !opts->sack_count) return 0; + if (!flow || !opts || !flow->tx.sack_ok || !opts->sack_count) return 0; int changed = 0; for (uint32_t b = 0; b < opts->sack_count; b++) { @@ -141,16 +153,16 @@ static int tcp_apply_sack_blocks(tcp_flow_t *flow, const tcp_parsed_opts_t *opts uint32_t right = opts->sacks[b].right; if (right <= left) continue; - if (right <= flow->snd_una) continue; - if (left >= flow->snd_nxt) continue; + if (right <= flow->tx.snd_una) continue; + if (left >= flow->tx.snd_nxt) continue; for (int i = 0; i < TCP_MAX_TX_SEGS; i++) { - tcp_tx_seg_t *s = &flow->txq[i]; + tcp_tx_seg_t *s = &flow->tx.txq[i]; if (!s->used) continue; uint32_t end = s->seq + s->len + (s->syn ? 1 : 0) + (s->fin ? 1 : 0); - if (end <= flow->snd_una) continue; + if (end <= flow->tx.snd_una) continue; if (s->seq < left || end > right) continue; if (!s->sacked) changed = 1; @@ -164,28 +176,28 @@ static int tcp_apply_sack_blocks(tcp_flow_t *flow, const tcp_parsed_opts_t *opts static tcp_tx_seg_t *tcp_find_sack_retransmit(tcp_flow_t *flow) { tcp_tx_seg_t *best = NULL; uint32_t best_seq = 0; - uint32_t highest_sacked = flow->snd_una; + uint32_t highest_sacked = flow->tx.snd_una; for (int i = 0; i < TCP_MAX_TX_SEGS; i++) { - tcp_tx_seg_t *s = &flow->txq[i]; + tcp_tx_seg_t *s = &flow->tx.txq[i]; if (!s->used || !s->sacked) continue; uint32_t end = s->seq + s->len + (s->syn ? 1u : 0u) + (s->fin ? 1u : 0u); - if (end <= flow->snd_una) continue; + if (end <= flow->tx.snd_una) continue; if (end > highest_sacked) highest_sacked = end; } - if (highest_sacked == flow->snd_una) return NULL; + if (highest_sacked == flow->tx.snd_una) return NULL; for (int i = 0; i < TCP_MAX_TX_SEGS; i++) { - tcp_tx_seg_t *s = &flow->txq[i]; + tcp_tx_seg_t *s = &flow->tx.txq[i]; if (!s->used)continue; if (s->sacked || s->sack_retransmitted) continue; uint32_t end = s->seq + s->len + (s->syn ? 1 : 0) + (s->fin ? 1 : 0); - if (end <= flow->snd_una) continue; + if (end <= flow->tx.snd_una) continue; if (s->seq >= highest_sacked) continue; if (!best || s->seq < best_seq) { @@ -198,64 +210,64 @@ static tcp_tx_seg_t *tcp_find_sack_retransmit(tcp_flow_t *flow) { } void tcp_cc_on_timeout(tcp_flow_t *f){ - uint32_t mss = f->mss ? f->mss : TCP_DEFAULT_MSS; - uint32_t flight = f->snd_nxt > f->snd_una ? f->snd_nxt - f->snd_una : 0; + uint32_t mss = f->tx.mss ? f->tx.mss : TCP_DEFAULT_MSS; + uint32_t flight = f->tx.snd_nxt > f->tx.snd_una ? f->tx.snd_nxt - f->tx.snd_una : 0; uint32_t half = flight / 2; uint32_t minth = 2u * mss; if (half < minth) half = minth; - f->ssthresh = half; - f->cwnd = mss; - f->cwnd_acc = 0; - f->dup_acks = 0; - f->in_fast_recovery = 0; - f->recover = 0; + f->tx.ssthresh = half; + f->tx.cwnd = mss; + f->tx.cwnd_acc = 0; + f->tx.dup_acks = 0; + f->tx.in_fast_recovery = 0; + f->tx.recover = 0; for (int i = 0; i < TCP_MAX_TX_SEGS; i++) { - f->txq[i].sacked = 0; - f->txq[i].sack_retransmitted = 0; + f->tx.txq[i].sacked = 0; + f->tx.txq[i].sack_retransmitted = 0; } } static void tcp_cc_on_new_ack(tcp_flow_t *f, uint32_t ack) { - uint32_t mss = f->mss ? f->mss : TCP_DEFAULT_MSS; + uint32_t mss = f->tx.mss ? f->tx.mss : TCP_DEFAULT_MSS; - if (f->in_fast_recovery){ - if (ack >= f->recover){ - f->cwnd = f->ssthresh; - if (f->cwnd < mss) f->cwnd = mss; + if (f->tx.in_fast_recovery){ + if (ack >= f->tx.recover){ + f->tx.cwnd = f->tx.ssthresh; + if (f->tx.cwnd < mss) f->tx.cwnd = mss; - f->in_fast_recovery = 0; - f->dup_acks = 0; - f->cwnd_acc = 0; + f->tx.in_fast_recovery = 0; + f->tx.dup_acks = 0; + f->tx.cwnd_acc = 0; return; } - f->cwnd = f->ssthresh; - if (f->cwnd < mss) f->cwnd = mss; + f->tx.cwnd = f->tx.ssthresh; + if (f->tx.cwnd < mss) f->tx.cwnd = mss; return; } - if (f->cwnd < f->ssthresh){ - f->cwnd += mss; - if (f->cwnd < mss) f->cwnd = mss; + if (f->tx.cwnd < f->tx.ssthresh){ + f->tx.cwnd += mss; + if (f->tx.cwnd < mss) f->tx.cwnd = mss; return; } - uint32_t denom = f->cwnd ? f->cwnd : 1u; + uint32_t denom = f->tx.cwnd ? f->tx.cwnd : 1u; uint32_t inc = (mss * mss) / denom; if (inc == 0) inc = 1; - f->cwnd += inc; + f->tx.cwnd += inc; } static void tcp_cc_on_dupack(tcp_flow_t *f) { - uint32_t mss = f->mss ? f->mss : TCP_DEFAULT_MSS; + uint32_t mss = f->tx.mss ? f->tx.mss : TCP_DEFAULT_MSS; - if (f->in_fast_recovery){ - f->cwnd += mss; + if (f->tx.in_fast_recovery){ + f->tx.cwnd += mss; tcp_tx_seg_t *s = tcp_find_sack_retransmit(f); if (s) { @@ -268,18 +280,18 @@ static void tcp_cc_on_dupack(tcp_flow_t *f) { return; } - if (f->dup_acks != 3) return; + if (f->tx.dup_acks != 3) return; - uint32_t flight = f->snd_nxt - f->snd_una; + uint32_t flight = f->tx.snd_nxt - f->tx.snd_una; uint32_t half = flight / 2; uint32_t minth = 2u * mss; if (half < minth) half = minth; - f->ssthresh = half; - f->recover = f->snd_nxt; - f->cwnd = f->ssthresh + 3u * mss; - f->in_fast_recovery = 1; + f->tx.ssthresh = half; + f->tx.recover = f->tx.snd_nxt; + f->tx.cwnd = f->tx.ssthresh + 3u * mss; + f->tx.in_fast_recovery = 1; tcp_tx_seg_t *s = tcp_find_sack_retransmit(f); if (!s) s = tcp_find_first_unacked(f); @@ -330,7 +342,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a uint16_t window = bswap16(hdr.window); uint8_t hdr_len = (uint8_t)((hdr.data_offset_reserved >> 4) * 4); - if (len < hdr_len) { + if (hdr_len < sizeof(tcp_hdr_t) || len < hdr_len) { netpkt_unref(pkt); return; } @@ -348,8 +360,8 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a int idx = find_flow(dst_port, ipver, dst_ip_addr, src_ip_addr, src_port); tcp_flow_t *flow = idx >= 0 ? tcp_flows[idx] : NULL; - if (flow) flow->keepalive_idle_ms = 0; - if (flow) flow->l3_id = l3_id; + if (flow) flow->timer.keepalive_idle_ms = 0; + if (flow) flow->base.l3_id = l3_id; port_manager_t *pm = NULL; uint8_t ifx = 0; @@ -378,11 +390,31 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a } port_recv_handler_t port_handler = port_get_handler(pm, PROTO_TCP, dst_port); + if (flow && flow->base.state == TCP_TIME_WAIT && (flags & (1 << SYN_F)) && !(flags& ((1 << ACK_F) | (1 << RST_F) | (1 << FIN_F))) && data_len == 0) { + int listen_idx = find_flow(dst_port, ipver, dst_ip_addr, NULL, 0); + if (listen_idx < 0)listen_idx = find_flow(dst_port, ipver, NULL, NULL, 0); + if (listen_idx >= 0) { + tcp_free_flow(idx); + flow = NULL; + idx = -1; + } + } + + if (flow && flow->base.state == TCP_SYN_RECEIVED && (flags & (1 << SYN_F)) && !(flags& ((1 << ACK_F) | (1 << RST_F))) && data_len == 0 && seq+1 == flow->rx.rcv_nxt) { + for (int i = 0; i < TCP_MAX_TX_SEGS; i++) { + tcp_tx_seg_t *seg = &flow->tx.txq[i]; + if (!seg->used || !seg->syn) continue; + seg->timer_ms = 0; + tcp_send_from_seg(flow, seg); + break; + } + netpkt_unref(pkt); + return; + } if (!flow){ int listen_idx = find_flow(dst_port, ipver, dst_ip_addr, NULL, 0); - if (listen_idx < 0) - listen_idx = find_flow(dst_port, ipver, NULL, NULL, 0); + if (listen_idx < 0)listen_idx = find_flow(dst_port, ipver, NULL, NULL, 0); if ((flags & (1u << SYN_F)) && !(flags & (1u << ACK_F)) && listen_idx >= 0){ rng_t rng; @@ -395,9 +427,9 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a for (int k = 0; k < MAX_TCP_FLOWS; k++){ tcp_flow_t *f = tcp_flows[k]; if (!f) continue; - if (f->state != TCP_SYN_RECEIVED) continue; + if (f->base.state != TCP_SYN_RECEIVED) continue; syn_total++; - if (f->local_port == dst_port && f->l3_id == l3_id) syn_port++; + if (f->base.local_port == dst_port && f->base.l3_id == l3_id) syn_port++; } if (syn_total >= (MAX_TCP_FLOWS / 4) || syn_port >= 32) { netpkt_unref(pkt); @@ -412,44 +444,38 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a } flow = nf; - for (int k = 0; k < MAX_TCP_FLOWS; k++) { - if (tcp_flows[k] == nf) { - idx = k; - break; - } - } - - flow->local_port = dst_port; - flow->l3_id = l3_id; + idx = (int)flow->base.slot; + flow->base.local_port = dst_port; + flow->base.l3_id = l3_id; - flow->remote.ver = ipver; - memset(flow->remote.ip, 0, 16); - memcpy(flow->remote.ip, src_ip_addr, (uint64_t)(ipver == IP_VER6 ? 16 : 4)); - flow->remote.port = src_port; + flow->base.remote.ver = ipver; + memset(flow->base.remote.ip, 0, 16); + memcpy(flow->base.remote.ip, src_ip_addr, (uint64_t)(ipver == IP_VER6 ? 16 : 4)); + flow->base.remote.port = src_port; - flow->local.ver = ipver; - memset(flow->local.ip, 0, 16); - memcpy(flow->local.ip, dst_ip_addr, (uint64_t)(ipver == IP_VER6 ? 16 : 4)); - flow->local.port = dst_port; + flow->base.local.ver = ipver; + memset(flow->base.local.ip, 0, 16); + memcpy(flow->base.local.ip, dst_ip_addr, (uint64_t)(ipver == IP_VER6 ? 16 : 4)); + flow->base.local.port = dst_port; - flow->state = TCP_SYN_RECEIVED; - flow->retries = TCP_SYN_RETRIES; + flow->base.state = TCP_SYN_RECEIVED; + flow->base.retries = TCP_SYN_RETRIES; tcp_parsed_opts_t pop = parsed_opts; - flow->ws_send = lf->ws_send; - flow->ws_recv = 0; - flow->ws_ok = (lf->ws_ok && pop.has_wscale) ? 1 : 0; - if (flow->ws_ok) { - flow->ws_recv = pop.wscale; - if (flow->ws_recv > 14) flow->ws_recv = 14; + flow->tx.ws_send = lf->tx.ws_send; + flow->tx.ws_recv = 0; + flow->tx.ws_ok = (lf->tx.ws_ok && pop.has_wscale) ? 1 : 0; + if (flow->tx.ws_ok) { + flow->tx.ws_recv = pop.wscale; + if (flow->tx.ws_recv > 14) flow->tx.ws_recv = 14; } else { - flow->ws_send = 0; - flow->ws_recv = 0; + flow->tx.ws_send = 0; + flow->tx.ws_recv = 0; } - flow->sack_ok = (lf->sack_ok && pop.sack_permitted) ? 1 : 0; + flow->tx.sack_ok = (lf->tx.sack_ok && pop.sack_permitted) ? 1 : 0; if (pop.has_mss && pop.mss){ uint32_t m = pop.mss; @@ -457,68 +483,61 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a uint32_t maxm = tcp_calc_mss_for_l3(l3_id, ipver, src_ip_addr); if (m < minm) m = minm; if (m > maxm) m = maxm; - flow->mss = m; - } else flow->mss = tcp_calc_mss_for_l3(l3_id, ipver, src_ip_addr); - flow->ctx.flags = 0; - flow->ctx.options = lf->ctx.options; - flow->ctx.payload.ptr = 0; - flow->ctx.payload.size = 0; + flow->tx.mss = m; + } else flow->tx.mss = tcp_calc_mss_for_l3(l3_id, ipver, src_ip_addr); + flow->base.ctx.flags = 0; + flow->base.ctx.options = lf->base.ctx.options; + flow->base.ctx.payload.ptr = 0; + flow->base.ctx.payload.size = 0; uint32_t iss = rng_next32(&rng); - flow->ctx.sequence = iss; - flow->snd_una = iss; - flow->snd_nxt = iss; + flow->base.ctx.sequence = iss; + flow->tx.snd_una = iss; + flow->tx.snd_nxt = iss; - flow->ctx.ack = seq + 1; - flow->rcv_nxt = seq + 1; + flow->base.ctx.ack = seq + 1; + flow->rx.rcv_nxt = seq + 1; - flow->ctx.expected_ack = iss + 1; - flow->ctx.ack_received = 0; + flow->base.ctx.expected_ack = iss + 1; + flow->base.ctx.ack_received = 0; uint32_t new_wnd = window; - if (flow->ws_ok && flow->ws_recv) new_wnd <<= flow->ws_recv; - flow->snd_wnd = new_wnd; - - flow->persist_active = 0; - flow->persist_timer_ms = 0; - flow->persist_timeout_ms = 0; - - flow->delayed_ack_pending = 0; - flow->delayed_ack_timer_ms = 0; - - flow->rcv_wnd_max = lf->rcv_wnd_max; - flow->rcv_base = flow->rcv_nxt; - flow->rcv_data_nxt = flow->rcv_nxt; - flow->rcv_ooo_used = 0; - flow->sack_recent_left = 0; - flow->sack_recent_right = 0; - flow->rcv_buf = (uintptr_t)zalloc(flow->rcv_wnd_max); - if (!flow->rcv_buf) { - flow->rcv_wnd = 0; - flow->rcv_adv_edge = flow->rcv_nxt; - flow->ctx.window = 0; - tcp_free_flow(idx); - netpkt_unref(pkt); - return; - } - tcp_calc_adv_wnd_field(flow, flow->ws_ok ? 1 : 0); - - flow->ip_ttl = lf->ip_ttl; - flow->ip_dontfrag = lf->ip_dontfrag; - flow->keepalive_on = lf->keepalive_on; - flow->keepalive_ms = lf->keepalive_ms; - flow->keepalive_idle_ms = 0; - - flow->cwnd = flow->mss * TCP_INIT_CWND_SEGS; - if (flow->cwnd < flow->mss) flow->cwnd = flow->mss; - flow->ssthresh = TCP_RECV_WINDOW; - flow->dup_acks = 0; - flow->in_fast_recovery = 0; - flow->recover = 0; - flow->cwnd_acc = 0; - - flow->time_wait_ms = 0; - flow->fin_wait2_ms = 0; + if (flow->tx.ws_ok && flow->tx.ws_recv) new_wnd <<= flow->tx.ws_recv; + flow->tx.snd_wnd = new_wnd; + + flow->timer.persist_active = 0; + flow->timer.persist_timer_ms = 0; + flow->timer.persist_timeout_ms = 0; + + flow->timer.delayed_ack_pending = 0; + flow->timer.delayed_ack_timer_ms = 0; + + flow->rx.rcv_wnd_max = lf->rx.rcv_wnd_max; + flow->rx.rcv_base = flow->rx.rcv_nxt; + flow->rx.rcv_data_nxt = flow->rx.rcv_nxt; + flow->rx.rcv_ooo_used = 0; + flow->rx.sack_recent_left = 0; + flow->rx.sack_recent_right = 0; + flow->rx.rcv_buf = 0; + flow->rx.rcv_adv_edge = flow->rx.rcv_nxt + flow->rx.rcv_wnd_max; + tcp_calc_adv_wnd_field(flow, flow->tx.ws_ok ? 1 : 0); + + flow->ip.ttl = lf->ip.ttl; + flow->ip.dontfrag = lf->ip.dontfrag; + flow->timer.keepalive_on = lf->timer.keepalive_on; + flow->timer.keepalive_ms = lf->timer.keepalive_ms; + flow->timer.keepalive_idle_ms = 0; + + flow->tx.cwnd = flow->tx.mss * TCP_INIT_CWND_SEGS; + if (flow->tx.cwnd < flow->tx.mss) flow->tx.cwnd = flow->tx.mss; + flow->tx.ssthresh = TCP_RECV_WINDOW; + flow->tx.dup_acks = 0; + flow->tx.in_fast_recovery = 0; + flow->tx.recover = 0; + flow->tx.cwnd_acc = 0; + + flow->timer.time_wait_ms = 0; + flow->timer.fin_wait2_ms = 0; tcp_tx_seg_t *seg = tcp_alloc_tx_seg(flow); if (!seg) { @@ -535,11 +554,15 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a seg->len = 0; seg->buf = 0; seg->timer_ms = 0; - seg->timeout_ms = flow->rto ? flow->rto : TCP_INIT_RTO; - seg->opts_len = tcp_build_syn_options(seg->opts, (uint16_t)flow->mss, flow->ws_ok ? flow->ws_send : 0xff, flow->sack_ok); - tcp_send_from_seg(flow, seg); + seg->timeout_ms = flow->tx.rto ? flow->tx.rto : TCP_INIT_RTO; + seg->opts_len = tcp_build_syn_options(seg->opts, (uint16_t)flow->tx.mss, flow->tx.ws_ok ? flow->tx.ws_send : 0xff, flow->tx.sack_ok); + if (!tcp_send_from_seg(flow, seg)) { + tcp_free_flow(idx); + netpkt_unref(pkt); + return; + } - flow->snd_nxt = iss + 1; + flow->tx.snd_nxt = iss + 1; tcp_daemon_kick(); netpkt_unref(pkt); @@ -563,7 +586,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a return; } - if (flow->state == TCP_TIME_WAIT){ + if (flow->base.state == TCP_TIME_WAIT){ if (flags & (1u << RST_F)) { netpkt_unref(pkt); return; @@ -576,23 +599,25 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a uint32_t seg_end = seq + seg_len; - if (seq <= flow->rcv_nxt && seg_end >= flow->rcv_nxt){ - flow->time_wait_ms = 0; + if (seq <= flow->rx.rcv_nxt && seg_end >= flow->rx.rcv_nxt){ + flow->timer.time_wait_ms = 0; tcp_send_ack_now(flow); } netpkt_unref(pkt); return; } + uint32_t old_wnd = flow->tx.snd_wnd; uint32_t new_wnd = window; - if (flow->ws_ok && flow->ws_recv) new_wnd <<= flow->ws_recv; - flow->snd_wnd = new_wnd; - - if (flow->snd_wnd > 0){ - flow->persist_active = 0; - flow->persist_timer_ms = 0; - flow->persist_timeout_ms = 0; - flow->persist_probe_cnt = 0; + if (flow->tx.ws_ok && flow->tx.ws_recv) new_wnd <<= flow->tx.ws_recv; + flow->tx.snd_wnd = new_wnd; + + if (flow->tx.snd_wnd > 0){ + flow->timer.persist_active = 0; + flow->timer.persist_timer_ms = 0; + flow->timer.persist_timeout_ms = 0; + flow->timer.persist_probe_cnt = 0; + if (!old_wnd) tcp_daemon_kick(); } else { tcp_daemon_kick(); } @@ -600,15 +625,15 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a uint8_t fin = (flags & (1u << FIN_F)) ? 1u : 0u; if (flags & (1u << ACK_F)){ - if (ack > flow->snd_una && ack <= flow->snd_nxt){ - uint32_t prev_una = flow->snd_una; + if (ack > flow->tx.snd_una && ack <= flow->tx.snd_nxt){ + uint32_t prev_una = flow->tx.snd_una; - flow->snd_una = ack; - flow->ctx.ack_received = ack; - flow->dup_acks = 0; + flow->tx.snd_una = ack; + flow->base.ctx.ack_received = ack; + flow->tx.dup_acks = 0; for (int i = 0; i < TCP_MAX_TX_SEGS; i++){ - tcp_tx_seg_t *s = &flow->txq[i]; + tcp_tx_seg_t *s = &flow->tx.txq[i]; if (!s->used) continue; uint32_t s_end = s->seq + s->len + (s->syn ? 1u : 0u) + (s->fin ? 1u : 0u); @@ -627,7 +652,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a } for (int i = 0; i < TCP_MAX_TX_SEGS; i++) { - tcp_tx_seg_t *s = &flow->txq[i]; + tcp_tx_seg_t *s = &flow->tx.txq[i]; if (!s->used) continue; if (s->seq >= ack) continue; @@ -635,54 +660,54 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a s->sack_retransmitted = 0; } - if (flow->sack_ok && parsed_opts.sack_count) tcp_apply_sack_blocks(flow, &parsed_opts); + if (flow->tx.sack_ok && parsed_opts.sack_count) tcp_apply_sack_blocks(flow, &parsed_opts); if (ack > prev_una) tcp_cc_on_new_ack(flow, ack); - if (flow->state == TCP_FIN_WAIT_1 && ack >= flow->ctx.expected_ack){ - flow->state = TCP_FIN_WAIT_2; - flow->fin_wait2_ms = 0; + if (flow->base.state == TCP_FIN_WAIT_1 && ack >= flow->base.ctx.expected_ack){ + flow->base.state = TCP_FIN_WAIT_2; + flow->timer.fin_wait2_ms = 0; tcp_daemon_kick(); - } else if ((flow->state == TCP_LAST_ACK || flow->state == TCP_CLOSING) && ack >= flow->ctx.expected_ack){ + } else if ((flow->base.state == TCP_LAST_ACK || flow->base.state == TCP_CLOSING) && ack >= flow->base.ctx.expected_ack){ tcp_free_flow(idx); netpkt_unref(pkt); return; } - } else if (ack == flow->snd_una && data_len == 0 && !fin){ - if (flow->sack_ok && parsed_opts.sack_count) tcp_apply_sack_blocks(flow, &parsed_opts); - if (flow->dup_acks < UINT8_MAX) flow->dup_acks++; + } else if (ack == flow->tx.snd_una && data_len == 0 && !fin){ + if (flow->tx.sack_ok && parsed_opts.sack_count) tcp_apply_sack_blocks(flow, &parsed_opts); + if (flow->tx.dup_acks < UINT8_MAX) flow->tx.dup_acks++; tcp_cc_on_dupack(flow); } else { - flow->dup_acks = 0; + flow->tx.dup_acks = 0; } } - if (flow->nagle_len) tcp_flush_nagle(flow, 0); - if (flow->fin_tx_pending) tcp_try_send_pending_fin(flow); + if (flow->tx.nagle_len) tcp_flush_nagle(flow, flow->tx.fin_tx_pending ? 1 : 0); + if (flow->tx.fin_tx_pending) tcp_try_send_pending_fin(flow); uint32_t seg_seq = seq; - switch (flow->state){ + switch (flow->base.state){ case TCP_SYN_SENT: - if ((flags & (1u << SYN_F)) && (flags & (1u << ACK_F)) && ack == flow->ctx.expected_ack){ - flow->ctx.ack = seq + 1; - flow->rcv_nxt = seq + 1; - flow->ctx.ack_received = ack; - flow->snd_una = ack; - flow->snd_nxt = flow->ctx.sequence; - flow->ctx.sequence = flow->snd_nxt; - flow->ctx.flags = 0; + if ((flags & (1u << SYN_F)) && (flags & (1u << ACK_F)) && ack == flow->base.ctx.expected_ack){ + flow->base.ctx.ack = seq + 1; + flow->rx.rcv_nxt = seq + 1; + flow->base.ctx.ack_received = ack; + flow->tx.snd_una = ack; + flow->tx.snd_nxt = flow->base.ctx.sequence; + flow->base.ctx.sequence = flow->tx.snd_nxt; + flow->base.ctx.flags = 0; tcp_parsed_opts_t pop = parsed_opts; - flow->ws_recv = pop.has_wscale ? pop.wscale : 0; - if (flow->ws_recv > 14) flow->ws_recv = 14; - flow->ws_ok = (flow->ws_send != 0) && pop.has_wscale ? 1 : 0; - if (!flow->ws_ok) { - flow->ws_send = 0; - flow->ws_recv = 0; + flow->tx.ws_recv = pop.has_wscale ? pop.wscale : 0; + if (flow->tx.ws_recv > 14) flow->tx.ws_recv = 14; + flow->tx.ws_ok = (flow->tx.ws_send != 0) && pop.has_wscale ? 1 : 0; + if (!flow->tx.ws_ok) { + flow->tx.ws_send = 0; + flow->tx.ws_recv = 0; } - flow->sack_ok = pop.sack_permitted ? 1 : 0; + flow->tx.sack_ok = pop.sack_permitted ? 1 : 0; if (pop.has_mss && pop.mss){ uint32_t m = pop.mss; @@ -690,81 +715,94 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a uint32_t maxm = tcp_calc_mss_for_l3(l3_id, ipver, src_ip_addr); if (m < minm) m = minm; if (m > maxm) m = maxm; - flow->mss = m; + flow->tx.mss = m; } else { - flow->mss = tcp_calc_mss_for_l3(l3_id, ipver, src_ip_addr); + flow->tx.mss = tcp_calc_mss_for_l3(l3_id, ipver, src_ip_addr); } uint32_t new_wnd = window; - if (flow->ws_ok && flow->ws_recv) new_wnd <<= flow->ws_recv; - flow->snd_wnd = new_wnd; - - flow->rcv_base = flow->rcv_nxt; - flow->rcv_data_nxt = flow->rcv_nxt; - flow->rcv_ooo_used = 0; - flow->sack_recent_left = 0; - flow->sack_recent_right = 0; - if (!flow->rcv_buf || !flow->rcv_wnd_max) { - flow->rcv_wnd = 0; - flow->rcv_adv_edge = flow->rcv_nxt; - flow->ctx.window = 0; + if (flow->tx.ws_ok && flow->tx.ws_recv) new_wnd <<= flow->tx.ws_recv; + flow->tx.snd_wnd = new_wnd; + + flow->rx.rcv_base = flow->rx.rcv_nxt; + flow->rx.rcv_data_nxt = flow->rx.rcv_nxt; + flow->rx.rcv_ooo_used = 0; + flow->rx.sack_recent_left = 0; + flow->rx.sack_recent_right = 0; + if (!flow->rx.rcv_buf || !flow->rx.rcv_wnd_max) { + flow->rx.rcv_wnd = 0; + flow->rx.rcv_adv_edge = flow->rx.rcv_nxt; + flow->base.ctx.window = 0; netpkt_unref(pkt); return; } (void)tcp_calc_adv_wnd_field(flow, 1); tcp_hdr_t final_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.src_port = bswap16(flow->base.local_port); + final_ack.dst_port = bswap16(flow->base.remote.port); + final_ack.sequence = bswap32(flow->base.ctx.sequence); + final_ack.ack = bswap32(flow->base.ctx.ack); final_ack.flags = (uint8_t)(1u << ACK_F); - final_ack.window = flow->ctx.window; + final_ack.window = flow->base.ctx.window; final_ack.urgent_ptr = 0; - if (flow->local.ver == IP_VER4) { + if (flow->base.local.ver == IP_VER4) { ipv4_tx_opts_t tx; - tcp_build_tx_opts_from_local_v4(flow->local.ip, &tx); - tcp_send_segment(IP_VER4, flow->local.ip, flow->remote.ip, &final_ack, NULL, 0, NULL, 0, (const ip_tx_opts_t *)&tx, flow->ip_ttl, flow->ip_dontfrag); + tcp_build_tx_opts_from_local_v4(flow->base.local.ip, &tx); + tcp_send_segment(IP_VER4, flow->base.local.ip, flow->base.remote.ip, &final_ack, NULL, 0, NULL, 0, (const ip_tx_opts_t *)&tx, flow->ip.ttl, flow->ip.dontfrag); } else { ipv6_tx_opts_t tx; - tcp_build_tx_opts_from_local_v6(flow->local.ip, &tx); - tcp_send_segment(IP_VER6, flow->local.ip, flow->remote.ip, &final_ack, NULL, 0, NULL, 0, (const ip_tx_opts_t *)&tx, flow->ip_ttl, flow->ip_dontfrag); + tcp_build_tx_opts_from_local_v6(flow->base.local.ip, &tx); + tcp_send_segment(IP_VER6, flow->base.local.ip, flow->base.remote.ip, &final_ack, NULL, 0, NULL, 0, (const ip_tx_opts_t *)&tx, flow->ip.ttl, flow->ip.dontfrag); } - flow->state = TCP_ESTABLISHED; - flow->delayed_ack_pending = 0; - flow->delayed_ack_timer_ms = 0; + flow->base.state = TCP_ESTABLISHED; + flow->timer.delayed_ack_pending = 0; + flow->timer.delayed_ack_timer_ms = 0; tcp_daemon_kick(); } else if (flags & (1u << RST_F)){ - flow->state = TCP_STATE_CLOSED; + flow->base.state = TCP_STATE_CLOSED; } netpkt_unref(pkt); return; case TCP_SYN_RECEIVED: - if ((flags & (1u << ACK_F)) && !(flags & (1u << SYN_F)) && !(flags & (1u << RST_F)) && ack == flow->ctx.expected_ack){ + if ((flags & (1u << ACK_F)) && !(flags & (1u << SYN_F)) && !(flags & (1u << RST_F)) && ack == flow->base.ctx.expected_ack){ + if (!flow->rx.rcv_buf && flow->rx.rcv_wnd_max) { + flow->rx.rcv_buf = (uintptr_t)zalloc(flow->rx.rcv_wnd_max); + if (!flow->rx.rcv_buf) { + tcp_send_reset(ipver, dst_ip_addr, src_ip_addr, dst_port, src_port, 0, flow->base.ctx.ack, true); + tcp_free_flow(idx); + netpkt_unref(pkt); + return; + } + flow->rx.rcv_base = flow->rx.rcv_nxt; + flow->rx.rcv_data_nxt = flow->rx.rcv_nxt; + tcp_calc_adv_wnd_field(flow, 1); + } + uint32_t queued = 0; if (port_handler) queued = port_handler(ifx, ipver, src_ip_addr, dst_ip_addr, 0, src_port, dst_port); if (!queued) { tcp_hdr_t rst_hdr; - rst_hdr.src_port = bswap16(flow->local_port); - rst_hdr.dst_port = bswap16(flow->remote.port); - rst_hdr.sequence = bswap32(flow->snd_nxt); - rst_hdr.ack = bswap32(flow->ctx.ack); + rst_hdr.src_port = bswap16(flow->base.local_port); + rst_hdr.dst_port = bswap16(flow->base.remote.port); + rst_hdr.sequence = bswap32(flow->tx.snd_nxt); + rst_hdr.ack = bswap32(flow->base.ctx.ack); rst_hdr.flags = (uint8_t)((1 << RST_F) | (1 << ACK_F)); rst_hdr.window = 0; rst_hdr.urgent_ptr = 0; - if (flow->local.ver == IP_VER4) { + if (flow->base.local.ver == IP_VER4) { ipv4_tx_opts_t tx; - tcp_build_tx_opts_from_local_v4(flow->local.ip, &tx); - (void)tcp_send_segment(IP_VER4, flow->local.ip, flow->remote.ip, &rst_hdr, NULL, 0, NULL, 0, (const ip_tx_opts_t *)&tx, flow->ip_ttl, flow->ip_dontfrag); - } else if (flow->local.ver == IP_VER6) { + tcp_build_tx_opts_from_local_v4(flow->base.local.ip, &tx); + (void)tcp_send_segment(IP_VER4, flow->base.local.ip, flow->base.remote.ip, &rst_hdr, NULL, 0, NULL, 0, (const ip_tx_opts_t *)&tx, flow->ip.ttl, flow->ip.dontfrag); + } else if (flow->base.local.ver == IP_VER6) { ipv6_tx_opts_t tx; - tcp_build_tx_opts_from_local_v6(flow->local.ip, &tx); - (void)tcp_send_segment(IP_VER6, flow->local.ip, flow->remote.ip, &rst_hdr, NULL, 0, NULL, 0, (const ip_tx_opts_t *)&tx, flow->ip_ttl, flow->ip_dontfrag); + tcp_build_tx_opts_from_local_v6(flow->base.local.ip, &tx); + (void)tcp_send_segment(IP_VER6, flow->base.local.ip, flow->base.remote.ip, &rst_hdr, NULL, 0, NULL, 0, (const ip_tx_opts_t *)&tx, flow->ip.ttl, flow->ip.dontfrag); } tcp_free_flow(idx); @@ -772,12 +810,12 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a return; } - flow->ctx.sequence = flow->snd_nxt; - flow->snd_una = ack; - flow->state = TCP_ESTABLISHED; - flow->delayed_ack_pending = 0; - flow->delayed_ack_timer_ms = 0; - flow->ctx.ack_received = ack; + flow->base.ctx.sequence = flow->tx.snd_nxt; + flow->tx.snd_una = ack; + flow->base.state = TCP_ESTABLISHED; + flow->timer.delayed_ack_pending = 0; + flow->timer.delayed_ack_timer_ms = 0; + flow->base.ctx.ack_received = ack; tcp_daemon_kick(); } else if (flags & (1u << RST_F)){ @@ -792,7 +830,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a } if (flags & (1u << RST_F)) { - tcp_abort_flow(idx); + tcp_free_flow(idx); netpkt_unref(pkt); return; } @@ -802,11 +840,12 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a int ack_defer = 0; if (data_len || fin) { - uint32_t rcv_nxt = flow->rcv_nxt; - uint32_t wnd_end = rcv_nxt + flow->rcv_wnd; + uint32_t rcv_nxt = flow->rx.rcv_nxt; + uint32_t wnd_end = rcv_nxt + flow->rx.rcv_wnd; uint32_t orig_data_len = data_len; uint8_t fin_in = fin; + uint8_t had_reass = flow->rx.reass_count ? 1 : 0; uint32_t fin_seq = seg_seq + orig_data_len; uint32_t orig_end = seg_seq + orig_data_len + (fin ? 1u : 0u); @@ -841,21 +880,21 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a if (!data_len && !fin_in){ need_ack = 1; ack_immediate = 1; - } else if (seg_seq == flow->rcv_nxt) { + } else if (seg_seq == flow->rx.rcv_nxt) { if (data_len){ uint32_t offer = data_len; - uint32_t right_edge = flow->rcv_adv_edge; - uint32_t hard_edge = flow->rcv_buf && flow->rcv_wnd_max ? flow->rcv_base + flow->rcv_wnd_max : flow->rcv_nxt; + uint32_t right_edge = flow->rx.rcv_adv_edge; + uint32_t hard_edge = flow->rx.rcv_buf && flow->rx.rcv_wnd_max ? flow->rx.rcv_base + flow->rx.rcv_wnd_max : flow->rx.rcv_nxt; if (right_edge > hard_edge) right_edge = hard_edge; if (seg_seq >= right_edge) offer = 0; else if (seg_seq + offer > right_edge) offer = right_edge - seg_seq; uint32_t accepted = 0; uint32_t stored = 0; - if (offer && (flow->state == TCP_FIN_WAIT_1 || flow->state == TCP_FIN_WAIT_2 || flow->state == TCP_CLOSING || flow->state == TCP_LAST_ACK || flow->state == TCP_TIME_WAIT)) accepted = offer; - else if (offer && flow->rcv_buf && flow->rcv_wnd_max) { - uint8_t *rx = (uint8_t *)flow->rcv_buf; - uint32_t cap = flow->rcv_wnd_max; + if (offer && (flow->base.state == TCP_FIN_WAIT_1 || flow->base.state == TCP_FIN_WAIT_2 || flow->base.state == TCP_CLOSING || flow->base.state == TCP_LAST_ACK || flow->base.state == TCP_TIME_WAIT)) accepted = offer; + else if (offer && flow->rx.rcv_buf && flow->rx.rcv_wnd_max) { + uint8_t *rx = (uint8_t *)flow->rx.rcv_buf; + uint32_t cap = flow->rx.rcv_wnd_max; uint32_t pos = seg_seq % cap; uint32_t first = cap - pos; @@ -868,9 +907,9 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a } if (accepted) { - flow->rcv_nxt += accepted; - if (stored) flow->rcv_data_nxt = flow->rcv_nxt; - flow->ctx.ack = flow->rcv_nxt; + flow->rx.rcv_nxt += accepted; + if (stored) flow->rx.rcv_data_nxt = flow->rx.rcv_nxt; + flow->base.ctx.ack = flow->rx.rcv_nxt; } else if (data_len) { (void)tcp_calc_adv_wnd_field(flow, 1); need_ack = 1; @@ -881,41 +920,44 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a } if (fin_in) { - if (flow->rcv_nxt == fin_seq) { - flow->rcv_nxt += 1; - flow->ctx.ack = flow->rcv_nxt; + if (flow->rx.rcv_nxt == fin_seq) { + flow->rx.rcv_nxt += 1; + flow->base.ctx.ack = flow->rx.rcv_nxt; - tcp_state_t old = flow->state; + tcp_state_t old = flow->base.state; - if (old == TCP_ESTABLISHED) flow->state = TCP_CLOSE_WAIT; - else if (old == TCP_FIN_WAIT_1) flow->state = TCP_CLOSING; + if (old == TCP_ESTABLISHED) flow->base.state = TCP_CLOSE_WAIT; + else if (old == TCP_FIN_WAIT_1) flow->base.state = TCP_CLOSING; else if (old == TCP_FIN_WAIT_2 || old == TCP_CLOSING || old == TCP_LAST_ACK) { - flow->state = TCP_TIME_WAIT; - flow->time_wait_ms = 0; + flow->base.state = TCP_TIME_WAIT; + tcp_release_io_buffers(flow); + flow->timer.time_wait_ms = 0; tcp_daemon_kick(); } ack_immediate = 1; } else { - flow->fin_pending = 1; - flow->fin_seq = fin_seq; + flow->rx.fin_pending = 1; + flow->rx.fin_seq = fin_seq; } } if (tcp_reass_drain_inseq(flow)) ack_immediate = 1; + if (had_reass) ack_immediate = 1; - if (flow->fin_pending && flow->fin_seq == flow->rcv_nxt){ - flow->fin_pending = 0; - flow->rcv_nxt += 1; - flow->ctx.ack = flow->rcv_nxt; + if (flow->rx.fin_pending && flow->rx.fin_seq == flow->rx.rcv_nxt){ + flow->rx.fin_pending = 0; + flow->rx.rcv_nxt += 1; + flow->base.ctx.ack = flow->rx.rcv_nxt; - tcp_state_t old = flow->state; + tcp_state_t old = flow->base.state; - if (old == TCP_ESTABLISHED) flow->state = TCP_CLOSE_WAIT; - else if (old == TCP_FIN_WAIT_1) flow->state = TCP_CLOSING; + if (old == TCP_ESTABLISHED) flow->base.state = TCP_CLOSE_WAIT; + else if (old == TCP_FIN_WAIT_1) flow->base.state = TCP_CLOSING; else if (old == TCP_FIN_WAIT_2 || old == TCP_CLOSING || old == TCP_LAST_ACK) { - flow->state = TCP_TIME_WAIT; - flow->time_wait_ms = 0; + flow->base.state = TCP_TIME_WAIT; + tcp_release_io_buffers(flow); + flow->timer.time_wait_ms = 0; tcp_daemon_kick(); } @@ -927,13 +969,13 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a if (!ack_immediate && data_len) ack_defer = 1; need_ack = 1; } else { - if (!(flow->state == TCP_FIN_WAIT_1 || flow->state == TCP_FIN_WAIT_2 || flow->state == TCP_CLOSING || flow->state == TCP_LAST_ACK || flow->state == TCP_TIME_WAIT) && data_len && flow->rcv_buf && flow->rcv_wnd_max) { + if (!(flow->base.state == TCP_FIN_WAIT_1 || flow->base.state == TCP_FIN_WAIT_2 || flow->base.state == TCP_CLOSING || flow->base.state == TCP_LAST_ACK || flow->base.state == TCP_TIME_WAIT) && data_len && flow->rx.rcv_buf && flow->rx.rcv_wnd_max) { uint32_t ooo_seq = seg_seq; uint32_t ooo_data = payload; uint32_t ooo_len = data_len; - if (ooo_seq rcv_nxt) { - uint32_t d = flow->rcv_nxt - ooo_seq; + if (ooo_seq rx.rcv_nxt) { + uint32_t d = flow->rx.rcv_nxt - ooo_seq; if (d >= ooo_len) ooo_len= 0; else { ooo_seq += d; @@ -943,8 +985,8 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a } if (ooo_len) { - uint32_t right_edge = flow->rcv_adv_edge; - uint32_t hard_edge = flow->rcv_base + flow->rcv_wnd_max; + uint32_t right_edge = flow->rx.rcv_adv_edge; + uint32_t hard_edge = flow->rx.rcv_base + flow->rx.rcv_wnd_max; if (right_edge > hard_edge) right_edge = hard_edge; if (ooo_seq >= right_edge) ooo_len = 0; else if (ooo_seq + ooo_len > right_edge) ooo_len = right_edge - ooo_seq; @@ -959,11 +1001,12 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a int16_t overlapping = 0; int16_t covered = 0; - for (int i = 0; i < flow->reass_count; i++) { - tcp_reass_seg_t *r = &flow->reass[i]; + if (!tcp_reass_count_ok(flow)) ooo_len = 0; + for (int i = 0; ooo_len && i < flow->rx.reass_count; i++) { + tcp_reass_seg_t *r = &flow->rx.reass[i]; if (r->seq <= start && r->end >= end) { - flow->sack_recent_left = r->seq; - flow->sack_recent_right = r->end; + flow->rx.sack_recent_left = r->seq; + flow->rx.sack_recent_right = r->end; covered = 1; break; } @@ -972,25 +1015,28 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a if (r->seq < merged_start) merged_start = r->seq; if (r->end > merged_end) merged_end = r->end; - old_bytes += r->end - r->seq; + if (r->end > r->seq) old_bytes += r->end - r->seq; overlapping++; } - if (!covered) { + if (!covered && ooo_len) { uint32_t merged_len = merged_end - merged_start; uint32_t increase = merged_len > old_bytes ? merged_len - old_bytes : 0; - uint32_t ooo_limit = flow->rcv_wnd_max >> 1; + uint32_t ooo_limit = flow->rx.rcv_wnd_max >> 1; + int remaining = flow->rx.reass_count - overlapping; + if (remaining < 0) remaining = 0; if (ooo_limit > TCP_REASS_MAX_BYTES) ooo_limit = TCP_REASS_MAX_BYTES; - if (ooo_limit < flow->mss * 4) ooo_limit = flow->mss * 4; - if (ooo_limit > flow->rcv_wnd_max) ooo_limit = flow->rcv_wnd_max; + if (ooo_limit < flow->tx.mss * 4) ooo_limit = flow->tx.mss * 4; + if (ooo_limit > flow->rx.rcv_wnd_max) ooo_limit = flow->rx.rcv_wnd_max; - if (increase && (flow->rcv_ooo_used + increase > ooo_limit || flow->reass_count - overlapping >= TCP_REASS_MAX_SEGS)) ooo_len = 0; + if (remaining >= TCP_REASS_MAX_SEGS) ooo_len = 0; + else if (increase && flow->rx.rcv_ooo_used + increase > ooo_limit) ooo_len = 0; } if (!covered && ooo_len) { - uint8_t *rx = (uint8_t *)flow->rcv_buf; - uint32_t cap = flow->rcv_wnd_max; + uint8_t *rx = (uint8_t *)flow->rx.rcv_buf; + uint32_t cap = flow->rx.rcv_wnd_max; uint32_t pos = start % cap; uint32_t first = cap - pos; int copied = 1; @@ -1000,8 +1046,8 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a if (copied && ooo_len > first && !netpkt_copyout(pkt, ooo_data + first, rx, ooo_len - first)) copied = 0; if (copied) { - for (int i = 0; i < flow->reass_count;) { - tcp_reass_seg_t *r = &flow->reass[i]; + for (int i = 0; i < flow->rx.reass_count;) { + tcp_reass_seg_t *r = &flow->rx.reass[i]; if (r->end < merged_start || r->seq > merged_end) { i++; continue; @@ -1010,27 +1056,29 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a tcp_reass_remove(flow,i); } - int pos_idx = flow->reass_count; - while (pos_idx > 0 &&flow->reass[pos_idx - 1].seq > merged_start) { - flow->reass[pos_idx] = flow->reass[pos_idx - 1]; - pos_idx--; - } + if (flow->rx.reass_count < TCP_REASS_MAX_SEGS) { + int pos_idx = flow->rx.reass_count; + while (pos_idx > 0 &&flow->rx.reass[pos_idx - 1].seq > merged_start) { + flow->rx.reass[pos_idx] = flow->rx.reass[pos_idx - 1]; + pos_idx--; + } - flow->reass[pos_idx].seq = merged_start; - flow->reass[pos_idx].end = merged_end; - flow->reass_count++; - flow->rcv_ooo_used += merged_end - merged_start; - flow->sack_recent_left = merged_start; - flow->sack_recent_right = merged_end; - tcp_calc_adv_wnd_field(flow, 1); + flow->rx.reass[pos_idx].seq = merged_start; + flow->rx.reass[pos_idx].end = merged_end; + flow->rx.reass_count++; + flow->rx.rcv_ooo_used += merged_end - merged_start; + flow->rx.sack_recent_left = merged_start; + flow->rx.sack_recent_right = merged_end; + tcp_calc_adv_wnd_field(flow, 1); + } } } } } if (fin_in){ - flow->fin_pending = 1; - flow->fin_seq = fin_seq; + flow->rx.fin_pending = 1; + flow->rx.fin_seq = fin_seq; } need_ack = 1; @@ -1043,17 +1091,17 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a if (ack_immediate){ tcp_send_ack_now(flow); } else if (ack_defer){ - if (!flow->delayed_ack_pending){ - flow->delayed_ack_pending = 1; - flow->delayed_ack_timer_ms = 0; + if (!flow->timer.delayed_ack_pending){ + flow->timer.delayed_ack_pending = 1; + flow->timer.delayed_ack_timer_ms = 0; tcp_daemon_kick(); } else { tcp_send_ack_now(flow); } } else { - if (!flow->delayed_ack_pending){ - flow->delayed_ack_pending = 1; - flow->delayed_ack_timer_ms = 0; + if (!flow->timer.delayed_ack_pending){ + flow->timer.delayed_ack_pending = 1; + flow->timer.delayed_ack_timer_ms = 0; tcp_daemon_kick(); } else { tcp_send_ack_now(flow); @@ -1069,20 +1117,20 @@ void tcp_flow_on_app_read(tcp_data *flow_ctx, uint32_t bytes_read){ tcp_flow_t *flow = tcp_flow_from_ctx(flow_ctx); if (!flow) return; - uint32_t old_edge = flow->rcv_adv_edge; - uint16_t old_adv_field = flow->ctx.window; - uint32_t old_nxt = flow->rcv_nxt; + uint32_t old_edge = flow->rx.rcv_adv_edge; + uint16_t old_adv_field = flow->base.ctx.window; + uint32_t old_nxt = flow->rx.rcv_nxt; int advanced = tcp_reass_drain_inseq(flow); tcp_calc_adv_wnd_field(flow, 1); - if (flow->state == TCP_STATE_CLOSED || flow->state == TCP_TIME_WAIT) return; + if (flow->base.state == TCP_STATE_CLOSED || flow->base.state == TCP_TIME_WAIT) return; - uint32_t threshold = flow->mss ? flow->mss : TCP_DEFAULT_MSS; - uint32_t half = flow->rcv_wnd_max >> 1; + uint32_t threshold = flow->tx.mss ? flow->tx.mss : TCP_DEFAULT_MSS; + uint32_t half = flow->rx.rcv_wnd_max >> 1; if (half && half < threshold) threshold = half; if (!threshold) threshold = 1; - uint32_t delta = flow->rcv_adv_edge > old_edge ? flow->rcv_adv_edge - old_edge : 0; - if (advanced || flow->rcv_nxt != old_nxt || (!old_adv_field && flow->ctx.window) || delta >= threshold) tcp_send_ack_now(flow); + uint32_t delta = flow->rx.rcv_adv_edge > old_edge ? flow->rx.rcv_adv_edge - old_edge : 0; + if (advanced || flow->rx.rcv_nxt != old_nxt || (!old_adv_field && flow->base.ctx.window) || delta >= threshold) tcp_send_ack_now(flow); } diff --git a/kernel/networking/transport_layer/tcp/tcp_timer.c b/kernel/networking/transport_layer/tcp/tcp_timer.c index 4d775257..3a4e2ee1 100644 --- a/kernel/networking/transport_layer/tcp/tcp_timer.c +++ b/kernel/networking/transport_layer/tcp/tcp_timer.c @@ -3,47 +3,47 @@ #include "exceptions/irq.h" static volatile int tcp_daemon_running = 0; -//TODO make tcp_daemon_running atomic or use a lock, this may end in a double deamon process +//TODO events void tcp_daemon_kick(void) { if(!tcp_has_pending_timers()) return; - disable_interrupt(); + irq_flags_t irq_flags = irq_save_disable(); if(tcp_daemon_running){ - enable_interrupt(); + irq_restore(irq_flags); return; } tcp_daemon_running = 1; - enable_interrupt(); + irq_restore(irq_flags); process_t *p = create_kernel_process("tcp_timer", tcp_daemon_entry, 0, 0); if(!p){ - disable_interrupt(); + irq_flags = irq_save_disable(); tcp_daemon_running = 0; - enable_interrupt(); + irq_restore(irq_flags); } } int tcp_has_pending_timers(void) { //TODO mhh this should be event driven to avoid MAX_TCP_FLOWS*TCP_MAX_TX_SEGS scans. - for (int i = 0; i < MAX_TCP_FLOWS; i++) { - tcp_flow_t *f = tcp_flows[i]; + for (uint16_t n = 0; n < tcp_active_count; n++) { + tcp_flow_t *f = tcp_flows[tcp_active_flows[n]]; if (!f) continue; - if (f->state == TCP_STATE_CLOSED) continue; + if (f->base.state == TCP_STATE_CLOSED) continue; - if (f->state == TCP_TIME_WAIT) return 1; - if (f->state == TCP_FIN_WAIT_2) return 1; - if (f->fin_tx_pending && f->snd_wnd == 0) return 1; - if (f->nagle_len) return 1; - if (f->delayed_ack_pending) return 1; - if (f->persist_active) return 1; - if (!f->fin_tx_pending && f->keepalive_on && f->state == TCP_ESTABLISHED && f->keepalive_ms) return 1; + if (f->base.state == TCP_TIME_WAIT) return 1; + if (f->base.state == TCP_FIN_WAIT_2) return 1; + if (f->tx.fin_tx_pending && f->tx.snd_wnd == 0) return 1; + if (f->tx.nagle_len) return 1; + if (f->timer.delayed_ack_pending) return 1; + if (f->timer.persist_active) return 1; + if (!f->tx.fin_tx_pending && f->timer.keepalive_on && f->base.state == TCP_ESTABLISHED && f->timer.keepalive_ms) return 1; for (int j = 0; j < TCP_MAX_TX_SEGS; j++) { - tcp_tx_seg_t *s = &f->txq[j]; - if (!s->used) continue; - uint32_t end = s->seq + s->len + (s->syn ? 1u : 0u) + (s->fin ? 1u : 0u); - if (end > f->snd_una) return 1; + tcp_tx_seg_t *seg = &f->tx.txq[j]; + if (!seg->used) continue; + uint32_t end_seq = seg->seq + seg->len + (seg->syn ? 1u : 0u) + (seg->fin ? 1u : 0u); + if (end_seq > f->tx.snd_una) return 1; } } @@ -51,93 +51,95 @@ int tcp_has_pending_timers(void) { //TODO mhh this should be event driven to avo } void tcp_tick_all(uint32_t elapsed_ms) { - for (int i = 0; i < MAX_TCP_FLOWS; i++) { + for (uint16_t pos = tcp_active_count; pos > 0; pos--) { + int i = tcp_active_flows[pos-1]; tcp_flow_t *f = tcp_flows[i]; if (!f) continue; - if (f->state == TCP_STATE_CLOSED) continue; + if (f->base.state == TCP_STATE_CLOSED) continue; - if (f->state == TCP_TIME_WAIT) { - f->time_wait_ms += elapsed_ms; - if (f->time_wait_ms >= TCP_2MSL_MS) { + if (f->base.state == TCP_TIME_WAIT) { + tcp_release_io_buffers(f); + f->timer.time_wait_ms += elapsed_ms; + if (f->timer.time_wait_ms >= TCP_2MSL_MS) { tcp_free_flow(i); continue; } } - if (f->state == TCP_FIN_WAIT_2) { - f->fin_wait2_ms += elapsed_ms; - if (f->fin_wait2_ms >= TCP_2MSL_MS) { + if (f->base.state == TCP_FIN_WAIT_2) { + f->timer.fin_wait2_ms += elapsed_ms; + if (f->timer.fin_wait2_ms >= TCP_2MSL_MS) { tcp_free_flow(i); continue; } } - if (f->delayed_ack_pending) { - f->delayed_ack_timer_ms += elapsed_ms; - if (f->delayed_ack_timer_ms >= TCP_DELAYED_ACK_MS) tcp_send_ack_now(f); + if (f->timer.delayed_ack_pending) { + f->timer.delayed_ack_timer_ms += elapsed_ms; + if (f->timer.delayed_ack_timer_ms >= TCP_DELAYED_ACK_MS) tcp_send_ack_now(f); } - if (f->nagle_len) { - if (f->snd_nxt == f->snd_una) tcp_flush_nagle(f, 1); + if (f->tx.nagle_len) { + if (f->tx.snd_nxt == f->tx.snd_una) tcp_flush_nagle(f, 1); else { - f->nagle_timer_ms += elapsed_ms; - if (f->nagle_timer_ms >= TCP_NAGLE_TIMEOUT_MS) tcp_flush_nagle(f, 1); + f->tx.nagle_timer_ms += elapsed_ms; + if (f->tx.nagle_timer_ms >= TCP_NAGLE_TIMEOUT_MS) tcp_flush_nagle(f, 1); } } - if (!f->fin_tx_pending && f->keepalive_on && f->state == TCP_ESTABLISHED && f->keepalive_ms) { - f->keepalive_idle_ms += elapsed_ms; - if (f->keepalive_idle_ms >= f->keepalive_ms) { + if (!f->tx.fin_tx_pending && f->timer.keepalive_on && f->base.state == TCP_ESTABLISHED && f->timer.keepalive_ms) { + f->timer.keepalive_idle_ms += elapsed_ms; + if (f->timer.keepalive_idle_ms >= f->timer.keepalive_ms) { tcp_hdr_t hdr; - hdr.src_port = bswap16(f->local_port); - hdr.dst_port = bswap16(f->remote.port); - uint32_t seq = f->snd_nxt; + hdr.src_port = bswap16(f->base.local_port); + hdr.dst_port = bswap16(f->base.remote.port); + uint32_t seq = f->tx.snd_nxt; if (seq) seq -= 1; hdr.sequence = bswap32(seq); - hdr.ack = bswap32(f->ctx.ack); + hdr.ack = bswap32(f->base.ctx.ack); hdr.flags = (uint8_t)(1u << ACK_F); hdr.window = tcp_calc_adv_wnd_field(f, 1); hdr.urgent_ptr = 0; - if (f->local.ver == IP_VER4) { + if (f->base.local.ver == IP_VER4) { ipv4_tx_opts_t tx; - tcp_build_tx_opts_from_local_v4(f->local.ip, &tx); - (void)tcp_send_segment(IP_VER4, f->local.ip, f->remote.ip, &hdr, NULL, 0, NULL, 0, (const ip_tx_opts_t *)&tx, f->ip_ttl, f->ip_dontfrag); - } else if (f->local.ver == IP_VER6) { + tcp_build_tx_opts_from_local_v4(f->base.local.ip, &tx); + (void)tcp_send_segment(IP_VER4, f->base.local.ip, f->base.remote.ip, &hdr, NULL, 0, NULL, 0, (const ip_tx_opts_t *)&tx, f->ip.ttl, f->ip.dontfrag); + } else if (f->base.local.ver == IP_VER6) { ipv6_tx_opts_t tx; - tcp_build_tx_opts_from_local_v6(f->local.ip, &tx); - (void)tcp_send_segment(IP_VER6, f->local.ip, f->remote.ip, &hdr, NULL, 0, NULL, 0, (const ip_tx_opts_t *)&tx, f->ip_ttl, f->ip_dontfrag); + tcp_build_tx_opts_from_local_v6(f->base.local.ip, &tx); + (void)tcp_send_segment(IP_VER6, f->base.local.ip, f->base.remote.ip, &hdr, NULL, 0, NULL, 0, (const ip_tx_opts_t *)&tx, f->ip.ttl, f->ip.dontfrag); } - f->keepalive_idle_ms = 0; + f->timer.keepalive_idle_ms = 0; } } - if (f->snd_wnd == 0 && (f->snd_nxt > f->snd_una || f->fin_tx_pending)) { - if (!f->persist_active) { - f->persist_active = 1; - f->persist_timer_ms = 0; - f->persist_probe_cnt = 0; - f->persist_timeout_ms = TCP_PERSIST_MIN_MS; + if (f->tx.snd_wnd == 0 && (f->tx.snd_nxt > f->tx.snd_una || f->tx.fin_tx_pending)) { + if (!f->timer.persist_active) { + f->timer.persist_active = 1; + f->timer.persist_timer_ms = 0; + f->timer.persist_probe_cnt = 0; + f->timer.persist_timeout_ms = TCP_PERSIST_MIN_MS; } else { - f->persist_timer_ms += elapsed_ms; - if (f->persist_timer_ms >= f->persist_timeout_ms) { - if (f->fin_tx_pending && f->persist_probe_cnt >= TCP_MAX_PERSIST_PROBES) { - if (f->state == TCP_ESTABLISHED || f->state == TCP_CLOSE_WAIT) tcp_abort_flow(i); + f->timer.persist_timer_ms += elapsed_ms; + if (f->timer.persist_timer_ms >= f->timer.persist_timeout_ms) { + if (f->tx.fin_tx_pending && f->timer.persist_probe_cnt >= TCP_MAX_PERSIST_PROBES) { + if (f->base.state == TCP_ESTABLISHED || f->base.state == TCP_CLOSE_WAIT) tcp_free_flow(i); else tcp_free_flow(i); continue; } tcp_tx_seg_t *best = tcp_find_first_unacked(f); tcp_hdr_t hdr; - hdr.src_port = bswap16(f->local_port); - hdr.dst_port = bswap16(f->remote.port); + hdr.src_port = bswap16(f->base.local_port); + hdr.dst_port = bswap16(f->base.remote.port); uint8_t payload[1]; const uint8_t *pp = NULL; uint16_t pl = 0; - uint32_t probe_seq = f->snd_una; - if (!best && f->fin_tx_pending && f->snd_nxt == f->snd_una && f->snd_nxt) probe_seq = f->snd_nxt-1; + uint32_t probe_seq = f->tx.snd_una; + if (!best && f->tx.fin_tx_pending && f->tx.snd_nxt == f->tx.snd_una && f->tx.snd_nxt) probe_seq = f->tx.snd_nxt-1; if (best && best->buf && best->len && probe_seq >= best->seq && probe_seq < best->seq + best->len) { payload[0] = *((uint8_t *)best->buf + (probe_seq - best->seq)); @@ -146,47 +148,48 @@ void tcp_tick_all(uint32_t elapsed_ms) { } hdr.sequence = bswap32(probe_seq); - hdr.ack = bswap32(f->ctx.ack); + hdr.ack = bswap32(f->base.ctx.ack); hdr.flags = (uint8_t)(1u << ACK_F); hdr.window = tcp_calc_adv_wnd_field(f, 1); hdr.urgent_ptr = 0; - if (f->local.ver == IP_VER4) { + if (f->base.local.ver == IP_VER4) { ipv4_tx_opts_t tx; - tcp_build_tx_opts_from_local_v4(f->local.ip, &tx); - (void)tcp_send_segment(IP_VER4, f->local.ip, f->remote.ip, &hdr, NULL, 0, pp, pl, (const ip_tx_opts_t *)&tx, f->ip_ttl, f->ip_dontfrag); - } else if (f->local.ver == IP_VER6) { + tcp_build_tx_opts_from_local_v4(f->base.local.ip, &tx); + (void)tcp_send_segment(IP_VER4, f->base.local.ip, f->base.remote.ip, &hdr, NULL, 0, pp, pl, (const ip_tx_opts_t *)&tx, f->ip.ttl, f->ip.dontfrag); + } else if (f->base.local.ver == IP_VER6) { ipv6_tx_opts_t tx; - tcp_build_tx_opts_from_local_v6(f->local.ip, &tx); - (void)tcp_send_segment(IP_VER6, f->local.ip, f->remote.ip, &hdr, NULL, 0, pp, pl, (const ip_tx_opts_t *)&tx, f->ip_ttl, f->ip_dontfrag); + tcp_build_tx_opts_from_local_v6(f->base.local.ip, &tx); + (void)tcp_send_segment(IP_VER6, f->base.local.ip, f->base.remote.ip, &hdr, NULL, 0, pp, pl, (const ip_tx_opts_t *)&tx, f->ip.ttl, f->ip.dontfrag); } - if (f->persist_probe_cnt < UINT8_MAX) f->persist_probe_cnt++; - f->persist_timer_ms = 0; + if (f->timer.persist_probe_cnt < UINT8_MAX) f->timer.persist_probe_cnt++; + f->timer.persist_timer_ms = 0; - if (f->persist_timeout_ms < TCP_PERSIST_MAX_MS) { - uint32_t next = f->persist_timeout_ms << 1; + if (f->timer.persist_timeout_ms < TCP_PERSIST_MAX_MS) { + uint32_t next = f->timer.persist_timeout_ms << 1; if (next > TCP_PERSIST_MAX_MS) next = TCP_PERSIST_MAX_MS; - f->persist_timeout_ms = next; + f->timer.persist_timeout_ms = next; } } } } else { - f->persist_active = 0; - f->persist_timer_ms = 0; - f->persist_timeout_ms = 0; - f->persist_probe_cnt = 0; + f->timer.persist_active = 0; + f->timer.persist_timer_ms = 0; + f->timer.persist_timeout_ms = 0; + f->timer.persist_probe_cnt = 0; } for (int j = 0; j < TCP_MAX_TX_SEGS; j++) { - tcp_tx_seg_t *s = &f->txq[j]; + tcp_tx_seg_t *s = &f->tx.txq[j]; if (!s->used) continue; s->timer_ms += elapsed_ms; if (s->timer_ms < s->timeout_ms) continue; + if (f->tx.snd_wnd == 0 && s->len > 0) continue; if (s->retransmit_cnt >= TCP_MAX_RETRANS) { - if (f->state == TCP_ESTABLISHED || f->state == TCP_CLOSE_WAIT) tcp_abort_flow(i); + if (f->base.state == TCP_ESTABLISHED || f->base.state == TCP_CLOSE_WAIT) tcp_free_flow(i); else tcp_free_flow(i); break; } @@ -199,7 +202,7 @@ void tcp_tick_all(uint32_t elapsed_ms) { s->timer_ms = 0; if (s->timeout_ms == 0) { - uint32_t rto = f->rto ? f->rto : TCP_INIT_RTO; + uint32_t rto = f->tx.rto ? f->tx.rto : TCP_INIT_RTO; if (rto < TCP_MIN_RTO) rto = TCP_MIN_RTO; s->timeout_ms = rto; } else if (s->timeout_ms < TCP_MAX_RTO) { @@ -217,22 +220,11 @@ int tcp_daemon_entry(int argc, char *argv[]) { (void)argv; const uint32_t tick_ms = 25; - const uint32_t grace_ms = 10000; - uint32_t idle_ms = 0; while (1) { - if (tcp_has_pending_timers()) { - tcp_tick_all(tick_ms); - idle_ms = 0; - } else { - idle_ms += tick_ms; - if(idle_ms >= grace_ms) break; - } + if (tcp_has_pending_timers()) tcp_tick_all(tick_ms); msleep(tick_ms); } - disable_interrupt(); - tcp_daemon_running = 0; - enable_interrupt(); return 0; } diff --git a/kernel/networking/transport_layer/tcp/tcp_tx.c b/kernel/networking/transport_layer/tcp/tcp_tx.c index 0cd18c87..15b36d11 100644 --- a/kernel/networking/transport_layer/tcp/tcp_tx.c +++ b/kernel/networking/transport_layer/tcp/tcp_tx.c @@ -3,77 +3,74 @@ uint16_t tcp_calc_adv_wnd_field(tcp_flow_t *flow, uint8_t apply_scale) { if (!flow) return 0; - uint32_t quantum = 1; - if (apply_scale && flow->ws_ok && flow->ws_send) quantum = 1u << flow->ws_send; - - uint32_t hard_edge = flow->rcv_nxt; - if (flow->rcv_buf && flow->rcv_wnd_max) { - hard_edge = flow->rcv_base + flow->rcv_wnd_max; - if (hard_edge < flow->rcv_nxt) hard_edge = flow->rcv_nxt; - } - - if (flow->rcv_adv_edge < flow->rcv_nxt) flow->rcv_adv_edge = flow->rcv_nxt; - uint32_t accept_edge = flow->rcv_adv_edge; - uint32_t accept_adv = accept_edge - flow->rcv_nxt; + uint32_t shift = 0; + if (apply_scale && flow->tx.ws_ok && flow->tx.ws_send) shift = flow->tx.ws_send; + if (flow->rx.rcv_adv_edge < flow->rx.rcv_nxt) flow->rx.rcv_adv_edge = flow->rx.rcv_nxt; + uint32_t accept_edge = flow->rx.rcv_adv_edge; + + uint32_t hard_edge = flow->rx.rcv_nxt; + if (flow->rx.rcv_buf && flow->rx.rcv_wnd_max) { + hard_edge = flow->rx.rcv_base + flow->rx.rcv_wnd_max; + if (hard_edge < flow->rx.rcv_nxt) hard_edge = flow->rx.rcv_nxt; + } else if (flow->base.state == TCP_SYN_RECEIVED && flow->rx.rcv_wnd_max) hard_edge = flow->rx.rcv_nxt + flow->rx.rcv_wnd_max; + + if (hard_edge > accept_edge) { + uint32_t adv = hard_edge - flow->rx.rcv_nxt; + + if (apply_scale && adv) { + uint32_t threshold = flow->tx.mss ? flow->tx.mss : TCP_DEFAULT_MSS; + uint32_t half = flow->rx.rcv_wnd_max >> 1; + uint32_t already = accept_edge > flow->rx.rcv_nxt ? accept_edge - flow->rx.rcv_nxt : 0; + if (half && half < threshold) threshold = half; + if (!threshold) threshold = 1; + if (!already && adv < threshold) adv = 0; + } - uint32_t adv = accept_adv; - if (hard_edge > accept_edge) adv = hard_edge - flow->rcv_nxt; - if (quantum > 1) adv &= ~(quantum - 1); + if (adv) { + uint32_t field = adv; + uint32_t actual = adv; + + if (shift) { + field = adv >> shift; + if (field > 65535u) field = 65535u; + actual = field << shift; + } else if (field > 65535u) { + field = 65535u; + actual = field; + } - if (apply_scale && adv) { - uint32_t threshold = flow->mss ? flow->mss : TCP_DEFAULT_MSS; - uint32_t half = flow->rcv_wnd_max >> 1; - if (half && half < threshold) threshold = half; - if (!threshold) threshold = 1; - if (adv < threshold) adv = 0; + uint32_t new_edge = flow->rx.rcv_nxt + actual; + if (actual && new_edge > accept_edge) accept_edge = new_edge; + } } - uint32_t field = adv; - if (!apply_scale || !flow->ws_ok || flow->ws_send == 0) { - if (field > 65535u) field = 65535u; - adv = field; - } else { - field = adv >> flow->ws_send; - if (field > 65535u) field = 65535u; - adv = field << flow->ws_send; - } + if (accept_edge < flow->rx.rcv_nxt) accept_edge = flow->rx.rcv_nxt; + uint32_t accept_adv = accept_edge - flow->rx.rcv_nxt; + uint32_t field = accept_adv; - if (adv) { - flow->rcv_wnd = adv; - flow->rcv_adv_edge = flow->rcv_nxt + adv; - } else { - flow->rcv_wnd = accept_adv; - flow->rcv_adv_edge = accept_edge; - } + if (shift) field >>= shift; + if (field > 65535) field = 65535; - flow->ctx.window = (uint16_t)field; + flow->rx.rcv_wnd = accept_adv; + flow->rx.rcv_adv_edge = accept_edge; + flow->base.ctx.window = (uint16_t)field; return (uint16_t)field; } -static void tcp_persist_arm(tcp_flow_t *flow) { - if (!flow) return; - flow->persist_active = 1; - flow->persist_timer_ms = 0; - if (flow->persist_timeout_ms == 0) flow->persist_timeout_ms = TCP_PERSIST_MIN_MS; - if (flow->persist_timeout_ms < TCP_PERSIST_MIN_MS) flow->persist_timeout_ms = TCP_PERSIST_MIN_MS; - if (flow->persist_timeout_ms > TCP_PERSIST_MAX_MS) flow->persist_timeout_ms = TCP_PERSIST_MAX_MS; - tcp_daemon_kick(); -} - static uint32_t tcp_nagle_threshold(tcp_flow_t *flow) { - uint32_t threshold = flow && flow->mss? flow->mss : TCP_NAGLE_FLUSH_THRESHOLD; + uint32_t threshold = flow && flow->tx.mss? flow->tx.mss : TCP_NAGLE_FLUSH_THRESHOLD; if (!threshold) threshold = 1; return threshold; } static uint64_t tcp_emit_data(tcp_flow_t *flow, const uint8_t *payload, uint64_t payload_len) { if (!flow || (!payload && payload_len)) return 0; - if (flow->state != TCP_ESTABLISHED && flow->state != TCP_CLOSE_WAIT) return 0; + if (flow->base.state != TCP_ESTABLISHED && flow->base.state != TCP_CLOSE_WAIT) return 0; - uint64_t in_flight = flow->snd_nxt - flow->snd_una; - uint32_t wnd = flow->snd_wnd; - uint32_t cwnd = flow->cwnd ? flow->cwnd : (flow->mss ? flow->mss : TCP_DEFAULT_MSS); + uint64_t in_flight = flow->tx.snd_nxt - flow->tx.snd_una; + uint32_t wnd = flow->tx.snd_wnd; + uint32_t cwnd = flow->tx.cwnd ? flow->tx.cwnd : (flow->tx.mss ? flow->tx.mss : TCP_DEFAULT_MSS); uint32_t eff_wnd = wnd < cwnd ? wnd : cwnd; uint64_t can_send = 0; @@ -85,7 +82,7 @@ static uint64_t tcp_emit_data(tcp_flow_t *flow, const uint8_t *payload, uint64_t while (remaining > 0 && can_send > 0) { uint64_t seg_len = remaining > can_send ? can_send : remaining; - if (flow->mss && seg_len > flow->mss) seg_len = flow->mss; + if (flow->tx.mss && seg_len > flow->tx.mss) seg_len = flow->tx.mss; tcp_tx_seg_t *seg = tcp_alloc_tx_seg(flow); if (!seg) break; @@ -98,105 +95,109 @@ static uint64_t tcp_emit_data(tcp_flow_t *flow, const uint8_t *payload, uint64_t memcpy((void*)buf, payload + sent_bytes, seg_len); - seg->seq = flow->snd_nxt; + seg->seq = flow->tx.snd_nxt; seg->len = seg_len; seg->buf = buf; seg->syn = 0; seg->fin = 0; seg->timer_ms = 0; - seg->timeout_ms = flow->rto ? flow->rto : TCP_INIT_RTO; + seg->timeout_ms = flow->tx.rto ? flow->tx.rto : TCP_INIT_RTO; seg->retransmit_cnt = 0; seg->rtt_sample = 0; - if (!flow->rtt_valid && first_segment) seg->rtt_sample = 1; - tcp_send_from_seg(flow, seg); + if (!flow->tx.rtt_valid && first_segment) seg->rtt_sample = 1; + if (!tcp_send_from_seg(flow, seg)) { + if (seg->buf && seg->len) release((void*)seg->buf); + memset(seg, 0, sizeof(*seg)); + break; + } - flow->snd_nxt += seg_len; + flow->tx.snd_nxt += seg_len; sent_bytes += seg_len; remaining -= seg_len; can_send -= seg_len; first_segment = 0; } - flow->ctx.sequence = flow->snd_nxt; + flow->base.ctx.sequence = flow->tx.snd_nxt; return sent_bytes; } static uint64_t tcp_nagle_append(tcp_flow_t *flow, const uint8_t *payload, uint64_t payload_len) { if (!flow || (!payload && payload_len) || !payload_len) return 0; - if (flow->nagle_flushing || flow->nagle_appending) return 0; + if (flow->tx.nagle_flushing || flow->tx.nagle_appending) return 0; - flow->nagle_appending = 1; + flow->tx.nagle_appending = 1; uint32_t cap = tcp_nagle_threshold(flow); - if (flow->nagle_len >= cap) { - flow->nagle_appending = 0; + if (flow->tx.nagle_len >= cap) { + flow->tx.nagle_appending = 0; return 0; } - if (!flow->nagle_buf || flow->nagle_cap < cap) { + if (!flow->tx.nagle_buf || flow->tx.nagle_cap < cap) { uintptr_t nb = (uintptr_t)zalloc(cap); if (!nb) { - flow->nagle_appending = 0; + flow->tx.nagle_appending = 0; return 0; } - if (flow->nagle_buf && flow->nagle_len) memcpy((void*)nb, (const void*)flow->nagle_buf, flow->nagle_len); - if (flow->nagle_buf) release((void*)flow->nagle_buf); + if (flow->tx.nagle_buf && flow->tx.nagle_len) memcpy((void*)nb, (const void*)flow->tx.nagle_buf, flow->tx.nagle_len); + if (flow->tx.nagle_buf) release((void*)flow->tx.nagle_buf); - flow->nagle_buf = nb; - flow->nagle_cap = cap; + flow->tx.nagle_buf = nb; + flow->tx.nagle_cap = cap; } uint64_t n = payload_len; - uint32_t room = flow->nagle_cap - flow->nagle_len; + uint32_t room = flow->tx.nagle_cap - flow->tx.nagle_len; if (n > room)n = room; - memcpy((void*)(flow->nagle_buf + flow->nagle_len), payload, n); - flow->nagle_len += (uint32_t)n; - flow->nagle_appending = 0; + memcpy((void*)(flow->tx.nagle_buf + flow->tx.nagle_len), payload, n); + flow->tx.nagle_len += (uint32_t)n; + flow->tx.nagle_appending = 0; tcp_daemon_kick(); return n; } uint64_t tcp_flush_nagle(tcp_flow_t *flow, uint8_t force) { - if (!flow || !flow->nagle_len || !flow->nagle_buf) return 0; - if (flow->nagle_flushing || flow->nagle_appending) return 0; + if (!flow || !flow->tx.nagle_len || !flow->tx.nagle_buf) return 0; + if (flow->tx.nagle_flushing || flow->tx.nagle_appending) return 0; - uint64_t in_flight = flow->snd_nxt - flow->snd_una; - if (!force && in_flight && flow->nagle_lentx.snd_nxt - flow->tx.snd_una; + if (!force && in_flight && flow->tx.nagle_lennagle_flushing = 1; + flow->tx.nagle_flushing = 1; - uint32_t old_len = flow->nagle_len; - uint64_t sent = tcp_emit_data(flow, (const uint8_t*)flow->nagle_buf, old_len); + uint32_t old_len = flow->tx.nagle_len; + uint64_t sent = tcp_emit_data(flow, (const uint8_t*)flow->tx.nagle_buf, old_len); if (!sent) { - flow->nagle_flushing = 0; + flow->tx.nagle_flushing = 0; return 0; } if (sent >= old_len) { - release((void*)flow->nagle_buf); - flow->nagle_buf = 0; - flow->nagle_len = 0; - flow->nagle_cap = 0; - flow->nagle_timer_ms = 0; - flow->nagle_flushing = 0; + release((void*)flow->tx.nagle_buf); + flow->tx.nagle_buf = 0; + flow->tx.nagle_len = 0; + flow->tx.nagle_cap = 0; + flow->tx.nagle_timer_ms = 0; + flow->tx.nagle_flushing = 0; return sent; } - memmove((void*)flow->nagle_buf, (const void*)(flow->nagle_buf + sent), old_len - sent); - flow->nagle_len = old_len - (uint32_t)sent; - flow->nagle_timer_ms = 0; - flow->nagle_flushing = 0; + memmove((void*)flow->tx.nagle_buf, (const void*)(flow->tx.nagle_buf + sent), old_len - sent); + flow->tx.nagle_len = old_len - (uint32_t)sent; + flow->tx.nagle_timer_ms = 0; + flow->tx.nagle_flushing = 0; tcp_daemon_kick(); return sent; } tcp_tx_seg_t *tcp_alloc_tx_seg(tcp_flow_t *flow){ for (int i = 0; i < TCP_MAX_TX_SEGS; i++) { - if (!flow->txq[i].used) { - tcp_tx_seg_t *s = &flow->txq[i]; + if (!flow->tx.txq[i].used) { + tcp_tx_seg_t *s = &flow->tx.txq[i]; s->used = 1; s->syn = 0; s->fin = 0; @@ -210,27 +211,26 @@ tcp_tx_seg_t *tcp_alloc_tx_seg(tcp_flow_t *flow){ s->len = 0; s->buf = 0; s->timer_ms = 0; - s->timeout_ms = flow->rto ? flow->rto : TCP_INIT_RTO; - tcp_daemon_kick(); + s->timeout_ms = flow->tx.rto ? flow->tx.rto : TCP_INIT_RTO; return s; } } return NULL; } -void tcp_send_from_seg(tcp_flow_t *flow, tcp_tx_seg_t *seg){ - if (!flow || !seg) return; - if (flow->state == TCP_STATE_CLOSED) return; - if (flow) flow->keepalive_idle_ms = 0; +bool tcp_send_from_seg(tcp_flow_t *flow, tcp_tx_seg_t *seg){ + if (!flow || !seg) return false; + if (flow->base.state == TCP_STATE_CLOSED) return false; + if (flow) flow->timer.keepalive_idle_ms = 0; tcp_hdr_t hdr; - hdr.src_port = bswap16(flow->local_port); - hdr.dst_port = bswap16(flow->remote.port); + hdr.src_port = bswap16(flow->base.local_port); + hdr.dst_port = bswap16(flow->base.remote.port); hdr.sequence = bswap32(seg->seq); - hdr.ack = bswap32(flow->ctx.ack); + hdr.ack = bswap32(flow->base.ctx.ack); uint8_t flags = 0; - if (!(flow->state == TCP_SYN_SENT && seg->syn && flow->ctx.ack == 0)) flags |= (uint8_t)(1u << ACK_F); + if (!(flow->base.state == TCP_SYN_SENT && seg->syn && flow->base.ctx.ack == 0)) flags |= (uint8_t)(1u << ACK_F); if (seg->syn) flags |= (uint8_t)(1u << SYN_F); if (seg->fin) flags |= (uint8_t)(1u << FIN_F); hdr.flags = flags; @@ -239,27 +239,31 @@ void tcp_send_from_seg(tcp_flow_t *flow, tcp_tx_seg_t *seg){ hdr.urgent_ptr = 0; const uint8_t *opts = seg->opts_len ? seg->opts : NULL; - if (flow->local.ver == IP_VER4) { + if (flow->base.local.ver == IP_VER4) { ipv4_tx_opts_t tx; - tcp_build_tx_opts_from_local_v4(flow->local.ip, &tx); - (void)tcp_send_segment(IP_VER4, flow->local.ip, flow->remote.ip, &hdr, opts, seg->opts_len, seg->buf ? (const uint8_t *)seg->buf : NULL, seg->len, (const ip_tx_opts_t *)&tx, flow->ip_ttl, flow->ip_dontfrag); - } else if (flow->local.ver == IP_VER6) { + tcp_build_tx_opts_from_local_v4(flow->base.local.ip, &tx); + bool ok = tcp_send_segment(IP_VER4, flow->base.local.ip, flow->base.remote.ip, &hdr, opts, seg->opts_len, seg->buf ? (const uint8_t *)seg->buf : NULL, seg->len, (const ip_tx_opts_t *)&tx, flow->ip.ttl, flow->ip.dontfrag); + if (ok) tcp_daemon_kick(); + return ok; + } else if (flow->base.local.ver == IP_VER6) { ipv6_tx_opts_t tx; - tcp_build_tx_opts_from_local_v6(flow->local.ip, &tx); - (void)tcp_send_segment(IP_VER6, flow->local.ip, flow->remote.ip, &hdr, opts, seg->opts_len, seg->buf ? (const uint8_t *)seg->buf : NULL, seg->len, (const ip_tx_opts_t *)&tx, flow->ip_ttl, flow->ip_dontfrag); + tcp_build_tx_opts_from_local_v6(flow->base.local.ip, &tx); + bool ok = tcp_send_segment(IP_VER6, flow->base.local.ip, flow->base.remote.ip, &hdr, opts, seg->opts_len, seg->buf ? (const uint8_t *)seg->buf : NULL, seg->len, (const ip_tx_opts_t *)&tx, flow->ip.ttl, flow->ip.dontfrag); + if (ok) tcp_daemon_kick(); + return ok; } - tcp_daemon_kick(); + return false; } void tcp_send_ack_now(tcp_flow_t *flow){ if (!flow) return; tcp_hdr_t ackhdr; - ackhdr.src_port = bswap16(flow->local_port); - ackhdr.dst_port = bswap16(flow->remote.port); - ackhdr.sequence = bswap32(flow->ctx.sequence); - ackhdr.ack = bswap32(flow->ctx.ack); + ackhdr.src_port = bswap16(flow->base.local_port); + ackhdr.dst_port = bswap16(flow->base.remote.port); + ackhdr.sequence = bswap32(flow->base.ctx.sequence); + ackhdr.ack = bswap32(flow->base.ctx.ack); ackhdr.flags = (uint8_t)(1u << ACK_F); ackhdr.window = tcp_calc_adv_wnd_field(flow, 1); ackhdr.urgent_ptr = 0; @@ -269,25 +273,25 @@ void tcp_send_ack_now(tcp_flow_t *flow){ opts_len = 0; - if (flow->sack_ok && flow->reass_count > 0) { + if (flow->tx.sack_ok && flow->rx.reass_count > 0) { tcp_sack_block_t blocks[TCP_SACK_MAX_BLOCKS]; uint32_t n = 0; - if (flow->sack_recent_right > flow->sack_recent_left) { - for (uint32_t i = 0; i < flow->reass_count; i++) { - if (flow->reass[i].seq > flow->sack_recent_left) continue; - if (flow->reass[i].end < flow->sack_recent_right) continue; + if (flow->rx.sack_recent_right > flow->rx.sack_recent_left) { + for (uint32_t i = 0; i < flow->rx.reass_count; i++) { + if (flow->rx.reass[i].seq > flow->rx.sack_recent_left) continue; + if (flow->rx.reass[i].end < flow->rx.sack_recent_right) continue; - blocks[n].left = flow->reass[i].seq; - blocks[n].right = flow->reass[i].end; + blocks[n].left = flow->rx.reass[i].seq; + blocks[n].right = flow->rx.reass[i].end; n++; break; } } - for (uint32_t i = 0; i < flow->reass_count && n < TCP_SACK_MAX_BLOCKS; i++) { - uint32_t left = flow->reass[i].seq; - uint32_t right = flow->reass[i].end; + for (uint32_t i = 0; i < flow->rx.reass_count && n < TCP_SACK_MAX_BLOCKS; i++) { + uint32_t left = flow->rx.reass[i].seq; + uint32_t right = flow->rx.reass[i].end; int duplicate = 0; for (uint32_t j = 0; j < n; j++) { @@ -326,56 +330,59 @@ void tcp_send_ack_now(tcp_flow_t *flow){ } } - if (flow->local.ver == IP_VER4) { + if (flow->base.local.ver == IP_VER4) { ipv4_tx_opts_t tx; - tcp_build_tx_opts_from_local_v4(flow->local.ip, &tx); - (void)tcp_send_segment(IP_VER4, flow->local.ip, flow->remote.ip, &ackhdr, opts_len ? opts : NULL, opts_len, NULL, 0, (const ip_tx_opts_t *)&tx, flow->ip_ttl, flow->ip_dontfrag); - } else if (flow->local.ver == IP_VER6) { + tcp_build_tx_opts_from_local_v4(flow->base.local.ip, &tx); + (void)tcp_send_segment(IP_VER4, flow->base.local.ip, flow->base.remote.ip, &ackhdr, opts_len ? opts : NULL, opts_len, NULL, 0, (const ip_tx_opts_t *)&tx, flow->ip.ttl, flow->ip.dontfrag); + } else if (flow->base.local.ver == IP_VER6) { ipv6_tx_opts_t tx; - tcp_build_tx_opts_from_local_v6(flow->local.ip, &tx); - (void)tcp_send_segment(IP_VER6, flow->local.ip, flow->remote.ip, &ackhdr, opts_len ? opts : NULL, opts_len, NULL, 0, (const ip_tx_opts_t *)&tx, flow->ip_ttl, flow->ip_dontfrag); + tcp_build_tx_opts_from_local_v6(flow->base.local.ip, &tx); + (void)tcp_send_segment(IP_VER6, flow->base.local.ip, flow->base.remote.ip, &ackhdr, opts_len ? opts : NULL, opts_len, NULL, 0, (const ip_tx_opts_t *)&tx, flow->ip.ttl, flow->ip.dontfrag); } - flow->delayed_ack_pending = 0; - flow->delayed_ack_timer_ms = 0; - tcp_daemon_kick(); + flow->timer.delayed_ack_pending = 0; + flow->timer.delayed_ack_timer_ms = 0; } int tcp_try_send_pending_fin(tcp_flow_t *flow) { - if (!flow || !flow->fin_tx_pending) return 0; - if (flow->state != TCP_ESTABLISHED&& flow->state != TCP_CLOSE_WAIT) return 0; + if (!flow || !flow->tx.fin_tx_pending) return 0; + if (flow->base.state != TCP_ESTABLISHED&& flow->base.state != TCP_CLOSE_WAIT) return 0; - if (flow->nagle_len) tcp_flush_nagle(flow, 1); - if (flow->nagle_len) return 0; + if (flow->tx.nagle_len) tcp_flush_nagle(flow, 1); + if (flow->tx.nagle_len) return 0; - uint64_t in_flight = flow->snd_nxt - flow->snd_una; - uint32_t wnd = flow->snd_wnd; - uint32_t cwnd = flow->cwnd ? flow->cwnd : (flow->mss ? flow->mss : TCP_DEFAULT_MSS); + uint64_t in_flight = flow->tx.snd_nxt - flow->tx.snd_una; + uint32_t wnd = flow->tx.snd_wnd; + uint32_t cwnd = flow->tx.cwnd ? flow->tx.cwnd : (flow->tx.mss ? flow->tx.mss : TCP_DEFAULT_MSS); uint32_t eff_wnd = wnd < cwnd ? wnd : cwnd; if (eff_wnd == 0 || in_flight >= eff_wnd) return 0; tcp_tx_seg_t *seg = tcp_alloc_tx_seg(flow); if (!seg) return 0; - seg->seq = flow->snd_nxt; + seg->seq = flow->tx.snd_nxt; seg->len = 0; seg->buf = 0; seg->syn = 0; seg->fin = 1; seg->timer_ms = 0; - seg->timeout_ms = flow->rto ? flow->rto : TCP_INIT_RTO; + seg->timeout_ms = flow->tx.rto ? flow->tx.rto : TCP_INIT_RTO; seg->retransmit_cnt = 0; seg->rtt_sample = 0; - tcp_send_from_seg(flow, seg); + if (!tcp_send_from_seg(flow, seg)) { + memset(seg, 0, sizeof(*seg)); + tcp_daemon_kick(); + return 0; + } - flow->snd_nxt += 1; - flow->ctx.expected_ack = flow->snd_nxt; - flow->ctx.sequence = flow->snd_nxt; - flow->fin_tx_pending = 0; + flow->tx.snd_nxt += 1; + flow->base.ctx.expected_ack = flow->tx.snd_nxt; + flow->base.ctx.sequence = flow->tx.snd_nxt; + flow->tx.fin_tx_pending = 0; - if (flow->state == TCP_ESTABLISHED) flow->state = TCP_FIN_WAIT_1; - else flow->state = TCP_LAST_ACK; + if (flow->base.state == TCP_ESTABLISHED) flow->base.state = TCP_FIN_WAIT_1; + else flow->base.state = TCP_LAST_ACK; tcp_daemon_kick(); return 1; @@ -384,40 +391,26 @@ int tcp_try_send_pending_fin(tcp_flow_t *flow) { tcp_result_t tcp_flow_flush(tcp_data *flow_ctx){ if (!flow_ctx) return TCP_INVALID; - tcp_flow_t *flow = NULL; - for (int i = 0; i < MAX_TCP_FLOWS; i++) { - if (!tcp_flows[i]) continue; - if (&tcp_flows[i]->ctx == flow_ctx) { - flow = tcp_flows[i]; - break; - } - } + tcp_flow_t *flow = tcp_flow_from_ctx(flow_ctx); if (!flow) return TCP_INVALID; - if (flow->state != TCP_ESTABLISHED && flow->state != TCP_CLOSE_WAIT && flow->state != TCP_FIN_WAIT_1 && flow->state != TCP_FIN_WAIT_2) return TCP_INVALID; + if (flow->base.state != TCP_ESTABLISHED && flow->base.state != TCP_CLOSE_WAIT && flow->base.state != TCP_FIN_WAIT_1 && flow->base.state != TCP_FIN_WAIT_2) return TCP_INVALID; - while (flow->nagle_len) { - uint32_t before = flow->nagle_len; + while (flow->tx.nagle_len) { + uint32_t before = flow->tx.nagle_len; uint64_t sent = tcp_flush_nagle(flow, 1); - if (!sent || flow->nagle_len == before) break; + if (!sent || flow->tx.nagle_len == before) break; } - if (flow->fin_tx_pending) tcp_try_send_pending_fin(flow); - tcp_daemon_kick(); - return flow->nagle_len ? TCP_WOULDBLOCK : TCP_OK; + if (flow->tx.fin_tx_pending) tcp_try_send_pending_fin(flow); + if (flow->tx.nagle_len)tcp_daemon_kick(); + return flow->tx.nagle_len ? TCP_WOULDBLOCK : TCP_OK; } tcp_result_t tcp_flow_send(tcp_data *flow_ctx){ if (!flow_ctx) return TCP_INVALID; - tcp_flow_t *flow = NULL; - for (int i = 0; i < MAX_TCP_FLOWS; i++) { - if (!tcp_flows[i]) continue; - if (&tcp_flows[i]->ctx == flow_ctx) { - flow = tcp_flows[i]; - break; - } - } + tcp_flow_t *flow = tcp_flow_from_ctx(flow_ctx); if (!flow) return TCP_INVALID; uint8_t flags = flow_ctx->flags; @@ -427,16 +420,16 @@ tcp_result_t tcp_flow_send(tcp_data *flow_ctx){ int want_fin = (flags & (1u << FIN_F)) != 0; int fin_queued = 0; - if (flow->state != TCP_ESTABLISHED && flow->state != TCP_CLOSE_WAIT) return TCP_INVALID; + if (flow->base.state != TCP_ESTABLISHED && flow->base.state != TCP_CLOSE_WAIT) return TCP_INVALID; uint64_t accepted = 0; - if (payload_len && flow->nagle_len) { + if (payload_len && flow->tx.nagle_len) { uint64_t n = tcp_nagle_append(flow, payload_ptr, payload_len); accepted += n; payload_ptr += n; payload_len -= n; - if (flow->nagle_len >= tcp_nagle_threshold(flow)) tcp_flush_nagle(flow, 1); + if (flow->tx.nagle_len >= tcp_nagle_threshold(flow)) tcp_flush_nagle(flow, 1); } if (payload_len && payload_len < tcp_nagle_threshold(flow)) { @@ -444,12 +437,12 @@ tcp_result_t tcp_flow_send(tcp_data *flow_ctx){ accepted += n; payload_ptr += n; payload_len -= n; - if (flow->nagle_len >= tcp_nagle_threshold(flow)) tcp_flush_nagle(flow, 1); + if (flow->tx.nagle_len >= tcp_nagle_threshold(flow)) tcp_flush_nagle(flow, 1); } if (payload_len) { - if (flow->nagle_len) tcp_flush_nagle(flow, 1); - if (!flow->nagle_len) { + if (flow->tx.nagle_len) tcp_flush_nagle(flow, 1); + if (!flow->tx.nagle_len) { uint64_t n = tcp_emit_data(flow, payload_ptr, payload_len); accepted += n; payload_ptr += n; @@ -458,40 +451,38 @@ tcp_result_t tcp_flow_send(tcp_data *flow_ctx){ } if (want_fin && payload_len == 0) { - flow->fin_tx_pending = 1; + flow->tx.fin_tx_pending = 1; fin_queued = 1; - if (flow->nagle_len) tcp_flush_nagle(flow, 1); + if (flow->tx.nagle_len) tcp_flush_nagle(flow, 1); tcp_try_send_pending_fin(flow); } - flow_ctx->sequence = flow->snd_nxt; - flow->ctx.sequence = flow->snd_nxt; - - tcp_daemon_kick(); + flow_ctx->sequence = flow->tx.snd_nxt; + flow->base.ctx.sequence = flow->tx.snd_nxt; flow_ctx->payload.size = accepted; - if (!accepted && fin_queued && flow->fin_tx_pending) return TCP_WOULDBLOCK; + if (!accepted && fin_queued && flow->tx.fin_tx_pending) return TCP_WOULDBLOCK; if (accepted || fin_queued) return TCP_OK; - if (flow->snd_wnd == 0) tcp_persist_arm(flow); + if (flow->tx.snd_wnd == 0) { + flow->timer.persist_active = 1; + flow->timer.persist_timer_ms = 0; + if (flow->timer.persist_timeout_ms == 0) flow->timer.persist_timeout_ms = TCP_PERSIST_MIN_MS; + if (flow->timer.persist_timeout_ms < TCP_PERSIST_MIN_MS) flow->timer.persist_timeout_ms = TCP_PERSIST_MIN_MS; + if (flow->timer.persist_timeout_ms > TCP_PERSIST_MAX_MS) flow->timer.persist_timeout_ms = TCP_PERSIST_MAX_MS; + tcp_daemon_kick(); + } return TCP_WOULDBLOCK; } tcp_result_t tcp_flow_close(tcp_data *flow_ctx){ if (!flow_ctx) return TCP_INVALID; - tcp_flow_t *flow = NULL; - for (int i = 0; i < MAX_TCP_FLOWS; i++) { - if (!tcp_flows[i]) continue; - if (&tcp_flows[i]->ctx == flow_ctx) { - flow = tcp_flows[i]; - break; - } - } + tcp_flow_t *flow = tcp_flow_from_ctx(flow_ctx); if (!flow) return TCP_INVALID; - if (flow->state == TCP_ESTABLISHED || flow->state == TCP_CLOSE_WAIT) { - flow->fin_tx_pending = 1; - if (flow->nagle_len) tcp_flow_flush(flow_ctx); + if (flow->base.state == TCP_ESTABLISHED || flow->base.state == TCP_CLOSE_WAIT) { + flow->tx.fin_tx_pending = 1; + if (flow->tx.nagle_len) tcp_flow_flush(flow_ctx); tcp_try_send_pending_fin(flow); tcp_daemon_kick(); return TCP_OK; diff --git a/kernel/networking/transport_layer/tcp/tcp_utils.h b/kernel/networking/transport_layer/tcp/tcp_utils.h index b79c51e2..7a07d1ac 100644 --- a/kernel/networking/transport_layer/tcp/tcp_utils.h +++ b/kernel/networking/transport_layer/tcp/tcp_utils.h @@ -30,7 +30,6 @@ typedef struct { void tcp_parse_options(const uint8_t *opts, uint32_t len, tcp_parsed_opts_t *out); uint8_t tcp_build_syn_options(uint8_t *out, uint16_t mss, uint8_t wscale, uint8_t sack_permitted); -port_manager_t *tcp_pm_for_l3(uint8_t l3_id); uint32_t tcp_calc_mss_for_l3(uint8_t l3_id, ip_version_t ver, const void *remote_ip); bool tcp_build_tx_opts_from_local_v4(const void *src_ip_addr, ipv4_tx_opts_t *out); From af9c353a85dfe6688f7a8e4d3f521e956a0ea545 Mon Sep 17 00:00:00 2001 From: CodeAnarchist Date: Wed, 13 May 2026 00:32:56 +0200 Subject: [PATCH 10/39] [net] fix virtio init and netpkt lifetime --- .../application_layer/socket_http_server.hpp | 83 ++++- .../drivers/virtio_net_pci/virtio_net_pci.cpp | 283 ++++++++---------- .../drivers/virtio_net_pci/virtio_net_pci.hpp | 9 +- kernel/networking/netpkt.c | 161 +++++++--- kernel/networking/network_dispatch.cpp | 44 +-- .../networking/transport_layer/tcp/tcp_rx.c | 7 + kernel/virtio/virtio_pci.c | 13 +- 7 files changed, 358 insertions(+), 242 deletions(-) diff --git a/kernel/networking/application_layer/socket_http_server.hpp b/kernel/networking/application_layer/socket_http_server.hpp index c3a9ff11..e69c86e2 100644 --- a/kernel/networking/application_layer/socket_http_server.hpp +++ b/kernel/networking/application_layer/socket_http_server.hpp @@ -204,7 +204,7 @@ class HTTPServer { bad_request = true; break; } - msleep(5); + msleep(2); continue; } if (r <= 0) { @@ -272,15 +272,50 @@ class HTTPServer { int32_t send_response(TCPSocket* client, const HTTPResponseMsg& res) { if (!client) return SOCK_ERR_STATE; + + HTTPResponseMsg head = res; + head.body = sizedptr{}; uint32_t code = (uint32_t)res.status_code; - string out = http_response_builder(&res); - uint32_t out_len = out.length; - uint32_t off = 0; + string out = http_response_builder(&head); + uint32_t body_len = (res.body.ptr && res.body.size) ? (uint32_t)res.body.size : 0; + uint32_t out_len = out.length + body_len; int64_t sent = 0; - while (off < out_len) { - int64_t r = client->send(out.data + off, out_len - off); - if (r == TCP_WOULDBLOCK) { - msleep(5); + uint32_t start_ms = (uint32_t)get_time(); + uint32_t progress_ms = start_ms; + const uint8_t* first_ptr = (const uint8_t*)out.data; + uint32_t first_len = out.length; + uint32_t first_body_len = 0; + uint32_t body_off = 0; + uint8_t* combo = nullptr; + + if (body_len && out.length < 1460) { + first_body_len = body_len; + uint32_t room = 1460 - out.length; + if (first_body_len > room) first_body_len = room; + + if (first_body_len) { + combo = (uint8_t*)zalloc(out.length + first_body_len); + if (combo) { + memcpy(combo, out.data, out.length); + memcpy(combo + out.length, (const void*)res.body.ptr, first_body_len); + + first_ptr = combo; + first_len = out.length + first_body_len; + } else first_body_len = 0; + } + } + + uint32_t off = 0; + while (sent >= 0 && off < first_len) { + int64_t r = client->send(first_ptr + off, first_len - off); + uint32_t now = (uint32_t)get_time(); + + if (r == TCP_WOULDBLOCK || r == 0) { + if ((now - progress_ms) > 3000 || (now - start_ms) > 30000) { + sent = SOCK_ERR_SYS; + break; + } + msleep(2); continue; } if (r < 0) { @@ -288,8 +323,38 @@ class HTTPServer { break; } off += (uint32_t)r; + progress_ms = now; + } + + if (sent >= 0) body_off = first_body_len; + if (sent >= 0 && body_len) { + const uint8_t* body = (const uint8_t*)res.body.ptr; + + while (body_off < body_len) { + uint32_t ask = body_len - body_off; + if (ask > 16384) ask = 16384; + int64_t r = client->send(body + body_off, ask); + uint32_t now = (uint32_t)get_time(); + + if (r == TCP_WOULDBLOCK || r == 0) { + if ((now - progress_ms) > 3000 || (now - start_ms) > 30000) { + sent = SOCK_ERR_SYS; + break; + } + msleep(2); + continue; + } + if (r < 0) { + sent = r; + break; + } + body_off += (uint32_t)r; + progress_ms = now; + } } - if (sent >= 0) sent = (int64_t)off; + + if (sent >= 0) sent = (int64_t)out.length + body_off; + if (combo) release(combo); netlog_socket_event_t ev{}; ev.comp = NETLOG_COMP_HTTP_SERVER; 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 7573964b..be586eca 100644 --- a/kernel/networking/drivers/virtio_net_pci/virtio_net_pci.cpp +++ b/kernel/networking/drivers/virtio_net_pci/virtio_net_pci.cpp @@ -13,9 +13,13 @@ #define CONTROL_QUEUE 2 constexpr uint32_t RX_BUF_SIZE = PAGE_SIZE; -constexpr uint16_t RX_CHAIN_SEGS = 4; +constexpr uint16_t RX_CHAIN_SEGS = 1; +constexpr uint64_t RX_PACKET_POOL_SIZE = 8*1024*1024; +constexpr uint64_t TX_PACKET_POOL_SIZE = 8*1024*1024; void* g_rx_pool = nullptr; +void* g_rx_packet_pool = nullptr; +void* g_tx_packet_pool = nullptr; #define kprintfv(fmt, ...) \ ({ \ @@ -84,7 +88,6 @@ VirtioNetDriver::~VirtioNetDriver(){} bool VirtioNetDriver::init_at(uint64_t addr, uint32_t irq_base_vector) { verbose = false; - mrg_rxbuf = false; ctrl_vq = false; ctrl_rx = false; header_size = sizeof(virtio_net_hdr_t); @@ -135,9 +138,10 @@ bool VirtioNetDriver::init_at(uint64_t addr, uint32_t irq_base_vector) { net_feature_mask |= (1ULL << VIRTIO_NET_F_MAC); net_feature_mask |= (1ULL << VIRTIO_NET_F_STATUS); net_feature_mask |= (1ULL << VIRTIO_NET_F_MTU); - net_feature_mask |= (1ULL << VIRTIO_NET_F_MRG_RXBUF); net_feature_mask |= (1ULL << VIRTIO_NET_F_CTRL_VQ); net_feature_mask |= (1ULL << VIRTIO_NET_F_CTRL_RX); + net_feature_mask |= (1ULL << VIRTIO_NET_F_SPEED_DUPLEX); + //TODO evaluate MRG_RXBUF CSUM TSO GSO GRO USO virtio_set_feature_mask(net_feature_mask); if (!virtio_init_device(&vnp_net_dev)){ @@ -146,8 +150,12 @@ bool VirtioNetDriver::init_at(uint64_t addr, uint32_t irq_base_vector) { } kprintfv("[virtio-net] common_cfg=%x device_cfg=%x", (uintptr_t)vnp_net_dev.common_cfg,(uintptr_t)vnp_net_dev.device_cfg); - mrg_rxbuf = (vnp_net_dev.negotiated_features & (1ULL << VIRTIO_NET_F_MRG_RXBUF)) != 0; - header_size = mrg_rxbuf ? sizeof(virtio_net_hdr_mrg_rxbuf_t) : sizeof(virtio_net_hdr_t); + if (!(vnp_net_dev.negotiated_features & (1ULL << VIRTIO_F_VERSION_1))) { + kprintf("[virtio-net][err] device did not accept VIRTIO_F_VERSION_1"); + vnp_net_dev.common_cfg->device_status |= VIRTIO_STATUS_FAILED; + return false; + } + header_size = sizeof(virtio_net_hdr_t); ctrl_vq = (vnp_net_dev.negotiated_features & (1ULL << VIRTIO_NET_F_CTRL_VQ)) != 0; ctrl_rx = (vnp_net_dev.negotiated_features & (1ULL << VIRTIO_NET_F_CTRL_RX)) != 0; @@ -173,6 +181,18 @@ bool VirtioNetDriver::init_at(uint64_t addr, uint32_t irq_base_vector) { if (!g_rx_pool) return false; } + if (!g_rx_packet_pool) { + g_rx_packet_pool = palloc(RX_PACKET_POOL_SIZE, MEM_PRIV_KERNEL, MEM_RW | MEM_NORM, false); + kprintfv("[virtio-net] rx_packet_pool=%x", (uintptr_t)g_rx_packet_pool); + if (!g_rx_packet_pool) return false; + } + + if (!g_tx_packet_pool) { + g_tx_packet_pool = palloc(TX_PACKET_POOL_SIZE, MEM_PRIV_KERNEL, MEM_DEV | MEM_RW, false); + kprintfv("[virtio-net] tx_packet_pool=%x", (uintptr_t)g_tx_packet_pool); + if (!g_tx_packet_pool) return false; + } + uint16_t chain_count = (uint16_t)(rx_qsz / RX_CHAIN_SEGS); if (!chain_count) return false; @@ -183,21 +203,15 @@ bool VirtioNetDriver::init_at(uint64_t addr, uint32_t irq_base_vector) { rx_used->idx = 0; for (uint16_t c = 0; c < chain_count; c++) { - uint16_t head = (uint16_t)(c * RX_CHAIN_SEGS); + uint16_t di = (uint16_t)(c * RX_CHAIN_SEGS); + void* buf = (void*)((uintptr_t)g_rx_pool + (uintptr_t)di * (uintptr_t)RX_BUF_SIZE); - for (uint16_t s = 0; s < RX_CHAIN_SEGS; s++){ - uint16_t di = (uint16_t)(head + s); - void* buf = (void*)((uintptr_t)g_rx_pool + (uintptr_t)di * (uintptr_t)RX_BUF_SIZE); - - rx_desc[di].addr = VIRT_TO_PHYS((uintptr_t)buf); - rx_desc[di].len = RX_BUF_SIZE; - rx_desc[di].flags = (uint16_t)(VIRTQ_DESC_F_WRITE | ((s + 1 < RX_CHAIN_SEGS) ? VIRTQ_DESC_F_NEXT : 0)); - rx_desc[di].next = (uint16_t)(di + 1); - } + rx_desc[di].addr = VIRT_TO_PHYS((uintptr_t)buf); + rx_desc[di].len = RX_BUF_SIZE; + rx_desc[di].flags = VIRTQ_DESC_F_WRITE; + rx_desc[di].next = 0; - rx_desc[head + (RX_CHAIN_SEGS - 1)].next = 0; - - rx_avail->ring[rx_avail->idx % rx_qsz] = head; + rx_avail->ring[rx_avail->idx % rx_qsz] = di; rx_avail->idx++; } @@ -207,7 +221,6 @@ bool VirtioNetDriver::init_at(uint64_t addr, uint32_t irq_base_vector) { vnp_net_dev.common_cfg->queue_msix_vector = 0; 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; - virtio_notify(&vnp_net_dev); if (TRANSMIT_QUEUE >= vnp_net_dev.num_queues) return false; if (!vnp_net_dev.queues[TRANSMIT_QUEUE].valid) return false; @@ -240,7 +253,6 @@ bool VirtioNetDriver::init_at(uint64_t addr, uint32_t irq_base_vector) { vnp_net_dev.common_cfg->queue_msix_vector = 0xFFFF; } - if (ctrl_vq && ctrl_rx) (void)sync_multicast((const uint8_t*)0, 0); kprintfv("[virtio-net] negotiated ctrl_vq=%u ctrl_rx=%u", (unsigned)ctrl_vq, (unsigned)ctrl_rx); volatile virtio_net_config* cfg = (volatile virtio_net_config*)vnp_net_dev.device_cfg; @@ -249,15 +261,18 @@ bool VirtioNetDriver::init_at(uint64_t addr, uint32_t irq_base_vector) { 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; - + if (vnp_net_dev.negotiated_features & (1ULL << VIRTIO_NET_F_MTU)) { + uint16_t dev_mtu = cfg->mtu; + if (dev_mtu != 0 && dev_mtu != 0xFFFF && dev_mtu >= 576) mtu = dev_mtu; + } - speed_mbps = cfg->speed; - switch (cfg->duplex) { - case 0: duplex = LINK_DUPLEX_HALF; break; - case 1: duplex = LINK_DUPLEX_FULL; break; - default: duplex = LINK_DUPLEX_UNKNOWN; break; + if (vnp_net_dev.negotiated_features & (1ULL << VIRTIO_NET_F_SPEED_DUPLEX)) { + speed_mbps = cfg->speed; + switch (cfg->duplex) { + case 0: duplex = LINK_DUPLEX_HALF; break; + case 1: duplex = LINK_DUPLEX_FULL; break; + default: duplex = LINK_DUPLEX_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; @@ -274,6 +289,11 @@ bool VirtioNetDriver::init_at(uint64_t addr, uint32_t irq_base_vector) { } vnp_net_dev.common_cfg->device_status |= VIRTIO_STATUS_DRIVER_OK; + asm volatile ("dsb sy" ::: "memory"); + + select_queue(&vnp_net_dev, RECEIVE_QUEUE); + virtio_notify(&vnp_net_dev); + if (ctrl_vq && ctrl_rx) (void)sync_multicast((const uint8_t*)0, 0); select_queue(&vnp_net_dev, RECEIVE_QUEUE); return true; } @@ -308,121 +328,69 @@ uint8_t VirtioNetDriver::get_duplex() const { } sizedptr VirtioNetDriver::allocate_packet(size_t size){ - size_t total = size + (size_t)header_size; - return (sizedptr){(uintptr_t)kalloc(vnp_net_dev.memory_page, total, ALIGN_64B, MEM_PRIV_KERNEL), total}; + if (!g_tx_packet_pool) return (sizedptr){0, 0}; + size_t total = size + header_size; + + irq_flags_t iqr = irq_save_disable(); + void* ptr = kalloc(g_tx_packet_pool, total, ALIGN_64B, MEM_PRIV_KERNEL); + irq_restore(iqr); + + return (sizedptr){(uintptr_t)ptr, total}; } sizedptr VirtioNetDriver::handle_receive_packet(){ - uint32_t desc_index = 0; - uint32_t total_len = 0; - uint16_t num_buffers = 1; + if (!rx_qsz || !rx_desc || !rx_used || !rx_avail) return (sizedptr){0,0}; - irq_flags_t irq = irq_save_disable(); select_queue(&vnp_net_dev, RECEIVE_QUEUE); - volatile virtq_used* used = rx_used; - volatile virtq_desc* desc = rx_desc; - volatile virtq_avail* avail = rx_avail; - uint16_t qsz = rx_qsz; - if (!qsz || !used || !desc || !avail) { - irq_restore(irq); - return (sizedptr){0,0}; - } asm volatile ("dmb ishld" ::: "memory"); - uint16_t new_idx = used->idx; - if (new_idx == last_used_receive_idx) { - irq_restore(irq); - return (sizedptr){0,0}; - } + uint16_t new_idx = rx_used->idx; + if (new_idx == last_used_receive_idx) return (sizedptr){0,0}; - uint16_t used_ring_index = (uint16_t)(last_used_receive_idx % qsz); - volatile virtq_used_elem* e = &used->ring[used_ring_index]; + uint16_t used_ring_index = (uint16_t)(last_used_receive_idx % rx_qsz); + volatile virtq_used_elem* e = &rx_used->ring[used_ring_index]; + uint32_t desc_index = e->id; + uint32_t total_len = e->len; last_used_receive_idx++; - desc_index = e->id; - total_len = e->len; - if (desc_index >= qsz || total_len <= (uint32_t)header_size){ - uint16_t aidx = avail->idx; - avail->ring[aidx % qsz] = (uint16_t)(desc_index % qsz); + if (desc_index >= rx_qsz) return (sizedptr){0,0}; + if (total_len <= (uint32_t)header_size || total_len > rx_desc[desc_index].len) { + uint16_t aidx = rx_avail->idx; + rx_avail->ring[aidx % rx_qsz] = (uint16_t)desc_index; asm volatile ("dmb ishst" ::: "memory"); - avail->idx = (uint16_t)(aidx + 1); + rx_avail->idx = (uint16_t)(aidx + 1); asm volatile ("dmb ishst" ::: "memory"); - select_queue(&vnp_net_dev, RECEIVE_QUEUE); virtio_notify(&vnp_net_dev); - irq_restore(irq); return (sizedptr){0,0}; } - volatile uint8_t* first_buf = (volatile uint8_t*)PHYS_TO_VIRT_P((void*)(uintptr_t)desc[desc_index].addr); - if (mrg_rxbuf) { - virtio_net_hdr_mrg_rxbuf_t* h = (virtio_net_hdr_mrg_rxbuf_t*)(uintptr_t)first_buf; - num_buffers = h->num_buffers; - if (num_buffers == 0) num_buffers = 1; - if (num_buffers > RX_CHAIN_SEGS) { - uint16_t aidx = avail->idx; - avail->ring[aidx % qsz] = (uint16_t)desc_index; - asm volatile ("dmb ishst" ::: "memory"); - avail->idx = (uint16_t)(aidx + 1); - asm volatile ("dmb ishst" ::: "memory"); - select_queue(&vnp_net_dev, RECEIVE_QUEUE); - virtio_notify(&vnp_net_dev); - irq_restore(irq); - return (sizedptr){0,0}; - } - } - - irq_restore(irq); - uint32_t payload_len = total_len - (uint32_t)header_size; - void* out_buf = kalloc(vnp_net_dev.memory_page, payload_len, ALIGN_64B, MEM_PRIV_KERNEL); + volatile uint8_t* buf = (volatile uint8_t*)PHYS_TO_VIRT_P((void*)rx_desc[desc_index].addr); + + void* out_buf = nullptr; + if (g_rx_packet_pool) { + irq_flags_t irq = irq_save_disable(); + out_buf = kalloc(g_rx_packet_pool, payload_len, ALIGN_64B, MEM_PRIV_KERNEL); + irq_restore(irq); + } if (!out_buf){ - irq = irq_save_disable(); - uint16_t aidx = avail->idx; - avail->ring[aidx % qsz] = (uint16_t)desc_index; + uint16_t aidx = rx_avail->idx; + rx_avail->ring[aidx % rx_qsz] = (uint16_t)desc_index; asm volatile ("dmb ishst" ::: "memory"); - avail->idx = (uint16_t)(aidx + 1); + rx_avail->idx = (uint16_t)(aidx + 1); asm volatile ("dmb ishst" ::: "memory"); - select_queue(&vnp_net_dev, RECEIVE_QUEUE); virtio_notify(&vnp_net_dev); - irq_restore(irq); return (sizedptr){0,0}; } - uint32_t written = 0; - uint32_t remaining = payload_len; - uint16_t di = (uint16_t)desc_index; - for (uint16_t bi = 0; bi < num_buffers && remaining; bi++) { - volatile uint8_t* buf = (volatile uint8_t*)PHYS_TO_VIRT_P((void*)(uintptr_t)desc[di].addr); - uint32_t cap = desc[di].len; - uint32_t off = (bi == 0) ? (uint32_t)header_size : 0; - if (cap <= off) break; - - uint32_t chunk = cap - off; - if (chunk > remaining) chunk = remaining; - memcpy((uint8_t*)out_buf + written, (const void*)((uintptr_t)buf + off), chunk); - written += chunk; - remaining -= chunk; - - if (bi + 1 < num_buffers) { - if (!(desc[di].flags & VIRTQ_DESC_F_NEXT)) break; - di = desc[di].next; - } - } + memcpy(out_buf, (const void*)((uintptr_t)buf + (uintptr_t)header_size), payload_len); - irq = irq_save_disable(); - uint16_t aidx = avail->idx; - avail->ring[aidx % qsz] = (uint16_t)desc_index; + uint16_t aidx = rx_avail->idx; + rx_avail->ring[aidx % rx_qsz] = (uint16_t)desc_index; asm volatile ("dmb ishst" ::: "memory"); - avail->idx = (uint16_t)(aidx + 1); + rx_avail->idx = (uint16_t)(aidx + 1); asm volatile ("dmb ishst" ::: "memory"); - select_queue(&vnp_net_dev, RECEIVE_QUEUE); virtio_notify(&vnp_net_dev); - irq_restore(irq); - - if (remaining != 0) { - kfree(out_buf, payload_len); - return (sizedptr){0,0}; - } return (sizedptr){ (uintptr_t)out_buf, payload_len }; } @@ -431,7 +399,6 @@ void VirtioNetDriver::handle_sent_packet(){ if (TRANSMIT_QUEUE >= vnp_net_dev.num_queues) return; if (!tx_qsz || !tx_desc || !tx_used || !tx_pending) return; - irq_flags_t irq = irq_save_disable(); select_queue(&vnp_net_dev, TRANSMIT_QUEUE); asm volatile ("dmb ishld" ::: "memory"); @@ -450,15 +417,14 @@ void VirtioNetDriver::handle_sent_packet(){ tx_desc[desc_index].flags = 0; tx_desc[desc_index].next = 0; - irq_restore(irq); - if (packet.ptr) kfree((void*)packet.ptr, packet.size); - irq = irq_save_disable(); - select_queue(&vnp_net_dev, TRANSMIT_QUEUE); + if (packet.ptr) { + irq_flags_t irq = irq_save_disable(); + kfree((void*)packet.ptr, packet.size); + irq_restore(irq); + } asm volatile ("dmb ishld" ::: "memory"); used_idx = tx_used->idx; } - - irq_restore(irq); } bool VirtioNetDriver::send_packet(sizedptr packet){ @@ -468,51 +434,42 @@ bool VirtioNetDriver::send_packet(sizedptr packet){ if (!tx_qsz || !tx_desc || !tx_avail || !tx_used || !tx_pending) return false; memset((void*)packet.ptr, 0, (size_t)header_size); - if (mrg_rxbuf) ((virtio_net_hdr_mrg_rxbuf_t*)packet.ptr)->num_buffers = 0; - for (uint32_t wait = 0; wait < 1000; wait++) { - handle_sent_packet(); + handle_sent_packet(); + select_queue(&vnp_net_dev, TRANSMIT_QUEUE); - irq_flags_t irq = irq_save_disable(); - select_queue(&vnp_net_dev, TRANSMIT_QUEUE); - - bool found = false; - uint16_t desc_index = 0; - for (uint16_t i = 0; i < tx_qsz; i++) { - uint16_t current = (tx_next_desc + i) % tx_qsz; - if (tx_pending[current].ptr) continue; - desc_index = current; - tx_next_desc = (current + 1) % tx_qsz; - found = true; - break; - } + bool found = false; + uint16_t desc_index = 0; + for (uint16_t i = 0; i < tx_qsz; i++) { + uint16_t current = (tx_next_desc + i) % tx_qsz; + if (tx_pending[current].ptr) continue; + desc_index = current; + tx_next_desc = (current + 1) % tx_qsz; + found = true; + break; + } - if (found) { - tx_pending[desc_index] = packet; - tx_desc[desc_index].addr = VIRT_TO_PHYS(packet.ptr); - tx_desc[desc_index].len = (uint32_t)packet.size; - tx_desc[desc_index].flags = 0; - tx_desc[desc_index].next = 0; - - asm volatile ("dmb ishst" ::: "memory"); - uint16_t avail_idx = tx_avail->idx; - tx_avail->ring[avail_idx % tx_qsz] = desc_index; - asm volatile ("dmb ishst" ::: "memory"); - tx_avail->idx = (uint16_t)(avail_idx + 1); - asm volatile ("dmb ishst" ::: "memory"); - virtio_notify(&vnp_net_dev); - irq_restore(irq); + if (!found) { + kprintfv("[virtio-net] tx queue len=%u", packet.size); + return false; + } - kprintfv("[virtio-net] tx queued desc=%u len=%u", desc_index,(unsigned)packet.size); - return true; - } + tx_pending[desc_index] = packet; + tx_desc[desc_index].addr = VIRT_TO_PHYS(packet.ptr); + tx_desc[desc_index].len = (uint32_t)packet.size; + tx_desc[desc_index].flags = 0; + tx_desc[desc_index].next = 0; - irq_restore(irq); - msleep(1); - } + asm volatile ("dmb ishst" ::: "memory"); + uint16_t avail_idx = tx_avail->idx; + tx_avail->ring[avail_idx % tx_qsz] = desc_index; + asm volatile ("dmb ishst" ::: "memory"); + tx_avail->idx = (uint16_t)(avail_idx + 1); + asm volatile ("dmb ishst" ::: "memory"); + virtio_notify(&vnp_net_dev); - kprintfv("[virtio-net] tx queue full len=%u", packet.size); - return false; + kprintfv("[virtio-net] tx queued desc=%u len=%u", desc_index,(unsigned)packet.size); + return true; } bool VirtioNetDriver::sync_multicast(const uint8_t* macs, uint32_t count) { @@ -520,8 +477,6 @@ bool VirtioNetDriver::sync_multicast(const uint8_t* macs, uint32_t count) { if (!ctrl_rx) return true; if (!macs && count) return false; - irq_flags_t irq = irq_save_disable(); - bool ok = true; uint8_t v0 = 0; @@ -536,7 +491,6 @@ bool VirtioNetDriver::sync_multicast(const uint8_t* macs, uint32_t count) { uint32_t payload_len = 8u + count * 6u; uint8_t* payload = (uint8_t*)kalloc(vnp_net_dev.memory_page, payload_len, ALIGN_16B, MEM_PRIV_KERNEL); if (!payload) { - irq_restore(irq); return false; } kprintfv("[virtio-net] sync_multicast ctrl_vq=%u ctrl_rx=%u count=%u",(unsigned)ctrl_vq, (unsigned)ctrl_rx, (unsigned)count); @@ -549,7 +503,6 @@ bool VirtioNetDriver::sync_multicast(const uint8_t* macs, uint32_t count) { ok = ok && virtio_net_ctrl_send(&vnp_net_dev, VIRTIO_NET_CTRL_MAC, VIRTIO_NET_CTRL_MAC_TABLE_SET, payload, payload_len); kfree(payload, payload_len); - irq_restore(irq); return ok; } 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 a828fba4..09a78031 100644 --- a/kernel/networking/drivers/virtio_net_pci/virtio_net_pci.hpp +++ b/kernel/networking/drivers/virtio_net_pci/virtio_net_pci.hpp @@ -4,7 +4,6 @@ #include "virtio/virtio_pci.h" #include "std/memory.h" #include "networking/link_layer/nic_types.h" -#define VIRTIO_F_VERSION_1 32 #define VIRTIO_NET_F_CSUM 0 #define VIRTIO_NET_F_GUEST_CSUM 1 @@ -24,6 +23,7 @@ #define VIRTIO_NET_F_STATUS 16 #define VIRTIO_NET_F_CTRL_VQ 17 #define VIRTIO_NET_F_CTRL_RX 18 +#define VIRTIO_NET_F_SPEED_DUPLEX 63 typedef struct __attribute__((packed)) virtio_net_hdr_t { uint8_t flags; @@ -32,12 +32,8 @@ typedef struct __attribute__((packed)) virtio_net_hdr_t { uint16_t gso_size; uint16_t csum_start; uint16_t csum_offset; -} virtio_net_hdr_t; - -typedef struct __attribute__((packed)) virtio_net_hdr_mrg_rxbuf_t { - virtio_net_hdr_t hdr; uint16_t num_buffers; -} virtio_net_hdr_mrg_rxbuf_t; +} virtio_net_hdr_t; typedef struct __attribute__((packed)) virtio_net_config { uint8_t mac[6]; @@ -92,7 +88,6 @@ class VirtioNetDriver : public NetDriver { size_t tx_pending_size = 0; bool verbose = false; - bool mrg_rxbuf = false; bool ctrl_vq = false; bool ctrl_rx = false; diff --git a/kernel/networking/netpkt.c b/kernel/networking/netpkt.c index 4aba9168..baf55163 100644 --- a/kernel/networking/netpkt.c +++ b/kernel/networking/netpkt.c @@ -1,6 +1,7 @@ #include "netpkt.h" #include "std/std.h" #include "memory/page_allocator.h" +#include "exceptions/irq.h" #define NETPKT_F_VIEW 1u #define NETPKT_BUF_F_EXTERNAL 1u @@ -346,13 +347,20 @@ static bool netpkt_realloc_to(netpkt_t* p, uint32_t new_head, uint32_t new_alloc } netpkt_t* netpkt_alloc(uint32_t data_capacity, uint32_t headroom, uint32_t tailroom) { + irq_flags_t irq = irq_save_disable(); uint64_t alloc = (uint64_t)headroom + (uint64_t)data_capacity + (uint64_t)tailroom; - if (alloc > (uint64_t)NETPKT_MAX_ALLOC) return 0; + if (alloc > (uint64_t)NETPKT_MAX_ALLOC) { + irq_restore(irq); + return 0; + } if (!alloc) alloc = 1; uint64_t cap64 = count_pages(alloc, PAGE_SIZE) * (uint64_t)PAGE_SIZE; if (!cap64) cap64 = PAGE_SIZE; - if (cap64 > (uint64_t)NETPKT_MAX_ALLOC) return 0; + if (cap64 > (uint64_t)NETPKT_MAX_ALLOC) { + irq_restore(irq); + return 0; + } uint32_t cap = (uint32_t)cap64; @@ -364,8 +372,10 @@ netpkt_t* netpkt_alloc(uint32_t data_capacity, uint32_t headroom, uint32_t tailr from_spare = true; memset(mem, 0, PAGE_SIZE); } else { - if ((uint64_t)cap > (uint64_t)NETPKT_MAX_PAGE_BYTES) return 0; - if (g_netpkt_page_bytes > (uint64_t)NETPKT_MAX_PAGE_BYTES - (uint64_t)cap) return 0; + if ((uint64_t)cap > (uint64_t)NETPKT_MAX_PAGE_BYTES || g_netpkt_page_bytes > (uint64_t)NETPKT_MAX_PAGE_BYTES - (uint64_t)cap) { + irq_restore(irq); + return 0; + } g_netpkt_page_bytes += (uint64_t)cap; g_netpkt_payload_page_bytes += (uint64_t)cap; @@ -375,6 +385,7 @@ netpkt_t* netpkt_alloc(uint32_t data_capacity, uint32_t headroom, uint32_t tailr else g_netpkt_page_bytes = 0; if (g_netpkt_payload_page_bytes >= (uint64_t)cap) g_netpkt_payload_page_bytes -= (uint64_t)cap; else g_netpkt_payload_page_bytes = 0; + irq_restore(irq); return 0; } } @@ -391,6 +402,7 @@ netpkt_t* netpkt_alloc(uint32_t data_capacity, uint32_t headroom, uint32_t tailr if (g_netpkt_payload_page_bytes >= (uint64_t)cap) g_netpkt_payload_page_bytes -= (uint64_t)cap; else g_netpkt_payload_page_bytes = 0; } + irq_restore(irq); return 0; } @@ -414,6 +426,7 @@ netpkt_t* netpkt_alloc(uint32_t data_capacity, uint32_t headroom, uint32_t tailr if (g_netpkt_payload_page_bytes >= (uint64_t)cap) g_netpkt_payload_page_bytes -= (uint64_t)cap; else g_netpkt_payload_page_bytes = 0; } + irq_restore(irq); return 0; } @@ -424,19 +437,23 @@ netpkt_t* netpkt_alloc(uint32_t data_capacity, uint32_t headroom, uint32_t tailr p->len = 0; p->refs = 1; p->flags = 0; + irq_restore(irq); return p; } netpkt_t* netpkt_wrap(uintptr_t base, uint32_t alloc_size, uint32_t data_off, uint32_t data_len, netpkt_free_fn free_fn, void* ctx) { - if (!base) return 0; - if (!alloc_size) return 0; - if (alloc_size > NETPKT_MAX_ALLOC) return 0; - if (data_off > alloc_size) return 0; - if (data_len > alloc_size - data_off) return 0; + irq_flags_t irq = irq_save_disable(); + if (!base || !alloc_size || alloc_size > NETPKT_MAX_ALLOC || data_off > alloc_size || data_len > alloc_size - data_off) { + irq_restore(irq); + return 0; + } uint32_t bsz = (uint32_t)(((uint64_t)sizeof(netpkt_buf_t) + 15ull) &~15ull); netpkt_buf_t* b = (netpkt_buf_t*)meta_slab_alloc(&g_meta_slab_buf, bsz); - if (!b) return 0; + if (!b) { + irq_restore(irq); + return 0; + } b->base = base; b->alloc = alloc_size; @@ -449,6 +466,7 @@ netpkt_t* netpkt_wrap(uintptr_t base, uint32_t alloc_size, uint32_t data_off, ui netpkt_t* p = (netpkt_t*)meta_slab_alloc(&g_meta_slab_pkt, psz); if (!p) { meta_slab_free(&g_meta_slab_buf, b); + irq_restore(irq); return 0; } @@ -459,22 +477,35 @@ netpkt_t* netpkt_wrap(uintptr_t base, uint32_t alloc_size, uint32_t data_off, ui p->len = data_len; p->refs = 1; p->flags = 0; + irq_restore(irq); return p; } netpkt_t* netpkt_view(netpkt_t* parent, uint32_t off, uint32_t len) { - if (!parent) return 0; - if (!parent->buf) return 0; + irq_flags_t irq = irq_save_disable(); + if (!parent || !parent->buf) { + irq_restore(irq); + return 0; + } uint64_t end = (uint64_t)off+(uint64_t)len; - if (end > (uint64_t)parent->len) return 0; + if (end > (uint64_t)parent->len) { + irq_restore(irq); + return 0; + } uint64_t abs = (uint64_t)parent->off + (uint64_t)parent->head + (uint64_t)off; - if (abs + (uint64_t)len > (uint64_t)parent->buf->alloc) return 0; + if (abs + (uint64_t)len > (uint64_t)parent->buf->alloc) { + irq_restore(irq); + return 0; + } uint32_t psz = (uint32_t)(((uint64_t)sizeof(netpkt_t) + 15ull) &~15ull); netpkt_t* v = (netpkt_t*)meta_slab_alloc(&g_meta_slab_pkt, psz); - if (!v) return 0; + if (!v) { + irq_restore(irq); + return 0; + } parent->buf->refs++; @@ -485,18 +516,23 @@ netpkt_t* netpkt_view(netpkt_t* parent, uint32_t off, uint32_t len) { v->len = len; v->refs = 1; v->flags = NETPKT_F_VIEW; + irq_restore(irq); return v; } void netpkt_ref(netpkt_t* p){ if (!p) return; + irq_flags_t irq = irq_save_disable(); p->refs++; + irq_restore(irq); } void netpkt_unref(netpkt_t* p) { if (!p) return; + irq_flags_t irq = irq_save_disable(); if (p->refs > 1) { p->refs--; + irq_restore(irq); return; } @@ -534,6 +570,7 @@ void netpkt_unref(netpkt_t* p) { } meta_slab_free(&g_meta_slab_pkt, p); + irq_restore(irq); } uintptr_t netpkt_data(const netpkt_t* p) { @@ -579,13 +616,20 @@ uint32_t netpkt_tailroom(const netpkt_t* p) { bool netpkt_ensure_headroom(netpkt_t* p, uint32_t need) { if (!p) return false; - if (!p->buf) return false; - if (p->flags & NETPKT_F_VIEW) return false; - if (need > NETPKT_MAX_ALLOC) return false; + irq_flags_t irq = irq_save_disable(); + if (!p->buf || (p->flags & NETPKT_F_VIEW) || need > NETPKT_MAX_ALLOC) { + irq_restore(irq); + return false; + } if (p->head >= need) { - if (p->buf->refs == 1) return true; - return netpkt_realloc_to(p, p->head, p->cap); + if (p->buf->refs == 1) { + irq_restore(irq); + return true; + } + bool ok = netpkt_realloc_to(p, p->head, p->cap); + irq_restore(irq); + return ok; } uint32_t tail = netpkt_tailroom(p); @@ -594,29 +638,46 @@ bool netpkt_ensure_headroom(netpkt_t* p, uint32_t need) { uint64_t alloc = (uint64_t)new_head + (uint64_t)p->len + (uint64_t)tail; uint64_t min = (uint64_t)p->cap + (uint64_t)(need - p->head); if (alloc < min) alloc = min; - if (alloc > (uint64_t)NETPKT_MAX_ALLOC) return false; + if (alloc > (uint64_t)NETPKT_MAX_ALLOC) { + irq_restore(irq); + return false; + } - return netpkt_realloc_to(p, new_head, (uint32_t)alloc); + bool ok = netpkt_realloc_to(p, new_head, (uint32_t)alloc); + irq_restore(irq); + return ok; } bool netpkt_ensure_tailroom(netpkt_t* p, uint32_t need) { if (!p) return false; - if (!p->buf) return false; - if (p->flags & NETPKT_F_VIEW) return false; - if (need > NETPKT_MAX_ALLOC) return false; + irq_flags_t irq = irq_save_disable(); + if (!p->buf || (p->flags & NETPKT_F_VIEW) || need > NETPKT_MAX_ALLOC) { + irq_restore(irq); + return false; + } uint32_t tail = netpkt_tailroom(p); if (tail >= need) { - if (p->buf->refs == 1) return true; - return netpkt_realloc_to(p, p->head, p->cap); + if (p->buf->refs == 1) { + irq_restore(irq); + return true; + } + bool ok = netpkt_realloc_to(p, p->head, p->cap); + irq_restore(irq); + return ok; } uint64_t alloc = (uint64_t)p->head + (uint64_t)p->len + (uint64_t)need; uint64_t min = (uint64_t)p->cap + (uint64_t)(need - tail); if (alloc < min) alloc = min; - if (alloc > (uint64_t)NETPKT_MAX_ALLOC) return false; + if (alloc > (uint64_t)NETPKT_MAX_ALLOC) { + irq_restore(irq); + return false; + } - return netpkt_realloc_to(p, p->head, (uint32_t)alloc); + bool ok = netpkt_realloc_to(p, p->head, (uint32_t)alloc); + irq_restore(irq); + return ok; } void* netpkt_push(netpkt_t* p, uint32_t bytes) { @@ -644,21 +705,27 @@ void* netpkt_put(netpkt_t* p, uint32_t bytes) { bool netpkt_pull(netpkt_t* p, uint32_t bytes) { if (!p) return false; - if (bytes > p->len) return false; + irq_flags_t irq = irq_save_disable(); + if (bytes > p->len) { + irq_restore(irq); + return false; + } p->head += bytes; p->len -= bytes; - if (p->flags & NETPKT_F_VIEW) return true; - if (!p->buf) return true; - if (p->buf->flags & NETPKT_BUF_F_EXTERNAL) return true; - if (p->buf->refs != 1) return true; - if (p->cap <= PAGE_SIZE) return true; + if ((p->flags & NETPKT_F_VIEW) || !p->buf || (p->buf->flags & NETPKT_BUF_F_EXTERNAL) || (p->buf->refs != 1) || (p->cap <= PAGE_SIZE)) { + irq_restore(irq); + return true; + } uint64_t need = (uint64_t)p->head+(uint64_t)p->len; if (!need) need = 1; uint64_t newcap64 = count_pages(need, PAGE_SIZE) * (uint64_t)PAGE_SIZE; if (newcap64 < PAGE_SIZE) newcap64 = PAGE_SIZE; - if (newcap64 >= (uint64_t)p->cap) return true; + if (newcap64 >= (uint64_t)p->cap) { + irq_restore(irq); + return true; + } uint32_t newcap = (uint32_t)newcap64; uint64_t dec = (uint64_t)p->buf->alloc - (uint64_t)newcap; @@ -672,25 +739,32 @@ bool netpkt_pull(netpkt_t* p, uint32_t bytes) { p->cap = newcap; } + irq_restore(irq); return true; } bool netpkt_trim(netpkt_t* p, uint32_t new_len) { if (!p) return false; - if (new_len > p->len) return false; + irq_flags_t irq = irq_save_disable(); + if (new_len > p->len) { + irq_restore(irq); + return false; + } p->len = new_len; - if (p->flags & NETPKT_F_VIEW) return true; - if (!p->buf) return true; - if (p->buf->flags & NETPKT_BUF_F_EXTERNAL) return true; - if (p->buf->refs != 1) return true; - if (p->cap <= PAGE_SIZE) return true; + if ((p->flags & NETPKT_F_VIEW) || !p->buf || (p->buf->flags & NETPKT_BUF_F_EXTERNAL) || (p->buf->refs != 1) || (p->cap <= PAGE_SIZE)) { + irq_restore(irq); + return true; + } uint64_t need = (uint64_t)p->head + (uint64_t)p->len; if (!need) need = 1; uint64_t newcap64 = count_pages(need, PAGE_SIZE) * (uint64_t)PAGE_SIZE; if (newcap64 < PAGE_SIZE) newcap64 = PAGE_SIZE; - if (newcap64 >= (uint64_t)p->cap) return true; + if (newcap64 >= (uint64_t)p->cap) { + irq_restore(irq); + return true; + } uint32_t newcap = (uint32_t)newcap64; uint64_t dec = (uint64_t)p->buf->alloc - (uint64_t)newcap; @@ -704,5 +778,6 @@ bool netpkt_trim(netpkt_t* p, uint32_t new_len) { p->cap = newcap; } + irq_restore(irq); return true; } diff --git a/kernel/networking/network_dispatch.cpp b/kernel/networking/network_dispatch.cpp index adc3c24f..25a4aa1a 100644 --- a/kernel/networking/network_dispatch.cpp +++ b/kernel/networking/network_dispatch.cpp @@ -14,15 +14,19 @@ #include "networking/internet_layer/ipv6_utils.h" #include "networking/netpkt.h" #include "networking/link_layer/link_utils.h" +#include "networking/transport_layer/tcp.h" #include "networking/drivers/loopback/loopback_driver.hpp" #include "exceptions/irq.h" -#define RX_INTR_BATCH_LIMIT 64 +#define RX_INTR_BATCH_LIMIT 256 #define TASK_RX_BATCH_LIMIT 256 #define TASK_TX_BATCH_LIMIT 256 static void netpkt_free_rx_frame(void* ctx, uintptr_t base, uint32_t alloc_size) { - if (base) kfree((void*)base, alloc_size); + if (!base) return; + irq_flags_t flags = irq_save_disable(); + kfree((void*)base, alloc_size); + irq_restore(flags); } NetworkDispatch::NetworkDispatch() @@ -97,14 +101,14 @@ bool NetworkDispatch::enqueue_frame(uint8_t ifindex, const sizedptr& frame) void* dst = (void*)(pkt.ptr + hs); memcpy(dst, (const void*)frame.ptr, frame.size); - irq_flags_t irq = irq_save_disable(); + irq_flags_t flags = irq_save_disable(); int pushed = nics[nic_id].tx.push(pkt); if (pushed) nics[nic_id].tx_produced++; - irq_restore(irq); + else nics[nic_id].tx_dropped++; + irq_restore(flags); if (!pushed) { free_frame(pkt); - nics[nic_id].tx_dropped++; return false; } return true; @@ -145,7 +149,7 @@ int NetworkDispatch::net_task() if (np) { eth_input(nics[n].ifindex, np); netpkt_unref(np); - } else kfree((void*)pkt.ptr, pkt.size); + } else free_frame(pkt); nics[n].rx_consumed++; processed++; } @@ -157,17 +161,20 @@ int NetworkDispatch::net_task() if (!driver) continue; int processed = 0; for (int i = 0; i < TASK_TX_BATCH_LIMIT; ++i) { - sizedptr pkt{0,0}; - irq_flags_t irq = irq_save_disable(); - int popped = nics[n].tx.pop(pkt); - if (popped) nics[n].tx_consumed++; - irq_restore(irq); - - if (!popped) break; - if (!driver->send_packet(pkt)) { - free_frame(pkt); - nics[n].tx_dropped++; + irq_flags_t flags = irq_save_disable(); + if (nics[n].tx.is_empty()) { + irq_restore(flags); + break; } + sizedptr pkt = nics[n].tx.peek(); + irq_restore(flags); + + if (!driver->send_packet(pkt)) break; + + flags = irq_save_disable(); + sizedptr popped{0,0}; + if (nics[n].tx.pop(popped)) nics[n].tx_consumed++; + irq_restore(flags); processed++; } if (processed) did_work = true; @@ -253,7 +260,10 @@ uint8_t NetworkDispatch::kind(uint8_t ifindex) const void NetworkDispatch::free_frame(const sizedptr &f) { - if (f.ptr) kfree((void*)f.ptr, f.size); + if (!f.ptr) return; + irq_flags_t flags = irq_save_disable(); + kfree((void*)f.ptr, f.size); + irq_restore(flags); } bool NetworkDispatch::register_all_from_bus() { diff --git a/kernel/networking/transport_layer/tcp/tcp_rx.c b/kernel/networking/transport_layer/tcp/tcp_rx.c index 7453a620..2880598e 100644 --- a/kernel/networking/transport_layer/tcp/tcp_rx.c +++ b/kernel/networking/transport_layer/tcp/tcp_rx.c @@ -818,8 +818,15 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a flow->base.ctx.ack_received = ack; tcp_daemon_kick(); + if (!data_len && !fin) { + netpkt_unref(pkt); + return; + } + break; } else if (flags & (1u << RST_F)){ tcp_free_flow(idx); + netpkt_unref(pkt); + return; } netpkt_unref(pkt); diff --git a/kernel/virtio/virtio_pci.c b/kernel/virtio/virtio_pci.c index 76c1259d..ce651661 100644 --- a/kernel/virtio/virtio_pci.c +++ b/kernel/virtio/virtio_pci.c @@ -109,15 +109,19 @@ bool virtio_init_device(virtio_device *dev) { dev->num_queues = 0; dev->current_queue = 0; dev->negotiated_features = 0; + cfg->device_status = VIRTIO_STATUS_RESET; + asm volatile ("dsb sy" ::: "memory"); uint32_t timeout = 2000; - while (cfg->device_status != 0) { + while (cfg->device_status != VIRTIO_STATUS_RESET) { if (timeout == 0) return false; timeout--; delay(1); } cfg->device_status = VIRTIO_STATUS_ACKNOWLEDGE; + asm volatile ("dsb sy" ::: "memory"); cfg->device_status |= VIRTIO_STATUS_DRIVER; + asm volatile ("dsb sy" ::: "memory"); cfg->device_feature_select = 0; uint32_t f_lo = cfg->device_feature; @@ -128,6 +132,11 @@ bool virtio_init_device(virtio_device *dev) { kprintfv("Features %llx",(unsigned long long)features); uint64_t negotiated = (features & feature_mask); + if ((feature_mask & (1ULL << VIRTIO_F_VERSION_1)) && !(negotiated & (1ULL << VIRTIO_F_VERSION_1))) { + kprintf("[VIRTIO] VIRTIO_F_VERSION_1 not supported"); + cfg->device_status |= VIRTIO_STATUS_FAILED; + return false; + } kprintfv("Negotiated features %llx",(unsigned long long)negotiated); @@ -139,8 +148,10 @@ bool virtio_init_device(virtio_device *dev) { dev->negotiated_features = negotiated; cfg->device_status |= VIRTIO_STATUS_FEATURES_OK; + asm volatile ("dsb sy" ::: "memory"); if (!(cfg->device_status & VIRTIO_STATUS_FEATURES_OK)){ kprintf("Failed to negotiate features. Supported features %llx",(unsigned long long)features); + cfg->device_status |= VIRTIO_STATUS_FAILED; return false; } From 0339f243449b39188d42d417c1ea69f65522c1d1 Mon Sep 17 00:00:00 2001 From: CodeAnarchist Date: Fri, 15 May 2026 02:09:41 +0200 Subject: [PATCH 11/39] [net] make packet path full netpkt --- .../drivers/loopback/loopback_driver.cpp | 28 +-- .../drivers/loopback/loopback_driver.hpp | 9 +- kernel/networking/drivers/net_driver.hpp | 9 +- .../drivers/virtio_net_pci/virtio_net_pci.cpp | 184 ++++++++---------- .../drivers/virtio_net_pci/virtio_net_pci.hpp | 17 +- kernel/networking/link_layer/eth.c | 25 ++- kernel/networking/netpkt.c | 112 +++++++++-- kernel/networking/network.cpp | 16 +- kernel/networking/network.h | 5 +- kernel/networking/network_dispatch.cpp | 119 +++-------- kernel/networking/network_dispatch.hpp | 18 +- kernel/virtio/virtio_pci.c | 8 +- kernel/virtio/virtio_pci.h | 1 + 13 files changed, 249 insertions(+), 302 deletions(-) diff --git a/kernel/networking/drivers/loopback/loopback_driver.cpp b/kernel/networking/drivers/loopback/loopback_driver.cpp index ee46f8bc..072a93bd 100644 --- a/kernel/networking/drivers/loopback/loopback_driver.cpp +++ b/kernel/networking/drivers/loopback/loopback_driver.cpp @@ -1,12 +1,10 @@ #include "loopback_driver.hpp" #include "std/memory.h" -#include "memory/page_allocator.h" LoopbackDriver::LoopbackDriver(){ - memory_page = 0; rx_head = 0; rx_tail = 0; - verbose = false; + memset(rxq, 0, sizeof(rxq)); hw_name[0]='l'; hw_name[1]='o'; hw_name[2]='o'; hw_name[3]='p'; hw_name[4]='b'; hw_name[5]='a'; hw_name[6]='c'; hw_name[7]='k'; hw_name[8]=0; } @@ -15,31 +13,21 @@ LoopbackDriver::~LoopbackDriver(){} bool LoopbackDriver::init_at(uint64_t pci_addr, uint32_t irq_base_vector){ (void)pci_addr; (void)irq_base_vector; - if (!memory_page) { - memory_page = palloc(PAGE_SIZE, MEM_PRIV_KERNEL, MEM_RW, true); - if (!memory_page) return false; - } return true; } -sizedptr LoopbackDriver::allocate_packet(size_t size){ - if (!size) return (sizedptr){0,0}; - if (!memory_page && !init_at(0, 0)) return (sizedptr){0,0}; - void* p = kalloc(memory_page, size, ALIGN_16B, MEM_PRIV_KERNEL); - return (sizedptr){(uintptr_t)p, (uint32_t)size}; -} - -sizedptr LoopbackDriver::handle_receive_packet(){ - if (rx_head == rx_tail) return (sizedptr){0,0}; - sizedptr p = rxq[rx_head]; +netpkt_t* LoopbackDriver::handle_receive_packet(){ + if (rx_head == rx_tail) return nullptr; + netpkt_t* p = rxq[rx_head]; + rxq[rx_head] = nullptr; rx_head = (uint16_t)((rx_head + 1) & 255); return p; } -void LoopbackDriver::enable_verbose(){ verbose = true; } +void LoopbackDriver::enable_verbose(){} -bool LoopbackDriver::send_packet(sizedptr packet){ - if (!packet.ptr || !packet.size) return false; +bool LoopbackDriver::send_packet(netpkt_t* packet){ + if (!packet || !netpkt_len(packet)) return false; uint16_t next = (uint16_t)((rx_tail + 1) & 255); if (next == rx_head)return false; rxq[rx_tail] = packet; diff --git a/kernel/networking/drivers/loopback/loopback_driver.hpp b/kernel/networking/drivers/loopback/loopback_driver.hpp index 1d50490b..00cd8f00 100644 --- a/kernel/networking/drivers/loopback/loopback_driver.hpp +++ b/kernel/networking/drivers/loopback/loopback_driver.hpp @@ -9,10 +9,9 @@ class LoopbackDriver : public NetDriver { ~LoopbackDriver() override; bool init_at(uint64_t pci_addr, uint32_t irq_base_vector) override; - sizedptr allocate_packet(size_t size) override; - sizedptr handle_receive_packet() override; + netpkt_t* handle_receive_packet() override; void enable_verbose() override; - bool send_packet(sizedptr packet) override; + bool send_packet(netpkt_t* packet) override; void get_mac(uint8_t out_mac[6]) const override; uint16_t get_mtu() const override; uint16_t get_header_size() const override; @@ -21,10 +20,8 @@ class LoopbackDriver : public NetDriver { uint8_t get_duplex() const override; private: - void* memory_page; - sizedptr rxq[256]; + netpkt_t* rxq[256]; uint16_t rx_head; uint16_t rx_tail; - bool verbose; char hw_name[16]; }; \ No newline at end of file diff --git a/kernel/networking/drivers/net_driver.hpp b/kernel/networking/drivers/net_driver.hpp index a13dbe4b..d8baa8d5 100644 --- a/kernel/networking/drivers/net_driver.hpp +++ b/kernel/networking/drivers/net_driver.hpp @@ -1,17 +1,20 @@ #pragma once #include "types.h" #include "net/network_types.h" +#include "networking/netpkt.h" class NetDriver { public: NetDriver() = default; 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() = 0; + virtual netpkt_t* handle_receive_packet() = 0; virtual void handle_sent_packet() {} + //TODO this looks like a violation of the single responsibility principle + virtual void flush_rx(){} + virtual void flush_tx(){} virtual void enable_verbose() = 0; - virtual bool send_packet(sizedptr packet) = 0; + virtual bool send_packet(netpkt_t* packet) = 0; 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; 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 be586eca..9800d2e0 100644 --- a/kernel/networking/drivers/virtio_net_pci/virtio_net_pci.cpp +++ b/kernel/networking/drivers/virtio_net_pci/virtio_net_pci.cpp @@ -12,14 +12,7 @@ #define TRANSMIT_QUEUE 1 #define CONTROL_QUEUE 2 -constexpr uint32_t RX_BUF_SIZE = PAGE_SIZE; -constexpr uint16_t RX_CHAIN_SEGS = 1; -constexpr uint64_t RX_PACKET_POOL_SIZE = 8*1024*1024; -constexpr uint64_t TX_PACKET_POOL_SIZE = 8*1024*1024; - -void* g_rx_pool = nullptr; -void* g_rx_packet_pool = nullptr; -void* g_tx_packet_pool = nullptr; +constexpr uint32_t RX_BUF_SIZE = 2048; #define kprintfv(fmt, ...) \ ({ \ @@ -46,6 +39,26 @@ typedef struct __attribute__((packed)) virtio_net_ctrl_ack_t { #define VIRTIO_NET_CTRL_MAC_TABLE_SET 0 +void virtio_net_rx_free(void* ctx, uintptr_t base, uint32_t alloc_size) { + VirtioNetDriver* driver = (VirtioNetDriver*)ctx; + if (!driver || !driver->rx_qsz || !driver->rx_avail || !driver->rx_pool) return; + if (base < (uintptr_t)driver->rx_pool) return; + + uintptr_t off = base - (uintptr_t)driver->rx_pool; + if (off % RX_BUF_SIZE) return; + + uint32_t desc_index = (uint32_t)(off/RX_BUF_SIZE); + if (desc_index >= driver->rx_qsz) return; + + irq_flags_t flags = irq_save_disable(); + uint16_t aidx = driver->rx_avail->idx; + driver->rx_avail->ring[aidx % driver->rx_qsz] = (uint16_t)desc_index; + asm volatile ("dmb ishst" ::: "memory"); + driver->rx_avail->idx = (uint16_t)(aidx + 1); + asm volatile ("dmb ishst" ::: "memory"); + driver->rx_notify_pending = true; + irq_restore(flags); +} bool virtio_net_ctrl_send(virtio_device* dev, uint8_t cls, uint8_t cmd, const void* payload, uint32_t payload_len) { if (!dev) return false; @@ -100,13 +113,15 @@ bool VirtioNetDriver::init_at(uint64_t addr, uint32_t irq_base_vector) { rx_avail = nullptr; rx_used = nullptr; rx_qsz = 0; + rx_pool = nullptr; + rx_notify_pending = false; tx_desc = nullptr; tx_avail = nullptr; tx_used = nullptr; tx_qsz = 0; tx_next_desc = 0; tx_pending = nullptr; - tx_pending_size = 0; + tx_notify_pending = false; memset(&vnp_net_dev, 0, sizeof(vnp_net_dev)); kprintfv("[virtio-net] probing pci_addr=%x",(uintptr_t)addr); @@ -175,26 +190,9 @@ bool VirtioNetDriver::init_at(uint64_t addr, uint32_t irq_base_vector) { if (!rx_qsz || !rx_desc || !rx_avail || !rx_used) return false; 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; - } - - if (!g_rx_packet_pool) { - g_rx_packet_pool = palloc(RX_PACKET_POOL_SIZE, MEM_PRIV_KERNEL, MEM_RW | MEM_NORM, false); - kprintfv("[virtio-net] rx_packet_pool=%x", (uintptr_t)g_rx_packet_pool); - if (!g_rx_packet_pool) return false; - } - - if (!g_tx_packet_pool) { - g_tx_packet_pool = palloc(TX_PACKET_POOL_SIZE, MEM_PRIV_KERNEL, MEM_DEV | MEM_RW, false); - kprintfv("[virtio-net] tx_packet_pool=%x", (uintptr_t)g_tx_packet_pool); - if (!g_tx_packet_pool) return false; - } - - uint16_t chain_count = (uint16_t)(rx_qsz / RX_CHAIN_SEGS); - if (!chain_count) return false; + rx_pool = palloc((uint64_t)rx_qsz * RX_BUF_SIZE, MEM_PRIV_KERNEL, MEM_RW | MEM_NORM, true); + kprintfv("[virtio-net] rx_pool=%x", (uintptr_t)rx_pool); + if (!rx_pool) return false; memset((void*)rx_desc, 0, 16ULL * rx_qsz); rx_avail->flags = 0; @@ -202,10 +200,8 @@ bool VirtioNetDriver::init_at(uint64_t addr, uint32_t irq_base_vector) { rx_used->flags = 0; rx_used->idx = 0; - for (uint16_t c = 0; c < chain_count; c++) { - uint16_t di = (uint16_t)(c * RX_CHAIN_SEGS); - void* buf = (void*)((uintptr_t)g_rx_pool + (uintptr_t)di * (uintptr_t)RX_BUF_SIZE); - + for (uint16_t di = 0; di < rx_qsz; di++) { + void* buf = (void*)((uintptr_t)rx_pool + (uintptr_t)di * (uintptr_t)RX_BUF_SIZE); rx_desc[di].addr = VIRT_TO_PHYS((uintptr_t)buf); rx_desc[di].len = RX_BUF_SIZE; rx_desc[di].flags = VIRTQ_DESC_F_WRITE; @@ -231,8 +227,7 @@ bool VirtioNetDriver::init_at(uint64_t addr, uint32_t irq_base_vector) { tx_used = vnp_net_dev.queues[TRANSMIT_QUEUE].device; if (!tx_qsz || !tx_desc || !tx_avail || !tx_used) return false; - tx_pending_size = (size_t)tx_qsz * sizeof(sizedptr); - tx_pending = (sizedptr*)kalloc(vnp_net_dev.memory_page, tx_pending_size, ALIGN_64B, MEM_PRIV_KERNEL); + tx_pending = (netpkt_t**)palloc((uint64_t)tx_qsz * sizeof(netpkt_t*), MEM_PRIV_KERNEL, MEM_RW | MEM_NORM, true); if (!tx_pending) return false; memset((void*)tx_desc, 0, 16 * tx_qsz); @@ -266,6 +261,9 @@ bool VirtioNetDriver::init_at(uint64_t addr, uint32_t irq_base_vector) { if (dev_mtu != 0 && dev_mtu != 0xFFFF && dev_mtu >= 576) mtu = dev_mtu; } + uint16_t rx_mtu_cap = (uint16_t)(RX_BUF_SIZE - header_size - 14); + if (mtu > rx_mtu_cap) mtu = rx_mtu_cap; + if (vnp_net_dev.negotiated_features & (1ULL << VIRTIO_NET_F_SPEED_DUPLEX)) { speed_mbps = cfg->speed; switch (cfg->duplex) { @@ -291,10 +289,8 @@ bool VirtioNetDriver::init_at(uint64_t addr, uint32_t irq_base_vector) { vnp_net_dev.common_cfg->device_status |= VIRTIO_STATUS_DRIVER_OK; asm volatile ("dsb sy" ::: "memory"); - select_queue(&vnp_net_dev, RECEIVE_QUEUE); - virtio_notify(&vnp_net_dev); + virtio_notify_queue(&vnp_net_dev, RECEIVE_QUEUE); if (ctrl_vq && ctrl_rx) (void)sync_multicast((const uint8_t*)0, 0); - select_queue(&vnp_net_dev, RECEIVE_QUEUE); return true; } @@ -327,25 +323,13 @@ uint8_t VirtioNetDriver::get_duplex() const { } } -sizedptr VirtioNetDriver::allocate_packet(size_t size){ - if (!g_tx_packet_pool) return (sizedptr){0, 0}; - size_t total = size + header_size; - - irq_flags_t iqr = irq_save_disable(); - void* ptr = kalloc(g_tx_packet_pool, total, ALIGN_64B, MEM_PRIV_KERNEL); - irq_restore(iqr); +netpkt_t* VirtioNetDriver::handle_receive_packet(){ + if (!rx_qsz || !rx_desc || !rx_used || !rx_avail || !rx_pool) return nullptr; - return (sizedptr){(uintptr_t)ptr, total}; -} - -sizedptr VirtioNetDriver::handle_receive_packet(){ - if (!rx_qsz || !rx_desc || !rx_used || !rx_avail) return (sizedptr){0,0}; - - select_queue(&vnp_net_dev, RECEIVE_QUEUE); asm volatile ("dmb ishld" ::: "memory"); uint16_t new_idx = rx_used->idx; - if (new_idx == last_used_receive_idx) return (sizedptr){0,0}; + if (new_idx == last_used_receive_idx) return nullptr; uint16_t used_ring_index = (uint16_t)(last_used_receive_idx % rx_qsz); volatile virtq_used_elem* e = &rx_used->ring[used_ring_index]; @@ -353,53 +337,35 @@ sizedptr VirtioNetDriver::handle_receive_packet(){ uint32_t total_len = e->len; last_used_receive_idx++; - if (desc_index >= rx_qsz) return (sizedptr){0,0}; + if (desc_index >= rx_qsz) return nullptr; + uintptr_t buf = (uintptr_t)PHYS_TO_VIRT_P((void*)rx_desc[desc_index].addr); if (total_len <= (uint32_t)header_size || total_len > rx_desc[desc_index].len) { - uint16_t aidx = rx_avail->idx; - rx_avail->ring[aidx % rx_qsz] = (uint16_t)desc_index; - asm volatile ("dmb ishst" ::: "memory"); - rx_avail->idx = (uint16_t)(aidx + 1); - asm volatile ("dmb ishst" ::: "memory"); - virtio_notify(&vnp_net_dev); - return (sizedptr){0,0}; + virtio_net_rx_free(this, buf, RX_BUF_SIZE); + return nullptr; } uint32_t payload_len = total_len - (uint32_t)header_size; - volatile uint8_t* buf = (volatile uint8_t*)PHYS_TO_VIRT_P((void*)rx_desc[desc_index].addr); - - void* out_buf = nullptr; - if (g_rx_packet_pool) { - irq_flags_t irq = irq_save_disable(); - out_buf = kalloc(g_rx_packet_pool, payload_len, ALIGN_64B, MEM_PRIV_KERNEL); - irq_restore(irq); + netpkt_t* pkt = netpkt_wrap(buf, RX_BUF_SIZE, header_size, payload_len, virtio_net_rx_free, this); + if (!pkt) { + virtio_net_rx_free(this, buf, RX_BUF_SIZE); + return nullptr; } - if (!out_buf){ - uint16_t aidx = rx_avail->idx; - rx_avail->ring[aidx % rx_qsz] = (uint16_t)desc_index; - asm volatile ("dmb ishst" ::: "memory"); - rx_avail->idx = (uint16_t)(aidx + 1); - asm volatile ("dmb ishst" ::: "memory"); - virtio_notify(&vnp_net_dev); - return (sizedptr){0,0}; - } - - memcpy(out_buf, (const void*)((uintptr_t)buf + (uintptr_t)header_size), payload_len); - uint16_t aidx = rx_avail->idx; - rx_avail->ring[aidx % rx_qsz] = (uint16_t)desc_index; - asm volatile ("dmb ishst" ::: "memory"); - rx_avail->idx = (uint16_t)(aidx + 1); - asm volatile ("dmb ishst" ::: "memory"); - virtio_notify(&vnp_net_dev); + return pkt; +} - return (sizedptr){ (uintptr_t)out_buf, payload_len }; +void VirtioNetDriver::flush_rx() { + irq_flags_t flags = irq_save_disable(); + bool pending = rx_notify_pending; + rx_notify_pending = false; + irq_restore(flags); + if (pending) virtio_notify_queue(&vnp_net_dev, RECEIVE_QUEUE); } void VirtioNetDriver::handle_sent_packet(){ if (TRANSMIT_QUEUE >= vnp_net_dev.num_queues) return; if (!tx_qsz || !tx_desc || !tx_used || !tx_pending) return; - select_queue(&vnp_net_dev, TRANSMIT_QUEUE); asm volatile ("dmb ishld" ::: "memory"); uint16_t used_idx = tx_used->idx; @@ -410,39 +376,30 @@ void VirtioNetDriver::handle_sent_packet(){ if (desc_index >= tx_qsz) continue; - sizedptr packet = tx_pending[desc_index]; - tx_pending[desc_index] = (sizedptr){0,0}; + netpkt_t* packet = tx_pending[desc_index]; + tx_pending[desc_index] = nullptr; tx_desc[desc_index].addr = 0; tx_desc[desc_index].len = 0; tx_desc[desc_index].flags = 0; tx_desc[desc_index].next = 0; - if (packet.ptr) { - irq_flags_t irq = irq_save_disable(); - kfree((void*)packet.ptr, packet.size); - irq_restore(irq); - } + if (packet) netpkt_unref(packet); asm volatile ("dmb ishld" ::: "memory"); used_idx = tx_used->idx; } } -bool VirtioNetDriver::send_packet(sizedptr packet){ - if (!packet.ptr || !packet.size) return false; - if ((size_t)header_size > packet.size) return false; - if (packet.size > 0xFFFFFFFF) return false; +bool VirtioNetDriver::send_packet(netpkt_t* packet){ + if (!packet || !netpkt_len(packet)) return false; if (!tx_qsz || !tx_desc || !tx_avail || !tx_used || !tx_pending) return false; - memset((void*)packet.ptr, 0, (size_t)header_size); - handle_sent_packet(); - select_queue(&vnp_net_dev, TRANSMIT_QUEUE); bool found = false; uint16_t desc_index = 0; for (uint16_t i = 0; i < tx_qsz; i++) { uint16_t current = (tx_next_desc + i) % tx_qsz; - if (tx_pending[current].ptr) continue; + if (tx_pending[current]) continue; desc_index = current; tx_next_desc = (current + 1) % tx_qsz; found = true; @@ -450,13 +407,18 @@ bool VirtioNetDriver::send_packet(sizedptr packet){ } if (!found) { - kprintfv("[virtio-net] tx queue len=%u", packet.size); + kprintfv("[virtio-net] tx queue full len=%u", (unsigned)netpkt_len(packet)); return false; } + if (!netpkt_ensure_headroom(packet, header_size)) return false; + void* hdr = netpkt_push(packet, header_size); + if (!hdr) return false; + memset(hdr, 0, (size_t)header_size); + tx_pending[desc_index] = packet; - tx_desc[desc_index].addr = VIRT_TO_PHYS(packet.ptr); - tx_desc[desc_index].len = (uint32_t)packet.size; + tx_desc[desc_index].addr = VIRT_TO_PHYS(netpkt_data(packet)); + tx_desc[desc_index].len = netpkt_len(packet); tx_desc[desc_index].flags = 0; tx_desc[desc_index].next = 0; @@ -466,12 +428,20 @@ bool VirtioNetDriver::send_packet(sizedptr packet){ asm volatile ("dmb ishst" ::: "memory"); tx_avail->idx = (uint16_t)(avail_idx + 1); asm volatile ("dmb ishst" ::: "memory"); - virtio_notify(&vnp_net_dev); + tx_notify_pending = true; - kprintfv("[virtio-net] tx queued desc=%u len=%u", desc_index,(unsigned)packet.size); + kprintfv("[virtio-net] tx queued desc=%u len=%u", desc_index,(unsigned)netpkt_len(packet)); return true; } +void VirtioNetDriver::flush_tx() { + irq_flags_t flags = irq_save_disable(); + bool pending = tx_notify_pending; + tx_notify_pending = false; + irq_restore(flags); + if (pending) virtio_notify_queue(&vnp_net_dev, TRANSMIT_QUEUE); +} + bool VirtioNetDriver::sync_multicast(const uint8_t* macs, uint32_t count) { if (!ctrl_vq) return true; if (!ctrl_rx) return true; 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 09a78031..b563cdd0 100644 --- a/kernel/networking/drivers/virtio_net_pci/virtio_net_pci.hpp +++ b/kernel/networking/drivers/virtio_net_pci/virtio_net_pci.hpp @@ -63,29 +63,30 @@ class VirtioNetDriver : public NetDriver { uint8_t get_duplex() const override; bool sync_multicast(const uint8_t* macs, uint32_t count) override; - sizedptr allocate_packet(size_t size) override; - sizedptr handle_receive_packet() override; + netpkt_t* handle_receive_packet() override; void handle_sent_packet() override; - bool send_packet(sizedptr packet) override; + void flush_rx() override; + void flush_tx() override; + bool send_packet(netpkt_t* packet) override; private: + friend void virtio_net_rx_free(void* ctx, uintptr_t base, uint32_t alloc_size); virtio_device vnp_net_dev = {}; volatile virtq_desc* rx_desc = nullptr; volatile virtq_avail* rx_avail = nullptr; volatile virtq_used* rx_used = nullptr; uint16_t rx_qsz = 0; + void* rx_pool = nullptr; + bool rx_notify_pending = false; volatile virtq_desc* tx_desc = nullptr; volatile virtq_avail* tx_avail = nullptr; volatile virtq_used* tx_used = nullptr; uint16_t tx_qsz = 0; uint16_t tx_next_desc = 0; - //TODO move tx own from sizedptr to netpkt. make it async - //[kfree] untracked ptr=0xffff800044342580 phys=0x44342580 size=0x42 off=0x580 - - sizedptr* tx_pending = nullptr; - size_t tx_pending_size = 0; + netpkt_t** tx_pending = nullptr; + bool tx_notify_pending = false; bool verbose = false; diff --git a/kernel/networking/link_layer/eth.c b/kernel/networking/link_layer/eth.c index 9d1bfbdb..d31c4e80 100644 --- a/kernel/networking/link_layer/eth.c +++ b/kernel/networking/link_layer/eth.c @@ -39,6 +39,12 @@ bool eth_send_frame_on(uint16_t ifindex, uint16_t ethertype, const uint8_t dst_m return false; } + uint32_t driver_headroom = network_get_header_size(ifindex); + if (!netpkt_ensure_headroom(pkt, (uint32_t)sizeof(eth_hdr_t) + driver_headroom)) { + netpkt_unref(pkt); + return false; + } + void* hdrp = netpkt_push(pkt, (uint32_t)sizeof(eth_hdr_t)); if (!hdrp) { netpkt_unref(pkt); @@ -47,8 +53,8 @@ bool eth_send_frame_on(uint16_t ifindex, uint16_t ethertype, const uint8_t dst_m (void)create_eth_packet((uintptr_t)hdrp, src_mac, dst_mac, ethertype); - bool ok = (net_tx_frame_on(ifindex, netpkt_data(pkt), netpkt_len(pkt)) == 0); - netpkt_unref(pkt); + bool ok = (net_tx_packet_on(ifindex, pkt) == 0); + if (!ok) netpkt_unref(pkt); return ok; } @@ -58,32 +64,25 @@ void eth_input(uint16_t ifindex, netpkt_t* pkt) { if (netpkt_len(pkt) < sizeof(eth_hdr_t)) return; eth_hdr_t eth; if (!netpkt_copyout(pkt, 0, ð, sizeof(eth))) return; + if (!netpkt_pull(pkt, (uint32_t)sizeof(eth_hdr_t))) return; uint16_t type = bswap16(eth.ethertype); - netpkt_t* payload = netpkt_view(pkt, (uint32_t)sizeof(eth_hdr_t), netpkt_len(pkt) - (uint32_t)sizeof(eth_hdr_t)); - if (!payload) return; switch (type) { case ETHERTYPE_ARP: - arp_input(ifindex, eth.src_mac, payload); - netpkt_unref(payload); + arp_input(ifindex, eth.src_mac, pkt); break; case ETHERTYPE_IPV4: - ipv4_input(ifindex, payload, eth.src_mac); - netpkt_unref(payload); + ipv4_input(ifindex, pkt, eth.src_mac); break; case ETHERTYPE_IPV6: - ipv6_input(ifindex, payload, eth.src_mac); - netpkt_unref(payload); + ipv6_input(ifindex, pkt, eth.src_mac); break; case ETHERTYPE_VLAN1Q: - netpkt_unref(payload); break; case ETHERTYPE_VLAN1AD: - netpkt_unref(payload); break; default: - netpkt_unref(payload); break; } } diff --git a/kernel/networking/netpkt.c b/kernel/networking/netpkt.c index baf55163..40d2815a 100644 --- a/kernel/networking/netpkt.c +++ b/kernel/networking/netpkt.c @@ -5,7 +5,9 @@ #define NETPKT_F_VIEW 1u #define NETPKT_BUF_F_EXTERNAL 1u +#define NETPKT_BUF_F_SMALL 2u #define NETPKT_META_KEEP_EMPTY_PAGES 2u +#define NETPKT_SMALL_CLASS_BYTES 2048u typedef struct netpkt_buf netpkt_buf_t; @@ -61,6 +63,7 @@ static meta_slab_t g_meta_slab_pkt; static meta_slab_t g_meta_slab_buf; static uintptr_t g_spare_page; +static void* g_netpkt_small_free; static void* meta_slab_alloc(meta_slab_t* s, uint32_t obj_size_aligned) { if (!s) return 0; @@ -249,16 +252,37 @@ static bool netpkt_realloc_to(netpkt_t* p, uint32_t new_head, uint32_t new_alloc uint64_t bytes = (uint64_t)new_alloc; if (!bytes) bytes = 1; - uint64_t cap64 = count_pages(bytes, PAGE_SIZE)*(uint64_t)PAGE_SIZE; - if (!cap64) cap64 = PAGE_SIZE; - if (cap64 > (uint64_t)NETPKT_MAX_ALLOC) return false; - uint32_t cap = (uint32_t)cap64; + bool small = bytes <= (uint64_t)NETPKT_SMALL_CLASS_BYTES; + uint32_t cap = small ? NETPKT_SMALL_CLASS_BYTES : 0; + if (!small) { + uint64_t cap64 = count_pages(bytes, PAGE_SIZE)*(uint64_t)PAGE_SIZE; + if (!cap64) cap64 = PAGE_SIZE; + if (cap64 > (uint64_t)NETPKT_MAX_ALLOC) return false; + + cap = (uint32_t)cap64; + } if ((uint64_t)new_head+(uint64_t)p->len > (uint64_t)cap) return false; void* mem = 0; bool from_spare = false; - if (cap == PAGE_SIZE && g_spare_page) { + uint32_t flags = 0; + if (small) { + if (!g_netpkt_small_free) { + if (g_netpkt_page_bytes > (uint64_t)NETPKT_MAX_PAGE_BYTES - (uint64_t)PAGE_SIZE) return false; + void* page = palloc(PAGE_SIZE, MEM_PRIV_KERNEL, MEM_RW | MEM_NORM, true); + if (!page) return false; + g_netpkt_page_bytes += (uint64_t)PAGE_SIZE; + g_netpkt_payload_page_bytes += (uint64_t)PAGE_SIZE; + *(void**)page = (void*)((uintptr_t)page + (uintptr_t)NETPKT_SMALL_CLASS_BYTES); + *(void**)((uintptr_t)page + (uintptr_t)NETPKT_SMALL_CLASS_BYTES) = 0; + g_netpkt_small_free = page; + } + mem = g_netpkt_small_free; + g_netpkt_small_free = *(void**)mem; + memset(mem, 0, NETPKT_SMALL_CLASS_BYTES); + flags = NETPKT_BUF_F_SMALL; + } else if (cap == PAGE_SIZE && g_spare_page) { mem = (void*)g_spare_page; g_spare_page = 0; from_spare = true; @@ -283,7 +307,10 @@ static bool netpkt_realloc_to(netpkt_t* p, uint32_t new_head, uint32_t new_alloc uint32_t sz = (uint32_t)(((uint64_t)sizeof(netpkt_buf_t) + 15) & ~15ull); netpkt_buf_t* nb = (netpkt_buf_t*)meta_slab_alloc(&g_meta_slab_buf, sz); if (!nb) { - if (from_spare) { + if (flags & NETPKT_BUF_F_SMALL) { + *(void**)mem = g_netpkt_small_free; + g_netpkt_small_free = mem; + } else if (from_spare) { g_spare_page = (uintptr_t)mem; } else { pfree(mem, (uint64_t)cap); @@ -298,7 +325,7 @@ static bool netpkt_realloc_to(netpkt_t* p, uint32_t new_head, uint32_t new_alloc nb->base = (uintptr_t)mem; nb->alloc = cap; nb->refs = 1; - nb->flags = 0; + nb->flags = flags; nb->free_fn = 0; nb->free_ctx = 0; @@ -316,6 +343,9 @@ static bool netpkt_realloc_to(netpkt_t* p, uint32_t new_head, uint32_t new_alloc } else { if (ob->flags & NETPKT_BUF_F_EXTERNAL) { if (ob->free_fn) ob->free_fn(ob->free_ctx, ob->base, ob->alloc); + } else if (ob->flags & NETPKT_BUF_F_SMALL) { + *(void**)ob->base = g_netpkt_small_free; + g_netpkt_small_free = (void*)ob->base; } else { bool aligned = ((ob->base & (PAGE_SIZE - 1)) == 0) && ((ob->alloc & (PAGE_SIZE - 1)) == 0); if (!g_spare_page && aligned) { @@ -355,18 +385,44 @@ netpkt_t* netpkt_alloc(uint32_t data_capacity, uint32_t headroom, uint32_t tailr } if (!alloc) alloc = 1; - uint64_t cap64 = count_pages(alloc, PAGE_SIZE) * (uint64_t)PAGE_SIZE; - if (!cap64) cap64 = PAGE_SIZE; - if (cap64 > (uint64_t)NETPKT_MAX_ALLOC) { - irq_restore(irq); - return 0; - } + bool small = alloc <= (uint64_t)NETPKT_SMALL_CLASS_BYTES; + uint32_t cap = small ? NETPKT_SMALL_CLASS_BYTES : 0; + if (!small) { + uint64_t cap64 = count_pages(alloc, PAGE_SIZE) * (uint64_t)PAGE_SIZE; + if (!cap64) cap64 = PAGE_SIZE; + if (cap64 > (uint64_t)NETPKT_MAX_ALLOC) { + irq_restore(irq); + return 0; + } - uint32_t cap = (uint32_t)cap64; + cap = (uint32_t)cap64; + } void* mem = 0; bool from_spare = false; - if (cap == PAGE_SIZE && g_spare_page) { + uint32_t flags = 0; + if (small) { + if (!g_netpkt_small_free) { + if (g_netpkt_page_bytes > (uint64_t)NETPKT_MAX_PAGE_BYTES - (uint64_t)PAGE_SIZE) { + irq_restore(irq); + return 0; + } + void* page = palloc(PAGE_SIZE, MEM_PRIV_KERNEL, MEM_RW | MEM_NORM, true); + if (!page) { + irq_restore(irq); + return 0; + } + g_netpkt_page_bytes += (uint64_t)PAGE_SIZE; + g_netpkt_payload_page_bytes += (uint64_t)PAGE_SIZE; + *(void**)page = (void*)((uintptr_t)page + (uintptr_t)NETPKT_SMALL_CLASS_BYTES); + *(void**)((uintptr_t)page + (uintptr_t)NETPKT_SMALL_CLASS_BYTES) = 0; + g_netpkt_small_free = page; + } + mem = g_netpkt_small_free; + g_netpkt_small_free = *(void**)mem; + memset(mem, 0, NETPKT_SMALL_CLASS_BYTES); + flags = NETPKT_BUF_F_SMALL; + } else if (cap == PAGE_SIZE && g_spare_page) { mem = (void*)g_spare_page; g_spare_page = 0; from_spare = true; @@ -390,10 +446,13 @@ netpkt_t* netpkt_alloc(uint32_t data_capacity, uint32_t headroom, uint32_t tailr } } - uint32_t bsz = (uint32_t)(((uint64_t)sizeof(netpkt_buf_t) + 15ull) &~15ull); + uint32_t bsz = (uint32_t)(((uint64_t)sizeof(netpkt_buf_t) + 15ull) & ~15ull); netpkt_buf_t* b = (netpkt_buf_t*)meta_slab_alloc(&g_meta_slab_buf, bsz); if (!b) { - if (from_spare) { + if (flags & NETPKT_BUF_F_SMALL) { + *(void**)mem = g_netpkt_small_free; + g_netpkt_small_free = mem; + } else if (from_spare) { g_spare_page = (uintptr_t)mem; } else { pfree(mem, (uint64_t)cap); @@ -409,7 +468,7 @@ netpkt_t* netpkt_alloc(uint32_t data_capacity, uint32_t headroom, uint32_t tailr b->base = (uintptr_t)mem; b->alloc = cap; b->refs = 1; - b->flags = 0; + b->flags = flags; b->free_fn = 0; b->free_ctx = 0; @@ -417,7 +476,10 @@ netpkt_t* netpkt_alloc(uint32_t data_capacity, uint32_t headroom, uint32_t tailr netpkt_t* p = (netpkt_t*)meta_slab_alloc(&g_meta_slab_pkt, psz); if (!p) { meta_slab_free(&g_meta_slab_buf, b); - if (from_spare) { + if (flags & NETPKT_BUF_F_SMALL) { + *(void**)mem = g_netpkt_small_free; + g_netpkt_small_free = mem; + } else if (from_spare) { g_spare_page = (uintptr_t)mem; } else { pfree(mem, (uint64_t)cap); @@ -543,6 +605,9 @@ void netpkt_unref(netpkt_t* p) { } else { if (b->flags & NETPKT_BUF_F_EXTERNAL) { if (b->free_fn) b->free_fn(b->free_ctx, b->base, b->alloc); + } else if (b->flags & NETPKT_BUF_F_SMALL) { + *(void**)b->base = g_netpkt_small_free; + g_netpkt_small_free = (void*)b->base; } else { bool aligned = ((b->base & (PAGE_SIZE - 1)) == 0) && ((b->alloc & (PAGE_SIZE - 1)) == 0); if (!g_spare_page && aligned) { @@ -633,6 +698,15 @@ bool netpkt_ensure_headroom(netpkt_t* p, uint32_t need) { } uint32_t tail = netpkt_tailroom(p); + uint32_t add = need - p->head; + if (p->buf->refs == 1 && !(p->buf->flags & NETPKT_BUF_F_EXTERNAL) && tail >= add) { + uintptr_t old_data = netpkt_data(p); + if (p->len) memmove((void*)(old_data + (uintptr_t)add), (const void*)old_data, p->len); + p->head += add; + irq_restore(irq); + return true; + } + uint32_t new_head = need; uint64_t alloc = (uint64_t)new_head + (uint64_t)p->len + (uint64_t)tail; diff --git a/kernel/networking/network.cpp b/kernel/networking/network.cpp index d9146174..0b0d02b6 100644 --- a/kernel/networking/network.cpp +++ b/kernel/networking/network.cpp @@ -1,6 +1,5 @@ #include "network.h" #include "network_dispatch.hpp" -#include "process/scheduler.h" static NetworkDispatch *dispatch = 0; @@ -11,11 +10,9 @@ bool network_init() { } 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_nic(uint16_t nic_id) { - if (dispatch) dispatch->handle_tx_irq((size_t)nic_id); } int network_net_task_entry(int argc, char* argv[]) { @@ -23,16 +20,9 @@ int network_net_task_entry(int argc, char* argv[]) { return 0; } -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; +int net_tx_packet_on(uint16_t ifindex, netpkt_t* pkt) { + if (!dispatch || !pkt || !netpkt_len(pkt)) return -1; + return dispatch->enqueue_packet((uint8_t)ifindex, pkt) ? 0 : -1; } const uint8_t* network_get_mac(uint16_t ifindex) { diff --git a/kernel/networking/network.h b/kernel/networking/network.h index 913ca034..9fd366d3 100644 --- a/kernel/networking/network.h +++ b/kernel/networking/network.h @@ -7,6 +7,7 @@ extern "C" { #include "types.h" #include "net/network_types.h" #include "dev/driver_base.h" +#include "networking/netpkt.h" #define NET_IRQ_BASE 40 //TODO: consider using the system MTU here @@ -20,9 +21,7 @@ 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); +int net_tx_packet_on(uint16_t ifindex, netpkt_t* pkt); const uint8_t* network_get_local_mac(void); const uint8_t* network_get_mac(uint16_t ifindex); diff --git a/kernel/networking/network_dispatch.cpp b/kernel/networking/network_dispatch.cpp index 25a4aa1a..150b9803 100644 --- a/kernel/networking/network_dispatch.cpp +++ b/kernel/networking/network_dispatch.cpp @@ -1,10 +1,8 @@ #include "network_dispatch.hpp" #include "drivers/virtio_net_pci/virtio_net_pci.hpp" #include "drivers/net_bus.hpp" -#include "memory/page_allocator.h" #include "networking/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" @@ -14,7 +12,6 @@ #include "networking/internet_layer/ipv6_utils.h" #include "networking/netpkt.h" #include "networking/link_layer/link_utils.h" -#include "networking/transport_layer/tcp.h" #include "networking/drivers/loopback/loopback_driver.hpp" #include "exceptions/irq.h" @@ -22,13 +19,6 @@ #define TASK_RX_BATCH_LIMIT 256 #define TASK_TX_BATCH_LIMIT 256 -static void netpkt_free_rx_frame(void* ctx, uintptr_t base, uint32_t alloc_size) { - if (!base) return; - irq_flags_t flags = irq_save_disable(); - kfree((void*)base, alloc_size); - irq_restore(flags); -} - NetworkDispatch::NetworkDispatch() { nic_num = 0; @@ -44,12 +34,6 @@ NetworkDispatch::NetworkDispatch() nics[i].speed_mbps = 0xFFFFFFFFu; nics[i].duplex_mode = 0xFFu; nics[i].kind_val = 0xFFu; - nics[i].rx_produced = 0; - nics[i].rx_consumed = 0; - nics[i].tx_produced = 0; - nics[i].tx_consumed = 0; - nics[i].rx_dropped = 0; - nics[i].tx_dropped = 0; } } @@ -74,44 +58,18 @@ bool NetworkDispatch::init() return nic_num > 0; } -void NetworkDispatch::handle_rx_irq(size_t nic_id) -{ - if (nic_id >= nic_num) return; - if (!nics[nic_id].drv) return; -} - -void NetworkDispatch::handle_tx_irq(size_t nic_id) -{ - if (nic_id >= nic_num) return; - //TODO wake net_task from tx irq when the network loop becomes event driven -} - -bool NetworkDispatch::enqueue_frame(uint8_t ifindex, const sizedptr& frame) +bool NetworkDispatch::enqueue_packet(uint8_t ifindex, netpkt_t* pkt) { 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; - - uint16_t hs = nics[nic_id].hdr_sz; - void* dst = (void*)(pkt.ptr + hs); - memcpy(dst, (const void*)frame.ptr, frame.size); + if (!pkt || !netpkt_len(pkt)) return false; + if (!nics[nic_id].drv) return false; irq_flags_t flags = irq_save_disable(); int pushed = nics[nic_id].tx.push(pkt); - if (pushed) nics[nic_id].tx_produced++; - else nics[nic_id].tx_dropped++; irq_restore(flags); - if (!pushed) { - free_frame(pkt); - return false; - } - return true; + return pushed != 0; } int NetworkDispatch::net_task() @@ -122,37 +80,25 @@ int NetworkDispatch::net_task() for (size_t n = 0; n < nic_num; ++n) { NetDriver* driver = nics[n].drv; - if (driver) { - driver->handle_sent_packet(); - int lim = nics[n].kind_val == NET_IFK_LOCALHOST ? TASK_RX_BATCH_LIMIT : RX_INTR_BATCH_LIMIT; - for (int i = 0; i < lim; ++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[n].rx.push(raw)) { - free_frame(raw); - nics[n].rx_dropped++; - continue; - } - nics[n].rx_produced++; - } - } + if (!driver) continue; + + driver->handle_sent_packet(); int processed = 0; - 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.pop(pkt)) break; - netpkt_t* np = netpkt_wrap(pkt.ptr, pkt.size, 0 , pkt.size, netpkt_free_rx_frame, 0); - if (np) { - eth_input(nics[n].ifindex, np); - netpkt_unref(np); - } else free_frame(pkt); - nics[n].rx_consumed++; + int lim = nics[n].kind_val == NET_IFK_LOCALHOST ? TASK_RX_BATCH_LIMIT : RX_INTR_BATCH_LIMIT; + for (int i = 0; i < lim; ++i) { + netpkt_t* pkt = driver->handle_receive_packet(); + if (!pkt) break; + if (!netpkt_len(pkt)) { + netpkt_unref(pkt); + break; + } + + if (netpkt_len(pkt) >= sizeof(eth_hdr_t)) eth_input(nics[n].ifindex, pkt); + netpkt_unref(pkt); processed++; } + + driver->flush_rx(); if (processed) did_work = true; } @@ -166,18 +112,21 @@ int NetworkDispatch::net_task() irq_restore(flags); break; } - sizedptr pkt = nics[n].tx.peek(); + netpkt_t* pkt = nics[n].tx.peek(); irq_restore(flags); if (!driver->send_packet(pkt)) break; flags = irq_save_disable(); - sizedptr popped{0,0}; - if (nics[n].tx.pop(popped)) nics[n].tx_consumed++; + netpkt_t* popped = nullptr; + nics[n].tx.pop(popped); irq_restore(flags); processed++; } - if (processed) did_work = true; + if (processed) { + driver->flush_tx(); + did_work = true; + } } if (!did_work) msleep(1);//TODO: manage it with an event @@ -252,20 +201,6 @@ uint8_t NetworkDispatch::duplex(uint8_t ifindex) const 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) -{ - if (!f.ptr) return; - irq_flags_t flags = irq_save_disable(); - kfree((void*)f.ptr, f.size); - irq_restore(flags); -} - bool NetworkDispatch::register_all_from_bus() { int n = net_bus_count(); if (n <= 0) return false; diff --git a/kernel/networking/network_dispatch.hpp b/kernel/networking/network_dispatch.hpp index bb3bda67..450d921a 100644 --- a/kernel/networking/network_dispatch.hpp +++ b/kernel/networking/network_dispatch.hpp @@ -12,10 +12,7 @@ class NetworkDispatch { NetworkDispatch(); bool init(); - void handle_rx_irq(size_t nic_id); - void handle_tx_irq(size_t nic_id); - - bool enqueue_frame(uint8_t ifindex, const sizedptr&); + bool enqueue_packet(uint8_t ifindex, netpkt_t* pkt); int net_task(); void set_net_pid(uint16_t pid); @@ -33,7 +30,6 @@ class NetworkDispatch { uint32_t speed(uint8_t ifindex) const; uint8_t duplex(uint8_t ifindex) const; - uint8_t kind(uint8_t ifindex) const; void dump_interfaces(); @@ -49,14 +45,7 @@ class NetworkDispatch { uint32_t speed_mbps; uint8_t duplex_mode; uint8_t kind_val; - RingBuffer tx; - RingBuffer rx; - uint64_t rx_produced; - uint64_t rx_consumed; - uint64_t tx_produced; - uint64_t tx_consumed; - uint64_t rx_dropped; - uint64_t tx_dropped; + RingBuffer tx; }; static const size_t MAX_NIC = 16; @@ -67,9 +56,6 @@ class NetworkDispatch { uint8_t ifindex_to_nicid[MAX_L2_INTERFACES + 1]; - 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/virtio/virtio_pci.c b/kernel/virtio/virtio_pci.c index ce651661..b5508934 100644 --- a/kernel/virtio/virtio_pci.c +++ b/kernel/virtio/virtio_pci.c @@ -234,9 +234,8 @@ uint32_t select_queue(virtio_device *dev, uint32_t index){ return dev->queues[index].size; } -void virtio_notify(virtio_device *dev) { +void virtio_notify_queue(virtio_device *dev, uint16_t index) { if (!dev || !dev->notify_cfg) return; - uint16_t index = dev->current_queue; if (index >= VIRTIO_MAX_QUEUES) return; if (!dev->queues[index].valid) return; @@ -249,6 +248,11 @@ void virtio_notify(virtio_device *dev) { *(volatile uint16_t*)((uintptr_t)dev->notify_cfg + (uint64_t)off * (uint64_t)mul) = value; } +void virtio_notify(virtio_device *dev) { + if (!dev) return; + virtio_notify_queue(dev, dev->current_queue); +} + bool virtio_send_nd(virtio_device *dev, const virtio_buf *bufs, uint16_t n) { if (!dev || !bufs || !n) return false; diff --git a/kernel/virtio/virtio_pci.h b/kernel/virtio/virtio_pci.h index 2744b651..c52a9488 100644 --- a/kernel/virtio/virtio_pci.h +++ b/kernel/virtio/virtio_pci.h @@ -107,6 +107,7 @@ typedef struct { #define VBUF(a,l,f) ((virtio_buf){.addr = (uint64_t)(a), .len = (uint32_t)(l), .flags = (uint16_t)(f)}) void virtio_notify(virtio_device *dev); +void virtio_notify_queue(virtio_device *dev, uint16_t index); void virtio_set_feature_mask(uint64_t mask); void virtio_enable_verbose(); void virtio_get_capabilities(virtio_device *dev, uint64_t pci_addr, uint64_t *mmio_start, uint64_t *mmio_size); From 5e046e9f0600aad86b57576aa0dd7c2ff6b8a826 Mon Sep 17 00:00:00 2001 From: CodeAnarchist Date: Fri, 22 May 2026 00:15:24 +0200 Subject: [PATCH 12/39] [tcp] add bounded OOO/SYN/TX limits and seq wrap handling --- .../networking/transport_layer/tcp/tcp_core.c | 132 +++++++++++++++- .../transport_layer/tcp/tcp_internal.h | 65 ++++++++ .../networking/transport_layer/tcp/tcp_rx.c | 147 +++++++++--------- .../transport_layer/tcp/tcp_timer.c | 6 +- .../networking/transport_layer/tcp/tcp_tx.c | 45 ++++-- .../transport_layer/tcp/tcp_utils.c | 3 +- 6 files changed, 303 insertions(+), 95 deletions(-) diff --git a/kernel/networking/transport_layer/tcp/tcp_core.c b/kernel/networking/transport_layer/tcp/tcp_core.c index a46c7ab0..545e7986 100644 --- a/kernel/networking/transport_layer/tcp/tcp_core.c +++ b/kernel/networking/transport_layer/tcp/tcp_core.c @@ -13,8 +13,118 @@ tcp_flow_t *tcp_flows[MAX_TCP_FLOWS]; uint16_t tcp_active_flows[MAX_TCP_FLOWS]; uint16_t tcp_active_count; +uint32_t tcp_ooo_global_bytes; +uint32_t tcp_ooo_global_segs; +uint32_t tcp_tx_global_bytes; +tcp_stats_t tcp_stats; static uint32_t tcp_generation_next = 1; +//TODO these statics are small and i find them annoying but theyre good for making the code more readable +//maybe move them to a dedicated file? +uint32_t tcp_clamp_rcvbuf(uint32_t size) { + if (!size) size = TCP_DEFAULT_RCV_BUF; + if (size < TCP_RCV_BUF_MIN) size = TCP_RCV_BUF_MIN; + if (size > TCP_RCV_BUF_MAX) size = TCP_RCV_BUF_MAX; + return size; +} + +void tcp_account_ooo_remove(uint32_t bytes, uint32_t segs) { + if (tcp_ooo_global_bytes >= bytes) tcp_ooo_global_bytes -= bytes; + else tcp_ooo_global_bytes = 0; + if (tcp_ooo_global_segs >= segs) tcp_ooo_global_segs -= segs; + else tcp_ooo_global_segs = 0; +} + +void tcp_account_tx_remove(tcp_flow_t *flow, uint32_t bytes) { + if (!flow || !bytes) return; + if (flow->tx.queued_bytes >= bytes) flow->tx.queued_bytes -= bytes; + else flow->tx.queued_bytes = 0; + if (tcp_tx_global_bytes >= bytes) tcp_tx_global_bytes -= bytes; + else tcp_tx_global_bytes = 0; +} + +tcp_admit_result_t tcp_admit_ooo(tcp_flow_t *flow, uint32_t increase, uint32_t remaining_nodes) { + if (!flow) return TCP_ADMIT_OOO_FLOW_BYTES; + + uint32_t mss = flow->tx.mss ? flow->tx.mss : TCP_DEFAULT_MSS; + uint32_t limit = flow->rx.rcv_wnd_max >> 1; + uint32_t floor = mss * 4u; + + if (floor > TCP_REASS_MAX_BYTES / 2) floor = TCP_REASS_MAX_BYTES / 2; + if (limit > TCP_REASS_MAX_BYTES) limit = TCP_REASS_MAX_BYTES; + if (limit < floor) limit = floor; + if (limit > flow->rx.rcv_wnd_max) limit = flow->rx.rcv_wnd_max; + + uint32_t seg_increase = remaining_nodes >= flow->rx.reass_count ? 1u : 0; + + if (remaining_nodes >= TCP_REASS_MAX_SEGS) return TCP_ADMIT_OOO_FLOW_SEGS; + if (increase && flow->rx.rcv_ooo_used + increase > limit) return TCP_ADMIT_OOO_FLOW_BYTES; + if (increase && tcp_ooo_global_bytes + increase > TCP_REASS_GLOBAL_MAX_BYTES) return TCP_ADMIT_OOO_GLOBAL_BYTES; + if (seg_increase && tcp_ooo_global_segs + seg_increase > TCP_REASS_GLOBAL_MAX_SEGS) return TCP_ADMIT_OOO_GLOBAL_SEGS; + + return TCP_ADMIT_OK; +} + +tcp_admit_result_t tcp_admit_syn(uint8_t l3_id, uint16_t port, ip_version_t ver, const void *src_ip) { + uint32_t syn_total = 0; + uint32_t syn_listener = 0; + uint32_t syn_source = 0; + uint32_t timewait = 0; + uint32_t orphan = 0; + size_t ip_len = (size_t)(ver == IP_VER6 ? 16 : 4); + + for (int i = 0; i < MAX_TCP_FLOWS; i++) { + tcp_flow_t *f = tcp_flows[i]; + if (!f) continue; + + if (f->base.state == TCP_SYN_RECEIVED) { + syn_total++; + if (f->base.local_port == port && f->base.l3_id == l3_id) syn_listener++; + if (src_ip && f->base.local_port == port && f->base.l3_id == l3_id && f->base.remote.ver == ver && memcmp(f->base.remote.ip, src_ip, ip_len) == 0) syn_source++; + } else if (f->base.state == TCP_TIME_WAIT) timewait++; + else if (f->base.state != TCP_LISTEN && f->base.state != TCP_STATE_CLOSED && !f->base.local_port) orphan++; + } + + uint32_t score = (uint32_t)tcp_active_count * 4 + syn_total * 4 + timewait + orphan * 4 + (tcp_ooo_global_bytes >> 12) + (tcp_tx_global_bytes >> 12); + + if (tcp_active_count >= MAX_TCP_FLOWS) return TCP_ADMIT_FLOW_TABLE_FULL; + if (syn_total >= TCP_SYN_RECV_MAX_GLOBAL) return TCP_ADMIT_SYN_GLOBAL; + if (syn_listener >= TCP_SYN_RECV_MAX_LISTENER) return TCP_ADMIT_SYN_LISTENER; + if (syn_source >= TCP_SYN_RECV_MAX_SOURCE) return TCP_ADMIT_SYN_SOURCE; + if (orphan >= TCP_ORPHAN_MAX_GLOBAL) return TCP_ADMIT_ORPHAN_LIMIT; + if (score >= TCP_RESOURCE_BUDGET) return TCP_ADMIT_RESOURCE_BUDGET; + + return TCP_ADMIT_OK; +} + +void tcp_enter_time_wait(tcp_flow_t *flow) { + if (!flow) return; + + uint32_t timewait = 0; + int oldest = -1; + uint32_t oldest_ms = 0; + + for (int i = 0; i < MAX_TCP_FLOWS; i++) { + tcp_flow_t *f = tcp_flows[i]; + if (!f || f->base.state != TCP_TIME_WAIT) continue; + timewait++; + if ((oldest < 0 || f->timer.time_wait_ms > oldest_ms) && f != flow) { + oldest = i; + oldest_ms = f->timer.time_wait_ms; + } + } + + if (timewait >= TCP_TIMEWAIT_MAX_GLOBAL && oldest >= 0) { + tcp_stats.timewait_reap_oldest++; + tcp_free_flow(oldest); + } + + flow->base.state = TCP_TIME_WAIT; + tcp_release_io_buffers(flow); + flow->timer.time_wait_ms = 0; + tcp_daemon_kick(); +} + int find_flow(uint16_t local_port, ip_version_t ver, const void *local_ip, const void *remote_ip, uint16_t remote_port){ for (uint16_t n = 0; n < tcp_active_count; n++){ int i = tcp_active_flows[n]; @@ -80,7 +190,10 @@ void tcp_release_io_buffers(tcp_flow_t *f) { for (int i = 0; i < TCP_MAX_TX_SEGS; i++) { tcp_tx_seg_t *s = &f->tx.txq[i]; - if (s->used && s->buf && s->len)release((void*)s->buf); + if (s->used && s->buf && s->len) { + tcp_account_tx_remove(f, (uint32_t)s->len); + release((void*)s->buf); + } memset(s, 0, sizeof(*s)); } @@ -92,6 +205,7 @@ void tcp_release_io_buffers(tcp_flow_t *f) { f->tx.nagle_flushing = 0; f->tx.nagle_appending = 0; + if (f->rx.reass_count || f->rx.rcv_ooo_used) tcp_account_ooo_remove(f->rx.rcv_ooo_used, f->rx.reass_count); if (f->rx.rcv_buf) release((void*)f->rx.rcv_buf); f->rx.rcv_buf = 0; f->rx.rcv_base = f->rx.rcv_nxt; @@ -127,7 +241,7 @@ tcp_flow_t *tcp_alloc_flow(void){ f->base.ctx.flow_generation = f->base.generation; f->tx.rto = TCP_INIT_RTO; - f->rx.rcv_wnd_max = TCP_DEFAULT_RCV_BUF; + f->rx.rcv_wnd_max = tcp_clamp_rcvbuf(TCP_DEFAULT_RCV_BUF); f->rx.rcv_wnd = f->rx.rcv_wnd_max; f->tx.mss = TCP_DEFAULT_MSS; @@ -162,7 +276,10 @@ void tcp_free_flow(int idx) { for (int i = 0; i < TCP_MAX_TX_SEGS; i++) { tcp_tx_seg_t *s =&f->tx.txq[i]; - if (s->used && s->buf && s->len)release((void*)s->buf); + if (s->used && s->buf && s->len) { + tcp_account_tx_remove(f, (uint32_t)s->len); + release((void*)s->buf); + } memset(s, 0, sizeof(*s)); } @@ -174,6 +291,7 @@ void tcp_free_flow(int idx) { f->tx.nagle_flushing = 0; f->tx.nagle_appending = 0; + if (f->rx.reass_count || f->rx.rcv_ooo_used) tcp_account_ooo_remove(f->rx.rcv_ooo_used, f->rx.reass_count); if (f->rx.rcv_buf) release((void*)f->rx.rcv_buf); f->rx.rcv_buf = 0; f->rx.rcv_base = f->rx.rcv_nxt; @@ -351,8 +469,8 @@ bool tcp_bind_l3(uint8_t l3_id, uint16_t port, uint16_t pid, port_recv_handler_t f->base.ctx.ack = 0; f->base.ctx.flags = 0; - f->rx.rcv_wnd_max = TCP_DEFAULT_RCV_BUF; - if (extra && (extra->flags & SOCK_OPT_BUF_SIZE) && extra->buf_size) f->rx.rcv_wnd_max = extra->buf_size; + f->rx.rcv_wnd_max = tcp_clamp_rcvbuf(TCP_DEFAULT_RCV_BUF); + if (extra && (extra->flags & SOCK_OPT_BUF_SIZE) && extra->buf_size) f->rx.rcv_wnd_max = tcp_clamp_rcvbuf(extra->buf_size); f->rx.rcv_base = 0; f->rx.rcv_data_nxt = 0; f->rx.rcv_ooo_used = 0; @@ -477,8 +595,8 @@ bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, flow->rx.rcv_ooo_used = 0; flow->rx.sack_recent_left = 0; flow->rx.sack_recent_right = 0; - flow->rx.rcv_wnd_max = TCP_DEFAULT_RCV_BUF; - if (extra && (extra->flags & SOCK_OPT_BUF_SIZE) && extra->buf_size) flow->rx.rcv_wnd_max = extra->buf_size; + flow->rx.rcv_wnd_max = tcp_clamp_rcvbuf(TCP_DEFAULT_RCV_BUF); + if (extra && (extra->flags & SOCK_OPT_BUF_SIZE) && extra->buf_size) flow->rx.rcv_wnd_max = tcp_clamp_rcvbuf(extra->buf_size); flow->rx.rcv_adv_edge = 0; flow->tx.mss = tcp_calc_mss_for_l3(l3_id, dst->ver, dst->ip); diff --git a/kernel/networking/transport_layer/tcp/tcp_internal.h b/kernel/networking/transport_layer/tcp/tcp_internal.h index 127c19b4..e191a1d5 100644 --- a/kernel/networking/transport_layer/tcp/tcp_internal.h +++ b/kernel/networking/transport_layer/tcp/tcp_internal.h @@ -19,10 +19,28 @@ extern "C" { #define TCP_REASS_MAX_SEGS 32 #define TCP_REASS_MAX_BYTES (64u * 1024u) +#define TCP_REASS_GLOBAL_MAX_SEGS 1024u +#define TCP_REASS_GLOBAL_MAX_BYTES (2 * 1024u * 1024u) #define TCP_DEFAULT_MSS 1460 +#define TCP_RCV_BUF_MIN (8u * 1024u) #define TCP_DEFAULT_RCV_BUF (256u * 1024u) +#define TCP_RCV_BUF_MAX (2u * 1024u * 1024u) +#define TCP_TX_MAX_BYTES_PER_FLOW (512u * 1024u) +#define TCP_TX_MAX_BYTES_GLOBAL (4u * 1024u * 1024u) +#define TCP_TX_CONTROL_RESERVE_SEGS 2u +#define TCP_SYN_RECV_MAX_GLOBAL (MAX_TCP_FLOWS / 4u) +#define TCP_SYN_RECV_MAX_LISTENER 32u +#define TCP_SYN_RECV_MAX_SOURCE 8u +#define TCP_TIMEWAIT_MAX_GLOBAL 128u +#define TCP_ORPHAN_MAX_GLOBAL 32u +#define TCP_RESOURCE_BUDGET 4096u #define TCP_PERSIST_PROBE_BUFSZ 1 +#define TCP_SEQ_LT(a,b) ((int32_t)((uint32_t)(a) - (uint32_t)(b)) < 0) +#define TCP_SEQ_LEQ(a,b) ((int32_t)((uint32_t)(a) - (uint32_t)(b)) <= 0) +#define TCP_SEQ_GT(a,b) ((int32_t)((uint32_t)(a) - (uint32_t)(b)) > 0) +#define TCP_SEQ_GEQ(a,b) ((int32_t)((uint32_t)(a) - (uint32_t)(b)) >= 0) + #define TCP_DELAYED_ACK_MS 10 #define TCP_PERSIST_MIN_MS 500 #define TCP_PERSIST_MAX_MS 60000 @@ -97,6 +115,7 @@ typedef struct { uint32_t nagle_timer_ms; tcp_tx_seg_t txq[TCP_MAX_TX_SEGS]; + uint32_t queued_bytes; uint8_t fin_tx_pending; } tcp_flow_tx_t; @@ -148,9 +167,55 @@ typedef struct { tcp_flow_ip_t ip; } tcp_flow_t; +typedef enum { + TCP_ADMIT_OK = 0, + TCP_ADMIT_OOO_FLOW_BYTES, + TCP_ADMIT_OOO_FLOW_SEGS, + TCP_ADMIT_OOO_GLOBAL_BYTES, + TCP_ADMIT_OOO_GLOBAL_SEGS, + TCP_ADMIT_SYN_GLOBAL, + TCP_ADMIT_SYN_LISTENER, + TCP_ADMIT_SYN_SOURCE, + TCP_ADMIT_ORPHAN_LIMIT, + TCP_ADMIT_TX_FLOW_BYTES, + TCP_ADMIT_TX_FLOW_SEGS, + TCP_ADMIT_TX_GLOBAL_BYTES, + TCP_ADMIT_RESOURCE_BUDGET, + TCP_ADMIT_FLOW_TABLE_FULL +} tcp_admit_result_t; + +typedef struct { + uint64_t ooo_drop_flow_bytes; + uint64_t ooo_drop_flow_segs; + uint64_t ooo_drop_global_bytes; + uint64_t ooo_drop_global_segs; + uint64_t syn_drop_global; + uint64_t syn_drop_listener; + uint64_t syn_drop_source; + uint64_t acceptq_drop_full; + uint64_t timewait_reap_oldest; + uint64_t orphan_drop_global; + uint64_t tx_block_flow_bytes; + uint64_t tx_block_flow_segs; + uint64_t tx_block_global_bytes; + uint64_t resource_budget_drop; + uint64_t flow_table_full; +} tcp_stats_t; + extern tcp_flow_t *tcp_flows[MAX_TCP_FLOWS]; extern uint16_t tcp_active_flows[MAX_TCP_FLOWS]; extern uint16_t tcp_active_count; +extern uint32_t tcp_ooo_global_bytes; +extern uint32_t tcp_ooo_global_segs; +extern uint32_t tcp_tx_global_bytes; +extern tcp_stats_t tcp_stats; + +uint32_t tcp_clamp_rcvbuf(uint32_t size); +tcp_admit_result_t tcp_admit_syn(uint8_t l3_id, uint16_t port, ip_version_t ver, const void *src_ip); +tcp_admit_result_t tcp_admit_ooo(tcp_flow_t *flow, uint32_t increase, uint32_t remaining_nodes); +void tcp_account_ooo_remove(uint32_t bytes, uint32_t segs); +void tcp_account_tx_remove(tcp_flow_t *flow, uint32_t bytes); +void tcp_enter_time_wait(tcp_flow_t *flow); tcp_flow_t *tcp_alloc_flow(void); void tcp_free_flow(int idx); diff --git a/kernel/networking/transport_layer/tcp/tcp_rx.c b/kernel/networking/transport_layer/tcp/tcp_rx.c index 2880598e..7b2d4c70 100644 --- a/kernel/networking/transport_layer/tcp/tcp_rx.c +++ b/kernel/networking/transport_layer/tcp/tcp_rx.c @@ -14,6 +14,7 @@ static int16_t tcp_reass_count_ok(tcp_flow_t *flow) { if (flow->rx.reass_count <= TCP_REASS_MAX_SEGS) return 1; + if (flow->rx.reass_count || flow->rx.rcv_ooo_used) tcp_account_ooo_remove(flow->rx.rcv_ooo_used, flow->rx.reass_count); flow->rx.reass_count = 0; flow->rx.rcv_ooo_used = 0; flow->rx.sack_recent_left = 0; @@ -32,10 +33,11 @@ static void tcp_reass_remove(tcp_flow_t *flow, int idx) { if (idx < 0 || idx >= flow->rx.reass_count) return; uint32_t len = 0; - if (flow->rx.reass[idx].end > flow->rx.reass[idx].seq) len = flow->rx.reass[idx].end - flow->rx.reass[idx].seq; + if (TCP_SEQ_GT(flow->rx.reass[idx].end, flow->rx.reass[idx].seq)) len = flow->rx.reass[idx].end - flow->rx.reass[idx].seq; if (flow->rx.rcv_ooo_used >= len) flow->rx.rcv_ooo_used -= len; else flow->rx.rcv_ooo_used = 0; + tcp_account_ooo_remove(len, 1); for (int i = idx; i + 1 < flow->rx.reass_count; i++) flow->rx.reass[i] = flow->rx.reass[i+1]; @@ -60,7 +62,7 @@ static int tcp_reass_drain_inseq(tcp_flow_t *flow) { if (idx < 0) break; uint32_t end = flow->rx.reass[idx].end; - if (end <= flow->rx.rcv_nxt) { + if (TCP_SEQ_LEQ(end, flow->rx.rcv_nxt)) { tcp_reass_remove(flow, idx); continue; } @@ -84,7 +86,7 @@ int64_t tcp_flow_read(tcp_data *flow_ctx, void *buf, uint64_t len) { if (flow->base.state == TCP_STATE_CLOSED) return TCP_DISCONNECT; if (!flow->rx.rcv_buf || !flow->rx.rcv_wnd_max) return 0; - if (flow->rx.rcv_data_nxt < flow->rx.rcv_base) return 0; + if (TCP_SEQ_LT(flow->rx.rcv_data_nxt, flow->rx.rcv_base)) return 0; uint32_t n = flow->rx.rcv_data_nxt - flow->rx.rcv_base; if (!n) return 0; @@ -107,7 +109,7 @@ int64_t tcp_flow_read(tcp_data *flow_ctx, void *buf, uint64_t len) { uint32_t tcp_flow_readable(tcp_data *flow_ctx) { tcp_flow_t *flow = tcp_flow_from_ctx(flow_ctx); if (!flow || flow->base.state == TCP_STATE_CLOSED || !flow->rx.rcv_buf || !flow->rx.rcv_wnd_max) return 0; - if (flow->rx.rcv_data_nxt < flow->rx.rcv_base) return 0; + if (TCP_SEQ_LT(flow->rx.rcv_data_nxt, flow->rx.rcv_base)) return 0; return flow->rx.rcv_data_nxt - flow->rx.rcv_base; } @@ -117,7 +119,7 @@ bool tcp_flow_recv_closed(tcp_data *flow_ctx) { if (flow->base.state == TCP_STATE_CLOSED || flow->base.state == TCP_TIME_WAIT) return true; if (flow->base.state == TCP_CLOSE_WAIT) { if (!flow->rx.rcv_buf || !flow->rx.rcv_wnd_max) return true; - if (flow->rx.rcv_data_nxt < flow->rx.rcv_base) return true; + if (TCP_SEQ_LT(flow->rx.rcv_data_nxt, flow->rx.rcv_base)) return true; return flow->rx.rcv_data_nxt == flow->rx.rcv_base; } return false; @@ -133,9 +135,9 @@ tcp_tx_seg_t *tcp_find_first_unacked(tcp_flow_t *flow) { if (!s->used) continue; uint32_t end = s->seq + s->len + (s->syn ? 1u : 0u) + (s->fin ? 1u : 0u); - if (end <= flow->tx.snd_una) continue; + if (TCP_SEQ_LEQ(end, flow->tx.snd_una)) continue; - if (!best || s->seq < best_seq){ + if (!best || TCP_SEQ_LT(s->seq, best_seq)){ best = s; best_seq = s->seq; } @@ -152,9 +154,9 @@ static int tcp_apply_sack_blocks(tcp_flow_t *flow, const tcp_parsed_opts_t *opts uint32_t left = opts->sacks[b].left; uint32_t right = opts->sacks[b].right; - if (right <= left) continue; - if (right <= flow->tx.snd_una) continue; - if (left >= flow->tx.snd_nxt) continue; + if (TCP_SEQ_LEQ(right, left)) continue; + if (TCP_SEQ_LEQ(right, flow->tx.snd_una)) continue; + if (TCP_SEQ_GEQ(left, flow->tx.snd_nxt)) continue; for (int i = 0; i < TCP_MAX_TX_SEGS; i++) { tcp_tx_seg_t *s = &flow->tx.txq[i]; @@ -162,8 +164,8 @@ static int tcp_apply_sack_blocks(tcp_flow_t *flow, const tcp_parsed_opts_t *opts if (!s->used) continue; uint32_t end = s->seq + s->len + (s->syn ? 1 : 0) + (s->fin ? 1 : 0); - if (end <= flow->tx.snd_una) continue; - if (s->seq < left || end > right) continue; + if (TCP_SEQ_LEQ(end, flow->tx.snd_una)) continue; + if (TCP_SEQ_LT(s->seq, left) || TCP_SEQ_GT(end, right)) continue; if (!s->sacked) changed = 1; s->sacked = 1; @@ -184,8 +186,8 @@ static tcp_tx_seg_t *tcp_find_sack_retransmit(tcp_flow_t *flow) { if (!s->used || !s->sacked) continue; uint32_t end = s->seq + s->len + (s->syn ? 1u : 0u) + (s->fin ? 1u : 0u); - if (end <= flow->tx.snd_una) continue; - if (end > highest_sacked) highest_sacked = end; + if (TCP_SEQ_LEQ(end, flow->tx.snd_una)) continue; + if (TCP_SEQ_GT(end, highest_sacked)) highest_sacked = end; } if (highest_sacked == flow->tx.snd_una) return NULL; @@ -197,10 +199,10 @@ static tcp_tx_seg_t *tcp_find_sack_retransmit(tcp_flow_t *flow) { if (s->sacked || s->sack_retransmitted) continue; uint32_t end = s->seq + s->len + (s->syn ? 1 : 0) + (s->fin ? 1 : 0); - if (end <= flow->tx.snd_una) continue; - if (s->seq >= highest_sacked) continue; + if (TCP_SEQ_LEQ(end, flow->tx.snd_una)) continue; + if (TCP_SEQ_GEQ(s->seq, highest_sacked)) continue; - if (!best || s->seq < best_seq) { + if (!best || TCP_SEQ_LT(s->seq, best_seq)) { best = s; best_seq = s->seq; } @@ -211,7 +213,7 @@ static tcp_tx_seg_t *tcp_find_sack_retransmit(tcp_flow_t *flow) { void tcp_cc_on_timeout(tcp_flow_t *f){ uint32_t mss = f->tx.mss ? f->tx.mss : TCP_DEFAULT_MSS; - uint32_t flight = f->tx.snd_nxt > f->tx.snd_una ? f->tx.snd_nxt - f->tx.snd_una : 0; + uint32_t flight = TCP_SEQ_GT(f->tx.snd_nxt, f->tx.snd_una) ? f->tx.snd_nxt - f->tx.snd_una : 0; uint32_t half = flight / 2; uint32_t minth = 2u * mss; @@ -234,7 +236,7 @@ static void tcp_cc_on_new_ack(tcp_flow_t *f, uint32_t ack) { uint32_t mss = f->tx.mss ? f->tx.mss : TCP_DEFAULT_MSS; if (f->tx.in_fast_recovery){ - if (ack >= f->tx.recover){ + if (TCP_SEQ_GEQ(ack, f->tx.recover)){ f->tx.cwnd = f->tx.ssthresh; if (f->tx.cwnd < mss) f->tx.cwnd = mss; @@ -416,22 +418,20 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a int listen_idx = find_flow(dst_port, ipver, dst_ip_addr, NULL, 0); if (listen_idx < 0)listen_idx = find_flow(dst_port, ipver, NULL, NULL, 0); - if ((flags & (1u << SYN_F)) && !(flags & (1u << ACK_F)) && listen_idx >= 0){ + if ((flags & (1u << SYN_F)) && !(flags & ((1u << ACK_F) | (1u << RST_F) | (1u << FIN_F))) && data_len == 0 && listen_idx >= 0){ rng_t rng; uint64_t virt_timer; asm volatile ("mrs %0, cntvct_el0" : "=r"(virt_timer)); rng_seed(&rng, virt_timer); - int syn_total = 0; - int syn_port = 0; - for (int k = 0; k < MAX_TCP_FLOWS; k++){ - tcp_flow_t *f = tcp_flows[k]; - if (!f) continue; - if (f->base.state != TCP_SYN_RECEIVED) continue; - syn_total++; - if (f->base.local_port == dst_port && f->base.l3_id == l3_id) syn_port++; - } - if (syn_total >= (MAX_TCP_FLOWS / 4) || syn_port >= 32) { + tcp_admit_result_t syn_admit = tcp_admit_syn(l3_id, dst_port, ipver, src_ip_addr); + if (syn_admit != TCP_ADMIT_OK) { + if (syn_admit == TCP_ADMIT_SYN_GLOBAL) tcp_stats.syn_drop_global++; + else if (syn_admit == TCP_ADMIT_SYN_LISTENER) tcp_stats.syn_drop_listener++; + else if (syn_admit == TCP_ADMIT_SYN_SOURCE) tcp_stats.syn_drop_source++; + else if (syn_admit == TCP_ADMIT_ORPHAN_LIMIT) tcp_stats.orphan_drop_global++; + else if (syn_admit == TCP_ADMIT_RESOURCE_BUDGET) tcp_stats.resource_budget_drop++; + else if (syn_admit == TCP_ADMIT_FLOW_TABLE_FULL) tcp_stats.flow_table_full++; netpkt_unref(pkt); return; } @@ -599,7 +599,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a uint32_t seg_end = seq + seg_len; - if (seq <= flow->rx.rcv_nxt && seg_end >= flow->rx.rcv_nxt){ + if (TCP_SEQ_LEQ(seq, flow->rx.rcv_nxt) && TCP_SEQ_GEQ(seg_end, flow->rx.rcv_nxt)){ flow->timer.time_wait_ms = 0; tcp_send_ack_now(flow); } @@ -625,7 +625,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a uint8_t fin = (flags & (1u << FIN_F)) ? 1u : 0u; if (flags & (1u << ACK_F)){ - if (ack > flow->tx.snd_una && ack <= flow->tx.snd_nxt){ + if (TCP_SEQ_GT(ack, flow->tx.snd_una) && TCP_SEQ_LEQ(ack, flow->tx.snd_nxt)){ uint32_t prev_una = flow->tx.snd_una; flow->tx.snd_una = ack; @@ -638,10 +638,13 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a uint32_t s_end = s->seq + s->len + (s->syn ? 1u : 0u) + (s->fin ? 1u : 0u); - if (s_end <= ack){ + if (TCP_SEQ_LEQ(s_end, ack)){ if (s->rtt_sample && s->retransmit_cnt == 0) tcp_rtt_update(flow, s->timer_ms); - if (s->buf && s->len) release((void *)s->buf); + if (s->buf && s->len) { + tcp_account_tx_remove(flow, (uint32_t)s->len); + release((void *)s->buf); + } s->used = 0; s->buf = 0; @@ -654,20 +657,20 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a for (int i = 0; i < TCP_MAX_TX_SEGS; i++) { tcp_tx_seg_t *s = &flow->tx.txq[i]; if (!s->used) continue; - if (s->seq >= ack) continue; + if (TCP_SEQ_GEQ(s->seq, ack)) continue; s->sacked = 0; s->sack_retransmitted = 0; } if (flow->tx.sack_ok && parsed_opts.sack_count) tcp_apply_sack_blocks(flow, &parsed_opts); - if (ack > prev_una) tcp_cc_on_new_ack(flow, ack); + if (TCP_SEQ_GT(ack, prev_una)) tcp_cc_on_new_ack(flow, ack); - if (flow->base.state == TCP_FIN_WAIT_1 && ack >= flow->base.ctx.expected_ack){ + if (flow->base.state == TCP_FIN_WAIT_1 && TCP_SEQ_GEQ(ack, flow->base.ctx.expected_ack)){ flow->base.state = TCP_FIN_WAIT_2; flow->timer.fin_wait2_ms = 0; tcp_daemon_kick(); - } else if ((flow->base.state == TCP_LAST_ACK || flow->base.state == TCP_CLOSING) && ack >= flow->base.ctx.expected_ack){ + } else if ((flow->base.state == TCP_LAST_ACK || flow->base.state == TCP_CLOSING) && TCP_SEQ_GEQ(ack, flow->base.ctx.expected_ack)){ tcp_free_flow(idx); netpkt_unref(pkt); return; @@ -786,6 +789,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a uint32_t queued = 0; if (port_handler) queued = port_handler(ifx, ipver, src_ip_addr, dst_ip_addr, 0, src_port, dst_port); if (!queued) { + tcp_stats.acceptq_drop_full++; tcp_hdr_t rst_hdr; rst_hdr.src_port = bswap16(flow->base.local_port); rst_hdr.dst_port = bswap16(flow->base.remote.port); @@ -856,17 +860,17 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a uint32_t fin_seq = seg_seq + orig_data_len; uint32_t orig_end = seg_seq + orig_data_len + (fin ? 1u : 0u); - if (orig_end <= rcv_nxt || seg_seq >= wnd_end) { + if (TCP_SEQ_LEQ(orig_end, rcv_nxt) || TCP_SEQ_GEQ(seg_seq, wnd_end)) { need_ack = 1; ack_immediate = 1; } else { if (fin_in) { - if (fin_seq < rcv_nxt || fin_seq >= wnd_end) fin_in = 0; + if (TCP_SEQ_LT(fin_seq, rcv_nxt) || TCP_SEQ_GEQ(fin_seq, wnd_end)) fin_in = 0; } uint32_t payload = hdr_len; - if (seg_seq < rcv_nxt) { + if (TCP_SEQ_LT(seg_seq, rcv_nxt)) { uint32_t d = rcv_nxt - seg_seq; if (d >= data_len) { payload += data_len; @@ -880,8 +884,8 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a } if (data_len) { - if (seg_seq >= wnd_end) data_len = 0; - else if (seg_seq + data_len > wnd_end) data_len = wnd_end - seg_seq; + if (TCP_SEQ_GEQ(seg_seq, wnd_end)) data_len = 0; + else if (TCP_SEQ_GT(seg_seq + data_len, wnd_end)) data_len = wnd_end - seg_seq; } if (!data_len && !fin_in){ @@ -892,9 +896,9 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a uint32_t offer = data_len; uint32_t right_edge = flow->rx.rcv_adv_edge; uint32_t hard_edge = flow->rx.rcv_buf && flow->rx.rcv_wnd_max ? flow->rx.rcv_base + flow->rx.rcv_wnd_max : flow->rx.rcv_nxt; - if (right_edge > hard_edge) right_edge = hard_edge; - if (seg_seq >= right_edge) offer = 0; - else if (seg_seq + offer > right_edge) offer = right_edge - seg_seq; + if (TCP_SEQ_GT(right_edge, hard_edge)) right_edge = hard_edge; + if (TCP_SEQ_GEQ(seg_seq, right_edge)) offer = 0; + else if (TCP_SEQ_GT(seg_seq + offer, right_edge)) offer = right_edge - seg_seq; uint32_t accepted = 0; uint32_t stored = 0; @@ -936,10 +940,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a if (old == TCP_ESTABLISHED) flow->base.state = TCP_CLOSE_WAIT; else if (old == TCP_FIN_WAIT_1) flow->base.state = TCP_CLOSING; else if (old == TCP_FIN_WAIT_2 || old == TCP_CLOSING || old == TCP_LAST_ACK) { - flow->base.state = TCP_TIME_WAIT; - tcp_release_io_buffers(flow); - flow->timer.time_wait_ms = 0; - tcp_daemon_kick(); + tcp_enter_time_wait(flow); } ack_immediate = 1; @@ -962,10 +963,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a if (old == TCP_ESTABLISHED) flow->base.state = TCP_CLOSE_WAIT; else if (old == TCP_FIN_WAIT_1) flow->base.state = TCP_CLOSING; else if (old == TCP_FIN_WAIT_2 || old == TCP_CLOSING || old == TCP_LAST_ACK) { - flow->base.state = TCP_TIME_WAIT; - tcp_release_io_buffers(flow); - flow->timer.time_wait_ms = 0; - tcp_daemon_kick(); + tcp_enter_time_wait(flow); } ack_immediate = 1; @@ -981,7 +979,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a uint32_t ooo_data = payload; uint32_t ooo_len = data_len; - if (ooo_seq rx.rcv_nxt) { + if (TCP_SEQ_LT(ooo_seq, flow->rx.rcv_nxt)) { uint32_t d = flow->rx.rcv_nxt - ooo_seq; if (d >= ooo_len) ooo_len= 0; else { @@ -994,9 +992,9 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a if (ooo_len) { uint32_t right_edge = flow->rx.rcv_adv_edge; uint32_t hard_edge = flow->rx.rcv_base + flow->rx.rcv_wnd_max; - if (right_edge > hard_edge) right_edge = hard_edge; - if (ooo_seq >= right_edge) ooo_len = 0; - else if (ooo_seq + ooo_len > right_edge) ooo_len = right_edge - ooo_seq; + if (TCP_SEQ_GT(right_edge, hard_edge)) right_edge = hard_edge; + if (TCP_SEQ_GEQ(ooo_seq, right_edge)) ooo_len = 0; + else if (TCP_SEQ_GT(ooo_seq + ooo_len, right_edge)) ooo_len = right_edge - ooo_seq; } if (ooo_len) { @@ -1011,34 +1009,35 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a if (!tcp_reass_count_ok(flow)) ooo_len = 0; for (int i = 0; ooo_len && i < flow->rx.reass_count; i++) { tcp_reass_seg_t *r = &flow->rx.reass[i]; - if (r->seq <= start && r->end >= end) { + if (TCP_SEQ_LEQ(r->seq, start) && TCP_SEQ_GEQ(r->end, end)) { flow->rx.sack_recent_left = r->seq; flow->rx.sack_recent_right = r->end; covered = 1; break; } - if (r->end < merged_start || r->seq > merged_end) continue; + if (TCP_SEQ_LT(r->end, merged_start) || TCP_SEQ_GT(r->seq, merged_end)) continue; - if (r->seq < merged_start) merged_start = r->seq; - if (r->end > merged_end) merged_end = r->end; - if (r->end > r->seq) old_bytes += r->end - r->seq; + if (TCP_SEQ_LT(r->seq, merged_start)) merged_start = r->seq; + if (TCP_SEQ_GT(r->end, merged_end)) merged_end = r->end; + if (TCP_SEQ_GT(r->end, r->seq)) old_bytes += r->end - r->seq; overlapping++; } if (!covered && ooo_len) { uint32_t merged_len = merged_end - merged_start; uint32_t increase = merged_len > old_bytes ? merged_len - old_bytes : 0; - uint32_t ooo_limit = flow->rx.rcv_wnd_max >> 1; int remaining = flow->rx.reass_count - overlapping; if (remaining < 0) remaining = 0; - if (ooo_limit > TCP_REASS_MAX_BYTES) ooo_limit = TCP_REASS_MAX_BYTES; - if (ooo_limit < flow->tx.mss * 4) ooo_limit = flow->tx.mss * 4; - if (ooo_limit > flow->rx.rcv_wnd_max) ooo_limit = flow->rx.rcv_wnd_max; - - if (remaining >= TCP_REASS_MAX_SEGS) ooo_len = 0; - else if (increase && flow->rx.rcv_ooo_used + increase > ooo_limit) ooo_len = 0; + tcp_admit_result_t ooo_admit = tcp_admit_ooo(flow, increase, (uint32_t)remaining); + if (ooo_admit != TCP_ADMIT_OK) { + if (ooo_admit == TCP_ADMIT_OOO_FLOW_BYTES) tcp_stats.ooo_drop_flow_bytes++; + else if (ooo_admit == TCP_ADMIT_OOO_FLOW_SEGS) tcp_stats.ooo_drop_flow_segs++; + else if (ooo_admit == TCP_ADMIT_OOO_GLOBAL_BYTES) tcp_stats.ooo_drop_global_bytes++; + else if (ooo_admit == TCP_ADMIT_OOO_GLOBAL_SEGS) tcp_stats.ooo_drop_global_segs++; + ooo_len = 0; + } } if (!covered && ooo_len) { @@ -1055,7 +1054,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a if (copied) { for (int i = 0; i < flow->rx.reass_count;) { tcp_reass_seg_t *r = &flow->rx.reass[i]; - if (r->end < merged_start || r->seq > merged_end) { + if (TCP_SEQ_LT(r->end, merged_start) || TCP_SEQ_GT(r->seq, merged_end)) { i++; continue; } @@ -1065,7 +1064,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a if (flow->rx.reass_count < TCP_REASS_MAX_SEGS) { int pos_idx = flow->rx.reass_count; - while (pos_idx > 0 &&flow->rx.reass[pos_idx - 1].seq > merged_start) { + while (pos_idx > 0 && TCP_SEQ_GT(flow->rx.reass[pos_idx - 1].seq, merged_start)) { flow->rx.reass[pos_idx] = flow->rx.reass[pos_idx - 1]; pos_idx--; } @@ -1074,6 +1073,8 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a flow->rx.reass[pos_idx].end = merged_end; flow->rx.reass_count++; flow->rx.rcv_ooo_used += merged_end - merged_start; + tcp_ooo_global_bytes += merged_end - merged_start; + tcp_ooo_global_segs++; flow->rx.sack_recent_left = merged_start; flow->rx.sack_recent_right = merged_end; tcp_calc_adv_wnd_field(flow, 1); @@ -1138,6 +1139,6 @@ void tcp_flow_on_app_read(tcp_data *flow_ctx, uint32_t bytes_read){ if (half && half < threshold) threshold = half; if (!threshold) threshold = 1; - uint32_t delta = flow->rx.rcv_adv_edge > old_edge ? flow->rx.rcv_adv_edge - old_edge : 0; + uint32_t delta = TCP_SEQ_GT(flow->rx.rcv_adv_edge, old_edge) ? flow->rx.rcv_adv_edge - old_edge : 0; if (advanced || flow->rx.rcv_nxt != old_nxt || (!old_adv_field && flow->base.ctx.window) || delta >= threshold) tcp_send_ack_now(flow); } diff --git a/kernel/networking/transport_layer/tcp/tcp_timer.c b/kernel/networking/transport_layer/tcp/tcp_timer.c index 3a4e2ee1..395fb848 100644 --- a/kernel/networking/transport_layer/tcp/tcp_timer.c +++ b/kernel/networking/transport_layer/tcp/tcp_timer.c @@ -43,7 +43,7 @@ int tcp_has_pending_timers(void) { //TODO mhh this should be event driven to avo tcp_tx_seg_t *seg = &f->tx.txq[j]; if (!seg->used) continue; uint32_t end_seq = seg->seq + seg->len + (seg->syn ? 1u : 0u) + (seg->fin ? 1u : 0u); - if (end_seq > f->tx.snd_una) return 1; + if (TCP_SEQ_GT(end_seq, f->tx.snd_una)) return 1; } } @@ -114,7 +114,7 @@ void tcp_tick_all(uint32_t elapsed_ms) { } } - if (f->tx.snd_wnd == 0 && (f->tx.snd_nxt > f->tx.snd_una || f->tx.fin_tx_pending)) { + if (f->tx.snd_wnd == 0 && (TCP_SEQ_GT(f->tx.snd_nxt, f->tx.snd_una) || f->tx.fin_tx_pending)) { if (!f->timer.persist_active) { f->timer.persist_active = 1; f->timer.persist_timer_ms = 0; @@ -141,7 +141,7 @@ void tcp_tick_all(uint32_t elapsed_ms) { uint32_t probe_seq = f->tx.snd_una; if (!best && f->tx.fin_tx_pending && f->tx.snd_nxt == f->tx.snd_una && f->tx.snd_nxt) probe_seq = f->tx.snd_nxt-1; - if (best && best->buf && best->len && probe_seq >= best->seq && probe_seq < best->seq + best->len) { + if (best && best->buf && best->len && TCP_SEQ_GEQ(probe_seq, best->seq) && TCP_SEQ_LT(probe_seq, best->seq + best->len)) { payload[0] = *((uint8_t *)best->buf + (probe_seq - best->seq)); pp = payload; pl = 1; diff --git a/kernel/networking/transport_layer/tcp/tcp_tx.c b/kernel/networking/transport_layer/tcp/tcp_tx.c index 15b36d11..4cec6c80 100644 --- a/kernel/networking/transport_layer/tcp/tcp_tx.c +++ b/kernel/networking/transport_layer/tcp/tcp_tx.c @@ -5,22 +5,22 @@ uint16_t tcp_calc_adv_wnd_field(tcp_flow_t *flow, uint8_t apply_scale) { uint32_t shift = 0; if (apply_scale && flow->tx.ws_ok && flow->tx.ws_send) shift = flow->tx.ws_send; - if (flow->rx.rcv_adv_edge < flow->rx.rcv_nxt) flow->rx.rcv_adv_edge = flow->rx.rcv_nxt; + if (TCP_SEQ_LT(flow->rx.rcv_adv_edge, flow->rx.rcv_nxt)) flow->rx.rcv_adv_edge = flow->rx.rcv_nxt; uint32_t accept_edge = flow->rx.rcv_adv_edge; uint32_t hard_edge = flow->rx.rcv_nxt; if (flow->rx.rcv_buf && flow->rx.rcv_wnd_max) { hard_edge = flow->rx.rcv_base + flow->rx.rcv_wnd_max; - if (hard_edge < flow->rx.rcv_nxt) hard_edge = flow->rx.rcv_nxt; + if (TCP_SEQ_LT(hard_edge, flow->rx.rcv_nxt)) hard_edge = flow->rx.rcv_nxt; } else if (flow->base.state == TCP_SYN_RECEIVED && flow->rx.rcv_wnd_max) hard_edge = flow->rx.rcv_nxt + flow->rx.rcv_wnd_max; - if (hard_edge > accept_edge) { + if (TCP_SEQ_GT(hard_edge, accept_edge)) { uint32_t adv = hard_edge - flow->rx.rcv_nxt; if (apply_scale && adv) { uint32_t threshold = flow->tx.mss ? flow->tx.mss : TCP_DEFAULT_MSS; uint32_t half = flow->rx.rcv_wnd_max >> 1; - uint32_t already = accept_edge > flow->rx.rcv_nxt ? accept_edge - flow->rx.rcv_nxt : 0; + uint32_t already = TCP_SEQ_GT(accept_edge, flow->rx.rcv_nxt) ? accept_edge - flow->rx.rcv_nxt : 0; if (half && half < threshold) threshold = half; if (!threshold) threshold = 1; if (!already && adv < threshold) adv = 0; @@ -40,11 +40,11 @@ uint16_t tcp_calc_adv_wnd_field(tcp_flow_t *flow, uint8_t apply_scale) { } uint32_t new_edge = flow->rx.rcv_nxt + actual; - if (actual && new_edge > accept_edge) accept_edge = new_edge; + if (actual && TCP_SEQ_GT(new_edge, accept_edge)) accept_edge = new_edge; } } - if (accept_edge < flow->rx.rcv_nxt) accept_edge = flow->rx.rcv_nxt; + if (TCP_SEQ_LT(accept_edge, flow->rx.rcv_nxt)) accept_edge = flow->rx.rcv_nxt; uint32_t accept_adv = accept_edge - flow->rx.rcv_nxt; uint32_t field = accept_adv; @@ -68,7 +68,7 @@ static uint64_t tcp_emit_data(tcp_flow_t *flow, const uint8_t *payload, uint64_t if (!flow || (!payload && payload_len)) return 0; if (flow->base.state != TCP_ESTABLISHED && flow->base.state != TCP_CLOSE_WAIT) return 0; - uint64_t in_flight = flow->tx.snd_nxt - flow->tx.snd_una; + uint64_t in_flight = TCP_SEQ_GT(flow->tx.snd_nxt, flow->tx.snd_una) ? flow->tx.snd_nxt - flow->tx.snd_una : 0; uint32_t wnd = flow->tx.snd_wnd; uint32_t cwnd = flow->tx.cwnd ? flow->tx.cwnd : (flow->tx.mss ? flow->tx.mss : TCP_DEFAULT_MSS); uint32_t eff_wnd = wnd < cwnd ? wnd : cwnd; @@ -81,9 +81,30 @@ static uint64_t tcp_emit_data(tcp_flow_t *flow, const uint8_t *payload, uint64_t int first_segment = 1; while (remaining > 0 && can_send > 0) { + uint32_t free_slots = 0; + for (int i = 0; i < TCP_MAX_TX_SEGS; i++) if (!flow->tx.txq[i].used) free_slots++; + if (free_slots <= TCP_TX_CONTROL_RESERVE_SEGS) { + tcp_stats.tx_block_flow_segs++; + break; + } + if (flow->tx.queued_bytes >= TCP_TX_MAX_BYTES_PER_FLOW) { + tcp_stats.tx_block_flow_bytes++; + break; + } + if (tcp_tx_global_bytes >= TCP_TX_MAX_BYTES_GLOBAL) { + tcp_stats.tx_block_global_bytes++; + break; + } + uint64_t seg_len = remaining > can_send ? can_send : remaining; if (flow->tx.mss && seg_len > flow->tx.mss) seg_len = flow->tx.mss; + uint32_t flow_room = TCP_TX_MAX_BYTES_PER_FLOW - flow->tx.queued_bytes; + uint32_t global_room = TCP_TX_MAX_BYTES_GLOBAL - tcp_tx_global_bytes; + if (seg_len > flow_room) seg_len = flow_room; + if (seg_len > global_room) seg_len = global_room; + if (!seg_len) break; + tcp_tx_seg_t *seg = tcp_alloc_tx_seg(flow); if (!seg) break; @@ -112,6 +133,8 @@ static uint64_t tcp_emit_data(tcp_flow_t *flow, const uint8_t *payload, uint64_t break; } + flow->tx.queued_bytes += (uint32_t)seg_len; + tcp_tx_global_bytes += (uint32_t)seg_len; flow->tx.snd_nxt += seg_len; sent_bytes += seg_len; remaining -= seg_len; @@ -164,7 +187,7 @@ uint64_t tcp_flush_nagle(tcp_flow_t *flow, uint8_t force) { if (!flow || !flow->tx.nagle_len || !flow->tx.nagle_buf) return 0; if (flow->tx.nagle_flushing || flow->tx.nagle_appending) return 0; - uint64_t in_flight = flow->tx.snd_nxt - flow->tx.snd_una; + uint64_t in_flight = TCP_SEQ_GT(flow->tx.snd_nxt, flow->tx.snd_una) ? flow->tx.snd_nxt - flow->tx.snd_una : 0; if (!force && in_flight && flow->tx.nagle_lentx.nagle_flushing = 1; @@ -279,8 +302,8 @@ void tcp_send_ack_now(tcp_flow_t *flow){ if (flow->rx.sack_recent_right > flow->rx.sack_recent_left) { for (uint32_t i = 0; i < flow->rx.reass_count; i++) { - if (flow->rx.reass[i].seq > flow->rx.sack_recent_left) continue; - if (flow->rx.reass[i].end < flow->rx.sack_recent_right) continue; + if (TCP_SEQ_GT(flow->rx.reass[i].seq, flow->rx.sack_recent_left)) continue; + if (TCP_SEQ_LT(flow->rx.reass[i].end, flow->rx.sack_recent_right)) continue; blocks[n].left = flow->rx.reass[i].seq; blocks[n].right = flow->rx.reass[i].end; @@ -351,7 +374,7 @@ int tcp_try_send_pending_fin(tcp_flow_t *flow) { if (flow->tx.nagle_len) tcp_flush_nagle(flow, 1); if (flow->tx.nagle_len) return 0; - uint64_t in_flight = flow->tx.snd_nxt - flow->tx.snd_una; + uint64_t in_flight = TCP_SEQ_GT(flow->tx.snd_nxt, flow->tx.snd_una) ? flow->tx.snd_nxt - flow->tx.snd_una : 0; uint32_t wnd = flow->tx.snd_wnd; uint32_t cwnd = flow->tx.cwnd ? flow->tx.cwnd : (flow->tx.mss ? flow->tx.mss : TCP_DEFAULT_MSS); uint32_t eff_wnd = wnd < cwnd ? wnd : cwnd; diff --git a/kernel/networking/transport_layer/tcp/tcp_utils.c b/kernel/networking/transport_layer/tcp/tcp_utils.c index 2becd325..ae89136c 100644 --- a/kernel/networking/transport_layer/tcp/tcp_utils.c +++ b/kernel/networking/transport_layer/tcp/tcp_utils.c @@ -1,5 +1,6 @@ #include "tcp_utils.h" #include "std/memory.h" +#include "tcp_internal.h" uint32_t tcp_calc_mss_for_l3(uint8_t l3_id, ip_version_t ver, const void *remote_ip){ uint32_t mtu = 1500; @@ -104,7 +105,7 @@ void tcp_parse_options(const uint8_t *opts, uint32_t len, tcp_parsed_opts_t *out uint32_t o = i + 2 + b*8; uint32_t left = rd_be32(&opts[o]); uint32_t right = rd_be32(&opts[o+4]); - if (right <= left) continue; + if (TCP_SEQ_LEQ(right, left)) continue; uint8_t n = out->sack_count; if (n >= TCP_SACK_MAX_BLOCKS) break; From 47638c321bf9c94c3a6192d6722f5b54a01b349b Mon Sep 17 00:00:00 2001 From: CodeAnarchist Date: Fri, 22 May 2026 20:34:00 +0200 Subject: [PATCH 13/39] [tcp] move resource accounting into tcp_limits --- .../networking/transport_layer/tcp/tcp_core.c | 171 ++++-------------- .../transport_layer/tcp/tcp_internal.h | 65 +------ .../transport_layer/tcp/tcp_limits.c | 104 +++++++++++ .../transport_layer/tcp/tcp_limits.h | 80 ++++++++ .../networking/transport_layer/tcp/tcp_rx.c | 3 +- .../transport_layer/tcp/tcp_timer.c | 60 +++--- .../networking/transport_layer/tcp/tcp_tx.c | 18 +- 7 files changed, 248 insertions(+), 253 deletions(-) create mode 100644 kernel/networking/transport_layer/tcp/tcp_limits.c create mode 100644 kernel/networking/transport_layer/tcp/tcp_limits.h diff --git a/kernel/networking/transport_layer/tcp/tcp_core.c b/kernel/networking/transport_layer/tcp/tcp_core.c index 545e7986..1a076885 100644 --- a/kernel/networking/transport_layer/tcp/tcp_core.c +++ b/kernel/networking/transport_layer/tcp/tcp_core.c @@ -13,118 +13,8 @@ tcp_flow_t *tcp_flows[MAX_TCP_FLOWS]; uint16_t tcp_active_flows[MAX_TCP_FLOWS]; uint16_t tcp_active_count; -uint32_t tcp_ooo_global_bytes; -uint32_t tcp_ooo_global_segs; -uint32_t tcp_tx_global_bytes; -tcp_stats_t tcp_stats; static uint32_t tcp_generation_next = 1; -//TODO these statics are small and i find them annoying but theyre good for making the code more readable -//maybe move them to a dedicated file? -uint32_t tcp_clamp_rcvbuf(uint32_t size) { - if (!size) size = TCP_DEFAULT_RCV_BUF; - if (size < TCP_RCV_BUF_MIN) size = TCP_RCV_BUF_MIN; - if (size > TCP_RCV_BUF_MAX) size = TCP_RCV_BUF_MAX; - return size; -} - -void tcp_account_ooo_remove(uint32_t bytes, uint32_t segs) { - if (tcp_ooo_global_bytes >= bytes) tcp_ooo_global_bytes -= bytes; - else tcp_ooo_global_bytes = 0; - if (tcp_ooo_global_segs >= segs) tcp_ooo_global_segs -= segs; - else tcp_ooo_global_segs = 0; -} - -void tcp_account_tx_remove(tcp_flow_t *flow, uint32_t bytes) { - if (!flow || !bytes) return; - if (flow->tx.queued_bytes >= bytes) flow->tx.queued_bytes -= bytes; - else flow->tx.queued_bytes = 0; - if (tcp_tx_global_bytes >= bytes) tcp_tx_global_bytes -= bytes; - else tcp_tx_global_bytes = 0; -} - -tcp_admit_result_t tcp_admit_ooo(tcp_flow_t *flow, uint32_t increase, uint32_t remaining_nodes) { - if (!flow) return TCP_ADMIT_OOO_FLOW_BYTES; - - uint32_t mss = flow->tx.mss ? flow->tx.mss : TCP_DEFAULT_MSS; - uint32_t limit = flow->rx.rcv_wnd_max >> 1; - uint32_t floor = mss * 4u; - - if (floor > TCP_REASS_MAX_BYTES / 2) floor = TCP_REASS_MAX_BYTES / 2; - if (limit > TCP_REASS_MAX_BYTES) limit = TCP_REASS_MAX_BYTES; - if (limit < floor) limit = floor; - if (limit > flow->rx.rcv_wnd_max) limit = flow->rx.rcv_wnd_max; - - uint32_t seg_increase = remaining_nodes >= flow->rx.reass_count ? 1u : 0; - - if (remaining_nodes >= TCP_REASS_MAX_SEGS) return TCP_ADMIT_OOO_FLOW_SEGS; - if (increase && flow->rx.rcv_ooo_used + increase > limit) return TCP_ADMIT_OOO_FLOW_BYTES; - if (increase && tcp_ooo_global_bytes + increase > TCP_REASS_GLOBAL_MAX_BYTES) return TCP_ADMIT_OOO_GLOBAL_BYTES; - if (seg_increase && tcp_ooo_global_segs + seg_increase > TCP_REASS_GLOBAL_MAX_SEGS) return TCP_ADMIT_OOO_GLOBAL_SEGS; - - return TCP_ADMIT_OK; -} - -tcp_admit_result_t tcp_admit_syn(uint8_t l3_id, uint16_t port, ip_version_t ver, const void *src_ip) { - uint32_t syn_total = 0; - uint32_t syn_listener = 0; - uint32_t syn_source = 0; - uint32_t timewait = 0; - uint32_t orphan = 0; - size_t ip_len = (size_t)(ver == IP_VER6 ? 16 : 4); - - for (int i = 0; i < MAX_TCP_FLOWS; i++) { - tcp_flow_t *f = tcp_flows[i]; - if (!f) continue; - - if (f->base.state == TCP_SYN_RECEIVED) { - syn_total++; - if (f->base.local_port == port && f->base.l3_id == l3_id) syn_listener++; - if (src_ip && f->base.local_port == port && f->base.l3_id == l3_id && f->base.remote.ver == ver && memcmp(f->base.remote.ip, src_ip, ip_len) == 0) syn_source++; - } else if (f->base.state == TCP_TIME_WAIT) timewait++; - else if (f->base.state != TCP_LISTEN && f->base.state != TCP_STATE_CLOSED && !f->base.local_port) orphan++; - } - - uint32_t score = (uint32_t)tcp_active_count * 4 + syn_total * 4 + timewait + orphan * 4 + (tcp_ooo_global_bytes >> 12) + (tcp_tx_global_bytes >> 12); - - if (tcp_active_count >= MAX_TCP_FLOWS) return TCP_ADMIT_FLOW_TABLE_FULL; - if (syn_total >= TCP_SYN_RECV_MAX_GLOBAL) return TCP_ADMIT_SYN_GLOBAL; - if (syn_listener >= TCP_SYN_RECV_MAX_LISTENER) return TCP_ADMIT_SYN_LISTENER; - if (syn_source >= TCP_SYN_RECV_MAX_SOURCE) return TCP_ADMIT_SYN_SOURCE; - if (orphan >= TCP_ORPHAN_MAX_GLOBAL) return TCP_ADMIT_ORPHAN_LIMIT; - if (score >= TCP_RESOURCE_BUDGET) return TCP_ADMIT_RESOURCE_BUDGET; - - return TCP_ADMIT_OK; -} - -void tcp_enter_time_wait(tcp_flow_t *flow) { - if (!flow) return; - - uint32_t timewait = 0; - int oldest = -1; - uint32_t oldest_ms = 0; - - for (int i = 0; i < MAX_TCP_FLOWS; i++) { - tcp_flow_t *f = tcp_flows[i]; - if (!f || f->base.state != TCP_TIME_WAIT) continue; - timewait++; - if ((oldest < 0 || f->timer.time_wait_ms > oldest_ms) && f != flow) { - oldest = i; - oldest_ms = f->timer.time_wait_ms; - } - } - - if (timewait >= TCP_TIMEWAIT_MAX_GLOBAL && oldest >= 0) { - tcp_stats.timewait_reap_oldest++; - tcp_free_flow(oldest); - } - - flow->base.state = TCP_TIME_WAIT; - tcp_release_io_buffers(flow); - flow->timer.time_wait_ms = 0; - tcp_daemon_kick(); -} - int find_flow(uint16_t local_port, ip_version_t ver, const void *local_ip, const void *remote_ip, uint16_t remote_port){ for (uint16_t n = 0; n < tcp_active_count; n++){ int i = tcp_active_flows[n]; @@ -197,6 +87,8 @@ void tcp_release_io_buffers(tcp_flow_t *f) { memset(s, 0, sizeof(*s)); } + f->tx.queued_bytes = 0; + if (f->tx.nagle_buf) release((void*)f->tx.nagle_buf); f->tx.nagle_buf = 0; f->tx.nagle_len = 0; @@ -219,6 +111,34 @@ void tcp_release_io_buffers(tcp_flow_t *f) { memset(f->rx.reass, 0, sizeof(f->rx.reass)); } +void tcp_enter_time_wait(tcp_flow_t *flow) { + if (!flow) return; + + uint32_t timewait = 0; + int oldest = -1; + uint32_t oldest_ms = 0; + + for (int i = 0; i < MAX_TCP_FLOWS; i++) { + tcp_flow_t *f = tcp_flows[i]; + if (!f || f->base.state != TCP_TIME_WAIT) continue; + timewait++; + if ((oldest < 0 || f->timer.time_wait_ms > oldest_ms) && f != flow) { + oldest = i; + oldest_ms = f->timer.time_wait_ms; + } + } + + if (timewait >= TCP_TIMEWAIT_MAX_GLOBAL && oldest >= 0) { + tcp_stats.timewait_reap_oldest++; + tcp_free_flow(oldest); + } + + flow->base.state = TCP_TIME_WAIT; + tcp_release_io_buffers(flow); + flow->timer.time_wait_ms = 0; + tcp_daemon_kick(); +} + tcp_flow_t *tcp_alloc_flow(void){ for (int i = 0; i < MAX_TCP_FLOWS; i++){ if (tcp_flows[i]) continue; @@ -274,36 +194,7 @@ void tcp_free_flow(int idx) { f->base.active_pos = 0; } - for (int i = 0; i < TCP_MAX_TX_SEGS; i++) { - tcp_tx_seg_t *s =&f->tx.txq[i]; - if (s->used && s->buf && s->len) { - tcp_account_tx_remove(f, (uint32_t)s->len); - release((void*)s->buf); - } - memset(s, 0, sizeof(*s)); - } - - if (f->tx.nagle_buf) release((void*)f->tx.nagle_buf); - f->tx.nagle_buf = 0; - f->tx.nagle_len = 0; - f->tx.nagle_cap = 0; - f->tx.nagle_timer_ms = 0; - f->tx.nagle_flushing = 0; - f->tx.nagle_appending = 0; - - if (f->rx.reass_count || f->rx.rcv_ooo_used) tcp_account_ooo_remove(f->rx.rcv_ooo_used, f->rx.reass_count); - if (f->rx.rcv_buf) release((void*)f->rx.rcv_buf); - f->rx.rcv_buf = 0; - f->rx.rcv_base = f->rx.rcv_nxt; - f->rx.rcv_data_nxt = f->rx.rcv_nxt; - f->rx.rcv_ooo_used = 0; - f->rx.rcv_wnd = 0; - f->rx.rcv_adv_edge = f->rx.rcv_nxt; - f->rx.fin_pending = 0; - f->base.ctx.window = 0; - f->rx.reass_count = 0; - - memset(f->rx.reass, 0, sizeof(f->rx.reass)); + tcp_release_io_buffers(f); memset(f, 0, sizeof(*f)); release(f); tcp_flows[idx] = NULL; diff --git a/kernel/networking/transport_layer/tcp/tcp_internal.h b/kernel/networking/transport_layer/tcp/tcp_internal.h index e191a1d5..ad62cdf6 100644 --- a/kernel/networking/transport_layer/tcp/tcp_internal.h +++ b/kernel/networking/transport_layer/tcp/tcp_internal.h @@ -12,28 +12,13 @@ #include "math/rng.h" #include "syscalls/syscalls.h" #include "tcp_utils.h" +#include "tcp_limits.h" #ifdef __cplusplus extern "C" { #endif -#define TCP_REASS_MAX_SEGS 32 -#define TCP_REASS_MAX_BYTES (64u * 1024u) -#define TCP_REASS_GLOBAL_MAX_SEGS 1024u -#define TCP_REASS_GLOBAL_MAX_BYTES (2 * 1024u * 1024u) #define TCP_DEFAULT_MSS 1460 -#define TCP_RCV_BUF_MIN (8u * 1024u) -#define TCP_DEFAULT_RCV_BUF (256u * 1024u) -#define TCP_RCV_BUF_MAX (2u * 1024u * 1024u) -#define TCP_TX_MAX_BYTES_PER_FLOW (512u * 1024u) -#define TCP_TX_MAX_BYTES_GLOBAL (4u * 1024u * 1024u) -#define TCP_TX_CONTROL_RESERVE_SEGS 2u -#define TCP_SYN_RECV_MAX_GLOBAL (MAX_TCP_FLOWS / 4u) -#define TCP_SYN_RECV_MAX_LISTENER 32u -#define TCP_SYN_RECV_MAX_SOURCE 8u -#define TCP_TIMEWAIT_MAX_GLOBAL 128u -#define TCP_ORPHAN_MAX_GLOBAL 32u -#define TCP_RESOURCE_BUDGET 4096u #define TCP_PERSIST_PROBE_BUFSZ 1 #define TCP_SEQ_LT(a,b) ((int32_t)((uint32_t)(a) - (uint32_t)(b)) < 0) @@ -159,7 +144,7 @@ typedef struct { uint8_t dontfrag; } tcp_flow_ip_t; -typedef struct { +typedef struct tcp_flow { tcp_flow_base_t base; tcp_flow_tx_t tx; tcp_flow_rx_t rx; @@ -167,54 +152,10 @@ typedef struct { tcp_flow_ip_t ip; } tcp_flow_t; -typedef enum { - TCP_ADMIT_OK = 0, - TCP_ADMIT_OOO_FLOW_BYTES, - TCP_ADMIT_OOO_FLOW_SEGS, - TCP_ADMIT_OOO_GLOBAL_BYTES, - TCP_ADMIT_OOO_GLOBAL_SEGS, - TCP_ADMIT_SYN_GLOBAL, - TCP_ADMIT_SYN_LISTENER, - TCP_ADMIT_SYN_SOURCE, - TCP_ADMIT_ORPHAN_LIMIT, - TCP_ADMIT_TX_FLOW_BYTES, - TCP_ADMIT_TX_FLOW_SEGS, - TCP_ADMIT_TX_GLOBAL_BYTES, - TCP_ADMIT_RESOURCE_BUDGET, - TCP_ADMIT_FLOW_TABLE_FULL -} tcp_admit_result_t; - -typedef struct { - uint64_t ooo_drop_flow_bytes; - uint64_t ooo_drop_flow_segs; - uint64_t ooo_drop_global_bytes; - uint64_t ooo_drop_global_segs; - uint64_t syn_drop_global; - uint64_t syn_drop_listener; - uint64_t syn_drop_source; - uint64_t acceptq_drop_full; - uint64_t timewait_reap_oldest; - uint64_t orphan_drop_global; - uint64_t tx_block_flow_bytes; - uint64_t tx_block_flow_segs; - uint64_t tx_block_global_bytes; - uint64_t resource_budget_drop; - uint64_t flow_table_full; -} tcp_stats_t; - extern tcp_flow_t *tcp_flows[MAX_TCP_FLOWS]; extern uint16_t tcp_active_flows[MAX_TCP_FLOWS]; extern uint16_t tcp_active_count; -extern uint32_t tcp_ooo_global_bytes; -extern uint32_t tcp_ooo_global_segs; -extern uint32_t tcp_tx_global_bytes; -extern tcp_stats_t tcp_stats; - -uint32_t tcp_clamp_rcvbuf(uint32_t size); -tcp_admit_result_t tcp_admit_syn(uint8_t l3_id, uint16_t port, ip_version_t ver, const void *src_ip); -tcp_admit_result_t tcp_admit_ooo(tcp_flow_t *flow, uint32_t increase, uint32_t remaining_nodes); -void tcp_account_ooo_remove(uint32_t bytes, uint32_t segs); -void tcp_account_tx_remove(tcp_flow_t *flow, uint32_t bytes); + void tcp_enter_time_wait(tcp_flow_t *flow); tcp_flow_t *tcp_alloc_flow(void); diff --git a/kernel/networking/transport_layer/tcp/tcp_limits.c b/kernel/networking/transport_layer/tcp/tcp_limits.c new file mode 100644 index 00000000..4e1d1632 --- /dev/null +++ b/kernel/networking/transport_layer/tcp/tcp_limits.c @@ -0,0 +1,104 @@ +#include "tcp_internal.h" +#include "std/memory.h" + +uint32_t tcp_ooo_global_bytes; +uint32_t tcp_ooo_global_segs; +uint32_t tcp_tx_global_bytes; +tcp_stats_t tcp_stats; + +uint32_t tcp_clamp_rcvbuf(uint32_t size) { + if (!size) size = TCP_DEFAULT_RCV_BUF; + if (size < TCP_RCV_BUF_MIN) size = TCP_RCV_BUF_MIN; + if (size > TCP_RCV_BUF_MAX) size = TCP_RCV_BUF_MAX; + return size; +} + +void tcp_account_ooo_add(uint32_t bytes, uint32_t segs) { + tcp_ooo_global_bytes += bytes; + tcp_ooo_global_segs += segs; +} + +void tcp_account_ooo_remove(uint32_t bytes, uint32_t segs) { + if (tcp_ooo_global_bytes >= bytes) tcp_ooo_global_bytes -= bytes; + else tcp_ooo_global_bytes = 0; + if (tcp_ooo_global_segs >= segs) tcp_ooo_global_segs -= segs; + else tcp_ooo_global_segs = 0; +} + +void tcp_account_tx_add(tcp_flow_t *flow, uint32_t bytes) { + if (!flow || !bytes) return; + flow->tx.queued_bytes += bytes; + tcp_tx_global_bytes += bytes; +} + +void tcp_account_tx_remove(tcp_flow_t *flow, uint32_t bytes) { + if (!flow || !bytes) return; + if (flow->tx.queued_bytes >= bytes) flow->tx.queued_bytes -= bytes; + else flow->tx.queued_bytes = 0; + if (tcp_tx_global_bytes >= bytes) tcp_tx_global_bytes -= bytes; + else tcp_tx_global_bytes = 0; +} + +tcp_admit_result_t tcp_admit_ooo(tcp_flow_t *flow, uint32_t increase, uint32_t remaining_nodes) { + if (!flow) return TCP_ADMIT_OOO_FLOW_BYTES; + + uint32_t mss = flow->tx.mss ? flow->tx.mss : TCP_DEFAULT_MSS; + uint32_t limit = flow->rx.rcv_wnd_max >> 1; + uint32_t floor = mss * 4u; + + if (floor > TCP_REASS_MAX_BYTES / 2u) floor = TCP_REASS_MAX_BYTES / 2u; + if (limit > TCP_REASS_MAX_BYTES) limit = TCP_REASS_MAX_BYTES; + if (limit < floor) limit = floor; + if (limit > flow->rx.rcv_wnd_max) limit = flow->rx.rcv_wnd_max; + + uint32_t seg_increase = remaining_nodes >= flow->rx.reass_count ? 1u : 0; + + if (remaining_nodes >= TCP_REASS_MAX_SEGS) return TCP_ADMIT_OOO_FLOW_SEGS; + if (increase && flow->rx.rcv_ooo_used + increase > limit) return TCP_ADMIT_OOO_FLOW_BYTES; + if (increase && tcp_ooo_global_bytes + increase > TCP_REASS_GLOBAL_MAX_BYTES) return TCP_ADMIT_OOO_GLOBAL_BYTES; + if (seg_increase && tcp_ooo_global_segs + seg_increase > TCP_REASS_GLOBAL_MAX_SEGS) return TCP_ADMIT_OOO_GLOBAL_SEGS; + + return TCP_ADMIT_OK; +} + +tcp_admit_result_t tcp_admit_syn(uint8_t l3_id, uint16_t port, ip_version_t ver, const void *src_ip) { + uint32_t syn_total = 0; + uint32_t syn_listener = 0; + uint32_t syn_source = 0; + uint32_t timewait = 0; + uint32_t orphan = 0; + size_t ip_len = (size_t)(ver == IP_VER6 ? 16 : 4); + + for (int i = 0; i < MAX_TCP_FLOWS; i++) { + tcp_flow_t *f = tcp_flows[i]; + if (!f) continue; + + if (f->base.state == TCP_SYN_RECEIVED) { + syn_total++; + if (f->base.local_port == port && f->base.l3_id == l3_id) syn_listener++; + if (src_ip && f->base.local_port == port && f->base.l3_id == l3_id && f->base.remote.ver == ver && memcmp(f->base.remote.ip, src_ip, ip_len) == 0) syn_source++; + } else if (f->base.state == TCP_TIME_WAIT) timewait++; + else if (f->base.state != TCP_LISTEN && f->base.state != TCP_STATE_CLOSED && !f->base.local_port) orphan++; + } + + uint32_t score = (uint32_t)tcp_active_count * 4 + syn_total * 4 + timewait + orphan * 4 + (tcp_ooo_global_bytes >> 12) + (tcp_tx_global_bytes >> 12); + + if (tcp_active_count >= MAX_TCP_FLOWS) return TCP_ADMIT_FLOW_TABLE_FULL; + if (syn_total >= TCP_SYN_RECV_MAX_GLOBAL) return TCP_ADMIT_SYN_GLOBAL; + if (syn_listener >= TCP_SYN_RECV_MAX_LISTENER) return TCP_ADMIT_SYN_LISTENER; + if (syn_source >= TCP_SYN_RECV_MAX_SOURCE) return TCP_ADMIT_SYN_SOURCE; + if (orphan >= TCP_ORPHAN_MAX_GLOBAL) return TCP_ADMIT_ORPHAN_LIMIT; + if (score >= TCP_RESOURCE_BUDGET) return TCP_ADMIT_RESOURCE_BUDGET; + + return TCP_ADMIT_OK; +} + +tcp_admit_result_t tcp_admit_tx(tcp_flow_t *flow, uint32_t bytes, uint32_t free_slots) { + if (!flow) return TCP_ADMIT_TX_FLOW_BYTES; + if (free_slots <= TCP_TX_CONTROL_RESERVE_SEGS) return TCP_ADMIT_TX_FLOW_SEGS; + if (flow->tx.queued_bytes >= TCP_TX_MAX_BYTES_PER_FLOW) return TCP_ADMIT_TX_FLOW_BYTES; + if (tcp_tx_global_bytes >= TCP_TX_MAX_BYTES_GLOBAL) return TCP_ADMIT_TX_GLOBAL_BYTES; + if (bytes && flow->tx.queued_bytes + bytes > TCP_TX_MAX_BYTES_PER_FLOW) return TCP_ADMIT_TX_FLOW_BYTES; + if (bytes && tcp_tx_global_bytes + bytes > TCP_TX_MAX_BYTES_GLOBAL) return TCP_ADMIT_TX_GLOBAL_BYTES; + return TCP_ADMIT_OK; +} diff --git a/kernel/networking/transport_layer/tcp/tcp_limits.h b/kernel/networking/transport_layer/tcp/tcp_limits.h new file mode 100644 index 00000000..48407754 --- /dev/null +++ b/kernel/networking/transport_layer/tcp/tcp_limits.h @@ -0,0 +1,80 @@ +#pragma once + +#include "../tcp.h" +#include "types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define TCP_REASS_MAX_SEGS 32 +#define TCP_REASS_MAX_BYTES (64u * 1024u) +#define TCP_REASS_GLOBAL_MAX_SEGS 1024u +#define TCP_REASS_GLOBAL_MAX_BYTES (2u * 1024u * 1024u) +#define TCP_RCV_BUF_MIN (8u * 1024u) +#define TCP_DEFAULT_RCV_BUF (256u * 1024u) +#define TCP_RCV_BUF_MAX (2u * 1024u * 1024u) +#define TCP_TX_MAX_BYTES_PER_FLOW (512u * 1024u) +#define TCP_TX_MAX_BYTES_GLOBAL (4u * 1024u * 1024u) +#define TCP_TX_CONTROL_RESERVE_SEGS 2u +#define TCP_SYN_RECV_MAX_GLOBAL (MAX_TCP_FLOWS / 4u) +#define TCP_SYN_RECV_MAX_LISTENER 32u +#define TCP_SYN_RECV_MAX_SOURCE 8u +#define TCP_TIMEWAIT_MAX_GLOBAL 128u +#define TCP_ORPHAN_MAX_GLOBAL 32u +#define TCP_RESOURCE_BUDGET 4096u + +typedef struct tcp_flow tcp_flow_t; + +typedef enum { + TCP_ADMIT_OK = 0, + TCP_ADMIT_OOO_FLOW_BYTES, + TCP_ADMIT_OOO_FLOW_SEGS, + TCP_ADMIT_OOO_GLOBAL_BYTES, + TCP_ADMIT_OOO_GLOBAL_SEGS, + TCP_ADMIT_SYN_GLOBAL, + TCP_ADMIT_SYN_LISTENER, + TCP_ADMIT_SYN_SOURCE, + TCP_ADMIT_ORPHAN_LIMIT, + TCP_ADMIT_TX_FLOW_BYTES, + TCP_ADMIT_TX_FLOW_SEGS, + TCP_ADMIT_TX_GLOBAL_BYTES, + TCP_ADMIT_RESOURCE_BUDGET, + TCP_ADMIT_FLOW_TABLE_FULL +} tcp_admit_result_t; + +typedef struct { + uint64_t ooo_drop_flow_bytes; + uint64_t ooo_drop_flow_segs; + uint64_t ooo_drop_global_bytes; + uint64_t ooo_drop_global_segs; + uint64_t syn_drop_global; + uint64_t syn_drop_listener; + uint64_t syn_drop_source; + uint64_t acceptq_drop_full; + uint64_t timewait_reap_oldest; + uint64_t orphan_drop_global; + uint64_t tx_block_flow_bytes; + uint64_t tx_block_flow_segs; + uint64_t tx_block_global_bytes; + uint64_t resource_budget_drop; + uint64_t flow_table_full; +} tcp_stats_t; + +extern uint32_t tcp_ooo_global_bytes; +extern uint32_t tcp_ooo_global_segs; +extern uint32_t tcp_tx_global_bytes; +extern tcp_stats_t tcp_stats; + +uint32_t tcp_clamp_rcvbuf(uint32_t size); +tcp_admit_result_t tcp_admit_syn(uint8_t l3_id, uint16_t port, ip_version_t ver, const void *src_ip); +tcp_admit_result_t tcp_admit_ooo(tcp_flow_t *flow, uint32_t increase, uint32_t remaining_nodes); +tcp_admit_result_t tcp_admit_tx(tcp_flow_t *flow, uint32_t bytes, uint32_t free_slots); +void tcp_account_ooo_add(uint32_t bytes, uint32_t segs); +void tcp_account_ooo_remove(uint32_t bytes, uint32_t segs); +void tcp_account_tx_add(tcp_flow_t *flow, uint32_t bytes); +void tcp_account_tx_remove(tcp_flow_t *flow, uint32_t bytes); + +#ifdef __cplusplus +} +#endif diff --git a/kernel/networking/transport_layer/tcp/tcp_rx.c b/kernel/networking/transport_layer/tcp/tcp_rx.c index 7b2d4c70..71ff4e67 100644 --- a/kernel/networking/transport_layer/tcp/tcp_rx.c +++ b/kernel/networking/transport_layer/tcp/tcp_rx.c @@ -1073,8 +1073,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a flow->rx.reass[pos_idx].end = merged_end; flow->rx.reass_count++; flow->rx.rcv_ooo_used += merged_end - merged_start; - tcp_ooo_global_bytes += merged_end - merged_start; - tcp_ooo_global_segs++; + tcp_account_ooo_add(merged_end - merged_start, 1); flow->rx.sack_recent_left = merged_start; flow->rx.sack_recent_right = merged_end; tcp_calc_adv_wnd_field(flow, 1); diff --git a/kernel/networking/transport_layer/tcp/tcp_timer.c b/kernel/networking/transport_layer/tcp/tcp_timer.c index 395fb848..540e0649 100644 --- a/kernel/networking/transport_layer/tcp/tcp_timer.c +++ b/kernel/networking/transport_layer/tcp/tcp_timer.c @@ -3,6 +3,27 @@ #include "exceptions/irq.h" static volatile int tcp_daemon_running = 0; + +static void tcp_timer_send_ack_segment(tcp_flow_t *f, uint32_t seq, const uint8_t *payload, uint16_t payload_len) { + tcp_hdr_t hdr; + hdr.src_port = bswap16(f->base.local_port); + hdr.dst_port = bswap16(f->base.remote.port); + hdr.sequence = bswap32(seq); + hdr.ack = bswap32(f->base.ctx.ack); + hdr.flags = (uint8_t)(1u << ACK_F); + hdr.window = tcp_calc_adv_wnd_field(f, 1); + hdr.urgent_ptr = 0; + + if (f->base.local.ver == IP_VER4) { + ipv4_tx_opts_t tx; + tcp_build_tx_opts_from_local_v4(f->base.local.ip, &tx); + (void)tcp_send_segment(IP_VER4, f->base.local.ip, f->base.remote.ip, &hdr, NULL, 0, payload, payload_len, (const ip_tx_opts_t *)&tx, f->ip.ttl, f->ip.dontfrag); + } else if (f->base.local.ver == IP_VER6) { + ipv6_tx_opts_t tx; + tcp_build_tx_opts_from_local_v6(f->base.local.ip, &tx); + (void)tcp_send_segment(IP_VER6, f->base.local.ip, f->base.remote.ip, &hdr, NULL, 0, payload, payload_len, (const ip_tx_opts_t *)&tx, f->ip.ttl, f->ip.dontfrag); + } +} //TODO events void tcp_daemon_kick(void) { if(!tcp_has_pending_timers()) return; @@ -90,26 +111,9 @@ void tcp_tick_all(uint32_t elapsed_ms) { if (!f->tx.fin_tx_pending && f->timer.keepalive_on && f->base.state == TCP_ESTABLISHED && f->timer.keepalive_ms) { f->timer.keepalive_idle_ms += elapsed_ms; if (f->timer.keepalive_idle_ms >= f->timer.keepalive_ms) { - tcp_hdr_t hdr; - hdr.src_port = bswap16(f->base.local_port); - hdr.dst_port = bswap16(f->base.remote.port); uint32_t seq = f->tx.snd_nxt; if (seq) seq -= 1; - hdr.sequence = bswap32(seq); - hdr.ack = bswap32(f->base.ctx.ack); - hdr.flags = (uint8_t)(1u << ACK_F); - hdr.window = tcp_calc_adv_wnd_field(f, 1); - hdr.urgent_ptr = 0; - - if (f->base.local.ver == IP_VER4) { - ipv4_tx_opts_t tx; - tcp_build_tx_opts_from_local_v4(f->base.local.ip, &tx); - (void)tcp_send_segment(IP_VER4, f->base.local.ip, f->base.remote.ip, &hdr, NULL, 0, NULL, 0, (const ip_tx_opts_t *)&tx, f->ip.ttl, f->ip.dontfrag); - } else if (f->base.local.ver == IP_VER6) { - ipv6_tx_opts_t tx; - tcp_build_tx_opts_from_local_v6(f->base.local.ip, &tx); - (void)tcp_send_segment(IP_VER6, f->base.local.ip, f->base.remote.ip, &hdr, NULL, 0, NULL, 0, (const ip_tx_opts_t *)&tx, f->ip.ttl, f->ip.dontfrag); - } + tcp_timer_send_ack_segment(f, seq, NULL, 0); f->timer.keepalive_idle_ms = 0; } } @@ -130,10 +134,6 @@ void tcp_tick_all(uint32_t elapsed_ms) { } tcp_tx_seg_t *best = tcp_find_first_unacked(f); - tcp_hdr_t hdr; - hdr.src_port = bswap16(f->base.local_port); - hdr.dst_port = bswap16(f->base.remote.port); - uint8_t payload[1]; const uint8_t *pp = NULL; uint16_t pl = 0; @@ -147,21 +147,7 @@ void tcp_tick_all(uint32_t elapsed_ms) { pl = 1; } - hdr.sequence = bswap32(probe_seq); - hdr.ack = bswap32(f->base.ctx.ack); - hdr.flags = (uint8_t)(1u << ACK_F); - hdr.window = tcp_calc_adv_wnd_field(f, 1); - hdr.urgent_ptr = 0; - - if (f->base.local.ver == IP_VER4) { - ipv4_tx_opts_t tx; - tcp_build_tx_opts_from_local_v4(f->base.local.ip, &tx); - (void)tcp_send_segment(IP_VER4, f->base.local.ip, f->base.remote.ip, &hdr, NULL, 0, pp, pl, (const ip_tx_opts_t *)&tx, f->ip.ttl, f->ip.dontfrag); - } else if (f->base.local.ver == IP_VER6) { - ipv6_tx_opts_t tx; - tcp_build_tx_opts_from_local_v6(f->base.local.ip, &tx); - (void)tcp_send_segment(IP_VER6, f->base.local.ip, f->base.remote.ip, &hdr, NULL, 0, pp, pl, (const ip_tx_opts_t *)&tx, f->ip.ttl, f->ip.dontfrag); - } + tcp_timer_send_ack_segment(f, probe_seq, pp, pl); if (f->timer.persist_probe_cnt < UINT8_MAX) f->timer.persist_probe_cnt++; f->timer.persist_timer_ms = 0; diff --git a/kernel/networking/transport_layer/tcp/tcp_tx.c b/kernel/networking/transport_layer/tcp/tcp_tx.c index 4cec6c80..15e08fee 100644 --- a/kernel/networking/transport_layer/tcp/tcp_tx.c +++ b/kernel/networking/transport_layer/tcp/tcp_tx.c @@ -83,16 +83,11 @@ static uint64_t tcp_emit_data(tcp_flow_t *flow, const uint8_t *payload, uint64_t while (remaining > 0 && can_send > 0) { uint32_t free_slots = 0; for (int i = 0; i < TCP_MAX_TX_SEGS; i++) if (!flow->tx.txq[i].used) free_slots++; - if (free_slots <= TCP_TX_CONTROL_RESERVE_SEGS) { - tcp_stats.tx_block_flow_segs++; - break; - } - if (flow->tx.queued_bytes >= TCP_TX_MAX_BYTES_PER_FLOW) { - tcp_stats.tx_block_flow_bytes++; - break; - } - if (tcp_tx_global_bytes >= TCP_TX_MAX_BYTES_GLOBAL) { - tcp_stats.tx_block_global_bytes++; + tcp_admit_result_t tx_admit = tcp_admit_tx(flow, 0, free_slots); + if (tx_admit != TCP_ADMIT_OK) { + if (tx_admit == TCP_ADMIT_TX_FLOW_SEGS) tcp_stats.tx_block_flow_segs++; + else if (tx_admit == TCP_ADMIT_TX_FLOW_BYTES) tcp_stats.tx_block_flow_bytes++; + else if (tx_admit == TCP_ADMIT_TX_GLOBAL_BYTES) tcp_stats.tx_block_global_bytes++; break; } @@ -133,8 +128,7 @@ static uint64_t tcp_emit_data(tcp_flow_t *flow, const uint8_t *payload, uint64_t break; } - flow->tx.queued_bytes += (uint32_t)seg_len; - tcp_tx_global_bytes += (uint32_t)seg_len; + tcp_account_tx_add(flow, (uint32_t)seg_len); flow->tx.snd_nxt += seg_len; sent_bytes += seg_len; remaining -= seg_len; From 0742263e6729597d71cec67dfbd1df096d7acde1 Mon Sep 17 00:00:00 2001 From: CodeAnarchist Date: Sat, 23 May 2026 23:53:56 +0200 Subject: [PATCH 14/39] [neigh/virtio] improve tx status and make arp/ndp non blocking --- .../drivers/loopback/loopback_driver.cpp | 8 +- .../drivers/loopback/loopback_driver.hpp | 2 +- kernel/networking/drivers/net_driver.hpp | 8 +- .../drivers/virtio_net_pci/virtio_net_pci.cpp | 16 +- .../drivers/virtio_net_pci/virtio_net_pci.hpp | 2 +- kernel/networking/internet_layer/ipv4.c | 18 +- kernel/networking/internet_layer/ipv6.c | 10 +- kernel/networking/link_layer/arp.c | 209 ++++++++++++++---- kernel/networking/link_layer/arp.h | 19 +- kernel/networking/link_layer/ndp.c | 206 ++++++++--------- kernel/networking/link_layer/ndp.h | 7 +- kernel/networking/network_dispatch.cpp | 5 +- kernel/virtio/virtio_pci.c | 6 +- 13 files changed, 337 insertions(+), 179 deletions(-) diff --git a/kernel/networking/drivers/loopback/loopback_driver.cpp b/kernel/networking/drivers/loopback/loopback_driver.cpp index 072a93bd..0495d402 100644 --- a/kernel/networking/drivers/loopback/loopback_driver.cpp +++ b/kernel/networking/drivers/loopback/loopback_driver.cpp @@ -26,13 +26,13 @@ netpkt_t* LoopbackDriver::handle_receive_packet(){ void LoopbackDriver::enable_verbose(){} -bool LoopbackDriver::send_packet(netpkt_t* packet){ - if (!packet || !netpkt_len(packet)) return false; +netdev_tx_result_t LoopbackDriver::send_packet(netpkt_t* packet){ + if (!packet || !netpkt_len(packet)) return NETDEV_TX_DROP; uint16_t next = (uint16_t)((rx_tail + 1) & 255); - if (next == rx_head)return false; + if (next == rx_head)return NETDEV_TX_BUSY; rxq[rx_tail] = packet; rx_tail = next; - return true; + return NETDEV_TX_OK; } void LoopbackDriver::get_mac(uint8_t out_mac[6]) const{ diff --git a/kernel/networking/drivers/loopback/loopback_driver.hpp b/kernel/networking/drivers/loopback/loopback_driver.hpp index 00cd8f00..5e7076a8 100644 --- a/kernel/networking/drivers/loopback/loopback_driver.hpp +++ b/kernel/networking/drivers/loopback/loopback_driver.hpp @@ -11,7 +11,7 @@ class LoopbackDriver : public NetDriver { bool init_at(uint64_t pci_addr, uint32_t irq_base_vector) override; netpkt_t* handle_receive_packet() override; void enable_verbose() override; - bool send_packet(netpkt_t* packet) override; + netdev_tx_result_t send_packet(netpkt_t* packet) override; void get_mac(uint8_t out_mac[6]) const override; uint16_t get_mtu() const override; uint16_t get_header_size() const override; diff --git a/kernel/networking/drivers/net_driver.hpp b/kernel/networking/drivers/net_driver.hpp index d8baa8d5..6f259baa 100644 --- a/kernel/networking/drivers/net_driver.hpp +++ b/kernel/networking/drivers/net_driver.hpp @@ -3,6 +3,12 @@ #include "net/network_types.h" #include "networking/netpkt.h" +typedef enum { + NETDEV_TX_OK = 0, + NETDEV_TX_BUSY = 1, + NETDEV_TX_DROP = 2 +} netdev_tx_result_t; + class NetDriver { public: NetDriver() = default; @@ -14,7 +20,7 @@ class NetDriver { virtual void flush_rx(){} virtual void flush_tx(){} virtual void enable_verbose() = 0; - virtual bool send_packet(netpkt_t* packet) = 0; + virtual netdev_tx_result_t send_packet(netpkt_t* packet) = 0; 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; 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 9800d2e0..3f53bf6f 100644 --- a/kernel/networking/drivers/virtio_net_pci/virtio_net_pci.cpp +++ b/kernel/networking/drivers/virtio_net_pci/virtio_net_pci.cpp @@ -389,9 +389,9 @@ void VirtioNetDriver::handle_sent_packet(){ } } -bool VirtioNetDriver::send_packet(netpkt_t* packet){ - if (!packet || !netpkt_len(packet)) return false; - if (!tx_qsz || !tx_desc || !tx_avail || !tx_used || !tx_pending) return false; +netdev_tx_result_t VirtioNetDriver::send_packet(netpkt_t* packet){ + if (!packet || !netpkt_len(packet)) return NETDEV_TX_DROP; + if (!tx_qsz || !tx_desc || !tx_avail || !tx_used || !tx_pending) return NETDEV_TX_DROP; handle_sent_packet(); @@ -401,19 +401,19 @@ bool VirtioNetDriver::send_packet(netpkt_t* packet){ uint16_t current = (tx_next_desc + i) % tx_qsz; if (tx_pending[current]) continue; desc_index = current; - tx_next_desc = (current + 1) % tx_qsz; + tx_next_desc = (uint16_t)((current + 1) % tx_qsz); found = true; break; } if (!found) { kprintfv("[virtio-net] tx queue full len=%u", (unsigned)netpkt_len(packet)); - return false; + return NETDEV_TX_BUSY; } - if (!netpkt_ensure_headroom(packet, header_size)) return false; + if (!netpkt_ensure_headroom(packet, header_size)) return NETDEV_TX_DROP; void* hdr = netpkt_push(packet, header_size); - if (!hdr) return false; + if (!hdr) return NETDEV_TX_DROP; memset(hdr, 0, (size_t)header_size); tx_pending[desc_index] = packet; @@ -431,7 +431,7 @@ bool VirtioNetDriver::send_packet(netpkt_t* packet){ tx_notify_pending = true; kprintfv("[virtio-net] tx queued desc=%u len=%u", desc_index,(unsigned)netpkt_len(packet)); - return true; + return NETDEV_TX_OK; } void VirtioNetDriver::flush_tx() { 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 b563cdd0..db7e541a 100644 --- a/kernel/networking/drivers/virtio_net_pci/virtio_net_pci.hpp +++ b/kernel/networking/drivers/virtio_net_pci/virtio_net_pci.hpp @@ -67,7 +67,7 @@ class VirtioNetDriver : public NetDriver { void handle_sent_packet() override; void flush_rx() override; void flush_tx() override; - bool send_packet(netpkt_t* packet) override; + netdev_tx_result_t send_packet(netpkt_t* packet) override; private: friend void virtio_net_rx_free(void* ctx, uintptr_t base, uint32_t alloc_size); diff --git a/kernel/networking/internet_layer/ipv4.c b/kernel/networking/internet_layer/ipv4.c index 5446c88b..7b8c2575 100644 --- a/kernel/networking/internet_layer/ipv4.c +++ b/kernel/networking/internet_layer/ipv4.c @@ -263,6 +263,7 @@ bool ipv4_send_packet(uint32_t dst_ip, uint8_t proto, netpkt_t* pkt, const ipv4_ uint8_t dst_mac[6]; bool is_dbcast = false; + bool need_arp = false; l2_interface_t* l2 = l2_interface_find_by_index(ifx); if (l2) { for (int s = 0; s < MAX_IPV4_PER_INTERFACE; ++s) { @@ -272,18 +273,10 @@ bool ipv4_send_packet(uint32_t dst_ip, uint8_t proto, netpkt_t* pkt, const ipv4_ } } - if (is_dbcast) { - memset(dst_mac, 0xFF, 6); - } else if (ipv4_is_multicast(dst_ip)) { - ipv4_mcast_to_mac(dst_ip, dst_mac); - } else { - if (l2 && l2->kind == NET_IFK_LOCALHOST) { - memset(dst_mac, 0, 6); - } else if (!arp_resolve_on(ifx, nh, dst_mac, 1000)) { - netpkt_unref(pkt); - return false; - } - } + if (is_dbcast) memset(dst_mac, 0xFF, 6); + else if (ipv4_is_multicast(dst_ip)) ipv4_mcast_to_mac(dst_ip, dst_mac); + else if (l2 && l2->kind == NET_IFK_LOCALHOST) memset(dst_mac, 0, 6); + else need_arp = true; uint16_t mtu = 1500; if (l2) { @@ -327,6 +320,7 @@ bool ipv4_send_packet(uint32_t dst_ip, uint8_t proto, netpkt_t* pkt, const ipv4_ ip->header_checksum = checksum16(ip_, hdr_len / 2); memcpy(hdrp, ip, sizeof(*ip)); + if (need_arp) return arp_send_or_queue_on(ifx, nh, pkt); return eth_send_frame_on(ifx, ETHERTYPE_IPV4, dst_mac, pkt); } diff --git a/kernel/networking/internet_layer/ipv6.c b/kernel/networking/internet_layer/ipv6.c index 65b359e2..858da9f4 100644 --- a/kernel/networking/internet_layer/ipv6.c +++ b/kernel/networking/internet_layer/ipv6.c @@ -404,13 +404,11 @@ bool ipv6_send_packet(const uint8_t dst[16], uint8_t next_header, netpkt_t* pkt, } uint8_t dst_mac[6]; + bool need_ndp = false; l2_interface_t* l2 = l2_interface_find_by_index(ifx); if (ipv6_is_multicast(dst)) ipv6_multicast_mac(dst, dst_mac); else if (l2 && l2->kind == NET_IFK_LOCALHOST) memset(dst_mac, 0, 6); - else if (!ndp_resolve_on(ifx, nh, dst_mac, 200)) { - netpkt_unref(pkt); - return false; - } + else need_ndp = true; uint16_t mtu = 1500; @@ -441,6 +439,7 @@ bool ipv6_send_packet(const uint8_t dst[16], uint8_t next_header, netpkt_t* pkt, memcpy(ip6.dst, dst, 16); memcpy(hdrp, &ip6, sizeof(ip6)); + if (need_ndp) return ndp_send_or_queue_on(ifx, nh, pkt); return eth_send_frame_on(ifx, ETHERTYPE_IPV6, dst_mac, pkt); } @@ -513,7 +512,8 @@ bool ipv6_send_packet(const uint8_t dst[16], uint8_t next_header, netpkt_t* pkt, memcpy((uint8_t*)buf + hdr_len + sizeof(fh), data + off, chunk); - if (!eth_send_frame_on(ifx, ETHERTYPE_IPV6, dst_mac, fpkt)) ok = false; + if (need_ndp) { if (!ndp_send_or_queue_on(ifx, nh, fpkt)) ok = false; } + else if (!eth_send_frame_on(ifx, ETHERTYPE_IPV6, dst_mac, fpkt)) ok = false; off += chunk; } diff --git a/kernel/networking/link_layer/arp.c b/kernel/networking/link_layer/arp.c index a0f64600..f61d85f2 100644 --- a/kernel/networking/link_layer/arp.c +++ b/kernel/networking/link_layer/arp.c @@ -9,10 +9,15 @@ #include "syscalls/syscalls.h" #include "networking/internet_layer/ipv4_utils.h" -typedef struct arp_table { +#define ARP_REACHABLE_MS 180000 +#define ARP_STALE_MS 60000 +#define ARP_RETRANS_MS 1000 +#define ARP_MAX_PROBES 3 + +struct arp_table { arp_entry_t entries[ARP_TABLE_MAX]; uint8_t init; -} arp_table_t; +}; static uint16_t g_arp_pid = 0xFFFF; @@ -21,6 +26,18 @@ static inline arp_table_t* l2_arp(uint8_t ifindex){ return l2 ? (arp_table_t*)l2->arp_table : 0; } +static void arp_entry_clear(arp_entry_t* e) { + if (!e) return; + if (e->pending) { + for (int i = 0; i < e->pending_len; i++) { + if (e->pending[i]) netpkt_unref(e->pending[i]); + } + release(e->pending); + } + memset(e, 0, sizeof(*e)); + e->state = ARP_STATE_UNUSED; +} + arp_table_t* arp_table_create(void){ arp_table_t* t = (arp_table_t*)zalloc(sizeof(arp_table_t)); if (!t) return 0; @@ -30,7 +47,9 @@ arp_table_t* arp_table_create(void){ } void arp_table_destroy(arp_table_t* t){ - if (t) release(t); + if (!t) return; + for (int i = 0; i < ARP_TABLE_MAX; i++) arp_entry_clear(&t->entries[i]); + release(t); } void arp_table_init_static_defaults(arp_table_t* t){ @@ -43,41 +62,86 @@ void arp_table_init_static_defaults(arp_table_t* t){ t->entries[0].mac[4] = 0xFF; t->entries[0].mac[5] = 0xFF; t->entries[0].ttl_ms = 0; + t->entries[0].state = ARP_STATE_REACHABLE; 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; + for (int i=0;ientries[i].state == ARP_STATE_UNUSED) continue; + if (t->entries[i].ip == ip) return i; + } return -1; } static int arp_find_free(arp_table_t* t){ if (!t) return -1; - for (int i=0;ientries[i].ip == 0) return i; + for (int i=0;ientries[i].state == ARP_STATE_UNUSED) return i; return -1; } +static int arp_find_replacement(arp_table_t* t) { + int best = -1; + uint32_t best_ttl = 0xFFFFFFFFu; + if (!t) return -1; + + for (int i = 0; i < ARP_TABLE_MAX; i++) { + arp_entry_t* e = &t->entries[i]; + if (e->static_entry) continue; + if (e->pending_len) continue; + if (e->state == ARP_STATE_UNUSED) return i; + if (e->ttl_ms < best_ttl) { + best_ttl = e->ttl_ms; + best = i; + } + } + + return best; +} + 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; + if (!t || !ip || !mac) return; int idx = arp_find_slot(t, ip); if (idx < 0) idx = arp_find_free(t); - if (idx < 0) idx = 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; + if (idx < 0) idx = arp_find_replacement(t); + if (idx < 0) return; + + arp_entry_t* e = &t->entries[idx]; + if (e->state != ARP_STATE_UNUSED && e->ip != ip) arp_entry_clear(e); + + e->ip = ip; + memcpy(e->mac, mac, 6); + e->ttl_ms = is_static ? 0 : (ttl_ms ? ttl_ms : ARP_REACHABLE_MS); + e->timer_ms = 0; + e->state = ARP_STATE_REACHABLE; + e->probes_sent = 0; + e->static_entry = is_static ? 1 : 0; + + if (e->pending) { + for (int i = 0; i < e->pending_len; i++) { + netpkt_t* pkt = e->pending[i]; + e->pending[i] = 0; + if (pkt) (void)eth_send_frame_on(ifindex, ETHERTYPE_IPV4, e->mac, pkt); + } + release(e->pending); + e->pending = 0; + e->pending_len = 0; + e->pending_bytes = 0; + } } 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; + if (!t || !mac_out) return false; + for (int i=0;ientries[i].ip == ip){ - memcpy(mac_out, t->entries[i].mac, 6); - return true; - } + arp_entry_t* e = &t->entries[i]; + if (e->state == ARP_STATE_UNUSED || e->state == ARP_STATE_INCOMPLETE) continue; + if (e->ip != ip) continue; + memcpy(mac_out, e->mac, 6); + return true; } return false; } @@ -86,12 +150,38 @@ 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 { - t->entries[i].ttl_ms -= ms; + arp_entry_t* e = &t->entries[i]; + if (e->state == ARP_STATE_UNUSED || e->static_entry) continue; + + if (e->state == ARP_STATE_INCOMPLETE) { + if (e->timer_ms > ms) { + e->timer_ms -= ms; + continue; + } + + if (e->probes_sent >= ARP_MAX_PROBES) { + arp_entry_clear(e); + continue; + } + + e->timer_ms = ARP_RETRANS_MS; + e->probes_sent++; + arp_send_request_on(ifindex, e->ip); + continue; + } + + if (e->ttl_ms > ms) { + e->ttl_ms -= ms; + continue; } + + if (e->state == ARP_STATE_REACHABLE) { + e->state = ARP_STATE_STALE; + e->ttl_ms = ARP_STALE_MS; + continue; + } + + arp_entry_clear(e); } } @@ -126,22 +216,65 @@ static uint32_t pick_spa_for_l2(uint8_t ifindex, uint32_t target_ip){ return 0; } -bool arp_resolve_on(uint8_t ifindex, uint32_t ip, uint8_t mac_out[6], uint32_t timeout_ms){ +bool arp_send_or_queue_on(uint8_t ifindex, uint32_t ip, netpkt_t* pkt) { + if (!pkt || !netpkt_len(pkt)) { + if (pkt) netpkt_unref(pkt); + return false; + } + + uint8_t mac[6]; if (ip == 0xFFFFFFFFu){ - memset(mac_out, 0xFF, 6); - return true; + memset(mac, 0xFF, 6); + return eth_send_frame_on(ifindex, ETHERTYPE_IPV4, mac, pkt); } - 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_for_l2(ifindex, POLL_MS); - if (arp_table_get_for_l2(ifindex, ip, mac_out)) return true; - msleep(POLL_MS); - waited += POLL_MS; + if (arp_table_get_for_l2(ifindex, ip, mac)) return eth_send_frame_on(ifindex, ETHERTYPE_IPV4, mac, pkt); + + arp_table_t* t = l2_arp(ifindex); + if (!t || !ip) { + netpkt_unref(pkt); + return false; } - return false; + + int idx = arp_find_slot(t, ip); + if (idx < 0) idx = arp_find_free(t); + if (idx < 0) idx = arp_find_replacement(t); + if (idx < 0) { + netpkt_unref(pkt); + return false; + } + + arp_entry_t* e = &t->entries[idx]; + if (e->state != ARP_STATE_UNUSED && e->ip != ip) arp_entry_clear(e); + + uint32_t len = netpkt_len(pkt); + if (e->pending_len >= ARP_PENDING_MAX || e->pending_bytes + len > ARP_PENDING_MAX_BYTES) { + netpkt_unref(pkt); + return false; + } + + if (!e->pending) { + e->pending = (netpkt_t**)zalloc(sizeof(netpkt_t*) * ARP_PENDING_MAX); + if (!e->pending) { + netpkt_unref(pkt); + return false; + } + } + + e->ip = ip; + e->state = ARP_STATE_INCOMPLETE; + e->ttl_ms = ARP_REACHABLE_MS; + e->static_entry = 0; + e->pending[e->pending_len] = pkt; + e->pending_len++; + e->pending_bytes += len; + + if (!e->probes_sent || !e->timer_ms) { + e->timer_ms = ARP_RETRANS_MS; + e->probes_sent++; + arp_send_request_on(ifindex, ip); + } + + return true; } void arp_send_request_on(uint8_t ifindex, uint32_t target_ip){ @@ -214,16 +347,12 @@ void arp_input(uint16_t ifindex, const uint8_t src_mac[6], netpkt_t* pkt) { 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); + arp_table_put_for_l2((uint8_t)ifindex, sender_ip, hdr.sender_mac, ARP_REACHABLE_MS, false); if (op == ARP_OPCODE_REQUEST) { - char tbuf[16], abuf[16]; - ipv4_to_string(target_ip, tbuf); - bool has = l2_has_ip((uint8_t)ifindex, target_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); } @@ -237,7 +366,7 @@ 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; + const uint32_t tick_ms = 1000; while (1){ arp_tick_all(tick_ms); msleep(tick_ms); diff --git a/kernel/networking/link_layer/arp.h b/kernel/networking/link_layer/arp.h index 7d59be70..e135421b 100644 --- a/kernel/networking/link_layer/arp.h +++ b/kernel/networking/link_layer/arp.h @@ -8,9 +8,18 @@ extern "C" { #endif #define ARP_TABLE_MAX 64 +#define ARP_PENDING_MAX 8 +#define ARP_PENDING_MAX_BYTES 32768 #define ARP_OPCODE_REQUEST 1 #define ARP_OPCODE_REPLY 2 +typedef enum { + ARP_STATE_UNUSED = 0, + ARP_STATE_INCOMPLETE = 1, + ARP_STATE_REACHABLE = 2, + ARP_STATE_STALE = 3 +} arp_state_t; + typedef struct __attribute__((packed)) arp_hdr_t { uint16_t htype; uint16_t ptype; @@ -27,7 +36,13 @@ typedef struct arp_entry { uint32_t ip; uint8_t mac[6]; uint32_t ttl_ms; - uint8_t static_entry;//1 static, 0 dynamic + uint32_t timer_ms; + uint32_t pending_bytes; + netpkt_t** pending; + uint8_t pending_len; + uint8_t state; + uint8_t probes_sent; + uint8_t static_entry;//1 static, 0 dynamic } arp_entry_t; typedef struct arp_table arp_table_t; @@ -41,7 +56,7 @@ 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); -bool arp_resolve_on(uint8_t ifindex, uint32_t ip, uint8_t mac_out[6], uint32_t timeout_ms); +bool arp_send_or_queue_on(uint8_t ifindex, uint32_t ip, netpkt_t* pkt); void arp_send_request_on(uint8_t ifindex, uint32_t target_ip); void arp_input(uint16_t ifindex, const uint8_t src_mac[6], netpkt_t* pkt); diff --git a/kernel/networking/link_layer/ndp.c b/kernel/networking/link_layer/ndp.c index ada22a54..73c151c6 100644 --- a/kernel/networking/link_layer/ndp.c +++ b/kernel/networking/link_layer/ndp.c @@ -1,4 +1,5 @@ #include "ndp.h" +#include "eth.h" #include "networking/internet_layer/icmpv6.h" #include "std/memory.h" #include "std/string.h" @@ -317,6 +318,16 @@ static void ndp_on_ra(uint8_t ifindex, const uint8_t router_ip[16], uint16_t rou if(!ipv6_is_placeholder_gua(slot->ip) && !ipv6_is_unspecified(slot->ip)) memcpy(slot->interface_id, slot->ip + 8, 8); } +static void ndp_entry_clear(ndp_entry_t* e) { + if (!e) return; + if (e->pending) { + for (int i = 0; i < e->pending_len; i++) if (e->pending[i]) netpkt_unref(e->pending[i]); + release(e->pending); + } + memset(e, 0, sizeof(*e)); + e->state = NDP_STATE_UNUSED; +} + ndp_table_t* ndp_table_create(void) { ndp_table_impl_t* t = (ndp_table_impl_t*)zalloc(sizeof(ndp_table_impl_t)); if (!t) return 0; @@ -327,6 +338,8 @@ ndp_table_t* ndp_table_create(void) { void ndp_table_destroy(ndp_table_t* t) { if (!t) return; + ndp_table_impl_t* impl = (ndp_table_impl_t*)t; + for (int i = 0; i < NDP_TABLE_MAX; i++) ndp_entry_clear(&impl->entries[i]); release(t); } @@ -356,9 +369,23 @@ static int ndp_find_free(ndp_table_impl_t* t) { return -1; } -static void ndp_entry_clear(ndp_entry_t* e) { - memset(e, 0, sizeof(*e)); - e->state = NDP_STATE_UNUSED; +static int ndp_find_replacement(ndp_table_impl_t* t) { + uint32_t best_ttl = 0xFFFFFFFFu; + int best = -1; + if (!t) return -1; + + for (int i = 0; i < NDP_TABLE_MAX; i++) { + ndp_entry_t* e = &t->entries[i]; + if (e->pending_len) continue; + if (e->state == NDP_STATE_UNUSED || e->ttl_ms == 0) return i; + if (e->is_router && e->router_lifetime_ms) continue; + if (e->ttl_ms < best_ttl) { + best_ttl = e->ttl_ms; + best = i; + } + } + + return best; } void ndp_table_put_for_l2(uint8_t ifindex, const uint8_t ip[16], const uint8_t mac[6], uint32_t ttl_ms, bool router) { @@ -368,30 +395,11 @@ void ndp_table_put_for_l2(uint8_t ifindex, const uint8_t ip[16], const uint8_t m int idx = ndp_find_slot(t, ip); if (idx < 0) idx = ndp_find_free(t); - if (idx < 0) { - uint32_t best_ttl = 0xFFFFFFFFu; - int best_i = -1; - - for (int i = 0; i < NDP_TABLE_MAX; i++) { - ndp_entry_t* e = &t->entries[i]; - if (e->state == NDP_STATE_UNUSED || e->ttl_ms == 0) { - best_i = i; - break; - } - - if (e->is_router && e->router_lifetime_ms) continue; - - if (e->ttl_ms < best_ttl) { - best_ttl = e->ttl_ms; - best_i = i; - } - } - - if (best_i < 0) best_i = 0; - idx = best_i; - } + if (idx < 0) idx = ndp_find_replacement(t); + if (idx < 0) return; ndp_entry_t* e = &t->entries[idx]; + if (e->state != NDP_STATE_UNUSED && memcmp(e->ip, ip, 16) != 0) ndp_entry_clear(e); memcpy(e->ip, ip, 16); if (mac) { @@ -409,6 +417,18 @@ void ndp_table_put_for_l2(uint8_t ifindex, const uint8_t ip[16], const uint8_t m e->is_router = router ? 1 : 0; e->router_lifetime_ms = router ? ttl_ms : 0; e->probes_sent = 0; + + if (e->pending) { + for (int i = 0; i < e->pending_len; i++) { + netpkt_t* pkt = e->pending[i]; + e->pending[i] = 0; + if (pkt) (void)eth_send_frame_on(ifindex, ETHERTYPE_IPV6, e->mac, pkt); + } + release(e->pending); + e->pending = 0; + e->pending_len = 0; + e->pending_bytes = 0; + } } static bool ndp_table_get_for_l2(uint8_t ifindex, const uint8_t ip[16], uint8_t mac_out[6]) { @@ -663,77 +683,68 @@ static void ndp_tick_all(uint32_t ms) { } } -bool ndp_resolve_on(uint16_t ifindex, const uint8_t next_hop[16], uint8_t out_mac[6], uint32_t timeout_ms) { +bool ndp_send_or_queue_on(uint16_t ifindex, const uint8_t next_hop[16], netpkt_t* pkt) { + if (!next_hop || !pkt || !netpkt_len(pkt)) { + if (pkt) netpkt_unref(pkt); + return false; + } + + uint8_t out_mac[6]; if (ipv6_is_multicast(next_hop)) { ipv6_multicast_mac(next_hop, out_mac); - return true; + return eth_send_frame_on(ifindex, ETHERTYPE_IPV6, out_mac, pkt); } - if (ndp_table_get_for_l2((uint8_t)ifindex, next_hop, out_mac)) return true; + if (ndp_table_get_for_l2((uint8_t)ifindex, next_hop, out_mac)) return eth_send_frame_on(ifindex, ETHERTYPE_IPV6, out_mac, pkt); ndp_table_impl_t* t = l2_ndp((uint8_t)ifindex); - if (t) { - int idx = ndp_find_slot(t, next_hop); - if (idx >= 0) { - ndp_entry_t* e = &t->entries[idx]; - if (e->ttl_ms && e->is_router && e->state != NDP_STATE_UNUSED && e->state != NDP_STATE_INCOMPLETE) { - memcpy(out_mac, e->mac, 6); - return true; - } - } + if (!t) { + netpkt_unref(pkt); + return false; } - uint8_t src_ip[16] = {0}; - l2_interface_t* l2 = l2_interface_find_by_index((uint8_t)ifindex); - - if (l2) { - for (int i = 0; i < MAX_IPV6_PER_INTERFACE; i++) { - l3_ipv6_interface_t* v6 = l2->l3_v6[i]; - if (!v6) continue; - if (v6->cfg == IPV6_CFG_DISABLE) continue; - if (v6->dad_state != IPV6_DAD_OK) continue; + int idx = ndp_find_slot(t, next_hop); + if (idx < 0) idx = ndp_find_free(t); + if (idx < 0) idx = ndp_find_replacement(t); + if (idx < 0) { + netpkt_unref(pkt); + return false; + } - if (ipv6_is_linklocal(v6->ip)) { - memcpy(src_ip, v6->ip, 16); - break; - } + ndp_entry_t* e = &t->entries[idx]; + if (e->state != NDP_STATE_UNUSED && memcmp(e->ip, next_hop, 16) != 0) ndp_entry_clear(e); - if (ipv6_is_unspecified(src_ip) && !ipv6_is_unspecified(v6->ip)) - memcpy(src_ip, v6->ip, 16); - } + uint32_t len = netpkt_len(pkt); + if (e->pending_len >= NDP_PENDING_MAX || e->pending_bytes + len > NDP_PENDING_MAX_BYTES) { + netpkt_unref(pkt); + return false; } - t = l2_ndp((uint8_t)ifindex); - if (t) { - int idx = ndp_find_slot(t, next_hop); - if (idx < 0) idx = ndp_find_free(t); - - if (idx >= 0) { - ndp_entry_t* e = &t->entries[idx]; - memcpy(e->ip, next_hop, 16); - memset(e->mac, 0, 6); - e->ttl_ms = g_ndp_reachable_time_ms * 4; - e->is_router = 0; - e->router_lifetime_ms = 0; - e->state = NDP_STATE_INCOMPLETE; - e->timer_ms = g_ndp_retrans_timer_ms; - e->probes_sent = 0; + if (!e->pending) { + e->pending = (netpkt_t**)zalloc(sizeof(netpkt_t*) * NDP_PENDING_MAX); + if (!e->pending) { + netpkt_unref(pkt); + return false; } } - ndp_send_ns_on((uint8_t)ifindex, next_hop, src_ip); - - uint32_t waited = 0; - const uint32_t poll = 50; - - while (waited < timeout_ms) { - ndp_table_tick_for_l2((uint8_t)ifindex, poll); - if (ndp_table_get_for_l2((uint8_t)ifindex, next_hop, out_mac)) return true; - msleep(poll); - waited += poll; + memcpy(e->ip, next_hop, 16); + memset(e->mac, 0, 6); + e->ttl_ms = g_ndp_reachable_time_ms * 4; + e->is_router = 0; + e->router_lifetime_ms = 0; + e->state = NDP_STATE_INCOMPLETE; + e->pending[e->pending_len] = pkt; + e->pending_len++; + e->pending_bytes += len; + + if (!e->probes_sent || !e->timer_ms) { + e->timer_ms = g_ndp_retrans_timer_ms; + e->probes_sent++; + ndp_send_probe((uint8_t)ifindex, e); } - return false; + return true; } bool ndp_request_dad_on(uint8_t ifindex, const uint8_t ip[16]) { @@ -874,30 +885,11 @@ void ndp_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[ int idx = ndp_find_slot(t, na.target); if (idx < 0) idx = ndp_find_free(t); - if (idx < 0) { - uint32_t best_ttl = 0xFFFFFFFFu; - int best_i = -1; - - for (int i = 0; i < NDP_TABLE_MAX; i++) { - ndp_entry_t* e = &t->entries[i]; - if (e->state == NDP_STATE_UNUSED || e->ttl_ms == 0) { - best_i = i; - break; - } - - if (e->is_router && e->router_lifetime_ms) continue; - - if (e->ttl_ms < best_ttl) { - best_ttl = e->ttl_ms; - best_i = i; - } - } - - if (best_i < 0) best_i = 0; - idx = best_i; - } + if (idx < 0) idx = ndp_find_replacement(t); + if (idx < 0) return; ndp_entry_t* e = &t->entries[idx]; + if (e->state != NDP_STATE_UNUSED && memcmp(e->ip, na.target, 16) != 0) ndp_entry_clear(e); uint8_t old_mac[6]; memcpy(old_mac, e->mac, 6); @@ -962,6 +954,18 @@ void ndp_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[ } e->probes_sent = 0; + + if (e->pending) { + for (int i = 0; i < e->pending_len; i++) { + netpkt_t* pp = e->pending[i]; + e->pending[i] = 0; + if (pp) eth_send_frame_on((uint8_t)ifindex, ETHERTYPE_IPV6, e->mac, pp); + } + release(e->pending); + e->pending = 0; + e->pending_len = 0; + e->pending_bytes = 0; + } return; } diff --git a/kernel/networking/link_layer/ndp.h b/kernel/networking/link_layer/ndp.h index 244ab819..90b16074 100644 --- a/kernel/networking/link_layer/ndp.h +++ b/kernel/networking/link_layer/ndp.h @@ -11,6 +11,8 @@ typedef struct ndp_table ndp_table_t; #define RA_FLAG_M 0x80 #define RA_FLAG_O 0x40 +#define NDP_PENDING_MAX 8 +#define NDP_PENDING_MAX_BYTES 32768 typedef enum { NDP_STATE_UNUSED = 0, @@ -26,6 +28,9 @@ typedef struct { uint8_t mac[6]; uint32_t ttl_ms; uint32_t timer_ms; + uint32_t pending_bytes; + netpkt_t** pending; + uint8_t pending_len; uint8_t state; uint8_t probes_sent; uint8_t is_router; @@ -41,7 +46,7 @@ void ndp_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[ void ndp_table_put_for_l2(uint8_t ifindex, const uint8_t ip[16], const uint8_t mac[6], uint32_t ttl_ms, bool router); -bool ndp_resolve_on(uint16_t ifindex, const uint8_t next_hop[16], uint8_t out_mac[6], uint32_t timeout_ms); +bool ndp_send_or_queue_on(uint16_t ifindex, const uint8_t next_hop[16], netpkt_t* pkt); bool ndp_request_dad_on(uint8_t ifindex, const uint8_t ip[16]); diff --git a/kernel/networking/network_dispatch.cpp b/kernel/networking/network_dispatch.cpp index 150b9803..0750cc22 100644 --- a/kernel/networking/network_dispatch.cpp +++ b/kernel/networking/network_dispatch.cpp @@ -115,12 +115,15 @@ int NetworkDispatch::net_task() netpkt_t* pkt = nics[n].tx.peek(); irq_restore(flags); - if (!driver->send_packet(pkt)) break; + netdev_tx_result_t txr = driver->send_packet(pkt); + if (txr == NETDEV_TX_BUSY) break; flags = irq_save_disable(); netpkt_t* popped = nullptr; nics[n].tx.pop(popped); irq_restore(flags); + + if (txr == NETDEV_TX_DROP && popped) netpkt_unref(popped); processed++; } if (processed) { diff --git a/kernel/virtio/virtio_pci.c b/kernel/virtio/virtio_pci.c index b5508934..a272a886 100644 --- a/kernel/virtio/virtio_pci.c +++ b/kernel/virtio/virtio_pci.c @@ -253,11 +253,12 @@ void virtio_notify(virtio_device *dev) { virtio_notify_queue(dev, dev->current_queue); } +//TODO this still blocks until the device completes the request +//t would make more sense to split submit/completion later and complete requests through async/events/promises bool virtio_send_nd(virtio_device *dev, const virtio_buf *bufs, uint16_t n) { - if (!dev || !bufs || !n) return false; + if (dev->current_queue >= VIRTIO_MAX_QUEUES || dev->current_queue >= dev->num_queues) return false; - if (dev->current_queue >= VIRTIO_MAX_QUEUES) return false; virtio_queue *queue = &dev->queues[dev->current_queue]; if (!queue->valid || !queue->size || n > queue->size) return false; @@ -291,6 +292,7 @@ bool virtio_send_nd(virtio_device *dev, const virtio_buf *bufs, uint16_t n) { virtio_notify(dev); while (last_used_idx == u->idx);//TODO: OPT + asm volatile ("dmb ishld" ::: "memory"); return true; } From ad764dbc7b9c66f6ce9e4f12540ae4be079d658f Mon Sep 17 00:00:00 2001 From: CodeAnarchist Date: Mon, 25 May 2026 15:02:46 +0200 Subject: [PATCH 15/39] [dns] add generic dns parser --- kernel/networking/application_layer/dns/dns.c | 515 +++++------------- kernel/networking/application_layer/dns/dns.h | 5 + .../application_layer/dns/dns_cache.c | 44 +- .../application_layer/dns/dns_cache.h | 5 +- .../application_layer/dns/dns_daemon.c | 18 +- .../application_layer/dns/dns_mdns.c | 304 ++++------- .../application_layer/dns/dns_mdns.h | 1 + .../networking/application_layer/dns/dns_sd.c | 149 ++--- .../networking/application_layer/dns/dns_sd.h | 19 +- .../application_layer/dns/dns_wire.c | 312 +++++++++++ .../application_layer/dns/dns_wire.h | 87 +++ .../application_layer/dns/mdns_responder.c | 422 ++++++-------- shared | 2 +- 13 files changed, 907 insertions(+), 976 deletions(-) create mode 100644 kernel/networking/application_layer/dns/dns_wire.c create mode 100644 kernel/networking/application_layer/dns/dns_wire.h diff --git a/kernel/networking/application_layer/dns/dns.c b/kernel/networking/application_layer/dns/dns.c index d9262a63..c0c1de50 100644 --- a/kernel/networking/application_layer/dns/dns.c +++ b/kernel/networking/application_layer/dns/dns.c @@ -15,153 +15,29 @@ #define MDNS_TIMEOUT_A_MS 500u #define MDNS_TIMEOUT_AAAA_MS 300u +#define DNS_MAX_CNAME_DEPTH 4 +#define DNS_QUERY_RECORDS 8 -static bool dns_is_local_name(const char* hostname) { - if (!hostname) return false; - uint32_t nlen = strlen(hostname); - if (nlen < 6u) return false; - if (strncmp(hostname +(nlen - 6u), ".local", 6) != 0)return false; - return true; -} - -static dns_result_t dns_write_qname(uint8_t* buf, uint32_t buf_len, uint32_t* offset, const char* name) { - if (!buf || !offset || !name) return DNS_ERR_FORMAT; - uint32_t off = *offset; - if (off >= buf_len) return DNS_ERR_FORMAT; - uint32_t label_len = 0; - uint32_t label_pos = off; - buf[off++] = 0; - for (const char* p = name; *p; ++p) { - char c = *p; - if (c =='.') { - if (!label_len || label_len > 63u) return DNS_ERR_FORMAT; - buf[label_pos] = (uint8_t)label_len; - label_len = 0; - label_pos = off; - if (off >= buf_len) return DNS_ERR_FORMAT; - buf[off++] = 0; - continue; - } - if (label_len >= 63u) return DNS_ERR_FORMAT; - if (off >= buf_len) return DNS_ERR_FORMAT; - buf[off++] = (uint8_t)c; - label_len++; - } - if (!label_len || label_len > 63u) return DNS_ERR_FORMAT; - buf[label_pos] = (uint8_t)label_len; - if (off >= buf_len) return DNS_ERR_FORMAT; - buf[off++]= 0; - *offset = off; - return DNS_OK; -} - -static uint32_t skip_dns_name(const uint8_t* message, uint32_t message_len, uint32_t offset){ - if (offset >= message_len) return message_len + 1; - uint32_t cursor = offset; - while (cursor < message_len) { - uint8_t len = message[cursor++]; - if (len == 0) break; - if ((len & 0xC0) == 0xC0) { - if (cursor >= message_len) return message_len + 1; - cursor++; - break; - } - cursor += len; - if (cursor > message_len) return message_len + 1; - } - return cursor; -} - -static dns_result_t parse_dns_a_record(uint8_t* buffer, uint32_t buffer_len, uint16_t message_id, uint32_t* out_ip, uint32_t* out_ttl_s){ - 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); - uint32_t ttl_s = rd_be32(buffer+offset+4); - uint16_t rdlength = rd_be16(buffer+offset+8); - offset += 10; - if (offset + rdlength > buffer_len) return DNS_ERR_FORMAT; - if (type == 1 && klass == 1 && rdlength == 4){ - uint32_t ip_host = rd_be32(buffer+offset); - *out_ip = ip_host; - if (out_ttl_s) *out_ttl_s = ttl_s; - return DNS_OK; - } - offset += rdlength; - } - return DNS_ERR_NO_ANSWER; -} +static dns_result_t perform_dns_query_once(socket_handle_t sock, const net_l4_endpoint *dns_srv, const char *name, dns_qtype_t qtype, uint32_t timeout_ms, dns_record_t *out_records, uint32_t max_records, uint32_t *out_count) { + if (out_count) *out_count = 0; + if (!sock) return DNS_ERR_SOCKET; + if (!dns_srv) return DNS_ERR_NO_DNS; + if (!name) return DNS_ERR_FORMAT; + if (!out_records && max_records) return DNS_ERR_FORMAT; -static dns_result_t parse_dns_aaaa_record(uint8_t* buffer, uint32_t buffer_len, uint16_t message_id, uint8_t out_ipv6[16], uint32_t* out_ttl_s){ - 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); - uint32_t ttl_s = rd_be32(buffer+offset+4); - 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); - if (out_ttl_s) *out_ttl_s = ttl_s; - return DNS_OK; - } - offset += rdlength; - } - return DNS_ERR_NO_ANSWER; -} - -static dns_result_t perform_dns_query_once_a(socket_handle_t sock, const net_l4_endpoint* dns_srv, const char* name, uint32_t timeout_ms, uint32_t* out_ip, uint32_t* out_ttl_s){ uint8_t request_buffer[512]; - memset(request_buffer,0,sizeof(request_buffer)); rng_t rng; uint64_t virt_timer; asm volatile ("mrs %0, cntvct_el0" : "=r"(virt_timer)); rng_seed(&rng, virt_timer); 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; - dns_result_t qnr=dns_write_qname(request_buffer, sizeof(request_buffer), &offset, name); - if (qnr != DNS_OK) return qnr; - - if (offset+ 4 > sizeof(request_buffer)) return DNS_ERR_FORMAT; - - wr_be16(request_buffer+offset+0, 1); - wr_be16(request_buffer+offset+2, 1); - offset += 4; + uint32_t request_len = dns_wire_build_query(request_buffer, sizeof(request_buffer), message_id, name, qtype, false); + if (!request_len) return DNS_ERR_FORMAT; net_l4_endpoint dst = *dns_srv; dst.port = 53; - int64_t sent = socket_sendto_udp_ex(sock, DST_ENDPOINT, &dst, 0, request_buffer, offset); + int64_t sent = socket_sendto_udp_ex(sock, DST_ENDPOINT, &dst, 0, request_buffer, request_len); if (sent < 0) return DNS_ERR_SEND; uint32_t waited_ms = 0; @@ -169,82 +45,39 @@ static dns_result_t perform_dns_query_once_a(socket_handle_t sock, const net_l4_ uint8_t response_buffer[512]; net_l4_endpoint source; int64_t received = socket_recvfrom_udp_ex(sock, response_buffer, sizeof(response_buffer), &source); - bool ok_src = false; - if (received > 0 && source.port == 53 && source.ver == dst.ver) { - if (dst.ver == IP_VER4) ok_src = (*(uint32_t*)source.ip == *(uint32_t*)dst.ip); - else if (dst.ver == IP_VER6) ok_src = (memcmp(source.ip, dst.ip, 16) == 0); - } - if (ok_src){ - uint32_t ip_host; - uint32_t ttl_s = 0; - dns_result_t pr = parse_dns_a_record(response_buffer, (uint32_t)received, message_id, &ip_host, &ttl_s); - if(pr == DNS_OK){ - *out_ip = ip_host; - if(out_ttl_s) *out_ttl_s = ttl_s; - return DNS_OK; + if (received > 0 && source.port == 53 && source.ver == dst.ver && ((dst.ver == IP_VER4 && memcmp(source.ip, dst.ip, 4) == 0) || (dst.ver == IP_VER6 && memcmp(source.ip, dst.ip, 16) == 0))) { + uint32_t received_len = received; + dns_record_t parsed[DNS_QUERY_RECORDS]; + uint32_t parsed_count = 0; + uint16_t flags = 0; + if (!dns_wire_parse_records(response_buffer, received_len, true, message_id, parsed, DNS_QUERY_RECORDS, &parsed_count, &flags)) { + msleep(50); + waited_ms += 50; + continue; + } + if ((flags & DNS_RCODE_MASK) == DNS_RCODE_NXDOMAIN) return DNS_ERR_NXDOMAIN; + + uint32_t count = 0; + for (uint32_t i = 0; i < parsed_count; i++) { + if ((parsed[i].rrclass & DNS_CLASS_MASK) != DNS_CLASS_IN) continue; + if (qtype != DNS_TYPE_ANY && parsed[i].type != qtype && parsed[i].type != DNS_TYPE_CNAME) continue; + if (count < max_records) out_records[count] = parsed[i]; + count++; } - if (pr == DNS_ERR_NXDOMAIN) return pr; - } - msleep(50); - waited_ms += 50; - } - return DNS_ERR_TIMEOUT; -} - -static dns_result_t perform_dns_query_once_aaaa(socket_handle_t sock, const net_l4_endpoint* dns_srv, const char* name, uint32_t timeout_ms, uint8_t out_ipv6[16], uint32_t* out_ttl_s){ - uint8_t request_buffer[512]; - memset(request_buffer, 0, sizeof(request_buffer)); - rng_t rng; - uint64_t virt_timer; - asm volatile ("mrs %0, cntvct_el0" : "=r"(virt_timer)); - rng_seed(&rng, virt_timer); - 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; - dns_result_t qnr=dns_write_qname(request_buffer, sizeof(request_buffer), &offset, name); - if (qnr != DNS_OK) return qnr; - - if (offset+ 4 > sizeof(request_buffer)) return DNS_ERR_FORMAT; - - wr_be16(request_buffer+offset+0, 28); - wr_be16(request_buffer+offset+2, 1); - offset += 4; - - net_l4_endpoint dst = *dns_srv; - 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); - bool ok_src = false; - if (received > 0 && source.port == 53 && source.ver == dst.ver) { - if (dst.ver == IP_VER4) ok_src = (*(uint32_t*)source.ip == *(uint32_t*)dst.ip); - else if (dst.ver == IP_VER6) ok_src = (memcmp(source.ip, dst.ip, 16) == 0); - } - if (ok_src){ - uint32_t ttl_s = 0; - dns_result_t pr = parse_dns_aaaa_record(response_buffer, (uint32_t)received, message_id, out_ipv6, &ttl_s); - if (pr == DNS_OK){ - if (out_ttl_s) *out_ttl_s = ttl_s; + if (count) { + if (out_count) *out_count = count < max_records ? count : max_records; return DNS_OK; } - if (pr == DNS_ERR_NXDOMAIN) return pr; - } else { - msleep(50); - waited_ms += 50; + return DNS_ERR_NO_ANSWER; } + msleep(50); + waited_ms += 50; } return DNS_ERR_TIMEOUT; } -static bool pick_dns_on_l3(uint8_t l3_id, net_l4_endpoint* out_primary, net_l4_endpoint* out_secondary){ +static bool pick_dns_on_l3(uint8_t l3_id, net_l4_endpoint* out_primary, net_l4_endpoint* out_secondary) { if (l3_ipv4_find_by_id(l3_id)) { l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(l3_id); if (!v4) return false; @@ -260,7 +93,7 @@ static bool pick_dns_on_l3(uint8_t l3_id, net_l4_endpoint* out_primary, net_l4_e out_secondary->ver = IP_VER4; memcpy(out_secondary->ip, &s, 4); } - return (p != 0) || (s != 0); + return p || s; } l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(l3_id); @@ -313,9 +146,8 @@ static bool pick_dns_first_iface(uint8_t* out_l3, net_l4_endpoint* out_primary, 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) continue; - static const uint8_t z[16] = {0}; - bool hp = memcmp(v6->runtime_opts_v6.dns[0], z, 16) != 0; - bool hq = memcmp(v6->runtime_opts_v6.dns[1], z, 16) != 0; + bool hp = !ipv6_is_unspecified(v6->runtime_opts_v6.dns[0]); + bool hq = !ipv6_is_unspecified(v6->runtime_opts_v6.dns[1]); if (hp || hq){ if (out_l3) *out_l3 = v6->l3_id; if (out_primary) { @@ -337,209 +169,150 @@ static bool pick_dns_first_iface(uint8_t* out_l3, net_l4_endpoint* out_primary, static bool dns_srv_is_zero(const net_l4_endpoint* e){ if (!e) return true; - if (e->ver == IP_VER4) return *(const uint32_t*)e->ip == 0; + if (e->ver == IP_VER4) return rd_be32(e->ip) == 0; if (e->ver == IP_VER6) return ipv6_is_unspecified(e->ip); return true; } -static dns_result_t query_with_selection_a(const net_l4_endpoint* primary, const net_l4_endpoint* secondary, dns_server_sel_t which, const char* hostname, uint32_t timeout_ms, uint32_t* out_ip){ +static dns_result_t query_with_selection(const net_l4_endpoint* primary, const net_l4_endpoint* secondary, dns_server_sel_t which, const char* hostname, dns_qtype_t qtype, uint32_t timeout_ms, dns_record_t* out_records, uint32_t max_records, uint32_t* out_count) { + if (out_count) *out_count = 0; if (which == DNS_USE_PRIMARY && dns_srv_is_zero(primary)) return DNS_ERR_NO_DNS; if (which == DNS_USE_SECONDARY && dns_srv_is_zero(secondary)) return DNS_ERR_NO_DNS; if (which == DNS_USE_BOTH && dns_srv_is_zero(primary) && dns_srv_is_zero(secondary)) 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; - uint32_t ttl_s = 0; - if (which == DNS_USE_PRIMARY) res = perform_dns_query_once_a(sock, primary, hostname, timeout_ms, out_ip, &ttl_s); - else if (which == DNS_USE_SECONDARY) res = perform_dns_query_once_a(sock, secondary, hostname, timeout_ms, out_ip, &ttl_s); - else { - const net_l4_endpoint* first = !dns_srv_is_zero(primary) ? primary : secondary; - const net_l4_endpoint* second = !dns_srv_is_zero(secondary) ? secondary : primary; - res = perform_dns_query_once_a(sock, first, hostname, timeout_ms, out_ip, &ttl_s); - if (res != DNS_OK && second && first != second) res = perform_dns_query_once_a(sock, second, hostname, timeout_ms, out_ip, &ttl_s); - } - if (res == DNS_OK) { - uint32_t ttl_ms = ttl_s > (0xFFFFFFFFu / 1000u) ? 0xFFFFFFFFu : ttl_s * 1000u; - uint8_t addr[16]; - memset(addr, 0, 16); - wr_be32(addr, *out_ip); - dns_cache_put_ip(hostname, 1, addr, ttl_ms); - } + + if (which == DNS_USE_PRIMARY) return perform_dns_query_once(sock, primary, hostname, qtype, timeout_ms, out_records, max_records, out_count); + if (which == DNS_USE_SECONDARY) return perform_dns_query_once(sock, secondary, hostname, qtype, timeout_ms, out_records, max_records, out_count); + + const net_l4_endpoint* first = !dns_srv_is_zero(primary) ? primary : secondary; + const net_l4_endpoint* second = !dns_srv_is_zero(secondary) ? secondary : primary; + + dns_result_t res = perform_dns_query_once(sock, first, hostname, qtype, timeout_ms, out_records, max_records, out_count); + if (res != DNS_OK && second && first != second) res = perform_dns_query_once(sock, second, hostname, qtype, timeout_ms, out_records, max_records, out_count); return res; } -static dns_result_t query_with_selection_aaaa(const net_l4_endpoint* primary, const net_l4_endpoint* secondary, dns_server_sel_t which, const char* hostname, uint32_t timeout_ms, uint8_t out_ipv6[16]){ - if (which == DNS_USE_PRIMARY && dns_srv_is_zero(primary)) return DNS_ERR_NO_DNS; - if (which == DNS_USE_SECONDARY && dns_srv_is_zero(secondary)) return DNS_ERR_NO_DNS; - if (which == DNS_USE_BOTH && dns_srv_is_zero(primary) && dns_srv_is_zero(secondary)) return DNS_ERR_NO_DNS; - socket_handle_t sock = dns_socket_handle(); - if (sock == 0) return DNS_ERR_SOCKET; +dns_result_t dns_query(const char* hostname, dns_qtype_t qtype, dns_record_t* out_records, uint32_t max_records, uint32_t* out_count, dns_server_sel_t which, uint32_t timeout_ms) { + if (out_count) *out_count = 0; + if (!hostname) return DNS_ERR_FORMAT; + if (!out_records && max_records) return DNS_ERR_FORMAT; + if (dns_wire_is_local_name(hostname)) return mdns_query(hostname, qtype, timeout_ms, out_records, max_records, out_count); + dns_result_t res = DNS_ERR_NO_DNS; - uint32_t ttl_s = 0; - if (which == DNS_USE_PRIMARY) res = perform_dns_query_once_aaaa(sock, primary, hostname, timeout_ms, out_ipv6, &ttl_s); - else if (which == DNS_USE_SECONDARY) res = perform_dns_query_once_aaaa(sock, secondary, hostname, timeout_ms, out_ipv6, &ttl_s); - else { - const net_l4_endpoint* first = !dns_srv_is_zero(primary) ? primary : secondary; - const net_l4_endpoint* second = !dns_srv_is_zero(secondary) ? secondary : primary; - res = perform_dns_query_once_aaaa(sock, first, hostname, timeout_ms, out_ipv6, &ttl_s); - if (res != DNS_OK && second && first != second) res = perform_dns_query_once_aaaa(sock, second, hostname, timeout_ms, out_ipv6, &ttl_s); - } - if (res == DNS_OK) { - uint32_t ttl_ms = ttl_s > (0xFFFFFFFFu / 1000u) ? 0xFFFFFFFFu : ttl_s * 1000u; - dns_cache_put_ip(hostname, 28, out_ipv6, ttl_ms); - } + uint8_t l3 = 0; + net_l4_endpoint p, s; + if (pick_dns_first_iface(&l3, &p, &s)) res = query_with_selection(&p, &s, which, hostname, qtype, timeout_ms, out_records, max_records, out_count); return res; } -dns_result_t dns_resolve_a(const char* hostname, uint32_t* out_ip, dns_server_sel_t which, uint32_t timeout_ms){ - if (!hostname || !out_ip) return DNS_ERR_FORMAT; - uint8_t cached[16]; - if (dns_cache_get_ip(hostname, 1, cached)) { - *out_ip = rd_be32(cached); - return DNS_OK; - } +dns_result_t dns_query_on_l3(uint8_t l3_id, const char* hostname, dns_qtype_t qtype, dns_record_t* out_records, uint32_t max_records, uint32_t* out_count, dns_server_sel_t which, uint32_t timeout_ms) { + if (out_count) *out_count = 0; + if (!hostname) return DNS_ERR_FORMAT; + if (!out_records && max_records) return DNS_ERR_FORMAT; + if (dns_wire_is_local_name(hostname))return mdns_query(hostname, qtype, timeout_ms, out_records, max_records, out_count); + dns_result_t res = DNS_ERR_NO_DNS; + net_l4_endpoint p,s; + if (pick_dns_on_l3(l3_id, &p, &s)) res = query_with_selection(&p, &s, which, hostname, qtype, timeout_ms, out_records, max_records, out_count); + return res; +} - bool is_local = dns_is_local_name(hostname); - - if (is_local){ - uint32_t ttl_s = 0; - dns_result_t mr = mdns_resolve_a(hostname, timeout_ms > MDNS_TIMEOUT_A_MS ? MDNS_TIMEOUT_A_MS : timeout_ms, out_ip, &ttl_s); - if (mr == DNS_OK) { - uint8_t a[16]; - memset(a, 0, 16); - wr_be32(a, *out_ip); - uint32_t ttl_ms = ttl_s > (0xFFFFFFFFu / 1000u) ? 0xFFFFFFFFu : ttl_s * 1000u; - dns_cache_put_ip(hostname, 1, a, ttl_ms); +static dns_result_t dns_resolve_ip_common(uint8_t use_l3, uint8_t l3_id, const char* hostname, dns_qtype_t qtype, uint8_t out_addr[16], dns_server_sel_t which, uint32_t timeout_ms, uint32_t *out_ttl_s) { + if (!hostname || !out_addr) return DNS_ERR_FORMAT; + + char current[DNS_WIRE_MAX_NAME]; + if (!dns_wire_name_normalize(hostname, current, sizeof(current))) return DNS_ERR_FORMAT; + + for (uint32_t depth = 0; depth <= DNS_MAX_CNAME_DEPTH; depth++) { + dns_record_t records[DNS_QUERY_RECORDS]; + uint32_t count = 0; + dns_result_t res; + if (use_l3) res = dns_query_on_l3(l3_id, current, qtype, records, DNS_QUERY_RECORDS, &count, which, timeout_ms); + else res = dns_query(current, qtype, records, DNS_QUERY_RECORDS, &count, which, timeout_ms); + if (res != DNS_OK) return res; + + char cname[DNS_WIRE_MAX_NAME]; + cname[0] = 0; + + for (uint32_t i = 0; i < count; i++) { + if ((records[i].rrclass & DNS_CLASS_MASK) != DNS_CLASS_IN) continue; + if (records[i].type == qtype) { + if (qtype == DNS_TYPE_A) memcpy(out_addr, records[i].addr, 4); + else memcpy(out_addr, records[i].addr, 16); + + uint32_t ttl_ms = 0xFFFFFFFF; + if (records[i].ttl_s <= 0xFFFFFFFFU / 1000) ttl_ms = records[i].ttl_s * 1000; + dns_cache_put_ip(current, qtype, out_addr, ttl_ms); + dns_cache_put_ip(hostname, qtype, out_addr, ttl_ms); + if (out_ttl_s) *out_ttl_s = records[i].ttl_s; + return DNS_OK; + } + if (records[i].type == DNS_TYPE_CNAME && records[i].target[0] && !cname[0]) strncpy(cname, records[i].target, sizeof(cname)); } - return mr; - } - dns_result_t res = DNS_ERR_NO_DNS; - uint8_t l3 = 0; - net_l4_endpoint p, s; - if (pick_dns_first_iface(&l3, &p, &s)) res = query_with_selection_a(&p, &s, which, hostname, timeout_ms, out_ip); - - if (res != DNS_OK && is_local){ - uint32_t ttl_s = 0; - dns_result_t mr = mdns_resolve_a(hostname, timeout_ms > MDNS_TIMEOUT_A_MS ? MDNS_TIMEOUT_A_MS : timeout_ms, out_ip, &ttl_s); - if (mr == DNS_OK) { - uint8_t a[16]; - memset(a,0, 16); - wr_be32(a, *out_ip); - uint32_t ttl_ms = ttl_s > (0xFFFFFFFFu / 1000u) ? 0xFFFFFFFFu : ttl_s * 1000u; - dns_cache_put_ip(hostname, 1, a, ttl_ms); - return DNS_OK; - } + if (!cname[0]) return DNS_ERR_NO_ANSWER; + if (dns_wire_name_equals(cname, current)) return DNS_ERR_FORMAT; + strncpy(current, cname, sizeof(current)); } - return res; + return DNS_ERR_NO_ANSWER; } -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_a(const char* hostname, uint32_t* out_ip, dns_server_sel_t which, uint32_t timeout_ms) { if (!hostname || !out_ip) return DNS_ERR_FORMAT; uint8_t cached[16]; - if (dns_cache_get_ip(hostname, 1, cached)) { + if (dns_cache_get_ip(hostname, DNS_TYPE_A, cached)) { *out_ip = rd_be32(cached); return DNS_OK; } - bool is_local = dns_is_local_name(hostname); - - if (is_local) { - uint32_t ttl_s = 0; - dns_result_t mr = mdns_resolve_a(hostname, timeout_ms > MDNS_TIMEOUT_A_MS ? MDNS_TIMEOUT_A_MS : timeout_ms, out_ip, &ttl_s); - if (mr == DNS_OK) { - uint8_t a[16]; - memset(a, 0, 16); - wr_be32(a, *out_ip); - uint32_t ttl_ms = ttl_s > (0xFFFFFFFFu / 1000u) ? 0xFFFFFFFFu : ttl_s * 1000u; - dns_cache_put_ip(hostname, 1, a, ttl_ms); - } - return mr; - } - - dns_result_t res = DNS_ERR_NO_DNS; - net_l4_endpoint p, s; - - if (pick_dns_on_l3(l3_id, &p, &s)) res = query_with_selection_a(&p, &s, which, hostname, timeout_ms, out_ip); - - if (res != DNS_OK && is_local) { - uint32_t ttl_s = 0; - dns_result_t mr = mdns_resolve_a(hostname, timeout_ms > MDNS_TIMEOUT_A_MS ? MDNS_TIMEOUT_A_MS : timeout_ms, out_ip, &ttl_s); - if (mr == DNS_OK) { - uint8_t a[16]; - memset(a, 0, 16); - wr_be32(a, *out_ip); - uint32_t ttl_ms = ttl_s > (0xFFFFFFFFu / 1000u) ? 0xFFFFFFFFu : ttl_s * 1000u; - dns_cache_put_ip(hostname, 1, a, ttl_ms); - return DNS_OK; - } - } + uint8_t addr[16]; + uint32_t ttl_s = 0; + uint32_t mdns_timeout = timeout_ms > MDNS_TIMEOUT_A_MS ? MDNS_TIMEOUT_A_MS : timeout_ms; + dns_result_t res; + if (dns_wire_is_local_name(hostname)) res = dns_resolve_ip_common(0,0, hostname, DNS_TYPE_A, addr, which, mdns_timeout, &ttl_s); + else res = dns_resolve_ip_common(0, 0, hostname, DNS_TYPE_A, addr, which, timeout_ms, &ttl_s); + if (res != DNS_OK) return res; - return res; + *out_ip = rd_be32(addr); + return DNS_OK; } -dns_result_t dns_resolve_aaaa(const char* hostname, uint8_t out_ipv6[16], dns_server_sel_t which, uint32_t timeout_ms){ - if (!hostname || !out_ipv6) return DNS_ERR_FORMAT; - if (dns_cache_get_ip(hostname, 28, out_ipv6)) return DNS_OK; - - bool is_local = dns_is_local_name(hostname); - - if (is_local) { - uint32_t ttl_s = 0; - dns_result_t mr = mdns_resolve_aaaa(hostname, timeout_ms > MDNS_TIMEOUT_AAAA_MS ? MDNS_TIMEOUT_AAAA_MS : timeout_ms, out_ipv6, &ttl_s); - if (mr == DNS_OK) { - uint32_t ttl_ms = ttl_s > (0xFFFFFFFFu / 1000u) ? 0xFFFFFFFFu : ttl_s * 1000u; - dns_cache_put_ip(hostname, 28, out_ipv6, ttl_ms); - } - return mr; +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) { + if (!hostname || !out_ip) return DNS_ERR_FORMAT; + uint8_t cached[16]; + if (dns_cache_get_ip(hostname, DNS_TYPE_A, cached)) { + *out_ip = rd_be32(cached); + return DNS_OK; } - dns_result_t res = DNS_ERR_NO_DNS; - uint8_t l3 = 0; - net_l4_endpoint p, s; - if (pick_dns_first_iface(&l3, &p, &s)) res = query_with_selection_aaaa(&p, &s, which, hostname, timeout_ms, out_ipv6); - - if (res != DNS_OK && is_local) { - uint32_t ttl_s = 0; - dns_result_t mr = mdns_resolve_aaaa(hostname, timeout_ms > MDNS_TIMEOUT_AAAA_MS ? MDNS_TIMEOUT_AAAA_MS : timeout_ms, out_ipv6, &ttl_s); - if (mr == DNS_OK) { - uint32_t ttl_ms = ttl_s > (0xFFFFFFFFu / 1000u) ? 0xFFFFFFFFu : ttl_s * 1000u; - dns_cache_put_ip(hostname, 28, out_ipv6, ttl_ms); - return DNS_OK; - } - } + uint8_t addr[16]; + uint32_t ttl_s = 0; + uint32_t mdns_timeout = timeout_ms > MDNS_TIMEOUT_A_MS ? MDNS_TIMEOUT_A_MS : timeout_ms; + dns_result_t res; + if (dns_wire_is_local_name(hostname)) res = dns_resolve_ip_common(1, l3_id, hostname, DNS_TYPE_A, addr, which, mdns_timeout, &ttl_s); + else res = dns_resolve_ip_common(1, l3_id, hostname, DNS_TYPE_A, addr, which, timeout_ms, &ttl_s); + if (res != DNS_OK) return res; - return res; + *out_ip = rd_be32(addr); + return DNS_OK; } -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){ +dns_result_t dns_resolve_aaaa(const char* hostname, uint8_t out_ipv6[16], dns_server_sel_t which, uint32_t timeout_ms) { if (!hostname || !out_ipv6) return DNS_ERR_FORMAT; - if (dns_cache_get_ip(hostname, 28, out_ipv6)) return DNS_OK; - bool is_local = dns_is_local_name(hostname); - if (is_local) { - uint32_t ttl_s = 0; - dns_result_t mr = mdns_resolve_aaaa(hostname, timeout_ms > MDNS_TIMEOUT_AAAA_MS ? MDNS_TIMEOUT_AAAA_MS : timeout_ms, out_ipv6, &ttl_s); - if (mr == DNS_OK) { - uint32_t ttl_ms = ttl_s > (0xFFFFFFFFu / 1000u) ? 0xFFFFFFFFu : ttl_s * 1000u; - dns_cache_put_ip(hostname, 28, out_ipv6, ttl_ms); - } - return mr; - } + if (dns_cache_get_ip(hostname, DNS_TYPE_AAAA, out_ipv6)) return DNS_OK; - dns_result_t res = DNS_ERR_NO_DNS; - net_l4_endpoint p, s; - if (pick_dns_on_l3(l3_id, &p, &s)) res = query_with_selection_aaaa(&p, &s, which, hostname, timeout_ms, out_ipv6); - - if (res != DNS_OK && is_local) { - uint32_t ttl_s = 0; - dns_result_t mr = mdns_resolve_aaaa(hostname, timeout_ms > MDNS_TIMEOUT_AAAA_MS ? MDNS_TIMEOUT_AAAA_MS : timeout_ms, out_ipv6, &ttl_s); - if (mr == DNS_OK) { - uint32_t ttl_ms = ttl_s > (0xFFFFFFFFu / 1000u) ? 0xFFFFFFFFu : ttl_s * 1000u; - dns_cache_put_ip(hostname, 28, out_ipv6, ttl_ms); - return DNS_OK; - } - } + uint32_t ttl_s = 0; + uint32_t mdns_timeout = timeout_ms > MDNS_TIMEOUT_AAAA_MS ? MDNS_TIMEOUT_AAAA_MS : timeout_ms; + if (dns_wire_is_local_name(hostname)) return dns_resolve_ip_common(0, 0, hostname, DNS_TYPE_AAAA, out_ipv6, which, mdns_timeout, &ttl_s); + return dns_resolve_ip_common(0, 0, hostname, DNS_TYPE_AAAA, out_ipv6, which, timeout_ms, &ttl_s); +} - return res; +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) { + if (!hostname || !out_ipv6) return DNS_ERR_FORMAT; + if (dns_cache_get_ip(hostname, DNS_TYPE_AAAA, out_ipv6)) return DNS_OK; + uint32_t ttl_s = 0; + uint32_t mdns_timeout = timeout_ms > MDNS_TIMEOUT_AAAA_MS ? MDNS_TIMEOUT_AAAA_MS : timeout_ms; + if (dns_wire_is_local_name(hostname)) return dns_resolve_ip_common(1, l3_id, hostname, DNS_TYPE_AAAA, out_ipv6, which, mdns_timeout, &ttl_s); + return dns_resolve_ip_common(1, l3_id, hostname, DNS_TYPE_AAAA, out_ipv6, which, timeout_ms, &ttl_s); } diff --git a/kernel/networking/application_layer/dns/dns.h b/kernel/networking/application_layer/dns/dns.h index f20ddfe3..4d5d1862 100644 --- a/kernel/networking/application_layer/dns/dns.h +++ b/kernel/networking/application_layer/dns/dns.h @@ -1,5 +1,6 @@ #pragma once #include "networking/transport_layer/csocket_udp.h" +#include "networking/application_layer/dns/dns_wire.h" #ifdef __cplusplus extern "C" { #endif @@ -20,6 +21,10 @@ typedef enum { DNS_USE_BOTH = 2 } dns_server_sel_t; +typedef uint16_t dns_qtype_t; + +dns_result_t dns_query(const char* hostname, dns_qtype_t qtype, dns_record_t* out_records, uint32_t max_records, uint32_t* out_count, dns_server_sel_t which, uint32_t timeout_ms); +dns_result_t dns_query_on_l3(uint8_t l3_id, const char* hostname, dns_qtype_t qtype, dns_record_t* out_records, uint32_t max_records, uint32_t* out_count, dns_server_sel_t which, uint32_t timeout_ms); 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); diff --git a/kernel/networking/application_layer/dns/dns_cache.c b/kernel/networking/application_layer/dns/dns_cache.c index f08fca98..b45e9c8c 100644 --- a/kernel/networking/application_layer/dns/dns_cache.c +++ b/kernel/networking/application_layer/dns/dns_cache.c @@ -1,9 +1,10 @@ #include "dns_cache.h" +#include "dns_wire.h" #include "std/std.h" typedef struct { uint8_t in_use; - uint8_t rr_type; + uint16_t rr_type; uint32_t name_len; char name[128]; uint32_t ttl_ms; @@ -20,23 +21,23 @@ static void dns_cache_ensure_init(void) { uint8_t a[16]; memset(a, 0, sizeof(a)); wr_be32(a, 0x7F000001u); - dns_cache_put_ip("localhost", 1, a, 0xFFFFFFFFu); + dns_cache_put_ip("localhost", DNS_TYPE_A, a, 0xFFFFFFFFu); uint8_t v6[16]; memset(v6, 0, sizeof(v6)); v6[15] = 1; - dns_cache_put_ip("localhost", 28, v6, 0xFFFFFFFFu); + dns_cache_put_ip("localhost", DNS_TYPE_AAAA, v6, 0xFFFFFFFFu); } -void dns_cache_put_ip(const char* name, uint8_t rr_type,const uint8_t addr[16], uint32_t ttl_ms) { +void dns_cache_put_ip(const char* name, uint16_t rr_type,const uint8_t addr[16], uint32_t ttl_ms) { if (!name || !addr) return; - uint32_t nlen = strlen(name); + char norm[128]; + if (!dns_wire_name_normalize(name, norm, sizeof(norm))) return; + uint32_t nlen = strlen(norm); if (!nlen) return; - if (nlen >= 128) return; if (!ttl_ms) return; - if (nlen == 9u&& strncmp(name, "localhost", 9) == 0 && (rr_type == 1 || rr_type == 28))ttl_ms = 0xFFFFFFFFu; - + if (nlen == 9u&& memcmp(norm, "localhost", 9) == 0 && (rr_type == DNS_TYPE_A || rr_type == DNS_TYPE_AAAA)) ttl_ms = 0xFFFFFFFF; int free_i = -1; for (int i = 0; i < 32; i++) { if (!g_dns_cache[i].in_use) { @@ -45,7 +46,7 @@ void dns_cache_put_ip(const char* name, uint8_t rr_type,const uint8_t addr[16], } if (g_dns_cache[i].rr_type != rr_type) continue; if (g_dns_cache[i].name_len != nlen) continue; - if (strncmp(g_dns_cache[i].name, name, (int)nlen) != 0) continue; + if (memcmp(g_dns_cache[i].name, norm, nlen) != 0) continue; memcpy(g_dns_cache[i].addr, addr, 16); g_dns_cache[i].ttl_ms = ttl_ms; return; @@ -57,24 +58,39 @@ void dns_cache_put_ip(const char* name, uint8_t rr_type,const uint8_t addr[16], g_dns_cache[idx].in_use = 1; g_dns_cache[idx].rr_type = rr_type; g_dns_cache[idx].name_len = nlen; - memcpy(g_dns_cache[idx].name, name, nlen); + memcpy(g_dns_cache[idx].name, norm, nlen); g_dns_cache[idx].name[nlen] = 0; g_dns_cache[idx].ttl_ms = ttl_ms; memcpy(g_dns_cache[idx].addr, addr, 16); } -bool dns_cache_get_ip(const char* name, uint8_t rr_type, uint8_t out_addr[16]) { +void dns_cache_remove_ip(const char* name, uint16_t rr_type) { + char norm[128]; + if (!dns_wire_name_normalize(name, norm, sizeof(norm))) return; + uint32_t nlen = strlen(norm); + for (int i = 0; i < 32; i++) { + if (!g_dns_cache[i].in_use) continue; + if (g_dns_cache[i].rr_type != rr_type) continue; + if (g_dns_cache[i].name_len != nlen) continue; + if (memcmp(g_dns_cache[i].name, norm, nlen) != 0) continue; + memset(&g_dns_cache[i], 0, sizeof(g_dns_cache[i])); + return; + } +} + +bool dns_cache_get_ip(const char* name, uint16_t rr_type, uint8_t out_addr[16]) { dns_cache_ensure_init(); if (!name || !out_addr) return false; - uint32_t nlen = strlen(name); + char norm[128]; + if (!dns_wire_name_normalize(name, norm, sizeof(norm))) return false; + uint32_t nlen = strlen(norm); if (!nlen) return false; - if (nlen >= 128) return false; for (int i = 0; i < 32; i++) { if (!g_dns_cache[i].in_use) continue; if (g_dns_cache[i].rr_type != rr_type) continue; if (g_dns_cache[i].ttl_ms == 0) continue; if (g_dns_cache[i].name_len != nlen) continue; - if (strncmp(g_dns_cache[i].name, name, (int)nlen) != 0) continue; + if (memcmp(g_dns_cache[i].name, norm, nlen) != 0) continue; memcpy(out_addr, g_dns_cache[i].addr, 16); return true; } diff --git a/kernel/networking/application_layer/dns/dns_cache.h b/kernel/networking/application_layer/dns/dns_cache.h index 7c3c6fef..4da0cb74 100644 --- a/kernel/networking/application_layer/dns/dns_cache.h +++ b/kernel/networking/application_layer/dns/dns_cache.h @@ -5,8 +5,9 @@ extern "C" { #endif -bool dns_cache_get_ip(const char* name, uint8_t rr_type, uint8_t out_addr[16]); -void dns_cache_put_ip(const char* name, uint8_t rr_type,const uint8_t addr[16], uint32_t ttl_ms); +bool dns_cache_get_ip(const char* name, uint16_t rr_type, uint8_t out_addr[16]); +void dns_cache_put_ip(const char* name, uint16_t rr_type,const uint8_t addr[16], uint32_t ttl_ms); +void dns_cache_remove_ip(const char* name, uint16_t rr_type); void dns_cache_tick(uint32_t ms); #ifdef __cplusplus diff --git a/kernel/networking/application_layer/dns/dns_daemon.c b/kernel/networking/application_layer/dns/dns_daemon.c index f0e55f19..9f9065e4 100644 --- a/kernel/networking/application_layer/dns/dns_daemon.c +++ b/kernel/networking/application_layer/dns/dns_daemon.c @@ -40,7 +40,7 @@ static socket_handle_t mdns_create_socket(ip_version_t ver, const void* group) { memset(&spec, 0, sizeof(spec)); spec.kind = BIND_ANY; - if(socket_bind_udp_ex(s, &spec, DNS_SD_MDNS_PORT) != SOCK_OK){ + if(socket_bind_udp_ex(s, &spec, DNS_MDNS_PORT) != SOCK_OK){ socket_destroy_udp(s); return 0; } @@ -54,10 +54,12 @@ int dns_deamon_entry(int argc, char* argv[]){ g_sock = udp_socket_create(SOCK_ROLE_CLIENT, g_pid_dnsd, NULL); uint32_t mdns_v4 = IPV4_MCAST_MDNS; + uint8_t mdns_v4_addr[4]; uint8_t mdns_v6[16]; + memcpy(mdns_v4_addr, &mdns_v4, 4); ipv6_make_multicast(0x02, IPV6_MCAST_MDNS, 0, mdns_v6); - g_sock_mdns4 = mdns_create_socket(IP_VER4, &mdns_v4); + g_sock_mdns4 = mdns_create_socket(IP_VER4, mdns_v4_addr); g_sock_mdns6 = mdns_create_socket(IP_VER6, mdns_v6); uint32_t tick_ms = 100; @@ -69,16 +71,22 @@ int dns_deamon_entry(int argc, char* argv[]){ if (g_sock_mdns4) { memset(&src, 0, sizeof(src)); int64_t r4 = socket_recvfrom_udp_ex(g_sock_mdns4, buf, sizeof(buf), &src); - if(r4 > 0) mdns_responder_handle_query(g_sock_mdns4, IP_VER4, (const uint8_t*)&mdns_v4, buf, (uint32_t)r4, &src); + if(r4 > 0) { + uint32_t len4 = r4; + mdns_responder_handle_query(g_sock_mdns4, IP_VER4, mdns_v4_addr, buf, len4, &src); + } } if (g_sock_mdns6) { memset(&src, 0, sizeof(src)); int64_t r6 = socket_recvfrom_udp_ex(g_sock_mdns6, buf, sizeof(buf), &src); - if(r6 > 0) mdns_responder_handle_query(g_sock_mdns6, IP_VER6, mdns_v6, buf, (uint32_t)r6, &src); + if(r6 > 0) { + uint32_t len6 = r6; + mdns_responder_handle_query(g_sock_mdns6, IP_VER6, mdns_v6, buf, len6, &src); + } } - mdns_responder_tick(g_sock_mdns4,g_sock_mdns6,(const uint8_t*)&mdns_v4,mdns_v6); + mdns_responder_tick(g_sock_mdns4,g_sock_mdns6,mdns_v4_addr,mdns_v6); msleep(tick_ms); } return 1; diff --git a/kernel/networking/application_layer/dns/dns_mdns.c b/kernel/networking/application_layer/dns/dns_mdns.c index d4a29226..3dbd9f56 100644 --- a/kernel/networking/application_layer/dns/dns_mdns.c +++ b/kernel/networking/application_layer/dns/dns_mdns.c @@ -4,198 +4,43 @@ #include "std/std.h" #include "networking/transport_layer/trans_utils.h" -#define MDNS_PORT 5353 - -static uint32_t skip_dns_name(const uint8_t* message, uint32_t message_len, uint32_t offset) { - if (offset >= message_len) return message_len + 1; - uint32_t cursor = offset; - while (cursor < message_len) { - uint8_t len = message[cursor]; - cursor++; - if (len == 0) break; - if ((len & 0xC0) == 0xC0) { - if (cursor >= message_len) return message_len + 1; - cursor++; - break; - } - cursor +=len; - if (cursor > message_len) return message_len + 1; - } - return cursor; -} - -static bool read_dns_name(const uint8_t* message, uint32_t message_len, uint32_t offset, char* out, uint32_t out_cap, uint32_t* consumed) { - if (!message) return false; - if (!out) return false; - if (!out_cap) return false; - if (offset >= message_len) return false; - - uint32_t cur = offset; - uint32_t out_len = 0; - uint32_t consumed_local = 0; - uint8_t jumped = 0; - uint32_t jumps = 0; - - for (;;){ - if (cur >= message_len) return false; - - uint8_t len = message[cur]; - if (len == 0) { - if (!jumped) consumed_local = cur - offset + 1; - if (out_len >= out_cap) return false; - out[out_len] = 0; - if (consumed) *consumed = consumed_local; - return true; - } - - if ((len & 0xC0) == 0xC0) { - if (cur + 1 >= message_len) return false; - uint16_t ptr = (uint16_t)(((uint16_t)(len & 0x3F) << 8) | (uint16_t)message[cur + 1]); - if (ptr >= message_len) return false; - if (!jumped) consumed_local = cur - offset + 2; - cur = ptr; - jumped = 1; - jumps++; - if (jumps> 16) return false; - continue; - } - - cur++; - if (cur + len > message_len) return false; - - if (out_len) { - if (out_len + 1 >= out_cap) return false; - out[out_len++] = '.'; - } - - if (out_len + len >= out_cap) return false; - memcpy(out + out_len, message + cur, len); - out_len += len; - cur += len; - - if (!jumped) consumed_local = cur - offset; - } -} - -static dns_result_t parse_mdns_ip_record(const uint8_t* buffer, uint32_t buffer_len, const char* name, uint16_t qtype, uint8_t* out_rdata, uint32_t out_len, uint32_t* out_ttl_s) { - if (!buffer) return DNS_ERR_FORMAT; - if (buffer_len < 12) return DNS_ERR_FORMAT; - if (!name) return DNS_ERR_FORMAT; - if (!out_rdata) return DNS_ERR_FORMAT; - if (!out_len) return DNS_ERR_FORMAT; - - uint16_t qd = rd_be16(buffer + 4); - uint16_t an = rd_be16(buffer + 6); - uint16_t ns = rd_be16(buffer + 8); - uint16_t ar = rd_be16(buffer + 10); - - uint32_t offset = 12; - for (uint16_t i = 0; i < qd; ++i) { - offset = skip_dns_name(buffer, buffer_len, offset); - if (offset+4 > buffer_len) return DNS_ERR_FORMAT; - offset += 4; - } - - uint32_t total = (uint32_t)an + (uint32_t)ns + (uint32_t)ar; - uint32_t name_len = (uint32_t)strlen(name); - - for (uint32_t i = 0; i < total; ++i) { - char rrname[256]; - uint32_t consumed = 0; - if (!read_dns_name(buffer, buffer_len, offset, rrname, sizeof(rrname), &consumed)) return DNS_ERR_FORMAT; - offset += consumed; - - 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); - uint32_t ttl_s = rd_be32(buffer + offset + 4); - uint16_t rdlen = rd_be16(buffer + offset + 8); - offset += 10; - - if (offset + rdlen > buffer_len) return DNS_ERR_FORMAT; - - if (type == qtype && (klass & 0x7FFFu) == 1u){ - uint32_t rrname_len = (uint32_t)strlen(rrname); - if (rrname_len == name_len&& strncmp(rrname, name, (int)name_len) == 0 && rdlen == out_len) { - memcpy(out_rdata, buffer + offset, out_len); - if (out_ttl_s) *out_ttl_s = ttl_s; - return DNS_OK; - } - } - - offset += rdlen; - } - - return DNS_ERR_NO_ANSWER; -} - -static bool dns_write_qname(uint8_t* buf, uint32_t buf_len, uint32_t*inout_off, const char* name) { - if (!buf || !inout_off || !name) return false; - uint32_t off = *inout_off; - if (off >= buf_len) return false; - uint32_t label_len = 0; - uint32_t label_pos = off; - buf[off++] = 0; - for (const char* p = name; *p; ++p) { - char c = *p; - if (c =='.') { - if (!label_len || label_len > 63u) return false; - buf[label_pos] = (uint8_t)label_len; - label_len = 0; - label_pos = off; - if (off >= buf_len) return false; - buf[off++] = 0; - continue; - } - if (label_len >= 63u) return false; - if (off >= buf_len) return false; - buf[off++]= (uint8_t)c; - label_len++; - } - if (!label_len || label_len > 63u) return false; - buf[label_pos] = (uint8_t)label_len; - if (off >= buf_len) return false; - buf[off++] = 0; - *inout_off = off; - return true; -} - -static dns_result_t perform_mdns_query_once(socket_handle_t sock, const net_l4_endpoint* dst, const char* name, uint16_t qtype, uint32_t timeout_ms, uint8_t* out_rdata, uint32_t out_len, uint32_t* out_ttl_s) { +static dns_result_t perform_mdns_query_once(socket_handle_t sock, const net_l4_endpoint *dst, const char *name, dns_qtype_t qtype, uint32_t timeout_ms, dns_record_t *out_records, uint32_t max_records, uint32_t *out_count) { + if (out_count) *out_count = 0; if (!sock) return DNS_ERR_NO_DNS; if (!dst) return DNS_ERR_NO_DNS; if (!name) return DNS_ERR_FORMAT; - if (!out_rdata) return DNS_ERR_FORMAT; - if (!out_len) return DNS_ERR_FORMAT; + if (!out_records && max_records) return DNS_ERR_FORMAT; uint8_t request_buffer[512]; - memset(request_buffer, 0, sizeof(request_buffer)); - - wr_be16(request_buffer + 0, 0); - wr_be16(request_buffer + 2, 0x0000); - wr_be16(request_buffer + 4, 1); - - uint32_t offset = 12; - if (!dns_write_qname(request_buffer, (uint32_t)sizeof(request_buffer), &offset, name)) return DNS_ERR_FORMAT; - if (offset + 4 > (uint32_t)sizeof(request_buffer)) return DNS_ERR_FORMAT; - wr_be16(request_buffer + offset + 0, qtype); - wr_be16(request_buffer + offset + 2, 0x0001); - offset += 4; + uint32_t offset = dns_wire_build_query(request_buffer, sizeof(request_buffer), 0, name, qtype, false); + if (!offset) return DNS_ERR_FORMAT; int64_t sent = socket_sendto_udp_ex(sock, DST_ENDPOINT, dst, 0, request_buffer, offset); if (sent < 0) return DNS_ERR_SEND; + uint32_t found = 0; 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 == MDNS_PORT){ - uint32_t ttl_s = 0; - dns_result_t pr = parse_mdns_ip_record(response_buffer, (uint32_t)received, name, qtype, out_rdata, out_len, &ttl_s); - if (pr == DNS_OK){ - if (out_ttl_s) *out_ttl_s = ttl_s; - return DNS_OK; + if (received > 0 && source.port == DNS_MDNS_PORT){ + uint32_t received_len = received; + dns_record_t records[12]; + uint32_t count = 0; + uint16_t flags = 0; + if (dns_wire_parse_records(response_buffer, received_len, false, 0, records,12, &count, &flags) && (flags & DNS_FLAG_QR)) { + for (uint32_t i = 0; i < count; i++) { + if ((records[i].rrclass & DNS_CLASS_MASK) != DNS_CLASS_IN) continue; + if (qtype != DNS_TYPE_ANY && records[i].type != qtype) continue; + if (!dns_wire_name_equals(records[i].name, name)) continue; + if (found < max_records) out_records[found] = records[i]; + found++; + if (found >= max_records && max_records) { + if (out_count) *out_count = max_records; + return DNS_OK; + } + } } } @@ -203,44 +48,93 @@ static dns_result_t perform_mdns_query_once(socket_handle_t sock, const net_l4_e waited_ms += 20; } + if (found) { + if (out_count) *out_count = found < max_records ? found : max_records; + return DNS_OK; + } + return DNS_ERR_TIMEOUT; } -dns_result_t mdns_resolve_a(const char* name, uint32_t timeout_ms, uint32_t* out_ip, uint32_t* out_ttl_s) { - socket_handle_t sock = mdns_socket_handle_v4(); - if (!sock) return DNS_ERR_NO_DNS; +dns_result_t mdns_query(const char* name, dns_qtype_t qtype, uint32_t timeout_ms, dns_record_t* out_records, uint32_t max_records, uint32_t* out_count) { + if (out_count) *out_count = 0; + if (!name) return DNS_ERR_FORMAT; + if (!out_records && max_records) return DNS_ERR_FORMAT; + + dns_result_t last = DNS_ERR_NO_DNS; + uint32_t total = 0; + + socket_handle_t sock4 = mdns_socket_handle_v4(); + if (sock4) { + uint32_t group =DNS_MDNS_GROUP_V4; + net_l4_endpoint dst; + make_ep(group, DNS_MDNS_PORT, IP_VER4, &dst); + uint32_t got = 0; + last = perform_mdns_query_once(sock4, &dst, name, qtype, timeout_ms, out_records, max_records, &got); + if (last == DNS_OK) total = got; + if (total >= max_records && max_records) { + if (out_count) *out_count =total; + return DNS_OK; + } + } + + socket_handle_t sock6 = mdns_socket_handle_v6(); + if (sock6) { + net_l4_endpoint dst; + memset(&dst, 0, sizeof(dst)); + dst.ver = IP_VER6; + ipv6_make_multicast(0x02, IPV6_MCAST_MDNS, 0, dst.ip); + dst.port = DNS_MDNS_PORT; + uint32_t got = 0; + dns_record_t *dst_records = out_records ? out_records + total : 0; + uint32_t left = max_records > total ? max_records - total : 0; + dns_result_t r6 = perform_mdns_query_once(sock6, &dst, name, qtype, timeout_ms, dst_records, left, &got); + if (r6 == DNS_OK) { + total += got; + last = DNS_OK; + } else if (last != DNS_OK) last = r6; + } + + if (total) { + if (out_count) *out_count = total; + return DNS_OK; + } + + return last; +} - uint32_t group = 0xE00000FBu; - net_l4_endpoint dst; - make_ep(group, MDNS_PORT, IP_VER4, &dst); +dns_result_t mdns_resolve_a(const char* name, uint32_t timeout_ms, uint32_t* out_ip, uint32_t* out_ttl_s) { + if (!out_ip) return DNS_ERR_FORMAT; - uint8_t rdata[4]; - uint32_t ttl_s = 0; + dns_record_t records[4]; + uint32_t count = 0; - dns_result_t r = perform_mdns_query_once(sock, &dst, name, 1, timeout_ms, rdata, 4, &ttl_s); + dns_result_t r = mdns_query(name, DNS_TYPE_A, timeout_ms, records, 4, &count); if (r != DNS_OK) return r; - uint32_t ip; - memcpy(&ip, rdata, 4); - if (out_ip) *out_ip = rd_be32((uint8_t*)&ip); - if (out_ttl_s) *out_ttl_s = ttl_s; - return DNS_OK; + for (uint32_t i = 0; i < count; i++) { + if (records[i].type != DNS_TYPE_A) continue; + *out_ip = rd_be32(records[i].addr); + if (out_ttl_s) *out_ttl_s = records[i].ttl_s; + return DNS_OK; + } + + return DNS_ERR_NO_ANSWER; } dns_result_t mdns_resolve_aaaa(const char* name, uint32_t timeout_ms, uint8_t out_ipv6[16], uint32_t* out_ttl_s) { - socket_handle_t sock = mdns_socket_handle_v6(); - if (!sock) return DNS_ERR_NO_DNS; - - net_l4_endpoint dst; - memset(&dst, 0, sizeof(dst)); - dst.ver = IP_VER6; - ipv6_make_multicast(0x02, IPV6_MCAST_MDNS, 0, dst.ip); - dst.port = MDNS_PORT; - - uint32_t ttl_s = 0; - dns_result_t r = perform_mdns_query_once(sock, &dst, name, 28, timeout_ms, out_ipv6, 16, &ttl_s); + if (!out_ipv6) return DNS_ERR_FORMAT; + dns_record_t records[4]; + uint32_t count = 0; + dns_result_t r = mdns_query(name, DNS_TYPE_AAAA, timeout_ms, records, 4, &count); if (r != DNS_OK) return r; - if (out_ttl_s) *out_ttl_s = ttl_s; - return DNS_OK; + for (uint32_t i = 0; i < count; i++) { + if (records[i].type != DNS_TYPE_AAAA) continue; + memcpy(out_ipv6, records[i].addr, 16); + if (out_ttl_s) *out_ttl_s = records[i].ttl_s; + return DNS_OK; + } + + return DNS_ERR_NO_ANSWER; } diff --git a/kernel/networking/application_layer/dns/dns_mdns.h b/kernel/networking/application_layer/dns/dns_mdns.h index e5a84ea5..10d5d8c1 100644 --- a/kernel/networking/application_layer/dns/dns_mdns.h +++ b/kernel/networking/application_layer/dns/dns_mdns.h @@ -2,5 +2,6 @@ #include "dns.h" #include "types.h" +dns_result_t mdns_query(const char* name, dns_qtype_t qtype, uint32_t timeout_ms, dns_record_t* out_records, uint32_t max_records, uint32_t* out_count); dns_result_t mdns_resolve_a(const char* name, uint32_t timeout_ms, uint32_t* out_ip, uint32_t* out_ttl_s); dns_result_t mdns_resolve_aaaa(const char* name, uint32_t timeout_ms, uint8_t out_ipv6[16], uint32_t* out_ttl_s); \ No newline at end of file diff --git a/kernel/networking/application_layer/dns/dns_sd.c b/kernel/networking/application_layer/dns/dns_sd.c index 5b53aac2..e7d7baeb 100644 --- a/kernel/networking/application_layer/dns/dns_sd.c +++ b/kernel/networking/application_layer/dns/dns_sd.c @@ -1,122 +1,54 @@ #include "dns_sd.h" #include "std/std.h" -uint32_t dns_sd_encode_qname(uint8_t* out, uint32_t cap, uint32_t off, const char* name) { - if(!out) return 0; - if(!cap) return 0; - if(off >= cap) return 0; - if(!name) return 0; - - uint32_t idx = off; - uint32_t lab_len = 0; - uint32_t lab_pos = idx; - - out[idx] = 0; - idx++; - - while (*name) { - if(*name == '.') { - if(lab_len > 63) return 0; - out[lab_pos] = (uint8_t)lab_len; - lab_len = 0; - lab_pos = idx; - if(idx >= cap) return 0; - out[idx] = 0; - idx++; - name++; - continue; - } - - if(idx >= cap) return 0; - out[idx] = (uint8_t)(*name); - idx++; - name++; - lab_len++; - if(lab_len > 63) return 0; - } - - if(lab_len > 63) return 0; - if(lab_pos >= cap) return 0; - out[lab_pos] = (uint8_t)lab_len; - if(idx >= cap) return 0; - out[idx] = 0; - idx++; - return idx; -} - -uint32_t dns_sd_put_u16(uint8_t* out, uint32_t cap, uint32_t off, uint16_t v) { - if(!out) return 0; - if(off + 2 > cap) return 0; - uint16_t t = be16(v); - memcpy(out + off, &t, 2); - return off + 2; -} - -uint32_t dns_sd_put_u32(uint8_t* out, uint32_t cap, uint32_t off, uint32_t v) { - if(!out) return 0; - if(off + 4 > cap) return 0; - uint32_t t = be32(v); - memcpy(out + off, &t, 4); - return off + 4; -} - -uint32_t dns_sd_add_rr_ptr(uint8_t* out, uint32_t cap, uint32_t off, const char* name, uint16_t rrclass, uint32_t ttl_s, const char* target) { - off = dns_sd_encode_qname(out, cap, off, name); - if(!off) return 0; - - off = dns_sd_put_u16(out, cap, off, DNS_SD_TYPE_PTR); +uint32_t dns_sd_add_rr_ptr(uint8_t* out, uint32_t cap, uint32_t off, const char* name, uint16_t rrclass, uint32_t ttl_s, const char *target) { + if (!dns_wire_write_name(out, cap, &off, name)) return 0; + off = dns_wire_put_u16(out, cap, off, DNS_TYPE_PTR); if(!off) return 0; - off = dns_sd_put_u16(out, cap, off, rrclass); + off = dns_wire_put_u16(out, cap, off, rrclass); if(!off) return 0; - off = dns_sd_put_u32(out, cap, off, ttl_s); + off = dns_wire_put_u32(out, cap, off, ttl_s); if(!off) return 0; uint32_t rdlen_pos = off; - off = dns_sd_put_u16(out, cap,off, 0); + off = dns_wire_put_u16(out, cap,off, 0); if(!off) return 0; uint32_t r0 = off; - off = dns_sd_encode_qname(out, cap, off, target); - if(!off) return 0; + if(!dns_wire_write_name(out, cap, &off, target)) return 0; uint16_t rdlen = (uint16_t)(off - r0); - uint16_t rdbe = be16(rdlen); - memcpy(out + rdlen_pos, &rdbe, 2); + wr_be16(out + rdlen_pos, rdlen); return off; } uint32_t dns_sd_add_rr_a(uint8_t* out, uint32_t cap, uint32_t off, const char* name, uint16_t rrclass, uint32_t ttl_s, uint32_t ip) { - off = dns_sd_encode_qname(out, cap, off, name); - if(!off) return 0; + if (!dns_wire_write_name(out, cap, &off, name)) return 0; - off = dns_sd_put_u16(out, cap,off,DNS_SD_TYPE_A); + off = dns_wire_put_u16(out, cap,off,DNS_TYPE_A); if(!off) return 0; - off = dns_sd_put_u16(out,cap, off, rrclass); + off = dns_wire_put_u16(out,cap, off, rrclass); if(!off) return 0; - off = dns_sd_put_u32(out, cap, off, ttl_s); + off = dns_wire_put_u32(out, cap, off, ttl_s); if(!off) return 0; - off = dns_sd_put_u16(out, cap, off, 4); + off = dns_wire_put_u16(out, cap, off, 4); if(!off) return 0; if(off + 4 > cap) return 0; - out[off + 0] = (uint8_t)(ip >> 24); - out[off + 1] = (uint8_t)(ip >> 16); - out[off + 2] = (uint8_t)(ip >> 8); - out[off + 3] = (uint8_t)(ip); + wr_be32(out + off, ip); return off + 4; } uint32_t dns_sd_add_rr_aaaa(uint8_t* out, uint32_t cap, uint32_t off, const char* name, uint16_t rrclass,uint32_t ttl_s, const uint8_t ip6[16]) { - off = dns_sd_encode_qname(out, cap, off, name); - if(!off) return 0; + if (!dns_wire_write_name(out, cap, &off, name)) return 0; - off = dns_sd_put_u16(out, cap, off, DNS_SD_TYPE_AAAA); + off = dns_wire_put_u16(out, cap, off, DNS_TYPE_AAAA); if(!off) return 0; - off = dns_sd_put_u16(out, cap, off, rrclass); + off = dns_wire_put_u16(out, cap, off, rrclass); if(!off) return 0; - off = dns_sd_put_u32(out, cap, off, ttl_s); + off = dns_wire_put_u32(out, cap, off, ttl_s); if(!off) return 0; - off = dns_sd_put_u16(out, cap, off, 16); + off = dns_wire_put_u16(out, cap, off, 16); if(!off) return 0; if(off + 16 > cap) return 0; @@ -131,35 +63,32 @@ uint32_t dns_sd_add_rr_srv(uint8_t* out, uint32_t cap, uint32_t off, const char* if(!name) return 0; if(!target) return 0; - off = dns_sd_encode_qname(out, cap, off, name); - if(!off) return 0; + if (!dns_wire_write_name(out, cap, &off, name)) return 0; - off = dns_sd_put_u16(out, cap, off, DNS_SD_TYPE_SRV); + off = dns_wire_put_u16(out, cap, off, DNS_TYPE_SRV); if(!off) return 0; - off = dns_sd_put_u16(out, cap, off, rrclass); + off = dns_wire_put_u16(out, cap, off, rrclass); if(!off) return 0; - off = dns_sd_put_u32(out, cap, off, ttl_s); + off = dns_wire_put_u32(out, cap, off, ttl_s); if(!off) return 0; uint32_t rdlen_pos = off; - off = dns_sd_put_u16(out, cap, off, 0); + off = dns_wire_put_u16(out, cap, off, 0); if(!off) return 0; uint32_t rdata_start = off; - off = dns_sd_put_u16(out, cap, off, priority); + off = dns_wire_put_u16(out, cap, off, priority); if(!off) return 0; - off = dns_sd_put_u16(out, cap, off, weight); + off = dns_wire_put_u16(out, cap, off, weight); if(!off) return 0; - off = dns_sd_put_u16(out, cap, off, port); + off = dns_wire_put_u16(out, cap, off, port); if(!off) return 0; - off = dns_sd_encode_qname(out, cap, off, target); - if(!off) return 0; + if (!dns_wire_write_name(out, cap, &off, target)) return 0; - uint16_t rdlen = (uint16_t)(off - rdata_start); - uint16_t t = be16(rdlen); - memcpy(out + rdlen_pos, &t, 2); + uint16_t rdlen = off - rdata_start; + wr_be16(out + rdlen_pos, rdlen); return off; } @@ -169,18 +98,17 @@ uint32_t dns_sd_add_rr_txt(uint8_t *out, uint32_t cap, uint32_t off, const char if(off >= cap) return 0; if(!name) return 0; - off = dns_sd_encode_qname(out, cap, off, name); - if(!off) return 0; + if (!dns_wire_write_name(out, cap, &off, name)) return 0; - off = dns_sd_put_u16(out, cap, off, DNS_SD_TYPE_TXT); + off = dns_wire_put_u16(out, cap, off, DNS_TYPE_TXT); if(!off) return 0; - off = dns_sd_put_u16(out, cap, off, rrclass); + off = dns_wire_put_u16(out, cap, off, rrclass); if(!off) return 0; - off = dns_sd_put_u32(out, cap, off, ttl_s); + off = dns_wire_put_u32(out, cap, off, ttl_s); if(!off) return 0; uint32_t rdlen_pos = off; - off = dns_sd_put_u16(out, cap, off, 0); + off = dns_wire_put_u16(out, cap, off, 0); if(!off) return 0; uint32_t rdata_start = off; @@ -188,7 +116,7 @@ uint32_t dns_sd_add_rr_txt(uint8_t *out, uint32_t cap, uint32_t off, const char if(txt && txt[0]){ const char* p = txt; while(*p) { - while(*p == ' ' || *p == '\t' || *p == ';' || *p == '\n' || *p == '\r') p++; + while(is_whitespace(*p) || *p == ';') p++; if(!*p) break; const char* start = p; @@ -203,12 +131,11 @@ uint32_t dns_sd_add_rr_txt(uint8_t *out, uint32_t cap, uint32_t off, const char memcpy(out + off, start,len); off += len; - while(*p == ' ' || *p == '\t' || *p == ';' || *p == '\n' || *p == '\r') p++; + while(is_whitespace(*p) || *p == ';') p++; } } uint16_t rdlen = (uint16_t)(off - rdata_start); - uint16_t t = be16(rdlen); - memcpy(out + rdlen_pos, &t, 2); + wr_be16(out + rdlen_pos, rdlen); return off; } diff --git a/kernel/networking/application_layer/dns/dns_sd.h b/kernel/networking/application_layer/dns/dns_sd.h index 312a432b..14e18a01 100644 --- a/kernel/networking/application_layer/dns/dns_sd.h +++ b/kernel/networking/application_layer/dns/dns_sd.h @@ -1,31 +1,14 @@ #pragma once #include "types.h" +#include "dns_wire.h" #ifdef __cplusplus extern "C" { #endif -#define DNS_SD_MDNS_PORT 5353 - -#define DNS_SD_TYPE_A 1 -#define DNS_SD_TYPE_PTR 12 -#define DNS_SD_TYPE_TXT 16 -#define DNS_SD_TYPE_SRV 33 -#define DNS_SD_TYPE_AAAA 28 -#define DNS_SD_TYPE_ANY 255 - -#define DNS_SD_CLASS_IN 1 - -#define DNS_SD_FLAG_QR 0x8000 -#define DNS_SD_FLAG_AA 0x0400 - #define DNS_SD_DOMAIN_LOCAL "local" #define DNS_SD_ENUM_SERVICES "_services._dns-sd._udp.local" -uint32_t dns_sd_encode_qname(uint8_t *out, uint32_t cap, uint32_t off, const char *name); -uint32_t dns_sd_put_u16(uint8_t *out, uint32_t cap, uint32_t off, uint16_t v); -uint32_t dns_sd_put_u32(uint8_t *out, uint32_t cap, uint32_t off, uint32_t v); - uint32_t dns_sd_add_rr_ptr(uint8_t *out, uint32_t cap, uint32_t off, const char *name, uint16_t rrclass, uint32_t ttl_s, const char *target); uint32_t dns_sd_add_rr_a(uint8_t *out, uint32_t cap, uint32_t off, const char *name, uint16_t rrclass, uint32_t ttl_s, uint32_t ip); uint32_t dns_sd_add_rr_aaaa(uint8_t *out, uint32_t cap, uint32_t off, const char *name, uint16_t rrclass, uint32_t ttl_s, const uint8_t ip6[16]); diff --git a/kernel/networking/application_layer/dns/dns_wire.c b/kernel/networking/application_layer/dns/dns_wire.c new file mode 100644 index 00000000..43365130 --- /dev/null +++ b/kernel/networking/application_layer/dns/dns_wire.c @@ -0,0 +1,312 @@ +#include "dns_wire.h" +#include "std/std.h" +#include "std/string.h" + +uint32_t dns_wire_put_u16(uint8_t *out, uint32_t cap, uint32_t off, uint16_t v) { + if (!out) return 0; + if (off + 2 > cap) return 0; + wr_be16(out + off, v); + return off + 2; +} + +uint32_t dns_wire_put_u32(uint8_t *out, uint32_t cap, uint32_t off, uint32_t v) { + if (!out) return 0; + if (off + 4 > cap) return 0; + wr_be32(out + off, v); + return off + 4; +} + +bool dns_wire_name_normalize(const char *name, char *out, uint32_t out_cap) { + if (!name) return false; + if (!out) return false; + if (!out_cap) return false; + + uint32_t in_len = strlen(name); + while (in_len && name[in_len - 1] == '.') in_len--; + if (!in_len) return false; + if (in_len >= out_cap) return false; + + uint32_t label_len = 0; + for (uint32_t i = 0; i < in_len; i++) { + char c = tolower(name[i]); + if (c == '.') { + if (!label_len) return false; + label_len = 0; + out[i] = '.'; + continue; + } + if (c <= 32) return false; + label_len++; + if (label_len > 63) return false; + out[i] = c; + } + + if (!label_len) return false; + out[in_len] = 0; + return true; +} + +bool dns_wire_name_equals(const char *a, const char *b) { + char na[DNS_WIRE_MAX_NAME]; + char nb[DNS_WIRE_MAX_NAME]; + if (!dns_wire_name_normalize(a, na, sizeof(na))) return false; + if (!dns_wire_name_normalize(b, nb, sizeof(nb))) return false; + return strcmp(na, nb) == 0; +} + +bool dns_wire_is_local_name(const char *name) { + char n[DNS_WIRE_MAX_NAME]; + if (!dns_wire_name_normalize(name, n, sizeof(n))) return false; + return strend(n, ".local") == 0; +} + +bool dns_wire_write_name(uint8_t *out, uint32_t cap, uint32_t *off, const char *name) { + if (!out) return false; + if (!off) return false; + if (!name) return false; + if (*off >= cap) return false; + + uint32_t in_len = strlen(name); + while (in_len && name[in_len - 1] == '.') in_len--; + if (!in_len) return false; + if (in_len >= DNS_WIRE_MAX_NAME) return false; + + uint32_t idx = *off; + uint32_t wire_len = 1; + uint32_t pos = 0; + while (pos < in_len) { + uint32_t start = pos; + uint32_t len = 0; + while (pos < in_len && name[pos] != '.') { + char c = name[pos]; + if (c <= 32) return false; + len++; + pos++; + } + if (!len || len > 63) return false; + wire_len += 1 + len; + if (wire_len > 255) return false; + if (idx + 1 + len > cap) return false; + + out[idx++] = len; + memcpy(out + idx, name + start, len); + idx += len; + + if (pos < in_len) { + if (name[pos] != '.') return false; + pos++; + if (pos == in_len) return false; + } + } + + if (idx >= cap) return false; + out[idx++] = 0; + *off = idx; + return true; +} + +bool dns_wire_read_name(const uint8_t *msg, uint32_t msg_len, uint32_t off, char *out, uint32_t out_cap, uint32_t *out_next) { + if (!msg) return false; + if (!out) return false; + if (!out_cap) return false; + if (off >= msg_len) return false; + + uint32_t idx = off; + uint32_t out_idx = 0; + uint32_t jumps = 0; + bool jumped = false; + + while (true) { + if (idx >= msg_len) return false; + uint8_t c = msg[idx]; + + if ((c & 0xC0) == 0xC0) { + if (idx + 1 >= msg_len) return false; + uint16_t ptr = ((c & 0x3F) << 8) | msg[idx+1]; + if (ptr >= msg_len) return false; + if (!jumped) { + if (out_next) *out_next = idx + 2; + jumped = true; + } + idx = ptr; + jumps++; + if (jumps > 16) return false; + continue; + } + + if (c & 0xC0) return false; + + idx++; + if (!c) { + if (!jumped && out_next) *out_next = idx; + if (!out_idx) { + if (out_cap < 2) return false; + out[0] = '.'; + out[1] = 0; + return true; + } + if (out_idx >= out_cap) return false; + out[out_idx] = 0; + return true; + } + + uint32_t label_len = c; + if (label_len > 63) return false; + if (idx + label_len > msg_len) return false; + + if (out_idx) { + if (out_idx + 1 >= out_cap) return false; + out[out_idx++] = '.'; + } + + if (out_idx + label_len >= out_cap) return false; + memcpy(out + out_idx, msg + idx, label_len); + out_idx += label_len; + idx += label_len; + } +} + +bool dns_wire_read_rr(const uint8_t *msg, uint32_t msg_len, uint32_t off, dns_section_t section, dns_rr_view_t *rr, uint32_t *out_next) { + if (!rr) return false; + memset(rr, 0, sizeof(*rr)); + if (!dns_wire_read_name(msg, msg_len, off, rr->name, sizeof(rr->name), &off)) return false; + if (off + 10 > msg_len) return false; + + rr->type = rd_be16(msg + off); + rr->rrclass = rd_be16(msg + off + 2); + rr->ttl_s = rd_be32(msg + off + 4); + rr->rdlen = rd_be16(msg + off + 8); + rr->rdata_off = off + 10; + rr->section = section; + if (rr->rdata_off + rr->rdlen > msg_len) return false; + if (out_next) *out_next = rr->rdata_off + rr->rdlen; + return true; +} + +bool dns_wire_parse_rdata(const uint8_t *msg, uint32_t msg_len, const dns_rr_view_t *rr, dns_record_t *out) { + if (!msg) return false; + if (!rr) return false; + if (!out) return false; + if (rr->rdata_off + rr->rdlen > msg_len) return false; + + memset(out, 0, sizeof(*out)); + strncpy(out->name, rr->name, sizeof(out->name)); + out->type = rr->type; + out->rrclass = rr->rrclass; + out->ttl_s = rr->ttl_s; + out->section = rr->section; + + const uint8_t *rdata = msg + rr->rdata_off; + if (rr->type == DNS_TYPE_A) { + if (rr->rdlen != 4)return false; + memcpy(out->addr, rdata, 4); + return true; + } + + if (rr->type == DNS_TYPE_AAAA) { + if (rr->rdlen != 16)return false; + memcpy(out->addr, rdata, 16); + return true; + } + + if (rr->type == DNS_TYPE_CNAME || rr->type == DNS_TYPE_PTR || rr->type == DNS_TYPE_NS) { + uint32_t next = 0; + if (!rr->rdlen)return false; + if (!dns_wire_read_name(msg, msg_len, rr->rdata_off, out->target, sizeof(out->target), &next)) return false; + return next == rr->rdata_off + rr->rdlen; + } + + if (rr->type == DNS_TYPE_TXT) { + uint32_t idx = 0; + uint32_t out_idx = 0; + out->txt[0] = 0; + while (idx < rr->rdlen) { + uint8_t len = rdata[idx++]; + if (idx + len > rr->rdlen) return false; + if (len) { + if (out_idx) { + if (out_idx+1 >= sizeof(out->txt)) return true; + out->txt[out_idx++] = ';'; + } + uint32_t copy = len; + if (out_idx + copy >= sizeof(out->txt)) copy = sizeof(out->txt) - out_idx - 1; + memcpy(out->txt + out_idx, rdata + idx, copy); + out_idx += copy; + } + idx += len; + } + out->txt[out_idx] = 0; + return true; + } + + if (rr->type == DNS_TYPE_SRV) { + if (rr->rdlen < 7) return false; + out->priority = rd_be16(rdata); + out->weight = rd_be16(rdata + 2); + out->port = rd_be16(rdata + 4); + uint32_t next = 0; + if (!dns_wire_read_name(msg, msg_len, rr->rdata_off + 6, out->target, sizeof(out->target), &next)) return false; + return next == rr->rdata_off + rr->rdlen; + } + + return true; +} + +bool dns_wire_parse_records(const uint8_t *msg, uint32_t msg_len, bool check_id, uint16_t message_id, dns_record_t *out, uint32_t out_cap, uint32_t *out_count, uint16_t *out_flags) { + if (out_count) *out_count = 0; + if (!msg) return false; + if (msg_len < 12) return false; + if (check_id && rd_be16(msg) != message_id) return false; + + uint16_t flags = rd_be16(msg+2); + uint16_t qd = rd_be16(msg+4); + uint16_t an = rd_be16(msg+6); + uint16_t ns = rd_be16(msg + 8); + uint16_t ar = rd_be16(msg + 10); + if (out_flags) *out_flags = flags; + + uint32_t off = 12; + for (uint16_t i = 0; i < qd; i++) { + if (!dns_wire_read_name(msg, msg_len, off, NULL, 0, &off)) return false; + if (off + 4 > msg_len) return false; + off += 4; + } + + uint32_t count = 0; + uint32_t section_count[3]; + dns_section_t section[3]; + section_count[0] = an; + section_count[1] = ns; + section_count[2] = ar; + section[0] = DNS_SECTION_ANSWER; + section[1] = DNS_SECTION_AUTHORITY; + section[2] = DNS_SECTION_ADDITIONAL; + + for (uint32_t s = 0; s < 3; s++) { + for (uint32_t i = 0; i < section_count[s]; i++) { + dns_rr_view_t rr; + if (!dns_wire_read_rr(msg, msg_len, off, section[s], &rr, &off)) return false; + if (out && count < out_cap && dns_wire_parse_rdata(msg, msg_len, &rr, &out[count])) count++; + } + } + + if (out_count) *out_count = count; + return true; +} + +uint32_t dns_wire_build_query(uint8_t *out, uint32_t cap, uint16_t message_id, const char *name,uint16_t qtype, bool mdns_qu) { + if (!out) return 0; + if (cap < 12) return 0; + + memset(out, 0, cap); + wr_be16(out, message_id); + wr_be16(out + 2, message_id ? DNS_FLAG_RD : 0); + wr_be16(out + 4,1); + + uint32_t off = 12; + if (!dns_wire_write_name(out, cap, &off, name)) return 0; + if (off + 4 > cap) return 0; + wr_be16(out + off, qtype); + wr_be16(out + off + 2, mdns_qu? DNS_CLASS_CACHE_FLUSH | DNS_CLASS_IN : DNS_CLASS_IN); + return off + 4; +} diff --git a/kernel/networking/application_layer/dns/dns_wire.h b/kernel/networking/application_layer/dns/dns_wire.h new file mode 100644 index 00000000..81a52ef2 --- /dev/null +++ b/kernel/networking/application_layer/dns/dns_wire.h @@ -0,0 +1,87 @@ +#pragma once +#include "types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define DNS_WIRE_MAX_NAME 256 +#define DNS_WIRE_MAX_TXT 256 + +#define DNS_CLASS_IN 1 +#define DNS_CLASS_ANY 255 +#define DNS_CLASS_CACHE_FLUSH 0x8000 +#define DNS_CLASS_MASK 0x7FFF + +#define DNS_FLAG_QR 0x8000 +#define DNS_FLAG_AA 0x0400 +#define DNS_FLAG_RD 0x0100 + +#define DNS_RCODE_MASK 0x000F +#define DNS_RCODE_NXDOMAIN 3 + +#define DNS_MDNS_PORT 5353 +#define DNS_MDNS_GROUP_V4 0xE00000FB + +#define DNS_TYPE_A 1 +#define DNS_TYPE_NS 2 +#define DNS_TYPE_CNAME 5 +#define DNS_TYPE_SOA 6 +#define DNS_TYPE_PTR 12 +#define DNS_TYPE_MX 15 +#define DNS_TYPE_TXT 16 +#define DNS_TYPE_AAAA 28 +#define DNS_TYPE_SRV 33 +#define DNS_TYPE_OPT 41 +#define DNS_TYPE_DS 43 +#define DNS_TYPE_NAPTR 35 +#define DNS_TYPE_CAA 257 +#define DNS_TYPE_ANY 255 + +//TODO add type handling for SOA OPT MX NS NAPTR DS CAA when needed + +typedef enum { + DNS_SECTION_ANSWER = 0, + DNS_SECTION_AUTHORITY = 1, + DNS_SECTION_ADDITIONAL = 2 +} dns_section_t; + +typedef struct { + char name[DNS_WIRE_MAX_NAME]; + uint16_t type; + uint16_t rrclass; + uint32_t ttl_s; + uint16_t rdlen; + uint32_t rdata_off; + dns_section_t section; +} dns_rr_view_t; + +typedef struct { + char name[DNS_WIRE_MAX_NAME]; + uint16_t type; + uint16_t rrclass; + uint32_t ttl_s; + dns_section_t section; + uint8_t addr[16]; + char target[DNS_WIRE_MAX_NAME]; + char txt[DNS_WIRE_MAX_TXT]; + uint16_t priority; + uint16_t weight; + uint16_t port; +} dns_record_t; + +uint32_t dns_wire_put_u16(uint8_t *out, uint32_t cap, uint32_t off, uint16_t v); +uint32_t dns_wire_put_u32(uint8_t *out, uint32_t cap, uint32_t off, uint32_t v); +bool dns_wire_write_name(uint8_t *out, uint32_t cap, uint32_t *off, const char *name); +bool dns_wire_read_name(const uint8_t *msg, uint32_t msg_len, uint32_t off, char *out, uint32_t out_cap, uint32_t *out_next); +bool dns_wire_name_normalize(const char *name, char *out, uint32_t out_cap); +bool dns_wire_name_equals(const char *a, const char *b); +bool dns_wire_is_local_name(const char *name); +bool dns_wire_read_rr(const uint8_t *msg, uint32_t msg_len, uint32_t off, dns_section_t section, dns_rr_view_t *rr, uint32_t *out_next); +bool dns_wire_parse_rdata(const uint8_t *msg, uint32_t msg_len, const dns_rr_view_t *rr, dns_record_t *out); +bool dns_wire_parse_records(const uint8_t *msg, uint32_t msg_len, bool check_id, uint16_t message_id, dns_record_t *out, uint32_t out_cap, uint32_t *out_count, uint16_t *out_flags); +uint32_t dns_wire_build_query(uint8_t *out, uint32_t cap, uint16_t message_id, const char *name,uint16_t qtype, bool mdns_qu); + +#ifdef __cplusplus +} +#endif diff --git a/kernel/networking/application_layer/dns/mdns_responder.c b/kernel/networking/application_layer/dns/mdns_responder.c index ba57b962..2572b592 100644 --- a/kernel/networking/application_layer/dns/mdns_responder.c +++ b/kernel/networking/application_layer/dns/mdns_responder.c @@ -16,6 +16,7 @@ #define MDNS_KEEPALIVE_MS 60000 #define MDNS_MAX_SERVICES 8 #define MDNS_CACHE_MAX 48 +#define MDNS_FLUSH_CLASS (DNS_CLASS_CACHE_FLUSH | DNS_CLASS_IN) typedef struct { bool used; @@ -70,69 +71,6 @@ static uint64_t g_mdns_host_last_tx_ms = 0; static mdns_service_t g_mdns_services[MDNS_MAX_SERVICES]; static mdns_cache_entry_t g_mdns_cache[MDNS_CACHE_MAX]; - -static bool mdns_read_name(const uint8_t *msg, uint32_t msg_len, uint32_t off, char *out, uint32_t out_cap, uint32_t *out_next) { - if (!msg) return false; - if (!msg_len) return false; - if (off >= msg_len) return false; - if (!out) return false; - if (!out_cap) return false; - - uint32_t idx = off; - uint32_t out_idx = 0; - uint32_t jumps = 0; - bool jumped = false; - - while (true) { - if (idx >= msg_len) return false; - - uint8_t c = msg[idx]; - if ((c & 0xC0) == 0xC0) { - if (idx + 1 >= msg_len) return false; - uint16_t ptr = (uint16_t)(((uint16_t)(c & 0x3F) << 8) | msg[idx + 1]); - if (ptr >= msg_len) return false; - if (!jumped) { - if (out_next) *out_next = idx + 2; - jumped = true; - } - idx = ptr; - jumps++; - if (jumps > 16) return false; - continue; - } - - if (c == 0) { - if (!jumped) { - if (out_next) *out_next = idx + 1; - } - if (out_idx == 0) { - if (out_cap < 2) return false; - out[0] = '.'; - out[1] = 0; - return true; - } - if (out_idx >= out_cap) return false; - out[out_idx] = 0; - return true; - } - - uint32_t lab_len = c; - idx++; - if (idx + lab_len > msg_len) return false; - - if (out_idx) { - if (out_idx + 1 >= out_cap) return false; - out[out_idx] = '.'; - out_idx++; - } - - if (out_idx + lab_len >= out_cap) return false; - memcpy(out + out_idx, msg + idx, lab_len); - out_idx += lab_len; - idx += lab_len; - } -} - static void mdns_send(socket_handle_t sock, const net_l4_endpoint *src, bool unicast, ip_version_t ver, const uint8_t *mcast_ip, const uint8_t *pkt, uint32_t pkt_len) { if (!sock) return; if (!pkt) return; @@ -143,7 +81,7 @@ static void mdns_send(socket_handle_t sock, const net_l4_endpoint *src, bool uni if (unicast && src) { dst = *src; - if (!dst.port) dst.port = DNS_SD_MDNS_PORT; + if (!dst.port) dst.port = DNS_MDNS_PORT; socket_sendto_udp_ex(sock, DST_ENDPOINT, &dst, 0, (void*)pkt, pkt_len); return; } @@ -151,7 +89,7 @@ static void mdns_send(socket_handle_t sock, const net_l4_endpoint *src, bool uni dst.ver = ver; if (ver == IP_VER4) memcpy(dst.ip, mcast_ip, 4); else memcpy(dst.ip, mcast_ip, 16); - dst.port = DNS_SD_MDNS_PORT; + dst.port = DNS_MDNS_PORT; socket_sendto_udp_ex(sock, DST_ENDPOINT, &dst, 0, (void*)pkt, pkt_len); } @@ -301,22 +239,22 @@ static bool mdns_pkt_begin(mdns_pkt_t *p, uint8_t *out, uint32_t cap, uint16_t f p->cap = cap; p->off = 0; - p->off = dns_sd_put_u16(out, cap, p->off, 0); + p->off = dns_wire_put_u16(out, cap, p->off, 0); if (!p->off) return false; - p->off = dns_sd_put_u16(out, cap, p->off, flags); + p->off = dns_wire_put_u16(out, cap, p->off, flags); if (!p->off) return false; - p->off = dns_sd_put_u16(out, cap, p->off, 0); + p->off = dns_wire_put_u16(out, cap, p->off, 0); if (!p->off) return false; p->an_pos = p->off; - p->off = dns_sd_put_u16(out, cap, p->off, 0); + p->off = dns_wire_put_u16(out, cap, p->off, 0); if (!p->off) return false; - p->off = dns_sd_put_u16(out, cap, p->off, 0); + p->off = dns_wire_put_u16(out, cap, p->off, 0); if (!p->off) return false; p->ar_pos = p->off; - p->off = dns_sd_put_u16(out, cap, p->off, 0); + p->off = dns_wire_put_u16(out, cap, p->off, 0); if (!p->off) return false; return true; @@ -386,11 +324,22 @@ static void mdns_cache_put_ptr(const char *name, const char *target, uint32_t tt if (!target) return; uint64_t now = get_time(); + if (!ttl_s) { + for (uint32_t i = 0; i < MDNS_CACHE_MAX; i++) { + mdns_cache_entry_t *e = &g_mdns_cache[i]; + if (!e->type) continue; + if (e->rrtype != DNS_TYPE_PTR) continue; + if (!dns_wire_name_equals(e->name, name)) continue; + memset(e, 0, sizeof(*e)); + return; + } + return; + } for (uint32_t i = 0; i < MDNS_CACHE_MAX; i++) { mdns_cache_entry_t *e = &g_mdns_cache[i]; if (!e->type) continue; - if (e->rrtype != DNS_SD_TYPE_PTR) continue; - if (strncmp(e->name, name, 256) != 0) continue; + if (e->rrtype != DNS_TYPE_PTR) continue; + if (!dns_wire_name_equals(e->name, name)) continue; strncpy(e->target, target, sizeof(e->target)); e->expire_ms = now + (uint64_t)ttl_s * 1000; return; @@ -401,7 +350,7 @@ static void mdns_cache_put_ptr(const char *name, const char *target, uint32_t tt if (e->type) continue; memset(e, 0, sizeof(*e)); e->type = 1; - e->rrtype = DNS_SD_TYPE_PTR; + e->rrtype = DNS_TYPE_PTR; strncpy(e->name, name, sizeof(e->name)); strncpy(e->target, target, sizeof(e->target)); e->expire_ms = now + (uint64_t)ttl_s * 1000; @@ -414,11 +363,22 @@ static void mdns_cache_put_srv(const char *name, uint16_t port, const char *targ if (!target) return; uint64_t now = get_time(); + if (!ttl_s) { + for (uint32_t i = 0; i < MDNS_CACHE_MAX; i++) { + mdns_cache_entry_t *e = &g_mdns_cache[i]; + if (!e->type) continue; + if (e->rrtype != DNS_TYPE_SRV) continue; + if (!dns_wire_name_equals(e->name, name)) continue; + memset(e, 0, sizeof(*e)); + return; + } + return; + } for (uint32_t i = 0; i < MDNS_CACHE_MAX; i++) { mdns_cache_entry_t *e = &g_mdns_cache[i]; if (!e->type) continue; - if (e->rrtype != DNS_SD_TYPE_SRV) continue; - if (strncmp(e->name, name, 256) != 0) continue; + if (e->rrtype != DNS_TYPE_SRV) continue; + if (!dns_wire_name_equals(e->name, name)) continue; e->port = port; strncpy(e->target, target, sizeof(e->target)); e->expire_ms = now + (uint64_t)ttl_s * 1000; @@ -430,7 +390,7 @@ static void mdns_cache_put_srv(const char *name, uint16_t port, const char *targ if (e->type) continue; memset(e, 0, sizeof(*e)); e->type = 1; - e->rrtype = DNS_SD_TYPE_SRV; + e->rrtype = DNS_TYPE_SRV; e->port = port; strncpy(e->name, name, sizeof(e->name)); strncpy(e->target, target, sizeof(e->target)); @@ -443,11 +403,22 @@ static void mdns_cache_put_txt(const char *name, const char *txt, uint32_t ttl_s if (!name) return; uint64_t now = get_time(); + if (!ttl_s) { + for (uint32_t i = 0; i < MDNS_CACHE_MAX; i++) { + mdns_cache_entry_t *e = &g_mdns_cache[i]; + if (!e->type) continue; + if (e->rrtype != DNS_TYPE_TXT) continue; + if (!dns_wire_name_equals(e->name, name)) continue; + memset(e, 0, sizeof(*e)); + return; + } + return; + } for (uint32_t i = 0; i < MDNS_CACHE_MAX; i++) { mdns_cache_entry_t *e = &g_mdns_cache[i]; if (!e->type) continue; - if (e->rrtype != DNS_SD_TYPE_TXT) continue; - if (strncmp(e->name, name, 256) != 0) continue; + if (e->rrtype != DNS_TYPE_TXT) continue; + if (!dns_wire_name_equals(e->name, name)) continue; if (txt) strncpy(e->txt, txt, sizeof(e->txt)); else e->txt[0] = 0; e->expire_ms = now + (uint64_t)ttl_s * 1000; @@ -459,7 +430,7 @@ static void mdns_cache_put_txt(const char *name, const char *txt, uint32_t ttl_s if (e->type) continue; memset(e, 0, sizeof(*e)); e->type = 1; - e->rrtype = DNS_SD_TYPE_TXT; + e->rrtype = DNS_TYPE_TXT; strncpy(e->name, name, sizeof(e->name)); if (txt) strncpy(e->txt, txt, sizeof(e->txt)); e->expire_ms = now + (uint64_t)ttl_s * 1000; @@ -471,14 +442,17 @@ static bool mdns_parse_ipv4_ptr_qname(const char *name, uint32_t *out_ip) { if (!name) return false; if (!out_ip) return false; + char norm[DNS_WIRE_MAX_NAME]; + if (!dns_wire_name_normalize(name, norm, sizeof(norm))) return false; + uint32_t oct[4]; memset(oct, 0, sizeof(oct)); - const char *p = name; + const char *p = norm; for (int i = 0; i < 4; i++) { uint32_t v = 0; uint32_t digits = 0; - while (*p >= '0' && *p <= '9') { + while (is_digit(*p)) { v = v * 10 + (uint32_t)(*p - '0'); p++; digits++; @@ -496,7 +470,7 @@ static bool mdns_parse_ipv4_ptr_qname(const char *name, uint32_t *out_ip) { if (*p != '.') return false; p++; - if (strncmp(p, "in-addr.arpa", 12) != 0) return false; + if (strncmp(p, "in-addr.arpa", 12) != 0 || p[12] != 0) return false; uint32_t a = oct[3]; uint32_t b = oct[2]; @@ -507,121 +481,36 @@ static bool mdns_parse_ipv4_ptr_qname(const char *name, uint32_t *out_ip) { return true; } -static void mdns_parse_txt(const uint8_t *rdata, uint16_t rdlen, char *out, uint32_t out_cap) { - if (!out) return; - if (!out_cap) return; - - out[0] = 0; - if (!rdata) return; - if (!rdlen) return; - - uint32_t idx = 0; - uint32_t out_idx = 0; - - while (idx < rdlen) { - uint8_t len = rdata[idx]; - idx++; - if (idx + len > rdlen) break; - - if (len) { - if (out_idx) { - if (out_idx + 1 >= out_cap) break; - out[out_idx] = ';'; - out_idx++; - } - uint32_t copy = len; - if (out_idx + copy >= out_cap) copy = out_cap - out_idx - 1; - memcpy(out + out_idx, rdata + idx, copy); - out_idx += copy; - } - - idx += len; - } - - if (out_idx >= out_cap) out_idx = out_cap - 1; - out[out_idx] = 0; -} - static void mdns_cache_from_packet(const uint8_t *pkt, uint32_t pkt_len) { if (!pkt) return; if (pkt_len < 12) return; - uint16_t flags = be16(*(const uint16_t *)(pkt + 2)); - if (!(flags & DNS_SD_FLAG_QR)) return; - - uint16_t qd = be16(*(const uint16_t *)(pkt + 4)); - uint16_t an = be16(*(const uint16_t *)(pkt + 6)); - uint16_t ns = be16(*(const uint16_t *)(pkt + 8)); - uint16_t ar = be16(*(const uint16_t *)(pkt + 10)); - - uint32_t off = 12; - - for (uint16_t i = 0; i < qd; i++) { - char qname[256]; - uint32_t next = 0; - if (!mdns_read_name(pkt, pkt_len, off, qname, sizeof(qname), &next)) return; - if (next + 4 > pkt_len) return; - off = next + 4; - if (off > pkt_len) return; - } - - uint32_t rr_total = (uint32_t)an + (uint32_t)ns + (uint32_t)ar; - for (uint32_t i = 0; i < rr_total; i++) { - char name[256]; - uint32_t next = 0; - if (!mdns_read_name(pkt, pkt_len, off, name, sizeof(name), &next)) return; - if (next + 10 > pkt_len) return; - - mdns_rr_hdr_t h; - h.rrtype = be16(*(const uint16_t *)(pkt + next)); - h.rrclass = be16(*(const uint16_t *)(pkt + next + 2)); - h.ttl_s = be32(*(const uint32_t *)(pkt + next + 4)); - h.rdlen = be16(*(const uint16_t *)(pkt + next + 8)); - - uint32_t rdata = next + 10; - if (rdata + h.rdlen > pkt_len) return; - - if (h.rrtype == DNS_SD_TYPE_A) { - if (h.rdlen == 4) { - uint8_t ip4[16]; - memset(ip4, 0, sizeof(ip4)); - memcpy(ip4, pkt + rdata, 4); - dns_cache_put_ip(name, DNS_SD_TYPE_A, ip4, h.ttl_s * 1000); - } - } else if (h.rrtype == DNS_SD_TYPE_AAAA) { - if (h.rdlen == 16) { - dns_cache_put_ip(name, DNS_SD_TYPE_AAAA, pkt + rdata, h.ttl_s * 1000); - } - } else if (h.rrtype == DNS_SD_TYPE_PTR) { - char target[256]; - uint32_t tnext = 0; - if (mdns_read_name(pkt, pkt_len, rdata, target, sizeof(target), &tnext)) { - mdns_cache_put_ptr(name, target, h.ttl_s); - } - } else if (h.rrtype == DNS_SD_TYPE_SRV) { - if (h.rdlen >= 6) { - uint16_t port = be16(*(const uint16_t *)(pkt + rdata + 4)); - char target[256]; - uint32_t tnext = 0; - if (mdns_read_name(pkt, pkt_len, rdata + 6, target, sizeof(target), &tnext)) { - mdns_cache_put_srv(name, port, target, h.ttl_s); - } - } - } else if (h.rrtype == DNS_SD_TYPE_TXT) { - char txt[256]; - mdns_parse_txt(pkt + rdata, h.rdlen, txt, sizeof(txt)); - mdns_cache_put_txt(name, txt, h.ttl_s); - } - - off = rdata + h.rdlen; - if (off > pkt_len) return; + dns_record_t records[12]; + uint32_t count = 0; + uint16_t flags = 0; + if (!dns_wire_parse_records(pkt, pkt_len, false, 0, records, 12, &count, &flags)) return; + if (!(flags & DNS_FLAG_QR)) return; + + for (uint32_t i = 0; i < count; i++) { + dns_record_t *r = &records[i]; + if ((r->rrclass & DNS_CLASS_MASK) != DNS_CLASS_IN) continue; + + if (r->type == DNS_TYPE_A) { + if (!r->ttl_s) dns_cache_remove_ip(r->name, DNS_TYPE_A); + else dns_cache_put_ip(r->name, DNS_TYPE_A, r->addr, r->ttl_s * 1000); + } else if (r->type == DNS_TYPE_AAAA) { + if (!r->ttl_s) dns_cache_remove_ip(r->name, DNS_TYPE_AAAA); + else dns_cache_put_ip(r->name, DNS_TYPE_AAAA, r->addr, r->ttl_s * 1000); + } else if (r->type == DNS_TYPE_PTR) mdns_cache_put_ptr(r->name, r->target, r->ttl_s); + else if (r->type == DNS_TYPE_SRV) mdns_cache_put_srv(r->name, r->port, r->target, r->ttl_s); + else if (r->type == DNS_TYPE_TXT) mdns_cache_put_txt(r->name, r->txt, r->ttl_s); } } static bool mdns_add_host_additionals(mdns_pkt_t *p) { if (!p) return false; - uint16_t rrclass = (uint16_t)(0x8000u | DNS_SD_CLASS_IN); + uint16_t rrclass = MDNS_FLUSH_CLASS; if (g_mdns_ipv4) { if (!mdns_pkt_add_a(p, true, g_mdns_fqdn, rrclass, MDNS_TTL_S, g_mdns_ipv4)) return false; @@ -648,8 +537,8 @@ static bool mdns_add_service_records(mdns_pkt_t *p, const mdns_service_t *s, uin if (!s->instance[0] || !s->service[0] || !s->proto[0]) return false; string_format_buf(inst, sizeof(inst), "%s._%s._%s.local", s->instance, s->service, s->proto); - uint16_t ptr_class = DNS_SD_CLASS_IN; - uint16_t flush_class = (uint16_t)(0x8000u | DNS_SD_CLASS_IN); + uint16_t ptr_class = DNS_CLASS_IN; + uint16_t flush_class = MDNS_FLUSH_CLASS; uint32_t ttl = goodbye ? 0 : ttl_s; @@ -668,17 +557,49 @@ static bool mdns_add_service_records(mdns_pkt_t *p, const mdns_service_t *s, uin return true; } +static bool mdns_label_ok(const char *s, uint32_t max_len) { + if (!s) return false; + uint32_t len = strlen(s); + if (!len || len >= max_len || len > 63) return false; + for (uint32_t i = 0; i < len; i++) { + char c = s[i]; + if (!is_alnum(c) && c != '-') return false; + } + return true; +} + +static bool mdns_instance_ok(const char *s) { + if (!s) return false; + uint32_t len = strlen(s); + if (!len || len >= 64 || len > 63) return false; + for (uint32_t i = 0; i < len; i++) { + char c = s[i]; + if (c < 32 || c == '.') return false; + } + return true; +} + bool mdns_register_service(const char *instance, const char *service, const char *proto, uint16_t port, const char *txt) { - if (!instance) return false; - if (!service) return false; + if (!port) return false; + if (!mdns_instance_ok(instance)) return false; + if (!mdns_label_ok(service, 32)) return false; if (!proto) return false; + if (strcmp_case(proto, "tcp", true) != 0 && strcmp_case(proto, "udp", true) != 0) return false; + if (txt) { + uint32_t txt_len = strlen(txt); + if (txt_len >= 128) return false; + for (uint32_t i = 0; i < txt_len; i++) { + char c = txt[i]; + if (!is_printable(c)) return false; + } + } for (uint32_t i = 0; i < MDNS_MAX_SERVICES; i++) { mdns_service_t *s = &g_mdns_services[i]; if (!s->used) continue; if (strncmp(s->instance, instance, (int)sizeof(s->instance)) != 0) continue; if (strncmp(s->service, service, (int)sizeof(s->service)) != 0) continue; - if (strncmp(s->proto, proto, (int)sizeof(s->proto)) != 0) continue; + if (strncmp_case(s->proto, proto, true, (int)sizeof(s->proto)) != 0) continue; s->active = true; s->port = port; @@ -714,9 +635,10 @@ bool mdns_register_service(const char *instance, const char *service, const char } bool mdns_deregister_service(const char *instance, const char *service, const char *proto) { - if (!instance) return false; - if (!service) return false; + if (!mdns_instance_ok(instance)) return false; + if (!mdns_label_ok(service, 32)) return false; if (!proto) return false; + if (strcmp_case(proto, "tcp", true) != 0 && strcmp_case(proto, "udp", true) != 0) return false; for (uint32_t i = 0; i < MDNS_MAX_SERVICES; i++) { mdns_service_t *s = &g_mdns_services[i]; @@ -724,7 +646,7 @@ bool mdns_deregister_service(const char *instance, const char *service, const ch if (!s->active) continue; if (strncmp(s->instance, instance, (int)sizeof(s->instance)) != 0) continue; if (strncmp(s->service, service, (int)sizeof(s->service)) != 0) continue; - if (strncmp(s->proto, proto, (int)sizeof(s->proto)) != 0) continue; + if (strncmp_case(s->proto, proto, true, (int)sizeof(s->proto)) != 0) continue; s->active = false; s->announce_left = 0; @@ -765,33 +687,36 @@ void mdns_responder_tick(socket_handle_t sock4, socket_handle_t sock6, const uin uint8_t pkt[900]; mdns_pkt_t p; - if (mdns_pkt_begin(&p, pkt, sizeof(pkt), (uint16_t)(DNS_SD_FLAG_QR | DNS_SD_FLAG_AA))) { - uint16_t flush_class = (uint16_t)(0x8000u | DNS_SD_CLASS_IN); - if (g_mdns_ipv4) mdns_pkt_add_a(&p, false, g_mdns_fqdn, flush_class, MDNS_TTL_S, g_mdns_ipv4); + if (mdns_pkt_begin(&p, pkt, sizeof(pkt), DNS_FLAG_QR | DNS_FLAG_AA)) { + bool ok = true; + uint16_t flush_class = MDNS_FLUSH_CLASS; + if (g_mdns_ipv4 && !mdns_pkt_add_a(&p, false, g_mdns_fqdn, flush_class, MDNS_TTL_S, g_mdns_ipv4)) ok = false; uint8_t ip6[16]; memcpy(ip6, g_mdns_ipv6, 16); if (!ip6[0] && g_mdns_ifindex) ipv6_make_lla_from_mac(g_mdns_ifindex, ip6); - if (ip6[0]) mdns_pkt_add_aaaa(&p, false, g_mdns_fqdn, flush_class, MDNS_TTL_S, ip6); + if (ip6[0] && !mdns_pkt_add_aaaa(&p, false, g_mdns_fqdn, flush_class, MDNS_TTL_S, ip6)) ok = false; - mdns_pkt_commit(&p); + if (ok && p.an) { + mdns_pkt_commit(&p); - if (sock4 && mcast_v4) mdns_send(sock4, 0, false, IP_VER4, mcast_v4, pkt, p.off); - if (sock6 && mcast_v6) mdns_send(sock6, 0, false, IP_VER6, mcast_v6, pkt, p.off); + if (sock4 && mcast_v4) mdns_send(sock4, 0, false, IP_VER4, mcast_v4, pkt, p.off); + if (sock6 && mcast_v6) mdns_send(sock6, 0, false, IP_VER6, mcast_v6, pkt, p.off); - uint32_t ttl_ms = MDNS_TTL_S * 1000; + uint32_t ttl_ms = MDNS_TTL_S * 1000; - if (g_mdns_ipv4) { - uint8_t ip4[16]; - memset(ip4, 0, sizeof(ip4)); - memcpy(ip4, &g_mdns_ipv4, 4); - dns_cache_put_ip(g_mdns_fqdn, DNS_SD_TYPE_A, ip4, ttl_ms); - } + if (g_mdns_ipv4) { + uint8_t ip4[16]; + memset(ip4, 0, sizeof(ip4)); + memcpy(ip4, &g_mdns_ipv4, 4); + dns_cache_put_ip(g_mdns_fqdn, DNS_TYPE_A, ip4, ttl_ms); + } - uint8_t ip6_cache[16]; - memcpy(ip6_cache, g_mdns_ipv6, 16); - if (!ip6_cache[0] && g_mdns_ifindex) ipv6_make_lla_from_mac(g_mdns_ifindex, ip6_cache); - if (ip6_cache[0]) dns_cache_put_ip(g_mdns_fqdn, DNS_SD_TYPE_AAAA, ip6_cache, ttl_ms); + uint8_t ip6_cache[16]; + memcpy(ip6_cache, g_mdns_ipv6, 16); + if (!ip6_cache[0] && g_mdns_ifindex) ipv6_make_lla_from_mac(g_mdns_ifindex, ip6_cache); + if (ip6_cache[0]) dns_cache_put_ip(g_mdns_fqdn, DNS_TYPE_AAAA, ip6_cache, ttl_ms); + } } g_mdns_host_announce_left--; @@ -813,8 +738,7 @@ void mdns_responder_tick(socket_handle_t sock4, socket_handle_t sock6, const uin uint8_t pkt[900]; mdns_pkt_t p; - if (mdns_pkt_begin(&p, pkt, sizeof(pkt), (uint16_t)(DNS_SD_FLAG_QR | DNS_SD_FLAG_AA))) { - mdns_add_service_records(&p, s, MDNS_TTL_S, do_goodbye); + if (mdns_pkt_begin(&p, pkt, sizeof(pkt), DNS_FLAG_QR | DNS_FLAG_AA) && mdns_add_service_records(&p, s, MDNS_TTL_S, do_goodbye) && (p.an || p.ar)) { mdns_pkt_commit(&p); if (sock4 && mcast_v4) mdns_send(sock4, 0, false, IP_VER4, mcast_v4, pkt, p.off); @@ -841,37 +765,41 @@ void mdns_responder_handle_query(socket_handle_t sock, ip_version_t ver, const u mdns_refresh_identity(); - uint16_t flags = be16(*(const uint16_t *)(pkt + 2)); - if (flags & DNS_SD_FLAG_QR) { + uint16_t flags = rd_be16(pkt + 2); + if (flags & DNS_FLAG_QR) { mdns_cache_from_packet(pkt, pkt_len); return; } - uint16_t qd = be16(*(const uint16_t *)(pkt + 4)); + uint16_t qd = rd_be16(pkt + 4); if (!qd) return; bool unicast_any = false; uint8_t out[1500]; mdns_pkt_t p; - if (!mdns_pkt_begin(&p, out, sizeof(out), (uint16_t)(DNS_SD_FLAG_QR | DNS_SD_FLAG_AA))) return; + if (!mdns_pkt_begin(&p, out, sizeof(out), DNS_FLAG_QR | DNS_FLAG_AA)) return; uint32_t qoff = 12; for (uint16_t qi = 0; qi < qd; qi++) { char qname[256]; uint32_t next = 0; - if (!mdns_read_name(pkt, pkt_len, qoff, qname, sizeof(qname), &next)) return; + if (!dns_wire_read_name(pkt, pkt_len, qoff, qname, sizeof(qname), &next)) return; if (next + 4 > pkt_len) return; - uint16_t qtype = be16(*(const uint16_t *)(pkt + next)); - uint16_t qclass = be16(*(const uint16_t *)(pkt + next + 2)); - if ((qclass & 0x8000u) != 0) unicast_any = true; + uint16_t qtype = rd_be16(pkt + next); + uint16_t qclass = rd_be16(pkt + next + 2); + if ((qclass & DNS_CLASS_MASK) != DNS_CLASS_IN && (qclass & DNS_CLASS_MASK) != DNS_CLASS_ANY) { + qoff = next + 4; + continue; + } + if ((qclass & DNS_CLASS_CACHE_FLUSH) != 0) unicast_any = true; uint32_t ipq = 0; - if (qtype == DNS_SD_TYPE_PTR && g_mdns_ipv4 && mdns_parse_ipv4_ptr_qname(qname, &ipq) && ipq == g_mdns_ipv4) { - uint16_t ptr_class = DNS_SD_CLASS_IN; - uint16_t flush_class = (uint16_t)(0x8000u | DNS_SD_CLASS_IN); + if (qtype == DNS_TYPE_PTR && g_mdns_ipv4 && mdns_parse_ipv4_ptr_qname(qname, &ipq) && ipq == g_mdns_ipv4) { + uint16_t ptr_class = DNS_CLASS_IN; + uint16_t flush_class = MDNS_FLUSH_CLASS; if (!mdns_pkt_add_ptr(&p, false, qname, ptr_class, MDNS_TTL_S, g_mdns_fqdn)) return; if (!mdns_pkt_add_a(&p, true, g_mdns_fqdn, flush_class, MDNS_TTL_S, g_mdns_ipv4)) return; @@ -884,28 +812,27 @@ void mdns_responder_handle_query(socket_handle_t sock, ip_version_t ver, const u } } - if (qtype == DNS_SD_TYPE_A || qtype == DNS_SD_TYPE_ANY) { - if (strncmp(qname, g_mdns_fqdn, 256) == 0 && g_mdns_ipv4) { - uint16_t flush_class = (uint16_t)(0x8000u | DNS_SD_CLASS_IN); + if (qtype == DNS_TYPE_A || qtype == DNS_TYPE_ANY) { + if (dns_wire_name_equals(qname, g_mdns_fqdn) && g_mdns_ipv4) { + uint16_t flush_class = MDNS_FLUSH_CLASS; if (!mdns_pkt_add_a(&p, false, g_mdns_fqdn, flush_class, MDNS_TTL_S, g_mdns_ipv4)) return; } } - if (qtype == DNS_SD_TYPE_AAAA || qtype == DNS_SD_TYPE_ANY) { - if (strncmp(qname, g_mdns_fqdn, 256) == 0) { + if (qtype == DNS_TYPE_AAAA || qtype == DNS_TYPE_ANY) { + if (dns_wire_name_equals(qname, g_mdns_fqdn)) { uint8_t ip6[16]; memcpy(ip6, g_mdns_ipv6, 16); if (!ip6[0] && g_mdns_ifindex) ipv6_make_lla_from_mac(g_mdns_ifindex, ip6); if (ip6[0]) { - uint16_t flush_class = (uint16_t)(0x8000u | DNS_SD_CLASS_IN); + uint16_t flush_class = MDNS_FLUSH_CLASS; if (!mdns_pkt_add_aaaa(&p, false, g_mdns_fqdn, flush_class, MDNS_TTL_S, ip6)) return; } } } - if ((qtype == DNS_SD_TYPE_PTR || qtype == DNS_SD_TYPE_ANY) && - strncmp(qname, DNS_SD_ENUM_SERVICES, 256) == 0) { - uint16_t ptr_class = DNS_SD_CLASS_IN; + if ((qtype == DNS_TYPE_PTR || qtype == DNS_TYPE_ANY) && dns_wire_name_equals(qname, DNS_SD_ENUM_SERVICES)) { + uint16_t ptr_class = DNS_CLASS_IN; for (uint32_t i = 0; i < MDNS_MAX_SERVICES; i++) { if (!g_mdns_services[i].used) continue; @@ -921,7 +848,7 @@ void mdns_responder_handle_query(socket_handle_t sock, ip_version_t ver, const u char type2[128]; mdns_make_service_type(type2,sizeof(type2),g_mdns_services[j].service, g_mdns_services[j].proto); - if (strncmp(type2, type, 128) == 0) { + if (dns_wire_name_equals(type2, type)) { seen = true; break; } @@ -946,27 +873,24 @@ void mdns_responder_handle_query(socket_handle_t sock, ip_version_t ver, const u if (!s->instance[0] || !s->service[0] || !s->proto[0]) continue; string_format_buf(inst, sizeof(inst), "%s._%s._%s.local", s->instance, s->service, s->proto); - if ((qtype == DNS_SD_TYPE_PTR || qtype == DNS_SD_TYPE_ANY) && - strncmp(qname, type, 256) == 0) { - uint16_t ptr_class = DNS_SD_CLASS_IN; + if ((qtype == DNS_TYPE_PTR || qtype == DNS_TYPE_ANY) && dns_wire_name_equals(qname, type)) { + uint16_t ptr_class = DNS_CLASS_IN; if (!mdns_pkt_add_ptr(&p, false, type, ptr_class, MDNS_TTL_S, inst)) return; - uint16_t flush_class = (uint16_t)(0x8000u | DNS_SD_CLASS_IN); + uint16_t flush_class = MDNS_FLUSH_CLASS; if (!mdns_pkt_add_srv(&p, true, inst, flush_class, MDNS_TTL_S, s->port, g_mdns_fqdn)) return; if (!mdns_pkt_add_txt(&p, true, inst, flush_class, MDNS_TTL_S, s->txt)) return; need_host_add = true; } - if ((qtype == DNS_SD_TYPE_SRV || qtype == DNS_SD_TYPE_ANY) && - strncmp(qname, inst, 256) == 0) { - uint16_t flush_class = (uint16_t)(0x8000u | DNS_SD_CLASS_IN); + if ((qtype == DNS_TYPE_SRV || qtype == DNS_TYPE_ANY) && dns_wire_name_equals(qname, inst)) { + uint16_t flush_class = MDNS_FLUSH_CLASS; if (!mdns_pkt_add_srv(&p, false, inst, flush_class, MDNS_TTL_S, s->port, g_mdns_fqdn)) return; need_host_add = true; } - if ((qtype == DNS_SD_TYPE_TXT || qtype == DNS_SD_TYPE_ANY) && - strncmp(qname, inst, 256) == 0) { - uint16_t flush_class = (uint16_t)(0x8000u | DNS_SD_CLASS_IN); + if ((qtype == DNS_TYPE_TXT || qtype == DNS_TYPE_ANY) && dns_wire_name_equals(qname, inst)) { + uint16_t flush_class = MDNS_FLUSH_CLASS; if (!mdns_pkt_add_txt(&p, false, inst, flush_class, MDNS_TTL_S, s->txt)) return; } } @@ -990,11 +914,11 @@ void mdns_responder_handle_query(socket_handle_t sock, ip_version_t ver, const u uint8_t ip4[16]; memset(ip4, 0, sizeof(ip4)); memcpy(ip4, &g_mdns_ipv4, 4); - dns_cache_put_ip(g_mdns_fqdn, DNS_SD_TYPE_A, ip4, ttl_ms); + dns_cache_put_ip(g_mdns_fqdn, DNS_TYPE_A, ip4, ttl_ms); } uint8_t ip6[16]; memcpy(ip6, g_mdns_ipv6, 16); if (!ip6[0] && g_mdns_ifindex) ipv6_make_lla_from_mac(g_mdns_ifindex, ip6); - if (ip6[0]) dns_cache_put_ip(g_mdns_fqdn, DNS_SD_TYPE_AAAA, ip6, ttl_ms); + if (ip6[0]) dns_cache_put_ip(g_mdns_fqdn, DNS_TYPE_AAAA, ip6, ttl_ms); } \ No newline at end of file diff --git a/shared b/shared index 7828b967..964533b3 160000 --- a/shared +++ b/shared @@ -1 +1 @@ -Subproject commit 7828b96763490f9a1cdb366a5a4e54b45dd19645 +Subproject commit 964533b3d91e40c6707a01b4a64381b6c101deb1 From ed4a3b7442a108d2e2be7bdb2705502681f0637b Mon Sep 17 00:00:00 2001 From: CodeAnarchist Date: Mon, 25 May 2026 17:13:27 +0200 Subject: [PATCH 16/39] [HTTP] HTTP parsing and request limits --- .../application_layer/csocket_http_client.cpp | 2 +- .../application_layer/csocket_http_client.h | 2 +- .../application_layer/dhcp_daemon.c | 16 +- .../application_layer/dhcpv6_daemon.c | 6 +- kernel/networking/application_layer/dns/dns.c | 4 +- .../application_layer/dns/dns_daemon.c | 6 +- .../application_layer/dns/dns_mdns.c | 4 +- .../application_layer/dns/mdns_responder.c | 4 +- kernel/networking/application_layer/http.c | 271 +++++++++++++----- kernel/networking/application_layer/http.h | 82 +++++- kernel/networking/application_layer/ntp.c | 4 +- kernel/networking/application_layer/sntp.c | 4 +- .../application_layer/socket_http_client.hpp | 138 ++++++--- .../application_layer/socket_http_server.hpp | 240 ++++++++-------- .../application_layer/ssdp_daemon.c | 14 +- kernel/networking/processes/net_proc.c | 6 +- kernel/networking/transport_layer/csocket.c | 6 +- .../transport_layer/csocket_udp.cpp | 6 +- .../networking/transport_layer/csocket_udp.h | 6 +- 19 files changed, 548 insertions(+), 273 deletions(-) diff --git a/kernel/networking/application_layer/csocket_http_client.cpp b/kernel/networking/application_layer/csocket_http_client.cpp index 2fe47744..dde77ff8 100644 --- a/kernel/networking/application_layer/csocket_http_client.cpp +++ b/kernel/networking/application_layer/csocket_http_client.cpp @@ -18,7 +18,7 @@ void http_client_destroy(http_client_handle_t h) { delete cli; } -int32_t http_client_connect_ex(http_client_handle_t h, uint8_t dst_kind, const void* dst, uint16_t port) { +int32_t http_client_connect(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(static_cast(dst_kind), dst, port); diff --git a/kernel/networking/application_layer/csocket_http_client.h b/kernel/networking/application_layer/csocket_http_client.h index 30bd1088..a0afacb8 100644 --- a/kernel/networking/application_layer/csocket_http_client.h +++ b/kernel/networking/application_layer/csocket_http_client.h @@ -11,7 +11,7 @@ typedef void* http_client_handle_t; http_client_handle_t http_client_create(uint16_t pid, const SocketExtraOptions* extra); void http_client_destroy(http_client_handle_t h); -int32_t http_client_connect_ex(http_client_handle_t h, uint8_t dst_kind, const void *dst, uint16_t port); +int32_t http_client_connect(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); diff --git a/kernel/networking/application_layer/dhcp_daemon.c b/kernel/networking/application_layer/dhcp_daemon.c index 5b4d6ece..c66f2b18 100644 --- a/kernel/networking/application_layer/dhcp_daemon.c +++ b/kernel/networking/application_layer/dhcp_daemon.c @@ -129,7 +129,7 @@ static void ensure_inventory() { 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) { + if (socket_bind_udp(st.sock, &spec, 68) != SOCK_OK) { socket_destroy_udp(st.sock); continue; } @@ -152,7 +152,7 @@ static bool udp_wait_for_type_on(socket_handle_t sock, uint8_t wanted, uint32_t 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); + int64_t r = socket_recvfrom_udp(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; } @@ -187,7 +187,7 @@ static bool udp_wait_for_ack_or_nak(socket_handle_t sock, uint32_t expect_xid, c 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); + int64_t r = socket_recvfrom_udp(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; } @@ -343,7 +343,7 @@ static void dhcp_send_discover_for(dhcp_if_state_t* st) { uint32_t bcast = 0xFFFFFFFFu; net_l4_endpoint dst; make_ep(bcast, 67, IP_VER4, &dst); - socket_sendto_udp_ex(st->sock, 0, &dst, 0, (const void*)pkt.ptr, pkt.size); + socket_sendto_udp(st->sock, 0, &dst, 0, (const void*)pkt.ptr, pkt.size); free_sized((void*)pkt.ptr, pkt.size); } @@ -352,7 +352,7 @@ static void dhcp_send_request_select_for(dhcp_if_state_t* st, const dhcp_request uint32_t dip = 0xFFFFFFFFu; net_l4_endpoint dst; make_ep(dip, 67, IP_VER4, &dst); - socket_sendto_udp_ex(st->sock, 0, &dst, 0, (const void*)pkt.ptr, pkt.size); + socket_sendto_udp(st->sock, 0, &dst, 0, (const void*)pkt.ptr, pkt.size); free_sized((void*)pkt.ptr, pkt.size); } @@ -372,7 +372,7 @@ static void dhcp_send_renew_for(dhcp_if_state_t* st) { uint32_t dip = st->server_ip_net ? st->server_ip_net : 0xFFFFFFFFu; net_l4_endpoint dst; make_ep(dip, 67, IP_VER4, &dst); - socket_sendto_udp_ex(st->sock, 0, &dst, 0, (const void*)pkt.ptr, pkt.size); + socket_sendto_udp(st->sock, 0, &dst, 0, (const void*)pkt.ptr, pkt.size); free_sized((void*)pkt.ptr, pkt.size); } @@ -392,7 +392,7 @@ static void dhcp_send_rebind_for(dhcp_if_state_t* st) { uint32_t dip = 0xFFFFFFFFu; net_l4_endpoint dst; make_ep(dip, 67, IP_VER4, &dst); - socket_sendto_udp_ex(st->sock, 0, &dst, 0, (const void*)pkt.ptr, pkt.size); + socket_sendto_udp(st->sock, 0, &dst, 0, (const void*)pkt.ptr, pkt.size); free_sized((void*)pkt.ptr, pkt.size); } @@ -412,7 +412,7 @@ static void dhcp_send_inform_for(dhcp_if_state_t* st) { uint32_t dip = 0xFFFFFFFFu; net_l4_endpoint dst; make_ep(dip, 67, IP_VER4, &dst); - socket_sendto_udp_ex(st->sock, 0, &dst, 0, (const void*)pkt.ptr, pkt.size); + socket_sendto_udp(st->sock, 0, &dst, 0, (const void*)pkt.ptr, pkt.size); free_sized((void*)pkt.ptr, pkt.size); } diff --git a/kernel/networking/application_layer/dhcpv6_daemon.c b/kernel/networking/application_layer/dhcpv6_daemon.c index 46333545..9cf27665 100644 --- a/kernel/networking/application_layer/dhcpv6_daemon.c +++ b/kernel/networking/application_layer/dhcpv6_daemon.c @@ -281,7 +281,7 @@ static void ensure_binds() { spec.ver = IP_VER6; spec.l3_id = b->bound_linklocal_l3_id; - if (socket_bind_udp_ex(b->sock, &spec, DHCPV6_CLIENT_PORT) != SOCK_OK) { + if (socket_bind_udp(b->sock, &spec, DHCPV6_CLIENT_PORT) != SOCK_OK) { socket_destroy_udp(b->sock); b->sock = 0; free_sized(b, sizeof(*b)); @@ -552,7 +552,7 @@ static void fsm_once(dhcpv6_bind_t* b, uint32_t tick_ms) { mcast_servers(dst.ip); dst.port = DHCPV6_SERVER_PORT; - (void)socket_sendto_udp_ex(b->sock, DST_ENDPOINT, &dst, 0, (const void*)msg, (uint64_t)msg_len); + (void)socket_sendto_udp(b->sock, DST_ENDPOINT, &dst, 0, (const void*)msg, (uint64_t)msg_len); b->tx_tries++; uint8_t rx[DHCPV6_MAX_MSG]; @@ -565,7 +565,7 @@ static void fsm_once(dhcpv6_bind_t* b, uint32_t tick_ms) { uint32_t waited = 0; while (waited < 250) { - int64_t r = socket_recvfrom_udp_ex(b->sock, rx, sizeof(rx), &src); + int64_t r = socket_recvfrom_udp(b->sock, rx, sizeof(rx), &src); if (r > 0) { if (src.port != DHCPV6_SERVER_PORT) { msleep(50); diff --git a/kernel/networking/application_layer/dns/dns.c b/kernel/networking/application_layer/dns/dns.c index c0c1de50..c874b846 100644 --- a/kernel/networking/application_layer/dns/dns.c +++ b/kernel/networking/application_layer/dns/dns.c @@ -37,14 +37,14 @@ static dns_result_t perform_dns_query_once(socket_handle_t sock, const net_l4_en net_l4_endpoint dst = *dns_srv; dst.port = 53; - int64_t sent = socket_sendto_udp_ex(sock, DST_ENDPOINT, &dst, 0, request_buffer, request_len); + int64_t sent = socket_sendto_udp(sock, DST_ENDPOINT, &dst, 0, request_buffer, request_len); 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); + int64_t received = socket_recvfrom_udp(sock, response_buffer, sizeof(response_buffer), &source); if (received > 0 && source.port == 53 && source.ver == dst.ver && ((dst.ver == IP_VER4 && memcmp(source.ip, dst.ip, 4) == 0) || (dst.ver == IP_VER6 && memcmp(source.ip, dst.ip, 16) == 0))) { uint32_t received_len = received; dns_record_t parsed[DNS_QUERY_RECORDS]; diff --git a/kernel/networking/application_layer/dns/dns_daemon.c b/kernel/networking/application_layer/dns/dns_daemon.c index 9f9065e4..481c4e5f 100644 --- a/kernel/networking/application_layer/dns/dns_daemon.c +++ b/kernel/networking/application_layer/dns/dns_daemon.c @@ -40,7 +40,7 @@ static socket_handle_t mdns_create_socket(ip_version_t ver, const void* group) { memset(&spec, 0, sizeof(spec)); spec.kind = BIND_ANY; - if(socket_bind_udp_ex(s, &spec, DNS_MDNS_PORT) != SOCK_OK){ + if(socket_bind_udp(s, &spec, DNS_MDNS_PORT) != SOCK_OK){ socket_destroy_udp(s); return 0; } @@ -70,7 +70,7 @@ int dns_deamon_entry(int argc, char* argv[]){ if (g_sock_mdns4) { memset(&src, 0, sizeof(src)); - int64_t r4 = socket_recvfrom_udp_ex(g_sock_mdns4, buf, sizeof(buf), &src); + int64_t r4 = socket_recvfrom_udp(g_sock_mdns4, buf, sizeof(buf), &src); if(r4 > 0) { uint32_t len4 = r4; mdns_responder_handle_query(g_sock_mdns4, IP_VER4, mdns_v4_addr, buf, len4, &src); @@ -79,7 +79,7 @@ int dns_deamon_entry(int argc, char* argv[]){ if (g_sock_mdns6) { memset(&src, 0, sizeof(src)); - int64_t r6 = socket_recvfrom_udp_ex(g_sock_mdns6, buf, sizeof(buf), &src); + int64_t r6 = socket_recvfrom_udp(g_sock_mdns6, buf, sizeof(buf), &src); if(r6 > 0) { uint32_t len6 = r6; mdns_responder_handle_query(g_sock_mdns6, IP_VER6, mdns_v6, buf, len6, &src); diff --git a/kernel/networking/application_layer/dns/dns_mdns.c b/kernel/networking/application_layer/dns/dns_mdns.c index 3dbd9f56..e6a52b8d 100644 --- a/kernel/networking/application_layer/dns/dns_mdns.c +++ b/kernel/networking/application_layer/dns/dns_mdns.c @@ -15,7 +15,7 @@ static dns_result_t perform_mdns_query_once(socket_handle_t sock, const net_l4_e uint32_t offset = dns_wire_build_query(request_buffer, sizeof(request_buffer), 0, name, qtype, false); if (!offset) return DNS_ERR_FORMAT; - int64_t sent = socket_sendto_udp_ex(sock, DST_ENDPOINT, dst, 0, request_buffer, offset); + int64_t sent = socket_sendto_udp(sock, DST_ENDPOINT, dst, 0, request_buffer, offset); if (sent < 0) return DNS_ERR_SEND; uint32_t found = 0; @@ -23,7 +23,7 @@ static dns_result_t perform_mdns_query_once(socket_handle_t sock, const net_l4_e 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); + int64_t received = socket_recvfrom_udp(sock, response_buffer, sizeof(response_buffer), &source); if (received > 0 && source.port == DNS_MDNS_PORT){ uint32_t received_len = received; dns_record_t records[12]; diff --git a/kernel/networking/application_layer/dns/mdns_responder.c b/kernel/networking/application_layer/dns/mdns_responder.c index 2572b592..31e1f480 100644 --- a/kernel/networking/application_layer/dns/mdns_responder.c +++ b/kernel/networking/application_layer/dns/mdns_responder.c @@ -82,7 +82,7 @@ static void mdns_send(socket_handle_t sock, const net_l4_endpoint *src, bool uni if (unicast && src) { dst = *src; if (!dst.port) dst.port = DNS_MDNS_PORT; - socket_sendto_udp_ex(sock, DST_ENDPOINT, &dst, 0, (void*)pkt, pkt_len); + socket_sendto_udp(sock, DST_ENDPOINT, &dst, 0, (void*)pkt, pkt_len); return; } @@ -90,7 +90,7 @@ static void mdns_send(socket_handle_t sock, const net_l4_endpoint *src, bool uni if (ver == IP_VER4) memcpy(dst.ip, mcast_ip, 4); else memcpy(dst.ip, mcast_ip, 16); dst.port = DNS_MDNS_PORT; - socket_sendto_udp_ex(sock, DST_ENDPOINT, &dst, 0, (void*)pkt, pkt_len); + socket_sendto_udp(sock, DST_ENDPOINT, &dst, 0, (void*)pkt, pkt_len); } static bool mdns_pick_identity(uint32_t *out_v4, uint8_t out_v6[16], uint8_t *out_ifindex, uint8_t out_ifid[8]) { diff --git a/kernel/networking/application_layer/http.c b/kernel/networking/application_layer/http.c index 9168a482..3a23efd5 100644 --- a/kernel/networking/application_layer/http.c +++ b/kernel/networking/application_layer/http.c @@ -1,8 +1,128 @@ #include "http.h" #include "std/string.h" #include "std/memory.h" -#include "syscalls/syscalls.h" -//TODO check for a maximum request body size before allocating the buffer + +HTTPPolicy http_default_policy(void) { + HTTPPolicy p = { + .max_start_line = HTTP_DEFAULT_MAX_START_LINE, + .max_header_bytes = HTTP_DEFAULT_MAX_HEADER_BYTES, + .max_header_count = HTTP_DEFAULT_MAX_HEADER_COUNT, + .max_header_key_len = HTTP_DEFAULT_MAX_HEADER_KEY_LEN, + .max_header_value_len = HTTP_DEFAULT_MAX_HEADER_VALUE_LEN, + .max_path_len = HTTP_DEFAULT_MAX_PATH_LEN, + .max_body_bytes = HTTP_DEFAULT_MAX_BODY_BYTES, + .header_idle_timeout_ms = HTTP_DEFAULT_HEADER_IDLE_TIMEOUT_MS, + .header_total_timeout_ms = HTTP_DEFAULT_HEADER_TOTAL_TIMEOUT_MS, + .body_idle_timeout_ms = HTTP_DEFAULT_BODY_IDLE_TIMEOUT_MS, + .body_total_timeout_ms = HTTP_DEFAULT_BODY_TOTAL_TIMEOUT_MS, + .allow_chunked = false, + .allow_keep_alive = false, + .allow_absolute_uri = true, + .require_host_http11 = true + }; + return p; +} + +const char* http_status_reason(HttpError status) { + switch (status) { + case HTTP_OK: return "OK"; + case HTTP_BAD_REQUEST: return "Bad Request"; + case HTTP_UNAUTHORIZED: return "Unauthorized"; + case HTTP_FORBIDDEN: return "Forbidden"; + case HTTP_NOT_FOUND: return "Not Found"; + case HTTP_PAYLOAD_TOO_LARGE: return "Payload Too Large"; + case HTTP_URI_TOO_LONG: return "URI Too Long"; + case HTTP_EXPECTATION_FAILED: return "Expectation Failed"; + case HTTP_HEADER_FIELDS_TOO_LARGE: return "Request Header Fields Too Large"; + case HTTP_INTERNAL_SERVER_ERROR: return "Internal Server Error"; + case HTTP_NOT_IMPLEMENTED: return "Not Implemented"; + case HTTP_SERVICE_UNAVAILABLE: return "Service Unavailable"; + case HTTP_VERSION_NOT_SUPPORTED: return "HTTP Version Not Supported"; + default: return "Error"; + } +} + +HttpError http_parse_result_status(HTTPParseResult result) { + switch (result) { + case HTTP_PARSE_TOO_LARGE: + case HTTP_PARSE_TOO_MANY_HEADERS: + return HTTP_HEADER_FIELDS_TOO_LARGE; + case HTTP_PARSE_BAD_CONTENT_LENGTH: + case HTTP_PARSE_BAD_FORMAT: + case HTTP_PARSE_MISSING_HOST: + return HTTP_BAD_REQUEST; + case HTTP_PARSE_UNSUPPORTED_TRANSFER: + case HTTP_PARSE_UNSUPPORTED_METHOD: + return HTTP_NOT_IMPLEMENTED; + case HTTP_PARSE_UNSUPPORTED_VERSION: + return HTTP_VERSION_NOT_SUPPORTED; + case HTTP_PARSE_PAYLOAD_TOO_LARGE: + return HTTP_PAYLOAD_TOO_LARGE; + default: + return HTTP_BAD_REQUEST; + } +} + +HTTPParseResult http_parse_request_line(const char *buf, uint32_t len, HTTPRequestLine *out) { + if (!buf || !out || !len) return HTTP_PARSE_BAD_FORMAT; + *out = (HTTPRequestLine){0}; + + uint32_t i = 0; + while (i < len && buf[i] != ' ') i++; + uint32_t mlen = i; + if (!mlen || i >= len) return HTTP_PARSE_BAD_FORMAT; + + if (mlen == 3 && memcmp(buf, "GET", 3) == 0) out->method = HTTP_METHOD_GET; + else if (mlen == 4 && memcmp(buf, "POST", 4) == 0) out->method = HTTP_METHOD_POST; + else if (mlen == 3 && memcmp(buf, "PUT", 3) == 0) out->method = HTTP_METHOD_PUT; + else if (mlen == 6 && memcmp(buf, "DELETE", 6) == 0) out->method = HTTP_METHOD_DELETE; + else return HTTP_PARSE_UNSUPPORTED_METHOD; + + i++; + while (i < len && buf[i] == ' ')i++; + uint32_t target_off = i; + while (i < len && buf[i] != ' ')i++; + uint32_t target_len = i - target_off; + if (!target_len || i >= len) return HTTP_PARSE_BAD_FORMAT; + + i++; + while (i < len && buf[i] == ' ') i++; + uint32_t version_len = len - i; + if (version_len != 8) return HTTP_PARSE_UNSUPPORTED_VERSION; + if (memcmp(buf + i, "HTTP/1.0", 8) == 0) out->version = HTTP_VERSION_10; + else if (memcmp(buf + i, "HTTP/1.1", 8) == 0) out->version = HTTP_VERSION_11; + else return HTTP_PARSE_UNSUPPORTED_VERSION; + + out->target_off = target_off; + out->target_len = target_len; + return HTTP_PARSE_OK; +} + +HTTPParseResult http_parse_status_line(const char *buf, uint32_t len, HTTPStatusLine *out) { + if (!buf || !out || len < 12) return HTTP_PARSE_BAD_FORMAT; + *out = (HTTPStatusLine){0}; + + if (memcmp(buf, "HTTP/1.0", 8) == 0) out->version = HTTP_VERSION_10; + else if (memcmp(buf, "HTTP/1.1", 8) == 0) out->version = HTTP_VERSION_11; + else return HTTP_PARSE_UNSUPPORTED_VERSION; + + if (buf[8] != ' ') return HTTP_PARSE_BAD_FORMAT; + char code_buf[4]; + memcpy(code_buf, buf + 9,3); + code_buf[3] = 0; + + char *end = code_buf; + uint64_t code = strtoul(code_buf, &end, 10); + if (end != code_buf + 3) return HTTP_PARSE_BAD_FORMAT; + out->status_code = code; + + uint32_t i = 12; + while (i < len && buf[i] == ' ') i++; + out->reason_off = i; + out->reason_len = len - i; + return HTTP_PARSE_OK; +} + string http_header_builder(const HTTPHeadersCommon *C, const HTTPHeader *H, uint32_t N){ string out = string_repeat('\0', 0); @@ -71,6 +191,7 @@ void http_headers_common_free(HTTPHeadersCommon *C){ if (C->keep_alive.mem_length) string_free(C->keep_alive); if (C->host.mem_length) string_free(C->host); if (C->content_type.mem_length) string_free(C->content_type); + if (C->transfer_encoding.mem_length) string_free(C->transfer_encoding); *C = (HTTPHeadersCommon){0}; } @@ -83,12 +204,19 @@ void http_headers_extra_free(HTTPHeader *extra, uint32_t extra_count){ release(extra); } -void http_header_parser(const char *buf, uint32_t len, - HTTPHeadersCommon *C, - HTTPHeader **out_extra, - uint32_t *out_extra_count) +HTTPParseResult http_header_parse_policy(const char *buf, uint32_t len, + const HTTPPolicy *policy, + HTTPHeadersCommon *C, + HTTPHeader **out_extra, + uint32_t *out_extra_count) { + HTTPPolicy p = policy ? *policy : http_default_policy(); + if (!buf || !C || !out_extra || !out_extra_count) return HTTP_PARSE_BAD_FORMAT; + if (len > p.max_header_bytes) return HTTP_PARSE_TOO_LARGE; + *C = (HTTPHeadersCommon){0}; + *out_extra = NULL; + *out_extra_count = 0; uint32_t max_lines = 0; uint32_t count_pos = 0; @@ -104,7 +232,20 @@ void http_header_parser(const char *buf, uint32_t len, } if (!has_crlf) eol = len; if (eol == count_pos) break; + + uint32_t sep = count_pos; + while (sep < eol && buf[sep] != ':') sep++; + if (sep == eol || sep == count_pos) return HTTP_PARSE_BAD_FORMAT; + + uint32_t key_len = sep - count_pos; + uint32_t val_start = sep + 1; + while (val_start < eol && is_whitespace(buf[val_start])) val_start++; + uint32_t val_end = eol; + while (val_end > val_start && is_whitespace(buf[val_end-1])) val_end--; + + if (key_len > p.max_header_key_len || key_len >= 128 || val_end - val_start > p.max_header_value_len) return HTTP_PARSE_TOO_LARGE; max_lines++; + if (max_lines > p.max_header_count) return HTTP_PARSE_TOO_MANY_HEADERS; if (!has_crlf) break; count_pos = eol + 2; } @@ -112,18 +253,12 @@ void http_header_parser(const char *buf, uint32_t len, HTTPHeader *extras = NULL; if (max_lines){ extras = (HTTPHeader*)zalloc(sizeof(*extras) * max_lines); - if (!extras){ - *out_extra = NULL; - *out_extra_count = 0; - return; - } + if (!extras) return HTTP_PARSE_TOO_LARGE; } + HTTPParseResult result = HTTP_PARSE_OK; uint32_t extra_i = 0; uint32_t pos = 0; - - char key_tmp[64]; - while (pos < len){ uint32_t eol = pos; bool has_crlf = false; @@ -136,70 +271,63 @@ void http_header_parser(const char *buf, uint32_t len, } if (!has_crlf) eol = len; - if (eol == pos){ - if (has_crlf) pos += 2; - break; - } + if (eol == pos) break; uint32_t sep = pos; while (sep < eol && buf[sep] != ':') sep++; - if (sep == eol){ - if (!has_crlf) break; - pos = eol + 2; - continue; - } - uint32_t key_len = sep - pos; uint32_t val_start = sep + 1; - while (val_start < eol && (buf[val_start]==' ' || buf[val_start]=='\t')) val_start++; - - uint32_t val_len = eol - val_start; - - uint32_t copy_len = (key_len < sizeof(key_tmp)-1) ? key_len : (sizeof(key_tmp)-1); - for (uint32_t i = 0; i < copy_len; i++){ - key_tmp[i] = buf[pos + i]; - } - key_tmp[copy_len] = '\0'; - - if (copy_len == 14 && strcmp_case(key_tmp, "content-length", true) == 0){ - uint32_t p = val_start; - uint32_t end = eol; - while (p < end && (buf[p] == ' ' || buf[p] == '\t')) p++; - while (end > p && (buf[end-1] == ' ' || buf[end-1] == '\t')) end--; - - bool ok = p < end; + while (val_start < eol && is_whitespace(buf[val_start])) val_start++; + uint32_t val_end = eol; + while (val_end > val_start && is_whitespace(buf[val_end - 1])) val_end--; + uint32_t val_len = val_end - val_start; + + if (key_len == 14 && strncmp_case(buf + pos, "content-length", true, key_len) == 0){ + char len_buf[32]; + bool ok = val_len > 0 && val_len < sizeof(len_buf); uint64_t parsed = 0; - while (ok && p < end) { - char c = buf[p++]; - if (! is_digit(c)) { - ok = false; - break; - } - parsed = parsed * 10 + (uint64_t)(c - '0'); - if (parsed > UINT32_MAX) ok = false; + if (ok) { + memcpy(len_buf, buf + val_start, val_len); + len_buf[val_len] = 0; + char *end = len_buf; + parsed = strtoul(len_buf, &end, 10); + ok = end == len_buf + val_len && parsed <= UINT32_MAX; } - if (!ok || (C->has_length && C->length != (uint32_t)parsed)) C->bad_length = 1; - else { + if (!ok || (C->has_length && C->length != parsed)) { + C->bad_length = 1; + result = HTTP_PARSE_BAD_CONTENT_LENGTH; + } else { C->has_length = 1; - C->length = (uint32_t)parsed; + C->length = parsed; } } - else if (copy_len == 12 && strcmp_case(key_tmp, "content-type", true) == 0){ - C->type = string_from_literal_length((char*)(buf + val_start), val_len); - } - else if (copy_len == 4 && strcmp_case(key_tmp, "date", true) == 0){ - C->date = string_from_literal_length((char*)(buf + val_start), val_len); + else if (key_len == 12 && strncmp_case(buf + pos, "content-type", true, key_len) == 0){ + C->type = string_from_literal_length(buf + val_start, val_len); + C->content_type = string_from_literal_length(buf + val_start, val_len); } - else if (copy_len == 10 && strcmp_case(key_tmp, "connection", true) == 0){ - C->connection = string_from_literal_length((char*)(buf + val_start), val_len); - } - else if (copy_len == 10 && strcmp_case(key_tmp, "keep-alive", true) == 0){ - C->keep_alive = string_from_literal_length((char*)(buf + val_start), val_len); - } - else if (copy_len == 4 && strcmp_case(key_tmp, "host", true) == 0){ - C->host = string_from_literal_length((char*)(buf + val_start), val_len); + else if (key_len == 4 && strncmp_case(buf + pos, "date", true, key_len) == 0) C->date = string_from_literal_length(buf + val_start, val_len); + else if (key_len == 10 && strncmp_case(buf + pos, "connection", true, key_len) == 0) C->connection = string_from_literal_length(buf + val_start, val_len); + else if (key_len == 10 && strncmp_case(buf + pos, "keep-alive", true, key_len) == 0) C->keep_alive = string_from_literal_length(buf + val_start, val_len); + else if (key_len == 4 && strncmp_case(buf + pos, "host", true, key_len) == 0) C->host = string_from_literal_length(buf + val_start, val_len); + else if (key_len == 17 && strncmp_case(buf + pos, "transfer-encoding", true, key_len) == 0){ + C->has_transfer_encoding = 1; + C->transfer_encoding = string_from_literal_length(buf + val_start, val_len); + uint32_t te_pos = val_start; + while (te_pos < val_end) { + while (te_pos < val_end && (is_whitespace(buf[te_pos]) || buf[te_pos] == ',')) te_pos++; + uint32_t te_start = te_pos; + while (te_pos < val_end && buf[te_pos] != ',') te_pos++; + uint32_t te_end = te_pos; + while (te_end > te_start && is_whitespace(buf[te_end - 1])) te_end--; + if (te_end - te_start == 7 && strncmp_case(buf + te_start, "chunked", true, 7) == 0) C->chunked = 1; + if (te_pos < val_end && buf[te_pos] == ',') te_pos++; + } + if (C->chunked && !p.allow_chunked) { + C->bad_transfer = 1; + result = HTTP_PARSE_UNSUPPORTED_TRANSFER; + } } else { string key = string_from_literal_length((char*)(buf + pos), key_len); @@ -221,13 +349,13 @@ void http_header_parser(const char *buf, uint32_t len, if (extras) release(extras); *out_extra = NULL; *out_extra_count = 0; - return; + return result; } if (extra_i == max_lines){ *out_extra = extras; *out_extra_count = extra_i; - return; + return result; } HTTPHeader *shr = (HTTPHeader*)zalloc(sizeof(*shr) * extra_i); @@ -236,7 +364,7 @@ void http_header_parser(const char *buf, uint32_t len, release(extras); *out_extra = shr; *out_extra_count = extra_i; - return; + return result; } for (uint32_t i = 0; i < extra_i; i++){ @@ -246,6 +374,7 @@ void http_header_parser(const char *buf, uint32_t len, release(extras); *out_extra = NULL; *out_extra_count = 0; + return HTTP_PARSE_TOO_LARGE; } string http_request_builder(const HTTPRequestMsg *R){ @@ -268,7 +397,11 @@ string http_request_builder(const HTTPRequestMsg *R){ string http_response_builder(const HTTPResponseMsg *R){ string out = string_format("HTTP/1.1 %i ", (int)R->status_code); - string_append_bytes(&out, R->reason.data, R->reason.length); + if (R->reason.length) string_append_bytes(&out, R->reason.data, R->reason.length); + else { + const char *reason = http_status_reason(R->status_code); + string_append_bytes(&out, reason, (uint32_t)strlen(reason)); + } string_append_bytes(&out, "\r\n", 2); string hdrs = http_header_builder(&R->headers_common, R->extra_headers, R->extra_header_count); diff --git a/kernel/networking/application_layer/http.h b/kernel/networking/application_layer/http.h index e3f461e8..0c173503 100644 --- a/kernel/networking/application_layer/http.h +++ b/kernel/networking/application_layer/http.h @@ -7,6 +7,18 @@ extern "C" { #endif +#define HTTP_DEFAULT_MAX_START_LINE 2048 +#define HTTP_DEFAULT_MAX_HEADER_BYTES (16 * 1024) +#define HTTP_DEFAULT_MAX_HEADER_COUNT 64 +#define HTTP_DEFAULT_MAX_HEADER_KEY_LEN 64 +#define HTTP_DEFAULT_MAX_HEADER_VALUE_LEN 4096 +#define HTTP_DEFAULT_MAX_PATH_LEN 2048 +#define HTTP_DEFAULT_MAX_BODY_BYTES (1024*1024) +#define HTTP_DEFAULT_HEADER_IDLE_TIMEOUT_MS 1000 +#define HTTP_DEFAULT_HEADER_TOTAL_TIMEOUT_MS 15000 +#define HTTP_DEFAULT_BODY_IDLE_TIMEOUT_MS 3000 +#define HTTP_DEFAULT_BODY_TOTAL_TIMEOUT_MS 20000 + typedef enum { HTTP_METHOD_GET, HTTP_METHOD_POST, @@ -14,18 +26,74 @@ typedef enum { HTTP_METHOD_DELETE } HTTPMethod; +typedef enum { + HTTP_VERSION_UNKNOWN, + HTTP_VERSION_10, + HTTP_VERSION_11 +} HTTPVersion; + +typedef enum { + HTTP_PARSE_OK, + HTTP_PARSE_BAD_FORMAT, + HTTP_PARSE_TOO_LARGE, + HTTP_PARSE_TOO_MANY_HEADERS, + HTTP_PARSE_BAD_CONTENT_LENGTH, + HTTP_PARSE_UNSUPPORTED_TRANSFER, + HTTP_PARSE_MISSING_HOST, + HTTP_PARSE_UNSUPPORTED_VERSION, + HTTP_PARSE_PAYLOAD_TOO_LARGE, + HTTP_PARSE_UNSUPPORTED_METHOD +} HTTPParseResult; + typedef enum { HTTP_OK = 200, HTTP_BAD_REQUEST = 400, HTTP_UNAUTHORIZED = 401, HTTP_FORBIDDEN = 403, HTTP_NOT_FOUND = 404, + HTTP_PAYLOAD_TOO_LARGE = 413, + HTTP_URI_TOO_LONG = 414, + HTTP_EXPECTATION_FAILED = 417, + HTTP_HEADER_FIELDS_TOO_LARGE = 431, HTTP_INTERNAL_SERVER_ERROR = 500, HTTP_NOT_IMPLEMENTED = 501, HTTP_SERVICE_UNAVAILABLE = 503, + HTTP_VERSION_NOT_SUPPORTED = 505, HTTP_DEBUG = 800, } HttpError; +typedef struct { + uint32_t max_start_line; + uint32_t max_header_bytes; + uint32_t max_header_count; + uint32_t max_header_key_len; + uint32_t max_header_value_len; + uint32_t max_path_len; + uint32_t max_body_bytes; + uint32_t header_idle_timeout_ms; + uint32_t header_total_timeout_ms; + uint32_t body_idle_timeout_ms; + uint32_t body_total_timeout_ms; + bool allow_chunked; + bool allow_keep_alive; + bool allow_absolute_uri; + bool require_host_http11; +} HTTPPolicy; + +typedef struct { + HTTPMethod method; + HTTPVersion version; + uint32_t target_off; + uint32_t target_len; +} HTTPRequestLine; + +typedef struct { + HTTPVersion version; + uint32_t status_code; + uint32_t reason_off; + uint32_t reason_len; +} HTTPStatusLine; + typedef struct { string key; string value; @@ -35,12 +103,16 @@ typedef struct { uint32_t length; uint8_t has_length; uint8_t bad_length; + uint8_t has_transfer_encoding; + uint8_t chunked; + uint8_t bad_transfer; string type; string date; string connection; string keep_alive; string host; string content_type; + string transfer_encoding; } HTTPHeadersCommon; typedef struct { @@ -58,14 +130,20 @@ typedef struct { HTTPHeadersCommon headers_common; HTTPHeader *extra_headers; uint32_t extra_header_count; - sizedptr body; + sizedptr body; } HTTPResponseMsg; +HTTPPolicy http_default_policy(void); +const char* http_status_reason(HttpError status); +HttpError http_parse_result_status(HTTPParseResult result); +HTTPParseResult http_parse_request_line(const char *buf, uint32_t len, HTTPRequestLine *out); +HTTPParseResult http_parse_status_line(const char *buf, uint32_t len, HTTPStatusLine *out); string http_header_builder(const HTTPHeadersCommon *common, const HTTPHeader *extra, uint32_t extra_count); -void http_header_parser(const char *buf, uint32_t len, +HTTPParseResult http_header_parse_policy(const char *buf, uint32_t len, + const HTTPPolicy *policy, HTTPHeadersCommon *out_common, HTTPHeader **out_extra, uint32_t *out_extra_count); diff --git a/kernel/networking/application_layer/ntp.c b/kernel/networking/application_layer/ntp.c index 60406243..73371708 100644 --- a/kernel/networking/application_layer/ntp.c +++ b/kernel/networking/application_layer/ntp.c @@ -110,7 +110,7 @@ static ntp_result_t ntp_send_query(socket_handle_t sock, uint32_t server_ip_host net_l4_endpoint dst; make_ep(server_ip_host, NTP_PORT, IP_VER4, &dst); - int64_t sent = socket_sendto_udp_ex(sock, DST_ENDPOINT, &dst, 0, &p, sizeof(p)); + int64_t sent = socket_sendto_udp(sock, DST_ENDPOINT, &dst, 0, &p, sizeof(p)); if (sent < 0) return NTP_ERR_SEND; *t1_us_out = t1_us; *tx_ntp64_be_out = tx_be; @@ -258,7 +258,7 @@ ntp_result_t ntp_poll_once(uint32_t timeout_ms){ while (waited < timeout_ms) { uint8_t buf[96]; net_l4_endpoint src; - int64_t n = socket_recvfrom_udp_ex(sock, buf, sizeof(buf), &src); + int64_t n = socket_recvfrom_udp(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; diff --git a/kernel/networking/application_layer/sntp.c b/kernel/networking/application_layer/sntp.c index 9f17b3e9..c8db27fd 100644 --- a/kernel/networking/application_layer/sntp.c +++ b/kernel/networking/application_layer/sntp.c @@ -51,7 +51,7 @@ static sntp_result_t sntp_send_query(socket_handle_t sock, uint32_t server_ip_ho p.txTs = unix_us_to_ntp64_be(t1_us); net_l4_endpoint dst; make_ep(server_ip_host, NTP_PORT, IP_VER4, &dst); - int64_t sent = socket_sendto_udp_ex(sock, DST_ENDPOINT, &dst, 0, &p, sizeof(p)); + int64_t sent = socket_sendto_udp(sock, DST_ENDPOINT, &dst, 0, &p, sizeof(p)); if (sent < 0) return SNTP_ERR_SEND; *t1_us_out = t1_us; return SNTP_OK; @@ -105,7 +105,7 @@ sntp_result_t sntp_poll_once(uint32_t timeout_ms){ while (waited < timeout_ms){ uint8_t buf[96]; net_l4_endpoint src; - int64_t n = socket_recvfrom_udp_ex(sock, buf, sizeof(buf), &src); + int64_t n = socket_recvfrom_udp(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; diff --git a/kernel/networking/application_layer/socket_http_client.hpp b/kernel/networking/application_layer/socket_http_client.hpp index fbf872b8..8fd4749c 100644 --- a/kernel/networking/application_layer/socket_http_client.hpp +++ b/kernel/networking/application_layer/socket_http_client.hpp @@ -63,6 +63,7 @@ class HTTPClient { return resp; } + HTTPPolicy policy = http_default_policy(); string out = http_request_builder(&req); uint32_t out_len = out.length; @@ -111,10 +112,18 @@ class HTTPClient { string buf = string_repeat('\0', 0); char tmp[512]; int hdr_end = -1; + uint32_t start_ms = (uint32_t)get_time(); + uint32_t last_rx_ms = start_ms; while (hdr_end < 0) { int64_t r = sock->recv(tmp, sizeof(tmp)); if (r == TCP_WOULDBLOCK) { + uint32_t now = (uint32_t)get_time(); + if ((now - last_rx_ms) > policy.header_idle_timeout_ms || (now - start_ms) > policy.header_total_timeout_ms) { + string_free(buf); + resp.status_code = (HttpError)SOCK_ERR_PROTO; + return resp; + } msleep(10); continue; } @@ -129,40 +138,48 @@ class HTTPClient { return resp; } string_append_bytes(&buf, tmp, (uint32_t)r); + last_rx_ms = (uint32_t)get_time(); + if (buf.length > policy.max_header_bytes) { + string_free(buf); + resp.status_code = (HttpError)SOCK_ERR_PROTO; + return resp; + } hdr_end = find_crlfcrlf(buf.data, buf.length); } - 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; + int status_line_end = strindex((char*)buf.data, "\r\n"); + if (status_line_end <= 0) { + string_free(buf); + resp.status_code = (HttpError)SOCK_ERR_PROTO; + return resp; + } + + HTTPStatusLine status_line{}; + if (http_parse_status_line(buf.data, status_line_end, &status_line) != HTTP_PARSE_OK) { + string_free(buf); + resp.status_code = (HttpError)SOCK_ERR_PROTO; + return resp; } - 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.status_code = (HttpError)status_line.status_code; + if (status_line.reason_len) { resp.reason = string_repeat('\0', 0); - string_append_bytes(&resp.reason, buf.data+j, rlen); + string_append_bytes(&resp.reason, buf.data + status_line.reason_off, status_line.reason_len); } HTTPHeader *extras = nullptr; uint32_t extra_count = 0; - int status_line_end = strindex((char*)buf.data, "\r\n"); - http_header_parser( + HTTPParseResult header_result = http_header_parse_policy( (char*)buf.data + status_line_end + 2, (uint32_t)hdr_end - (uint32_t)(status_line_end + 2), + &policy, &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; - - if (resp.headers_common.bad_length) { + if (header_result != HTTP_PARSE_OK) { if (resp.reason.mem_length) string_free(resp.reason); resp.reason = string{}; http_headers_common_free(&resp.headers_common); @@ -175,45 +192,76 @@ class HTTPClient { return resp; } + uint32_t body_start = (uint32_t)hdr_end + 4; + uint32_t have = buf.length > body_start ? buf.length - body_start : 0; uint32_t need = resp.headers_common.has_length ? resp.headers_common.length : 0; uint32_t body_len = resp.headers_common.has_length ? need : have; char *body_copy = nullptr; + if (body_len > policy.max_body_bytes) { + if (resp.reason.mem_length) string_free(resp.reason); + resp.reason = string{}; + http_headers_common_free(&resp.headers_common); + http_headers_extra_free(resp.extra_headers, resp.extra_header_count); + resp.extra_headers = nullptr; + resp.extra_header_count = 0; + string_free(buf); + sock->close(); + resp.status_code = (HttpError)SOCK_ERR_PROTO; + return resp; + } + if (body_len > 0) { body_copy = (char*)zalloc(body_len); - if (body_copy) { - uint32_t copied = have < body_len ? have : body_len; - if (copied) memcpy(body_copy, buf.data + body_start, copied); - - if (resp.headers_common.has_length) { - while (copied < need) { - int64_t r = sock->recv(body_copy + copied, need - copied); - if (r == TCP_WOULDBLOCK) { - msleep(1); - continue; - } - if (r <= 0) break; - copied += (uint32_t)r; - } + if (!body_copy) { + if (resp.reason.mem_length) string_free(resp.reason); + resp.reason = string{}; + http_headers_common_free(&resp.headers_common); + http_headers_extra_free(resp.extra_headers, resp.extra_header_count); + resp.extra_headers = nullptr; + resp.extra_header_count = 0; + string_free(buf); + sock->close(); + resp.status_code = (HttpError)SOCK_ERR_SYS; + return resp; + } - if (copied < need) { - release(body_copy); - if (resp.reason.mem_length) string_free(resp.reason); - resp.reason = string{}; - http_headers_common_free(&resp.headers_common); - http_headers_extra_free(resp.extra_headers, resp.extra_header_count); - resp.extra_headers = nullptr; - resp.extra_header_count = 0; - string_free(buf); - sock->close(); - resp.status_code = (HttpError)SOCK_ERR_PROTO; - return resp; + uint32_t copied = have < body_len ? have : body_len; + if (copied) memcpy(body_copy, buf.data + body_start, copied); + + if (resp.headers_common.has_length) { + uint32_t body_start_ms = (uint32_t)get_time(); + uint32_t body_last_rx_ms = body_start_ms; + while (copied < need) { + int64_t r = sock->recv(body_copy + copied, need - copied); + if (r == TCP_WOULDBLOCK) { + uint32_t now = (uint32_t)get_time(); + if ((now - body_last_rx_ms) > policy.body_idle_timeout_ms || (now - body_start_ms) > policy.body_total_timeout_ms) break; + msleep(1); + continue; } + if (r <= 0) break; + copied += (uint32_t)r; + body_last_rx_ms = (uint32_t)get_time(); } - resp.body.ptr = (uintptr_t)body_copy; - resp.body.size = body_len; + if (copied < need) { + release(body_copy); + if (resp.reason.mem_length) string_free(resp.reason); + resp.reason = string{}; + http_headers_common_free(&resp.headers_common); + http_headers_extra_free(resp.extra_headers, resp.extra_header_count); + resp.extra_headers = nullptr; + resp.extra_header_count = 0; + string_free(buf); + sock->close(); + resp.status_code = (HttpError)SOCK_ERR_PROTO; + return resp; + } } + + resp.body.ptr = (uintptr_t)body_copy; + resp.body.size = body_len; } netlog_socket_event_t ev1{}; diff --git a/kernel/networking/application_layer/socket_http_server.hpp b/kernel/networking/application_layer/socket_http_server.hpp index e69c86e2..d2964c5e 100644 --- a/kernel/networking/application_layer/socket_http_server.hpp +++ b/kernel/networking/application_layer/socket_http_server.hpp @@ -81,17 +81,21 @@ class HTTPServer { HTTPRequestMsg req{}; if (!client) return req; + HTTPPolicy policy = http_default_policy(); string buf = string_repeat('\0', 0); char tmp[2048]; int hdr_end = -1; uint32_t start_ms = (uint32_t)get_time(); uint32_t last_rx_ms = start_ms; + bool bad_request = false; + HttpError reject_status = HTTP_BAD_REQUEST; + char* body_copy = nullptr; while (hdr_end < 0) { int64_t r = client->recv(tmp, sizeof(tmp)); if (r == TCP_WOULDBLOCK) { uint32_t now = (uint32_t)get_time(); - if ((uint32_t)(now - last_rx_ms) > 1000 || (uint32_t)(now - start_ms) > 15000) { + if ((uint32_t)(now - last_rx_ms) > policy.header_idle_timeout_ms || (uint32_t)(now - start_ms) > policy.header_total_timeout_ms) { string_free(buf); return req; } @@ -104,147 +108,159 @@ class HTTPServer { } string_append_bytes(&buf, tmp, (uint32_t)r); last_rx_ms = (uint32_t)get_time(); - if (buf.length > 16384) { - string_free(buf); - return req; + if (buf.length > policy.max_header_bytes) { + bad_request = true; + reject_status = HTTP_HEADER_FIELDS_TOO_LARGE; + break; } hdr_end = find_crlfcrlf(buf.data, buf.length); } - uint32_t line_end = 0; - while (line_end + 1u < (uint32_t)hdr_end) { - if (buf.data[line_end] == '\r' && buf.data[line_end + 1u] == '\n') - break; - ++line_end; - } + if (!bad_request) { + uint32_t line_end = 0; + while (line_end + 1u < (uint32_t)hdr_end) { + if (buf.data[line_end] == '\r' && buf.data[line_end + 1] == '\n') break; + line_end++; + } - uint32_t p = 0; - while (p + 1u < line_end && buf.data[p] == '\r' && buf.data[p + 1u] == '\n') - p += 2; + if (line_end > policy.max_start_line) { + bad_request = true; + reject_status = HTTP_URI_TOO_LONG; + } - bool bad_request = false; - uint32_t i = p; - while (i < line_end && buf.data[i] != ' ') ++i; - - const char* method_tok = buf.data + p; - uint32_t mlen = i > p ? (i - p) : 0; - - if (mlen == 3 && memcmp(method_tok, "GET", 3) == 0) req.method = HTTP_METHOD_GET; - else if (mlen == 4 && memcmp(method_tok, "POST", 4) == 0) req.method = HTTP_METHOD_POST; - else if (mlen == 3 && memcmp(method_tok, "PUT", 3) == 0) req.method = HTTP_METHOD_PUT; - else if (mlen == 6 && memcmp(method_tok, "DELETE", 6) == 0) req.method = HTTP_METHOD_DELETE; - else bad_request = true; - - if (i >= line_end) bad_request = true; - uint32_t j = (i < line_end) ? (i + 1u) : line_end; - while (j < line_end && buf.data[j] == ' ') ++j; - uint32_t path_start = j; - while (j < line_end && buf.data[j] != ' ') ++j; - uint32_t path_len = j > path_start ? (j - path_start) : 0; - if (!path_len || j >= line_end) bad_request = true; - - uint32_t version_start = j; - while (version_start < line_end && buf.data[version_start ] == ' ') ++version_start; - uint32_t version_len = line_end > version_start ? (line_end - version_start) : 0; - if (version_len != 8) bad_request = true; - else if (memcmp(buf.data + version_start, "HTTP/1.0", 8) != 0 && memcmp(buf.data + version_start, "HTTP/1.1", 8) != 0) bad_request = true; - - req.path = string_repeat('\0', 0); - if (path_len) string_append_bytes(&req.path, buf.data + path_start, path_len); - - if (req.path.length >= 7 && memcmp(req.path.data, "http://", 7) == 0) { - uint32_t k = 7; - while (k < req.path.length && req.path.data[k] != '/') ++k; - if (k < req.path.length) { - string newp = string_repeat('\0', 0); - string_append_bytes(&newp, req.path.data + k, req.path.length - k); - string_free(req.path); - req.path = newp; + HTTPRequestLine line{}; + HTTPParseResult line_result = HTTP_PARSE_OK; + if (!bad_request) { + line_result = http_parse_request_line(buf.data, line_end, &line); + if (line_result != HTTP_PARSE_OK) { + bad_request = true; + reject_status = http_parse_result_status(line_result); + } } - } else if (req.path.length >= 8 && memcmp(req.path.data, "https://", 8) == 0) { - uint32_t k = 8; - while (k < req.path.length && req.path.data[k] != '/') ++k; - if (k < req.path.length) { - string newp = string_repeat('\0', 0); - string_append_bytes(&newp, req.path.data + k, req.path.length - k); - string_free(req.path); - req.path = newp; + + if (!bad_request) { + const char* target = buf.data + line.target_off; + uint32_t target_len = line.target_len; + req.method = line.method; + req.path = string_repeat('\0', 0); + + if (target_len >= 7 && memcmp(target, "http://", 7) == 0) { + if (!policy.allow_absolute_uri) { + bad_request = true; + reject_status = HTTP_BAD_REQUEST; + } else { + uint32_t k = 7; + while (k < target_len && target[k] != '/') k++; + if (k < target_len) string_append_bytes(&req.path, target + k, target_len - k); + else string_append_bytes(&req.path, "/", 1); + } + } else if (target_len >= 8 && memcmp(target, "https://", 8) == 0) { + if (!policy.allow_absolute_uri) { + bad_request = true; + reject_status = HTTP_BAD_REQUEST; + } else { + uint32_t k = 8; + while (k < target_len && target[k] != '/') k++; + if (k < target_len) string_append_bytes(&req.path, target + k, target_len - k); + else string_append_bytes(&req.path, "/", 1); + } + } else string_append_bytes(&req.path, target, target_len); + + if (!bad_request && (!req.path.length || req.path.length > policy.max_path_len)) { + bad_request = true; + reject_status = req.path.length > policy.max_path_len ? HTTP_URI_TOO_LONG : HTTP_BAD_REQUEST; + } } - } - int status_line_end = (int)line_end; - http_header_parser( - (char*)buf.data + status_line_end + 2, - (uint32_t)hdr_end - (uint32_t)(status_line_end + 2), - &req.headers_common, - &req.extra_headers, - &req.extra_header_count - ); - - uint32_t body_start = hdr_end + 4; - uint32_t have = buf.length > body_start ? buf.length - body_start : 0; - uint32_t need = req.headers_common.has_length ? req.headers_common.length : 0; - if (req.headers_common.bad_length) bad_request = true; - char* body_copy = nullptr; + if (!bad_request) { + HTTPParseResult header_result = http_header_parse_policy( + (char*)buf.data + line_end + 2, + (uint32_t)hdr_end - (line_end + 2), + &policy, + &req.headers_common, + &req.extra_headers, + &req.extra_header_count + ); + + if (header_result != HTTP_PARSE_OK) { + bad_request = true; + reject_status = http_parse_result_status(header_result); + } else if (policy.require_host_http11 && line.version == HTTP_VERSION_11 && !req.headers_common.host.length) { + bad_request = true; + reject_status = HTTP_BAD_REQUEST; + } + } + + uint32_t body_start = hdr_end + 4; + uint32_t have = buf.length > body_start ? buf.length - body_start : 0; + uint32_t need = req.headers_common.has_length ? req.headers_common.length : 0; + if (!bad_request && need > policy.max_body_bytes) { + bad_request = true; + reject_status = HTTP_PAYLOAD_TOO_LARGE; + } - if (!bad_request && need > 0) { - body_copy = (char*)zalloc(need); - if (!body_copy) bad_request = true; - else { - uint32_t copied = have < need ? have : need; - if (copied) memcpy(body_copy, buf.data + body_start, copied); - - uint32_t body_start_ms = (uint32_t)get_time(); - uint32_t body_last_rx_ms = body_start_ms; - while (copied < need) { - int64_t r = client->recv(body_copy + copied, need - copied); - if (r == TCP_WOULDBLOCK) { - uint32_t now = (uint32_t)get_time(); - if ((now - body_last_rx_ms) > 3000 || (now - body_start_ms) > 20000) { + if (!bad_request && need > 0) { + body_copy = (char*)zalloc(need); + if (!body_copy) { + bad_request = true; + reject_status = HTTP_INTERNAL_SERVER_ERROR; + } else { + uint32_t copied = have < need ? have : need; + if (copied) memcpy(body_copy, buf.data + body_start, copied); + + uint32_t body_start_ms = (uint32_t)get_time(); + uint32_t body_last_rx_ms = body_start_ms; + while (copied < need) { + int64_t r = client->recv(body_copy + copied, need - copied); + if (r == TCP_WOULDBLOCK) { + uint32_t now = (uint32_t)get_time(); + if ((now - body_last_rx_ms) > policy.body_idle_timeout_ms || (now - body_start_ms) > policy.body_total_timeout_ms) { + bad_request = true; + break; + } + msleep(2); + continue; + } + if (r <= 0) { bad_request = true; break; } - msleep(2); - continue; - } - if (r <= 0) { - bad_request = true; - break; + copied += (uint32_t)r; + body_last_rx_ms = (uint32_t)get_time(); } - copied += (uint32_t)r; - body_last_rx_ms = (uint32_t)get_time(); } } + + if (body_copy && !bad_request) { + req.body.ptr = (uintptr_t)body_copy; + req.body.size = need; + } } if (bad_request) { if (body_copy) release(body_copy); - static const char BAD_BODY[] = "bad request\n"; - static const char BAD_REASON[] = "Bad Request"; - static const char BAD_TYPE[] = "text/plain"; - static const char BAD_CONN[] = "close"; + const char *reason = http_status_reason(reject_status); + string body = string_format("%s\n", reason); HTTPResponseMsg res{}; - res.status_code = HTTP_BAD_REQUEST; - res.reason = (string) {(char*)BAD_REASON, sizeof(BAD_REASON)-1, 0}; - res.headers_common.length = sizeof(BAD_BODY)-1; - res.headers_common.type = (string){(char*)BAD_TYPE, sizeof(BAD_TYPE)-1, 0}; - res.headers_common.connection = (string){(char*)BAD_CONN, sizeof(BAD_CONN)-1, 0}; - res.body.ptr = (uintptr_t)BAD_BODY; - res.body.size = sizeof(BAD_BODY)-1; + res.status_code = reject_status; + res.reason = string_from_literal(reason); + res.headers_common.length = body.length; + res.headers_common.type = string_from_literal("text/plain"); + res.headers_common.connection = string_from_literal("close"); + res.body.ptr = (uintptr_t)body.data; + res.body.size = body.length; send_response(client, res); if (req.path.mem_length) string_free(req.path); http_headers_common_free(&req.headers_common); http_headers_extra_free(req.extra_headers, req.extra_header_count); + http_headers_common_free(&res.headers_common); + if (res.reason.mem_length) string_free(res.reason); + string_free(body); string_free(buf); return HTTPRequestMsg{}; } - if (body_copy) { - req.body.ptr = (uintptr_t)body_copy; - req.body.size = need; - } - netlog_socket_event_t ev{}; ev.comp = NETLOG_COMP_HTTP_SERVER; ev.action = NETLOG_ACT_HTTP_RECV_REQUEST; diff --git a/kernel/networking/application_layer/ssdp_daemon.c b/kernel/networking/application_layer/ssdp_daemon.c index 8771dcd5..e019e177 100644 --- a/kernel/networking/application_layer/ssdp_daemon.c +++ b/kernel/networking/application_layer/ssdp_daemon.c @@ -53,7 +53,7 @@ static void ssdp_send_notify(socket_handle_t s4, socket_handle_t s6, bool alive) string msg = ssdp_build_notify(alive, false); net_l4_endpoint dst; make_ep(ssdp_host_v4, 1900, IP_VER4, &dst); - (void)socket_sendto_udp_ex(s4, DST_ENDPOINT, &dst, 0, msg.data, msg.length); + (void)socket_sendto_udp(s4, DST_ENDPOINT, &dst, 0, msg.data, msg.length); string_free(msg); } @@ -63,7 +63,7 @@ static void ssdp_send_notify(socket_handle_t s4, socket_handle_t s6, bool alive) dst.ver = IP_VER6; memcpy(dst.ip, ssdp_host_v6, 16); dst.port = 1900; - (void)socket_sendto_udp_ex(s6, DST_ENDPOINT, &dst, 0, msg.data, msg.length); + (void)socket_sendto_udp(s6, DST_ENDPOINT, &dst, 0, msg.data, msg.length); string_free(msg); } } @@ -94,12 +94,12 @@ int ssdp_daemon_entry(int argc, char* argv[]) { struct SockBindSpec spec = (struct SockBindSpec){0}; spec.kind = BIND_ANY; - if (s4 && socket_bind_udp_ex(s4, &spec, 1900) < 0) { + if (s4 && socket_bind_udp(s4, &spec, 1900) < 0) { socket_close_udp(s4); socket_destroy_udp(s4); s4 = 0; } - if (s6 && socket_bind_udp_ex(s6, &spec, 1900) < 0) { + if (s6 && socket_bind_udp(s6, &spec, 1900) < 0) { socket_close_udp(s6); socket_destroy_udp(s6); s6 = 0; @@ -127,7 +127,7 @@ int ssdp_daemon_entry(int argc, char* argv[]) { net_l4_endpoint src = (net_l4_endpoint){0}; if (s4) { - int64_t r4 = socket_recvfrom_udp_ex(s4, buf, sizeof(buf) - 1, &src); + int64_t r4 = socket_recvfrom_udp(s4, buf, sizeof(buf) - 1, &src); if (r4 > 0) { buf[r4] = 0; if (ssdp_is_msearch(buf, (int)r4)) ssdp_schedule_response(&src, ssdp_parse_mx_ms(buf, (int)r4)); @@ -135,7 +135,7 @@ int ssdp_daemon_entry(int argc, char* argv[]) { } if (s6) { - int64_t r6 = socket_recvfrom_udp_ex(s6, buf, sizeof(buf) - 1, &src); + int64_t r6 = socket_recvfrom_udp(s6, buf, sizeof(buf) - 1, &src); if (r6 > 0) { buf[r6] = 0; if (ssdp_is_msearch(buf, (int)r6)) ssdp_schedule_response(&src, ssdp_parse_mx_ms(buf, (int)r6)); @@ -159,7 +159,7 @@ int ssdp_daemon_entry(int argc, char* argv[]) { string resp = ssdp_build_search_response(); socket_handle_t sock = (ssdp_pending[i].dst.ver == IP_VER6) ? s6 : s4; - if (sock) (void)socket_sendto_udp_ex(sock, DST_ENDPOINT, &ssdp_pending[i].dst, 0, resp.data, resp.length); + if (sock) (void)socket_sendto_udp(sock, DST_ENDPOINT, &ssdp_pending[i].dst, 0, resp.data, resp.length); string_free(resp); break; } diff --git a/kernel/networking/processes/net_proc.c b/kernel/networking/processes/net_proc.c index 644e5fea..f19a35be 100644 --- a/kernel/networking/processes/net_proc.c +++ b/kernel/networking/processes/net_proc.c @@ -49,7 +49,7 @@ static int udp_probe_server(uint32_t probe_ip, uint16_t probe_port, net_l4_endpo make_ep(probe_ip, probe_port, IP_VER4, &dst); static const char greeting[] = "hello"; - if (socket_sendto_udp_ex(sock, DST_ENDPOINT, &dst, 0, greeting, sizeof(greeting)) < 0) { + if (socket_sendto_udp(sock, DST_ENDPOINT, &dst, 0, greeting, sizeof(greeting)) < 0) { socket_close_udp(sock); socket_destroy_udp(sock); return 0; @@ -63,7 +63,7 @@ static int udp_probe_server(uint32_t probe_ip, uint16_t probe_port, net_l4_endpo net_l4_endpoint src = (net_l4_endpoint){0}; while (waited < TIMEOUT_MS) { - recvd = socket_recvfrom_udp_ex(sock, recv_buf, sizeof(recv_buf), &src); + recvd = socket_recvfrom_udp(sock, recv_buf, sizeof(recv_buf), &src); if (recvd > 0) break; msleep(INTERVAL_MS); @@ -203,7 +203,7 @@ static void test_http(const net_l4_endpoint* ep) { } e.port = 80; - int rc = http_client_connect_ex(cli, DST_ENDPOINT, &e, 0); + int rc = http_client_connect(cli, DST_ENDPOINT, &e, 0); if (rc < 0) { http_client_destroy(cli); return; diff --git a/kernel/networking/transport_layer/csocket.c b/kernel/networking/transport_layer/csocket.c index 42f61586..b89fb13e 100644 --- a/kernel/networking/transport_layer/csocket.c +++ b/kernel/networking/transport_layer/csocket.c @@ -85,7 +85,7 @@ int32_t bind_socket(SocketHandle *handle, uint16_t port, ip_version_t ip_version res = socket_bind_tcp_ex(sh->sh, spec, port); break; case PROTO_UDP: - res = socket_bind_udp_ex(sh->sh, spec, port); + res = socket_bind_udp(sh->sh, spec, port); break; } return res; @@ -123,7 +123,7 @@ int64_t send_on_socket(SocketHandle *handle, uint8_t dst_kind, const void* dst, return socket_send_tcp(sh->sh, buf, len); break; case PROTO_UDP: - return socket_sendto_udp_ex(sh->sh, dst_kind, dst, port, buf, len); + return socket_sendto_udp(sh->sh, dst_kind, dst, port, buf, len); break; } return 0; @@ -142,7 +142,7 @@ int64_t receive_from_socket(SocketHandle *handle, void* buf, uint64_t len, net_l return socket_recv_tcp(sh->sh, buf, len); break; case PROTO_UDP: - return socket_recvfrom_udp_ex(sh->sh, buf, len, out_src); + return socket_recvfrom_udp(sh->sh, buf, len, out_src); break; } return 0; diff --git a/kernel/networking/transport_layer/csocket_udp.cpp b/kernel/networking/transport_layer/csocket_udp.cpp index 3dc04248..19005797 100644 --- a/kernel/networking/transport_layer/csocket_udp.cpp +++ b/kernel/networking/transport_layer/csocket_udp.cpp @@ -6,17 +6,17 @@ extern "C" socket_handle_t udp_socket_create(uint8_t role, uint32_t pid, const S return reinterpret_cast(new UDPSocket(role, pid, extra)); } -extern "C" int32_t socket_bind_udp_ex(socket_handle_t sh, const SockBindSpec* spec, uint16_t port) { +extern "C" int32_t socket_bind_udp(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_ex(socket_handle_t sh, uint8_t dst_kind, const void* dst, uint16_t port, const void* buf, uint64_t len) { +extern "C" int64_t socket_sendto_udp(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_ex(socket_handle_t sh, void* buf, uint64_t len, net_l4_endpoint* out_src) { +extern "C" int64_t socket_recvfrom_udp(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); } diff --git a/kernel/networking/transport_layer/csocket_udp.h b/kernel/networking/transport_layer/csocket_udp.h index 479fefcf..7872b8b4 100644 --- a/kernel/networking/transport_layer/csocket_udp.h +++ b/kernel/networking/transport_layer/csocket_udp.h @@ -11,9 +11,9 @@ extern "C" { typedef void* socket_handle_t; socket_handle_t udp_socket_create(uint8_t role, uint32_t pid, const SocketExtraOptions* extra); -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_bind_udp(socket_handle_t sh, const SockBindSpec* spec, uint16_t port); +int64_t socket_sendto_udp(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(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); From 08f7fe748d1987e34822e996d78553bd95376e96 Mon Sep 17 00:00:00 2001 From: CodeAnarchist Date: Wed, 27 May 2026 00:45:25 +0200 Subject: [PATCH 17/39] [http] add chunked HTTP bodies and keep-alive support --- .../application_layer/csocket_http_client.cpp | 10 +- .../application_layer/csocket_http_client.h | 3 +- .../application_layer/csocket_http_server.cpp | 24 +- .../application_layer/csocket_http_server.h | 3 +- kernel/networking/application_layer/http.c | 207 +++++++++++++++--- kernel/networking/application_layer/http.h | 44 +++- .../application_layer/socket_http_client.hpp | 144 ++++++++---- .../application_layer/socket_http_server.hpp | 165 ++++++++++---- kernel/networking/processes/net_proc.c | 4 +- 9 files changed, 464 insertions(+), 140 deletions(-) diff --git a/kernel/networking/application_layer/csocket_http_client.cpp b/kernel/networking/application_layer/csocket_http_client.cpp index dde77ff8..efaa7f85 100644 --- a/kernel/networking/application_layer/csocket_http_client.cpp +++ b/kernel/networking/application_layer/csocket_http_client.cpp @@ -6,8 +6,8 @@ extern "C" { -http_client_handle_t http_client_create(uint16_t pid, const SocketExtraOptions* extra) { - HTTPClient* cli = new HTTPClient(pid, extra); +http_client_handle_t http_client_create(uint16_t pid, const SocketExtraOptions* extra, const HTTPPolicyOptions *options) { + HTTPClient* cli = new HTTPClient(pid, extra, options); if (!cli) return nullptr; return reinterpret_cast(cli); } @@ -18,6 +18,12 @@ void http_client_destroy(http_client_handle_t h) { delete cli; } +int32_t http_client_set_options(http_client_handle_t h, const HTTPPolicyOptions *options) { + if (!h) return (int32_t)SOCK_ERR_INVAL; + HTTPClient* cli = reinterpret_cast(h); + return cli->set_options(options); +} + int32_t http_client_connect(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); diff --git a/kernel/networking/application_layer/csocket_http_client.h b/kernel/networking/application_layer/csocket_http_client.h index a0afacb8..c37ea93f 100644 --- a/kernel/networking/application_layer/csocket_http_client.h +++ b/kernel/networking/application_layer/csocket_http_client.h @@ -8,7 +8,8 @@ extern "C" { typedef void* http_client_handle_t; -http_client_handle_t http_client_create(uint16_t pid, const SocketExtraOptions* extra); +http_client_handle_t http_client_create(uint16_t pid, const SocketExtraOptions* extra, const HTTPPolicyOptions *options); +int32_t http_client_set_options(http_client_handle_t h, const HTTPPolicyOptions *options); void http_client_destroy(http_client_handle_t h); int32_t http_client_connect(http_client_handle_t h, uint8_t dst_kind, const void *dst, uint16_t port); diff --git a/kernel/networking/application_layer/csocket_http_server.cpp b/kernel/networking/application_layer/csocket_http_server.cpp index afe75eba..08d67876 100644 --- a/kernel/networking/application_layer/csocket_http_server.cpp +++ b/kernel/networking/application_layer/csocket_http_server.cpp @@ -6,8 +6,8 @@ extern "C" { -http_server_handle_t http_server_create(uint16_t pid, const SocketExtraOptions* extra) { - HTTPServer* srv = new HTTPServer(pid, extra); +http_server_handle_t http_server_create(uint16_t pid, const SocketExtraOptions* extra, const HTTPPolicyOptions *options) { + HTTPServer* srv = new HTTPServer(pid, extra, options); if (!srv) return nullptr; return reinterpret_cast(srv); } @@ -18,6 +18,12 @@ void http_server_destroy(http_server_handle_t h) { delete srv; } +int32_t http_server_set_options(http_server_handle_t h, const HTTPPolicyOptions *options) { + if (!h) return (int32_t)SOCK_ERR_INVAL; + HTTPServer* srv = reinterpret_cast(h); + return srv->set_options(options); +} + int32_t http_server_bind(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); @@ -33,29 +39,31 @@ int32_t http_server_listen(http_server_handle_t h, int backlog) { http_connection_handle_t http_server_accept(http_server_handle_t h) { if (!h) return nullptr; HTTPServer* srv = reinterpret_cast(h); - TCPSocket* cli = srv->accept(); - return reinterpret_cast(cli); + HTTPConnection* conn = srv->accept(); + return reinterpret_cast(conn); } HTTPRequestMsg http_server_recv_request(http_server_handle_t h, http_connection_handle_t c) { HTTPRequestMsg empty{}; if (!h || !c) return empty; HTTPServer* srv = reinterpret_cast(h); - TCPSocket* conn = reinterpret_cast(c); + HTTPConnection* 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) { if (!h || !c || !res) return (int32_t)SOCK_ERR_INVAL; HTTPServer* srv = reinterpret_cast(h); - TCPSocket* conn = reinterpret_cast(c); + HTTPConnection* conn = reinterpret_cast(c); return srv->send_response(conn, *res); } int32_t http_connection_close(http_connection_handle_t c) { if (!c) return (int32_t)SOCK_ERR_INVAL; - TCPSocket* conn = reinterpret_cast(c); - delete conn; + HTTPConnection* conn = reinterpret_cast(c); + if (conn->carry_buf.mem_length) string_free(conn->carry_buf); + if (conn->client) delete conn->client; + release(conn); return (int32_t)SOCK_OK; } diff --git a/kernel/networking/application_layer/csocket_http_server.h b/kernel/networking/application_layer/csocket_http_server.h index 0b673473..16e08d31 100644 --- a/kernel/networking/application_layer/csocket_http_server.h +++ b/kernel/networking/application_layer/csocket_http_server.h @@ -10,7 +10,8 @@ extern "C" { typedef void* http_server_handle_t; typedef void* http_connection_handle_t; -http_server_handle_t http_server_create(uint16_t pid, const SocketExtraOptions* extra); +http_server_handle_t http_server_create(uint16_t pid, const SocketExtraOptions* extra, const HTTPPolicyOptions *options); +int32_t http_server_set_options(http_server_handle_t srv, const HTTPPolicyOptions *options); void http_server_destroy(http_server_handle_t srv); int32_t http_server_bind(http_server_handle_t srv, const struct SockBindSpec *spec, uint16_t port); diff --git a/kernel/networking/application_layer/http.c b/kernel/networking/application_layer/http.c index 3a23efd5..920fedba 100644 --- a/kernel/networking/application_layer/http.c +++ b/kernel/networking/application_layer/http.c @@ -15,7 +15,9 @@ HTTPPolicy http_default_policy(void) { .header_total_timeout_ms = HTTP_DEFAULT_HEADER_TOTAL_TIMEOUT_MS, .body_idle_timeout_ms = HTTP_DEFAULT_BODY_IDLE_TIMEOUT_MS, .body_total_timeout_ms = HTTP_DEFAULT_BODY_TOTAL_TIMEOUT_MS, - .allow_chunked = false, + .max_keepalive_requests = HTTP_DEFAULT_MAX_KEEPALIVE_REQUESTS, + .max_redirects = HTTP_DEFAULT_MAX_REDIRECTS, + .allow_chunked = true, .allow_keep_alive = false, .allow_absolute_uri = true, .require_host_http11 = true @@ -23,9 +25,39 @@ HTTPPolicy http_default_policy(void) { return p; } +HTTPPolicy http_policy_from_options(const HTTPPolicyOptions *options) { + HTTPPolicy p = http_default_policy(); + if (!options) return p; + + if (options->max_start_line > 0) p.max_start_line = options->max_start_line; + if (options->max_header_bytes > 0) p.max_header_bytes = options->max_header_bytes; + if (options->max_header_count > 0) p.max_header_count = options->max_header_count; + if (options->max_header_key_len > 0) p.max_header_key_len = options->max_header_key_len; + if (options->max_header_value_len > 0) p.max_header_value_len = options->max_header_value_len; + if (options->max_path_len > 0) p.max_path_len = options->max_path_len; + if (options->max_body_bytes > 0) p.max_body_bytes = options->max_body_bytes; + if (options->header_idle_timeout_ms > 0) p.header_idle_timeout_ms = options->header_idle_timeout_ms; + if (options->header_total_timeout_ms > 0) p.header_total_timeout_ms = options->header_total_timeout_ms; + if (options->body_idle_timeout_ms > 0) p.body_idle_timeout_ms = options->body_idle_timeout_ms; + if (options->body_total_timeout_ms > 0) p.body_total_timeout_ms = options->body_total_timeout_ms; + if (options->max_keepalive_requests > 0) p.max_keepalive_requests = options->max_keepalive_requests; + if (options->max_redirects > 0) p.max_redirects = options->max_redirects; + + p.allow_chunked = options->allow_chunked != 0; + p.allow_keep_alive = options->allow_keep_alive != 0; + p.allow_absolute_uri = options->allow_absolute_uri != 0; + p.require_host_http11 = options->require_host_http11 != 0; + return p; +} + const char* http_status_reason(HttpError status) { switch (status) { case HTTP_OK: return "OK"; + case HTTP_MOVED_PERMANENTLY: return "Moved Permanently"; + case HTTP_FOUND: return "Found"; + case HTTP_SEE_OTHER: return "See Other"; + case HTTP_TEMPORARY_REDIRECT: return "Temporary Redirect"; + case HTTP_PERMANENT_REDIRECT: return "Permanent Redirect"; case HTTP_BAD_REQUEST: return "Bad Request"; case HTTP_UNAUTHORIZED: return "Unauthorized"; case HTTP_FORBIDDEN: return "Forbidden"; @@ -58,11 +90,30 @@ HttpError http_parse_result_status(HTTPParseResult result) { return HTTP_VERSION_NOT_SUPPORTED; case HTTP_PARSE_PAYLOAD_TOO_LARGE: return HTTP_PAYLOAD_TOO_LARGE; + case HTTP_PARSE_INCOMPLETE: + return HTTP_BAD_REQUEST; default: return HTTP_BAD_REQUEST; } } +bool http_header_value_has_token(const char *buf, uint32_t len, const char *token, uint32_t token_len) { + if (!buf || !token || !token_len) return false; + + uint32_t pos = 0; + while (pos < len) { + while (pos < len && (is_whitespace(buf[pos]) || buf[pos] == ',')) pos++; + uint32_t start = pos; + while (pos < len && buf[pos] != ',') pos++; + uint32_t end = pos; + while (end > start && is_whitespace(buf[end - 1])) end--; + if (end - start == token_len && strncmp_case(buf + start, token, true, token_len) == 0) return true; + if (pos < len && buf[pos] == ',') pos++; + } + + return false; +} + HTTPParseResult http_parse_request_line(const char *buf, uint32_t len, HTTPRequestLine *out) { if (!buf || !out || !len) return HTTP_PARSE_BAD_FORMAT; *out = (HTTPRequestLine){0}; @@ -111,9 +162,8 @@ HTTPParseResult http_parse_status_line(const char *buf, uint32_t len, HTTPStatus memcpy(code_buf, buf + 9,3); code_buf[3] = 0; - char *end = code_buf; - uint64_t code = strtoul(code_buf, &end, 10); - if (end != code_buf + 3) return HTTP_PARSE_BAD_FORMAT; + uint32_t code = 0; + if (!parse_uint32_dec(code_buf, &code)) return HTTP_PARSE_BAD_FORMAT; out->status_code = code; uint32_t i = 12; @@ -132,9 +182,12 @@ string http_header_builder(const HTTPHeadersCommon *C, const HTTPHeader *H, uint string_append_bytes(&out, "\r\n", 2); } - string tmp = string_format("Content-Length: %i\r\n", (int)C->length); - string_append_bytes(&out, tmp.data, tmp.length); - string_free(tmp); + if (C->chunked) string_append_bytes(&out, "Transfer-Encoding: chunked\r\n", 28); + else { + string tmp = string_format("Content-Length: %i\r\n", (int)C->length); + string_append_bytes(&out, tmp.data, tmp.length); + string_free(tmp); + } if (C->date.length){ string_append_bytes(&out, "Date: ", 6); @@ -286,13 +339,11 @@ HTTPParseResult http_header_parse_policy(const char *buf, uint32_t len, if (key_len == 14 && strncmp_case(buf + pos, "content-length", true, key_len) == 0){ char len_buf[32]; bool ok = val_len > 0 && val_len < sizeof(len_buf); - uint64_t parsed = 0; + uint32_t parsed = 0; if (ok) { memcpy(len_buf, buf + val_start, val_len); len_buf[val_len] = 0; - char *end = len_buf; - parsed = strtoul(len_buf, &end, 10); - ok = end == len_buf + val_len && parsed <= UINT32_MAX; + ok = parse_uint32_dec(len_buf, &parsed); } if (!ok || (C->has_length && C->length != parsed)) { @@ -308,22 +359,17 @@ HTTPParseResult http_header_parse_policy(const char *buf, uint32_t len, C->content_type = string_from_literal_length(buf + val_start, val_len); } else if (key_len == 4 && strncmp_case(buf + pos, "date", true, key_len) == 0) C->date = string_from_literal_length(buf + val_start, val_len); - else if (key_len == 10 && strncmp_case(buf + pos, "connection", true, key_len) == 0) C->connection = string_from_literal_length(buf + val_start, val_len); + else if (key_len == 10 && strncmp_case(buf + pos, "connection", true, key_len) == 0){ + C->connection = string_from_literal_length(buf + val_start, val_len); + C->connection_close = http_header_value_has_token(buf + val_start, val_len, "close", 5) ? 1 : 0; + C->connection_keep_alive = http_header_value_has_token(buf + val_start, val_len, "keep-alive", 10) ? 1 : 0; + } else if (key_len == 10 && strncmp_case(buf + pos, "keep-alive", true, key_len) == 0) C->keep_alive = string_from_literal_length(buf + val_start, val_len); else if (key_len == 4 && strncmp_case(buf + pos, "host", true, key_len) == 0) C->host = string_from_literal_length(buf + val_start, val_len); else if (key_len == 17 && strncmp_case(buf + pos, "transfer-encoding", true, key_len) == 0){ C->has_transfer_encoding = 1; C->transfer_encoding = string_from_literal_length(buf + val_start, val_len); - uint32_t te_pos = val_start; - while (te_pos < val_end) { - while (te_pos < val_end && (is_whitespace(buf[te_pos]) || buf[te_pos] == ',')) te_pos++; - uint32_t te_start = te_pos; - while (te_pos < val_end && buf[te_pos] != ',') te_pos++; - uint32_t te_end = te_pos; - while (te_end > te_start && is_whitespace(buf[te_end - 1])) te_end--; - if (te_end - te_start == 7 && strncmp_case(buf + te_start, "chunked", true, 7) == 0) C->chunked = 1; - if (te_pos < val_end && buf[te_pos] == ',') te_pos++; - } + C->chunked = http_header_value_has_token(buf + val_start, val_len, "chunked", 7) ? 1 : 0; if (C->chunked && !p.allow_chunked) { C->bad_transfer = 1; result = HTTP_PARSE_UNSUPPORTED_TRANSFER; @@ -377,6 +423,20 @@ HTTPParseResult http_header_parse_policy(const char *buf, uint32_t len, return HTTP_PARSE_TOO_LARGE; } +static void http_append_chunked_body(string *out, uintptr_t ptr, uint32_t len) { + if (!out) return; + + if (len) { + string tmp = string_format("%x\r\n", (int)len); + string_append_bytes(out, tmp.data, tmp.length); + string_free(tmp); + string_append_bytes(out, (char*)ptr, len); + string_append_bytes(out, "\r\n", 2); + } + + string_append_bytes(out, "0\r\n\r\n", 5); +} + string http_request_builder(const HTTPRequestMsg *R){ static const char *Mnames[] = { "GET", "POST", "PUT", "DELETE" }; string out = string_format("%s ", Mnames[R->method]); @@ -388,9 +448,8 @@ string http_request_builder(const HTTPRequestMsg *R){ string_append_bytes(&out, hdrs.data, hdrs.length); string_free(hdrs); - if (R->body.ptr && R->body.size){ - string_append_bytes(&out, (char*)R->body.ptr, (uint32_t)R->body.size); - } + if (R->headers_common.chunked) http_append_chunked_body(&out, R->body.ptr, (uint32_t)R->body.size); + else if (R->body.ptr && R->body.size) string_append_bytes(&out, (char*)R->body.ptr, (uint32_t)R->body.size); return out; } @@ -408,9 +467,8 @@ string http_response_builder(const HTTPResponseMsg *R){ string_append_bytes(&out, hdrs.data, hdrs.length); string_free(hdrs); - if (R->body.ptr && R->body.size){ - string_append_bytes(&out, (char*)R->body.ptr, (uint32_t)R->body.size); - } + if (R->headers_common.chunked) http_append_chunked_body(&out, R->body.ptr, (uint32_t)R->body.size); + else if (R->body.ptr && R->body.size) string_append_bytes(&out, (char*)R->body.ptr, (uint32_t)R->body.size); return out; } @@ -434,11 +492,92 @@ sizedptr http_get_payload(sizedptr header){ }; } -string http_get_chunked_payload(sizedptr chunk){ - if (chunk.ptr && chunk.size > 0){ - int sizetrm = strindex((char*)chunk.ptr, "\r\n"); - uint64_t chunk_size = parse_hex_u64((char*)chunk.ptr, sizetrm); - return string_from_literal_length((char*)(chunk.ptr + sizetrm + 2), (uint32_t)chunk_size); +HTTPParseResult http_decode_chunked_body(const char *buf, uint32_t len, const HTTPPolicy *policy, string *out_body, uint32_t *out_used) { + HTTPPolicy p = policy ? *policy : http_default_policy(); + if (!buf || !out_body || !out_used) return HTTP_PARSE_BAD_FORMAT; + + string out = string_repeat('\0', 0); + uint32_t off = 0; + uint32_t total = 0; + + while (1) { + uint32_t line_end = off; + while (line_end + 1 < len && !(buf[line_end] == '\r' && buf[line_end+1] == '\n')) line_end++; + if (line_end + 1 >= len) { + string_free(out); + return HTTP_PARSE_INCOMPLETE; + } + + uint32_t scan = off; + while (scan < line_end && is_whitespace(buf[scan])) scan++; + if (scan >= line_end) { + string_free(out); + return HTTP_PARSE_BAD_FORMAT; + } + + uint64_t chunk_len = 0; + bool saw_digit = false; + while (scan < line_end) { + char c = buf[scan]; + if (c == ';') break; + if (is_whitespace(c)) { + while (scan < line_end && is_whitespace(buf[scan])) scan++; + if (scan < line_end && buf[scan] != ';') { + string_free(out); + return HTTP_PARSE_BAD_FORMAT; + } + break; + } + + int digit = hex_val(c); + if (digit < 0) { + string_free(out); + return HTTP_PARSE_BAD_FORMAT; + } + saw_digit = true; + chunk_len = chunk_len * 16 + digit; + if (chunk_len > p.max_body_bytes || total + chunk_len > p.max_body_bytes) { + string_free(out); + return HTTP_PARSE_PAYLOAD_TOO_LARGE; + } + scan++; + } + + if (!saw_digit) { + string_free(out); + return HTTP_PARSE_BAD_FORMAT; + } + + off = line_end + 2; + if (chunk_len == 0) { + if (off + 1 < len && buf[off] == '\r' && buf[off+1] == '\n') { + *out_body = out; + *out_used = off + 2; + return HTTP_PARSE_OK; + } + + int trailers_end = find_crlfcrlf(buf + off, len - off); + if (trailers_end >= 0) { + *out_body = out; + *out_used = off + (uint32_t)trailers_end + 4; + return HTTP_PARSE_OK; + } + + string_free(out); + return HTTP_PARSE_INCOMPLETE; + } + + if (off + chunk_len + 2 > len) { + string_free(out); + return HTTP_PARSE_INCOMPLETE; + } + if (buf[off + chunk_len] != '\r' || buf[off+1 + chunk_len] != '\n') { + string_free(out); + return HTTP_PARSE_BAD_FORMAT; + } + + string_append_bytes(&out, buf + off, (uint32_t)chunk_len); + total += (uint32_t)chunk_len; + off += (uint32_t)chunk_len + 2; } - return (string){0}; } diff --git a/kernel/networking/application_layer/http.h b/kernel/networking/application_layer/http.h index 0c173503..f077fa67 100644 --- a/kernel/networking/application_layer/http.h +++ b/kernel/networking/application_layer/http.h @@ -18,6 +18,8 @@ extern "C" { #define HTTP_DEFAULT_HEADER_TOTAL_TIMEOUT_MS 15000 #define HTTP_DEFAULT_BODY_IDLE_TIMEOUT_MS 3000 #define HTTP_DEFAULT_BODY_TOTAL_TIMEOUT_MS 20000 +#define HTTP_DEFAULT_MAX_KEEPALIVE_REQUESTS 16 +#define HTTP_DEFAULT_MAX_REDIRECTS 5 typedef enum { HTTP_METHOD_GET, @@ -42,11 +44,17 @@ typedef enum { HTTP_PARSE_MISSING_HOST, HTTP_PARSE_UNSUPPORTED_VERSION, HTTP_PARSE_PAYLOAD_TOO_LARGE, - HTTP_PARSE_UNSUPPORTED_METHOD + HTTP_PARSE_UNSUPPORTED_METHOD, + HTTP_PARSE_INCOMPLETE } HTTPParseResult; typedef enum { HTTP_OK = 200, + HTTP_MOVED_PERMANENTLY = 301, + HTTP_FOUND = 302, + HTTP_SEE_OTHER = 303, + HTTP_TEMPORARY_REDIRECT = 307, + HTTP_PERMANENT_REDIRECT = 308, HTTP_BAD_REQUEST = 400, HTTP_UNAUTHORIZED = 401, HTTP_FORBIDDEN = 403, @@ -74,12 +82,34 @@ typedef struct { uint32_t header_total_timeout_ms; uint32_t body_idle_timeout_ms; uint32_t body_total_timeout_ms; + uint32_t max_keepalive_requests; + uint32_t max_redirects; bool allow_chunked; bool allow_keep_alive; bool allow_absolute_uri; bool require_host_http11; } HTTPPolicy; +typedef struct { + int32_t max_start_line; + int32_t max_header_bytes; + int32_t max_header_count; + int32_t max_header_key_len; + int32_t max_header_value_len; + int32_t max_path_len; + int32_t max_body_bytes; + int32_t header_idle_timeout_ms; + int32_t header_total_timeout_ms; + int32_t body_idle_timeout_ms; + int32_t body_total_timeout_ms; + int32_t max_keepalive_requests; + int32_t max_redirects; + int32_t allow_chunked; + int32_t allow_keep_alive; + int32_t allow_absolute_uri; + int32_t require_host_http11; +} HTTPPolicyOptions; + typedef struct { HTTPMethod method; HTTPVersion version; @@ -106,6 +136,8 @@ typedef struct { uint8_t has_transfer_encoding; uint8_t chunked; uint8_t bad_transfer; + uint8_t connection_close; + uint8_t connection_keep_alive; string type; string date; string connection; @@ -117,6 +149,7 @@ typedef struct { typedef struct { HTTPMethod method; + HTTPVersion version; string path; HTTPHeadersCommon headers_common; HTTPHeader *extra_headers; @@ -134,8 +167,10 @@ typedef struct { } HTTPResponseMsg; HTTPPolicy http_default_policy(void); +HTTPPolicy http_policy_from_options(const HTTPPolicyOptions *options); const char* http_status_reason(HttpError status); HttpError http_parse_result_status(HTTPParseResult result); +bool http_header_value_has_token(const char *buf, uint32_t len, const char *token, uint32_t token_len); HTTPParseResult http_parse_request_line(const char *buf, uint32_t len, HTTPRequestLine *out); HTTPParseResult http_parse_status_line(const char *buf, uint32_t len, HTTPStatusLine *out); string http_header_builder(const HTTPHeadersCommon *common, @@ -148,6 +183,11 @@ HTTPParseResult http_header_parse_policy(const char *buf, uint32_t len, HTTPHeader **out_extra, uint32_t *out_extra_count); +HTTPParseResult http_decode_chunked_body(const char *buf, uint32_t len, + const HTTPPolicy *policy, + string *out_body, + uint32_t *out_used); + void http_headers_common_free(HTTPHeadersCommon *common); void http_headers_extra_free(HTTPHeader *extra, uint32_t extra_count); @@ -159,8 +199,6 @@ int find_crlfcrlf(const char *data, uint32_t len); sizedptr http_get_payload(sizedptr header); -string http_get_chunked_payload(sizedptr chunk); - #ifdef __cplusplus } #endif diff --git a/kernel/networking/application_layer/socket_http_client.hpp b/kernel/networking/application_layer/socket_http_client.hpp index 8fd4749c..9fe431e2 100644 --- a/kernel/networking/application_layer/socket_http_client.hpp +++ b/kernel/networking/application_layer/socket_http_client.hpp @@ -4,16 +4,17 @@ #include "http.h" #include "std/std.h" #include "net/socket_types.h" -//TODO chunked; handle http bodies without Content-Length + class HTTPClient { private: uint16_t pid; TCPSocket* sock; SocketExtraOptions log_opts; SocketExtraOptions* tcp_extra; + HTTPPolicy policy; public: - explicit HTTPClient(uint16_t pid_, const SocketExtraOptions* extra) : pid(pid_), sock(nullptr), log_opts{}, tcp_extra(nullptr) { + explicit HTTPClient(uint16_t pid_, const SocketExtraOptions* extra, const HTTPPolicyOptions* http_options) : pid(pid_), sock(nullptr), log_opts{}, tcp_extra(nullptr), policy(http_policy_from_options(http_options)) { if (extra) log_opts = *extra; const SocketExtraOptions* tcp_ptr = extra; @@ -32,6 +33,11 @@ class HTTPClient { ~HTTPClient() {close();} + int32_t set_options(const HTTPPolicyOptions* http_options) { + policy = http_policy_from_options(http_options); + return SOCK_OK; + } + int32_t connect(SockDstKind kind, const void* dst, uint16_t port) { uint16_t p = port; int32_t r = sock ? sock->connect(kind, dst, p) : SOCK_ERR_STATE; @@ -63,7 +69,6 @@ class HTTPClient { return resp; } - HTTPPolicy policy = http_default_policy(); string out = http_request_builder(&req); uint32_t out_len = out.length; @@ -194,26 +199,34 @@ class HTTPClient { uint32_t body_start = (uint32_t)hdr_end + 4; uint32_t have = buf.length > body_start ? buf.length - body_start : 0; - uint32_t need = resp.headers_common.has_length ? resp.headers_common.length : 0; - uint32_t body_len = resp.headers_common.has_length ? need : have; - char *body_copy = nullptr; - if (body_len > policy.max_body_bytes) { - if (resp.reason.mem_length) string_free(resp.reason); - resp.reason = string{}; - http_headers_common_free(&resp.headers_common); - http_headers_extra_free(resp.extra_headers, resp.extra_header_count); - resp.extra_headers = nullptr; - resp.extra_header_count = 0; - string_free(buf); - sock->close(); - resp.status_code = (HttpError)SOCK_ERR_PROTO; - return resp; - } + if (resp.headers_common.chunked) { + string chunk_buf = string_repeat('\0', 0); + if (have) string_append_bytes(&chunk_buf, buf.data + body_start, have); + + uint32_t used = 0; + string decoded = string{}; + HTTPParseResult chunk_result = http_decode_chunked_body(chunk_buf.data, chunk_buf.length, &policy, &decoded, &used); + uint32_t body_start_ms = (uint32_t)get_time(); + uint32_t body_last_rx_ms = body_start_ms; + + while (chunk_result == HTTP_PARSE_INCOMPLETE) { + int64_t r = sock->recv(tmp, sizeof(tmp)); + if (r == TCP_WOULDBLOCK) { + uint32_t now = (uint32_t)get_time(); + if ((now - body_last_rx_ms) > policy.body_idle_timeout_ms || (now - body_start_ms) > policy.body_total_timeout_ms) break; + msleep(1); + continue; + } + if (r <= 0) break; + string_append_bytes(&chunk_buf, tmp, (uint32_t)r); + body_last_rx_ms = (uint32_t)get_time(); + chunk_result = http_decode_chunked_body(chunk_buf.data, chunk_buf.length, &policy, &decoded, &used); + } - if (body_len > 0) { - body_copy = (char*)zalloc(body_len); - if (!body_copy) { + string_free(chunk_buf); + if (chunk_result != HTTP_PARSE_OK) { + if (decoded.mem_length) string_free(decoded); if (resp.reason.mem_length) string_free(resp.reason); resp.reason = string{}; http_headers_common_free(&resp.headers_common); @@ -222,31 +235,34 @@ class HTTPClient { resp.extra_header_count = 0; string_free(buf); sock->close(); - resp.status_code = (HttpError)SOCK_ERR_SYS; + resp.status_code = (HttpError)SOCK_ERR_PROTO; + return resp; + } + if (decoded.length) { + resp.body.ptr = (uintptr_t)decoded.data; + resp.body.size = decoded.length; + } else if (decoded.mem_length) string_free(decoded); + } else { + uint32_t need = resp.headers_common.has_length ? resp.headers_common.length : 0; + uint32_t body_len = resp.headers_common.has_length ? need : have; + char *body_copy = nullptr; + + if (body_len > policy.max_body_bytes) { + if (resp.reason.mem_length) string_free(resp.reason); + resp.reason = string{}; + http_headers_common_free(&resp.headers_common); + http_headers_extra_free(resp.extra_headers, resp.extra_header_count); + resp.extra_headers = nullptr; + resp.extra_header_count = 0; + string_free(buf); + sock->close(); + resp.status_code = (HttpError)SOCK_ERR_PROTO; return resp; } - uint32_t copied = have < body_len ? have : body_len; - if (copied) memcpy(body_copy, buf.data + body_start, copied); - - if (resp.headers_common.has_length) { - uint32_t body_start_ms = (uint32_t)get_time(); - uint32_t body_last_rx_ms = body_start_ms; - while (copied < need) { - int64_t r = sock->recv(body_copy + copied, need - copied); - if (r == TCP_WOULDBLOCK) { - uint32_t now = (uint32_t)get_time(); - if ((now - body_last_rx_ms) > policy.body_idle_timeout_ms || (now - body_start_ms) > policy.body_total_timeout_ms) break; - msleep(1); - continue; - } - if (r <= 0) break; - copied += (uint32_t)r; - body_last_rx_ms = (uint32_t)get_time(); - } - - if (copied < need) { - release(body_copy); + if (body_len > 0) { + body_copy = (char*)zalloc(body_len); + if (!body_copy) { if (resp.reason.mem_length) string_free(resp.reason); resp.reason = string{}; http_headers_common_free(&resp.headers_common); @@ -255,13 +271,47 @@ class HTTPClient { resp.extra_header_count = 0; string_free(buf); sock->close(); - resp.status_code = (HttpError)SOCK_ERR_PROTO; + resp.status_code = (HttpError)SOCK_ERR_SYS; return resp; } - } - resp.body.ptr = (uintptr_t)body_copy; - resp.body.size = body_len; + uint32_t copied = have < body_len ? have : body_len; + if (copied) memcpy(body_copy, buf.data + body_start, copied); + + if (resp.headers_common.has_length) { + uint32_t body_start_ms = (uint32_t)get_time(); + uint32_t body_last_rx_ms = body_start_ms; + while (copied < need) { + int64_t r = sock->recv(body_copy + copied, need - copied); + if (r == TCP_WOULDBLOCK) { + uint32_t now = (uint32_t)get_time(); + if ((now - body_last_rx_ms) > policy.body_idle_timeout_ms || (now - body_start_ms) > policy.body_total_timeout_ms) break; + msleep(1); + continue; + } + if (r <= 0) break; + copied += (uint32_t)r; + body_last_rx_ms = (uint32_t)get_time(); + } + + if (copied < need) { + release(body_copy); + if (resp.reason.mem_length) string_free(resp.reason); + resp.reason = string{}; + http_headers_common_free(&resp.headers_common); + http_headers_extra_free(resp.extra_headers, resp.extra_header_count); + resp.extra_headers = nullptr; + resp.extra_header_count = 0; + string_free(buf); + sock->close(); + resp.status_code = (HttpError)SOCK_ERR_PROTO; + return resp; + } + } + + resp.body.ptr = (uintptr_t)body_copy; + resp.body.size = body_len; + } } netlog_socket_event_t ev1{}; diff --git a/kernel/networking/application_layer/socket_http_server.hpp b/kernel/networking/application_layer/socket_http_server.hpp index d2964c5e..1ba3e068 100644 --- a/kernel/networking/application_layer/socket_http_server.hpp +++ b/kernel/networking/application_layer/socket_http_server.hpp @@ -6,6 +6,10 @@ #include "net/socket_types.h" #include "syscalls/syscalls.h" +struct HTTPConnection { + TCPSocket* client; + string carry_buf; +}; class HTTPServer { private: @@ -13,9 +17,10 @@ class HTTPServer { TCPSocket* sock; SocketExtraOptions log_opts; SocketExtraOptions* tcp_extra; + HTTPPolicy policy; public: - explicit HTTPServer(uint16_t pid_, const SocketExtraOptions* extra) : pid(pid_), sock(nullptr), log_opts{}, tcp_extra(nullptr) { + explicit HTTPServer(uint16_t pid_, const SocketExtraOptions* extra, const HTTPPolicyOptions* http_options) : pid(pid_), sock(nullptr), log_opts{}, tcp_extra(nullptr), policy(http_policy_from_options(http_options)) { if (extra) log_opts = *extra; const SocketExtraOptions* tcp_ptr = extra; @@ -34,6 +39,11 @@ class HTTPServer { ~HTTPServer() { close(); } + int32_t set_options(const HTTPPolicyOptions* http_options) { + policy = http_policy_from_options(http_options); + return SOCK_OK; + } + int32_t bind(const SockBindSpec& spec, uint16_t port) { uint16_t p = port; int32_t r = sock ? sock->bind(spec, p) : SOCK_ERR_STATE; @@ -62,8 +72,19 @@ class HTTPServer { return r; } - TCPSocket* accept() { + HTTPConnection* accept() { TCPSocket* c = sock ? sock->accept() : nullptr; + if (!c) return nullptr; + + HTTPConnection* conn = (HTTPConnection*)zalloc(sizeof(HTTPConnection)); + if (!conn) { + delete c; + return nullptr; + } + + conn->client = c; + conn->carry_buf = string{nullptr, 0, 0}; + if (c) { netlog_socket_event_t ev{}; ev.comp = NETLOG_COMP_HTTP_SERVER; @@ -74,17 +95,24 @@ class HTTPServer { ev.remote_ep = c->get_remote_ep(); netlog_socket_event(&log_opts, &ev); } - return c; + return conn; } - HTTPRequestMsg recv_request(TCPSocket* client) { + HTTPRequestMsg recv_request(HTTPConnection* conn) { HTTPRequestMsg req{}; - if (!client) return req; + if (!conn || !conn->client) return req; - HTTPPolicy policy = http_default_policy(); + TCPSocket* client = conn->client; string buf = string_repeat('\0', 0); + if (conn->carry_buf.length) { + string_append_bytes(&buf, conn->carry_buf.data, conn->carry_buf.length); + string_free(conn->carry_buf); + conn->carry_buf = string{nullptr, 0, 0}; + } + char tmp[2048]; - int hdr_end = -1; + int hdr_end = find_crlfcrlf(buf.data, buf.length); + uint32_t consumed = 0; uint32_t start_ms = (uint32_t)get_time(); uint32_t last_rx_ms = start_ms; bool bad_request = false; @@ -142,6 +170,7 @@ class HTTPServer { const char* target = buf.data + line.target_off; uint32_t target_len = line.target_len; req.method = line.method; + req.version = line.version; req.path = string_repeat('\0', 0); if (target_len >= 7 && memcmp(target, "http://", 7) == 0) { @@ -194,46 +223,91 @@ class HTTPServer { uint32_t body_start = hdr_end + 4; uint32_t have = buf.length > body_start ? buf.length - body_start : 0; uint32_t need = req.headers_common.has_length ? req.headers_common.length : 0; - if (!bad_request && need > policy.max_body_bytes) { - bad_request = true; - reject_status = HTTP_PAYLOAD_TOO_LARGE; - } + consumed = body_start; + + if (!bad_request && req.headers_common.chunked) { + string chunk_buf = string_repeat('\0', 0); + if (have) string_append_bytes(&chunk_buf, buf.data + body_start, have); + + uint32_t used = 0; + string decoded = string_repeat('\0', 0); + HTTPParseResult chunk_result = http_decode_chunked_body(chunk_buf.data, chunk_buf.length, &policy, &decoded, &used); + uint32_t body_start_ms = (uint32_t)get_time(); + uint32_t body_last_rx_ms = body_start_ms; + + while (chunk_result == HTTP_PARSE_INCOMPLETE) { + int64_t r = client->recv(tmp, sizeof(tmp)); + if (r == TCP_WOULDBLOCK) { + uint32_t now = (uint32_t)get_time(); + if ((now - body_last_rx_ms) > policy.body_idle_timeout_ms || (now - body_start_ms) > policy.body_total_timeout_ms) break; + msleep(2); + continue; + } + if (r <= 0) break; + string_append_bytes(&chunk_buf, tmp, (uint32_t)r); + body_last_rx_ms = (uint32_t)get_time(); + chunk_result = http_decode_chunked_body(chunk_buf.data, chunk_buf.length, &policy, &decoded, &used); + } - if (!bad_request && need > 0) { - body_copy = (char*)zalloc(need); - if (!body_copy) { + if (chunk_result != HTTP_PARSE_OK) { + if (decoded.mem_length) string_free(decoded); bad_request = true; - reject_status = HTTP_INTERNAL_SERVER_ERROR; + reject_status = http_parse_result_status(chunk_result); } else { - uint32_t copied = have < need ? have : need; - if (copied) memcpy(body_copy, buf.data + body_start, copied); - - uint32_t body_start_ms = (uint32_t)get_time(); - uint32_t body_last_rx_ms = body_start_ms; - while (copied < need) { - int64_t r = client->recv(body_copy + copied, need - copied); - if (r == TCP_WOULDBLOCK) { - uint32_t now = (uint32_t)get_time(); - if ((now - body_last_rx_ms) > policy.body_idle_timeout_ms || (now - body_start_ms) > policy.body_total_timeout_ms) { + consumed = buf.length; + if (used < chunk_buf.length) { + conn->carry_buf = string_repeat('\0', 0); + string_append_bytes(&conn->carry_buf, chunk_buf.data + used, chunk_buf.length - used); + } + if (decoded.length) { + req.body.ptr = (uintptr_t)decoded.data; + req.body.size = decoded.length; + } else if (decoded.mem_length) string_free(decoded); + } + string_free(chunk_buf); + } else { + if (!bad_request && need > policy.max_body_bytes) { + bad_request = true; + reject_status = HTTP_PAYLOAD_TOO_LARGE; + } + + if (!bad_request && need > 0) { + body_copy = (char*)zalloc(need); + if (!body_copy) { + bad_request = true; + reject_status = HTTP_INTERNAL_SERVER_ERROR; + } else { + uint32_t copied = have < need ? have : need; + if (copied) memcpy(body_copy, buf.data + body_start, copied); + + uint32_t body_start_ms = (uint32_t)get_time(); + uint32_t body_last_rx_ms = body_start_ms; + while (copied < need) { + int64_t r = client->recv(body_copy + copied, need - copied); + if (r == TCP_WOULDBLOCK) { + uint32_t now = (uint32_t)get_time(); + if ((now - body_last_rx_ms) > policy.body_idle_timeout_ms || (now - body_start_ms) > policy.body_total_timeout_ms) { + bad_request = true; + break; + } + msleep(2); + continue; + } + if (r <= 0) { bad_request = true; break; } - msleep(2); - continue; - } - if (r <= 0) { - bad_request = true; - break; + copied += (uint32_t)r; + body_last_rx_ms = (uint32_t)get_time(); } - copied += (uint32_t)r; - body_last_rx_ms = (uint32_t)get_time(); } } - } - if (body_copy && !bad_request) { - req.body.ptr = (uintptr_t)body_copy; - req.body.size = need; + if (!bad_request) consumed = body_start + need; + if (body_copy && !bad_request) { + req.body.ptr = (uintptr_t)body_copy; + req.body.size = need; + } } } @@ -249,7 +323,7 @@ class HTTPServer { res.headers_common.connection = string_from_literal("close"); res.body.ptr = (uintptr_t)body.data; res.body.size = body.length; - send_response(client, res); + send_response(conn, res); if (req.path.mem_length) string_free(req.path); http_headers_common_free(&req.headers_common); @@ -282,18 +356,25 @@ class HTTPServer { netlog_socket_event(&log_opts, &ev); + if (consumed < buf.length && !conn->carry_buf.length) { + conn->carry_buf = string_repeat('\0', 0); + string_append_bytes(&conn->carry_buf, buf.data + consumed, buf.length - consumed); + } + string_free(buf); return req; } - int32_t send_response(TCPSocket* client, const HTTPResponseMsg& res) { - if (!client) return SOCK_ERR_STATE; + int32_t send_response(HTTPConnection* conn, const HTTPResponseMsg& res) { + if (!conn || !conn->client) return SOCK_ERR_STATE; + TCPSocket* client = conn->client; + bool send_chunked = res.headers_common.chunked; HTTPResponseMsg head = res; - head.body = sizedptr{}; + if (!send_chunked) head.body = sizedptr{}; uint32_t code = (uint32_t)res.status_code; string out = http_response_builder(&head); - uint32_t body_len = (res.body.ptr && res.body.size) ? (uint32_t)res.body.size : 0; + uint32_t body_len = (!send_chunked && res.body.ptr && res.body.size) ? (uint32_t)res.body.size : 0; uint32_t out_len = out.length + body_len; int64_t sent = 0; uint32_t start_ms = (uint32_t)get_time(); diff --git a/kernel/networking/processes/net_proc.c b/kernel/networking/processes/net_proc.c index f19a35be..7a76ec3e 100644 --- a/kernel/networking/processes/net_proc.c +++ b/kernel/networking/processes/net_proc.c @@ -102,7 +102,7 @@ static void run_http_server() { SocketExtraOptions opt = {0}; opt.debug_level = SOCK_DBG_ALL; opt.flags = SOCK_OPT_DEBUG; - http_server_handle_t srv = http_server_create(pid, &opt); + http_server_handle_t srv = http_server_create(pid, &opt, NULL); if (!srv) { stop_current_process(1); return; @@ -187,7 +187,7 @@ static void test_http(const net_l4_endpoint* ep) { } uint16_t pid = get_current_proc_pid(); - http_client_handle_t cli = http_client_create(pid, NULL); + http_client_handle_t cli = http_client_create(pid, NULL, NULL); if (!cli) { kprintf("[HTTP] http_client_create FAIL"); return; From ff52f9ecca57df3376bd0890e6bb47d35238a806 Mon Sep 17 00:00:00 2001 From: CodeAnarchist Date: Sat, 30 May 2026 01:26:58 +0200 Subject: [PATCH 18/39] [http] improve policy and framing --- .../application_layer/csocket_http_client.cpp | 4 +- .../application_layer/csocket_http_client.h | 4 +- .../application_layer/csocket_http_server.cpp | 4 +- .../application_layer/csocket_http_server.h | 4 +- kernel/networking/application_layer/dns/dns.c | 25 +- .../application_layer/dns/dns_wire.c | 22 +- kernel/networking/application_layer/http.c | 523 +++++++++----- kernel/networking/application_layer/http.h | 130 +++- .../application_layer/socket_http_client.hpp | 660 +++++++++++------- .../application_layer/socket_http_server.hpp | 90 ++- kernel/networking/application_layer/ssdp.c | 3 +- kernel/networking/net_logger/net_logger.c | 20 +- kernel/networking/processes/net_proc.c | 241 +++---- kernel/process/kernel_syscall_impl.c | 4 +- kernel/tools/curl.c | 176 +++++ kernel/tools/curl.h | 12 + kernel/tools/ping.c | 42 +- kernel/tools/tools.c | 2 + kernel/tools/tracert.c | 24 +- 19 files changed, 1257 insertions(+), 733 deletions(-) create mode 100644 kernel/tools/curl.c create mode 100644 kernel/tools/curl.h diff --git a/kernel/networking/application_layer/csocket_http_client.cpp b/kernel/networking/application_layer/csocket_http_client.cpp index efaa7f85..741469e1 100644 --- a/kernel/networking/application_layer/csocket_http_client.cpp +++ b/kernel/networking/application_layer/csocket_http_client.cpp @@ -6,7 +6,7 @@ extern "C" { -http_client_handle_t http_client_create(uint16_t pid, const SocketExtraOptions* extra, const HTTPPolicyOptions *options) { +http_client_handle_t http_client_create(uint16_t pid, const SocketExtraOptions* extra, const HTTPClientPolicyOptions *options) { HTTPClient* cli = new HTTPClient(pid, extra, options); if (!cli) return nullptr; return reinterpret_cast(cli); @@ -18,7 +18,7 @@ void http_client_destroy(http_client_handle_t h) { delete cli; } -int32_t http_client_set_options(http_client_handle_t h, const HTTPPolicyOptions *options) { +int32_t http_client_set_options(http_client_handle_t h, const HTTPClientPolicyOptions *options) { if (!h) return (int32_t)SOCK_ERR_INVAL; HTTPClient* cli = reinterpret_cast(h); return cli->set_options(options); diff --git a/kernel/networking/application_layer/csocket_http_client.h b/kernel/networking/application_layer/csocket_http_client.h index c37ea93f..a3f71c4d 100644 --- a/kernel/networking/application_layer/csocket_http_client.h +++ b/kernel/networking/application_layer/csocket_http_client.h @@ -8,8 +8,8 @@ extern "C" { typedef void* http_client_handle_t; -http_client_handle_t http_client_create(uint16_t pid, const SocketExtraOptions* extra, const HTTPPolicyOptions *options); -int32_t http_client_set_options(http_client_handle_t h, const HTTPPolicyOptions *options); +http_client_handle_t http_client_create(uint16_t pid, const SocketExtraOptions* extra, const HTTPClientPolicyOptions *options); +int32_t http_client_set_options(http_client_handle_t h, const HTTPClientPolicyOptions *options); void http_client_destroy(http_client_handle_t h); int32_t http_client_connect(http_client_handle_t h, uint8_t dst_kind, const void *dst, uint16_t port); diff --git a/kernel/networking/application_layer/csocket_http_server.cpp b/kernel/networking/application_layer/csocket_http_server.cpp index 08d67876..07db8507 100644 --- a/kernel/networking/application_layer/csocket_http_server.cpp +++ b/kernel/networking/application_layer/csocket_http_server.cpp @@ -6,7 +6,7 @@ extern "C" { -http_server_handle_t http_server_create(uint16_t pid, const SocketExtraOptions* extra, const HTTPPolicyOptions *options) { +http_server_handle_t http_server_create(uint16_t pid, const SocketExtraOptions* extra, const HTTPServerPolicyOptions *options) { HTTPServer* srv = new HTTPServer(pid, extra, options); if (!srv) return nullptr; return reinterpret_cast(srv); @@ -18,7 +18,7 @@ void http_server_destroy(http_server_handle_t h) { delete srv; } -int32_t http_server_set_options(http_server_handle_t h, const HTTPPolicyOptions *options) { +int32_t http_server_set_options(http_server_handle_t h, const HTTPServerPolicyOptions *options) { if (!h) return (int32_t)SOCK_ERR_INVAL; HTTPServer* srv = reinterpret_cast(h); return srv->set_options(options); diff --git a/kernel/networking/application_layer/csocket_http_server.h b/kernel/networking/application_layer/csocket_http_server.h index 16e08d31..629a8fb0 100644 --- a/kernel/networking/application_layer/csocket_http_server.h +++ b/kernel/networking/application_layer/csocket_http_server.h @@ -10,8 +10,8 @@ extern "C" { typedef void* http_server_handle_t; typedef void* http_connection_handle_t; -http_server_handle_t http_server_create(uint16_t pid, const SocketExtraOptions* extra, const HTTPPolicyOptions *options); -int32_t http_server_set_options(http_server_handle_t srv, const HTTPPolicyOptions *options); +http_server_handle_t http_server_create(uint16_t pid, const SocketExtraOptions* extra, const HTTPServerPolicyOptions *options); +int32_t http_server_set_options(http_server_handle_t srv, const HTTPServerPolicyOptions *options); void http_server_destroy(http_server_handle_t srv); int32_t http_server_bind(http_server_handle_t srv, const struct SockBindSpec *spec, uint16_t port); diff --git a/kernel/networking/application_layer/dns/dns.c b/kernel/networking/application_layer/dns/dns.c index c874b846..9d6a2c23 100644 --- a/kernel/networking/application_layer/dns/dns.c +++ b/kernel/networking/application_layer/dns/dns.c @@ -45,7 +45,7 @@ static dns_result_t perform_dns_query_once(socket_handle_t sock, const net_l4_en uint8_t response_buffer[512]; net_l4_endpoint source; int64_t received = socket_recvfrom_udp(sock, response_buffer, sizeof(response_buffer), &source); - if (received > 0 && source.port == 53 && source.ver == dst.ver && ((dst.ver == IP_VER4 && memcmp(source.ip, dst.ip, 4) == 0) || (dst.ver == IP_VER6 && memcmp(source.ip, dst.ip, 16) == 0))) { + if (received > 0 && source.port == 53 && source.ver == dst.ver) { uint32_t received_len = received; dns_record_t parsed[DNS_QUERY_RECORDS]; uint32_t parsed_count = 0; @@ -179,17 +179,22 @@ static dns_result_t query_with_selection(const net_l4_endpoint* primary, const n if (which == DNS_USE_PRIMARY && dns_srv_is_zero(primary)) return DNS_ERR_NO_DNS; if (which == DNS_USE_SECONDARY && dns_srv_is_zero(secondary)) return DNS_ERR_NO_DNS; if (which == DNS_USE_BOTH && dns_srv_is_zero(primary) && dns_srv_is_zero(secondary)) return DNS_ERR_NO_DNS; - socket_handle_t sock = dns_socket_handle(); - if (sock == 0) return DNS_ERR_SOCKET; - - if (which == DNS_USE_PRIMARY) return perform_dns_query_once(sock, primary, hostname, qtype, timeout_ms, out_records, max_records, out_count); - if (which == DNS_USE_SECONDARY) return perform_dns_query_once(sock, secondary, hostname, qtype, timeout_ms, out_records, max_records, out_count); + socket_handle_t sock = udp_socket_create(SOCK_ROLE_CLIENT, get_current_proc_pid(), NULL); + if (!sock) return DNS_ERR_SOCKET; - const net_l4_endpoint* first = !dns_srv_is_zero(primary) ? primary : secondary; - const net_l4_endpoint* second = !dns_srv_is_zero(secondary) ? secondary : primary; + dns_result_t res = DNS_ERR_NO_DNS; + if (which == DNS_USE_PRIMARY) res = perform_dns_query_once(sock, primary, hostname, qtype, timeout_ms, out_records, max_records, out_count); + else if (which == DNS_USE_SECONDARY) res = perform_dns_query_once(sock, secondary, hostname, qtype, timeout_ms, out_records, max_records, out_count); + else { + const net_l4_endpoint* first = !dns_srv_is_zero(primary) ? primary : secondary; + const net_l4_endpoint* second = !dns_srv_is_zero(secondary) ? secondary : primary; + + res = perform_dns_query_once(sock, first, hostname, qtype, timeout_ms, out_records, max_records, out_count); + if (res != DNS_OK && second && first != second) res = perform_dns_query_once(sock, second, hostname, qtype, timeout_ms, out_records, max_records, out_count); + } - dns_result_t res = perform_dns_query_once(sock, first, hostname, qtype, timeout_ms, out_records, max_records, out_count); - if (res != DNS_OK && second && first != second) res = perform_dns_query_once(sock, second, hostname, qtype, timeout_ms, out_records, max_records, out_count); + socket_close_udp(sock); + socket_destroy_udp(sock); return res; } diff --git a/kernel/networking/application_layer/dns/dns_wire.c b/kernel/networking/application_layer/dns/dns_wire.c index 43365130..76407db8 100644 --- a/kernel/networking/application_layer/dns/dns_wire.c +++ b/kernel/networking/application_layer/dns/dns_wire.c @@ -107,8 +107,9 @@ bool dns_wire_write_name(uint8_t *out, uint32_t cap, uint32_t *off, const char * bool dns_wire_read_name(const uint8_t *msg, uint32_t msg_len, uint32_t off, char *out, uint32_t out_cap, uint32_t *out_next) { if (!msg) return false; - if (!out) return false; - if (!out_cap) return false; + bool skip = !out && !out_cap; + if (!skip && !out) return false; + if (!skip && !out_cap) return false; if (off >= msg_len) return false; uint32_t idx = off; @@ -139,6 +140,7 @@ bool dns_wire_read_name(const uint8_t *msg, uint32_t msg_len, uint32_t off, char idx++; if (!c) { if (!jumped && out_next) *out_next = idx; + if (skip) return true; if (!out_idx) { if (out_cap < 2) return false; out[0] = '.'; @@ -154,14 +156,16 @@ bool dns_wire_read_name(const uint8_t *msg, uint32_t msg_len, uint32_t off, char if (label_len > 63) return false; if (idx + label_len > msg_len) return false; - if (out_idx) { - if (out_idx + 1 >= out_cap) return false; - out[out_idx++] = '.'; - } + if (!skip) { + if (out_idx) { + if (out_idx + 1 >= out_cap) return false; + out[out_idx++] = '.'; + } - if (out_idx + label_len >= out_cap) return false; - memcpy(out + out_idx, msg + idx, label_len); - out_idx += label_len; + if (out_idx + label_len >= out_cap) return false; + memcpy(out + out_idx, msg + idx, label_len); + out_idx += label_len; + } idx += label_len; } } diff --git a/kernel/networking/application_layer/http.c b/kernel/networking/application_layer/http.c index 920fedba..fb9ce669 100644 --- a/kernel/networking/application_layer/http.c +++ b/kernel/networking/application_layer/http.c @@ -15,12 +15,7 @@ HTTPPolicy http_default_policy(void) { .header_total_timeout_ms = HTTP_DEFAULT_HEADER_TOTAL_TIMEOUT_MS, .body_idle_timeout_ms = HTTP_DEFAULT_BODY_IDLE_TIMEOUT_MS, .body_total_timeout_ms = HTTP_DEFAULT_BODY_TOTAL_TIMEOUT_MS, - .max_keepalive_requests = HTTP_DEFAULT_MAX_KEEPALIVE_REQUESTS, - .max_redirects = HTTP_DEFAULT_MAX_REDIRECTS, .allow_chunked = true, - .allow_keep_alive = false, - .allow_absolute_uri = true, - .require_host_http11 = true }; return p; } @@ -40,19 +35,64 @@ HTTPPolicy http_policy_from_options(const HTTPPolicyOptions *options) { if (options->header_total_timeout_ms > 0) p.header_total_timeout_ms = options->header_total_timeout_ms; if (options->body_idle_timeout_ms > 0) p.body_idle_timeout_ms = options->body_idle_timeout_ms; if (options->body_total_timeout_ms > 0) p.body_total_timeout_ms = options->body_total_timeout_ms; - if (options->max_keepalive_requests > 0) p.max_keepalive_requests = options->max_keepalive_requests; - if (options->max_redirects > 0) p.max_redirects = options->max_redirects; - p.allow_chunked = options->allow_chunked != 0; + return p; +} + +HTTPServerPolicy http_server_policy_from_options(const HTTPServerPolicyOptions *options) { + HTTPServerPolicy p = { + .common = http_default_policy(), + .max_keepalive_requests = HTTP_DEFAULT_MAX_KEEPALIVE_REQUESTS, + .allow_keep_alive = false, + .allow_absolute_uri = true, + .require_host_http11 = true + }; + + if (!options) return p; + + p.common = http_policy_from_options(&options->common); + if (options->max_keepalive_requests > 0) p.max_keepalive_requests = options->max_keepalive_requests; p.allow_keep_alive = options->allow_keep_alive != 0; p.allow_absolute_uri = options->allow_absolute_uri != 0; p.require_host_http11 = options->require_host_http11 != 0; return p; } +HTTPClientPolicy http_client_policy_from_options(const HTTPClientPolicyOptions *options) { + HTTPClientPolicy p = { + .common = http_default_policy(), + .max_redirects = HTTP_DEFAULT_MAX_REDIRECTS, + .follow_redirects = false, + .allow_close_delimited = true + }; + + if (!options) return p; + + p.common = http_policy_from_options(&options->common); + if (options->max_redirects > 0) p.max_redirects = options->max_redirects; + p.follow_redirects = options->follow_redirects != 0; + if (options->allow_close_delimited > 0) p.allow_close_delimited = true; + else if (options->allow_close_delimited < 0) p.allow_close_delimited = false; + return p; +} + +const char* http_method_name(HTTPMethod method) { + switch (method) { + case HTTP_METHOD_GET: return "GET"; + case HTTP_METHOD_POST: return "POST"; + case HTTP_METHOD_PUT: return "PUT"; + case HTTP_METHOD_DELETE: return "DELETE"; + case HTTP_METHOD_HEAD: return "HEAD"; + case HTTP_METHOD_OPTIONS: return "OPTIONS"; + default: return "GET"; + } +} + const char* http_status_reason(HttpError status) { switch (status) { + case HTTP_CONTINUE: return "Continue"; case HTTP_OK: return "OK"; + case HTTP_PARTIAL_CONTENT: return "Partial Content"; case HTTP_MOVED_PERMANENTLY: return "Moved Permanently"; case HTTP_FOUND: return "Found"; case HTTP_SEE_OTHER: return "See Other"; @@ -64,6 +104,7 @@ const char* http_status_reason(HttpError status) { case HTTP_NOT_FOUND: return "Not Found"; case HTTP_PAYLOAD_TOO_LARGE: return "Payload Too Large"; case HTTP_URI_TOO_LONG: return "URI Too Long"; + case HTTP_RANGE_NOT_SATISFIABLE: return "Range Not Satisfiable"; case HTTP_EXPECTATION_FAILED: return "Expectation Failed"; case HTTP_HEADER_FIELDS_TOO_LARGE: return "Request Header Fields Too Large"; case HTTP_INTERNAL_SERVER_ERROR: return "Internal Server Error"; @@ -127,6 +168,8 @@ HTTPParseResult http_parse_request_line(const char *buf, uint32_t len, HTTPReque else if (mlen == 4 && memcmp(buf, "POST", 4) == 0) out->method = HTTP_METHOD_POST; else if (mlen == 3 && memcmp(buf, "PUT", 3) == 0) out->method = HTTP_METHOD_PUT; else if (mlen == 6 && memcmp(buf, "DELETE", 6) == 0) out->method = HTTP_METHOD_DELETE; + else if (mlen == 4 && memcmp(buf, "HEAD", 4) == 0) out->method = HTTP_METHOD_HEAD; + else if (mlen == 7 && memcmp(buf, "OPTIONS", 7) == 0) out->method = HTTP_METHOD_OPTIONS; else return HTTP_PARSE_UNSUPPORTED_METHOD; i++; @@ -173,64 +216,58 @@ HTTPParseResult http_parse_status_line(const char *buf, uint32_t len, HTTPStatus return HTTP_PARSE_OK; } -string http_header_builder(const HTTPHeadersCommon *C, const HTTPHeader *H, uint32_t N){ +static void http_append_field(string *out, const char *key, uint32_t key_len, const char *val, uint32_t val_len) { + if (!out || !key || !key_len || !val) return; + string_append_bytes(out, key, key_len); + string_append_bytes(out, ": ", 2); + string_append_bytes(out, val, val_len); + string_append_bytes(out, "\r\n", 2); +} + +string http_header_builder(const HTTPHeadersCommon *C, const HTTPHeader *H, uint32_t N, HTTPHeaderBuildKind kind, HTTPMethod method, uint32_t status_code){ string out = string_repeat('\0', 0); + bool request = kind == HTTP_HEADER_BUILD_REQUEST; + bool response = kind == HTTP_HEADER_BUILD_RESPONSE; + bool informational = response && status_code >= 100 && status_code < 200; - if (C->type.length){ - string_append_bytes(&out, "Content-Type: ", 14); - string_append_bytes(&out, C->type.data, C->type.length); + if (!C){ + for (uint32_t i = 0; H && i < N; i++) http_append_field(&out, H[i].key.data, H[i].key.length, H[i].value.data, H[i].value.length); string_append_bytes(&out, "\r\n", 2); + return out; } - if (C->chunked) string_append_bytes(&out, "Transfer-Encoding: chunked\r\n", 28); - else { - string tmp = string_format("Content-Length: %i\r\n", (int)C->length); - string_append_bytes(&out, tmp.data, tmp.length); - string_free(tmp); - } - - if (C->date.length){ - string_append_bytes(&out, "Date: ", 6); - string_append_bytes(&out, C->date.data, C->date.length); - string_append_bytes(&out, "\r\n", 2); + if (C->fields.content_type.length && !informational) http_append_field(&out,"Content-Type", 12, C->fields.content_type.data, C->fields.content_type.length); + if (!informational) { + if (C->framing.chunked) string_append_bytes(&out, "Transfer-Encoding: chunked\r\n", 28); + else if (C->framing.has_content_length || C->fields.content_length || response || method == HTTP_METHOD_POST || method == HTTP_METHOD_PUT) { + string tmp = string_format("Content-Length: %i\r\n", (int)C->fields.content_length); + string_append_bytes(&out, tmp.data, tmp.length); + string_free(tmp); + } } - if (C->host.length){ + if (response && C->fields.location.length) http_append_field(&out, "Location", 8, C->fields.location.data, C->fields.location.length); + if (request && C->fields.range.length) http_append_field(&out, "Range", 5, C->fields.range.data, C->fields.range.length); + if (response && C->fields.content_range.length) http_append_field(&out, "Content-Range", 13, C->fields.content_range.data, C->fields.content_range.length); + if (request && C->fields.expect.length) http_append_field(&out, "Expect", 6, C->fields.expect.data, C->fields.expect.length); + + if (request && C->fields.host.length){ string_append_bytes(&out, "Host: ", 6); - bool has_colon = str_has_char(C->host.data, C->host.length, ':') >= 0; - bool has_lb = str_has_char(C->host.data, C->host.length, '[') >= 0; - bool has_rb = str_has_char(C->host.data, C->host.length, ']') >= 0; + bool has_colon = str_has_char(C->fields.host.data, C->fields.host.length, ':') >= 0; + bool has_lb = str_has_char(C->fields.host.data, C->fields.host.length, '[') >= 0; + bool has_rb = str_has_char(C->fields.host.data, C->fields.host.length, ']') >= 0; if (has_colon && !has_lb && !has_rb){ string_append_bytes(&out, "[", 1); - string_append_bytes(&out, C->host.data, C->host.length); + string_append_bytes(&out, C->fields.host.data, C->fields.host.length); string_append_bytes(&out, "]", 1); } else { - string_append_bytes(&out, C->host.data, C->host.length); + string_append_bytes(&out, C->fields.host.data, C->fields.host.length); } string_append_bytes(&out, "\r\n", 2); - } else { - string_append_bytes(&out, "Host: RedactedOS_0.1\r\n", 22); - } - - if (C->connection.length){ - string_append_bytes(&out, "Connection: ", 12); - string_append_bytes(&out, C->connection.data, C->connection.length); - string_append_bytes(&out, "\r\n", 2); } - if (C->keep_alive.length){ - string_append_bytes(&out, "Keep-Alive: ", 12); - string_append_bytes(&out, C->keep_alive.data, C->keep_alive.length); - string_append_bytes(&out, "\r\n", 2); - } - - for (uint32_t i = 0; i < N; i++){ - const HTTPHeader *hdr = &H[i]; - string_append_bytes(&out, hdr->key.data, hdr->key.length); - string_append_bytes(&out, ": ", 2); - string_append_bytes(&out, hdr->value.data, hdr->value.length); - string_append_bytes(&out, "\r\n", 2); - } + if (C->fields.connection.length) http_append_field(&out, "Connection", 10, C->fields.connection.data, C->fields.connection.length); + for (uint32_t i = 0; H && i < N; i++) http_append_field(&out, H[i].key.data, H[i].key.length, H[i].value.data, H[i].value.length); string_append_bytes(&out, "\r\n", 2); return out; @@ -238,13 +275,13 @@ string http_header_builder(const HTTPHeadersCommon *C, const HTTPHeader *H, uint void http_headers_common_free(HTTPHeadersCommon *C){ if (!C) return; - if (C->type.mem_length) string_free(C->type); - if (C->date.mem_length) string_free(C->date); - if (C->connection.mem_length) string_free(C->connection); - if (C->keep_alive.mem_length) string_free(C->keep_alive); - if (C->host.mem_length) string_free(C->host); - if (C->content_type.mem_length) string_free(C->content_type); - if (C->transfer_encoding.mem_length) string_free(C->transfer_encoding); + if (C->fields.content_type.mem_length) string_free(C->fields.content_type); + if (C->fields.connection.mem_length) string_free(C->fields.connection); + if (C->fields.host.mem_length) string_free(C->fields.host); + if (C->fields.expect.mem_length) string_free(C->fields.expect); + if (C->fields.range.mem_length) string_free(C->fields.range); + if (C->fields.location.mem_length) string_free(C->fields.location); + if (C->fields.content_range.mem_length) string_free(C->fields.content_range); *C = (HTTPHeadersCommon){0}; } @@ -257,12 +294,7 @@ void http_headers_extra_free(HTTPHeader *extra, uint32_t extra_count){ release(extra); } -HTTPParseResult http_header_parse_policy(const char *buf, uint32_t len, - const HTTPPolicy *policy, - HTTPHeadersCommon *C, - HTTPHeader **out_extra, - uint32_t *out_extra_count) -{ +HTTPParseResult http_header_parse(const char *buf, uint32_t len, const HTTPPolicy *policy, HTTPHeadersCommon *C, HTTPHeader **out_extra, uint32_t *out_extra_count){ HTTPPolicy p = policy ? *policy : http_default_policy(); if (!buf || !C || !out_extra || !out_extra_count) return HTTP_PARSE_BAD_FORMAT; if (len > p.max_header_bytes) return HTTP_PARSE_TOO_LARGE; @@ -328,6 +360,10 @@ HTTPParseResult http_header_parse_policy(const char *buf, uint32_t len, uint32_t sep = pos; while (sep < eol && buf[sep] != ':') sep++; + if (sep == eol || sep == pos) { + result = HTTP_PARSE_BAD_FORMAT; + break; + } uint32_t key_len = sep - pos; uint32_t val_start = sep + 1; @@ -335,6 +371,7 @@ HTTPParseResult http_header_parse_policy(const char *buf, uint32_t len, uint32_t val_end = eol; while (val_end > val_start && is_whitespace(buf[val_end - 1])) val_end--; uint32_t val_len = val_end - val_start; + bool handled = false; if (key_len == 14 && strncmp_case(buf + pos, "content-length", true, key_len) == 0){ char len_buf[32]; @@ -346,36 +383,119 @@ HTTPParseResult http_header_parse_policy(const char *buf, uint32_t len, ok = parse_uint32_dec(len_buf, &parsed); } - if (!ok || (C->has_length && C->length != parsed)) { - C->bad_length = 1; + if (!ok || (C->framing.has_content_length && C->fields.content_length != parsed)) { + C->bad_content_length = 1; result = HTTP_PARSE_BAD_CONTENT_LENGTH; } else { - C->has_length = 1; - C->length = parsed; + C->framing.has_content_length = 1; + C->fields.content_length = parsed; } - } - else if (key_len == 12 && strncmp_case(buf + pos, "content-type", true, key_len) == 0){ - C->type = string_from_literal_length(buf + val_start, val_len); - C->content_type = string_from_literal_length(buf + val_start, val_len); - } - else if (key_len == 4 && strncmp_case(buf + pos, "date", true, key_len) == 0) C->date = string_from_literal_length(buf + val_start, val_len); - else if (key_len == 10 && strncmp_case(buf + pos, "connection", true, key_len) == 0){ - C->connection = string_from_literal_length(buf + val_start, val_len); - C->connection_close = http_header_value_has_token(buf + val_start, val_len, "close", 5) ? 1 : 0; - C->connection_keep_alive = http_header_value_has_token(buf + val_start, val_len, "keep-alive", 10) ? 1 : 0; - } - else if (key_len == 10 && strncmp_case(buf + pos, "keep-alive", true, key_len) == 0) C->keep_alive = string_from_literal_length(buf + val_start, val_len); - else if (key_len == 4 && strncmp_case(buf + pos, "host", true, key_len) == 0) C->host = string_from_literal_length(buf + val_start, val_len); - else if (key_len == 17 && strncmp_case(buf + pos, "transfer-encoding", true, key_len) == 0){ - C->has_transfer_encoding = 1; - C->transfer_encoding = string_from_literal_length(buf + val_start, val_len); - C->chunked = http_header_value_has_token(buf + val_start, val_len, "chunked", 7) ? 1 : 0; - if (C->chunked && !p.allow_chunked) { - C->bad_transfer = 1; - result = HTTP_PARSE_UNSUPPORTED_TRANSFER; + handled = true; + } else if (key_len == 12 && strncmp_case(buf + pos, "content-type", true, key_len) == 0){ + C->fields.content_type = string_from_literal_length(buf + val_start, val_len); + handled = true; + } else if (key_len == 10 && strncmp_case(buf + pos, "connection", true, key_len) == 0){ + C->fields.connection = string_from_literal_length(buf + val_start, val_len); + C->framing.connection_close = http_header_value_has_token(buf + val_start, val_len, "close", 5) ? 1 : 0; + C->framing.connection_keep_alive = http_header_value_has_token(buf + val_start, val_len, "keep-alive", 10) ? 1 : 0; + handled = true; + } else if (key_len == 4 && strncmp_case(buf + pos, "host", true, key_len) == 0) { + C->fields.host = string_from_literal_length(buf + val_start, val_len); + handled = true; + } else if (key_len == 6 && strncmp_case(buf + pos, "expect", true, key_len) == 0){ + C->fields.expect = string_from_literal_length(buf + val_start, val_len); + C->framing.expect_continue = http_header_value_has_token(buf + val_start, val_len, "100-continue", 12) ? 1 : 0; + handled = true; + } else if (key_len == 5 && strncmp_case(buf + pos, "range", true, key_len) == 0){ + C->fields.range = string_from_literal_length(buf + val_start, val_len); + C->range.invalid = 1; + if (val_len > 6 && strncmp_case(buf + val_start, "bytes=", true, 6) == 0) { + uint32_t rp = val_start + 6; + uint32_t rend = val_start + val_len; + uint64_t start = 0; + uint64_t end = 0; + bool has_start = false; + bool has_end = false; + bool overflow = false; + while (rp < rend && is_digit(buf[rp])) { + has_start = true; + uint64_t next = start * 10 + (uint64_t)(buf[rp] - '0'); + if (next < start) overflow = true; + start = next; + rp++; + } + if (rp < rend && buf[rp] == '-') { + rp++; + while (rp < rend && is_digit(buf[rp])) { + has_end = true; + uint64_t next = end * 10 + (uint64_t)(buf[rp] - '0'); + if (next < end) overflow = true; + end = next; + rp++; + } + if (rp == rend && (has_start || has_end) && !(has_start && has_end && end < start) && !overflow) { + C->range.has = 1; + C->range.invalid = 0; + C->range.has_start = has_start ? 1 : 0; + C->range.has_end = has_end ? 1 : 0; + C->range.start = start; + C->range.end = end; + } + } } + handled = true; + } else if (key_len == 8 && strncmp_case(buf + pos, "location", true, key_len) == 0) { + C->fields.location = string_from_literal_length(buf + val_start, val_len); + handled = true; + } else if (key_len == 13 && strncmp_case(buf + pos, "content-range", true, key_len) == 0) { + C->fields.content_range = string_from_literal_length(buf + val_start, val_len); + handled = true; + } else if (key_len == 17 && strncmp_case(buf + pos, "transfer-encoding", true, key_len) == 0){ + uint32_t te_pos = val_start; + uint32_t te_end = val_start + val_len; + uint32_t count = 0; + bool chunked = false; + + while (te_pos < te_end) { + while (te_pos < te_end && is_whitespace(buf[te_pos])) te_pos++; + if (te_pos >= te_end) break; + + uint32_t token_start = te_pos; + while (te_pos < te_end && buf[te_pos] != ',' && !is_whitespace(buf[te_pos]) && buf[te_pos] != ';') te_pos++; + uint32_t token_len = te_pos - token_start; + if (!token_len) { + result = HTTP_PARSE_BAD_FORMAT; + break; + } + + while (te_pos < te_end && is_whitespace(buf[te_pos])) te_pos++; + if (te_pos < te_end && buf[te_pos] == ';') { + result = HTTP_PARSE_UNSUPPORTED_TRANSFER; + break; + } + if (token_len != 7 || strncmp_case(buf + token_start, "chunked", true, 7) != 0) { + result = HTTP_PARSE_UNSUPPORTED_TRANSFER; + break; + } + + count++; + chunked = true; + if (te_pos < te_end) { + if (buf[te_pos] != ',') { + result = HTTP_PARSE_BAD_FORMAT; + break; + } + te_pos++; + } + } + + if (result == HTTP_PARSE_OK && count != 1) result = HTTP_PARSE_UNSUPPORTED_TRANSFER; + else if (result == HTTP_PARSE_OK && chunked && !p.allow_chunked) result = HTTP_PARSE_UNSUPPORTED_TRANSFER; + else if (result == HTTP_PARSE_OK) C->framing.chunked = chunked ? 1 : 0; + handled = true; } - else { + + if (!handled) { string key = string_from_literal_length((char*)(buf + pos), key_len); string value = string_from_literal_length((char*)(buf + val_start), val_len); @@ -391,6 +511,15 @@ HTTPParseResult http_header_parse_policy(const char *buf, uint32_t len, pos = eol + 2; } + if (result == HTTP_PARSE_OK && C->framing.chunked && C->framing.has_content_length) result = HTTP_PARSE_BAD_FORMAT; + + if (result != HTTP_PARSE_OK) { + http_headers_extra_free(extras, extra_i); + *out_extra = NULL; + *out_extra_count = 0; + return result; + } + if (!extras || extra_i == 0){ if (extras) release(extras); *out_extra = NULL; @@ -413,11 +542,7 @@ HTTPParseResult http_header_parse_policy(const char *buf, uint32_t len, return result; } - for (uint32_t i = 0; i < extra_i; i++){ - if (extras[i].key.mem_length) string_free(extras[i].key); - if (extras[i].value.mem_length) string_free(extras[i].value); - } - release(extras); + http_headers_extra_free(extras, extra_i); *out_extra = NULL; *out_extra_count = 0; return HTTP_PARSE_TOO_LARGE; @@ -438,23 +563,38 @@ static void http_append_chunked_body(string *out, uintptr_t ptr, uint32_t len) { } string http_request_builder(const HTTPRequestMsg *R){ - static const char *Mnames[] = { "GET", "POST", "PUT", "DELETE" }; - string out = string_format("%s ", Mnames[R->method]); + HTTPHeadersCommon common = R->headers_common; + if (R->host_override) common.fields.host = R->host_override[0] ? string_from_const(R->host_override) : (string){0}; + if (!common.framing.chunked && (R->body.size || R->method == HTTP_METHOD_POST || R->method == HTTP_METHOD_PUT)) { + common.fields.content_length = (uint32_t)R->body.size; + common.framing.has_content_length = 1; + } + + HTTPVersion version = R->version == HTTP_VERSION_10 ? HTTP_VERSION_10 : HTTP_VERSION_11; + string out = string_format("%s ", http_method_name(R->method)); string_append_bytes(&out, R->path.data, R->path.length); - string_append_bytes(&out, " HTTP/1.1\r\n", 11); + if (version == HTTP_VERSION_10) string_append_bytes(&out, " HTTP/1.0\r\n", 11); + else string_append_bytes(&out, " HTTP/1.1\r\n", 11); - string hdrs = http_header_builder(&R->headers_common, R->extra_headers, R->extra_header_count); + string hdrs = http_header_builder(&common, R->extra_headers, R->extra_header_count, HTTP_HEADER_BUILD_REQUEST, R->method, 0); string_append_bytes(&out, hdrs.data, hdrs.length); string_free(hdrs); - if (R->headers_common.chunked) http_append_chunked_body(&out, R->body.ptr, (uint32_t)R->body.size); + if (common.framing.chunked) http_append_chunked_body(&out, R->body.ptr, (uint32_t)R->body.size); else if (R->body.ptr && R->body.size) string_append_bytes(&out, (char*)R->body.ptr, (uint32_t)R->body.size); return out; } string http_response_builder(const HTTPResponseMsg *R){ + HTTPHeadersCommon common = R->headers_common; + bool informational = R->status_code >= 100 && R->status_code < 200; + if (!informational && !common.framing.chunked) { + if (!common.framing.has_content_length && !common.fields.content_length) common.fields.content_length = (uint32_t)R->body.size; + common.framing.has_content_length = 1; + } + string out = string_format("HTTP/1.1 %i ", (int)R->status_code); if (R->reason.length) string_append_bytes(&out, R->reason.data, R->reason.length); else { @@ -463,12 +603,14 @@ string http_response_builder(const HTTPResponseMsg *R){ } string_append_bytes(&out, "\r\n", 2); - string hdrs = http_header_builder(&R->headers_common, R->extra_headers, R->extra_header_count); + string hdrs = http_header_builder(&common, R->extra_headers, R->extra_header_count, HTTP_HEADER_BUILD_RESPONSE, HTTP_METHOD_GET, (uint32_t)R->status_code); string_append_bytes(&out, hdrs.data, hdrs.length); string_free(hdrs); - if (R->headers_common.chunked) http_append_chunked_body(&out, R->body.ptr, (uint32_t)R->body.size); - else if (R->body.ptr && R->body.size) string_append_bytes(&out, (char*)R->body.ptr, (uint32_t)R->body.size); + if (!informational) { + if (common.framing.chunked) http_append_chunked_body(&out, R->body.ptr, (uint32_t)R->body.size); + else if (R->body.ptr && R->body.size) string_append_bytes(&out, (char*)R->body.ptr, (uint32_t)R->body.size); + } return out; } @@ -480,104 +622,113 @@ int find_crlfcrlf(const char *data, uint32_t len){ return -1; } -sizedptr http_get_payload(sizedptr header){ - if (!header.ptr || header.size < 4) return (sizedptr){0}; - - int start = find_crlfcrlf((char*)header.ptr, header.size); - if (start < 0) return (sizedptr){0}; - - return (sizedptr){ - header.ptr + (uint32_t)(start + 4), - header.size - (uint32_t)(start + 4) - }; +void http_chunked_decoder_init(HTTPChunkedDecoder *dec, const HTTPPolicy *policy){ + if (!dec) return; + *dec = (HTTPChunkedDecoder){0}; + dec->policy = policy ? *policy : http_default_policy(); + dec->stage = HTTP_CHUNK_STAGE_SIZE; + dec->line = string_repeat('\0', 0); + dec->body = string_repeat('\0', 0); + dec->trailers_buf = string_repeat('\0', 0); } -HTTPParseResult http_decode_chunked_body(const char *buf, uint32_t len, const HTTPPolicy *policy, string *out_body, uint32_t *out_used) { - HTTPPolicy p = policy ? *policy : http_default_policy(); - if (!buf || !out_body || !out_used) return HTTP_PARSE_BAD_FORMAT; - - string out = string_repeat('\0', 0); - uint32_t off = 0; - uint32_t total = 0; - - while (1) { - uint32_t line_end = off; - while (line_end + 1 < len && !(buf[line_end] == '\r' && buf[line_end+1] == '\n')) line_end++; - if (line_end + 1 >= len) { - string_free(out); - return HTTP_PARSE_INCOMPLETE; - } - - uint32_t scan = off; - while (scan < line_end && is_whitespace(buf[scan])) scan++; - if (scan >= line_end) { - string_free(out); - return HTTP_PARSE_BAD_FORMAT; - } +HTTPParseResult http_chunked_decoder_feed(HTTPChunkedDecoder *dec, const char *buf, uint32_t len, uint32_t *out_used) { + if (out_used) *out_used = 0; + if (!dec || !buf) return HTTP_PARSE_BAD_FORMAT; + if (dec->stage == HTTP_CHUNK_STAGE_DONE) return HTTP_PARSE_OK; - uint64_t chunk_len = 0; - bool saw_digit = false; - while (scan < line_end) { - char c = buf[scan]; - if (c == ';') break; - if (is_whitespace(c)) { - while (scan < line_end && is_whitespace(buf[scan])) scan++; - if (scan < line_end && buf[scan] != ';') { - string_free(out); - return HTTP_PARSE_BAD_FORMAT; + uint32_t i = 0; + while (i < len) { + if (dec->stage == HTTP_CHUNK_STAGE_SIZE) { + string_append_bytes(&dec->line, buf+i, 1); + i++; + if (dec->line.length > dec->policy.max_header_value_len) return HTTP_PARSE_TOO_LARGE; + if (dec->line.length >= 2 && dec->line.data[dec->line.length - 2] == '\r' && dec->line.data[dec->line.length - 1] == '\n') { + uint64_t chunk_len = 0; + HTTPParseResult r = HTTP_PARSE_OK; + uint32_t line_end = dec->line.length - 2; + uint32_t scan = 0; + bool saw_digit = false; + while (scan < line_end && is_whitespace(dec->line.data[scan])) scan++; + if (scan >= line_end) r = HTTP_PARSE_BAD_FORMAT; + while (r == HTTP_PARSE_OK && scan < line_end) { + char c = dec->line.data[scan]; + if (c == ';') break; + if (is_whitespace(c)) { + while (scan < line_end && is_whitespace(dec->line.data[scan])) scan++; + if (scan < line_end && dec->line.data[scan] != ';') r = HTTP_PARSE_BAD_FORMAT; + break; + } + + int digit = hex_val(c); + if (digit < 0) { + r = HTTP_PARSE_BAD_FORMAT; + break; + } + saw_digit = true; + chunk_len = chunk_len * 16 + (uint64_t)digit; + if (chunk_len > dec->policy.max_body_bytes) { + r = HTTP_PARSE_PAYLOAD_TOO_LARGE; + break; + } + scan++; } - break; + string_free(dec->line); + dec->line = string_repeat('\0', 0); + if (!saw_digit && r == HTTP_PARSE_OK) r = HTTP_PARSE_BAD_FORMAT; + if (r != HTTP_PARSE_OK) return r; + if (dec->body_total + chunk_len > dec->policy.max_body_bytes) return HTTP_PARSE_PAYLOAD_TOO_LARGE; + dec->chunk_size = chunk_len; + dec->chunk_read = 0; + dec->crlf_seen = 0; + dec->stage = chunk_len ? HTTP_CHUNK_STAGE_DATA : HTTP_CHUNK_STAGE_TRAILERS; } - - int digit = hex_val(c); - if (digit < 0) { - string_free(out); - return HTTP_PARSE_BAD_FORMAT; + } else if (dec->stage == HTTP_CHUNK_STAGE_DATA) { + uint64_t need64 = dec->chunk_size - dec->chunk_read; + uint32_t avail = len - i; //++ + uint32_t take = need64 < avail ? (uint32_t)need64: avail; + if (take) { + string_append_bytes(&dec->body, buf + i, take); + dec->chunk_read += take; + dec->body_total += take; + i += take; } - saw_digit = true; - chunk_len = chunk_len * 16 + digit; - if (chunk_len > p.max_body_bytes || total + chunk_len > p.max_body_bytes) { - string_free(out); - return HTTP_PARSE_PAYLOAD_TOO_LARGE; + if (dec->chunk_read == dec->chunk_size) dec->stage = HTTP_CHUNK_STAGE_DATA_CRLF; + } else if (dec->stage == HTTP_CHUNK_STAGE_DATA_CRLF) { + char expected = dec->crlf_seen ? '\n' : '\r'; + if (buf[i] != expected) return HTTP_PARSE_BAD_FORMAT; + dec->crlf_seen++; + i++; + if (dec->crlf_seen == 2) { + dec->crlf_seen = 0; + dec->stage = HTTP_CHUNK_STAGE_SIZE; } - scan++; - } - - if (!saw_digit) { - string_free(out); - return HTTP_PARSE_BAD_FORMAT; - } - - off = line_end + 2; - if (chunk_len == 0) { - if (off + 1 < len && buf[off] == '\r' && buf[off+1] == '\n') { - *out_body = out; - *out_used = off + 2; + } else if (dec->stage == HTTP_CHUNK_STAGE_TRAILERS) { + string_append_bytes(&dec->trailers_buf, buf + i, 1); + i++; + if (dec->trailers_buf.length > dec->policy.max_header_bytes) return HTTP_PARSE_TOO_LARGE; + if (dec->trailers_buf.length == 2 && dec->trailers_buf.data[0] == '\r' && dec->trailers_buf.data[1] == '\n') { + dec->stage = HTTP_CHUNK_STAGE_DONE; + if (out_used) *out_used = i; return HTTP_PARSE_OK; } - int trailers_end = find_crlfcrlf(buf + off, len - off); - if (trailers_end >= 0) { - *out_body = out; - *out_used = off + (uint32_t)trailers_end + 4; + if (find_crlfcrlf(dec->trailers_buf.data, dec->trailers_buf.length) >= 0) { + dec->stage = HTTP_CHUNK_STAGE_DONE; + if (out_used) *out_used = i; return HTTP_PARSE_OK; } - string_free(out); - return HTTP_PARSE_INCOMPLETE; } - - if (off + chunk_len + 2 > len) { - string_free(out); - return HTTP_PARSE_INCOMPLETE; - } - if (buf[off + chunk_len] != '\r' || buf[off+1 + chunk_len] != '\n') { - string_free(out); - return HTTP_PARSE_BAD_FORMAT; - } - - string_append_bytes(&out, buf + off, (uint32_t)chunk_len); - total += (uint32_t)chunk_len; - off += (uint32_t)chunk_len + 2; } + if (out_used) *out_used = i; + return dec->stage == HTTP_CHUNK_STAGE_DONE ? HTTP_PARSE_OK : HTTP_PARSE_INCOMPLETE; +} + +void http_chunked_decoder_free(HTTPChunkedDecoder *dec) { + if (!dec) return; + if (dec->line.mem_length) string_free(dec->line); + if (dec->body.mem_length) string_free(dec->body); + if (dec->trailers_buf.mem_length) string_free(dec->trailers_buf); + *dec = (HTTPChunkedDecoder){0}; } diff --git a/kernel/networking/application_layer/http.h b/kernel/networking/application_layer/http.h index f077fa67..b92d8eda 100644 --- a/kernel/networking/application_layer/http.h +++ b/kernel/networking/application_layer/http.h @@ -25,7 +25,9 @@ typedef enum { HTTP_METHOD_GET, HTTP_METHOD_POST, HTTP_METHOD_PUT, - HTTP_METHOD_DELETE + HTTP_METHOD_DELETE, + HTTP_METHOD_HEAD, + HTTP_METHOD_OPTIONS } HTTPMethod; typedef enum { @@ -49,7 +51,9 @@ typedef enum { } HTTPParseResult; typedef enum { + HTTP_CONTINUE = 100, HTTP_OK = 200, + HTTP_PARTIAL_CONTENT = 206, HTTP_MOVED_PERMANENTLY = 301, HTTP_FOUND = 302, HTTP_SEE_OTHER = 303, @@ -61,15 +65,28 @@ typedef enum { HTTP_NOT_FOUND = 404, HTTP_PAYLOAD_TOO_LARGE = 413, HTTP_URI_TOO_LONG = 414, + HTTP_RANGE_NOT_SATISFIABLE = 416, HTTP_EXPECTATION_FAILED = 417, HTTP_HEADER_FIELDS_TOO_LARGE = 431, HTTP_INTERNAL_SERVER_ERROR = 500, HTTP_NOT_IMPLEMENTED = 501, HTTP_SERVICE_UNAVAILABLE = 503, HTTP_VERSION_NOT_SUPPORTED = 505, - HTTP_DEBUG = 800, } HttpError; +typedef enum { + HTTP_HEADER_BUILD_REQUEST, + HTTP_HEADER_BUILD_RESPONSE +} HTTPHeaderBuildKind; + +typedef enum { + HTTP_CHUNK_STAGE_SIZE, + HTTP_CHUNK_STAGE_DATA, + HTTP_CHUNK_STAGE_DATA_CRLF, + HTTP_CHUNK_STAGE_TRAILERS, + HTTP_CHUNK_STAGE_DONE +} HTTPChunkStage; + typedef struct { uint32_t max_start_line; uint32_t max_header_bytes; @@ -82,12 +99,7 @@ typedef struct { uint32_t header_total_timeout_ms; uint32_t body_idle_timeout_ms; uint32_t body_total_timeout_ms; - uint32_t max_keepalive_requests; - uint32_t max_redirects; bool allow_chunked; - bool allow_keep_alive; - bool allow_absolute_uri; - bool require_host_http11; } HTTPPolicy; typedef struct { @@ -102,13 +114,38 @@ typedef struct { int32_t header_total_timeout_ms; int32_t body_idle_timeout_ms; int32_t body_total_timeout_ms; - int32_t max_keepalive_requests; - int32_t max_redirects; int32_t allow_chunked; +} HTTPPolicyOptions; + +typedef struct { + HTTPPolicy common; + uint32_t max_keepalive_requests; + bool allow_keep_alive; + bool allow_absolute_uri; + bool require_host_http11; +} HTTPServerPolicy; + +typedef struct { + HTTPPolicyOptions common; + int32_t max_keepalive_requests; int32_t allow_keep_alive; int32_t allow_absolute_uri; int32_t require_host_http11; -} HTTPPolicyOptions; +} HTTPServerPolicyOptions; + +typedef struct { + HTTPPolicy common; + uint32_t max_redirects; + bool follow_redirects; + bool allow_close_delimited; +} HTTPClientPolicy; + +typedef struct { + HTTPPolicyOptions common; + int32_t max_redirects; + int32_t follow_redirects; + int32_t allow_close_delimited; +} HTTPClientPolicyOptions; typedef struct { HTTPMethod method; @@ -130,27 +167,45 @@ typedef struct { } HTTPHeader; typedef struct { - uint32_t length; - uint8_t has_length; - uint8_t bad_length; - uint8_t has_transfer_encoding; + string content_type; + string connection; + string host; + string expect; + string range; + string location; + string content_range; + uint32_t content_length; +} HTTPHeaderFields; + +typedef struct { + uint8_t has_content_length; uint8_t chunked; - uint8_t bad_transfer; uint8_t connection_close; uint8_t connection_keep_alive; - string type; - string date; - string connection; - string keep_alive; - string host; - string content_type; - string transfer_encoding; + uint8_t expect_continue; +} HTTPMessageFraming; + +typedef struct { + uint8_t has; + uint8_t invalid; + uint8_t has_start; + uint8_t has_end; + uint64_t start; + uint64_t end; +} HTTPRangeSpec; + +typedef struct { + HTTPHeaderFields fields; + HTTPMessageFraming framing; + HTTPRangeSpec range; + uint8_t bad_content_length; } HTTPHeadersCommon; typedef struct { HTTPMethod method; HTTPVersion version; string path; + const char *host_override; HTTPHeadersCommon headers_common; HTTPHeader *extra_headers; uint32_t extra_header_count; @@ -166,8 +221,23 @@ typedef struct { sizedptr body; } HTTPResponseMsg; +typedef struct { + HTTPPolicy policy; + HTTPChunkStage stage; + uint64_t chunk_size; + uint64_t chunk_read; + uint32_t body_total; + uint8_t crlf_seen; + string line; + string body; + string trailers_buf; +} HTTPChunkedDecoder; + HTTPPolicy http_default_policy(void); HTTPPolicy http_policy_from_options(const HTTPPolicyOptions *options); +HTTPServerPolicy http_server_policy_from_options(const HTTPServerPolicyOptions *options); +HTTPClientPolicy http_client_policy_from_options(const HTTPClientPolicyOptions *options); +const char* http_method_name(HTTPMethod method); const char* http_status_reason(HttpError status); HttpError http_parse_result_status(HTTPParseResult result); bool http_header_value_has_token(const char *buf, uint32_t len, const char *token, uint32_t token_len); @@ -175,18 +245,20 @@ HTTPParseResult http_parse_request_line(const char *buf, uint32_t len, HTTPReque HTTPParseResult http_parse_status_line(const char *buf, uint32_t len, HTTPStatusLine *out); string http_header_builder(const HTTPHeadersCommon *common, const HTTPHeader *extra, - uint32_t extra_count); + uint32_t extra_count, + HTTPHeaderBuildKind kind, + HTTPMethod method, + uint32_t status_code); -HTTPParseResult http_header_parse_policy(const char *buf, uint32_t len, +HTTPParseResult http_header_parse(const char *buf, uint32_t len, const HTTPPolicy *policy, HTTPHeadersCommon *out_common, HTTPHeader **out_extra, uint32_t *out_extra_count); -HTTPParseResult http_decode_chunked_body(const char *buf, uint32_t len, - const HTTPPolicy *policy, - string *out_body, - uint32_t *out_used); +void http_chunked_decoder_init(HTTPChunkedDecoder *dec, const HTTPPolicy *policy); +HTTPParseResult http_chunked_decoder_feed(HTTPChunkedDecoder *dec, const char *buf, uint32_t len, uint32_t *out_used); +void http_chunked_decoder_free(HTTPChunkedDecoder *dec); void http_headers_common_free(HTTPHeadersCommon *common); void http_headers_extra_free(HTTPHeader *extra, uint32_t extra_count); @@ -197,8 +269,6 @@ string http_response_builder(const HTTPResponseMsg *res); int find_crlfcrlf(const char *data, uint32_t len); -sizedptr http_get_payload(sizedptr header); - #ifdef __cplusplus } #endif diff --git a/kernel/networking/application_layer/socket_http_client.hpp b/kernel/networking/application_layer/socket_http_client.hpp index 9fe431e2..419e186d 100644 --- a/kernel/networking/application_layer/socket_http_client.hpp +++ b/kernel/networking/application_layer/socket_http_client.hpp @@ -4,6 +4,16 @@ #include "http.h" #include "std/std.h" #include "net/socket_types.h" +#include "data/format/url.h" +#include "networking/transport_layer/trans_utils.h" + +struct HTTPClientOrigin { + SockDstKind kind; + string domain; + net_l4_endpoint ep; + uint16_t port; + bool valid; +}; class HTTPClient { private: @@ -11,10 +21,264 @@ class HTTPClient { TCPSocket* sock; SocketExtraOptions log_opts; SocketExtraOptions* tcp_extra; - HTTPPolicy policy; + HTTPClientPolicy policy; + HTTPClientOrigin origin; + + void free_response(HTTPResponseMsg *resp) { + if (!resp) return; + if (resp->body.ptr && resp->body.size) release((void*)resp->body.ptr); + if (resp->reason.mem_length) string_free(resp->reason); + http_headers_common_free(&resp->headers_common); + http_headers_extra_free(resp->extra_headers, resp->extra_header_count); + *resp = HTTPResponseMsg{}; + } + + HTTPResponseMsg error_response(int64_t err) { + HTTPResponseMsg resp{}; + resp.status_code = (HttpError)err; + return resp; + } + + HTTPResponseMsg receive_response(HTTPMethod request_method) { + HTTPResponseMsg resp{}; + string buf = string_repeat('\0', 0); + char tmp[512]; + + while (1) { + int hdr_end = find_crlfcrlf(buf.data, buf.length); + uint32_t start_ms = (uint32_t)get_time(); + uint32_t last_rx_ms = start_ms; + + while (hdr_end < 0) { + int64_t r = sock->recv(tmp, sizeof(tmp)); + if (r == TCP_WOULDBLOCK) { + uint32_t now = (uint32_t)get_time(); + if ((now - last_rx_ms) > policy.common.header_idle_timeout_ms || (now - start_ms) > policy.common.header_total_timeout_ms) { + string_free(buf); + return error_response(SOCK_ERR_PROTO); + } + msleep(10); + continue; + } + if (r < 0) { + string_free(buf); + return error_response(r); + } + if (r == 0) { + string_free(buf); + return error_response(SOCK_ERR_PROTO); + } + string_append_bytes(&buf, tmp, (uint32_t)r); + last_rx_ms = (uint32_t)get_time(); + if (buf.length > policy.common.max_header_bytes) { + string_free(buf); + return error_response(SOCK_ERR_PROTO); + } + hdr_end = find_crlfcrlf(buf.data, buf.length); + } + + int status_line_end = strindex((char*)buf.data, "\r\n"); + if (status_line_end <= 0) { + string_free(buf); + return error_response(SOCK_ERR_PROTO); + } + + HTTPStatusLine status_line{}; + if (http_parse_status_line(buf.data, status_line_end, &status_line) != HTTP_PARSE_OK) { + string_free(buf); + return error_response(SOCK_ERR_PROTO); + } + + resp.status_code = (HttpError)status_line.status_code; + if (status_line.reason_len) { + resp.reason = string_repeat('\0', 0); + string_append_bytes(&resp.reason, buf.data + status_line.reason_off, status_line.reason_len); + } + + HTTPParseResult header_result = http_header_parse( + (char*)buf.data + status_line_end + 2, + (uint32_t)hdr_end - (uint32_t)(status_line_end + 2), + &policy.common, + &resp.headers_common, + &resp.extra_headers, + &resp.extra_header_count); + + if (header_result != HTTP_PARSE_OK) { + free_response(&resp); + string_free(buf); + sock->close(); + return error_response(SOCK_ERR_PROTO); + } + + uint32_t body_start = (uint32_t)hdr_end + 4; + uint32_t have = buf.length > body_start ? buf.length - body_start : 0; + uint32_t code = (uint32_t)resp.status_code; + bool no_body = request_method == HTTP_METHOD_HEAD || code == 204 || code == 304; + if (code >= 100 && code < 200) { + HTTPResponseMsg info = resp; + resp = HTTPResponseMsg{}; + if (info.reason.mem_length) string_free(info.reason); + http_headers_common_free(&info.headers_common); + http_headers_extra_free(info.extra_headers, info.extra_header_count); + + string next = string_repeat('\0', 0); + if (have) string_append_bytes(&next, buf.data + body_start, have); + string_free(buf); + buf = next; + continue; + } + + if (no_body) { + } else if (resp.headers_common.framing.chunked) { + HTTPChunkedDecoder dec; + http_chunked_decoder_init(&dec, &policy.common); + uint32_t used = 0; + HTTPParseResult chunk_result = have ? http_chunked_decoder_feed(&dec, buf.data + body_start, have, &used) : HTTP_PARSE_INCOMPLETE; + uint32_t body_start_ms = (uint32_t)get_time(); + uint32_t body_last_rx_ms = body_start_ms; + + while (chunk_result == HTTP_PARSE_INCOMPLETE) { + int64_t r = sock->recv(tmp, sizeof(tmp)); + if (r == TCP_WOULDBLOCK) { + uint32_t now = (uint32_t)get_time(); + if ((now - body_last_rx_ms) > policy.common.body_idle_timeout_ms || (now - body_start_ms) > policy.common.body_total_timeout_ms) break; + msleep(1); + continue; + } + if (r <= 0) break; + chunk_result = http_chunked_decoder_feed(&dec, tmp, (uint32_t)r, &used); + body_last_rx_ms = (uint32_t)get_time(); + //sock->close(); + } + + if (chunk_result != HTTP_PARSE_OK) { + http_chunked_decoder_free(&dec); + free_response(&resp); + string_free(buf); + sock->close(); + return error_response(SOCK_ERR_PROTO); + } + + string decoded = dec.body; + dec.body = string{}; + http_chunked_decoder_free(&dec); + if (decoded.length) { + resp.body.ptr = (uintptr_t)decoded.data; + resp.body.size = decoded.length; + } else if (decoded.mem_length) string_free(decoded); + } else { + uint32_t need = resp.headers_common.framing.has_content_length ? resp.headers_common.fields.content_length : 0; + + if (resp.headers_common.framing.has_content_length) { + if (need > policy.common.max_body_bytes) { + free_response(&resp); + string_free(buf); + sock->close(); + return error_response(SOCK_ERR_PROTO); + } + + if (need) { + char *body_copy = (char*)zalloc(need); + if (!body_copy) { + free_response(&resp); + string_free(buf); + sock->close(); + return error_response(SOCK_ERR_SYS); + } + + uint32_t copied = have < need ? have : need; + if (copied) memcpy(body_copy, buf.data + body_start, copied); + + uint32_t body_start_ms = (uint32_t)get_time(); + uint32_t body_last_rx_ms = body_start_ms; + while (copied < need) { + int64_t r = sock->recv(body_copy + copied, need - copied); + if (r == TCP_WOULDBLOCK) { + uint32_t now = (uint32_t)get_time(); + if ((now - body_last_rx_ms) > policy.common.body_idle_timeout_ms || (now - body_start_ms) > policy.common.body_total_timeout_ms) break; + msleep(1); + continue; + } + if (r <= 0) break; + copied += (uint32_t)r; + body_last_rx_ms = (uint32_t)get_time(); + } + + if (copied < need) { + release(body_copy); + free_response(&resp); + string_free(buf); + sock->close(); + return error_response(SOCK_ERR_PROTO); + } + + resp.body.ptr = (uintptr_t)body_copy; + resp.body.size = need; + } + } else if (policy.allow_close_delimited) { + string body = string_repeat('\0', 0); + if (have) string_append_bytes(&body, buf.data + body_start, have); + + uint32_t body_start_ms = (uint32_t)get_time(); + uint32_t body_last_rx_ms = body_start_ms; + while (body.length <= policy.common.max_body_bytes) { + int64_t r = sock->recv(tmp, sizeof(tmp)); + if (r == TCP_WOULDBLOCK) { + uint32_t now = (uint32_t)get_time(); + if ((now - body_last_rx_ms) > policy.common.body_idle_timeout_ms || (now - body_start_ms) > policy.common.body_total_timeout_ms) { + string_free(body); + free_response(&resp); + string_free(buf); + sock->close(); + return error_response(SOCK_ERR_PROTO); + } + msleep(1); + continue; + } + if (r < 0) { + string_free(body); + free_response(&resp); + string_free(buf); + sock->close(); + return error_response(r); + } + if (r == 0) break; + if (body.length + (uint32_t)r > policy.common.max_body_bytes) { + string_free(body); + free_response(&resp); + string_free(buf); + sock->close(); + return error_response(SOCK_ERR_PROTO); + } + string_append_bytes(&body, tmp, (uint32_t)r); + body_last_rx_ms = (uint32_t)get_time(); + } + + if (body.length) { + resp.body.ptr = (uintptr_t)body.data; + resp.body.size = body.length; + } else if (body.mem_length) string_free(body); + } + } + + netlog_socket_event_t ev{}; + ev.comp = NETLOG_COMP_HTTP_CLIENT; + ev.action = NETLOG_ACT_HTTP_RECV_RESPONSE; + ev.pid = pid; + ev.u0 = (uint32_t)resp.status_code; + ev.u1 = (uint32_t)resp.body.size; + ev.local_port = sock->get_local_port(); + ev.remote_ep = sock->get_remote_ep(); + netlog_socket_event(&log_opts, &ev); + + string_free(buf); + return resp; + } + } + public: - explicit HTTPClient(uint16_t pid_, const SocketExtraOptions* extra, const HTTPPolicyOptions* http_options) : pid(pid_), sock(nullptr), log_opts{}, tcp_extra(nullptr), policy(http_policy_from_options(http_options)) { + explicit HTTPClient(uint16_t pid_, const SocketExtraOptions* extra, const HTTPClientPolicyOptions* http_options) : pid(pid_), sock(nullptr), log_opts{}, tcp_extra(nullptr), policy(http_client_policy_from_options(http_options)), origin{} { if (extra) log_opts = *extra; const SocketExtraOptions* tcp_ptr = extra; @@ -33,14 +297,24 @@ class HTTPClient { ~HTTPClient() {close();} - int32_t set_options(const HTTPPolicyOptions* http_options) { - policy = http_policy_from_options(http_options); + int32_t set_options(const HTTPClientPolicyOptions* http_options) { + policy = http_client_policy_from_options(http_options); return SOCK_OK; } int32_t connect(SockDstKind kind, const void* dst, uint16_t port) { uint16_t p = port; int32_t r = sock ? sock->connect(kind, dst, p) : SOCK_ERR_STATE; + if (r >= 0) { + HTTPClientOrigin next{}; + next.kind = kind; + next.port = p; + next.valid = true; + if (kind == DST_DOMAIN && dst) next.domain = string_from_literal((const char*)dst); + else if (kind == DST_ENDPOINT && dst) next.ep = *(const net_l4_endpoint*)dst; + if (origin.domain.mem_length) string_free(origin.domain); + origin = next; + } netlog_socket_event_t ev{}; ev.comp = NETLOG_COMP_HTTP_CLIENT; @@ -63,268 +337,172 @@ class HTTPClient { } HTTPResponseMsg send_request(const HTTPRequestMsg& req) { + HTTPRequestMsg curr = req; + curr.path = string_repeat('\0', 0); + if (req.path.data && req.path.length) string_append_bytes(&curr.path, req.path.data, req.path.length); + curr.headers_common.fields.host = string_repeat('\0', 0); + if (req.headers_common.fields.host.data && req.headers_common.fields.host.length) string_append_bytes(&curr.headers_common.fields.host, req.headers_common.fields.host.data, req.headers_common.fields.host.length); + HTTPResponseMsg resp{}; - if (!sock) { - resp.status_code = (HttpError)SOCK_ERR_STATE; - return resp; - } - - string out = http_request_builder(&req); - uint32_t out_len = out.length; - - uint32_t off = 0; - int64_t sent = 0; - while (off < out_len) { - int64_t r = sock->send(out.data + off, out_len - off); - if (r == TCP_WOULDBLOCK) { - msleep(5); - continue; - } - if (r < 0) { - sent = r; + uint32_t redirects = policy.follow_redirects ? policy.max_redirects : 0; + for (uint32_t i = 0;; i++) { + if (!sock) { + resp = error_response(SOCK_ERR_STATE); break; } - off += (uint32_t)r; - } - if (sent >= 0) sent = (int64_t)off; - netlog_socket_event_t ev{}; - ev.comp = NETLOG_COMP_HTTP_CLIENT; - ev.action = NETLOG_ACT_HTTP_SEND_REQUEST; - ev.pid = pid; - ev.u0 = out_len; - ev.i0 = sent; - ev.local_port = sock->get_local_port(); - ev.remote_ep = sock->get_remote_ep(); - - char pathbuf[128]; - if (req.path.length && req.path.data) { - uint32_t n = req.path.length; - if (n > sizeof(pathbuf) - 1) n = sizeof(pathbuf) - 1; - memcpy(pathbuf, req.path.data, n); - pathbuf[n] = 0; - ev.s0 = pathbuf; - } - - netlog_socket_event(&log_opts, &ev); - string_free(out); + if (!curr.host_override && curr.version != HTTP_VERSION_10 && !curr.headers_common.fields.host.length && origin.valid) { + if (origin.kind == DST_DOMAIN && origin.domain.data) { + bool ipv6 = str_has_char(origin.domain.data, origin.domain.length, ':') >= 0; + if (origin.port && origin.port != 80) { + if (ipv6) curr.headers_common.fields.host = string_format("[%.*s]:%i", (int)origin.domain.length, origin.domain.data, (int)origin.port); + else curr.headers_common.fields.host = string_format("%.*s:%i", (int)origin.domain.length, origin.domain.data, (int)origin.port); + } else curr.headers_common.fields.host = string_from_literal_length(origin.domain.data, origin.domain.length); + } else if (origin.kind == DST_ENDPOINT && origin.ep.ver) { + char ip[48]; + bool ipv6 = false; + uint16_t ep_port = 0; + net_ep_split(&origin.ep, ip, sizeof(ip), &ipv6, &ep_port); + uint16_t host_port = origin.port ? origin.port : ep_port; + if (host_port && host_port != 80) { + if (ipv6) curr.headers_common.fields.host = string_format("[%s]:%i", ip, (int)host_port); + else curr.headers_common.fields.host = string_format("%s:%i", ip, (int)host_port); + } else { + if (ipv6) curr.headers_common.fields.host = string_format("[%s]", ip); + else curr.headers_common.fields.host = string_from_literal(ip); + } + } + } - if (sent < 0) { - resp.status_code = (HttpError)sent; - return resp; - } + string out = http_request_builder(&curr); + uint32_t out_len = out.length; - string buf = string_repeat('\0', 0); - char tmp[512]; - int hdr_end = -1; - uint32_t start_ms = (uint32_t)get_time(); - uint32_t last_rx_ms = start_ms; - - while (hdr_end < 0) { - int64_t r = sock->recv(tmp, sizeof(tmp)); - if (r == TCP_WOULDBLOCK) { - uint32_t now = (uint32_t)get_time(); - if ((now - last_rx_ms) > policy.header_idle_timeout_ms || (now - start_ms) > policy.header_total_timeout_ms) { - string_free(buf); - resp.status_code = (HttpError)SOCK_ERR_PROTO; - return resp; + uint32_t off = 0; + int64_t sent = 0; + while (off < out_len) { + int64_t r = sock->send(out.data + off, out_len - off); + if (r == TCP_WOULDBLOCK) { + msleep(5); + continue; } - msleep(10); - continue; - } - if (r < 0) { - string_free(buf); - resp.status_code = (HttpError)r; - return resp; - } - if (r == 0) { - string_free(buf); - resp.status_code = (HttpError)SOCK_ERR_PROTO; - return resp; + if (r < 0) { + sent = r; + break; + } + off += (uint32_t)r; } - string_append_bytes(&buf, tmp, (uint32_t)r); - last_rx_ms = (uint32_t)get_time(); - if (buf.length > policy.max_header_bytes) { - string_free(buf); - resp.status_code = (HttpError)SOCK_ERR_PROTO; - return resp; + if (sent >= 0) sent = (int64_t)off; + + netlog_socket_event_t ev{}; + ev.comp = NETLOG_COMP_HTTP_CLIENT; + ev.action = NETLOG_ACT_HTTP_SEND_REQUEST; + ev.pid = pid; + ev.u0 = out_len; + ev.i0 = sent; + ev.local_port = sock->get_local_port(); + ev.remote_ep = sock->get_remote_ep(); + + char pathbuf[128]; + if (curr.path.length && curr.path.data) { + uint32_t n = curr.path.length; + if (n > sizeof(pathbuf) - 1) n = sizeof(pathbuf) - 1; + memcpy(pathbuf, curr.path.data, n); + pathbuf[n] = 0; + ev.s0 = pathbuf; } - hdr_end = find_crlfcrlf(buf.data, buf.length); - } - int status_line_end = strindex((char*)buf.data, "\r\n"); - if (status_line_end <= 0) { - string_free(buf); - resp.status_code = (HttpError)SOCK_ERR_PROTO; - return resp; - } + netlog_socket_event(&log_opts, &ev); + string_free(out); - HTTPStatusLine status_line{}; - if (http_parse_status_line(buf.data, status_line_end, &status_line) != HTTP_PARSE_OK) { - string_free(buf); - resp.status_code = (HttpError)SOCK_ERR_PROTO; - return resp; - } + if (sent < 0) { + resp = error_response(sent); + break; + } - resp.status_code = (HttpError)status_line.status_code; - if (status_line.reason_len) { - resp.reason = string_repeat('\0', 0); - string_append_bytes(&resp.reason, buf.data + status_line.reason_off, status_line.reason_len); - } + resp = receive_response(curr.method); + uint32_t code = (uint32_t)resp.status_code; + bool redirect = code == HTTP_MOVED_PERMANENTLY || code == HTTP_FOUND || code == HTTP_SEE_OTHER || code == HTTP_TEMPORARY_REDIRECT || code == HTTP_PERMANENT_REDIRECT; + if (!redirect || i >= redirects || !resp.headers_common.fields.location.length) break; - HTTPHeader *extras = nullptr; - uint32_t extra_count = 0; - HTTPParseResult header_result = http_header_parse_policy( - (char*)buf.data + status_line_end + 2, - (uint32_t)hdr_end - (uint32_t)(status_line_end + 2), - &policy, - &resp.headers_common, - &extras, - &extra_count); - resp.extra_headers = extras; - resp.extra_header_count = extra_count; - - if (header_result != HTTP_PARSE_OK) { - if (resp.reason.mem_length) string_free(resp.reason); - resp.reason = string{}; - http_headers_common_free(&resp.headers_common); - http_headers_extra_free(resp.extra_headers, resp.extra_header_count); - resp.extra_headers = nullptr; - resp.extra_header_count = 0; - string_free(buf); - sock->close(); - resp.status_code = (HttpError)SOCK_ERR_PROTO; - return resp; - } + ParsedURL url = parse_url(resp.headers_common.fields.location.data, resp.headers_common.fields.location.length); + if (!url.ok) break; - uint32_t body_start = (uint32_t)hdr_end + 4; - uint32_t have = buf.length > body_start ? buf.length - body_start : 0; + bool absolute = url.scheme.size || url.host.size; + uint16_t next_port = origin.port; + if (url.scheme.size) { + if (url.scheme.size == 5 && strncmp_case((const char*)url.scheme.ptr, "https", true, 5) == 0) break; + if (!(url.scheme.size == 4 && strncmp_case((const char*)url.scheme.ptr, "http", true, 4) == 0)) break; + } + if (absolute && (!url.host.ptr || !url.host.size)) break; - if (resp.headers_common.chunked) { - string chunk_buf = string_repeat('\0', 0); - if (have) string_append_bytes(&chunk_buf, buf.data + body_start, have); + string next_path = url_request_path(&url, &curr.path); + if (!next_path.length) break; - uint32_t used = 0; - string decoded = string{}; - HTTPParseResult chunk_result = http_decode_chunked_body(chunk_buf.data, chunk_buf.length, &policy, &decoded, &used); - uint32_t body_start_ms = (uint32_t)get_time(); - uint32_t body_last_rx_ms = body_start_ms; + if (curr.path.mem_length) string_free(curr.path); + curr.path = next_path; - while (chunk_result == HTTP_PARSE_INCOMPLETE) { - int64_t r = sock->recv(tmp, sizeof(tmp)); - if (r == TCP_WOULDBLOCK) { - uint32_t now = (uint32_t)get_time(); - if ((now - body_last_rx_ms) > policy.body_idle_timeout_ms || (now - body_start_ms) > policy.body_total_timeout_ms) break; - msleep(1); - continue; + if (absolute) { + next_port = url.port ? url.port : 80; + string next_domain = string_from_literal_length((const char*)url.host.ptr, url.host.size); + if (!curr.host_override) { + if (curr.headers_common.fields.host.mem_length) string_free(curr.headers_common.fields.host); + curr.headers_common.fields.host = string_repeat('\0', 0); } - if (r <= 0) break; - string_append_bytes(&chunk_buf, tmp, (uint32_t)r); - body_last_rx_ms = (uint32_t)get_time(); - chunk_result = http_decode_chunked_body(chunk_buf.data, chunk_buf.length, &policy, &decoded, &used); - } - - string_free(chunk_buf); - if (chunk_result != HTTP_PARSE_OK) { - if (decoded.mem_length) string_free(decoded); - if (resp.reason.mem_length) string_free(resp.reason); - resp.reason = string{}; - http_headers_common_free(&resp.headers_common); - http_headers_extra_free(resp.extra_headers, resp.extra_header_count); - resp.extra_headers = nullptr; - resp.extra_header_count = 0; - string_free(buf); - sock->close(); - resp.status_code = (HttpError)SOCK_ERR_PROTO; - return resp; - } - if (decoded.length) { - resp.body.ptr = (uintptr_t)decoded.data; - resp.body.size = decoded.length; - } else if (decoded.mem_length) string_free(decoded); - } else { - uint32_t need = resp.headers_common.has_length ? resp.headers_common.length : 0; - uint32_t body_len = resp.headers_common.has_length ? need : have; - char *body_copy = nullptr; - - if (body_len > policy.max_body_bytes) { - if (resp.reason.mem_length) string_free(resp.reason); - resp.reason = string{}; - http_headers_common_free(&resp.headers_common); - http_headers_extra_free(resp.extra_headers, resp.extra_header_count); - resp.extra_headers = nullptr; - resp.extra_header_count = 0; - string_free(buf); - sock->close(); - resp.status_code = (HttpError)SOCK_ERR_PROTO; - return resp; - } - if (body_len > 0) { - body_copy = (char*)zalloc(body_len); - if (!body_copy) { - if (resp.reason.mem_length) string_free(resp.reason); - resp.reason = string{}; - http_headers_common_free(&resp.headers_common); - http_headers_extra_free(resp.extra_headers, resp.extra_header_count); - resp.extra_headers = nullptr; - resp.extra_header_count = 0; - string_free(buf); + if (sock) { sock->close(); - resp.status_code = (HttpError)SOCK_ERR_SYS; - return resp; + sock->~TCPSocket(); + release(sock); + sock = nullptr; } - - uint32_t copied = have < body_len ? have : body_len; - if (copied) memcpy(body_copy, buf.data + body_start, copied); - - if (resp.headers_common.has_length) { - uint32_t body_start_ms = (uint32_t)get_time(); - uint32_t body_last_rx_ms = body_start_ms; - while (copied < need) { - int64_t r = sock->recv(body_copy + copied, need - copied); - if (r == TCP_WOULDBLOCK) { - uint32_t now = (uint32_t)get_time(); - if ((now - body_last_rx_ms) > policy.body_idle_timeout_ms || (now - body_start_ms) > policy.body_total_timeout_ms) break; - msleep(1); - continue; - } - if (r <= 0) break; - copied += (uint32_t)r; - body_last_rx_ms = (uint32_t)get_time(); - } - - if (copied < need) { - release(body_copy); - if (resp.reason.mem_length) string_free(resp.reason); - resp.reason = string{}; - http_headers_common_free(&resp.headers_common); - http_headers_extra_free(resp.extra_headers, resp.extra_header_count); - resp.extra_headers = nullptr; - resp.extra_header_count = 0; - string_free(buf); - sock->close(); - resp.status_code = (HttpError)SOCK_ERR_PROTO; - return resp; - } + sock = (TCPSocket*)zalloc(sizeof(TCPSocket)); + int32_t rr = sock && next_domain.data ? SOCK_OK : SOCK_ERR_SYS; + if (rr >= 0) new (sock) TCPSocket(SOCK_ROLE_CLIENT, pid, tcp_extra ? tcp_extra : &log_opts); + if (rr >= 0) rr = connect(DST_DOMAIN, next_domain.data, next_port); + if (next_domain.mem_length) string_free(next_domain); + if (rr < 0) { + free_response(&resp); + resp.status_code = (HttpError)rr; + break; } + } else { + SockDstKind reconnect_kind = origin.kind; + uint16_t reconnect_port = origin.port; + net_l4_endpoint reconnect_ep = origin.ep; + string reconnect_domain = string{}; + if (origin.kind == DST_DOMAIN && origin.domain.data) reconnect_domain = string_from_literal(origin.domain.data); + + if (sock) { + sock->close(); + sock->~TCPSocket(); + release(sock); + sock = nullptr; + } + sock = (TCPSocket*)zalloc(sizeof(TCPSocket)); + int32_t rr = sock && origin.valid && (reconnect_kind != DST_DOMAIN || reconnect_domain.data) ? SOCK_OK : SOCK_ERR_SYS; + if (rr >= 0) new (sock) TCPSocket(SOCK_ROLE_CLIENT, pid, tcp_extra ? tcp_extra : &log_opts); + if (rr >= 0) rr = reconnect_kind == DST_DOMAIN ? connect(DST_DOMAIN, reconnect_domain.data, reconnect_port) : connect(DST_ENDPOINT, &reconnect_ep, reconnect_port); + if (reconnect_domain.mem_length) string_free(reconnect_domain); + if (rr < 0) { + free_response(&resp); + resp.status_code = (HttpError)rr; + break; + } + } - resp.body.ptr = (uintptr_t)body_copy; - resp.body.size = body_len; + if (code == HTTP_SEE_OTHER && curr.method != HTTP_METHOD_HEAD) { + curr.method = HTTP_METHOD_GET; + curr.body = sizedptr{}; + curr.headers_common.framing.chunked = 0; + curr.headers_common.framing.has_content_length = 0; + curr.headers_common.fields.content_length = 0; } + + free_response(&resp); } - netlog_socket_event_t ev1{}; - ev1.comp = NETLOG_COMP_HTTP_CLIENT; - ev1.action = NETLOG_ACT_HTTP_RECV_RESPONSE; - ev1.pid = pid; - ev1.u0 = (uint32_t)resp.status_code; - ev1.u1 = (uint32_t)resp.body.size; - ev1.local_port = sock->get_local_port(); - ev1.remote_ep = sock->get_remote_ep(); - netlog_socket_event(&log_opts, &ev1); - - string_free(buf); + if (curr.path.mem_length) string_free(curr.path); + if (curr.headers_common.fields.host.mem_length) string_free(curr.headers_common.fields.host); return resp; } @@ -352,6 +530,8 @@ class HTTPClient { if (tcp_extra) release(tcp_extra); tcp_extra = nullptr; + if (origin.domain.mem_length) string_free(origin.domain); + origin = HTTPClientOrigin{}; log_opts.flags &= ~SOCK_OPT_DEBUG; return r; } diff --git a/kernel/networking/application_layer/socket_http_server.hpp b/kernel/networking/application_layer/socket_http_server.hpp index 1ba3e068..a36da143 100644 --- a/kernel/networking/application_layer/socket_http_server.hpp +++ b/kernel/networking/application_layer/socket_http_server.hpp @@ -17,10 +17,10 @@ class HTTPServer { TCPSocket* sock; SocketExtraOptions log_opts; SocketExtraOptions* tcp_extra; - HTTPPolicy policy; + HTTPServerPolicy policy; public: - explicit HTTPServer(uint16_t pid_, const SocketExtraOptions* extra, const HTTPPolicyOptions* http_options) : pid(pid_), sock(nullptr), log_opts{}, tcp_extra(nullptr), policy(http_policy_from_options(http_options)) { + explicit HTTPServer(uint16_t pid_, const SocketExtraOptions* extra, const HTTPServerPolicyOptions* http_options) : pid(pid_), sock(nullptr), log_opts{}, tcp_extra(nullptr), policy(http_server_policy_from_options(http_options)) { if (extra) log_opts = *extra; const SocketExtraOptions* tcp_ptr = extra; @@ -39,8 +39,8 @@ class HTTPServer { ~HTTPServer() { close(); } - int32_t set_options(const HTTPPolicyOptions* http_options) { - policy = http_policy_from_options(http_options); + int32_t set_options(const HTTPServerPolicyOptions* http_options) { + policy = http_server_policy_from_options(http_options); return SOCK_OK; } @@ -123,7 +123,7 @@ class HTTPServer { int64_t r = client->recv(tmp, sizeof(tmp)); if (r == TCP_WOULDBLOCK) { uint32_t now = (uint32_t)get_time(); - if ((uint32_t)(now - last_rx_ms) > policy.header_idle_timeout_ms || (uint32_t)(now - start_ms) > policy.header_total_timeout_ms) { + if ((uint32_t)(now - last_rx_ms) > policy.common.header_idle_timeout_ms || (uint32_t)(now - start_ms) > policy.common.header_total_timeout_ms) { string_free(buf); return req; } @@ -136,7 +136,7 @@ class HTTPServer { } string_append_bytes(&buf, tmp, (uint32_t)r); last_rx_ms = (uint32_t)get_time(); - if (buf.length > policy.max_header_bytes) { + if (buf.length > policy.common.max_header_bytes) { bad_request = true; reject_status = HTTP_HEADER_FIELDS_TOO_LARGE; break; @@ -151,7 +151,7 @@ class HTTPServer { line_end++; } - if (line_end > policy.max_start_line) { + if (line_end > policy.common.max_start_line) { bad_request = true; reject_status = HTTP_URI_TOO_LONG; } @@ -195,17 +195,17 @@ class HTTPServer { } } else string_append_bytes(&req.path, target, target_len); - if (!bad_request && (!req.path.length || req.path.length > policy.max_path_len)) { + if (!bad_request && (!req.path.length || req.path.length > policy.common.max_path_len)) { bad_request = true; - reject_status = req.path.length > policy.max_path_len ? HTTP_URI_TOO_LONG : HTTP_BAD_REQUEST; + reject_status = req.path.length > policy.common.max_path_len ? HTTP_URI_TOO_LONG : HTTP_BAD_REQUEST; } } if (!bad_request) { - HTTPParseResult header_result = http_header_parse_policy( + HTTPParseResult header_result = http_header_parse( (char*)buf.data + line_end + 2, (uint32_t)hdr_end - (line_end + 2), - &policy, + &policy.common, &req.headers_common, &req.extra_headers, &req.extra_header_count @@ -214,7 +214,7 @@ class HTTPServer { if (header_result != HTTP_PARSE_OK) { bad_request = true; reject_status = http_parse_result_status(header_result); - } else if (policy.require_host_http11 && line.version == HTTP_VERSION_11 && !req.headers_common.host.length) { + } else if (policy.require_host_http11 && line.version == HTTP_VERSION_11 && !req.headers_common.fields.host.length) { bad_request = true; reject_status = HTTP_BAD_REQUEST; } @@ -222,51 +222,63 @@ class HTTPServer { uint32_t body_start = hdr_end + 4; uint32_t have = buf.length > body_start ? buf.length - body_start : 0; - uint32_t need = req.headers_common.has_length ? req.headers_common.length : 0; + + if (!bad_request && req.headers_common.framing.expect_continue) { + if (req.headers_common.framing.has_content_length && req.headers_common.fields.content_length > policy.common.max_body_bytes) { + bad_request = true; + reject_status = HTTP_PAYLOAD_TOO_LARGE; + } else { + HTTPResponseMsg cont{}; + cont.status_code = HTTP_CONTINUE; + send_response(conn, cont); + } + } + uint32_t need = req.headers_common.framing.has_content_length ? req.headers_common.fields.content_length : 0; consumed = body_start; - if (!bad_request && req.headers_common.chunked) { - string chunk_buf = string_repeat('\0', 0); - if (have) string_append_bytes(&chunk_buf, buf.data + body_start, have); + if (!bad_request && req.headers_common.framing.chunked) { + HTTPChunkedDecoder dec; + http_chunked_decoder_init(&dec, &policy.common); uint32_t used = 0; - string decoded = string_repeat('\0', 0); - HTTPParseResult chunk_result = http_decode_chunked_body(chunk_buf.data, chunk_buf.length, &policy, &decoded, &used); + HTTPParseResult chunk_result = have ? http_chunked_decoder_feed(&dec, buf.data + body_start, have, &used) : HTTP_PARSE_INCOMPLETE; uint32_t body_start_ms = (uint32_t)get_time(); uint32_t body_last_rx_ms = body_start_ms; + consumed = body_start + used; while (chunk_result == HTTP_PARSE_INCOMPLETE) { int64_t r = client->recv(tmp, sizeof(tmp)); if (r == TCP_WOULDBLOCK) { uint32_t now = (uint32_t)get_time(); - if ((now - body_last_rx_ms) > policy.body_idle_timeout_ms || (now - body_start_ms) > policy.body_total_timeout_ms) break; + if ((now - body_last_rx_ms) > policy.common.body_idle_timeout_ms || (now - body_start_ms) > policy.common.body_total_timeout_ms) break; msleep(2); continue; } if (r <= 0) break; - string_append_bytes(&chunk_buf, tmp, (uint32_t)r); + used = 0; + chunk_result = http_chunked_decoder_feed(&dec, tmp, (uint32_t)r, &used); body_last_rx_ms = (uint32_t)get_time(); - chunk_result = http_decode_chunked_body(chunk_buf.data, chunk_buf.length, &policy, &decoded, &used); + consumed = buf.length; + if (chunk_result == HTTP_PARSE_OK && used < (uint32_t)r && !conn->carry_buf.length) { + conn->carry_buf = string_repeat('\0', 0); + string_append_bytes(&conn->carry_buf, tmp + used, (uint32_t)r - used); + } } if (chunk_result != HTTP_PARSE_OK) { - if (decoded.mem_length) string_free(decoded); bad_request = true; reject_status = http_parse_result_status(chunk_result); } else { - consumed = buf.length; - if (used < chunk_buf.length) { - conn->carry_buf = string_repeat('\0', 0); - string_append_bytes(&conn->carry_buf, chunk_buf.data + used, chunk_buf.length - used); - } + string decoded = dec.body; + dec.body = string{}; if (decoded.length) { req.body.ptr = (uintptr_t)decoded.data; req.body.size = decoded.length; } else if (decoded.mem_length) string_free(decoded); } - string_free(chunk_buf); + http_chunked_decoder_free(&dec); } else { - if (!bad_request && need > policy.max_body_bytes) { + if (!bad_request && need > policy.common.max_body_bytes) { bad_request = true; reject_status = HTTP_PAYLOAD_TOO_LARGE; } @@ -286,7 +298,7 @@ class HTTPServer { int64_t r = client->recv(body_copy + copied, need - copied); if (r == TCP_WOULDBLOCK) { uint32_t now = (uint32_t)get_time(); - if ((now - body_last_rx_ms) > policy.body_idle_timeout_ms || (now - body_start_ms) > policy.body_total_timeout_ms) { + if ((now - body_last_rx_ms) > policy.common.body_idle_timeout_ms || (now - body_start_ms) > policy.common.body_total_timeout_ms) { bad_request = true; break; } @@ -318,9 +330,9 @@ class HTTPServer { HTTPResponseMsg res{}; res.status_code = reject_status; res.reason = string_from_literal(reason); - res.headers_common.length = body.length; - res.headers_common.type = string_from_literal("text/plain"); - res.headers_common.connection = string_from_literal("close"); + res.headers_common.fields.content_length = body.length; + res.headers_common.fields.content_type = string_from_literal("text/plain"); + res.headers_common.fields.connection = string_from_literal("close"); res.body.ptr = (uintptr_t)body.data; res.body.size = body.length; send_response(conn, res); @@ -369,12 +381,18 @@ class HTTPServer { if (!conn || !conn->client) return SOCK_ERR_STATE; TCPSocket* client = conn->client; - bool send_chunked = res.headers_common.chunked; + bool send_chunked = res.headers_common.framing.chunked; HTTPResponseMsg head = res; - if (!send_chunked) head.body = sizedptr{}; + uint32_t body_len = (!send_chunked && res.body.ptr && res.body.size) ? (uint32_t)res.body.size : 0; + if (!send_chunked) { + head.body = sizedptr{}; + if (body_len && !head.headers_common.framing.has_content_length) { + head.headers_common.fields.content_length = body_len; + head.headers_common.framing.has_content_length = 1; + } + } uint32_t code = (uint32_t)res.status_code; string out = http_response_builder(&head); - uint32_t body_len = (!send_chunked && res.body.ptr && res.body.size) ? (uint32_t)res.body.size : 0; uint32_t out_len = out.length + body_len; int64_t sent = 0; uint32_t start_ms = (uint32_t)get_time(); diff --git a/kernel/networking/application_layer/ssdp.c b/kernel/networking/application_layer/ssdp.c index 96fa265b..a2b7d50f 100644 --- a/kernel/networking/application_layer/ssdp.c +++ b/kernel/networking/application_layer/ssdp.c @@ -61,9 +61,8 @@ string ssdp_build_notify(bool alive, bool v6) { extra[5] = (HTTPHeader){ string_from_literal("SERVER"), string_from_literal("RedactedOS/1.0 UPnP/1.1")}; HTTPHeadersCommon c = (HTTPHeadersCommon){0}; - string hdrs = http_header_builder(&c, extra, 6); + string hdrs = http_header_builder(&c, extra, 6, HTTP_HEADER_BUILD_REQUEST, HTTP_METHOD_GET, 0); string_append_bytes(&out, hdrs.data, hdrs.length); string_free(hdrs); - string_append_bytes(&out, "\r\n", 2); return out; } diff --git a/kernel/networking/net_logger/net_logger.c b/kernel/networking/net_logger/net_logger.c index 2ab6b449..45514aff 100644 --- a/kernel/networking/net_logger/net_logger.c +++ b/kernel/networking/net_logger/net_logger.c @@ -3,16 +3,6 @@ #include "networking/transport_layer/trans_utils.h" #include "networking/application_layer/http.h" -static const char* http_method_str(uint32_t m) { - switch ((HTTPMethod)m) { - case HTTP_METHOD_GET: return "GET"; - case HTTP_METHOD_POST: return "POST"; - case HTTP_METHOD_PUT: return "PUT"; - case HTTP_METHOD_DELETE: return "DELETE"; - default: return ""; - } -} - static const char* comp_str(netlog_component_t c) { switch (c) { case NETLOG_COMP_UDP: return "UDP"; @@ -97,7 +87,7 @@ void netlog_socket_event(const SocketExtraOptions* extra, const netlog_socket_ev } if (e->action == NETLOG_ACT_HTTP_RECV_REQUEST) { - kprintf("[NET][%s] %s method=%s path_len=%u body=%u", c, a, http_method_str(e->u0), (uint32_t)e->u1, (uint32_t)e->i0); + kprintf("[NET][%s] %s method=%s path_len=%u body=%u", c, a, http_method_name(e->u0), (uint32_t)e->u1, (uint32_t)e->i0); return; } @@ -142,8 +132,8 @@ void netlog_socket_event(const SocketExtraOptions* extra, const netlog_socket_ev uint16_t rport = 0; net_ep_split(&e->remote_ep, rip, (int)sizeof(rip), &rv6, &rport); - if (rv6) kprintf("[NET][%s] %s method=%s path_len=%u body=%u remote=[%s]:%u", c, a, http_method_str(e->u0), (uint32_t)e->u1, (uint32_t)e->i0, rip, (uint32_t)rport); - else kprintf("[NET][%s] %s method=%s path_len=%u body=%u remote=%s:%u", c, a, http_method_str(e->u0), (uint32_t)e->u1, (uint32_t)e->i0, rip, (uint32_t)rport); + if (rv6) kprintf("[NET][%s] %s method=%s path_len=%u body=%u remote=[%s]:%u", c, a, http_method_name(e->u0), (uint32_t)e->u1, (uint32_t)e->i0, rip, (uint32_t)rport); + else kprintf("[NET][%s] %s method=%s path_len=%u body=%u remote=%s:%u", c, a, http_method_name(e->u0), (uint32_t)e->u1, (uint32_t)e->i0, rip, (uint32_t)rport); return; } @@ -327,10 +317,10 @@ void netlog_socket_event(const SocketExtraOptions* extra, const netlog_socket_ev if (e->action == NETLOG_ACT_HTTP_RECV_REQUEST) { if (rem_v6) { - if (e->s0) kprintf("[NET][%s] %s method=%s path=%s body=%u remote=[%s]:%u", c, a, http_method_str(e->u0), e->s0, (uint32_t)e->i0, rem_ip, (uint32_t)rem_port); + if (e->s0) kprintf("[NET][%s] %s method=%s path=%s body=%u remote=[%s]:%u", c, a, http_method_name(e->u0), e->s0, (uint32_t)e->i0, rem_ip, (uint32_t)rem_port); else kprintf("[NET][%s] %s method=%u path_len=%u body=%u remote=[%s]:%u", c, a, (uint32_t)e->u0, (uint32_t)e->u1, (uint32_t)e->i0, rem_ip, (uint32_t)rem_port); } else { - if (e->s0) kprintf("[NET][%s] %s method=%s path=%s body=%u remote=%s:%u", c, a, http_method_str(e->u0), e->s0, (uint32_t)e->i0, rem_ip, (uint32_t)rem_port); + if (e->s0) kprintf("[NET][%s] %s method=%s path=%s body=%u remote=%s:%u", c, a, http_method_name(e->u0), e->s0, (uint32_t)e->i0, rem_ip, (uint32_t)rem_port); else kprintf("[NET][%s] %s method=%u path_len=%u body=%u remote=%s:%u", c, a, (uint32_t)e->u0, (uint32_t)e->u1, (uint32_t)e->i0, rem_ip, (uint32_t)rem_port); } return; diff --git a/kernel/networking/processes/net_proc.c b/kernel/networking/processes/net_proc.c index 7a76ec3e..fc223801 100644 --- a/kernel/networking/processes/net_proc.c +++ b/kernel/networking/processes/net_proc.c @@ -12,12 +12,8 @@ #include "networking/link_layer/arp.h" #include "networking/link_layer/ndp.h" -#include "networking/internet_layer/ipv4.h" #include "networking/internet_layer/ipv4_utils.h" - -#include "networking/internet_layer/ipv6.h" #include "networking/internet_layer/ipv6_utils.h" -#include "net/checksums.h" #include "networking/transport_layer/csocket_udp.h" #include "networking/transport_layer/trans_utils.h" @@ -27,18 +23,17 @@ #include "networking/application_layer/dhcp_daemon.h" #include "networking/application_layer/dns/dns_daemon.h" #include "networking/application_layer/dns/mdns_responder.h" -#include "networking/application_layer/dns/dns.h" -#include "networking/application_layer/sntp_daemon.h" #include "networking/application_layer/ntp.h" #include "networking/application_layer/ntp_daemon.h" #include "networking/application_layer/dhcpv6_daemon.h" -#include "networking/application_layer/ssdp_daemon.h" #include "exceptions/timer.h" #include "syscalls/syscalls.h" -#include "memory/page_allocator.h" -#include "memory/mmu.h" +#define HTTP_PORT 80 +#define PROBE_PORT 8080 +#define PROBE_TIMEOUT_MS 2000 +#define PROBE_INTERVAL_MS 50 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(), NULL); @@ -56,18 +51,15 @@ static int udp_probe_server(uint32_t probe_ip, uint16_t probe_port, net_l4_endpo } char recv_buf[64]; + net_l4_endpoint src = (net_l4_endpoint){0}; uint32_t waited = 0; - const uint32_t TIMEOUT_MS = 2000; - const uint32_t INTERVAL_MS = 50; int64_t recvd = 0; - net_l4_endpoint src = (net_l4_endpoint){0}; - while (waited < TIMEOUT_MS) { + while (waited < PROBE_TIMEOUT_MS) { recvd = socket_recvfrom_udp(sock, recv_buf, sizeof(recv_buf), &src); - if (recvd > 0) - break; - msleep(INTERVAL_MS); - waited += INTERVAL_MS; + if (recvd > 0) break; + msleep(PROBE_INTERVAL_MS); + waited += PROBE_INTERVAL_MS; } socket_close_udp(sock); @@ -78,26 +70,30 @@ static int udp_probe_server(uint32_t probe_ip, uint16_t probe_port, net_l4_endpo return 1; } -static void free_request(HTTPRequestMsg *req) { - if (!req) return; +static int net_has_ready_address(void) { + uint8_t n_if = l2_interface_count(); - if (req->path.mem_length) string_free(req->path); + for (uint8_t i = 0; i < n_if; i++) { + l2_interface_t *l2 = l2_interface_at(i); + if (!l2 || !l2->is_up) continue; - http_headers_common_free(&req->headers_common); - http_headers_extra_free(req->extra_headers, req->extra_header_count); - req->extra_headers = NULL; - req->extra_header_count = 0; + for (uint8_t j = 0; j < MAX_IPV4_PER_INTERFACE; j++) { + l3_ipv4_interface_t *v4 = l2->l3_v4[j]; + if (!v4 || v4->mode == IPV4_CFG_DISABLED || v4->is_localhost) continue; + if (!ipv4_is_unspecified(v4->ip) && !ipv4_is_loopback(v4->ip)) return 1; + } - if (req->body.ptr && req->body.size) release((void*)req->body.ptr); + for (uint8_t j = 0; j < MAX_IPV6_PER_INTERFACE; j++) { + l3_ipv6_interface_t *v6 = l2->l3_v6[j]; + if (!v6 || v6->cfg == IPV6_CFG_DISABLE || v6->is_localhost) continue; + if (!ipv6_is_unspecified(v6->ip) && !ipv6_is_loopback(v6->ip) && v6->dad_state == IPV6_DAD_OK) return 1; + } + } - req->path = (string){0}; - req->body = (sizedptr){0}; + return 0; } - static void run_http_server() { - //mmu_enable_verbose(); - //page_alloc_enable_verbose(); uint16_t pid = get_current_proc_pid(); SocketExtraOptions opt = {0}; opt.debug_level = SOCK_DBG_ALL; @@ -109,7 +105,7 @@ static void run_http_server() { } struct SockBindSpec spec = {0}; spec.kind = BIND_ANY; - if (http_server_bind(srv, &spec, 80) < 0) { + if (http_server_bind(srv, &spec, HTTP_PORT) < 0) { http_server_destroy(srv); stop_current_process(2); return; @@ -122,7 +118,7 @@ static void run_http_server() { return; } - mdns_register_service("RedactedOS", "http", "tcp", 80, "path=/"); + mdns_register_service("RedactedOS", "http", "tcp", HTTP_PORT, "path=/"); static const char HTML_ROOT[] = "

Hello, world!

\n" @@ -132,11 +128,6 @@ static void run_http_server() { "

404 Regrettably, no such page exists in this realm

\n" "

Im rather inclined to deduce that your page simply does not exist. Given the state of affairs, I dare say it's not altogether surprising, innit?

"; - const string STR_OK = string_from_const("OK"); - const string STR_HTML = string_from_const("text/html"); - const string STR_CLOSE = string_from_const("close"); - const string STR_NOTFOUND= string_from_const("Not Found"); - while (1) { http_connection_handle_t conn = http_server_accept(srv); if (!conn){ @@ -144,50 +135,41 @@ static void run_http_server() { 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; - memcpy(tmp, req.path.data, n); - } + int is_root = req.path.length == 1 && req.path.data[0] == '/'; + const char *body = is_root ? HTML_ROOT : HTML_404; + uint32_t len = (uint32_t)strlen(body); HTTPResponseMsg res = (HTTPResponseMsg){0}; - - if (req.path.length == 1 && req.path.data[0] == '/') { - res.status_code = HTTP_OK; - res.reason = STR_OK; - res.headers_common.length = sizeof(HTML_ROOT) - 1; - res.headers_common.type = STR_HTML; - res.headers_common.connection = STR_CLOSE; - res.body.ptr = (uintptr_t)HTML_ROOT; - res.body.size = sizeof(HTML_ROOT) - 1; - } else { - res.status_code = HTTP_NOT_FOUND; - res.reason = STR_NOTFOUND; - res.headers_common.length = sizeof(HTML_404) - 1; - res.headers_common.type = STR_HTML; - res.headers_common.connection = STR_CLOSE; - res.body.ptr = (uintptr_t)HTML_404; - res.body.size = sizeof(HTML_404) - 1; - } + res.status_code = is_root ? HTTP_OK : HTTP_NOT_FOUND; + res.reason = string_from_const(is_root ? "OK" : "Not Found"); + res.headers_common.fields.content_length = len; + res.headers_common.framing.has_content_length = 1; + res.headers_common.fields.content_type = string_from_const("text/html"); + res.headers_common.fields.connection = string_from_const("close"); + res.body.ptr = (uintptr_t)body; + res.body.size = len; http_server_send_response(srv, conn, &res); http_connection_close(conn); - free_request(&req); + if (req.path.mem_length) string_free(req.path); + http_headers_common_free(&req.headers_common); + http_headers_extra_free(req.extra_headers, req.extra_header_count); + if (req.body.ptr && req.body.size) release((void*)req.body.ptr); } } static void test_http(const net_l4_endpoint* ep) { + if (!ep) return; 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:%i", ip_str, HTTP_PORT); } - uint16_t pid = get_current_proc_pid(); - http_client_handle_t cli = http_client_create(pid, NULL, NULL); + http_client_handle_t cli = http_client_create((uint16_t)get_current_proc_pid(), NULL, NULL); if (!cli) { kprintf("[HTTP] http_client_create FAIL"); return; @@ -195,15 +177,15 @@ static void test_http(const net_l4_endpoint* ep) { net_l4_endpoint e = {0}; e.ver = ep->ver; + e.port = HTTP_PORT; if (e.ver == IP_VER4) memcpy(e.ip, ep->ip, 4); else if (e.ver == IP_VER6) memcpy(e.ip, ep->ip, 16); else { http_client_destroy(cli); return; } - e.port = 80; - int rc = http_client_connect(cli, DST_ENDPOINT, &e, 0); + int32_t rc = http_client_connect(cli, DST_ENDPOINT, &e, HTTP_PORT); if (rc < 0) { http_client_destroy(cli); return; @@ -211,19 +193,14 @@ static void test_http(const net_l4_endpoint* ep) { HTTPRequestMsg req = (HTTPRequestMsg){0}; req.method = HTTP_METHOD_GET; + req.version = HTTP_VERSION_11; req.path = string_from_const("/"); - req.headers_common.connection = string_from_const("close"); + req.headers_common.fields.connection = string_from_const("close"); HTTPResponseMsg resp = http_client_send_request(cli, &req); - 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) { + if ((int)resp.status_code < 0) kprintf("[HTTP] request FAIL status=%i", (int)resp.status_code); + else if (resp.body.ptr && resp.body.size) { char *body_str = (char*)zalloc(resp.body.size + 1); if (body_str) { memcpy(body_str, (void*)resp.body.ptr, resp.body.size); @@ -238,59 +215,14 @@ static void test_http(const net_l4_endpoint* ep) { http_client_destroy(cli); if (resp.body.ptr && resp.body.size) release((void*)resp.body.ptr); + if (resp.reason.mem_length) string_free(resp.reason); http_headers_common_free(&resp.headers_common); - - if (resp.reason.data && resp.reason.mem_length) string_free(resp.reason); http_headers_extra_free(resp.extra_headers, resp.extra_header_count); } -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 ((ifv4->ip & 0xFF000000u) == 0x7F000000u) return 0; - return 1; -} - -static int ifv6_is_ready_nonlocal(const l3_ipv6_interface_t* ifv6) { - if (!ifv6) return 0; - if (ifv6->cfg == IPV6_CFG_DISABLE) return 0; - if (ifv6->is_localhost) return 0; - if (ipv6_is_unspecified(ifv6->ip)) return 0; - if (ifv6->dad_state != IPV6_DAD_OK) return 0; - if (ipv6_is_loopback(ifv6->ip)) return 0; - return 1; -} - -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 int any_ipv6_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_IPV6_PER_INTERFACE; j++) { - l3_ipv6_interface_t* ifv6 = l2->l3_v6[j]; - if (ifv6_is_ready_nonlocal(ifv6)) return 1; - } - } - return 0; -} - static int ntp(int argc, char* argv[]) { + (void)argc; (void)argv; if (!ntp_is_running()) { kprintf("[TIME] starting NTP..."); create_kernel_process("ntpd", ntp_daemon_entry, 0, 0); @@ -311,39 +243,20 @@ static int ntp(int argc, char* argv[]) { 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); + 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); + timer_datetime_to_string(&now_dt_loc, s, sizeof(s)); kprintf("[TIME] LOCAL: %s (TZ %i min)", s, (int32_t)timer_get_timezone_minutes()); } return 0; } -static void test_net_for_interface(l3_ipv4_interface_t* ifv4) { - if (!ifv4_is_ready_nonlocal(ifv4)) 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); - uint32_t probe_ip = (ifv4->ip && ifv4->mask) ? ipv4_broadcast_calc(ifv4->ip, ifv4->mask) : 0; - 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); - } -} +static int net_test_entry(int argc, char *argv[]) { + (void)argc; (void)argv; -static void test_net() { create_kernel_process("ntp", ntp, 0, NULL); uint8_t n_if = l2_interface_count(); int tested_any = 0; @@ -352,32 +265,38 @@ static void test_net() { 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; + if (!ifv4 || ifv4->mode == IPV4_CFG_DISABLED || ifv4->is_localhost) continue; + if (ipv4_is_unspecified(ifv4->ip) || ipv4_is_loopback(ifv4->ip) || !ifv4->mask) continue; + + uint32_t probe_ip = ipv4_broadcast_calc(ifv4->ip, ifv4->mask); + if (!probe_ip) continue; + + 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, PROBE_PORT, &srv)) { + test_http(&srv); + tested_any = 1; + } else kprintf("[NET] no UDP responder at %s:%i (l3_id=%u)", probe_str, PROBE_PORT, (unsigned)ifv4->l3_id); } } - 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"); + if (udp_probe_server(fallback, PROBE_PORT, &srv)) test_http(&srv); + else kprintf("[NET] could not find update server"); } -} -static int net_test_entry(int argc, char* argv[]) { - (void)argc; (void)argv; - test_net(); + run_http_server(); return 0; } static int ip_waiter_entry(int argc, char* argv[]) { (void)argc; (void)argv; uint32_t waited = 0; - while (!any_ipv4_ready() && !any_ipv6_ready()) { + while (!net_has_ready_address()) { if ((waited % 1000) == 0) kprintf("[NET] ip_waiter: waiting for ip..."); msleep(200); waited += 200; @@ -395,12 +314,10 @@ process_t* launch_net_process() { create_kernel_process("dhcpv6_daemon", dhcpv6_daemon_entry, 0, 0); create_kernel_process("dns_daemon", dns_deamon_entry, 0, 0); - if (any_ipv4_ready() || any_ipv6_ready()) { + if (net_has_ready_address()) { kprintf("[NET] ip ready, starting net_test"); create_kernel_process("net_test", net_test_entry, 0, 0); - return NULL; - } + } else create_kernel_process("ip_waiter", ip_waiter_entry, 0, 0); - create_kernel_process("ip_waiter", ip_waiter_entry, 0, 0); return NULL; } \ No newline at end of file diff --git a/kernel/process/kernel_syscall_impl.c b/kernel/process/kernel_syscall_impl.c index d7fdd52c..9613d539 100644 --- a/kernel/process/kernel_syscall_impl.c +++ b/kernel/process/kernel_syscall_impl.c @@ -126,11 +126,11 @@ bool socket_accept(SocketHandle *spec){ return 1; } -size_t socket_send(SocketHandle *handle, SockDstKind dst_kind, const void* dst, uint16_t port, void *packet, size_t size){ +int64_t socket_send(SocketHandle *handle, SockDstKind dst_kind, const void* dst, uint16_t port, void *packet, size_t size){ return send_on_socket(handle, dst_kind, dst, port, packet, size, get_current_proc_pid()); } -bool socket_receive(SocketHandle *handle, void *packet, size_t size, net_l4_endpoint* out_src){ +int64_t socket_receive(SocketHandle *handle, void *packet, size_t size, net_l4_endpoint* out_src){ return receive_from_socket(handle, packet, size, out_src, get_current_proc_pid()); } diff --git a/kernel/tools/curl.c b/kernel/tools/curl.c new file mode 100644 index 00000000..b2e68895 --- /dev/null +++ b/kernel/tools/curl.c @@ -0,0 +1,176 @@ +#include "curl.h" +#include "console/kio.h" +#include "data/format/url.h" +#include "networking/application_layer/csocket_http_client.h" +#include "networking/application_layer/http.h" +#include "process/scheduler.h" +#include "std/memory.h" +#include "std/string.h" +#include "syscalls/syscalls.h" + +#define CURL_MAX_REDIRECTS 5 + +typedef struct { + const char *out_path; + char *url; + bool head_only; + bool follow; +} curl_opts_t; + +static int curl_fetch(char *url, const char *out_path, bool head_only, bool follow) { + string host = (string){0}; + string path = (string){0}; + uint16_t port = 0; + bool https = false; + + if (!url) { + print("curl: bad url"); + return 2; + } + + ParsedURL parsed = parse_url(url, (uint32_t)strlen(url)); + if (!parsed.ok || !parsed.scheme.ptr || !parsed.host.ptr || !parsed.host.size) { + print("curl: bad url"); + return 2; + } + + if (parsed.scheme.size == 5 && strncmp_case((const char*)parsed.scheme.ptr, "https", true, 5) == 0) { + https = true; + port = parsed.port ? parsed.port : 443; + } else if (parsed.scheme.size == 4 && strncmp_case((const char*)parsed.scheme.ptr, "http", true, 4) == 0) port = parsed.port ? parsed.port : 80; + else { + print("curl: bad url"); + return 2; + } + + host = string_from_literal_length((const char*)parsed.host.ptr, parsed.host.size); + if (!host.data) { + print("curl: bad url"); + return 2; + } + + path = string_repeat('\0', 0); + if (parsed.path.ptr && parsed.path.size) string_append_bytes(&path, (const char*)parsed.path.ptr, parsed.path.size); + else string_append_bytes(&path, "/", 1); + if (parsed.query.ptr && parsed.query.size) { + string_append_bytes(&path, "?", 1); + string_append_bytes(&path, (const char*)parsed.query.ptr, parsed.query.size); + } + + if (!path.data) { + string_free(host); + print("curl: bad url"); + return 2; + } + + if (https) { + string_free(host); + string_free(path); + print("curl: https is not supported yet"); + return 3; + } + + HTTPClientPolicyOptions opts = {0}; + opts.common.allow_chunked = 1; + opts.follow_redirects = follow ? 1 : 0; + opts.max_redirects = CURL_MAX_REDIRECTS; + + http_client_handle_t cli = http_client_create((uint16_t)get_current_proc_pid(), NULL, &opts); + if (!cli) { + string_free(host); + string_free(path); + print("curl: socket create failed"); + return 4; + } + + int32_t rc = http_client_connect(cli, DST_DOMAIN, host.data, port); + if (rc < 0) { + print("curl: connect failed for %s:%d", host.data, port); + http_client_destroy(cli); + string_free(host); + string_free(path); + return 5; + } + + HTTPRequestMsg req = (HTTPRequestMsg){0}; + req.method = head_only ? HTTP_METHOD_HEAD : HTTP_METHOD_GET; + req.version = HTTP_VERSION_11; + req.path = path; + req.headers_common.fields.connection = string_from_const("close"); + + HTTPResponseMsg resp = http_client_send_request(cli, &req); + if ((int32_t)resp.status_code < 0) { + print("curl: request failed (%d)", (int)resp.status_code); + http_client_destroy(cli); + string_free(host); + string_free(path); + return 6; + } + + if (head_only) { + HTTPResponseMsg head = resp; + head.body = (sizedptr){0}; + string raw = http_response_builder(&head); + print("%.*s", (int)raw.length, raw.data); + string_free(raw); + } else if (resp.body.ptr && resp.body.size) { + int write_ok; + + if (!out_path) { + print("%.*s", (int)resp.body.size, (const char*)resp.body.ptr); + write_ok = 1; + } else write_ok = swritef(out_path, (const void*)resp.body.ptr, (uint32_t)resp.body.size, false) == resp.body.size; + + if (!write_ok) { + print("curl: write failed"); + + if (resp.reason.mem_length) string_free(resp.reason); + http_headers_common_free(&resp.headers_common); + http_headers_extra_free(resp.extra_headers, resp.extra_header_count); + if (resp.body.ptr && resp.body.size) release((void*)resp.body.ptr); + resp = (HTTPResponseMsg){0}; + + http_client_destroy(cli); + string_free(host); + string_free(path); + return 7; + } + } + + if (resp.reason.mem_length) string_free(resp.reason); + http_headers_common_free(&resp.headers_common); + http_headers_extra_free(resp.extra_headers, resp.extra_header_count); + if (resp.body.ptr && resp.body.size) release((void*)resp.body.ptr); + resp = (HTTPResponseMsg){0}; + + http_client_destroy(cli); + string_free(host); + string_free(path); + return 0; +} + +static bool parse_args(int argc, char *argv[], curl_opts_t *o) { + memset(o, 0, sizeof(*o)); + + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "-I") == 0) o->head_only = true; + else if (strcmp(argv[i], "-L") == 0) o->follow = true; + else if (strcmp(argv[i], "-o") == 0) { + if (++i >= argc) return false; + o->out_path = argv[i]; + } else if (!o->url) o->url = argv[i]; + else return false; + } + + return o->url != NULL; +} + +int run_curl(int argc, char* argv[]) { + curl_opts_t opts; + if (!parse_args(argc, argv, &opts)) { + print("usage: curl [-L] [-I] [-o file] http://host/path"); + return 2; + } + + return curl_fetch(opts.url, opts.out_path, opts.head_only, opts.follow); +} diff --git a/kernel/tools/curl.h b/kernel/tools/curl.h new file mode 100644 index 00000000..13c58089 --- /dev/null +++ b/kernel/tools/curl.h @@ -0,0 +1,12 @@ +#pragma once +#include "process/process.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int run_curl(int argc, char* argv[]); + +#ifdef __cplusplus +} +#endif diff --git a/kernel/tools/ping.c b/kernel/tools/ping.c index 157dd36a..1c2db943 100644 --- a/kernel/tools/ping.c +++ b/kernel/tools/ping.c @@ -99,7 +99,7 @@ static int ping_v4(const ping_opts_t *o) { uint32_t r = 0; dns_result_t dr = dns_resolve_a(host, &r, DNS_USE_BOTH, o->timeout_ms); if (dr != DNS_OK) { - print("ping: dns lookup failed (%d) for '%s'\n", (int)dr, host); + print("ping: dns lookup failed (%d) for '%s'", (int)dr, host); return 2; } dst_ip_be = r; @@ -108,7 +108,7 @@ static int ping_v4(const ping_opts_t *o) { char ipstr[16]; ipv4_to_string(dst_ip_be, ipstr); - print("PING %s (%s) with 32 bytes of data:\n", host, ipstr); + print("PING %s (%s) with 32 bytes of data:", host, ipstr); uint32_t sent = 0, received = 0, min_ms = UINT32_MAX, max_ms = 0; uint64_t sum_ms = 0; @@ -120,7 +120,7 @@ static int ping_v4(const ping_opts_t *o) { if (o->src_set) { l3_ipv4_interface_t *l3 = l3_ipv4_find_by_ip(o->src_ip); if (!l3) { - print("ping: invalid source (no local ip match)\n"); + print("ping: invalid source (no local ip match)"); return 2; } txo.index = (uint8_t)l3->l3_id; @@ -141,26 +141,26 @@ static int ping_v4(const ping_opts_t *o) { if (rtt < min_ms) min_ms = rtt; if (rtt > max_ms) max_ms = rtt; sum_ms += rtt; - print("Reply from %s: bytes=32 time=%ums\n", ipstr, rtt); + print("Reply from %s: bytes=32 time=%ums", ipstr, rtt); } else { - print("%s\n", status_to_msg(res.status)); + print("%s", status_to_msg(res.status)); } if (i + 1 < o->count) msleep(o->interval_ms); } - print("\n"); - print("--- %s ping statistics ---\n", host); + print(""); + print("--- %s ping statistics ---", host); uint32_t loss = (sent == 0) ? 0 : (uint32_t)((((uint64_t)(sent - received)) * 100) / sent); uint32_t total_time = (o->count > 0) ? (o->count - 1) * o->interval_ms : 0; - print("%u packets transmitted, %u received, %u%% packet loss, time %ums\n", sent, received, loss, total_time); + print("%u packets transmitted, %u received, %u%% packet loss, time %ums", sent, received, loss, total_time); if (received > 0) { uint32_t avg = (uint32_t)(sum_ms / received); if (min_ms == UINT32_MAX) min_ms = avg; - print("rtt min/avg/max = %u/%u/%u ms\n", min_ms, avg, max_ms); + print("rtt min/avg/max = %u/%u/%u ms", min_ms, avg, max_ms); } return (received > 0) ? 0 : 1; @@ -174,7 +174,7 @@ static int ping_v6(const ping_opts_t *o) { if (!is_lit) { dns_result_t dr = dns_resolve_aaaa(host, dst6, DNS_USE_BOTH, o->timeout_ms); if (dr != DNS_OK) { - print("ping: dns lookup failed (%d) for '%s'\n",(int)dr, host); + print("ping: dns lookup failed (%d) for '%s'",(int)dr, host); return 2; } } @@ -182,7 +182,7 @@ static int ping_v6(const ping_opts_t *o) { char ipstr[64]; ipv6_to_string(dst6, ipstr, (int)sizeof(ipstr)); - print("PING %s (%s) with 32 bytes of data:\n", host, ipstr); + print("PING %s (%s) with 32 bytes of data:", host, ipstr); uint32_t sent = 0, received = 0, min_ms = UINT32_MAX, max_ms = 0; uint64_t sum_ms = 0; @@ -202,27 +202,27 @@ static int ping_v6(const ping_opts_t *o) { if (rtt < min_ms) min_ms = rtt; if (rtt > max_ms) max_ms = rtt; sum_ms += rtt; - print("Reply from %s: bytes=32 time=%ums\n", ipstr, rtt); + print("Reply from %s: bytes=32 time=%ums", ipstr, rtt); } else { - print("%s\n", status_to_msg(res.status)); + print("%s", status_to_msg(res.status)); } if (i + 1 < o->count) msleep(o->interval_ms); } - print("\n"); + print(""); - print("--- %s ping statistics ---\n", host); + print("--- %s ping statistics ---", host); uint32_t loss = (sent == 0) ? 0 : (uint32_t)((((uint64_t)(sent - received)) * 100) / sent); uint32_t total_time = (o->count > 0) ? (o->count - 1) * o->interval_ms : 0; - print("%u packets transmitted, %u received, %u%% packet loss, time %ums\n", sent, received, loss, total_time); + print("%u packets transmitted, %u received, %u%% packet loss, time %ums", sent, received, loss, total_time); if (received > 0) { uint32_t avg = (uint32_t)(sum_ms / received); if (min_ms == UINT32_MAX) min_ms = avg; - print("rtt min/avg/max = %u/%u/%u ms\n", min_ms, avg, max_ms); + print("rtt min/avg/max = %u/%u/%u ms", min_ms, avg, max_ms); } return (received > 0) ? 0 : 1; @@ -231,12 +231,12 @@ static int ping_v6(const ping_opts_t *o) { int run_ping(int argc, char *argv[]) { ping_opts_t opts; if (!parse_args(argc, argv, &opts)) { - print("usage: ping [-4/-6] [-n times] [-w timeout] [-i interval] [-t TTL] [-s src_local_ip] host\n"); + print("usage: ping [-4/-6] [-n times] [-w timeout] [-i interval] [-t TTL] [-s src_local_ip] host"); return 2; } if (opts.ver == IP_VER6 && opts.src_set) { - print("ping: -s is only supported for IPv4\n"); + print("ping: -s is only supported for IPv4"); return 2; } @@ -245,14 +245,14 @@ int run_ping(int argc, char *argv[]) { if (!l3) { char ssrc[16]; ipv4_to_string(opts.src_ip, ssrc); - print("ping: invalid source %s (no local ip match)\n", ssrc); + print("ping: invalid source %s (no local ip match)", ssrc); return 2; } } if (opts.ver == IP_VER4) return ping_v4(&opts); if (opts.ver == IP_VER6) return ping_v6(&opts); - print("usage: ping [-4/-6] [-n times] [-w timeout] [-i interval] [-t TTL] [-s src_local_ip] host\n"); + print("usage: ping [-4/-6] [-n times] [-w timeout] [-i interval] [-t TTL] [-s src_local_ip] host"); return 2; } diff --git a/kernel/tools/tools.c b/kernel/tools/tools.c index 37d1de5f..22980158 100644 --- a/kernel/tools/tools.c +++ b/kernel/tools/tools.c @@ -2,6 +2,7 @@ #include "ping.h" #include "shutdown.h" #include "tracert.h" +#include "curl.h" #include "monitor_processes.h" #include "kernel_processes/kprocess_loader.h" #include "filesystem/filesystem.h" @@ -26,6 +27,7 @@ open_tools_ref available_cmds[] = { { "ping", run_ping }, { "shutdown", run_shutdown }, { "tracert", run_tracert }, + { "curl", run_curl }, { "monitor", monitor_procs }, }; diff --git a/kernel/tools/tracert.c b/kernel/tools/tracert.c index a37ea22d..f4c5b688 100644 --- a/kernel/tools/tracert.c +++ b/kernel/tools/tracert.c @@ -92,7 +92,7 @@ static int tracert_v4(const tr_opts_t *o) { uint32_t r = 0; dns_result_t dr = dns_resolve_a(o->host, &r, DNS_USE_BOTH, o->timeout_ms); if (dr != DNS_OK) { - print("tracert: dns lookup failed (%d) for '%s'\n", (int)dr, o->host); + print("tracert: dns lookup failed (%d) for '%s'", (int)dr, o->host); return 2; } dst = r; @@ -101,11 +101,11 @@ static int tracert_v4(const tr_opts_t *o) { char dip[16]; char line[256]; ipv4_to_string(dst, dip); - print("Tracing route to %s [%s]\n", o->host, dip); + print("Tracing route to %s [%s]", o->host, dip); size_t len = string_format_buf(line, sizeof(line), "hop "); for (uint32_t p = 0; p < o->count && len < sizeof(line); p++) len += string_format_buf(line + len, sizeof(line) - len, "rtt%u ", p + 1); string_format_buf(line + len, sizeof(line) - len, "address"); - print("%s\n", line); + print("%s", line); ipv4_tx_opts_t txo = (ipv4_tx_opts_t){0}; const ipv4_tx_opts_t *txop = NULL; @@ -114,7 +114,7 @@ static int tracert_v4(const tr_opts_t *o) { if (!l3) { char ssrc[16]; ipv4_to_string(o->src_ip, ssrc); - print("tracert: invalid source %s (no local ip match)\n", ssrc); + print("tracert: invalid source %s (no local ip match)", ssrc); return 2; } txo.index = l3->l3_id; @@ -162,11 +162,11 @@ static int tracert_v4(const tr_opts_t *o) { dead_streak++; string_format_buf(line + len, sizeof(line) - len, "Request timed out."); } - print("%s\n", line); + print("%s", line); if (hop_ip == dst) break; if (dead_streak >= o->timeout_streak_limit) { - print("stopping after %u consecutive timeout hops\n", dead_streak); + print("stopping after %u consecutive timeout hops", dead_streak); break; } } @@ -180,7 +180,7 @@ static int tracert_v6(const tr_opts_t *o) { if (!lit) { dns_result_t dr = dns_resolve_aaaa(o->host, dst, DNS_USE_BOTH, o->timeout_ms); if (dr != DNS_OK) { - print("tracert: dns lookup failed (%d) for '%s'\n", (int)dr, o->host); + print("tracert: dns lookup failed (%d) for '%s'", (int)dr, o->host); return 2; } } @@ -188,11 +188,11 @@ static int tracert_v6(const tr_opts_t *o) { char dip[64]; char line[256]; ipv6_to_string(dst, dip, (int)sizeof(dip)); - print("Tracing route to %s [%s]\n", o->host, dip); + print("Tracing route to %s [%s]", o->host, dip); size_t len = string_format_buf(line, sizeof(line), "hop "); for (uint32_t p = 0; p < o->count && len < sizeof(line); p++) len += string_format_buf(line + len, sizeof(line) - len, "rtt%u ", p + 1); string_format_buf(line + len, sizeof(line) - len, "address"); - print("%s\n", line); + print("%s", line); uint16_t id = (uint16_t)(get_current_proc_pid() & 0xFFFF); uint16_t seq0 = (uint16_t)(get_time() & 0xFFFF); @@ -235,11 +235,11 @@ static int tracert_v6(const tr_opts_t *o) { dead_streak++; string_format_buf(line + len, sizeof(line) - len, "Request timed out."); } - print("%s\n", line); + print("%s", line); if (ipv6_cmp(hop_ip, dst) == 0) break; if (dead_streak >= o->timeout_streak_limit) { - print("stopping after %u consecutive timeout hops\n", dead_streak); + print("stopping after %u consecutive timeout hops", dead_streak); break; } } @@ -250,7 +250,7 @@ static int tracert_v6(const tr_opts_t *o) { int run_tracert(int argc, char *argv[]) { tr_opts_t o; if (!parse_args(argc, argv, &o)) { - print("usage: tracert [-4/-6] [-m max_hops] [-n probes] [-w timeout_ms] [-i interval_ms] [-x stop_after_timeouts] [-s src_local_ip] host\n"); + print("usage: tracert [-4/-6] [-m max_hops] [-n probes] [-w timeout_ms] [-i interval_ms] [-x stop_after_timeouts] [-s src_local_ip] host"); return 2; } From e3da69e5509dd23ee30448b867b4a52d085b61f5 Mon Sep 17 00:00:00 2001 From: CodeAnarchist Date: Tue, 2 Jun 2026 02:05:43 +0200 Subject: [PATCH 19/39] [network] fix mdns, multicast sockets, http chunked and misc --- .../application_layer/dns/dns_daemon.c | 24 +- .../application_layer/dns/mdns_responder.c | 3 +- kernel/networking/application_layer/http.c | 26 +- .../application_layer/socket_http_client.hpp | 1 - .../application_layer/socket_http_server.hpp | 49 ++- .../application_layer/ssdp_daemon.c | 60 ++-- kernel/networking/interface_manager.c | 42 ++- kernel/networking/interface_manager.h | 8 +- kernel/networking/internet_layer/ipv4_route.c | 10 +- kernel/networking/internet_layer/ipv6.c | 4 +- kernel/networking/net_logger/net_logger.c | 2 + .../networking/transport_layer/socket_tcp.hpp | 146 +++++---- .../networking/transport_layer/socket_udp.hpp | 291 ++++++++++++++---- .../networking/transport_layer/tcp/tcp_rx.c | 9 +- 14 files changed, 453 insertions(+), 222 deletions(-) diff --git a/kernel/networking/application_layer/dns/dns_daemon.c b/kernel/networking/application_layer/dns/dns_daemon.c index 481c4e5f..e6ffcab5 100644 --- a/kernel/networking/application_layer/dns/dns_daemon.c +++ b/kernel/networking/application_layer/dns/dns_daemon.c @@ -8,7 +8,6 @@ #include "networking/transport_layer/csocket_udp.h" #include "networking/internet_layer/ipv6_utils.h" #include "std/memory.h" -#include "net/socket_types.h" static uint16_t g_pid_dnsd = 0xFFFF; static socket_handle_t g_sock = 0; @@ -24,14 +23,18 @@ socket_handle_t dns_socket_handle(void){ return g_sock; } socket_handle_t mdns_socket_handle_v4(void){ return g_sock_mdns4; } socket_handle_t mdns_socket_handle_v6(void){ return g_sock_mdns6; } -static socket_handle_t mdns_create_socket(ip_version_t ver, const void* group) { +static socket_handle_t mdns_create_socket(ip_version_t ver, const uint8_t* group) { + if (!group) return 0; SocketExtraOptions opt; memset(&opt, 0, sizeof(opt)); opt.flags = SOCK_OPT_MCAST_JOIN | SOCK_OPT_TTL; opt.ttl = 255; - opt.mcast_ver = ver; - if(ver == IP_VER4) memcpy(opt.mcast_group, group, 4); - else memcpy(opt.mcast_group, group, 16); + opt.mcast_count = 1; + opt.mcast_groups[0].ver = ver; + opt.mcast_groups[0].port = DNS_MDNS_PORT; + if(ver == IP_VER4) memcpy(opt.mcast_groups[0].ip, group, 4); + else if (ver == IP_VER6) memcpy(opt.mcast_groups[0].ip, group, 16); + else return 0; socket_handle_t s = udp_socket_create(SOCK_ROLE_SERVER, g_pid_dnsd, &opt); if(!s) return 0; @@ -39,6 +42,7 @@ static socket_handle_t mdns_create_socket(ip_version_t ver, const void* group) { SockBindSpec spec; memset(&spec, 0, sizeof(spec)); spec.kind = BIND_ANY; + spec.ver = ver; if(socket_bind_udp(s, &spec, DNS_MDNS_PORT) != SOCK_OK){ socket_destroy_udp(s); @@ -71,19 +75,13 @@ int dns_deamon_entry(int argc, char* argv[]){ if (g_sock_mdns4) { memset(&src, 0, sizeof(src)); int64_t r4 = socket_recvfrom_udp(g_sock_mdns4, buf, sizeof(buf), &src); - if(r4 > 0) { - uint32_t len4 = r4; - mdns_responder_handle_query(g_sock_mdns4, IP_VER4, mdns_v4_addr, buf, len4, &src); - } + if(r4 > 0 && src.ver == IP_VER4) mdns_responder_handle_query(g_sock_mdns4, IP_VER4, mdns_v4_addr, buf, (uint32_t)r4, &src); } if (g_sock_mdns6) { memset(&src, 0, sizeof(src)); int64_t r6 = socket_recvfrom_udp(g_sock_mdns6, buf, sizeof(buf), &src); - if(r6 > 0) { - uint32_t len6 = r6; - mdns_responder_handle_query(g_sock_mdns6, IP_VER6, mdns_v6, buf, len6, &src); - } + if(r6 > 0 && src.ver == IP_VER6) mdns_responder_handle_query(g_sock_mdns6, IP_VER6, mdns_v6, buf, (uint32_t)r6, &src); } mdns_responder_tick(g_sock_mdns4,g_sock_mdns6,mdns_v4_addr,mdns_v6); diff --git a/kernel/networking/application_layer/dns/mdns_responder.c b/kernel/networking/application_layer/dns/mdns_responder.c index 31e1f480..b26f214d 100644 --- a/kernel/networking/application_layer/dns/mdns_responder.c +++ b/kernel/networking/application_layer/dns/mdns_responder.c @@ -774,7 +774,7 @@ void mdns_responder_handle_query(socket_handle_t sock, ip_version_t ver, const u uint16_t qd = rd_be16(pkt + 4); if (!qd) return; - bool unicast_any = false; + bool unicast_any = src && src->port && src->port != DNS_MDNS_PORT; uint8_t out[1500]; mdns_pkt_t p; @@ -907,6 +907,7 @@ void mdns_responder_handle_query(socket_handle_t sock, ip_version_t ver, const u mdns_pkt_commit(&p); mdns_send(sock, src, unicast_any, ver, mcast_ip, out, p.off); + if (unicast_any) mdns_send(sock, NULL, false, ver, mcast_ip, out, p.off); uint32_t ttl_ms = MDNS_TTL_S * 1000; diff --git a/kernel/networking/application_layer/http.c b/kernel/networking/application_layer/http.c index fb9ce669..d96197d8 100644 --- a/kernel/networking/application_layer/http.c +++ b/kernel/networking/application_layer/http.c @@ -35,7 +35,8 @@ HTTPPolicy http_policy_from_options(const HTTPPolicyOptions *options) { if (options->header_total_timeout_ms > 0) p.header_total_timeout_ms = options->header_total_timeout_ms; if (options->body_idle_timeout_ms > 0) p.body_idle_timeout_ms = options->body_idle_timeout_ms; if (options->body_total_timeout_ms > 0) p.body_total_timeout_ms = options->body_total_timeout_ms; - p.allow_chunked = options->allow_chunked != 0; + if (options->allow_chunked > 0) p.allow_chunked = true; + else if (options->allow_chunked < 0) p.allow_chunked = false; return p; } @@ -43,7 +44,7 @@ HTTPServerPolicy http_server_policy_from_options(const HTTPServerPolicyOptions * HTTPServerPolicy p = { .common = http_default_policy(), .max_keepalive_requests = HTTP_DEFAULT_MAX_KEEPALIVE_REQUESTS, - .allow_keep_alive = false, + .allow_keep_alive = true, .allow_absolute_uri = true, .require_host_http11 = true }; @@ -52,9 +53,12 @@ HTTPServerPolicy http_server_policy_from_options(const HTTPServerPolicyOptions * p.common = http_policy_from_options(&options->common); if (options->max_keepalive_requests > 0) p.max_keepalive_requests = options->max_keepalive_requests; - p.allow_keep_alive = options->allow_keep_alive != 0; - p.allow_absolute_uri = options->allow_absolute_uri != 0; - p.require_host_http11 = options->require_host_http11 != 0; + if (options->allow_keep_alive > 0) p.allow_keep_alive = true; + else if (options->allow_keep_alive < 0) p.allow_keep_alive = false; + if (options->allow_absolute_uri > 0) p.allow_absolute_uri = true; + else if (options->allow_absolute_uri < 0) p.allow_absolute_uri = false; + if (options->require_host_http11 > 0) p.require_host_http11 = true; + else if (options->require_host_http11 < 0) p.require_host_http11 = false; return p; } @@ -70,7 +74,8 @@ HTTPClientPolicy http_client_policy_from_options(const HTTPClientPolicyOptions * p.common = http_policy_from_options(&options->common); if (options->max_redirects > 0) p.max_redirects = options->max_redirects; - p.follow_redirects = options->follow_redirects != 0; + if (options->follow_redirects > 0) p.follow_redirects = true; + else if (options->follow_redirects < 0) p.follow_redirects = false; if (options->allow_close_delimited > 0) p.allow_close_delimited = true; else if (options->allow_close_delimited < 0) p.allow_close_delimited = false; return p; @@ -552,9 +557,10 @@ static void http_append_chunked_body(string *out, uintptr_t ptr, uint32_t len) { if (!out) return; if (len) { - string tmp = string_format("%x\r\n", (int)len); - string_append_bytes(out, tmp.data, tmp.length); - string_free(tmp); + char hex[16]; + uint32_t n = u64_to_base(hex, len, 16, 0); + string_append_bytes(out, hex, n); + string_append_bytes(out, "\r\n", 2); string_append_bytes(out, (char*)ptr, len); string_append_bytes(out, "\r\n", 2); } @@ -685,7 +691,7 @@ HTTPParseResult http_chunked_decoder_feed(HTTPChunkedDecoder *dec, const char *b } } else if (dec->stage == HTTP_CHUNK_STAGE_DATA) { uint64_t need64 = dec->chunk_size - dec->chunk_read; - uint32_t avail = len - i; //++ + uint32_t avail = len - i; uint32_t take = need64 < avail ? (uint32_t)need64: avail; if (take) { string_append_bytes(&dec->body, buf + i, take); diff --git a/kernel/networking/application_layer/socket_http_client.hpp b/kernel/networking/application_layer/socket_http_client.hpp index 419e186d..560e9f33 100644 --- a/kernel/networking/application_layer/socket_http_client.hpp +++ b/kernel/networking/application_layer/socket_http_client.hpp @@ -148,7 +148,6 @@ class HTTPClient { if (r <= 0) break; chunk_result = http_chunked_decoder_feed(&dec, tmp, (uint32_t)r, &used); body_last_rx_ms = (uint32_t)get_time(); - //sock->close(); } if (chunk_result != HTTP_PARSE_OK) { diff --git a/kernel/networking/application_layer/socket_http_server.hpp b/kernel/networking/application_layer/socket_http_server.hpp index a36da143..f4b033d6 100644 --- a/kernel/networking/application_layer/socket_http_server.hpp +++ b/kernel/networking/application_layer/socket_http_server.hpp @@ -9,6 +9,10 @@ struct HTTPConnection { TCPSocket* client; string carry_buf; + uint32_t request_count; + HTTPMethod current_method; + bool close_after_response; + bool send_keep_alive_header; }; class HTTPServer { @@ -84,6 +88,10 @@ class HTTPServer { conn->client = c; conn->carry_buf = string{nullptr, 0, 0}; + conn->request_count = 0; + conn->current_method = HTTP_METHOD_GET; + conn->close_after_response = true; + conn->send_keep_alive_header = false; if (c) { netlog_socket_event_t ev{}; @@ -217,6 +225,19 @@ class HTTPServer { } else if (policy.require_host_http11 && line.version == HTTP_VERSION_11 && !req.headers_common.fields.host.length) { bad_request = true; reject_status = HTTP_BAD_REQUEST; + } else { + conn->request_count++; + conn->current_method = req.method; + conn->close_after_response = true; + conn->send_keep_alive_header = false; + if (policy.allow_keep_alive) { + if (line.version == HTTP_VERSION_11) conn->close_after_response = req.headers_common.framing.connection_close != 0; + else if (line.version == HTTP_VERSION_10 && req.headers_common.framing.connection_keep_alive) { + conn->close_after_response = false; + conn->send_keep_alive_header = true; + } + } + if (policy.max_keepalive_requests && conn->request_count >= policy.max_keepalive_requests) conn->close_after_response = true; } } @@ -381,9 +402,31 @@ class HTTPServer { if (!conn || !conn->client) return SOCK_ERR_STATE; TCPSocket* client = conn->client; - bool send_chunked = res.headers_common.framing.chunked; + uint32_t code = (uint32_t)res.status_code; + bool informational = code >= 100 && code < 200; + bool suppress_body = informational || conn->current_method == HTTP_METHOD_HEAD || code == 204 || code == 304; + bool send_chunked = suppress_body ? false : res.headers_common.framing.chunked; HTTPResponseMsg head = res; - uint32_t body_len = (!send_chunked && res.body.ptr && res.body.size) ? (uint32_t)res.body.size : 0; + bool explicit_close = http_header_value_has_token(res.headers_common.fields.connection.data, res.headers_common.fields.connection.length, "close", 5); + bool explicit_keep_alive = http_header_value_has_token(res.headers_common.fields.connection.data, res.headers_common.fields.connection.length, "keep-alive", 10); + bool close_after_send = !informational && (conn->close_after_response || explicit_close); + static char conn_close_data[] = "close"; + static char conn_keep_alive_data[] = "keep-alive"; + const string conn_close = {conn_close_data, sizeof(conn_close_data) - 1, 0 }; + const string conn_keep_alive = {conn_keep_alive_data, sizeof(conn_keep_alive_data) - 1, 0 }; + + if (!informational) { + if (close_after_send) head.headers_common.fields.connection = conn_close; + else if (!res.headers_common.fields.connection.length && conn->send_keep_alive_header) head.headers_common.fields.connection = conn_keep_alive; + else if (explicit_keep_alive) head.headers_common.fields.connection = res.headers_common.fields.connection; + } + + if (suppress_body) { + head.body = sizedptr{}; + head.headers_common.framing.chunked = 0; + } + + uint32_t body_len = (!send_chunked && !suppress_body && res.body.ptr && res.body.size) ? (uint32_t)res.body.size : 0; if (!send_chunked) { head.body = sizedptr{}; if (body_len && !head.headers_common.framing.has_content_length) { @@ -391,7 +434,6 @@ class HTTPServer { head.headers_common.framing.has_content_length = 1; } } - uint32_t code = (uint32_t)res.status_code; string out = http_response_builder(&head); uint32_t out_len = out.length + body_len; int64_t sent = 0; @@ -483,6 +525,7 @@ class HTTPServer { netlog_socket_event(&log_opts, &ev); string_free(out); + if (sent >= 0 && close_after_send) client->close(); return sent < 0 ? (int32_t)sent : SOCK_OK; } diff --git a/kernel/networking/application_layer/ssdp_daemon.c b/kernel/networking/application_layer/ssdp_daemon.c index e019e177..56a6d90b 100644 --- a/kernel/networking/application_layer/ssdp_daemon.c +++ b/kernel/networking/application_layer/ssdp_daemon.c @@ -31,6 +31,7 @@ static uint8_t ssdp_host_v6[16]; #define SSDP_RATE_WINDOW_MS 1000 #define SSDP_RATE_MAX 20 #define SSDP_NOTIFY_INTERVAL_MS 300000 +#define SSDP_RECV_BURST 8 static ssdp_pending_t ssdp_pending[SSDP_MAX_PENDING]; static uint32_t ssdp_rate_window_ms = 0; @@ -77,34 +78,28 @@ int ssdp_daemon_entry(int argc, char* argv[]) { ipv6_make_multicast(0x02, IPV6_MCAST_SSDP, NULL, ssdp_host_v6); - SocketExtraOptions opt4 = (SocketExtraOptions){0}; - opt4.flags = SOCK_OPT_MCAST_JOIN; - opt4.mcast_ver = IP_VER4; - memcpy(opt4.mcast_group, &ssdp_host_v4, 4); - - SocketExtraOptions opt6 = (SocketExtraOptions){0}; - opt6.flags = SOCK_OPT_MCAST_JOIN; - opt6.mcast_ver = IP_VER6; - memcpy(opt6.mcast_group, ssdp_host_v6, 16); - - uint16_t pid = get_current_proc_pid(); - - socket_handle_t s4 = udp_socket_create(SOCK_ROLE_SERVER, pid, &opt4); - socket_handle_t s6 = udp_socket_create(SOCK_ROLE_SERVER, pid, &opt6); + SocketExtraOptions opt = (SocketExtraOptions){0}; + opt.flags = SOCK_OPT_MCAST_JOIN; + opt.mcast_count = 2; + opt.mcast_groups[0].ver = IP_VER4; + opt.mcast_groups[0].port = 1900; + memcpy(opt.mcast_groups[0].ip, &ssdp_host_v4, 4); + opt.mcast_groups[1].ver = IP_VER6; + opt.mcast_groups[1].port = 1900; + memcpy(opt.mcast_groups[1].ip, ssdp_host_v6, 16); + socket_handle_t s = udp_socket_create(SOCK_ROLE_SERVER, get_current_proc_pid(), &opt); struct SockBindSpec spec = (struct SockBindSpec){0}; spec.kind = BIND_ANY; - if (s4 && socket_bind_udp(s4, &spec, 1900) < 0) { - socket_close_udp(s4); - socket_destroy_udp(s4); - s4 = 0; - } - if (s6 && socket_bind_udp(s6, &spec, 1900) < 0) { - socket_close_udp(s6); - socket_destroy_udp(s6); - s6 = 0; + if (s && socket_bind_udp(s, &spec, 1900) < 0) { + socket_close_udp(s); + socket_destroy_udp(s); + s = 0; } + socket_handle_t s4 = s; + socket_handle_t s6 = s; + if (!s4 && !s6) return 1; ssdp_send_notify(s4, s6, true); @@ -126,19 +121,12 @@ int ssdp_daemon_entry(int argc, char* argv[]) { char buf[2048]; net_l4_endpoint src = (net_l4_endpoint){0}; - if (s4) { - int64_t r4 = socket_recvfrom_udp(s4, buf, sizeof(buf) - 1, &src); - if (r4 > 0) { - buf[r4] = 0; - if (ssdp_is_msearch(buf, (int)r4)) ssdp_schedule_response(&src, ssdp_parse_mx_ms(buf, (int)r4)); - } - } - - if (s6) { - int64_t r6 = socket_recvfrom_udp(s6, buf, sizeof(buf) - 1, &src); - if (r6 > 0) { - buf[r6] = 0; - if (ssdp_is_msearch(buf, (int)r6)) ssdp_schedule_response(&src, ssdp_parse_mx_ms(buf, (int)r6)); + if (s) { + for (int i = 0; i < SSDP_RECV_BURST; ++i) { + int64_t r = socket_recvfrom_udp(s, buf, sizeof(buf) - 1, &src); + if (r <= 0) break; + buf[r] = 0; + if (ssdp_is_msearch(buf, (int)r)) ssdp_schedule_response(&src, ssdp_parse_mx_ms(buf, (int)r)); } } diff --git a/kernel/networking/interface_manager.c b/kernel/networking/interface_manager.c index 3858c095..ff61c1a8 100644 --- a/kernel/networking/interface_manager.c +++ b/kernel/networking/interface_manager.c @@ -212,9 +212,15 @@ 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; + int idx = find_ipv4_group_index(itf, group); + if (idx >= 0) { + if (itf->ipv4_mcast_ref[idx] < 0xFFFFu) itf->ipv4_mcast_ref[idx] += 1; + return true; + } if (itf->ipv4_mcast_count >= MAX_IPV4_MCAST_PER_INTERFACE) return false; - itf->ipv4_mcast[itf->ipv4_mcast_count++] = group; + itf->ipv4_mcast[itf->ipv4_mcast_count] = group; + itf->ipv4_mcast_ref[itf->ipv4_mcast_count] = 1; + itf->ipv4_mcast_count += 1; if (itf->kind != NET_IFK_LOCALHOST) (void)l2_sync_multicast_filters(itf); if (itf->kind != NET_IFK_LOCALHOST && l2_has_active_v4(itf)) (void)igmp_send_join(ifindex, group); return true; @@ -225,8 +231,16 @@ bool l2_ipv4_mcast_leave(uint8_t ifindex, uint32_t group) { 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_ref[idx] > 1) { + itf->ipv4_mcast_ref[idx] -= 1; + return true; + } + for (int i = idx + 1; i < (int)itf->ipv4_mcast_count; ++i) { + itf->ipv4_mcast[i-1] = itf->ipv4_mcast[i]; + itf->ipv4_mcast_ref[i-1] = itf->ipv4_mcast_ref[i]; + } if (itf->ipv4_mcast_count) itf->ipv4_mcast_count -= 1; + if (itf->ipv4_mcast_count < MAX_IPV4_MCAST_PER_INTERFACE) itf->ipv4_mcast_ref[itf->ipv4_mcast_count] = 0; if (itf->kind != NET_IFK_LOCALHOST) (void)l2_sync_multicast_filters(itf); if (itf->kind != NET_IFK_LOCALHOST && l2_has_active_v4(itf)) (void)igmp_send_leave(ifindex, group); return true; @@ -240,9 +254,14 @@ 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; + int idx = find_ipv6_group_index(itf, group); + if (idx >= 0) { + if (itf->ipv6_mcast_ref[idx] < 0xFFFFu) itf->ipv6_mcast_ref[idx] += 1; + return true; + } if (itf->ipv6_mcast_count >= MAX_IPV6_MCAST_PER_INTERFACE) return false; ipv6_cpy(itf->ipv6_mcast[itf->ipv6_mcast_count], group); + itf->ipv6_mcast_ref[itf->ipv6_mcast_count] = 1; itf->ipv6_mcast_count += 1; if (itf->kind != NET_IFK_LOCALHOST) (void)l2_sync_multicast_filters(itf); if (itf->kind != NET_IFK_LOCALHOST && l2_has_active_v6(itf)) (void)mld_send_join(ifindex, group); @@ -253,9 +272,17 @@ bool l2_ipv6_mcast_leave(uint8_t ifindex, const uint8_t group[16]) { if (!itf || !group) return false; int idx = find_ipv6_group_index(itf, group); if (idx < 0) return true; + if (itf->ipv6_mcast_ref[idx] > 1) { + itf->ipv6_mcast_ref[idx] -= 1; + return true; + } if (itf->kind != NET_IFK_LOCALHOST && l2_has_active_v6(itf)) (void)mld_send_leave(ifindex, group); - for (int i = idx + 1; i < (int)itf->ipv6_mcast_count; ++i) ipv6_cpy(itf->ipv6_mcast[i-1], itf->ipv6_mcast[i]); + for (int i = idx + 1; i < (int)itf->ipv6_mcast_count; ++i) { + ipv6_cpy(itf->ipv6_mcast[i-1], itf->ipv6_mcast[i]); + itf->ipv6_mcast_ref[i-1] = itf->ipv6_mcast_ref[i]; + } if (itf->ipv6_mcast_count) itf->ipv6_mcast_count -= 1; + if (itf->ipv6_mcast_count < MAX_IPV6_MCAST_PER_INTERFACE) itf->ipv6_mcast_ref[itf->ipv6_mcast_count] = 0; if (itf->kind != NET_IFK_LOCALHOST) (void)l2_sync_multicast_filters(itf); return true; } @@ -391,6 +418,7 @@ uint8_t l3_ipv4_add_to_interface(uint8_t ifindex, uint32_t ip, uint32_t mask, ui port_manager_init(n->port_manager); if (n->mode != IPV4_CFG_DISABLED && n->ip && l2->kind != NET_IFK_LOCALHOST) (void)l2_ipv4_mcast_join(ifindex, IPV4_MCAST_ALL_HOSTS); + if (l2->kind != NET_IFK_LOCALHOST && l2_has_active_v4(l2)) for (int i = 0; i < (int)l2->ipv4_mcast_count; ++i) igmp_send_join(l2->ifindex, l2->ipv4_mcast[i]); return n->l3_id; } @@ -451,6 +479,8 @@ bool l3_ipv4_update(uint8_t l3_id, uint32_t ip, uint32_t mask, uint32_t gw, ipv4 n->routing_table = NULL; } } + if (l2->kind != NET_IFK_LOCALHOST && l2_has_active_v4(l2)) for (int i = 0; i < (int)l2->ipv4_mcast_count; ++i) igmp_send_join(l2->ifindex, l2->ipv4_mcast[i]); + return true; } @@ -662,6 +692,7 @@ uint8_t l3_ipv6_add_to_interface(uint8_t ifindex, const uint8_t ip[16], uint8_t (void)l2_ipv6_mcast_join(ifindex, m); } } + if (l2->kind != NET_IFK_LOCALHOST && l2_has_active_v6(l2)) for (int i = 0; i < (int)l2->ipv6_mcast_count; ++i) mld_send_join(l2->ifindex, l2->ipv6_mcast[i]); return n->l3_id; } @@ -806,6 +837,7 @@ bool l3_ipv6_update(uint8_t l3_id, const uint8_t ip[16], uint8_t prefix_len, con n->routing_table = NULL; } } + if (l2->kind != NET_IFK_LOCALHOST && l2_has_active_v6(l2)) for (int i = 0; i < (int)l2->ipv6_mcast_count; ++i) mld_send_join(l2->ifindex, l2->ipv6_mcast[i]); return true; } diff --git a/kernel/networking/interface_manager.h b/kernel/networking/interface_manager.h index 7786b16d..3f1485f9 100644 --- a/kernel/networking/interface_manager.h +++ b/kernel/networking/interface_manager.h @@ -7,11 +7,11 @@ extern "C" { #endif -#define MAX_L2_INTERFACES 15 +#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 +#define MAX_IPV4_MCAST_PER_INTERFACE 16 +#define MAX_IPV6_MCAST_PER_INTERFACE 32 typedef enum { IPV4_CFG_DISABLED = -1, @@ -68,8 +68,10 @@ typedef struct l2_interface { uint8_t ipv4_count; uint8_t ipv6_count; uint32_t ipv4_mcast[MAX_IPV4_MCAST_PER_INTERFACE]; + uint16_t ipv4_mcast_ref[MAX_IPV4_MCAST_PER_INTERFACE]; uint8_t ipv4_mcast_count; uint8_t ipv6_mcast[MAX_IPV6_MCAST_PER_INTERFACE][16]; + uint16_t ipv6_mcast_ref[MAX_IPV6_MCAST_PER_INTERFACE]; uint8_t ipv6_mcast_count; } l2_interface_t; diff --git a/kernel/networking/internet_layer/ipv4_route.c b/kernel/networking/internet_layer/ipv4_route.c index da806b72..887aaa7d 100644 --- a/kernel/networking/internet_layer/ipv4_route.c +++ b/kernel/networking/internet_layer/ipv4_route.c @@ -1,13 +1,13 @@ #include "ipv4_route.h" #include "std/memory.h" #include "networking/interface_manager.h" +#include "networking/internet_layer/ipv4_utils.h" #include "syscalls/syscalls.h" static bool v4_l3_ok_for_tx(l3_ipv4_interface_t* v4){ if (!v4 || !v4->l2) return false; if (!v4->l2->is_up) return false; if (v4->mode == IPV4_CFG_DISABLED) return false; - if (v4->is_localhost) return false; if (!v4->ip) return false; if (!v4->port_manager) return false; return true; @@ -30,7 +30,7 @@ bool ipv4_build_tx_plan(uint32_t dst, const ip_tx_opts_t* hint, const uint8_t* a uint8_t id = hint->index; if (!l3_allowed(id, allowed_l3, allowed_n)) return false; l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(id); - if (!v4_l3_ok_for_tx(v4)) return false; + if (!v4_l3_ok_for_tx(v4) || ipv4_is_loopback(dst) != v4->is_localhost) return false; out->l3_id = id; out->src_ip = v4->ip; out->fixed_opts.scope = IP_TX_BOUND_L3; @@ -46,7 +46,7 @@ bool ipv4_build_tx_plan(uint32_t dst, const ip_tx_opts_t* hint, const uint8_t* a if (!l2 || !l2->is_up) return false; for (int s = 0; s < MAX_IPV4_PER_INTERFACE && n < (int)sizeof(cand); ++s){ l3_ipv4_interface_t* v4 = l2->l3_v4[s]; - if (!v4_l3_ok_for_tx(v4)) continue; + if (!v4_l3_ok_for_tx(v4) || ipv4_is_loopback(dst) != v4->is_localhost) continue; if (!l3_allowed(v4->l3_id, allowed_l3, allowed_n)) continue; cand[n++] = v4->l3_id; } @@ -57,7 +57,7 @@ bool ipv4_build_tx_plan(uint32_t dst, const ip_tx_opts_t* hint, const uint8_t* a if (!l2 || !l2->is_up) continue; for (int s = 0; s < MAX_IPV4_PER_INTERFACE && n < (int)sizeof(cand); ++s){ l3_ipv4_interface_t* v4 = l2->l3_v4[s]; - if (!v4_l3_ok_for_tx(v4)) continue; + if (!v4_l3_ok_for_tx(v4) || ipv4_is_loopback(dst) != v4->is_localhost) continue; if (!l3_allowed(v4->l3_id, allowed_l3, allowed_n)) continue; cand[n++] = v4->l3_id; } @@ -70,7 +70,7 @@ bool ipv4_build_tx_plan(uint32_t dst, const ip_tx_opts_t* hint, const uint8_t* a if (!ipv4_rt_pick_best_l3_in(cand, n, dst, &chosen)) chosen = cand[0]; l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(chosen); - if (!v4_l3_ok_for_tx(v4)) return false; + if (!v4_l3_ok_for_tx(v4) || ipv4_is_loopback(dst) != v4->is_localhost) return false; out->l3_id = chosen; out->src_ip = v4->ip; diff --git a/kernel/networking/internet_layer/ipv6.c b/kernel/networking/internet_layer/ipv6.c index 858da9f4..102ef3bf 100644 --- a/kernel/networking/internet_layer/ipv6.c +++ b/kernel/networking/internet_layer/ipv6.c @@ -909,7 +909,7 @@ void ipv6_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { for (int i = 0; i < ccount; i++) { l3_ipv6_interface_t* v6 = cand[i]; - if (!ipv6_is_linklocal(v6->ip) && ipv6_is_linklocal(ip6->dst)) continue; + if (!ipv6_is_linklocal(v6->ip) && ipv6_is_linkscope_mcast(ip6->dst)) continue; netpkt_t* l4pkt = netpkt_view(reassembled, payload_off, payload_size); if (!l4pkt) continue; if (inner_nh == 17) udp_input(IP_VER6, ip6->src, ip6->dst, v6->l3_id, l4pkt); @@ -975,7 +975,7 @@ void ipv6_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { for (int i = 0; i < ccount; i++) { l3_ipv6_interface_t* v6 = cand[i]; - if (!ipv6_is_linklocal(v6->ip) && ipv6_is_linklocal(ip6->dst)) + if (!ipv6_is_linklocal(v6->ip) && ipv6_is_linkscope_mcast(ip6->dst)) continue; switch (nh) { diff --git a/kernel/networking/net_logger/net_logger.c b/kernel/networking/net_logger/net_logger.c index 45514aff..d875a444 100644 --- a/kernel/networking/net_logger/net_logger.c +++ b/kernel/networking/net_logger/net_logger.c @@ -39,6 +39,8 @@ static const char* bind_kind_str(SockBindKind k) { case BIND_L2: return "L2"; case BIND_IP: return "IP"; case BIND_ANY: return "ANY"; + case BIND_ANY4: return "ANY4"; + case BIND_ANY6: return "ANY6"; default: return ""; } } diff --git a/kernel/networking/transport_layer/socket_tcp.hpp b/kernel/networking/transport_layer/socket_tcp.hpp index dbe6d3dc..415cdac2 100644 --- a/kernel/networking/transport_layer/socket_tcp.hpp +++ b/kernel/networking/transport_layer/socket_tcp.hpp @@ -9,6 +9,7 @@ #include "types.h" #include "net/socket_types.h" #include "networking/internet_layer/ipv4_route.h" +#include "networking/internet_layer/ipv4_utils.h" #include "networking/internet_layer/ipv6_route.h" #include "networking/internet_layer/ipv6_utils.h" #include "networking/transport_layer/trans_utils.h" @@ -28,12 +29,10 @@ class TCPSocket : public Socket { int backlogCap = 0; int backlogLen = 0; TCPSocket* next = nullptr; - int idleAcceptSkips = 0; static bool is_valid_v4_l3_for_bind(l3_ipv4_interface_t* v4) { if (!v4 || !v4->l2) 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; @@ -226,57 +225,65 @@ class TCPSocket : public Socket { (void)tcp_unbind_l3(l3_id, port, pid); } - bool add_all_l3_on_l2(uint8_t ifindex, uint8_t* tmp_ids, ip_version_t* tmp_ver, int& n) { + bool add_all_l3_on_l2(uint8_t ifindex, ip_version_t ver, uint8_t* tmp_ids, ip_version_t* tmp_ver, int& n) { l2_interface_t* l2 = l2_interface_find_by_index(ifindex); if (!l2 || !l2->is_up) return false; - for (int s = 0; s < MAX_IPV4_PER_INTERFACE; ++s) { - l3_ipv4_interface_t* v4 = l2->l3_v4[s]; - if (!is_valid_v4_l3_for_bind(v4)) continue; - if (n < SOCK_MAX_L3) { - tmp_ids[n] = v4->l3_id; - tmp_ver[n] = IP_VER4; - ++n; + if ((ver == 0 || ver == IP_VER4)) { + for (int s = 0; s < MAX_IPV4_PER_INTERFACE; ++s) { + l3_ipv4_interface_t* v4 = l2->l3_v4[s]; + if (!is_valid_v4_l3_for_bind(v4)) continue; + if (n < SOCK_MAX_L3) { + tmp_ids[n] = v4->l3_id; + tmp_ver[n] = IP_VER4; + ++n; + } } } - for (int s = 0; s < MAX_IPV6_PER_INTERFACE; ++s) { - l3_ipv6_interface_t* v6 = l2->l3_v6[s]; - if (!is_valid_v6_l3_for_bind(v6)) continue; - if (n < SOCK_MAX_L3) { - tmp_ids[n] = v6->l3_id; - tmp_ver[n] = IP_VER6; - ++n; + if ((ver == 0 || ver == IP_VER6)) { + for (int s = 0; s < MAX_IPV6_PER_INTERFACE; ++s) { + l3_ipv6_interface_t* v6 = l2->l3_v6[s]; + if (!is_valid_v6_l3_for_bind(v6)) continue; + if (n < SOCK_MAX_L3) { + tmp_ids[n] = v6->l3_id; + tmp_ver[n] = IP_VER6; + ++n; + } } } return n > 0; } - bool add_all_l3_any(uint8_t* tmp_ids, ip_version_t* tmp_ver, int& n) { + bool add_all_l3_any(ip_version_t ver, uint8_t* tmp_ids, ip_version_t* tmp_ver, 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 || !l2->is_up) continue; - for (int s = 0; s < MAX_IPV4_PER_INTERFACE; ++s) { - l3_ipv4_interface_t* v4 = l2->l3_v4[s]; - if (!is_valid_v4_l3_for_bind(v4)) continue; - if (n < SOCK_MAX_L3) { - tmp_ids[n] = v4->l3_id; - tmp_ver[n] = IP_VER4; - ++n; + if ((ver == 0 || ver == IP_VER4)) { + for (int s = 0; s < MAX_IPV4_PER_INTERFACE; ++s) { + l3_ipv4_interface_t* v4 = l2->l3_v4[s]; + if (!is_valid_v4_l3_for_bind(v4)) continue; + if (n < SOCK_MAX_L3) { + tmp_ids[n] = v4->l3_id; + tmp_ver[n] = IP_VER4; + ++n; + } } } - for (int s = 0; s < MAX_IPV6_PER_INTERFACE; ++s) { - l3_ipv6_interface_t* v6 = l2->l3_v6[s]; - if (!is_valid_v6_l3_for_bind(v6)) continue; - if (n < SOCK_MAX_L3) { - tmp_ids[n] = v6->l3_id; - tmp_ver[n] = IP_VER6; - ++n; + if ((ver == 0 || ver == IP_VER6)) { + for (int s = 0; s < MAX_IPV6_PER_INTERFACE; ++s) { + l3_ipv6_interface_t* v6 = l2->l3_v6[s]; + if (!is_valid_v6_l3_for_bind(v6)) continue; + if (n < SOCK_MAX_L3) { + tmp_ids[n] = v6->l3_id; + tmp_ver[n] = IP_VER6; + ++n; + } } } } @@ -338,8 +345,8 @@ class TCPSocket : public Socket { l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(spec.l3_id); l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(spec.l3_id); - bool ok4 = is_valid_v4_l3_for_bind(v4); - bool ok6 = is_valid_v6_l3_for_bind(v6); + bool ok4 = (spec.ver == 0 || spec.ver == IP_VER4) && is_valid_v4_l3_for_bind(v4); + bool ok6 = (spec.ver == 0 || spec.ver == IP_VER6) && is_valid_v6_l3_for_bind(v6); if (!ok4 && !ok6) return SOCK_ERR_INVAL; @@ -354,25 +361,36 @@ class TCPSocket : public Socket { ++n; } } else if (spec.kind == BIND_L2){ - if (!add_all_l3_on_l2(spec.ifindex, ids, vers, n)) return SOCK_ERR_INVAL; + if (!add_all_l3_on_l2(spec.ifindex, spec.ver, ids, vers, n)) return SOCK_ERR_INVAL; } else if (spec.kind == BIND_IP){ if (spec.ver == IP_VER4){ uint32_t v4ip = 0; memcpy(&v4ip, spec.ip, 4); - l3_ipv4_interface_t* ipif = l3_ipv4_find_by_ip(v4ip); - if (!is_valid_v4_l3_for_bind(ipif)) return SOCK_ERR_INVAL; - ids[n] = ipif->l3_id; - vers[n] = IP_VER4; - ++n; + if (ipv4_is_unspecified(v4ip)) { + if (!add_all_l3_any(IP_VER4, ids, vers, n)) return SOCK_ERR_INVAL; + } else { + l3_ipv4_interface_t* ipif = l3_ipv4_find_by_ip(v4ip); + if (!is_valid_v4_l3_for_bind(ipif)) return SOCK_ERR_INVAL; + ids[n] = ipif->l3_id; + vers[n] = IP_VER4; + ++n; + } } else if (spec.ver == IP_VER6) { - l3_ipv6_interface_t* ipif = l3_ipv6_find_by_ip(spec.ip); - if (!is_valid_v6_l3_for_bind(ipif)) return SOCK_ERR_INVAL; - ids[n] = ipif->l3_id; - vers[n] = IP_VER6; - ++n; + if (ipv6_is_unspecified(spec.ip)) { + if (!add_all_l3_any(IP_VER6, ids, vers, n)) return SOCK_ERR_INVAL; + } else { + l3_ipv6_interface_t* ipif = l3_ipv6_find_by_ip(spec.ip); + if (!is_valid_v6_l3_for_bind(ipif)) return SOCK_ERR_INVAL; + ids[n] = ipif->l3_id; + vers[n] = IP_VER6; + ++n; + } } else return SOCK_ERR_INVAL; - } else if (spec.kind == BIND_ANY){ - if (!add_all_l3_any(ids, vers, n)) return SOCK_ERR_INVAL; + } else if (spec.kind == BIND_ANY || spec.kind == BIND_ANY4 || spec.kind == BIND_ANY6){ + ip_version_t ver = spec.ver; + if (spec.kind == BIND_ANY4) ver = IP_VER4; + else if (spec.kind == BIND_ANY6) ver = IP_VER6; + if (!add_all_l3_any(ver, ids, vers, n)) return SOCK_ERR_INVAL; } else return SOCK_ERR_INVAL; if (n==0) return SOCK_ERR_INVAL; @@ -434,36 +452,13 @@ class TCPSocket : public Socket { TCPSocket* accept(){ const int max_empty_iters = 200; const int ready_wait_iters = 4; - const int max_idle_skips = 20; int iter = 0; while (backlogLen == 0){ - idleAcceptSkips = 0; if (++iter > max_empty_iters) return nullptr; msleep(5); } - for (int i = 0; i < backlogLen;) { - TCPSocket* client = pending[i]; - - if (client && client->flow.flow_generation) { - bool readable = tcp_flow_readable(&client->flow) != 0; - bool closed = tcp_flow_recv_closed(&client->flow); - - if (!closed || readable) { - i++; - continue; - } - } - - client = pop_pending_at(i); - if (client) delete client; - } - - if (backlogLen == 0) return nullptr; - - //TODO temporary http workaround - //without socket events accept() can pick an idle preconnect and block in recv() while a later ipv4/6 request is already ready iter = 0; while (1) { for (int i = 0; i < backlogLen;) { @@ -487,16 +482,13 @@ class TCPSocket : public Socket { for (int i = 0; i < backlogLen; ++i) { TCPSocket* client = pending[i]; if (!client) continue; - if (!client->flow.flow_generation || tcp_flow_readable(&client->flow) || tcp_flow_recv_closed(&client->flow)) { - idleAcceptSkips = 0; - return pop_pending_at(i); - } + if (!client->flow.flow_generation || tcp_flow_readable(&client->flow) || tcp_flow_recv_closed(&client->flow)) return pop_pending_at(i); } if (++iter > ready_wait_iters) { - if (backlogLen >= backlogCap || ++idleAcceptSkips > max_idle_skips) { - idleAcceptSkips = 0; - return pop_pending_at(0); + if (backlogLen >= backlogCap) { + TCPSocket* client = pop_pending_at(0); + if (client) delete client; } return nullptr; } diff --git a/kernel/networking/transport_layer/socket_udp.hpp b/kernel/networking/transport_layer/socket_udp.hpp index 17b92de5..c9df2736 100644 --- a/kernel/networking/transport_layer/socket_udp.hpp +++ b/kernel/networking/transport_layer/socket_udp.hpp @@ -12,7 +12,6 @@ #include "networking/internet_layer/ipv6_utils.h" #include "networking/transport_layer/trans_utils.h" #include "networking/internet_layer/ipv6.h" -#include "networking/internet_layer/igmp.h" #include "exceptions/irq.h" #include "sysregs.h" @@ -50,6 +49,31 @@ class UDPSocket : public Socket { return true; } + static bool udp_mcast_match(UDPSocket* s, ip_version_t ver, const void* dst_ip_addr) { + if (!s || !dst_ip_addr) return false; + if ((s->extraOpts.flags & SOCK_OPT_MCAST_JOIN) == 0) return false; + + uint32_t count = s->extraOpts.mcast_count; + if (count > SOCK_MAX_MCAST_GROUPS) count = SOCK_MAX_MCAST_GROUPS; + + for (uint32_t i = 0; i < count; ++i) { + const net_l4_endpoint* group = &s->extraOpts.mcast_groups[i]; + if (group->ver != ver) continue; + + if (ver == IP_VER4) { + uint32_t want = 0; + uint32_t got = 0; + memcpy(&want, group->ip, 4); + memcpy(&got, dst_ip_addr, 4); + if (want == got) return true; + } else if (ver == IP_VER6) { + if (memcmp(group->ip, dst_ip_addr, 16) == 0) return true; + } + } + + return false; + } + 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) { @@ -80,6 +104,7 @@ class UDPSocket : public Socket { uint32_t dip = *(const uint32_t*)dst_ip_addr; bool lb = dip == 0xFFFFFFFFu; bool mc = ipv4_is_multicast(dip); + if (mc && !udp_mcast_match(s, IP_VER4, dst_ip_addr)) return false; for (int i = 0; i < s->bound_l3_count; ++i) { uint8_t id = s->bound_l3[i]; @@ -101,12 +126,14 @@ class UDPSocket : public Socket { if (ver == IP_VER6) { bool mcast = ipv6_is_multicast((const uint8_t*)dst_ip_addr); + if (mcast && !udp_mcast_match(s, IP_VER6, dst_ip_addr)) return false; 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 (!is_valid_v6_l3_for_bind(v6)) continue; if (v6->l2->ifindex != ifx) continue; + if (mcast && ipv6_is_linkscope_mcast((const uint8_t*)dst_ip_addr) && !ipv6_is_linklocal(v6->ip)) continue; if (mcast) return true; if (memcmp(v6->ip, dst_ip_addr, 16) == 0) return true; @@ -191,46 +218,98 @@ class UDPSocket : public Socket { next = nullptr; } - bool add_all_l3_on_l2(uint8_t ifindex, uint8_t* tmp_ids, int& n) { + bool add_all_l3_on_l2(uint8_t ifindex, ip_version_t ver, uint8_t* tmp_ids, int& n) { l2_interface_t* l2 = l2_interface_find_by_index(ifindex); if (!l2) return false; if (!l2->is_up) return false; - for (int s = 0; s < MAX_IPV4_PER_INTERFACE; ++s) { - l3_ipv4_interface_t* v4 = l2->l3_v4[s]; - if (!is_valid_v4_l3_for_bind(v4)) continue; - if (n < SOCK_MAX_L3) tmp_ids[n++] = v4->l3_id; + if ((ver == 0 || ver == IP_VER4)) { + for (int s = 0; s < MAX_IPV4_PER_INTERFACE; ++s) { + l3_ipv4_interface_t* v4 = l2->l3_v4[s]; + if (!is_valid_v4_l3_for_bind(v4)) continue; + 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 (!is_valid_v6_l3_for_bind(v6)) continue; - if (n < SOCK_MAX_L3) tmp_ids[n++] = v6->l3_id; + if ((ver == 0 || ver == IP_VER6)) { + for (int s = 0; s < MAX_IPV6_PER_INTERFACE; ++s) { + l3_ipv6_interface_t* v6 = l2->l3_v6[s]; + if (!is_valid_v6_l3_for_bind(v6)) continue; + 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) { + bool add_all_l3_any(ip_version_t ver, 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; if (!l2->is_up) continue; + if ((ver == 0 || ver == IP_VER4)) { + for (int s = 0; s < MAX_IPV4_PER_INTERFACE; ++s) { + l3_ipv4_interface_t* v4 = l2->l3_v4[s]; + if (!is_valid_v4_l3_for_bind(v4)) continue; + if (n < SOCK_MAX_L3) tmp_ids[n++] = v4->l3_id; + } + } + + if ((ver == 0 || ver == IP_VER6)) { + for (int s = 0; s < MAX_IPV6_PER_INTERFACE; ++s) { + l3_ipv6_interface_t* v6 = l2->l3_v6[s]; + if (!is_valid_v6_l3_for_bind(v6)) continue; + if (n < SOCK_MAX_L3) tmp_ids[n++] = v6->l3_id; + } + } + } + return n > 0; + } + + bool add_l3_for_mcast_group_on_l2(l2_interface_t* l2, const net_l4_endpoint* group, uint8_t* tmp_ids, int& n) { + if (!l2) return false; + if (!l2->is_up) return false; + if (!group) return false; + + bool added = false; + if (group->ver == IP_VER4) { + uint32_t g = 0; + memcpy(&g, group->ip, 4); + if (!ipv4_is_multicast(g)) return false; + for (int s = 0; s < MAX_IPV4_PER_INTERFACE; ++s) { l3_ipv4_interface_t* v4 = l2->l3_v4[s]; if (!is_valid_v4_l3_for_bind(v4)) continue; if (n < SOCK_MAX_L3) tmp_ids[n++] = v4->l3_id; + added = true; } + } else if (group->ver == IP_VER6) { + if (!ipv6_is_multicast(group->ip)) return false; + bool linkscope = ipv6_is_linkscope_mcast(group->ip); for (int s = 0; s < MAX_IPV6_PER_INTERFACE; ++s) { l3_ipv6_interface_t* v6 = l2->l3_v6[s]; if (!is_valid_v6_l3_for_bind(v6)) continue; + if (linkscope && !ipv6_is_linklocal(v6->ip)) continue; if (n < SOCK_MAX_L3) tmp_ids[n++] = v6->l3_id; + added = true; } } - return n > 0; + return added; + } + + bool add_l3_for_mcast_group_any(const net_l4_endpoint* group, uint8_t* tmp_ids, int& n) { + bool added = false; + uint8_t cnt = l2_interface_count(); + + for (uint8_t i = 0; i < cnt; ++i) { + l2_interface_t* l2 = l2_interface_at(i); + if (add_l3_for_mcast_group_on_l2(l2, group, tmp_ids, n)) added = true; + } + + return added; } void do_unbind_one(uint8_t l3_id, uint16_t port, uint16_t pid_) override { @@ -254,6 +333,39 @@ class UDPSocket : public Socket { return true; } + void leave_mcast_groups() { + if ((extraOpts.flags & SOCK_OPT_MCAST_JOIN) == 0) return; + + uint32_t count = extraOpts.mcast_count; + if (count > SOCK_MAX_MCAST_GROUPS) count = SOCK_MAX_MCAST_GROUPS; + + for (uint32_t gidx = 0; gidx < count; ++gidx) { + const net_l4_endpoint* group = &extraOpts.mcast_groups[gidx]; + + if (group->ver == IP_VER4) { + uint32_t g = 0; + memcpy(&g, group->ip, 4); + if (!ipv4_is_multicast(g)) continue; + + for (int i = 0; i < bound_l3_count; ++i) { + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(bound_l3[i]); + if (!is_valid_v4_l3_for_bind(v4)) continue; + l2_ipv4_mcast_leave(v4->l2->ifindex,g); + } + } else if (group->ver == IP_VER6) { + if (!ipv6_is_multicast(group->ip)) continue; + bool linkscope = ipv6_is_linkscope_mcast(group->ip); + + for (int i = 0; i < bound_l3_count; ++i) { + l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(bound_l3[i]); + if (!is_valid_v6_l3_for_bind(v6)) continue; + if (linkscope && !ipv6_is_linklocal(v6->ip)) continue; + l2_ipv6_mcast_leave(v6->l2->ifindex,group->ip); + } + } + } + } + public: UDPSocket(uint8_t r, uint32_t pid_, const SocketExtraOptions* extra = nullptr) : Socket(PROTO_UDP, r, extra) { pid = pid_; @@ -266,29 +378,6 @@ class UDPSocket : public Socket { irq_flags_t irq = irq_save_disable(); //TODO locking is needed asap remove_from_list(); irq_restore(irq); - if ((extraOpts.flags & SOCK_OPT_MCAST_JOIN) && extraOpts.mcast_ver) { - if (extraOpts.mcast_ver == IP_VER4) { - uint32_t g = 0; - memcpy(&g, extraOpts.mcast_group, 4); - if (ipv4_is_multicast(g)) { - for (int i = 0; i < bound_l3_count; ++i) { - l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(bound_l3[i]); - if (!is_valid_v4_l3_for_bind(v4)) continue; - if (!v4->l2) continue; - (void)l2_ipv4_mcast_leave(v4->l2->ifindex, g); - } - } - } else if (extraOpts.mcast_ver == IP_VER6) { - if (ipv6_is_multicast(extraOpts.mcast_group)) { - for (int i = 0; i < bound_l3_count; ++i) { - l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(bound_l3[i]); - if (!is_valid_v6_l3_for_bind(v6)) continue; - if (!v6->l2) continue; - (void)l2_ipv6_mcast_leave(v6->l2->ifindex, extraOpts.mcast_group); - } - } - } - } close(); } @@ -316,31 +405,85 @@ class UDPSocket : public Socket { l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(spec.l3_id); l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(spec.l3_id); - bool ok4 = is_valid_v4_l3_for_bind(v4); - bool ok6 = is_valid_v6_l3_for_bind(v6); + bool ok4 = (spec.ver == 0 || spec.ver == IP_VER4) && is_valid_v4_l3_for_bind(v4); + bool ok6 = (spec.ver == 0 || spec.ver == IP_VER6) && is_valid_v6_l3_for_bind(v6); if (!ok4 && !ok6) return SOCK_ERR_INVAL; if (ok4 && n < SOCK_MAX_L3) ids[n++] = spec.l3_id; if (ok6 && n < SOCK_MAX_L3) 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; + bool used_mcast = false; + if ((extraOpts.flags & SOCK_OPT_MCAST_JOIN) && extraOpts.mcast_count) { + l2_interface_t* l2 = l2_interface_find_by_index(spec.ifindex); + uint32_t count = extraOpts.mcast_count; + if (count > SOCK_MAX_MCAST_GROUPS) count = SOCK_MAX_MCAST_GROUPS; + + for (uint32_t i = 0; i < count; ++i) { + const net_l4_endpoint* group = &extraOpts.mcast_groups[i]; + if (spec.ver && group->ver != spec.ver) continue; + if (add_l3_for_mcast_group_on_l2(l2, group, ids, n)) used_mcast = true; + } + } + if (!used_mcast && !add_all_l3_on_l2(spec.ifindex, spec.ver, ids, n)) return SOCK_ERR_INVAL; } else if (spec.kind == BIND_IP) { if (spec.ver == IP_VER4) { uint32_t v4ip = 0; memcpy(&v4ip, spec.ip, 4); - l3_ipv4_interface_t* ipif = l3_ipv4_find_by_ip(v4ip); - if (!is_valid_v4_l3_for_bind(ipif)) return SOCK_ERR_INVAL; - if (n < SOCK_MAX_L3) ids[n++] = ipif->l3_id; + if (ipv4_is_unspecified(v4ip)) { + if (!add_all_l3_any(IP_VER4, ids, n)) return SOCK_ERR_INVAL; + } else if (ipv4_is_multicast(v4ip)) { + net_l4_endpoint group; + memset(&group, 0, sizeof(group)); + group.ver = IP_VER4; + memcpy(group.ip, &v4ip, 4); + if (spec.ifindex) { + l2_interface_t* l2 = l2_interface_find_by_index(spec.ifindex); + if (!add_l3_for_mcast_group_on_l2(l2, &group, ids, n)) return SOCK_ERR_INVAL; + } else if (!add_l3_for_mcast_group_any(&group, ids, n)) return SOCK_ERR_INVAL; + } else { + l3_ipv4_interface_t* ipif = l3_ipv4_find_by_ip(v4ip); + if (!is_valid_v4_l3_for_bind(ipif)) return SOCK_ERR_INVAL; + if (n < SOCK_MAX_L3) ids[n++] = ipif->l3_id; + } } else if (spec.ver == IP_VER6) { - l3_ipv6_interface_t* ipif6 = l3_ipv6_find_by_ip(spec.ip); - if (!is_valid_v6_l3_for_bind(ipif6)) return SOCK_ERR_INVAL; - if (n < SOCK_MAX_L3) ids[n++] = ipif6->l3_id; + if (ipv6_is_unspecified(spec.ip)) { + if (!add_all_l3_any(IP_VER6, ids, n)) return SOCK_ERR_INVAL; + } else if (ipv6_is_multicast(spec.ip)) { + net_l4_endpoint group; + memset(&group, 0, sizeof(group)); + group.ver = IP_VER6; + memcpy(group.ip, spec.ip, 16); + if (spec.ifindex) { + l2_interface_t* l2 = l2_interface_find_by_index(spec.ifindex); + if (!add_l3_for_mcast_group_on_l2(l2, &group, ids, n)) return SOCK_ERR_INVAL; + } else if (!add_l3_for_mcast_group_any(&group, ids, n)) return SOCK_ERR_INVAL; + } else { + l3_ipv6_interface_t* ipif6 = l3_ipv6_find_by_ip(spec.ip); + if (!is_valid_v6_l3_for_bind(ipif6)) return SOCK_ERR_INVAL; + if (n < SOCK_MAX_L3) ids[n++] = ipif6->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 if (spec.kind == BIND_ANY || spec.kind == BIND_ANY4 || spec.kind == BIND_ANY6) { + ip_version_t ver = spec.ver; + if (spec.kind == BIND_ANY4) ver = IP_VER4; + else if (spec.kind == BIND_ANY6) ver = IP_VER6; + + if ((extraOpts.flags & SOCK_OPT_MCAST_JOIN) && extraOpts.mcast_count) { + bool used_mcast = false; + uint32_t count = extraOpts.mcast_count; + if (count > SOCK_MAX_MCAST_GROUPS) count = SOCK_MAX_MCAST_GROUPS; + + for (uint32_t i = 0; i < count; ++i) { + const net_l4_endpoint* group = &extraOpts.mcast_groups[i]; + if (ver && group->ver != ver) continue; + if (add_l3_for_mcast_group_any(group, ids, n)) used_mcast = true; + } + + if (!used_mcast && !add_all_l3_any(ver, ids, n)) return SOCK_ERR_INVAL; + } else if (!add_all_l3_any(ver, ids, n)) return SOCK_ERR_INVAL; } else { return SOCK_ERR_INVAL; } @@ -375,27 +518,50 @@ class UDPSocket : public Socket { clear_bound_l3(); for (int i = 0; i < m; ++i) add_bound_l3(dedup[i]); - if ((extraOpts.flags & SOCK_OPT_MCAST_JOIN) && extraOpts.mcast_ver) { - if (extraOpts.mcast_ver == IP_VER4) { - uint32_t g = 0; - memcpy(&g, extraOpts.mcast_group, 4); - if (ipv4_is_multicast(g)) { + if (extraOpts.flags & SOCK_OPT_MCAST_JOIN) { + uint32_t count = extraOpts.mcast_count; + if (count > SOCK_MAX_MCAST_GROUPS) count = SOCK_MAX_MCAST_GROUPS; + + for (uint32_t gidx = 0; gidx < count; ++gidx) { + const net_l4_endpoint* group = &extraOpts.mcast_groups[gidx]; + bool joined = false; + + bool applicable = false; + bool same_family_bound = false; + + if (group->ver == IP_VER4) { + uint32_t g = 0; + memcpy(&g, group->ip, 4); + if (!ipv4_is_multicast(g)) continue; + for (int i = 0; i < bound_l3_count; ++i) { l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(bound_l3[i]); if (!is_valid_v4_l3_for_bind(v4)) continue; - if (!v4->l2) continue; - (void)l2_ipv4_mcast_join(v4->l2->ifindex, g); + same_family_bound = true; + applicable = true; + if (l2_ipv4_mcast_join(v4->l2->ifindex, g)) joined = true; } - } - } else if (extraOpts.mcast_ver == IP_VER6) { - if (ipv6_is_multicast(extraOpts.mcast_group)) { + } else if (group->ver == IP_VER6) { + if (!ipv6_is_multicast(group->ip)) continue; + bool linkscope = ipv6_is_linkscope_mcast(group->ip); + for (int i = 0; i < bound_l3_count; ++i) { l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(bound_l3[i]); if (!is_valid_v6_l3_for_bind(v6)) continue; - if (!v6->l2) continue; - (void)l2_ipv6_mcast_join(v6->l2->ifindex, extraOpts.mcast_group); + same_family_bound = true; + if (linkscope && !ipv6_is_linklocal(v6->ip)) continue; + applicable = true; + if (l2_ipv6_mcast_join(v6->l2->ifindex, group->ip)) joined = true; } } + + if (!applicable && !same_family_bound) continue; + if (!joined) { + leave_mcast_groups(); + for (int i = 0; i < m; ++i) udp_unbind_l3(dedup[i], port, pid); + clear_bound_l3(); + return SOCK_ERR_SYS; + } } } @@ -577,9 +743,6 @@ class UDPSocket : public Socket { localPort = (uint16_t)p; } - (void)l2_ipv4_mcast_join(v4->l2->ifindex, dip); - (void)igmp_send_join(v4->l2->ifindex, dip); - net_l4_endpoint src; src.ver = IP_VER4; memset(src.ip, 0, 16); @@ -647,10 +810,13 @@ class UDPSocket : public Socket { if (!localPort) return SOCK_ERR_BOUND; if (bound_l3_count == 0) return SOCK_ERR_SYS; + int sent = 0; + bool linkscope = ipv6_is_linkscope_mcast(d.ip); for (int i = 0; i < bound_l3_count; ++i) { uint8_t bl3 = bound_l3[i]; l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(bl3); if (!is_valid_v6_l3_for_bind(v6)) continue; + if (linkscope && !ipv6_is_linklocal(v6->ip)) continue; net_l4_endpoint src; src.ver = IP_VER6; @@ -663,8 +829,10 @@ class UDPSocket : public Socket { tx.index = bl3; udp_send_segment(&src, &d, pay, &tx, (extraOpts.flags & SOCK_OPT_TTL) ? extraOpts.ttl : 0, (extraOpts.flags & SOCK_OPT_DONTFRAG) ? 1 : 0); + sent++; } + if (!sent) return SOCK_ERR_SYS; remoteEP = d; return (int64_t)len; } @@ -743,6 +911,7 @@ class UDPSocket : public Socket { } int32_t close() override { + leave_mcast_groups(); netlog_socket_event_t ev{}; ev.comp = NETLOG_COMP_UDP; ev.action = NETLOG_ACT_CLOSE; diff --git a/kernel/networking/transport_layer/tcp/tcp_rx.c b/kernel/networking/transport_layer/tcp/tcp_rx.c index 71ff4e67..ae49b0ad 100644 --- a/kernel/networking/transport_layer/tcp/tcp_rx.c +++ b/kernel/networking/transport_layer/tcp/tcp_rx.c @@ -83,13 +83,12 @@ int64_t tcp_flow_read(tcp_data *flow_ctx, void *buf, uint64_t len) { tcp_flow_t *flow = tcp_flow_from_ctx(flow_ctx); if (!flow) return TCP_DISCONNECT; - if (flow->base.state == TCP_STATE_CLOSED) return TCP_DISCONNECT; - if (!flow->rx.rcv_buf || !flow->rx.rcv_wnd_max) return 0; - if (TCP_SEQ_LT(flow->rx.rcv_data_nxt, flow->rx.rcv_base)) return 0; + if (!flow->rx.rcv_buf || !flow->rx.rcv_wnd_max) return flow->base.state == TCP_STATE_CLOSED ? TCP_DISCONNECT : 0; + if (TCP_SEQ_LT(flow->rx.rcv_data_nxt, flow->rx.rcv_base)) return flow->base.state == TCP_STATE_CLOSED ? TCP_DISCONNECT : 0; uint32_t n = flow->rx.rcv_data_nxt - flow->rx.rcv_base; - if (!n) return 0; + if (!n) return flow->base.state == TCP_STATE_CLOSED ? TCP_DISCONNECT : 0; if (len < n) n = (uint32_t)len; uint8_t *rx = (uint8_t*)flow->rx.rcv_buf; @@ -108,7 +107,7 @@ int64_t tcp_flow_read(tcp_data *flow_ctx, void *buf, uint64_t len) { uint32_t tcp_flow_readable(tcp_data *flow_ctx) { tcp_flow_t *flow = tcp_flow_from_ctx(flow_ctx); - if (!flow || flow->base.state == TCP_STATE_CLOSED || !flow->rx.rcv_buf || !flow->rx.rcv_wnd_max) return 0; + if (!flow || !flow->rx.rcv_buf || !flow->rx.rcv_wnd_max) return 0; if (TCP_SEQ_LT(flow->rx.rcv_data_nxt, flow->rx.rcv_base)) return 0; return flow->rx.rcv_data_nxt - flow->rx.rcv_base; } From f7c38f2e5b04984dc360dd22ab8291fb0056e44b Mon Sep 17 00:00:00 2001 From: CodeAnarchist Date: Thu, 4 Jun 2026 16:10:30 +0200 Subject: [PATCH 20/39] [net] refactor socket --- .../application_layer/csocket_http_client.cpp | 17 +- .../application_layer/csocket_http_client.h | 5 +- .../application_layer/csocket_http_server.cpp | 9 +- .../application_layer/csocket_http_server.h | 2 +- .../application_layer/dhcp_daemon.c | 55 +- .../application_layer/dhcpv6_daemon.c | 36 +- kernel/networking/application_layer/dns/dns.c | 15 +- kernel/networking/application_layer/dns/dns.h | 2 +- .../application_layer/dns/dns_daemon.c | 34 +- .../application_layer/dns/dns_daemon.h | 2 +- .../application_layer/dns/dns_mdns.c | 10 +- .../application_layer/dns/mdns_responder.c | 18 +- .../application_layer/dns/mdns_responder.h | 2 +- kernel/networking/application_layer/http.c | 5 +- kernel/networking/application_layer/ntp.c | 13 +- .../networking/application_layer/ntp_daemon.c | 5 +- .../networking/application_layer/ntp_daemon.h | 2 +- kernel/networking/application_layer/sntp.c | 13 +- .../application_layer/sntp_daemon.c | 5 +- .../application_layer/sntp_daemon.h | 2 +- .../application_layer/socket_http_client.hpp | 189 ++--- .../application_layer/socket_http_server.hpp | 99 +-- kernel/networking/application_layer/ssdp.c | 11 +- .../application_layer/ssdp_daemon.c | 28 +- kernel/networking/interface_manager.c | 40 -- kernel/networking/interface_manager.h | 12 - kernel/networking/internet_layer/icmpv6.c | 12 +- kernel/networking/internet_layer/ipv4.c | 40 +- kernel/networking/internet_layer/ipv4_route.c | 1 - kernel/networking/internet_layer/ipv6.c | 30 +- kernel/networking/internet_layer/ipv6_route.c | 1 - kernel/networking/internet_layer/mld.c | 4 +- kernel/networking/link_layer/ndp.c | 6 +- kernel/networking/net_logger/net_logger.c | 34 +- kernel/networking/net_logger/net_logger.h | 1 - kernel/networking/port_manager.c | 129 ---- kernel/networking/port_manager.h | 74 -- kernel/networking/processes/net_proc.c | 23 +- kernel/networking/transport_layer/csocket.c | 360 +++++----- kernel/networking/transport_layer/csocket.h | 34 +- .../transport_layer/csocket_tcp.cpp | 63 +- .../networking/transport_layer/csocket_tcp.h | 40 +- .../transport_layer/csocket_udp.cpp | 54 +- .../networking/transport_layer/csocket_udp.h | 34 +- kernel/networking/transport_layer/socket.hpp | 132 ++-- .../networking/transport_layer/socket_bind.c | 260 +++++++ .../networking/transport_layer/socket_bind.h | 26 + .../networking/transport_layer/socket_core.c | 215 ++++++ .../networking/transport_layer/socket_core.h | 42 ++ .../transport_layer/socket_endpoint.c | 42 ++ .../transport_layer/socket_endpoint.h | 16 + .../networking/transport_layer/socket_tcp.hpp | 600 +++++----------- .../networking/transport_layer/socket_udp.hpp | 660 +++++------------- kernel/networking/transport_layer/tcp.h | 6 +- .../networking/transport_layer/tcp/tcp_core.c | 140 +--- .../transport_layer/tcp/tcp_internal.h | 7 +- .../transport_layer/tcp/tcp_limits.c | 5 +- .../networking/transport_layer/tcp/tcp_rx.c | 65 +- .../networking/transport_layer/tcp/tcp_tx.c | 7 +- .../transport_layer/tcp/tcp_utils.h | 1 - kernel/networking/transport_layer/udp.c | 61 +- kernel/networking/transport_layer/udp.h | 4 - kernel/process/kernel_syscall_impl.c | 35 +- kernel/process/syscall.c | 98 ++- kernel/tools/curl.c | 40 +- shared | 2 +- user/demo/default_process.c | 2 +- 67 files changed, 1916 insertions(+), 2121 deletions(-) delete mode 100644 kernel/networking/port_manager.c delete mode 100644 kernel/networking/port_manager.h create mode 100644 kernel/networking/transport_layer/socket_bind.c create mode 100644 kernel/networking/transport_layer/socket_bind.h create mode 100644 kernel/networking/transport_layer/socket_core.c create mode 100644 kernel/networking/transport_layer/socket_core.h create mode 100644 kernel/networking/transport_layer/socket_endpoint.c create mode 100644 kernel/networking/transport_layer/socket_endpoint.h diff --git a/kernel/networking/application_layer/csocket_http_client.cpp b/kernel/networking/application_layer/csocket_http_client.cpp index 741469e1..d40aca95 100644 --- a/kernel/networking/application_layer/csocket_http_client.cpp +++ b/kernel/networking/application_layer/csocket_http_client.cpp @@ -1,13 +1,10 @@ #include "csocket_http_client.h" #include "socket_http_client.hpp" -#include "networking/transport_layer/socket.hpp" -#include "networking/transport_layer/socket_tcp.hpp" - extern "C" { -http_client_handle_t http_client_create(uint16_t pid, const SocketExtraOptions* extra, const HTTPClientPolicyOptions *options) { - HTTPClient* cli = new HTTPClient(pid, extra, options); +http_client_handle_t http_client_create(const SocketExtraOptions* extra, const HTTPClientPolicyOptions *options) { + HTTPClient* cli = new HTTPClient(extra, options); if (!cli) return nullptr; return reinterpret_cast(cli); } @@ -24,10 +21,16 @@ int32_t http_client_set_options(http_client_handle_t h, const HTTPClientPolicyOp return cli->set_options(options); } -int32_t http_client_connect(http_client_handle_t h, uint8_t dst_kind, const void* dst, uint16_t port) { +int32_t http_client_connect_endpoint(http_client_handle_t h, const net_l4_endpoint* dst) { if (!h || !dst) return (int32_t)SOCK_ERR_INVAL; HTTPClient *cli = reinterpret_cast(h); - return cli->connect(static_cast(dst_kind), dst, port); + return cli->connect_endpoint(dst); +} + +int32_t http_client_connect_domain(http_client_handle_t h, const char* host, uint16_t port) { + if (!h || !host) return (int32_t)SOCK_ERR_INVAL; + HTTPClient *cli = reinterpret_cast(h); + return cli->connect_domain(host, port); } HTTPResponseMsg http_client_send_request(http_client_handle_t h, const HTTPRequestMsg *req) { diff --git a/kernel/networking/application_layer/csocket_http_client.h b/kernel/networking/application_layer/csocket_http_client.h index a3f71c4d..5ae5e79a 100644 --- a/kernel/networking/application_layer/csocket_http_client.h +++ b/kernel/networking/application_layer/csocket_http_client.h @@ -8,11 +8,12 @@ extern "C" { typedef void* http_client_handle_t; -http_client_handle_t http_client_create(uint16_t pid, const SocketExtraOptions* extra, const HTTPClientPolicyOptions *options); +http_client_handle_t http_client_create(const SocketExtraOptions* extra, const HTTPClientPolicyOptions *options); int32_t http_client_set_options(http_client_handle_t h, const HTTPClientPolicyOptions *options); void http_client_destroy(http_client_handle_t h); -int32_t http_client_connect(http_client_handle_t h, uint8_t dst_kind, const void *dst, uint16_t port); +int32_t http_client_connect_endpoint(http_client_handle_t h, const net_l4_endpoint *dst); +int32_t http_client_connect_domain(http_client_handle_t h, const char *host, uint16_t port); HTTPResponseMsg http_client_send_request(http_client_handle_t h, const HTTPRequestMsg *req); diff --git a/kernel/networking/application_layer/csocket_http_server.cpp b/kernel/networking/application_layer/csocket_http_server.cpp index 07db8507..7e8fd4ab 100644 --- a/kernel/networking/application_layer/csocket_http_server.cpp +++ b/kernel/networking/application_layer/csocket_http_server.cpp @@ -1,13 +1,10 @@ #include "csocket_http_server.h" #include "socket_http_server.hpp" -#include "networking/transport_layer/socket_tcp.hpp" -#include "networking/transport_layer/socket.hpp" - extern "C" { -http_server_handle_t http_server_create(uint16_t pid, const SocketExtraOptions* extra, const HTTPServerPolicyOptions *options) { - HTTPServer* srv = new HTTPServer(pid, extra, options); +http_server_handle_t http_server_create(const SocketExtraOptions* extra, const HTTPServerPolicyOptions *options) { + HTTPServer* srv = new HTTPServer(extra, options); if (!srv) return nullptr; return reinterpret_cast(srv); } @@ -62,7 +59,7 @@ int32_t http_connection_close(http_connection_handle_t c) { if (!c) return (int32_t)SOCK_ERR_INVAL; HTTPConnection* conn = reinterpret_cast(c); if (conn->carry_buf.mem_length) string_free(conn->carry_buf); - if (conn->client) delete conn->client; + if (((conn->client).id && (conn->client).protocol != PROTO_NONE)) close_socket(&conn->client); release(conn); return (int32_t)SOCK_OK; } diff --git a/kernel/networking/application_layer/csocket_http_server.h b/kernel/networking/application_layer/csocket_http_server.h index 629a8fb0..6bb7cee9 100644 --- a/kernel/networking/application_layer/csocket_http_server.h +++ b/kernel/networking/application_layer/csocket_http_server.h @@ -10,7 +10,7 @@ extern "C" { typedef void* http_server_handle_t; typedef void* http_connection_handle_t; -http_server_handle_t http_server_create(uint16_t pid, const SocketExtraOptions* extra, const HTTPServerPolicyOptions *options); +http_server_handle_t http_server_create(const SocketExtraOptions* extra, const HTTPServerPolicyOptions *options); int32_t http_server_set_options(http_server_handle_t srv, const HTTPServerPolicyOptions *options); void http_server_destroy(http_server_handle_t srv); diff --git a/kernel/networking/application_layer/dhcp_daemon.c b/kernel/networking/application_layer/dhcp_daemon.c index c66f2b18..b53c90bf 100644 --- a/kernel/networking/application_layer/dhcp_daemon.c +++ b/kernel/networking/application_layer/dhcp_daemon.c @@ -11,7 +11,7 @@ #include "networking/link_layer/arp.h" #include "networking/transport_layer/udp.h" -#include "networking/transport_layer/csocket_udp.h" +#include "networking/transport_layer/csocket.h" #include "networking/transport_layer/trans_utils.h" #include "types.h" @@ -78,26 +78,16 @@ static void dhcp_reset_backoff(dhcp_if_state_t* st) { st->retry_left_ms = 0; } -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 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 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; } + if (!v4 || v4->mode == IPV4_CFG_DISABLED || !v4->l2 || !v4->l2->is_up) { + if (g_if[i].sock.id && g_if[i].sock.protocol != PROTO_NONE) close_socket(&g_if[i].sock); + if (i < g_if_count - 1) g_if[i] = g_if[g_if_count - 1]; + g_if_count--; + continue; + } i++; } uint8_t n = l2_interface_count(); @@ -108,8 +98,14 @@ static void ensure_inventory() { 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; + bool found = false; + for (int i = 0; i < g_if_count; i++) { + if (g_if[i].l3_id == v4->l3_id) { + found = true; + break; + } + } + if (found) continue; dhcp_if_state_t st; memset(&st, 0, sizeof(st)); st.ifindex = l2->ifindex; @@ -124,13 +120,14 @@ static void ensure_inventory() { 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, NULL); + memset(&st.sock, 0, sizeof(st.sock)); + create_socket(SOCKET_SERVER, PROTO_UDP, NULL, &st.sock); SockBindSpec spec; memset(&spec, 0, sizeof(spec)); spec.kind = BIND_L3; spec.l3_id = st.l3_id; - if (socket_bind_udp(st.sock, &spec, 68) != SOCK_OK) { - socket_destroy_udp(st.sock); + if (bind_socket_spec(&st.sock, &spec, 68) != SOCK_OK) { + close_socket(&st.sock); continue; } st.retry_left_ms = 0; @@ -152,7 +149,7 @@ static bool udp_wait_for_type_on(socket_handle_t sock, uint8_t wanted, uint32_t uint8_t buf[1024]; net_l4_endpoint src; memset(&src, 0, sizeof(src)); - int64_t r = socket_recvfrom_udp(sock, buf, sizeof(buf), &src); + int64_t r = receive_from_socket(&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; } @@ -187,7 +184,7 @@ static bool udp_wait_for_ack_or_nak(socket_handle_t sock, uint32_t expect_xid, c uint8_t buf[1024]; net_l4_endpoint src; memset(&src, 0, sizeof(src)); - int64_t r = socket_recvfrom_udp(sock, buf, sizeof(buf), &src); + int64_t r = receive_from_socket(&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; } @@ -343,7 +340,7 @@ static void dhcp_send_discover_for(dhcp_if_state_t* st) { uint32_t bcast = 0xFFFFFFFFu; net_l4_endpoint dst; make_ep(bcast, 67, IP_VER4, &dst); - socket_sendto_udp(st->sock, 0, &dst, 0, (const void*)pkt.ptr, pkt.size); + send_to_socket(&st->sock, &dst, (const void*)pkt.ptr, pkt.size); free_sized((void*)pkt.ptr, pkt.size); } @@ -352,7 +349,7 @@ static void dhcp_send_request_select_for(dhcp_if_state_t* st, const dhcp_request uint32_t dip = 0xFFFFFFFFu; net_l4_endpoint dst; make_ep(dip, 67, IP_VER4, &dst); - socket_sendto_udp(st->sock, 0, &dst, 0, (const void*)pkt.ptr, pkt.size); + send_to_socket(&st->sock, &dst, (const void*)pkt.ptr, pkt.size); free_sized((void*)pkt.ptr, pkt.size); } @@ -372,7 +369,7 @@ static void dhcp_send_renew_for(dhcp_if_state_t* st) { uint32_t dip = st->server_ip_net ? st->server_ip_net : 0xFFFFFFFFu; net_l4_endpoint dst; make_ep(dip, 67, IP_VER4, &dst); - socket_sendto_udp(st->sock, 0, &dst, 0, (const void*)pkt.ptr, pkt.size); + send_to_socket(&st->sock, &dst, (const void*)pkt.ptr, pkt.size); free_sized((void*)pkt.ptr, pkt.size); } @@ -392,7 +389,7 @@ static void dhcp_send_rebind_for(dhcp_if_state_t* st) { uint32_t dip = 0xFFFFFFFFu; net_l4_endpoint dst; make_ep(dip, 67, IP_VER4, &dst); - socket_sendto_udp(st->sock, 0, &dst, 0, (const void*)pkt.ptr, pkt.size); + send_to_socket(&st->sock, &dst, (const void*)pkt.ptr, pkt.size); free_sized((void*)pkt.ptr, pkt.size); } @@ -412,7 +409,7 @@ static void dhcp_send_inform_for(dhcp_if_state_t* st) { uint32_t dip = 0xFFFFFFFFu; net_l4_endpoint dst; make_ep(dip, 67, IP_VER4, &dst); - socket_sendto_udp(st->sock, 0, &dst, 0, (const void*)pkt.ptr, pkt.size); + send_to_socket(&st->sock, &dst, (const void*)pkt.ptr, pkt.size); free_sized((void*)pkt.ptr, pkt.size); } diff --git a/kernel/networking/application_layer/dhcpv6_daemon.c b/kernel/networking/application_layer/dhcpv6_daemon.c index 9cf27665..2ed92c79 100644 --- a/kernel/networking/application_layer/dhcpv6_daemon.c +++ b/kernel/networking/application_layer/dhcpv6_daemon.c @@ -15,7 +15,7 @@ #include "networking/internet_layer/ipv6.h" #include "networking/internet_layer/ipv6_utils.h" -#include "networking/transport_layer/csocket_udp.h" +#include "networking/transport_layer/csocket.h" enum { DHCPV6_S_INIT = 0, @@ -102,14 +102,6 @@ void dhcpv6_force_decline_l3(uint8_t l3_id) { g_force_decline_mask |= (1ull << (uint64_t)b); } -static void mcast_servers(uint8_t out_ip[16]) { - memset(out_ip, 0, 16); - out_ip[0] = 0xFF; - out_ip[1] = 0x02; - out_ip[14] = 0x01; - out_ip[15] = 0x02; -} - static uint32_t next_backoff_ms(dhcpv6_bind_t* b) { if (!b) return 4000; @@ -198,11 +190,7 @@ static void ensure_binds() { dhcpv6_bind_t* rb = (dhcpv6_bind_t*)linked_list_remove(g_dhcpv6_binds, it); if (rb) { - if (rb->sock) { - socket_close_udp(rb->sock); - socket_destroy_udp(rb->sock); - rb->sock = 0; - } + if (rb->sock.id && rb->sock.protocol != PROTO_NONE) close_socket(&rb->sock); free_sized(rb, sizeof(*rb)); } } @@ -269,8 +257,8 @@ static void ensure_binds() { const uint8_t* mac = network_get_mac(b->ifindex); if (mac) { memcpy(b->mac, mac, 6); b->mac_ok = 1; } - b->sock = udp_socket_create(SOCKET_SERVER, g_dhcpv6_pid, NULL); - if (!b->sock) { + create_socket(SOCKET_SERVER, PROTO_UDP, NULL, &b->sock); + if (!b->sock.id || b->sock.protocol == PROTO_NONE) { free_sized(b, sizeof(*b)); continue; } @@ -281,15 +269,15 @@ static void ensure_binds() { spec.ver = IP_VER6; spec.l3_id = b->bound_linklocal_l3_id; - if (socket_bind_udp(b->sock, &spec, DHCPV6_CLIENT_PORT) != SOCK_OK) { - socket_destroy_udp(b->sock); - b->sock = 0; + if (bind_socket_spec(&b->sock, &spec, DHCPV6_CLIENT_PORT) != SOCK_OK) { + close_socket(&b->sock); + b->sock = ((socket_handle_t){0}); free_sized(b, sizeof(*b)); continue; } uint8_t m[16]; - mcast_servers(m); + ipv6_make_multicast(2, IPV6_MCAST_DHCPV6_SERVERS, NULL, m); (void)l2_ipv6_mcast_join(b->ifindex, m); linked_list_push_front(g_dhcpv6_binds, b); @@ -297,7 +285,7 @@ static void ensure_binds() { } static void fsm_once(dhcpv6_bind_t* b, uint32_t tick_ms) { - if (!b || !b->mac_ok || !b->sock) return; + if (!b || !b->mac_ok || !b->sock.id || b->sock.protocol == PROTO_NONE) return; if (b->done) return; l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(b->target_l3_id); @@ -549,10 +537,10 @@ static void fsm_once(dhcpv6_bind_t* b, uint32_t tick_ms) { net_l4_endpoint dst; memset(&dst, 0, sizeof(dst)); dst.ver = IP_VER6; - mcast_servers(dst.ip); + ipv6_make_multicast(2, IPV6_MCAST_DHCPV6_SERVERS, NULL, dst.ip); dst.port = DHCPV6_SERVER_PORT; - (void)socket_sendto_udp(b->sock, DST_ENDPOINT, &dst, 0, (const void*)msg, (uint64_t)msg_len); + (void)send_to_socket(&b->sock, &dst, msg, (uint64_t)msg_len); b->tx_tries++; uint8_t rx[DHCPV6_MAX_MSG]; @@ -565,7 +553,7 @@ static void fsm_once(dhcpv6_bind_t* b, uint32_t tick_ms) { uint32_t waited = 0; while (waited < 250) { - int64_t r = socket_recvfrom_udp(b->sock, rx, sizeof(rx), &src); + int64_t r = receive_from_socket(&b->sock, rx, sizeof(rx), &src); if (r > 0) { if (src.port != DHCPV6_SERVER_PORT) { msleep(50); diff --git a/kernel/networking/application_layer/dns/dns.c b/kernel/networking/application_layer/dns/dns.c index 9d6a2c23..116fa455 100644 --- a/kernel/networking/application_layer/dns/dns.c +++ b/kernel/networking/application_layer/dns/dns.c @@ -20,7 +20,7 @@ static dns_result_t perform_dns_query_once(socket_handle_t sock, const net_l4_endpoint *dns_srv, const char *name, dns_qtype_t qtype, uint32_t timeout_ms, dns_record_t *out_records, uint32_t max_records, uint32_t *out_count) { if (out_count) *out_count = 0; - if (!sock) return DNS_ERR_SOCKET; + if (!((sock).id && (sock).protocol != PROTO_NONE)) return DNS_ERR_SOCKET; if (!dns_srv) return DNS_ERR_NO_DNS; if (!name) return DNS_ERR_FORMAT; if (!out_records && max_records) return DNS_ERR_FORMAT; @@ -37,14 +37,14 @@ static dns_result_t perform_dns_query_once(socket_handle_t sock, const net_l4_en net_l4_endpoint dst = *dns_srv; dst.port = 53; - int64_t sent = socket_sendto_udp(sock, DST_ENDPOINT, &dst, 0, request_buffer, request_len); + int64_t sent = send_to_socket(&sock, &dst, (void*)request_buffer, request_len); 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(sock, response_buffer, sizeof(response_buffer), &source); + int64_t received = receive_from_socket(&sock, response_buffer, sizeof(response_buffer), &source); if (received > 0 && source.port == 53 && source.ver == dst.ver) { uint32_t received_len = received; dns_record_t parsed[DNS_QUERY_RECORDS]; @@ -179,8 +179,9 @@ static dns_result_t query_with_selection(const net_l4_endpoint* primary, const n if (which == DNS_USE_PRIMARY && dns_srv_is_zero(primary)) return DNS_ERR_NO_DNS; if (which == DNS_USE_SECONDARY && dns_srv_is_zero(secondary)) return DNS_ERR_NO_DNS; if (which == DNS_USE_BOTH && dns_srv_is_zero(primary) && dns_srv_is_zero(secondary)) return DNS_ERR_NO_DNS; - socket_handle_t sock = udp_socket_create(SOCK_ROLE_CLIENT, get_current_proc_pid(), NULL); - if (!sock) return DNS_ERR_SOCKET; + socket_handle_t sock = {0}; + create_socket(SOCKET_CLIENT, PROTO_UDP, NULL, &sock); + if (!((sock).id && (sock).protocol != PROTO_NONE)) return DNS_ERR_SOCKET; dns_result_t res = DNS_ERR_NO_DNS; if (which == DNS_USE_PRIMARY) res = perform_dns_query_once(sock, primary, hostname, qtype, timeout_ms, out_records, max_records, out_count); @@ -193,8 +194,8 @@ static dns_result_t query_with_selection(const net_l4_endpoint* primary, const n if (res != DNS_OK && second && first != second) res = perform_dns_query_once(sock, second, hostname, qtype, timeout_ms, out_records, max_records, out_count); } - socket_close_udp(sock); - socket_destroy_udp(sock); + close_socket(&sock); + close_socket(&sock); return res; } diff --git a/kernel/networking/application_layer/dns/dns.h b/kernel/networking/application_layer/dns/dns.h index 4d5d1862..e5863269 100644 --- a/kernel/networking/application_layer/dns/dns.h +++ b/kernel/networking/application_layer/dns/dns.h @@ -1,5 +1,5 @@ #pragma once -#include "networking/transport_layer/csocket_udp.h" +#include "networking/transport_layer/csocket.h" #include "networking/application_layer/dns/dns_wire.h" #ifdef __cplusplus extern "C" { diff --git a/kernel/networking/application_layer/dns/dns_daemon.c b/kernel/networking/application_layer/dns/dns_daemon.c index e6ffcab5..f185c913 100644 --- a/kernel/networking/application_layer/dns/dns_daemon.c +++ b/kernel/networking/application_layer/dns/dns_daemon.c @@ -5,15 +5,15 @@ #include "process/scheduler.h" #include "syscalls/syscalls.h" #include "net/socket_types.h" -#include "networking/transport_layer/csocket_udp.h" +#include "networking/transport_layer/csocket.h" #include "networking/internet_layer/ipv6_utils.h" #include "std/memory.h" static uint16_t g_pid_dnsd = 0xFFFF; -static socket_handle_t g_sock = 0; +static socket_handle_t g_sock = {0}; -static socket_handle_t g_sock_mdns4 = 0; -static socket_handle_t g_sock_mdns6 = 0; +static socket_handle_t g_sock_mdns4 = {0}; +static socket_handle_t g_sock_mdns6 = {0}; uint16_t dns_get_pid(void){ return g_pid_dnsd; } bool dns_is_running(void){ return g_pid_dnsd != 0xFFFF; } @@ -24,7 +24,7 @@ socket_handle_t mdns_socket_handle_v4(void){ return g_sock_mdns4; } socket_handle_t mdns_socket_handle_v6(void){ return g_sock_mdns6; } static socket_handle_t mdns_create_socket(ip_version_t ver, const uint8_t* group) { - if (!group) return 0; + if (!group) return ((socket_handle_t){0}); SocketExtraOptions opt; memset(&opt, 0, sizeof(opt)); opt.flags = SOCK_OPT_MCAST_JOIN | SOCK_OPT_TTL; @@ -34,19 +34,20 @@ static socket_handle_t mdns_create_socket(ip_version_t ver, const uint8_t* group opt.mcast_groups[0].port = DNS_MDNS_PORT; if(ver == IP_VER4) memcpy(opt.mcast_groups[0].ip, group, 4); else if (ver == IP_VER6) memcpy(opt.mcast_groups[0].ip, group, 16); - else return 0; + else return ((socket_handle_t){0}); - socket_handle_t s = udp_socket_create(SOCK_ROLE_SERVER, g_pid_dnsd, &opt); - if(!s) return 0; + socket_handle_t s = {0}; + create_socket(SOCKET_SERVER, PROTO_UDP, &opt, &s); + if(!((s).id && (s).protocol != PROTO_NONE)) return ((socket_handle_t){0}); SockBindSpec spec; memset(&spec, 0, sizeof(spec)); spec.kind = BIND_ANY; spec.ver = ver; - if(socket_bind_udp(s, &spec, DNS_MDNS_PORT) != SOCK_OK){ - socket_destroy_udp(s); - return 0; + if(bind_socket_spec(&s, &spec, DNS_MDNS_PORT) != SOCK_OK){ + close_socket(&s); + return ((socket_handle_t){0}); } return s; @@ -55,7 +56,8 @@ static socket_handle_t mdns_create_socket(ip_version_t ver, const uint8_t* group int dns_deamon_entry(int argc, char* argv[]){ (void)argc; (void)argv; dns_set_pid(get_current_proc_pid()); - g_sock = udp_socket_create(SOCK_ROLE_CLIENT, g_pid_dnsd, NULL); + memset(&g_sock, 0, sizeof(g_sock)); + create_socket(SOCKET_CLIENT, PROTO_UDP, NULL, &g_sock); uint32_t mdns_v4 = IPV4_MCAST_MDNS; uint8_t mdns_v4_addr[4]; @@ -72,15 +74,15 @@ int dns_deamon_entry(int argc, char* argv[]){ uint8_t buf[900]; net_l4_endpoint src; - if (g_sock_mdns4) { + if (((g_sock_mdns4).id && (g_sock_mdns4).protocol != PROTO_NONE)) { memset(&src, 0, sizeof(src)); - int64_t r4 = socket_recvfrom_udp(g_sock_mdns4, buf, sizeof(buf), &src); + int64_t r4 = receive_from_socket(&g_sock_mdns4, buf, sizeof(buf), &src); if(r4 > 0 && src.ver == IP_VER4) mdns_responder_handle_query(g_sock_mdns4, IP_VER4, mdns_v4_addr, buf, (uint32_t)r4, &src); } - if (g_sock_mdns6) { + if (((g_sock_mdns6).id && (g_sock_mdns6).protocol != PROTO_NONE)) { memset(&src, 0, sizeof(src)); - int64_t r6 = socket_recvfrom_udp(g_sock_mdns6, buf, sizeof(buf), &src); + int64_t r6 = receive_from_socket(&g_sock_mdns6, buf, sizeof(buf), &src); if(r6 > 0 && src.ver == IP_VER6) mdns_responder_handle_query(g_sock_mdns6, IP_VER6, mdns_v6, buf, (uint32_t)r6, &src); } diff --git a/kernel/networking/application_layer/dns/dns_daemon.h b/kernel/networking/application_layer/dns/dns_daemon.h index ca386c53..4c4afd4a 100644 --- a/kernel/networking/application_layer/dns/dns_daemon.h +++ b/kernel/networking/application_layer/dns/dns_daemon.h @@ -1,5 +1,5 @@ #pragma once -#include "networking/transport_layer/csocket_udp.h" +#include "networking/transport_layer/csocket.h" #define IPV4_MCAST_MDNS 0xE00000FBu diff --git a/kernel/networking/application_layer/dns/dns_mdns.c b/kernel/networking/application_layer/dns/dns_mdns.c index e6a52b8d..b23091f9 100644 --- a/kernel/networking/application_layer/dns/dns_mdns.c +++ b/kernel/networking/application_layer/dns/dns_mdns.c @@ -6,7 +6,7 @@ static dns_result_t perform_mdns_query_once(socket_handle_t sock, const net_l4_endpoint *dst, const char *name, dns_qtype_t qtype, uint32_t timeout_ms, dns_record_t *out_records, uint32_t max_records, uint32_t *out_count) { if (out_count) *out_count = 0; - if (!sock) return DNS_ERR_NO_DNS; + if (!((sock).id && (sock).protocol != PROTO_NONE)) return DNS_ERR_NO_DNS; if (!dst) return DNS_ERR_NO_DNS; if (!name) return DNS_ERR_FORMAT; if (!out_records && max_records) return DNS_ERR_FORMAT; @@ -15,7 +15,7 @@ static dns_result_t perform_mdns_query_once(socket_handle_t sock, const net_l4_e uint32_t offset = dns_wire_build_query(request_buffer, sizeof(request_buffer), 0, name, qtype, false); if (!offset) return DNS_ERR_FORMAT; - int64_t sent = socket_sendto_udp(sock, DST_ENDPOINT, dst, 0, request_buffer, offset); + int64_t sent = send_to_socket(&sock, dst, (void*)request_buffer, offset); if (sent < 0) return DNS_ERR_SEND; uint32_t found = 0; @@ -23,7 +23,7 @@ static dns_result_t perform_mdns_query_once(socket_handle_t sock, const net_l4_e while (waited_ms < timeout_ms) { uint8_t response_buffer[512]; net_l4_endpoint source; - int64_t received = socket_recvfrom_udp(sock, response_buffer, sizeof(response_buffer), &source); + int64_t received = receive_from_socket(&sock, response_buffer, sizeof(response_buffer), &source); if (received > 0 && source.port == DNS_MDNS_PORT){ uint32_t received_len = received; dns_record_t records[12]; @@ -65,7 +65,7 @@ dns_result_t mdns_query(const char* name, dns_qtype_t qtype, uint32_t timeout_ms uint32_t total = 0; socket_handle_t sock4 = mdns_socket_handle_v4(); - if (sock4) { + if (((sock4).id && (sock4).protocol != PROTO_NONE)) { uint32_t group =DNS_MDNS_GROUP_V4; net_l4_endpoint dst; make_ep(group, DNS_MDNS_PORT, IP_VER4, &dst); @@ -79,7 +79,7 @@ dns_result_t mdns_query(const char* name, dns_qtype_t qtype, uint32_t timeout_ms } socket_handle_t sock6 = mdns_socket_handle_v6(); - if (sock6) { + if (((sock6).id && (sock6).protocol != PROTO_NONE)) { net_l4_endpoint dst; memset(&dst, 0, sizeof(dst)); dst.ver = IP_VER6; diff --git a/kernel/networking/application_layer/dns/mdns_responder.c b/kernel/networking/application_layer/dns/mdns_responder.c index b26f214d..5527821c 100644 --- a/kernel/networking/application_layer/dns/mdns_responder.c +++ b/kernel/networking/application_layer/dns/mdns_responder.c @@ -3,7 +3,7 @@ #include "dns_sd.h" #include "dns_cache.h" #include "networking/internet_layer/ipv6_utils.h" -#include "networking/transport_layer/csocket_udp.h" +#include "networking/transport_layer/csocket.h" #include "networking/interface_manager.h" #include "std/std.h" #include "std/string.h" @@ -72,7 +72,7 @@ static mdns_service_t g_mdns_services[MDNS_MAX_SERVICES]; static mdns_cache_entry_t g_mdns_cache[MDNS_CACHE_MAX]; static void mdns_send(socket_handle_t sock, const net_l4_endpoint *src, bool unicast, ip_version_t ver, const uint8_t *mcast_ip, const uint8_t *pkt, uint32_t pkt_len) { - if (!sock) return; + if (!((sock).id && (sock).protocol != PROTO_NONE)) return; if (!pkt) return; if (!pkt_len) return; @@ -82,7 +82,7 @@ static void mdns_send(socket_handle_t sock, const net_l4_endpoint *src, bool uni if (unicast && src) { dst = *src; if (!dst.port) dst.port = DNS_MDNS_PORT; - socket_sendto_udp(sock, DST_ENDPOINT, &dst, 0, (void*)pkt, pkt_len); + send_to_socket(&sock, &dst, pkt, pkt_len); return; } @@ -90,7 +90,7 @@ static void mdns_send(socket_handle_t sock, const net_l4_endpoint *src, bool uni if (ver == IP_VER4) memcpy(dst.ip, mcast_ip, 4); else memcpy(dst.ip, mcast_ip, 16); dst.port = DNS_MDNS_PORT; - socket_sendto_udp(sock, DST_ENDPOINT, &dst, 0, (void*)pkt, pkt_len); + send_to_socket(&sock, &dst, pkt, pkt_len); } static bool mdns_pick_identity(uint32_t *out_v4, uint8_t out_v6[16], uint8_t *out_ifindex, uint8_t out_ifid[8]) { @@ -700,8 +700,8 @@ void mdns_responder_tick(socket_handle_t sock4, socket_handle_t sock6, const uin if (ok && p.an) { mdns_pkt_commit(&p); - if (sock4 && mcast_v4) mdns_send(sock4, 0, false, IP_VER4, mcast_v4, pkt, p.off); - if (sock6 && mcast_v6) mdns_send(sock6, 0, false, IP_VER6, mcast_v6, pkt, p.off); + if (((sock4).id && (sock4).protocol != PROTO_NONE) && mcast_v4) mdns_send(sock4, 0, false, IP_VER4, mcast_v4, pkt, p.off); + if (((sock6).id && (sock6).protocol != PROTO_NONE) && mcast_v6) mdns_send(sock6, 0, false, IP_VER6, mcast_v6, pkt, p.off); uint32_t ttl_ms = MDNS_TTL_S * 1000; @@ -741,8 +741,8 @@ void mdns_responder_tick(socket_handle_t sock4, socket_handle_t sock6, const uin if (mdns_pkt_begin(&p, pkt, sizeof(pkt), DNS_FLAG_QR | DNS_FLAG_AA) && mdns_add_service_records(&p, s, MDNS_TTL_S, do_goodbye) && (p.an || p.ar)) { mdns_pkt_commit(&p); - if (sock4 && mcast_v4) mdns_send(sock4, 0, false, IP_VER4, mcast_v4, pkt, p.off); - if (sock6 && mcast_v6) mdns_send(sock6, 0, false, IP_VER6, mcast_v6, pkt, p.off); + if (((sock4).id && (sock4).protocol != PROTO_NONE) && mcast_v4) mdns_send(sock4, 0, false, IP_VER4, mcast_v4, pkt, p.off); + if (((sock6).id && (sock6).protocol != PROTO_NONE) && mcast_v6) mdns_send(sock6, 0, false, IP_VER6, mcast_v6, pkt, p.off); } if (do_goodbye) { @@ -758,7 +758,7 @@ void mdns_responder_tick(socket_handle_t sock4, socket_handle_t sock6, const uin } void mdns_responder_handle_query(socket_handle_t sock, ip_version_t ver, const uint8_t *mcast_ip, const uint8_t *pkt, uint32_t pkt_len, const net_l4_endpoint *src) { - if (!sock) return; + if (!((sock).id && (sock).protocol != PROTO_NONE)) return; if (!mcast_ip) return; if (!pkt) return; if (pkt_len < 12) return; diff --git a/kernel/networking/application_layer/dns/mdns_responder.h b/kernel/networking/application_layer/dns/mdns_responder.h index be14dd07..c2e00914 100644 --- a/kernel/networking/application_layer/dns/mdns_responder.h +++ b/kernel/networking/application_layer/dns/mdns_responder.h @@ -1,5 +1,5 @@ #pragma once -#include "networking/transport_layer/csocket_udp.h" +#include "networking/transport_layer/csocket.h" #include "net/network_types.h" #ifdef __cplusplus diff --git a/kernel/networking/application_layer/http.c b/kernel/networking/application_layer/http.c index d96197d8..284e9dd0 100644 --- a/kernel/networking/application_layer/http.c +++ b/kernel/networking/application_layer/http.c @@ -211,7 +211,8 @@ HTTPParseResult http_parse_status_line(const char *buf, uint32_t len, HTTPStatus code_buf[3] = 0; uint32_t code = 0; - if (!parse_uint32_dec(code_buf, &code)) return HTTP_PARSE_BAD_FORMAT; + if (!parse_uint32_dec_exact(code_buf, &code)) return HTTP_PARSE_BAD_FORMAT; + if (code < 100 || code > 999) return HTTP_PARSE_BAD_FORMAT; out->status_code = code; uint32_t i = 12; @@ -385,7 +386,7 @@ HTTPParseResult http_header_parse(const char *buf, uint32_t len, const HTTPPolic if (ok) { memcpy(len_buf, buf + val_start, val_len); len_buf[val_len] = 0; - ok = parse_uint32_dec(len_buf, &parsed); + ok = parse_uint32_dec_exact(len_buf, &parsed); } if (!ok || (C->framing.has_content_length && C->fields.content_length != parsed)) { diff --git a/kernel/networking/application_layer/ntp.c b/kernel/networking/application_layer/ntp.c index 73371708..11d24a34 100644 --- a/kernel/networking/application_layer/ntp.c +++ b/kernel/networking/application_layer/ntp.c @@ -5,7 +5,7 @@ #include "process/scheduler.h" #include "console/kio.h" #include "math/math.h" -#include "networking/transport_layer/csocket_udp.h" +#include "networking/transport_layer/csocket.h" #include "networking/transport_layer/trans_utils.h" #include "syscalls/syscalls.h" @@ -110,7 +110,7 @@ static ntp_result_t ntp_send_query(socket_handle_t sock, uint32_t server_ip_host net_l4_endpoint dst; make_ep(server_ip_host, NTP_PORT, IP_VER4, &dst); - int64_t sent = socket_sendto_udp(sock, DST_ENDPOINT, &dst, 0, &p, sizeof(p)); + int64_t sent = send_to_socket(&sock, &dst, &p, sizeof(p)); if (sent < 0) return NTP_ERR_SEND; *t1_us_out = t1_us; *tx_ntp64_be_out = tx_be; @@ -237,8 +237,9 @@ ntp_result_t ntp_poll_once(uint32_t timeout_ms){ discover_servers(&s0, &s1); if (s0 == 0 && s1 == 0) return NTP_ERR_NO_SERVER; - socket_handle_t sock = udp_socket_create(0, (uint32_t)get_current_proc_pid(), NULL); - if (sock == 0) return NTP_ERR_SOCKET; + socket_handle_t sock = {0}; + create_socket(SOCKET_CLIENT, PROTO_UDP, NULL, &sock); + if (!((sock).id && (sock).protocol != PROTO_NONE)) return NTP_ERR_SOCKET; uint64_t t1_0 = 0, t1_1 = 0; uint64_t o0 = 0, o1 = 0; @@ -258,7 +259,7 @@ ntp_result_t ntp_poll_once(uint32_t timeout_ms){ while (waited < timeout_ms) { uint8_t buf[96]; net_l4_endpoint src; - int64_t n = socket_recvfrom_udp(sock, buf, sizeof(buf), &src); + int64_t n = receive_from_socket(&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; @@ -386,7 +387,7 @@ ntp_result_t ntp_poll_once(uint32_t timeout_ms){ if (best_err == NTP_OK && waited >= (timeout_ms / 2)) break; } - socket_destroy_udp(sock); + close_socket(&sock); ntp_peer_t* best = NULL; for (uint32_t i = 0; i < 2; i++) { diff --git a/kernel/networking/application_layer/ntp_daemon.c b/kernel/networking/application_layer/ntp_daemon.c index 827bf1e1..cf414bf8 100644 --- a/kernel/networking/application_layer/ntp_daemon.c +++ b/kernel/networking/application_layer/ntp_daemon.c @@ -8,7 +8,7 @@ #include "syscalls/syscalls.h" static uint16_t g_pid_ntp = 0xFFFF; -static socket_handle_t g_sock = 0; +static socket_handle_t g_sock = {0}; uint16_t ntp_get_pid(void){ return g_pid_ntp; } bool ntp_is_running(void){ return g_pid_ntp != 0xFFFF; } @@ -43,7 +43,8 @@ int ntp_daemon_entry(int argc, char* argv[]){ (void)argv; g_pid_ntp = get_current_proc_pid(); - g_sock = udp_socket_create(0, g_pid_ntp, NULL); + g_sock = ((socket_handle_t){0}); + create_socket(SOCKET_CLIENT, PROTO_UDP, NULL, &g_sock); ntp_set_pid(get_current_proc_pid()); uint32_t attempts = 0; diff --git a/kernel/networking/application_layer/ntp_daemon.h b/kernel/networking/application_layer/ntp_daemon.h index ffec242e..61140754 100644 --- a/kernel/networking/application_layer/ntp_daemon.h +++ b/kernel/networking/application_layer/ntp_daemon.h @@ -1,7 +1,7 @@ #pragma once #include "types.h" -#include "networking/transport_layer/csocket_udp.h" +#include "networking/transport_layer/csocket.h" #ifdef __cplusplus extern "C" { diff --git a/kernel/networking/application_layer/sntp.c b/kernel/networking/application_layer/sntp.c index c8db27fd..6d7461b3 100644 --- a/kernel/networking/application_layer/sntp.c +++ b/kernel/networking/application_layer/sntp.c @@ -5,7 +5,7 @@ #include "process/scheduler.h" #include "console/kio.h" #include "types.h" -#include "networking/transport_layer/csocket_udp.h" +#include "networking/transport_layer/csocket.h" #include "syscalls/syscalls.h" #include "networking/transport_layer/trans_utils.h" @@ -51,7 +51,7 @@ static sntp_result_t sntp_send_query(socket_handle_t sock, uint32_t server_ip_ho p.txTs = unix_us_to_ntp64_be(t1_us); net_l4_endpoint dst; make_ep(server_ip_host, NTP_PORT, IP_VER4, &dst); - int64_t sent = socket_sendto_udp(sock, DST_ENDPOINT, &dst, 0, &p, sizeof(p)); + int64_t sent = send_to_socket(&sock, &dst, (void*)&p, sizeof(p)); if (sent < 0) return SNTP_ERR_SEND; *t1_us_out = t1_us; return SNTP_OK; @@ -86,8 +86,9 @@ sntp_result_t sntp_poll_once(uint32_t timeout_ms){ if (s0 == 0 && s1 == 0) return SNTP_ERR_NO_SERVER; - socket_handle_t sock = udp_socket_create(0, (uint32_t)get_current_proc_pid(), NULL); - if (sock == 0) return SNTP_ERR_SOCKET; + socket_handle_t sock = {0}; + create_socket(SOCKET_CLIENT, PROTO_UDP, NULL, &sock); + if (!((sock).id && (sock).protocol != PROTO_NONE)) return SNTP_ERR_SOCKET; uint64_t t1_0 = 0, t1_1 = 0; @@ -105,7 +106,7 @@ sntp_result_t sntp_poll_once(uint32_t timeout_ms){ while (waited < timeout_ms){ uint8_t buf[96]; net_l4_endpoint src; - int64_t n = socket_recvfrom_udp(sock, buf, sizeof(buf), &src); + int64_t n = receive_from_socket(&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; @@ -156,7 +157,7 @@ sntp_result_t sntp_poll_once(uint32_t timeout_ms){ if (best_server_unix_us != 0 && waited >= (timeout_ms / 2)) break; } - socket_destroy_udp(sock); + close_socket(&sock); if (best_server_unix_us == 0) return SNTP_ERR_TIMEOUT; diff --git a/kernel/networking/application_layer/sntp_daemon.c b/kernel/networking/application_layer/sntp_daemon.c index 21a7adca..76724540 100644 --- a/kernel/networking/application_layer/sntp_daemon.c +++ b/kernel/networking/application_layer/sntp_daemon.c @@ -10,7 +10,7 @@ static uint16_t g_pid_sntp = 0xFFFF; -static socket_handle_t g_sock = 0; +static socket_handle_t g_sock = {0}; uint16_t sntp_get_pid(void){ return g_pid_sntp; } bool sntp_is_running(void){ return g_pid_sntp != 0xFFFF; } @@ -42,7 +42,8 @@ static bool any_ipv4_configured_nonlocal(void){ int sntp_daemon_entry(int argc, char* argv[]){ (void)argc; (void)argv; g_pid_sntp = (uint16_t)get_current_proc_pid(); - g_sock = udp_socket_create(0, g_pid_sntp, NULL); + g_sock = ((socket_handle_t){0}); + create_socket(SOCKET_CLIENT, PROTO_UDP, NULL, &g_sock); sntp_set_pid(get_current_proc_pid()); uint32_t attempts = 0; while (attempts < SNTP_BOOTSTRAP_MAX_RETRY){ diff --git a/kernel/networking/application_layer/sntp_daemon.h b/kernel/networking/application_layer/sntp_daemon.h index 8ea56ac1..372e2bc5 100644 --- a/kernel/networking/application_layer/sntp_daemon.h +++ b/kernel/networking/application_layer/sntp_daemon.h @@ -1,6 +1,6 @@ #pragma once//deprecated, use ntp #include "types.h" -#include "networking/transport_layer/csocket_udp.h" +#include "networking/transport_layer/csocket.h" #ifdef __cplusplus extern "C" { diff --git a/kernel/networking/application_layer/socket_http_client.hpp b/kernel/networking/application_layer/socket_http_client.hpp index 560e9f33..1e7b7bb1 100644 --- a/kernel/networking/application_layer/socket_http_client.hpp +++ b/kernel/networking/application_layer/socket_http_client.hpp @@ -1,24 +1,26 @@ #pragma once #include "console/kio.h" -#include "networking/transport_layer/socket_tcp.hpp" +#include "networking/transport_layer/csocket.h" +#include "networking/transport_layer/tcp.h" +#include "networking/net_logger/net_logger.h" #include "http.h" #include "std/std.h" #include "net/socket_types.h" #include "data/format/url.h" #include "networking/transport_layer/trans_utils.h" +#include "networking/transport_layer/socket_endpoint.h" +#include "process/scheduler.h" struct HTTPClientOrigin { - SockDstKind kind; string domain; net_l4_endpoint ep; - uint16_t port; bool valid; }; class HTTPClient { private: uint16_t pid; - TCPSocket* sock; + socket_handle_t sock; SocketExtraOptions log_opts; SocketExtraOptions* tcp_extra; HTTPClientPolicy policy; @@ -50,7 +52,7 @@ class HTTPClient { uint32_t last_rx_ms = start_ms; while (hdr_end < 0) { - int64_t r = sock->recv(tmp, sizeof(tmp)); + int64_t r = receive_from_socket(&sock, tmp, sizeof(tmp), nullptr); if (r == TCP_WOULDBLOCK) { uint32_t now = (uint32_t)get_time(); if ((now - last_rx_ms) > policy.common.header_idle_timeout_ms || (now - start_ms) > policy.common.header_total_timeout_ms) { @@ -106,7 +108,10 @@ class HTTPClient { if (header_result != HTTP_PARSE_OK) { free_response(&resp); string_free(buf); - sock->close(); + if (((sock).id && (sock).protocol != PROTO_NONE)) { + close_socket(&sock); + sock = socket_handle_t{}; + } return error_response(SOCK_ERR_PROTO); } @@ -138,7 +143,7 @@ class HTTPClient { uint32_t body_last_rx_ms = body_start_ms; while (chunk_result == HTTP_PARSE_INCOMPLETE) { - int64_t r = sock->recv(tmp, sizeof(tmp)); + int64_t r = receive_from_socket(&sock, tmp, sizeof(tmp), nullptr); if (r == TCP_WOULDBLOCK) { uint32_t now = (uint32_t)get_time(); if ((now - body_last_rx_ms) > policy.common.body_idle_timeout_ms || (now - body_start_ms) > policy.common.body_total_timeout_ms) break; @@ -154,7 +159,10 @@ class HTTPClient { http_chunked_decoder_free(&dec); free_response(&resp); string_free(buf); - sock->close(); + if (((sock).id && (sock).protocol != PROTO_NONE)) { + close_socket(&sock); + sock = socket_handle_t{}; + } return error_response(SOCK_ERR_PROTO); } @@ -172,7 +180,10 @@ class HTTPClient { if (need > policy.common.max_body_bytes) { free_response(&resp); string_free(buf); - sock->close(); + if (((sock).id && (sock).protocol != PROTO_NONE)) { + close_socket(&sock); + sock = socket_handle_t{}; + } return error_response(SOCK_ERR_PROTO); } @@ -181,7 +192,10 @@ class HTTPClient { if (!body_copy) { free_response(&resp); string_free(buf); - sock->close(); + if (((sock).id && (sock).protocol != PROTO_NONE)) { + close_socket(&sock); + sock = socket_handle_t{}; + } return error_response(SOCK_ERR_SYS); } @@ -191,7 +205,7 @@ class HTTPClient { uint32_t body_start_ms = (uint32_t)get_time(); uint32_t body_last_rx_ms = body_start_ms; while (copied < need) { - int64_t r = sock->recv(body_copy + copied, need - copied); + int64_t r = receive_from_socket(&sock, body_copy + copied, need - copied, nullptr); if (r == TCP_WOULDBLOCK) { uint32_t now = (uint32_t)get_time(); if ((now - body_last_rx_ms) > policy.common.body_idle_timeout_ms || (now - body_start_ms) > policy.common.body_total_timeout_ms) break; @@ -207,7 +221,10 @@ class HTTPClient { release(body_copy); free_response(&resp); string_free(buf); - sock->close(); + if (((sock).id && (sock).protocol != PROTO_NONE)) { + close_socket(&sock); + sock = socket_handle_t{}; + } return error_response(SOCK_ERR_PROTO); } @@ -221,14 +238,17 @@ class HTTPClient { uint32_t body_start_ms = (uint32_t)get_time(); uint32_t body_last_rx_ms = body_start_ms; while (body.length <= policy.common.max_body_bytes) { - int64_t r = sock->recv(tmp, sizeof(tmp)); + int64_t r = receive_from_socket(&sock, tmp, sizeof(tmp), nullptr); if (r == TCP_WOULDBLOCK) { uint32_t now = (uint32_t)get_time(); if ((now - body_last_rx_ms) > policy.common.body_idle_timeout_ms || (now - body_start_ms) > policy.common.body_total_timeout_ms) { string_free(body); free_response(&resp); string_free(buf); - sock->close(); + if (((sock).id && (sock).protocol != PROTO_NONE)) { + close_socket(&sock); + sock = socket_handle_t{}; + } return error_response(SOCK_ERR_PROTO); } msleep(1); @@ -238,7 +258,10 @@ class HTTPClient { string_free(body); free_response(&resp); string_free(buf); - sock->close(); + if (((sock).id && (sock).protocol != PROTO_NONE)) { + close_socket(&sock); + sock = socket_handle_t{}; + } return error_response(r); } if (r == 0) break; @@ -246,7 +269,10 @@ class HTTPClient { string_free(body); free_response(&resp); string_free(buf); - sock->close(); + if (((sock).id && (sock).protocol != PROTO_NONE)) { + close_socket(&sock); + sock = socket_handle_t{}; + } return error_response(SOCK_ERR_PROTO); } string_append_bytes(&body, tmp, (uint32_t)r); @@ -266,8 +292,10 @@ class HTTPClient { ev.pid = pid; ev.u0 = (uint32_t)resp.status_code; ev.u1 = (uint32_t)resp.body.size; - ev.local_port = sock->get_local_port(); - ev.remote_ep = sock->get_remote_ep(); + ev.local_port = get_socket_local_port(&sock); + net_l4_endpoint sock_remote_ep{}; + get_socket_remote_endpoint(&sock, &sock_remote_ep); + ev.remote_ep = sock_remote_ep; netlog_socket_event(&log_opts, &ev); string_free(buf); @@ -277,7 +305,7 @@ class HTTPClient { public: - explicit HTTPClient(uint16_t pid_, const SocketExtraOptions* extra, const HTTPClientPolicyOptions* http_options) : pid(pid_), sock(nullptr), log_opts{}, tcp_extra(nullptr), policy(http_client_policy_from_options(http_options)), origin{} { + explicit HTTPClient(const SocketExtraOptions* extra, const HTTPClientPolicyOptions* http_options) : pid((uint16_t)get_current_proc_pid()), sock(socket_handle_t{}), log_opts{}, tcp_extra(nullptr), policy(http_client_policy_from_options(http_options)), origin{} { if (extra) log_opts = *extra; const SocketExtraOptions* tcp_ptr = extra; @@ -290,8 +318,8 @@ class HTTPClient { } } - sock = (TCPSocket*)zalloc(sizeof(TCPSocket)); - if (sock) new (sock) TCPSocket(SOCK_ROLE_CLIENT, pid, tcp_ptr); + sock = socket_handle_t{}; + create_socket(SOCKET_CLIENT, PROTO_TCP, tcp_ptr, &sock); } ~HTTPClient() {close();} @@ -301,16 +329,13 @@ class HTTPClient { return SOCK_OK; } - int32_t connect(SockDstKind kind, const void* dst, uint16_t port) { - uint16_t p = port; - int32_t r = sock ? sock->connect(kind, dst, p) : SOCK_ERR_STATE; + int32_t connect_endpoint(const net_l4_endpoint* dst, const char* host_name = nullptr) { + int32_t r = ((sock).id && (sock).protocol != PROTO_NONE) ? connect_socket(&sock, dst) : SOCK_ERR_STATE; if (r >= 0) { HTTPClientOrigin next{}; - next.kind = kind; - next.port = p; next.valid = true; - if (kind == DST_DOMAIN && dst) next.domain = string_from_literal((const char*)dst); - else if (kind == DST_ENDPOINT && dst) next.ep = *(const net_l4_endpoint*)dst; + if (dst) next.ep = *dst; + if (host_name) next.domain = string_from_literal(host_name); if (origin.domain.mem_length) string_free(origin.domain); origin = next; } @@ -319,15 +344,14 @@ class HTTPClient { ev.comp = NETLOG_COMP_HTTP_CLIENT; ev.action = NETLOG_ACT_CONNECT; ev.pid = pid; - ev.dst_kind = kind; - ev.u0 = p; - if (kind == DST_DOMAIN) ev.s0 = (const char*)dst; - if (kind == DST_ENDPOINT && dst) ev.dst_ep = *(const net_l4_endpoint*)dst; + if (dst) ev.dst_ep = *dst; ev.i0 = r; - if (sock) { - ev.local_port = sock->get_local_port(); - ev.remote_ep = sock->get_remote_ep(); + if (((sock).id && (sock).protocol != PROTO_NONE)) { + ev.local_port = get_socket_local_port(&sock); + net_l4_endpoint sock_remote_ep{}; + get_socket_remote_endpoint(&sock, &sock_remote_ep); + ev.remote_ep = sock_remote_ep; if (ev.remote_ep.ver) ev.dst_ep = ev.remote_ep; } @@ -335,6 +359,13 @@ class HTTPClient { return r; } + int32_t connect_domain(const char* host, uint16_t port) { + if (!host || !port) return SOCK_ERR_INVAL; + net_l4_endpoint ep = socket_endpoint_select(host, port, (ip_version_t)0, DNS_USE_BOTH, 3000); + if (!ep.ver) return SOCK_ERR_DNS; + return connect_endpoint(&ep, host); + } + HTTPResponseMsg send_request(const HTTPRequestMsg& req) { HTTPRequestMsg curr = req; curr.path = string_repeat('\0', 0); @@ -345,24 +376,28 @@ class HTTPClient { HTTPResponseMsg resp{}; uint32_t redirects = policy.follow_redirects ? policy.max_redirects : 0; for (uint32_t i = 0;; i++) { - if (!sock) { + if (!((sock).id && (sock).protocol != PROTO_NONE)) { resp = error_response(SOCK_ERR_STATE); break; } if (!curr.host_override && curr.version != HTTP_VERSION_10 && !curr.headers_common.fields.host.length && origin.valid) { - if (origin.kind == DST_DOMAIN && origin.domain.data) { + if (origin.domain.data) { + uint16_t host_port = origin.ep.port; bool ipv6 = str_has_char(origin.domain.data, origin.domain.length, ':') >= 0; - if (origin.port && origin.port != 80) { - if (ipv6) curr.headers_common.fields.host = string_format("[%.*s]:%i", (int)origin.domain.length, origin.domain.data, (int)origin.port); - else curr.headers_common.fields.host = string_format("%.*s:%i", (int)origin.domain.length, origin.domain.data, (int)origin.port); - } else curr.headers_common.fields.host = string_from_literal_length(origin.domain.data, origin.domain.length); - } else if (origin.kind == DST_ENDPOINT && origin.ep.ver) { + if (host_port && host_port != 80) { + if (ipv6) curr.headers_common.fields.host = string_format("[%.*s]:%i", (int)origin.domain.length, origin.domain.data, (int)host_port); + else curr.headers_common.fields.host = string_format("%.*s:%i", (int)origin.domain.length, origin.domain.data, (int)host_port); + } else { + if (ipv6) curr.headers_common.fields.host = string_format("[%.*s]", (int)origin.domain.length, origin.domain.data); + else curr.headers_common.fields.host = string_from_literal_length(origin.domain.data, origin.domain.length); + } + } else if (origin.ep.ver) { char ip[48]; bool ipv6 = false; uint16_t ep_port = 0; net_ep_split(&origin.ep, ip, sizeof(ip), &ipv6, &ep_port); - uint16_t host_port = origin.port ? origin.port : ep_port; + uint16_t host_port = ep_port; if (host_port && host_port != 80) { if (ipv6) curr.headers_common.fields.host = string_format("[%s]:%i", ip, (int)host_port); else curr.headers_common.fields.host = string_format("%s:%i", ip, (int)host_port); @@ -379,7 +414,7 @@ class HTTPClient { uint32_t off = 0; int64_t sent = 0; while (off < out_len) { - int64_t r = sock->send(out.data + off, out_len - off); + int64_t r = send_on_socket(&sock, (void*)(out.data + off), out_len - off); if (r == TCP_WOULDBLOCK) { msleep(5); continue; @@ -398,8 +433,10 @@ class HTTPClient { ev.pid = pid; ev.u0 = out_len; ev.i0 = sent; - ev.local_port = sock->get_local_port(); - ev.remote_ep = sock->get_remote_ep(); + ev.local_port = get_socket_local_port(&sock); + net_l4_endpoint sock_remote_ep{}; + get_socket_remote_endpoint(&sock, &sock_remote_ep); + ev.remote_ep = sock_remote_ep; char pathbuf[128]; if (curr.path.length && curr.path.data) { @@ -427,7 +464,7 @@ class HTTPClient { if (!url.ok) break; bool absolute = url.scheme.size || url.host.size; - uint16_t next_port = origin.port; + uint16_t next_port = origin.ep.port; if (url.scheme.size) { if (url.scheme.size == 5 && strncmp_case((const char*)url.scheme.ptr, "https", true, 5) == 0) break; if (!(url.scheme.size == 4 && strncmp_case((const char*)url.scheme.ptr, "http", true, 4) == 0)) break; @@ -448,16 +485,12 @@ class HTTPClient { curr.headers_common.fields.host = string_repeat('\0', 0); } - if (sock) { - sock->close(); - sock->~TCPSocket(); - release(sock); - sock = nullptr; - } - sock = (TCPSocket*)zalloc(sizeof(TCPSocket)); - int32_t rr = sock && next_domain.data ? SOCK_OK : SOCK_ERR_SYS; - if (rr >= 0) new (sock) TCPSocket(SOCK_ROLE_CLIENT, pid, tcp_extra ? tcp_extra : &log_opts); - if (rr >= 0) rr = connect(DST_DOMAIN, next_domain.data, next_port); + if (((sock).id && (sock).protocol != PROTO_NONE)) { + close_socket(&sock); + sock = socket_handle_t{}; + } + int32_t rr = next_domain.data && create_socket(SOCKET_CLIENT, PROTO_TCP, tcp_extra ? tcp_extra : &log_opts, &sock) ? SOCK_OK : SOCK_ERR_SYS; + if (rr >= 0) rr = connect_domain(next_domain.data, next_port); if (next_domain.mem_length) string_free(next_domain); if (rr < 0) { free_response(&resp); @@ -465,22 +498,16 @@ class HTTPClient { break; } } else { - SockDstKind reconnect_kind = origin.kind; - uint16_t reconnect_port = origin.port; net_l4_endpoint reconnect_ep = origin.ep; string reconnect_domain = string{}; - if (origin.kind == DST_DOMAIN && origin.domain.data) reconnect_domain = string_from_literal(origin.domain.data); + if (origin.domain.data) reconnect_domain = string_from_literal(origin.domain.data); - if (sock) { - sock->close(); - sock->~TCPSocket(); - release(sock); - sock = nullptr; - } - sock = (TCPSocket*)zalloc(sizeof(TCPSocket)); - int32_t rr = sock && origin.valid && (reconnect_kind != DST_DOMAIN || reconnect_domain.data) ? SOCK_OK : SOCK_ERR_SYS; - if (rr >= 0) new (sock) TCPSocket(SOCK_ROLE_CLIENT, pid, tcp_extra ? tcp_extra : &log_opts); - if (rr >= 0) rr = reconnect_kind == DST_DOMAIN ? connect(DST_DOMAIN, reconnect_domain.data, reconnect_port) : connect(DST_ENDPOINT, &reconnect_ep, reconnect_port); + if (((sock).id && (sock).protocol != PROTO_NONE)) { + close_socket(&sock); + sock = socket_handle_t{}; + } + int32_t rr = origin.valid && create_socket(SOCKET_CLIENT, PROTO_TCP, tcp_extra ? tcp_extra : &log_opts, &sock) ? SOCK_OK : SOCK_ERR_SYS; + if (rr >= 0) rr = reconnect_domain.data ? connect_domain(reconnect_domain.data, reconnect_ep.port) : connect_endpoint(&reconnect_ep); if (reconnect_domain.mem_length) string_free(reconnect_domain); if (rr < 0) { free_response(&resp); @@ -506,26 +533,26 @@ class HTTPClient { } int32_t close() { - int32_t r = SOCK_ERR_STATE; - if (sock) r = sock->close(); - netlog_socket_event_t ev{}; ev.comp = NETLOG_COMP_HTTP_CLIENT; ev.action = NETLOG_ACT_CLOSE; ev.pid = pid; - ev.i0 = r; - if (sock) { - ev.local_port = sock->get_local_port(); - ev.remote_ep = sock->get_remote_ep(); + if (((sock).id && (sock).protocol != PROTO_NONE)) { + ev.local_port = get_socket_local_port(&sock); + net_l4_endpoint sock_remote_ep{}; + get_socket_remote_endpoint(&sock, &sock_remote_ep); + ev.remote_ep = sock_remote_ep; } + int32_t r = SOCK_ERR_STATE; + if (((sock).id && (sock).protocol != PROTO_NONE)) { + r = close_socket(&sock); + sock = socket_handle_t{}; + } + ev.i0 = r; netlog_socket_event(&log_opts, &ev); - if (sock) sock->~TCPSocket(); - if (sock) release(sock); - sock = nullptr; - if (tcp_extra) release(tcp_extra); tcp_extra = nullptr; diff --git a/kernel/networking/application_layer/socket_http_server.hpp b/kernel/networking/application_layer/socket_http_server.hpp index f4b033d6..4d110af9 100644 --- a/kernel/networking/application_layer/socket_http_server.hpp +++ b/kernel/networking/application_layer/socket_http_server.hpp @@ -1,13 +1,17 @@ #pragma once #include "console/kio.h" -#include "networking/transport_layer/socket_tcp.hpp" +#include "networking/transport_layer/csocket.h" +#include "networking/transport_layer/tcp.h" +#include "networking/net_logger/net_logger.h" #include "http.h" #include "std/std.h" #include "net/socket_types.h" #include "syscalls/syscalls.h" +#include "process/scheduler.h" struct HTTPConnection { - TCPSocket* client; + socket_handle_t client; + uint16_t pid; string carry_buf; uint32_t request_count; HTTPMethod current_method; @@ -18,13 +22,13 @@ struct HTTPConnection { class HTTPServer { private: uint16_t pid; - TCPSocket* sock; + socket_handle_t sock; SocketExtraOptions log_opts; SocketExtraOptions* tcp_extra; HTTPServerPolicy policy; public: - explicit HTTPServer(uint16_t pid_, const SocketExtraOptions* extra, const HTTPServerPolicyOptions* http_options) : pid(pid_), sock(nullptr), log_opts{}, tcp_extra(nullptr), policy(http_server_policy_from_options(http_options)) { + explicit HTTPServer(const SocketExtraOptions* extra, const HTTPServerPolicyOptions* http_options) : pid((uint16_t)get_current_proc_pid()), sock(socket_handle_t{}), log_opts{}, tcp_extra(nullptr), policy(http_server_policy_from_options(http_options)) { if (extra) log_opts = *extra; const SocketExtraOptions* tcp_ptr = extra; @@ -37,8 +41,8 @@ class HTTPServer { } } - sock = (TCPSocket*)zalloc(sizeof(TCPSocket)); - if (sock) new (sock) TCPSocket(SOCK_ROLE_SERVER, pid, tcp_ptr); + sock = socket_handle_t{}; + create_socket(SOCKET_SERVER, PROTO_TCP, tcp_ptr, &sock); } ~HTTPServer() { close(); } @@ -50,7 +54,7 @@ class HTTPServer { int32_t bind(const SockBindSpec& spec, uint16_t port) { uint16_t p = port; - int32_t r = sock ? sock->bind(spec, p) : SOCK_ERR_STATE; + int32_t r = ((sock).id && (sock).protocol != PROTO_NONE) ? bind_socket_spec(&sock, &spec, p) : SOCK_ERR_STATE; netlog_socket_event_t ev{}; ev.comp = NETLOG_COMP_HTTP_SERVER; @@ -64,7 +68,7 @@ class HTTPServer { int32_t listen(int backlog = 4) { int b = backlog; - int32_t r = sock ? sock->listen(b) : SOCK_ERR_STATE; + int32_t r = ((sock).id && (sock).protocol != PROTO_NONE) ? listen_on(&sock, b) : SOCK_ERR_STATE; netlog_socket_event_t ev{}; ev.comp = NETLOG_COMP_HTTP_SERVER; @@ -77,40 +81,40 @@ class HTTPServer { } HTTPConnection* accept() { - TCPSocket* c = sock ? sock->accept() : nullptr; - if (!c) return nullptr; + socket_handle_t child = socket_handle_t{}; + if (!((sock).id && (sock).protocol != PROTO_NONE) || !accept_on_socket(&sock, &child)) return nullptr; HTTPConnection* conn = (HTTPConnection*)zalloc(sizeof(HTTPConnection)); if (!conn) { - delete c; + close_socket(&child); return nullptr; } - conn->client = c; + conn->client = child; + conn->pid = pid; conn->carry_buf = string{nullptr, 0, 0}; conn->request_count = 0; conn->current_method = HTTP_METHOD_GET; conn->close_after_response = true; conn->send_keep_alive_header = false; - if (c) { - netlog_socket_event_t ev{}; - ev.comp = NETLOG_COMP_HTTP_SERVER; - ev.action = NETLOG_ACT_ACCEPT; - ev.pid = pid; - ev.i0 = (int64_t)(uintptr_t)c; - ev.local_port = c->get_local_port(); - ev.remote_ep = c->get_remote_ep(); - netlog_socket_event(&log_opts, &ev); - } + netlog_socket_event_t ev{}; + ev.comp = NETLOG_COMP_HTTP_SERVER; + ev.action = NETLOG_ACT_ACCEPT; + ev.pid = pid; + ev.i0 = (int64_t)child.id; + ev.local_port = get_socket_local_port(&child); + net_l4_endpoint child_remote_ep{}; + get_socket_remote_endpoint(&child, &child_remote_ep); + ev.remote_ep = child_remote_ep; + netlog_socket_event(&log_opts, &ev); return conn; } HTTPRequestMsg recv_request(HTTPConnection* conn) { HTTPRequestMsg req{}; - if (!conn || !conn->client) return req; + if (!conn || !((conn->client).id && (conn->client).protocol != PROTO_NONE)) return req; - TCPSocket* client = conn->client; string buf = string_repeat('\0', 0); if (conn->carry_buf.length) { string_append_bytes(&buf, conn->carry_buf.data, conn->carry_buf.length); @@ -128,7 +132,7 @@ class HTTPServer { char* body_copy = nullptr; while (hdr_end < 0) { - int64_t r = client->recv(tmp, sizeof(tmp)); + int64_t r = receive_from_socket(&conn->client, tmp, sizeof(tmp), nullptr); if (r == TCP_WOULDBLOCK) { uint32_t now = (uint32_t)get_time(); if ((uint32_t)(now - last_rx_ms) > policy.common.header_idle_timeout_ms || (uint32_t)(now - start_ms) > policy.common.header_total_timeout_ms) { @@ -268,7 +272,7 @@ class HTTPServer { consumed = body_start + used; while (chunk_result == HTTP_PARSE_INCOMPLETE) { - int64_t r = client->recv(tmp, sizeof(tmp)); + int64_t r = receive_from_socket(&conn->client, tmp, sizeof(tmp), nullptr); if (r == TCP_WOULDBLOCK) { uint32_t now = (uint32_t)get_time(); if ((now - body_last_rx_ms) > policy.common.body_idle_timeout_ms || (now - body_start_ms) > policy.common.body_total_timeout_ms) break; @@ -316,7 +320,7 @@ class HTTPServer { uint32_t body_start_ms = (uint32_t)get_time(); uint32_t body_last_rx_ms = body_start_ms; while (copied < need) { - int64_t r = client->recv(body_copy + copied, need - copied); + int64_t r = receive_from_socket(&conn->client, body_copy + copied, need - copied, nullptr); if (r == TCP_WOULDBLOCK) { uint32_t now = (uint32_t)get_time(); if ((now - body_last_rx_ms) > policy.common.body_idle_timeout_ms || (now - body_start_ms) > policy.common.body_total_timeout_ms) { @@ -375,8 +379,10 @@ class HTTPServer { ev.u0 = (uint32_t)req.method; ev.u1 = (uint32_t)req.path.length; ev.i0 = (int64_t)req.body.size; - ev.local_port = client->get_local_port(); - ev.remote_ep = client->get_remote_ep(); + ev.local_port = get_socket_local_port(&conn->client); + net_l4_endpoint conn_remote_ep{}; + get_socket_remote_endpoint(&conn->client, &conn_remote_ep); + ev.remote_ep = conn_remote_ep; char pathbuf[128]; if (req.path.length && req.path.data) { @@ -399,9 +405,8 @@ class HTTPServer { } int32_t send_response(HTTPConnection* conn, const HTTPResponseMsg& res) { - if (!conn || !conn->client) return SOCK_ERR_STATE; + if (!conn || !((conn->client).id && (conn->client).protocol != PROTO_NONE)) return SOCK_ERR_STATE; - TCPSocket* client = conn->client; uint32_t code = (uint32_t)res.status_code; bool informational = code >= 100 && code < 200; bool suppress_body = informational || conn->current_method == HTTP_METHOD_HEAD || code == 204 || code == 304; @@ -464,7 +469,7 @@ class HTTPServer { uint32_t off = 0; while (sent >= 0 && off < first_len) { - int64_t r = client->send(first_ptr + off, first_len - off); + int64_t r = send_on_socket(&conn->client, (void*)(first_ptr + off), first_len - off); uint32_t now = (uint32_t)get_time(); if (r == TCP_WOULDBLOCK || r == 0) { @@ -490,7 +495,7 @@ class HTTPServer { while (body_off < body_len) { uint32_t ask = body_len - body_off; if (ask > 16384) ask = 16384; - int64_t r = client->send(body + body_off, ask); + int64_t r = send_on_socket(&conn->client, (void*)(body + body_off), ask); uint32_t now = (uint32_t)get_time(); if (r == TCP_WOULDBLOCK || r == 0) { @@ -520,31 +525,39 @@ class HTTPServer { ev.u0 = code; ev.u1 = out_len; ev.i0 = sent; - ev.local_port = client->get_local_port(); - ev.remote_ep = client->get_remote_ep(); + ev.local_port = get_socket_local_port(&conn->client); + net_l4_endpoint conn_remote_ep{}; + get_socket_remote_endpoint(&conn->client, &conn_remote_ep); + ev.remote_ep = conn_remote_ep; netlog_socket_event(&log_opts, &ev); string_free(out); - if (sent >= 0 && close_after_send) client->close(); + if (sent >= 0 && close_after_send) if (conn && ((conn->client).id && (conn->client).protocol != PROTO_NONE)) { + close_socket(&conn->client); + conn->client = socket_handle_t{}; + } return sent < 0 ? (int32_t)sent : SOCK_OK; } int32_t close() { - int32_t r = sock ? SOCK_OK : SOCK_ERR_STATE; + int32_t r = ((sock).id && (sock).protocol != PROTO_NONE) ? SOCK_OK : SOCK_ERR_STATE; netlog_socket_event_t ev{}; ev.comp = NETLOG_COMP_HTTP_SERVER; ev.action = NETLOG_ACT_CLOSE; ev.pid = pid; ev.i0 = r; - if (sock) { - ev.local_port = sock->get_local_port(); - ev.remote_ep = sock->get_remote_ep(); + if (((sock).id && (sock).protocol != PROTO_NONE)) { + ev.local_port = get_socket_local_port(&sock); + net_l4_endpoint sock_remote_ep{}; + get_socket_remote_endpoint(&sock, &sock_remote_ep); + ev.remote_ep = sock_remote_ep; } netlog_socket_event(&log_opts, &ev); - if (sock) sock->~TCPSocket(); - if (sock) release(sock); - sock = nullptr; + if (((sock).id && (sock).protocol != PROTO_NONE)) { + r = close_socket(&sock); + sock = socket_handle_t{}; + } if (tcp_extra) release(tcp_extra); tcp_extra = nullptr; diff --git a/kernel/networking/application_layer/ssdp.c b/kernel/networking/application_layer/ssdp.c index a2b7d50f..64ef04a7 100644 --- a/kernel/networking/application_layer/ssdp.c +++ b/kernel/networking/application_layer/ssdp.c @@ -14,9 +14,18 @@ uint32_t ssdp_parse_mx_ms(const char* buf, int len){ ++i; while (i < len && (buf[i] == ' ' || buf[i] == '\t')) ++i; + uint32_t start = (uint32_t)i; + while (i < len && buf[i] >= '0' && buf[i] <= '9') ++i; + if ((uint32_t)i == start) return 1000; + + char mx_buf[16]; + uint32_t mx_len = (uint32_t)i - start; + if (mx_len >= sizeof(mx_buf)) return 1000; + memcpy(mx_buf, buf + start, mx_len); + mx_buf[mx_len] = 0; uint32_t v = 0; - parse_uint32_dec(buf, &v); + if (!parse_uint32_dec_exact(mx_buf, &v)) return 1000; if (v == 0) v = 1; if (v > 5) v = 5; diff --git a/kernel/networking/application_layer/ssdp_daemon.c b/kernel/networking/application_layer/ssdp_daemon.c index 56a6d90b..4c50a111 100644 --- a/kernel/networking/application_layer/ssdp_daemon.c +++ b/kernel/networking/application_layer/ssdp_daemon.c @@ -5,7 +5,7 @@ #include "std/string.h" #include "syscalls/syscalls.h" #include "net/network_types.h" -#include "networking/transport_layer/csocket_udp.h" +#include "networking/transport_layer/csocket.h" #include "networking/application_layer/ssdp.h" #include "networking/internet_layer/ipv6_utils.h" #include "math/math.h" @@ -50,21 +50,21 @@ static void ssdp_schedule_response(const net_l4_endpoint* src, uint32_t mx_ms) { } static void ssdp_send_notify(socket_handle_t s4, socket_handle_t s6, bool alive) { - if (s4) { + if (((s4).id && (s4).protocol != PROTO_NONE)) { string msg = ssdp_build_notify(alive, false); net_l4_endpoint dst; make_ep(ssdp_host_v4, 1900, IP_VER4, &dst); - (void)socket_sendto_udp(s4, DST_ENDPOINT, &dst, 0, msg.data, msg.length); + (void)send_to_socket(&s4, &dst, (void*)msg.data, msg.length); string_free(msg); } - if (s6) { + if (((s6).id && (s6).protocol != PROTO_NONE)) { string msg = ssdp_build_notify(alive, true); net_l4_endpoint dst = (net_l4_endpoint){0}; dst.ver = IP_VER6; memcpy(dst.ip, ssdp_host_v6, 16); dst.port = 1900; - (void)socket_sendto_udp(s6, DST_ENDPOINT, &dst, 0, msg.data, msg.length); + (void)send_to_socket(&s6, &dst, (void*)msg.data, msg.length); string_free(msg); } } @@ -87,20 +87,20 @@ int ssdp_daemon_entry(int argc, char* argv[]) { opt.mcast_groups[1].ver = IP_VER6; opt.mcast_groups[1].port = 1900; memcpy(opt.mcast_groups[1].ip, ssdp_host_v6, 16); - socket_handle_t s = udp_socket_create(SOCK_ROLE_SERVER, get_current_proc_pid(), &opt); + socket_handle_t s = {0}; + create_socket(SOCKET_SERVER, PROTO_UDP, &opt,&s); struct SockBindSpec spec = (struct SockBindSpec){0}; spec.kind = BIND_ANY; - if (s && socket_bind_udp(s, &spec, 1900) < 0) { - socket_close_udp(s); - socket_destroy_udp(s); - s = 0; + if (((s).id && (s).protocol != PROTO_NONE) && bind_socket_spec(&s, &spec, 1900) < 0) { + close_socket(&s); + s = ((socket_handle_t){0}); } socket_handle_t s4 = s; socket_handle_t s6 = s; - if (!s4 && !s6) return 1; + if (!((s4).id && (s4).protocol != PROTO_NONE) && !((s6).id && (s6).protocol != PROTO_NONE)) return 1; ssdp_send_notify(s4, s6, true); msleep(100); @@ -121,9 +121,9 @@ int ssdp_daemon_entry(int argc, char* argv[]) { char buf[2048]; net_l4_endpoint src = (net_l4_endpoint){0}; - if (s) { + if (((s).id && (s).protocol != PROTO_NONE)) { for (int i = 0; i < SSDP_RECV_BURST; ++i) { - int64_t r = socket_recvfrom_udp(s, buf, sizeof(buf) - 1, &src); + int64_t r = receive_from_socket(&s, buf, sizeof(buf) - 1, &src); if (r <= 0) break; buf[r] = 0; if (ssdp_is_msearch(buf, (int)r)) ssdp_schedule_response(&src, ssdp_parse_mx_ms(buf, (int)r)); @@ -147,7 +147,7 @@ int ssdp_daemon_entry(int argc, char* argv[]) { string resp = ssdp_build_search_response(); socket_handle_t sock = (ssdp_pending[i].dst.ver == IP_VER6) ? s6 : s4; - if (sock) (void)socket_sendto_udp(sock, DST_ENDPOINT, &ssdp_pending[i].dst, 0, resp.data, resp.length); + if (((sock).id && (sock).protocol != PROTO_NONE)) (void)send_to_socket(&sock, &ssdp_pending[i].dst, (void*)resp.data, resp.length); string_free(resp); break; } diff --git a/kernel/networking/interface_manager.c b/kernel/networking/interface_manager.c index ff61c1a8..cfb01566 100644 --- a/kernel/networking/interface_manager.c +++ b/kernel/networking/interface_manager.c @@ -4,7 +4,6 @@ #include "networking/link_layer/ndp.h" #include "networking/internet_layer/ipv4_route.h" #include "networking/internet_layer/ipv6_route.h" -#include "networking/port_manager.h" #include "process/scheduler.h" #include "memory/page_allocator.h" #include "networking/internet_layer/ipv4_utils.h" @@ -13,9 +12,6 @@ #include "networking/internet_layer/mld.h" #include "networking/link_layer/nic_types.h" #include "networking/network.h" - -static void* g_kmem_page_v4 = NULL; -static void* g_kmem_page_v6 = NULL; //TODO: add network settings static l2_interface_t g_l2[MAX_L2_INTERFACES]; @@ -400,22 +396,6 @@ uint8_t l3_ipv4_add_to_interface(uint8_t ifindex, uint32_t ip, uint32_t mask, ui l2->l3_v4[loc] = n; l2->ipv4_count++; - if (!g_kmem_page_v4) g_kmem_page_v4 = palloc(PAGE_SIZE*1, MEM_PRIV_KERNEL, MEM_RW|MEM_NORM, false); - if (!g_kmem_page_v4) return NULL; - - n->port_manager = (port_manager_t*)kalloc(g_kmem_page_v4, 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--; - if (n->routing_table) { - ipv4_rt_destroy((ipv4_rt_table_t*)n->routing_table); - n->routing_table = NULL; - } - g_v4[g].used = false; - memset(&g_v4[g], 0, sizeof(g_v4[g])); - return 0; - } - port_manager_init(n->port_manager); if (n->mode != IPV4_CFG_DISABLED && n->ip && l2->kind != NET_IFK_LOCALHOST) (void)l2_ipv4_mcast_join(ifindex, IPV4_MCAST_ALL_HOSTS); if (l2->kind != NET_IFK_LOCALHOST && l2_has_active_v4(l2)) for (int i = 0; i < (int)l2->ipv4_mcast_count; ++i) igmp_send_join(l2->ifindex, l2->ipv4_mcast[i]); @@ -497,10 +477,6 @@ bool l3_ipv4_remove_from_interface(uint8_t l3_id){ } if (g < 0) return false; - if (n->port_manager) { - kfree(n->port_manager, sizeof(port_manager_t)); - n->port_manager = NULL; - } uint8_t slot = l3_slot_from_id(l3_id); if (slot < MAX_IPV4_PER_INTERFACE && l2->l3_v4[slot] == n){ @@ -659,18 +635,6 @@ uint8_t l3_ipv6_add_to_interface(uint8_t ifindex, const uint8_t ip[16], uint8_t l2->l3_v6[loc] = n; l2->ipv6_count++; - if (!g_kmem_page_v6) g_kmem_page_v6 = palloc(PAGE_SIZE*1, MEM_PRIV_KERNEL, MEM_RW|MEM_NORM, false); - if (!g_kmem_page_v6) return NULL; - - n->port_manager = (port_manager_t*)kalloc(g_kmem_page_v6, 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; - memset(&g_v6[g], 0, sizeof(g_v6[g])); - return 0; - } - port_manager_init(n->port_manager); if (cfg == IPV6_CFG_DHCPV6){ uint8_t m[16]; ipv6_make_multicast(2, IPV6_MCAST_DHCPV6_SERVERS, NULL, m); @@ -863,10 +827,6 @@ bool l3_ipv6_remove_from_interface(uint8_t l3_id){ } if (g < 0) return false; - if (n->port_manager) { - kfree(n->port_manager, sizeof(port_manager_t)); - n->port_manager = NULL; - } uint8_t slot = l3_slot_from_id(l3_id); if (slot < MAX_IPV6_PER_INTERFACE && l2->l3_v6[slot] == n){ diff --git a/kernel/networking/interface_manager.h b/kernel/networking/interface_manager.h index 3f1485f9..5eaf2b98 100644 --- a/kernel/networking/interface_manager.h +++ b/kernel/networking/interface_manager.h @@ -1,7 +1,6 @@ #pragma once #include "types.h" -#include "networking/port_manager.h" #ifdef __cplusplus extern "C" { @@ -85,7 +84,6 @@ typedef struct l3_ipv4_interface { 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; @@ -130,7 +128,6 @@ typedef struct l3_ipv6_interface { uint8_t dad_probes_sent; uint32_t dad_timer_ms; void *routing_table; - port_manager_t *port_manager; l2_interface_t *l2; uint8_t ra_has; uint8_t ra_autonomous; @@ -185,15 +182,6 @@ 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]); -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; -} - static inline uint8_t make_l3_id_v4(uint8_t ifindex, uint8_t local_slot){ return (uint8_t)((ifindex<<4) | (local_slot & 0x03)); } static inline uint8_t make_l3_id_v6(uint8_t ifindex, uint8_t local_slot){ return (uint8_t)((ifindex<<4) | 0x08 | (local_slot & 0x03)); } static inline uint8_t l3_ifindex_from_id(uint8_t l3_id){ return (uint8_t)((l3_id >> 4) & 0x0F); } diff --git a/kernel/networking/internet_layer/icmpv6.c b/kernel/networking/internet_layer/icmpv6.c index cfa24d3e..620a89f6 100644 --- a/kernel/networking/internet_layer/icmpv6.c +++ b/kernel/networking/internet_layer/icmpv6.c @@ -77,7 +77,7 @@ bool icmpv6_send_on_l2(uint8_t ifindex, const uint8_t dst_ip[16], const uint8_t ipv6_hdr_t ip6; ip6.ver_tc_fl = bswap32((uint32_t)(6u << 28)); ip6.payload_len = bswap16((uint16_t)icmp_len); - ip6.next_header = 58; + ip6.next_header = PROTO_ICMPV6; ip6.hop_limit = hop_limit ? hop_limit : 64; memcpy(ip6.src, src_ip, 16); memcpy(ip6.dst, dst_ip, 16); @@ -107,7 +107,7 @@ static bool icmpv6_send_echo_reply(uint16_t ifindex, const uint8_t src_ip[16], c return false; } - e->hdr.checksum = bswap16(checksum16_pipv6(dst_ip, src_ip, 58, (const uint8_t*)buf, icmp_len)); + e->hdr.checksum = bswap16(checksum16_pipv6(dst_ip, src_ip, PROTO_ICMPV6, (const uint8_t*)buf, icmp_len)); icmpv6_send_on_l2(ifindex, src_ip, dst_ip, src_mac, (const void*)buf, icmp_len, hop_limit ? hop_limit : 64); @@ -143,10 +143,10 @@ static bool icmpv6_send_echo_request(const uint8_t dst_ip[16], uint16_t id, uint netpkt_unref(pkt); return false; } - e.hdr.checksum = bswap16(checksum16_pipv6(plan.src_ip, dst_ip, 58, (const uint8_t*)buf, len)); + e.hdr.checksum = bswap16(checksum16_pipv6(plan.src_ip, dst_ip, PROTO_ICMPV6, (const uint8_t*)buf, len)); memcpy(buf, &e, sizeof(e)); - ipv6_send_packet(dst_ip, 58, pkt, (const ipv6_tx_opts_t*)tx_opts_or_null, hop_limit ? hop_limit : 64, 0); + ipv6_send_packet(dst_ip, PROTO_ICMPV6, pkt, (const ipv6_tx_opts_t*)tx_opts_or_null, hop_limit ? hop_limit : 64, 0); return true; } @@ -247,7 +247,7 @@ static bool extract_echo_id_seq_from_error(const uint8_t *icmp, uint32_t icmp_le memcpy(&inner, icmp + 8, sizeof(inner)); uint32_t v = bswap32(inner.ver_tc_fl); if ((v >>28) != 6) return false; - if (inner.next_header != 58) return false; + if (inner.next_header != PROTO_ICMPV6) return false; const uint8_t *inner_icmp = icmp + 8u + (uint32_t)sizeof(ipv6_hdr_t); if ((uintptr_t)inner_icmp + sizeof(icmpv6_echo_t)>(uintptr_t)icmp + icmp_len) return false; @@ -280,7 +280,7 @@ void icmpv6_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ return; } - uint16_t calc = bswap16(checksum16_pipv6(src_ip, dst_ip, 58, icmp, icmp_len)); + uint16_t calc = bswap16(checksum16_pipv6(src_ip, dst_ip, PROTO_ICMPV6, icmp, icmp_len)); if (calc != 0) { netpkt_unref(pkt); return; diff --git a/kernel/networking/internet_layer/ipv4.c b/kernel/networking/internet_layer/ipv4.c index 7b8c2575..74f20c93 100644 --- a/kernel/networking/internet_layer/ipv4.c +++ b/kernel/networking/internet_layer/ipv4.c @@ -393,15 +393,15 @@ void ipv4_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { uint8_t l3id = cand[i]->l3_id; switch (proto) { netpkt_t* l4pkt; - case 2: + case PROTO_IGMP: l4pkt = netpkt_view(pkt, hdr_len, l4_len); if (l4pkt) igmp_input((uint8_t)ifindex, src, dst, l4pkt); break; - case 6: + case PROTO_TCP: l4pkt = netpkt_view(pkt, hdr_len, l4_len); if (l4pkt) tcp_input(IP_VER4, &src, &dst, l3id, l4pkt); break; - case 17: + case PROTO_UDP: l4pkt = netpkt_view(pkt, hdr_len, l4_len); if (l4pkt) udp_input(IP_VER4, &src, &dst, l3id, l4pkt); break; @@ -417,16 +417,16 @@ void ipv4_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { netpkt_t* l4pkt = netpkt_view(pkt, hdr_len, l4_len); if (l4pkt) { switch (proto) { - case 1: + case PROTO_ICMP: icmp_input(l4pkt, src, dst); break; - case 2: + case PROTO_IGMP: igmp_input((uint8_t)ifindex, src, dst, l4pkt); break; - case 6: + case PROTO_TCP: tcp_input(IP_VER4, &src, &dst, cand[0]->l3_id, l4pkt); break; - case 17: + case PROTO_UDP: udp_input(IP_VER4, &src, &dst, cand[0]->l3_id, l4pkt); break; default: @@ -440,15 +440,15 @@ void ipv4_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { uint8_t l3id = cand[i]->l3_id; switch (proto) { netpkt_t* l4pkt; - case 1: + case PROTO_ICMP: l4pkt = netpkt_view(pkt, hdr_len, l4_len); if (l4pkt) icmp_input(l4pkt, src, dst); break; - case 6: + case PROTO_TCP: l4pkt = netpkt_view(pkt, hdr_len, l4_len); if (l4pkt) tcp_input(IP_VER4, &src, &dst, l3id, l4pkt); break; - case 17: + case PROTO_UDP: l4pkt = netpkt_view(pkt, hdr_len, l4_len); if (l4pkt) udp_input(IP_VER4, &src, &dst, l3id, l4pkt); break; @@ -472,16 +472,16 @@ void ipv4_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { if (l4pkt) { switch (proto) { - case 1: + case PROTO_ICMP: icmp_input(l4pkt, src, dst); break; - case 2: + case PROTO_IGMP: igmp_input((uint8_t)ifindex, src, dst, l4pkt); break; - case 6: + case PROTO_TCP: tcp_input(IP_VER4, &src, &dst, match_l3id, l4pkt); break; - case 17: + case PROTO_UDP: udp_input(IP_VER4, &src, &dst, match_l3id, l4pkt); break; default: @@ -497,15 +497,15 @@ void ipv4_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { uint8_t l3id = cand[i]->l3_id; switch (proto) { netpkt_t* l4pkt; - case 1: + case PROTO_ICMP: l4pkt = netpkt_view(pkt, hdr_len, l4_len); if (l4pkt) icmp_input(l4pkt, src, dst); break; - case 6: + case PROTO_TCP: l4pkt = netpkt_view(pkt, hdr_len, l4_len); if (l4pkt) tcp_input(IP_VER4, &src, &dst, l3id, l4pkt); break; - case 17: + case PROTO_UDP: l4pkt = netpkt_view(pkt, hdr_len, l4_len); if (l4pkt) udp_input(IP_VER4, &src, &dst, l3id, l4pkt); break; @@ -529,15 +529,15 @@ void ipv4_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { uint8_t l3id = v4->l3_id; switch (proto) { netpkt_t* l4pkt; - case 1: + case PROTO_ICMP: l4pkt = netpkt_view(pkt, hdr_len, l4_len); if (l4pkt) icmp_input(l4pkt, src, dst); break; - case 6: + case PROTO_TCP: l4pkt = netpkt_view(pkt, hdr_len, l4_len); if (l4pkt) tcp_input(IP_VER4, &src, &dst, l3id, l4pkt); break; - case 17: + case PROTO_UDP: l4pkt = netpkt_view(pkt, hdr_len, l4_len); if (l4pkt) udp_input(IP_VER4, &src, &dst, l3id, l4pkt); break; diff --git a/kernel/networking/internet_layer/ipv4_route.c b/kernel/networking/internet_layer/ipv4_route.c index 887aaa7d..057f464a 100644 --- a/kernel/networking/internet_layer/ipv4_route.c +++ b/kernel/networking/internet_layer/ipv4_route.c @@ -9,7 +9,6 @@ static bool v4_l3_ok_for_tx(l3_ipv4_interface_t* v4){ if (!v4->l2->is_up) return false; if (v4->mode == IPV4_CFG_DISABLED) return false; if (!v4->ip) return false; - if (!v4->port_manager) return false; return true; } diff --git a/kernel/networking/internet_layer/ipv6.c b/kernel/networking/internet_layer/ipv6.c index 102ef3bf..1ef6738b 100644 --- a/kernel/networking/internet_layer/ipv6.c +++ b/kernel/networking/internet_layer/ipv6.c @@ -145,7 +145,7 @@ static void icmpv6_send_error(uint8_t ifindex, const uint8_t src_ip[16], const u memcpy(buf + base, invoking, copy); - h->checksum =bswap16(checksum16_pipv6(src_ip, dst_ip, 58, buf, icmp_len)); + h->checksum =bswap16(checksum16_pipv6(src_ip, dst_ip, PROTO_ICMPV6, buf, icmp_len)); icmpv6_send_on_l2(ifindex, dst_ip, src_ip, dst_mac, buf, icmp_len, 64); @@ -590,7 +590,7 @@ void ipv6_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { if (ipv6_is_linklocal(ip6->src) && !ipv6_is_linklocal(ip6->dst) && !ipv6_is_multicast(ip6->dst) && - ip6->next_header != 58){ + ip6->next_header != PROTO_ICMPV6){ bool dst_is_local = false; l2_interface_t* l2 = l2_interface_find_by_index((uint8_t)ifindex); @@ -758,9 +758,9 @@ void ipv6_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { if (ok) { uint32_t need = 1; - if (nh == 6) need = 20; - else if (nh == 17) need = 8; - else if (nh == 58) need = 4; + if (nh == PROTO_TCP) need = 20; + else if (nh == PROTO_UDP) need = 8; + else if (nh == PROTO_ICMPV6) need = 4; if (frag_len - ulh_off >= need) has_ulh = 1; } } @@ -864,7 +864,7 @@ void ipv6_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { return; } - if (inner_nh == 58) { + if (inner_nh == PROTO_ICMPV6) { netpkt_t* l4pkt = netpkt_view(reassembled, payload_off, payload_size); if (l4pkt) icmpv6_input(ifindex, ip6->src, ip6->dst, ip6->hop_limit, src_mac, l4pkt); netpkt_unref(reassembled); @@ -912,8 +912,8 @@ void ipv6_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { if (!ipv6_is_linklocal(v6->ip) && ipv6_is_linkscope_mcast(ip6->dst)) continue; netpkt_t* l4pkt = netpkt_view(reassembled, payload_off, payload_size); if (!l4pkt) continue; - if (inner_nh == 17) udp_input(IP_VER6, ip6->src, ip6->dst, v6->l3_id, l4pkt); - else if (inner_nh == 6) tcp_input(IP_VER6, ip6->src, ip6->dst, v6->l3_id, l4pkt); + if (inner_nh == PROTO_UDP) udp_input(IP_VER6, ip6->src, ip6->dst, v6->l3_id, l4pkt); + else if (inner_nh == PROTO_TCP) tcp_input(IP_VER6, ip6->src, ip6->dst, v6->l3_id, l4pkt); else netpkt_unref(l4pkt); } @@ -934,8 +934,8 @@ void ipv6_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { if (match_count >= 1) { netpkt_t* l4pkt = netpkt_view(reassembled, payload_off, payload_size); if (l4pkt) { - if (inner_nh == 6) tcp_input(IP_VER6, ip6->src, ip6->dst, match_l3id, l4pkt); - else if (inner_nh == 17) udp_input(IP_VER6, ip6->src, ip6->dst, match_l3id, l4pkt); + if (inner_nh == PROTO_TCP) tcp_input(IP_VER6, ip6->src, ip6->dst, match_l3id, l4pkt); + else if (inner_nh == PROTO_UDP) udp_input(IP_VER6, ip6->src, ip6->dst, match_l3id, l4pkt); else netpkt_unref(l4pkt); } } @@ -944,7 +944,7 @@ void ipv6_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { return; } - if (nh == 58) { + if (nh == PROTO_ICMPV6) { netpkt_t* l4pkt = netpkt_view(pkt, l4_off, l4_len); if (l4pkt) icmpv6_input(ifindex, ip6->src, ip6->dst, ip6->hop_limit, src_mac, l4pkt); return; @@ -980,11 +980,11 @@ void ipv6_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { switch (nh) { netpkt_t* l4pkt; - case 17: + case PROTO_UDP: l4pkt = netpkt_view(pkt, l4_off, l4_len); if (l4pkt) udp_input(IP_VER6, ip6->src, ip6->dst, v6->l3_id, l4pkt); break; - case 6: + case PROTO_TCP: l4pkt = netpkt_view(pkt, l4_off, l4_len); if (l4pkt) tcp_input(IP_VER6, ip6->src, ip6->dst, v6->l3_id, l4pkt); break; @@ -1007,11 +1007,11 @@ void ipv6_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { if (match_count >= 1) { switch (nh) { netpkt_t* l4pkt; - case 6: + case PROTO_TCP: l4pkt = netpkt_view(pkt, l4_off, l4_len); if (l4pkt) tcp_input(IP_VER6, ip6->src, ip6->dst, match_l3id, l4pkt); break; - case 17: + case PROTO_UDP: l4pkt = netpkt_view(pkt, l4_off, l4_len); if (l4pkt) udp_input(IP_VER6, ip6->src, ip6->dst, match_l3id, l4pkt); break; diff --git a/kernel/networking/internet_layer/ipv6_route.c b/kernel/networking/internet_layer/ipv6_route.c index f3484cd7..215b24f2 100644 --- a/kernel/networking/internet_layer/ipv6_route.c +++ b/kernel/networking/internet_layer/ipv6_route.c @@ -12,7 +12,6 @@ static bool v6_l3_ok_for_tx(l3_ipv6_interface_t* v6, int dst_is_ll, int dst_is_l if (v6->is_localhost && !dst_is_loop) return false; if (ipv6_is_unspecified(v6->ip)) return false; if (v6->dad_state != IPV6_DAD_OK)return false; - if (!v6->port_manager) return false; int src_is_ll = ipv6_is_linklocal(v6->ip) ? 1 : 0; if (src_is_ll != dst_is_ll) return false; diff --git a/kernel/networking/internet_layer/mld.c b/kernel/networking/internet_layer/mld.c index b5bbdefe..f8414d0e 100644 --- a/kernel/networking/internet_layer/mld.c +++ b/kernel/networking/internet_layer/mld.c @@ -112,12 +112,12 @@ static bool mld_send_report(uint8_t ifindex, const uint8_t group[16], uint8_t re icmp[11] = 0; memcpy(icmp + 12, group, 16); - uint16_t csum = checksum16_pipv6(src_ip, dst_ip, 58, icmp, sizeof(icmp)); + uint16_t csum = checksum16_pipv6(src_ip, dst_ip, PROTO_ICMPV6, icmp, sizeof(icmp)); icmp[2] = (uint8_t)(csum >> 8); icmp[3] = (uint8_t)(csum & 0xFF); uint8_t hbh[8]; - hbh[0] = 58; + hbh[0] = PROTO_ICMPV6; hbh[1] = 0; hbh[2] = 5; hbh[3] = 2; diff --git a/kernel/networking/link_layer/ndp.c b/kernel/networking/link_layer/ndp.c index 73c151c6..912fa14b 100644 --- a/kernel/networking/link_layer/ndp.c +++ b/kernel/networking/link_layer/ndp.c @@ -471,7 +471,7 @@ static bool ndp_send_na_on(uint8_t ifindex, const uint8_t dst_ip[16], const uint opt->length = 1; memcpy(opt->mac, my_mac, 6); - na->hdr.checksum = bswap16(checksum16_pipv6(src_ip, dst_ip, 58, (const uint8_t*)buf, plen)); + na->hdr.checksum = bswap16(checksum16_pipv6(src_ip, dst_ip, PROTO_ICMPV6, (const uint8_t*)buf, plen)); uint8_t dst_mac[6]; if (ipv6_is_multicast(dst_ip)) ipv6_multicast_mac(dst_ip, dst_mac); @@ -510,7 +510,7 @@ static void ndp_send_ns_on(uint8_t ifindex, const uint8_t target_ip[16], const u uint8_t dst_ip[16]; ipv6_make_multicast(2, IPV6_MCAST_SOLICITED_NODE, target_ip, dst_ip); - ns->hdr.checksum = bswap16(checksum16_pipv6(src_ip, dst_ip, 58, (const uint8_t*)buf, plen)); + ns->hdr.checksum = bswap16(checksum16_pipv6(src_ip, dst_ip, PROTO_ICMPV6, (const uint8_t*)buf, plen)); uint8_t dst_mac[6]; ipv6_multicast_mac(dst_ip, dst_mac); @@ -563,7 +563,7 @@ static void ndp_send_rs_on(uint8_t ifindex) { if (mac) memcpy(opt->mac, mac, 6); else memset(opt->mac, 0, 6); - rs->hdr.checksum = bswap16(checksum16_pipv6(src_ip, dst_ip, 58, (const uint8_t*)buf, plen)); + rs->hdr.checksum = bswap16(checksum16_pipv6(src_ip, dst_ip, PROTO_ICMPV6, (const uint8_t*)buf, plen)); uint8_t dst_mac[6]; ipv6_multicast_mac(dst_ip, dst_mac); diff --git a/kernel/networking/net_logger/net_logger.c b/kernel/networking/net_logger/net_logger.c index d875a444..9ee9c3d5 100644 --- a/kernel/networking/net_logger/net_logger.c +++ b/kernel/networking/net_logger/net_logger.c @@ -45,14 +45,6 @@ static const char* bind_kind_str(SockBindKind k) { } } -static const char* dst_kind_str(SockDstKind k) { - switch (k) { - case DST_ENDPOINT: return "EP"; - case DST_DOMAIN: return "DNS"; - default: return ""; - } -} - void netlog_socket_event(const SocketExtraOptions* extra, const netlog_socket_event_t* e) { if (!extra) return; if (!e) return; @@ -115,15 +107,10 @@ void netlog_socket_event(const SocketExtraOptions* extra, const netlog_socket_ev uint16_t dport = 0; net_ep_split(&e->dst_ep, dip, (int)sizeof(dip), &dv6, &dport); - if (e->dst_kind == DST_DOMAIN && e->s0) { - if (dv6) kprintf("[NET][%s] %s host=%s port=%u dst=[%s]:%u r=%lld", c, a, e->s0, (uint32_t)e->u0, dip, (uint32_t)dport, (long long)e->i0); - else kprintf("[NET][%s] %s host=%s port=%u dst=%s:%u r=%lld", c, a, e->s0, (uint32_t)e->u0, dip, (uint32_t)dport, (long long)e->i0); - } else { - if (dv6) kprintf("[NET][%s] %s dst=[%s]:%u r=%lld", c, a, dip, (uint32_t)dport, (long long)e->i0); - else kprintf("[NET][%s] %s dst=%s:%u r=%lld", c, a, dip, (uint32_t)dport, (long long)e->i0); - } + if (dv6) kprintf("[NET][%s] %s dst=[%s]:%u r=%lld", c, a, dip, (uint32_t)dport, (long long)e->i0); + else kprintf("[NET][%s] %s dst=%s:%u r=%lld", c, a, dip, (uint32_t)dport, (long long)e->i0); } else { - kprintf("[NET][%s] %s kind=%s port=%u", c, a, dst_kind_str(e->dst_kind), (uint32_t)e->u0); + kprintf("[NET][%s] %s port=%u", c, a, (uint32_t)e->u0); } return; } @@ -177,7 +164,7 @@ void netlog_socket_event(const SocketExtraOptions* extra, const netlog_socket_ev } if (e->action == NETLOG_ACT_SENDTO) { - kprintf("[NET][%s] %s kind=%s port=%u n=%u", c, a, dst_kind_str(e->dst_kind), (uint32_t)e->u0, (uint32_t)e->u1); + kprintf("[NET][%s] %s port=%u n=%u", c, a, (uint32_t)e->u0, (uint32_t)e->u1); return; } @@ -227,10 +214,7 @@ void netlog_socket_event(const SocketExtraOptions* extra, const netlog_socket_ev } if (e->action == NETLOG_ACT_CONNECT) { - if (e->dst_kind == DST_DOMAIN && e->s0) { - if (dst_v6) kprintf("[NET][%s] %s host=%s port=%u dst=[%s]:%u r=%lld", c, a, e->s0, (uint32_t)e->u0, dst_ip, (uint32_t)dst_port, (long long)e->i0); - else kprintf("[NET][%s] %s host=%s port=%u dst=%s:%u r=%lld", c, a, e->s0, (uint32_t)e->u0, dst_ip, (uint32_t)dst_port, (long long)e->i0); - } else if (dst_v6) { + if (dst_v6) { kprintf("[NET][%s] %s dst=[%s]:%u r=%lld", c, a, dst_ip, (uint32_t)dst_port, (long long)e->i0); } else { kprintf("[NET][%s] %s dst=%s:%u r=%lld", c, a, dst_ip, (uint32_t)dst_port, (long long)e->i0); @@ -239,12 +223,8 @@ void netlog_socket_event(const SocketExtraOptions* extra, const netlog_socket_ev } if (e->action == NETLOG_ACT_SENDTO) { - if (e->dst_kind == DST_DOMAIN && e->s0) - kprintf("[NET][%s] %s host=%s port=%u n=%u", c, a, e->s0, (uint32_t)e->u0, (uint32_t)e->u1); - else if (dst_v6) - kprintf("[NET][%s] %s dst=[%s]:%u n=%u", c, a, dst_ip, (uint32_t)dst_port, (uint32_t)e->u1); - else - kprintf("[NET][%s] %s dst=%s:%u n=%u", c, a, dst_ip, (uint32_t)dst_port, (uint32_t)e->u1); + if (dst_v6) kprintf("[NET][%s] %s dst=[%s]:%u n=%u", c, a, dst_ip, (uint32_t)dst_port, (uint32_t)e->u1); + else kprintf("[NET][%s] %s dst=%s:%u n=%u", c, a, dst_ip, (uint32_t)dst_port, (uint32_t)e->u1); return; } diff --git a/kernel/networking/net_logger/net_logger.h b/kernel/networking/net_logger/net_logger.h index 17dedc27..2cf5d5e0 100644 --- a/kernel/networking/net_logger/net_logger.h +++ b/kernel/networking/net_logger/net_logger.h @@ -45,7 +45,6 @@ typedef struct netlog_socket_event_t { SockBindSpec bind_spec; - SockDstKind dst_kind; net_l4_endpoint dst_ep; net_l4_endpoint remote_ep; diff --git a/kernel/networking/port_manager.c b/kernel/networking/port_manager.c deleted file mode 100644 index a516b8c6..00000000 --- a/kernel/networking/port_manager.c +++ /dev/null @@ -1,129 +0,0 @@ -#include "networking/port_manager.h" -#include "types.h" -#include "random/random.h" -#include "net/network_types.h" - -static inline bool proto_valid(protocol_t proto) { - return (uint32_t)proto < PROTO_COUNT; -} - -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) { - pm->tab[pr][p].used = false; - pm->tab[pr][p].pid = PORT_FREE_OWNER; - pm->tab[pr][p].handler = NULL; - } - } -} - -int port_alloc_ephemeral(port_manager_t* pm, - protocol_t proto, - uint16_t pid, - port_recv_handler_t handler) -{ - if (!pm || !proto_valid(proto)) return -1; - - rng_t rng; - rng_init_random(&rng); - uint32_t seed = rng_next32(&rng); - - 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 = &pm->tab[proto][p]; - if (!e->used) { - e->used = true; - e->pid = pid; - e->handler = handler; - return (int)p; - } - } - return -1; -} - -bool port_bind_manual(port_manager_t* pm, - protocol_t proto, - uint16_t port, - uint16_t pid, - port_recv_handler_t handler) -{ - 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; - e->handler = handler; - return true; -} - -bool port_unbind(port_manager_t* pm, - protocol_t proto, - uint16_t port, - uint16_t pid) -{ - 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; - e->handler = NULL; - return true; -} - -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 = &pm->tab[pr][p]; - if (e->used && e->pid == pid) { - e->used = false; - e->pid = PORT_FREE_OWNER; - e->handler = NULL; - } - } - } -} - -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(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(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; -} - -uint32_t port_deliver(const port_manager_t* pm, - protocol_t proto, - uint16_t port, - uint8_t ifindex, - ip_version_t ipver, - const void* src_ip_addr, - const void* dst_ip_addr, - netpkt_t* pkt, - uint16_t src_port, - uint16_t dst_port, - bool* handled) -{ - if (handled) *handled = false; - if (!pm || !proto_valid(proto)) return 0; - - port_recv_handler_t handler = port_get_handler(pm, proto, port); - if (!handler) return 0; - - if (handled) *handled = true; - return handler(ifindex, ipver, src_ip_addr, dst_ip_addr, pkt, src_port, dst_port); -} diff --git a/kernel/networking/port_manager.h b/kernel/networking/port_manager.h deleted file mode 100644 index e035f20d..00000000 --- a/kernel/networking/port_manager.h +++ /dev/null @@ -1,74 +0,0 @@ -#pragma once -#include "types.h" -#include "net/network_types.h" -#include "networking/netpkt.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define MAX_PORTS 65536 -#define PORT_MIN_EPHEMERAL 49152 -#define PORT_MAX_EPHEMERAL 65535 -#define PORT_FREE_OWNER 0xFFFF - -#define PROTO_COUNT 2 - -typedef uint32_t (*port_recv_handler_t)( - uint8_t ifindex, - ip_version_t ipver, - const void* src_ip_addr, - const void* dst_ip_addr, - netpkt_t* pkt, - uint16_t src_port, - uint16_t dst_port -); - -typedef struct { - uint16_t pid; - port_recv_handler_t handler; - bool used; -} port_entry_t; - -typedef struct { - port_entry_t tab[PROTO_COUNT][MAX_PORTS]; -} port_manager_t; - -void port_manager_init(port_manager_t* pm); - -int port_alloc_ephemeral(port_manager_t* pm, - protocol_t proto, - uint16_t pid, - port_recv_handler_t handler); - -bool port_bind_manual(port_manager_t* pm, - protocol_t proto, - uint16_t port, - uint16_t pid, - port_recv_handler_t handler); - -bool port_unbind(port_manager_t* pm, - protocol_t proto, - uint16_t port, - uint16_t pid); - -void port_unbind_all(port_manager_t* pm, uint16_t pid); - -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); -uint32_t port_deliver(const port_manager_t* pm, - protocol_t proto, - uint16_t port, - uint8_t ifindex, - ip_version_t ipver, - const void* src_ip_addr, - const void* dst_ip_addr, - netpkt_t* pkt, - uint16_t src_port, - uint16_t dst_port, - bool* handled); - -#ifdef __cplusplus -} -#endif diff --git a/kernel/networking/processes/net_proc.c b/kernel/networking/processes/net_proc.c index fc223801..9b3ddb6c 100644 --- a/kernel/networking/processes/net_proc.c +++ b/kernel/networking/processes/net_proc.c @@ -15,7 +15,7 @@ #include "networking/internet_layer/ipv4_utils.h" #include "networking/internet_layer/ipv6_utils.h" -#include "networking/transport_layer/csocket_udp.h" +#include "networking/transport_layer/csocket.h" #include "networking/transport_layer/trans_utils.h" #include "networking/application_layer/csocket_http_client.h" @@ -36,17 +36,16 @@ #define PROBE_INTERVAL_MS 50 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(), NULL); - if (!sock) + socket_handle_t sock = {0}; + if (!create_socket(SOCKET_CLIENT, PROTO_UDP, NULL, &sock)) return 0; net_l4_endpoint dst; make_ep(probe_ip, probe_port, IP_VER4, &dst); static const char greeting[] = "hello"; - if (socket_sendto_udp(sock, DST_ENDPOINT, &dst, 0, greeting, sizeof(greeting)) < 0) { - socket_close_udp(sock); - socket_destroy_udp(sock); + if (send_to_socket(&sock, &dst, greeting, sizeof(greeting)) < 0) { + close_socket(&sock); return 0; } @@ -56,14 +55,13 @@ static int udp_probe_server(uint32_t probe_ip, uint16_t probe_port, net_l4_endpo int64_t recvd = 0; while (waited < PROBE_TIMEOUT_MS) { - recvd = socket_recvfrom_udp(sock, recv_buf, sizeof(recv_buf), &src); + recvd = receive_from_socket(&sock, recv_buf, sizeof(recv_buf), &src); if (recvd > 0) break; msleep(PROBE_INTERVAL_MS); waited += PROBE_INTERVAL_MS; } - socket_close_udp(sock); - socket_destroy_udp(sock); + close_socket(&sock); if (recvd <= 0) return 0; if (out_l4) *out_l4 = src; @@ -94,11 +92,10 @@ static int net_has_ready_address(void) { } static void run_http_server() { - uint16_t pid = get_current_proc_pid(); SocketExtraOptions opt = {0}; opt.debug_level = SOCK_DBG_ALL; opt.flags = SOCK_OPT_DEBUG; - http_server_handle_t srv = http_server_create(pid, &opt, NULL); + http_server_handle_t srv = http_server_create(&opt, NULL); if (!srv) { stop_current_process(1); return; @@ -169,7 +166,7 @@ static void test_http(const net_l4_endpoint* ep) { kprintf("[HTTP] GET %s:%i", ip_str, HTTP_PORT); } - http_client_handle_t cli = http_client_create((uint16_t)get_current_proc_pid(), NULL, NULL); + http_client_handle_t cli = http_client_create(NULL, NULL); if (!cli) { kprintf("[HTTP] http_client_create FAIL"); return; @@ -185,7 +182,7 @@ static void test_http(const net_l4_endpoint* ep) { return; } - int32_t rc = http_client_connect(cli, DST_ENDPOINT, &e, HTTP_PORT); + int32_t rc = http_client_connect_endpoint(cli, &e); if (rc < 0) { http_client_destroy(cli); return; diff --git a/kernel/networking/transport_layer/csocket.c b/kernel/networking/transport_layer/csocket.c index b89fb13e..99581af0 100644 --- a/kernel/networking/transport_layer/csocket.c +++ b/kernel/networking/transport_layer/csocket.c @@ -1,205 +1,217 @@ #include "csocket.h" #include "csocket_tcp.h" #include "csocket_udp.h" -#include "memory/page_allocator.h" -#include "console/kio.h" -#include "data/struct/hashmap.h" +#include "socket_core.h" +#include "process/scheduler.h" +#include "std/memory.h" -uint16_t socket_ids; - -void *sock_mem_page; - -hash_map_t *map; - -typedef struct ksock_handle_t { - uint16_t id; - net_l4_endpoint connection; - protocol_t protocol; - void* sh; - uint16_t pid; -} ksock_handle_t; - -void* custom_alloc(size_t size){ - return kalloc(sock_mem_page, size, ALIGN_64B, MEM_PRIV_KERNEL); -} - -static inline void check_mem(){ - if (!sock_mem_page) - sock_mem_page = palloc(PAGE_SIZE, MEM_PRIV_KERNEL, MEM_RW, false); - if (!map){ - map = hash_map_create_alloc(128, custom_alloc, kfree); - } -} - -bool create_socket(Socket_Role role, protocol_t protocol, const SocketExtraOptions* extra, uint16_t pid, SocketHandle *out_handle){ - check_mem(); +bool create_socket(Socket_Role role, protocol_t protocol, const SocketExtraOptions* extra, SocketHandle *out_handle){ if (!out_handle) return false; - socket_handle_t *in_handle = {}; - switch (protocol) { - case PROTO_UDP: - in_handle = udp_socket_create(role, pid, extra); - break; - case PROTO_TCP: - in_handle = socket_tcp_create(role, pid, extra); - break; + memset(out_handle, 0, sizeof(*out_handle)); + + if (protocol == PROTO_NONE) { + //TODO PROTO_NONE for RAW SET or L2 + return false; } - if (!in_handle){ - kprintf("[SOCKET] failed to create socket for %i",pid); + if (protocol != PROTO_TCP && protocol != PROTO_UDP) return false; + + uint16_t pid = get_current_proc_pid(); + ksocket_t* socket = NULL; + if (!socket_core_alloc(role, protocol, pid, &socket)) return false; + + socket_impl_t impl = NULL; + socket_impl_destroy_fn destroy = NULL; + socket_impl_close_fn close = NULL; + socket_impl_setopt_fn setopt = NULL; + socket_impl_getopt_fn getopt = NULL; + + if (protocol == PROTO_UDP) { + impl = udp_socket_create(socket, role, pid, extra); + destroy = socket_destroy_udp; + close = socket_close_udp; + setopt = socket_setopt_udp; + getopt = socket_getopt_udp; + } else if (protocol == PROTO_TCP) { + impl = socket_tcp_create(socket, role, pid, extra); + destroy = socket_destroy_tcp; + close = socket_close_tcp; + setopt = socket_setopt_tcp; + getopt = socket_getopt_tcp; + } + if (!impl) { + socket_core_close_socket(socket); return false; } - out_handle->id = socket_ids++; - out_handle->connection = (net_l4_endpoint){}; - out_handle->protocol = protocol; - - kprintf("Allocating"); - ksock_handle_t *sh = (ksock_handle_t*)custom_alloc(sizeof(ksock_handle_t)); - sh->sh = in_handle; - sh->id = out_handle->id; - sh->protocol = protocol; - sh->pid = pid; - - if (hash_map_put(map, &out_handle->id, sizeof(uint16_t), sh) < 0){ - kprint("Failed to save socket"); + + if (!socket_core_attach_impl(socket, impl, destroy, close, setopt, getopt, out_handle)) { + destroy(impl); + socket_core_close_socket(socket); return false; } return true; } -int32_t bind_socket(SocketHandle *handle, uint16_t port, ip_version_t ip_version, uint16_t pid){ - check_mem(); - ksock_handle_t *sh = (ksock_handle_t*)hash_map_get(map, &handle->id, sizeof(uint16_t)); - if (sh->pid != pid){ - kprintf("[SOCKET, error] illegal socket binding"); - return 0; - } - protocol_t protocol = sh->protocol; - SockBindSpec *spec = kalloc(sock_mem_page, sizeof(SockBindSpec), ALIGN_64B, MEM_PRIV_KERNEL); - spec->kind = BIND_IP; - spec->ver = ip_version; - memset(spec->ip, 0, sizeof(spec->ip)); - if (ip_version == IP_VER4) memcpy(spec->ip, handle->connection.ip, 4); - else if (ip_version == IP_VER6) memcpy(spec->ip, handle->connection.ip, 16); - int32_t res = -1; - switch (protocol) { - case PROTO_TCP: - res = socket_bind_tcp_ex(sh->sh, spec, port); - break; - case PROTO_UDP: - res = socket_bind_udp(sh->sh, spec, port); - break; +int32_t bind_socket(SocketHandle *handle, uint16_t port, ip_version_t ip_version){ + ksocket_t* socket = socket_core_get(handle, get_current_proc_pid()); + if (!socket) return SOCK_ERR_INVAL; + + SockBindSpec spec; + memset(&spec, 0, sizeof(spec)); + spec.kind = BIND_IP; + spec.ver = ip_version; + if (handle) { + if (ip_version == IP_VER4) memcpy(spec.ip, handle->connection.ip, 4); + else if (ip_version == IP_VER6) memcpy(spec.ip, handle->connection.ip, 16); + else spec.kind = BIND_ANY; } + + int32_t res = SOCK_ERR_PROTO; + if (socket_core_protocol(socket) == PROTO_TCP) res = socket_bind_tcp(socket_core_impl(socket), &spec, port); + else if (socket_core_protocol(socket) == PROTO_UDP) res = socket_bind_udp(socket_core_impl(socket), &spec, port); + + socket_core_put(socket); return res; } -int32_t connect_socket(SocketHandle *handle, uint8_t dst_kind, const void* dst, uint16_t port, uint16_t pid){ - check_mem(); - ksock_handle_t *sh = (ksock_handle_t*)hash_map_get(map, &handle->id, sizeof(uint16_t)); - if (sh->pid != pid){ - kprintf("[SOCKET, error] illegal socket connection"); - return 0; - } - protocol_t protocol = sh->protocol; - switch (protocol) { - case PROTO_TCP: - return socket_connect_tcp_ex(sh->sh, dst_kind, dst, port); - break; - case PROTO_UDP: - kprintf("[SOCKET] connect is a TCP-only function and isn't needed in UDP sockets"); - return -1; - } - return -1; +int32_t bind_socket_spec(SocketHandle* handle, const SockBindSpec* spec, uint16_t port){ + if (!spec) return SOCK_ERR_INVAL; + ksocket_t* socket = socket_core_get(handle, get_current_proc_pid()); + if (!socket) return SOCK_ERR_INVAL; + + int32_t res = SOCK_ERR_PROTO; + if (socket_core_protocol(socket) == PROTO_TCP) res = socket_bind_tcp(socket_core_impl(socket), spec, port); + else if (socket_core_protocol(socket) == PROTO_UDP) res = socket_bind_udp(socket_core_impl(socket), spec, port); + + socket_core_put(socket); + return res; } -int64_t send_on_socket(SocketHandle *handle, uint8_t dst_kind, const void* dst, uint16_t port, void* buf, uint64_t len, uint16_t pid){ - check_mem(); - ksock_handle_t *sh = (ksock_handle_t*)hash_map_get(map, &handle->id, sizeof(uint16_t)); - if (sh->pid != pid){ - kprintf("[SOCKET, error] illegal socket send"); - return 0; - } - protocol_t protocol = sh->protocol; - switch (protocol) { - case PROTO_TCP: - return socket_send_tcp(sh->sh, buf, len); - break; - case PROTO_UDP: - return socket_sendto_udp(sh->sh, dst_kind, dst, port, buf, len); - break; +int32_t connect_socket(SocketHandle *handle, const net_l4_endpoint* dst){ + ksocket_t* socket = socket_core_get(handle, get_current_proc_pid()); + if (!socket) return SOCK_ERR_INVAL; + + int32_t res = SOCK_ERR_PROTO; + if (socket_core_protocol(socket) == PROTO_TCP) { + res = socket_connect_tcp(socket_core_impl(socket), dst); + if (res == SOCK_OK && handle) socket_get_remote_ep_tcp(socket_core_impl(socket), &handle->connection); } - return 0; + + socket_core_put(socket); + return res; } -int64_t receive_from_socket(SocketHandle *handle, void* buf, uint64_t len, net_l4_endpoint* out_src, uint16_t pid){ - check_mem(); - ksock_handle_t *sh = (ksock_handle_t*)hash_map_get(map, &handle->id, sizeof(uint16_t)); - if (sh->pid != pid){ - kprintf("[SOCKET, error] illegal socket receive %i, %i",sh->id,pid); - return 0; - } - protocol_t protocol = sh->protocol; - switch (protocol) { - case PROTO_TCP: - return socket_recv_tcp(sh->sh, buf, len); - break; - case PROTO_UDP: - return socket_recvfrom_udp(sh->sh, buf, len, out_src); - break; - } - return 0; +int64_t send_on_socket(SocketHandle *handle, const void* buf, uint64_t len){ + ksocket_t* socket = socket_core_get(handle, get_current_proc_pid()); + if (!socket) return SOCK_ERR_INVAL; + + int64_t res = SOCK_ERR_PROTO; + if (socket_core_protocol(socket) == PROTO_TCP) res = socket_send_tcp(socket_core_impl(socket), buf, len); + else if (socket_core_protocol(socket) == PROTO_UDP) res = socket_sendto_udp(socket_core_impl(socket), NULL, buf, len); + + socket_core_put(socket); + return res; } -int32_t close_socket(SocketHandle *handle, uint16_t pid){ - check_mem(); - ksock_handle_t *sh = (ksock_handle_t*)hash_map_get(map, &handle->id, sizeof(uint16_t)); - if (sh->pid != pid){ - kprintf("[SOCKET, error] illegal socket close"); - return 0; - } - protocol_t protocol = sh->protocol; - switch (protocol) { - case PROTO_TCP: - return socket_close_tcp(sh->sh); - break; - case PROTO_UDP: - return socket_close_udp(sh->sh); - break; - } - return 0; +int64_t send_to_socket(SocketHandle *handle, const net_l4_endpoint* dst, const void* buf, uint64_t len){ + if (!dst) return SOCK_ERR_INVAL; + ksocket_t* socket = socket_core_get(handle, get_current_proc_pid()); + if (!socket) return SOCK_ERR_INVAL; + + int64_t res = SOCK_ERR_PROTO; + if (socket_core_protocol(socket) == PROTO_UDP) res = socket_sendto_udp(socket_core_impl(socket), dst, buf, len); + + socket_core_put(socket); + return res; } -int32_t listen_on(SocketHandle *handle, int32_t backlog, uint16_t pid){ - check_mem(); - ksock_handle_t *sh = (ksock_handle_t*)hash_map_get(map, &handle->id, sizeof(uint16_t)); - if (sh->pid != pid){ - kprintf("[SOCKET, error] illegal socket listen"); - return 0; - } - protocol_t protocol = sh->protocol; - switch (protocol) { - case PROTO_TCP: - return socket_listen_tcp(sh->sh, backlog); - break; - case PROTO_UDP: - kprintf("[SOCKET] listen is a TCP-only function and isn't needed in UDP sockets"); - break; - } - return 0; +int64_t receive_from_socket(SocketHandle *handle, void* buf, uint64_t len, net_l4_endpoint* out_src){ + ksocket_t* socket = socket_core_get(handle, get_current_proc_pid()); + if (!socket) return SOCK_ERR_INVAL; + + int64_t res = SOCK_ERR_PROTO; + if (socket_core_protocol(socket) == PROTO_TCP) res = socket_recv_tcp(socket_core_impl(socket), buf, len); + else if (socket_core_protocol(socket) == PROTO_UDP) res = socket_recvfrom_udp(socket_core_impl(socket), buf, len, out_src); + + socket_core_put(socket); + return res; +} + +int32_t set_socket_option(SocketHandle *handle, int32_t opt, const void* value, uint32_t len){ + ksocket_t* socket = socket_core_get(handle, get_current_proc_pid()); + if (!socket) return SOCK_ERR_INVAL; + int32_t res = socket_core_set_option(socket, opt, value, len); + socket_core_put(socket); + return res; +} + +int32_t get_socket_option(SocketHandle *handle, int32_t opt, void* value, uint32_t* len){ + ksocket_t* socket = socket_core_get(handle, get_current_proc_pid()); + if (!socket) return SOCK_ERR_INVAL; + int32_t res = socket_core_get_option(socket, opt, value, len); + socket_core_put(socket); + return res; +} + +uint16_t get_socket_local_port(SocketHandle* handle){ + ksocket_t* socket = socket_core_get(handle, get_current_proc_pid()); + if (!socket) return 0; + + uint16_t port = 0; + if (socket_core_protocol(socket) == PROTO_TCP) port = socket_get_local_port_tcp(socket_core_impl(socket)); + else if (socket_core_protocol(socket) == PROTO_UDP) port = socket_get_local_port_udp(socket_core_impl(socket)); + + socket_core_put(socket); + return port; } -void accept_on_socket(SocketHandle *handle, uint16_t pid){ - check_mem(); - ksock_handle_t *sh = (ksock_handle_t*)hash_map_get(map, &handle->id, sizeof(uint16_t)); - if (sh->pid != pid){ - kprintf("[SOCKET, error] illegal socket accept"); - return; +bool get_socket_remote_endpoint(SocketHandle *handle, net_l4_endpoint* out){ + if (!out) return false; + ksocket_t* socket = socket_core_get(handle, get_current_proc_pid()); + if (!socket) return false; + + bool ok = true; + if (socket_core_protocol(socket) == PROTO_TCP) socket_get_remote_ep_tcp(socket_core_impl(socket), out); + else if (socket_core_protocol(socket) == PROTO_UDP) socket_get_remote_ep_udp(socket_core_impl(socket), out); + else ok = false; + + socket_core_put(socket); + return ok; +} + +int32_t close_socket(SocketHandle *handle){ + return socket_core_close_handle(handle, get_current_proc_pid()); +} + +int32_t listen_on(SocketHandle *handle, int32_t backlog){ + ksocket_t* socket = socket_core_get(handle, get_current_proc_pid()); + if (!socket) return SOCK_ERR_INVAL; + + int32_t res = SOCK_ERR_PROTO; + if (socket_core_protocol(socket) == PROTO_TCP) res = socket_listen_tcp(socket_core_impl(socket), backlog); + + socket_core_put(socket); + return res; +} + +bool accept_on_socket(SocketHandle *handle, SocketHandle* out_child) { + if (!out_child) return false; + memset(out_child, 0, sizeof(*out_child)); + ksocket_t* listener = socket_core_get(handle, get_current_proc_pid()); + if (!listener) return false; + if (socket_core_protocol(listener) != PROTO_TCP) { + socket_core_put(listener); + return false; } - protocol_t protocol = sh->protocol; - switch (protocol) { - case PROTO_TCP: - socket_accept_tcp(sh->sh); - break; - case PROTO_UDP: - kprintf("[SOCKET] accept is a TCP-only function and isn't needed in UDP sockets"); - break; + + ksocket_t* child = socket_accept_tcp(socket_core_impl(listener)); + if (!child) { + socket_core_put(listener); + return false; } + + socket_core_export_handle(child, out_child); + socket_get_remote_ep_tcp(socket_core_impl(child), &out_child->connection); + socket_core_put(child); + socket_core_put(listener); + return true; } \ No newline at end of file diff --git a/kernel/networking/transport_layer/csocket.h b/kernel/networking/transport_layer/csocket.h index 0434c30b..214e688c 100644 --- a/kernel/networking/transport_layer/csocket.h +++ b/kernel/networking/transport_layer/csocket.h @@ -3,14 +3,32 @@ #include "types.h" #include "net/network_types.h" #include "net/socket_types.h" +#include "networking/transport_layer/socket_core.h" -bool create_socket(Socket_Role role, protocol_t protocol, const SocketExtraOptions* extra, uint16_t pid, SocketHandle *out_handle); -int32_t bind_socket(SocketHandle *handle, uint16_t port, ip_version_t ip_vers, uint16_t pid); -int32_t connect_socket(SocketHandle *handle, uint8_t dst_kind, const void* dst, uint16_t port, uint16_t pid); +#ifdef __cplusplus +extern "C" { +#endif -int64_t send_on_socket(SocketHandle *sh, uint8_t dst_kind, const void* dst, uint16_t port, void* buf, uint64_t len, uint16_t pid); -int64_t receive_from_socket(SocketHandle *sh, void* buf, uint64_t len, net_l4_endpoint* out_src, uint16_t pid); -int32_t close_socket(SocketHandle *sh, uint16_t pid); +typedef SocketHandle socket_handle_t; -int32_t listen_on(SocketHandle *sh, int32_t backlog, uint16_t pid); -void accept_on_socket(SocketHandle *sh, uint16_t pid); \ No newline at end of file +bool create_socket(Socket_Role role, protocol_t protocol, const SocketExtraOptions* extra, SocketHandle* out_handle); +int32_t bind_socket(SocketHandle *handle, uint16_t port, ip_version_t ip_vers); +int32_t bind_socket_spec(SocketHandle *handle, const SockBindSpec* spec, uint16_t port); +int32_t connect_socket(SocketHandle *handle, const net_l4_endpoint* dst); + +int64_t send_on_socket(SocketHandle *handle, const void* buf, uint64_t len); +int64_t send_to_socket(SocketHandle *handle, const net_l4_endpoint* dst, const void* buf, uint64_t len); +int64_t receive_from_socket(SocketHandle *handle, void* buf, uint64_t len, net_l4_endpoint* out_src); +int32_t set_socket_option(SocketHandle *handle, int32_t opt, const void* value, uint32_t len); +int32_t get_socket_option(SocketHandle *handle, int32_t opt, void* value, uint32_t* len); + +uint16_t get_socket_local_port(SocketHandle *handle); +bool get_socket_remote_endpoint(SocketHandle *handle, net_l4_endpoint* out); +int32_t close_socket(SocketHandle *handle); + +int32_t listen_on(SocketHandle *handle, int32_t backlog); +bool accept_on_socket(SocketHandle *handle, SocketHandle* out_child); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/kernel/networking/transport_layer/csocket_tcp.cpp b/kernel/networking/transport_layer/csocket_tcp.cpp index 63659d87..52c7aa2c 100644 --- a/kernel/networking/transport_layer/csocket_tcp.cpp +++ b/kernel/networking/transport_layer/csocket_tcp.cpp @@ -4,83 +4,104 @@ extern "C" { -socket_handle_t socket_tcp_create(uint8_t role, uint32_t pid, const SocketExtraOptions* extra) { - return reinterpret_cast(new TCPSocket(role, pid, extra)); +socket_impl_t socket_tcp_create(ksocket_t* owner, uint8_t role, uint32_t pid, const SocketExtraOptions* extra) { + if (!owner) return nullptr; + return reinterpret_cast(new TCPSocket(owner, role, pid, extra)); } -int32_t socket_bind_tcp_ex(socket_handle_t sh, const SockBindSpec* spec, uint16_t port) { +int32_t socket_bind_tcp(socket_impl_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) { +int32_t socket_listen_tcp(socket_impl_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) { +ksocket_t* socket_accept_tcp(socket_impl_t sh) { if (!sh) return nullptr; - TCPSocket* srv = reinterpret_cast(sh); - TCPSocket* client = srv->accept(); - return reinterpret_cast(client); + return reinterpret_cast(sh)->accept(); } -int32_t socket_connect_tcp_ex(socket_handle_t sh, uint8_t dst_kind, const void* dst, uint16_t port) { +int32_t socket_connect_tcp(socket_impl_t sh, const net_l4_endpoint* dst) { if (!sh || !dst) return SOCK_ERR_INVAL; - return reinterpret_cast(sh)->connect(static_cast(dst_kind), dst, port); + return reinterpret_cast(sh)->connect(dst); } -int64_t socket_send_tcp(socket_handle_t sh, const void* buf, uint64_t len) { +int64_t socket_send_tcp(socket_impl_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) { +int64_t socket_recv_tcp(socket_impl_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) { +int32_t socket_close_tcp(socket_impl_t sh) { if (!sh) return SOCK_ERR_INVAL; return reinterpret_cast(sh)->close(); } -void socket_destroy_tcp(socket_handle_t sh) { +int32_t socket_setopt_tcp(socket_impl_t sh, int32_t opt, const void* value, uint32_t len) { + if (!sh) return SOCK_ERR_INVAL; + return reinterpret_cast(sh)->set_option(opt, value, len); +} + +int32_t socket_getopt_tcp(socket_impl_t sh, int32_t opt, void* value, uint32_t* len) { + if (!sh) return SOCK_ERR_INVAL; + return reinterpret_cast(sh)->get_option(opt, value, len); +} + +void socket_destroy_tcp(socket_impl_t sh) { if (!sh) return; delete reinterpret_cast(sh); } -uint16_t socket_get_local_port_tcp(socket_handle_t sh) { +const SocketExtraOptions* socket_tcp_extra_options(socket_impl_t sh) { + if (!sh) return nullptr; + return reinterpret_cast(sh)->get_extra_options(); +} + +uint32_t tcp_accept_enqueue(ksocket_t* listener, uint8_t ifindex, ip_version_t ipver, const void* src_ip_addr, const void* dst_ip_addr, uint16_t src_port, uint16_t dst_port) { + if (!listener) return 0; + socket_impl_t sh = socket_core_impl(listener); + if (!sh) return 0; + return reinterpret_cast(sh)->queue_accepted_child(ifindex, ipver, src_ip_addr, dst_ip_addr, src_port, dst_port); +} + +uint16_t socket_get_local_port_tcp(socket_impl_t sh) { if (!sh) return 0; return reinterpret_cast(sh)->get_local_port(); } -uint16_t socket_get_remote_port_tcp(socket_handle_t sh) { +uint16_t socket_get_remote_port_tcp(socket_impl_t sh) { if (!sh) return 0; return reinterpret_cast(sh)->get_remote_port(); } -void socket_get_remote_ep_tcp(socket_handle_t sh, net_l4_endpoint* out) { +void socket_get_remote_ep_tcp(socket_impl_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) { +uint8_t socket_get_protocol_tcp(socket_impl_t sh) { if (!sh) return 0xFF; return reinterpret_cast(sh)->get_protocol(); } -uint8_t socket_get_role_tcp(socket_handle_t sh) { +uint8_t socket_get_role_tcp(socket_impl_t sh) { if (!sh) return 0xFF; return reinterpret_cast(sh)->get_role(); } -bool socket_is_bound_tcp(socket_handle_t sh) { +bool socket_is_bound_tcp(socket_impl_t sh) { if (!sh) return false; return reinterpret_cast(sh)->is_bound(); } -bool socket_is_connected_tcp(socket_handle_t sh) { +bool socket_is_connected_tcp(socket_impl_t sh) { if (!sh) return false; return reinterpret_cast(sh)->is_connected(); } diff --git a/kernel/networking/transport_layer/csocket_tcp.h b/kernel/networking/transport_layer/csocket_tcp.h index bbc7deb2..d2f8736a 100644 --- a/kernel/networking/transport_layer/csocket_tcp.h +++ b/kernel/networking/transport_layer/csocket_tcp.h @@ -1,32 +1,34 @@ #pragma once #include "types.h" #include "net/network_types.h" -#include "socket.hpp" +#include "socket_core.h" #include "net/socket_types.h" #ifdef __cplusplus extern "C" { #endif -typedef void* socket_handle_t; +socket_impl_t socket_tcp_create(ksocket_t* owner, uint8_t role, uint32_t pid, const SocketExtraOptions* extra); +int32_t socket_bind_tcp(socket_impl_t sh, const SockBindSpec* spec, uint16_t port); +int32_t socket_listen_tcp(socket_impl_t sh, int32_t backlog); +ksocket_t* socket_accept_tcp(socket_impl_t sh); +int32_t socket_connect_tcp(socket_impl_t sh, const net_l4_endpoint* dst); +int64_t socket_send_tcp(socket_impl_t sh, const void* buf, uint64_t len); +int64_t socket_recv_tcp(socket_impl_t sh, void* buf, uint64_t len); +int32_t socket_close_tcp(socket_impl_t sh); +int32_t socket_setopt_tcp(socket_impl_t sh, int32_t opt, const void* value, uint32_t len); +int32_t socket_getopt_tcp(socket_impl_t sh, int32_t opt, void* value, uint32_t* len); +void socket_destroy_tcp(socket_impl_t sh); +const SocketExtraOptions* socket_tcp_extra_options(socket_impl_t sh); +uint32_t tcp_accept_enqueue(ksocket_t* listener, uint8_t ifindex, ip_version_t ipver, const void* src_ip_addr, const void* dst_ip_addr, uint16_t src_port, uint16_t dst_port); -socket_handle_t socket_tcp_create(uint8_t role, uint32_t pid, const SocketExtraOptions* extra); -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_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); -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); -bool socket_is_connected_tcp(socket_handle_t sh); +uint16_t socket_get_local_port_tcp(socket_impl_t sh); +uint16_t socket_get_remote_port_tcp(socket_impl_t sh); +void socket_get_remote_ep_tcp(socket_impl_t sh, net_l4_endpoint* out); +uint8_t socket_get_protocol_tcp(socket_impl_t sh); +uint8_t socket_get_role_tcp(socket_impl_t sh); +bool socket_is_bound_tcp(socket_impl_t sh); +bool socket_is_connected_tcp(socket_impl_t sh); #ifdef __cplusplus } diff --git a/kernel/networking/transport_layer/csocket_udp.cpp b/kernel/networking/transport_layer/csocket_udp.cpp index 19005797..dcdcd0ec 100644 --- a/kernel/networking/transport_layer/csocket_udp.cpp +++ b/kernel/networking/transport_layer/csocket_udp.cpp @@ -2,66 +2,88 @@ #include "networking/transport_layer/socket.hpp" #include "csocket_udp.h" -extern "C" socket_handle_t udp_socket_create(uint8_t role, uint32_t pid, const SocketExtraOptions* extra) { - return reinterpret_cast(new UDPSocket(role, pid, extra)); +extern "C" { + +socket_impl_t udp_socket_create(ksocket_t* owner, uint8_t role, uint32_t pid, const SocketExtraOptions* extra) { + if (!owner) return nullptr; + return reinterpret_cast(new UDPSocket(owner, role, pid, extra)); } -extern "C" int32_t socket_bind_udp(socket_handle_t sh, const SockBindSpec* spec, uint16_t port) { +int32_t socket_bind_udp(socket_impl_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, 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); +int64_t socket_sendto_udp(socket_impl_t sh, const net_l4_endpoint* dst, const void* buf, uint64_t len) { + if (!sh || !buf || !len) return SOCK_ERR_INVAL; + return reinterpret_cast(sh)->sendto(dst, buf, len); } -extern "C" int64_t socket_recvfrom_udp(socket_handle_t sh, void* buf, uint64_t len, net_l4_endpoint* out_src) { +int64_t socket_recvfrom_udp(socket_impl_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) { +int32_t socket_close_udp(socket_impl_t sh) { if (!sh) return SOCK_ERR_INVAL; return reinterpret_cast(sh)->close(); } -extern "C" void socket_destroy_udp(socket_handle_t sh) { +int32_t socket_setopt_udp(socket_impl_t sh, int32_t opt, const void* value, uint32_t len) { + if (!sh) return SOCK_ERR_INVAL; + return reinterpret_cast(sh)->set_option(opt, value, len); +} + +int32_t socket_getopt_udp(socket_impl_t sh, int32_t opt, void* value, uint32_t* len) { + if (!sh) return SOCK_ERR_INVAL; + return reinterpret_cast(sh)->get_option(opt, value, len); +} + +void socket_destroy_udp(socket_impl_t sh) { if (!sh) return; delete reinterpret_cast(sh); } -extern "C" uint16_t socket_get_local_port_udp(socket_handle_t sh) { +uint32_t socket_udp_input(ksocket_t* socket, uint8_t ifindex, uint8_t l3_id, ip_version_t ipver, const void* src_ip_addr, const void* dst_ip_addr, netpkt_t* pkt, uint16_t src_port, uint16_t dst_port) { + if (!socket || !pkt) return 0; + socket_impl_t sh = socket_core_impl(socket); + if (!sh) return 0; + return reinterpret_cast(sh)->enqueue_datagram(ifindex, l3_id, ipver, src_ip_addr, dst_ip_addr, pkt, src_port, dst_port); +} + +uint16_t socket_get_local_port_udp(socket_impl_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) { +uint16_t socket_get_remote_port_udp(socket_impl_t sh) { if (!sh) return 0; return reinterpret_cast(sh)->get_remote_port(); } -extern "C" void socket_get_remote_ep_udp(socket_handle_t sh, net_l4_endpoint* out) { +void socket_get_remote_ep_udp(socket_impl_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) { +uint8_t socket_get_protocol_udp(socket_impl_t sh) { if (!sh) return 0xFF; return reinterpret_cast(sh)->get_protocol(); } -extern "C" uint8_t socket_get_role_udp(socket_handle_t sh) { +uint8_t socket_get_role_udp(socket_impl_t sh) { if (!sh) return 0xFF; return reinterpret_cast(sh)->get_role(); } -extern "C" bool socket_is_bound_udp(socket_handle_t sh) { +bool socket_is_bound_udp(socket_impl_t sh) { if (!sh) return false; return reinterpret_cast(sh)->is_bound(); } -extern "C" bool socket_is_connected_udp(socket_handle_t sh) { +bool socket_is_connected_udp(socket_impl_t sh) { if (!sh) return false; return reinterpret_cast(sh)->is_connected(); } + +} diff --git a/kernel/networking/transport_layer/csocket_udp.h b/kernel/networking/transport_layer/csocket_udp.h index 7872b8b4..a697bc7a 100644 --- a/kernel/networking/transport_layer/csocket_udp.h +++ b/kernel/networking/transport_layer/csocket_udp.h @@ -1,30 +1,32 @@ #pragma once #include "types.h" #include "net/network_types.h" -#include "networking/transport_layer/socket.hpp" +#include "socket_core.h" #include "net/socket_types.h" +#include "networking/netpkt.h" #ifdef __cplusplus extern "C" { #endif -typedef void* socket_handle_t; +socket_impl_t udp_socket_create(ksocket_t* owner, uint8_t role, uint32_t pid, const SocketExtraOptions* extra); +int32_t socket_bind_udp(socket_impl_t sh, const SockBindSpec* spec, uint16_t port); +int64_t socket_sendto_udp(socket_impl_t sh, const net_l4_endpoint* dst, const void* buf, uint64_t len); +int64_t socket_recvfrom_udp(socket_impl_t sh, void* buf, uint64_t len, net_l4_endpoint* out_src); +int32_t socket_close_udp(socket_impl_t sh); +void socket_destroy_udp(socket_impl_t sh); +int32_t socket_setopt_udp(socket_impl_t sh, int32_t opt, const void* value, uint32_t len); +int32_t socket_getopt_udp(socket_impl_t sh, int32_t opt, void* value, uint32_t* len); +uint32_t socket_udp_input(ksocket_t* socket, uint8_t ifindex, uint8_t l3_id, ip_version_t ipver, const void* src_ip_addr, const void* dst_ip_addr, netpkt_t* pkt, uint16_t src_port, uint16_t dst_port); -socket_handle_t udp_socket_create(uint8_t role, uint32_t pid, const SocketExtraOptions* extra); -int32_t socket_bind_udp(socket_handle_t sh, const SockBindSpec* spec, uint16_t port); -int64_t socket_sendto_udp(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(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_impl_t sh); +uint16_t socket_get_remote_port_udp(socket_impl_t sh); +void socket_get_remote_ep_udp(socket_impl_t sh, net_l4_endpoint* out); -uint16_t socket_get_local_port_udp(socket_handle_t sh); -uint16_t socket_get_remote_port_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); +uint8_t socket_get_protocol_udp(socket_impl_t sh); +uint8_t socket_get_role_udp(socket_impl_t sh); +bool socket_is_bound_udp(socket_impl_t sh); +bool socket_is_connected_udp(socket_impl_t sh); #ifdef __cplusplus } diff --git a/kernel/networking/transport_layer/socket.hpp b/kernel/networking/transport_layer/socket.hpp index 2c7a3030..dd4b2c5a 100644 --- a/kernel/networking/transport_layer/socket.hpp +++ b/kernel/networking/transport_layer/socket.hpp @@ -1,42 +1,19 @@ #pragma once #include "types.h" #include "net/network_types.h" -#include "networking/port_manager.h" +#include "networking/transport_layer/socket_core.h" +#include "networking/transport_layer/socket_bind.h" #include "tcp.h" #include "udp.h" #include "net/socket_types.h" #include "console/kio.h" #include "networking/net_logger/net_logger.h" -#ifdef __cplusplus -extern "C" { -#endif - -//TODO: replace with enum -#define SOCK_ROLE_CLIENT 0 -#define SOCK_ROLE_SERVER 1 - -#define SOCK_OK 0 -#define SOCK_ERR_INVAL -1 -#define SOCK_ERR_BOUND -2 -#define SOCK_ERR_NOT_BOUND -3 -#define SOCK_ERR_PERM -4 -#define SOCK_ERR_NO_PORT -5 -#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 +//TODO should these sockets be c instead of cpp? class Socket { protected: - static constexpr int SOCK_MAX_L3 = 32; - uint16_t localPort = 0; net_l4_endpoint remoteEP = { IP_VER4, {0}, 0 }; @@ -45,40 +22,109 @@ class Socket { bool bound = false; bool connected = false; uint16_t pid = 0; + ksocket_t* ownerSocket = nullptr; SocketExtraOptions extraOpts = {}; + SockBindSpec bindSpec = {}; - uint8_t bound_l3[SOCK_MAX_L3] = {0}; - int bound_l3_count = 0; - Socket(uint8_t protocol, uint8_t r, const SocketExtraOptions* extra) : proto(protocol), role(r) { + Socket(ksocket_t* owner, uint8_t protocol, uint8_t r, const SocketExtraOptions* extra) : proto(protocol), role(r), ownerSocket(owner) { if (extra) extraOpts = *extra; } - 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(); } + ksocket_t* get_owner_socket() const { return ownerSocket; } + const SocketExtraOptions* get_extra_options() const { return &extraOpts; } + + int32_t set_option(int32_t opt, const void* value, uint32_t len) { + if (!value || len != sizeof(uint32_t)) return SOCK_ERR_INVAL; + + uint32_t v = 0; + memcpy(&v, value, sizeof(v)); + + switch ((uint32_t)opt) { + case SOCK_OPT_RECV_TIMEOUT: + extraOpts.recv_timeout_ms = v; + if (v) extraOpts.flags |= SOCK_OPT_RECV_TIMEOUT; + else extraOpts.flags &= ~SOCK_OPT_RECV_TIMEOUT; + return SOCK_OK; + case SOCK_OPT_SEND_TIMEOUT: + extraOpts.send_timeout_ms = v; + if (v) extraOpts.flags |= SOCK_OPT_SEND_TIMEOUT; + else extraOpts.flags &= ~SOCK_OPT_SEND_TIMEOUT; + return SOCK_OK; + case SOCK_OPT_BUF_SIZE: + if (!v) return SOCK_ERR_INVAL; + extraOpts.buf_size = v; + extraOpts.flags |= SOCK_OPT_BUF_SIZE; + return SOCK_OK; + case SOCK_OPT_DEBUG: + if (v > SOCK_DBG_ALL) return SOCK_ERR_INVAL; + extraOpts.debug_level = (SockDebugLevel)v; + if (v) extraOpts.flags |= SOCK_OPT_DEBUG; + else extraOpts.flags &= ~SOCK_OPT_DEBUG; + return SOCK_OK; + case SOCK_OPT_DONTFRAG: + if (v) extraOpts.flags |= SOCK_OPT_DONTFRAG; + else extraOpts.flags &= ~SOCK_OPT_DONTFRAG; + return SOCK_OK; + case SOCK_OPT_TTL: + if (v > 255) return SOCK_ERR_INVAL; + extraOpts.ttl = (uint8_t)v; + if (v) extraOpts.flags |= SOCK_OPT_TTL; + else extraOpts.flags &= ~SOCK_OPT_TTL; + return SOCK_OK; + default: + return SOCK_ERR_INVAL; + } + } + + int32_t get_option(int32_t opt, void* value, uint32_t* len) const { + if (!value || !len || *len < sizeof(uint32_t)) return SOCK_ERR_INVAL; + + uint32_t v = 0; + switch ((uint32_t)opt) { + case SOCK_OPT_RECV_TIMEOUT: + v = extraOpts.recv_timeout_ms; + break; + case SOCK_OPT_SEND_TIMEOUT: + v = extraOpts.send_timeout_ms; + break; + case SOCK_OPT_BUF_SIZE: + v = extraOpts.buf_size; + break; + case SOCK_OPT_DEBUG: + v = extraOpts.debug_level; + break; + case SOCK_OPT_DONTFRAG: + v = (extraOpts.flags & SOCK_OPT_DONTFRAG) != 0; + break; + case SOCK_OPT_TTL: + v = extraOpts.ttl; + break; + default: + return SOCK_ERR_INVAL; + } + + memcpy(value, &v, sizeof(v)); + *len = sizeof(uint32_t); + return SOCK_OK; + } + virtual int32_t bind(const SockBindSpec& spec, uint16_t port) = 0; virtual int32_t close() { - if (bound) { - for (int i = 0; i < bound_l3_count; ++i) { - do_unbind_one(bound_l3[i], localPort, pid); - } - bound = false; - localPort = 0; - clear_bound_l3(); - } + if (ownerSocket) socket_bind_remove_socket(ownerSocket); + bound = false; + localPort = 0; connected = false; remoteEP.port = 0; remoteEP.ver = IP_VER4; memset(remoteEP.ip, 0, 16); + memset(&bindSpec, 0, sizeof(bindSpec)); return SOCK_OK; } @@ -94,8 +140,6 @@ class Socket { 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/kernel/networking/transport_layer/socket_bind.c b/kernel/networking/transport_layer/socket_bind.c new file mode 100644 index 00000000..607e41cc --- /dev/null +++ b/kernel/networking/transport_layer/socket_bind.c @@ -0,0 +1,260 @@ +#include "socket_bind.h" +#include "exceptions/irq.h" +#include "networking/interface_manager.h" +#include "networking/internet_layer/ipv4_utils.h" +#include "networking/internet_layer/ipv6_utils.h" +#include "random/random.h" +#include "std/memory.h" + +#define SOCKET_PORT_MIN_EPHEMERAL 49152u +#define SOCKET_PORT_MAX_EPHEMERAL 65535u + +typedef struct socket_bind_entry { + bool used; + protocol_t protocol; + uint16_t port; + SockBindSpec spec; + ksocket_t* socket; +} socket_bind_entry_t; + +static socket_bind_entry_t bind_entries[SOCKET_BIND_MAX]; + +static bool socket_bind_l3_valid(ip_version_t ver, uint8_t l3_id) { + if (!l3_id) return false; + + if (ver == IP_VER4) { + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(l3_id); + return v4 && v4->l2 && v4->l2->is_up && v4->mode != IPV4_CFG_DISABLED; + } + + if (ver == IP_VER6) { + l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(l3_id); + return v6 && v6->l2 && v6->l2->is_up && v6->cfg != IPV6_CFG_DISABLE && v6->dad_state == IPV6_DAD_OK; + } + + return false; +} + +uint32_t socket_bind_l3_list(const SockBindSpec* spec, ip_version_t ver, uint8_t* out, uint32_t cap) { + if (!spec || !out || !cap || (ver != IP_VER4 && ver != IP_VER6)) return 0; + + uint32_t count = 0; + SockBindSpec normal = *spec; + if (normal.kind == BIND_L3 && normal.l3_id == 0 && normal.ifindex == 0 && normal.ver == 0 && ipv6_is_unspecified(normal.ip)) normal.kind = BIND_ANY; + + if (normal.kind == BIND_ANY4 && ver != IP_VER4) return 0; + if (normal.kind == BIND_ANY6 && ver != IP_VER6) return 0; + if (normal.ver && normal.ver != ver && normal.kind != BIND_ANY && normal.kind != BIND_ANY4 && normal.kind != BIND_ANY6) return 0; + + if (normal.kind == BIND_L3) { + if (socket_bind_l3_valid(ver, normal.l3_id)) out[count++] = normal.l3_id; + return count; + } + + if (normal.kind == BIND_IP) { + if (ver == IP_VER4) { + uint32_t ip = 0; + memcpy(&ip, normal.ip, 4); + if (!ipv4_is_unspecified(ip) && !ipv4_is_multicast(ip)) { + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_ip(ip); + if (v4 && socket_bind_l3_valid(IP_VER4, v4->l3_id)) out[count++] = v4->l3_id; + return count; + } + } else { + if (!ipv6_is_unspecified(normal.ip) && !ipv6_is_multicast(normal.ip)) { + l3_ipv6_interface_t* v6 = l3_ipv6_find_by_ip(normal.ip); + if (v6 && socket_bind_l3_valid(IP_VER6, v6->l3_id)) out[count++] = v6->l3_id; + return count; + } + } + } + + for (uint8_t i = 0, n = l2_interface_count(); i < n && count < cap; ++i) { + l2_interface_t* l2 = l2_interface_at(i); + if (!l2 || !l2->is_up) continue; + if (normal.kind == BIND_L2 && l2->ifindex != normal.ifindex) continue; + if (normal.kind != BIND_ANY && normal.kind != BIND_ANY4 && normal.kind != BIND_ANY6 && normal.kind != BIND_IP && normal.kind != BIND_L2) continue; + + if (ver == IP_VER4) { + for (int s = 0; s < MAX_IPV4_PER_INTERFACE && count < cap; ++s) { + l3_ipv4_interface_t* v4 = l2->l3_v4[s]; + if (!v4 || !socket_bind_l3_valid(IP_VER4, v4->l3_id)) continue; + out[count++] = v4->l3_id; + } + } else { + for (int s = 0; s < MAX_IPV6_PER_INTERFACE && count < cap; ++s) { + l3_ipv6_interface_t* v6 = l2->l3_v6[s]; + if (!v6 || !socket_bind_l3_valid(IP_VER6, v6->l3_id)) continue; + out[count++] = v6->l3_id; + } + } + } + + return count; +} + +bool socket_bind_insert(ksocket_t* socket, protocol_t protocol, const SockBindSpec* spec, uint16_t port) { + if (!socket || !spec || !port) return false; + if (protocol != PROTO_TCP && protocol != PROTO_UDP) return false; + + SockBindSpec normal = *spec; + if (normal.kind == BIND_L3 && normal.l3_id == 0 && normal.ifindex == 0 && normal.ver == 0 && ipv6_is_unspecified(normal.ip)) normal.kind = BIND_ANY; + + irq_flags_t irq = irq_save_disable(); //TODO lock + + for (uint32_t i = 0; i < SOCKET_BIND_MAX; ++i) { + socket_bind_entry_t* e = &bind_entries[i]; + if (!e->used) continue; + if (e->protocol != protocol || e->port != port) continue; + + ip_version_t a_family = normal.kind == BIND_ANY4 ? IP_VER4 : (normal.kind == BIND_ANY6 ? IP_VER6 : normal.ver); + ip_version_t b_family = e->spec.kind == BIND_ANY4 ? IP_VER4 : (e->spec.kind == BIND_ANY6 ? IP_VER6 : e->spec.ver); + if (a_family && b_family && a_family != b_family) continue; + + bool overlap = true; + if (normal.kind == BIND_IP && e->spec.kind == BIND_IP && a_family && b_family) { + bool a_unspec = true; + bool b_unspec = true; + if (a_family == IP_VER4) { + uint32_t av = 0; + uint32_t bv = 0; + memcpy(&av, normal.ip, 4); + memcpy(&bv, e->spec.ip, 4); + a_unspec = ipv4_is_unspecified(av); + b_unspec = ipv4_is_unspecified(bv); + overlap = a_unspec || b_unspec || av == bv; + } else if (a_family == IP_VER6) { + a_unspec = ipv6_is_unspecified(normal.ip); + b_unspec = ipv6_is_unspecified(e->spec.ip); + overlap = a_unspec || b_unspec || memcmp(normal.ip, e->spec.ip, 16) == 0; + } + } else if (normal.kind == BIND_L3 && e->spec.kind == BIND_L3) overlap = normal.l3_id == e->spec.l3_id; + else if (normal.kind == BIND_L2 && e->spec.kind == BIND_L2) overlap = normal.ifindex == e->spec.ifindex; + + if (overlap) { + irq_restore(irq); + return false; + } + } + + for (uint32_t i = 0; i < SOCKET_BIND_MAX; ++i) { + socket_bind_entry_t* e = &bind_entries[i]; + if (e->used) continue; + e->used = true; + e->protocol = protocol; + e->port = port; + e->spec = normal; + e->socket = socket; + socket_core_ref(socket); + irq_restore(irq); + return true; + } + + irq_restore(irq); + return false; +} + +void socket_bind_remove_socket(ksocket_t* socket) { + if (!socket) return; + + ksocket_t* drop[SOCKET_BIND_MAX]; + uint32_t drop_count = 0; + + irq_flags_t irq = irq_save_disable(); //TODO lock + for (uint32_t i = 0; i < SOCKET_BIND_MAX; ++i) { + socket_bind_entry_t* e = &bind_entries[i]; + if (!e->used || e->socket != socket) continue; + drop[drop_count++] = e->socket; + memset(e, 0, sizeof(*e)); + } + irq_restore(irq); + + for (uint32_t i = 0; i < drop_count; ++i) socket_core_put(drop[i]); +} + +bool socket_bind_port_busy(protocol_t protocol, ip_version_t ipver, uint8_t l3_id, uint16_t port) { + if (!port) return true; + if (protocol != PROTO_TCP && protocol != PROTO_UDP) return true; + + irq_flags_t irq = irq_save_disable(); //TODO lock + for (uint32_t i = 0; i < SOCKET_BIND_MAX; ++i) { + socket_bind_entry_t* e = &bind_entries[i]; + if (!e->used) continue; + if (e->protocol != protocol || e->port != port) continue; + + ip_version_t family = e->spec.kind == BIND_ANY4 ? IP_VER4 : (e->spec.kind == BIND_ANY6 ? IP_VER6 : e->spec.ver); + if (family && family != ipver) continue; + if (e->spec.kind == BIND_L3 && e->spec.l3_id && e->spec.l3_id != l3_id) continue; + + irq_restore(irq); + return true; + } + irq_restore(irq); + return false; +} + +int socket_bind_alloc_ephemeral_l3(ksocket_t* socket, protocol_t protocol, uint8_t l3_id, uint16_t pid) { + if (!socket || !l3_id) return -1; + if (protocol != PROTO_TCP && protocol != PROTO_UDP) return -1; + + ip_version_t ver = l3_is_v6_from_id(l3_id) ? IP_VER6 : IP_VER4; + + rng_t rng; + rng_init_random(&rng); + uint32_t seed = rng_next32(&rng); + + uint32_t minp = SOCKET_PORT_MIN_EPHEMERAL; + uint32_t maxp = SOCKET_PORT_MAX_EPHEMERAL; + uint32_t range = maxp - minp + 1u; + uint32_t first = minp + (seed % range); + + SockBindSpec spec; + memset(&spec, 0, sizeof(spec)); + spec.kind = BIND_L3; + spec.ver = ver; + spec.l3_id = l3_id; + + for (uint32_t i = 0; i < range; ++i) { + uint16_t port = (uint16_t)(minp + ((first - minp + i) % range)); + if (socket_bind_port_busy(protocol, ver, l3_id, port)) continue; + if (socket_bind_insert(socket, protocol, &spec, port)) return port; + } + + return -1; +} + +uint32_t socket_bind_collect(protocol_t protocol, ip_version_t ipver, uint8_t l3_id, uint8_t ifindex, const void* dst_ip_addr, uint16_t dst_port, ksocket_t** out, uint32_t out_cap) { + if (!out || !out_cap || !dst_ip_addr) return 0; + if (protocol != PROTO_TCP && protocol != PROTO_UDP) return 0; + + uint32_t count = 0; + irq_flags_t irq = irq_save_disable(); //TODO lock + for (uint32_t i = 0; i < SOCKET_BIND_MAX && count < out_cap; ++i) { + socket_bind_entry_t* e = &bind_entries[i]; + if (!e->used || e->protocol != protocol || e->port != dst_port || !e->socket) continue; + + ip_version_t family = e->spec.kind == BIND_ANY4 ? IP_VER4 : (e->spec.kind == BIND_ANY6 ? IP_VER6 : e->spec.ver); + if (family && family != ipver) continue; + + if (e->spec.kind == BIND_L3 && e->spec.l3_id != l3_id) continue; + if (e->spec.kind == BIND_L2 && e->spec.ifindex != ifindex) continue; + + if (e->spec.kind == BIND_IP) { + if (ipver == IP_VER4) { + uint32_t want = 0; + uint32_t got = 0; + memcpy(&want, e->spec.ip, 4); + memcpy(&got, dst_ip_addr, 4); + if (!ipv4_is_unspecified(want) && want != got) continue; + } else if (ipver == IP_VER6) { + if (!ipv6_is_unspecified(e->spec.ip) && memcmp(e->spec.ip, dst_ip_addr, 16) != 0) continue; + } else continue; + } else if (e->spec.kind != BIND_ANY && e->spec.kind != BIND_ANY4 && e->spec.kind != BIND_ANY6 && e->spec.kind != BIND_L3 && e->spec.kind != BIND_L2) continue; + + if (socket_core_is_closing(e->socket)) continue; + socket_core_ref(e->socket); + out[count++] = e->socket; + } + irq_restore(irq); + return count; +} \ No newline at end of file diff --git a/kernel/networking/transport_layer/socket_bind.h b/kernel/networking/transport_layer/socket_bind.h new file mode 100644 index 00000000..8c8605da --- /dev/null +++ b/kernel/networking/transport_layer/socket_bind.h @@ -0,0 +1,26 @@ +#pragma once + +#include "types.h" +#include "net/network_types.h" +#include "net/socket_types.h" +#include "networking/transport_layer/socket_core.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define SOCKET_BIND_MAX 1024 +#define SOCKET_BIND_COLLECT_MAX 32 + +bool socket_bind_insert(ksocket_t* socket, protocol_t protocol, const SockBindSpec* spec, uint16_t port); +void socket_bind_remove_socket(ksocket_t* socket); +int socket_bind_alloc_ephemeral_l3(ksocket_t* socket, protocol_t protocol, uint8_t l3_id, uint16_t pid); + +uint32_t socket_bind_collect(protocol_t protocol, ip_version_t ipver, uint8_t l3_id, uint8_t ifindex, const void* dst_ip_addr, uint16_t dst_port, ksocket_t** out, uint32_t out_cap); + +bool socket_bind_port_busy(protocol_t protocol, ip_version_t ipver, uint8_t l3_id, uint16_t port); +uint32_t socket_bind_l3_list(const SockBindSpec* spec, ip_version_t ver, uint8_t* out, uint32_t cap); + +#ifdef __cplusplus +} +#endif diff --git a/kernel/networking/transport_layer/socket_core.c b/kernel/networking/transport_layer/socket_core.c new file mode 100644 index 00000000..e510084c --- /dev/null +++ b/kernel/networking/transport_layer/socket_core.c @@ -0,0 +1,215 @@ +#include "socket_core.h" +#include "socket_bind.h" +#include "exceptions/irq.h" +#include "std/memory.h" +#include "alloc/allocate.h" + +struct ksocket { + uint32_t id; + uint32_t generation; + uint16_t pid; + Socket_Role role; + protocol_t protocol; + socket_impl_t impl; + socket_impl_destroy_fn destroy; + socket_impl_close_fn close; + socket_impl_setopt_fn setopt; + socket_impl_getopt_fn getopt; + int32_t refs; + bool closing; + bool visible; +}; + +static ksocket_t* sockets[SOCKET_MAX_OPEN]; +static uint32_t generations[SOCKET_MAX_OPEN]; + +bool socket_core_alloc(Socket_Role role, protocol_t protocol, uint16_t pid, ksocket_t** out_socket) { + if (!out_socket) return false; + if (protocol == PROTO_NONE) return false; + + ksocket_t* socket = (ksocket_t*)zalloc(sizeof(ksocket_t)); + if (!socket) return false; + + irq_flags_t irq = irq_save_disable(); //TODO lock + + uint32_t id = SOCKET_MAX_OPEN; + for (uint32_t i = 1; i < SOCKET_MAX_OPEN; ++i) { + if (!sockets[i]) { + id = i; + break; + } + } + + if (id == SOCKET_MAX_OPEN) { + irq_restore(irq); + release(socket); + return false; + } + + uint32_t gen = generations[id] + 1; + if (!gen) gen = 1; + generations[id] = gen; + + socket->id = id; + socket->generation = gen; + socket->pid = pid; + socket->role = role; + socket->protocol = protocol; + socket->refs = 1; + sockets[id] = socket; + + irq_restore(irq); + + *out_socket = socket; + return true; +} + +bool socket_core_attach_impl(ksocket_t* socket, socket_impl_t impl, socket_impl_destroy_fn destroy, socket_impl_close_fn close, socket_impl_setopt_fn setopt, socket_impl_getopt_fn getopt, SocketHandle* out_handle) { + if (!socket || !impl || !destroy || !close) return false; + irq_flags_t irq = irq_save_disable(); //TODO lock + if (socket->closing || socket->visible || socket->impl) { + irq_restore(irq); + return false; + } + + socket->impl = impl; + socket->destroy = destroy; + socket->close = close; + socket->setopt = setopt; + socket->getopt = getopt; + socket->visible = true; + irq_restore(irq); + + if (out_handle) socket_core_export_handle(socket, out_handle); + return true; +} + +ksocket_t* socket_core_get(const SocketHandle* handle, uint16_t pid) { + if (!handle || handle->protocol == PROTO_NONE) return NULL; + if (handle->id >= SOCKET_MAX_OPEN) return NULL; + + irq_flags_t irq = irq_save_disable(); //TODO lock + ksocket_t* socket = sockets[handle->id]; + if (!socket || !socket->visible || socket->closing || socket->generation != handle->generation || (pid && socket->pid != pid) || socket->protocol != handle->protocol) { + irq_restore(irq); + return NULL; + } + socket->refs++; + irq_restore(irq); + return socket; +} + +void socket_core_ref(ksocket_t* socket) { + if (!socket) return; + irq_flags_t irq = irq_save_disable(); //TODO lock + socket->refs++; + irq_restore(irq); +} + +void socket_core_put(ksocket_t* socket) { + if (!socket) return; + + bool do_destroy = false; + irq_flags_t irq = irq_save_disable(); + if (socket->refs > 0) socket->refs--; + if (socket->refs == 0 && socket->closing) do_destroy = true; + irq_restore(irq); + + if (!do_destroy) return; + + socket_impl_t impl = socket->impl; + socket_impl_destroy_fn destroy = socket->destroy; + socket->impl = NULL; + socket->destroy = NULL; + socket->close = NULL; + socket->setopt = NULL; + socket->getopt = NULL; + + if (destroy && impl) destroy(impl); + release(socket); +} + +int32_t socket_core_close_socket(ksocket_t* socket) { + if (!socket) return SOCK_ERR_INVAL; + + bool first_close = false; + irq_flags_t irq = irq_save_disable(); + if (!socket->closing) { + socket->closing = true; + socket->visible = false; + if (socket->id < SOCKET_MAX_OPEN && sockets[socket->id] == socket) sockets[socket->id] = NULL; + first_close = true; + } + irq_restore(irq); + + if (!first_close) return SOCK_OK; + socket_bind_remove_socket(socket); + + int32_t ret = SOCK_OK; + if (socket->close && socket->impl) ret = socket->close(socket->impl); + + socket_core_put(socket); + return ret; +} + +int32_t socket_core_close_handle(SocketHandle* handle, uint16_t pid) { + ksocket_t* socket = socket_core_get(handle, pid); + if (!socket) return SOCK_ERR_INVAL; + int32_t ret = socket_core_close_socket(socket); + socket_core_put(socket); + if (handle) { + handle->id = 0; + handle->generation = 0; + handle->protocol = PROTO_NONE; + handle->connection = (net_l4_endpoint){}; + } + return ret; +} + +int32_t socket_core_set_option(ksocket_t* socket, int32_t opt, const void* value, uint32_t len) { + if (!socket || !value || !len) return SOCK_ERR_INVAL; + if (!socket->setopt || !socket->impl) return SOCK_ERR_PROTO; + return socket->setopt(socket->impl, opt, value, len); +} + +int32_t socket_core_get_option(ksocket_t* socket, int32_t opt, void* value, uint32_t* len) { + if (!socket || !value || !len) return SOCK_ERR_INVAL; + if (!socket->getopt || !socket->impl) return SOCK_ERR_PROTO; + return socket->getopt(socket->impl, opt, value, len); +} + +socket_impl_t socket_core_impl(ksocket_t* socket) { + return socket ? socket->impl : NULL; +} + +protocol_t socket_core_protocol(const ksocket_t* socket) { + return socket ? socket->protocol : PROTO_NONE; +} + +Socket_Role socket_core_role(const ksocket_t* socket) { + return socket ? socket->role : SOCKET_CLIENT; +} + +uint16_t socket_core_pid(const ksocket_t* socket) { + return socket ? socket->pid : 0; +} + +uint32_t socket_core_id(const ksocket_t* socket) { + return socket ? socket->id : 0; +} + +uint32_t socket_core_generation(const ksocket_t* socket) { + return socket ? socket->generation : 0; +} + +bool socket_core_is_closing(const ksocket_t* socket) { + return !socket || socket->closing; +} + +void socket_core_export_handle(const ksocket_t* socket, SocketHandle* out_handle) { + if (!socket || !out_handle) return; + out_handle->id = socket->id; + out_handle->generation = socket->generation; + out_handle->protocol = socket->protocol; + out_handle->connection = (net_l4_endpoint){}; +} diff --git a/kernel/networking/transport_layer/socket_core.h b/kernel/networking/transport_layer/socket_core.h new file mode 100644 index 00000000..17a7be2d --- /dev/null +++ b/kernel/networking/transport_layer/socket_core.h @@ -0,0 +1,42 @@ +#pragma once + +#include "types.h" +#include "net/network_types.h" +#include "net/socket_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void* socket_impl_t; +typedef struct ksocket ksocket_t; + +typedef void (*socket_impl_destroy_fn)(socket_impl_t impl); +typedef int32_t (*socket_impl_close_fn)(socket_impl_t impl); +typedef int32_t (*socket_impl_setopt_fn)(socket_impl_t impl, int32_t opt, const void* value, uint32_t len); +typedef int32_t (*socket_impl_getopt_fn)(socket_impl_t impl, int32_t opt, void* value, uint32_t* len); + +#define SOCKET_MAX_OPEN 2048 + +bool socket_core_alloc(Socket_Role role, protocol_t protocol, uint16_t pid, ksocket_t** out_socket); +bool socket_core_attach_impl(ksocket_t* socket, socket_impl_t impl, socket_impl_destroy_fn destroy, socket_impl_close_fn close, socket_impl_setopt_fn setopt, socket_impl_getopt_fn getopt, SocketHandle* out_handle); +ksocket_t* socket_core_get(const SocketHandle* handle, uint16_t pid); +void socket_core_ref(ksocket_t* socket); +void socket_core_put(ksocket_t* socket); +int32_t socket_core_close_handle(SocketHandle* handle, uint16_t pid); +int32_t socket_core_close_socket(ksocket_t* socket); +int32_t socket_core_set_option(ksocket_t* socket, int32_t opt, const void* value, uint32_t len); +int32_t socket_core_get_option(ksocket_t* socket, int32_t opt, void* value, uint32_t* len); + +socket_impl_t socket_core_impl(ksocket_t* socket); +protocol_t socket_core_protocol(const ksocket_t* socket); +Socket_Role socket_core_role(const ksocket_t* socket); +uint16_t socket_core_pid(const ksocket_t* socket); +uint32_t socket_core_id(const ksocket_t* socket); +uint32_t socket_core_generation(const ksocket_t* socket); +bool socket_core_is_closing(const ksocket_t* socket); +void socket_core_export_handle(const ksocket_t* socket, SocketHandle* out_handle); + +#ifdef __cplusplus +} +#endif diff --git a/kernel/networking/transport_layer/socket_endpoint.c b/kernel/networking/transport_layer/socket_endpoint.c new file mode 100644 index 00000000..d9c60cd8 --- /dev/null +++ b/kernel/networking/transport_layer/socket_endpoint.c @@ -0,0 +1,42 @@ +#include "socket_endpoint.h" +#include "networking/transport_layer/trans_utils.h" +#include "std/memory.h" + +uint32_t socket_endpoint_resolve(const char* host, uint16_t port, dns_server_sel_t sel, uint32_t timeout_ms, net_l4_endpoint* out) { + if (!host || !port || !out) return 0; + + memset(out, 0, sizeof(net_l4_endpoint) * 2); + + uint32_t count = 0; + uint8_t v6addr[16]; + uint32_t v4addr = 0; + + memset(v6addr, 0, sizeof(v6addr)); + if (dns_resolve_aaaa(host, v6addr, sel, timeout_ms) == DNS_OK) { + out[count].ver = IP_VER6; + memcpy(out[count].ip, v6addr, 16); + out[count].port = port; + count++; + } + + if (dns_resolve_a(host, &v4addr, sel, timeout_ms) == DNS_OK) { + make_ep(v4addr, port, IP_VER4, &out[count]); + count++; + } + + return count; +} + +net_l4_endpoint socket_endpoint_select(const char* host, uint16_t port, ip_version_t preferred, dns_server_sel_t sel, uint32_t timeout_ms) { + net_l4_endpoint endpoints[2]; + memset(endpoints, 0, sizeof(endpoints)); + + uint32_t count = socket_endpoint_resolve(host, port, sel, timeout_ms, endpoints); + if (!count) return (net_l4_endpoint){}; + + if (preferred == IP_VER4 || preferred == IP_VER6) { + for (uint32_t i = 0; i < count; ++i) if (endpoints[i].ver == preferred) return endpoints[i]; + } + + return endpoints[0]; +} diff --git a/kernel/networking/transport_layer/socket_endpoint.h b/kernel/networking/transport_layer/socket_endpoint.h new file mode 100644 index 00000000..b7df79ef --- /dev/null +++ b/kernel/networking/transport_layer/socket_endpoint.h @@ -0,0 +1,16 @@ +#pragma once + +#include "types.h" +#include "net/network_types.h" +#include "networking/application_layer/dns/dns.h" + +#ifdef __cplusplus +extern "C" { +#endif + +uint32_t socket_endpoint_resolve(const char* host, uint16_t port, dns_server_sel_t sel, uint32_t timeout_ms, net_l4_endpoint* out); +net_l4_endpoint socket_endpoint_select(const char* host, uint16_t port, ip_version_t preferred, dns_server_sel_t sel, uint32_t timeout_ms); + +#ifdef __cplusplus +} +#endif diff --git a/kernel/networking/transport_layer/socket_tcp.hpp b/kernel/networking/transport_layer/socket_tcp.hpp index 415cdac2..503ecad7 100644 --- a/kernel/networking/transport_layer/socket_tcp.hpp +++ b/kernel/networking/transport_layer/socket_tcp.hpp @@ -4,8 +4,8 @@ #include "std/string.h" #include "socket.hpp" #include "networking/transport_layer/tcp.h" +#include "networking/transport_layer/csocket_tcp.h" #include "networking/internet_layer/ipv4.h" -#include "networking/application_layer/dns/dns.h" #include "types.h" #include "net/socket_types.h" #include "networking/internet_layer/ipv4_route.h" @@ -16,26 +16,21 @@ #include "syscalls/syscalls.h" static constexpr int TCP_MAX_BACKLOG = 128; -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_list_head = nullptr; static constexpr uint32_t TCP_DEFAULT_SOCKET_BUF = 256 * 1024; tcp_data flow = {}; - TCPSocket* pending[TCP_MAX_BACKLOG] = { nullptr }; + ksocket_t* pending[TCP_MAX_BACKLOG] = { nullptr }; int backlogCap = 0; int backlogLen = 0; - TCPSocket* next = nullptr; static bool is_valid_v4_l3_for_bind(l3_ipv4_interface_t* v4) { if (!v4 || !v4->l2) return false; if (!v4->l2->is_up) return false; if (v4->mode == IPV4_CFG_DISABLED) return false; if (v4->ip == 0) return false; - if (!v4->port_manager) return false; return true; } @@ -47,255 +42,79 @@ class TCPSocket : public Socket { if (ipv6_is_unspecified(v6->ip)) return false; if (v6->dad_state == IPV6_DAD_FAILED) return false; if (!(v6->kind & IPV6_ADDRK_LINK_LOCAL) && v6->dad_state != IPV6_DAD_OK) return false; - if (!v6->port_manager) return false; return true; } - static uint32_t dispatch(uint8_t ifindex, ip_version_t ipver, const void* src_ip_addr, const void* dst_ip_addr, netpkt_t* pkt, uint16_t src_port, uint16_t dst_port) { - if (!pkt){ - for (TCPSocket* srv = s_list_head; srv; srv = srv->next){ - if (srv->role != SOCK_ROLE_SERVER) continue; - if (!srv->bound) continue; - if (srv->localPort != dst_port) continue; - - bool matches_dst = false; - for (int i = 0; i < srv->bound_l3_count; ++i) { - uint8_t id = srv->bound_l3[i]; - - if (ipver == 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 != ifindex) continue; - uint32_t dst_v4 = 0; - memcpy(&dst_v4, dst_ip_addr, 4); - if (v4->ip == dst_v4) { - matches_dst = true; - break; - } - } else if (ipver == IP_VER6) { - l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(id); - if (!is_valid_v6_l3_for_bind(v6)) continue; - if (v6->l2->ifindex != ifindex) continue; - if (memcmp(v6->ip, dst_ip_addr, 16) == 0){ - matches_dst = true; - break; - } - } - } - - if (!matches_dst) continue; - for (int i = 0; i < srv->backlogLen;) { - TCPSocket* client = srv->pending[i]; - - if (client && client->flow.flow_generation) { - bool readable = tcp_flow_readable(&client->flow) != 0; - bool closed = tcp_flow_recv_closed(&client->flow); - - if (!closed || readable) { - i++; - continue; - } - } - - client = srv->pop_pending_at(i); - if (client) delete client; - } - - if (srv->backlogLen >= srv->backlogCap) break; - - TCPSocket* child = new TCPSocket(SOCK_ROLE_CLIENT, srv->pid, &srv->extraOpts); - if (!child) break; - - child->localPort = dst_port; - - 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 = 0; - - for (int i = 0; i < srv->bound_l3_count; ++i) { - uint8_t id = srv->bound_l3[i]; - - if (ipver == 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 != ifindex) continue; - uint32_t dst_v4 = 0; - memcpy(&dst_v4, dst_ip_addr, 4); - if (v4->ip == dst_v4) { - l3id = id; - break; - } - } else if (ipver == IP_VER6) { - l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(id); - if (!is_valid_v6_l3_for_bind(v6)) continue; - if (v6->l2->ifindex != ifindex) continue; - if (memcmp(v6->ip, dst_ip_addr, 16) == 0) { - l3id = id; - break; - } - } - } - - if (!l3id) { - if (ipver == IP_VER4) { - uint32_t v4dst = 0; - 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) l3id = v4->l3_id; - } else if (ipver == IP_VER6) { - l3_ipv6_interface_t* v6 = l3_ipv6_find_by_ip((const uint8_t*)dst_ip_addr); - if (is_valid_v6_l3_for_bind(v6) && v6->l2 && v6->l2->ifindex == ifindex) l3id = v6->l3_id; - } - } - - child->clear_bound_l3(); - - if (l3id) { - child->add_bound_l3(l3id); - } else if (ipver == IP_VER4){ - uint32_t v4dst = 0; - 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; i < srv->bound_l3_count; ++i) { - l3_ipv4_interface_t* sv4 = l3_ipv4_find_by_id(srv->bound_l3[i]); - if (!sv4 || !sv4->l2) continue; - if (!sv4->l2->is_up) continue; - if (sv4->l2->ifindex != ifindex) continue; - child->add_bound_l3(sv4->l3_id); - } - } - } else if (ipver == IP_VER6) { - for (int i = 0; i < srv->bound_l3_count; ++i) { - l3_ipv6_interface_t* sv6 = l3_ipv6_find_by_id(srv->bound_l3[i]); - if (!is_valid_v6_l3_for_bind(sv6)) continue; - if (!sv6->l2 || !sv6->l2->is_up) continue; - if (sv6->l2->ifindex != ifindex) continue; - child->add_bound_l3(sv6->l3_id); - } - } - - if (!tcp_get_ctx(dst_port, ipver, dst_ip_addr, child->remoteEP.ip, src_port, &child->flow)) { - delete child; - break; +public: + uint32_t queue_accepted_child(uint8_t ifindex, ip_version_t ipver, const void* src_ip_addr, const void* dst_ip_addr, uint16_t src_port, uint16_t dst_port) { + if (role != SOCKET_SERVER || !bound || localPort != dst_port) return 0; + + for (int i = 0; i < backlogLen;) { + ksocket_t* owner = pending[i]; + TCPSocket* client = owner ? (TCPSocket*)socket_core_impl(owner) : nullptr; + if (client && client->flow.flow_generation) { + bool readable = tcp_flow_readable(&client->flow) != 0; + bool closed = tcp_flow_recv_closed(&client->flow); + if (!closed || readable) { + ++i; + continue; } - - child->connected = true; - - srv->pending[srv->backlogLen++] = child; - return 1; } - return 0; - } - return 0; - } - - void insert_in_list() { - for (TCPSocket* it = s_list_head; it; it = it->next){ - if (it == this) { - return; + owner = pop_pending_at(i); + if (owner) { + socket_core_close_socket(owner); + socket_core_put(owner); } } - next = s_list_head; - s_list_head = this; - } + if (backlogLen >= backlogCap) return 0; - void remove_from_list() { - TCPSocket** cur = &s_list_head; - while (*cur) { - if (*cur == this) { - *cur = (*cur)->next; - break; - } - cur = &((*cur)->next); - } - next = nullptr; - } + ksocket_t* child_owner = nullptr; + if (!socket_core_alloc(SOCKET_CLIENT, PROTO_TCP, pid, &child_owner)) return 0; - 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); - } - - bool add_all_l3_on_l2(uint8_t ifindex, ip_version_t ver, uint8_t* tmp_ids, ip_version_t* tmp_ver, int& n) { - l2_interface_t* l2 = l2_interface_find_by_index(ifindex); - if (!l2 || !l2->is_up) return false; - - if ((ver == 0 || ver == IP_VER4)) { - for (int s = 0; s < MAX_IPV4_PER_INTERFACE; ++s) { - l3_ipv4_interface_t* v4 = l2->l3_v4[s]; - if (!is_valid_v4_l3_for_bind(v4)) continue; - if (n < SOCK_MAX_L3) { - tmp_ids[n] = v4->l3_id; - tmp_ver[n] = IP_VER4; - ++n; - } - } + TCPSocket* child = new TCPSocket(child_owner, SOCKET_CLIENT, pid, &extraOpts); + if (!child) { + socket_core_close_socket(child_owner); + return 0; } - if ((ver == 0 || ver == IP_VER6)) { - for (int s = 0; s < MAX_IPV6_PER_INTERFACE; ++s) { - l3_ipv6_interface_t* v6 = l2->l3_v6[s]; - if (!is_valid_v6_l3_for_bind(v6)) continue; - if (n < SOCK_MAX_L3) { - tmp_ids[n] = v6->l3_id; - tmp_ver[n] = IP_VER6; - ++n; - } - } + child->localPort = dst_port; + + child->bound = 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; + child->bindSpec.kind = BIND_IP; + child->bindSpec.ver = ipver; + if (ipver == IP_VER4) memcpy(child->bindSpec.ip, dst_ip_addr, 4); + else if (ipver == IP_VER6) memcpy(child->bindSpec.ip, dst_ip_addr, 16); + + if (!tcp_get_ctx(dst_port, ipver, dst_ip_addr, child->remoteEP.ip, src_port, &child->flow)) { + delete child; + socket_core_close_socket(child_owner); + return 0; } - return n > 0; - } - - bool add_all_l3_any(ip_version_t ver, uint8_t* tmp_ids, ip_version_t* tmp_ver, 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 || !l2->is_up) continue; - - if ((ver == 0 || ver == IP_VER4)) { - for (int s = 0; s < MAX_IPV4_PER_INTERFACE; ++s) { - l3_ipv4_interface_t* v4 = l2->l3_v4[s]; - if (!is_valid_v4_l3_for_bind(v4)) continue; - if (n < SOCK_MAX_L3) { - tmp_ids[n] = v4->l3_id; - tmp_ver[n] = IP_VER4; - ++n; - } - } - } - - if ((ver == 0 || ver == IP_VER6)) { - for (int s = 0; s < MAX_IPV6_PER_INTERFACE; ++s) { - l3_ipv6_interface_t* v6 = l2->l3_v6[s]; - if (!is_valid_v6_l3_for_bind(v6)) continue; - if (n < SOCK_MAX_L3) { - tmp_ids[n] = v6->l3_id; - tmp_ver[n] = IP_VER6; - ++n; - } - } - } + if (!socket_core_attach_impl(child_owner, child, socket_destroy_tcp, socket_close_tcp, socket_setopt_tcp, socket_getopt_tcp, nullptr)) { + delete child; + socket_core_close_socket(child_owner); + return 0; } - return n > 0; + child->connected = true; + socket_core_ref(child_owner); + pending[backlogLen++] = child_owner; + return 1; } +private: - TCPSocket* pop_pending_at(int idx) { + ksocket_t* pop_pending_at(int idx) { if (idx < 0 || idx >= backlogLen) return nullptr; - TCPSocket* client = pending[idx]; + ksocket_t* client = pending[idx]; for (int i = idx + 1; i < backlogLen; ++i) pending[i-1] = pending[i]; pending[--backlogLen] = nullptr; @@ -303,7 +122,7 @@ class TCPSocket : public Socket { } public: - explicit TCPSocket(uint8_t r = SOCK_ROLE_CLIENT, uint32_t pid_ = 0, const SocketExtraOptions* extra = nullptr) : Socket(PROTO_TCP, r, extra) { + explicit TCPSocket(ksocket_t* owner, uint8_t r = SOCKET_CLIENT, uint32_t pid_ = 0, const SocketExtraOptions* extra = nullptr) : Socket(owner, PROTO_TCP, r, extra) { pid = pid_; if (!(extraOpts.flags & SOCK_OPT_BUF_SIZE)) { extraOpts.flags |= SOCK_OPT_BUF_SIZE; @@ -312,14 +131,81 @@ class TCPSocket : public Socket { if (!extraOpts.buf_size) extraOpts.buf_size = TCP_DEFAULT_SOCKET_BUF; if (extraOpts.buf_size > TCP_DEFAULT_SOCKET_BUF) extraOpts.buf_size = TCP_DEFAULT_SOCKET_BUF; - insert_in_list(); } ~TCPSocket() override { - remove_from_list(); close(); } + int32_t set_option(int32_t opt, const void* value, uint32_t len) { + if (!value || len != sizeof(uint32_t)) return SOCK_ERR_INVAL; + + uint32_t v = 0; + memcpy(&v, value, sizeof(v)); + + switch ((uint32_t)opt) { + case SOCK_OPT_KEEPALIVE: + if (v) { + extraOpts.flags |= SOCK_OPT_KEEPALIVE; + if (!extraOpts.keepalive_ms) extraOpts.keepalive_ms = SOCKET_DEFAULT_KEEPALIVE_MS; + } else extraOpts.flags &= ~SOCK_OPT_KEEPALIVE; + if (flow.flow_generation) tcp_flow_apply_socket_options(&flow, &extraOpts); + return SOCK_OK; + case SOCK_OPT_KEEPALIVE_INTERVAL: + if (!v) return SOCK_ERR_INVAL; + extraOpts.keepalive_ms = v; + extraOpts.flags |= SOCK_OPT_KEEPALIVE | SOCK_OPT_KEEPALIVE_INTERVAL; + if (flow.flow_generation) tcp_flow_apply_socket_options(&flow, &extraOpts); + return SOCK_OK; + case SOCK_OPT_TCP_NO_DELAY: + if (v) extraOpts.flags |= SOCK_OPT_TCP_NO_DELAY; + else extraOpts.flags &= ~SOCK_OPT_TCP_NO_DELAY; + if (flow.flow_generation) tcp_flow_apply_socket_options(&flow, &extraOpts); + return SOCK_OK; + case SOCK_OPT_SEND_BUF_SIZE: + if (!v) return SOCK_ERR_INVAL; + extraOpts.send_buf_size = v; + extraOpts.flags |= SOCK_OPT_SEND_BUF_SIZE; + if (flow.flow_generation) tcp_flow_apply_socket_options(&flow, &extraOpts); + return SOCK_OK; + case SOCK_OPT_MCAST_JOIN: + return SOCK_ERR_INVAL; + default: { + int32_t ret = Socket::set_option(opt, value, len); + if (ret == SOCK_OK && flow.flow_generation) tcp_flow_apply_socket_options(&flow, &extraOpts); + return ret; + } + } + } + + int32_t get_option(int32_t opt, void* value, uint32_t* len) const { + if (!value || !len || *len < sizeof(uint32_t)) return SOCK_ERR_INVAL; + + uint32_t v = 0; + switch ((uint32_t)opt) { + case SOCK_OPT_KEEPALIVE: + v = (extraOpts.flags & SOCK_OPT_KEEPALIVE) != 0; + break; + case SOCK_OPT_KEEPALIVE_INTERVAL: + v = extraOpts.keepalive_ms; + break; + case SOCK_OPT_TCP_NO_DELAY: + v = (extraOpts.flags & SOCK_OPT_TCP_NO_DELAY) != 0; + break; + case SOCK_OPT_SEND_BUF_SIZE: + v = extraOpts.send_buf_size; + break; + case SOCK_OPT_MCAST_JOIN: + return SOCK_ERR_INVAL; + default: + return Socket::get_option(opt, value, len); + } + + memcpy(value, &v, sizeof(v)); + *len = sizeof(uint32_t); + return SOCK_OK; + } + int32_t bind(const SockBindSpec& spec_in, uint16_t port) override { netlog_socket_event_t ev{}; ev.comp = NETLOG_COMP_TCP; @@ -328,120 +214,44 @@ class TCPSocket : public Socket { ev.u0 = port; ev.bind_spec = spec_in; netlog_socket_event(&extraOpts, &ev); - if (role != SOCK_ROLE_SERVER) return SOCK_ERR_PERM; + if (role != SOCKET_SERVER) return SOCK_ERR_PERM; if (bound) return SOCK_ERR_BOUND; + if (!ownerSocket) return SOCK_ERR_SYS; SockBindSpec spec = spec_in; bool empty = spec.kind == BIND_L3 && spec.l3_id == 0 && spec.ifindex == 0 && spec.ver == 0 && ipv6_is_unspecified(spec.ip); if (empty) spec.kind = BIND_ANY; - uint8_t ids[SOCK_MAX_L3]; - ip_version_t vers[SOCK_MAX_L3]; - int n = 0; - if (spec.kind == BIND_L3){ if (!spec.l3_id) return SOCK_ERR_INVAL; - l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(spec.l3_id); l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(spec.l3_id); - bool ok4 = (spec.ver == 0 || spec.ver == IP_VER4) && is_valid_v4_l3_for_bind(v4); bool ok6 = (spec.ver == 0 || spec.ver == IP_VER6) && is_valid_v6_l3_for_bind(v6); - if (!ok4 && !ok6) return SOCK_ERR_INVAL; - - if (ok4 && n < SOCK_MAX_L3) { - ids[n] = spec.l3_id; - vers[n] = IP_VER4; - ++n; - } - if (ok6 && n < SOCK_MAX_L3) { - ids[n] = spec.l3_id; - vers[n] = IP_VER6; - ++n; - } - } else if (spec.kind == BIND_L2){ - if (!add_all_l3_on_l2(spec.ifindex, spec.ver, ids, vers, n)) return SOCK_ERR_INVAL; + } else if (spec.kind == BIND_L2) { + l2_interface_t* l2 = l2_interface_find_by_index(spec.ifindex); + if (!l2 || !l2->is_up) return SOCK_ERR_INVAL; } else if (spec.kind == BIND_IP){ if (spec.ver == IP_VER4){ uint32_t v4ip = 0; memcpy(&v4ip, spec.ip, 4); - if (ipv4_is_unspecified(v4ip)) { - if (!add_all_l3_any(IP_VER4, ids, vers, n)) return SOCK_ERR_INVAL; - } else { - l3_ipv4_interface_t* ipif = l3_ipv4_find_by_ip(v4ip); - if (!is_valid_v4_l3_for_bind(ipif)) return SOCK_ERR_INVAL; - ids[n] = ipif->l3_id; - vers[n] = IP_VER4; - ++n; - } + if (!ipv4_is_unspecified(v4ip) && !is_valid_v4_l3_for_bind(l3_ipv4_find_by_ip(v4ip))) return SOCK_ERR_INVAL; } else if (spec.ver == IP_VER6) { - if (ipv6_is_unspecified(spec.ip)) { - if (!add_all_l3_any(IP_VER6, ids, vers, n)) return SOCK_ERR_INVAL; - } else { - l3_ipv6_interface_t* ipif = l3_ipv6_find_by_ip(spec.ip); - if (!is_valid_v6_l3_for_bind(ipif)) return SOCK_ERR_INVAL; - ids[n] = ipif->l3_id; - vers[n] = IP_VER6; - ++n; - } + if (!ipv6_is_unspecified(spec.ip) && !is_valid_v6_l3_for_bind(l3_ipv6_find_by_ip(spec.ip))) return SOCK_ERR_INVAL; } else return SOCK_ERR_INVAL; - } else if (spec.kind == BIND_ANY || spec.kind == BIND_ANY4 || spec.kind == BIND_ANY6){ - ip_version_t ver = spec.ver; - if (spec.kind == BIND_ANY4) ver = IP_VER4; - else if (spec.kind == BIND_ANY6) ver = IP_VER6; - if (!add_all_l3_any(ver, ids, vers, n)) return SOCK_ERR_INVAL; - } else return SOCK_ERR_INVAL; - - if (n==0) return SOCK_ERR_INVAL; - - uint8_t dedup_ids[SOCK_MAX_L3]; - ip_version_t dedup_ver[SOCK_MAX_L3]; - int m = 0; + } else if (spec.kind != BIND_ANY && spec.kind != BIND_ANY4 && spec.kind != BIND_ANY6) return SOCK_ERR_INVAL; - for (int i = 0; i < n; ++i) { - bool seen = false; - for (int j = 0; j < m; ++j) { - if (dedup_ids[j] == ids[i] && dedup_ver[j] == vers[i]) { - seen = true; - break; - } - } - if (!seen && m < SOCK_MAX_L3) { - dedup_ids[m] = ids[i]; - dedup_ver[m] = vers[i]; - ++m; - } - } - - if (m==0) return SOCK_ERR_INVAL; - - uint8_t bound_ids[SOCK_MAX_L3]; - int bdone=0; - - for (int i = 0; i < m; ++i){ - uint8_t id = dedup_ids[i]; - bool ok = tcp_bind_l3(id, port, pid, dispatch, &extraOpts); - if (!ok){ - for (int j=0;j TCP_MAX_BACKLOG ? TCP_MAX_BACKLOG : max_backlog; if (backlogCap < 1) backlogCap = 1; @@ -449,7 +259,7 @@ class TCPSocket : public Socket { return SOCK_OK; } - TCPSocket* accept(){ + ksocket_t* accept(){ const int max_empty_iters = 200; const int ready_wait_iters = 4; int iter = 0; @@ -462,7 +272,8 @@ class TCPSocket : public Socket { iter = 0; while (1) { for (int i = 0; i < backlogLen;) { - TCPSocket* client = pending[i]; + ksocket_t* owner = pending[i]; + TCPSocket* client = owner ? (TCPSocket*)socket_core_impl(owner) : nullptr; if (client && client->flow.flow_generation) { bool readable = tcp_flow_readable(&client->flow) != 0; @@ -474,21 +285,28 @@ class TCPSocket : public Socket { } } - client = pop_pending_at(i); - if (client) delete client; + owner = pop_pending_at(i); + if (owner) { + socket_core_close_socket(owner); + socket_core_put(owner); + } } if (backlogLen == 0) return nullptr; for (int i = 0; i < backlogLen; ++i) { - TCPSocket* client = pending[i]; + ksocket_t* owner = pending[i]; + TCPSocket* client = owner ? (TCPSocket*)socket_core_impl(owner) : nullptr; if (!client) continue; if (!client->flow.flow_generation || tcp_flow_readable(&client->flow) || tcp_flow_recv_closed(&client->flow)) return pop_pending_at(i); } if (++iter > ready_wait_iters) { if (backlogLen >= backlogCap) { - TCPSocket* client = pop_pending_at(0); - if (client) delete client; + ksocket_t* owner = pop_pending_at(0); + if (owner) { + socket_core_close_socket(owner); + socket_core_put(owner); + } } return nullptr; } @@ -496,95 +314,37 @@ class TCPSocket : public Socket { } } - int32_t connect(SockDstKind kind, const void* dst, uint16_t port) { + int32_t connect(const net_l4_endpoint* dst) { netlog_socket_event_t ev{}; ev.comp = NETLOG_COMP_TCP; ev.action = NETLOG_ACT_CONNECT; ev.pid = pid; - ev.dst_kind = kind; - ev.u0 = port; - if (kind == DST_ENDPOINT && dst) ev.dst_ep = *(const net_l4_endpoint*)dst; - if (kind == DST_DOMAIN) ev.s0 = (const char*)dst; + if (dst) ev.dst_ep = *dst; netlog_socket_event(&extraOpts, &ev); - if (role != SOCK_ROLE_CLIENT) return SOCK_ERR_PERM; + if (role != SOCKET_CLIENT) return SOCK_ERR_PERM; if (connected) return SOCK_ERR_STATE; - if (!dst) return SOCK_ERR_INVAL; + if (!dst || !dst->port) return SOCK_ERR_INVAL; - net_l4_endpoint d{}; + net_l4_endpoint d = *dst; uint8_t chosen_l3 = 0; - uint8_t allow_v4[SOCK_MAX_L3]; - uint8_t allow_v6[SOCK_MAX_L3]; - int n4 = 0; - int n6 = 0; - - for (int i = 0; i < bound_l3_count; ++i) { - uint8_t id = bound_l3[i]; - - l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(id); - if (is_valid_v4_l3_for_bind(v4) && n4 < SOCK_MAX_L3) allow_v4[n4++] = id; - - l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(id); - if (is_valid_v6_l3_for_bind(v6) && n6 < SOCK_MAX_L3) allow_v6[n6++] = id; - } - - 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; - - if (d.ver == IP_VER6) { - ipv6_tx_plan_t p6; - if (!ipv6_build_tx_plan(d.ip, nullptr, n6 ? allow_v6 : nullptr, n6, &p6)) return SOCK_ERR_SYS; - chosen_l3 = p6.l3_id; - } else if (d.ver == IP_VER4) { - uint32_t dip = 0; - memcpy(&dip, d.ip, 4); - ipv4_tx_plan_t p4; - if (!ipv4_build_tx_plan(dip, nullptr, n4 ? allow_v4 : nullptr, n4, &p4)) return SOCK_ERR_SYS; - chosen_l3 = p4.l3_id; - } else return SOCK_ERR_INVAL; - } else if (kind == DST_DOMAIN){ - const char* host = (const char*)dst; - if (!port) return SOCK_ERR_INVAL; - - uint8_t v6addr[16]; - memset(v6addr, 0, 16); - uint32_t v4addr = 0; - - dns_result_t dr6 = dns_resolve_aaaa(host, v6addr, TCP_DNS_SEL, TCP_DNS_TIMEOUT_MS); - dns_result_t dr4 = dns_resolve_a(host, &v4addr, TCP_DNS_SEL, TCP_DNS_TIMEOUT_MS); - - if (dr6 != DNS_OK && dr4 != DNS_OK) return SOCK_ERR_DNS; - - if (dr6 == DNS_OK) { - net_l4_endpoint d6{}; - d6.ver = IP_VER6; - memcpy(d6.ip, v6addr, 16); - d6.port = port; - - ipv6_tx_plan_t p6; - if (ipv6_build_tx_plan(d6.ip, nullptr, n6 ? allow_v6 : nullptr, n6, &p6)) { - d = d6; - chosen_l3 = p6.l3_id; - } - } - - if (!chosen_l3 && dr4 == DNS_OK) { - net_l4_endpoint d4{}; - make_ep(v4addr, port, IP_VER4, &d4); - - uint32_t dip = 0; - memcpy(&dip, d4.ip, 4); - ipv4_tx_plan_t p4; - if (ipv4_build_tx_plan(dip, nullptr, n4 ? allow_v4 : nullptr, n4, &p4)) { - d = d4; - chosen_l3 = p4.l3_id; - } - } - - if (!chosen_l3) return SOCK_ERR_SYS; + uint8_t allow_v4[MAX_L2_INTERFACES * MAX_IPV4_PER_INTERFACE]; + uint8_t allow_v6[MAX_L2_INTERFACES * MAX_IPV6_PER_INTERFACE]; + uint32_t n4 = bound ? socket_bind_l3_list(&bindSpec, IP_VER4, allow_v4, MAX_L2_INTERFACES * MAX_IPV4_PER_INTERFACE) : 0; + uint32_t n6 = bound ? socket_bind_l3_list(&bindSpec, IP_VER6, allow_v6, MAX_L2_INTERFACES * MAX_IPV6_PER_INTERFACE) : 0; + + if (d.ver == IP_VER6) { + if (bound && n6 == 0) return SOCK_ERR_SYS; + ipv6_tx_plan_t p6; + if (!ipv6_build_tx_plan(d.ip, nullptr, n6 ? allow_v6 : nullptr, n6, &p6)) return SOCK_ERR_SYS; + chosen_l3 = p6.l3_id; + } else if (d.ver == IP_VER4) { + if (bound && n4 == 0) return SOCK_ERR_SYS; + uint32_t dip = 0; + memcpy(&dip, d.ip, 4); + ipv4_tx_plan_t p4; + if (!ipv4_build_tx_plan(dip, nullptr, n4 ? allow_v4 : nullptr, n4, &p4)) return SOCK_ERR_SYS; + chosen_l3 = p4.l3_id; } else return SOCK_ERR_INVAL; if (!chosen_l3) return SOCK_ERR_SYS; @@ -598,13 +358,16 @@ class TCPSocket : public Socket { } else return SOCK_ERR_INVAL; if (localPort == 0) { - int p = tcp_alloc_ephemeral_l3(chosen_l3, pid, dispatch); + if (!ownerSocket) return SOCK_ERR_SYS; + int p = socket_bind_alloc_ephemeral_l3(ownerSocket, PROTO_TCP, chosen_l3, pid); if (p < 0) return SOCK_ERR_NO_PORT; localPort = (uint16_t)p; } - clear_bound_l3(); - add_bound_l3(chosen_l3); + bindSpec = {}; + bindSpec.kind = BIND_L3; + bindSpec.ver = d.ver; + bindSpec.l3_id = chosen_l3; bound = true; flow = tcp_data{}; @@ -702,7 +465,12 @@ class TCPSocket : public Socket { flow = tcp_data{}; } - for (int i = 0; i < backlogLen; ++i) delete pending[i]; + for (int i = 0; i < backlogLen; ++i) { + if (!pending[i]) continue; + socket_core_close_socket(pending[i]); + socket_core_put(pending[i]); + pending[i] = nullptr; + } backlogLen = 0; return Socket::close(); diff --git a/kernel/networking/transport_layer/socket_udp.hpp b/kernel/networking/transport_layer/socket_udp.hpp index c9df2736..e25a7ba2 100644 --- a/kernel/networking/transport_layer/socket_udp.hpp +++ b/kernel/networking/transport_layer/socket_udp.hpp @@ -3,7 +3,6 @@ #include "networking/transport_layer/udp.h" #include "types.h" #include "std/memory.h" -#include "networking/application_layer/dns/dns.h" #include "net/socket_types.h" #include "networking/internet_layer/ipv4_route.h" #include "networking/internet_layer/ipv6_route.h" @@ -16,26 +15,20 @@ #include "sysregs.h" static constexpr int32_t UDP_RING_CAP = 1024; -static constexpr dns_server_sel_t UDP_DNS_SEL = DNS_USE_BOTH; -static constexpr uint32_t UDP_DNS_TIMEOUT_MS = 3000; class UDPSocket : public Socket { - inline static UDPSocket* s_list_head = nullptr; - sizedptr ring[UDP_RING_CAP]; net_l4_endpoint src_eps[UDP_RING_CAP]; int32_t r_head = 0; int32_t r_tail = 0; uint32_t rx_bytes = 0; - UDPSocket* next = nullptr; static bool is_valid_v4_l3_for_bind(l3_ipv4_interface_t* v4) { if (!v4) return false; if (!v4->l2) return false; if (!v4->l2->is_up) return false; if (v4->mode == IPV4_CFG_DISABLED) return false; - if (!v4->port_manager) return false; return true; } @@ -45,7 +38,6 @@ class UDPSocket : public Socket { if (!v6->l2->is_up) return false; if (v6->cfg == IPV6_CFG_DISABLE) return false; if (v6->dad_state != IPV6_DAD_OK) return false; - if (!v6->port_manager) return false; return true; } @@ -94,70 +86,6 @@ class UDPSocket : public Socket { return false; } - 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) return false; - if (!s->bound) return false; - if (s->localPort != dst_port) return false; - if (!dst_ip_addr) return false; - - if (ver == IP_VER4) { - uint32_t dip = *(const uint32_t*)dst_ip_addr; - bool lb = dip == 0xFFFFFFFFu; - bool mc = ipv4_is_multicast(dip); - if (mc && !udp_mcast_match(s, IP_VER4, dst_ip_addr)) return false; - - 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 (!is_valid_v4_l3_for_bind(v4)) continue; - if (v4->l2->ifindex != ifx) continue; - if (lb) return true; - if (mc) return true; - - if (v4->mask) { - uint32_t b = ipv4_broadcast_calc(v4->ip, v4->mask); - if (b == dip)return true; - } - - if (v4->ip == dip) return true; - } - return false; - } - - if (ver == IP_VER6) { - bool mcast = ipv6_is_multicast((const uint8_t*)dst_ip_addr); - if (mcast && !udp_mcast_match(s, IP_VER6, dst_ip_addr)) return false; - - 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 (!is_valid_v6_l3_for_bind(v6)) continue; - if (v6->l2->ifindex != ifx) continue; - if (mcast && ipv6_is_linkscope_mcast((const uint8_t*)dst_ip_addr) && !ipv6_is_linklocal(v6->ip)) continue; - - if (mcast) return true; - if (memcmp(v6->ip, dst_ip_addr, 16) == 0) return true; - } - return false; - } - - return false; - } - - static uint32_t dispatch(uint8_t ifindex, ip_version_t ipver, const void* src_ip_addr, const void* dst_ip_addr, netpkt_t* pkt, uint16_t src_port, uint16_t dst_port) { - if (!pkt) return 0; - uint32_t ret = netpkt_len(pkt); - irq_flags_t irq = irq_save_disable(); - - for (UDPSocket* s = s_list_head; s; s = s->next) { - if (!socket_matches_dst(s, ifindex, ipver, dst_ip_addr, dst_port)) continue; - s->on_receive(ipver, src_ip_addr, src_port, pkt); - } - - irq_restore(irq); - return ret; - } - void on_receive(ip_version_t ver, const void* src_ip_addr, uint16_t src_port, netpkt_t* pkt) { if (!pkt) return; uintptr_t ptr = netpkt_data(pkt); @@ -196,149 +124,13 @@ class UDPSocket : public Socket { remoteEP = src_eps[(r_tail + UDP_RING_CAP - 1) % UDP_RING_CAP]; } - void insert_in_list() { - for (UDPSocket* it = s_list_head; it; it = it->next) if (it == this) return; - next = s_list_head; - s_list_head = this; - } - - void remove_from_list() { - UDPSocket** cur = &s_list_head; - int hops = 0; - while (*cur && hops++ < UDP_RING_CAP * 4) { - UDPSocket* p = *cur; - if (((uintptr_t)p & HIGH_VA) != HIGH_VA) break; //TODO this check should be useless but for now it prevents crashes if someone modifies the list, remove it once the issue is fixed - if (p == this) { - *cur = p->next; - break; - } - if (p->next && (((uintptr_t)p->next & HIGH_VA) != HIGH_VA)) break; - cur = &(p->next); - } - next = nullptr; - } - - bool add_all_l3_on_l2(uint8_t ifindex, ip_version_t ver, uint8_t* tmp_ids, int& n) { - l2_interface_t* l2 = l2_interface_find_by_index(ifindex); - if (!l2) return false; - if (!l2->is_up) return false; - - if ((ver == 0 || ver == IP_VER4)) { - for (int s = 0; s < MAX_IPV4_PER_INTERFACE; ++s) { - l3_ipv4_interface_t* v4 = l2->l3_v4[s]; - if (!is_valid_v4_l3_for_bind(v4)) continue; - if (n < SOCK_MAX_L3) tmp_ids[n++] = v4->l3_id; - } - } - - if ((ver == 0 || ver == IP_VER6)) { - for (int s = 0; s < MAX_IPV6_PER_INTERFACE; ++s) { - l3_ipv6_interface_t* v6 = l2->l3_v6[s]; - if (!is_valid_v6_l3_for_bind(v6)) continue; - if (n < SOCK_MAX_L3) tmp_ids[n++] = v6->l3_id; - } - } - - return n > 0; - } - - bool add_all_l3_any(ip_version_t ver, 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; - if (!l2->is_up) continue; - - if ((ver == 0 || ver == IP_VER4)) { - for (int s = 0; s < MAX_IPV4_PER_INTERFACE; ++s) { - l3_ipv4_interface_t* v4 = l2->l3_v4[s]; - if (!is_valid_v4_l3_for_bind(v4)) continue; - if (n < SOCK_MAX_L3) tmp_ids[n++] = v4->l3_id; - } - } - - if ((ver == 0 || ver == IP_VER6)) { - for (int s = 0; s < MAX_IPV6_PER_INTERFACE; ++s) { - l3_ipv6_interface_t* v6 = l2->l3_v6[s]; - if (!is_valid_v6_l3_for_bind(v6)) continue; - if (n < SOCK_MAX_L3) tmp_ids[n++] = v6->l3_id; - } - } - } - return n > 0; - } - - bool add_l3_for_mcast_group_on_l2(l2_interface_t* l2, const net_l4_endpoint* group, uint8_t* tmp_ids, int& n) { - if (!l2) return false; - if (!l2->is_up) return false; - if (!group) return false; - - bool added = false; - if (group->ver == IP_VER4) { - uint32_t g = 0; - memcpy(&g, group->ip, 4); - if (!ipv4_is_multicast(g)) return false; - - for (int s = 0; s < MAX_IPV4_PER_INTERFACE; ++s) { - l3_ipv4_interface_t* v4 = l2->l3_v4[s]; - if (!is_valid_v4_l3_for_bind(v4)) continue; - if (n < SOCK_MAX_L3) tmp_ids[n++] = v4->l3_id; - added = true; - } - } else if (group->ver == IP_VER6) { - if (!ipv6_is_multicast(group->ip)) return false; - bool linkscope = ipv6_is_linkscope_mcast(group->ip); - - for (int s = 0; s < MAX_IPV6_PER_INTERFACE; ++s) { - l3_ipv6_interface_t* v6 = l2->l3_v6[s]; - if (!is_valid_v6_l3_for_bind(v6)) continue; - if (linkscope && !ipv6_is_linklocal(v6->ip)) continue; - if (n < SOCK_MAX_L3) tmp_ids[n++] = v6->l3_id; - added = true; - } - } - return added; - } - - bool add_l3_for_mcast_group_any(const net_l4_endpoint* group, uint8_t* tmp_ids, int& n) { - bool added = false; - uint8_t cnt = l2_interface_count(); - - for (uint8_t i = 0; i < cnt; ++i) { - l2_interface_t* l2 = l2_interface_at(i); - if (add_l3_for_mcast_group_on_l2(l2, group, tmp_ids, n)) added = true; - } - - return added; - } - - 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 pick_v4_l3_for_unicast(uint32_t dip, const uint8_t* candidates, int n, uint8_t* out_l3) { - if (!out_l3) return false; - ipv4_tx_plan_t plan; - if (!ipv4_build_tx_plan(dip, nullptr, candidates, n, &plan)) return false; - *out_l3 = plan.l3_id; - return true; - } - - static bool pick_v6_l3_for_unicast(const uint8_t dst_ip[16], const uint8_t* candidates, int n, uint8_t* out_l3) { - if (!out_l3) return false; - ipv6_tx_plan_t plan; - if (!ipv6_build_tx_plan(dst_ip, nullptr, candidates, n, &plan)) return false; - *out_l3 = plan.l3_id; - return true; - } - void leave_mcast_groups() { if ((extraOpts.flags & SOCK_OPT_MCAST_JOIN) == 0) return; uint32_t count = extraOpts.mcast_count; if (count > SOCK_MAX_MCAST_GROUPS) count = SOCK_MAX_MCAST_GROUPS; + uint8_t ids[MAX_L2_INTERFACES * MAX_IPV6_PER_INTERFACE]; for (uint32_t gidx = 0; gidx < count; ++gidx) { const net_l4_endpoint* group = &extraOpts.mcast_groups[gidx]; @@ -347,8 +139,9 @@ class UDPSocket : public Socket { memcpy(&g, group->ip, 4); if (!ipv4_is_multicast(g)) continue; - for (int i = 0; i < bound_l3_count; ++i) { - l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(bound_l3[i]); + uint32_t n = socket_bind_l3_list(&bindSpec, IP_VER4, ids, MAX_L2_INTERFACES * MAX_IPV4_PER_INTERFACE); + for (uint32_t i = 0; i < n; ++i) { + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(ids[i]); if (!is_valid_v4_l3_for_bind(v4)) continue; l2_ipv4_mcast_leave(v4->l2->ifindex,g); } @@ -356,8 +149,9 @@ class UDPSocket : public Socket { if (!ipv6_is_multicast(group->ip)) continue; bool linkscope = ipv6_is_linkscope_mcast(group->ip); - for (int i = 0; i < bound_l3_count; ++i) { - l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(bound_l3[i]); + uint32_t n = socket_bind_l3_list(&bindSpec, IP_VER6, ids, MAX_L2_INTERFACES * MAX_IPV6_PER_INTERFACE); + for (uint32_t i = 0; i < n; ++i) { + l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(ids[i]); if (!is_valid_v6_l3_for_bind(v6)) continue; if (linkscope && !ipv6_is_linklocal(v6->ip)) continue; l2_ipv6_mcast_leave(v6->l2->ifindex,group->ip); @@ -367,17 +161,11 @@ class UDPSocket : public Socket { } public: - UDPSocket(uint8_t r, uint32_t pid_, const SocketExtraOptions* extra = nullptr) : Socket(PROTO_UDP, r, extra) { + UDPSocket(ksocket_t* owner, uint8_t r, uint32_t pid_, const SocketExtraOptions* extra = nullptr) : Socket(owner, PROTO_UDP, r, extra) { pid = pid_; - irq_flags_t irq = irq_save_disable(); - insert_in_list(); - irq_restore(irq); } ~UDPSocket() override { - irq_flags_t irq = irq_save_disable(); //TODO locking is needed asap - remove_from_list(); - irq_restore(irq); close(); } @@ -389,251 +177,107 @@ class UDPSocket : public Socket { ev.u0 = port; ev.bind_spec = spec_in; netlog_socket_event(&extraOpts, &ev); - if (role != SOCK_ROLE_SERVER) return SOCK_ERR_PERM; + if (role != SOCKET_SERVER) return SOCK_ERR_PERM; if (bound) return SOCK_ERR_BOUND; + if (!ownerSocket) return SOCK_ERR_SYS; SockBindSpec spec = spec_in; bool empty = spec.kind == BIND_L3 && spec.l3_id == 0 && spec.ifindex == 0 && spec.ver == 0 && ipv6_is_unspecified(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) return SOCK_ERR_INVAL; - - l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(spec.l3_id); - l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(spec.l3_id); - - bool ok4 = (spec.ver == 0 || spec.ver == IP_VER4) && is_valid_v4_l3_for_bind(v4); - bool ok6 = (spec.ver == 0 || spec.ver == IP_VER6) && is_valid_v6_l3_for_bind(v6); - - if (!ok4 && !ok6) return SOCK_ERR_INVAL; - - if (ok4 && n < SOCK_MAX_L3) ids[n++] = spec.l3_id; - if (ok6 && n < SOCK_MAX_L3) ids[n++] = spec.l3_id; + uint8_t one[1]; + if (spec.ver == IP_VER4 && !socket_bind_l3_list(&spec, IP_VER4, one, 1)) return SOCK_ERR_INVAL; + if (spec.ver == IP_VER6 && !socket_bind_l3_list(&spec, IP_VER6, one, 1)) return SOCK_ERR_INVAL; + if (!spec.ver && !socket_bind_l3_list(&spec, IP_VER4, one, 1) && !socket_bind_l3_list(&spec, IP_VER6, one, 1)) return SOCK_ERR_INVAL; } else if (spec.kind == BIND_L2) { - bool used_mcast = false; - if ((extraOpts.flags & SOCK_OPT_MCAST_JOIN) && extraOpts.mcast_count) { - l2_interface_t* l2 = l2_interface_find_by_index(spec.ifindex); - uint32_t count = extraOpts.mcast_count; - if (count > SOCK_MAX_MCAST_GROUPS) count = SOCK_MAX_MCAST_GROUPS; - - for (uint32_t i = 0; i < count; ++i) { - const net_l4_endpoint* group = &extraOpts.mcast_groups[i]; - if (spec.ver && group->ver != spec.ver) continue; - if (add_l3_for_mcast_group_on_l2(l2, group, ids, n)) used_mcast = true; - } - } - if (!used_mcast && !add_all_l3_on_l2(spec.ifindex, spec.ver, ids, n)) return SOCK_ERR_INVAL; + l2_interface_t* l2 = l2_interface_find_by_index(spec.ifindex); + if (!l2 || !l2->is_up) return SOCK_ERR_INVAL; } else if (spec.kind == BIND_IP) { if (spec.ver == IP_VER4) { uint32_t v4ip = 0; memcpy(&v4ip, spec.ip, 4); - if (ipv4_is_unspecified(v4ip)) { - if (!add_all_l3_any(IP_VER4, ids, n)) return SOCK_ERR_INVAL; - } else if (ipv4_is_multicast(v4ip)) { - net_l4_endpoint group; - memset(&group, 0, sizeof(group)); - group.ver = IP_VER4; - memcpy(group.ip, &v4ip, 4); - if (spec.ifindex) { - l2_interface_t* l2 = l2_interface_find_by_index(spec.ifindex); - if (!add_l3_for_mcast_group_on_l2(l2, &group, ids, n)) return SOCK_ERR_INVAL; - } else if (!add_l3_for_mcast_group_any(&group, ids, n)) return SOCK_ERR_INVAL; - } else { - l3_ipv4_interface_t* ipif = l3_ipv4_find_by_ip(v4ip); - if (!is_valid_v4_l3_for_bind(ipif)) return SOCK_ERR_INVAL; - if (n < SOCK_MAX_L3) ids[n++] = ipif->l3_id; + if (!ipv4_is_unspecified(v4ip)) { + uint8_t one[1]; + if (!socket_bind_l3_list(&spec, IP_VER4, one, 1)) return SOCK_ERR_INVAL; } } else if (spec.ver == IP_VER6) { - if (ipv6_is_unspecified(spec.ip)) { - if (!add_all_l3_any(IP_VER6, ids, n)) return SOCK_ERR_INVAL; - } else if (ipv6_is_multicast(spec.ip)) { - net_l4_endpoint group; - memset(&group, 0, sizeof(group)); - group.ver = IP_VER6; - memcpy(group.ip, spec.ip, 16); - if (spec.ifindex) { - l2_interface_t* l2 = l2_interface_find_by_index(spec.ifindex); - if (!add_l3_for_mcast_group_on_l2(l2, &group, ids, n)) return SOCK_ERR_INVAL; - } else if (!add_l3_for_mcast_group_any(&group, ids, n)) return SOCK_ERR_INVAL; - } else { - l3_ipv6_interface_t* ipif6 = l3_ipv6_find_by_ip(spec.ip); - if (!is_valid_v6_l3_for_bind(ipif6)) return SOCK_ERR_INVAL; - if (n < SOCK_MAX_L3) ids[n++] = ipif6->l3_id; + if (!ipv6_is_unspecified(spec.ip)) { + uint8_t one[1]; + if (!socket_bind_l3_list(&spec, IP_VER6, one, 1)) return SOCK_ERR_INVAL; } - } else { - return SOCK_ERR_INVAL; - } - } else if (spec.kind == BIND_ANY || spec.kind == BIND_ANY4 || spec.kind == BIND_ANY6) { - ip_version_t ver = spec.ver; - if (spec.kind == BIND_ANY4) ver = IP_VER4; - else if (spec.kind == BIND_ANY6) ver = IP_VER6; - - if ((extraOpts.flags & SOCK_OPT_MCAST_JOIN) && extraOpts.mcast_count) { - bool used_mcast = false; - uint32_t count = extraOpts.mcast_count; - if (count > SOCK_MAX_MCAST_GROUPS) count = SOCK_MAX_MCAST_GROUPS; - - for (uint32_t i = 0; i < count; ++i) { - const net_l4_endpoint* group = &extraOpts.mcast_groups[i]; - if (ver && group->ver != ver) continue; - if (add_l3_for_mcast_group_any(group, ids, n)) used_mcast = true; - } - - if (!used_mcast && !add_all_l3_any(ver, ids, n)) return SOCK_ERR_INVAL; - } else if (!add_all_l3_any(ver, ids, n)) return SOCK_ERR_INVAL; - } else { - return SOCK_ERR_INVAL; - } - - if (n == 0) return SOCK_ERR_INVAL; + } else return SOCK_ERR_INVAL; + } else if (spec.kind != BIND_ANY && spec.kind != BIND_ANY4 && spec.kind != BIND_ANY6) return SOCK_ERR_INVAL; - uint8_t dedup[SOCK_MAX_L3]; - int m = 0; - for (int i = 0; i < n; ++i) { - bool seen = false; - for (int j = 0; j < m; ++j) { - if (dedup[j] == ids[i]) { - seen = true; - break; - } - } - if (!seen && m < SOCK_MAX_L3) dedup[m++] = ids[i]; - } - if (m == 0) return SOCK_ERR_INVAL; - - int bdone = 0; - for (int i = 0; i < m; ++i) { - uint8_t id = dedup[i]; - if (udp_bind_l3(id, port, pid, dispatch)) { - bdone++; - continue; - } - for (int j = 0; j < bdone; ++j) udp_unbind_l3(dedup[j], port, pid); - return SOCK_ERR_SYS; - } + if (!socket_bind_insert(ownerSocket, PROTO_UDP, &spec, port)) return SOCK_ERR_SYS; - clear_bound_l3(); - for (int i = 0; i < m; ++i) add_bound_l3(dedup[i]); + bindSpec = spec; + localPort = port; + bound = true; if (extraOpts.flags & SOCK_OPT_MCAST_JOIN) { uint32_t count = extraOpts.mcast_count; if (count > SOCK_MAX_MCAST_GROUPS) count = SOCK_MAX_MCAST_GROUPS; + uint8_t ids[MAX_L2_INTERFACES * MAX_IPV6_PER_INTERFACE]; for (uint32_t gidx = 0; gidx < count; ++gidx) { const net_l4_endpoint* group = &extraOpts.mcast_groups[gidx]; + uint32_t n = 0; bool joined = false; - bool applicable = false; - bool same_family_bound = false; - if (group->ver == IP_VER4) { uint32_t g = 0; memcpy(&g, group->ip, 4); if (!ipv4_is_multicast(g)) continue; - - for (int i = 0; i < bound_l3_count; ++i) { - l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(bound_l3[i]); + n = socket_bind_l3_list(&bindSpec, IP_VER4, ids, MAX_L2_INTERFACES * MAX_IPV4_PER_INTERFACE); + for (uint32_t i = 0; i < n; ++i) { + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(ids[i]); if (!is_valid_v4_l3_for_bind(v4)) continue; - same_family_bound = true; - applicable = true; if (l2_ipv4_mcast_join(v4->l2->ifindex, g)) joined = true; } } else if (group->ver == IP_VER6) { if (!ipv6_is_multicast(group->ip)) continue; bool linkscope = ipv6_is_linkscope_mcast(group->ip); - - for (int i = 0; i < bound_l3_count; ++i) { - l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(bound_l3[i]); + n = socket_bind_l3_list(&bindSpec, IP_VER6, ids, MAX_L2_INTERFACES * MAX_IPV6_PER_INTERFACE); + for (uint32_t i = 0; i < n; ++i) { + l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(ids[i]); if (!is_valid_v6_l3_for_bind(v6)) continue; - same_family_bound = true; if (linkscope && !ipv6_is_linklocal(v6->ip)) continue; - applicable = true; if (l2_ipv6_mcast_join(v6->l2->ifindex, group->ip)) joined = true; } } - if (!applicable && !same_family_bound) continue; if (!joined) { leave_mcast_groups(); - for (int i = 0; i < m; ++i) udp_unbind_l3(dedup[i], port, pid); - clear_bound_l3(); + socket_bind_remove_socket(ownerSocket); + memset(&bindSpec, 0, sizeof(bindSpec)); + bound = false; + localPort = 0; return SOCK_ERR_SYS; } } } - localPort = port; - bound = true; return SOCK_OK; } - int64_t sendto(SockDstKind kind, const void* dst, uint16_t port, const void* buf, uint64_t len) { + int64_t sendto(const net_l4_endpoint* dst, const void* buf, uint64_t len) { netlog_socket_event_t ev{}; ev.comp = NETLOG_COMP_UDP; ev.action = NETLOG_ACT_SENDTO; ev.pid = pid; - ev.dst_kind = kind; - ev.u0 = port; ev.u1 = (uint32_t)len; - if (kind == DST_ENDPOINT && dst) ev.dst_ep = *(const net_l4_endpoint*)dst; - if (kind == DST_DOMAIN) ev.s0 = (const char*)dst; - netlog_socket_event(&extraOpts, &ev); - if (!dst) return SOCK_ERR_INVAL; - if (!buf) return SOCK_ERR_INVAL; - if (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; - - uint8_t a6[16]; - memset(a6, 0, 16); - uint32_t a4 = 0; - - dns_result_t dr6 = dns_resolve_aaaa(host, a6, UDP_DNS_SEL, UDP_DNS_TIMEOUT_MS); - dns_result_t dr4 = dns_resolve_a(host, &a4, UDP_DNS_SEL, UDP_DNS_TIMEOUT_MS); - if (dr6 != DNS_OK && dr4 != DNS_OK) return SOCK_ERR_DNS; - - uint8_t allow_v4[SOCK_MAX_L3]; - uint8_t allow_v6[SOCK_MAX_L3]; - int n4 = 0; - int n6 = 0; - for (int i = 0; i < bound_l3_count; ++i) { - uint8_t id = bound_l3[i]; - if (n4 < SOCK_MAX_L3 && l3_ipv4_find_by_id(id)) allow_v4[n4++] = id; - if (n6 < SOCK_MAX_L3 && l3_ipv6_find_by_id(id)) allow_v6[n6++] = id; - } - - if (dr6 == DNS_OK) { - ipv6_tx_plan_t p6; - if (ipv6_build_tx_plan(a6, nullptr, n6 ? allow_v6 : nullptr, n6, &p6)) { - d.ver = IP_VER6; - memcpy(d.ip, a6, 16); - d.port = port; - } - } - - if (d.ver == 0 && dr4 == DNS_OK) { - ipv4_tx_plan_t p4; - if (ipv4_build_tx_plan(a4, nullptr, n4 ? allow_v4 : nullptr, n4, &p4)) { - make_ep(a4, port, IP_VER4, &d); - } - } - - if (d.ver == 0) return SOCK_ERR_SYS; - } else { - return SOCK_ERR_INVAL; + if (dst) { + ev.dst_ep = *dst; + ev.u0 = dst->port; } + netlog_socket_event(&extraOpts, &ev); + if (!dst || !dst->port) return SOCK_ERR_INVAL; + if (!buf || !len) return SOCK_ERR_INVAL; + if (!bound && !ownerSocket) return SOCK_ERR_SYS; + net_l4_endpoint d = *dst; sizedptr pay; pay.ptr = (uintptr_t)buf; pay.size = (uint32_t)len; @@ -642,34 +286,17 @@ class UDPSocket : public Socket { uint32_t dip = 0; memcpy(&dip, d.ip, 4); - bool is_bcast = false; - if (dip == 0xFFFFFFFFu) is_bcast = true; - else { - uint8_t dummy = 0; - if (is_dbcast(dip, &dummy)) is_bcast = true; - } + if (dip == 0xFFFFFFFFu || is_dbcast(dip, nullptr)) { + if (!bound || !localPort) return SOCK_ERR_BOUND; - if (is_bcast) { - if (dip == 0xFFFFFFFFu) { - if (bound_l3_count == 0) return SOCK_ERR_SYS; + uint8_t ids[MAX_L2_INTERFACES * MAX_IPV4_PER_INTERFACE]; + uint32_t n = socket_bind_l3_list(&bindSpec, IP_VER4, ids, MAX_L2_INTERFACES * MAX_IPV4_PER_INTERFACE); + if (!n) 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 (!is_valid_v4_l3_for_bind(v4)) continue; - if (!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 if (localPort == 0) { - int p = udp_alloc_ephemeral_l3(bl3, pid, dispatch); - if (p < 0) continue; - localPort = (uint16_t)p; - } + if (dip == 0xFFFFFFFFu) { + for (uint32_t i = 0; i < n; ++i) { + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(ids[i]); + if (!is_valid_v4_l3_for_bind(v4) || !v4->l2) continue; net_l4_endpoint src; src.ver = IP_VER4; @@ -679,7 +306,7 @@ class UDPSocket : public Socket { ipv4_tx_opts_t tx; tx.scope = IP_TX_BOUND_L3; - tx.index = bl3; + tx.index = ids[i]; udp_send_segment(&src, &d, pay, &tx, (extraOpts.flags & SOCK_OPT_TTL) ? extraOpts.ttl : 0, (extraOpts.flags & SOCK_OPT_DONTFRAG) ? 1 : 0); } @@ -690,22 +317,12 @@ class UDPSocket : public Socket { uint8_t db_l3 = 0; if (!is_dbcast(dip, &db_l3)) return SOCK_ERR_SYS; + bool allowed = false; + for (uint32_t i = 0; i < n; ++i) if (ids[i] == db_l3) allowed = true; + if (!allowed) return SOCK_ERR_SYS; l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(db_l3); - if (!is_valid_v4_l3_for_bind(v4)) return SOCK_ERR_SYS; - if (!v4->l2) return SOCK_ERR_SYS; - - if (!bound) { - int p = udp_alloc_ephemeral_l3(db_l3, pid, dispatch); - if (p < 0) return SOCK_ERR_NO_PORT; - localPort = (uint16_t)p; - add_bound_l3(db_l3); - bound = true; - } else if (localPort == 0) { - int p = udp_alloc_ephemeral_l3(db_l3, pid, dispatch); - if (p < 0) return SOCK_ERR_NO_PORT; - localPort = (uint16_t)p; - } + if (!is_valid_v4_l3_for_bind(v4) || !v4->l2) return SOCK_ERR_SYS; net_l4_endpoint src; src.ver = IP_VER4; @@ -723,25 +340,15 @@ class UDPSocket : public Socket { } if (ipv4_is_multicast(dip)) { - if (bound_l3_count == 0) return SOCK_ERR_SYS; + if (!bound || !localPort) return SOCK_ERR_BOUND; - 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 (!is_valid_v4_l3_for_bind(v4)) continue; - if (!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 if (localPort == 0) { - int p = udp_alloc_ephemeral_l3(bl3, pid, dispatch); - if (p < 0) continue; - localPort = (uint16_t)p; - } + uint8_t ids[MAX_L2_INTERFACES * MAX_IPV4_PER_INTERFACE]; + uint32_t n = socket_bind_l3_list(&bindSpec, IP_VER4, ids, MAX_L2_INTERFACES * MAX_IPV4_PER_INTERFACE); + if (!n) return SOCK_ERR_SYS; + + for (uint32_t i = 0; i < n; ++i) { + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(ids[i]); + if (!is_valid_v4_l3_for_bind(v4) || !v4->l2) continue; net_l4_endpoint src; src.ver = IP_VER4; @@ -751,7 +358,7 @@ class UDPSocket : public Socket { ipv4_tx_opts_t tx; tx.scope = IP_TX_BOUND_L3; - tx.index = bl3; + tx.index = ids[i]; udp_send_segment(&src, &d, pay, &tx, (extraOpts.flags & SOCK_OPT_TTL) ? extraOpts.ttl : 0, (extraOpts.flags & SOCK_OPT_DONTFRAG) ? 1 : 0); } @@ -760,13 +367,9 @@ class UDPSocket : public Socket { return (int64_t)len; } - uint8_t allowed_v4[SOCK_MAX_L3]; - int n_allowed = 0; - for (int i = 0; i < bound_l3_count && n_allowed < SOCK_MAX_L3; ++i) { - uint8_t id = bound_l3[i]; - if (l3_ipv4_find_by_id(id)) allowed_v4[n_allowed++] = id; - } - if (bound_l3_count > 0 && n_allowed == 0) return SOCK_ERR_SYS; + uint8_t allowed_v4[MAX_L2_INTERFACES * MAX_IPV4_PER_INTERFACE]; + uint32_t n_allowed = bound ? socket_bind_l3_list(&bindSpec, IP_VER4, allowed_v4, MAX_L2_INTERFACES * MAX_IPV4_PER_INTERFACE) : 0; + if (bound && n_allowed == 0) return SOCK_ERR_SYS; ipv4_tx_plan_t plan; if (!ipv4_build_tx_plan(dip, nullptr, n_allowed ? allowed_v4 : nullptr, n_allowed, &plan)) return SOCK_ERR_SYS; @@ -776,13 +379,16 @@ class UDPSocket : public Socket { if (!is_valid_v4_l3_for_bind(v4)) return SOCK_ERR_SYS; if (!bound) { - int p = udp_alloc_ephemeral_l3(chosen_l3, pid, dispatch); + int p = socket_bind_alloc_ephemeral_l3(ownerSocket, PROTO_UDP, chosen_l3, pid); if (p < 0) return SOCK_ERR_NO_PORT; localPort = (uint16_t)p; - add_bound_l3(chosen_l3); + bindSpec = {}; + bindSpec.kind = BIND_L3; + bindSpec.ver = IP_VER4; + bindSpec.l3_id = chosen_l3; bound = true; } else if (localPort == 0) { - int p = udp_alloc_ephemeral_l3(chosen_l3, pid, dispatch); + int p = socket_bind_alloc_ephemeral_l3(ownerSocket, PROTO_UDP, chosen_l3, pid); if (p < 0) return SOCK_ERR_NO_PORT; localPort = (uint16_t)p; } @@ -806,15 +412,16 @@ class UDPSocket : public Socket { bool is_mcast = ipv6_is_multicast(d.ip); if (is_mcast) { - if (!bound) return SOCK_ERR_BOUND; - if (!localPort) return SOCK_ERR_BOUND; - if (bound_l3_count == 0) return SOCK_ERR_SYS; + if (!bound || !localPort) return SOCK_ERR_BOUND; + + uint8_t ids[MAX_L2_INTERFACES * MAX_IPV6_PER_INTERFACE]; + uint32_t n = socket_bind_l3_list(&bindSpec, IP_VER6, ids, MAX_L2_INTERFACES * MAX_IPV6_PER_INTERFACE); + if (!n) return SOCK_ERR_SYS; int sent = 0; bool linkscope = ipv6_is_linkscope_mcast(d.ip); - for (int i = 0; i < bound_l3_count; ++i) { - uint8_t bl3 = bound_l3[i]; - l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(bl3); + for (uint32_t i = 0; i < n; ++i) { + l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(ids[i]); if (!is_valid_v6_l3_for_bind(v6)) continue; if (linkscope && !ipv6_is_linklocal(v6->ip)) continue; @@ -826,7 +433,7 @@ class UDPSocket : public Socket { ipv6_tx_opts_t tx; tx.scope = IP_TX_BOUND_L3; - tx.index = bl3; + tx.index = ids[i]; udp_send_segment(&src, &d, pay, &tx, (extraOpts.flags & SOCK_OPT_TTL) ? extraOpts.ttl : 0, (extraOpts.flags & SOCK_OPT_DONTFRAG) ? 1 : 0); sent++; @@ -837,13 +444,9 @@ class UDPSocket : public Socket { return (int64_t)len; } - uint8_t allowed_v6[SOCK_MAX_L3]; - int n_allowed = 0; - for (int i = 0; i < bound_l3_count && n_allowed < SOCK_MAX_L3; ++i) { - uint8_t id = bound_l3[i]; - if (l3_ipv6_find_by_id(id)) allowed_v6[n_allowed++] = id; - } - if (bound_l3_count > 0 && n_allowed == 0) return SOCK_ERR_SYS; + uint8_t allowed_v6[MAX_L2_INTERFACES * MAX_IPV6_PER_INTERFACE]; + uint32_t n_allowed = bound ? socket_bind_l3_list(&bindSpec, IP_VER6, allowed_v6, MAX_L2_INTERFACES * MAX_IPV6_PER_INTERFACE) : 0; + if (bound && n_allowed == 0) return SOCK_ERR_SYS; ipv6_tx_plan_t plan; if (!ipv6_build_tx_plan(d.ip, nullptr, n_allowed ? allowed_v6 : nullptr, n_allowed, &plan)) return SOCK_ERR_SYS; @@ -854,13 +457,16 @@ class UDPSocket : public Socket { if (!is_valid_v6_l3_for_bind(v6)) return SOCK_ERR_SYS; if (!bound) { - int p = udp_alloc_ephemeral_l3(chosen_l3, pid, dispatch); + int p = socket_bind_alloc_ephemeral_l3(ownerSocket, PROTO_UDP, chosen_l3, pid); if (p < 0) return SOCK_ERR_NO_PORT; localPort = (uint16_t)p; - add_bound_l3(chosen_l3); + bindSpec = {}; + bindSpec.kind = BIND_L3; + bindSpec.ver = IP_VER6; + bindSpec.l3_id = chosen_l3; bound = true; } else if (localPort == 0) { - int p = udp_alloc_ephemeral_l3(chosen_l3, pid, dispatch); + int p = socket_bind_alloc_ephemeral_l3(ownerSocket, PROTO_UDP, chosen_l3, pid); if (p < 0) return SOCK_ERR_NO_PORT; localPort = (uint16_t)p; } @@ -883,6 +489,19 @@ class UDPSocket : public Socket { return SOCK_ERR_INVAL; } + uint32_t enqueue_datagram(uint8_t ifindex, uint8_t l3_id, ip_version_t ipver, const void* src_ip_addr, const void* dst_ip_addr, netpkt_t* pkt, uint16_t src_port, uint16_t dst_port) { + if (!bound || localPort != dst_port || !pkt || !dst_ip_addr) return 0; + if (ipver == IP_VER4) { + uint32_t dip = 0; + memcpy(&dip, dst_ip_addr, 4); + if (ipv4_is_multicast(dip) && !udp_mcast_match(this, IP_VER4, dst_ip_addr)) return 0; + } else if (ipver == IP_VER6) { + if (ipv6_is_multicast((const uint8_t*)dst_ip_addr) && !udp_mcast_match(this, IP_VER6, dst_ip_addr)) return 0; + } + on_receive(ipver, src_ip_addr, src_port, pkt); + return netpkt_len(pkt); + } + int64_t recvfrom(void* buf, uint64_t len, net_l4_endpoint* src) { netlog_socket_event_t ev{}; ev.comp = NETLOG_COMP_UDP; @@ -910,6 +529,63 @@ class UDPSocket : public Socket { return tocpy; } + + int32_t set_option(int32_t opt, const void* value, uint32_t len) { + switch ((uint32_t)opt) { + case SOCK_OPT_KEEPALIVE: + case SOCK_OPT_KEEPALIVE_INTERVAL: + case SOCK_OPT_TCP_NO_DELAY: + case SOCK_OPT_SEND_BUF_SIZE: + return SOCK_ERR_INVAL; + case SOCK_OPT_MCAST_JOIN: { + if (!value || !len || (len % sizeof(net_l4_endpoint)) != 0) return SOCK_ERR_INVAL; + if (bound) return SOCK_ERR_STATE; + + uint32_t count = len / sizeof(net_l4_endpoint); + if (!count || count > SOCK_MAX_MCAST_GROUPS) return SOCK_ERR_INVAL; + + const net_l4_endpoint* groups = (const net_l4_endpoint*)value; + for (uint32_t i = 0; i < count; ++i) { + if (groups[i].ver != IP_VER4 && groups[i].ver != IP_VER6) return SOCK_ERR_INVAL; + if (groups[i].ver == IP_VER4) { + uint32_t ip = 0; + memcpy(&ip, groups[i].ip, 4); + if (!ipv4_is_multicast(ip)) return SOCK_ERR_INVAL; + } else if (!ipv6_is_multicast(groups[i].ip)) return SOCK_ERR_INVAL; + } + + extraOpts.flags |= SOCK_OPT_MCAST_JOIN; + extraOpts.mcast_count = (uint8_t)count; + memcpy(extraOpts.mcast_groups, groups, count * sizeof(net_l4_endpoint)); + return SOCK_OK; + } + default: + return Socket::set_option(opt, value, len); + } + } + + int32_t get_option(int32_t opt, void* value, uint32_t* len) const { + switch ((uint32_t)opt) { + case SOCK_OPT_KEEPALIVE: + case SOCK_OPT_KEEPALIVE_INTERVAL: + case SOCK_OPT_TCP_NO_DELAY: + case SOCK_OPT_SEND_BUF_SIZE: + return SOCK_ERR_INVAL; + case SOCK_OPT_MCAST_JOIN: { + if (!value || !len) return SOCK_ERR_INVAL; + uint32_t count = (extraOpts.flags & SOCK_OPT_MCAST_JOIN) ? extraOpts.mcast_count : 0; + if (count > SOCK_MAX_MCAST_GROUPS) count = SOCK_MAX_MCAST_GROUPS; + uint32_t need = count * sizeof(net_l4_endpoint); + if (*len < need) return SOCK_ERR_INVAL; + if (need) memcpy(value, extraOpts.mcast_groups, need); + *len = need; + return SOCK_OK; + } + default: + return Socket::get_option(opt, value, len); + } + } + int32_t close() override { leave_mcast_groups(); netlog_socket_event_t ev{}; diff --git a/kernel/networking/transport_layer/tcp.h b/kernel/networking/transport_layer/tcp.h index 1218b6ef..55ccf994 100644 --- a/kernel/networking/transport_layer/tcp.h +++ b/kernel/networking/transport_layer/tcp.h @@ -1,6 +1,5 @@ #pragma once -#include "networking/port_manager.h" #include "networking/internet_layer/ipv4.h" #include "networking/link_layer/eth.h" #include "networking/netpkt.h" @@ -82,6 +81,7 @@ typedef enum { #define TCP_MIN_RTO 200 #define TCP_MAX_RTO 60000 #define TCP_MSL_MS 30000 +#define SOCKET_DEFAULT_KEEPALIVE_MS 60000 #define TCP_2MSL_MS (2 * TCP_MSL_MS) #define TCP_MAX_RETRANS 8 #define TCP_MAX_PERSIST_PROBES 8 @@ -89,10 +89,8 @@ typedef enum { int find_flow(uint16_t local_port, ip_version_t ver, const void *local_ip, const void *remote_ip, uint16_t remote_port); bool tcp_get_ctx(uint16_t local_port, ip_version_t ver, const void *local_ip, const void *remote_ip, uint16_t remote_port, tcp_data *out_ctx); -bool tcp_bind_l3(uint8_t l3_id, uint16_t port, uint16_t pid, port_recv_handler_t handler, const SocketExtraOptions* extra); -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, const SocketExtraOptions* extra); +void tcp_flow_apply_socket_options(tcp_data *flow_ctx, const SocketExtraOptions* extra); tcp_result_t tcp_flow_send(tcp_data *flow_ctx); tcp_result_t tcp_flow_flush(tcp_data *flow_ctx); diff --git a/kernel/networking/transport_layer/tcp/tcp_core.c b/kernel/networking/transport_layer/tcp/tcp_core.c index 1a076885..c9d6c41f 100644 --- a/kernel/networking/transport_layer/tcp/tcp_core.c +++ b/kernel/networking/transport_layer/tcp/tcp_core.c @@ -1,5 +1,4 @@ #include "tcp_internal.h" -#include "networking/port_manager.h" #include "networking/interface_manager.h" #include "networking/internet_layer/ipv4.h" #include "networking/internet_layer/ipv6.h" @@ -75,6 +74,26 @@ tcp_flow_t *tcp_flow_from_ctx(tcp_data *flow_ctx) { return flow; } +void tcp_flow_apply_socket_options(tcp_data *flow_ctx, const SocketExtraOptions* extra) { + tcp_flow_t *flow = tcp_flow_from_ctx(flow_ctx); + if (!flow) return; + + uint32_t flags = extra ? extra->flags : 0; + flow->timer.keepalive_on = (flags & SOCK_OPT_KEEPALIVE) ? 1 : 0; + flow->timer.keepalive_ms = flow->timer.keepalive_on ?(extra->keepalive_ms ? extra->keepalive_ms : SOCKET_DEFAULT_KEEPALIVE_MS) : 0; + flow->timer.keepalive_idle_ms = 0; + + flow->tx.nodelay = (flags & SOCK_OPT_TCP_NO_DELAY) ? 1 : 0; + if (flow->tx.nodelay && flow->tx.nagle_len) tcp_flush_nagle(flow, 1); + + uint32_t queued_limit = (flags & SOCK_OPT_SEND_BUF_SIZE) && extra->send_buf_size ? extra->send_buf_size : TCP_TX_MAX_BYTES_PER_FLOW; + if (queued_limit > TCP_TX_MAX_BYTES_PER_FLOW) queued_limit = TCP_TX_MAX_BYTES_PER_FLOW; + flow->tx.queued_limit = queued_limit; + + flow->ip.ttl = (flags & SOCK_OPT_TTL) ? extra->ttl : 0; + flow->ip.dontfrag = (flags & SOCK_OPT_DONTFRAG) ? 1 : 0; +} + void tcp_release_io_buffers(tcp_flow_t *f) { if (!f) return; @@ -249,11 +268,11 @@ bool tcp_send_segment(ip_version_t ver, const void *src_ip_addr, const void *dst h.checksum = tcp_checksum_ipv4(segment, tcp_len, s, d); memcpy(segment, &h, sizeof(h)); - return ipv4_send_packet(d, 6, pkt, (const ipv4_tx_opts_t *)txp, ttl, dontfrag); + return ipv4_send_packet(d, PROTO_TCP, pkt, (const ipv4_tx_opts_t *)txp, ttl, dontfrag); } else if (ver == IP_VER6){ h.checksum = tcp_checksum_ipv6(segment, tcp_len, (const uint8_t *)src_ip_addr, (const uint8_t *)dst_ip_addr); memcpy(segment, &h, sizeof(h)); - return ipv6_send_packet((const uint8_t *)dst_ip_addr, 6, pkt, (const ipv6_tx_opts_t *)txp, ttl, dontfrag); + return ipv6_send_packet((const uint8_t *)dst_ip_addr, PROTO_TCP, pkt, (const ipv6_tx_opts_t *)txp, ttl, dontfrag); } netpkt_unref(pkt); @@ -326,109 +345,6 @@ void tcp_rtt_update(tcp_flow_t *flow, uint32_t sample_ms){ flow->tx.rto = rto; } -bool tcp_bind_l3(uint8_t l3_id, uint16_t port, uint16_t pid, port_recv_handler_t handler, const SocketExtraOptions* extra){ - ip_version_t ver = l3_is_v6_from_id(l3_id) ? IP_VER6 : IP_VER4; - - port_manager_t *pm = (ver == IP_VER6) ? ifmgr_pm_v6(l3_id) : ifmgr_pm_v4(l3_id); - - if (!pm) return false; - if (!port_bind_manual(pm, PROTO_TCP, port, pid, handler)) return false; - - int listen_idx = find_flow(port, ver, NULL, NULL, 0); - if (listen_idx >= 0) return true; - - tcp_flow_t *f = tcp_alloc_flow(); - if (!f) { - (void)port_unbind(pm, PROTO_TCP, port, pid); - return false; - } - if (f){ - f->base.local_port = port; - f->base.l3_id = l3_id; - - f->base.local.ver = l3_is_v6_from_id(l3_id) ? IP_VER6 : IP_VER4; - memset(f->base.local.ip, 0, sizeof(f->base.local.ip)); - f->base.local.port = port; - - f->base.remote.ver = 0; - memset(f->base.remote.ip, 0, sizeof(f->base.remote.ip)); - f->base.remote.port = 0; - - f->base.state = TCP_LISTEN; - - f->base.ctx.sequence = 0; - f->base.ctx.ack = 0; - f->base.ctx.flags = 0; - - f->rx.rcv_wnd_max = tcp_clamp_rcvbuf(TCP_DEFAULT_RCV_BUF); - if (extra && (extra->flags & SOCK_OPT_BUF_SIZE) && extra->buf_size) f->rx.rcv_wnd_max = tcp_clamp_rcvbuf(extra->buf_size); - f->rx.rcv_base = 0; - f->rx.rcv_data_nxt = 0; - f->rx.rcv_ooo_used = 0; - f->rx.sack_recent_left = 0; - f->rx.sack_recent_right = 0; - f->rx.rcv_adv_edge = 0; - - f->ip.ttl = extra && (extra->flags & SOCK_OPT_TTL) ? extra->ttl : 0; - f->ip.dontfrag = extra && (extra->flags & SOCK_OPT_DONTFRAG) ? 1 : 0; - f->timer.keepalive_on = extra && (extra->flags & SOCK_OPT_KEEPALIVE) ? 1 : 0; - f->timer.keepalive_ms = extra && (extra->flags & SOCK_OPT_KEEPALIVE) ? extra->keepalive_ms : 0; - f->timer.keepalive_idle_ms = 0; - - f->tx.mss = TCP_DEFAULT_MSS; - if (f->rx.rcv_wnd_max > 65535u) { - f->tx.ws_send = 8; - f->tx.ws_recv = 0; - f->tx.ws_ok = 1; - } else { - f->tx.ws_send = 0; - f->tx.ws_recv = 0; - f->tx.ws_ok = 0; - } - f->tx.sack_ok = 1; - - - f->base.ctx.options.ptr = 0; - f->base.ctx.options.size = 0; - f->base.ctx.payload.ptr = 0; - f->base.ctx.payload.size = 0; - - f->base.ctx.expected_ack = 0; - f->base.ctx.ack_received = 0; - - f->timer.time_wait_ms = 0; - f->timer.fin_wait2_ms = 0; - } - - return true; -} - -int tcp_alloc_ephemeral_l3(uint8_t l3_id, uint16_t pid, port_recv_handler_t handler){ - - port_manager_t *pm = l3_is_v6_from_id(l3_id) ? ifmgr_pm_v6(l3_id) : ifmgr_pm_v4(l3_id); - if (!pm) return -1; - return port_alloc_ephemeral(pm, PROTO_TCP, pid, handler); -} - -bool tcp_unbind_l3(uint8_t l3_id, uint16_t port, uint16_t pid){ - ip_version_t ver = l3_is_v6_from_id(l3_id) ? IP_VER6 : IP_VER4; - - port_manager_t *pm = (ver == IP_VER6) ? ifmgr_pm_v6(l3_id) : ifmgr_pm_v4(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++){ - tcp_flow_t *f = tcp_flows[i]; - if (!f) continue; - if (f->base.state==TCP_LISTEN && f->base.local_port==port && f->base.local.ver==ver) tcp_free_flow(i); - } - } - - return res; -} - bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, tcp_data *flow_ctx, uint16_t pid, const SocketExtraOptions* extra){ (void)pid; if (!dst || !flow_ctx) return false; @@ -486,9 +402,11 @@ bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, flow->rx.rcv_ooo_used = 0; flow->rx.sack_recent_left = 0; flow->rx.sack_recent_right = 0; - flow->rx.rcv_wnd_max = tcp_clamp_rcvbuf(TCP_DEFAULT_RCV_BUF); - if (extra && (extra->flags & SOCK_OPT_BUF_SIZE) && extra->buf_size) flow->rx.rcv_wnd_max = tcp_clamp_rcvbuf(extra->buf_size); + uint32_t rcvbuf = TCP_DEFAULT_RCV_BUF; + if (extra && (extra->flags & SOCK_OPT_BUF_SIZE) && extra->buf_size) rcvbuf = extra->buf_size; + flow->rx.rcv_wnd_max = tcp_clamp_rcvbuf(rcvbuf); flow->rx.rcv_adv_edge = 0; + tcp_flow_apply_socket_options(&flow->base.ctx, extra); flow->tx.mss = tcp_calc_mss_for_l3(l3_id, dst->ver, dst->ip); @@ -512,12 +430,6 @@ bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, } (void)tcp_calc_adv_wnd_field(flow, 1); - flow->ip.ttl = extra && (extra->flags & SOCK_OPT_TTL) ? extra->ttl : 0; - flow->ip.dontfrag = extra && (extra->flags & SOCK_OPT_DONTFRAG) ? 1 : 0; - flow->timer.keepalive_on = extra && (extra->flags & SOCK_OPT_KEEPALIVE) ? 1 : 0; - flow->timer.keepalive_ms = extra && (extra->flags & SOCK_OPT_KEEPALIVE) ? extra->keepalive_ms : 0; - flow->timer.keepalive_idle_ms = 0; - flow->base.ctx.options.ptr = 0; flow->base.ctx.options.size = 0; flow->base.ctx.payload.ptr = 0; diff --git a/kernel/networking/transport_layer/tcp/tcp_internal.h b/kernel/networking/transport_layer/tcp/tcp_internal.h index ad62cdf6..018a1f14 100644 --- a/kernel/networking/transport_layer/tcp/tcp_internal.h +++ b/kernel/networking/transport_layer/tcp/tcp_internal.h @@ -2,7 +2,6 @@ #include "../tcp.h" #include "types.h" -#include "networking/port_manager.h" #include "networking/internet_layer/ipv4.h" #include "networking/internet_layer/ipv6.h" #include "networking/internet_layer/ipv4_utils.h" @@ -93,6 +92,7 @@ typedef struct { uint8_t nagle_flushing; uint8_t nagle_appending; + uint8_t nodelay; uintptr_t nagle_buf; uint32_t nagle_len; @@ -101,6 +101,7 @@ typedef struct { tcp_tx_seg_t txq[TCP_MAX_TX_SEGS]; uint32_t queued_bytes; + uint32_t queued_limit; uint8_t fin_tx_pending; } tcp_flow_tx_t; @@ -172,11 +173,11 @@ int tcp_try_send_pending_fin(tcp_flow_t *flow); uint64_t tcp_flush_nagle(tcp_flow_t *flow, uint8_t force); static inline uint16_t tcp_checksum_ipv4(const void *segment, uint16_t seg_len, uint32_t src_ip, uint32_t dst_ip) { - uint16_t csum = checksum16_pipv4(src_ip, dst_ip, 6, (const uint8_t *)segment, seg_len); + uint16_t csum = checksum16_pipv4(src_ip, dst_ip, PROTO_TCP, (const uint8_t *)segment, seg_len); return bswap16(csum); } static inline uint16_t tcp_checksum_ipv6(const void *segment, uint16_t seg_len, const uint8_t src_ip[16], const uint8_t dst_ip[16]) { - uint16_t csum = checksum16_pipv6(src_ip, dst_ip, 6, (const uint8_t *)segment, seg_len); + uint16_t csum = checksum16_pipv6(src_ip, dst_ip, PROTO_TCP, (const uint8_t *)segment, seg_len); return bswap16(csum); } diff --git a/kernel/networking/transport_layer/tcp/tcp_limits.c b/kernel/networking/transport_layer/tcp/tcp_limits.c index 4e1d1632..83309503 100644 --- a/kernel/networking/transport_layer/tcp/tcp_limits.c +++ b/kernel/networking/transport_layer/tcp/tcp_limits.c @@ -96,9 +96,10 @@ tcp_admit_result_t tcp_admit_syn(uint8_t l3_id, uint16_t port, ip_version_t ver, tcp_admit_result_t tcp_admit_tx(tcp_flow_t *flow, uint32_t bytes, uint32_t free_slots) { if (!flow) return TCP_ADMIT_TX_FLOW_BYTES; if (free_slots <= TCP_TX_CONTROL_RESERVE_SEGS) return TCP_ADMIT_TX_FLOW_SEGS; - if (flow->tx.queued_bytes >= TCP_TX_MAX_BYTES_PER_FLOW) return TCP_ADMIT_TX_FLOW_BYTES; + uint32_t limit = flow->tx.queued_limit ? flow->tx.queued_limit : TCP_TX_MAX_BYTES_PER_FLOW; + if (flow->tx.queued_bytes >= limit) return TCP_ADMIT_TX_FLOW_BYTES; if (tcp_tx_global_bytes >= TCP_TX_MAX_BYTES_GLOBAL) return TCP_ADMIT_TX_GLOBAL_BYTES; - if (bytes && flow->tx.queued_bytes + bytes > TCP_TX_MAX_BYTES_PER_FLOW) return TCP_ADMIT_TX_FLOW_BYTES; + if (bytes && flow->tx.queued_bytes + bytes > limit) return TCP_ADMIT_TX_FLOW_BYTES; if (bytes && tcp_tx_global_bytes + bytes > TCP_TX_MAX_BYTES_GLOBAL) return TCP_ADMIT_TX_GLOBAL_BYTES; return TCP_ADMIT_OK; } diff --git a/kernel/networking/transport_layer/tcp/tcp_rx.c b/kernel/networking/transport_layer/tcp/tcp_rx.c index ae49b0ad..5c646cc7 100644 --- a/kernel/networking/transport_layer/tcp/tcp_rx.c +++ b/kernel/networking/transport_layer/tcp/tcp_rx.c @@ -1,5 +1,6 @@ #include "tcp_internal.h" -#include "networking/port_manager.h" +#include "networking/transport_layer/socket_bind.h" +#include "networking/transport_layer/csocket_tcp.h" #include "networking/internet_layer/ipv4.h" #include "networking/internet_layer/ipv6.h" #include "networking/internet_layer/ipv4_utils.h" @@ -364,16 +365,13 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a if (flow) flow->timer.keepalive_idle_ms = 0; if (flow) flow->base.l3_id = l3_id; - port_manager_t *pm = NULL; uint8_t ifx = 0; - if (ipver == IP_VER4) { l3_ipv4_interface_t *v4 = l3_ipv4_find_by_id(l3_id); if (!v4 || !v4->l2) { netpkt_unref(pkt); return; } - pm = ifmgr_pm_v4(l3_id); ifx = v4->l2->ifindex; } else { l3_ipv6_interface_t *v6 = l3_ipv6_find_by_id(l3_id); @@ -381,20 +379,14 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a netpkt_unref(pkt); return; } - pm = ifmgr_pm_v6(l3_id); ifx = v6->l2->ifindex; } - if (!pm) { - netpkt_unref(pkt); - return; - } - - port_recv_handler_t port_handler = port_get_handler(pm, PROTO_TCP, dst_port); if (flow && flow->base.state == TCP_TIME_WAIT && (flags & (1 << SYN_F)) && !(flags& ((1 << ACK_F) | (1 << RST_F) | (1 << FIN_F))) && data_len == 0) { - int listen_idx = find_flow(dst_port, ipver, dst_ip_addr, NULL, 0); - if (listen_idx < 0)listen_idx = find_flow(dst_port, ipver, NULL, NULL, 0); - if (listen_idx >= 0) { + ksocket_t* listener = NULL; + socket_bind_collect(PROTO_TCP, ipver, l3_id, ifx, dst_ip_addr, dst_port, &listener, 1); + if (listener) { + socket_core_put(listener); tcp_free_flow(idx); flow = NULL; idx = -1; @@ -414,10 +406,10 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a } if (!flow){ - int listen_idx = find_flow(dst_port, ipver, dst_ip_addr, NULL, 0); - if (listen_idx < 0)listen_idx = find_flow(dst_port, ipver, NULL, NULL, 0); + ksocket_t* listener = NULL; + socket_bind_collect(PROTO_TCP, ipver, l3_id, ifx, dst_ip_addr, dst_port, &listener, 1); - if ((flags & (1u << SYN_F)) && !(flags & ((1u << ACK_F) | (1u << RST_F) | (1u << FIN_F))) && data_len == 0 && listen_idx >= 0){ + if ((flags & (1u << SYN_F)) && !(flags & ((1u << ACK_F) | (1u << RST_F) | (1u << FIN_F))) && data_len == 0 && listener){ rng_t rng; uint64_t virt_timer; asm volatile ("mrs %0, cntvct_el0" : "=r"(virt_timer)); @@ -431,13 +423,15 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a else if (syn_admit == TCP_ADMIT_ORPHAN_LIMIT) tcp_stats.orphan_drop_global++; else if (syn_admit == TCP_ADMIT_RESOURCE_BUDGET) tcp_stats.resource_budget_drop++; else if (syn_admit == TCP_ADMIT_FLOW_TABLE_FULL) tcp_stats.flow_table_full++; + socket_core_put(listener); netpkt_unref(pkt); return; } - tcp_flow_t *lf = tcp_flows[listen_idx]; + const SocketExtraOptions* listener_extra = socket_tcp_extra_options(socket_core_impl(listener)); tcp_flow_t *nf = tcp_alloc_flow(); if (!nf) { + socket_core_put(listener); netpkt_unref(pkt); return; } @@ -462,9 +456,9 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a tcp_parsed_opts_t pop = parsed_opts; - flow->tx.ws_send = lf->tx.ws_send; + flow->tx.ws_send = 0; flow->tx.ws_recv = 0; - flow->tx.ws_ok = (lf->tx.ws_ok && pop.has_wscale) ? 1 : 0; + flow->tx.ws_ok = pop.has_wscale ? 1 : 0; if (flow->tx.ws_ok) { flow->tx.ws_recv = pop.wscale; if (flow->tx.ws_recv > 14) flow->tx.ws_recv = 14; @@ -474,7 +468,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a flow->tx.ws_recv = 0; } - flow->tx.sack_ok = (lf->tx.sack_ok && pop.sack_permitted) ? 1 : 0; + flow->tx.sack_ok = pop.sack_permitted ? 1 : 0; if (pop.has_mss && pop.mss){ uint32_t m = pop.mss; @@ -485,7 +479,8 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a flow->tx.mss = m; } else flow->tx.mss = tcp_calc_mss_for_l3(l3_id, ipver, src_ip_addr); flow->base.ctx.flags = 0; - flow->base.ctx.options = lf->base.ctx.options; + flow->base.ctx.options.ptr = 0; + flow->base.ctx.options.size = 0; flow->base.ctx.payload.ptr = 0; flow->base.ctx.payload.size = 0; @@ -511,7 +506,14 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a flow->timer.delayed_ack_pending = 0; flow->timer.delayed_ack_timer_ms = 0; - flow->rx.rcv_wnd_max = lf->rx.rcv_wnd_max; + uint32_t rcvbuf = TCP_DEFAULT_RCV_BUF; + if (listener_extra && (listener_extra->flags & SOCK_OPT_BUF_SIZE) && listener_extra->buf_size) rcvbuf = listener_extra->buf_size; + flow->rx.rcv_wnd_max = tcp_clamp_rcvbuf(rcvbuf); + tcp_flow_apply_socket_options(&flow->base.ctx, listener_extra); + if (flow->rx.rcv_wnd_max > 65535u && pop.has_wscale) { + flow->tx.ws_send = 8; + flow->tx.ws_ok = 1; + } flow->rx.rcv_base = flow->rx.rcv_nxt; flow->rx.rcv_data_nxt = flow->rx.rcv_nxt; flow->rx.rcv_ooo_used = 0; @@ -521,12 +523,6 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a flow->rx.rcv_adv_edge = flow->rx.rcv_nxt + flow->rx.rcv_wnd_max; tcp_calc_adv_wnd_field(flow, flow->tx.ws_ok ? 1 : 0); - flow->ip.ttl = lf->ip.ttl; - flow->ip.dontfrag = lf->ip.dontfrag; - flow->timer.keepalive_on = lf->timer.keepalive_on; - flow->timer.keepalive_ms = lf->timer.keepalive_ms; - flow->timer.keepalive_idle_ms = 0; - flow->tx.cwnd = flow->tx.mss * TCP_INIT_CWND_SEGS; if (flow->tx.cwnd < flow->tx.mss) flow->tx.cwnd = flow->tx.mss; flow->tx.ssthresh = TCP_RECV_WINDOW; @@ -541,6 +537,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a tcp_tx_seg_t *seg = tcp_alloc_tx_seg(flow); if (!seg) { tcp_free_flow(idx); + socket_core_put(listener); netpkt_unref(pkt); return; } @@ -557,6 +554,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a seg->opts_len = tcp_build_syn_options(seg->opts, (uint16_t)flow->tx.mss, flow->tx.ws_ok ? flow->tx.ws_send : 0xff, flow->tx.sack_ok); if (!tcp_send_from_seg(flow, seg)) { tcp_free_flow(idx); + socket_core_put(listener); netpkt_unref(pkt); return; } @@ -564,10 +562,12 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a flow->tx.snd_nxt = iss + 1; tcp_daemon_kick(); + socket_core_put(listener); netpkt_unref(pkt); return; } + if (listener) socket_core_put(listener); if (!(flags & (1u << RST_F))){ if (flags & (1u << ACK_F)){ tcp_send_reset(ipver, dst_ip_addr, src_ip_addr, dst_port, src_port, ack, 0, false); @@ -786,7 +786,12 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a } uint32_t queued = 0; - if (port_handler) queued = port_handler(ifx, ipver, src_ip_addr, dst_ip_addr, 0, src_port, dst_port); + ksocket_t* listener = NULL; + socket_bind_collect(PROTO_TCP, ipver, l3_id, ifx, dst_ip_addr, dst_port, &listener, 1); + if (listener) { + queued = tcp_accept_enqueue(listener, ifx, ipver, src_ip_addr, dst_ip_addr, src_port, dst_port); + socket_core_put(listener); + } if (!queued) { tcp_stats.acceptq_drop_full++; tcp_hdr_t rst_hdr; diff --git a/kernel/networking/transport_layer/tcp/tcp_tx.c b/kernel/networking/transport_layer/tcp/tcp_tx.c index 15e08fee..b51997dd 100644 --- a/kernel/networking/transport_layer/tcp/tcp_tx.c +++ b/kernel/networking/transport_layer/tcp/tcp_tx.c @@ -94,7 +94,8 @@ static uint64_t tcp_emit_data(tcp_flow_t *flow, const uint8_t *payload, uint64_t uint64_t seg_len = remaining > can_send ? can_send : remaining; if (flow->tx.mss && seg_len > flow->tx.mss) seg_len = flow->tx.mss; - uint32_t flow_room = TCP_TX_MAX_BYTES_PER_FLOW - flow->tx.queued_bytes; + uint32_t tx_limit = flow->tx.queued_limit ? flow->tx.queued_limit : TCP_TX_MAX_BYTES_PER_FLOW; + uint32_t flow_room = tx_limit > flow->tx.queued_bytes ? tx_limit - flow->tx.queued_bytes : 0; uint32_t global_room = TCP_TX_MAX_BYTES_GLOBAL - tcp_tx_global_bytes; if (seg_len > flow_room) seg_len = flow_room; if (seg_len > global_room) seg_len = global_room; @@ -441,7 +442,7 @@ tcp_result_t tcp_flow_send(tcp_data *flow_ctx){ uint64_t accepted = 0; - if (payload_len && flow->tx.nagle_len) { + if (!flow->tx.nodelay && payload_len && flow->tx.nagle_len) { uint64_t n = tcp_nagle_append(flow, payload_ptr, payload_len); accepted += n; payload_ptr += n; @@ -449,7 +450,7 @@ tcp_result_t tcp_flow_send(tcp_data *flow_ctx){ if (flow->tx.nagle_len >= tcp_nagle_threshold(flow)) tcp_flush_nagle(flow, 1); } - if (payload_len && payload_len < tcp_nagle_threshold(flow)) { + if (!flow->tx.nodelay && payload_len && payload_len < tcp_nagle_threshold(flow)) { uint64_t n = tcp_nagle_append(flow, payload_ptr, payload_len); accepted += n; payload_ptr += n; diff --git a/kernel/networking/transport_layer/tcp/tcp_utils.h b/kernel/networking/transport_layer/tcp/tcp_utils.h index 7a07d1ac..b5c41f83 100644 --- a/kernel/networking/transport_layer/tcp/tcp_utils.h +++ b/kernel/networking/transport_layer/tcp/tcp_utils.h @@ -5,7 +5,6 @@ #include "networking/internet_layer/ipv6.h" #include "networking/internet_layer/ipv4_utils.h" #include "networking/internet_layer/ipv6_utils.h" -#include "networking/port_manager.h" #ifdef __cplusplus extern "C" { diff --git a/kernel/networking/transport_layer/udp.c b/kernel/networking/transport_layer/udp.c index 71ab7fc6..dfb3e4f5 100644 --- a/kernel/networking/transport_layer/udp.c +++ b/kernel/networking/transport_layer/udp.c @@ -2,7 +2,8 @@ #include "net/checksums.h" #include "networking/internet_layer/ipv4.h" #include "networking/internet_layer/ipv6.h" -#include "networking/port_manager.h" +#include "networking/transport_layer/socket_bind.h" +#include "networking/transport_layer/csocket_udp.h" #include "std/memory.h" #include "types.h" #include "syscalls/syscalls.h" @@ -24,9 +25,9 @@ size_t create_udp_segment(uintptr_t buf, const net_l4_endpoint *src, const net_l uint32_t d = 0; memcpy(&s, src->ip, 4); memcpy(&d, dst->ip, 4); - udp.checksum = bswap16(checksum16_pipv4(s, d, 0x11, (const uint8_t *)buf, full_len)); + udp.checksum = bswap16(checksum16_pipv4(s, d, PROTO_UDP, (const uint8_t *)buf, full_len)); } else if (src->ver == IP_VER6) { - udp.checksum = bswap16(checksum16_pipv6(src->ip, dst->ip, 17, (const uint8_t *)buf, full_len)); + udp.checksum = bswap16(checksum16_pipv6(src->ip, dst->ip, PROTO_UDP, (const uint8_t *)buf, full_len)); } memcpy((void*)buf, &udp, sizeof(udp)); @@ -50,10 +51,10 @@ void udp_send_segment(const net_l4_endpoint *src, const net_l4_endpoint *dst, si uint32_t dst_ip = 0; memcpy(&dst_ip, dst->ip, 4); (void)netpkt_trim(pkt, (uint32_t)written); - ipv4_send_packet(dst_ip, 0x11, pkt, (const ipv4_tx_opts_t*)tx_opts, ttl, dontfrag); + ipv4_send_packet(dst_ip, PROTO_UDP, pkt, (const ipv4_tx_opts_t*)tx_opts, ttl, dontfrag); } else if (src->ver == IP_VER6) { (void)netpkt_trim(pkt, (uint32_t)written); - ipv6_send_packet(dst->ip, 0x11, pkt, (const ipv6_tx_opts_t*)tx_opts, ttl, dontfrag); + ipv6_send_packet(dst->ip, PROTO_UDP, pkt, (const ipv6_tx_opts_t*)tx_opts, ttl, dontfrag); } else { netpkt_unref(pkt); } @@ -88,12 +89,12 @@ void udp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a uint32_t dst_ip = 0; memcpy(&src_ip, src_ip_addr, sizeof(src_ip)); memcpy(&dst_ip, dst_ip_addr, sizeof(dst_ip)); - if (checksum16_pipv4(src_ip, dst_ip, 0x11, (const uint8_t*)netpkt_data(pkt), (uint16_t)(payload_len + sizeof(hdr))) != 0) { + if (checksum16_pipv4(src_ip, dst_ip, PROTO_UDP, (const uint8_t*)netpkt_data(pkt), (uint16_t)(payload_len + sizeof(hdr))) != 0) { netpkt_unref(pkt); return; } } else if (ipver == IP_VER6) { - if (checksum16_pipv6((const uint8_t*)src_ip_addr, (const uint8_t*)dst_ip_addr, 0x11, (const uint8_t*)netpkt_data(pkt), (uint32_t)(payload_len + sizeof(hdr))) != 0) { + if (checksum16_pipv6((const uint8_t*)src_ip_addr, (const uint8_t*)dst_ip_addr, PROTO_UDP, (const uint8_t*)netpkt_data(pkt), (uint32_t)(payload_len + sizeof(hdr))) != 0) { netpkt_unref(pkt); return; } @@ -105,17 +106,11 @@ void udp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a l3_ipv4_interface_t *v4 = NULL; l3_ipv6_interface_t *v6 = NULL; - port_manager_t *pm = NULL; - - if (ipver == IP_VER4) { - v4 = l3_ipv4_find_by_id(l3_id); - if (v4) pm = ifmgr_pm_v4(l3_id); - } else if (ipver == IP_VER6) { - v6 = l3_ipv6_find_by_id(l3_id); - if (v6) pm = ifmgr_pm_v6(l3_id); - } - if (!pm) { + if (ipver == IP_VER4) v4 = l3_ipv4_find_by_id(l3_id); + else if (ipver == IP_VER6) v6 = l3_ipv6_find_by_id(l3_id); + + if (!v4 && !v6) { netpkt_unref(pkt); return; } @@ -130,32 +125,14 @@ void udp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a if (v4 && v4->l2) ifx = v4->l2->ifindex; else if (v6 && v6->l2) ifx = v6->l2->ifindex; - port_recv_handler_t handler = port_get_handler(pm, PROTO_UDP, dst_port); - if (handler) handler(ifx, ipver, src_ip_addr, dst_ip_addr, plpkt, src_port, dst_port); + ksocket_t* sockets[SOCKET_BIND_COLLECT_MAX]; + uint32_t count = socket_bind_collect(PROTO_UDP, ipver, l3_id, ifx, dst_ip_addr, dst_port, sockets, SOCKET_BIND_COLLECT_MAX); + for (uint32_t i = 0; i < count; ++i) { + socket_udp_input(sockets[i], ifx, l3_id, ipver, src_ip_addr, dst_ip_addr, plpkt, src_port, dst_port); + socket_core_put(sockets[i]); + } + netpkt_unref(plpkt); netpkt_unref(pkt); } -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); -} diff --git a/kernel/networking/transport_layer/udp.h b/kernel/networking/transport_layer/udp.h index 58bbaae3..80552b88 100644 --- a/kernel/networking/transport_layer/udp.h +++ b/kernel/networking/transport_layer/udp.h @@ -1,7 +1,6 @@ #pragma once #include "types.h" #include "net/network_types.h" -#include "networking/port_manager.h" #include "networking/internet_layer/ipv4.h" #include "networking/netpkt.h" @@ -29,9 +28,6 @@ void udp_input(ip_version_t ipver, uint8_t l3_id, netpkt_t* pkt); -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 } diff --git a/kernel/process/kernel_syscall_impl.c b/kernel/process/kernel_syscall_impl.c index 9613d539..8e2138c6 100644 --- a/kernel/process/kernel_syscall_impl.c +++ b/kernel/process/kernel_syscall_impl.c @@ -106,36 +106,47 @@ uint64_t get_time(){ } bool socket_create(Socket_Role role, protocol_t protocol, const SocketExtraOptions* extra, SocketHandle *out_handle){ - return create_socket(role, protocol, extra, get_current_proc_pid(), out_handle); + return create_socket(role, protocol, extra, out_handle); } int32_t socket_bind(SocketHandle *handle, ip_version_t ip_version, uint16_t port){ - return bind_socket(handle, port, ip_version, get_current_proc_pid()); + return bind_socket(handle, port, ip_version); } -int32_t socket_connect(SocketHandle *handle, SockDstKind dst_kind, void* dst, uint16_t port){ - return connect_socket(handle, dst_kind, dst, port, get_current_proc_pid()); +int32_t socket_connect(SocketHandle *handle, const net_l4_endpoint* dst){ + return connect_socket(handle, dst); } int32_t socket_listen(SocketHandle *handle){ - return listen_on(handle, 0, get_current_proc_pid()); + return listen_on(handle, 0); } -bool socket_accept(SocketHandle *spec){ - accept_on_socket(spec, get_current_proc_pid()); - return 1; +bool socket_accept(SocketHandle* handle, SocketHandle* out_child){ + return accept_on_socket(handle, out_child); } -int64_t socket_send(SocketHandle *handle, SockDstKind dst_kind, const void* dst, uint16_t port, void *packet, size_t size){ - return send_on_socket(handle, dst_kind, dst, port, packet, size, get_current_proc_pid()); +int64_t socket_send(SocketHandle *handle, void *packet, size_t size){ + return send_on_socket(handle, packet, size); +} + +int64_t socket_send_to(SocketHandle *handle, const net_l4_endpoint* dst, void *packet, size_t size){ + return send_to_socket(handle, dst, packet, size); } int64_t socket_receive(SocketHandle *handle, void *packet, size_t size, net_l4_endpoint* out_src){ - return receive_from_socket(handle, packet, size, out_src, get_current_proc_pid()); + return receive_from_socket(handle, packet, size, out_src); } int32_t socket_close(SocketHandle *handle){ - return close_socket(handle, get_current_proc_pid()); + return close_socket(handle); +} + +int32_t socket_set_option(SocketHandle *handle, int32_t opt, const void* value, uint32_t len){ + return set_socket_option(handle, opt, value, len); +} + +int32_t socket_get_option(SocketHandle *handle, int32_t opt, void* value, uint32_t* len){ + return get_socket_option(handle, opt, value, len); } FS_RESULT openf(const char* path, file* descriptor){ diff --git a/kernel/process/syscall.c b/kernel/process/syscall.c index c9e4d0b0..7699f0de 100644 --- a/kernel/process/syscall.c +++ b/kernel/process/syscall.c @@ -14,7 +14,6 @@ #include "std/string.h" #include "exceptions/timer.h" #include "networking/network.h" -#include "networking/port_manager.h" #include "filesystem/filesystem.h" #include "syscalls/syscall_codes.h" #include "graph/tres.h" @@ -261,94 +260,84 @@ u64 syscall_socket_create(process_t *ctx){ SYSCALL_ARG(const SocketExtraOptions, extra, PROC_X2, false); SYSCALL_ARG(SocketHandle, out, PROC_X3, true); - return create_socket(role, protocol, extra, ctx->id, out); + return create_socket(role, protocol, extra, out); } u64 syscall_socket_bind(process_t *ctx){ - SYSCALL_ARG(SocketHandle,handle,PROC_X0, true); + SYSCALL_ARG(SocketHandle,handle,PROC_X0, false); ip_version_t ip_version = (ip_version_t)ctx->PROC_X1; uint16_t port = (uint16_t)ctx->PROC_X2; - return bind_socket(handle, port, ip_version, ctx->id); + return bind_socket(handle, port, ip_version); } u64 syscall_socket_connect(process_t *ctx){ - uint8_t dst_kind = (uint8_t)ctx->PROC_X1; - uint16_t port = (uint16_t)ctx->PROC_X3; - SYSCALL_ARG(SocketHandle,handle,PROC_X0,true); - - const void *dst = 0; - - if (dst_kind == DST_ENDPOINT) { - SYSCALL_ARG(net_l4_endpoint, ep, PROC_X2, true); - dst = ep; - } else if (dst_kind == DST_DOMAIN) { - SYSCALL_STR(domain, PROC_X2, true); - dst = domain; - } else { - return 0; - } - - return connect_socket(handle, dst_kind, dst, port, ctx->id); + SYSCALL_ARG(net_l4_endpoint, ep, PROC_X1, false); + return connect_socket(handle, ep); } u64 syscall_socket_listen(process_t *ctx){ - SYSCALL_ARG(SocketHandle,handle, PROC_X0, true); + SYSCALL_ARG(SocketHandle,handle, PROC_X0, false); int32_t backlog = (int32_t)ctx->PROC_X1; - return listen_on(handle, backlog, ctx->id); + return listen_on(handle, backlog); } u64 syscall_socket_accept(process_t *ctx){ - SYSCALL_ARG(SocketHandle,handle, PROC_X0, true); - accept_on_socket(handle, ctx->id); - return 1; + SYSCALL_ARG(SocketHandle,handle, PROC_X0, false); + SYSCALL_ARG(SocketHandle,out_child, PROC_X1, true); + return accept_on_socket(handle, out_child); } u64 syscall_socket_send(process_t *ctx){ - uint8_t dst_kind = (uint8_t)ctx->PROC_X1; - uint16_t port = (uint16_t)ctx->PROC_X3; - size_t size = (size_t)ctx->regs[5]; - - SYSCALL_ARG(SocketHandle,handle, PROC_X0, true); - - const void *dst = 0; + size_t size = (size_t)ctx->PROC_X2; + if (!size) return 0; - if (dst_kind == DST_ENDPOINT){ - SYSCALL_ARG(net_l4_endpoint, ep, PROC_X2, true); - dst = ep; - } else if (dst_kind == DST_DOMAIN){ - SYSCALL_STR(domain, PROC_X2, true); - dst = domain; - } else { - return 0; - } + SYSCALL_ARG(SocketHandle,handle, PROC_X0, false); + SYSCALL_ARG_SIZE(void, buf, size, PROC_X1, false); + return send_on_socket(handle, buf, size); +} +u64 syscall_socket_send_to(process_t *ctx) { + size_t size = (size_t)ctx->PROC_X3; if (!size) return 0; - - uint64_t alloc_size = (size + 0xFFF) & ~0xFFFULL; + SYSCALL_ARG(SocketHandle,handle, PROC_X0, false); + SYSCALL_ARG(net_l4_endpoint, dst, PROC_X1, false); + SYSCALL_ARG_SIZE(void, buf, size, PROC_X2, false); - SYSCALL_ARG_SIZE(void, kbuf, alloc_size, PROC_X4, true); - if (!kbuf) return 0; - - return send_on_socket(handle, dst_kind, dst, port, kbuf, size, ctx->id); + return send_to_socket(handle, dst, buf, size); } u64 syscall_socket_receive(process_t *ctx){ size_t size = (size_t)ctx->PROC_X2; if (!size) return 0; - uint64_t alloc_size = (size + 0xFFF) & ~0xFFFULL; - SYSCALL_ARG(SocketHandle, handle, PROC_X0, true); - SYSCALL_ARG_SIZE(void, buf, alloc_size, PROC_X1, true); + SYSCALL_ARG(SocketHandle, handle, PROC_X0, false); + SYSCALL_ARG_SIZE(void, buf, size, PROC_X1, true); SYSCALL_ARG(net_l4_endpoint, src, PROC_X3, true); - return receive_from_socket(handle, buf, size, src, ctx->id); + return receive_from_socket(handle, buf, size, src); } u64 syscall_socket_close(process_t *ctx){ SYSCALL_ARG(SocketHandle,handle, PROC_X0, true); - return close_socket(handle, ctx->id); + return close_socket(handle); +} + +u64 syscall_socket_setopt(process_t *ctx){ + SYSCALL_ARG(SocketHandle, handle, PROC_X0, false); + int32_t opt = (int32_t)ctx->PROC_X1; + uint32_t len = (uint32_t)ctx->PROC_X3; + SYSCALL_ARG_SIZE(void, value, len, PROC_X2, false); + return set_socket_option(handle, opt, value, len); +} + +u64 syscall_socket_getopt(process_t *ctx){ + SYSCALL_ARG(SocketHandle, handle, PROC_X0, false); + int32_t opt = (int32_t)ctx->PROC_X1; + SYSCALL_ARG(uint32_t, len, PROC_X3, true); + SYSCALL_ARG_SIZE(void, value, *len, PROC_X2, true); + return get_socket_option(handle, opt, value, len); } #define ISOLATEDFS @@ -544,9 +533,12 @@ syscall_entry syscalls[] = { [SOCKET_CONNECT_CODE] = syscall_socket_connect, [SOCKET_LISTEN_CODE] = syscall_socket_listen, [SOCKET_ACCEPT_CODE] = syscall_socket_accept, + [SOCKET_SENDTO_CODE] = syscall_socket_send_to, [SOCKET_SEND_CODE] = syscall_socket_send, [SOCKET_RECEIVE_CODE] = syscall_socket_receive, [SOCKET_CLOSE_CODE] = syscall_socket_close, + [SOCKET_SETOPT_CODE] = syscall_socket_setopt, + [SOCKET_GETOPT_CODE] = syscall_socket_getopt, [FILE_OPEN_CODE] = syscall_openf, [FILE_READ_CODE] = syscall_readf, [FILE_WRITE_CODE] = syscall_writef, diff --git a/kernel/tools/curl.c b/kernel/tools/curl.c index b2e68895..8389aa50 100644 --- a/kernel/tools/curl.c +++ b/kernel/tools/curl.c @@ -11,13 +11,12 @@ #define CURL_MAX_REDIRECTS 5 typedef struct { - const char *out_path; char *url; bool head_only; bool follow; } curl_opts_t; -static int curl_fetch(char *url, const char *out_path, bool head_only, bool follow) { +static int curl_fetch(char *url, bool head_only, bool follow) { string host = (string){0}; string path = (string){0}; uint16_t port = 0; @@ -75,7 +74,7 @@ static int curl_fetch(char *url, const char *out_path, bool head_only, bool foll opts.follow_redirects = follow ? 1 : 0; opts.max_redirects = CURL_MAX_REDIRECTS; - http_client_handle_t cli = http_client_create((uint16_t)get_current_proc_pid(), NULL, &opts); + http_client_handle_t cli = http_client_create(NULL, &opts); if (!cli) { string_free(host); string_free(path); @@ -83,7 +82,7 @@ static int curl_fetch(char *url, const char *out_path, bool head_only, bool foll return 4; } - int32_t rc = http_client_connect(cli, DST_DOMAIN, host.data, port); + int32_t rc = http_client_connect_domain(cli, host.data, port); if (rc < 0) { print("curl: connect failed for %s:%d", host.data, port); http_client_destroy(cli); @@ -113,29 +112,7 @@ static int curl_fetch(char *url, const char *out_path, bool head_only, bool foll string raw = http_response_builder(&head); print("%.*s", (int)raw.length, raw.data); string_free(raw); - } else if (resp.body.ptr && resp.body.size) { - int write_ok; - - if (!out_path) { - print("%.*s", (int)resp.body.size, (const char*)resp.body.ptr); - write_ok = 1; - } else write_ok = swritef(out_path, (const void*)resp.body.ptr, (uint32_t)resp.body.size, false) == resp.body.size; - - if (!write_ok) { - print("curl: write failed"); - - if (resp.reason.mem_length) string_free(resp.reason); - http_headers_common_free(&resp.headers_common); - http_headers_extra_free(resp.extra_headers, resp.extra_header_count); - if (resp.body.ptr && resp.body.size) release((void*)resp.body.ptr); - resp = (HTTPResponseMsg){0}; - - http_client_destroy(cli); - string_free(host); - string_free(path); - return 7; - } - } + } else if (resp.body.ptr && resp.body.size) print("%.*s", (int)resp.body.size, (const char*)resp.body.ptr); if (resp.reason.mem_length) string_free(resp.reason); http_headers_common_free(&resp.headers_common); @@ -155,10 +132,7 @@ static bool parse_args(int argc, char *argv[], curl_opts_t *o) { for (int i = 1; i < argc; i++) { if (strcmp(argv[i], "-I") == 0) o->head_only = true; else if (strcmp(argv[i], "-L") == 0) o->follow = true; - else if (strcmp(argv[i], "-o") == 0) { - if (++i >= argc) return false; - o->out_path = argv[i]; - } else if (!o->url) o->url = argv[i]; + else if (!o->url) o->url = argv[i]; else return false; } @@ -168,9 +142,9 @@ static bool parse_args(int argc, char *argv[], curl_opts_t *o) { int run_curl(int argc, char* argv[]) { curl_opts_t opts; if (!parse_args(argc, argv, &opts)) { - print("usage: curl [-L] [-I] [-o file] http://host/path"); + print("usage: curl [-L] [-I] http://host/path"); return 2; } - return curl_fetch(opts.url, opts.out_path, opts.head_only, opts.follow); + return curl_fetch(opts.url, opts.head_only, opts.follow); } diff --git a/shared b/shared index 989018da..8a270697 160000 --- a/shared +++ b/shared @@ -1 +1 @@ -Subproject commit 989018dad1ce844014ae7205e2f18339df17229a +Subproject commit 8a2706973ccd262b23078863196db1b678bc6f8a diff --git a/user/demo/default_process.c b/user/demo/default_process.c index 41e2b144..493d8155 100644 --- a/user/demo/default_process.c +++ b/user/demo/default_process.c @@ -76,7 +76,7 @@ int net_example() { char *str = "Hello node"; - print("Sent %i",socket_send(&spec, DST_ENDPOINT, &rc.ip, rc.port, str, strlen(str))); + print("Sent %i",socket_send_to(&spec, &rc, str, strlen(str))); socket_close(&spec); From 2e71ca2800b87bba6d5ac35547e42385a4322812 Mon Sep 17 00:00:00 2001 From: CodeAnarchist Date: Tue, 9 Jun 2026 21:49:35 +0200 Subject: [PATCH 21/39] [net] clean up sockets, multicast and tcp lifetime --- .../application_layer/csocket_http_server.cpp | 2 +- .../application_layer/dhcp_daemon.c | 35 +- .../application_layer/dhcp_daemon.h | 1 - .../application_layer/dhcpv6_daemon.c | 22 +- .../application_layer/dhcpv6_daemon.h | 1 - kernel/networking/application_layer/dns/dns.c | 14 +- .../application_layer/dns/dns_daemon.c | 171 +++-- .../application_layer/dns/dns_daemon.h | 6 +- .../application_layer/dns/dns_mdns.c | 18 +- .../application_layer/dns/mdns_responder.c | 46 +- .../application_layer/dns/mdns_responder.h | 10 +- kernel/networking/application_layer/http.c | 1 + kernel/networking/application_layer/ntp.c | 11 +- .../networking/application_layer/ntp_daemon.c | 7 +- .../networking/application_layer/ntp_daemon.h | 1 - kernel/networking/application_layer/sntp.c | 11 +- .../application_layer/sntp_daemon.c | 7 +- .../application_layer/sntp_daemon.h | 1 - .../application_layer/socket_http_client.hpp | 124 ++-- .../application_layer/socket_http_server.hpp | 83 ++- .../application_layer/ssdp_daemon.c | 162 +++-- kernel/networking/interface_manager.c | 55 +- kernel/networking/interface_manager.h | 3 + kernel/networking/internet_layer/icmpv6.c | 4 +- kernel/networking/internet_layer/ipv4.c | 113 +-- kernel/networking/internet_layer/ipv4_route.c | 52 +- kernel/networking/internet_layer/ipv4_route.h | 4 +- kernel/networking/internet_layer/ipv6_route.c | 58 +- kernel/networking/internet_layer/ipv6_route.h | 4 +- kernel/networking/link_layer/arp.c | 7 +- kernel/networking/link_layer/arp.h | 1 - kernel/networking/link_layer/ndp.c | 20 +- kernel/networking/network.cpp | 3 - kernel/networking/network.h | 1 - kernel/networking/network_dispatch.cpp | 7 +- kernel/networking/processes/net_proc.c | 64 +- kernel/networking/transport_layer/csocket.c | 82 +-- kernel/networking/transport_layer/csocket.h | 35 +- .../transport_layer/csocket_tcp.cpp | 27 +- .../networking/transport_layer/csocket_tcp.h | 8 +- .../transport_layer/csocket_udp.cpp | 44 +- .../networking/transport_layer/csocket_udp.h | 9 +- kernel/networking/transport_layer/socket.hpp | 46 +- .../networking/transport_layer/socket_bind.c | 362 +++++++--- .../networking/transport_layer/socket_bind.h | 16 +- .../networking/transport_layer/socket_core.c | 62 +- .../networking/transport_layer/socket_core.h | 14 +- .../networking/transport_layer/socket_tcp.hpp | 168 +++-- .../networking/transport_layer/socket_udp.hpp | 644 ++++++++++-------- kernel/networking/transport_layer/tcp.h | 3 +- .../networking/transport_layer/tcp/tcp_core.c | 245 ++++--- .../transport_layer/tcp/tcp_internal.h | 6 +- .../networking/transport_layer/tcp/tcp_rx.c | 130 ++-- .../transport_layer/tcp/tcp_timer.c | 117 ++-- .../networking/transport_layer/tcp/tcp_tx.c | 105 ++- kernel/networking/transport_layer/udp.c | 2 +- kernel/process/kernel_syscall_impl.c | 30 +- kernel/process/syscall.c | 60 +- shared | 2 +- user/demo/default_process.c | 28 +- 60 files changed, 2004 insertions(+), 1371 deletions(-) diff --git a/kernel/networking/application_layer/csocket_http_server.cpp b/kernel/networking/application_layer/csocket_http_server.cpp index 7e8fd4ab..4d881129 100644 --- a/kernel/networking/application_layer/csocket_http_server.cpp +++ b/kernel/networking/application_layer/csocket_http_server.cpp @@ -59,7 +59,7 @@ int32_t http_connection_close(http_connection_handle_t c) { if (!c) return (int32_t)SOCK_ERR_INVAL; HTTPConnection* conn = reinterpret_cast(c); if (conn->carry_buf.mem_length) string_free(conn->carry_buf); - if (((conn->client).id && (conn->client).protocol != PROTO_NONE)) close_socket(&conn->client); + if (conn->client) close_socket(conn->client); release(conn); return (int32_t)SOCK_OK; } diff --git a/kernel/networking/application_layer/dhcp_daemon.c b/kernel/networking/application_layer/dhcp_daemon.c index b53c90bf..47d4078f 100644 --- a/kernel/networking/application_layer/dhcp_daemon.c +++ b/kernel/networking/application_layer/dhcp_daemon.c @@ -49,12 +49,11 @@ typedef struct { static volatile bool g_force_renew = false; static uint16_t g_pid_dhcpd = 0xFFFF; -static dhcp_if_state_t g_if[MAX_L2_INTERFACES * MAX_IPV4_PER_INTERFACE]; +static dhcp_if_state_t g_if[MAX_IPV4_L3_INTERFACES]; 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 uint32_t dhcp_next_backoff_ms(dhcp_if_state_t* st) { @@ -83,7 +82,7 @@ static void ensure_inventory() { 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) { - if (g_if[i].sock.id && g_if[i].sock.protocol != PROTO_NONE) close_socket(&g_if[i].sock); + if (g_if[i].sock) close_socket(g_if[i].sock); if (i < g_if_count - 1) g_if[i] = g_if[g_if_count - 1]; g_if_count--; continue; @@ -120,14 +119,19 @@ static void ensure_inventory() { 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); - memset(&st.sock, 0, sizeof(st.sock)); - create_socket(SOCKET_SERVER, PROTO_UDP, NULL, &st.sock); + st.sock = create_socket(PROTO_UDP, NULL); + if (!st.sock) continue; + if (set_socket_option(st.sock, SOCK_OPT_BROADCAST_ALLOWED, NULL, 0) != SOCK_OK) { + close_socket(st.sock); + continue; + } SockBindSpec spec; memset(&spec, 0, sizeof(spec)); spec.kind = BIND_L3; + spec.ver = IP_VER4; spec.l3_id = st.l3_id; - if (bind_socket_spec(&st.sock, &spec, 68) != SOCK_OK) { - close_socket(&st.sock); + if (bind_socket(st.sock, &spec, 68) != SOCK_OK) { + close_socket(st.sock); continue; } st.retry_left_ms = 0; @@ -149,7 +153,7 @@ static bool udp_wait_for_type_on(socket_handle_t sock, uint8_t wanted, uint32_t uint8_t buf[1024]; net_l4_endpoint src; memset(&src, 0, sizeof(src)); - int64_t r = receive_from_socket(&sock, buf, sizeof(buf), &src); + int64_t r = receive_from_socket(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; } @@ -184,7 +188,7 @@ static bool udp_wait_for_ack_or_nak(socket_handle_t sock, uint32_t expect_xid, c uint8_t buf[1024]; net_l4_endpoint src; memset(&src, 0, sizeof(src)); - int64_t r = receive_from_socket(&sock, buf, sizeof(buf), &src); + int64_t r = receive_from_socket(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; } @@ -340,7 +344,7 @@ static void dhcp_send_discover_for(dhcp_if_state_t* st) { uint32_t bcast = 0xFFFFFFFFu; net_l4_endpoint dst; make_ep(bcast, 67, IP_VER4, &dst); - send_to_socket(&st->sock, &dst, (const void*)pkt.ptr, pkt.size); + send_to_socket(st->sock, &dst, (const void*)pkt.ptr, pkt.size); free_sized((void*)pkt.ptr, pkt.size); } @@ -349,7 +353,7 @@ static void dhcp_send_request_select_for(dhcp_if_state_t* st, const dhcp_request uint32_t dip = 0xFFFFFFFFu; net_l4_endpoint dst; make_ep(dip, 67, IP_VER4, &dst); - send_to_socket(&st->sock, &dst, (const void*)pkt.ptr, pkt.size); + send_to_socket(st->sock, &dst, (const void*)pkt.ptr, pkt.size); free_sized((void*)pkt.ptr, pkt.size); } @@ -369,7 +373,7 @@ static void dhcp_send_renew_for(dhcp_if_state_t* st) { uint32_t dip = st->server_ip_net ? st->server_ip_net : 0xFFFFFFFFu; net_l4_endpoint dst; make_ep(dip, 67, IP_VER4, &dst); - send_to_socket(&st->sock, &dst, (const void*)pkt.ptr, pkt.size); + send_to_socket(st->sock, &dst, (const void*)pkt.ptr, pkt.size); free_sized((void*)pkt.ptr, pkt.size); } @@ -389,7 +393,7 @@ static void dhcp_send_rebind_for(dhcp_if_state_t* st) { uint32_t dip = 0xFFFFFFFFu; net_l4_endpoint dst; make_ep(dip, 67, IP_VER4, &dst); - send_to_socket(&st->sock, &dst, (const void*)pkt.ptr, pkt.size); + send_to_socket(st->sock, &dst, (const void*)pkt.ptr, pkt.size); free_sized((void*)pkt.ptr, pkt.size); } @@ -409,7 +413,7 @@ static void dhcp_send_inform_for(dhcp_if_state_t* st) { uint32_t dip = 0xFFFFFFFFu; net_l4_endpoint dst; make_ep(dip, 67, IP_VER4, &dst); - send_to_socket(&st->sock, &dst, (const void*)pkt.ptr, pkt.size); + send_to_socket(st->sock, &dst, (const void*)pkt.ptr, pkt.size); free_sized((void*)pkt.ptr, pkt.size); } @@ -563,8 +567,7 @@ static void maybe_send_inform() { 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_pid_dhcpd = get_current_proc_pid(); for (;;) { ensure_inventory(); if (g_if_count == 0) { msleep(250); continue; } diff --git a/kernel/networking/application_layer/dhcp_daemon.h b/kernel/networking/application_layer/dhcp_daemon.h index 9b1f281a..11a593d8 100644 --- a/kernel/networking/application_layer/dhcp_daemon.h +++ b/kernel/networking/application_layer/dhcp_daemon.h @@ -10,7 +10,6 @@ int dhcp_daemon_entry(int argc, char* argv[]); uint16_t dhcp_get_pid(); bool dhcp_is_running(); -void dhcp_set_pid(uint16_t pid); void dhcp_notify_link_up(); void dhcp_notify_link_down(); diff --git a/kernel/networking/application_layer/dhcpv6_daemon.c b/kernel/networking/application_layer/dhcpv6_daemon.c index 2ed92c79..891d6b27 100644 --- a/kernel/networking/application_layer/dhcpv6_daemon.c +++ b/kernel/networking/application_layer/dhcpv6_daemon.c @@ -72,7 +72,6 @@ static uint64_t g_force_decline_mask = 0; uint16_t dhcpv6_get_pid() { return g_dhcpv6_pid; } bool dhcpv6_is_running() { return g_dhcpv6_pid != 0xFFFF; } -void dhcpv6_set_pid(uint16_t pid) { g_dhcpv6_pid = pid; } void dhcpv6_force_renew_all() { g_force_renew_all = true; } void dhcpv6_force_rebind_all() { g_force_rebind_all = true; } @@ -190,7 +189,7 @@ static void ensure_binds() { dhcpv6_bind_t* rb = (dhcpv6_bind_t*)linked_list_remove(g_dhcpv6_binds, it); if (rb) { - if (rb->sock.id && rb->sock.protocol != PROTO_NONE) close_socket(&rb->sock); + if (rb->sock) close_socket(rb->sock); free_sized(rb, sizeof(*rb)); } } @@ -257,8 +256,8 @@ static void ensure_binds() { const uint8_t* mac = network_get_mac(b->ifindex); if (mac) { memcpy(b->mac, mac, 6); b->mac_ok = 1; } - create_socket(SOCKET_SERVER, PROTO_UDP, NULL, &b->sock); - if (!b->sock.id || b->sock.protocol == PROTO_NONE) { + b->sock = create_socket(PROTO_UDP, NULL); + if (!b->sock) { free_sized(b, sizeof(*b)); continue; } @@ -269,9 +268,9 @@ static void ensure_binds() { spec.ver = IP_VER6; spec.l3_id = b->bound_linklocal_l3_id; - if (bind_socket_spec(&b->sock, &spec, DHCPV6_CLIENT_PORT) != SOCK_OK) { - close_socket(&b->sock); - b->sock = ((socket_handle_t){0}); + if (bind_socket(b->sock, &spec, DHCPV6_CLIENT_PORT) != SOCK_OK) { + close_socket(b->sock); + b->sock = 0; free_sized(b, sizeof(*b)); continue; } @@ -285,7 +284,7 @@ static void ensure_binds() { } static void fsm_once(dhcpv6_bind_t* b, uint32_t tick_ms) { - if (!b || !b->mac_ok || !b->sock.id || b->sock.protocol == PROTO_NONE) return; + if (!b || !b->mac_ok || !b->sock) return; if (b->done) return; l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(b->target_l3_id); @@ -540,7 +539,7 @@ static void fsm_once(dhcpv6_bind_t* b, uint32_t tick_ms) { ipv6_make_multicast(2, IPV6_MCAST_DHCPV6_SERVERS, NULL, dst.ip); dst.port = DHCPV6_SERVER_PORT; - (void)send_to_socket(&b->sock, &dst, msg, (uint64_t)msg_len); + (void)send_to_socket(b->sock, &dst, msg, (uint64_t)msg_len); b->tx_tries++; uint8_t rx[DHCPV6_MAX_MSG]; @@ -553,7 +552,7 @@ static void fsm_once(dhcpv6_bind_t* b, uint32_t tick_ms) { uint32_t waited = 0; while (waited < 250) { - int64_t r = receive_from_socket(&b->sock, rx, sizeof(rx), &src); + int64_t r = receive_from_socket(b->sock, rx, sizeof(rx), &src); if (r > 0) { if (src.port != DHCPV6_SERVER_PORT) { msleep(50); @@ -774,8 +773,7 @@ int dhcpv6_daemon_entry(int argc, char* argv[]) { (void)argc; (void)argv; - g_dhcpv6_pid = (uint16_t)get_current_proc_pid(); - dhcpv6_set_pid(g_dhcpv6_pid); + g_dhcpv6_pid = get_current_proc_pid(); uint64_t virt_timer; asm volatile ("mrs %0, cntvct_el0" : "=r"(virt_timer)); diff --git a/kernel/networking/application_layer/dhcpv6_daemon.h b/kernel/networking/application_layer/dhcpv6_daemon.h index 6a2c7675..25cdf605 100644 --- a/kernel/networking/application_layer/dhcpv6_daemon.h +++ b/kernel/networking/application_layer/dhcpv6_daemon.h @@ -10,7 +10,6 @@ int dhcpv6_daemon_entry(int argc, char* argv[]); uint16_t dhcpv6_get_pid(); bool dhcpv6_is_running(); -void dhcpv6_set_pid(uint16_t pid); void dhcpv6_force_renew_all(); void dhcpv6_force_rebind_all(); diff --git a/kernel/networking/application_layer/dns/dns.c b/kernel/networking/application_layer/dns/dns.c index 116fa455..b4c42433 100644 --- a/kernel/networking/application_layer/dns/dns.c +++ b/kernel/networking/application_layer/dns/dns.c @@ -20,7 +20,7 @@ static dns_result_t perform_dns_query_once(socket_handle_t sock, const net_l4_endpoint *dns_srv, const char *name, dns_qtype_t qtype, uint32_t timeout_ms, dns_record_t *out_records, uint32_t max_records, uint32_t *out_count) { if (out_count) *out_count = 0; - if (!((sock).id && (sock).protocol != PROTO_NONE)) return DNS_ERR_SOCKET; + if (!sock) return DNS_ERR_SOCKET; if (!dns_srv) return DNS_ERR_NO_DNS; if (!name) return DNS_ERR_FORMAT; if (!out_records && max_records) return DNS_ERR_FORMAT; @@ -37,14 +37,14 @@ static dns_result_t perform_dns_query_once(socket_handle_t sock, const net_l4_en net_l4_endpoint dst = *dns_srv; dst.port = 53; - int64_t sent = send_to_socket(&sock, &dst, (void*)request_buffer, request_len); + int64_t sent = send_to_socket(sock, &dst, (void*)request_buffer, request_len); 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 = receive_from_socket(&sock, response_buffer, sizeof(response_buffer), &source); + int64_t received = receive_from_socket(sock, response_buffer, sizeof(response_buffer), &source); if (received > 0 && source.port == 53 && source.ver == dst.ver) { uint32_t received_len = received; dns_record_t parsed[DNS_QUERY_RECORDS]; @@ -179,9 +179,8 @@ static dns_result_t query_with_selection(const net_l4_endpoint* primary, const n if (which == DNS_USE_PRIMARY && dns_srv_is_zero(primary)) return DNS_ERR_NO_DNS; if (which == DNS_USE_SECONDARY && dns_srv_is_zero(secondary)) return DNS_ERR_NO_DNS; if (which == DNS_USE_BOTH && dns_srv_is_zero(primary) && dns_srv_is_zero(secondary)) return DNS_ERR_NO_DNS; - socket_handle_t sock = {0}; - create_socket(SOCKET_CLIENT, PROTO_UDP, NULL, &sock); - if (!((sock).id && (sock).protocol != PROTO_NONE)) return DNS_ERR_SOCKET; + socket_handle_t sock = create_socket(PROTO_UDP, NULL); + if (!sock) return DNS_ERR_SOCKET; dns_result_t res = DNS_ERR_NO_DNS; if (which == DNS_USE_PRIMARY) res = perform_dns_query_once(sock, primary, hostname, qtype, timeout_ms, out_records, max_records, out_count); @@ -194,8 +193,7 @@ static dns_result_t query_with_selection(const net_l4_endpoint* primary, const n if (res != DNS_OK && second && first != second) res = perform_dns_query_once(sock, second, hostname, qtype, timeout_ms, out_records, max_records, out_count); } - close_socket(&sock); - close_socket(&sock); + close_socket(sock); return res; } diff --git a/kernel/networking/application_layer/dns/dns_daemon.c b/kernel/networking/application_layer/dns/dns_daemon.c index f185c913..b5d7e2d2 100644 --- a/kernel/networking/application_layer/dns/dns_daemon.c +++ b/kernel/networking/application_layer/dns/dns_daemon.c @@ -7,57 +7,128 @@ #include "net/socket_types.h" #include "networking/transport_layer/csocket.h" #include "networking/internet_layer/ipv6_utils.h" +#include "networking/interface_manager.h" #include "std/memory.h" static uint16_t g_pid_dnsd = 0xFFFF; -static socket_handle_t g_sock = {0}; - -static socket_handle_t g_sock_mdns4 = {0}; -static socket_handle_t g_sock_mdns6 = {0}; +static socket_handle_t g_sock = 0; +static mdns_tx_target_t g_mdns[MAX_L3_INTERFACES]; +static uint8_t g_mdns_count = 0; uint16_t dns_get_pid(void){ return g_pid_dnsd; } bool dns_is_running(void){ return g_pid_dnsd != 0xFFFF; } -void dns_set_pid(uint16_t p){ g_pid_dnsd = p; } socket_handle_t dns_socket_handle(void){ return g_sock; } -socket_handle_t mdns_socket_handle_v4(void){ return g_sock_mdns4; } -socket_handle_t mdns_socket_handle_v6(void){ return g_sock_mdns6; } - -static socket_handle_t mdns_create_socket(ip_version_t ver, const uint8_t* group) { - if (!group) return ((socket_handle_t){0}); - SocketExtraOptions opt; - memset(&opt, 0, sizeof(opt)); - opt.flags = SOCK_OPT_MCAST_JOIN | SOCK_OPT_TTL; - opt.ttl = 255; - opt.mcast_count = 1; - opt.mcast_groups[0].ver = ver; - opt.mcast_groups[0].port = DNS_MDNS_PORT; - if(ver == IP_VER4) memcpy(opt.mcast_groups[0].ip, group, 4); - else if (ver == IP_VER6) memcpy(opt.mcast_groups[0].ip, group, 16); - else return ((socket_handle_t){0}); - - socket_handle_t s = {0}; - create_socket(SOCKET_SERVER, PROTO_UDP, &opt, &s); - if(!((s).id && (s).protocol != PROTO_NONE)) return ((socket_handle_t){0}); - - SockBindSpec spec; - memset(&spec, 0, sizeof(spec)); - spec.kind = BIND_ANY; - spec.ver = ver; - - if(bind_socket_spec(&s, &spec, DNS_MDNS_PORT) != SOCK_OK){ - close_socket(&s); - return ((socket_handle_t){0}); +socket_handle_t mdns_socket_handle(void){ return g_mdns_count ? g_mdns[0].sock : 0; } +socket_handle_t mdns_socket_handle_for(ip_version_t ver){ + for (uint8_t i = 0; i < g_mdns_count; i++) { + if (g_mdns[i].ver == ver) return g_mdns[i].sock; } + return 0; +} + +static void mdns_open_sockets(const uint8_t *group4, const uint8_t *group6) { + if (!group4 || !group6) return; + + uint8_t n_if = l2_interface_count(); + for (uint8_t i = 0; i < n_if && g_mdns_count < MAX_L3_INTERFACES; 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 && g_mdns_count < MAX_L3_INTERFACES; j++) { + l3_ipv4_interface_t *v4 = l2->l3_v4[j]; + if (!v4 || v4->mode == IPV4_CFG_DISABLED || v4->is_localhost || !v4->ip) continue; + bool have_socket = false; + for (uint8_t k = 0; k < g_mdns_count; k++) { + if (g_mdns[k].sock && g_mdns[k].ver == IP_VER4 && g_mdns[k].l3_id == v4->l3_id) { + have_socket = true; + break; + } + } + if (have_socket) continue; + + SocketExtraOptions opt; + memset(&opt, 0, sizeof(opt)); + opt.flags = SOCK_OPT_TTL; + opt.ttl = 255; + + socket_handle_t s = create_socket(PROTO_UDP, &opt); + if (!s) continue; + + SockBindSpec spec; + memset(&spec, 0, sizeof(spec)); + spec.kind = BIND_L3; + spec.ver = IP_VER4; + spec.l3_id = v4->l3_id; + + net_l4_endpoint group; + memset(&group, 0, sizeof(group)); + group.ver = IP_VER4; + group.port = DNS_MDNS_PORT; + memcpy(group.ip, group4, 4); + + if (bind_socket(s, &spec, DNS_MDNS_PORT) != SOCK_OK || set_socket_option(s, SOCK_OPT_MCAST_JOIN, &group, sizeof(group)) != SOCK_OK) { + close_socket(s); + continue; + } + + g_mdns[g_mdns_count].sock = s; + g_mdns[g_mdns_count].ver = IP_VER4; + g_mdns[g_mdns_count].l3_id = v4->l3_id; + memcpy(g_mdns[g_mdns_count].mcast_ip, group4, 4); + g_mdns_count++; + } - return s; + for (uint8_t j = 0; j < MAX_IPV6_PER_INTERFACE && g_mdns_count < MAX_L3_INTERFACES; j++) { + l3_ipv6_interface_t *v6 = l2->l3_v6[j]; + if (!v6 || v6->cfg == IPV6_CFG_DISABLE || v6->dad_state != IPV6_DAD_OK || v6->is_localhost || !v6->ip[0]) continue; + bool have_socket = false; + for (uint8_t k = 0; k < g_mdns_count; k++) { + if (g_mdns[k].sock && g_mdns[k].ver == IP_VER6 && g_mdns[k].l3_id == v6->l3_id) { + have_socket = true; + break; + } + } + if (have_socket) continue; + + SocketExtraOptions opt; + memset(&opt, 0, sizeof(opt)); + opt.flags = SOCK_OPT_TTL; + opt.ttl = 255; + + socket_handle_t s = create_socket(PROTO_UDP, &opt); + if (!s) continue; + + SockBindSpec spec; + memset(&spec, 0, sizeof(spec)); + spec.kind = BIND_L3; + spec.ver = IP_VER6; + spec.l3_id = v6->l3_id; + + net_l4_endpoint group; + memset(&group, 0, sizeof(group)); + group.ver = IP_VER6; + group.port = DNS_MDNS_PORT; + memcpy(group.ip, group6, 16); + + if (bind_socket(s, &spec, DNS_MDNS_PORT) != SOCK_OK || set_socket_option(s, SOCK_OPT_MCAST_JOIN, &group, sizeof(group)) != SOCK_OK) { + close_socket(s); + continue; + } + + g_mdns[g_mdns_count].sock = s; + g_mdns[g_mdns_count].ver = IP_VER6; + g_mdns[g_mdns_count].l3_id = v6->l3_id; + memcpy(g_mdns[g_mdns_count].mcast_ip, group6, 16); + g_mdns_count++; + } + } } int dns_deamon_entry(int argc, char* argv[]){ (void)argc; (void)argv; - dns_set_pid(get_current_proc_pid()); - memset(&g_sock, 0, sizeof(g_sock)); - create_socket(SOCKET_CLIENT, PROTO_UDP, NULL, &g_sock); + g_pid_dnsd = get_current_proc_pid(); + g_sock = create_socket(PROTO_UDP, NULL); uint32_t mdns_v4 = IPV4_MCAST_MDNS; uint8_t mdns_v4_addr[4]; @@ -65,28 +136,28 @@ int dns_deamon_entry(int argc, char* argv[]){ memcpy(mdns_v4_addr, &mdns_v4, 4); ipv6_make_multicast(0x02, IPV6_MCAST_MDNS, 0, mdns_v6); - g_sock_mdns4 = mdns_create_socket(IP_VER4, mdns_v4_addr); - g_sock_mdns6 = mdns_create_socket(IP_VER6, mdns_v6); + mdns_open_sockets(mdns_v4_addr, mdns_v6); uint32_t tick_ms = 100; for(;;) { + mdns_open_sockets(mdns_v4_addr, mdns_v6); dns_cache_tick(tick_ms); uint8_t buf[900]; net_l4_endpoint src; - if (((g_sock_mdns4).id && (g_sock_mdns4).protocol != PROTO_NONE)) { - memset(&src, 0, sizeof(src)); - int64_t r4 = receive_from_socket(&g_sock_mdns4, buf, sizeof(buf), &src); - if(r4 > 0 && src.ver == IP_VER4) mdns_responder_handle_query(g_sock_mdns4, IP_VER4, mdns_v4_addr, buf, (uint32_t)r4, &src); - } - - if (((g_sock_mdns6).id && (g_sock_mdns6).protocol != PROTO_NONE)) { - memset(&src, 0, sizeof(src)); - int64_t r6 = receive_from_socket(&g_sock_mdns6, buf, sizeof(buf), &src); - if(r6 > 0 && src.ver == IP_VER6) mdns_responder_handle_query(g_sock_mdns6, IP_VER6, mdns_v6, buf, (uint32_t)r6, &src); + for (uint8_t sidx = 0; sidx < g_mdns_count; sidx++) { + socket_handle_t s = g_mdns[sidx].sock; + for (int i = 0; i < 4; ++i) { + memset(&src, 0, sizeof(src)); + int64_t r = receive_from_socket(s, buf, sizeof(buf), &src); + if (r == SOCK_ERR_WOULDBLOCK) break; + if (r < 0) break; + if (!r) continue; + mdns_responder_handle_query(s, g_mdns[sidx].ver, g_mdns[sidx].mcast_ip, buf, (uint32_t)r, &src); + } } - mdns_responder_tick(g_sock_mdns4,g_sock_mdns6,mdns_v4_addr,mdns_v6); + mdns_responder_tick_multi(g_mdns, g_mdns_count); msleep(tick_ms); } return 1; diff --git a/kernel/networking/application_layer/dns/dns_daemon.h b/kernel/networking/application_layer/dns/dns_daemon.h index 4c4afd4a..739b6a0a 100644 --- a/kernel/networking/application_layer/dns/dns_daemon.h +++ b/kernel/networking/application_layer/dns/dns_daemon.h @@ -1,5 +1,6 @@ #pragma once #include "networking/transport_layer/csocket.h" +#include "net/network_types.h" #define IPV4_MCAST_MDNS 0xE00000FBu @@ -7,11 +8,10 @@ extern "C" { #endif bool dns_is_running(void); -void dns_set_pid(uint16_t p); socket_handle_t dns_socket_handle(void); -socket_handle_t mdns_socket_handle_v4(void); -socket_handle_t mdns_socket_handle_v6(void); +socket_handle_t mdns_socket_handle(void); +socket_handle_t mdns_socket_handle_for(ip_version_t ver); uint16_t dns_get_pid(void); diff --git a/kernel/networking/application_layer/dns/dns_mdns.c b/kernel/networking/application_layer/dns/dns_mdns.c index b23091f9..798d0f27 100644 --- a/kernel/networking/application_layer/dns/dns_mdns.c +++ b/kernel/networking/application_layer/dns/dns_mdns.c @@ -6,7 +6,7 @@ static dns_result_t perform_mdns_query_once(socket_handle_t sock, const net_l4_endpoint *dst, const char *name, dns_qtype_t qtype, uint32_t timeout_ms, dns_record_t *out_records, uint32_t max_records, uint32_t *out_count) { if (out_count) *out_count = 0; - if (!((sock).id && (sock).protocol != PROTO_NONE)) return DNS_ERR_NO_DNS; + if (!sock) return DNS_ERR_NO_DNS; if (!dst) return DNS_ERR_NO_DNS; if (!name) return DNS_ERR_FORMAT; if (!out_records && max_records) return DNS_ERR_FORMAT; @@ -15,7 +15,7 @@ static dns_result_t perform_mdns_query_once(socket_handle_t sock, const net_l4_e uint32_t offset = dns_wire_build_query(request_buffer, sizeof(request_buffer), 0, name, qtype, false); if (!offset) return DNS_ERR_FORMAT; - int64_t sent = send_to_socket(&sock, dst, (void*)request_buffer, offset); + int64_t sent = send_to_socket(sock, dst, (void*)request_buffer, offset); if (sent < 0) return DNS_ERR_SEND; uint32_t found = 0; @@ -23,7 +23,7 @@ static dns_result_t perform_mdns_query_once(socket_handle_t sock, const net_l4_e while (waited_ms < timeout_ms) { uint8_t response_buffer[512]; net_l4_endpoint source; - int64_t received = receive_from_socket(&sock, response_buffer, sizeof(response_buffer), &source); + int64_t received = receive_from_socket(sock, response_buffer, sizeof(response_buffer), &source); if (received > 0 && source.port == DNS_MDNS_PORT){ uint32_t received_len = received; dns_record_t records[12]; @@ -64,13 +64,13 @@ dns_result_t mdns_query(const char* name, dns_qtype_t qtype, uint32_t timeout_ms dns_result_t last = DNS_ERR_NO_DNS; uint32_t total = 0; - socket_handle_t sock4 = mdns_socket_handle_v4(); - if (((sock4).id && (sock4).protocol != PROTO_NONE)) { + socket_handle_t sock = mdns_socket_handle_for(IP_VER4); + if (sock) { uint32_t group =DNS_MDNS_GROUP_V4; net_l4_endpoint dst; make_ep(group, DNS_MDNS_PORT, IP_VER4, &dst); uint32_t got = 0; - last = perform_mdns_query_once(sock4, &dst, name, qtype, timeout_ms, out_records, max_records, &got); + last = perform_mdns_query_once(sock, &dst, name, qtype, timeout_ms, out_records, max_records, &got); if (last == DNS_OK) total = got; if (total >= max_records && max_records) { if (out_count) *out_count =total; @@ -78,8 +78,8 @@ dns_result_t mdns_query(const char* name, dns_qtype_t qtype, uint32_t timeout_ms } } - socket_handle_t sock6 = mdns_socket_handle_v6(); - if (((sock6).id && (sock6).protocol != PROTO_NONE)) { + sock = mdns_socket_handle_for(IP_VER6); + if (sock) { net_l4_endpoint dst; memset(&dst, 0, sizeof(dst)); dst.ver = IP_VER6; @@ -88,7 +88,7 @@ dns_result_t mdns_query(const char* name, dns_qtype_t qtype, uint32_t timeout_ms uint32_t got = 0; dns_record_t *dst_records = out_records ? out_records + total : 0; uint32_t left = max_records > total ? max_records - total : 0; - dns_result_t r6 = perform_mdns_query_once(sock6, &dst, name, qtype, timeout_ms, dst_records, left, &got); + dns_result_t r6 = perform_mdns_query_once(sock, &dst, name, qtype, timeout_ms, dst_records, left, &got); if (r6 == DNS_OK) { total += got; last = DNS_OK; diff --git a/kernel/networking/application_layer/dns/mdns_responder.c b/kernel/networking/application_layer/dns/mdns_responder.c index 5527821c..8ac1b394 100644 --- a/kernel/networking/application_layer/dns/mdns_responder.c +++ b/kernel/networking/application_layer/dns/mdns_responder.c @@ -72,7 +72,7 @@ static mdns_service_t g_mdns_services[MDNS_MAX_SERVICES]; static mdns_cache_entry_t g_mdns_cache[MDNS_CACHE_MAX]; static void mdns_send(socket_handle_t sock, const net_l4_endpoint *src, bool unicast, ip_version_t ver, const uint8_t *mcast_ip, const uint8_t *pkt, uint32_t pkt_len) { - if (!((sock).id && (sock).protocol != PROTO_NONE)) return; + if (!sock) return; if (!pkt) return; if (!pkt_len) return; @@ -82,7 +82,7 @@ static void mdns_send(socket_handle_t sock, const net_l4_endpoint *src, bool uni if (unicast && src) { dst = *src; if (!dst.port) dst.port = DNS_MDNS_PORT; - send_to_socket(&sock, &dst, pkt, pkt_len); + send_to_socket(sock, &dst, pkt, pkt_len); return; } @@ -90,7 +90,7 @@ static void mdns_send(socket_handle_t sock, const net_l4_endpoint *src, bool uni if (ver == IP_VER4) memcpy(dst.ip, mcast_ip, 4); else memcpy(dst.ip, mcast_ip, 16); dst.port = DNS_MDNS_PORT; - send_to_socket(&sock, &dst, pkt, pkt_len); + send_to_socket(sock, &dst, pkt, pkt_len); } static bool mdns_pick_identity(uint32_t *out_v4, uint8_t out_v6[16], uint8_t *out_ifindex, uint8_t out_ifid[8]) { @@ -658,7 +658,8 @@ bool mdns_deregister_service(const char *instance, const char *service, const ch return false; } -void mdns_responder_tick(socket_handle_t sock4, socket_handle_t sock6, const uint8_t mcast_v4[4], const uint8_t mcast_v6[16]) { +void mdns_responder_tick_multi(const mdns_tx_target_t *targets, uint32_t target_count) { + if (!targets) target_count = 0; mdns_refresh_identity(); uint64_t now = get_time(); for (uint32_t i = 0; i < MDNS_CACHE_MAX; i++) { @@ -700,8 +701,10 @@ void mdns_responder_tick(socket_handle_t sock4, socket_handle_t sock6, const uin if (ok && p.an) { mdns_pkt_commit(&p); - if (((sock4).id && (sock4).protocol != PROTO_NONE) && mcast_v4) mdns_send(sock4, 0, false, IP_VER4, mcast_v4, pkt, p.off); - if (((sock6).id && (sock6).protocol != PROTO_NONE) && mcast_v6) mdns_send(sock6, 0, false, IP_VER6, mcast_v6, pkt, p.off); + for (uint32_t t = 0; t < target_count; t++) { + if (!targets[t].sock) continue; + mdns_send(targets[t].sock, 0, false, targets[t].ver, targets[t].mcast_ip, pkt, p.off); + } uint32_t ttl_ms = MDNS_TTL_S * 1000; @@ -741,8 +744,10 @@ void mdns_responder_tick(socket_handle_t sock4, socket_handle_t sock6, const uin if (mdns_pkt_begin(&p, pkt, sizeof(pkt), DNS_FLAG_QR | DNS_FLAG_AA) && mdns_add_service_records(&p, s, MDNS_TTL_S, do_goodbye) && (p.an || p.ar)) { mdns_pkt_commit(&p); - if (((sock4).id && (sock4).protocol != PROTO_NONE) && mcast_v4) mdns_send(sock4, 0, false, IP_VER4, mcast_v4, pkt, p.off); - if (((sock6).id && (sock6).protocol != PROTO_NONE) && mcast_v6) mdns_send(sock6, 0, false, IP_VER6, mcast_v6, pkt, p.off); + for (uint32_t t = 0; t < target_count; t++) { + if (!targets[t].sock) continue; + mdns_send(targets[t].sock, 0, false, targets[t].ver, targets[t].mcast_ip, pkt, p.off); + } } if (do_goodbye) { @@ -757,8 +762,31 @@ void mdns_responder_tick(socket_handle_t sock4, socket_handle_t sock6, const uin } } +void mdns_responder_tick(socket_handle_t sock, const uint8_t mcast_v4[4], const uint8_t mcast_v6[16]) { + mdns_tx_target_t targets[2]; + uint32_t n = 0; + + if (sock && mcast_v4) { + memset(&targets[n], 0, sizeof(targets[n])); + targets[n].sock = sock; + targets[n].ver = IP_VER4; + memcpy(targets[n].mcast_ip, mcast_v4, 4); + n++; + } + + if (sock && mcast_v6) { + memset(&targets[n], 0, sizeof(targets[n])); + targets[n].sock = sock; + targets[n].ver = IP_VER6; + memcpy(targets[n].mcast_ip, mcast_v6, 16); + n++; + } + + mdns_responder_tick_multi(targets, n); +} + void mdns_responder_handle_query(socket_handle_t sock, ip_version_t ver, const uint8_t *mcast_ip, const uint8_t *pkt, uint32_t pkt_len, const net_l4_endpoint *src) { - if (!((sock).id && (sock).protocol != PROTO_NONE)) return; + if (!sock) return; if (!mcast_ip) return; if (!pkt) return; if (pkt_len < 12) return; diff --git a/kernel/networking/application_layer/dns/mdns_responder.h b/kernel/networking/application_layer/dns/mdns_responder.h index c2e00914..211d7935 100644 --- a/kernel/networking/application_layer/dns/mdns_responder.h +++ b/kernel/networking/application_layer/dns/mdns_responder.h @@ -9,7 +9,15 @@ extern "C" { bool mdns_register_service(const char *instance, const char *service, const char *proto, uint16_t port, const char *txt); bool mdns_deregister_service(const char *instance, const char *service, const char *proto); -void mdns_responder_tick(socket_handle_t sock4, socket_handle_t sock6, const uint8_t mcast_v4[4], const uint8_t mcast_v6[16]); +typedef struct { + socket_handle_t sock; + ip_version_t ver; + uint8_t l3_id; + uint8_t mcast_ip[16]; +} mdns_tx_target_t; + +void mdns_responder_tick(socket_handle_t sock, const uint8_t mcast_v4[4], const uint8_t mcast_v6[16]); +void mdns_responder_tick_multi(const mdns_tx_target_t *targets, uint32_t target_count); void mdns_responder_handle_query(socket_handle_t sock, ip_version_t ver, const uint8_t *mcast_ip, const uint8_t *pkt, uint32_t pkt_len, const net_l4_endpoint *src); #ifdef __cplusplus diff --git a/kernel/networking/application_layer/http.c b/kernel/networking/application_layer/http.c index 284e9dd0..f5805d18 100644 --- a/kernel/networking/application_layer/http.c +++ b/kernel/networking/application_layer/http.c @@ -414,6 +414,7 @@ HTTPParseResult http_header_parse(const char *buf, uint32_t len, const HTTPPolic handled = true; } else if (key_len == 5 && strncmp_case(buf + pos, "range", true, key_len) == 0){ C->fields.range = string_from_literal_length(buf + val_start, val_len); + C->range.has = 1; C->range.invalid = 1; if (val_len > 6 && strncmp_case(buf + val_start, "bytes=", true, 6) == 0) { uint32_t rp = val_start + 6; diff --git a/kernel/networking/application_layer/ntp.c b/kernel/networking/application_layer/ntp.c index 11d24a34..5ff7aca4 100644 --- a/kernel/networking/application_layer/ntp.c +++ b/kernel/networking/application_layer/ntp.c @@ -110,7 +110,7 @@ static ntp_result_t ntp_send_query(socket_handle_t sock, uint32_t server_ip_host net_l4_endpoint dst; make_ep(server_ip_host, NTP_PORT, IP_VER4, &dst); - int64_t sent = send_to_socket(&sock, &dst, &p, sizeof(p)); + int64_t sent = send_to_socket(sock, &dst, &p, sizeof(p)); if (sent < 0) return NTP_ERR_SEND; *t1_us_out = t1_us; *tx_ntp64_be_out = tx_be; @@ -237,9 +237,8 @@ ntp_result_t ntp_poll_once(uint32_t timeout_ms){ discover_servers(&s0, &s1); if (s0 == 0 && s1 == 0) return NTP_ERR_NO_SERVER; - socket_handle_t sock = {0}; - create_socket(SOCKET_CLIENT, PROTO_UDP, NULL, &sock); - if (!((sock).id && (sock).protocol != PROTO_NONE)) return NTP_ERR_SOCKET; + socket_handle_t sock = create_socket(PROTO_UDP, NULL); + if (!sock) return NTP_ERR_SOCKET; uint64_t t1_0 = 0, t1_1 = 0; uint64_t o0 = 0, o1 = 0; @@ -259,7 +258,7 @@ ntp_result_t ntp_poll_once(uint32_t timeout_ms){ while (waited < timeout_ms) { uint8_t buf[96]; net_l4_endpoint src; - int64_t n = receive_from_socket(&sock, buf, sizeof(buf), &src); + int64_t n = receive_from_socket(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; @@ -387,7 +386,7 @@ ntp_result_t ntp_poll_once(uint32_t timeout_ms){ if (best_err == NTP_OK && waited >= (timeout_ms / 2)) break; } - close_socket(&sock); + close_socket(sock); ntp_peer_t* best = NULL; for (uint32_t i = 0; i < 2; i++) { diff --git a/kernel/networking/application_layer/ntp_daemon.c b/kernel/networking/application_layer/ntp_daemon.c index cf414bf8..c120bf12 100644 --- a/kernel/networking/application_layer/ntp_daemon.c +++ b/kernel/networking/application_layer/ntp_daemon.c @@ -8,11 +8,10 @@ #include "syscalls/syscalls.h" static uint16_t g_pid_ntp = 0xFFFF; -static socket_handle_t g_sock = {0}; +static socket_handle_t g_sock = 0; uint16_t ntp_get_pid(void){ return g_pid_ntp; } bool ntp_is_running(void){ return g_pid_ntp != 0xFFFF; } -void ntp_set_pid(uint16_t p){ g_pid_ntp = p; } socket_handle_t ntp_socket_handle(void){ return g_sock; } #define NTP_POLL_INTERVAL_MS 60000u @@ -43,9 +42,7 @@ int ntp_daemon_entry(int argc, char* argv[]){ (void)argv; g_pid_ntp = get_current_proc_pid(); - g_sock = ((socket_handle_t){0}); - create_socket(SOCKET_CLIENT, PROTO_UDP, NULL, &g_sock); - ntp_set_pid(get_current_proc_pid()); + g_sock = create_socket(PROTO_UDP, NULL); uint32_t attempts = 0; while (attempts < NTP_BOOTSTRAP_MAX_RETRY) { diff --git a/kernel/networking/application_layer/ntp_daemon.h b/kernel/networking/application_layer/ntp_daemon.h index 61140754..b27ffc7f 100644 --- a/kernel/networking/application_layer/ntp_daemon.h +++ b/kernel/networking/application_layer/ntp_daemon.h @@ -9,7 +9,6 @@ extern "C" { uint16_t ntp_get_pid(void); bool ntp_is_running(void); -void ntp_set_pid(uint16_t p); socket_handle_t ntp_socket_handle(void); int ntp_daemon_entry(int argc, char* argv[]); diff --git a/kernel/networking/application_layer/sntp.c b/kernel/networking/application_layer/sntp.c index 6d7461b3..bda3911d 100644 --- a/kernel/networking/application_layer/sntp.c +++ b/kernel/networking/application_layer/sntp.c @@ -51,7 +51,7 @@ static sntp_result_t sntp_send_query(socket_handle_t sock, uint32_t server_ip_ho p.txTs = unix_us_to_ntp64_be(t1_us); net_l4_endpoint dst; make_ep(server_ip_host, NTP_PORT, IP_VER4, &dst); - int64_t sent = send_to_socket(&sock, &dst, (void*)&p, sizeof(p)); + int64_t sent = send_to_socket(sock, &dst, (void*)&p, sizeof(p)); if (sent < 0) return SNTP_ERR_SEND; *t1_us_out = t1_us; return SNTP_OK; @@ -86,9 +86,8 @@ sntp_result_t sntp_poll_once(uint32_t timeout_ms){ if (s0 == 0 && s1 == 0) return SNTP_ERR_NO_SERVER; - socket_handle_t sock = {0}; - create_socket(SOCKET_CLIENT, PROTO_UDP, NULL, &sock); - if (!((sock).id && (sock).protocol != PROTO_NONE)) return SNTP_ERR_SOCKET; + socket_handle_t sock = create_socket(PROTO_UDP, NULL); + if (!sock) return SNTP_ERR_SOCKET; uint64_t t1_0 = 0, t1_1 = 0; @@ -106,7 +105,7 @@ sntp_result_t sntp_poll_once(uint32_t timeout_ms){ while (waited < timeout_ms){ uint8_t buf[96]; net_l4_endpoint src; - int64_t n = receive_from_socket(&sock, buf, sizeof(buf), &src); + int64_t n = receive_from_socket(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; @@ -157,7 +156,7 @@ sntp_result_t sntp_poll_once(uint32_t timeout_ms){ if (best_server_unix_us != 0 && waited >= (timeout_ms / 2)) break; } - close_socket(&sock); + close_socket(sock); if (best_server_unix_us == 0) return SNTP_ERR_TIMEOUT; diff --git a/kernel/networking/application_layer/sntp_daemon.c b/kernel/networking/application_layer/sntp_daemon.c index 76724540..f9f2bc41 100644 --- a/kernel/networking/application_layer/sntp_daemon.c +++ b/kernel/networking/application_layer/sntp_daemon.c @@ -10,11 +10,10 @@ static uint16_t g_pid_sntp = 0xFFFF; -static socket_handle_t g_sock = {0}; +static socket_handle_t g_sock = 0; uint16_t sntp_get_pid(void){ return g_pid_sntp; } bool sntp_is_running(void){ return g_pid_sntp != 0xFFFF; } -void sntp_set_pid(uint16_t p){ g_pid_sntp = p; } socket_handle_t sntp_socket_handle(void){ return g_sock; } #define SNTP_POLL_INTERVAL_MS (10u * 60u * 1000u) @@ -42,9 +41,7 @@ static bool any_ipv4_configured_nonlocal(void){ int sntp_daemon_entry(int argc, char* argv[]){ (void)argc; (void)argv; g_pid_sntp = (uint16_t)get_current_proc_pid(); - g_sock = ((socket_handle_t){0}); - create_socket(SOCKET_CLIENT, PROTO_UDP, NULL, &g_sock); - sntp_set_pid(get_current_proc_pid()); + g_sock = create_socket(PROTO_UDP, NULL); uint32_t attempts = 0; while (attempts < SNTP_BOOTSTRAP_MAX_RETRY){ if (!any_ipv4_configured_nonlocal()){ diff --git a/kernel/networking/application_layer/sntp_daemon.h b/kernel/networking/application_layer/sntp_daemon.h index 372e2bc5..589d7bba 100644 --- a/kernel/networking/application_layer/sntp_daemon.h +++ b/kernel/networking/application_layer/sntp_daemon.h @@ -8,7 +8,6 @@ extern "C" { uint16_t sntp_get_pid(void); bool sntp_is_running(void); -void sntp_set_pid(uint16_t p); socket_handle_t sntp_socket_handle(void); int sntp_daemon_entry(int argc, char* argv[]); diff --git a/kernel/networking/application_layer/socket_http_client.hpp b/kernel/networking/application_layer/socket_http_client.hpp index 1e7b7bb1..445ed3e0 100644 --- a/kernel/networking/application_layer/socket_http_client.hpp +++ b/kernel/networking/application_layer/socket_http_client.hpp @@ -19,7 +19,6 @@ struct HTTPClientOrigin { class HTTPClient { private: - uint16_t pid; socket_handle_t sock; SocketExtraOptions log_opts; SocketExtraOptions* tcp_extra; @@ -52,7 +51,7 @@ class HTTPClient { uint32_t last_rx_ms = start_ms; while (hdr_end < 0) { - int64_t r = receive_from_socket(&sock, tmp, sizeof(tmp), nullptr); + int64_t r = receive_from_socket(sock, tmp, sizeof(tmp), nullptr); if (r == TCP_WOULDBLOCK) { uint32_t now = (uint32_t)get_time(); if ((now - last_rx_ms) > policy.common.header_idle_timeout_ms || (now - start_ms) > policy.common.header_total_timeout_ms) { @@ -108,9 +107,9 @@ class HTTPClient { if (header_result != HTTP_PARSE_OK) { free_response(&resp); string_free(buf); - if (((sock).id && (sock).protocol != PROTO_NONE)) { - close_socket(&sock); - sock = socket_handle_t{}; + if (sock) { + close_socket(sock); + sock = 0; } return error_response(SOCK_ERR_PROTO); } @@ -143,7 +142,7 @@ class HTTPClient { uint32_t body_last_rx_ms = body_start_ms; while (chunk_result == HTTP_PARSE_INCOMPLETE) { - int64_t r = receive_from_socket(&sock, tmp, sizeof(tmp), nullptr); + int64_t r = receive_from_socket(sock, tmp, sizeof(tmp), nullptr); if (r == TCP_WOULDBLOCK) { uint32_t now = (uint32_t)get_time(); if ((now - body_last_rx_ms) > policy.common.body_idle_timeout_ms || (now - body_start_ms) > policy.common.body_total_timeout_ms) break; @@ -159,9 +158,9 @@ class HTTPClient { http_chunked_decoder_free(&dec); free_response(&resp); string_free(buf); - if (((sock).id && (sock).protocol != PROTO_NONE)) { - close_socket(&sock); - sock = socket_handle_t{}; + if (sock) { + close_socket(sock); + sock = 0; } return error_response(SOCK_ERR_PROTO); } @@ -180,9 +179,9 @@ class HTTPClient { if (need > policy.common.max_body_bytes) { free_response(&resp); string_free(buf); - if (((sock).id && (sock).protocol != PROTO_NONE)) { - close_socket(&sock); - sock = socket_handle_t{}; + if (sock) { + close_socket(sock); + sock = 0; } return error_response(SOCK_ERR_PROTO); } @@ -192,9 +191,9 @@ class HTTPClient { if (!body_copy) { free_response(&resp); string_free(buf); - if (((sock).id && (sock).protocol != PROTO_NONE)) { - close_socket(&sock); - sock = socket_handle_t{}; + if (sock) { + close_socket(sock); + sock = 0; } return error_response(SOCK_ERR_SYS); } @@ -205,7 +204,7 @@ class HTTPClient { uint32_t body_start_ms = (uint32_t)get_time(); uint32_t body_last_rx_ms = body_start_ms; while (copied < need) { - int64_t r = receive_from_socket(&sock, body_copy + copied, need - copied, nullptr); + int64_t r = receive_from_socket(sock, body_copy + copied, need - copied, nullptr); if (r == TCP_WOULDBLOCK) { uint32_t now = (uint32_t)get_time(); if ((now - body_last_rx_ms) > policy.common.body_idle_timeout_ms || (now - body_start_ms) > policy.common.body_total_timeout_ms) break; @@ -221,9 +220,9 @@ class HTTPClient { release(body_copy); free_response(&resp); string_free(buf); - if (((sock).id && (sock).protocol != PROTO_NONE)) { - close_socket(&sock); - sock = socket_handle_t{}; + if (sock) { + close_socket(sock); + sock = 0; } return error_response(SOCK_ERR_PROTO); } @@ -238,16 +237,16 @@ class HTTPClient { uint32_t body_start_ms = (uint32_t)get_time(); uint32_t body_last_rx_ms = body_start_ms; while (body.length <= policy.common.max_body_bytes) { - int64_t r = receive_from_socket(&sock, tmp, sizeof(tmp), nullptr); + int64_t r = receive_from_socket(sock, tmp, sizeof(tmp), nullptr); if (r == TCP_WOULDBLOCK) { uint32_t now = (uint32_t)get_time(); if ((now - body_last_rx_ms) > policy.common.body_idle_timeout_ms || (now - body_start_ms) > policy.common.body_total_timeout_ms) { string_free(body); free_response(&resp); string_free(buf); - if (((sock).id && (sock).protocol != PROTO_NONE)) { - close_socket(&sock); - sock = socket_handle_t{}; + if (sock) { + close_socket(sock); + sock = 0; } return error_response(SOCK_ERR_PROTO); } @@ -258,9 +257,9 @@ class HTTPClient { string_free(body); free_response(&resp); string_free(buf); - if (((sock).id && (sock).protocol != PROTO_NONE)) { - close_socket(&sock); - sock = socket_handle_t{}; + if (sock) { + close_socket(sock); + sock = 0; } return error_response(r); } @@ -269,9 +268,9 @@ class HTTPClient { string_free(body); free_response(&resp); string_free(buf); - if (((sock).id && (sock).protocol != PROTO_NONE)) { - close_socket(&sock); - sock = socket_handle_t{}; + if (sock) { + close_socket(sock); + sock = 0; } return error_response(SOCK_ERR_PROTO); } @@ -289,12 +288,12 @@ class HTTPClient { netlog_socket_event_t ev{}; ev.comp = NETLOG_COMP_HTTP_CLIENT; ev.action = NETLOG_ACT_HTTP_RECV_RESPONSE; - ev.pid = pid; + ev.pid = get_current_proc_pid(); ev.u0 = (uint32_t)resp.status_code; ev.u1 = (uint32_t)resp.body.size; - ev.local_port = get_socket_local_port(&sock); + ev.local_port = get_socket_local_port(sock); net_l4_endpoint sock_remote_ep{}; - get_socket_remote_endpoint(&sock, &sock_remote_ep); + get_socket_remote_endpoint(sock, &sock_remote_ep); ev.remote_ep = sock_remote_ep; netlog_socket_event(&log_opts, &ev); @@ -305,21 +304,18 @@ class HTTPClient { public: - explicit HTTPClient(const SocketExtraOptions* extra, const HTTPClientPolicyOptions* http_options) : pid((uint16_t)get_current_proc_pid()), sock(socket_handle_t{}), log_opts{}, tcp_extra(nullptr), policy(http_client_policy_from_options(http_options)), origin{} { + explicit HTTPClient(const SocketExtraOptions* extra, const HTTPClientPolicyOptions* http_options) : sock(0), log_opts{}, tcp_extra(nullptr), policy(http_client_policy_from_options(http_options)), origin{} { if (extra) log_opts = *extra; - const SocketExtraOptions* tcp_ptr = extra; if (extra && (log_opts.flags & SOCK_OPT_DEBUG)) { tcp_extra = (SocketExtraOptions*)zalloc(sizeof(SocketExtraOptions)); if (tcp_extra) { *tcp_extra = *extra; tcp_extra->flags &= ~SOCK_OPT_DEBUG; - tcp_ptr = tcp_extra; } } - sock = socket_handle_t{}; - create_socket(SOCKET_CLIENT, PROTO_TCP, tcp_ptr, &sock); + sock = 0; } ~HTTPClient() {close();} @@ -330,7 +326,9 @@ class HTTPClient { } int32_t connect_endpoint(const net_l4_endpoint* dst, const char* host_name = nullptr) { - int32_t r = ((sock).id && (sock).protocol != PROTO_NONE) ? connect_socket(&sock, dst) : SOCK_ERR_STATE; + if (!sock) sock = create_socket(PROTO_TCP, tcp_extra ? tcp_extra : &log_opts); + if (!sock) return SOCK_ERR_SYS; + int32_t r = connect_socket(sock, dst); if (r >= 0) { HTTPClientOrigin next{}; next.valid = true; @@ -343,14 +341,14 @@ class HTTPClient { netlog_socket_event_t ev{}; ev.comp = NETLOG_COMP_HTTP_CLIENT; ev.action = NETLOG_ACT_CONNECT; - ev.pid = pid; + ev.pid = get_current_proc_pid(); if (dst) ev.dst_ep = *dst; ev.i0 = r; - if (((sock).id && (sock).protocol != PROTO_NONE)) { - ev.local_port = get_socket_local_port(&sock); + if (sock) { + ev.local_port = get_socket_local_port(sock); net_l4_endpoint sock_remote_ep{}; - get_socket_remote_endpoint(&sock, &sock_remote_ep); + get_socket_remote_endpoint(sock, &sock_remote_ep); ev.remote_ep = sock_remote_ep; if (ev.remote_ep.ver) ev.dst_ep = ev.remote_ep; } @@ -376,7 +374,7 @@ class HTTPClient { HTTPResponseMsg resp{}; uint32_t redirects = policy.follow_redirects ? policy.max_redirects : 0; for (uint32_t i = 0;; i++) { - if (!((sock).id && (sock).protocol != PROTO_NONE)) { + if (!sock) { resp = error_response(SOCK_ERR_STATE); break; } @@ -414,7 +412,7 @@ class HTTPClient { uint32_t off = 0; int64_t sent = 0; while (off < out_len) { - int64_t r = send_on_socket(&sock, (void*)(out.data + off), out_len - off); + int64_t r = send_on_socket(sock, (void*)(out.data + off), out_len - off); if (r == TCP_WOULDBLOCK) { msleep(5); continue; @@ -430,12 +428,12 @@ class HTTPClient { netlog_socket_event_t ev{}; ev.comp = NETLOG_COMP_HTTP_CLIENT; ev.action = NETLOG_ACT_HTTP_SEND_REQUEST; - ev.pid = pid; + ev.pid = get_current_proc_pid(); ev.u0 = out_len; ev.i0 = sent; - ev.local_port = get_socket_local_port(&sock); + ev.local_port = get_socket_local_port(sock); net_l4_endpoint sock_remote_ep{}; - get_socket_remote_endpoint(&sock, &sock_remote_ep); + get_socket_remote_endpoint(sock, &sock_remote_ep); ev.remote_ep = sock_remote_ep; char pathbuf[128]; @@ -485,11 +483,12 @@ class HTTPClient { curr.headers_common.fields.host = string_repeat('\0', 0); } - if (((sock).id && (sock).protocol != PROTO_NONE)) { - close_socket(&sock); - sock = socket_handle_t{}; + if (sock) { + close_socket(sock); + sock = 0; } - int32_t rr = next_domain.data && create_socket(SOCKET_CLIENT, PROTO_TCP, tcp_extra ? tcp_extra : &log_opts, &sock) ? SOCK_OK : SOCK_ERR_SYS; + sock = next_domain.data ? create_socket(PROTO_TCP, tcp_extra ? tcp_extra : &log_opts) : 0; + int32_t rr = sock ? SOCK_OK : SOCK_ERR_SYS; if (rr >= 0) rr = connect_domain(next_domain.data, next_port); if (next_domain.mem_length) string_free(next_domain); if (rr < 0) { @@ -502,11 +501,12 @@ class HTTPClient { string reconnect_domain = string{}; if (origin.domain.data) reconnect_domain = string_from_literal(origin.domain.data); - if (((sock).id && (sock).protocol != PROTO_NONE)) { - close_socket(&sock); - sock = socket_handle_t{}; + if (sock) { + close_socket(sock); + sock = 0; } - int32_t rr = origin.valid && create_socket(SOCKET_CLIENT, PROTO_TCP, tcp_extra ? tcp_extra : &log_opts, &sock) ? SOCK_OK : SOCK_ERR_SYS; + sock = origin.valid ? create_socket(PROTO_TCP, tcp_extra ? tcp_extra : &log_opts) : 0; + int32_t rr = sock ? SOCK_OK : SOCK_ERR_SYS; if (rr >= 0) rr = reconnect_domain.data ? connect_domain(reconnect_domain.data, reconnect_ep.port) : connect_endpoint(&reconnect_ep); if (reconnect_domain.mem_length) string_free(reconnect_domain); if (rr < 0) { @@ -536,19 +536,19 @@ class HTTPClient { netlog_socket_event_t ev{}; ev.comp = NETLOG_COMP_HTTP_CLIENT; ev.action = NETLOG_ACT_CLOSE; - ev.pid = pid; + ev.pid = get_current_proc_pid(); - if (((sock).id && (sock).protocol != PROTO_NONE)) { - ev.local_port = get_socket_local_port(&sock); + if (sock) { + ev.local_port = get_socket_local_port(sock); net_l4_endpoint sock_remote_ep{}; - get_socket_remote_endpoint(&sock, &sock_remote_ep); + get_socket_remote_endpoint(sock, &sock_remote_ep); ev.remote_ep = sock_remote_ep; } int32_t r = SOCK_ERR_STATE; - if (((sock).id && (sock).protocol != PROTO_NONE)) { - r = close_socket(&sock); - sock = socket_handle_t{}; + if (sock) { + r = close_socket(sock); + sock = 0; } ev.i0 = r; netlog_socket_event(&log_opts, &ev); diff --git a/kernel/networking/application_layer/socket_http_server.hpp b/kernel/networking/application_layer/socket_http_server.hpp index 4d110af9..799a38f6 100644 --- a/kernel/networking/application_layer/socket_http_server.hpp +++ b/kernel/networking/application_layer/socket_http_server.hpp @@ -11,7 +11,6 @@ struct HTTPConnection { socket_handle_t client; - uint16_t pid; string carry_buf; uint32_t request_count; HTTPMethod current_method; @@ -21,28 +20,24 @@ struct HTTPConnection { class HTTPServer { private: - uint16_t pid; socket_handle_t sock; SocketExtraOptions log_opts; SocketExtraOptions* tcp_extra; HTTPServerPolicy policy; public: - explicit HTTPServer(const SocketExtraOptions* extra, const HTTPServerPolicyOptions* http_options) : pid((uint16_t)get_current_proc_pid()), sock(socket_handle_t{}), log_opts{}, tcp_extra(nullptr), policy(http_server_policy_from_options(http_options)) { + explicit HTTPServer(const SocketExtraOptions* extra, const HTTPServerPolicyOptions* http_options) : sock(0), log_opts{}, tcp_extra(nullptr), policy(http_server_policy_from_options(http_options)) { if (extra) log_opts = *extra; - const SocketExtraOptions* tcp_ptr = extra; if (extra && (log_opts.flags & SOCK_OPT_DEBUG)) { tcp_extra = (SocketExtraOptions*)zalloc(sizeof(SocketExtraOptions)); if (tcp_extra) { *tcp_extra = *extra; tcp_extra->flags &= ~SOCK_OPT_DEBUG; - tcp_ptr = tcp_extra; } } - sock = socket_handle_t{}; - create_socket(SOCKET_SERVER, PROTO_TCP, tcp_ptr, &sock); + sock = 0; } ~HTTPServer() { close(); } @@ -54,12 +49,14 @@ class HTTPServer { int32_t bind(const SockBindSpec& spec, uint16_t port) { uint16_t p = port; - int32_t r = ((sock).id && (sock).protocol != PROTO_NONE) ? bind_socket_spec(&sock, &spec, p) : SOCK_ERR_STATE; + if (!sock) sock = create_socket(PROTO_TCP, tcp_extra ? tcp_extra : &log_opts); + if (!sock) return SOCK_ERR_SYS; + int32_t r = bind_socket(sock, &spec, p); netlog_socket_event_t ev{}; ev.comp = NETLOG_COMP_HTTP_SERVER; ev.action = NETLOG_ACT_BIND; - ev.pid = pid; + ev.pid = get_current_proc_pid(); ev.u0 = p; ev.i0 = r; netlog_socket_event(&log_opts, &ev); @@ -68,12 +65,12 @@ class HTTPServer { int32_t listen(int backlog = 4) { int b = backlog; - int32_t r = ((sock).id && (sock).protocol != PROTO_NONE) ? listen_on(&sock, b) : SOCK_ERR_STATE; + int32_t r = sock ? listen_on(sock, b) : SOCK_ERR_STATE; netlog_socket_event_t ev{}; ev.comp = NETLOG_COMP_HTTP_SERVER; ev.action = NETLOG_ACT_LISTEN; - ev.pid = pid; + ev.pid = get_current_proc_pid(); ev.u0 = (uint32_t)b; ev.i0 = r; netlog_socket_event(&log_opts, &ev); @@ -81,17 +78,17 @@ class HTTPServer { } HTTPConnection* accept() { - socket_handle_t child = socket_handle_t{}; - if (!((sock).id && (sock).protocol != PROTO_NONE) || !accept_on_socket(&sock, &child)) return nullptr; + if (!sock) return nullptr; + socket_handle_t child = accept_on_socket(sock); + if (!child) return nullptr; HTTPConnection* conn = (HTTPConnection*)zalloc(sizeof(HTTPConnection)); if (!conn) { - close_socket(&child); + close_socket(child); return nullptr; } conn->client = child; - conn->pid = pid; conn->carry_buf = string{nullptr, 0, 0}; conn->request_count = 0; conn->current_method = HTTP_METHOD_GET; @@ -101,11 +98,11 @@ class HTTPServer { netlog_socket_event_t ev{}; ev.comp = NETLOG_COMP_HTTP_SERVER; ev.action = NETLOG_ACT_ACCEPT; - ev.pid = pid; - ev.i0 = (int64_t)child.id; - ev.local_port = get_socket_local_port(&child); + ev.pid = get_current_proc_pid(); + ev.i0 = (int64_t)child; + ev.local_port = get_socket_local_port(child); net_l4_endpoint child_remote_ep{}; - get_socket_remote_endpoint(&child, &child_remote_ep); + get_socket_remote_endpoint(child, &child_remote_ep); ev.remote_ep = child_remote_ep; netlog_socket_event(&log_opts, &ev); return conn; @@ -113,7 +110,7 @@ class HTTPServer { HTTPRequestMsg recv_request(HTTPConnection* conn) { HTTPRequestMsg req{}; - if (!conn || !((conn->client).id && (conn->client).protocol != PROTO_NONE)) return req; + if (!conn || !conn->client) return req; string buf = string_repeat('\0', 0); if (conn->carry_buf.length) { @@ -132,7 +129,7 @@ class HTTPServer { char* body_copy = nullptr; while (hdr_end < 0) { - int64_t r = receive_from_socket(&conn->client, tmp, sizeof(tmp), nullptr); + int64_t r = receive_from_socket(conn->client, tmp, sizeof(tmp), nullptr); if (r == TCP_WOULDBLOCK) { uint32_t now = (uint32_t)get_time(); if ((uint32_t)(now - last_rx_ms) > policy.common.header_idle_timeout_ms || (uint32_t)(now - start_ms) > policy.common.header_total_timeout_ms) { @@ -272,7 +269,7 @@ class HTTPServer { consumed = body_start + used; while (chunk_result == HTTP_PARSE_INCOMPLETE) { - int64_t r = receive_from_socket(&conn->client, tmp, sizeof(tmp), nullptr); + int64_t r = receive_from_socket(conn->client, tmp, sizeof(tmp), nullptr); if (r == TCP_WOULDBLOCK) { uint32_t now = (uint32_t)get_time(); if ((now - body_last_rx_ms) > policy.common.body_idle_timeout_ms || (now - body_start_ms) > policy.common.body_total_timeout_ms) break; @@ -320,7 +317,7 @@ class HTTPServer { uint32_t body_start_ms = (uint32_t)get_time(); uint32_t body_last_rx_ms = body_start_ms; while (copied < need) { - int64_t r = receive_from_socket(&conn->client, body_copy + copied, need - copied, nullptr); + int64_t r = receive_from_socket(conn->client, body_copy + copied, need - copied, nullptr); if (r == TCP_WOULDBLOCK) { uint32_t now = (uint32_t)get_time(); if ((now - body_last_rx_ms) > policy.common.body_idle_timeout_ms || (now - body_start_ms) > policy.common.body_total_timeout_ms) { @@ -375,13 +372,13 @@ class HTTPServer { netlog_socket_event_t ev{}; ev.comp = NETLOG_COMP_HTTP_SERVER; ev.action = NETLOG_ACT_HTTP_RECV_REQUEST; - ev.pid = pid; + ev.pid = get_current_proc_pid(); ev.u0 = (uint32_t)req.method; ev.u1 = (uint32_t)req.path.length; ev.i0 = (int64_t)req.body.size; - ev.local_port = get_socket_local_port(&conn->client); + ev.local_port = get_socket_local_port(conn->client); net_l4_endpoint conn_remote_ep{}; - get_socket_remote_endpoint(&conn->client, &conn_remote_ep); + get_socket_remote_endpoint(conn->client, &conn_remote_ep); ev.remote_ep = conn_remote_ep; char pathbuf[128]; @@ -405,7 +402,7 @@ class HTTPServer { } int32_t send_response(HTTPConnection* conn, const HTTPResponseMsg& res) { - if (!conn || !((conn->client).id && (conn->client).protocol != PROTO_NONE)) return SOCK_ERR_STATE; + if (!conn || !conn->client) return SOCK_ERR_STATE; uint32_t code = (uint32_t)res.status_code; bool informational = code >= 100 && code < 200; @@ -469,7 +466,7 @@ class HTTPServer { uint32_t off = 0; while (sent >= 0 && off < first_len) { - int64_t r = send_on_socket(&conn->client, (void*)(first_ptr + off), first_len - off); + int64_t r = send_on_socket(conn->client, (void*)(first_ptr + off), first_len - off); uint32_t now = (uint32_t)get_time(); if (r == TCP_WOULDBLOCK || r == 0) { @@ -495,7 +492,7 @@ class HTTPServer { while (body_off < body_len) { uint32_t ask = body_len - body_off; if (ask > 16384) ask = 16384; - int64_t r = send_on_socket(&conn->client, (void*)(body + body_off), ask); + int64_t r = send_on_socket(conn->client, (void*)(body + body_off), ask); uint32_t now = (uint32_t)get_time(); if (r == TCP_WOULDBLOCK || r == 0) { @@ -521,42 +518,42 @@ class HTTPServer { netlog_socket_event_t ev{}; ev.comp = NETLOG_COMP_HTTP_SERVER; ev.action = NETLOG_ACT_HTTP_SEND_RESPONSE; - ev.pid = pid; + ev.pid = get_current_proc_pid(); ev.u0 = code; ev.u1 = out_len; ev.i0 = sent; - ev.local_port = get_socket_local_port(&conn->client); + ev.local_port = get_socket_local_port(conn->client); net_l4_endpoint conn_remote_ep{}; - get_socket_remote_endpoint(&conn->client, &conn_remote_ep); + get_socket_remote_endpoint(conn->client, &conn_remote_ep); ev.remote_ep = conn_remote_ep; netlog_socket_event(&log_opts, &ev); string_free(out); - if (sent >= 0 && close_after_send) if (conn && ((conn->client).id && (conn->client).protocol != PROTO_NONE)) { - close_socket(&conn->client); - conn->client = socket_handle_t{}; + if (sent >= 0 && close_after_send) if (conn && conn->client) { + close_socket(conn->client); + conn->client = 0; } return sent < 0 ? (int32_t)sent : SOCK_OK; } int32_t close() { - int32_t r = ((sock).id && (sock).protocol != PROTO_NONE) ? SOCK_OK : SOCK_ERR_STATE; + int32_t r = sock ? SOCK_OK : SOCK_ERR_STATE; netlog_socket_event_t ev{}; ev.comp = NETLOG_COMP_HTTP_SERVER; ev.action = NETLOG_ACT_CLOSE; - ev.pid = pid; + ev.pid = get_current_proc_pid(); ev.i0 = r; - if (((sock).id && (sock).protocol != PROTO_NONE)) { - ev.local_port = get_socket_local_port(&sock); + if (sock) { + ev.local_port = get_socket_local_port(sock); net_l4_endpoint sock_remote_ep{}; - get_socket_remote_endpoint(&sock, &sock_remote_ep); + get_socket_remote_endpoint(sock, &sock_remote_ep); ev.remote_ep = sock_remote_ep; } netlog_socket_event(&log_opts, &ev); - if (((sock).id && (sock).protocol != PROTO_NONE)) { - r = close_socket(&sock); - sock = socket_handle_t{}; + if (sock) { + r = close_socket(sock); + sock = 0; } if (tcp_extra) release(tcp_extra); diff --git a/kernel/networking/application_layer/ssdp_daemon.c b/kernel/networking/application_layer/ssdp_daemon.c index 4c50a111..e91deb74 100644 --- a/kernel/networking/application_layer/ssdp_daemon.c +++ b/kernel/networking/application_layer/ssdp_daemon.c @@ -5,11 +5,12 @@ #include "std/string.h" #include "syscalls/syscalls.h" #include "net/network_types.h" +#include "net/socket_types.h" #include "networking/transport_layer/csocket.h" #include "networking/application_layer/ssdp.h" #include "networking/internet_layer/ipv6_utils.h" +#include "networking/interface_manager.h" #include "math/math.h" -#include "networking/transport_layer/trans_utils.h" //at the moment it's a very basic version. it's a protocol still in use but only in few cases //it;s used in some printers, upnp, local video streaming and various other things @@ -17,15 +18,24 @@ typedef struct { uint8_t used; + socket_handle_t sock; uint32_t due_ms; net_l4_endpoint dst; } ssdp_pending_t; +typedef struct { + socket_handle_t sock; + ip_version_t ver; + uint8_t mcast_ip[16]; +} ssdp_socket_entry_t; + static rng_t ssdp_rng; static uint32_t ssdp_uptime_ms = 0; static uint32_t ssdp_host_v4 = IPV4_MCAST_SSDP; static uint8_t ssdp_host_v6[16]; +static ssdp_socket_entry_t ssdp_sockets[MAX_L3_INTERFACES]; +static uint8_t ssdp_socket_count = 0; #define SSDP_MAX_PENDING 64 #define SSDP_RATE_WINDOW_MS 1000 @@ -37,11 +47,12 @@ static ssdp_pending_t ssdp_pending[SSDP_MAX_PENDING]; static uint32_t ssdp_rate_window_ms = 0; static uint32_t ssdp_rate_count = 0; -static void ssdp_schedule_response(const net_l4_endpoint* src, uint32_t mx_ms) { - if (!src) return; +static void ssdp_schedule_response(socket_handle_t sock, const net_l4_endpoint* src, uint32_t mx_ms) { + if (!sock || !src) return; for (int i = 0; i < SSDP_MAX_PENDING; ++i) { if (!ssdp_pending[i].used) { ssdp_pending[i].used = 1; + ssdp_pending[i].sock = sock; ssdp_pending[i].dst = *src; ssdp_pending[i].due_ms = ssdp_uptime_ms + rng_between32(&ssdp_rng, 0, mx_ms); return; @@ -49,22 +60,19 @@ static void ssdp_schedule_response(const net_l4_endpoint* src, uint32_t mx_ms) { } } -static void ssdp_send_notify(socket_handle_t s4, socket_handle_t s6, bool alive) { - if (((s4).id && (s4).protocol != PROTO_NONE)) { - string msg = ssdp_build_notify(alive, false); - net_l4_endpoint dst; - make_ep(ssdp_host_v4, 1900, IP_VER4, &dst); - (void)send_to_socket(&s4, &dst, (void*)msg.data, msg.length); - string_free(msg); - } +static void ssdp_send_notify(bool alive) { + for (uint8_t i = 0; i < ssdp_socket_count; i++) { + socket_handle_t s = ssdp_sockets[i].sock; + if (!s) continue; - if (((s6).id && (s6).protocol != PROTO_NONE)) { - string msg = ssdp_build_notify(alive, true); - net_l4_endpoint dst = (net_l4_endpoint){0}; - dst.ver = IP_VER6; - memcpy(dst.ip, ssdp_host_v6, 16); + string msg = ssdp_build_notify(alive, ssdp_sockets[i].ver == IP_VER6); + net_l4_endpoint dst; + memset(&dst, 0, sizeof(dst)); + dst.ver = ssdp_sockets[i].ver; dst.port = 1900; - (void)send_to_socket(&s6, &dst, (void*)msg.data, msg.length); + if (dst.ver == IP_VER4) memcpy(dst.ip, &ssdp_host_v4, 4); + else memcpy(dst.ip, ssdp_host_v6, 16); + (void)send_to_socket(s, &dst, (void*)msg.data, msg.length); string_free(msg); } } @@ -78,35 +86,91 @@ int ssdp_daemon_entry(int argc, char* argv[]) { ipv6_make_multicast(0x02, IPV6_MCAST_SSDP, NULL, ssdp_host_v6); - SocketExtraOptions opt = (SocketExtraOptions){0}; - opt.flags = SOCK_OPT_MCAST_JOIN; - opt.mcast_count = 2; - opt.mcast_groups[0].ver = IP_VER4; - opt.mcast_groups[0].port = 1900; - memcpy(opt.mcast_groups[0].ip, &ssdp_host_v4, 4); - opt.mcast_groups[1].ver = IP_VER6; - opt.mcast_groups[1].port = 1900; - memcpy(opt.mcast_groups[1].ip, ssdp_host_v6, 16); - socket_handle_t s = {0}; - create_socket(SOCKET_SERVER, PROTO_UDP, &opt,&s); - - struct SockBindSpec spec = (struct SockBindSpec){0}; - spec.kind = BIND_ANY; - if (((s).id && (s).protocol != PROTO_NONE) && bind_socket_spec(&s, &spec, 1900) < 0) { - close_socket(&s); - s = ((socket_handle_t){0}); - } + uint8_t n_if = l2_interface_count(); + for (uint8_t i = 0; i < n_if && ssdp_socket_count < MAX_L3_INTERFACES; 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 && ssdp_socket_count < MAX_L3_INTERFACES; j++) { + l3_ipv4_interface_t *v4 = l2->l3_v4[j]; + if (!v4 || v4->mode == IPV4_CFG_DISABLED || v4->is_localhost || !v4->ip) continue; + + uint32_t ttl = 2; + SocketExtraOptions opt; + memset(&opt, 0, sizeof(opt)); + opt.flags = SOCK_OPT_TTL; + opt.ttl = (uint8_t)ttl; + + socket_handle_t s = create_socket(PROTO_UDP, &opt); + if (!s) continue; + + SockBindSpec spec; + memset(&spec, 0, sizeof(spec)); + spec.kind = BIND_L3; + spec.ver = IP_VER4; + spec.l3_id = v4->l3_id; + + net_l4_endpoint group; + memset(&group, 0, sizeof(group)); + group.ver = IP_VER4; + group.port = 1900; + memcpy(group.ip, &ssdp_host_v4, 4); + + if (bind_socket(s, &spec, 1900) != SOCK_OK || set_socket_option(s, SOCK_OPT_MCAST_JOIN, &group, sizeof(group)) != SOCK_OK) { + close_socket(s); + continue; + } + + ssdp_sockets[ssdp_socket_count].sock = s; + ssdp_sockets[ssdp_socket_count].ver = IP_VER4; + memcpy(ssdp_sockets[ssdp_socket_count].mcast_ip, &ssdp_host_v4, 4); + ssdp_socket_count++; + } - socket_handle_t s4 = s; - socket_handle_t s6 = s; + for (uint8_t j = 0; j < MAX_IPV6_PER_INTERFACE && ssdp_socket_count < MAX_L3_INTERFACES; j++) { + l3_ipv6_interface_t *v6 = l2->l3_v6[j]; + if (!v6 || v6->cfg == IPV6_CFG_DISABLE || v6->dad_state != IPV6_DAD_OK || v6->is_localhost || !v6->ip[0]) continue; + + uint32_t ttl = 2; + SocketExtraOptions opt; + memset(&opt, 0, sizeof(opt)); + opt.flags = SOCK_OPT_TTL; + opt.ttl = (uint8_t)ttl; + + socket_handle_t s = create_socket(PROTO_UDP, &opt); + if (!s) continue; + + SockBindSpec spec; + memset(&spec, 0, sizeof(spec)); + spec.kind = BIND_L3; + spec.ver = IP_VER6; + spec.l3_id = v6->l3_id; + + net_l4_endpoint group; + memset(&group, 0, sizeof(group)); + group.ver = IP_VER6; + group.port = 1900; + memcpy(group.ip, ssdp_host_v6, 16); + + if (bind_socket(s, &spec, 1900) != SOCK_OK || set_socket_option(s, SOCK_OPT_MCAST_JOIN, &group, sizeof(group)) != SOCK_OK) { + close_socket(s); + continue; + } + + ssdp_sockets[ssdp_socket_count].sock = s; + ssdp_sockets[ssdp_socket_count].ver = IP_VER6; + memcpy(ssdp_sockets[ssdp_socket_count].mcast_ip, ssdp_host_v6, 16); + ssdp_socket_count++; + } + } - if (!((s4).id && (s4).protocol != PROTO_NONE) && !((s6).id && (s6).protocol != PROTO_NONE)) return 1; + if (!ssdp_socket_count) return 1; - ssdp_send_notify(s4, s6, true); + ssdp_send_notify(true); msleep(100); - ssdp_send_notify(s4, s6, true); + ssdp_send_notify(true); msleep(100); - ssdp_send_notify(s4, s6, true); + ssdp_send_notify(true); uint32_t notify_ms = 0; const uint32_t tick_ms = 50; @@ -115,18 +179,21 @@ int ssdp_daemon_entry(int argc, char* argv[]) { notify_ms += tick_ms; if (notify_ms >= SSDP_NOTIFY_INTERVAL_MS) { notify_ms = 0; - ssdp_send_notify(s4, s6, true); + ssdp_send_notify(true); } char buf[2048]; net_l4_endpoint src = (net_l4_endpoint){0}; - if (((s).id && (s).protocol != PROTO_NONE)) { + for (uint8_t sidx = 0; sidx < ssdp_socket_count; sidx++) { + socket_handle_t s = ssdp_sockets[sidx].sock; for (int i = 0; i < SSDP_RECV_BURST; ++i) { - int64_t r = receive_from_socket(&s, buf, sizeof(buf) - 1, &src); - if (r <= 0) break; + int64_t r = receive_from_socket(s, buf, sizeof(buf) - 1, &src); + if (r == SOCK_ERR_WOULDBLOCK) break; + if (r < 0) break; + if (!r) continue; buf[r] = 0; - if (ssdp_is_msearch(buf, (int)r)) ssdp_schedule_response(&src, ssdp_parse_mx_ms(buf, (int)r)); + if (ssdp_is_msearch(buf, (int)r)) ssdp_schedule_response(s, &src, ssdp_parse_mx_ms(buf, (int)r)); } } @@ -146,8 +213,7 @@ int ssdp_daemon_entry(int argc, char* argv[]) { ssdp_pending[i].used = 0; string resp = ssdp_build_search_response(); - socket_handle_t sock = (ssdp_pending[i].dst.ver == IP_VER6) ? s6 : s4; - if (((sock).id && (sock).protocol != PROTO_NONE)) (void)send_to_socket(&sock, &ssdp_pending[i].dst, (void*)resp.data, resp.length); + if (ssdp_pending[i].sock) (void)send_to_socket(ssdp_pending[i].sock, &ssdp_pending[i].dst, (void*)resp.data, resp.length); string_free(resp); break; } diff --git a/kernel/networking/interface_manager.c b/kernel/networking/interface_manager.c index cfb01566..3f51565e 100644 --- a/kernel/networking/interface_manager.c +++ b/kernel/networking/interface_manager.c @@ -29,11 +29,8 @@ typedef struct { uint8_t slot_in_l2; } v6_slot_t; -#define V4_POOL_SIZE (MAX_L2_INTERFACES * MAX_IPV4_PER_INTERFACE) -#define V6_POOL_SIZE (MAX_L2_INTERFACES * MAX_IPV6_PER_INTERFACE) - -static v4_slot_t g_v4[V4_POOL_SIZE]; -static v6_slot_t g_v6[V6_POOL_SIZE]; +static v4_slot_t g_v4[MAX_IPV4_L3_INTERFACES]; +static v6_slot_t g_v6[MAX_IPV6_L3_INTERFACES]; static inline int l2_slot_from_ifindex(uint8_t ifindex){ if (!ifindex) return -1; @@ -44,7 +41,7 @@ static inline int l2_slot_from_ifindex(uint8_t ifindex){ } static bool v4_has_dhcp_on_l2(uint8_t ifindex){ - for (int i = 0; i < V4_POOL_SIZE; i++){ + for (int i = 0; i < MAX_IPV4_L3_INTERFACES; i++){ if (!g_v4[i].used) continue; l3_ipv4_interface_t *x = &g_v4[i].node; if (!x->l2) continue; @@ -284,13 +281,13 @@ bool l2_ipv6_mcast_leave(uint8_t ifindex, const uint8_t group[16]) { } static bool v4_ip_exists_anywhere(uint32_t ip){ - for (int i=0;il2 || x->l2->ifindex != ifindex) continue; @@ -303,7 +300,7 @@ static bool v4_overlap_intra_l2(uint8_t ifindex, uint32_t ip, uint32_t mask){ 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; @@ -359,7 +356,7 @@ uint8_t l3_ipv4_add_to_interface(uint8_t ifindex, uint32_t ip, uint32_t mask, ui break; } int g = -1; - for (int i=0;iip && v4_ip_exists_anywhere(ip)) return false; - for (int i = 0; i < V4_POOL_SIZE; i++){ + for (int i = 0; i < MAX_IPV4_L3_INTERFACES; i++){ if (!g_v4[i].used) continue; l3_ipv4_interface_t *x = &g_v4[i].node; if (x==n) continue; @@ -472,7 +469,7 @@ bool l3_ipv4_remove_from_interface(uint8_t l3_id){ if (l2->ipv4_count <= 1) return false; int g = -1; - for (int i=0;il3_v4[loc]; } l3_ipv4_interface_t* l3_ipv4_find_by_ip(uint32_t ip){ - 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; @@ -562,7 +559,7 @@ uint8_t l3_ipv6_add_to_interface(uint8_t ifindex, const uint8_t ip[16], uint8_t } if (!is_loop){ bool has_lla=false; - for (int i=0;il2 || x->l2->ifindex != ifindex) continue; @@ -583,7 +580,7 @@ uint8_t l3_ipv6_add_to_interface(uint8_t ifindex, const uint8_t ip[16], uint8_t } int g = -1; - for (int i=0;ikind && cfg == n->cfg && prefix_len == n->prefix_len && ipv6_cmp(ip, n->ip) == 0 && ipv6_cmp(gw, n->gateway) == 0) return true; if ((n->kind & IPV6_ADDRK_LINK_LOCAL) && cfg == IPV6_CFG_DISABLE){ - for (int i=0;il2 || x->l2->ifindex != l2->ifindex) continue; @@ -684,7 +681,7 @@ bool l3_ipv6_update(uint8_t l3_id, const uint8_t ip[16], uint8_t prefix_len, con if (!ipv6_is_linklocal(ip)) return false; } if (!ipv6_is_unspecified(ip) && ipv6_cmp(ip, n->ip)!=0 && v6_ip_exists_anywhere(ip)) return false; - for (int i=0;ikind != NET_IFK_LOCALHOST)) return false; if (ipv6_cmp(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; if (!l2) return false; if ((n->kind & IPV6_ADDRK_LINK_LOCAL)){ - for (int i=0;il2 || x->l2->ifindex != l2->ifindex) continue; @@ -822,7 +819,7 @@ bool l3_ipv6_remove_from_interface(uint8_t l3_id){ if (l2->ipv6_count <= 1) return false; int g = -1; - for (int i=0;ikind & IPV6_ADDRK_LINK_LOCAL)){ l2_interface_t *l2 = n->l2; - for (int i=0;il2 || x->l2->ifindex != l2->ifindex) continue; @@ -877,7 +874,7 @@ l3_ipv6_interface_t* l3_ipv6_find_by_id(uint8_t l3_id){ return l2->l3_v6[loc]; } l3_ipv6_interface_t* l3_ipv6_find_by_ip(const uint8_t ip[16]){ - for (int i=0;il2) continue; @@ -1017,7 +1014,7 @@ ip_resolution_result_t resolve_ipv6_to_interface(const uint8_t dst_ip[16]) { int best_pl = -1; uint16_t best_cost = 0x7FFF; - for (int i = 0; i < V6_POOL_SIZE; i++) { + for (int i = 0; i < MAX_IPV6_L3_INTERFACES; i++) { if (!g_v6[i].used) continue; l3_ipv6_interface_t *x = &g_v6[i].node; if (!x->l2) continue; diff --git a/kernel/networking/interface_manager.h b/kernel/networking/interface_manager.h index 5eaf2b98..b6474a11 100644 --- a/kernel/networking/interface_manager.h +++ b/kernel/networking/interface_manager.h @@ -9,6 +9,9 @@ extern "C" { #define MAX_L2_INTERFACES 16 #define MAX_IPV4_PER_INTERFACE 4 #define MAX_IPV6_PER_INTERFACE 4 +#define MAX_L3_INTERFACES (MAX_L2_INTERFACES * (MAX_IPV4_PER_INTERFACE + MAX_IPV6_PER_INTERFACE)) +#define MAX_IPV4_L3_INTERFACES (MAX_L2_INTERFACES * MAX_IPV4_PER_INTERFACE) +#define MAX_IPV6_L3_INTERFACES (MAX_L2_INTERFACES * MAX_IPV6_PER_INTERFACE) #define MAX_IPV4_MCAST_PER_INTERFACE 16 #define MAX_IPV6_MCAST_PER_INTERFACE 32 diff --git a/kernel/networking/internet_layer/icmpv6.c b/kernel/networking/internet_layer/icmpv6.c index 620a89f6..8a107769 100644 --- a/kernel/networking/internet_layer/icmpv6.c +++ b/kernel/networking/internet_layer/icmpv6.c @@ -102,7 +102,7 @@ static bool icmpv6_send_echo_reply(uint16_t ifindex, const uint8_t src_ip[16], c e->hdr.checksum = 0; ipv6_tx_plan_t plan; - if (!ipv6_build_tx_plan(dst_ip, 0 ,0, 0, &plan)) { + if (!ipv6_build_tx_plan(dst_ip, 0, &plan)) { release((void*)buf); return false; } @@ -139,7 +139,7 @@ static bool icmpv6_send_echo_request(const uint8_t dst_ip[16], uint16_t id, uint if (payload_len) memcpy((void*)((uintptr_t)buf + sizeof(icmpv6_echo_t)), payload, payload_len); ipv6_tx_plan_t plan; - if (!ipv6_build_tx_plan(dst_ip, tx_opts_or_null, 0, 0, &plan)) { + if (!ipv6_build_tx_plan(dst_ip, tx_opts_or_null, &plan)) { netpkt_unref(pkt); return false; } diff --git a/kernel/networking/internet_layer/ipv4.c b/kernel/networking/internet_layer/ipv4.c index 74f20c93..8234b093 100644 --- a/kernel/networking/internet_layer/ipv4.c +++ b/kernel/networking/internet_layer/ipv4.c @@ -88,58 +88,8 @@ static bool pick_broadcast_bound_l3(uint8_t l3_id, uint8_t* out_ifx, uint32_t* o 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 (ipv4_is_limited_broadcast(dst)) return pick_broadcast_global(out_ifx, out_src, out_nh); + if (ipv4_is_limited_broadcast(dst)) return false; ip_resolution_result_t r = resolve_ipv4_to_interface(dst); if (r.found && r.ipv4 && r.l2) { @@ -203,7 +153,7 @@ static bool pick_route_bound_l3(uint8_t l3_id, uint32_t dst, uint8_t* out_ifx, u } 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 (ipv4_is_limited_broadcast(dst)) return pick_broadcast_bound_l2(ifindex, out_ifx, out_src, out_nh); + if (ipv4_is_limited_broadcast(dst)) return false; l2_interface_t* l2 = l2_interface_find_by_index(ifindex); if (!l2) return false; @@ -269,11 +219,14 @@ bool ipv4_send_packet(uint32_t dst_ip, uint8_t proto, netpkt_t* pkt, const ipv4_ 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 (v4->mask && ipv4_broadcast_calc(v4->ip, v4->mask) == dst_ip) { is_dbcast = true; break; } + if (v4->mask && ipv4_broadcast_calc(v4->ip, v4->mask) == dst_ip) { + is_dbcast = true; + break; + } } } - if (is_dbcast) memset(dst_mac, 0xFF, 6); + if (ipv4_is_limited_broadcast(dst_ip) || is_dbcast) memset(dst_mac, 0xFF, 6); else if (ipv4_is_multicast(dst_ip)) ipv4_mcast_to_mac(dst_ip, dst_mac); else if (l2 && l2->kind == NET_IFK_LOCALHOST) memset(dst_mac, 0, 6); else need_arp = true; @@ -516,37 +469,31 @@ void ipv4_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { return; } - 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 (v4->mask && ipv4_broadcast_calc(v4->ip, v4->mask) == dst) { - any_dbcast = 1; - uint8_t l3id = v4->l3_id; - switch (proto) { - netpkt_t* l4pkt; - case PROTO_ICMP: - l4pkt = netpkt_view(pkt, hdr_len, l4_len); - if (l4pkt) icmp_input(l4pkt, src, dst); - break; - case PROTO_TCP: - l4pkt = netpkt_view(pkt, hdr_len, l4_len); - if (l4pkt) tcp_input(IP_VER4, &src, &dst, l3id, l4pkt); - break; - case PROTO_UDP: - l4pkt = netpkt_view(pkt, hdr_len, l4_len); - if (l4pkt) udp_input(IP_VER4, &src, &dst, l3id, l4pkt); - break; - default: break; - } - } + for (int i = 0; i < ccount; ++i) { + l3_ipv4_interface_t* v4 = cand[i]; + if (!v4 || !v4->ip || !v4->mask) continue; + if (v4->is_localhost) continue; + if (ipv4_broadcast_calc(v4->ip, v4->mask) != dst) continue; + uint8_t l3id = v4->l3_id; + switch (proto) { + netpkt_t* l4pkt; + case PROTO_ICMP: + l4pkt = netpkt_view(pkt, hdr_len, l4_len); + if (l4pkt) icmp_input(l4pkt, src, dst); + break; + case PROTO_TCP: + l4pkt = netpkt_view(pkt, hdr_len, l4_len); + if (l4pkt) tcp_input(IP_VER4, &src, &dst, l3id, l4pkt); + break; + case PROTO_UDP: + l4pkt = netpkt_view(pkt, hdr_len, l4_len); + if (l4pkt) udp_input(IP_VER4, &src, &dst, l3id, l4pkt); + break; + default: + break; } + return; } - if (any_dbcast) return; return; } diff --git a/kernel/networking/internet_layer/ipv4_route.c b/kernel/networking/internet_layer/ipv4_route.c index 057f464a..4f862564 100644 --- a/kernel/networking/internet_layer/ipv4_route.c +++ b/kernel/networking/internet_layer/ipv4_route.c @@ -3,6 +3,11 @@ #include "networking/interface_manager.h" #include "networking/internet_layer/ipv4_utils.h" #include "syscalls/syscalls.h" +//TODO see ipv6_route.c +struct ipv4_rt_table { + ipv4_rt_entry_t e[IPV4_RT_PER_IF_MAX]; + int len; +}; static bool v4_l3_ok_for_tx(l3_ipv4_interface_t* v4){ if (!v4 || !v4->l2) return false; @@ -12,22 +17,50 @@ static bool v4_l3_ok_for_tx(l3_ipv4_interface_t* v4){ return true; } -static bool l3_allowed(uint8_t id, const uint8_t* allowed, int n){ - if (!allowed || n <= 0) return true; - for (int i = 0; i < n; ++i) if (allowed[i] == id) return true; - return false; +static uint32_t ipv4_route_epoch(void) { + uint32_t h = 0x811C9DC5; + uint8_t cnt = l2_interface_count(); + h = (h ^ cnt) * 16777619; + for (uint8_t i = 0; i < cnt; ++i) { + l2_interface_t* l2 = l2_interface_at(i); + if (!l2) continue; + h = (h ^ l2->ifindex) * 16777619; + h = (h ^ (uint32_t)l2->is_up) * 16777619; + h = (h ^ l2->base_metric) * 16777619; + h = (h ^ l2->ipv4_count) * 16777619; + for (int s = 0; s < MAX_IPV4_PER_INTERFACE; ++s) { + l3_ipv4_interface_t* v4 = l2->l3_v4[s]; + if (!v4) continue; + h = (h ^ v4->l3_id) * 16777619; + h = (h ^ v4->ip) * 16777619; + h = (h ^ v4->mask) * 16777619; + h = (h ^ v4->gw) * 16777619; + h = (h ^ (uint32_t)v4->mode) * 16777619; + h = (h ^ (uint32_t)(uintptr_t)v4->routing_table) * 16777619; + } + } + return h ? h : 1; } -bool ipv4_build_tx_plan(uint32_t dst, const ip_tx_opts_t* hint, const uint8_t* allowed_l3, int allowed_n, ipv4_tx_plan_t* out){ +bool ipv4_tx_plan_valid(const ipv4_tx_plan_t* plan) { + if (!plan || !plan->l3_id || !plan->net_epoch) return false; + if (plan->net_epoch != ipv4_route_epoch()) return false; + + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(plan->l3_id); + if (!v4_l3_ok_for_tx(v4)) return false; + return v4->ip == plan->src_ip; +} + +bool ipv4_build_tx_plan(uint32_t dst, const ip_tx_opts_t* hint, ipv4_tx_plan_t* out){ if (!out) return false; out->l3_id = 0; out->src_ip = 0; + out->net_epoch = ipv4_route_epoch(); out->fixed_opts.scope = IP_TX_AUTO; out->fixed_opts.index = 0; if (hint && hint->scope == IP_TX_BOUND_L3) { uint8_t id = hint->index; - if (!l3_allowed(id, allowed_l3, allowed_n)) return false; l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(id); if (!v4_l3_ok_for_tx(v4) || ipv4_is_loopback(dst) != v4->is_localhost) return false; out->l3_id = id; @@ -46,7 +79,6 @@ bool ipv4_build_tx_plan(uint32_t dst, const ip_tx_opts_t* hint, const uint8_t* a for (int s = 0; s < MAX_IPV4_PER_INTERFACE && n < (int)sizeof(cand); ++s){ l3_ipv4_interface_t* v4 = l2->l3_v4[s]; if (!v4_l3_ok_for_tx(v4) || ipv4_is_loopback(dst) != v4->is_localhost) continue; - if (!l3_allowed(v4->l3_id, allowed_l3, allowed_n)) continue; cand[n++] = v4->l3_id; } } else { @@ -57,7 +89,6 @@ bool ipv4_build_tx_plan(uint32_t dst, const ip_tx_opts_t* hint, const uint8_t* a for (int s = 0; s < MAX_IPV4_PER_INTERFACE && n < (int)sizeof(cand); ++s){ l3_ipv4_interface_t* v4 = l2->l3_v4[s]; if (!v4_l3_ok_for_tx(v4) || ipv4_is_loopback(dst) != v4->is_localhost) continue; - if (!l3_allowed(v4->l3_id, allowed_l3, allowed_n)) continue; cand[n++] = v4->l3_id; } } @@ -78,11 +109,6 @@ bool ipv4_build_tx_plan(uint32_t dst, const ip_tx_opts_t* hint, const uint8_t* a return true; } -struct ipv4_rt_table { - ipv4_rt_entry_t e[IPV4_RT_PER_IF_MAX]; - int len; -}; - static int prefix_len(uint32_t m) { int n = 0; while (m & 0x80000000u) { n++; m <<= 1; } diff --git a/kernel/networking/internet_layer/ipv4_route.h b/kernel/networking/internet_layer/ipv4_route.h index 20b3c9c3..b8583719 100644 --- a/kernel/networking/internet_layer/ipv4_route.h +++ b/kernel/networking/internet_layer/ipv4_route.h @@ -32,10 +32,12 @@ void ipv4_rt_sync_basics(ipv4_rt_table_t* t, uint32_t ip, uint32_t mask, uint32_ typedef struct { uint8_t l3_id; uint32_t src_ip; + uint32_t net_epoch; ip_tx_opts_t fixed_opts; } ipv4_tx_plan_t; -bool ipv4_build_tx_plan(uint32_t dst, const ip_tx_opts_t* hint, const uint8_t* allowed_l3, int allowed_n, ipv4_tx_plan_t* out); +bool ipv4_tx_plan_valid(const ipv4_tx_plan_t* plan); +bool ipv4_build_tx_plan(uint32_t dst, const ip_tx_opts_t* hint, ipv4_tx_plan_t* out); bool ipv4_rt_pick_best_l3_in(const uint8_t* l3_ids, int n_ids, uint32_t dst, uint8_t* out_l3); diff --git a/kernel/networking/internet_layer/ipv6_route.c b/kernel/networking/internet_layer/ipv6_route.c index 215b24f2..5ad8faaa 100644 --- a/kernel/networking/internet_layer/ipv6_route.c +++ b/kernel/networking/internet_layer/ipv6_route.c @@ -4,6 +4,11 @@ #include "networking/internet_layer/ipv6_utils.h" #include "networking/interface_manager.h" #include "syscalls/syscalls.h" +//TODO simplify plans by getting IP_TX_BOUND_L3 from l3_id instead of fixed_opts, both ipv4 and ipv6 +struct ipv6_rt_table { + ipv6_rt_entry_t e[IPV6_RT_PER_IF_MAX]; + int len; +}; static bool v6_l3_ok_for_tx(l3_ipv6_interface_t* v6, int dst_is_ll, int dst_is_loop) { if (!v6 || !v6->l2) return false; @@ -17,17 +22,50 @@ static bool v6_l3_ok_for_tx(l3_ipv6_interface_t* v6, int dst_is_ll, int dst_is_l if (src_is_ll != dst_is_ll) return false; return true; } +//TODO the epoch system is terrible, use interface events when avaible, same for ipv4 +static uint32_t ipv6_route_epoch(void) { + uint32_t h = 0x811C9DC5; + uint8_t cnt = l2_interface_count(); + h = (h ^ cnt) * 16777619; + for (uint8_t i = 0; i < cnt; ++i) { + l2_interface_t* l2 = l2_interface_at(i); + if (!l2) continue; + h = (h ^ l2->ifindex) * 16777619; + h = (h ^ (uint32_t)l2->is_up) * 16777619; + h = (h ^ l2->base_metric) * 16777619; + h = (h ^ l2->ipv6_count) * 16777619; + for (int s = 0; s < MAX_IPV6_PER_INTERFACE; ++s) { + l3_ipv6_interface_t* v6 = l2->l3_v6[s]; + if (!v6) continue; + h = (h ^ v6->l3_id) * 16777619; + h = (h ^ v6->prefix_len) * 16777619; + h = (h ^ (uint32_t)v6->cfg) * 16777619; + h = (h ^ (uint32_t)v6->kind) * 16777619; + h = (h ^ (uint32_t)v6->dad_state) * 16777619; + h = (h ^ (uint32_t)(uintptr_t)v6->routing_table) * 16777619; + for (int b = 0; b < 16; ++b) h = (h ^ v6->ip[b]) * 16777619; + for (int b = 0; b < 16; ++b) h = (h ^ v6->gateway[b]) * 16777619; + } + } + return h ? h : 1; +} -static bool l3_allowed(uint8_t id, const uint8_t* allowed, int n) { - if (!allowed || n <= 0) return true; - for (int i = 0; i < n; ++i) if (allowed[i] == id) return true; - return false; +bool ipv6_tx_plan_valid(const ipv6_tx_plan_t* plan) { + if (!plan || !plan->l3_id || !plan->net_epoch) return false; + if (plan->net_epoch != ipv6_route_epoch()) return false; + + l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(plan->l3_id); + if (!v6 || !v6->l2 || !v6->l2->is_up) return false; + if (v6->cfg == IPV6_CFG_DISABLE) return false; + if (v6->dad_state != IPV6_DAD_OK) return false; + return memcmp(v6->ip, plan->src_ip, 16) == 0; } -bool ipv6_build_tx_plan(const uint8_t dst[16], const ip_tx_opts_t* hint, const uint8_t* allowed_l3, int allowed_n, ipv6_tx_plan_t* out) { +bool ipv6_build_tx_plan(const uint8_t dst[16], const ip_tx_opts_t* hint, ipv6_tx_plan_t* out) { if (!dst || !out) return false; memset(out, 0, sizeof(*out)); + out->net_epoch = ipv6_route_epoch(); out->fixed_opts.scope = IP_TX_AUTO; out->fixed_opts.index = 0; @@ -36,7 +74,6 @@ bool ipv6_build_tx_plan(const uint8_t dst[16], const ip_tx_opts_t* hint, const u if (hint && hint->scope == IP_TX_BOUND_L3) { uint8_t id = hint->index; - if (!l3_allowed(id, allowed_l3, allowed_n)) return false; l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(id); if (!v6_l3_ok_for_tx(v6, dst_is_ll, dst_is_loop)) return false; out->l3_id = id; @@ -55,7 +92,6 @@ bool ipv6_build_tx_plan(const uint8_t dst[16], const ip_tx_opts_t* hint, const u for (int s = 0; s < MAX_IPV6_PER_INTERFACE && n < (int)sizeof(cand); ++s){ l3_ipv6_interface_t* v6 = l2->l3_v6[s]; if (!v6_l3_ok_for_tx(v6, dst_is_ll, dst_is_loop)) continue; - if (!l3_allowed(v6->l3_id, allowed_l3, allowed_n)) continue; cand[n++] = v6->l3_id; } } else { @@ -65,8 +101,7 @@ bool ipv6_build_tx_plan(const uint8_t dst[16], const ip_tx_opts_t* hint, const u if (!l2 || !l2->is_up) continue; for (int s = 0; s < MAX_IPV6_PER_INTERFACE && n < (int)sizeof(cand); ++s){ l3_ipv6_interface_t* v6 = l2->l3_v6[s]; - if (!v6_l3_ok_for_tx(v6, dst_is_ll, dst_is_loop)) continue; - if (!l3_allowed(v6->l3_id, allowed_l3, allowed_n)) continue; + if (!v6_l3_ok_for_tx(v6, dst_is_ll, dst_is_loop)) continue; cand[n++] = v6->l3_id; } } @@ -87,11 +122,6 @@ bool ipv6_build_tx_plan(const uint8_t dst[16], const ip_tx_opts_t* hint, const u return true; } -struct ipv6_rt_table { - ipv6_rt_entry_t e[IPV6_RT_PER_IF_MAX]; - int len; -}; - ipv6_rt_table_t* ipv6_rt_create(void) { ipv6_rt_table_t* t = zalloc(sizeof(*t)); if (!t) return 0; diff --git a/kernel/networking/internet_layer/ipv6_route.h b/kernel/networking/internet_layer/ipv6_route.h index 3b632955..1d4875b3 100644 --- a/kernel/networking/internet_layer/ipv6_route.h +++ b/kernel/networking/internet_layer/ipv6_route.h @@ -33,10 +33,12 @@ bool ipv6_rt_pick_best_l3_in(const uint8_t* l3_ids, int n_ids, const uint8_t dst typedef struct { uint8_t l3_id; uint8_t src_ip[16]; + uint32_t net_epoch; ip_tx_opts_t fixed_opts; } ipv6_tx_plan_t; -bool ipv6_build_tx_plan(const uint8_t dst[16], const ip_tx_opts_t* hint, const uint8_t* allowed_l3, int allowed_n, ipv6_tx_plan_t* out); +bool ipv6_tx_plan_valid(const ipv6_tx_plan_t* plan); +bool ipv6_build_tx_plan(const uint8_t dst[16], const ip_tx_opts_t* hint, ipv6_tx_plan_t* out); #ifdef __cplusplus } diff --git a/kernel/networking/link_layer/arp.c b/kernel/networking/link_layer/arp.c index f61d85f2..bc84d05a 100644 --- a/kernel/networking/link_layer/arp.c +++ b/kernel/networking/link_layer/arp.c @@ -347,7 +347,9 @@ void arp_input(uint16_t ifindex, const uint8_t src_mac[6], netpkt_t* pkt) { 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, ARP_REACHABLE_MS, false); + bool sender_mac_matches = memcmp(hdr.sender_mac, src_mac, 6) == 0; + bool sender_is_usable = sender_ip != 0 && sender_ip != 0xFFFFFFFF && !ipv4_is_multicast(sender_ip) && !l2_has_ip((uint8_t)ifindex, sender_ip); + if (sender_mac_matches && sender_is_usable) arp_table_put_for_l2((uint8_t)ifindex, sender_ip, hdr.sender_mac, ARP_REACHABLE_MS, false); if (op == ARP_OPCODE_REQUEST) { bool has = l2_has_ip((uint8_t)ifindex, target_ip); @@ -360,12 +362,11 @@ void arp_input(uint16_t ifindex, const uint8_t src_mac[6], netpkt_t* pkt) { } -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()); + g_arp_pid = get_current_proc_pid(); const uint32_t tick_ms = 1000; while (1){ arp_tick_all(tick_ms); diff --git a/kernel/networking/link_layer/arp.h b/kernel/networking/link_layer/arp.h index e135421b..f306eb53 100644 --- a/kernel/networking/link_layer/arp.h +++ b/kernel/networking/link_layer/arp.h @@ -61,7 +61,6 @@ void arp_send_request_on(uint8_t ifindex, uint32_t target_ip); void arp_input(uint16_t ifindex, const uint8_t src_mac[6], netpkt_t* pkt); -void arp_set_pid(uint16_t pid); uint16_t arp_get_pid(void); int arp_daemon_entry(int argc, char* argv[]); diff --git a/kernel/networking/link_layer/ndp.c b/kernel/networking/link_layer/ndp.c index 912fa14b..9c5aa98b 100644 --- a/kernel/networking/link_layer/ndp.c +++ b/kernel/networking/link_layer/ndp.c @@ -822,7 +822,17 @@ void ndp_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[ if (self->dad_state != IPV6_DAD_OK) return; - ndp_table_put_for_l2((uint8_t)ifindex, src_ip, src_mac, 180000, false); + int src_is_local = 0; + for (int i = 0; i < MAX_IPV6_PER_INTERFACE; i++) { + l3_ipv6_interface_t* v6 = l2->l3_v6[i]; + if (!v6) continue; + if (v6->cfg == IPV6_CFG_DISABLE) continue; + if (ipv6_cmp(v6->ip, src_ip) == 0) { + src_is_local = 1; + break; + } + } + if (!src_is_local) ndp_table_put_for_l2((uint8_t)ifindex, src_ip, src_mac, 180000, false); uint8_t src_my[16] = {0}; @@ -873,6 +883,14 @@ void ndp_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[ } if (ipv6_is_unspecified(src_ip)) return; + if (l2) { + for (int i = 0; i < MAX_IPV6_PER_INTERFACE; i++) { + l3_ipv6_interface_t* v6 = l2->l3_v6[i]; + if (!v6) continue; + if (v6->cfg == IPV6_CFG_DISABLE) continue; + if (ipv6_cmp(v6->ip, src_ip) == 0 || ipv6_cmp(v6->ip, na.target) == 0) return; + } + } uint32_t f = bswap32(na.flags); uint8_t router = (uint8_t)((f >> 31) & 1u); diff --git a/kernel/networking/network.cpp b/kernel/networking/network.cpp index e1e15a0a..4643e3b4 100644 --- a/kernel/networking/network.cpp +++ b/kernel/networking/network.cpp @@ -57,9 +57,6 @@ size_t network_nic_count() { return dispatch->nic_count(); } -void network_net_set_pid(uint16_t pid) { - if (dispatch) dispatch->set_net_pid(pid); -} uint16_t network_net_get_pid() { return dispatch ? dispatch->get_net_pid() : UINT16_MAX; diff --git a/kernel/networking/network.h b/kernel/networking/network.h index 5076cfb5..5f26c1a7 100644 --- a/kernel/networking/network.h +++ b/kernel/networking/network.h @@ -13,7 +13,6 @@ extern "C" { //TODO: consider using the system MTU here #define MAX_PACKET_SIZE 0x1000 -void network_net_set_pid(uint16_t pid); uint16_t network_net_get_pid(); bool network_init(); diff --git a/kernel/networking/network_dispatch.cpp b/kernel/networking/network_dispatch.cpp index 0750cc22..45392771 100644 --- a/kernel/networking/network_dispatch.cpp +++ b/kernel/networking/network_dispatch.cpp @@ -74,7 +74,7 @@ bool NetworkDispatch::enqueue_packet(uint8_t ifindex, netpkt_t* pkt) int NetworkDispatch::net_task() { - set_net_pid(get_current_proc_pid()); + g_net_pid = get_current_proc_pid(); for (;;) { bool did_work = false; @@ -136,11 +136,6 @@ int NetworkDispatch::net_task() } } -void NetworkDispatch::set_net_pid(uint16_t pid) -{ - g_net_pid = pid; -} - uint16_t NetworkDispatch::get_net_pid() const { return g_net_pid; diff --git a/kernel/networking/processes/net_proc.c b/kernel/networking/processes/net_proc.c index 9b3ddb6c..4d6be397 100644 --- a/kernel/networking/processes/net_proc.c +++ b/kernel/networking/processes/net_proc.c @@ -1,7 +1,6 @@ #include "net_proc.h" #include "kernel_processes/kprocess_loader.h" #include "process/scheduler.h" -#include "console/kio.h" #include "std/memory.h" #include "std/string.h" @@ -28,7 +27,6 @@ #include "networking/application_layer/dhcpv6_daemon.h" #include "exceptions/timer.h" -#include "syscalls/syscalls.h" #define HTTP_PORT 80 #define PROBE_PORT 8080 @@ -36,16 +34,19 @@ #define PROBE_INTERVAL_MS 50 static int udp_probe_server(uint32_t probe_ip, uint16_t probe_port, net_l4_endpoint *out_l4) { - socket_handle_t sock = {0}; - if (!create_socket(SOCKET_CLIENT, PROTO_UDP, NULL, &sock)) + socket_handle_t sock = create_socket(PROTO_UDP, NULL); + if (!sock) return 0; + if (set_socket_option(sock, SOCK_OPT_BROADCAST_ALLOWED, NULL, 0) < 0) { + close_socket(sock); return 0; + } net_l4_endpoint dst; make_ep(probe_ip, probe_port, IP_VER4, &dst); static const char greeting[] = "hello"; - if (send_to_socket(&sock, &dst, greeting, sizeof(greeting)) < 0) { - close_socket(&sock); + if (send_to_socket(sock, &dst, greeting, sizeof(greeting)) < 0) { + close_socket(sock); return 0; } @@ -55,13 +56,13 @@ static int udp_probe_server(uint32_t probe_ip, uint16_t probe_port, net_l4_endpo int64_t recvd = 0; while (waited < PROBE_TIMEOUT_MS) { - recvd = receive_from_socket(&sock, recv_buf, sizeof(recv_buf), &src); + recvd = receive_from_socket(sock, recv_buf, sizeof(recv_buf), &src); if (recvd > 0) break; msleep(PROBE_INTERVAL_MS); waited += PROBE_INTERVAL_MS; } - close_socket(&sock); + close_socket(sock); if (recvd <= 0) return 0; if (out_l4) *out_l4 = src; @@ -92,10 +93,7 @@ static int net_has_ready_address(void) { } static void run_http_server() { - SocketExtraOptions opt = {0}; - opt.debug_level = SOCK_DBG_ALL; - opt.flags = SOCK_OPT_DEBUG; - http_server_handle_t srv = http_server_create(&opt, NULL); + http_server_handle_t srv = http_server_create(NULL, NULL); if (!srv) { stop_current_process(1); return; @@ -167,10 +165,7 @@ static void test_http(const net_l4_endpoint* ep) { } http_client_handle_t cli = http_client_create(NULL, NULL); - if (!cli) { - kprintf("[HTTP] http_client_create FAIL"); - return; - } + if (!cli) return; net_l4_endpoint e = {0}; e.ver = ep->ver; @@ -182,8 +177,7 @@ static void test_http(const net_l4_endpoint* ep) { return; } - int32_t rc = http_client_connect_endpoint(cli, &e); - if (rc < 0) { + if (http_client_connect_endpoint(cli, &e) < 0) { http_client_destroy(cli); return; } @@ -196,14 +190,14 @@ static void test_http(const net_l4_endpoint* ep) { HTTPResponseMsg resp = http_client_send_request(cli, &req); - if ((int)resp.status_code < 0) kprintf("[HTTP] request FAIL status=%i", (int)resp.status_code); + if ((int)resp.status_code < 0) print("[HTTP] request FAIL status=%i", (int)resp.status_code); else if (resp.body.ptr && resp.body.size) { char *body_str = (char*)zalloc(resp.body.size + 1); if (body_str) { memcpy(body_str, (void*)resp.body.ptr, resp.body.size); body_str[resp.body.size] = '\0'; - kprintf("[HTTP] %i %i bytes of body", resp.status_code, resp.body.size); - kprintf("%s", body_str); + print("[HTTP] %i %i bytes of body", resp.status_code, resp.body.size); + print("%s", body_str); release(body_str); } } @@ -221,32 +215,30 @@ static void test_http(const net_l4_endpoint* ep) { static int ntp(int argc, char* argv[]) { (void)argc; (void)argv; if (!ntp_is_running()) { - kprintf("[TIME] starting NTP..."); + print("[TIME] starting NTP..."); create_kernel_process("ntpd", ntp_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 NTP sync..."); + if ((waited % 1000) == 0) print("[TIME] waiting NTP sync..."); msleep(step); waited += step; - } - if (!timer_is_synchronised()) kprintf("[TIME] NTP sync timeout, continuing"); } timer_set_timezone_minutes(120); - kprintf("[TIME]timezone offset %i minutes", (int32_t)timer_get_timezone_minutes()); + print("[TIME]timezone offset %i minutes", (int32_t)timer_get_timezone_minutes()); 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); + print("[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()); + print("[TIME] LOCAL: %s (TZ %i min)", s, (int32_t)timer_get_timezone_minutes()); } return 0; } @@ -268,22 +260,17 @@ static int net_test_entry(int argc, char *argv[]) { uint32_t probe_ip = ipv4_broadcast_calc(ifv4->ip, ifv4->mask); if (!probe_ip) continue; - 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, PROBE_PORT, &srv)) { test_http(&srv); tested_any = 1; - } else kprintf("[NET] no UDP responder at %s:%i (l3_id=%u)", probe_str, PROBE_PORT, (unsigned)ifv4->l3_id); + } } } 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, PROBE_PORT, &srv)) test_http(&srv); - else kprintf("[NET] could not find update server"); } run_http_server(); @@ -292,12 +279,7 @@ static int net_test_entry(int argc, char *argv[]) { static int ip_waiter_entry(int argc, char* argv[]) { (void)argc; (void)argv; - uint32_t waited = 0; - while (!net_has_ready_address()) { - if ((waited % 1000) == 0) kprintf("[NET] ip_waiter: waiting for ip..."); - msleep(200); - waited += 200; - } + while (!net_has_ready_address()) msleep(200); create_kernel_process("net_test", net_test_entry, 0, 0); return 0; } @@ -312,7 +294,7 @@ process_t* launch_net_process() { create_kernel_process("dns_daemon", dns_deamon_entry, 0, 0); if (net_has_ready_address()) { - kprintf("[NET] ip ready, starting net_test"); + print("[NET] ip ready, starting net_test"); create_kernel_process("net_test", net_test_entry, 0, 0); } else create_kernel_process("ip_waiter", ip_waiter_entry, 0, 0); diff --git a/kernel/networking/transport_layer/csocket.c b/kernel/networking/transport_layer/csocket.c index 99581af0..e986677a 100644 --- a/kernel/networking/transport_layer/csocket.c +++ b/kernel/networking/transport_layer/csocket.c @@ -5,9 +5,9 @@ #include "process/scheduler.h" #include "std/memory.h" -bool create_socket(Socket_Role role, protocol_t protocol, const SocketExtraOptions* extra, SocketHandle *out_handle){ - if (!out_handle) return false; - memset(out_handle, 0, sizeof(*out_handle)); +socket_handle_t create_socket(protocol_t protocol, const SocketExtraOptions* extra){ + SocketExtraOptions default_extra = {}; + if (!extra) extra = &default_extra; if (protocol == PROTO_NONE) { //TODO PROTO_NONE for RAW SET or L2 @@ -17,7 +17,7 @@ bool create_socket(Socket_Role role, protocol_t protocol, const SocketExtraOptio uint16_t pid = get_current_proc_pid(); ksocket_t* socket = NULL; - if (!socket_core_alloc(role, protocol, pid, &socket)) return false; + if (!socket_core_alloc(protocol, pid, &socket)) return false; socket_impl_t impl = NULL; socket_impl_destroy_fn destroy = NULL; @@ -26,13 +26,13 @@ bool create_socket(Socket_Role role, protocol_t protocol, const SocketExtraOptio socket_impl_getopt_fn getopt = NULL; if (protocol == PROTO_UDP) { - impl = udp_socket_create(socket, role, pid, extra); + impl = udp_socket_create(socket, extra); destroy = socket_destroy_udp; close = socket_close_udp; setopt = socket_setopt_udp; getopt = socket_getopt_udp; } else if (protocol == PROTO_TCP) { - impl = socket_tcp_create(socket, role, pid, extra); + impl = socket_tcp_create(socket, extra); destroy = socket_destroy_tcp; close = socket_close_tcp; setopt = socket_setopt_tcp; @@ -43,64 +43,45 @@ bool create_socket(Socket_Role role, protocol_t protocol, const SocketExtraOptio return false; } - if (!socket_core_attach_impl(socket, impl, destroy, close, setopt, getopt, out_handle)) { + if (!socket_core_attach_impl(socket, impl, destroy, close, setopt, getopt)) { destroy(impl); socket_core_close_socket(socket); return false; } - return true; + return socket_core_export_handle(socket); } -int32_t bind_socket(SocketHandle *handle, uint16_t port, ip_version_t ip_version){ - ksocket_t* socket = socket_core_get(handle, get_current_proc_pid()); - if (!socket) return SOCK_ERR_INVAL; - +int32_t bind_socket(socket_handle_t handle, const SockBindSpec *spec_in, uint16_t port){ SockBindSpec spec; memset(&spec, 0, sizeof(spec)); - spec.kind = BIND_IP; - spec.ver = ip_version; - if (handle) { - if (ip_version == IP_VER4) memcpy(spec.ip, handle->connection.ip, 4); - else if (ip_version == IP_VER6) memcpy(spec.ip, handle->connection.ip, 16); - else spec.kind = BIND_ANY; - } + if (spec_in) spec = *spec_in; + else spec.kind = BIND_ANY; - int32_t res = SOCK_ERR_PROTO; - if (socket_core_protocol(socket) == PROTO_TCP) res = socket_bind_tcp(socket_core_impl(socket), &spec, port); - else if (socket_core_protocol(socket) == PROTO_UDP) res = socket_bind_udp(socket_core_impl(socket), &spec, port); - - socket_core_put(socket); - return res; -} - -int32_t bind_socket_spec(SocketHandle* handle, const SockBindSpec* spec, uint16_t port){ - if (!spec) return SOCK_ERR_INVAL; ksocket_t* socket = socket_core_get(handle, get_current_proc_pid()); if (!socket) return SOCK_ERR_INVAL; int32_t res = SOCK_ERR_PROTO; - if (socket_core_protocol(socket) == PROTO_TCP) res = socket_bind_tcp(socket_core_impl(socket), spec, port); - else if (socket_core_protocol(socket) == PROTO_UDP) res = socket_bind_udp(socket_core_impl(socket), spec, port); + if (socket_core_protocol(socket) == PROTO_TCP) res = socket_bind_tcp(socket_core_impl(socket), &spec, port); + else if (socket_core_protocol(socket) == PROTO_UDP) res = socket_bind_udp(socket_core_impl(socket), &spec, port); socket_core_put(socket); return res; } -int32_t connect_socket(SocketHandle *handle, const net_l4_endpoint* dst){ +int32_t connect_socket(socket_handle_t handle, const net_l4_endpoint* dst){ ksocket_t* socket = socket_core_get(handle, get_current_proc_pid()); if (!socket) return SOCK_ERR_INVAL; int32_t res = SOCK_ERR_PROTO; if (socket_core_protocol(socket) == PROTO_TCP) { res = socket_connect_tcp(socket_core_impl(socket), dst); - if (res == SOCK_OK && handle) socket_get_remote_ep_tcp(socket_core_impl(socket), &handle->connection); - } + } else if (socket_core_protocol(socket) == PROTO_UDP) res = socket_connect_udp(socket_core_impl(socket), dst); socket_core_put(socket); return res; } -int64_t send_on_socket(SocketHandle *handle, const void* buf, uint64_t len){ +int64_t send_on_socket(socket_handle_t handle, const void* buf, uint64_t len){ ksocket_t* socket = socket_core_get(handle, get_current_proc_pid()); if (!socket) return SOCK_ERR_INVAL; @@ -112,7 +93,7 @@ int64_t send_on_socket(SocketHandle *handle, const void* buf, uint64_t len){ return res; } -int64_t send_to_socket(SocketHandle *handle, const net_l4_endpoint* dst, const void* buf, uint64_t len){ +int64_t send_to_socket(socket_handle_t handle, const net_l4_endpoint* dst, const void* buf, uint64_t len){ if (!dst) return SOCK_ERR_INVAL; ksocket_t* socket = socket_core_get(handle, get_current_proc_pid()); if (!socket) return SOCK_ERR_INVAL; @@ -124,7 +105,7 @@ int64_t send_to_socket(SocketHandle *handle, const net_l4_endpoint* dst, const v return res; } -int64_t receive_from_socket(SocketHandle *handle, void* buf, uint64_t len, net_l4_endpoint* out_src){ +int64_t receive_from_socket(socket_handle_t handle, void* buf, uint64_t len, net_l4_endpoint* out_src){ ksocket_t* socket = socket_core_get(handle, get_current_proc_pid()); if (!socket) return SOCK_ERR_INVAL; @@ -136,7 +117,7 @@ int64_t receive_from_socket(SocketHandle *handle, void* buf, uint64_t len, net_l return res; } -int32_t set_socket_option(SocketHandle *handle, int32_t opt, const void* value, uint32_t len){ +int32_t set_socket_option(socket_handle_t handle, int32_t opt, const void* value, uint32_t len){ ksocket_t* socket = socket_core_get(handle, get_current_proc_pid()); if (!socket) return SOCK_ERR_INVAL; int32_t res = socket_core_set_option(socket, opt, value, len); @@ -144,7 +125,7 @@ int32_t set_socket_option(SocketHandle *handle, int32_t opt, const void* value, return res; } -int32_t get_socket_option(SocketHandle *handle, int32_t opt, void* value, uint32_t* len){ +int32_t get_socket_option(socket_handle_t handle, int32_t opt, void* value, uint32_t* len){ ksocket_t* socket = socket_core_get(handle, get_current_proc_pid()); if (!socket) return SOCK_ERR_INVAL; int32_t res = socket_core_get_option(socket, opt, value, len); @@ -152,7 +133,7 @@ int32_t get_socket_option(SocketHandle *handle, int32_t opt, void* value, uint32 return res; } -uint16_t get_socket_local_port(SocketHandle* handle){ +uint16_t get_socket_local_port(socket_handle_t handle){ ksocket_t* socket = socket_core_get(handle, get_current_proc_pid()); if (!socket) return 0; @@ -164,7 +145,7 @@ uint16_t get_socket_local_port(SocketHandle* handle){ return port; } -bool get_socket_remote_endpoint(SocketHandle *handle, net_l4_endpoint* out){ +bool get_socket_remote_endpoint(socket_handle_t handle, net_l4_endpoint* out){ if (!out) return false; ksocket_t* socket = socket_core_get(handle, get_current_proc_pid()); if (!socket) return false; @@ -178,11 +159,11 @@ bool get_socket_remote_endpoint(SocketHandle *handle, net_l4_endpoint* out){ return ok; } -int32_t close_socket(SocketHandle *handle){ +int32_t close_socket(socket_handle_t handle){ return socket_core_close_handle(handle, get_current_proc_pid()); } -int32_t listen_on(SocketHandle *handle, int32_t backlog){ +int32_t listen_on(socket_handle_t handle, int32_t backlog){ ksocket_t* socket = socket_core_get(handle, get_current_proc_pid()); if (!socket) return SOCK_ERR_INVAL; @@ -193,25 +174,22 @@ int32_t listen_on(SocketHandle *handle, int32_t backlog){ return res; } -bool accept_on_socket(SocketHandle *handle, SocketHandle* out_child) { - if (!out_child) return false; - memset(out_child, 0, sizeof(*out_child)); +socket_handle_t accept_on_socket(socket_handle_t handle) { ksocket_t* listener = socket_core_get(handle, get_current_proc_pid()); - if (!listener) return false; + if (!listener) return 0; if (socket_core_protocol(listener) != PROTO_TCP) { socket_core_put(listener); - return false; + return 0; } ksocket_t* child = socket_accept_tcp(socket_core_impl(listener)); if (!child) { socket_core_put(listener); - return false; + return 0; } - socket_core_export_handle(child, out_child); - socket_get_remote_ep_tcp(socket_core_impl(child), &out_child->connection); + socket_handle_t child_handle = socket_core_export_handle(child); socket_core_put(child); socket_core_put(listener); - return true; + return child_handle; } \ No newline at end of file diff --git a/kernel/networking/transport_layer/csocket.h b/kernel/networking/transport_layer/csocket.h index 214e688c..03dfa260 100644 --- a/kernel/networking/transport_layer/csocket.h +++ b/kernel/networking/transport_layer/csocket.h @@ -9,25 +9,22 @@ extern "C" { #endif -typedef SocketHandle socket_handle_t; - -bool create_socket(Socket_Role role, protocol_t protocol, const SocketExtraOptions* extra, SocketHandle* out_handle); -int32_t bind_socket(SocketHandle *handle, uint16_t port, ip_version_t ip_vers); -int32_t bind_socket_spec(SocketHandle *handle, const SockBindSpec* spec, uint16_t port); -int32_t connect_socket(SocketHandle *handle, const net_l4_endpoint* dst); - -int64_t send_on_socket(SocketHandle *handle, const void* buf, uint64_t len); -int64_t send_to_socket(SocketHandle *handle, const net_l4_endpoint* dst, const void* buf, uint64_t len); -int64_t receive_from_socket(SocketHandle *handle, void* buf, uint64_t len, net_l4_endpoint* out_src); -int32_t set_socket_option(SocketHandle *handle, int32_t opt, const void* value, uint32_t len); -int32_t get_socket_option(SocketHandle *handle, int32_t opt, void* value, uint32_t* len); - -uint16_t get_socket_local_port(SocketHandle *handle); -bool get_socket_remote_endpoint(SocketHandle *handle, net_l4_endpoint* out); -int32_t close_socket(SocketHandle *handle); - -int32_t listen_on(SocketHandle *handle, int32_t backlog); -bool accept_on_socket(SocketHandle *handle, SocketHandle* out_child); +socket_handle_t create_socket(protocol_t protocol, const SocketExtraOptions* extra); +int32_t bind_socket(socket_handle_t handle, const SockBindSpec* spec, uint16_t port); +int32_t connect_socket(socket_handle_t handle, const net_l4_endpoint* dst); + +int64_t send_on_socket(socket_handle_t handle, const void* buf, uint64_t len); +int64_t send_to_socket(socket_handle_t handle, const net_l4_endpoint* dst, const void* buf, uint64_t len); +int64_t receive_from_socket(socket_handle_t handle, void* buf, uint64_t len, net_l4_endpoint* out_src); +int32_t set_socket_option(socket_handle_t handle, int32_t opt, const void* value, uint32_t len); +int32_t get_socket_option(socket_handle_t handle, int32_t opt, void* value, uint32_t* len); + +uint16_t get_socket_local_port(socket_handle_t handle); +bool get_socket_remote_endpoint(socket_handle_t handle, net_l4_endpoint* out); +int32_t close_socket(socket_handle_t handle); + +int32_t listen_on(socket_handle_t handle, int32_t backlog); +socket_handle_t accept_on_socket(socket_handle_t handle); #ifdef __cplusplus } diff --git a/kernel/networking/transport_layer/csocket_tcp.cpp b/kernel/networking/transport_layer/csocket_tcp.cpp index 52c7aa2c..76d077cb 100644 --- a/kernel/networking/transport_layer/csocket_tcp.cpp +++ b/kernel/networking/transport_layer/csocket_tcp.cpp @@ -4,9 +4,9 @@ extern "C" { -socket_impl_t socket_tcp_create(ksocket_t* owner, uint8_t role, uint32_t pid, const SocketExtraOptions* extra) { +socket_impl_t socket_tcp_create(ksocket_t* owner, const SocketExtraOptions* extra) { if (!owner) return nullptr; - return reinterpret_cast(new TCPSocket(owner, role, pid, extra)); + return reinterpret_cast(new TCPSocket(owner, extra)); } int32_t socket_bind_tcp(socket_impl_t sh, const SockBindSpec* spec, uint16_t port) { @@ -64,11 +64,11 @@ const SocketExtraOptions* socket_tcp_extra_options(socket_impl_t sh) { return reinterpret_cast(sh)->get_extra_options(); } -uint32_t tcp_accept_enqueue(ksocket_t* listener, uint8_t ifindex, ip_version_t ipver, const void* src_ip_addr, const void* dst_ip_addr, uint16_t src_port, uint16_t dst_port) { +uint32_t tcp_accept_enqueue(ksocket_t* listener, ip_version_t ipver, const void* src_ip_addr, const void* dst_ip_addr, uint16_t src_port, uint16_t dst_port) { if (!listener) return 0; socket_impl_t sh = socket_core_impl(listener); if (!sh) return 0; - return reinterpret_cast(sh)->queue_accepted_child(ifindex, ipver, src_ip_addr, dst_ip_addr, src_port, dst_port); + return reinterpret_cast(sh)->queue_accepted_child(ipver, src_ip_addr, dst_ip_addr, src_port, dst_port); } uint16_t socket_get_local_port_tcp(socket_impl_t sh) { @@ -76,31 +76,12 @@ uint16_t socket_get_local_port_tcp(socket_impl_t sh) { return reinterpret_cast(sh)->get_local_port(); } -uint16_t socket_get_remote_port_tcp(socket_impl_t sh) { - if (!sh) return 0; - return reinterpret_cast(sh)->get_remote_port(); -} void socket_get_remote_ep_tcp(socket_impl_t sh, net_l4_endpoint* out) { if (!sh || !out) return; *out = reinterpret_cast(sh)->get_remote_ep(); } -uint8_t socket_get_protocol_tcp(socket_impl_t sh) { - if (!sh) return 0xFF; - return reinterpret_cast(sh)->get_protocol(); -} - -uint8_t socket_get_role_tcp(socket_impl_t sh) { - if (!sh) return 0xFF; - return reinterpret_cast(sh)->get_role(); -} - -bool socket_is_bound_tcp(socket_impl_t sh) { - if (!sh) return false; - return reinterpret_cast(sh)->is_bound(); -} - bool socket_is_connected_tcp(socket_impl_t sh) { if (!sh) return false; return reinterpret_cast(sh)->is_connected(); diff --git a/kernel/networking/transport_layer/csocket_tcp.h b/kernel/networking/transport_layer/csocket_tcp.h index d2f8736a..fafb94b3 100644 --- a/kernel/networking/transport_layer/csocket_tcp.h +++ b/kernel/networking/transport_layer/csocket_tcp.h @@ -8,7 +8,7 @@ extern "C" { #endif -socket_impl_t socket_tcp_create(ksocket_t* owner, uint8_t role, uint32_t pid, const SocketExtraOptions* extra); +socket_impl_t socket_tcp_create(ksocket_t* owner, const SocketExtraOptions* extra); int32_t socket_bind_tcp(socket_impl_t sh, const SockBindSpec* spec, uint16_t port); int32_t socket_listen_tcp(socket_impl_t sh, int32_t backlog); ksocket_t* socket_accept_tcp(socket_impl_t sh); @@ -20,14 +20,10 @@ int32_t socket_setopt_tcp(socket_impl_t sh, int32_t opt, const void* value, uint int32_t socket_getopt_tcp(socket_impl_t sh, int32_t opt, void* value, uint32_t* len); void socket_destroy_tcp(socket_impl_t sh); const SocketExtraOptions* socket_tcp_extra_options(socket_impl_t sh); -uint32_t tcp_accept_enqueue(ksocket_t* listener, uint8_t ifindex, ip_version_t ipver, const void* src_ip_addr, const void* dst_ip_addr, uint16_t src_port, uint16_t dst_port); +uint32_t tcp_accept_enqueue(ksocket_t* listener, ip_version_t ipver, const void* src_ip_addr, const void* dst_ip_addr, uint16_t src_port, uint16_t dst_port); uint16_t socket_get_local_port_tcp(socket_impl_t sh); -uint16_t socket_get_remote_port_tcp(socket_impl_t sh); void socket_get_remote_ep_tcp(socket_impl_t sh, net_l4_endpoint* out); -uint8_t socket_get_protocol_tcp(socket_impl_t sh); -uint8_t socket_get_role_tcp(socket_impl_t sh); -bool socket_is_bound_tcp(socket_impl_t sh); bool socket_is_connected_tcp(socket_impl_t sh); #ifdef __cplusplus diff --git a/kernel/networking/transport_layer/csocket_udp.cpp b/kernel/networking/transport_layer/csocket_udp.cpp index dcdcd0ec..117cc649 100644 --- a/kernel/networking/transport_layer/csocket_udp.cpp +++ b/kernel/networking/transport_layer/csocket_udp.cpp @@ -4,9 +4,17 @@ extern "C" { -socket_impl_t udp_socket_create(ksocket_t* owner, uint8_t role, uint32_t pid, const SocketExtraOptions* extra) { +socket_impl_t udp_socket_create(ksocket_t* owner, const SocketExtraOptions* extra) { if (!owner) return nullptr; - return reinterpret_cast(new UDPSocket(owner, role, pid, extra)); + UDPSocket* s = new UDPSocket(owner, extra); + if (!s) return nullptr; + if (extra && extra->mcast_count) { + if (!extra->mcast_groups || s->set_option(SOCK_OPT_MCAST_JOIN, extra->mcast_groups, sizeof(net_l4_endpoint) * extra->mcast_count) != SOCK_OK) { + delete s; + return nullptr; + } + } + return reinterpret_cast(s); } int32_t socket_bind_udp(socket_impl_t sh, const SockBindSpec* spec, uint16_t port) { @@ -14,13 +22,18 @@ int32_t socket_bind_udp(socket_impl_t sh, const SockBindSpec* spec, uint16_t por return reinterpret_cast(sh)->bind(*spec, port); } +int32_t socket_connect_udp(socket_impl_t sh, const net_l4_endpoint* dst) { + if (!sh || !dst) return SOCK_ERR_INVAL; + return reinterpret_cast(sh)->connect(dst); +} + int64_t socket_sendto_udp(socket_impl_t sh, const net_l4_endpoint* dst, const void* buf, uint64_t len) { - if (!sh || !buf || !len) return SOCK_ERR_INVAL; + if (!sh || (!buf && len)) return SOCK_ERR_INVAL; return reinterpret_cast(sh)->sendto(dst, buf, len); } int64_t socket_recvfrom_udp(socket_impl_t sh, void* buf, uint64_t len, net_l4_endpoint* out_src) { - if (!sh || !buf || !len) return 0; + if (!sh || (!buf && len)) return SOCK_ERR_INVAL; return reinterpret_cast(sh)->recvfrom(buf, len, out_src); } @@ -44,11 +57,11 @@ void socket_destroy_udp(socket_impl_t sh) { delete reinterpret_cast(sh); } -uint32_t socket_udp_input(ksocket_t* socket, uint8_t ifindex, uint8_t l3_id, ip_version_t ipver, const void* src_ip_addr, const void* dst_ip_addr, netpkt_t* pkt, uint16_t src_port, uint16_t dst_port) { +uint32_t socket_udp_input(ksocket_t* socket, ip_version_t ipver, const void* src_ip_addr, const void* dst_ip_addr, netpkt_t* pkt, uint16_t src_port, uint16_t dst_port) { if (!socket || !pkt) return 0; socket_impl_t sh = socket_core_impl(socket); if (!sh) return 0; - return reinterpret_cast(sh)->enqueue_datagram(ifindex, l3_id, ipver, src_ip_addr, dst_ip_addr, pkt, src_port, dst_port); + return reinterpret_cast(sh)->enqueue_datagram(ipver, src_ip_addr, dst_ip_addr, pkt, src_port, dst_port); } uint16_t socket_get_local_port_udp(socket_impl_t sh) { @@ -56,31 +69,12 @@ uint16_t socket_get_local_port_udp(socket_impl_t sh) { return reinterpret_cast(sh)->get_local_port(); } -uint16_t socket_get_remote_port_udp(socket_impl_t sh) { - if (!sh) return 0; - return reinterpret_cast(sh)->get_remote_port(); -} void socket_get_remote_ep_udp(socket_impl_t sh, net_l4_endpoint* out) { if (!sh || !out) return; *out = reinterpret_cast(sh)->get_remote_ep(); } -uint8_t socket_get_protocol_udp(socket_impl_t sh) { - if (!sh) return 0xFF; - return reinterpret_cast(sh)->get_protocol(); -} - -uint8_t socket_get_role_udp(socket_impl_t sh) { - if (!sh) return 0xFF; - return reinterpret_cast(sh)->get_role(); -} - -bool socket_is_bound_udp(socket_impl_t sh) { - if (!sh) return false; - return reinterpret_cast(sh)->is_bound(); -} - bool socket_is_connected_udp(socket_impl_t sh) { if (!sh) return false; return reinterpret_cast(sh)->is_connected(); diff --git a/kernel/networking/transport_layer/csocket_udp.h b/kernel/networking/transport_layer/csocket_udp.h index a697bc7a..755a7dc5 100644 --- a/kernel/networking/transport_layer/csocket_udp.h +++ b/kernel/networking/transport_layer/csocket_udp.h @@ -9,23 +9,20 @@ extern "C" { #endif -socket_impl_t udp_socket_create(ksocket_t* owner, uint8_t role, uint32_t pid, const SocketExtraOptions* extra); +socket_impl_t udp_socket_create(ksocket_t* owner, const SocketExtraOptions* extra); int32_t socket_bind_udp(socket_impl_t sh, const SockBindSpec* spec, uint16_t port); +int32_t socket_connect_udp(socket_impl_t sh, const net_l4_endpoint* dst); int64_t socket_sendto_udp(socket_impl_t sh, const net_l4_endpoint* dst, const void* buf, uint64_t len); int64_t socket_recvfrom_udp(socket_impl_t sh, void* buf, uint64_t len, net_l4_endpoint* out_src); int32_t socket_close_udp(socket_impl_t sh); void socket_destroy_udp(socket_impl_t sh); int32_t socket_setopt_udp(socket_impl_t sh, int32_t opt, const void* value, uint32_t len); int32_t socket_getopt_udp(socket_impl_t sh, int32_t opt, void* value, uint32_t* len); -uint32_t socket_udp_input(ksocket_t* socket, uint8_t ifindex, uint8_t l3_id, ip_version_t ipver, const void* src_ip_addr, const void* dst_ip_addr, netpkt_t* pkt, uint16_t src_port, uint16_t dst_port); +uint32_t socket_udp_input(ksocket_t* socket, ip_version_t ipver, const void* src_ip_addr, const void* dst_ip_addr, netpkt_t* pkt, uint16_t src_port, uint16_t dst_port); uint16_t socket_get_local_port_udp(socket_impl_t sh); -uint16_t socket_get_remote_port_udp(socket_impl_t sh); void socket_get_remote_ep_udp(socket_impl_t sh, net_l4_endpoint* out); -uint8_t socket_get_protocol_udp(socket_impl_t sh); -uint8_t socket_get_role_udp(socket_impl_t sh); -bool socket_is_bound_udp(socket_impl_t sh); bool socket_is_connected_udp(socket_impl_t sh); #ifdef __cplusplus diff --git a/kernel/networking/transport_layer/socket.hpp b/kernel/networking/transport_layer/socket.hpp index dd4b2c5a..a37c203e 100644 --- a/kernel/networking/transport_layer/socket.hpp +++ b/kernel/networking/transport_layer/socket.hpp @@ -17,22 +17,17 @@ class Socket { 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; ksocket_t* ownerSocket = nullptr; SocketExtraOptions extraOpts = {}; SockBindSpec bindSpec = {}; + socket_bind_token_t bindToken = 0; - - Socket(ksocket_t* owner, uint8_t protocol, uint8_t r, const SocketExtraOptions* extra) : proto(protocol), role(r), ownerSocket(owner) { + Socket(ksocket_t* owner, const SocketExtraOptions* extra) : ownerSocket(owner) { if (extra) extraOpts = *extra; } - public: virtual ~Socket() { close(); } @@ -40,12 +35,13 @@ class Socket { const SocketExtraOptions* get_extra_options() const { return &extraOpts; } int32_t set_option(int32_t opt, const void* value, uint32_t len) { - if (!value || len != sizeof(uint32_t)) return SOCK_ERR_INVAL; - - uint32_t v = 0; - memcpy(&v, value, sizeof(v)); + uint32_t v = 1; + if (value) { + if (len != sizeof(uint32_t)) return SOCK_ERR_INVAL; + memcpy(&v, value, sizeof(v)); + } else if (len || (opt != SOCK_OPT_DONTFRAG && opt != SOCK_OPT_BROADCAST_ALLOWED)) return SOCK_ERR_INVAL; - switch ((uint32_t)opt) { + switch (opt) { case SOCK_OPT_RECV_TIMEOUT: extraOpts.recv_timeout_ms = v; if (v) extraOpts.flags |= SOCK_OPT_RECV_TIMEOUT; @@ -71,6 +67,10 @@ class Socket { if (v) extraOpts.flags |= SOCK_OPT_DONTFRAG; else extraOpts.flags &= ~SOCK_OPT_DONTFRAG; return SOCK_OK; + case SOCK_OPT_BROADCAST_ALLOWED: + if (v) extraOpts.flags |= SOCK_OPT_BROADCAST_ALLOWED; + else extraOpts.flags &= ~SOCK_OPT_BROADCAST_ALLOWED; + return SOCK_OK; case SOCK_OPT_TTL: if (v > 255) return SOCK_ERR_INVAL; extraOpts.ttl = (uint8_t)v; @@ -83,7 +83,7 @@ class Socket { } int32_t get_option(int32_t opt, void* value, uint32_t* len) const { - if (!value || !len || *len < sizeof(uint32_t)) return SOCK_ERR_INVAL; + if (!len) return SOCK_ERR_INVAL; uint32_t v = 0; switch ((uint32_t)opt) { @@ -102,6 +102,9 @@ class Socket { case SOCK_OPT_DONTFRAG: v = (extraOpts.flags & SOCK_OPT_DONTFRAG) != 0; break; + case SOCK_OPT_BROADCAST_ALLOWED: + v = (extraOpts.flags & SOCK_OPT_BROADCAST_ALLOWED) != 0; + break; case SOCK_OPT_TTL: v = extraOpts.ttl; break; @@ -109,6 +112,11 @@ class Socket { return SOCK_ERR_INVAL; } + if (!value) { + *len = sizeof(uint32_t); + return SOCK_OK; + } + if (*len < sizeof(uint32_t)) return SOCK_ERR_INVAL; memcpy(value, &v, sizeof(v)); *len = sizeof(uint32_t); return SOCK_OK; @@ -117,23 +125,21 @@ class Socket { virtual int32_t bind(const SockBindSpec& spec, uint16_t port) = 0; virtual int32_t close() { - if (ownerSocket) socket_bind_remove_socket(ownerSocket); - bound = false; + if (bindToken) { + socket_bind_remove(bindToken); + bindToken = 0; + } localPort = 0; + memset(&bindSpec, 0, sizeof(bindSpec)); connected = false; remoteEP.port = 0; remoteEP.ver = IP_VER4; memset(remoteEP.ip, 0, 16); - memset(&bindSpec, 0, sizeof(bindSpec)); return SOCK_OK; } uint16_t get_local_port() const { return localPort; } 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; } diff --git a/kernel/networking/transport_layer/socket_bind.c b/kernel/networking/transport_layer/socket_bind.c index 607e41cc..be10e866 100644 --- a/kernel/networking/transport_layer/socket_bind.c +++ b/kernel/networking/transport_layer/socket_bind.c @@ -3,21 +3,91 @@ #include "networking/interface_manager.h" #include "networking/internet_layer/ipv4_utils.h" #include "networking/internet_layer/ipv6_utils.h" +#include "networking/internet_layer/ipv4_route.h" +#include "networking/internet_layer/ipv6_route.h" #include "random/random.h" #include "std/memory.h" +#include "data/struct/hashmap.h" #define SOCKET_PORT_MIN_EPHEMERAL 49152u #define SOCKET_PORT_MAX_EPHEMERAL 65535u +#define SOCKET_BIND_LIST_END 0xFFFF typedef struct socket_bind_entry { bool used; protocol_t protocol; uint16_t port; + uint16_t next; + uint32_t generation; SockBindSpec spec; ksocket_t* socket; } socket_bind_entry_t; static socket_bind_entry_t bind_entries[SOCKET_BIND_MAX]; +static hash_map_t* bind_map = NULL; +static uint16_t bind_next_alloc = 0; + +static void socket_bind_map_init(void) { + if (bind_map) return; + bind_map = hash_map_create(256); + bind_next_alloc = 0; + for (uint32_t i = 0; i < SOCKET_BIND_MAX; ++i) bind_entries[i].next = SOCKET_BIND_LIST_END; +} + +static uint16_t socket_bind_head(protocol_t protocol, uint16_t port) { + uint32_t key = ((uint32_t)protocol << 16) | port; + void* value = hash_map_get(bind_map, &key, sizeof(key)); + if (!value) return SOCKET_BIND_LIST_END; + return (uint16_t)((uintptr_t)value - 1); +} + +static bool socket_bind_set_head(protocol_t protocol, uint16_t port, uint16_t head) { + uint32_t key = ((uint32_t)protocol << 16) | port; + if (head == SOCKET_BIND_LIST_END) { + void* old = NULL; + hash_map_remove(bind_map, &key, sizeof(key), &old); + return true; + } + return hash_map_put(bind_map, &key, sizeof(key), (void*)(uintptr_t)(head + 1)) >= 0; +} + +bool socket_bind_normalize_spec(SockBindSpec* spec) { + if (!spec) return false; + + if (spec->kind == BIND_L3 && !spec->l3_id && !spec->ifindex && !spec->ver && ipv6_is_unspecified(spec->ip)) { + spec->kind = BIND_ANY; + return true; + } + + if (spec->kind != BIND_IP) return true; + + if (spec->ver == IP_VER4) { + uint32_t ip = 0; + memcpy(&ip, spec->ip, 4); + if (ipv4_is_unspecified(ip)) { + spec->kind = BIND_ANY4; + return true; + } + if (ipv4_is_multicast(ip) || ipv4_is_limited_broadcast(ip)) return false; + return true; + } + + if (spec->ver == IP_VER6) { + if (ipv6_is_unspecified(spec->ip)) { + spec->kind = BIND_ANY6; + return true; + } + if (ipv6_is_multicast(spec->ip)) return false; + return true; + } + + if (!spec->ver && ipv6_is_unspecified(spec->ip)) { + spec->kind = BIND_ANY; + return true; + } + + return false; +} static bool socket_bind_l3_valid(ip_version_t ver, uint8_t l3_id) { if (!l3_id) return false; @@ -35,12 +105,12 @@ static bool socket_bind_l3_valid(ip_version_t ver, uint8_t l3_id) { return false; } -uint32_t socket_bind_l3_list(const SockBindSpec* spec, ip_version_t ver, uint8_t* out, uint32_t cap) { +uint32_t socket_bind_select_l3(const SockBindSpec* spec, ip_version_t ver, uint8_t* out, uint32_t cap) { if (!spec || !out || !cap || (ver != IP_VER4 && ver != IP_VER6)) return 0; uint32_t count = 0; SockBindSpec normal = *spec; - if (normal.kind == BIND_L3 && normal.l3_id == 0 && normal.ifindex == 0 && normal.ver == 0 && ipv6_is_unspecified(normal.ip)) normal.kind = BIND_ANY; + if (!socket_bind_normalize_spec(&normal)) return 0; if (normal.kind == BIND_ANY4 && ver != IP_VER4) return 0; if (normal.kind == BIND_ANY6 && ver != IP_VER6) return 0; @@ -55,13 +125,15 @@ uint32_t socket_bind_l3_list(const SockBindSpec* spec, ip_version_t ver, uint8_t if (ver == IP_VER4) { uint32_t ip = 0; memcpy(&ip, normal.ip, 4); - if (!ipv4_is_unspecified(ip) && !ipv4_is_multicast(ip)) { + if (ipv4_is_multicast(ip) || ipv4_is_limited_broadcast(ip)) return 0; + if (!ipv4_is_unspecified(ip)) { l3_ipv4_interface_t* v4 = l3_ipv4_find_by_ip(ip); if (v4 && socket_bind_l3_valid(IP_VER4, v4->l3_id)) out[count++] = v4->l3_id; return count; } } else { - if (!ipv6_is_unspecified(normal.ip) && !ipv6_is_multicast(normal.ip)) { + if (ipv6_is_multicast(normal.ip)) return 0; + if (!ipv6_is_unspecified(normal.ip)) { l3_ipv6_interface_t* v6 = l3_ipv6_find_by_ip(normal.ip); if (v6 && socket_bind_l3_valid(IP_VER6, v6->l3_id)) out[count++] = v6->l3_id; return count; @@ -93,147 +165,181 @@ uint32_t socket_bind_l3_list(const SockBindSpec* spec, ip_version_t ver, uint8_t return count; } -bool socket_bind_insert(ksocket_t* socket, protocol_t protocol, const SockBindSpec* spec, uint16_t port) { +bool socket_bind_insert(ksocket_t* socket, protocol_t protocol, const SockBindSpec* spec, uint16_t port, socket_bind_token_t* out_token) { + if (out_token) *out_token = 0; if (!socket || !spec || !port) return false; if (protocol != PROTO_TCP && protocol != PROTO_UDP) return false; SockBindSpec normal = *spec; - if (normal.kind == BIND_L3 && normal.l3_id == 0 && normal.ifindex == 0 && normal.ver == 0 && ipv6_is_unspecified(normal.ip)) normal.kind = BIND_ANY; - - irq_flags_t irq = irq_save_disable(); //TODO lock - - for (uint32_t i = 0; i < SOCKET_BIND_MAX; ++i) { - socket_bind_entry_t* e = &bind_entries[i]; - if (!e->used) continue; - if (e->protocol != protocol || e->port != port) continue; - - ip_version_t a_family = normal.kind == BIND_ANY4 ? IP_VER4 : (normal.kind == BIND_ANY6 ? IP_VER6 : normal.ver); - ip_version_t b_family = e->spec.kind == BIND_ANY4 ? IP_VER4 : (e->spec.kind == BIND_ANY6 ? IP_VER6 : e->spec.ver); + if (!socket_bind_normalize_spec(&normal)) return false; + + socket_bind_map_init(); + if (!bind_map) return false; + + irq_flags_t irq = irq_save_disable(); + uint16_t head = socket_bind_head(protocol, port); + for (uint16_t idx = head; idx != SOCKET_BIND_LIST_END; idx = bind_entries[idx].next) { + socket_bind_entry_t* e = &bind_entries[idx]; + if (!e->used || e->protocol != protocol || e->port != port) continue; + + ip_version_t a_family = 0; + ip_version_t b_family = 0; + if (normal.kind == BIND_ANY4) a_family = IP_VER4; + else if (normal.kind == BIND_ANY6) a_family = IP_VER6; + else a_family = normal.ver; + if (e->spec.kind == BIND_ANY4) b_family = IP_VER4; + else if (e->spec.kind == BIND_ANY6) b_family = IP_VER6; + else b_family = e->spec.ver; if (a_family && b_family && a_family != b_family) continue; bool overlap = true; if (normal.kind == BIND_IP && e->spec.kind == BIND_IP && a_family && b_family) { - bool a_unspec = true; - bool b_unspec = true; if (a_family == IP_VER4) { uint32_t av = 0; uint32_t bv = 0; memcpy(&av, normal.ip, 4); memcpy(&bv, e->spec.ip, 4); - a_unspec = ipv4_is_unspecified(av); - b_unspec = ipv4_is_unspecified(bv); - overlap = a_unspec || b_unspec || av == bv; - } else if (a_family == IP_VER6) { - a_unspec = ipv6_is_unspecified(normal.ip); - b_unspec = ipv6_is_unspecified(e->spec.ip); - overlap = a_unspec || b_unspec || memcmp(normal.ip, e->spec.ip, 16) == 0; - } + overlap = ipv4_is_unspecified(av) || ipv4_is_unspecified(bv) || av == bv; + } else if (a_family == IP_VER6) overlap = ipv6_is_unspecified(normal.ip) || ipv6_is_unspecified(e->spec.ip) || memcmp(normal.ip, e->spec.ip, 16) == 0; } else if (normal.kind == BIND_L3 && e->spec.kind == BIND_L3) overlap = normal.l3_id == e->spec.l3_id; else if (normal.kind == BIND_L2 && e->spec.kind == BIND_L2) overlap = normal.ifindex == e->spec.ifindex; + if (!overlap) continue; - if (overlap) { - irq_restore(irq); - return false; - } + irq_restore(irq); + return false; } + uint16_t idx = SOCKET_BIND_LIST_END; for (uint32_t i = 0; i < SOCKET_BIND_MAX; ++i) { - socket_bind_entry_t* e = &bind_entries[i]; - if (e->used) continue; - e->used = true; - e->protocol = protocol; - e->port = port; - e->spec = normal; - e->socket = socket; - socket_core_ref(socket); + uint16_t cand = (uint16_t)((bind_next_alloc + i) % SOCKET_BIND_MAX); + if (bind_entries[cand].used) continue; + idx = cand; + bind_next_alloc = (uint16_t)((cand + 1) % SOCKET_BIND_MAX); + break; + } + if (idx == SOCKET_BIND_LIST_END) { irq_restore(irq); - return true; + return false; + } + + socket_bind_entry_t* e = &bind_entries[idx]; + uint32_t generation = (e->generation + 1) & 0xFFFF; + if (!generation) generation = 1; + memset(e, 0, sizeof(*e)); + e->used = true; + e->protocol = protocol; + e->port = port; + e->generation = generation; + e->spec = normal; + e->socket = socket; + e->next = head; + if (!socket_bind_set_head(protocol, port, idx)) { + memset(e, 0, sizeof(*e)); + e->generation = generation; + e->next = SOCKET_BIND_LIST_END; + irq_restore(irq); + return false; } + socket_core_ref(socket); + if (out_token) *out_token = (generation << 16) | (uint32_t)(idx + 1); + irq_restore(irq); - return false; + return true; } -void socket_bind_remove_socket(ksocket_t* socket) { - if (!socket) return; - - ksocket_t* drop[SOCKET_BIND_MAX]; - uint32_t drop_count = 0; +void socket_bind_remove(socket_bind_token_t token) { + if (!token) return; - irq_flags_t irq = irq_save_disable(); //TODO lock - for (uint32_t i = 0; i < SOCKET_BIND_MAX; ++i) { - socket_bind_entry_t* e = &bind_entries[i]; - if (!e->used || e->socket != socket) continue; - drop[drop_count++] = e->socket; - memset(e, 0, sizeof(*e)); - } - irq_restore(irq); + uint32_t idxplus = (uint16_t)token; + uint32_t generation = token >> 16; + if (!idxplus || idxplus > SOCKET_BIND_MAX || !generation) return; - for (uint32_t i = 0; i < drop_count; ++i) socket_core_put(drop[i]); -} + uint16_t idx = (uint16_t)(idxplus - 1); + ksocket_t* drop = NULL; -bool socket_bind_port_busy(protocol_t protocol, ip_version_t ipver, uint8_t l3_id, uint16_t port) { - if (!port) return true; - if (protocol != PROTO_TCP && protocol != PROTO_UDP) return true; + socket_bind_map_init(); + if (!bind_map) return; irq_flags_t irq = irq_save_disable(); //TODO lock - for (uint32_t i = 0; i < SOCKET_BIND_MAX; ++i) { - socket_bind_entry_t* e = &bind_entries[i]; - if (!e->used) continue; - if (e->protocol != protocol || e->port != port) continue; - - ip_version_t family = e->spec.kind == BIND_ANY4 ? IP_VER4 : (e->spec.kind == BIND_ANY6 ? IP_VER6 : e->spec.ver); - if (family && family != ipver) continue; - if (e->spec.kind == BIND_L3 && e->spec.l3_id && e->spec.l3_id != l3_id) continue; + socket_bind_entry_t* e = &bind_entries[idx]; + if (e->used && e->generation == generation) { + uint16_t head = socket_bind_head(e->protocol, e->port); + uint16_t cur = head; + uint16_t prev = SOCKET_BIND_LIST_END; + while (cur != SOCKET_BIND_LIST_END) { + if (cur == idx) { + if (prev == SOCKET_BIND_LIST_END) head = bind_entries[cur].next; + else bind_entries[prev].next = bind_entries[cur].next; + socket_bind_set_head(e->protocol, e->port, head); + break; + } + prev = cur; + cur = bind_entries[cur].next; + } - irq_restore(irq); - return true; + drop = e->socket; + memset(e, 0, sizeof(*e)); + e->generation = generation; + e->next = SOCKET_BIND_LIST_END; + bind_next_alloc = idx; } irq_restore(irq); - return false; + + if (drop) socket_core_put(drop); } -int socket_bind_alloc_ephemeral_l3(ksocket_t* socket, protocol_t protocol, uint8_t l3_id, uint16_t pid) { - if (!socket || !l3_id) return -1; +int socket_bind_alloc_ephemeral(ksocket_t* socket, protocol_t protocol, const SockBindSpec* spec, socket_bind_token_t* out_token) { + if (out_token) *out_token = 0; + if (!socket || !spec) return -1; if (protocol != PROTO_TCP && protocol != PROTO_UDP) return -1; - ip_version_t ver = l3_is_v6_from_id(l3_id) ? IP_VER6 : IP_VER4; - rng_t rng; rng_init_random(&rng); uint32_t seed = rng_next32(&rng); uint32_t minp = SOCKET_PORT_MIN_EPHEMERAL; uint32_t maxp = SOCKET_PORT_MAX_EPHEMERAL; - uint32_t range = maxp - minp + 1u; + uint32_t range = maxp - minp + 1; uint32_t first = minp + (seed % range); - SockBindSpec spec; - memset(&spec, 0, sizeof(spec)); - spec.kind = BIND_L3; - spec.ver = ver; - spec.l3_id = l3_id; - for (uint32_t i = 0; i < range; ++i) { uint16_t port = (uint16_t)(minp + ((first - minp + i) % range)); - if (socket_bind_port_busy(protocol, ver, l3_id, port)) continue; - if (socket_bind_insert(socket, protocol, &spec, port)) return port; + if (socket_bind_insert(socket, protocol, spec, port, out_token)) return port; } return -1; } +int socket_bind_alloc_ephemeral_l3(ksocket_t* socket, protocol_t protocol, uint8_t l3_id, socket_bind_token_t* out_token) { + if (!socket || !l3_id) return -1; + + SockBindSpec spec; + memset(&spec, 0, sizeof(spec)); + spec.kind = BIND_L3; + spec.ver = l3_is_v6_from_id(l3_id) ? IP_VER6 : IP_VER4; + spec.l3_id = l3_id; + + return socket_bind_alloc_ephemeral(socket, protocol, &spec, out_token); +} + uint32_t socket_bind_collect(protocol_t protocol, ip_version_t ipver, uint8_t l3_id, uint8_t ifindex, const void* dst_ip_addr, uint16_t dst_port, ksocket_t** out, uint32_t out_cap) { if (!out || !out_cap || !dst_ip_addr) return 0; if (protocol != PROTO_TCP && protocol != PROTO_UDP) return 0; uint32_t count = 0; + socket_bind_map_init(); + if (!bind_map) return 0; + irq_flags_t irq = irq_save_disable(); //TODO lock - for (uint32_t i = 0; i < SOCKET_BIND_MAX && count < out_cap; ++i) { - socket_bind_entry_t* e = &bind_entries[i]; + for (uint16_t idx = socket_bind_head(protocol, dst_port); idx != SOCKET_BIND_LIST_END && count < out_cap; idx = bind_entries[idx].next) { + socket_bind_entry_t* e = &bind_entries[idx]; if (!e->used || e->protocol != protocol || e->port != dst_port || !e->socket) continue; - ip_version_t family = e->spec.kind == BIND_ANY4 ? IP_VER4 : (e->spec.kind == BIND_ANY6 ? IP_VER6 : e->spec.ver); + ip_version_t family = 0; + if (e->spec.kind == BIND_ANY4) family = IP_VER4; + else if (e->spec.kind == BIND_ANY6) family = IP_VER6; + else family = e->spec.ver; if (family && family != ipver) continue; if (e->spec.kind == BIND_L3 && e->spec.l3_id != l3_id) continue; @@ -257,4 +363,90 @@ uint32_t socket_bind_collect(protocol_t protocol, ip_version_t ipver, uint8_t l3 } irq_restore(irq); return count; +} + +bool socket_bind_build_ipv4_tx_plan(const SockBindSpec* spec, bool use_spec, uint32_t dst, ipv4_tx_plan_t* out) { + if (!out) return false; + + ip_tx_opts_t tx; + memset(&tx, 0, sizeof(tx)); + const ip_tx_opts_t* hint = NULL; + + if (use_spec) { + if (!spec) return 0; + + SockBindSpec normal = *spec; + if (!socket_bind_normalize_spec(&normal)) return false; + if (normal.kind == BIND_ANY6) return false; + if (normal.ver && normal.ver != IP_VER4 && normal.kind != BIND_ANY && normal.kind != BIND_ANY4) return false; + + if (normal.kind == BIND_L3) { + if (!socket_bind_l3_valid(IP_VER4, normal.l3_id)) return false; + tx.scope = IP_TX_BOUND_L3; + tx.index = normal.l3_id; + hint = &tx; + } else if (normal.kind == BIND_L2) { + l2_interface_t* l2 = l2_interface_find_by_index(normal.ifindex); + if (!l2 || !l2->is_up) return false; + tx.scope = IP_TX_BOUND_L2; + tx.index = normal.ifindex; + hint = &tx; + } else if (normal.kind == BIND_IP) { + uint32_t ip = 0; + memcpy(&ip, normal.ip, 4); + if (ipv4_is_multicast(ip) || ipv4_is_limited_broadcast(ip)) return false; + if (!ipv4_is_unspecified(ip)) { + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_ip(ip); + if (!v4 || !socket_bind_l3_valid(IP_VER4, v4->l3_id)) return false; + tx.scope = IP_TX_BOUND_L3; + tx.index = v4->l3_id; + hint = &tx; + } + } else if (normal.kind != BIND_ANY && normal.kind != BIND_ANY4) return false; + } + + if (!ipv4_build_tx_plan(dst, hint, out)) return false; + return ipv4_tx_plan_valid(out); +} + +bool socket_bind_build_ipv6_tx_plan(const SockBindSpec* spec, bool use_spec, const uint8_t dst[16], ipv6_tx_plan_t* out) { + if (!out || !dst) return false; + + ip_tx_opts_t tx; + memset(&tx, 0, sizeof(tx)); + const ip_tx_opts_t* hint = NULL; + + if (use_spec) { + if (!spec) return false; + + SockBindSpec normal = *spec; + if (!socket_bind_normalize_spec(&normal)) return false; + if (normal.kind == BIND_ANY4) return false; + if (normal.ver && normal.ver != IP_VER6 && normal.kind != BIND_ANY && normal.kind != BIND_ANY6) return false; + + if (normal.kind == BIND_L3) { + if (!socket_bind_l3_valid(IP_VER6, normal.l3_id)) return false; + tx.scope = IP_TX_BOUND_L3; + tx.index = normal.l3_id; + hint = &tx; + } else if (normal.kind == BIND_L2) { + l2_interface_t* l2 = l2_interface_find_by_index(normal.ifindex); + if (!l2 || !l2->is_up) return false; + tx.scope = IP_TX_BOUND_L2; + tx.index = normal.ifindex; + hint = &tx; + } else if (normal.kind == BIND_IP) { + if (ipv6_is_multicast(normal.ip)) return false; + if (!ipv6_is_unspecified(normal.ip)) { + l3_ipv6_interface_t* v6 = l3_ipv6_find_by_ip(normal.ip); + if (!v6 || !socket_bind_l3_valid(IP_VER6, v6->l3_id)) return false; + tx.scope = IP_TX_BOUND_L3; + tx.index = v6->l3_id; + hint = &tx; + } + } else if (normal.kind != BIND_ANY && normal.kind != BIND_ANY6) return false; + } + + if (!ipv6_build_tx_plan(dst, hint, out)) return false; + return ipv6_tx_plan_valid(out); } \ No newline at end of file diff --git a/kernel/networking/transport_layer/socket_bind.h b/kernel/networking/transport_layer/socket_bind.h index 8c8605da..ffe52219 100644 --- a/kernel/networking/transport_layer/socket_bind.h +++ b/kernel/networking/transport_layer/socket_bind.h @@ -4,6 +4,8 @@ #include "net/network_types.h" #include "net/socket_types.h" #include "networking/transport_layer/socket_core.h" +#include "networking/internet_layer/ipv4_route.h" +#include "networking/internet_layer/ipv6_route.h" #ifdef __cplusplus extern "C" { @@ -11,15 +13,19 @@ extern "C" { #define SOCKET_BIND_MAX 1024 #define SOCKET_BIND_COLLECT_MAX 32 +typedef uint32_t socket_bind_token_t; -bool socket_bind_insert(ksocket_t* socket, protocol_t protocol, const SockBindSpec* spec, uint16_t port); -void socket_bind_remove_socket(ksocket_t* socket); -int socket_bind_alloc_ephemeral_l3(ksocket_t* socket, protocol_t protocol, uint8_t l3_id, uint16_t pid); +bool socket_bind_normalize_spec(SockBindSpec* spec); +bool socket_bind_insert(ksocket_t* socket, protocol_t protocol, const SockBindSpec* spec, uint16_t port, socket_bind_token_t* out_token); +void socket_bind_remove(socket_bind_token_t token); +int socket_bind_alloc_ephemeral(ksocket_t* socket, protocol_t protocol, const SockBindSpec* spec, socket_bind_token_t* out_token); +int socket_bind_alloc_ephemeral_l3(ksocket_t* socket, protocol_t protocol, uint8_t l3_id, socket_bind_token_t* out_token); uint32_t socket_bind_collect(protocol_t protocol, ip_version_t ipver, uint8_t l3_id, uint8_t ifindex, const void* dst_ip_addr, uint16_t dst_port, ksocket_t** out, uint32_t out_cap); -bool socket_bind_port_busy(protocol_t protocol, ip_version_t ipver, uint8_t l3_id, uint16_t port); -uint32_t socket_bind_l3_list(const SockBindSpec* spec, ip_version_t ver, uint8_t* out, uint32_t cap); +uint32_t socket_bind_select_l3(const SockBindSpec* spec, ip_version_t ver, uint8_t* out, uint32_t cap); +bool socket_bind_build_ipv4_tx_plan(const SockBindSpec* spec, bool use_spec, uint32_t dst, ipv4_tx_plan_t* out); +bool socket_bind_build_ipv6_tx_plan(const SockBindSpec* spec, bool use_spec, const uint8_t dst[16], ipv6_tx_plan_t* out); #ifdef __cplusplus } diff --git a/kernel/networking/transport_layer/socket_core.c b/kernel/networking/transport_layer/socket_core.c index e510084c..ed377b01 100644 --- a/kernel/networking/transport_layer/socket_core.c +++ b/kernel/networking/transport_layer/socket_core.c @@ -6,9 +6,8 @@ struct ksocket { uint32_t id; - uint32_t generation; + uint64_t generation; uint16_t pid; - Socket_Role role; protocol_t protocol; socket_impl_t impl; socket_impl_destroy_fn destroy; @@ -21,9 +20,9 @@ struct ksocket { }; static ksocket_t* sockets[SOCKET_MAX_OPEN]; -static uint32_t generations[SOCKET_MAX_OPEN]; +static uint64_t generations[SOCKET_MAX_OPEN]; -bool socket_core_alloc(Socket_Role role, protocol_t protocol, uint16_t pid, ksocket_t** out_socket) { +bool socket_core_alloc(protocol_t protocol, uint16_t pid, ksocket_t** out_socket) { if (!out_socket) return false; if (protocol == PROTO_NONE) return false; @@ -46,14 +45,13 @@ bool socket_core_alloc(Socket_Role role, protocol_t protocol, uint16_t pid, ksoc return false; } - uint32_t gen = generations[id] + 1; + uint64_t gen = generations[id] + 1; if (!gen) gen = 1; generations[id] = gen; socket->id = id; socket->generation = gen; socket->pid = pid; - socket->role = role; socket->protocol = protocol; socket->refs = 1; sockets[id] = socket; @@ -64,7 +62,7 @@ bool socket_core_alloc(Socket_Role role, protocol_t protocol, uint16_t pid, ksoc return true; } -bool socket_core_attach_impl(ksocket_t* socket, socket_impl_t impl, socket_impl_destroy_fn destroy, socket_impl_close_fn close, socket_impl_setopt_fn setopt, socket_impl_getopt_fn getopt, SocketHandle* out_handle) { +bool socket_core_attach_impl(ksocket_t* socket, socket_impl_t impl, socket_impl_destroy_fn destroy, socket_impl_close_fn close, socket_impl_setopt_fn setopt, socket_impl_getopt_fn getopt) { if (!socket || !impl || !destroy || !close) return false; irq_flags_t irq = irq_save_disable(); //TODO lock if (socket->closing || socket->visible || socket->impl) { @@ -80,17 +78,20 @@ bool socket_core_attach_impl(ksocket_t* socket, socket_impl_t impl, socket_impl_ socket->visible = true; irq_restore(irq); - if (out_handle) socket_core_export_handle(socket, out_handle); return true; } -ksocket_t* socket_core_get(const SocketHandle* handle, uint16_t pid) { - if (!handle || handle->protocol == PROTO_NONE) return NULL; - if (handle->id >= SOCKET_MAX_OPEN) return NULL; +ksocket_t* socket_core_get(socket_handle_t handle, uint16_t pid) { + if (!handle) return NULL; + + uint64_t index_mask = ((uint64_t)1 << SOCKET_HANDLE_INDEX_BITS) - 1; + uint32_t id = (uint32_t)(handle & index_mask); + uint64_t generation = handle >> SOCKET_HANDLE_INDEX_BITS; + if (!id || id >= SOCKET_MAX_OPEN || !generation) return NULL; irq_flags_t irq = irq_save_disable(); //TODO lock - ksocket_t* socket = sockets[handle->id]; - if (!socket || !socket->visible || socket->closing || socket->generation != handle->generation || (pid && socket->pid != pid) || socket->protocol != handle->protocol) { + ksocket_t* socket = sockets[id]; + if (!socket || !socket->visible || socket->closing || socket->generation != generation || (pid && socket->pid != pid)) { irq_restore(irq); return NULL; } @@ -143,8 +144,6 @@ int32_t socket_core_close_socket(ksocket_t* socket) { irq_restore(irq); if (!first_close) return SOCK_OK; - socket_bind_remove_socket(socket); - int32_t ret = SOCK_OK; if (socket->close && socket->impl) ret = socket->close(socket->impl); @@ -152,28 +151,24 @@ int32_t socket_core_close_socket(ksocket_t* socket) { return ret; } -int32_t socket_core_close_handle(SocketHandle* handle, uint16_t pid) { +int32_t socket_core_close_handle(socket_handle_t handle, uint16_t pid) { ksocket_t* socket = socket_core_get(handle, pid); if (!socket) return SOCK_ERR_INVAL; int32_t ret = socket_core_close_socket(socket); socket_core_put(socket); - if (handle) { - handle->id = 0; - handle->generation = 0; - handle->protocol = PROTO_NONE; - handle->connection = (net_l4_endpoint){}; - } return ret; } int32_t socket_core_set_option(ksocket_t* socket, int32_t opt, const void* value, uint32_t len) { - if (!socket || !value || !len) return SOCK_ERR_INVAL; + if (!socket) return SOCK_ERR_INVAL; + if (!value && len) return SOCK_ERR_INVAL; if (!socket->setopt || !socket->impl) return SOCK_ERR_PROTO; return socket->setopt(socket->impl, opt, value, len); } int32_t socket_core_get_option(ksocket_t* socket, int32_t opt, void* value, uint32_t* len) { - if (!socket || !value || !len) return SOCK_ERR_INVAL; + if (!socket || !len) return SOCK_ERR_INVAL; + if (!value && *len) return SOCK_ERR_INVAL; if (!socket->getopt || !socket->impl) return SOCK_ERR_PROTO; return socket->getopt(socket->impl, opt, value, len); } @@ -186,30 +181,17 @@ protocol_t socket_core_protocol(const ksocket_t* socket) { return socket ? socket->protocol : PROTO_NONE; } -Socket_Role socket_core_role(const ksocket_t* socket) { - return socket ? socket->role : SOCKET_CLIENT; -} uint16_t socket_core_pid(const ksocket_t* socket) { return socket ? socket->pid : 0; } -uint32_t socket_core_id(const ksocket_t* socket) { - return socket ? socket->id : 0; -} - -uint32_t socket_core_generation(const ksocket_t* socket) { - return socket ? socket->generation : 0; -} bool socket_core_is_closing(const ksocket_t* socket) { return !socket || socket->closing; } -void socket_core_export_handle(const ksocket_t* socket, SocketHandle* out_handle) { - if (!socket || !out_handle) return; - out_handle->id = socket->id; - out_handle->generation = socket->generation; - out_handle->protocol = socket->protocol; - out_handle->connection = (net_l4_endpoint){}; +socket_handle_t socket_core_export_handle(const ksocket_t* socket) { + if (!socket || !socket->id || !socket->generation) return 0; + return (socket->generation << SOCKET_HANDLE_INDEX_BITS) | socket->id; } diff --git a/kernel/networking/transport_layer/socket_core.h b/kernel/networking/transport_layer/socket_core.h index 17a7be2d..258846a0 100644 --- a/kernel/networking/transport_layer/socket_core.h +++ b/kernel/networking/transport_layer/socket_core.h @@ -17,25 +17,23 @@ typedef int32_t (*socket_impl_setopt_fn)(socket_impl_t impl, int32_t opt, const typedef int32_t (*socket_impl_getopt_fn)(socket_impl_t impl, int32_t opt, void* value, uint32_t* len); #define SOCKET_MAX_OPEN 2048 +#define SOCKET_HANDLE_INDEX_BITS 20 -bool socket_core_alloc(Socket_Role role, protocol_t protocol, uint16_t pid, ksocket_t** out_socket); -bool socket_core_attach_impl(ksocket_t* socket, socket_impl_t impl, socket_impl_destroy_fn destroy, socket_impl_close_fn close, socket_impl_setopt_fn setopt, socket_impl_getopt_fn getopt, SocketHandle* out_handle); -ksocket_t* socket_core_get(const SocketHandle* handle, uint16_t pid); +bool socket_core_alloc(protocol_t protocol, uint16_t pid, ksocket_t** out_socket); +bool socket_core_attach_impl(ksocket_t* socket, socket_impl_t impl, socket_impl_destroy_fn destroy, socket_impl_close_fn close, socket_impl_setopt_fn setopt, socket_impl_getopt_fn getopt); +ksocket_t* socket_core_get(socket_handle_t handle, uint16_t pid); void socket_core_ref(ksocket_t* socket); void socket_core_put(ksocket_t* socket); -int32_t socket_core_close_handle(SocketHandle* handle, uint16_t pid); +int32_t socket_core_close_handle(socket_handle_t handle, uint16_t pid); int32_t socket_core_close_socket(ksocket_t* socket); int32_t socket_core_set_option(ksocket_t* socket, int32_t opt, const void* value, uint32_t len); int32_t socket_core_get_option(ksocket_t* socket, int32_t opt, void* value, uint32_t* len); socket_impl_t socket_core_impl(ksocket_t* socket); protocol_t socket_core_protocol(const ksocket_t* socket); -Socket_Role socket_core_role(const ksocket_t* socket); uint16_t socket_core_pid(const ksocket_t* socket); -uint32_t socket_core_id(const ksocket_t* socket); -uint32_t socket_core_generation(const ksocket_t* socket); bool socket_core_is_closing(const ksocket_t* socket); -void socket_core_export_handle(const ksocket_t* socket, SocketHandle* out_handle); +socket_handle_t socket_core_export_handle(const ksocket_t* socket); #ifdef __cplusplus } diff --git a/kernel/networking/transport_layer/socket_tcp.hpp b/kernel/networking/transport_layer/socket_tcp.hpp index 503ecad7..9419822d 100644 --- a/kernel/networking/transport_layer/socket_tcp.hpp +++ b/kernel/networking/transport_layer/socket_tcp.hpp @@ -22,9 +22,10 @@ class TCPSocket : public Socket { tcp_data flow = {}; - ksocket_t* pending[TCP_MAX_BACKLOG] = { nullptr }; + ksocket_t** pending = nullptr; int backlogCap = 0; int backlogLen = 0; + bool listening = false; static bool is_valid_v4_l3_for_bind(l3_ipv4_interface_t* v4) { if (!v4 || !v4->l2) return false; @@ -46,8 +47,8 @@ class TCPSocket : public Socket { } public: - uint32_t queue_accepted_child(uint8_t ifindex, ip_version_t ipver, const void* src_ip_addr, const void* dst_ip_addr, uint16_t src_port, uint16_t dst_port) { - if (role != SOCKET_SERVER || !bound || localPort != dst_port) return 0; + uint32_t queue_accepted_child(ip_version_t ipver, const void* src_ip_addr, const void* dst_ip_addr, uint16_t src_port, uint16_t dst_port) { + if (!listening || !localPort || localPort != dst_port || !pending) return 0; for (int i = 0; i < backlogLen;) { ksocket_t* owner = pending[i]; @@ -70,9 +71,10 @@ class TCPSocket : public Socket { if (backlogLen >= backlogCap) return 0; ksocket_t* child_owner = nullptr; - if (!socket_core_alloc(SOCKET_CLIENT, PROTO_TCP, pid, &child_owner)) return 0; + uint16_t owner_pid = socket_core_pid(ownerSocket); + if (!socket_core_alloc(PROTO_TCP, owner_pid, &child_owner)) return 0; - TCPSocket* child = new TCPSocket(child_owner, SOCKET_CLIENT, pid, &extraOpts); + TCPSocket* child = new TCPSocket(child_owner, &extraOpts); if (!child) { socket_core_close_socket(child_owner); return 0; @@ -80,7 +82,6 @@ class TCPSocket : public Socket { child->localPort = dst_port; - child->bound = true; child->remoteEP.ver = ipver; memset(child->remoteEP.ip, 0, 16); if (ipver == IP_VER4) memcpy(child->remoteEP.ip, src_ip_addr, 4); @@ -97,7 +98,7 @@ class TCPSocket : public Socket { return 0; } - if (!socket_core_attach_impl(child_owner, child, socket_destroy_tcp, socket_close_tcp, socket_setopt_tcp, socket_getopt_tcp, nullptr)) { + if (!socket_core_attach_impl(child_owner, child, socket_destroy_tcp, socket_close_tcp, socket_setopt_tcp, socket_getopt_tcp)) { delete child; socket_core_close_socket(child_owner); return 0; @@ -122,8 +123,7 @@ class TCPSocket : public Socket { } public: - explicit TCPSocket(ksocket_t* owner, uint8_t r = SOCKET_CLIENT, uint32_t pid_ = 0, const SocketExtraOptions* extra = nullptr) : Socket(owner, PROTO_TCP, r, extra) { - pid = pid_; + explicit TCPSocket(ksocket_t* owner, const SocketExtraOptions* extra = nullptr) : Socket(owner, extra) { if (!(extraOpts.flags & SOCK_OPT_BUF_SIZE)) { extraOpts.flags |= SOCK_OPT_BUF_SIZE; extraOpts.buf_size = TCP_DEFAULT_SOCKET_BUF; @@ -138,36 +138,47 @@ class TCPSocket : public Socket { } int32_t set_option(int32_t opt, const void* value, uint32_t len) { - if (!value || len != sizeof(uint32_t)) return SOCK_ERR_INVAL; - - uint32_t v = 0; - memcpy(&v, value, sizeof(v)); - switch ((uint32_t)opt) { - case SOCK_OPT_KEEPALIVE: + case SOCK_OPT_KEEPALIVE: { + if (!value || len != sizeof(uint32_t)) return SOCK_ERR_INVAL; + uint32_t v = 0; + memcpy(&v, value, sizeof(v)); if (v) { extraOpts.flags |= SOCK_OPT_KEEPALIVE; if (!extraOpts.keepalive_ms) extraOpts.keepalive_ms = SOCKET_DEFAULT_KEEPALIVE_MS; } else extraOpts.flags &= ~SOCK_OPT_KEEPALIVE; if (flow.flow_generation) tcp_flow_apply_socket_options(&flow, &extraOpts); return SOCK_OK; - case SOCK_OPT_KEEPALIVE_INTERVAL: + } + case SOCK_OPT_KEEPALIVE_INTERVAL: { + if (!value || len != sizeof(uint32_t)) return SOCK_ERR_INVAL; + uint32_t v = 0; + memcpy(&v, value, sizeof(v)); if (!v) return SOCK_ERR_INVAL; extraOpts.keepalive_ms = v; extraOpts.flags |= SOCK_OPT_KEEPALIVE | SOCK_OPT_KEEPALIVE_INTERVAL; if (flow.flow_generation) tcp_flow_apply_socket_options(&flow, &extraOpts); return SOCK_OK; - case SOCK_OPT_TCP_NO_DELAY: + } + case SOCK_OPT_TCP_NO_DELAY: { + if (!value || len != sizeof(uint32_t)) return SOCK_ERR_INVAL; + uint32_t v = 0; + memcpy(&v, value, sizeof(v)); if (v) extraOpts.flags |= SOCK_OPT_TCP_NO_DELAY; else extraOpts.flags &= ~SOCK_OPT_TCP_NO_DELAY; if (flow.flow_generation) tcp_flow_apply_socket_options(&flow, &extraOpts); return SOCK_OK; - case SOCK_OPT_SEND_BUF_SIZE: + } + case SOCK_OPT_SEND_BUF_SIZE: { + if (!value || len != sizeof(uint32_t)) return SOCK_ERR_INVAL; + uint32_t v = 0; + memcpy(&v, value, sizeof(v)); if (!v) return SOCK_ERR_INVAL; extraOpts.send_buf_size = v; extraOpts.flags |= SOCK_OPT_SEND_BUF_SIZE; if (flow.flow_generation) tcp_flow_apply_socket_options(&flow, &extraOpts); return SOCK_OK; + } case SOCK_OPT_MCAST_JOIN: return SOCK_ERR_INVAL; default: { @@ -179,7 +190,7 @@ class TCPSocket : public Socket { } int32_t get_option(int32_t opt, void* value, uint32_t* len) const { - if (!value || !len || *len < sizeof(uint32_t)) return SOCK_ERR_INVAL; + if (!len) return SOCK_ERR_INVAL; uint32_t v = 0; switch ((uint32_t)opt) { @@ -201,6 +212,11 @@ class TCPSocket : public Socket { return Socket::get_option(opt, value, len); } + if (!value) { + *len = sizeof(uint32_t); + return SOCK_OK; + } + if (*len < sizeof(uint32_t)) return SOCK_ERR_INVAL; memcpy(value, &v, sizeof(v)); *len = sizeof(uint32_t); return SOCK_OK; @@ -210,17 +226,15 @@ class TCPSocket : public Socket { netlog_socket_event_t ev{}; ev.comp = NETLOG_COMP_TCP; ev.action = NETLOG_ACT_BIND; - ev.pid = pid; + ev.pid = socket_core_pid(ownerSocket); ev.u0 = port; ev.bind_spec = spec_in; netlog_socket_event(&extraOpts, &ev); - if (role != SOCKET_SERVER) return SOCK_ERR_PERM; - if (bound) return SOCK_ERR_BOUND; + if (localPort) return SOCK_ERR_BOUND; if (!ownerSocket) return SOCK_ERR_SYS; SockBindSpec spec = spec_in; - bool empty = spec.kind == BIND_L3 && spec.l3_id == 0 && spec.ifindex == 0 && spec.ver == 0 && ipv6_is_unspecified(spec.ip); - if (empty) spec.kind = BIND_ANY; + if (!socket_bind_normalize_spec(&spec)) return SOCK_ERR_INVAL; if (spec.kind == BIND_L3){ if (!spec.l3_id) return SOCK_ERR_INVAL; @@ -242,24 +256,51 @@ class TCPSocket : public Socket { } else return SOCK_ERR_INVAL; } else if (spec.kind != BIND_ANY && spec.kind != BIND_ANY4 && spec.kind != BIND_ANY6) return SOCK_ERR_INVAL; - if (!socket_bind_insert(ownerSocket, PROTO_TCP, &spec, port)) return SOCK_ERR_SYS; + int bind_port = port; + socket_bind_token_t token = 0; + if (bind_port == 0) { + bind_port = socket_bind_alloc_ephemeral(ownerSocket, PROTO_TCP, &spec, &token); + if (bind_port < 0) return SOCK_ERR_NO_PORT; + } else if (!socket_bind_insert(ownerSocket, PROTO_TCP, &spec, port, &token)) return SOCK_ERR_BOUND; bindSpec = spec; - localPort = port; - bound = true; + bindToken = token; + localPort = (uint16_t)bind_port; return SOCK_OK; } int32_t listen(int max_backlog){ - if (!bound || role != SOCKET_SERVER) return SOCK_ERR_STATE; + if (!bindToken || connected) return SOCK_ERR_STATE; - backlogCap = max_backlog > TCP_MAX_BACKLOG ? TCP_MAX_BACKLOG : max_backlog; - if (backlogCap < 1) backlogCap = 1; + int cap = max_backlog > TCP_MAX_BACKLOG ? TCP_MAX_BACKLOG : max_backlog; + if (cap < 1) cap = 1; + + if (pending) { + for (int i = 0; i < backlogLen; ++i) { + if (!pending[i]) continue; + socket_core_close_socket(pending[i]); + socket_core_put(pending[i]); + } + release(pending); + } + + pending = (ksocket_t**)zalloc(sizeof(ksocket_t*) * cap); + if (!pending) { + backlogCap = 0; + backlogLen = 0; + listening = false; + return SOCK_ERR_SYS; + } + + backlogCap = cap; backlogLen = 0; + listening = true; return SOCK_OK; } ksocket_t* accept(){ + if (!listening || !pending) return nullptr; + //TODO replace polling with a socket wait queue when kernel events are present const int max_empty_iters = 200; const int ready_wait_iters = 4; int iter = 0; @@ -318,32 +359,24 @@ class TCPSocket : public Socket { netlog_socket_event_t ev{}; ev.comp = NETLOG_COMP_TCP; ev.action = NETLOG_ACT_CONNECT; - ev.pid = pid; + ev.pid = socket_core_pid(ownerSocket); if (dst) ev.dst_ep = *dst; netlog_socket_event(&extraOpts, &ev); - if (role != SOCKET_CLIENT) return SOCK_ERR_PERM; - if (connected) return SOCK_ERR_STATE; + if (connected || listening) return SOCK_ERR_STATE; if (!dst || !dst->port) return SOCK_ERR_INVAL; net_l4_endpoint d = *dst; uint8_t chosen_l3 = 0; - uint8_t allow_v4[MAX_L2_INTERFACES * MAX_IPV4_PER_INTERFACE]; - uint8_t allow_v6[MAX_L2_INTERFACES * MAX_IPV6_PER_INTERFACE]; - uint32_t n4 = bound ? socket_bind_l3_list(&bindSpec, IP_VER4, allow_v4, MAX_L2_INTERFACES * MAX_IPV4_PER_INTERFACE) : 0; - uint32_t n6 = bound ? socket_bind_l3_list(&bindSpec, IP_VER6, allow_v6, MAX_L2_INTERFACES * MAX_IPV6_PER_INTERFACE) : 0; - if (d.ver == IP_VER6) { - if (bound && n6 == 0) return SOCK_ERR_SYS; ipv6_tx_plan_t p6; - if (!ipv6_build_tx_plan(d.ip, nullptr, n6 ? allow_v6 : nullptr, n6, &p6)) return SOCK_ERR_SYS; + if (!socket_bind_build_ipv6_tx_plan(&bindSpec, localPort, d.ip, &p6)) return SOCK_ERR_SYS; chosen_l3 = p6.l3_id; } else if (d.ver == IP_VER4) { - if (bound && n4 == 0) return SOCK_ERR_SYS; uint32_t dip = 0; memcpy(&dip, d.ip, 4); ipv4_tx_plan_t p4; - if (!ipv4_build_tx_plan(dip, nullptr, n4 ? allow_v4 : nullptr, n4, &p4)) return SOCK_ERR_SYS; + if (!socket_bind_build_ipv4_tx_plan(&bindSpec, localPort, dip, &p4)) return SOCK_ERR_SYS; chosen_l3 = p4.l3_id; } else return SOCK_ERR_INVAL; @@ -357,35 +390,37 @@ class TCPSocket : public Socket { if (!is_valid_v6_l3_for_bind(v6)) return SOCK_ERR_SYS; } else return SOCK_ERR_INVAL; + bool ephemeral_allocated = false; if (localPort == 0) { if (!ownerSocket) return SOCK_ERR_SYS; - int p = socket_bind_alloc_ephemeral_l3(ownerSocket, PROTO_TCP, chosen_l3, pid); + socket_bind_token_t token = 0; + int p = socket_bind_alloc_ephemeral_l3(ownerSocket, PROTO_TCP, chosen_l3, &token); if (p < 0) return SOCK_ERR_NO_PORT; localPort = (uint16_t)p; + bindToken = token; + memset(&bindSpec, 0, sizeof(bindSpec)); + bindSpec.kind = BIND_L3; + bindSpec.ver = d.ver; + bindSpec.l3_id = chosen_l3; + ephemeral_allocated = true; } - bindSpec = {}; - bindSpec.kind = BIND_L3; - bindSpec.ver = d.ver; - bindSpec.l3_id = chosen_l3; - bound = true; - flow = tcp_data{}; - if (!tcp_handshake_l3(chosen_l3, localPort, &d, &flow, pid, &extraOpts)) { - Socket::close(); + if (!tcp_handshake_l3(chosen_l3, localPort, &d, &flow, &extraOpts)) { + if (ephemeral_allocated) Socket::close(); return SOCK_ERR_SYS; } if (d.ver == IP_VER4) { l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(chosen_l3); if (!is_valid_v4_l3_for_bind(v4)) { - Socket::close(); + if (ephemeral_allocated) Socket::close(); return SOCK_ERR_SYS; } } else { l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(chosen_l3); if (!is_valid_v6_l3_for_bind(v6)) { - Socket::close(); + if (ephemeral_allocated) Socket::close(); return SOCK_ERR_SYS; } } @@ -395,7 +430,7 @@ class TCPSocket : public Socket { netlog_socket_event_t ev1{}; ev1.comp = NETLOG_COMP_TCP; ev1.action = NETLOG_ACT_CONNECTED; - ev1.pid = pid; + ev1.pid = socket_core_pid(ownerSocket); ev1.u0 = localPort; ev1.u1 = remoteEP.port; ev1.local_port = localPort; @@ -408,7 +443,7 @@ class TCPSocket : public Socket { netlog_socket_event_t ev{}; ev.comp = NETLOG_COMP_TCP; ev.action = NETLOG_ACT_SEND; - ev.pid = pid; + ev.pid = socket_core_pid(ownerSocket); ev.u0 = (uint32_t)len; ev.local_port = localPort; ev.remote_ep = remoteEP; @@ -432,7 +467,7 @@ class TCPSocket : public Socket { netlog_socket_event_t ev{}; ev.comp = NETLOG_COMP_TCP; ev.action = NETLOG_ACT_RECV; - ev.pid = pid; + ev.pid = socket_core_pid(ownerSocket); ev.u0 = (uint32_t)len; ev.local_port = localPort; ev.remote_ep = remoteEP; @@ -451,13 +486,12 @@ class TCPSocket : public Socket { netlog_socket_event_t ev{}; ev.comp = NETLOG_COMP_TCP; ev.action = NETLOG_ACT_CLOSE; - ev.pid = pid; + ev.pid = socket_core_pid(ownerSocket); ev.local_port = localPort; ev.remote_ep = remoteEP; netlog_socket_event(&extraOpts, &ev); if (connected && flow.flow_generation){ - if (tcp_flow_is_closed(&flow)) tcp_flow_release_closed(&flow); - else { + if (!tcp_flow_is_closed(&flow)) { tcp_flow_flush(&flow); tcp_flow_close(&flow); } @@ -465,13 +499,19 @@ class TCPSocket : public Socket { flow = tcp_data{}; } - for (int i = 0; i < backlogLen; ++i) { - if (!pending[i]) continue; - socket_core_close_socket(pending[i]); - socket_core_put(pending[i]); - pending[i] = nullptr; + if (pending) { + for (int i = 0; i < backlogLen; ++i) { + if (!pending[i]) continue; + socket_core_close_socket(pending[i]); + socket_core_put(pending[i]); + pending[i] = nullptr; + } + release(pending); + pending = nullptr; } + backlogCap = 0; backlogLen = 0; + listening = false; return Socket::close(); } diff --git a/kernel/networking/transport_layer/socket_udp.hpp b/kernel/networking/transport_layer/socket_udp.hpp index e25a7ba2..dcbdba14 100644 --- a/kernel/networking/transport_layer/socket_udp.hpp +++ b/kernel/networking/transport_layer/socket_udp.hpp @@ -11,16 +11,22 @@ #include "networking/internet_layer/ipv6_utils.h" #include "networking/transport_layer/trans_utils.h" #include "networking/internet_layer/ipv6.h" +#include "networking/network.h" +#include "networking/interface_manager.h" #include "exceptions/irq.h" #include "sysregs.h" -static constexpr int32_t UDP_RING_CAP = 1024; +static constexpr uint32_t UDP_DEFAULT_RING_CAP = 64; +static constexpr uint32_t UDP_MAX_RING_CAP = 1024; class UDPSocket : public Socket { - sizedptr ring[UDP_RING_CAP]; - net_l4_endpoint src_eps[UDP_RING_CAP]; - int32_t r_head = 0; - int32_t r_tail = 0; + netpkt_t** ring = nullptr; + net_l4_endpoint* src_eps = nullptr; + net_l4_endpoint* mcast_groups = nullptr; + uint8_t mcast_count = 0; + uint32_t ring_cap = 0; + uint32_t r_head = 0; + uint32_t r_tail = 0; uint32_t rx_bytes = 0; @@ -43,13 +49,10 @@ class UDPSocket : public Socket { static bool udp_mcast_match(UDPSocket* s, ip_version_t ver, const void* dst_ip_addr) { if (!s || !dst_ip_addr) return false; - if ((s->extraOpts.flags & SOCK_OPT_MCAST_JOIN) == 0) return false; + if (!s->mcast_groups || !s->mcast_count) return false; - uint32_t count = s->extraOpts.mcast_count; - if (count > SOCK_MAX_MCAST_GROUPS) count = SOCK_MAX_MCAST_GROUPS; - - for (uint32_t i = 0; i < count; ++i) { - const net_l4_endpoint* group = &s->extraOpts.mcast_groups[i]; + for (uint32_t i = 0; i < s->mcast_count; ++i) { + const net_l4_endpoint* group = &s->mcast_groups[i]; if (group->ver != ver) continue; if (ver == IP_VER4) { @@ -66,80 +69,65 @@ class UDPSocket : public Socket { return false; } - 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) continue; - if (!v4->mask) continue; - uint32_t b = ipv4_broadcast_calc(v4->ip, v4->mask); - if (b != ip) continue; - if (out_l3) *out_l3 = v4->l3_id; - return true; - } - } - return false; - } - void on_receive(ip_version_t ver, const void* src_ip_addr, uint16_t src_port, netpkt_t* pkt) { if (!pkt) return; - uintptr_t ptr = netpkt_data(pkt); uint32_t len = netpkt_len(pkt); uint32_t limit = 0xFFFFFFFFu; if ((extraOpts.flags & SOCK_OPT_BUF_SIZE) && extraOpts.buf_size) limit = extraOpts.buf_size; if (len > limit) return; if (rx_bytes > limit - len) return; - int nexti = (r_tail + 1) % UDP_RING_CAP; - if (nexti == r_head) return; + if (!ring || !src_eps || !ring_cap) { + uint32_t usable = UDP_DEFAULT_RING_CAP; + if ((extraOpts.flags & SOCK_OPT_BUF_SIZE) && extraOpts.buf_size) { + usable = extraOpts.buf_size / MAX_PACKET_SIZE; + if (usable < 4) usable = 4; + if (usable > UDP_MAX_RING_CAP) usable = UDP_MAX_RING_CAP; + } - uintptr_t copy = 0; - if (len) { - copy = (uintptr_t)zalloc(len); - if (!copy) return; - memcpy((void*)copy, (const void*)ptr, len); + ring_cap = usable + 1; + ring = (netpkt_t**)zalloc(sizeof(netpkt_t*) * ring_cap); + src_eps = (net_l4_endpoint*)zalloc(sizeof(net_l4_endpoint) * ring_cap); + if (!ring || !src_eps) { + if (ring) release(ring); + if (src_eps) release(src_eps); + ring = nullptr; + src_eps = nullptr; + ring_cap = 0; + return; + } } - ring[r_tail].ptr = copy; - ring[r_tail].size = len; + uint32_t nexti = (r_tail + 1) % ring_cap; + if (nexti == r_head) return; + + netpkt_ref(pkt); + ring[r_tail] = pkt; rx_bytes += len; src_eps[r_tail].ver = ver; memset(src_eps[r_tail].ip, 0, 16); - if (ver == IP_VER4) { - memcpy(src_eps[r_tail].ip, src_ip_addr, 4); - } else if (ver == IP_VER6) { - memcpy(src_eps[r_tail].ip, src_ip_addr, 16); - } + if (ver == IP_VER4) memcpy(src_eps[r_tail].ip, src_ip_addr, 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]; } void leave_mcast_groups() { - if ((extraOpts.flags & SOCK_OPT_MCAST_JOIN) == 0) return; + if (!mcast_groups || !mcast_count || !localPort) return; - uint32_t count = extraOpts.mcast_count; - if (count > SOCK_MAX_MCAST_GROUPS) count = SOCK_MAX_MCAST_GROUPS; - - uint8_t ids[MAX_L2_INTERFACES * MAX_IPV6_PER_INTERFACE]; - for (uint32_t gidx = 0; gidx < count; ++gidx) { - const net_l4_endpoint* group = &extraOpts.mcast_groups[gidx]; + uint8_t ids[MAX_L3_INTERFACES]; + for (uint32_t gidx = 0; gidx < mcast_count; ++gidx) { + const net_l4_endpoint* group = &mcast_groups[gidx]; if (group->ver == IP_VER4) { uint32_t g = 0; memcpy(&g, group->ip, 4); if (!ipv4_is_multicast(g)) continue; - uint32_t n = socket_bind_l3_list(&bindSpec, IP_VER4, ids, MAX_L2_INTERFACES * MAX_IPV4_PER_INTERFACE); + uint32_t n = socket_bind_select_l3(&bindSpec, IP_VER4, ids, MAX_IPV4_L3_INTERFACES); for (uint32_t i = 0; i < n; ++i) { l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(ids[i]); if (!is_valid_v4_l3_for_bind(v4)) continue; @@ -149,7 +137,7 @@ class UDPSocket : public Socket { if (!ipv6_is_multicast(group->ip)) continue; bool linkscope = ipv6_is_linkscope_mcast(group->ip); - uint32_t n = socket_bind_l3_list(&bindSpec, IP_VER6, ids, MAX_L2_INTERFACES * MAX_IPV6_PER_INTERFACE); + uint32_t n = socket_bind_select_l3(&bindSpec, IP_VER6, ids, MAX_IPV6_L3_INTERFACES); for (uint32_t i = 0; i < n; ++i) { l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(ids[i]); if (!is_valid_v6_l3_for_bind(v6)) continue; @@ -160,9 +148,49 @@ class UDPSocket : public Socket { } } + int32_t join_mcast_groups() { + if (!mcast_groups || !mcast_count) return SOCK_OK; + if (!localPort) return SOCK_OK; + + uint8_t ids[MAX_L3_INTERFACES]; + for (uint32_t gidx = 0; gidx < mcast_count; ++gidx) { + const net_l4_endpoint* group = &mcast_groups[gidx]; + uint32_t n = 0; + bool joined = false; + + if (group->ver == IP_VER4) { + uint32_t g = 0; + memcpy(&g, group->ip, 4); + if (!ipv4_is_multicast(g)) return SOCK_ERR_INVAL; + n = socket_bind_select_l3(&bindSpec, IP_VER4, ids, MAX_IPV4_L3_INTERFACES); + for (uint32_t i = 0; i < n; ++i) { + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(ids[i]); + if (!is_valid_v4_l3_for_bind(v4)) continue; + if (l2_ipv4_mcast_join(v4->l2->ifindex, g)) joined = true; + } + } else if (group->ver == IP_VER6) { + if (!ipv6_is_multicast(group->ip)) return SOCK_ERR_INVAL; + bool linkscope = ipv6_is_linkscope_mcast(group->ip); + n = socket_bind_select_l3(&bindSpec, IP_VER6, ids, MAX_IPV6_L3_INTERFACES); + for (uint32_t i = 0; i < n; ++i) { + l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(ids[i]); + if (!is_valid_v6_l3_for_bind(v6)) continue; + if (linkscope && !ipv6_is_linklocal(v6->ip)) continue; + if (l2_ipv6_mcast_join(v6->l2->ifindex, group->ip)) joined = true; + } + } else return SOCK_ERR_INVAL; + + if (!joined) return SOCK_ERR_SYS; + } + + return SOCK_OK; + } + public: - UDPSocket(ksocket_t* owner, uint8_t r, uint32_t pid_, const SocketExtraOptions* extra = nullptr) : Socket(owner, PROTO_UDP, r, extra) { - pid = pid_; + UDPSocket(ksocket_t* owner, const SocketExtraOptions* extra = nullptr) : Socket(owner, extra) { + extraOpts.flags &= ~(SOCK_OPT_MCAST_JOIN | SOCK_OPT_MCAST_LEAVE); + extraOpts.mcast_count = 0; + extraOpts.mcast_groups = nullptr; } ~UDPSocket() override { @@ -173,24 +201,22 @@ class UDPSocket : public Socket { netlog_socket_event_t ev{}; ev.comp = NETLOG_COMP_UDP; ev.action = NETLOG_ACT_BIND; - ev.pid = pid; + ev.pid = socket_core_pid(ownerSocket); ev.u0 = port; ev.bind_spec = spec_in; netlog_socket_event(&extraOpts, &ev); - if (role != SOCKET_SERVER) return SOCK_ERR_PERM; - if (bound) return SOCK_ERR_BOUND; + if (localPort) return SOCK_ERR_BOUND; if (!ownerSocket) return SOCK_ERR_SYS; SockBindSpec spec = spec_in; - bool empty = spec.kind == BIND_L3 && spec.l3_id == 0 && spec.ifindex == 0 && spec.ver == 0 && ipv6_is_unspecified(spec.ip); - if (empty) spec.kind = BIND_ANY; + if (!socket_bind_normalize_spec(&spec)) return SOCK_ERR_INVAL; if (spec.kind == BIND_L3) { if (!spec.l3_id) return SOCK_ERR_INVAL; uint8_t one[1]; - if (spec.ver == IP_VER4 && !socket_bind_l3_list(&spec, IP_VER4, one, 1)) return SOCK_ERR_INVAL; - if (spec.ver == IP_VER6 && !socket_bind_l3_list(&spec, IP_VER6, one, 1)) return SOCK_ERR_INVAL; - if (!spec.ver && !socket_bind_l3_list(&spec, IP_VER4, one, 1) && !socket_bind_l3_list(&spec, IP_VER6, one, 1)) return SOCK_ERR_INVAL; + if (spec.ver == IP_VER4 && !socket_bind_select_l3(&spec, IP_VER4, one, 1)) return SOCK_ERR_INVAL; + if (spec.ver == IP_VER6 && !socket_bind_select_l3(&spec, IP_VER6, one, 1)) return SOCK_ERR_INVAL; + if (!spec.ver && !socket_bind_select_l3(&spec, IP_VER4, one, 1) && !socket_bind_select_l3(&spec, IP_VER6, one, 1)) return SOCK_ERR_INVAL; } else if (spec.kind == BIND_L2) { l2_interface_t* l2 = l2_interface_find_by_index(spec.ifindex); if (!l2 || !l2->is_up) return SOCK_ERR_INVAL; @@ -200,84 +226,83 @@ class UDPSocket : public Socket { memcpy(&v4ip, spec.ip, 4); if (!ipv4_is_unspecified(v4ip)) { uint8_t one[1]; - if (!socket_bind_l3_list(&spec, IP_VER4, one, 1)) return SOCK_ERR_INVAL; + if (!socket_bind_select_l3(&spec, IP_VER4, one, 1)) return SOCK_ERR_INVAL; } } else if (spec.ver == IP_VER6) { if (!ipv6_is_unspecified(spec.ip)) { uint8_t one[1]; - if (!socket_bind_l3_list(&spec, IP_VER6, one, 1)) return SOCK_ERR_INVAL; + if (!socket_bind_select_l3(&spec, IP_VER6, one, 1)) return SOCK_ERR_INVAL; } } else return SOCK_ERR_INVAL; } else if (spec.kind != BIND_ANY && spec.kind != BIND_ANY4 && spec.kind != BIND_ANY6) return SOCK_ERR_INVAL; - if (!socket_bind_insert(ownerSocket, PROTO_UDP, &spec, port)) return SOCK_ERR_SYS; + int bind_port = port; + socket_bind_token_t token = 0; + if (bind_port == 0) { + bind_port = socket_bind_alloc_ephemeral(ownerSocket, PROTO_UDP, &spec, &token); + if (bind_port < 0) return SOCK_ERR_NO_PORT; + } else if (!socket_bind_insert(ownerSocket, PROTO_UDP, &spec, port, &token)) return SOCK_ERR_BOUND; bindSpec = spec; - localPort = port; - bound = true; - - if (extraOpts.flags & SOCK_OPT_MCAST_JOIN) { - uint32_t count = extraOpts.mcast_count; - if (count > SOCK_MAX_MCAST_GROUPS) count = SOCK_MAX_MCAST_GROUPS; - uint8_t ids[MAX_L2_INTERFACES * MAX_IPV6_PER_INTERFACE]; - - for (uint32_t gidx = 0; gidx < count; ++gidx) { - const net_l4_endpoint* group = &extraOpts.mcast_groups[gidx]; - uint32_t n = 0; - bool joined = false; - - if (group->ver == IP_VER4) { - uint32_t g = 0; - memcpy(&g, group->ip, 4); - if (!ipv4_is_multicast(g)) continue; - n = socket_bind_l3_list(&bindSpec, IP_VER4, ids, MAX_L2_INTERFACES * MAX_IPV4_PER_INTERFACE); - for (uint32_t i = 0; i < n; ++i) { - l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(ids[i]); - if (!is_valid_v4_l3_for_bind(v4)) continue; - if (l2_ipv4_mcast_join(v4->l2->ifindex, g)) joined = true; - } - } else if (group->ver == IP_VER6) { - if (!ipv6_is_multicast(group->ip)) continue; - bool linkscope = ipv6_is_linkscope_mcast(group->ip); - n = socket_bind_l3_list(&bindSpec, IP_VER6, ids, MAX_L2_INTERFACES * MAX_IPV6_PER_INTERFACE); - for (uint32_t i = 0; i < n; ++i) { - l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(ids[i]); - if (!is_valid_v6_l3_for_bind(v6)) continue; - if (linkscope && !ipv6_is_linklocal(v6->ip)) continue; - if (l2_ipv6_mcast_join(v6->l2->ifindex, group->ip)) joined = true; - } - } - - if (!joined) { - leave_mcast_groups(); - socket_bind_remove_socket(ownerSocket); - memset(&bindSpec, 0, sizeof(bindSpec)); - bound = false; - localPort = 0; - return SOCK_ERR_SYS; - } - } + bindToken = token; + localPort = (uint16_t)bind_port; + + int32_t mcast_res = join_mcast_groups(); + if (mcast_res != SOCK_OK) { + leave_mcast_groups(); + socket_bind_remove(bindToken); + localPort = 0; + memset(&bindSpec, 0, sizeof(bindSpec)); + bindToken = 0; + return mcast_res; } return SOCK_OK; } + int32_t connect(const net_l4_endpoint* dst) { + netlog_socket_event_t ev{}; + ev.comp = NETLOG_COMP_UDP; + ev.action = NETLOG_ACT_CONNECT; + ev.pid = socket_core_pid(ownerSocket); + if (dst) ev.dst_ep = *dst; + netlog_socket_event(&extraOpts, &ev); + + if (!dst || !dst->port) return SOCK_ERR_INVAL; + if (dst->ver != IP_VER4 && dst->ver != IP_VER6) return SOCK_ERR_INVAL; + + remoteEP = *dst; + connected = true; + return SOCK_OK; + } + int64_t sendto(const net_l4_endpoint* dst, const void* buf, uint64_t len) { netlog_socket_event_t ev{}; ev.comp = NETLOG_COMP_UDP; ev.action = NETLOG_ACT_SENDTO; - ev.pid = pid; + ev.pid = socket_core_pid(ownerSocket); ev.u1 = (uint32_t)len; if (dst) { ev.dst_ep = *dst; ev.u0 = dst->port; } netlog_socket_event(&extraOpts, &ev); - if (!dst || !dst->port) return SOCK_ERR_INVAL; - if (!buf || !len) return SOCK_ERR_INVAL; - if (!bound && !ownerSocket) return SOCK_ERR_SYS; + bool explicit_dst = dst != nullptr; + if (!dst) { + if (!connected) return SOCK_ERR_STATE; + dst = &remoteEP; + } + if (!dst->port) return SOCK_ERR_INVAL; + if (!buf && len) return SOCK_ERR_INVAL; + if (!ownerSocket) return SOCK_ERR_SYS; net_l4_endpoint d = *dst; + if (connected && explicit_dst) { + if (d.ver != remoteEP.ver || d.port != remoteEP.port) return SOCK_ERR_STATE; + if (d.ver == IP_VER4 && memcmp(d.ip, remoteEP.ip, 4) != 0) return SOCK_ERR_STATE; + if (d.ver == IP_VER6 && memcmp(d.ip, remoteEP.ip, 16) != 0) return SOCK_ERR_STATE; + } + sizedptr pay; pay.ptr = (uintptr_t)buf; pay.size = (uint32_t)len; @@ -286,111 +311,85 @@ class UDPSocket : public Socket { uint32_t dip = 0; memcpy(&dip, d.ip, 4); - if (dip == 0xFFFFFFFFu || is_dbcast(dip, nullptr)) { - if (!bound || !localPort) return SOCK_ERR_BOUND; - - uint8_t ids[MAX_L2_INTERFACES * MAX_IPV4_PER_INTERFACE]; - uint32_t n = socket_bind_l3_list(&bindSpec, IP_VER4, ids, MAX_L2_INTERFACES * MAX_IPV4_PER_INTERFACE); - if (!n) return SOCK_ERR_SYS; - - if (dip == 0xFFFFFFFFu) { - for (uint32_t i = 0; i < n; ++i) { - l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(ids[i]); - if (!is_valid_v4_l3_for_bind(v4) || !v4->l2) continue; + bool limited_bcast = ipv4_is_limited_broadcast(dip); + uint8_t bcast_ids[MAX_IPV4_L3_INTERFACES]; + uint8_t chosen_l3 = 0; + l3_ipv4_interface_t* bcast_v4 = nullptr; - net_l4_endpoint src; - src.ver = IP_VER4; - memset(src.ip, 0, 16); - memcpy(src.ip, &v4->ip, 4); - src.port = localPort; + if (limited_bcast) { + if (!(extraOpts.flags & SOCK_OPT_BROADCAST_ALLOWED)) return SOCK_ERR_PERM; - ipv4_tx_opts_t tx; - tx.scope = IP_TX_BOUND_L3; - tx.index = ids[i]; + uint32_t n = socket_bind_select_l3(&bindSpec, IP_VER4, bcast_ids, MAX_IPV4_L3_INTERFACES); + uint32_t valid = 0; + for (uint32_t i = 0; i < n; ++i) { + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(bcast_ids[i]); + if (!is_valid_v4_l3_for_bind(v4) || v4->is_localhost) continue; - udp_send_segment(&src, &d, pay, &tx, (extraOpts.flags & SOCK_OPT_TTL) ? extraOpts.ttl : 0, (extraOpts.flags & SOCK_OPT_DONTFRAG) ? 1 : 0); + if (!v4->ip && v4->mode != IPV4_CFG_DHCP) continue; + if (!valid) { + chosen_l3 = bcast_ids[i]; + bcast_v4 = v4; } - - remoteEP = d; - return (int64_t)len; + valid++; } + if (valid != 1 || !bcast_v4) return SOCK_ERR_INVAL; + } else { + uint32_t n = socket_bind_select_l3(&bindSpec, IP_VER4, bcast_ids, MAX_IPV4_L3_INTERFACES); + for (uint32_t i = 0; i < n; ++i) { + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(bcast_ids[i]); + if (!is_valid_v4_l3_for_bind(v4) || !v4->ip || !v4->mask || v4->is_localhost) continue; + if (ipv4_broadcast_calc(v4->ip, v4->mask) != dip) continue; + chosen_l3 = bcast_ids[i]; + bcast_v4 = v4; + break; + } + if (bcast_v4 && !(extraOpts.flags & SOCK_OPT_BROADCAST_ALLOWED)) return SOCK_ERR_PERM; + } - uint8_t db_l3 = 0; - if (!is_dbcast(dip, &db_l3)) return SOCK_ERR_SYS; - bool allowed = false; - for (uint32_t i = 0; i < n; ++i) if (ids[i] == db_l3) allowed = true; - if (!allowed) return SOCK_ERR_SYS; - - l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(db_l3); - if (!is_valid_v4_l3_for_bind(v4) || !v4->l2) return SOCK_ERR_SYS; + if (bcast_v4) { + if (!localPort) { + socket_bind_token_t token = 0; + int p = socket_bind_alloc_ephemeral_l3(ownerSocket, PROTO_UDP, chosen_l3, &token); + if (p < 0) return SOCK_ERR_NO_PORT; + localPort = (uint16_t)p; + bindToken = token; + memset(&bindSpec, 0, sizeof(bindSpec)); + bindSpec.kind = BIND_L3; + bindSpec.ver = IP_VER4; + bindSpec.l3_id = chosen_l3; + } net_l4_endpoint src; src.ver = IP_VER4; memset(src.ip, 0, 16); - memcpy(src.ip, &v4->ip, 4); + memcpy(src.ip, &bcast_v4->ip, 4); src.port = localPort; ipv4_tx_opts_t tx; tx.scope = IP_TX_BOUND_L3; - tx.index = db_l3; + tx.index = chosen_l3; udp_send_segment(&src, &d, pay, &tx, (extraOpts.flags & SOCK_OPT_TTL) ? extraOpts.ttl : 0, (extraOpts.flags & SOCK_OPT_DONTFRAG) ? 1 : 0); - remoteEP = d; - return (int64_t)len; - } - - if (ipv4_is_multicast(dip)) { - if (!bound || !localPort) return SOCK_ERR_BOUND; - - uint8_t ids[MAX_L2_INTERFACES * MAX_IPV4_PER_INTERFACE]; - uint32_t n = socket_bind_l3_list(&bindSpec, IP_VER4, ids, MAX_L2_INTERFACES * MAX_IPV4_PER_INTERFACE); - if (!n) return SOCK_ERR_SYS; - - for (uint32_t i = 0; i < n; ++i) { - l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(ids[i]); - if (!is_valid_v4_l3_for_bind(v4) || !v4->l2) continue; - - net_l4_endpoint src; - src.ver = IP_VER4; - memset(src.ip, 0, 16); - memcpy(src.ip, &v4->ip, 4); - src.port = localPort; - - ipv4_tx_opts_t tx; - tx.scope = IP_TX_BOUND_L3; - tx.index = ids[i]; - - udp_send_segment(&src, &d, pay, &tx, (extraOpts.flags & SOCK_OPT_TTL) ? extraOpts.ttl : 0, (extraOpts.flags & SOCK_OPT_DONTFRAG) ? 1 : 0); - } - - remoteEP = d; return (int64_t)len; } - uint8_t allowed_v4[MAX_L2_INTERFACES * MAX_IPV4_PER_INTERFACE]; - uint32_t n_allowed = bound ? socket_bind_l3_list(&bindSpec, IP_VER4, allowed_v4, MAX_L2_INTERFACES * MAX_IPV4_PER_INTERFACE) : 0; - if (bound && n_allowed == 0) return SOCK_ERR_SYS; - ipv4_tx_plan_t plan; - if (!ipv4_build_tx_plan(dip, nullptr, n_allowed ? allowed_v4 : nullptr, n_allowed, &plan)) return SOCK_ERR_SYS; + if (!socket_bind_build_ipv4_tx_plan(&bindSpec, localPort, dip, &plan)) return SOCK_ERR_SYS; - uint8_t chosen_l3 = plan.l3_id; - l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(chosen_l3); + uint8_t tx_l3 = plan.l3_id; + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(tx_l3); if (!is_valid_v4_l3_for_bind(v4)) return SOCK_ERR_SYS; - if (!bound) { - int p = socket_bind_alloc_ephemeral_l3(ownerSocket, PROTO_UDP, chosen_l3, pid); + if (!localPort) { + socket_bind_token_t token = 0; + int p = socket_bind_alloc_ephemeral_l3(ownerSocket, PROTO_UDP, tx_l3, &token); if (p < 0) return SOCK_ERR_NO_PORT; localPort = (uint16_t)p; - bindSpec = {}; + bindToken = token; + memset(&bindSpec, 0, sizeof(bindSpec)); bindSpec.kind = BIND_L3; bindSpec.ver = IP_VER4; - bindSpec.l3_id = chosen_l3; - bound = true; - } else if (localPort == 0) { - int p = socket_bind_alloc_ephemeral_l3(ownerSocket, PROTO_UDP, chosen_l3, pid); - if (p < 0) return SOCK_ERR_NO_PORT; - localPort = (uint16_t)p; + bindSpec.l3_id = tx_l3; } net_l4_endpoint src; @@ -404,71 +403,28 @@ class UDPSocket : public Socket { tx.index = plan.fixed_opts.index; udp_send_segment(&src, &d, pay, &tx, (extraOpts.flags & SOCK_OPT_TTL) ? extraOpts.ttl : 0, (extraOpts.flags & SOCK_OPT_DONTFRAG) ? 1 : 0); - remoteEP = d; return (int64_t)len; } if (d.ver == IP_VER6) { - bool is_mcast = ipv6_is_multicast(d.ip); - - if (is_mcast) { - if (!bound || !localPort) return SOCK_ERR_BOUND; - - uint8_t ids[MAX_L2_INTERFACES * MAX_IPV6_PER_INTERFACE]; - uint32_t n = socket_bind_l3_list(&bindSpec, IP_VER6, ids, MAX_L2_INTERFACES * MAX_IPV6_PER_INTERFACE); - if (!n) return SOCK_ERR_SYS; - - int sent = 0; - bool linkscope = ipv6_is_linkscope_mcast(d.ip); - for (uint32_t i = 0; i < n; ++i) { - l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(ids[i]); - if (!is_valid_v6_l3_for_bind(v6)) continue; - if (linkscope && !ipv6_is_linklocal(v6->ip)) continue; - - net_l4_endpoint src; - src.ver = IP_VER6; - memset(src.ip, 0, 16); - memcpy(src.ip, v6->ip, 16); - src.port = localPort; - - ipv6_tx_opts_t tx; - tx.scope = IP_TX_BOUND_L3; - tx.index = ids[i]; - - udp_send_segment(&src, &d, pay, &tx, (extraOpts.flags & SOCK_OPT_TTL) ? extraOpts.ttl : 0, (extraOpts.flags & SOCK_OPT_DONTFRAG) ? 1 : 0); - sent++; - } - - if (!sent) return SOCK_ERR_SYS; - remoteEP = d; - return (int64_t)len; - } - - uint8_t allowed_v6[MAX_L2_INTERFACES * MAX_IPV6_PER_INTERFACE]; - uint32_t n_allowed = bound ? socket_bind_l3_list(&bindSpec, IP_VER6, allowed_v6, MAX_L2_INTERFACES * MAX_IPV6_PER_INTERFACE) : 0; - if (bound && n_allowed == 0) return SOCK_ERR_SYS; - ipv6_tx_plan_t plan; - if (!ipv6_build_tx_plan(d.ip, nullptr, n_allowed ? allowed_v6 : nullptr, n_allowed, &plan)) return SOCK_ERR_SYS; + if (!socket_bind_build_ipv6_tx_plan(&bindSpec, localPort, d.ip, &plan)) return SOCK_ERR_SYS; uint8_t chosen_l3 = plan.l3_id; l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(chosen_l3); if (!is_valid_v6_l3_for_bind(v6)) return SOCK_ERR_SYS; - if (!bound) { - int p = socket_bind_alloc_ephemeral_l3(ownerSocket, PROTO_UDP, chosen_l3, pid); + if (!localPort) { + socket_bind_token_t token = 0; + int p = socket_bind_alloc_ephemeral_l3(ownerSocket, PROTO_UDP, chosen_l3, &token); if (p < 0) return SOCK_ERR_NO_PORT; localPort = (uint16_t)p; - bindSpec = {}; + bindToken = token; + memset(&bindSpec, 0, sizeof(bindSpec)); bindSpec.kind = BIND_L3; bindSpec.ver = IP_VER6; bindSpec.l3_id = chosen_l3; - bound = true; - } else if (localPort == 0) { - int p = socket_bind_alloc_ephemeral_l3(ownerSocket, PROTO_UDP, chosen_l3, pid); - if (p < 0) return SOCK_ERR_NO_PORT; - localPort = (uint16_t)p; } net_l4_endpoint src; @@ -482,15 +438,14 @@ class UDPSocket : public Socket { tx.index = plan.fixed_opts.index; udp_send_segment(&src, &d, pay, &tx, (extraOpts.flags & SOCK_OPT_TTL) ? extraOpts.ttl : 0, (extraOpts.flags & SOCK_OPT_DONTFRAG) ? 1 : 0); - remoteEP = d; return (int64_t)len; } return SOCK_ERR_INVAL; } - uint32_t enqueue_datagram(uint8_t ifindex, uint8_t l3_id, ip_version_t ipver, const void* src_ip_addr, const void* dst_ip_addr, netpkt_t* pkt, uint16_t src_port, uint16_t dst_port) { - if (!bound || localPort != dst_port || !pkt || !dst_ip_addr) return 0; + uint32_t enqueue_datagram(ip_version_t ipver, const void* src_ip_addr, const void* dst_ip_addr, netpkt_t* pkt, uint16_t src_port, uint16_t dst_port) { + if (!bindToken || localPort != dst_port || !pkt || !dst_ip_addr || !src_ip_addr) return 0; if (ipver == IP_VER4) { uint32_t dip = 0; memcpy(&dip, dst_ip_addr, 4); @@ -498,6 +453,12 @@ class UDPSocket : public Socket { } else if (ipver == IP_VER6) { if (ipv6_is_multicast((const uint8_t*)dst_ip_addr) && !udp_mcast_match(this, IP_VER6, dst_ip_addr)) return 0; } + if (connected) { + if (remoteEP.ver != ipver || remoteEP.port != src_port) return 0; + if (ipver == IP_VER4 && memcmp(remoteEP.ip, src_ip_addr, 4) != 0) return 0; + if (ipver == IP_VER6 && memcmp(remoteEP.ip, src_ip_addr, 16) != 0) return 0; + } + on_receive(ipver, src_ip_addr, src_port, pkt); return netpkt_len(pkt); } @@ -506,26 +467,27 @@ class UDPSocket : public Socket { netlog_socket_event_t ev{}; ev.comp = NETLOG_COMP_UDP; ev.action = NETLOG_ACT_RECVFROM; - ev.pid = pid; + ev.pid = socket_core_pid(ownerSocket); ev.u0 = (uint32_t)len; ev.local_port = localPort; ev.remote_ep = remoteEP; netlog_socket_event(&extraOpts, &ev); - if (r_head == r_tail) return 0; + if (!ring || r_head == r_tail) return SOCK_ERR_WOULDBLOCK; - sizedptr p = ring[r_head]; + netpkt_t* p = ring[r_head]; net_l4_endpoint se = src_eps[r_head]; - r_head = (r_head + 1) % UDP_RING_CAP; - rx_bytes -= p.size; + ring[r_head] = nullptr; + r_head = (r_head + 1) % ring_cap; - uint32_t tocpy = p.size; + uint32_t pkt_len = p ? netpkt_len(p) : 0; + rx_bytes -= pkt_len; + uint32_t tocpy = pkt_len; if (tocpy > len) tocpy = (uint32_t)len; - memcpy(buf, (void*)p.ptr, tocpy); + if (tocpy && !netpkt_copyout(p, 0, buf, tocpy)) tocpy = 0; if (src) *src = se; - release((void*)p.ptr); - remoteEP = se; + if (p) netpkt_unref(p); return tocpy; } @@ -539,10 +501,70 @@ class UDPSocket : public Socket { return SOCK_ERR_INVAL; case SOCK_OPT_MCAST_JOIN: { if (!value || !len || (len % sizeof(net_l4_endpoint)) != 0) return SOCK_ERR_INVAL; - if (bound) return SOCK_ERR_STATE; + uint32_t count32 = len / sizeof(net_l4_endpoint); + if (!count32 || count32 > 255 || count32 + mcast_count > 255) return SOCK_ERR_INVAL; + + const net_l4_endpoint* groups = (const net_l4_endpoint*)value; + for (uint32_t i = 0; i < count32; ++i) { + if (groups[i].ver != IP_VER4 && groups[i].ver != IP_VER6) return SOCK_ERR_INVAL; + if (groups[i].ver == IP_VER4) { + uint32_t ip = 0; + memcpy(&ip, groups[i].ip, 4); + if (!ipv4_is_multicast(ip)) return SOCK_ERR_INVAL; + } else if (!ipv6_is_multicast(groups[i].ip)) return SOCK_ERR_INVAL; + } + + net_l4_endpoint* next = (net_l4_endpoint*)zalloc(sizeof(net_l4_endpoint) * (mcast_count + count32)); + if (!next) return SOCK_ERR_SYS; + if (mcast_count) memcpy(next, mcast_groups, sizeof(net_l4_endpoint) * mcast_count); + + uint8_t next_count = mcast_count; + for (uint32_t i = 0; i < count32; ++i) { + bool exists = false; + for (uint8_t j = 0; j < next_count; ++j) { + if (next[j].ver != groups[i].ver || next[j].port != groups[i].port) continue; + uint32_t sz = groups[i].ver == IP_VER4 ? 4 : 16; + if (memcmp(next[j].ip, groups[i].ip, sz) == 0) exists = true; + } + if (!exists) next[next_count++] = groups[i]; + } + + if (next_count == mcast_count) { + release(next); + return SOCK_OK; + } + + net_l4_endpoint* old_groups = mcast_groups; + uint8_t old_count = mcast_count; + if (localPort) leave_mcast_groups(); + mcast_groups = next; + mcast_count = next_count; + int32_t rc = join_mcast_groups(); + if (rc != SOCK_OK) { + leave_mcast_groups(); + release(next); + mcast_groups = old_groups; + mcast_count = old_count; + extraOpts.mcast_groups = mcast_groups; + extraOpts.mcast_count = mcast_count; + if (mcast_count) extraOpts.flags |= SOCK_OPT_MCAST_JOIN; + else extraOpts.flags &= ~SOCK_OPT_MCAST_JOIN; + if (localPort) join_mcast_groups(); + return rc; + } + + if (old_groups) release(old_groups); + extraOpts.mcast_groups = mcast_groups; + extraOpts.mcast_count = mcast_count; + extraOpts.flags |= SOCK_OPT_MCAST_JOIN; + return SOCK_OK; + } + case SOCK_OPT_MCAST_LEAVE: { + if (!value || !len || (len % sizeof(net_l4_endpoint)) != 0) return SOCK_ERR_INVAL; uint32_t count = len / sizeof(net_l4_endpoint); - if (!count || count > SOCK_MAX_MCAST_GROUPS) return SOCK_ERR_INVAL; + if (!count || count > 255) return SOCK_ERR_INVAL; + if (!mcast_groups || !mcast_count) return SOCK_OK; const net_l4_endpoint* groups = (const net_l4_endpoint*)value; for (uint32_t i = 0; i < count; ++i) { @@ -554,9 +576,63 @@ class UDPSocket : public Socket { } else if (!ipv6_is_multicast(groups[i].ip)) return SOCK_ERR_INVAL; } - extraOpts.flags |= SOCK_OPT_MCAST_JOIN; - extraOpts.mcast_count = (uint8_t)count; - memcpy(extraOpts.mcast_groups, groups, count * sizeof(net_l4_endpoint)); + net_l4_endpoint* next = nullptr; + if (mcast_count > count) { + next = (net_l4_endpoint*)zalloc(sizeof(net_l4_endpoint) * mcast_count); + if (!next) return SOCK_ERR_SYS; + } + + uint8_t next_count = 0; + for (uint8_t i = 0; i < mcast_count; ++i) { + bool remove_group = false; + for (uint32_t j = 0; j < count; ++j) { + if (mcast_groups[i].ver != groups[j].ver || mcast_groups[i].port != groups[j].port) continue; + uint32_t sz = groups[j].ver == IP_VER4 ? 4 : 16; + if (memcmp(mcast_groups[i].ip, groups[j].ip, sz) == 0) remove_group = true; + } + if (!remove_group) { + if (!next) { + next = (net_l4_endpoint*)zalloc(sizeof(net_l4_endpoint) * mcast_count); + if (!next) return SOCK_ERR_SYS; + } + next[next_count++] = mcast_groups[i]; + } + } + + if (next_count == mcast_count) { + if (next) release(next); + return SOCK_OK; + } + + if (!next_count && next) { + release(next); + next = nullptr; + } + + net_l4_endpoint* old_groups = mcast_groups; + uint8_t old_count = mcast_count; + if (localPort) leave_mcast_groups(); + + mcast_groups = next; + mcast_count = next_count; + int32_t rc = join_mcast_groups(); + if (rc != SOCK_OK) { + leave_mcast_groups(); + if (next) release(next); + mcast_groups = old_groups; + mcast_count = old_count; + extraOpts.mcast_groups = mcast_groups; + extraOpts.mcast_count = mcast_count; + extraOpts.flags |= SOCK_OPT_MCAST_JOIN; + if (localPort) join_mcast_groups(); + return rc; + } + + release(old_groups); + extraOpts.mcast_count = mcast_count; + extraOpts.mcast_groups = mcast_groups; + if (mcast_count) extraOpts.flags |= SOCK_OPT_MCAST_JOIN; + else extraOpts.flags &= ~SOCK_OPT_MCAST_JOIN; return SOCK_OK; } default: @@ -572,12 +648,14 @@ class UDPSocket : public Socket { case SOCK_OPT_SEND_BUF_SIZE: return SOCK_ERR_INVAL; case SOCK_OPT_MCAST_JOIN: { - if (!value || !len) return SOCK_ERR_INVAL; - uint32_t count = (extraOpts.flags & SOCK_OPT_MCAST_JOIN) ? extraOpts.mcast_count : 0; - if (count > SOCK_MAX_MCAST_GROUPS) count = SOCK_MAX_MCAST_GROUPS; - uint32_t need = count * sizeof(net_l4_endpoint); + if (!len) return SOCK_ERR_INVAL; + uint32_t need = mcast_count * sizeof(net_l4_endpoint); + if (!value) { + *len = need; + return SOCK_OK; + } if (*len < need) return SOCK_ERR_INVAL; - if (need) memcpy(value, extraOpts.mcast_groups, need); + if (need) memcpy(value, mcast_groups, need); *len = need; return SOCK_OK; } @@ -591,15 +669,37 @@ class UDPSocket : public Socket { netlog_socket_event_t ev{}; ev.comp = NETLOG_COMP_UDP; ev.action = NETLOG_ACT_CLOSE; - ev.pid = pid; + ev.pid = socket_core_pid(ownerSocket); ev.local_port = localPort; ev.remote_ep = remoteEP; netlog_socket_event(&extraOpts, &ev); - while (r_head != r_tail) { - rx_bytes -= ring[r_head].size; - release((void*)ring[r_head].ptr); - r_head = (r_head + 1) % UDP_RING_CAP; + if (ring) { + while (r_head != r_tail) { + if (ring[r_head]) { + rx_bytes -= netpkt_len(ring[r_head]); + netpkt_unref(ring[r_head]); + } + r_head = (r_head + 1) % ring_cap; + } + release(ring); + ring = nullptr; + } + if (src_eps) { + release(src_eps); + src_eps = nullptr; + } + if (mcast_groups) { + release(mcast_groups); + mcast_groups = nullptr; } + mcast_count = 0; + extraOpts.mcast_count = 0; + extraOpts.mcast_groups = nullptr; + extraOpts.flags &= ~(SOCK_OPT_MCAST_JOIN | SOCK_OPT_MCAST_LEAVE); + ring_cap = 0; + r_head = 0; + r_tail = 0; + rx_bytes = 0; return Socket::close(); } diff --git a/kernel/networking/transport_layer/tcp.h b/kernel/networking/transport_layer/tcp.h index 55ccf994..3437a326 100644 --- a/kernel/networking/transport_layer/tcp.h +++ b/kernel/networking/transport_layer/tcp.h @@ -86,10 +86,9 @@ typedef enum { #define TCP_MAX_RETRANS 8 #define TCP_MAX_PERSIST_PROBES 8 -int find_flow(uint16_t local_port, ip_version_t ver, const void *local_ip, const void *remote_ip, uint16_t remote_port); bool tcp_get_ctx(uint16_t local_port, ip_version_t ver, const void *local_ip, const void *remote_ip, uint16_t remote_port, tcp_data *out_ctx); -bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, tcp_data *flow_ctx, uint16_t pid, const SocketExtraOptions* extra); +bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, tcp_data *flow_ctx, const SocketExtraOptions* extra); void tcp_flow_apply_socket_options(tcp_data *flow_ctx, const SocketExtraOptions* extra); tcp_result_t tcp_flow_send(tcp_data *flow_ctx); diff --git a/kernel/networking/transport_layer/tcp/tcp_core.c b/kernel/networking/transport_layer/tcp/tcp_core.c index c9d6c41f..6dd3568c 100644 --- a/kernel/networking/transport_layer/tcp/tcp_core.c +++ b/kernel/networking/transport_layer/tcp/tcp_core.c @@ -8,69 +8,100 @@ #include "math/rng.h" #include "syscalls/syscalls.h" #include "networking/transport_layer/trans_utils.h" +#include "exceptions/irq.h" tcp_flow_t *tcp_flows[MAX_TCP_FLOWS]; uint16_t tcp_active_flows[MAX_TCP_FLOWS]; uint16_t tcp_active_count; static uint32_t tcp_generation_next = 1; -int find_flow(uint16_t local_port, ip_version_t ver, const void *local_ip, const void *remote_ip, uint16_t remote_port){ +tcp_flow_t *tcp_flow_acquire_match(uint16_t local_port, ip_version_t ver, const void *local_ip, const void *remote_ip, uint16_t remote_port, int *out_idx){ + irq_flags_t irq = irq_save_disable(); for (uint16_t n = 0; n < tcp_active_count; n++){ int i = tcp_active_flows[n]; tcp_flow_t *f = tcp_flows[i]; if (!f) continue; - if (f->base.state == TCP_STATE_CLOSED) continue; + if (f->base.retired || f->base.state == TCP_STATE_CLOSED) continue; if (f->base.local_port != local_port) continue; + uint8_t match = 0; if (f->base.state == TCP_LISTEN){ if (remote_ip || remote_port) continue; if (f->base.local.ver && f->base.local.ver != ver) continue; - if (!local_ip) return i; - - size_t l = (size_t)(ver == IP_VER6 ? 16 : 4); - int unspec = 1; - for (size_t k = 0; k < l; ++k){ - if (f->base.local.ip[k]){ - unspec = 0; - break; + if (!local_ip) match = 1; + else { + size_t l = (size_t)(ver == IP_VER6 ? 16 : 4); + uint8_t unspec = 1; + for (size_t k = 0; k < l; ++k){ + if (f->base.local.ip[k]){ + unspec = 0; + break; + } } + match = unspec || memcmp(f->base.local.ip, local_ip, l) == 0; } - if (unspec) return i; - if (memcmp(f->base.local.ip, local_ip, l) == 0) return i; - continue; - } + } else { + if (!remote_ip || !local_ip) continue; + if (f->base.remote.ver != ver) continue; + if (f->base.remote.port != remote_port) continue; - if (!remote_ip) continue; - if (!local_ip) continue; - if (f->base.remote.ver != ver) continue; - if (f->base.remote.port != remote_port) continue; + size_t l = (size_t)(ver == IP_VER6 ? 16 : 4); + if (memcmp(f->base.local.ip, local_ip, l) != 0) continue; + if (memcmp(f->base.remote.ip, remote_ip, l) != 0) continue; + match = 1; + } - size_t l = (size_t)(ver == IP_VER6 ? 16 : 4); - if (memcmp(f->base.local.ip, local_ip, l) != 0) continue; - if (memcmp(f->base.remote.ip, remote_ip, l) != 0) continue; + if (!match) continue; + if (!f->base.refs || f->base.refs == UINT16_MAX) break; - return i; + f->base.refs++; + if (out_idx) *out_idx = i; + irq_restore(irq); + return f; } - return -1; + if (out_idx) *out_idx = -1; + irq_restore(irq); + return NULL; } bool tcp_get_ctx(uint16_t local_port, ip_version_t ver, const void *local_ip, const void *remote_ip, uint16_t remote_port, tcp_data *out_ctx){ if (!out_ctx) return false; - int idx = find_flow(local_port, ver, local_ip, remote_ip, remote_port); - if (idx < 0) return false; - *out_ctx = tcp_flows[idx]->base.ctx; + tcp_flow_t *flow = tcp_flow_acquire_match(local_port, ver, local_ip, remote_ip, remote_port, NULL); + if (!flow) return false; + *out_ctx = flow->base.ctx; + tcp_flow_put(flow); return true; } +int tcp_flow_hold(tcp_flow_t *flow) { + if (!flow)return 0; + + irq_flags_t irq = irq_save_disable(); + if (flow->base.retired || !flow->base.refs || flow->base.refs == UINT16_MAX) { + irq_restore(irq); + return 0; + } + + flow->base.refs++; + irq_restore(irq); + return 1; +} + tcp_flow_t *tcp_flow_from_ctx(tcp_data *flow_ctx) { if (!flow_ctx) return NULL; if (flow_ctx->flow_index >= MAX_TCP_FLOWS) return NULL; + irq_flags_t irq = irq_save_disable(); tcp_flow_t *flow = tcp_flows[flow_ctx->flow_index]; - if (!flow) return NULL; - if (flow->base.generation != flow_ctx->flow_generation) return NULL; + if (!flow || flow->base.retired || flow->base.generation != flow_ctx->flow_generation || !flow->base.refs || flow->base.refs == UINT16_MAX) { + irq_restore(irq); + return NULL; + } + + flow->base.refs++; + irq_restore(irq); return flow; } @@ -92,24 +123,44 @@ void tcp_flow_apply_socket_options(tcp_data *flow_ctx, const SocketExtraOptions* flow->ip.ttl = (flags & SOCK_OPT_TTL) ? extra->ttl : 0; flow->ip.dontfrag = (flags & SOCK_OPT_DONTFRAG) ? 1 : 0; + tcp_flow_put(flow); } -void tcp_release_io_buffers(tcp_flow_t *f) { +void tcp_flow_put(tcp_flow_t *f) { if (!f) return; + irq_flags_t irq = irq_save_disable(); + if (!f->base.refs) { + irq_restore(irq); + return; + } + + f->base.refs--; + uint8_t free_now = f->base.refs == 0; + irq_restore(irq); + + if (!free_now) return; + for (int i = 0; i < TCP_MAX_TX_SEGS; i++) { tcp_tx_seg_t *s = &f->tx.txq[i]; - if (s->used && s->buf && s->len) { - tcp_account_tx_remove(f, (uint32_t)s->len); - release((void*)s->buf); + if (s->buf && s->len) { + uintptr_t buf = s->buf; + uint32_t len = (uint32_t)s->len; + s->buf = 0; + s->len = 0; + tcp_account_tx_remove(f, len); + release((void*)buf); } memset(s, 0, sizeof(*s)); } f->tx.queued_bytes = 0; - if (f->tx.nagle_buf) release((void*)f->tx.nagle_buf); - f->tx.nagle_buf = 0; + if (f->tx.nagle_buf) { + uintptr_t buf = f->tx.nagle_buf; + f->tx.nagle_buf = 0; + release((void*)buf); + } f->tx.nagle_len = 0; f->tx.nagle_cap = 0; f->tx.nagle_timer_ms = 0; @@ -117,8 +168,11 @@ void tcp_release_io_buffers(tcp_flow_t *f) { f->tx.nagle_appending = 0; if (f->rx.reass_count || f->rx.rcv_ooo_used) tcp_account_ooo_remove(f->rx.rcv_ooo_used, f->rx.reass_count); - if (f->rx.rcv_buf) release((void*)f->rx.rcv_buf); - f->rx.rcv_buf = 0; + if (f->rx.rcv_buf) { + uintptr_t buf = f->rx.rcv_buf; + f->rx.rcv_buf = 0; + release((void*)buf); + } f->rx.rcv_base = f->rx.rcv_nxt; f->rx.rcv_data_nxt = f->rx.rcv_nxt; f->rx.rcv_ooo_used = 0; @@ -128,6 +182,8 @@ void tcp_release_io_buffers(tcp_flow_t *f) { f->base.ctx.window = 0; f->rx.reass_count = 0; memset(f->rx.reass, 0, sizeof(f->rx.reass)); + memset(f, 0, sizeof(*f)); + release(f); } void tcp_enter_time_wait(tcp_flow_t *flow) { @@ -137,15 +193,17 @@ void tcp_enter_time_wait(tcp_flow_t *flow) { int oldest = -1; uint32_t oldest_ms = 0; + irq_flags_t irq = irq_save_disable(); for (int i = 0; i < MAX_TCP_FLOWS; i++) { tcp_flow_t *f = tcp_flows[i]; - if (!f || f->base.state != TCP_TIME_WAIT) continue; + if (!f || f->base.retired || f->base.state != TCP_TIME_WAIT) continue; timewait++; if ((oldest < 0 || f->timer.time_wait_ms > oldest_ms) && f != flow) { oldest = i; oldest_ms = f->timer.time_wait_ms; } } + irq_restore(irq); if (timewait >= TCP_TIMEWAIT_MAX_GLOBAL && oldest >= 0) { tcp_stats.timewait_reap_oldest++; @@ -153,50 +211,63 @@ void tcp_enter_time_wait(tcp_flow_t *flow) { } flow->base.state = TCP_TIME_WAIT; - tcp_release_io_buffers(flow); flow->timer.time_wait_ms = 0; tcp_daemon_kick(); } tcp_flow_t *tcp_alloc_flow(void){ - for (int i = 0; i < MAX_TCP_FLOWS; i++){ - if (tcp_flows[i]) continue; - - tcp_flow_t *f = (tcp_flow_t *)zalloc(sizeof(tcp_flow_t)); - if (!f) return NULL; - tcp_flows[i] = f; + tcp_flow_t *f = (tcp_flow_t *)zalloc(sizeof(tcp_flow_t)); + if (!f) return NULL; - if (tcp_active_count >= MAX_TCP_FLOWS) { - release(f); - tcp_flows[i] = NULL; - return NULL; + irq_flags_t irq = irq_save_disable(); + int slot = -1; + for (int i = 0; i < MAX_TCP_FLOWS; i++) { + if (!tcp_flows[i]) { + slot = i; + break; } - f->base.active_pos = tcp_active_count; - tcp_active_flows[tcp_active_count++] = (uint16_t)i; - f->base.slot = (uint16_t)i; - f->base.generation = tcp_generation_next++; - if (!tcp_generation_next) tcp_generation_next = 1; - f->base.ctx.flow_index = f->base.slot; - f->base.ctx.flow_generation = f->base.generation; - - f->tx.rto = TCP_INIT_RTO; - f->rx.rcv_wnd_max = tcp_clamp_rcvbuf(TCP_DEFAULT_RCV_BUF); - f->rx.rcv_wnd = f->rx.rcv_wnd_max; - - f->tx.mss = TCP_DEFAULT_MSS; - f->tx.cwnd = f->tx.mss * TCP_INIT_CWND_SEGS; - f->tx.ssthresh = TCP_RECV_WINDOW; - return f; } - return NULL; + if (slot < 0 || tcp_active_count >= MAX_TCP_FLOWS) { + irq_restore(irq); + release(f); + return NULL; + } + + tcp_flows[slot] = f; + f->base.active_pos = tcp_active_count; + tcp_active_flows[tcp_active_count++] = (uint16_t)slot; + f->base.slot = (uint16_t)slot; + f->base.generation = tcp_generation_next++; + if (!tcp_generation_next) tcp_generation_next = 1; + f->base.ctx.flow_index = f->base.slot; + f->base.ctx.flow_generation = f->base.generation; + f->base.refs = 1; + irq_restore(irq); + + f->tx.rto = TCP_INIT_RTO; + f->rx.rcv_wnd_max = tcp_clamp_rcvbuf(TCP_DEFAULT_RCV_BUF); + f->rx.rcv_wnd = f->rx.rcv_wnd_max; + + f->tx.mss = TCP_DEFAULT_MSS; + f->tx.cwnd = f->tx.mss * TCP_INIT_CWND_SEGS; + f->tx.ssthresh = TCP_RECV_WINDOW; + return f; } void tcp_free_flow(int idx) { if (idx < 0 || idx >= MAX_TCP_FLOWS) return; + irq_flags_t irq = irq_save_disable(); tcp_flow_t *f = tcp_flows[idx]; - if (!f) return; + if (!f || f->base.retired) { + irq_restore(irq); + return; + } + + f->base.retired = 1; + f->base.state = TCP_STATE_CLOSED; + tcp_flows[idx] = NULL; if (tcp_active_count != 0) { uint16_t pos = f->base.active_pos; @@ -212,24 +283,29 @@ void tcp_free_flow(int idx) { f->base.active_pos = 0; } + irq_restore(irq); - tcp_release_io_buffers(f); - memset(f, 0, sizeof(*f)); - release(f); - tcp_flows[idx] = NULL; + tcp_flow_put(f); } bool tcp_flow_is_closed(tcp_data *flow_ctx) { tcp_flow_t *flow = tcp_flow_from_ctx(flow_ctx); if (!flow) return true; - return flow->base.state == TCP_STATE_CLOSED || flow->base.state == TCP_TIME_WAIT; + bool closed = flow->base.state == TCP_STATE_CLOSED || flow->base.state == TCP_TIME_WAIT; + tcp_flow_put(flow); + return closed; } tcp_result_t tcp_flow_release_closed(tcp_data *flow_ctx) { tcp_flow_t *flow = tcp_flow_from_ctx(flow_ctx); if (!flow) return TCP_INVALID; - if (flow->base.state != TCP_STATE_CLOSED && flow->base.state != TCP_TIME_WAIT) return TCP_BUSY; - tcp_free_flow((int)flow->base.slot); + if (!flow->base.retired && flow->base.state != TCP_STATE_CLOSED && flow->base.state != TCP_TIME_WAIT) { + tcp_flow_put(flow); + return TCP_BUSY; + } + int slot = (int)flow->base.slot; + tcp_flow_put(flow); + tcp_free_flow(slot); return TCP_OK; } @@ -345,12 +421,15 @@ void tcp_rtt_update(tcp_flow_t *flow, uint32_t sample_ms){ flow->tx.rto = rto; } -bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, tcp_data *flow_ctx, uint16_t pid, const SocketExtraOptions* extra){ - (void)pid; +bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, tcp_data *flow_ctx, const SocketExtraOptions* extra){ if (!dst || !flow_ctx) return false; tcp_flow_t *flow = tcp_alloc_flow(); if (!flow) return false; + if (!tcp_flow_hold(flow)) { + tcp_free_flow((int)flow->base.slot); + return false; + } int idx = (int)flow->base.slot; @@ -366,6 +445,7 @@ bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, if (!v4 || !v4->ip){ tcp_free_flow(idx); + tcp_flow_put(flow); return false; } @@ -375,6 +455,7 @@ bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, if (!v6 || ipv6_is_unspecified(v6->ip)){ tcp_free_flow(idx); + tcp_flow_put(flow); return false; } @@ -426,6 +507,7 @@ bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, flow->rx.rcv_adv_edge = flow->rx.rcv_nxt; flow->base.ctx.window = 0; tcp_free_flow(idx); + tcp_flow_put(flow); return false; } (void)tcp_calc_adv_wnd_field(flow, 1); @@ -457,6 +539,7 @@ bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, if (!seg){ tcp_free_flow(idx); + tcp_flow_put(flow); return false; } @@ -472,15 +555,16 @@ bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, seg->opts_len = tcp_build_syn_options(seg->opts, (uint16_t)flow->tx.mss, flow->rx.rcv_wnd_max > 65535u ? flow->tx.ws_send : 0xffu, flow->tx.sack_ok); + flow->tx.snd_nxt += 1; + flow->base.ctx.sequence = flow->tx.snd_nxt; + flow->base.ctx.expected_ack = flow->tx.snd_nxt; + if (!tcp_send_from_seg(flow, seg)) { tcp_free_flow(idx); + tcp_flow_put(flow); return false; } - flow->tx.snd_nxt += 1; - flow->base.ctx.sequence = flow->tx.snd_nxt; - flow->base.ctx.expected_ack = flow->tx.snd_nxt; - tcp_daemon_kick(); uint64_t waited = 0; @@ -490,11 +574,13 @@ bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, while (waited < max_wait){ if (flow->base.state == TCP_ESTABLISHED || flow->base.state == TCP_CLOSE_WAIT){ *flow_ctx = flow->base.ctx; + tcp_flow_put(flow); return true; } if (flow->base.state == TCP_STATE_CLOSED){ tcp_free_flow(idx); + tcp_flow_put(flow); return false; } @@ -503,5 +589,6 @@ bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, } tcp_free_flow(idx); + tcp_flow_put(flow); return false; } \ No newline at end of file diff --git a/kernel/networking/transport_layer/tcp/tcp_internal.h b/kernel/networking/transport_layer/tcp/tcp_internal.h index 018a1f14..5c7bb150 100644 --- a/kernel/networking/transport_layer/tcp/tcp_internal.h +++ b/kernel/networking/transport_layer/tcp/tcp_internal.h @@ -66,6 +66,8 @@ typedef struct { tcp_state_t state; tcp_data ctx; uint8_t retries; + uint16_t refs; + uint8_t retired; } tcp_flow_base_t; typedef struct { @@ -161,8 +163,10 @@ void tcp_enter_time_wait(tcp_flow_t *flow); tcp_flow_t *tcp_alloc_flow(void); void tcp_free_flow(int idx); -void tcp_release_io_buffers(tcp_flow_t *f); tcp_flow_t *tcp_flow_from_ctx(tcp_data *flow_ctx); +tcp_flow_t *tcp_flow_acquire_match(uint16_t local_port, ip_version_t ver, const void *local_ip, const void *remote_ip, uint16_t remote_port, int *out_idx); +int tcp_flow_hold(tcp_flow_t *flow); +void tcp_flow_put(tcp_flow_t *flow); void tcp_rtt_update(tcp_flow_t *flow, uint32_t sample_ms); diff --git a/kernel/networking/transport_layer/tcp/tcp_rx.c b/kernel/networking/transport_layer/tcp/tcp_rx.c index 5c646cc7..d6c9248d 100644 --- a/kernel/networking/transport_layer/tcp/tcp_rx.c +++ b/kernel/networking/transport_layer/tcp/tcp_rx.c @@ -85,44 +85,59 @@ int64_t tcp_flow_read(tcp_data *flow_ctx, void *buf, uint64_t len) { tcp_flow_t *flow = tcp_flow_from_ctx(flow_ctx); if (!flow) return TCP_DISCONNECT; - if (!flow->rx.rcv_buf || !flow->rx.rcv_wnd_max) return flow->base.state == TCP_STATE_CLOSED ? TCP_DISCONNECT : 0; - if (TCP_SEQ_LT(flow->rx.rcv_data_nxt, flow->rx.rcv_base)) return flow->base.state == TCP_STATE_CLOSED ? TCP_DISCONNECT : 0; - - uint32_t n = flow->rx.rcv_data_nxt - flow->rx.rcv_base; - if (!n) return flow->base.state == TCP_STATE_CLOSED ? TCP_DISCONNECT : 0; - if (len < n) n = (uint32_t)len; - - uint8_t *rx = (uint8_t*)flow->rx.rcv_buf; - uint8_t *dst = (uint8_t*)buf; - uint32_t pos = flow->rx.rcv_base % flow->rx.rcv_wnd_max; - uint32_t first = flow->rx.rcv_wnd_max - pos; - - if (first > n) first = n; - if (first) memcpy(dst, rx + pos, first); - if (n > first) memcpy(dst + first, rx, n - first); - - flow->rx.rcv_base += n; - tcp_flow_on_app_read(flow_ctx, n); - return n; + int64_t rc = 0; + if (!flow->rx.rcv_buf || !flow->rx.rcv_wnd_max) rc = flow->base.state == TCP_STATE_CLOSED ? TCP_DISCONNECT : 0; + else if (TCP_SEQ_LT(flow->rx.rcv_data_nxt, flow->rx.rcv_base)) rc = flow->base.state == TCP_STATE_CLOSED ? TCP_DISCONNECT : 0; + else { + uint32_t n = flow->rx.rcv_data_nxt - flow->rx.rcv_base; + if (!n) rc = flow->base.state == TCP_STATE_CLOSED ? TCP_DISCONNECT : 0; + else { + if (len < n) n = (uint32_t)len; + + uint8_t *rx = (uint8_t*)flow->rx.rcv_buf; + uint8_t *dst = (uint8_t*)buf; + uint32_t pos = flow->rx.rcv_base % flow->rx.rcv_wnd_max; + uint32_t first = flow->rx.rcv_wnd_max - pos; + + if (first > n) first = n; + if (first) memcpy(dst, rx + pos, first); + if (n > first) memcpy(dst + first, rx, n - first); + + flow->rx.rcv_base += n; + rc = n; + } + } + + tcp_flow_put(flow); + if (rc > 0) tcp_flow_on_app_read(flow_ctx, (uint32_t)rc); + return rc; } uint32_t tcp_flow_readable(tcp_data *flow_ctx) { tcp_flow_t *flow = tcp_flow_from_ctx(flow_ctx); - if (!flow || !flow->rx.rcv_buf || !flow->rx.rcv_wnd_max) return 0; - if (TCP_SEQ_LT(flow->rx.rcv_data_nxt, flow->rx.rcv_base)) return 0; - return flow->rx.rcv_data_nxt - flow->rx.rcv_base; + if (!flow) return 0; + + uint32_t n = 0; + if (flow->rx.rcv_buf && flow->rx.rcv_wnd_max && !TCP_SEQ_LT(flow->rx.rcv_data_nxt, flow->rx.rcv_base)) n = flow->rx.rcv_data_nxt - flow->rx.rcv_base; + + tcp_flow_put(flow); + return n; } bool tcp_flow_recv_closed(tcp_data *flow_ctx) { tcp_flow_t *flow = tcp_flow_from_ctx(flow_ctx); if (!flow) return true; - if (flow->base.state == TCP_STATE_CLOSED || flow->base.state == TCP_TIME_WAIT) return true; - if (flow->base.state == TCP_CLOSE_WAIT) { - if (!flow->rx.rcv_buf || !flow->rx.rcv_wnd_max) return true; - if (TCP_SEQ_LT(flow->rx.rcv_data_nxt, flow->rx.rcv_base)) return true; - return flow->rx.rcv_data_nxt == flow->rx.rcv_base; + + bool closed = false; + if (flow->base.state == TCP_STATE_CLOSED || flow->base.state == TCP_TIME_WAIT) closed = true; + else if (flow->base.state == TCP_CLOSE_WAIT) { + if (!flow->rx.rcv_buf || !flow->rx.rcv_wnd_max) closed = true; + else if (TCP_SEQ_LT(flow->rx.rcv_data_nxt, flow->rx.rcv_base)) closed = true; + else closed = flow->rx.rcv_data_nxt == flow->rx.rcv_base; } - return false; + + tcp_flow_put(flow); + return closed; } tcp_tx_seg_t *tcp_find_first_unacked(tcp_flow_t *flow) { @@ -360,8 +375,8 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a } tcp_parse_options(parsed_opts_len ? parsed_opts_buf : NULL, parsed_opts_len, &parsed_opts); - int idx = find_flow(dst_port, ipver, dst_ip_addr, src_ip_addr, src_port); - tcp_flow_t *flow = idx >= 0 ? tcp_flows[idx] : NULL; + int idx = -1; + tcp_flow_t *flow = tcp_flow_acquire_match(dst_port, ipver, dst_ip_addr, src_ip_addr, src_port, &idx); if (flow) flow->timer.keepalive_idle_ms = 0; if (flow) flow->base.l3_id = l3_id; @@ -369,6 +384,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a if (ipver == IP_VER4) { l3_ipv4_interface_t *v4 = l3_ipv4_find_by_id(l3_id); if (!v4 || !v4->l2) { + if (flow) tcp_flow_put(flow); netpkt_unref(pkt); return; } @@ -376,6 +392,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a } else { l3_ipv6_interface_t *v6 = l3_ipv6_find_by_id(l3_id); if (!v6 || !v6->l2) { + if (flow) tcp_flow_put(flow); netpkt_unref(pkt); return; } @@ -388,6 +405,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a if (listener) { socket_core_put(listener); tcp_free_flow(idx); + tcp_flow_put(flow); flow = NULL; idx = -1; } @@ -401,6 +419,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a tcp_send_from_seg(flow, seg); break; } + tcp_flow_put(flow); netpkt_unref(pkt); return; } @@ -435,6 +454,12 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a netpkt_unref(pkt); return; } + if (!tcp_flow_hold(nf)) { + tcp_free_flow((int)nf->base.slot); + socket_core_put(listener); + netpkt_unref(pkt); + return; + } flow = nf; idx = (int)flow->base.slot; @@ -537,6 +562,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a tcp_tx_seg_t *seg = tcp_alloc_tx_seg(flow); if (!seg) { tcp_free_flow(idx); + tcp_flow_put(flow); socket_core_put(listener); netpkt_unref(pkt); return; @@ -552,17 +578,19 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a seg->timer_ms = 0; seg->timeout_ms = flow->tx.rto ? flow->tx.rto : TCP_INIT_RTO; seg->opts_len = tcp_build_syn_options(seg->opts, (uint16_t)flow->tx.mss, flow->tx.ws_ok ? flow->tx.ws_send : 0xff, flow->tx.sack_ok); + flow->tx.snd_nxt = iss + 1; + flow->base.ctx.sequence = flow->tx.snd_nxt; if (!tcp_send_from_seg(flow, seg)) { tcp_free_flow(idx); + tcp_flow_put(flow); socket_core_put(listener); netpkt_unref(pkt); return; } - flow->tx.snd_nxt = iss + 1; - tcp_daemon_kick(); socket_core_put(listener); + tcp_flow_put(flow); netpkt_unref(pkt); return; } @@ -587,6 +615,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a if (flow->base.state == TCP_TIME_WAIT){ if (flags & (1u << RST_F)) { + tcp_flow_put(flow); netpkt_unref(pkt); return; } @@ -603,6 +632,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a tcp_send_ack_now(flow); } + tcp_flow_put(flow); netpkt_unref(pkt); return; } @@ -641,8 +671,12 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a if (s->rtt_sample && s->retransmit_cnt == 0) tcp_rtt_update(flow, s->timer_ms); if (s->buf && s->len) { - tcp_account_tx_remove(flow, (uint32_t)s->len); - release((void *)s->buf); + uintptr_t seg_buf = s->buf; + uint32_t seg_len = (uint32_t)s->len; + s->buf = 0; + s->len = 0; + tcp_account_tx_remove(flow, seg_len); + release((void*)seg_buf); } s->used = 0; @@ -671,6 +705,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a tcp_daemon_kick(); } else if ((flow->base.state == TCP_LAST_ACK || flow->base.state == TCP_CLOSING) && TCP_SEQ_GEQ(ack, flow->base.ctx.expected_ack)){ tcp_free_flow(idx); + tcp_flow_put(flow); netpkt_unref(pkt); return; } @@ -735,6 +770,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a flow->rx.rcv_wnd = 0; flow->rx.rcv_adv_edge = flow->rx.rcv_nxt; flow->base.ctx.window = 0; + tcp_flow_put(flow); netpkt_unref(pkt); return; } @@ -767,6 +803,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a flow->base.state = TCP_STATE_CLOSED; } + tcp_flow_put(flow); netpkt_unref(pkt); return; @@ -777,6 +814,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a if (!flow->rx.rcv_buf) { tcp_send_reset(ipver, dst_ip_addr, src_ip_addr, dst_port, src_port, 0, flow->base.ctx.ack, true); tcp_free_flow(idx); + tcp_flow_put(flow); netpkt_unref(pkt); return; } @@ -789,7 +827,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a ksocket_t* listener = NULL; socket_bind_collect(PROTO_TCP, ipver, l3_id, ifx, dst_ip_addr, dst_port, &listener, 1); if (listener) { - queued = tcp_accept_enqueue(listener, ifx, ipver, src_ip_addr, dst_ip_addr, src_port, dst_port); + queued = tcp_accept_enqueue(listener, ipver, src_ip_addr, dst_ip_addr, src_port, dst_port); socket_core_put(listener); } if (!queued) { @@ -814,6 +852,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a } tcp_free_flow(idx); + tcp_flow_put(flow); netpkt_unref(pkt); return; } @@ -827,16 +866,19 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a tcp_daemon_kick(); if (!data_len && !fin) { + tcp_flow_put(flow); netpkt_unref(pkt); return; } break; } else if (flags & (1u << RST_F)){ tcp_free_flow(idx); + tcp_flow_put(flow); netpkt_unref(pkt); return; } + tcp_flow_put(flow); netpkt_unref(pkt); return; @@ -846,6 +888,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a if (flags & (1u << RST_F)) { tcp_free_flow(idx); + tcp_flow_put(flow); netpkt_unref(pkt); return; } @@ -1119,6 +1162,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a } } } + tcp_flow_put(flow); netpkt_unref(pkt); } @@ -1135,13 +1179,15 @@ void tcp_flow_on_app_read(tcp_data *flow_ctx, uint32_t bytes_read){ int advanced = tcp_reass_drain_inseq(flow); tcp_calc_adv_wnd_field(flow, 1); - if (flow->base.state == TCP_STATE_CLOSED || flow->base.state == TCP_TIME_WAIT) return; + if (flow->base.state != TCP_STATE_CLOSED && flow->base.state != TCP_TIME_WAIT) { + uint32_t threshold = flow->tx.mss ? flow->tx.mss : TCP_DEFAULT_MSS; + uint32_t half = flow->rx.rcv_wnd_max >> 1; + if (half && half < threshold) threshold = half; + if (!threshold) threshold = 1; - uint32_t threshold = flow->tx.mss ? flow->tx.mss : TCP_DEFAULT_MSS; - uint32_t half = flow->rx.rcv_wnd_max >> 1; - if (half && half < threshold) threshold = half; - if (!threshold) threshold = 1; + uint32_t delta = TCP_SEQ_GT(flow->rx.rcv_adv_edge, old_edge) ? flow->rx.rcv_adv_edge - old_edge : 0; + if (advanced || flow->rx.rcv_nxt != old_nxt || (!old_adv_field && flow->base.ctx.window) || delta >= threshold) tcp_send_ack_now(flow); + } - uint32_t delta = TCP_SEQ_GT(flow->rx.rcv_adv_edge, old_edge) ? flow->rx.rcv_adv_edge - old_edge : 0; - if (advanced || flow->rx.rcv_nxt != old_nxt || (!old_adv_field && flow->base.ctx.window) || delta >= threshold) tcp_send_ack_now(flow); + tcp_flow_put(flow); } diff --git a/kernel/networking/transport_layer/tcp/tcp_timer.c b/kernel/networking/transport_layer/tcp/tcp_timer.c index 540e0649..285ab334 100644 --- a/kernel/networking/transport_layer/tcp/tcp_timer.c +++ b/kernel/networking/transport_layer/tcp/tcp_timer.c @@ -45,54 +45,65 @@ void tcp_daemon_kick(void) { } int tcp_has_pending_timers(void) { //TODO mhh this should be event driven to avoid MAX_TCP_FLOWS*TCP_MAX_TX_SEGS scans. - + irq_flags_t irq = irq_save_disable(); for (uint16_t n = 0; n < tcp_active_count; n++) { tcp_flow_t *f = tcp_flows[tcp_active_flows[n]]; if (!f) continue; - if (f->base.state == TCP_STATE_CLOSED) continue; + if (f->base.retired || f->base.state == TCP_STATE_CLOSED) continue; - if (f->base.state == TCP_TIME_WAIT) return 1; - if (f->base.state == TCP_FIN_WAIT_2) return 1; - if (f->tx.fin_tx_pending && f->tx.snd_wnd == 0) return 1; - if (f->tx.nagle_len) return 1; - if (f->timer.delayed_ack_pending) return 1; - if (f->timer.persist_active) return 1; - if (!f->tx.fin_tx_pending && f->timer.keepalive_on && f->base.state == TCP_ESTABLISHED && f->timer.keepalive_ms) return 1; + if (f->base.state == TCP_TIME_WAIT || f->base.state == TCP_FIN_WAIT_2 || f->tx.fin_tx_pending || f->tx.nagle_len || f->timer.delayed_ack_pending || f->timer.persist_active || (!f->tx.fin_tx_pending && f->timer.keepalive_on && f->base.state == TCP_ESTABLISHED && f->timer.keepalive_ms)) { + irq_restore(irq); + return 1; + } for (int j = 0; j < TCP_MAX_TX_SEGS; j++) { tcp_tx_seg_t *seg = &f->tx.txq[j]; if (!seg->used) continue; uint32_t end_seq = seg->seq + seg->len + (seg->syn ? 1u : 0u) + (seg->fin ? 1u : 0u); - if (TCP_SEQ_GT(end_seq, f->tx.snd_una)) return 1; + if (TCP_SEQ_GT(end_seq, f->tx.snd_una)) { + irq_restore(irq); + return 1; + } } } + irq_restore(irq); return 0; } void tcp_tick_all(uint32_t elapsed_ms) { for (uint16_t pos = tcp_active_count; pos > 0; pos--) { + irq_flags_t irq = irq_save_disable(); + if (pos > tcp_active_count) { + irq_restore(irq); + continue; + } + int i = tcp_active_flows[pos-1]; tcp_flow_t *f = tcp_flows[i]; - if (!f) continue; - if (f->base.state == TCP_STATE_CLOSED) continue; + if (!f || f->base.retired || f->base.state == TCP_STATE_CLOSED || !f->base.refs || f->base.refs == UINT16_MAX) { + irq_restore(irq); + continue; + } + f->base.refs++; + irq_restore(irq); + int retire_flow = 0; if (f->base.state == TCP_TIME_WAIT) { - tcp_release_io_buffers(f); f->timer.time_wait_ms += elapsed_ms; - if (f->timer.time_wait_ms >= TCP_2MSL_MS) { - tcp_free_flow(i); - continue; - } + if (f->timer.time_wait_ms >= TCP_2MSL_MS) retire_flow = 1; } - if (f->base.state == TCP_FIN_WAIT_2) { + if (!retire_flow && f->base.state == TCP_FIN_WAIT_2) { f->timer.fin_wait2_ms += elapsed_ms; - if (f->timer.fin_wait2_ms >= TCP_2MSL_MS) { - tcp_free_flow(i); - continue; - } + if (f->timer.fin_wait2_ms >= TCP_2MSL_MS) retire_flow = 1; + } + + if (retire_flow) { + tcp_free_flow(i); + tcp_flow_put(f); + continue; } if (f->timer.delayed_ack_pending) { @@ -108,6 +119,8 @@ void tcp_tick_all(uint32_t elapsed_ms) { } } + if (f->tx.fin_tx_pending) tcp_try_send_pending_fin(f); + if (!f->tx.fin_tx_pending && f->timer.keepalive_on && f->base.state == TCP_ESTABLISHED && f->timer.keepalive_ms) { f->timer.keepalive_idle_ms += elapsed_ms; if (f->timer.keepalive_idle_ms >= f->timer.keepalive_ms) { @@ -128,34 +141,33 @@ void tcp_tick_all(uint32_t elapsed_ms) { f->timer.persist_timer_ms += elapsed_ms; if (f->timer.persist_timer_ms >= f->timer.persist_timeout_ms) { if (f->tx.fin_tx_pending && f->timer.persist_probe_cnt >= TCP_MAX_PERSIST_PROBES) { - if (f->base.state == TCP_ESTABLISHED || f->base.state == TCP_CLOSE_WAIT) tcp_free_flow(i); - else tcp_free_flow(i); - continue; - } - tcp_tx_seg_t *best = tcp_find_first_unacked(f); + retire_flow = 1; + } else { + tcp_tx_seg_t *best = tcp_find_first_unacked(f); - uint8_t payload[1]; - const uint8_t *pp = NULL; - uint16_t pl = 0; + uint8_t payload[1]; + const uint8_t *pp = NULL; + uint16_t pl = 0; - uint32_t probe_seq = f->tx.snd_una; - if (!best && f->tx.fin_tx_pending && f->tx.snd_nxt == f->tx.snd_una && f->tx.snd_nxt) probe_seq = f->tx.snd_nxt-1; + uint32_t probe_seq = f->tx.snd_una; + if (!best && f->tx.fin_tx_pending && f->tx.snd_nxt == f->tx.snd_una && f->tx.snd_nxt) probe_seq = f->tx.snd_nxt-1; - if (best && best->buf && best->len && TCP_SEQ_GEQ(probe_seq, best->seq) && TCP_SEQ_LT(probe_seq, best->seq + best->len)) { - payload[0] = *((uint8_t *)best->buf + (probe_seq - best->seq)); - pp = payload; - pl = 1; - } + if (best && best->buf && best->len && TCP_SEQ_GEQ(probe_seq, best->seq) && TCP_SEQ_LT(probe_seq, best->seq + best->len)) { + payload[0] = *((uint8_t *)best->buf + (probe_seq - best->seq)); + pp = payload; + pl = 1; + } - tcp_timer_send_ack_segment(f, probe_seq, pp, pl); + tcp_timer_send_ack_segment(f, probe_seq, pp, pl); - if (f->timer.persist_probe_cnt < UINT8_MAX) f->timer.persist_probe_cnt++; - f->timer.persist_timer_ms = 0; + if (f->timer.persist_probe_cnt < UINT8_MAX) f->timer.persist_probe_cnt++; + f->timer.persist_timer_ms = 0; - if (f->timer.persist_timeout_ms < TCP_PERSIST_MAX_MS) { - uint32_t next = f->timer.persist_timeout_ms << 1; - if (next > TCP_PERSIST_MAX_MS) next = TCP_PERSIST_MAX_MS; - f->timer.persist_timeout_ms = next; + if (f->timer.persist_timeout_ms < TCP_PERSIST_MAX_MS) { + uint32_t next = f->timer.persist_timeout_ms << 1; + if (next > TCP_PERSIST_MAX_MS) next = TCP_PERSIST_MAX_MS; + f->timer.persist_timeout_ms = next; + } } } } @@ -166,6 +178,12 @@ void tcp_tick_all(uint32_t elapsed_ms) { f->timer.persist_probe_cnt = 0; } + if (retire_flow) { + tcp_free_flow(i); + tcp_flow_put(f); + continue; + } + for (int j = 0; j < TCP_MAX_TX_SEGS; j++) { tcp_tx_seg_t *s = &f->tx.txq[j]; if (!s->used) continue; @@ -175,8 +193,7 @@ void tcp_tick_all(uint32_t elapsed_ms) { if (f->tx.snd_wnd == 0 && s->len > 0) continue; if (s->retransmit_cnt >= TCP_MAX_RETRANS) { - if (f->base.state == TCP_ESTABLISHED || f->base.state == TCP_CLOSE_WAIT) tcp_free_flow(i); - else tcp_free_flow(i); + retire_flow = 1; break; } @@ -197,6 +214,14 @@ void tcp_tick_all(uint32_t elapsed_ms) { s->timeout_ms = next; } } + + if (retire_flow) { + tcp_free_flow(i); + tcp_flow_put(f); + continue; + } + + tcp_flow_put(f); } } diff --git a/kernel/networking/transport_layer/tcp/tcp_tx.c b/kernel/networking/transport_layer/tcp/tcp_tx.c index b51997dd..ce6d1c3e 100644 --- a/kernel/networking/transport_layer/tcp/tcp_tx.c +++ b/kernel/networking/transport_layer/tcp/tcp_tx.c @@ -123,14 +123,24 @@ static uint64_t tcp_emit_data(tcp_flow_t *flow, const uint8_t *payload, uint64_t seg->rtt_sample = 0; if (!flow->tx.rtt_valid && first_segment) seg->rtt_sample = 1; + tcp_account_tx_add(flow, (uint32_t)seg_len); + flow->tx.snd_nxt += seg_len; + flow->base.ctx.sequence = flow->tx.snd_nxt; + if (!tcp_send_from_seg(flow, seg)) { - if (seg->buf && seg->len) release((void*)seg->buf); + flow->tx.snd_nxt -= seg_len; + flow->base.ctx.sequence = flow->tx.snd_nxt; + tcp_account_tx_remove(flow, (uint32_t)seg_len); + if (seg->buf && seg->len) { + uintptr_t seg_buf = seg->buf; + seg->buf = 0; + seg->len = 0; + release((void*)seg_buf); + } memset(seg, 0, sizeof(*seg)); break; } - tcp_account_tx_add(flow, (uint32_t)seg_len); - flow->tx.snd_nxt += seg_len; sent_bytes += seg_len; remaining -= seg_len; can_send -= seg_len; @@ -238,7 +248,7 @@ tcp_tx_seg_t *tcp_alloc_tx_seg(tcp_flow_t *flow){ bool tcp_send_from_seg(tcp_flow_t *flow, tcp_tx_seg_t *seg){ if (!flow || !seg) return false; - if (flow->base.state == TCP_STATE_CLOSED) return false; + if (flow->base.retired || flow->base.state == TCP_STATE_CLOSED) return false; if (flow) flow->timer.keepalive_idle_ms = 0; tcp_hdr_t hdr; @@ -388,20 +398,26 @@ int tcp_try_send_pending_fin(tcp_flow_t *flow) { seg->retransmit_cnt = 0; seg->rtt_sample = 0; - if (!tcp_send_from_seg(flow, seg)) { - memset(seg, 0, sizeof(*seg)); - tcp_daemon_kick(); - return 0; - } - + tcp_state_t old_state = flow->base.state; flow->tx.snd_nxt += 1; flow->base.ctx.expected_ack = flow->tx.snd_nxt; flow->base.ctx.sequence = flow->tx.snd_nxt; flow->tx.fin_tx_pending = 0; - if (flow->base.state == TCP_ESTABLISHED) flow->base.state = TCP_FIN_WAIT_1; + if (old_state == TCP_ESTABLISHED) flow->base.state = TCP_FIN_WAIT_1; else flow->base.state = TCP_LAST_ACK; + if (!tcp_send_from_seg(flow, seg)) { + flow->tx.snd_nxt -= 1; + flow->base.ctx.expected_ack = flow->tx.snd_nxt; + flow->base.ctx.sequence = flow->tx.snd_nxt; + flow->base.state = old_state; + flow->tx.fin_tx_pending = 1; + memset(seg, 0, sizeof(*seg)); + tcp_daemon_kick(); + return 0; + } + tcp_daemon_kick(); return 1; } @@ -412,17 +428,25 @@ tcp_result_t tcp_flow_flush(tcp_data *flow_ctx){ tcp_flow_t *flow = tcp_flow_from_ctx(flow_ctx); if (!flow) return TCP_INVALID; - if (flow->base.state != TCP_ESTABLISHED && flow->base.state != TCP_CLOSE_WAIT && flow->base.state != TCP_FIN_WAIT_1 && flow->base.state != TCP_FIN_WAIT_2) return TCP_INVALID; + tcp_result_t rc = TCP_OK; + if (flow->base.state != TCP_ESTABLISHED && flow->base.state != TCP_CLOSE_WAIT && flow->base.state != TCP_FIN_WAIT_1 && flow->base.state != TCP_FIN_WAIT_2) rc = TCP_INVALID; - while (flow->tx.nagle_len) { + while (rc == TCP_OK && flow->tx.nagle_len) { uint32_t before = flow->tx.nagle_len; uint64_t sent = tcp_flush_nagle(flow, 1); if (!sent || flow->tx.nagle_len == before) break; } - if (flow->tx.fin_tx_pending) tcp_try_send_pending_fin(flow); - if (flow->tx.nagle_len)tcp_daemon_kick(); - return flow->tx.nagle_len ? TCP_WOULDBLOCK : TCP_OK; + if (rc == TCP_OK) { + if (flow->tx.fin_tx_pending) tcp_try_send_pending_fin(flow); + if (flow->tx.nagle_len) { + tcp_daemon_kick(); + rc = TCP_WOULDBLOCK; + } + } + + tcp_flow_put(flow); + return rc; } tcp_result_t tcp_flow_send(tcp_data *flow_ctx){ @@ -437,12 +461,12 @@ tcp_result_t tcp_flow_send(tcp_data *flow_ctx){ flow_ctx->payload.size = 0; int want_fin = (flags & (1u << FIN_F)) != 0; int fin_queued = 0; - - if (flow->base.state != TCP_ESTABLISHED && flow->base.state != TCP_CLOSE_WAIT) return TCP_INVALID; - uint64_t accepted = 0; + tcp_result_t rc = TCP_OK; + + if (flow->base.state != TCP_ESTABLISHED && flow->base.state != TCP_CLOSE_WAIT) rc = TCP_INVALID; - if (!flow->tx.nodelay && payload_len && flow->tx.nagle_len) { + if (rc == TCP_OK && !flow->tx.nodelay && payload_len && flow->tx.nagle_len) { uint64_t n = tcp_nagle_append(flow, payload_ptr, payload_len); accepted += n; payload_ptr += n; @@ -450,7 +474,7 @@ tcp_result_t tcp_flow_send(tcp_data *flow_ctx){ if (flow->tx.nagle_len >= tcp_nagle_threshold(flow)) tcp_flush_nagle(flow, 1); } - if (!flow->tx.nodelay && payload_len && payload_len < tcp_nagle_threshold(flow)) { + if (rc == TCP_OK && !flow->tx.nodelay && payload_len && payload_len < tcp_nagle_threshold(flow)) { uint64_t n = tcp_nagle_append(flow, payload_ptr, payload_len); accepted += n; payload_ptr += n; @@ -458,7 +482,7 @@ tcp_result_t tcp_flow_send(tcp_data *flow_ctx){ if (flow->tx.nagle_len >= tcp_nagle_threshold(flow)) tcp_flush_nagle(flow, 1); } - if (payload_len) { + if (rc == TCP_OK && payload_len) { if (flow->tx.nagle_len) tcp_flush_nagle(flow, 1); if (!flow->tx.nagle_len) { uint64_t n = tcp_emit_data(flow, payload_ptr, payload_len); @@ -468,7 +492,7 @@ tcp_result_t tcp_flow_send(tcp_data *flow_ctx){ } } - if (want_fin && payload_len == 0) { + if (rc == TCP_OK && want_fin && payload_len == 0) { flow->tx.fin_tx_pending = 1; fin_queued = 1; if (flow->tx.nagle_len) tcp_flush_nagle(flow, 1); @@ -479,17 +503,24 @@ tcp_result_t tcp_flow_send(tcp_data *flow_ctx){ flow->base.ctx.sequence = flow->tx.snd_nxt; flow_ctx->payload.size = accepted; - if (!accepted && fin_queued && flow->tx.fin_tx_pending) return TCP_WOULDBLOCK; - if (accepted || fin_queued) return TCP_OK; - if (flow->tx.snd_wnd == 0) { - flow->timer.persist_active = 1; - flow->timer.persist_timer_ms = 0; - if (flow->timer.persist_timeout_ms == 0) flow->timer.persist_timeout_ms = TCP_PERSIST_MIN_MS; - if (flow->timer.persist_timeout_ms < TCP_PERSIST_MIN_MS) flow->timer.persist_timeout_ms = TCP_PERSIST_MIN_MS; - if (flow->timer.persist_timeout_ms > TCP_PERSIST_MAX_MS) flow->timer.persist_timeout_ms = TCP_PERSIST_MAX_MS; - tcp_daemon_kick(); + if (rc == TCP_OK) { + if (!accepted && fin_queued && flow->tx.fin_tx_pending) rc = TCP_WOULDBLOCK; + else if (accepted || fin_queued) rc = TCP_OK; + else { + if (flow->tx.snd_wnd == 0) { + flow->timer.persist_active = 1; + flow->timer.persist_timer_ms = 0; + if (flow->timer.persist_timeout_ms == 0) flow->timer.persist_timeout_ms = TCP_PERSIST_MIN_MS; + if (flow->timer.persist_timeout_ms < TCP_PERSIST_MIN_MS) flow->timer.persist_timeout_ms = TCP_PERSIST_MIN_MS; + if (flow->timer.persist_timeout_ms > TCP_PERSIST_MAX_MS) flow->timer.persist_timeout_ms = TCP_PERSIST_MAX_MS; + tcp_daemon_kick(); + } + rc = TCP_WOULDBLOCK; + } } - return TCP_WOULDBLOCK; + + tcp_flow_put(flow); + return rc; } tcp_result_t tcp_flow_close(tcp_data *flow_ctx){ @@ -498,13 +529,15 @@ tcp_result_t tcp_flow_close(tcp_data *flow_ctx){ tcp_flow_t *flow = tcp_flow_from_ctx(flow_ctx); if (!flow) return TCP_INVALID; + tcp_result_t rc = TCP_INVALID; if (flow->base.state == TCP_ESTABLISHED || flow->base.state == TCP_CLOSE_WAIT) { flow->tx.fin_tx_pending = 1; - if (flow->tx.nagle_len) tcp_flow_flush(flow_ctx); + if (flow->tx.nagle_len) tcp_flush_nagle(flow, 1); tcp_try_send_pending_fin(flow); tcp_daemon_kick(); - return TCP_OK; + rc = TCP_OK; } - return TCP_INVALID; + tcp_flow_put(flow); + return rc; } \ No newline at end of file diff --git a/kernel/networking/transport_layer/udp.c b/kernel/networking/transport_layer/udp.c index dfb3e4f5..bc64a9a5 100644 --- a/kernel/networking/transport_layer/udp.c +++ b/kernel/networking/transport_layer/udp.c @@ -128,7 +128,7 @@ void udp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a ksocket_t* sockets[SOCKET_BIND_COLLECT_MAX]; uint32_t count = socket_bind_collect(PROTO_UDP, ipver, l3_id, ifx, dst_ip_addr, dst_port, sockets, SOCKET_BIND_COLLECT_MAX); for (uint32_t i = 0; i < count; ++i) { - socket_udp_input(sockets[i], ifx, l3_id, ipver, src_ip_addr, dst_ip_addr, plpkt, src_port, dst_port); + socket_udp_input(sockets[i], ipver, src_ip_addr, dst_ip_addr, plpkt, src_port, dst_port); socket_core_put(sockets[i]); } diff --git a/kernel/process/kernel_syscall_impl.c b/kernel/process/kernel_syscall_impl.c index 8e2138c6..0133707d 100644 --- a/kernel/process/kernel_syscall_impl.c +++ b/kernel/process/kernel_syscall_impl.c @@ -105,47 +105,47 @@ uint64_t get_time(){ return timer_now_msec(); } -bool socket_create(Socket_Role role, protocol_t protocol, const SocketExtraOptions* extra, SocketHandle *out_handle){ - return create_socket(role, protocol, extra, out_handle); +socket_handle_t socket_create(protocol_t protocol, const SocketExtraOptions* extra){ + return create_socket(protocol, extra); } -int32_t socket_bind(SocketHandle *handle, ip_version_t ip_version, uint16_t port){ - return bind_socket(handle, port, ip_version); +int32_t socket_bind(socket_handle_t handle, const SockBindSpec* spec, uint16_t port){ + return bind_socket(handle, spec, port); } -int32_t socket_connect(SocketHandle *handle, const net_l4_endpoint* dst){ +int32_t socket_connect(socket_handle_t handle, const net_l4_endpoint* dst){ return connect_socket(handle, dst); } -int32_t socket_listen(SocketHandle *handle){ - return listen_on(handle, 0); +int32_t socket_listen(socket_handle_t handle, int32_t backlog){ + return listen_on(handle, backlog); } -bool socket_accept(SocketHandle* handle, SocketHandle* out_child){ - return accept_on_socket(handle, out_child); +socket_handle_t socket_accept(socket_handle_t handle){ + return accept_on_socket(handle); } -int64_t socket_send(SocketHandle *handle, void *packet, size_t size){ +int64_t socket_send(socket_handle_t handle, void *packet, size_t size){ return send_on_socket(handle, packet, size); } -int64_t socket_send_to(SocketHandle *handle, const net_l4_endpoint* dst, void *packet, size_t size){ +int64_t socket_send_to(socket_handle_t handle, const net_l4_endpoint* dst, void *packet, size_t size){ return send_to_socket(handle, dst, packet, size); } -int64_t socket_receive(SocketHandle *handle, void *packet, size_t size, net_l4_endpoint* out_src){ +int64_t socket_receive(socket_handle_t handle, void *packet, size_t size, net_l4_endpoint* out_src){ return receive_from_socket(handle, packet, size, out_src); } -int32_t socket_close(SocketHandle *handle){ +int32_t socket_close(socket_handle_t handle){ return close_socket(handle); } -int32_t socket_set_option(SocketHandle *handle, int32_t opt, const void* value, uint32_t len){ +int32_t socket_set_option(socket_handle_t handle, int32_t opt, const void* value, uint32_t len){ return set_socket_option(handle, opt, value, len); } -int32_t socket_get_option(SocketHandle *handle, int32_t opt, void* value, uint32_t* len){ +int32_t socket_get_option(socket_handle_t handle, int32_t opt, void* value, uint32_t* len){ return get_socket_option(handle, opt, value, len); } diff --git a/kernel/process/syscall.c b/kernel/process/syscall.c index 7699f0de..d94958d3 100644 --- a/kernel/process/syscall.c +++ b/kernel/process/syscall.c @@ -255,53 +255,63 @@ u64 syscall_get_time(process_t *ctx){ } u64 syscall_socket_create(process_t *ctx){ - Socket_Role role = (Socket_Role)ctx->PROC_X0; - protocol_t protocol = (protocol_t)ctx->PROC_X1; - SYSCALL_ARG(const SocketExtraOptions, extra, PROC_X2, false); - SYSCALL_ARG(SocketHandle, out, PROC_X3, true); + protocol_t protocol = (protocol_t)ctx->PROC_X0; + const SocketExtraOptions* extra = NULL; + + if (ctx->PROC_X1) { + SYSCALL_ARG(SocketExtraOptions, user_extra, PROC_X1, false); + if (user_extra->mcast_count) { + if (!user_extra->mcast_groups) return 0; + if (!validate_address(ctx, (uptr)user_extra->mcast_groups, sizeof(net_l4_endpoint) * user_extra->mcast_count, false)) return 0; + } + extra = user_extra; + } - return create_socket(role, protocol, extra, out); + return create_socket(protocol, extra); } u64 syscall_socket_bind(process_t *ctx){ - SYSCALL_ARG(SocketHandle,handle,PROC_X0, false); - ip_version_t ip_version = (ip_version_t)ctx->PROC_X1; + socket_handle_t handle = (socket_handle_t)ctx->PROC_X0; + const SockBindSpec* spec = NULL; + + if (ctx->PROC_X1){ + SYSCALL_ARG(SockBindSpec, user_spec, PROC_X1, false); + spec = user_spec; + } + uint16_t port = (uint16_t)ctx->PROC_X2; - return bind_socket(handle, port, ip_version); + return bind_socket(handle, spec, port); } u64 syscall_socket_connect(process_t *ctx){ - SYSCALL_ARG(SocketHandle,handle,PROC_X0,true); + socket_handle_t handle = (socket_handle_t)ctx->PROC_X0; SYSCALL_ARG(net_l4_endpoint, ep, PROC_X1, false); return connect_socket(handle, ep); } u64 syscall_socket_listen(process_t *ctx){ - SYSCALL_ARG(SocketHandle,handle, PROC_X0, false); + socket_handle_t handle = (socket_handle_t)ctx->PROC_X0; int32_t backlog = (int32_t)ctx->PROC_X1; return listen_on(handle, backlog); } u64 syscall_socket_accept(process_t *ctx){ - SYSCALL_ARG(SocketHandle,handle, PROC_X0, false); - SYSCALL_ARG(SocketHandle,out_child, PROC_X1, true); - return accept_on_socket(handle, out_child); + socket_handle_t handle = (socket_handle_t)ctx->PROC_X0; + return accept_on_socket(handle); } u64 syscall_socket_send(process_t *ctx){ size_t size = (size_t)ctx->PROC_X2; - if (!size) return 0; - SYSCALL_ARG(SocketHandle,handle, PROC_X0, false); + socket_handle_t handle = (socket_handle_t)ctx->PROC_X0; SYSCALL_ARG_SIZE(void, buf, size, PROC_X1, false); return send_on_socket(handle, buf, size); } u64 syscall_socket_send_to(process_t *ctx) { size_t size = (size_t)ctx->PROC_X3; - if (!size) return 0; - SYSCALL_ARG(SocketHandle,handle, PROC_X0, false); + socket_handle_t handle = (socket_handle_t)ctx->PROC_X0; SYSCALL_ARG(net_l4_endpoint, dst, PROC_X1, false); SYSCALL_ARG_SIZE(void, buf, size, PROC_X2, false); @@ -310,22 +320,26 @@ u64 syscall_socket_send_to(process_t *ctx) { u64 syscall_socket_receive(process_t *ctx){ size_t size = (size_t)ctx->PROC_X2; - if (!size) return 0; - SYSCALL_ARG(SocketHandle, handle, PROC_X0, false); + socket_handle_t handle = (socket_handle_t)ctx->PROC_X0; SYSCALL_ARG_SIZE(void, buf, size, PROC_X1, true); - SYSCALL_ARG(net_l4_endpoint, src, PROC_X3, true); + net_l4_endpoint* src = NULL; + if (ctx->PROC_X3) { + src = (net_l4_endpoint*)ctx->PROC_X3; + if (!validate_address(ctx, (uptr)src, sizeof(net_l4_endpoint), true)) return 0; + } + return receive_from_socket(handle, buf, size, src); } u64 syscall_socket_close(process_t *ctx){ - SYSCALL_ARG(SocketHandle,handle, PROC_X0, true); + socket_handle_t handle = (socket_handle_t)ctx->PROC_X0; return close_socket(handle); } u64 syscall_socket_setopt(process_t *ctx){ - SYSCALL_ARG(SocketHandle, handle, PROC_X0, false); + socket_handle_t handle = (socket_handle_t)ctx->PROC_X0; int32_t opt = (int32_t)ctx->PROC_X1; uint32_t len = (uint32_t)ctx->PROC_X3; SYSCALL_ARG_SIZE(void, value, len, PROC_X2, false); @@ -333,7 +347,7 @@ u64 syscall_socket_setopt(process_t *ctx){ } u64 syscall_socket_getopt(process_t *ctx){ - SYSCALL_ARG(SocketHandle, handle, PROC_X0, false); + socket_handle_t handle = (socket_handle_t)ctx->PROC_X0; int32_t opt = (int32_t)ctx->PROC_X1; SYSCALL_ARG(uint32_t, len, PROC_X3, true); SYSCALL_ARG_SIZE(void, value, *len, PROC_X2, true); diff --git a/shared b/shared index 8a270697..838a7d77 160000 --- a/shared +++ b/shared @@ -1 +1 @@ -Subproject commit 8a2706973ccd262b23078863196db1b678bc6f8a +Subproject commit 838a7d7761cc70da1c5fce367dfdbaf99fff91ad diff --git a/user/demo/default_process.c b/user/demo/default_process.c index 493d8155..a24cb9e8 100644 --- a/user/demo/default_process.c +++ b/user/demo/default_process.c @@ -50,22 +50,24 @@ int img_example() { } int net_example() { - SocketHandle spec = {}; - socket_create(SOCKET_SERVER, PROTO_UDP, NULL, &spec); - print("Created socket for type %i",spec.protocol); - //Fill in manually with your local IP. A syscall will be added soon to get it for you - spec.connection.ip[0] = 0; - spec.connection.ip[1] = 0; - spec.connection.ip[2] = 0; - spec.connection.ip[3] = 0; - if (socket_bind(&spec, IP_VER4, 9000) < 0) return -1; + socket_handle_t sock = socket_create(PROTO_UDP, NULL); + print("Created socket"); + SockBindSpec spec = {}; + spec.kind = BIND_IP; + spec.ver = IP_VER4; + //Fill in manually with your local IPV4. address (0.0.0.0 is ANYV4) + spec.ip[0] = 0; + spec.ip[1] = 0; + spec.ip[2] = 0; + spec.ip[3] = 0; + if (socket_bind(sock, &spec, 9000) < 0) return -1; // socket_listen(&spec); void *ptr = malloc(0x1000); - print("Waiting for data %i.%i.%i.%i", spec.connection.ip[0],spec.connection.ip[1],spec.connection.ip[2],spec.connection.ip[3]); + print("Waiting for data %i.%i.%i.%i", spec.ip[0],spec.ip[1],spec.ip[2],spec.ip[3]); net_l4_endpoint rc = {}; - while (!socket_receive(&spec, ptr, 0x1000, &rc)){ + while (socket_receive(sock, ptr, 0x1000, &rc) == SOCK_ERR_WOULDBLOCK){ } print("Received data from %i.%i.%i.%i:%i", rc.ip[0],rc.ip[1],rc.ip[2],rc.ip[3],rc.port); @@ -76,9 +78,9 @@ int net_example() { char *str = "Hello node"; - print("Sent %i",socket_send_to(&spec, &rc, str, strlen(str))); + print("Sent %i",socket_send_to(sock, &rc, str, strlen(str))); - socket_close(&spec); + socket_close(sock); return 1; } From 46f81d41bafc29929c1fc130afc1d439373329c8 Mon Sep 17 00:00:00 2001 From: CodeAnarchist Date: Wed, 10 Jun 2026 18:58:20 +0200 Subject: [PATCH 22/39] [http/l3] http policy handling and l3 fragment reassembly --- kernel/networking/application_layer/http.c | 224 ++++-- kernel/networking/application_layer/http.h | 84 ++- .../application_layer/socket_http_client.hpp | 44 +- .../application_layer/socket_http_server.hpp | 57 +- .../drivers/virtio_net_pci/virtio_net_pci.cpp | 6 +- .../drivers/virtio_net_pci/virtio_net_pci.hpp | 4 +- kernel/networking/internet_layer/icmp.c | 4 +- kernel/networking/internet_layer/icmpv6.c | 2 +- kernel/networking/internet_layer/igmp.c | 2 +- kernel/networking/internet_layer/ipv4.c | 693 ++++++++---------- kernel/networking/internet_layer/ipv4.h | 4 +- kernel/networking/internet_layer/ipv4_route.c | 6 - kernel/networking/internet_layer/ipv4_route.h | 1 - kernel/networking/internet_layer/ipv6.c | 388 ++-------- kernel/networking/internet_layer/ipv6.h | 4 +- kernel/networking/internet_layer/ipv6_route.c | 8 +- kernel/networking/internet_layer/ipv6_route.h | 1 - kernel/networking/net_fragbuf.c | 76 ++ kernel/networking/net_fragbuf.h | 32 + kernel/networking/netpkt.c | 22 +- kernel/networking/netpkt.h | 2 +- kernel/networking/processes/net_proc.c | 70 +- .../networking/transport_layer/socket_udp.hpp | 14 +- .../networking/transport_layer/tcp/tcp_core.c | 12 +- .../networking/transport_layer/tcp/tcp_rx.c | 16 +- .../transport_layer/tcp/tcp_timer.c | 8 +- .../networking/transport_layer/tcp/tcp_tx.c | 16 +- .../transport_layer/tcp/tcp_utils.c | 6 +- .../transport_layer/tcp/tcp_utils.h | 6 +- kernel/networking/transport_layer/udp.c | 4 +- kernel/tools/curl.c | 19 +- kernel/tools/ping.c | 4 +- kernel/tools/tracert.c | 4 +- 33 files changed, 845 insertions(+), 998 deletions(-) create mode 100644 kernel/networking/net_fragbuf.c create mode 100644 kernel/networking/net_fragbuf.h diff --git a/kernel/networking/application_layer/http.c b/kernel/networking/application_layer/http.c index f5805d18..b8122d5b 100644 --- a/kernel/networking/application_layer/http.c +++ b/kernel/networking/application_layer/http.c @@ -20,23 +20,22 @@ HTTPPolicy http_default_policy(void) { return p; } -HTTPPolicy http_policy_from_options(const HTTPPolicyOptions *options) { - HTTPPolicy p = http_default_policy(); +HTTPPolicy http_policy_apply_options(HTTPPolicy p, const HTTPPolicyOptions *options) { if (!options) return p; - if (options->max_start_line > 0) p.max_start_line = options->max_start_line; - if (options->max_header_bytes > 0) p.max_header_bytes = options->max_header_bytes; - if (options->max_header_count > 0) p.max_header_count = options->max_header_count; - if (options->max_header_key_len > 0) p.max_header_key_len = options->max_header_key_len; - if (options->max_header_value_len > 0) p.max_header_value_len = options->max_header_value_len; - if (options->max_path_len > 0) p.max_path_len = options->max_path_len; - if (options->max_body_bytes > 0) p.max_body_bytes = options->max_body_bytes; - if (options->header_idle_timeout_ms > 0) p.header_idle_timeout_ms = options->header_idle_timeout_ms; - if (options->header_total_timeout_ms > 0) p.header_total_timeout_ms = options->header_total_timeout_ms; - if (options->body_idle_timeout_ms > 0) p.body_idle_timeout_ms = options->body_idle_timeout_ms; - if (options->body_total_timeout_ms > 0) p.body_total_timeout_ms = options->body_total_timeout_ms; - if (options->allow_chunked > 0) p.allow_chunked = true; - else if (options->allow_chunked < 0) p.allow_chunked = false; + uint32_t f = options->flags; + if (f & HTTP_POLICY_OPT_MAX_START_LINE) p.max_start_line = options->value.max_start_line; + if (f & HTTP_POLICY_OPT_MAX_HEADER_BYTES) p.max_header_bytes = options->value.max_header_bytes; + if (f & HTTP_POLICY_OPT_MAX_HEADER_COUNT) p.max_header_count = options->value.max_header_count; + if (f & HTTP_POLICY_OPT_MAX_HEADER_KEY_LEN) p.max_header_key_len = options->value.max_header_key_len; + if (f & HTTP_POLICY_OPT_MAX_HEADER_VALUE_LEN) p.max_header_value_len = options->value.max_header_value_len; + if (f & HTTP_POLICY_OPT_MAX_PATH_LEN) p.max_path_len = options->value.max_path_len; + if (f & HTTP_POLICY_OPT_MAX_BODY_BYTES) p.max_body_bytes = options->value.max_body_bytes; + if (f & HTTP_POLICY_OPT_HEADER_IDLE_TIMEOUT_MS) p.header_idle_timeout_ms = options->value.header_idle_timeout_ms; + if (f & HTTP_POLICY_OPT_HEADER_TOTAL_TIMEOUT_MS) p.header_total_timeout_ms = options->value.header_total_timeout_ms; + if (f & HTTP_POLICY_OPT_BODY_IDLE_TIMEOUT_MS) p.body_idle_timeout_ms = options->value.body_idle_timeout_ms; + if (f & HTTP_POLICY_OPT_BODY_TOTAL_TIMEOUT_MS) p.body_total_timeout_ms = options->value.body_total_timeout_ms; + if (f & HTTP_POLICY_OPT_ALLOW_CHUNKED) p.allow_chunked = options->value.allow_chunked; return p; } @@ -44,21 +43,30 @@ HTTPServerPolicy http_server_policy_from_options(const HTTPServerPolicyOptions * HTTPServerPolicy p = { .common = http_default_policy(), .max_keepalive_requests = HTTP_DEFAULT_MAX_KEEPALIVE_REQUESTS, + .allowed_methods = HTTP_METHOD_MASK_ALL, + .error_content_type = "text/plain", .allow_keep_alive = true, .allow_absolute_uri = true, - .require_host_http11 = true + .require_host_http11 = true, + .send_error_body = true }; if (!options) return p; - p.common = http_policy_from_options(&options->common); - if (options->max_keepalive_requests > 0) p.max_keepalive_requests = options->max_keepalive_requests; - if (options->allow_keep_alive > 0) p.allow_keep_alive = true; - else if (options->allow_keep_alive < 0) p.allow_keep_alive = false; - if (options->allow_absolute_uri > 0) p.allow_absolute_uri = true; - else if (options->allow_absolute_uri < 0) p.allow_absolute_uri = false; - if (options->require_host_http11 > 0) p.require_host_http11 = true; - else if (options->require_host_http11 < 0) p.require_host_http11 = false; + HTTPPolicyOptions common = { + .value = options->value.common, + .flags = options->common_flags + }; + p.common = http_policy_apply_options(p.common, &common); + + uint32_t f = options->flags; + if (f & HTTP_SERVER_OPT_MAX_KEEPALIVE_REQUESTS) p.max_keepalive_requests = options->value.max_keepalive_requests; + if (f & HTTP_SERVER_OPT_ALLOWED_METHODS) p.allowed_methods = options->value.allowed_methods; + if (f & HTTP_SERVER_OPT_ERROR_CONTENT_TYPE) p.error_content_type = options->value.error_content_type; + if (f & HTTP_SERVER_OPT_ALLOW_KEEP_ALIVE) p.allow_keep_alive = options->value.allow_keep_alive; + if (f & HTTP_SERVER_OPT_ALLOW_ABSOLUTE_URI) p.allow_absolute_uri = options->value.allow_absolute_uri; + if (f & HTTP_SERVER_OPT_REQUIRE_HOST_HTTP11) p.require_host_http11 = options->value.require_host_http11; + if (f & HTTP_SERVER_OPT_SEND_ERROR_BODY) p.send_error_body = options->value.send_error_body; return p; } @@ -72,12 +80,16 @@ HTTPClientPolicy http_client_policy_from_options(const HTTPClientPolicyOptions * if (!options) return p; - p.common = http_policy_from_options(&options->common); - if (options->max_redirects > 0) p.max_redirects = options->max_redirects; - if (options->follow_redirects > 0) p.follow_redirects = true; - else if (options->follow_redirects < 0) p.follow_redirects = false; - if (options->allow_close_delimited > 0) p.allow_close_delimited = true; - else if (options->allow_close_delimited < 0) p.allow_close_delimited = false; + HTTPPolicyOptions common = { + .value = options->value.common, + .flags = options->common_flags + }; + p.common = http_policy_apply_options(p.common, &common); + + uint32_t f = options->flags; + if (f & HTTP_CLIENT_OPT_MAX_REDIRECTS) p.max_redirects = options->value.max_redirects; + if (f & HTTP_CLIENT_OPT_FOLLOW_REDIRECTS) p.follow_redirects = options->value.follow_redirects; + if (f & HTTP_CLIENT_OPT_ALLOW_CLOSE_DELIMITED) p.allow_close_delimited = options->value.allow_close_delimited; return p; } @@ -93,6 +105,31 @@ const char* http_method_name(HTTPMethod method) { } } +bool http_method_allowed(uint32_t mask, HTTPMethod method) { + if ((uint32_t)method >= 32) return false; + return (mask & (1u << method)) != 0; +} + +string http_methods_allow_header(uint32_t mask) { + HTTPMethod methods[] = { + HTTP_METHOD_GET, + HTTP_METHOD_HEAD, + HTTP_METHOD_POST, + HTTP_METHOD_PUT, + HTTP_METHOD_DELETE, + HTTP_METHOD_OPTIONS + }; + + string out = string_repeat('\0', 0); + for (uint32_t i = 0; i < sizeof(methods) / sizeof(methods[0]);i++) { + if (!http_method_allowed(mask, methods[i])) continue; + if (out.length) string_append_bytes(&out, ", ", 2); + const char *name = http_method_name(methods[i]); + string_append_bytes(&out, name, (uint32_t)strlen(name)); + } + return out; +} + const char* http_status_reason(HttpError status) { switch (status) { case HTTP_CONTINUE: return "Continue"; @@ -107,6 +144,7 @@ const char* http_status_reason(HttpError status) { case HTTP_UNAUTHORIZED: return "Unauthorized"; case HTTP_FORBIDDEN: return "Forbidden"; case HTTP_NOT_FOUND: return "Not Found"; + case HTTP_METHOD_NOT_ALLOWED: return "Method Not Allowed"; case HTTP_PAYLOAD_TOO_LARGE: return "Payload Too Large"; case HTTP_URI_TOO_LONG: return "URI Too Long"; case HTTP_RANGE_NOT_SATISFIABLE: return "Range Not Satisfiable"; @@ -130,7 +168,8 @@ HttpError http_parse_result_status(HTTPParseResult result) { case HTTP_PARSE_MISSING_HOST: return HTTP_BAD_REQUEST; case HTTP_PARSE_UNSUPPORTED_TRANSFER: - case HTTP_PARSE_UNSUPPORTED_METHOD: + return HTTP_NOT_IMPLEMENTED; + case HTTP_PARSE_UNKNOWN_METHOD: return HTTP_NOT_IMPLEMENTED; case HTTP_PARSE_UNSUPPORTED_VERSION: return HTTP_VERSION_NOT_SUPPORTED; @@ -175,7 +214,10 @@ HTTPParseResult http_parse_request_line(const char *buf, uint32_t len, HTTPReque else if (mlen == 6 && memcmp(buf, "DELETE", 6) == 0) out->method = HTTP_METHOD_DELETE; else if (mlen == 4 && memcmp(buf, "HEAD", 4) == 0) out->method = HTTP_METHOD_HEAD; else if (mlen == 7 && memcmp(buf, "OPTIONS", 7) == 0) out->method = HTTP_METHOD_OPTIONS; - else return HTTP_PARSE_UNSUPPORTED_METHOD; + else { + for (uint32_t j = 0; j < mlen; j++) if (!is_alnum( buf[j]) && str_has_char("!#$%&'*+-.^_`|~", 0, buf[j]) < 0) return HTTP_PARSE_BAD_FORMAT; + return HTTP_PARSE_UNKNOWN_METHOD; + } i++; while (i < len && buf[i] == ' ')i++; @@ -300,6 +342,24 @@ void http_headers_extra_free(HTTPHeader *extra, uint32_t extra_count){ release(extra); } +void http_request_free(HTTPRequestMsg *req) { + if (!req) return; + if (req->path.mem_length) string_free(req->path); + http_headers_common_free(&req->headers_common); + http_headers_extra_free(req->extra_headers, req->extra_header_count); + if (req->body.mem_length) string_free(req->body); + *req = (HTTPRequestMsg){0}; +} + +void http_response_free(HTTPResponseMsg *res) { + if (!res) return; + if (res->reason.mem_length) string_free(res->reason); + http_headers_common_free(&res->headers_common); + http_headers_extra_free(res->extra_headers, res->extra_header_count); + if (res->body.mem_length) string_free(res->body); + *res = (HTTPResponseMsg){0}; +} + HTTPParseResult http_header_parse(const char *buf, uint32_t len, const HTTPPolicy *policy, HTTPHeadersCommon *C, HTTPHeader **out_extra, uint32_t *out_extra_count){ HTTPPolicy p = policy ? *policy : http_default_policy(); if (!buf || !C || !out_extra || !out_extra_count) return HTTP_PARSE_BAD_FORMAT; @@ -309,61 +369,47 @@ HTTPParseResult http_header_parse(const char *buf, uint32_t len, const HTTPPolic *out_extra = NULL; *out_extra_count = 0; - uint32_t max_lines = 0; - uint32_t count_pos = 0; - while (count_pos < len) { - uint32_t eol = count_pos; - bool has_crlf = false; - while (eol + 1 < len) { - if (buf[eol] == '\r' && buf[eol+1] == '\n') { - has_crlf = true; - break; - } - eol++; - } - if (!has_crlf) eol = len; - if (eol == count_pos) break; - - uint32_t sep = count_pos; - while (sep < eol && buf[sep] != ':') sep++; - if (sep == eol || sep == count_pos) return HTTP_PARSE_BAD_FORMAT; - - uint32_t key_len = sep - count_pos; - uint32_t val_start = sep + 1; - while (val_start < eol && is_whitespace(buf[val_start])) val_start++; - uint32_t val_end = eol; - while (val_end > val_start && is_whitespace(buf[val_end-1])) val_end--; - - if (key_len > p.max_header_key_len || key_len >= 128 || val_end - val_start > p.max_header_value_len) return HTTP_PARSE_TOO_LARGE; - max_lines++; - if (max_lines > p.max_header_count) return HTTP_PARSE_TOO_MANY_HEADERS; - if (!has_crlf) break; - count_pos = eol + 2; - } - HTTPHeader *extras = NULL; - if (max_lines){ - extras = (HTTPHeader*)zalloc(sizeof(*extras) * max_lines); + if (p.max_header_count) { + extras = (HTTPHeader*)zalloc(sizeof(*extras) * p.max_header_count); if (!extras) return HTTP_PARSE_TOO_LARGE; } HTTPParseResult result = HTTP_PARSE_OK; uint32_t extra_i = 0; + uint32_t header_i = 0; uint32_t pos = 0; + bool seen_host = false; + while (pos < len){ uint32_t eol = pos; bool has_crlf = false; - while (eol + 1 < len) { - if (buf[eol] == '\r' && buf[eol+1] == '\n') { - has_crlf = true; + while (eol < len) { + if (buf[eol] == 0) { + result = HTTP_PARSE_BAD_FORMAT; + break; + } + if (buf[eol] == '\r') { + if (eol + 1 >= len || buf[eol+1] != '\n') result = HTTP_PARSE_BAD_FORMAT; + else has_crlf = true; + break; + } + if (buf[eol] == '\n') { + result = HTTP_PARSE_BAD_FORMAT; break; } eol++; } - if (!has_crlf) eol = len; + if (result != HTTP_PARSE_OK) break; if (eol == pos) break; + header_i++; + if (header_i > p.max_header_count) { + result = HTTP_PARSE_TOO_MANY_HEADERS; + break; + } + uint32_t sep = pos; while (sep < eol && buf[sep] != ':') sep++; if (sep == eol || sep == pos) { @@ -379,6 +425,11 @@ HTTPParseResult http_header_parse(const char *buf, uint32_t len, const HTTPPolic uint32_t val_len = val_end - val_start; bool handled = false; + if (key_len > p.max_header_key_len || val_len > p.max_header_value_len) { + result = HTTP_PARSE_TOO_LARGE; + break; + } + if (key_len == 14 && strncmp_case(buf + pos, "content-length", true, key_len) == 0){ char len_buf[32]; bool ok = val_len > 0 && val_len < sizeof(len_buf); @@ -398,21 +449,29 @@ HTTPParseResult http_header_parse(const char *buf, uint32_t len, const HTTPPolic } handled = true; } else if (key_len == 12 && strncmp_case(buf + pos, "content-type", true, key_len) == 0){ + if (C->fields.content_type.mem_length) string_free(C->fields.content_type); C->fields.content_type = string_from_literal_length(buf + val_start, val_len); handled = true; } else if (key_len == 10 && strncmp_case(buf + pos, "connection", true, key_len) == 0){ + if (C->fields.connection.mem_length) string_free(C->fields.connection); C->fields.connection = string_from_literal_length(buf + val_start, val_len); C->framing.connection_close = http_header_value_has_token(buf + val_start, val_len, "close", 5) ? 1 : 0; C->framing.connection_keep_alive = http_header_value_has_token(buf + val_start, val_len, "keep-alive", 10) ? 1 : 0; handled = true; } else if (key_len == 4 && strncmp_case(buf + pos, "host", true, key_len) == 0) { - C->fields.host = string_from_literal_length(buf + val_start, val_len); + if (seen_host) result = HTTP_PARSE_BAD_FORMAT; + else { + seen_host = 1; + C->fields.host = string_from_literal_length(buf + val_start, val_len); + } handled = true; } else if (key_len == 6 && strncmp_case(buf + pos, "expect", true, key_len) == 0){ + if (C->fields.expect.mem_length) string_free(C->fields.expect); C->fields.expect = string_from_literal_length(buf + val_start, val_len); C->framing.expect_continue = http_header_value_has_token(buf + val_start, val_len, "100-continue", 12) ? 1 : 0; handled = true; } else if (key_len == 5 && strncmp_case(buf + pos, "range", true, key_len) == 0){ + if (C->fields.range.mem_length) string_free(C->fields.range); C->fields.range = string_from_literal_length(buf + val_start, val_len); C->range.has = 1; C->range.invalid = 1; @@ -441,7 +500,6 @@ HTTPParseResult http_header_parse(const char *buf, uint32_t len, const HTTPPolic rp++; } if (rp == rend && (has_start || has_end) && !(has_start && has_end && end < start) && !overflow) { - C->range.has = 1; C->range.invalid = 0; C->range.has_start = has_start ? 1 : 0; C->range.has_end = has_end ? 1 : 0; @@ -452,9 +510,11 @@ HTTPParseResult http_header_parse(const char *buf, uint32_t len, const HTTPPolic } handled = true; } else if (key_len == 8 && strncmp_case(buf + pos, "location", true, key_len) == 0) { + if (C->fields.location.mem_length) string_free(C->fields.location); C->fields.location = string_from_literal_length(buf + val_start, val_len); handled = true; } else if (key_len == 13 && strncmp_case(buf + pos, "content-range", true, key_len) == 0) { + if (C->fields.content_range.mem_length) string_free(C->fields.content_range); C->fields.content_range = string_from_literal_length(buf + val_start, val_len); handled = true; } else if (key_len == 17 && strncmp_case(buf + pos, "transfer-encoding", true, key_len) == 0){ @@ -502,11 +562,12 @@ HTTPParseResult http_header_parse(const char *buf, uint32_t len, const HTTPPolic handled = true; } + if (result != HTTP_PARSE_OK) break; if (!handled) { string key = string_from_literal_length((char*)(buf + pos), key_len); string value = string_from_literal_length((char*)(buf + val_start), val_len); - if (extras && extra_i < max_lines){ + if (extras && extra_i < p.max_header_count){ extras[extra_i++] = (HTTPHeader){ key, value }; } else { if (key.mem_length) string_free(key); @@ -522,6 +583,7 @@ HTTPParseResult http_header_parse(const char *buf, uint32_t len, const HTTPPolic if (result != HTTP_PARSE_OK) { http_headers_extra_free(extras, extra_i); + http_headers_common_free(C); *out_extra = NULL; *out_extra_count = 0; return result; @@ -534,7 +596,7 @@ HTTPParseResult http_header_parse(const char *buf, uint32_t len, const HTTPPolic return result; } - if (extra_i == max_lines){ + if (extra_i == p.max_header_count){ *out_extra = extras; *out_extra_count = extra_i; return result; @@ -573,8 +635,8 @@ static void http_append_chunked_body(string *out, uintptr_t ptr, uint32_t len) { string http_request_builder(const HTTPRequestMsg *R){ HTTPHeadersCommon common = R->headers_common; if (R->host_override) common.fields.host = R->host_override[0] ? string_from_const(R->host_override) : (string){0}; - if (!common.framing.chunked && (R->body.size || R->method == HTTP_METHOD_POST || R->method == HTTP_METHOD_PUT)) { - common.fields.content_length = (uint32_t)R->body.size; + if (!common.framing.chunked && (R->body.length || R->method == HTTP_METHOD_POST || R->method == HTTP_METHOD_PUT)) { + common.fields.content_length = R->body.length; common.framing.has_content_length = 1; } @@ -589,8 +651,8 @@ string http_request_builder(const HTTPRequestMsg *R){ string_append_bytes(&out, hdrs.data, hdrs.length); string_free(hdrs); - if (common.framing.chunked) http_append_chunked_body(&out, R->body.ptr, (uint32_t)R->body.size); - else if (R->body.ptr && R->body.size) string_append_bytes(&out, (char*)R->body.ptr, (uint32_t)R->body.size); + if (common.framing.chunked) http_append_chunked_body(&out, (uintptr_t)R->body.data, R->body.length); + else if ((uintptr_t)R->body.data && R->body.length) string_append_bytes(&out, R->body.data, R->body.length); return out; } @@ -599,7 +661,7 @@ string http_response_builder(const HTTPResponseMsg *R){ HTTPHeadersCommon common = R->headers_common; bool informational = R->status_code >= 100 && R->status_code < 200; if (!informational && !common.framing.chunked) { - if (!common.framing.has_content_length && !common.fields.content_length) common.fields.content_length = (uint32_t)R->body.size; + if (!common.framing.has_content_length && !common.fields.content_length) common.fields.content_length = R->body.length; common.framing.has_content_length = 1; } @@ -616,8 +678,8 @@ string http_response_builder(const HTTPResponseMsg *R){ string_free(hdrs); if (!informational) { - if (common.framing.chunked) http_append_chunked_body(&out, R->body.ptr, (uint32_t)R->body.size); - else if (R->body.ptr && R->body.size) string_append_bytes(&out, (char*)R->body.ptr, (uint32_t)R->body.size); + if (common.framing.chunked) http_append_chunked_body(&out, (uintptr_t)R->body.data, R->body.length); + else if ((uintptr_t)R->body.data && R->body.length) string_append_bytes(&out, R->body.data, R->body.length); } return out; diff --git a/kernel/networking/application_layer/http.h b/kernel/networking/application_layer/http.h index b92d8eda..fc2fc552 100644 --- a/kernel/networking/application_layer/http.h +++ b/kernel/networking/application_layer/http.h @@ -30,6 +30,14 @@ typedef enum { HTTP_METHOD_OPTIONS } HTTPMethod; +#define HTTP_METHOD_MASK_GET (1u << HTTP_METHOD_GET) +#define HTTP_METHOD_MASK_POST (1u << HTTP_METHOD_POST) +#define HTTP_METHOD_MASK_PUT (1u << HTTP_METHOD_PUT) +#define HTTP_METHOD_MASK_DELETE (1u << HTTP_METHOD_DELETE) +#define HTTP_METHOD_MASK_HEAD (1u << HTTP_METHOD_HEAD) +#define HTTP_METHOD_MASK_OPTIONS (1u << HTTP_METHOD_OPTIONS) +#define HTTP_METHOD_MASK_ALL (HTTP_METHOD_MASK_GET | HTTP_METHOD_MASK_POST | HTTP_METHOD_MASK_PUT | HTTP_METHOD_MASK_DELETE | HTTP_METHOD_MASK_HEAD | HTTP_METHOD_MASK_OPTIONS) + typedef enum { HTTP_VERSION_UNKNOWN, HTTP_VERSION_10, @@ -46,7 +54,7 @@ typedef enum { HTTP_PARSE_MISSING_HOST, HTTP_PARSE_UNSUPPORTED_VERSION, HTTP_PARSE_PAYLOAD_TOO_LARGE, - HTTP_PARSE_UNSUPPORTED_METHOD, + HTTP_PARSE_UNKNOWN_METHOD, HTTP_PARSE_INCOMPLETE } HTTPParseResult; @@ -63,6 +71,7 @@ typedef enum { HTTP_UNAUTHORIZED = 401, HTTP_FORBIDDEN = 403, HTTP_NOT_FOUND = 404, + HTTP_METHOD_NOT_ALLOWED = 405, HTTP_PAYLOAD_TOO_LARGE = 413, HTTP_URI_TOO_LONG = 414, HTTP_RANGE_NOT_SATISFIABLE = 416, @@ -87,6 +96,37 @@ typedef enum { HTTP_CHUNK_STAGE_DONE } HTTPChunkStage; +typedef enum { + HTTP_POLICY_OPT_MAX_START_LINE = 1u << 0, + HTTP_POLICY_OPT_MAX_HEADER_BYTES = 1u << 1, + HTTP_POLICY_OPT_MAX_HEADER_COUNT = 1u << 2, + HTTP_POLICY_OPT_MAX_HEADER_KEY_LEN = 1u << 3, + HTTP_POLICY_OPT_MAX_HEADER_VALUE_LEN = 1u << 4, + HTTP_POLICY_OPT_MAX_PATH_LEN = 1u << 5, + HTTP_POLICY_OPT_MAX_BODY_BYTES = 1u << 6, + HTTP_POLICY_OPT_HEADER_IDLE_TIMEOUT_MS = 1u << 7, + HTTP_POLICY_OPT_HEADER_TOTAL_TIMEOUT_MS = 1u << 8, + HTTP_POLICY_OPT_BODY_IDLE_TIMEOUT_MS = 1u << 9, + HTTP_POLICY_OPT_BODY_TOTAL_TIMEOUT_MS = 1u << 10, + HTTP_POLICY_OPT_ALLOW_CHUNKED = 1u << 11 +} HTTPPolicyOptionFlag; + +typedef enum { + HTTP_SERVER_OPT_MAX_KEEPALIVE_REQUESTS = 1u << 0, + HTTP_SERVER_OPT_ALLOWED_METHODS = 1u << 1, + HTTP_SERVER_OPT_ERROR_CONTENT_TYPE = 1u << 2, + HTTP_SERVER_OPT_ALLOW_KEEP_ALIVE = 1u << 3, + HTTP_SERVER_OPT_ALLOW_ABSOLUTE_URI = 1u << 4, + HTTP_SERVER_OPT_REQUIRE_HOST_HTTP11 = 1u << 5, + HTTP_SERVER_OPT_SEND_ERROR_BODY = 1u << 6 +} HTTPServerPolicyOptionFlag; + +typedef enum { + HTTP_CLIENT_OPT_MAX_REDIRECTS = 1u << 0, + HTTP_CLIENT_OPT_FOLLOW_REDIRECTS = 1u << 1, + HTTP_CLIENT_OPT_ALLOW_CLOSE_DELIMITED = 1u << 2 +} HTTPClientPolicyOptionFlag; + typedef struct { uint32_t max_start_line; uint32_t max_header_bytes; @@ -103,34 +143,25 @@ typedef struct { } HTTPPolicy; typedef struct { - int32_t max_start_line; - int32_t max_header_bytes; - int32_t max_header_count; - int32_t max_header_key_len; - int32_t max_header_value_len; - int32_t max_path_len; - int32_t max_body_bytes; - int32_t header_idle_timeout_ms; - int32_t header_total_timeout_ms; - int32_t body_idle_timeout_ms; - int32_t body_total_timeout_ms; - int32_t allow_chunked; + HTTPPolicy value; + uint32_t flags; } HTTPPolicyOptions; typedef struct { HTTPPolicy common; uint32_t max_keepalive_requests; + uint32_t allowed_methods; + const char *error_content_type; bool allow_keep_alive; bool allow_absolute_uri; bool require_host_http11; + bool send_error_body; } HTTPServerPolicy; typedef struct { - HTTPPolicyOptions common; - int32_t max_keepalive_requests; - int32_t allow_keep_alive; - int32_t allow_absolute_uri; - int32_t require_host_http11; + HTTPServerPolicy value; + uint32_t flags; + uint32_t common_flags; } HTTPServerPolicyOptions; typedef struct { @@ -141,10 +172,9 @@ typedef struct { } HTTPClientPolicy; typedef struct { - HTTPPolicyOptions common; - int32_t max_redirects; - int32_t follow_redirects; - int32_t allow_close_delimited; + HTTPClientPolicy value; + uint32_t flags; + uint32_t common_flags; } HTTPClientPolicyOptions; typedef struct { @@ -209,7 +239,7 @@ typedef struct { HTTPHeadersCommon headers_common; HTTPHeader *extra_headers; uint32_t extra_header_count; - sizedptr body; + string body; } HTTPRequestMsg; typedef struct { @@ -218,7 +248,7 @@ typedef struct { HTTPHeadersCommon headers_common; HTTPHeader *extra_headers; uint32_t extra_header_count; - sizedptr body; + string body; } HTTPResponseMsg; typedef struct { @@ -234,10 +264,12 @@ typedef struct { } HTTPChunkedDecoder; HTTPPolicy http_default_policy(void); -HTTPPolicy http_policy_from_options(const HTTPPolicyOptions *options); +HTTPPolicy http_policy_apply_options(HTTPPolicy base, const HTTPPolicyOptions *options); HTTPServerPolicy http_server_policy_from_options(const HTTPServerPolicyOptions *options); HTTPClientPolicy http_client_policy_from_options(const HTTPClientPolicyOptions *options); const char* http_method_name(HTTPMethod method); +bool http_method_allowed(uint32_t mask, HTTPMethod method); +string http_methods_allow_header(uint32_t mask); const char* http_status_reason(HttpError status); HttpError http_parse_result_status(HTTPParseResult result); bool http_header_value_has_token(const char *buf, uint32_t len, const char *token, uint32_t token_len); @@ -262,6 +294,8 @@ void http_chunked_decoder_free(HTTPChunkedDecoder *dec); void http_headers_common_free(HTTPHeadersCommon *common); void http_headers_extra_free(HTTPHeader *extra, uint32_t extra_count); +void http_request_free(HTTPRequestMsg *req); +void http_response_free(HTTPResponseMsg *res); string http_request_builder(const HTTPRequestMsg *req); diff --git a/kernel/networking/application_layer/socket_http_client.hpp b/kernel/networking/application_layer/socket_http_client.hpp index 445ed3e0..03e4353c 100644 --- a/kernel/networking/application_layer/socket_http_client.hpp +++ b/kernel/networking/application_layer/socket_http_client.hpp @@ -25,15 +25,6 @@ class HTTPClient { HTTPClientPolicy policy; HTTPClientOrigin origin; - void free_response(HTTPResponseMsg *resp) { - if (!resp) return; - if (resp->body.ptr && resp->body.size) release((void*)resp->body.ptr); - if (resp->reason.mem_length) string_free(resp->reason); - http_headers_common_free(&resp->headers_common); - http_headers_extra_free(resp->extra_headers, resp->extra_header_count); - *resp = HTTPResponseMsg{}; - } - HTTPResponseMsg error_response(int64_t err) { HTTPResponseMsg resp{}; resp.status_code = (HttpError)err; @@ -105,7 +96,7 @@ class HTTPClient { &resp.extra_header_count); if (header_result != HTTP_PARSE_OK) { - free_response(&resp); + http_response_free(&resp); string_free(buf); if (sock) { close_socket(sock); @@ -156,7 +147,7 @@ class HTTPClient { if (chunk_result != HTTP_PARSE_OK) { http_chunked_decoder_free(&dec); - free_response(&resp); + http_response_free(&resp); string_free(buf); if (sock) { close_socket(sock); @@ -169,15 +160,14 @@ class HTTPClient { dec.body = string{}; http_chunked_decoder_free(&dec); if (decoded.length) { - resp.body.ptr = (uintptr_t)decoded.data; - resp.body.size = decoded.length; + resp.body = decoded; } else if (decoded.mem_length) string_free(decoded); } else { uint32_t need = resp.headers_common.framing.has_content_length ? resp.headers_common.fields.content_length : 0; if (resp.headers_common.framing.has_content_length) { if (need > policy.common.max_body_bytes) { - free_response(&resp); + http_response_free(&resp); string_free(buf); if (sock) { close_socket(sock); @@ -189,7 +179,7 @@ class HTTPClient { if (need) { char *body_copy = (char*)zalloc(need); if (!body_copy) { - free_response(&resp); + http_response_free(&resp); string_free(buf); if (sock) { close_socket(sock); @@ -218,7 +208,7 @@ class HTTPClient { if (copied < need) { release(body_copy); - free_response(&resp); + http_response_free(&resp); string_free(buf); if (sock) { close_socket(sock); @@ -227,8 +217,7 @@ class HTTPClient { return error_response(SOCK_ERR_PROTO); } - resp.body.ptr = (uintptr_t)body_copy; - resp.body.size = need; + resp.body = (string){body_copy, need, need}; } } else if (policy.allow_close_delimited) { string body = string_repeat('\0', 0); @@ -242,7 +231,7 @@ class HTTPClient { uint32_t now = (uint32_t)get_time(); if ((now - body_last_rx_ms) > policy.common.body_idle_timeout_ms || (now - body_start_ms) > policy.common.body_total_timeout_ms) { string_free(body); - free_response(&resp); + http_response_free(&resp); string_free(buf); if (sock) { close_socket(sock); @@ -255,7 +244,7 @@ class HTTPClient { } if (r < 0) { string_free(body); - free_response(&resp); + http_response_free(&resp); string_free(buf); if (sock) { close_socket(sock); @@ -266,7 +255,7 @@ class HTTPClient { if (r == 0) break; if (body.length + (uint32_t)r > policy.common.max_body_bytes) { string_free(body); - free_response(&resp); + http_response_free(&resp); string_free(buf); if (sock) { close_socket(sock); @@ -279,8 +268,7 @@ class HTTPClient { } if (body.length) { - resp.body.ptr = (uintptr_t)body.data; - resp.body.size = body.length; + resp.body = body; } else if (body.mem_length) string_free(body); } } @@ -290,7 +278,7 @@ class HTTPClient { ev.action = NETLOG_ACT_HTTP_RECV_RESPONSE; ev.pid = get_current_proc_pid(); ev.u0 = (uint32_t)resp.status_code; - ev.u1 = (uint32_t)resp.body.size; + ev.u1 = (uint32_t)resp.body.length; ev.local_port = get_socket_local_port(sock); net_l4_endpoint sock_remote_ep{}; get_socket_remote_endpoint(sock, &sock_remote_ep); @@ -492,7 +480,7 @@ class HTTPClient { if (rr >= 0) rr = connect_domain(next_domain.data, next_port); if (next_domain.mem_length) string_free(next_domain); if (rr < 0) { - free_response(&resp); + http_response_free(&resp); resp.status_code = (HttpError)rr; break; } @@ -510,7 +498,7 @@ class HTTPClient { if (rr >= 0) rr = reconnect_domain.data ? connect_domain(reconnect_domain.data, reconnect_ep.port) : connect_endpoint(&reconnect_ep); if (reconnect_domain.mem_length) string_free(reconnect_domain); if (rr < 0) { - free_response(&resp); + http_response_free(&resp); resp.status_code = (HttpError)rr; break; } @@ -518,13 +506,13 @@ class HTTPClient { if (code == HTTP_SEE_OTHER && curr.method != HTTP_METHOD_HEAD) { curr.method = HTTP_METHOD_GET; - curr.body = sizedptr{}; + curr.body = string{}; curr.headers_common.framing.chunked = 0; curr.headers_common.framing.has_content_length = 0; curr.headers_common.fields.content_length = 0; } - free_response(&resp); + http_response_free(&resp); } if (curr.path.mem_length) string_free(curr.path); diff --git a/kernel/networking/application_layer/socket_http_server.hpp b/kernel/networking/application_layer/socket_http_server.hpp index 799a38f6..18a918a7 100644 --- a/kernel/networking/application_layer/socket_http_server.hpp +++ b/kernel/networking/application_layer/socket_http_server.hpp @@ -172,6 +172,9 @@ class HTTPServer { if (line_result != HTTP_PARSE_OK) { bad_request = true; reject_status = http_parse_result_status(line_result); + } else if (!http_method_allowed(policy.allowed_methods, line.method)) { + bad_request = true; + reject_status = HTTP_METHOD_NOT_ALLOWED; } } @@ -294,8 +297,7 @@ class HTTPServer { string decoded = dec.body; dec.body = string{}; if (decoded.length) { - req.body.ptr = (uintptr_t)decoded.data; - req.body.size = decoded.length; + req.body = decoded; } else if (decoded.mem_length) string_free(decoded); } http_chunked_decoder_free(&dec); @@ -339,8 +341,7 @@ class HTTPServer { if (!bad_request) consumed = body_start + need; if (body_copy && !bad_request) { - req.body.ptr = (uintptr_t)body_copy; - req.body.size = need; + req.body = (string){body_copy, need, need}; } } } @@ -348,23 +349,37 @@ class HTTPServer { if (bad_request) { if (body_copy) release(body_copy); const char *reason = http_status_reason(reject_status); - string body = string_format("%s\n", reason); + string body = policy.send_error_body ? string_format("%s\n", reason) : string{}; HTTPResponseMsg res{}; + HTTPHeader allow_header{}; + string allow_value{}; + static char allow_key[] = "Allow"; + res.status_code = reject_status; - res.reason = string_from_literal(reason); res.headers_common.fields.content_length = body.length; - res.headers_common.fields.content_type = string_from_literal("text/plain"); - res.headers_common.fields.connection = string_from_literal("close"); - res.body.ptr = (uintptr_t)body.data; - res.body.size = body.length; + res.headers_common.framing.has_content_length = 1; + if (policy.error_content_type) res.headers_common.fields.content_type = string_from_literal(policy.error_content_type); + res.body = body; + + if (reject_status == HTTP_METHOD_NOT_ALLOWED) { + allow_value = http_methods_allow_header(policy.allowed_methods); + allow_header.key = string{allow_key, sizeof(allow_key) - 1, 0}; + allow_header.value = allow_value; + res.extra_headers = &allow_header; + res.extra_header_count =1; + } + + conn->close_after_response = true; send_response(conn, res); + if (conn->client) { + close_socket(conn->client); + conn->client = 0; + } - if (req.path.mem_length) string_free(req.path); - http_headers_common_free(&req.headers_common); - http_headers_extra_free(req.extra_headers, req.extra_header_count); + http_request_free(&req); http_headers_common_free(&res.headers_common); - if (res.reason.mem_length) string_free(res.reason); - string_free(body); + if (allow_value.mem_length) string_free(allow_value); + if (body.mem_length) string_free(body); string_free(buf); return HTTPRequestMsg{}; } @@ -375,7 +390,7 @@ class HTTPServer { ev.pid = get_current_proc_pid(); ev.u0 = (uint32_t)req.method; ev.u1 = (uint32_t)req.path.length; - ev.i0 = (int64_t)req.body.size; + ev.i0 = (int64_t)req.body.length; ev.local_port = get_socket_local_port(conn->client); net_l4_endpoint conn_remote_ep{}; get_socket_remote_endpoint(conn->client, &conn_remote_ep); @@ -424,13 +439,13 @@ class HTTPServer { } if (suppress_body) { - head.body = sizedptr{}; + head.body = string{}; head.headers_common.framing.chunked = 0; } - uint32_t body_len = (!send_chunked && !suppress_body && res.body.ptr && res.body.size) ? (uint32_t)res.body.size : 0; + uint32_t body_len = (!send_chunked && !suppress_body && res.body.data && res.body.length) ? (uint32_t)res.body.length : 0; if (!send_chunked) { - head.body = sizedptr{}; + head.body = string{}; if (body_len && !head.headers_common.framing.has_content_length) { head.headers_common.fields.content_length = body_len; head.headers_common.framing.has_content_length = 1; @@ -456,7 +471,7 @@ class HTTPServer { combo = (uint8_t*)zalloc(out.length + first_body_len); if (combo) { memcpy(combo, out.data, out.length); - memcpy(combo + out.length, (const void*)res.body.ptr, first_body_len); + memcpy(combo + out.length, (const void*)res.body.data, first_body_len); first_ptr = combo; first_len = out.length + first_body_len; @@ -487,7 +502,7 @@ class HTTPServer { if (sent >= 0) body_off = first_body_len; if (sent >= 0 && body_len) { - const uint8_t* body = (const uint8_t*)res.body.ptr; + const uint8_t* body = (const uint8_t*)res.body.data; while (body_off < body_len) { uint32_t ask = body_len - body_off; 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 3f53bf6f..c6191803 100644 --- a/kernel/networking/drivers/virtio_net_pci/virtio_net_pci.cpp +++ b/kernel/networking/drivers/virtio_net_pci/virtio_net_pci.cpp @@ -39,7 +39,7 @@ typedef struct __attribute__((packed)) virtio_net_ctrl_ack_t { #define VIRTIO_NET_CTRL_MAC_TABLE_SET 0 -void virtio_net_rx_free(void* ctx, uintptr_t base, uint32_t alloc_size) { +void virtio_net_rx_free(void* ctx, uintptr_t base) { VirtioNetDriver* driver = (VirtioNetDriver*)ctx; if (!driver || !driver->rx_qsz || !driver->rx_avail || !driver->rx_pool) return; if (base < (uintptr_t)driver->rx_pool) return; @@ -340,14 +340,14 @@ netpkt_t* VirtioNetDriver::handle_receive_packet(){ if (desc_index >= rx_qsz) return nullptr; uintptr_t buf = (uintptr_t)PHYS_TO_VIRT_P((void*)rx_desc[desc_index].addr); if (total_len <= (uint32_t)header_size || total_len > rx_desc[desc_index].len) { - virtio_net_rx_free(this, buf, RX_BUF_SIZE); + virtio_net_rx_free(this, buf); return nullptr; } uint32_t payload_len = total_len - (uint32_t)header_size; netpkt_t* pkt = netpkt_wrap(buf, RX_BUF_SIZE, header_size, payload_len, virtio_net_rx_free, this); if (!pkt) { - virtio_net_rx_free(this, buf, RX_BUF_SIZE); + virtio_net_rx_free(this, buf); return nullptr; } 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 db7e541a..2ba0919e 100644 --- a/kernel/networking/drivers/virtio_net_pci/virtio_net_pci.hpp +++ b/kernel/networking/drivers/virtio_net_pci/virtio_net_pci.hpp @@ -52,7 +52,7 @@ class VirtioNetDriver : public NetDriver { VirtioNetDriver(); ~VirtioNetDriver(); - bool init_at(uint64_t pci_addr, uint32_t irq_base_vector); + bool init_at(uint64_t pci_addr, uint32_t irq_base_vector) override; void get_mac(uint8_t out_mac[6]) const override; uint16_t get_mtu() const override; uint16_t get_header_size() const override; @@ -70,7 +70,7 @@ class VirtioNetDriver : public NetDriver { netdev_tx_result_t send_packet(netpkt_t* packet) override; private: - friend void virtio_net_rx_free(void* ctx, uintptr_t base, uint32_t alloc_size); + friend void virtio_net_rx_free(void* ctx, uintptr_t base); virtio_device vnp_net_dev = {}; volatile virtq_desc* rx_desc = nullptr; diff --git a/kernel/networking/internet_layer/icmp.c b/kernel/networking/internet_layer/icmp.c index dd56198b..5a6341c5 100644 --- a/kernel/networking/internet_layer/icmp.c +++ b/kernel/networking/internet_layer/icmp.c @@ -115,7 +115,7 @@ bool icmp_ping(uint32_t dst_ip, uint16_t id, uint16_t seq, uint32_t timeout_ms, } memcpy(p, (const void*)buf, tot_len); release((void*)buf); - ipv4_send_packet(dst_ip, 1, pkt, (const ipv4_tx_opts_t*)tx_opts_or_null, (uint8_t)ttl, 0); + ipv4_send_packet(dst_ip, 1, pkt, (const ip_tx_opts_t*)tx_opts_or_null, (uint8_t)ttl, 0); uint32_t start = (uint32_t)get_time(); for (;;) { @@ -218,7 +218,7 @@ void icmp_input(netpkt_t* pkt, uint32_t src_ip, uint32_t dst_ip) { 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 = IP_TX_BOUND_L3}; + ip_tx_opts_t o = {.index = l3->l3_id, .scope = IP_TX_BOUND_L3}; uint32_t headroom = (uint32_t)sizeof(eth_hdr_t) + (uint32_t)sizeof(ipv4_hdr_t); netpkt_t* pkt = netpkt_alloc(rlen, headroom, 0); if (pkt) { diff --git a/kernel/networking/internet_layer/icmpv6.c b/kernel/networking/internet_layer/icmpv6.c index 8a107769..407e537a 100644 --- a/kernel/networking/internet_layer/icmpv6.c +++ b/kernel/networking/internet_layer/icmpv6.c @@ -146,7 +146,7 @@ static bool icmpv6_send_echo_request(const uint8_t dst_ip[16], uint16_t id, uint e.hdr.checksum = bswap16(checksum16_pipv6(plan.src_ip, dst_ip, PROTO_ICMPV6, (const uint8_t*)buf, len)); memcpy(buf, &e, sizeof(e)); - ipv6_send_packet(dst_ip, PROTO_ICMPV6, pkt, (const ipv6_tx_opts_t*)tx_opts_or_null, hop_limit ? hop_limit : 64, 0); + ipv6_send_packet(dst_ip, PROTO_ICMPV6, pkt, (const ip_tx_opts_t*)tx_opts_or_null, hop_limit ? hop_limit : 64, 0); return true; } diff --git a/kernel/networking/internet_layer/igmp.c b/kernel/networking/internet_layer/igmp.c index e36bae68..541e4f82 100644 --- a/kernel/networking/internet_layer/igmp.c +++ b/kernel/networking/internet_layer/igmp.c @@ -59,7 +59,7 @@ static bool send_igmp(uint8_t ifindex, uint32_t dst, uint8_t type, uint32_t grou h->checksum = checksum16(igmp_words, sizeof(igmp_hdr_t)/2); memcpy((void*)netpkt_data(pkt), h, sizeof(*h)); - ipv4_tx_opts_t tx; + ip_tx_opts_t tx; tx.scope = IP_TX_BOUND_L2; tx.index = ifindex; diff --git a/kernel/networking/internet_layer/ipv4.c b/kernel/networking/internet_layer/ipv4.c index 8234b093..4d1303a3 100644 --- a/kernel/networking/internet_layer/ipv4.c +++ b/kernel/networking/internet_layer/ipv4.c @@ -12,316 +12,202 @@ #include "ipv4_utils.h" #include "net/network_types.h" #include "networking/link_layer/nic_types.h" +#include "networking/net_fragbuf.h" static uint16_t g_ip_ident = 1; -static l3_ipv4_interface_t* best_v4_on_l2_for_dst(l2_interface_t* l2, uint32_t dst) { - l3_ipv4_interface_t* best = NULL; - uint32_t 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 && (ipv4_net(dst, m) == ipv4_net(v4->ip, m))) { - uint32_t pl = ipv4_prefix_len(m); - if (pl > best_pl) { - best_pl = pl; - best = v4; - } - } else if (!best) { - best = v4; - } - } - return best; +#define IPV4_REASS_SLOTS 8 +typedef struct { + uint8_t used; + uint8_t ifindex; + uint16_t ident; + uint8_t proto; + uint32_t src; + uint32_t dst; + uint32_t last_update_ms; + net_fragbuf_t frag; +} ipv4_reass_slot_t; + +static ipv4_reass_slot_t g_ipv4_reass[IPV4_REASS_SLOTS]; + +static void ipv4_reass_free(ipv4_reass_slot_t *s) { + if (!s) return; + net_fragbuf_free(&s->frag); + memset(s, 0, sizeof(*s)); } -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; +bool ipv4_send_packet(uint32_t dst_ip, uint8_t proto, netpkt_t* pkt, const ip_tx_opts_t* opts, uint8_t ttl, uint8_t dontfrag) { + if (!pkt || !netpkt_len(pkt)) { + if (pkt) netpkt_unref(pkt); + return false; } - 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; -} + uint8_t ifx = 0; + uint32_t src_ip = 0; + uint32_t nh = dst_ip; + l3_ipv4_interface_t* src_v4 = NULL; -static bool pick_route_global(uint32_t dst, uint8_t* out_ifx, uint32_t* out_src, uint32_t* out_nh) { - if (ipv4_is_limited_broadcast(dst)) return false; - - 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 && (ipv4_net(dst, m) == ipv4_net(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 (ipv4_is_limited_broadcast(dst_ip)) { + if (!opts || opts->scope != IP_TX_BOUND_L3) { + netpkt_unref(pkt); + return false; } - 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 (ipv4_is_limited_broadcast(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 && (ipv4_net(dst, m) == ipv4_net(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; + src_v4 = l3_ipv4_find_by_id(opts->index); + if (!src_v4 || !src_v4->l2 || !src_v4->l2->is_up || src_v4->mode == IPV4_CFG_DISABLED) { + netpkt_unref(pkt); + return false; } - } - 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 (ipv4_is_limited_broadcast(dst)) return false; - - 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 && (ipv4_net(dst, m) == ipv4_net(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; + ifx = src_v4->l2->ifindex; + src_ip = src_v4->ip; + nh = 0xFFFFFFFF; + } else { + ipv4_tx_plan_t plan; + if (!ipv4_build_tx_plan(dst_ip, opts, &plan)) { + netpkt_unref(pkt); + return false; } - } - 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 == IP_TX_BOUND_L3) return pick_route_bound_l3(opts->index, dst, out_ifx, out_src, out_nh); - if (opts->scope == IP_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); -} + src_v4 = l3_ipv4_find_by_id(plan.l3_id); + if (!src_v4 || !src_v4->l2 || !src_v4->l2->is_up || src_v4->mode == IPV4_CFG_DISABLED || !src_v4->ip) { + netpkt_unref(pkt); + return false; + } + ifx = src_v4->l2->ifindex; + src_ip = plan.src_ip; -bool ipv4_send_packet(uint32_t dst_ip, uint8_t proto, netpkt_t* pkt, const ipv4_tx_opts_t* opts, uint8_t ttl, uint8_t dontfrag) { - if (!pkt || !netpkt_len(pkt)) { - if (pkt) netpkt_unref(pkt); - return false; - } + uint32_t route_nh = 0; + bool have_nh = false; - uint8_t ifx = 0; - uint32_t src_ip = 0; - uint32_t nh = 0; - if (!pick_route(dst_ip, opts, &ifx, &src_ip, &nh)) { - netpkt_unref(pkt); - return false; + if (src_v4->routing_table && ipv4_rt_lookup_in(src_v4->routing_table, dst_ip, &route_nh, NULL, NULL)) { + nh = route_nh ? route_nh : dst_ip; + have_nh = true; + } + if (!have_nh && src_v4->mask && ipv4_net(dst_ip, src_v4->mask) == ipv4_net(src_v4->ip, src_v4->mask)) { + nh = dst_ip; + have_nh = true; + } + if (!have_nh && src_v4->gw) { + nh = src_v4->gw; + have_nh = true; + } + if (!have_nh) { + netpkt_unref(pkt); + return false; + } } + l2_interface_t* l2 = src_v4->l2; uint8_t dst_mac[6]; - bool is_dbcast = false; bool need_arp = 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 (v4->mask && ipv4_broadcast_calc(v4->ip, v4->mask) == dst_ip) { - is_dbcast = true; - break; - } - } - } + bool is_dbcast = src_v4->mask && nh == dst_ip && ipv4_broadcast_calc(src_v4->ip, src_v4->mask) == dst_ip; if (ipv4_is_limited_broadcast(dst_ip) || is_dbcast) memset(dst_mac, 0xFF, 6); else if (ipv4_is_multicast(dst_ip)) ipv4_mcast_to_mac(dst_ip, dst_mac); - else if (l2 && l2->kind == NET_IFK_LOCALHOST) memset(dst_mac, 0, 6); + else if (l2->kind == NET_IFK_LOCALHOST) memset(dst_mac, 0, 6); else need_arp = true; - uint16_t mtu = 1500; - if (l2) { - 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 != src_ip) continue; - if (v4->runtime_opts_v4.mtu) mtu = v4->runtime_opts_v4.mtu; - break; + uint16_t mtu = src_v4->runtime_opts_v4.mtu ? src_v4->runtime_opts_v4.mtu : 1500; + uint32_t hdr_len = IP_IHL_NOOPTS * 4; + uint32_t seg_len = netpkt_len(pkt); + uint32_t total = hdr_len + seg_len; + + if (total <= (uint32_t)mtu) { + void* hdrp = netpkt_push(pkt, hdr_len); + if (!hdrp) { + netpkt_unref(pkt); + return false; } + + uint16_t ip_[sizeof(ipv4_hdr_t)/sizeof(uint16_t)]; + ipv4_hdr_t* ip = (ipv4_hdr_t*)ip_; + 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++); + uint16_t ff = 0; + if (dontfrag) ff |= 0x4000; + ip->flags_frag_offset = bswap16(ff); + ip->ttl = ttl ? ttl : IP_TTL_DEFAULT; + ip->protocol = proto; + ip->header_checksum = 0; + ip->src_ip = bswap32(src_ip); + ip->dst_ip = bswap32(dst_ip); + ip->header_checksum = checksum16(ip_, hdr_len / 2); + memcpy(hdrp, ip, sizeof(*ip)); + + if (need_arp) return arp_send_or_queue_on(ifx, nh, pkt); + return eth_send_frame_on(ifx, ETHERTYPE_IPV4, dst_mac, pkt); } - uint32_t hdr_len = IP_IHL_NOOPTS * 4; - uint32_t seg_len = netpkt_len(pkt); - void* hdrp = netpkt_push(pkt, hdr_len); - if (!hdrp) { + if (dontfrag || (uint32_t)mtu < hdr_len + 8) { netpkt_unref(pkt); return false; } - uint32_t total = hdr_len + seg_len; - if (dontfrag && total > (uint32_t)mtu) { + uint32_t max_chunk = ((uint32_t)mtu - hdr_len) / 8 * 8; + if (!max_chunk) { netpkt_unref(pkt); return false; } - uint16_t ip_[sizeof(ipv4_hdr_t)/sizeof(uint16_t)]; - ipv4_hdr_t* ip = (ipv4_hdr_t*)ip_; - 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++); - uint16_t ff = 0; - if (dontfrag) ff |= 0x4000u; - ip->flags_frag_offset = bswap16(ff); - ip->ttl = ttl ? ttl : IP_TTL_DEFAULT; - ip->protocol = proto; - ip->header_checksum = 0; - ip->src_ip = bswap32(src_ip); - ip->dst_ip = bswap32(dst_ip); - ip->header_checksum = checksum16(ip_, hdr_len / 2); - memcpy(hdrp, ip, sizeof(*ip)); - - if (need_arp) return arp_send_or_queue_on(ifx, nh, pkt); - return eth_send_frame_on(ifx, ETHERTYPE_IPV4, dst_mac, pkt); -} -void ipv4_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { - if (!pkt) return; - uint32_t ip_len = netpkt_len(pkt); - if (ip_len < sizeof(ipv4_hdr_t)) return; + uint16_t ident = g_ip_ident++; + uint32_t off = 0; + const uint8_t* data = (const uint8_t*)netpkt_data(pkt); + bool ok = true; - uint8_t first = 0; - if (!netpkt_copyout(pkt, 0, &first, sizeof(first))) return; - uint8_t ver = (uint8_t)(first >> 4); - uint8_t ihl = (uint8_t)(first & 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 hdr_words[60 /sizeof(uint16_t)]; - uint8_t* hdr_copy = (uint8_t*)hdr_words; - if (!netpkt_copyout(pkt, 0, hdr_copy, hdr_len)) return; - if (checksum16(hdr_words, hdr_len / 2) != 0) return; - ipv4_hdr_t ip; - memcpy(&ip, hdr_copy, sizeof(ip)); + while (off < seg_len) { + uint32_t remain = seg_len - off; + uint32_t chunk = remain > max_chunk ? max_chunk : remain; + uint8_t more = off + chunk < seg_len ? 1 : 0; + uint32_t frame_len = hdr_len + chunk; - uint16_t ip_totlen = bswap16(ip.total_length); - if (ip_totlen < hdr_len) return; - if (ip_len < ip_totlen) return; - (void)netpkt_trim(pkt, ip_totlen); - ip_len = ip_totlen; - - uint32_t l4_len = (uint32_t)ip_totlen - hdr_len; - - uint32_t src = bswap32(ip.src_ip); - uint32_t dst = bswap32(ip.dst_ip); + netpkt_t* fpkt = netpkt_alloc(frame_len, sizeof(eth_hdr_t), 0); + if (!fpkt) { + ok = false; + break; + } - 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 { - arp_table_put_for_l2((uint8_t)ifindex, src, mac_old, 180000, false); + void* buf = netpkt_put(fpkt, frame_len); + if (!buf) { + netpkt_unref(fpkt); + ok = false; + break; } + + uint16_t ip_[sizeof(ipv4_hdr_t) / sizeof(uint16_t)]; + ipv4_hdr_t* ip = (ipv4_hdr_t*)ip_; + ip->version_ihl = (uint8_t)((IP_VERSION_4 << 4) | IP_IHL_NOOPTS); + ip->dscp_ecn = 0; + ip->total_length = bswap16((uint16_t)frame_len); + ip->identification = bswap16(ident); + uint16_t ff = (uint16_t)((off / 8) & 0x1FFF); + if (more) ff |= 0x2000; + ip->flags_frag_offset = bswap16(ff); + ip->ttl = ttl ? ttl : IP_TTL_DEFAULT; + ip->protocol = proto; + ip->header_checksum = 0; + ip->src_ip = bswap32(src_ip); + ip->dst_ip = bswap32(dst_ip); + ip->header_checksum = checksum16(ip_, hdr_len / 2); + + memcpy(buf, ip, sizeof(*ip)); + memcpy((uint8_t*)buf + hdr_len, data + off, chunk); + + if (need_arp) { + if (!arp_send_or_queue_on(ifx, nh, fpkt)) ok = false; + } else if (!eth_send_frame_on(ifx, ETHERTYPE_IPV4, dst_mac, fpkt)) ok = false; + + off += chunk; } - uint8_t proto = ip.protocol; + netpkt_unref(pkt); + return ok && off == seg_len; +} +static void ipv4_deliver_l4(uint16_t ifindex, netpkt_t* pkt, uint32_t l4_off, uint32_t l4_len, uint8_t proto, uint32_t src, uint32_t dst) { l2_interface_t* l2 = l2_interface_find_by_index((uint8_t)ifindex); if (!l2) return; @@ -333,83 +219,42 @@ void ipv4_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { if (v4->mode == IPV4_CFG_DISABLED) continue; cand[ccount++] = v4; } - if (ccount == 0) return; + if (ccount == 0) return; if (ipv4_is_multicast(dst)) { bool joined = false; - for (int i = 0; i < (int)l2->ipv4_mcast_count; ++i) if (l2->ipv4_mcast[i] == dst) { - joined = true; - break; + for (int i = 0; i < (int)l2->ipv4_mcast_count; ++i) { + if (l2->ipv4_mcast[i] == dst) { + joined = true; + break; + } } if (!joined) return; + for (int i = 0; i < ccount; ++i) { + netpkt_t* l4pkt = netpkt_view(pkt, l4_off, l4_len); + if (!l4pkt) continue; uint8_t l3id = cand[i]->l3_id; - switch (proto) { - netpkt_t* l4pkt; - case PROTO_IGMP: - l4pkt = netpkt_view(pkt, hdr_len, l4_len); - if (l4pkt) igmp_input((uint8_t)ifindex, src, dst, l4pkt); - break; - case PROTO_TCP: - l4pkt = netpkt_view(pkt, hdr_len, l4_len); - if (l4pkt) tcp_input(IP_VER4, &src, &dst, l3id, l4pkt); - break; - case PROTO_UDP: - l4pkt = netpkt_view(pkt, hdr_len, l4_len); - if (l4pkt) udp_input(IP_VER4, &src, &dst, l3id, l4pkt); - break; - default: break; - } + if (proto == PROTO_IGMP) igmp_input((uint8_t)ifindex, src, dst, l4pkt); + else if (proto == PROTO_TCP) tcp_input(IP_VER4, &src, &dst, l3id, l4pkt); + else if (proto == PROTO_UDP) udp_input(IP_VER4, &src, &dst, l3id, l4pkt); + else netpkt_unref(l4pkt); } return; } - - if (dst == 0xFFFFFFFFu) { - if (ccount == 1) { - netpkt_t* l4pkt = netpkt_view(pkt, hdr_len, l4_len); - if (l4pkt) { - switch (proto) { - case PROTO_ICMP: - icmp_input(l4pkt, src, dst); - break; - case PROTO_IGMP: - igmp_input((uint8_t)ifindex, src, dst, l4pkt); - break; - case PROTO_TCP: - tcp_input(IP_VER4, &src, &dst, cand[0]->l3_id, l4pkt); - break; - case PROTO_UDP: - udp_input(IP_VER4, &src, &dst, cand[0]->l3_id, l4pkt); - break; - default: - netpkt_unref(l4pkt); - break; - } - } - return; - } else { - for (int i = 0; i < ccount; ++i) { - uint8_t l3id = cand[i]->l3_id; - switch (proto) { - netpkt_t* l4pkt; - case PROTO_ICMP: - l4pkt = netpkt_view(pkt, hdr_len, l4_len); - if (l4pkt) icmp_input(l4pkt, src, dst); - break; - case PROTO_TCP: - l4pkt = netpkt_view(pkt, hdr_len, l4_len); - if (l4pkt) tcp_input(IP_VER4, &src, &dst, l3id, l4pkt); - break; - case PROTO_UDP: - l4pkt = netpkt_view(pkt, hdr_len, l4_len); - if (l4pkt) udp_input(IP_VER4, &src, &dst, l3id, l4pkt); - break; - default: break; - } - } - return; + if (dst == 0xFFFFFFFF) { + for (int i = 0; i < ccount; ++i) { + netpkt_t* l4pkt = netpkt_view(pkt, l4_off, l4_len); + if (!l4pkt) continue; + uint8_t l3id = cand[i]->l3_id; + if (proto == PROTO_ICMP) icmp_input(l4pkt, src, dst); + else if (proto == PROTO_IGMP) igmp_input((uint8_t)ifindex, src, dst, l4pkt); + else if (proto == PROTO_TCP) tcp_input(IP_VER4, &src, &dst, l3id, l4pkt); + else if (proto == PROTO_UDP) udp_input(IP_VER4, &src, &dst, l3id, l4pkt); + else netpkt_unref(l4pkt); } + return; } int match_count = 0; @@ -417,54 +262,33 @@ void ipv4_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { 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) match_l3id = cand[i]->l3_id; } } + if (match_count == 1) { - netpkt_t* l4pkt = netpkt_view(pkt, hdr_len, l4_len); - if (l4pkt) { - - switch (proto) { - case PROTO_ICMP: - icmp_input(l4pkt, src, dst); - break; - case PROTO_IGMP: - igmp_input((uint8_t)ifindex, src, dst, l4pkt); - break; - case PROTO_TCP: - tcp_input(IP_VER4, &src, &dst, match_l3id, l4pkt); - break; - case PROTO_UDP: - udp_input(IP_VER4, &src, &dst, match_l3id, l4pkt); - break; - default: - netpkt_unref(l4pkt); - break; - } - } + netpkt_t* l4pkt = netpkt_view(pkt, l4_off, l4_len); + if (!l4pkt) return; + if (proto == PROTO_ICMP) icmp_input(l4pkt, src, dst); + else if (proto == PROTO_IGMP) igmp_input((uint8_t)ifindex, src, dst, l4pkt); + else if (proto == PROTO_TCP) tcp_input(IP_VER4, &src, &dst, match_l3id, l4pkt); + else if (proto == PROTO_UDP) udp_input(IP_VER4, &src, &dst, match_l3id, l4pkt); + else netpkt_unref(l4pkt); 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) { - netpkt_t* l4pkt; - case PROTO_ICMP: - l4pkt = netpkt_view(pkt, hdr_len, l4_len); - if (l4pkt) icmp_input(l4pkt, src, dst); - break; - case PROTO_TCP: - l4pkt = netpkt_view(pkt, hdr_len, l4_len); - if (l4pkt) tcp_input(IP_VER4, &src, &dst, l3id, l4pkt); - break; - case PROTO_UDP: - l4pkt = netpkt_view(pkt, hdr_len, l4_len); - if (l4pkt) udp_input(IP_VER4, &src, &dst, l3id, l4pkt); - break; - default: break; - } - } + if (cand[i]->ip != dst) continue; + + netpkt_t* l4pkt = netpkt_view(pkt, l4_off, l4_len); + if (!l4pkt) continue; + uint8_t l3id = cand[i]->l3_id; + if (proto == PROTO_ICMP) icmp_input(l4pkt, src, dst); + else if (proto == PROTO_IGMP) igmp_input((uint8_t)ifindex, src, dst, l4pkt); + else if (proto == PROTO_TCP) tcp_input(IP_VER4, &src, &dst, l3id, l4pkt); + else if (proto == PROTO_UDP) udp_input(IP_VER4, &src, &dst, l3id, l4pkt); + else netpkt_unref(l4pkt); } return; } @@ -474,26 +298,117 @@ void ipv4_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { if (!v4 || !v4->ip || !v4->mask) continue; if (v4->is_localhost) continue; if (ipv4_broadcast_calc(v4->ip, v4->mask) != dst) continue; - uint8_t l3id = v4->l3_id; - switch (proto) { - netpkt_t* l4pkt; - case PROTO_ICMP: - l4pkt = netpkt_view(pkt, hdr_len, l4_len); - if (l4pkt) icmp_input(l4pkt, src, dst); - break; - case PROTO_TCP: - l4pkt = netpkt_view(pkt, hdr_len, l4_len); - if (l4pkt) tcp_input(IP_VER4, &src, &dst, l3id, l4pkt); - break; - case PROTO_UDP: - l4pkt = netpkt_view(pkt, hdr_len, l4_len); - if (l4pkt) udp_input(IP_VER4, &src, &dst, l3id, l4pkt); - break; - default: + + netpkt_t* l4pkt = netpkt_view(pkt, l4_off, l4_len); + if (!l4pkt) return; + if (proto == PROTO_ICMP) icmp_input(l4pkt, src, dst); + else if (proto == PROTO_TCP) tcp_input(IP_VER4, &src, &dst, v4->l3_id, l4pkt); + else if (proto == PROTO_UDP) udp_input(IP_VER4, &src, &dst, v4->l3_id, l4pkt); + else netpkt_unref(l4pkt); + return; + } +} + +void ipv4_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { + if (!pkt) return; + uint32_t ip_len = netpkt_len(pkt); + if (ip_len < sizeof(ipv4_hdr_t)) return; + + uint8_t first = 0; + if (!netpkt_copyout(pkt, 0, &first, sizeof(first))) return; + uint8_t ver = (uint8_t)(first >> 4); + uint8_t ihl = (uint8_t)(first & 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 hdr_words[60 /sizeof(uint16_t)]; + uint8_t* hdr_copy = (uint8_t*)hdr_words; + if (!netpkt_copyout(pkt, 0, hdr_copy, hdr_len)) return; + if (checksum16(hdr_words, hdr_len / 2) != 0) return; + ipv4_hdr_t ip; + memcpy(&ip, hdr_copy, sizeof(ip)); + + uint16_t ip_totlen = bswap16(ip.total_length); + if (ip_totlen < hdr_len) return; + if (ip_len < ip_totlen) return; + (void)netpkt_trim(pkt, ip_totlen); + + uint32_t src = bswap32(ip.src_ip); + uint32_t dst = bswap32(ip.dst_ip); + + if (ifindex && src && src_mac) { + 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 arp_table_put_for_l2((uint8_t)ifindex, src, mac_old, 180000, false); + } + + uint8_t proto = ip.protocol; + uint32_t l4_len = (uint32_t)ip_totlen - hdr_len; + uint16_t ff = bswap16(ip.flags_frag_offset); + uint32_t off = (uint32_t)(ff & 0x1FFF) * 8; + uint8_t more = (ff & 0x2000) ? 1 : 0; + + if (off || more) { + if (!l4_len) return; + if (more && (l4_len & 7)) return; + if (off + l4_len > NET_FRAGBUF_DEFAULT_MAX_LEN) return; + + uint32_t now = (uint32_t)get_time(); + ipv4_reass_slot_t* slot = NULL; + uint16_t ident = bswap16(ip.identification); + + for (int i = 0; i < IPV4_REASS_SLOTS; i++) { + ipv4_reass_slot_t* s = &g_ipv4_reass[i]; + if (!s->used) continue; + if (now - s->last_update_ms > 60000) { + ipv4_reass_free(s); + continue; + } + if (s->ifindex == (uint8_t)ifindex && s->ident == ident && s->proto == proto && s->src == src && s->dst == dst) slot = s; + } + + if (!slot) { + for (int i = 0; i < IPV4_REASS_SLOTS; i++) { + if (g_ipv4_reass[i].used) continue; + slot = &g_ipv4_reass[i]; + memset(slot, 0, sizeof(*slot)); + slot->used = 1; + net_fragbuf_init(&slot->frag); + slot->ifindex = (uint8_t)ifindex; + slot->ident = ident; + slot->proto = proto; + slot->src = src; + slot->dst = dst; + slot->last_update_ms = now; break; + } + } + + if (!slot) return; + if (!net_fragbuf_add(&slot->frag, pkt, hdr_len, off, l4_len, more)) { + ipv4_reass_free(slot); + return; } + + slot->last_update_ms = now; + + if (!net_fragbuf_complete(&slot->frag)) return; + + netpkt_t* reassembled = net_fragbuf_take_packet(&slot->frag); + if (!reassembled) { + ipv4_reass_free(slot); + return; + } + + ipv4_reass_free(slot); + ipv4_deliver_l4(ifindex, reassembled, 0, netpkt_len(reassembled), proto, src, dst); + netpkt_unref(reassembled); return; } - return; + ipv4_deliver_l4(ifindex, pkt, hdr_len, l4_len, proto, src, dst); } diff --git a/kernel/networking/internet_layer/ipv4.h b/kernel/networking/internet_layer/ipv4.h index ab0fa01e..62568fcd 100644 --- a/kernel/networking/internet_layer/ipv4.h +++ b/kernel/networking/internet_layer/ipv4.h @@ -28,10 +28,8 @@ typedef struct __attribute__((packed)) ipv4_hdr_t { uint32_t dst_ip; } ipv4_hdr_t; -typedef ip_tx_scope_t ipv4_tx_scope_t; -typedef ip_tx_opts_t ipv4_tx_opts_t; -bool ipv4_send_packet(uint32_t dst_ip, uint8_t proto, netpkt_t* pkt, const ipv4_tx_opts_t* opts, uint8_t ttl, uint8_t dontfrag); +bool ipv4_send_packet(uint32_t dst_ip, uint8_t proto, netpkt_t* pkt, const ip_tx_opts_t* opts, uint8_t ttl, uint8_t dontfrag); void ipv4_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]); #ifdef __cplusplus diff --git a/kernel/networking/internet_layer/ipv4_route.c b/kernel/networking/internet_layer/ipv4_route.c index 4f862564..cc3e7158 100644 --- a/kernel/networking/internet_layer/ipv4_route.c +++ b/kernel/networking/internet_layer/ipv4_route.c @@ -56,8 +56,6 @@ bool ipv4_build_tx_plan(uint32_t dst, const ip_tx_opts_t* hint, ipv4_tx_plan_t* out->l3_id = 0; out->src_ip = 0; out->net_epoch = ipv4_route_epoch(); - out->fixed_opts.scope = IP_TX_AUTO; - out->fixed_opts.index = 0; if (hint && hint->scope == IP_TX_BOUND_L3) { uint8_t id = hint->index; @@ -65,8 +63,6 @@ bool ipv4_build_tx_plan(uint32_t dst, const ip_tx_opts_t* hint, ipv4_tx_plan_t* if (!v4_l3_ok_for_tx(v4) || ipv4_is_loopback(dst) != v4->is_localhost) return false; out->l3_id = id; out->src_ip = v4->ip; - out->fixed_opts.scope = IP_TX_BOUND_L3; - out->fixed_opts.index = id; return true; } @@ -104,8 +100,6 @@ bool ipv4_build_tx_plan(uint32_t dst, const ip_tx_opts_t* hint, ipv4_tx_plan_t* out->l3_id = chosen; out->src_ip = v4->ip; - out->fixed_opts.scope = IP_TX_BOUND_L3; - out->fixed_opts.index = chosen; return true; } diff --git a/kernel/networking/internet_layer/ipv4_route.h b/kernel/networking/internet_layer/ipv4_route.h index b8583719..507f5481 100644 --- a/kernel/networking/internet_layer/ipv4_route.h +++ b/kernel/networking/internet_layer/ipv4_route.h @@ -33,7 +33,6 @@ typedef struct { uint8_t l3_id; uint32_t src_ip; uint32_t net_epoch; - ip_tx_opts_t fixed_opts; } ipv4_tx_plan_t; bool ipv4_tx_plan_valid(const ipv4_tx_plan_t* plan); diff --git a/kernel/networking/internet_layer/ipv6.c b/kernel/networking/internet_layer/ipv6.c index 1ef6738b..4d8f8694 100644 --- a/kernel/networking/internet_layer/ipv6.c +++ b/kernel/networking/internet_layer/ipv6.c @@ -15,14 +15,11 @@ #include "math/rng.h" #include "net/checksums.h" #include "networking/link_layer/nic_types.h" +#include "networking/net_fragbuf.h" #define IPV6_MIN_MTU 1280u #define PMTU_CACHE_SIZE 16 #define REASS_SLOTS 8 -#define REASS_STEP 2048u -#define REASS_MAX_LEN 65535 -#define REASS_BITMAP_BYTES ((REASS_MAX_LEN + 7u) / 8u) - typedef struct { uint8_t used; uint8_t dst[16]; @@ -41,8 +38,6 @@ typedef struct { uint32_t first_rx_ms; uint32_t last_update_ms; - uint32_t total_len; - uint8_t have_last; uint8_t have_first; uint8_t first_src_mac[6]; uint8_t _pad0[1]; @@ -50,10 +45,7 @@ typedef struct { uint16_t first_pkt_len; uint8_t _pad1[2]; uint8_t first_pkt[1280]; - - uint8_t *buf; - uint32_t buf_cap; - uint8_t bitmap[REASS_BITMAP_BYTES]; + net_fragbuf_t frag; } reass_slot_t; typedef struct __attribute__((packed)) { @@ -113,15 +105,10 @@ void ipv6_pmtu_note(const uint8_t dst[16], uint16_t mtu) { static void reass_free(reass_slot_t *s) { if (!s) return; - if (s->buf) release(s->buf); + net_fragbuf_free(&s->frag); memset(s, 0, sizeof(*s)); } - -static void free_release(void* ctx, uintptr_t base, uint32_t alloc_size) { - if (base) release((void*)base); -} - static void icmpv6_send_error(uint8_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[16], const uint8_t dst_mac[6], uint8_t type, uint8_t code, uint32_t param32, const uint8_t *invoking, uint32_t invoking_len) { if (!ifindex || !src_ip || !dst_ip || !dst_mac || !invoking || !invoking_len) return; @@ -152,272 +139,73 @@ static void icmpv6_send_error(uint8_t ifindex, const uint8_t src_ip[16], const u release(buf); } -static l3_ipv6_interface_t* best_v6_on_l2_for_dst(l2_interface_t* l2, const uint8_t dst[16]) { - l3_ipv6_interface_t* best = NULL; - int best_cmp = -1; - int best_cost = 0x7FFFFFFF; - int dst_is_ll = (ipv6_is_linklocal(dst) || ipv6_is_linkscope_mcast(dst)) ? 1 : 0; - - for (int s = 0; s < MAX_IPV6_PER_INTERFACE; s++) { - l3_ipv6_interface_t* v6 = l2->l3_v6[s]; - if (!v6) continue; - if (v6->cfg == IPV6_CFG_DISABLE) continue; - if (ipv6_is_unspecified(v6->ip)) continue; - if (v6->dad_state != IPV6_DAD_OK) continue; - - int v6_is_ll = ipv6_is_linklocal(v6->ip) ? 1 : 0; - if (v6_is_ll != dst_is_ll) continue; - - int cmp = ipv6_common_prefix_len(dst, v6->ip); - int cost = (int)l2->base_metric; - if (cmp > best_cmp || (cmp == best_cmp && cost < best_cost)) { - best_cmp = cmp; - best_cost = cost; - best = v6; - } - } - return best; -} - -static bool pick_route_bound_l3(uint8_t l3_id, const uint8_t dst[16], uint8_t* out_ifx, uint8_t out_src[16], uint8_t out_nh[16]) { - l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(l3_id); - if (!v6 || !v6->l2) return false; - if (v6->cfg == IPV6_CFG_DISABLE) return false; - if (ipv6_is_unspecified(v6->ip)) return false; - if (v6->dad_state != IPV6_DAD_OK) return false; - - int dst_is_ll = (ipv6_is_linklocal(dst) || ipv6_is_linkscope_mcast(dst)) ? 1 : 0; - int src_is_ll = ipv6_is_linklocal(v6->ip) ? 1 : 0; - if (dst_is_ll != src_is_ll) return false; - - if (out_ifx) *out_ifx = v6->l2->ifindex; - if (out_src) ipv6_cpy(out_src, v6->ip); - - uint8_t nh[16]; - ipv6_cpy(nh, dst); - - if (!dst_is_ll && v6->prefix_len && ipv6_common_prefix_len(dst, v6->ip) < v6->prefix_len) { - uint8_t via[16] = {0}; - int pl = -1,met = 0x7FFF; - - if (v6->routing_table && - ipv6_rt_lookup_in((const ipv6_rt_table_t*)v6->routing_table, dst, via, &pl, &met)) - { - if (!ipv6_is_unspecified(via)) ipv6_cpy(nh, via); - } else if (!ipv6_is_unspecified(v6->gateway) && ipv6_is_linklocal(v6->gateway)) { - ipv6_cpy(nh, v6->gateway); - } - } - - if (out_nh) ipv6_cpy(out_nh, nh); - return true; -} - -static bool pick_route_bound_l2(uint8_t ifindex, const uint8_t dst[16], uint8_t* out_ifx, uint8_t out_src[16], uint8_t out_nh[16]) { - l2_interface_t* l2 = l2_interface_find_by_index(ifindex); - if (!l2) return false; - - l3_ipv6_interface_t* v6 = best_v6_on_l2_for_dst(l2, dst); - if (!v6) return false; - - if (out_ifx) *out_ifx = l2->ifindex; - if (out_src) ipv6_cpy(out_src, v6->ip); - - uint8_t nh[16]; - ipv6_cpy(nh, dst); - - if (!ipv6_is_linklocal(dst) && v6->prefix_len && ipv6_common_prefix_len(dst, v6->ip) < v6->prefix_len) { - uint8_t via[16] = {0}; - int pl = -1; - int met =0x7FFF; - - if (v6->routing_table && ipv6_rt_lookup_in((const ipv6_rt_table_t*)v6->routing_table, dst, via, &pl, &met)) { - if (!ipv6_is_unspecified(via)) ipv6_cpy(nh, via); - } else if (!ipv6_is_unspecified(v6->gateway) && ipv6_is_linklocal(v6->gateway))ipv6_cpy(nh, v6->gateway); - } - - if (out_nh) ipv6_cpy(out_nh, nh); - return true; -} - -static bool pick_route_global(const uint8_t dst[16], uint8_t* out_ifx, uint8_t out_src[16], uint8_t out_nh[16]) { - int dst_is_ll = ipv6_is_linklocal(dst) ? 1 : 0; - - ip_resolution_result_t r = resolve_ipv6_to_interface(dst); - if (r.found && r.ipv6 && r.l2) { - if (r.ipv6->cfg != IPV6_CFG_DISABLE && - !ipv6_is_unspecified(r.ipv6->ip) && - r.ipv6->dad_state == IPV6_DAD_OK) - { - int src_is_ll = ipv6_is_linklocal(r.ipv6->ip) ? 1 : 0; - if (src_is_ll == dst_is_ll) { - if (out_ifx) *out_ifx = r.l2->ifindex; - if (out_src) ipv6_cpy(out_src, r.ipv6->ip); - - uint8_t nh[16]; - ipv6_cpy(nh, dst); - - if (!dst_is_ll && r.ipv6->prefix_len && ipv6_common_prefix_len(dst, r.ipv6->ip) < r.ipv6->prefix_len) { - uint8_t via[16] = {0}; - int pl = -1; - int met = 0x7FFF; - - if (r.ipv6->routing_table && ipv6_rt_lookup_in((const ipv6_rt_table_t*)r.ipv6->routing_table, dst, via, &pl, &met)) { - if (!ipv6_is_unspecified(via)) ipv6_cpy(nh, via); - } else if (!ipv6_is_unspecified(r.ipv6->gateway) && ipv6_is_linklocal(r.ipv6->gateway)) { - ipv6_cpy(nh, r.ipv6->gateway); - } - } - - if (out_nh) ipv6_cpy(out_nh, nh); - return true; - } - } - } - - uint8_t best_ifx = 0; - uint8_t best_src[16] ={0}; - uint8_t best_nh[16] ={0}; - int best_pl = -1; - int best_cost = 0x7FFFFFFF; - - 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_IPV6_PER_INTERFACE; s++) { - l3_ipv6_interface_t* v6 = l2->l3_v6[s]; - if (!v6) continue; - if (v6->cfg == IPV6_CFG_DISABLE) continue; - if (ipv6_is_unspecified(v6->ip)) continue; - if (v6->dad_state != IPV6_DAD_OK) continue; - - int src_is_ll = ipv6_is_linklocal(v6->ip) ? 1 : 0; - if (src_is_ll != dst_is_ll) continue; - - int pl_conn = -1; - if (!dst_is_ll && v6->prefix_len && ipv6_common_prefix_len(dst, v6->ip) >= v6->prefix_len) pl_conn = v6->prefix_len; - if (dst_is_ll) pl_conn = 128; - - int pl_tab = -1; - int met_tab = 0x7FFF; - uint8_t via[16] = {0}; - - if (!dst_is_ll && v6->routing_table) { - int out_pl = -1; - int out_met = 0x7FFF; - if(ipv6_rt_lookup_in((const ipv6_rt_table_t*)v6->routing_table, dst, via, &out_pl, &out_met)) { - pl_tab = out_pl; - met_tab = out_met; - } - } - - int cand_pl = pl_conn; - int cand_cost = (int)l2->base_metric; - uint8_t cand_nh[16]; - ipv6_cpy(cand_nh, dst); - - if (pl_tab > cand_pl || (pl_tab == cand_pl && ((int)l2->base_metric + met_tab) < cand_cost)) { - cand_pl =pl_tab; - cand_cost = (int)l2->base_metric + met_tab; - if (!ipv6_is_unspecified(via)) ipv6_cpy(cand_nh, via); - } else if (cand_pl < 0) { - if (!ipv6_is_unspecified(v6->gateway) && ipv6_is_linklocal(v6->gateway)) ipv6_cpy(cand_nh, v6->gateway); - } - - if (cand_pl > best_pl || (cand_pl == best_pl && cand_cost < best_cost)) { - best_pl = cand_pl; - best_cost = cand_cost; - best_ifx = l2->ifindex; - ipv6_cpy(best_src, v6->ip); - ipv6_cpy(best_nh, cand_nh); - } - } +bool ipv6_send_packet(const uint8_t dst[16], uint8_t next_header, netpkt_t* pkt, const ip_tx_opts_t* opts, uint8_t hop_limit, uint8_t dontfrag) { + if (!dst || !pkt || !netpkt_len(pkt)) { + if (pkt) netpkt_unref(pkt); + return false; } - if (best_pl < 0) return false; - if (out_ifx) *out_ifx = best_ifx; - if (out_src) ipv6_cpy(out_src, best_src); - if (out_nh) ipv6_cpy(out_nh, best_nh); - return true; -} - -static bool pick_route(const uint8_t dst[16], const ipv6_tx_opts_t* opts, uint8_t* out_ifx, uint8_t out_src[16], uint8_t out_nh[16]) { - if (opts) { - if (opts->scope == IP_TX_BOUND_L3) return pick_route_bound_l3(opts->index, dst, out_ifx, out_src, out_nh); - if (opts->scope == IP_TX_BOUND_L2) return pick_route_bound_l2(opts->index, dst, out_ifx, out_src, out_nh); + ipv6_tx_plan_t plan; + if (!ipv6_build_tx_plan(dst, opts, &plan)) { + netpkt_unref(pkt); + return false; } - return pick_route_global(dst, out_ifx, out_src, out_nh); -} -bool ipv6_send_packet(const uint8_t dst[16], uint8_t next_header, netpkt_t* pkt, const ipv6_tx_opts_t* opts, uint8_t hop_limit, uint8_t dontfrag) { - if (!dst || !pkt || !netpkt_len(pkt)) { - if (pkt) netpkt_unref(pkt); + l3_ipv6_interface_t* src_v6 = l3_ipv6_find_by_id(plan.l3_id); + if (!src_v6 || !src_v6->l2 || !src_v6->l2->is_up || src_v6->cfg == IPV6_CFG_DISABLE|| src_v6->dad_state != IPV6_DAD_OK) { + netpkt_unref(pkt); return false; } - uint8_t ifx = 0; - uint8_t src[16] = {0}; - uint8_t nh[16] = {0}; - - l3_ipv6_interface_t* src_v6 = NULL; + uint8_t ifx = src_v6->l2->ifindex; + uint8_t src[16]; + uint8_t nh[16]; + ipv6_cpy(src, plan.src_ip); + ipv6_cpy(nh, dst); - if (!pick_route(dst, opts, &ifx, src, nh)) { + if (ipv6_is_linklocal(src) && !ipv6_is_linklocal(dst) && !ipv6_is_multicast(dst)) { netpkt_unref(pkt); return false; } - if (!ipv6_is_unspecified(src)) { - l2_interface_t* l2 = l2_interface_find_by_index(ifx); - if (!l2) { - netpkt_unref(pkt); - return false; - } + if (!ipv6_is_linklocal(dst) && !ipv6_is_multicast(dst)) { + uint8_t via[16] = {0}; + int route_pl = -1; + bool have_nh = false; - int ok = 0; - for (int i = 0; i < MAX_IPV6_PER_INTERFACE; i++) { - l3_ipv6_interface_t* v6 = l2->l3_v6[i]; - if (!v6) continue; - if (ipv6_cmp(v6->ip, src) != 0) continue; - if (v6->cfg == IPV6_CFG_DISABLE) { - netpkt_unref(pkt); - return false; - } - if (v6->dad_state != IPV6_DAD_OK) { + if (src_v6->routing_table && ipv6_rt_lookup_in((const ipv6_rt_table_t*)src_v6->routing_table, dst, via, &route_pl, NULL)) { + if (!ipv6_is_unspecified(via)) ipv6_cpy(nh, via); + else if (src_v6->prefix_len && ipv6_common_prefix_len(dst, src_v6->ip) >= src_v6->prefix_len) ipv6_cpy(nh, dst); + else if (route_pl > 0) ipv6_cpy(nh, dst); + else if (!ipv6_is_unspecified(src_v6->gateway) && ipv6_is_linklocal(src_v6->gateway)) ipv6_cpy(nh, src_v6->gateway); + else { netpkt_unref(pkt); return false; } - ok = 1; - src_v6 = v6; - break; + have_nh = true; } - if (!ok) { + + if (!have_nh && src_v6->prefix_len && ipv6_common_prefix_len(dst, src_v6->ip) >= src_v6->prefix_len) have_nh = true; + if (!have_nh && !ipv6_is_unspecified(src_v6->gateway) && ipv6_is_linklocal(src_v6->gateway)) { + ipv6_cpy(nh, src_v6->gateway); + have_nh = true; + } + if (!have_nh) { netpkt_unref(pkt); return false; } } - if (ipv6_is_linklocal(src) && !ipv6_is_linklocal(dst) && !ipv6_is_multicast(dst)) { - netpkt_unref(pkt); - return false; - } - + l2_interface_t* l2 = src_v6->l2; uint8_t dst_mac[6]; bool need_ndp = false; - l2_interface_t* l2 = l2_interface_find_by_index(ifx); if (ipv6_is_multicast(dst)) ipv6_multicast_mac(dst, dst_mac); else if (l2 && l2->kind == NET_IFK_LOCALHOST) memset(dst_mac, 0, 6); else need_ndp = true; - uint16_t mtu = 1500; - - if (!src_v6 && opts && opts->scope == IP_TX_BOUND_L3) src_v6 = l3_ipv6_find_by_id(opts->index); - if (src_v6 && src_v6->mtu) mtu = src_v6->mtu; - + uint16_t mtu = src_v6->mtu ? src_v6->mtu : 1500; uint16_t pmtu = ipv6_pmtu_get(dst); - if (pmtu && pmtu max_chunk)? max_chunk : remain; - uint8_t more = (off + chunk < data_len) ? 1u : 0u; + uint8_t more = off + chunk < seg_len ? 1u : 0; uint32_t payload_len = frag_hdr_len + chunk; uint32_t frame_len = hdr_len + payload_len; @@ -519,7 +301,7 @@ bool ipv6_send_packet(const uint8_t dst[16], uint8_t next_header, netpkt_t* pkt, } netpkt_unref(pkt); - return ok && off == data_len; + return ok && off == seg_len; } static bool ipv6_skip_ext_headers(const netpkt_t* pkt, uint8_t* nh, uint32_t* l4_off, uint32_t* l4_len) { @@ -669,7 +451,7 @@ void ipv6_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { return; } - if (off + frag_len > REASS_MAX_LEN) return; + if (off + frag_len > NET_FRAGBUF_DEFAULT_MAX_LEN) return; reass_slot_t* s = NULL; uint32_t now = (uint32_t)get_time(); @@ -695,9 +477,6 @@ void ipv6_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { reass_slot_t *t = &g_reass[i]; if (t->used) continue; - t->buf = 0; - t->buf_cap = 0; - t->used = 1; t->ifindex = (uint8_t)ifindex; t->ident = ident; @@ -706,33 +485,17 @@ void ipv6_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { t->next_header = inner_nh; t->first_rx_ms = now; t->last_update_ms = now; - t->total_len = 0; - t->have_last = 0; t->have_first = 0; + net_fragbuf_init(&t->frag); memset(t->first_src_mac, 0, 6); t->first_pkt_len = 0; memset(t->first_pkt, 0, sizeof(t->first_pkt)); - memset(t->bitmap, 0, sizeof(t->bitmap)); s = t; break; } } if (!s) return; - int overlap = 0; - uint32_t start = off / 8u; - uint32_t end = (off + frag_len + 7u) / 8u; - if (end > sizeof(s->bitmap)) end = sizeof(s->bitmap); - for (uint32_t i = start; i < end; i++) { - if (s->bitmap[i]) { - overlap = 1; - break; - } - } - if (overlap) { - reass_free(s); - return; - } int has_ulh = 0; if (off == 0) { @@ -796,67 +559,22 @@ void ipv6_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { s->have_first = 1; } - if (off + frag_len > REASS_MAX_LEN) { + if (!net_fragbuf_add(&s->frag, pkt, frag_off, off, frag_len, more)) { reass_free(s); return; } - if (s->buf_cap < off + frag_len) { - uint32_t new_cap = (off + frag_len + (REASS_STEP - 1u)) & ~(REASS_STEP - 1u); - if (new_cap < REASS_STEP) new_cap = REASS_STEP; - if (new_cap > REASS_MAX_LEN) new_cap = REASS_MAX_LEN; - - uint8_t* new_buf = (uint8_t*)zalloc(new_cap); - if (!new_buf) { - reass_free(s); - return; - } - - if (s->buf && s->buf_cap) memcpy(new_buf, s->buf, s->buf_cap); - if (s->buf) release(s->buf); - - s->buf = new_buf; - s->buf_cap = new_cap; - } - if (!netpkt_copyout(pkt, frag_off, s->buf + off, frag_len)) { - reass_free(s); - return; - } - - start = off / 8u; - end = (off + frag_len + 7u) / 8u; - if (end > sizeof(s->bitmap)) end = sizeof(s->bitmap); - for (uint32_t i = start; i < end; i++) s->bitmap[i] = 1; - s->last_update_ms = now; - if (!more) { - s->have_last = 1; - s->total_len = off + frag_len; - } - - int complete = 0; - if (s->have_last) { - uint32_t needed = (s->total_len + 7u) / 8u; - if (needed <= sizeof(s->bitmap)) { - complete = 1; - for (uint32_t i = 0; i < needed; i++) if (!s->bitmap[i]) { - complete = 0; - break; - } - } - } - if (!complete) return; - + if (!net_fragbuf_complete(&s->frag)) return; uint32_t payload_off = 0; - uint32_t payload_size = s->total_len; + uint32_t payload_size = s->frag.total_len; - netpkt_t* reassembled = netpkt_wrap((uintptr_t)s->buf, s->total_len, 0, s->total_len, free_release, 0); + netpkt_t* reassembled = net_fragbuf_take_packet(&s->frag); if (!reassembled) { reass_free(s); return; } - s->buf = 0; if (!ipv6_skip_ext_headers(reassembled, &inner_nh, &payload_off, &payload_size)) { netpkt_unref(reassembled); diff --git a/kernel/networking/internet_layer/ipv6.h b/kernel/networking/internet_layer/ipv6.h index e716893d..d0901ba9 100644 --- a/kernel/networking/internet_layer/ipv6.h +++ b/kernel/networking/internet_layer/ipv6.h @@ -17,10 +17,8 @@ typedef struct __attribute__((packed)) { uint8_t dst[16]; } ipv6_hdr_t; -typedef ip_tx_scope_t ipv6_tx_scope_t; -typedef ip_tx_opts_t ipv6_tx_opts_t; -bool ipv6_send_packet(const uint8_t dst[16], uint8_t next_header, netpkt_t* pkt, const ipv6_tx_opts_t* opts, uint8_t hop_limit, uint8_t dontfrag); +bool ipv6_send_packet(const uint8_t dst[16], uint8_t next_header, netpkt_t* pkt, const ip_tx_opts_t* opts, uint8_t hop_limit, uint8_t dontfrag); void ipv6_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]); uint16_t ipv6_pmtu_get(const uint8_t dst[16]); diff --git a/kernel/networking/internet_layer/ipv6_route.c b/kernel/networking/internet_layer/ipv6_route.c index 5ad8faaa..7bd8167d 100644 --- a/kernel/networking/internet_layer/ipv6_route.c +++ b/kernel/networking/internet_layer/ipv6_route.c @@ -4,7 +4,7 @@ #include "networking/internet_layer/ipv6_utils.h" #include "networking/interface_manager.h" #include "syscalls/syscalls.h" -//TODO simplify plans by getting IP_TX_BOUND_L3 from l3_id instead of fixed_opts, both ipv4 and ipv6 + struct ipv6_rt_table { ipv6_rt_entry_t e[IPV6_RT_PER_IF_MAX]; int len; @@ -66,8 +66,6 @@ bool ipv6_build_tx_plan(const uint8_t dst[16], const ip_tx_opts_t* hint, ipv6_tx memset(out, 0, sizeof(*out)); out->net_epoch = ipv6_route_epoch(); - out->fixed_opts.scope = IP_TX_AUTO; - out->fixed_opts.index = 0; int dst_is_ll = (ipv6_is_linklocal(dst) || ipv6_is_linkscope_mcast(dst)) ? 1 : 0; int dst_is_loop = ipv6_is_loopback(dst) ? 1 : 0; @@ -78,8 +76,6 @@ bool ipv6_build_tx_plan(const uint8_t dst[16], const ip_tx_opts_t* hint, ipv6_tx if (!v6_l3_ok_for_tx(v6, dst_is_ll, dst_is_loop)) return false; out->l3_id = id; memcpy(out->src_ip, v6->ip, 16); - out->fixed_opts.scope = IP_TX_BOUND_L3; - out->fixed_opts.index = id; return true; } @@ -117,8 +113,6 @@ bool ipv6_build_tx_plan(const uint8_t dst[16], const ip_tx_opts_t* hint, ipv6_tx out->l3_id = chosen; memcpy(out->src_ip, v6->ip, 16); - out->fixed_opts.scope = IP_TX_BOUND_L3; - out->fixed_opts.index = chosen; return true; } diff --git a/kernel/networking/internet_layer/ipv6_route.h b/kernel/networking/internet_layer/ipv6_route.h index 1d4875b3..62fd5c41 100644 --- a/kernel/networking/internet_layer/ipv6_route.h +++ b/kernel/networking/internet_layer/ipv6_route.h @@ -34,7 +34,6 @@ typedef struct { uint8_t l3_id; uint8_t src_ip[16]; uint32_t net_epoch; - ip_tx_opts_t fixed_opts; } ipv6_tx_plan_t; bool ipv6_tx_plan_valid(const ipv6_tx_plan_t* plan); diff --git a/kernel/networking/net_fragbuf.c b/kernel/networking/net_fragbuf.c new file mode 100644 index 00000000..544e122c --- /dev/null +++ b/kernel/networking/net_fragbuf.c @@ -0,0 +1,76 @@ +#include "networking/net_fragbuf.h" +#include "std/memory.h" +#include "std/string.h" + +void net_fragbuf_init(net_fragbuf_t *fb) { + if (!fb) return; + *fb = (net_fragbuf_t){0}; + fb->max_len = NET_FRAGBUF_DEFAULT_MAX_LEN; + fb->step = NET_FRAGBUF_DEFAULT_STEP; +} + +void net_fragbuf_free(net_fragbuf_t *fb) { + if (!fb) return; + if (fb->data) release(fb->data); + if (fb->blocks) release(fb->blocks); + *fb = (net_fragbuf_t){0}; +} + +bool net_fragbuf_add(net_fragbuf_t *fb, netpkt_t *pkt, uint32_t pkt_off, uint32_t frag_off, uint32_t frag_len, uint8_t more) { + if (!fb) return false; + if (frag_len && !pkt) return false; + if (!fb->max_len) fb->max_len = NET_FRAGBUF_DEFAULT_MAX_LEN; + if (!fb->step) fb->step = NET_FRAGBUF_DEFAULT_STEP; + if (frag_off > fb->max_len || frag_len > fb->max_len - frag_off) return false; + if (more && (frag_len & 7u)) return false; + + uint32_t need_blocks = (frag_off + frag_len + 7u) / 8u; + if (!fb->blocks) { + fb->block_count = (fb->max_len + 7u) / 8u; + fb->blocks = (uint8_t*)zalloc(fb->block_count ? fb->block_count : 1u); + if (!fb->blocks) return false; + } + if (need_blocks > fb->block_count) return false; + + uint32_t start = frag_off / 8u; + for (uint32_t i = start; i < need_blocks; i++) if (fb->blocks[i]) return false; + + if (frag_len && fb->cap < frag_off + frag_len) { + uint32_t new_cap = ((frag_off + frag_len + fb->step - 1u) / fb->step) * fb->step; + if (new_cap < fb->step) new_cap = fb->step; + if (new_cap > fb->max_len) new_cap = fb->max_len; + + uint8_t *new_data = (uint8_t*)zalloc(new_cap); + if (!new_data) return false; + if (fb->data && fb->cap) memcpy(new_data, fb->data, fb->cap); + if (fb->data) release(fb->data); + fb->data = new_data; + fb->cap = new_cap; + } + + if (frag_len && !netpkt_copyout(pkt, pkt_off, fb->data + frag_off, frag_len)) return false; + for (uint32_t i = start; i < need_blocks; i++) fb->blocks[i] = 1; + + if (!more) { + fb->have_last = 1; + fb->total_len = frag_off + frag_len; + } + return true; +} + +bool net_fragbuf_complete(const net_fragbuf_t *fb) { + if (!fb || !fb->have_last || !fb->blocks) return false; + uint32_t need = (fb->total_len + 7u) / 8u; + if (need > fb->block_count) return false; + for (uint32_t i = 0; i < need; i++) if (!fb->blocks[i]) return false; + return true; +} + +netpkt_t* net_fragbuf_take_packet(net_fragbuf_t *fb) { + if (!net_fragbuf_complete(fb)) return NULL; + netpkt_t *pkt = netpkt_wrap((uintptr_t)fb->data, fb->total_len, 0, fb->total_len, NULL, NULL); + if (!pkt) return NULL; + fb->data = NULL; + fb->cap = 0; + return pkt; +} diff --git a/kernel/networking/net_fragbuf.h b/kernel/networking/net_fragbuf.h new file mode 100644 index 00000000..6c3f5469 --- /dev/null +++ b/kernel/networking/net_fragbuf.h @@ -0,0 +1,32 @@ +#pragma once + +#include "types.h" +#include "networking/netpkt.h" + +#define NET_FRAGBUF_DEFAULT_MAX_LEN 65535u +#define NET_FRAGBUF_DEFAULT_STEP 2048u + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + uint8_t *data; + uint8_t *blocks; + uint32_t cap; + uint32_t total_len; + uint32_t max_len; + uint32_t step; + uint32_t block_count; + uint8_t have_last; +} net_fragbuf_t; + +void net_fragbuf_init(net_fragbuf_t *fb); +void net_fragbuf_free(net_fragbuf_t *fb); +bool net_fragbuf_add(net_fragbuf_t *fb, netpkt_t *pkt, uint32_t pkt_off, uint32_t frag_off, uint32_t frag_len, uint8_t more); +bool net_fragbuf_complete(const net_fragbuf_t *fb); +netpkt_t* net_fragbuf_take_packet(net_fragbuf_t *fb); + +#ifdef __cplusplus +} +#endif diff --git a/kernel/networking/netpkt.c b/kernel/networking/netpkt.c index 40d2815a..6f0b21e1 100644 --- a/kernel/networking/netpkt.c +++ b/kernel/networking/netpkt.c @@ -342,7 +342,8 @@ static bool netpkt_realloc_to(netpkt_t* p, uint32_t new_head, uint32_t new_alloc ob->refs--; } else { if (ob->flags & NETPKT_BUF_F_EXTERNAL) { - if (ob->free_fn) ob->free_fn(ob->free_ctx, ob->base, ob->alloc); + if (ob->free_fn) ob->free_fn(ob->free_ctx, ob->base); + else if (ob->base) release((void*)ob->base); } else if (ob->flags & NETPKT_BUF_F_SMALL) { *(void**)ob->base = g_netpkt_small_free; g_netpkt_small_free = (void*)ob->base; @@ -428,10 +429,10 @@ netpkt_t* netpkt_alloc(uint32_t data_capacity, uint32_t headroom, uint32_t tailr from_spare = true; memset(mem, 0, PAGE_SIZE); } else { - if ((uint64_t)cap > (uint64_t)NETPKT_MAX_PAGE_BYTES || g_netpkt_page_bytes > (uint64_t)NETPKT_MAX_PAGE_BYTES - (uint64_t)cap) { - irq_restore(irq); - return 0; - } + if ((uint64_t)cap > (uint64_t)NETPKT_MAX_PAGE_BYTES || g_netpkt_page_bytes > (uint64_t)NETPKT_MAX_PAGE_BYTES - (uint64_t)cap) { + irq_restore(irq); + return 0; + } g_netpkt_page_bytes += (uint64_t)cap; g_netpkt_payload_page_bytes += (uint64_t)cap; @@ -604,7 +605,8 @@ void netpkt_unref(netpkt_t* p) { b->refs--; } else { if (b->flags & NETPKT_BUF_F_EXTERNAL) { - if (b->free_fn) b->free_fn(b->free_ctx, b->base, b->alloc); + if (b->free_fn) b->free_fn(b->free_ctx, b->base); + else if (b->base) release((void*)b->base); } else if (b->flags & NETPKT_BUF_F_SMALL) { *(void**)b->base = g_netpkt_small_free; g_netpkt_small_free = (void*)b->base; @@ -787,10 +789,10 @@ bool netpkt_pull(netpkt_t* p, uint32_t bytes) { p->head += bytes; p->len -= bytes; - if ((p->flags & NETPKT_F_VIEW) || !p->buf || (p->buf->flags & NETPKT_BUF_F_EXTERNAL) || (p->buf->refs != 1) || (p->cap <= PAGE_SIZE)) { - irq_restore(irq); - return true; - } + if ((p->flags & NETPKT_F_VIEW) || !p->buf || (p->buf->flags & NETPKT_BUF_F_EXTERNAL) || (p->buf->refs != 1) || (p->cap <= PAGE_SIZE)) { + irq_restore(irq); + return true; + } uint64_t need = (uint64_t)p->head+(uint64_t)p->len; if (!need) need = 1; diff --git a/kernel/networking/netpkt.h b/kernel/networking/netpkt.h index d9b8bfe6..b6520318 100644 --- a/kernel/networking/netpkt.h +++ b/kernel/networking/netpkt.h @@ -8,7 +8,7 @@ extern "C" { typedef struct netpkt netpkt_t; -typedef void (*netpkt_free_fn)(void* ctx, uintptr_t base, uint32_t alloc_size); +typedef void (*netpkt_free_fn)(void* ctx, uintptr_t base); #define NETPKT_MAX_ALLOC 65536u #define NETPKT_MAX_PAGE_BYTES (32ull * 1024ull * 1024ull) diff --git a/kernel/networking/processes/net_proc.c b/kernel/networking/processes/net_proc.c index 4d6be397..d92f0ff4 100644 --- a/kernel/networking/processes/net_proc.c +++ b/kernel/networking/processes/net_proc.c @@ -93,7 +93,12 @@ static int net_has_ready_address(void) { } static void run_http_server() { - http_server_handle_t srv = http_server_create(NULL, NULL); + uint32_t http_methods = (1 << HTTP_METHOD_GET) | (1 << HTTP_METHOD_HEAD) | (1 << HTTP_METHOD_OPTIONS); + HTTPServerPolicyOptions http_options = {0}; + http_options.flags = HTTP_SERVER_OPT_ALLOWED_METHODS; + http_options.value.allowed_methods = http_methods; + + http_server_handle_t srv = http_server_create(NULL, &http_options); if (!srv) { stop_current_process(1); return; @@ -115,11 +120,11 @@ static void run_http_server() { mdns_register_service("RedactedOS", "http", "tcp", HTTP_PORT, "path=/"); - static const char HTML_ROOT[] = + static char HTML_ROOT[] = "

Hello, world!

\n" "

[Redacted]

"; - static const char HTML_404[] = + static char HTML_404[] = "

404 Regrettably, no such page exists in this realm

\n" "

Im rather inclined to deduce that your page simply does not exist. Given the state of affairs, I dare say it's not altogether surprising, innit?

"; @@ -130,26 +135,48 @@ static void run_http_server() { continue; } HTTPRequestMsg req = http_server_recv_request(srv, conn); + if (!req.path.length) { + http_connection_close(conn); + continue; + } + + if (req.method == HTTP_METHOD_OPTIONS) { + static char allow_key[] = "Allow"; + string allow_value = http_methods_allow_header(http_methods); + HTTPHeader allow_header = { + {allow_key, sizeof(allow_key) - 1, 0}, + allow_value + }; + + HTTPResponseMsg res = (HTTPResponseMsg){0}; + res.status_code = HTTP_OK; + res.headers_common.fields.content_length = 0; + res.headers_common.framing.has_content_length = 1; + res.extra_headers = &allow_header; + res.extra_header_count = 1; + + http_server_send_response(srv, conn, &res); + string_free(allow_value); + http_connection_close(conn); + http_request_free(&req); + continue; + } + int is_root = req.path.length == 1 && req.path.data[0] == '/'; - const char *body = is_root ? HTML_ROOT : HTML_404; + char *body = is_root ? HTML_ROOT : HTML_404; uint32_t len = (uint32_t)strlen(body); HTTPResponseMsg res = (HTTPResponseMsg){0}; res.status_code = is_root ? HTTP_OK : HTTP_NOT_FOUND; - res.reason = string_from_const(is_root ? "OK" : "Not Found"); res.headers_common.fields.content_length = len; res.headers_common.framing.has_content_length = 1; res.headers_common.fields.content_type = string_from_const("text/html"); - res.headers_common.fields.connection = string_from_const("close"); - res.body.ptr = (uintptr_t)body; - res.body.size = len; + res.body = (string){body, len, 0}; http_server_send_response(srv, conn, &res); http_connection_close(conn); - if (req.path.mem_length) string_free(req.path); - http_headers_common_free(&req.headers_common); - http_headers_extra_free(req.extra_headers, req.extra_header_count); - if (req.body.ptr && req.body.size) release((void*)req.body.ptr); + http_request_free(&req); + http_response_free(&res); } } @@ -161,7 +188,7 @@ static void test_http(const net_l4_endpoint* ep) { char ip_str[16]; ipv4_to_string(ip_u32, ip_str); - kprintf("[HTTP] GET %s:%i", ip_str, HTTP_PORT); + print("[HTTP] GET %s:%i", ip_str, HTTP_PORT); } http_client_handle_t cli = http_client_create(NULL, NULL); @@ -191,12 +218,12 @@ static void test_http(const net_l4_endpoint* ep) { HTTPResponseMsg resp = http_client_send_request(cli, &req); if ((int)resp.status_code < 0) print("[HTTP] request FAIL status=%i", (int)resp.status_code); - else if (resp.body.ptr && resp.body.size) { - char *body_str = (char*)zalloc(resp.body.size + 1); + else if (resp.body.data && resp.body.length) { + char *body_str = (char*)zalloc(resp.body.length + 1); if (body_str) { - memcpy(body_str, (void*)resp.body.ptr, resp.body.size); - body_str[resp.body.size] = '\0'; - print("[HTTP] %i %i bytes of body", resp.status_code, resp.body.size); + memcpy(body_str, (void*)resp.body.data, resp.body.length); + body_str[resp.body.length] = '\0'; + print("[HTTP] %i %i bytes of body", resp.status_code, resp.body.length); print("%s", body_str); release(body_str); } @@ -205,11 +232,8 @@ static void test_http(const net_l4_endpoint* ep) { http_client_close(cli); http_client_destroy(cli); - if (resp.body.ptr && resp.body.size) release((void*)resp.body.ptr); - if (resp.reason.mem_length) string_free(resp.reason); - - http_headers_common_free(&resp.headers_common); - http_headers_extra_free(resp.extra_headers, resp.extra_header_count); + http_response_free(&resp); + http_request_free(&req); } static int ntp(int argc, char* argv[]) { diff --git a/kernel/networking/transport_layer/socket_udp.hpp b/kernel/networking/transport_layer/socket_udp.hpp index dcbdba14..829cc2a2 100644 --- a/kernel/networking/transport_layer/socket_udp.hpp +++ b/kernel/networking/transport_layer/socket_udp.hpp @@ -365,7 +365,7 @@ class UDPSocket : public Socket { memcpy(src.ip, &bcast_v4->ip, 4); src.port = localPort; - ipv4_tx_opts_t tx; + ip_tx_opts_t tx; tx.scope = IP_TX_BOUND_L3; tx.index = chosen_l3; @@ -398,9 +398,9 @@ class UDPSocket : public Socket { memcpy(src.ip, &v4->ip, 4); src.port = localPort; - ipv4_tx_opts_t tx; - tx.scope = (ip_tx_scope_t)plan.fixed_opts.scope; - tx.index = plan.fixed_opts.index; + ip_tx_opts_t tx; + tx.scope = IP_TX_BOUND_L3; + tx.index = plan.l3_id; udp_send_segment(&src, &d, pay, &tx, (extraOpts.flags & SOCK_OPT_TTL) ? extraOpts.ttl : 0, (extraOpts.flags & SOCK_OPT_DONTFRAG) ? 1 : 0); return (int64_t)len; @@ -433,9 +433,9 @@ class UDPSocket : public Socket { memcpy(src.ip, v6->ip, 16); src.port = localPort; - ipv6_tx_opts_t tx; - tx.scope = (ip_tx_scope_t)plan.fixed_opts.scope; - tx.index = plan.fixed_opts.index; + ip_tx_opts_t tx; + tx.scope = IP_TX_BOUND_L3; + tx.index = plan.l3_id; udp_send_segment(&src, &d, pay, &tx, (extraOpts.flags & SOCK_OPT_TTL) ? extraOpts.ttl : 0, (extraOpts.flags & SOCK_OPT_DONTFRAG) ? 1 : 0); return (int64_t)len; diff --git a/kernel/networking/transport_layer/tcp/tcp_core.c b/kernel/networking/transport_layer/tcp/tcp_core.c index 6dd3568c..a9212976 100644 --- a/kernel/networking/transport_layer/tcp/tcp_core.c +++ b/kernel/networking/transport_layer/tcp/tcp_core.c @@ -344,11 +344,11 @@ bool tcp_send_segment(ip_version_t ver, const void *src_ip_addr, const void *dst h.checksum = tcp_checksum_ipv4(segment, tcp_len, s, d); memcpy(segment, &h, sizeof(h)); - return ipv4_send_packet(d, PROTO_TCP, pkt, (const ipv4_tx_opts_t *)txp, ttl, dontfrag); + return ipv4_send_packet(d, PROTO_TCP, pkt, txp, ttl, dontfrag); } else if (ver == IP_VER6){ h.checksum = tcp_checksum_ipv6(segment, tcp_len, (const uint8_t *)src_ip_addr, (const uint8_t *)dst_ip_addr); memcpy(segment, &h, sizeof(h)); - return ipv6_send_packet((const uint8_t *)dst_ip_addr, PROTO_TCP, pkt, (const ipv6_tx_opts_t *)txp, ttl, dontfrag); + return ipv6_send_packet((const uint8_t *)dst_ip_addr, PROTO_TCP, pkt, txp, ttl, dontfrag); } netpkt_unref(pkt); @@ -375,15 +375,15 @@ void tcp_send_reset(ip_version_t ver, const void *src_ip_addr, const void *dst_i rst_hdr.urgent_ptr = 0; if (ver == IP_VER4){ - ipv4_tx_opts_t tx; + ip_tx_opts_t tx; tcp_build_tx_opts_from_local_v4(src_ip_addr, &tx); - tcp_send_segment(IP_VER4, src_ip_addr, dst_ip_addr, &rst_hdr, NULL, 0, NULL, 0, (const ip_tx_opts_t *)&tx, 0, 0); + tcp_send_segment(IP_VER4, src_ip_addr, dst_ip_addr, &rst_hdr, NULL, 0, NULL, 0, &tx, 0, 0); } else if (ver == IP_VER6){ - ipv6_tx_opts_t tx; + ip_tx_opts_t tx; tcp_build_tx_opts_from_local_v6(src_ip_addr, &tx); - tcp_send_segment(IP_VER6, src_ip_addr, dst_ip_addr, &rst_hdr, NULL, 0, NULL, 0, (const ip_tx_opts_t *)&tx, 0, 0); + tcp_send_segment(IP_VER6, src_ip_addr, dst_ip_addr, &rst_hdr, NULL, 0, NULL, 0, &tx, 0, 0); } } diff --git a/kernel/networking/transport_layer/tcp/tcp_rx.c b/kernel/networking/transport_layer/tcp/tcp_rx.c index d6c9248d..a2daa40f 100644 --- a/kernel/networking/transport_layer/tcp/tcp_rx.c +++ b/kernel/networking/transport_layer/tcp/tcp_rx.c @@ -786,13 +786,13 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a final_ack.urgent_ptr = 0; if (flow->base.local.ver == IP_VER4) { - ipv4_tx_opts_t tx; + ip_tx_opts_t tx; tcp_build_tx_opts_from_local_v4(flow->base.local.ip, &tx); - tcp_send_segment(IP_VER4, flow->base.local.ip, flow->base.remote.ip, &final_ack, NULL, 0, NULL, 0, (const ip_tx_opts_t *)&tx, flow->ip.ttl, flow->ip.dontfrag); + tcp_send_segment(IP_VER4, flow->base.local.ip, flow->base.remote.ip, &final_ack, NULL, 0, NULL, 0, &tx, flow->ip.ttl, flow->ip.dontfrag); } else { - ipv6_tx_opts_t tx; + ip_tx_opts_t tx; tcp_build_tx_opts_from_local_v6(flow->base.local.ip, &tx); - tcp_send_segment(IP_VER6, flow->base.local.ip, flow->base.remote.ip, &final_ack, NULL, 0, NULL, 0, (const ip_tx_opts_t *)&tx, flow->ip.ttl, flow->ip.dontfrag); + tcp_send_segment(IP_VER6, flow->base.local.ip, flow->base.remote.ip, &final_ack, NULL, 0, NULL, 0, &tx, flow->ip.ttl, flow->ip.dontfrag); } flow->base.state = TCP_ESTABLISHED; @@ -842,13 +842,13 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a rst_hdr.urgent_ptr = 0; if (flow->base.local.ver == IP_VER4) { - ipv4_tx_opts_t tx; + ip_tx_opts_t tx; tcp_build_tx_opts_from_local_v4(flow->base.local.ip, &tx); - (void)tcp_send_segment(IP_VER4, flow->base.local.ip, flow->base.remote.ip, &rst_hdr, NULL, 0, NULL, 0, (const ip_tx_opts_t *)&tx, flow->ip.ttl, flow->ip.dontfrag); + (void)tcp_send_segment(IP_VER4, flow->base.local.ip, flow->base.remote.ip, &rst_hdr, NULL, 0, NULL, 0, &tx, flow->ip.ttl, flow->ip.dontfrag); } else if (flow->base.local.ver == IP_VER6) { - ipv6_tx_opts_t tx; + ip_tx_opts_t tx; tcp_build_tx_opts_from_local_v6(flow->base.local.ip, &tx); - (void)tcp_send_segment(IP_VER6, flow->base.local.ip, flow->base.remote.ip, &rst_hdr, NULL, 0, NULL, 0, (const ip_tx_opts_t *)&tx, flow->ip.ttl, flow->ip.dontfrag); + (void)tcp_send_segment(IP_VER6, flow->base.local.ip, flow->base.remote.ip, &rst_hdr, NULL, 0, NULL, 0, &tx, flow->ip.ttl, flow->ip.dontfrag); } tcp_free_flow(idx); diff --git a/kernel/networking/transport_layer/tcp/tcp_timer.c b/kernel/networking/transport_layer/tcp/tcp_timer.c index 285ab334..a34ec38b 100644 --- a/kernel/networking/transport_layer/tcp/tcp_timer.c +++ b/kernel/networking/transport_layer/tcp/tcp_timer.c @@ -15,13 +15,13 @@ static void tcp_timer_send_ack_segment(tcp_flow_t *f, uint32_t seq, const uint8_ hdr.urgent_ptr = 0; if (f->base.local.ver == IP_VER4) { - ipv4_tx_opts_t tx; + ip_tx_opts_t tx; tcp_build_tx_opts_from_local_v4(f->base.local.ip, &tx); - (void)tcp_send_segment(IP_VER4, f->base.local.ip, f->base.remote.ip, &hdr, NULL, 0, payload, payload_len, (const ip_tx_opts_t *)&tx, f->ip.ttl, f->ip.dontfrag); + (void)tcp_send_segment(IP_VER4, f->base.local.ip, f->base.remote.ip, &hdr, NULL, 0, payload, payload_len, &tx, f->ip.ttl, f->ip.dontfrag); } else if (f->base.local.ver == IP_VER6) { - ipv6_tx_opts_t tx; + ip_tx_opts_t tx; tcp_build_tx_opts_from_local_v6(f->base.local.ip, &tx); - (void)tcp_send_segment(IP_VER6, f->base.local.ip, f->base.remote.ip, &hdr, NULL, 0, payload, payload_len, (const ip_tx_opts_t *)&tx, f->ip.ttl, f->ip.dontfrag); + (void)tcp_send_segment(IP_VER6, f->base.local.ip, f->base.remote.ip, &hdr, NULL, 0, payload, payload_len, &tx, f->ip.ttl, f->ip.dontfrag); } } //TODO events diff --git a/kernel/networking/transport_layer/tcp/tcp_tx.c b/kernel/networking/transport_layer/tcp/tcp_tx.c index ce6d1c3e..eeebf450 100644 --- a/kernel/networking/transport_layer/tcp/tcp_tx.c +++ b/kernel/networking/transport_layer/tcp/tcp_tx.c @@ -268,15 +268,15 @@ bool tcp_send_from_seg(tcp_flow_t *flow, tcp_tx_seg_t *seg){ const uint8_t *opts = seg->opts_len ? seg->opts : NULL; if (flow->base.local.ver == IP_VER4) { - ipv4_tx_opts_t tx; + ip_tx_opts_t tx; tcp_build_tx_opts_from_local_v4(flow->base.local.ip, &tx); - bool ok = tcp_send_segment(IP_VER4, flow->base.local.ip, flow->base.remote.ip, &hdr, opts, seg->opts_len, seg->buf ? (const uint8_t *)seg->buf : NULL, seg->len, (const ip_tx_opts_t *)&tx, flow->ip.ttl, flow->ip.dontfrag); + bool ok = tcp_send_segment(IP_VER4, flow->base.local.ip, flow->base.remote.ip, &hdr, opts, seg->opts_len, seg->buf ? (const uint8_t *)seg->buf : NULL, seg->len, &tx, flow->ip.ttl, flow->ip.dontfrag); if (ok) tcp_daemon_kick(); return ok; } else if (flow->base.local.ver == IP_VER6) { - ipv6_tx_opts_t tx; + ip_tx_opts_t tx; tcp_build_tx_opts_from_local_v6(flow->base.local.ip, &tx); - bool ok = tcp_send_segment(IP_VER6, flow->base.local.ip, flow->base.remote.ip, &hdr, opts, seg->opts_len, seg->buf ? (const uint8_t *)seg->buf : NULL, seg->len, (const ip_tx_opts_t *)&tx, flow->ip.ttl, flow->ip.dontfrag); + bool ok = tcp_send_segment(IP_VER6, flow->base.local.ip, flow->base.remote.ip, &hdr, opts, seg->opts_len, seg->buf ? (const uint8_t *)seg->buf : NULL, seg->len, &tx, flow->ip.ttl, flow->ip.dontfrag); if (ok) tcp_daemon_kick(); return ok; } @@ -359,13 +359,13 @@ void tcp_send_ack_now(tcp_flow_t *flow){ } if (flow->base.local.ver == IP_VER4) { - ipv4_tx_opts_t tx; + ip_tx_opts_t tx; tcp_build_tx_opts_from_local_v4(flow->base.local.ip, &tx); - (void)tcp_send_segment(IP_VER4, flow->base.local.ip, flow->base.remote.ip, &ackhdr, opts_len ? opts : NULL, opts_len, NULL, 0, (const ip_tx_opts_t *)&tx, flow->ip.ttl, flow->ip.dontfrag); + (void)tcp_send_segment(IP_VER4, flow->base.local.ip, flow->base.remote.ip, &ackhdr, opts_len ? opts : NULL, opts_len, NULL, 0, &tx, flow->ip.ttl, flow->ip.dontfrag); } else if (flow->base.local.ver == IP_VER6) { - ipv6_tx_opts_t tx; + ip_tx_opts_t tx; tcp_build_tx_opts_from_local_v6(flow->base.local.ip, &tx); - (void)tcp_send_segment(IP_VER6, flow->base.local.ip, flow->base.remote.ip, &ackhdr, opts_len ? opts : NULL, opts_len, NULL, 0, (const ip_tx_opts_t *)&tx, flow->ip.ttl, flow->ip.dontfrag); + (void)tcp_send_segment(IP_VER6, flow->base.local.ip, flow->base.remote.ip, &ackhdr, opts_len ? opts : NULL, opts_len, NULL, 0, &tx, flow->ip.ttl, flow->ip.dontfrag); } flow->timer.delayed_ack_pending = 0; diff --git a/kernel/networking/transport_layer/tcp/tcp_utils.c b/kernel/networking/transport_layer/tcp/tcp_utils.c index ae89136c..acfe7482 100644 --- a/kernel/networking/transport_layer/tcp/tcp_utils.c +++ b/kernel/networking/transport_layer/tcp/tcp_utils.c @@ -23,7 +23,7 @@ uint32_t tcp_calc_mss_for_l3(uint8_t l3_id, ip_version_t ver, const void *remote return mss; } -bool tcp_build_tx_opts_from_local_v4(const void *src_ip_addr, ipv4_tx_opts_t *out){ +bool tcp_build_tx_opts_from_local_v4(const void *src_ip_addr, ip_tx_opts_t *out){ if (!out) return false; uint32_t src_ip = 0; memcpy(&src_ip, src_ip_addr, sizeof(src_ip)); @@ -38,14 +38,14 @@ bool tcp_build_tx_opts_from_local_v4(const void *src_ip_addr, ipv4_tx_opts_t *ou return true; } -bool tcp_build_tx_opts_from_l3(uint8_t l3_id, ipv4_tx_opts_t *out){ +bool tcp_build_tx_opts_from_l3(uint8_t l3_id, ip_tx_opts_t *out){ if (!out) return false; out->scope = IP_TX_BOUND_L3; out->index = l3_id; return true; } -bool tcp_build_tx_opts_from_local_v6(const void *src_ip_addr, ipv6_tx_opts_t *out){ +bool tcp_build_tx_opts_from_local_v6(const void *src_ip_addr, ip_tx_opts_t *out){ if (!out) return false; const uint8_t *sip = (const uint8_t *)src_ip_addr; l3_ipv6_interface_t *v6 = l3_ipv6_find_by_ip(sip); diff --git a/kernel/networking/transport_layer/tcp/tcp_utils.h b/kernel/networking/transport_layer/tcp/tcp_utils.h index b5c41f83..41ffed40 100644 --- a/kernel/networking/transport_layer/tcp/tcp_utils.h +++ b/kernel/networking/transport_layer/tcp/tcp_utils.h @@ -31,9 +31,9 @@ uint8_t tcp_build_syn_options(uint8_t *out, uint16_t mss, uint8_t wscale, uint8_ uint32_t tcp_calc_mss_for_l3(uint8_t l3_id, ip_version_t ver, const void *remote_ip); -bool tcp_build_tx_opts_from_local_v4(const void *src_ip_addr, ipv4_tx_opts_t *out); -bool tcp_build_tx_opts_from_l3(uint8_t l3_id, ipv4_tx_opts_t *out); -bool tcp_build_tx_opts_from_local_v6(const void *src_ip_addr, ipv6_tx_opts_t *out); +bool tcp_build_tx_opts_from_local_v4(const void *src_ip_addr, ip_tx_opts_t *out); +bool tcp_build_tx_opts_from_l3(uint8_t l3_id, ip_tx_opts_t *out); +bool tcp_build_tx_opts_from_local_v6(const void *src_ip_addr, ip_tx_opts_t *out); #ifdef __cplusplus } diff --git a/kernel/networking/transport_layer/udp.c b/kernel/networking/transport_layer/udp.c index bc64a9a5..0ff1b250 100644 --- a/kernel/networking/transport_layer/udp.c +++ b/kernel/networking/transport_layer/udp.c @@ -51,10 +51,10 @@ void udp_send_segment(const net_l4_endpoint *src, const net_l4_endpoint *dst, si uint32_t dst_ip = 0; memcpy(&dst_ip, dst->ip, 4); (void)netpkt_trim(pkt, (uint32_t)written); - ipv4_send_packet(dst_ip, PROTO_UDP, pkt, (const ipv4_tx_opts_t*)tx_opts, ttl, dontfrag); + ipv4_send_packet(dst_ip, PROTO_UDP, pkt, tx_opts, ttl, dontfrag); } else if (src->ver == IP_VER6) { (void)netpkt_trim(pkt, (uint32_t)written); - ipv6_send_packet(dst->ip, PROTO_UDP, pkt, (const ipv6_tx_opts_t*)tx_opts, ttl, dontfrag); + ipv6_send_packet(dst->ip, PROTO_UDP, pkt, tx_opts, ttl, dontfrag); } else { netpkt_unref(pkt); } diff --git a/kernel/tools/curl.c b/kernel/tools/curl.c index 8389aa50..ed6b58ab 100644 --- a/kernel/tools/curl.c +++ b/kernel/tools/curl.c @@ -70,9 +70,9 @@ static int curl_fetch(char *url, bool head_only, bool follow) { } HTTPClientPolicyOptions opts = {0}; - opts.common.allow_chunked = 1; - opts.follow_redirects = follow ? 1 : 0; - opts.max_redirects = CURL_MAX_REDIRECTS; + opts.flags = HTTP_CLIENT_OPT_FOLLOW_REDIRECTS | HTTP_CLIENT_OPT_MAX_REDIRECTS; + opts.value.follow_redirects = follow != 0; + opts.value.max_redirects = CURL_MAX_REDIRECTS; http_client_handle_t cli = http_client_create(NULL, &opts); if (!cli) { @@ -100,6 +100,8 @@ static int curl_fetch(char *url, bool head_only, bool follow) { HTTPResponseMsg resp = http_client_send_request(cli, &req); if ((int32_t)resp.status_code < 0) { print("curl: request failed (%d)", (int)resp.status_code); + http_response_free(&resp); + http_headers_common_free(&req.headers_common); http_client_destroy(cli); string_free(host); string_free(path); @@ -108,17 +110,14 @@ static int curl_fetch(char *url, bool head_only, bool follow) { if (head_only) { HTTPResponseMsg head = resp; - head.body = (sizedptr){0}; + head.body = (string){0}; string raw = http_response_builder(&head); print("%.*s", (int)raw.length, raw.data); string_free(raw); - } else if (resp.body.ptr && resp.body.size) print("%.*s", (int)resp.body.size, (const char*)resp.body.ptr); + } else if (resp.body.data && resp.body.length) print("%.*s", (int)resp.body.length, (const char*)resp.body.data); - if (resp.reason.mem_length) string_free(resp.reason); - http_headers_common_free(&resp.headers_common); - http_headers_extra_free(resp.extra_headers, resp.extra_header_count); - if (resp.body.ptr && resp.body.size) release((void*)resp.body.ptr); - resp = (HTTPResponseMsg){0}; + http_response_free(&resp); + http_headers_common_free(&req.headers_common); http_client_destroy(cli); string_free(host); diff --git a/kernel/tools/ping.c b/kernel/tools/ping.c index 1c2db943..9c55a84e 100644 --- a/kernel/tools/ping.c +++ b/kernel/tools/ping.c @@ -115,8 +115,8 @@ static int ping_v4(const ping_opts_t *o) { uint16_t id = (uint16_t)(get_current_proc_pid() & 0xFFFF); uint16_t seq_base = (uint16_t)(get_time() & 0xFFFF); - ipv4_tx_opts_t txo = {0}; - const ipv4_tx_opts_t *txop = NULL; + ip_tx_opts_t txo = {0}; + const ip_tx_opts_t *txop = NULL; if (o->src_set) { l3_ipv4_interface_t *l3 = l3_ipv4_find_by_ip(o->src_ip); if (!l3) { diff --git a/kernel/tools/tracert.c b/kernel/tools/tracert.c index f4c5b688..ad9aeef9 100644 --- a/kernel/tools/tracert.c +++ b/kernel/tools/tracert.c @@ -107,8 +107,8 @@ static int tracert_v4(const tr_opts_t *o) { string_format_buf(line + len, sizeof(line) - len, "address"); print("%s", line); - ipv4_tx_opts_t txo = (ipv4_tx_opts_t){0}; - const ipv4_tx_opts_t *txop = NULL; + ip_tx_opts_t txo = (ip_tx_opts_t){0}; + const ip_tx_opts_t *txop = NULL; if (o->src_set) { l3_ipv4_interface_t *l3 = l3_ipv4_find_by_ip(o->src_ip); if (!l3) { From c953e2b78731ced63d88558ca19fa4aec7c4093c Mon Sep 17 00:00:00 2001 From: CodeAnarchist Date: Thu, 11 Jun 2026 21:22:24 +0200 Subject: [PATCH 23/39] [netpkt] clean up netpkt payload --- kernel/networking/net_fragbuf.c | 4 +- kernel/networking/netpkt.c | 624 +++++++----------- kernel/networking/netpkt.h | 2 +- .../networking/transport_layer/tcp/tcp_tx.c | 5 +- 4 files changed, 256 insertions(+), 379 deletions(-) diff --git a/kernel/networking/net_fragbuf.c b/kernel/networking/net_fragbuf.c index 544e122c..d7d07215 100644 --- a/kernel/networking/net_fragbuf.c +++ b/kernel/networking/net_fragbuf.c @@ -40,10 +40,8 @@ bool net_fragbuf_add(net_fragbuf_t *fb, netpkt_t *pkt, uint32_t pkt_off, uint32_ if (new_cap < fb->step) new_cap = fb->step; if (new_cap > fb->max_len) new_cap = fb->max_len; - uint8_t *new_data = (uint8_t*)zalloc(new_cap); + uint8_t *new_data = fb->data ? (uint8_t*)reallocate(fb->data, new_cap) : (uint8_t*)zalloc(new_cap); if (!new_data) return false; - if (fb->data && fb->cap) memcpy(new_data, fb->data, fb->cap); - if (fb->data) release(fb->data); fb->data = new_data; fb->cap = new_cap; } diff --git a/kernel/networking/netpkt.c b/kernel/networking/netpkt.c index 6f0b21e1..e207edc3 100644 --- a/kernel/networking/netpkt.c +++ b/kernel/networking/netpkt.c @@ -8,6 +8,10 @@ #define NETPKT_BUF_F_SMALL 2u #define NETPKT_META_KEEP_EMPTY_PAGES 2u #define NETPKT_SMALL_CLASS_BYTES 2048u +#define NETPKT_PAYLOAD_CLASS_COUNT 6 +#define NETPKT_PAYLOAD_CACHE_KEEP 8 +#define NETPKT_PAYLOAD_CACHE_MAX_BYTES (2 *1024 * 1024ull) +#define NETPKT_MAX_STORAGE_BYTES 131072u typedef struct netpkt_buf netpkt_buf_t; @@ -56,14 +60,131 @@ struct netpkt { }; static uint64_t g_netpkt_page_bytes; -static uint64_t g_netpkt_payload_page_bytes; -static uint64_t g_netpkt_meta_page_bytes; static meta_slab_t g_meta_slab_pkt; static meta_slab_t g_meta_slab_buf; +static uint64_t g_payload_cache_bytes; -static uintptr_t g_spare_page; static void* g_netpkt_small_free; +static void* g_payload_cache[NETPKT_PAYLOAD_CLASS_COUNT]; +static uint32_t g_payload_cache_count[NETPKT_PAYLOAD_CLASS_COUNT]; +static const uint32_t g_payload_cache_size[NETPKT_PAYLOAD_CLASS_COUNT] = {PAGE_SIZE, PAGE_SIZE*2, PAGE_SIZE*4, PAGE_SIZE*8, PAGE_SIZE*16, NETPKT_MAX_STORAGE_BYTES}; + +static void* netpkt_payload_alloc(uint32_t cap, uint32_t* flags) { + if (!flags) return 0; + *flags = 0; + if (!cap) return 0; + + if (cap == NETPKT_SMALL_CLASS_BYTES) { + irq_flags_t irq = irq_save_disable(); + if (g_netpkt_small_free) { + void* mem = g_netpkt_small_free; + g_netpkt_small_free = *(void**)mem; + irq_restore(irq); + memset(mem, 0, NETPKT_SMALL_CLASS_BYTES); + *flags = NETPKT_BUF_F_SMALL; + return mem; + } + if ((uint64_t)PAGE_SIZE > (uint64_t)NETPKT_MAX_PAGE_BYTES || g_netpkt_page_bytes > (uint64_t)NETPKT_MAX_PAGE_BYTES - (uint64_t)PAGE_SIZE) { + irq_restore(irq); + return 0; + } + g_netpkt_page_bytes += (uint64_t)PAGE_SIZE; + irq_restore(irq); + + void* page = palloc(PAGE_SIZE, MEM_PRIV_KERNEL, MEM_RW | MEM_NORM, true); + if (!page) { + irq = irq_save_disable(); + if (g_netpkt_page_bytes >= (uint64_t)PAGE_SIZE) g_netpkt_page_bytes -= (uint64_t)PAGE_SIZE; + else g_netpkt_page_bytes = 0; + irq_restore(irq); + return 0; + } + + void* second = (void*)((uintptr_t)page+NETPKT_SMALL_CLASS_BYTES); + irq = irq_save_disable(); + *(void**)second = g_netpkt_small_free; + g_netpkt_small_free = second; + irq_restore(irq); + + *flags = NETPKT_BUF_F_SMALL; + return page; + } + + uint32_t cls = NETPKT_PAYLOAD_CLASS_COUNT; + for (uint32_t i = 0; i < NETPKT_PAYLOAD_CLASS_COUNT; i++)if (g_payload_cache_size[i] == cap) { + cls = i; + break; + } + + irq_flags_t irq = irq_save_disable(); + if (cls < NETPKT_PAYLOAD_CLASS_COUNT && g_payload_cache[cls]) { + void* mem = g_payload_cache[cls]; + g_payload_cache[cls] = *(void**)mem; + if (g_payload_cache_count[cls]) g_payload_cache_count[cls]--; + if (g_payload_cache_bytes >= (uint64_t)cap) g_payload_cache_bytes -= (uint64_t)cap; + else g_payload_cache_bytes = 0; + irq_restore(irq); + memset(mem, 0, cap); + return mem; + } + if ((uint64_t)cap > (uint64_t)NETPKT_MAX_PAGE_BYTES || g_netpkt_page_bytes > (uint64_t)NETPKT_MAX_PAGE_BYTES - (uint64_t)cap) { + irq_restore(irq); + return 0; + } + g_netpkt_page_bytes += (uint64_t)cap; + irq_restore(irq); + + void* mem = palloc((uint64_t)cap, MEM_PRIV_KERNEL, MEM_RW | MEM_NORM, true); + if (!mem) { + irq = irq_save_disable(); + if (g_netpkt_page_bytes >= (uint64_t)cap) g_netpkt_page_bytes -= (uint64_t)cap; + else g_netpkt_page_bytes = 0; + irq_restore(irq); + return 0; + } + + return mem; +} + +static void netpkt_payload_free(uintptr_t base, uint32_t cap, uint32_t flags) { + if (!base) return; + if (flags & NETPKT_BUF_F_SMALL) { + irq_flags_t irq = irq_save_disable(); + *(void**)base = g_netpkt_small_free; + g_netpkt_small_free = (void*)base; + irq_restore(irq); + return; + } + + uint32_t cls = NETPKT_PAYLOAD_CLASS_COUNT; + for (uint32_t i = 0; i < NETPKT_PAYLOAD_CLASS_COUNT; i++)if (g_payload_cache_size[i] == cap) { + cls = i; + break; + } + if (cls < NETPKT_PAYLOAD_CLASS_COUNT) { + irq_flags_t irq = irq_save_disable(); + if (g_payload_cache_count[cls] < NETPKT_PAYLOAD_CACHE_KEEP && g_payload_cache_bytes <= NETPKT_PAYLOAD_CACHE_MAX_BYTES - (uint64_t)cap) { + *(void**)base = g_payload_cache[cls]; + g_payload_cache[cls] = (void*)base; + g_payload_cache_count[cls]++; + g_payload_cache_bytes += (uint64_t)cap; + irq_restore(irq); + return; + } + if (g_netpkt_page_bytes >= (uint64_t)cap) g_netpkt_page_bytes -= (uint64_t)cap; + else g_netpkt_page_bytes = 0; + irq_restore(irq); + pfree((void*)base, (uint64_t)cap); + return; + } + + irq_flags_t irq = irq_save_disable(); + if (g_netpkt_page_bytes >= (uint64_t)cap) g_netpkt_page_bytes -= (uint64_t)cap; + else g_netpkt_page_bytes = 0; + irq_restore(irq); + pfree((void*)base, (uint64_t)cap); +} static void* meta_slab_alloc(meta_slab_t* s, uint32_t obj_size_aligned) { if (!s) return 0; @@ -86,25 +207,20 @@ static void* meta_slab_alloc(meta_slab_t* s, uint32_t obj_size_aligned) { s->empty = p->next; if (s->empty_pages) s->empty_pages--; p->next = s->partial; - s->partial = p; + s->partial = p; } else { if ((uint64_t)PAGE_SIZE > (uint64_t)NETPKT_MAX_PAGE_BYTES) return 0; if (g_netpkt_page_bytes > (uint64_t)NETPKT_MAX_PAGE_BYTES - (uint64_t)PAGE_SIZE) return 0; g_netpkt_page_bytes += (uint64_t)PAGE_SIZE; - g_netpkt_meta_page_bytes += (uint64_t)PAGE_SIZE; - meta_slab_page_t* p = (meta_slab_page_t*)palloc(PAGE_SIZE, MEM_PRIV_KERNEL, MEM_RW | MEM_NORM, true); if (!p) { if (g_netpkt_page_bytes >= (uint64_t)PAGE_SIZE) g_netpkt_page_bytes-= (uint64_t)PAGE_SIZE; else g_netpkt_page_bytes = 0; - - if (g_netpkt_meta_page_bytes >= (uint64_t)PAGE_SIZE) g_netpkt_meta_page_bytes -= (uint64_t)PAGE_SIZE; - else g_netpkt_meta_page_bytes = 0; return 0; } - p->next = 0;// + p->next = 0; p->free_list = 0; p->in_use = 0; p->capacity = 0; @@ -118,9 +234,6 @@ static void* meta_slab_alloc(meta_slab_t* s, uint32_t obj_size_aligned) { pfree(p, (uint64_t)PAGE_SIZE); if (g_netpkt_page_bytes >= (uint64_t)PAGE_SIZE) g_netpkt_page_bytes -= (uint64_t)PAGE_SIZE; else g_netpkt_page_bytes = 0; - - if (g_netpkt_meta_page_bytes >= (uint64_t)PAGE_SIZE) g_netpkt_meta_page_bytes -= (uint64_t)PAGE_SIZE; - else g_netpkt_meta_page_bytes = 0; return 0; } @@ -129,15 +242,12 @@ static void* meta_slab_alloc(meta_slab_t* s, uint32_t obj_size_aligned) { pfree(p, (uint64_t)PAGE_SIZE); if (g_netpkt_page_bytes >= (uint64_t)PAGE_SIZE) g_netpkt_page_bytes -= (uint64_t)PAGE_SIZE; else g_netpkt_page_bytes = 0; - if (g_netpkt_meta_page_bytes >= (uint64_t)PAGE_SIZE) g_netpkt_meta_page_bytes -= (uint64_t)PAGE_SIZE; - else g_netpkt_meta_page_bytes = 0; return 0; } p->capacity = (uint16_t)cap; - uint32_t i = 0; - while (i< cap) { + for (uint32_t i = 0; i < cap; i++) { uint8_t* slot = start + (uint64_t)i * (uint64_t)p->stride; meta_obj_hdr_t* h = (meta_obj_hdr_t*)(uintptr_t)slot; h->page = p; @@ -146,7 +256,6 @@ static void* meta_slab_alloc(meta_slab_t* s, uint32_t obj_size_aligned) { *(void**)obj = p->free_list; p->free_list = obj; - i++; } p->next = s->partial; @@ -182,8 +291,7 @@ static void meta_slab_free(meta_slab_t* s, void* obj) { *(void**)obj = page->free_list; page->free_list = obj; - bool was_full = false; - if (page->in_use == page->capacity) was_full = true; + bool was_full = page->in_use == page->capacity; if (page->in_use) page->in_use--; if (was_full) { @@ -236,8 +344,6 @@ static void meta_slab_free(meta_slab_t* s, void* obj) { pfree(e,(uint64_t)PAGE_SIZE); if (g_netpkt_page_bytes >= (uint64_t)PAGE_SIZE) g_netpkt_page_bytes -= (uint64_t)PAGE_SIZE; else g_netpkt_page_bytes = 0; - if (g_netpkt_meta_page_bytes >= (uint64_t)PAGE_SIZE) g_netpkt_meta_page_bytes -= (uint64_t)PAGE_SIZE; - else g_netpkt_meta_page_bytes = 0; } } } @@ -247,81 +353,38 @@ static bool netpkt_realloc_to(netpkt_t* p, uint32_t new_head, uint32_t new_alloc if (!p->buf) return false; if (p->flags & NETPKT_F_VIEW) return false; - uint64_t min = (uint64_t)new_head + (uint64_t)p->len; - if ((uint64_t)new_alloc < min) return false; + if (p->len > NETPKT_MAX_ALLOC) return false; + if ((uint64_t)new_head + (uint64_t)p->len > (uint64_t)new_alloc) return false; + if (new_alloc > NETPKT_MAX_STORAGE_BYTES) return false; - uint64_t bytes = (uint64_t)new_alloc; - if (!bytes) bytes = 1; + uint64_t cap64 = new_alloc ? new_alloc : 1; - bool small = bytes <= (uint64_t)NETPKT_SMALL_CLASS_BYTES; - uint32_t cap = small ? NETPKT_SMALL_CLASS_BYTES : 0; - if (!small) { - uint64_t cap64 = count_pages(bytes, PAGE_SIZE)*(uint64_t)PAGE_SIZE; + uint32_t cap = NETPKT_SMALL_CLASS_BYTES; + if (cap64 > NETPKT_SMALL_CLASS_BYTES) { + cap64 = count_pages(cap64, PAGE_SIZE)*(uint64_t)PAGE_SIZE; if (!cap64) cap64 = PAGE_SIZE; - if (cap64 > (uint64_t)NETPKT_MAX_ALLOC) return false; + if (cap64 > NETPKT_MAX_STORAGE_BYTES)return false; cap = (uint32_t)cap64; } if ((uint64_t)new_head+(uint64_t)p->len > (uint64_t)cap) return false; - void* mem = 0; - bool from_spare = false; uint32_t flags = 0; - if (small) { - if (!g_netpkt_small_free) { - if (g_netpkt_page_bytes > (uint64_t)NETPKT_MAX_PAGE_BYTES - (uint64_t)PAGE_SIZE) return false; - void* page = palloc(PAGE_SIZE, MEM_PRIV_KERNEL, MEM_RW | MEM_NORM, true); - if (!page) return false; - g_netpkt_page_bytes += (uint64_t)PAGE_SIZE; - g_netpkt_payload_page_bytes += (uint64_t)PAGE_SIZE; - *(void**)page = (void*)((uintptr_t)page + (uintptr_t)NETPKT_SMALL_CLASS_BYTES); - *(void**)((uintptr_t)page + (uintptr_t)NETPKT_SMALL_CLASS_BYTES) = 0; - g_netpkt_small_free = page; - } - mem = g_netpkt_small_free; - g_netpkt_small_free = *(void**)mem; - memset(mem, 0, NETPKT_SMALL_CLASS_BYTES); - flags = NETPKT_BUF_F_SMALL; - } else if (cap == PAGE_SIZE && g_spare_page) { - mem = (void*)g_spare_page; - g_spare_page = 0; - from_spare = true; - memset(mem, 0, PAGE_SIZE); - } else { - if ((uint64_t)cap > (uint64_t)NETPKT_MAX_PAGE_BYTES) return false; - if (g_netpkt_page_bytes > (uint64_t)NETPKT_MAX_PAGE_BYTES - (uint64_t)cap)return false; - g_netpkt_page_bytes += (uint64_t)cap; - g_netpkt_payload_page_bytes += (uint64_t)cap; - - mem = palloc((uint64_t)cap, MEM_PRIV_KERNEL, MEM_RW | MEM_NORM, true); - if (!mem) { - if (g_netpkt_page_bytes >= (uint64_t)cap) g_netpkt_page_bytes -= (uint64_t)cap; - else g_netpkt_page_bytes = 0; - - if (g_netpkt_payload_page_bytes >= (uint64_t)cap) g_netpkt_payload_page_bytes -= (uint64_t)cap; - else g_netpkt_payload_page_bytes = 0; - return false; - } - } + void* mem = netpkt_payload_alloc(cap, &flags); + if (!mem) return false; - uint32_t sz = (uint32_t)(((uint64_t)sizeof(netpkt_buf_t) + 15) & ~15ull); - netpkt_buf_t* nb = (netpkt_buf_t*)meta_slab_alloc(&g_meta_slab_buf, sz); + uint32_t bsz = (uint32_t)(((uint64_t)sizeof(netpkt_buf_t) + 15ull) & ~15ull); + irq_flags_t irq = irq_save_disable(); + netpkt_buf_t* nb = (netpkt_buf_t*)meta_slab_alloc(&g_meta_slab_buf, bsz); + irq_restore(irq); if (!nb) { - if (flags & NETPKT_BUF_F_SMALL) { - *(void**)mem = g_netpkt_small_free; - g_netpkt_small_free = mem; - } else if (from_spare) { - g_spare_page = (uintptr_t)mem; - } else { - pfree(mem, (uint64_t)cap); - if (g_netpkt_page_bytes >= (uint64_t)cap) g_netpkt_page_bytes -= (uint64_t)cap; - else g_netpkt_page_bytes = 0; - if (g_netpkt_payload_page_bytes >= (uint64_t)cap) g_netpkt_payload_page_bytes -= (uint64_t)cap; - else g_netpkt_payload_page_bytes = 0; - } + netpkt_payload_free((uintptr_t)mem, cap, flags); return false; } + uintptr_t old_data = netpkt_data(p); + if (p->len && old_data) memcpy((void*)((uintptr_t)mem + (uintptr_t)new_head), (const void*)old_data, p->len); + nb->base = (uintptr_t)mem; nb->alloc = cap; nb->refs = 1; @@ -329,8 +392,13 @@ static bool netpkt_realloc_to(netpkt_t* p, uint32_t new_head, uint32_t new_alloc nb->free_fn = 0; nb->free_ctx = 0; - if (p->len) memcpy((void*)(nb->base + (uintptr_t)new_head), (const void*)netpkt_data(p), p->len); + uintptr_t free_base = 0; + uint32_t free_alloc = 0; + uint32_t free_flags = 0; + netpkt_free_fn free_fn = 0; + void* free_ctx = 0; + irq = irq_save_disable(); netpkt_buf_t* ob = p->buf; p->buf = nb; p->off = 0; @@ -338,131 +406,51 @@ static bool netpkt_realloc_to(netpkt_t* p, uint32_t new_head, uint32_t new_alloc p->head = new_head; if (ob) { - if (ob->refs > 1) { - ob->refs--; - } else { - if (ob->flags & NETPKT_BUF_F_EXTERNAL) { - if (ob->free_fn) ob->free_fn(ob->free_ctx, ob->base); - else if (ob->base) release((void*)ob->base); - } else if (ob->flags & NETPKT_BUF_F_SMALL) { - *(void**)ob->base = g_netpkt_small_free; - g_netpkt_small_free = (void*)ob->base; - } else { - bool aligned = ((ob->base & (PAGE_SIZE - 1)) == 0) && ((ob->alloc & (PAGE_SIZE - 1)) == 0); - if (!g_spare_page && aligned) { - g_spare_page = ob->base; - - if (ob->alloc > PAGE_SIZE) { - pfree((void*)(ob->base + PAGE_SIZE), (uint64_t)ob->alloc - (uint64_t)PAGE_SIZE); - uint64_t dec = (uint64_t)ob->alloc-(uint64_t)PAGE_SIZE; - if (g_netpkt_page_bytes >= dec) g_netpkt_page_bytes -= dec; - else g_netpkt_page_bytes = 0; - if (g_netpkt_payload_page_bytes >= dec) g_netpkt_payload_page_bytes -= dec; - else g_netpkt_payload_page_bytes = 0; - } - } else { - if (ob->base) pfree((void*)ob->base,(uint64_t)ob->alloc); - uint64_t dec = (uint64_t)ob->alloc; - if (g_netpkt_page_bytes >= dec) g_netpkt_page_bytes -= dec; - else g_netpkt_page_bytes = 0; - if (g_netpkt_payload_page_bytes >= dec) g_netpkt_payload_page_bytes -= dec; - else g_netpkt_payload_page_bytes = 0; - } - } - + if (ob->refs > 1) ob->refs--; + else { + free_base = ob->base; + free_alloc = ob->alloc; + free_flags = ob->flags; + free_fn = ob->free_fn; + free_ctx = ob->free_ctx; meta_slab_free(&g_meta_slab_buf, ob); } } + irq_restore(irq); + if (free_base) { + if (free_flags & NETPKT_BUF_F_EXTERNAL) { + if (free_fn) free_fn(free_ctx, free_base); + else release((void*)free_base); + } else netpkt_payload_free(free_base, free_alloc, free_flags); + } return true; } netpkt_t* netpkt_alloc(uint32_t data_capacity, uint32_t headroom, uint32_t tailroom) { - irq_flags_t irq = irq_save_disable(); + if (data_capacity > NETPKT_MAX_ALLOC) return 0; uint64_t alloc = (uint64_t)headroom + (uint64_t)data_capacity + (uint64_t)tailroom; - if (alloc > (uint64_t)NETPKT_MAX_ALLOC) { - irq_restore(irq); - return 0; - } - - if (!alloc) alloc = 1; - bool small = alloc <= (uint64_t)NETPKT_SMALL_CLASS_BYTES; - uint32_t cap = small ? NETPKT_SMALL_CLASS_BYTES : 0; - if (!small) { - uint64_t cap64 = count_pages(alloc, PAGE_SIZE) * (uint64_t)PAGE_SIZE; - if (!cap64) cap64 = PAGE_SIZE; - if (cap64 > (uint64_t)NETPKT_MAX_ALLOC) { - irq_restore(irq); - return 0; - } - - cap = (uint32_t)cap64; + if (alloc > (uint64_t)NETPKT_MAX_STORAGE_BYTES) return 0; + uint64_t cap_bytes = alloc ? alloc : 1; + uint32_t cap = NETPKT_SMALL_CLASS_BYTES; + if (cap_bytes > NETPKT_SMALL_CLASS_BYTES) { + cap_bytes = count_pages(cap_bytes, PAGE_SIZE) * (uint64_t)PAGE_SIZE; + if (!cap_bytes) cap_bytes = PAGE_SIZE; + if (cap_bytes > NETPKT_MAX_STORAGE_BYTES) return 0; + cap = (uint32_t)cap_bytes; } - void* mem = 0; - bool from_spare = false; uint32_t flags = 0; - if (small) { - if (!g_netpkt_small_free) { - if (g_netpkt_page_bytes > (uint64_t)NETPKT_MAX_PAGE_BYTES - (uint64_t)PAGE_SIZE) { - irq_restore(irq); - return 0; - } - void* page = palloc(PAGE_SIZE, MEM_PRIV_KERNEL, MEM_RW | MEM_NORM, true); - if (!page) { - irq_restore(irq); - return 0; - } - g_netpkt_page_bytes += (uint64_t)PAGE_SIZE; - g_netpkt_payload_page_bytes += (uint64_t)PAGE_SIZE; - *(void**)page = (void*)((uintptr_t)page + (uintptr_t)NETPKT_SMALL_CLASS_BYTES); - *(void**)((uintptr_t)page + (uintptr_t)NETPKT_SMALL_CLASS_BYTES) = 0; - g_netpkt_small_free = page; - } - mem = g_netpkt_small_free; - g_netpkt_small_free = *(void**)mem; - memset(mem, 0, NETPKT_SMALL_CLASS_BYTES); - flags = NETPKT_BUF_F_SMALL; - } else if (cap == PAGE_SIZE && g_spare_page) { - mem = (void*)g_spare_page; - g_spare_page = 0; - from_spare = true; - memset(mem, 0, PAGE_SIZE); - } else { - if ((uint64_t)cap > (uint64_t)NETPKT_MAX_PAGE_BYTES || g_netpkt_page_bytes > (uint64_t)NETPKT_MAX_PAGE_BYTES - (uint64_t)cap) { - irq_restore(irq); - return 0; - } - g_netpkt_page_bytes += (uint64_t)cap; - g_netpkt_payload_page_bytes += (uint64_t)cap; - - mem = palloc((uint64_t)cap, MEM_PRIV_KERNEL, MEM_RW | MEM_NORM, true); - if (!mem) { - if (g_netpkt_page_bytes >= (uint64_t)cap) g_netpkt_page_bytes -= (uint64_t)cap; - else g_netpkt_page_bytes = 0; - if (g_netpkt_payload_page_bytes >= (uint64_t)cap) g_netpkt_payload_page_bytes -= (uint64_t)cap; - else g_netpkt_payload_page_bytes = 0; - irq_restore(irq); - return 0; - } - } + void* mem = netpkt_payload_alloc(cap, &flags); + if (!mem) return 0; - uint32_t bsz = (uint32_t)(((uint64_t)sizeof(netpkt_buf_t) + 15ull) & ~15ull); - netpkt_buf_t* b = (netpkt_buf_t*)meta_slab_alloc(&g_meta_slab_buf, bsz); - if (!b) { - if (flags & NETPKT_BUF_F_SMALL) { - *(void**)mem = g_netpkt_small_free; - g_netpkt_small_free = mem; - } else if (from_spare) { - g_spare_page = (uintptr_t)mem; - } else { - pfree(mem, (uint64_t)cap); - if (g_netpkt_page_bytes >= (uint64_t)cap) g_netpkt_page_bytes -= (uint64_t)cap; - else g_netpkt_page_bytes = 0; - if (g_netpkt_payload_page_bytes >= (uint64_t)cap) g_netpkt_payload_page_bytes -= (uint64_t)cap; - else g_netpkt_payload_page_bytes = 0; - } + irq_flags_t irq = irq_save_disable(); + netpkt_buf_t* b = (netpkt_buf_t*)meta_slab_alloc(&g_meta_slab_buf, (uint32_t)(((uint64_t)sizeof(netpkt_buf_t) + 15ull) & ~15ull)); + netpkt_t* p = b ? (netpkt_t*)meta_slab_alloc(&g_meta_slab_pkt, (uint32_t)(((uint64_t)sizeof(netpkt_t) + 15ull) & ~15ull)) : 0; + if (!b || !p) { + if (b) meta_slab_free(&g_meta_slab_buf, b); irq_restore(irq); + netpkt_payload_free((uintptr_t)mem, cap, flags); return 0; } @@ -473,26 +461,6 @@ netpkt_t* netpkt_alloc(uint32_t data_capacity, uint32_t headroom, uint32_t tailr b->free_fn = 0; b->free_ctx = 0; - uint32_t psz = (uint32_t)(((uint64_t)sizeof(netpkt_t) + 15ull) &~15ull); - netpkt_t* p = (netpkt_t*)meta_slab_alloc(&g_meta_slab_pkt, psz); - if (!p) { - meta_slab_free(&g_meta_slab_buf, b); - if (flags & NETPKT_BUF_F_SMALL) { - *(void**)mem = g_netpkt_small_free; - g_netpkt_small_free = mem; - } else if (from_spare) { - g_spare_page = (uintptr_t)mem; - } else { - pfree(mem, (uint64_t)cap); - if (g_netpkt_page_bytes >= (uint64_t)cap) g_netpkt_page_bytes -= (uint64_t)cap; - else g_netpkt_page_bytes = 0; - if (g_netpkt_payload_page_bytes >= (uint64_t)cap) g_netpkt_payload_page_bytes -= (uint64_t)cap; - else g_netpkt_payload_page_bytes = 0; - } - irq_restore(irq); - return 0; - } - p->buf = b; p->off = 0; p->cap = cap; @@ -505,15 +473,12 @@ netpkt_t* netpkt_alloc(uint32_t data_capacity, uint32_t headroom, uint32_t tailr } netpkt_t* netpkt_wrap(uintptr_t base, uint32_t alloc_size, uint32_t data_off, uint32_t data_len, netpkt_free_fn free_fn, void* ctx) { + if (!base || !alloc_size || alloc_size > NETPKT_MAX_STORAGE_BYTES || data_len > NETPKT_MAX_ALLOC || data_off > alloc_size || data_len > alloc_size - data_off) return 0; irq_flags_t irq = irq_save_disable(); - if (!base || !alloc_size || alloc_size > NETPKT_MAX_ALLOC || data_off > alloc_size || data_len > alloc_size - data_off) { - irq_restore(irq); - return 0; - } - - uint32_t bsz = (uint32_t)(((uint64_t)sizeof(netpkt_buf_t) + 15ull) &~15ull); - netpkt_buf_t* b = (netpkt_buf_t*)meta_slab_alloc(&g_meta_slab_buf, bsz); - if (!b) { + netpkt_buf_t* b = (netpkt_buf_t*)meta_slab_alloc(&g_meta_slab_buf, (uint32_t)(((uint64_t)sizeof(netpkt_buf_t) + 15ull) & ~15ull)); + netpkt_t* p = b ? (netpkt_t*)meta_slab_alloc(&g_meta_slab_pkt, (uint32_t)(((uint64_t)sizeof(netpkt_t) + 15ull) & ~15ull)) : 0; + if (!b || !p) { + if (b) meta_slab_free(&g_meta_slab_buf, b); irq_restore(irq); return 0; } @@ -525,14 +490,6 @@ netpkt_t* netpkt_wrap(uintptr_t base, uint32_t alloc_size, uint32_t data_off, ui b->free_fn = free_fn; b->free_ctx = ctx; - uint32_t psz = (uint32_t)(((uint64_t)sizeof(netpkt_t) + 15ull) &~15ull); - netpkt_t* p = (netpkt_t*)meta_slab_alloc(&g_meta_slab_pkt, psz); - if (!p) { - meta_slab_free(&g_meta_slab_buf, b); - irq_restore(irq); - return 0; - } - p->buf = b; p->off = 0; p->cap = alloc_size; @@ -545,14 +502,9 @@ netpkt_t* netpkt_wrap(uintptr_t base, uint32_t alloc_size, uint32_t data_off, ui } netpkt_t* netpkt_view(netpkt_t* parent, uint32_t off, uint32_t len) { + if (!parent) return 0; irq_flags_t irq = irq_save_disable(); - if (!parent || !parent->buf) { - irq_restore(irq); - return 0; - } - - uint64_t end = (uint64_t)off+(uint64_t)len; - if (end > (uint64_t)parent->len) { + if (!parent->buf || off > parent->len || len > parent->len - off) { irq_restore(irq); return 0; } @@ -563,8 +515,7 @@ netpkt_t* netpkt_view(netpkt_t* parent, uint32_t off, uint32_t len) { return 0; } - uint32_t psz = (uint32_t)(((uint64_t)sizeof(netpkt_t) + 15ull) &~15ull); - netpkt_t* v = (netpkt_t*)meta_slab_alloc(&g_meta_slab_pkt, psz); + netpkt_t* v = (netpkt_t*)meta_slab_alloc(&g_meta_slab_pkt, (uint32_t)(((uint64_t)sizeof(netpkt_t) + 15ull) & ~15ull)); if (!v) { irq_restore(irq); return 0; @@ -592,6 +543,13 @@ void netpkt_ref(netpkt_t* p){ void netpkt_unref(netpkt_t* p) { if (!p) return; + + uintptr_t free_base = 0; + uint32_t free_alloc = 0; + uint32_t free_flags = 0; + netpkt_free_fn free_fn = 0; + void* free_ctx = 0; + irq_flags_t irq = irq_save_disable(); if (p->refs > 1) { p->refs--; @@ -601,43 +559,26 @@ void netpkt_unref(netpkt_t* p) { netpkt_buf_t* b = p->buf; if (b) { - if (b->refs > 1) { - b->refs--; - } else { - if (b->flags & NETPKT_BUF_F_EXTERNAL) { - if (b->free_fn) b->free_fn(b->free_ctx, b->base); - else if (b->base) release((void*)b->base); - } else if (b->flags & NETPKT_BUF_F_SMALL) { - *(void**)b->base = g_netpkt_small_free; - g_netpkt_small_free = (void*)b->base; - } else { - bool aligned = ((b->base & (PAGE_SIZE - 1)) == 0) && ((b->alloc & (PAGE_SIZE - 1)) == 0); - if (!g_spare_page && aligned) { - g_spare_page = b->base; - if (b->alloc > PAGE_SIZE) { - pfree((void*)(b->base + PAGE_SIZE), (uint64_t)b->alloc-(uint64_t)PAGE_SIZE); - uint64_t dec = (uint64_t)b->alloc-(uint64_t)PAGE_SIZE; - if (g_netpkt_page_bytes >= dec) g_netpkt_page_bytes -= dec; - else g_netpkt_page_bytes = 0; - if (g_netpkt_payload_page_bytes >= dec) g_netpkt_payload_page_bytes -= dec; - else g_netpkt_payload_page_bytes = 0; - } - } else { - if (b->base) pfree((void*)b->base, (uint64_t)b->alloc); - uint64_t dec = (uint64_t)b->alloc; - if (g_netpkt_page_bytes >= dec) g_netpkt_page_bytes -= dec; - else g_netpkt_page_bytes = 0; - if (g_netpkt_payload_page_bytes >= dec) g_netpkt_payload_page_bytes -= dec; - else g_netpkt_payload_page_bytes = 0; - } - } - + if (b->refs > 1) b->refs--; + else { + free_base = b->base; + free_alloc = b->alloc; + free_flags = b->flags; + free_fn = b->free_fn; + free_ctx = b->free_ctx; meta_slab_free(&g_meta_slab_buf, b); } } meta_slab_free(&g_meta_slab_pkt, p); irq_restore(irq); + + if (free_base) { + if (free_flags & NETPKT_BUF_F_EXTERNAL) { + if (free_fn) free_fn(free_ctx, free_base); + else release((void*)free_base); + } else netpkt_payload_free(free_base, free_alloc, free_flags); + } } uintptr_t netpkt_data(const netpkt_t* p) { @@ -677,83 +618,63 @@ uint32_t netpkt_headroom(const netpkt_t* p) { uint32_t netpkt_tailroom(const netpkt_t* p) { if (!p) return 0; - uint32_t used = p->head + p->len; - return used >= p->cap ? 0: (p->cap - used); + if (p->head > p->cap || p->len > p->cap - p->head) return 0; + return p->cap - p->head - p->len; } bool netpkt_ensure_headroom(netpkt_t* p, uint32_t need) { - if (!p) return false; + if (!p || need > NETPKT_MAX_STORAGE_BYTES) return false; irq_flags_t irq = irq_save_disable(); - if (!p->buf || (p->flags & NETPKT_F_VIEW) || need > NETPKT_MAX_ALLOC) { + if (!p->buf || (p->flags & NETPKT_F_VIEW)) { irq_restore(irq); return false; } - if (p->head >= need) { - if (p->buf->refs == 1) { - irq_restore(irq); - return true; - } - bool ok = netpkt_realloc_to(p, p->head, p->cap); + if (p->head >= need && p->buf->refs == 1) { irq_restore(irq); - return ok; + return true; } - uint32_t tail = netpkt_tailroom(p); - uint32_t add = need - p->head; - if (p->buf->refs == 1 && !(p->buf->flags & NETPKT_BUF_F_EXTERNAL) && tail >= add) { - uintptr_t old_data = netpkt_data(p); + uint32_t add = need > p->head ? need - p->head : 0; + uint32_t tail = p->head <= p->cap && p->len <= p->cap - p->head ? p->cap - p->head - p->len : 0; + if (add && p->buf->refs == 1 && !(p->buf->flags & NETPKT_BUF_F_EXTERNAL) && tail >= add) { + uintptr_t old_data = p->buf->base + (uintptr_t)p->off + (uintptr_t)p->head; if (p->len) memmove((void*)(old_data + (uintptr_t)add), (const void*)old_data, p->len); p->head += add; irq_restore(irq); return true; } - uint32_t new_head = need; + uint64_t alloc = (uint64_t)need + (uint64_t)p->len + (uint64_t)tail; + uint64_t min = (uint64_t)p->cap + (uint64_t)add; + irq_restore(irq); - uint64_t alloc = (uint64_t)new_head + (uint64_t)p->len + (uint64_t)tail; - uint64_t min = (uint64_t)p->cap + (uint64_t)(need - p->head); if (alloc < min) alloc = min; - if (alloc > (uint64_t)NETPKT_MAX_ALLOC) { - irq_restore(irq); - return false; - } - - bool ok = netpkt_realloc_to(p, new_head, (uint32_t)alloc); - irq_restore(irq); - return ok; + if (alloc > (uint64_t)NETPKT_MAX_STORAGE_BYTES) return false; + return netpkt_realloc_to(p, need, (uint32_t)alloc); } bool netpkt_ensure_tailroom(netpkt_t* p, uint32_t need) { - if (!p) return false; + if (!p || need > NETPKT_MAX_STORAGE_BYTES) return false; irq_flags_t irq = irq_save_disable(); - if (!p->buf || (p->flags & NETPKT_F_VIEW) || need > NETPKT_MAX_ALLOC) { + if (!p->buf || (p->flags & NETPKT_F_VIEW)) { irq_restore(irq); return false; } - uint32_t tail = netpkt_tailroom(p); - if (tail >= need) { - if (p->buf->refs == 1) { - irq_restore(irq); - return true; - } - bool ok = netpkt_realloc_to(p, p->head, p->cap); + uint32_t tail = p->head <= p->cap && p->len <= p->cap - p->head ? p->cap - p->head - p->len : 0; + if (tail >= need && p->buf->refs == 1) { irq_restore(irq); - return ok; + return true; } + uint32_t new_head = p->head; uint64_t alloc = (uint64_t)p->head + (uint64_t)p->len + (uint64_t)need; - uint64_t min = (uint64_t)p->cap + (uint64_t)(need - tail); - if (alloc < min) alloc = min; - if (alloc > (uint64_t)NETPKT_MAX_ALLOC) { - irq_restore(irq); - return false; - } - - bool ok = netpkt_realloc_to(p, p->head, (uint32_t)alloc); + uint64_t min = (uint64_t)p->cap + (uint64_t)(need > tail ? need - tail : 0); irq_restore(irq); - return ok; + if (alloc < min) alloc = min; + if (alloc > (uint64_t)NETPKT_MAX_STORAGE_BYTES) return false; + return netpkt_realloc_to(p, new_head, (uint32_t)alloc); } void* netpkt_push(netpkt_t* p, uint32_t bytes) { @@ -761,21 +682,36 @@ void* netpkt_push(netpkt_t* p, uint32_t bytes) { if ((p->flags & NETPKT_F_VIEW) && bytes) return 0; if (!bytes) return (void*)netpkt_data(p); + if (p->len > NETPKT_MAX_ALLOC - bytes) return 0; if (!netpkt_ensure_headroom(p, bytes)) return 0; + irq_flags_t irq = irq_save_disable(); + if (!p->buf || bytes > p->head || p->len > NETPKT_MAX_ALLOC - bytes) { + irq_restore(irq); + return 0; + } p->head -= bytes; p->len += bytes; - return (void*)(p->buf->base + (uintptr_t)p->off + (uintptr_t)p->head); + void* out = (void*)(p->buf->base + (uintptr_t)p->off + (uintptr_t)p->head); + irq_restore(irq); + return out; } void* netpkt_put(netpkt_t* p, uint32_t bytes) { if (!p) return 0; if ((p->flags & NETPKT_F_VIEW) && bytes) return 0; if (!bytes) return (void*)(netpkt_data(p) + (uintptr_t)p->len); + if (p->len > NETPKT_MAX_ALLOC - bytes) return 0; if (!netpkt_ensure_tailroom(p, bytes)) return 0; + irq_flags_t irq = irq_save_disable(); + if (!p->buf || bytes > netpkt_tailroom(p) || p->len > NETPKT_MAX_ALLOC - bytes) { + irq_restore(irq); + return 0; + } uintptr_t out = p->buf->base+(uintptr_t)p->off + (uintptr_t)p->head + (uintptr_t)p->len; p->len += bytes; + irq_restore(irq); return (void*)out; } @@ -788,33 +724,6 @@ bool netpkt_pull(netpkt_t* p, uint32_t bytes) { } p->head += bytes; p->len -= bytes; - - if ((p->flags & NETPKT_F_VIEW) || !p->buf || (p->buf->flags & NETPKT_BUF_F_EXTERNAL) || (p->buf->refs != 1) || (p->cap <= PAGE_SIZE)) { - irq_restore(irq); - return true; - } - - uint64_t need = (uint64_t)p->head+(uint64_t)p->len; - if (!need) need = 1; - uint64_t newcap64 = count_pages(need, PAGE_SIZE) * (uint64_t)PAGE_SIZE; - if (newcap64 < PAGE_SIZE) newcap64 = PAGE_SIZE; - if (newcap64 >= (uint64_t)p->cap) { - irq_restore(irq); - return true; - } - - uint32_t newcap = (uint32_t)newcap64; - uint64_t dec = (uint64_t)p->buf->alloc - (uint64_t)newcap; - if (dec && ((p->buf->base & (PAGE_SIZE - 1)) == 0)) { - pfree((void*)(p->buf->base + (uintptr_t)newcap), dec); - if (g_netpkt_page_bytes >= dec) g_netpkt_page_bytes -= dec; - else g_netpkt_page_bytes = 0; - if (g_netpkt_payload_page_bytes >= dec) g_netpkt_payload_page_bytes -= dec; - else g_netpkt_payload_page_bytes = 0; - p->buf->alloc = newcap; - p->cap = newcap; - } - irq_restore(irq); return true; } @@ -827,33 +736,6 @@ bool netpkt_trim(netpkt_t* p, uint32_t new_len) { return false; } p->len = new_len; - - if ((p->flags & NETPKT_F_VIEW) || !p->buf || (p->buf->flags & NETPKT_BUF_F_EXTERNAL) || (p->buf->refs != 1) || (p->cap <= PAGE_SIZE)) { - irq_restore(irq); - return true; - } - - uint64_t need = (uint64_t)p->head + (uint64_t)p->len; - if (!need) need = 1; - uint64_t newcap64 = count_pages(need, PAGE_SIZE) * (uint64_t)PAGE_SIZE; - if (newcap64 < PAGE_SIZE) newcap64 = PAGE_SIZE; - if (newcap64 >= (uint64_t)p->cap) { - irq_restore(irq); - return true; - } - - uint32_t newcap = (uint32_t)newcap64; - uint64_t dec = (uint64_t)p->buf->alloc - (uint64_t)newcap; - if (dec && ((p->buf->base & (PAGE_SIZE - 1)) == 0)) { - pfree((void*)(p->buf->base + (uintptr_t)newcap), dec); - if (g_netpkt_page_bytes >= dec) g_netpkt_page_bytes -= dec; - else g_netpkt_page_bytes = 0; - if (g_netpkt_payload_page_bytes >= dec) g_netpkt_payload_page_bytes -= dec; - else g_netpkt_payload_page_bytes = 0; - p->buf->alloc = newcap; - p->cap = newcap; - } - irq_restore(irq); return true; } diff --git a/kernel/networking/netpkt.h b/kernel/networking/netpkt.h index b6520318..394fa6a8 100644 --- a/kernel/networking/netpkt.h +++ b/kernel/networking/netpkt.h @@ -10,7 +10,7 @@ typedef struct netpkt netpkt_t; typedef void (*netpkt_free_fn)(void* ctx, uintptr_t base); -#define NETPKT_MAX_ALLOC 65536u +#define NETPKT_MAX_ALLOC 65535u #define NETPKT_MAX_PAGE_BYTES (32ull * 1024ull * 1024ull) netpkt_t* netpkt_alloc(uint32_t data_capacity, uint32_t headroom, uint32_t tailroom); diff --git a/kernel/networking/transport_layer/tcp/tcp_tx.c b/kernel/networking/transport_layer/tcp/tcp_tx.c index eeebf450..5bd62c5b 100644 --- a/kernel/networking/transport_layer/tcp/tcp_tx.c +++ b/kernel/networking/transport_layer/tcp/tcp_tx.c @@ -164,15 +164,12 @@ static uint64_t tcp_nagle_append(tcp_flow_t *flow, const uint8_t *payload, uint6 } if (!flow->tx.nagle_buf || flow->tx.nagle_cap < cap) { - uintptr_t nb = (uintptr_t)zalloc(cap); + uintptr_t nb = flow->tx.nagle_buf ? (uintptr_t)reallocate((void*)flow->tx.nagle_buf, cap) : (uintptr_t)zalloc(cap); if (!nb) { flow->tx.nagle_appending = 0; return 0; } - if (flow->tx.nagle_buf && flow->tx.nagle_len) memcpy((void*)nb, (const void*)flow->tx.nagle_buf, flow->tx.nagle_len); - if (flow->tx.nagle_buf) release((void*)flow->tx.nagle_buf); - flow->tx.nagle_buf = nb; flow->tx.nagle_cap = cap; } From 183a7b22ca4c710cd5a7ea6de843d724641c3463 Mon Sep 17 00:00:00 2001 From: CodeAnarchist Date: Wed, 17 Jun 2026 00:21:10 +0200 Subject: [PATCH 24/39] [net] port sockets to c, improve ping and clean up networking --- .../application_layer/csocket_http_client.c | 583 +++++++++++++ .../application_layer/csocket_http_client.cpp | 51 -- .../application_layer/csocket_http_server.c | 618 ++++++++++++++ .../application_layer/csocket_http_server.cpp | 73 -- .../application_layer/dhcp_daemon.c | 60 +- .../application_layer/dhcpv6_daemon.c | 34 +- kernel/networking/application_layer/dns/dns.c | 56 +- .../application_layer/dns/dns_daemon.c | 7 +- .../application_layer/dns/dns_mdns.c | 9 +- .../application_layer/dns/mdns_responder.c | 260 ++---- kernel/networking/application_layer/http.c | 18 +- kernel/networking/application_layer/ntp.c | 6 +- .../networking/application_layer/ntp_daemon.c | 4 +- kernel/networking/application_layer/sntp.c | 6 +- .../application_layer/sntp_daemon.c | 4 +- .../application_layer/socket_http_client.hpp | 552 ------------- .../application_layer/socket_http_server.hpp | 580 ------------- .../application_layer/ssdp_daemon.c | 18 +- kernel/networking/drivers/net_bus.cpp | 6 +- kernel/networking/interface_manager.c | 154 ++-- kernel/networking/internet_layer/icmp.c | 175 ++-- kernel/networking/internet_layer/icmp.h | 1 + kernel/networking/internet_layer/icmpv6.c | 134 ++- kernel/networking/internet_layer/icmpv6.h | 1 + kernel/networking/internet_layer/igmp.c | 25 +- kernel/networking/internet_layer/ipv4.c | 68 +- kernel/networking/internet_layer/ipv4_route.c | 30 +- kernel/networking/internet_layer/ipv4_utils.c | 41 +- kernel/networking/internet_layer/ipv4_utils.h | 4 + kernel/networking/internet_layer/ipv6.c | 16 +- kernel/networking/internet_layer/ipv6_route.c | 66 +- kernel/networking/internet_layer/ipv6_utils.c | 49 +- kernel/networking/internet_layer/ipv6_utils.h | 6 + kernel/networking/internet_layer/mld.c | 9 +- kernel/networking/link_layer/arp.c | 13 +- kernel/networking/link_layer/ndp.c | 101 +-- kernel/networking/netpkt.c | 7 - kernel/networking/processes/net_proc.c | 14 +- .../networking/transport_layer/csocket_tcp.c | 591 ++++++++++++++ .../transport_layer/csocket_tcp.cpp | 90 -- .../networking/transport_layer/csocket_udp.c | 768 ++++++++++++++++++ .../transport_layer/csocket_udp.cpp | 83 -- kernel/networking/transport_layer/socket.hpp | 151 ---- .../networking/transport_layer/socket_bind.c | 13 +- .../networking/transport_layer/socket_core.c | 90 ++ .../networking/transport_layer/socket_core.h | 2 + .../transport_layer/socket_endpoint.c | 2 +- .../networking/transport_layer/socket_tcp.hpp | 520 ------------ .../networking/transport_layer/socket_udp.hpp | 709 ---------------- .../networking/transport_layer/tcp/tcp_core.c | 7 +- .../networking/transport_layer/tcp/tcp_rx.c | 5 +- .../transport_layer/tcp/tcp_utils.c | 4 +- .../networking/transport_layer/trans_utils.h | 5 +- kernel/tools/ping.c | 251 ++++-- shared | 2 +- 55 files changed, 3382 insertions(+), 3770 deletions(-) create mode 100644 kernel/networking/application_layer/csocket_http_client.c delete mode 100644 kernel/networking/application_layer/csocket_http_client.cpp create mode 100644 kernel/networking/application_layer/csocket_http_server.c delete mode 100644 kernel/networking/application_layer/csocket_http_server.cpp delete mode 100644 kernel/networking/application_layer/socket_http_client.hpp delete mode 100644 kernel/networking/application_layer/socket_http_server.hpp create mode 100644 kernel/networking/transport_layer/csocket_tcp.c delete mode 100644 kernel/networking/transport_layer/csocket_tcp.cpp create mode 100644 kernel/networking/transport_layer/csocket_udp.c delete mode 100644 kernel/networking/transport_layer/csocket_udp.cpp delete mode 100644 kernel/networking/transport_layer/socket.hpp delete mode 100644 kernel/networking/transport_layer/socket_tcp.hpp delete mode 100644 kernel/networking/transport_layer/socket_udp.hpp diff --git a/kernel/networking/application_layer/csocket_http_client.c b/kernel/networking/application_layer/csocket_http_client.c new file mode 100644 index 00000000..b1187629 --- /dev/null +++ b/kernel/networking/application_layer/csocket_http_client.c @@ -0,0 +1,583 @@ +#include "csocket_http_client.h" +#include "console/kio.h" +#include "networking/transport_layer/csocket.h" +#include "networking/transport_layer/tcp.h" +#include "networking/net_logger/net_logger.h" +#include "http.h" +#include "std/std.h" +#include "net/socket_types.h" +#include "data/format/url.h" +#include "networking/transport_layer/trans_utils.h" +#include "networking/transport_layer/socket_endpoint.h" +#include "process/scheduler.h" +#include "alloc/allocate.h" + +typedef struct HTTPClientOrigin { + string domain; + net_l4_endpoint ep; + bool valid; +} HTTPClientOrigin; + +typedef struct HTTPClient { + socket_handle_t sock; + SocketExtraOptions log_opts; + SocketExtraOptions* tcp_extra; + HTTPClientPolicy policy; + HTTPClientOrigin origin; +} HTTPClient; + +static HTTPResponseMsg http_client_error_response(int64_t err) { + HTTPResponseMsg resp = {0}; + resp.status_code = (HttpError)err; + return resp; +} + +static HTTPResponseMsg http_client_receive_response(HTTPClient* cli, HTTPMethod request_method) { + HTTPResponseMsg resp = {0}; + string buf = (string){0}; + char tmp[512]; + + while (1) { + int hdr_end = find_crlfcrlf(buf.data, buf.length); + uint32_t start_ms = (uint32_t)get_time(); + uint32_t last_rx_ms = start_ms; + + while (hdr_end < 0) { + int64_t r = receive_from_socket(cli->sock, tmp, sizeof(tmp), NULL); + if (r == TCP_WOULDBLOCK) { + uint32_t now = (uint32_t)get_time(); + if ((now - last_rx_ms) > cli->policy.common.header_idle_timeout_ms || (now - start_ms) > cli->policy.common.header_total_timeout_ms) { + string_free(buf); + return http_client_error_response(SOCK_ERR_PROTO); + } + msleep(10); + continue; + } + if (r < 0) { + string_free(buf); + return http_client_error_response(r); + } + if (r == 0) { + string_free(buf); + return http_client_error_response(SOCK_ERR_PROTO); + } + string_append_bytes(&buf, tmp, (uint32_t)r); + last_rx_ms = (uint32_t)get_time(); + if (buf.length > cli->policy.common.max_header_bytes) { + string_free(buf); + return http_client_error_response(SOCK_ERR_PROTO); + } + hdr_end = find_crlfcrlf(buf.data, buf.length); + } + + int status_line_end = strindex((char*)buf.data, "\r\n"); + if (status_line_end <= 0) { + string_free(buf); + return http_client_error_response(SOCK_ERR_PROTO); + } + + HTTPStatusLine status_line = {0}; + if (http_parse_status_line(buf.data, status_line_end, &status_line) != HTTP_PARSE_OK) { + string_free(buf); + return http_client_error_response(SOCK_ERR_PROTO); + } + + resp.status_code = (HttpError)status_line.status_code; + if (status_line.reason_len) { + resp.reason = (string){0}; + string_append_bytes(&resp.reason, buf.data + status_line.reason_off, status_line.reason_len); + } + + HTTPParseResult header_result = http_header_parse( + (char*)buf.data + status_line_end + 2, + (uint32_t)hdr_end - (uint32_t)(status_line_end + 2), + &cli->policy.common, + &resp.headers_common, + &resp.extra_headers, + &resp.extra_header_count); + + if (header_result != HTTP_PARSE_OK) { + http_response_free(&resp); + string_free(buf); + if (cli->sock) { + close_socket(cli->sock); + cli->sock = 0; + } + return http_client_error_response(SOCK_ERR_PROTO); + } + + uint32_t body_start = (uint32_t)hdr_end + 4; + uint32_t have = buf.length > body_start ? buf.length - body_start : 0; + uint32_t code = (uint32_t)resp.status_code; + bool no_body = request_method == HTTP_METHOD_HEAD || code == 204 || code == 304; + if (code >= 100 && code < 200) { + HTTPResponseMsg info = resp; + resp = (HTTPResponseMsg){0}; + if (info.reason.mem_length) string_free(info.reason); + http_headers_common_free(&info.headers_common); + http_headers_extra_free(info.extra_headers, info.extra_header_count); + + string next = (string){0}; + if (have) string_append_bytes(&next, buf.data + body_start, have); + string_free(buf); + buf = next; + continue; + } + + if (no_body) { + } else if (resp.headers_common.framing.chunked) { + HTTPChunkedDecoder dec; + http_chunked_decoder_init(&dec, &cli->policy.common); + uint32_t used = 0; + HTTPParseResult chunk_result = have ? http_chunked_decoder_feed(&dec, buf.data + body_start, have, &used) : HTTP_PARSE_INCOMPLETE; + uint32_t body_start_ms = (uint32_t)get_time(); + uint32_t body_last_rx_ms = body_start_ms; + + while (chunk_result == HTTP_PARSE_INCOMPLETE) { + int64_t r = receive_from_socket(cli->sock, tmp, sizeof(tmp), NULL); + if (r == TCP_WOULDBLOCK) { + uint32_t now = (uint32_t)get_time(); + if ((now - body_last_rx_ms) > cli->policy.common.body_idle_timeout_ms || (now - body_start_ms) > cli->policy.common.body_total_timeout_ms) break; + msleep(1); + continue; + } + if (r <= 0) break; + chunk_result = http_chunked_decoder_feed(&dec, tmp, (uint32_t)r, &used); + body_last_rx_ms = (uint32_t)get_time(); + } + + if (chunk_result != HTTP_PARSE_OK) { + http_chunked_decoder_free(&dec); + http_response_free(&resp); + string_free(buf); + if (cli->sock) { + close_socket(cli->sock); + cli->sock = 0; + } + return http_client_error_response(SOCK_ERR_PROTO); + } + + string decoded = dec.body; + dec.body = (string){0}; + http_chunked_decoder_free(&dec); + if (decoded.length) { + resp.body = decoded; + } else if (decoded.mem_length) string_free(decoded); + } else { + uint32_t need = resp.headers_common.framing.has_content_length ? resp.headers_common.fields.content_length : 0; + + if (resp.headers_common.framing.has_content_length) { + if (need > cli->policy.common.max_body_bytes) { + http_response_free(&resp); + string_free(buf); + if (cli->sock) { + close_socket(cli->sock); + cli->sock = 0; + } + return http_client_error_response(SOCK_ERR_PROTO); + } + + if (need) { + char* body_copy = (char*)zalloc(need); + if (!body_copy) { + http_response_free(&resp); + string_free(buf); + if (cli->sock) { + close_socket(cli->sock); + cli->sock = 0; + } + return http_client_error_response(SOCK_ERR_SYS); + } + + uint32_t copied = have < need ? have : need; + if (copied) memcpy(body_copy, buf.data + body_start, copied); + + uint32_t body_start_ms = (uint32_t)get_time(); + uint32_t body_last_rx_ms = body_start_ms; + while (copied < need) { + int64_t r = receive_from_socket(cli->sock, body_copy + copied, need - copied, NULL); + if (r == TCP_WOULDBLOCK) { + uint32_t now = (uint32_t)get_time(); + if ((now - body_last_rx_ms) > cli->policy.common.body_idle_timeout_ms || (now - body_start_ms) > cli->policy.common.body_total_timeout_ms) break; + msleep(1); + continue; + } + if (r <= 0) break; + copied += (uint32_t)r; + body_last_rx_ms = (uint32_t)get_time(); + } + + if (copied < need) { + release(body_copy); + http_response_free(&resp); + string_free(buf); + if (cli->sock) { + close_socket(cli->sock); + cli->sock = 0; + } + return http_client_error_response(SOCK_ERR_PROTO); + } + + resp.body = (string){body_copy, need, need}; + } + } else if (cli->policy.allow_close_delimited) { + string body = (string){0}; + if (have) string_append_bytes(&body, buf.data + body_start, have); + + uint32_t body_start_ms = (uint32_t)get_time(); + uint32_t body_last_rx_ms = body_start_ms; + while (body.length <= cli->policy.common.max_body_bytes) { + int64_t r = receive_from_socket(cli->sock, tmp, sizeof(tmp), NULL); + if (r == TCP_WOULDBLOCK) { + uint32_t now = (uint32_t)get_time(); + if ((now - body_last_rx_ms) > cli->policy.common.body_idle_timeout_ms || (now - body_start_ms) > cli->policy.common.body_total_timeout_ms) { + string_free(body); + http_response_free(&resp); + string_free(buf); + if (cli->sock) { + close_socket(cli->sock); + cli->sock = 0; + } + return http_client_error_response(SOCK_ERR_PROTO); + } + msleep(1); + continue; + } + if (r < 0) { + string_free(body); + http_response_free(&resp); + string_free(buf); + if (cli->sock) { + close_socket(cli->sock); + cli->sock = 0; + } + return http_client_error_response(r); + } + if (r == 0) break; + if (body.length + (uint32_t)r > cli->policy.common.max_body_bytes) { + string_free(body); + http_response_free(&resp); + string_free(buf); + if (cli->sock) { + close_socket(cli->sock); + cli->sock = 0; + } + return http_client_error_response(SOCK_ERR_PROTO); + } + string_append_bytes(&body, tmp, (uint32_t)r); + body_last_rx_ms = (uint32_t)get_time(); + } + + if (body.length) { + resp.body = body; + } else if (body.mem_length) string_free(body); + } + } + + netlog_socket_event_t ev = {0}; + ev.comp = NETLOG_COMP_HTTP_CLIENT; + ev.action = NETLOG_ACT_HTTP_RECV_RESPONSE; + ev.pid = get_current_proc_pid(); + ev.u0 = (uint32_t)resp.status_code; + ev.u1 = (uint32_t)resp.body.length; + ev.local_port = get_socket_local_port(cli->sock); + net_l4_endpoint sock_remote_ep = {0}; + get_socket_remote_endpoint(cli->sock, &sock_remote_ep); + ev.remote_ep = sock_remote_ep; + netlog_socket_event(&cli->log_opts, &ev); + + string_free(buf); + return resp; + } +} + +static int32_t http_client_connect_endpoint_origin(HTTPClient* cli, const net_l4_endpoint* dst, const char* host_name) { + if (!cli->sock) cli->sock = create_socket(PROTO_TCP, cli->tcp_extra ? cli->tcp_extra : &cli->log_opts); + if (!cli->sock) return SOCK_ERR_SYS; + int32_t r = connect_socket(cli->sock, dst); + if (r >= 0) { + HTTPClientOrigin next = {0}; + next.valid = true; + if (dst) next.ep = *dst; + if (host_name) next.domain = string_from_literal(host_name); + if (cli->origin.domain.mem_length) string_free(cli->origin.domain); + cli->origin = next; + } + + netlog_socket_event_t ev = {0}; + ev.comp = NETLOG_COMP_HTTP_CLIENT; + ev.action = NETLOG_ACT_CONNECT; + ev.pid = get_current_proc_pid(); + if (dst) ev.dst_ep = *dst; + ev.i0 = r; + + if (cli->sock) { + ev.local_port = get_socket_local_port(cli->sock); + net_l4_endpoint sock_remote_ep = {0}; + get_socket_remote_endpoint(cli->sock, &sock_remote_ep); + ev.remote_ep = sock_remote_ep; + if (ev.remote_ep.ver) ev.dst_ep = ev.remote_ep; + } + + netlog_socket_event(&cli->log_opts, &ev); + return r; +} + +static int32_t http_client_connect_domain_origin(HTTPClient* cli, const char* host, uint16_t port) { + if (!host || !port) return SOCK_ERR_INVAL; + net_l4_endpoint ep = socket_endpoint_select(host, port, (ip_version_t)0, DNS_USE_BOTH, 3000); + if (!ep.ver) return SOCK_ERR_DNS; + return http_client_connect_endpoint_origin(cli, &ep, host); +} + +http_client_handle_t http_client_create(const SocketExtraOptions* extra, const HTTPClientPolicyOptions* http_options) { + HTTPClient* cli = (HTTPClient*)zalloc(sizeof(*cli)); + if (!cli) return NULL; + + cli->policy = http_client_policy_from_options(http_options); + if (extra) cli->log_opts = *extra; + + if (extra && (cli->log_opts.flags & SOCK_OPT_DEBUG)) { + cli->tcp_extra = (SocketExtraOptions*)zalloc(sizeof(SocketExtraOptions)); + if (cli->tcp_extra) { + *cli->tcp_extra = *extra; + cli->tcp_extra->flags &= ~SOCK_OPT_DEBUG; + } + } + + return cli; +} + +void http_client_destroy(http_client_handle_t h) { + if (!h) return; + HTTPClient* cli = (HTTPClient*)h; + http_client_close(cli); + release(cli); +} + +int32_t http_client_set_options(http_client_handle_t h, const HTTPClientPolicyOptions* http_options) { + if (!h) return (int32_t)SOCK_ERR_INVAL; + HTTPClient* cli = (HTTPClient*)h; + cli->policy = http_client_policy_from_options(http_options); + return SOCK_OK; +} + +int32_t http_client_connect_endpoint(http_client_handle_t h, const net_l4_endpoint* dst) { + if (!h || !dst) return (int32_t)SOCK_ERR_INVAL; + HTTPClient* cli = (HTTPClient*)h; + return http_client_connect_endpoint_origin(cli, dst, NULL); +} + +int32_t http_client_connect_domain(http_client_handle_t h, const char* host, uint16_t port) { + if (!h || !host) return (int32_t)SOCK_ERR_INVAL; + HTTPClient* cli = (HTTPClient*)h; + return http_client_connect_domain_origin(cli, host, port); +} + +HTTPResponseMsg http_client_send_request(http_client_handle_t h, const HTTPRequestMsg* req) { + HTTPResponseMsg empty = {0}; + if (!h || !req) { + empty.status_code = (HttpError)SOCK_ERR_INVAL; + return empty; + } + + HTTPClient* cli = (HTTPClient*)h; + HTTPRequestMsg curr = *req; + curr.path = (string){0}; + if (req->path.data && req->path.length) string_append_bytes(&curr.path, req->path.data, req->path.length); + curr.headers_common.fields.host = (string){0}; + if (req->headers_common.fields.host.data && req->headers_common.fields.host.length) string_append_bytes(&curr.headers_common.fields.host, req->headers_common.fields.host.data, req->headers_common.fields.host.length); + + HTTPResponseMsg resp = {0}; + uint32_t redirects = cli->policy.follow_redirects ? cli->policy.max_redirects : 0; + for (uint32_t i = 0;; i++) { + if (!cli->sock) { + resp = http_client_error_response(SOCK_ERR_STATE); + break; + } + + if (!curr.host_override && curr.version != HTTP_VERSION_10 && !curr.headers_common.fields.host.length && cli->origin.valid) { + if (cli->origin.domain.data) { + uint16_t host_port = cli->origin.ep.port; + bool ipv6 = str_has_char(cli->origin.domain.data, cli->origin.domain.length, ':') >= 0; + if (host_port && host_port != 80) { + if (ipv6) curr.headers_common.fields.host = string_format("[%.*s]:%i", (int)cli->origin.domain.length, cli->origin.domain.data, (int)host_port); + else curr.headers_common.fields.host = string_format("%.*s:%i", (int)cli->origin.domain.length, cli->origin.domain.data, (int)host_port); + } else { + if (ipv6) curr.headers_common.fields.host = string_format("[%.*s]", (int)cli->origin.domain.length, cli->origin.domain.data); + else curr.headers_common.fields.host = string_from_literal_length(cli->origin.domain.data, cli->origin.domain.length); + } + } else if (cli->origin.ep.ver) { + char ip[48]; + bool ipv6 = false; + uint16_t ep_port = 0; + net_ep_split(&cli->origin.ep, ip, sizeof(ip), &ipv6, &ep_port); + uint16_t host_port = ep_port; + if (host_port && host_port != 80) { + if (ipv6) curr.headers_common.fields.host = string_format("[%s]:%i", ip, (int)host_port); + else curr.headers_common.fields.host = string_format("%s:%i", ip, (int)host_port); + } else { + if (ipv6) curr.headers_common.fields.host = string_format("[%s]", ip); + else curr.headers_common.fields.host = string_from_literal(ip); + } + } + } + + string out = http_request_builder(&curr); + uint32_t out_len = out.length; + + uint32_t off = 0; + int64_t sent = 0; + while (off < out_len) { + int64_t r = send_on_socket(cli->sock, (void*)(out.data + off), out_len - off); + if (r == TCP_WOULDBLOCK) { + msleep(5); + continue; + } + if (r < 0) { + sent = r; + break; + } + off += (uint32_t)r; + } + if (sent >= 0) sent = (int64_t)off; + + netlog_socket_event_t ev = {0}; + ev.comp = NETLOG_COMP_HTTP_CLIENT; + ev.action = NETLOG_ACT_HTTP_SEND_REQUEST; + ev.pid = get_current_proc_pid(); + ev.u0 = out_len; + ev.i0 = sent; + ev.local_port = get_socket_local_port(cli->sock); + net_l4_endpoint sock_remote_ep = {0}; + get_socket_remote_endpoint(cli->sock, &sock_remote_ep); + ev.remote_ep = sock_remote_ep; + + char pathbuf[128]; + if (curr.path.length && curr.path.data) { + uint32_t n = curr.path.length; + if (n > sizeof(pathbuf) - 1) n = sizeof(pathbuf) - 1; + memcpy(pathbuf, curr.path.data, n); + pathbuf[n] = 0; + ev.s0 = pathbuf; + } + + netlog_socket_event(&cli->log_opts, &ev); + string_free(out); + + if (sent < 0) { + resp = http_client_error_response(sent); + break; + } + + resp = http_client_receive_response(cli, curr.method); + uint32_t code = (uint32_t)resp.status_code; + bool redirect = code == HTTP_MOVED_PERMANENTLY || code == HTTP_FOUND || code == HTTP_SEE_OTHER || code == HTTP_TEMPORARY_REDIRECT || code == HTTP_PERMANENT_REDIRECT; + if (!redirect || i >= redirects || !resp.headers_common.fields.location.length) break; + + ParsedURL url = parse_url(resp.headers_common.fields.location.data, resp.headers_common.fields.location.length); + if (!url.ok) break; + + bool absolute = url.scheme.size || url.host.size; + uint16_t next_port = cli->origin.ep.port; + if (url.scheme.size) { + if (url.scheme.size == 5 && strncmp_case((const char*)url.scheme.ptr, "https", true, 5) == 0) break; + if (!(url.scheme.size == 4 && strncmp_case((const char*)url.scheme.ptr, "http", true, 4) == 0)) break; + } + if (absolute && (!url.host.ptr || !url.host.size)) break; + + string next_path = url_request_path(&url, &curr.path); + if (!next_path.length) break; + + if (curr.path.mem_length) string_free(curr.path); + curr.path = next_path; + + if (absolute) { + next_port = url.port ? url.port : 80; + string next_domain = string_from_literal_length((const char*)url.host.ptr, url.host.size); + if (!curr.host_override) { + if (curr.headers_common.fields.host.mem_length) string_free(curr.headers_common.fields.host); + curr.headers_common.fields.host = (string){0}; + } + + if (cli->sock) { + close_socket(cli->sock); + cli->sock = 0; + } + cli->sock = next_domain.data ? create_socket(PROTO_TCP, cli->tcp_extra ? cli->tcp_extra : &cli->log_opts) : 0; + int32_t rr = cli->sock ? SOCK_OK : SOCK_ERR_SYS; + if (rr >= 0) rr = http_client_connect_domain_origin(cli, next_domain.data, next_port); + if (next_domain.mem_length) string_free(next_domain); + if (rr < 0) { + http_response_free(&resp); + resp = http_client_error_response(rr); + break; + } + } else { + net_l4_endpoint reconnect_ep = cli->origin.ep; + string reconnect_domain = {0}; + if (cli->origin.domain.data) reconnect_domain = string_from_literal(cli->origin.domain.data); + + if (cli->sock) { + close_socket(cli->sock); + cli->sock = 0; + } + cli->sock = cli->origin.valid ? create_socket(PROTO_TCP, cli->tcp_extra ? cli->tcp_extra : &cli->log_opts) : 0; + int32_t rr = cli->sock ? SOCK_OK : SOCK_ERR_SYS; + if (rr >= 0) rr = reconnect_domain.data ? http_client_connect_domain_origin(cli, reconnect_domain.data, reconnect_ep.port) : http_client_connect_endpoint_origin(cli, &reconnect_ep, NULL); + if (reconnect_domain.mem_length) string_free(reconnect_domain); + if (rr < 0) { + http_response_free(&resp); + resp = http_client_error_response(rr); + break; + } + } + + if (code == HTTP_SEE_OTHER && curr.method != HTTP_METHOD_HEAD) { + curr.method = HTTP_METHOD_GET; + curr.body = (string){0}; + curr.headers_common.framing.chunked = 0; + curr.headers_common.framing.has_content_length = 0; + curr.headers_common.fields.content_length = 0; + } + + http_response_free(&resp); + } + + if (curr.path.mem_length) string_free(curr.path); + if (curr.headers_common.fields.host.mem_length) string_free(curr.headers_common.fields.host); + return resp; +} + +int32_t http_client_close(http_client_handle_t h) { + if (!h) return (int32_t)SOCK_ERR_INVAL; + + HTTPClient* cli = (HTTPClient*)h; + netlog_socket_event_t ev = {0}; + ev.comp = NETLOG_COMP_HTTP_CLIENT; + ev.action = NETLOG_ACT_CLOSE; + ev.pid = get_current_proc_pid(); + + if (cli->sock) { + ev.local_port = get_socket_local_port(cli->sock); + net_l4_endpoint sock_remote_ep = {0}; + get_socket_remote_endpoint(cli->sock, &sock_remote_ep); + ev.remote_ep = sock_remote_ep; + } + + int32_t r = SOCK_ERR_STATE; + if (cli->sock) { + r = close_socket(cli->sock); + cli->sock = 0; + } + ev.i0 = r; + netlog_socket_event(&cli->log_opts, &ev); + + if (cli->tcp_extra) release(cli->tcp_extra); + cli->tcp_extra = NULL; + + if (cli->origin.domain.mem_length) string_free(cli->origin.domain); + cli->origin = (HTTPClientOrigin){0}; + cli->log_opts.flags &= ~SOCK_OPT_DEBUG; + return r; +} diff --git a/kernel/networking/application_layer/csocket_http_client.cpp b/kernel/networking/application_layer/csocket_http_client.cpp deleted file mode 100644 index d40aca95..00000000 --- a/kernel/networking/application_layer/csocket_http_client.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "csocket_http_client.h" -#include "socket_http_client.hpp" - -extern "C" { - -http_client_handle_t http_client_create(const SocketExtraOptions* extra, const HTTPClientPolicyOptions *options) { - HTTPClient* cli = new HTTPClient(extra, options); - if (!cli) return nullptr; - return reinterpret_cast(cli); -} - -void http_client_destroy(http_client_handle_t h) { - if (!h) return; - HTTPClient* cli = reinterpret_cast(h); - delete cli; -} - -int32_t http_client_set_options(http_client_handle_t h, const HTTPClientPolicyOptions *options) { - if (!h) return (int32_t)SOCK_ERR_INVAL; - HTTPClient* cli = reinterpret_cast(h); - return cli->set_options(options); -} - -int32_t http_client_connect_endpoint(http_client_handle_t h, const net_l4_endpoint* dst) { - if (!h || !dst) return (int32_t)SOCK_ERR_INVAL; - HTTPClient *cli = reinterpret_cast(h); - return cli->connect_endpoint(dst); -} - -int32_t http_client_connect_domain(http_client_handle_t h, const char* host, uint16_t port) { - if (!h || !host) return (int32_t)SOCK_ERR_INVAL; - HTTPClient *cli = reinterpret_cast(h); - return cli->connect_domain(host, port); -} - -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; - } - HTTPClient *cli = reinterpret_cast(h); - return cli->send_request(*req); -} - -int32_t http_client_close(http_client_handle_t h) { - if (!h) return (int32_t)SOCK_ERR_INVAL; - HTTPClient *cli = reinterpret_cast(h); - return cli->close(); -} -} diff --git a/kernel/networking/application_layer/csocket_http_server.c b/kernel/networking/application_layer/csocket_http_server.c new file mode 100644 index 00000000..a7e51b76 --- /dev/null +++ b/kernel/networking/application_layer/csocket_http_server.c @@ -0,0 +1,618 @@ +#include "csocket_http_server.h" +#include "console/kio.h" +#include "networking/transport_layer/csocket.h" +#include "networking/transport_layer/tcp.h" +#include "networking/net_logger/net_logger.h" +#include "http.h" +#include "std/std.h" +#include "net/socket_types.h" +#include "syscalls/syscalls.h" +#include "process/scheduler.h" +#include "alloc/allocate.h" + +typedef struct HTTPConnection { + socket_handle_t client; + string carry_buf; + uint32_t request_count; + HTTPMethod current_method; + bool close_after_response; + bool send_keep_alive_header; +} HTTPConnection; + +typedef struct HTTPServer { + socket_handle_t sock; + SocketExtraOptions log_opts; + SocketExtraOptions* tcp_extra; + HTTPServerPolicy policy; +} HTTPServer; + +http_server_handle_t http_server_create(const SocketExtraOptions* extra, const HTTPServerPolicyOptions* http_options) { + HTTPServer* srv = (HTTPServer*)zalloc(sizeof(*srv)); + if (!srv) return NULL; + + srv->policy = http_server_policy_from_options(http_options); + if (extra) srv->log_opts = *extra; + + if (extra && (srv->log_opts.flags & SOCK_OPT_DEBUG)) { + srv->tcp_extra = (SocketExtraOptions*)zalloc(sizeof(SocketExtraOptions)); + if (srv->tcp_extra) { + *srv->tcp_extra = *extra; + srv->tcp_extra->flags &= ~SOCK_OPT_DEBUG; + } + } + + return srv; +} + +void http_server_destroy(http_server_handle_t h) { + if (!h) return; + HTTPServer* srv = (HTTPServer*)h; + http_server_close(srv); + release(srv); +} + +int32_t http_server_set_options(http_server_handle_t h, const HTTPServerPolicyOptions* http_options) { + if (!h) return (int32_t)SOCK_ERR_INVAL; + HTTPServer* srv = (HTTPServer*)h; + srv->policy = http_server_policy_from_options(http_options); + return SOCK_OK; +} + +int32_t http_server_bind(http_server_handle_t h, const SockBindSpec* spec, uint16_t port) { + if (!h || !spec) return (int32_t)SOCK_ERR_INVAL; + + HTTPServer* srv = (HTTPServer*)h; + uint16_t p = port; + if (!srv->sock) srv->sock = create_socket(PROTO_TCP, srv->tcp_extra ? srv->tcp_extra : &srv->log_opts); + if (!srv->sock) return SOCK_ERR_SYS; + int32_t r = bind_socket(srv->sock, spec, p); + + netlog_socket_event_t ev = {0}; + ev.comp = NETLOG_COMP_HTTP_SERVER; + ev.action = NETLOG_ACT_BIND; + ev.pid = get_current_proc_pid(); + ev.u0 = p; + ev.i0 = r; + netlog_socket_event(&srv->log_opts, &ev); + return r; +} + +int32_t http_server_listen(http_server_handle_t h, int backlog) { + if (!h) return (int32_t)SOCK_ERR_INVAL; + + HTTPServer* srv = (HTTPServer*)h; + int b = backlog; + int32_t r = srv->sock ? listen_on(srv->sock, b) : SOCK_ERR_STATE; + + netlog_socket_event_t ev = {0}; + ev.comp = NETLOG_COMP_HTTP_SERVER; + ev.action = NETLOG_ACT_LISTEN; + ev.pid = get_current_proc_pid(); + ev.u0 = (uint32_t)b; + ev.i0 = r; + netlog_socket_event(&srv->log_opts, &ev); + return r; +} + +http_connection_handle_t http_server_accept(http_server_handle_t h) { + if (!h) return NULL; + + HTTPServer* srv = (HTTPServer*)h; + if (!srv->sock) return NULL; + socket_handle_t child = accept_on_socket(srv->sock); + if (!child) return NULL; + + HTTPConnection* conn = (HTTPConnection*)zalloc(sizeof(HTTPConnection)); + if (!conn) { + close_socket(child); + return NULL; + } + + conn->client = child; + conn->carry_buf = (string){0}; + conn->request_count = 0; + conn->current_method = HTTP_METHOD_GET; + conn->close_after_response = true; + conn->send_keep_alive_header = false; + + netlog_socket_event_t ev = {0}; + ev.comp = NETLOG_COMP_HTTP_SERVER; + ev.action = NETLOG_ACT_ACCEPT; + ev.pid = get_current_proc_pid(); + ev.i0 = (int64_t)child; + ev.local_port = get_socket_local_port(child); + net_l4_endpoint child_remote_ep = {0}; + get_socket_remote_endpoint(child, &child_remote_ep); + ev.remote_ep = child_remote_ep; + netlog_socket_event(&srv->log_opts, &ev); + return conn; +} + +HTTPRequestMsg http_server_recv_request(http_server_handle_t h, http_connection_handle_t c) { + HTTPRequestMsg req = {0}; + if (!h || !c) return req; + + HTTPServer* srv = (HTTPServer*)h; + HTTPConnection* conn = (HTTPConnection*)c; + if (!conn->client) return req; + + string buf = (string){0}; + if (conn->carry_buf.length) { + string_append_bytes(&buf, conn->carry_buf.data, conn->carry_buf.length); + string_free(conn->carry_buf); + conn->carry_buf = (string){0}; + } + + char tmp[2048]; + int hdr_end = find_crlfcrlf(buf.data, buf.length); + uint32_t consumed = 0; + uint32_t start_ms = (uint32_t)get_time(); + uint32_t last_rx_ms = start_ms; + bool bad_request = false; + HttpError reject_status = HTTP_BAD_REQUEST; + char* body_copy = NULL; + + while (hdr_end < 0) { + int64_t r = receive_from_socket(conn->client, tmp, sizeof(tmp), NULL); + if (r == TCP_WOULDBLOCK) { + uint32_t now = (uint32_t)get_time(); + if ((uint32_t)(now - last_rx_ms) > srv->policy.common.header_idle_timeout_ms || (uint32_t)(now - start_ms) > srv->policy.common.header_total_timeout_ms) { + string_free(buf); + return req; + } + msleep(10); + continue; + } + if (r <= 0) { + string_free(buf); + return req; + } + string_append_bytes(&buf, tmp, (uint32_t)r); + last_rx_ms = (uint32_t)get_time(); + if (buf.length > srv->policy.common.max_header_bytes) { + bad_request = true; + reject_status = HTTP_HEADER_FIELDS_TOO_LARGE; + break; + } + hdr_end = find_crlfcrlf(buf.data, buf.length); + } + + if (!bad_request) { + uint32_t line_end = 0; + while (line_end + 1u < (uint32_t)hdr_end) { + if (buf.data[line_end] == '\r' && buf.data[line_end + 1] == '\n') break; + line_end++; + } + + if (line_end > srv->policy.common.max_start_line) { + bad_request = true; + reject_status = HTTP_URI_TOO_LONG; + } + + HTTPRequestLine line = {0}; + HTTPParseResult line_result = HTTP_PARSE_OK; + if (!bad_request) { + line_result = http_parse_request_line(buf.data, line_end, &line); + if (line_result != HTTP_PARSE_OK) { + bad_request = true; + reject_status = http_parse_result_status(line_result); + } else if (!http_method_allowed(srv->policy.allowed_methods, line.method)) { + bad_request = true; + reject_status = HTTP_METHOD_NOT_ALLOWED; + } + } + + if (!bad_request) { + const char* target = buf.data + line.target_off; + uint32_t target_len = line.target_len; + req.method = line.method; + req.version = line.version; + req.path = (string){0}; + + if (target_len >= 7 && memcmp(target, "http://", 7) == 0) { + if (!srv->policy.allow_absolute_uri) { + bad_request = true; + reject_status = HTTP_BAD_REQUEST; + } else { + uint32_t k = 7; + while (k < target_len && target[k] != '/') k++; + if (k < target_len) string_append_bytes(&req.path, target + k, target_len - k); + else string_append_bytes(&req.path, "/", 1); + } + } else if (target_len >= 8 && memcmp(target, "https://", 8) == 0) { + if (!srv->policy.allow_absolute_uri) { + bad_request = true; + reject_status = HTTP_BAD_REQUEST; + } else { + uint32_t k = 8; + while (k < target_len && target[k] != '/') k++; + if (k < target_len) string_append_bytes(&req.path, target + k, target_len - k); + else string_append_bytes(&req.path, "/", 1); + } + } else string_append_bytes(&req.path, target, target_len); + + if (!bad_request && (!req.path.length || req.path.length > srv->policy.common.max_path_len)) { + bad_request = true; + reject_status = req.path.length > srv->policy.common.max_path_len ? HTTP_URI_TOO_LONG : HTTP_BAD_REQUEST; + } + } + + if (!bad_request) { + HTTPParseResult header_result = http_header_parse( + (char*)buf.data + line_end + 2, + (uint32_t)hdr_end - (line_end + 2), + &srv->policy.common, + &req.headers_common, + &req.extra_headers, + &req.extra_header_count); + + if (header_result != HTTP_PARSE_OK) { + bad_request = true; + reject_status = http_parse_result_status(header_result); + } else if (srv->policy.require_host_http11 && line.version == HTTP_VERSION_11 && !req.headers_common.fields.host.length) { + bad_request = true; + reject_status = HTTP_BAD_REQUEST; + } else { + conn->request_count++; + conn->current_method = req.method; + conn->close_after_response = true; + conn->send_keep_alive_header = false; + if (srv->policy.allow_keep_alive) { + if (line.version == HTTP_VERSION_11) conn->close_after_response = req.headers_common.framing.connection_close != 0; + else if (line.version == HTTP_VERSION_10 && req.headers_common.framing.connection_keep_alive) { + conn->close_after_response = false; + conn->send_keep_alive_header = true; + } + } + if (srv->policy.max_keepalive_requests && conn->request_count >= srv->policy.max_keepalive_requests) conn->close_after_response = true; + } + } + + uint32_t body_start = hdr_end + 4; + uint32_t have = buf.length > body_start ? buf.length - body_start : 0; + + if (!bad_request && req.headers_common.framing.expect_continue) { + if (req.headers_common.framing.has_content_length && req.headers_common.fields.content_length > srv->policy.common.max_body_bytes) { + bad_request = true; + reject_status = HTTP_PAYLOAD_TOO_LARGE; + } else { + HTTPResponseMsg cont = {0}; + cont.status_code = HTTP_CONTINUE; + http_server_send_response(srv, conn, &cont); + } + } + uint32_t need = req.headers_common.framing.has_content_length ? req.headers_common.fields.content_length : 0; + consumed = body_start; + + if (!bad_request && req.headers_common.framing.chunked) { + HTTPChunkedDecoder dec; + http_chunked_decoder_init(&dec, &srv->policy.common); + + uint32_t used = 0; + HTTPParseResult chunk_result = have ? http_chunked_decoder_feed(&dec, buf.data + body_start, have, &used) : HTTP_PARSE_INCOMPLETE; + uint32_t body_start_ms = (uint32_t)get_time(); + uint32_t body_last_rx_ms = body_start_ms; + consumed = body_start + used; + + while (chunk_result == HTTP_PARSE_INCOMPLETE) { + int64_t r = receive_from_socket(conn->client, tmp, sizeof(tmp), NULL); + if (r == TCP_WOULDBLOCK) { + uint32_t now = (uint32_t)get_time(); + if ((now - body_last_rx_ms) > srv->policy.common.body_idle_timeout_ms || (now - body_start_ms) > srv->policy.common.body_total_timeout_ms) break; + msleep(2); + continue; + } + if (r <= 0) break; + used = 0; + chunk_result = http_chunked_decoder_feed(&dec, tmp, (uint32_t)r, &used); + body_last_rx_ms = (uint32_t)get_time(); + consumed = buf.length; + if (chunk_result == HTTP_PARSE_OK && used < (uint32_t)r && !conn->carry_buf.length) { + conn->carry_buf = (string){0}; + string_append_bytes(&conn->carry_buf, tmp + used, (uint32_t)r - used); + } + } + + if (chunk_result != HTTP_PARSE_OK) { + bad_request = true; + reject_status = http_parse_result_status(chunk_result); + } else { + string decoded = dec.body; + dec.body = (string){0}; + if (decoded.length) { + req.body = decoded; + } else if (decoded.mem_length) string_free(decoded); + } + http_chunked_decoder_free(&dec); + } else { + if (!bad_request && need > srv->policy.common.max_body_bytes) { + bad_request = true; + reject_status = HTTP_PAYLOAD_TOO_LARGE; + } + + if (!bad_request && need > 0) { + body_copy = (char*)zalloc(need); + if (!body_copy) { + bad_request = true; + reject_status = HTTP_INTERNAL_SERVER_ERROR; + } else { + uint32_t copied = have < need ? have : need; + if (copied) memcpy(body_copy, buf.data + body_start, copied); + + uint32_t body_start_ms = (uint32_t)get_time(); + uint32_t body_last_rx_ms = body_start_ms; + while (copied < need) { + int64_t r = receive_from_socket(conn->client, body_copy + copied, need - copied, NULL); + if (r == TCP_WOULDBLOCK) { + uint32_t now = (uint32_t)get_time(); + if ((now - body_last_rx_ms) > srv->policy.common.body_idle_timeout_ms || (now - body_start_ms) > srv->policy.common.body_total_timeout_ms) { + bad_request = true; + break; + } + msleep(2); + continue; + } + if (r <= 0) { + bad_request = true; + break; + } + copied += (uint32_t)r; + body_last_rx_ms = (uint32_t)get_time(); + } + } + } + + if (!bad_request) consumed = body_start + need; + if (body_copy && !bad_request) { + req.body = (string){body_copy, need, need}; + } + } + } + + if (bad_request) { + if (body_copy) release(body_copy); + const char* reason = http_status_reason(reject_status); + string body = srv->policy.send_error_body ? string_format("%s\n", reason) : (string){0}; + HTTPResponseMsg res = {0}; + HTTPHeader allow_header = {0}; + string allow_value = {0}; + static char allow_key[] = "Allow"; + + res.status_code = reject_status; + res.headers_common.fields.content_length = body.length; + res.headers_common.framing.has_content_length = 1; + if (srv->policy.error_content_type) res.headers_common.fields.content_type = string_from_literal(srv->policy.error_content_type); + res.body = body; + + if (reject_status == HTTP_METHOD_NOT_ALLOWED) { + allow_value = http_methods_allow_header(srv->policy.allowed_methods); + allow_header.key = (string){allow_key, sizeof(allow_key) - 1, 0}; + allow_header.value = allow_value; + res.extra_headers = &allow_header; + res.extra_header_count = 1; + } + + conn->close_after_response = true; + http_server_send_response(srv, conn, &res); + if (conn->client) { + close_socket(conn->client); + conn->client = 0; + } + + http_request_free(&req); + http_headers_common_free(&res.headers_common); + if (allow_value.mem_length) string_free(allow_value); + if (body.mem_length) string_free(body); + string_free(buf); + return (HTTPRequestMsg){0}; + } + + netlog_socket_event_t ev = {0}; + ev.comp = NETLOG_COMP_HTTP_SERVER; + ev.action = NETLOG_ACT_HTTP_RECV_REQUEST; + ev.pid = get_current_proc_pid(); + ev.u0 = (uint32_t)req.method; + ev.u1 = (uint32_t)req.path.length; + ev.i0 = (int64_t)req.body.length; + ev.local_port = get_socket_local_port(conn->client); + net_l4_endpoint conn_remote_ep = {0}; + get_socket_remote_endpoint(conn->client, &conn_remote_ep); + ev.remote_ep = conn_remote_ep; + + char pathbuf[128]; + if (req.path.length && req.path.data) { + uint32_t n = req.path.length; + if (n > sizeof(pathbuf) - 1) n = sizeof(pathbuf) - 1; + memcpy(pathbuf, req.path.data, n); + pathbuf[n] = 0; + ev.s0 = pathbuf; + } + + netlog_socket_event(&srv->log_opts, &ev); + + if (consumed < buf.length && !conn->carry_buf.length) { + conn->carry_buf = (string){0}; + string_append_bytes(&conn->carry_buf, buf.data + consumed, buf.length - consumed); + } + + string_free(buf); + return req; +} + +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 = (HTTPServer*)h; + HTTPConnection* conn = (HTTPConnection*)c; + if (!conn->client) return SOCK_ERR_STATE; + + uint32_t code = (uint32_t)res->status_code; + bool informational = code >= 100 && code < 200; + bool suppress_body = informational || conn->current_method == HTTP_METHOD_HEAD || code == 204 || code == 304; + bool send_chunked = suppress_body ? false : res->headers_common.framing.chunked; + HTTPResponseMsg head = *res; + bool explicit_close = http_header_value_has_token(res->headers_common.fields.connection.data, res->headers_common.fields.connection.length, "close", 5); + bool explicit_keep_alive = http_header_value_has_token(res->headers_common.fields.connection.data, res->headers_common.fields.connection.length, "keep-alive", 10); + bool close_after_send = !informational && (conn->close_after_response || explicit_close); + static char conn_close_data[] = "close"; + static char conn_keep_alive_data[] = "keep-alive"; + const string conn_close = {conn_close_data, sizeof(conn_close_data) - 1, 0}; + const string conn_keep_alive = {conn_keep_alive_data, sizeof(conn_keep_alive_data) - 1, 0}; + + if (!informational) { + if (close_after_send) head.headers_common.fields.connection = conn_close; + else if (!res->headers_common.fields.connection.length && conn->send_keep_alive_header) head.headers_common.fields.connection = conn_keep_alive; + else if (explicit_keep_alive) head.headers_common.fields.connection = res->headers_common.fields.connection; + } + + if (suppress_body) { + head.body = (string){0}; + head.headers_common.framing.chunked = 0; + } + + uint32_t body_len = (!send_chunked && !suppress_body && res->body.data && res->body.length) ? (uint32_t)res->body.length : 0; + if (!send_chunked) { + head.body = (string){0}; + if (body_len && !head.headers_common.framing.has_content_length) { + head.headers_common.fields.content_length = body_len; + head.headers_common.framing.has_content_length = 1; + } + } + string out = http_response_builder(&head); + uint32_t out_len = out.length + body_len; + int64_t sent = 0; + uint32_t start_ms = (uint32_t)get_time(); + uint32_t progress_ms = start_ms; + const uint8_t* first_ptr = (const uint8_t*)out.data; + uint32_t first_len = out.length; + uint32_t first_body_len = 0; + uint32_t body_off = 0; + uint8_t* combo = NULL; + + if (body_len && out.length < 1460) { + first_body_len = body_len; + uint32_t room = 1460 - out.length; + if (first_body_len > room) first_body_len = room; + + if (first_body_len) { + combo = (uint8_t*)zalloc(out.length + first_body_len); + if (combo) { + memcpy(combo, out.data, out.length); + memcpy(combo + out.length, (const void*)res->body.data, first_body_len); + + first_ptr = combo; + first_len = out.length + first_body_len; + } else first_body_len = 0; + } + } + + uint32_t off = 0; + while (sent >= 0 && off < first_len) { + int64_t r = send_on_socket(conn->client, (void*)(first_ptr + off), first_len - off); + uint32_t now = (uint32_t)get_time(); + + if (r == TCP_WOULDBLOCK || r == 0) { + if ((now - progress_ms) > 3000 || (now - start_ms) > 30000) { + sent = SOCK_ERR_SYS; + break; + } + msleep(2); + continue; + } + if (r < 0) { + sent = r; + break; + } + off += (uint32_t)r; + progress_ms = now; + } + + if (sent >= 0) body_off = first_body_len; + if (sent >= 0 && body_len) { + const uint8_t* body = (const uint8_t*)res->body.data; + + while (body_off < body_len) { + uint32_t ask = body_len - body_off; + if (ask > 16384) ask = 16384; + int64_t r = send_on_socket(conn->client, (void*)(body + body_off), ask); + uint32_t now = (uint32_t)get_time(); + + if (r == TCP_WOULDBLOCK || r == 0) { + if ((now - progress_ms) > 3000 || (now - start_ms) > 30000) { + sent = SOCK_ERR_SYS; + break; + } + msleep(2); + continue; + } + if (r < 0) { + sent = r; + break; + } + body_off += (uint32_t)r; + progress_ms = now; + } + } + + if (sent >= 0) sent = (int64_t)out.length + body_off; + if (combo) release(combo); + + netlog_socket_event_t ev = {0}; + ev.comp = NETLOG_COMP_HTTP_SERVER; + ev.action = NETLOG_ACT_HTTP_SEND_RESPONSE; + ev.pid = get_current_proc_pid(); + ev.u0 = code; + ev.u1 = out_len; + ev.i0 = sent; + ev.local_port = get_socket_local_port(conn->client); + net_l4_endpoint conn_remote_ep = {0}; + get_socket_remote_endpoint(conn->client, &conn_remote_ep); + ev.remote_ep = conn_remote_ep; + netlog_socket_event(&srv->log_opts, &ev); + + string_free(out); + if (sent >= 0 && close_after_send && conn->client) { + close_socket(conn->client); + conn->client = 0; + } + return sent < 0 ? (int32_t)sent : SOCK_OK; +} + +int32_t http_connection_close(http_connection_handle_t c) { + if (!c) return (int32_t)SOCK_ERR_INVAL; + HTTPConnection* conn = (HTTPConnection*)c; + if (conn->carry_buf.mem_length) string_free(conn->carry_buf); + if (conn->client) close_socket(conn->client); + release(conn); + return (int32_t)SOCK_OK; +} + +int32_t http_server_close(http_server_handle_t h) { + if (!h) return (int32_t)SOCK_ERR_INVAL; + + HTTPServer* srv = (HTTPServer*)h; + int32_t r = srv->sock ? SOCK_OK : SOCK_ERR_STATE; + netlog_socket_event_t ev = {0}; + ev.comp = NETLOG_COMP_HTTP_SERVER; + ev.action = NETLOG_ACT_CLOSE; + ev.pid = get_current_proc_pid(); + ev.i0 = r; + if (srv->sock) { + ev.local_port = get_socket_local_port(srv->sock); + net_l4_endpoint sock_remote_ep = {0}; + get_socket_remote_endpoint(srv->sock, &sock_remote_ep); + ev.remote_ep = sock_remote_ep; + } + netlog_socket_event(&srv->log_opts, &ev); + + if (srv->sock) { + r = close_socket(srv->sock); + srv->sock = 0; + } + + if (srv->tcp_extra) release(srv->tcp_extra); + srv->tcp_extra = NULL; + + srv->log_opts.flags &= ~SOCK_OPT_DEBUG; + return r; +} diff --git a/kernel/networking/application_layer/csocket_http_server.cpp b/kernel/networking/application_layer/csocket_http_server.cpp deleted file mode 100644 index 4d881129..00000000 --- a/kernel/networking/application_layer/csocket_http_server.cpp +++ /dev/null @@ -1,73 +0,0 @@ -#include "csocket_http_server.h" -#include "socket_http_server.hpp" - -extern "C" { - -http_server_handle_t http_server_create(const SocketExtraOptions* extra, const HTTPServerPolicyOptions *options) { - HTTPServer* srv = new HTTPServer(extra, options); - if (!srv) return nullptr; - return reinterpret_cast(srv); -} - -void http_server_destroy(http_server_handle_t h) { - if (!h) return; - HTTPServer* srv = reinterpret_cast(h); - delete srv; -} - -int32_t http_server_set_options(http_server_handle_t h, const HTTPServerPolicyOptions *options) { - if (!h) return (int32_t)SOCK_ERR_INVAL; - HTTPServer* srv = reinterpret_cast(h); - return srv->set_options(options); -} - -int32_t http_server_bind(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(*spec, port); -} - -int32_t http_server_listen(http_server_handle_t h, int backlog) { - if (!h) return (int32_t)SOCK_ERR_INVAL; - HTTPServer* srv = reinterpret_cast(h); - return srv->listen(backlog); -} - -http_connection_handle_t http_server_accept(http_server_handle_t h) { - if (!h) return nullptr; - HTTPServer* srv = reinterpret_cast(h); - HTTPConnection* conn = srv->accept(); - return reinterpret_cast(conn); -} - -HTTPRequestMsg http_server_recv_request(http_server_handle_t h, http_connection_handle_t c) { - HTTPRequestMsg empty{}; - if (!h || !c) return empty; - HTTPServer* srv = reinterpret_cast(h); - HTTPConnection* 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) { - if (!h || !c || !res) return (int32_t)SOCK_ERR_INVAL; - HTTPServer* srv = reinterpret_cast(h); - HTTPConnection* conn = reinterpret_cast(c); - return srv->send_response(conn, *res); -} - -int32_t http_connection_close(http_connection_handle_t c) { - if (!c) return (int32_t)SOCK_ERR_INVAL; - HTTPConnection* conn = reinterpret_cast(c); - if (conn->carry_buf.mem_length) string_free(conn->carry_buf); - if (conn->client) close_socket(conn->client); - release(conn); - return (int32_t)SOCK_OK; -} - -int32_t http_server_close(http_server_handle_t h) { - if (!h) return (int32_t)SOCK_ERR_INVAL; - HTTPServer* srv = reinterpret_cast(h); - return srv->close(); -} - -} diff --git a/kernel/networking/application_layer/dhcp_daemon.c b/kernel/networking/application_layer/dhcp_daemon.c index 47d4078f..9cce37cf 100644 --- a/kernel/networking/application_layer/dhcp_daemon.c +++ b/kernel/networking/application_layer/dhcp_daemon.c @@ -7,6 +7,7 @@ #include "networking/application_layer/dhcp.h" #include "networking/internet_layer/ipv4.h" +#include "networking/internet_layer/ipv4_utils.h" #include "net/network_types.h" #include "networking/link_layer/arp.h" #include "networking/transport_layer/udp.h" @@ -81,7 +82,7 @@ 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) { + if (!ipv4_l3_is_active(v4)) { if (g_if[i].sock) close_socket(g_if[i].sock); if (i < g_if_count - 1) g_if[i] = g_if[g_if_count - 1]; g_if_count--; @@ -95,8 +96,7 @@ static void ensure_inventory() { 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 (!ipv4_l3_is_active(v4)) continue; bool found = false; for (int i = 0; i < g_if_count; i++) { if (g_if[i].l3_id == v4->l3_id) { @@ -343,7 +343,7 @@ static void dhcp_send_discover_for(dhcp_if_state_t* st) { sizedptr pkt = dhcp_build_packet(&req, DHCPDISCOVER, xid, DHCPK_DISCOVER, true); uint32_t bcast = 0xFFFFFFFFu; net_l4_endpoint dst; - make_ep(bcast, 67, IP_VER4, &dst); + make_ep(&bcast, 67, IP_VER4, &dst); send_to_socket(st->sock, &dst, (const void*)pkt.ptr, pkt.size); free_sized((void*)pkt.ptr, pkt.size); } @@ -352,7 +352,7 @@ static void dhcp_send_request_select_for(dhcp_if_state_t* st, const dhcp_request sizedptr pkt = dhcp_build_packet(base, DHCPREQUEST, st->trans_xid, DHCPK_SELECT, true); uint32_t dip = 0xFFFFFFFFu; net_l4_endpoint dst; - make_ep(dip, 67, IP_VER4, &dst); + make_ep(&dip, 67, IP_VER4, &dst); send_to_socket(st->sock, &dst, (const void*)pkt.ptr, pkt.size); free_sized((void*)pkt.ptr, pkt.size); } @@ -372,7 +372,7 @@ static void dhcp_send_renew_for(dhcp_if_state_t* st) { sizedptr pkt = dhcp_build_packet(&req, DHCPREQUEST, st->trans_xid, DHCPK_RENEW, st->server_ip_net == 0); uint32_t dip = st->server_ip_net ? st->server_ip_net : 0xFFFFFFFFu; net_l4_endpoint dst; - make_ep(dip, 67, IP_VER4, &dst); + make_ep(&dip, 67, IP_VER4, &dst); send_to_socket(st->sock, &dst, (const void*)pkt.ptr, pkt.size); free_sized((void*)pkt.ptr, pkt.size); } @@ -392,7 +392,7 @@ static void dhcp_send_rebind_for(dhcp_if_state_t* st) { sizedptr pkt = dhcp_build_packet(&req, DHCPREQUEST, st->trans_xid, DHCPK_REBIND, true); uint32_t dip = 0xFFFFFFFFu; net_l4_endpoint dst; - make_ep(dip, 67, IP_VER4, &dst); + make_ep(&dip, 67, IP_VER4, &dst); send_to_socket(st->sock, &dst, (const void*)pkt.ptr, pkt.size); free_sized((void*)pkt.ptr, pkt.size); } @@ -412,12 +412,18 @@ static void dhcp_send_inform_for(dhcp_if_state_t* st) { sizedptr pkt = dhcp_build_packet(&req, DHCPINFORM, xid, DHCPK_INFORM, true); uint32_t dip = 0xFFFFFFFFu; net_l4_endpoint dst; - make_ep(dip, 67, IP_VER4, &dst); + make_ep(&dip, 67, IP_VER4, &dst); send_to_socket(st->sock, &dst, (const void*)pkt.ptr, pkt.size); free_sized((void*)pkt.ptr, pkt.size); } -static void schedule_retry(dhcp_if_state_t* st) { +static void dhcp_drop_lease_and_retry(dhcp_if_state_t* st) { + 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; st->retry_left_ms = dhcp_next_backoff_ms(st); } @@ -434,7 +440,7 @@ static void fsm_once_for(dhcp_if_state_t* st) { 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); + st->retry_left_ms = dhcp_next_backoff_ms(st); break; } dhcp_request req; @@ -453,7 +459,7 @@ static void fsm_once_for(dhcp_if_state_t* st) { 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); + st->retry_left_ms = dhcp_next_backoff_ms(st); } else { if (mtype == DHCPACK) { apply_offer_to_l3(st->ifindex, st->l3_id, resp, sp, st->last_xid, st); @@ -462,13 +468,7 @@ static void fsm_once_for(dhcp_if_state_t* st) { dhcp_reset_backoff(st); } else { free_sized((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); + dhcp_drop_lease_and_retry(st); } } } break; @@ -498,13 +498,7 @@ static void fsm_once_for(dhcp_if_state_t* st) { dhcp_reset_backoff(st); } else { free_sized((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); + dhcp_drop_lease_and_retry(st); } } else { dhcp_send_rebind_for(st); @@ -522,22 +516,10 @@ static void fsm_once_for(dhcp_if_state_t* st) { dhcp_reset_backoff(st); } else { free_sized((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); + dhcp_drop_lease_and_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); + dhcp_drop_lease_and_retry(st); } } break; } diff --git a/kernel/networking/application_layer/dhcpv6_daemon.c b/kernel/networking/application_layer/dhcpv6_daemon.c index 891d6b27..f5997dfb 100644 --- a/kernel/networking/application_layer/dhcpv6_daemon.c +++ b/kernel/networking/application_layer/dhcpv6_daemon.c @@ -5,6 +5,7 @@ #include "syscalls/syscalls.h" #include "process/scheduler.h" #include "math/rng.h" +#include "random/random.h" #include "data/struct/linked_list.h" @@ -172,7 +173,7 @@ static void ensure_binds() { bool stateless = (t->cfg == IPV6_CFG_SLAAC && t->dhcpv6_stateless); if (!stateful && !stateless) keep = false; } - if (keep) if (!t->l2 || !t->l2->is_up) keep = false; + if (keep && (!t->l2 || !t->l2->is_up)) keep = false; l3_ipv6_interface_t* llv6 = NULL; if (keep) { @@ -180,9 +181,8 @@ static void ensure_binds() { if (!llv6) keep = false; } - if (keep) if (llv6->cfg == IPV6_CFG_DISABLE) keep = false; - if (keep) if (llv6->dad_state != IPV6_DAD_OK) keep = false; - if (keep) if (!ipv6_is_linklocal(llv6->ip)) keep = false; + if (keep && !ipv6_l3_is_ready(llv6)) keep = false; + if (keep && !ipv6_is_linklocal(llv6->ip)) keep = false; if (!keep) { if (t) reset_lease_state(t, b); @@ -234,9 +234,7 @@ static void ensure_binds() { for (int i = 0; i < MAX_IPV6_PER_INTERFACE; i++) { l3_ipv6_interface_t* v6 = l2->l3_v6[i]; - if (!v6) continue; - if (v6->cfg == IPV6_CFG_DISABLE) continue; - if (v6->dad_state != IPV6_DAD_OK) continue; + if (!ipv6_l3_is_ready(v6)) continue; if (!ipv6_is_linklocal(v6->ip)) continue; ll_l3 = v6->l3_id; ll_ok = true; @@ -398,8 +396,7 @@ static void fsm_once(dhcpv6_bind_t* b, uint32_t tick_ms) { if (v6->dhcpv6_state == DHCPV6_S_INIT) { if (stateless) { - uint8_t zero16[16] = {0}; - int has_dns = (memcmp(v6->runtime_opts_v6.dns[0], zero16, 16) != 0) || (memcmp(v6->runtime_opts_v6.dns[1], zero16, 16) != 0); + int has_dns = !ipv6_is_unspecified(v6->runtime_opts_v6.dns[0]) || !ipv6_is_unspecified(v6->runtime_opts_v6.dns[1]); if (has_dns) { v6->dhcpv6_stateless_done = 1; @@ -467,8 +464,7 @@ static void fsm_once(dhcpv6_bind_t* b, uint32_t tick_ms) { else if (type_peek == DHCPV6_MSG_REQUEST) lim = DHCPV6_MAX_REQUEST_TX; if (b->tx_tries >= lim) { - uint8_t zero16[16] = {0}; - int has_dns =(memcmp(v6->runtime_opts_v6.dns[0], zero16, 16) != 0) || (memcmp(v6->runtime_opts_v6.dns[1], zero16, 16) != 0); + int has_dns = !ipv6_is_unspecified(v6->runtime_opts_v6.dns[0]) || !ipv6_is_unspecified(v6->runtime_opts_v6.dns[1]); if (!has_dns) { if (!ipv6_is_unspecified(v6->gateway) && !ipv6_is_multicast(v6->gateway)) { @@ -583,8 +579,7 @@ static void fsm_once(dhcpv6_bind_t* b, uint32_t tick_ms) { if (p.has_dns) memcpy(v6->runtime_opts_v6.dns, p.dns, sizeof(v6->runtime_opts_v6.dns)); if (p.has_ntp) memcpy(v6->runtime_opts_v6.ntp, p.ntp, sizeof(v6->runtime_opts_v6.ntp)); - uint8_t zero16[16] = {0}; - int has_dns = (memcmp(v6->runtime_opts_v6.dns[0], zero16, 16) != 0) || (memcmp(v6->runtime_opts_v6.dns[1], zero16, 16) != 0); + int has_dns = !ipv6_is_unspecified(v6->runtime_opts_v6.dns[0]) || !ipv6_is_unspecified(v6->runtime_opts_v6.dns[1]); if (!has_dns) { if (!ipv6_is_unspecified(v6->gateway) && !ipv6_is_multicast(v6->gateway)) { @@ -633,8 +628,7 @@ static void fsm_once(dhcpv6_bind_t* b, uint32_t tick_ms) { if (p.has_dns) memcpy(v6->runtime_opts_v6.dns, p.dns, sizeof(v6->runtime_opts_v6.dns)); if (p.has_ntp) memcpy(v6->runtime_opts_v6.ntp, p.ntp, sizeof(v6->runtime_opts_v6.ntp)); - uint8_t zero16[16] = {0}; - int has_dns = (memcmp(v6->runtime_opts_v6.dns[0], zero16, 16) != 0) || (memcmp(v6->runtime_opts_v6.dns[1], zero16, 16) != 0); + int has_dns = !ipv6_is_unspecified(v6->runtime_opts_v6.dns[0]) || !ipv6_is_unspecified(v6->runtime_opts_v6.dns[1]); if (!has_dns) { if (!ipv6_is_unspecified(v6->gateway) && !ipv6_is_multicast(v6->gateway)) { @@ -654,8 +648,7 @@ static void fsm_once(dhcpv6_bind_t* b, uint32_t tick_ms) { if (p.has_dns) memcpy(v6->runtime_opts_v6.dns, p.dns, sizeof(v6->runtime_opts_v6.dns)); if (p.has_ntp) memcpy(v6->runtime_opts_v6.ntp, p.ntp, sizeof(v6->runtime_opts_v6.ntp)); - uint8_t zero16[16] = {0}; - int has_dns = (memcmp(v6->runtime_opts_v6.dns[0], zero16, 16) != 0) || (memcmp(v6->runtime_opts_v6.dns[1], zero16, 16) != 0); + int has_dns = !ipv6_is_unspecified(v6->runtime_opts_v6.dns[0]) || !ipv6_is_unspecified(v6->runtime_opts_v6.dns[1]); if (!has_dns) { if (!ipv6_is_unspecified(v6->gateway) && !ipv6_is_multicast(v6->gateway)) { @@ -706,8 +699,7 @@ static void fsm_once(dhcpv6_bind_t* b, uint32_t tick_ms) { if (p.has_dns) memcpy(v6->runtime_opts_v6.dns, p.dns, sizeof(v6->runtime_opts_v6.dns)); if (p.has_ntp) memcpy(v6->runtime_opts_v6.ntp, p.ntp, sizeof(v6->runtime_opts_v6.ntp)); - uint8_t zero16[16] = {0}; - int has_dns = (memcmp(v6->runtime_opts_v6.dns[0], zero16, 16) != 0) || (memcmp(v6->runtime_opts_v6.dns[1], zero16, 16) != 0); + int has_dns = !ipv6_is_unspecified(v6->runtime_opts_v6.dns[0]) || !ipv6_is_unspecified(v6->runtime_opts_v6.dns[1]); if (!has_dns) { if (!ipv6_is_unspecified(v6->gateway) && !ipv6_is_multicast(v6->gateway)) { @@ -775,9 +767,7 @@ int dhcpv6_daemon_entry(int argc, char* argv[]) { g_dhcpv6_pid = get_current_proc_pid(); - uint64_t virt_timer; - asm volatile ("mrs %0, cntvct_el0" : "=r"(virt_timer)); - rng_seed(&g_dhcpv6_rng, virt_timer); + rng_init_random(&g_dhcpv6_rng); const uint32_t tick_ms = 250; diff --git a/kernel/networking/application_layer/dns/dns.c b/kernel/networking/application_layer/dns/dns.c index b4c42433..bfd57f09 100644 --- a/kernel/networking/application_layer/dns/dns.c +++ b/kernel/networking/application_layer/dns/dns.c @@ -6,12 +6,14 @@ #include "process/scheduler.h" #include "types.h" #include "networking/internet_layer/ipv4.h" +#include "networking/internet_layer/ipv4_utils.h" #include "networking/internet_layer/ipv6_utils.h" #include "networking/interface_manager.h" #include "dns_daemon.h" #include "syscalls/syscalls.h" #include "networking/transport_layer/trans_utils.h" +#include "random/random.h" #define MDNS_TIMEOUT_A_MS 500u #define MDNS_TIMEOUT_AAAA_MS 300u @@ -27,9 +29,7 @@ static dns_result_t perform_dns_query_once(socket_handle_t sock, const net_l4_en uint8_t request_buffer[512]; rng_t rng; - uint64_t virt_timer; - asm volatile ("mrs %0, cntvct_el0" : "=r"(virt_timer)); - rng_seed(&rng, virt_timer); + rng_init_random(&rng); uint16_t message_id = (uint16_t)(rng_next32(&rng) & 0xFFFF); uint32_t request_len = dns_wire_build_query(request_buffer, sizeof(request_buffer), message_id, name, qtype, false); if (!request_len) return DNS_ERR_FORMAT; @@ -78,41 +78,27 @@ static dns_result_t perform_dns_query_once(socket_handle_t sock, const net_l4_en } static bool pick_dns_on_l3(uint8_t l3_id, net_l4_endpoint* out_primary, net_l4_endpoint* out_secondary) { - if (l3_ipv4_find_by_id(l3_id)) { - l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(l3_id); - if (!v4) return false; + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(l3_id); + if (v4) { uint32_t p = v4->runtime_opts_v4.dns[0]; uint32_t s = v4->runtime_opts_v4.dns[1]; if (out_primary) { - memset(out_primary, 0, sizeof(*out_primary)); - out_primary->ver = IP_VER4; - memcpy(out_primary->ip, &p, 4); + make_ep(&p, 0, IP_VER4, out_primary); } if (out_secondary) { - memset(out_secondary, 0, sizeof(*out_secondary)); - out_secondary->ver = IP_VER4; - memcpy(out_secondary->ip, &s, 4); + make_ep(&s, 0, IP_VER4, out_secondary); } return p || s; } l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(l3_id); if (!v6) return false; - static const uint8_t z[16] = {0}; const uint8_t* p6 = v6->runtime_opts_v6.dns[0]; const uint8_t* s6 = v6->runtime_opts_v6.dns[1]; - bool hp = memcmp(p6, z, 16) != 0; - bool hs = memcmp(s6, z, 16) != 0; - if (out_primary) { - memset(out_primary, 0, sizeof(*out_primary)); - out_primary->ver = IP_VER6; - if (hp) memcpy(out_primary->ip, p6, 16); - } - if (out_secondary) { - memset(out_secondary, 0, sizeof(*out_secondary)); - out_secondary->ver = IP_VER6; - if (hs) memcpy(out_secondary->ip, s6, 16); - } + bool hp = !ipv6_is_unspecified(p6); + bool hs = !ipv6_is_unspecified(s6); + if (out_primary) make_ep(hp ? p6 : NULL, 0, IP_VER6, out_primary); + if (out_secondary) make_ep(hs ? s6 : NULL, 0, IP_VER6, out_secondary); return hp || hs; } @@ -123,21 +109,17 @@ static bool pick_dns_first_iface(uint8_t* out_l3, net_l4_endpoint* out_primary, 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; + if (!ipv4_l3_is_active(v4)) 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) { - memset(out_primary, 0, sizeof(*out_primary)); - out_primary->ver = IP_VER4; - memcpy(out_primary->ip, &p, 4); + make_ep(&p, 0, IP_VER4, out_primary); } if (out_secondary) { - memset(out_secondary, 0, sizeof(*out_secondary)); - out_secondary->ver = IP_VER4; - memcpy(out_secondary->ip, &q, 4); + make_ep(&q, 0, IP_VER4, out_secondary); } return true; } @@ -145,20 +127,16 @@ static bool pick_dns_first_iface(uint8_t* out_l3, net_l4_endpoint* out_primary, 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) continue; + if (!ipv6_l3_is_active(v6)) continue; bool hp = !ipv6_is_unspecified(v6->runtime_opts_v6.dns[0]); bool hq = !ipv6_is_unspecified(v6->runtime_opts_v6.dns[1]); if (hp || hq){ if (out_l3) *out_l3 = v6->l3_id; if (out_primary) { - memset(out_primary, 0, sizeof(*out_primary)); - out_primary->ver = IP_VER6; - if (hp) memcpy(out_primary->ip, v6->runtime_opts_v6.dns[0], 16); + make_ep(hp ? v6->runtime_opts_v6.dns[0] : NULL, 0, IP_VER6, out_primary); } if (out_secondary) { - memset(out_secondary, 0, sizeof(*out_secondary)); - out_secondary->ver = IP_VER6; - if (hq) memcpy(out_secondary->ip, v6->runtime_opts_v6.dns[1], 16); + make_ep(hq ? v6->runtime_opts_v6.dns[1] : NULL, 0, IP_VER6, out_secondary); } return true; } diff --git a/kernel/networking/application_layer/dns/dns_daemon.c b/kernel/networking/application_layer/dns/dns_daemon.c index b5d7e2d2..16e2cc52 100644 --- a/kernel/networking/application_layer/dns/dns_daemon.c +++ b/kernel/networking/application_layer/dns/dns_daemon.c @@ -6,6 +6,7 @@ #include "syscalls/syscalls.h" #include "net/socket_types.h" #include "networking/transport_layer/csocket.h" +#include "networking/internet_layer/ipv4_utils.h" #include "networking/internet_layer/ipv6_utils.h" #include "networking/interface_manager.h" #include "std/memory.h" @@ -37,7 +38,7 @@ static void mdns_open_sockets(const uint8_t *group4, const uint8_t *group6) { for (uint8_t j = 0; j < MAX_IPV4_PER_INTERFACE && g_mdns_count < MAX_L3_INTERFACES; j++) { l3_ipv4_interface_t *v4 = l2->l3_v4[j]; - if (!v4 || v4->mode == IPV4_CFG_DISABLED || v4->is_localhost || !v4->ip) continue; + if (!ipv4_l3_is_ready(v4) || v4->is_localhost) continue; bool have_socket = false; for (uint8_t k = 0; k < g_mdns_count; k++) { if (g_mdns[k].sock && g_mdns[k].ver == IP_VER4 && g_mdns[k].l3_id == v4->l3_id) { @@ -81,7 +82,7 @@ static void mdns_open_sockets(const uint8_t *group4, const uint8_t *group6) { for (uint8_t j = 0; j < MAX_IPV6_PER_INTERFACE && g_mdns_count < MAX_L3_INTERFACES; j++) { l3_ipv6_interface_t *v6 = l2->l3_v6[j]; - if (!v6 || v6->cfg == IPV6_CFG_DISABLE || v6->dad_state != IPV6_DAD_OK || v6->is_localhost || !v6->ip[0]) continue; + if (!ipv6_l3_is_ready(v6) || v6->is_localhost) continue; bool have_socket = false; for (uint8_t k = 0; k < g_mdns_count; k++) { if (g_mdns[k].sock && g_mdns[k].ver == IP_VER6 && g_mdns[k].l3_id == v6->l3_id) { @@ -147,7 +148,7 @@ int dns_deamon_entry(int argc, char* argv[]){ for (uint8_t sidx = 0; sidx < g_mdns_count; sidx++) { socket_handle_t s = g_mdns[sidx].sock; - for (int i = 0; i < 4; ++i) { + for (int i = 0; i < 64; i++) { memset(&src, 0, sizeof(src)); int64_t r = receive_from_socket(s, buf, sizeof(buf), &src); if (r == SOCK_ERR_WOULDBLOCK) break; diff --git a/kernel/networking/application_layer/dns/dns_mdns.c b/kernel/networking/application_layer/dns/dns_mdns.c index 798d0f27..42f63937 100644 --- a/kernel/networking/application_layer/dns/dns_mdns.c +++ b/kernel/networking/application_layer/dns/dns_mdns.c @@ -68,7 +68,7 @@ dns_result_t mdns_query(const char* name, dns_qtype_t qtype, uint32_t timeout_ms if (sock) { uint32_t group =DNS_MDNS_GROUP_V4; net_l4_endpoint dst; - make_ep(group, DNS_MDNS_PORT, IP_VER4, &dst); + make_ep(&group, DNS_MDNS_PORT, IP_VER4, &dst); uint32_t got = 0; last = perform_mdns_query_once(sock, &dst, name, qtype, timeout_ms, out_records, max_records, &got); if (last == DNS_OK) total = got; @@ -80,11 +80,10 @@ dns_result_t mdns_query(const char* name, dns_qtype_t qtype, uint32_t timeout_ms sock = mdns_socket_handle_for(IP_VER6); if (sock) { + uint8_t group6[16]; net_l4_endpoint dst; - memset(&dst, 0, sizeof(dst)); - dst.ver = IP_VER6; - ipv6_make_multicast(0x02, IPV6_MCAST_MDNS, 0, dst.ip); - dst.port = DNS_MDNS_PORT; + ipv6_make_multicast(0x02, IPV6_MCAST_MDNS, 0, group6); + make_ep(group6, DNS_MDNS_PORT, IP_VER6, &dst); uint32_t got = 0; dns_record_t *dst_records = out_records ? out_records + total : 0; uint32_t left = max_records > total ? max_records - total : 0; diff --git a/kernel/networking/application_layer/dns/mdns_responder.c b/kernel/networking/application_layer/dns/mdns_responder.c index 8ac1b394..7ed5229f 100644 --- a/kernel/networking/application_layer/dns/mdns_responder.c +++ b/kernel/networking/application_layer/dns/mdns_responder.c @@ -2,6 +2,7 @@ #include "dns_sd.h" #include "dns_cache.h" +#include "networking/internet_layer/ipv4_utils.h" #include "networking/internet_layer/ipv6_utils.h" #include "networking/transport_layer/csocket.h" #include "networking/interface_manager.h" @@ -17,6 +18,7 @@ #define MDNS_MAX_SERVICES 8 #define MDNS_CACHE_MAX 48 #define MDNS_FLUSH_CLASS (DNS_CLASS_CACHE_FLUSH | DNS_CLASS_IN) +#define MDNS_HOST_NAME "RedactedOS" typedef struct { bool used; @@ -31,13 +33,6 @@ typedef struct { uint16_t port; } mdns_service_t; -typedef struct { - uint16_t rrtype; - uint16_t rrclass; - uint32_t ttl_s; - uint16_t rdlen; -} mdns_rr_hdr_t; - typedef struct { uint8_t type; uint16_t rrtype; @@ -93,37 +88,29 @@ static void mdns_send(socket_handle_t sock, const net_l4_endpoint *src, bool uni send_to_socket(sock, &dst, pkt, pkt_len); } -static bool mdns_pick_identity(uint32_t *out_v4, uint8_t out_v6[16], uint8_t *out_ifindex, uint8_t out_ifid[8]) { +static bool mdns_pick_identity(uint32_t *out_v4, uint8_t out_v6[16], uint8_t *out_ifindex) { if (!out_v4) return false; if (!out_v6) return false; if (!out_ifindex) return false; - if (!out_ifid) return false; uint32_t v4 = 0; uint8_t v6_best[16]; uint8_t v6_fallback[16]; - uint8_t ifid_best[8]; - uint8_t ifid_fallback[8]; uint8_t if_best = 0; uint8_t if_fallback = 0; memset(v6_best, 0, sizeof(v6_best)); memset(v6_fallback, 0, sizeof(v6_fallback)); - memset(ifid_best, 0, sizeof(ifid_best)); - memset(ifid_fallback, 0, sizeof(ifid_fallback)); uint8_t c = l2_interface_count(); for (uint8_t i = 0; i < c; i++) { l2_interface_t *l2 = l2_interface_at(i); - if (!l2)continue; - if (!l2->is_up) continue; + if (!l2 || !l2->is_up) continue; if (!v4) { for (uint8_t j = 0; j < l2->ipv4_count; j++) { l3_ipv4_interface_t *a = l2->l3_v4[j]; - if (!a) continue; - if (a->is_localhost) continue; - if (!a->ip) continue; + if (!ipv4_l3_is_ready(a) || a->is_localhost) continue; v4 = a->ip; break; } @@ -131,20 +118,15 @@ static bool mdns_pick_identity(uint32_t *out_v4, uint8_t out_v6[16], uint8_t *ou for (uint8_t j = 0; j < l2->ipv6_count; j++) { l3_ipv6_interface_t *a = l2->l3_v6[j]; - if (!a) continue; - if (a->is_localhost) continue; - if (!a->ip[0]) continue; + if (!ipv6_l3_is_ready(a) || a->is_localhost) continue; - bool is_lla = (a->ip[0] == 0xFE && (a->ip[1] & 0xC0) == 0x80); - if (!is_lla && !if_best) { + if (!ipv6_is_linklocal(a->ip) && !if_best) { memcpy(v6_best, a->ip, 16); - memcpy(ifid_best, a->interface_id, 8); if_best = l2->ifindex; } if (!if_fallback) { memcpy(v6_fallback, a->ip, 16); - memcpy(ifid_fallback,a->interface_id, 8); if_fallback = l2->ifindex; } } @@ -154,7 +136,6 @@ static bool mdns_pick_identity(uint32_t *out_v4, uint8_t out_v6[16], uint8_t *ou *out_v4 = v4; memcpy(out_v6, v6_best, 16); *out_ifindex = if_best; - memcpy(out_ifid, ifid_best, 8); return true; } @@ -162,7 +143,6 @@ static bool mdns_pick_identity(uint32_t *out_v4, uint8_t out_v6[16], uint8_t *ou *out_v4 = v4; memcpy(out_v6, v6_fallback, 16); *out_ifindex = if_fallback; - memcpy(out_ifid, ifid_fallback, 8); return true; } @@ -170,7 +150,6 @@ static bool mdns_pick_identity(uint32_t *out_v4, uint8_t out_v6[16], uint8_t *ou *out_v4 = v4; memset(out_v6, 0, 16); *out_ifindex = 0; - memset(out_ifid, 0, 8); return true; } @@ -184,12 +163,10 @@ static void mdns_refresh_identity(void) { uint32_t v4 = 0; uint8_t v6[16]; - uint8_t ifid[8]; uint8_t ifindex = 0; memset(v6, 0, sizeof(v6)); - memset(ifid, 0, sizeof(ifid)); - if (!mdns_pick_identity(&v4, v6, &ifindex, ifid)) return; + if (!mdns_pick_identity(&v4, v6, &ifindex)) return; bool changed = false; if (g_mdns_ipv4 != v4) changed = true; @@ -201,9 +178,7 @@ static void mdns_refresh_identity(void) { g_mdns_ifindex = ifindex; if (!g_mdns_fqdn[0]) { - char host[64]; - string_format_buf(host, sizeof(host),"redactedos-%02x%02x%02x%02x%02x%02x%02x%02x", ifid[0],ifid[1],ifid[2],ifid[3],ifid[4],ifid[5], ifid[6], ifid[7]); - string_format_buf(g_mdns_fqdn, sizeof(g_mdns_fqdn), "%s.local", host); + string_format_buf(g_mdns_fqdn, sizeof(g_mdns_fqdn), "%s.local", MDNS_HOST_NAME); changed = true; } @@ -319,123 +294,33 @@ static bool mdns_pkt_add_txt(mdns_pkt_t *p, bool additional, const char *name, u } -static void mdns_cache_put_ptr(const char *name, const char *target, uint32_t ttl_s) { - if (!name) return; - if (!target) return; - - uint64_t now = get_time(); - if (!ttl_s) { - for (uint32_t i = 0; i < MDNS_CACHE_MAX; i++) { - mdns_cache_entry_t *e = &g_mdns_cache[i]; - if (!e->type) continue; - if (e->rrtype != DNS_TYPE_PTR) continue; - if (!dns_wire_name_equals(e->name, name)) continue; - memset(e, 0, sizeof(*e)); - return; - } - return; - } - for (uint32_t i = 0; i < MDNS_CACHE_MAX; i++) { - mdns_cache_entry_t *e = &g_mdns_cache[i]; - if (!e->type) continue; - if (e->rrtype != DNS_TYPE_PTR) continue; - if (!dns_wire_name_equals(e->name, name)) continue; - strncpy(e->target, target, sizeof(e->target)); - e->expire_ms = now + (uint64_t)ttl_s * 1000; - return; - } +static mdns_cache_entry_t *mdns_cache_entry_for(const char *name, uint16_t rrtype, bool create) { + if (!name) return NULL; + mdns_cache_entry_t *empty = NULL; for (uint32_t i = 0; i < MDNS_CACHE_MAX; i++) { mdns_cache_entry_t *e = &g_mdns_cache[i]; - if (e->type) continue; - memset(e, 0, sizeof(*e)); - e->type = 1; - e->rrtype = DNS_TYPE_PTR; - strncpy(e->name, name, sizeof(e->name)); - strncpy(e->target, target, sizeof(e->target)); - e->expire_ms = now + (uint64_t)ttl_s * 1000; - return; - } -} - -static void mdns_cache_put_srv(const char *name, uint16_t port, const char *target, uint32_t ttl_s) { - if (!name) return; - if (!target) return; - - uint64_t now = get_time(); - if (!ttl_s) { - for (uint32_t i = 0; i < MDNS_CACHE_MAX; i++) { - mdns_cache_entry_t *e = &g_mdns_cache[i]; - if (!e->type) continue; - if (e->rrtype != DNS_TYPE_SRV) continue; - if (!dns_wire_name_equals(e->name, name)) continue; - memset(e, 0, sizeof(*e)); - return; + if (!e->type) { + if (!empty) empty = e; + continue; } - return; - } - for (uint32_t i = 0; i < MDNS_CACHE_MAX; i++) { - mdns_cache_entry_t *e = &g_mdns_cache[i]; - if (!e->type) continue; - if (e->rrtype != DNS_TYPE_SRV) continue; - if (!dns_wire_name_equals(e->name, name)) continue; - e->port = port; - strncpy(e->target, target, sizeof(e->target)); - e->expire_ms = now + (uint64_t)ttl_s * 1000; - return; + if (e->rrtype == rrtype && dns_wire_name_equals(e->name, name)) return e; } - for (uint32_t i = 0; i < MDNS_CACHE_MAX; i++) { - mdns_cache_entry_t *e = &g_mdns_cache[i]; - if (e->type) continue; - memset(e, 0, sizeof(*e)); - e->type = 1; - e->rrtype = DNS_TYPE_SRV; - e->port = port; - strncpy(e->name, name, sizeof(e->name)); - strncpy(e->target, target, sizeof(e->target)); - e->expire_ms = now + (uint64_t)ttl_s * 1000; - return; - } + if (!create || !empty) return NULL; + memset(empty, 0, sizeof(*empty)); + empty->type = 1; + empty->rrtype = rrtype; + strncpy(empty->name, name, sizeof(empty->name)); + return empty; } -static void mdns_cache_put_txt(const char *name, const char *txt, uint32_t ttl_s) { - if (!name) return; - - uint64_t now = get_time(); - if (!ttl_s) { - for (uint32_t i = 0; i < MDNS_CACHE_MAX; i++) { - mdns_cache_entry_t *e = &g_mdns_cache[i]; - if (!e->type) continue; - if (e->rrtype != DNS_TYPE_TXT) continue; - if (!dns_wire_name_equals(e->name, name)) continue; - memset(e, 0, sizeof(*e)); - return; - } - return; - } - for (uint32_t i = 0; i < MDNS_CACHE_MAX; i++) { - mdns_cache_entry_t *e = &g_mdns_cache[i]; - if (!e->type) continue; - if (e->rrtype != DNS_TYPE_TXT) continue; - if (!dns_wire_name_equals(e->name, name)) continue; - if (txt) strncpy(e->txt, txt, sizeof(e->txt)); - else e->txt[0] = 0; - e->expire_ms = now + (uint64_t)ttl_s * 1000; - return; - } +static bool mdns_current_ipv6(uint8_t ip[16]) { + if (!ip) return false; - for (uint32_t i = 0; i < MDNS_CACHE_MAX; i++) { - mdns_cache_entry_t *e = &g_mdns_cache[i]; - if (e->type) continue; - memset(e, 0, sizeof(*e)); - e->type = 1; - e->rrtype = DNS_TYPE_TXT; - strncpy(e->name, name, sizeof(e->name)); - if (txt) strncpy(e->txt, txt, sizeof(e->txt)); - e->expire_ms = now + (uint64_t)ttl_s * 1000; - return; - } + memcpy(ip, g_mdns_ipv6, 16); + if (ipv6_is_unspecified(ip) && g_mdns_ifindex) ipv6_make_lla_from_mac(g_mdns_ifindex, ip); + return !ipv6_is_unspecified(ip); } static bool mdns_parse_ipv4_ptr_qname(const char *name, uint32_t *out_ip) { @@ -445,39 +330,26 @@ static bool mdns_parse_ipv4_ptr_qname(const char *name, uint32_t *out_ip) { char norm[DNS_WIRE_MAX_NAME]; if (!dns_wire_name_normalize(name, norm, sizeof(norm))) return false; - uint32_t oct[4]; - memset(oct, 0, sizeof(oct)); - + uint8_t ip[4]; const char *p = norm; - for (int i = 0; i < 4; i++) { + for (int i = 3; i >= 0; i--) { + const char *label = p; uint32_t v = 0; - uint32_t digits = 0; - while (is_digit(*p)) { - v = v * 10 + (uint32_t)(*p - '0'); - p++; - digits++; - if (digits > 3) return false; - } - if (!digits) return false; + while (is_digit(*p) && (p - label) < 3) v = v * 10 + (*p++ - '0'); + if (p == label) return false; + if (is_digit(*p)) return false; if (v > 255) return false; - oct[i] = v; - if (i < 3) { + ip[i] = v; + if (i) { if (*p != '.') return false; p++; } } - if (*p != '.') return false; - p++; - - if (strncmp(p, "in-addr.arpa", 12) != 0 || p[12] != 0) return false; - - uint32_t a = oct[3]; - uint32_t b = oct[2]; - uint32_t c = oct[1]; - uint32_t d = oct[0]; + if (*p++ != '.') return false; - *out_ip = (uint32_t)((a & 255u) | ((b & 255u) << 8) | ((c & 255u) << 16) | ((d & 255u) << 24)); + if (strcmp(p, "in-addr.arpa") != 0) return false; + *out_ip = rd_be32(ip); return true; } @@ -495,15 +367,35 @@ static void mdns_cache_from_packet(const uint8_t *pkt, uint32_t pkt_len) { dns_record_t *r = &records[i]; if ((r->rrclass & DNS_CLASS_MASK) != DNS_CLASS_IN) continue; - if (r->type == DNS_TYPE_A) { + switch (r->type) { + case DNS_TYPE_A: if (!r->ttl_s) dns_cache_remove_ip(r->name, DNS_TYPE_A); else dns_cache_put_ip(r->name, DNS_TYPE_A, r->addr, r->ttl_s * 1000); - } else if (r->type == DNS_TYPE_AAAA) { + break; + case DNS_TYPE_AAAA: if (!r->ttl_s) dns_cache_remove_ip(r->name, DNS_TYPE_AAAA); else dns_cache_put_ip(r->name, DNS_TYPE_AAAA, r->addr, r->ttl_s * 1000); - } else if (r->type == DNS_TYPE_PTR) mdns_cache_put_ptr(r->name, r->target, r->ttl_s); - else if (r->type == DNS_TYPE_SRV) mdns_cache_put_srv(r->name, r->port, r->target, r->ttl_s); - else if (r->type == DNS_TYPE_TXT) mdns_cache_put_txt(r->name, r->txt, r->ttl_s); + break; + case DNS_TYPE_PTR: + case DNS_TYPE_SRV: + case DNS_TYPE_TXT: + if (r->ttl_s && (r->type == DNS_TYPE_PTR || r->type == DNS_TYPE_SRV) && !r->target[0]) continue; + + mdns_cache_entry_t *e = mdns_cache_entry_for(r->name, r->type, r->ttl_s != 0); + if (!e) continue; + if (!r->ttl_s) { + memset(e, 0, sizeof(*e)); + continue; + } + + if (r->type == DNS_TYPE_SRV) e->port = r->port; + if (r->type == DNS_TYPE_PTR || r->type == DNS_TYPE_SRV) strncpy(e->target, r->target, sizeof(e->target)); + if (r->type == DNS_TYPE_TXT) strncpy(e->txt, r->txt, sizeof(e->txt)); + e->expire_ms = get_time() + (uint64_t)r->ttl_s * 1000; + break; + default: + break; + } } } @@ -517,9 +409,7 @@ static bool mdns_add_host_additionals(mdns_pkt_t *p) { } uint8_t ip6[16]; - memcpy(ip6, g_mdns_ipv6, 16); - if (!ip6[0] && g_mdns_ifindex) ipv6_make_lla_from_mac(g_mdns_ifindex, ip6); - if (ip6[0]) { + if (mdns_current_ipv6(ip6)) { if (!mdns_pkt_add_aaaa(p, true, g_mdns_fqdn, rrclass, MDNS_TTL_S, ip6)) return false; } @@ -694,9 +584,7 @@ void mdns_responder_tick_multi(const mdns_tx_target_t *targets, uint32_t target_ if (g_mdns_ipv4 && !mdns_pkt_add_a(&p, false, g_mdns_fqdn, flush_class, MDNS_TTL_S, g_mdns_ipv4)) ok = false; uint8_t ip6[16]; - memcpy(ip6, g_mdns_ipv6, 16); - if (!ip6[0] && g_mdns_ifindex) ipv6_make_lla_from_mac(g_mdns_ifindex, ip6); - if (ip6[0] && !mdns_pkt_add_aaaa(&p, false, g_mdns_fqdn, flush_class, MDNS_TTL_S, ip6)) ok = false; + if (mdns_current_ipv6(ip6) && !mdns_pkt_add_aaaa(&p, false, g_mdns_fqdn, flush_class, MDNS_TTL_S, ip6)) ok = false; if (ok && p.an) { mdns_pkt_commit(&p); @@ -716,9 +604,7 @@ void mdns_responder_tick_multi(const mdns_tx_target_t *targets, uint32_t target_ } uint8_t ip6_cache[16]; - memcpy(ip6_cache, g_mdns_ipv6, 16); - if (!ip6_cache[0] && g_mdns_ifindex) ipv6_make_lla_from_mac(g_mdns_ifindex, ip6_cache); - if (ip6_cache[0]) dns_cache_put_ip(g_mdns_fqdn, DNS_TYPE_AAAA, ip6_cache, ttl_ms); + if (mdns_current_ipv6(ip6_cache)) dns_cache_put_ip(g_mdns_fqdn, DNS_TYPE_AAAA, ip6_cache, ttl_ms); } } @@ -825,7 +711,7 @@ void mdns_responder_handle_query(socket_handle_t sock, ip_version_t ver, const u if ((qclass & DNS_CLASS_CACHE_FLUSH) != 0) unicast_any = true; uint32_t ipq = 0; - if (qtype == DNS_TYPE_PTR && g_mdns_ipv4 && mdns_parse_ipv4_ptr_qname(qname, &ipq) && ipq == g_mdns_ipv4) { + if ((qtype == DNS_TYPE_PTR || qtype == DNS_TYPE_ANY) && g_mdns_ipv4 && mdns_parse_ipv4_ptr_qname(qname, &ipq) && ipq == g_mdns_ipv4) { uint16_t ptr_class = DNS_CLASS_IN; uint16_t flush_class = MDNS_FLUSH_CLASS; @@ -833,9 +719,7 @@ void mdns_responder_handle_query(socket_handle_t sock, ip_version_t ver, const u if (!mdns_pkt_add_a(&p, true, g_mdns_fqdn, flush_class, MDNS_TTL_S, g_mdns_ipv4)) return; uint8_t ip6[16]; - memcpy(ip6, g_mdns_ipv6, 16); - if (!ip6[0] && g_mdns_ifindex) ipv6_make_lla_from_mac(g_mdns_ifindex, ip6); - if (ip6[0]) { + if (mdns_current_ipv6(ip6)) { if (!mdns_pkt_add_aaaa(&p, true, g_mdns_fqdn, flush_class, MDNS_TTL_S, ip6)) return; } } @@ -850,9 +734,7 @@ void mdns_responder_handle_query(socket_handle_t sock, ip_version_t ver, const u if (qtype == DNS_TYPE_AAAA || qtype == DNS_TYPE_ANY) { if (dns_wire_name_equals(qname, g_mdns_fqdn)) { uint8_t ip6[16]; - memcpy(ip6, g_mdns_ipv6, 16); - if (!ip6[0] && g_mdns_ifindex) ipv6_make_lla_from_mac(g_mdns_ifindex, ip6); - if (ip6[0]) { + if (mdns_current_ipv6(ip6)) { uint16_t flush_class = MDNS_FLUSH_CLASS; if (!mdns_pkt_add_aaaa(&p, false, g_mdns_fqdn, flush_class, MDNS_TTL_S, ip6)) return; } @@ -947,7 +829,5 @@ void mdns_responder_handle_query(socket_handle_t sock, ip_version_t ver, const u } uint8_t ip6[16]; - memcpy(ip6, g_mdns_ipv6, 16); - if (!ip6[0] && g_mdns_ifindex) ipv6_make_lla_from_mac(g_mdns_ifindex, ip6); - if (ip6[0]) dns_cache_put_ip(g_mdns_fqdn, DNS_TYPE_AAAA, ip6, ttl_ms); + if (mdns_current_ipv6(ip6)) dns_cache_put_ip(g_mdns_fqdn, DNS_TYPE_AAAA, ip6, ttl_ms); } \ No newline at end of file diff --git a/kernel/networking/application_layer/http.c b/kernel/networking/application_layer/http.c index b8122d5b..8d5d84da 100644 --- a/kernel/networking/application_layer/http.c +++ b/kernel/networking/application_layer/http.c @@ -120,7 +120,7 @@ string http_methods_allow_header(uint32_t mask) { HTTP_METHOD_OPTIONS }; - string out = string_repeat('\0', 0); + string out = (string){0}; for (uint32_t i = 0; i < sizeof(methods) / sizeof(methods[0]);i++) { if (!http_method_allowed(mask, methods[i])) continue; if (out.length) string_append_bytes(&out, ", ", 2); @@ -273,7 +273,7 @@ static void http_append_field(string *out, const char *key, uint32_t key_len, co } string http_header_builder(const HTTPHeadersCommon *C, const HTTPHeader *H, uint32_t N, HTTPHeaderBuildKind kind, HTTPMethod method, uint32_t status_code){ - string out = string_repeat('\0', 0); + string out = (string){0}; bool request = kind == HTTP_HEADER_BUILD_REQUEST; bool response = kind == HTTP_HEADER_BUILD_RESPONSE; bool informational = response && status_code >= 100 && status_code < 200; @@ -686,10 +686,8 @@ string http_response_builder(const HTTPResponseMsg *R){ } int find_crlfcrlf(const char *data, uint32_t len){ - for (uint32_t i = 0; i + 3 < len; i++){ - if (data[i]=='\r' && data[i+1]=='\n' && data[i+2]=='\r' && data[i+3]=='\n') return (int)i; - } - return -1; + const char *p = memmem(data, len, "\r\n\r\n", 4); + return p ? (int)(p-data) : -1; } void http_chunked_decoder_init(HTTPChunkedDecoder *dec, const HTTPPolicy *policy){ @@ -697,9 +695,9 @@ void http_chunked_decoder_init(HTTPChunkedDecoder *dec, const HTTPPolicy *policy *dec = (HTTPChunkedDecoder){0}; dec->policy = policy ? *policy : http_default_policy(); dec->stage = HTTP_CHUNK_STAGE_SIZE; - dec->line = string_repeat('\0', 0); - dec->body = string_repeat('\0', 0); - dec->trailers_buf = string_repeat('\0', 0); + dec->line = (string){0}; + dec->body = (string){0}; + dec->trailers_buf = (string){0}; } HTTPParseResult http_chunked_decoder_feed(HTTPChunkedDecoder *dec, const char *buf, uint32_t len, uint32_t *out_used) { @@ -744,7 +742,7 @@ HTTPParseResult http_chunked_decoder_feed(HTTPChunkedDecoder *dec, const char *b scan++; } string_free(dec->line); - dec->line = string_repeat('\0', 0); + dec->line = (string){0}; if (!saw_digit && r == HTTP_PARSE_OK) r = HTTP_PARSE_BAD_FORMAT; if (r != HTTP_PARSE_OK) return r; if (dec->body_total + chunk_len > dec->policy.max_body_bytes) return HTTP_PARSE_PAYLOAD_TOO_LARGE; diff --git a/kernel/networking/application_layer/ntp.c b/kernel/networking/application_layer/ntp.c index 5ff7aca4..5fb58305 100644 --- a/kernel/networking/application_layer/ntp.c +++ b/kernel/networking/application_layer/ntp.c @@ -2,6 +2,7 @@ #include "exceptions/timer.h" #include "std/memory.h" #include "networking/internet_layer/ipv4.h" +#include "networking/internet_layer/ipv4_utils.h" #include "process/scheduler.h" #include "console/kio.h" #include "math/math.h" @@ -109,7 +110,7 @@ static ntp_result_t ntp_send_query(socket_handle_t sock, uint32_t server_ip_host p.txTs = tx_be; net_l4_endpoint dst; - make_ep(server_ip_host, NTP_PORT, IP_VER4, &dst); + make_ep(&server_ip_host, NTP_PORT, IP_VER4, &dst); int64_t sent = send_to_socket(sock, &dst, &p, sizeof(p)); if (sent < 0) return NTP_ERR_SEND; *t1_us_out = t1_us; @@ -153,8 +154,7 @@ static void discover_servers(uint32_t* s0, uint32_t* s1){ 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; + if (!ipv4_l3_is_active(v4)) continue; const net_runtime_opts_t* rt =&v4->runtime_opts_v4; if (!rt) continue; uint32_t c0 = rt->ntp[0]; diff --git a/kernel/networking/application_layer/ntp_daemon.c b/kernel/networking/application_layer/ntp_daemon.c index c120bf12..cb5f9efa 100644 --- a/kernel/networking/application_layer/ntp_daemon.c +++ b/kernel/networking/application_layer/ntp_daemon.c @@ -26,9 +26,7 @@ static bool any_ipv4_configured_nonlocal(void){ 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 (!ipv4_l3_is_ready(v4)) continue; if (v4->is_localhost) continue; if (ipv4_is_loopback(v4->ip)) continue; return true; diff --git a/kernel/networking/application_layer/sntp.c b/kernel/networking/application_layer/sntp.c index bda3911d..695fe704 100644 --- a/kernel/networking/application_layer/sntp.c +++ b/kernel/networking/application_layer/sntp.c @@ -2,6 +2,7 @@ #include "exceptions/timer.h" #include "std/memory.h" #include "networking/internet_layer/ipv4.h" +#include "networking/internet_layer/ipv4_utils.h" #include "process/scheduler.h" #include "console/kio.h" #include "types.h" @@ -50,7 +51,7 @@ static sntp_result_t sntp_send_query(socket_handle_t sock, uint32_t server_ip_ho uint64_t t1_us = timer_wall_time_us(); p.txTs = unix_us_to_ntp64_be(t1_us); net_l4_endpoint dst; - make_ep(server_ip_host, NTP_PORT, IP_VER4, &dst); + make_ep(&server_ip_host, NTP_PORT, IP_VER4, &dst); int64_t sent = send_to_socket(sock, &dst, (void*)&p, sizeof(p)); if (sent < 0) return SNTP_ERR_SEND; *t1_us_out = t1_us; @@ -67,8 +68,7 @@ sntp_result_t sntp_poll_once(uint32_t timeout_ms){ 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; + if (!ipv4_l3_is_active(v4)) continue; const net_runtime_opts_t* rt = &v4->runtime_opts_v4; if (!rt) continue; uint32_t c0 = rt->ntp[0]; diff --git a/kernel/networking/application_layer/sntp_daemon.c b/kernel/networking/application_layer/sntp_daemon.c index f9f2bc41..2172a1fe 100644 --- a/kernel/networking/application_layer/sntp_daemon.c +++ b/kernel/networking/application_layer/sntp_daemon.c @@ -27,9 +27,7 @@ static bool any_ipv4_configured_nonlocal(void){ 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 (!ipv4_l3_is_ready(v4)) continue; if (v4->is_localhost) continue; if (ipv4_is_loopback(v4->ip)) continue; return true; diff --git a/kernel/networking/application_layer/socket_http_client.hpp b/kernel/networking/application_layer/socket_http_client.hpp deleted file mode 100644 index 03e4353c..00000000 --- a/kernel/networking/application_layer/socket_http_client.hpp +++ /dev/null @@ -1,552 +0,0 @@ -#pragma once -#include "console/kio.h" -#include "networking/transport_layer/csocket.h" -#include "networking/transport_layer/tcp.h" -#include "networking/net_logger/net_logger.h" -#include "http.h" -#include "std/std.h" -#include "net/socket_types.h" -#include "data/format/url.h" -#include "networking/transport_layer/trans_utils.h" -#include "networking/transport_layer/socket_endpoint.h" -#include "process/scheduler.h" - -struct HTTPClientOrigin { - string domain; - net_l4_endpoint ep; - bool valid; -}; - -class HTTPClient { -private: - socket_handle_t sock; - SocketExtraOptions log_opts; - SocketExtraOptions* tcp_extra; - HTTPClientPolicy policy; - HTTPClientOrigin origin; - - HTTPResponseMsg error_response(int64_t err) { - HTTPResponseMsg resp{}; - resp.status_code = (HttpError)err; - return resp; - } - - HTTPResponseMsg receive_response(HTTPMethod request_method) { - HTTPResponseMsg resp{}; - string buf = string_repeat('\0', 0); - char tmp[512]; - - while (1) { - int hdr_end = find_crlfcrlf(buf.data, buf.length); - uint32_t start_ms = (uint32_t)get_time(); - uint32_t last_rx_ms = start_ms; - - while (hdr_end < 0) { - int64_t r = receive_from_socket(sock, tmp, sizeof(tmp), nullptr); - if (r == TCP_WOULDBLOCK) { - uint32_t now = (uint32_t)get_time(); - if ((now - last_rx_ms) > policy.common.header_idle_timeout_ms || (now - start_ms) > policy.common.header_total_timeout_ms) { - string_free(buf); - return error_response(SOCK_ERR_PROTO); - } - msleep(10); - continue; - } - if (r < 0) { - string_free(buf); - return error_response(r); - } - if (r == 0) { - string_free(buf); - return error_response(SOCK_ERR_PROTO); - } - string_append_bytes(&buf, tmp, (uint32_t)r); - last_rx_ms = (uint32_t)get_time(); - if (buf.length > policy.common.max_header_bytes) { - string_free(buf); - return error_response(SOCK_ERR_PROTO); - } - hdr_end = find_crlfcrlf(buf.data, buf.length); - } - - int status_line_end = strindex((char*)buf.data, "\r\n"); - if (status_line_end <= 0) { - string_free(buf); - return error_response(SOCK_ERR_PROTO); - } - - HTTPStatusLine status_line{}; - if (http_parse_status_line(buf.data, status_line_end, &status_line) != HTTP_PARSE_OK) { - string_free(buf); - return error_response(SOCK_ERR_PROTO); - } - - resp.status_code = (HttpError)status_line.status_code; - if (status_line.reason_len) { - resp.reason = string_repeat('\0', 0); - string_append_bytes(&resp.reason, buf.data + status_line.reason_off, status_line.reason_len); - } - - HTTPParseResult header_result = http_header_parse( - (char*)buf.data + status_line_end + 2, - (uint32_t)hdr_end - (uint32_t)(status_line_end + 2), - &policy.common, - &resp.headers_common, - &resp.extra_headers, - &resp.extra_header_count); - - if (header_result != HTTP_PARSE_OK) { - http_response_free(&resp); - string_free(buf); - if (sock) { - close_socket(sock); - sock = 0; - } - return error_response(SOCK_ERR_PROTO); - } - - uint32_t body_start = (uint32_t)hdr_end + 4; - uint32_t have = buf.length > body_start ? buf.length - body_start : 0; - uint32_t code = (uint32_t)resp.status_code; - bool no_body = request_method == HTTP_METHOD_HEAD || code == 204 || code == 304; - if (code >= 100 && code < 200) { - HTTPResponseMsg info = resp; - resp = HTTPResponseMsg{}; - if (info.reason.mem_length) string_free(info.reason); - http_headers_common_free(&info.headers_common); - http_headers_extra_free(info.extra_headers, info.extra_header_count); - - string next = string_repeat('\0', 0); - if (have) string_append_bytes(&next, buf.data + body_start, have); - string_free(buf); - buf = next; - continue; - } - - if (no_body) { - } else if (resp.headers_common.framing.chunked) { - HTTPChunkedDecoder dec; - http_chunked_decoder_init(&dec, &policy.common); - uint32_t used = 0; - HTTPParseResult chunk_result = have ? http_chunked_decoder_feed(&dec, buf.data + body_start, have, &used) : HTTP_PARSE_INCOMPLETE; - uint32_t body_start_ms = (uint32_t)get_time(); - uint32_t body_last_rx_ms = body_start_ms; - - while (chunk_result == HTTP_PARSE_INCOMPLETE) { - int64_t r = receive_from_socket(sock, tmp, sizeof(tmp), nullptr); - if (r == TCP_WOULDBLOCK) { - uint32_t now = (uint32_t)get_time(); - if ((now - body_last_rx_ms) > policy.common.body_idle_timeout_ms || (now - body_start_ms) > policy.common.body_total_timeout_ms) break; - msleep(1); - continue; - } - if (r <= 0) break; - chunk_result = http_chunked_decoder_feed(&dec, tmp, (uint32_t)r, &used); - body_last_rx_ms = (uint32_t)get_time(); - } - - if (chunk_result != HTTP_PARSE_OK) { - http_chunked_decoder_free(&dec); - http_response_free(&resp); - string_free(buf); - if (sock) { - close_socket(sock); - sock = 0; - } - return error_response(SOCK_ERR_PROTO); - } - - string decoded = dec.body; - dec.body = string{}; - http_chunked_decoder_free(&dec); - if (decoded.length) { - resp.body = decoded; - } else if (decoded.mem_length) string_free(decoded); - } else { - uint32_t need = resp.headers_common.framing.has_content_length ? resp.headers_common.fields.content_length : 0; - - if (resp.headers_common.framing.has_content_length) { - if (need > policy.common.max_body_bytes) { - http_response_free(&resp); - string_free(buf); - if (sock) { - close_socket(sock); - sock = 0; - } - return error_response(SOCK_ERR_PROTO); - } - - if (need) { - char *body_copy = (char*)zalloc(need); - if (!body_copy) { - http_response_free(&resp); - string_free(buf); - if (sock) { - close_socket(sock); - sock = 0; - } - return error_response(SOCK_ERR_SYS); - } - - uint32_t copied = have < need ? have : need; - if (copied) memcpy(body_copy, buf.data + body_start, copied); - - uint32_t body_start_ms = (uint32_t)get_time(); - uint32_t body_last_rx_ms = body_start_ms; - while (copied < need) { - int64_t r = receive_from_socket(sock, body_copy + copied, need - copied, nullptr); - if (r == TCP_WOULDBLOCK) { - uint32_t now = (uint32_t)get_time(); - if ((now - body_last_rx_ms) > policy.common.body_idle_timeout_ms || (now - body_start_ms) > policy.common.body_total_timeout_ms) break; - msleep(1); - continue; - } - if (r <= 0) break; - copied += (uint32_t)r; - body_last_rx_ms = (uint32_t)get_time(); - } - - if (copied < need) { - release(body_copy); - http_response_free(&resp); - string_free(buf); - if (sock) { - close_socket(sock); - sock = 0; - } - return error_response(SOCK_ERR_PROTO); - } - - resp.body = (string){body_copy, need, need}; - } - } else if (policy.allow_close_delimited) { - string body = string_repeat('\0', 0); - if (have) string_append_bytes(&body, buf.data + body_start, have); - - uint32_t body_start_ms = (uint32_t)get_time(); - uint32_t body_last_rx_ms = body_start_ms; - while (body.length <= policy.common.max_body_bytes) { - int64_t r = receive_from_socket(sock, tmp, sizeof(tmp), nullptr); - if (r == TCP_WOULDBLOCK) { - uint32_t now = (uint32_t)get_time(); - if ((now - body_last_rx_ms) > policy.common.body_idle_timeout_ms || (now - body_start_ms) > policy.common.body_total_timeout_ms) { - string_free(body); - http_response_free(&resp); - string_free(buf); - if (sock) { - close_socket(sock); - sock = 0; - } - return error_response(SOCK_ERR_PROTO); - } - msleep(1); - continue; - } - if (r < 0) { - string_free(body); - http_response_free(&resp); - string_free(buf); - if (sock) { - close_socket(sock); - sock = 0; - } - return error_response(r); - } - if (r == 0) break; - if (body.length + (uint32_t)r > policy.common.max_body_bytes) { - string_free(body); - http_response_free(&resp); - string_free(buf); - if (sock) { - close_socket(sock); - sock = 0; - } - return error_response(SOCK_ERR_PROTO); - } - string_append_bytes(&body, tmp, (uint32_t)r); - body_last_rx_ms = (uint32_t)get_time(); - } - - if (body.length) { - resp.body = body; - } else if (body.mem_length) string_free(body); - } - } - - netlog_socket_event_t ev{}; - ev.comp = NETLOG_COMP_HTTP_CLIENT; - ev.action = NETLOG_ACT_HTTP_RECV_RESPONSE; - ev.pid = get_current_proc_pid(); - ev.u0 = (uint32_t)resp.status_code; - ev.u1 = (uint32_t)resp.body.length; - ev.local_port = get_socket_local_port(sock); - net_l4_endpoint sock_remote_ep{}; - get_socket_remote_endpoint(sock, &sock_remote_ep); - ev.remote_ep = sock_remote_ep; - netlog_socket_event(&log_opts, &ev); - - string_free(buf); - return resp; - } - } - - -public: - explicit HTTPClient(const SocketExtraOptions* extra, const HTTPClientPolicyOptions* http_options) : sock(0), log_opts{}, tcp_extra(nullptr), policy(http_client_policy_from_options(http_options)), origin{} { - if (extra) log_opts = *extra; - - if (extra && (log_opts.flags & SOCK_OPT_DEBUG)) { - tcp_extra = (SocketExtraOptions*)zalloc(sizeof(SocketExtraOptions)); - if (tcp_extra) { - *tcp_extra = *extra; - tcp_extra->flags &= ~SOCK_OPT_DEBUG; - } - } - - sock = 0; - } - - ~HTTPClient() {close();} - - int32_t set_options(const HTTPClientPolicyOptions* http_options) { - policy = http_client_policy_from_options(http_options); - return SOCK_OK; - } - - int32_t connect_endpoint(const net_l4_endpoint* dst, const char* host_name = nullptr) { - if (!sock) sock = create_socket(PROTO_TCP, tcp_extra ? tcp_extra : &log_opts); - if (!sock) return SOCK_ERR_SYS; - int32_t r = connect_socket(sock, dst); - if (r >= 0) { - HTTPClientOrigin next{}; - next.valid = true; - if (dst) next.ep = *dst; - if (host_name) next.domain = string_from_literal(host_name); - if (origin.domain.mem_length) string_free(origin.domain); - origin = next; - } - - netlog_socket_event_t ev{}; - ev.comp = NETLOG_COMP_HTTP_CLIENT; - ev.action = NETLOG_ACT_CONNECT; - ev.pid = get_current_proc_pid(); - if (dst) ev.dst_ep = *dst; - ev.i0 = r; - - if (sock) { - ev.local_port = get_socket_local_port(sock); - net_l4_endpoint sock_remote_ep{}; - get_socket_remote_endpoint(sock, &sock_remote_ep); - ev.remote_ep = sock_remote_ep; - if (ev.remote_ep.ver) ev.dst_ep = ev.remote_ep; - } - - netlog_socket_event(&log_opts, &ev); - return r; - } - - int32_t connect_domain(const char* host, uint16_t port) { - if (!host || !port) return SOCK_ERR_INVAL; - net_l4_endpoint ep = socket_endpoint_select(host, port, (ip_version_t)0, DNS_USE_BOTH, 3000); - if (!ep.ver) return SOCK_ERR_DNS; - return connect_endpoint(&ep, host); - } - - HTTPResponseMsg send_request(const HTTPRequestMsg& req) { - HTTPRequestMsg curr = req; - curr.path = string_repeat('\0', 0); - if (req.path.data && req.path.length) string_append_bytes(&curr.path, req.path.data, req.path.length); - curr.headers_common.fields.host = string_repeat('\0', 0); - if (req.headers_common.fields.host.data && req.headers_common.fields.host.length) string_append_bytes(&curr.headers_common.fields.host, req.headers_common.fields.host.data, req.headers_common.fields.host.length); - - HTTPResponseMsg resp{}; - uint32_t redirects = policy.follow_redirects ? policy.max_redirects : 0; - for (uint32_t i = 0;; i++) { - if (!sock) { - resp = error_response(SOCK_ERR_STATE); - break; - } - - if (!curr.host_override && curr.version != HTTP_VERSION_10 && !curr.headers_common.fields.host.length && origin.valid) { - if (origin.domain.data) { - uint16_t host_port = origin.ep.port; - bool ipv6 = str_has_char(origin.domain.data, origin.domain.length, ':') >= 0; - if (host_port && host_port != 80) { - if (ipv6) curr.headers_common.fields.host = string_format("[%.*s]:%i", (int)origin.domain.length, origin.domain.data, (int)host_port); - else curr.headers_common.fields.host = string_format("%.*s:%i", (int)origin.domain.length, origin.domain.data, (int)host_port); - } else { - if (ipv6) curr.headers_common.fields.host = string_format("[%.*s]", (int)origin.domain.length, origin.domain.data); - else curr.headers_common.fields.host = string_from_literal_length(origin.domain.data, origin.domain.length); - } - } else if (origin.ep.ver) { - char ip[48]; - bool ipv6 = false; - uint16_t ep_port = 0; - net_ep_split(&origin.ep, ip, sizeof(ip), &ipv6, &ep_port); - uint16_t host_port = ep_port; - if (host_port && host_port != 80) { - if (ipv6) curr.headers_common.fields.host = string_format("[%s]:%i", ip, (int)host_port); - else curr.headers_common.fields.host = string_format("%s:%i", ip, (int)host_port); - } else { - if (ipv6) curr.headers_common.fields.host = string_format("[%s]", ip); - else curr.headers_common.fields.host = string_from_literal(ip); - } - } - } - - string out = http_request_builder(&curr); - uint32_t out_len = out.length; - - uint32_t off = 0; - int64_t sent = 0; - while (off < out_len) { - int64_t r = send_on_socket(sock, (void*)(out.data + off), out_len - off); - if (r == TCP_WOULDBLOCK) { - msleep(5); - continue; - } - if (r < 0) { - sent = r; - break; - } - off += (uint32_t)r; - } - if (sent >= 0) sent = (int64_t)off; - - netlog_socket_event_t ev{}; - ev.comp = NETLOG_COMP_HTTP_CLIENT; - ev.action = NETLOG_ACT_HTTP_SEND_REQUEST; - ev.pid = get_current_proc_pid(); - ev.u0 = out_len; - ev.i0 = sent; - ev.local_port = get_socket_local_port(sock); - net_l4_endpoint sock_remote_ep{}; - get_socket_remote_endpoint(sock, &sock_remote_ep); - ev.remote_ep = sock_remote_ep; - - char pathbuf[128]; - if (curr.path.length && curr.path.data) { - uint32_t n = curr.path.length; - if (n > sizeof(pathbuf) - 1) n = sizeof(pathbuf) - 1; - memcpy(pathbuf, curr.path.data, n); - pathbuf[n] = 0; - ev.s0 = pathbuf; - } - - netlog_socket_event(&log_opts, &ev); - string_free(out); - - if (sent < 0) { - resp = error_response(sent); - break; - } - - resp = receive_response(curr.method); - uint32_t code = (uint32_t)resp.status_code; - bool redirect = code == HTTP_MOVED_PERMANENTLY || code == HTTP_FOUND || code == HTTP_SEE_OTHER || code == HTTP_TEMPORARY_REDIRECT || code == HTTP_PERMANENT_REDIRECT; - if (!redirect || i >= redirects || !resp.headers_common.fields.location.length) break; - - ParsedURL url = parse_url(resp.headers_common.fields.location.data, resp.headers_common.fields.location.length); - if (!url.ok) break; - - bool absolute = url.scheme.size || url.host.size; - uint16_t next_port = origin.ep.port; - if (url.scheme.size) { - if (url.scheme.size == 5 && strncmp_case((const char*)url.scheme.ptr, "https", true, 5) == 0) break; - if (!(url.scheme.size == 4 && strncmp_case((const char*)url.scheme.ptr, "http", true, 4) == 0)) break; - } - if (absolute && (!url.host.ptr || !url.host.size)) break; - - string next_path = url_request_path(&url, &curr.path); - if (!next_path.length) break; - - if (curr.path.mem_length) string_free(curr.path); - curr.path = next_path; - - if (absolute) { - next_port = url.port ? url.port : 80; - string next_domain = string_from_literal_length((const char*)url.host.ptr, url.host.size); - if (!curr.host_override) { - if (curr.headers_common.fields.host.mem_length) string_free(curr.headers_common.fields.host); - curr.headers_common.fields.host = string_repeat('\0', 0); - } - - if (sock) { - close_socket(sock); - sock = 0; - } - sock = next_domain.data ? create_socket(PROTO_TCP, tcp_extra ? tcp_extra : &log_opts) : 0; - int32_t rr = sock ? SOCK_OK : SOCK_ERR_SYS; - if (rr >= 0) rr = connect_domain(next_domain.data, next_port); - if (next_domain.mem_length) string_free(next_domain); - if (rr < 0) { - http_response_free(&resp); - resp.status_code = (HttpError)rr; - break; - } - } else { - net_l4_endpoint reconnect_ep = origin.ep; - string reconnect_domain = string{}; - if (origin.domain.data) reconnect_domain = string_from_literal(origin.domain.data); - - if (sock) { - close_socket(sock); - sock = 0; - } - sock = origin.valid ? create_socket(PROTO_TCP, tcp_extra ? tcp_extra : &log_opts) : 0; - int32_t rr = sock ? SOCK_OK : SOCK_ERR_SYS; - if (rr >= 0) rr = reconnect_domain.data ? connect_domain(reconnect_domain.data, reconnect_ep.port) : connect_endpoint(&reconnect_ep); - if (reconnect_domain.mem_length) string_free(reconnect_domain); - if (rr < 0) { - http_response_free(&resp); - resp.status_code = (HttpError)rr; - break; - } - } - - if (code == HTTP_SEE_OTHER && curr.method != HTTP_METHOD_HEAD) { - curr.method = HTTP_METHOD_GET; - curr.body = string{}; - curr.headers_common.framing.chunked = 0; - curr.headers_common.framing.has_content_length = 0; - curr.headers_common.fields.content_length = 0; - } - - http_response_free(&resp); - } - - if (curr.path.mem_length) string_free(curr.path); - if (curr.headers_common.fields.host.mem_length) string_free(curr.headers_common.fields.host); - return resp; - } - - int32_t close() { - netlog_socket_event_t ev{}; - ev.comp = NETLOG_COMP_HTTP_CLIENT; - ev.action = NETLOG_ACT_CLOSE; - ev.pid = get_current_proc_pid(); - - if (sock) { - ev.local_port = get_socket_local_port(sock); - net_l4_endpoint sock_remote_ep{}; - get_socket_remote_endpoint(sock, &sock_remote_ep); - ev.remote_ep = sock_remote_ep; - } - - int32_t r = SOCK_ERR_STATE; - if (sock) { - r = close_socket(sock); - sock = 0; - } - ev.i0 = r; - netlog_socket_event(&log_opts, &ev); - - if (tcp_extra) release(tcp_extra); - tcp_extra = nullptr; - - if (origin.domain.mem_length) string_free(origin.domain); - origin = HTTPClientOrigin{}; - log_opts.flags &= ~SOCK_OPT_DEBUG; - return r; - } -}; diff --git a/kernel/networking/application_layer/socket_http_server.hpp b/kernel/networking/application_layer/socket_http_server.hpp deleted file mode 100644 index 18a918a7..00000000 --- a/kernel/networking/application_layer/socket_http_server.hpp +++ /dev/null @@ -1,580 +0,0 @@ -#pragma once -#include "console/kio.h" -#include "networking/transport_layer/csocket.h" -#include "networking/transport_layer/tcp.h" -#include "networking/net_logger/net_logger.h" -#include "http.h" -#include "std/std.h" -#include "net/socket_types.h" -#include "syscalls/syscalls.h" -#include "process/scheduler.h" - -struct HTTPConnection { - socket_handle_t client; - string carry_buf; - uint32_t request_count; - HTTPMethod current_method; - bool close_after_response; - bool send_keep_alive_header; -}; - -class HTTPServer { -private: - socket_handle_t sock; - SocketExtraOptions log_opts; - SocketExtraOptions* tcp_extra; - HTTPServerPolicy policy; - -public: - explicit HTTPServer(const SocketExtraOptions* extra, const HTTPServerPolicyOptions* http_options) : sock(0), log_opts{}, tcp_extra(nullptr), policy(http_server_policy_from_options(http_options)) { - if (extra) log_opts = *extra; - - if (extra && (log_opts.flags & SOCK_OPT_DEBUG)) { - tcp_extra = (SocketExtraOptions*)zalloc(sizeof(SocketExtraOptions)); - if (tcp_extra) { - *tcp_extra = *extra; - tcp_extra->flags &= ~SOCK_OPT_DEBUG; - } - } - - sock = 0; - } - - ~HTTPServer() { close(); } - - int32_t set_options(const HTTPServerPolicyOptions* http_options) { - policy = http_server_policy_from_options(http_options); - return SOCK_OK; - } - - int32_t bind(const SockBindSpec& spec, uint16_t port) { - uint16_t p = port; - if (!sock) sock = create_socket(PROTO_TCP, tcp_extra ? tcp_extra : &log_opts); - if (!sock) return SOCK_ERR_SYS; - int32_t r = bind_socket(sock, &spec, p); - - netlog_socket_event_t ev{}; - ev.comp = NETLOG_COMP_HTTP_SERVER; - ev.action = NETLOG_ACT_BIND; - ev.pid = get_current_proc_pid(); - ev.u0 = p; - ev.i0 = r; - netlog_socket_event(&log_opts, &ev); - return r; - } - - int32_t listen(int backlog = 4) { - int b = backlog; - int32_t r = sock ? listen_on(sock, b) : SOCK_ERR_STATE; - - netlog_socket_event_t ev{}; - ev.comp = NETLOG_COMP_HTTP_SERVER; - ev.action = NETLOG_ACT_LISTEN; - ev.pid = get_current_proc_pid(); - ev.u0 = (uint32_t)b; - ev.i0 = r; - netlog_socket_event(&log_opts, &ev); - return r; - } - - HTTPConnection* accept() { - if (!sock) return nullptr; - socket_handle_t child = accept_on_socket(sock); - if (!child) return nullptr; - - HTTPConnection* conn = (HTTPConnection*)zalloc(sizeof(HTTPConnection)); - if (!conn) { - close_socket(child); - return nullptr; - } - - conn->client = child; - conn->carry_buf = string{nullptr, 0, 0}; - conn->request_count = 0; - conn->current_method = HTTP_METHOD_GET; - conn->close_after_response = true; - conn->send_keep_alive_header = false; - - netlog_socket_event_t ev{}; - ev.comp = NETLOG_COMP_HTTP_SERVER; - ev.action = NETLOG_ACT_ACCEPT; - ev.pid = get_current_proc_pid(); - ev.i0 = (int64_t)child; - ev.local_port = get_socket_local_port(child); - net_l4_endpoint child_remote_ep{}; - get_socket_remote_endpoint(child, &child_remote_ep); - ev.remote_ep = child_remote_ep; - netlog_socket_event(&log_opts, &ev); - return conn; - } - - HTTPRequestMsg recv_request(HTTPConnection* conn) { - HTTPRequestMsg req{}; - if (!conn || !conn->client) return req; - - string buf = string_repeat('\0', 0); - if (conn->carry_buf.length) { - string_append_bytes(&buf, conn->carry_buf.data, conn->carry_buf.length); - string_free(conn->carry_buf); - conn->carry_buf = string{nullptr, 0, 0}; - } - - char tmp[2048]; - int hdr_end = find_crlfcrlf(buf.data, buf.length); - uint32_t consumed = 0; - uint32_t start_ms = (uint32_t)get_time(); - uint32_t last_rx_ms = start_ms; - bool bad_request = false; - HttpError reject_status = HTTP_BAD_REQUEST; - char* body_copy = nullptr; - - while (hdr_end < 0) { - int64_t r = receive_from_socket(conn->client, tmp, sizeof(tmp), nullptr); - if (r == TCP_WOULDBLOCK) { - uint32_t now = (uint32_t)get_time(); - if ((uint32_t)(now - last_rx_ms) > policy.common.header_idle_timeout_ms || (uint32_t)(now - start_ms) > policy.common.header_total_timeout_ms) { - string_free(buf); - return req; - } - msleep(10); - continue; - } - if (r <= 0) { - string_free(buf); - return req; - } - string_append_bytes(&buf, tmp, (uint32_t)r); - last_rx_ms = (uint32_t)get_time(); - if (buf.length > policy.common.max_header_bytes) { - bad_request = true; - reject_status = HTTP_HEADER_FIELDS_TOO_LARGE; - break; - } - hdr_end = find_crlfcrlf(buf.data, buf.length); - } - - if (!bad_request) { - uint32_t line_end = 0; - while (line_end + 1u < (uint32_t)hdr_end) { - if (buf.data[line_end] == '\r' && buf.data[line_end + 1] == '\n') break; - line_end++; - } - - if (line_end > policy.common.max_start_line) { - bad_request = true; - reject_status = HTTP_URI_TOO_LONG; - } - - HTTPRequestLine line{}; - HTTPParseResult line_result = HTTP_PARSE_OK; - if (!bad_request) { - line_result = http_parse_request_line(buf.data, line_end, &line); - if (line_result != HTTP_PARSE_OK) { - bad_request = true; - reject_status = http_parse_result_status(line_result); - } else if (!http_method_allowed(policy.allowed_methods, line.method)) { - bad_request = true; - reject_status = HTTP_METHOD_NOT_ALLOWED; - } - } - - if (!bad_request) { - const char* target = buf.data + line.target_off; - uint32_t target_len = line.target_len; - req.method = line.method; - req.version = line.version; - req.path = string_repeat('\0', 0); - - if (target_len >= 7 && memcmp(target, "http://", 7) == 0) { - if (!policy.allow_absolute_uri) { - bad_request = true; - reject_status = HTTP_BAD_REQUEST; - } else { - uint32_t k = 7; - while (k < target_len && target[k] != '/') k++; - if (k < target_len) string_append_bytes(&req.path, target + k, target_len - k); - else string_append_bytes(&req.path, "/", 1); - } - } else if (target_len >= 8 && memcmp(target, "https://", 8) == 0) { - if (!policy.allow_absolute_uri) { - bad_request = true; - reject_status = HTTP_BAD_REQUEST; - } else { - uint32_t k = 8; - while (k < target_len && target[k] != '/') k++; - if (k < target_len) string_append_bytes(&req.path, target + k, target_len - k); - else string_append_bytes(&req.path, "/", 1); - } - } else string_append_bytes(&req.path, target, target_len); - - if (!bad_request && (!req.path.length || req.path.length > policy.common.max_path_len)) { - bad_request = true; - reject_status = req.path.length > policy.common.max_path_len ? HTTP_URI_TOO_LONG : HTTP_BAD_REQUEST; - } - } - - if (!bad_request) { - HTTPParseResult header_result = http_header_parse( - (char*)buf.data + line_end + 2, - (uint32_t)hdr_end - (line_end + 2), - &policy.common, - &req.headers_common, - &req.extra_headers, - &req.extra_header_count - ); - - if (header_result != HTTP_PARSE_OK) { - bad_request = true; - reject_status = http_parse_result_status(header_result); - } else if (policy.require_host_http11 && line.version == HTTP_VERSION_11 && !req.headers_common.fields.host.length) { - bad_request = true; - reject_status = HTTP_BAD_REQUEST; - } else { - conn->request_count++; - conn->current_method = req.method; - conn->close_after_response = true; - conn->send_keep_alive_header = false; - if (policy.allow_keep_alive) { - if (line.version == HTTP_VERSION_11) conn->close_after_response = req.headers_common.framing.connection_close != 0; - else if (line.version == HTTP_VERSION_10 && req.headers_common.framing.connection_keep_alive) { - conn->close_after_response = false; - conn->send_keep_alive_header = true; - } - } - if (policy.max_keepalive_requests && conn->request_count >= policy.max_keepalive_requests) conn->close_after_response = true; - } - } - - uint32_t body_start = hdr_end + 4; - uint32_t have = buf.length > body_start ? buf.length - body_start : 0; - - if (!bad_request && req.headers_common.framing.expect_continue) { - if (req.headers_common.framing.has_content_length && req.headers_common.fields.content_length > policy.common.max_body_bytes) { - bad_request = true; - reject_status = HTTP_PAYLOAD_TOO_LARGE; - } else { - HTTPResponseMsg cont{}; - cont.status_code = HTTP_CONTINUE; - send_response(conn, cont); - } - } - uint32_t need = req.headers_common.framing.has_content_length ? req.headers_common.fields.content_length : 0; - consumed = body_start; - - if (!bad_request && req.headers_common.framing.chunked) { - HTTPChunkedDecoder dec; - http_chunked_decoder_init(&dec, &policy.common); - - uint32_t used = 0; - HTTPParseResult chunk_result = have ? http_chunked_decoder_feed(&dec, buf.data + body_start, have, &used) : HTTP_PARSE_INCOMPLETE; - uint32_t body_start_ms = (uint32_t)get_time(); - uint32_t body_last_rx_ms = body_start_ms; - consumed = body_start + used; - - while (chunk_result == HTTP_PARSE_INCOMPLETE) { - int64_t r = receive_from_socket(conn->client, tmp, sizeof(tmp), nullptr); - if (r == TCP_WOULDBLOCK) { - uint32_t now = (uint32_t)get_time(); - if ((now - body_last_rx_ms) > policy.common.body_idle_timeout_ms || (now - body_start_ms) > policy.common.body_total_timeout_ms) break; - msleep(2); - continue; - } - if (r <= 0) break; - used = 0; - chunk_result = http_chunked_decoder_feed(&dec, tmp, (uint32_t)r, &used); - body_last_rx_ms = (uint32_t)get_time(); - consumed = buf.length; - if (chunk_result == HTTP_PARSE_OK && used < (uint32_t)r && !conn->carry_buf.length) { - conn->carry_buf = string_repeat('\0', 0); - string_append_bytes(&conn->carry_buf, tmp + used, (uint32_t)r - used); - } - } - - if (chunk_result != HTTP_PARSE_OK) { - bad_request = true; - reject_status = http_parse_result_status(chunk_result); - } else { - string decoded = dec.body; - dec.body = string{}; - if (decoded.length) { - req.body = decoded; - } else if (decoded.mem_length) string_free(decoded); - } - http_chunked_decoder_free(&dec); - } else { - if (!bad_request && need > policy.common.max_body_bytes) { - bad_request = true; - reject_status = HTTP_PAYLOAD_TOO_LARGE; - } - - if (!bad_request && need > 0) { - body_copy = (char*)zalloc(need); - if (!body_copy) { - bad_request = true; - reject_status = HTTP_INTERNAL_SERVER_ERROR; - } else { - uint32_t copied = have < need ? have : need; - if (copied) memcpy(body_copy, buf.data + body_start, copied); - - uint32_t body_start_ms = (uint32_t)get_time(); - uint32_t body_last_rx_ms = body_start_ms; - while (copied < need) { - int64_t r = receive_from_socket(conn->client, body_copy + copied, need - copied, nullptr); - if (r == TCP_WOULDBLOCK) { - uint32_t now = (uint32_t)get_time(); - if ((now - body_last_rx_ms) > policy.common.body_idle_timeout_ms || (now - body_start_ms) > policy.common.body_total_timeout_ms) { - bad_request = true; - break; - } - msleep(2); - continue; - } - if (r <= 0) { - bad_request = true; - break; - } - copied += (uint32_t)r; - body_last_rx_ms = (uint32_t)get_time(); - } - } - } - - if (!bad_request) consumed = body_start + need; - if (body_copy && !bad_request) { - req.body = (string){body_copy, need, need}; - } - } - } - - if (bad_request) { - if (body_copy) release(body_copy); - const char *reason = http_status_reason(reject_status); - string body = policy.send_error_body ? string_format("%s\n", reason) : string{}; - HTTPResponseMsg res{}; - HTTPHeader allow_header{}; - string allow_value{}; - static char allow_key[] = "Allow"; - - res.status_code = reject_status; - res.headers_common.fields.content_length = body.length; - res.headers_common.framing.has_content_length = 1; - if (policy.error_content_type) res.headers_common.fields.content_type = string_from_literal(policy.error_content_type); - res.body = body; - - if (reject_status == HTTP_METHOD_NOT_ALLOWED) { - allow_value = http_methods_allow_header(policy.allowed_methods); - allow_header.key = string{allow_key, sizeof(allow_key) - 1, 0}; - allow_header.value = allow_value; - res.extra_headers = &allow_header; - res.extra_header_count =1; - } - - conn->close_after_response = true; - send_response(conn, res); - if (conn->client) { - close_socket(conn->client); - conn->client = 0; - } - - http_request_free(&req); - http_headers_common_free(&res.headers_common); - if (allow_value.mem_length) string_free(allow_value); - if (body.mem_length) string_free(body); - string_free(buf); - return HTTPRequestMsg{}; - } - - netlog_socket_event_t ev{}; - ev.comp = NETLOG_COMP_HTTP_SERVER; - ev.action = NETLOG_ACT_HTTP_RECV_REQUEST; - ev.pid = get_current_proc_pid(); - ev.u0 = (uint32_t)req.method; - ev.u1 = (uint32_t)req.path.length; - ev.i0 = (int64_t)req.body.length; - ev.local_port = get_socket_local_port(conn->client); - net_l4_endpoint conn_remote_ep{}; - get_socket_remote_endpoint(conn->client, &conn_remote_ep); - ev.remote_ep = conn_remote_ep; - - char pathbuf[128]; - if (req.path.length && req.path.data) { - uint32_t n = req.path.length; - if (n > sizeof(pathbuf) - 1) n = sizeof(pathbuf) - 1; - memcpy(pathbuf, req.path.data, n); - pathbuf[n] = 0; - ev.s0 = pathbuf; - } - - netlog_socket_event(&log_opts, &ev); - - if (consumed < buf.length && !conn->carry_buf.length) { - conn->carry_buf = string_repeat('\0', 0); - string_append_bytes(&conn->carry_buf, buf.data + consumed, buf.length - consumed); - } - - string_free(buf); - return req; - } - - int32_t send_response(HTTPConnection* conn, const HTTPResponseMsg& res) { - if (!conn || !conn->client) return SOCK_ERR_STATE; - - uint32_t code = (uint32_t)res.status_code; - bool informational = code >= 100 && code < 200; - bool suppress_body = informational || conn->current_method == HTTP_METHOD_HEAD || code == 204 || code == 304; - bool send_chunked = suppress_body ? false : res.headers_common.framing.chunked; - HTTPResponseMsg head = res; - bool explicit_close = http_header_value_has_token(res.headers_common.fields.connection.data, res.headers_common.fields.connection.length, "close", 5); - bool explicit_keep_alive = http_header_value_has_token(res.headers_common.fields.connection.data, res.headers_common.fields.connection.length, "keep-alive", 10); - bool close_after_send = !informational && (conn->close_after_response || explicit_close); - static char conn_close_data[] = "close"; - static char conn_keep_alive_data[] = "keep-alive"; - const string conn_close = {conn_close_data, sizeof(conn_close_data) - 1, 0 }; - const string conn_keep_alive = {conn_keep_alive_data, sizeof(conn_keep_alive_data) - 1, 0 }; - - if (!informational) { - if (close_after_send) head.headers_common.fields.connection = conn_close; - else if (!res.headers_common.fields.connection.length && conn->send_keep_alive_header) head.headers_common.fields.connection = conn_keep_alive; - else if (explicit_keep_alive) head.headers_common.fields.connection = res.headers_common.fields.connection; - } - - if (suppress_body) { - head.body = string{}; - head.headers_common.framing.chunked = 0; - } - - uint32_t body_len = (!send_chunked && !suppress_body && res.body.data && res.body.length) ? (uint32_t)res.body.length : 0; - if (!send_chunked) { - head.body = string{}; - if (body_len && !head.headers_common.framing.has_content_length) { - head.headers_common.fields.content_length = body_len; - head.headers_common.framing.has_content_length = 1; - } - } - string out = http_response_builder(&head); - uint32_t out_len = out.length + body_len; - int64_t sent = 0; - uint32_t start_ms = (uint32_t)get_time(); - uint32_t progress_ms = start_ms; - const uint8_t* first_ptr = (const uint8_t*)out.data; - uint32_t first_len = out.length; - uint32_t first_body_len = 0; - uint32_t body_off = 0; - uint8_t* combo = nullptr; - - if (body_len && out.length < 1460) { - first_body_len = body_len; - uint32_t room = 1460 - out.length; - if (first_body_len > room) first_body_len = room; - - if (first_body_len) { - combo = (uint8_t*)zalloc(out.length + first_body_len); - if (combo) { - memcpy(combo, out.data, out.length); - memcpy(combo + out.length, (const void*)res.body.data, first_body_len); - - first_ptr = combo; - first_len = out.length + first_body_len; - } else first_body_len = 0; - } - } - - uint32_t off = 0; - while (sent >= 0 && off < first_len) { - int64_t r = send_on_socket(conn->client, (void*)(first_ptr + off), first_len - off); - uint32_t now = (uint32_t)get_time(); - - if (r == TCP_WOULDBLOCK || r == 0) { - if ((now - progress_ms) > 3000 || (now - start_ms) > 30000) { - sent = SOCK_ERR_SYS; - break; - } - msleep(2); - continue; - } - if (r < 0) { - sent = r; - break; - } - off += (uint32_t)r; - progress_ms = now; - } - - if (sent >= 0) body_off = first_body_len; - if (sent >= 0 && body_len) { - const uint8_t* body = (const uint8_t*)res.body.data; - - while (body_off < body_len) { - uint32_t ask = body_len - body_off; - if (ask > 16384) ask = 16384; - int64_t r = send_on_socket(conn->client, (void*)(body + body_off), ask); - uint32_t now = (uint32_t)get_time(); - - if (r == TCP_WOULDBLOCK || r == 0) { - if ((now - progress_ms) > 3000 || (now - start_ms) > 30000) { - sent = SOCK_ERR_SYS; - break; - } - msleep(2); - continue; - } - if (r < 0) { - sent = r; - break; - } - body_off += (uint32_t)r; - progress_ms = now; - } - } - - if (sent >= 0) sent = (int64_t)out.length + body_off; - if (combo) release(combo); - - netlog_socket_event_t ev{}; - ev.comp = NETLOG_COMP_HTTP_SERVER; - ev.action = NETLOG_ACT_HTTP_SEND_RESPONSE; - ev.pid = get_current_proc_pid(); - ev.u0 = code; - ev.u1 = out_len; - ev.i0 = sent; - ev.local_port = get_socket_local_port(conn->client); - net_l4_endpoint conn_remote_ep{}; - get_socket_remote_endpoint(conn->client, &conn_remote_ep); - ev.remote_ep = conn_remote_ep; - netlog_socket_event(&log_opts, &ev); - - string_free(out); - if (sent >= 0 && close_after_send) if (conn && conn->client) { - close_socket(conn->client); - conn->client = 0; - } - return sent < 0 ? (int32_t)sent : SOCK_OK; - } - - int32_t close() { - int32_t r = sock ? SOCK_OK : SOCK_ERR_STATE; - netlog_socket_event_t ev{}; - ev.comp = NETLOG_COMP_HTTP_SERVER; - ev.action = NETLOG_ACT_CLOSE; - ev.pid = get_current_proc_pid(); - ev.i0 = r; - if (sock) { - ev.local_port = get_socket_local_port(sock); - net_l4_endpoint sock_remote_ep{}; - get_socket_remote_endpoint(sock, &sock_remote_ep); - ev.remote_ep = sock_remote_ep; - } - netlog_socket_event(&log_opts, &ev); - - if (sock) { - r = close_socket(sock); - sock = 0; - } - - if (tcp_extra) release(tcp_extra); - tcp_extra = nullptr; - - log_opts.flags &= ~SOCK_OPT_DEBUG; - return r; - } -}; \ No newline at end of file diff --git a/kernel/networking/application_layer/ssdp_daemon.c b/kernel/networking/application_layer/ssdp_daemon.c index e91deb74..c7ee5c63 100644 --- a/kernel/networking/application_layer/ssdp_daemon.c +++ b/kernel/networking/application_layer/ssdp_daemon.c @@ -7,10 +7,13 @@ #include "net/network_types.h" #include "net/socket_types.h" #include "networking/transport_layer/csocket.h" +#include "networking/transport_layer/trans_utils.h" #include "networking/application_layer/ssdp.h" +#include "networking/internet_layer/ipv4_utils.h" #include "networking/internet_layer/ipv6_utils.h" #include "networking/interface_manager.h" #include "math/math.h" +#include "random/random.h" //at the moment it's a very basic version. it's a protocol still in use but only in few cases //it;s used in some printers, upnp, local video streaming and various other things @@ -67,11 +70,8 @@ static void ssdp_send_notify(bool alive) { string msg = ssdp_build_notify(alive, ssdp_sockets[i].ver == IP_VER6); net_l4_endpoint dst; - memset(&dst, 0, sizeof(dst)); - dst.ver = ssdp_sockets[i].ver; - dst.port = 1900; - if (dst.ver == IP_VER4) memcpy(dst.ip, &ssdp_host_v4, 4); - else memcpy(dst.ip, ssdp_host_v6, 16); + if (ssdp_sockets[i].ver == IP_VER4) make_ep(&ssdp_host_v4, 1900, IP_VER4, &dst); + else make_ep(ssdp_host_v6, 1900, IP_VER6, &dst); (void)send_to_socket(s, &dst, (void*)msg.data, msg.length); string_free(msg); } @@ -80,9 +80,7 @@ static void ssdp_send_notify(bool alive) { int ssdp_daemon_entry(int argc, char* argv[]) { (void)argc; (void)argv; - uint64_t virt_timer; - asm volatile ("mrs %0, cntvct_el0" : "=r"(virt_timer)); - rng_seed(&ssdp_rng, virt_timer); + rng_init_random(&ssdp_rng); ipv6_make_multicast(0x02, IPV6_MCAST_SSDP, NULL, ssdp_host_v6); @@ -93,7 +91,7 @@ int ssdp_daemon_entry(int argc, char* argv[]) { for (uint8_t j = 0; j < MAX_IPV4_PER_INTERFACE && ssdp_socket_count < MAX_L3_INTERFACES; j++) { l3_ipv4_interface_t *v4 = l2->l3_v4[j]; - if (!v4 || v4->mode == IPV4_CFG_DISABLED || v4->is_localhost || !v4->ip) continue; + if (!ipv4_l3_is_ready(v4) || v4->is_localhost) continue; uint32_t ttl = 2; SocketExtraOptions opt; @@ -129,7 +127,7 @@ int ssdp_daemon_entry(int argc, char* argv[]) { for (uint8_t j = 0; j < MAX_IPV6_PER_INTERFACE && ssdp_socket_count < MAX_L3_INTERFACES; j++) { l3_ipv6_interface_t *v6 = l2->l3_v6[j]; - if (!v6 || v6->cfg == IPV6_CFG_DISABLE || v6->dad_state != IPV6_DAD_OK || v6->is_localhost || !v6->ip[0]) continue; + if (!ipv6_l3_is_ready(v6) || v6->is_localhost) continue; uint32_t ttl = 2; SocketExtraOptions opt; diff --git a/kernel/networking/drivers/net_bus.cpp b/kernel/networking/drivers/net_bus.cpp index 511513fb..7ff939a7 100644 --- a/kernel/networking/drivers/net_bus.cpp +++ b/kernel/networking/drivers/net_bus.cpp @@ -49,11 +49,7 @@ static void add_loopback(){ if (g_lo_added) return; if (g_count >= MAX_L2_INTERFACES) return; net_nic_desc_t* d = &g_nics[g_count++]; - d->drv = nullptr; - - memset(d->ifname, 0, sizeof(d->ifname)); - memset(d->hw_ifname, 0, sizeof(d->hw_ifname)); - memset(d->mac, 0, sizeof(d->mac)); + memset(d, 0, sizeof(*d)); strncpy(d->ifname, "lo0", sizeof(d->ifname)); strncpy(d->hw_ifname, "loopback", sizeof(d->hw_ifname)); diff --git a/kernel/networking/interface_manager.c b/kernel/networking/interface_manager.c index 3f51565e..d4c5f4ed 100644 --- a/kernel/networking/interface_manager.c +++ b/kernel/networking/interface_manager.c @@ -1,5 +1,6 @@ #include "interface_manager.h" #include "std/memory.h" +#include "std/string.h" #include "networking/link_layer/arp.h" #include "networking/link_layer/ndp.h" #include "networking/internet_layer/ipv4_route.h" @@ -85,14 +86,7 @@ uint8_t l2_interface_create(const char *name, void *driver_ctx, uint16_t base_me memset(itf, 0, sizeof(*itf)); itf->ifindex = (uint8_t)(slot + 1); - int i = 0; - if (name) { - while (name[i] && i < 15) { - itf->name[i] = name[i]; - i++; - } - } - itf->name[i] = 0; + if (name) strncpy(itf->name, name, sizeof(itf->name)); itf->driver_context = driver_ctx; itf->base_metric = base_metric; @@ -306,23 +300,17 @@ static bool v6_ip_exists_anywhere(const uint8_t ip[16]){ return false; } -static bool v6_overlap_intra_l2(uint8_t ifindex, const uint8_t ip[16], uint8_t prefix_len){ +static bool v6_overlap_intra_l2(uint8_t ifindex, const uint8_t ip[16], uint8_t prefix_len, const l3_ipv6_interface_t *except){ 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; + if (ipv6_common_prefix_len(ip, x->ip) >= minp) return true; } return false; } @@ -553,7 +541,7 @@ uint8_t l3_ipv6_add_to_interface(uint8_t ifindex, const uint8_t ip[16], uint8_t 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 (v6_overlap_intra_l2(ifindex, ip, prefix_len, NULL)) return 0; } } } @@ -696,24 +684,7 @@ bool l3_ipv6_update(uint8_t l3_id, const uint8_t ip[16], uint8_t prefix_len, con if (ipv6_is_multicast(ip)) return false; if (ipv6_is_loopback(ip) && (l2->kind != NET_IFK_LOCALHOST)) return false; if (ipv6_cmp(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; - } - } + if (v6_overlap_intra_l2(l2->ifindex, ip, prefix_len, n)) return false; } } else { return false; @@ -914,8 +885,7 @@ void l3_init_localhost_ipv6(void){ if (!g_v6[i].node.l2 || g_v6[i].node.l2 != lo) continue; if (ipv6_is_loopback(g_v6[i].node.ip)) return; } - uint8_t zero16[16]={0}; - (void)l3_ipv6_add_to_interface(lo->ifindex, loop6, 128, zero16, IPV6_CFG_STATIC, IPV6_ADDRK_GLOBAL); + (void)l3_ipv6_add_to_interface(lo->ifindex, loop6, 128, (const uint8_t[16]){0}, IPV6_CFG_STATIC, IPV6_ADDRK_GLOBAL); uint8_t multi[16]; ipv6_make_multicast(2, IPV6_MCAST_ALL_NODES, loop6, multi); @@ -944,7 +914,7 @@ void ifmgr_autoconfig_l2(uint8_t ifindex){ l3_ipv6_interface_t *x=&g_v6[i].node; if (!x->l2 || x->l2->ifindex != ifindex) continue; - if (!has_lla) if (ipv6_is_linklocal(x->ip) && x->cfg != IPV6_CFG_DISABLE) has_lla=true; + if (!has_lla && ipv6_is_linklocal(x->ip) && x->cfg != IPV6_CFG_DISABLE) has_lla = true; if (!has_gua) { if ((x->kind == IPV6_ADDRK_GLOBAL) && x->cfg != IPV6_CFG_DISABLE) has_gua=true; @@ -955,10 +925,9 @@ void ifmgr_autoconfig_l2(uint8_t ifindex){ } if (!has_lla){ uint8_t lla[16]; - uint8_t zero16[16] = {0}; ipv6_make_lla_from_mac(ifindex, lla); - (void)l3_ipv6_add_to_interface(ifindex, lla, 64, zero16, IPV6_CFG_SLAAC, IPV6_ADDRK_LINK_LOCAL); + (void)l3_ipv6_add_to_interface(ifindex, lla, 64, (const uint8_t[16]){0}, IPV6_CFG_SLAAC, IPV6_ADDRK_LINK_LOCAL); uint8_t m[16]; ipv6_make_multicast(2, IPV6_MCAST_ALL_NODES, lla, m); @@ -967,10 +936,9 @@ void ifmgr_autoconfig_l2(uint8_t ifindex){ if (!has_gua) { uint8_t ph[16]; - uint8_t zero16[16]={0}; ipv6_make_placeholder_gua(ph); - (void)l3_ipv6_add_to_interface(ifindex, ph, 64, zero16, IPV6_CFG_SLAAC, IPV6_ADDRK_GLOBAL); + (void)l3_ipv6_add_to_interface(ifindex, ph, 64, (const uint8_t[16]){0}, IPV6_CFG_SLAAC, IPV6_ADDRK_GLOBAL); } } @@ -983,23 +951,26 @@ void ifmgr_autoconfig_all_l2(void){ 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; } - } + if (!ipv4_l3_is_ready(x)) continue; + if (ipv4_is_loopback(dst_ip) != x->is_localhost) continue; + cand[n++] = x->l3_id; } + + uint8_t chosen = 0; + if (!ipv4_rt_pick_best_l3_in(cand, n, dst_ip, &chosen)) return r; + + l3_ipv4_interface_t *v4 = l3_ipv4_find_by_id(chosen); + if (!ipv4_l3_is_ready(v4)) return r; + + r.found = true; + r.ipv4 = v4; + r.l2 = v4->l2; return r; } @@ -1010,61 +981,30 @@ ip_resolution_result_t resolve_ipv6_to_interface(const uint8_t dst_ip[16]) { r.ipv6 = NULL; r.l2= NULL; - int dst_is_ll = ipv6_is_linklocal(dst_ip); - int best_pl = -1; - uint16_t best_cost = 0x7FFF; + if (!dst_ip) return r; + int dst_is_ll = (ipv6_is_linklocal(dst_ip) || ipv6_is_linkscope_mcast(dst_ip)) ? 1 : 0; + int dst_is_loop = ipv6_is_loopback(dst_ip) ? 1 : 0; + uint8_t cand[MAX_IPV6_L3_INTERFACES]; + int n = 0; - for (int i = 0; i < MAX_IPV6_L3_INTERFACES; i++) { + for (int i = 0; i < MAX_IPV6_L3_INTERFACES && n < MAX_IPV6_L3_INTERFACES; i++) { if (!g_v6[i].used) continue; l3_ipv6_interface_t *x = &g_v6[i].node; - if (!x->l2) continue; - if (x->cfg == IPV6_CFG_DISABLE) continue; - if (ipv6_is_unspecified(x->ip)) continue; - - int src_is_ll = ipv6_is_linklocal(x->ip); - - if (dst_is_ll != src_is_ll) - continue; - - int pl_conn = -1; - int match = ipv6_common_prefix_len(dst_ip, x->ip); - if (match >= x->prefix_len) pl_conn = x->prefix_len; - - int pl_tab = -1; - uint16_t met_tab = 0x7FFF; - uint8_t via[16] = {0}; - - if (x->routing_table) { - int out_pl = -1; - int out_met = 0x7FFF; - if (ipv6_rt_lookup_in((const ipv6_rt_table_t*)x->routing_table,dst_ip, via, &out_pl, &out_met)) - { - pl_tab = out_pl; - met_tab = out_met; - } - } - - int cand_pl = pl_conn; - uint16_t cand_cost = x->l2->base_metric; + if (!ipv6_l3_is_ready(x)) continue; + if (x->is_localhost && !dst_is_loop) continue; + int src_is_ll = ipv6_is_linklocal(x->ip) ? 1 : 0; + if (src_is_ll != dst_is_ll) continue; + cand[n++] = x->l3_id; + } - if (pl_tab > cand_pl || (pl_tab == cand_pl && (x->l2->base_metric + met_tab) < cand_cost)) { - cand_pl = pl_tab; - cand_cost = x->l2->base_metric + met_tab; - } + uint8_t chosen = 0; + if (!ipv6_rt_pick_best_l3_in(cand, n, dst_ip, &chosen)) return r; - if (cand_pl > best_pl || (cand_pl == best_pl && cand_cost < best_cost)) { - best_pl = cand_pl; - best_cost = cand_cost; - r.found = true; - r.ipv6 = x; - r.l2 = x->l2; - } - } - if (best_pl < 0) { - r.found = false; - r.ipv6 = NULL; - r.l2 = NULL; - } + l3_ipv6_interface_t *v6 = l3_ipv6_find_by_id(chosen); + if (!ipv6_l3_is_ready(v6)) return r; + r.found = true; + r.ipv6 = v6; + r.l2 = v6->l2; return r; } \ No newline at end of file diff --git a/kernel/networking/internet_layer/icmp.c b/kernel/networking/internet_layer/icmp.c index 5a6341c5..f4f77478 100644 --- a/kernel/networking/internet_layer/icmp.c +++ b/kernel/networking/internet_layer/icmp.c @@ -7,17 +7,23 @@ #include "syscalls/syscalls.h" #define MAX_PENDING 16 +#define MAX_PING_REPLIES 16 + +//TODO use raw sockets +typedef struct { + uint8_t type; + uint8_t code; + uint32_t end_ms; + uint32_t src_ip; +} ping_reply_t; typedef struct { bool in_use; uint16_t id; uint16_t seq; - bool received; - uint8_t rx_type; - uint8_t rx_code; uint32_t start_ms; - uint32_t end_ms; - uint32_t rx_src_ip; + uint8_t rx_count; + ping_reply_t replies[MAX_PING_REPLIES]; } ping_slot_t; static ping_slot_t g_pending[MAX_PENDING] = {0}; @@ -28,12 +34,9 @@ static int alloc_slot(uint16_t id, uint16_t seq) { g_pending[i].in_use = true; g_pending[i].id = id; g_pending[i].seq = seq; - g_pending[i].received = false; - g_pending[i].rx_type = 0xFF; - g_pending[i].rx_code = 0xFF; g_pending[i].start_ms = (uint32_t)get_time(); - g_pending[i].end_ms = 0; - g_pending[i].rx_src_ip = 0; + g_pending[i].rx_count = 0; + memset(g_pending[i].replies, 0, sizeof(g_pending[i].replies)); return i; } } @@ -43,11 +46,12 @@ static int alloc_slot(uint16_t id, uint16_t seq) { static void mark_received(uint16_t id, uint16_t seq, uint8_t type, uint8_t code, uint32_t src_ip) { for (int i = 0; i < MAX_PENDING; i++) { if (g_pending[i].in_use && g_pending[i].id == id && g_pending[i].seq == seq) { - g_pending[i].received = true; - g_pending[i].rx_type = type; - g_pending[i].rx_code = code; - g_pending[i].end_ms = (uint32_t)get_time(); - g_pending[i].rx_src_ip = src_ip; + if (g_pending[i].rx_count >= MAX_PING_REPLIES) return; + ping_reply_t *r = &g_pending[i].replies[g_pending[i].rx_count++]; + r->type = type; + r->code = code; + r->end_ms = (uint32_t)get_time(); + r->src_ip = src_ip; return; } } @@ -68,35 +72,20 @@ static uintptr_t build_echo(uint16_t id, uint16_t seq, const uint8_t* payload, u memset(pkt->payload, 0, 56); if (payload && pay_len) memcpy(pkt->payload, payload, (pay_len > 56 ? 56 : pay_len)); pkt->checksum = 0; - pkt->checksum = checksum16((uint16_t*)pkt, (len+1)/2); + pkt->checksum = bswap16(checksum16(pkt, len)); return buf; } -bool icmp_ping(uint32_t dst_ip, uint16_t id, uint16_t seq, uint32_t timeout_ms, const void* tx_opts_or_null, uint32_t ttl, ping_result_t* out) { +uint32_t icmp_ping_collect(uint32_t dst_ip, uint16_t id, uint16_t seq, uint32_t timeout_ms, const void* tx_opts_or_null, uint32_t ttl, ping_result_t* out, uint32_t max_results) { + if (!out || !max_results) return 0; int slot = alloc_slot(id, seq); - if (slot < 0) { - if (out) { - out->rtt_ms = 0; - out->status = PING_UNKNOWN_ERROR; - out->icmp_type = 0xFF; - out->icmp_code = 0xFF; - out->responder_ip = 0; - } - return false; - } + if (slot < 0) return 0; uint32_t tot_len = 0; uintptr_t buf = build_echo(id, seq, NULL, 32, &tot_len); if (!buf) { - if (out) { - out->rtt_ms = 0; - out->status = PING_UNKNOWN_ERROR; - out->icmp_type = 0xFF; - out->icmp_code = 0xFF; - out->responder_ip = 0; - } g_pending[slot].in_use = false; - return false; + return 0; } uint32_t headroom = (uint32_t)sizeof(eth_hdr_t) + (uint32_t)sizeof(ipv4_hdr_t); @@ -104,68 +93,68 @@ bool icmp_ping(uint32_t dst_ip, uint16_t id, uint16_t seq, uint32_t timeout_ms, if (!pkt) { release((void*)buf); g_pending[slot].in_use = false; - return false; + return 0; } void* p = netpkt_put(pkt, tot_len); if (!p) { netpkt_unref(pkt); release((void*)buf); g_pending[slot].in_use = false; - return false; + return 0; } memcpy(p, (const void*)buf, tot_len); release((void*)buf); ipv4_send_packet(dst_ip, 1, pkt, (const ip_tx_opts_t*)tx_opts_or_null, (uint8_t)ttl, 0); + uint32_t count = 0; + uint32_t copied = 0; uint32_t start = (uint32_t)get_time(); for (;;) { - if (g_pending[slot].received) { - if (out) { - out->icmp_type = g_pending[slot].rx_type; - out->icmp_code = g_pending[slot].rx_code; - out->responder_ip = g_pending[slot].rx_src_ip; - switch (g_pending[slot].rx_type) { - case ICMP_ECHO_REPLY: out->status = PING_OK; break; - case ICMP_DEST_UNREACH: - switch (g_pending[slot].rx_code) { - case 0: out->status = PING_NET_UNREACH; break; - case 1: out->status = PING_HOST_UNREACH; break; - case 2: out->status = PING_PROTO_UNREACH; break; - case 3: out->status = PING_PORT_UNREACH; break; - case 4: out->status = PING_FRAG_NEEDED; break; - case 5: out->status = PING_SRC_ROUTE_FAILED; break; - case 13: out->status = PING_ADMIN_PROHIBITED; break; - default: out->status = PING_UNKNOWN_ERROR; break; - } - break; - case ICMP_TIME_EXCEEDED: out->status = PING_TTL_EXPIRED; break; - case ICMP_PARAM_PROBLEM: out->status = PING_PARAM_PROBLEM; break; - case ICMP_REDIRECT: out->status = PING_REDIRECT; break; - default: out->status = PING_UNKNOWN_ERROR; break; - } - - if (g_pending[slot].end_ms >= g_pending[slot].start_ms) out->rtt_ms = g_pending[slot].end_ms - g_pending[slot].start_ms; - else out->rtt_ms = 0; + while (copied < g_pending[slot].rx_count && count < max_results) { + ping_reply_t *r = &g_pending[slot].replies[copied++]; + ping_result_t *dst = &out[count++]; + dst->icmp_type = r->type; + dst->icmp_code = r->code; + dst->responder_ip = r->src_ip; + switch (r->type) { + case ICMP_ECHO_REPLY: dst->status = PING_OK; break; + case ICMP_DEST_UNREACH: + switch (r->code) { + case 0: dst->status = PING_NET_UNREACH; break; + case 1: dst->status = PING_HOST_UNREACH; break; + case 2: dst->status = PING_PROTO_UNREACH; break; + case 3: dst->status = PING_PORT_UNREACH; break; + case 4: dst->status = PING_FRAG_NEEDED; break; + case 5: dst->status = PING_SRC_ROUTE_FAILED; break; + case 13: dst->status = PING_ADMIN_PROHIBITED; break; + default: dst->status = PING_UNKNOWN_ERROR; break; + } + break; + case ICMP_TIME_EXCEEDED: dst->status = PING_TTL_EXPIRED; break; + case ICMP_PARAM_PROBLEM: dst->status = PING_PARAM_PROBLEM; break; + case ICMP_REDIRECT: dst->status = PING_REDIRECT; break; + default: dst->status = PING_UNKNOWN_ERROR; break; } - bool ok = (g_pending[slot].rx_type == ICMP_ECHO_REPLY); - g_pending[slot].in_use = false; - return ok; + + if (r->end_ms >= g_pending[slot].start_ms) dst->rtt_ms = r->end_ms - g_pending[slot].start_ms; + else dst->rtt_ms = 0; } + if (count == max_results || (max_results == 1 && count)) break; uint32_t now = (uint32_t)get_time(); if (now - start >= timeout_ms) break; msleep(5); } - if (out) { - out->rtt_ms = 0; - out->status = PING_TIMEOUT; - out->icmp_type = 0xFF; - out->icmp_code = 0xFF; - out->responder_ip = 0; - } g_pending[slot].in_use = false; - return false; + return count; +} + +bool icmp_ping(uint32_t dst_ip, uint16_t id, uint16_t seq, uint32_t timeout_ms, const void* tx_opts_or_null, uint32_t ttl, ping_result_t* out) { + ping_result_t res; + ping_result_t *dst = out ? out : &res; + uint32_t n = icmp_ping_collect(dst_ip, id, seq, timeout_ms, tx_opts_or_null, ttl, dst, 1); + return n && dst->status == PING_OK; } void icmp_input(netpkt_t* pkt, uint32_t src_ip, uint32_t dst_ip) { @@ -183,11 +172,7 @@ void icmp_input(netpkt_t* pkt, uint32_t src_ip, uint32_t dst_ip) { netpkt_unref(pkt); return; } - uint32_t sum = 0; - for (uint32_t i = 0; i + 1 < len; i += 2) sum += (uint32_t)((raw[i] << 8) | raw[i + 1]); - if (len & 1u) sum += (uint32_t)(raw[len - 1] << 8); - while (sum >> 16) sum = (sum & 0xFFFFu) + (sum >> 16); - if ((uint16_t)sum != 0xFFFFu) { + if (checksum16(raw, len) != 0) { netpkt_unref(pkt); return; } @@ -214,7 +199,7 @@ void icmp_input(netpkt_t* pkt, uint32_t src_ip, uint32_t dst_ip) { if (pay) memcpy(rp->payload, raw + 8, pay); rp->checksum = 0; uint32_t rlen = 8 + pay; - rp->checksum = checksum16((uint16_t*)rp, (rlen+1)/2); + rp->checksum = bswap16(checksum16(rp, rlen)); l3_ipv4_interface_t* l3 = l3_ipv4_find_by_ip(dst_ip); if (l3 && l3->l2) { @@ -244,23 +229,23 @@ void icmp_input(netpkt_t* pkt, uint32_t src_ip, uint32_t dst_ip) { if (type == ICMP_TIME_EXCEEDED || type == ICMP_DEST_UNREACH || type == ICMP_PARAM_PROBLEM || type == ICMP_REDIRECT) { if (pay >= 28) { - const uint8_t *ip = raw + 8; - uint8_t ihl = (uint8_t)(ip[0] & 0x0F); - uint32_t iphdr = (uint32_t)ihl * 4; - - if (pay >= iphdr + 8) { - uint8_t proto = ip[9]; - if (proto == 1) { - const uint8_t *ic = raw + 8 + iphdr; - uint8_t t = ic[0]; - if (t == ICMP_ECHO_REQUEST || t == ICMP_ECHO_REPLY) { - uint16_t iid = (uint16_t)((ic[4] << 8) | ic[5]); - uint16_t isq = (uint16_t)((ic[6] << 8) | ic[7]); - mark_received(iid, isq, type, code, src_ip); + const uint8_t *ip = raw + 8; + uint8_t ihl = (uint8_t)(ip[0] & 0x0F); + uint32_t iphdr = (uint32_t)ihl * 4; + + if (pay >= iphdr + 8) { + uint8_t proto = ip[9]; + if (proto == 1) { + const uint8_t *ic = raw + 8 + iphdr; + uint8_t t = ic[0]; + if (t == ICMP_ECHO_REQUEST || t == ICMP_ECHO_REPLY) { + uint16_t iid = (uint16_t)((ic[4] << 8) | ic[5]); + uint16_t isq = (uint16_t)((ic[6] << 8) | ic[7]); + mark_received(iid, isq, type, code, src_ip); + } } } } - } netpkt_unref(pkt); return; } diff --git a/kernel/networking/internet_layer/icmp.h b/kernel/networking/internet_layer/icmp.h index c0cf4b64..20f44e1a 100644 --- a/kernel/networking/internet_layer/icmp.h +++ b/kernel/networking/internet_layer/icmp.h @@ -49,6 +49,7 @@ typedef struct { } ping_result_t; bool icmp_ping(uint32_t dst_ip, uint16_t id, uint16_t seq, uint32_t timeout_ms, const void *tx_opts_or_null, uint32_t ttl, ping_result_t *out); +uint32_t icmp_ping_collect(uint32_t dst_ip, uint16_t id, uint16_t seq, uint32_t timeout_ms, const void *tx_opts_or_null, uint32_t ttl, ping_result_t *out, uint32_t max_results); void icmp_input(netpkt_t* pkt, uint32_t src_ip, uint32_t dst_ip); #ifdef __cplusplus diff --git a/kernel/networking/internet_layer/icmpv6.c b/kernel/networking/internet_layer/icmpv6.c index 407e537a..87ad2e5a 100644 --- a/kernel/networking/internet_layer/icmpv6.c +++ b/kernel/networking/internet_layer/icmpv6.c @@ -10,6 +10,7 @@ #include "syscalls/syscalls.h" #define MAX_PENDING 16 +#define MAX_PING6_REPLIES 16 typedef struct __attribute__((packed)) { icmpv6_hdr_t hdr; @@ -17,16 +18,21 @@ typedef struct __attribute__((packed)) { uint16_t seq; } icmpv6_echo_t; +typedef struct { + uint8_t type; + uint8_t code; + uint32_t end_ms; + uint8_t src_ip[16]; +} ping6_reply_t; + +//TODO use raw sockets typedef struct { bool in_use; uint16_t id; uint16_t seq; - bool received; - uint8_t rx_type; - uint8_t rx_code; uint32_t start_ms; - uint32_t end_ms; - uint8_t rx_src_ip[16]; + uint8_t rx_count; + ping6_reply_t replies[MAX_PING6_REPLIES]; } ping6_slot_t; static ping6_slot_t g_pending[MAX_PENDING] = {0}; @@ -37,12 +43,9 @@ static int alloc_slot(uint16_t id, uint16_t seq) { g_pending[i].in_use = true; g_pending[i].id = id; g_pending[i].seq = seq; - g_pending[i].received = false; - g_pending[i].rx_type = 0xFF; - g_pending[i].rx_code = 0xFF; g_pending[i].start_ms = (uint32_t)get_time(); - g_pending[i].end_ms = 0; - memset(g_pending[i].rx_src_ip, 0, 16); + g_pending[i].rx_count = 0; + memset(g_pending[i].replies, 0, sizeof(g_pending[i].replies)); return i; } } @@ -52,11 +55,12 @@ static int alloc_slot(uint16_t id, uint16_t seq) { static void mark_received(uint16_t id, uint16_t seq, uint8_t type, uint8_t code, const uint8_t src_ip[16]) { for (int i = 0; i < MAX_PENDING; i++) { if (g_pending[i].in_use && g_pending[i].id == id && g_pending[i].seq == seq) { - g_pending[i].received = true; - g_pending[i].rx_type = type; - g_pending[i].rx_code = code; - g_pending[i].end_ms = (uint32_t)get_time(); - if (src_ip) memcpy(g_pending[i].rx_src_ip, src_ip, 16); + if (g_pending[i].rx_count >= MAX_PING6_REPLIES) return; + ping6_reply_t *r = &g_pending[i].replies[g_pending[i].rx_count++]; + r->type = type; + r->code = code; + r->end_ms = (uint32_t)get_time(); + if (src_ip) memcpy(r->src_ip, src_ip, 16); return; } } @@ -150,94 +154,68 @@ static bool icmpv6_send_echo_request(const uint8_t dst_ip[16], uint16_t id, uint return true; } -bool icmpv6_ping(const uint8_t dst_ip[16], uint16_t id, uint16_t seq, uint32_t timeout_ms, const void *tx_opts_or_null, uint8_t hop_limit, ping6_result_t *out) { +uint32_t icmpv6_ping_collect(const uint8_t dst_ip[16], uint16_t id, uint16_t seq, uint32_t timeout_ms, const void *tx_opts_or_null, uint8_t hop_limit, ping6_result_t *out, uint32_t max_results) { + if (!out || !max_results) return 0; int slot = alloc_slot(id, seq); if (slot < 0) { - if (out) { - out->rtt_ms = 0; - out->status = PING_UNKNOWN_ERROR; - out->icmp_type = 0xFF; - out->icmp_code = 0xFF; - memset(out->responder_ip, 0, 16); - } - return false; + return 0; } uint8_t payload[32]; memset(payload, 0, sizeof(payload)); if (!icmpv6_send_echo_request(dst_ip, id, seq, payload, sizeof(payload), tx_opts_or_null, hop_limit)) { - if (out) { - out->rtt_ms = 0; - out->status = PING_UNKNOWN_ERROR; - out->icmp_type = 0xFF; - out->icmp_code = 0xFF; - memset(out->responder_ip, 0, 16); - } - g_pending[slot].in_use =false; - return false; + g_pending[slot].in_use = false; + return 0; } + uint32_t count = 0; + uint32_t copied = 0; uint32_t start = (uint32_t)get_time(); for (;;) { - if (g_pending[slot].received) { - if (out) { - out->icmp_type = g_pending[slot].rx_type; - out->icmp_code = g_pending[slot].rx_code; - memcpy(out->responder_ip, g_pending[slot].rx_src_ip, 16); - - switch (g_pending[slot].rx_type) { - case ICMPV6_ECHO_REPLY: - out->status = PING_OK; - break; + while (copied < g_pending[slot].rx_count && count < max_results) { + ping6_reply_t *r = &g_pending[slot].replies[copied++]; + ping6_result_t *dst = &out[count++]; + dst->icmp_type = r->type; + dst->icmp_code = r->code; + memcpy(dst->responder_ip, r->src_ip, 16); + switch (r->type) { + case ICMPV6_ECHO_REPLY: dst->status = PING_OK; break; case ICMPV6_DEST_UNREACH: - switch (g_pending[slot].rx_code) { - case 0: out->status = PING_NET_UNREACH; break; - case 1: out->status = PING_ADMIN_PROHIBITED; break; - case 2: out->status = PING_ADMIN_PROHIBITED; break; - case 3: out->status = PING_HOST_UNREACH; break; - case 4: out->status = PING_PORT_UNREACH; break; - default: out->status = PING_UNKNOWN_ERROR; break; + switch (r->code) { + case 0: dst->status = PING_NET_UNREACH; break; + case 1: dst->status = PING_ADMIN_PROHIBITED; break; + case 2: dst->status = PING_ADMIN_PROHIBITED; break; + case 3: dst->status = PING_HOST_UNREACH; break; + case 4: dst->status = PING_PORT_UNREACH; break; + default: dst->status = PING_UNKNOWN_ERROR; break; } break; - case ICMPV6_PACKET_TOO_BIG: - out->status = PING_FRAG_NEEDED; - break; - case ICMPV6_TIME_EXCEEDED: - out->status = PING_TTL_EXPIRED; - break; - case ICMPV6_PARAM_PROBLEM: - out->status = PING_PARAM_PROBLEM; - break; - default: - out->status = PING_UNKNOWN_ERROR; - break; - } - - if (g_pending[slot].end_ms >= g_pending[slot].start_ms) out->rtt_ms = g_pending[slot].end_ms - g_pending[slot].start_ms; - else out->rtt_ms = 0; + case ICMPV6_PACKET_TOO_BIG: dst->status = PING_FRAG_NEEDED; break; + case ICMPV6_TIME_EXCEEDED: dst->status = PING_TTL_EXPIRED; break; + case ICMPV6_PARAM_PROBLEM: dst->status = PING_PARAM_PROBLEM; break; + default: dst->status = PING_UNKNOWN_ERROR; break; } - bool ok = (g_pending[slot].rx_type == ICMPV6_ECHO_REPLY); - g_pending[slot].in_use = false; - return ok; + if (r->end_ms >= g_pending[slot].start_ms) dst->rtt_ms = r->end_ms - g_pending[slot].start_ms; + else dst->rtt_ms = 0; } + if (count == max_results || (max_results == 1 && count)) break; uint32_t now = (uint32_t)get_time(); if (now - start >= timeout_ms) break; msleep(5); } - if (out) { - out->rtt_ms = 0; - out->status = PING_TIMEOUT; - out->icmp_type = 0xFF; - out->icmp_code = 0xFF; - memset(out->responder_ip, 0, 16); - } - g_pending[slot].in_use = false; - return false; + return count; +} + +bool icmpv6_ping(const uint8_t dst_ip[16], uint16_t id, uint16_t seq, uint32_t timeout_ms, const void *tx_opts_or_null, uint8_t hop_limit, ping6_result_t *out) { + ping6_result_t res; + ping6_result_t *dst = out ? out : &res; + uint32_t n = icmpv6_ping_collect(dst_ip, id, seq, timeout_ms, tx_opts_or_null, hop_limit, dst, 1); + return n && dst->status == PING_OK; } static bool extract_echo_id_seq_from_error(const uint8_t *icmp, uint32_t icmp_len, uint16_t *out_id, uint16_t *out_seq) { diff --git a/kernel/networking/internet_layer/icmpv6.h b/kernel/networking/internet_layer/icmpv6.h index 86e12eba..099f8f34 100644 --- a/kernel/networking/internet_layer/icmpv6.h +++ b/kernel/networking/internet_layer/icmpv6.h @@ -33,6 +33,7 @@ typedef struct { void icmpv6_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[16], uint8_t hop_limit, const uint8_t src_mac[6], netpkt_t* pkt); bool icmpv6_ping(const uint8_t dst_ip[16], uint16_t id, uint16_t seq, uint32_t timeout_ms, const void *tx_opts_or_null, uint8_t hop_limit, ping6_result_t *out); +uint32_t icmpv6_ping_collect(const uint8_t dst_ip[16], uint16_t id, uint16_t seq, uint32_t timeout_ms, const void *tx_opts_or_null, uint8_t hop_limit, ping6_result_t *out, uint32_t max_results); bool icmpv6_send_on_l2(uint8_t ifindex, const uint8_t dst_ip[16], const uint8_t src_ip[16], const uint8_t dst_mac[6], const void *icmp, uint32_t icmp_len, uint8_t hop_limit); #ifdef __cplusplus diff --git a/kernel/networking/internet_layer/igmp.c b/kernel/networking/internet_layer/igmp.c index 541e4f82..56c150fa 100644 --- a/kernel/networking/internet_layer/igmp.c +++ b/kernel/networking/internet_layer/igmp.c @@ -5,6 +5,7 @@ #include "networking/interface_manager.h" #include "kernel_processes/kprocess_loader.h" #include "math/rng.h" +#include "random/random.h" #include "std/memory.h" #include "std/string.h" #include "syscalls/syscalls.h" @@ -45,19 +46,18 @@ static bool send_igmp(uint8_t ifindex, uint32_t dst, uint8_t type, uint32_t grou netpkt_t* pkt = netpkt_alloc(sizeof(igmp_hdr_t),headroom, 0); if (!pkt) return false; - uint16_t igmp_words[sizeof(igmp_hdr_t)/sizeof(uint16_t)]; - igmp_hdr_t* h = (igmp_hdr_t*)igmp_words; + igmp_hdr_t igmp; if (!netpkt_put(pkt, sizeof(igmp_hdr_t))) { netpkt_unref(pkt); return false; } - h->type = type; - h->max_resp_time = 0; - h->group = bswap32(group); - h->checksum = 0; - h->checksum = checksum16(igmp_words, sizeof(igmp_hdr_t)/2); - memcpy((void*)netpkt_data(pkt), h, sizeof(*h)); + igmp.type = type; + igmp.max_resp_time = 0; + igmp.group = bswap32(group); + igmp.checksum = 0; + igmp.checksum = bswap16(checksum16(&igmp, sizeof(igmp))); + memcpy((void*)netpkt_data(pkt), &igmp, sizeof(igmp)); ip_tx_opts_t tx; tx.scope = IP_TX_BOUND_L2; @@ -111,9 +111,7 @@ static int igmp_daemon_entry(int argc, char* argv[]) { igmp_daemon_running = 1; if (!igmp_rng_inited) { - uint64_t virt_timer; - asm volatile ("mrs %0, cntvct_el0" : "=r"(virt_timer)); - rng_seed(&igmp_rng, virt_timer); + rng_init_random(&igmp_rng); igmp_rng_inited = 1; } @@ -211,10 +209,7 @@ void igmp_input(uint8_t ifindex, uint32_t src, uint32_t dst, netpkt_t* pkt) { netpkt_unref(pkt); return; } - uint32_t sum = 0; - for (uint32_t i = 0; i + 1 < sizeof(igmp_hdr_t); i += 2) sum += (uint32_t)((p[i] << 8) | p[i + 1]); - while (sum >> 16) sum = (sum & 0xFFFFu) + (sum >> 16); - if ((uint16_t)sum != 0xFFFFu) { + if (checksum16(p, sizeof(igmp_hdr_t)) != 0) { netpkt_unref(pkt); return; } diff --git a/kernel/networking/internet_layer/ipv4.c b/kernel/networking/internet_layer/ipv4.c index 4d1303a3..471b6b16 100644 --- a/kernel/networking/internet_layer/ipv4.c +++ b/kernel/networking/internet_layer/ipv4.c @@ -54,7 +54,7 @@ bool ipv4_send_packet(uint32_t dst_ip, uint8_t proto, netpkt_t* pkt, const ip_tx } src_v4 = l3_ipv4_find_by_id(opts->index); - if (!src_v4 || !src_v4->l2 || !src_v4->l2->is_up || src_v4->mode == IPV4_CFG_DISABLED) { + if (!ipv4_l3_is_active(src_v4)) { netpkt_unref(pkt); return false; } @@ -70,7 +70,7 @@ bool ipv4_send_packet(uint32_t dst_ip, uint8_t proto, netpkt_t* pkt, const ip_tx } src_v4 = l3_ipv4_find_by_id(plan.l3_id); - if (!src_v4 || !src_v4->l2 || !src_v4->l2->is_up || src_v4->mode == IPV4_CFG_DISABLED || !src_v4->ip) { + if (!ipv4_l3_is_ready(src_v4)) { netpkt_unref(pkt); return false; } @@ -121,22 +121,21 @@ bool ipv4_send_packet(uint32_t dst_ip, uint8_t proto, netpkt_t* pkt, const ip_tx return false; } - uint16_t ip_[sizeof(ipv4_hdr_t)/sizeof(uint16_t)]; - ipv4_hdr_t* ip = (ipv4_hdr_t*)ip_; - 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++); + ipv4_hdr_t ip; + 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++); uint16_t ff = 0; if (dontfrag) ff |= 0x4000; - ip->flags_frag_offset = bswap16(ff); - ip->ttl = ttl ? ttl : IP_TTL_DEFAULT; - ip->protocol = proto; - ip->header_checksum = 0; - ip->src_ip = bswap32(src_ip); - ip->dst_ip = bswap32(dst_ip); - ip->header_checksum = checksum16(ip_, hdr_len / 2); - memcpy(hdrp, ip, sizeof(*ip)); + ip.flags_frag_offset = bswap16(ff); + ip.ttl = ttl ? ttl : IP_TTL_DEFAULT; + ip.protocol = proto; + ip.header_checksum = 0; + ip.src_ip = bswap32(src_ip); + ip.dst_ip = bswap32(dst_ip); + ip.header_checksum = bswap16(checksum16(&ip, hdr_len)); + memcpy(hdrp, &ip, sizeof(ip)); if (need_arp) return arp_send_or_queue_on(ifx, nh, pkt); return eth_send_frame_on(ifx, ETHERTYPE_IPV4, dst_mac, pkt); @@ -177,23 +176,22 @@ bool ipv4_send_packet(uint32_t dst_ip, uint8_t proto, netpkt_t* pkt, const ip_tx break; } - uint16_t ip_[sizeof(ipv4_hdr_t) / sizeof(uint16_t)]; - ipv4_hdr_t* ip = (ipv4_hdr_t*)ip_; - ip->version_ihl = (uint8_t)((IP_VERSION_4 << 4) | IP_IHL_NOOPTS); - ip->dscp_ecn = 0; - ip->total_length = bswap16((uint16_t)frame_len); - ip->identification = bswap16(ident); + ipv4_hdr_t ip; + ip.version_ihl = (uint8_t)((IP_VERSION_4 << 4) | IP_IHL_NOOPTS); + ip.dscp_ecn = 0; + ip.total_length = bswap16((uint16_t)frame_len); + ip.identification = bswap16(ident); uint16_t ff = (uint16_t)((off / 8) & 0x1FFF); if (more) ff |= 0x2000; - ip->flags_frag_offset = bswap16(ff); - ip->ttl = ttl ? ttl : IP_TTL_DEFAULT; - ip->protocol = proto; - ip->header_checksum = 0; - ip->src_ip = bswap32(src_ip); - ip->dst_ip = bswap32(dst_ip); - ip->header_checksum = checksum16(ip_, hdr_len / 2); - - memcpy(buf, ip, sizeof(*ip)); + ip.flags_frag_offset = bswap16(ff); + ip.ttl = ttl ? ttl : IP_TTL_DEFAULT; + ip.protocol = proto; + ip.header_checksum = 0; + ip.src_ip = bswap32(src_ip); + ip.dst_ip = bswap32(dst_ip); + ip.header_checksum = bswap16(checksum16(&ip, hdr_len)); + + memcpy(buf, &ip, sizeof(ip)); memcpy((uint8_t*)buf + hdr_len, data + off, chunk); if (need_arp) { @@ -215,8 +213,7 @@ static void ipv4_deliver_l4(uint16_t ifindex, netpkt_t* pkt, uint32_t l4_off, ui 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; + if (!ipv4_l3_is_active(v4)) continue; cand[ccount++] = v4; } @@ -324,10 +321,9 @@ void ipv4_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { uint32_t hdr_len = (uint32_t)ihl * 4; if (ip_len < hdr_len) return; - uint16_t hdr_words[60 /sizeof(uint16_t)]; - uint8_t* hdr_copy = (uint8_t*)hdr_words; + uint8_t hdr_copy[60]; if (!netpkt_copyout(pkt, 0, hdr_copy, hdr_len)) return; - if (checksum16(hdr_words, hdr_len / 2) != 0) return; + if (checksum16(hdr_copy, hdr_len) != 0) return; ipv4_hdr_t ip; memcpy(&ip, hdr_copy, sizeof(ip)); diff --git a/kernel/networking/internet_layer/ipv4_route.c b/kernel/networking/internet_layer/ipv4_route.c index cc3e7158..c10fce28 100644 --- a/kernel/networking/internet_layer/ipv4_route.c +++ b/kernel/networking/internet_layer/ipv4_route.c @@ -9,14 +9,6 @@ struct ipv4_rt_table { int len; }; -static bool v4_l3_ok_for_tx(l3_ipv4_interface_t* v4){ - if (!v4 || !v4->l2) return false; - if (!v4->l2->is_up) return false; - if (v4->mode == IPV4_CFG_DISABLED) return false; - if (!v4->ip) return false; - return true; -} - static uint32_t ipv4_route_epoch(void) { uint32_t h = 0x811C9DC5; uint8_t cnt = l2_interface_count(); @@ -47,7 +39,7 @@ bool ipv4_tx_plan_valid(const ipv4_tx_plan_t* plan) { if (plan->net_epoch != ipv4_route_epoch()) return false; l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(plan->l3_id); - if (!v4_l3_ok_for_tx(v4)) return false; + if (!ipv4_l3_is_ready(v4)) return false; return v4->ip == plan->src_ip; } @@ -60,7 +52,7 @@ bool ipv4_build_tx_plan(uint32_t dst, const ip_tx_opts_t* hint, ipv4_tx_plan_t* if (hint && hint->scope == IP_TX_BOUND_L3) { uint8_t id = hint->index; l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(id); - if (!v4_l3_ok_for_tx(v4) || ipv4_is_loopback(dst) != v4->is_localhost) return false; + if (!ipv4_l3_is_ready(v4) || ipv4_is_loopback(dst) != v4->is_localhost) return false; out->l3_id = id; out->src_ip = v4->ip; return true; @@ -74,7 +66,7 @@ bool ipv4_build_tx_plan(uint32_t dst, const ip_tx_opts_t* hint, ipv4_tx_plan_t* if (!l2 || !l2->is_up) return false; for (int s = 0; s < MAX_IPV4_PER_INTERFACE && n < (int)sizeof(cand); ++s){ l3_ipv4_interface_t* v4 = l2->l3_v4[s]; - if (!v4_l3_ok_for_tx(v4) || ipv4_is_loopback(dst) != v4->is_localhost) continue; + if (!ipv4_l3_is_ready(v4) || ipv4_is_loopback(dst) != v4->is_localhost) continue; cand[n++] = v4->l3_id; } } else { @@ -84,7 +76,7 @@ bool ipv4_build_tx_plan(uint32_t dst, const ip_tx_opts_t* hint, ipv4_tx_plan_t* if (!l2 || !l2->is_up) continue; for (int s = 0; s < MAX_IPV4_PER_INTERFACE && n < (int)sizeof(cand); ++s){ l3_ipv4_interface_t* v4 = l2->l3_v4[s]; - if (!v4_l3_ok_for_tx(v4) || ipv4_is_loopback(dst) != v4->is_localhost) continue; + if (!ipv4_l3_is_ready(v4) || ipv4_is_loopback(dst) != v4->is_localhost) continue; cand[n++] = v4->l3_id; } } @@ -96,18 +88,13 @@ bool ipv4_build_tx_plan(uint32_t dst, const ip_tx_opts_t* hint, ipv4_tx_plan_t* if (!ipv4_rt_pick_best_l3_in(cand, n, dst, &chosen)) chosen = cand[0]; l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(chosen); - if (!v4_l3_ok_for_tx(v4) || ipv4_is_loopback(dst) != v4->is_localhost) return false; + if (!ipv4_l3_is_ready(v4) || ipv4_is_loopback(dst) != v4->is_localhost) return false; out->l3_id = chosen; out->src_ip = v4->ip; return true; } -static int prefix_len(uint32_t m) { - int n = 0; - while (m & 0x80000000u) { n++; m <<= 1; } - return n; -} ipv4_rt_table_t* ipv4_rt_create(void) { ipv4_rt_table_t* t = (ipv4_rt_table_t*)zalloc(sizeof(ipv4_rt_table_t)); @@ -162,7 +149,7 @@ bool ipv4_rt_lookup_in(const ipv4_rt_table_t* t, uint32_t dst, uint32_t* next_ho 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 pl = ipv4_prefix_len(mask); int met = t->e[i].metric; if (pl > best_pl || (pl == best_pl && met < best_metric)) { best_pl = pl; @@ -208,13 +195,12 @@ bool ipv4_rt_pick_best_l3_in(const uint8_t* l3_ids, int n_ids, uint32_t dst, uin uint8_t best_l3 = 0; for (int i=0;il2) continue; - if (x->mode == IPV4_CFG_DISABLED) continue; + if (!ipv4_l3_is_ready(x)) 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); + if ((dst & x->mask) == netx) pl_conn = ipv4_prefix_len(x->mask); } int pl_tab = -1, met_tab = 0x7FFF; if (x->routing_table){ diff --git a/kernel/networking/internet_layer/ipv4_utils.c b/kernel/networking/internet_layer/ipv4_utils.c index cc7825da..68ec3b1e 100644 --- a/kernel/networking/internet_layer/ipv4_utils.c +++ b/kernel/networking/internet_layer/ipv4_utils.c @@ -1,19 +1,5 @@ #include "ipv4_utils.h" - -static char* u8_to_str(uint8_t val, char* out) { - if (val >= 100) { - *out++ = '0' + (val / 100); - val %= 100; - *out++ = '0' + (val / 10); - *out++ = '0' + (val % 10); - } else if (val >= 10) { - *out++ = '0' + (val / 10); - *out++ = '0' + (val % 10); - } else { - *out++ = '0' + val; - } - return out; -} +#include "std/string.h" bool ipv4_is_unspecified(uint32_t ip) { return ip == 0; } bool ipv4_is_loopback(uint32_t ip) { return (ip & 0xFF000000u) == 0x7F000000u; } @@ -59,6 +45,19 @@ bool ipv4_is_unicast_global(uint32_t ip) { return true; } +bool ipv4_l3_is_active(l3_ipv4_interface_t *v4) { + if (!v4 || !v4->l2) return false; + if (!v4->l2->is_up) return false; + if (v4->mode == IPV4_CFG_DISABLED) return false; + return true; +} + +bool ipv4_l3_is_ready(l3_ipv4_interface_t *v4) { + if (!ipv4_l3_is_active(v4)) return false; + if (ipv4_is_unspecified(v4->ip)) return false; + return true; +} + bool ipv4_mask_is_contiguous(uint32_t mask) { if (mask == 0) return true; return ((mask | (mask - 1u)) == 0xFFFFFFFFu); @@ -99,16 +98,8 @@ bool ipv4_same_subnet(uint32_t a, uint32_t b, uint32_t mask) { } void ipv4_to_string(uint32_t ip, char* buf) { - uint8_t a = (uint8_t)(ip >> 24); - uint8_t b = (uint8_t)(ip >> 16); - uint8_t c = (uint8_t)(ip >> 8); - uint8_t d = (uint8_t)(ip); - char* p = buf; - p = u8_to_str(a, p); *p++ = '.'; - p = u8_to_str(b, p); *p++ = '.'; - p = u8_to_str(c, p); *p++ = '.'; - p = u8_to_str(d, p); - *p = '\0'; + if (!buf) return; + string_format_buf(buf, 16, "%u.%u.%u.%u", ip >> 24, (ip >> 16) & 0xFF, (ip >> 8) & 0xFF, ip & 0xFF); } bool ipv4_parse(const char* s, uint32_t* out) { diff --git a/kernel/networking/internet_layer/ipv4_utils.h b/kernel/networking/internet_layer/ipv4_utils.h index 6fe4da8c..84500f88 100644 --- a/kernel/networking/internet_layer/ipv4_utils.h +++ b/kernel/networking/internet_layer/ipv4_utils.h @@ -1,5 +1,6 @@ #pragma once #include "types.h" +#include "networking/interface_manager.h" #define IPV4_MCAST_ALL_HOSTS 0xE0000001u #define IPV4_MCAST_ALL_ROUTERS 0xE0000002u @@ -19,6 +20,9 @@ bool ipv4_is_reserved(uint32_t ip); bool ipv4_is_reserved_special(uint32_t ip); bool ipv4_is_unicast_global(uint32_t ip); +bool ipv4_l3_is_active(l3_ipv4_interface_t *v4); +bool ipv4_l3_is_ready(l3_ipv4_interface_t *v4); + bool ipv4_mask_is_contiguous(uint32_t mask); int ipv4_prefix_len(uint32_t mask); diff --git a/kernel/networking/internet_layer/ipv6.c b/kernel/networking/internet_layer/ipv6.c index 4d8f8694..92bf648b 100644 --- a/kernel/networking/internet_layer/ipv6.c +++ b/kernel/networking/internet_layer/ipv6.c @@ -13,6 +13,7 @@ #include "networking/internet_layer/ipv6_route.h" #include "networking/internet_layer/icmpv6.h" #include "math/rng.h" +#include "random/random.h" #include "net/checksums.h" #include "networking/link_layer/nic_types.h" #include "networking/net_fragbuf.h" @@ -152,7 +153,7 @@ bool ipv6_send_packet(const uint8_t dst[16], uint8_t next_header, netpkt_t* pkt, } l3_ipv6_interface_t* src_v6 = l3_ipv6_find_by_id(plan.l3_id); - if (!src_v6 || !src_v6->l2 || !src_v6->l2->is_up || src_v6->cfg == IPV6_CFG_DISABLE|| src_v6->dad_state != IPV6_DAD_OK) { + if (!ipv6_l3_is_ready(src_v6)) { netpkt_unref(pkt); return false; } @@ -245,9 +246,7 @@ bool ipv6_send_packet(const uint8_t dst[16], uint8_t next_header, netpkt_t* pkt, } rng_t rng; - uint64_t virt_timer; - asm volatile ("mrs %0, cntvct_el0" : "=r"(virt_timer)); - rng_seed(&rng, virt_timer); + rng_init_random(&rng); uint32_t ident = rng_next32(&rng); uint32_t off = 0; @@ -379,8 +378,7 @@ void ipv6_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { if (l2) { for (int i = 0; i < MAX_IPV6_PER_INTERFACE; i++) { l3_ipv6_interface_t* v6 = l2->l3_v6[i]; - if (!v6) continue; - if (v6->cfg == IPV6_CFG_DISABLE) continue; + if (!ipv6_l3_is_active(v6)) continue; if (ipv6_cmp(v6->ip, ip6->dst) == 0) { dst_is_local = true; break; @@ -601,8 +599,7 @@ void ipv6_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { int ccount = 0; for (int x = 0; x < MAX_IPV6_PER_INTERFACE; x++) { l3_ipv6_interface_t* v6 = l2->l3_v6[x]; - if (!v6) continue; - if (v6->cfg == IPV6_CFG_DISABLE) continue; + if (!ipv6_l3_is_active(v6)) continue; cand[ccount++] = v6; } if (ccount == 0) { @@ -675,8 +672,7 @@ void ipv6_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { int ccount = 0; for (int s = 0; s < MAX_IPV6_PER_INTERFACE; ++s) { l3_ipv6_interface_t* v6 = l2->l3_v6[s]; - if (!v6) continue; - if (v6->cfg == IPV6_CFG_DISABLE) continue; + if (!ipv6_l3_is_active(v6)) continue; cand[ccount++] = v6; } if (ccount == 0) return; diff --git a/kernel/networking/internet_layer/ipv6_route.c b/kernel/networking/internet_layer/ipv6_route.c index 7bd8167d..4914df41 100644 --- a/kernel/networking/internet_layer/ipv6_route.c +++ b/kernel/networking/internet_layer/ipv6_route.c @@ -11,12 +11,8 @@ struct ipv6_rt_table { }; static bool v6_l3_ok_for_tx(l3_ipv6_interface_t* v6, int dst_is_ll, int dst_is_loop) { - if (!v6 || !v6->l2) return false; - if (!v6->l2->is_up) return false; - if (v6->cfg == IPV6_CFG_DISABLE) return false; + if (!ipv6_l3_is_ready(v6)) return false; if (v6->is_localhost && !dst_is_loop) return false; - if (ipv6_is_unspecified(v6->ip)) return false; - if (v6->dad_state != IPV6_DAD_OK)return false; int src_is_ll = ipv6_is_linklocal(v6->ip) ? 1 : 0; if (src_is_ll != dst_is_ll) return false; @@ -55,9 +51,7 @@ bool ipv6_tx_plan_valid(const ipv6_tx_plan_t* plan) { if (plan->net_epoch != ipv6_route_epoch()) return false; l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(plan->l3_id); - if (!v6 || !v6->l2 || !v6->l2->is_up) return false; - if (v6->cfg == IPV6_CFG_DISABLE) return false; - if (v6->dad_state != IPV6_DAD_OK) return false; + if (!ipv6_l3_is_ready(v6)) return false; return memcmp(v6->ip, plan->src_ip, 16) == 0; } @@ -182,26 +176,8 @@ bool ipv6_rt_lookup_in(const ipv6_rt_table_t* t, const uint8_t dst[16], uint8_t for (int i = 0; i < t->len; i++) { bool match = false; - if (t->e[i].prefix_len == 0) { - match = true; - } else { - int plen = t->e[i].prefix_len; - int fb = plen / 8; - int rb = plen % 8; - - match = true; - for (int j = 0; j < fb; j++) { - if (dst[j] != t->e[i].network[j]) { - match = false; - break; - } - } - - if (match && rb) { - uint8_t m = (uint8_t)(0xFF << (8 - rb)); - if ((dst[fb] & m) != (t->e[i].network[fb] & m)) match = false; - } - } + if (t->e[i].prefix_len == 0) match = true; + else match = ipv6_common_prefix_len(dst, t->e[i].network) >= t->e[i].prefix_len; if (!match) continue; @@ -229,20 +205,7 @@ void ipv6_rt_ensure_basics(ipv6_rt_table_t* t, const uint8_t ip[16], uint8_t ple if (ip && plen &&!ipv6_is_unspecified(ip)) { uint8_t net[16]; - ipv6_cpy(net, ip); - - if (plen < 128) { - int fb = plen / 8; - int rb = plen % 8; - - for (int i = fb + (rb > 0); i < 16; i++) net[i] = 0; - - if (rb) { - uint8_t m = (uint8_t)(0xFF <<(8 - rb)); - net[fb] &=m; - } - } - + ipv6_prefix_network(ip, plen, net); ipv6_rt_add_in(t, net, plen, (const uint8_t[16]){0}, base_metric); } @@ -262,20 +225,7 @@ void ipv6_rt_sync_basics(ipv6_rt_table_t* t, const uint8_t ip[16], uint8_t plen, if (ip && plen && !ipv6_is_unspecified(ip)) { uint8_t net[16]; - ipv6_cpy(net, ip); - - if (plen < 128) { - int fb = plen / 8; - int rb = plen % 8; - - for (int i = fb + (rb > 0); i < 16; i++)net[i] = 0; - - if (rb) { - uint8_t m = (uint8_t)(0xFF << (8 - rb)); - net[fb] &= m; - } - } - + ipv6_prefix_network(ip, plen, net); ipv6_rt_add_in(t, net, plen, (const uint8_t[16]) {0}, base_metric); } } @@ -287,9 +237,7 @@ bool ipv6_rt_pick_best_l3_in(const uint8_t* l3_ids, int n_ids, const uint8_t dst for (int i = 0; i < n_ids; i++) { l3_ipv6_interface_t* x = l3_ipv6_find_by_id(l3_ids[i]); - if (!x || !x->l2)continue; - if (x->cfg == IPV6_CFG_DISABLE) continue; - if (ipv6_is_unspecified(x->ip)) continue; + if (!ipv6_l3_is_ready(x)) continue; int l2base = x->l2->base_metric; diff --git a/kernel/networking/internet_layer/ipv6_utils.c b/kernel/networking/internet_layer/ipv6_utils.c index 3b1a9143..fdca16a5 100644 --- a/kernel/networking/internet_layer/ipv6_utils.c +++ b/kernel/networking/internet_layer/ipv6_utils.c @@ -18,6 +18,29 @@ bool ipv6_is_ula(const uint8_t ip[16]) { return (ip[0] & 0xFE) == 0xFC; } bool ipv6_is_linklocal(const uint8_t ip[16]) { return ip[0] == 0xFE && (ip[1] & 0xC0) == 0x80; } +bool ipv6_l3_is_active(l3_ipv6_interface_t *v6) { + if (!v6 || !v6->l2) return false; + if (!v6->l2->is_up) return false; + if (v6->cfg == IPV6_CFG_DISABLE) return false; + return true; +} + +bool ipv6_l3_is_ready(l3_ipv6_interface_t *v6) { + if (!ipv6_l3_is_active(v6)) return false; + if (ipv6_is_unspecified(v6->ip)) return false; + if (v6->dad_state != IPV6_DAD_OK) return false; + return true; +} + +bool ipv6_l3_is_tcp_usable(l3_ipv6_interface_t *v6) { + if (!ipv6_l3_is_active(v6)) return false; + if (v6->is_localhost) return false; + if (ipv6_is_unspecified(v6->ip)) return false; + if (v6->dad_state == IPV6_DAD_FAILED) return false; + if (!(v6->kind & IPV6_ADDRK_LINK_LOCAL) && v6->dad_state != IPV6_DAD_OK) return false; + return true; +} + int ipv6_cmp(const uint8_t a[16], const uint8_t b[16]) { for (int i = 0; i < 16; i++) if (a[i] != b[i]) return (int)a[i] - (int)b[i]; return 0; @@ -37,6 +60,24 @@ int ipv6_common_prefix_len(const uint8_t a[16], const uint8_t b[16]) { return 128; } +void ipv6_prefix_network(const uint8_t ip[16], uint8_t prefix_len, uint8_t out[16]) { + if (!out) return; + if (!ip) { + memset(out, 0, 16); + return; + } + + if (prefix_len > 128) prefix_len = 128; + memcpy(out, ip, 16); + + if (prefix_len == 128) return; + + int fb = prefix_len / 8; + int rb = prefix_len % 8; + for (int i = fb + (rb > 0); i < 16; i++) out[i] = 0; + if (rb) out[fb] &= (uint8_t)(0xFF << (8 - rb)); +} + void ipv6_make_multicast(uint8_t scope, ipv6_mcast_kind_t kind, const uint8_t unicast[16], uint8_t out[16]) { memset(out, 0, 16); out[0] = 0xFF; @@ -80,12 +121,6 @@ void ipv6_make_multicast(uint8_t scope, ipv6_mcast_kind_t kind, const uint8_t un } } -static int hexval(int c) { - if (c >= '0' && c <= '9') return c - '0'; - if (c >= 'a' && c <= 'f') return c - 'a' + 10; - if (c >= 'A' && c <= 'F') return c - 'A' + 10; - return -1; -} bool ipv6_parse(const char* s, uint8_t out[16]) { if (!s || !out) return false; @@ -103,7 +138,7 @@ bool ipv6_parse(const char* s, uint8_t out[16]) { if (wi >= 8) return false; int val = 0, cnt = 0, hv; - while ((hv = hexval(*p)) >= 0) { + while ((hv = hex_val(*p)) >= 0) { val = (val << 4) | hv; cnt++; if (cnt > 4) return false; diff --git a/kernel/networking/internet_layer/ipv6_utils.h b/kernel/networking/internet_layer/ipv6_utils.h index 69ee0724..f23b51c5 100644 --- a/kernel/networking/internet_layer/ipv6_utils.h +++ b/kernel/networking/internet_layer/ipv6_utils.h @@ -1,5 +1,6 @@ #pragma once #include "types.h" +#include "networking/interface_manager.h" #ifdef __cplusplus extern "C" { @@ -23,9 +24,14 @@ bool ipv6_is_linklocal(const uint8_t ip[16]); int ipv6_cmp(const uint8_t a[16], const uint8_t b[16]); void ipv6_cpy(uint8_t dst[16], const uint8_t src[16]); int ipv6_common_prefix_len(const uint8_t a[16], const uint8_t b[16]); +void ipv6_prefix_network(const uint8_t ip[16], uint8_t prefix_len, uint8_t out[16]); void ipv6_make_multicast(uint8_t scope, ipv6_mcast_kind_t kind, const uint8_t unicast[16], uint8_t out[16]); void ipv6_to_string(const uint8_t ip[16], char* buf, int buflen); bool ipv6_parse(const char* s, uint8_t out[16]); + +bool ipv6_l3_is_active(l3_ipv6_interface_t *v6); +bool ipv6_l3_is_ready(l3_ipv6_interface_t *v6); +bool ipv6_l3_is_tcp_usable(l3_ipv6_interface_t *v6); void ipv6_multicast_mac(const uint8_t ip[16], uint8_t mac[6]); void ipv6_make_lla_from_mac(uint8_t ifindex, uint8_t out[16]); diff --git a/kernel/networking/internet_layer/mld.c b/kernel/networking/internet_layer/mld.c index f8414d0e..a62747b6 100644 --- a/kernel/networking/internet_layer/mld.c +++ b/kernel/networking/internet_layer/mld.c @@ -2,6 +2,7 @@ #include "kernel_processes/kprocess_loader.h" #include "math/rng.h" +#include "random/random.h" #include "networking/interface_manager.h" #include "net/checksums.h" #include "networking/internet_layer/ipv6.h" @@ -224,9 +225,7 @@ static int mld_daemon_entry(int argc, char* argv[]) { mld_daemon_running = 1; if(! mld_rng_inited) { - uint64_t virt_timer; - asm volatile ("mrs %0, cntvct_el0" : "=r"(virt_timer)); - rng_seed(&mld_rng, virt_timer); + rng_init_random(&mld_rng); mld_rng_inited = 1; } @@ -294,9 +293,7 @@ static void schedule_report(uint8_t ifindex, const uint8_t group[16], uint16_t m if(!ipv6_is_multicast(group)) return; if(!mld_rng_inited) { - uint64_t virt_timer; - asm volatile ("mrs %0, cntvct_el0" : "=r"(virt_timer)); - rng_seed(&mld_rng, virt_timer); + rng_init_random(&mld_rng); mld_rng_inited = 1; } diff --git a/kernel/networking/link_layer/arp.c b/kernel/networking/link_layer/arp.c index bc84d05a..13726de6 100644 --- a/kernel/networking/link_layer/arp.c +++ b/kernel/networking/link_layer/arp.c @@ -199,9 +199,8 @@ static uint32_t pick_spa_for_l2(uint8_t ifindex, uint32_t target_ip){ 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){ + if (!ipv4_l3_is_ready(v4)) continue; + if (v4->mask){ uint32_t a = v4->ip & v4->mask; uint32_t b = target_ip & v4->mask; if (a == b) return v4->ip; @@ -209,9 +208,8 @@ static uint32_t pick_spa_for_l2(uint8_t ifindex, uint32_t target_ip){ } for (int s=0;sl3_v4[s]; - if (!v4) continue; - if (v4->mode == IPV4_CFG_DISABLED) continue; - if (v4->ip) return v4->ip; + if (!ipv4_l3_is_ready(v4)) continue; + return v4->ip; } return 0; } @@ -308,8 +306,7 @@ static bool l2_has_ip(uint8_t ifindex, uint32_t ip){ if (!l2) return false; for (int s=0;sl3_v4[s]; - if (!v4) continue; - if (v4->mode == IPV4_CFG_DISABLED) continue; + if (!ipv4_l3_is_ready(v4)) continue; if (v4->ip == ip) return true; } return false; diff --git a/kernel/networking/link_layer/ndp.c b/kernel/networking/link_layer/ndp.c index 9c5aa98b..3371c899 100644 --- a/kernel/networking/link_layer/ndp.c +++ b/kernel/networking/link_layer/ndp.c @@ -11,6 +11,7 @@ #include "networking/network.h" #include "process/scheduler.h" #include "math/rng.h" +#include "random/random.h" typedef struct { ndp_entry_t entries[NDP_TABLE_MAX]; @@ -72,6 +73,18 @@ typedef struct __attribute__((packed)) { static uint8_t g_rs_tries[MAX_L2_INTERFACES]; static uint32_t g_rs_timer_ms[MAX_L2_INTERFACES]; + +static void ndp_mark_neighbor_observed(ndp_entry_t *e, bool solicited) { + if (!e) return; + if (solicited) { + e->state = NDP_STATE_REACHABLE; + e->timer_ms = g_ndp_reachable_time_ms; + } else { + e->state = NDP_STATE_STALE; + e->timer_ms = 0; + } +} + static void make_random_iid(uint8_t out_iid[8]) { uint64_t x = 0; @@ -93,7 +106,6 @@ static void handle_dad_failed(l3_ipv6_interface_t* v6) { uint8_t iid[8]; uint8_t new_ip[16]; - uint8_t zero16[16] = {0}; make_random_iid(iid); @@ -103,7 +115,7 @@ static void handle_dad_failed(l3_ipv6_interface_t* v6) { memset(new_ip + 2, 0, 6); memcpy(new_ip + 8, iid, 8); - (void)l3_ipv6_update(v6->l3_id, new_ip, 64, zero16, v6->cfg, v6->kind); + (void)l3_ipv6_update(v6->l3_id, new_ip, 64, (const uint8_t[16]){0}, v6->cfg, v6->kind); (void)ndp_request_dad_on(v6->l2 ? v6->l2->ifindex : 0, new_ip); return; } @@ -117,7 +129,7 @@ static void handle_dad_failed(l3_ipv6_interface_t* v6) { return; } - if (memcmp(v6->prefix, zero16, 16) != 0) ipv6_cpy(new_ip, v6->prefix); + if (!ipv6_is_unspecified(v6->prefix)) ipv6_cpy(new_ip, v6->prefix); else { ipv6_cpy(new_ip, v6->ip); memset(new_ip + 8, 0, 8); @@ -160,27 +172,21 @@ static void apply_ra_policy(uint32_t now_ms, l2_interface_t* l2) { int has_lla_ok = 0; for (int i = 0; i < MAX_IPV6_PER_INTERFACE; i++) { l3_ipv6_interface_t* v6 = l2->l3_v6[i]; - if (!v6) continue; - if (v6->cfg == IPV6_CFG_DISABLE) continue; + if (!ipv6_l3_is_ready(v6)) continue; if (!ipv6_is_linklocal(v6->ip)) continue; - if (v6->dad_state == IPV6_DAD_OK) { - has_lla_ok = 1; - break; - } + has_lla_ok = 1; + break; } if (!has_lla_ok) return; - uint8_t zero16[16] = {0}; - for (int i = 0; i < MAX_IPV6_PER_INTERFACE; i++) { l3_ipv6_interface_t* v6 = l2->l3_v6[i]; - if (!v6) continue; - if (v6->cfg == IPV6_CFG_DISABLE) continue; + if (!ipv6_l3_is_active(v6)) continue; if (!(v6->kind & IPV6_ADDRK_GLOBAL)) continue; if (!(v6->cfg & (IPV6_CFG_SLAAC | IPV6_CFG_DHCPV6))) continue; if (!v6->ra_has)continue; - if (memcmp(v6->prefix, zero16, 16) == 0) continue; + if (ipv6_is_unspecified(v6->prefix)) continue; uint8_t m = (v6->ra_flags & RA_FLAG_M) ? 1u : 0u; uint8_t o = (v6->ra_flags & RA_FLAG_O) ? 1u : 0u; if (!v6->ra_autonomous) { @@ -258,7 +264,6 @@ static void ndp_on_ra(uint8_t ifindex, const uint8_t router_ip[16], uint16_t rou if (!l2) return; uint32_t now_ms = get_time(); - uint8_t zero16[16] = {0}; l3_ipv6_interface_t* slot = NULL; for (int i = 0; i < MAX_IPV6_PER_INTERFACE; i++) { @@ -268,7 +273,7 @@ static void ndp_on_ra(uint8_t ifindex, const uint8_t router_ip[16], uint16_t rou if (!(v6->kind == IPV6_ADDRK_GLOBAL)) continue; if (!(v6->cfg & (IPV6_CFG_SLAAC | IPV6_CFG_DHCPV6))) continue; - if (memcmp(v6->prefix, zero16, 16) != 0) { + if (!ipv6_is_unspecified(v6->prefix)) { if (ipv6_common_prefix_len(v6->prefix, prefix) >=64) { slot = v6; break; @@ -292,7 +297,7 @@ static void ndp_on_ra(uint8_t ifindex, const uint8_t router_ip[16], uint16_t rou uint8_t ph[16]; ipv6_make_placeholder_gua(ph); - uint8_t id = l3_ipv6_add_to_interface(ifindex, ph, 64, zero16, IPV6_CFG_SLAAC, IPV6_ADDRK_GLOBAL); + uint8_t id = l3_ipv6_add_to_interface(ifindex, ph, 64, (const uint8_t[16]){0}, IPV6_CFG_SLAAC, IPV6_ADDRK_GLOBAL); if (!id) return; slot = l3_ipv6_find_by_id(id); @@ -308,12 +313,12 @@ static void ndp_on_ra(uint8_t ifindex, const uint8_t router_ip[16], uint16_t rou ipv6_cpy(slot->prefix, prefix); if (slot->ra_is_default && router_ip) ipv6_cpy(slot->gateway, router_ip); - else ipv6_cpy(slot->gateway, zero16); + else memset(slot->gateway, 0, 16); slot->valid_lifetime = valid_lft; slot->preferred_lifetime = preferred_lft; - if (memcmp(slot->ip, zero16, 16) == 0) slot->timestamp_created = now_ms; + if (ipv6_is_unspecified(slot->ip)) slot->timestamp_created = now_ms; if(!ipv6_is_placeholder_gua(slot->ip) && !ipv6_is_unspecified(slot->ip)) memcpy(slot->interface_id, slot->ip + 8, 8); } @@ -579,9 +584,7 @@ static void ndp_send_probe(uint8_t ifindex, ndp_entry_t* e) { if (l2) { for (int i = 0; i < MAX_IPV6_PER_INTERFACE; i++) { l3_ipv6_interface_t* v6 = l2->l3_v6[i]; - if (!v6) continue; - if (v6->cfg == IPV6_CFG_DISABLE) continue; - if (v6->dad_state!= IPV6_DAD_OK) continue; + if (!ipv6_l3_is_ready(v6)) continue; if (ipv6_is_linklocal(v6->ip)) { memcpy(src_ip, v6->ip, 16); @@ -838,9 +841,7 @@ void ndp_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[ for (int i = 0; i < MAX_IPV6_PER_INTERFACE; i++) { l3_ipv6_interface_t* v6 = l2->l3_v6[i]; - if (!v6) continue; - if (v6->cfg == IPV6_CFG_DISABLE) continue; - if (v6->dad_state != IPV6_DAD_OK) continue; + if (!ipv6_l3_is_ready(v6)) continue; if (ipv6_cmp(v6->ip, ns.target) == 0) { ipv6_cpy(src_my, v6->ip); @@ -920,13 +921,7 @@ void ndp_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[ e->is_router = router ? 1 : 0; e->router_lifetime_ms = e->is_router ? e->ttl_ms : 0; - if (solicited) { - e->state = NDP_STATE_REACHABLE; - e->timer_ms = g_ndp_reachable_time_ms; - } else { - e->state = NDP_STATE_STALE; - e->timer_ms = 0; - } + ndp_mark_neighbor_observed(e, solicited); } else { int mac_changed = memcmp(old_mac, src_mac, 6) != 0; @@ -934,34 +929,18 @@ void ndp_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[ memcpy(e->mac, src_mac, 6); e->ttl_ms = g_ndp_reachable_time_ms * 4; - if (solicited) { - e->state = NDP_STATE_REACHABLE; - e->timer_ms = g_ndp_reachable_time_ms; - } else { - e->state = NDP_STATE_STALE; - e->timer_ms = 0; - } + ndp_mark_neighbor_observed(e, solicited); } else { if (!mac_changed) { - if (solicited) { - e->state = NDP_STATE_REACHABLE; - e->timer_ms = g_ndp_reachable_time_ms; - } + if (solicited) ndp_mark_neighbor_observed(e, true); } else { if (override) { memcpy(e->mac, src_mac, 6); e->ttl_ms = g_ndp_reachable_time_ms * 4; - if (solicited) { - e->state = NDP_STATE_REACHABLE; - e->timer_ms = g_ndp_reachable_time_ms; - } else { - e->state = NDP_STATE_STALE; - e->timer_ms = 0; - } + ndp_mark_neighbor_observed(e, solicited); } else { - e->state = NDP_STATE_STALE; - e->timer_ms = 0; + ndp_mark_neighbor_observed(e, false); } } } @@ -1061,13 +1040,11 @@ void ndp_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[ uint32_t addr_bytes = opt_size - 8u; uint32_t addr_count = addr_bytes / 16u; - uint8_t zero16[16] = {0}; - uint8_t a0[16], a1[16]; if (addr_count >= 1 && !netpkt_copyout(pkt, opt_off + 8u, a0, sizeof(a0))) break; - else if (addr_count < 1) memcpy(a0, zero16, sizeof(a0)); + else if (addr_count < 1) memset(a0, 0, sizeof(a0)); if (addr_count >= 2 && !netpkt_copyout(pkt, opt_off + 24u, a1, sizeof(a1))) break; - else if (addr_count < 2) memcpy(a1, zero16, sizeof(a1)); + else if (addr_count < 2) memset(a1, 0, sizeof(a1)); l3_ipv6_interface_t* slot = NULL; @@ -1078,7 +1055,7 @@ void ndp_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[ if (!(v6->kind & IPV6_ADDRK_GLOBAL)) continue; if (!(v6->cfg & (IPV6_CFG_SLAAC | IPV6_CFG_DHCPV6))) continue; - if (memcmp(v6->prefix, zero16, 16) != 0) { + if (!ipv6_is_unspecified(v6->prefix)) { if (ipv6_common_prefix_len(v6->prefix, src_ip) >= 64) { slot = v6; break; @@ -1132,9 +1109,7 @@ int ndp_daemon_entry(int argc, char* argv[]) { (void)argv; g_ndp_pid = (uint16_t)get_current_proc_pid(); - uint64_t virt_timer; - asm volatile ("mrs %0, cntvct_el0" : "=r"(virt_timer)); - rng_seed(&g_rng, virt_timer); + rng_init_random(&g_rng); const uint32_t tick_ms = 1000; @@ -1204,12 +1179,11 @@ int ndp_daemon_entry(int argc, char* argv[]) { v6->dad_timer_ms = 0; uint8_t sn[16]; - uint8_t zero16[16] = {0}; ipv6_make_multicast(2, IPV6_MCAST_SOLICITED_NODE, v6->ip, sn); (void)l2_ipv6_mcast_join(l2->ifindex, sn); - ndp_send_ns_on(l2->ifindex, v6->ip, zero16); + ndp_send_ns_on(l2->ifindex, v6->ip, (const uint8_t[16]){0}); v6->dad_probes_sent++; } } else { @@ -1218,8 +1192,7 @@ int ndp_daemon_entry(int argc, char* argv[]) { v6->dad_state = IPV6_DAD_OK; uint8_t all_nodes[16]; - uint8_t zero16[16] = {0}; - ipv6_make_multicast(2, IPV6_MCAST_ALL_NODES, zero16, all_nodes); + ipv6_make_multicast(2, IPV6_MCAST_ALL_NODES, (const uint8_t[16]){0}, all_nodes); const uint8_t* my_mac = network_get_mac(l2->ifindex); if (my_mac) (void)ndp_send_na_on(l2->ifindex, all_nodes, v6->ip, v6->ip, 0, my_mac, 0); diff --git a/kernel/networking/netpkt.c b/kernel/networking/netpkt.c index e207edc3..6659b402 100644 --- a/kernel/networking/netpkt.c +++ b/kernel/networking/netpkt.c @@ -601,13 +601,6 @@ bool netpkt_copyout(const netpkt_t* p, uint32_t off, void* dst, uint32_t len) { if (!src) return false; src += (uintptr_t)off; - if (len <= 16u) { - const uint8_t* s = (const uint8_t*)src; - uint8_t* d = (uint8_t*)dst; - for (uint32_t i = 0; i < len; i++) d[i] = s[i]; - return true; - } - memcpy(dst, (const void*)src, len); return true; } diff --git a/kernel/networking/processes/net_proc.c b/kernel/networking/processes/net_proc.c index d92f0ff4..80cb9d89 100644 --- a/kernel/networking/processes/net_proc.c +++ b/kernel/networking/processes/net_proc.c @@ -42,7 +42,7 @@ static int udp_probe_server(uint32_t probe_ip, uint16_t probe_port, net_l4_endpo } net_l4_endpoint dst; - make_ep(probe_ip, probe_port, IP_VER4, &dst); + make_ep(&probe_ip, probe_port, IP_VER4, &dst); static const char greeting[] = "hello"; if (send_to_socket(sock, &dst, greeting, sizeof(greeting)) < 0) { @@ -78,14 +78,14 @@ static int net_has_ready_address(void) { for (uint8_t j = 0; j < MAX_IPV4_PER_INTERFACE; j++) { l3_ipv4_interface_t *v4 = l2->l3_v4[j]; - if (!v4 || v4->mode == IPV4_CFG_DISABLED || v4->is_localhost) continue; - if (!ipv4_is_unspecified(v4->ip) && !ipv4_is_loopback(v4->ip)) return 1; + if (!ipv4_l3_is_ready(v4) || v4->is_localhost) continue; + if (!ipv4_is_loopback(v4->ip)) return 1; } for (uint8_t j = 0; j < MAX_IPV6_PER_INTERFACE; j++) { l3_ipv6_interface_t *v6 = l2->l3_v6[j]; - if (!v6 || v6->cfg == IPV6_CFG_DISABLE || v6->is_localhost) continue; - if (!ipv6_is_unspecified(v6->ip) && !ipv6_is_loopback(v6->ip) && v6->dad_state == IPV6_DAD_OK) return 1; + if (!ipv6_l3_is_ready(v6) || v6->is_localhost) continue; + if (!ipv6_is_loopback(v6->ip)) return 1; } } @@ -278,8 +278,8 @@ static int net_test_entry(int argc, char *argv[]) { 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 || ifv4->mode == IPV4_CFG_DISABLED || ifv4->is_localhost) continue; - if (ipv4_is_unspecified(ifv4->ip) || ipv4_is_loopback(ifv4->ip) || !ifv4->mask) continue; + if (!ipv4_l3_is_ready(ifv4) || ifv4->is_localhost) continue; + if (ipv4_is_loopback(ifv4->ip) || !ifv4->mask) continue; uint32_t probe_ip = ipv4_broadcast_calc(ifv4->ip, ifv4->mask); if (!probe_ip) continue; diff --git a/kernel/networking/transport_layer/csocket_tcp.c b/kernel/networking/transport_layer/csocket_tcp.c new file mode 100644 index 00000000..983db44f --- /dev/null +++ b/kernel/networking/transport_layer/csocket_tcp.c @@ -0,0 +1,591 @@ +#include "csocket_tcp.h" +#include "networking/transport_layer/socket_bind.h" +#include "networking/transport_layer/tcp.h" +#include "networking/internet_layer/ipv4.h" +#include "networking/internet_layer/ipv4_route.h" +#include "networking/internet_layer/ipv4_utils.h" +#include "networking/internet_layer/ipv6_route.h" +#include "networking/internet_layer/ipv6_utils.h" +#include "networking/transport_layer/trans_utils.h" +#include "networking/net_logger/net_logger.h" +#include "syscalls/syscalls.h" +#include "std/memory.h" +#include "alloc/allocate.h" + +#define TCP_MAX_BACKLOG 128 +#define TCP_DEFAULT_SOCKET_BUF (256 * 1024) + +typedef struct tcp_socket { + uint16_t localPort; + net_l4_endpoint remoteEP; + bool connected; + ksocket_t* ownerSocket; + SocketExtraOptions extraOpts; + SockBindSpec bindSpec; + socket_bind_token_t bindToken; + tcp_data flow; + ksocket_t** pending; + int backlogCap; + int backlogLen; + bool listening; +} tcp_socket_t; + +static ksocket_t* tcp_socket_pop_pending_at(tcp_socket_t* s, int idx) { + if (!s || idx < 0 || idx >= s->backlogLen) return NULL; + + ksocket_t* client = s->pending[idx]; + for (int i = idx + 1; i < s->backlogLen; ++i) s->pending[i-1] = s->pending[i]; + + s->pending[--s->backlogLen] = NULL; + return client; +} + +socket_impl_t socket_tcp_create(ksocket_t* owner, const SocketExtraOptions* extra) { + if (!owner) return NULL; + + tcp_socket_t* s = (tcp_socket_t*)zalloc(sizeof(*s)); + if (!s) return NULL; + + s->ownerSocket = owner; + s->remoteEP.ver = IP_VER4; + if (extra) s->extraOpts = *extra; + if (!(s->extraOpts.flags & SOCK_OPT_BUF_SIZE)) { + s->extraOpts.flags |= SOCK_OPT_BUF_SIZE; + s->extraOpts.buf_size = TCP_DEFAULT_SOCKET_BUF; + } + + if (!s->extraOpts.buf_size) s->extraOpts.buf_size = TCP_DEFAULT_SOCKET_BUF; + if (s->extraOpts.buf_size > TCP_DEFAULT_SOCKET_BUF) s->extraOpts.buf_size = TCP_DEFAULT_SOCKET_BUF; + return s; +} + +int32_t socket_setopt_tcp(socket_impl_t sh, int32_t opt, const void* value, uint32_t len) { + tcp_socket_t* s = (tcp_socket_t*)sh; + if (!s) return SOCK_ERR_INVAL; + + switch ((uint32_t)opt) { + case SOCK_OPT_KEEPALIVE: { + if (!value || len != sizeof(uint32_t)) return SOCK_ERR_INVAL; + uint32_t v = 0; + memcpy(&v, value, sizeof(v)); + if (v) { + s->extraOpts.flags |= SOCK_OPT_KEEPALIVE; + if (!s->extraOpts.keepalive_ms) s->extraOpts.keepalive_ms = SOCKET_DEFAULT_KEEPALIVE_MS; + } else s->extraOpts.flags &= ~SOCK_OPT_KEEPALIVE; + if (s->flow.flow_generation) tcp_flow_apply_socket_options(&s->flow, &s->extraOpts); + return SOCK_OK; + } + case SOCK_OPT_KEEPALIVE_INTERVAL: { + if (!value || len != sizeof(uint32_t)) return SOCK_ERR_INVAL; + uint32_t v = 0; + memcpy(&v, value, sizeof(v)); + if (!v) return SOCK_ERR_INVAL; + s->extraOpts.keepalive_ms = v; + s->extraOpts.flags |= SOCK_OPT_KEEPALIVE | SOCK_OPT_KEEPALIVE_INTERVAL; + if (s->flow.flow_generation) tcp_flow_apply_socket_options(&s->flow, &s->extraOpts); + return SOCK_OK; + } + case SOCK_OPT_TCP_NO_DELAY: { + if (!value || len != sizeof(uint32_t)) return SOCK_ERR_INVAL; + uint32_t v = 0; + memcpy(&v, value, sizeof(v)); + if (v) s->extraOpts.flags |= SOCK_OPT_TCP_NO_DELAY; + else s->extraOpts.flags &= ~SOCK_OPT_TCP_NO_DELAY; + if (s->flow.flow_generation) tcp_flow_apply_socket_options(&s->flow, &s->extraOpts); + return SOCK_OK; + } + case SOCK_OPT_SEND_BUF_SIZE: { + if (!value || len != sizeof(uint32_t)) return SOCK_ERR_INVAL; + uint32_t v = 0; + memcpy(&v, value, sizeof(v)); + if (!v) return SOCK_ERR_INVAL; + s->extraOpts.send_buf_size = v; + s->extraOpts.flags |= SOCK_OPT_SEND_BUF_SIZE; + if (s->flow.flow_generation) tcp_flow_apply_socket_options(&s->flow, &s->extraOpts); + return SOCK_OK; + } + case SOCK_OPT_MCAST_JOIN: + return SOCK_ERR_INVAL; + case SOCK_OPT_RECV_TIMEOUT: + case SOCK_OPT_SEND_TIMEOUT: + case SOCK_OPT_BUF_SIZE: + case SOCK_OPT_DEBUG: + case SOCK_OPT_DONTFRAG: + case SOCK_OPT_BROADCAST_ALLOWED: + case SOCK_OPT_TTL: { + int32_t rc = socket_extra_setopt(&s->extraOpts, opt, value, len); + if (rc != SOCK_OK) return rc; + if (s->flow.flow_generation) tcp_flow_apply_socket_options(&s->flow, &s->extraOpts); + return SOCK_OK; + } + default: + return SOCK_ERR_INVAL; + } +} + +int32_t socket_getopt_tcp(socket_impl_t sh, int32_t opt, void* value, uint32_t* len) { + tcp_socket_t* s = (tcp_socket_t*)sh; + if (!s || !len) return SOCK_ERR_INVAL; + + uint32_t v = 0; + switch ((uint32_t)opt) { + case SOCK_OPT_KEEPALIVE: + v = (s->extraOpts.flags & SOCK_OPT_KEEPALIVE) != 0; + break; + case SOCK_OPT_KEEPALIVE_INTERVAL: + v = s->extraOpts.keepalive_ms; + break; + case SOCK_OPT_TCP_NO_DELAY: + v = (s->extraOpts.flags & SOCK_OPT_TCP_NO_DELAY) != 0; + break; + case SOCK_OPT_SEND_BUF_SIZE: + v = s->extraOpts.send_buf_size; + break; + case SOCK_OPT_MCAST_JOIN: + return SOCK_ERR_INVAL; + case SOCK_OPT_RECV_TIMEOUT: + case SOCK_OPT_SEND_TIMEOUT: + case SOCK_OPT_BUF_SIZE: + case SOCK_OPT_DEBUG: + case SOCK_OPT_DONTFRAG: + case SOCK_OPT_BROADCAST_ALLOWED: + case SOCK_OPT_TTL: + return socket_extra_getopt(&s->extraOpts, opt, value, len); + default: + return SOCK_ERR_INVAL; + } + + if (!value) { + *len = sizeof(uint32_t); + return SOCK_OK; + } + if (*len < sizeof(uint32_t)) return SOCK_ERR_INVAL; + memcpy(value, &v, sizeof(v)); + *len = sizeof(uint32_t); + return SOCK_OK; +} + +int32_t socket_bind_tcp(socket_impl_t sh, const SockBindSpec* spec_in, uint16_t port) { + tcp_socket_t* s = (tcp_socket_t*)sh; + if (!s || !spec_in) return SOCK_ERR_INVAL; + + netlog_socket_event_t ev = {0}; + ev.comp = NETLOG_COMP_TCP; + ev.action = NETLOG_ACT_BIND; + ev.pid = socket_core_pid(s->ownerSocket); + ev.u0 = port; + ev.bind_spec = *spec_in; + netlog_socket_event(&s->extraOpts, &ev); + + if (s->localPort) return SOCK_ERR_BOUND; + if (!s->ownerSocket) return SOCK_ERR_SYS; + + SockBindSpec spec = *spec_in; + if (!socket_bind_normalize_spec(&spec)) return SOCK_ERR_INVAL; + + if (spec.kind == BIND_L3) { + if (!spec.l3_id) return SOCK_ERR_INVAL; + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(spec.l3_id); + l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(spec.l3_id); + bool ok4 = (spec.ver == 0 || spec.ver == IP_VER4) && ipv4_l3_is_ready(v4); + bool ok6 = (spec.ver == 0 || spec.ver == IP_VER6) && ipv6_l3_is_tcp_usable(v6); + if (!ok4 && !ok6) return SOCK_ERR_INVAL; + } else if (spec.kind == BIND_L2) { + l2_interface_t* l2 = l2_interface_find_by_index(spec.ifindex); + if (!l2 || !l2->is_up) return SOCK_ERR_INVAL; + } else if (spec.kind == BIND_IP) { + if (spec.ver == IP_VER4) { + uint32_t v4ip = 0; + memcpy(&v4ip, spec.ip, 4); + if (!ipv4_is_unspecified(v4ip) && !ipv4_l3_is_ready(l3_ipv4_find_by_ip(v4ip))) return SOCK_ERR_INVAL; + } else if (spec.ver == IP_VER6) { + if (!ipv6_is_unspecified(spec.ip) && !ipv6_l3_is_tcp_usable(l3_ipv6_find_by_ip(spec.ip))) return SOCK_ERR_INVAL; + } else return SOCK_ERR_INVAL; + } else if (spec.kind != BIND_ANY && spec.kind != BIND_ANY4 && spec.kind != BIND_ANY6) return SOCK_ERR_INVAL; + + int bind_port = port; + socket_bind_token_t token = 0; + if (bind_port == 0) { + bind_port = socket_bind_alloc_ephemeral(s->ownerSocket, PROTO_TCP, &spec, &token); + if (bind_port < 0) return SOCK_ERR_NO_PORT; + } else if (!socket_bind_insert(s->ownerSocket, PROTO_TCP, &spec, port, &token)) return SOCK_ERR_BOUND; + + s->bindSpec = spec; + s->bindToken = token; + s->localPort = (uint16_t)bind_port; + return SOCK_OK; +} + +int32_t socket_listen_tcp(socket_impl_t sh, int32_t backlog) { + tcp_socket_t* s = (tcp_socket_t*)sh; + if (!s) return SOCK_ERR_INVAL; + if (!s->bindToken || s->connected) return SOCK_ERR_STATE; + + int cap = backlog > TCP_MAX_BACKLOG ? TCP_MAX_BACKLOG : backlog; + if (cap < 1) cap = 1; + + if (s->pending) { + for (int i = 0; i < s->backlogLen; ++i) { + if (!s->pending[i]) continue; + socket_core_close_socket(s->pending[i]); + socket_core_put(s->pending[i]); + } + release(s->pending); + } + + s->pending = (ksocket_t**)zalloc(sizeof(ksocket_t*) * cap); + if (!s->pending) { + s->backlogCap = 0; + s->backlogLen = 0; + s->listening = false; + return SOCK_ERR_SYS; + } + + s->backlogCap = cap; + s->backlogLen = 0; + s->listening = true; + return SOCK_OK; +} + +//TODO replace polling with a socket wait queue when kernel events are present +ksocket_t* socket_accept_tcp(socket_impl_t sh) { + tcp_socket_t* s = (tcp_socket_t*)sh; + if (!s || !s->listening || !s->pending) return NULL; + + const int max_empty_iters = 200; + const int ready_wait_iters = 4; + int iter = 0; + while (s->backlogLen == 0) { + if (++iter > max_empty_iters) return NULL; + msleep(5); + } + + iter = 0; + while (1) { + for (int i = 0; i < s->backlogLen;) { + ksocket_t* owner = s->pending[i]; + tcp_socket_t* client = owner ? (tcp_socket_t*)socket_core_impl(owner) : NULL; + + if (client && client->flow.flow_generation) { + bool readable = tcp_flow_readable(&client->flow) != 0; + bool closed = tcp_flow_recv_closed(&client->flow); + if (!closed || readable) { + ++i; + continue; + } + } + + owner = tcp_socket_pop_pending_at(s, i); + if (owner) { + socket_core_close_socket(owner); + socket_core_put(owner); + } + } + + if (s->backlogLen == 0) return NULL; + + for (int i = 0; i < s->backlogLen; ++i) { + ksocket_t* owner = s->pending[i]; + tcp_socket_t* client = owner ? (tcp_socket_t*)socket_core_impl(owner) : NULL; + if (!client) continue; + if (!client->flow.flow_generation || tcp_flow_readable(&client->flow) || tcp_flow_recv_closed(&client->flow)) return tcp_socket_pop_pending_at(s, i); + } + + if (++iter > ready_wait_iters) { + if (s->backlogLen >= s->backlogCap) { + ksocket_t* owner = tcp_socket_pop_pending_at(s, 0); + if (owner) { + socket_core_close_socket(owner); + socket_core_put(owner); + } + } + return NULL; + } + msleep(5); + } +} + +int32_t socket_connect_tcp(socket_impl_t sh, const net_l4_endpoint* dst) { + tcp_socket_t* s = (tcp_socket_t*)sh; + if (!s) return SOCK_ERR_INVAL; + + netlog_socket_event_t ev = {0}; + ev.comp = NETLOG_COMP_TCP; + ev.action = NETLOG_ACT_CONNECT; + ev.pid = socket_core_pid(s->ownerSocket); + if (dst) ev.dst_ep = *dst; + netlog_socket_event(&s->extraOpts, &ev); + + if (s->connected || s->listening) return SOCK_ERR_STATE; + if (!dst || !dst->port) return SOCK_ERR_INVAL; + + net_l4_endpoint d = *dst; + uint8_t chosen_l3 = 0; + + if (d.ver == IP_VER6) { + ipv6_tx_plan_t p6; + if (!socket_bind_build_ipv6_tx_plan(&s->bindSpec, s->localPort, d.ip, &p6)) return SOCK_ERR_SYS; + chosen_l3 = p6.l3_id; + } else if (d.ver == IP_VER4) { + uint32_t dip = 0; + memcpy(&dip, d.ip, 4); + ipv4_tx_plan_t p4; + if (!socket_bind_build_ipv4_tx_plan(&s->bindSpec, s->localPort, dip, &p4)) return SOCK_ERR_SYS; + chosen_l3 = p4.l3_id; + } else return SOCK_ERR_INVAL; + + if (!chosen_l3) return SOCK_ERR_SYS; + + if (d.ver == IP_VER4) { + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(chosen_l3); + if (!ipv4_l3_is_ready(v4)) return SOCK_ERR_SYS; + } else if (d.ver == IP_VER6) { + l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(chosen_l3); + if (!ipv6_l3_is_tcp_usable(v6)) return SOCK_ERR_SYS; + } else return SOCK_ERR_INVAL; + + bool ephemeral_allocated = false; + if (s->localPort == 0) { + if (!s->ownerSocket) return SOCK_ERR_SYS; + socket_bind_token_t token = 0; + int p = socket_bind_alloc_ephemeral_l3(s->ownerSocket, PROTO_TCP, chosen_l3, &token); + if (p < 0) return SOCK_ERR_NO_PORT; + s->localPort = (uint16_t)p; + s->bindToken = token; + memset(&s->bindSpec, 0, sizeof(s->bindSpec)); + s->bindSpec.kind = BIND_L3; + s->bindSpec.ver = d.ver; + s->bindSpec.l3_id = chosen_l3; + ephemeral_allocated = true; + } + + memset(&s->flow, 0, sizeof(s->flow)); + bool ok = tcp_handshake_l3(chosen_l3, s->localPort, &d, &s->flow, &s->extraOpts); + if (ok && d.ver == IP_VER4) { + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(chosen_l3); + ok = ipv4_l3_is_ready(v4); + } else if (ok && d.ver == IP_VER6) ok = ipv6_l3_is_tcp_usable(l3_ipv6_find_by_id(chosen_l3)); + + if (!ok) { + if (ephemeral_allocated) { + if (s->bindToken) { + socket_bind_remove(s->bindToken); + s->bindToken = 0; + } + s->localPort = 0; + memset(&s->bindSpec, 0, sizeof(s->bindSpec)); + s->connected = false; + s->remoteEP.port = 0; + s->remoteEP.ver = IP_VER4; + memset(s->remoteEP.ip, 0, sizeof(s->remoteEP.ip)); + } + return SOCK_ERR_SYS; + } + + s->remoteEP = d; + s->connected = true; + + netlog_socket_event_t ev1 = {0}; + ev1.comp = NETLOG_COMP_TCP; + ev1.action = NETLOG_ACT_CONNECTED; + ev1.pid = socket_core_pid(s->ownerSocket); + ev1.u0 = s->localPort; + ev1.u1 = s->remoteEP.port; + ev1.local_port = s->localPort; + ev1.remote_ep = s->remoteEP; + netlog_socket_event(&s->extraOpts, &ev1); + return SOCK_OK; +} + +int64_t socket_send_tcp(socket_impl_t sh, const void* buf, uint64_t len) { + tcp_socket_t* s = (tcp_socket_t*)sh; + if (!s) return SOCK_ERR_INVAL; + + netlog_socket_event_t ev = {0}; + ev.comp = NETLOG_COMP_TCP; + ev.action = NETLOG_ACT_SEND; + ev.pid = socket_core_pid(s->ownerSocket); + ev.u0 = (uint32_t)len; + ev.local_port = s->localPort; + ev.remote_ep = s->remoteEP; + netlog_socket_event(&s->extraOpts, &ev); + + if (!s->connected || !s->flow.flow_generation) return SOCK_ERR_STATE; + if (!buf && len) return SOCK_ERR_INVAL; + if (!len) return 0; + + uint32_t chunk = len > UINT32_MAX ? UINT32_MAX : (uint32_t)len; + s->flow.payload.ptr = (uintptr_t)buf; + s->flow.payload.size = chunk; + s->flow.flags = (1u << PSH_F) | (1u << ACK_F); + + tcp_result_t res = tcp_flow_send(&s->flow); + if (res != TCP_OK) return (int64_t)res; + if (s->flow.payload.size) return (int64_t)s->flow.payload.size; + return TCP_WOULDBLOCK; +} + +int64_t socket_recv_tcp(socket_impl_t sh, void* buf, uint64_t len) { + tcp_socket_t* s = (tcp_socket_t*)sh; + if (!s) return SOCK_ERR_INVAL; + + netlog_socket_event_t ev = {0}; + ev.comp = NETLOG_COMP_TCP; + ev.action = NETLOG_ACT_RECV; + ev.pid = socket_core_pid(s->ownerSocket); + ev.u0 = (uint32_t)len; + ev.local_port = s->localPort; + ev.remote_ep = s->remoteEP; + netlog_socket_event(&s->extraOpts, &ev); + + if (!buf || !len) return 0; + if (!s->connected || !s->flow.flow_generation) return s->connected ? TCP_WOULDBLOCK : 0; + + int64_t n = tcp_flow_read(&s->flow, buf, len); + if (n != 0) return n; + if (tcp_flow_recv_closed(&s->flow)) return 0; + if (s->connected) return TCP_WOULDBLOCK; + return 0; +} + +int32_t socket_close_tcp(socket_impl_t sh) { + tcp_socket_t* s = (tcp_socket_t*)sh; + if (!s) return SOCK_ERR_INVAL; + + netlog_socket_event_t ev = {0}; + ev.comp = NETLOG_COMP_TCP; + ev.action = NETLOG_ACT_CLOSE; + ev.pid = socket_core_pid(s->ownerSocket); + ev.local_port = s->localPort; + ev.remote_ep = s->remoteEP; + netlog_socket_event(&s->extraOpts, &ev); + + if (s->connected && s->flow.flow_generation) { + if (!tcp_flow_is_closed(&s->flow)) { + tcp_flow_flush(&s->flow); + tcp_flow_close(&s->flow); + } + s->connected = false; + memset(&s->flow, 0, sizeof(s->flow)); + } + + if (s->pending) { + for (int i = 0; i < s->backlogLen; ++i) { + if (!s->pending[i]) continue; + socket_core_close_socket(s->pending[i]); + socket_core_put(s->pending[i]); + s->pending[i] = NULL; + } + release(s->pending); + s->pending = NULL; + } + s->backlogCap = 0; + s->backlogLen = 0; + s->listening = false; + + if (s->bindToken) { + socket_bind_remove(s->bindToken); + s->bindToken = 0; + } + s->localPort = 0; + memset(&s->bindSpec, 0, sizeof(s->bindSpec)); + s->connected = false; + s->remoteEP.port = 0; + s->remoteEP.ver = IP_VER4; + memset(s->remoteEP.ip, 0, sizeof(s->remoteEP.ip)); + return SOCK_OK; +} + +void socket_destroy_tcp(socket_impl_t sh) { + tcp_socket_t* s = (tcp_socket_t*)sh; + if (!s) return; + socket_close_tcp(s); + release(s); +} + +const SocketExtraOptions* socket_tcp_extra_options(socket_impl_t sh) { + tcp_socket_t* s = (tcp_socket_t*)sh; + if (!s) return NULL; + return &s->extraOpts; +} + +uint32_t tcp_accept_enqueue(ksocket_t* listener, ip_version_t ipver, const void* src_ip_addr, const void* dst_ip_addr, uint16_t src_port, uint16_t dst_port) { + if (!listener) return 0; + tcp_socket_t* s = (tcp_socket_t*)socket_core_impl(listener); + if (!s) return 0; + if (!s->listening || !s->localPort || s->localPort != dst_port || !s->pending) return 0; + + for (int i = 0; i < s->backlogLen;) { + ksocket_t* owner = s->pending[i]; + tcp_socket_t* client = owner ? (tcp_socket_t*)socket_core_impl(owner) : NULL; + if (client && client->flow.flow_generation) { + bool readable = tcp_flow_readable(&client->flow) != 0; + bool closed = tcp_flow_recv_closed(&client->flow); + if (!closed || readable) { + ++i; + continue; + } + } + + owner = tcp_socket_pop_pending_at(s, i); + if (owner) { + socket_core_close_socket(owner); + socket_core_put(owner); + } + } + if (s->backlogLen >= s->backlogCap) return 0; + + ksocket_t* child_owner = NULL; + uint16_t owner_pid = socket_core_pid(s->ownerSocket); + if (!socket_core_alloc(PROTO_TCP, owner_pid, &child_owner)) return 0; + + tcp_socket_t* child = (tcp_socket_t*)socket_tcp_create(child_owner, &s->extraOpts); + if (!child) { + socket_core_close_socket(child_owner); + return 0; + } + + child->localPort = dst_port; + child->remoteEP.ver = ipver; + memset(child->remoteEP.ip, 0, sizeof(child->remoteEP.ip)); + 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; + child->bindSpec.kind = BIND_IP; + child->bindSpec.ver = ipver; + if (ipver == IP_VER4) memcpy(child->bindSpec.ip, dst_ip_addr, 4); + else if (ipver == IP_VER6) memcpy(child->bindSpec.ip, dst_ip_addr, 16); + + if (!tcp_get_ctx(dst_port, ipver, dst_ip_addr, child->remoteEP.ip, src_port, &child->flow)) { + socket_destroy_tcp(child); + socket_core_close_socket(child_owner); + return 0; + } + + if (!socket_core_attach_impl(child_owner, child, socket_destroy_tcp, socket_close_tcp, socket_setopt_tcp, socket_getopt_tcp)) { + socket_destroy_tcp(child); + socket_core_close_socket(child_owner); + return 0; + } + + child->connected = true; + socket_core_ref(child_owner); + s->pending[s->backlogLen++] = child_owner; + return 1; +} + +uint16_t socket_get_local_port_tcp(socket_impl_t sh) { + tcp_socket_t* s = (tcp_socket_t*)sh; + return s ? s->localPort : 0; +} + +void socket_get_remote_ep_tcp(socket_impl_t sh, net_l4_endpoint* out) { + tcp_socket_t* s = (tcp_socket_t*)sh; + if (!s || !out) return; + *out = s->remoteEP; +} + +bool socket_is_connected_tcp(socket_impl_t sh) { + tcp_socket_t* s = (tcp_socket_t*)sh; + return s ? s->connected : false; +} diff --git a/kernel/networking/transport_layer/csocket_tcp.cpp b/kernel/networking/transport_layer/csocket_tcp.cpp deleted file mode 100644 index 76d077cb..00000000 --- a/kernel/networking/transport_layer/csocket_tcp.cpp +++ /dev/null @@ -1,90 +0,0 @@ -#include "networking/transport_layer/socket_tcp.hpp" -#include "networking/transport_layer/socket.hpp" -#include "csocket_tcp.h" - -extern "C" { - -socket_impl_t socket_tcp_create(ksocket_t* owner, const SocketExtraOptions* extra) { - if (!owner) return nullptr; - return reinterpret_cast(new TCPSocket(owner, extra)); -} - -int32_t socket_bind_tcp(socket_impl_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_impl_t sh, int32_t backlog) { - if (!sh) return SOCK_ERR_INVAL; - return reinterpret_cast(sh)->listen(backlog); -} - -ksocket_t* socket_accept_tcp(socket_impl_t sh) { - if (!sh) return nullptr; - return reinterpret_cast(sh)->accept(); -} - -int32_t socket_connect_tcp(socket_impl_t sh, const net_l4_endpoint* dst) { - if (!sh || !dst) return SOCK_ERR_INVAL; - return reinterpret_cast(sh)->connect(dst); -} - -int64_t socket_send_tcp(socket_impl_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_impl_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_impl_t sh) { - if (!sh) return SOCK_ERR_INVAL; - return reinterpret_cast(sh)->close(); -} - -int32_t socket_setopt_tcp(socket_impl_t sh, int32_t opt, const void* value, uint32_t len) { - if (!sh) return SOCK_ERR_INVAL; - return reinterpret_cast(sh)->set_option(opt, value, len); -} - -int32_t socket_getopt_tcp(socket_impl_t sh, int32_t opt, void* value, uint32_t* len) { - if (!sh) return SOCK_ERR_INVAL; - return reinterpret_cast(sh)->get_option(opt, value, len); -} - -void socket_destroy_tcp(socket_impl_t sh) { - if (!sh) return; - delete reinterpret_cast(sh); -} - -const SocketExtraOptions* socket_tcp_extra_options(socket_impl_t sh) { - if (!sh) return nullptr; - return reinterpret_cast(sh)->get_extra_options(); -} - -uint32_t tcp_accept_enqueue(ksocket_t* listener, ip_version_t ipver, const void* src_ip_addr, const void* dst_ip_addr, uint16_t src_port, uint16_t dst_port) { - if (!listener) return 0; - socket_impl_t sh = socket_core_impl(listener); - if (!sh) return 0; - return reinterpret_cast(sh)->queue_accepted_child(ipver, src_ip_addr, dst_ip_addr, src_port, dst_port); -} - -uint16_t socket_get_local_port_tcp(socket_impl_t sh) { - if (!sh) return 0; - return reinterpret_cast(sh)->get_local_port(); -} - - -void socket_get_remote_ep_tcp(socket_impl_t sh, net_l4_endpoint* out) { - if (!sh || !out) return; - *out = reinterpret_cast(sh)->get_remote_ep(); -} - -bool socket_is_connected_tcp(socket_impl_t sh) { - if (!sh) return false; - return reinterpret_cast(sh)->is_connected(); -} - -} diff --git a/kernel/networking/transport_layer/csocket_udp.c b/kernel/networking/transport_layer/csocket_udp.c new file mode 100644 index 00000000..c825c419 --- /dev/null +++ b/kernel/networking/transport_layer/csocket_udp.c @@ -0,0 +1,768 @@ +#include "csocket_udp.h" +#include "networking/transport_layer/socket_bind.h" +#include "networking/transport_layer/udp.h" +#include "networking/internet_layer/ipv4_route.h" +#include "networking/internet_layer/ipv6_route.h" +#include "networking/internet_layer/ipv4_utils.h" +#include "networking/internet_layer/ipv6_utils.h" +#include "networking/internet_layer/ipv6.h" +#include "networking/transport_layer/trans_utils.h" +#include "networking/network.h" +#include "networking/interface_manager.h" +#include "networking/net_logger/net_logger.h" +#include "std/memory.h" +#include "alloc/allocate.h" + +#define UDP_DEFAULT_RING_CAP 64 +#define UDP_MAX_RING_CAP 1024 + +typedef struct udp_socket { + uint16_t localPort; + net_l4_endpoint remoteEP; + bool connected; + ksocket_t* ownerSocket; + SocketExtraOptions extraOpts; + SockBindSpec bindSpec; + socket_bind_token_t bindToken; + netpkt_t** ring; + net_l4_endpoint* src_eps; + net_l4_endpoint* mcast_groups; + uint8_t mcast_count; + uint32_t ring_cap; + uint32_t r_head; + uint32_t r_tail; + uint32_t rx_bytes; +} udp_socket_t; + +static bool udp_socket_mcast_endpoint_valid(const net_l4_endpoint* ep) { + if (!ep) return false; + if (ep->ver == IP_VER4) { + uint32_t ip = 0; + memcpy(&ip, ep->ip, 4); + return ipv4_is_multicast(ip); + } + if (ep->ver == IP_VER6) return ipv6_is_multicast(ep->ip); + return false; +} + +static bool udp_socket_mcast_match(udp_socket_t* s, ip_version_t ver, const void* dst_ip_addr) { + if (!s || !dst_ip_addr) return false; + if (!s->mcast_groups || !s->mcast_count) return false; + + for (uint32_t i = 0; i < s->mcast_count; ++i) { + const net_l4_endpoint* group = &s->mcast_groups[i]; + if (group->ver != ver) continue; + + if (ver == IP_VER4) { + uint32_t want = 0; + uint32_t got = 0; + memcpy(&want, group->ip, 4); + memcpy(&got, dst_ip_addr, 4); + if (want == got) return true; + } else if (ver == IP_VER6) { + if (memcmp(group->ip, dst_ip_addr, 16) == 0) return true; + } + } + + return false; +} + +static void udp_socket_leave_mcast_groups(udp_socket_t* s) { + if (!s || !s->mcast_groups || !s->mcast_count || !s->localPort) return; + + uint8_t ids[MAX_L3_INTERFACES]; + for (uint32_t gidx = 0; gidx < s->mcast_count; ++gidx) { + const net_l4_endpoint* group = &s->mcast_groups[gidx]; + + if (group->ver == IP_VER4) { + uint32_t g = 0; + memcpy(&g, group->ip, 4); + if (!ipv4_is_multicast(g)) continue; + + uint32_t n = socket_bind_select_l3(&s->bindSpec, IP_VER4, ids, MAX_IPV4_L3_INTERFACES); + for (uint32_t i = 0; i < n; ++i) { + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(ids[i]); + if (!ipv4_l3_is_active(v4)) continue; + l2_ipv4_mcast_leave(v4->l2->ifindex, g); + } + } else if (group->ver == IP_VER6) { + if (!ipv6_is_multicast(group->ip)) continue; + bool linkscope = ipv6_is_linkscope_mcast(group->ip); + + uint32_t n = socket_bind_select_l3(&s->bindSpec, IP_VER6, ids, MAX_IPV6_L3_INTERFACES); + for (uint32_t i = 0; i < n; ++i) { + l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(ids[i]); + if (!ipv6_l3_is_ready(v6)) continue; + if (linkscope && !ipv6_is_linklocal(v6->ip)) continue; + l2_ipv6_mcast_leave(v6->l2->ifindex, group->ip); + } + } + } +} + +static int32_t udp_socket_join_mcast_groups(udp_socket_t* s) { + if (!s || !s->mcast_groups || !s->mcast_count) return SOCK_OK; + if (!s->localPort) return SOCK_OK; + + uint8_t ids[MAX_L3_INTERFACES]; + for (uint32_t gidx = 0; gidx < s->mcast_count; ++gidx) { + const net_l4_endpoint* group = &s->mcast_groups[gidx]; + uint32_t n = 0; + bool joined = false; + + if (group->ver == IP_VER4) { + uint32_t g = 0; + memcpy(&g, group->ip, 4); + if (!ipv4_is_multicast(g)) return SOCK_ERR_INVAL; + n = socket_bind_select_l3(&s->bindSpec, IP_VER4, ids, MAX_IPV4_L3_INTERFACES); + for (uint32_t i = 0; i < n; ++i) { + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(ids[i]); + if (!ipv4_l3_is_active(v4)) continue; + if (l2_ipv4_mcast_join(v4->l2->ifindex, g)) joined = true; + } + } else if (group->ver == IP_VER6) { + if (!ipv6_is_multicast(group->ip)) return SOCK_ERR_INVAL; + bool linkscope = ipv6_is_linkscope_mcast(group->ip); + n = socket_bind_select_l3(&s->bindSpec, IP_VER6, ids, MAX_IPV6_L3_INTERFACES); + for (uint32_t i = 0; i < n; ++i) { + l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(ids[i]); + if (!ipv6_l3_is_ready(v6)) continue; + if (linkscope && !ipv6_is_linklocal(v6->ip)) continue; + if (l2_ipv6_mcast_join(v6->l2->ifindex, group->ip)) joined = true; + } + } else return SOCK_ERR_INVAL; + + if (!joined) return SOCK_ERR_SYS; + } + + return SOCK_OK; +} + +uint32_t socket_udp_input(ksocket_t* socket, ip_version_t ipver, const void* src_ip_addr, const void* dst_ip_addr, netpkt_t* pkt, uint16_t src_port, uint16_t dst_port) { + if (!socket || !pkt) return 0; + udp_socket_t* s = (udp_socket_t*)socket_core_impl(socket); + if (!s) return 0; + if (!s->bindToken || s->localPort != dst_port || !dst_ip_addr || !src_ip_addr) return 0; + + if (ipver == IP_VER4) { + uint32_t dip = 0; + memcpy(&dip, dst_ip_addr, 4); + if (ipv4_is_multicast(dip) && !udp_socket_mcast_match(s, IP_VER4, dst_ip_addr)) return 0; + } else if (ipver == IP_VER6) { + if (ipv6_is_multicast(dst_ip_addr) && !udp_socket_mcast_match(s, IP_VER6, dst_ip_addr)) return 0; + } + + if (s->connected) { + if (s->remoteEP.ver != ipver || s->remoteEP.port != src_port) return 0; + if (ipver == IP_VER4 && memcmp(s->remoteEP.ip, src_ip_addr, 4) != 0) return 0; + if (ipver == IP_VER6 && memcmp(s->remoteEP.ip, src_ip_addr, 16) != 0) return 0; + } + + uint32_t pkt_len = netpkt_len(pkt); + uint32_t limit = UINT32_MAX; + if ((s->extraOpts.flags & SOCK_OPT_BUF_SIZE) && s->extraOpts.buf_size) limit = s->extraOpts.buf_size; + if (pkt_len > limit) return 0; + if (s->rx_bytes > limit - pkt_len) return 0; + + if (!s->ring || !s->src_eps || !s->ring_cap) { + uint32_t usable = UDP_DEFAULT_RING_CAP; + if ((s->extraOpts.flags & SOCK_OPT_BUF_SIZE) && s->extraOpts.buf_size) { + usable = s->extraOpts.buf_size / MAX_PACKET_SIZE; + if (usable < 4) usable = 4; + if (usable > UDP_MAX_RING_CAP) usable = UDP_MAX_RING_CAP; + } + + s->ring_cap = usable + 1; + s->ring = (netpkt_t**)zalloc(sizeof(netpkt_t*) * s->ring_cap); + s->src_eps = (net_l4_endpoint*)zalloc(sizeof(net_l4_endpoint) * s->ring_cap); + if (!s->ring || !s->src_eps) { + if (s->ring) release(s->ring); + if (s->src_eps) release(s->src_eps); + s->ring = NULL; + s->src_eps = NULL; + s->ring_cap = 0; + return 0; + } + } + + uint32_t nexti = (s->r_tail + 1) % s->ring_cap; + if (nexti == s->r_head) return 0; + + netpkt_ref(pkt); + s->ring[s->r_tail] = pkt; + s->rx_bytes += pkt_len; + + s->src_eps[s->r_tail].ver = ipver; + memset(s->src_eps[s->r_tail].ip, 0, 16); + if (ipver == IP_VER4) memcpy(s->src_eps[s->r_tail].ip, src_ip_addr, 4); + else if (ipver == IP_VER6) memcpy(s->src_eps[s->r_tail].ip, src_ip_addr, 16); + s->src_eps[s->r_tail].port = src_port; + s->r_tail = nexti; + return pkt_len; +} + +socket_impl_t udp_socket_create(ksocket_t* owner, const SocketExtraOptions* extra) { + if (!owner) return NULL; + + udp_socket_t* s = (udp_socket_t*)zalloc(sizeof(*s)); + if (!s) return NULL; + s->ownerSocket = owner; + s->remoteEP.ver = IP_VER4; + if (extra) s->extraOpts = *extra; + s->extraOpts.flags &= ~(SOCK_OPT_MCAST_JOIN | SOCK_OPT_MCAST_LEAVE); + s->extraOpts.mcast_count = 0; + s->extraOpts.mcast_groups = NULL; + + if (extra && extra->mcast_count) { + if (!extra->mcast_groups || socket_setopt_udp(s, SOCK_OPT_MCAST_JOIN, extra->mcast_groups, sizeof(net_l4_endpoint) * extra->mcast_count) != SOCK_OK) { + socket_destroy_udp(s); + return NULL; + } + } + + return s; +} + +int32_t socket_bind_udp(socket_impl_t sh, const SockBindSpec* spec_in, uint16_t port) { + udp_socket_t* s = (udp_socket_t*)sh; + if (!s || !spec_in) return SOCK_ERR_INVAL; + + netlog_socket_event_t ev = {0}; + ev.comp = NETLOG_COMP_UDP; + ev.action = NETLOG_ACT_BIND; + ev.pid = socket_core_pid(s->ownerSocket); + ev.u0 = port; + ev.bind_spec = *spec_in; + netlog_socket_event(&s->extraOpts, &ev); + + if (s->localPort) return SOCK_ERR_BOUND; + if (!s->ownerSocket) return SOCK_ERR_SYS; + + SockBindSpec spec = *spec_in; + if (!socket_bind_normalize_spec(&spec)) return SOCK_ERR_INVAL; + + if (spec.kind == BIND_L3) { + if (!spec.l3_id) return SOCK_ERR_INVAL; + uint8_t one[1]; + if (spec.ver == IP_VER4 && !socket_bind_select_l3(&spec, IP_VER4, one, 1)) return SOCK_ERR_INVAL; + if (spec.ver == IP_VER6 && !socket_bind_select_l3(&spec, IP_VER6, one, 1)) return SOCK_ERR_INVAL; + if (!spec.ver && !socket_bind_select_l3(&spec, IP_VER4, one, 1) && !socket_bind_select_l3(&spec, IP_VER6, one, 1)) return SOCK_ERR_INVAL; + } else if (spec.kind == BIND_L2) { + l2_interface_t* l2 = l2_interface_find_by_index(spec.ifindex); + if (!l2 || !l2->is_up) return SOCK_ERR_INVAL; + } else if (spec.kind == BIND_IP) { + if (spec.ver == IP_VER4) { + uint32_t v4ip = 0; + memcpy(&v4ip, spec.ip, 4); + if (!ipv4_is_unspecified(v4ip)) { + uint8_t one[1]; + if (!socket_bind_select_l3(&spec, IP_VER4, one, 1)) return SOCK_ERR_INVAL; + } + } else if (spec.ver == IP_VER6) { + if (!ipv6_is_unspecified(spec.ip)) { + uint8_t one[1]; + if (!socket_bind_select_l3(&spec, IP_VER6, one, 1)) return SOCK_ERR_INVAL; + } + } else return SOCK_ERR_INVAL; + } else if (spec.kind != BIND_ANY && spec.kind != BIND_ANY4 && spec.kind != BIND_ANY6) return SOCK_ERR_INVAL; + + int bind_port = port; + socket_bind_token_t token = 0; + if (bind_port == 0) { + bind_port = socket_bind_alloc_ephemeral(s->ownerSocket, PROTO_UDP, &spec, &token); + if (bind_port < 0) return SOCK_ERR_NO_PORT; + } else if (!socket_bind_insert(s->ownerSocket, PROTO_UDP, &spec, port, &token)) return SOCK_ERR_BOUND; + + s->bindSpec = spec; + s->bindToken = token; + s->localPort = (uint16_t)bind_port; + + int32_t mcast_res = udp_socket_join_mcast_groups(s); + if (mcast_res != SOCK_OK) { + udp_socket_leave_mcast_groups(s); + socket_bind_remove(s->bindToken); + s->localPort = 0; + memset(&s->bindSpec, 0, sizeof(s->bindSpec)); + s->bindToken = 0; + return mcast_res; + } + + return SOCK_OK; +} + +int32_t socket_connect_udp(socket_impl_t sh, const net_l4_endpoint* dst) { + udp_socket_t* s = (udp_socket_t*)sh; + if (!s) return SOCK_ERR_INVAL; + + netlog_socket_event_t ev = {0}; + ev.comp = NETLOG_COMP_UDP; + ev.action = NETLOG_ACT_CONNECT; + ev.pid = socket_core_pid(s->ownerSocket); + if (dst) ev.dst_ep = *dst; + netlog_socket_event(&s->extraOpts, &ev); + + if (!dst || !dst->port) return SOCK_ERR_INVAL; + if (dst->ver != IP_VER4 && dst->ver != IP_VER6) return SOCK_ERR_INVAL; + + s->remoteEP = *dst; + s->connected = true; + return SOCK_OK; +} + +int64_t socket_sendto_udp(socket_impl_t sh, const net_l4_endpoint* dst, const void* buf, uint64_t len) { + udp_socket_t* s = (udp_socket_t*)sh; + if (!s || (!buf && len)) return SOCK_ERR_INVAL; + + netlog_socket_event_t ev = {0}; + ev.comp = NETLOG_COMP_UDP; + ev.action = NETLOG_ACT_SENDTO; + ev.pid = socket_core_pid(s->ownerSocket); + ev.u1 = (uint32_t)len; + if (dst) { + ev.dst_ep = *dst; + ev.u0 = dst->port; + } + netlog_socket_event(&s->extraOpts, &ev); + + bool explicit_dst = dst != NULL; + if (!dst) { + if (!s->connected) return SOCK_ERR_STATE; + dst = &s->remoteEP; + } + if (!dst->port) return SOCK_ERR_INVAL; + if (!buf && len) return SOCK_ERR_INVAL; + if (!s->ownerSocket) return SOCK_ERR_SYS; + + net_l4_endpoint d = *dst; + if (s->connected && explicit_dst) { + if (d.ver != s->remoteEP.ver || d.port != s->remoteEP.port) return SOCK_ERR_STATE; + if (d.ver == IP_VER4 && memcmp(d.ip, s->remoteEP.ip, 4) != 0) return SOCK_ERR_STATE; + if (d.ver == IP_VER6 && memcmp(d.ip, s->remoteEP.ip, 16) != 0) return SOCK_ERR_STATE; + } + + sizedptr pay; + pay.ptr = (uintptr_t)buf; + pay.size = (uint32_t)len; + + if (d.ver == IP_VER4) { + uint32_t dip = 0; + memcpy(&dip, d.ip, 4); + + bool limited_bcast = ipv4_is_limited_broadcast(dip); + uint8_t bcast_ids[MAX_IPV4_L3_INTERFACES]; + uint8_t chosen_l3 = 0; + l3_ipv4_interface_t* bcast_v4 = NULL; + + if (limited_bcast) { + if (!(s->extraOpts.flags & SOCK_OPT_BROADCAST_ALLOWED)) return SOCK_ERR_PERM; + + uint32_t n = socket_bind_select_l3(&s->bindSpec, IP_VER4, bcast_ids, MAX_IPV4_L3_INTERFACES); + uint32_t valid = 0; + for (uint32_t i = 0; i < n; ++i) { + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(bcast_ids[i]); + if (!ipv4_l3_is_active(v4) || v4->is_localhost) continue; + if (!v4->ip && v4->mode != IPV4_CFG_DHCP) continue; + if (!valid) { + chosen_l3 = bcast_ids[i]; + bcast_v4 = v4; + } + valid++; + } + if (valid != 1 || !bcast_v4) return SOCK_ERR_INVAL; + } else { + uint32_t n = socket_bind_select_l3(&s->bindSpec, IP_VER4, bcast_ids, MAX_IPV4_L3_INTERFACES); + for (uint32_t i = 0; i < n; ++i) { + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(bcast_ids[i]); + if (!ipv4_l3_is_ready(v4) || !v4->mask || v4->is_localhost) continue; + if (ipv4_broadcast_calc(v4->ip, v4->mask) != dip) continue; + chosen_l3 = bcast_ids[i]; + bcast_v4 = v4; + break; + } + if (bcast_v4 && !(s->extraOpts.flags & SOCK_OPT_BROADCAST_ALLOWED)) return SOCK_ERR_PERM; + } + + if (bcast_v4) { + if (!s->localPort) { + socket_bind_token_t token = 0; + int p = socket_bind_alloc_ephemeral_l3(s->ownerSocket, PROTO_UDP, chosen_l3, &token); + if (p < 0) return SOCK_ERR_NO_PORT; + s->localPort = (uint16_t)p; + s->bindToken = token; + memset(&s->bindSpec, 0, sizeof(s->bindSpec)); + s->bindSpec.kind = BIND_L3; + s->bindSpec.ver = IP_VER4; + s->bindSpec.l3_id = chosen_l3; + } + + net_l4_endpoint src; + src.ver = IP_VER4; + memset(src.ip, 0, 16); + memcpy(src.ip, &bcast_v4->ip, 4); + src.port = s->localPort; + + ip_tx_opts_t tx; + tx.scope = IP_TX_BOUND_L3; + tx.index = chosen_l3; + + udp_send_segment(&src, &d, pay, &tx, (s->extraOpts.flags & SOCK_OPT_TTL) ? s->extraOpts.ttl : 0, (s->extraOpts.flags & SOCK_OPT_DONTFRAG) ? 1 : 0); + return (int64_t)len; + } + + ipv4_tx_plan_t plan; + if (!socket_bind_build_ipv4_tx_plan(&s->bindSpec, s->localPort, dip, &plan)) return SOCK_ERR_SYS; + + uint8_t tx_l3 = plan.l3_id; + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(tx_l3); + if (!ipv4_l3_is_ready(v4)) return SOCK_ERR_SYS; + + if (!s->localPort) { + socket_bind_token_t token = 0; + int p = socket_bind_alloc_ephemeral_l3(s->ownerSocket, PROTO_UDP, tx_l3, &token); + if (p < 0) return SOCK_ERR_NO_PORT; + s->localPort = (uint16_t)p; + s->bindToken = token; + memset(&s->bindSpec, 0, sizeof(s->bindSpec)); + s->bindSpec.kind = BIND_L3; + s->bindSpec.ver = IP_VER4; + s->bindSpec.l3_id = tx_l3; + } + + net_l4_endpoint src; + src.ver = IP_VER4; + memset(src.ip, 0, 16); + memcpy(src.ip, &v4->ip, 4); + src.port = s->localPort; + + ip_tx_opts_t tx; + tx.scope = IP_TX_BOUND_L3; + tx.index = plan.l3_id; + + udp_send_segment(&src, &d, pay, &tx, (s->extraOpts.flags & SOCK_OPT_TTL) ? s->extraOpts.ttl : 0, (s->extraOpts.flags & SOCK_OPT_DONTFRAG) ? 1 : 0); + return (int64_t)len; + } + + if (d.ver == IP_VER6) { + ipv6_tx_plan_t plan; + if (!socket_bind_build_ipv6_tx_plan(&s->bindSpec, s->localPort, d.ip, &plan)) return SOCK_ERR_SYS; + + uint8_t chosen_l3 = plan.l3_id; + l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(chosen_l3); + if (!ipv6_l3_is_ready(v6)) return SOCK_ERR_SYS; + + if (!s->localPort) { + socket_bind_token_t token = 0; + int p = socket_bind_alloc_ephemeral_l3(s->ownerSocket, PROTO_UDP, chosen_l3, &token); + if (p < 0) return SOCK_ERR_NO_PORT; + s->localPort = (uint16_t)p; + s->bindToken = token; + memset(&s->bindSpec, 0, sizeof(s->bindSpec)); + s->bindSpec.kind = BIND_L3; + s->bindSpec.ver = IP_VER6; + s->bindSpec.l3_id = chosen_l3; + } + + net_l4_endpoint src; + src.ver = IP_VER6; + memset(src.ip, 0, 16); + memcpy(src.ip, v6->ip, 16); + src.port = s->localPort; + + ip_tx_opts_t tx; + tx.scope = IP_TX_BOUND_L3; + tx.index = plan.l3_id; + + udp_send_segment(&src, &d, pay, &tx, (s->extraOpts.flags & SOCK_OPT_TTL) ? s->extraOpts.ttl : 0, (s->extraOpts.flags & SOCK_OPT_DONTFRAG) ? 1 : 0); + return (int64_t)len; + } + + return SOCK_ERR_INVAL; +} + +int64_t socket_recvfrom_udp(socket_impl_t sh, void* buf, uint64_t len, net_l4_endpoint* out_src) { + udp_socket_t* s = (udp_socket_t*)sh; + if (!s || (!buf && len)) return SOCK_ERR_INVAL; + + netlog_socket_event_t ev = {0}; + ev.comp = NETLOG_COMP_UDP; + ev.action = NETLOG_ACT_RECVFROM; + ev.pid = socket_core_pid(s->ownerSocket); + ev.u0 = (uint32_t)len; + ev.local_port = s->localPort; + ev.remote_ep = s->remoteEP; + netlog_socket_event(&s->extraOpts, &ev); + + if (!s->ring || s->r_head == s->r_tail) return SOCK_ERR_WOULDBLOCK; + + netpkt_t* p = s->ring[s->r_head]; + net_l4_endpoint se = s->src_eps[s->r_head]; + s->ring[s->r_head] = NULL; + s->r_head = (s->r_head + 1) % s->ring_cap; + + uint32_t pkt_len = p ? netpkt_len(p) : 0; + s->rx_bytes -= pkt_len; + uint32_t tocpy = pkt_len; + if (tocpy > len) tocpy = (uint32_t)len; + + if (tocpy && !netpkt_copyout(p, 0, buf, tocpy)) tocpy = 0; + if (out_src) *out_src = se; + + if (p) netpkt_unref(p); + return tocpy; +} + +int32_t socket_setopt_udp(socket_impl_t sh, int32_t opt, const void* value, uint32_t len) { + udp_socket_t* s = (udp_socket_t*)sh; + if (!s) return SOCK_ERR_INVAL; + + switch ((uint32_t)opt) { + case SOCK_OPT_KEEPALIVE: + case SOCK_OPT_KEEPALIVE_INTERVAL: + case SOCK_OPT_TCP_NO_DELAY: + case SOCK_OPT_SEND_BUF_SIZE: + return SOCK_ERR_INVAL; + case SOCK_OPT_MCAST_JOIN: { + if (!value || !len || (len % sizeof(net_l4_endpoint)) != 0) return SOCK_ERR_INVAL; + uint32_t count32 = len / sizeof(net_l4_endpoint); + if (!count32 || count32 > 255 || count32 + s->mcast_count > 255) return SOCK_ERR_INVAL; + + const net_l4_endpoint* groups = value; + for (uint32_t i = 0; i < count32; ++i) if (!udp_socket_mcast_endpoint_valid(&groups[i])) return SOCK_ERR_INVAL; + + net_l4_endpoint* next = (net_l4_endpoint*)zalloc(sizeof(net_l4_endpoint) * (s->mcast_count + count32)); + if (!next) return SOCK_ERR_SYS; + if (s->mcast_count) memcpy(next, s->mcast_groups, sizeof(net_l4_endpoint) * s->mcast_count); + + uint8_t next_count = s->mcast_count; + for (uint32_t i = 0; i < count32; ++i) { + bool exists = false; + for (uint8_t j = 0; j < next_count; ++j) { + if (next[j].ver != groups[i].ver || next[j].port != groups[i].port) continue; + uint32_t sz = groups[i].ver == IP_VER4 ? 4 : 16; + if (memcmp(next[j].ip, groups[i].ip, sz) == 0) exists = true; + } + if (!exists) next[next_count++] = groups[i]; + } + + if (next_count == s->mcast_count) { + release(next); + return SOCK_OK; + } + + net_l4_endpoint* old_groups = s->mcast_groups; + uint8_t old_count = s->mcast_count; + if (s->localPort) udp_socket_leave_mcast_groups(s); + + s->mcast_groups = next; + s->mcast_count = next_count; + int32_t rc = udp_socket_join_mcast_groups(s); + if (rc != SOCK_OK) { + udp_socket_leave_mcast_groups(s); + release(next); + s->mcast_groups = old_groups; + s->mcast_count = old_count; + s->extraOpts.mcast_groups = s->mcast_groups; + s->extraOpts.mcast_count = s->mcast_count; + if (s->mcast_count) s->extraOpts.flags |= SOCK_OPT_MCAST_JOIN; + else s->extraOpts.flags &= ~SOCK_OPT_MCAST_JOIN; + if (s->localPort) udp_socket_join_mcast_groups(s); + return rc; + } + + if (old_groups) release(old_groups); + s->extraOpts.mcast_groups = s->mcast_groups; + s->extraOpts.mcast_count = s->mcast_count; + s->extraOpts.flags |= SOCK_OPT_MCAST_JOIN; + return SOCK_OK; + } + case SOCK_OPT_MCAST_LEAVE: { + if (!value || !len || (len % sizeof(net_l4_endpoint)) != 0) return SOCK_ERR_INVAL; + uint32_t count = len / sizeof(net_l4_endpoint); + if (!count || count > 255) return SOCK_ERR_INVAL; + if (!s->mcast_groups || !s->mcast_count) return SOCK_OK; + + const net_l4_endpoint* groups = value; + for (uint32_t i = 0; i < count; ++i) if (!udp_socket_mcast_endpoint_valid(&groups[i])) return SOCK_ERR_INVAL; + + net_l4_endpoint* next = NULL; + if (s->mcast_count > count) { + next = (net_l4_endpoint*)zalloc(sizeof(net_l4_endpoint) * s->mcast_count); + if (!next) return SOCK_ERR_SYS; + } + + uint8_t next_count = 0; + for (uint8_t i = 0; i < s->mcast_count; ++i) { + bool remove_group = false; + for (uint32_t j = 0; j < count; ++j) { + if (s->mcast_groups[i].ver != groups[j].ver || s->mcast_groups[i].port != groups[j].port) continue; + uint32_t sz = groups[j].ver == IP_VER4 ? 4 : 16; + if (memcmp(s->mcast_groups[i].ip, groups[j].ip, sz) == 0) remove_group = true; + } + if (!remove_group) { + if (!next) { + next = (net_l4_endpoint*)zalloc(sizeof(net_l4_endpoint) * s->mcast_count); + if (!next) return SOCK_ERR_SYS; + } + next[next_count++] = s->mcast_groups[i]; + } + } + + if (next_count == s->mcast_count) { + if (next) release(next); + return SOCK_OK; + } + + if (!next_count && next) { + release(next); + next = NULL; + } + + net_l4_endpoint* old_groups = s->mcast_groups; + uint8_t old_count = s->mcast_count; + if (s->localPort) udp_socket_leave_mcast_groups(s); + + s->mcast_groups = next; + s->mcast_count = next_count; + int32_t rc = udp_socket_join_mcast_groups(s); + if (rc != SOCK_OK) { + udp_socket_leave_mcast_groups(s); + if (next) release(next); + s->mcast_groups = old_groups; + s->mcast_count = old_count; + s->extraOpts.mcast_groups = s->mcast_groups; + s->extraOpts.mcast_count = s->mcast_count; + s->extraOpts.flags |= SOCK_OPT_MCAST_JOIN; + if (s->localPort) udp_socket_join_mcast_groups(s); + return rc; + } + + release(old_groups); + s->extraOpts.mcast_count = s->mcast_count; + s->extraOpts.mcast_groups = s->mcast_groups; + if (s->mcast_count) s->extraOpts.flags |= SOCK_OPT_MCAST_JOIN; + else s->extraOpts.flags &= ~SOCK_OPT_MCAST_JOIN; + return SOCK_OK; + } + case SOCK_OPT_RECV_TIMEOUT: + case SOCK_OPT_SEND_TIMEOUT: + case SOCK_OPT_BUF_SIZE: + case SOCK_OPT_DEBUG: + case SOCK_OPT_DONTFRAG: + case SOCK_OPT_BROADCAST_ALLOWED: + case SOCK_OPT_TTL: + return socket_extra_setopt(&s->extraOpts, opt, value, len); + default: + return SOCK_ERR_INVAL; + } +} + +int32_t socket_getopt_udp(socket_impl_t sh, int32_t opt, void* value, uint32_t* len) { + udp_socket_t* s = (udp_socket_t*)sh; + if (!s || !len) return SOCK_ERR_INVAL; + + switch ((uint32_t)opt) { + case SOCK_OPT_KEEPALIVE: + case SOCK_OPT_KEEPALIVE_INTERVAL: + case SOCK_OPT_TCP_NO_DELAY: + case SOCK_OPT_SEND_BUF_SIZE: + return SOCK_ERR_INVAL; + case SOCK_OPT_MCAST_JOIN: { + uint32_t need = s->mcast_count * sizeof(net_l4_endpoint); + if (!value) { + *len = need; + return SOCK_OK; + } + if (*len < need) return SOCK_ERR_INVAL; + if (need) memcpy(value, s->mcast_groups, need); + *len = need; + return SOCK_OK; + } + case SOCK_OPT_RECV_TIMEOUT: + case SOCK_OPT_SEND_TIMEOUT: + case SOCK_OPT_BUF_SIZE: + case SOCK_OPT_DEBUG: + case SOCK_OPT_DONTFRAG: + case SOCK_OPT_BROADCAST_ALLOWED: + case SOCK_OPT_TTL: + return socket_extra_getopt(&s->extraOpts, opt, value, len); + default: + return SOCK_ERR_INVAL; + } +} + +int32_t socket_close_udp(socket_impl_t sh) { + udp_socket_t* s = (udp_socket_t*)sh; + if (!s) return SOCK_ERR_INVAL; + + udp_socket_leave_mcast_groups(s); + + netlog_socket_event_t ev = {0}; + ev.comp = NETLOG_COMP_UDP; + ev.action = NETLOG_ACT_CLOSE; + ev.pid = socket_core_pid(s->ownerSocket); + ev.local_port = s->localPort; + ev.remote_ep = s->remoteEP; + netlog_socket_event(&s->extraOpts, &ev); + + if (s->ring) { + while (s->r_head != s->r_tail) { + if (s->ring[s->r_head]) { + s->rx_bytes -= netpkt_len(s->ring[s->r_head]); + netpkt_unref(s->ring[s->r_head]); + } + s->r_head = (s->r_head + 1) % s->ring_cap; + } + release(s->ring); + s->ring = NULL; + } + if (s->src_eps) { + release(s->src_eps); + s->src_eps = NULL; + } + if (s->mcast_groups) { + release(s->mcast_groups); + s->mcast_groups = NULL; + } + s->mcast_count = 0; + s->extraOpts.mcast_count = 0; + s->extraOpts.mcast_groups = NULL; + s->extraOpts.flags &= ~(SOCK_OPT_MCAST_JOIN | SOCK_OPT_MCAST_LEAVE); + s->ring_cap = 0; + s->r_head = 0; + s->r_tail = 0; + s->rx_bytes = 0; + if (s->bindToken) { + socket_bind_remove(s->bindToken); + s->bindToken = 0; + } + s->localPort = 0; + memset(&s->bindSpec, 0, sizeof(s->bindSpec)); + s->connected = false; + s->remoteEP.port = 0; + s->remoteEP.ver = IP_VER4; + memset(s->remoteEP.ip, 0, sizeof(s->remoteEP.ip)); + return SOCK_OK; +} + +void socket_destroy_udp(socket_impl_t sh) { + udp_socket_t* s = (udp_socket_t*)sh; + if (!s) return; + socket_close_udp(s); + release(s); +} + +uint16_t socket_get_local_port_udp(socket_impl_t sh) { + udp_socket_t* s = (udp_socket_t*)sh; + return s ? s->localPort : 0; +} + +void socket_get_remote_ep_udp(socket_impl_t sh, net_l4_endpoint* out) { + udp_socket_t* s = (udp_socket_t*)sh; + if (!s || !out) return; + *out = s->remoteEP; +} + +bool socket_is_connected_udp(socket_impl_t sh) { + udp_socket_t* s = (udp_socket_t*)sh; + return s ? s->connected : false; +} diff --git a/kernel/networking/transport_layer/csocket_udp.cpp b/kernel/networking/transport_layer/csocket_udp.cpp deleted file mode 100644 index 117cc649..00000000 --- a/kernel/networking/transport_layer/csocket_udp.cpp +++ /dev/null @@ -1,83 +0,0 @@ -#include "networking/transport_layer/socket_udp.hpp" -#include "networking/transport_layer/socket.hpp" -#include "csocket_udp.h" - -extern "C" { - -socket_impl_t udp_socket_create(ksocket_t* owner, const SocketExtraOptions* extra) { - if (!owner) return nullptr; - UDPSocket* s = new UDPSocket(owner, extra); - if (!s) return nullptr; - if (extra && extra->mcast_count) { - if (!extra->mcast_groups || s->set_option(SOCK_OPT_MCAST_JOIN, extra->mcast_groups, sizeof(net_l4_endpoint) * extra->mcast_count) != SOCK_OK) { - delete s; - return nullptr; - } - } - return reinterpret_cast(s); -} - -int32_t socket_bind_udp(socket_impl_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_connect_udp(socket_impl_t sh, const net_l4_endpoint* dst) { - if (!sh || !dst) return SOCK_ERR_INVAL; - return reinterpret_cast(sh)->connect(dst); -} - -int64_t socket_sendto_udp(socket_impl_t sh, const net_l4_endpoint* dst, const void* buf, uint64_t len) { - if (!sh || (!buf && len)) return SOCK_ERR_INVAL; - return reinterpret_cast(sh)->sendto(dst, buf, len); -} - -int64_t socket_recvfrom_udp(socket_impl_t sh, void* buf, uint64_t len, net_l4_endpoint* out_src) { - if (!sh || (!buf && len)) return SOCK_ERR_INVAL; - return reinterpret_cast(sh)->recvfrom(buf, len, out_src); -} - -int32_t socket_close_udp(socket_impl_t sh) { - if (!sh) return SOCK_ERR_INVAL; - return reinterpret_cast(sh)->close(); -} - -int32_t socket_setopt_udp(socket_impl_t sh, int32_t opt, const void* value, uint32_t len) { - if (!sh) return SOCK_ERR_INVAL; - return reinterpret_cast(sh)->set_option(opt, value, len); -} - -int32_t socket_getopt_udp(socket_impl_t sh, int32_t opt, void* value, uint32_t* len) { - if (!sh) return SOCK_ERR_INVAL; - return reinterpret_cast(sh)->get_option(opt, value, len); -} - -void socket_destroy_udp(socket_impl_t sh) { - if (!sh) return; - delete reinterpret_cast(sh); -} - -uint32_t socket_udp_input(ksocket_t* socket, ip_version_t ipver, const void* src_ip_addr, const void* dst_ip_addr, netpkt_t* pkt, uint16_t src_port, uint16_t dst_port) { - if (!socket || !pkt) return 0; - socket_impl_t sh = socket_core_impl(socket); - if (!sh) return 0; - return reinterpret_cast(sh)->enqueue_datagram(ipver, src_ip_addr, dst_ip_addr, pkt, src_port, dst_port); -} - -uint16_t socket_get_local_port_udp(socket_impl_t sh) { - if (!sh) return 0; - return reinterpret_cast(sh)->get_local_port(); -} - - -void socket_get_remote_ep_udp(socket_impl_t sh, net_l4_endpoint* out) { - if (!sh || !out) return; - *out = reinterpret_cast(sh)->get_remote_ep(); -} - -bool socket_is_connected_udp(socket_impl_t sh) { - if (!sh) return false; - return reinterpret_cast(sh)->is_connected(); -} - -} diff --git a/kernel/networking/transport_layer/socket.hpp b/kernel/networking/transport_layer/socket.hpp deleted file mode 100644 index a37c203e..00000000 --- a/kernel/networking/transport_layer/socket.hpp +++ /dev/null @@ -1,151 +0,0 @@ -#pragma once -#include "types.h" -#include "net/network_types.h" -#include "networking/transport_layer/socket_core.h" -#include "networking/transport_layer/socket_bind.h" -#include "tcp.h" -#include "udp.h" -#include "net/socket_types.h" -#include "console/kio.h" -#include "networking/net_logger/net_logger.h" - -#ifdef __cplusplus - -//TODO should these sockets be c instead of cpp? -class Socket { -protected: - uint16_t localPort = 0; - net_l4_endpoint remoteEP = { IP_VER4, {0}, 0 }; - - bool connected = false; - ksocket_t* ownerSocket = nullptr; - - SocketExtraOptions extraOpts = {}; - SockBindSpec bindSpec = {}; - socket_bind_token_t bindToken = 0; - - Socket(ksocket_t* owner, const SocketExtraOptions* extra) : ownerSocket(owner) { - if (extra) extraOpts = *extra; - } - -public: - virtual ~Socket() { close(); } - - ksocket_t* get_owner_socket() const { return ownerSocket; } - const SocketExtraOptions* get_extra_options() const { return &extraOpts; } - - int32_t set_option(int32_t opt, const void* value, uint32_t len) { - uint32_t v = 1; - if (value) { - if (len != sizeof(uint32_t)) return SOCK_ERR_INVAL; - memcpy(&v, value, sizeof(v)); - } else if (len || (opt != SOCK_OPT_DONTFRAG && opt != SOCK_OPT_BROADCAST_ALLOWED)) return SOCK_ERR_INVAL; - - switch (opt) { - case SOCK_OPT_RECV_TIMEOUT: - extraOpts.recv_timeout_ms = v; - if (v) extraOpts.flags |= SOCK_OPT_RECV_TIMEOUT; - else extraOpts.flags &= ~SOCK_OPT_RECV_TIMEOUT; - return SOCK_OK; - case SOCK_OPT_SEND_TIMEOUT: - extraOpts.send_timeout_ms = v; - if (v) extraOpts.flags |= SOCK_OPT_SEND_TIMEOUT; - else extraOpts.flags &= ~SOCK_OPT_SEND_TIMEOUT; - return SOCK_OK; - case SOCK_OPT_BUF_SIZE: - if (!v) return SOCK_ERR_INVAL; - extraOpts.buf_size = v; - extraOpts.flags |= SOCK_OPT_BUF_SIZE; - return SOCK_OK; - case SOCK_OPT_DEBUG: - if (v > SOCK_DBG_ALL) return SOCK_ERR_INVAL; - extraOpts.debug_level = (SockDebugLevel)v; - if (v) extraOpts.flags |= SOCK_OPT_DEBUG; - else extraOpts.flags &= ~SOCK_OPT_DEBUG; - return SOCK_OK; - case SOCK_OPT_DONTFRAG: - if (v) extraOpts.flags |= SOCK_OPT_DONTFRAG; - else extraOpts.flags &= ~SOCK_OPT_DONTFRAG; - return SOCK_OK; - case SOCK_OPT_BROADCAST_ALLOWED: - if (v) extraOpts.flags |= SOCK_OPT_BROADCAST_ALLOWED; - else extraOpts.flags &= ~SOCK_OPT_BROADCAST_ALLOWED; - return SOCK_OK; - case SOCK_OPT_TTL: - if (v > 255) return SOCK_ERR_INVAL; - extraOpts.ttl = (uint8_t)v; - if (v) extraOpts.flags |= SOCK_OPT_TTL; - else extraOpts.flags &= ~SOCK_OPT_TTL; - return SOCK_OK; - default: - return SOCK_ERR_INVAL; - } - } - - int32_t get_option(int32_t opt, void* value, uint32_t* len) const { - if (!len) return SOCK_ERR_INVAL; - - uint32_t v = 0; - switch ((uint32_t)opt) { - case SOCK_OPT_RECV_TIMEOUT: - v = extraOpts.recv_timeout_ms; - break; - case SOCK_OPT_SEND_TIMEOUT: - v = extraOpts.send_timeout_ms; - break; - case SOCK_OPT_BUF_SIZE: - v = extraOpts.buf_size; - break; - case SOCK_OPT_DEBUG: - v = extraOpts.debug_level; - break; - case SOCK_OPT_DONTFRAG: - v = (extraOpts.flags & SOCK_OPT_DONTFRAG) != 0; - break; - case SOCK_OPT_BROADCAST_ALLOWED: - v = (extraOpts.flags & SOCK_OPT_BROADCAST_ALLOWED) != 0; - break; - case SOCK_OPT_TTL: - v = extraOpts.ttl; - break; - default: - return SOCK_ERR_INVAL; - } - - if (!value) { - *len = sizeof(uint32_t); - return SOCK_OK; - } - if (*len < sizeof(uint32_t)) return SOCK_ERR_INVAL; - memcpy(value, &v, sizeof(v)); - *len = sizeof(uint32_t); - return SOCK_OK; - } - - virtual int32_t bind(const SockBindSpec& spec, uint16_t port) = 0; - - virtual int32_t close() { - if (bindToken) { - socket_bind_remove(bindToken); - bindToken = 0; - } - localPort = 0; - memset(&bindSpec, 0, sizeof(bindSpec)); - 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 remoteEP.port; } - 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; } - -}; - -#endif diff --git a/kernel/networking/transport_layer/socket_bind.c b/kernel/networking/transport_layer/socket_bind.c index be10e866..2629d163 100644 --- a/kernel/networking/transport_layer/socket_bind.c +++ b/kernel/networking/transport_layer/socket_bind.c @@ -91,17 +91,8 @@ bool socket_bind_normalize_spec(SockBindSpec* spec) { static bool socket_bind_l3_valid(ip_version_t ver, uint8_t l3_id) { if (!l3_id) return false; - - if (ver == IP_VER4) { - l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(l3_id); - return v4 && v4->l2 && v4->l2->is_up && v4->mode != IPV4_CFG_DISABLED; - } - - if (ver == IP_VER6) { - l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(l3_id); - return v6 && v6->l2 && v6->l2->is_up && v6->cfg != IPV6_CFG_DISABLE && v6->dad_state == IPV6_DAD_OK; - } - + if (ver == IP_VER4) return ipv4_l3_is_active(l3_ipv4_find_by_id(l3_id)); + if (ver == IP_VER6) return ipv6_l3_is_ready(l3_ipv6_find_by_id(l3_id)); return false; } diff --git a/kernel/networking/transport_layer/socket_core.c b/kernel/networking/transport_layer/socket_core.c index ed377b01..61181e69 100644 --- a/kernel/networking/transport_layer/socket_core.c +++ b/kernel/networking/transport_layer/socket_core.c @@ -173,6 +173,96 @@ int32_t socket_core_get_option(ksocket_t* socket, int32_t opt, void* value, uint return socket->getopt(socket->impl, opt, value, len); } +int32_t socket_extra_setopt(SocketExtraOptions* opts, int32_t opt, const void* value, uint32_t len) { + if (!opts) return SOCK_ERR_INVAL; + + uint32_t v = 1; + if (value) { + if (len != sizeof(uint32_t)) return SOCK_ERR_INVAL; + memcpy(&v, value, sizeof(v)); + } else if (len || (opt != SOCK_OPT_DONTFRAG && opt != SOCK_OPT_BROADCAST_ALLOWED)) return SOCK_ERR_INVAL; + + switch ((uint32_t)opt) { + case SOCK_OPT_RECV_TIMEOUT: + opts->recv_timeout_ms = v; + if (v) opts->flags |= SOCK_OPT_RECV_TIMEOUT; + else opts->flags &= ~SOCK_OPT_RECV_TIMEOUT; + return SOCK_OK; + case SOCK_OPT_SEND_TIMEOUT: + opts->send_timeout_ms = v; + if (v) opts->flags |= SOCK_OPT_SEND_TIMEOUT; + else opts->flags &= ~SOCK_OPT_SEND_TIMEOUT; + return SOCK_OK; + case SOCK_OPT_BUF_SIZE: + if (!v) return SOCK_ERR_INVAL; + opts->buf_size = v; + opts->flags |= SOCK_OPT_BUF_SIZE; + return SOCK_OK; + case SOCK_OPT_DEBUG: + if (v > SOCK_DBG_ALL) return SOCK_ERR_INVAL; + opts->debug_level = (SockDebugLevel)v; + if (v) opts->flags |= SOCK_OPT_DEBUG; + else opts->flags &= ~SOCK_OPT_DEBUG; + return SOCK_OK; + case SOCK_OPT_DONTFRAG: + if (v) opts->flags |= SOCK_OPT_DONTFRAG; + else opts->flags &= ~SOCK_OPT_DONTFRAG; + return SOCK_OK; + case SOCK_OPT_BROADCAST_ALLOWED: + if (v) opts->flags |= SOCK_OPT_BROADCAST_ALLOWED; + else opts->flags &= ~SOCK_OPT_BROADCAST_ALLOWED; + return SOCK_OK; + case SOCK_OPT_TTL: + if (v > 255) return SOCK_ERR_INVAL; + opts->ttl = (uint8_t)v; + if (v) opts->flags |= SOCK_OPT_TTL; + else opts->flags &= ~SOCK_OPT_TTL; + return SOCK_OK; + default: + return SOCK_ERR_INVAL; + } +} + +int32_t socket_extra_getopt(const SocketExtraOptions* opts, int32_t opt, void* value, uint32_t* len) { + if (!opts || !len) return SOCK_ERR_INVAL; + + uint32_t v = 0; + switch ((uint32_t)opt) { + case SOCK_OPT_RECV_TIMEOUT: + v = opts->recv_timeout_ms; + break; + case SOCK_OPT_SEND_TIMEOUT: + v = opts->send_timeout_ms; + break; + case SOCK_OPT_BUF_SIZE: + v = opts->buf_size; + break; + case SOCK_OPT_DEBUG: + v = opts->debug_level; + break; + case SOCK_OPT_DONTFRAG: + v = (opts->flags & SOCK_OPT_DONTFRAG) != 0; + break; + case SOCK_OPT_BROADCAST_ALLOWED: + v = (opts->flags & SOCK_OPT_BROADCAST_ALLOWED) != 0; + break; + case SOCK_OPT_TTL: + v = opts->ttl; + break; + default: + return SOCK_ERR_INVAL; + } + + if (!value) { + *len = sizeof(uint32_t); + return SOCK_OK; + } + if (*len < sizeof(uint32_t)) return SOCK_ERR_INVAL; + memcpy(value, &v, sizeof(v)); + *len = sizeof(uint32_t); + return SOCK_OK; +} + socket_impl_t socket_core_impl(ksocket_t* socket) { return socket ? socket->impl : NULL; } diff --git a/kernel/networking/transport_layer/socket_core.h b/kernel/networking/transport_layer/socket_core.h index 258846a0..5565a709 100644 --- a/kernel/networking/transport_layer/socket_core.h +++ b/kernel/networking/transport_layer/socket_core.h @@ -28,6 +28,8 @@ int32_t socket_core_close_handle(socket_handle_t handle, uint16_t pid); int32_t socket_core_close_socket(ksocket_t* socket); int32_t socket_core_set_option(ksocket_t* socket, int32_t opt, const void* value, uint32_t len); int32_t socket_core_get_option(ksocket_t* socket, int32_t opt, void* value, uint32_t* len); +int32_t socket_extra_setopt(SocketExtraOptions* opts, int32_t opt, const void* value, uint32_t len); +int32_t socket_extra_getopt(const SocketExtraOptions* opts, int32_t opt, void* value, uint32_t* len); socket_impl_t socket_core_impl(ksocket_t* socket); protocol_t socket_core_protocol(const ksocket_t* socket); diff --git a/kernel/networking/transport_layer/socket_endpoint.c b/kernel/networking/transport_layer/socket_endpoint.c index d9c60cd8..9cd7d6d1 100644 --- a/kernel/networking/transport_layer/socket_endpoint.c +++ b/kernel/networking/transport_layer/socket_endpoint.c @@ -20,7 +20,7 @@ uint32_t socket_endpoint_resolve(const char* host, uint16_t port, dns_server_sel } if (dns_resolve_a(host, &v4addr, sel, timeout_ms) == DNS_OK) { - make_ep(v4addr, port, IP_VER4, &out[count]); + make_ep(&v4addr, port, IP_VER4, &out[count]); count++; } diff --git a/kernel/networking/transport_layer/socket_tcp.hpp b/kernel/networking/transport_layer/socket_tcp.hpp deleted file mode 100644 index 9419822d..00000000 --- a/kernel/networking/transport_layer/socket_tcp.hpp +++ /dev/null @@ -1,520 +0,0 @@ -#pragma once - -#include "std/memory.h" -#include "std/string.h" -#include "socket.hpp" -#include "networking/transport_layer/tcp.h" -#include "networking/transport_layer/csocket_tcp.h" -#include "networking/internet_layer/ipv4.h" -#include "types.h" -#include "net/socket_types.h" -#include "networking/internet_layer/ipv4_route.h" -#include "networking/internet_layer/ipv4_utils.h" -#include "networking/internet_layer/ipv6_route.h" -#include "networking/internet_layer/ipv6_utils.h" -#include "networking/transport_layer/trans_utils.h" -#include "syscalls/syscalls.h" - -static constexpr int TCP_MAX_BACKLOG = 128; - -class TCPSocket : public Socket { - static constexpr uint32_t TCP_DEFAULT_SOCKET_BUF = 256 * 1024; - - tcp_data flow = {}; - - ksocket_t** pending = nullptr; - int backlogCap = 0; - int backlogLen = 0; - bool listening = false; - - static bool is_valid_v4_l3_for_bind(l3_ipv4_interface_t* v4) { - if (!v4 || !v4->l2) return false; - if (!v4->l2->is_up) return false; - if (v4->mode == IPV4_CFG_DISABLED) return false; - if (v4->ip == 0) return false; - return true; - } - - static bool is_valid_v6_l3_for_bind(l3_ipv6_interface_t* v6) { - if (!v6 || !v6->l2) return false; - if (!v6->l2->is_up) return false; - if (v6->is_localhost) return false; - if (v6->cfg == IPV6_CFG_DISABLE) return false; - if (ipv6_is_unspecified(v6->ip)) return false; - if (v6->dad_state == IPV6_DAD_FAILED) return false; - if (!(v6->kind & IPV6_ADDRK_LINK_LOCAL) && v6->dad_state != IPV6_DAD_OK) return false; - return true; - } - -public: - uint32_t queue_accepted_child(ip_version_t ipver, const void* src_ip_addr, const void* dst_ip_addr, uint16_t src_port, uint16_t dst_port) { - if (!listening || !localPort || localPort != dst_port || !pending) return 0; - - for (int i = 0; i < backlogLen;) { - ksocket_t* owner = pending[i]; - TCPSocket* client = owner ? (TCPSocket*)socket_core_impl(owner) : nullptr; - if (client && client->flow.flow_generation) { - bool readable = tcp_flow_readable(&client->flow) != 0; - bool closed = tcp_flow_recv_closed(&client->flow); - if (!closed || readable) { - ++i; - continue; - } - } - - owner = pop_pending_at(i); - if (owner) { - socket_core_close_socket(owner); - socket_core_put(owner); - } - } - if (backlogLen >= backlogCap) return 0; - - ksocket_t* child_owner = nullptr; - uint16_t owner_pid = socket_core_pid(ownerSocket); - if (!socket_core_alloc(PROTO_TCP, owner_pid, &child_owner)) return 0; - - TCPSocket* child = new TCPSocket(child_owner, &extraOpts); - if (!child) { - socket_core_close_socket(child_owner); - return 0; - } - - child->localPort = dst_port; - - 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; - child->bindSpec.kind = BIND_IP; - child->bindSpec.ver = ipver; - if (ipver == IP_VER4) memcpy(child->bindSpec.ip, dst_ip_addr, 4); - else if (ipver == IP_VER6) memcpy(child->bindSpec.ip, dst_ip_addr, 16); - - if (!tcp_get_ctx(dst_port, ipver, dst_ip_addr, child->remoteEP.ip, src_port, &child->flow)) { - delete child; - socket_core_close_socket(child_owner); - return 0; - } - - if (!socket_core_attach_impl(child_owner, child, socket_destroy_tcp, socket_close_tcp, socket_setopt_tcp, socket_getopt_tcp)) { - delete child; - socket_core_close_socket(child_owner); - return 0; - } - - child->connected = true; - socket_core_ref(child_owner); - pending[backlogLen++] = child_owner; - return 1; - } - -private: - - ksocket_t* pop_pending_at(int idx) { - if (idx < 0 || idx >= backlogLen) return nullptr; - - ksocket_t* client = pending[idx]; - for (int i = idx + 1; i < backlogLen; ++i) pending[i-1] = pending[i]; - - pending[--backlogLen] = nullptr; - return client; - } - -public: - explicit TCPSocket(ksocket_t* owner, const SocketExtraOptions* extra = nullptr) : Socket(owner, extra) { - if (!(extraOpts.flags & SOCK_OPT_BUF_SIZE)) { - extraOpts.flags |= SOCK_OPT_BUF_SIZE; - extraOpts.buf_size = TCP_DEFAULT_SOCKET_BUF; - } - - if (!extraOpts.buf_size) extraOpts.buf_size = TCP_DEFAULT_SOCKET_BUF; - if (extraOpts.buf_size > TCP_DEFAULT_SOCKET_BUF) extraOpts.buf_size = TCP_DEFAULT_SOCKET_BUF; - } - - ~TCPSocket() override { - close(); - } - - int32_t set_option(int32_t opt, const void* value, uint32_t len) { - switch ((uint32_t)opt) { - case SOCK_OPT_KEEPALIVE: { - if (!value || len != sizeof(uint32_t)) return SOCK_ERR_INVAL; - uint32_t v = 0; - memcpy(&v, value, sizeof(v)); - if (v) { - extraOpts.flags |= SOCK_OPT_KEEPALIVE; - if (!extraOpts.keepalive_ms) extraOpts.keepalive_ms = SOCKET_DEFAULT_KEEPALIVE_MS; - } else extraOpts.flags &= ~SOCK_OPT_KEEPALIVE; - if (flow.flow_generation) tcp_flow_apply_socket_options(&flow, &extraOpts); - return SOCK_OK; - } - case SOCK_OPT_KEEPALIVE_INTERVAL: { - if (!value || len != sizeof(uint32_t)) return SOCK_ERR_INVAL; - uint32_t v = 0; - memcpy(&v, value, sizeof(v)); - if (!v) return SOCK_ERR_INVAL; - extraOpts.keepalive_ms = v; - extraOpts.flags |= SOCK_OPT_KEEPALIVE | SOCK_OPT_KEEPALIVE_INTERVAL; - if (flow.flow_generation) tcp_flow_apply_socket_options(&flow, &extraOpts); - return SOCK_OK; - } - case SOCK_OPT_TCP_NO_DELAY: { - if (!value || len != sizeof(uint32_t)) return SOCK_ERR_INVAL; - uint32_t v = 0; - memcpy(&v, value, sizeof(v)); - if (v) extraOpts.flags |= SOCK_OPT_TCP_NO_DELAY; - else extraOpts.flags &= ~SOCK_OPT_TCP_NO_DELAY; - if (flow.flow_generation) tcp_flow_apply_socket_options(&flow, &extraOpts); - return SOCK_OK; - } - case SOCK_OPT_SEND_BUF_SIZE: { - if (!value || len != sizeof(uint32_t)) return SOCK_ERR_INVAL; - uint32_t v = 0; - memcpy(&v, value, sizeof(v)); - if (!v) return SOCK_ERR_INVAL; - extraOpts.send_buf_size = v; - extraOpts.flags |= SOCK_OPT_SEND_BUF_SIZE; - if (flow.flow_generation) tcp_flow_apply_socket_options(&flow, &extraOpts); - return SOCK_OK; - } - case SOCK_OPT_MCAST_JOIN: - return SOCK_ERR_INVAL; - default: { - int32_t ret = Socket::set_option(opt, value, len); - if (ret == SOCK_OK && flow.flow_generation) tcp_flow_apply_socket_options(&flow, &extraOpts); - return ret; - } - } - } - - int32_t get_option(int32_t opt, void* value, uint32_t* len) const { - if (!len) return SOCK_ERR_INVAL; - - uint32_t v = 0; - switch ((uint32_t)opt) { - case SOCK_OPT_KEEPALIVE: - v = (extraOpts.flags & SOCK_OPT_KEEPALIVE) != 0; - break; - case SOCK_OPT_KEEPALIVE_INTERVAL: - v = extraOpts.keepalive_ms; - break; - case SOCK_OPT_TCP_NO_DELAY: - v = (extraOpts.flags & SOCK_OPT_TCP_NO_DELAY) != 0; - break; - case SOCK_OPT_SEND_BUF_SIZE: - v = extraOpts.send_buf_size; - break; - case SOCK_OPT_MCAST_JOIN: - return SOCK_ERR_INVAL; - default: - return Socket::get_option(opt, value, len); - } - - if (!value) { - *len = sizeof(uint32_t); - return SOCK_OK; - } - if (*len < sizeof(uint32_t)) return SOCK_ERR_INVAL; - memcpy(value, &v, sizeof(v)); - *len = sizeof(uint32_t); - return SOCK_OK; - } - - int32_t bind(const SockBindSpec& spec_in, uint16_t port) override { - netlog_socket_event_t ev{}; - ev.comp = NETLOG_COMP_TCP; - ev.action = NETLOG_ACT_BIND; - ev.pid = socket_core_pid(ownerSocket); - ev.u0 = port; - ev.bind_spec = spec_in; - netlog_socket_event(&extraOpts, &ev); - if (localPort) return SOCK_ERR_BOUND; - if (!ownerSocket) return SOCK_ERR_SYS; - - SockBindSpec spec = spec_in; - if (!socket_bind_normalize_spec(&spec)) return SOCK_ERR_INVAL; - - if (spec.kind == BIND_L3){ - if (!spec.l3_id) return SOCK_ERR_INVAL; - l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(spec.l3_id); - l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(spec.l3_id); - bool ok4 = (spec.ver == 0 || spec.ver == IP_VER4) && is_valid_v4_l3_for_bind(v4); - bool ok6 = (spec.ver == 0 || spec.ver == IP_VER6) && is_valid_v6_l3_for_bind(v6); - if (!ok4 && !ok6) return SOCK_ERR_INVAL; - } else if (spec.kind == BIND_L2) { - l2_interface_t* l2 = l2_interface_find_by_index(spec.ifindex); - if (!l2 || !l2->is_up) return SOCK_ERR_INVAL; - } else if (spec.kind == BIND_IP){ - if (spec.ver == IP_VER4){ - uint32_t v4ip = 0; - memcpy(&v4ip, spec.ip, 4); - if (!ipv4_is_unspecified(v4ip) && !is_valid_v4_l3_for_bind(l3_ipv4_find_by_ip(v4ip))) return SOCK_ERR_INVAL; - } else if (spec.ver == IP_VER6) { - if (!ipv6_is_unspecified(spec.ip) && !is_valid_v6_l3_for_bind(l3_ipv6_find_by_ip(spec.ip))) return SOCK_ERR_INVAL; - } else return SOCK_ERR_INVAL; - } else if (spec.kind != BIND_ANY && spec.kind != BIND_ANY4 && spec.kind != BIND_ANY6) return SOCK_ERR_INVAL; - - int bind_port = port; - socket_bind_token_t token = 0; - if (bind_port == 0) { - bind_port = socket_bind_alloc_ephemeral(ownerSocket, PROTO_TCP, &spec, &token); - if (bind_port < 0) return SOCK_ERR_NO_PORT; - } else if (!socket_bind_insert(ownerSocket, PROTO_TCP, &spec, port, &token)) return SOCK_ERR_BOUND; - - bindSpec = spec; - bindToken = token; - localPort = (uint16_t)bind_port; - return SOCK_OK; - } - - int32_t listen(int max_backlog){ - if (!bindToken || connected) return SOCK_ERR_STATE; - - int cap = max_backlog > TCP_MAX_BACKLOG ? TCP_MAX_BACKLOG : max_backlog; - if (cap < 1) cap = 1; - - if (pending) { - for (int i = 0; i < backlogLen; ++i) { - if (!pending[i]) continue; - socket_core_close_socket(pending[i]); - socket_core_put(pending[i]); - } - release(pending); - } - - pending = (ksocket_t**)zalloc(sizeof(ksocket_t*) * cap); - if (!pending) { - backlogCap = 0; - backlogLen = 0; - listening = false; - return SOCK_ERR_SYS; - } - - backlogCap = cap; - backlogLen = 0; - listening = true; - return SOCK_OK; - } - - ksocket_t* accept(){ - if (!listening || !pending) return nullptr; - //TODO replace polling with a socket wait queue when kernel events are present - const int max_empty_iters = 200; - const int ready_wait_iters = 4; - int iter = 0; - - while (backlogLen == 0){ - if (++iter > max_empty_iters) return nullptr; - msleep(5); - } - - iter = 0; - while (1) { - for (int i = 0; i < backlogLen;) { - ksocket_t* owner = pending[i]; - TCPSocket* client = owner ? (TCPSocket*)socket_core_impl(owner) : nullptr; - - if (client && client->flow.flow_generation) { - bool readable = tcp_flow_readable(&client->flow) != 0; - bool closed = tcp_flow_recv_closed(&client->flow); - - if (!closed || readable) { - i++; - continue; - } - } - - owner = pop_pending_at(i); - if (owner) { - socket_core_close_socket(owner); - socket_core_put(owner); - } - } - - if (backlogLen == 0) return nullptr; - for (int i = 0; i < backlogLen; ++i) { - ksocket_t* owner = pending[i]; - TCPSocket* client = owner ? (TCPSocket*)socket_core_impl(owner) : nullptr; - if (!client) continue; - if (!client->flow.flow_generation || tcp_flow_readable(&client->flow) || tcp_flow_recv_closed(&client->flow)) return pop_pending_at(i); - } - - if (++iter > ready_wait_iters) { - if (backlogLen >= backlogCap) { - ksocket_t* owner = pop_pending_at(0); - if (owner) { - socket_core_close_socket(owner); - socket_core_put(owner); - } - } - return nullptr; - } - msleep(5); - } - } - - int32_t connect(const net_l4_endpoint* dst) { - netlog_socket_event_t ev{}; - ev.comp = NETLOG_COMP_TCP; - ev.action = NETLOG_ACT_CONNECT; - ev.pid = socket_core_pid(ownerSocket); - if (dst) ev.dst_ep = *dst; - netlog_socket_event(&extraOpts, &ev); - if (connected || listening) return SOCK_ERR_STATE; - if (!dst || !dst->port) return SOCK_ERR_INVAL; - - net_l4_endpoint d = *dst; - uint8_t chosen_l3 = 0; - - if (d.ver == IP_VER6) { - ipv6_tx_plan_t p6; - if (!socket_bind_build_ipv6_tx_plan(&bindSpec, localPort, d.ip, &p6)) return SOCK_ERR_SYS; - chosen_l3 = p6.l3_id; - } else if (d.ver == IP_VER4) { - uint32_t dip = 0; - memcpy(&dip, d.ip, 4); - ipv4_tx_plan_t p4; - if (!socket_bind_build_ipv4_tx_plan(&bindSpec, localPort, dip, &p4)) return SOCK_ERR_SYS; - chosen_l3 = p4.l3_id; - } else return SOCK_ERR_INVAL; - - if (!chosen_l3) return SOCK_ERR_SYS; - - if (d.ver == IP_VER4) { - l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(chosen_l3); - if (!is_valid_v4_l3_for_bind(v4)) return SOCK_ERR_SYS; - } else if (d.ver == IP_VER6) { - l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(chosen_l3); - if (!is_valid_v6_l3_for_bind(v6)) return SOCK_ERR_SYS; - } else return SOCK_ERR_INVAL; - - bool ephemeral_allocated = false; - if (localPort == 0) { - if (!ownerSocket) return SOCK_ERR_SYS; - socket_bind_token_t token = 0; - int p = socket_bind_alloc_ephemeral_l3(ownerSocket, PROTO_TCP, chosen_l3, &token); - if (p < 0) return SOCK_ERR_NO_PORT; - localPort = (uint16_t)p; - bindToken = token; - memset(&bindSpec, 0, sizeof(bindSpec)); - bindSpec.kind = BIND_L3; - bindSpec.ver = d.ver; - bindSpec.l3_id = chosen_l3; - ephemeral_allocated = true; - } - - flow = tcp_data{}; - if (!tcp_handshake_l3(chosen_l3, localPort, &d, &flow, &extraOpts)) { - if (ephemeral_allocated) Socket::close(); - return SOCK_ERR_SYS; - } - - if (d.ver == IP_VER4) { - l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(chosen_l3); - if (!is_valid_v4_l3_for_bind(v4)) { - if (ephemeral_allocated) Socket::close(); - return SOCK_ERR_SYS; - } - } else { - l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(chosen_l3); - if (!is_valid_v6_l3_for_bind(v6)) { - if (ephemeral_allocated) Socket::close(); - return SOCK_ERR_SYS; - } - } - - remoteEP = d; - connected = true; - netlog_socket_event_t ev1{}; - ev1.comp = NETLOG_COMP_TCP; - ev1.action = NETLOG_ACT_CONNECTED; - ev1.pid = socket_core_pid(ownerSocket); - ev1.u0 = localPort; - ev1.u1 = remoteEP.port; - ev1.local_port = localPort; - ev1.remote_ep = remoteEP; - netlog_socket_event(&extraOpts, &ev1); - return SOCK_OK; - } - - int64_t send(const void* buf, uint64_t len) { - netlog_socket_event_t ev{}; - ev.comp = NETLOG_COMP_TCP; - ev.action = NETLOG_ACT_SEND; - ev.pid = socket_core_pid(ownerSocket); - ev.u0 = (uint32_t)len; - ev.local_port = localPort; - ev.remote_ep = remoteEP; - netlog_socket_event(&extraOpts, &ev); - if (!connected || !flow.flow_generation) return SOCK_ERR_STATE; - if (!buf && len) return SOCK_ERR_INVAL; - if (!len) return 0; - - uint32_t chunk = len > UINT32_MAX ? UINT32_MAX : (uint32_t)len; - flow.payload.ptr = (uintptr_t)buf; - flow.payload.size = chunk; - flow.flags = (1u<l2) return false; - if (!v4->l2->is_up) return false; - if (v4->mode == IPV4_CFG_DISABLED) return false; - return true; - } - - static bool is_valid_v6_l3_for_bind(l3_ipv6_interface_t* v6) { - if (!v6) return false; - if (!v6->l2) return false; - if (!v6->l2->is_up) return false; - if (v6->cfg == IPV6_CFG_DISABLE) return false; - if (v6->dad_state != IPV6_DAD_OK) return false; - return true; - } - - static bool udp_mcast_match(UDPSocket* s, ip_version_t ver, const void* dst_ip_addr) { - if (!s || !dst_ip_addr) return false; - if (!s->mcast_groups || !s->mcast_count) return false; - - for (uint32_t i = 0; i < s->mcast_count; ++i) { - const net_l4_endpoint* group = &s->mcast_groups[i]; - if (group->ver != ver) continue; - - if (ver == IP_VER4) { - uint32_t want = 0; - uint32_t got = 0; - memcpy(&want, group->ip, 4); - memcpy(&got, dst_ip_addr, 4); - if (want == got) return true; - } else if (ver == IP_VER6) { - if (memcmp(group->ip, dst_ip_addr, 16) == 0) return true; - } - } - - return false; - } - - void on_receive(ip_version_t ver, const void* src_ip_addr, uint16_t src_port, netpkt_t* pkt) { - if (!pkt) return; - uint32_t len = netpkt_len(pkt); - uint32_t limit = 0xFFFFFFFFu; - if ((extraOpts.flags & SOCK_OPT_BUF_SIZE) && extraOpts.buf_size) limit = extraOpts.buf_size; - if (len > limit) return; - if (rx_bytes > limit - len) return; - - if (!ring || !src_eps || !ring_cap) { - uint32_t usable = UDP_DEFAULT_RING_CAP; - if ((extraOpts.flags & SOCK_OPT_BUF_SIZE) && extraOpts.buf_size) { - usable = extraOpts.buf_size / MAX_PACKET_SIZE; - if (usable < 4) usable = 4; - if (usable > UDP_MAX_RING_CAP) usable = UDP_MAX_RING_CAP; - } - - ring_cap = usable + 1; - ring = (netpkt_t**)zalloc(sizeof(netpkt_t*) * ring_cap); - src_eps = (net_l4_endpoint*)zalloc(sizeof(net_l4_endpoint) * ring_cap); - if (!ring || !src_eps) { - if (ring) release(ring); - if (src_eps) release(src_eps); - ring = nullptr; - src_eps = nullptr; - ring_cap = 0; - return; - } - } - - uint32_t nexti = (r_tail + 1) % ring_cap; - if (nexti == r_head) return; - - netpkt_ref(pkt); - ring[r_tail] = pkt; - rx_bytes += len; - - src_eps[r_tail].ver = ver; - memset(src_eps[r_tail].ip, 0, 16); - - if (ver == IP_VER4) memcpy(src_eps[r_tail].ip, src_ip_addr, 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; - } - - void leave_mcast_groups() { - if (!mcast_groups || !mcast_count || !localPort) return; - - uint8_t ids[MAX_L3_INTERFACES]; - for (uint32_t gidx = 0; gidx < mcast_count; ++gidx) { - const net_l4_endpoint* group = &mcast_groups[gidx]; - - if (group->ver == IP_VER4) { - uint32_t g = 0; - memcpy(&g, group->ip, 4); - if (!ipv4_is_multicast(g)) continue; - - uint32_t n = socket_bind_select_l3(&bindSpec, IP_VER4, ids, MAX_IPV4_L3_INTERFACES); - for (uint32_t i = 0; i < n; ++i) { - l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(ids[i]); - if (!is_valid_v4_l3_for_bind(v4)) continue; - l2_ipv4_mcast_leave(v4->l2->ifindex,g); - } - } else if (group->ver == IP_VER6) { - if (!ipv6_is_multicast(group->ip)) continue; - bool linkscope = ipv6_is_linkscope_mcast(group->ip); - - uint32_t n = socket_bind_select_l3(&bindSpec, IP_VER6, ids, MAX_IPV6_L3_INTERFACES); - for (uint32_t i = 0; i < n; ++i) { - l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(ids[i]); - if (!is_valid_v6_l3_for_bind(v6)) continue; - if (linkscope && !ipv6_is_linklocal(v6->ip)) continue; - l2_ipv6_mcast_leave(v6->l2->ifindex,group->ip); - } - } - } - } - - int32_t join_mcast_groups() { - if (!mcast_groups || !mcast_count) return SOCK_OK; - if (!localPort) return SOCK_OK; - - uint8_t ids[MAX_L3_INTERFACES]; - for (uint32_t gidx = 0; gidx < mcast_count; ++gidx) { - const net_l4_endpoint* group = &mcast_groups[gidx]; - uint32_t n = 0; - bool joined = false; - - if (group->ver == IP_VER4) { - uint32_t g = 0; - memcpy(&g, group->ip, 4); - if (!ipv4_is_multicast(g)) return SOCK_ERR_INVAL; - n = socket_bind_select_l3(&bindSpec, IP_VER4, ids, MAX_IPV4_L3_INTERFACES); - for (uint32_t i = 0; i < n; ++i) { - l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(ids[i]); - if (!is_valid_v4_l3_for_bind(v4)) continue; - if (l2_ipv4_mcast_join(v4->l2->ifindex, g)) joined = true; - } - } else if (group->ver == IP_VER6) { - if (!ipv6_is_multicast(group->ip)) return SOCK_ERR_INVAL; - bool linkscope = ipv6_is_linkscope_mcast(group->ip); - n = socket_bind_select_l3(&bindSpec, IP_VER6, ids, MAX_IPV6_L3_INTERFACES); - for (uint32_t i = 0; i < n; ++i) { - l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(ids[i]); - if (!is_valid_v6_l3_for_bind(v6)) continue; - if (linkscope && !ipv6_is_linklocal(v6->ip)) continue; - if (l2_ipv6_mcast_join(v6->l2->ifindex, group->ip)) joined = true; - } - } else return SOCK_ERR_INVAL; - - if (!joined) return SOCK_ERR_SYS; - } - - return SOCK_OK; - } - -public: - UDPSocket(ksocket_t* owner, const SocketExtraOptions* extra = nullptr) : Socket(owner, extra) { - extraOpts.flags &= ~(SOCK_OPT_MCAST_JOIN | SOCK_OPT_MCAST_LEAVE); - extraOpts.mcast_count = 0; - extraOpts.mcast_groups = nullptr; - } - - ~UDPSocket() override { - close(); - } - - int32_t bind(const SockBindSpec& spec_in, uint16_t port) override { - netlog_socket_event_t ev{}; - ev.comp = NETLOG_COMP_UDP; - ev.action = NETLOG_ACT_BIND; - ev.pid = socket_core_pid(ownerSocket); - ev.u0 = port; - ev.bind_spec = spec_in; - netlog_socket_event(&extraOpts, &ev); - if (localPort) return SOCK_ERR_BOUND; - if (!ownerSocket) return SOCK_ERR_SYS; - - SockBindSpec spec = spec_in; - if (!socket_bind_normalize_spec(&spec)) return SOCK_ERR_INVAL; - - if (spec.kind == BIND_L3) { - if (!spec.l3_id) return SOCK_ERR_INVAL; - uint8_t one[1]; - if (spec.ver == IP_VER4 && !socket_bind_select_l3(&spec, IP_VER4, one, 1)) return SOCK_ERR_INVAL; - if (spec.ver == IP_VER6 && !socket_bind_select_l3(&spec, IP_VER6, one, 1)) return SOCK_ERR_INVAL; - if (!spec.ver && !socket_bind_select_l3(&spec, IP_VER4, one, 1) && !socket_bind_select_l3(&spec, IP_VER6, one, 1)) return SOCK_ERR_INVAL; - } else if (spec.kind == BIND_L2) { - l2_interface_t* l2 = l2_interface_find_by_index(spec.ifindex); - if (!l2 || !l2->is_up) return SOCK_ERR_INVAL; - } else if (spec.kind == BIND_IP) { - if (spec.ver == IP_VER4) { - uint32_t v4ip = 0; - memcpy(&v4ip, spec.ip, 4); - if (!ipv4_is_unspecified(v4ip)) { - uint8_t one[1]; - if (!socket_bind_select_l3(&spec, IP_VER4, one, 1)) return SOCK_ERR_INVAL; - } - } else if (spec.ver == IP_VER6) { - if (!ipv6_is_unspecified(spec.ip)) { - uint8_t one[1]; - if (!socket_bind_select_l3(&spec, IP_VER6, one, 1)) return SOCK_ERR_INVAL; - } - } else return SOCK_ERR_INVAL; - } else if (spec.kind != BIND_ANY && spec.kind != BIND_ANY4 && spec.kind != BIND_ANY6) return SOCK_ERR_INVAL; - - int bind_port = port; - socket_bind_token_t token = 0; - if (bind_port == 0) { - bind_port = socket_bind_alloc_ephemeral(ownerSocket, PROTO_UDP, &spec, &token); - if (bind_port < 0) return SOCK_ERR_NO_PORT; - } else if (!socket_bind_insert(ownerSocket, PROTO_UDP, &spec, port, &token)) return SOCK_ERR_BOUND; - - bindSpec = spec; - bindToken = token; - localPort = (uint16_t)bind_port; - - int32_t mcast_res = join_mcast_groups(); - if (mcast_res != SOCK_OK) { - leave_mcast_groups(); - socket_bind_remove(bindToken); - localPort = 0; - memset(&bindSpec, 0, sizeof(bindSpec)); - bindToken = 0; - return mcast_res; - } - - return SOCK_OK; - } - - int32_t connect(const net_l4_endpoint* dst) { - netlog_socket_event_t ev{}; - ev.comp = NETLOG_COMP_UDP; - ev.action = NETLOG_ACT_CONNECT; - ev.pid = socket_core_pid(ownerSocket); - if (dst) ev.dst_ep = *dst; - netlog_socket_event(&extraOpts, &ev); - - if (!dst || !dst->port) return SOCK_ERR_INVAL; - if (dst->ver != IP_VER4 && dst->ver != IP_VER6) return SOCK_ERR_INVAL; - - remoteEP = *dst; - connected = true; - return SOCK_OK; - } - - int64_t sendto(const net_l4_endpoint* dst, const void* buf, uint64_t len) { - netlog_socket_event_t ev{}; - ev.comp = NETLOG_COMP_UDP; - ev.action = NETLOG_ACT_SENDTO; - ev.pid = socket_core_pid(ownerSocket); - ev.u1 = (uint32_t)len; - if (dst) { - ev.dst_ep = *dst; - ev.u0 = dst->port; - } - netlog_socket_event(&extraOpts, &ev); - bool explicit_dst = dst != nullptr; - if (!dst) { - if (!connected) return SOCK_ERR_STATE; - dst = &remoteEP; - } - if (!dst->port) return SOCK_ERR_INVAL; - if (!buf && len) return SOCK_ERR_INVAL; - if (!ownerSocket) return SOCK_ERR_SYS; - - net_l4_endpoint d = *dst; - if (connected && explicit_dst) { - if (d.ver != remoteEP.ver || d.port != remoteEP.port) return SOCK_ERR_STATE; - if (d.ver == IP_VER4 && memcmp(d.ip, remoteEP.ip, 4) != 0) return SOCK_ERR_STATE; - if (d.ver == IP_VER6 && memcmp(d.ip, remoteEP.ip, 16) != 0) return SOCK_ERR_STATE; - } - - sizedptr pay; - pay.ptr = (uintptr_t)buf; - pay.size = (uint32_t)len; - - if (d.ver == IP_VER4) { - uint32_t dip = 0; - memcpy(&dip, d.ip, 4); - - bool limited_bcast = ipv4_is_limited_broadcast(dip); - uint8_t bcast_ids[MAX_IPV4_L3_INTERFACES]; - uint8_t chosen_l3 = 0; - l3_ipv4_interface_t* bcast_v4 = nullptr; - - if (limited_bcast) { - if (!(extraOpts.flags & SOCK_OPT_BROADCAST_ALLOWED)) return SOCK_ERR_PERM; - - uint32_t n = socket_bind_select_l3(&bindSpec, IP_VER4, bcast_ids, MAX_IPV4_L3_INTERFACES); - uint32_t valid = 0; - for (uint32_t i = 0; i < n; ++i) { - l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(bcast_ids[i]); - if (!is_valid_v4_l3_for_bind(v4) || v4->is_localhost) continue; - - if (!v4->ip && v4->mode != IPV4_CFG_DHCP) continue; - if (!valid) { - chosen_l3 = bcast_ids[i]; - bcast_v4 = v4; - } - valid++; - } - if (valid != 1 || !bcast_v4) return SOCK_ERR_INVAL; - } else { - uint32_t n = socket_bind_select_l3(&bindSpec, IP_VER4, bcast_ids, MAX_IPV4_L3_INTERFACES); - for (uint32_t i = 0; i < n; ++i) { - l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(bcast_ids[i]); - if (!is_valid_v4_l3_for_bind(v4) || !v4->ip || !v4->mask || v4->is_localhost) continue; - if (ipv4_broadcast_calc(v4->ip, v4->mask) != dip) continue; - chosen_l3 = bcast_ids[i]; - bcast_v4 = v4; - break; - } - if (bcast_v4 && !(extraOpts.flags & SOCK_OPT_BROADCAST_ALLOWED)) return SOCK_ERR_PERM; - } - - if (bcast_v4) { - if (!localPort) { - socket_bind_token_t token = 0; - int p = socket_bind_alloc_ephemeral_l3(ownerSocket, PROTO_UDP, chosen_l3, &token); - if (p < 0) return SOCK_ERR_NO_PORT; - localPort = (uint16_t)p; - bindToken = token; - memset(&bindSpec, 0, sizeof(bindSpec)); - bindSpec.kind = BIND_L3; - bindSpec.ver = IP_VER4; - bindSpec.l3_id = chosen_l3; - } - - net_l4_endpoint src; - src.ver = IP_VER4; - memset(src.ip, 0, 16); - memcpy(src.ip, &bcast_v4->ip, 4); - src.port = localPort; - - ip_tx_opts_t tx; - tx.scope = IP_TX_BOUND_L3; - tx.index = chosen_l3; - - udp_send_segment(&src, &d, pay, &tx, (extraOpts.flags & SOCK_OPT_TTL) ? extraOpts.ttl : 0, (extraOpts.flags & SOCK_OPT_DONTFRAG) ? 1 : 0); - return (int64_t)len; - } - - ipv4_tx_plan_t plan; - if (!socket_bind_build_ipv4_tx_plan(&bindSpec, localPort, dip, &plan)) return SOCK_ERR_SYS; - - uint8_t tx_l3 = plan.l3_id; - l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(tx_l3); - if (!is_valid_v4_l3_for_bind(v4)) return SOCK_ERR_SYS; - - if (!localPort) { - socket_bind_token_t token = 0; - int p = socket_bind_alloc_ephemeral_l3(ownerSocket, PROTO_UDP, tx_l3, &token); - if (p < 0) return SOCK_ERR_NO_PORT; - localPort = (uint16_t)p; - bindToken = token; - memset(&bindSpec, 0, sizeof(bindSpec)); - bindSpec.kind = BIND_L3; - bindSpec.ver = IP_VER4; - bindSpec.l3_id = tx_l3; - } - - net_l4_endpoint src; - src.ver = IP_VER4; - memset(src.ip, 0, 16); - memcpy(src.ip, &v4->ip, 4); - src.port = localPort; - - ip_tx_opts_t tx; - tx.scope = IP_TX_BOUND_L3; - tx.index = plan.l3_id; - - udp_send_segment(&src, &d, pay, &tx, (extraOpts.flags & SOCK_OPT_TTL) ? extraOpts.ttl : 0, (extraOpts.flags & SOCK_OPT_DONTFRAG) ? 1 : 0); - return (int64_t)len; - } - - if (d.ver == IP_VER6) { - ipv6_tx_plan_t plan; - if (!socket_bind_build_ipv6_tx_plan(&bindSpec, localPort, d.ip, &plan)) return SOCK_ERR_SYS; - - uint8_t chosen_l3 = plan.l3_id; - - l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(chosen_l3); - if (!is_valid_v6_l3_for_bind(v6)) return SOCK_ERR_SYS; - - if (!localPort) { - socket_bind_token_t token = 0; - int p = socket_bind_alloc_ephemeral_l3(ownerSocket, PROTO_UDP, chosen_l3, &token); - if (p < 0) return SOCK_ERR_NO_PORT; - localPort = (uint16_t)p; - bindToken = token; - memset(&bindSpec, 0, sizeof(bindSpec)); - bindSpec.kind = BIND_L3; - bindSpec.ver = IP_VER6; - bindSpec.l3_id = chosen_l3; - } - - net_l4_endpoint src; - src.ver = IP_VER6; - memset(src.ip, 0, 16); - memcpy(src.ip, v6->ip, 16); - src.port = localPort; - - ip_tx_opts_t tx; - tx.scope = IP_TX_BOUND_L3; - tx.index = plan.l3_id; - - udp_send_segment(&src, &d, pay, &tx, (extraOpts.flags & SOCK_OPT_TTL) ? extraOpts.ttl : 0, (extraOpts.flags & SOCK_OPT_DONTFRAG) ? 1 : 0); - return (int64_t)len; - } - - return SOCK_ERR_INVAL; - } - - uint32_t enqueue_datagram(ip_version_t ipver, const void* src_ip_addr, const void* dst_ip_addr, netpkt_t* pkt, uint16_t src_port, uint16_t dst_port) { - if (!bindToken || localPort != dst_port || !pkt || !dst_ip_addr || !src_ip_addr) return 0; - if (ipver == IP_VER4) { - uint32_t dip = 0; - memcpy(&dip, dst_ip_addr, 4); - if (ipv4_is_multicast(dip) && !udp_mcast_match(this, IP_VER4, dst_ip_addr)) return 0; - } else if (ipver == IP_VER6) { - if (ipv6_is_multicast((const uint8_t*)dst_ip_addr) && !udp_mcast_match(this, IP_VER6, dst_ip_addr)) return 0; - } - if (connected) { - if (remoteEP.ver != ipver || remoteEP.port != src_port) return 0; - if (ipver == IP_VER4 && memcmp(remoteEP.ip, src_ip_addr, 4) != 0) return 0; - if (ipver == IP_VER6 && memcmp(remoteEP.ip, src_ip_addr, 16) != 0) return 0; - } - - on_receive(ipver, src_ip_addr, src_port, pkt); - return netpkt_len(pkt); - } - - int64_t recvfrom(void* buf, uint64_t len, net_l4_endpoint* src) { - netlog_socket_event_t ev{}; - ev.comp = NETLOG_COMP_UDP; - ev.action = NETLOG_ACT_RECVFROM; - ev.pid = socket_core_pid(ownerSocket); - ev.u0 = (uint32_t)len; - ev.local_port = localPort; - ev.remote_ep = remoteEP; - netlog_socket_event(&extraOpts, &ev); - if (!ring || r_head == r_tail) return SOCK_ERR_WOULDBLOCK; - - netpkt_t* p = ring[r_head]; - net_l4_endpoint se = src_eps[r_head]; - ring[r_head] = nullptr; - r_head = (r_head + 1) % ring_cap; - - uint32_t pkt_len = p ? netpkt_len(p) : 0; - rx_bytes -= pkt_len; - uint32_t tocpy = pkt_len; - if (tocpy > len) tocpy = (uint32_t)len; - - if (tocpy && !netpkt_copyout(p, 0, buf, tocpy)) tocpy = 0; - if (src) *src = se; - - if (p) netpkt_unref(p); - return tocpy; - } - - - int32_t set_option(int32_t opt, const void* value, uint32_t len) { - switch ((uint32_t)opt) { - case SOCK_OPT_KEEPALIVE: - case SOCK_OPT_KEEPALIVE_INTERVAL: - case SOCK_OPT_TCP_NO_DELAY: - case SOCK_OPT_SEND_BUF_SIZE: - return SOCK_ERR_INVAL; - case SOCK_OPT_MCAST_JOIN: { - if (!value || !len || (len % sizeof(net_l4_endpoint)) != 0) return SOCK_ERR_INVAL; - uint32_t count32 = len / sizeof(net_l4_endpoint); - if (!count32 || count32 > 255 || count32 + mcast_count > 255) return SOCK_ERR_INVAL; - - const net_l4_endpoint* groups = (const net_l4_endpoint*)value; - for (uint32_t i = 0; i < count32; ++i) { - if (groups[i].ver != IP_VER4 && groups[i].ver != IP_VER6) return SOCK_ERR_INVAL; - if (groups[i].ver == IP_VER4) { - uint32_t ip = 0; - memcpy(&ip, groups[i].ip, 4); - if (!ipv4_is_multicast(ip)) return SOCK_ERR_INVAL; - } else if (!ipv6_is_multicast(groups[i].ip)) return SOCK_ERR_INVAL; - } - - net_l4_endpoint* next = (net_l4_endpoint*)zalloc(sizeof(net_l4_endpoint) * (mcast_count + count32)); - if (!next) return SOCK_ERR_SYS; - if (mcast_count) memcpy(next, mcast_groups, sizeof(net_l4_endpoint) * mcast_count); - - uint8_t next_count = mcast_count; - for (uint32_t i = 0; i < count32; ++i) { - bool exists = false; - for (uint8_t j = 0; j < next_count; ++j) { - if (next[j].ver != groups[i].ver || next[j].port != groups[i].port) continue; - uint32_t sz = groups[i].ver == IP_VER4 ? 4 : 16; - if (memcmp(next[j].ip, groups[i].ip, sz) == 0) exists = true; - } - if (!exists) next[next_count++] = groups[i]; - } - - if (next_count == mcast_count) { - release(next); - return SOCK_OK; - } - - net_l4_endpoint* old_groups = mcast_groups; - uint8_t old_count = mcast_count; - if (localPort) leave_mcast_groups(); - - mcast_groups = next; - mcast_count = next_count; - int32_t rc = join_mcast_groups(); - if (rc != SOCK_OK) { - leave_mcast_groups(); - release(next); - mcast_groups = old_groups; - mcast_count = old_count; - extraOpts.mcast_groups = mcast_groups; - extraOpts.mcast_count = mcast_count; - if (mcast_count) extraOpts.flags |= SOCK_OPT_MCAST_JOIN; - else extraOpts.flags &= ~SOCK_OPT_MCAST_JOIN; - if (localPort) join_mcast_groups(); - return rc; - } - - if (old_groups) release(old_groups); - extraOpts.mcast_groups = mcast_groups; - extraOpts.mcast_count = mcast_count; - extraOpts.flags |= SOCK_OPT_MCAST_JOIN; - return SOCK_OK; - } - case SOCK_OPT_MCAST_LEAVE: { - if (!value || !len || (len % sizeof(net_l4_endpoint)) != 0) return SOCK_ERR_INVAL; - uint32_t count = len / sizeof(net_l4_endpoint); - if (!count || count > 255) return SOCK_ERR_INVAL; - if (!mcast_groups || !mcast_count) return SOCK_OK; - - const net_l4_endpoint* groups = (const net_l4_endpoint*)value; - for (uint32_t i = 0; i < count; ++i) { - if (groups[i].ver != IP_VER4 && groups[i].ver != IP_VER6) return SOCK_ERR_INVAL; - if (groups[i].ver == IP_VER4) { - uint32_t ip = 0; - memcpy(&ip, groups[i].ip, 4); - if (!ipv4_is_multicast(ip)) return SOCK_ERR_INVAL; - } else if (!ipv6_is_multicast(groups[i].ip)) return SOCK_ERR_INVAL; - } - - net_l4_endpoint* next = nullptr; - if (mcast_count > count) { - next = (net_l4_endpoint*)zalloc(sizeof(net_l4_endpoint) * mcast_count); - if (!next) return SOCK_ERR_SYS; - } - - uint8_t next_count = 0; - for (uint8_t i = 0; i < mcast_count; ++i) { - bool remove_group = false; - for (uint32_t j = 0; j < count; ++j) { - if (mcast_groups[i].ver != groups[j].ver || mcast_groups[i].port != groups[j].port) continue; - uint32_t sz = groups[j].ver == IP_VER4 ? 4 : 16; - if (memcmp(mcast_groups[i].ip, groups[j].ip, sz) == 0) remove_group = true; - } - if (!remove_group) { - if (!next) { - next = (net_l4_endpoint*)zalloc(sizeof(net_l4_endpoint) * mcast_count); - if (!next) return SOCK_ERR_SYS; - } - next[next_count++] = mcast_groups[i]; - } - } - - if (next_count == mcast_count) { - if (next) release(next); - return SOCK_OK; - } - - if (!next_count && next) { - release(next); - next = nullptr; - } - - net_l4_endpoint* old_groups = mcast_groups; - uint8_t old_count = mcast_count; - if (localPort) leave_mcast_groups(); - - mcast_groups = next; - mcast_count = next_count; - int32_t rc = join_mcast_groups(); - if (rc != SOCK_OK) { - leave_mcast_groups(); - if (next) release(next); - mcast_groups = old_groups; - mcast_count = old_count; - extraOpts.mcast_groups = mcast_groups; - extraOpts.mcast_count = mcast_count; - extraOpts.flags |= SOCK_OPT_MCAST_JOIN; - if (localPort) join_mcast_groups(); - return rc; - } - - release(old_groups); - extraOpts.mcast_count = mcast_count; - extraOpts.mcast_groups = mcast_groups; - if (mcast_count) extraOpts.flags |= SOCK_OPT_MCAST_JOIN; - else extraOpts.flags &= ~SOCK_OPT_MCAST_JOIN; - return SOCK_OK; - } - default: - return Socket::set_option(opt, value, len); - } - } - - int32_t get_option(int32_t opt, void* value, uint32_t* len) const { - switch ((uint32_t)opt) { - case SOCK_OPT_KEEPALIVE: - case SOCK_OPT_KEEPALIVE_INTERVAL: - case SOCK_OPT_TCP_NO_DELAY: - case SOCK_OPT_SEND_BUF_SIZE: - return SOCK_ERR_INVAL; - case SOCK_OPT_MCAST_JOIN: { - if (!len) return SOCK_ERR_INVAL; - uint32_t need = mcast_count * sizeof(net_l4_endpoint); - if (!value) { - *len = need; - return SOCK_OK; - } - if (*len < need) return SOCK_ERR_INVAL; - if (need) memcpy(value, mcast_groups, need); - *len = need; - return SOCK_OK; - } - default: - return Socket::get_option(opt, value, len); - } - } - - int32_t close() override { - leave_mcast_groups(); - netlog_socket_event_t ev{}; - ev.comp = NETLOG_COMP_UDP; - ev.action = NETLOG_ACT_CLOSE; - ev.pid = socket_core_pid(ownerSocket); - ev.local_port = localPort; - ev.remote_ep = remoteEP; - netlog_socket_event(&extraOpts, &ev); - if (ring) { - while (r_head != r_tail) { - if (ring[r_head]) { - rx_bytes -= netpkt_len(ring[r_head]); - netpkt_unref(ring[r_head]); - } - r_head = (r_head + 1) % ring_cap; - } - release(ring); - ring = nullptr; - } - if (src_eps) { - release(src_eps); - src_eps = nullptr; - } - if (mcast_groups) { - release(mcast_groups); - mcast_groups = nullptr; - } - mcast_count = 0; - extraOpts.mcast_count = 0; - extraOpts.mcast_groups = nullptr; - extraOpts.flags &= ~(SOCK_OPT_MCAST_JOIN | SOCK_OPT_MCAST_LEAVE); - ring_cap = 0; - r_head = 0; - r_tail = 0; - rx_bytes = 0; - return Socket::close(); - } - - net_l4_endpoint get_remote_ep() const { - return remoteEP; - } -}; \ No newline at end of file diff --git a/kernel/networking/transport_layer/tcp/tcp_core.c b/kernel/networking/transport_layer/tcp/tcp_core.c index a9212976..fb86e154 100644 --- a/kernel/networking/transport_layer/tcp/tcp_core.c +++ b/kernel/networking/transport_layer/tcp/tcp_core.c @@ -6,6 +6,7 @@ #include "networking/internet_layer/ipv6_utils.h" #include "std/memory.h" #include "math/rng.h" +#include "random/random.h" #include "syscalls/syscalls.h" #include "networking/transport_layer/trans_utils.h" #include "exceptions/irq.h" @@ -449,7 +450,7 @@ bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, return false; } - make_ep(v4->ip, local_port, IP_VER4, &flow->base.local); + make_ep(&v4->ip, local_port, IP_VER4, &flow->base.local); } else{ l3_ipv6_interface_t *v6 = l3_ipv6_find_by_id(l3_id); @@ -469,9 +470,7 @@ bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, flow->base.retries = TCP_SYN_RETRIES; rng_t rng; - uint64_t virt_timer; - asm volatile ("mrs %0, cntvct_el0" : "=r"(virt_timer)); - rng_seed(&rng, virt_timer); + rng_init_random(&rng); uint32_t iss = rng_next32(&rng); flow->base.ctx.sequence = iss; diff --git a/kernel/networking/transport_layer/tcp/tcp_rx.c b/kernel/networking/transport_layer/tcp/tcp_rx.c index a2daa40f..b20c027c 100644 --- a/kernel/networking/transport_layer/tcp/tcp_rx.c +++ b/kernel/networking/transport_layer/tcp/tcp_rx.c @@ -7,6 +7,7 @@ #include "networking/internet_layer/ipv6_utils.h" #include "std/memory.h" #include "math/rng.h" +#include "random/random.h" #include "syscalls/syscalls.h" #include "../tcp.h" @@ -430,9 +431,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a if ((flags & (1u << SYN_F)) && !(flags & ((1u << ACK_F) | (1u << RST_F) | (1u << FIN_F))) && data_len == 0 && listener){ rng_t rng; - uint64_t virt_timer; - asm volatile ("mrs %0, cntvct_el0" : "=r"(virt_timer)); - rng_seed(&rng, virt_timer); + rng_init_random(&rng); tcp_admit_result_t syn_admit = tcp_admit_syn(l3_id, dst_port, ipver, src_ip_addr); if (syn_admit != TCP_ADMIT_OK) { diff --git a/kernel/networking/transport_layer/tcp/tcp_utils.c b/kernel/networking/transport_layer/tcp/tcp_utils.c index acfe7482..70fe2bca 100644 --- a/kernel/networking/transport_layer/tcp/tcp_utils.c +++ b/kernel/networking/transport_layer/tcp/tcp_utils.c @@ -126,8 +126,8 @@ uint8_t tcp_build_syn_options(uint8_t *out, uint16_t mss, uint8_t wscale, uint8_ out[i++] = 2; out[i++] = 4; - out[i++] = (uint8_t)(mss >> 8); - out[i++] = (uint8_t)(mss & 0xff); + wr_be16(out + i, mss); + i += 2; if (wscale != 0xffu){ out[i++] = 1; diff --git a/kernel/networking/transport_layer/trans_utils.h b/kernel/networking/transport_layer/trans_utils.h index 19418cfb..7f834bc3 100644 --- a/kernel/networking/transport_layer/trans_utils.h +++ b/kernel/networking/transport_layer/trans_utils.h @@ -34,10 +34,11 @@ static inline void net_ep_split(const net_l4_endpoint* ep, char* ip, int iplen, } } -static void make_ep(uint32_t ip_host, uint16_t port, ip_version_t ver, net_l4_endpoint* ep) { +static inline void make_ep(const void *ip, uint16_t port, ip_version_t ver, net_l4_endpoint* ep) { + if (!ep) return; memset(ep, 0, sizeof(*ep)); ep->ver = ver; - memcpy(ep->ip, &ip_host, 4); + if (ip) memcpy(ep->ip, ip, ver == IP_VER6 ? 16 : 4); ep->port = port; } diff --git a/kernel/tools/ping.c b/kernel/tools/ping.c index 9c55a84e..38f138a5 100644 --- a/kernel/tools/ping.c +++ b/kernel/tools/ping.c @@ -14,8 +14,16 @@ #include "networking/internet_layer/ipv4_utils.h" #include "networking/internet_layer/ipv6_utils.h" #include "networking/internet_layer/icmpv6.h" -//TODO add a print variant that does not append a newline automatically in serial -//std printf behavior is useful here since output requires explicit \n +#include "networking/interface_manager.h" +#include "networking/transport_layer/trans_utils.h" + +#define PING_MAX_REPLIES 16 + +typedef struct { + const char *host; + net_l4_endpoint src_ip; + ip_tx_opts_t src_if; +} ping_addressing_t; typedef struct { ip_version_t ver; @@ -23,9 +31,7 @@ typedef struct { uint32_t timeout_ms; uint32_t interval_ms; uint32_t ttl; - uint32_t src_ip; - bool src_set; - const char *host; + ping_addressing_t addr; } ping_opts_t; static bool parse_args(int argc, char *argv[], ping_opts_t *o) { @@ -34,9 +40,7 @@ static bool parse_args(int argc, char *argv[], ping_opts_t *o) { o->timeout_ms = 1000; o->interval_ms = 1000; o->ttl = 64; - o->src_ip = 0; - o->src_set = false; - o->host = NULL; + memset(&o->addr, 0, sizeof(o->addr)); for (int i = 1; i < argc; ++i) { const char *a = argv[i]; @@ -57,19 +61,32 @@ static bool parse_args(int argc, char *argv[], ping_opts_t *o) { if (!parse_uint32_dec(argv[i], &o->ttl)) return false; } else if (strcmp_case(a, "-s",true) == 0) { if (++i >= argc) return false; - uint32_t src = 0; - if (!ipv4_parse(argv[i], &src)) return false; - o->src_ip = src; - o->src_set = true; + if (o->addr.src_ip.ver || o->addr.src_if.scope != IP_TX_AUTO) return false; + uint32_t id = 0; + if (strncmp(argv[i], "l2:", 3) == 0) { + if (!parse_uint32_dec_exact(argv[i] + 3, &id) || id == 0 || id > UINT8_MAX) return false; + o->addr.src_if.index = (uint8_t)id; + o->addr.src_if.scope = IP_TX_BOUND_L2; + } else if (strncmp(argv[i], "l3:", 3) == 0) { + if (!parse_uint32_dec_exact(argv[i] + 3, &id) || id == 0 || id > UINT8_MAX) return false; + o->addr.src_if.index = (uint8_t)id; + o->addr.src_if.scope = IP_TX_BOUND_L3; + } else { + uint32_t src4 = 0; + uint8_t src6[16]; + if (ipv4_parse(argv[i], &src4)) make_ep(&src4, 0, IP_VER4, &o->addr.src_ip); + else if (ipv6_parse(argv[i], src6)) make_ep(src6, 0, IP_VER6, &o->addr.src_ip); + else return false; + } } else return false; } else { - if (o->host) return false; - o->host = a; + if (o->addr.host) return false; + o->addr.host = a; } } - if (!o->host) return false; + if (!o->addr.host) return false; return true; } @@ -91,7 +108,7 @@ static const char *status_to_msg(uint8_t st) { } static int ping_v4(const ping_opts_t *o) { - const char *host = o->host; + const char *host = o->addr.host; uint32_t dst_ip_be = 0; bool is_lit = ipv4_parse(host, &dst_ip_be); @@ -117,33 +134,65 @@ static int ping_v4(const ping_opts_t *o) { ip_tx_opts_t txo = {0}; const ip_tx_opts_t *txop = NULL; - if (o->src_set) { - l3_ipv4_interface_t *l3 = l3_ipv4_find_by_ip(o->src_ip); - if (!l3) { - print("ping: invalid source (no local ip match)"); + if (o->addr.src_ip.ver) { + uint32_t src4 = 0; + memcpy(&src4, o->addr.src_ip.ip, 4); + l3_ipv4_interface_t *l3 = l3_ipv4_find_by_ip(src4); + txo.index = l3->l3_id; + txo.scope = IP_TX_BOUND_L3; + txop = &txo; + } else if (o->addr.src_if.scope != IP_TX_AUTO) txop = &o->addr.src_if; + else if (ipv4_is_limited_broadcast(dst_ip_be)) { + l3_ipv4_interface_t *chosen = NULL; + uint8_t cnt = l2_interface_count(); + for (uint8_t li = 0; li < cnt; li++) { + l2_interface_t *l2 = l2_interface_at(li); + if (!l2 || !l2->is_up) continue; + for (int vi = 0; vi < MAX_IPV4_PER_INTERFACE; vi++) { + l3_ipv4_interface_t *v4 = l2->l3_v4[vi]; + if (!ipv4_l3_is_ready(v4) || v4->is_localhost) continue; + if (chosen) { + print("ping: broadcast requires -s when more IPv4 interfaces are usable"); + return 2; + } + chosen = v4; + } + } + if (!chosen) { + print("ping: no usable IPv4 interface"); return 2; } - txo.index = (uint8_t)l3->l3_id; + txo.index = (uint8_t)chosen->l3_id; txo.scope = IP_TX_BOUND_L3; txop = &txo; } + bool multi = ipv4_is_multicast(dst_ip_be) || ipv4_is_limited_broadcast(dst_ip_be); + uint32_t max_results = multi ? PING_MAX_REPLIES : 1; for (uint32_t i = 0; i < o->count; i++) { ++sent; uint16_t seq = (uint16_t)(seq_base + i); - ping_result_t res = {0}; - bool ok = icmp_ping(dst_ip_be, id, seq, o->timeout_ms, txop, (uint8_t)o->ttl, &res); - - if (ok) { - ++received; - uint32_t rtt = res.rtt_ms; - if (rtt < min_ms) min_ms = rtt; - if (rtt > max_ms) max_ms = rtt; - sum_ms += rtt; - print("Reply from %s: bytes=32 time=%ums", ipstr, rtt); + ping_result_t res[PING_MAX_REPLIES]; + uint32_t n = icmp_ping_collect(dst_ip_be, id, seq, o->timeout_ms, txop, (uint8_t)o->ttl, res, max_results); + + if (n) { + for (uint32_t j = 0; j < n; j++) { + if (res[j].status == PING_OK) { + received++; + uint32_t rtt = res[j].rtt_ms; + if (rtt < min_ms) min_ms = rtt; + if (rtt > max_ms) max_ms = rtt; + sum_ms += rtt; + char rip[16]; + ipv4_to_string(res[j].responder_ip, rip); + print("Reply from %s: bytes=32 time=%ums", rip, rtt); + } else { + print("%s", status_to_msg(res[j].status)); + } + } } else { - print("%s", status_to_msg(res.status)); + print("%s", status_to_msg(PING_TIMEOUT)); } if (i + 1 < o->count) msleep(o->interval_ms); @@ -152,7 +201,7 @@ static int ping_v4(const ping_opts_t *o) { print(""); print("--- %s ping statistics ---", host); - uint32_t loss = (sent == 0) ? 0 : (uint32_t)((((uint64_t)(sent - received)) * 100) / sent); + uint32_t loss = (sent == 0 || received >= sent) ? 0 : (uint32_t)((((uint64_t)(sent - received)) * 100) / sent); uint32_t total_time = (o->count > 0) ? (o->count - 1) * o->interval_ms : 0; print("%u packets transmitted, %u received, %u%% packet loss, time %ums", sent, received, loss, total_time); @@ -167,7 +216,7 @@ static int ping_v4(const ping_opts_t *o) { } static int ping_v6(const ping_opts_t *o) { - const char *host = o->host; + const char *host = o->addr.host; uint8_t dst6[16] ={0}; bool is_lit = ipv6_parse(host, dst6); @@ -189,23 +238,62 @@ static int ping_v6(const ping_opts_t *o) { uint16_t id = (uint16_t)(get_current_proc_pid() & 0xFFFF); uint16_t seq_base = (uint16_t)(get_time() & 0xFFFF); + ip_tx_opts_t txo = {0}; + const ip_tx_opts_t *txop = NULL; + if (o->addr.src_ip.ver) { + l3_ipv6_interface_t *l3 = l3_ipv6_find_by_ip(o->addr.src_ip.ip); + txo.index = l3->l3_id; + txo.scope = IP_TX_BOUND_L3; + txop = &txo; + } else if (o->addr.src_if.scope != IP_TX_AUTO) txop = &o->addr.src_if; + else if (ipv6_is_linkscope_mcast(dst6)) { + l3_ipv6_interface_t *chosen = NULL; + uint8_t cnt = l2_interface_count(); + for (uint8_t li = 0; li < cnt; li++) { + l2_interface_t *l2 = l2_interface_at(li); + if (!l2 || !l2->is_up) continue; + for (int vi = 0; vi < MAX_IPV6_PER_INTERFACE; vi++) { + l3_ipv6_interface_t *v6 = l2->l3_v6[vi]; + if (!ipv6_l3_is_ready(v6) || v6->is_localhost || !ipv6_is_linklocal(v6->ip)) continue; + if (chosen) { + print("ping: IPv6 link local multicast requires -s when more IPv6 interfaces are usable"); + return 2; + } + chosen = v6; + } + } + if (!chosen) { + print("ping: no usable IPv6 interface"); + return 2; + } + txo.index = (uint8_t)chosen->l3_id; + txo.scope = IP_TX_BOUND_L3; + txop = &txo; + } + + bool multi = ipv6_is_multicast(dst6); + uint32_t max_results = multi ? PING_MAX_REPLIES : 1; for (uint32_t i = 0; i < o->count; i++) { ++sent; uint16_t seq = (uint16_t)(seq_base + i); - ping6_result_t res = {0}; - bool ok = icmpv6_ping(dst6, id, seq, o->timeout_ms, NULL, (uint8_t)o->ttl, &res); - - if (ok) { - ++received; - uint32_t rtt = res.rtt_ms; - if (rtt < min_ms) min_ms = rtt; - if (rtt > max_ms) max_ms = rtt; - sum_ms += rtt; - print("Reply from %s: bytes=32 time=%ums", ipstr, rtt); - } else { - print("%s", status_to_msg(res.status)); - } + ping6_result_t res[PING_MAX_REPLIES]; + uint32_t n = icmpv6_ping_collect(dst6, id, seq, o->timeout_ms, txop, (uint8_t)o->ttl, res, max_results); + + if (n) { + for (uint32_t j = 0; j < n; j++) { + if (res[j].status == PING_OK) { + ++received; + uint32_t rtt = res[j].rtt_ms; + if (rtt < min_ms) min_ms = rtt; + if (rtt > max_ms) max_ms = rtt; + sum_ms += rtt; + char rip[64]; + ipv6_to_string(res[j].responder_ip, rip, (int)sizeof(rip)); + print("Reply from %s: bytes=32 time=%ums", rip, rtt); + } else print("%s", status_to_msg(res[j].status)); + } + } else print("%s", status_to_msg(PING_TIMEOUT)); if (i + 1 < o->count) msleep(o->interval_ms); } @@ -214,7 +302,7 @@ static int ping_v6(const ping_opts_t *o) { print("--- %s ping statistics ---", host); - uint32_t loss = (sent == 0) ? 0 : (uint32_t)((((uint64_t)(sent - received)) * 100) / sent); + uint32_t loss = (sent == 0 || received >= sent) ? 0 : (uint32_t)((((uint64_t)(sent - received)) * 100) / sent); uint32_t total_time = (o->count > 0) ? (o->count - 1) * o->interval_ms : 0; print("%u packets transmitted, %u received, %u%% packet loss, time %ums", sent, received, loss, total_time); @@ -231,28 +319,79 @@ static int ping_v6(const ping_opts_t *o) { int run_ping(int argc, char *argv[]) { ping_opts_t opts; if (!parse_args(argc, argv, &opts)) { - print("usage: ping [-4/-6] [-n times] [-w timeout] [-i interval] [-t TTL] [-s src_local_ip] host"); + print("usage: ping [-4/-6] [-n times] [-w timeout] [-i interval] [-t TTL] [-s ip|l2:id|l3:id] host"); return 2; } - if (opts.ver == IP_VER6 && opts.src_set) { - print("ping: -s is only supported for IPv4"); + if (opts.addr.src_ip.ver && opts.addr.src_ip.ver != opts.ver) { + print("ping: source address version doesn't match target version"); return 2; } - if (opts.ver == IP_VER4 && opts.src_set) { - l3_ipv4_interface_t *l3 = l3_ipv4_find_by_ip(opts.src_ip); - if (!l3) { + if (opts.ver == IP_VER4 && opts.addr.src_ip.ver) { + uint32_t src4 = 0; + memcpy(&src4, opts.addr.src_ip.ip, 4); + if (!l3_ipv4_find_by_ip(src4)) { char ssrc[16]; - ipv4_to_string(opts.src_ip, ssrc); + ipv4_to_string(src4, ssrc); print("ping: invalid source %s (no local ip match)", ssrc); return 2; } } + if (opts.ver == IP_VER6 && opts.addr.src_ip.ver && !l3_ipv6_find_by_ip(opts.addr.src_ip.ip)) { + char ssrc[64]; + ipv6_to_string(opts.addr.src_ip.ip, ssrc, (int)sizeof(ssrc)); + print("ping: invalid source %s (no local ip match)", ssrc); + return 2; + } + + if (opts.ver == IP_VER4 && opts.addr.src_if.scope == IP_TX_BOUND_L3) { + if (!ipv4_l3_is_ready(l3_ipv4_find_by_id(opts.addr.src_if.index))) { + print("ping: invalid IPv4 L3 interface %u", opts.addr.src_if.index); + return 2; + } + } + + if (opts.ver == IP_VER6 && opts.addr.src_if.scope == IP_TX_BOUND_L3) { + if (!ipv6_l3_is_ready(l3_ipv6_find_by_id(opts.addr.src_if.index))) { + print("ping: invalid IPv6 L3 interface %u", opts.addr.src_if.index); + return 2; + } + } + + if (opts.addr.src_if.scope == IP_TX_BOUND_L2) { + l2_interface_t *l2 = l2_interface_find_by_index(opts.addr.src_if.index); + if (!l2 || !l2->is_up) { + print("ping: invalid L2 interface %u", opts.addr.src_if.index); + return 2; + } + + bool has_l3 = false; + if (opts.ver == IP_VER4) { + for (int i = 0; i < MAX_IPV4_PER_INTERFACE; i++) { + if (ipv4_l3_is_ready(l2->l3_v4[i])) { + has_l3 = true; + break; + } + } + } else { + for (int i = 0; i < MAX_IPV6_PER_INTERFACE; i++) { + if (ipv6_l3_is_ready(l2->l3_v6[i])) { + has_l3 = true; + break; + } + } + } + if (!has_l3) { + print("ping: L2 interface %u has no usable %s L3 interface", opts.addr.src_if.index, opts.ver == IP_VER4 ? "IPv4" : "IPv6"); + return 2; + } + } + if (opts.ver == IP_VER4) return ping_v4(&opts); if (opts.ver == IP_VER6) return ping_v6(&opts); - print("usage: ping [-4/-6] [-n times] [-w timeout] [-i interval] [-t TTL] [-s src_local_ip] host"); + print("usage: ping [-4/-6] [-n times] [-w timeout] [-i interval] [-t TTL] [-s ip|l2:id|l3:id] host"); return 2; } diff --git a/shared b/shared index a3a584a8..ff368319 160000 --- a/shared +++ b/shared @@ -1 +1 @@ -Subproject commit a3a584a89691a40c4c28a85beb9987ad82b10147 +Subproject commit ff3683196a366f834f1fecbcf6ebc8729192b9bd From df553033812c31ee9360cfe371dc2041d140fdd9 Mon Sep 17 00:00:00 2001 From: CodeAnarchist Date: Thu, 18 Jun 2026 14:56:48 +0200 Subject: [PATCH 25/39] [net] improve socket options and route epochs --- .../application_layer/csocket_http_client.c | 75 +++-- .../application_layer/csocket_http_client.h | 2 +- .../application_layer/csocket_http_server.c | 71 +++-- .../application_layer/csocket_http_server.h | 2 +- .../application_layer/dhcp_daemon.c | 50 ++-- .../application_layer/dhcpv6_daemon.c | 39 ++- .../application_layer/dns/dns_daemon.c | 4 +- .../application_layer/ssdp_daemon.c | 4 +- kernel/networking/drivers/net_driver.hpp | 5 +- .../drivers/virtio_net_pci/virtio_net_pci.cpp | 4 +- .../drivers/virtio_net_pci/virtio_net_pci.hpp | 4 +- kernel/networking/interface_manager.c | 64 ++++- kernel/networking/interface_manager.h | 2 + kernel/networking/internet_layer/ipv4_route.c | 58 ++-- kernel/networking/internet_layer/ipv4_route.h | 4 +- kernel/networking/internet_layer/ipv6_route.c | 92 +++--- kernel/networking/internet_layer/ipv6_route.h | 4 +- kernel/networking/link_layer/ndp.c | 41 +-- kernel/networking/net_logger/net_logger.c | 2 +- kernel/networking/net_logger/net_logger.h | 2 +- kernel/networking/network_dispatch.cpp | 4 +- kernel/networking/transport_layer/csocket.c | 30 +- kernel/networking/transport_layer/csocket.h | 4 +- .../networking/transport_layer/csocket_tcp.c | 269 ++++++++++++------ .../networking/transport_layer/csocket_tcp.h | 8 +- .../networking/transport_layer/csocket_udp.c | 208 +++++++++----- .../networking/transport_layer/csocket_udp.h | 7 +- .../networking/transport_layer/socket_bind.c | 19 +- .../networking/transport_layer/socket_core.c | 34 ++- .../networking/transport_layer/socket_core.h | 4 +- kernel/networking/transport_layer/tcp.h | 4 +- .../networking/transport_layer/tcp/tcp_core.c | 4 +- .../networking/transport_layer/tcp/tcp_rx.c | 2 +- kernel/process/kernel_syscall_impl.c | 2 +- kernel/process/syscall.c | 4 +- shared | 2 +- 36 files changed, 649 insertions(+), 485 deletions(-) diff --git a/kernel/networking/application_layer/csocket_http_client.c b/kernel/networking/application_layer/csocket_http_client.c index b1187629..9d153cab 100644 --- a/kernel/networking/application_layer/csocket_http_client.c +++ b/kernel/networking/application_layer/csocket_http_client.c @@ -1,7 +1,6 @@ #include "csocket_http_client.h" #include "console/kio.h" #include "networking/transport_layer/csocket.h" -#include "networking/transport_layer/tcp.h" #include "networking/net_logger/net_logger.h" #include "http.h" #include "std/std.h" @@ -20,8 +19,8 @@ typedef struct HTTPClientOrigin { typedef struct HTTPClient { socket_handle_t sock; - SocketExtraOptions log_opts; - SocketExtraOptions* tcp_extra; + SocketOptions log_opts; + SocketOptions tcp_opts; HTTPClientPolicy policy; HTTPClientOrigin origin; } HTTPClient; @@ -44,7 +43,7 @@ static HTTPResponseMsg http_client_receive_response(HTTPClient* cli, HTTPMethod while (hdr_end < 0) { int64_t r = receive_from_socket(cli->sock, tmp, sizeof(tmp), NULL); - if (r == TCP_WOULDBLOCK) { + if (r == SOCK_ERR_WOULDBLOCK) { uint32_t now = (uint32_t)get_time(); if ((now - last_rx_ms) > cli->policy.common.header_idle_timeout_ms || (now - start_ms) > cli->policy.common.header_total_timeout_ms) { string_free(buf); @@ -135,7 +134,7 @@ static HTTPResponseMsg http_client_receive_response(HTTPClient* cli, HTTPMethod while (chunk_result == HTTP_PARSE_INCOMPLETE) { int64_t r = receive_from_socket(cli->sock, tmp, sizeof(tmp), NULL); - if (r == TCP_WOULDBLOCK) { + if (r == SOCK_ERR_WOULDBLOCK) { uint32_t now = (uint32_t)get_time(); if ((now - body_last_rx_ms) > cli->policy.common.body_idle_timeout_ms || (now - body_start_ms) > cli->policy.common.body_total_timeout_ms) break; msleep(1); @@ -196,7 +195,7 @@ static HTTPResponseMsg http_client_receive_response(HTTPClient* cli, HTTPMethod uint32_t body_last_rx_ms = body_start_ms; while (copied < need) { int64_t r = receive_from_socket(cli->sock, body_copy + copied, need - copied, NULL); - if (r == TCP_WOULDBLOCK) { + if (r == SOCK_ERR_WOULDBLOCK) { uint32_t now = (uint32_t)get_time(); if ((now - body_last_rx_ms) > cli->policy.common.body_idle_timeout_ms || (now - body_start_ms) > cli->policy.common.body_total_timeout_ms) break; msleep(1); @@ -228,7 +227,7 @@ static HTTPResponseMsg http_client_receive_response(HTTPClient* cli, HTTPMethod uint32_t body_last_rx_ms = body_start_ms; while (body.length <= cli->policy.common.max_body_bytes) { int64_t r = receive_from_socket(cli->sock, tmp, sizeof(tmp), NULL); - if (r == TCP_WOULDBLOCK) { + if (r == SOCK_ERR_WOULDBLOCK) { uint32_t now = (uint32_t)get_time(); if ((now - body_last_rx_ms) > cli->policy.common.body_idle_timeout_ms || (now - body_start_ms) > cli->policy.common.body_total_timeout_ms) { string_free(body); @@ -280,10 +279,11 @@ static HTTPResponseMsg http_client_receive_response(HTTPClient* cli, HTTPMethod ev.pid = get_current_proc_pid(); ev.u0 = (uint32_t)resp.status_code; ev.u1 = (uint32_t)resp.body.length; - ev.local_port = get_socket_local_port(cli->sock); - net_l4_endpoint sock_remote_ep = {0}; - get_socket_remote_endpoint(cli->sock, &sock_remote_ep); - ev.remote_ep = sock_remote_ep; + uint32_t local_port = 0; + uint32_t opt_len = sizeof(local_port); + if (get_socket_option(cli->sock, SOCK_GET_LOCAL_PORT, &local_port, &opt_len) == SOCK_OK) ev.local_port = local_port; + opt_len = sizeof(ev.remote_ep); + get_socket_option(cli->sock, SOCK_GET_REMOTE_ENDPOINT, &ev.remote_ep, &opt_len); netlog_socket_event(&cli->log_opts, &ev); string_free(buf); @@ -292,7 +292,7 @@ static HTTPResponseMsg http_client_receive_response(HTTPClient* cli, HTTPMethod } static int32_t http_client_connect_endpoint_origin(HTTPClient* cli, const net_l4_endpoint* dst, const char* host_name) { - if (!cli->sock) cli->sock = create_socket(PROTO_TCP, cli->tcp_extra ? cli->tcp_extra : &cli->log_opts); + if (!cli->sock) cli->sock = create_socket(PROTO_TCP, &cli->tcp_opts); if (!cli->sock) return SOCK_ERR_SYS; int32_t r = connect_socket(cli->sock, dst); if (r >= 0) { @@ -312,10 +312,11 @@ static int32_t http_client_connect_endpoint_origin(HTTPClient* cli, const net_l4 ev.i0 = r; if (cli->sock) { - ev.local_port = get_socket_local_port(cli->sock); - net_l4_endpoint sock_remote_ep = {0}; - get_socket_remote_endpoint(cli->sock, &sock_remote_ep); - ev.remote_ep = sock_remote_ep; + uint32_t local_port = 0; + uint32_t opt_len = sizeof(local_port); + if (get_socket_option(cli->sock, SOCK_GET_LOCAL_PORT, &local_port, &opt_len) == SOCK_OK) ev.local_port = local_port; + opt_len = sizeof(ev.remote_ep); + get_socket_option(cli->sock, SOCK_GET_REMOTE_ENDPOINT, &ev.remote_ep, &opt_len); if (ev.remote_ep.ver) ev.dst_ep = ev.remote_ep; } @@ -330,19 +331,15 @@ static int32_t http_client_connect_domain_origin(HTTPClient* cli, const char* ho return http_client_connect_endpoint_origin(cli, &ep, host); } -http_client_handle_t http_client_create(const SocketExtraOptions* extra, const HTTPClientPolicyOptions* http_options) { +http_client_handle_t http_client_create(const SocketOptions* extra, const HTTPClientPolicyOptions* http_options) { HTTPClient* cli = (HTTPClient*)zalloc(sizeof(*cli)); if (!cli) return NULL; cli->policy = http_client_policy_from_options(http_options); - if (extra) cli->log_opts = *extra; - - if (extra && (cli->log_opts.flags & SOCK_OPT_DEBUG)) { - cli->tcp_extra = (SocketExtraOptions*)zalloc(sizeof(SocketExtraOptions)); - if (cli->tcp_extra) { - *cli->tcp_extra = *extra; - cli->tcp_extra->flags &= ~SOCK_OPT_DEBUG; - } + if (extra) { + cli->log_opts = *extra; + cli->tcp_opts = *extra; + cli->tcp_opts.flags &= ~SOCK_OPT_DEBUG; } return cli; @@ -430,7 +427,7 @@ HTTPResponseMsg http_client_send_request(http_client_handle_t h, const HTTPReque int64_t sent = 0; while (off < out_len) { int64_t r = send_on_socket(cli->sock, (void*)(out.data + off), out_len - off); - if (r == TCP_WOULDBLOCK) { + if (r == SOCK_ERR_WOULDBLOCK) { msleep(5); continue; } @@ -448,10 +445,11 @@ HTTPResponseMsg http_client_send_request(http_client_handle_t h, const HTTPReque ev.pid = get_current_proc_pid(); ev.u0 = out_len; ev.i0 = sent; - ev.local_port = get_socket_local_port(cli->sock); - net_l4_endpoint sock_remote_ep = {0}; - get_socket_remote_endpoint(cli->sock, &sock_remote_ep); - ev.remote_ep = sock_remote_ep; + uint32_t local_port = 0; + uint32_t opt_len = sizeof(local_port); + if (get_socket_option(cli->sock, SOCK_GET_LOCAL_PORT, &local_port, &opt_len) == SOCK_OK) ev.local_port = local_port; + opt_len = sizeof(ev.remote_ep); + get_socket_option(cli->sock, SOCK_GET_REMOTE_ENDPOINT, &ev.remote_ep, &opt_len); char pathbuf[128]; if (curr.path.length && curr.path.data) { @@ -504,7 +502,7 @@ HTTPResponseMsg http_client_send_request(http_client_handle_t h, const HTTPReque close_socket(cli->sock); cli->sock = 0; } - cli->sock = next_domain.data ? create_socket(PROTO_TCP, cli->tcp_extra ? cli->tcp_extra : &cli->log_opts) : 0; + cli->sock = next_domain.data ? create_socket(PROTO_TCP, &cli->tcp_opts) : 0; int32_t rr = cli->sock ? SOCK_OK : SOCK_ERR_SYS; if (rr >= 0) rr = http_client_connect_domain_origin(cli, next_domain.data, next_port); if (next_domain.mem_length) string_free(next_domain); @@ -522,7 +520,7 @@ HTTPResponseMsg http_client_send_request(http_client_handle_t h, const HTTPReque close_socket(cli->sock); cli->sock = 0; } - cli->sock = cli->origin.valid ? create_socket(PROTO_TCP, cli->tcp_extra ? cli->tcp_extra : &cli->log_opts) : 0; + cli->sock = cli->origin.valid ? create_socket(PROTO_TCP, &cli->tcp_opts) : 0; int32_t rr = cli->sock ? SOCK_OK : SOCK_ERR_SYS; if (rr >= 0) rr = reconnect_domain.data ? http_client_connect_domain_origin(cli, reconnect_domain.data, reconnect_ep.port) : http_client_connect_endpoint_origin(cli, &reconnect_ep, NULL); if (reconnect_domain.mem_length) string_free(reconnect_domain); @@ -559,10 +557,11 @@ int32_t http_client_close(http_client_handle_t h) { ev.pid = get_current_proc_pid(); if (cli->sock) { - ev.local_port = get_socket_local_port(cli->sock); - net_l4_endpoint sock_remote_ep = {0}; - get_socket_remote_endpoint(cli->sock, &sock_remote_ep); - ev.remote_ep = sock_remote_ep; + uint32_t local_port = 0; + uint32_t opt_len = sizeof(local_port); + if (get_socket_option(cli->sock, SOCK_GET_LOCAL_PORT, &local_port, &opt_len) == SOCK_OK) ev.local_port = local_port; + opt_len = sizeof(ev.remote_ep); + get_socket_option(cli->sock, SOCK_GET_REMOTE_ENDPOINT, &ev.remote_ep, &opt_len); } int32_t r = SOCK_ERR_STATE; @@ -573,11 +572,7 @@ int32_t http_client_close(http_client_handle_t h) { ev.i0 = r; netlog_socket_event(&cli->log_opts, &ev); - if (cli->tcp_extra) release(cli->tcp_extra); - cli->tcp_extra = NULL; - if (cli->origin.domain.mem_length) string_free(cli->origin.domain); cli->origin = (HTTPClientOrigin){0}; - cli->log_opts.flags &= ~SOCK_OPT_DEBUG; return r; } diff --git a/kernel/networking/application_layer/csocket_http_client.h b/kernel/networking/application_layer/csocket_http_client.h index 5ae5e79a..5c541ab9 100644 --- a/kernel/networking/application_layer/csocket_http_client.h +++ b/kernel/networking/application_layer/csocket_http_client.h @@ -8,7 +8,7 @@ extern "C" { typedef void* http_client_handle_t; -http_client_handle_t http_client_create(const SocketExtraOptions* extra, const HTTPClientPolicyOptions *options); +http_client_handle_t http_client_create(const SocketOptions* extra, const HTTPClientPolicyOptions *options); int32_t http_client_set_options(http_client_handle_t h, const HTTPClientPolicyOptions *options); void http_client_destroy(http_client_handle_t h); diff --git a/kernel/networking/application_layer/csocket_http_server.c b/kernel/networking/application_layer/csocket_http_server.c index a7e51b76..4d9dadad 100644 --- a/kernel/networking/application_layer/csocket_http_server.c +++ b/kernel/networking/application_layer/csocket_http_server.c @@ -1,7 +1,6 @@ #include "csocket_http_server.h" #include "console/kio.h" #include "networking/transport_layer/csocket.h" -#include "networking/transport_layer/tcp.h" #include "networking/net_logger/net_logger.h" #include "http.h" #include "std/std.h" @@ -21,24 +20,20 @@ typedef struct HTTPConnection { typedef struct HTTPServer { socket_handle_t sock; - SocketExtraOptions log_opts; - SocketExtraOptions* tcp_extra; + SocketOptions log_opts; + SocketOptions tcp_opts; HTTPServerPolicy policy; } HTTPServer; -http_server_handle_t http_server_create(const SocketExtraOptions* extra, const HTTPServerPolicyOptions* http_options) { +http_server_handle_t http_server_create(const SocketOptions* extra, const HTTPServerPolicyOptions* http_options) { HTTPServer* srv = (HTTPServer*)zalloc(sizeof(*srv)); if (!srv) return NULL; srv->policy = http_server_policy_from_options(http_options); - if (extra) srv->log_opts = *extra; - - if (extra && (srv->log_opts.flags & SOCK_OPT_DEBUG)) { - srv->tcp_extra = (SocketExtraOptions*)zalloc(sizeof(SocketExtraOptions)); - if (srv->tcp_extra) { - *srv->tcp_extra = *extra; - srv->tcp_extra->flags &= ~SOCK_OPT_DEBUG; - } + if (extra) { + srv->log_opts = *extra; + srv->tcp_opts = *extra; + srv->tcp_opts.flags &= ~SOCK_OPT_DEBUG; } return srv; @@ -63,7 +58,7 @@ int32_t http_server_bind(http_server_handle_t h, const SockBindSpec* spec, uint1 HTTPServer* srv = (HTTPServer*)h; uint16_t p = port; - if (!srv->sock) srv->sock = create_socket(PROTO_TCP, srv->tcp_extra ? srv->tcp_extra : &srv->log_opts); + if (!srv->sock) srv->sock = create_socket(PROTO_TCP, &srv->tcp_opts); if (!srv->sock) return SOCK_ERR_SYS; int32_t r = bind_socket(srv->sock, spec, p); @@ -120,10 +115,11 @@ http_connection_handle_t http_server_accept(http_server_handle_t h) { ev.action = NETLOG_ACT_ACCEPT; ev.pid = get_current_proc_pid(); ev.i0 = (int64_t)child; - ev.local_port = get_socket_local_port(child); - net_l4_endpoint child_remote_ep = {0}; - get_socket_remote_endpoint(child, &child_remote_ep); - ev.remote_ep = child_remote_ep; + uint32_t local_port = 0; + uint32_t opt_len = sizeof(local_port); + if (get_socket_option(child, SOCK_GET_LOCAL_PORT, &local_port, &opt_len) == SOCK_OK) ev.local_port = local_port; + opt_len = sizeof(ev.remote_ep); + get_socket_option(child, SOCK_GET_REMOTE_ENDPOINT, &ev.remote_ep, &opt_len); netlog_socket_event(&srv->log_opts, &ev); return conn; } @@ -154,7 +150,7 @@ HTTPRequestMsg http_server_recv_request(http_server_handle_t h, http_connection_ while (hdr_end < 0) { int64_t r = receive_from_socket(conn->client, tmp, sizeof(tmp), NULL); - if (r == TCP_WOULDBLOCK) { + if (r == SOCK_ERR_WOULDBLOCK) { uint32_t now = (uint32_t)get_time(); if ((uint32_t)(now - last_rx_ms) > srv->policy.common.header_idle_timeout_ms || (uint32_t)(now - start_ms) > srv->policy.common.header_total_timeout_ms) { string_free(buf); @@ -296,7 +292,7 @@ HTTPRequestMsg http_server_recv_request(http_server_handle_t h, http_connection_ while (chunk_result == HTTP_PARSE_INCOMPLETE) { int64_t r = receive_from_socket(conn->client, tmp, sizeof(tmp), NULL); - if (r == TCP_WOULDBLOCK) { + if (r == SOCK_ERR_WOULDBLOCK) { uint32_t now = (uint32_t)get_time(); if ((now - body_last_rx_ms) > srv->policy.common.body_idle_timeout_ms || (now - body_start_ms) > srv->policy.common.body_total_timeout_ms) break; msleep(2); @@ -343,7 +339,7 @@ HTTPRequestMsg http_server_recv_request(http_server_handle_t h, http_connection_ uint32_t body_last_rx_ms = body_start_ms; while (copied < need) { int64_t r = receive_from_socket(conn->client, body_copy + copied, need - copied, NULL); - if (r == TCP_WOULDBLOCK) { + if (r == SOCK_ERR_WOULDBLOCK) { uint32_t now = (uint32_t)get_time(); if ((now - body_last_rx_ms) > srv->policy.common.body_idle_timeout_ms || (now - body_start_ms) > srv->policy.common.body_total_timeout_ms) { bad_request = true; @@ -414,10 +410,11 @@ HTTPRequestMsg http_server_recv_request(http_server_handle_t h, http_connection_ ev.u0 = (uint32_t)req.method; ev.u1 = (uint32_t)req.path.length; ev.i0 = (int64_t)req.body.length; - ev.local_port = get_socket_local_port(conn->client); - net_l4_endpoint conn_remote_ep = {0}; - get_socket_remote_endpoint(conn->client, &conn_remote_ep); - ev.remote_ep = conn_remote_ep; + uint32_t local_port = 0; + uint32_t opt_len = sizeof(local_port); + if (get_socket_option(conn->client, SOCK_GET_LOCAL_PORT, &local_port, &opt_len) == SOCK_OK) ev.local_port = local_port; + opt_len = sizeof(ev.remote_ep); + get_socket_option(conn->client, SOCK_GET_REMOTE_ENDPOINT, &ev.remote_ep, &opt_len); char pathbuf[128]; if (req.path.length && req.path.data) { @@ -511,7 +508,7 @@ int32_t http_server_send_response(http_server_handle_t h, http_connection_handle int64_t r = send_on_socket(conn->client, (void*)(first_ptr + off), first_len - off); uint32_t now = (uint32_t)get_time(); - if (r == TCP_WOULDBLOCK || r == 0) { + if (r == SOCK_ERR_WOULDBLOCK || r == 0) { if ((now - progress_ms) > 3000 || (now - start_ms) > 30000) { sent = SOCK_ERR_SYS; break; @@ -537,7 +534,7 @@ int32_t http_server_send_response(http_server_handle_t h, http_connection_handle int64_t r = send_on_socket(conn->client, (void*)(body + body_off), ask); uint32_t now = (uint32_t)get_time(); - if (r == TCP_WOULDBLOCK || r == 0) { + if (r == SOCK_ERR_WOULDBLOCK || r == 0) { if ((now - progress_ms) > 3000 || (now - start_ms) > 30000) { sent = SOCK_ERR_SYS; break; @@ -564,10 +561,11 @@ int32_t http_server_send_response(http_server_handle_t h, http_connection_handle ev.u0 = code; ev.u1 = out_len; ev.i0 = sent; - ev.local_port = get_socket_local_port(conn->client); - net_l4_endpoint conn_remote_ep = {0}; - get_socket_remote_endpoint(conn->client, &conn_remote_ep); - ev.remote_ep = conn_remote_ep; + uint32_t local_port = 0; + uint32_t opt_len = sizeof(local_port); + if (get_socket_option(conn->client, SOCK_GET_LOCAL_PORT, &local_port, &opt_len) == SOCK_OK) ev.local_port = local_port; + opt_len = sizeof(ev.remote_ep); + get_socket_option(conn->client, SOCK_GET_REMOTE_ENDPOINT, &ev.remote_ep, &opt_len); netlog_socket_event(&srv->log_opts, &ev); string_free(out); @@ -598,10 +596,11 @@ int32_t http_server_close(http_server_handle_t h) { ev.pid = get_current_proc_pid(); ev.i0 = r; if (srv->sock) { - ev.local_port = get_socket_local_port(srv->sock); - net_l4_endpoint sock_remote_ep = {0}; - get_socket_remote_endpoint(srv->sock, &sock_remote_ep); - ev.remote_ep = sock_remote_ep; + uint32_t local_port = 0; + uint32_t opt_len = sizeof(local_port); + if (get_socket_option(srv->sock, SOCK_GET_LOCAL_PORT, &local_port, &opt_len) == SOCK_OK) ev.local_port = local_port; + opt_len = sizeof(ev.remote_ep); + get_socket_option(srv->sock, SOCK_GET_REMOTE_ENDPOINT, &ev.remote_ep, &opt_len); } netlog_socket_event(&srv->log_opts, &ev); @@ -610,9 +609,5 @@ int32_t http_server_close(http_server_handle_t h) { srv->sock = 0; } - if (srv->tcp_extra) release(srv->tcp_extra); - srv->tcp_extra = NULL; - - srv->log_opts.flags &= ~SOCK_OPT_DEBUG; return r; } diff --git a/kernel/networking/application_layer/csocket_http_server.h b/kernel/networking/application_layer/csocket_http_server.h index 6bb7cee9..59a7d81f 100644 --- a/kernel/networking/application_layer/csocket_http_server.h +++ b/kernel/networking/application_layer/csocket_http_server.h @@ -10,7 +10,7 @@ extern "C" { typedef void* http_server_handle_t; typedef void* http_connection_handle_t; -http_server_handle_t http_server_create(const SocketExtraOptions* extra, const HTTPServerPolicyOptions *options); +http_server_handle_t http_server_create(const SocketOptions* extra, const HTTPServerPolicyOptions *options); int32_t http_server_set_options(http_server_handle_t srv, const HTTPServerPolicyOptions *options); void http_server_destroy(http_server_handle_t srv); diff --git a/kernel/networking/application_layer/dhcp_daemon.c b/kernel/networking/application_layer/dhcp_daemon.c index 9cce37cf..17f6e00d 100644 --- a/kernel/networking/application_layer/dhcp_daemon.c +++ b/kernel/networking/application_layer/dhcp_daemon.c @@ -18,7 +18,6 @@ #include "types.h" #include "networking/interface_manager.h" #include "networking/network.h" -#include "networking/internet_layer/ipv4_route.h" #include "syscalls/syscalls.h" typedef enum { @@ -215,9 +214,9 @@ static bool udp_wait_for_ack_or_nak(socket_handle_t sock, uint32_t expect_xid, c return false; } -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) { +static bool apply_offer_to_l3(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; + if (!v4) return false; net_runtime_opts_t rt_local; memset(&rt_local, 0, sizeof(rt_local)); uint32_t yi_net = p->yiaddr; @@ -307,29 +306,12 @@ static void apply_offer_to_l3(uint8_t ifindex, uint8_t l3_id, dhcp_packet *p, si 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 (!l3_ipv4_update(l3_id, ip_host, mask_host, gw_host, IPV4_CFG_DHCP, &rt_local)) return false; st->t1_left_ms = t1_s * 1000; st->t2_left_ms = t2_s * 1000; st->lease_left_ms = lease_s * 1000; + return true; } static void dhcp_send_discover_for(dhcp_if_state_t* st) { @@ -462,10 +444,12 @@ static void fsm_once_for(dhcp_if_state_t* st) { st->retry_left_ms = dhcp_next_backoff_ms(st); } else { if (mtype == DHCPACK) { - apply_offer_to_l3(st->ifindex, st->l3_id, resp, sp, st->last_xid, st); + bool ok = apply_offer_to_l3(st->l3_id, resp, sp, st->last_xid, st); free_sized((void*)sp.ptr, sp.size); - st->state = DHCP_S_BOUND; - dhcp_reset_backoff(st); + if (ok) { + st->state = DHCP_S_BOUND; + dhcp_reset_backoff(st); + } else dhcp_drop_lease_and_retry(st); } else { free_sized((void*)sp.ptr, sp.size); dhcp_drop_lease_and_retry(st); @@ -492,10 +476,12 @@ static void fsm_once_for(dhcp_if_state_t* st) { 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); + bool ok = apply_offer_to_l3(st->l3_id, p, sp, st->last_xid, st); free_sized((void*)sp.ptr, sp.size); - st->state = DHCP_S_BOUND; - dhcp_reset_backoff(st); + if (ok) { + st->state = DHCP_S_BOUND; + dhcp_reset_backoff(st); + } else dhcp_drop_lease_and_retry(st); } else { free_sized((void*)sp.ptr, sp.size); dhcp_drop_lease_and_retry(st); @@ -510,10 +496,12 @@ static void fsm_once_for(dhcp_if_state_t* st) { 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); + bool ok = apply_offer_to_l3(st->l3_id, p, sp, st->last_xid, st); free_sized((void*)sp.ptr, sp.size); - st->state = DHCP_S_BOUND; - dhcp_reset_backoff(st); + if (ok) { + st->state = DHCP_S_BOUND; + dhcp_reset_backoff(st); + } else dhcp_drop_lease_and_retry(st); } else { free_sized((void*)sp.ptr, sp.size); dhcp_drop_lease_and_retry(st); diff --git a/kernel/networking/application_layer/dhcpv6_daemon.c b/kernel/networking/application_layer/dhcpv6_daemon.c index f5997dfb..6d72efb7 100644 --- a/kernel/networking/application_layer/dhcpv6_daemon.c +++ b/kernel/networking/application_layer/dhcpv6_daemon.c @@ -127,6 +127,11 @@ static void reset_backoff(dhcpv6_bind_t* b) { static void reset_lease_state(l3_ipv6_interface_t* v6, dhcpv6_bind_t* b) { if (v6) { + if (v6->cfg == IPV6_CFG_DHCPV6) { + if (!ipv6_is_unspecified(v6->ip) || v6->prefix_len || !ipv6_is_unspecified(v6->gateway)) { + l3_ipv6_update(v6->l3_id, (const uint8_t[16]){0}, 0, (const uint8_t[16]){0}, IPV6_CFG_DHCPV6, v6->kind); + } + } v6->dhcpv6_state = DHCPV6_S_INIT; v6->runtime_opts_v6.server_id_len = 0; v6->runtime_opts_v6.lease = 0; @@ -301,9 +306,7 @@ static void fsm_once(dhcpv6_bind_t* b, uint32_t tick_ms) { uint32_t lease_s = v6->runtime_opts_v6.lease; if (elapsed_s >= lease_s) { - v6->runtime_opts_v6.lease = 0; - v6->runtime_opts_v6.lease_start_time = 0; - v6->dhcpv6_state = DHCPV6_S_INIT; + reset_lease_state(v6, b); } else { uint32_t left_s = lease_s - elapsed_s; b->lease_left_ms = left_s * 1000u; @@ -419,9 +422,7 @@ static void fsm_once(dhcpv6_bind_t* b, uint32_t tick_ms) { if (v6->dhcpv6_state == DHCPV6_S_BOUND) { if (!b->lease_left_ms && v6->runtime_opts_v6.lease) { - v6->dhcpv6_state = DHCPV6_S_SOLICIT; - v6->runtime_opts_v6.server_id_len = 0; - reset_backoff(b); + reset_lease_state(v6, b); return; } @@ -468,7 +469,7 @@ static void fsm_once(dhcpv6_bind_t* b, uint32_t tick_ms) { if (!has_dns) { if (!ipv6_is_unspecified(v6->gateway) && !ipv6_is_multicast(v6->gateway)) { - memcpy(v6->runtime_opts_v6.dns[0], v6->gateway, 16); + ipv6_cpy(v6->runtime_opts_v6.dns[0], v6->gateway); } } @@ -480,6 +481,12 @@ static void fsm_once(dhcpv6_bind_t* b, uint32_t tick_ms) { return; } + if (type_peek == DHCPV6_MSG_RELEASE || type_peek == DHCPV6_MSG_DECLINE) { + reset_lease_state(v6, b); + b->done = 1; + return; + } + b->done = 1; v6->dhcpv6_state = DHCPV6_S_INIT; reset_backoff(b); @@ -583,7 +590,7 @@ static void fsm_once(dhcpv6_bind_t* b, uint32_t tick_ms) { if (!has_dns) { if (!ipv6_is_unspecified(v6->gateway) && !ipv6_is_multicast(v6->gateway)) { - memcpy(v6->runtime_opts_v6.dns[0], v6->gateway, 16); + ipv6_cpy(v6->runtime_opts_v6.dns[0], v6->gateway); } } @@ -632,7 +639,7 @@ static void fsm_once(dhcpv6_bind_t* b, uint32_t tick_ms) { if (!has_dns) { if (!ipv6_is_unspecified(v6->gateway) && !ipv6_is_multicast(v6->gateway)) { - memcpy(v6->runtime_opts_v6.dns[0], v6->gateway, 16); + ipv6_cpy(v6->runtime_opts_v6.dns[0], v6->gateway); } } @@ -652,7 +659,7 @@ static void fsm_once(dhcpv6_bind_t* b, uint32_t tick_ms) { if (!has_dns) { if (!ipv6_is_unspecified(v6->gateway) && !ipv6_is_multicast(v6->gateway)) { - memcpy(v6->runtime_opts_v6.dns[0], v6->gateway, 16); + ipv6_cpy(v6->runtime_opts_v6.dns[0], v6->gateway); } } @@ -703,7 +710,7 @@ static void fsm_once(dhcpv6_bind_t* b, uint32_t tick_ms) { if (!has_dns) { if (!ipv6_is_unspecified(v6->gateway) && !ipv6_is_multicast(v6->gateway)) { - memcpy(v6->runtime_opts_v6.dns[0], v6->gateway, 16); + ipv6_cpy(v6->runtime_opts_v6.dns[0], v6->gateway); } } @@ -743,15 +750,7 @@ static void fsm_once(dhcpv6_bind_t* b, uint32_t tick_ms) { v6->dhcpv6_state = DHCPV6_S_BOUND; reset_backoff(b); } else if (v6->dhcpv6_state == DHCPV6_S_RELEASING || v6->dhcpv6_state == DHCPV6_S_DECLINING) { - v6->runtime_opts_v6.lease = 0; - v6->runtime_opts_v6.lease_start_time = 0; - - b->t1_left_ms = 0; - b->t2_left_ms = 0; - b->lease_left_ms = 0; - - v6->dhcpv6_state = DHCPV6_S_INIT; - reset_backoff(b); + reset_lease_state(v6, b); } } } diff --git a/kernel/networking/application_layer/dns/dns_daemon.c b/kernel/networking/application_layer/dns/dns_daemon.c index 16e2cc52..d06852cf 100644 --- a/kernel/networking/application_layer/dns/dns_daemon.c +++ b/kernel/networking/application_layer/dns/dns_daemon.c @@ -48,7 +48,7 @@ static void mdns_open_sockets(const uint8_t *group4, const uint8_t *group6) { } if (have_socket) continue; - SocketExtraOptions opt; + SocketOptions opt; memset(&opt, 0, sizeof(opt)); opt.flags = SOCK_OPT_TTL; opt.ttl = 255; @@ -92,7 +92,7 @@ static void mdns_open_sockets(const uint8_t *group4, const uint8_t *group6) { } if (have_socket) continue; - SocketExtraOptions opt; + SocketOptions opt; memset(&opt, 0, sizeof(opt)); opt.flags = SOCK_OPT_TTL; opt.ttl = 255; diff --git a/kernel/networking/application_layer/ssdp_daemon.c b/kernel/networking/application_layer/ssdp_daemon.c index c7ee5c63..f553dbf2 100644 --- a/kernel/networking/application_layer/ssdp_daemon.c +++ b/kernel/networking/application_layer/ssdp_daemon.c @@ -94,7 +94,7 @@ int ssdp_daemon_entry(int argc, char* argv[]) { if (!ipv4_l3_is_ready(v4) || v4->is_localhost) continue; uint32_t ttl = 2; - SocketExtraOptions opt; + SocketOptions opt; memset(&opt, 0, sizeof(opt)); opt.flags = SOCK_OPT_TTL; opt.ttl = (uint8_t)ttl; @@ -130,7 +130,7 @@ int ssdp_daemon_entry(int argc, char* argv[]) { if (!ipv6_l3_is_ready(v6) || v6->is_localhost) continue; uint32_t ttl = 2; - SocketExtraOptions opt; + SocketOptions opt; memset(&opt, 0, sizeof(opt)); opt.flags = SOCK_OPT_TTL; opt.ttl = (uint8_t)ttl; diff --git a/kernel/networking/drivers/net_driver.hpp b/kernel/networking/drivers/net_driver.hpp index 6f259baa..1a206d41 100644 --- a/kernel/networking/drivers/net_driver.hpp +++ b/kernel/networking/drivers/net_driver.hpp @@ -16,9 +16,8 @@ class NetDriver { virtual bool init_at(uint64_t pci_addr, uint32_t irq_base_vector) = 0; virtual netpkt_t* handle_receive_packet() = 0; virtual void handle_sent_packet() {} - //TODO this looks like a violation of the single responsibility principle - virtual void flush_rx(){} - virtual void flush_tx(){} + virtual void complete_rx_batch(){} + virtual void complete_tx_batch(){} virtual void enable_verbose() = 0; virtual netdev_tx_result_t send_packet(netpkt_t* packet) = 0; virtual void get_mac(uint8_t out_mac[6]) 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 c6191803..158ef87a 100644 --- a/kernel/networking/drivers/virtio_net_pci/virtio_net_pci.cpp +++ b/kernel/networking/drivers/virtio_net_pci/virtio_net_pci.cpp @@ -354,7 +354,7 @@ netpkt_t* VirtioNetDriver::handle_receive_packet(){ return pkt; } -void VirtioNetDriver::flush_rx() { +void VirtioNetDriver::complete_rx_batch() { irq_flags_t flags = irq_save_disable(); bool pending = rx_notify_pending; rx_notify_pending = false; @@ -434,7 +434,7 @@ netdev_tx_result_t VirtioNetDriver::send_packet(netpkt_t* packet){ return NETDEV_TX_OK; } -void VirtioNetDriver::flush_tx() { +void VirtioNetDriver::complete_tx_batch() { irq_flags_t flags = irq_save_disable(); bool pending = tx_notify_pending; tx_notify_pending = false; 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 2ba0919e..4c48b1ea 100644 --- a/kernel/networking/drivers/virtio_net_pci/virtio_net_pci.hpp +++ b/kernel/networking/drivers/virtio_net_pci/virtio_net_pci.hpp @@ -65,8 +65,8 @@ class VirtioNetDriver : public NetDriver { netpkt_t* handle_receive_packet() override; void handle_sent_packet() override; - void flush_rx() override; - void flush_tx() override; + void complete_rx_batch() override; + void complete_tx_batch() override; netdev_tx_result_t send_packet(netpkt_t* packet) override; private: diff --git a/kernel/networking/interface_manager.c b/kernel/networking/interface_manager.c index d4c5f4ed..4eb48154 100644 --- a/kernel/networking/interface_manager.c +++ b/kernel/networking/interface_manager.c @@ -18,6 +18,7 @@ static l2_interface_t g_l2[MAX_L2_INTERFACES]; static uint8_t g_l2_used[MAX_L2_INTERFACES]; static uint8_t g_l2_count = 0; +static uint32_t g_if_epoch = 1; typedef struct { l3_ipv4_interface_t node; @@ -33,6 +34,12 @@ typedef struct { static v4_slot_t g_v4[MAX_IPV4_L3_INTERFACES]; static v6_slot_t g_v6[MAX_IPV6_L3_INTERFACES]; +static uint32_t net_interface_mark_changed(void) { + g_if_epoch++; + if (!g_if_epoch) g_if_epoch = 1; + return g_if_epoch; +} + static inline int l2_slot_from_ifindex(uint8_t ifindex){ if (!ifindex) return -1; int s = (int)ifindex - 1; @@ -85,6 +92,7 @@ uint8_t l2_interface_create(const char *name, void *driver_ctx, uint16_t base_me l2_interface_t* itf = &g_l2[slot]; memset(itf, 0, sizeof(*itf)); itf->ifindex = (uint8_t)(slot + 1); + net_interface_mark_changed(); if (name) strncpy(itf->name, name, sizeof(itf->name)); @@ -122,6 +130,7 @@ bool l2_interface_destroy(uint8_t ifindex){ memset(&g_l2[slot], 0, sizeof(l2_interface_t)); g_l2_used[slot] = 0; if (g_l2_count) g_l2_count -= 1; + net_interface_mark_changed(); return true; } @@ -146,7 +155,9 @@ l2_interface_t* l2_interface_at(uint8_t idx) { bool l2_interface_set_up(uint8_t ifindex, bool up) { l2_interface_t* itf = l2_interface_find_by_index(ifindex); if (!itf) return false; + if (itf->is_up == up) return true; itf->is_up = up; + net_interface_mark_changed(); return true; } @@ -365,9 +376,11 @@ uint8_t l3_ipv4_add_to_interface(uint8_t ifindex, uint32_t ip, uint32_t mask, ui memset(&n->runtime_opts_v4, 0, sizeof(n->runtime_opts_v4)); if (runtime_opts) n->runtime_opts_v4 = *runtime_opts; + n->is_localhost = (l2->kind == NET_IFK_LOCALHOST); + n->l3_id = make_l3_id_v4(l2->ifindex, (uint8_t)loc); n->routing_table = NULL; - if (l2->kind != NET_IFK_LOCALHOST) { - n->routing_table = ipv4_rt_create(); + if (!n->is_localhost) { + n->routing_table = ipv4_rt_create(n->l3_id); if (!n->routing_table) { g_v4[g].used = false; memset(&g_v4[g], 0, sizeof(g_v4[g])); @@ -376,8 +389,7 @@ uint8_t l3_ipv4_add_to_interface(uint8_t ifindex, uint32_t ip, uint32_t mask, ui ipv4_rt_ensure_basics((ipv4_rt_table_t*)n->routing_table, n->ip, n->mask, n->gw, l2->base_metric); } - n->is_localhost = (l2->kind == NET_IFK_LOCALHOST); - n->l3_id = make_l3_id_v4(l2->ifindex, (uint8_t)loc); + n->epoch = net_interface_mark_changed(); l2->l3_v4[loc] = n; l2->ipv4_count++; @@ -418,6 +430,16 @@ bool l3_ipv4_update(uint8_t l3_id, uint32_t ip, uint32_t mask, uint32_t gw, ipv4 } } + uint32_t old_ip = n->ip; + uint32_t old_mask = n->mask; + + bool l3_changed = n->mode != mode; + if (mode == IPV4_CFG_STATIC || mode == IPV4_CFG_DHCP) { + if (n->ip != ip || n->mask != mask) l3_changed = true; + } else if (n->ip || n->mask) { + l3_changed = true; + } + n->mode = mode; if (runtime_opts) n->runtime_opts_v4 = *runtime_opts; @@ -436,8 +458,17 @@ bool l3_ipv4_update(uint8_t l3_id, uint32_t ip, uint32_t mask, uint32_t gw, ipv4 } if (l2->kind != NET_IFK_LOCALHOST) { - if (!n->routing_table) n->routing_table = ipv4_rt_create(); - if (n->routing_table) ipv4_rt_sync_basics((ipv4_rt_table_t*)n->routing_table, n->ip, n->mask, n->gw, l2->base_metric); + if (!n->routing_table) n->routing_table = ipv4_rt_create(n->l3_id); + if (n->routing_table) { + if (old_ip && old_mask) { + uint32_t old_net = old_ip & old_mask; + uint32_t new_net = (n->ip && n->mask) ? (n->ip & n->mask) : 0; + if (!n->ip || !n->mask || old_mask != n->mask || old_net != new_net) { + ipv4_rt_del_in((ipv4_rt_table_t*)n->routing_table, old_net, old_mask); + } + } + ipv4_rt_sync_basics((ipv4_rt_table_t*)n->routing_table, n->ip, n->mask, n->gw, l2->base_metric); + } } else { if (n->routing_table) { ipv4_rt_destroy((ipv4_rt_table_t*)n->routing_table); @@ -446,6 +477,7 @@ bool l3_ipv4_update(uint8_t l3_id, uint32_t ip, uint32_t mask, uint32_t gw, ipv4 } if (l2->kind != NET_IFK_LOCALHOST && l2_has_active_v4(l2)) for (int i = 0; i < (int)l2->ipv4_mcast_count; ++i) igmp_send_join(l2->ifindex, l2->ipv4_mcast[i]); + if (l3_changed) n->epoch = net_interface_mark_changed(); return true; } @@ -467,6 +499,7 @@ bool l3_ipv4_remove_from_interface(uint8_t 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--; + net_interface_mark_changed(); } if (n->routing_table) { @@ -617,6 +650,7 @@ uint8_t l3_ipv6_add_to_interface(uint8_t ifindex, const uint8_t ip[16], uint8_t } n->l3_id = make_l3_id_v6(l2->ifindex, (uint8_t)loc); + n->epoch = net_interface_mark_changed(); l2->l3_v6[loc] = n; l2->ipv6_count++; @@ -627,7 +661,7 @@ uint8_t l3_ipv6_add_to_interface(uint8_t ifindex, const uint8_t ip[16], uint8_t } n->routing_table = NULL; if (!n->is_localhost) { - n->routing_table = ipv6_rt_create(); + n->routing_table = ipv6_rt_create(n->l3_id); if (n->routing_table){ ipv6_rt_ensure_basics((ipv6_rt_table_t*)n->routing_table, n->ip, n->prefix_len, n->gateway, l2->base_metric); } @@ -654,6 +688,7 @@ bool l3_ipv6_update(uint8_t l3_id, const uint8_t ip[16], uint8_t prefix_len, con if (prefix_len > 128) return false; if (kind == n->kind && cfg == n->cfg && prefix_len == n->prefix_len && ipv6_cmp(ip, n->ip) == 0 && ipv6_cmp(gw, n->gateway) == 0) return true; + bool l3_changed = kind != n->kind || cfg != n->cfg || prefix_len != n->prefix_len || ipv6_cmp(ip, n->ip) != 0; if ((n->kind & IPV6_ADDRK_LINK_LOCAL) && cfg == IPV6_CFG_DISABLE){ for (int i=0;iip); + uint8_t old_prefix_len = n->prefix_len; n->cfg = cfg; n->kind = kind; @@ -759,8 +795,17 @@ bool l3_ipv6_update(uint8_t l3_id, const uint8_t ip[16], uint8_t prefix_len, con (void)l2_ipv6_mcast_join(l2->ifindex, sn); } - if (!n->routing_table) n->routing_table = ipv6_rt_create(); + if (!n->routing_table) n->routing_table = ipv6_rt_create(n->l3_id); if (n->routing_table){ + if (old_prefix_len && !ipv6_is_unspecified(old_ip)) { + uint8_t old_net[16]; + uint8_t new_net[16]; + ipv6_prefix_network(old_ip, old_prefix_len, old_net); + if (n->prefix_len && !ipv6_is_unspecified(n->ip)) ipv6_prefix_network(n->ip, n->prefix_len, new_net); + if (!n->prefix_len || ipv6_is_unspecified(n->ip) || old_prefix_len != n->prefix_len || ipv6_cmp(old_net, new_net) != 0) { + ipv6_rt_del_in((ipv6_rt_table_t*)n->routing_table, old_net, old_prefix_len); + } + } ipv6_rt_sync_basics((ipv6_rt_table_t*)n->routing_table, n->ip, n->prefix_len, n->gateway, l2->base_metric); } } else { @@ -771,6 +816,7 @@ bool l3_ipv6_update(uint8_t l3_id, const uint8_t ip[16], uint8_t prefix_len, con } if (l2->kind != NET_IFK_LOCALHOST && l2_has_active_v6(l2)) for (int i = 0; i < (int)l2->ipv6_mcast_count; ++i) mld_send_join(l2->ifindex, l2->ipv6_mcast[i]); + if (l3_changed) n->epoch = net_interface_mark_changed(); return true; } @@ -800,6 +846,7 @@ bool l3_ipv6_remove_from_interface(uint8_t 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--; + net_interface_mark_changed(); } if (n->routing_table){ @@ -831,6 +878,7 @@ bool l3_ipv6_set_enabled(uint8_t l3_id, bool enable){ n->dad_state = IPV6_DAD_NONE; n->dad_probes_sent = 0; n->dad_timer_ms = 0; + n->epoch = net_interface_mark_changed(); return true; } } diff --git a/kernel/networking/interface_manager.h b/kernel/networking/interface_manager.h index b6474a11..c2792131 100644 --- a/kernel/networking/interface_manager.h +++ b/kernel/networking/interface_manager.h @@ -79,6 +79,7 @@ typedef struct l2_interface { typedef struct l3_ipv4_interface { uint8_t l3_id; + uint32_t epoch; uint32_t ip; uint32_t mask; uint32_t gw; @@ -113,6 +114,7 @@ typedef struct net_runtime_opts_v6 { typedef struct l3_ipv6_interface { uint8_t l3_id; + uint32_t epoch; uint16_t mtu; uint8_t ip[16]; diff --git a/kernel/networking/internet_layer/ipv4_route.c b/kernel/networking/internet_layer/ipv4_route.c index c10fce28..28eb4761 100644 --- a/kernel/networking/internet_layer/ipv4_route.c +++ b/kernel/networking/internet_layer/ipv4_route.c @@ -3,57 +3,41 @@ #include "networking/interface_manager.h" #include "networking/internet_layer/ipv4_utils.h" #include "syscalls/syscalls.h" -//TODO see ipv6_route.c + struct ipv4_rt_table { + uint8_t owner_l3_id; + uint32_t epoch; ipv4_rt_entry_t e[IPV4_RT_PER_IF_MAX]; int len; }; -static uint32_t ipv4_route_epoch(void) { - uint32_t h = 0x811C9DC5; - uint8_t cnt = l2_interface_count(); - h = (h ^ cnt) * 16777619; - for (uint8_t i = 0; i < cnt; ++i) { - l2_interface_t* l2 = l2_interface_at(i); - if (!l2) continue; - h = (h ^ l2->ifindex) * 16777619; - h = (h ^ (uint32_t)l2->is_up) * 16777619; - h = (h ^ l2->base_metric) * 16777619; - h = (h ^ l2->ipv4_count) * 16777619; - for (int s = 0; s < MAX_IPV4_PER_INTERFACE; ++s) { - l3_ipv4_interface_t* v4 = l2->l3_v4[s]; - if (!v4) continue; - h = (h ^ v4->l3_id) * 16777619; - h = (h ^ v4->ip) * 16777619; - h = (h ^ v4->mask) * 16777619; - h = (h ^ v4->gw) * 16777619; - h = (h ^ (uint32_t)v4->mode) * 16777619; - h = (h ^ (uint32_t)(uintptr_t)v4->routing_table) * 16777619; - } - } - return h ? h : 1; +static void ipv4_rt_bump(ipv4_rt_table_t* t) { + if (!t) return; + t->epoch++; + if (!t->epoch) t->epoch = 1; } bool ipv4_tx_plan_valid(const ipv4_tx_plan_t* plan) { - if (!plan || !plan->l3_id || !plan->net_epoch) return false; - if (plan->net_epoch != ipv4_route_epoch()) return false; + if (!plan || !plan->l3_id) return false; l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(plan->l3_id); if (!ipv4_l3_is_ready(v4)) return false; + if (v4->epoch != plan->l3_epoch) return false; return v4->ip == plan->src_ip; } bool ipv4_build_tx_plan(uint32_t dst, const ip_tx_opts_t* hint, ipv4_tx_plan_t* out){ if (!out) return false; out->l3_id = 0; + out->l3_epoch = 0; out->src_ip = 0; - out->net_epoch = ipv4_route_epoch(); if (hint && hint->scope == IP_TX_BOUND_L3) { uint8_t id = hint->index; l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(id); if (!ipv4_l3_is_ready(v4) || ipv4_is_loopback(dst) != v4->is_localhost) return false; out->l3_id = id; + out->l3_epoch = v4->epoch; out->src_ip = v4->ip; return true; } @@ -91,14 +75,17 @@ bool ipv4_build_tx_plan(uint32_t dst, const ip_tx_opts_t* hint, ipv4_tx_plan_t* if (!ipv4_l3_is_ready(v4) || ipv4_is_loopback(dst) != v4->is_localhost) return false; out->l3_id = chosen; + out->l3_epoch = v4->epoch; out->src_ip = v4->ip; return true; } -ipv4_rt_table_t* ipv4_rt_create(void) { +ipv4_rt_table_t* ipv4_rt_create(uint8_t owner_l3_id) { ipv4_rt_table_t* t = (ipv4_rt_table_t*)zalloc(sizeof(ipv4_rt_table_t)); if (!t) return 0; + t->owner_l3_id = owner_l3_id; + t->epoch = 1; return t; } @@ -109,21 +96,26 @@ void ipv4_rt_destroy(ipv4_rt_table_t* t) { void ipv4_rt_clear(ipv4_rt_table_t* t) { if (!t) return; + bool changed = t->len != 0; t->len = 0; memset(t->e, 0, sizeof(t->e)); + if (changed) ipv4_rt_bump(t); } 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) { + if (t->e[i].gateway == gateway && t->e[i].metric == metric) return true; t->e[i].gateway = gateway; t->e[i].metric = metric; + ipv4_rt_bump(t); return true; } } if (t->len >= IPV4_RT_PER_IF_MAX) return false; t->e[t->len++] = (ipv4_rt_entry_t){ network, mask, gateway, metric }; + ipv4_rt_bump(t); return true; } @@ -133,6 +125,7 @@ bool ipv4_rt_del_in(ipv4_rt_table_t* t, uint32_t network, uint32_t mask) { 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])); + ipv4_rt_bump(t); return true; } } @@ -189,6 +182,7 @@ void ipv4_rt_sync_basics(ipv4_rt_table_t* t, uint32_t ip, uint32_t mask, uint32_ (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; @@ -204,18 +198,20 @@ bool ipv4_rt_pick_best_l3_in(const uint8_t* l3_ids, int n_ids, uint32_t dst, uin } int pl_tab = -1, met_tab = 0x7FFF; if (x->routing_table){ + const ipv4_rt_table_t* rt = (const ipv4_rt_table_t*)x->routing_table; + if (rt->owner_l3_id && rt->owner_l3_id != x->l3_id) continue; 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)){ + if (ipv4_rt_lookup_in(rt, 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)){ + if (pl_tab > cand_pl || (pl_tab == cand_pl && met_tab < cand_cost)){ cand_pl = pl_tab; - cand_cost = l2base + met_tab; + cand_cost = 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; diff --git a/kernel/networking/internet_layer/ipv4_route.h b/kernel/networking/internet_layer/ipv4_route.h index 507f5481..45e49487 100644 --- a/kernel/networking/internet_layer/ipv4_route.h +++ b/kernel/networking/internet_layer/ipv4_route.h @@ -17,7 +17,7 @@ typedef struct { typedef struct ipv4_rt_table ipv4_rt_table_t; -ipv4_rt_table_t* ipv4_rt_create(void); +ipv4_rt_table_t* ipv4_rt_create(uint8_t owner_l3_id); void ipv4_rt_destroy(ipv4_rt_table_t* t); void ipv4_rt_clear(ipv4_rt_table_t* t); @@ -31,8 +31,8 @@ void ipv4_rt_sync_basics(ipv4_rt_table_t* t, uint32_t ip, uint32_t mask, uint32_ typedef struct { uint8_t l3_id; + uint32_t l3_epoch; uint32_t src_ip; - uint32_t net_epoch; } ipv4_tx_plan_t; bool ipv4_tx_plan_valid(const ipv4_tx_plan_t* plan); diff --git a/kernel/networking/internet_layer/ipv6_route.c b/kernel/networking/internet_layer/ipv6_route.c index 4914df41..858077e4 100644 --- a/kernel/networking/internet_layer/ipv6_route.c +++ b/kernel/networking/internet_layer/ipv6_route.c @@ -6,10 +6,18 @@ #include "syscalls/syscalls.h" struct ipv6_rt_table { + uint8_t owner_l3_id; + uint32_t epoch; ipv6_rt_entry_t e[IPV6_RT_PER_IF_MAX]; int len; }; +static void ipv6_rt_bump(ipv6_rt_table_t* t) { + if (!t) return; + t->epoch++; + if (!t->epoch) t->epoch = 1; +} + static bool v6_l3_ok_for_tx(l3_ipv6_interface_t* v6, int dst_is_ll, int dst_is_loop) { if (!ipv6_l3_is_ready(v6)) return false; if (v6->is_localhost && !dst_is_loop) return false; @@ -18,48 +26,20 @@ static bool v6_l3_ok_for_tx(l3_ipv6_interface_t* v6, int dst_is_ll, int dst_is_l if (src_is_ll != dst_is_ll) return false; return true; } -//TODO the epoch system is terrible, use interface events when avaible, same for ipv4 -static uint32_t ipv6_route_epoch(void) { - uint32_t h = 0x811C9DC5; - uint8_t cnt = l2_interface_count(); - h = (h ^ cnt) * 16777619; - for (uint8_t i = 0; i < cnt; ++i) { - l2_interface_t* l2 = l2_interface_at(i); - if (!l2) continue; - h = (h ^ l2->ifindex) * 16777619; - h = (h ^ (uint32_t)l2->is_up) * 16777619; - h = (h ^ l2->base_metric) * 16777619; - h = (h ^ l2->ipv6_count) * 16777619; - for (int s = 0; s < MAX_IPV6_PER_INTERFACE; ++s) { - l3_ipv6_interface_t* v6 = l2->l3_v6[s]; - if (!v6) continue; - h = (h ^ v6->l3_id) * 16777619; - h = (h ^ v6->prefix_len) * 16777619; - h = (h ^ (uint32_t)v6->cfg) * 16777619; - h = (h ^ (uint32_t)v6->kind) * 16777619; - h = (h ^ (uint32_t)v6->dad_state) * 16777619; - h = (h ^ (uint32_t)(uintptr_t)v6->routing_table) * 16777619; - for (int b = 0; b < 16; ++b) h = (h ^ v6->ip[b]) * 16777619; - for (int b = 0; b < 16; ++b) h = (h ^ v6->gateway[b]) * 16777619; - } - } - return h ? h : 1; -} bool ipv6_tx_plan_valid(const ipv6_tx_plan_t* plan) { - if (!plan || !plan->l3_id || !plan->net_epoch) return false; - if (plan->net_epoch != ipv6_route_epoch()) return false; + if (!plan || !plan->l3_id) return false; l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(plan->l3_id); if (!ipv6_l3_is_ready(v6)) return false; - return memcmp(v6->ip, plan->src_ip, 16) == 0; + if (v6->epoch != plan->l3_epoch) return false; + return ipv6_cmp(v6->ip, plan->src_ip) == 0; } bool ipv6_build_tx_plan(const uint8_t dst[16], const ip_tx_opts_t* hint, ipv6_tx_plan_t* out) { if (!dst || !out) return false; memset(out, 0, sizeof(*out)); - out->net_epoch = ipv6_route_epoch(); int dst_is_ll = (ipv6_is_linklocal(dst) || ipv6_is_linkscope_mcast(dst)) ? 1 : 0; int dst_is_loop = ipv6_is_loopback(dst) ? 1 : 0; @@ -69,7 +49,8 @@ bool ipv6_build_tx_plan(const uint8_t dst[16], const ip_tx_opts_t* hint, ipv6_tx l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(id); if (!v6_l3_ok_for_tx(v6, dst_is_ll, dst_is_loop)) return false; out->l3_id = id; - memcpy(out->src_ip, v6->ip, 16); + out->l3_epoch = v6->epoch; + ipv6_cpy(out->src_ip, v6->ip); return true; } @@ -106,14 +87,17 @@ bool ipv6_build_tx_plan(const uint8_t dst[16], const ip_tx_opts_t* hint, ipv6_tx if (!v6_l3_ok_for_tx(v6, dst_is_ll, dst_is_loop)) return false; out->l3_id = chosen; - memcpy(out->src_ip, v6->ip, 16); + out->l3_epoch = v6->epoch; + ipv6_cpy(out->src_ip, v6->ip); return true; } -ipv6_rt_table_t* ipv6_rt_create(void) { +ipv6_rt_table_t* ipv6_rt_create(uint8_t owner_l3_id) { ipv6_rt_table_t* t = zalloc(sizeof(*t)); if (!t) return 0; + t->owner_l3_id = owner_l3_id; + t->epoch = 1; return t; } @@ -125,29 +109,33 @@ void ipv6_rt_destroy(ipv6_rt_table_t* t) { void ipv6_rt_clear(ipv6_rt_table_t* t) { if (!t) return; - + bool changed = t->len != 0; t->len = 0; memset(t->e, 0, sizeof(t->e)); + if (changed) ipv6_rt_bump(t); } bool ipv6_rt_add_in(ipv6_rt_table_t* t, const uint8_t net[16], uint8_t plen, const uint8_t gw[16], uint16_t metric) { if (!t) return false; for (int i = 0; i < t->len; i++) { - if (t->e[i].prefix_len == plen && memcmp(t->e[i].network, net, 16) == 0) { - memcpy(t->e[i].gateway, gw, 16); + if (t->e[i].prefix_len == plen && ipv6_cmp(t->e[i].network, net) == 0) { + if (ipv6_cmp(t->e[i].gateway, gw) == 0 && t->e[i].metric == metric) return true; + ipv6_cpy(t->e[i].gateway, gw); t->e[i].metric = metric; + ipv6_rt_bump(t); return true; } } if (t->len >= IPV6_RT_PER_IF_MAX) return false; - memcpy(t->e[t->len].network, net, 16); - memcpy(t->e[t->len].gateway, gw, 16); + ipv6_cpy(t->e[t->len].network, net); + ipv6_cpy(t->e[t->len].gateway, gw); t->e[t->len].prefix_len = plen; t->e[t->len].metric = metric; t->len++; + ipv6_rt_bump(t); return true; } @@ -156,9 +144,10 @@ bool ipv6_rt_del_in(ipv6_rt_table_t* t, const uint8_t net[16], uint8_t plen) { if (!t) return false; for (int i = 0; i < t->len; i++) { - if (t->e[i].prefix_len == plen && memcmp(t->e[i].network, net, 16) == 0) { + if (t->e[i].prefix_len == plen && ipv6_cmp(t->e[i].network, net) == 0) { t->e[i] = t->e[--t->len]; memset(&t->e[t->len], 0, sizeof(t->e[0])); + ipv6_rt_bump(t); return true; } } @@ -187,13 +176,13 @@ bool ipv6_rt_lookup_in(const ipv6_rt_table_t* t, const uint8_t dst[16], uint8_t if (pl > best_pl || (pl == best_pl && met < best_metric)) { best_pl = pl; best_metric = met; - memcpy(best_gw, t->e[i].gateway, 16); + ipv6_cpy(best_gw, t->e[i].gateway); } } if (best_pl < 0) return false; - if (next_hop) memcpy(next_hop, best_gw, 16); + if (next_hop) ipv6_cpy(next_hop, best_gw); if (out_pl) *out_pl =best_pl; if (out_metric) *out_metric = best_metric; @@ -210,18 +199,15 @@ void ipv6_rt_ensure_basics(ipv6_rt_table_t* t, const uint8_t ip[16], uint8_t ple } if (gw && !ipv6_is_unspecified(gw)) { - uint8_t z[16] = {0}; - ipv6_rt_add_in(t, z, 0, gw, (uint16_t)(base_metric + 1)); + ipv6_rt_add_in(t, (const uint8_t[16]){0}, 0, gw, (uint16_t)(base_metric + 1)); } } void ipv6_rt_sync_basics(ipv6_rt_table_t* t, const uint8_t ip[16], uint8_t plen, const uint8_t gw[16], uint16_t base_metric) { if (!t) return; - uint8_t z[16] = {0}; - - if (gw && !ipv6_is_unspecified(gw)) ipv6_rt_add_in(t, z, 0,gw, (uint16_t)(base_metric + 1)); - else ipv6_rt_del_in(t, z, 0); + if (gw && !ipv6_is_unspecified(gw)) ipv6_rt_add_in(t, (const uint8_t[16]){0}, 0,gw, (uint16_t)(base_metric + 1)); + else ipv6_rt_del_in(t, (const uint8_t[16]){0}, 0); if (ip && plen && !ipv6_is_unspecified(ip)) { uint8_t net[16]; @@ -251,11 +237,13 @@ bool ipv6_rt_pick_best_l3_in(const uint8_t* l3_ids, int n_ids, const uint8_t dst int met_tab = 0x7FFF; if (x->routing_table) { + const ipv6_rt_table_t* rt = (const ipv6_rt_table_t*)x->routing_table; + if (rt->owner_l3_id && rt->owner_l3_id != x->l3_id) continue; uint8_t via[16] = {0}; int out_pl = -1; int out_met = 0x7FFF; - if (ipv6_rt_lookup_in((const ipv6_rt_table_t*)x->routing_table, dst, via, &out_pl, &out_met)) { + if (ipv6_rt_lookup_in(rt, dst, via, &out_pl, &out_met)) { pl_tab = out_pl; met_tab = out_met; } @@ -264,12 +252,12 @@ bool ipv6_rt_pick_best_l3_in(const uint8_t* l3_ids, int n_ids, const uint8_t dst int cand_pl = pl_conn; int cand_cost = l2base; - if (pl_tab > cand_pl || (pl_tab == cand_pl && l2base + met_tab < cand_cost)) { + if (pl_tab > cand_pl || (pl_tab == cand_pl && met_tab < cand_cost)) { cand_pl = pl_tab; - cand_cost = l2base + met_tab; + cand_cost = met_tab; } - if (cand_pl > best_pl || (cand_pl == best_pl && cand_cost best_pl || (cand_pl == best_pl && cand_cost dhcpv6_stateless_done = 0; if (v6->cfg != IPV6_CFG_DHCPV6 || ipv6_is_placeholder_gua(v6->ip)) { - uint8_t z[16] = {0}; - (void)l3_ipv6_update(v6->l3_id, z, 0, gw, IPV6_CFG_DHCPV6, v6->kind); + (void)l3_ipv6_update(v6->l3_id, (const uint8_t[16]){0}, 0, gw, IPV6_CFG_DHCPV6, v6->kind); } else { (void)l3_ipv6_update(v6->l3_id, v6->ip, v6->prefix_len, gw, IPV6_CFG_DHCPV6, v6->kind); } @@ -312,12 +311,16 @@ static void ndp_on_ra(uint8_t ifindex, const uint8_t router_ip[16], uint16_t rou ipv6_cpy(slot->prefix, prefix); - if (slot->ra_is_default && router_ip) ipv6_cpy(slot->gateway, router_ip); - else memset(slot->gateway, 0, 16); + uint8_t gw[16]; + if (slot->ra_is_default && router_ip) ipv6_cpy(gw, router_ip); + else memset(gw, 0, sizeof(gw)); slot->valid_lifetime = valid_lft; slot->preferred_lifetime = preferred_lft; + l3_ipv6_update(slot->l3_id, slot->ip, slot->prefix_len, gw, slot->cfg, slot->kind); + apply_ra_policy(now_ms, l2); + if (ipv6_is_unspecified(slot->ip)) slot->timestamp_created = now_ms; if(!ipv6_is_placeholder_gua(slot->ip) && !ipv6_is_unspecified(slot->ip)) memcpy(slot->interface_id, slot->ip + 8, 8); @@ -360,7 +363,7 @@ static int ndp_find_slot(ndp_table_impl_t* t, const uint8_t ip[16]) { for (int i = 0; i < NDP_TABLE_MAX; i++) { if (!t->entries[i].ttl_ms) continue; - if (memcmp(t->entries[i].ip, ip, 16) == 0) return i; + if (ipv6_cmp(t->entries[i].ip, ip) == 0) return i; } return -1; @@ -404,8 +407,8 @@ void ndp_table_put_for_l2(uint8_t ifindex, const uint8_t ip[16], const uint8_t m if (idx < 0) return; ndp_entry_t* e = &t->entries[idx]; - if (e->state != NDP_STATE_UNUSED && memcmp(e->ip, ip, 16) != 0) ndp_entry_clear(e); - memcpy(e->ip, ip, 16); + if (e->state != NDP_STATE_UNUSED && ipv6_cmp(e->ip, ip) != 0) ndp_entry_clear(e); + ipv6_cpy(e->ip, ip); if (mac) { memcpy(e->mac, mac, 6); @@ -445,7 +448,7 @@ static bool ndp_table_get_for_l2(uint8_t ifindex, const uint8_t ip[16], uint8_t if (!e->ttl_ms) continue; if (e->state == NDP_STATE_UNUSED) continue; if (e->state == NDP_STATE_INCOMPLETE) continue; - if (memcmp(e->ip, ip, 16) != 0) continue; + if (ipv6_cmp(e->ip, ip) != 0) continue; memcpy(mac_out, e->mac, 6); return true; @@ -469,7 +472,7 @@ static bool ndp_send_na_on(uint8_t ifindex, const uint8_t dst_ip[16], const uint flags |= (1u << 29); na->flags = bswap32(flags); - memcpy(na->target, target_ip, 16); + ipv6_cpy(na->target, target_ip); icmpv6_opt_lladdr_t* opt = (icmpv6_opt_lladdr_t*)(buf + sizeof(icmpv6_na_t)); opt->type = 2; @@ -500,7 +503,7 @@ static void ndp_send_ns_on(uint8_t ifindex, const uint8_t target_ip[16], const u ns->hdr.checksum = 0; ns->rsv = 0; - memcpy(ns->target, target_ip, 16); + ipv6_cpy(ns->target, target_ip); if (!dad) { icmpv6_opt_lladdr_t* opt = (icmpv6_opt_lladdr_t*)(buf + sizeof(icmpv6_ns_t)); @@ -587,12 +590,12 @@ static void ndp_send_probe(uint8_t ifindex, ndp_entry_t* e) { if (!ipv6_l3_is_ready(v6)) continue; if (ipv6_is_linklocal(v6->ip)) { - memcpy(src_ip, v6->ip, 16); + ipv6_cpy(src_ip, v6->ip); break; } if (ipv6_is_unspecified(src_ip) && !ipv6_is_unspecified(v6->ip)) - memcpy(src_ip, v6->ip, 16); + ipv6_cpy(src_ip, v6->ip); } } @@ -715,7 +718,7 @@ bool ndp_send_or_queue_on(uint16_t ifindex, const uint8_t next_hop[16], netpkt_t } ndp_entry_t* e = &t->entries[idx]; - if (e->state != NDP_STATE_UNUSED && memcmp(e->ip, next_hop, 16) != 0) ndp_entry_clear(e); + if (e->state != NDP_STATE_UNUSED && ipv6_cmp(e->ip, next_hop) != 0) ndp_entry_clear(e); uint32_t len = netpkt_len(pkt); if (e->pending_len >= NDP_PENDING_MAX || e->pending_bytes + len > NDP_PENDING_MAX_BYTES) { @@ -731,7 +734,7 @@ bool ndp_send_or_queue_on(uint16_t ifindex, const uint8_t next_hop[16], netpkt_t } } - memcpy(e->ip, next_hop, 16); + ipv6_cpy(e->ip, next_hop); memset(e->mac, 0, 6); e->ttl_ms = g_ndp_reachable_time_ms * 4; e->is_router = 0; @@ -908,13 +911,13 @@ void ndp_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[ if (idx < 0) return; ndp_entry_t* e = &t->entries[idx]; - if (e->state != NDP_STATE_UNUSED && memcmp(e->ip, na.target, 16) != 0) ndp_entry_clear(e); + if (e->state != NDP_STATE_UNUSED && ipv6_cmp(e->ip, na.target) != 0) ndp_entry_clear(e); uint8_t old_mac[6]; memcpy(old_mac, e->mac, 6); if (e->ttl_ms == 0 && e->state == NDP_STATE_UNUSED) { - memcpy(e->ip, na.target, 16); + ipv6_cpy(e->ip, na.target); memcpy(e->mac, src_mac, 6); e->ttl_ms = g_ndp_reachable_time_ms * 4; e->probes_sent = 0; @@ -1014,7 +1017,7 @@ void ndp_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[ uint32_t pref_lft = bswap32(pio.preferred_lifetime); uint8_t pfx[16]; - memcpy(pfx, pio.prefix, 16); + ipv6_cpy(pfx, pio.prefix); if (pfx_len != 0) ndp_on_ra((uint8_t)ifindex, src_ip, router_lifetime, pfx, pfx_len, valid_lft, pref_lft, autonomous, ra.flags); } else if (opt_type == 5 && opt_size >= (uint32_t)sizeof(ndp_opt_mtu_t)) { @@ -1087,10 +1090,10 @@ void ndp_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[ } if (slot) { - if (addr_count >= 1) memcpy(slot->runtime_opts_v6.dns[0], a0, 16); + if (addr_count >= 1) ipv6_cpy(slot->runtime_opts_v6.dns[0], a0); else memset(slot->runtime_opts_v6.dns[0], 0, 16); - if (addr_count >= 2) memcpy(slot->runtime_opts_v6.dns[1], a1, 16); + if (addr_count >= 2) ipv6_cpy(slot->runtime_opts_v6.dns[1], a1); else memset(slot->runtime_opts_v6.dns[1], 0, 16); } } diff --git a/kernel/networking/net_logger/net_logger.c b/kernel/networking/net_logger/net_logger.c index 9ee9c3d5..6bbb3743 100644 --- a/kernel/networking/net_logger/net_logger.c +++ b/kernel/networking/net_logger/net_logger.c @@ -45,7 +45,7 @@ static const char* bind_kind_str(SockBindKind k) { } } -void netlog_socket_event(const SocketExtraOptions* extra, const netlog_socket_event_t* e) { +void netlog_socket_event(const SocketOptions* extra, const netlog_socket_event_t* e) { if (!extra) return; if (!e) return; diff --git a/kernel/networking/net_logger/net_logger.h b/kernel/networking/net_logger/net_logger.h index 2cf5d5e0..b9517df3 100644 --- a/kernel/networking/net_logger/net_logger.h +++ b/kernel/networking/net_logger/net_logger.h @@ -52,7 +52,7 @@ typedef struct netlog_socket_event_t { const char* s1; } netlog_socket_event_t; -void netlog_socket_event(const SocketExtraOptions*extra, const netlog_socket_event_t* e); +void netlog_socket_event(const SocketOptions*extra, const netlog_socket_event_t* e); #ifdef __cplusplus } diff --git a/kernel/networking/network_dispatch.cpp b/kernel/networking/network_dispatch.cpp index 45392771..efc7fe73 100644 --- a/kernel/networking/network_dispatch.cpp +++ b/kernel/networking/network_dispatch.cpp @@ -98,7 +98,7 @@ int NetworkDispatch::net_task() processed++; } - driver->flush_rx(); + driver->complete_rx_batch(); if (processed) did_work = true; } @@ -127,7 +127,7 @@ int NetworkDispatch::net_task() processed++; } if (processed) { - driver->flush_tx(); + driver->complete_tx_batch(); did_work = true; } } diff --git a/kernel/networking/transport_layer/csocket.c b/kernel/networking/transport_layer/csocket.c index 21c29aa5..89c16b05 100644 --- a/kernel/networking/transport_layer/csocket.c +++ b/kernel/networking/transport_layer/csocket.c @@ -9,8 +9,8 @@ #include "data/struct/hashmap.h" #include "alloc/allocate.h" -socket_handle_t create_socket(protocol_t protocol, const SocketExtraOptions* extra){ - SocketExtraOptions default_extra = {}; +socket_handle_t create_socket(protocol_t protocol, const SocketOptions* extra){ + SocketOptions default_extra = {}; if (!extra) extra = &default_extra; if (protocol == PROTO_NONE) { @@ -139,32 +139,6 @@ int32_t get_socket_option(socket_handle_t handle, int32_t opt, void* value, uint return res; } -uint16_t get_socket_local_port(socket_handle_t handle){ - ksocket_t* socket = socket_core_get(handle, get_current_proc_pid()); - if (!socket) return 0; - - uint16_t port = 0; - if (socket_core_protocol(socket) == PROTO_TCP) port = socket_get_local_port_tcp(socket_core_impl(socket)); - else if (socket_core_protocol(socket) == PROTO_UDP) port = socket_get_local_port_udp(socket_core_impl(socket)); - - socket_core_put(socket); - return port; -} - -bool get_socket_remote_endpoint(socket_handle_t handle, net_l4_endpoint* out){ - if (!out) return false; - ksocket_t* socket = socket_core_get(handle, get_current_proc_pid()); - if (!socket) return false; - - bool ok = true; - if (socket_core_protocol(socket) == PROTO_TCP) socket_get_remote_ep_tcp(socket_core_impl(socket), out); - else if (socket_core_protocol(socket) == PROTO_UDP) socket_get_remote_ep_udp(socket_core_impl(socket), out); - else ok = false; - - socket_core_put(socket); - return ok; -} - int32_t close_socket(socket_handle_t handle){ return socket_core_close_handle(handle, get_current_proc_pid()); } diff --git a/kernel/networking/transport_layer/csocket.h b/kernel/networking/transport_layer/csocket.h index 03dfa260..c8027e48 100644 --- a/kernel/networking/transport_layer/csocket.h +++ b/kernel/networking/transport_layer/csocket.h @@ -9,7 +9,7 @@ extern "C" { #endif -socket_handle_t create_socket(protocol_t protocol, const SocketExtraOptions* extra); +socket_handle_t create_socket(protocol_t protocol, const SocketOptions* extra); int32_t bind_socket(socket_handle_t handle, const SockBindSpec* spec, uint16_t port); int32_t connect_socket(socket_handle_t handle, const net_l4_endpoint* dst); @@ -19,8 +19,6 @@ int64_t receive_from_socket(socket_handle_t handle, void* buf, uint64_t len, net int32_t set_socket_option(socket_handle_t handle, int32_t opt, const void* value, uint32_t len); int32_t get_socket_option(socket_handle_t handle, int32_t opt, void* value, uint32_t* len); -uint16_t get_socket_local_port(socket_handle_t handle); -bool get_socket_remote_endpoint(socket_handle_t handle, net_l4_endpoint* out); int32_t close_socket(socket_handle_t handle); int32_t listen_on(socket_handle_t handle, int32_t backlog); diff --git a/kernel/networking/transport_layer/csocket_tcp.c b/kernel/networking/transport_layer/csocket_tcp.c index 983db44f..7e108ba3 100644 --- a/kernel/networking/transport_layer/csocket_tcp.c +++ b/kernel/networking/transport_layer/csocket_tcp.c @@ -1,6 +1,7 @@ #include "csocket_tcp.h" #include "networking/transport_layer/socket_bind.h" #include "networking/transport_layer/tcp.h" +#include "networking/transport_layer/tcp/tcp_internal.h" #include "networking/internet_layer/ipv4.h" #include "networking/internet_layer/ipv4_route.h" #include "networking/internet_layer/ipv4_utils.h" @@ -20,7 +21,7 @@ typedef struct tcp_socket { net_l4_endpoint remoteEP; bool connected; ksocket_t* ownerSocket; - SocketExtraOptions extraOpts; + SocketOptions options; SockBindSpec bindSpec; socket_bind_token_t bindToken; tcp_data flow; @@ -40,7 +41,7 @@ static ksocket_t* tcp_socket_pop_pending_at(tcp_socket_t* s, int idx) { return client; } -socket_impl_t socket_tcp_create(ksocket_t* owner, const SocketExtraOptions* extra) { +socket_impl_t socket_tcp_create(ksocket_t* owner, const SocketOptions* extra) { if (!owner) return NULL; tcp_socket_t* s = (tcp_socket_t*)zalloc(sizeof(*s)); @@ -48,14 +49,14 @@ socket_impl_t socket_tcp_create(ksocket_t* owner, const SocketExtraOptions* extr s->ownerSocket = owner; s->remoteEP.ver = IP_VER4; - if (extra) s->extraOpts = *extra; - if (!(s->extraOpts.flags & SOCK_OPT_BUF_SIZE)) { - s->extraOpts.flags |= SOCK_OPT_BUF_SIZE; - s->extraOpts.buf_size = TCP_DEFAULT_SOCKET_BUF; + if (extra) s->options = *extra; + if (!(s->options.flags & SOCK_OPT_BUF_SIZE)) { + s->options.flags |= SOCK_OPT_BUF_SIZE; + s->options.buf_size = TCP_DEFAULT_SOCKET_BUF; } - if (!s->extraOpts.buf_size) s->extraOpts.buf_size = TCP_DEFAULT_SOCKET_BUF; - if (s->extraOpts.buf_size > TCP_DEFAULT_SOCKET_BUF) s->extraOpts.buf_size = TCP_DEFAULT_SOCKET_BUF; + if (!s->options.buf_size) s->options.buf_size = TCP_DEFAULT_SOCKET_BUF; + if (s->options.buf_size > TCP_DEFAULT_SOCKET_BUF) s->options.buf_size = TCP_DEFAULT_SOCKET_BUF; return s; } @@ -69,10 +70,10 @@ int32_t socket_setopt_tcp(socket_impl_t sh, int32_t opt, const void* value, uint uint32_t v = 0; memcpy(&v, value, sizeof(v)); if (v) { - s->extraOpts.flags |= SOCK_OPT_KEEPALIVE; - if (!s->extraOpts.keepalive_ms) s->extraOpts.keepalive_ms = SOCKET_DEFAULT_KEEPALIVE_MS; - } else s->extraOpts.flags &= ~SOCK_OPT_KEEPALIVE; - if (s->flow.flow_generation) tcp_flow_apply_socket_options(&s->flow, &s->extraOpts); + s->options.flags |= SOCK_OPT_KEEPALIVE; + if (!s->options.keepalive_ms) s->options.keepalive_ms = SOCKET_DEFAULT_KEEPALIVE_MS; + } else s->options.flags &= ~SOCK_OPT_KEEPALIVE; + if (s->flow.flow_generation) tcp_flow_apply_socket_options(&s->flow, &s->options); return SOCK_OK; } case SOCK_OPT_KEEPALIVE_INTERVAL: { @@ -80,18 +81,18 @@ int32_t socket_setopt_tcp(socket_impl_t sh, int32_t opt, const void* value, uint uint32_t v = 0; memcpy(&v, value, sizeof(v)); if (!v) return SOCK_ERR_INVAL; - s->extraOpts.keepalive_ms = v; - s->extraOpts.flags |= SOCK_OPT_KEEPALIVE | SOCK_OPT_KEEPALIVE_INTERVAL; - if (s->flow.flow_generation) tcp_flow_apply_socket_options(&s->flow, &s->extraOpts); + s->options.keepalive_ms = v; + s->options.flags |= SOCK_OPT_KEEPALIVE | SOCK_OPT_KEEPALIVE_INTERVAL; + if (s->flow.flow_generation) tcp_flow_apply_socket_options(&s->flow, &s->options); return SOCK_OK; } case SOCK_OPT_TCP_NO_DELAY: { if (!value || len != sizeof(uint32_t)) return SOCK_ERR_INVAL; uint32_t v = 0; memcpy(&v, value, sizeof(v)); - if (v) s->extraOpts.flags |= SOCK_OPT_TCP_NO_DELAY; - else s->extraOpts.flags &= ~SOCK_OPT_TCP_NO_DELAY; - if (s->flow.flow_generation) tcp_flow_apply_socket_options(&s->flow, &s->extraOpts); + if (v) s->options.flags |= SOCK_OPT_TCP_NO_DELAY; + else s->options.flags &= ~SOCK_OPT_TCP_NO_DELAY; + if (s->flow.flow_generation) tcp_flow_apply_socket_options(&s->flow, &s->options); return SOCK_OK; } case SOCK_OPT_SEND_BUF_SIZE: { @@ -99,23 +100,24 @@ int32_t socket_setopt_tcp(socket_impl_t sh, int32_t opt, const void* value, uint uint32_t v = 0; memcpy(&v, value, sizeof(v)); if (!v) return SOCK_ERR_INVAL; - s->extraOpts.send_buf_size = v; - s->extraOpts.flags |= SOCK_OPT_SEND_BUF_SIZE; - if (s->flow.flow_generation) tcp_flow_apply_socket_options(&s->flow, &s->extraOpts); + s->options.send_buf_size = v; + s->options.flags |= SOCK_OPT_SEND_BUF_SIZE; + if (s->flow.flow_generation) tcp_flow_apply_socket_options(&s->flow, &s->options); return SOCK_OK; } case SOCK_OPT_MCAST_JOIN: + case SOCK_OPT_MCAST_LEAVE: + case SOCK_OPT_BROADCAST_ALLOWED: return SOCK_ERR_INVAL; case SOCK_OPT_RECV_TIMEOUT: case SOCK_OPT_SEND_TIMEOUT: case SOCK_OPT_BUF_SIZE: case SOCK_OPT_DEBUG: case SOCK_OPT_DONTFRAG: - case SOCK_OPT_BROADCAST_ALLOWED: case SOCK_OPT_TTL: { - int32_t rc = socket_extra_setopt(&s->extraOpts, opt, value, len); + int32_t rc = socket_common_options_set(&s->options, opt, value, len); if (rc != SOCK_OK) return rc; - if (s->flow.flow_generation) tcp_flow_apply_socket_options(&s->flow, &s->extraOpts); + if (s->flow.flow_generation) tcp_flow_apply_socket_options(&s->flow, &s->options); return SOCK_OK; } default: @@ -127,30 +129,108 @@ int32_t socket_getopt_tcp(socket_impl_t sh, int32_t opt, void* value, uint32_t* tcp_socket_t* s = (tcp_socket_t*)sh; if (!s || !len) return SOCK_ERR_INVAL; + switch ((uint32_t)opt) { + case SOCK_GET_REMOTE_ENDPOINT: + if (!value) { + *len = sizeof(net_l4_endpoint); + return SOCK_OK; + } + if (*len < sizeof(net_l4_endpoint)) return SOCK_ERR_INVAL; + memcpy(value, &s->remoteEP, sizeof(s->remoteEP)); + *len = sizeof(net_l4_endpoint); + return SOCK_OK; + case SOCK_GET_BIND_SPEC: + if (!value) { + *len = sizeof(SockBindSpec); + return SOCK_OK; + } + if (*len < sizeof(SockBindSpec)) return SOCK_ERR_INVAL; + memcpy(value, &s->bindSpec, sizeof(s->bindSpec)); + *len = sizeof(SockBindSpec); + return SOCK_OK; + default: + break; + } + uint32_t v = 0; switch ((uint32_t)opt) { - case SOCK_OPT_KEEPALIVE: - v = (s->extraOpts.flags & SOCK_OPT_KEEPALIVE) != 0; + case SOCK_GET_BOUND: + v = s->localPort != 0; break; - case SOCK_OPT_KEEPALIVE_INTERVAL: - v = s->extraOpts.keepalive_ms; + case SOCK_GET_CONNECTED: + v = s->connected; break; - case SOCK_OPT_TCP_NO_DELAY: - v = (s->extraOpts.flags & SOCK_OPT_TCP_NO_DELAY) != 0; + case SOCK_GET_LISTENING: + v = s->listening; break; - case SOCK_OPT_SEND_BUF_SIZE: - v = s->extraOpts.send_buf_size; + case SOCK_GET_LOCAL_PORT: + v = s->localPort; break; - case SOCK_OPT_MCAST_JOIN: + case SOCK_GET_RECV_QUEUED: + v = s->flow.flow_generation ? tcp_flow_readable(&s->flow) : 0; + break; + case SOCK_GET_SEND_QUEUED: { + tcp_flow_t* flow = tcp_flow_from_ctx(&s->flow); + if (flow) { + v = flow->tx.queued_bytes + flow->tx.nagle_len; + tcp_flow_put(flow); + } + break; + } + case SOCK_GET_TCP_STATE: { + tcp_flow_t* flow = tcp_flow_from_ctx(&s->flow); + if (flow) { + v = flow->base.state; + tcp_flow_put(flow); + } else v = s->listening ? TCP_LISTEN : TCP_STATE_CLOSED; + break; + } + case SOCK_GET_TCP_MSS: { + tcp_flow_t* flow = tcp_flow_from_ctx(&s->flow); + if (flow) { + v = flow->tx.mss; + tcp_flow_put(flow); + } + break; + } + case SOCK_GET_TCP_RTT_MS: { + tcp_flow_t* flow = tcp_flow_from_ctx(&s->flow); + if (flow) { + v = flow->tx.rtt_valid ? flow->tx.srtt : 0; + tcp_flow_put(flow); + } + break; + } + case SOCK_GET_TCP_RETRANSMITS: { + tcp_flow_t* flow = tcp_flow_from_ctx(&s->flow); + if (flow) { + for (int i = 0; i < TCP_MAX_TX_SEGS; ++i) if (flow->tx.txq[i].used) v += flow->tx.txq[i].retransmit_cnt; + tcp_flow_put(flow); + } + break; + } + case SOCK_GET_OPT_KEEPALIVE: + v = (s->options.flags & SOCK_OPT_KEEPALIVE) != 0; + break; + case SOCK_GET_OPT_KEEPALIVE_INTERVAL: + v = s->options.keepalive_ms; + break; + case SOCK_GET_OPT_TCP_NO_DELAY: + v = (s->options.flags & SOCK_OPT_TCP_NO_DELAY) != 0; + break; + case SOCK_GET_OPT_SEND_BUF_SIZE: + v = s->options.send_buf_size; + break; + case SOCK_GET_MCAST_GROUPS: + case SOCK_GET_OPT_BROADCAST_ALLOWED: return SOCK_ERR_INVAL; - case SOCK_OPT_RECV_TIMEOUT: - case SOCK_OPT_SEND_TIMEOUT: - case SOCK_OPT_BUF_SIZE: - case SOCK_OPT_DEBUG: - case SOCK_OPT_DONTFRAG: - case SOCK_OPT_BROADCAST_ALLOWED: - case SOCK_OPT_TTL: - return socket_extra_getopt(&s->extraOpts, opt, value, len); + case SOCK_GET_OPT_RECV_TIMEOUT: + case SOCK_GET_OPT_SEND_TIMEOUT: + case SOCK_GET_OPT_BUF_SIZE: + case SOCK_GET_OPT_DEBUG: + case SOCK_GET_OPT_DONTFRAG: + case SOCK_GET_OPT_TTL: + return socket_common_options_get(&s->options, opt, value, len); default: return SOCK_ERR_INVAL; } @@ -175,7 +255,7 @@ int32_t socket_bind_tcp(socket_impl_t sh, const SockBindSpec* spec_in, uint16_t ev.pid = socket_core_pid(s->ownerSocket); ev.u0 = port; ev.bind_spec = *spec_in; - netlog_socket_event(&s->extraOpts, &ev); + netlog_socket_event(&s->options, &ev); if (s->localPort) return SOCK_ERR_BOUND; if (!s->ownerSocket) return SOCK_ERR_SYS; @@ -314,7 +394,7 @@ int32_t socket_connect_tcp(socket_impl_t sh, const net_l4_endpoint* dst) { ev.action = NETLOG_ACT_CONNECT; ev.pid = socket_core_pid(s->ownerSocket); if (dst) ev.dst_ep = *dst; - netlog_socket_event(&s->extraOpts, &ev); + netlog_socket_event(&s->options, &ev); if (s->connected || s->listening) return SOCK_ERR_STATE; if (!dst || !dst->port) return SOCK_ERR_INVAL; @@ -360,7 +440,7 @@ int32_t socket_connect_tcp(socket_impl_t sh, const net_l4_endpoint* dst) { } memset(&s->flow, 0, sizeof(s->flow)); - bool ok = tcp_handshake_l3(chosen_l3, s->localPort, &d, &s->flow, &s->extraOpts); + bool ok = tcp_handshake_l3(chosen_l3, s->localPort, &d, &s->flow, &s->options); if (ok && d.ver == IP_VER4) { l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(chosen_l3); ok = ipv4_l3_is_ready(v4); @@ -393,7 +473,7 @@ int32_t socket_connect_tcp(socket_impl_t sh, const net_l4_endpoint* dst) { ev1.u1 = s->remoteEP.port; ev1.local_port = s->localPort; ev1.remote_ep = s->remoteEP; - netlog_socket_event(&s->extraOpts, &ev1); + netlog_socket_event(&s->options, &ev1); return SOCK_OK; } @@ -408,21 +488,47 @@ int64_t socket_send_tcp(socket_impl_t sh, const void* buf, uint64_t len) { ev.u0 = (uint32_t)len; ev.local_port = s->localPort; ev.remote_ep = s->remoteEP; - netlog_socket_event(&s->extraOpts, &ev); + netlog_socket_event(&s->options, &ev); if (!s->connected || !s->flow.flow_generation) return SOCK_ERR_STATE; if (!buf && len) return SOCK_ERR_INVAL; if (!len) return 0; uint32_t chunk = len > UINT32_MAX ? UINT32_MAX : (uint32_t)len; - s->flow.payload.ptr = (uintptr_t)buf; - s->flow.payload.size = chunk; - s->flow.flags = (1u << PSH_F) | (1u << ACK_F); - - tcp_result_t res = tcp_flow_send(&s->flow); - if (res != TCP_OK) return (int64_t)res; - if (s->flow.payload.size) return (int64_t)s->flow.payload.size; - return TCP_WOULDBLOCK; + uint32_t start_ms = (uint32_t)get_time(); + while (1) { + s->flow.payload.ptr = (uintptr_t)buf; + s->flow.payload.size = chunk; + s->flow.flags = (1u << PSH_F) | (1u << ACK_F); + + tcp_result_t res = tcp_flow_send(&s->flow); + if (res != TCP_OK && res != TCP_WOULDBLOCK) { + switch (res) { + case TCP_BUSY: + case TCP_TIMEOUT: + return SOCK_ERR_WOULDBLOCK; + case TCP_UNIMPLEMENT: + return SOCK_ERR_UNSUP; + case TCP_INVALID: + case TCP_RESET: + case TCP_DISCONNECT: + return SOCK_ERR_STATE; + case TCP_CSUM_ERR: + return SOCK_ERR_PROTO; + default: + return SOCK_ERR_SYS; + } + } + if (s->flow.payload.size) return (int64_t)s->flow.payload.size; + if (!(s->options.flags & SOCK_OPT_SEND_TIMEOUT) || !s->options.send_timeout_ms) return SOCK_ERR_WOULDBLOCK; + + uint32_t now_ms = (uint32_t)get_time(); + uint32_t elapsed_ms = now_ms - start_ms; + if (elapsed_ms >= s->options.send_timeout_ms) return SOCK_ERR_WOULDBLOCK; + uint32_t wait_ms = s->options.send_timeout_ms - elapsed_ms; + if (wait_ms > 5) wait_ms = 5; + msleep(wait_ms); + } } int64_t socket_recv_tcp(socket_impl_t sh, void* buf, uint64_t len) { @@ -436,16 +542,29 @@ int64_t socket_recv_tcp(socket_impl_t sh, void* buf, uint64_t len) { ev.u0 = (uint32_t)len; ev.local_port = s->localPort; ev.remote_ep = s->remoteEP; - netlog_socket_event(&s->extraOpts, &ev); + netlog_socket_event(&s->options, &ev); if (!buf || !len) return 0; - if (!s->connected || !s->flow.flow_generation) return s->connected ? TCP_WOULDBLOCK : 0; + if (!s->connected || !s->flow.flow_generation) return s->connected ? SOCK_ERR_WOULDBLOCK : 0; - int64_t n = tcp_flow_read(&s->flow, buf, len); - if (n != 0) return n; - if (tcp_flow_recv_closed(&s->flow)) return 0; - if (s->connected) return TCP_WOULDBLOCK; - return 0; + uint32_t start_ms = (uint32_t)get_time(); + while (1) { + int64_t n = tcp_flow_read(&s->flow, buf, len); + if (n > 0) return n; + if (n == TCP_DISCONNECT) return 0; + if (n < 0) return SOCK_ERR_STATE; + if (tcp_flow_recv_closed(&s->flow)) return 0; + if (!s->connected) return 0; + if (!(s->options.flags & SOCK_OPT_RECV_TIMEOUT) || !s->options.recv_timeout_ms) return SOCK_ERR_WOULDBLOCK; + + uint32_t now_ms = (uint32_t)get_time(); + uint32_t elapsed_ms = now_ms - start_ms; + if (elapsed_ms >= s->options.recv_timeout_ms) return SOCK_ERR_WOULDBLOCK; + + uint32_t wait_ms = s->options.recv_timeout_ms - elapsed_ms; + if (wait_ms > 5) wait_ms = 5; + msleep(wait_ms); + } } int32_t socket_close_tcp(socket_impl_t sh) { @@ -458,7 +577,7 @@ int32_t socket_close_tcp(socket_impl_t sh) { ev.pid = socket_core_pid(s->ownerSocket); ev.local_port = s->localPort; ev.remote_ep = s->remoteEP; - netlog_socket_event(&s->extraOpts, &ev); + netlog_socket_event(&s->options, &ev); if (s->connected && s->flow.flow_generation) { if (!tcp_flow_is_closed(&s->flow)) { @@ -503,10 +622,10 @@ void socket_destroy_tcp(socket_impl_t sh) { release(s); } -const SocketExtraOptions* socket_tcp_extra_options(socket_impl_t sh) { +const SocketOptions* socket_tcp_options(socket_impl_t sh) { tcp_socket_t* s = (tcp_socket_t*)sh; if (!s) return NULL; - return &s->extraOpts; + return &s->options; } uint32_t tcp_accept_enqueue(ksocket_t* listener, ip_version_t ipver, const void* src_ip_addr, const void* dst_ip_addr, uint16_t src_port, uint16_t dst_port) { @@ -539,7 +658,7 @@ uint32_t tcp_accept_enqueue(ksocket_t* listener, ip_version_t ipver, const void* uint16_t owner_pid = socket_core_pid(s->ownerSocket); if (!socket_core_alloc(PROTO_TCP, owner_pid, &child_owner)) return 0; - tcp_socket_t* child = (tcp_socket_t*)socket_tcp_create(child_owner, &s->extraOpts); + tcp_socket_t* child = (tcp_socket_t*)socket_tcp_create(child_owner, &s->options); if (!child) { socket_core_close_socket(child_owner); return 0; @@ -549,12 +668,12 @@ uint32_t tcp_accept_enqueue(ksocket_t* listener, ip_version_t ipver, const void* child->remoteEP.ver = ipver; memset(child->remoteEP.ip, 0, sizeof(child->remoteEP.ip)); if (ipver == IP_VER4) memcpy(child->remoteEP.ip, src_ip_addr, 4); - else memcpy(child->remoteEP.ip, src_ip_addr, 16); + else ipv6_cpy(child->remoteEP.ip, src_ip_addr); child->remoteEP.port = src_port; child->bindSpec.kind = BIND_IP; child->bindSpec.ver = ipver; if (ipver == IP_VER4) memcpy(child->bindSpec.ip, dst_ip_addr, 4); - else if (ipver == IP_VER6) memcpy(child->bindSpec.ip, dst_ip_addr, 16); + else if (ipver == IP_VER6) ipv6_cpy(child->bindSpec.ip, dst_ip_addr); if (!tcp_get_ctx(dst_port, ipver, dst_ip_addr, child->remoteEP.ip, src_port, &child->flow)) { socket_destroy_tcp(child); @@ -573,19 +692,3 @@ uint32_t tcp_accept_enqueue(ksocket_t* listener, ip_version_t ipver, const void* s->pending[s->backlogLen++] = child_owner; return 1; } - -uint16_t socket_get_local_port_tcp(socket_impl_t sh) { - tcp_socket_t* s = (tcp_socket_t*)sh; - return s ? s->localPort : 0; -} - -void socket_get_remote_ep_tcp(socket_impl_t sh, net_l4_endpoint* out) { - tcp_socket_t* s = (tcp_socket_t*)sh; - if (!s || !out) return; - *out = s->remoteEP; -} - -bool socket_is_connected_tcp(socket_impl_t sh) { - tcp_socket_t* s = (tcp_socket_t*)sh; - return s ? s->connected : false; -} diff --git a/kernel/networking/transport_layer/csocket_tcp.h b/kernel/networking/transport_layer/csocket_tcp.h index fafb94b3..e75d549e 100644 --- a/kernel/networking/transport_layer/csocket_tcp.h +++ b/kernel/networking/transport_layer/csocket_tcp.h @@ -8,7 +8,7 @@ extern "C" { #endif -socket_impl_t socket_tcp_create(ksocket_t* owner, const SocketExtraOptions* extra); +socket_impl_t socket_tcp_create(ksocket_t* owner, const SocketOptions* extra); int32_t socket_bind_tcp(socket_impl_t sh, const SockBindSpec* spec, uint16_t port); int32_t socket_listen_tcp(socket_impl_t sh, int32_t backlog); ksocket_t* socket_accept_tcp(socket_impl_t sh); @@ -19,13 +19,9 @@ int32_t socket_close_tcp(socket_impl_t sh); int32_t socket_setopt_tcp(socket_impl_t sh, int32_t opt, const void* value, uint32_t len); int32_t socket_getopt_tcp(socket_impl_t sh, int32_t opt, void* value, uint32_t* len); void socket_destroy_tcp(socket_impl_t sh); -const SocketExtraOptions* socket_tcp_extra_options(socket_impl_t sh); +const SocketOptions* socket_tcp_options(socket_impl_t sh); uint32_t tcp_accept_enqueue(ksocket_t* listener, ip_version_t ipver, const void* src_ip_addr, const void* dst_ip_addr, uint16_t src_port, uint16_t dst_port); -uint16_t socket_get_local_port_tcp(socket_impl_t sh); -void socket_get_remote_ep_tcp(socket_impl_t sh, net_l4_endpoint* out); -bool socket_is_connected_tcp(socket_impl_t sh); - #ifdef __cplusplus } #endif diff --git a/kernel/networking/transport_layer/csocket_udp.c b/kernel/networking/transport_layer/csocket_udp.c index c825c419..20df0344 100644 --- a/kernel/networking/transport_layer/csocket_udp.c +++ b/kernel/networking/transport_layer/csocket_udp.c @@ -10,6 +10,7 @@ #include "networking/network.h" #include "networking/interface_manager.h" #include "networking/net_logger/net_logger.h" +#include "syscalls/syscalls.h" #include "std/memory.h" #include "alloc/allocate.h" @@ -21,7 +22,7 @@ typedef struct udp_socket { net_l4_endpoint remoteEP; bool connected; ksocket_t* ownerSocket; - SocketExtraOptions extraOpts; + SocketOptions options; SockBindSpec bindSpec; socket_bind_token_t bindToken; netpkt_t** ring; @@ -60,7 +61,7 @@ static bool udp_socket_mcast_match(udp_socket_t* s, ip_version_t ver, const void memcpy(&got, dst_ip_addr, 4); if (want == got) return true; } else if (ver == IP_VER6) { - if (memcmp(group->ip, dst_ip_addr, 16) == 0) return true; + if (ipv6_cmp(group->ip, dst_ip_addr) == 0) return true; } } @@ -155,19 +156,19 @@ uint32_t socket_udp_input(ksocket_t* socket, ip_version_t ipver, const void* src if (s->connected) { if (s->remoteEP.ver != ipver || s->remoteEP.port != src_port) return 0; if (ipver == IP_VER4 && memcmp(s->remoteEP.ip, src_ip_addr, 4) != 0) return 0; - if (ipver == IP_VER6 && memcmp(s->remoteEP.ip, src_ip_addr, 16) != 0) return 0; + if (ipver == IP_VER6 && ipv6_cmp(s->remoteEP.ip, src_ip_addr) != 0) return 0; } uint32_t pkt_len = netpkt_len(pkt); uint32_t limit = UINT32_MAX; - if ((s->extraOpts.flags & SOCK_OPT_BUF_SIZE) && s->extraOpts.buf_size) limit = s->extraOpts.buf_size; + if ((s->options.flags & SOCK_OPT_BUF_SIZE) && s->options.buf_size) limit = s->options.buf_size; if (pkt_len > limit) return 0; if (s->rx_bytes > limit - pkt_len) return 0; if (!s->ring || !s->src_eps || !s->ring_cap) { uint32_t usable = UDP_DEFAULT_RING_CAP; - if ((s->extraOpts.flags & SOCK_OPT_BUF_SIZE) && s->extraOpts.buf_size) { - usable = s->extraOpts.buf_size / MAX_PACKET_SIZE; + if ((s->options.flags & SOCK_OPT_BUF_SIZE) && s->options.buf_size) { + usable = s->options.buf_size / MAX_PACKET_SIZE; if (usable < 4) usable = 4; if (usable > UDP_MAX_RING_CAP) usable = UDP_MAX_RING_CAP; } @@ -195,23 +196,23 @@ uint32_t socket_udp_input(ksocket_t* socket, ip_version_t ipver, const void* src s->src_eps[s->r_tail].ver = ipver; memset(s->src_eps[s->r_tail].ip, 0, 16); if (ipver == IP_VER4) memcpy(s->src_eps[s->r_tail].ip, src_ip_addr, 4); - else if (ipver == IP_VER6) memcpy(s->src_eps[s->r_tail].ip, src_ip_addr, 16); + else if (ipver == IP_VER6) ipv6_cpy(s->src_eps[s->r_tail].ip, src_ip_addr); s->src_eps[s->r_tail].port = src_port; s->r_tail = nexti; return pkt_len; } -socket_impl_t udp_socket_create(ksocket_t* owner, const SocketExtraOptions* extra) { +socket_impl_t udp_socket_create(ksocket_t* owner, const SocketOptions* extra) { if (!owner) return NULL; udp_socket_t* s = (udp_socket_t*)zalloc(sizeof(*s)); if (!s) return NULL; s->ownerSocket = owner; s->remoteEP.ver = IP_VER4; - if (extra) s->extraOpts = *extra; - s->extraOpts.flags &= ~(SOCK_OPT_MCAST_JOIN | SOCK_OPT_MCAST_LEAVE); - s->extraOpts.mcast_count = 0; - s->extraOpts.mcast_groups = NULL; + if (extra) s->options = *extra; + s->options.flags &= ~(SOCK_OPT_MCAST_JOIN | SOCK_OPT_MCAST_LEAVE); + s->options.mcast_count = 0; + s->options.mcast_groups = NULL; if (extra && extra->mcast_count) { if (!extra->mcast_groups || socket_setopt_udp(s, SOCK_OPT_MCAST_JOIN, extra->mcast_groups, sizeof(net_l4_endpoint) * extra->mcast_count) != SOCK_OK) { @@ -233,7 +234,7 @@ int32_t socket_bind_udp(socket_impl_t sh, const SockBindSpec* spec_in, uint16_t ev.pid = socket_core_pid(s->ownerSocket); ev.u0 = port; ev.bind_spec = *spec_in; - netlog_socket_event(&s->extraOpts, &ev); + netlog_socket_event(&s->options, &ev); if (s->localPort) return SOCK_ERR_BOUND; if (!s->ownerSocket) return SOCK_ERR_SYS; @@ -299,7 +300,7 @@ int32_t socket_connect_udp(socket_impl_t sh, const net_l4_endpoint* dst) { ev.action = NETLOG_ACT_CONNECT; ev.pid = socket_core_pid(s->ownerSocket); if (dst) ev.dst_ep = *dst; - netlog_socket_event(&s->extraOpts, &ev); + netlog_socket_event(&s->options, &ev); if (!dst || !dst->port) return SOCK_ERR_INVAL; if (dst->ver != IP_VER4 && dst->ver != IP_VER6) return SOCK_ERR_INVAL; @@ -322,7 +323,7 @@ int64_t socket_sendto_udp(socket_impl_t sh, const net_l4_endpoint* dst, const vo ev.dst_ep = *dst; ev.u0 = dst->port; } - netlog_socket_event(&s->extraOpts, &ev); + netlog_socket_event(&s->options, &ev); bool explicit_dst = dst != NULL; if (!dst) { @@ -337,7 +338,7 @@ int64_t socket_sendto_udp(socket_impl_t sh, const net_l4_endpoint* dst, const vo if (s->connected && explicit_dst) { if (d.ver != s->remoteEP.ver || d.port != s->remoteEP.port) return SOCK_ERR_STATE; if (d.ver == IP_VER4 && memcmp(d.ip, s->remoteEP.ip, 4) != 0) return SOCK_ERR_STATE; - if (d.ver == IP_VER6 && memcmp(d.ip, s->remoteEP.ip, 16) != 0) return SOCK_ERR_STATE; + if (d.ver == IP_VER6 && ipv6_cmp(d.ip, s->remoteEP.ip) != 0) return SOCK_ERR_STATE; } sizedptr pay; @@ -354,7 +355,7 @@ int64_t socket_sendto_udp(socket_impl_t sh, const net_l4_endpoint* dst, const vo l3_ipv4_interface_t* bcast_v4 = NULL; if (limited_bcast) { - if (!(s->extraOpts.flags & SOCK_OPT_BROADCAST_ALLOWED)) return SOCK_ERR_PERM; + if (!(s->options.flags & SOCK_OPT_BROADCAST_ALLOWED)) return SOCK_ERR_PERM; uint32_t n = socket_bind_select_l3(&s->bindSpec, IP_VER4, bcast_ids, MAX_IPV4_L3_INTERFACES); uint32_t valid = 0; @@ -379,7 +380,7 @@ int64_t socket_sendto_udp(socket_impl_t sh, const net_l4_endpoint* dst, const vo bcast_v4 = v4; break; } - if (bcast_v4 && !(s->extraOpts.flags & SOCK_OPT_BROADCAST_ALLOWED)) return SOCK_ERR_PERM; + if (bcast_v4 && !(s->options.flags & SOCK_OPT_BROADCAST_ALLOWED)) return SOCK_ERR_PERM; } if (bcast_v4) { @@ -405,7 +406,7 @@ int64_t socket_sendto_udp(socket_impl_t sh, const net_l4_endpoint* dst, const vo tx.scope = IP_TX_BOUND_L3; tx.index = chosen_l3; - udp_send_segment(&src, &d, pay, &tx, (s->extraOpts.flags & SOCK_OPT_TTL) ? s->extraOpts.ttl : 0, (s->extraOpts.flags & SOCK_OPT_DONTFRAG) ? 1 : 0); + udp_send_segment(&src, &d, pay, &tx, (s->options.flags & SOCK_OPT_TTL) ? s->options.ttl : 0, (s->options.flags & SOCK_OPT_DONTFRAG) ? 1 : 0); return (int64_t)len; } @@ -438,7 +439,7 @@ int64_t socket_sendto_udp(socket_impl_t sh, const net_l4_endpoint* dst, const vo tx.scope = IP_TX_BOUND_L3; tx.index = plan.l3_id; - udp_send_segment(&src, &d, pay, &tx, (s->extraOpts.flags & SOCK_OPT_TTL) ? s->extraOpts.ttl : 0, (s->extraOpts.flags & SOCK_OPT_DONTFRAG) ? 1 : 0); + udp_send_segment(&src, &d, pay, &tx, (s->options.flags & SOCK_OPT_TTL) ? s->options.ttl : 0, (s->options.flags & SOCK_OPT_DONTFRAG) ? 1 : 0); return (int64_t)len; } @@ -465,14 +466,14 @@ int64_t socket_sendto_udp(socket_impl_t sh, const net_l4_endpoint* dst, const vo net_l4_endpoint src; src.ver = IP_VER6; memset(src.ip, 0, 16); - memcpy(src.ip, v6->ip, 16); + ipv6_cpy(src.ip, v6->ip); src.port = s->localPort; ip_tx_opts_t tx; tx.scope = IP_TX_BOUND_L3; tx.index = plan.l3_id; - udp_send_segment(&src, &d, pay, &tx, (s->extraOpts.flags & SOCK_OPT_TTL) ? s->extraOpts.ttl : 0, (s->extraOpts.flags & SOCK_OPT_DONTFRAG) ? 1 : 0); + udp_send_segment(&src, &d, pay, &tx, (s->options.flags & SOCK_OPT_TTL) ? s->options.ttl : 0, (s->options.flags & SOCK_OPT_DONTFRAG) ? 1 : 0); return (int64_t)len; } @@ -490,9 +491,22 @@ int64_t socket_recvfrom_udp(socket_impl_t sh, void* buf, uint64_t len, net_l4_en ev.u0 = (uint32_t)len; ev.local_port = s->localPort; ev.remote_ep = s->remoteEP; - netlog_socket_event(&s->extraOpts, &ev); + netlog_socket_event(&s->options, &ev); - if (!s->ring || s->r_head == s->r_tail) return SOCK_ERR_WOULDBLOCK; + if (!s->ring || s->r_head == s->r_tail) { + if (!(s->options.flags & SOCK_OPT_RECV_TIMEOUT) || !s->options.recv_timeout_ms) return SOCK_ERR_WOULDBLOCK; + + uint32_t start_ms = (uint32_t)get_time(); + while (!s->ring || s->r_head == s->r_tail) { + uint32_t now_ms = (uint32_t)get_time(); + uint32_t elapsed_ms = now_ms - start_ms; + if (elapsed_ms >= s->options.recv_timeout_ms) return SOCK_ERR_WOULDBLOCK; + + uint32_t wait_ms = s->options.recv_timeout_ms - elapsed_ms; + if (wait_ms > 5) wait_ms = 5; + msleep(wait_ms); + } + } netpkt_t* p = s->ring[s->r_head]; net_l4_endpoint se = s->src_eps[s->r_head]; @@ -537,11 +551,15 @@ int32_t socket_setopt_udp(socket_impl_t sh, int32_t opt, const void* value, uint for (uint32_t i = 0; i < count32; ++i) { bool exists = false; for (uint8_t j = 0; j < next_count; ++j) { - if (next[j].ver != groups[i].ver || next[j].port != groups[i].port) continue; + if (next[j].ver != groups[i].ver) continue; uint32_t sz = groups[i].ver == IP_VER4 ? 4 : 16; if (memcmp(next[j].ip, groups[i].ip, sz) == 0) exists = true; } - if (!exists) next[next_count++] = groups[i]; + if (!exists) { + next[next_count] = groups[i]; + next[next_count].port = 0; + next_count++; + } } if (next_count == s->mcast_count) { @@ -561,18 +579,18 @@ int32_t socket_setopt_udp(socket_impl_t sh, int32_t opt, const void* value, uint release(next); s->mcast_groups = old_groups; s->mcast_count = old_count; - s->extraOpts.mcast_groups = s->mcast_groups; - s->extraOpts.mcast_count = s->mcast_count; - if (s->mcast_count) s->extraOpts.flags |= SOCK_OPT_MCAST_JOIN; - else s->extraOpts.flags &= ~SOCK_OPT_MCAST_JOIN; + s->options.mcast_groups = s->mcast_groups; + s->options.mcast_count = s->mcast_count; + if (s->mcast_count) s->options.flags |= SOCK_OPT_MCAST_JOIN; + else s->options.flags &= ~SOCK_OPT_MCAST_JOIN; if (s->localPort) udp_socket_join_mcast_groups(s); return rc; } if (old_groups) release(old_groups); - s->extraOpts.mcast_groups = s->mcast_groups; - s->extraOpts.mcast_count = s->mcast_count; - s->extraOpts.flags |= SOCK_OPT_MCAST_JOIN; + s->options.mcast_groups = s->mcast_groups; + s->options.mcast_count = s->mcast_count; + s->options.flags |= SOCK_OPT_MCAST_JOIN; return SOCK_OK; } case SOCK_OPT_MCAST_LEAVE: { @@ -594,7 +612,7 @@ int32_t socket_setopt_udp(socket_impl_t sh, int32_t opt, const void* value, uint for (uint8_t i = 0; i < s->mcast_count; ++i) { bool remove_group = false; for (uint32_t j = 0; j < count; ++j) { - if (s->mcast_groups[i].ver != groups[j].ver || s->mcast_groups[i].port != groups[j].port) continue; + if (s->mcast_groups[i].ver != groups[j].ver) continue; uint32_t sz = groups[j].ver == IP_VER4 ? 4 : 16; if (memcmp(s->mcast_groups[i].ip, groups[j].ip, sz) == 0) remove_group = true; } @@ -629,18 +647,18 @@ int32_t socket_setopt_udp(socket_impl_t sh, int32_t opt, const void* value, uint if (next) release(next); s->mcast_groups = old_groups; s->mcast_count = old_count; - s->extraOpts.mcast_groups = s->mcast_groups; - s->extraOpts.mcast_count = s->mcast_count; - s->extraOpts.flags |= SOCK_OPT_MCAST_JOIN; + s->options.mcast_groups = s->mcast_groups; + s->options.mcast_count = s->mcast_count; + s->options.flags |= SOCK_OPT_MCAST_JOIN; if (s->localPort) udp_socket_join_mcast_groups(s); return rc; } release(old_groups); - s->extraOpts.mcast_count = s->mcast_count; - s->extraOpts.mcast_groups = s->mcast_groups; - if (s->mcast_count) s->extraOpts.flags |= SOCK_OPT_MCAST_JOIN; - else s->extraOpts.flags &= ~SOCK_OPT_MCAST_JOIN; + s->options.mcast_count = s->mcast_count; + s->options.mcast_groups = s->mcast_groups; + if (s->mcast_count) s->options.flags |= SOCK_OPT_MCAST_JOIN; + else s->options.flags &= ~SOCK_OPT_MCAST_JOIN; return SOCK_OK; } case SOCK_OPT_RECV_TIMEOUT: @@ -650,7 +668,7 @@ int32_t socket_setopt_udp(socket_impl_t sh, int32_t opt, const void* value, uint case SOCK_OPT_DONTFRAG: case SOCK_OPT_BROADCAST_ALLOWED: case SOCK_OPT_TTL: - return socket_extra_setopt(&s->extraOpts, opt, value, len); + return socket_common_options_set(&s->options, opt, value, len); default: return SOCK_ERR_INVAL; } @@ -661,12 +679,58 @@ int32_t socket_getopt_udp(socket_impl_t sh, int32_t opt, void* value, uint32_t* if (!s || !len) return SOCK_ERR_INVAL; switch ((uint32_t)opt) { - case SOCK_OPT_KEEPALIVE: - case SOCK_OPT_KEEPALIVE_INTERVAL: - case SOCK_OPT_TCP_NO_DELAY: - case SOCK_OPT_SEND_BUF_SIZE: + case SOCK_GET_REMOTE_ENDPOINT: + if (!value) { + *len = sizeof(net_l4_endpoint); + return SOCK_OK; + } + if (*len < sizeof(net_l4_endpoint)) return SOCK_ERR_INVAL; + memcpy(value, &s->remoteEP, sizeof(s->remoteEP)); + *len = sizeof(net_l4_endpoint); + return SOCK_OK; + case SOCK_GET_BIND_SPEC: + if (!value) { + *len = sizeof(SockBindSpec); + return SOCK_OK; + } + if (*len < sizeof(SockBindSpec)) return SOCK_ERR_INVAL; + memcpy(value, &s->bindSpec, sizeof(s->bindSpec)); + *len = sizeof(SockBindSpec); + return SOCK_OK; + default: + break; + } + + uint32_t v = 0; + switch ((uint32_t)opt) { + case SOCK_GET_BOUND: + v = s->localPort != 0; + break; + case SOCK_GET_CONNECTED: + v = s->connected; + break; + case SOCK_GET_LISTENING: + v = 0; + break; + case SOCK_GET_LOCAL_PORT: + v = s->localPort; + break; + case SOCK_GET_RECV_QUEUED: + v = s->rx_bytes; + break; + case SOCK_GET_SEND_QUEUED: + v = 0; + break; + case SOCK_GET_OPT_KEEPALIVE: + case SOCK_GET_OPT_KEEPALIVE_INTERVAL: + case SOCK_GET_OPT_TCP_NO_DELAY: + case SOCK_GET_OPT_SEND_BUF_SIZE: + case SOCK_GET_TCP_STATE: + case SOCK_GET_TCP_MSS: + case SOCK_GET_TCP_RTT_MS: + case SOCK_GET_TCP_RETRANSMITS: return SOCK_ERR_INVAL; - case SOCK_OPT_MCAST_JOIN: { + case SOCK_GET_MCAST_GROUPS: { uint32_t need = s->mcast_count * sizeof(net_l4_endpoint); if (!value) { *len = need; @@ -677,17 +741,27 @@ int32_t socket_getopt_udp(socket_impl_t sh, int32_t opt, void* value, uint32_t* *len = need; return SOCK_OK; } - case SOCK_OPT_RECV_TIMEOUT: - case SOCK_OPT_SEND_TIMEOUT: - case SOCK_OPT_BUF_SIZE: - case SOCK_OPT_DEBUG: - case SOCK_OPT_DONTFRAG: - case SOCK_OPT_BROADCAST_ALLOWED: - case SOCK_OPT_TTL: - return socket_extra_getopt(&s->extraOpts, opt, value, len); + case SOCK_GET_OPT_RECV_TIMEOUT: + case SOCK_GET_OPT_SEND_TIMEOUT: + case SOCK_GET_OPT_BUF_SIZE: + case SOCK_GET_OPT_DEBUG: + case SOCK_GET_OPT_DONTFRAG: + case SOCK_GET_OPT_BROADCAST_ALLOWED: + case SOCK_GET_OPT_TTL: + return socket_common_options_get(&s->options, opt, value, len); default: return SOCK_ERR_INVAL; } + + if (!value) { + *len = sizeof(uint32_t); + return SOCK_OK; + } + + if (*len < sizeof(uint32_t)) return SOCK_ERR_INVAL; + memcpy(value, &v, sizeof(v)); + *len = sizeof(uint32_t); + return SOCK_OK; } int32_t socket_close_udp(socket_impl_t sh) { @@ -702,7 +776,7 @@ int32_t socket_close_udp(socket_impl_t sh) { ev.pid = socket_core_pid(s->ownerSocket); ev.local_port = s->localPort; ev.remote_ep = s->remoteEP; - netlog_socket_event(&s->extraOpts, &ev); + netlog_socket_event(&s->options, &ev); if (s->ring) { while (s->r_head != s->r_tail) { @@ -724,9 +798,9 @@ int32_t socket_close_udp(socket_impl_t sh) { s->mcast_groups = NULL; } s->mcast_count = 0; - s->extraOpts.mcast_count = 0; - s->extraOpts.mcast_groups = NULL; - s->extraOpts.flags &= ~(SOCK_OPT_MCAST_JOIN | SOCK_OPT_MCAST_LEAVE); + s->options.mcast_count = 0; + s->options.mcast_groups = NULL; + s->options.flags &= ~(SOCK_OPT_MCAST_JOIN | SOCK_OPT_MCAST_LEAVE); s->ring_cap = 0; s->r_head = 0; s->r_tail = 0; @@ -750,19 +824,3 @@ void socket_destroy_udp(socket_impl_t sh) { socket_close_udp(s); release(s); } - -uint16_t socket_get_local_port_udp(socket_impl_t sh) { - udp_socket_t* s = (udp_socket_t*)sh; - return s ? s->localPort : 0; -} - -void socket_get_remote_ep_udp(socket_impl_t sh, net_l4_endpoint* out) { - udp_socket_t* s = (udp_socket_t*)sh; - if (!s || !out) return; - *out = s->remoteEP; -} - -bool socket_is_connected_udp(socket_impl_t sh) { - udp_socket_t* s = (udp_socket_t*)sh; - return s ? s->connected : false; -} diff --git a/kernel/networking/transport_layer/csocket_udp.h b/kernel/networking/transport_layer/csocket_udp.h index 755a7dc5..757f8e28 100644 --- a/kernel/networking/transport_layer/csocket_udp.h +++ b/kernel/networking/transport_layer/csocket_udp.h @@ -9,7 +9,7 @@ extern "C" { #endif -socket_impl_t udp_socket_create(ksocket_t* owner, const SocketExtraOptions* extra); +socket_impl_t udp_socket_create(ksocket_t* owner, const SocketOptions* extra); int32_t socket_bind_udp(socket_impl_t sh, const SockBindSpec* spec, uint16_t port); int32_t socket_connect_udp(socket_impl_t sh, const net_l4_endpoint* dst); int64_t socket_sendto_udp(socket_impl_t sh, const net_l4_endpoint* dst, const void* buf, uint64_t len); @@ -20,11 +20,6 @@ int32_t socket_setopt_udp(socket_impl_t sh, int32_t opt, const void* value, uint int32_t socket_getopt_udp(socket_impl_t sh, int32_t opt, void* value, uint32_t* len); uint32_t socket_udp_input(ksocket_t* socket, ip_version_t ipver, const void* src_ip_addr, const void* dst_ip_addr, netpkt_t* pkt, uint16_t src_port, uint16_t dst_port); -uint16_t socket_get_local_port_udp(socket_impl_t sh); -void socket_get_remote_ep_udp(socket_impl_t sh, net_l4_endpoint* out); - -bool socket_is_connected_udp(socket_impl_t sh); - #ifdef __cplusplus } #endif diff --git a/kernel/networking/transport_layer/socket_bind.c b/kernel/networking/transport_layer/socket_bind.c index 2629d163..7d8ff720 100644 --- a/kernel/networking/transport_layer/socket_bind.c +++ b/kernel/networking/transport_layer/socket_bind.c @@ -164,6 +164,21 @@ bool socket_bind_insert(ksocket_t* socket, protocol_t protocol, const SockBindSp SockBindSpec normal = *spec; if (!socket_bind_normalize_spec(&normal)) return false; + if (normal.kind == BIND_L3) { + ip_version_t ver = normal.ver ? normal.ver : (l3_is_v6_from_id(normal.l3_id) ? IP_VER6 : IP_VER4); + if (!socket_bind_l3_valid(ver, normal.l3_id)) return false; + } else if (normal.kind == BIND_L2) { + if (!l2_interface_find_by_index(normal.ifindex)) return false; + } else if (normal.kind == BIND_IP) { + if (normal.ver == IP_VER4) { + uint32_t ip = 0; + memcpy(&ip, normal.ip, 4); + if (!ipv4_is_unspecified(ip) && !l3_ipv4_find_by_ip(ip)) return false; + } else if (normal.ver == IP_VER6 && !ipv6_is_unspecified(normal.ip)) { + if (!l3_ipv6_find_by_ip(normal.ip)) return false; + } + } + socket_bind_map_init(); if (!bind_map) return false; @@ -191,7 +206,7 @@ bool socket_bind_insert(ksocket_t* socket, protocol_t protocol, const SockBindSp memcpy(&av, normal.ip, 4); memcpy(&bv, e->spec.ip, 4); overlap = ipv4_is_unspecified(av) || ipv4_is_unspecified(bv) || av == bv; - } else if (a_family == IP_VER6) overlap = ipv6_is_unspecified(normal.ip) || ipv6_is_unspecified(e->spec.ip) || memcmp(normal.ip, e->spec.ip, 16) == 0; + } else if (a_family == IP_VER6) overlap = ipv6_is_unspecified(normal.ip) || ipv6_is_unspecified(e->spec.ip) || ipv6_cmp(normal.ip, e->spec.ip) == 0; } else if (normal.kind == BIND_L3 && e->spec.kind == BIND_L3) overlap = normal.l3_id == e->spec.l3_id; else if (normal.kind == BIND_L2 && e->spec.kind == BIND_L2) overlap = normal.ifindex == e->spec.ifindex; if (!overlap) continue; @@ -344,7 +359,7 @@ uint32_t socket_bind_collect(protocol_t protocol, ip_version_t ipver, uint8_t l3 memcpy(&got, dst_ip_addr, 4); if (!ipv4_is_unspecified(want) && want != got) continue; } else if (ipver == IP_VER6) { - if (!ipv6_is_unspecified(e->spec.ip) && memcmp(e->spec.ip, dst_ip_addr, 16) != 0) continue; + if (!ipv6_is_unspecified(e->spec.ip) && ipv6_cmp(e->spec.ip, dst_ip_addr) != 0) continue; } else continue; } else if (e->spec.kind != BIND_ANY && e->spec.kind != BIND_ANY4 && e->spec.kind != BIND_ANY6 && e->spec.kind != BIND_L3 && e->spec.kind != BIND_L2) continue; diff --git a/kernel/networking/transport_layer/socket_core.c b/kernel/networking/transport_layer/socket_core.c index 61181e69..f6b5cdc7 100644 --- a/kernel/networking/transport_layer/socket_core.c +++ b/kernel/networking/transport_layer/socket_core.c @@ -168,12 +168,24 @@ int32_t socket_core_set_option(ksocket_t* socket, int32_t opt, const void* value int32_t socket_core_get_option(ksocket_t* socket, int32_t opt, void* value, uint32_t* len) { if (!socket || !len) return SOCK_ERR_INVAL; - if (!value && *len) return SOCK_ERR_INVAL; if (!socket->getopt || !socket->impl) return SOCK_ERR_PROTO; - return socket->getopt(socket->impl, opt, value, len); + + uint32_t v = 0; + if (opt == SOCK_GET_PROTOCOL) v = socket->protocol; + else if (opt == SOCK_GET_OWNER_PID) v = socket->pid; + else return socket->getopt(socket->impl, opt, value, len); + + if (!value) { + *len = sizeof(uint32_t); + return SOCK_OK; + } + if (*len < sizeof(uint32_t)) return SOCK_ERR_INVAL; + memcpy(value, &v, sizeof(v)); + *len = sizeof(uint32_t); + return SOCK_OK; } -int32_t socket_extra_setopt(SocketExtraOptions* opts, int32_t opt, const void* value, uint32_t len) { +int32_t socket_common_options_set(SocketOptions* opts, int32_t opt, const void* value, uint32_t len) { if (!opts) return SOCK_ERR_INVAL; uint32_t v = 1; @@ -223,30 +235,30 @@ int32_t socket_extra_setopt(SocketExtraOptions* opts, int32_t opt, const void* v } } -int32_t socket_extra_getopt(const SocketExtraOptions* opts, int32_t opt, void* value, uint32_t* len) { +int32_t socket_common_options_get(const SocketOptions* opts, int32_t opt, void* value, uint32_t* len) { if (!opts || !len) return SOCK_ERR_INVAL; uint32_t v = 0; switch ((uint32_t)opt) { - case SOCK_OPT_RECV_TIMEOUT: + case SOCK_GET_OPT_RECV_TIMEOUT: v = opts->recv_timeout_ms; break; - case SOCK_OPT_SEND_TIMEOUT: + case SOCK_GET_OPT_SEND_TIMEOUT: v = opts->send_timeout_ms; break; - case SOCK_OPT_BUF_SIZE: + case SOCK_GET_OPT_BUF_SIZE: v = opts->buf_size; break; - case SOCK_OPT_DEBUG: + case SOCK_GET_OPT_DEBUG: v = opts->debug_level; break; - case SOCK_OPT_DONTFRAG: + case SOCK_GET_OPT_DONTFRAG: v = (opts->flags & SOCK_OPT_DONTFRAG) != 0; break; - case SOCK_OPT_BROADCAST_ALLOWED: + case SOCK_GET_OPT_BROADCAST_ALLOWED: v = (opts->flags & SOCK_OPT_BROADCAST_ALLOWED) != 0; break; - case SOCK_OPT_TTL: + case SOCK_GET_OPT_TTL: v = opts->ttl; break; default: diff --git a/kernel/networking/transport_layer/socket_core.h b/kernel/networking/transport_layer/socket_core.h index 5565a709..117a20fe 100644 --- a/kernel/networking/transport_layer/socket_core.h +++ b/kernel/networking/transport_layer/socket_core.h @@ -28,8 +28,8 @@ int32_t socket_core_close_handle(socket_handle_t handle, uint16_t pid); int32_t socket_core_close_socket(ksocket_t* socket); int32_t socket_core_set_option(ksocket_t* socket, int32_t opt, const void* value, uint32_t len); int32_t socket_core_get_option(ksocket_t* socket, int32_t opt, void* value, uint32_t* len); -int32_t socket_extra_setopt(SocketExtraOptions* opts, int32_t opt, const void* value, uint32_t len); -int32_t socket_extra_getopt(const SocketExtraOptions* opts, int32_t opt, void* value, uint32_t* len); +int32_t socket_common_options_set(SocketOptions* opts, int32_t opt, const void* value, uint32_t len); +int32_t socket_common_options_get(const SocketOptions* opts, int32_t opt, void* value, uint32_t* len); socket_impl_t socket_core_impl(ksocket_t* socket); protocol_t socket_core_protocol(const ksocket_t* socket); diff --git a/kernel/networking/transport_layer/tcp.h b/kernel/networking/transport_layer/tcp.h index 3437a326..db306ac6 100644 --- a/kernel/networking/transport_layer/tcp.h +++ b/kernel/networking/transport_layer/tcp.h @@ -88,8 +88,8 @@ typedef enum { bool tcp_get_ctx(uint16_t local_port, ip_version_t ver, const void *local_ip, const void *remote_ip, uint16_t remote_port, tcp_data *out_ctx); -bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, tcp_data *flow_ctx, const SocketExtraOptions* extra); -void tcp_flow_apply_socket_options(tcp_data *flow_ctx, const SocketExtraOptions* extra); +bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, tcp_data *flow_ctx, const SocketOptions* extra); +void tcp_flow_apply_socket_options(tcp_data *flow_ctx, const SocketOptions* extra); tcp_result_t tcp_flow_send(tcp_data *flow_ctx); tcp_result_t tcp_flow_flush(tcp_data *flow_ctx); diff --git a/kernel/networking/transport_layer/tcp/tcp_core.c b/kernel/networking/transport_layer/tcp/tcp_core.c index fb86e154..60b4994f 100644 --- a/kernel/networking/transport_layer/tcp/tcp_core.c +++ b/kernel/networking/transport_layer/tcp/tcp_core.c @@ -106,7 +106,7 @@ tcp_flow_t *tcp_flow_from_ctx(tcp_data *flow_ctx) { return flow; } -void tcp_flow_apply_socket_options(tcp_data *flow_ctx, const SocketExtraOptions* extra) { +void tcp_flow_apply_socket_options(tcp_data *flow_ctx, const SocketOptions* extra) { tcp_flow_t *flow = tcp_flow_from_ctx(flow_ctx); if (!flow) return; @@ -422,7 +422,7 @@ void tcp_rtt_update(tcp_flow_t *flow, uint32_t sample_ms){ flow->tx.rto = rto; } -bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, tcp_data *flow_ctx, const SocketExtraOptions* extra){ +bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, tcp_data *flow_ctx, const SocketOptions* extra){ if (!dst || !flow_ctx) return false; tcp_flow_t *flow = tcp_alloc_flow(); diff --git a/kernel/networking/transport_layer/tcp/tcp_rx.c b/kernel/networking/transport_layer/tcp/tcp_rx.c index b20c027c..ed740328 100644 --- a/kernel/networking/transport_layer/tcp/tcp_rx.c +++ b/kernel/networking/transport_layer/tcp/tcp_rx.c @@ -446,7 +446,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a return; } - const SocketExtraOptions* listener_extra = socket_tcp_extra_options(socket_core_impl(listener)); + const SocketOptions* listener_extra = socket_tcp_options(socket_core_impl(listener)); tcp_flow_t *nf = tcp_alloc_flow(); if (!nf) { socket_core_put(listener); diff --git a/kernel/process/kernel_syscall_impl.c b/kernel/process/kernel_syscall_impl.c index c87ccbf2..9d07ea2f 100644 --- a/kernel/process/kernel_syscall_impl.c +++ b/kernel/process/kernel_syscall_impl.c @@ -89,7 +89,7 @@ uint64_t get_time(){ return timer_now_msec(); } -socket_handle_t socket_create(protocol_t protocol, const SocketExtraOptions* extra){ +socket_handle_t socket_create(protocol_t protocol, const SocketOptions* extra){ return create_socket(protocol, extra); } diff --git a/kernel/process/syscall.c b/kernel/process/syscall.c index 90e95aa8..ada55de7 100644 --- a/kernel/process/syscall.c +++ b/kernel/process/syscall.c @@ -227,10 +227,10 @@ u64 syscall_get_time(process_t *ctx){ u64 syscall_socket_create(process_t *ctx){ protocol_t protocol = (protocol_t)ctx->PROC_X0; - const SocketExtraOptions* extra = NULL; + const SocketOptions* extra = NULL; if (ctx->PROC_X1) { - SYSCALL_ARG(SocketExtraOptions, user_extra, PROC_X1, false); + SYSCALL_ARG(SocketOptions, user_extra, PROC_X1, false); if (user_extra->mcast_count) { if (!user_extra->mcast_groups) return 0; if (!validate_address(ctx, (uptr)user_extra->mcast_groups, sizeof(net_l4_endpoint) * user_extra->mcast_count, false)) return 0; diff --git a/shared b/shared index ff368319..69cb634a 160000 --- a/shared +++ b/shared @@ -1 +1 @@ -Subproject commit ff3683196a366f834f1fecbcf6ebc8729192b9bd +Subproject commit 69cb634a1083aad5e5373b18f9a150d8b76065c7 From 4407dcf5c68024515babd83abfc89bd28d8bde0b Mon Sep 17 00:00:00 2001 From: CodeAnarchist Date: Thu, 18 Jun 2026 22:50:46 +0200 Subject: [PATCH 26/39] [sock] add special socket base --- kernel/networking/transport_layer/csocket.c | 42 +++++++++++++------ .../transport_layer/csocket_special.c | 41 ++++++++++++++++++ .../transport_layer/csocket_special.h | 17 ++++++++ .../networking/transport_layer/csocket_tcp.c | 2 +- .../networking/transport_layer/socket_core.c | 9 +++- .../networking/transport_layer/socket_core.h | 3 +- 6 files changed, 98 insertions(+), 16 deletions(-) create mode 100644 kernel/networking/transport_layer/csocket_special.c create mode 100644 kernel/networking/transport_layer/csocket_special.h diff --git a/kernel/networking/transport_layer/csocket.c b/kernel/networking/transport_layer/csocket.c index 89c16b05..2ba93f2c 100644 --- a/kernel/networking/transport_layer/csocket.c +++ b/kernel/networking/transport_layer/csocket.c @@ -1,4 +1,5 @@ #include "csocket.h" +#include "csocket_special.h" #include "csocket_tcp.h" #include "csocket_udp.h" #include "socket_core.h" @@ -13,15 +14,16 @@ socket_handle_t create_socket(protocol_t protocol, const SocketOptions* extra){ SocketOptions default_extra = {}; if (!extra) extra = &default_extra; - if (protocol == PROTO_NONE) { - //TODO PROTO_NONE for RAW SET or L2 - return false; - } - if (protocol != PROTO_TCP && protocol != PROTO_UDP) return false; + SocketSpecialKind special_kind = extra->special_kind; + bool normal_socket = special_kind == SOCKET_SPECIAL_NONE && (protocol == PROTO_TCP || protocol == PROTO_UDP); + bool raw_socket = special_kind == SOCKET_SPECIAL_RAW && (protocol == PROTO_ICMP || protocol == PROTO_ICMPV6 || protocol == PROTO_IGMP); + bool ctrl_socket = special_kind == SOCKET_SPECIAL_CTRL && protocol == PROTO_NONE; + bool packet_socket = special_kind == SOCKET_SPECIAL_PACKET && protocol == PROTO_NONE; + if (!normal_socket && !raw_socket && !ctrl_socket && !packet_socket) return false; uint16_t pid = get_current_proc_pid(); ksocket_t* socket = NULL; - if (!socket_core_alloc(protocol, pid, &socket)) return false; + if (!socket_core_alloc(protocol, special_kind, pid, &socket)) return false; socket_impl_t impl = NULL; @@ -42,6 +44,12 @@ socket_handle_t create_socket(protocol_t protocol, const SocketOptions* extra){ close = socket_close_tcp; setopt = socket_setopt_tcp; getopt = socket_getopt_tcp; + } else { + impl = socket_special_create(socket, extra); + destroy = socket_destroy_special; + close = socket_close_special; + setopt = socket_setopt_special; + getopt = socket_getopt_special; } if (!impl) { socket_core_close_socket(socket); @@ -66,7 +74,8 @@ int32_t bind_socket(socket_handle_t handle, const SockBindSpec *spec_in, uint16_ if (!socket) return SOCK_ERR_INVAL; int32_t res = SOCK_ERR_PROTO; - if (socket_core_protocol(socket) == PROTO_TCP) res = socket_bind_tcp(socket_core_impl(socket), &spec, port); + if (socket_core_special_kind(socket) != SOCKET_SPECIAL_NONE) res = SOCK_ERR_UNSUP; + else if (socket_core_protocol(socket) == PROTO_TCP) res = socket_bind_tcp(socket_core_impl(socket), &spec, port); else if (socket_core_protocol(socket) == PROTO_UDP) res = socket_bind_udp(socket_core_impl(socket), &spec, port); socket_core_put(socket); @@ -79,7 +88,8 @@ int32_t connect_socket(socket_handle_t handle, const net_l4_endpoint* dst){ if (!socket) return SOCK_ERR_INVAL; int32_t res = SOCK_ERR_PROTO; - if (socket_core_protocol(socket) == PROTO_TCP) { + if (socket_core_special_kind(socket) != SOCKET_SPECIAL_NONE) res = SOCK_ERR_UNSUP; + else if (socket_core_protocol(socket) == PROTO_TCP) { res = socket_connect_tcp(socket_core_impl(socket), dst); } else if (socket_core_protocol(socket) == PROTO_UDP) res = socket_connect_udp(socket_core_impl(socket), dst); @@ -92,7 +102,8 @@ int64_t send_on_socket(socket_handle_t handle, const void* buf, uint64_t len){ if (!socket) return SOCK_ERR_INVAL; int64_t res = SOCK_ERR_PROTO; - if (socket_core_protocol(socket) == PROTO_TCP) res = socket_send_tcp(socket_core_impl(socket), buf, len); + if (socket_core_special_kind(socket) != SOCKET_SPECIAL_NONE) res = SOCK_ERR_UNSUP; + else if (socket_core_protocol(socket) == PROTO_TCP) res = socket_send_tcp(socket_core_impl(socket), buf, len); else if (socket_core_protocol(socket) == PROTO_UDP) res = socket_sendto_udp(socket_core_impl(socket), NULL, buf, len); socket_core_put(socket); @@ -105,7 +116,8 @@ int64_t send_to_socket(socket_handle_t handle, const net_l4_endpoint* dst, const if (!socket) return SOCK_ERR_INVAL; int64_t res = SOCK_ERR_PROTO; - if (socket_core_protocol(socket) == PROTO_UDP) res = socket_sendto_udp(socket_core_impl(socket), dst, buf, len); + if (socket_core_special_kind(socket) != SOCKET_SPECIAL_NONE) res = SOCK_ERR_UNSUP; + else if (socket_core_protocol(socket) == PROTO_UDP) res = socket_sendto_udp(socket_core_impl(socket), dst, buf, len); socket_core_put(socket); return res; @@ -116,7 +128,8 @@ int64_t receive_from_socket(socket_handle_t handle, void* buf, uint64_t len, net if (!socket) return SOCK_ERR_INVAL; int64_t res = SOCK_ERR_PROTO; - if (socket_core_protocol(socket) == PROTO_TCP) res = socket_recv_tcp(socket_core_impl(socket), buf, len); + if (socket_core_special_kind(socket) != SOCKET_SPECIAL_NONE) res = SOCK_ERR_UNSUP; + else if (socket_core_protocol(socket) == PROTO_TCP) res = socket_recv_tcp(socket_core_impl(socket), buf, len); else if (socket_core_protocol(socket) == PROTO_UDP) res = socket_recvfrom_udp(socket_core_impl(socket), buf, len, out_src); socket_core_put(socket); @@ -148,7 +161,8 @@ int32_t listen_on(socket_handle_t handle, int32_t backlog){ if (!socket) return SOCK_ERR_INVAL; int32_t res = SOCK_ERR_PROTO; - if (socket_core_protocol(socket) == PROTO_TCP) res = socket_listen_tcp(socket_core_impl(socket), backlog); + if (socket_core_special_kind(socket) != SOCKET_SPECIAL_NONE) res = SOCK_ERR_UNSUP; + else if (socket_core_protocol(socket) == PROTO_TCP) res = socket_listen_tcp(socket_core_impl(socket), backlog); socket_core_put(socket); return res; @@ -157,6 +171,10 @@ int32_t listen_on(socket_handle_t handle, int32_t backlog){ socket_handle_t accept_on_socket(socket_handle_t handle) { ksocket_t* listener = socket_core_get(handle, get_current_proc_pid()); if (!listener) return 0; + if (socket_core_special_kind(listener) != SOCKET_SPECIAL_NONE) { + socket_core_put(listener); + return 0; + } if (socket_core_protocol(listener) != PROTO_TCP) { socket_core_put(listener); return 0; diff --git a/kernel/networking/transport_layer/csocket_special.c b/kernel/networking/transport_layer/csocket_special.c new file mode 100644 index 00000000..3b22ffe8 --- /dev/null +++ b/kernel/networking/transport_layer/csocket_special.c @@ -0,0 +1,41 @@ +#include "csocket_special.h" +#include "alloc/allocate.h" +#include "std/memory.h" + +typedef struct special_socket { + ksocket_t* ownerSocket; + SocketOptions options; +} special_socket_t; + +socket_impl_t socket_special_create(ksocket_t* owner, const SocketOptions* extra) { + if (!owner) return NULL; + + special_socket_t* s = (special_socket_t*)zalloc(sizeof(special_socket_t)); + if (!s) return NULL; + + s->ownerSocket = owner; + if (extra) s->options = *extra; + return s; +} + +void socket_destroy_special(socket_impl_t sh) { + special_socket_t* s = (special_socket_t*)sh; + if (!s) return; + release(s); +} + +int32_t socket_close_special(socket_impl_t sh) { + return sh ? SOCK_OK : SOCK_ERR_INVAL; +} + +int32_t socket_setopt_special(socket_impl_t sh, int32_t opt, const void* value, uint32_t len) { + special_socket_t* s = (special_socket_t*)sh; + if (!s) return SOCK_ERR_INVAL; + return socket_common_options_set(&s->options, opt, value, len); +} + +int32_t socket_getopt_special(socket_impl_t sh, int32_t opt, void* value, uint32_t* len) { + special_socket_t* s = (special_socket_t*)sh; + if (!s) return SOCK_ERR_INVAL; + return socket_common_options_get(&s->options, opt, value, len); +} diff --git a/kernel/networking/transport_layer/csocket_special.h b/kernel/networking/transport_layer/csocket_special.h new file mode 100644 index 00000000..2a2d446e --- /dev/null +++ b/kernel/networking/transport_layer/csocket_special.h @@ -0,0 +1,17 @@ +#pragma once + +#include "socket_core.h" + +#ifdef __cplusplus +extern "C" { +#endif + +socket_impl_t socket_special_create(ksocket_t* owner, const SocketOptions* extra); +void socket_destroy_special(socket_impl_t sh); +int32_t socket_close_special(socket_impl_t sh); +int32_t socket_setopt_special(socket_impl_t sh, int32_t opt, const void* value, uint32_t len); +int32_t socket_getopt_special(socket_impl_t sh, int32_t opt, void* value, uint32_t* len); + +#ifdef __cplusplus +} +#endif diff --git a/kernel/networking/transport_layer/csocket_tcp.c b/kernel/networking/transport_layer/csocket_tcp.c index 7e108ba3..fc016836 100644 --- a/kernel/networking/transport_layer/csocket_tcp.c +++ b/kernel/networking/transport_layer/csocket_tcp.c @@ -656,7 +656,7 @@ uint32_t tcp_accept_enqueue(ksocket_t* listener, ip_version_t ipver, const void* ksocket_t* child_owner = NULL; uint16_t owner_pid = socket_core_pid(s->ownerSocket); - if (!socket_core_alloc(PROTO_TCP, owner_pid, &child_owner)) return 0; + if (!socket_core_alloc(PROTO_TCP, SOCKET_SPECIAL_NONE, owner_pid, &child_owner)) return 0; tcp_socket_t* child = (tcp_socket_t*)socket_tcp_create(child_owner, &s->options); if (!child) { diff --git a/kernel/networking/transport_layer/socket_core.c b/kernel/networking/transport_layer/socket_core.c index f6b5cdc7..3f6ca352 100644 --- a/kernel/networking/transport_layer/socket_core.c +++ b/kernel/networking/transport_layer/socket_core.c @@ -9,6 +9,7 @@ struct ksocket { uint64_t generation; uint16_t pid; protocol_t protocol; + SocketSpecialKind special_kind; socket_impl_t impl; socket_impl_destroy_fn destroy; socket_impl_close_fn close; @@ -22,9 +23,8 @@ struct ksocket { static ksocket_t* sockets[SOCKET_MAX_OPEN]; static uint64_t generations[SOCKET_MAX_OPEN]; -bool socket_core_alloc(protocol_t protocol, uint16_t pid, ksocket_t** out_socket) { +bool socket_core_alloc(protocol_t protocol, SocketSpecialKind special_kind, uint16_t pid, ksocket_t** out_socket) { if (!out_socket) return false; - if (protocol == PROTO_NONE) return false; ksocket_t* socket = (ksocket_t*)zalloc(sizeof(ksocket_t)); if (!socket) return false; @@ -53,6 +53,7 @@ bool socket_core_alloc(protocol_t protocol, uint16_t pid, ksocket_t** out_socket socket->generation = gen; socket->pid = pid; socket->protocol = protocol; + socket->special_kind = special_kind; socket->refs = 1; sockets[id] = socket; @@ -173,6 +174,7 @@ int32_t socket_core_get_option(ksocket_t* socket, int32_t opt, void* value, uint uint32_t v = 0; if (opt == SOCK_GET_PROTOCOL) v = socket->protocol; else if (opt == SOCK_GET_OWNER_PID) v = socket->pid; + else if (opt == SOCK_GET_SPECIAL_KIND) v = socket->special_kind; else return socket->getopt(socket->impl, opt, value, len); if (!value) { @@ -283,6 +285,9 @@ protocol_t socket_core_protocol(const ksocket_t* socket) { return socket ? socket->protocol : PROTO_NONE; } +SocketSpecialKind socket_core_special_kind(const ksocket_t* socket) { + return socket ? socket->special_kind : SOCKET_SPECIAL_NONE; +} uint16_t socket_core_pid(const ksocket_t* socket) { return socket ? socket->pid : 0; diff --git a/kernel/networking/transport_layer/socket_core.h b/kernel/networking/transport_layer/socket_core.h index 117a20fe..9560f92a 100644 --- a/kernel/networking/transport_layer/socket_core.h +++ b/kernel/networking/transport_layer/socket_core.h @@ -19,7 +19,7 @@ typedef int32_t (*socket_impl_getopt_fn)(socket_impl_t impl, int32_t opt, void* #define SOCKET_MAX_OPEN 2048 #define SOCKET_HANDLE_INDEX_BITS 20 -bool socket_core_alloc(protocol_t protocol, uint16_t pid, ksocket_t** out_socket); +bool socket_core_alloc(protocol_t protocol, SocketSpecialKind special_kind, uint16_t pid, ksocket_t** out_socket); bool socket_core_attach_impl(ksocket_t* socket, socket_impl_t impl, socket_impl_destroy_fn destroy, socket_impl_close_fn close, socket_impl_setopt_fn setopt, socket_impl_getopt_fn getopt); ksocket_t* socket_core_get(socket_handle_t handle, uint16_t pid); void socket_core_ref(ksocket_t* socket); @@ -33,6 +33,7 @@ int32_t socket_common_options_get(const SocketOptions* opts, int32_t opt, void* socket_impl_t socket_core_impl(ksocket_t* socket); protocol_t socket_core_protocol(const ksocket_t* socket); +SocketSpecialKind socket_core_special_kind(const ksocket_t* socket); uint16_t socket_core_pid(const ksocket_t* socket); bool socket_core_is_closing(const ksocket_t* socket); socket_handle_t socket_core_export_handle(const ksocket_t* socket); From 0aa06e1d5dade81a7e3a9416bd8858d2748da611 Mon Sep 17 00:00:00 2001 From: CodeAnarchist Date: Fri, 19 Jun 2026 15:41:04 +0200 Subject: [PATCH 27/39] [socket] control socket --- kernel/networking/interface_manager.c | 19 + kernel/networking/interface_manager.h | 27 +- kernel/networking/internet_layer/ipv4_route.c | 14 + kernel/networking/internet_layer/ipv4_route.h | 3 + kernel/networking/internet_layer/ipv6.c | 2 +- kernel/networking/internet_layer/ipv6_route.c | 14 + kernel/networking/internet_layer/ipv6_route.h | 3 + kernel/networking/link_layer/arp.c | 21 + kernel/networking/link_layer/arp.h | 2 + kernel/networking/link_layer/ndp.c | 39 +- kernel/networking/link_layer/ndp.h | 6 +- kernel/networking/transport_layer/csocket.c | 4 +- .../transport_layer/csocket_special.c | 47 ++ .../transport_layer/csocket_special.h | 2 + kernel/networking/transport_layer/net_ctrl.c | 598 ++++++++++++++++++ kernel/networking/transport_layer/net_ctrl.h | 14 + 16 files changed, 779 insertions(+), 36 deletions(-) create mode 100644 kernel/networking/transport_layer/net_ctrl.c create mode 100644 kernel/networking/transport_layer/net_ctrl.h diff --git a/kernel/networking/interface_manager.c b/kernel/networking/interface_manager.c index 4eb48154..70d66b9e 100644 --- a/kernel/networking/interface_manager.c +++ b/kernel/networking/interface_manager.c @@ -161,6 +161,25 @@ bool l2_interface_set_up(uint8_t ifindex, bool up) { return true; } +bool l2_interface_set_metric(uint8_t ifindex, uint16_t metric) { + l2_interface_t* itf = l2_interface_find_by_index(ifindex); + if (!itf) return false; + if (itf->base_metric == metric) return true; + itf->base_metric = metric; + for (int i = 0; i < MAX_IPV4_PER_INTERFACE; i++) { + l3_ipv4_interface_t* v4 = itf->l3_v4[i]; + if (!v4 || !v4->routing_table) continue; + ipv4_rt_sync_basics((ipv4_rt_table_t*)v4->routing_table, v4->ip, v4->mask, v4->gw, itf->base_metric); + } + for (int i = 0; i < MAX_IPV6_PER_INTERFACE; i++) { + l3_ipv6_interface_t* v6 = itf->l3_v6[i]; + if (!v6 || !v6->routing_table) continue; + ipv6_rt_sync_basics((ipv6_rt_table_t*)v6->routing_table, v6->ip, v6->prefix_len, v6->gateway, itf->base_metric); + } + net_interface_mark_changed(); + return true; +} + static bool l2_sync_multicast_filters(l2_interface_t* itf) { if (!itf) return false; uint8_t macs[(MAX_IPV4_MCAST_PER_INTERFACE + MAX_IPV6_MCAST_PER_INTERFACE) * 6]; diff --git a/kernel/networking/interface_manager.h b/kernel/networking/interface_manager.h index c2792131..5d265806 100644 --- a/kernel/networking/interface_manager.h +++ b/kernel/networking/interface_manager.h @@ -1,6 +1,7 @@ #pragma once #include "types.h" +#include "net/interface_types.h" #ifdef __cplusplus extern "C" { @@ -15,31 +16,6 @@ extern "C" { #define MAX_IPV4_MCAST_PER_INTERFACE 16 #define MAX_IPV6_MCAST_PER_INTERFACE 32 -typedef enum { - IPV4_CFG_DISABLED = -1, - IPV4_CFG_DHCP = 0, - IPV4_CFG_STATIC = 1 -} ipv4_cfg_t; - -typedef enum { - IPV6_DAD_NONE = 0, - IPV6_DAD_IN_PROGRESS = 1, - IPV6_DAD_FAILED = 2, - IPV6_DAD_OK = 3 -} ipv6_dad_state_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; @@ -159,6 +135,7 @@ 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_interface_set_metric(uint8_t ifindex, uint16_t metric); bool l2_ipv4_mcast_join(uint8_t ifindex, uint32_t group); bool l2_ipv4_mcast_leave(uint8_t ifindex, uint32_t group); diff --git a/kernel/networking/internet_layer/ipv4_route.c b/kernel/networking/internet_layer/ipv4_route.c index 28eb4761..d06f6fb9 100644 --- a/kernel/networking/internet_layer/ipv4_route.c +++ b/kernel/networking/internet_layer/ipv4_route.c @@ -132,6 +132,20 @@ bool ipv4_rt_del_in(ipv4_rt_table_t* t, uint32_t network, uint32_t mask) { return false; } +int ipv4_rt_count(const ipv4_rt_table_t* t) { + return t ? t->len : 0; +} + +bool ipv4_rt_get(const ipv4_rt_table_t* t, int index, ipv4_rt_entry_t* out) { + if (!t || !out || index < 0 || index >= t->len) return false; + *out = t->e[index]; + return true; +} + +uint32_t ipv4_rt_epoch(const ipv4_rt_table_t* t) { + return t ? t->epoch : 0; +} + 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; diff --git a/kernel/networking/internet_layer/ipv4_route.h b/kernel/networking/internet_layer/ipv4_route.h index 45e49487..82a39c5a 100644 --- a/kernel/networking/internet_layer/ipv4_route.h +++ b/kernel/networking/internet_layer/ipv4_route.h @@ -23,6 +23,9 @@ 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); +int ipv4_rt_count(const ipv4_rt_table_t* t); +bool ipv4_rt_get(const ipv4_rt_table_t* t, int index, ipv4_rt_entry_t* out); +uint32_t ipv4_rt_epoch(const ipv4_rt_table_t* t); 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); diff --git a/kernel/networking/internet_layer/ipv6.c b/kernel/networking/internet_layer/ipv6.c index 92bf648b..1f05d3ca 100644 --- a/kernel/networking/internet_layer/ipv6.c +++ b/kernel/networking/internet_layer/ipv6.c @@ -394,7 +394,7 @@ void ipv6_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { if (ipv6_is_linklocal(ip6->dst) && !ipv6_is_unspecified(ip6->src) && !ipv6_is_linklocal(ip6->src)) return; - if (ifindex && !ipv6_is_unspecified(ip6->src) && src_mac) ndp_table_put_for_l2((uint8_t)ifindex, ip6->src, src_mac, 180000, false); + if (ifindex && !ipv6_is_unspecified(ip6->src) && src_mac) ndp_table_put_for_l2((uint8_t)ifindex, ip6->src, src_mac, 180000, false, false); uint8_t nh = ip6->next_header; diff --git a/kernel/networking/internet_layer/ipv6_route.c b/kernel/networking/internet_layer/ipv6_route.c index 858077e4..48765ad5 100644 --- a/kernel/networking/internet_layer/ipv6_route.c +++ b/kernel/networking/internet_layer/ipv6_route.c @@ -155,6 +155,20 @@ bool ipv6_rt_del_in(ipv6_rt_table_t* t, const uint8_t net[16], uint8_t plen) { return false; } +int ipv6_rt_count(const ipv6_rt_table_t* t) { + return t ? t->len : 0; +} + +bool ipv6_rt_get(const ipv6_rt_table_t* t, int index, ipv6_rt_entry_t* out) { + if (!t || !out || index < 0 || index >= t->len) return false; + *out = t->e[index]; + return true; +} + +uint32_t ipv6_rt_epoch(const ipv6_rt_table_t* t) { + return t ? t->epoch : 0; +} + bool ipv6_rt_lookup_in(const ipv6_rt_table_t* t, const uint8_t dst[16], uint8_t next_hop[16], int* out_pl, int* out_metric) { if (!t) return false; diff --git a/kernel/networking/internet_layer/ipv6_route.h b/kernel/networking/internet_layer/ipv6_route.h index f8973dcb..6b714131 100644 --- a/kernel/networking/internet_layer/ipv6_route.h +++ b/kernel/networking/internet_layer/ipv6_route.h @@ -23,6 +23,9 @@ void ipv6_rt_clear(ipv6_rt_table_t* t); bool ipv6_rt_add_in(ipv6_rt_table_t* t, const uint8_t net[16], uint8_t plen, const uint8_t gw[16], uint16_t metric); bool ipv6_rt_del_in(ipv6_rt_table_t* t, const uint8_t net[16], uint8_t plen); +int ipv6_rt_count(const ipv6_rt_table_t* t); +bool ipv6_rt_get(const ipv6_rt_table_t* t, int index, ipv6_rt_entry_t* out); +uint32_t ipv6_rt_epoch(const ipv6_rt_table_t* t); bool ipv6_rt_lookup_in(const ipv6_rt_table_t* t, const uint8_t dst[16], uint8_t next_hop[16], int* out_prefix_len, int* out_metric); void ipv6_rt_ensure_basics(ipv6_rt_table_t* t, const uint8_t ip[16], uint8_t plen, const uint8_t gw[16], uint16_t base_metric); diff --git a/kernel/networking/link_layer/arp.c b/kernel/networking/link_layer/arp.c index 13726de6..cdf0e2e7 100644 --- a/kernel/networking/link_layer/arp.c +++ b/kernel/networking/link_layer/arp.c @@ -146,6 +146,27 @@ bool arp_table_get_for_l2(uint8_t ifindex, uint32_t ip, uint8_t mac_out[6]){ return false; } +bool arp_table_delete_for_l2(uint8_t ifindex, uint32_t ip) { + arp_table_t* t = l2_arp(ifindex); + if (!t || !ip) return false; + int idx = arp_find_slot(t, ip); + if (idx < 0) return false; + arp_entry_clear(&t->entries[idx]); + return true; +} + +uint32_t arp_table_dump_for_l2(uint8_t ifindex, arp_entry_t* out, uint32_t out_cap) { + arp_table_t* t = l2_arp(ifindex); + if (!t || !out || !out_cap) return 0; + uint32_t n = 0; + for (int i = 0; i < ARP_TABLE_MAX && n < out_cap; i++) { + arp_entry_t* e = &t->entries[i]; + if (e->state == ARP_STATE_UNUSED) continue; + out[n++] = *e; + } + return n; +} + void arp_table_tick_for_l2(uint8_t ifindex, uint32_t ms){ arp_table_t* t = l2_arp(ifindex); if (!t) return; diff --git a/kernel/networking/link_layer/arp.h b/kernel/networking/link_layer/arp.h index f306eb53..dd5a822b 100644 --- a/kernel/networking/link_layer/arp.h +++ b/kernel/networking/link_layer/arp.h @@ -53,6 +53,8 @@ void arp_table_init_static_defaults(arp_table_t* t); 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]); +bool arp_table_delete_for_l2(uint8_t ifindex, uint32_t ip); +uint32_t arp_table_dump_for_l2(uint8_t ifindex, arp_entry_t* out, uint32_t out_cap); void arp_table_tick_for_l2(uint8_t ifindex, uint32_t ms); void arp_tick_all(uint32_t ms); diff --git a/kernel/networking/link_layer/ndp.c b/kernel/networking/link_layer/ndp.c index e90eb7bd..1364b276 100644 --- a/kernel/networking/link_layer/ndp.c +++ b/kernel/networking/link_layer/ndp.c @@ -386,6 +386,7 @@ static int ndp_find_replacement(ndp_table_impl_t* t) { ndp_entry_t* e = &t->entries[i]; if (e->pending_len) continue; if (e->state == NDP_STATE_UNUSED || e->ttl_ms == 0) return i; + if (e->static_entry) continue; if (e->is_router && e->router_lifetime_ms) continue; if (e->ttl_ms < best_ttl) { best_ttl = e->ttl_ms; @@ -396,7 +397,7 @@ static int ndp_find_replacement(ndp_table_impl_t* t) { return best; } -void ndp_table_put_for_l2(uint8_t ifindex, const uint8_t ip[16], const uint8_t mac[6], uint32_t ttl_ms, bool router) { +void ndp_table_put_for_l2(uint8_t ifindex, const uint8_t ip[16], const uint8_t mac[6], uint32_t ttl_ms, bool router, bool is_static) { ndp_table_impl_t* t = l2_ndp(ifindex); if (!t) return; @@ -416,14 +417,16 @@ void ndp_table_put_for_l2(uint8_t ifindex, const uint8_t ip[16], const uint8_t m e->timer_ms = g_ndp_reachable_time_ms; } - if (ttl_ms == 0) { + if (is_static) ttl_ms = UINT32_MAX; + else if (ttl_ms == 0) { ttl_ms = g_ndp_reachable_time_ms * 4; if (ttl_ms == 0) ttl_ms = 1; } e->ttl_ms = ttl_ms; e->is_router = router ? 1 : 0; - e->router_lifetime_ms = router ? ttl_ms : 0; + e->static_entry = is_static ? 1 : 0; + e->router_lifetime_ms = router && !is_static ? ttl_ms : 0; e->probes_sent = 0; if (e->pending) { @@ -439,7 +442,7 @@ void ndp_table_put_for_l2(uint8_t ifindex, const uint8_t ip[16], const uint8_t m } } -static bool ndp_table_get_for_l2(uint8_t ifindex, const uint8_t ip[16], uint8_t mac_out[6]) { +bool ndp_table_get_for_l2(uint8_t ifindex, const uint8_t ip[16], uint8_t mac_out[6]) { ndp_table_impl_t* t = l2_ndp(ifindex); if (!t) return false; @@ -457,6 +460,27 @@ static bool ndp_table_get_for_l2(uint8_t ifindex, const uint8_t ip[16], uint8_t return false; } +bool ndp_table_delete_for_l2(uint8_t ifindex, const uint8_t ip[16]) { + ndp_table_impl_t* t = l2_ndp(ifindex); + if (!t || !ip) return false; + int idx = ndp_find_slot(t, ip); + if (idx < 0) return false; + ndp_entry_clear(&t->entries[idx]); + return true; +} + +uint32_t ndp_table_dump_for_l2(uint8_t ifindex, ndp_entry_t* out, uint32_t out_cap) { + ndp_table_impl_t* t = l2_ndp(ifindex); + if (!t || !out || !out_cap) return 0; + uint32_t n = 0; + for (int i = 0; i < NDP_TABLE_MAX && n < out_cap; i++) { + ndp_entry_t* e = &t->entries[i]; + if (e->state == NDP_STATE_UNUSED) continue; + out[n++] = *e; + } + return n; +} + static bool ndp_send_na_on(uint8_t ifindex, const uint8_t dst_ip[16], const uint8_t src_ip[16], const uint8_t target_ip[16], const uint8_t dst_mac_in[6], const uint8_t my_mac[6], uint8_t solicited) { uint32_t plen = (uint32_t)(sizeof(icmpv6_na_t) + sizeof(icmpv6_opt_lladdr_t)); uintptr_t buf = (uintptr_t)zalloc(plen ? plen : 1u); @@ -614,6 +638,7 @@ static void ndp_table_tick_for_l2(uint8_t ifindex, uint32_t ms) { continue; } + if (e->static_entry) continue; if (e->ttl_ms <= ms) { ndp_entry_clear(e); continue; @@ -838,7 +863,7 @@ void ndp_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[ break; } } - if (!src_is_local) ndp_table_put_for_l2((uint8_t)ifindex, src_ip, src_mac, 180000, false); + if (!src_is_local) ndp_table_put_for_l2((uint8_t)ifindex, src_ip, src_mac, 180000, false, false); uint8_t src_my[16] = {0}; @@ -981,8 +1006,8 @@ void ndp_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[ uint32_t router_lifetime_ms = (uint32_t)router_lifetime * 1000u; - if (router_lifetime == 0) ndp_table_put_for_l2((uint8_t)ifindex, src_ip, src_mac, 180000, false); - else ndp_table_put_for_l2((uint8_t)ifindex, src_ip, src_mac, router_lifetime_ms, true); + if (router_lifetime == 0) ndp_table_put_for_l2((uint8_t)ifindex, src_ip, src_mac, 180000, false, false); + else ndp_table_put_for_l2((uint8_t)ifindex, src_ip, src_mac, router_lifetime_ms, true, false); if (reachable_time) g_ndp_reachable_time_ms = reachable_time; if (retrans_timer) g_ndp_retrans_timer_ms = retrans_timer; diff --git a/kernel/networking/link_layer/ndp.h b/kernel/networking/link_layer/ndp.h index 90b16074..6d799f16 100644 --- a/kernel/networking/link_layer/ndp.h +++ b/kernel/networking/link_layer/ndp.h @@ -34,6 +34,7 @@ typedef struct { uint8_t state; uint8_t probes_sent; uint8_t is_router; + uint8_t static_entry; uint32_t router_lifetime_ms; } ndp_entry_t; @@ -44,7 +45,10 @@ void ndp_table_destroy(ndp_table_t* t); void ndp_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[16], const uint8_t src_mac[6], netpkt_t* pkt); -void ndp_table_put_for_l2(uint8_t ifindex, const uint8_t ip[16], const uint8_t mac[6], uint32_t ttl_ms, bool router); +void ndp_table_put_for_l2(uint8_t ifindex, const uint8_t ip[16], const uint8_t mac[6], uint32_t ttl_ms, bool router, bool is_static); +bool ndp_table_get_for_l2(uint8_t ifindex, const uint8_t ip[16], uint8_t mac_out[6]); +bool ndp_table_delete_for_l2(uint8_t ifindex, const uint8_t ip[16]); +uint32_t ndp_table_dump_for_l2(uint8_t ifindex, ndp_entry_t* out, uint32_t out_cap); bool ndp_send_or_queue_on(uint16_t ifindex, const uint8_t next_hop[16], netpkt_t* pkt); diff --git a/kernel/networking/transport_layer/csocket.c b/kernel/networking/transport_layer/csocket.c index 2ba93f2c..ee6ec140 100644 --- a/kernel/networking/transport_layer/csocket.c +++ b/kernel/networking/transport_layer/csocket.c @@ -102,7 +102,7 @@ int64_t send_on_socket(socket_handle_t handle, const void* buf, uint64_t len){ if (!socket) return SOCK_ERR_INVAL; int64_t res = SOCK_ERR_PROTO; - if (socket_core_special_kind(socket) != SOCKET_SPECIAL_NONE) res = SOCK_ERR_UNSUP; + if (socket_core_special_kind(socket) != SOCKET_SPECIAL_NONE) res = socket_send_special(socket_core_impl(socket), buf, len); else if (socket_core_protocol(socket) == PROTO_TCP) res = socket_send_tcp(socket_core_impl(socket), buf, len); else if (socket_core_protocol(socket) == PROTO_UDP) res = socket_sendto_udp(socket_core_impl(socket), NULL, buf, len); @@ -128,7 +128,7 @@ int64_t receive_from_socket(socket_handle_t handle, void* buf, uint64_t len, net if (!socket) return SOCK_ERR_INVAL; int64_t res = SOCK_ERR_PROTO; - if (socket_core_special_kind(socket) != SOCKET_SPECIAL_NONE) res = SOCK_ERR_UNSUP; + if (socket_core_special_kind(socket) != SOCKET_SPECIAL_NONE) res = socket_recv_special(socket_core_impl(socket), buf, len); else if (socket_core_protocol(socket) == PROTO_TCP) res = socket_recv_tcp(socket_core_impl(socket), buf, len); else if (socket_core_protocol(socket) == PROTO_UDP) res = socket_recvfrom_udp(socket_core_impl(socket), buf, len, out_src); diff --git a/kernel/networking/transport_layer/csocket_special.c b/kernel/networking/transport_layer/csocket_special.c index 3b22ffe8..f2720446 100644 --- a/kernel/networking/transport_layer/csocket_special.c +++ b/kernel/networking/transport_layer/csocket_special.c @@ -1,10 +1,14 @@ #include "csocket_special.h" +#include "net_ctrl.h" #include "alloc/allocate.h" #include "std/memory.h" typedef struct special_socket { ksocket_t* ownerSocket; SocketOptions options; + uint8_t* rx_buf; + uint32_t rx_len; + uint32_t rx_off; } special_socket_t; socket_impl_t socket_special_create(ksocket_t* owner, const SocketOptions* extra) { @@ -21,6 +25,7 @@ socket_impl_t socket_special_create(ksocket_t* owner, const SocketOptions* extra void socket_destroy_special(socket_impl_t sh) { special_socket_t* s = (special_socket_t*)sh; if (!s) return; + if (s->rx_buf) release(s->rx_buf); release(s); } @@ -39,3 +44,45 @@ int32_t socket_getopt_special(socket_impl_t sh, int32_t opt, void* value, uint32 if (!s) return SOCK_ERR_INVAL; return socket_common_options_get(&s->options, opt, value, len); } + +int64_t socket_send_special(socket_impl_t sh, const void* buf, uint64_t len) { + special_socket_t* s = (special_socket_t*)sh; + if (!s || (!buf && len) || len > UINT32_MAX) return SOCK_ERR_INVAL; + if (socket_core_special_kind(s->ownerSocket) != SOCKET_SPECIAL_CTRL) return SOCK_ERR_UNSUP; + + if (s->rx_buf) { + release(s->rx_buf); + s->rx_buf = NULL; + s->rx_len = 0; + s->rx_off = 0; + } + + uint8_t* out = NULL; + uint32_t out_len = 0; + int32_t rc = net_ctrl_dispatch(buf, (uint32_t)len, &out, &out_len); + if (rc != SOCK_OK) return rc; + + s->rx_buf = out; + s->rx_len = out_len; + s->rx_off = 0; + return (int64_t)len; +} + +int64_t socket_recv_special(socket_impl_t sh, void* buf, uint64_t len) { + special_socket_t* s = (special_socket_t*)sh; + if (!s || (!buf && len) || len > UINT32_MAX) return SOCK_ERR_INVAL; + if (socket_core_special_kind(s->ownerSocket) != SOCKET_SPECIAL_CTRL) return SOCK_ERR_UNSUP; + if (!s->rx_buf || s->rx_off >= s->rx_len) return SOCK_ERR_WOULDBLOCK; + + uint32_t avail = s->rx_len - s->rx_off; + uint32_t n = avail < (uint32_t)len ? avail : (uint32_t)len; + if (n) memcpy(buf, s->rx_buf + s->rx_off, n); + s->rx_off += n; + if (s->rx_off >= s->rx_len) { + release(s->rx_buf); + s->rx_buf = NULL; + s->rx_len = 0; + s->rx_off = 0; + } + return n; +} diff --git a/kernel/networking/transport_layer/csocket_special.h b/kernel/networking/transport_layer/csocket_special.h index 2a2d446e..716ef457 100644 --- a/kernel/networking/transport_layer/csocket_special.h +++ b/kernel/networking/transport_layer/csocket_special.h @@ -11,6 +11,8 @@ void socket_destroy_special(socket_impl_t sh); int32_t socket_close_special(socket_impl_t sh); int32_t socket_setopt_special(socket_impl_t sh, int32_t opt, const void* value, uint32_t len); int32_t socket_getopt_special(socket_impl_t sh, int32_t opt, void* value, uint32_t* len); +int64_t socket_send_special(socket_impl_t sh, const void* buf, uint64_t len); +int64_t socket_recv_special(socket_impl_t sh, void* buf, uint64_t len); #ifdef __cplusplus } diff --git a/kernel/networking/transport_layer/net_ctrl.c b/kernel/networking/transport_layer/net_ctrl.c new file mode 100644 index 00000000..0e03acf3 --- /dev/null +++ b/kernel/networking/transport_layer/net_ctrl.c @@ -0,0 +1,598 @@ +#include "net_ctrl.h" +#include "files/buffer.h" +#include "net/socket_types.h" +#include "std/memory.h" +#include "networking/interface_manager.h" +#include "networking/internet_layer/ipv4_route.h" +#include "networking/internet_layer/ipv4_utils.h" +#include "networking/internet_layer/ipv6_route.h" +#include "networking/internet_layer/ipv6_utils.h" +#include "networking/link_layer/arp.h" +#include "networking/link_layer/ndp.h" +#include "networking/network.h" + +typedef struct { + uint8_t ifindex; + uint8_t l3_id; + uint8_t prefix_len; + uint8_t ifname_len; + char ifname[16]; + uint8_t mac[6]; + uint16_t metric; + uint16_t mtu; + uint16_t flags; + int16_t config; + uint8_t state; + uint8_t kind; + uint8_t dad_state; + uint32_t ttl_ms; + uint32_t present; + net_l4_endpoint address; + net_l4_endpoint gateway; +} net_ctrl_attrs_t; + +static bool net_ctrl_read_attrs(const uint8_t* p, uint32_t len, net_ctrl_attrs_t* out) { + if (!out)return false; + memset(out, 0, sizeof(*out)); + uint32_t off = 0; + while (off < len) { + if (len - off < sizeof(NetCtrlAttr)) return false; + const NetCtrlAttr* attr = (const NetCtrlAttr*)(p + off); + NetCtrlAttr a; + memcpy(&a, attr, sizeof(a)); + off += sizeof(a); + if (a.length > len - off) return false; + const uint8_t* v = NET_CTRL_ATTR_CONST_DATA(attr); + switch ((uint32_t)a.ext) { + case NET_CTRL_EXT_IFINDEX: + if (a.length != sizeof(uint8_t)) return false; + out->ifindex = v[0]; + out->present |= 1u << NET_CTRL_EXT_IFINDEX; + break; + case NET_CTRL_EXT_IFNAME: + if (!a.length || a.length > sizeof(out->ifname)) return false; + memcpy(out->ifname, v, a.length); + out->ifname_len = (uint8_t)a.length; + out->present |= 1u << NET_CTRL_EXT_IFNAME; + break; + case NET_CTRL_EXT_L3_ID: + if (a.length != sizeof(uint8_t)) return false; + out->l3_id = v[0]; + out->present |= 1u << NET_CTRL_EXT_L3_ID; + break; + case NET_CTRL_EXT_PREFIX_LEN: + if (a.length != sizeof(uint8_t)) return false; + out->prefix_len = v[0]; + out->present |= 1u << NET_CTRL_EXT_PREFIX_LEN; + break; + case NET_CTRL_EXT_METRIC: + if (a.length != sizeof(uint16_t)) return false; + memcpy(&out->metric, v, sizeof(out->metric)); + out->present |= 1u << NET_CTRL_EXT_METRIC; + break; + case NET_CTRL_EXT_MTU: + if (a.length != sizeof(uint16_t)) return false; + memcpy(&out->mtu, v, sizeof(out->mtu)); + out->present |= 1u << NET_CTRL_EXT_MTU; + break; + case NET_CTRL_EXT_FLAGS: + if (a.length != sizeof(uint16_t)) return false; + memcpy(&out->flags, v, sizeof(out->flags)); + out->present |= 1u << NET_CTRL_EXT_FLAGS; + break; + case NET_CTRL_EXT_STATE: + if (a.length != sizeof(uint8_t)) return false; + out->state = v[0]; + out->present |= 1u << NET_CTRL_EXT_STATE; + break; + case NET_CTRL_EXT_CONFIG: + if (a.length != sizeof(int16_t)) return false; + memcpy(&out->config, v, sizeof(out->config)); + out->present |= 1u << NET_CTRL_EXT_CONFIG; + break; + case NET_CTRL_EXT_KIND: + if (a.length != sizeof(uint8_t)) return false; + out->kind = v[0]; + out->present |= 1u << NET_CTRL_EXT_KIND; + break; + case NET_CTRL_EXT_DAD_STATE: + if (a.length != sizeof(uint8_t)) return false; + out->dad_state = v[0]; + out->present |= 1u << NET_CTRL_EXT_DAD_STATE; + break; + case NET_CTRL_EXT_TTL_MS: + if (a.length != sizeof(uint32_t)) return false; + memcpy(&out->ttl_ms, v, sizeof(out->ttl_ms)); + out->present |= 1u << NET_CTRL_EXT_TTL_MS; + break; + case NET_CTRL_EXT_ADDRESS: + if (a.length != sizeof(net_l4_endpoint)) return false; + memcpy(&out->address, v, sizeof(out->address)); + out->present |= 1u << NET_CTRL_EXT_ADDRESS; + break; + case NET_CTRL_EXT_GATEWAY: + if (a.length != sizeof(net_l4_endpoint)) return false; + memcpy(&out->gateway, v, sizeof(out->gateway)); + out->present |= 1u << NET_CTRL_EXT_GATEWAY; + break; + case NET_CTRL_EXT_MAC: + if (a.length != sizeof(out->mac)) return false; + memcpy(out->mac, v, sizeof(out->mac)); + out->present |= 1u << NET_CTRL_EXT_MAC; + break; + //case NET_CTRL_EXT_NONE: + default: + break; + } + off += a.length; + } + return true; +} + +static bool net_ctrl_ifname_matches(const l2_interface_t* l2, const net_ctrl_attrs_t* a) { + if (!NET_CTRL_HAS(a, NET_CTRL_EXT_IFNAME)) return true; + if (memcmp(l2->name, a->ifname, a->ifname_len) != 0) return false; + return a->ifname_len == sizeof(a->ifname) || l2->name[a->ifname_len] == '\0'; +} + +static inline bool net_ctrl_l2_matches(const l2_interface_t* l2, const net_ctrl_attrs_t* a) { + if (!l2) return false; + if (NET_CTRL_HAS(a, NET_CTRL_EXT_IFINDEX) && l2->ifindex != a->ifindex) return false; + return net_ctrl_ifname_matches(l2, a); +} + +static l2_interface_t* net_ctrl_l2_from_attrs(const net_ctrl_attrs_t* a) { + if (NET_CTRL_HAS(a, NET_CTRL_EXT_IFINDEX)) { + l2_interface_t* l2 = l2_interface_find_by_index(a->ifindex); + if (!l2 || !net_ctrl_ifname_matches(l2, a)) return NULL; + return l2; + } + if (!NET_CTRL_HAS(a, NET_CTRL_EXT_IFNAME)) return NULL; + uint8_t count = l2_interface_count(); + for (uint8_t i = 0; i < count; i++) { + l2_interface_t* l2 = l2_interface_at(i); + if (l2 && net_ctrl_ifname_matches(l2, a)) return l2; + } + return NULL; +} + +static bool net_ctrl_link_dump(const net_ctrl_attrs_t* a, buffer* b) { + uint8_t count = l2_interface_count(); + for (uint8_t i = 0; i < count; i++) { + l2_interface_t* l2 = l2_interface_at(i); + if (!net_ctrl_l2_matches(l2, a)) continue; + NetCtrlLinkInfo info; + memset(&info, 0, sizeof(info)); + info.ifindex = l2->ifindex; + info.up = l2->is_up ? 1 : 0; + info.metric = l2->base_metric; + info.mtu = network_get_mtu(l2->ifindex); + info.kind = l2->kind; + info.ipv4_count = l2->ipv4_count; + info.ipv6_count = l2->ipv6_count; + memcpy(info.name, l2->name, sizeof(info.name)); + if (buffer_write_lim(b, (const char*)&info, sizeof(info)) != sizeof(info)) return false; + } + return true; +} + +static int32_t net_ctrl_link_upd(const net_ctrl_attrs_t* a) { + l2_interface_t* l2 = net_ctrl_l2_from_attrs(a); + if (!l2) return SOCK_ERR_INVAL; + if (NET_CTRL_HAS(a, NET_CTRL_EXT_MTU)) return SOCK_ERR_UNSUP; + if (NET_CTRL_HAS(a, NET_CTRL_EXT_STATE) && !l2_interface_set_up(l2->ifindex, a->state != 0)) return SOCK_ERR_INVAL; + if (NET_CTRL_HAS(a, NET_CTRL_EXT_METRIC) && !l2_interface_set_metric(l2->ifindex, a->metric)) return SOCK_ERR_INVAL; + return SOCK_OK; +} + +static bool net_ctrl_addr_dump(const net_ctrl_attrs_t* a, buffer* b) { + uint8_t count = l2_interface_count(); + for (uint8_t i = 0; i < count; i++) { + l2_interface_t* l2 = l2_interface_at(i); + if (!net_ctrl_l2_matches(l2, a)) continue; + for (int j = 0; j < MAX_IPV4_PER_INTERFACE; j++) { + l3_ipv4_interface_t* v4 = l2->l3_v4[j]; + if (!v4) continue; + if (NET_CTRL_HAS(a, NET_CTRL_EXT_L3_ID) && v4->l3_id != a->l3_id) continue; + NetCtrlAddrInfo info; + memset(&info, 0, sizeof(info)); + info.prefix.ifindex = l2->ifindex; + info.prefix.l3_id = v4->l3_id; + int prefix_len = ipv4_prefix_len(v4->mask); + info.prefix.prefix_len = prefix_len < 0 ? 0 : (uint8_t)prefix_len; + info.config = v4->mode; + info.epoch = v4->epoch; + info.mtu = v4->runtime_opts_v4.mtu ? v4->runtime_opts_v4.mtu : network_get_mtu(l2->ifindex); + info.prefix.address.ver = IP_VER4; + info.prefix.gateway.ver = IP_VER4; + memcpy(info.prefix.address.ip, &v4->ip, sizeof(v4->ip)); + memcpy(info.prefix.gateway.ip, &v4->gw, sizeof(v4->gw)); + if (buffer_write_lim(b, (const char*)&info, sizeof(info)) != sizeof(info)) return false; + } + for (int j = 0; j < MAX_IPV6_PER_INTERFACE; j++) { + l3_ipv6_interface_t* v6 = l2->l3_v6[j]; + if (!v6) continue; + if (NET_CTRL_HAS(a, NET_CTRL_EXT_L3_ID) && v6->l3_id != a->l3_id) continue; + NetCtrlAddrInfo info; + memset(&info, 0, sizeof(info)); + info.prefix.ifindex = l2->ifindex; + info.prefix.l3_id = v6->l3_id; + info.prefix.prefix_len = v6->prefix_len; + info.kind = v6->kind; + info.config = v6->cfg; + info.epoch = v6->epoch; + info.mtu = v6->mtu ? v6->mtu : network_get_mtu(l2->ifindex); + info.dad_state = v6->dad_state; + info.prefix.address.ver = IP_VER6; + info.prefix.gateway.ver = IP_VER6; + ipv6_cpy(info.prefix.address.ip, v6->ip); + ipv6_cpy(info.prefix.gateway.ip, v6->gateway); + if (buffer_write_lim(b, (const char*)&info, sizeof(info)) != sizeof(info)) return false; + } + } + return true; +} + +static int32_t net_ctrl_addr_apply(const net_ctrl_attrs_t* a, bool update) { + if (!NET_CTRL_HAS(a, NET_CTRL_EXT_ADDRESS)) return SOCK_ERR_INVAL; + if (NET_CTRL_HAS(a, NET_CTRL_EXT_MTU)) return SOCK_ERR_UNSUP; + if (NET_CTRL_HAS(a, NET_CTRL_EXT_DAD_STATE)) return SOCK_ERR_UNSUP; + if (update && !NET_CTRL_HAS(a, NET_CTRL_EXT_L3_ID)) return SOCK_ERR_INVAL; + l2_interface_t* l2 = update ? NULL : net_ctrl_l2_from_attrs(a); + if (!update && (!l2 || NET_CTRL_HAS(a, NET_CTRL_EXT_L3_ID))) return SOCK_ERR_INVAL; + if (a->address.ver == IP_VER4) { + uint32_t ip = 0; + uint32_t gw = 0; + int16_t config = NET_CTRL_HAS(a, NET_CTRL_EXT_CONFIG) ? a->config : IPV4_CFG_STATIC; + if ((config != IPV4_CFG_DISABLED && config != IPV4_CFG_DHCP && config != IPV4_CFG_STATIC) || NET_CTRL_HAS(a, NET_CTRL_EXT_KIND)) return SOCK_ERR_INVAL; + memcpy(&ip, a->address.ip, sizeof(ip)); + if (NET_CTRL_HAS(a, NET_CTRL_EXT_GATEWAY)) { + if (a->gateway.ver != IP_VER4) return SOCK_ERR_INVAL; + memcpy(&gw, a->gateway.ip, sizeof(gw)); + } + if (config == IPV4_CFG_STATIC && (!NET_CTRL_HAS(a, NET_CTRL_EXT_PREFIX_LEN) || a->prefix_len > 32)) return SOCK_ERR_INVAL; + if (config != IPV4_CFG_STATIC && NET_CTRL_HAS(a, NET_CTRL_EXT_PREFIX_LEN) && a->prefix_len > 32) return SOCK_ERR_INVAL; + uint32_t mask = 0; + if (config == IPV4_CFG_STATIC) { + if (a->prefix_len >= 32) mask = 0xFFFFFFFF; + else if (a->prefix_len) mask = 0xFFFFFFFF << (32 - a->prefix_len); + } + if (config != IPV4_CFG_STATIC) { + ip = 0; + gw = 0; + } + if (update) return l3_ipv4_update(a->l3_id, ip, mask, gw, (ipv4_cfg_t)config, NULL) ? SOCK_OK : SOCK_ERR_INVAL; + return l3_ipv4_add_to_interface(l2->ifindex, ip, mask, gw, (ipv4_cfg_t)config, NULL) ? SOCK_OK : SOCK_ERR_INVAL; + } + if (a->address.ver == IP_VER6) { + int16_t config = NET_CTRL_HAS(a, NET_CTRL_EXT_CONFIG) ? a->config : IPV6_CFG_STATIC; + uint8_t kind = NET_CTRL_HAS(a, NET_CTRL_EXT_KIND) ? a->kind : (ipv6_is_linklocal(a->address.ip) ? IPV6_ADDRK_LINK_LOCAL : IPV6_ADDRK_GLOBAL); + if (config != IPV6_CFG_DISABLE && config != IPV6_CFG_STATIC && config != IPV6_CFG_SLAAC && config != IPV6_CFG_DHCPV6) return SOCK_ERR_INVAL; + if (kind != IPV6_ADDRK_GLOBAL && kind != IPV6_ADDRK_LINK_LOCAL) return SOCK_ERR_INVAL; + if (config == IPV6_CFG_STATIC && (!NET_CTRL_HAS(a, NET_CTRL_EXT_PREFIX_LEN) || a->prefix_len > 128)) return SOCK_ERR_INVAL; + if (config != IPV6_CFG_STATIC && NET_CTRL_HAS(a, NET_CTRL_EXT_PREFIX_LEN) && a->prefix_len > 128) return SOCK_ERR_INVAL; + if (NET_CTRL_HAS(a, NET_CTRL_EXT_GATEWAY) && a->gateway.ver != IP_VER6) return SOCK_ERR_INVAL; + const uint8_t* gw = NET_CTRL_HAS(a, NET_CTRL_EXT_GATEWAY) ? a->gateway.ip : (const uint8_t[16]){0}; + uint8_t prefix_len = NET_CTRL_HAS(a, NET_CTRL_EXT_PREFIX_LEN) ? a->prefix_len : 0; + if (update) return l3_ipv6_update(a->l3_id, a->address.ip, prefix_len, gw, (ipv6_cfg_t)config, kind) ? SOCK_OK : SOCK_ERR_INVAL; + return l3_ipv6_add_to_interface(l2->ifindex, a->address.ip, prefix_len, gw, (ipv6_cfg_t)config, kind) ? SOCK_OK : SOCK_ERR_INVAL; + } + return SOCK_ERR_INVAL; +} + +static int32_t net_ctrl_addr_del(const net_ctrl_attrs_t* a) { + if (NET_CTRL_HAS(a, NET_CTRL_EXT_L3_ID)) { + if (l3_is_v6_from_id(a->l3_id)) return l3_ipv6_remove_from_interface(a->l3_id) ? SOCK_OK : SOCK_ERR_INVAL; + return l3_ipv4_remove_from_interface(a->l3_id) ? SOCK_OK : SOCK_ERR_INVAL; + } + if (!NET_CTRL_HAS(a, NET_CTRL_EXT_ADDRESS)) return SOCK_ERR_INVAL; + if (a->address.ver == IP_VER4) { + uint32_t ip = 0; + memcpy(&ip, a->address.ip, sizeof(ip)); + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_ip(ip); + return v4 && l3_ipv4_remove_from_interface(v4->l3_id) ? SOCK_OK : SOCK_ERR_INVAL; + } + if (a->address.ver == IP_VER6) { + l3_ipv6_interface_t* v6 = l3_ipv6_find_by_ip(a->address.ip); + return v6 && l3_ipv6_remove_from_interface(v6->l3_id) ? SOCK_OK : SOCK_ERR_INVAL; + } + return SOCK_ERR_INVAL; +} + +static bool net_ctrl_route_dump(const net_ctrl_attrs_t* a, buffer* b) { + uint8_t count = l2_interface_count(); + for (uint8_t i = 0; i < count; i++) { + l2_interface_t* l2 = l2_interface_at(i); + if (!net_ctrl_l2_matches(l2, a)) continue; + for (int j = 0; j < MAX_IPV4_PER_INTERFACE; j++) { + l3_ipv4_interface_t* v4 = l2->l3_v4[j]; + if (!v4 || !v4->routing_table) continue; + if (NET_CTRL_HAS(a, NET_CTRL_EXT_L3_ID) && v4->l3_id != a->l3_id) continue; + int n = ipv4_rt_count((const ipv4_rt_table_t*)v4->routing_table); + for (int r = 0; r < n; r++) { + ipv4_rt_entry_t e; + if (!ipv4_rt_get((const ipv4_rt_table_t*)v4->routing_table, r, &e)) continue; + NetCtrlRouteInfo info; + memset(&info, 0, sizeof(info)); + info.prefix.ifindex = l2->ifindex; + info.prefix.l3_id = v4->l3_id; + int prefix_len = ipv4_prefix_len(e.mask); + info.prefix.prefix_len = prefix_len < 0 ? 0 : (uint8_t)prefix_len; + info.metric = e.metric; + info.route_epoch = ipv4_rt_epoch((const ipv4_rt_table_t*)v4->routing_table); + info.prefix.address.ver = IP_VER4; + info.prefix.gateway.ver = IP_VER4; + memcpy(info.prefix.address.ip, &e.network, sizeof(e.network)); + memcpy(info.prefix.gateway.ip, &e.gateway, sizeof(e.gateway)); + if (buffer_write_lim(b, (const char*)&info, sizeof(info)) != sizeof(info)) return false; + } + } + for (int j = 0; j < MAX_IPV6_PER_INTERFACE; j++) { + l3_ipv6_interface_t* v6 = l2->l3_v6[j]; + if (!v6 || !v6->routing_table) continue; + if (NET_CTRL_HAS(a, NET_CTRL_EXT_L3_ID) && v6->l3_id != a->l3_id) continue; + int n = ipv6_rt_count((const ipv6_rt_table_t*)v6->routing_table); + for (int r = 0; r < n; r++) { + ipv6_rt_entry_t e; + if (!ipv6_rt_get((const ipv6_rt_table_t*)v6->routing_table, r, &e)) continue; + NetCtrlRouteInfo info; + memset(&info, 0, sizeof(info)); + info.prefix.ifindex = l2->ifindex; + info.prefix.l3_id = v6->l3_id; + info.prefix.prefix_len = e.prefix_len; + info.metric = e.metric; + info.route_epoch = ipv6_rt_epoch((const ipv6_rt_table_t*)v6->routing_table); + info.prefix.address.ver = IP_VER6; + info.prefix.gateway.ver = IP_VER6; + ipv6_cpy(info.prefix.address.ip, e.network); + ipv6_cpy(info.prefix.gateway.ip, e.gateway); + if (buffer_write_lim(b, (const char*)&info, sizeof(info)) != sizeof(info)) return false; + } + } + } + return true; +} + +static int32_t net_ctrl_route_set(const net_ctrl_attrs_t* a) { + if (!NET_CTRL_HAS(a, NET_CTRL_EXT_L3_ID) || !NET_CTRL_HAS(a, NET_CTRL_EXT_ADDRESS) || !NET_CTRL_HAS(a, NET_CTRL_EXT_PREFIX_LEN)) return SOCK_ERR_INVAL; + uint16_t metric = NET_CTRL_HAS(a, NET_CTRL_EXT_METRIC) ? a->metric : 0; + if (!l3_is_v6_from_id(a->l3_id)) { + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(a->l3_id); + uint32_t network = 0; + uint32_t gw = 0; + if (!v4 || v4->is_localhost || a->address.ver != IP_VER4) return SOCK_ERR_INVAL; + memcpy(&network, a->address.ip, sizeof(network)); + if (a->prefix_len > 32) return SOCK_ERR_INVAL; + if (NET_CTRL_HAS(a, NET_CTRL_EXT_GATEWAY)) { + if (a->gateway.ver != IP_VER4) return SOCK_ERR_INVAL; + memcpy(&gw, a->gateway.ip, sizeof(gw)); + } + if (!v4->routing_table) v4->routing_table = ipv4_rt_create(v4->l3_id); + if (!v4->routing_table) return SOCK_ERR_SYS; + uint32_t mask = 0; + if (a->prefix_len >= 32) mask = 0xFFFFFFFF; + else if (a->prefix_len) mask = 0xFFFFFFFF << (32 -a->prefix_len); + return ipv4_rt_add_in((ipv4_rt_table_t*)v4->routing_table, network & mask, mask, gw, metric) ? SOCK_OK : SOCK_ERR_INVAL; + } + l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(a->l3_id); + if (!v6 || v6->is_localhost || a->address.ver != IP_VER6 || a->prefix_len > 128) return SOCK_ERR_INVAL; + if (NET_CTRL_HAS(a, NET_CTRL_EXT_GATEWAY) && a->gateway.ver != IP_VER6) return SOCK_ERR_INVAL; + const uint8_t* gw = NET_CTRL_HAS(a, NET_CTRL_EXT_GATEWAY) ? a->gateway.ip : (const uint8_t[16]){0}; + if (!v6->routing_table) v6->routing_table = ipv6_rt_create(v6->l3_id); + if (!v6->routing_table) return SOCK_ERR_SYS; + uint8_t net[16]; + ipv6_prefix_network(a->address.ip, a->prefix_len, net); + return ipv6_rt_add_in((ipv6_rt_table_t*)v6->routing_table, net, a->prefix_len, gw, metric) ? SOCK_OK : SOCK_ERR_INVAL; +} + +static int32_t net_ctrl_route_del(const net_ctrl_attrs_t* a) { + if (!NET_CTRL_HAS(a, NET_CTRL_EXT_L3_ID) || !NET_CTRL_HAS(a, NET_CTRL_EXT_ADDRESS) || !NET_CTRL_HAS(a, NET_CTRL_EXT_PREFIX_LEN)) return SOCK_ERR_INVAL; + if (!l3_is_v6_from_id(a->l3_id)) { + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(a->l3_id); + uint32_t network = 0; + if (!v4 || !v4->routing_table || a->address.ver != IP_VER4) return SOCK_ERR_INVAL; + memcpy(&network, a->address.ip, sizeof(network)); + if (a->prefix_len > 32) return SOCK_ERR_INVAL; + uint32_t mask = 0; + if (a->prefix_len >= 32) mask = 0xFFFFFFFF; + else if (a->prefix_len) mask = 0xFFFFFFFF << (32 -(a->prefix_len)); + return ipv4_rt_del_in((ipv4_rt_table_t*)v4->routing_table, network & mask, mask) ? SOCK_OK : SOCK_ERR_INVAL; + } + l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(a->l3_id); + if (!v6 || !v6->routing_table || a->address.ver != IP_VER6 || a->prefix_len > 128) return SOCK_ERR_INVAL; + uint8_t net[16]; + ipv6_prefix_network(a->address.ip, a->prefix_len, net); + return ipv6_rt_del_in((ipv6_rt_table_t*)v6->routing_table, net, a->prefix_len) ? SOCK_OK : SOCK_ERR_INVAL; +} + +static bool net_ctrl_neigh_dump(const net_ctrl_attrs_t* a, buffer* b) { + uint8_t count = l2_interface_count(); + for (uint8_t i = 0; i < count; i++) { + l2_interface_t* l2 = l2_interface_at(i); + if (!net_ctrl_l2_matches(l2, a)) continue; + arp_entry_t ae[ARP_TABLE_MAX]; + uint32_t an = arp_table_dump_for_l2(l2->ifindex, ae, ARP_TABLE_MAX); + for (uint32_t j = 0; j < an; j++) { + if (NET_CTRL_HAS(a, NET_CTRL_EXT_ADDRESS)) { + uint32_t want = 0; + if (a->address.ver != IP_VER4) continue; + memcpy(&want, a->address.ip, sizeof(want)); + if (ae[j].ip != want) continue; + } + NetCtrlNeighInfo info; + memset(&info, 0, sizeof(info)); + info.ifindex = l2->ifindex; + info.state = ae[j].state; + info.ttl_ms = ae[j].ttl_ms; + info.router_lifetime_ms = 0; + if (ae[j].static_entry) info.flags |= NET_CTRL_NEIGH_F_STATIC; + info.address.ver = IP_VER4; + memcpy(info.address.ip, &ae[j].ip, sizeof(ae[j].ip)); + memcpy(info.mac, ae[j].mac, 6); + if (buffer_write_lim(b, (const char*)&info, sizeof(info)) != sizeof(info)) return false; + } + ndp_entry_t ne[NDP_TABLE_MAX]; + uint32_t nn = ndp_table_dump_for_l2(l2->ifindex, ne, NDP_TABLE_MAX); + for (uint32_t j = 0; j < nn; j++) { + if (NET_CTRL_HAS(a, NET_CTRL_EXT_ADDRESS)) { + if (a->address.ver != IP_VER6 || ipv6_cmp(a->address.ip, ne[j].ip) != 0) continue; + } + NetCtrlNeighInfo info; + memset(&info, 0, sizeof(info)); + info.ifindex = l2->ifindex; + info.state = ne[j].state; + info.ttl_ms = ne[j].ttl_ms; + info.router_lifetime_ms = ne[j].router_lifetime_ms; + if (ne[j].static_entry) info.flags |= NET_CTRL_NEIGH_F_STATIC; + if (ne[j].is_router) info.flags |= NET_CTRL_NEIGH_F_ROUTER; + info.address.ver = IP_VER6; + ipv6_cpy(info.address.ip, ne[j].ip); + memcpy(info.mac, ne[j].mac, 6); + if (buffer_write_lim(b, (const char*)&info, sizeof(info)) != sizeof(info)) return false; + } + } + return true; +} + +static int32_t net_ctrl_neigh_set(const net_ctrl_attrs_t* a) { + if (!NET_CTRL_HAS(a, NET_CTRL_EXT_ADDRESS) || !NET_CTRL_HAS(a, NET_CTRL_EXT_MAC)) return SOCK_ERR_INVAL; + l2_interface_t* l2 = net_ctrl_l2_from_attrs(a); + if (!l2) return SOCK_ERR_INVAL; + uint32_t ttl = NET_CTRL_HAS(a, NET_CTRL_EXT_TTL_MS) ? a->ttl_ms : 0; + if (a->address.ver == IP_VER4) { + uint32_t ip = 0; + memcpy(&ip, a->address.ip, sizeof(ip)); + if (!l2->arp_table) return SOCK_ERR_INVAL; + arp_table_put_for_l2(l2->ifindex, ip, a->mac, ttl, NET_CTRL_HAS(a, NET_CTRL_EXT_FLAGS) && (a->flags & NET_CTRL_NEIGH_F_STATIC)); + return SOCK_OK; + } + if (a->address.ver == IP_VER6) { + if (!l2->nd_table) return SOCK_ERR_INVAL; + bool router = NET_CTRL_HAS(a, NET_CTRL_EXT_FLAGS) && (a->flags & NET_CTRL_NEIGH_F_ROUTER); + bool is_static = NET_CTRL_HAS(a, NET_CTRL_EXT_FLAGS) && (a->flags & NET_CTRL_NEIGH_F_STATIC); + ndp_table_put_for_l2(l2->ifindex, a->address.ip, a->mac, ttl, router, is_static); + return SOCK_OK; + } + return SOCK_ERR_INVAL; +} + +static int32_t net_ctrl_neigh_del(const net_ctrl_attrs_t* a) { + if (!NET_CTRL_HAS(a, NET_CTRL_EXT_ADDRESS)) return SOCK_ERR_INVAL; + l2_interface_t* l2 = net_ctrl_l2_from_attrs(a); + if (!l2) return SOCK_ERR_INVAL; + if (a->address.ver == IP_VER4) { + uint32_t ip = 0; + memcpy(&ip, a->address.ip, sizeof(ip)); + if (!l2->arp_table) return SOCK_ERR_INVAL; + return arp_table_delete_for_l2(l2->ifindex, ip) ? SOCK_OK : SOCK_ERR_INVAL; + } + if (a->address.ver == IP_VER6) { + if (!l2->nd_table) return SOCK_ERR_INVAL; + return ndp_table_delete_for_l2(l2->ifindex, a->address.ip) ? SOCK_OK : SOCK_ERR_INVAL; + } + return SOCK_ERR_INVAL; +} + +//TODO improve handling of UPD and ADD cases in neight e route +int32_t net_ctrl_dispatch(const void* req, uint32_t req_len, uint8_t** out, uint32_t* out_len) { + if (!req || req_len < sizeof(NetCtrlMsg) || !out || !out_len) return SOCK_ERR_INVAL; + *out = NULL; + *out_len = 0; + + NetCtrlMsg in; + memcpy(&in, req, sizeof(in)); + if (in.length != req_len || in.length < sizeof(NetCtrlMsg)) return SOCK_ERR_INVAL; + if (!(in.flags & NET_CTRL_F_REQUEST)) return SOCK_ERR_INVAL; + + net_ctrl_attrs_t attrs; + if (!net_ctrl_read_attrs(NET_CTRL_MSG_CONST_DATA((const NetCtrlMsg*)req), NET_CTRL_MSG_PAYLOAD_LEN(&in), &attrs)) return SOCK_ERR_INVAL; + + buffer b = buffer_create(256, buffer_can_grow); + if (!b.buffer) return SOCK_ERR_SYS; + NetCtrlMsg response = { in.object, in.op, NET_CTRL_F_RESPONSE, 0, sizeof(NetCtrlMsg), SOCK_OK }; + if (buffer_write_lim(&b, (const char*)&response, sizeof(response)) != sizeof(response)) { + buffer_destroy(&b); + return SOCK_ERR_SYS; + } + + int32_t status; + switch ((uint32_t)in.object) { + case NET_CTRL_OBJ_LINK: + switch ((uint32_t)in.op) { + case NET_CTRL_OP_GET: + status = net_ctrl_link_dump(&attrs, &b) ? SOCK_OK : SOCK_ERR_SYS; + break; + case NET_CTRL_OP_UPD: + status = net_ctrl_link_upd(&attrs); + break; + default: + status = SOCK_ERR_INVAL; + break; + } + break; + case NET_CTRL_OBJ_ADDR: + switch ((uint32_t)in.op) { + case NET_CTRL_OP_GET: + status = net_ctrl_addr_dump(&attrs, &b) ? SOCK_OK : SOCK_ERR_SYS; + break; + case NET_CTRL_OP_ADD: + status = net_ctrl_addr_apply(&attrs, false); + break; + case NET_CTRL_OP_UPD: + status = net_ctrl_addr_apply(&attrs, true); + break; + case NET_CTRL_OP_DEL: + status = net_ctrl_addr_del(&attrs); + break; + default: + status = SOCK_ERR_INVAL; + break; + } + break; + case NET_CTRL_OBJ_ROUTE: + switch ((uint32_t)in.op) { + case NET_CTRL_OP_GET: + status = net_ctrl_route_dump(&attrs, &b) ? SOCK_OK : SOCK_ERR_SYS; + break; + case NET_CTRL_OP_ADD: + case NET_CTRL_OP_UPD: + status = net_ctrl_route_set(&attrs); + break; + case NET_CTRL_OP_DEL: + status = net_ctrl_route_del(&attrs); + break; + default: + status = SOCK_ERR_INVAL; + break; + } + break; + case NET_CTRL_OBJ_NEIGH: + switch ((uint32_t)in.op) { + case NET_CTRL_OP_GET: + status = net_ctrl_neigh_dump(&attrs, &b) ? SOCK_OK : SOCK_ERR_SYS; + break; + case NET_CTRL_OP_ADD: + case NET_CTRL_OP_UPD: + status = net_ctrl_neigh_set(&attrs); + break; + case NET_CTRL_OP_DEL: + status = net_ctrl_neigh_del(&attrs); + break; + default: + status = SOCK_ERR_INVAL; + break; + } + break; + default: + status = SOCK_ERR_INVAL; + break; + } + + if (status != SOCK_OK) b.buffer_size = sizeof(NetCtrlMsg); + NetCtrlMsg* hdr = (NetCtrlMsg*)b.buffer; + hdr->status = status; + hdr->length = b.buffer_size; + *out = b.buffer; + *out_len = b.buffer_size; + return SOCK_OK; +} diff --git a/kernel/networking/transport_layer/net_ctrl.h b/kernel/networking/transport_layer/net_ctrl.h new file mode 100644 index 00000000..b7b557c3 --- /dev/null +++ b/kernel/networking/transport_layer/net_ctrl.h @@ -0,0 +1,14 @@ +#pragma once + +#include "types.h" +#include "net/net_ctrl.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int32_t net_ctrl_dispatch(const void* req, uint32_t req_len, uint8_t** out, uint32_t* out_len); + +#ifdef __cplusplus +} +#endif \ No newline at end of file From 2141b82eaa81cf9174f188101dcaf63ad85051b6 Mon Sep 17 00:00:00 2001 From: CodeAnarchist Date: Sat, 20 Jun 2026 14:35:41 +0200 Subject: [PATCH 28/39] [net]add raw sockets and ipv4 dad --- kernel/networking/interface_manager.c | 10 +- kernel/networking/internet_layer/icmp.c | 222 ++++---- kernel/networking/internet_layer/icmp.h | 16 +- kernel/networking/internet_layer/icmpv6.c | 241 ++++----- kernel/networking/internet_layer/igmp.c | 45 +- kernel/networking/internet_layer/ipv4.c | 14 +- kernel/networking/internet_layer/ipv4.h | 1 - kernel/networking/link_layer/arp.c | 106 ++-- kernel/networking/link_layer/arp.h | 3 +- kernel/networking/transport_layer/csocket.c | 22 +- .../networking/transport_layer/csocket_raw.c | 491 ++++++++++++++++++ .../networking/transport_layer/csocket_raw.h | 25 + .../transport_layer/csocket_special.c | 10 +- kernel/networking/transport_layer/net_ctrl.c | 61 ++- kernel/tools/tracert.c | 2 +- shared | 2 +- user/demo/default_process.c | 158 ++++++ 17 files changed, 1065 insertions(+), 364 deletions(-) create mode 100644 kernel/networking/transport_layer/csocket_raw.c create mode 100644 kernel/networking/transport_layer/csocket_raw.h diff --git a/kernel/networking/interface_manager.c b/kernel/networking/interface_manager.c index 70d66b9e..1f3cff73 100644 --- a/kernel/networking/interface_manager.c +++ b/kernel/networking/interface_manager.c @@ -317,7 +317,9 @@ static bool v4_overlap_intra_l2(uint8_t ifindex, uint32_t ip, uint32_t mask){ if (!x->l2 || 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; + if (ipv4_net(ip, m) != ipv4_net(x->ip, m)) continue; + if (x->mask == mask && ipv4_net(ip, mask) == ipv4_net(x->ip, x->mask)) continue; + return true; } return false; } @@ -365,6 +367,7 @@ uint8_t l3_ipv4_add_to_interface(uint8_t ifindex, uint32_t ip, uint32_t mask, ui 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->kind != NET_IFK_LOCALHOST && !arp_dad_ipv4_on(ifindex, ip)) return 0; } if (l2->ipv4_count >= MAX_IPV4_PER_INTERFACE) return 0; @@ -445,8 +448,11 @@ bool l3_ipv4_update(uint8_t l3_id, uint32_t ip, uint32_t mask, uint32_t gw, ipv4 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; + if (ipv4_net(ip, m) != ipv4_net(x->ip, m)) continue; + if (x->mask == mask && ipv4_net(ip, mask) == ipv4_net(x->ip, x->mask)) continue; + return false; } + if (ip != n->ip && l2->kind != NET_IFK_LOCALHOST && !arp_dad_ipv4_on(l2->ifindex, ip)) return false; } uint32_t old_ip = n->ip; diff --git a/kernel/networking/internet_layer/icmp.c b/kernel/networking/internet_layer/icmp.c index f4f77478..8cf102b8 100644 --- a/kernel/networking/internet_layer/icmp.c +++ b/kernel/networking/internet_layer/icmp.c @@ -4,59 +4,11 @@ #include "console/kio.h" #include "networking/interface_manager.h" #include "networking/internet_layer/ipv4.h" +#include "networking/transport_layer/csocket.h" +#include "networking/transport_layer/csocket_raw.h" +#include "networking/transport_layer/trans_utils.h" #include "syscalls/syscalls.h" -#define MAX_PENDING 16 -#define MAX_PING_REPLIES 16 - -//TODO use raw sockets -typedef struct { - uint8_t type; - uint8_t code; - uint32_t end_ms; - uint32_t src_ip; -} ping_reply_t; - -typedef struct { - bool in_use; - uint16_t id; - uint16_t seq; - uint32_t start_ms; - uint8_t rx_count; - ping_reply_t replies[MAX_PING_REPLIES]; -} ping_slot_t; - -static ping_slot_t g_pending[MAX_PENDING] = {0}; - -static int alloc_slot(uint16_t id, uint16_t seq) { - for (int i = 0; i < MAX_PENDING; i++) { - if (!g_pending[i].in_use) { - g_pending[i].in_use = true; - g_pending[i].id = id; - g_pending[i].seq = seq; - g_pending[i].start_ms = (uint32_t)get_time(); - g_pending[i].rx_count = 0; - memset(g_pending[i].replies, 0, sizeof(g_pending[i].replies)); - return i; - } - } - return -1; -} - -static void mark_received(uint16_t id, uint16_t seq, uint8_t type, uint8_t code, uint32_t src_ip) { - for (int i = 0; i < MAX_PENDING; i++) { - if (g_pending[i].in_use && g_pending[i].id == id && g_pending[i].seq == seq) { - if (g_pending[i].rx_count >= MAX_PING_REPLIES) return; - ping_reply_t *r = &g_pending[i].replies[g_pending[i].rx_count++]; - r->type = type; - r->code = code; - r->end_ms = (uint32_t)get_time(); - r->src_ip = src_ip; - return; - } - } -} - static uintptr_t build_echo(uint16_t id, uint16_t seq, const uint8_t* payload, uint32_t pay_len, uint32_t* out_total_len) { uint32_t len = 8 + (pay_len > 56 ? 56 : pay_len); *out_total_len = len; @@ -78,75 +30,120 @@ static uintptr_t build_echo(uint16_t id, uint16_t seq, const uint8_t* payload, u uint32_t icmp_ping_collect(uint32_t dst_ip, uint16_t id, uint16_t seq, uint32_t timeout_ms, const void* tx_opts_or_null, uint32_t ttl, ping_result_t* out, uint32_t max_results) { if (!out || !max_results) return 0; - int slot = alloc_slot(id, seq); - if (slot < 0) return 0; uint32_t tot_len = 0; uintptr_t buf = build_echo(id, seq, NULL, 32, &tot_len); - if (!buf) { - g_pending[slot].in_use = false; - return 0; - } + if (!buf) return 0; - uint32_t headroom = (uint32_t)sizeof(eth_hdr_t) + (uint32_t)sizeof(ipv4_hdr_t); - netpkt_t* pkt = netpkt_alloc(tot_len, headroom, 0); - if (!pkt) { + SocketOptions opt; + memset(&opt, 0, sizeof(opt)); + opt.special_kind = SOCKET_SPECIAL_RAW; + if (ttl) { + opt.flags |= SOCK_OPT_TTL; + opt.ttl = (uint8_t)ttl; + } + socket_handle_t sock = create_socket(PROTO_ICMP, &opt); + if (!sock) { release((void*)buf); - g_pending[slot].in_use = false; return 0; } - void* p = netpkt_put(pkt, tot_len); - if (!p) { - netpkt_unref(pkt); + + const ip_tx_opts_t* tx = (const ip_tx_opts_t*)tx_opts_or_null; + if (tx && tx->scope != IP_TX_AUTO) { + SockBindSpec bind; + memset(&bind, 0, sizeof(bind)); + if (tx->scope == IP_TX_BOUND_L2) { + bind.kind = BIND_L2; + bind.ifindex = tx->index; + } else if (tx->scope == IP_TX_BOUND_L3) { + bind.kind = BIND_L3; + bind.ver = IP_VER4; + bind.l3_id = tx->index; + } + if (bind.kind && bind_socket(sock, &bind, 0) != SOCK_OK) { + close_socket(sock); + release((void*)buf); + return 0; + } + } + + net_l4_endpoint dst; + make_ep(&dst_ip, 0, IP_VER4, &dst); + if (send_to_socket(sock, &dst, (const void*)buf, tot_len) != (int64_t)tot_len) { + close_socket(sock); release((void*)buf); - g_pending[slot].in_use = false; return 0; } - memcpy(p, (const void*)buf, tot_len); release((void*)buf); - ipv4_send_packet(dst_ip, 1, pkt, (const ip_tx_opts_t*)tx_opts_or_null, (uint8_t)ttl, 0); uint32_t count = 0; - uint32_t copied = 0; uint32_t start = (uint32_t)get_time(); - for (;;) { - while (copied < g_pending[slot].rx_count && count < max_results) { - ping_reply_t *r = &g_pending[slot].replies[copied++]; - ping_result_t *dst = &out[count++]; - dst->icmp_type = r->type; - dst->icmp_code = r->code; - dst->responder_ip = r->src_ip; - switch (r->type) { - case ICMP_ECHO_REPLY: dst->status = PING_OK; break; - case ICMP_DEST_UNREACH: - switch (r->code) { - case 0: dst->status = PING_NET_UNREACH; break; - case 1: dst->status = PING_HOST_UNREACH; break; - case 2: dst->status = PING_PROTO_UNREACH; break; - case 3: dst->status = PING_PORT_UNREACH; break; - case 4: dst->status = PING_FRAG_NEEDED; break; - case 5: dst->status = PING_SRC_ROUTE_FAILED; break; - case 13: dst->status = PING_ADMIN_PROHIBITED; break; - default: dst->status = PING_UNKNOWN_ERROR; break; - } - break; - case ICMP_TIME_EXCEEDED: dst->status = PING_TTL_EXPIRED; break; - case ICMP_PARAM_PROBLEM: dst->status = PING_PARAM_PROBLEM; break; - case ICMP_REDIRECT: dst->status = PING_REDIRECT; break; - default: dst->status = PING_UNKNOWN_ERROR; break; - } + while (count < max_results) { + uint32_t now = (uint32_t)get_time(); + if (now - start >= timeout_ms) break; - if (r->end_ms >= g_pending[slot].start_ms) dst->rtt_ms = r->end_ms - g_pending[slot].start_ms; - else dst->rtt_ms = 0; + uint8_t rx[576]; + net_l4_endpoint src; + memset(&src, 0, sizeof(src)); + int64_t n = receive_from_socket(sock, rx, sizeof(rx), &src); + if (n == SOCK_ERR_WOULDBLOCK) { + msleep(5); + continue; } - if (count == max_results || (max_results == 1 && count)) break; - uint32_t now = (uint32_t)get_time(); - if (now - start >= timeout_ms) break; - msleep(5); + if (n < 8) { + if (n < 0) msleep(5); + continue; + } + + uint8_t type = rx[0]; + uint8_t code = rx[1]; + bool matched = false; + if (type == ICMP_ECHO_REPLY && rd_be16(rx + 4) == id && rd_be16(rx + 6) == seq) matched = true; + else if ((type == ICMP_TIME_EXCEEDED || type == ICMP_DEST_UNREACH || type == ICMP_PARAM_PROBLEM || type == ICMP_REDIRECT) && n >= 36) { + const uint8_t* ip = rx + 8; + uint8_t ihl = (uint8_t)(ip[0] & 0x0F); + uint32_t iphdr = (uint32_t)ihl * 4; + if (ihl >= IP_IHL_NOOPTS && (uint32_t)n >= 8 + iphdr + 8 && ip[9] == PROTO_ICMP) { + const uint8_t* ic = rx + 8 + iphdr; + uint8_t inner_type = ic[0]; + if ((inner_type == ICMP_ECHO_REQUEST || inner_type == ICMP_ECHO_REPLY) && rd_be16(ic + 4) == id && rd_be16(ic + 6) == seq) matched = true; + } + } + if (!matched) continue; + + ping_result_t* r = &out[count++]; + memset(r, 0, sizeof(*r)); + r->icmp_type = type; + r->icmp_code = code; + memcpy(&r->responder_ip, src.ip, sizeof(r->responder_ip)); + now = (uint32_t)get_time(); + r->rtt_ms = now >= start ? now - start : 0; + switch (type) { + case ICMP_ECHO_REPLY: + r->status = PING_OK; + break; + case ICMP_DEST_UNREACH: + switch (code) { + case 0: r->status = PING_NET_UNREACH; break; + case 1: r->status = PING_HOST_UNREACH; break; + case 2: r->status = PING_PROTO_UNREACH; break; + case 3: r->status = PING_PORT_UNREACH; break; + case 4: r->status = PING_FRAG_NEEDED; break; + case 5: r->status = PING_SRC_ROUTE_FAILED; break; + case 13: r->status = PING_ADMIN_PROHIBITED; break; + default: r->status = PING_UNKNOWN_ERROR; break; + } + break; + case ICMP_TIME_EXCEEDED: r->status = PING_TTL_EXPIRED; break; + case ICMP_PARAM_PROBLEM: r->status = PING_PARAM_PROBLEM; break; + case ICMP_REDIRECT: r->status = PING_REDIRECT; break; + default: r->status = PING_UNKNOWN_ERROR; break; + } + if (max_results == 1) break; } - g_pending[slot].in_use = false; + close_socket(sock); return count; } @@ -157,7 +154,7 @@ bool icmp_ping(uint32_t dst_ip, uint16_t id, uint16_t seq, uint32_t timeout_ms, return n && dst->status == PING_OK; } -void icmp_input(netpkt_t* pkt, uint32_t src_ip, uint32_t dst_ip) { +void icmp_input(uint8_t ifindex, netpkt_t* pkt, uint32_t src_ip, uint32_t dst_ip) { if (!pkt) return; uint32_t len = netpkt_len(pkt); @@ -177,8 +174,8 @@ void icmp_input(netpkt_t* pkt, uint32_t src_ip, uint32_t dst_ip) { return; } + socket_raw_input_v4(PROTO_ICMP, ifindex, src_ip, dst_ip, pkt); uint8_t type = hdr[0]; - uint8_t code = hdr[1]; uint16_t id = rd_be16(hdr + 4); uint16_t sq = rd_be16(hdr + 6); uint32_t pay = len - 8; @@ -222,30 +219,11 @@ void icmp_input(netpkt_t* pkt, uint32_t src_ip, uint32_t dst_ip) { } if (type == ICMP_ECHO_REPLY) { - mark_received(id, sq, type, code, src_ip); netpkt_unref(pkt); return; } if (type == ICMP_TIME_EXCEEDED || type == ICMP_DEST_UNREACH || type == ICMP_PARAM_PROBLEM || type == ICMP_REDIRECT) { - if (pay >= 28) { - const uint8_t *ip = raw + 8; - uint8_t ihl = (uint8_t)(ip[0] & 0x0F); - uint32_t iphdr = (uint32_t)ihl * 4; - - if (pay >= iphdr + 8) { - uint8_t proto = ip[9]; - if (proto == 1) { - const uint8_t *ic = raw + 8 + iphdr; - uint8_t t = ic[0]; - if (t == ICMP_ECHO_REQUEST || t == ICMP_ECHO_REPLY) { - uint16_t iid = (uint16_t)((ic[4] << 8) | ic[5]); - uint16_t isq = (uint16_t)((ic[6] << 8) | ic[7]); - mark_received(iid, isq, type, code, src_ip); - } - } - } - } netpkt_unref(pkt); return; } diff --git a/kernel/networking/internet_layer/icmp.h b/kernel/networking/internet_layer/icmp.h index 20f44e1a..0c174e76 100644 --- a/kernel/networking/internet_layer/icmp.h +++ b/kernel/networking/internet_layer/icmp.h @@ -7,12 +7,14 @@ extern "C" { #endif -#define ICMP_ECHO_REPLY 0 -#define ICMP_DEST_UNREACH 3 -#define ICMP_REDIRECT 5 -#define ICMP_ECHO_REQUEST 8 -#define ICMP_TIME_EXCEEDED 11 -#define ICMP_PARAM_PROBLEM 12 +typedef enum { + ICMP_ECHO_REPLY = 0, + ICMP_DEST_UNREACH = 3, + ICMP_REDIRECT = 5, + ICMP_ECHO_REQUEST = 8, + ICMP_TIME_EXCEEDED = 11, + ICMP_PARAM_PROBLEM = 12 +} icmp_type_t; typedef enum { PING_OK = 0, @@ -50,7 +52,7 @@ typedef struct { bool icmp_ping(uint32_t dst_ip, uint16_t id, uint16_t seq, uint32_t timeout_ms, const void *tx_opts_or_null, uint32_t ttl, ping_result_t *out); uint32_t icmp_ping_collect(uint32_t dst_ip, uint16_t id, uint16_t seq, uint32_t timeout_ms, const void *tx_opts_or_null, uint32_t ttl, ping_result_t *out, uint32_t max_results); -void icmp_input(netpkt_t* pkt, uint32_t src_ip, uint32_t dst_ip); +void icmp_input(uint8_t ifindex, netpkt_t* pkt, uint32_t src_ip, uint32_t dst_ip); #ifdef __cplusplus } diff --git a/kernel/networking/internet_layer/icmpv6.c b/kernel/networking/internet_layer/icmpv6.c index 87ad2e5a..6ef8f48f 100644 --- a/kernel/networking/internet_layer/icmpv6.c +++ b/kernel/networking/internet_layer/icmpv6.c @@ -7,64 +7,18 @@ #include "networking/link_layer/eth.h" #include "networking/link_layer/ndp.h" #include "networking/internet_layer/mld.h" +#include "networking/transport_layer/csocket.h" +#include "networking/transport_layer/csocket_raw.h" +#include "networking/transport_layer/trans_utils.h" #include "syscalls/syscalls.h" -#define MAX_PENDING 16 -#define MAX_PING6_REPLIES 16 - typedef struct __attribute__((packed)) { icmpv6_hdr_t hdr; uint16_t id; uint16_t seq; } icmpv6_echo_t; -typedef struct { - uint8_t type; - uint8_t code; - uint32_t end_ms; - uint8_t src_ip[16]; -} ping6_reply_t; - -//TODO use raw sockets -typedef struct { - bool in_use; - uint16_t id; - uint16_t seq; - uint32_t start_ms; - uint8_t rx_count; - ping6_reply_t replies[MAX_PING6_REPLIES]; -} ping6_slot_t; - -static ping6_slot_t g_pending[MAX_PENDING] = {0}; - -static int alloc_slot(uint16_t id, uint16_t seq) { - for (int i = 0; i < MAX_PENDING; i++) { - if (!g_pending[i].in_use) { - g_pending[i].in_use = true; - g_pending[i].id = id; - g_pending[i].seq = seq; - g_pending[i].start_ms = (uint32_t)get_time(); - g_pending[i].rx_count = 0; - memset(g_pending[i].replies, 0, sizeof(g_pending[i].replies)); - return i; - } - } - return -1; -} - -static void mark_received(uint16_t id, uint16_t seq, uint8_t type, uint8_t code, const uint8_t src_ip[16]) { - for (int i = 0; i < MAX_PENDING; i++) { - if (g_pending[i].in_use && g_pending[i].id == id && g_pending[i].seq == seq) { - if (g_pending[i].rx_count >= MAX_PING6_REPLIES) return; - ping6_reply_t *r = &g_pending[i].replies[g_pending[i].rx_count++]; - r->type = type; - r->code = code; - r->end_ms = (uint32_t)get_time(); - if (src_ip) memcpy(r->src_ip, src_ip, 16); - return; - } - } -} +static bool extract_echo_id_seq_from_error(const uint8_t *icmp, uint32_t icmp_len, uint16_t *out_id, uint16_t *out_seq); bool icmpv6_send_on_l2(uint8_t ifindex, const uint8_t dst_ip[16], const uint8_t src_ip[16], const uint8_t dst_mac[6], const void *icmp, uint32_t icmp_len, uint8_t hop_limit) { if (!ifindex || !dst_ip || !src_ip || !dst_mac || !icmp || !icmp_len) return false; @@ -83,8 +37,8 @@ bool icmpv6_send_on_l2(uint8_t ifindex, const uint8_t dst_ip[16], const uint8_t ip6.payload_len = bswap16((uint16_t)icmp_len); ip6.next_header = PROTO_ICMPV6; ip6.hop_limit = hop_limit ? hop_limit : 64; - memcpy(ip6.src, src_ip, 16); - memcpy(ip6.dst, dst_ip, 16); + ipv6_cpy(ip6.src, src_ip); + ipv6_cpy(ip6.dst, dst_ip); memcpy(buf, &ip6, sizeof(ip6)); memcpy((void*)((uintptr_t)buf + sizeof(ipv6_hdr_t)), icmp, icmp_len); @@ -119,95 +73,120 @@ static bool icmpv6_send_echo_reply(uint16_t ifindex, const uint8_t src_ip[16], c return true; } -static bool icmpv6_send_echo_request(const uint8_t dst_ip[16], uint16_t id, uint16_t seq, const void *payload, uint32_t payload_len, const void *tx_opts_or_null, uint8_t hop_limit) { - if (!dst_ip) return false; +uint32_t icmpv6_ping_collect(const uint8_t dst_ip[16], uint16_t id, uint16_t seq, uint32_t timeout_ms, const void *tx_opts_or_null, uint8_t hop_limit, ping6_result_t *out, uint32_t max_results) { + if (!out || !max_results) return 0; - uint32_t len = (uint32_t)sizeof(icmpv6_echo_t) + payload_len; - uint32_t headroom = (uint32_t)sizeof(eth_hdr_t) + (uint32_t)sizeof(ipv6_hdr_t); - netpkt_t* pkt = netpkt_alloc(len, headroom, 0); - if (!pkt) return false; - void* buf = netpkt_put(pkt, len); - if (!buf) { - netpkt_unref(pkt); - return false; + SocketOptions opt; + memset(&opt, 0, sizeof(opt)); + opt.special_kind = SOCKET_SPECIAL_RAW; + if (hop_limit) { + opt.flags |= SOCK_OPT_TTL; + opt.ttl = hop_limit; } - - icmpv6_echo_t e; - e.hdr.type = ICMPV6_ECHO_REQUEST; - e.hdr.code = 0; - e.hdr.checksum = 0; - e.id = bswap16(id); - e.seq = bswap16(seq); - - memcpy(buf, &e, sizeof(e)); - if (payload_len) memcpy((void*)((uintptr_t)buf + sizeof(icmpv6_echo_t)), payload, payload_len); - - ipv6_tx_plan_t plan; - if (!ipv6_build_tx_plan(dst_ip, tx_opts_or_null, &plan)) { - netpkt_unref(pkt); - return false; + socket_handle_t sock = create_socket(PROTO_ICMPV6, &opt); + if (!sock) { + return 0; } - e.hdr.checksum = bswap16(checksum16_pipv6(plan.src_ip, dst_ip, PROTO_ICMPV6, (const uint8_t*)buf, len)); - memcpy(buf, &e, sizeof(e)); - - ipv6_send_packet(dst_ip, PROTO_ICMPV6, pkt, (const ip_tx_opts_t*)tx_opts_or_null, hop_limit ? hop_limit : 64, 0); - return true; -} -uint32_t icmpv6_ping_collect(const uint8_t dst_ip[16], uint16_t id, uint16_t seq, uint32_t timeout_ms, const void *tx_opts_or_null, uint8_t hop_limit, ping6_result_t *out, uint32_t max_results) { - if (!out || !max_results) return 0; - int slot = alloc_slot(id, seq); - if (slot < 0) { - return 0; + const ip_tx_opts_t* tx = (const ip_tx_opts_t*)tx_opts_or_null; + if (tx && tx->scope != IP_TX_AUTO) { + SockBindSpec bind; + memset(&bind, 0, sizeof(bind)); + if (tx->scope == IP_TX_BOUND_L2) { + bind.kind = BIND_L2; + bind.ifindex = tx->index; + } else if (tx->scope == IP_TX_BOUND_L3) { + bind.kind = BIND_L3; + bind.ver = IP_VER6; + bind.l3_id = tx->index; + } + if (bind.kind && bind_socket(sock, &bind, 0) != SOCK_OK) { + close_socket(sock); + return 0; + } } - uint8_t payload[32]; + uint8_t payload[sizeof(icmpv6_echo_t) + 32]; memset(payload, 0, sizeof(payload)); - - if (!icmpv6_send_echo_request(dst_ip, id, seq, payload, sizeof(payload), tx_opts_or_null, hop_limit)) { - g_pending[slot].in_use = false; + icmpv6_echo_t* e = (icmpv6_echo_t*)payload; + e->hdr.type = ICMPV6_ECHO_REQUEST; + e->hdr.code = 0; + e->id = bswap16(id); + e->seq = bswap16(seq); + + net_l4_endpoint dst; + memset(&dst, 0, sizeof(dst)); + dst.ver = IP_VER6; + ipv6_cpy(dst.ip, dst_ip); + if (send_to_socket(sock, &dst, payload, sizeof(payload)) != (int64_t)sizeof(payload)) { + close_socket(sock); return 0; } uint32_t count = 0; - uint32_t copied = 0; uint32_t start = (uint32_t)get_time(); - for (;;) { - while (copied < g_pending[slot].rx_count && count < max_results) { - ping6_reply_t *r = &g_pending[slot].replies[copied++]; - ping6_result_t *dst = &out[count++]; - dst->icmp_type = r->type; - dst->icmp_code = r->code; - memcpy(dst->responder_ip, r->src_ip, 16); - switch (r->type) { - case ICMPV6_ECHO_REPLY: dst->status = PING_OK; break; - case ICMPV6_DEST_UNREACH: - switch (r->code) { - case 0: dst->status = PING_NET_UNREACH; break; - case 1: dst->status = PING_ADMIN_PROHIBITED; break; - case 2: dst->status = PING_ADMIN_PROHIBITED; break; - case 3: dst->status = PING_HOST_UNREACH; break; - case 4: dst->status = PING_PORT_UNREACH; break; - default: dst->status = PING_UNKNOWN_ERROR; break; - } - break; - case ICMPV6_PACKET_TOO_BIG: dst->status = PING_FRAG_NEEDED; break; - case ICMPV6_TIME_EXCEEDED: dst->status = PING_TTL_EXPIRED; break; - case ICMPV6_PARAM_PROBLEM: dst->status = PING_PARAM_PROBLEM; break; - default: dst->status = PING_UNKNOWN_ERROR; break; - } - - if (r->end_ms >= g_pending[slot].start_ms) dst->rtt_ms = r->end_ms - g_pending[slot].start_ms; - else dst->rtt_ms = 0; - } - - if (count == max_results || (max_results == 1 && count)) break; + while (count < max_results) { uint32_t now = (uint32_t)get_time(); if (now - start >= timeout_ms) break; - msleep(5); + + uint8_t rx[1280]; + net_l4_endpoint src; + memset(&src, 0, sizeof(src)); + int64_t n = receive_from_socket(sock, rx, sizeof(rx), &src); + if (n == SOCK_ERR_WOULDBLOCK) { + msleep(5); + continue; + } + + if (n < (int64_t)sizeof(icmpv6_hdr_t)) { + if (n < 0) msleep(5); + continue; + } + + uint8_t type = rx[0]; + uint8_t code = rx[1]; + bool matched = false; + if (type == ICMPV6_ECHO_REPLY && n >= (int64_t)sizeof(icmpv6_echo_t)) { + icmpv6_echo_t reply; + memcpy(&reply, rx, sizeof(reply)); + if (bswap16(reply.id) == id &&bswap16(reply.seq) == seq) matched = true; + } else if (type == ICMPV6_DEST_UNREACH || type == ICMPV6_PACKET_TOO_BIG || type == ICMPV6_TIME_EXCEEDED || type == ICMPV6_PARAM_PROBLEM) { + uint16_t inner_id = 0; + uint16_t inner_seq = 0; + if (extract_echo_id_seq_from_error(rx, (uint32_t)n, &inner_id, &inner_seq) && inner_id == id && inner_seq == seq) matched = true; + } + if (!matched) continue; + + ping6_result_t* r = &out[count++]; + memset(r, 0, sizeof(*r)); + r->icmp_type = type; + r->icmp_code = code; + ipv6_cpy(r->responder_ip, src.ip); + now = (uint32_t)get_time(); + r->rtt_ms = now >= start ? now - start : 0; + switch (type) { + case ICMPV6_ECHO_REPLY: + r->status = PING_OK; + break; + case ICMPV6_DEST_UNREACH: + switch (code) { + case 0: r->status = PING_NET_UNREACH; break; + case 1: r->status = PING_ADMIN_PROHIBITED; break; + case 2: r->status = PING_ADMIN_PROHIBITED; break; + case 3: r->status = PING_HOST_UNREACH; break; + case 4: r->status = PING_PORT_UNREACH; break; + default: r->status = PING_UNKNOWN_ERROR; break; + } + break; + case ICMPV6_PACKET_TOO_BIG: r->status = PING_FRAG_NEEDED; break; + case ICMPV6_TIME_EXCEEDED: r->status = PING_TTL_EXPIRED; break; + case ICMPV6_PARAM_PROBLEM: r->status = PING_PARAM_PROBLEM; break; + default: r->status = PING_UNKNOWN_ERROR; break; + } + if (max_results == 1) break; } - g_pending[slot].in_use = false; + close_socket(sock); return count; } @@ -268,6 +247,8 @@ void icmpv6_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ netpkt_unref(pkt); return; } + + socket_raw_input_v6((uint8_t)ifindex, src_ip, dst_ip, pkt); if (h->type == 130 || h->type == 131 || h->type == 132 || h->type == 143) { mld_input((uint8_t)ifindex, src_ip, dst_ip, pkt); netpkt_unref(pkt); @@ -288,7 +269,6 @@ void icmpv6_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ } icmpv6_echo_t e; memcpy(&e, icmp, sizeof(e)); - mark_received(bswap16(e.id), bswap16(e.seq), h->type, h->code, src_ip); netpkt_unref(pkt); return; } @@ -307,20 +287,13 @@ void icmpv6_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ memcpy(&inner, icmp + 8, sizeof(inner)); uint32_t v = bswap32(inner.ver_tc_fl); - if ((v >> 28) == 6 && mtu >= 1280u && mtu <= 65535u) - ipv6_pmtu_note(inner.dst, (uint16_t)mtu); - - uint16_t id = 0, seq = 0; - if (extract_echo_id_seq_from_error(icmp, icmp_len, &id, &seq)) - mark_received(id, seq, h->type, h->code, src_ip); + if ((v >> 28) == 6 && mtu >= 1280u && mtu <= 65535u) ipv6_pmtu_note(inner.dst, (uint16_t)mtu); } netpkt_unref(pkt); return; } if (h->type == ICMPV6_DEST_UNREACH || h->type == ICMPV6_TIME_EXCEEDED || h->type == ICMPV6_PARAM_PROBLEM) { - uint16_t id = 0, seq = 0; - if (extract_echo_id_seq_from_error(icmp, icmp_len, &id, &seq)) mark_received(id, seq, h->type, h->code, src_ip); netpkt_unref(pkt); return; } diff --git a/kernel/networking/internet_layer/igmp.c b/kernel/networking/internet_layer/igmp.c index 56c150fa..4360494e 100644 --- a/kernel/networking/internet_layer/igmp.c +++ b/kernel/networking/internet_layer/igmp.c @@ -3,6 +3,7 @@ #include "networking/internet_layer/ipv4_utils.h" #include "net/checksums.h" #include "networking/interface_manager.h" +#include "networking/transport_layer/csocket_raw.h" #include "kernel_processes/kprocess_loader.h" #include "math/rng.h" #include "random/random.h" @@ -41,34 +42,32 @@ static int igmp_rng_inited = 0; static igmp_state_t igmp_states[IGMP_MAX_TRACK]; -static bool send_igmp(uint8_t ifindex, uint32_t dst, uint8_t type, uint32_t group) { +static bool igmp_send_packet(uint8_t ifindex, uint32_t dst, uint8_t type, uint32_t group) { uint32_t headroom = (uint32_t)sizeof(eth_hdr_t) + (uint32_t)sizeof(ipv4_hdr_t); - netpkt_t* pkt = netpkt_alloc(sizeof(igmp_hdr_t),headroom, 0); + netpkt_t* pkt = netpkt_alloc(sizeof(igmp_hdr_t), headroom, 0); if (!pkt) return false; - igmp_hdr_t igmp; - if (!netpkt_put(pkt, sizeof(igmp_hdr_t))) { + igmp_hdr_t* igmp = (igmp_hdr_t*)netpkt_put(pkt, sizeof(igmp_hdr_t)); + if (!igmp) { netpkt_unref(pkt); return false; } - igmp.type = type; - igmp.max_resp_time = 0; - igmp.group = bswap32(group); - igmp.checksum = 0; - igmp.checksum = bswap16(checksum16(&igmp, sizeof(igmp))); - memcpy((void*)netpkt_data(pkt), &igmp, sizeof(igmp)); + igmp->type = type; + igmp->max_resp_time = 0; + igmp->group = bswap32(group); + igmp->checksum = 0; + igmp->checksum = bswap16(checksum16(igmp, sizeof(*igmp))); ip_tx_opts_t tx; tx.scope = IP_TX_BOUND_L2; tx.index = ifindex; - ipv4_send_packet(dst, 2, pkt, &tx, 1, 0); - return true; + return ipv4_send_packet(dst, PROTO_IGMP, pkt, &tx, 1, 0); } static igmp_state_t* igmp_find_state(uint8_t ifindex, uint32_t group) { - for (int i = 0; i < IGMP_MAX_TRACK; ++i) { + for (int i = 0; i < (int)N_ARR(igmp_states); i++) { igmp_state_t* s = &igmp_states[i]; if (!s->used) continue; if (s->ifindex == ifindex &&s->group == group) return s; @@ -79,7 +78,7 @@ static igmp_state_t* igmp_find_state(uint8_t ifindex, uint32_t group) { static igmp_state_t* igmp_get_state(uint8_t ifindex, uint32_t group) { igmp_state_t* s = igmp_find_state(ifindex, group); if (s) return s; - for (int i = 0; i < IGMP_MAX_TRACK; ++i) { + for (int i = 0; i < (int)N_ARR(igmp_states); i++) { if (!igmp_states[i].used) { igmp_states[i].used = 1; igmp_states[i].ifindex = ifindex; @@ -94,7 +93,7 @@ static igmp_state_t* igmp_get_state(uint8_t ifindex, uint32_t group) { } static int igmp_has_pending_timers(void) { - for (int i = 0; i < IGMP_MAX_TRACK; ++i) { + for (int i = 0; i < (int)N_ARR(igmp_states); i++) { igmp_state_t* s = &igmp_states[i]; if (!s->used) continue; if (s->query_pending) return 1; @@ -120,7 +119,7 @@ static int igmp_daemon_entry(int argc, char* argv[]) { while (igmp_has_pending_timers()) { igmp_uptime_ms += tick_ms; - for (int i = 0; i < IGMP_MAX_TRACK; ++i) { + for (int i = 0; i < (int)N_ARR(igmp_states); i++) { igmp_state_t* s = &igmp_states[i]; if (!s->used) continue; @@ -139,15 +138,15 @@ static int igmp_daemon_entry(int argc, char* argv[]) { continue; } - s->refresh_ms+= tick_ms; - if (s->refresh_ms>= IGMP_REFRESH_PERIOD_MS) { + s->refresh_ms += tick_ms; + if (s->refresh_ms >= IGMP_REFRESH_PERIOD_MS) { s->refresh_ms = 0; - (void)send_igmp(s->ifindex, s->group, IGMP_TYPE_V2_REPORT, s->group); + (void)igmp_send_packet(s->ifindex, s->group, IGMP_TYPE_V2_REPORT, s->group); } if (s->query_pending && igmp_uptime_ms >= s->query_due_ms) { s->query_pending = 0; - (void)send_igmp(s->ifindex, s->group, IGMP_TYPE_V2_REPORT, s->group); + (void)igmp_send_packet(s->ifindex, s->group, IGMP_TYPE_V2_REPORT, s->group); } } msleep(tick_ms); @@ -169,7 +168,7 @@ bool igmp_send_join(uint8_t ifindex, uint32_t group) { igmp_state_t* s = igmp_get_state(ifindex, group); if (s) s->refresh_ms = 0; igmp_daemon_kick(); - return send_igmp(ifindex, group, IGMP_TYPE_V2_REPORT, group); + return igmp_send_packet(ifindex, group, IGMP_TYPE_V2_REPORT, group); } bool igmp_send_leave(uint8_t ifindex, uint32_t group) { @@ -177,7 +176,7 @@ bool igmp_send_leave(uint8_t ifindex, uint32_t group) { igmp_state_t* s = igmp_find_state(ifindex, group); if (s) s->used = 0; igmp_daemon_kick(); - return send_igmp(ifindex, IPV4_MCAST_ALL_ROUTERS, IGMP_TYPE_V2_LEAVE, group); + return igmp_send_packet(ifindex, IPV4_MCAST_ALL_ROUTERS, IGMP_TYPE_V2_LEAVE, group); } static void schedule_report(uint8_t ifindex, uint32_t group, uint32_t max_resp_ds) { @@ -214,6 +213,8 @@ void igmp_input(uint8_t ifindex, uint32_t src, uint32_t dst, netpkt_t* pkt) { return; } + socket_raw_input_v4(PROTO_IGMP, ifindex, src, dst, pkt); + uint8_t type = hdr[0]; uint32_t group = rd_be32(hdr + 4); diff --git a/kernel/networking/internet_layer/ipv4.c b/kernel/networking/internet_layer/ipv4.c index 471b6b16..3811d9e6 100644 --- a/kernel/networking/internet_layer/ipv4.c +++ b/kernel/networking/internet_layer/ipv4.c @@ -122,7 +122,7 @@ bool ipv4_send_packet(uint32_t dst_ip, uint8_t proto, netpkt_t* pkt, const ip_tx } ipv4_hdr_t ip; - ip.version_ihl = (uint8_t)((IP_VERSION_4 << 4) | IP_IHL_NOOPTS); + ip.version_ihl = (uint8_t)((IP_VER4 << 4) | IP_IHL_NOOPTS); ip.dscp_ecn = 0; ip.total_length = bswap16((uint16_t)total); ip.identification = bswap16(g_ip_ident++); @@ -177,7 +177,7 @@ bool ipv4_send_packet(uint32_t dst_ip, uint8_t proto, netpkt_t* pkt, const ip_tx } ipv4_hdr_t ip; - ip.version_ihl = (uint8_t)((IP_VERSION_4 << 4) | IP_IHL_NOOPTS); + ip.version_ihl = (uint8_t)((IP_VER4 << 4) | IP_IHL_NOOPTS); ip.dscp_ecn = 0; ip.total_length = bswap16((uint16_t)frame_len); ip.identification = bswap16(ident); @@ -245,7 +245,7 @@ static void ipv4_deliver_l4(uint16_t ifindex, netpkt_t* pkt, uint32_t l4_off, ui netpkt_t* l4pkt = netpkt_view(pkt, l4_off, l4_len); if (!l4pkt) continue; uint8_t l3id = cand[i]->l3_id; - if (proto == PROTO_ICMP) icmp_input(l4pkt, src, dst); + if (proto == PROTO_ICMP) icmp_input((uint8_t)ifindex, l4pkt, src, dst); else if (proto == PROTO_IGMP) igmp_input((uint8_t)ifindex, src, dst, l4pkt); else if (proto == PROTO_TCP) tcp_input(IP_VER4, &src, &dst, l3id, l4pkt); else if (proto == PROTO_UDP) udp_input(IP_VER4, &src, &dst, l3id, l4pkt); @@ -266,7 +266,7 @@ static void ipv4_deliver_l4(uint16_t ifindex, netpkt_t* pkt, uint32_t l4_off, ui if (match_count == 1) { netpkt_t* l4pkt = netpkt_view(pkt, l4_off, l4_len); if (!l4pkt) return; - if (proto == PROTO_ICMP) icmp_input(l4pkt, src, dst); + if (proto == PROTO_ICMP) icmp_input((uint8_t)ifindex, l4pkt, src, dst); else if (proto == PROTO_IGMP) igmp_input((uint8_t)ifindex, src, dst, l4pkt); else if (proto == PROTO_TCP) tcp_input(IP_VER4, &src, &dst, match_l3id, l4pkt); else if (proto == PROTO_UDP) udp_input(IP_VER4, &src, &dst, match_l3id, l4pkt); @@ -281,7 +281,7 @@ static void ipv4_deliver_l4(uint16_t ifindex, netpkt_t* pkt, uint32_t l4_off, ui netpkt_t* l4pkt = netpkt_view(pkt, l4_off, l4_len); if (!l4pkt) continue; uint8_t l3id = cand[i]->l3_id; - if (proto == PROTO_ICMP) icmp_input(l4pkt, src, dst); + if (proto == PROTO_ICMP) icmp_input((uint8_t)ifindex, l4pkt, src, dst); else if (proto == PROTO_IGMP) igmp_input((uint8_t)ifindex, src, dst, l4pkt); else if (proto == PROTO_TCP) tcp_input(IP_VER4, &src, &dst, l3id, l4pkt); else if (proto == PROTO_UDP) udp_input(IP_VER4, &src, &dst, l3id, l4pkt); @@ -298,7 +298,7 @@ static void ipv4_deliver_l4(uint16_t ifindex, netpkt_t* pkt, uint32_t l4_off, ui netpkt_t* l4pkt = netpkt_view(pkt, l4_off, l4_len); if (!l4pkt) return; - if (proto == PROTO_ICMP) icmp_input(l4pkt, src, dst); + if (proto == PROTO_ICMP) icmp_input((uint8_t)ifindex, l4pkt, src, dst); else if (proto == PROTO_TCP) tcp_input(IP_VER4, &src, &dst, v4->l3_id, l4pkt); else if (proto == PROTO_UDP) udp_input(IP_VER4, &src, &dst, v4->l3_id, l4pkt); else netpkt_unref(l4pkt); @@ -315,7 +315,7 @@ void ipv4_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { if (!netpkt_copyout(pkt, 0, &first, sizeof(first))) return; uint8_t ver = (uint8_t)(first >> 4); uint8_t ihl = (uint8_t)(first & 0x0F); - if (ver != IP_VERSION_4) return; + if (ver != IP_VER4) return; if (ihl < IP_IHL_NOOPTS) return; uint32_t hdr_len = (uint32_t)ihl * 4; diff --git a/kernel/networking/internet_layer/ipv4.h b/kernel/networking/internet_layer/ipv4.h index 62568fcd..2a7fc7a4 100644 --- a/kernel/networking/internet_layer/ipv4.h +++ b/kernel/networking/internet_layer/ipv4.h @@ -8,7 +8,6 @@ #include "networking/netpkt.h" #define IP_IHL_NOOPTS 5 -#define IP_VERSION_4 4 #define IP_TTL_DEFAULT 64 #ifdef __cplusplus diff --git a/kernel/networking/link_layer/arp.c b/kernel/networking/link_layer/arp.c index cdf0e2e7..6f73a4b2 100644 --- a/kernel/networking/link_layer/arp.c +++ b/kernel/networking/link_layer/arp.c @@ -4,7 +4,6 @@ #include "std/string.h" #include "networking/network.h" #include "process/scheduler.h" -#include "console/kio.h" #include "networking/internet_layer/ipv4.h" #include "syscalls/syscalls.h" #include "networking/internet_layer/ipv4_utils.h" @@ -13,6 +12,8 @@ #define ARP_STALE_MS 60000 #define ARP_RETRANS_MS 1000 #define ARP_MAX_PROBES 3 +#define ARP_DAD_PROBES 3 +#define ARP_DAD_WAIT_MS 150 struct arp_table { arp_entry_t entries[ARP_TABLE_MAX]; @@ -48,7 +49,7 @@ arp_table_t* arp_table_create(void){ void arp_table_destroy(arp_table_t* t){ if (!t) return; - for (int i = 0; i < ARP_TABLE_MAX; i++) arp_entry_clear(&t->entries[i]); + for (int i = 0; i < (int)N_ARR(t->entries); i++) arp_entry_clear(&t->entries[i]); release(t); } @@ -68,7 +69,7 @@ void arp_table_init_static_defaults(arp_table_t* t){ static int arp_find_slot(arp_table_t* t, uint32_t ip){ if (!t) return -1; - for (int i=0;ientries); i++) { if (t->entries[i].state == ARP_STATE_UNUSED) continue; if (t->entries[i].ip == ip) return i; } @@ -77,7 +78,7 @@ static int arp_find_slot(arp_table_t* t, uint32_t ip){ static int arp_find_free(arp_table_t* t){ if (!t) return -1; - for (int i=0;ientries[i].state == ARP_STATE_UNUSED) return i; + for (int i = 0; i < (int)N_ARR(t->entries); i++) if (t->entries[i].state == ARP_STATE_UNUSED) return i; return -1; } @@ -86,7 +87,7 @@ static int arp_find_replacement(arp_table_t* t) { uint32_t best_ttl = 0xFFFFFFFFu; if (!t) return -1; - for (int i = 0; i < ARP_TABLE_MAX; i++) { + for (int i = 0; i < (int)N_ARR(t->entries); i++) { arp_entry_t* e = &t->entries[i]; if (e->static_entry) continue; if (e->pending_len) continue; @@ -136,7 +137,7 @@ 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 || !mac_out) return false; - for (int i=0;ientries); i++){ arp_entry_t* e = &t->entries[i]; if (e->state == ARP_STATE_UNUSED || e->state == ARP_STATE_INCOMPLETE) continue; if (e->ip != ip) continue; @@ -159,7 +160,7 @@ uint32_t arp_table_dump_for_l2(uint8_t ifindex, arp_entry_t* out, uint32_t out_c arp_table_t* t = l2_arp(ifindex); if (!t || !out || !out_cap) return 0; uint32_t n = 0; - for (int i = 0; i < ARP_TABLE_MAX && n < out_cap; i++) { + for (int i = 0; i < (int)N_ARR(t->entries) && n < out_cap; i++) { arp_entry_t* e = &t->entries[i]; if (e->state == ARP_STATE_UNUSED) continue; out[n++] = *e; @@ -167,10 +168,31 @@ uint32_t arp_table_dump_for_l2(uint8_t ifindex, arp_entry_t* out, uint32_t out_c return n; } +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; s < (int)N_ARR(l2->l3_v4); s++) { + l3_ipv4_interface_t* v4 = l2->l3_v4[s]; + if (!ipv4_l3_is_ready(v4)) continue; + if (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; s < (int)N_ARR(l2->l3_v4); s++) { + l3_ipv4_interface_t* v4 = l2->l3_v4[s]; + if (!ipv4_l3_is_ready(v4)) continue; + return v4->ip; + } + return 0; +} + 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++){ arp_entry_t* e = &t->entries[i]; if (e->state == ARP_STATE_UNUSED || e->static_entry) continue; @@ -187,7 +209,7 @@ void arp_table_tick_for_l2(uint8_t ifindex, uint32_t ms){ e->timer_ms = ARP_RETRANS_MS; e->probes_sent++; - arp_send_request_on(ifindex, e->ip); + arp_send_request_on(ifindex, pick_spa_for_l2(ifindex, e->ip), e->ip); continue; } @@ -207,7 +229,7 @@ void arp_table_tick_for_l2(uint8_t ifindex, uint32_t ms){ } void arp_tick_all(uint32_t ms){ - for (uint8_t i=1;i<=MAX_L2_INTERFACES;i++){ + 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; @@ -215,26 +237,6 @@ void arp_tick_all(uint32_t 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 (!ipv4_l3_is_ready(v4)) continue; - if (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 (!ipv4_l3_is_ready(v4)) continue; - return v4->ip; - } - return 0; -} - bool arp_send_or_queue_on(uint8_t ifindex, uint32_t ip, netpkt_t* pkt) { if (!pkt || !netpkt_len(pkt)) { if (pkt) netpkt_unref(pkt); @@ -290,16 +292,15 @@ bool arp_send_or_queue_on(uint8_t ifindex, uint32_t ip, netpkt_t* pkt) { if (!e->probes_sent || !e->timer_ms) { e->timer_ms = ARP_RETRANS_MS; e->probes_sent++; - arp_send_request_on(ifindex, ip); + arp_send_request_on(ifindex, pick_spa_for_l2(ifindex, ip), ip); } return true; } -void arp_send_request_on(uint8_t ifindex, uint32_t target_ip){ +bool arp_send_request_on(uint8_t ifindex, uint32_t sender_ip, 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); + if (!local_mac || !target_ip) return false; uint8_t dst_mac[6] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}; arp_hdr_t hdr; memset(&hdr, 0, sizeof(hdr)); @@ -309,23 +310,39 @@ void arp_send_request_on(uint8_t ifindex, uint32_t target_ip){ hdr.plen = 4; hdr.opcode = bswap16(ARP_OPCODE_REQUEST); memcpy(hdr.sender_mac, local_mac, 6); - hdr.sender_ip = bswap32(spa); + hdr.sender_ip = bswap32(sender_ip); hdr.target_ip = bswap32(target_ip); netpkt_t* pkt = netpkt_alloc((uint32_t)sizeof(hdr), (uint32_t)sizeof(eth_hdr_t), 0); - if (!pkt) return; + if (!pkt) return false; void* p = netpkt_put(pkt, (uint32_t)sizeof(hdr)); if (!p) { netpkt_unref(pkt); - return; + return false; } memcpy(p, &hdr, sizeof(hdr)); - (void)eth_send_frame_on(ifindex, ETHERTYPE_ARP, dst_mac, pkt); + return eth_send_frame_on(ifindex, ETHERTYPE_ARP, dst_mac, pkt); +} + +bool arp_dad_ipv4_on(uint8_t ifindex, uint32_t ip) { + uint8_t mac[6]; + if (!l2_arp(ifindex) || !ip || ip == 0xFFFFFFFFu || ipv4_is_multicast(ip)) return false; + if (arp_table_get_for_l2(ifindex, ip, mac)) return false; + + for (int i = 0; i < ARP_DAD_PROBES; i++) { + if (!arp_send_request_on(ifindex, 0, ip)) return false; + uint32_t start = (uint32_t)get_time(); + while ((uint32_t)get_time() - start < ARP_DAD_WAIT_MS) { + if (arp_table_get_for_l2(ifindex, ip, mac)) return false; + msleep(10); + } + } + return !arp_table_get_for_l2(ifindex, ip, mac); } 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++){ l3_ipv4_interface_t* v4 = l2->l3_v4[s]; if (!ipv4_l3_is_ready(v4)) continue; if (v4->ip == ip) return true; @@ -336,8 +353,8 @@ static bool l2_has_ip(uint8_t ifindex, uint32_t ip){ 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; + uint32_t spa = bswap32(in_arp->target_ip); + if (!l2_has_ip(ifindex, spa)) return; arp_hdr_t reply = *in_arp; memcpy(reply.target_mac, in_arp->sender_mac, 6); memcpy(reply.sender_mac, local_mac, 6); @@ -370,12 +387,7 @@ void arp_input(uint16_t ifindex, const uint8_t src_mac[6], netpkt_t* pkt) { if (sender_mac_matches && sender_is_usable) arp_table_put_for_l2((uint8_t)ifindex, sender_ip, hdr.sender_mac, ARP_REACHABLE_MS, false); if (op == ARP_OPCODE_REQUEST) { - bool has = l2_has_ip((uint8_t)ifindex, target_ip); - - uint32_t spa_guess = pick_spa_for_l2((uint8_t)ifindex, sender_ip); - if (has || (spa_guess == target_ip)) { - arp_send_reply_on((uint8_t)ifindex, &hdr, src_mac); - } + if (l2_has_ip((uint8_t)ifindex, target_ip)) arp_send_reply_on((uint8_t)ifindex, &hdr, src_mac); } } diff --git a/kernel/networking/link_layer/arp.h b/kernel/networking/link_layer/arp.h index dd5a822b..f7cdf8bf 100644 --- a/kernel/networking/link_layer/arp.h +++ b/kernel/networking/link_layer/arp.h @@ -59,7 +59,8 @@ void arp_table_tick_for_l2(uint8_t ifindex, uint32_t ms); void arp_tick_all(uint32_t ms); bool arp_send_or_queue_on(uint8_t ifindex, uint32_t ip, netpkt_t* pkt); -void arp_send_request_on(uint8_t ifindex, uint32_t target_ip); +bool arp_send_request_on(uint8_t ifindex, uint32_t sender_ip, uint32_t target_ip); +bool arp_dad_ipv4_on(uint8_t ifindex, uint32_t ip); void arp_input(uint16_t ifindex, const uint8_t src_mac[6], netpkt_t* pkt); diff --git a/kernel/networking/transport_layer/csocket.c b/kernel/networking/transport_layer/csocket.c index ee6ec140..8e856c82 100644 --- a/kernel/networking/transport_layer/csocket.c +++ b/kernel/networking/transport_layer/csocket.c @@ -1,4 +1,5 @@ #include "csocket.h" +#include "csocket_raw.h" #include "csocket_special.h" #include "csocket_tcp.h" #include "csocket_udp.h" @@ -44,6 +45,12 @@ socket_handle_t create_socket(protocol_t protocol, const SocketOptions* extra){ close = socket_close_tcp; setopt = socket_setopt_tcp; getopt = socket_getopt_tcp; + } else if (special_kind == SOCKET_SPECIAL_RAW) { + impl = socket_raw_create(socket, extra); + destroy = socket_destroy_raw; + close = socket_close_raw; + setopt = socket_setopt_raw; + getopt = socket_getopt_raw; } else { impl = socket_special_create(socket, extra); destroy = socket_destroy_special; @@ -74,7 +81,8 @@ int32_t bind_socket(socket_handle_t handle, const SockBindSpec *spec_in, uint16_ if (!socket) return SOCK_ERR_INVAL; int32_t res = SOCK_ERR_PROTO; - if (socket_core_special_kind(socket) != SOCKET_SPECIAL_NONE) res = SOCK_ERR_UNSUP; + if (socket_core_special_kind(socket) == SOCKET_SPECIAL_RAW) res = socket_bind_raw(socket_core_impl(socket), &spec); + else if (socket_core_special_kind(socket) != SOCKET_SPECIAL_NONE) res = SOCK_ERR_UNSUP; else if (socket_core_protocol(socket) == PROTO_TCP) res = socket_bind_tcp(socket_core_impl(socket), &spec, port); else if (socket_core_protocol(socket) == PROTO_UDP) res = socket_bind_udp(socket_core_impl(socket), &spec, port); @@ -88,7 +96,8 @@ int32_t connect_socket(socket_handle_t handle, const net_l4_endpoint* dst){ if (!socket) return SOCK_ERR_INVAL; int32_t res = SOCK_ERR_PROTO; - if (socket_core_special_kind(socket) != SOCKET_SPECIAL_NONE) res = SOCK_ERR_UNSUP; + if (socket_core_special_kind(socket) == SOCKET_SPECIAL_RAW) res = socket_connect_raw(socket_core_impl(socket), dst); + else if (socket_core_special_kind(socket) != SOCKET_SPECIAL_NONE) res = SOCK_ERR_UNSUP; else if (socket_core_protocol(socket) == PROTO_TCP) { res = socket_connect_tcp(socket_core_impl(socket), dst); } else if (socket_core_protocol(socket) == PROTO_UDP) res = socket_connect_udp(socket_core_impl(socket), dst); @@ -102,7 +111,8 @@ int64_t send_on_socket(socket_handle_t handle, const void* buf, uint64_t len){ if (!socket) return SOCK_ERR_INVAL; int64_t res = SOCK_ERR_PROTO; - if (socket_core_special_kind(socket) != SOCKET_SPECIAL_NONE) res = socket_send_special(socket_core_impl(socket), buf, len); + if (socket_core_special_kind(socket) == SOCKET_SPECIAL_RAW) res = socket_send_raw(socket_core_impl(socket), buf, len); + else if (socket_core_special_kind(socket) != SOCKET_SPECIAL_NONE) res = socket_send_special(socket_core_impl(socket), buf, len); else if (socket_core_protocol(socket) == PROTO_TCP) res = socket_send_tcp(socket_core_impl(socket), buf, len); else if (socket_core_protocol(socket) == PROTO_UDP) res = socket_sendto_udp(socket_core_impl(socket), NULL, buf, len); @@ -116,7 +126,8 @@ int64_t send_to_socket(socket_handle_t handle, const net_l4_endpoint* dst, const if (!socket) return SOCK_ERR_INVAL; int64_t res = SOCK_ERR_PROTO; - if (socket_core_special_kind(socket) != SOCKET_SPECIAL_NONE) res = SOCK_ERR_UNSUP; + if (socket_core_special_kind(socket) == SOCKET_SPECIAL_RAW) res = socket_sendto_raw(socket_core_impl(socket), dst, buf, len); + else if (socket_core_special_kind(socket) != SOCKET_SPECIAL_NONE) res = SOCK_ERR_UNSUP; else if (socket_core_protocol(socket) == PROTO_UDP) res = socket_sendto_udp(socket_core_impl(socket), dst, buf, len); socket_core_put(socket); @@ -128,7 +139,8 @@ int64_t receive_from_socket(socket_handle_t handle, void* buf, uint64_t len, net if (!socket) return SOCK_ERR_INVAL; int64_t res = SOCK_ERR_PROTO; - if (socket_core_special_kind(socket) != SOCKET_SPECIAL_NONE) res = socket_recv_special(socket_core_impl(socket), buf, len); + if (socket_core_special_kind(socket) == SOCKET_SPECIAL_RAW) res = socket_recv_raw(socket_core_impl(socket), buf, len, out_src); + else if (socket_core_special_kind(socket) != SOCKET_SPECIAL_NONE) res = socket_recv_special(socket_core_impl(socket), buf, len); else if (socket_core_protocol(socket) == PROTO_TCP) res = socket_recv_tcp(socket_core_impl(socket), buf, len); else if (socket_core_protocol(socket) == PROTO_UDP) res = socket_recvfrom_udp(socket_core_impl(socket), buf, len, out_src); diff --git a/kernel/networking/transport_layer/csocket_raw.c b/kernel/networking/transport_layer/csocket_raw.c new file mode 100644 index 00000000..ea2dd165 --- /dev/null +++ b/kernel/networking/transport_layer/csocket_raw.c @@ -0,0 +1,491 @@ +#include "csocket_raw.h" +#include "alloc/allocate.h" +#include "exceptions/irq.h" +#include "net/checksums.h" +#include "std/memory.h" +#include "networking/interface_manager.h" +#include "networking/internet_layer/ipv4.h" +#include "networking/internet_layer/ipv4_utils.h" +#include "networking/internet_layer/ipv6.h" +#include "networking/internet_layer/ipv6_route.h" +#include "networking/internet_layer/ipv6_utils.h" +#include "networking/link_layer/eth.h" +#include "syscalls/syscalls.h" + +#define RAW_SOCKET_MAX 64 +#define RAW_RX_RING_CAP 16 +#define RAW_RX_MAX_BYTES 32768 + +typedef struct raw_rx_entry { + uint8_t* data; + uint32_t len; + net_l4_endpoint src; +} raw_rx_entry_t; + +typedef struct raw_socket { + ksocket_t* ownerSocket; + SocketOptions options; + bool registered; + bool bound; + bool connected; + SockBindSpec bind_spec; + net_l4_endpoint remote_ep; + raw_rx_entry_t rx[RAW_RX_RING_CAP]; + uint8_t head; + uint8_t tail; + uint8_t count; + uint32_t bytes; +} raw_socket_t; + +static raw_socket_t* g_raw_sockets[RAW_SOCKET_MAX]; + +socket_impl_t socket_raw_create(ksocket_t* owner, const SocketOptions* extra) { + if (!owner || socket_core_special_kind(owner) != SOCKET_SPECIAL_RAW) return NULL; + protocol_t proto = socket_core_protocol(owner); + if (proto != PROTO_ICMP && proto != PROTO_IGMP && proto != PROTO_ICMPV6) return NULL; + + raw_socket_t* s = (raw_socket_t*)zalloc(sizeof(raw_socket_t)); + if (!s) return NULL; + + s->ownerSocket = owner; + s->options.special_kind = SOCKET_SPECIAL_RAW; + if (extra) { + if ((extra->flags & SOCK_OPT_RECV_TIMEOUT) && extra->recv_timeout_ms) { + s->options.flags |= SOCK_OPT_RECV_TIMEOUT; + s->options.recv_timeout_ms = extra->recv_timeout_ms; + } + + if ((extra->flags & SOCK_OPT_DEBUG) && extra->debug_level <= SOCK_DBG_ALL) { + s->options.flags |= SOCK_OPT_DEBUG; + s->options.debug_level = extra->debug_level; + } + + if (extra->flags & SOCK_OPT_DONTFRAG) s->options.flags |= SOCK_OPT_DONTFRAG; + if ((extra->flags & SOCK_OPT_TTL) && extra->ttl) { + s->options.flags |= SOCK_OPT_TTL; + s->options.ttl = extra->ttl; + } + } + + irq_flags_t irq = irq_save_disable(); + for (int i = 0; i < RAW_SOCKET_MAX; i++) { + if (!g_raw_sockets[i]) { + g_raw_sockets[i] = s; + s->registered = true; + irq_restore(irq); + return s; + } + } + irq_restore(irq); + release(s); + return NULL; +} + +void socket_destroy_raw(socket_impl_t sh) { + raw_socket_t* s = (raw_socket_t*)sh; + if (!s) return; + + if (s->registered) { + irq_flags_t irq = irq_save_disable(); + for (int i = 0; i < RAW_SOCKET_MAX; i++) if (g_raw_sockets[i] == s) { + g_raw_sockets[i] = NULL; + break; + } + s->registered = false; + irq_restore(irq); + } + + for (int i = 0; i < RAW_RX_RING_CAP; i++) if (s->rx[i].data) release(s->rx[i].data); + release(s); +} + +int32_t socket_close_raw(socket_impl_t sh) { + raw_socket_t* s = (raw_socket_t*)sh; + if (!s) return SOCK_ERR_INVAL; + + if (s->registered) { + irq_flags_t irq = irq_save_disable(); + for (int i = 0; i < RAW_SOCKET_MAX; i++) { + if (g_raw_sockets[i] == s) { + g_raw_sockets[i] = NULL; + break; + } + } + s->registered = false; + irq_restore(irq); + } + + return SOCK_OK; +} + +int32_t socket_setopt_raw(socket_impl_t sh, int32_t opt, const void* value, uint32_t len) { + raw_socket_t* s = (raw_socket_t*)sh; + if (!s) return SOCK_ERR_INVAL; + + switch ((uint32_t)opt) { + case SOCK_OPT_RECV_TIMEOUT: + case SOCK_OPT_DEBUG: + case SOCK_OPT_DONTFRAG: + case SOCK_OPT_TTL: + return socket_common_options_set(&s->options, opt, value, len); + default: + return SOCK_ERR_INVAL; + } +} + +int32_t socket_getopt_raw(socket_impl_t sh, int32_t opt, void* value, uint32_t* len) { + raw_socket_t* s = (raw_socket_t*)sh; + if (!s || !len) return SOCK_ERR_INVAL; + + switch ((uint32_t)opt) { + case SOCK_GET_REMOTE_ENDPOINT: + if (!value) { + *len = sizeof(net_l4_endpoint); + return SOCK_OK; + } + if (*len < sizeof(net_l4_endpoint)) return SOCK_ERR_INVAL; + memcpy(value, &s->remote_ep, sizeof(s->remote_ep)); + *len = sizeof(net_l4_endpoint); + return SOCK_OK; + case SOCK_GET_BIND_SPEC: + if (!value) { + *len = sizeof(SockBindSpec); + return SOCK_OK; + } + if (*len < sizeof(SockBindSpec)) return SOCK_ERR_INVAL; + memcpy(value, &s->bind_spec, sizeof(s->bind_spec)); + *len = sizeof(SockBindSpec); + return SOCK_OK; + default: + break; + } + + uint32_t v = 0; + switch ((uint32_t)opt) { + case SOCK_GET_BOUND: + v = s->bound; + break; + case SOCK_GET_CONNECTED: + v = s->connected; + break; + case SOCK_GET_LISTENING: + case SOCK_GET_LOCAL_PORT: + case SOCK_GET_SEND_QUEUED: + v = 0; + break; + case SOCK_GET_RECV_QUEUED: + v = s->bytes; + break; + case SOCK_GET_OPT_KEEPALIVE: + case SOCK_GET_OPT_KEEPALIVE_INTERVAL: + case SOCK_GET_OPT_TCP_NO_DELAY: + case SOCK_GET_OPT_SEND_BUF_SIZE: + case SOCK_GET_TCP_STATE: + case SOCK_GET_TCP_MSS: + case SOCK_GET_TCP_RTT_MS: + case SOCK_GET_TCP_RETRANSMITS: + case SOCK_GET_MCAST_GROUPS: + return SOCK_ERR_INVAL; + case SOCK_GET_OPT_RECV_TIMEOUT: + case SOCK_GET_OPT_DEBUG: + case SOCK_GET_OPT_DONTFRAG: + case SOCK_GET_OPT_TTL: + return socket_common_options_get(&s->options, opt, value, len); + case SOCK_GET_OPT_SEND_TIMEOUT: + case SOCK_GET_OPT_BUF_SIZE: + case SOCK_GET_OPT_BROADCAST_ALLOWED: + return SOCK_ERR_INVAL; + default: + return SOCK_ERR_INVAL; + } + + if (!value) { + *len = sizeof(uint32_t); + return SOCK_OK; + } + if (*len < sizeof(uint32_t)) return SOCK_ERR_INVAL; + memcpy(value, &v, sizeof(v)); + *len = sizeof(uint32_t); + return SOCK_OK; +} + +int32_t socket_bind_raw(socket_impl_t sh, const SockBindSpec* spec) { + raw_socket_t* s = (raw_socket_t*)sh; + if (!s || !spec) return SOCK_ERR_INVAL; + + protocol_t proto = socket_core_protocol(s->ownerSocket); + SockBindSpec next; + memset(&next, 0, sizeof(next)); + + if (spec->kind == BIND_ANY || ((proto == PROTO_ICMP || proto == PROTO_IGMP) && spec->kind == BIND_ANY4) || (proto == PROTO_ICMPV6 && spec->kind == BIND_ANY6)) { + s->bind_spec = next; + s->bound = false; + return SOCK_OK; + } + if (spec->kind == BIND_L2) { + if (!l2_interface_find_by_index(spec->ifindex)) return SOCK_ERR_INVAL; + next.kind = BIND_L2; + next.ifindex = spec->ifindex; + } else if (spec->kind == BIND_L3) { + if (proto == PROTO_ICMP || proto == PROTO_IGMP) { + if (!l3_ipv4_find_by_id(spec->l3_id)) return SOCK_ERR_INVAL; + next.ver = IP_VER4; + } else if (proto == PROTO_ICMPV6) { + if (!l3_ipv6_find_by_id(spec->l3_id)) return SOCK_ERR_INVAL; + next.ver = IP_VER6; + } else return SOCK_ERR_PROTO; + next.kind = BIND_L3; + next.l3_id = spec->l3_id; + } else if (spec->kind == BIND_IP) { + if ((proto == PROTO_ICMP || proto == PROTO_IGMP) && spec->ver == IP_VER4) { + uint32_t ip = 0; + memcpy(&ip, spec->ip, sizeof(ip)); + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_ip(ip); + if (!v4) return SOCK_ERR_INVAL; + next.kind = BIND_L3; + next.ver = IP_VER4; + next.l3_id = v4->l3_id; + } else if (proto == PROTO_ICMPV6 && spec->ver == IP_VER6) { + l3_ipv6_interface_t* v6 = l3_ipv6_find_by_ip(spec->ip); + if (!v6) return SOCK_ERR_INVAL; + next.kind = BIND_L3; + next.ver = IP_VER6; + next.l3_id = v6->l3_id; + } else return SOCK_ERR_INVAL; + } else return SOCK_ERR_INVAL; + + s->bind_spec = next; + s->bound = true; + return SOCK_OK; +} + +int32_t socket_connect_raw(socket_impl_t sh, const net_l4_endpoint* dst) { + raw_socket_t* s = (raw_socket_t*)sh; + if (!s || !dst) return SOCK_ERR_INVAL; + + protocol_t proto = socket_core_protocol(s->ownerSocket); + if (((proto == PROTO_ICMP || proto == PROTO_IGMP) && dst->ver != IP_VER4) || (proto == PROTO_ICMPV6 && dst->ver != IP_VER6)) return SOCK_ERR_INVAL; + + s->remote_ep = *dst; + s->connected = true; + return SOCK_OK; +} + +int64_t socket_send_raw(socket_impl_t sh, const void* buf, uint64_t len) { + raw_socket_t* s = (raw_socket_t*)sh; + if (!s || (!buf && len) || len > UINT32_MAX) return SOCK_ERR_INVAL; + if (!s->connected) return SOCK_ERR_NOT_BOUND; + return socket_sendto_raw(sh, &s->remote_ep, buf, len); +} + +int64_t socket_sendto_raw(socket_impl_t sh, const net_l4_endpoint* dst, const void* buf, uint64_t len) { + raw_socket_t* s = (raw_socket_t*)sh; + if (!s || !dst || (!buf && len) || !len || len > NETPKT_MAX_ALLOC) return SOCK_ERR_INVAL; + + protocol_t proto = socket_core_protocol(s->ownerSocket); + if (((proto == PROTO_ICMP || proto == PROTO_IGMP) && dst->ver != IP_VER4) || (proto == PROTO_ICMPV6 && dst->ver != IP_VER6)) return SOCK_ERR_INVAL; + + ip_tx_opts_t tx; + ip_tx_opts_t* txp = NULL; + if (s->bound && s->bind_spec.kind == BIND_L2) { + tx.scope = IP_TX_BOUND_L2; + tx.index = s->bind_spec.ifindex; + txp = &tx; + } else if (s->bound && s->bind_spec.kind == BIND_L3) { + tx.scope = IP_TX_BOUND_L3; + tx.index = s->bind_spec.l3_id; + txp = &tx; + } + + uint32_t headroom = (uint32_t)sizeof(eth_hdr_t) + ((proto == PROTO_ICMP || proto == PROTO_IGMP) ? (uint32_t)sizeof(ipv4_hdr_t) : (uint32_t)sizeof(ipv6_hdr_t)); + netpkt_t* pkt = netpkt_alloc((uint32_t)len, headroom, 0); + if (!pkt) return SOCK_ERR_SYS; + + void* p = netpkt_put(pkt, (uint32_t)len); + if (!p) { + netpkt_unref(pkt); + return SOCK_ERR_SYS; + } + memcpy(p, buf, (uint32_t)len); + + uint8_t ttl = (s->options.flags & SOCK_OPT_TTL) ? s->options.ttl : 0; + uint8_t dontfrag = (s->options.flags & SOCK_OPT_DONTFRAG) ? 1 : 0; + + if (proto == PROTO_ICMP || proto == PROTO_IGMP) { + uint32_t dst_ip = 0; + memcpy(&dst_ip, dst->ip, sizeof(dst_ip)); + return ipv4_send_packet(dst_ip, (uint8_t)proto, pkt, txp, ttl, dontfrag) ? (int64_t)len : SOCK_ERR_SYS; + } + + if (len >= 4) { + ipv6_tx_plan_t plan; + if (!ipv6_build_tx_plan(dst->ip, txp, &plan)) { + netpkt_unref(pkt); + return SOCK_ERR_SYS; + } + ((uint8_t*)p)[2] = 0; + ((uint8_t*)p)[3] = 0; + uint16_t sum = bswap16(checksum16_pipv6(plan.src_ip, dst->ip, PROTO_ICMPV6, p, (uint32_t)len)); + memcpy((uint8_t*)p + 2, &sum, sizeof(sum)); + } + + return ipv6_send_packet(dst->ip, PROTO_ICMPV6, pkt, txp, ttl ? ttl : 64, dontfrag) ? (int64_t)len : SOCK_ERR_SYS; +} + +int64_t socket_recv_raw(socket_impl_t sh, void* buf, uint64_t len, net_l4_endpoint* out_src) { + raw_socket_t* s = (raw_socket_t*)sh; + if (!s || (!buf && len) || len > UINT32_MAX) return SOCK_ERR_INVAL; + + for (;;) { + irq_flags_t irq = irq_save_disable(); + bool ready = s->count != 0; + irq_restore(irq); + if (ready) break; + if (!(s->options.flags & SOCK_OPT_RECV_TIMEOUT) || !s->options.recv_timeout_ms) return SOCK_ERR_WOULDBLOCK; + + uint32_t start_ms = (uint32_t)get_time(); + while (1) { + irq = irq_save_disable(); + ready = s->count != 0; + irq_restore(irq); + if (ready) break; + + uint32_t now_ms = (uint32_t)get_time(); + uint32_t elapsed_ms = now_ms - start_ms; + if (elapsed_ms >= s->options.recv_timeout_ms) return SOCK_ERR_WOULDBLOCK; + uint32_t wait_ms = s->options.recv_timeout_ms - elapsed_ms; + msleep(wait_ms > 5 ? 5 : wait_ms); + } + } + + irq_flags_t irq = irq_save_disable(); + uint8_t pos = s->head; + uint8_t* data = s->rx[pos].data; + uint32_t pkt_len = s->rx[pos].len; + net_l4_endpoint src = s->rx[pos].src; + s->bytes -= pkt_len; + s->rx[pos].data = NULL; + s->rx[pos].len = 0; + s->head = (uint8_t)((s->head + 1) % RAW_RX_RING_CAP); + s->count--; + irq_restore(irq); + + uint32_t n = pkt_len; + if (n > len) n = (uint32_t)len; + if (n) memcpy(buf, data, n); + if (out_src) *out_src = src; + release(data); + return n; +} + +bool socket_raw_input_v4(protocol_t protocol, uint8_t ifindex, uint32_t src, uint32_t dst, netpkt_t* pkt) { + if ((protocol != PROTO_ICMP && protocol != PROTO_IGMP) || !ifindex || !pkt) return false; + + net_l4_endpoint src_ep; + memset(&src_ep, 0, sizeof(src_ep)); + src_ep.ver = IP_VER4; + memcpy(src_ep.ip, &src, sizeof(src)); + + raw_socket_t* targets[RAW_SOCKET_MAX]; + int n = 0; + irq_flags_t irq = irq_save_disable(); + for (int i = 0; i < RAW_SOCKET_MAX; i++) { + raw_socket_t* s = g_raw_sockets[i]; + if (!s || socket_core_protocol(s->ownerSocket) != protocol || socket_core_is_closing(s->ownerSocket)) continue; + if (s->connected) { + uint32_t remote = 0; + memcpy(&remote, s->remote_ep.ip, sizeof(remote)); + if (remote != src) continue; + } + if (s->bound && s->bind_spec.kind == BIND_L2 && s->bind_spec.ifindex != ifindex) continue; + if (s->bound && s->bind_spec.kind == BIND_L3) { + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(s->bind_spec.l3_id); + if (!v4 || !v4->l2 || v4->l2->ifindex != ifindex) continue; + if (!ipv4_is_multicast(dst) && dst != 0xFFFFFFFF && (!v4->mask || ipv4_broadcast_calc(v4->ip, v4->mask) != dst) && v4->ip != dst) continue; + } + socket_core_ref(s->ownerSocket); + targets[n++] = s; + } + irq_restore(irq); + + bool delivered = false; + uint32_t len = netpkt_len(pkt); + for (int i = 0; i < n; i++) { + raw_socket_t* s = targets[i]; + if (len && len <= NETPKT_MAX_ALLOC && len <= RAW_RX_MAX_BYTES) { + uint8_t* copy = (uint8_t*)zalloc(len); + if (copy && netpkt_copyout(pkt, 0, copy, len)) { + irq = irq_save_disable(); + if (s->count < RAW_RX_RING_CAP && s->bytes <= RAW_RX_MAX_BYTES - len) { + uint8_t pos = s->tail; + s->rx[pos].data = copy; + s->rx[pos].len = len; + s->rx[pos].src = src_ep; + s->tail = (uint8_t)((s->tail + 1) % RAW_RX_RING_CAP); + s->count++; + s->bytes += len; + copy = NULL; + delivered = true; + } + irq_restore(irq); + } + if (copy) release(copy); + } + socket_core_put(s->ownerSocket); + } + return delivered; +} + +bool socket_raw_input_v6(uint8_t ifindex, const uint8_t src[16], const uint8_t dst[16], netpkt_t* pkt) { + if (!ifindex || !src || !dst || !pkt) return false; + + net_l4_endpoint src_ep; + memset(&src_ep, 0, sizeof(src_ep)); + src_ep.ver = IP_VER6; + ipv6_cpy(src_ep.ip, src); + + raw_socket_t* targets[RAW_SOCKET_MAX]; + int n = 0; + irq_flags_t irq = irq_save_disable(); + for (int i = 0; i < RAW_SOCKET_MAX; i++) { + raw_socket_t* s = g_raw_sockets[i]; + if (!s || socket_core_protocol(s->ownerSocket) != PROTO_ICMPV6 || socket_core_is_closing(s->ownerSocket)) continue; + if (s->connected && ipv6_cmp(s->remote_ep.ip, src) != 0) continue; + if (s->bound && s->bind_spec.kind == BIND_L2 && s->bind_spec.ifindex != ifindex) continue; + if (s->bound && s->bind_spec.kind == BIND_L3) { + l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(s->bind_spec.l3_id); + if (!v6 || !v6->l2 || v6->l2->ifindex != ifindex) continue; + if (!ipv6_is_multicast(dst) && ipv6_cmp(v6->ip, dst) != 0) continue; + } + socket_core_ref(s->ownerSocket); + targets[n++] = s; + } + irq_restore(irq); + + bool delivered = false; + uint32_t len = netpkt_len(pkt); + for (int i = 0; i < n; i++) { + raw_socket_t* s = targets[i]; + if (len && len <= NETPKT_MAX_ALLOC && len <= RAW_RX_MAX_BYTES) { + uint8_t* copy = (uint8_t*)zalloc(len); + if (copy && netpkt_copyout(pkt, 0, copy, len)) { + irq = irq_save_disable(); + if (s->count < RAW_RX_RING_CAP && s->bytes <= RAW_RX_MAX_BYTES - len) { + uint8_t pos = s->tail; + s->rx[pos].data = copy; + s->rx[pos].len = len; + s->rx[pos].src = src_ep; + s->tail = (uint8_t)((s->tail + 1) % RAW_RX_RING_CAP); + s->count++; + s->bytes += len; + copy = NULL; + delivered = true; + } + irq_restore(irq); + } + if (copy) release(copy); + } + socket_core_put(s->ownerSocket); + } + return delivered; +} diff --git a/kernel/networking/transport_layer/csocket_raw.h b/kernel/networking/transport_layer/csocket_raw.h new file mode 100644 index 00000000..88b67adb --- /dev/null +++ b/kernel/networking/transport_layer/csocket_raw.h @@ -0,0 +1,25 @@ +#pragma once + +#include "socket_core.h" +#include "networking/netpkt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +socket_impl_t socket_raw_create(ksocket_t* owner, const SocketOptions* extra); +void socket_destroy_raw(socket_impl_t sh); +int32_t socket_close_raw(socket_impl_t sh); +int32_t socket_setopt_raw(socket_impl_t sh, int32_t opt, const void* value, uint32_t len); +int32_t socket_getopt_raw(socket_impl_t sh, int32_t opt, void* value, uint32_t* len); +int32_t socket_bind_raw(socket_impl_t sh, const SockBindSpec* spec); +int32_t socket_connect_raw(socket_impl_t sh, const net_l4_endpoint* dst); +int64_t socket_send_raw(socket_impl_t sh, const void* buf, uint64_t len); +int64_t socket_sendto_raw(socket_impl_t sh, const net_l4_endpoint* dst, const void* buf, uint64_t len); +int64_t socket_recv_raw(socket_impl_t sh, void* buf, uint64_t len, net_l4_endpoint* out_src); +bool socket_raw_input_v4(protocol_t protocol, uint8_t ifindex, uint32_t src, uint32_t dst, netpkt_t* pkt); +bool socket_raw_input_v6(uint8_t ifindex, const uint8_t src[16], const uint8_t dst[16], netpkt_t* pkt); + +#ifdef __cplusplus +} +#endif diff --git a/kernel/networking/transport_layer/csocket_special.c b/kernel/networking/transport_layer/csocket_special.c index f2720446..c6a10df0 100644 --- a/kernel/networking/transport_layer/csocket_special.c +++ b/kernel/networking/transport_layer/csocket_special.c @@ -5,7 +5,6 @@ typedef struct special_socket { ksocket_t* ownerSocket; - SocketOptions options; uint8_t* rx_buf; uint32_t rx_len; uint32_t rx_off; @@ -18,7 +17,6 @@ socket_impl_t socket_special_create(ksocket_t* owner, const SocketOptions* extra if (!s) return NULL; s->ownerSocket = owner; - if (extra) s->options = *extra; return s; } @@ -34,15 +32,11 @@ int32_t socket_close_special(socket_impl_t sh) { } int32_t socket_setopt_special(socket_impl_t sh, int32_t opt, const void* value, uint32_t len) { - special_socket_t* s = (special_socket_t*)sh; - if (!s) return SOCK_ERR_INVAL; - return socket_common_options_set(&s->options, opt, value, len); + return sh ? SOCK_ERR_UNSUP : SOCK_ERR_INVAL; } int32_t socket_getopt_special(socket_impl_t sh, int32_t opt, void* value, uint32_t* len) { - special_socket_t* s = (special_socket_t*)sh; - if (!s) return SOCK_ERR_INVAL; - return socket_common_options_get(&s->options, opt, value, len); + return sh ? SOCK_ERR_UNSUP : SOCK_ERR_INVAL; } int64_t socket_send_special(socket_impl_t sh, const void* buf, uint64_t len) { diff --git a/kernel/networking/transport_layer/net_ctrl.c b/kernel/networking/transport_layer/net_ctrl.c index 0e03acf3..c733726c 100644 --- a/kernel/networking/transport_layer/net_ctrl.c +++ b/kernel/networking/transport_layer/net_ctrl.c @@ -353,7 +353,7 @@ static bool net_ctrl_route_dump(const net_ctrl_attrs_t* a, buffer* b) { return true; } -static int32_t net_ctrl_route_set(const net_ctrl_attrs_t* a) { +static int32_t net_ctrl_route_apply(const net_ctrl_attrs_t* a, bool add) { if (!NET_CTRL_HAS(a, NET_CTRL_EXT_L3_ID) || !NET_CTRL_HAS(a, NET_CTRL_EXT_ADDRESS) || !NET_CTRL_HAS(a, NET_CTRL_EXT_PREFIX_LEN)) return SOCK_ERR_INVAL; uint16_t metric = NET_CTRL_HAS(a, NET_CTRL_EXT_METRIC) ? a->metric : 0; if (!l3_is_v6_from_id(a->l3_id)) { @@ -367,21 +367,45 @@ static int32_t net_ctrl_route_set(const net_ctrl_attrs_t* a) { if (a->gateway.ver != IP_VER4) return SOCK_ERR_INVAL; memcpy(&gw, a->gateway.ip, sizeof(gw)); } + if (!v4->routing_table && !add) return SOCK_ERR_NOT_FOUND; if (!v4->routing_table) v4->routing_table = ipv4_rt_create(v4->l3_id); if (!v4->routing_table) return SOCK_ERR_SYS; uint32_t mask = 0; if (a->prefix_len >= 32) mask = 0xFFFFFFFF; else if (a->prefix_len) mask = 0xFFFFFFFF << (32 -a->prefix_len); + bool exists = false; + int n = ipv4_rt_count((const ipv4_rt_table_t*)v4->routing_table); + for (int i = 0; i < n; i++) { + ipv4_rt_entry_t e; + if (ipv4_rt_get((const ipv4_rt_table_t*)v4->routing_table, i, &e) && e.network == (network & mask) && e.mask == mask) { + exists = true; + break; + } + } + if (add && exists) return SOCK_ERR_EXIST; + if (!add && !exists) return SOCK_ERR_NOT_FOUND; return ipv4_rt_add_in((ipv4_rt_table_t*)v4->routing_table, network & mask, mask, gw, metric) ? SOCK_OK : SOCK_ERR_INVAL; } l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(a->l3_id); if (!v6 || v6->is_localhost || a->address.ver != IP_VER6 || a->prefix_len > 128) return SOCK_ERR_INVAL; if (NET_CTRL_HAS(a, NET_CTRL_EXT_GATEWAY) && a->gateway.ver != IP_VER6) return SOCK_ERR_INVAL; const uint8_t* gw = NET_CTRL_HAS(a, NET_CTRL_EXT_GATEWAY) ? a->gateway.ip : (const uint8_t[16]){0}; + if (!v6->routing_table && !add) return SOCK_ERR_NOT_FOUND; if (!v6->routing_table) v6->routing_table = ipv6_rt_create(v6->l3_id); if (!v6->routing_table) return SOCK_ERR_SYS; uint8_t net[16]; ipv6_prefix_network(a->address.ip, a->prefix_len, net); + bool exists = false; + int n = ipv6_rt_count((const ipv6_rt_table_t*)v6->routing_table); + for (int i = 0; i < n; i++) { + ipv6_rt_entry_t e; + if (ipv6_rt_get((const ipv6_rt_table_t*)v6->routing_table, i, &e) && e.prefix_len == a->prefix_len && ipv6_cmp(e.network, net) == 0) { + exists = true; + break; + } + } + if (add && exists) return SOCK_ERR_EXIST; + if (!add && !exists) return SOCK_ERR_NOT_FOUND; return ipv6_rt_add_in((ipv6_rt_table_t*)v6->routing_table, net, a->prefix_len, gw, metric) ? SOCK_OK : SOCK_ERR_INVAL; } @@ -454,20 +478,42 @@ static bool net_ctrl_neigh_dump(const net_ctrl_attrs_t* a, buffer* b) { return true; } -static int32_t net_ctrl_neigh_set(const net_ctrl_attrs_t* a) { +static int32_t net_ctrl_neigh_set(const net_ctrl_attrs_t* a, bool add) { if (!NET_CTRL_HAS(a, NET_CTRL_EXT_ADDRESS) || !NET_CTRL_HAS(a, NET_CTRL_EXT_MAC)) return SOCK_ERR_INVAL; l2_interface_t* l2 = net_ctrl_l2_from_attrs(a); if (!l2) return SOCK_ERR_INVAL; - uint32_t ttl = NET_CTRL_HAS(a, NET_CTRL_EXT_TTL_MS) ? a->ttl_ms : 0; + bool exists = false; if (a->address.ver == IP_VER4) { uint32_t ip = 0; memcpy(&ip, a->address.ip, sizeof(ip)); + arp_entry_t ae[ARP_TABLE_MAX]; + uint32_t n = arp_table_dump_for_l2(l2->ifindex, ae, ARP_TABLE_MAX); + for (uint32_t i = 0; i < n; i++) { + if (ae[i].ip == ip) { + exists = true; + break; + } + } + if (add && exists) return SOCK_ERR_EXIST; + if (!add && !exists) return SOCK_ERR_NOT_FOUND; if (!l2->arp_table) return SOCK_ERR_INVAL; + uint32_t ttl = NET_CTRL_HAS(a, NET_CTRL_EXT_TTL_MS) ? a->ttl_ms : 0; arp_table_put_for_l2(l2->ifindex, ip, a->mac, ttl, NET_CTRL_HAS(a, NET_CTRL_EXT_FLAGS) && (a->flags & NET_CTRL_NEIGH_F_STATIC)); return SOCK_OK; } if (a->address.ver == IP_VER6) { + ndp_entry_t ne[NDP_TABLE_MAX]; + uint32_t n = ndp_table_dump_for_l2(l2->ifindex, ne, NDP_TABLE_MAX); + for (uint32_t i = 0; i < n; i++) { + if (ipv6_cmp(ne[i].ip, a->address.ip) == 0) { + exists = true; + break; + } + } + if (add && exists) return SOCK_ERR_EXIST; + if (!add && !exists) return SOCK_ERR_NOT_FOUND; if (!l2->nd_table) return SOCK_ERR_INVAL; + uint32_t ttl = NET_CTRL_HAS(a, NET_CTRL_EXT_TTL_MS) ? a->ttl_ms : 0; bool router = NET_CTRL_HAS(a, NET_CTRL_EXT_FLAGS) && (a->flags & NET_CTRL_NEIGH_F_ROUTER); bool is_static = NET_CTRL_HAS(a, NET_CTRL_EXT_FLAGS) && (a->flags & NET_CTRL_NEIGH_F_STATIC); ndp_table_put_for_l2(l2->ifindex, a->address.ip, a->mac, ttl, router, is_static); @@ -493,7 +539,6 @@ static int32_t net_ctrl_neigh_del(const net_ctrl_attrs_t* a) { return SOCK_ERR_INVAL; } -//TODO improve handling of UPD and ADD cases in neight e route int32_t net_ctrl_dispatch(const void* req, uint32_t req_len, uint8_t** out, uint32_t* out_len) { if (!req || req_len < sizeof(NetCtrlMsg) || !out || !out_len) return SOCK_ERR_INVAL; *out = NULL; @@ -555,8 +600,10 @@ int32_t net_ctrl_dispatch(const void* req, uint32_t req_len, uint8_t** out, uint status = net_ctrl_route_dump(&attrs, &b) ? SOCK_OK : SOCK_ERR_SYS; break; case NET_CTRL_OP_ADD: + status = net_ctrl_route_apply(&attrs, true); + break; case NET_CTRL_OP_UPD: - status = net_ctrl_route_set(&attrs); + status = net_ctrl_route_apply(&attrs, false); break; case NET_CTRL_OP_DEL: status = net_ctrl_route_del(&attrs); @@ -572,8 +619,10 @@ int32_t net_ctrl_dispatch(const void* req, uint32_t req_len, uint8_t** out, uint status = net_ctrl_neigh_dump(&attrs, &b) ? SOCK_OK : SOCK_ERR_SYS; break; case NET_CTRL_OP_ADD: + status = net_ctrl_neigh_set(&attrs, true); + break; case NET_CTRL_OP_UPD: - status = net_ctrl_neigh_set(&attrs); + status = net_ctrl_neigh_set(&attrs, false); break; case NET_CTRL_OP_DEL: status = net_ctrl_neigh_del(&attrs); diff --git a/kernel/tools/tracert.c b/kernel/tools/tracert.c index ad9aeef9..bc2e8ebe 100644 --- a/kernel/tools/tracert.c +++ b/kernel/tools/tracert.c @@ -26,7 +26,7 @@ typedef struct { bool src_set; const char *host; } tr_opts_t; - +//TODO raw socket static bool parse_args(int argc, char *argv[], tr_opts_t *o) { o->ver = IP_VER4; o->max_ttl = 30; diff --git a/shared b/shared index 69cb634a..bfef207c 160000 --- a/shared +++ b/shared @@ -1 +1 @@ -Subproject commit 69cb634a1083aad5e5373b18f9a150d8b76065c7 +Subproject commit bfef207c4de843388b8fea5e421f851dfc642570 diff --git a/user/demo/default_process.c b/user/demo/default_process.c index ba75afe4..f2a9edce 100644 --- a/user/demo/default_process.c +++ b/user/demo/default_process.c @@ -10,6 +10,7 @@ #include "memory/memory.h" #include "files/helpers.h" #include "utils/clipboard.h" +#include "net/net_ctrl.h" #define BORDER 20 @@ -85,6 +86,163 @@ int net_example() { return 1; } +int net_ctrl_example() { + //create ctrl socket + SocketOptions opt = {}; + opt.special_kind = SOCKET_SPECIAL_CTRL; + socket_handle_t sock = socket_create(PROTO_NONE, &opt); + if (!sock) return -1; + + //create a REQUEST to get ADDR + NetCtrlMsg req = {}; + req.object = NET_CTRL_OBJ_ADDR; + req.op = NET_CTRL_OP_GET; + req.flags = NET_CTRL_F_REQUEST; + req.length = sizeof(req); + + if (socket_send(sock, &req, req.length) < 0) { + socket_close(sock); + return -1; + } + + //read response + uint8_t rx[1024]; + int64_t n = socket_receive(sock, rx, sizeof(rx), NULL); + if (n < (int64_t)sizeof(NetCtrlMsg)) { + socket_close(sock); + return -1; + } + + NetCtrlMsg* res = (NetCtrlMsg*)rx; + if (res->status != SOCK_OK) { + socket_close(sock); + return -1; + } + + //parse respose + NetCtrlAddrInfo* addrs = (NetCtrlAddrInfo*)NET_CTRL_MSG_DATA(res); + uint32_t count = NET_CTRL_MSG_PAYLOAD_LEN(res) / sizeof(NetCtrlAddrInfo); + NetCtrlAddrInfo* main_v4 = NULL; + + //find a valid ip + for (uint32_t i = 0; i < count; i++) { + if (addrs[i].prefix.address.ver != IP_VER4) continue; + if (addrs[i].config != IPV4_CFG_DHCP && addrs[i].config != IPV4_CFG_STATIC) continue; + main_v4 = &addrs[i]; + break; + } + + if (!main_v4) { + socket_close(sock); + return -1; + } + + uint32_t current_ip = 0; + memcpy(¤t_ip, main_v4->prefix.address.ip, sizeof(current_ip)); + + uint32_t mask = 0xFFFFFFFF << (32 - main_v4->prefix.prefix_len); + + print("main IPv4 is %u.%u.%u.%u/%u mask %u.%u.%u.%u", + current_ip >> 24, (current_ip >> 16) & 0xFF, (current_ip >> 8) & 0xFF, current_ip & 0xFF, + main_v4->prefix.prefix_len, mask >> 24, (mask >> 16) & 0xFF, (mask >> 8) & 0xFF, mask & 0xFF); + + uint8_t msg_buf[128]; + memset(msg_buf, 0, sizeof(msg_buf)); + + //create a REQUEST to add OBJ_ADDR + NetCtrlMsg* msg = (NetCtrlMsg*)msg_buf; + msg->object = NET_CTRL_OBJ_ADDR; + msg->op = NET_CTRL_OP_ADD; + msg->flags = NET_CTRL_F_REQUEST; + + uint32_t off = sizeof(NetCtrlMsg); + + //reuse the same L2 interface as the found ipv4 + NetCtrlAttr* attr = (NetCtrlAttr*)(msg_buf + off); + attr->ext = NET_CTRL_EXT_IFINDEX; + attr->length = sizeof(main_v4->prefix.ifindex); + off += sizeof(NetCtrlAttr); + memcpy(msg_buf + off, &main_v4->prefix.ifindex, sizeof(main_v4->prefix.ifindex)); + off += sizeof(main_v4->prefix.ifindex); + + //reuse the same mask (prefix length) + attr = (NetCtrlAttr*)(msg_buf + off); + attr->ext = NET_CTRL_EXT_PREFIX_LEN; + attr->length = sizeof(main_v4->prefix.prefix_len); + off += sizeof(NetCtrlAttr); + memcpy(msg_buf + off, &main_v4->prefix.prefix_len, sizeof(main_v4->prefix.prefix_len)); + off += sizeof(main_v4->prefix.prefix_len); + + uint32_t host_mask = ~mask; + if (host_mask <= 1) { + socket_close(sock); + return -1; + } + + uint32_t gateway_ip = 0; + if (main_v4->prefix.gateway.ver == IP_VER4) memcpy(&gateway_ip, main_v4->prefix.gateway.ip, sizeof(gateway_ip)); + + uint32_t network = current_ip & mask; + uint32_t first_host = 1; + uint32_t last_host = host_mask - 1; + if (host_mask > 32) { + first_host = 10; + last_host = host_mask - 10; + } + uint32_t host_span = last_host - first_host + 1; + uint32_t host = first_host + (((uint32_t)get_time() ^ current_ip) % host_span); + uint32_t static_ip = network | host; + for (uint32_t i = 0; i < host_span; i++) { + if (static_ip != current_ip && static_ip != gateway_ip) break; + host++; + if (host > last_host) host = first_host; + static_ip = network | host; + } + + print("adding static %u.%u.%u.%u on the same /%u network", + static_ip >> 24, (static_ip >> 16) & 0xFF, (static_ip >> 8) & 0xFF, static_ip & 0xFF, main_v4->prefix.prefix_len); + + //add another address in the same network and with the same mask + net_l4_endpoint static_addr = {}; + static_addr.ver = IP_VER4; + memcpy(static_addr.ip, &static_ip, sizeof(static_ip)); + + attr = (NetCtrlAttr*)(msg_buf + off); + attr->ext = NET_CTRL_EXT_ADDRESS; + attr->length = sizeof(static_addr); + off += sizeof(NetCtrlAttr); + memcpy(msg_buf + off, &static_addr, sizeof(static_addr)); + off += sizeof(static_addr); + + //static config + int16_t cfg = IPV4_CFG_STATIC; + attr = (NetCtrlAttr*)(msg_buf + off); + attr->ext = NET_CTRL_EXT_CONFIG; + attr->length = sizeof(cfg); + off += sizeof(NetCtrlAttr); + memcpy(msg_buf + off, &cfg, sizeof(cfg)); + off += sizeof(cfg); + msg->length = off; + + int64_t sent = socket_send(sock, msg, msg->length); + if (sent != (int64_t)msg->length) { + socket_close(sock); + return -1; + } + + n = socket_receive(sock, rx, sizeof(rx), NULL); + if (n < (int64_t)sizeof(NetCtrlMsg)) { + socket_close(sock); + return -1; + } + + res = (NetCtrlMsg*)rx; + if (res->status != SOCK_OK) print("add failed with status %i", res->status); + else print("add ok"); + socket_close(sock); + return res->status == SOCK_OK ? 0 : -1; +} + static int8_t mixin[MIXER_INPUTS] = { NULL }; static audio_samples audio[MIXER_INPUTS]; From 9014a7fdea41727ea2876efab5eea0a5196fd1c1 Mon Sep 17 00:00:00 2001 From: CodeAnarchist Date: Tue, 23 Jun 2026 00:03:05 +0200 Subject: [PATCH 29/39] [net ]move ping and traceroute to raw --- kernel/networking/application_layer/dhcp.c | 5 +- kernel/networking/application_layer/dhcp.h | 2 +- .../application_layer/dhcp_daemon.c | 105 ++++--- kernel/networking/application_layer/dhcpv6.c | 9 +- kernel/networking/application_layer/dhcpv6.h | 7 +- .../application_layer/dhcpv6_daemon.c | 54 ++-- kernel/networking/application_layer/ntp.c | 1 + kernel/networking/application_layer/sntp.c | 1 + kernel/networking/interface_manager.c | 15 +- kernel/networking/internet_layer/icmp.c | 148 ---------- kernel/networking/internet_layer/icmp.h | 27 -- kernel/networking/internet_layer/icmpv6.c | 155 ---------- kernel/networking/internet_layer/icmpv6.h | 26 +- kernel/networking/internet_layer/ipv4.c | 18 +- kernel/networking/internet_layer/ipv4.h | 3 +- kernel/networking/internet_layer/ipv4_utils.c | 40 ++- kernel/networking/internet_layer/ipv4_utils.h | 5 +- kernel/networking/internet_layer/ipv6.c | 11 +- kernel/networking/internet_layer/ipv6_utils.c | 1 + kernel/networking/internet_layer/ipv6_utils.h | 1 + kernel/networking/link_layer/arp.c | 51 ++-- kernel/networking/link_layer/arp.h | 14 +- kernel/networking/link_layer/eth.c | 16 +- kernel/networking/link_layer/eth.h | 11 +- kernel/networking/link_layer/link_utils.c | 28 +- kernel/networking/link_layer/link_utils.h | 8 +- kernel/networking/link_layer/ndp.c | 32 ++- kernel/networking/network_dispatch.cpp | 2 +- kernel/networking/transport_layer/csocket.c | 9 +- .../networking/transport_layer/csocket_raw.c | 170 +++++++---- .../networking/transport_layer/csocket_tcp.c | 4 + .../networking/transport_layer/csocket_udp.c | 73 ++--- kernel/networking/transport_layer/net_ctrl.c | 9 +- .../networking/transport_layer/tcp/tcp_rx.c | 1 + .../transport_layer/tcp/tcp_utils.c | 1 + kernel/networking/transport_layer/udp.c | 1 + kernel/tools/icmp_probe.c | 237 +++++++++++++++ kernel/tools/icmp_probe.h | 41 +++ kernel/tools/ping.c | 270 ++++-------------- kernel/tools/tracert.c | 134 ++++----- 40 files changed, 799 insertions(+), 947 deletions(-) create mode 100644 kernel/tools/icmp_probe.c create mode 100644 kernel/tools/icmp_probe.h diff --git a/kernel/networking/application_layer/dhcp.c b/kernel/networking/application_layer/dhcp.c index c161823a..1a794e02 100644 --- a/kernel/networking/application_layer/dhcp.c +++ b/kernel/networking/application_layer/dhcp.c @@ -2,6 +2,7 @@ #include "std/memory.h" #include "networking/transport_layer/udp.h" #include "networking/internet_layer/ipv4.h" +#include "networking/link_layer/link_utils.h" #include "types.h" #include "syscalls/syscalls.h" @@ -23,7 +24,7 @@ sizedptr dhcp_build_packet(const dhcp_request *req, uint8_t msg_type, uint32_t x p.op = 1; p.htype = 1; - p.hlen = 6; + p.hlen = MAC_ADDR_LEN; p.hops = 0; p.xid = xid; p.secs = 0; @@ -32,7 +33,7 @@ sizedptr dhcp_build_packet(const dhcp_request *req, uint8_t msg_type, uint32_t x p.yiaddr = 0; p.siaddr = 0; p.giaddr = 0; - memcpy(p.chaddr, req->mac, 6); + mac_copy(p.chaddr, req->mac); if (msg_type == DHCPINFORM) p.ciaddr = req->offered_ip; if (msg_type == DHCPREQUEST && (kind == DHCPK_RENEW || kind == DHCPK_REBIND)) p.ciaddr = req->offered_ip; diff --git a/kernel/networking/application_layer/dhcp.h b/kernel/networking/application_layer/dhcp.h index 2483c077..a4f93df9 100644 --- a/kernel/networking/application_layer/dhcp.h +++ b/kernel/networking/application_layer/dhcp.h @@ -46,7 +46,7 @@ typedef struct __attribute__((packed)) { } dhcp_packet; typedef struct { - uint8_t mac[6]; + uint8_t mac[MAC_ADDR_LEN]; uint32_t server_ip; uint32_t offered_ip; } dhcp_request; diff --git a/kernel/networking/application_layer/dhcp_daemon.c b/kernel/networking/application_layer/dhcp_daemon.c index 17f6e00d..7e0b10c6 100644 --- a/kernel/networking/application_layer/dhcp_daemon.c +++ b/kernel/networking/application_layer/dhcp_daemon.c @@ -10,6 +10,7 @@ #include "networking/internet_layer/ipv4_utils.h" #include "net/network_types.h" #include "networking/link_layer/arp.h" +#include "networking/link_layer/link_utils.h" #include "networking/transport_layer/udp.h" #include "networking/transport_layer/csocket.h" @@ -39,7 +40,7 @@ typedef struct { uint32_t last_xid; uint32_t trans_xid; uint32_t server_ip_net; - uint8_t mac[6]; + uint8_t mac[MAC_ADDR_LEN]; bool mac_ok; bool needs_inform; socket_handle_t sock; @@ -116,7 +117,10 @@ static void ensure_inventory() { 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; } + if (m) { + mac_copy(st.mac, m); + st.mac_ok = true; + } st.needs_inform = (v4->mode == IPV4_CFG_STATIC && v4->ip != 0); st.sock = create_socket(PROTO_UDP, NULL); if (!st.sock) continue; @@ -140,13 +144,7 @@ static void ensure_inventory() { } } -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 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) { +static bool udp_wait_for_type_on(socket_handle_t sock, uint8_t wanted, uint32_t expect_xid, const uint8_t mac[MAC_ADDR_LEN], dhcp_packet** outp, sizedptr* outsp, uint32_t timeout_ms) { uint32_t waited = 0; while(waited < timeout_ms){ uint8_t buf[1024]; @@ -154,34 +152,33 @@ static bool udp_wait_for_type_on(socket_handle_t sock, uint8_t wanted, uint32_t memset(&src, 0, sizeof(src)); int64_t r = receive_from_socket(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; } + 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) { 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; } + if (p->htype != 1) continue; + if (p->hlen != MAC_ADDR_LEN) continue; + if (!dhcp_has_valid_cookie(p)) continue; + if (expect_xid && p->xid != expect_xid) continue; + if (mac && !mac_equal(p->chaddr, mac)) continue; uint16_t idx = dhcp_parse_option_bounded(p, (uint32_t)r, 53); - if (idx == UINT16_MAX) { continue; } + if (idx == UINT16_MAX) continue; uint8_t len = p->options[idx+1]; - if (len < 1) { continue; } - if (p->options[idx+2] != wanted) { continue; } + 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 { - msleep(50); - waited += 50; } + msleep(50); + waited += 50; } return false; } -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) { +static bool udp_wait_for_ack_or_nak(socket_handle_t sock, uint32_t expect_xid, const uint8_t mac[MAC_ADDR_LEN], 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]; @@ -189,27 +186,26 @@ static bool udp_wait_for_ack_or_nak(socket_handle_t sock, uint32_t expect_xid, c memset(&src, 0, sizeof(src)); int64_t r = receive_from_socket(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; } + 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; } + if (p->htype != 1 || p->hlen != MAC_ADDR_LEN) continue; + if (!dhcp_has_valid_cookie(p)) continue; + if (expect_xid && p->xid != expect_xid) continue; + if (mac && !mac_equal(p->chaddr, mac)) continue; uint16_t idx = dhcp_parse_option_bounded(p, (uint32_t)r, 53); - if (idx == UINT16_MAX || p->options[idx+1] < 1) { continue; } + if (idx == UINT16_MAX || p->options[idx+1] < 1) continue; uint8_t mtype = p->options[idx+2]; - if (mtype != DHCPACK && mtype != DHCPNAK) { continue; } + 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 { - msleep(50); - waited += 50; } + msleep(50); + waited += 50; } return false; } @@ -321,30 +317,21 @@ static void dhcp_send_discover_for(dhcp_if_state_t* st) { st->trans_xid = xid; dhcp_request req; memset(&req, 0, sizeof(req)); - if (st->mac_ok) memcpy(req.mac, st->mac, 6); + if (st->mac_ok) mac_copy(req.mac, st->mac); sizedptr pkt = dhcp_build_packet(&req, DHCPDISCOVER, xid, DHCPK_DISCOVER, true); - uint32_t bcast = 0xFFFFFFFFu; + uint32_t bcast = IPV4_LIMITED_BROADCAST; net_l4_endpoint dst; make_ep(&bcast, 67, IP_VER4, &dst); send_to_socket(st->sock, &dst, (const void*)pkt.ptr, pkt.size); free_sized((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); - uint32_t dip = 0xFFFFFFFFu; - net_l4_endpoint dst; - make_ep(&dip, 67, IP_VER4, &dst); - send_to_socket(st->sock, &dst, (const void*)pkt.ptr, pkt.size); - free_sized((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); + if (st->mac_ok) mac_copy(req.mac, st->mac); uint32_t ip_net = bswap32(v4->ip); req.offered_ip = ip_net; req.server_ip = st->server_ip_net; @@ -352,7 +339,7 @@ static void dhcp_send_renew_for(dhcp_if_state_t* st) { 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); - uint32_t dip = st->server_ip_net ? st->server_ip_net : 0xFFFFFFFFu; + uint32_t dip = st->server_ip_net ? st->server_ip_net : IPV4_LIMITED_BROADCAST; net_l4_endpoint dst; make_ep(&dip, 67, IP_VER4, &dst); send_to_socket(st->sock, &dst, (const void*)pkt.ptr, pkt.size); @@ -364,7 +351,7 @@ static void dhcp_send_rebind_for(dhcp_if_state_t* st) { if (!v4) return; dhcp_request req; memset(&req, 0, sizeof(req)); - if (st->mac_ok) memcpy(req.mac, st->mac, 6); + if (st->mac_ok) mac_copy(req.mac, st->mac); uint32_t ip_net = bswap32(v4->ip); req.offered_ip = ip_net; req.server_ip = 0; @@ -372,7 +359,7 @@ static void dhcp_send_rebind_for(dhcp_if_state_t* st) { rng_init_random(&rng); st->trans_xid = rng_next32(&rng); sizedptr pkt = dhcp_build_packet(&req, DHCPREQUEST, st->trans_xid, DHCPK_REBIND, true); - uint32_t dip = 0xFFFFFFFFu; + uint32_t dip = IPV4_LIMITED_BROADCAST; net_l4_endpoint dst; make_ep(&dip, 67, IP_VER4, &dst); send_to_socket(st->sock, &dst, (const void*)pkt.ptr, pkt.size); @@ -384,7 +371,7 @@ static void dhcp_send_inform_for(dhcp_if_state_t* st) { if (!v4 || !v4->ip) return; dhcp_request req; memset(&req, 0, sizeof(req)); - if (st->mac_ok) memcpy(req.mac, st->mac, 6); + if (st->mac_ok) mac_copy(req.mac, st->mac); uint32_t ip_net = bswap32(v4->ip); req.offered_ip = ip_net; req.server_ip = 0; @@ -392,7 +379,7 @@ static void dhcp_send_inform_for(dhcp_if_state_t* st) { rng_init_random(&rng); uint32_t xid = rng_next32(&rng); sizedptr pkt = dhcp_build_packet(&req, DHCPINFORM, xid, DHCPK_INFORM, true); - uint32_t dip = 0xFFFFFFFFu; + uint32_t dip = IPV4_LIMITED_BROADCAST; net_l4_endpoint dst; make_ep(&dip, 67, IP_VER4, &dst); send_to_socket(st->sock, &dst, (const void*)pkt.ptr, pkt.size); @@ -419,7 +406,8 @@ static void fsm_once_for(dhcp_if_state_t* st) { st->state = DHCP_S_SELECTING; } break; case DHCP_S_SELECTING: { - dhcp_packet* offer = NULL; sizedptr sp = (sizedptr){0,0}; + 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; st->retry_left_ms = dhcp_next_backoff_ms(st); @@ -427,18 +415,25 @@ static void fsm_once_for(dhcp_if_state_t* st) { } dhcp_request req; memset(&req, 0, sizeof(req)); - if (st->mac_ok) memcpy(req.mac, st->mac, 6); + if (st->mac_ok) mac_copy(req.mac, st->mac); 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_sized((void*)sp.ptr, sp.size); - dhcp_send_request_select_for(st, &req); + sizedptr pkt = dhcp_build_packet(&req, DHCPREQUEST, st->trans_xid, DHCPK_SELECT, true); + uint32_t dip = IPV4_LIMITED_BROADCAST; + net_l4_endpoint dst; + make_ep(&dip, 67, IP_VER4, &dst); + send_to_socket(st->sock, &dst, (const void*)pkt.ptr, pkt.size); + free_sized((void*)pkt.ptr, pkt.size); 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; + 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; st->retry_left_ms = dhcp_next_backoff_ms(st); diff --git a/kernel/networking/application_layer/dhcpv6.c b/kernel/networking/application_layer/dhcpv6.c index 339ad12e..75a1abe6 100644 --- a/kernel/networking/application_layer/dhcpv6.c +++ b/kernel/networking/application_layer/dhcpv6.c @@ -2,6 +2,7 @@ #include "std/memory.h" #include "std/string.h" +#include "networking/link_layer/link_utils.h" static void opt_append(uint8_t*b, uint32_t cap, uint32_t*off, uint16_t code, const void*data, uint16_t len){ if (!b || !off) return; @@ -24,12 +25,12 @@ uint32_t dhcpv6_make_xid24(uint32_t r32){ return x; } -uint32_t dhcpv6_iaid_from_mac(const uint8_t mac[6]){ +uint32_t dhcpv6_iaid_from_mac(const uint8_t mac[MAC_ADDR_LEN]){ if (!mac) return 0; return ((uint32_t)mac[2] << 24) | ((uint32_t)mac[3] << 16) | ((uint32_t)mac[4] << 8) | (uint32_t)mac[5]; } -bool dhcpv6_build_message(uint8_t*out, uint32_t out_cap, uint32_t*out_len, const net_runtime_opts_v6_t*rt, const uint8_t mac[6], uint8_t msg_type, dhcpv6_req_kind kind, uint32_t xid24, bool want_address) { +bool dhcpv6_build_message(uint8_t*out, uint32_t out_cap, uint32_t*out_len, const net_runtime_opts_v6_t*rt, const uint8_t mac[MAC_ADDR_LEN], uint8_t msg_type, dhcpv6_req_kind kind, uint32_t xid24, bool want_address) { if (!out || !out_len) return false; if (out_cap < 4) return false; @@ -50,8 +51,8 @@ bool dhcpv6_build_message(uint8_t*out, uint32_t out_cap, uint32_t*out_len, const memcpy(duid + 0, &duid_type, 2); memcpy(duid + 2, &hw_type, 2); - if (mac) memcpy(duid + 4, mac, 6); - else memset(duid + 4, 0, 6); + if (mac) mac_copy(duid + 4, mac); + else mac_clear(duid + 4); opt_append(out, out_cap, &off, DHCPV6_OPT_CLIENTID, duid, 10); diff --git a/kernel/networking/application_layer/dhcpv6.h b/kernel/networking/application_layer/dhcpv6.h index 9fb690a1..dd093f85 100644 --- a/kernel/networking/application_layer/dhcpv6.h +++ b/kernel/networking/application_layer/dhcpv6.h @@ -2,6 +2,7 @@ #include "types.h" #include "networking/interface_manager.h" +#include "networking/link_layer/link_utils.h" #ifdef __cplusplus extern "C" { @@ -78,10 +79,10 @@ typedef struct { uint32_t dhcpv6_make_xid24(uint32_t r32); -void dhcpv6_duid_ll_from_mac(uint8_t out_duid[10], const uint8_t mac[6]); -uint32_t dhcpv6_iaid_from_mac(const uint8_t mac[6]); +void dhcpv6_duid_ll_from_mac(uint8_t out_duid[10], const uint8_t mac[MAC_ADDR_LEN]); +uint32_t dhcpv6_iaid_from_mac(const uint8_t mac[MAC_ADDR_LEN]); -bool dhcpv6_build_message(uint8_t* out, uint32_t out_cap, uint32_t* out_len, const net_runtime_opts_v6_t* rt, const uint8_t mac[6], uint8_t type, dhcpv6_req_kind kind, uint32_t xid24, bool want_address); +bool dhcpv6_build_message(uint8_t* out, uint32_t out_cap, uint32_t* out_len, const net_runtime_opts_v6_t* rt, const uint8_t mac[MAC_ADDR_LEN], uint8_t type, dhcpv6_req_kind kind, uint32_t xid24, bool want_address); bool dhcpv6_parse_message(const uint8_t *msg, uint32_t msg_len, uint32_t expect_xid24, uint32_t expect_iaid, dhcpv6_parsed_t *out); diff --git a/kernel/networking/application_layer/dhcpv6_daemon.c b/kernel/networking/application_layer/dhcpv6_daemon.c index 6d72efb7..2b359904 100644 --- a/kernel/networking/application_layer/dhcpv6_daemon.c +++ b/kernel/networking/application_layer/dhcpv6_daemon.c @@ -10,6 +10,7 @@ #include "data/struct/linked_list.h" #include "networking/interface_manager.h" +#include "networking/link_layer/link_utils.h" #include "networking/network.h" #include "networking/application_layer/dhcpv6.h" @@ -38,7 +39,7 @@ typedef struct { uint8_t last_gateway[16]; uint8_t last_gateway_ok; - uint8_t mac[6]; + uint8_t mac[MAC_ADDR_LEN]; uint8_t mac_ok; socket_handle_t sock; @@ -78,28 +79,26 @@ void dhcpv6_force_renew_all() { g_force_renew_all = true; } void dhcpv6_force_rebind_all() { g_force_rebind_all = true; } void dhcpv6_force_confirm_all() { g_force_confirm_all = true; } -static int l3id_to_bit(uint8_t l3_id) { - if (!l3_id) return -1; - if ((l3_id & 0x08) == 0) return -1; +static uint64_t dhcpv6_l3_mask(uint8_t l3_id) { + if (!l3_id || !l3_is_v6_from_id(l3_id)) return 0; - uint8_t ifx = (uint8_t)((l3_id >> 4) & 0x0F); - uint8_t sl = (uint8_t)(l3_id & 0x03); - int idx = ((int)(ifx - 1) * MAX_IPV6_PER_INTERFACE) + (int)sl; + uint8_t ifindex = l3_ifindex_from_id(l3_id); + uint8_t slot = l3_slot_from_id(l3_id); + if (!ifindex || slot >= MAX_IPV6_PER_INTERFACE) return 0; - if (idx < 0 || idx >= 64) return -1; - return idx; + uint32_t bit = ((uint32_t)(ifindex - 1) * MAX_IPV6_PER_INTERFACE) + slot; + if (bit >= 64) return 0; + return (uint64_t)1 << bit; } void dhcpv6_force_release_l3(uint8_t l3_id) { - int b = l3id_to_bit(l3_id); - if (b < 0) return; - g_force_release_mask |= (1ull << (uint64_t)b); + uint64_t m = dhcpv6_l3_mask(l3_id); + if (m) g_force_release_mask |= m; } void dhcpv6_force_decline_l3(uint8_t l3_id) { - int b = l3id_to_bit(l3_id); - if (b < 0) return; - g_force_decline_mask |= (1ull << (uint64_t)b); + uint64_t m = dhcpv6_l3_mask(l3_id); + if (m) g_force_decline_mask |= m; } static uint32_t next_backoff_ms(dhcpv6_bind_t* b) { @@ -257,7 +256,10 @@ static void ensure_binds() { b->bound_linklocal_l3_id = ll_l3; const uint8_t* mac = network_get_mac(b->ifindex); - if (mac) { memcpy(b->mac, mac, 6); b->mac_ok = 1; } + if (mac) { + mac_copy(b->mac, mac); + b->mac_ok = 1; + } b->sock = create_socket(PROTO_UDP, NULL); if (!b->sock) { @@ -343,22 +345,18 @@ static void fsm_once(dhcpv6_bind_t* b, uint32_t tick_ms) { if (!v6->runtime_opts_v6.iaid) v6->runtime_opts_v6.iaid = dhcpv6_iaid_from_mac(b->mac); if (!v6->runtime_opts_v6.iaid) v6->runtime_opts_v6.iaid = rng_next32(&g_dhcpv6_rng); - int bit = l3id_to_bit(v6->l3_id); + uint64_t l3_mask = dhcpv6_l3_mask(v6->l3_id); bool do_release = false; bool do_decline = false; - if (bit >= 0) { - uint64_t m = (1ull << (uint64_t)bit); - - if (g_force_release_mask & m) { - g_force_release_mask &= ~m; - do_release = true; - } + if (l3_mask && (g_force_release_mask & l3_mask)) { + g_force_release_mask &= ~l3_mask; + do_release = true; + } - if (g_force_decline_mask & m) { - g_force_decline_mask &= ~m; - do_decline = true; - } + if (l3_mask && (g_force_decline_mask & l3_mask)) { + g_force_decline_mask &= ~l3_mask; + do_decline = true; } if (do_release) { diff --git a/kernel/networking/application_layer/ntp.c b/kernel/networking/application_layer/ntp.c index 5fb58305..77f35cba 100644 --- a/kernel/networking/application_layer/ntp.c +++ b/kernel/networking/application_layer/ntp.c @@ -1,6 +1,7 @@ #include "ntp.h" #include "exceptions/timer.h" #include "std/memory.h" +#include "networking/interface_manager.h" #include "networking/internet_layer/ipv4.h" #include "networking/internet_layer/ipv4_utils.h" #include "process/scheduler.h" diff --git a/kernel/networking/application_layer/sntp.c b/kernel/networking/application_layer/sntp.c index 695fe704..faebbb60 100644 --- a/kernel/networking/application_layer/sntp.c +++ b/kernel/networking/application_layer/sntp.c @@ -1,6 +1,7 @@ #include "sntp.h" //deprecated, use ntp #include "exceptions/timer.h" #include "std/memory.h" +#include "networking/interface_manager.h" #include "networking/internet_layer/ipv4.h" #include "networking/internet_layer/ipv4_utils.h" #include "process/scheduler.h" diff --git a/kernel/networking/interface_manager.c b/kernel/networking/interface_manager.c index 1f3cff73..6850286d 100644 --- a/kernel/networking/interface_manager.c +++ b/kernel/networking/interface_manager.c @@ -2,6 +2,7 @@ #include "std/memory.h" #include "std/string.h" #include "networking/link_layer/arp.h" +#include "networking/link_layer/link_utils.h" #include "networking/link_layer/ndp.h" #include "networking/internet_layer/ipv4_route.h" #include "networking/internet_layer/ipv6_route.h" @@ -182,37 +183,37 @@ bool l2_interface_set_metric(uint8_t ifindex, uint16_t metric) { static bool l2_sync_multicast_filters(l2_interface_t* itf) { if (!itf) return false; - uint8_t macs[(MAX_IPV4_MCAST_PER_INTERFACE + MAX_IPV6_MCAST_PER_INTERFACE) * 6]; + uint8_t macs[(MAX_IPV4_MCAST_PER_INTERFACE + MAX_IPV6_MCAST_PER_INTERFACE) * MAC_ADDR_LEN]; uint32_t count = 0; for (int i = 0; i < (int)itf->ipv4_mcast_count; ++i) { - uint8_t m[6]; + uint8_t m[MAC_ADDR_LEN]; ipv4_mcast_to_mac(itf->ipv4_mcast[i], m); bool exists = false; for (uint32_t j = 0; j < count; ++j) { - if (memcmp(&macs[j * 6], m, 6) == 0) { + if (mac_equal(&macs[j * MAC_ADDR_LEN], m)) { exists = true; break; } } if (!exists) { - memcpy(&macs[count * 6], m, 6); + mac_copy(&macs[count * MAC_ADDR_LEN], m); count++; } } for (int i = 0; i < (int)itf->ipv6_mcast_count; ++i) { - uint8_t m[6]; + uint8_t m[MAC_ADDR_LEN]; ipv6_multicast_mac(itf->ipv6_mcast[i], m); bool exists = false; for (uint32_t j = 0; j < count; ++j) { - if (memcmp(&macs[j*6], m, 6) == 0) { + if (mac_equal(&macs[j * MAC_ADDR_LEN], m)) { exists = true; break; } } if (!exists) { - memcpy(&macs[count * 6], m, 6); + mac_copy(&macs[count * MAC_ADDR_LEN], m); count++; } } diff --git a/kernel/networking/internet_layer/icmp.c b/kernel/networking/internet_layer/icmp.c index 8cf102b8..78eb9b62 100644 --- a/kernel/networking/internet_layer/icmp.c +++ b/kernel/networking/internet_layer/icmp.c @@ -1,158 +1,10 @@ #include "networking/internet_layer/icmp.h" #include "net/checksums.h" #include "std/std.h" -#include "console/kio.h" #include "networking/interface_manager.h" #include "networking/internet_layer/ipv4.h" -#include "networking/transport_layer/csocket.h" #include "networking/transport_layer/csocket_raw.h" #include "networking/transport_layer/trans_utils.h" -#include "syscalls/syscalls.h" - -static uintptr_t build_echo(uint16_t id, uint16_t seq, const uint8_t* payload, uint32_t pay_len, uint32_t* out_total_len) { - uint32_t len = 8 + (pay_len > 56 ? 56 : pay_len); - *out_total_len = len; - uintptr_t buf = (uintptr_t)zalloc(8 + 56); - if (!buf) return 0; - - icmp_packet *pkt = (icmp_packet*)buf; - pkt->type = ICMP_ECHO_REQUEST; - pkt->code = 0; - pkt->id = bswap16(id); - pkt->seq = bswap16(seq); - - memset(pkt->payload, 0, 56); - if (payload && pay_len) memcpy(pkt->payload, payload, (pay_len > 56 ? 56 : pay_len)); - pkt->checksum = 0; - pkt->checksum = bswap16(checksum16(pkt, len)); - return buf; -} - -uint32_t icmp_ping_collect(uint32_t dst_ip, uint16_t id, uint16_t seq, uint32_t timeout_ms, const void* tx_opts_or_null, uint32_t ttl, ping_result_t* out, uint32_t max_results) { - if (!out || !max_results) return 0; - - uint32_t tot_len = 0; - uintptr_t buf = build_echo(id, seq, NULL, 32, &tot_len); - if (!buf) return 0; - - SocketOptions opt; - memset(&opt, 0, sizeof(opt)); - opt.special_kind = SOCKET_SPECIAL_RAW; - if (ttl) { - opt.flags |= SOCK_OPT_TTL; - opt.ttl = (uint8_t)ttl; - } - socket_handle_t sock = create_socket(PROTO_ICMP, &opt); - if (!sock) { - release((void*)buf); - return 0; - } - - const ip_tx_opts_t* tx = (const ip_tx_opts_t*)tx_opts_or_null; - if (tx && tx->scope != IP_TX_AUTO) { - SockBindSpec bind; - memset(&bind, 0, sizeof(bind)); - if (tx->scope == IP_TX_BOUND_L2) { - bind.kind = BIND_L2; - bind.ifindex = tx->index; - } else if (tx->scope == IP_TX_BOUND_L3) { - bind.kind = BIND_L3; - bind.ver = IP_VER4; - bind.l3_id = tx->index; - } - if (bind.kind && bind_socket(sock, &bind, 0) != SOCK_OK) { - close_socket(sock); - release((void*)buf); - return 0; - } - } - - net_l4_endpoint dst; - make_ep(&dst_ip, 0, IP_VER4, &dst); - if (send_to_socket(sock, &dst, (const void*)buf, tot_len) != (int64_t)tot_len) { - close_socket(sock); - release((void*)buf); - return 0; - } - release((void*)buf); - - uint32_t count = 0; - uint32_t start = (uint32_t)get_time(); - while (count < max_results) { - uint32_t now = (uint32_t)get_time(); - if (now - start >= timeout_ms) break; - - uint8_t rx[576]; - net_l4_endpoint src; - memset(&src, 0, sizeof(src)); - int64_t n = receive_from_socket(sock, rx, sizeof(rx), &src); - if (n == SOCK_ERR_WOULDBLOCK) { - msleep(5); - continue; - } - - if (n < 8) { - if (n < 0) msleep(5); - continue; - } - - uint8_t type = rx[0]; - uint8_t code = rx[1]; - bool matched = false; - if (type == ICMP_ECHO_REPLY && rd_be16(rx + 4) == id && rd_be16(rx + 6) == seq) matched = true; - else if ((type == ICMP_TIME_EXCEEDED || type == ICMP_DEST_UNREACH || type == ICMP_PARAM_PROBLEM || type == ICMP_REDIRECT) && n >= 36) { - const uint8_t* ip = rx + 8; - uint8_t ihl = (uint8_t)(ip[0] & 0x0F); - uint32_t iphdr = (uint32_t)ihl * 4; - if (ihl >= IP_IHL_NOOPTS && (uint32_t)n >= 8 + iphdr + 8 && ip[9] == PROTO_ICMP) { - const uint8_t* ic = rx + 8 + iphdr; - uint8_t inner_type = ic[0]; - if ((inner_type == ICMP_ECHO_REQUEST || inner_type == ICMP_ECHO_REPLY) && rd_be16(ic + 4) == id && rd_be16(ic + 6) == seq) matched = true; - } - } - if (!matched) continue; - - ping_result_t* r = &out[count++]; - memset(r, 0, sizeof(*r)); - r->icmp_type = type; - r->icmp_code = code; - memcpy(&r->responder_ip, src.ip, sizeof(r->responder_ip)); - now = (uint32_t)get_time(); - r->rtt_ms = now >= start ? now - start : 0; - switch (type) { - case ICMP_ECHO_REPLY: - r->status = PING_OK; - break; - case ICMP_DEST_UNREACH: - switch (code) { - case 0: r->status = PING_NET_UNREACH; break; - case 1: r->status = PING_HOST_UNREACH; break; - case 2: r->status = PING_PROTO_UNREACH; break; - case 3: r->status = PING_PORT_UNREACH; break; - case 4: r->status = PING_FRAG_NEEDED; break; - case 5: r->status = PING_SRC_ROUTE_FAILED; break; - case 13: r->status = PING_ADMIN_PROHIBITED; break; - default: r->status = PING_UNKNOWN_ERROR; break; - } - break; - case ICMP_TIME_EXCEEDED: r->status = PING_TTL_EXPIRED; break; - case ICMP_PARAM_PROBLEM: r->status = PING_PARAM_PROBLEM; break; - case ICMP_REDIRECT: r->status = PING_REDIRECT; break; - default: r->status = PING_UNKNOWN_ERROR; break; - } - if (max_results == 1) break; - } - - close_socket(sock); - return count; -} - -bool icmp_ping(uint32_t dst_ip, uint16_t id, uint16_t seq, uint32_t timeout_ms, const void* tx_opts_or_null, uint32_t ttl, ping_result_t* out) { - ping_result_t res; - ping_result_t *dst = out ? out : &res; - uint32_t n = icmp_ping_collect(dst_ip, id, seq, timeout_ms, tx_opts_or_null, ttl, dst, 1); - return n && dst->status == PING_OK; -} void icmp_input(uint8_t ifindex, netpkt_t* pkt, uint32_t src_ip, uint32_t dst_ip) { if (!pkt) return; diff --git a/kernel/networking/internet_layer/icmp.h b/kernel/networking/internet_layer/icmp.h index 0c174e76..0ec36e02 100644 --- a/kernel/networking/internet_layer/icmp.h +++ b/kernel/networking/internet_layer/icmp.h @@ -16,22 +16,6 @@ typedef enum { ICMP_PARAM_PROBLEM = 12 } icmp_type_t; -typedef enum { - PING_OK = 0, - PING_TIMEOUT = 1, - PING_NET_UNREACH = 2, - PING_HOST_UNREACH = 3, - PING_PROTO_UNREACH = 4, - PING_PORT_UNREACH = 5, - PING_FRAG_NEEDED = 6, - PING_SRC_ROUTE_FAILED = 7, - PING_ADMIN_PROHIBITED = 8, - PING_TTL_EXPIRED = 9, - PING_PARAM_PROBLEM = 10, - PING_REDIRECT = 11, - PING_UNKNOWN_ERROR = 255 -} ping_status_t; - typedef struct __attribute__((packed)) { uint8_t type; uint8_t code; @@ -41,17 +25,6 @@ typedef struct __attribute__((packed)) { uint8_t payload[56]; } icmp_packet; -typedef struct { - uint32_t rtt_ms; - uint8_t status; - uint8_t icmp_type; - uint8_t icmp_code; - uint8_t _pad; - uint32_t responder_ip; -} ping_result_t; - -bool icmp_ping(uint32_t dst_ip, uint16_t id, uint16_t seq, uint32_t timeout_ms, const void *tx_opts_or_null, uint32_t ttl, ping_result_t *out); -uint32_t icmp_ping_collect(uint32_t dst_ip, uint16_t id, uint16_t seq, uint32_t timeout_ms, const void *tx_opts_or_null, uint32_t ttl, ping_result_t *out, uint32_t max_results); void icmp_input(uint8_t ifindex, netpkt_t* pkt, uint32_t src_ip, uint32_t dst_ip); #ifdef __cplusplus diff --git a/kernel/networking/internet_layer/icmpv6.c b/kernel/networking/internet_layer/icmpv6.c index 6ef8f48f..0ea021a2 100644 --- a/kernel/networking/internet_layer/icmpv6.c +++ b/kernel/networking/internet_layer/icmpv6.c @@ -7,10 +7,8 @@ #include "networking/link_layer/eth.h" #include "networking/link_layer/ndp.h" #include "networking/internet_layer/mld.h" -#include "networking/transport_layer/csocket.h" #include "networking/transport_layer/csocket_raw.h" #include "networking/transport_layer/trans_utils.h" -#include "syscalls/syscalls.h" typedef struct __attribute__((packed)) { icmpv6_hdr_t hdr; @@ -18,8 +16,6 @@ typedef struct __attribute__((packed)) { uint16_t seq; } icmpv6_echo_t; -static bool extract_echo_id_seq_from_error(const uint8_t *icmp, uint32_t icmp_len, uint16_t *out_id, uint16_t *out_seq); - bool icmpv6_send_on_l2(uint8_t ifindex, const uint8_t dst_ip[16], const uint8_t src_ip[16], const uint8_t dst_mac[6], const void *icmp, uint32_t icmp_len, uint8_t hop_limit) { if (!ifindex || !dst_ip || !src_ip || !dst_mac || !icmp || !icmp_len) return false; @@ -73,151 +69,6 @@ static bool icmpv6_send_echo_reply(uint16_t ifindex, const uint8_t src_ip[16], c return true; } -uint32_t icmpv6_ping_collect(const uint8_t dst_ip[16], uint16_t id, uint16_t seq, uint32_t timeout_ms, const void *tx_opts_or_null, uint8_t hop_limit, ping6_result_t *out, uint32_t max_results) { - if (!out || !max_results) return 0; - - SocketOptions opt; - memset(&opt, 0, sizeof(opt)); - opt.special_kind = SOCKET_SPECIAL_RAW; - if (hop_limit) { - opt.flags |= SOCK_OPT_TTL; - opt.ttl = hop_limit; - } - socket_handle_t sock = create_socket(PROTO_ICMPV6, &opt); - if (!sock) { - return 0; - } - - const ip_tx_opts_t* tx = (const ip_tx_opts_t*)tx_opts_or_null; - if (tx && tx->scope != IP_TX_AUTO) { - SockBindSpec bind; - memset(&bind, 0, sizeof(bind)); - if (tx->scope == IP_TX_BOUND_L2) { - bind.kind = BIND_L2; - bind.ifindex = tx->index; - } else if (tx->scope == IP_TX_BOUND_L3) { - bind.kind = BIND_L3; - bind.ver = IP_VER6; - bind.l3_id = tx->index; - } - if (bind.kind && bind_socket(sock, &bind, 0) != SOCK_OK) { - close_socket(sock); - return 0; - } - } - - uint8_t payload[sizeof(icmpv6_echo_t) + 32]; - memset(payload, 0, sizeof(payload)); - icmpv6_echo_t* e = (icmpv6_echo_t*)payload; - e->hdr.type = ICMPV6_ECHO_REQUEST; - e->hdr.code = 0; - e->id = bswap16(id); - e->seq = bswap16(seq); - - net_l4_endpoint dst; - memset(&dst, 0, sizeof(dst)); - dst.ver = IP_VER6; - ipv6_cpy(dst.ip, dst_ip); - if (send_to_socket(sock, &dst, payload, sizeof(payload)) != (int64_t)sizeof(payload)) { - close_socket(sock); - return 0; - } - - uint32_t count = 0; - uint32_t start = (uint32_t)get_time(); - while (count < max_results) { - uint32_t now = (uint32_t)get_time(); - if (now - start >= timeout_ms) break; - - uint8_t rx[1280]; - net_l4_endpoint src; - memset(&src, 0, sizeof(src)); - int64_t n = receive_from_socket(sock, rx, sizeof(rx), &src); - if (n == SOCK_ERR_WOULDBLOCK) { - msleep(5); - continue; - } - - if (n < (int64_t)sizeof(icmpv6_hdr_t)) { - if (n < 0) msleep(5); - continue; - } - - uint8_t type = rx[0]; - uint8_t code = rx[1]; - bool matched = false; - if (type == ICMPV6_ECHO_REPLY && n >= (int64_t)sizeof(icmpv6_echo_t)) { - icmpv6_echo_t reply; - memcpy(&reply, rx, sizeof(reply)); - if (bswap16(reply.id) == id &&bswap16(reply.seq) == seq) matched = true; - } else if (type == ICMPV6_DEST_UNREACH || type == ICMPV6_PACKET_TOO_BIG || type == ICMPV6_TIME_EXCEEDED || type == ICMPV6_PARAM_PROBLEM) { - uint16_t inner_id = 0; - uint16_t inner_seq = 0; - if (extract_echo_id_seq_from_error(rx, (uint32_t)n, &inner_id, &inner_seq) && inner_id == id && inner_seq == seq) matched = true; - } - if (!matched) continue; - - ping6_result_t* r = &out[count++]; - memset(r, 0, sizeof(*r)); - r->icmp_type = type; - r->icmp_code = code; - ipv6_cpy(r->responder_ip, src.ip); - now = (uint32_t)get_time(); - r->rtt_ms = now >= start ? now - start : 0; - switch (type) { - case ICMPV6_ECHO_REPLY: - r->status = PING_OK; - break; - case ICMPV6_DEST_UNREACH: - switch (code) { - case 0: r->status = PING_NET_UNREACH; break; - case 1: r->status = PING_ADMIN_PROHIBITED; break; - case 2: r->status = PING_ADMIN_PROHIBITED; break; - case 3: r->status = PING_HOST_UNREACH; break; - case 4: r->status = PING_PORT_UNREACH; break; - default: r->status = PING_UNKNOWN_ERROR; break; - } - break; - case ICMPV6_PACKET_TOO_BIG: r->status = PING_FRAG_NEEDED; break; - case ICMPV6_TIME_EXCEEDED: r->status = PING_TTL_EXPIRED; break; - case ICMPV6_PARAM_PROBLEM: r->status = PING_PARAM_PROBLEM; break; - default: r->status = PING_UNKNOWN_ERROR; break; - } - if (max_results == 1) break; - } - - close_socket(sock); - return count; -} - -bool icmpv6_ping(const uint8_t dst_ip[16], uint16_t id, uint16_t seq, uint32_t timeout_ms, const void *tx_opts_or_null, uint8_t hop_limit, ping6_result_t *out) { - ping6_result_t res; - ping6_result_t *dst = out ? out : &res; - uint32_t n = icmpv6_ping_collect(dst_ip, id, seq, timeout_ms, tx_opts_or_null, hop_limit, dst, 1); - return n && dst->status == PING_OK; -} - -static bool extract_echo_id_seq_from_error(const uint8_t *icmp, uint32_t icmp_len, uint16_t *out_id, uint16_t *out_seq) { - if (!icmp || icmp_len < 8u + (uint32_t)sizeof(ipv6_hdr_t) + (uint32_t)sizeof(icmpv6_echo_t)) return false; - - ipv6_hdr_t inner; - memcpy(&inner, icmp + 8, sizeof(inner)); - uint32_t v = bswap32(inner.ver_tc_fl); - if ((v >>28) != 6) return false; - if (inner.next_header != PROTO_ICMPV6) return false; - - const uint8_t *inner_icmp = icmp + 8u + (uint32_t)sizeof(ipv6_hdr_t); - if ((uintptr_t)inner_icmp + sizeof(icmpv6_echo_t)>(uintptr_t)icmp + icmp_len) return false; - - icmpv6_echo_t e; - memcpy(&e, inner_icmp, sizeof(e)); - if (e.hdr.type != ICMPV6_ECHO_REQUEST) return false; - - if (out_id) *out_id = bswap16(e.id); - if (out_seq) *out_seq = bswap16(e.seq); - return true; -} - void icmpv6_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[16], uint8_t hop_limit, const uint8_t src_mac[6], netpkt_t* pkt) { if (!ifindex || !src_ip || !dst_ip || !pkt || netpkt_len(pkt) < sizeof(icmpv6_hdr_t)) { if (pkt) netpkt_unref(pkt); @@ -263,12 +114,6 @@ void icmpv6_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ } if (h->type == ICMPV6_ECHO_REPLY) { - if (icmp_len < sizeof(icmpv6_echo_t)) { - netpkt_unref(pkt); - return; - } - icmpv6_echo_t e; - memcpy(&e, icmp, sizeof(e)); netpkt_unref(pkt); return; } diff --git a/kernel/networking/internet_layer/icmpv6.h b/kernel/networking/internet_layer/icmpv6.h index 099f8f34..2caaac73 100644 --- a/kernel/networking/internet_layer/icmpv6.h +++ b/kernel/networking/internet_layer/icmpv6.h @@ -2,19 +2,20 @@ #include "types.h" #include "net/network_types.h" -#include "networking/internet_layer/icmp.h" #include "networking/netpkt.h" #ifdef __cplusplus extern "C" { #endif -#define ICMPV6_DEST_UNREACH 1 -#define ICMPV6_PACKET_TOO_BIG 2 -#define ICMPV6_TIME_EXCEEDED 3 -#define ICMPV6_PARAM_PROBLEM 4 -#define ICMPV6_ECHO_REQUEST 128 -#define ICMPV6_ECHO_REPLY 129 +typedef enum { + ICMPV6_DEST_UNREACH = 1, + ICMPV6_PACKET_TOO_BIG = 2, + ICMPV6_TIME_EXCEEDED = 3, + ICMPV6_PARAM_PROBLEM = 4, + ICMPV6_ECHO_REQUEST = 128, + ICMPV6_ECHO_REPLY = 129 +} icmpv6_type_t; typedef struct __attribute__((packed)) { uint8_t type; @@ -22,18 +23,7 @@ typedef struct __attribute__((packed)) { uint16_t checksum; } icmpv6_hdr_t; -typedef struct { - uint32_t rtt_ms; - uint8_t status; - uint8_t icmp_type; - uint8_t icmp_code; - uint8_t _pad; - uint8_t responder_ip[16]; -} ping6_result_t; - void icmpv6_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[16], uint8_t hop_limit, const uint8_t src_mac[6], netpkt_t* pkt); -bool icmpv6_ping(const uint8_t dst_ip[16], uint16_t id, uint16_t seq, uint32_t timeout_ms, const void *tx_opts_or_null, uint8_t hop_limit, ping6_result_t *out); -uint32_t icmpv6_ping_collect(const uint8_t dst_ip[16], uint16_t id, uint16_t seq, uint32_t timeout_ms, const void *tx_opts_or_null, uint8_t hop_limit, ping6_result_t *out, uint32_t max_results); bool icmpv6_send_on_l2(uint8_t ifindex, const uint8_t dst_ip[16], const uint8_t src_ip[16], const uint8_t dst_mac[6], const void *icmp, uint32_t icmp_len, uint8_t hop_limit); #ifdef __cplusplus diff --git a/kernel/networking/internet_layer/ipv4.c b/kernel/networking/internet_layer/ipv4.c index 3811d9e6..0643576c 100644 --- a/kernel/networking/internet_layer/ipv4.c +++ b/kernel/networking/internet_layer/ipv4.c @@ -1,6 +1,7 @@ #include "ipv4.h" #include "ipv4_route.h" #include "networking/link_layer/arp.h" +#include "networking/link_layer/link_utils.h" #include "networking/internet_layer/icmp.h" #include "networking/internet_layer/igmp.h" #include "std/memory.h" @@ -13,6 +14,7 @@ #include "net/network_types.h" #include "networking/link_layer/nic_types.h" #include "networking/net_fragbuf.h" +#include "networking/interface_manager.h" static uint16_t g_ip_ident = 1; @@ -61,7 +63,7 @@ bool ipv4_send_packet(uint32_t dst_ip, uint8_t proto, netpkt_t* pkt, const ip_tx ifx = src_v4->l2->ifindex; src_ip = src_v4->ip; - nh = 0xFFFFFFFF; + nh = IPV4_LIMITED_BROADCAST; } else { ipv4_tx_plan_t plan; if (!ipv4_build_tx_plan(dst_ip, opts, &plan)) { @@ -100,13 +102,13 @@ bool ipv4_send_packet(uint32_t dst_ip, uint8_t proto, netpkt_t* pkt, const ip_tx } l2_interface_t* l2 = src_v4->l2; - uint8_t dst_mac[6]; + uint8_t dst_mac[MAC_ADDR_LEN]; bool need_arp = false; bool is_dbcast = src_v4->mask && nh == dst_ip && ipv4_broadcast_calc(src_v4->ip, src_v4->mask) == dst_ip; - if (ipv4_is_limited_broadcast(dst_ip) || is_dbcast) memset(dst_mac, 0xFF, 6); + if (ipv4_is_limited_broadcast(dst_ip) || is_dbcast) mac_set_broadcast(dst_mac); else if (ipv4_is_multicast(dst_ip)) ipv4_mcast_to_mac(dst_ip, dst_mac); - else if (l2->kind == NET_IFK_LOCALHOST) memset(dst_mac, 0, 6); + else if (l2->kind == NET_IFK_LOCALHOST) mac_clear(dst_mac); else need_arp = true; uint16_t mtu = src_v4->runtime_opts_v4.mtu ? src_v4->runtime_opts_v4.mtu : 1500; @@ -240,7 +242,7 @@ static void ipv4_deliver_l4(uint16_t ifindex, netpkt_t* pkt, uint32_t l4_off, ui return; } - if (dst == 0xFFFFFFFF) { + if (dst == IPV4_LIMITED_BROADCAST) { for (int i = 0; i < ccount; ++i) { netpkt_t* l4pkt = netpkt_view(pkt, l4_off, l4_len); if (!l4pkt) continue; @@ -306,7 +308,7 @@ static void ipv4_deliver_l4(uint16_t ifindex, netpkt_t* pkt, uint32_t l4_off, ui } } -void ipv4_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { +void ipv4_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[MAC_ADDR_LEN]) { if (!pkt) return; uint32_t ip_len = netpkt_len(pkt); if (ip_len < sizeof(ipv4_hdr_t)) return; @@ -336,9 +338,9 @@ void ipv4_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { uint32_t dst = bswap32(ip.dst_ip); if (ifindex && src && src_mac) { - uint8_t mac_old[6]; + uint8_t mac_old[MAC_ADDR_LEN]; 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); + if (!had || !mac_equal(mac_old, src_mac)) arp_table_put_for_l2((uint8_t)ifindex, src, src_mac, 180000, false); else arp_table_put_for_l2((uint8_t)ifindex, src, mac_old, 180000, false); } diff --git a/kernel/networking/internet_layer/ipv4.h b/kernel/networking/internet_layer/ipv4.h index 2a7fc7a4..f494859c 100644 --- a/kernel/networking/internet_layer/ipv4.h +++ b/kernel/networking/internet_layer/ipv4.h @@ -4,7 +4,6 @@ #include "networking/link_layer/eth.h" #include "net/network_types.h" #include "net/checksums.h" -#include "networking/interface_manager.h" #include "networking/netpkt.h" #define IP_IHL_NOOPTS 5 @@ -29,7 +28,7 @@ typedef struct __attribute__((packed)) ipv4_hdr_t { bool ipv4_send_packet(uint32_t dst_ip, uint8_t proto, netpkt_t* pkt, const ip_tx_opts_t* opts, uint8_t ttl, uint8_t dontfrag); -void ipv4_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]); +void ipv4_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[MAC_ADDR_LEN]); #ifdef __cplusplus } diff --git a/kernel/networking/internet_layer/ipv4_utils.c b/kernel/networking/internet_layer/ipv4_utils.c index 68ec3b1e..ad3b4513 100644 --- a/kernel/networking/internet_layer/ipv4_utils.c +++ b/kernel/networking/internet_layer/ipv4_utils.c @@ -60,7 +60,7 @@ bool ipv4_l3_is_ready(l3_ipv4_interface_t *v4) { bool ipv4_mask_is_contiguous(uint32_t mask) { if (mask == 0) return true; - return ((mask | (mask - 1u)) == 0xFFFFFFFFu); + return ((mask | (mask - 1u)) == IPV4_LIMITED_BROADCAST); } int ipv4_prefix_len(uint32_t mask) { @@ -74,21 +74,21 @@ uint32_t ipv4_broadcast_calc(uint32_t ip, uint32_t mask) { return (mask == 0) ? bool ipv4_is_network_address(uint32_t ip, uint32_t mask) { if (!ipv4_mask_is_contiguous(mask)) return false; - if (mask == 0 || mask == 0xFFFFFFFFu) return false; + if (mask == 0 || mask == IPV4_LIMITED_BROADCAST) return false; return (ip & ~mask) == 0; } bool ipv4_is_broadcast_address(uint32_t ip, uint32_t mask) { if (!ipv4_mask_is_contiguous(mask)) return false; - if (mask == 0 || mask == 0xFFFFFFFFu) return false; + if (mask == 0 || mask == IPV4_LIMITED_BROADCAST) return false; return (ip & ~mask) == ~mask; } -bool ipv4_is_limited_broadcast(uint32_t ip) { return ip == 0xFFFFFFFFu; } +bool ipv4_is_limited_broadcast(uint32_t ip) { return ip == IPV4_LIMITED_BROADCAST; } bool ipv4_is_directed_broadcast(uint32_t ip, uint32_t mask, uint32_t dst) { if (!ipv4_mask_is_contiguous(mask)) return false; - if (mask == 0 || mask == 0xFFFFFFFFu) return false; + if (mask == 0 || mask == IPV4_LIMITED_BROADCAST) return false; return ipv4_broadcast_calc(ip, mask) == dst; } @@ -104,32 +104,26 @@ void ipv4_to_string(uint32_t ip, char* buf) { bool ipv4_parse(const char* s, uint32_t* out) { if (!s || !out) return false; - uint32_t ip = 0, v = 0; - int oct = 0, digits = 0; + uint32_t ip = 0; const char* p = s; - while (*p) { - if (*p == '.') { - if (digits == 0 || v > 255 || oct >= 3) return false; - ip = (ip << 8) | (v & 0xFF); - v = 0; - digits = 0; - oct++; - } else if (*p >= '0' && *p <= '9') { - v = v * 10 + (uint32_t)(*p - '0'); - if (v > 255) return false; - digits++; + for (uint32_t oct = 0; oct < 4; oct++) { + if (!is_digit(*p)) return false; + char* end = 0; + uint64_t v = strtoul(p, &end, 10); + if (v > 255) return false; + ip = (ip << 8) | (uint32_t)v; + if (oct == 3) { + if (*end) return false; } else { - return false; + if (*end != '.') return false; + p = end + 1; } - ++p; } - if (oct != 3 || digits == 0 || v > 255) return false; - ip = (ip << 8) | (v & 0xFF); *out = ip; return true; } -void ipv4_mcast_to_mac(uint32_t group, uint8_t out_mac[6]) { +void ipv4_mcast_to_mac(uint32_t group, uint8_t out_mac[MAC_ADDR_LEN]) { if (!out_mac) return; out_mac[0] = 0x01; out_mac[1] = 0x00; diff --git a/kernel/networking/internet_layer/ipv4_utils.h b/kernel/networking/internet_layer/ipv4_utils.h index 84500f88..4718f347 100644 --- a/kernel/networking/internet_layer/ipv4_utils.h +++ b/kernel/networking/internet_layer/ipv4_utils.h @@ -1,8 +1,11 @@ #pragma once #include "types.h" +#include "networking/link_layer/link_utils.h" #include "networking/interface_manager.h" + #define IPV4_MCAST_ALL_HOSTS 0xE0000001u #define IPV4_MCAST_ALL_ROUTERS 0xE0000002u +#define IPV4_LIMITED_BROADCAST UINT32_MAX #ifdef __cplusplus extern "C" { @@ -38,7 +41,7 @@ bool ipv4_same_subnet(uint32_t a, uint32_t b, uint32_t mask); void ipv4_to_string(uint32_t ip, char* buf); bool ipv4_parse(const char* s, uint32_t* out); -void ipv4_mcast_to_mac(uint32_t group, uint8_t out_mac[6]); +void ipv4_mcast_to_mac(uint32_t group, uint8_t out_mac[MAC_ADDR_LEN]); #ifdef __cplusplus } diff --git a/kernel/networking/internet_layer/ipv6.c b/kernel/networking/internet_layer/ipv6.c index 1f05d3ca..1eaa17c7 100644 --- a/kernel/networking/internet_layer/ipv6.c +++ b/kernel/networking/internet_layer/ipv6.c @@ -3,6 +3,7 @@ #include "std/memory.h" #include "std/string.h" #include "networking/link_layer/eth.h" +#include "networking/link_layer/link_utils.h" #include "networking/interface_manager.h" #include "networking/link_layer/ndp.h" #include "networking/transport_layer/tcp.h" @@ -201,7 +202,7 @@ bool ipv6_send_packet(const uint8_t dst[16], uint8_t next_header, netpkt_t* pkt, uint8_t dst_mac[6]; bool need_ndp = false; if (ipv6_is_multicast(dst)) ipv6_multicast_mac(dst, dst_mac); - else if (l2 && l2->kind == NET_IFK_LOCALHOST) memset(dst_mac, 0, 6); + else if (l2 && l2->kind == NET_IFK_LOCALHOST) mac_clear(dst_mac); else need_ndp = true; uint16_t mtu = src_v6->mtu ? src_v6->mtu : 1500; @@ -224,8 +225,8 @@ bool ipv6_send_packet(const uint8_t dst[16], uint8_t next_header, netpkt_t* pkt, ip6.payload_len = bswap16((uint16_t)seg_len); ip6.next_header = next_header; ip6.hop_limit = hop_limit ? hop_limit : 64; - memcpy(ip6.src, src, 16); - memcpy(ip6.dst, dst, 16); + ipv6_cpy(ip6.src, src); + ipv6_cpy(ip6.dst, dst); memcpy(hdrp, &ip6, sizeof(ip6)); if (need_ndp) return ndp_send_or_queue_on(ifx, nh, pkt); @@ -485,7 +486,7 @@ void ipv6_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { t->last_update_ms = now; t->have_first = 0; net_fragbuf_init(&t->frag); - memset(t->first_src_mac, 0, 6); + mac_clear(t->first_src_mac); t->first_pkt_len = 0; memset(t->first_pkt, 0, sizeof(t->first_pkt)); s = t; @@ -553,7 +554,7 @@ void ipv6_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { return; } s->first_pkt_len = (uint16_t)inv_len; - memcpy(s->first_src_mac, src_mac, 6); + mac_copy(s->first_src_mac, src_mac); s->have_first = 1; } diff --git a/kernel/networking/internet_layer/ipv6_utils.c b/kernel/networking/internet_layer/ipv6_utils.c index fdca16a5..cd0ef144 100644 --- a/kernel/networking/internet_layer/ipv6_utils.c +++ b/kernel/networking/internet_layer/ipv6_utils.c @@ -1,6 +1,7 @@ #include "ipv6_utils.h" #include "std/std.h" #include "networking/network.h" +#include "networking/interface_manager.h" bool ipv6_is_unspecified(const uint8_t ip[16]) { for (int i = 0; i < 16; i++) if (ip[i] != 0) return false; diff --git a/kernel/networking/internet_layer/ipv6_utils.h b/kernel/networking/internet_layer/ipv6_utils.h index f23b51c5..4140e041 100644 --- a/kernel/networking/internet_layer/ipv6_utils.h +++ b/kernel/networking/internet_layer/ipv6_utils.h @@ -1,5 +1,6 @@ #pragma once #include "types.h" +#include "net/interface_types.h" #include "networking/interface_manager.h" #ifdef __cplusplus diff --git a/kernel/networking/link_layer/arp.c b/kernel/networking/link_layer/arp.c index 6f73a4b2..9f92ba60 100644 --- a/kernel/networking/link_layer/arp.c +++ b/kernel/networking/link_layer/arp.c @@ -1,8 +1,9 @@ #include "arp.h" #include "eth.h" +#include "link_utils.h" #include "std/memory.h" -#include "std/string.h" #include "networking/network.h" +#include "networking/interface_manager.h" #include "process/scheduler.h" #include "networking/internet_layer/ipv4.h" #include "syscalls/syscalls.h" @@ -55,13 +56,8 @@ void arp_table_destroy(arp_table_t* t){ 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].ip = IPV4_LIMITED_BROADCAST; + mac_set_broadcast(t->entries[0].mac); t->entries[0].ttl_ms = 0; t->entries[0].state = ARP_STATE_REACHABLE; t->entries[0].static_entry = 1; @@ -84,7 +80,7 @@ static int arp_find_free(arp_table_t* t){ static int arp_find_replacement(arp_table_t* t) { int best = -1; - uint32_t best_ttl = 0xFFFFFFFFu; + uint32_t best_ttl = UINT32_MAX; if (!t) return -1; for (int i = 0; i < (int)N_ARR(t->entries); i++) { @@ -101,7 +97,7 @@ static int arp_find_replacement(arp_table_t* t) { return best; } -void arp_table_put_for_l2(uint8_t ifindex, uint32_t ip, const uint8_t mac[6], uint32_t ttl_ms, bool is_static){ +void arp_table_put_for_l2(uint8_t ifindex, uint32_t ip, const uint8_t mac[MAC_ADDR_LEN], uint32_t ttl_ms, bool is_static){ arp_table_t* t = l2_arp(ifindex); if (!t || !ip || !mac) return; int idx = arp_find_slot(t, ip); @@ -113,7 +109,7 @@ void arp_table_put_for_l2(uint8_t ifindex, uint32_t ip, const uint8_t mac[6], ui if (e->state != ARP_STATE_UNUSED && e->ip != ip) arp_entry_clear(e); e->ip = ip; - memcpy(e->mac, mac, 6); + mac_copy(e->mac, mac); e->ttl_ms = is_static ? 0 : (ttl_ms ? ttl_ms : ARP_REACHABLE_MS); e->timer_ms = 0; e->state = ARP_STATE_REACHABLE; @@ -133,7 +129,7 @@ void arp_table_put_for_l2(uint8_t ifindex, uint32_t ip, const uint8_t mac[6], ui } } -bool arp_table_get_for_l2(uint8_t ifindex, uint32_t ip, uint8_t mac_out[6]){ +bool arp_table_get_for_l2(uint8_t ifindex, uint32_t ip, uint8_t mac_out[MAC_ADDR_LEN]){ arp_table_t* t = l2_arp(ifindex); if (!t || !mac_out) return false; @@ -141,7 +137,7 @@ bool arp_table_get_for_l2(uint8_t ifindex, uint32_t ip, uint8_t mac_out[6]){ arp_entry_t* e = &t->entries[i]; if (e->state == ARP_STATE_UNUSED || e->state == ARP_STATE_INCOMPLETE) continue; if (e->ip != ip) continue; - memcpy(mac_out, e->mac, 6); + mac_copy(mac_out, e->mac); return true; } return false; @@ -243,9 +239,9 @@ bool arp_send_or_queue_on(uint8_t ifindex, uint32_t ip, netpkt_t* pkt) { return false; } - uint8_t mac[6]; - if (ip == 0xFFFFFFFFu){ - memset(mac, 0xFF, 6); + uint8_t mac[MAC_ADDR_LEN]; + if (ip == IPV4_LIMITED_BROADCAST) { + mac_set_broadcast(mac); return eth_send_frame_on(ifindex, ETHERTYPE_IPV4, mac, pkt); } if (arp_table_get_for_l2(ifindex, ip, mac)) return eth_send_frame_on(ifindex, ETHERTYPE_IPV4, mac, pkt); @@ -301,15 +297,16 @@ bool arp_send_or_queue_on(uint8_t ifindex, uint32_t ip, netpkt_t* pkt) { bool arp_send_request_on(uint8_t ifindex, uint32_t sender_ip, uint32_t target_ip){ const uint8_t* local_mac = network_get_mac(ifindex); if (!local_mac || !target_ip) return false; - uint8_t dst_mac[6] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}; + uint8_t dst_mac[MAC_ADDR_LEN]; + mac_set_broadcast(dst_mac); arp_hdr_t hdr; memset(&hdr, 0, sizeof(hdr)); hdr.htype = bswap16(1); hdr.ptype = bswap16(ETHERTYPE_IPV4); - hdr.hlen = 6; + hdr.hlen = MAC_ADDR_LEN; hdr.plen = 4; hdr.opcode = bswap16(ARP_OPCODE_REQUEST); - memcpy(hdr.sender_mac, local_mac, 6); + mac_copy(hdr.sender_mac, local_mac); hdr.sender_ip = bswap32(sender_ip); hdr.target_ip = bswap32(target_ip); netpkt_t* pkt = netpkt_alloc((uint32_t)sizeof(hdr), (uint32_t)sizeof(eth_hdr_t), 0); @@ -324,8 +321,8 @@ bool arp_send_request_on(uint8_t ifindex, uint32_t sender_ip, uint32_t target_ip } bool arp_dad_ipv4_on(uint8_t ifindex, uint32_t ip) { - uint8_t mac[6]; - if (!l2_arp(ifindex) || !ip || ip == 0xFFFFFFFFu || ipv4_is_multicast(ip)) return false; + uint8_t mac[MAC_ADDR_LEN]; + if (!l2_arp(ifindex) || !ip || ip == IPV4_LIMITED_BROADCAST || ipv4_is_multicast(ip)) return false; if (arp_table_get_for_l2(ifindex, ip, mac)) return false; for (int i = 0; i < ARP_DAD_PROBES; i++) { @@ -350,14 +347,14 @@ static bool l2_has_ip(uint8_t ifindex, uint32_t ip){ return false; } -static void arp_send_reply_on(uint8_t ifindex, const arp_hdr_t* in_arp, const uint8_t in_src_mac[6]){ +static void arp_send_reply_on(uint8_t ifindex, const arp_hdr_t* in_arp, const uint8_t in_src_mac[MAC_ADDR_LEN]){ const uint8_t* local_mac = network_get_mac(ifindex); if (!local_mac) return; uint32_t spa = bswap32(in_arp->target_ip); if (!l2_has_ip(ifindex, spa)) return; arp_hdr_t reply = *in_arp; - memcpy(reply.target_mac, in_arp->sender_mac, 6); - memcpy(reply.sender_mac, local_mac, 6); + mac_copy(reply.target_mac, in_arp->sender_mac); + mac_copy(reply.sender_mac, local_mac); reply.target_ip = in_arp->sender_ip; reply.sender_ip = bswap32(spa); reply.opcode = bswap16(ARP_OPCODE_REPLY); @@ -372,7 +369,7 @@ static void arp_send_reply_on(uint8_t ifindex, const arp_hdr_t* in_arp, const ui (void)eth_send_frame_on(ifindex, ETHERTYPE_ARP, in_src_mac, pkt); } -void arp_input(uint16_t ifindex, const uint8_t src_mac[6], netpkt_t* pkt) { +void arp_input(uint16_t ifindex, const uint8_t src_mac[MAC_ADDR_LEN], netpkt_t* pkt) { if (!pkt || !src_mac) return; if (netpkt_len(pkt) < (uint32_t)sizeof(arp_hdr_t)) return; @@ -382,8 +379,8 @@ void arp_input(uint16_t ifindex, const uint8_t src_mac[6], netpkt_t* pkt) { uint32_t sender_ip = bswap32(hdr.sender_ip); uint32_t target_ip = bswap32(hdr.target_ip); - bool sender_mac_matches = memcmp(hdr.sender_mac, src_mac, 6) == 0; - bool sender_is_usable = sender_ip != 0 && sender_ip != 0xFFFFFFFF && !ipv4_is_multicast(sender_ip) && !l2_has_ip((uint8_t)ifindex, sender_ip); + bool sender_mac_matches = mac_equal(hdr.sender_mac, src_mac); + bool sender_is_usable = sender_ip != 0 && sender_ip != IPV4_LIMITED_BROADCAST && !ipv4_is_multicast(sender_ip) && !l2_has_ip((uint8_t)ifindex, sender_ip); if (sender_mac_matches && sender_is_usable) arp_table_put_for_l2((uint8_t)ifindex, sender_ip, hdr.sender_mac, ARP_REACHABLE_MS, false); if (op == ARP_OPCODE_REQUEST) { diff --git a/kernel/networking/link_layer/arp.h b/kernel/networking/link_layer/arp.h index f7cdf8bf..23d6f122 100644 --- a/kernel/networking/link_layer/arp.h +++ b/kernel/networking/link_layer/arp.h @@ -1,7 +1,7 @@ #pragma once #include "types.h" -#include "networking/interface_manager.h" #include "networking/netpkt.h" +#include "networking/link_layer/link_utils.h" #ifdef __cplusplus extern "C" { @@ -26,15 +26,15 @@ typedef struct __attribute__((packed)) arp_hdr_t { uint8_t hlen; uint8_t plen; uint16_t opcode;//1=request, 2=reply - uint8_t sender_mac[6]; + uint8_t sender_mac[MAC_ADDR_LEN]; uint32_t sender_ip; - uint8_t target_mac[6]; + uint8_t target_mac[MAC_ADDR_LEN]; uint32_t target_ip; } arp_hdr_t; typedef struct arp_entry { uint32_t ip; - uint8_t mac[6]; + uint8_t mac[MAC_ADDR_LEN]; uint32_t ttl_ms; uint32_t timer_ms; uint32_t pending_bytes; @@ -51,8 +51,8 @@ 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_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_put_for_l2(uint8_t ifindex, uint32_t ip, const uint8_t mac[MAC_ADDR_LEN], uint32_t ttl_ms, bool is_static); +bool arp_table_get_for_l2(uint8_t ifindex, uint32_t ip, uint8_t mac_out[MAC_ADDR_LEN]); bool arp_table_delete_for_l2(uint8_t ifindex, uint32_t ip); uint32_t arp_table_dump_for_l2(uint8_t ifindex, arp_entry_t* out, uint32_t out_cap); void arp_table_tick_for_l2(uint8_t ifindex, uint32_t ms); @@ -62,7 +62,7 @@ bool arp_send_or_queue_on(uint8_t ifindex, uint32_t ip, netpkt_t* pkt); bool arp_send_request_on(uint8_t ifindex, uint32_t sender_ip, uint32_t target_ip); bool arp_dad_ipv4_on(uint8_t ifindex, uint32_t ip); -void arp_input(uint16_t ifindex, const uint8_t src_mac[6], netpkt_t* pkt); +void arp_input(uint16_t ifindex, const uint8_t src_mac[MAC_ADDR_LEN], netpkt_t* pkt); uint16_t arp_get_pid(void); int arp_daemon_entry(int argc, char* argv[]); diff --git a/kernel/networking/link_layer/eth.c b/kernel/networking/link_layer/eth.c index d31c4e80..5e03e840 100644 --- a/kernel/networking/link_layer/eth.c +++ b/kernel/networking/link_layer/eth.c @@ -6,11 +6,11 @@ #include "networking/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], const uint8_t dst_mac[6], uint16_t type) { +uintptr_t create_eth_packet(uintptr_t p, const uint8_t src_mac[MAC_ADDR_LEN], const uint8_t dst_mac[MAC_ADDR_LEN], uint16_t type) { uint8_t* eth = (uint8_t*)p; - memcpy(eth, dst_mac, 6); - memcpy(eth+6, src_mac, 6); + mac_copy(eth, dst_mac); + mac_copy(eth + MAC_ADDR_LEN, src_mac); wr_be16(eth+12, type); return p + (uint32_t)sizeof(eth_hdr_t); @@ -22,17 +22,17 @@ uint16_t eth_parse_type(const netpkt_t* pkt){ return rd_be16(&type); } -bool eth_src(const netpkt_t* pkt, uint8_t out[6]){ +bool eth_src(const netpkt_t* pkt, uint8_t out[MAC_ADDR_LEN]){ if (!pkt || !out) return false; - return netpkt_copyout(pkt, 6u, out, 6); + return netpkt_copyout(pkt, 6u, out, MAC_ADDR_LEN); } -bool eth_dst(const netpkt_t* pkt, uint8_t out[6]){ +bool eth_dst(const netpkt_t* pkt, uint8_t out[MAC_ADDR_LEN]){ if (!pkt || !out) return false; - return netpkt_copyout(pkt, 0u, out, 6); + return netpkt_copyout(pkt, 0u, out, MAC_ADDR_LEN); } -bool eth_send_frame_on(uint16_t ifindex, uint16_t ethertype, const uint8_t dst_mac[6], netpkt_t* pkt){ +bool eth_send_frame_on(uint16_t ifindex, uint16_t ethertype, const uint8_t dst_mac[MAC_ADDR_LEN], netpkt_t* pkt){ const uint8_t* src_mac = network_get_mac(ifindex); if (!src_mac || !dst_mac || !pkt) { if (pkt) netpkt_unref(pkt); diff --git a/kernel/networking/link_layer/eth.h b/kernel/networking/link_layer/eth.h index 7ac9d339..40bacd3e 100644 --- a/kernel/networking/link_layer/eth.h +++ b/kernel/networking/link_layer/eth.h @@ -2,6 +2,7 @@ #include "types.h" #include "net/network_types.h" #include "networking/netpkt.h" +#include "networking/link_layer/link_utils.h" #ifdef __cplusplus extern "C" { @@ -14,16 +15,16 @@ extern "C" { #define ETHERTYPE_IPV6 0x86DD typedef struct __attribute__((packed)) eth_hdr_t { - uint8_t dst_mac[6]; - uint8_t src_mac[6]; + uint8_t dst_mac[MAC_ADDR_LEN]; + uint8_t src_mac[MAC_ADDR_LEN]; uint16_t ethertype; } eth_hdr_t; uint16_t eth_parse_type(const netpkt_t* pkt); -bool eth_src(const netpkt_t* pkt, uint8_t out[6]); -bool eth_dst(const netpkt_t* pkt, uint8_t out[6]); +bool eth_src(const netpkt_t* pkt, uint8_t out[MAC_ADDR_LEN]); +bool eth_dst(const netpkt_t* pkt, uint8_t out[MAC_ADDR_LEN]); -bool eth_send_frame_on(uint16_t ifindex, uint16_t ethertype, const uint8_t dst_mac[6], netpkt_t* pkt); +bool eth_send_frame_on(uint16_t ifindex, uint16_t ethertype, const uint8_t dst_mac[MAC_ADDR_LEN], netpkt_t* pkt); void eth_input(uint16_t ifindex, netpkt_t* pkt); diff --git a/kernel/networking/link_layer/link_utils.c b/kernel/networking/link_layer/link_utils.c index 09d6af3e..20a92023 100644 --- a/kernel/networking/link_layer/link_utils.c +++ b/kernel/networking/link_layer/link_utils.c @@ -1,13 +1,35 @@ #include "link_utils.h" +#include "std/memory.h" -void mac_to_string(const uint8_t mac[6], char out[18]){ +void mac_copy(uint8_t dst[MAC_ADDR_LEN], const uint8_t src[MAC_ADDR_LEN]){ + if (!dst || !src) return; + memcpy(dst, src, MAC_ADDR_LEN); +} + +bool mac_equal(const uint8_t a[MAC_ADDR_LEN], const uint8_t b[MAC_ADDR_LEN]){ + if (!a || !b) return false; + return memcmp(a, b, MAC_ADDR_LEN) == 0; +} + +void mac_clear(uint8_t mac[MAC_ADDR_LEN]){ + if (!mac) return; + memset(mac, 0, MAC_ADDR_LEN); +} + +void mac_set_broadcast(uint8_t mac[MAC_ADDR_LEN]){ + if (!mac) return; + memset(mac, 0xFF, MAC_ADDR_LEN); +} + +void mac_to_string(const uint8_t mac[MAC_ADDR_LEN], char out[18]){ + if (!out) return; static const char HEX[] = "0123456789abcdef"; int p = 0; - for (int i = 0; i < 6; ++i) { + for (int i = 0; i < MAC_ADDR_LEN; ++i) { uint8_t b = mac ? mac[i] : 0; out[p++] = HEX[b >> 4]; out[p++] = HEX[b & 0x0F]; - if (i != 5) out[p++] = ':'; + if (i != MAC_ADDR_LEN - 1) out[p++] = ':'; } out[p] = 0; } diff --git a/kernel/networking/link_layer/link_utils.h b/kernel/networking/link_layer/link_utils.h index 78d12204..2688adba 100644 --- a/kernel/networking/link_layer/link_utils.h +++ b/kernel/networking/link_layer/link_utils.h @@ -6,7 +6,13 @@ extern "C" { #endif -void mac_to_string(const uint8_t mac[6], char out[18]); +#define MAC_ADDR_LEN 6 + +void mac_copy(uint8_t dst[MAC_ADDR_LEN], const uint8_t src[MAC_ADDR_LEN]); +bool mac_equal(const uint8_t a[MAC_ADDR_LEN], const uint8_t b[MAC_ADDR_LEN]); +void mac_clear(uint8_t mac[MAC_ADDR_LEN]); +void mac_set_broadcast(uint8_t mac[MAC_ADDR_LEN]); +void mac_to_string(const uint8_t mac[MAC_ADDR_LEN], char out[18]); #ifdef __cplusplus } diff --git a/kernel/networking/link_layer/ndp.c b/kernel/networking/link_layer/ndp.c index 1364b276..b9f8b4cd 100644 --- a/kernel/networking/link_layer/ndp.c +++ b/kernel/networking/link_layer/ndp.c @@ -1,5 +1,6 @@ #include "ndp.h" #include "eth.h" +#include "link_utils.h" #include "networking/internet_layer/icmpv6.h" #include "std/memory.h" #include "std/string.h" @@ -412,7 +413,7 @@ void ndp_table_put_for_l2(uint8_t ifindex, const uint8_t ip[16], const uint8_t m ipv6_cpy(e->ip, ip); if (mac) { - memcpy(e->mac, mac, 6); + mac_copy(e->mac, mac); e->state = NDP_STATE_REACHABLE; e->timer_ms = g_ndp_reachable_time_ms; } @@ -453,7 +454,7 @@ bool ndp_table_get_for_l2(uint8_t ifindex, const uint8_t ip[16], uint8_t mac_out if (e->state == NDP_STATE_INCOMPLETE) continue; if (ipv6_cmp(e->ip, ip) != 0) continue; - memcpy(mac_out, e->mac, 6); + mac_copy(mac_out, e->mac); return true; } @@ -482,6 +483,9 @@ uint32_t ndp_table_dump_for_l2(uint8_t ifindex, ndp_entry_t* out, uint32_t out_c } static bool ndp_send_na_on(uint8_t ifindex, const uint8_t dst_ip[16], const uint8_t src_ip[16], const uint8_t target_ip[16], const uint8_t dst_mac_in[6], const uint8_t my_mac[6], uint8_t solicited) { + if (!my_mac) return false; + if (!ipv6_is_multicast(dst_ip) && !dst_mac_in) return false; + uint32_t plen = (uint32_t)(sizeof(icmpv6_na_t) + sizeof(icmpv6_opt_lladdr_t)); uintptr_t buf = (uintptr_t)zalloc(plen ? plen : 1u); if (!buf) return false; @@ -501,13 +505,13 @@ static bool ndp_send_na_on(uint8_t ifindex, const uint8_t dst_ip[16], const uint icmpv6_opt_lladdr_t* opt = (icmpv6_opt_lladdr_t*)(buf + sizeof(icmpv6_na_t)); opt->type = 2; opt->length = 1; - memcpy(opt->mac, my_mac, 6); + mac_copy(opt->mac, my_mac); na->hdr.checksum = bswap16(checksum16_pipv6(src_ip, dst_ip, PROTO_ICMPV6, (const uint8_t*)buf, plen)); uint8_t dst_mac[6]; if (ipv6_is_multicast(dst_ip)) ipv6_multicast_mac(dst_ip, dst_mac); - else memcpy(dst_mac, dst_mac_in, 6); + else mac_copy(dst_mac, dst_mac_in); bool ok = icmpv6_send_on_l2(ifindex, dst_ip, src_ip, dst_mac, (const void*)buf, plen, 255); @@ -535,8 +539,8 @@ static void ndp_send_ns_on(uint8_t ifindex, const uint8_t target_ip[16], const u opt->length = 1; const uint8_t* mac = network_get_mac(ifindex); - if (mac) memcpy(opt->mac, mac, 6); - else memset(opt->mac, 0, 6); + if (mac) mac_copy(opt->mac, mac); + else mac_clear(opt->mac); } uint8_t dst_ip[16]; @@ -592,8 +596,8 @@ static void ndp_send_rs_on(uint8_t ifindex) { opt->length = 1; const uint8_t* mac = network_get_mac(ifindex); - if (mac) memcpy(opt->mac, mac, 6); - else memset(opt->mac, 0, 6); + if (mac) mac_copy(opt->mac, mac); + else mac_clear(opt->mac); rs->hdr.checksum = bswap16(checksum16_pipv6(src_ip, dst_ip, PROTO_ICMPV6, (const uint8_t*)buf, plen)); @@ -760,7 +764,7 @@ bool ndp_send_or_queue_on(uint16_t ifindex, const uint8_t next_hop[16], netpkt_t } ipv6_cpy(e->ip, next_hop); - memset(e->mac, 0, 6); + mac_clear(e->mac); e->ttl_ms = g_ndp_reachable_time_ms * 4; e->is_router = 0; e->router_lifetime_ms = 0; @@ -939,11 +943,11 @@ void ndp_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[ if (e->state != NDP_STATE_UNUSED && ipv6_cmp(e->ip, na.target) != 0) ndp_entry_clear(e); uint8_t old_mac[6]; - memcpy(old_mac, e->mac, 6); + mac_copy(old_mac, e->mac); if (e->ttl_ms == 0 && e->state == NDP_STATE_UNUSED) { ipv6_cpy(e->ip, na.target); - memcpy(e->mac, src_mac, 6); + mac_copy(e->mac, src_mac); e->ttl_ms = g_ndp_reachable_time_ms * 4; e->probes_sent = 0; e->is_router = router ? 1 : 0; @@ -951,10 +955,10 @@ void ndp_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[ ndp_mark_neighbor_observed(e, solicited); } else { - int mac_changed = memcmp(old_mac, src_mac, 6) != 0; + int mac_changed = !mac_equal(old_mac, src_mac); if (e->state == NDP_STATE_INCOMPLETE) { - memcpy(e->mac, src_mac, 6); + mac_copy(e->mac, src_mac); e->ttl_ms = g_ndp_reachable_time_ms * 4; ndp_mark_neighbor_observed(e, solicited); @@ -963,7 +967,7 @@ void ndp_input(uint16_t ifindex, const uint8_t src_ip[16], const uint8_t dst_ip[ if (solicited) ndp_mark_neighbor_observed(e, true); } else { if (override) { - memcpy(e->mac, src_mac, 6); + mac_copy(e->mac, src_mac); e->ttl_ms = g_ndp_reachable_time_ms * 4; ndp_mark_neighbor_observed(e, solicited); diff --git a/kernel/networking/network_dispatch.cpp b/kernel/networking/network_dispatch.cpp index efc7fe73..b7809ad2 100644 --- a/kernel/networking/network_dispatch.cpp +++ b/kernel/networking/network_dispatch.cpp @@ -258,7 +258,7 @@ bool NetworkDispatch::register_all_from_bus() { strncpy(c->ifname_str, name, (int)sizeof(c->ifname_str)); strncpy(c->hwname_str, hw, (int)sizeof(c->hwname_str)); - memcpy(c->mac_addr, macbuf, 6); + mac_copy(c->mac_addr, macbuf); c->mtu_val = m; c->hdr_sz = hs; c->speed_mbps = sp; diff --git a/kernel/networking/transport_layer/csocket.c b/kernel/networking/transport_layer/csocket.c index 8e856c82..210a29f8 100644 --- a/kernel/networking/transport_layer/csocket.c +++ b/kernel/networking/transport_layer/csocket.c @@ -20,12 +20,11 @@ socket_handle_t create_socket(protocol_t protocol, const SocketOptions* extra){ bool raw_socket = special_kind == SOCKET_SPECIAL_RAW && (protocol == PROTO_ICMP || protocol == PROTO_ICMPV6 || protocol == PROTO_IGMP); bool ctrl_socket = special_kind == SOCKET_SPECIAL_CTRL && protocol == PROTO_NONE; bool packet_socket = special_kind == SOCKET_SPECIAL_PACKET && protocol == PROTO_NONE; - if (!normal_socket && !raw_socket && !ctrl_socket && !packet_socket) return false; + if (!normal_socket && !raw_socket && !ctrl_socket && !packet_socket) return 0; uint16_t pid = get_current_proc_pid(); ksocket_t* socket = NULL; - if (!socket_core_alloc(protocol, special_kind, pid, &socket)) return false; - + if (!socket_core_alloc(protocol, special_kind, pid, &socket)) return 0; socket_impl_t impl = NULL; socket_impl_destroy_fn destroy = NULL; @@ -60,13 +59,13 @@ socket_handle_t create_socket(protocol_t protocol, const SocketOptions* extra){ } if (!impl) { socket_core_close_socket(socket); - return false; + return 0; } if (!socket_core_attach_impl(socket, impl, destroy, close, setopt, getopt)) { destroy(impl); socket_core_close_socket(socket); - return false; + return 0; } return socket_core_export_handle(socket); } diff --git a/kernel/networking/transport_layer/csocket_raw.c b/kernel/networking/transport_layer/csocket_raw.c index ea2dd165..378bc17f 100644 --- a/kernel/networking/transport_layer/csocket_raw.c +++ b/kernel/networking/transport_layer/csocket_raw.c @@ -10,6 +10,7 @@ #include "networking/internet_layer/ipv6_route.h" #include "networking/internet_layer/ipv6_utils.h" #include "networking/link_layer/eth.h" +#include "networking/transport_layer/trans_utils.h" #include "syscalls/syscalls.h" #define RAW_SOCKET_MAX 64 @@ -31,18 +32,83 @@ typedef struct raw_socket { SockBindSpec bind_spec; net_l4_endpoint remote_ep; raw_rx_entry_t rx[RAW_RX_RING_CAP]; - uint8_t head; - uint8_t tail; - uint8_t count; - uint32_t bytes; + uint8_t rx_head; + uint8_t rx_tail; + uint8_t rx_count; + uint32_t rx_bytes; } raw_socket_t; static raw_socket_t* g_raw_sockets[RAW_SOCKET_MAX]; +static int32_t raw_set_filter(raw_socket_t* s, const void* value, uint32_t len) { + if (!s) return SOCK_ERR_INVAL; + if (!value && !len) { + memset(&s->options.raw_filter, 0, sizeof(s->options.raw_filter)); + s->options.flags &= ~SOCK_OPT_RAW_FILTER; + return SOCK_OK; + } + if (!value || len != sizeof(SocketRawFilter)) return SOCK_ERR_INVAL; + + SocketRawFilter filter; + memcpy(&filter, value, sizeof(filter)); + if (filter.count > SOCKET_RAW_FILTER_MAX_RULES) return SOCK_ERR_INVAL; + if (filter.reserved[0] || filter.reserved[1] || filter.reserved[2]) return SOCK_ERR_INVAL; + for (uint32_t i = 0; i < filter.count; i++) if (filter.rules[i].has_code > 1 || filter.rules[i].reserved) return SOCK_ERR_INVAL; + + s->options.raw_filter = filter; + if (filter.count) s->options.flags |= SOCK_OPT_RAW_FILTER; + else s->options.flags &= ~SOCK_OPT_RAW_FILTER; + return SOCK_OK; +} + +static bool raw_enqueue(raw_socket_t* s, const net_l4_endpoint* src, netpkt_t* pkt, uint32_t len) { + if (!s || !src || !pkt || !len || len > NETPKT_MAX_ALLOC || len > RAW_RX_MAX_BYTES) return false; + + if (s->options.raw_filter.count) { + if (netpkt_len(pkt) < 2) return false; + uint8_t hdr[2]; + if (!netpkt_copyout(pkt, 0, hdr, sizeof(hdr))) return false; + bool ok = false; + for (uint32_t i = 0; i < s->options.raw_filter.count; i++) { + const SocketRawFilterRule* rule = &s->options.raw_filter.rules[i]; + if (rule->type != hdr[0]) continue; + if (rule->has_code && rule->code != hdr[1]) continue; + ok = true; + break; + } + if (!ok) return false; + } + + uint8_t* copy = (uint8_t*)zalloc(len); + if (!copy) return false; + if (!netpkt_copyout(pkt, 0, copy, len)) { + release(copy); + return false; + } + + irq_flags_t irq = irq_save_disable(); + if (s->rx_count >= RAW_RX_RING_CAP || s->rx_bytes > RAW_RX_MAX_BYTES - len) { + irq_restore(irq); + release(copy); + return false; + } + + uint8_t pos = s->rx_tail; + s->rx[pos].data = copy; + s->rx[pos].len = len; + s->rx[pos].src = *src; + s->rx_tail = (uint8_t)((s->rx_tail + 1) % RAW_RX_RING_CAP); + s->rx_count++; + s->rx_bytes += len; + irq_restore(irq); + return true; +} + socket_impl_t socket_raw_create(ksocket_t* owner, const SocketOptions* extra) { if (!owner || socket_core_special_kind(owner) != SOCKET_SPECIAL_RAW) return NULL; protocol_t proto = socket_core_protocol(owner); if (proto != PROTO_ICMP && proto != PROTO_IGMP && proto != PROTO_ICMPV6) return NULL; + //TODO add ESP/AH sock if needed raw_socket_t* s = (raw_socket_t*)zalloc(sizeof(raw_socket_t)); if (!s) return NULL; @@ -65,6 +131,12 @@ socket_impl_t socket_raw_create(ksocket_t* owner, const SocketOptions* extra) { s->options.flags |= SOCK_OPT_TTL; s->options.ttl = extra->ttl; } + if (extra->flags & SOCK_OPT_RAW_FILTER) { + if (raw_set_filter(s, &extra->raw_filter, sizeof(extra->raw_filter)) != SOCK_OK) { + release(s); + return NULL; + } + } } irq_flags_t irq = irq_save_disable(); @@ -128,6 +200,8 @@ int32_t socket_setopt_raw(socket_impl_t sh, int32_t opt, const void* value, uint case SOCK_OPT_DONTFRAG: case SOCK_OPT_TTL: return socket_common_options_set(&s->options, opt, value, len); + case SOCK_OPT_RAW_FILTER: + return raw_set_filter(s, value, len); default: return SOCK_ERR_INVAL; } @@ -156,6 +230,15 @@ int32_t socket_getopt_raw(socket_impl_t sh, int32_t opt, void* value, uint32_t* memcpy(value, &s->bind_spec, sizeof(s->bind_spec)); *len = sizeof(SockBindSpec); return SOCK_OK; + case SOCK_GET_OPT_RAW_FILTER: + if (!value) { + *len = sizeof(SocketRawFilter); + return SOCK_OK; + } + if (*len < sizeof(SocketRawFilter)) return SOCK_ERR_INVAL; + memcpy(value, &s->options.raw_filter, sizeof(s->options.raw_filter)); + *len = sizeof(SocketRawFilter); + return SOCK_OK; default: break; } @@ -174,7 +257,7 @@ int32_t socket_getopt_raw(socket_impl_t sh, int32_t opt, void* value, uint32_t* v = 0; break; case SOCK_GET_RECV_QUEUED: - v = s->bytes; + v = s->rx_bytes; break; case SOCK_GET_OPT_KEEPALIVE: case SOCK_GET_OPT_KEEPALIVE_INTERVAL: @@ -242,15 +325,17 @@ int32_t socket_bind_raw(socket_impl_t sh, const SockBindSpec* spec) { memcpy(&ip, spec->ip, sizeof(ip)); l3_ipv4_interface_t* v4 = l3_ipv4_find_by_ip(ip); if (!v4) return SOCK_ERR_INVAL; - next.kind = BIND_L3; + next.kind = BIND_IP; next.ver = IP_VER4; next.l3_id = v4->l3_id; + memcpy(next.ip, spec->ip, 4); } else if (proto == PROTO_ICMPV6 && spec->ver == IP_VER6) { l3_ipv6_interface_t* v6 = l3_ipv6_find_by_ip(spec->ip); if (!v6) return SOCK_ERR_INVAL; - next.kind = BIND_L3; + next.kind = BIND_IP; next.ver = IP_VER6; next.l3_id = v6->l3_id; + ipv6_cpy(next.ip, spec->ip); } else return SOCK_ERR_INVAL; } else return SOCK_ERR_INVAL; @@ -267,6 +352,7 @@ int32_t socket_connect_raw(socket_impl_t sh, const net_l4_endpoint* dst) { if (((proto == PROTO_ICMP || proto == PROTO_IGMP) && dst->ver != IP_VER4) || (proto == PROTO_ICMPV6 && dst->ver != IP_VER6)) return SOCK_ERR_INVAL; s->remote_ep = *dst; + s->remote_ep.port = 0; s->connected = true; return SOCK_OK; } @@ -291,7 +377,7 @@ int64_t socket_sendto_raw(socket_impl_t sh, const net_l4_endpoint* dst, const vo tx.scope = IP_TX_BOUND_L2; tx.index = s->bind_spec.ifindex; txp = &tx; - } else if (s->bound && s->bind_spec.kind == BIND_L3) { + } else if (s->bound && (s->bind_spec.kind == BIND_L3 || s->bind_spec.kind == BIND_IP)) { tx.scope = IP_TX_BOUND_L3; tx.index = s->bind_spec.l3_id; txp = &tx; @@ -338,7 +424,7 @@ int64_t socket_recv_raw(socket_impl_t sh, void* buf, uint64_t len, net_l4_endpoi for (;;) { irq_flags_t irq = irq_save_disable(); - bool ready = s->count != 0; + bool ready = s->rx_count != 0; irq_restore(irq); if (ready) break; if (!(s->options.flags & SOCK_OPT_RECV_TIMEOUT) || !s->options.recv_timeout_ms) return SOCK_ERR_WOULDBLOCK; @@ -346,7 +432,7 @@ int64_t socket_recv_raw(socket_impl_t sh, void* buf, uint64_t len, net_l4_endpoi uint32_t start_ms = (uint32_t)get_time(); while (1) { irq = irq_save_disable(); - ready = s->count != 0; + ready = s->rx_count != 0; irq_restore(irq); if (ready) break; @@ -359,15 +445,15 @@ int64_t socket_recv_raw(socket_impl_t sh, void* buf, uint64_t len, net_l4_endpoi } irq_flags_t irq = irq_save_disable(); - uint8_t pos = s->head; + uint8_t pos = s->rx_head; uint8_t* data = s->rx[pos].data; uint32_t pkt_len = s->rx[pos].len; net_l4_endpoint src = s->rx[pos].src; - s->bytes -= pkt_len; + s->rx_bytes -= pkt_len; s->rx[pos].data = NULL; s->rx[pos].len = 0; - s->head = (uint8_t)((s->head + 1) % RAW_RX_RING_CAP); - s->count--; + s->rx_head = (uint8_t)((s->rx_head + 1) % RAW_RX_RING_CAP); + s->rx_count--; irq_restore(irq); uint32_t n = pkt_len; @@ -382,9 +468,7 @@ bool socket_raw_input_v4(protocol_t protocol, uint8_t ifindex, uint32_t src, uin if ((protocol != PROTO_ICMP && protocol != PROTO_IGMP) || !ifindex || !pkt) return false; net_l4_endpoint src_ep; - memset(&src_ep, 0, sizeof(src_ep)); - src_ep.ver = IP_VER4; - memcpy(src_ep.ip, &src, sizeof(src)); + make_ep(&src, 0, IP_VER4, &src_ep); raw_socket_t* targets[RAW_SOCKET_MAX]; int n = 0; @@ -401,7 +485,12 @@ bool socket_raw_input_v4(protocol_t protocol, uint8_t ifindex, uint32_t src, uin if (s->bound && s->bind_spec.kind == BIND_L3) { l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(s->bind_spec.l3_id); if (!v4 || !v4->l2 || v4->l2->ifindex != ifindex) continue; - if (!ipv4_is_multicast(dst) && dst != 0xFFFFFFFF && (!v4->mask || ipv4_broadcast_calc(v4->ip, v4->mask) != dst) && v4->ip != dst) continue; + if (!ipv4_is_multicast(dst) && dst != IPV4_LIMITED_BROADCAST && (!v4->mask || ipv4_broadcast_calc(v4->ip, v4->mask) != dst) && v4->ip != dst) continue; + } + if (s->bound && s->bind_spec.kind == BIND_IP) { + uint32_t local = 0; + memcpy(&local, s->bind_spec.ip, sizeof(local)); + if (local != dst) continue; } socket_core_ref(s->ownerSocket); targets[n++] = s; @@ -412,25 +501,7 @@ bool socket_raw_input_v4(protocol_t protocol, uint8_t ifindex, uint32_t src, uin uint32_t len = netpkt_len(pkt); for (int i = 0; i < n; i++) { raw_socket_t* s = targets[i]; - if (len && len <= NETPKT_MAX_ALLOC && len <= RAW_RX_MAX_BYTES) { - uint8_t* copy = (uint8_t*)zalloc(len); - if (copy && netpkt_copyout(pkt, 0, copy, len)) { - irq = irq_save_disable(); - if (s->count < RAW_RX_RING_CAP && s->bytes <= RAW_RX_MAX_BYTES - len) { - uint8_t pos = s->tail; - s->rx[pos].data = copy; - s->rx[pos].len = len; - s->rx[pos].src = src_ep; - s->tail = (uint8_t)((s->tail + 1) % RAW_RX_RING_CAP); - s->count++; - s->bytes += len; - copy = NULL; - delivered = true; - } - irq_restore(irq); - } - if (copy) release(copy); - } + if (raw_enqueue(s, &src_ep, pkt, len)) delivered = true; socket_core_put(s->ownerSocket); } return delivered; @@ -440,9 +511,7 @@ bool socket_raw_input_v6(uint8_t ifindex, const uint8_t src[16], const uint8_t d if (!ifindex || !src || !dst || !pkt) return false; net_l4_endpoint src_ep; - memset(&src_ep, 0, sizeof(src_ep)); - src_ep.ver = IP_VER6; - ipv6_cpy(src_ep.ip, src); + make_ep(src, 0, IP_VER6, &src_ep); raw_socket_t* targets[RAW_SOCKET_MAX]; int n = 0; @@ -457,6 +526,7 @@ bool socket_raw_input_v6(uint8_t ifindex, const uint8_t src[16], const uint8_t d if (!v6 || !v6->l2 || v6->l2->ifindex != ifindex) continue; if (!ipv6_is_multicast(dst) && ipv6_cmp(v6->ip, dst) != 0) continue; } + if (s->bound && s->bind_spec.kind == BIND_IP && ipv6_cmp(s->bind_spec.ip, dst) != 0) continue; socket_core_ref(s->ownerSocket); targets[n++] = s; } @@ -466,25 +536,7 @@ bool socket_raw_input_v6(uint8_t ifindex, const uint8_t src[16], const uint8_t d uint32_t len = netpkt_len(pkt); for (int i = 0; i < n; i++) { raw_socket_t* s = targets[i]; - if (len && len <= NETPKT_MAX_ALLOC && len <= RAW_RX_MAX_BYTES) { - uint8_t* copy = (uint8_t*)zalloc(len); - if (copy && netpkt_copyout(pkt, 0, copy, len)) { - irq = irq_save_disable(); - if (s->count < RAW_RX_RING_CAP && s->bytes <= RAW_RX_MAX_BYTES - len) { - uint8_t pos = s->tail; - s->rx[pos].data = copy; - s->rx[pos].len = len; - s->rx[pos].src = src_ep; - s->tail = (uint8_t)((s->tail + 1) % RAW_RX_RING_CAP); - s->count++; - s->bytes += len; - copy = NULL; - delivered = true; - } - irq_restore(irq); - } - if (copy) release(copy); - } + if (raw_enqueue(s, &src_ep, pkt, len)) delivered = true; socket_core_put(s->ownerSocket); } return delivered; diff --git a/kernel/networking/transport_layer/csocket_tcp.c b/kernel/networking/transport_layer/csocket_tcp.c index fc016836..8db66ef8 100644 --- a/kernel/networking/transport_layer/csocket_tcp.c +++ b/kernel/networking/transport_layer/csocket_tcp.c @@ -2,6 +2,7 @@ #include "networking/transport_layer/socket_bind.h" #include "networking/transport_layer/tcp.h" #include "networking/transport_layer/tcp/tcp_internal.h" +#include "networking/interface_manager.h" #include "networking/internet_layer/ipv4.h" #include "networking/internet_layer/ipv4_route.h" #include "networking/internet_layer/ipv4_utils.h" @@ -43,6 +44,7 @@ static ksocket_t* tcp_socket_pop_pending_at(tcp_socket_t* s, int idx) { socket_impl_t socket_tcp_create(ksocket_t* owner, const SocketOptions* extra) { if (!owner) return NULL; + if (extra && (extra->flags & SOCK_OPT_RAW_FILTER)) return NULL; tcp_socket_t* s = (tcp_socket_t*)zalloc(sizeof(*s)); if (!s) return NULL; @@ -108,6 +110,7 @@ int32_t socket_setopt_tcp(socket_impl_t sh, int32_t opt, const void* value, uint case SOCK_OPT_MCAST_JOIN: case SOCK_OPT_MCAST_LEAVE: case SOCK_OPT_BROADCAST_ALLOWED: + case SOCK_OPT_RAW_FILTER: return SOCK_ERR_INVAL; case SOCK_OPT_RECV_TIMEOUT: case SOCK_OPT_SEND_TIMEOUT: @@ -223,6 +226,7 @@ int32_t socket_getopt_tcp(socket_impl_t sh, int32_t opt, void* value, uint32_t* break; case SOCK_GET_MCAST_GROUPS: case SOCK_GET_OPT_BROADCAST_ALLOWED: + case SOCK_GET_OPT_RAW_FILTER: return SOCK_ERR_INVAL; case SOCK_GET_OPT_RECV_TIMEOUT: case SOCK_GET_OPT_SEND_TIMEOUT: diff --git a/kernel/networking/transport_layer/csocket_udp.c b/kernel/networking/transport_layer/csocket_udp.c index 20df0344..6c3bb3e1 100644 --- a/kernel/networking/transport_layer/csocket_udp.c +++ b/kernel/networking/transport_layer/csocket_udp.c @@ -17,6 +17,11 @@ #define UDP_DEFAULT_RING_CAP 64 #define UDP_MAX_RING_CAP 1024 +typedef struct udp_rx_entry { + netpkt_t* pkt; + net_l4_endpoint src; +} udp_rx_entry_t; + typedef struct udp_socket { uint16_t localPort; net_l4_endpoint remoteEP; @@ -25,8 +30,7 @@ typedef struct udp_socket { SocketOptions options; SockBindSpec bindSpec; socket_bind_token_t bindToken; - netpkt_t** ring; - net_l4_endpoint* src_eps; + udp_rx_entry_t* rx_ring; net_l4_endpoint* mcast_groups; uint8_t mcast_count; uint32_t ring_cap; @@ -144,6 +148,7 @@ uint32_t socket_udp_input(ksocket_t* socket, ip_version_t ipver, const void* src udp_socket_t* s = (udp_socket_t*)socket_core_impl(socket); if (!s) return 0; if (!s->bindToken || s->localPort != dst_port || !dst_ip_addr || !src_ip_addr) return 0; + if (ipver != IP_VER4 && ipver != IP_VER6) return 0; if (ipver == IP_VER4) { uint32_t dip = 0; @@ -165,7 +170,7 @@ uint32_t socket_udp_input(ksocket_t* socket, ip_version_t ipver, const void* src if (pkt_len > limit) return 0; if (s->rx_bytes > limit - pkt_len) return 0; - if (!s->ring || !s->src_eps || !s->ring_cap) { + if (!s->rx_ring || !s->ring_cap) { uint32_t usable = UDP_DEFAULT_RING_CAP; if ((s->options.flags & SOCK_OPT_BUF_SIZE) && s->options.buf_size) { usable = s->options.buf_size / MAX_PACKET_SIZE; @@ -174,13 +179,8 @@ uint32_t socket_udp_input(ksocket_t* socket, ip_version_t ipver, const void* src } s->ring_cap = usable + 1; - s->ring = (netpkt_t**)zalloc(sizeof(netpkt_t*) * s->ring_cap); - s->src_eps = (net_l4_endpoint*)zalloc(sizeof(net_l4_endpoint) * s->ring_cap); - if (!s->ring || !s->src_eps) { - if (s->ring) release(s->ring); - if (s->src_eps) release(s->src_eps); - s->ring = NULL; - s->src_eps = NULL; + s->rx_ring = (udp_rx_entry_t*)zalloc(sizeof(udp_rx_entry_t) * s->ring_cap); + if (!s->rx_ring) { s->ring_cap = 0; return 0; } @@ -190,20 +190,16 @@ uint32_t socket_udp_input(ksocket_t* socket, ip_version_t ipver, const void* src if (nexti == s->r_head) return 0; netpkt_ref(pkt); - s->ring[s->r_tail] = pkt; + s->rx_ring[s->r_tail].pkt = pkt; + make_ep(src_ip_addr, src_port, ipver, &s->rx_ring[s->r_tail].src); s->rx_bytes += pkt_len; - - s->src_eps[s->r_tail].ver = ipver; - memset(s->src_eps[s->r_tail].ip, 0, 16); - if (ipver == IP_VER4) memcpy(s->src_eps[s->r_tail].ip, src_ip_addr, 4); - else if (ipver == IP_VER6) ipv6_cpy(s->src_eps[s->r_tail].ip, src_ip_addr); - s->src_eps[s->r_tail].port = src_port; s->r_tail = nexti; return pkt_len; } socket_impl_t udp_socket_create(ksocket_t* owner, const SocketOptions* extra) { if (!owner) return NULL; + if (extra && (extra->flags & SOCK_OPT_RAW_FILTER)) return NULL; udp_socket_t* s = (udp_socket_t*)zalloc(sizeof(*s)); if (!s) return NULL; @@ -397,10 +393,7 @@ int64_t socket_sendto_udp(socket_impl_t sh, const net_l4_endpoint* dst, const vo } net_l4_endpoint src; - src.ver = IP_VER4; - memset(src.ip, 0, 16); - memcpy(src.ip, &bcast_v4->ip, 4); - src.port = s->localPort; + make_ep(&bcast_v4->ip, s->localPort, IP_VER4, &src); ip_tx_opts_t tx; tx.scope = IP_TX_BOUND_L3; @@ -430,10 +423,7 @@ int64_t socket_sendto_udp(socket_impl_t sh, const net_l4_endpoint* dst, const vo } net_l4_endpoint src; - src.ver = IP_VER4; - memset(src.ip, 0, 16); - memcpy(src.ip, &v4->ip, 4); - src.port = s->localPort; + make_ep(&v4->ip, s->localPort, IP_VER4, &src); ip_tx_opts_t tx; tx.scope = IP_TX_BOUND_L3; @@ -464,10 +454,7 @@ int64_t socket_sendto_udp(socket_impl_t sh, const net_l4_endpoint* dst, const vo } net_l4_endpoint src; - src.ver = IP_VER6; - memset(src.ip, 0, 16); - ipv6_cpy(src.ip, v6->ip); - src.port = s->localPort; + make_ep(v6->ip, s->localPort, IP_VER6, &src); ip_tx_opts_t tx; tx.scope = IP_TX_BOUND_L3; @@ -493,11 +480,11 @@ int64_t socket_recvfrom_udp(socket_impl_t sh, void* buf, uint64_t len, net_l4_en ev.remote_ep = s->remoteEP; netlog_socket_event(&s->options, &ev); - if (!s->ring || s->r_head == s->r_tail) { + if (!s->rx_ring || s->r_head == s->r_tail) { if (!(s->options.flags & SOCK_OPT_RECV_TIMEOUT) || !s->options.recv_timeout_ms) return SOCK_ERR_WOULDBLOCK; uint32_t start_ms = (uint32_t)get_time(); - while (!s->ring || s->r_head == s->r_tail) { + while (!s->rx_ring || s->r_head == s->r_tail) { uint32_t now_ms = (uint32_t)get_time(); uint32_t elapsed_ms = now_ms - start_ms; if (elapsed_ms >= s->options.recv_timeout_ms) return SOCK_ERR_WOULDBLOCK; @@ -508,9 +495,9 @@ int64_t socket_recvfrom_udp(socket_impl_t sh, void* buf, uint64_t len, net_l4_en } } - netpkt_t* p = s->ring[s->r_head]; - net_l4_endpoint se = s->src_eps[s->r_head]; - s->ring[s->r_head] = NULL; + netpkt_t* p = s->rx_ring[s->r_head].pkt; + net_l4_endpoint se = s->rx_ring[s->r_head].src; + memset(&s->rx_ring[s->r_head], 0, sizeof(s->rx_ring[s->r_head])); s->r_head = (s->r_head + 1) % s->ring_cap; uint32_t pkt_len = p ? netpkt_len(p) : 0; @@ -534,6 +521,7 @@ int32_t socket_setopt_udp(socket_impl_t sh, int32_t opt, const void* value, uint case SOCK_OPT_KEEPALIVE_INTERVAL: case SOCK_OPT_TCP_NO_DELAY: case SOCK_OPT_SEND_BUF_SIZE: + case SOCK_OPT_RAW_FILTER: return SOCK_ERR_INVAL; case SOCK_OPT_MCAST_JOIN: { if (!value || !len || (len % sizeof(net_l4_endpoint)) != 0) return SOCK_ERR_INVAL; @@ -725,6 +713,7 @@ int32_t socket_getopt_udp(socket_impl_t sh, int32_t opt, void* value, uint32_t* case SOCK_GET_OPT_KEEPALIVE_INTERVAL: case SOCK_GET_OPT_TCP_NO_DELAY: case SOCK_GET_OPT_SEND_BUF_SIZE: + case SOCK_GET_OPT_RAW_FILTER: case SOCK_GET_TCP_STATE: case SOCK_GET_TCP_MSS: case SOCK_GET_TCP_RTT_MS: @@ -778,20 +767,16 @@ int32_t socket_close_udp(socket_impl_t sh) { ev.remote_ep = s->remoteEP; netlog_socket_event(&s->options, &ev); - if (s->ring) { + if (s->rx_ring) { while (s->r_head != s->r_tail) { - if (s->ring[s->r_head]) { - s->rx_bytes -= netpkt_len(s->ring[s->r_head]); - netpkt_unref(s->ring[s->r_head]); + if (s->rx_ring[s->r_head].pkt) { + s->rx_bytes -= netpkt_len(s->rx_ring[s->r_head].pkt); + netpkt_unref(s->rx_ring[s->r_head].pkt); } s->r_head = (s->r_head + 1) % s->ring_cap; } - release(s->ring); - s->ring = NULL; - } - if (s->src_eps) { - release(s->src_eps); - s->src_eps = NULL; + release(s->rx_ring); + s->rx_ring = NULL; } if (s->mcast_groups) { release(s->mcast_groups); diff --git a/kernel/networking/transport_layer/net_ctrl.c b/kernel/networking/transport_layer/net_ctrl.c index c733726c..58eeac2b 100644 --- a/kernel/networking/transport_layer/net_ctrl.c +++ b/kernel/networking/transport_layer/net_ctrl.c @@ -8,6 +8,7 @@ #include "networking/internet_layer/ipv6_route.h" #include "networking/internet_layer/ipv6_utils.h" #include "networking/link_layer/arp.h" +#include "networking/link_layer/link_utils.h" #include "networking/link_layer/ndp.h" #include "networking/network.h" @@ -17,7 +18,7 @@ typedef struct { uint8_t prefix_len; uint8_t ifname_len; char ifname[16]; - uint8_t mac[6]; + uint8_t mac[MAC_ADDR_LEN]; uint16_t metric; uint16_t mtu; uint16_t flags; @@ -32,7 +33,7 @@ typedef struct { } net_ctrl_attrs_t; static bool net_ctrl_read_attrs(const uint8_t* p, uint32_t len, net_ctrl_attrs_t* out) { - if (!out)return false; + if (!out) return false; memset(out, 0, sizeof(*out)); uint32_t off = 0; while (off < len) { @@ -452,7 +453,7 @@ static bool net_ctrl_neigh_dump(const net_ctrl_attrs_t* a, buffer* b) { if (ae[j].static_entry) info.flags |= NET_CTRL_NEIGH_F_STATIC; info.address.ver = IP_VER4; memcpy(info.address.ip, &ae[j].ip, sizeof(ae[j].ip)); - memcpy(info.mac, ae[j].mac, 6); + mac_copy(info.mac, ae[j].mac); if (buffer_write_lim(b, (const char*)&info, sizeof(info)) != sizeof(info)) return false; } ndp_entry_t ne[NDP_TABLE_MAX]; @@ -471,7 +472,7 @@ static bool net_ctrl_neigh_dump(const net_ctrl_attrs_t* a, buffer* b) { if (ne[j].is_router) info.flags |= NET_CTRL_NEIGH_F_ROUTER; info.address.ver = IP_VER6; ipv6_cpy(info.address.ip, ne[j].ip); - memcpy(info.mac, ne[j].mac, 6); + mac_copy(info.mac, ne[j].mac); if (buffer_write_lim(b, (const char*)&info, sizeof(info)) != sizeof(info)) return false; } } diff --git a/kernel/networking/transport_layer/tcp/tcp_rx.c b/kernel/networking/transport_layer/tcp/tcp_rx.c index ed740328..8efdf971 100644 --- a/kernel/networking/transport_layer/tcp/tcp_rx.c +++ b/kernel/networking/transport_layer/tcp/tcp_rx.c @@ -1,6 +1,7 @@ #include "tcp_internal.h" #include "networking/transport_layer/socket_bind.h" #include "networking/transport_layer/csocket_tcp.h" +#include "networking/interface_manager.h" #include "networking/internet_layer/ipv4.h" #include "networking/internet_layer/ipv6.h" #include "networking/internet_layer/ipv4_utils.h" diff --git a/kernel/networking/transport_layer/tcp/tcp_utils.c b/kernel/networking/transport_layer/tcp/tcp_utils.c index 70fe2bca..dfbba1b5 100644 --- a/kernel/networking/transport_layer/tcp/tcp_utils.c +++ b/kernel/networking/transport_layer/tcp/tcp_utils.c @@ -1,6 +1,7 @@ #include "tcp_utils.h" #include "std/memory.h" #include "tcp_internal.h" +#include "networking/interface_manager.h" uint32_t tcp_calc_mss_for_l3(uint8_t l3_id, ip_version_t ver, const void *remote_ip){ uint32_t mtu = 1500; diff --git a/kernel/networking/transport_layer/udp.c b/kernel/networking/transport_layer/udp.c index 0ff1b250..c2f13220 100644 --- a/kernel/networking/transport_layer/udp.c +++ b/kernel/networking/transport_layer/udp.c @@ -1,5 +1,6 @@ #include "udp.h" #include "net/checksums.h" +#include "networking/interface_manager.h" #include "networking/internet_layer/ipv4.h" #include "networking/internet_layer/ipv6.h" #include "networking/transport_layer/socket_bind.h" diff --git a/kernel/tools/icmp_probe.c b/kernel/tools/icmp_probe.c new file mode 100644 index 00000000..4f232b34 --- /dev/null +++ b/kernel/tools/icmp_probe.c @@ -0,0 +1,237 @@ +#include "icmp_probe.h" +#include "net/checksums.h" +#include "std/memory.h" +#include "std/string.h" +#include "networking/internet_layer/icmp.h" +#include "networking/internet_layer/icmpv6.h" +#include "networking/internet_layer/ipv4.h" +#include "networking/internet_layer/ipv4_utils.h" +#include "networking/internet_layer/ipv6.h" +#include "networking/internet_layer/ipv6_utils.h" +#include "networking/transport_layer/csocket.h" +#include "syscalls/syscalls.h" + +#define ICMP_PROBE_PAYLOAD_LEN 32 + +typedef struct __attribute__((packed)) { + uint8_t type; + uint8_t code; + uint16_t checksum; + uint16_t id; + uint16_t seq; + uint8_t payload[ICMP_PROBE_PAYLOAD_LEN]; +} icmp_probe4_echo_t; + +typedef struct __attribute__((packed)) { + icmpv6_hdr_t hdr; + uint16_t id; + uint16_t seq; + uint8_t payload[ICMP_PROBE_PAYLOAD_LEN]; +} icmp_probe6_echo_t; + +bool icmp_probe_parse_bind(const char* arg, SockBindSpec* out){ + if (!arg || !out) return false; + + memset(out, 0, sizeof(*out)); + uint32_t id = 0; + if (strncmp(arg, "l2:", 3) == 0) { + if (!parse_uint32_dec_exact(arg + 3, &id) || id == 0 || id > UINT8_MAX) return false; + out->kind = BIND_L2; + out->ifindex = (uint8_t)id; + return true; + } + + if (strncmp(arg, "l3:", 3) == 0) { + if (!parse_uint32_dec_exact(arg + 3, &id) || id == 0 || id > UINT8_MAX) return false; + out->kind = BIND_L3; + out->l3_id = (uint8_t)id; + return true; + } + + uint32_t v4 = 0; + if (ipv4_parse(arg, &v4)) { + out->kind = BIND_IP; + out->ver = IP_VER4; + memcpy(out->ip, &v4, sizeof(v4)); + return true; + } + + if (ipv6_parse(arg, out->ip)) { + out->kind = BIND_IP; + out->ver = IP_VER6; + return true; + } + + return false; +} + +uint32_t icmp_probe_collect(const net_l4_endpoint* dst, uint16_t id, uint16_t seq, uint32_t timeout_ms, const SockBindSpec* bind, uint8_t ttl, icmp_probe_result_t* out, uint32_t max_results){ + if (!dst || !out || !max_results) return 0; + if (dst->ver != IP_VER4 && dst->ver != IP_VER6) return 0; + + SocketOptions opt; + memset(&opt, 0, sizeof(opt)); + opt.special_kind = SOCKET_SPECIAL_RAW; + opt.flags = SOCK_OPT_RAW_FILTER; + opt.raw_filter.count = 5; + if (dst->ver == IP_VER4) { + opt.raw_filter.rules[0].type = ICMP_ECHO_REPLY; + opt.raw_filter.rules[0].code = 0; + opt.raw_filter.rules[0].has_code = 1; + opt.raw_filter.rules[1].type = ICMP_DEST_UNREACH; + opt.raw_filter.rules[2].type = ICMP_TIME_EXCEEDED; + opt.raw_filter.rules[3].type = ICMP_PARAM_PROBLEM; + opt.raw_filter.rules[4].type = ICMP_REDIRECT; + } else { + opt.raw_filter.rules[0].type = ICMPV6_ECHO_REPLY; + opt.raw_filter.rules[0].code = 0; + opt.raw_filter.rules[0].has_code = 1; + opt.raw_filter.rules[1].type = ICMPV6_DEST_UNREACH; + opt.raw_filter.rules[2].type = ICMPV6_PACKET_TOO_BIG; + opt.raw_filter.rules[3].type = ICMPV6_TIME_EXCEEDED; + opt.raw_filter.rules[4].type = ICMPV6_PARAM_PROBLEM; + } + if (ttl) { + opt.flags |= SOCK_OPT_TTL; + opt.ttl = ttl; + } + + protocol_t proto = dst->ver == IP_VER4 ? PROTO_ICMP : PROTO_ICMPV6; + socket_handle_t sock = create_socket(proto, &opt); + if (!sock) return 0; + + if (bind) { + SockBindSpec spec = *bind; + if (spec.kind == BIND_L3) spec.ver = dst->ver; + if (bind_socket(sock, &spec, 0) != SOCK_OK) { + close_socket(sock); + return 0; + } + } + + int64_t sent = 0; + uint32_t tx_len = 0; + if (dst->ver == IP_VER4) { + icmp_probe4_echo_t echo; + memset(&echo, 0, sizeof(echo)); + echo.type = ICMP_ECHO_REQUEST; + echo.id = bswap16(id); + echo.seq = bswap16(seq); + echo.checksum = bswap16(checksum16(&echo, sizeof(echo))); + tx_len = (uint32_t)sizeof(echo); + sent = send_to_socket(sock, dst, &echo, sizeof(echo)); + } else { + icmp_probe6_echo_t echo; + memset(&echo, 0, sizeof(echo)); + echo.hdr.type = ICMPV6_ECHO_REQUEST; + echo.id = bswap16(id); + echo.seq = bswap16(seq); + tx_len = (uint32_t)sizeof(echo); + sent = send_to_socket(sock, dst, &echo, sizeof(echo)); + } + + if (sent != (int64_t)tx_len) { + close_socket(sock); + return 0; + } + + uint32_t count = 0; + uint32_t start = (uint32_t)get_time(); + while (count < max_results) { + uint32_t now = (uint32_t)get_time(); + if (now - start >= timeout_ms) break; + + uint8_t rx[1280]; + net_l4_endpoint src; + memset(&src, 0, sizeof(src)); + int64_t n = receive_from_socket(sock, rx, sizeof(rx), &src); + if (n == SOCK_ERR_WOULDBLOCK) { + msleep(5); + continue; + } + if (n < 8) { + if (n < 0) msleep(5); + continue; + } + + uint8_t type = rx[0]; + uint8_t code = rx[1]; + uint8_t status = ICMP_PROBE_UNKNOWN_ERROR; + bool matched = false; + + if (dst->ver == IP_VER4) { + if (type == ICMP_ECHO_REPLY && rd_be16(rx + 4) == id && rd_be16(rx + 6) == seq) matched = true; + else if ((type == ICMP_TIME_EXCEEDED || type == ICMP_DEST_UNREACH || type == ICMP_PARAM_PROBLEM || type == ICMP_REDIRECT) && (uint32_t)n >= 8 + sizeof(ipv4_hdr_t) + 8) { + const uint8_t* inner_ip = rx + 8; + uint8_t ihl = (uint8_t)(inner_ip[0] & 0x0F); + uint32_t iphdr = (uint32_t)ihl * 4; + if ((inner_ip[0] >> 4) == IP_VER4 && ihl >= IP_IHL_NOOPTS && (uint32_t)n >= 8 + iphdr + 8 && inner_ip[9] == PROTO_ICMP) { + const uint8_t* inner_icmp = inner_ip + iphdr; + matched = (inner_icmp[0] == ICMP_ECHO_REQUEST || inner_icmp[0] == ICMP_ECHO_REPLY) && rd_be16(inner_icmp + 4) == id && rd_be16(inner_icmp + 6) == seq; + } + } + if (!matched) continue; + + switch (type) { + case ICMP_ECHO_REPLY: status = ICMP_PROBE_OK; break; + case ICMP_DEST_UNREACH: + switch (code) { + case 0: status = ICMP_PROBE_NET_UNREACH; break; + case 1: status = ICMP_PROBE_HOST_UNREACH; break; + case 2: status = ICMP_PROBE_PROTO_UNREACH; break; + case 3: status = ICMP_PROBE_PORT_UNREACH; break; + case 4: status = ICMP_PROBE_FRAG_NEEDED; break; + case 5: status = ICMP_PROBE_SRC_ROUTE_FAILED; break; + case 13: status = ICMP_PROBE_ADMIN_PROHIBITED; break; + default: status = ICMP_PROBE_UNKNOWN_ERROR; break; + } + break; + case ICMP_TIME_EXCEEDED: status = ICMP_PROBE_TTL_EXPIRED; break; + case ICMP_PARAM_PROBLEM: status = ICMP_PROBE_PARAM_PROBLEM; break; + case ICMP_REDIRECT: status = ICMP_PROBE_REDIRECT; break; + default: break; + } + } else { + if (type == ICMPV6_ECHO_REPLY && rd_be16(rx + 4) == id && rd_be16(rx + 6) == seq) matched = true; + else if ((type == ICMPV6_DEST_UNREACH || type == ICMPV6_PACKET_TOO_BIG || type == ICMPV6_TIME_EXCEEDED || type == ICMPV6_PARAM_PROBLEM) && (uint32_t)n >= 8 + sizeof(ipv6_hdr_t) + 8) { + ipv6_hdr_t inner; + memcpy(&inner, rx + 8, sizeof(inner)); + uint32_t v = bswap32(inner.ver_tc_fl); + const uint8_t* inner_icmp = rx + 8 + sizeof(ipv6_hdr_t); + matched = (v >> 28) == IP_VER6 && inner.next_header == PROTO_ICMPV6 && inner_icmp[0] == ICMPV6_ECHO_REQUEST && rd_be16(inner_icmp + 4) == id && rd_be16(inner_icmp + 6) == seq; + } + if (!matched) continue; + + switch (type) { //b + case ICMPV6_ECHO_REPLY: status = ICMP_PROBE_OK; break; + case ICMPV6_DEST_UNREACH: + switch (code) { + case 0:status = ICMP_PROBE_NET_UNREACH; break; + case 1: + case 2:status = ICMP_PROBE_ADMIN_PROHIBITED; break; + case 3:status = ICMP_PROBE_HOST_UNREACH; break; + case 4:status = ICMP_PROBE_PORT_UNREACH; break; + default: status = ICMP_PROBE_UNKNOWN_ERROR; break; + } + break; + case ICMPV6_PACKET_TOO_BIG: status = ICMP_PROBE_FRAG_NEEDED; break; + case ICMPV6_TIME_EXCEEDED: status = ICMP_PROBE_TTL_EXPIRED; break; + case ICMPV6_PARAM_PROBLEM: status = ICMP_PROBE_PARAM_PROBLEM; break; + default: break; + } + } + + icmp_probe_result_t* r = &out[count++]; + memset(r, 0, sizeof(*r)); + r->responder = src; + r->icmp_type = type; + r->icmp_code = code; + r->status = status; + now = (uint32_t)get_time(); + r->rtt_ms = now >= start ? now - start : 0; + if (max_results == 1) break; + } + + close_socket(sock); + return count; +} diff --git a/kernel/tools/icmp_probe.h b/kernel/tools/icmp_probe.h new file mode 100644 index 00000000..3a3600f7 --- /dev/null +++ b/kernel/tools/icmp_probe.h @@ -0,0 +1,41 @@ +#pragma once + +#include "types.h" +#include "net/network_types.h" +#include "net/socket_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + ICMP_PROBE_OK = 0, + ICMP_PROBE_TIMEOUT = 1, + ICMP_PROBE_NET_UNREACH = 2, + ICMP_PROBE_HOST_UNREACH = 3, + ICMP_PROBE_PROTO_UNREACH = 4, + ICMP_PROBE_PORT_UNREACH = 5, + ICMP_PROBE_FRAG_NEEDED = 6, + ICMP_PROBE_SRC_ROUTE_FAILED = 7, + ICMP_PROBE_ADMIN_PROHIBITED = 8, + ICMP_PROBE_TTL_EXPIRED = 9, + ICMP_PROBE_PARAM_PROBLEM = 10, + ICMP_PROBE_REDIRECT = 11, + ICMP_PROBE_UNKNOWN_ERROR = 255 +} icmp_probe_status_t; + +typedef struct { + uint32_t rtt_ms; + net_l4_endpoint responder; + uint8_t status; + uint8_t icmp_type; + uint8_t icmp_code; + uint8_t reserved; +} icmp_probe_result_t; + +bool icmp_probe_parse_bind(const char* arg, SockBindSpec* out); +uint32_t icmp_probe_collect(const net_l4_endpoint* dst, uint16_t id, uint16_t seq, uint32_t timeout_ms, const SockBindSpec* bind, uint8_t ttl, icmp_probe_result_t* out, uint32_t max_results); + +#ifdef __cplusplus +} +#endif diff --git a/kernel/tools/ping.c b/kernel/tools/ping.c index 38f138a5..c6365af5 100644 --- a/kernel/tools/ping.c +++ b/kernel/tools/ping.c @@ -1,28 +1,22 @@ #include "ping.h" -#include "networking/internet_layer/icmp.h" -#include "net/network_types.h" +#include "icmp_probe.h" #include "std/string.h" #include "std/memory.h" #include "types.h" #include "console/kio.h" -#include "filesystem/filesystem.h" #include "process/scheduler.h" #include "syscalls/syscalls.h" -#include "networking/internet_layer/ipv4.h" -#include "networking/internet_layer/ipv4_route.h" #include "networking/application_layer/dns/dns.h" #include "networking/internet_layer/ipv4_utils.h" #include "networking/internet_layer/ipv6_utils.h" -#include "networking/internet_layer/icmpv6.h" -#include "networking/interface_manager.h" #include "networking/transport_layer/trans_utils.h" #define PING_MAX_REPLIES 16 typedef struct { const char *host; - net_l4_endpoint src_ip; - ip_tx_opts_t src_if; + SockBindSpec bind; + bool bind_set; } ping_addressing_t; typedef struct { @@ -60,26 +54,10 @@ static bool parse_args(int argc, char *argv[], ping_opts_t *o) { if (++i >= argc) return false; if (!parse_uint32_dec(argv[i], &o->ttl)) return false; } else if (strcmp_case(a, "-s",true) == 0) { - if (++i >= argc) return false; - if (o->addr.src_ip.ver || o->addr.src_if.scope != IP_TX_AUTO) return false; - uint32_t id = 0; - if (strncmp(argv[i], "l2:", 3) == 0) { - if (!parse_uint32_dec_exact(argv[i] + 3, &id) || id == 0 || id > UINT8_MAX) return false; - o->addr.src_if.index = (uint8_t)id; - o->addr.src_if.scope = IP_TX_BOUND_L2; - } else if (strncmp(argv[i], "l3:", 3) == 0) { - if (!parse_uint32_dec_exact(argv[i] + 3, &id) || id == 0 || id > UINT8_MAX) return false; - o->addr.src_if.index = (uint8_t)id; - o->addr.src_if.scope = IP_TX_BOUND_L3; - } else { - uint32_t src4 = 0; - uint8_t src6[16]; - if (ipv4_parse(argv[i], &src4)) make_ep(&src4, 0, IP_VER4, &o->addr.src_ip); - else if (ipv6_parse(argv[i], src6)) make_ep(src6, 0, IP_VER6, &o->addr.src_ip); - else return false; - } - } - else return false; + if (++i >= argc || o->addr.bind_set) return false; + if (!icmp_probe_parse_bind(argv[i], &o->addr.bind)) return false; + o->addr.bind_set = true; + } else return false; } else { if (o->addr.host) return false; o->addr.host = a; @@ -92,26 +70,26 @@ static bool parse_args(int argc, char *argv[], ping_opts_t *o) { static const char *status_to_msg(uint8_t st) { switch (st) { - case PING_TIMEOUT: return "Request timed out."; - case PING_NET_UNREACH: return "Destination Net Unreachable."; - case PING_HOST_UNREACH: return "Destination Host Unreachable."; - case PING_PROTO_UNREACH: return "Protocol Unreachable."; - case PING_PORT_UNREACH: return "Port Unreachable."; - case PING_FRAG_NEEDED: return "Fragmentation Needed."; - case PING_SRC_ROUTE_FAILED: return "Source Route Failed."; - case PING_ADMIN_PROHIBITED: return "Administratively Prohibited."; - case PING_TTL_EXPIRED: return "Time To Live exceeded."; - case PING_PARAM_PROBLEM: return "Parameter Problem."; - case PING_REDIRECT: return "Redirect received."; - default: return "No reply (unknown error)."; + case ICMP_PROBE_TIMEOUT: return "Request timed out."; + case ICMP_PROBE_NET_UNREACH: return "Destination Net Unreachable."; + case ICMP_PROBE_HOST_UNREACH: return "Destination Host Unreachable."; + case ICMP_PROBE_PROTO_UNREACH: return "Protocol Unreachable."; + case ICMP_PROBE_PORT_UNREACH: return "Port Unreachable."; + case ICMP_PROBE_FRAG_NEEDED: return "Fragmentation Needed."; + case ICMP_PROBE_SRC_ROUTE_FAILED: return "Source Route Failed."; + case ICMP_PROBE_ADMIN_PROHIBITED: return "Administratively Prohibited."; + case ICMP_PROBE_TTL_EXPIRED: return "Time To Live exceeded."; + case ICMP_PROBE_PARAM_PROBLEM: return "Parameter Problem."; + case ICMP_PROBE_REDIRECT: return "Redirect received."; + default: return "No reply (unknown error)."; } } static int ping_v4(const ping_opts_t *o) { const char *host = o->addr.host; - uint32_t dst_ip_be = 0; - bool is_lit = ipv4_parse(host, &dst_ip_be); + uint32_t dst_ip = 0; + bool is_lit = ipv4_parse(host, &dst_ip); if (!is_lit) { uint32_t r = 0; dns_result_t dr = dns_resolve_a(host, &r, DNS_USE_BOTH, o->timeout_ms); @@ -119,81 +97,48 @@ static int ping_v4(const ping_opts_t *o) { print("ping: dns lookup failed (%d) for '%s'", (int)dr, host); return 2; } - dst_ip_be = r; + dst_ip = r; } - char ipstr[16]; - ipv4_to_string(dst_ip_be, ipstr); + if (ipv4_is_limited_broadcast(dst_ip) && (!o->addr.bind_set || o->addr.bind.kind == BIND_L2)) { + print("ping: limited broadcast requires -s local_ipv4 or l3:id"); + return 2; + } + char ipstr[16]; + ipv4_to_string(dst_ip, ipstr); print("PING %s (%s) with 32 bytes of data:", host, ipstr); uint32_t sent = 0, received = 0, min_ms = UINT32_MAX, max_ms = 0; uint64_t sum_ms = 0; uint16_t id = (uint16_t)(get_current_proc_pid() & 0xFFFF); uint16_t seq_base = (uint16_t)(get_time() & 0xFFFF); - - ip_tx_opts_t txo = {0}; - const ip_tx_opts_t *txop = NULL; - if (o->addr.src_ip.ver) { - uint32_t src4 = 0; - memcpy(&src4, o->addr.src_ip.ip, 4); - l3_ipv4_interface_t *l3 = l3_ipv4_find_by_ip(src4); - txo.index = l3->l3_id; - txo.scope = IP_TX_BOUND_L3; - txop = &txo; - } else if (o->addr.src_if.scope != IP_TX_AUTO) txop = &o->addr.src_if; - else if (ipv4_is_limited_broadcast(dst_ip_be)) { - l3_ipv4_interface_t *chosen = NULL; - uint8_t cnt = l2_interface_count(); - for (uint8_t li = 0; li < cnt; li++) { - l2_interface_t *l2 = l2_interface_at(li); - if (!l2 || !l2->is_up) continue; - for (int vi = 0; vi < MAX_IPV4_PER_INTERFACE; vi++) { - l3_ipv4_interface_t *v4 = l2->l3_v4[vi]; - if (!ipv4_l3_is_ready(v4) || v4->is_localhost) continue; - if (chosen) { - print("ping: broadcast requires -s when more IPv4 interfaces are usable"); - return 2; - } - chosen = v4; - } - } - if (!chosen) { - print("ping: no usable IPv4 interface"); - return 2; - } - txo.index = (uint8_t)chosen->l3_id; - txo.scope = IP_TX_BOUND_L3; - txop = &txo; - } - - bool multi = ipv4_is_multicast(dst_ip_be) || ipv4_is_limited_broadcast(dst_ip_be); + const SockBindSpec* bind = o->addr.bind_set ? &o->addr.bind : NULL; + bool multi = ipv4_is_multicast(dst_ip) || ipv4_is_limited_broadcast(dst_ip); uint32_t max_results = multi ? PING_MAX_REPLIES : 1; + net_l4_endpoint dst; + make_ep(&dst_ip, 0, IP_VER4, &dst); + for (uint32_t i = 0; i < o->count; i++) { - ++sent; + sent++; uint16_t seq = (uint16_t)(seq_base + i); - - ping_result_t res[PING_MAX_REPLIES]; - uint32_t n = icmp_ping_collect(dst_ip_be, id, seq, o->timeout_ms, txop, (uint8_t)o->ttl, res, max_results); + icmp_probe_result_t res[PING_MAX_REPLIES]; + uint32_t n = icmp_probe_collect(&dst, id, seq, o->timeout_ms, bind, (uint8_t)o->ttl, res, max_results); if (n) { for (uint32_t j = 0; j < n; j++) { - if (res[j].status == PING_OK) { + if (res[j].status == ICMP_PROBE_OK) { received++; uint32_t rtt = res[j].rtt_ms; if (rtt < min_ms) min_ms = rtt; if (rtt > max_ms) max_ms = rtt; sum_ms += rtt; - char rip[16]; - ipv4_to_string(res[j].responder_ip, rip); + char rip[64]; + net_ep_split(&res[j].responder, rip, (int)sizeof(rip), NULL, NULL); print("Reply from %s: bytes=32 time=%ums", rip, rtt); - } else { - print("%s", status_to_msg(res[j].status)); - } + } else print("%s", status_to_msg(res[j].status)); } - } else { - print("%s", status_to_msg(PING_TIMEOUT)); - } + } else print("%s", status_to_msg(ICMP_PROBE_TIMEOUT)); if (i + 1 < o->count) msleep(o->interval_ms); } @@ -212,24 +157,28 @@ static int ping_v4(const ping_opts_t *o) { print("rtt min/avg/max = %u/%u/%u ms", min_ms, avg, max_ms); } - return (received > 0) ? 0 : 1; + return received > 0 ? 0 : 1; } static int ping_v6(const ping_opts_t *o) { const char *host = o->addr.host; - - uint8_t dst6[16] ={0}; - bool is_lit = ipv6_parse(host, dst6); + uint8_t dst_ip[16] ={0}; + bool is_lit = ipv6_parse(host, dst_ip); if (!is_lit) { - dns_result_t dr = dns_resolve_aaaa(host, dst6, DNS_USE_BOTH, o->timeout_ms); + dns_result_t dr = dns_resolve_aaaa(host, dst_ip, DNS_USE_BOTH, o->timeout_ms); if (dr != DNS_OK) { print("ping: dns lookup failed (%d) for '%s'",(int)dr, host); return 2; } } + if (ipv6_is_linkscope_mcast(dst_ip) && !o->addr.bind_set) { + print("ping: IPv6 link-local multicast requires -s local_ipv6, l2:id or l3:id"); + return 2; + } + char ipstr[64]; - ipv6_to_string(dst6, ipstr, (int)sizeof(ipstr)); + ipv6_to_string(dst_ip, ipstr, (int)sizeof(ipstr)); print("PING %s (%s) with 32 bytes of data:", host, ipstr); @@ -237,63 +186,31 @@ static int ping_v6(const ping_opts_t *o) { uint64_t sum_ms = 0; uint16_t id = (uint16_t)(get_current_proc_pid() & 0xFFFF); uint16_t seq_base = (uint16_t)(get_time() & 0xFFFF); + const SockBindSpec* bind = o->addr.bind_set ? &o->addr.bind : NULL; + uint32_t max_results = ipv6_is_multicast(dst_ip) ? PING_MAX_REPLIES : 1; + net_l4_endpoint dst; + make_ep(dst_ip, 0, IP_VER6, &dst); - ip_tx_opts_t txo = {0}; - const ip_tx_opts_t *txop = NULL; - if (o->addr.src_ip.ver) { - l3_ipv6_interface_t *l3 = l3_ipv6_find_by_ip(o->addr.src_ip.ip); - txo.index = l3->l3_id; - txo.scope = IP_TX_BOUND_L3; - txop = &txo; - } else if (o->addr.src_if.scope != IP_TX_AUTO) txop = &o->addr.src_if; - else if (ipv6_is_linkscope_mcast(dst6)) { - l3_ipv6_interface_t *chosen = NULL; - uint8_t cnt = l2_interface_count(); - for (uint8_t li = 0; li < cnt; li++) { - l2_interface_t *l2 = l2_interface_at(li); - if (!l2 || !l2->is_up) continue; - for (int vi = 0; vi < MAX_IPV6_PER_INTERFACE; vi++) { - l3_ipv6_interface_t *v6 = l2->l3_v6[vi]; - if (!ipv6_l3_is_ready(v6) || v6->is_localhost || !ipv6_is_linklocal(v6->ip)) continue; - if (chosen) { - print("ping: IPv6 link local multicast requires -s when more IPv6 interfaces are usable"); - return 2; - } - chosen = v6; - } - } - if (!chosen) { - print("ping: no usable IPv6 interface"); - return 2; - } - txo.index = (uint8_t)chosen->l3_id; - txo.scope = IP_TX_BOUND_L3; - txop = &txo; - } - - bool multi = ipv6_is_multicast(dst6); - uint32_t max_results = multi ? PING_MAX_REPLIES : 1; for (uint32_t i = 0; i < o->count; i++) { - ++sent; + sent++; uint16_t seq = (uint16_t)(seq_base + i); - - ping6_result_t res[PING_MAX_REPLIES]; - uint32_t n = icmpv6_ping_collect(dst6, id, seq, o->timeout_ms, txop, (uint8_t)o->ttl, res, max_results); + icmp_probe_result_t res[PING_MAX_REPLIES]; + uint32_t n = icmp_probe_collect(&dst, id, seq, o->timeout_ms, bind, (uint8_t)o->ttl, res, max_results); if (n) { for (uint32_t j = 0; j < n; j++) { - if (res[j].status == PING_OK) { - ++received; + if (res[j].status == ICMP_PROBE_OK) { + received++; uint32_t rtt = res[j].rtt_ms; if (rtt < min_ms) min_ms = rtt; if (rtt > max_ms) max_ms = rtt; sum_ms += rtt; char rip[64]; - ipv6_to_string(res[j].responder_ip, rip, (int)sizeof(rip)); + net_ep_split(&res[j].responder, rip, (int)sizeof(rip), NULL, NULL); print("Reply from %s: bytes=32 time=%ums", rip, rtt); } else print("%s", status_to_msg(res[j].status)); } - } else print("%s", status_to_msg(PING_TIMEOUT)); + } else print("%s", status_to_msg(ICMP_PROBE_TIMEOUT)); if (i + 1 < o->count) msleep(o->interval_ms); } @@ -323,72 +240,11 @@ int run_ping(int argc, char *argv[]) { return 2; } - if (opts.addr.src_ip.ver && opts.addr.src_ip.ver != opts.ver) { + if (opts.addr.bind_set && opts.addr.bind.kind == BIND_IP && opts.addr.bind.ver != opts.ver) { print("ping: source address version doesn't match target version"); return 2; } - if (opts.ver == IP_VER4 && opts.addr.src_ip.ver) { - uint32_t src4 = 0; - memcpy(&src4, opts.addr.src_ip.ip, 4); - if (!l3_ipv4_find_by_ip(src4)) { - char ssrc[16]; - ipv4_to_string(src4, ssrc); - print("ping: invalid source %s (no local ip match)", ssrc); - return 2; - } - } - - if (opts.ver == IP_VER6 && opts.addr.src_ip.ver && !l3_ipv6_find_by_ip(opts.addr.src_ip.ip)) { - char ssrc[64]; - ipv6_to_string(opts.addr.src_ip.ip, ssrc, (int)sizeof(ssrc)); - print("ping: invalid source %s (no local ip match)", ssrc); - return 2; - } - - if (opts.ver == IP_VER4 && opts.addr.src_if.scope == IP_TX_BOUND_L3) { - if (!ipv4_l3_is_ready(l3_ipv4_find_by_id(opts.addr.src_if.index))) { - print("ping: invalid IPv4 L3 interface %u", opts.addr.src_if.index); - return 2; - } - } - - if (opts.ver == IP_VER6 && opts.addr.src_if.scope == IP_TX_BOUND_L3) { - if (!ipv6_l3_is_ready(l3_ipv6_find_by_id(opts.addr.src_if.index))) { - print("ping: invalid IPv6 L3 interface %u", opts.addr.src_if.index); - return 2; - } - } - - if (opts.addr.src_if.scope == IP_TX_BOUND_L2) { - l2_interface_t *l2 = l2_interface_find_by_index(opts.addr.src_if.index); - if (!l2 || !l2->is_up) { - print("ping: invalid L2 interface %u", opts.addr.src_if.index); - return 2; - } - - bool has_l3 = false; - if (opts.ver == IP_VER4) { - for (int i = 0; i < MAX_IPV4_PER_INTERFACE; i++) { - if (ipv4_l3_is_ready(l2->l3_v4[i])) { - has_l3 = true; - break; - } - } - } else { - for (int i = 0; i < MAX_IPV6_PER_INTERFACE; i++) { - if (ipv6_l3_is_ready(l2->l3_v6[i])) { - has_l3 = true; - break; - } - } - } - if (!has_l3) { - print("ping: L2 interface %u has no usable %s L3 interface", opts.addr.src_if.index, opts.ver == IP_VER4 ? "IPv4" : "IPv6"); - return 2; - } - } - if (opts.ver == IP_VER4) return ping_v4(&opts); if (opts.ver == IP_VER6) return ping_v6(&opts); print("usage: ping [-4/-6] [-n times] [-w timeout] [-i interval] [-t TTL] [-s ip|l2:id|l3:id] host"); diff --git a/kernel/tools/tracert.c b/kernel/tools/tracert.c index bc2e8ebe..898fd1ea 100644 --- a/kernel/tools/tracert.c +++ b/kernel/tools/tracert.c @@ -1,19 +1,15 @@ #include "tracert.h" -#include "networking/internet_layer/icmp.h" -#include "net/network_types.h" +#include "icmp_probe.h" #include "std/string.h" #include "std/memory.h" #include "types.h" #include "console/kio.h" -#include "filesystem/filesystem.h" #include "process/scheduler.h" #include "syscalls/syscalls.h" -#include "networking/internet_layer/ipv4.h" -#include "networking/internet_layer/ipv4_route.h" #include "networking/application_layer/dns/dns.h" #include "networking/internet_layer/ipv4_utils.h" #include "networking/internet_layer/ipv6_utils.h" -#include "networking/internet_layer/icmpv6.h" +#include "networking/transport_layer/trans_utils.h" typedef struct { ip_version_t ver; @@ -21,31 +17,29 @@ typedef struct { uint32_t count; uint32_t timeout_ms; uint32_t interval_ms; - uint32_t src_ip; uint32_t timeout_streak_limit; - bool src_set; + SockBindSpec bind; + bool bind_set; const char *host; } tr_opts_t; -//TODO raw socket + static bool parse_args(int argc, char *argv[], tr_opts_t *o) { o->ver = IP_VER4; o->max_ttl = 30; o->count = 3; o->timeout_ms = 1000; o->interval_ms = 250; - o->src_ip = 0; o->timeout_streak_limit = 5; - o->src_set = false; + o->bind_set = false; o->host = NULL; + memset(&o->bind, 0, sizeof(o->bind)); for (int i = 1; i < argc; i++) { const char *a = argv[i]; if (a && a[0] == '-') { - if (strcmp_case(a, "-4",true) == 0) { - o->ver = IP_VER4; - } else if (strcmp_case(a, "-6",true) == 0) { - o->ver = IP_VER6; - } else if (strcmp_case(a, "-m",true) == 0) { + if (strcmp_case(a, "-4",true) == 0) o->ver = IP_VER4; + else if (strcmp_case(a, "-6",true) == 0) o->ver = IP_VER6; + else if (strcmp_case(a, "-m",true) == 0) { if (++i >= argc) return false; if (!parse_uint32_dec(argv[i], &o->max_ttl) || o->max_ttl == 0) return false; } else if (strcmp_case(a, "-n",true) == 0) { @@ -61,14 +55,10 @@ static bool parse_args(int argc, char *argv[], tr_opts_t *o) { if (++i >= argc) return false; if (!parse_uint32_dec(argv[i], &o->timeout_streak_limit) || o->timeout_streak_limit == 0) return false; } else if (strcmp_case(a, "-s",true) == 0) { - if (++i >= argc) return false; - uint32_t src = 0; - if (!ipv4_parse(argv[i], &src)) return false; - o->src_ip = src; - o->src_set = true; - } else { - return false; - } + if (++i >= argc || o->bind_set) return false; + if (!icmp_probe_parse_bind(argv[i], &o->bind)) return false; + o->bind_set = true; + } else return false; } else { if (o->host) return false; o->host = a; @@ -86,8 +76,8 @@ static bool parse_args(int argc, char *argv[], tr_opts_t *o) { } static int tracert_v4(const tr_opts_t *o) { - uint32_t dst = 0; - bool lit = ipv4_parse(o->host, &dst); + uint32_t dst_ip = 0; + bool lit = ipv4_parse(o->host, &dst_ip); if (!lit) { uint32_t r = 0; dns_result_t dr = dns_resolve_a(o->host, &r, DNS_USE_BOTH, o->timeout_ms); @@ -95,56 +85,43 @@ static int tracert_v4(const tr_opts_t *o) { print("tracert: dns lookup failed (%d) for '%s'", (int)dr, o->host); return 2; } - dst = r; + dst_ip = r; } char dip[16]; char line[256]; - ipv4_to_string(dst, dip); + ipv4_to_string(dst_ip, dip); print("Tracing route to %s [%s]", o->host, dip); size_t len = string_format_buf(line, sizeof(line), "hop "); for (uint32_t p = 0; p < o->count && len < sizeof(line); p++) len += string_format_buf(line + len, sizeof(line) - len, "rtt%u ", p + 1); string_format_buf(line + len, sizeof(line) - len, "address"); print("%s", line); - ip_tx_opts_t txo = (ip_tx_opts_t){0}; - const ip_tx_opts_t *txop = NULL; - if (o->src_set) { - l3_ipv4_interface_t *l3 = l3_ipv4_find_by_ip(o->src_ip); - if (!l3) { - char ssrc[16]; - ipv4_to_string(o->src_ip, ssrc); - print("tracert: invalid source %s (no local ip match)", ssrc); - return 2; - } - txo.index = l3->l3_id; - txo.scope = IP_TX_BOUND_L3; - txop = &txo; - } - uint16_t id = (uint16_t)(get_current_proc_pid() & 0xFFFF); uint16_t seq0 = (uint16_t)(get_time() & 0xFFFF); uint32_t dead_streak = 0; + const SockBindSpec* bind = o->bind_set ? &o->bind : NULL; + net_l4_endpoint dst; + make_ep(&dst_ip, 0, IP_VER4, &dst); for (uint32_t ttl = 1; ttl <= o->max_ttl; ttl++) { len = string_format_buf(line, sizeof(line), "%2u ", ttl); uint32_t hop_ip = 0; bool any = false; + bool reached = false; for (uint32_t p = 0; p < o->count && len < sizeof(line); p++) { uint16_t seq = (uint16_t)(seq0 + (ttl << 6) + p); - ping_result_t r = (ping_result_t){0}; - bool ok = icmp_ping(dst, id, seq, o->timeout_ms, txop, ttl, &r); - if (r.responder_ip && hop_ip == 0) hop_ip = r.responder_ip; + icmp_probe_result_t r; + memset(&r, 0, sizeof(r)); + bool answered = icmp_probe_collect(&dst, id, seq, o->timeout_ms, bind, (uint8_t)ttl, &r, 1) && r.status != ICMP_PROBE_UNKNOWN_ERROR; + if (r.responder.ver == IP_VER4 && hop_ip == 0) memcpy(&hop_ip, r.responder.ip, sizeof(hop_ip)); - if (ok || r.status == PING_TTL_EXPIRED || r.status == PING_REDIRECT || r.status == PING_PARAM_PROBLEM || - r.status == PING_NET_UNREACH || r.status == PING_HOST_UNREACH || r.status == PING_ADMIN_PROHIBITED || - r.status == PING_FRAG_NEEDED || r.status == PING_SRC_ROUTE_FAILED) { + if (answered) { any = true; + reached |= r.status == ICMP_PROBE_OK; len += string_format_buf(line + len, sizeof(line) - len, "%ums ", r.rtt_ms); - } else { - len += string_format_buf(line + len, sizeof(line) - len, "* "); - } + } else len += string_format_buf(line + len, sizeof(line) - len, "* "); if (p + 1 < o->count) msleep(o->interval_ms); } @@ -155,16 +132,14 @@ static int tracert_v4(const tr_opts_t *o) { char hip[16]; ipv4_to_string(hop_ip, hip); string_format_buf(line + len, sizeof(line) - len, "%s", hip); - } else { - string_format_buf(line + len, sizeof(line) - len, "???"); - } + } else string_format_buf(line + len, sizeof(line) - len, "???"); } else { dead_streak++; string_format_buf(line + len, sizeof(line) - len, "Request timed out."); } print("%s", line); - if (hop_ip == dst) break; + if (reached || hop_ip == dst_ip) break; if (dead_streak >= o->timeout_streak_limit) { print("stopping after %u consecutive timeout hops", dead_streak); break; @@ -175,19 +150,24 @@ static int tracert_v4(const tr_opts_t *o) { } static int tracert_v6(const tr_opts_t *o) { - uint8_t dst[16] = {0}; - bool lit = ipv6_parse(o->host, dst); + uint8_t dst_ip[16] = {0}; + bool lit = ipv6_parse(o->host, dst_ip); if (!lit) { - dns_result_t dr = dns_resolve_aaaa(o->host, dst, DNS_USE_BOTH, o->timeout_ms); + dns_result_t dr = dns_resolve_aaaa(o->host, dst_ip, DNS_USE_BOTH, o->timeout_ms); if (dr != DNS_OK) { print("tracert: dns lookup failed (%d) for '%s'", (int)dr, o->host); return 2; } } + if (ipv6_is_linkscope_mcast(dst_ip) && !o->bind_set) { + print("tracert: IPv6 link-local multicast requires -s ipv6, l2:id or l3:id"); + return 2; + } + char dip[64]; char line[256]; - ipv6_to_string(dst, dip, (int)sizeof(dip)); + ipv6_to_string(dst_ip, dip, (int)sizeof(dip)); print("Tracing route to %s [%s]", o->host, dip); size_t len = string_format_buf(line, sizeof(line), "hop "); for (uint32_t p = 0; p < o->count && len < sizeof(line); p++) len += string_format_buf(line + len, sizeof(line) - len, "rtt%u ", p + 1); @@ -197,27 +177,28 @@ static int tracert_v6(const tr_opts_t *o) { uint16_t id = (uint16_t)(get_current_proc_pid() & 0xFFFF); uint16_t seq0 = (uint16_t)(get_time() & 0xFFFF); uint32_t dead_streak = 0; + const SockBindSpec* bind = o->bind_set ? &o->bind : NULL; + net_l4_endpoint dst; + make_ep(dst_ip, 0, IP_VER6, &dst); for (uint32_t hl = 1; hl <= o->max_ttl; hl++) { len = string_format_buf(line, sizeof(line), "%2u ", hl); uint8_t hop_ip[16] = {0}; bool any = false; + bool reached = false; for (uint32_t p = 0; p < o->count && len < sizeof(line); p++) { uint16_t seq = (uint16_t)(seq0 + (hl << 6) + p); - ping6_result_t r = (ping6_result_t){0}; - bool ok = icmpv6_ping(dst, id, seq, o->timeout_ms, NULL, (uint8_t)hl, &r); - - if (!ipv6_is_unspecified(r.responder_ip) && ipv6_is_unspecified(hop_ip)) ipv6_cpy(hop_ip, r.responder_ip); + icmp_probe_result_t r; + memset(&r, 0, sizeof(r)); + bool answered = icmp_probe_collect(&dst, id, seq, o->timeout_ms, bind, (uint8_t)hl, &r, 1) && r.status != ICMP_PROBE_UNKNOWN_ERROR; + if (r.responder.ver == IP_VER6 && ipv6_is_unspecified(hop_ip)) ipv6_cpy(hop_ip, r.responder.ip); - if (ok || r.status == PING_TTL_EXPIRED || r.status == PING_REDIRECT || r.status == PING_PARAM_PROBLEM || - r.status == PING_NET_UNREACH || r.status == PING_HOST_UNREACH || r.status == PING_ADMIN_PROHIBITED || - r.status == PING_FRAG_NEEDED || r.status == PING_SRC_ROUTE_FAILED) { + if (answered) { any = true; + reached |= r.status == ICMP_PROBE_OK; len += string_format_buf(line + len, sizeof(line) - len, "%ums ", r.rtt_ms); - } else { - len += string_format_buf(line + len, sizeof(line) - len, "* "); - } + } else len += string_format_buf(line + len, sizeof(line) - len, "* "); if (p + 1 < o->count) msleep(o->interval_ms); } @@ -228,16 +209,14 @@ static int tracert_v6(const tr_opts_t *o) { char hip[64]; ipv6_to_string(hop_ip, hip, (int)sizeof(hip)); string_format_buf(line + len, sizeof(line) - len, "%s", hip); - } else { - string_format_buf(line + len, sizeof(line) - len, "???"); - } + } else string_format_buf(line + len, sizeof(line) - len, "???"); } else { dead_streak++; string_format_buf(line + len, sizeof(line) - len, "Request timed out."); } print("%s", line); - if (ipv6_cmp(hop_ip, dst) == 0) break; + if (reached || ipv6_cmp(hop_ip, dst_ip) == 0) break; if (dead_streak >= o->timeout_streak_limit) { print("stopping after %u consecutive timeout hops", dead_streak); break; @@ -250,7 +229,12 @@ static int tracert_v6(const tr_opts_t *o) { int run_tracert(int argc, char *argv[]) { tr_opts_t o; if (!parse_args(argc, argv, &o)) { - print("usage: tracert [-4/-6] [-m max_hops] [-n probes] [-w timeout_ms] [-i interval_ms] [-x stop_after_timeouts] [-s src_local_ip] host"); + print("usage: tracert [-4/-6] [-m max_hops] [-n probes] [-w timeout_ms] [-i interval_ms] [-x stop_after_timeouts] [-s ip|l2:id|l3:id] host"); + return 2; + } + + if (o.bind_set && o.bind.kind == BIND_IP && o.bind.ver != o.ver) { + print("tracert: source address version doesn't' match target version"); return 2; } From a413f56bf90b8333ae153be3087261ee448daecb Mon Sep 17 00:00:00 2001 From: CodeAnarchist Date: Wed, 24 Jun 2026 12:45:08 +0200 Subject: [PATCH 30/39] [net] add packet sockets and improve socket options --- kernel/networking/network_dispatch.cpp | 2 + kernel/networking/transport_layer/csocket.c | 29 +- .../networking/transport_layer/csocket_ctrl.c | 108 ++++++ .../networking/transport_layer/csocket_ctrl.h | 19 ++ .../transport_layer/csocket_packet.c | 318 ++++++++++++++++++ .../transport_layer/csocket_packet.h | 21 ++ .../networking/transport_layer/csocket_raw.c | 104 +++--- .../transport_layer/csocket_special.c | 82 ----- .../transport_layer/csocket_special.h | 19 -- .../networking/transport_layer/csocket_tcp.c | 54 +-- .../networking/transport_layer/csocket_udp.c | 68 ++-- .../networking/transport_layer/csocket_udp.h | 2 +- .../networking/transport_layer/socket_core.c | 22 +- .../networking/transport_layer/socket_core.h | 1 + kernel/networking/transport_layer/udp.c | 2 +- 15 files changed, 616 insertions(+), 235 deletions(-) create mode 100644 kernel/networking/transport_layer/csocket_ctrl.c create mode 100644 kernel/networking/transport_layer/csocket_ctrl.h create mode 100644 kernel/networking/transport_layer/csocket_packet.c create mode 100644 kernel/networking/transport_layer/csocket_packet.h delete mode 100644 kernel/networking/transport_layer/csocket_special.c delete mode 100644 kernel/networking/transport_layer/csocket_special.h diff --git a/kernel/networking/network_dispatch.cpp b/kernel/networking/network_dispatch.cpp index b7809ad2..ba0a41e3 100644 --- a/kernel/networking/network_dispatch.cpp +++ b/kernel/networking/network_dispatch.cpp @@ -12,6 +12,7 @@ #include "networking/internet_layer/ipv6_utils.h" #include "networking/netpkt.h" #include "networking/link_layer/link_utils.h" +#include "networking/transport_layer/csocket_packet.h" #include "networking/drivers/loopback/loopback_driver.hpp" #include "exceptions/irq.h" @@ -93,6 +94,7 @@ int NetworkDispatch::net_task() break; } + socket_packet_input(nics[n].ifindex, pkt); if (netpkt_len(pkt) >= sizeof(eth_hdr_t)) eth_input(nics[n].ifindex, pkt); netpkt_unref(pkt); processed++; diff --git a/kernel/networking/transport_layer/csocket.c b/kernel/networking/transport_layer/csocket.c index 210a29f8..a10c702a 100644 --- a/kernel/networking/transport_layer/csocket.c +++ b/kernel/networking/transport_layer/csocket.c @@ -1,6 +1,7 @@ #include "csocket.h" +#include "csocket_ctrl.h" +#include "csocket_packet.h" #include "csocket_raw.h" -#include "csocket_special.h" #include "csocket_tcp.h" #include "csocket_udp.h" #include "socket_core.h" @@ -50,12 +51,18 @@ socket_handle_t create_socket(protocol_t protocol, const SocketOptions* extra){ close = socket_close_raw; setopt = socket_setopt_raw; getopt = socket_getopt_raw; - } else { - impl = socket_special_create(socket, extra); - destroy = socket_destroy_special; - close = socket_close_special; - setopt = socket_setopt_special; - getopt = socket_getopt_special; + } else if (special_kind == SOCKET_SPECIAL_CTRL) { + impl = socket_ctrl_create(socket, extra); + destroy = socket_destroy_ctrl; + close = socket_close_ctrl; + setopt = socket_setopt_ctrl; + getopt = socket_getopt_ctrl; + } else if (special_kind == SOCKET_SPECIAL_PACKET) { + impl = socket_packet_create(socket, extra); + destroy = socket_destroy_packet; + close = socket_close_packet; + setopt = socket_setopt_packet; + getopt = socket_getopt_packet; } if (!impl) { socket_core_close_socket(socket); @@ -81,6 +88,7 @@ int32_t bind_socket(socket_handle_t handle, const SockBindSpec *spec_in, uint16_ int32_t res = SOCK_ERR_PROTO; if (socket_core_special_kind(socket) == SOCKET_SPECIAL_RAW) res = socket_bind_raw(socket_core_impl(socket), &spec); + else if (socket_core_special_kind(socket) == SOCKET_SPECIAL_PACKET) res = socket_bind_packet(socket_core_impl(socket), &spec); else if (socket_core_special_kind(socket) != SOCKET_SPECIAL_NONE) res = SOCK_ERR_UNSUP; else if (socket_core_protocol(socket) == PROTO_TCP) res = socket_bind_tcp(socket_core_impl(socket), &spec, port); else if (socket_core_protocol(socket) == PROTO_UDP) res = socket_bind_udp(socket_core_impl(socket), &spec, port); @@ -111,7 +119,8 @@ int64_t send_on_socket(socket_handle_t handle, const void* buf, uint64_t len){ int64_t res = SOCK_ERR_PROTO; if (socket_core_special_kind(socket) == SOCKET_SPECIAL_RAW) res = socket_send_raw(socket_core_impl(socket), buf, len); - else if (socket_core_special_kind(socket) != SOCKET_SPECIAL_NONE) res = socket_send_special(socket_core_impl(socket), buf, len); + else if (socket_core_special_kind(socket) == SOCKET_SPECIAL_CTRL) res = socket_send_ctrl(socket_core_impl(socket), buf, len); + else if (socket_core_special_kind(socket) != SOCKET_SPECIAL_NONE) res = SOCK_ERR_UNSUP; else if (socket_core_protocol(socket) == PROTO_TCP) res = socket_send_tcp(socket_core_impl(socket), buf, len); else if (socket_core_protocol(socket) == PROTO_UDP) res = socket_sendto_udp(socket_core_impl(socket), NULL, buf, len); @@ -139,7 +148,9 @@ int64_t receive_from_socket(socket_handle_t handle, void* buf, uint64_t len, net int64_t res = SOCK_ERR_PROTO; if (socket_core_special_kind(socket) == SOCKET_SPECIAL_RAW) res = socket_recv_raw(socket_core_impl(socket), buf, len, out_src); - else if (socket_core_special_kind(socket) != SOCKET_SPECIAL_NONE) res = socket_recv_special(socket_core_impl(socket), buf, len); + else if (socket_core_special_kind(socket) == SOCKET_SPECIAL_CTRL) res = socket_recv_ctrl(socket_core_impl(socket), buf, len); + else if (socket_core_special_kind(socket) == SOCKET_SPECIAL_PACKET) res = socket_recv_packet(socket_core_impl(socket), buf, len); + else if (socket_core_special_kind(socket) != SOCKET_SPECIAL_NONE) res = SOCK_ERR_UNSUP; else if (socket_core_protocol(socket) == PROTO_TCP) res = socket_recv_tcp(socket_core_impl(socket), buf, len); else if (socket_core_protocol(socket) == PROTO_UDP) res = socket_recvfrom_udp(socket_core_impl(socket), buf, len, out_src); diff --git a/kernel/networking/transport_layer/csocket_ctrl.c b/kernel/networking/transport_layer/csocket_ctrl.c new file mode 100644 index 00000000..cdf258df --- /dev/null +++ b/kernel/networking/transport_layer/csocket_ctrl.c @@ -0,0 +1,108 @@ +#include "csocket_ctrl.h" +#include "net_ctrl.h" +#include "alloc/allocate.h" +#include "std/memory.h" + +typedef struct ctrl_socket { + uint8_t* rx_buf; + uint32_t rx_len; + uint32_t rx_off; +} ctrl_socket_t; + +socket_impl_t socket_ctrl_create(ksocket_t* owner, const SocketOptions* extra) { + if (!owner) return NULL; + if (socket_core_special_kind(owner) != SOCKET_SPECIAL_CTRL) return NULL; + if (extra && extra->flags) return NULL; + + ctrl_socket_t* s = (ctrl_socket_t*)zalloc(sizeof(ctrl_socket_t)); + if (!s) return NULL; + + return s; +} + +void socket_destroy_ctrl(socket_impl_t sh) { + ctrl_socket_t* s = (ctrl_socket_t*)sh; + if (!s) return; + if (s->rx_buf) release(s->rx_buf); + release(s); +} + +int32_t socket_close_ctrl(socket_impl_t sh) { + return sh ? SOCK_OK : SOCK_ERR_INVAL; +} + +int32_t socket_setopt_ctrl(socket_impl_t sh, int32_t opt, const void* value, uint32_t len) { + return sh ? SOCK_ERR_UNSUP : SOCK_ERR_INVAL; +} + +int32_t socket_getopt_ctrl(socket_impl_t sh, int32_t opt, void* value, uint32_t* len) { + ctrl_socket_t* s = (ctrl_socket_t*)sh; + if (!s || !len) return SOCK_ERR_INVAL; + + if ((uint32_t)opt == SOCK_GET_BIND_SPEC) { + SockBindSpec spec; + memset(&spec, 0, sizeof(spec)); + spec.kind = BIND_ANY; + return socket_common_get_value(&spec, sizeof(spec), value, len); + } + + if ((uint32_t)opt == SOCK_GET_LAST_RX_SPEC) return SOCK_ERR_UNSUP; + + uint32_t v = 0; + switch ((uint32_t)opt) { + case SOCK_GET_BOUND: + break; + case SOCK_GET_RECV_QUEUED: + if (s->rx_buf && s->rx_off < s->rx_len) v = s->rx_len - s->rx_off; + break; + case SOCK_GET_CONNECTED: + case SOCK_GET_LISTENING: + case SOCK_GET_LOCAL_PORT: + case SOCK_GET_SEND_QUEUED: + return SOCK_ERR_UNSUP; + default: + return SOCK_ERR_INVAL; + } + + return socket_common_get_value(&v, sizeof(v), value, len); +} + +int64_t socket_send_ctrl(socket_impl_t sh, const void* buf, uint64_t len) { + ctrl_socket_t* s = (ctrl_socket_t*)sh; + if (!s || (!buf && len) || len > UINT32_MAX) return SOCK_ERR_INVAL; + + if (s->rx_buf) { + release(s->rx_buf); + s->rx_buf = NULL; + s->rx_len = 0; + s->rx_off = 0; + } + + uint8_t* out = NULL; + uint32_t out_len = 0; + int32_t rc = net_ctrl_dispatch(buf, (uint32_t)len, &out, &out_len); + if (rc != SOCK_OK) return rc; + + s->rx_buf = out; + s->rx_len = out_len; + s->rx_off = 0; + return (int64_t)len; +} + +int64_t socket_recv_ctrl(socket_impl_t sh, void* buf, uint64_t len) { + ctrl_socket_t* s = (ctrl_socket_t*)sh; + if (!s || (!buf && len) || len > UINT32_MAX) return SOCK_ERR_INVAL; + if (!s->rx_buf || s->rx_off >= s->rx_len) return SOCK_ERR_WOULDBLOCK; + + uint32_t avail = s->rx_len - s->rx_off; + uint32_t n = avail < (uint32_t)len ? avail : (uint32_t)len; + if (n) memcpy(buf, s->rx_buf + s->rx_off, n); + s->rx_off += n; + if (s->rx_off >= s->rx_len) { + release(s->rx_buf); + s->rx_buf = NULL; + s->rx_len = 0; + s->rx_off = 0; + } + return n; +} diff --git a/kernel/networking/transport_layer/csocket_ctrl.h b/kernel/networking/transport_layer/csocket_ctrl.h new file mode 100644 index 00000000..737216c4 --- /dev/null +++ b/kernel/networking/transport_layer/csocket_ctrl.h @@ -0,0 +1,19 @@ +#pragma once + +#include "socket_core.h" + +#ifdef __cplusplus +extern "C" { +#endif + +socket_impl_t socket_ctrl_create(ksocket_t* owner, const SocketOptions* extra); +void socket_destroy_ctrl(socket_impl_t sh); +int32_t socket_close_ctrl(socket_impl_t sh); +int32_t socket_setopt_ctrl(socket_impl_t sh, int32_t opt, const void* value, uint32_t len); +int32_t socket_getopt_ctrl(socket_impl_t sh, int32_t opt, void* value, uint32_t* len); +int64_t socket_send_ctrl(socket_impl_t sh, const void* buf, uint64_t len); +int64_t socket_recv_ctrl(socket_impl_t sh, void* buf, uint64_t len); + +#ifdef __cplusplus +} +#endif diff --git a/kernel/networking/transport_layer/csocket_packet.c b/kernel/networking/transport_layer/csocket_packet.c new file mode 100644 index 00000000..6dc46cb7 --- /dev/null +++ b/kernel/networking/transport_layer/csocket_packet.c @@ -0,0 +1,318 @@ +#include "csocket_packet.h" +#include "networking/network.h" +#include "alloc/allocate.h" +#include "exceptions/irq.h" +#include "std/memory.h" +#include "syscalls/syscalls.h" + +#define PACKET_SOCKET_MAX 64 +#define PACKET_RX_DEFAULT_RING_CAP 64 +#define PACKET_RX_MAX_RING_CAP 1024 +#define PACKET_RX_DEFAULT_BUF_SIZE (MAX_PACKET_SIZE * PACKET_RX_DEFAULT_RING_CAP) + +typedef struct packet_rx_entry { + netpkt_t* pkt; + uint8_t ifindex; +} packet_rx_entry_t; + +typedef struct packet_socket { + ksocket_t* ownerSocket; + SocketOptions options; + bool registered; + SockBindSpec bind_spec; + SockBindSpec last_rx_spec; + packet_rx_entry_t* ring; + uint32_t ring_cap; + uint32_t head; + uint32_t tail; + uint32_t rx_bytes; +} packet_socket_t; + +static packet_socket_t* g_packet_sockets[PACKET_SOCKET_MAX]; + +static void packet_socket_unregister(packet_socket_t* s) { + if (!s || !s->registered) return; + + irq_flags_t irq = irq_save_disable(); + for (int i = 0; i < PACKET_SOCKET_MAX; i++) { + if (g_packet_sockets[i] == s) { + g_packet_sockets[i] = NULL; + break; + } + } + s->registered = false; + irq_restore(irq); +} + +static void packet_socket_clear_rx(packet_socket_t* s) { + if (!s || !s->ring) return; + + for (uint32_t i = 0; i < s->ring_cap; i++) { + if (s->ring[i].pkt) netpkt_unref(s->ring[i].pkt); + s->ring[i].pkt = NULL; + s->ring[i].ifindex = 0; + } + + release(s->ring); + s->ring = NULL; + s->ring_cap = 0; + s->head = 0; + s->tail = 0; + s->rx_bytes = 0; +} + +socket_impl_t socket_packet_create(ksocket_t* owner, const SocketOptions* extra) { + if (!owner) return NULL; + if (socket_core_special_kind(owner) != SOCKET_SPECIAL_PACKET) return NULL; + + uint32_t supported = SOCK_OPT_RECV_TIMEOUT | SOCK_OPT_BUF_SIZE | SOCK_OPT_DEBUG; + if (extra && (extra->flags & ~supported)) return NULL; + + packet_socket_t* s = (packet_socket_t*)zalloc(sizeof(packet_socket_t)); + if (!s) return NULL; + + s->ownerSocket = owner; + s->options.special_kind = SOCKET_SPECIAL_PACKET; + s->options.buf_size = PACKET_RX_DEFAULT_BUF_SIZE; + s->bind_spec.kind = BIND_ANY; + s->last_rx_spec.kind = BIND_ANY; + + if (extra) { + if ((extra->flags & SOCK_OPT_DEBUG) && extra->debug_level > SOCK_DBG_ALL) { + release(s); + return 0; + } + if (extra->flags & SOCK_OPT_DEBUG) { + s->options.flags |= SOCK_OPT_DEBUG; + s->options.debug_level = extra->debug_level; + } + if (extra->flags & SOCK_OPT_RECV_TIMEOUT) { + s->options.flags |= SOCK_OPT_RECV_TIMEOUT; + s->options.recv_timeout_ms = extra->recv_timeout_ms; + } + if (extra->flags & SOCK_OPT_BUF_SIZE) { + if (!extra->buf_size) { + release(s); + return 0; + } + s->options.flags |= SOCK_OPT_BUF_SIZE; + s->options.buf_size = extra->buf_size; + } + } + + uint32_t usable = s->options.buf_size/MAX_PACKET_SIZE; + if (usable < 4) usable = 4; + if (usable > PACKET_RX_MAX_RING_CAP) usable = PACKET_RX_MAX_RING_CAP; + + s->ring_cap = usable+1; + s->ring = (packet_rx_entry_t*)zalloc(sizeof(packet_rx_entry_t) * s->ring_cap); + if (!s->ring) { + release(s); + return NULL; + } + + irq_flags_t irq = irq_save_disable(); + for (int i = 0; i < PACKET_SOCKET_MAX; i++) { + if (!g_packet_sockets[i]) { + g_packet_sockets[i] = s; + s->registered = true; + irq_restore(irq); + return s; + } + } + irq_restore(irq); + + packet_socket_clear_rx(s); + release(s); + return NULL; +} + +void socket_destroy_packet(socket_impl_t sh) { + packet_socket_t* s = (packet_socket_t*)sh; + if (!s) return; + + packet_socket_unregister(s); + packet_socket_clear_rx(s); + release(s); +} + +int32_t socket_close_packet(socket_impl_t sh) { + packet_socket_t* s = (packet_socket_t*)sh; + if (!s) return SOCK_ERR_INVAL; + + packet_socket_unregister(s); + return SOCK_OK; +} + +int32_t socket_setopt_packet(socket_impl_t sh, int32_t opt, const void* value, uint32_t len) { + packet_socket_t* s = (packet_socket_t*)sh; + if (!s) return SOCK_ERR_INVAL; + + switch ((uint32_t)opt) { + case SOCK_OPT_RECV_TIMEOUT: + case SOCK_OPT_DEBUG: + return socket_common_options_set(&s->options, opt, value, len); + case SOCK_OPT_BUF_SIZE: + return SOCK_ERR_UNSUP; + default: + return SOCK_ERR_INVAL; + } +} + +int32_t socket_getopt_packet(socket_impl_t sh, int32_t opt, void* value, uint32_t* len) { + packet_socket_t* s = (packet_socket_t*)sh; + if (!s || !len) return SOCK_ERR_INVAL; + + switch ((uint32_t)opt) { + case SOCK_GET_BIND_SPEC: + return socket_common_get_value(&s->bind_spec, sizeof(s->bind_spec), value, len); + case SOCK_GET_LAST_RX_SPEC: + return socket_common_get_value(&s->last_rx_spec, sizeof(s->last_rx_spec), value, len); + default: + break; + } + + uint32_t v = 0; + switch ((uint32_t)opt) { + case SOCK_GET_BOUND: + v = s->bind_spec.kind != BIND_ANY; + break; + case SOCK_GET_RECV_QUEUED: + v = s->rx_bytes; + break; + case SOCK_GET_OPT_RECV_TIMEOUT: + case SOCK_GET_OPT_DEBUG: + case SOCK_GET_OPT_BUF_SIZE: + return socket_common_options_get(&s->options, opt, value, len); + case SOCK_GET_CONNECTED: + case SOCK_GET_LISTENING: + case SOCK_GET_LOCAL_PORT: + case SOCK_GET_SEND_QUEUED: + return SOCK_ERR_UNSUP; + default: + return SOCK_ERR_INVAL; + } + + return socket_common_get_value(&v, sizeof(v), value, len); +} + +int32_t socket_bind_packet(socket_impl_t sh, const SockBindSpec* spec) { + packet_socket_t* s = (packet_socket_t*)sh; + if (!s || !spec) return SOCK_ERR_INVAL; + + SockBindSpec next; + memset(&next, 0, sizeof(next)); + + if (spec->kind == BIND_ANY) { + next.kind = BIND_ANY; + s->bind_spec = next; + memset(&s->last_rx_spec, 0, sizeof(s->last_rx_spec)); + s->last_rx_spec.kind = BIND_ANY; + return SOCK_OK; + } + + if (spec->kind != BIND_L2 || !spec->ifindex || !network_get_ifname(spec->ifindex)) return SOCK_ERR_INVAL; + + next.kind = BIND_L2; + next.ifindex = spec->ifindex; + s->bind_spec = next; + memset(&s->last_rx_spec, 0, sizeof(s->last_rx_spec)); + s->last_rx_spec.kind = BIND_ANY; + return SOCK_OK; +} + +int64_t socket_recv_packet(socket_impl_t sh, void* buf, uint64_t len) { + packet_socket_t* s = (packet_socket_t*)sh; + if (!s || (!buf && len) || len > UINT32_MAX) return SOCK_ERR_INVAL; + + for (;;) { + irq_flags_t irq = irq_save_disable(); + bool ready = s->head != s->tail; + irq_restore(irq); + if (ready) break; + if (!(s->options.flags & SOCK_OPT_RECV_TIMEOUT) || !s->options.recv_timeout_ms) return SOCK_ERR_WOULDBLOCK; + + uint32_t start_ms = (uint32_t)get_time(); + while (1) { + irq = irq_save_disable(); + ready = s->head != s->tail; + irq_restore(irq); + if (ready) break; + + uint32_t now_ms = (uint32_t)get_time(); + uint32_t elapsed_ms = now_ms - start_ms; + if (elapsed_ms >= s->options.recv_timeout_ms) return SOCK_ERR_WOULDBLOCK; + uint32_t wait_ms = s->options.recv_timeout_ms - elapsed_ms; + msleep(wait_ms > 5 ? 5 : wait_ms); + } + } + + irq_flags_t irq = irq_save_disable(); + uint32_t pos = s->head; + netpkt_t* pkt = s->ring[pos].pkt; + uint8_t ifindex = s->ring[pos].ifindex; + uint32_t pkt_len = netpkt_len(pkt); + memset(&s->last_rx_spec, 0, sizeof(s->last_rx_spec)); + s->last_rx_spec.kind = BIND_L2; + s->last_rx_spec.ifindex = ifindex; + s->ring[pos].pkt = NULL; + s->ring[pos].ifindex = 0; + s->head = (s->head + 1) % s->ring_cap; + if (s->rx_bytes >= pkt_len) s->rx_bytes -= pkt_len; + else s->rx_bytes = 0; + irq_restore(irq); + + uint32_t n = pkt_len; + if (n > len) n = (uint32_t)len; + if (n && !netpkt_copyout(pkt, 0, buf, n)) n = 0; + netpkt_unref(pkt); + return n; +} + +bool socket_packet_input(uint8_t ifindex, netpkt_t* pkt) { + if (!ifindex || !pkt || !netpkt_len(pkt)) return false; + + packet_socket_t* targets[PACKET_SOCKET_MAX]; + int n = 0; + irq_flags_t irq = irq_save_disable(); + for (int i = 0; i < PACKET_SOCKET_MAX; i++) { + packet_socket_t* s = g_packet_sockets[i]; + if (!s || socket_core_is_closing(s->ownerSocket)) continue; + if (s->bind_spec.kind == BIND_L2 && s->bind_spec.ifindex != ifindex) continue; + socket_core_ref(s->ownerSocket); + targets[n++] = s; + } + irq_restore(irq); + + bool delivered = false; + uint32_t pkt_len = netpkt_len(pkt); + for (int i = 0; i < n; i++) { + packet_socket_t* s = targets[i]; + uint32_t limit = s->options.buf_size ? s->options.buf_size : PACKET_RX_DEFAULT_BUF_SIZE; + netpkt_t* view = 0; + + irq = irq_save_disable(); + bool can_queue = s->ring && s->ring_cap && pkt_len <= limit && s->rx_bytes <= limit - pkt_len; + uint32_t nexti = s->ring_cap ? (s->tail + 1) % s->ring_cap : 0; + if (can_queue && nexti == s->head) can_queue = false; + irq_restore(irq); + + if (can_queue) view = netpkt_view(pkt, 0, pkt_len); + if (view) { + irq = irq_save_disable(); + nexti = (s->tail + 1) % s->ring_cap; + if (s->ring && nexti != s->head && s->rx_bytes <= limit - pkt_len) { + s->ring[s->tail].pkt = view; + s->ring[s->tail].ifindex = ifindex; + s->tail = nexti; + s->rx_bytes += pkt_len; + view = 0; + delivered = true; + } + irq_restore(irq); + if (view) netpkt_unref(view); + } + socket_core_put(s->ownerSocket); + } + return delivered; +} diff --git a/kernel/networking/transport_layer/csocket_packet.h b/kernel/networking/transport_layer/csocket_packet.h new file mode 100644 index 00000000..2abc8e6e --- /dev/null +++ b/kernel/networking/transport_layer/csocket_packet.h @@ -0,0 +1,21 @@ +#pragma once + +#include "socket_core.h" +#include "networking/netpkt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +socket_impl_t socket_packet_create(ksocket_t* owner, const SocketOptions* extra); +void socket_destroy_packet(socket_impl_t sh); +int32_t socket_close_packet(socket_impl_t sh); +int32_t socket_setopt_packet(socket_impl_t sh, int32_t opt, const void* value, uint32_t len); +int32_t socket_getopt_packet(socket_impl_t sh, int32_t opt, void* value, uint32_t* len); +int32_t socket_bind_packet(socket_impl_t sh, const SockBindSpec* spec); +int64_t socket_recv_packet(socket_impl_t sh, void* buf, uint64_t len); +bool socket_packet_input(uint8_t ifindex, netpkt_t* pkt); + +#ifdef __cplusplus +} +#endif diff --git a/kernel/networking/transport_layer/csocket_raw.c b/kernel/networking/transport_layer/csocket_raw.c index 378bc17f..aed154cb 100644 --- a/kernel/networking/transport_layer/csocket_raw.c +++ b/kernel/networking/transport_layer/csocket_raw.c @@ -21,6 +21,7 @@ typedef struct raw_rx_entry { uint8_t* data; uint32_t len; net_l4_endpoint src; + SockBindSpec rx_spec; } raw_rx_entry_t; typedef struct raw_socket { @@ -30,6 +31,7 @@ typedef struct raw_socket { bool bound; bool connected; SockBindSpec bind_spec; + SockBindSpec last_rx_spec; net_l4_endpoint remote_ep; raw_rx_entry_t rx[RAW_RX_RING_CAP]; uint8_t rx_head; @@ -52,7 +54,6 @@ static int32_t raw_set_filter(raw_socket_t* s, const void* value, uint32_t len) SocketRawFilter filter; memcpy(&filter, value, sizeof(filter)); if (filter.count > SOCKET_RAW_FILTER_MAX_RULES) return SOCK_ERR_INVAL; - if (filter.reserved[0] || filter.reserved[1] || filter.reserved[2]) return SOCK_ERR_INVAL; for (uint32_t i = 0; i < filter.count; i++) if (filter.rules[i].has_code > 1 || filter.rules[i].reserved) return SOCK_ERR_INVAL; s->options.raw_filter = filter; @@ -61,7 +62,7 @@ static int32_t raw_set_filter(raw_socket_t* s, const void* value, uint32_t len) return SOCK_OK; } -static bool raw_enqueue(raw_socket_t* s, const net_l4_endpoint* src, netpkt_t* pkt, uint32_t len) { +static bool raw_enqueue(raw_socket_t* s, const net_l4_endpoint* src, const SockBindSpec* rx_spec, netpkt_t* pkt, uint32_t len) { if (!s || !src || !pkt || !len || len > NETPKT_MAX_ALLOC || len > RAW_RX_MAX_BYTES) return false; if (s->options.raw_filter.count) { @@ -97,6 +98,11 @@ static bool raw_enqueue(raw_socket_t* s, const net_l4_endpoint* src, netpkt_t* p s->rx[pos].data = copy; s->rx[pos].len = len; s->rx[pos].src = *src; + if (rx_spec) s->rx[pos].rx_spec = *rx_spec; + else { + memset(&s->rx[pos].rx_spec, 0, sizeof(s->rx[pos].rx_spec)); + s->rx[pos].rx_spec.kind = BIND_ANY; + } s->rx_tail = (uint8_t)((s->rx_tail + 1) % RAW_RX_RING_CAP); s->rx_count++; s->rx_bytes += len; @@ -108,20 +114,27 @@ socket_impl_t socket_raw_create(ksocket_t* owner, const SocketOptions* extra) { if (!owner || socket_core_special_kind(owner) != SOCKET_SPECIAL_RAW) return NULL; protocol_t proto = socket_core_protocol(owner); if (proto != PROTO_ICMP && proto != PROTO_IGMP && proto != PROTO_ICMPV6) return NULL; - //TODO add ESP/AH sock if needed + + uint32_t supported = SOCK_OPT_RECV_TIMEOUT | SOCK_OPT_DEBUG | SOCK_OPT_DONTFRAG | SOCK_OPT_TTL | SOCK_OPT_RAW_FILTER; + if (extra && (extra->flags & ~supported)) return NULL; raw_socket_t* s = (raw_socket_t*)zalloc(sizeof(raw_socket_t)); if (!s) return NULL; s->ownerSocket = owner; s->options.special_kind = SOCKET_SPECIAL_RAW; + s->last_rx_spec.kind = BIND_ANY; if (extra) { if ((extra->flags & SOCK_OPT_RECV_TIMEOUT) && extra->recv_timeout_ms) { s->options.flags |= SOCK_OPT_RECV_TIMEOUT; s->options.recv_timeout_ms = extra->recv_timeout_ms; } - if ((extra->flags & SOCK_OPT_DEBUG) && extra->debug_level <= SOCK_DBG_ALL) { + if (extra->flags & SOCK_OPT_DEBUG) { + if (extra->debug_level > SOCK_DBG_ALL) { + release(s); + return NULL; + } s->options.flags |= SOCK_OPT_DEBUG; s->options.debug_level = extra->debug_level; } @@ -213,32 +226,13 @@ int32_t socket_getopt_raw(socket_impl_t sh, int32_t opt, void* value, uint32_t* switch ((uint32_t)opt) { case SOCK_GET_REMOTE_ENDPOINT: - if (!value) { - *len = sizeof(net_l4_endpoint); - return SOCK_OK; - } - if (*len < sizeof(net_l4_endpoint)) return SOCK_ERR_INVAL; - memcpy(value, &s->remote_ep, sizeof(s->remote_ep)); - *len = sizeof(net_l4_endpoint); - return SOCK_OK; + return socket_common_get_value(&s->remote_ep, sizeof(s->remote_ep), value, len); case SOCK_GET_BIND_SPEC: - if (!value) { - *len = sizeof(SockBindSpec); - return SOCK_OK; - } - if (*len < sizeof(SockBindSpec)) return SOCK_ERR_INVAL; - memcpy(value, &s->bind_spec, sizeof(s->bind_spec)); - *len = sizeof(SockBindSpec); - return SOCK_OK; + return socket_common_get_value(&s->bind_spec, sizeof(s->bind_spec), value, len); + case SOCK_GET_LAST_RX_SPEC: + return socket_common_get_value(&s->last_rx_spec, sizeof(s->last_rx_spec), value, len); case SOCK_GET_OPT_RAW_FILTER: - if (!value) { - *len = sizeof(SocketRawFilter); - return SOCK_OK; - } - if (*len < sizeof(SocketRawFilter)) return SOCK_ERR_INVAL; - memcpy(value, &s->options.raw_filter, sizeof(s->options.raw_filter)); - *len = sizeof(SocketRawFilter); - return SOCK_OK; + return socket_common_get_value(&s->options.raw_filter, sizeof(s->options.raw_filter), value, len); default: break; } @@ -251,14 +245,12 @@ int32_t socket_getopt_raw(socket_impl_t sh, int32_t opt, void* value, uint32_t* case SOCK_GET_CONNECTED: v = s->connected; break; - case SOCK_GET_LISTENING: - case SOCK_GET_LOCAL_PORT: - case SOCK_GET_SEND_QUEUED: - v = 0; - break; case SOCK_GET_RECV_QUEUED: v = s->rx_bytes; break; + case SOCK_GET_LISTENING: + case SOCK_GET_LOCAL_PORT: + case SOCK_GET_SEND_QUEUED: case SOCK_GET_OPT_KEEPALIVE: case SOCK_GET_OPT_KEEPALIVE_INTERVAL: case SOCK_GET_OPT_TCP_NO_DELAY: @@ -268,7 +260,7 @@ int32_t socket_getopt_raw(socket_impl_t sh, int32_t opt, void* value, uint32_t* case SOCK_GET_TCP_RTT_MS: case SOCK_GET_TCP_RETRANSMITS: case SOCK_GET_MCAST_GROUPS: - return SOCK_ERR_INVAL; + return SOCK_ERR_UNSUP; case SOCK_GET_OPT_RECV_TIMEOUT: case SOCK_GET_OPT_DEBUG: case SOCK_GET_OPT_DONTFRAG: @@ -277,19 +269,12 @@ int32_t socket_getopt_raw(socket_impl_t sh, int32_t opt, void* value, uint32_t* case SOCK_GET_OPT_SEND_TIMEOUT: case SOCK_GET_OPT_BUF_SIZE: case SOCK_GET_OPT_BROADCAST_ALLOWED: - return SOCK_ERR_INVAL; + return SOCK_ERR_UNSUP; default: return SOCK_ERR_INVAL; } - if (!value) { - *len = sizeof(uint32_t); - return SOCK_OK; - } - if (*len < sizeof(uint32_t)) return SOCK_ERR_INVAL; - memcpy(value, &v, sizeof(v)); - *len = sizeof(uint32_t); - return SOCK_OK; + return socket_common_get_value(&v, sizeof(v), value, len); } int32_t socket_bind_raw(socket_impl_t sh, const SockBindSpec* spec) { @@ -303,6 +288,8 @@ int32_t socket_bind_raw(socket_impl_t sh, const SockBindSpec* spec) { if (spec->kind == BIND_ANY || ((proto == PROTO_ICMP || proto == PROTO_IGMP) && spec->kind == BIND_ANY4) || (proto == PROTO_ICMPV6 && spec->kind == BIND_ANY6)) { s->bind_spec = next; s->bound = false; + memset(&s->last_rx_spec, 0, sizeof(s->last_rx_spec)); + s->last_rx_spec.kind = BIND_ANY; return SOCK_OK; } if (spec->kind == BIND_L2) { @@ -341,6 +328,8 @@ int32_t socket_bind_raw(socket_impl_t sh, const SockBindSpec* spec) { s->bind_spec = next; s->bound = true; + memset(&s->last_rx_spec, 0, sizeof(s->last_rx_spec)); + s->last_rx_spec.kind = BIND_ANY; return SOCK_OK; } @@ -449,6 +438,7 @@ int64_t socket_recv_raw(socket_impl_t sh, void* buf, uint64_t len, net_l4_endpoi uint8_t* data = s->rx[pos].data; uint32_t pkt_len = s->rx[pos].len; net_l4_endpoint src = s->rx[pos].src; + s->last_rx_spec = s->rx[pos].rx_spec; s->rx_bytes -= pkt_len; s->rx[pos].data = NULL; s->rx[pos].len = 0; @@ -470,6 +460,18 @@ bool socket_raw_input_v4(protocol_t protocol, uint8_t ifindex, uint32_t src, uin net_l4_endpoint src_ep; make_ep(&src, 0, IP_VER4, &src_ep); + SockBindSpec rx_spec; + memset(&rx_spec, 0, sizeof(rx_spec)); + l3_ipv4_interface_t* rx_l3 = l3_ipv4_find_by_ip(dst); + if (rx_l3) { + rx_spec.kind = BIND_L3; + rx_spec.ver = IP_VER4; + rx_spec.l3_id = rx_l3->l3_id; + } else { + rx_spec.kind = BIND_L2; + rx_spec.ifindex = ifindex; + } + raw_socket_t* targets[RAW_SOCKET_MAX]; int n = 0; irq_flags_t irq = irq_save_disable(); @@ -501,7 +503,7 @@ bool socket_raw_input_v4(protocol_t protocol, uint8_t ifindex, uint32_t src, uin uint32_t len = netpkt_len(pkt); for (int i = 0; i < n; i++) { raw_socket_t* s = targets[i]; - if (raw_enqueue(s, &src_ep, pkt, len)) delivered = true; + if (raw_enqueue(s, &src_ep, &rx_spec, pkt, len)) delivered = true; socket_core_put(s->ownerSocket); } return delivered; @@ -513,6 +515,18 @@ bool socket_raw_input_v6(uint8_t ifindex, const uint8_t src[16], const uint8_t d net_l4_endpoint src_ep; make_ep(src, 0, IP_VER6, &src_ep); + SockBindSpec rx_spec; + memset(&rx_spec, 0, sizeof(rx_spec)); + l3_ipv6_interface_t* rx_l3 = l3_ipv6_find_by_ip(dst); + if (rx_l3) { + rx_spec.kind = BIND_L3; + rx_spec.ver = IP_VER6; + rx_spec.l3_id = rx_l3->l3_id; + } else { + rx_spec.kind = BIND_L2; + rx_spec.ifindex = ifindex; + } + raw_socket_t* targets[RAW_SOCKET_MAX]; int n = 0; irq_flags_t irq = irq_save_disable(); @@ -536,7 +550,7 @@ bool socket_raw_input_v6(uint8_t ifindex, const uint8_t src[16], const uint8_t d uint32_t len = netpkt_len(pkt); for (int i = 0; i < n; i++) { raw_socket_t* s = targets[i]; - if (raw_enqueue(s, &src_ep, pkt, len)) delivered = true; + if (raw_enqueue(s, &src_ep, &rx_spec, pkt, len)) delivered = true; socket_core_put(s->ownerSocket); } return delivered; diff --git a/kernel/networking/transport_layer/csocket_special.c b/kernel/networking/transport_layer/csocket_special.c deleted file mode 100644 index c6a10df0..00000000 --- a/kernel/networking/transport_layer/csocket_special.c +++ /dev/null @@ -1,82 +0,0 @@ -#include "csocket_special.h" -#include "net_ctrl.h" -#include "alloc/allocate.h" -#include "std/memory.h" - -typedef struct special_socket { - ksocket_t* ownerSocket; - uint8_t* rx_buf; - uint32_t rx_len; - uint32_t rx_off; -} special_socket_t; - -socket_impl_t socket_special_create(ksocket_t* owner, const SocketOptions* extra) { - if (!owner) return NULL; - - special_socket_t* s = (special_socket_t*)zalloc(sizeof(special_socket_t)); - if (!s) return NULL; - - s->ownerSocket = owner; - return s; -} - -void socket_destroy_special(socket_impl_t sh) { - special_socket_t* s = (special_socket_t*)sh; - if (!s) return; - if (s->rx_buf) release(s->rx_buf); - release(s); -} - -int32_t socket_close_special(socket_impl_t sh) { - return sh ? SOCK_OK : SOCK_ERR_INVAL; -} - -int32_t socket_setopt_special(socket_impl_t sh, int32_t opt, const void* value, uint32_t len) { - return sh ? SOCK_ERR_UNSUP : SOCK_ERR_INVAL; -} - -int32_t socket_getopt_special(socket_impl_t sh, int32_t opt, void* value, uint32_t* len) { - return sh ? SOCK_ERR_UNSUP : SOCK_ERR_INVAL; -} - -int64_t socket_send_special(socket_impl_t sh, const void* buf, uint64_t len) { - special_socket_t* s = (special_socket_t*)sh; - if (!s || (!buf && len) || len > UINT32_MAX) return SOCK_ERR_INVAL; - if (socket_core_special_kind(s->ownerSocket) != SOCKET_SPECIAL_CTRL) return SOCK_ERR_UNSUP; - - if (s->rx_buf) { - release(s->rx_buf); - s->rx_buf = NULL; - s->rx_len = 0; - s->rx_off = 0; - } - - uint8_t* out = NULL; - uint32_t out_len = 0; - int32_t rc = net_ctrl_dispatch(buf, (uint32_t)len, &out, &out_len); - if (rc != SOCK_OK) return rc; - - s->rx_buf = out; - s->rx_len = out_len; - s->rx_off = 0; - return (int64_t)len; -} - -int64_t socket_recv_special(socket_impl_t sh, void* buf, uint64_t len) { - special_socket_t* s = (special_socket_t*)sh; - if (!s || (!buf && len) || len > UINT32_MAX) return SOCK_ERR_INVAL; - if (socket_core_special_kind(s->ownerSocket) != SOCKET_SPECIAL_CTRL) return SOCK_ERR_UNSUP; - if (!s->rx_buf || s->rx_off >= s->rx_len) return SOCK_ERR_WOULDBLOCK; - - uint32_t avail = s->rx_len - s->rx_off; - uint32_t n = avail < (uint32_t)len ? avail : (uint32_t)len; - if (n) memcpy(buf, s->rx_buf + s->rx_off, n); - s->rx_off += n; - if (s->rx_off >= s->rx_len) { - release(s->rx_buf); - s->rx_buf = NULL; - s->rx_len = 0; - s->rx_off = 0; - } - return n; -} diff --git a/kernel/networking/transport_layer/csocket_special.h b/kernel/networking/transport_layer/csocket_special.h deleted file mode 100644 index 716ef457..00000000 --- a/kernel/networking/transport_layer/csocket_special.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include "socket_core.h" - -#ifdef __cplusplus -extern "C" { -#endif - -socket_impl_t socket_special_create(ksocket_t* owner, const SocketOptions* extra); -void socket_destroy_special(socket_impl_t sh); -int32_t socket_close_special(socket_impl_t sh); -int32_t socket_setopt_special(socket_impl_t sh, int32_t opt, const void* value, uint32_t len); -int32_t socket_getopt_special(socket_impl_t sh, int32_t opt, void* value, uint32_t* len); -int64_t socket_send_special(socket_impl_t sh, const void* buf, uint64_t len); -int64_t socket_recv_special(socket_impl_t sh, void* buf, uint64_t len); - -#ifdef __cplusplus -} -#endif diff --git a/kernel/networking/transport_layer/csocket_tcp.c b/kernel/networking/transport_layer/csocket_tcp.c index 8db66ef8..5d2b4131 100644 --- a/kernel/networking/transport_layer/csocket_tcp.c +++ b/kernel/networking/transport_layer/csocket_tcp.c @@ -44,7 +44,15 @@ static ksocket_t* tcp_socket_pop_pending_at(tcp_socket_t* s, int idx) { socket_impl_t socket_tcp_create(ksocket_t* owner, const SocketOptions* extra) { if (!owner) return NULL; - if (extra && (extra->flags & SOCK_OPT_RAW_FILTER)) return NULL; + + uint32_t supported = SOCK_OPT_KEEPALIVE | SOCK_OPT_KEEPALIVE_INTERVAL | SOCK_OPT_SEND_TIMEOUT | SOCK_OPT_RECV_TIMEOUT | SOCK_OPT_BUF_SIZE | SOCK_OPT_DEBUG | SOCK_OPT_DONTFRAG | SOCK_OPT_TTL | SOCK_OPT_SEND_BUF_SIZE | SOCK_OPT_TCP_NO_DELAY; + if (extra) { + if (extra->flags & ~supported) return NULL; + if ((extra->flags & SOCK_OPT_DEBUG) && extra->debug_level > SOCK_DBG_ALL) return NULL; + if ((extra->flags & SOCK_OPT_BUF_SIZE) && !extra->buf_size) return NULL; + if ((extra->flags & SOCK_OPT_SEND_BUF_SIZE) && !extra->send_buf_size) return NULL; + if ((extra->flags & SOCK_OPT_KEEPALIVE_INTERVAL) && !extra->keepalive_ms) return NULL; + } tcp_socket_t* s = (tcp_socket_t*)zalloc(sizeof(*s)); if (!s) return NULL; @@ -111,7 +119,7 @@ int32_t socket_setopt_tcp(socket_impl_t sh, int32_t opt, const void* value, uint case SOCK_OPT_MCAST_LEAVE: case SOCK_OPT_BROADCAST_ALLOWED: case SOCK_OPT_RAW_FILTER: - return SOCK_ERR_INVAL; + return SOCK_ERR_UNSUP; case SOCK_OPT_RECV_TIMEOUT: case SOCK_OPT_SEND_TIMEOUT: case SOCK_OPT_BUF_SIZE: @@ -134,23 +142,24 @@ int32_t socket_getopt_tcp(socket_impl_t sh, int32_t opt, void* value, uint32_t* switch ((uint32_t)opt) { case SOCK_GET_REMOTE_ENDPOINT: - if (!value) { - *len = sizeof(net_l4_endpoint); - return SOCK_OK; - } - if (*len < sizeof(net_l4_endpoint)) return SOCK_ERR_INVAL; - memcpy(value, &s->remoteEP, sizeof(s->remoteEP)); - *len = sizeof(net_l4_endpoint); - return SOCK_OK; + return socket_common_get_value(&s->remoteEP, sizeof(s->remoteEP), value, len); case SOCK_GET_BIND_SPEC: - if (!value) { - *len = sizeof(SockBindSpec); - return SOCK_OK; + return socket_common_get_value(&s->bindSpec, sizeof(s->bindSpec), value, len); + case SOCK_GET_LAST_RX_SPEC: { + SockBindSpec spec; + memset(&spec, 0, sizeof(spec)); + spec.kind = BIND_ANY; + tcp_flow_t* flow = tcp_flow_from_ctx(&s->flow); + if (flow) { + if (flow->base.l3_id) { + spec.kind = BIND_L3; + spec.ver = flow->base.local.ver; + spec.l3_id = flow->base.l3_id; + } + tcp_flow_put(flow); } - if (*len < sizeof(SockBindSpec)) return SOCK_ERR_INVAL; - memcpy(value, &s->bindSpec, sizeof(s->bindSpec)); - *len = sizeof(SockBindSpec); - return SOCK_OK; + return socket_common_get_value(&spec, sizeof(spec), value, len); + } default: break; } @@ -227,7 +236,7 @@ int32_t socket_getopt_tcp(socket_impl_t sh, int32_t opt, void* value, uint32_t* case SOCK_GET_MCAST_GROUPS: case SOCK_GET_OPT_BROADCAST_ALLOWED: case SOCK_GET_OPT_RAW_FILTER: - return SOCK_ERR_INVAL; + return SOCK_ERR_UNSUP; case SOCK_GET_OPT_RECV_TIMEOUT: case SOCK_GET_OPT_SEND_TIMEOUT: case SOCK_GET_OPT_BUF_SIZE: @@ -239,14 +248,7 @@ int32_t socket_getopt_tcp(socket_impl_t sh, int32_t opt, void* value, uint32_t* return SOCK_ERR_INVAL; } - if (!value) { - *len = sizeof(uint32_t); - return SOCK_OK; - } - if (*len < sizeof(uint32_t)) return SOCK_ERR_INVAL; - memcpy(value, &v, sizeof(v)); - *len = sizeof(uint32_t); - return SOCK_OK; + return socket_common_get_value(&v, sizeof(v), value, len); } int32_t socket_bind_tcp(socket_impl_t sh, const SockBindSpec* spec_in, uint16_t port) { diff --git a/kernel/networking/transport_layer/csocket_udp.c b/kernel/networking/transport_layer/csocket_udp.c index 6c3bb3e1..607dba1c 100644 --- a/kernel/networking/transport_layer/csocket_udp.c +++ b/kernel/networking/transport_layer/csocket_udp.c @@ -20,6 +20,7 @@ typedef struct udp_rx_entry { netpkt_t* pkt; net_l4_endpoint src; + SockBindSpec rx_spec; } udp_rx_entry_t; typedef struct udp_socket { @@ -29,6 +30,7 @@ typedef struct udp_socket { ksocket_t* ownerSocket; SocketOptions options; SockBindSpec bindSpec; + SockBindSpec lastRxSpec; socket_bind_token_t bindToken; udp_rx_entry_t* rx_ring; net_l4_endpoint* mcast_groups; @@ -143,7 +145,7 @@ static int32_t udp_socket_join_mcast_groups(udp_socket_t* s) { return SOCK_OK; } -uint32_t socket_udp_input(ksocket_t* socket, ip_version_t ipver, const void* src_ip_addr, const void* dst_ip_addr, netpkt_t* pkt, uint16_t src_port, uint16_t dst_port) { +uint32_t socket_udp_input(ksocket_t* socket, ip_version_t ipver, uint8_t l3_id, const void* src_ip_addr, const void* dst_ip_addr, netpkt_t* pkt, uint16_t src_port, uint16_t dst_port) { if (!socket || !pkt) return 0; udp_socket_t* s = (udp_socket_t*)socket_core_impl(socket); if (!s) return 0; @@ -192,19 +194,30 @@ uint32_t socket_udp_input(ksocket_t* socket, ip_version_t ipver, const void* src netpkt_ref(pkt); s->rx_ring[s->r_tail].pkt = pkt; make_ep(src_ip_addr, src_port, ipver, &s->rx_ring[s->r_tail].src); + memset(&s->rx_ring[s->r_tail].rx_spec, 0, sizeof(s->rx_ring[s->r_tail].rx_spec)); + s->rx_ring[s->r_tail].rx_spec.kind = BIND_L3; + s->rx_ring[s->r_tail].rx_spec.ver = ipver; + s->rx_ring[s->r_tail].rx_spec.l3_id = l3_id; s->rx_bytes += pkt_len; s->r_tail = nexti; return pkt_len; -} +} socket_impl_t udp_socket_create(ksocket_t* owner, const SocketOptions* extra) { if (!owner) return NULL; - if (extra && (extra->flags & SOCK_OPT_RAW_FILTER)) return NULL; + + uint32_t supported = SOCK_OPT_RECV_TIMEOUT | SOCK_OPT_SEND_TIMEOUT | SOCK_OPT_BUF_SIZE | SOCK_OPT_DEBUG | SOCK_OPT_DONTFRAG | SOCK_OPT_BROADCAST_ALLOWED | SOCK_OPT_TTL | SOCK_OPT_MCAST_JOIN; + if (extra) { + if (extra->flags & ~supported) return NULL; + if ((extra->flags & SOCK_OPT_DEBUG) && extra->debug_level > SOCK_DBG_ALL) return NULL; + if ((extra->flags & SOCK_OPT_BUF_SIZE) && !extra->buf_size) return NULL; + } udp_socket_t* s = (udp_socket_t*)zalloc(sizeof(*s)); if (!s) return NULL; s->ownerSocket = owner; s->remoteEP.ver = IP_VER4; + s->lastRxSpec.kind = BIND_ANY; if (extra) s->options = *extra; s->options.flags &= ~(SOCK_OPT_MCAST_JOIN | SOCK_OPT_MCAST_LEAVE); s->options.mcast_count = 0; @@ -497,6 +510,7 @@ int64_t socket_recvfrom_udp(socket_impl_t sh, void* buf, uint64_t len, net_l4_en netpkt_t* p = s->rx_ring[s->r_head].pkt; net_l4_endpoint se = s->rx_ring[s->r_head].src; + s->lastRxSpec = s->rx_ring[s->r_head].rx_spec; memset(&s->rx_ring[s->r_head], 0, sizeof(s->rx_ring[s->r_head])); s->r_head = (s->r_head + 1) % s->ring_cap; @@ -522,7 +536,7 @@ int32_t socket_setopt_udp(socket_impl_t sh, int32_t opt, const void* value, uint case SOCK_OPT_TCP_NO_DELAY: case SOCK_OPT_SEND_BUF_SIZE: case SOCK_OPT_RAW_FILTER: - return SOCK_ERR_INVAL; + return SOCK_ERR_UNSUP; case SOCK_OPT_MCAST_JOIN: { if (!value || !len || (len % sizeof(net_l4_endpoint)) != 0) return SOCK_ERR_INVAL; uint32_t count32 = len / sizeof(net_l4_endpoint); @@ -668,23 +682,11 @@ int32_t socket_getopt_udp(socket_impl_t sh, int32_t opt, void* value, uint32_t* switch ((uint32_t)opt) { case SOCK_GET_REMOTE_ENDPOINT: - if (!value) { - *len = sizeof(net_l4_endpoint); - return SOCK_OK; - } - if (*len < sizeof(net_l4_endpoint)) return SOCK_ERR_INVAL; - memcpy(value, &s->remoteEP, sizeof(s->remoteEP)); - *len = sizeof(net_l4_endpoint); - return SOCK_OK; + return socket_common_get_value(&s->remoteEP, sizeof(s->remoteEP), value, len); case SOCK_GET_BIND_SPEC: - if (!value) { - *len = sizeof(SockBindSpec); - return SOCK_OK; - } - if (*len < sizeof(SockBindSpec)) return SOCK_ERR_INVAL; - memcpy(value, &s->bindSpec, sizeof(s->bindSpec)); - *len = sizeof(SockBindSpec); - return SOCK_OK; + return socket_common_get_value(&s->bindSpec, sizeof(s->bindSpec), value, len); + case SOCK_GET_LAST_RX_SPEC: + return socket_common_get_value(&s->lastRxSpec, sizeof(s->lastRxSpec), value, len); default: break; } @@ -698,8 +700,7 @@ int32_t socket_getopt_udp(socket_impl_t sh, int32_t opt, void* value, uint32_t* v = s->connected; break; case SOCK_GET_LISTENING: - v = 0; - break; + return SOCK_ERR_UNSUP; case SOCK_GET_LOCAL_PORT: v = s->localPort; break; @@ -718,17 +719,10 @@ int32_t socket_getopt_udp(socket_impl_t sh, int32_t opt, void* value, uint32_t* case SOCK_GET_TCP_MSS: case SOCK_GET_TCP_RTT_MS: case SOCK_GET_TCP_RETRANSMITS: - return SOCK_ERR_INVAL; + return SOCK_ERR_UNSUP; case SOCK_GET_MCAST_GROUPS: { uint32_t need = s->mcast_count * sizeof(net_l4_endpoint); - if (!value) { - *len = need; - return SOCK_OK; - } - if (*len < need) return SOCK_ERR_INVAL; - if (need) memcpy(value, s->mcast_groups, need); - *len = need; - return SOCK_OK; + return socket_common_get_value(s->mcast_groups, need, value, len); } case SOCK_GET_OPT_RECV_TIMEOUT: case SOCK_GET_OPT_SEND_TIMEOUT: @@ -742,15 +736,7 @@ int32_t socket_getopt_udp(socket_impl_t sh, int32_t opt, void* value, uint32_t* return SOCK_ERR_INVAL; } - if (!value) { - *len = sizeof(uint32_t); - return SOCK_OK; - } - - if (*len < sizeof(uint32_t)) return SOCK_ERR_INVAL; - memcpy(value, &v, sizeof(v)); - *len = sizeof(uint32_t); - return SOCK_OK; + return socket_common_get_value(&v, sizeof(v), value, len); } int32_t socket_close_udp(socket_impl_t sh) { @@ -790,6 +776,8 @@ int32_t socket_close_udp(socket_impl_t sh) { s->r_head = 0; s->r_tail = 0; s->rx_bytes = 0; + memset(&s->lastRxSpec, 0, sizeof(s->lastRxSpec)); + s->lastRxSpec.kind = BIND_ANY; if (s->bindToken) { socket_bind_remove(s->bindToken); s->bindToken = 0; diff --git a/kernel/networking/transport_layer/csocket_udp.h b/kernel/networking/transport_layer/csocket_udp.h index 757f8e28..f6208b8e 100644 --- a/kernel/networking/transport_layer/csocket_udp.h +++ b/kernel/networking/transport_layer/csocket_udp.h @@ -18,7 +18,7 @@ int32_t socket_close_udp(socket_impl_t sh); void socket_destroy_udp(socket_impl_t sh); int32_t socket_setopt_udp(socket_impl_t sh, int32_t opt, const void* value, uint32_t len); int32_t socket_getopt_udp(socket_impl_t sh, int32_t opt, void* value, uint32_t* len); -uint32_t socket_udp_input(ksocket_t* socket, ip_version_t ipver, const void* src_ip_addr, const void* dst_ip_addr, netpkt_t* pkt, uint16_t src_port, uint16_t dst_port); +uint32_t socket_udp_input(ksocket_t* socket, ip_version_t ipver, uint8_t l3_id, const void* src_ip_addr, const void* dst_ip_addr, netpkt_t* pkt, uint16_t src_port, uint16_t dst_port); #ifdef __cplusplus } diff --git a/kernel/networking/transport_layer/socket_core.c b/kernel/networking/transport_layer/socket_core.c index 3f6ca352..aedae88f 100644 --- a/kernel/networking/transport_layer/socket_core.c +++ b/kernel/networking/transport_layer/socket_core.c @@ -177,13 +177,18 @@ int32_t socket_core_get_option(ksocket_t* socket, int32_t opt, void* value, uint else if (opt == SOCK_GET_SPECIAL_KIND) v = socket->special_kind; else return socket->getopt(socket->impl, opt, value, len); + return socket_common_get_value(&v, sizeof(v), value, len); +} + +int32_t socket_common_get_value(const void* data, uint32_t data_len, void* value, uint32_t* len) { + if ((!data && data_len) || !len) return SOCK_ERR_INVAL; if (!value) { - *len = sizeof(uint32_t); + *len = data_len; return SOCK_OK; } - if (*len < sizeof(uint32_t)) return SOCK_ERR_INVAL; - memcpy(value, &v, sizeof(v)); - *len = sizeof(uint32_t); + if (*len < data_len) return SOCK_ERR_INVAL; + if (data_len) memcpy(value, data, data_len); + *len = data_len; return SOCK_OK; } @@ -267,14 +272,7 @@ int32_t socket_common_options_get(const SocketOptions* opts, int32_t opt, void* return SOCK_ERR_INVAL; } - if (!value) { - *len = sizeof(uint32_t); - return SOCK_OK; - } - if (*len < sizeof(uint32_t)) return SOCK_ERR_INVAL; - memcpy(value, &v, sizeof(v)); - *len = sizeof(uint32_t); - return SOCK_OK; + return socket_common_get_value(&v, sizeof(v), value, len); } socket_impl_t socket_core_impl(ksocket_t* socket) { diff --git a/kernel/networking/transport_layer/socket_core.h b/kernel/networking/transport_layer/socket_core.h index 9560f92a..33e8de1f 100644 --- a/kernel/networking/transport_layer/socket_core.h +++ b/kernel/networking/transport_layer/socket_core.h @@ -30,6 +30,7 @@ int32_t socket_core_set_option(ksocket_t* socket, int32_t opt, const void* value int32_t socket_core_get_option(ksocket_t* socket, int32_t opt, void* value, uint32_t* len); int32_t socket_common_options_set(SocketOptions* opts, int32_t opt, const void* value, uint32_t len); int32_t socket_common_options_get(const SocketOptions* opts, int32_t opt, void* value, uint32_t* len); +int32_t socket_common_get_value(const void* data, uint32_t data_len, void* value, uint32_t* len); socket_impl_t socket_core_impl(ksocket_t* socket); protocol_t socket_core_protocol(const ksocket_t* socket); diff --git a/kernel/networking/transport_layer/udp.c b/kernel/networking/transport_layer/udp.c index c2f13220..44c70730 100644 --- a/kernel/networking/transport_layer/udp.c +++ b/kernel/networking/transport_layer/udp.c @@ -129,7 +129,7 @@ void udp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a ksocket_t* sockets[SOCKET_BIND_COLLECT_MAX]; uint32_t count = socket_bind_collect(PROTO_UDP, ipver, l3_id, ifx, dst_ip_addr, dst_port, sockets, SOCKET_BIND_COLLECT_MAX); for (uint32_t i = 0; i < count; ++i) { - socket_udp_input(sockets[i], ipver, src_ip_addr, dst_ip_addr, plpkt, src_port, dst_port); + socket_udp_input(sockets[i], ipver, l3_id, src_ip_addr, dst_ip_addr, plpkt, src_port, dst_port); socket_core_put(sockets[i]); } From fcf893cf86e5e7567ea9ff231151fc8b6e31a75b Mon Sep 17 00:00:00 2001 From: CodeAnarchist Date: Wed, 24 Jun 2026 23:07:38 +0200 Subject: [PATCH 31/39] [sock] add packet/raw filters and special socket flag --- kernel/networking/transport_layer/csocket.c | 9 +- .../networking/transport_layer/csocket_ctrl.c | 19 ++- .../transport_layer/csocket_packet.c | 122 +++++++++++++++--- .../networking/transport_layer/csocket_raw.c | 37 ++++-- .../networking/transport_layer/csocket_tcp.c | 5 +- .../networking/transport_layer/csocket_udp.c | 5 +- kernel/tools/icmp_probe.c | 6 +- user/demo/default_process.c | 1 + 8 files changed, 160 insertions(+), 44 deletions(-) diff --git a/kernel/networking/transport_layer/csocket.c b/kernel/networking/transport_layer/csocket.c index a10c702a..a2a01b9d 100644 --- a/kernel/networking/transport_layer/csocket.c +++ b/kernel/networking/transport_layer/csocket.c @@ -17,10 +17,11 @@ socket_handle_t create_socket(protocol_t protocol, const SocketOptions* extra){ if (!extra) extra = &default_extra; SocketSpecialKind special_kind = extra->special_kind; - bool normal_socket = special_kind == SOCKET_SPECIAL_NONE && (protocol == PROTO_TCP || protocol == PROTO_UDP); - bool raw_socket = special_kind == SOCKET_SPECIAL_RAW && (protocol == PROTO_ICMP || protocol == PROTO_ICMPV6 || protocol == PROTO_IGMP); - bool ctrl_socket = special_kind == SOCKET_SPECIAL_CTRL && protocol == PROTO_NONE; - bool packet_socket = special_kind == SOCKET_SPECIAL_PACKET && protocol == PROTO_NONE; + bool special_requested = (extra->flags & SOCK_OPT_SPECIAL) != 0; + bool normal_socket = !special_requested && special_kind == SOCKET_SPECIAL_NONE && (protocol == PROTO_TCP || protocol == PROTO_UDP); + bool raw_socket = special_requested && special_kind == SOCKET_SPECIAL_RAW && (protocol == PROTO_ICMP || protocol == PROTO_ICMPV6 || protocol == PROTO_IGMP); + bool ctrl_socket = special_requested && special_kind == SOCKET_SPECIAL_CTRL && protocol == PROTO_NONE; + bool packet_socket = special_requested && special_kind == SOCKET_SPECIAL_PACKET && protocol == PROTO_NONE; if (!normal_socket && !raw_socket && !ctrl_socket && !packet_socket) return 0; uint16_t pid = get_current_proc_pid(); diff --git a/kernel/networking/transport_layer/csocket_ctrl.c b/kernel/networking/transport_layer/csocket_ctrl.c index cdf258df..f409341e 100644 --- a/kernel/networking/transport_layer/csocket_ctrl.c +++ b/kernel/networking/transport_layer/csocket_ctrl.c @@ -12,7 +12,7 @@ typedef struct ctrl_socket { socket_impl_t socket_ctrl_create(ksocket_t* owner, const SocketOptions* extra) { if (!owner) return NULL; if (socket_core_special_kind(owner) != SOCKET_SPECIAL_CTRL) return NULL; - if (extra && extra->flags) return NULL; + if (extra && (extra->flags & ~SOCK_OPT_SPECIAL)) return NULL; ctrl_socket_t* s = (ctrl_socket_t*)zalloc(sizeof(ctrl_socket_t)); if (!s) return NULL; @@ -59,6 +59,23 @@ int32_t socket_getopt_ctrl(socket_impl_t sh, int32_t opt, void* value, uint32_t* case SOCK_GET_LISTENING: case SOCK_GET_LOCAL_PORT: case SOCK_GET_SEND_QUEUED: + case SOCK_GET_OPT_RECV_TIMEOUT: + case SOCK_GET_OPT_SEND_TIMEOUT: + case SOCK_GET_OPT_BUF_SIZE: + case SOCK_GET_OPT_DEBUG: + case SOCK_GET_OPT_DONTFRAG: + case SOCK_GET_OPT_TTL: + case SOCK_GET_OPT_KEEPALIVE: + case SOCK_GET_OPT_KEEPALIVE_INTERVAL: + case SOCK_GET_OPT_TCP_NO_DELAY: + case SOCK_GET_OPT_SEND_BUF_SIZE: + case SOCK_GET_OPT_BROADCAST_ALLOWED: + case SOCK_GET_OPT_FILTER: + case SOCK_GET_MCAST_GROUPS: + case SOCK_GET_TCP_STATE: + case SOCK_GET_TCP_MSS: + case SOCK_GET_TCP_RTT_MS: + case SOCK_GET_TCP_RETRANSMITS: return SOCK_ERR_UNSUP; default: return SOCK_ERR_INVAL; diff --git a/kernel/networking/transport_layer/csocket_packet.c b/kernel/networking/transport_layer/csocket_packet.c index 6dc46cb7..bd6ece48 100644 --- a/kernel/networking/transport_layer/csocket_packet.c +++ b/kernel/networking/transport_layer/csocket_packet.c @@ -1,5 +1,6 @@ #include "csocket_packet.h" #include "networking/network.h" +#include "networking/link_layer/eth.h" #include "alloc/allocate.h" #include "exceptions/irq.h" #include "std/memory.h" @@ -61,17 +62,46 @@ static void packet_socket_clear_rx(packet_socket_t* s) { s->rx_bytes = 0; } +static int32_t packet_set_filter(packet_socket_t* s, const void* value, uint32_t len) { + if (!s) return SOCK_ERR_INVAL; + if (!value && !len) { + memset(&s->options.packet_filter, 0, sizeof(s->options.packet_filter)); + s->options.flags &= ~SOCK_OPT_FILTER; + return SOCK_OK; + } + if (!value || len != sizeof(SocketPacketFilter)) return SOCK_ERR_INVAL; + + SocketPacketFilter filter; + memcpy(&filter, value, sizeof(filter)); + + uint32_t valid_flags = SOCKET_PACKET_FILTER_HAS_ETHERTYPE | SOCKET_PACKET_FILTER_HAS_MIN_LEN | SOCKET_PACKET_FILTER_HAS_MAX_LEN; + if (filter.reserved || (filter.flags & ~valid_flags)) return SOCK_ERR_INVAL; + if ((filter.flags & SOCKET_PACKET_FILTER_HAS_ETHERTYPE) && !filter.ethertype) return SOCK_ERR_INVAL; + if (!(filter.flags & SOCKET_PACKET_FILTER_HAS_ETHERTYPE) && filter.ethertype) return SOCK_ERR_INVAL; + if ((filter.flags & SOCKET_PACKET_FILTER_HAS_MIN_LEN) && !filter.min_len) return SOCK_ERR_INVAL; + if (!(filter.flags & SOCKET_PACKET_FILTER_HAS_MIN_LEN) && filter.min_len) return SOCK_ERR_INVAL; + if ((filter.flags & SOCKET_PACKET_FILTER_HAS_MAX_LEN) && !filter.max_len) return SOCK_ERR_INVAL; + if (!(filter.flags & SOCKET_PACKET_FILTER_HAS_MAX_LEN) && filter.max_len) return SOCK_ERR_INVAL; + if ((filter.flags & SOCKET_PACKET_FILTER_HAS_MIN_LEN) && (filter.flags & SOCKET_PACKET_FILTER_HAS_MAX_LEN) && filter.min_len > filter.max_len) return SOCK_ERR_INVAL; + + s->options.packet_filter = filter; + if (filter.flags) s->options.flags |= SOCK_OPT_FILTER; + else s->options.flags &= ~SOCK_OPT_FILTER; + return SOCK_OK; +} + socket_impl_t socket_packet_create(ksocket_t* owner, const SocketOptions* extra) { if (!owner) return NULL; if (socket_core_special_kind(owner) != SOCKET_SPECIAL_PACKET) return NULL; - uint32_t supported = SOCK_OPT_RECV_TIMEOUT | SOCK_OPT_BUF_SIZE | SOCK_OPT_DEBUG; + uint32_t supported = SOCK_OPT_RECV_TIMEOUT | SOCK_OPT_BUF_SIZE | SOCK_OPT_DEBUG | SOCK_OPT_FILTER | SOCK_OPT_SPECIAL; if (extra && (extra->flags & ~supported)) return NULL; packet_socket_t* s = (packet_socket_t*)zalloc(sizeof(packet_socket_t)); if (!s) return NULL; s->ownerSocket = owner; + s->options.flags = SOCK_OPT_SPECIAL; s->options.special_kind = SOCKET_SPECIAL_PACKET; s->options.buf_size = PACKET_RX_DEFAULT_BUF_SIZE; s->bind_spec.kind = BIND_ANY; @@ -98,6 +128,12 @@ socket_impl_t socket_packet_create(ksocket_t* owner, const SocketOptions* extra) s->options.flags |= SOCK_OPT_BUF_SIZE; s->options.buf_size = extra->buf_size; } + if (extra->flags & SOCK_OPT_FILTER) { + if (packet_set_filter(s, &extra->packet_filter, sizeof(extra->packet_filter)) != SOCK_OK) { + release(s); + return NULL; + } + } } uint32_t usable = s->options.buf_size/MAX_PACKET_SIZE; @@ -152,7 +188,20 @@ int32_t socket_setopt_packet(socket_impl_t sh, int32_t opt, const void* value, u case SOCK_OPT_RECV_TIMEOUT: case SOCK_OPT_DEBUG: return socket_common_options_set(&s->options, opt, value, len); + case SOCK_OPT_FILTER: + return packet_set_filter(s, value, len); case SOCK_OPT_BUF_SIZE: + case SOCK_OPT_SEND_TIMEOUT: + case SOCK_OPT_SEND_BUF_SIZE: + case SOCK_OPT_KEEPALIVE: + case SOCK_OPT_KEEPALIVE_INTERVAL: + case SOCK_OPT_TCP_NO_DELAY: + case SOCK_OPT_BROADCAST_ALLOWED: + case SOCK_OPT_SPECIAL: + case SOCK_OPT_MCAST_JOIN: + case SOCK_OPT_MCAST_LEAVE: + case SOCK_OPT_DONTFRAG: + case SOCK_OPT_TTL: return SOCK_ERR_UNSUP; default: return SOCK_ERR_INVAL; @@ -168,6 +217,8 @@ int32_t socket_getopt_packet(socket_impl_t sh, int32_t opt, void* value, uint32_ return socket_common_get_value(&s->bind_spec, sizeof(s->bind_spec), value, len); case SOCK_GET_LAST_RX_SPEC: return socket_common_get_value(&s->last_rx_spec, sizeof(s->last_rx_spec), value, len); + case SOCK_GET_OPT_FILTER: + return socket_common_get_value(&s->options.packet_filter, sizeof(s->options.packet_filter), value, len); default: break; } @@ -188,6 +239,19 @@ int32_t socket_getopt_packet(socket_impl_t sh, int32_t opt, void* value, uint32_ case SOCK_GET_LISTENING: case SOCK_GET_LOCAL_PORT: case SOCK_GET_SEND_QUEUED: + case SOCK_GET_OPT_SEND_TIMEOUT: + case SOCK_GET_OPT_SEND_BUF_SIZE: + case SOCK_GET_OPT_KEEPALIVE: + case SOCK_GET_OPT_KEEPALIVE_INTERVAL: + case SOCK_GET_OPT_TCP_NO_DELAY: + case SOCK_GET_OPT_BROADCAST_ALLOWED: + case SOCK_GET_OPT_DONTFRAG: + case SOCK_GET_OPT_TTL: + case SOCK_GET_MCAST_GROUPS: + case SOCK_GET_TCP_STATE: + case SOCK_GET_TCP_MSS: + case SOCK_GET_TCP_RTT_MS: + case SOCK_GET_TCP_RETRANSMITS: return SOCK_ERR_UNSUP; default: return SOCK_ERR_INVAL; @@ -270,8 +334,11 @@ int64_t socket_recv_packet(socket_impl_t sh, void* buf, uint64_t len) { } bool socket_packet_input(uint8_t ifindex, netpkt_t* pkt) { - if (!ifindex || !pkt || !netpkt_len(pkt)) return false; + if (!ifindex || !pkt) return false; + uint32_t pkt_len = netpkt_len(pkt); + if (!pkt_len) return false; + uint16_t ethertype = eth_parse_type(pkt); packet_socket_t* targets[PACKET_SOCKET_MAX]; int n = 0; irq_flags_t irq = irq_save_disable(); @@ -279,39 +346,54 @@ bool socket_packet_input(uint8_t ifindex, netpkt_t* pkt) { packet_socket_t* s = g_packet_sockets[i]; if (!s || socket_core_is_closing(s->ownerSocket)) continue; if (s->bind_spec.kind == BIND_L2 && s->bind_spec.ifindex != ifindex) continue; + if ((s->options.packet_filter.flags & SOCKET_PACKET_FILTER_HAS_ETHERTYPE) && s->options.packet_filter.ethertype != ethertype) continue; + if ((s->options.packet_filter.flags & SOCKET_PACKET_FILTER_HAS_MIN_LEN) && pkt_len < s->options.packet_filter.min_len) continue; + if ((s->options.packet_filter.flags & SOCKET_PACKET_FILTER_HAS_MAX_LEN) && pkt_len > s->options.packet_filter.max_len) continue; socket_core_ref(s->ownerSocket); targets[n++] = s; } irq_restore(irq); + if (!n) return false; + bool delivered = false; - uint32_t pkt_len = netpkt_len(pkt); for (int i = 0; i < n; i++) { packet_socket_t* s = targets[i]; uint32_t limit = s->options.buf_size ? s->options.buf_size : PACKET_RX_DEFAULT_BUF_SIZE; - netpkt_t* view = 0; + if (pkt_len > limit) { + socket_core_put(s->ownerSocket); + continue; + } irq = irq_save_disable(); - bool can_queue = s->ring && s->ring_cap && pkt_len <= limit && s->rx_bytes <= limit - pkt_len; - uint32_t nexti = s->ring_cap ? (s->tail + 1) % s->ring_cap : 0; + bool can_queue = s->ring && s->ring_cap && s->rx_bytes <= limit - pkt_len; + uint32_t nexti = can_queue ? (s->tail + 1) % s->ring_cap : 0; if (can_queue && nexti == s->head) can_queue = false; irq_restore(irq); - if (can_queue) view = netpkt_view(pkt, 0, pkt_len); - if (view) { - irq = irq_save_disable(); - nexti = (s->tail + 1) % s->ring_cap; - if (s->ring && nexti != s->head && s->rx_bytes <= limit - pkt_len) { - s->ring[s->tail].pkt = view; - s->ring[s->tail].ifindex = ifindex; - s->tail = nexti; - s->rx_bytes += pkt_len; - view = 0; - delivered = true; - } - irq_restore(irq); - if (view) netpkt_unref(view); + if (!can_queue){ + socket_core_put(s->ownerSocket); + continue; } + + netpkt_t* view = netpkt_view(pkt, 0, pkt_len); + if (!view) { + socket_core_put(s->ownerSocket); + continue; + } + + irq = irq_save_disable(); + nexti = (s->tail + 1) % s->ring_cap; + if (s->ring && nexti != s->head && s->rx_bytes <= limit - pkt_len) { + s->ring[s->tail].pkt = view; + s->ring[s->tail].ifindex = ifindex; + s->tail = nexti; + s->rx_bytes += pkt_len; + view = 0; + delivered = true; + } + irq_restore(irq); + if (view) netpkt_unref(view); socket_core_put(s->ownerSocket); } return delivered; diff --git a/kernel/networking/transport_layer/csocket_raw.c b/kernel/networking/transport_layer/csocket_raw.c index aed154cb..0263a4cf 100644 --- a/kernel/networking/transport_layer/csocket_raw.c +++ b/kernel/networking/transport_layer/csocket_raw.c @@ -46,7 +46,7 @@ static int32_t raw_set_filter(raw_socket_t* s, const void* value, uint32_t len) if (!s) return SOCK_ERR_INVAL; if (!value && !len) { memset(&s->options.raw_filter, 0, sizeof(s->options.raw_filter)); - s->options.flags &= ~SOCK_OPT_RAW_FILTER; + s->options.flags &= ~SOCK_OPT_FILTER; return SOCK_OK; } if (!value || len != sizeof(SocketRawFilter)) return SOCK_ERR_INVAL; @@ -54,11 +54,17 @@ static int32_t raw_set_filter(raw_socket_t* s, const void* value, uint32_t len) SocketRawFilter filter; memcpy(&filter, value, sizeof(filter)); if (filter.count > SOCKET_RAW_FILTER_MAX_RULES) return SOCK_ERR_INVAL; - for (uint32_t i = 0; i < filter.count; i++) if (filter.rules[i].has_code > 1 || filter.rules[i].reserved) return SOCK_ERR_INVAL; + for (uint32_t i = 0; i < filter.count; i++) { + SocketRawFilterRule* rule = &filter.rules[i]; + if (rule->reserved || (rule->flags & ~(SOCKET_RAW_FILTER_HAS_CODE | SOCKET_RAW_FILTER_HAS_ID | SOCKET_RAW_FILTER_HAS_SEQ))) return SOCK_ERR_INVAL; + if (!(rule->flags & SOCKET_RAW_FILTER_HAS_CODE) && rule->code) return SOCK_ERR_INVAL; + if (!(rule->flags & SOCKET_RAW_FILTER_HAS_ID) && rule->id) return SOCK_ERR_INVAL; + if (!(rule->flags & SOCKET_RAW_FILTER_HAS_SEQ) && rule->seq) return SOCK_ERR_INVAL; + } s->options.raw_filter = filter; - if (filter.count) s->options.flags |= SOCK_OPT_RAW_FILTER; - else s->options.flags &= ~SOCK_OPT_RAW_FILTER; + if (filter.count) s->options.flags |= SOCK_OPT_FILTER; + else s->options.flags &= ~SOCK_OPT_FILTER; return SOCK_OK; } @@ -66,14 +72,17 @@ static bool raw_enqueue(raw_socket_t* s, const net_l4_endpoint* src, const SockB if (!s || !src || !pkt || !len || len > NETPKT_MAX_ALLOC || len > RAW_RX_MAX_BYTES) return false; if (s->options.raw_filter.count) { - if (netpkt_len(pkt) < 2) return false; - uint8_t hdr[2]; - if (!netpkt_copyout(pkt, 0, hdr, sizeof(hdr))) return false; + if (netpkt_len(pkt) < 1) return false; + uint8_t hdr[8]; + uint32_t hdr_len = netpkt_len(pkt) >= sizeof(hdr) ? (uint32_t)sizeof(hdr) : netpkt_len(pkt); + if (!netpkt_copyout(pkt, 0, hdr, hdr_len)) return false; bool ok = false; for (uint32_t i = 0; i < s->options.raw_filter.count; i++) { const SocketRawFilterRule* rule = &s->options.raw_filter.rules[i]; if (rule->type != hdr[0]) continue; - if (rule->has_code && rule->code != hdr[1]) continue; + if ((rule->flags & SOCKET_RAW_FILTER_HAS_CODE) && (hdr_len < 2 || rule->code != hdr[1])) continue; + if ((rule->flags & SOCKET_RAW_FILTER_HAS_ID) && (hdr_len < 6 || rule->id != rd_be16(hdr+4))) continue; + if ((rule->flags & SOCKET_RAW_FILTER_HAS_SEQ) && (hdr_len < 8 || rule->seq != rd_be16(hdr+6))) continue; ok = true; break; } @@ -114,14 +123,16 @@ socket_impl_t socket_raw_create(ksocket_t* owner, const SocketOptions* extra) { if (!owner || socket_core_special_kind(owner) != SOCKET_SPECIAL_RAW) return NULL; protocol_t proto = socket_core_protocol(owner); if (proto != PROTO_ICMP && proto != PROTO_IGMP && proto != PROTO_ICMPV6) return NULL; + //TODO add ESP/AH sock if needed - uint32_t supported = SOCK_OPT_RECV_TIMEOUT | SOCK_OPT_DEBUG | SOCK_OPT_DONTFRAG | SOCK_OPT_TTL | SOCK_OPT_RAW_FILTER; + uint32_t supported = SOCK_OPT_RECV_TIMEOUT | SOCK_OPT_DEBUG | SOCK_OPT_DONTFRAG | SOCK_OPT_TTL | SOCK_OPT_FILTER | SOCK_OPT_SPECIAL; if (extra && (extra->flags & ~supported)) return NULL; raw_socket_t* s = (raw_socket_t*)zalloc(sizeof(raw_socket_t)); if (!s) return NULL; s->ownerSocket = owner; + s->options.flags = SOCK_OPT_SPECIAL; s->options.special_kind = SOCKET_SPECIAL_RAW; s->last_rx_spec.kind = BIND_ANY; if (extra) { @@ -144,7 +155,7 @@ socket_impl_t socket_raw_create(ksocket_t* owner, const SocketOptions* extra) { s->options.flags |= SOCK_OPT_TTL; s->options.ttl = extra->ttl; } - if (extra->flags & SOCK_OPT_RAW_FILTER) { + if (extra->flags & SOCK_OPT_FILTER) { if (raw_set_filter(s, &extra->raw_filter, sizeof(extra->raw_filter)) != SOCK_OK) { release(s); return NULL; @@ -213,8 +224,10 @@ int32_t socket_setopt_raw(socket_impl_t sh, int32_t opt, const void* value, uint case SOCK_OPT_DONTFRAG: case SOCK_OPT_TTL: return socket_common_options_set(&s->options, opt, value, len); - case SOCK_OPT_RAW_FILTER: + case SOCK_OPT_FILTER: return raw_set_filter(s, value, len); + case SOCK_OPT_SPECIAL: + return SOCK_ERR_UNSUP; default: return SOCK_ERR_INVAL; } @@ -231,7 +244,7 @@ int32_t socket_getopt_raw(socket_impl_t sh, int32_t opt, void* value, uint32_t* return socket_common_get_value(&s->bind_spec, sizeof(s->bind_spec), value, len); case SOCK_GET_LAST_RX_SPEC: return socket_common_get_value(&s->last_rx_spec, sizeof(s->last_rx_spec), value, len); - case SOCK_GET_OPT_RAW_FILTER: + case SOCK_GET_OPT_FILTER: return socket_common_get_value(&s->options.raw_filter, sizeof(s->options.raw_filter), value, len); default: break; diff --git a/kernel/networking/transport_layer/csocket_tcp.c b/kernel/networking/transport_layer/csocket_tcp.c index 5d2b4131..531ddd04 100644 --- a/kernel/networking/transport_layer/csocket_tcp.c +++ b/kernel/networking/transport_layer/csocket_tcp.c @@ -118,7 +118,8 @@ int32_t socket_setopt_tcp(socket_impl_t sh, int32_t opt, const void* value, uint case SOCK_OPT_MCAST_JOIN: case SOCK_OPT_MCAST_LEAVE: case SOCK_OPT_BROADCAST_ALLOWED: - case SOCK_OPT_RAW_FILTER: + case SOCK_OPT_FILTER: + case SOCK_OPT_SPECIAL: return SOCK_ERR_UNSUP; case SOCK_OPT_RECV_TIMEOUT: case SOCK_OPT_SEND_TIMEOUT: @@ -235,7 +236,7 @@ int32_t socket_getopt_tcp(socket_impl_t sh, int32_t opt, void* value, uint32_t* break; case SOCK_GET_MCAST_GROUPS: case SOCK_GET_OPT_BROADCAST_ALLOWED: - case SOCK_GET_OPT_RAW_FILTER: + case SOCK_GET_OPT_FILTER: return SOCK_ERR_UNSUP; case SOCK_GET_OPT_RECV_TIMEOUT: case SOCK_GET_OPT_SEND_TIMEOUT: diff --git a/kernel/networking/transport_layer/csocket_udp.c b/kernel/networking/transport_layer/csocket_udp.c index 607dba1c..a1ba75ae 100644 --- a/kernel/networking/transport_layer/csocket_udp.c +++ b/kernel/networking/transport_layer/csocket_udp.c @@ -535,7 +535,8 @@ int32_t socket_setopt_udp(socket_impl_t sh, int32_t opt, const void* value, uint case SOCK_OPT_KEEPALIVE_INTERVAL: case SOCK_OPT_TCP_NO_DELAY: case SOCK_OPT_SEND_BUF_SIZE: - case SOCK_OPT_RAW_FILTER: + case SOCK_OPT_FILTER: + case SOCK_OPT_SPECIAL: return SOCK_ERR_UNSUP; case SOCK_OPT_MCAST_JOIN: { if (!value || !len || (len % sizeof(net_l4_endpoint)) != 0) return SOCK_ERR_INVAL; @@ -714,7 +715,7 @@ int32_t socket_getopt_udp(socket_impl_t sh, int32_t opt, void* value, uint32_t* case SOCK_GET_OPT_KEEPALIVE_INTERVAL: case SOCK_GET_OPT_TCP_NO_DELAY: case SOCK_GET_OPT_SEND_BUF_SIZE: - case SOCK_GET_OPT_RAW_FILTER: + case SOCK_GET_OPT_FILTER: case SOCK_GET_TCP_STATE: case SOCK_GET_TCP_MSS: case SOCK_GET_TCP_RTT_MS: diff --git a/kernel/tools/icmp_probe.c b/kernel/tools/icmp_probe.c index 4f232b34..a9beb4af 100644 --- a/kernel/tools/icmp_probe.c +++ b/kernel/tools/icmp_probe.c @@ -72,12 +72,12 @@ uint32_t icmp_probe_collect(const net_l4_endpoint* dst, uint16_t id, uint16_t se SocketOptions opt; memset(&opt, 0, sizeof(opt)); opt.special_kind = SOCKET_SPECIAL_RAW; - opt.flags = SOCK_OPT_RAW_FILTER; + opt.flags = SOCK_OPT_SPECIAL | SOCK_OPT_FILTER; opt.raw_filter.count = 5; if (dst->ver == IP_VER4) { opt.raw_filter.rules[0].type = ICMP_ECHO_REPLY; opt.raw_filter.rules[0].code = 0; - opt.raw_filter.rules[0].has_code = 1; + opt.raw_filter.rules[0].flags = SOCKET_RAW_FILTER_HAS_CODE; opt.raw_filter.rules[1].type = ICMP_DEST_UNREACH; opt.raw_filter.rules[2].type = ICMP_TIME_EXCEEDED; opt.raw_filter.rules[3].type = ICMP_PARAM_PROBLEM; @@ -85,7 +85,7 @@ uint32_t icmp_probe_collect(const net_l4_endpoint* dst, uint16_t id, uint16_t se } else { opt.raw_filter.rules[0].type = ICMPV6_ECHO_REPLY; opt.raw_filter.rules[0].code = 0; - opt.raw_filter.rules[0].has_code = 1; + opt.raw_filter.rules[0].flags = SOCKET_RAW_FILTER_HAS_CODE; opt.raw_filter.rules[1].type = ICMPV6_DEST_UNREACH; opt.raw_filter.rules[2].type = ICMPV6_PACKET_TOO_BIG; opt.raw_filter.rules[3].type = ICMPV6_TIME_EXCEEDED; diff --git a/user/demo/default_process.c b/user/demo/default_process.c index f2a9edce..585070ed 100644 --- a/user/demo/default_process.c +++ b/user/demo/default_process.c @@ -90,6 +90,7 @@ int net_ctrl_example() { //create ctrl socket SocketOptions opt = {}; opt.special_kind = SOCKET_SPECIAL_CTRL; + opt.flags = SOCK_OPT_SPECIAL; socket_handle_t sock = socket_create(PROTO_NONE, &opt); if (!sock) return -1; From 5786c1439caa03e12cd1f1f2d7ab1aa213627466 Mon Sep 17 00:00:00 2001 From: CodeAnarchist Date: Wed, 8 Jul 2026 13:20:46 +0200 Subject: [PATCH 32/39] [net] web routing, reduce network noise, minor fixes --- .../application_layer/csocket_http_server.c | 2 +- .../application_layer/dns/mdns_responder.c | 43 +++ kernel/networking/application_layer/http.c | 31 ++- kernel/networking/application_layer/http.h | 4 +- .../application_layer/http_webserver.c | 259 ++++++++++++++++++ .../application_layer/http_webserver.h | 94 +++++++ kernel/networking/interface_manager.c | 19 +- kernel/networking/link_layer/ndp.c | 11 +- kernel/networking/processes/net_proc.c | 141 +++++----- .../networking/transport_layer/csocket_tcp.c | 92 +++---- .../networking/transport_layer/tcp/tcp_core.c | 20 +- .../networking/transport_layer/tcp/tcp_rx.c | 18 +- .../networking/transport_layer/tcp/tcp_tx.c | 3 +- shared | 2 +- 14 files changed, 576 insertions(+), 163 deletions(-) create mode 100644 kernel/networking/application_layer/http_webserver.c create mode 100644 kernel/networking/application_layer/http_webserver.h diff --git a/kernel/networking/application_layer/csocket_http_server.c b/kernel/networking/application_layer/csocket_http_server.c index 4d9dadad..0f7ff562 100644 --- a/kernel/networking/application_layer/csocket_http_server.c +++ b/kernel/networking/application_layer/csocket_http_server.c @@ -194,7 +194,7 @@ HTTPRequestMsg http_server_recv_request(http_server_handle_t h, http_connection_ reject_status = http_parse_result_status(line_result); } else if (!http_method_allowed(srv->policy.allowed_methods, line.method)) { bad_request = true; - reject_status = HTTP_METHOD_NOT_ALLOWED; + reject_status = line.method == HTTP_METHOD_UNKNOWN ? HTTP_NOT_IMPLEMENTED : HTTP_METHOD_NOT_ALLOWED; } } diff --git a/kernel/networking/application_layer/dns/mdns_responder.c b/kernel/networking/application_layer/dns/mdns_responder.c index 7ed5229f..13cc514a 100644 --- a/kernel/networking/application_layer/dns/mdns_responder.c +++ b/kernel/networking/application_layer/dns/mdns_responder.c @@ -6,6 +6,7 @@ #include "networking/internet_layer/ipv6_utils.h" #include "networking/transport_layer/csocket.h" #include "networking/interface_manager.h" +#include "data/hash.h" #include "std/std.h" #include "std/string.h" #include "syscalls/syscalls.h" @@ -17,6 +18,8 @@ #define MDNS_KEEPALIVE_MS 60000 #define MDNS_MAX_SERVICES 8 #define MDNS_CACHE_MAX 48 +#define MDNS_QUERY_DEDUP_MAX 8 +#define MDNS_QUERY_DEDUP_MS 250 #define MDNS_FLUSH_CLASS (DNS_CLASS_CACHE_FLUSH | DNS_CLASS_IN) #define MDNS_HOST_NAME "RedactedOS" @@ -43,6 +46,15 @@ typedef struct { char txt[256]; } mdns_cache_entry_t; +typedef struct { + bool used; + ip_version_t ver; + uint16_t port; + uint64_t hash; + uint64_t last_ms; + uint8_t ip[16]; +} mdns_query_dedup_t; + typedef struct { uint8_t *out; uint32_t cap; @@ -65,6 +77,8 @@ static uint64_t g_mdns_host_last_tx_ms = 0; static mdns_service_t g_mdns_services[MDNS_MAX_SERVICES]; static mdns_cache_entry_t g_mdns_cache[MDNS_CACHE_MAX]; +static mdns_query_dedup_t g_mdns_query_dedup[MDNS_QUERY_DEDUP_MAX]; +static uint8_t g_mdns_query_dedup_next = 0; static void mdns_send(socket_handle_t sock, const net_l4_endpoint *src, bool unicast, ip_version_t ver, const uint8_t *mcast_ip, const uint8_t *pkt, uint32_t pkt_len) { if (!sock) return; @@ -688,6 +702,35 @@ void mdns_responder_handle_query(socket_handle_t sock, ip_version_t ver, const u uint16_t qd = rd_be16(pkt + 4); if (!qd) return; + uint64_t now = get_time(); + uint64_t hash = hash_map_fnv1a64(pkt, pkt_len); + uint16_t src_port = src ? src->port : 0; + uint8_t src_ip_zero[16]; + memset(src_ip_zero, 0, sizeof(src_ip_zero)); + const uint8_t *src_ip = src ? src->ip : src_ip_zero; + bool duplicate_query = false; + + for (uint32_t i = 0; i < MDNS_QUERY_DEDUP_MAX; i++) { + mdns_query_dedup_t *e = &g_mdns_query_dedup[i]; + if (!e->used || e->ver != ver || e->port != src_port || e->hash != hash) continue; + if (memcmp(e->ip, src_ip, ver == IP_VER4 ? 4 : 16) != 0) continue; + if (now - e->last_ms >= MDNS_QUERY_DEDUP_MS) continue; + e->last_ms = now; + duplicate_query = true; + break; + } + + if (duplicate_query) return; + + mdns_query_dedup_t *dedup = &g_mdns_query_dedup[g_mdns_query_dedup_next++ % MDNS_QUERY_DEDUP_MAX]; + memset(dedup, 0, sizeof(*dedup)); + dedup->used = true; + dedup->ver = ver; + dedup->port = src_port; + dedup->hash = hash; + dedup->last_ms = now; + memcpy(dedup->ip, src_ip, ver == IP_VER4 ? 4 : 16); + bool unicast_any = src && src->port && src->port != DNS_MDNS_PORT; uint8_t out[1500]; diff --git a/kernel/networking/application_layer/http.c b/kernel/networking/application_layer/http.c index 8d5d84da..82d29e36 100644 --- a/kernel/networking/application_layer/http.c +++ b/kernel/networking/application_layer/http.c @@ -101,7 +101,8 @@ const char* http_method_name(HTTPMethod method) { case HTTP_METHOD_DELETE: return "DELETE"; case HTTP_METHOD_HEAD: return "HEAD"; case HTTP_METHOD_OPTIONS: return "OPTIONS"; - default: return "GET"; + case HTTP_METHOD_UNKNOWN: return "UNKNOWN"; + default: return "UNKNOWN"; } } @@ -169,8 +170,6 @@ HttpError http_parse_result_status(HTTPParseResult result) { return HTTP_BAD_REQUEST; case HTTP_PARSE_UNSUPPORTED_TRANSFER: return HTTP_NOT_IMPLEMENTED; - case HTTP_PARSE_UNKNOWN_METHOD: - return HTTP_NOT_IMPLEMENTED; case HTTP_PARSE_UNSUPPORTED_VERSION: return HTTP_VERSION_NOT_SUPPORTED; case HTTP_PARSE_PAYLOAD_TOO_LARGE: @@ -216,7 +215,7 @@ HTTPParseResult http_parse_request_line(const char *buf, uint32_t len, HTTPReque else if (mlen == 7 && memcmp(buf, "OPTIONS", 7) == 0) out->method = HTTP_METHOD_OPTIONS; else { for (uint32_t j = 0; j < mlen; j++) if (!is_alnum( buf[j]) && str_has_char("!#$%&'*+-.^_`|~", 0, buf[j]) < 0) return HTTP_PARSE_BAD_FORMAT; - return HTTP_PARSE_UNKNOWN_METHOD; + out->method = HTTP_METHOD_UNKNOWN; } i++; @@ -370,10 +369,7 @@ HTTPParseResult http_header_parse(const char *buf, uint32_t len, const HTTPPolic *out_extra_count = 0; HTTPHeader *extras = NULL; - if (p.max_header_count) { - extras = (HTTPHeader*)zalloc(sizeof(*extras) * p.max_header_count); - if (!extras) return HTTP_PARSE_TOO_LARGE; - } + uint32_t extra_cap = 0; HTTPParseResult result = HTTP_PARSE_OK; uint32_t extra_i = 0; @@ -567,7 +563,24 @@ HTTPParseResult http_header_parse(const char *buf, uint32_t len, const HTTPPolic string key = string_from_literal_length((char*)(buf + pos), key_len); string value = string_from_literal_length((char*)(buf + val_start), val_len); - if (extras && extra_i < p.max_header_count){ + if (extra_i < p.max_header_count) { + if (extra_i == extra_cap) { + uint32_t new_cap = extra_cap ? extra_cap * 2 : 4; + if (new_cap > p.max_header_count) new_cap = p.max_header_count; + HTTPHeader *grown = (HTTPHeader*)zalloc(sizeof(*grown) * new_cap); + if (!grown) { + if (key.mem_length) string_free(key); + if (value.mem_length) string_free(value); + result = HTTP_PARSE_TOO_LARGE; + break; + } + if (extras) { + memcpy(grown, extras, sizeof(*grown) * extra_i); + release(extras); + } + extras = grown; + extra_cap = new_cap; + } extras[extra_i++] = (HTTPHeader){ key, value }; } else { if (key.mem_length) string_free(key); diff --git a/kernel/networking/application_layer/http.h b/kernel/networking/application_layer/http.h index fc2fc552..9a4616cb 100644 --- a/kernel/networking/application_layer/http.h +++ b/kernel/networking/application_layer/http.h @@ -27,7 +27,8 @@ typedef enum { HTTP_METHOD_PUT, HTTP_METHOD_DELETE, HTTP_METHOD_HEAD, - HTTP_METHOD_OPTIONS + HTTP_METHOD_OPTIONS, + HTTP_METHOD_UNKNOWN } HTTPMethod; #define HTTP_METHOD_MASK_GET (1u << HTTP_METHOD_GET) @@ -54,7 +55,6 @@ typedef enum { HTTP_PARSE_MISSING_HOST, HTTP_PARSE_UNSUPPORTED_VERSION, HTTP_PARSE_PAYLOAD_TOO_LARGE, - HTTP_PARSE_UNKNOWN_METHOD, HTTP_PARSE_INCOMPLETE } HTTPParseResult; diff --git a/kernel/networking/application_layer/http_webserver.c b/kernel/networking/application_layer/http_webserver.c new file mode 100644 index 00000000..ddb5c06d --- /dev/null +++ b/kernel/networking/application_layer/http_webserver.c @@ -0,0 +1,259 @@ +#include "http_webserver.h" +#include "filesystem/filesystem.h" +#include "networking/application_layer/dns/mdns_responder.h" +#include "networking/transport_layer/socket_bind.h" +#include "std/memory.h" +#include "std/string.h" +#include "syscalls/syscalls.h" + +typedef struct { + const HTTPRoute *route; + const HTTPRoute *method_route; + uint32_t methods; +} HTTPWebRouteMatch; + +static uint32_t http_web_path_len(string path) { + uint32_t len = path.length; + int32_t q = str_has_char(path.data, len, '?'); + if (q >= 0)len = (uint32_t)q; + int32_t f = str_has_char(path.data, len, '#'); + if (f >= 0)len = (uint32_t)f; + return len; +} + +static HTTPWebRouteMatch http_web_find_route(const HTTPWebServerConfig *config, string path, HTTPMethod method) { + HTTPWebRouteMatch match = {0}; + if (!config || (!config->routes && config->route_count)) return match; + + uint32_t path_len = http_web_path_len(path); + uint32_t best_prefix_len = 0; + uint32_t best_method_prefix_len = 0; + uint32_t exact_methods = 0; + uint32_t prefix_methods = 0; + bool exact = false; + + for (uint32_t i = 0; i < config->route_count; i++) { + const HTTPRoute *route = &config->routes[i]; + if (!route->path || !path.data) continue; + + uint32_t route_len = (uint32_t)strlen(route->path); + bool prefix = (route->flags & HTTP_ROUTE_PREFIX) != 0; + if (!(prefix ? path_len >= route_len && memcmp(path.data, route->path, route_len) == 0 : path_len == route_len && memcmp(path.data, route->path, route_len) == 0)) continue; + + uint32_t methods = route->methods; + if ((methods & HTTP_METHOD_MASK_GET) && (config->head_as_get || (route->flags & HTTP_ROUTE_HEAD_AS_GET))) methods |= HTTP_METHOD_MASK_HEAD; + bool allowed = http_method_allowed(methods, method); + + if (!prefix) { + exact = true; + exact_methods |= methods; + if (!match.route) match.route = route; + if (allowed && !match.method_route) match.method_route = route; + } else if (!exact) { + if (route_len >= best_prefix_len) { + if (route_len > best_prefix_len) { + match.route = route; + prefix_methods = 0; + best_prefix_len = route_len; + } + prefix_methods |= methods; + } + if (allowed && route_len >= best_method_prefix_len) { + match.method_route = route; + best_method_prefix_len = route_len; + } + } + } + + match.methods = exact_methods ? exact_methods : prefix_methods; + return match; +} + +static int32_t http_web_send_blob(HTTPWebContext *ctx, const HTTPWebFile *file, const void *body, uint32_t body_len) { + static char cache_name[] = "Cache-Control"; + string cache = {0}; + HTTPHeader local[4]; + uint32_t base_count = file && file->headers ? file->header_count : 0; + uint32_t total = base_count; + + if (file && file->cache_max_age_sec) total++; + HTTPHeader *headers = total ? local : NULL; + if (total > N_ARR(local)) headers = (HTTPHeader*)zalloc(sizeof(HTTPHeader) * total); + if (total && !headers) { + HTTPWebResponse response = HTTP_WEB_TEXT_RESPONSE(HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error\n"); + return http_web_send(ctx, &response); + } + + for (uint32_t i = 0; i < base_count; i++) headers[i] = file->headers[i]; + if (file && file->cache_max_age_sec) { + cache = string_format("public, max-age=%i", (int)file->cache_max_age_sec); + headers[base_count] = (HTTPHeader){{cache_name, sizeof(cache_name) - 1, 0}, cache}; + } + + HTTPWebResponse response = { + .status = HTTP_OK, + .content_type = file && file->content_type ? file->content_type : "application/octet-stream", + .body = body, + .body_len = body_len, + .headers = headers, + .header_count = total + }; + + int32_t rc = http_web_send(ctx, &response); + if (headers && headers != local) release(headers); + string_free(cache); + return rc; +} + +int32_t http_web_send(HTTPWebContext *ctx, const HTTPWebResponse *response) { + if (!ctx || !response) return SOCK_ERR_INVAL; + static char close_data[] = "close"; + const char *type = response->content_type ? response->content_type : "application/octet-stream"; + HTTPResponseMsg res = {0}; + res.status_code = response->status; + res.headers_common.fields.content_length = response->body_len; + res.headers_common.framing.has_content_length = 1; + res.headers_common.fields.content_type = (string){(char*)type, (uint32_t)strlen(type), 0}; + if (ctx->close_after_response) res.headers_common.fields.connection = (string){close_data, sizeof(close_data) - 1, 0}; + if (response->body && response->body_len) res.body = (string){(char*)response->body, response->body_len, 0}; + res.extra_headers = (HTTPHeader*)response->headers; + res.extra_header_count = response->header_count; + return http_server_send_response(ctx->server, ctx->conn, &res); +} + +static int32_t http_web_send_allow(HTTPWebContext *ctx, HttpError status, uint32_t methods) { + methods |= HTTP_METHOD_MASK_OPTIONS; + string allow = http_methods_allow_header(methods); + string body = status == HTTP_METHOD_NOT_ALLOWED ? string_format("%s\n", http_status_reason(status)) : (string){0}; + static char allow_name[] = "Allow"; + static char text_plain[] = "text/plain"; + HTTPHeader allow_header = {{allow_name, sizeof(allow_name)-1, 0}, allow}; + HTTPResponseMsg res = {0}; + res.status_code = status; + res.headers_common.fields.content_length = body.length; + res.headers_common.framing.has_content_length = 1; + res.headers_common.fields.content_type = (string){text_plain, sizeof(text_plain) - 1, 0}; + res.extra_headers = &allow_header; + res.extra_header_count = 1; + res.body = body; + int32_t rc = http_server_send_response(ctx->server, ctx->conn, &res); + string_free(allow); + string_free(body); + return rc; +} + +static int32_t http_web_send_file(HTTPWebContext *ctx, const HTTPWebFile *file) { + if (!ctx || !file || !file->fs_path) return SOCK_ERR_INVAL; + + fs_stat st = {0}; + if (!get_stat(kernel_fs(), file->fs_path, &st) || st.type != entry_file) { + HTTPWebResponse response = HTTP_WEB_TEXT_RESPONSE(HTTP_NOT_FOUND, "Not Found\n"); + return http_web_send(ctx, &response); + } + if (st.size > UINT32_MAX || (file->max_bytes && st.size > file->max_bytes)) { + HTTPWebResponse response = HTTP_WEB_TEXT_RESPONSE(HTTP_PAYLOAD_TOO_LARGE, "Payload Too Large\n"); + return http_web_send(ctx, &response); + } + + uint32_t size = (uint32_t)st.size; + uint8_t *buf = NULL; + bool head = ctx->request && ctx->request->method == HTTP_METHOD_HEAD; + if (size && !head) { + buf = (uint8_t*)zalloc(size); + if (!buf) { + HTTPWebResponse response = HTTP_WEB_TEXT_RESPONSE(HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error\n"); + return http_web_send(ctx, &response); + } + if (simple_read(kernel_fs(), file->fs_path, buf, size) != size) { + release(buf); + HTTPWebResponse response = HTTP_WEB_TEXT_RESPONSE(HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error\n"); + return http_web_send(ctx, &response); + } + } + + int32_t rc = http_web_send_blob(ctx, file, buf, size); + if (buf) release(buf); + return rc; +} + +int32_t http_webserver_run(const HTTPWebServerConfig *config) { + if (!config || !config->port || (!config->routes && config->route_count)) return SOCK_ERR_INVAL; + + HTTPServerPolicyOptions local_policy = config->policy_options ? *config->policy_options : (HTTPServerPolicyOptions){0}; + if (!(local_policy.flags & HTTP_SERVER_OPT_ALLOWED_METHODS)) { + uint32_t methods = HTTP_METHOD_MASK_OPTIONS; + for (uint32_t i = 0; i < config->route_count; i++) { + methods |= config->routes[i].methods; + if ((config->routes[i].methods & HTTP_METHOD_MASK_GET) && (config->head_as_get || (config->routes[i].flags & HTTP_ROUTE_HEAD_AS_GET))) methods |= HTTP_METHOD_MASK_HEAD; + } + local_policy.flags |= HTTP_SERVER_OPT_ALLOWED_METHODS; + local_policy.value.allowed_methods = methods; + } + + http_server_handle_t srv = http_server_create(config->socket_options, &local_policy); + if (!srv) return SOCK_ERR_SYS; + + struct SockBindSpec spec = {0}; + spec.kind = BIND_ANY; + int32_t rc = http_server_bind(srv, &spec, config->port); + if (rc < 0) { + http_server_destroy(srv); + return rc; + } + + rc = http_server_listen(srv, config->backlog > 0 ? config->backlog : 8); + if (rc < 0) { + http_server_close(srv); + http_server_destroy(srv); + return rc; + } + + if (config->mdns_instance && config->mdns_type && config->mdns_proto) mdns_register_service(config->mdns_instance, config->mdns_type, config->mdns_proto, config->port, config->mdns_txt); + + while (1) { + http_connection_handle_t conn = http_server_accept(srv); + if (!conn) { + msleep(50); + continue; + } + + while (2) { + HTTPRequestMsg req = http_server_recv_request(srv, conn); + if (!req.path.length) { + http_connection_close(conn); + break; + } + + HTTPWebRouteMatch match = http_web_find_route(config, req.path, req.method); + const HTTPRoute *route = match.method_route ? match.method_route : match.route; + HTTPWebContext ctx = { + .server = srv, + .conn = conn, + .request = &req, + .route = route, + .config = config, + .user = route && route->user ? route->user : config->user, + .close_after_response = config->close_each_response + }; + + if (req.method == HTTP_METHOD_OPTIONS && (match.route || config->options_for_any_path)) rc = http_web_send_allow(&ctx, HTTP_OK, match.methods); + else if (match.method_route) { + if (match.method_route->kind == HTTP_ROUTE_STATIC) rc = http_web_send(&ctx, &match.method_route->as.response); + else if (match.method_route->kind == HTTP_ROUTE_FILE) rc = http_web_send_file(&ctx, &match.method_route->as.file); + else if (match.method_route->kind == HTTP_ROUTE_HANDLER && match.method_route->as.handler) rc = match.method_route->as.handler(&ctx); + else rc = SOCK_ERR_INVAL; + } else if (match.route) rc = http_web_send_allow(&ctx, HTTP_METHOD_NOT_ALLOWED, match.methods); + else if (config->not_found.body || config->not_found.body_len || config->not_found.content_type) rc = http_web_send(&ctx, &config->not_found); + else { + HTTPWebResponse response = HTTP_WEB_TEXT_RESPONSE(HTTP_NOT_FOUND, "Not Found\n"); + rc = http_web_send(&ctx, &response); + } + + http_request_free(&req); + if (config->close_each_response || rc < 0) { + http_connection_close(conn); + break; + } + } + } +} diff --git a/kernel/networking/application_layer/http_webserver.h b/kernel/networking/application_layer/http_webserver.h new file mode 100644 index 00000000..439c75a4 --- /dev/null +++ b/kernel/networking/application_layer/http_webserver.h @@ -0,0 +1,94 @@ +#pragma once + +#include "http.h" +#include "csocket_http_server.h" +#include "net/socket_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct HTTPWebContext HTTPWebContext; +typedef int32_t (*HTTPWebHandler)(HTTPWebContext *ctx); + +typedef enum { + HTTP_ROUTE_EXACT = 0, + HTTP_ROUTE_PREFIX = 1u << 0, + HTTP_ROUTE_HEAD_AS_GET = 1u << 1 +} HTTPRouteFlags; + +typedef enum { + HTTP_ROUTE_HANDLER, + HTTP_ROUTE_STATIC, + HTTP_ROUTE_FILE +} HTTPRouteKind; + +typedef struct { + HttpError status; + const char *content_type; + const void *body; + uint32_t body_len; + const HTTPHeader *headers; + uint32_t header_count; +} HTTPWebResponse; + +typedef struct { + const char *fs_path; + const char *content_type; + uint32_t max_bytes; + uint32_t cache_max_age_sec; + const HTTPHeader *headers; + uint32_t header_count; +} HTTPWebFile; + +typedef struct { + const char *path; + uint32_t methods; + uint32_t flags; + HTTPRouteKind kind; + void *user; + union { + HTTPWebHandler handler; + HTTPWebResponse response; + HTTPWebFile file; + } as; +} HTTPRoute; + +typedef struct { + uint16_t port; + int backlog; + const SocketOptions *socket_options; + const HTTPServerPolicyOptions *policy_options; + const HTTPRoute *routes; + uint32_t route_count; + HTTPWebResponse not_found; + void *user; + const char *mdns_instance; + const char *mdns_type; + const char *mdns_proto; + const char *mdns_txt; + bool close_each_response; + bool head_as_get; + bool options_for_any_path; +} HTTPWebServerConfig; + +struct HTTPWebContext { + http_server_handle_t server; + http_connection_handle_t conn; + HTTPRequestMsg *request; + const HTTPRoute *route; + const HTTPWebServerConfig *config; + void *user; + bool close_after_response; +}; + +#define HTTP_WEB_RESPONSE(code_, type_, body_, body_len_) (HTTPWebResponse){code_, type_, body_, body_len_, NULL, 0} +#define HTTP_WEB_TEXT_RESPONSE(code_, text_) (HTTPWebResponse){code_, "text/plain", text_, sizeof(text_) - 1, NULL, 0} +#define HTTP_WEB_HTML_RESPONSE(code_, html_) (HTTPWebResponse){code_, "text/html", html_, sizeof(html_) - 1, NULL, 0} + +int32_t http_webserver_run(const HTTPWebServerConfig *config); +int32_t http_web_send(HTTPWebContext *ctx, const HTTPWebResponse *response); + +#ifdef __cplusplus +} +#endif diff --git a/kernel/networking/interface_manager.c b/kernel/networking/interface_manager.c index 6850286d..c537fca3 100644 --- a/kernel/networking/interface_manager.c +++ b/kernel/networking/interface_manager.c @@ -555,6 +555,8 @@ 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){ l2_interface_t *l2 = l2_interface_find_by_index(ifindex); if (!l2) return 0; + bool had_active_v6 = l2_has_active_v6(l2); + uint8_t pre_mcast_count = l2->ipv6_mcast_count; if (prefix_len > 128) return 0; int placeholder_ll = 0; @@ -680,7 +682,7 @@ uint8_t l3_ipv6_add_to_interface(uint8_t ifindex, const uint8_t ip[16], uint8_t l2->l3_v6[loc] = n; l2->ipv6_count++; - if (cfg == IPV6_CFG_DHCPV6){ + if (cfg & IPV6_CFG_DHCPV6){ uint8_t m[16]; ipv6_make_multicast(2, IPV6_MCAST_DHCPV6_SERVERS, NULL, m); (void)l2_ipv6_mcast_join(ifindex, m); @@ -701,7 +703,7 @@ uint8_t l3_ipv6_add_to_interface(uint8_t ifindex, const uint8_t ip[16], uint8_t (void)l2_ipv6_mcast_join(ifindex, m); } } - if (l2->kind != NET_IFK_LOCALHOST && l2_has_active_v6(l2)) for (int i = 0; i < (int)l2->ipv6_mcast_count; ++i) mld_send_join(l2->ifindex, l2->ipv6_mcast[i]); + if (!had_active_v6 && l2->kind != NET_IFK_LOCALHOST && l2_has_active_v6(l2)) for (int i = 0; i < (int)pre_mcast_count && i < (int)l2->ipv6_mcast_count; ++i) mld_send_join(l2->ifindex, l2->ipv6_mcast[i]); return n->l3_id; } @@ -711,6 +713,8 @@ bool l3_ipv6_update(uint8_t l3_id, const uint8_t ip[16], uint8_t prefix_len, con if (!n) return false; l2_interface_t *l2 = n->l2; if (!l2) return false; + bool had_active_v6 = l2_has_active_v6(l2); + uint8_t pre_mcast_count = l2->ipv6_mcast_count; if (prefix_len > 128) return false; if (kind == n->kind && cfg == n->cfg && prefix_len == n->prefix_len && ipv6_cmp(ip, n->ip) == 0 && ipv6_cmp(gw, n->gateway) == 0) return true; @@ -783,12 +787,12 @@ bool l3_ipv6_update(uint8_t l3_id, const uint8_t ip[16], uint8_t prefix_len, con (void)l2_ipv6_mcast_join(l2->ifindex, m); } - if (cfg == IPV6_CFG_DHCPV6){ + if (cfg & IPV6_CFG_DHCPV6){ ipv6_make_multicast(2, IPV6_MCAST_DHCPV6_SERVERS, NULL, m); (void)l2_ipv6_mcast_join(l2->ifindex, m); } - if (cfg == IPV6_CFG_SLAAC){ + if (cfg & IPV6_CFG_SLAAC){ ipv6_make_multicast(2, IPV6_MCAST_ALL_ROUTERS, NULL, m); (void)l2_ipv6_mcast_join(l2->ifindex, m); } @@ -840,7 +844,7 @@ bool l3_ipv6_update(uint8_t l3_id, const uint8_t ip[16], uint8_t prefix_len, con n->routing_table = NULL; } } - if (l2->kind != NET_IFK_LOCALHOST && l2_has_active_v6(l2)) for (int i = 0; i < (int)l2->ipv6_mcast_count; ++i) mld_send_join(l2->ifindex, l2->ipv6_mcast[i]); + if (!had_active_v6 && l2->kind != NET_IFK_LOCALHOST && l2_has_active_v6(l2)) for (int i = 0; i < (int)pre_mcast_count && i < (int)l2->ipv6_mcast_count; ++i) mld_send_join(l2->ifindex, l2->ipv6_mcast[i]); if (l3_changed) n->epoch = net_interface_mark_changed(); return true; @@ -1003,16 +1007,13 @@ void ifmgr_autoconfig_l2(uint8_t ifindex){ ipv6_make_lla_from_mac(ifindex, lla); (void)l3_ipv6_add_to_interface(ifindex, lla, 64, (const uint8_t[16]){0}, IPV6_CFG_SLAAC, IPV6_ADDRK_LINK_LOCAL); - uint8_t m[16]; - ipv6_make_multicast(2, IPV6_MCAST_ALL_NODES, lla, m); - (void)l2_ipv6_mcast_join(ifindex, m); } if (!has_gua) { uint8_t ph[16]; ipv6_make_placeholder_gua(ph); - (void)l3_ipv6_add_to_interface(ifindex, ph, 64, (const uint8_t[16]){0}, IPV6_CFG_SLAAC, IPV6_ADDRK_GLOBAL); + (void)l3_ipv6_add_to_interface(ifindex, ph, 64, (const uint8_t[16]){0}, IPV6_CFG_STATELESS, IPV6_ADDRK_GLOBAL); } } diff --git a/kernel/networking/link_layer/ndp.c b/kernel/networking/link_layer/ndp.c index b9f8b4cd..e8c4d6b4 100644 --- a/kernel/networking/link_layer/ndp.c +++ b/kernel/networking/link_layer/ndp.c @@ -215,7 +215,8 @@ static void apply_ra_policy(uint32_t now_ms, l2_interface_t* l2) { v6->dhcpv6_stateless = o ? 1 : 0; v6->dhcpv6_stateless_done = 0; - if (v6->cfg != IPV6_CFG_SLAAC) { + ipv6_cfg_t ra_cfg = o ? IPV6_CFG_STATELESS : IPV6_CFG_SLAAC; + if (v6->cfg != ra_cfg) { uint8_t ph[16]; uint8_t gw[16]; @@ -224,7 +225,7 @@ static void apply_ra_policy(uint32_t now_ms, l2_interface_t* l2) { if (v6->ra_is_default) ipv6_cpy(gw, v6->gateway); else memset(gw, 0, 16); - (void)l3_ipv6_update(v6->l3_id, ph, 64, gw, IPV6_CFG_SLAAC, v6->kind); + (void)l3_ipv6_update(v6->l3_id, ph, 64, gw, ra_cfg, v6->kind); } if (ipv6_is_placeholder_gua(v6->ip)) { @@ -235,7 +236,7 @@ static void apply_ra_policy(uint32_t now_ms, l2_interface_t* l2) { ipv6_cpy(ip, v6->prefix); memcpy(ip + 8, iid, 8); - (void)l3_ipv6_update(v6->l3_id, ip, 64, v6->gateway, IPV6_CFG_SLAAC, v6->kind); + (void)l3_ipv6_update(v6->l3_id, ip, 64, v6->gateway, ra_cfg, v6->kind); v6->timestamp_created = now_ms; memcpy(v6->interface_id, ip + 8, 8); @@ -248,7 +249,7 @@ static void apply_ra_policy(uint32_t now_ms, l2_interface_t* l2) { if (v6->ra_is_default) ipv6_cpy(gw, v6->gateway); else memset(gw, 0, 16); - (void)l3_ipv6_update(v6->l3_id, v6->ip, v6->prefix_len, gw, IPV6_CFG_SLAAC, v6->kind); + (void)l3_ipv6_update(v6->l3_id, v6->ip, v6->prefix_len, gw, ra_cfg, v6->kind); v6->timestamp_created = now_ms; memcpy(v6->interface_id, v6->ip + 8, 8); @@ -297,7 +298,7 @@ static void ndp_on_ra(uint8_t ifindex, const uint8_t router_ip[16], uint16_t rou uint8_t ph[16]; ipv6_make_placeholder_gua(ph); - uint8_t id = l3_ipv6_add_to_interface(ifindex, ph, 64, (const uint8_t[16]){0}, IPV6_CFG_SLAAC, IPV6_ADDRK_GLOBAL); + uint8_t id = l3_ipv6_add_to_interface(ifindex, ph, 64, (const uint8_t[16]){0}, (ra_flags & RA_FLAG_O) ? IPV6_CFG_STATELESS : IPV6_CFG_SLAAC, IPV6_ADDRK_GLOBAL); if (!id) return; slot = l3_ipv6_find_by_id(id); diff --git a/kernel/networking/processes/net_proc.c b/kernel/networking/processes/net_proc.c index 80cb9d89..b70e6f4d 100644 --- a/kernel/networking/processes/net_proc.c +++ b/kernel/networking/processes/net_proc.c @@ -18,7 +18,7 @@ #include "networking/transport_layer/trans_utils.h" #include "networking/application_layer/csocket_http_client.h" -#include "networking/application_layer/csocket_http_server.h" +#include "networking/application_layer/http_webserver.h" #include "networking/application_layer/dhcp_daemon.h" #include "networking/application_layer/dns/dns_daemon.h" #include "networking/application_layer/dns/mdns_responder.h" @@ -93,91 +93,72 @@ static int net_has_ready_address(void) { } static void run_http_server() { - uint32_t http_methods = (1 << HTTP_METHOD_GET) | (1 << HTTP_METHOD_HEAD) | (1 << HTTP_METHOD_OPTIONS); - HTTPServerPolicyOptions http_options = {0}; - http_options.flags = HTTP_SERVER_OPT_ALLOWED_METHODS; - http_options.value.allowed_methods = http_methods; - - http_server_handle_t srv = http_server_create(NULL, &http_options); - if (!srv) { - stop_current_process(1); - return; - } - struct SockBindSpec spec = {0}; - spec.kind = BIND_ANY; - if (http_server_bind(srv, &spec, HTTP_PORT) < 0) { - http_server_destroy(srv); - stop_current_process(2); - return; - } - - if (http_server_listen(srv, 4) < 0) { - http_server_close(srv); - http_server_destroy(srv); - stop_current_process(3); - return; - } - - mdns_register_service("RedactedOS", "http", "tcp", HTTP_PORT, "path=/"); - - static char HTML_ROOT[] = + static const char HTTP_ROOT_BODY[] = + "\n" + "RedactedOS\n" + "\n" + "\n" + "\n" "

Hello, world!

\n" - "

[Redacted]

"; + "

[Redacted]

" + "

\n" + "

download test

\n" + "\n"; static char HTML_404[] = "

404 Regrettably, no such page exists in this realm

\n" "

Im rather inclined to deduce that your page simply does not exist. Given the state of affairs, I dare say it's not altogether surprising, innit?

"; - while (1) { - http_connection_handle_t conn = http_server_accept(srv); - if (!conn){ - msleep(50); - continue; - } - HTTPRequestMsg req = http_server_recv_request(srv, conn); - if (!req.path.length) { - http_connection_close(conn); - continue; - } - - if (req.method == HTTP_METHOD_OPTIONS) { - static char allow_key[] = "Allow"; - string allow_value = http_methods_allow_header(http_methods); - HTTPHeader allow_header = { - {allow_key, sizeof(allow_key) - 1, 0}, - allow_value - }; - - HTTPResponseMsg res = (HTTPResponseMsg){0}; - res.status_code = HTTP_OK; - res.headers_common.fields.content_length = 0; - res.headers_common.framing.has_content_length = 1; - res.extra_headers = &allow_header; - res.extra_header_count = 1; - - http_server_send_response(srv, conn, &res); - string_free(allow_value); - http_connection_close(conn); - http_request_free(&req); - continue; - } - - int is_root = req.path.length == 1 && req.path.data[0] == '/'; - char *body = is_root ? HTML_ROOT : HTML_404; - uint32_t len = (uint32_t)strlen(body); - - HTTPResponseMsg res = (HTTPResponseMsg){0}; - res.status_code = is_root ? HTTP_OK : HTTP_NOT_FOUND; - res.headers_common.fields.content_length = len; - res.headers_common.framing.has_content_length = 1; - res.headers_common.fields.content_type = string_from_const("text/html"); - res.body = (string){body, len, 0}; - - http_server_send_response(srv, conn, &res); - http_connection_close(conn); - http_request_free(&req); - http_response_free(&res); - } + static const HTTPRoute routes[] = { + { + .path = "/", + .methods = HTTP_METHOD_MASK_GET, + .flags = HTTP_ROUTE_HEAD_AS_GET, + .kind = HTTP_ROUTE_STATIC, + .as.response = { + .status = HTTP_OK, + .content_type = "text/html", + .body = HTTP_ROOT_BODY, + .body_len = sizeof(HTTP_ROOT_BODY) - 1 + } + }, + { + .path = "/assets/test.png", + .methods = HTTP_METHOD_MASK_GET, + .flags = HTTP_ROUTE_HEAD_AS_GET, + .kind = HTTP_ROUTE_FILE, + .as.file = { + .fs_path = "/boot/redos/system/demo.red/resources/test.png", + .content_type = "image/png", + .cache_max_age_sec = 60 + } + }, + { + .path = "/favicon.ico", + .methods = HTTP_METHOD_MASK_GET, + .flags = HTTP_ROUTE_HEAD_AS_GET, + .kind = HTTP_ROUTE_FILE, + .as.file = { + .fs_path = "/boot/redos/system/demo.red/resources/test.png", + .content_type = "image/png", + .cache_max_age_sec = 60 + } + }, + }; + + HTTPWebServerConfig config = {0}; + config.port = HTTP_PORT; + config.backlog = 4; + config.routes = routes; + config.route_count = N_ARR(routes); + config.not_found = HTTP_WEB_HTML_RESPONSE(HTTP_NOT_FOUND, HTML_404); + config.mdns_instance = "RedactedOS"; + config.mdns_type = "http"; + config.mdns_proto = "tcp"; + config.mdns_txt = "path=/"; + + http_webserver_run(&config); + stop_current_process(0); } static void test_http(const net_l4_endpoint* ep) { diff --git a/kernel/networking/transport_layer/csocket_tcp.c b/kernel/networking/transport_layer/csocket_tcp.c index 531ddd04..03717623 100644 --- a/kernel/networking/transport_layer/csocket_tcp.c +++ b/kernel/networking/transport_layer/csocket_tcp.c @@ -13,6 +13,7 @@ #include "syscalls/syscalls.h" #include "std/memory.h" #include "alloc/allocate.h" +#include "exceptions/irq.h" #define TCP_MAX_BACKLOG 128 #define TCP_DEFAULT_SOCKET_BUF (256 * 1024) @@ -32,13 +33,29 @@ typedef struct tcp_socket { bool listening; } tcp_socket_t; +static int tcp_socket_backlog_len(tcp_socket_t* s) { + if (!s) return 0; + + irq_flags_t irq = irq_save_disable(); + int len = s->backlogLen; + irq_restore(irq); + return len; +} + static ksocket_t* tcp_socket_pop_pending_at(tcp_socket_t* s, int idx) { - if (!s || idx < 0 || idx >= s->backlogLen) return NULL; + if (!s) return NULL; + + irq_flags_t irq = irq_save_disable(); + if (idx < 0 || idx >= s->backlogLen) { + irq_restore(irq); + return NULL; + } ksocket_t* client = s->pending[idx]; for (int i = idx + 1; i < s->backlogLen; ++i) s->pending[i-1] = s->pending[i]; s->pending[--s->backlogLen] = NULL; + irq_restore(irq); return client; } @@ -340,56 +357,25 @@ ksocket_t* socket_accept_tcp(socket_impl_t sh) { if (!s || !s->listening || !s->pending) return NULL; const int max_empty_iters = 200; - const int ready_wait_iters = 4; int iter = 0; - while (s->backlogLen == 0) { + while (tcp_socket_backlog_len(s) == 0) { if (++iter > max_empty_iters) return NULL; msleep(5); } - iter = 0; - while (1) { - for (int i = 0; i < s->backlogLen;) { - ksocket_t* owner = s->pending[i]; - tcp_socket_t* client = owner ? (tcp_socket_t*)socket_core_impl(owner) : NULL; - - if (client && client->flow.flow_generation) { - bool readable = tcp_flow_readable(&client->flow) != 0; - bool closed = tcp_flow_recv_closed(&client->flow); - if (!closed || readable) { - ++i; - continue; - } - } - - owner = tcp_socket_pop_pending_at(s, i); - if (owner) { - socket_core_close_socket(owner); - socket_core_put(owner); - } - } + while (tcp_socket_backlog_len(s) > 0) { + ksocket_t* owner = tcp_socket_pop_pending_at(s, 0); + tcp_socket_t* client = owner ? (tcp_socket_t*)socket_core_impl(owner) : NULL; - if (s->backlogLen == 0) return NULL; + if (client && client->flow.flow_generation) return owner; - for (int i = 0; i < s->backlogLen; ++i) { - ksocket_t* owner = s->pending[i]; - tcp_socket_t* client = owner ? (tcp_socket_t*)socket_core_impl(owner) : NULL; - if (!client) continue; - if (!client->flow.flow_generation || tcp_flow_readable(&client->flow) || tcp_flow_recv_closed(&client->flow)) return tcp_socket_pop_pending_at(s, i); - } - - if (++iter > ready_wait_iters) { - if (s->backlogLen >= s->backlogCap) { - ksocket_t* owner = tcp_socket_pop_pending_at(s, 0); - if (owner) { - socket_core_close_socket(owner); - socket_core_put(owner); - } - } - return NULL; + if (owner) { + socket_core_close_socket(owner); + socket_core_put(owner); } - msleep(5); } + + return NULL; } int32_t socket_connect_tcp(socket_impl_t sh, const net_l4_endpoint* dst) { @@ -641,9 +627,14 @@ uint32_t tcp_accept_enqueue(ksocket_t* listener, ip_version_t ipver, const void* if (!s) return 0; if (!s->listening || !s->localPort || s->localPort != dst_port || !s->pending) return 0; - for (int i = 0; i < s->backlogLen;) { - ksocket_t* owner = s->pending[i]; - tcp_socket_t* client = owner ? (tcp_socket_t*)socket_core_impl(owner) : NULL; + for (int i = 0;;) { + irq_flags_t irq = irq_save_disable(); + ksocket_t* owner = (i >= 0 && i < s->backlogLen) ? s->pending[i] : NULL; + irq_restore(irq); + + if (!owner) break; + + tcp_socket_t* client = (tcp_socket_t*)socket_core_impl(owner); if (client && client->flow.flow_generation) { bool readable = tcp_flow_readable(&client->flow) != 0; bool closed = tcp_flow_recv_closed(&client->flow); @@ -659,7 +650,7 @@ uint32_t tcp_accept_enqueue(ksocket_t* listener, ip_version_t ipver, const void* socket_core_put(owner); } } - if (s->backlogLen >= s->backlogCap) return 0; + if (tcp_socket_backlog_len(s) >= s->backlogCap) return 0; ksocket_t* child_owner = NULL; uint16_t owner_pid = socket_core_pid(s->ownerSocket); @@ -696,6 +687,15 @@ uint32_t tcp_accept_enqueue(ksocket_t* listener, ip_version_t ipver, const void* child->connected = true; socket_core_ref(child_owner); + + irq_flags_t irq = irq_save_disable(); + if (s->backlogLen >= s->backlogCap) { + irq_restore(irq); + socket_core_close_socket(child_owner); + socket_core_put(child_owner); + return 0; + } s->pending[s->backlogLen++] = child_owner; + irq_restore(irq); return 1; } diff --git a/kernel/networking/transport_layer/tcp/tcp_core.c b/kernel/networking/transport_layer/tcp/tcp_core.c index 60b4994f..e8329d25 100644 --- a/kernel/networking/transport_layer/tcp/tcp_core.c +++ b/kernel/networking/transport_layer/tcp/tcp_core.c @@ -566,25 +566,35 @@ bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, tcp_daemon_kick(); + uint64_t start_ms = get_time(); uint64_t waited = 0; const uint64_t interval = 50; - const uint64_t max_wait = TCP_CONNECT_TIMEOUT_MS; + uint64_t max_wait = TCP_CONNECT_TIMEOUT_MS; + if (extra && (extra->flags & SOCK_OPT_SEND_TIMEOUT) && extra->send_timeout_ms) max_wait = extra->send_timeout_ms; while (waited < max_wait){ - if (flow->base.state == TCP_ESTABLISHED || flow->base.state == TCP_CLOSE_WAIT){ - *flow_ctx = flow->base.ctx; + tcp_data ctx_snapshot; + memset(&ctx_snapshot, 0, sizeof(ctx_snapshot)); + + irq_flags_t irq = irq_save_disable(); + tcp_state_t state = flow->base.state; + ctx_snapshot = flow->base.ctx; + irq_restore(irq); + + if (state == TCP_ESTABLISHED || state == TCP_CLOSE_WAIT){ + *flow_ctx = ctx_snapshot; tcp_flow_put(flow); return true; } - if (flow->base.state == TCP_STATE_CLOSED){ + if (state == TCP_STATE_CLOSED){ tcp_free_flow(idx); tcp_flow_put(flow); return false; } msleep(interval); - waited += interval; + waited = get_time() - start_ms; } tcp_free_flow(idx); diff --git a/kernel/networking/transport_layer/tcp/tcp_rx.c b/kernel/networking/transport_layer/tcp/tcp_rx.c index 8efdf971..cf1d004e 100644 --- a/kernel/networking/transport_layer/tcp/tcp_rx.c +++ b/kernel/networking/transport_layer/tcp/tcp_rx.c @@ -709,10 +709,12 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a netpkt_unref(pkt); return; } - } else if (ack == flow->tx.snd_una && data_len == 0 && !fin){ - if (flow->tx.sack_ok && parsed_opts.sack_count) tcp_apply_sack_blocks(flow, &parsed_opts); - if (flow->tx.dup_acks < UINT8_MAX) flow->tx.dup_acks++; - tcp_cc_on_dupack(flow); + } else if (ack == flow->tx.snd_una && data_len == 0 && !fin && !(flags & (1u << SYN_F))){ + int sack_changed = flow->tx.sack_ok && parsed_opts.sack_count ? tcp_apply_sack_blocks(flow, &parsed_opts) : 0; + if (TCP_SEQ_LT(flow->tx.snd_una, flow->tx.snd_nxt) && (new_wnd == old_wnd || sack_changed)) { + if (flow->tx.dup_acks < UINT8_MAX) flow->tx.dup_acks++; + tcp_cc_on_dupack(flow); + } } else { flow->tx.dup_acks = 0; } @@ -887,6 +889,14 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a } if (flags & (1u << RST_F)) { + bool rst_acceptable = flow->rx.rcv_wnd ? (TCP_SEQ_GEQ(seq, flow->rx.rcv_nxt) && TCP_SEQ_LT(seq, flow->rx.rcv_nxt + flow->rx.rcv_wnd)) : (seq == flow->rx.rcv_nxt); + if (!rst_acceptable) { + tcp_send_ack_now(flow); + tcp_flow_put(flow); + netpkt_unref(pkt); + return; + } + tcp_free_flow(idx); tcp_flow_put(flow); netpkt_unref(pkt); diff --git a/kernel/networking/transport_layer/tcp/tcp_tx.c b/kernel/networking/transport_layer/tcp/tcp_tx.c index 5bd62c5b..74bcfe3b 100644 --- a/kernel/networking/transport_layer/tcp/tcp_tx.c +++ b/kernel/networking/transport_layer/tcp/tcp_tx.c @@ -471,7 +471,8 @@ tcp_result_t tcp_flow_send(tcp_data *flow_ctx){ if (flow->tx.nagle_len >= tcp_nagle_threshold(flow)) tcp_flush_nagle(flow, 1); } - if (rc == TCP_OK && !flow->tx.nodelay && payload_len && payload_len < tcp_nagle_threshold(flow)) { + uint64_t in_flight = TCP_SEQ_GT(flow->tx.snd_nxt, flow->tx.snd_una) ? flow->tx.snd_nxt - flow->tx.snd_una : 0; + if (rc == TCP_OK && !flow->tx.nodelay && payload_len && payload_len < tcp_nagle_threshold(flow) && in_flight != 0) { uint64_t n = tcp_nagle_append(flow, payload_ptr, payload_len); accepted += n; payload_ptr += n; diff --git a/shared b/shared index bfef207c..e045992f 160000 --- a/shared +++ b/shared @@ -1 +1 @@ -Subproject commit bfef207c4de843388b8fea5e421f851dfc642570 +Subproject commit e045992f815369db420d46be91f593656083fb6e From d99dad00da81c96bd2a2336f6bfab63ac69be7ae Mon Sep 17 00:00:00 2001 From: CodeAnarchist Date: Wed, 15 Jul 2026 01:35:21 +0200 Subject: [PATCH 33/39] [make/tcp/http] clean builds, flow tracking, range responses --- Makefile | 5 +- .../application_layer/http_webserver.c | 178 +++++++++++++----- .../networking/transport_layer/tcp/tcp_core.c | 103 +++++++--- .../transport_layer/tcp/tcp_internal.h | 6 +- .../transport_layer/tcp/tcp_limits.c | 5 +- .../networking/transport_layer/tcp/tcp_rx.c | 22 +-- .../transport_layer/tcp/tcp_timer.c | 5 +- .../networking/transport_layer/tcp/tcp_tx.c | 67 ++++--- modules/Makefile | 8 +- modules/MakefileModule | 11 +- shared | 2 +- 11 files changed, 271 insertions(+), 141 deletions(-) diff --git a/Makefile b/Makefile index 0dcf6cd5..ebb1e437 100644 --- a/Makefile +++ b/Makefile @@ -47,7 +47,10 @@ clean: $(MAKE) -C tools $@ $(MAKE) -C modules $@ @echo "removing images" - $(RM) kernel.img kernel.elf dump + $(RM) -f kernel.img kernel.elf disk.img dump trace serial.log gdb_net.log screenlog.0 dump.dtb + $(RM) -f logs/*.log fs/kernel.elf + $(RM) -rf fs/redos/tools fs/redos/system fs/redos/user fs/redos/docs .cache + @find ./bin ./user ./tools ./modules ./kernel ./shared -type f \( -name dump -o -name '*.dump' -o -name '*.trace' -o -name '*.log' -o -name '*.tmp' \) -exec rm -f {} + raspi: $(MAKE) LOAD_ADDR=0x80000 XHCI_CTX_SIZE=64 QEMU=true MODE=raspi all diff --git a/kernel/networking/application_layer/http_webserver.c b/kernel/networking/application_layer/http_webserver.c index ddb5c06d..e6c9fe2d 100644 --- a/kernel/networking/application_layer/http_webserver.c +++ b/kernel/networking/application_layer/http_webserver.c @@ -2,6 +2,7 @@ #include "filesystem/filesystem.h" #include "networking/application_layer/dns/mdns_responder.h" #include "networking/transport_layer/socket_bind.h" +#include "data/format/url.h" #include "std/memory.h" #include "std/string.h" #include "syscalls/syscalls.h" @@ -12,20 +13,11 @@ typedef struct { uint32_t methods; } HTTPWebRouteMatch; -static uint32_t http_web_path_len(string path) { - uint32_t len = path.length; - int32_t q = str_has_char(path.data, len, '?'); - if (q >= 0)len = (uint32_t)q; - int32_t f = str_has_char(path.data, len, '#'); - if (f >= 0)len = (uint32_t)f; - return len; -} - static HTTPWebRouteMatch http_web_find_route(const HTTPWebServerConfig *config, string path, HTTPMethod method) { HTTPWebRouteMatch match = {0}; if (!config || (!config->routes && config->route_count)) return match; - uint32_t path_len = http_web_path_len(path); + uint32_t path_len = url_path_len(path); uint32_t best_prefix_len = 0; uint32_t best_method_prefix_len = 0; uint32_t exact_methods = 0; @@ -69,40 +61,33 @@ static HTTPWebRouteMatch http_web_find_route(const HTTPWebServerConfig *config, return match; } -static int32_t http_web_send_blob(HTTPWebContext *ctx, const HTTPWebFile *file, const void *body, uint32_t body_len) { +static HTTPHeader *http_web_build_file_headers(const HTTPWebFile *file, string content_range, HTTPHeader local[8], uint32_t *out_count, string *out_cache) { static char cache_name[] = "Cache-Control"; - string cache = {0}; - HTTPHeader local[4]; - uint32_t base_count = file && file->headers ? file->header_count : 0; - uint32_t total = base_count; - - if (file && file->cache_max_age_sec) total++; - HTTPHeader *headers = total ? local : NULL; - if (total > N_ARR(local)) headers = (HTTPHeader*)zalloc(sizeof(HTTPHeader) * total); - if (total && !headers) { - HTTPWebResponse response = HTTP_WEB_TEXT_RESPONSE(HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error\n"); - return http_web_send(ctx, &response); - } + static char accept_ranges_name[] = "Accept-Ranges"; + static char accept_ranges_value[] = "bytes"; + static char content_range_name[] = "Content-Range"; + + *out_count = 0; + *out_cache = (string){0}; + uint32_t base_count = file->headers ? file->header_count : 0; + uint32_t total = base_count+1; - for (uint32_t i = 0; i < base_count; i++) headers[i] = file->headers[i]; - if (file && file->cache_max_age_sec) { - cache = string_format("public, max-age=%i", (int)file->cache_max_age_sec); - headers[base_count] = (HTTPHeader){{cache_name, sizeof(cache_name) - 1, 0}, cache}; + if (file->cache_max_age_sec) total++; + if (content_range.length) total++; + HTTPHeader *headers = total <= 8 ? local : (HTTPHeader*)zalloc(sizeof(HTTPHeader) * total); + if (!headers) return NULL; + uint32_t n = 0; + for (uint32_t i = 0; i < base_count; i++) headers[n++] = file->headers[i]; + headers[n++] = (HTTPHeader){{accept_ranges_name, sizeof(accept_ranges_name) - 1, 0}, {accept_ranges_value, sizeof(accept_ranges_value) - 1, 0}}; + if (file->cache_max_age_sec) { + *out_cache = string_format("public, max-age=%i", (int)file->cache_max_age_sec); + headers[n++] = (HTTPHeader){{cache_name, sizeof(cache_name) - 1, 0}, *out_cache}; } - HTTPWebResponse response = { - .status = HTTP_OK, - .content_type = file && file->content_type ? file->content_type : "application/octet-stream", - .body = body, - .body_len = body_len, - .headers = headers, - .header_count = total - }; - - int32_t rc = http_web_send(ctx, &response); - if (headers && headers != local) release(headers); - string_free(cache); - return rc; + if (content_range.length) headers[n++] = (HTTPHeader){{content_range_name, sizeof(content_range_name) - 1, 0}, content_range}; + + *out_count = n; + return headers; } int32_t http_web_send(HTTPWebContext *ctx, const HTTPWebResponse *response) { @@ -142,36 +127,129 @@ static int32_t http_web_send_allow(HTTPWebContext *ctx, HttpError status, uint32 return rc; } -static int32_t http_web_send_file(HTTPWebContext *ctx, const HTTPWebFile *file) { - if (!ctx || !file || !file->fs_path) return SOCK_ERR_INVAL; +static int32_t http_web_send_file(HTTPWebContext *ctx, const HTTPWebFile *web_file) { + if (!ctx || !web_file || !web_file->fs_path) return SOCK_ERR_INVAL; fs_stat st = {0}; - if (!get_stat(kernel_fs(), file->fs_path, &st) || st.type != entry_file) { + if (!get_stat(kernel_fs(), web_file->fs_path, &st) || st.type != entry_file) { HTTPWebResponse response = HTTP_WEB_TEXT_RESPONSE(HTTP_NOT_FOUND, "Not Found\n"); return http_web_send(ctx, &response); } - if (st.size > UINT32_MAX || (file->max_bytes && st.size > file->max_bytes)) { + if (st.size > UINT32_MAX || (web_file->max_bytes && st.size > web_file->max_bytes)) { HTTPWebResponse response = HTTP_WEB_TEXT_RESPONSE(HTTP_PAYLOAD_TOO_LARGE, "Payload Too Large\n"); return http_web_send(ctx, &response); } - uint32_t size = (uint32_t)st.size; + uint64_t file_size = st.size; + uint64_t range_start = 0; + uint64_t range_end = file_size ? file_size - 1 : 0; + uint64_t range_len = file_size; + bool partial = ctx->request->headers_common.range.has; + bool not_satisfiable = false; + + if (partial) { + const HTTPRangeSpec *in = &ctx->request->headers_common.range; + + if (in->invalid || file_size == 0 || (!in->has_start && !in->has_end)) not_satisfiable = true; + else if (in->has_start) { + range_start = in->start; + range_end = in->has_end && in->end < file_size ? in->end : file_size - 1; + not_satisfiable = range_start >= file_size || range_end < range_start; + } else { + uint64_t suffix = in->end; + not_satisfiable = suffix == 0; + if (!not_satisfiable) { + range_start = suffix >= file_size ? 0 : file_size - suffix; + range_end = file_size - 1; + } + } + + range_len = not_satisfiable ? 0 : range_end - range_start + 1; + } + + char content_range_buf[64]; + string content_range = {0}; + if (not_satisfiable) { + //print("aaa %s %llu", web_file->fs_path, file_size); + content_range.length = (uint32_t)string_format_buf(content_range_buf, sizeof(content_range_buf), "bytes */%llu", file_size); + content_range.data = content_range_buf; + + HTTPHeader local[8]; + uint32_t total = 0; + string cache = {0}; + HTTPHeader *headers = http_web_build_file_headers(web_file, content_range, local, &total, &cache); + if (!headers) { + HTTPWebResponse response = HTTP_WEB_TEXT_RESPONSE(HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error\n"); + return http_web_send(ctx, &response); + } + + HTTPResponseMsg res = {0}; + res.status_code = HTTP_RANGE_NOT_SATISFIABLE; + res.headers_common.fields.content_length = 0; + res.headers_common.framing.has_content_length = 1; + res.extra_headers = headers; + res.extra_header_count = total; + + int32_t rc = http_server_send_response(ctx->server, ctx->conn, &res); + if (headers != local) release(headers); + string_free(cache); + return rc; + } + + if (partial) { + //print("%s %llu-%llu %llu", web_file->fs_path, range_start, range_end, file_size); + content_range.length = (uint32_t)string_format_buf(content_range_buf, sizeof(content_range_buf), "bytes %llu-%llu/%llu", range_start, range_end, file_size); + content_range.data = content_range_buf; + } + + uint32_t read_len = (uint32_t)range_len; uint8_t *buf = NULL; - bool head = ctx->request && ctx->request->method == HTTP_METHOD_HEAD; - if (size && !head) { - buf = (uint8_t*)zalloc(size); + bool head = ctx->request->method == HTTP_METHOD_HEAD; + if (read_len && !head) { + buf = (uint8_t*)zalloc(read_len); if (!buf) { HTTPWebResponse response = HTTP_WEB_TEXT_RESPONSE(HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error\n"); return http_web_send(ctx, &response); } - if (simple_read(kernel_fs(), file->fs_path, buf, size) != size) { + file fd = {0}; + FS_RESULT ores = open_file(kernel_fs(), web_file->fs_path, &fd); + if (ores != FS_RESULT_SUCCESS) { + release(buf); + HTTPWebResponse response = HTTP_WEB_TEXT_RESPONSE(HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error\n"); + return http_web_send(ctx, &response); + } + fd.cursor = range_start; + size_t got = read_file(&fd, (char*)buf, read_len); + close_file(&fd); + if (got != read_len) { release(buf); HTTPWebResponse response = HTTP_WEB_TEXT_RESPONSE(HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error\n"); return http_web_send(ctx, &response); } } - int32_t rc = http_web_send_blob(ctx, file, buf, size); + HTTPHeader local[8]; + uint32_t total = 0; + string cache = {0}; + HTTPHeader *headers = http_web_build_file_headers(web_file, content_range, local, &total, &cache); + if (!headers) { + if (buf) release(buf); + HTTPWebResponse response = HTTP_WEB_TEXT_RESPONSE(HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error\n"); + return http_web_send(ctx, &response); + } + + HTTPWebResponse response = { + .status = partial ? HTTP_PARTIAL_CONTENT : HTTP_OK, + .content_type = web_file->content_type ? web_file->content_type : "application/octet-stream", + .body = buf, + .body_len = read_len, + .headers = headers, + .header_count = total + }; + + int32_t rc = http_web_send(ctx, &response); + if (headers != local) release(headers); + string_free(cache); if (buf) release(buf); return rc; } diff --git a/kernel/networking/transport_layer/tcp/tcp_core.c b/kernel/networking/transport_layer/tcp/tcp_core.c index e8329d25..d8c11b02 100644 --- a/kernel/networking/transport_layer/tcp/tcp_core.c +++ b/kernel/networking/transport_layer/tcp/tcp_core.c @@ -15,14 +15,63 @@ tcp_flow_t *tcp_flows[MAX_TCP_FLOWS]; uint16_t tcp_active_flows[MAX_TCP_FLOWS]; uint16_t tcp_active_count; static uint32_t tcp_generation_next = 1; +static uint16_t tcp_alloc_cursor; + +static uint16_t tcp_active_port_lower_bound(uint16_t local_port) { + uint16_t lo = 0; + uint16_t hi = tcp_active_count; + + while (lo < hi) { + uint16_t mid = lo + ((hi - lo) >> 1); + tcp_flow_t *flow = tcp_flows[tcp_active_flows[mid]]; + if (flow->base.local_port < local_port) lo = (uint16_t)(mid + 1); + else hi = mid; + } + + return lo; +} + +void tcp_active_insert_flow(tcp_flow_t *flow) { + if (!flow) return; + + irq_flags_t irq = irq_save_disable(); + uint16_t slot = flow->base.slot; + if (slot >= MAX_TCP_FLOWS || tcp_flows[slot] != flow || flow->base.retired || tcp_active_count >= MAX_TCP_FLOWS) { + irq_restore(irq); + return; + } + + uint16_t local_port = flow->base.local_port; + uint16_t pos = tcp_active_port_lower_bound(local_port); + while (pos < tcp_active_count) { + uint16_t cur_slot = tcp_active_flows[pos]; + tcp_flow_t *cur = cur_slot < MAX_TCP_FLOWS ? tcp_flows[cur_slot] : NULL; + uint16_t cur_port = cur ? cur->base.local_port : UINT16_MAX; + if (cur_port != local_port || cur_slot > slot) break; + pos++; + } + + for (uint16_t i = tcp_active_count; i > pos; i--) { + uint16_t moved_slot = tcp_active_flows[i-1]; + tcp_active_flows[i] = moved_slot; + if (moved_slot < MAX_TCP_FLOWS && tcp_flows[moved_slot]) tcp_flows[moved_slot]->base.active_pos = i; + } + + tcp_active_flows[pos] = slot; + flow->base.active_pos = pos; + tcp_active_count++; + irq_restore(irq); +} tcp_flow_t *tcp_flow_acquire_match(uint16_t local_port, ip_version_t ver, const void *local_ip, const void *remote_ip, uint16_t remote_port, int *out_idx){ irq_flags_t irq = irq_save_disable(); - for (uint16_t n = 0; n < tcp_active_count; n++){ + uint16_t start = tcp_active_port_lower_bound(local_port); + for (uint16_t n = start; n < tcp_active_count; n++){ int i = tcp_active_flows[n]; - tcp_flow_t *f = tcp_flows[i]; + tcp_flow_t *f = i < MAX_TCP_FLOWS ? tcp_flows[i] : NULL; if (!f) continue; + if (f->base.local_port > local_port) break; if (f->base.retired || f->base.state == TCP_STATE_CLOSED) continue; if (f->base.local_port != local_port) continue; @@ -142,18 +191,7 @@ void tcp_flow_put(tcp_flow_t *f) { if (!free_now) return; - for (int i = 0; i < TCP_MAX_TX_SEGS; i++) { - tcp_tx_seg_t *s = &f->tx.txq[i]; - if (s->buf && s->len) { - uintptr_t buf = s->buf; - uint32_t len = (uint32_t)s->len; - s->buf = 0; - s->len = 0; - tcp_account_tx_remove(f, len); - release((void*)buf); - } - memset(s, 0, sizeof(*s)); - } + for (int i = 0; i < TCP_MAX_TX_SEGS; i++) tcp_tx_seg_clear(f, &f->tx.txq[i]); f->tx.queued_bytes = 0; @@ -195,12 +233,13 @@ void tcp_enter_time_wait(tcp_flow_t *flow) { uint32_t oldest_ms = 0; irq_flags_t irq = irq_save_disable(); - for (int i = 0; i < MAX_TCP_FLOWS; i++) { - tcp_flow_t *f = tcp_flows[i]; + for (uint16_t n = 0; n < tcp_active_count; n++) { + uint16_t slot = tcp_active_flows[n]; + tcp_flow_t *f = slot < MAX_TCP_FLOWS ? tcp_flows[slot] : NULL; if (!f || f->base.retired || f->base.state != TCP_TIME_WAIT) continue; timewait++; if ((oldest < 0 || f->timer.time_wait_ms > oldest_ms) && f != flow) { - oldest = i; + oldest = slot; oldest_ms = f->timer.time_wait_ms; } } @@ -222,9 +261,11 @@ tcp_flow_t *tcp_alloc_flow(void){ irq_flags_t irq = irq_save_disable(); int slot = -1; - for (int i = 0; i < MAX_TCP_FLOWS; i++) { + for (int n = 0; n < MAX_TCP_FLOWS; n++) { + uint16_t i = (uint16_t)((tcp_alloc_cursor+n) % MAX_TCP_FLOWS); if (!tcp_flows[i]) { slot = i; + tcp_alloc_cursor = (uint16_t)((i + 1) % MAX_TCP_FLOWS); break; } } @@ -236,8 +277,6 @@ tcp_flow_t *tcp_alloc_flow(void){ } tcp_flows[slot] = f; - f->base.active_pos = tcp_active_count; - tcp_active_flows[tcp_active_count++] = (uint16_t)slot; f->base.slot = (uint16_t)slot; f->base.generation = tcp_generation_next++; if (!tcp_generation_next) tcp_generation_next = 1; @@ -268,22 +307,30 @@ void tcp_free_flow(int idx) { f->base.retired = 1; f->base.state = TCP_STATE_CLOSED; - tcp_flows[idx] = NULL; if (tcp_active_count != 0) { uint16_t pos = f->base.active_pos; - if (pos >= tcp_active_count || tcp_active_flows[pos] != idx) for (pos = 0; pos < tcp_active_count && tcp_active_flows[pos] != idx; pos++); + if (pos >= tcp_active_count || tcp_active_flows[pos] != (uint16_t)idx) { + pos = 0; + while (pos < tcp_active_count && tcp_active_flows[pos] != (uint16_t)idx) pos++; + } + if (pos < tcp_active_count) { - uint16_t last = tcp_active_flows[--tcp_active_count]; - tcp_active_flows[pos] = last; + for (uint16_t i = pos; i + 1 < tcp_active_count; i++) { + uint16_t moved = tcp_active_flows[i+1]; + tcp_active_flows[i] = moved; + if (moved < MAX_TCP_FLOWS && tcp_flows[moved]) tcp_flows[moved]->base.active_pos = i; + } + tcp_active_count--; tcp_active_flows[tcp_active_count] = 0; - - if (last != idx && tcp_flows[last]) tcp_flows[last]->base.active_pos = pos; } f->base.active_pos = 0; } + + tcp_flows[idx] = NULL; + if ((uint16_t)idx < tcp_alloc_cursor) tcp_alloc_cursor = (uint16_t)idx; irq_restore(irq); tcp_flow_put(f); @@ -436,6 +483,7 @@ bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, flow->base.local_port = local_port; flow->base.l3_id = l3_id; + tcp_active_insert_flow(flow); flow->base.remote.ver = dst->ver; memcpy(flow->base.remote.ip, dst->ip, (size_t)(dst->ver == IP_VER6 ? 16 : 4)); @@ -548,7 +596,8 @@ bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, seg->retransmit_cnt = 0; seg->seq = flow->tx.snd_nxt; seg->len = 0; - seg->buf = 0; + seg->pkt = NULL; + seg->payload_off = 0; seg->timer_ms = 0; seg->timeout_ms = flow->tx.rto ? flow->tx.rto : TCP_INIT_RTO; diff --git a/kernel/networking/transport_layer/tcp/tcp_internal.h b/kernel/networking/transport_layer/tcp/tcp_internal.h index 5c7bb150..831db16d 100644 --- a/kernel/networking/transport_layer/tcp/tcp_internal.h +++ b/kernel/networking/transport_layer/tcp/tcp_internal.h @@ -45,7 +45,8 @@ typedef struct { uint8_t opts[40]; uint32_t seq; uint64_t len; - uintptr_t buf; + netpkt_t *pkt; + uint32_t payload_off; uint32_t timer_ms; uint32_t timeout_ms; } tcp_tx_seg_t; @@ -163,6 +164,7 @@ void tcp_enter_time_wait(tcp_flow_t *flow); tcp_flow_t *tcp_alloc_flow(void); void tcp_free_flow(int idx); +void tcp_active_insert_flow(tcp_flow_t *flow); tcp_flow_t *tcp_flow_from_ctx(tcp_data *flow_ctx); tcp_flow_t *tcp_flow_acquire_match(uint16_t local_port, ip_version_t ver, const void *local_ip, const void *remote_ip, uint16_t remote_port, int *out_idx); int tcp_flow_hold(tcp_flow_t *flow); @@ -171,6 +173,8 @@ void tcp_flow_put(tcp_flow_t *flow); void tcp_rtt_update(tcp_flow_t *flow, uint32_t sample_ms); tcp_tx_seg_t *tcp_alloc_tx_seg(tcp_flow_t *flow); +const uint8_t *tcp_tx_seg_payload_ptr(const tcp_tx_seg_t *seg); +void tcp_tx_seg_clear(tcp_flow_t *flow, tcp_tx_seg_t *seg); bool tcp_send_from_seg(tcp_flow_t *flow, tcp_tx_seg_t *seg); void tcp_send_ack_now(tcp_flow_t *flow); int tcp_try_send_pending_fin(tcp_flow_t *flow); diff --git a/kernel/networking/transport_layer/tcp/tcp_limits.c b/kernel/networking/transport_layer/tcp/tcp_limits.c index 83309503..97e35126 100644 --- a/kernel/networking/transport_layer/tcp/tcp_limits.c +++ b/kernel/networking/transport_layer/tcp/tcp_limits.c @@ -69,8 +69,9 @@ tcp_admit_result_t tcp_admit_syn(uint8_t l3_id, uint16_t port, ip_version_t ver, uint32_t orphan = 0; size_t ip_len = (size_t)(ver == IP_VER6 ? 16 : 4); - for (int i = 0; i < MAX_TCP_FLOWS; i++) { - tcp_flow_t *f = tcp_flows[i]; + for (uint16_t n = 0; n < tcp_active_count; n++) { + uint16_t slot = tcp_active_flows[n]; + tcp_flow_t *f = slot < MAX_TCP_FLOWS ? tcp_flows[slot] : NULL; if (!f) continue; if (f->base.state == TCP_SYN_RECEIVED) { diff --git a/kernel/networking/transport_layer/tcp/tcp_rx.c b/kernel/networking/transport_layer/tcp/tcp_rx.c index cf1d004e..b2456178 100644 --- a/kernel/networking/transport_layer/tcp/tcp_rx.c +++ b/kernel/networking/transport_layer/tcp/tcp_rx.c @@ -143,6 +143,7 @@ bool tcp_flow_recv_closed(tcp_data *flow_ctx) { } tcp_tx_seg_t *tcp_find_first_unacked(tcp_flow_t *flow) { + if (!flow) return NULL; tcp_tx_seg_t *best = NULL; uint32_t best_seq = 0; @@ -193,6 +194,7 @@ static int tcp_apply_sack_blocks(tcp_flow_t *flow, const tcp_parsed_opts_t *opts } static tcp_tx_seg_t *tcp_find_sack_retransmit(tcp_flow_t *flow) { + if (!flow) return NULL; tcp_tx_seg_t *best = NULL; uint32_t best_seq = 0; uint32_t highest_sacked = flow->tx.snd_una; @@ -465,6 +467,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a idx = (int)flow->base.slot; flow->base.local_port = dst_port; flow->base.l3_id = l3_id; + tcp_active_insert_flow(flow); flow->base.remote.ver = ipver; memset(flow->base.remote.ip, 0, 16); @@ -574,7 +577,8 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a seg->retransmit_cnt = 0; seg->seq = iss; seg->len = 0; - seg->buf = 0; + seg->pkt = NULL; + seg->payload_off = 0; seg->timer_ms = 0; seg->timeout_ms = flow->tx.rto ? flow->tx.rto : TCP_INIT_RTO; seg->opts_len = tcp_build_syn_options(seg->opts, (uint16_t)flow->tx.mss, flow->tx.ws_ok ? flow->tx.ws_send : 0xff, flow->tx.sack_ok); @@ -669,21 +673,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a if (TCP_SEQ_LEQ(s_end, ack)){ if (s->rtt_sample && s->retransmit_cnt == 0) tcp_rtt_update(flow, s->timer_ms); - - if (s->buf && s->len) { - uintptr_t seg_buf = s->buf; - uint32_t seg_len = (uint32_t)s->len; - s->buf = 0; - s->len = 0; - tcp_account_tx_remove(flow, seg_len); - release((void*)seg_buf); - } - - s->used = 0; - s->buf = 0; - s->len = 0; - s->sacked = 0; - s->sack_retransmitted = 0; + tcp_tx_seg_clear(flow, s); } } diff --git a/kernel/networking/transport_layer/tcp/tcp_timer.c b/kernel/networking/transport_layer/tcp/tcp_timer.c index a34ec38b..09c7154d 100644 --- a/kernel/networking/transport_layer/tcp/tcp_timer.c +++ b/kernel/networking/transport_layer/tcp/tcp_timer.c @@ -152,8 +152,9 @@ void tcp_tick_all(uint32_t elapsed_ms) { uint32_t probe_seq = f->tx.snd_una; if (!best && f->tx.fin_tx_pending && f->tx.snd_nxt == f->tx.snd_una && f->tx.snd_nxt) probe_seq = f->tx.snd_nxt-1; - if (best && best->buf && best->len && TCP_SEQ_GEQ(probe_seq, best->seq) && TCP_SEQ_LT(probe_seq, best->seq + best->len)) { - payload[0] = *((uint8_t *)best->buf + (probe_seq - best->seq)); + const uint8_t *best_payload = tcp_tx_seg_payload_ptr(best); + if (best && best_payload && best->len && TCP_SEQ_GEQ(probe_seq, best->seq) && TCP_SEQ_LT(probe_seq, best->seq + best->len)) { + payload[0] = best_payload[probe_seq - best->seq]; pp = payload; pl = 1; } diff --git a/kernel/networking/transport_layer/tcp/tcp_tx.c b/kernel/networking/transport_layer/tcp/tcp_tx.c index 74bcfe3b..9fbb5681 100644 --- a/kernel/networking/transport_layer/tcp/tcp_tx.c +++ b/kernel/networking/transport_layer/tcp/tcp_tx.c @@ -1,5 +1,25 @@ #include "tcp_internal.h" +const uint8_t *tcp_tx_seg_payload_ptr(const tcp_tx_seg_t *seg) { + if (!seg || !seg->pkt || !seg->len) return NULL; + + uint32_t pkt_len = netpkt_len(seg->pkt); + if (seg->payload_off > pkt_len) return NULL; + if (seg->len > pkt_len - seg->payload_off) return NULL; + + return (const uint8_t*)(netpkt_data(seg->pkt) + (uintptr_t)seg->payload_off); +} + +void tcp_tx_seg_clear(tcp_flow_t *flow, tcp_tx_seg_t *seg) { + if (!seg) return; + + uint32_t len = seg->len ? (uint32_t)seg->len : 0; + if (seg->pkt) netpkt_unref(seg->pkt); + if (len && flow) tcp_account_tx_remove(flow, len); + + memset(seg, 0, sizeof(*seg)); +} + uint16_t tcp_calc_adv_wnd_field(tcp_flow_t *flow, uint8_t apply_scale) { if (!flow) return 0; @@ -104,17 +124,20 @@ static uint64_t tcp_emit_data(tcp_flow_t *flow, const uint8_t *payload, uint64_t tcp_tx_seg_t *seg = tcp_alloc_tx_seg(flow); if (!seg) break; - uintptr_t buf = (uintptr_t)zalloc(seg_len); - if (!buf) { - seg->used = 0; + netpkt_t *payload_pkt = netpkt_alloc((uint32_t)seg_len, 0, 0); + uint8_t *payload_dst = payload_pkt ? (uint8_t*)netpkt_put(payload_pkt, (uint32_t)seg_len) : NULL; + if (!payload_dst) { + if (payload_pkt) netpkt_unref(payload_pkt); + tcp_tx_seg_clear(flow, seg); break; } - memcpy((void*)buf, payload + sent_bytes, seg_len); + memcpy(payload_dst, payload + sent_bytes, (uint32_t)seg_len); seg->seq = flow->tx.snd_nxt; seg->len = seg_len; - seg->buf = buf; + seg->pkt = payload_pkt; + seg->payload_off = 0; seg->syn = 0; seg->fin = 0; seg->timer_ms = 0; @@ -130,14 +153,7 @@ static uint64_t tcp_emit_data(tcp_flow_t *flow, const uint8_t *payload, uint64_t if (!tcp_send_from_seg(flow, seg)) { flow->tx.snd_nxt -= seg_len; flow->base.ctx.sequence = flow->tx.snd_nxt; - tcp_account_tx_remove(flow, (uint32_t)seg_len); - if (seg->buf && seg->len) { - uintptr_t seg_buf = seg->buf; - seg->buf = 0; - seg->len = 0; - release((void*)seg_buf); - } - memset(seg, 0, sizeof(*seg)); + tcp_tx_seg_clear(flow, seg); break; } @@ -220,22 +236,12 @@ uint64_t tcp_flush_nagle(tcp_flow_t *flow, uint8_t force) { } tcp_tx_seg_t *tcp_alloc_tx_seg(tcp_flow_t *flow){ + if (!flow) return NULL; for (int i = 0; i < TCP_MAX_TX_SEGS; i++) { if (!flow->tx.txq[i].used) { tcp_tx_seg_t *s = &flow->tx.txq[i]; + memset(s, 0, sizeof(*s)); s->used = 1; - s->syn = 0; - s->fin = 0; - s->rtt_sample = 0; - s->retransmit_cnt = 0; - s->opts_len = 0; - s->sacked = 0; - s->sack_retransmitted = 0; - memset(s->opts, 0, sizeof(s->opts)); - s->seq = 0; - s->len = 0; - s->buf = 0; - s->timer_ms = 0; s->timeout_ms = flow->tx.rto ? flow->tx.rto : TCP_INIT_RTO; return s; } @@ -267,13 +273,13 @@ bool tcp_send_from_seg(tcp_flow_t *flow, tcp_tx_seg_t *seg){ if (flow->base.local.ver == IP_VER4) { ip_tx_opts_t tx; tcp_build_tx_opts_from_local_v4(flow->base.local.ip, &tx); - bool ok = tcp_send_segment(IP_VER4, flow->base.local.ip, flow->base.remote.ip, &hdr, opts, seg->opts_len, seg->buf ? (const uint8_t *)seg->buf : NULL, seg->len, &tx, flow->ip.ttl, flow->ip.dontfrag); + bool ok = tcp_send_segment(IP_VER4, flow->base.local.ip, flow->base.remote.ip, &hdr, opts, seg->opts_len, tcp_tx_seg_payload_ptr(seg), (uint16_t)seg->len, &tx, flow->ip.ttl, flow->ip.dontfrag); if (ok) tcp_daemon_kick(); return ok; } else if (flow->base.local.ver == IP_VER6) { ip_tx_opts_t tx; tcp_build_tx_opts_from_local_v6(flow->base.local.ip, &tx); - bool ok = tcp_send_segment(IP_VER6, flow->base.local.ip, flow->base.remote.ip, &hdr, opts, seg->opts_len, seg->buf ? (const uint8_t *)seg->buf : NULL, seg->len, &tx, flow->ip.ttl, flow->ip.dontfrag); + bool ok = tcp_send_segment(IP_VER6, flow->base.local.ip, flow->base.remote.ip, &hdr, opts, seg->opts_len, tcp_tx_seg_payload_ptr(seg), (uint16_t)seg->len, &tx, flow->ip.ttl, flow->ip.dontfrag); if (ok) tcp_daemon_kick(); return ok; } @@ -296,8 +302,6 @@ void tcp_send_ack_now(tcp_flow_t *flow){ uint8_t opts[64]; uint8_t opts_len = 0; - opts_len = 0; - if (flow->tx.sack_ok && flow->rx.reass_count > 0) { tcp_sack_block_t blocks[TCP_SACK_MAX_BLOCKS]; uint32_t n = 0; @@ -387,7 +391,8 @@ int tcp_try_send_pending_fin(tcp_flow_t *flow) { seg->seq = flow->tx.snd_nxt; seg->len = 0; - seg->buf = 0; + seg->pkt = NULL; + seg->payload_off = 0; seg->syn = 0; seg->fin = 1; seg->timer_ms = 0; @@ -410,7 +415,7 @@ int tcp_try_send_pending_fin(tcp_flow_t *flow) { flow->base.ctx.sequence = flow->tx.snd_nxt; flow->base.state = old_state; flow->tx.fin_tx_pending = 1; - memset(seg, 0, sizeof(*seg)); + tcp_tx_seg_clear(flow, seg); tcp_daemon_kick(); return 0; } diff --git a/modules/Makefile b/modules/Makefile index 30131e55..38322ebe 100644 --- a/modules/Makefile +++ b/modules/Makefile @@ -1,8 +1,8 @@ include ../common.mk SUBDIRS := $(wildcard */.) -SUBCLEAN = $(addsuffix .clean, $(SUBDIRS)) -MAKEFILE := $(shell pwd)/MakefileModule +SUBCLEAN := $(addsuffix .clean, $(SUBDIRS)) +MAKEFILE := $(CURDIR)/MakefileModule all: $(SUBDIRS) @@ -14,6 +14,4 @@ clean: $(SUBCLEAN) $(SUBCLEAN): %.clean: $(MAKE) -f $(MAKEFILE) -C $* clean DRIVER_TARGET=$(DRIVER_TARGET) -clean: $(SUBCLEAN) - -.PHONY: all $(SUBDIRS) +.PHONY: all clean $(SUBDIRS) $(SUBCLEAN) diff --git a/modules/MakefileModule b/modules/MakefileModule index 64259653..437f60cf 100644 --- a/modules/MakefileModule +++ b/modules/MakefileModule @@ -13,11 +13,12 @@ endif CFLAGS := $(CFLAGS_BASE) $(CPPFLAGS) CXXFLAGS := $(CXXFLAGS_BASE) $(CPPFLAGS) -CLEAN_OBJS := $(shell find $(DRIVER_TARGET) -name "*.o") $(shell find ./common -name "*.o") -CLEAN_DEPS := $(shell find $(DRIVER_TARGET) -name "*.d") $(shell find ./common -name "*.d") -C_SRC := $(shell find $(DRIVER_TARGET) -name "*.c") $(shell find ./common -name "*.c") -CPP_SRC := $(shell find $(DRIVER_TARGET) -name "*.cpp") $(shell find ./common -name "*.cpp") -ASM_SRC := $(shell find $(DRIVER_TARGET) -name "*.S") $(shell find ./common -name "*.S") +SRC_DIRS := $(if $(strip $(DRIVER_TARGET)),$(wildcard ./$(DRIVER_TARGET)) $(wildcard ./common),.) +CLEAN_OBJS := $(if $(SRC_DIRS),$(shell find $(SRC_DIRS) -name "*.o"),) +CLEAN_DEPS := $(if $(SRC_DIRS),$(shell find $(SRC_DIRS) -name "*.d"),) +C_SRC := $(if $(SRC_DIRS),$(shell find $(SRC_DIRS) -name "*.c"),) +CPP_SRC := $(if $(SRC_DIRS),$(shell find $(SRC_DIRS) -name "*.cpp"),) +ASM_SRC := $(if $(SRC_DIRS),$(shell find $(SRC_DIRS) -name "*.S"),) OBJ := $(C_SRC:%.c=$(BUILD_DIR)/%.o) $(ASM_SRC:%.S=$(BUILD_DIR)/%.o) $(CPP_SRC:%.cpp=$(BUILD_DIR)/%.o) DEP := $(C_SRC:%.c=$(BUILD_DIR)/%.d) $(ASM_SRC:%.S=$(BUILD_DIR)/%.d) $(CPP_SRC:%.cpp=$(BUILD_DIR)/%.d) diff --git a/shared b/shared index e045992f..ee12c278 160000 --- a/shared +++ b/shared @@ -1 +1 @@ -Subproject commit e045992f815369db420d46be91f593656083fb6e +Subproject commit ee12c278e322a1ca14b58a81b7bb6e2377c43251 From ee05374009f615d1c52170cb5533df82a6919a63 Mon Sep 17 00:00:00 2001 From: CodeAnarchist Date: Thu, 23 Jul 2026 19:24:27 +0200 Subject: [PATCH 34/39] [net] batch virtio and improve packet paths --- .../drivers/virtio_net_pci/virtio_net_pci.cpp | 94 ++++++++++--------- .../drivers/virtio_net_pci/virtio_net_pci.hpp | 5 +- kernel/networking/netpkt.c | 9 +- kernel/networking/network_dispatch.cpp | 82 ++++++++-------- .../networking/transport_layer/csocket_udp.c | 49 ++++++++-- .../networking/transport_layer/tcp/tcp_core.c | 5 +- kernel/virtio/virtio_pci.c | 2 - kernel/virtio/virtio_pci.h | 2 + 8 files changed, 143 insertions(+), 105 deletions(-) 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 158ef87a..4ce06969 100644 --- a/kernel/networking/drivers/virtio_net_pci/virtio_net_pci.cpp +++ b/kernel/networking/drivers/virtio_net_pci/virtio_net_pci.cpp @@ -55,7 +55,6 @@ void virtio_net_rx_free(void* ctx, uintptr_t base) { driver->rx_avail->ring[aidx % driver->rx_qsz] = (uint16_t)desc_index; asm volatile ("dmb ishst" ::: "memory"); driver->rx_avail->idx = (uint16_t)(aidx + 1); - asm volatile ("dmb ishst" ::: "memory"); driver->rx_notify_pending = true; irq_restore(flags); } @@ -109,6 +108,7 @@ bool VirtioNetDriver::init_at(uint64_t addr, uint32_t irq_base_vector) { duplex = LINK_DUPLEX_UNKNOWN; last_used_receive_idx = 0; last_used_sent_idx = 0; + rx_used_batch_end = 0; rx_desc = nullptr; rx_avail = nullptr; rx_used = nullptr; @@ -119,9 +119,9 @@ bool VirtioNetDriver::init_at(uint64_t addr, uint32_t irq_base_vector) { tx_avail = nullptr; tx_used = nullptr; tx_qsz = 0; - tx_next_desc = 0; + tx_avail_shadow_idx = 0; tx_pending = nullptr; - tx_notify_pending = false; + tx_free_head = UINT16_MAX; memset(&vnp_net_dev, 0, sizeof(vnp_net_dev)); kprintfv("[virtio-net] probing pci_addr=%x",(uintptr_t)addr); @@ -195,7 +195,8 @@ bool VirtioNetDriver::init_at(uint64_t addr, uint32_t irq_base_vector) { if (!rx_pool) return false; memset((void*)rx_desc, 0, 16ULL * rx_qsz); - rx_avail->flags = 0; + //re enable used buffer notifications when network irqs are used + rx_avail->flags = VIRTQ_AVAIL_F_NO_INTERRUPT; rx_avail->idx = 0; rx_used->flags = 0; rx_used->idx = 0; @@ -231,12 +232,15 @@ bool VirtioNetDriver::init_at(uint64_t addr, uint32_t irq_base_vector) { if (!tx_pending) return false; memset((void*)tx_desc, 0, 16 * tx_qsz); - tx_avail->flags = 0; + //re enable used buffer notifications when network irqs are used + tx_avail->flags = VIRTQ_AVAIL_F_NO_INTERRUPT; tx_avail->idx = 0; tx_used->flags = 0; tx_used->idx = 0; last_used_sent_idx = 0; - tx_next_desc = 0; + tx_avail_shadow_idx = 0; + tx_free_head = 0; + for (uint16_t i = 0; i < tx_qsz; i++) tx_desc[i].next = (i + 1 < tx_qsz) ? (i + 1) : UINT16_MAX; select_queue(&vnp_net_dev, TRANSMIT_QUEUE); vnp_net_dev.common_cfg->queue_msix_vector = 1; @@ -326,10 +330,11 @@ uint8_t VirtioNetDriver::get_duplex() const { netpkt_t* VirtioNetDriver::handle_receive_packet(){ if (!rx_qsz || !rx_desc || !rx_used || !rx_avail || !rx_pool) return nullptr; - asm volatile ("dmb ishld" ::: "memory"); - - uint16_t new_idx = rx_used->idx; - if (new_idx == last_used_receive_idx) return nullptr; + if (last_used_receive_idx == rx_used_batch_end) { + rx_used_batch_end = rx_used->idx; + asm volatile ("dmb ishld" ::: "memory"); + if (last_used_receive_idx == rx_used_batch_end) return nullptr; + } uint16_t used_ring_index = (uint16_t)(last_used_receive_idx % rx_qsz); volatile virtq_used_elem* e = &rx_used->ring[used_ring_index]; @@ -359,33 +364,36 @@ void VirtioNetDriver::complete_rx_batch() { bool pending = rx_notify_pending; rx_notify_pending = false; irq_restore(flags); - if (pending) virtio_notify_queue(&vnp_net_dev, RECEIVE_QUEUE); + if (!pending) return; + asm volatile ("dmb ishst" ::: "memory"); + virtio_notify_queue(&vnp_net_dev, RECEIVE_QUEUE); } void VirtioNetDriver::handle_sent_packet(){ if (TRANSMIT_QUEUE >= vnp_net_dev.num_queues) return; if (!tx_qsz || !tx_desc || !tx_used || !tx_pending) return; + uint16_t used_end = tx_used->idx; asm volatile ("dmb ishld" ::: "memory"); - uint16_t used_idx = tx_used->idx; - while (last_used_sent_idx != used_idx) { + while (last_used_sent_idx != used_end) { uint16_t ring_index = (uint16_t)(last_used_sent_idx % tx_qsz); - uint16_t desc_index = (uint16_t)tx_used->ring[ring_index].id; + uint32_t used_id = tx_used->ring[ring_index].id; last_used_sent_idx++; - if (desc_index >= tx_qsz) continue; + if (used_id >= tx_qsz) continue; + uint16_t desc_index = (uint16_t)used_id; netpkt_t* packet = tx_pending[desc_index]; + if (!packet) continue; tx_pending[desc_index] = nullptr; tx_desc[desc_index].addr = 0; tx_desc[desc_index].len = 0; tx_desc[desc_index].flags = 0; - tx_desc[desc_index].next = 0; + tx_desc[desc_index].next = tx_free_head; + tx_free_head = desc_index; - if (packet) netpkt_unref(packet); - asm volatile ("dmb ishld" ::: "memory"); - used_idx = tx_used->idx; + netpkt_unref(packet); } } @@ -393,27 +401,25 @@ netdev_tx_result_t VirtioNetDriver::send_packet(netpkt_t* packet){ if (!packet || !netpkt_len(packet)) return NETDEV_TX_DROP; if (!tx_qsz || !tx_desc || !tx_avail || !tx_used || !tx_pending) return NETDEV_TX_DROP; - handle_sent_packet(); - - bool found = false; - uint16_t desc_index = 0; - for (uint16_t i = 0; i < tx_qsz; i++) { - uint16_t current = (tx_next_desc + i) % tx_qsz; - if (tx_pending[current]) continue; - desc_index = current; - tx_next_desc = (uint16_t)((current + 1) % tx_qsz); - found = true; - break; - } - - if (!found) { + if (tx_free_head == UINT16_MAX) { kprintfv("[virtio-net] tx queue full len=%u", (unsigned)netpkt_len(packet)); return NETDEV_TX_BUSY; } - if (!netpkt_ensure_headroom(packet, header_size)) return NETDEV_TX_DROP; + uint16_t desc_index = tx_free_head; + tx_free_head = tx_desc[desc_index].next; + if (!netpkt_ensure_headroom(packet, header_size)) { + tx_desc[desc_index].next = tx_free_head; + tx_free_head = desc_index; + return NETDEV_TX_DROP; + } + void* hdr = netpkt_push(packet, header_size); - if (!hdr) return NETDEV_TX_DROP; + if (!hdr) { + tx_desc[desc_index].next = tx_free_head; + tx_free_head = desc_index; + return NETDEV_TX_DROP; + } memset(hdr, 0, (size_t)header_size); tx_pending[desc_index] = packet; @@ -422,24 +428,20 @@ netdev_tx_result_t VirtioNetDriver::send_packet(netpkt_t* packet){ tx_desc[desc_index].flags = 0; tx_desc[desc_index].next = 0; - asm volatile ("dmb ishst" ::: "memory"); - uint16_t avail_idx = tx_avail->idx; + uint16_t avail_idx = tx_avail_shadow_idx; tx_avail->ring[avail_idx % tx_qsz] = desc_index; - asm volatile ("dmb ishst" ::: "memory"); - tx_avail->idx = (uint16_t)(avail_idx + 1); - asm volatile ("dmb ishst" ::: "memory"); - tx_notify_pending = true; + tx_avail_shadow_idx = (uint16_t)(avail_idx + 1); kprintfv("[virtio-net] tx queued desc=%u len=%u", desc_index,(unsigned)netpkt_len(packet)); return NETDEV_TX_OK; } void VirtioNetDriver::complete_tx_batch() { - irq_flags_t flags = irq_save_disable(); - bool pending = tx_notify_pending; - tx_notify_pending = false; - irq_restore(flags); - if (pending) virtio_notify_queue(&vnp_net_dev, TRANSMIT_QUEUE); + if (tx_avail->idx == tx_avail_shadow_idx) return; + asm volatile ("dmb ishst" ::: "memory"); + tx_avail->idx = tx_avail_shadow_idx; + asm volatile ("dmb ishst" ::: "memory"); + virtio_notify_queue(&vnp_net_dev, TRANSMIT_QUEUE); } bool VirtioNetDriver::sync_multicast(const uint8_t* macs, uint32_t count) { 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 4c48b1ea..26c9bbba 100644 --- a/kernel/networking/drivers/virtio_net_pci/virtio_net_pci.hpp +++ b/kernel/networking/drivers/virtio_net_pci/virtio_net_pci.hpp @@ -77,6 +77,7 @@ class VirtioNetDriver : public NetDriver { volatile virtq_avail* rx_avail = nullptr; volatile virtq_used* rx_used = nullptr; uint16_t rx_qsz = 0; + uint16_t rx_used_batch_end = 0; void* rx_pool = nullptr; bool rx_notify_pending = false; @@ -84,9 +85,9 @@ class VirtioNetDriver : public NetDriver { volatile virtq_avail* tx_avail = nullptr; volatile virtq_used* tx_used = nullptr; uint16_t tx_qsz = 0; - uint16_t tx_next_desc = 0; + uint16_t tx_avail_shadow_idx = 0; netpkt_t** tx_pending = nullptr; - bool tx_notify_pending = false; + uint16_t tx_free_head = UINT16_MAX; bool verbose = false; diff --git a/kernel/networking/netpkt.c b/kernel/networking/netpkt.c index 6659b402..659ce411 100644 --- a/kernel/networking/netpkt.c +++ b/kernel/networking/netpkt.c @@ -12,6 +12,7 @@ #define NETPKT_PAYLOAD_CACHE_KEEP 8 #define NETPKT_PAYLOAD_CACHE_MAX_BYTES (2 *1024 * 1024ull) #define NETPKT_MAX_STORAGE_BYTES 131072u +#define NETPKT_HEADROOM_SLACK 32u typedef struct netpkt_buf netpkt_buf_t; @@ -81,7 +82,6 @@ static void* netpkt_payload_alloc(uint32_t cap, uint32_t* flags) { void* mem = g_netpkt_small_free; g_netpkt_small_free = *(void**)mem; irq_restore(irq); - memset(mem, 0, NETPKT_SMALL_CLASS_BYTES); *flags = NETPKT_BUF_F_SMALL; return mem; } @@ -125,7 +125,6 @@ static void* netpkt_payload_alloc(uint32_t cap, uint32_t* flags) { if (g_payload_cache_bytes >= (uint64_t)cap) g_payload_cache_bytes -= (uint64_t)cap; else g_payload_cache_bytes = 0; irq_restore(irq); - memset(mem, 0, cap); return mem; } if ((uint64_t)cap > (uint64_t)NETPKT_MAX_PAGE_BYTES || g_netpkt_page_bytes > (uint64_t)NETPKT_MAX_PAGE_BYTES - (uint64_t)cap) { @@ -461,10 +460,14 @@ netpkt_t* netpkt_alloc(uint32_t data_capacity, uint32_t headroom, uint32_t tailr b->free_fn = 0; b->free_ctx = 0; + uint32_t used = headroom + data_capacity + tailroom; + uint32_t slack = cap > used ? cap - used : 0; + if (slack > NETPKT_HEADROOM_SLACK) slack = NETPKT_HEADROOM_SLACK; + p->buf = b; p->off = 0; p->cap = cap; - p->head = headroom; + p->head = headroom + slack; p->len = 0; p->refs = 1; p->flags = 0; diff --git a/kernel/networking/network_dispatch.cpp b/kernel/networking/network_dispatch.cpp index ba0a41e3..ed990ebf 100644 --- a/kernel/networking/network_dispatch.cpp +++ b/kernel/networking/network_dispatch.cpp @@ -16,7 +16,8 @@ #include "networking/drivers/loopback/loopback_driver.hpp" #include "exceptions/irq.h" -#define RX_INTR_BATCH_LIMIT 256 +#define TASK_RX_QUANTUM 64 +#define TASK_TX_QUANTUM 64 #define TASK_RX_BATCH_LIMIT 256 #define TASK_TX_BATCH_LIMIT 256 @@ -83,55 +84,52 @@ int NetworkDispatch::net_task() NetDriver* driver = nics[n].drv; if (!driver) continue; - driver->handle_sent_packet(); - int processed = 0; - int lim = nics[n].kind_val == NET_IFK_LOCALHOST ? TASK_RX_BATCH_LIMIT : RX_INTR_BATCH_LIMIT; - for (int i = 0; i < lim; ++i) { - netpkt_t* pkt = driver->handle_receive_packet(); - if (!pkt) break; - if (!netpkt_len(pkt)) { + uint16_t rx_processed = 0; + uint16_t tx_processed = 0; + while (rx_processed < TASK_RX_BATCH_LIMIT || tx_processed < TASK_TX_BATCH_LIMIT) { + uint16_t rx_round = 0; + uint16_t tx_round = 0; + while (rx_processed < TASK_RX_BATCH_LIMIT && rx_round < TASK_RX_QUANTUM) { + netpkt_t* pkt = driver->handle_receive_packet(); + if (!pkt) break; + if (!netpkt_len(pkt)) { + netpkt_unref(pkt); + break; + } + + socket_packet_input(nics[n].ifindex, pkt); + if (netpkt_len(pkt) >= sizeof(eth_hdr_t)) eth_input(nics[n].ifindex, pkt); netpkt_unref(pkt); - break; + rx_processed++; + rx_round++; } - - socket_packet_input(nics[n].ifindex, pkt); - if (netpkt_len(pkt) >= sizeof(eth_hdr_t)) eth_input(nics[n].ifindex, pkt); - netpkt_unref(pkt); - processed++; - } - - driver->complete_rx_batch(); - if (processed) did_work = true; - } - - for (size_t n = 0; n < nic_num; ++n) { - NetDriver* driver = nics[n].drv; - if (!driver) continue; - int processed = 0; - for (int i = 0; i < TASK_TX_BATCH_LIMIT; ++i) { - irq_flags_t flags = irq_save_disable(); - if (nics[n].tx.is_empty()) { + driver->complete_rx_batch(); + driver->handle_sent_packet(); + while (tx_processed < TASK_TX_BATCH_LIMIT && tx_round < TASK_TX_QUANTUM) { + irq_flags_t flags = irq_save_disable(); + if (nics[n].tx.is_empty()) { + irq_restore(flags); + break; + } + netpkt_t* pkt = nics[n].tx.peek(); irq_restore(flags); - break; - } - netpkt_t* pkt = nics[n].tx.peek(); - irq_restore(flags); - netdev_tx_result_t txr = driver->send_packet(pkt); - if (txr == NETDEV_TX_BUSY) break; + netdev_tx_result_t txr = driver->send_packet(pkt); + if (txr == NETDEV_TX_BUSY) break; - flags = irq_save_disable(); - netpkt_t* popped = nullptr; - nics[n].tx.pop(popped); - irq_restore(flags); + flags = irq_save_disable(); + netpkt_t* popped = nullptr; + nics[n].tx.pop(popped); + irq_restore(flags); - if (txr == NETDEV_TX_DROP && popped) netpkt_unref(popped); - processed++; - } - if (processed) { + if (txr == NETDEV_TX_DROP && popped) netpkt_unref(popped); + tx_processed++; + tx_round++; + } driver->complete_tx_batch(); - did_work = true; + if (!rx_round && !tx_round) break; } + if (rx_processed || tx_processed) did_work = true; } if (!did_work) msleep(1);//TODO: manage it with an event diff --git a/kernel/networking/transport_layer/csocket_udp.c b/kernel/networking/transport_layer/csocket_udp.c index a1ba75ae..39ae3903 100644 --- a/kernel/networking/transport_layer/csocket_udp.c +++ b/kernel/networking/transport_layer/csocket_udp.c @@ -1,4 +1,5 @@ #include "csocket_udp.h" +#include "exceptions/irq.h" #include "networking/transport_layer/socket_bind.h" #include "networking/transport_layer/udp.h" #include "networking/internet_layer/ipv4_route.h" @@ -170,7 +171,6 @@ uint32_t socket_udp_input(ksocket_t* socket, ip_version_t ipver, uint8_t l3_id, uint32_t limit = UINT32_MAX; if ((s->options.flags & SOCK_OPT_BUF_SIZE) && s->options.buf_size) limit = s->options.buf_size; if (pkt_len > limit) return 0; - if (s->rx_bytes > limit - pkt_len) return 0; if (!s->rx_ring || !s->ring_cap) { uint32_t usable = UDP_DEFAULT_RING_CAP; @@ -180,16 +180,29 @@ uint32_t socket_udp_input(ksocket_t* socket, ip_version_t ipver, uint8_t l3_id, if (usable > UDP_MAX_RING_CAP) usable = UDP_MAX_RING_CAP; } - s->ring_cap = usable + 1; - s->rx_ring = (udp_rx_entry_t*)zalloc(sizeof(udp_rx_entry_t) * s->ring_cap); + udp_rx_entry_t* ring = (udp_rx_entry_t*)zalloc(sizeof(udp_rx_entry_t) * (usable+1)); + if (!ring) return 0; + irq_flags_t irq = irq_save_disable(); if (!s->rx_ring) { - s->ring_cap = 0; - return 0; + s->rx_ring = ring; + s->ring_cap = usable + 1; + ring = NULL; } + irq_restore(irq); + if (ring) release(ring); + } + + irq_flags_t irq = irq_save_disable(); + if (!s->rx_ring || !s->ring_cap || s->rx_bytes > limit - pkt_len) { + irq_restore(irq); + return 0; } uint32_t nexti = (s->r_tail + 1) % s->ring_cap; - if (nexti == s->r_head) return 0; + if (nexti == s->r_head) { + irq_restore(irq); + return 0; + } netpkt_ref(pkt); s->rx_ring[s->r_tail].pkt = pkt; @@ -200,6 +213,7 @@ uint32_t socket_udp_input(ksocket_t* socket, ip_version_t ipver, uint8_t l3_id, s->rx_ring[s->r_tail].rx_spec.l3_id = l3_id; s->rx_bytes += pkt_len; s->r_tail = nexti; + irq_restore(irq); return pkt_len; } @@ -493,11 +507,19 @@ int64_t socket_recvfrom_udp(socket_impl_t sh, void* buf, uint64_t len, net_l4_en ev.remote_ep = s->remoteEP; netlog_socket_event(&s->options, &ev); - if (!s->rx_ring || s->r_head == s->r_tail) { + irq_flags_t irq = irq_save_disable(); + bool ready = s->rx_ring && s->r_head != s->r_tail; + irq_restore(irq); + if (!ready) { if (!(s->options.flags & SOCK_OPT_RECV_TIMEOUT) || !s->options.recv_timeout_ms) return SOCK_ERR_WOULDBLOCK; uint32_t start_ms = (uint32_t)get_time(); - while (!s->rx_ring || s->r_head == s->r_tail) { + while (1) { + irq = irq_save_disable(); + ready = s->rx_ring && s->r_head != s->r_tail; + irq_restore(irq); + if (ready) break; + uint32_t now_ms = (uint32_t)get_time(); uint32_t elapsed_ms = now_ms - start_ms; if (elapsed_ms >= s->options.recv_timeout_ms) return SOCK_ERR_WOULDBLOCK; @@ -508,6 +530,12 @@ int64_t socket_recvfrom_udp(socket_impl_t sh, void* buf, uint64_t len, net_l4_en } } + irq = irq_save_disable(); + if (!s->rx_ring || s->r_head == s->r_tail) { + irq_restore(irq); + return SOCK_ERR_WOULDBLOCK; + } + netpkt_t* p = s->rx_ring[s->r_head].pkt; net_l4_endpoint se = s->rx_ring[s->r_head].src; s->lastRxSpec = s->rx_ring[s->r_head].rx_spec; @@ -515,7 +543,10 @@ int64_t socket_recvfrom_udp(socket_impl_t sh, void* buf, uint64_t len, net_l4_en s->r_head = (s->r_head + 1) % s->ring_cap; uint32_t pkt_len = p ? netpkt_len(p) : 0; - s->rx_bytes -= pkt_len; + if (s->rx_bytes >= pkt_len) s->rx_bytes -= pkt_len; + else s->rx_bytes = 0; + irq_restore(irq); + uint32_t tocpy = pkt_len; if (tocpy > len) tocpy = (uint32_t)len; diff --git a/kernel/networking/transport_layer/tcp/tcp_core.c b/kernel/networking/transport_layer/tcp/tcp_core.c index d8c11b02..66907730 100644 --- a/kernel/networking/transport_layer/tcp/tcp_core.c +++ b/kernel/networking/transport_layer/tcp/tcp_core.c @@ -362,8 +362,11 @@ bool tcp_send_segment(ip_version_t ver, const void *src_ip_addr, const void *dst if (opts_len & 3u) return false; if (opts_len > 40u) return false; + if ((opts_len && !opts) || (payload_len && !payload)) return false; - uint16_t tcp_len = (uint16_t)(sizeof(tcp_hdr_t) + opts_len + payload_len); + uint32_t tcp_len32 = (uint32_t)sizeof(tcp_hdr_t) + (uint32_t)opts_len + (uint32_t)payload_len; + if (tcp_len32 > NETPKT_MAX_ALLOC) return false; + uint16_t tcp_len = (uint16_t)tcp_len32; uint32_t headroom = (uint32_t)sizeof(eth_hdr_t) + (uint32_t)(ver == IP_VER4 ? sizeof(ipv4_hdr_t) : sizeof(ipv6_hdr_t)); netpkt_t *pkt = netpkt_alloc(tcp_len, headroom, 0); if (!pkt) return false; diff --git a/kernel/virtio/virtio_pci.c b/kernel/virtio/virtio_pci.c index a272a886..d706d834 100644 --- a/kernel/virtio/virtio_pci.c +++ b/kernel/virtio/virtio_pci.c @@ -240,8 +240,6 @@ void virtio_notify_queue(virtio_device *dev, uint16_t index) { if (!dev->queues[index].valid) return; uint32_t mul = dev->notify_off_multiplier; - if (!mul) mul = 1; - uint16_t off = dev->queues[index].notify_off; uint16_t value = (dev->negotiated_features & (1ULL << VIRTIO_F_NOTIFICATION_DATA)) ? dev->queues[index].notify_data : index; diff --git a/kernel/virtio/virtio_pci.h b/kernel/virtio/virtio_pci.h index c52a9488..2637aee9 100644 --- a/kernel/virtio/virtio_pci.h +++ b/kernel/virtio/virtio_pci.h @@ -9,6 +9,8 @@ extern "C" { #define VIRTQ_DESC_F_NEXT 1 #define VIRTQ_DESC_F_WRITE 2 +#define VIRTQ_AVAIL_F_NO_INTERRUPT 1 + #define VIRTIO_VENDOR 0x1AF4 #define VIRTIO_F_VERSION_1 32 From 6dbf5e541d38ecf1d02448f130798d84f4417937 Mon Sep 17 00:00:00 2001 From: CodeAnarchist Date: Tue, 28 Jul 2026 23:40:57 +0200 Subject: [PATCH 35/39] [net] add NONBLOCK and DONTROUTE sock options --- .../application_layer/csocket_http_client.c | 27 +- .../application_layer/csocket_http_server.c | 8 + .../application_layer/dhcp_daemon.c | 2 +- .../application_layer/dhcpv6_daemon.c | 2 +- kernel/networking/application_layer/dns/dns.c | 2 +- .../application_layer/dns/dns_daemon.c | 14 +- kernel/networking/application_layer/ntp.c | 2 +- kernel/networking/application_layer/sntp.c | 2 +- .../application_layer/ssdp_daemon.c | 16 +- kernel/networking/internet_layer/ipv4_route.c | 8 + kernel/networking/internet_layer/ipv4_route.h | 1 + kernel/networking/internet_layer/ipv6_route.c | 8 + kernel/networking/internet_layer/ipv6_route.h | 1 + kernel/networking/processes/net_proc.c | 2 +- .../transport_layer/csocket_packet.c | 20 +- .../networking/transport_layer/csocket_raw.c | 50 ++- .../networking/transport_layer/csocket_tcp.c | 293 ++++++++++++------ .../networking/transport_layer/csocket_udp.c | 45 ++- .../networking/transport_layer/socket_core.c | 15 + kernel/networking/transport_layer/tcp.h | 1 + .../networking/transport_layer/tcp/tcp_core.c | 48 +-- kernel/tools/icmp_probe.c | 2 +- 22 files changed, 365 insertions(+), 204 deletions(-) diff --git a/kernel/networking/application_layer/csocket_http_client.c b/kernel/networking/application_layer/csocket_http_client.c index 9d153cab..1c321004 100644 --- a/kernel/networking/application_layer/csocket_http_client.c +++ b/kernel/networking/application_layer/csocket_http_client.c @@ -292,16 +292,23 @@ static HTTPResponseMsg http_client_receive_response(HTTPClient* cli, HTTPMethod } static int32_t http_client_connect_endpoint_origin(HTTPClient* cli, const net_l4_endpoint* dst, const char* host_name) { - if (!cli->sock) cli->sock = create_socket(PROTO_TCP, &cli->tcp_opts); + if (!cli->sock) { + SocketOptions connect_options = cli->tcp_opts; + connect_options.flags &= ~SOCK_OPT_NONBLOCK; + cli->sock = create_socket(PROTO_TCP, &connect_options); + } if (!cli->sock) return SOCK_ERR_SYS; int32_t r = connect_socket(cli->sock, dst); - if (r >= 0) { + if (r >= 0 && (r = set_socket_option(cli->sock, SOCK_OPT_NONBLOCK, &(uint32_t){1}, sizeof(uint32_t))) >= 0) { HTTPClientOrigin next = {0}; next.valid = true; if (dst) next.ep = *dst; if (host_name) next.domain = string_from_literal(host_name); if (cli->origin.domain.mem_length) string_free(cli->origin.domain); cli->origin = next; + } else { + close_socket(cli->sock); + cli->sock = 0; } netlog_socket_event_t ev = {0}; @@ -477,7 +484,6 @@ HTTPResponseMsg http_client_send_request(http_client_handle_t h, const HTTPReque if (!url.ok) break; bool absolute = url.scheme.size || url.host.size; - uint16_t next_port = cli->origin.ep.port; if (url.scheme.size) { if (url.scheme.size == 5 && strncmp_case((const char*)url.scheme.ptr, "https", true, 5) == 0) break; if (!(url.scheme.size == 4 && strncmp_case((const char*)url.scheme.ptr, "http", true, 4) == 0)) break; @@ -491,7 +497,7 @@ HTTPResponseMsg http_client_send_request(http_client_handle_t h, const HTTPReque curr.path = next_path; if (absolute) { - next_port = url.port ? url.port : 80; + uint16_t next_port = url.port ? url.port : 80; string next_domain = string_from_literal_length((const char*)url.host.ptr, url.host.size); if (!curr.host_override) { if (curr.headers_common.fields.host.mem_length) string_free(curr.headers_common.fields.host); @@ -502,9 +508,8 @@ HTTPResponseMsg http_client_send_request(http_client_handle_t h, const HTTPReque close_socket(cli->sock); cli->sock = 0; } - cli->sock = next_domain.data ? create_socket(PROTO_TCP, &cli->tcp_opts) : 0; - int32_t rr = cli->sock ? SOCK_OK : SOCK_ERR_SYS; - if (rr >= 0) rr = http_client_connect_domain_origin(cli, next_domain.data, next_port); + int32_t rr = SOCK_ERR_SYS; + if (next_domain.data) rr = http_client_connect_domain_origin(cli, next_domain.data, next_port); if (next_domain.mem_length) string_free(next_domain); if (rr < 0) { http_response_free(&resp); @@ -520,9 +525,11 @@ HTTPResponseMsg http_client_send_request(http_client_handle_t h, const HTTPReque close_socket(cli->sock); cli->sock = 0; } - cli->sock = cli->origin.valid ? create_socket(PROTO_TCP, &cli->tcp_opts) : 0; - int32_t rr = cli->sock ? SOCK_OK : SOCK_ERR_SYS; - if (rr >= 0) rr = reconnect_domain.data ? http_client_connect_domain_origin(cli, reconnect_domain.data, reconnect_ep.port) : http_client_connect_endpoint_origin(cli, &reconnect_ep, NULL); + int32_t rr = SOCK_ERR_STATE; + if (cli->origin.valid) { + if (reconnect_domain.data) rr = http_client_connect_domain_origin(cli, reconnect_domain.data, reconnect_ep.port); + else rr = http_client_connect_endpoint_origin(cli, &reconnect_ep, NULL); + } if (reconnect_domain.mem_length) string_free(reconnect_domain); if (rr < 0) { http_response_free(&resp); diff --git a/kernel/networking/application_layer/csocket_http_server.c b/kernel/networking/application_layer/csocket_http_server.c index 0f7ff562..4cce9ed3 100644 --- a/kernel/networking/application_layer/csocket_http_server.c +++ b/kernel/networking/application_layer/csocket_http_server.c @@ -36,6 +36,8 @@ http_server_handle_t http_server_create(const SocketOptions* extra, const HTTPSe srv->tcp_opts.flags &= ~SOCK_OPT_DEBUG; } + srv->tcp_opts.flags |= SOCK_OPT_NONBLOCK; + return srv; } @@ -96,6 +98,12 @@ http_connection_handle_t http_server_accept(http_server_handle_t h) { if (!srv->sock) return NULL; socket_handle_t child = accept_on_socket(srv->sock); if (!child) return NULL; + //TODO for now almost everything is using SOCK_OPT_NONBLOCK for compatibilit + //in many app protocols it should be blocking, while for others such as HTTP, it should depend on the given options (blocking by default) + if (set_socket_option(child, SOCK_OPT_NONBLOCK,&(uint32_t){1}, sizeof(uint32_t)) != SOCK_OK) { + close_socket(child); + return NULL; + } HTTPConnection* conn = (HTTPConnection*)zalloc(sizeof(HTTPConnection)); if (!conn) { diff --git a/kernel/networking/application_layer/dhcp_daemon.c b/kernel/networking/application_layer/dhcp_daemon.c index 7e0b10c6..a2c082d9 100644 --- a/kernel/networking/application_layer/dhcp_daemon.c +++ b/kernel/networking/application_layer/dhcp_daemon.c @@ -122,7 +122,7 @@ static void ensure_inventory() { st.mac_ok = true; } st.needs_inform = (v4->mode == IPV4_CFG_STATIC && v4->ip != 0); - st.sock = create_socket(PROTO_UDP, NULL); + st.sock = create_socket(PROTO_UDP, &(SocketOptions){.flags = SOCK_OPT_NONBLOCK}); if (!st.sock) continue; if (set_socket_option(st.sock, SOCK_OPT_BROADCAST_ALLOWED, NULL, 0) != SOCK_OK) { close_socket(st.sock); diff --git a/kernel/networking/application_layer/dhcpv6_daemon.c b/kernel/networking/application_layer/dhcpv6_daemon.c index 2b359904..2bcf21be 100644 --- a/kernel/networking/application_layer/dhcpv6_daemon.c +++ b/kernel/networking/application_layer/dhcpv6_daemon.c @@ -261,7 +261,7 @@ static void ensure_binds() { b->mac_ok = 1; } - b->sock = create_socket(PROTO_UDP, NULL); + b->sock = create_socket(PROTO_UDP, &(SocketOptions){.flags = SOCK_OPT_NONBLOCK}); if (!b->sock) { free_sized(b, sizeof(*b)); continue; diff --git a/kernel/networking/application_layer/dns/dns.c b/kernel/networking/application_layer/dns/dns.c index bfd57f09..534795ad 100644 --- a/kernel/networking/application_layer/dns/dns.c +++ b/kernel/networking/application_layer/dns/dns.c @@ -157,7 +157,7 @@ static dns_result_t query_with_selection(const net_l4_endpoint* primary, const n if (which == DNS_USE_PRIMARY && dns_srv_is_zero(primary)) return DNS_ERR_NO_DNS; if (which == DNS_USE_SECONDARY && dns_srv_is_zero(secondary)) return DNS_ERR_NO_DNS; if (which == DNS_USE_BOTH && dns_srv_is_zero(primary) && dns_srv_is_zero(secondary)) return DNS_ERR_NO_DNS; - socket_handle_t sock = create_socket(PROTO_UDP, NULL); + socket_handle_t sock = create_socket(PROTO_UDP, &(SocketOptions){.flags = SOCK_OPT_NONBLOCK}); if (!sock) return DNS_ERR_SOCKET; dns_result_t res = DNS_ERR_NO_DNS; diff --git a/kernel/networking/application_layer/dns/dns_daemon.c b/kernel/networking/application_layer/dns/dns_daemon.c index d06852cf..c9baeb00 100644 --- a/kernel/networking/application_layer/dns/dns_daemon.c +++ b/kernel/networking/application_layer/dns/dns_daemon.c @@ -48,12 +48,7 @@ static void mdns_open_sockets(const uint8_t *group4, const uint8_t *group6) { } if (have_socket) continue; - SocketOptions opt; - memset(&opt, 0, sizeof(opt)); - opt.flags = SOCK_OPT_TTL; - opt.ttl = 255; - - socket_handle_t s = create_socket(PROTO_UDP, &opt); + socket_handle_t s = create_socket(PROTO_UDP, &(SocketOptions){.flags = SOCK_OPT_TTL | SOCK_OPT_NONBLOCK, .ttl = 255}); if (!s) continue; SockBindSpec spec; @@ -92,12 +87,7 @@ static void mdns_open_sockets(const uint8_t *group4, const uint8_t *group6) { } if (have_socket) continue; - SocketOptions opt; - memset(&opt, 0, sizeof(opt)); - opt.flags = SOCK_OPT_TTL; - opt.ttl = 255; - - socket_handle_t s = create_socket(PROTO_UDP, &opt); + socket_handle_t s = create_socket(PROTO_UDP, &(SocketOptions){.flags = SOCK_OPT_TTL | SOCK_OPT_NONBLOCK, .ttl = 255}); if (!s) continue; SockBindSpec spec; diff --git a/kernel/networking/application_layer/ntp.c b/kernel/networking/application_layer/ntp.c index 77f35cba..a89ce8d2 100644 --- a/kernel/networking/application_layer/ntp.c +++ b/kernel/networking/application_layer/ntp.c @@ -238,7 +238,7 @@ ntp_result_t ntp_poll_once(uint32_t timeout_ms){ discover_servers(&s0, &s1); if (s0 == 0 && s1 == 0) return NTP_ERR_NO_SERVER; - socket_handle_t sock = create_socket(PROTO_UDP, NULL); + socket_handle_t sock = create_socket(PROTO_UDP, &(SocketOptions){.flags = SOCK_OPT_NONBLOCK}); if (!sock) return NTP_ERR_SOCKET; uint64_t t1_0 = 0, t1_1 = 0; diff --git a/kernel/networking/application_layer/sntp.c b/kernel/networking/application_layer/sntp.c index faebbb60..277d21cf 100644 --- a/kernel/networking/application_layer/sntp.c +++ b/kernel/networking/application_layer/sntp.c @@ -87,7 +87,7 @@ sntp_result_t sntp_poll_once(uint32_t timeout_ms){ if (s0 == 0 && s1 == 0) return SNTP_ERR_NO_SERVER; - socket_handle_t sock = create_socket(PROTO_UDP, NULL); + socket_handle_t sock = create_socket(PROTO_UDP, &(SocketOptions){.flags = SOCK_OPT_NONBLOCK}); if (!sock) return SNTP_ERR_SOCKET; uint64_t t1_0 = 0, t1_1 = 0; diff --git a/kernel/networking/application_layer/ssdp_daemon.c b/kernel/networking/application_layer/ssdp_daemon.c index f553dbf2..c8c9593d 100644 --- a/kernel/networking/application_layer/ssdp_daemon.c +++ b/kernel/networking/application_layer/ssdp_daemon.c @@ -93,13 +93,7 @@ int ssdp_daemon_entry(int argc, char* argv[]) { l3_ipv4_interface_t *v4 = l2->l3_v4[j]; if (!ipv4_l3_is_ready(v4) || v4->is_localhost) continue; - uint32_t ttl = 2; - SocketOptions opt; - memset(&opt, 0, sizeof(opt)); - opt.flags = SOCK_OPT_TTL; - opt.ttl = (uint8_t)ttl; - - socket_handle_t s = create_socket(PROTO_UDP, &opt); + socket_handle_t s = create_socket(PROTO_UDP, &(SocketOptions){.flags = SOCK_OPT_TTL | SOCK_OPT_NONBLOCK, .ttl = 2}); if (!s) continue; SockBindSpec spec; @@ -129,13 +123,7 @@ int ssdp_daemon_entry(int argc, char* argv[]) { l3_ipv6_interface_t *v6 = l2->l3_v6[j]; if (!ipv6_l3_is_ready(v6) || v6->is_localhost) continue; - uint32_t ttl = 2; - SocketOptions opt; - memset(&opt, 0, sizeof(opt)); - opt.flags = SOCK_OPT_TTL; - opt.ttl = (uint8_t)ttl; - - socket_handle_t s = create_socket(PROTO_UDP, &opt); + socket_handle_t s = create_socket(PROTO_UDP, &(SocketOptions){.flags = SOCK_OPT_TTL | SOCK_OPT_NONBLOCK, .ttl = 2}); if (!s) continue; SockBindSpec spec; diff --git a/kernel/networking/internet_layer/ipv4_route.c b/kernel/networking/internet_layer/ipv4_route.c index d06f6fb9..84627673 100644 --- a/kernel/networking/internet_layer/ipv4_route.c +++ b/kernel/networking/internet_layer/ipv4_route.c @@ -26,6 +26,14 @@ bool ipv4_tx_plan_valid(const ipv4_tx_plan_t* plan) { return v4->ip == plan->src_ip; } +bool ipv4_tx_plan_onlink(const ipv4_tx_plan_t* plan, uint32_t dst) { + if (!ipv4_tx_plan_valid(plan)) return false; + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(plan->l3_id); + if (ipv4_is_loopback(dst)) return v4->is_localhost; + if (ipv4_is_multicast(dst) || ipv4_is_limited_broadcast(dst) || dst == v4->broadcast) return true; + return v4->mask && (dst & v4->mask) == (v4->ip & v4->mask); +} + bool ipv4_build_tx_plan(uint32_t dst, const ip_tx_opts_t* hint, ipv4_tx_plan_t* out){ if (!out) return false; out->l3_id = 0; diff --git a/kernel/networking/internet_layer/ipv4_route.h b/kernel/networking/internet_layer/ipv4_route.h index 82a39c5a..e3d00b53 100644 --- a/kernel/networking/internet_layer/ipv4_route.h +++ b/kernel/networking/internet_layer/ipv4_route.h @@ -39,6 +39,7 @@ typedef struct { } ipv4_tx_plan_t; bool ipv4_tx_plan_valid(const ipv4_tx_plan_t* plan); +bool ipv4_tx_plan_onlink(const ipv4_tx_plan_t* plan, uint32_t dst); bool ipv4_build_tx_plan(uint32_t dst, const ip_tx_opts_t* hint, ipv4_tx_plan_t* out); bool ipv4_rt_pick_best_l3_in(const uint8_t* l3_ids, int n_ids, uint32_t dst, uint8_t* out_l3); diff --git a/kernel/networking/internet_layer/ipv6_route.c b/kernel/networking/internet_layer/ipv6_route.c index 48765ad5..fb330892 100644 --- a/kernel/networking/internet_layer/ipv6_route.c +++ b/kernel/networking/internet_layer/ipv6_route.c @@ -36,6 +36,14 @@ bool ipv6_tx_plan_valid(const ipv6_tx_plan_t* plan) { return ipv6_cmp(v6->ip, plan->src_ip) == 0; } +bool ipv6_tx_plan_onlink(const ipv6_tx_plan_t* plan, const uint8_t dst[16]) { + if (!dst || !ipv6_tx_plan_valid(plan)) return false; + l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(plan->l3_id); + if (ipv6_is_loopback(dst)) return v6->is_localhost; + if (ipv6_is_linklocal(dst) || ipv6_is_linkscope_mcast(dst) || ipv6_is_multicast(dst)) return true; + return v6->prefix_len && ipv6_common_prefix_len(v6->ip, dst) >= v6->prefix_len; +} + bool ipv6_build_tx_plan(const uint8_t dst[16], const ip_tx_opts_t* hint, ipv6_tx_plan_t* out) { if (!dst || !out) return false; diff --git a/kernel/networking/internet_layer/ipv6_route.h b/kernel/networking/internet_layer/ipv6_route.h index 6b714131..62a11154 100644 --- a/kernel/networking/internet_layer/ipv6_route.h +++ b/kernel/networking/internet_layer/ipv6_route.h @@ -40,6 +40,7 @@ typedef struct { } ipv6_tx_plan_t; bool ipv6_tx_plan_valid(const ipv6_tx_plan_t* plan); +bool ipv6_tx_plan_onlink(const ipv6_tx_plan_t* plan, const uint8_t dst[16]); bool ipv6_build_tx_plan(const uint8_t dst[16], const ip_tx_opts_t* hint, ipv6_tx_plan_t* out); #ifdef __cplusplus diff --git a/kernel/networking/processes/net_proc.c b/kernel/networking/processes/net_proc.c index b70e6f4d..d415d08b 100644 --- a/kernel/networking/processes/net_proc.c +++ b/kernel/networking/processes/net_proc.c @@ -34,7 +34,7 @@ #define PROBE_INTERVAL_MS 50 static int udp_probe_server(uint32_t probe_ip, uint16_t probe_port, net_l4_endpoint *out_l4) { - socket_handle_t sock = create_socket(PROTO_UDP, NULL); + socket_handle_t sock = create_socket(PROTO_UDP, &(SocketOptions){.flags = SOCK_OPT_NONBLOCK}); if (!sock) return 0; if (set_socket_option(sock, SOCK_OPT_BROADCAST_ALLOWED, NULL, 0) < 0) { close_socket(sock); diff --git a/kernel/networking/transport_layer/csocket_packet.c b/kernel/networking/transport_layer/csocket_packet.c index bd6ece48..c4973a81 100644 --- a/kernel/networking/transport_layer/csocket_packet.c +++ b/kernel/networking/transport_layer/csocket_packet.c @@ -94,7 +94,7 @@ socket_impl_t socket_packet_create(ksocket_t* owner, const SocketOptions* extra) if (!owner) return NULL; if (socket_core_special_kind(owner) != SOCKET_SPECIAL_PACKET) return NULL; - uint32_t supported = SOCK_OPT_RECV_TIMEOUT | SOCK_OPT_BUF_SIZE | SOCK_OPT_DEBUG | SOCK_OPT_FILTER | SOCK_OPT_SPECIAL; + uint32_t supported = SOCK_OPT_RECV_TIMEOUT | SOCK_OPT_BUF_SIZE | SOCK_OPT_DEBUG | SOCK_OPT_FILTER | SOCK_OPT_SPECIAL | SOCK_OPT_NONBLOCK; if (extra && (extra->flags & ~supported)) return NULL; packet_socket_t* s = (packet_socket_t*)zalloc(sizeof(packet_socket_t)); @@ -120,6 +120,7 @@ socket_impl_t socket_packet_create(ksocket_t* owner, const SocketOptions* extra) s->options.flags |= SOCK_OPT_RECV_TIMEOUT; s->options.recv_timeout_ms = extra->recv_timeout_ms; } + if (extra->flags & SOCK_OPT_NONBLOCK) s->options.flags |= SOCK_OPT_NONBLOCK; if (extra->flags & SOCK_OPT_BUF_SIZE) { if (!extra->buf_size) { release(s); @@ -187,6 +188,7 @@ int32_t socket_setopt_packet(socket_impl_t sh, int32_t opt, const void* value, u switch ((uint32_t)opt) { case SOCK_OPT_RECV_TIMEOUT: case SOCK_OPT_DEBUG: + case SOCK_OPT_NONBLOCK: return socket_common_options_set(&s->options, opt, value, len); case SOCK_OPT_FILTER: return packet_set_filter(s, value, len); @@ -234,6 +236,7 @@ int32_t socket_getopt_packet(socket_impl_t sh, int32_t opt, void* value, uint32_ case SOCK_GET_OPT_RECV_TIMEOUT: case SOCK_GET_OPT_DEBUG: case SOCK_GET_OPT_BUF_SIZE: + case SOCK_GET_OPT_NONBLOCK: return socket_common_options_get(&s->options, opt, value, len); case SOCK_GET_CONNECTED: case SOCK_GET_LISTENING: @@ -294,7 +297,7 @@ int64_t socket_recv_packet(socket_impl_t sh, void* buf, uint64_t len) { bool ready = s->head != s->tail; irq_restore(irq); if (ready) break; - if (!(s->options.flags & SOCK_OPT_RECV_TIMEOUT) || !s->options.recv_timeout_ms) return SOCK_ERR_WOULDBLOCK; + if (s->options.flags & SOCK_OPT_NONBLOCK) return SOCK_ERR_WOULDBLOCK; uint32_t start_ms = (uint32_t)get_time(); while (1) { @@ -303,11 +306,14 @@ int64_t socket_recv_packet(socket_impl_t sh, void* buf, uint64_t len) { irq_restore(irq); if (ready) break; - uint32_t now_ms = (uint32_t)get_time(); - uint32_t elapsed_ms = now_ms - start_ms; - if (elapsed_ms >= s->options.recv_timeout_ms) return SOCK_ERR_WOULDBLOCK; - uint32_t wait_ms = s->options.recv_timeout_ms - elapsed_ms; - msleep(wait_ms > 5 ? 5 : wait_ms); + if ((s->options.flags & SOCK_OPT_RECV_TIMEOUT) && s->options.recv_timeout_ms) { + uint32_t now_ms = (uint32_t)get_time(); + uint32_t elapsed_ms = now_ms - start_ms; + if (elapsed_ms >= s->options.recv_timeout_ms) return SOCK_ERR_WOULDBLOCK; + uint32_t wait_ms = s->options.recv_timeout_ms - elapsed_ms; + if (wait_ms > 5) wait_ms = 5; + msleep(wait_ms); + }else msleep(5); } } diff --git a/kernel/networking/transport_layer/csocket_raw.c b/kernel/networking/transport_layer/csocket_raw.c index 0263a4cf..ceaa3be7 100644 --- a/kernel/networking/transport_layer/csocket_raw.c +++ b/kernel/networking/transport_layer/csocket_raw.c @@ -11,6 +11,7 @@ #include "networking/internet_layer/ipv6_utils.h" #include "networking/link_layer/eth.h" #include "networking/transport_layer/trans_utils.h" +#include "networking/transport_layer/socket_bind.h" #include "syscalls/syscalls.h" #define RAW_SOCKET_MAX 64 @@ -125,7 +126,7 @@ socket_impl_t socket_raw_create(ksocket_t* owner, const SocketOptions* extra) { if (proto != PROTO_ICMP && proto != PROTO_IGMP && proto != PROTO_ICMPV6) return NULL; //TODO add ESP/AH sock if needed - uint32_t supported = SOCK_OPT_RECV_TIMEOUT | SOCK_OPT_DEBUG | SOCK_OPT_DONTFRAG | SOCK_OPT_TTL | SOCK_OPT_FILTER | SOCK_OPT_SPECIAL; + uint32_t supported = SOCK_OPT_RECV_TIMEOUT | SOCK_OPT_DEBUG | SOCK_OPT_DONTFRAG | SOCK_OPT_TTL | SOCK_OPT_FILTER | SOCK_OPT_SPECIAL | SOCK_OPT_NONBLOCK | SOCK_OPT_DONTROUTE; if (extra && (extra->flags & ~supported)) return NULL; raw_socket_t* s = (raw_socket_t*)zalloc(sizeof(raw_socket_t)); @@ -151,6 +152,8 @@ socket_impl_t socket_raw_create(ksocket_t* owner, const SocketOptions* extra) { } if (extra->flags & SOCK_OPT_DONTFRAG) s->options.flags |= SOCK_OPT_DONTFRAG; + if (extra->flags & SOCK_OPT_NONBLOCK) s->options.flags |= SOCK_OPT_NONBLOCK; + if (extra->flags & SOCK_OPT_DONTROUTE) s->options.flags |= SOCK_OPT_DONTROUTE; if ((extra->flags & SOCK_OPT_TTL) && extra->ttl) { s->options.flags |= SOCK_OPT_TTL; s->options.ttl = extra->ttl; @@ -223,6 +226,8 @@ int32_t socket_setopt_raw(socket_impl_t sh, int32_t opt, const void* value, uint case SOCK_OPT_DEBUG: case SOCK_OPT_DONTFRAG: case SOCK_OPT_TTL: + case SOCK_OPT_NONBLOCK: + case SOCK_OPT_DONTROUTE: return socket_common_options_set(&s->options, opt, value, len); case SOCK_OPT_FILTER: return raw_set_filter(s, value, len); @@ -278,6 +283,8 @@ int32_t socket_getopt_raw(socket_impl_t sh, int32_t opt, void* value, uint32_t* case SOCK_GET_OPT_DEBUG: case SOCK_GET_OPT_DONTFRAG: case SOCK_GET_OPT_TTL: + case SOCK_GET_OPT_NONBLOCK: + case SOCK_GET_OPT_DONTROUTE: return socket_common_options_get(&s->options, opt, value, len); case SOCK_GET_OPT_SEND_TIMEOUT: case SOCK_GET_OPT_BUF_SIZE: @@ -402,17 +409,29 @@ int64_t socket_sendto_raw(socket_impl_t sh, const net_l4_endpoint* dst, const vo if (proto == PROTO_ICMP || proto == PROTO_IGMP) { uint32_t dst_ip = 0; memcpy(&dst_ip, dst->ip, sizeof(dst_ip)); + if (s->options.flags & SOCK_OPT_DONTROUTE) { + ipv4_tx_plan_t plan; + if (!socket_bind_build_ipv4_tx_plan(&s->bind_spec, s->bound, dst_ip, &plan) || !ipv4_tx_plan_onlink(&plan, dst_ip)) { + netpkt_unref(pkt); + return SOCK_ERR_NO_ROUTE; + } + } return ipv4_send_packet(dst_ip, (uint8_t)proto, pkt, txp, ttl, dontfrag) ? (int64_t)len : SOCK_ERR_SYS; } + ipv6_tx_plan_t plan; + if (!ipv6_build_tx_plan(dst->ip, txp, &plan)) { + netpkt_unref(pkt); + return (s->options.flags & SOCK_OPT_DONTROUTE) ? SOCK_ERR_NO_ROUTE : SOCK_ERR_SYS; + } + + if ((s->options.flags & SOCK_OPT_DONTROUTE) && !ipv6_tx_plan_onlink(&plan, dst->ip)){ + netpkt_unref(pkt); + return SOCK_ERR_NO_ROUTE; + } + if (len >= 4) { - ipv6_tx_plan_t plan; - if (!ipv6_build_tx_plan(dst->ip, txp, &plan)) { - netpkt_unref(pkt); - return SOCK_ERR_SYS; - } - ((uint8_t*)p)[2] = 0; - ((uint8_t*)p)[3] = 0; + memset((uint8_t*)p + 2, 0, sizeof(uint16_t)); uint16_t sum = bswap16(checksum16_pipv6(plan.src_ip, dst->ip, PROTO_ICMPV6, p, (uint32_t)len)); memcpy((uint8_t*)p + 2, &sum, sizeof(sum)); } @@ -429,7 +448,7 @@ int64_t socket_recv_raw(socket_impl_t sh, void* buf, uint64_t len, net_l4_endpoi bool ready = s->rx_count != 0; irq_restore(irq); if (ready) break; - if (!(s->options.flags & SOCK_OPT_RECV_TIMEOUT) || !s->options.recv_timeout_ms) return SOCK_ERR_WOULDBLOCK; + if (s->options.flags & SOCK_OPT_NONBLOCK) return SOCK_ERR_WOULDBLOCK; uint32_t start_ms = (uint32_t)get_time(); while (1) { @@ -438,11 +457,14 @@ int64_t socket_recv_raw(socket_impl_t sh, void* buf, uint64_t len, net_l4_endpoi irq_restore(irq); if (ready) break; - uint32_t now_ms = (uint32_t)get_time(); - uint32_t elapsed_ms = now_ms - start_ms; - if (elapsed_ms >= s->options.recv_timeout_ms) return SOCK_ERR_WOULDBLOCK; - uint32_t wait_ms = s->options.recv_timeout_ms - elapsed_ms; - msleep(wait_ms > 5 ? 5 : wait_ms); + if ((s->options.flags & SOCK_OPT_RECV_TIMEOUT) && s->options.recv_timeout_ms) { + uint32_t now_ms = (uint32_t)get_time(); + uint32_t elapsed_ms = now_ms - start_ms; + if (elapsed_ms >= s->options.recv_timeout_ms) return SOCK_ERR_WOULDBLOCK; + uint32_t wait_ms = s->options.recv_timeout_ms - elapsed_ms; + if (wait_ms > 5) wait_ms = 5; + msleep(wait_ms); + }else msleep(5); } } diff --git a/kernel/networking/transport_layer/csocket_tcp.c b/kernel/networking/transport_layer/csocket_tcp.c index 03717623..83a0d863 100644 --- a/kernel/networking/transport_layer/csocket_tcp.c +++ b/kernel/networking/transport_layer/csocket_tcp.c @@ -59,10 +59,52 @@ static ksocket_t* tcp_socket_pop_pending_at(tcp_socket_t* s, int idx) { return client; } +static int32_t tcp_socket_connection_state(tcp_socket_t* s) { + if (!s) return -1; + if (!s->flow.flow_generation) { + s->connected = false; + memset(&s->flow, 0, sizeof(s->flow)); + s->remoteEP.port = 0; + s->remoteEP.ver = IP_VER4; + memset(s->remoteEP.ip, 0, sizeof(s->remoteEP.ip)); + return -1; + } + + tcp_flow_t* flow = tcp_flow_from_ctx(&s->flow); + if (!flow) { + s->connected = false; + memset(&s->flow, 0, sizeof(s->flow)); + s->remoteEP.port = 0; + s->remoteEP.ver = IP_VER4; + memset(s->remoteEP.ip, 0, sizeof(s->remoteEP.ip)); + return -1; + } + + tcp_state_t state = flow->base.state; + tcp_flow_put(flow); + + if (state == TCP_ESTABLISHED || state == TCP_CLOSE_WAIT) { + s->connected = true; + return 1; + } + + if (state == TCP_STATE_CLOSED || state == TCP_TIME_WAIT) { + tcp_flow_release_closed(&s->flow); + s->connected = false; + memset(&s->flow, 0, sizeof(s->flow)); + s->remoteEP.port = 0; + s->remoteEP.ver = IP_VER4; + memset(s->remoteEP.ip, 0, sizeof(s->remoteEP.ip)); + return -1; + } + + return 0; +} + socket_impl_t socket_tcp_create(ksocket_t* owner, const SocketOptions* extra) { if (!owner) return NULL; - uint32_t supported = SOCK_OPT_KEEPALIVE | SOCK_OPT_KEEPALIVE_INTERVAL | SOCK_OPT_SEND_TIMEOUT | SOCK_OPT_RECV_TIMEOUT | SOCK_OPT_BUF_SIZE | SOCK_OPT_DEBUG | SOCK_OPT_DONTFRAG | SOCK_OPT_TTL | SOCK_OPT_SEND_BUF_SIZE | SOCK_OPT_TCP_NO_DELAY; + uint32_t supported = SOCK_OPT_KEEPALIVE | SOCK_OPT_KEEPALIVE_INTERVAL | SOCK_OPT_SEND_TIMEOUT | SOCK_OPT_RECV_TIMEOUT | SOCK_OPT_BUF_SIZE | SOCK_OPT_DEBUG | SOCK_OPT_DONTFRAG | SOCK_OPT_TTL | SOCK_OPT_SEND_BUF_SIZE | SOCK_OPT_TCP_NO_DELAY | SOCK_OPT_NONBLOCK | SOCK_OPT_DONTROUTE; if (extra) { if (extra->flags & ~supported) return NULL; if ((extra->flags & SOCK_OPT_DEBUG) && extra->debug_level > SOCK_DBG_ALL) return NULL; @@ -132,6 +174,12 @@ int32_t socket_setopt_tcp(socket_impl_t sh, int32_t opt, const void* value, uint if (s->flow.flow_generation) tcp_flow_apply_socket_options(&s->flow, &s->options); return SOCK_OK; } + case SOCK_OPT_DONTROUTE: { + if (s->flow.flow_generation) return SOCK_ERR_STATE; + return socket_common_options_set(&s->options, opt, value, len); + } + case SOCK_OPT_NONBLOCK: + return socket_common_options_set(&s->options, opt, value, len); case SOCK_OPT_MCAST_JOIN: case SOCK_OPT_MCAST_LEAVE: case SOCK_OPT_BROADCAST_ALLOWED: @@ -188,7 +236,7 @@ int32_t socket_getopt_tcp(socket_impl_t sh, int32_t opt, void* value, uint32_t* v = s->localPort != 0; break; case SOCK_GET_CONNECTED: - v = s->connected; + v = tcp_socket_connection_state(s) > 0; break; case SOCK_GET_LISTENING: v = s->listening; @@ -261,6 +309,8 @@ int32_t socket_getopt_tcp(socket_impl_t sh, int32_t opt, void* value, uint32_t* case SOCK_GET_OPT_DEBUG: case SOCK_GET_OPT_DONTFRAG: case SOCK_GET_OPT_TTL: + case SOCK_GET_OPT_NONBLOCK: + case SOCK_GET_OPT_DONTROUTE: return socket_common_options_get(&s->options, opt, value, len); default: return SOCK_ERR_INVAL; @@ -356,10 +406,13 @@ ksocket_t* socket_accept_tcp(socket_impl_t sh) { tcp_socket_t* s = (tcp_socket_t*)sh; if (!s || !s->listening || !s->pending) return NULL; - const int max_empty_iters = 200; - int iter = 0; + uint32_t start_ms = get_time(); while (tcp_socket_backlog_len(s) == 0) { - if (++iter > max_empty_iters) return NULL; + if (s->options.flags & SOCK_OPT_NONBLOCK) return NULL; + if ((s->options.flags & SOCK_OPT_RECV_TIMEOUT) && s->options.recv_timeout_ms) { + uint32_t elapsed = get_time() - start_ms; + if (elapsed >= s->options.recv_timeout_ms) return NULL; + } msleep(5); } @@ -389,74 +442,109 @@ int32_t socket_connect_tcp(socket_impl_t sh, const net_l4_endpoint* dst) { if (dst) ev.dst_ep = *dst; netlog_socket_event(&s->options, &ev); - if (s->connected || s->listening) return SOCK_ERR_STATE; + if (s->listening) return SOCK_ERR_STATE; if (!dst || !dst->port) return SOCK_ERR_INVAL; + if (s->flow.flow_generation) { + if (s->remoteEP.port && + (s->remoteEP.ver != dst->ver || s->remoteEP.port != dst->port || memcmp(s->remoteEP.ip, dst->ip, dst->ver == IP_VER6 ? 16 : 4) != 0)) return SOCK_ERR_STATE; + } else { + if (s->connected) return SOCK_ERR_STATE; + + net_l4_endpoint d = *dst; + uint8_t chosen_l3 = 0; + + if (d.ver == IP_VER6) { + ipv6_tx_plan_t p6; + if (!socket_bind_build_ipv6_tx_plan(&s->bindSpec, s->localPort != 0, d.ip, &p6)) return SOCK_ERR_NO_ROUTE; + if ((s->options.flags & SOCK_OPT_DONTROUTE) && !ipv6_tx_plan_onlink(&p6, d.ip)) return SOCK_ERR_NO_ROUTE; + chosen_l3 = p6.l3_id; + } else if (d.ver == IP_VER4) { + uint32_t dip = 0; + memcpy(&dip, d.ip, 4); + ipv4_tx_plan_t p4; + if (!socket_bind_build_ipv4_tx_plan(&s->bindSpec, s->localPort != 0, dip, &p4)) return SOCK_ERR_NO_ROUTE; + if ((s->options.flags & SOCK_OPT_DONTROUTE) && !ipv4_tx_plan_onlink(&p4, dip)) return SOCK_ERR_NO_ROUTE; + chosen_l3 = p4.l3_id; + } else return SOCK_ERR_INVAL; - net_l4_endpoint d = *dst; - uint8_t chosen_l3 = 0; - - if (d.ver == IP_VER6) { - ipv6_tx_plan_t p6; - if (!socket_bind_build_ipv6_tx_plan(&s->bindSpec, s->localPort, d.ip, &p6)) return SOCK_ERR_SYS; - chosen_l3 = p6.l3_id; - } else if (d.ver == IP_VER4) { - uint32_t dip = 0; - memcpy(&dip, d.ip, 4); - ipv4_tx_plan_t p4; - if (!socket_bind_build_ipv4_tx_plan(&s->bindSpec, s->localPort, dip, &p4)) return SOCK_ERR_SYS; - chosen_l3 = p4.l3_id; - } else return SOCK_ERR_INVAL; - - if (!chosen_l3) return SOCK_ERR_SYS; - - if (d.ver == IP_VER4) { - l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(chosen_l3); - if (!ipv4_l3_is_ready(v4)) return SOCK_ERR_SYS; - } else if (d.ver == IP_VER6) { - l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(chosen_l3); - if (!ipv6_l3_is_tcp_usable(v6)) return SOCK_ERR_SYS; - } else return SOCK_ERR_INVAL; - - bool ephemeral_allocated = false; - if (s->localPort == 0) { - if (!s->ownerSocket) return SOCK_ERR_SYS; - socket_bind_token_t token = 0; - int p = socket_bind_alloc_ephemeral_l3(s->ownerSocket, PROTO_TCP, chosen_l3, &token); - if (p < 0) return SOCK_ERR_NO_PORT; - s->localPort = (uint16_t)p; - s->bindToken = token; - memset(&s->bindSpec, 0, sizeof(s->bindSpec)); - s->bindSpec.kind = BIND_L3; - s->bindSpec.ver = d.ver; - s->bindSpec.l3_id = chosen_l3; - ephemeral_allocated = true; - } + if (!chosen_l3) return SOCK_ERR_NO_ROUTE; - memset(&s->flow, 0, sizeof(s->flow)); - bool ok = tcp_handshake_l3(chosen_l3, s->localPort, &d, &s->flow, &s->options); - if (ok && d.ver == IP_VER4) { - l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(chosen_l3); - ok = ipv4_l3_is_ready(v4); - } else if (ok && d.ver == IP_VER6) ok = ipv6_l3_is_tcp_usable(l3_ipv6_find_by_id(chosen_l3)); - - if (!ok) { - if (ephemeral_allocated) { - if (s->bindToken) { - socket_bind_remove(s->bindToken); - s->bindToken = 0; - } - s->localPort = 0; + if (d.ver == IP_VER4) { + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(chosen_l3); + if (!ipv4_l3_is_ready(v4)) return SOCK_ERR_SYS; + } else if (d.ver == IP_VER6) { + l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(chosen_l3); + if (!ipv6_l3_is_tcp_usable(v6)) return SOCK_ERR_SYS; + } else return SOCK_ERR_INVAL; + + bool ephemeral_allocated = false; + if (s->localPort == 0) { + if (!s->ownerSocket) return SOCK_ERR_SYS; + socket_bind_token_t token = 0; + int p = socket_bind_alloc_ephemeral_l3(s->ownerSocket, PROTO_TCP, chosen_l3, &token); + if (p < 0) return SOCK_ERR_NO_PORT; + s->localPort = (uint16_t)p; + s->bindToken = token; memset(&s->bindSpec, 0, sizeof(s->bindSpec)); + s->bindSpec.kind = BIND_L3; + s->bindSpec.ver = d.ver; + s->bindSpec.l3_id = chosen_l3; + ephemeral_allocated = true; + } + + memset(&s->flow, 0, sizeof(s->flow)); + bool ok = tcp_handshake_l3(chosen_l3, s->localPort, &d, &s->flow, &s->options); + if (ok && d.ver == IP_VER4) { + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(chosen_l3); + ok = ipv4_l3_is_ready(v4); + } else if (ok && d.ver == IP_VER6) ok = ipv6_l3_is_tcp_usable(l3_ipv6_find_by_id(chosen_l3)); + + if (!ok) { + if (s->flow.flow_generation) { + tcp_flow_abort(&s->flow); + memset(&s->flow, 0, sizeof(s->flow)); + } + s->connected = false; s->remoteEP.port = 0; s->remoteEP.ver = IP_VER4; memset(s->remoteEP.ip, 0, sizeof(s->remoteEP.ip)); + + if (ephemeral_allocated) { + if (s->bindToken) { + socket_bind_remove(s->bindToken); + s->bindToken = 0; + } + s->localPort = 0; + memset(&s->bindSpec, 0, sizeof(s->bindSpec)); + } + return SOCK_ERR_SYS; } - return SOCK_ERR_SYS; + + s->remoteEP = d; } - s->remoteEP = d; - s->connected = true; + uint32_t start_ms = get_time(); + uint32_t timeout_ms = (s->options.flags & SOCK_OPT_SEND_TIMEOUT) && s->options.send_timeout_ms ? s->options.send_timeout_ms : TCP_CONNECT_TIMEOUT_MS; + + while (1) { + int32_t state = tcp_socket_connection_state(s); + if (state > 0) break; + if (state < 0) return SOCK_ERR_STATE; + if (s->options.flags & SOCK_OPT_NONBLOCK) return SOCK_ERR_WOULDBLOCK; + + uint32_t now_ms = get_time(); + if (now_ms - start_ms >= timeout_ms) { + if (s->flow.flow_generation) tcp_flow_abort(&s->flow); + s->connected = false; + memset(&s->flow, 0, sizeof(s->flow)); + s->remoteEP.port = 0; + s->remoteEP.ver = IP_VER4; + memset(s->remoteEP.ip, 0, sizeof(s->remoteEP.ip)); + return SOCK_ERR_WOULDBLOCK; + } + msleep(5); + } netlog_socket_event_t ev1 = {0}; ev1.comp = NETLOG_COMP_TCP; @@ -483,7 +571,8 @@ int64_t socket_send_tcp(socket_impl_t sh, const void* buf, uint64_t len) { ev.remote_ep = s->remoteEP; netlog_socket_event(&s->options, &ev); - if (!s->connected || !s->flow.flow_generation) return SOCK_ERR_STATE; + int connection_state = tcp_socket_connection_state(s); + if (connection_state <= 0 || !s->flow.flow_generation) return connection_state == 0 ? SOCK_ERR_WOULDBLOCK : SOCK_ERR_STATE; if (!buf && len) return SOCK_ERR_INVAL; if (!len) return 0; @@ -513,14 +602,16 @@ int64_t socket_send_tcp(socket_impl_t sh, const void* buf, uint64_t len) { } } if (s->flow.payload.size) return (int64_t)s->flow.payload.size; - if (!(s->options.flags & SOCK_OPT_SEND_TIMEOUT) || !s->options.send_timeout_ms) return SOCK_ERR_WOULDBLOCK; - - uint32_t now_ms = (uint32_t)get_time(); - uint32_t elapsed_ms = now_ms - start_ms; - if (elapsed_ms >= s->options.send_timeout_ms) return SOCK_ERR_WOULDBLOCK; - uint32_t wait_ms = s->options.send_timeout_ms - elapsed_ms; - if (wait_ms > 5) wait_ms = 5; - msleep(wait_ms); + if (s->options.flags & SOCK_OPT_NONBLOCK) return SOCK_ERR_WOULDBLOCK; + + if ((s->options.flags & SOCK_OPT_SEND_TIMEOUT) && s->options.send_timeout_ms) { + uint32_t now_ms = (uint32_t)get_time(); + uint32_t elapsed_ms = now_ms - start_ms; + if (elapsed_ms >= s->options.send_timeout_ms) return SOCK_ERR_WOULDBLOCK; + uint32_t wait_ms = s->options.send_timeout_ms - elapsed_ms; + if (wait_ms > 5) wait_ms = 5; + msleep(wait_ms); + }else msleep(5); } } @@ -538,25 +629,33 @@ int64_t socket_recv_tcp(socket_impl_t sh, void* buf, uint64_t len) { netlog_socket_event(&s->options, &ev); if (!buf || !len) return 0; - if (!s->connected || !s->flow.flow_generation) return s->connected ? SOCK_ERR_WOULDBLOCK : 0; + int connection_state = tcp_socket_connection_state(s); + if (connection_state < 0) return 0; + if (connection_state == 0 || !s->flow.flow_generation) return SOCK_ERR_WOULDBLOCK; + //TODO add receive low water mark support when socket events exist uint32_t start_ms = (uint32_t)get_time(); while (1) { - int64_t n = tcp_flow_read(&s->flow, buf, len); - if (n > 0) return n; - if (n == TCP_DISCONNECT) return 0; - if (n < 0) return SOCK_ERR_STATE; - if (tcp_flow_recv_closed(&s->flow)) return 0; - if (!s->connected) return 0; - if (!(s->options.flags & SOCK_OPT_RECV_TIMEOUT) || !s->options.recv_timeout_ms) return SOCK_ERR_WOULDBLOCK; - - uint32_t now_ms = (uint32_t)get_time(); - uint32_t elapsed_ms = now_ms - start_ms; - if (elapsed_ms >= s->options.recv_timeout_ms) return SOCK_ERR_WOULDBLOCK; - - uint32_t wait_ms = s->options.recv_timeout_ms - elapsed_ms; - if (wait_ms > 5) wait_ms = 5; - msleep(wait_ms); + uint32_t readable = tcp_flow_readable(&s->flow); + bool closed = tcp_flow_recv_closed(&s->flow); + if (readable || closed) { + int64_t n = tcp_flow_read(&s->flow, buf, len); + if (n > 0) return n; + if (n == TCP_DISCONNECT) return 0; + if (n < 0) return SOCK_ERR_STATE; + } + if (closed || !s->connected) return 0; + if (s->options.flags & SOCK_OPT_NONBLOCK) return SOCK_ERR_WOULDBLOCK; + if ((s->options.flags & SOCK_OPT_RECV_TIMEOUT) && s->options.recv_timeout_ms) { + + uint32_t now_ms = (uint32_t)get_time(); + uint32_t elapsed_ms = now_ms - start_ms; + if (elapsed_ms >= s->options.recv_timeout_ms) return SOCK_ERR_WOULDBLOCK; + + uint32_t wait_ms = s->options.recv_timeout_ms - elapsed_ms; + if (wait_ms > 5) wait_ms = 5; + msleep(wait_ms); + }else msleep(5); } } @@ -572,13 +671,14 @@ int32_t socket_close_tcp(socket_impl_t sh) { ev.remote_ep = s->remoteEP; netlog_socket_event(&s->options, &ev); - if (s->connected && s->flow.flow_generation) { - if (!tcp_flow_is_closed(&s->flow)) { - tcp_flow_flush(&s->flow); - tcp_flow_close(&s->flow); - } - s->connected = false; - memset(&s->flow, 0, sizeof(s->flow)); + int32_t connection_state = tcp_socket_connection_state(s); + if (s->flow.flow_generation) { + if (connection_state > 0) { + if (!tcp_flow_is_closed(&s->flow)) { + tcp_flow_flush(&s->flow); + tcp_flow_close(&s->flow); + } + } else if (connection_state == 0) tcp_flow_abort(&s->flow); } if (s->pending) { @@ -602,6 +702,7 @@ int32_t socket_close_tcp(socket_impl_t sh) { s->localPort = 0; memset(&s->bindSpec, 0, sizeof(s->bindSpec)); s->connected = false; + memset(&s->flow, 0, sizeof(s->flow)); s->remoteEP.port = 0; s->remoteEP.ver = IP_VER4; memset(s->remoteEP.ip, 0, sizeof(s->remoteEP.ip)); @@ -656,11 +757,13 @@ uint32_t tcp_accept_enqueue(ksocket_t* listener, ip_version_t ipver, const void* uint16_t owner_pid = socket_core_pid(s->ownerSocket); if (!socket_core_alloc(PROTO_TCP, SOCKET_SPECIAL_NONE, owner_pid, &child_owner)) return 0; - tcp_socket_t* child = (tcp_socket_t*)socket_tcp_create(child_owner, &s->options); + tcp_socket_t* child = (tcp_socket_t*)socket_tcp_create(child_owner, NULL); if (!child) { socket_core_close_socket(child_owner); return 0; } + child->options = s->options; + child->options.flags &= ~SOCK_OPT_NONBLOCK; child->localPort = dst_port; child->remoteEP.ver = ipver; diff --git a/kernel/networking/transport_layer/csocket_udp.c b/kernel/networking/transport_layer/csocket_udp.c index 39ae3903..b6e95d11 100644 --- a/kernel/networking/transport_layer/csocket_udp.c +++ b/kernel/networking/transport_layer/csocket_udp.c @@ -220,7 +220,7 @@ uint32_t socket_udp_input(ksocket_t* socket, ip_version_t ipver, uint8_t l3_id, socket_impl_t udp_socket_create(ksocket_t* owner, const SocketOptions* extra) { if (!owner) return NULL; - uint32_t supported = SOCK_OPT_RECV_TIMEOUT | SOCK_OPT_SEND_TIMEOUT | SOCK_OPT_BUF_SIZE | SOCK_OPT_DEBUG | SOCK_OPT_DONTFRAG | SOCK_OPT_BROADCAST_ALLOWED | SOCK_OPT_TTL | SOCK_OPT_MCAST_JOIN; + uint32_t supported = SOCK_OPT_RECV_TIMEOUT | SOCK_OPT_SEND_TIMEOUT | SOCK_OPT_BUF_SIZE | SOCK_OPT_DEBUG | SOCK_OPT_DONTFRAG | SOCK_OPT_BROADCAST_ALLOWED | SOCK_OPT_TTL | SOCK_OPT_MCAST_JOIN | SOCK_OPT_NONBLOCK | SOCK_OPT_DONTROUTE; if (extra) { if (extra->flags & ~supported) return NULL; if ((extra->flags & SOCK_OPT_DEBUG) && extra->debug_level > SOCK_DBG_ALL) return NULL; @@ -328,6 +328,18 @@ int32_t socket_connect_udp(socket_impl_t sh, const net_l4_endpoint* dst) { if (!dst || !dst->port) return SOCK_ERR_INVAL; if (dst->ver != IP_VER4 && dst->ver != IP_VER6) return SOCK_ERR_INVAL; + if (dst->ver == IP_VER4) { + uint32_t dip = 0; + memcpy(&dip, dst->ip, 4); + ipv4_tx_plan_t plan; + if (!socket_bind_build_ipv4_tx_plan(&s->bindSpec, s->localPort != 0, dip, &plan)) return SOCK_ERR_NO_ROUTE; + if ((s->options.flags & SOCK_OPT_DONTROUTE) && !ipv4_tx_plan_onlink(&plan, dip)) return SOCK_ERR_NO_ROUTE; + } else { + ipv6_tx_plan_t plan; + if (!socket_bind_build_ipv6_tx_plan(&s->bindSpec, s->localPort != 0, dst->ip, &plan)) return SOCK_ERR_NO_ROUTE; + if ((s->options.flags & SOCK_OPT_DONTROUTE) && !ipv6_tx_plan_onlink(&plan, dst->ip)) return SOCK_ERR_NO_ROUTE; + } + s->remoteEP = *dst; s->connected = true; return SOCK_OK; @@ -431,11 +443,12 @@ int64_t socket_sendto_udp(socket_impl_t sh, const net_l4_endpoint* dst, const vo } ipv4_tx_plan_t plan; - if (!socket_bind_build_ipv4_tx_plan(&s->bindSpec, s->localPort, dip, &plan)) return SOCK_ERR_SYS; + if (!socket_bind_build_ipv4_tx_plan(&s->bindSpec, s->localPort != 0, dip, &plan)) return SOCK_ERR_NO_ROUTE; + if ((s->options.flags & SOCK_OPT_DONTROUTE) && !ipv4_tx_plan_onlink(&plan, dip)) return SOCK_ERR_NO_ROUTE; uint8_t tx_l3 = plan.l3_id; l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(tx_l3); - if (!ipv4_l3_is_ready(v4)) return SOCK_ERR_SYS; + if (!ipv4_l3_is_ready(v4)) return SOCK_ERR_NO_ROUTE; if (!s->localPort) { socket_bind_token_t token = 0; @@ -462,11 +475,12 @@ int64_t socket_sendto_udp(socket_impl_t sh, const net_l4_endpoint* dst, const vo if (d.ver == IP_VER6) { ipv6_tx_plan_t plan; - if (!socket_bind_build_ipv6_tx_plan(&s->bindSpec, s->localPort, d.ip, &plan)) return SOCK_ERR_SYS; + if (!socket_bind_build_ipv6_tx_plan(&s->bindSpec, s->localPort != 0, d.ip, &plan)) return SOCK_ERR_NO_ROUTE; + if ((s->options.flags & SOCK_OPT_DONTROUTE) && !ipv6_tx_plan_onlink(&plan, d.ip)) return SOCK_ERR_NO_ROUTE; uint8_t chosen_l3 = plan.l3_id; l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(chosen_l3); - if (!ipv6_l3_is_ready(v6)) return SOCK_ERR_SYS; + if (!ipv6_l3_is_ready(v6)) return SOCK_ERR_NO_ROUTE; if (!s->localPort) { socket_bind_token_t token = 0; @@ -511,7 +525,7 @@ int64_t socket_recvfrom_udp(socket_impl_t sh, void* buf, uint64_t len, net_l4_en bool ready = s->rx_ring && s->r_head != s->r_tail; irq_restore(irq); if (!ready) { - if (!(s->options.flags & SOCK_OPT_RECV_TIMEOUT) || !s->options.recv_timeout_ms) return SOCK_ERR_WOULDBLOCK; + if (s->options.flags & SOCK_OPT_NONBLOCK) return SOCK_ERR_WOULDBLOCK; uint32_t start_ms = (uint32_t)get_time(); while (1) { @@ -520,13 +534,15 @@ int64_t socket_recvfrom_udp(socket_impl_t sh, void* buf, uint64_t len, net_l4_en irq_restore(irq); if (ready) break; - uint32_t now_ms = (uint32_t)get_time(); - uint32_t elapsed_ms = now_ms - start_ms; - if (elapsed_ms >= s->options.recv_timeout_ms) return SOCK_ERR_WOULDBLOCK; + if ((s->options.flags & SOCK_OPT_RECV_TIMEOUT) && s->options.recv_timeout_ms) { + uint32_t now_ms = get_time(); + uint32_t elapsed_ms = now_ms - start_ms; + if (elapsed_ms >= s->options.recv_timeout_ms) return SOCK_ERR_WOULDBLOCK; - uint32_t wait_ms = s->options.recv_timeout_ms - elapsed_ms; - if (wait_ms > 5) wait_ms = 5; - msleep(wait_ms); + uint32_t wait_ms = s->options.recv_timeout_ms - elapsed_ms; + if (wait_ms > 5) wait_ms = 5; + msleep(wait_ms); + }else msleep(5); } } @@ -569,6 +585,9 @@ int32_t socket_setopt_udp(socket_impl_t sh, int32_t opt, const void* value, uint case SOCK_OPT_FILTER: case SOCK_OPT_SPECIAL: return SOCK_ERR_UNSUP; + case SOCK_OPT_NONBLOCK: + case SOCK_OPT_DONTROUTE: + return socket_common_options_set(&s->options, opt, value, len); case SOCK_OPT_MCAST_JOIN: { if (!value || !len || (len % sizeof(net_l4_endpoint)) != 0) return SOCK_ERR_INVAL; uint32_t count32 = len / sizeof(net_l4_endpoint); @@ -763,6 +782,8 @@ int32_t socket_getopt_udp(socket_impl_t sh, int32_t opt, void* value, uint32_t* case SOCK_GET_OPT_DONTFRAG: case SOCK_GET_OPT_BROADCAST_ALLOWED: case SOCK_GET_OPT_TTL: + case SOCK_GET_OPT_NONBLOCK: + case SOCK_GET_OPT_DONTROUTE: return socket_common_options_get(&s->options, opt, value, len); default: return SOCK_ERR_INVAL; diff --git a/kernel/networking/transport_layer/socket_core.c b/kernel/networking/transport_layer/socket_core.c index aedae88f..e6967d09 100644 --- a/kernel/networking/transport_layer/socket_core.c +++ b/kernel/networking/transport_layer/socket_core.c @@ -231,6 +231,15 @@ int32_t socket_common_options_set(SocketOptions* opts, int32_t opt, const void* if (v) opts->flags |= SOCK_OPT_BROADCAST_ALLOWED; else opts->flags &= ~SOCK_OPT_BROADCAST_ALLOWED; return SOCK_OK; + case SOCK_OPT_NONBLOCK: + if (v) opts->flags |= SOCK_OPT_NONBLOCK; + else opts->flags &= ~SOCK_OPT_NONBLOCK; + return SOCK_OK; + + case SOCK_OPT_DONTROUTE: + if (v) opts->flags |= SOCK_OPT_DONTROUTE; + else opts->flags &= ~SOCK_OPT_DONTROUTE; + return SOCK_OK; case SOCK_OPT_TTL: if (v > 255) return SOCK_ERR_INVAL; opts->ttl = (uint8_t)v; @@ -265,6 +274,12 @@ int32_t socket_common_options_get(const SocketOptions* opts, int32_t opt, void* case SOCK_GET_OPT_BROADCAST_ALLOWED: v = (opts->flags & SOCK_OPT_BROADCAST_ALLOWED) != 0; break; + case SOCK_GET_OPT_NONBLOCK: + v = (opts->flags & SOCK_OPT_NONBLOCK) != 0; + break; + case SOCK_GET_OPT_DONTROUTE: + v = (opts->flags & SOCK_OPT_DONTROUTE) != 0; + break; case SOCK_GET_OPT_TTL: v = opts->ttl; break; diff --git a/kernel/networking/transport_layer/tcp.h b/kernel/networking/transport_layer/tcp.h index db306ac6..1fc4ce5f 100644 --- a/kernel/networking/transport_layer/tcp.h +++ b/kernel/networking/transport_layer/tcp.h @@ -94,6 +94,7 @@ void tcp_flow_apply_socket_options(tcp_data *flow_ctx, const SocketOptions* extr tcp_result_t tcp_flow_send(tcp_data *flow_ctx); tcp_result_t tcp_flow_flush(tcp_data *flow_ctx); tcp_result_t tcp_flow_close(tcp_data *flow_ctx); +tcp_result_t tcp_flow_abort(tcp_data *flow_ctx); bool tcp_flow_is_closed(tcp_data *flow_ctx); tcp_result_t tcp_flow_release_closed(tcp_data *flow_ctx); int64_t tcp_flow_read(tcp_data *flow_ctx, void *buf, uint64_t len); diff --git a/kernel/networking/transport_layer/tcp/tcp_core.c b/kernel/networking/transport_layer/tcp/tcp_core.c index 66907730..0c03a4e1 100644 --- a/kernel/networking/transport_layer/tcp/tcp_core.c +++ b/kernel/networking/transport_layer/tcp/tcp_core.c @@ -336,6 +336,19 @@ void tcp_free_flow(int idx) { tcp_flow_put(f); } +tcp_result_t tcp_flow_abort(tcp_data *flow_ctx) { + tcp_flow_t *flow = tcp_flow_from_ctx(flow_ctx); + if (!flow) return TCP_INVALID; + + int slot = (int)flow->base.slot; + tcp_state_t state = flow->base.state; + if ( !flow->base.retired && state != TCP_STATE_CLOSED && state != TCP_TIME_WAIT && state != TCP_SYN_SENT) tcp_send_reset(flow->base.local.ver, flow->base.local.ip, flow->base.remote.ip, flow->base.local_port, flow->base.remote.port, flow->tx.snd_nxt, 0, false); + + tcp_free_flow(slot); + tcp_flow_put(flow); + return TCP_OK; +} + bool tcp_flow_is_closed(tcp_data *flow_ctx) { tcp_flow_t *flow = tcp_flow_from_ctx(flow_ctx); if (!flow) return true; @@ -618,38 +631,7 @@ bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, tcp_daemon_kick(); - uint64_t start_ms = get_time(); - uint64_t waited = 0; - const uint64_t interval = 50; - uint64_t max_wait = TCP_CONNECT_TIMEOUT_MS; - if (extra && (extra->flags & SOCK_OPT_SEND_TIMEOUT) && extra->send_timeout_ms) max_wait = extra->send_timeout_ms; - - while (waited < max_wait){ - tcp_data ctx_snapshot; - memset(&ctx_snapshot, 0, sizeof(ctx_snapshot)); - - irq_flags_t irq = irq_save_disable(); - tcp_state_t state = flow->base.state; - ctx_snapshot = flow->base.ctx; - irq_restore(irq); - - if (state == TCP_ESTABLISHED || state == TCP_CLOSE_WAIT){ - *flow_ctx = ctx_snapshot; - tcp_flow_put(flow); - return true; - } - - if (state == TCP_STATE_CLOSED){ - tcp_free_flow(idx); - tcp_flow_put(flow); - return false; - } - - msleep(interval); - waited = get_time() - start_ms; - } - - tcp_free_flow(idx); + *flow_ctx = flow->base.ctx; tcp_flow_put(flow); - return false; + return true; } \ No newline at end of file diff --git a/kernel/tools/icmp_probe.c b/kernel/tools/icmp_probe.c index a9beb4af..92012c6c 100644 --- a/kernel/tools/icmp_probe.c +++ b/kernel/tools/icmp_probe.c @@ -72,7 +72,7 @@ uint32_t icmp_probe_collect(const net_l4_endpoint* dst, uint16_t id, uint16_t se SocketOptions opt; memset(&opt, 0, sizeof(opt)); opt.special_kind = SOCKET_SPECIAL_RAW; - opt.flags = SOCK_OPT_SPECIAL | SOCK_OPT_FILTER; + opt.flags = SOCK_OPT_SPECIAL | SOCK_OPT_FILTER | SOCK_OPT_NONBLOCK; opt.raw_filter.count = 5; if (dst->ver == IP_VER4) { opt.raw_filter.rules[0].type = ICMP_ECHO_REPLY; From 67685b0a25d7d5b11671784a6e590efc46ab79b8 Mon Sep 17 00:00:00 2001 From: CodeAnarchist Date: Fri, 31 Jul 2026 18:59:40 +0200 Subject: [PATCH 36/39] [net] add socket reuse, MAXSEG and linger; known issue with tcp REUSEADDR --- .../application_layer/csocket_http_server.c | 2 +- .../networking/transport_layer/csocket_tcp.c | 135 +++++-- .../networking/transport_layer/csocket_udp.c | 27 +- .../networking/transport_layer/socket_bind.c | 357 ++++++++++++++---- .../networking/transport_layer/socket_bind.h | 12 +- .../networking/transport_layer/socket_core.c | 37 +- kernel/networking/transport_layer/tcp.h | 1 + .../networking/transport_layer/tcp/tcp_core.c | 162 ++++---- .../transport_layer/tcp/tcp_internal.h | 9 + .../transport_layer/tcp/tcp_limits.c | 65 ++-- .../transport_layer/tcp/tcp_limits.h | 25 +- .../networking/transport_layer/tcp/tcp_rx.c | 78 ++-- .../networking/transport_layer/tcp/tcp_tx.c | 2 +- .../transport_layer/tcp/tcp_utils.c | 17 + kernel/networking/transport_layer/udp.c | 26 +- 15 files changed, 676 insertions(+), 279 deletions(-) diff --git a/kernel/networking/application_layer/csocket_http_server.c b/kernel/networking/application_layer/csocket_http_server.c index 4cce9ed3..e7ad8f32 100644 --- a/kernel/networking/application_layer/csocket_http_server.c +++ b/kernel/networking/application_layer/csocket_http_server.c @@ -36,7 +36,7 @@ http_server_handle_t http_server_create(const SocketOptions* extra, const HTTPSe srv->tcp_opts.flags &= ~SOCK_OPT_DEBUG; } - srv->tcp_opts.flags |= SOCK_OPT_NONBLOCK; + srv->tcp_opts.flags |= SOCK_OPT_NONBLOCK | SOCK_OPT_REUSEADDR; return srv; } diff --git a/kernel/networking/transport_layer/csocket_tcp.c b/kernel/networking/transport_layer/csocket_tcp.c index 83a0d863..756c5ca9 100644 --- a/kernel/networking/transport_layer/csocket_tcp.c +++ b/kernel/networking/transport_layer/csocket_tcp.c @@ -104,7 +104,7 @@ static int32_t tcp_socket_connection_state(tcp_socket_t* s) { socket_impl_t socket_tcp_create(ksocket_t* owner, const SocketOptions* extra) { if (!owner) return NULL; - uint32_t supported = SOCK_OPT_KEEPALIVE | SOCK_OPT_KEEPALIVE_INTERVAL | SOCK_OPT_SEND_TIMEOUT | SOCK_OPT_RECV_TIMEOUT | SOCK_OPT_BUF_SIZE | SOCK_OPT_DEBUG | SOCK_OPT_DONTFRAG | SOCK_OPT_TTL | SOCK_OPT_SEND_BUF_SIZE | SOCK_OPT_TCP_NO_DELAY | SOCK_OPT_NONBLOCK | SOCK_OPT_DONTROUTE; + uint32_t supported = SOCK_OPT_KEEPALIVE | SOCK_OPT_KEEPALIVE_INTERVAL | SOCK_OPT_SEND_TIMEOUT | SOCK_OPT_RECV_TIMEOUT | SOCK_OPT_BUF_SIZE | SOCK_OPT_DEBUG | SOCK_OPT_DONTFRAG | SOCK_OPT_TTL | SOCK_OPT_SEND_BUF_SIZE | SOCK_OPT_TCP_NO_DELAY | SOCK_OPT_NONBLOCK | SOCK_OPT_DONTROUTE | SOCK_OPT_REUSEADDR | SOCK_OPT_TCP_MAXSEG | SOCK_OPT_LINGER; if (extra) { if (extra->flags & ~supported) return NULL; if ((extra->flags & SOCK_OPT_DEBUG) && extra->debug_level > SOCK_DBG_ALL) return NULL; @@ -124,8 +124,11 @@ socket_impl_t socket_tcp_create(ksocket_t* owner, const SocketOptions* extra) { s->options.buf_size = TCP_DEFAULT_SOCKET_BUF; } - if (!s->options.buf_size) s->options.buf_size = TCP_DEFAULT_SOCKET_BUF; - if (s->options.buf_size > TCP_DEFAULT_SOCKET_BUF) s->options.buf_size = TCP_DEFAULT_SOCKET_BUF; + s->options.buf_size = tcp_clamp_rcvbuf(s->options.buf_size); + if (s->options.tcp_maxseg && (s->options.tcp_maxseg < 256u || s->options.tcp_maxseg > 65535u)) { + release(s); + return NULL; + } return s; } @@ -174,21 +177,54 @@ int32_t socket_setopt_tcp(socket_impl_t sh, int32_t opt, const void* value, uint if (s->flow.flow_generation) tcp_flow_apply_socket_options(&s->flow, &s->options); return SOCK_OK; } + case SOCK_OPT_REUSEADDR: { + if (s->localPort || s->connected || s->listening) return SOCK_ERR_STATE; + return socket_common_options_set(&s->options, opt, value, len); + } case SOCK_OPT_DONTROUTE: { if (s->flow.flow_generation) return SOCK_ERR_STATE; return socket_common_options_set(&s->options, opt, value, len); } case SOCK_OPT_NONBLOCK: return socket_common_options_set(&s->options, opt, value, len); + case SOCK_OPT_TCP_MAXSEG: { + if (!value || len != sizeof(uint32_t)) return SOCK_ERR_INVAL; + uint32_t v = 0; + memcpy(&v, value, sizeof(v)); + if (v && (v < 256u || v > 65535u)) return SOCK_ERR_INVAL; + s->options.tcp_maxseg = v; + if (v) s->options.flags |= SOCK_OPT_TCP_MAXSEG; + else s->options.flags &= ~SOCK_OPT_TCP_MAXSEG; + if (s->flow.flow_generation) tcp_flow_apply_socket_options(&s->flow, &s->options); + return SOCK_OK; + } + case SOCK_OPT_LINGER: { + if (!value || len != sizeof(SocketLinger)) return SOCK_ERR_INVAL; + SocketLinger linger; + memcpy(&linger, value, sizeof(linger)); + s->options.linger = linger; + if (linger.enabled) s->options.flags |= SOCK_OPT_LINGER; + else s->options.flags &= ~SOCK_OPT_LINGER; + return SOCK_OK; + } + case SOCK_OPT_REUSEPORT: case SOCK_OPT_MCAST_JOIN: case SOCK_OPT_MCAST_LEAVE: case SOCK_OPT_BROADCAST_ALLOWED: case SOCK_OPT_FILTER: case SOCK_OPT_SPECIAL: return SOCK_ERR_UNSUP; + case SOCK_OPT_BUF_SIZE: { + if (!value || len != sizeof(uint32_t)) return SOCK_ERR_INVAL; + if (s->flow.flow_generation || s->listening) return SOCK_ERR_STATE; + uint32_t v = 0; + memcpy(&v, value, sizeof(v)); + if (!v || v > TCP_RCV_BUF_MAX) return SOCK_ERR_INVAL; + v = tcp_clamp_rcvbuf(v); + return socket_common_options_set(&s->options, opt, &v, sizeof(v)); + } case SOCK_OPT_RECV_TIMEOUT: case SOCK_OPT_SEND_TIMEOUT: - case SOCK_OPT_BUF_SIZE: case SOCK_OPT_DEBUG: case SOCK_OPT_DONTFRAG: case SOCK_OPT_TTL: { @@ -226,6 +262,8 @@ int32_t socket_getopt_tcp(socket_impl_t sh, int32_t opt, void* value, uint32_t* } return socket_common_get_value(&spec, sizeof(spec), value, len); } + case SOCK_GET_OPT_LINGER: + return socket_common_get_value( &s->options.linger, sizeof(s->options.linger), value, len); default: break; } @@ -299,6 +337,10 @@ int32_t socket_getopt_tcp(socket_impl_t sh, int32_t opt, void* value, uint32_t* case SOCK_GET_OPT_SEND_BUF_SIZE: v = s->options.send_buf_size; break; + case SOCK_GET_OPT_TCP_MAXSEG: + v = s->options.tcp_maxseg; + break; + case SOCK_GET_OPT_REUSEPORT: case SOCK_GET_MCAST_GROUPS: case SOCK_GET_OPT_BROADCAST_ALLOWED: case SOCK_GET_OPT_FILTER: @@ -311,6 +353,7 @@ int32_t socket_getopt_tcp(socket_impl_t sh, int32_t opt, void* value, uint32_t* case SOCK_GET_OPT_TTL: case SOCK_GET_OPT_NONBLOCK: case SOCK_GET_OPT_DONTROUTE: + case SOCK_GET_OPT_REUSEADDR: return socket_common_options_get(&s->options, opt, value, len); default: return SOCK_ERR_INVAL; @@ -360,9 +403,9 @@ int32_t socket_bind_tcp(socket_impl_t sh, const SockBindSpec* spec_in, uint16_t int bind_port = port; socket_bind_token_t token = 0; if (bind_port == 0) { - bind_port = socket_bind_alloc_ephemeral(s->ownerSocket, PROTO_TCP, &spec, &token); + bind_port = socket_bind_alloc_ephemeral(s->ownerSocket, PROTO_TCP, &spec, s->options.flags & SOCK_OPT_REUSEADDR, &token); if (bind_port < 0) return SOCK_ERR_NO_PORT; - } else if (!socket_bind_insert(s->ownerSocket, PROTO_TCP, &spec, port, &token)) return SOCK_ERR_BOUND; + } else if (!socket_bind_insert(s->ownerSocket, PROTO_TCP, &spec, port, s->options.flags & SOCK_OPT_REUSEADDR, true, &token)) return SOCK_ERR_BOUND; s->bindSpec = spec; s->bindToken = token; @@ -375,29 +418,42 @@ int32_t socket_listen_tcp(socket_impl_t sh, int32_t backlog) { if (!s) return SOCK_ERR_INVAL; if (!s->bindToken || s->connected) return SOCK_ERR_STATE; - int cap = backlog > TCP_MAX_BACKLOG ? TCP_MAX_BACKLOG : backlog; + int32_t cap = backlog > TCP_MAX_BACKLOG ? TCP_MAX_BACKLOG : backlog; if (cap < 1) cap = 1; - if (s->pending) { - for (int i = 0; i < s->backlogLen; ++i) { - if (!s->pending[i]) continue; - socket_core_close_socket(s->pending[i]); - socket_core_put(s->pending[i]); - } - release(s->pending); - } - - s->pending = (ksocket_t**)zalloc(sizeof(ksocket_t*) * cap); - if (!s->pending) { - s->backlogCap = 0; - s->backlogLen = 0; - s->listening = false; - return SOCK_ERR_SYS; - } + ksocket_t** pending = (ksocket_t**)zalloc(sizeof(ksocket_t*)*cap); + if (!pending) return SOCK_ERR_SYS; + irq_flags_t irq = irq_save_disable(); + int32_t keep = s->backlogLen < cap ? s->backlogLen : cap; + for (int32_t i = 0; i < keep; ++i) pending[i] = s->pending[i]; + ksocket_t** old_pending = s->pending; + int32_t old_cap = s->backlogCap; + int32_t old_len = s->backlogLen; + bool old_listening = s->listening; + s->pending = pending; s->backlogCap = cap; - s->backlogLen = 0; + s->backlogLen = keep; s->listening = true; + irq_restore(irq); + + if (!socket_bind_tcp_listen(s->bindToken)) { + irq = irq_save_disable(); + s->pending = old_pending; + s->backlogCap = old_cap; + s->backlogLen = old_len; + s->listening = old_listening; + irq_restore(irq); + release(pending); + return SOCK_ERR_BOUND; + } + + for (int32_t i = keep; i < old_len; ++i) { + if (!old_pending[i]) continue; + socket_core_close_socket(old_pending[i]); + socket_core_put(old_pending[i]); + } + if (old_pending) release(old_pending); return SOCK_OK; } @@ -481,7 +537,7 @@ int32_t socket_connect_tcp(socket_impl_t sh, const net_l4_endpoint* dst) { if (s->localPort == 0) { if (!s->ownerSocket) return SOCK_ERR_SYS; socket_bind_token_t token = 0; - int p = socket_bind_alloc_ephemeral_l3(s->ownerSocket, PROTO_TCP, chosen_l3, &token); + int p = socket_bind_alloc_ephemeral_l3(s->ownerSocket, PROTO_TCP, chosen_l3, s->options.flags & SOCK_OPT_REUSEADDR, &token); if (p < 0) return SOCK_ERR_NO_PORT; s->localPort = (uint16_t)p; s->bindToken = token; @@ -672,13 +728,32 @@ int32_t socket_close_tcp(socket_impl_t sh) { netlog_socket_event(&s->options, &ev); int32_t connection_state = tcp_socket_connection_state(s); + bool linger = (s->options.flags & SOCK_OPT_LINGER) && s->options.linger.enabled; if (s->flow.flow_generation) { - if (connection_state > 0) { - if (!tcp_flow_is_closed(&s->flow)) { - tcp_flow_flush(&s->flow); - tcp_flow_close(&s->flow); + if (connection_state == 0 && !s->connected) tcp_flow_abort(&s->flow); + else if (connection_state >= 0) { + if (linger && s->options.linger.timeout_ms == 0) tcp_flow_abort(&s->flow); + else { + bool closed = tcp_flow_is_closed(&s->flow); + if (connection_state > 0 && !closed) { + tcp_flow_flush(&s->flow); + tcp_flow_close(&s->flow); + closed = tcp_flow_is_closed(&s->flow); + } + + if (linger && !closed) { + if (s->options.flags & SOCK_OPT_NONBLOCK) return SOCK_ERR_WOULDBLOCK; + uint32_t start_ms = get_time(); + while (!tcp_flow_is_closed(&s->flow)) { + if (get_time() - start_ms >= s->options.linger.timeout_ms) break; + msleep(5); + } + closed = tcp_flow_is_closed(&s->flow); + } + + if (closed) tcp_flow_release_closed(&s->flow); } - } else if (connection_state == 0) tcp_flow_abort(&s->flow); + } } if (s->pending) { diff --git a/kernel/networking/transport_layer/csocket_udp.c b/kernel/networking/transport_layer/csocket_udp.c index b6e95d11..9af2ea96 100644 --- a/kernel/networking/transport_layer/csocket_udp.c +++ b/kernel/networking/transport_layer/csocket_udp.c @@ -220,7 +220,7 @@ uint32_t socket_udp_input(ksocket_t* socket, ip_version_t ipver, uint8_t l3_id, socket_impl_t udp_socket_create(ksocket_t* owner, const SocketOptions* extra) { if (!owner) return NULL; - uint32_t supported = SOCK_OPT_RECV_TIMEOUT | SOCK_OPT_SEND_TIMEOUT | SOCK_OPT_BUF_SIZE | SOCK_OPT_DEBUG | SOCK_OPT_DONTFRAG | SOCK_OPT_BROADCAST_ALLOWED | SOCK_OPT_TTL | SOCK_OPT_MCAST_JOIN | SOCK_OPT_NONBLOCK | SOCK_OPT_DONTROUTE; + uint32_t supported = SOCK_OPT_RECV_TIMEOUT | SOCK_OPT_SEND_TIMEOUT | SOCK_OPT_BUF_SIZE | SOCK_OPT_DEBUG | SOCK_OPT_DONTFRAG | SOCK_OPT_BROADCAST_ALLOWED | SOCK_OPT_TTL | SOCK_OPT_MCAST_JOIN | SOCK_OPT_NONBLOCK | SOCK_OPT_DONTROUTE | SOCK_OPT_REUSEADDR | SOCK_OPT_REUSEPORT; if (extra) { if (extra->flags & ~supported) return NULL; if ((extra->flags & SOCK_OPT_DEBUG) && extra->debug_level > SOCK_DBG_ALL) return NULL; @@ -293,13 +293,14 @@ int32_t socket_bind_udp(socket_impl_t sh, const SockBindSpec* spec_in, uint16_t int bind_port = port; socket_bind_token_t token = 0; if (bind_port == 0) { - bind_port = socket_bind_alloc_ephemeral(s->ownerSocket, PROTO_UDP, &spec, &token); + bind_port = socket_bind_alloc_ephemeral(s->ownerSocket, PROTO_UDP, &spec, s->options.flags & (SOCK_OPT_REUSEADDR | SOCK_OPT_REUSEPORT), &token); if (bind_port < 0) return SOCK_ERR_NO_PORT; - } else if (!socket_bind_insert(s->ownerSocket, PROTO_UDP, &spec, port, &token)) return SOCK_ERR_BOUND; + } else if (!socket_bind_insert(s->ownerSocket, PROTO_UDP, &spec, port, s->options.flags & (SOCK_OPT_REUSEADDR | SOCK_OPT_REUSEPORT), true, &token)) return SOCK_ERR_BOUND; s->bindSpec = spec; s->bindToken = token; s->localPort = (uint16_t)bind_port; + if (s->connected) socket_bind_udp_set_remote(s->bindToken, &s->remoteEP); int32_t mcast_res = udp_socket_join_mcast_groups(s); if (mcast_res != SOCK_OK) { @@ -342,6 +343,7 @@ int32_t socket_connect_udp(socket_impl_t sh, const net_l4_endpoint* dst) { s->remoteEP = *dst; s->connected = true; + if (s->bindToken) socket_bind_udp_set_remote(s->bindToken, &s->remoteEP); return SOCK_OK; } @@ -421,7 +423,7 @@ int64_t socket_sendto_udp(socket_impl_t sh, const net_l4_endpoint* dst, const vo if (bcast_v4) { if (!s->localPort) { socket_bind_token_t token = 0; - int p = socket_bind_alloc_ephemeral_l3(s->ownerSocket, PROTO_UDP, chosen_l3, &token); + int p = socket_bind_alloc_ephemeral_l3(s->ownerSocket, PROTO_UDP, chosen_l3, s->options.flags & (SOCK_OPT_REUSEADDR | SOCK_OPT_REUSEPORT), &token); if (p < 0) return SOCK_ERR_NO_PORT; s->localPort = (uint16_t)p; s->bindToken = token; @@ -429,6 +431,7 @@ int64_t socket_sendto_udp(socket_impl_t sh, const net_l4_endpoint* dst, const vo s->bindSpec.kind = BIND_L3; s->bindSpec.ver = IP_VER4; s->bindSpec.l3_id = chosen_l3; + if (s->connected) socket_bind_udp_set_remote(s->bindToken, &s->remoteEP); } net_l4_endpoint src; @@ -452,7 +455,7 @@ int64_t socket_sendto_udp(socket_impl_t sh, const net_l4_endpoint* dst, const vo if (!s->localPort) { socket_bind_token_t token = 0; - int p = socket_bind_alloc_ephemeral_l3(s->ownerSocket, PROTO_UDP, tx_l3, &token); + int p = socket_bind_alloc_ephemeral_l3(s->ownerSocket, PROTO_UDP, tx_l3, s->options.flags & (SOCK_OPT_REUSEADDR | SOCK_OPT_REUSEPORT), &token); if (p < 0) return SOCK_ERR_NO_PORT; s->localPort = (uint16_t)p; s->bindToken = token; @@ -460,6 +463,7 @@ int64_t socket_sendto_udp(socket_impl_t sh, const net_l4_endpoint* dst, const vo s->bindSpec.kind = BIND_L3; s->bindSpec.ver = IP_VER4; s->bindSpec.l3_id = tx_l3; + if (s->connected) socket_bind_udp_set_remote(s->bindToken, &s->remoteEP); } net_l4_endpoint src; @@ -484,7 +488,7 @@ int64_t socket_sendto_udp(socket_impl_t sh, const net_l4_endpoint* dst, const vo if (!s->localPort) { socket_bind_token_t token = 0; - int p = socket_bind_alloc_ephemeral_l3(s->ownerSocket, PROTO_UDP, chosen_l3, &token); + int p = socket_bind_alloc_ephemeral_l3(s->ownerSocket, PROTO_UDP, chosen_l3, s->options.flags & (SOCK_OPT_REUSEADDR | SOCK_OPT_REUSEPORT), &token); if (p < 0) return SOCK_ERR_NO_PORT; s->localPort = (uint16_t)p; s->bindToken = token; @@ -492,6 +496,7 @@ int64_t socket_sendto_udp(socket_impl_t sh, const net_l4_endpoint* dst, const vo s->bindSpec.kind = BIND_L3; s->bindSpec.ver = IP_VER6; s->bindSpec.l3_id = chosen_l3; + if (s->connected) socket_bind_udp_set_remote(s->bindToken, &s->remoteEP); } net_l4_endpoint src; @@ -582,9 +587,15 @@ int32_t socket_setopt_udp(socket_impl_t sh, int32_t opt, const void* value, uint case SOCK_OPT_KEEPALIVE_INTERVAL: case SOCK_OPT_TCP_NO_DELAY: case SOCK_OPT_SEND_BUF_SIZE: + case SOCK_OPT_TCP_MAXSEG: + case SOCK_OPT_LINGER: case SOCK_OPT_FILTER: case SOCK_OPT_SPECIAL: return SOCK_ERR_UNSUP; + case SOCK_OPT_REUSEADDR: + case SOCK_OPT_REUSEPORT: + if (s->localPort) return SOCK_ERR_STATE; + return socket_common_options_set(&s->options, opt, value, len); case SOCK_OPT_NONBLOCK: case SOCK_OPT_DONTROUTE: return socket_common_options_set(&s->options, opt, value, len); @@ -765,6 +776,8 @@ int32_t socket_getopt_udp(socket_impl_t sh, int32_t opt, void* value, uint32_t* case SOCK_GET_OPT_KEEPALIVE_INTERVAL: case SOCK_GET_OPT_TCP_NO_DELAY: case SOCK_GET_OPT_SEND_BUF_SIZE: + case SOCK_GET_OPT_TCP_MAXSEG: + case SOCK_GET_OPT_LINGER: case SOCK_GET_OPT_FILTER: case SOCK_GET_TCP_STATE: case SOCK_GET_TCP_MSS: @@ -784,6 +797,8 @@ int32_t socket_getopt_udp(socket_impl_t sh, int32_t opt, void* value, uint32_t* case SOCK_GET_OPT_TTL: case SOCK_GET_OPT_NONBLOCK: case SOCK_GET_OPT_DONTROUTE: + case SOCK_GET_OPT_REUSEADDR: + case SOCK_GET_OPT_REUSEPORT: return socket_common_options_get(&s->options, opt, value, len); default: return SOCK_ERR_INVAL; diff --git a/kernel/networking/transport_layer/socket_bind.c b/kernel/networking/transport_layer/socket_bind.c index 7d8ff720..4b4f0723 100644 --- a/kernel/networking/transport_layer/socket_bind.c +++ b/kernel/networking/transport_layer/socket_bind.c @@ -1,4 +1,5 @@ #include "socket_bind.h" +#include "networking/transport_layer/tcp.h" #include "exceptions/irq.h" #include "networking/interface_manager.h" #include "networking/internet_layer/ipv4_utils.h" @@ -15,11 +16,15 @@ typedef struct socket_bind_entry { bool used; + bool listening; protocol_t protocol; uint16_t port; uint16_t next; - uint32_t generation; + uint16_t generation; SockBindSpec spec; + uint32_t options; + uint16_t owner_pid; + net_l4_endpoint remote; ksocket_t* socket; } socket_bind_entry_t; @@ -59,7 +64,26 @@ bool socket_bind_normalize_spec(SockBindSpec* spec) { return true; } - if (spec->kind != BIND_IP) return true; + if (spec->kind == BIND_L3) { + if (!spec->l3_id) return false; + if (!spec->ver) spec->ver = l3_is_v6_from_id(spec->l3_id) ? IP_VER6 : IP_VER4; + return spec->ver == IP_VER4 || spec->ver == IP_VER6; + } + + if (spec->kind == BIND_L2) return !spec->ver || spec->ver == IP_VER4 || spec->ver == IP_VER6; + if (spec->kind == BIND_ANY) { + spec->ver = 0; + return true; + } + if (spec->kind == BIND_ANY4) { + spec->ver = IP_VER4; + return true; + } + if (spec->kind == BIND_ANY6) { + spec->ver = IP_VER6; + return true; + } + if (spec->kind != BIND_IP) return false; if (spec->ver == IP_VER4) { uint32_t ip = 0; @@ -156,7 +180,27 @@ uint32_t socket_bind_select_l3(const SockBindSpec* spec, ip_version_t ver, uint8 return count; } -bool socket_bind_insert(ksocket_t* socket, protocol_t protocol, const SockBindSpec* spec, uint16_t port, socket_bind_token_t* out_token) { +static bool socket_bind_specs_overlap(const SockBindSpec* a, const SockBindSpec* b) { + if (!a || !b) return false; + + ip_version_t af = a->kind == BIND_ANY4 ? IP_VER4 : a->kind == BIND_ANY6 ? IP_VER6 : a->ver; + ip_version_t bf = b->kind == BIND_ANY4 ? IP_VER4 : b->kind == BIND_ANY6 ? IP_VER6 : b->ver; + if (af && bf && af != bf) return false; + if (a->kind == BIND_ANY || b->kind == BIND_ANY) return true; + if (a->kind == BIND_ANY4 || a->kind == BIND_ANY6 || b->kind == BIND_ANY4 || b->kind == BIND_ANY6) return true; + + if (a->kind == b->kind) { + if (a->kind == BIND_L3) return a->l3_id == b->l3_id; + if (a->kind == BIND_L2) return a->ifindex == b->ifindex; + if (a->kind == BIND_IP) return af == IP_VER4 ? memcmp(a->ip, b->ip, 4) == 0 : memcmp(a->ip, b->ip, 16) == 0; + return true; + } + + if (a->kind != BIND_L2 && b->kind != BIND_L2 && a->l3_id && b->l3_id) return a->l3_id == b->l3_id; + return a->ifindex && b->ifindex && a->ifindex == b->ifindex; +} + +bool socket_bind_insert(ksocket_t* socket, protocol_t protocol, const SockBindSpec* spec, uint16_t port, uint32_t options, bool allow_reuse, socket_bind_token_t* out_token) { if (out_token) *out_token = 0; if (!socket || !spec || !port) return false; if (protocol != PROTO_TCP && protocol != PROTO_UDP) return false; @@ -165,56 +209,56 @@ bool socket_bind_insert(ksocket_t* socket, protocol_t protocol, const SockBindSp if (!socket_bind_normalize_spec(&normal)) return false; if (normal.kind == BIND_L3) { - ip_version_t ver = normal.ver ? normal.ver : (l3_is_v6_from_id(normal.l3_id) ? IP_VER6 : IP_VER4); - if (!socket_bind_l3_valid(ver, normal.l3_id)) return false; + if (!socket_bind_l3_valid(normal.ver, normal.l3_id)) return false; + normal.ifindex = l3_ifindex_from_id(normal.l3_id); } else if (normal.kind == BIND_L2) { if (!l2_interface_find_by_index(normal.ifindex)) return false; } else if (normal.kind == BIND_IP) { if (normal.ver == IP_VER4) { uint32_t ip = 0; memcpy(&ip, normal.ip, 4); - if (!ipv4_is_unspecified(ip) && !l3_ipv4_find_by_ip(ip)) return false; - } else if (normal.ver == IP_VER6 && !ipv6_is_unspecified(normal.ip)) { - if (!l3_ipv6_find_by_ip(normal.ip)) return false; - } + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_ip(ip); + if (!v4 || !v4->l2) return false; + normal.l3_id = v4->l3_id; + normal.ifindex = v4->l2->ifindex; + } else if (normal.ver == IP_VER6) { + l3_ipv6_interface_t* v6 = l3_ipv6_find_by_ip(normal.ip); + if (!v6 || !v6->l2) return false; + normal.l3_id = v6->l3_id; + normal.ifindex = v6->l2->ifindex; + } else return false; } socket_bind_map_init(); if (!bind_map) return false; + uint16_t owner_pid = protocol == PROTO_UDP ? socket_core_pid(socket) : 0; + uint32_t reuse = options & SOCK_OPT_REUSEADDR; + if (protocol == PROTO_UDP) reuse |= options & SOCK_OPT_REUSEPORT; irq_flags_t irq = irq_save_disable(); - uint16_t head = socket_bind_head(protocol, port); - for (uint16_t idx = head; idx != SOCKET_BIND_LIST_END; idx = bind_entries[idx].next) { - socket_bind_entry_t* e = &bind_entries[idx]; - if (!e->used || e->protocol != protocol || e->port != port) continue; - - ip_version_t a_family = 0; - ip_version_t b_family = 0; - if (normal.kind == BIND_ANY4) a_family = IP_VER4; - else if (normal.kind == BIND_ANY6) a_family = IP_VER6; - else a_family = normal.ver; - if (e->spec.kind == BIND_ANY4) b_family = IP_VER4; - else if (e->spec.kind == BIND_ANY6) b_family = IP_VER6; - else b_family = e->spec.ver; - if (a_family && b_family && a_family != b_family) continue; - - bool overlap = true; - if (normal.kind == BIND_IP && e->spec.kind == BIND_IP && a_family && b_family) { - if (a_family == IP_VER4) { - uint32_t av = 0; - uint32_t bv = 0; - memcpy(&av, normal.ip, 4); - memcpy(&bv, e->spec.ip, 4); - overlap = ipv4_is_unspecified(av) || ipv4_is_unspecified(bv) || av == bv; - } else if (a_family == IP_VER6) overlap = ipv6_is_unspecified(normal.ip) || ipv6_is_unspecified(e->spec.ip) || ipv6_cmp(normal.ip, e->spec.ip) == 0; - } else if (normal.kind == BIND_L3 && e->spec.kind == BIND_L3) overlap = normal.l3_id == e->spec.l3_id; - else if (normal.kind == BIND_L2 && e->spec.kind == BIND_L2) overlap = normal.ifindex == e->spec.ifindex; - if (!overlap) continue; + if (protocol == PROTO_TCP && tcp_bind_conflicts(&normal, port, allow_reuse && reuse != 0)) { irq_restore(irq); return false; } + uint16_t head = socket_bind_head(protocol, port); + for (uint16_t idx = head; idx != SOCKET_BIND_LIST_END; idx = bind_entries[idx].next) { + socket_bind_entry_t* other = &bind_entries[idx]; + if (!other->used || other->protocol != protocol || other->port != port) continue; + if (!socket_bind_specs_overlap(&normal, &other->spec)) continue; + + bool allowed = allow_reuse && (reuse & SOCK_OPT_REUSEADDR) && (other->options & SOCK_OPT_REUSEADDR); + if (protocol == PROTO_UDP && !allowed) { + allowed = allow_reuse && owner_pid == other->owner_pid && (reuse & SOCK_OPT_REUSEPORT) && (other->options & SOCK_OPT_REUSEPORT); + } + if (protocol == PROTO_TCP && other->listening) allowed = false; + if (!allowed) { + irq_restore(irq); + return false; + } + } + uint16_t idx = SOCKET_BIND_LIST_END; for (uint32_t i = 0; i < SOCKET_BIND_MAX; ++i) { uint16_t cand = (uint16_t)((bind_next_alloc + i) % SOCKET_BIND_MAX); @@ -229,7 +273,7 @@ bool socket_bind_insert(ksocket_t* socket, protocol_t protocol, const SockBindSp } socket_bind_entry_t* e = &bind_entries[idx]; - uint32_t generation = (e->generation + 1) & 0xFFFF; + uint16_t generation = (uint16_t)(e->generation + 1); if (!generation) generation = 1; memset(e, 0, sizeof(*e)); e->used = true; @@ -237,6 +281,8 @@ bool socket_bind_insert(ksocket_t* socket, protocol_t protocol, const SockBindSp e->port = port; e->generation = generation; e->spec = normal; + e->options = reuse; + e->owner_pid = owner_pid; e->socket = socket; e->next = head; if (!socket_bind_set_head(protocol, port, idx)) { @@ -248,8 +294,44 @@ bool socket_bind_insert(ksocket_t* socket, protocol_t protocol, const SockBindSp } socket_core_ref(socket); - if (out_token) *out_token = (generation << 16) | (uint32_t)(idx + 1); + if (out_token) *out_token = ((uint32_t)generation << 16) | (uint32_t)(idx + 1); + + irq_restore(irq); + return true; +} + +bool socket_bind_tcp_listen(socket_bind_token_t token) { + if (!token) return false; + + uint32_t idxplus = (uint16_t)token; + uint32_t generation = token >> 16; + if (!idxplus || idxplus > SOCKET_BIND_MAX || !generation) return false; + + socket_bind_map_init(); + if (!bind_map) return false; + + uint16_t idx = (uint16_t)(idxplus - 1); + irq_flags_t irq = irq_save_disable(); + socket_bind_entry_t* entry = &bind_entries[idx]; + if (!entry->used || entry->generation != generation || entry->protocol != PROTO_TCP || !entry->socket) { + irq_restore(irq); + return false; + } + if (entry->listening) { + irq_restore(irq); + return true; + } + + uint16_t head = socket_bind_head(PROTO_TCP, entry->port); + for (uint16_t other_idx = head; other_idx != SOCKET_BIND_LIST_END; other_idx = bind_entries[other_idx].next) { + socket_bind_entry_t* other = &bind_entries[other_idx]; + if (other_idx == idx || !other->used || !other->listening || other->protocol != PROTO_TCP || other->port != entry->port) continue; + if (!socket_bind_specs_overlap(&entry->spec, &other->spec)) continue; + irq_restore(irq); + return false; + } + entry->listening = true; irq_restore(irq); return true; } @@ -295,7 +377,24 @@ void socket_bind_remove(socket_bind_token_t token) { if (drop) socket_core_put(drop); } -int socket_bind_alloc_ephemeral(ksocket_t* socket, protocol_t protocol, const SockBindSpec* spec, socket_bind_token_t* out_token) { +void socket_bind_udp_set_remote(socket_bind_token_t token, const net_l4_endpoint* remote) { + if (!token) return; + + uint32_t idxplus = (uint16_t)token; + uint32_t generation = token >> 16; + if (!idxplus || idxplus > SOCKET_BIND_MAX || !generation) return; + + uint16_t idx = (uint16_t)(idxplus-1); + irq_flags_t irq = irq_save_disable(); + socket_bind_entry_t* e = &bind_entries[idx]; + if (e->used && e->generation == generation && e->protocol == PROTO_UDP) { + memset(&e->remote, 0, sizeof(e->remote)); + if (remote && remote->port && (remote->ver == IP_VER4 || remote->ver == IP_VER6)) e->remote = *remote; + } + irq_restore(irq); +} + +int socket_bind_alloc_ephemeral(ksocket_t* socket, protocol_t protocol, const SockBindSpec* spec, uint32_t options, socket_bind_token_t* out_token) { if (out_token) *out_token = 0; if (!socket || !spec) return -1; if (protocol != PROTO_TCP && protocol != PROTO_UDP) return -1; @@ -311,13 +410,13 @@ int socket_bind_alloc_ephemeral(ksocket_t* socket, protocol_t protocol, const So for (uint32_t i = 0; i < range; ++i) { uint16_t port = (uint16_t)(minp + ((first - minp + i) % range)); - if (socket_bind_insert(socket, protocol, spec, port, out_token)) return port; + if (socket_bind_insert(socket, protocol, spec, port, options, false, out_token)) return port; } return -1; } -int socket_bind_alloc_ephemeral_l3(ksocket_t* socket, protocol_t protocol, uint8_t l3_id, socket_bind_token_t* out_token) { +int socket_bind_alloc_ephemeral_l3(ksocket_t* socket, protocol_t protocol, uint8_t l3_id, uint32_t options, socket_bind_token_t* out_token) { if (!socket || !l3_id) return -1; SockBindSpec spec; @@ -326,49 +425,159 @@ int socket_bind_alloc_ephemeral_l3(ksocket_t* socket, protocol_t protocol, uint8 spec.ver = l3_is_v6_from_id(l3_id) ? IP_VER6 : IP_VER4; spec.l3_id = l3_id; - return socket_bind_alloc_ephemeral(socket, protocol, &spec, out_token); + return socket_bind_alloc_ephemeral(socket, protocol, &spec, options, out_token); } -uint32_t socket_bind_collect(protocol_t protocol, ip_version_t ipver, uint8_t l3_id, uint8_t ifindex, const void* dst_ip_addr, uint16_t dst_port, ksocket_t** out, uint32_t out_cap) { - if (!out || !out_cap || !dst_ip_addr) return 0; - if (protocol != PROTO_TCP && protocol != PROTO_UDP) return 0; +ksocket_t* socket_bind_lookup(protocol_t protocol, ip_version_t ipver, uint8_t l3_id, uint8_t ifindex, const void* src_ip_addr, uint16_t src_port, const void* dst_ip_addr, uint16_t dst_port) { + if (!dst_ip_addr || (protocol != PROTO_TCP && protocol != PROTO_UDP)) return NULL; + if (ipver != IP_VER4 && ipver != IP_VER6) return NULL; - uint32_t count = 0; socket_bind_map_init(); - if (!bind_map) return 0; + if (!bind_map) return NULL; + //are all these fields needed? + net_l4_endpoint src = {0}; + net_l4_endpoint dst = {0}; + src.ver = ipver; + src.port = src_port; + dst.ver = ipver; + dst.port = dst_port; + uint32_t ip_len = ipver == IP_VER6 ? 16 : 4; + if (src_ip_addr) memcpy(src.ip, src_ip_addr, ip_len); + memcpy(dst.ip, dst_ip_addr, ip_len); irq_flags_t irq = irq_save_disable(); //TODO lock - for (uint16_t idx = socket_bind_head(protocol, dst_port); idx != SOCKET_BIND_LIST_END && count < out_cap; idx = bind_entries[idx].next) { + uint16_t head = socket_bind_head(protocol, dst_port); + int32_t best_score = 0; + uint32_t best_count = 0; + bool reuseport_group = false; + uint16_t reuseport_owner = 0; + uint64_t reuseport_hash = 0; + socket_bind_entry_t* first = NULL; + socket_bind_entry_t* reuseport_selected = NULL; + + for (uint16_t idx = head; idx != SOCKET_BIND_LIST_END; idx = bind_entries[idx].next) { socket_bind_entry_t* e = &bind_entries[idx]; - if (!e->used || e->protocol != protocol || e->port != dst_port || !e->socket) continue; - - ip_version_t family = 0; - if (e->spec.kind == BIND_ANY4) family = IP_VER4; - else if (e->spec.kind == BIND_ANY6) family = IP_VER6; - else family = e->spec.ver; - if (family && family != ipver) continue; - - if (e->spec.kind == BIND_L3 && e->spec.l3_id != l3_id) continue; - if (e->spec.kind == BIND_L2 && e->spec.ifindex != ifindex) continue; - - if (e->spec.kind == BIND_IP) { - if (ipver == IP_VER4) { - uint32_t want = 0; - uint32_t got = 0; - memcpy(&want, e->spec.ip, 4); - memcpy(&got, dst_ip_addr, 4); - if (!ipv4_is_unspecified(want) && want != got) continue; - } else if (ipver == IP_VER6) { - if (!ipv6_is_unspecified(e->spec.ip) && ipv6_cmp(e->spec.ip, dst_ip_addr) != 0) continue; - } else continue; - } else if (e->spec.kind != BIND_ANY && e->spec.kind != BIND_ANY4 && e->spec.kind != BIND_ANY6 && e->spec.kind != BIND_L3 && e->spec.kind != BIND_L2) continue; - - if (socket_core_is_closing(e->socket)) continue; + if (!e->used || e->protocol != protocol || e->port != dst_port || !e->socket || socket_core_is_closing(e->socket)) continue; + if (protocol == PROTO_TCP && !e->listening) continue; + if ((e->spec.kind == BIND_L3 || e->spec.kind == BIND_L2) && e->spec.ver && e->spec.ver != ipver) continue; + + int32_t score = 0; + switch (e->spec.kind) { + case BIND_IP: + if (e->spec.ver == ipver && memcmp(e->spec.ip, dst_ip_addr, ip_len) == 0) score = 5; + break; + case BIND_L3: + if (e->spec.l3_id == l3_id) score = 4; + break; + case BIND_L2: + if (e->spec.ifindex == ifindex) score = 3; + break; + case BIND_ANY4: + if (ipver == IP_VER4) score = 2; + break; + case BIND_ANY6: + if (ipver == IP_VER6) score = 2; + break; + case BIND_ANY: + score = 1; + break; + default: + break; + } + if (!score) continue; + + if (protocol == PROTO_UDP && e->remote.port) { + if (!src_ip_addr || e->remote.ver != ipver || e->remote.port != src_port) continue; + if (memcmp(e->remote.ip, src_ip_addr, ip_len) != 0) continue; + score += 8; + } + if (score < best_score) continue; + + bool reuseport = protocol == PROTO_UDP && (e->options & SOCK_OPT_REUSEPORT); + if (score > best_score) { + best_score = score; + best_count = 1; + reuseport_group = reuseport; + reuseport_owner = e->owner_pid; + reuseport_hash = 0; + first = e; + reuseport_selected = NULL; + } else { + best_count++; + if (!reuseport || e->owner_pid != reuseport_owner) reuseport_group = false; + } + + if (reuseport) { + struct { + net_l4_endpoint src; + net_l4_endpoint dst; + socket_handle_t handle; + } key; + memset(&key, 0, sizeof(key)); + key.src = src; + key.dst = dst; + key.handle = socket_core_export_handle(e->socket); + uint64_t hash = hash_map_fnv1a64(&key, sizeof(key)); + if (!reuseport_selected || hash > reuseport_hash) { + reuseport_selected = e; + reuseport_hash = hash; + } + } + } + + socket_bind_entry_t* selected = best_count > 1 && reuseport_group ? reuseport_selected : first; + ksocket_t* socket = selected ? selected->socket : NULL; + if (socket) socket_core_ref(socket); + irq_restore(irq); + return socket; +} + +ksocket_t* socket_bind_udp_next_fanout(ip_version_t ipver, uint8_t l3_id, uint8_t ifindex, const void* dst_ip_addr, uint16_t dst_port, uint32_t* cursor) { + if (!cursor || !dst_ip_addr || (ipver != IP_VER4 && ipver != IP_VER6)) return NULL; + + uint32_t start = *cursor; + if (start >= SOCKET_BIND_MAX) return NULL; + + irq_flags_t irq = irq_save_disable(); + for (uint32_t i = start; i < SOCKET_BIND_MAX; ++i) { + socket_bind_entry_t* e = &bind_entries[i]; + if (!e->used || e->protocol != PROTO_UDP || e->port != dst_port || !e->socket || socket_core_is_closing(e->socket)) continue; + if ((e->spec.kind == BIND_L3 || e->spec.kind == BIND_L2) && e->spec.ver && e->spec.ver != ipver) continue; + + bool match = false; + switch (e->spec.kind) { + case BIND_IP: + match = e->spec.ver == ipver && memcmp(e->spec.ip, dst_ip_addr, ipver == IP_VER6 ? 16 : 4) == 0; + break; + case BIND_L3: + match = e->spec.l3_id == l3_id; + break; + case BIND_L2: + match = e->spec.ifindex == ifindex; + break; + case BIND_ANY4: + match = ipver == IP_VER4; + break; + case BIND_ANY6: + match = ipver == IP_VER6; + break; + case BIND_ANY: + match = true; + break; + default: + break; + } + if (!match) continue; + + *cursor = i++; socket_core_ref(e->socket); - out[count++] = e->socket; + irq_restore(irq); + return e->socket; } + + *cursor = SOCKET_BIND_MAX; irq_restore(irq); - return count; + return NULL; } bool socket_bind_build_ipv4_tx_plan(const SockBindSpec* spec, bool use_spec, uint32_t dst, ipv4_tx_plan_t* out) { diff --git a/kernel/networking/transport_layer/socket_bind.h b/kernel/networking/transport_layer/socket_bind.h index ffe52219..98c04110 100644 --- a/kernel/networking/transport_layer/socket_bind.h +++ b/kernel/networking/transport_layer/socket_bind.h @@ -12,16 +12,18 @@ extern "C" { #endif #define SOCKET_BIND_MAX 1024 -#define SOCKET_BIND_COLLECT_MAX 32 typedef uint32_t socket_bind_token_t; bool socket_bind_normalize_spec(SockBindSpec* spec); -bool socket_bind_insert(ksocket_t* socket, protocol_t protocol, const SockBindSpec* spec, uint16_t port, socket_bind_token_t* out_token); +bool socket_bind_insert(ksocket_t* socket, protocol_t protocol, const SockBindSpec* spec, uint16_t port, uint32_t options, bool allow_reuse, socket_bind_token_t* out_token); +bool socket_bind_tcp_listen(socket_bind_token_t token); void socket_bind_remove(socket_bind_token_t token); -int socket_bind_alloc_ephemeral(ksocket_t* socket, protocol_t protocol, const SockBindSpec* spec, socket_bind_token_t* out_token); -int socket_bind_alloc_ephemeral_l3(ksocket_t* socket, protocol_t protocol, uint8_t l3_id, socket_bind_token_t* out_token); +void socket_bind_udp_set_remote(socket_bind_token_t token, const net_l4_endpoint* remote); +int socket_bind_alloc_ephemeral(ksocket_t* socket, protocol_t protocol, const SockBindSpec* spec, uint32_t options, socket_bind_token_t* out_token); +int socket_bind_alloc_ephemeral_l3(ksocket_t* socket, protocol_t protocol, uint8_t l3_id, uint32_t options, socket_bind_token_t* out_token); -uint32_t socket_bind_collect(protocol_t protocol, ip_version_t ipver, uint8_t l3_id, uint8_t ifindex, const void* dst_ip_addr, uint16_t dst_port, ksocket_t** out, uint32_t out_cap); +ksocket_t* socket_bind_lookup(protocol_t protocol, ip_version_t ipver, uint8_t l3_id, uint8_t ifindex, const void* src_ip_addr, uint16_t src_port, const void* dst_ip_addr, uint16_t dst_port); +ksocket_t* socket_bind_udp_next_fanout(ip_version_t ipver, uint8_t l3_id, uint8_t ifindex, const void* dst_ip_addr, uint16_t dst_port, uint32_t* cursor); uint32_t socket_bind_select_l3(const SockBindSpec* spec, ip_version_t ver, uint8_t* out, uint32_t cap); bool socket_bind_build_ipv4_tx_plan(const SockBindSpec* spec, bool use_spec, uint32_t dst, ipv4_tx_plan_t* out); diff --git a/kernel/networking/transport_layer/socket_core.c b/kernel/networking/transport_layer/socket_core.c index e6967d09..179438be 100644 --- a/kernel/networking/transport_layer/socket_core.c +++ b/kernel/networking/transport_layer/socket_core.c @@ -134,20 +134,29 @@ void socket_core_put(ksocket_t* socket) { int32_t socket_core_close_socket(ksocket_t* socket) { if (!socket) return SOCK_ERR_INVAL; - bool first_close = false; irq_flags_t irq = irq_save_disable(); - if (!socket->closing) { - socket->closing = true; - socket->visible = false; - if (socket->id < SOCKET_MAX_OPEN && sockets[socket->id] == socket) sockets[socket->id] = NULL; - first_close = true; + if (socket->closing) { + irq_restore(irq); + return SOCK_OK; } + socket->closing = true; irq_restore(irq); - if (!first_close) return SOCK_OK; int32_t ret = SOCK_OK; if (socket->close && socket->impl) ret = socket->close(socket->impl); + if (ret == SOCK_ERR_WOULDBLOCK) { + irq = irq_save_disable(); + socket->closing = false; + irq_restore(irq); + return ret; + } + + irq = irq_save_disable(); + socket->visible = false; + if (socket->id < SOCKET_MAX_OPEN && sockets[socket->id] == socket) sockets[socket->id] = NULL; + irq_restore(irq); + socket_core_put(socket); return ret; } @@ -240,6 +249,14 @@ int32_t socket_common_options_set(SocketOptions* opts, int32_t opt, const void* if (v) opts->flags |= SOCK_OPT_DONTROUTE; else opts->flags &= ~SOCK_OPT_DONTROUTE; return SOCK_OK; + case SOCK_OPT_REUSEADDR: + if (v) opts->flags |= SOCK_OPT_REUSEADDR; + else opts->flags &= ~SOCK_OPT_REUSEADDR; + return SOCK_OK; + case SOCK_OPT_REUSEPORT: + if (v) opts->flags |= SOCK_OPT_REUSEPORT; + else opts->flags &= ~SOCK_OPT_REUSEPORT; + return SOCK_OK; case SOCK_OPT_TTL: if (v > 255) return SOCK_ERR_INVAL; opts->ttl = (uint8_t)v; @@ -280,6 +297,12 @@ int32_t socket_common_options_get(const SocketOptions* opts, int32_t opt, void* case SOCK_GET_OPT_DONTROUTE: v = (opts->flags & SOCK_OPT_DONTROUTE) != 0; break; + case SOCK_GET_OPT_REUSEADDR: + v = (opts->flags & SOCK_OPT_REUSEADDR) != 0; + break; + case SOCK_GET_OPT_REUSEPORT: + v = (opts->flags & SOCK_OPT_REUSEPORT) != 0; + break; case SOCK_GET_OPT_TTL: v = opts->ttl; break; diff --git a/kernel/networking/transport_layer/tcp.h b/kernel/networking/transport_layer/tcp.h index 1fc4ce5f..7a435529 100644 --- a/kernel/networking/transport_layer/tcp.h +++ b/kernel/networking/transport_layer/tcp.h @@ -87,6 +87,7 @@ typedef enum { #define TCP_MAX_PERSIST_PROBES 8 bool tcp_get_ctx(uint16_t local_port, ip_version_t ver, const void *local_ip, const void *remote_ip, uint16_t remote_port, tcp_data *out_ctx); +bool tcp_bind_conflicts(const SockBindSpec* spec, uint16_t port, bool reuseaddr); bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, tcp_data *flow_ctx, const SocketOptions* extra); void tcp_flow_apply_socket_options(tcp_data *flow_ctx, const SocketOptions* extra); diff --git a/kernel/networking/transport_layer/tcp/tcp_core.c b/kernel/networking/transport_layer/tcp/tcp_core.c index 0c03a4e1..3793092e 100644 --- a/kernel/networking/transport_layer/tcp/tcp_core.c +++ b/kernel/networking/transport_layer/tcp/tcp_core.c @@ -9,6 +9,7 @@ #include "random/random.h" #include "syscalls/syscalls.h" #include "networking/transport_layer/trans_utils.h" +#include "networking/transport_layer/socket_core.h" #include "exceptions/irq.h" tcp_flow_t *tcp_flows[MAX_TCP_FLOWS]; @@ -75,34 +76,13 @@ tcp_flow_t *tcp_flow_acquire_match(uint16_t local_port, ip_version_t ver, const if (f->base.retired || f->base.state == TCP_STATE_CLOSED) continue; if (f->base.local_port != local_port) continue; - uint8_t match = 0; - if (f->base.state == TCP_LISTEN){ - if (remote_ip || remote_port) continue; - if (f->base.local.ver && f->base.local.ver != ver) continue; - if (!local_ip) match = 1; - else { - size_t l = (size_t)(ver == IP_VER6 ? 16 : 4); - uint8_t unspec = 1; - for (size_t k = 0; k < l; ++k){ - if (f->base.local.ip[k]){ - unspec = 0; - break; - } - } - match = unspec || memcmp(f->base.local.ip, local_ip, l) == 0; - } - } else { - if (!remote_ip || !local_ip) continue; - if (f->base.remote.ver != ver) continue; - if (f->base.remote.port != remote_port) continue; - - size_t l = (size_t)(ver == IP_VER6 ? 16 : 4); - if (memcmp(f->base.local.ip, local_ip, l) != 0) continue; - if (memcmp(f->base.remote.ip, remote_ip, l) != 0) continue; - match = 1; - } + if (!remote_ip || !local_ip) continue; + if (f->base.local.ver != ver || f->base.remote.ver != ver) continue; + if (f->base.remote.port != remote_port) continue; - if (!match) continue; + size_t l = (size_t)(ver == IP_VER6 ? 16 : 4); + if (memcmp(f->base.local.ip, local_ip, l) != 0) continue; + if (memcmp(f->base.remote.ip, remote_ip, l) != 0) continue; if (!f->base.refs || f->base.refs == UINT16_MAX) break; f->base.refs++; @@ -116,6 +96,30 @@ tcp_flow_t *tcp_flow_acquire_match(uint16_t local_port, ip_version_t ver, const return NULL; } +bool tcp_bind_conflicts(const SockBindSpec* spec, uint16_t port, bool reuseaddr) { + if (!spec || !port) return true; + + bool conflict = false; + irq_flags_t irq = irq_save_disable(); + uint16_t start = tcp_active_port_lower_bound(port); + for (uint16_t n = start; n < tcp_active_count; n++) { + uint16_t slot = tcp_active_flows[n]; + tcp_flow_t* flow = slot < MAX_TCP_FLOWS ? tcp_flows[slot] : NULL; + if (!flow) continue; + if (flow->base.local_port > port) break; + if (flow->base.retired || flow->base.state == TCP_STATE_CLOSED) continue; + if (flow->base.local_port != port || !tcp_flow_matches_bind(flow, spec)) continue; + + bool allowed = reuseaddr && flow->ip.reuseaddr; + if (!allowed) { + conflict = true; + break; + } + } + irq_restore(irq); + return conflict; +} + bool tcp_get_ctx(uint16_t local_port, ip_version_t ver, const void *local_ip, const void *remote_ip, uint16_t remote_port, tcp_data *out_ctx){ if (!out_ctx) return false; tcp_flow_t *flow = tcp_flow_acquire_match(local_port, ver, local_ip, remote_ip, remote_port, NULL); @@ -155,13 +159,25 @@ tcp_flow_t *tcp_flow_from_ctx(tcp_data *flow_ctx) { return flow; } +void tcp_update_mss(tcp_flow_t *flow) { + if (!flow) return; + uint32_t local_mss = flow->tx.path_mss ? flow->tx.path_mss : TCP_DEFAULT_MSS; + if (flow->tx.configured_mss && flow->tx.configured_mss < local_mss) local_mss = flow->tx.configured_mss; + if (!local_mss) local_mss = 1; + flow->tx.advertised_mss = local_mss; + + uint32_t mss = local_mss; + if (flow->tx.peer_mss && flow->tx.peer_mss < mss) mss = flow->tx.peer_mss; + flow->tx.mss = mss; +} + void tcp_flow_apply_socket_options(tcp_data *flow_ctx, const SocketOptions* extra) { tcp_flow_t *flow = tcp_flow_from_ctx(flow_ctx); if (!flow) return; uint32_t flags = extra ? extra->flags : 0; flow->timer.keepalive_on = (flags & SOCK_OPT_KEEPALIVE) ? 1 : 0; - flow->timer.keepalive_ms = flow->timer.keepalive_on ?(extra->keepalive_ms ? extra->keepalive_ms : SOCKET_DEFAULT_KEEPALIVE_MS) : 0; + flow->timer.keepalive_ms = flow->timer.keepalive_on ? (extra->keepalive_ms ? extra->keepalive_ms : SOCKET_DEFAULT_KEEPALIVE_MS) : 0; flow->timer.keepalive_idle_ms = 0; flow->tx.nodelay = (flags & SOCK_OPT_TCP_NO_DELAY) ? 1 : 0; @@ -170,12 +186,51 @@ void tcp_flow_apply_socket_options(tcp_data *flow_ctx, const SocketOptions* extr uint32_t queued_limit = (flags & SOCK_OPT_SEND_BUF_SIZE) && extra->send_buf_size ? extra->send_buf_size : TCP_TX_MAX_BYTES_PER_FLOW; if (queued_limit > TCP_TX_MAX_BYTES_PER_FLOW) queued_limit = TCP_TX_MAX_BYTES_PER_FLOW; flow->tx.queued_limit = queued_limit; + flow->tx.configured_mss = (flags & SOCK_OPT_TCP_MAXSEG) && extra->tcp_maxseg ? extra->tcp_maxseg : 0; + tcp_update_mss(flow); flow->ip.ttl = (flags & SOCK_OPT_TTL) ? extra->ttl : 0; flow->ip.dontfrag = (flags & SOCK_OPT_DONTFRAG) ? 1 : 0; + flow->ip.reuseaddr = (flags & SOCK_OPT_REUSEADDR) ? 1 : 0; tcp_flow_put(flow); } +static void tcp_flow_clear_storage(tcp_flow_t *flow) { + for (int i = 0; i < TCP_MAX_TX_SEGS; i++) tcp_tx_seg_clear(flow, &flow->tx.txq[i]); + flow->tx.queued_bytes = 0; + + if (flow->tx.nagle_buf) { + uintptr_t buf = flow->tx.nagle_buf; + flow->tx.nagle_buf = 0; + release((void*)buf); + } + flow->tx.nagle_len = 0; + flow->tx.nagle_cap = 0; + flow->tx.nagle_timer_ms = 0; + flow->tx.nagle_flushing = 0; + flow->tx.nagle_appending = 0; + + if (flow->rx.reass_count || flow->rx.rcv_ooo_used) tcp_account_ooo_remove(flow->rx.rcv_ooo_used, flow->rx.reass_count); + if (flow->rx.rcv_buf) { + uintptr_t buf = flow->rx.rcv_buf; + flow->rx.rcv_buf = 0; + release((void*)buf); + } + flow->rx.rcv_base = flow->rx.rcv_nxt; + flow->rx.rcv_data_nxt = flow->rx.rcv_nxt; + flow->rx.rcv_ooo_used = 0; + flow->rx.rcv_wnd = 0; + flow->rx.rcv_adv_edge = flow->rx.rcv_nxt; + flow->rx.fin_pending = 0; + flow->base.ctx.window = 0; + flow->rx.reass_count = 0; + memset(flow->rx.reass, 0, sizeof(flow->rx.reass)); + if (flow->base.listener) { + socket_core_put(flow->base.listener); + flow->base.listener = NULL; + } +} + void tcp_flow_put(tcp_flow_t *f) { if (!f) return; @@ -191,36 +246,7 @@ void tcp_flow_put(tcp_flow_t *f) { if (!free_now) return; - for (int i = 0; i < TCP_MAX_TX_SEGS; i++) tcp_tx_seg_clear(f, &f->tx.txq[i]); - - f->tx.queued_bytes = 0; - - if (f->tx.nagle_buf) { - uintptr_t buf = f->tx.nagle_buf; - f->tx.nagle_buf = 0; - release((void*)buf); - } - f->tx.nagle_len = 0; - f->tx.nagle_cap = 0; - f->tx.nagle_timer_ms = 0; - f->tx.nagle_flushing = 0; - f->tx.nagle_appending = 0; - - if (f->rx.reass_count || f->rx.rcv_ooo_used) tcp_account_ooo_remove(f->rx.rcv_ooo_used, f->rx.reass_count); - if (f->rx.rcv_buf) { - uintptr_t buf = f->rx.rcv_buf; - f->rx.rcv_buf = 0; - release((void*)buf); - } - f->rx.rcv_base = f->rx.rcv_nxt; - f->rx.rcv_data_nxt = f->rx.rcv_nxt; - f->rx.rcv_ooo_used = 0; - f->rx.rcv_wnd = 0; - f->rx.rcv_adv_edge = f->rx.rcv_nxt; - f->rx.fin_pending = 0; - f->base.ctx.window = 0; - f->rx.reass_count = 0; - memset(f->rx.reass, 0, sizeof(f->rx.reass)); + tcp_flow_clear_storage(f); memset(f, 0, sizeof(*f)); release(f); } @@ -252,6 +278,7 @@ void tcp_enter_time_wait(tcp_flow_t *flow) { flow->base.state = TCP_TIME_WAIT; flow->timer.time_wait_ms = 0; + tcp_flow_clear_storage(flow); tcp_daemon_kick(); } @@ -289,6 +316,9 @@ tcp_flow_t *tcp_alloc_flow(void){ f->rx.rcv_wnd_max = tcp_clamp_rcvbuf(TCP_DEFAULT_RCV_BUF); f->rx.rcv_wnd = f->rx.rcv_wnd_max; + f->tx.path_mss = TCP_DEFAULT_MSS; + f->tx.peer_mss = 0; + f->tx.advertised_mss = TCP_DEFAULT_MSS; f->tx.mss = TCP_DEFAULT_MSS; f->tx.cwnd = f->tx.mss * TCP_INIT_CWND_SEGS; f->tx.ssthresh = TCP_RECV_WINDOW; @@ -342,7 +372,7 @@ tcp_result_t tcp_flow_abort(tcp_data *flow_ctx) { int slot = (int)flow->base.slot; tcp_state_t state = flow->base.state; - if ( !flow->base.retired && state != TCP_STATE_CLOSED && state != TCP_TIME_WAIT && state != TCP_SYN_SENT) tcp_send_reset(flow->base.local.ver, flow->base.local.ip, flow->base.remote.ip, flow->base.local_port, flow->base.remote.port, flow->tx.snd_nxt, 0, false); + if (state != TCP_STATE_CLOSED && state != TCP_TIME_WAIT && state != TCP_SYN_SENT) tcp_send_reset(flow->base.local.ver, flow->base.local.ip, flow->base.remote.ip, flow->base.local_port, flow->base.remote.port, flow->tx.snd_nxt, 0, false); tcp_free_flow(slot); tcp_flow_put(flow); @@ -360,7 +390,11 @@ bool tcp_flow_is_closed(tcp_data *flow_ctx) { tcp_result_t tcp_flow_release_closed(tcp_data *flow_ctx) { tcp_flow_t *flow = tcp_flow_from_ctx(flow_ctx); if (!flow) return TCP_INVALID; - if (!flow->base.retired && flow->base.state != TCP_STATE_CLOSED && flow->base.state != TCP_TIME_WAIT) { + if (flow->base.state == TCP_TIME_WAIT) { + tcp_flow_put(flow); + return TCP_OK; + } + if (flow->base.state != TCP_STATE_CLOSED) { tcp_flow_put(flow); return TCP_BUSY; } @@ -499,7 +533,6 @@ bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, flow->base.local_port = local_port; flow->base.l3_id = l3_id; - tcp_active_insert_flow(flow); flow->base.remote.ver = dst->ver; memcpy(flow->base.remote.ip, dst->ip, (size_t)(dst->ver == IP_VER6 ? 16 : 4)); @@ -550,10 +583,10 @@ bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, if (extra && (extra->flags & SOCK_OPT_BUF_SIZE) && extra->buf_size) rcvbuf = extra->buf_size; flow->rx.rcv_wnd_max = tcp_clamp_rcvbuf(rcvbuf); flow->rx.rcv_adv_edge = 0; + flow->tx.path_mss = tcp_calc_mss_for_l3(l3_id, dst->ver, dst->ip); + flow->tx.peer_mss = 0; tcp_flow_apply_socket_options(&flow->base.ctx, extra); - flow->tx.mss = tcp_calc_mss_for_l3(l3_id, dst->ver, dst->ip); - if (flow->rx.rcv_wnd_max > 65535u) { flow->tx.ws_send = 8; flow->tx.ws_recv = 0; @@ -597,6 +630,7 @@ bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, flow->timer.time_wait_ms = 0; flow->timer.fin_wait2_ms = 0; + tcp_active_insert_flow(flow); tcp_tx_seg_t *seg = tcp_alloc_tx_seg(flow); @@ -617,7 +651,7 @@ bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, seg->timer_ms = 0; seg->timeout_ms = flow->tx.rto ? flow->tx.rto : TCP_INIT_RTO; - seg->opts_len = tcp_build_syn_options(seg->opts, (uint16_t)flow->tx.mss, flow->rx.rcv_wnd_max > 65535u ? flow->tx.ws_send : 0xffu, flow->tx.sack_ok); + seg->opts_len = tcp_build_syn_options(seg->opts, (uint16_t)flow->tx.advertised_mss, flow->rx.rcv_wnd_max > 65535u ? flow->tx.ws_send : 0xffu, flow->tx.sack_ok); flow->tx.snd_nxt += 1; flow->base.ctx.sequence = flow->tx.snd_nxt; diff --git a/kernel/networking/transport_layer/tcp/tcp_internal.h b/kernel/networking/transport_layer/tcp/tcp_internal.h index 831db16d..ac352133 100644 --- a/kernel/networking/transport_layer/tcp/tcp_internal.h +++ b/kernel/networking/transport_layer/tcp/tcp_internal.h @@ -69,6 +69,7 @@ typedef struct { uint8_t retries; uint16_t refs; uint8_t retired; + struct ksocket *listener; } tcp_flow_base_t; typedef struct { @@ -83,6 +84,9 @@ typedef struct { uint32_t cwnd; uint32_t ssthresh; uint32_t mss; + uint32_t advertised_mss; + uint32_t path_mss; + uint32_t peer_mss; uint8_t ws_send; uint8_t ws_recv; @@ -92,6 +96,7 @@ typedef struct { uint8_t in_fast_recovery; uint32_t recover; uint32_t cwnd_acc; + uint32_t configured_mss; uint8_t nagle_flushing; uint8_t nagle_appending; @@ -146,6 +151,7 @@ typedef struct { typedef struct { uint8_t ttl; uint8_t dontfrag; + uint8_t reuseaddr; } tcp_flow_ip_t; typedef struct tcp_flow { @@ -165,12 +171,15 @@ void tcp_enter_time_wait(tcp_flow_t *flow); tcp_flow_t *tcp_alloc_flow(void); void tcp_free_flow(int idx); void tcp_active_insert_flow(tcp_flow_t *flow); +//TODO REUSEADDR can create flows that rx cant distinguish tcp_flow_t *tcp_flow_from_ctx(tcp_data *flow_ctx); tcp_flow_t *tcp_flow_acquire_match(uint16_t local_port, ip_version_t ver, const void *local_ip, const void *remote_ip, uint16_t remote_port, int *out_idx); int tcp_flow_hold(tcp_flow_t *flow); void tcp_flow_put(tcp_flow_t *flow); void tcp_rtt_update(tcp_flow_t *flow, uint32_t sample_ms); +void tcp_update_mss(tcp_flow_t *flow); +bool tcp_flow_matches_bind(const tcp_flow_t *flow, const SockBindSpec *spec); tcp_tx_seg_t *tcp_alloc_tx_seg(tcp_flow_t *flow); const uint8_t *tcp_tx_seg_payload_ptr(const tcp_tx_seg_t *seg); diff --git a/kernel/networking/transport_layer/tcp/tcp_limits.c b/kernel/networking/transport_layer/tcp/tcp_limits.c index 97e35126..f33e9cfb 100644 --- a/kernel/networking/transport_layer/tcp/tcp_limits.c +++ b/kernel/networking/transport_layer/tcp/tcp_limits.c @@ -1,5 +1,5 @@ #include "tcp_internal.h" -#include "std/memory.h" +#include "exceptions/irq.h" uint32_t tcp_ooo_global_bytes; uint32_t tcp_ooo_global_segs; @@ -54,42 +54,57 @@ tcp_admit_result_t tcp_admit_ooo(tcp_flow_t *flow, uint32_t increase, uint32_t r uint32_t seg_increase = remaining_nodes >= flow->rx.reass_count ? 1u : 0; if (remaining_nodes >= TCP_REASS_MAX_SEGS) return TCP_ADMIT_OOO_FLOW_SEGS; - if (increase && flow->rx.rcv_ooo_used + increase > limit) return TCP_ADMIT_OOO_FLOW_BYTES; - if (increase && tcp_ooo_global_bytes + increase > TCP_REASS_GLOBAL_MAX_BYTES) return TCP_ADMIT_OOO_GLOBAL_BYTES; + if (increase && (flow->rx.rcv_ooo_used > limit || increase > limit - flow->rx.rcv_ooo_used)) return TCP_ADMIT_OOO_FLOW_BYTES; + if (increase && (tcp_ooo_global_bytes > TCP_REASS_GLOBAL_MAX_BYTES || increase > TCP_REASS_GLOBAL_MAX_BYTES - tcp_ooo_global_bytes)) return TCP_ADMIT_OOO_GLOBAL_BYTES; if (seg_increase && tcp_ooo_global_segs + seg_increase > TCP_REASS_GLOBAL_MAX_SEGS) return TCP_ADMIT_OOO_GLOBAL_SEGS; return TCP_ADMIT_OK; } -tcp_admit_result_t tcp_admit_syn(uint8_t l3_id, uint16_t port, ip_version_t ver, const void *src_ip) { +tcp_admit_result_t tcp_admit_syn(struct ksocket* listener, ip_version_t ver, const void *src_ip) { + if (!listener) return TCP_ADMIT_SYN_LISTENER; uint32_t syn_total = 0; uint32_t syn_listener = 0; uint32_t syn_source = 0; - uint32_t timewait = 0; - uint32_t orphan = 0; size_t ip_len = (size_t)(ver == IP_VER6 ? 16 : 4); - for (uint16_t n = 0; n < tcp_active_count; n++) { + irq_flags_t irq = irq_save_disable(); + uint32_t active_count = tcp_active_count; + for (uint16_t n = 0; n < active_count; n++) { uint16_t slot = tcp_active_flows[n]; - tcp_flow_t *f = slot < MAX_TCP_FLOWS ? tcp_flows[slot] : NULL; - if (!f) continue; - - if (f->base.state == TCP_SYN_RECEIVED) { - syn_total++; - if (f->base.local_port == port && f->base.l3_id == l3_id) syn_listener++; - if (src_ip && f->base.local_port == port && f->base.l3_id == l3_id && f->base.remote.ver == ver && memcmp(f->base.remote.ip, src_ip, ip_len) == 0) syn_source++; - } else if (f->base.state == TCP_TIME_WAIT) timewait++; - else if (f->base.state != TCP_LISTEN && f->base.state != TCP_STATE_CLOSED && !f->base.local_port) orphan++; + tcp_flow_t* flow = slot < MAX_TCP_FLOWS ? tcp_flows[slot] : NULL; + if (!flow || flow->base.retired || flow->base.state != TCP_SYN_RECEIVED) continue; + syn_total++; + if (flow->base.listener != listener) continue; + syn_listener++; + if (src_ip && flow->base.remote.ver == ver && memcmp(flow->base.remote.ip, src_ip, ip_len) == 0) syn_source++; } + irq_restore(irq); - uint32_t score = (uint32_t)tcp_active_count * 4 + syn_total * 4 + timewait + orphan * 4 + (tcp_ooo_global_bytes >> 12) + (tcp_tx_global_bytes >> 12); + if (active_count >= MAX_TCP_FLOWS) return TCP_ADMIT_FLOW_TABLE_FULL; - if (tcp_active_count >= MAX_TCP_FLOWS) return TCP_ADMIT_FLOW_TABLE_FULL; - if (syn_total >= TCP_SYN_RECV_MAX_GLOBAL) return TCP_ADMIT_SYN_GLOBAL; - if (syn_listener >= TCP_SYN_RECV_MAX_LISTENER) return TCP_ADMIT_SYN_LISTENER; - if (syn_source >= TCP_SYN_RECV_MAX_SOURCE) return TCP_ADMIT_SYN_SOURCE; - if (orphan >= TCP_ORPHAN_MAX_GLOBAL) return TCP_ADMIT_ORPHAN_LIMIT; - if (score >= TCP_RESOURCE_BUDGET) return TCP_ADMIT_RESOURCE_BUDGET; + uint32_t available = MAX_TCP_FLOWS - active_count; + if (available <= TCP_FLOW_CONTROL_RESERVE) return TCP_ADMIT_FLOW_RESERVE; + + uint32_t usable = available - TCP_FLOW_CONTROL_RESERVE; + uint32_t global_limit = usable / 2; + if (global_limit < TCP_SYN_RECV_MIN_GLOBAL) global_limit = TCP_SYN_RECV_MIN_GLOBAL; + if (global_limit > TCP_SYN_RECV_MAX_GLOBAL) global_limit = TCP_SYN_RECV_MAX_GLOBAL; + if (global_limit > usable) global_limit = usable; + + uint32_t listener_limit = usable / 8; + if (listener_limit < TCP_SYN_RECV_MIN_LISTENER) listener_limit = TCP_SYN_RECV_MIN_LISTENER; + if (listener_limit > TCP_SYN_RECV_MAX_LISTENER) listener_limit = TCP_SYN_RECV_MAX_LISTENER; + if (listener_limit > global_limit) listener_limit = global_limit; + + uint32_t source_limit = listener_limit / 4; + if (source_limit < TCP_SYN_RECV_MIN_SOURCE) source_limit = TCP_SYN_RECV_MIN_SOURCE; + if (source_limit > TCP_SYN_RECV_MAX_SOURCE) source_limit = TCP_SYN_RECV_MAX_SOURCE; + if (source_limit > listener_limit) source_limit = listener_limit; + + if (syn_total >= global_limit) return TCP_ADMIT_SYN_GLOBAL; + if (syn_listener >= listener_limit) return TCP_ADMIT_SYN_LISTENER; + if (syn_source >= source_limit) return TCP_ADMIT_SYN_SOURCE; return TCP_ADMIT_OK; } @@ -100,7 +115,7 @@ tcp_admit_result_t tcp_admit_tx(tcp_flow_t *flow, uint32_t bytes, uint32_t free_ uint32_t limit = flow->tx.queued_limit ? flow->tx.queued_limit : TCP_TX_MAX_BYTES_PER_FLOW; if (flow->tx.queued_bytes >= limit) return TCP_ADMIT_TX_FLOW_BYTES; if (tcp_tx_global_bytes >= TCP_TX_MAX_BYTES_GLOBAL) return TCP_ADMIT_TX_GLOBAL_BYTES; - if (bytes && flow->tx.queued_bytes + bytes > limit) return TCP_ADMIT_TX_FLOW_BYTES; - if (bytes && tcp_tx_global_bytes + bytes > TCP_TX_MAX_BYTES_GLOBAL) return TCP_ADMIT_TX_GLOBAL_BYTES; + if (bytes && (flow->tx.queued_bytes > limit || bytes > limit - flow->tx.queued_bytes)) return TCP_ADMIT_TX_FLOW_BYTES; + if (bytes && (tcp_tx_global_bytes > TCP_TX_MAX_BYTES_GLOBAL || bytes > TCP_TX_MAX_BYTES_GLOBAL - tcp_tx_global_bytes)) return TCP_ADMIT_TX_GLOBAL_BYTES; return TCP_ADMIT_OK; } diff --git a/kernel/networking/transport_layer/tcp/tcp_limits.h b/kernel/networking/transport_layer/tcp/tcp_limits.h index 48407754..238bc32f 100644 --- a/kernel/networking/transport_layer/tcp/tcp_limits.h +++ b/kernel/networking/transport_layer/tcp/tcp_limits.h @@ -21,10 +21,13 @@ extern "C" { #define TCP_SYN_RECV_MAX_LISTENER 32u #define TCP_SYN_RECV_MAX_SOURCE 8u #define TCP_TIMEWAIT_MAX_GLOBAL 128u -#define TCP_ORPHAN_MAX_GLOBAL 32u -#define TCP_RESOURCE_BUDGET 4096u +#define TCP_FLOW_CONTROL_RESERVE 32u +#define TCP_SYN_RECV_MIN_GLOBAL 16u +#define TCP_SYN_RECV_MIN_LISTENER 4u +#define TCP_SYN_RECV_MIN_SOURCE 2u -typedef struct tcp_flow tcp_flow_t; +struct tcp_flow; +struct ksocket; typedef enum { TCP_ADMIT_OK = 0, @@ -35,11 +38,10 @@ typedef enum { TCP_ADMIT_SYN_GLOBAL, TCP_ADMIT_SYN_LISTENER, TCP_ADMIT_SYN_SOURCE, - TCP_ADMIT_ORPHAN_LIMIT, + TCP_ADMIT_FLOW_RESERVE, TCP_ADMIT_TX_FLOW_BYTES, TCP_ADMIT_TX_FLOW_SEGS, TCP_ADMIT_TX_GLOBAL_BYTES, - TCP_ADMIT_RESOURCE_BUDGET, TCP_ADMIT_FLOW_TABLE_FULL } tcp_admit_result_t; @@ -53,11 +55,10 @@ typedef struct { uint64_t syn_drop_source; uint64_t acceptq_drop_full; uint64_t timewait_reap_oldest; - uint64_t orphan_drop_global; + uint64_t syn_drop_flow_reserve; uint64_t tx_block_flow_bytes; uint64_t tx_block_flow_segs; uint64_t tx_block_global_bytes; - uint64_t resource_budget_drop; uint64_t flow_table_full; } tcp_stats_t; @@ -67,13 +68,13 @@ extern uint32_t tcp_tx_global_bytes; extern tcp_stats_t tcp_stats; uint32_t tcp_clamp_rcvbuf(uint32_t size); -tcp_admit_result_t tcp_admit_syn(uint8_t l3_id, uint16_t port, ip_version_t ver, const void *src_ip); -tcp_admit_result_t tcp_admit_ooo(tcp_flow_t *flow, uint32_t increase, uint32_t remaining_nodes); -tcp_admit_result_t tcp_admit_tx(tcp_flow_t *flow, uint32_t bytes, uint32_t free_slots); +tcp_admit_result_t tcp_admit_syn(struct ksocket *listener, ip_version_t ver, const void *src_ip); +tcp_admit_result_t tcp_admit_ooo(struct tcp_flow *flow, uint32_t increase, uint32_t remaining_nodes); +tcp_admit_result_t tcp_admit_tx(struct tcp_flow *flow, uint32_t bytes, uint32_t free_slots); void tcp_account_ooo_add(uint32_t bytes, uint32_t segs); void tcp_account_ooo_remove(uint32_t bytes, uint32_t segs); -void tcp_account_tx_add(tcp_flow_t *flow, uint32_t bytes); -void tcp_account_tx_remove(tcp_flow_t *flow, uint32_t bytes); +void tcp_account_tx_add(struct tcp_flow *flow, uint32_t bytes); +void tcp_account_tx_remove(struct tcp_flow *flow, uint32_t bytes); #ifdef __cplusplus } diff --git a/kernel/networking/transport_layer/tcp/tcp_rx.c b/kernel/networking/transport_layer/tcp/tcp_rx.c index b2456178..b8bf116d 100644 --- a/kernel/networking/transport_layer/tcp/tcp_rx.c +++ b/kernel/networking/transport_layer/tcp/tcp_rx.c @@ -404,8 +404,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a } if (flow && flow->base.state == TCP_TIME_WAIT && (flags & (1 << SYN_F)) && !(flags& ((1 << ACK_F) | (1 << RST_F) | (1 << FIN_F))) && data_len == 0) { - ksocket_t* listener = NULL; - socket_bind_collect(PROTO_TCP, ipver, l3_id, ifx, dst_ip_addr, dst_port, &listener, 1); + ksocket_t* listener = socket_bind_lookup(PROTO_TCP, ipver, l3_id, ifx, src_ip_addr, src_port, dst_ip_addr, dst_port); if (listener) { socket_core_put(listener); tcp_free_flow(idx); @@ -429,20 +428,18 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a } if (!flow){ - ksocket_t* listener = NULL; - socket_bind_collect(PROTO_TCP, ipver, l3_id, ifx, dst_ip_addr, dst_port, &listener, 1); + ksocket_t* listener = socket_bind_lookup(PROTO_TCP, ipver, l3_id, ifx, src_ip_addr, src_port, dst_ip_addr, dst_port); if ((flags & (1u << SYN_F)) && !(flags & ((1u << ACK_F) | (1u << RST_F) | (1u << FIN_F))) && data_len == 0 && listener){ rng_t rng; - rng_init_random(&rng); + rng_init_random(&rng); - tcp_admit_result_t syn_admit = tcp_admit_syn(l3_id, dst_port, ipver, src_ip_addr); + tcp_admit_result_t syn_admit = tcp_admit_syn(listener, ipver, src_ip_addr); if (syn_admit != TCP_ADMIT_OK) { if (syn_admit == TCP_ADMIT_SYN_GLOBAL) tcp_stats.syn_drop_global++; else if (syn_admit == TCP_ADMIT_SYN_LISTENER) tcp_stats.syn_drop_listener++; else if (syn_admit == TCP_ADMIT_SYN_SOURCE) tcp_stats.syn_drop_source++; - else if (syn_admit == TCP_ADMIT_ORPHAN_LIMIT) tcp_stats.orphan_drop_global++; - else if (syn_admit == TCP_ADMIT_RESOURCE_BUDGET) tcp_stats.resource_budget_drop++; + else if (syn_admit == TCP_ADMIT_FLOW_RESERVE) tcp_stats.syn_drop_flow_reserve++; else if (syn_admit == TCP_ADMIT_FLOW_TABLE_FULL) tcp_stats.flow_table_full++; socket_core_put(listener); netpkt_unref(pkt); @@ -467,7 +464,6 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a idx = (int)flow->base.slot; flow->base.local_port = dst_port; flow->base.l3_id = l3_id; - tcp_active_insert_flow(flow); flow->base.remote.ver = ipver; memset(flow->base.remote.ip, 0, 16); @@ -498,14 +494,8 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a flow->tx.sack_ok = pop.sack_permitted ? 1 : 0; - if (pop.has_mss && pop.mss){ - uint32_t m = pop.mss; - uint32_t minm = ipver == IP_VER6 ? 1220u : 536u; - uint32_t maxm = tcp_calc_mss_for_l3(l3_id, ipver, src_ip_addr); - if (m < minm) m = minm; - if (m > maxm) m = maxm; - flow->tx.mss = m; - } else flow->tx.mss = tcp_calc_mss_for_l3(l3_id, ipver, src_ip_addr); + flow->tx.path_mss = tcp_calc_mss_for_l3(l3_id, ipver, src_ip_addr); + flow->tx.peer_mss = pop.has_mss && pop.mss ? pop.mss : 0; flow->base.ctx.flags = 0; flow->base.ctx.options.ptr = 0; flow->base.ctx.options.size = 0; @@ -561,12 +551,14 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a flow->timer.time_wait_ms = 0; flow->timer.fin_wait2_ms = 0; + flow->base.listener = listener; + listener = NULL; + tcp_active_insert_flow(flow); tcp_tx_seg_t *seg = tcp_alloc_tx_seg(flow); if (!seg) { tcp_free_flow(idx); tcp_flow_put(flow); - socket_core_put(listener); netpkt_unref(pkt); return; } @@ -581,19 +573,17 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a seg->payload_off = 0; seg->timer_ms = 0; seg->timeout_ms = flow->tx.rto ? flow->tx.rto : TCP_INIT_RTO; - seg->opts_len = tcp_build_syn_options(seg->opts, (uint16_t)flow->tx.mss, flow->tx.ws_ok ? flow->tx.ws_send : 0xff, flow->tx.sack_ok); + seg->opts_len = tcp_build_syn_options(seg->opts, (uint16_t)flow->tx.advertised_mss, flow->tx.ws_ok ? flow->tx.ws_send : 0xff, flow->tx.sack_ok); flow->tx.snd_nxt = iss + 1; flow->base.ctx.sequence = flow->tx.snd_nxt; if (!tcp_send_from_seg(flow, seg)) { tcp_free_flow(idx); tcp_flow_put(flow); - socket_core_put(listener); netpkt_unref(pkt); return; } tcp_daemon_kick(); - socket_core_put(listener); tcp_flow_put(flow); netpkt_unref(pkt); return; @@ -693,7 +683,12 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a flow->base.state = TCP_FIN_WAIT_2; flow->timer.fin_wait2_ms = 0; tcp_daemon_kick(); - } else if ((flow->base.state == TCP_LAST_ACK || flow->base.state == TCP_CLOSING) && TCP_SEQ_GEQ(ack, flow->base.ctx.expected_ack)){ + } else if (flow->base.state == TCP_CLOSING && TCP_SEQ_GEQ(ack, flow->base.ctx.expected_ack)){ + tcp_enter_time_wait(flow); + tcp_flow_put(flow); + netpkt_unref(pkt); + return; + } else if (flow->base.state == TCP_LAST_ACK && TCP_SEQ_GEQ(ack, flow->base.ctx.expected_ack)){ tcp_free_flow(idx); tcp_flow_put(flow); netpkt_unref(pkt); @@ -738,16 +733,9 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a flow->tx.sack_ok = pop.sack_permitted ? 1 : 0; - if (pop.has_mss && pop.mss){ - uint32_t m = pop.mss; - uint32_t minm = ipver == IP_VER6 ? 1220u : 536u; - uint32_t maxm = tcp_calc_mss_for_l3(l3_id, ipver, src_ip_addr); - if (m < minm) m = minm; - if (m > maxm) m = maxm; - flow->tx.mss = m; - } else { - flow->tx.mss = tcp_calc_mss_for_l3(l3_id, ipver, src_ip_addr); - } + flow->tx.path_mss = tcp_calc_mss_for_l3(l3_id, ipver, src_ip_addr); + flow->tx.peer_mss = pop.has_mss && pop.mss ? pop.mss : 0; + tcp_update_mss(flow); uint32_t new_wnd = window; if (flow->tx.ws_ok && flow->tx.ws_recv) new_wnd <<= flow->tx.ws_recv; @@ -816,12 +804,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a } uint32_t queued = 0; - ksocket_t* listener = NULL; - socket_bind_collect(PROTO_TCP, ipver, l3_id, ifx, dst_ip_addr, dst_port, &listener, 1); - if (listener) { - queued = tcp_accept_enqueue(listener, ipver, src_ip_addr, dst_ip_addr, src_port, dst_port); - socket_core_put(listener); - } + if (flow->base.listener) queued = tcp_accept_enqueue(flow->base.listener, ipver, src_ip_addr, dst_ip_addr, src_port, dst_port); if (!queued) { tcp_stats.acceptq_drop_full++; tcp_hdr_t rst_hdr; @@ -849,6 +832,10 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a return; } + if (flow->base.listener) { + socket_core_put(flow->base.listener); + flow->base.listener = NULL; + } flow->base.ctx.sequence = flow->tx.snd_nxt; flow->tx.snd_una = ack; flow->base.state = TCP_ESTABLISHED; @@ -970,7 +957,6 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a flow->base.ctx.ack = flow->rx.rcv_nxt; } else if (data_len) { (void)tcp_calc_adv_wnd_field(flow, 1); - need_ack = 1; ack_immediate = 1; } @@ -986,9 +972,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a if (old == TCP_ESTABLISHED) flow->base.state = TCP_CLOSE_WAIT; else if (old == TCP_FIN_WAIT_1) flow->base.state = TCP_CLOSING; - else if (old == TCP_FIN_WAIT_2 || old == TCP_CLOSING || old == TCP_LAST_ACK) { - tcp_enter_time_wait(flow); - } + else if (old == TCP_FIN_WAIT_2) tcp_enter_time_wait(flow); ack_immediate = 1; } else { @@ -1009,9 +993,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a if (old == TCP_ESTABLISHED) flow->base.state = TCP_CLOSE_WAIT; else if (old == TCP_FIN_WAIT_1) flow->base.state = TCP_CLOSING; - else if (old == TCP_FIN_WAIT_2 || old == TCP_CLOSING || old == TCP_LAST_ACK) { - tcp_enter_time_wait(flow); - } + else if (old == TCP_FIN_WAIT_2) tcp_enter_time_wait(flow); ack_immediate = 1; } @@ -1053,7 +1035,6 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a int16_t overlapping = 0; int16_t covered = 0; - if (!tcp_reass_count_ok(flow)) ooo_len = 0; for (int i = 0; ooo_len && i < flow->rx.reass_count; i++) { tcp_reass_seg_t *r = &flow->rx.reass[i]; if (TCP_SEQ_LEQ(r->seq, start) && TCP_SEQ_GEQ(r->end, end)) { @@ -1074,10 +1055,9 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a if (!covered && ooo_len) { uint32_t merged_len = merged_end - merged_start; uint32_t increase = merged_len > old_bytes ? merged_len - old_bytes : 0; - int remaining = flow->rx.reass_count - overlapping; + uint32_t remaining_nodes = overlapping < flow->rx.reass_count ? (uint32_t)(flow->rx.reass_count - overlapping) : 0; - if (remaining < 0) remaining = 0; - tcp_admit_result_t ooo_admit = tcp_admit_ooo(flow, increase, (uint32_t)remaining); + tcp_admit_result_t ooo_admit = tcp_admit_ooo(flow, increase, remaining_nodes); if (ooo_admit != TCP_ADMIT_OK) { if (ooo_admit == TCP_ADMIT_OOO_FLOW_BYTES) tcp_stats.ooo_drop_flow_bytes++; else if (ooo_admit == TCP_ADMIT_OOO_FLOW_SEGS) tcp_stats.ooo_drop_flow_segs++; diff --git a/kernel/networking/transport_layer/tcp/tcp_tx.c b/kernel/networking/transport_layer/tcp/tcp_tx.c index 9fbb5681..0809940d 100644 --- a/kernel/networking/transport_layer/tcp/tcp_tx.c +++ b/kernel/networking/transport_layer/tcp/tcp_tx.c @@ -116,7 +116,7 @@ static uint64_t tcp_emit_data(tcp_flow_t *flow, const uint8_t *payload, uint64_t uint32_t tx_limit = flow->tx.queued_limit ? flow->tx.queued_limit : TCP_TX_MAX_BYTES_PER_FLOW; uint32_t flow_room = tx_limit > flow->tx.queued_bytes ? tx_limit - flow->tx.queued_bytes : 0; - uint32_t global_room = TCP_TX_MAX_BYTES_GLOBAL - tcp_tx_global_bytes; + uint32_t global_room = tcp_tx_global_bytes < TCP_TX_MAX_BYTES_GLOBAL ? TCP_TX_MAX_BYTES_GLOBAL - tcp_tx_global_bytes : 0; if (seg_len > flow_room) seg_len = flow_room; if (seg_len > global_room) seg_len = global_room; if (!seg_len) break; diff --git a/kernel/networking/transport_layer/tcp/tcp_utils.c b/kernel/networking/transport_layer/tcp/tcp_utils.c index dfbba1b5..70b31bf5 100644 --- a/kernel/networking/transport_layer/tcp/tcp_utils.c +++ b/kernel/networking/transport_layer/tcp/tcp_utils.c @@ -3,6 +3,23 @@ #include "tcp_internal.h" #include "networking/interface_manager.h" +bool tcp_flow_matches_bind(const tcp_flow_t *flow, const SockBindSpec *spec) { + if (!flow || !spec) return false; + + ip_version_t ver = flow->base.local.ver; + if ((spec->kind == BIND_L3 || spec->kind == BIND_L2) && spec->ver && spec->ver != ver) return false; + if (spec->kind == BIND_ANY) return true; + if (spec->kind == BIND_ANY4) return ver == IP_VER4; + if (spec->kind == BIND_ANY6) return ver == IP_VER6; + if (spec->kind == BIND_IP) { + if (spec->ver != ver) return false; + return memcmp(spec->ip, flow->base.local.ip, ver == IP_VER6 ? 16 : 4) == 0; + } + if (spec->kind == BIND_L3) return spec->l3_id == flow->base.l3_id; + if (spec->kind == BIND_L2) return l3_ifindex_from_id(flow->base.l3_id) == spec->ifindex; + return false; +} + uint32_t tcp_calc_mss_for_l3(uint8_t l3_id, ip_version_t ver, const void *remote_ip){ uint32_t mtu = 1500; l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(l3_id); diff --git a/kernel/networking/transport_layer/udp.c b/kernel/networking/transport_layer/udp.c index 44c70730..5a9724fe 100644 --- a/kernel/networking/transport_layer/udp.c +++ b/kernel/networking/transport_layer/udp.c @@ -9,6 +9,7 @@ #include "types.h" #include "syscalls/syscalls.h" #include "networking/internet_layer/ipv4_utils.h" +#include "networking/internet_layer/ipv6_utils.h" size_t create_udp_segment(uintptr_t buf, const net_l4_endpoint *src, const net_l4_endpoint *dst, sizedptr payload) { udp_hdr_t udp; @@ -126,11 +127,26 @@ void udp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a if (v4 && v4->l2) ifx = v4->l2->ifindex; else if (v6 && v6->l2) ifx = v6->l2->ifindex; - ksocket_t* sockets[SOCKET_BIND_COLLECT_MAX]; - uint32_t count = socket_bind_collect(PROTO_UDP, ipver, l3_id, ifx, dst_ip_addr, dst_port, sockets, SOCKET_BIND_COLLECT_MAX); - for (uint32_t i = 0; i < count; ++i) { - socket_udp_input(sockets[i], ipver, l3_id, src_ip_addr, dst_ip_addr, plpkt, src_port, dst_port); - socket_core_put(sockets[i]); + bool fanout = false; + if (ipver == IP_VER4) { + uint32_t dst = 0; + memcpy(&dst, dst_ip_addr, sizeof(dst)); + fanout = ipv4_is_multicast(dst) || ipv4_is_limited_broadcast(dst) || (v4 && dst == v4->broadcast); + } else if (ipver == IP_VER6) fanout = ipv6_is_multicast(dst_ip_addr); + + if (!fanout) { + ksocket_t* socket = socket_bind_lookup(PROTO_UDP, ipver, l3_id, ifx, src_ip_addr, src_port, dst_ip_addr, dst_port); + if (socket) { + socket_udp_input(socket, ipver, l3_id, src_ip_addr, dst_ip_addr, plpkt, src_port, dst_port); + socket_core_put(socket); + } + } else { + uint32_t cursor = 0; + ksocket_t* socket = NULL; + while ((socket = socket_bind_udp_next_fanout(ipver, l3_id, ifx, dst_ip_addr, dst_port, &cursor))) { + socket_udp_input(socket, ipver, l3_id, src_ip_addr, dst_ip_addr, plpkt, src_port, dst_port); + socket_core_put(socket); + } } netpkt_unref(plpkt); From 4990727ef9bb8f79ee534565dc9629b89801b4e0 Mon Sep 17 00:00:00 2001 From: CodeAnarchist Date: Mon, 3 Aug 2026 01:38:25 +0200 Subject: [PATCH 37/39] [l4/http] improve tcp, udp and sockets; centralize HTTP helpers --- .../application_layer/csocket_http_client.c | 210 ++----- .../application_layer/csocket_http_internal.c | 133 +++++ .../application_layer/csocket_http_internal.h | 44 ++ .../application_layer/csocket_http_server.c | 179 ++---- .../networking/transport_layer/csocket_tcp.c | 191 +++--- .../networking/transport_layer/csocket_udp.c | 453 +++++++------- .../networking/transport_layer/socket_bind.c | 551 ++++++++---------- .../networking/transport_layer/socket_bind.h | 9 +- kernel/networking/transport_layer/tcp.h | 5 +- .../networking/transport_layer/tcp/tcp_core.c | 265 ++++----- .../transport_layer/tcp/tcp_internal.h | 26 +- .../transport_layer/tcp/tcp_limits.c | 11 - .../transport_layer/tcp/tcp_limits.h | 4 - .../networking/transport_layer/tcp/tcp_rx.c | 223 +++---- .../transport_layer/tcp/tcp_timer.c | 336 +++++------ .../networking/transport_layer/tcp/tcp_tx.c | 167 +++--- .../transport_layer/tcp/tcp_utils.c | 75 +-- .../transport_layer/tcp/tcp_utils.h | 7 +- kernel/networking/transport_layer/udp.c | 50 +- kernel/networking/transport_layer/udp.h | 2 +- 20 files changed, 1357 insertions(+), 1584 deletions(-) create mode 100644 kernel/networking/application_layer/csocket_http_internal.c create mode 100644 kernel/networking/application_layer/csocket_http_internal.h diff --git a/kernel/networking/application_layer/csocket_http_client.c b/kernel/networking/application_layer/csocket_http_client.c index 1c321004..46968e73 100644 --- a/kernel/networking/application_layer/csocket_http_client.c +++ b/kernel/networking/application_layer/csocket_http_client.c @@ -1,4 +1,5 @@ #include "csocket_http_client.h" +#include "csocket_http_internal.h" #include "console/kio.h" #include "networking/transport_layer/csocket.h" #include "networking/net_logger/net_logger.h" @@ -37,39 +38,14 @@ static HTTPResponseMsg http_client_receive_response(HTTPClient* cli, HTTPMethod char tmp[512]; while (1) { - int hdr_end = find_crlfcrlf(buf.data, buf.length); - uint32_t start_ms = (uint32_t)get_time(); - uint32_t last_rx_ms = start_ms; - - while (hdr_end < 0) { - int64_t r = receive_from_socket(cli->sock, tmp, sizeof(tmp), NULL); - if (r == SOCK_ERR_WOULDBLOCK) { - uint32_t now = (uint32_t)get_time(); - if ((now - last_rx_ms) > cli->policy.common.header_idle_timeout_ms || (now - start_ms) > cli->policy.common.header_total_timeout_ms) { - string_free(buf); - return http_client_error_response(SOCK_ERR_PROTO); - } - msleep(10); - continue; - } - if (r < 0) { - string_free(buf); - return http_client_error_response(r); - } - if (r == 0) { - string_free(buf); - return http_client_error_response(SOCK_ERR_PROTO); - } - string_append_bytes(&buf, tmp, (uint32_t)r); - last_rx_ms = (uint32_t)get_time(); - if (buf.length > cli->policy.common.max_header_bytes) { - string_free(buf); - return http_client_error_response(SOCK_ERR_PROTO); - } - hdr_end = find_crlfcrlf(buf.data, buf.length); + int32_t hdr_end = -1; + int32_t head_result = http_socket_recv_head(cli->sock, &buf, cli->policy.common.max_header_bytes, cli->policy.common.header_idle_timeout_ms, cli->policy.common.header_total_timeout_ms, &hdr_end); + if (head_result < 0) { + string_free(buf); + return http_client_error_response(head_result == SOCK_ERR_WOULDBLOCK ? SOCK_ERR_PROTO : head_result); } - int status_line_end = strindex((char*)buf.data, "\r\n"); + int32_t status_line_end = strindex((char*)buf.data, "\r\n"); if (status_line_end <= 0) { string_free(buf); return http_client_error_response(SOCK_ERR_PROTO); @@ -98,10 +74,7 @@ static HTTPResponseMsg http_client_receive_response(HTTPClient* cli, HTTPMethod if (header_result != HTTP_PARSE_OK) { http_response_free(&resp); string_free(buf); - if (cli->sock) { - close_socket(cli->sock); - cli->sock = 0; - } + if (cli->sock) (void)http_socket_close(&cli->sock, true); return http_client_error_response(SOCK_ERR_PROTO); } @@ -130,29 +103,19 @@ static HTTPResponseMsg http_client_receive_response(HTTPClient* cli, HTTPMethod uint32_t used = 0; HTTPParseResult chunk_result = have ? http_chunked_decoder_feed(&dec, buf.data + body_start, have, &used) : HTTP_PARSE_INCOMPLETE; uint32_t body_start_ms = (uint32_t)get_time(); - uint32_t body_last_rx_ms = body_start_ms; + HTTPSocketTimeoutState body_timeout = {body_start_ms, body_start_ms}; while (chunk_result == HTTP_PARSE_INCOMPLETE) { - int64_t r = receive_from_socket(cli->sock, tmp, sizeof(tmp), NULL); - if (r == SOCK_ERR_WOULDBLOCK) { - uint32_t now = (uint32_t)get_time(); - if ((now - body_last_rx_ms) > cli->policy.common.body_idle_timeout_ms || (now - body_start_ms) > cli->policy.common.body_total_timeout_ms) break; - msleep(1); - continue; - } + int64_t r = http_socket_recv_wait(cli->sock, tmp, sizeof(tmp), cli->policy.common.body_idle_timeout_ms, cli->policy.common.body_total_timeout_ms, &body_timeout); if (r <= 0) break; chunk_result = http_chunked_decoder_feed(&dec, tmp, (uint32_t)r, &used); - body_last_rx_ms = (uint32_t)get_time(); } if (chunk_result != HTTP_PARSE_OK) { http_chunked_decoder_free(&dec); http_response_free(&resp); string_free(buf); - if (cli->sock) { - close_socket(cli->sock); - cli->sock = 0; - } + if (cli->sock) (void)http_socket_close(&cli->sock, true); return http_client_error_response(SOCK_ERR_PROTO); } @@ -169,10 +132,7 @@ static HTTPResponseMsg http_client_receive_response(HTTPClient* cli, HTTPMethod if (need > cli->policy.common.max_body_bytes) { http_response_free(&resp); string_free(buf); - if (cli->sock) { - close_socket(cli->sock); - cli->sock = 0; - } + if (cli->sock) (void)http_socket_close(&cli->sock, true); return http_client_error_response(SOCK_ERR_PROTO); } @@ -181,40 +141,22 @@ static HTTPResponseMsg http_client_receive_response(HTTPClient* cli, HTTPMethod if (!body_copy) { http_response_free(&resp); string_free(buf); - if (cli->sock) { - close_socket(cli->sock); - cli->sock = 0; - } + if (cli->sock) (void)http_socket_close(&cli->sock, true); return http_client_error_response(SOCK_ERR_SYS); } uint32_t copied = have < need ? have : need; if (copied) memcpy(body_copy, buf.data + body_start, copied); - uint32_t body_start_ms = (uint32_t)get_time(); - uint32_t body_last_rx_ms = body_start_ms; - while (copied < need) { - int64_t r = receive_from_socket(cli->sock, body_copy + copied, need - copied, NULL); - if (r == SOCK_ERR_WOULDBLOCK) { - uint32_t now = (uint32_t)get_time(); - if ((now - body_last_rx_ms) > cli->policy.common.body_idle_timeout_ms || (now - body_start_ms) > cli->policy.common.body_total_timeout_ms) break; - msleep(1); - continue; - } - if (r <= 0) break; - copied += (uint32_t)r; - body_last_rx_ms = (uint32_t)get_time(); - } - if (copied < need) { - release(body_copy); - http_response_free(&resp); - string_free(buf); - if (cli->sock) { - close_socket(cli->sock); - cli->sock = 0; + int64_t receive_result = http_socket_recv_exact(cli->sock, body_copy + copied, need - copied, cli->policy.common.body_idle_timeout_ms, cli->policy.common.body_total_timeout_ms); + if (receive_result < 0) { + release(body_copy); + http_response_free(&resp); + string_free(buf); + if (cli->sock) (void)http_socket_close(&cli->sock, true); + return http_client_error_response(receive_result == SOCK_ERR_WOULDBLOCK ? SOCK_ERR_PROTO : receive_result); } - return http_client_error_response(SOCK_ERR_PROTO); } resp.body = (string){body_copy, need, need}; @@ -224,47 +166,25 @@ static HTTPResponseMsg http_client_receive_response(HTTPClient* cli, HTTPMethod if (have) string_append_bytes(&body, buf.data + body_start, have); uint32_t body_start_ms = (uint32_t)get_time(); - uint32_t body_last_rx_ms = body_start_ms; + HTTPSocketTimeoutState body_timeout = {body_start_ms, body_start_ms}; while (body.length <= cli->policy.common.max_body_bytes) { - int64_t r = receive_from_socket(cli->sock, tmp, sizeof(tmp), NULL); - if (r == SOCK_ERR_WOULDBLOCK) { - uint32_t now = (uint32_t)get_time(); - if ((now - body_last_rx_ms) > cli->policy.common.body_idle_timeout_ms || (now - body_start_ms) > cli->policy.common.body_total_timeout_ms) { - string_free(body); - http_response_free(&resp); - string_free(buf); - if (cli->sock) { - close_socket(cli->sock); - cli->sock = 0; - } - return http_client_error_response(SOCK_ERR_PROTO); - } - msleep(1); - continue; - } + int64_t r = http_socket_recv_wait(cli->sock, tmp, sizeof(tmp), cli->policy.common.body_idle_timeout_ms, cli->policy.common.body_total_timeout_ms, &body_timeout); if (r < 0) { string_free(body); http_response_free(&resp); string_free(buf); - if (cli->sock) { - close_socket(cli->sock); - cli->sock = 0; - } - return http_client_error_response(r); + if (cli->sock) (void)http_socket_close(&cli->sock, true); + return http_client_error_response(r == SOCK_ERR_WOULDBLOCK ? SOCK_ERR_PROTO : r); } if (r == 0) break; if (body.length + (uint32_t)r > cli->policy.common.max_body_bytes) { string_free(body); http_response_free(&resp); string_free(buf); - if (cli->sock) { - close_socket(cli->sock); - cli->sock = 0; - } + if (cli->sock) (void)http_socket_close(&cli->sock, true); return http_client_error_response(SOCK_ERR_PROTO); } string_append_bytes(&body, tmp, (uint32_t)r); - body_last_rx_ms = (uint32_t)get_time(); } if (body.length) { @@ -279,11 +199,7 @@ static HTTPResponseMsg http_client_receive_response(HTTPClient* cli, HTTPMethod ev.pid = get_current_proc_pid(); ev.u0 = (uint32_t)resp.status_code; ev.u1 = (uint32_t)resp.body.length; - uint32_t local_port = 0; - uint32_t opt_len = sizeof(local_port); - if (get_socket_option(cli->sock, SOCK_GET_LOCAL_PORT, &local_port, &opt_len) == SOCK_OK) ev.local_port = local_port; - opt_len = sizeof(ev.remote_ep); - get_socket_option(cli->sock, SOCK_GET_REMOTE_ENDPOINT, &ev.remote_ep, &opt_len); + http_socket_fill_log_endpoints(cli->sock, &ev); netlog_socket_event(&cli->log_opts, &ev); string_free(buf); @@ -307,8 +223,7 @@ static int32_t http_client_connect_endpoint_origin(HTTPClient* cli, const net_l4 if (cli->origin.domain.mem_length) string_free(cli->origin.domain); cli->origin = next; } else { - close_socket(cli->sock); - cli->sock = 0; + (void)http_socket_close(&cli->sock, true); } netlog_socket_event_t ev = {0}; @@ -319,11 +234,7 @@ static int32_t http_client_connect_endpoint_origin(HTTPClient* cli, const net_l4 ev.i0 = r; if (cli->sock) { - uint32_t local_port = 0; - uint32_t opt_len = sizeof(local_port); - if (get_socket_option(cli->sock, SOCK_GET_LOCAL_PORT, &local_port, &opt_len) == SOCK_OK) ev.local_port = local_port; - opt_len = sizeof(ev.remote_ep); - get_socket_option(cli->sock, SOCK_GET_REMOTE_ENDPOINT, &ev.remote_ep, &opt_len); + http_socket_fill_log_endpoints(cli->sock, &ev); if (ev.remote_ep.ver) ev.dst_ep = ev.remote_ep; } @@ -355,7 +266,8 @@ http_client_handle_t http_client_create(const SocketOptions* extra, const HTTPCl void http_client_destroy(http_client_handle_t h) { if (!h) return; HTTPClient* cli = (HTTPClient*)h; - http_client_close(cli); + if (cli->sock) (void)http_socket_close(&cli->sock, true); + if (cli->origin.domain.mem_length) string_free(cli->origin.domain); release(cli); } @@ -430,21 +342,9 @@ HTTPResponseMsg http_client_send_request(http_client_handle_t h, const HTTPReque string out = http_request_builder(&curr); uint32_t out_len = out.length; - uint32_t off = 0; - int64_t sent = 0; - while (off < out_len) { - int64_t r = send_on_socket(cli->sock, (void*)(out.data + off), out_len - off); - if (r == SOCK_ERR_WOULDBLOCK) { - msleep(5); - continue; - } - if (r < 0) { - sent = r; - break; - } - off += (uint32_t)r; - } - if (sent >= 0) sent = (int64_t)off; + uint32_t send_start_ms = (uint32_t)get_time(); + HTTPSocketTimeoutState send_timeout = {send_start_ms, send_start_ms}; + int64_t sent = http_socket_send_all(cli->sock, out.data, out_len, 0, HTTP_SOCKET_WRITE_IDLE_TIMEOUT_MS, HTTP_SOCKET_WRITE_TOTAL_TIMEOUT_MS, &send_timeout); netlog_socket_event_t ev = {0}; ev.comp = NETLOG_COMP_HTTP_CLIENT; @@ -452,11 +352,7 @@ HTTPResponseMsg http_client_send_request(http_client_handle_t h, const HTTPReque ev.pid = get_current_proc_pid(); ev.u0 = out_len; ev.i0 = sent; - uint32_t local_port = 0; - uint32_t opt_len = sizeof(local_port); - if (get_socket_option(cli->sock, SOCK_GET_LOCAL_PORT, &local_port, &opt_len) == SOCK_OK) ev.local_port = local_port; - opt_len = sizeof(ev.remote_ep); - get_socket_option(cli->sock, SOCK_GET_REMOTE_ENDPOINT, &ev.remote_ep, &opt_len); + http_socket_fill_log_endpoints(cli->sock, &ev); char pathbuf[128]; if (curr.path.length && curr.path.data) { @@ -504,12 +400,8 @@ HTTPResponseMsg http_client_send_request(http_client_handle_t h, const HTTPReque curr.headers_common.fields.host = (string){0}; } - if (cli->sock) { - close_socket(cli->sock); - cli->sock = 0; - } - int32_t rr = SOCK_ERR_SYS; - if (next_domain.data) rr = http_client_connect_domain_origin(cli, next_domain.data, next_port); + int32_t rr = cli->sock ? http_socket_close(&cli->sock, false) : SOCK_OK; + if (rr >= 0) rr = next_domain.data ? http_client_connect_domain_origin(cli, next_domain.data, next_port) : SOCK_ERR_SYS; if (next_domain.mem_length) string_free(next_domain); if (rr < 0) { http_response_free(&resp); @@ -521,15 +413,11 @@ HTTPResponseMsg http_client_send_request(http_client_handle_t h, const HTTPReque string reconnect_domain = {0}; if (cli->origin.domain.data) reconnect_domain = string_from_literal(cli->origin.domain.data); - if (cli->sock) { - close_socket(cli->sock); - cli->sock = 0; - } - int32_t rr = SOCK_ERR_STATE; - if (cli->origin.valid) { + int32_t rr = cli->sock ? http_socket_close(&cli->sock, false) : SOCK_OK; + if (rr >= 0 && cli->origin.valid) { if (reconnect_domain.data) rr = http_client_connect_domain_origin(cli, reconnect_domain.data, reconnect_ep.port); else rr = http_client_connect_endpoint_origin(cli, &reconnect_ep, NULL); - } + } else if (rr >= 0) rr = SOCK_ERR_STATE; if (reconnect_domain.mem_length) string_free(reconnect_domain); if (rr < 0) { http_response_free(&resp); @@ -563,23 +451,15 @@ int32_t http_client_close(http_client_handle_t h) { ev.action = NETLOG_ACT_CLOSE; ev.pid = get_current_proc_pid(); - if (cli->sock) { - uint32_t local_port = 0; - uint32_t opt_len = sizeof(local_port); - if (get_socket_option(cli->sock, SOCK_GET_LOCAL_PORT, &local_port, &opt_len) == SOCK_OK) ev.local_port = local_port; - opt_len = sizeof(ev.remote_ep); - get_socket_option(cli->sock, SOCK_GET_REMOTE_ENDPOINT, &ev.remote_ep, &opt_len); - } + if (cli->sock) http_socket_fill_log_endpoints(cli->sock, &ev); - int32_t r = SOCK_ERR_STATE; - if (cli->sock) { - r = close_socket(cli->sock); - cli->sock = 0; - } + int32_t r = cli->sock ? http_socket_close(&cli->sock, false) : SOCK_ERR_STATE; ev.i0 = r; netlog_socket_event(&cli->log_opts, &ev); - if (cli->origin.domain.mem_length) string_free(cli->origin.domain); - cli->origin = (HTTPClientOrigin){0}; + if (!cli->sock){ + if (cli->origin.domain.mem_length) string_free(cli->origin.domain); + cli->origin = (HTTPClientOrigin){0}; + } return r; } diff --git a/kernel/networking/application_layer/csocket_http_internal.c b/kernel/networking/application_layer/csocket_http_internal.c new file mode 100644 index 00000000..ad5fdeb2 --- /dev/null +++ b/kernel/networking/application_layer/csocket_http_internal.c @@ -0,0 +1,133 @@ +#include "csocket_http_internal.h" +#include "http.h" +#include "net/socket_types.h" +#include "process/scheduler.h" +#include "syscalls/syscalls.h" + +int32_t http_socket_close(socket_handle_t *socket, bool abort) { + if (!socket || !*socket) return SOCK_ERR_STATE; + + socket_handle_t handle = *socket; + if (abort) { + SocketLinger linger = {.enabled = 1, .timeout_ms = 0}; + set_socket_option(handle, SOCK_OPT_LINGER, &linger, sizeof(linger)); + } else { + uint32_t nonblock = 0; + set_socket_option(handle, SOCK_OPT_NONBLOCK, &nonblock, sizeof(nonblock)); + } + + int32_t close_result = close_socket(handle); + if (close_result != SOCK_ERR_WOULDBLOCK) *socket = 0; + return close_result; +} + +int64_t http_socket_recv_wait(socket_handle_t socket, + void *buffer, + uint32_t length, + uint32_t idle_timeout_ms, + uint32_t total_timeout_ms, + HTTPSocketTimeoutState *timeout) { + while (1) { + uint32_t now = (uint32_t)get_time(); + if ((idle_timeout_ms && now - timeout->last_progress_ms >= idle_timeout_ms)|| (total_timeout_ms && now - timeout->start_ms >= total_timeout_ms)) return SOCK_ERR_WOULDBLOCK; + + int64_t result = receive_from_socket(socket, buffer, length, NULL); + now = (uint32_t)get_time(); + if (result == SOCK_ERR_WOULDBLOCK) { + if ((idle_timeout_ms && now - timeout->last_progress_ms >= idle_timeout_ms) || (total_timeout_ms && now - timeout->start_ms >= total_timeout_ms)) return SOCK_ERR_WOULDBLOCK; + msleep(2); + continue; + } + if (result > 0) timeout->last_progress_ms = now; + return result; + } +} + +int32_t http_socket_recv_head(socket_handle_t socket, + string *buffer, + uint32_t max_header_bytes, + uint32_t idle_timeout_ms, + uint32_t total_timeout_ms, + int32_t *header_end) { + *header_end = find_crlfcrlf(buffer->data, buffer->length); + if (*header_end >= 0) return (uint32_t)*header_end + 4 <= max_header_bytes ? SOCK_OK : SOCK_ERR_PROTO; + if (buffer->length >= max_header_bytes) return SOCK_ERR_PROTO; + + uint32_t now = (uint32_t)get_time(); + HTTPSocketTimeoutState timeout = {now, now}; + char temporary[2048]; + + while (*header_end < 0) { + uint32_t available = max_header_bytes - buffer->length; + uint32_t request = available < sizeof(temporary) ? available : sizeof(temporary); + int64_t result = http_socket_recv_wait(socket, temporary, request, idle_timeout_ms, total_timeout_ms, &timeout); + if (result < 0) return (int32_t)result; + if (result == 0) return SOCK_ERR_PROTO; + + uint32_t expected = buffer->length + (uint32_t)result; + string_append_bytes(buffer, temporary, (uint32_t)result); + if (buffer->length != expected) return SOCK_ERR_SYS; + + *header_end = find_crlfcrlf(buffer->data, buffer->length); + if (*header_end >= 0) return (uint32_t)*header_end + 4 <= max_header_bytes ? SOCK_OK : SOCK_ERR_PROTO; + if (buffer->length >= max_header_bytes) return SOCK_ERR_PROTO; + } + + return SOCK_OK; +} + +int64_t http_socket_recv_exact(socket_handle_t socket, void *buffer, uint32_t length, uint32_t idle_timeout_ms, uint32_t total_timeout_ms) { + uint32_t now = (uint32_t)get_time(); + HTTPSocketTimeoutState timeout = {now, now}; + uint32_t received = 0; + + while (received < length) { + int64_t result = http_socket_recv_wait(socket, (uint8_t*)buffer + received, length - received, idle_timeout_ms, total_timeout_ms, &timeout); + if (result < 0) return result; + if (result == 0) return SOCK_ERR_PROTO; + if ((uint64_t)result > length - received) return SOCK_ERR_STATE; + received += (uint32_t)result; + } + + return received; +} + +int64_t http_socket_send_all(socket_handle_t socket, + const void *buffer, + uint32_t length, + uint32_t max_chunk, + uint32_t idle_timeout_ms, + uint32_t total_timeout_ms, + HTTPSocketTimeoutState *timeout) { + uint32_t sent = 0; + while (sent < length) { + uint32_t now = (uint32_t)get_time(); + if ((idle_timeout_ms && now - timeout->last_progress_ms >= idle_timeout_ms) || (total_timeout_ms && now - timeout->start_ms >= total_timeout_ms)) return SOCK_ERR_WOULDBLOCK; + + uint32_t request = length - sent; + if (max_chunk && request > max_chunk) request = max_chunk; + + int64_t result = send_on_socket(socket, (const uint8_t*)buffer + sent, request); + now = (uint32_t)get_time(); + if (result == SOCK_ERR_WOULDBLOCK || result == 0) { + if ((idle_timeout_ms && now - timeout->last_progress_ms >= idle_timeout_ms) || (total_timeout_ms && now - timeout->start_ms >= total_timeout_ms)) return SOCK_ERR_WOULDBLOCK; + msleep(2); + continue; + } + if (result < 0) return result; + if ((uint64_t)result > request) return SOCK_ERR_STATE; + + sent += (uint32_t)result; + timeout->last_progress_ms = now; + } + + return sent; +} + +void http_socket_fill_log_endpoints(socket_handle_t socket, netlog_socket_event_t *event) { + uint32_t local_port = 0; + uint32_t length = sizeof(local_port); + if (get_socket_option(socket, SOCK_GET_LOCAL_PORT, &local_port, &length) == SOCK_OK) event->local_port = local_port; + length = sizeof(event->remote_ep); + get_socket_option(socket, SOCK_GET_REMOTE_ENDPOINT, &event->remote_ep, &length); +} diff --git a/kernel/networking/application_layer/csocket_http_internal.h b/kernel/networking/application_layer/csocket_http_internal.h new file mode 100644 index 00000000..ef1c39f6 --- /dev/null +++ b/kernel/networking/application_layer/csocket_http_internal.h @@ -0,0 +1,44 @@ +#pragma once + +#include "networking/transport_layer/csocket.h" +#include "networking/net_logger/net_logger.h" +#include "std/string.h" + +#define HTTP_SOCKET_WRITE_IDLE_TIMEOUT_MS 3000 +#define HTTP_SOCKET_WRITE_TOTAL_TIMEOUT_MS 30000 + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct HTTPSocketTimeoutState { + uint32_t start_ms; + uint32_t last_progress_ms; +} HTTPSocketTimeoutState; + +int32_t http_socket_close(socket_handle_t *socket, bool abort); +int64_t http_socket_recv_wait(socket_handle_t socket, + void *buffer, + uint32_t length, + uint32_t idle_timeout_ms, + uint32_t total_timeout_ms, + HTTPSocketTimeoutState *timeout); +int32_t http_socket_recv_head(socket_handle_t socket, + string *buffer, + uint32_t max_header_bytes, + uint32_t idle_timeout_ms, + uint32_t total_timeout_ms, + int32_t *header_end); +int64_t http_socket_recv_exact(socket_handle_t socket, void *buffer, uint32_t length, uint32_t idle_timeout_ms, uint32_t total_timeout_ms); +int64_t http_socket_send_all(socket_handle_t socket, + const void *buffer, + uint32_t length, + uint32_t max_chunk, + uint32_t idle_timeout_ms, + uint32_t total_timeout_ms, + HTTPSocketTimeoutState *timeout); +void http_socket_fill_log_endpoints(socket_handle_t socket, netlog_socket_event_t *event); + +#ifdef __cplusplus +} +#endif diff --git a/kernel/networking/application_layer/csocket_http_server.c b/kernel/networking/application_layer/csocket_http_server.c index e7ad8f32..de6ef8f1 100644 --- a/kernel/networking/application_layer/csocket_http_server.c +++ b/kernel/networking/application_layer/csocket_http_server.c @@ -1,4 +1,5 @@ #include "csocket_http_server.h" +#include "csocket_http_internal.h" #include "console/kio.h" #include "networking/transport_layer/csocket.h" #include "networking/net_logger/net_logger.h" @@ -44,7 +45,7 @@ http_server_handle_t http_server_create(const SocketOptions* extra, const HTTPSe void http_server_destroy(http_server_handle_t h) { if (!h) return; HTTPServer* srv = (HTTPServer*)h; - http_server_close(srv); + if (srv->sock) (void)http_socket_close(&srv->sock, true); release(srv); } @@ -78,7 +79,7 @@ int32_t http_server_listen(http_server_handle_t h, int backlog) { if (!h) return (int32_t)SOCK_ERR_INVAL; HTTPServer* srv = (HTTPServer*)h; - int b = backlog; + int32_t b = backlog; int32_t r = srv->sock ? listen_on(srv->sock, b) : SOCK_ERR_STATE; netlog_socket_event_t ev = {0}; @@ -101,13 +102,13 @@ http_connection_handle_t http_server_accept(http_server_handle_t h) { //TODO for now almost everything is using SOCK_OPT_NONBLOCK for compatibilit //in many app protocols it should be blocking, while for others such as HTTP, it should depend on the given options (blocking by default) if (set_socket_option(child, SOCK_OPT_NONBLOCK,&(uint32_t){1}, sizeof(uint32_t)) != SOCK_OK) { - close_socket(child); + http_socket_close(&child, true); return NULL; } HTTPConnection* conn = (HTTPConnection*)zalloc(sizeof(HTTPConnection)); if (!conn) { - close_socket(child); + http_socket_close(&child, true); return NULL; } @@ -123,11 +124,7 @@ http_connection_handle_t http_server_accept(http_server_handle_t h) { ev.action = NETLOG_ACT_ACCEPT; ev.pid = get_current_proc_pid(); ev.i0 = (int64_t)child; - uint32_t local_port = 0; - uint32_t opt_len = sizeof(local_port); - if (get_socket_option(child, SOCK_GET_LOCAL_PORT, &local_port, &opt_len) == SOCK_OK) ev.local_port = local_port; - opt_len = sizeof(ev.remote_ep); - get_socket_option(child, SOCK_GET_REMOTE_ENDPOINT, &ev.remote_ep, &opt_len); + http_socket_fill_log_endpoints(child, &ev); netlog_socket_event(&srv->log_opts, &ev); return conn; } @@ -148,37 +145,27 @@ HTTPRequestMsg http_server_recv_request(http_server_handle_t h, http_connection_ } char tmp[2048]; - int hdr_end = find_crlfcrlf(buf.data, buf.length); + int32_t hdr_end = -1; uint32_t consumed = 0; - uint32_t start_ms = (uint32_t)get_time(); - uint32_t last_rx_ms = start_ms; bool bad_request = false; HttpError reject_status = HTTP_BAD_REQUEST; char* body_copy = NULL; - while (hdr_end < 0) { - int64_t r = receive_from_socket(conn->client, tmp, sizeof(tmp), NULL); - if (r == SOCK_ERR_WOULDBLOCK) { - uint32_t now = (uint32_t)get_time(); - if ((uint32_t)(now - last_rx_ms) > srv->policy.common.header_idle_timeout_ms || (uint32_t)(now - start_ms) > srv->policy.common.header_total_timeout_ms) { - string_free(buf); - return req; - } - msleep(10); - continue; - } - if (r <= 0) { - string_free(buf); - return req; - } - string_append_bytes(&buf, tmp, (uint32_t)r); - last_rx_ms = (uint32_t)get_time(); - if (buf.length > srv->policy.common.max_header_bytes) { + int32_t head_result = http_socket_recv_head(conn->client, &buf, srv->policy.common.max_header_bytes, srv->policy.common.header_idle_timeout_ms, srv->policy.common.header_total_timeout_ms, &hdr_end); + if (head_result < 0) { + bool header_too_large = (hdr_end >= 0 && (uint32_t)hdr_end + 4 > srv->policy.common.max_header_bytes) || (hdr_end < 0 && buf.length >= srv->policy.common.max_header_bytes); + if (header_too_large) { bad_request = true; reject_status = HTTP_HEADER_FIELDS_TOO_LARGE; - break; + } else { + string_free(buf); + return req; } - hdr_end = find_crlfcrlf(buf.data, buf.length); + } + + if (!bad_request && (!buf.data || hdr_end < 2)) { + bad_request = true; + reject_status = HTTP_BAD_REQUEST; } if (!bad_request) { @@ -200,6 +187,9 @@ HTTPRequestMsg http_server_recv_request(http_server_handle_t h, http_connection_ if (line_result != HTTP_PARSE_OK) { bad_request = true; reject_status = http_parse_result_status(line_result); + } else if (line.target_off > line_end || line.target_len > line_end - line.target_off) { + bad_request = true; + reject_status = HTTP_BAD_REQUEST; } else if (!http_method_allowed(srv->policy.allowed_methods, line.method)) { bad_request = true; reject_status = line.method == HTTP_METHOD_UNKNOWN ? HTTP_NOT_IMPLEMENTED : HTTP_METHOD_NOT_ALLOWED; @@ -295,21 +285,14 @@ HTTPRequestMsg http_server_recv_request(http_server_handle_t h, http_connection_ uint32_t used = 0; HTTPParseResult chunk_result = have ? http_chunked_decoder_feed(&dec, buf.data + body_start, have, &used) : HTTP_PARSE_INCOMPLETE; uint32_t body_start_ms = (uint32_t)get_time(); - uint32_t body_last_rx_ms = body_start_ms; + HTTPSocketTimeoutState body_timeout = {body_start_ms, body_start_ms}; consumed = body_start + used; while (chunk_result == HTTP_PARSE_INCOMPLETE) { - int64_t r = receive_from_socket(conn->client, tmp, sizeof(tmp), NULL); - if (r == SOCK_ERR_WOULDBLOCK) { - uint32_t now = (uint32_t)get_time(); - if ((now - body_last_rx_ms) > srv->policy.common.body_idle_timeout_ms || (now - body_start_ms) > srv->policy.common.body_total_timeout_ms) break; - msleep(2); - continue; - } + int64_t r = http_socket_recv_wait(conn->client, tmp, sizeof(tmp), srv->policy.common.body_idle_timeout_ms, srv->policy.common.body_total_timeout_ms, &body_timeout); if (r <= 0) break; used = 0; chunk_result = http_chunked_decoder_feed(&dec, tmp, (uint32_t)r, &used); - body_last_rx_ms = (uint32_t)get_time(); consumed = buf.length; if (chunk_result == HTTP_PARSE_OK && used < (uint32_t)r && !conn->carry_buf.length) { conn->carry_buf = (string){0}; @@ -343,25 +326,9 @@ HTTPRequestMsg http_server_recv_request(http_server_handle_t h, http_connection_ uint32_t copied = have < need ? have : need; if (copied) memcpy(body_copy, buf.data + body_start, copied); - uint32_t body_start_ms = (uint32_t)get_time(); - uint32_t body_last_rx_ms = body_start_ms; - while (copied < need) { - int64_t r = receive_from_socket(conn->client, body_copy + copied, need - copied, NULL); - if (r == SOCK_ERR_WOULDBLOCK) { - uint32_t now = (uint32_t)get_time(); - if ((now - body_last_rx_ms) > srv->policy.common.body_idle_timeout_ms || (now - body_start_ms) > srv->policy.common.body_total_timeout_ms) { - bad_request = true; - break; - } - msleep(2); - continue; - } - if (r <= 0) { - bad_request = true; - break; - } - copied += (uint32_t)r; - body_last_rx_ms = (uint32_t)get_time(); + if (copied < need) { + int64_t receive_result = http_socket_recv_exact(conn->client, body_copy + copied, need - copied, srv->policy.common.body_idle_timeout_ms, srv->policy.common.body_total_timeout_ms); + if (receive_result < 0) bad_request = true; } } } @@ -398,10 +365,7 @@ HTTPRequestMsg http_server_recv_request(http_server_handle_t h, http_connection_ conn->close_after_response = true; http_server_send_response(srv, conn, &res); - if (conn->client) { - close_socket(conn->client); - conn->client = 0; - } + if (conn->client) (void)http_socket_close(&conn->client, true); http_request_free(&req); http_headers_common_free(&res.headers_common); @@ -418,11 +382,7 @@ HTTPRequestMsg http_server_recv_request(http_server_handle_t h, http_connection_ ev.u0 = (uint32_t)req.method; ev.u1 = (uint32_t)req.path.length; ev.i0 = (int64_t)req.body.length; - uint32_t local_port = 0; - uint32_t opt_len = sizeof(local_port); - if (get_socket_option(conn->client, SOCK_GET_LOCAL_PORT, &local_port, &opt_len) == SOCK_OK) ev.local_port = local_port; - opt_len = sizeof(ev.remote_ep); - get_socket_option(conn->client, SOCK_GET_REMOTE_ENDPOINT, &ev.remote_ep, &opt_len); + http_socket_fill_log_endpoints(conn->client, &ev); char pathbuf[128]; if (req.path.length && req.path.data) { @@ -487,7 +447,7 @@ int32_t http_server_send_response(http_server_handle_t h, http_connection_handle uint32_t out_len = out.length + body_len; int64_t sent = 0; uint32_t start_ms = (uint32_t)get_time(); - uint32_t progress_ms = start_ms; + HTTPSocketTimeoutState send_timeout = {start_ms, start_ms}; const uint8_t* first_ptr = (const uint8_t*)out.data; uint32_t first_len = out.length; uint32_t first_body_len = 0; @@ -511,52 +471,14 @@ int32_t http_server_send_response(http_server_handle_t h, http_connection_handle } } - uint32_t off = 0; - while (sent >= 0 && off < first_len) { - int64_t r = send_on_socket(conn->client, (void*)(first_ptr + off), first_len - off); - uint32_t now = (uint32_t)get_time(); - - if (r == SOCK_ERR_WOULDBLOCK || r == 0) { - if ((now - progress_ms) > 3000 || (now - start_ms) > 30000) { - sent = SOCK_ERR_SYS; - break; - } - msleep(2); - continue; - } - if (r < 0) { - sent = r; - break; - } - off += (uint32_t)r; - progress_ms = now; - } + sent = http_socket_send_all(conn->client, first_ptr, first_len, 0, HTTP_SOCKET_WRITE_IDLE_TIMEOUT_MS, HTTP_SOCKET_WRITE_TOTAL_TIMEOUT_MS, &send_timeout); if (sent >= 0) body_off = first_body_len; - if (sent >= 0 && body_len) { + if (sent >= 0 && body_len > body_off) { const uint8_t* body = (const uint8_t*)res->body.data; - - while (body_off < body_len) { - uint32_t ask = body_len - body_off; - if (ask > 16384) ask = 16384; - int64_t r = send_on_socket(conn->client, (void*)(body + body_off), ask); - uint32_t now = (uint32_t)get_time(); - - if (r == SOCK_ERR_WOULDBLOCK || r == 0) { - if ((now - progress_ms) > 3000 || (now - start_ms) > 30000) { - sent = SOCK_ERR_SYS; - break; - } - msleep(2); - continue; - } - if (r < 0) { - sent = r; - break; - } - body_off += (uint32_t)r; - progress_ms = now; - } + int64_t body_sent = http_socket_send_all(conn->client, body + body_off, body_len - body_off, 16384, HTTP_SOCKET_WRITE_IDLE_TIMEOUT_MS, HTTP_SOCKET_WRITE_TOTAL_TIMEOUT_MS, &send_timeout); + if (body_sent < 0) sent = body_sent; + else body_off += (uint32_t)body_sent; } if (sent >= 0) sent = (int64_t)out.length + body_off; @@ -569,17 +491,13 @@ int32_t http_server_send_response(http_server_handle_t h, http_connection_handle ev.u0 = code; ev.u1 = out_len; ev.i0 = sent; - uint32_t local_port = 0; - uint32_t opt_len = sizeof(local_port); - if (get_socket_option(conn->client, SOCK_GET_LOCAL_PORT, &local_port, &opt_len) == SOCK_OK) ev.local_port = local_port; - opt_len = sizeof(ev.remote_ep); - get_socket_option(conn->client, SOCK_GET_REMOTE_ENDPOINT, &ev.remote_ep, &opt_len); + http_socket_fill_log_endpoints(conn->client, &ev); netlog_socket_event(&srv->log_opts, &ev); string_free(out); if (sent >= 0 && close_after_send && conn->client) { - close_socket(conn->client); - conn->client = 0; + int32_t close_result = http_socket_close(&conn->client, false); + if (close_result < 0) return close_result; } return sent < 0 ? (int32_t)sent : SOCK_OK; } @@ -587,10 +505,11 @@ int32_t http_server_send_response(http_server_handle_t h, http_connection_handle int32_t http_connection_close(http_connection_handle_t c) { if (!c) return (int32_t)SOCK_ERR_INVAL; HTTPConnection* conn = (HTTPConnection*)c; + int32_t result = conn->client ? http_socket_close(&conn->client, false) : SOCK_OK; + if (result < 0) return result; if (conn->carry_buf.mem_length) string_free(conn->carry_buf); - if (conn->client) close_socket(conn->client); release(conn); - return (int32_t)SOCK_OK; + return SOCK_OK; } int32_t http_server_close(http_server_handle_t h) { @@ -603,19 +522,9 @@ int32_t http_server_close(http_server_handle_t h) { ev.action = NETLOG_ACT_CLOSE; ev.pid = get_current_proc_pid(); ev.i0 = r; - if (srv->sock) { - uint32_t local_port = 0; - uint32_t opt_len = sizeof(local_port); - if (get_socket_option(srv->sock, SOCK_GET_LOCAL_PORT, &local_port, &opt_len) == SOCK_OK) ev.local_port = local_port; - opt_len = sizeof(ev.remote_ep); - get_socket_option(srv->sock, SOCK_GET_REMOTE_ENDPOINT, &ev.remote_ep, &opt_len); - } + if (srv->sock) http_socket_fill_log_endpoints(srv->sock, &ev); + if (srv->sock) r = http_socket_close(&srv->sock, false); + ev.i0 = r; netlog_socket_event(&srv->log_opts, &ev); - - if (srv->sock) { - r = close_socket(srv->sock); - srv->sock = 0; - } - return r; } diff --git a/kernel/networking/transport_layer/csocket_tcp.c b/kernel/networking/transport_layer/csocket_tcp.c index 756c5ca9..181c1af8 100644 --- a/kernel/networking/transport_layer/csocket_tcp.c +++ b/kernel/networking/transport_layer/csocket_tcp.c @@ -28,21 +28,22 @@ typedef struct tcp_socket { socket_bind_token_t bindToken; tcp_data flow; ksocket_t** pending; - int backlogCap; - int backlogLen; + int32_t backlogCap; + int32_t backlogLen; bool listening; + bool closed; } tcp_socket_t; -static int tcp_socket_backlog_len(tcp_socket_t* s) { +static int32_t tcp_socket_backlog_len(tcp_socket_t* s) { if (!s) return 0; irq_flags_t irq = irq_save_disable(); - int len = s->backlogLen; + int32_t len = s->backlogLen; irq_restore(irq); return len; } -static ksocket_t* tcp_socket_pop_pending_at(tcp_socket_t* s, int idx) { +static ksocket_t* tcp_socket_pop_pending_at(tcp_socket_t* s, int32_t idx) { if (!s) return NULL; irq_flags_t irq = irq_save_disable(); @@ -52,31 +53,44 @@ static ksocket_t* tcp_socket_pop_pending_at(tcp_socket_t* s, int idx) { } ksocket_t* client = s->pending[idx]; - for (int i = idx + 1; i < s->backlogLen; ++i) s->pending[i-1] = s->pending[i]; + for (int32_t i = idx + 1; i < s->backlogLen; ++i) s->pending[i-1] = s->pending[i]; s->pending[--s->backlogLen] = NULL; irq_restore(irq); return client; } +static void tcp_socket_reset_connection(tcp_socket_t* s, bool abort_flow) { + if (!s) return; + if (abort_flow && s->flow.flow_generation) tcp_flow_abort(&s->flow); + + s->connected = false; + memset(&s->flow, 0, sizeof(s->flow)); + s->remoteEP.port = 0; + s->remoteEP.ver = IP_VER4; + memset(s->remoteEP.ip, 0, sizeof(s->remoteEP.ip)); +} + +static void tcp_socket_abort_pending(ksocket_t* owner) { + if (!owner) return; + + tcp_socket_t* child = (tcp_socket_t*)socket_core_impl(owner); + if (child && child->flow.flow_generation) tcp_socket_reset_connection(child, true); + + socket_core_close_socket(owner); + socket_core_put(owner); +} + static int32_t tcp_socket_connection_state(tcp_socket_t* s) { if (!s) return -1; if (!s->flow.flow_generation) { - s->connected = false; - memset(&s->flow, 0, sizeof(s->flow)); - s->remoteEP.port = 0; - s->remoteEP.ver = IP_VER4; - memset(s->remoteEP.ip, 0, sizeof(s->remoteEP.ip)); + tcp_socket_reset_connection(s, false); return -1; } tcp_flow_t* flow = tcp_flow_from_ctx(&s->flow); if (!flow) { - s->connected = false; - memset(&s->flow, 0, sizeof(s->flow)); - s->remoteEP.port = 0; - s->remoteEP.ver = IP_VER4; - memset(s->remoteEP.ip, 0, sizeof(s->remoteEP.ip)); + tcp_socket_reset_connection(s, false); return -1; } @@ -90,11 +104,7 @@ static int32_t tcp_socket_connection_state(tcp_socket_t* s) { if (state == TCP_STATE_CLOSED || state == TCP_TIME_WAIT) { tcp_flow_release_closed(&s->flow); - s->connected = false; - memset(&s->flow, 0, sizeof(s->flow)); - s->remoteEP.port = 0; - s->remoteEP.ver = IP_VER4; - memset(s->remoteEP.ip, 0, sizeof(s->remoteEP.ip)); + tcp_socket_reset_connection(s, false); return -1; } @@ -118,6 +128,7 @@ socket_impl_t socket_tcp_create(ksocket_t* owner, const SocketOptions* extra) { s->ownerSocket = owner; s->remoteEP.ver = IP_VER4; + s->bindSpec.kind = BIND_ANY; if (extra) s->options = *extra; if (!(s->options.flags & SOCK_OPT_BUF_SIZE)) { s->options.flags |= SOCK_OPT_BUF_SIZE; @@ -125,10 +136,17 @@ socket_impl_t socket_tcp_create(ksocket_t* owner, const SocketOptions* extra) { } s->options.buf_size = tcp_clamp_rcvbuf(s->options.buf_size); - if (s->options.tcp_maxseg && (s->options.tcp_maxseg < 256u || s->options.tcp_maxseg > 65535u)) { + if (!(s->options.flags & SOCK_OPT_TCP_MAXSEG) || !s->options.tcp_maxseg) { + s->options.flags &= ~SOCK_OPT_TCP_MAXSEG; + s->options.tcp_maxseg = 0; + } else if (s->options.tcp_maxseg < 256u || s->options.tcp_maxseg > TCP_MAX_MSS) { release(s); return NULL; } + if (!(s->options.flags & SOCK_OPT_LINGER) || !s->options.linger.enabled) { + s->options.flags &= ~SOCK_OPT_LINGER; + memset(&s->options.linger, 0, sizeof(s->options.linger)); + } else s->options.linger.enabled = 1; return s; } @@ -145,7 +163,7 @@ int32_t socket_setopt_tcp(socket_impl_t sh, int32_t opt, const void* value, uint s->options.flags |= SOCK_OPT_KEEPALIVE; if (!s->options.keepalive_ms) s->options.keepalive_ms = SOCKET_DEFAULT_KEEPALIVE_MS; } else s->options.flags &= ~SOCK_OPT_KEEPALIVE; - if (s->flow.flow_generation) tcp_flow_apply_socket_options(&s->flow, &s->options); + if (s->flow.flow_generation) tcp_flow_apply_socket_options(&s->flow, &s->options, (uint32_t)opt); return SOCK_OK; } case SOCK_OPT_KEEPALIVE_INTERVAL: { @@ -155,7 +173,7 @@ int32_t socket_setopt_tcp(socket_impl_t sh, int32_t opt, const void* value, uint if (!v) return SOCK_ERR_INVAL; s->options.keepalive_ms = v; s->options.flags |= SOCK_OPT_KEEPALIVE | SOCK_OPT_KEEPALIVE_INTERVAL; - if (s->flow.flow_generation) tcp_flow_apply_socket_options(&s->flow, &s->options); + if (s->flow.flow_generation) tcp_flow_apply_socket_options(&s->flow, &s->options, (uint32_t)opt); return SOCK_OK; } case SOCK_OPT_TCP_NO_DELAY: { @@ -164,7 +182,7 @@ int32_t socket_setopt_tcp(socket_impl_t sh, int32_t opt, const void* value, uint memcpy(&v, value, sizeof(v)); if (v) s->options.flags |= SOCK_OPT_TCP_NO_DELAY; else s->options.flags &= ~SOCK_OPT_TCP_NO_DELAY; - if (s->flow.flow_generation) tcp_flow_apply_socket_options(&s->flow, &s->options); + if (s->flow.flow_generation) tcp_flow_apply_socket_options(&s->flow, &s->options, (uint32_t)opt); return SOCK_OK; } case SOCK_OPT_SEND_BUF_SIZE: { @@ -174,7 +192,7 @@ int32_t socket_setopt_tcp(socket_impl_t sh, int32_t opt, const void* value, uint if (!v) return SOCK_ERR_INVAL; s->options.send_buf_size = v; s->options.flags |= SOCK_OPT_SEND_BUF_SIZE; - if (s->flow.flow_generation) tcp_flow_apply_socket_options(&s->flow, &s->options); + if (s->flow.flow_generation) tcp_flow_apply_socket_options(&s->flow, &s->options, (uint32_t)opt); return SOCK_OK; } case SOCK_OPT_REUSEADDR: { @@ -191,22 +209,28 @@ int32_t socket_setopt_tcp(socket_impl_t sh, int32_t opt, const void* value, uint if (!value || len != sizeof(uint32_t)) return SOCK_ERR_INVAL; uint32_t v = 0; memcpy(&v, value, sizeof(v)); - if (v && (v < 256u || v > 65535u)) return SOCK_ERR_INVAL; + if (v && (v < 256u || v > TCP_MAX_MSS)) return SOCK_ERR_INVAL; s->options.tcp_maxseg = v; if (v) s->options.flags |= SOCK_OPT_TCP_MAXSEG; else s->options.flags &= ~SOCK_OPT_TCP_MAXSEG; - if (s->flow.flow_generation) tcp_flow_apply_socket_options(&s->flow, &s->options); + if (s->flow.flow_generation) tcp_flow_apply_socket_options(&s->flow, &s->options, (uint32_t)opt); return SOCK_OK; } case SOCK_OPT_LINGER: { if (!value || len != sizeof(SocketLinger)) return SOCK_ERR_INVAL; SocketLinger linger; memcpy(&linger, value, sizeof(linger)); - s->options.linger = linger; - if (linger.enabled) s->options.flags |= SOCK_OPT_LINGER; - else s->options.flags &= ~SOCK_OPT_LINGER; + if (linger.enabled) { + linger.enabled = 1; + s->options.linger = linger; + s->options.flags |= SOCK_OPT_LINGER; + } else { + memset(&s->options.linger, 0, sizeof(s->options.linger)); + s->options.flags &= ~SOCK_OPT_LINGER; + } return SOCK_OK; } + case SOCK_OPT_TCP_SACK: case SOCK_OPT_REUSEPORT: case SOCK_OPT_MCAST_JOIN: case SOCK_OPT_MCAST_LEAVE: @@ -226,11 +250,12 @@ int32_t socket_setopt_tcp(socket_impl_t sh, int32_t opt, const void* value, uint case SOCK_OPT_RECV_TIMEOUT: case SOCK_OPT_SEND_TIMEOUT: case SOCK_OPT_DEBUG: + return socket_common_options_set(&s->options, opt, value, len); case SOCK_OPT_DONTFRAG: case SOCK_OPT_TTL: { int32_t rc = socket_common_options_set(&s->options, opt, value, len); if (rc != SOCK_OK) return rc; - if (s->flow.flow_generation) tcp_flow_apply_socket_options(&s->flow, &s->options); + if (s->flow.flow_generation) tcp_flow_apply_socket_options(&s->flow, &s->options, (uint32_t)opt); return SOCK_OK; } default: @@ -320,7 +345,7 @@ int32_t socket_getopt_tcp(socket_impl_t sh, int32_t opt, void* value, uint32_t* case SOCK_GET_TCP_RETRANSMITS: { tcp_flow_t* flow = tcp_flow_from_ctx(&s->flow); if (flow) { - for (int i = 0; i < TCP_MAX_TX_SEGS; ++i) if (flow->tx.txq[i].used) v += flow->tx.txq[i].retransmit_cnt; + for (uint32_t i = 0; i < TCP_MAX_TX_SEGS; ++i) if (flow->tx.txq[i].used) v += flow->tx.txq[i].retransmit_cnt; tcp_flow_put(flow); } break; @@ -340,6 +365,7 @@ int32_t socket_getopt_tcp(socket_impl_t sh, int32_t opt, void* value, uint32_t* case SOCK_GET_OPT_TCP_MAXSEG: v = s->options.tcp_maxseg; break; + case SOCK_GET_OPT_TCP_SACK: case SOCK_GET_OPT_REUSEPORT: case SOCK_GET_MCAST_GROUPS: case SOCK_GET_OPT_BROADCAST_ALLOWED: @@ -378,29 +404,9 @@ int32_t socket_bind_tcp(socket_impl_t sh, const SockBindSpec* spec_in, uint16_t if (!s->ownerSocket) return SOCK_ERR_SYS; SockBindSpec spec = *spec_in; - if (!socket_bind_normalize_spec(&spec)) return SOCK_ERR_INVAL; - - if (spec.kind == BIND_L3) { - if (!spec.l3_id) return SOCK_ERR_INVAL; - l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(spec.l3_id); - l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(spec.l3_id); - bool ok4 = (spec.ver == 0 || spec.ver == IP_VER4) && ipv4_l3_is_ready(v4); - bool ok6 = (spec.ver == 0 || spec.ver == IP_VER6) && ipv6_l3_is_tcp_usable(v6); - if (!ok4 && !ok6) return SOCK_ERR_INVAL; - } else if (spec.kind == BIND_L2) { - l2_interface_t* l2 = l2_interface_find_by_index(spec.ifindex); - if (!l2 || !l2->is_up) return SOCK_ERR_INVAL; - } else if (spec.kind == BIND_IP) { - if (spec.ver == IP_VER4) { - uint32_t v4ip = 0; - memcpy(&v4ip, spec.ip, 4); - if (!ipv4_is_unspecified(v4ip) && !ipv4_l3_is_ready(l3_ipv4_find_by_ip(v4ip))) return SOCK_ERR_INVAL; - } else if (spec.ver == IP_VER6) { - if (!ipv6_is_unspecified(spec.ip) && !ipv6_l3_is_tcp_usable(l3_ipv6_find_by_ip(spec.ip))) return SOCK_ERR_INVAL; - } else return SOCK_ERR_INVAL; - } else if (spec.kind != BIND_ANY && spec.kind != BIND_ANY4 && spec.kind != BIND_ANY6) return SOCK_ERR_INVAL; + if (!socket_bind_prepare_spec(&spec, PROTO_TCP)) return SOCK_ERR_INVAL; - int bind_port = port; + int32_t bind_port = port; socket_bind_token_t token = 0; if (bind_port == 0) { bind_port = socket_bind_alloc_ephemeral(s->ownerSocket, PROTO_TCP, &spec, s->options.flags & SOCK_OPT_REUSEADDR, &token); @@ -420,17 +426,19 @@ int32_t socket_listen_tcp(socket_impl_t sh, int32_t backlog) { int32_t cap = backlog > TCP_MAX_BACKLOG ? TCP_MAX_BACKLOG : backlog; if (cap < 1) cap = 1; + if (s->listening && s->pending && s->backlogCap == cap) return SOCK_OK; ksocket_t** pending = (ksocket_t**)zalloc(sizeof(ksocket_t*)*cap); if (!pending) return SOCK_ERR_SYS; irq_flags_t irq = irq_save_disable(); - int32_t keep = s->backlogLen < cap ? s->backlogLen : cap; - for (int32_t i = 0; i < keep; ++i) pending[i] = s->pending[i]; ksocket_t** old_pending = s->pending; int32_t old_cap = s->backlogCap; int32_t old_len = s->backlogLen; bool old_listening = s->listening; + int32_t valid_old_len = old_pending ? old_len : 0; + int32_t keep = valid_old_len < cap ? valid_old_len : cap; + for (int32_t i = 0; i < keep; ++i) pending[i] = old_pending[i]; s->pending = pending; s->backlogCap = cap; s->backlogLen = keep; @@ -448,10 +456,9 @@ int32_t socket_listen_tcp(socket_impl_t sh, int32_t backlog) { return SOCK_ERR_BOUND; } - for (int32_t i = keep; i < old_len; ++i) { + for (int32_t i = keep; i < valid_old_len; ++i) { if (!old_pending[i]) continue; - socket_core_close_socket(old_pending[i]); - socket_core_put(old_pending[i]); + tcp_socket_abort_pending(old_pending[i]); } if (old_pending) release(old_pending); return SOCK_OK; @@ -479,8 +486,7 @@ ksocket_t* socket_accept_tcp(socket_impl_t sh) { if (client && client->flow.flow_generation) return owner; if (owner) { - socket_core_close_socket(owner); - socket_core_put(owner); + tcp_socket_abort_pending(owner); } } @@ -537,34 +543,16 @@ int32_t socket_connect_tcp(socket_impl_t sh, const net_l4_endpoint* dst) { if (s->localPort == 0) { if (!s->ownerSocket) return SOCK_ERR_SYS; socket_bind_token_t token = 0; - int p = socket_bind_alloc_ephemeral_l3(s->ownerSocket, PROTO_TCP, chosen_l3, s->options.flags & SOCK_OPT_REUSEADDR, &token); + int32_t p = socket_bind_alloc_ephemeral_l3(s->ownerSocket, PROTO_TCP, chosen_l3, s->options.flags & SOCK_OPT_REUSEADDR, &s->bindSpec, &token); if (p < 0) return SOCK_ERR_NO_PORT; s->localPort = (uint16_t)p; s->bindToken = token; - memset(&s->bindSpec, 0, sizeof(s->bindSpec)); - s->bindSpec.kind = BIND_L3; - s->bindSpec.ver = d.ver; - s->bindSpec.l3_id = chosen_l3; ephemeral_allocated = true; } memset(&s->flow, 0, sizeof(s->flow)); - bool ok = tcp_handshake_l3(chosen_l3, s->localPort, &d, &s->flow, &s->options); - if (ok && d.ver == IP_VER4) { - l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(chosen_l3); - ok = ipv4_l3_is_ready(v4); - } else if (ok && d.ver == IP_VER6) ok = ipv6_l3_is_tcp_usable(l3_ipv6_find_by_id(chosen_l3)); - - if (!ok) { - if (s->flow.flow_generation) { - tcp_flow_abort(&s->flow); - memset(&s->flow, 0, sizeof(s->flow)); - } - - s->connected = false; - s->remoteEP.port = 0; - s->remoteEP.ver = IP_VER4; - memset(s->remoteEP.ip, 0, sizeof(s->remoteEP.ip)); + if (!tcp_handshake_l3(chosen_l3, s->localPort, &d, &s->flow, &s->options)) { + tcp_socket_reset_connection(s, true); if (ephemeral_allocated) { if (s->bindToken) { @@ -573,6 +561,7 @@ int32_t socket_connect_tcp(socket_impl_t sh, const net_l4_endpoint* dst) { } s->localPort = 0; memset(&s->bindSpec, 0, sizeof(s->bindSpec)); + s->bindSpec.kind = BIND_ANY; } return SOCK_ERR_SYS; } @@ -591,12 +580,7 @@ int32_t socket_connect_tcp(socket_impl_t sh, const net_l4_endpoint* dst) { uint32_t now_ms = get_time(); if (now_ms - start_ms >= timeout_ms) { - if (s->flow.flow_generation) tcp_flow_abort(&s->flow); - s->connected = false; - memset(&s->flow, 0, sizeof(s->flow)); - s->remoteEP.port = 0; - s->remoteEP.ver = IP_VER4; - memset(s->remoteEP.ip, 0, sizeof(s->remoteEP.ip)); + tcp_socket_reset_connection(s, true); return SOCK_ERR_WOULDBLOCK; } msleep(5); @@ -627,7 +611,7 @@ int64_t socket_send_tcp(socket_impl_t sh, const void* buf, uint64_t len) { ev.remote_ep = s->remoteEP; netlog_socket_event(&s->options, &ev); - int connection_state = tcp_socket_connection_state(s); + int32_t connection_state = tcp_socket_connection_state(s); if (connection_state <= 0 || !s->flow.flow_generation) return connection_state == 0 ? SOCK_ERR_WOULDBLOCK : SOCK_ERR_STATE; if (!buf && len) return SOCK_ERR_INVAL; if (!len) return 0; @@ -685,7 +669,7 @@ int64_t socket_recv_tcp(socket_impl_t sh, void* buf, uint64_t len) { netlog_socket_event(&s->options, &ev); if (!buf || !len) return 0; - int connection_state = tcp_socket_connection_state(s); + int32_t connection_state = tcp_socket_connection_state(s); if (connection_state < 0) return 0; if (connection_state == 0 || !s->flow.flow_generation) return SOCK_ERR_WOULDBLOCK; @@ -718,6 +702,7 @@ int64_t socket_recv_tcp(socket_impl_t sh, void* buf, uint64_t len) { int32_t socket_close_tcp(socket_impl_t sh) { tcp_socket_t* s = (tcp_socket_t*)sh; if (!s) return SOCK_ERR_INVAL; + if (s->closed) return SOCK_OK; netlog_socket_event_t ev = {0}; ev.comp = NETLOG_COMP_TCP; @@ -757,10 +742,9 @@ int32_t socket_close_tcp(socket_impl_t sh) { } if (s->pending) { - for (int i = 0; i < s->backlogLen; ++i) { + for (int32_t i = 0; i < s->backlogLen; ++i) { if (!s->pending[i]) continue; - socket_core_close_socket(s->pending[i]); - socket_core_put(s->pending[i]); + tcp_socket_abort_pending(s->pending[i]); s->pending[i] = NULL; } release(s->pending); @@ -776,11 +760,9 @@ int32_t socket_close_tcp(socket_impl_t sh) { } s->localPort = 0; memset(&s->bindSpec, 0, sizeof(s->bindSpec)); - s->connected = false; - memset(&s->flow, 0, sizeof(s->flow)); - s->remoteEP.port = 0; - s->remoteEP.ver = IP_VER4; - memset(s->remoteEP.ip, 0, sizeof(s->remoteEP.ip)); + s->bindSpec.kind = BIND_ANY; + tcp_socket_reset_connection(s, false); + s->closed = true; return SOCK_OK; } @@ -803,7 +785,7 @@ uint32_t tcp_accept_enqueue(ksocket_t* listener, ip_version_t ipver, const void* if (!s) return 0; if (!s->listening || !s->localPort || s->localPort != dst_port || !s->pending) return 0; - for (int i = 0;;) { + for (int32_t i = 0;;) { irq_flags_t irq = irq_save_disable(); ksocket_t* owner = (i >= 0 && i < s->backlogLen) ? s->pending[i] : NULL; irq_restore(irq); @@ -821,10 +803,7 @@ uint32_t tcp_accept_enqueue(ksocket_t* listener, ip_version_t ipver, const void* } owner = tcp_socket_pop_pending_at(s, i); - if (owner) { - socket_core_close_socket(owner); - socket_core_put(owner); - } + if (owner) tcp_socket_abort_pending(owner); } if (tcp_socket_backlog_len(s) >= s->backlogCap) return 0; @@ -858,6 +837,7 @@ uint32_t tcp_accept_enqueue(ksocket_t* listener, ip_version_t ipver, const void* } if (!socket_core_attach_impl(child_owner, child, socket_destroy_tcp, socket_close_tcp, socket_setopt_tcp, socket_getopt_tcp)) { + tcp_socket_reset_connection(child, true); socket_destroy_tcp(child); socket_core_close_socket(child_owner); return 0; @@ -869,8 +849,7 @@ uint32_t tcp_accept_enqueue(ksocket_t* listener, ip_version_t ipver, const void* irq_flags_t irq = irq_save_disable(); if (s->backlogLen >= s->backlogCap) { irq_restore(irq); - socket_core_close_socket(child_owner); - socket_core_put(child_owner); + tcp_socket_abort_pending(child_owner); return 0; } s->pending[s->backlogLen++] = child_owner; diff --git a/kernel/networking/transport_layer/csocket_udp.c b/kernel/networking/transport_layer/csocket_udp.c index 9af2ea96..d81140c5 100644 --- a/kernel/networking/transport_layer/csocket_udp.c +++ b/kernel/networking/transport_layer/csocket_udp.c @@ -14,7 +14,7 @@ #include "syscalls/syscalls.h" #include "std/memory.h" #include "alloc/allocate.h" - +//TODO manage multicast membership join and leave with udp rx before freeing group storage #define UDP_DEFAULT_RING_CAP 64 #define UDP_MAX_RING_CAP 1024 @@ -34,12 +34,12 @@ typedef struct udp_socket { SockBindSpec lastRxSpec; socket_bind_token_t bindToken; udp_rx_entry_t* rx_ring; - net_l4_endpoint* mcast_groups; - uint8_t mcast_count; + uint16_t* mcast_ifmasks; uint32_t ring_cap; uint32_t r_head; uint32_t r_tail; uint32_t rx_bytes; + bool closed; } udp_socket_t; static bool udp_socket_mcast_endpoint_valid(const net_l4_endpoint* ep) { @@ -55,10 +55,10 @@ static bool udp_socket_mcast_endpoint_valid(const net_l4_endpoint* ep) { static bool udp_socket_mcast_match(udp_socket_t* s, ip_version_t ver, const void* dst_ip_addr) { if (!s || !dst_ip_addr) return false; - if (!s->mcast_groups || !s->mcast_count) return false; + if (!s->options.mcast_groups || !s->options.mcast_count) return false; - for (uint32_t i = 0; i < s->mcast_count; ++i) { - const net_l4_endpoint* group = &s->mcast_groups[i]; + for (uint32_t i = 0; i < s->options.mcast_count; ++i) { + const net_l4_endpoint* group = &s->options.mcast_groups[i]; if (group->ver != ver) continue; if (ver == IP_VER4) { @@ -75,77 +75,116 @@ static bool udp_socket_mcast_match(udp_socket_t* s, ip_version_t ver, const void return false; } -static void udp_socket_leave_mcast_groups(udp_socket_t* s) { - if (!s || !s->mcast_groups || !s->mcast_count || !s->localPort) return; +static int32_t udp_socket_apply_mcast_group(const SockBindSpec* spec, const net_l4_endpoint* group, bool joining, uint16_t* ifmask) { + if (!spec || !group || !ifmask) return SOCK_ERR_INVAL; + + uint32_t v4_group = 0; + if (group->ver == IP_VER4) { + memcpy(&v4_group, group->ip, 4); + if (!ipv4_is_multicast(v4_group)) return SOCK_ERR_INVAL; + } else if (group->ver == IP_VER6) { + if (!ipv6_is_multicast(group->ip)) return SOCK_ERR_INVAL; + } else return SOCK_ERR_INVAL; + + if (!joining) { + uint16_t mask = *ifmask; + for (uint8_t ifindex = 1; ifindex <= MAX_L2_INTERFACES; ++ifindex) { + if (!(mask & (uint16_t)(1u << (ifindex - 1)))) continue; + if (group->ver == IP_VER4) l2_ipv4_mcast_leave(ifindex, v4_group); + else l2_ipv6_mcast_leave(ifindex, group->ip); + } + *ifmask = 0; + return SOCK_OK; + } - uint8_t ids[MAX_L3_INTERFACES]; - for (uint32_t gidx = 0; gidx < s->mcast_count; ++gidx) { - const net_l4_endpoint* group = &s->mcast_groups[gidx]; + uint8_t l3_ids[MAX_L3_INTERFACES]; + bool linkscope = group->ver == IP_VER6 && ipv6_is_linkscope_mcast(group->ip); + uint32_t l3_count = socket_bind_select_l3(spec, group->ver, l3_ids, MAX_L3_INTERFACES); + uint16_t targets = 0; + *ifmask = 0; + for (uint32_t i = 0; i < l3_count; ++i) { + l2_interface_t* l2 = NULL; if (group->ver == IP_VER4) { - uint32_t g = 0; - memcpy(&g, group->ip, 4); - if (!ipv4_is_multicast(g)) continue; + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(l3_ids[i]); + if (!v4 || !v4->l2 || !ipv4_l3_is_active(v4)) continue; + l2 = v4->l2; + } else { + l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(l3_ids[i]); + if (!v6 || !v6->l2 || !ipv6_l3_is_ready(v6)) continue; + if (linkscope && !ipv6_is_linklocal(v6->ip)) continue; + l2 = v6->l2; + } - uint32_t n = socket_bind_select_l3(&s->bindSpec, IP_VER4, ids, MAX_IPV4_L3_INTERFACES); - for (uint32_t i = 0; i < n; ++i) { - l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(ids[i]); - if (!ipv4_l3_is_active(v4)) continue; - l2_ipv4_mcast_leave(v4->l2->ifindex, g); - } - } else if (group->ver == IP_VER6) { - if (!ipv6_is_multicast(group->ip)) continue; - bool linkscope = ipv6_is_linkscope_mcast(group->ip); + if (!l2->ifindex || l2->ifindex > MAX_L2_INTERFACES) continue; + targets |= (uint16_t)(1u << (l2->ifindex - 1)); + } - uint32_t n = socket_bind_select_l3(&s->bindSpec, IP_VER6, ids, MAX_IPV6_L3_INTERFACES); - for (uint32_t i = 0; i < n; ++i) { - l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(ids[i]); - if (!ipv6_l3_is_ready(v6)) continue; - if (linkscope && !ipv6_is_linklocal(v6->ip)) continue; - l2_ipv6_mcast_leave(v6->l2->ifindex, group->ip); - } + if (!targets) return SOCK_ERR_SYS; + for (uint8_t ifindex = 1; ifindex <= MAX_L2_INTERFACES; ++ifindex) { + uint16_t bit = (uint16_t)(1u << (ifindex - 1)); + if (!(targets & bit)) continue; + + bool ok = group->ver == IP_VER4 ? l2_ipv4_mcast_join(ifindex, v4_group) : l2_ipv6_mcast_join(ifindex, group->ip); + if (ok) { + *ifmask |= bit; + continue; + } + + uint16_t joined = *ifmask; + for (uint8_t joined_ifindex = 1; joined_ifindex <= MAX_L2_INTERFACES; ++joined_ifindex) { + if (!(joined & (uint16_t)(1u << (joined_ifindex - 1)))) continue; + if (group->ver == IP_VER4) l2_ipv4_mcast_leave(joined_ifindex, v4_group); + else l2_ipv6_mcast_leave(joined_ifindex, group->ip); } + *ifmask = 0; + return SOCK_ERR_SYS; } + + return SOCK_OK; } static int32_t udp_socket_join_mcast_groups(udp_socket_t* s) { - if (!s || !s->mcast_groups || !s->mcast_count) return SOCK_OK; - if (!s->localPort) return SOCK_OK; + if (!s || !s->options.mcast_groups || !s->options.mcast_count || !s->localPort) return SOCK_OK; + if (!s->mcast_ifmasks) return SOCK_ERR_SYS; - uint8_t ids[MAX_L3_INTERFACES]; - for (uint32_t gidx = 0; gidx < s->mcast_count; ++gidx) { - const net_l4_endpoint* group = &s->mcast_groups[gidx]; - uint32_t n = 0; - bool joined = false; + for (uint32_t i = 0; i < s->options.mcast_count; ++i) { + int32_t rc = udp_socket_apply_mcast_group(&s->bindSpec, &s->options.mcast_groups[i], true, &s->mcast_ifmasks[i]); + if (rc == SOCK_OK) continue; - if (group->ver == IP_VER4) { - uint32_t g = 0; - memcpy(&g, group->ip, 4); - if (!ipv4_is_multicast(g)) return SOCK_ERR_INVAL; - n = socket_bind_select_l3(&s->bindSpec, IP_VER4, ids, MAX_IPV4_L3_INTERFACES); - for (uint32_t i = 0; i < n; ++i) { - l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(ids[i]); - if (!ipv4_l3_is_active(v4)) continue; - if (l2_ipv4_mcast_join(v4->l2->ifindex, g)) joined = true; - } - } else if (group->ver == IP_VER6) { - if (!ipv6_is_multicast(group->ip)) return SOCK_ERR_INVAL; - bool linkscope = ipv6_is_linkscope_mcast(group->ip); - n = socket_bind_select_l3(&s->bindSpec, IP_VER6, ids, MAX_IPV6_L3_INTERFACES); - for (uint32_t i = 0; i < n; ++i) { - l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(ids[i]); - if (!ipv6_l3_is_ready(v6)) continue; - if (linkscope && !ipv6_is_linklocal(v6->ip)) continue; - if (l2_ipv6_mcast_join(v6->l2->ifindex, group->ip)) joined = true; - } - } else return SOCK_ERR_INVAL; - - if (!joined) return SOCK_ERR_SYS; + while (i > 0) { + --i; + (void)udp_socket_apply_mcast_group(&s->bindSpec, &s->options.mcast_groups[i], false, &s->mcast_ifmasks[i]); + } + return rc; } return SOCK_OK; } +static int32_t udp_socket_bind_l3(udp_socket_t* s, uint8_t l3_id) { + if (!s || !s->ownerSocket || !l3_id) return SOCK_ERR_INVAL; + if (s->localPort) return SOCK_OK; + + socket_bind_token_t token = 0; + int32_t port = socket_bind_alloc_ephemeral_l3(s->ownerSocket, PROTO_UDP, l3_id, s->options.flags & (SOCK_OPT_REUSEADDR | SOCK_OPT_REUSEPORT), &s->bindSpec, &token); + if (port < 0) return SOCK_ERR_NO_PORT; + + s->localPort = (uint16_t)port; + s->bindToken = token; + if (s->connected) socket_bind_udp_set_remote(token, &s->remoteEP); + + int32_t rc = udp_socket_join_mcast_groups(s); + if (rc == SOCK_OK) return SOCK_OK; + + socket_bind_remove(token); + s->localPort = 0; + s->bindToken = 0; + memset(&s->bindSpec, 0, sizeof(s->bindSpec)); + s->bindSpec.kind = BIND_ANY; + return rc; +} + uint32_t socket_udp_input(ksocket_t* socket, ip_version_t ipver, uint8_t l3_id, const void* src_ip_addr, const void* dst_ip_addr, netpkt_t* pkt, uint16_t src_port, uint16_t dst_port) { if (!socket || !pkt) return 0; udp_socket_t* s = (udp_socket_t*)socket_core_impl(socket); @@ -220,7 +259,7 @@ uint32_t socket_udp_input(ksocket_t* socket, ip_version_t ipver, uint8_t l3_id, socket_impl_t udp_socket_create(ksocket_t* owner, const SocketOptions* extra) { if (!owner) return NULL; - uint32_t supported = SOCK_OPT_RECV_TIMEOUT | SOCK_OPT_SEND_TIMEOUT | SOCK_OPT_BUF_SIZE | SOCK_OPT_DEBUG | SOCK_OPT_DONTFRAG | SOCK_OPT_BROADCAST_ALLOWED | SOCK_OPT_TTL | SOCK_OPT_MCAST_JOIN | SOCK_OPT_NONBLOCK | SOCK_OPT_DONTROUTE | SOCK_OPT_REUSEADDR | SOCK_OPT_REUSEPORT; + uint32_t supported = SOCK_OPT_RECV_TIMEOUT | SOCK_OPT_BUF_SIZE | SOCK_OPT_DEBUG | SOCK_OPT_DONTFRAG | SOCK_OPT_BROADCAST_ALLOWED | SOCK_OPT_TTL | SOCK_OPT_MCAST_JOIN | SOCK_OPT_NONBLOCK | SOCK_OPT_DONTROUTE | SOCK_OPT_REUSEADDR | SOCK_OPT_REUSEPORT; if (extra) { if (extra->flags & ~supported) return NULL; if ((extra->flags & SOCK_OPT_DEBUG) && extra->debug_level > SOCK_DBG_ALL) return NULL; @@ -231,6 +270,7 @@ socket_impl_t udp_socket_create(ksocket_t* owner, const SocketOptions* extra) { if (!s) return NULL; s->ownerSocket = owner; s->remoteEP.ver = IP_VER4; + s->bindSpec.kind = BIND_ANY; s->lastRxSpec.kind = BIND_ANY; if (extra) s->options = *extra; s->options.flags &= ~(SOCK_OPT_MCAST_JOIN | SOCK_OPT_MCAST_LEAVE); @@ -263,34 +303,9 @@ int32_t socket_bind_udp(socket_impl_t sh, const SockBindSpec* spec_in, uint16_t if (!s->ownerSocket) return SOCK_ERR_SYS; SockBindSpec spec = *spec_in; - if (!socket_bind_normalize_spec(&spec)) return SOCK_ERR_INVAL; - - if (spec.kind == BIND_L3) { - if (!spec.l3_id) return SOCK_ERR_INVAL; - uint8_t one[1]; - if (spec.ver == IP_VER4 && !socket_bind_select_l3(&spec, IP_VER4, one, 1)) return SOCK_ERR_INVAL; - if (spec.ver == IP_VER6 && !socket_bind_select_l3(&spec, IP_VER6, one, 1)) return SOCK_ERR_INVAL; - if (!spec.ver && !socket_bind_select_l3(&spec, IP_VER4, one, 1) && !socket_bind_select_l3(&spec, IP_VER6, one, 1)) return SOCK_ERR_INVAL; - } else if (spec.kind == BIND_L2) { - l2_interface_t* l2 = l2_interface_find_by_index(spec.ifindex); - if (!l2 || !l2->is_up) return SOCK_ERR_INVAL; - } else if (spec.kind == BIND_IP) { - if (spec.ver == IP_VER4) { - uint32_t v4ip = 0; - memcpy(&v4ip, spec.ip, 4); - if (!ipv4_is_unspecified(v4ip)) { - uint8_t one[1]; - if (!socket_bind_select_l3(&spec, IP_VER4, one, 1)) return SOCK_ERR_INVAL; - } - } else if (spec.ver == IP_VER6) { - if (!ipv6_is_unspecified(spec.ip)) { - uint8_t one[1]; - if (!socket_bind_select_l3(&spec, IP_VER6, one, 1)) return SOCK_ERR_INVAL; - } - } else return SOCK_ERR_INVAL; - } else if (spec.kind != BIND_ANY && spec.kind != BIND_ANY4 && spec.kind != BIND_ANY6) return SOCK_ERR_INVAL; + if (!socket_bind_prepare_spec(&spec, PROTO_UDP)) return SOCK_ERR_INVAL; - int bind_port = port; + int32_t bind_port = port; socket_bind_token_t token = 0; if (bind_port == 0) { bind_port = socket_bind_alloc_ephemeral(s->ownerSocket, PROTO_UDP, &spec, s->options.flags & (SOCK_OPT_REUSEADDR | SOCK_OPT_REUSEPORT), &token); @@ -304,10 +319,10 @@ int32_t socket_bind_udp(socket_impl_t sh, const SockBindSpec* spec_in, uint16_t int32_t mcast_res = udp_socket_join_mcast_groups(s); if (mcast_res != SOCK_OK) { - udp_socket_leave_mcast_groups(s); socket_bind_remove(s->bindToken); s->localPort = 0; memset(&s->bindSpec, 0, sizeof(s->bindSpec)); + s->bindSpec.kind = BIND_ANY; s->bindToken = 0; return mcast_res; } @@ -368,10 +383,12 @@ int64_t socket_sendto_udp(socket_impl_t sh, const net_l4_endpoint* dst, const vo dst = &s->remoteEP; } if (!dst->port) return SOCK_ERR_INVAL; - if (!buf && len) return SOCK_ERR_INVAL; if (!s->ownerSocket) return SOCK_ERR_SYS; net_l4_endpoint d = *dst; + if (d.ver != IP_VER4 && d.ver != IP_VER6) return SOCK_ERR_INVAL; + uint64_t max_payload = d.ver == IP_VER4 ? UINT16_MAX - sizeof(ipv4_hdr_t) - sizeof(udp_hdr_t) : UINT16_MAX - sizeof(udp_hdr_t); + if (len > max_payload) return SOCK_ERR_INVAL; if (s->connected && explicit_dst) { if (d.ver != s->remoteEP.ver || d.port != s->remoteEP.port) return SOCK_ERR_STATE; if (d.ver == IP_VER4 && memcmp(d.ip, s->remoteEP.ip, 4) != 0) return SOCK_ERR_STATE; @@ -381,6 +398,8 @@ int64_t socket_sendto_udp(socket_impl_t sh, const net_l4_endpoint* dst, const vo sizedptr pay; pay.ptr = (uintptr_t)buf; pay.size = (uint32_t)len; + uint8_t ttl = (s->options.flags & SOCK_OPT_TTL) ? s->options.ttl : 0; + uint8_t dontfrag = (s->options.flags & SOCK_OPT_DONTFRAG) ? 1 : 0; if (d.ver == IP_VER4) { uint32_t dip = 0; @@ -421,18 +440,8 @@ int64_t socket_sendto_udp(socket_impl_t sh, const net_l4_endpoint* dst, const vo } if (bcast_v4) { - if (!s->localPort) { - socket_bind_token_t token = 0; - int p = socket_bind_alloc_ephemeral_l3(s->ownerSocket, PROTO_UDP, chosen_l3, s->options.flags & (SOCK_OPT_REUSEADDR | SOCK_OPT_REUSEPORT), &token); - if (p < 0) return SOCK_ERR_NO_PORT; - s->localPort = (uint16_t)p; - s->bindToken = token; - memset(&s->bindSpec, 0, sizeof(s->bindSpec)); - s->bindSpec.kind = BIND_L3; - s->bindSpec.ver = IP_VER4; - s->bindSpec.l3_id = chosen_l3; - if (s->connected) socket_bind_udp_set_remote(s->bindToken, &s->remoteEP); - } + int32_t bind_rc = udp_socket_bind_l3(s, chosen_l3); + if (bind_rc != SOCK_OK) return bind_rc; net_l4_endpoint src; make_ep(&bcast_v4->ip, s->localPort, IP_VER4, &src); @@ -441,7 +450,7 @@ int64_t socket_sendto_udp(socket_impl_t sh, const net_l4_endpoint* dst, const vo tx.scope = IP_TX_BOUND_L3; tx.index = chosen_l3; - udp_send_segment(&src, &d, pay, &tx, (s->options.flags & SOCK_OPT_TTL) ? s->options.ttl : 0, (s->options.flags & SOCK_OPT_DONTFRAG) ? 1 : 0); + if (!udp_send_segment(&src, &d, pay, &tx, ttl, dontfrag)) return SOCK_ERR_SYS; return (int64_t)len; } @@ -453,18 +462,8 @@ int64_t socket_sendto_udp(socket_impl_t sh, const net_l4_endpoint* dst, const vo l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(tx_l3); if (!ipv4_l3_is_ready(v4)) return SOCK_ERR_NO_ROUTE; - if (!s->localPort) { - socket_bind_token_t token = 0; - int p = socket_bind_alloc_ephemeral_l3(s->ownerSocket, PROTO_UDP, tx_l3, s->options.flags & (SOCK_OPT_REUSEADDR | SOCK_OPT_REUSEPORT), &token); - if (p < 0) return SOCK_ERR_NO_PORT; - s->localPort = (uint16_t)p; - s->bindToken = token; - memset(&s->bindSpec, 0, sizeof(s->bindSpec)); - s->bindSpec.kind = BIND_L3; - s->bindSpec.ver = IP_VER4; - s->bindSpec.l3_id = tx_l3; - if (s->connected) socket_bind_udp_set_remote(s->bindToken, &s->remoteEP); - } + int32_t bind_rc = udp_socket_bind_l3(s, tx_l3); + if (bind_rc != SOCK_OK) return bind_rc; net_l4_endpoint src; make_ep(&v4->ip, s->localPort, IP_VER4, &src); @@ -473,7 +472,7 @@ int64_t socket_sendto_udp(socket_impl_t sh, const net_l4_endpoint* dst, const vo tx.scope = IP_TX_BOUND_L3; tx.index = plan.l3_id; - udp_send_segment(&src, &d, pay, &tx, (s->options.flags & SOCK_OPT_TTL) ? s->options.ttl : 0, (s->options.flags & SOCK_OPT_DONTFRAG) ? 1 : 0); + if (!udp_send_segment(&src, &d, pay, &tx, ttl, dontfrag)) return SOCK_ERR_SYS; return (int64_t)len; } @@ -486,18 +485,8 @@ int64_t socket_sendto_udp(socket_impl_t sh, const net_l4_endpoint* dst, const vo l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(chosen_l3); if (!ipv6_l3_is_ready(v6)) return SOCK_ERR_NO_ROUTE; - if (!s->localPort) { - socket_bind_token_t token = 0; - int p = socket_bind_alloc_ephemeral_l3(s->ownerSocket, PROTO_UDP, chosen_l3, s->options.flags & (SOCK_OPT_REUSEADDR | SOCK_OPT_REUSEPORT), &token); - if (p < 0) return SOCK_ERR_NO_PORT; - s->localPort = (uint16_t)p; - s->bindToken = token; - memset(&s->bindSpec, 0, sizeof(s->bindSpec)); - s->bindSpec.kind = BIND_L3; - s->bindSpec.ver = IP_VER6; - s->bindSpec.l3_id = chosen_l3; - if (s->connected) socket_bind_udp_set_remote(s->bindToken, &s->remoteEP); - } + int32_t bind_rc = udp_socket_bind_l3(s, chosen_l3); + if (bind_rc != SOCK_OK) return bind_rc; net_l4_endpoint src; make_ep(v6->ip, s->localPort, IP_VER6, &src); @@ -506,7 +495,7 @@ int64_t socket_sendto_udp(socket_impl_t sh, const net_l4_endpoint* dst, const vo tx.scope = IP_TX_BOUND_L3; tx.index = plan.l3_id; - udp_send_segment(&src, &d, pay, &tx, (s->options.flags & SOCK_OPT_TTL) ? s->options.ttl : 0, (s->options.flags & SOCK_OPT_DONTFRAG) ? 1 : 0); + if (!udp_send_segment(&src, &d, pay, &tx, ttl, dontfrag)) return SOCK_ERR_SYS; return (int64_t)len; } @@ -588,6 +577,7 @@ int32_t socket_setopt_udp(socket_impl_t sh, int32_t opt, const void* value, uint case SOCK_OPT_TCP_NO_DELAY: case SOCK_OPT_SEND_BUF_SIZE: case SOCK_OPT_TCP_MAXSEG: + case SOCK_OPT_TCP_SACK: case SOCK_OPT_LINGER: case SOCK_OPT_FILTER: case SOCK_OPT_SPECIAL: @@ -601,59 +591,67 @@ int32_t socket_setopt_udp(socket_impl_t sh, int32_t opt, const void* value, uint return socket_common_options_set(&s->options, opt, value, len); case SOCK_OPT_MCAST_JOIN: { if (!value || !len || (len % sizeof(net_l4_endpoint)) != 0) return SOCK_ERR_INVAL; - uint32_t count32 = len / sizeof(net_l4_endpoint); - if (!count32 || count32 > 255 || count32 + s->mcast_count > 255) return SOCK_ERR_INVAL; + uint32_t count = len / sizeof(net_l4_endpoint); + if (!count || count > 255 || count + s->options.mcast_count > 255) return SOCK_ERR_INVAL; const net_l4_endpoint* groups = value; - for (uint32_t i = 0; i < count32; ++i) if (!udp_socket_mcast_endpoint_valid(&groups[i])) return SOCK_ERR_INVAL; + for (uint32_t i = 0; i < count; ++i) if (!udp_socket_mcast_endpoint_valid(&groups[i])) return SOCK_ERR_INVAL; - net_l4_endpoint* next = (net_l4_endpoint*)zalloc(sizeof(net_l4_endpoint) * (s->mcast_count + count32)); - if (!next) return SOCK_ERR_SYS; - if (s->mcast_count) memcpy(next, s->mcast_groups, sizeof(net_l4_endpoint) * s->mcast_count); + uint32_t capacity = s->options.mcast_count + count; + net_l4_endpoint* next_groups = (net_l4_endpoint*)zalloc(sizeof(net_l4_endpoint) * capacity); + uint16_t* next_ifmasks = (uint16_t*)zalloc(sizeof(uint16_t) * capacity); + if (!next_groups || !next_ifmasks) { + if (next_groups) release(next_groups); + if (next_ifmasks) release(next_ifmasks); + return SOCK_ERR_SYS; + } + + if (s->options.mcast_count) { + memcpy(next_groups, s->options.mcast_groups, sizeof(net_l4_endpoint) * s->options.mcast_count); + memcpy(next_ifmasks, s->mcast_ifmasks, sizeof(uint16_t) * s->options.mcast_count); + } - uint8_t next_count = s->mcast_count; - for (uint32_t i = 0; i < count32; ++i) { + uint8_t next_count = s->options.mcast_count; + for (uint32_t i = 0; i < count; ++i) { bool exists = false; for (uint8_t j = 0; j < next_count; ++j) { - if (next[j].ver != groups[i].ver) continue; - uint32_t sz = groups[i].ver == IP_VER4 ? 4 : 16; - if (memcmp(next[j].ip, groups[i].ip, sz) == 0) exists = true; + if (next_groups[j].ver != groups[i].ver) continue; + uint32_t ip_len = groups[i].ver == IP_VER4 ? 4 : 16; + if (memcmp(next_groups[j].ip, groups[i].ip, ip_len) == 0) { + exists = true; + break; + } } - if (!exists) { - next[next_count] = groups[i]; - next[next_count].port = 0; - next_count++; + if (exists) continue; + + next_groups[next_count] = groups[i]; + next_groups[next_count].port = 0; + if (s->localPort) { + int32_t rc = udp_socket_apply_mcast_group(&s->bindSpec, &next_groups[next_count], true, &next_ifmasks[next_count]); + if (rc != SOCK_OK) { + while (next_count > s->options.mcast_count) { + next_count--; + (void)udp_socket_apply_mcast_group(&s->bindSpec, &next_groups[next_count], false, &next_ifmasks[next_count]); + } + release(next_groups); + release(next_ifmasks); + return rc; + } } + next_count++; } - if (next_count == s->mcast_count) { - release(next); + if (next_count == s->options.mcast_count) { + release(next_groups); + release(next_ifmasks); return SOCK_OK; } - net_l4_endpoint* old_groups = s->mcast_groups; - uint8_t old_count = s->mcast_count; - if (s->localPort) udp_socket_leave_mcast_groups(s); - - s->mcast_groups = next; - s->mcast_count = next_count; - int32_t rc = udp_socket_join_mcast_groups(s); - if (rc != SOCK_OK) { - udp_socket_leave_mcast_groups(s); - release(next); - s->mcast_groups = old_groups; - s->mcast_count = old_count; - s->options.mcast_groups = s->mcast_groups; - s->options.mcast_count = s->mcast_count; - if (s->mcast_count) s->options.flags |= SOCK_OPT_MCAST_JOIN; - else s->options.flags &= ~SOCK_OPT_MCAST_JOIN; - if (s->localPort) udp_socket_join_mcast_groups(s); - return rc; - } - - if (old_groups) release(old_groups); - s->options.mcast_groups = s->mcast_groups; - s->options.mcast_count = s->mcast_count; + if (s->options.mcast_groups) release((void*)s->options.mcast_groups); + if (s->mcast_ifmasks) release(s->mcast_ifmasks); + s->options.mcast_groups = next_groups; + s->mcast_ifmasks = next_ifmasks; + s->options.mcast_count = next_count; s->options.flags |= SOCK_OPT_MCAST_JOIN; return SOCK_OK; } @@ -661,78 +659,72 @@ int32_t socket_setopt_udp(socket_impl_t sh, int32_t opt, const void* value, uint if (!value || !len || (len % sizeof(net_l4_endpoint)) != 0) return SOCK_ERR_INVAL; uint32_t count = len / sizeof(net_l4_endpoint); if (!count || count > 255) return SOCK_ERR_INVAL; - if (!s->mcast_groups || !s->mcast_count) return SOCK_OK; + if (!s->options.mcast_groups || !s->options.mcast_count) return SOCK_OK; const net_l4_endpoint* groups = value; for (uint32_t i = 0; i < count; ++i) if (!udp_socket_mcast_endpoint_valid(&groups[i])) return SOCK_ERR_INVAL; - net_l4_endpoint* next = NULL; - if (s->mcast_count > count) { - next = (net_l4_endpoint*)zalloc(sizeof(net_l4_endpoint) * s->mcast_count); - if (!next) return SOCK_ERR_SYS; + net_l4_endpoint* next_groups = (net_l4_endpoint*)zalloc(sizeof(net_l4_endpoint) * s->options.mcast_count); + uint16_t* next_ifmasks = (uint16_t*)zalloc(sizeof(uint16_t) * s->options.mcast_count); + if (!next_groups || !next_ifmasks) { + if (next_groups) release(next_groups); + if (next_ifmasks) release(next_ifmasks); + return SOCK_ERR_SYS; } uint8_t next_count = 0; - for (uint8_t i = 0; i < s->mcast_count; ++i) { - bool remove_group = false; + for (uint8_t i = 0; i < s->options.mcast_count; ++i) { + bool removed = false; for (uint32_t j = 0; j < count; ++j) { - if (s->mcast_groups[i].ver != groups[j].ver) continue; - uint32_t sz = groups[j].ver == IP_VER4 ? 4 : 16; - if (memcmp(s->mcast_groups[i].ip, groups[j].ip, sz) == 0) remove_group = true; - } - if (!remove_group) { - if (!next) { - next = (net_l4_endpoint*)zalloc(sizeof(net_l4_endpoint) * s->mcast_count); - if (!next) return SOCK_ERR_SYS; + if (s->options.mcast_groups[i].ver != groups[j].ver) continue; + uint32_t ip_len = groups[j].ver == IP_VER4 ? 4 : 16; + if (memcmp(s->options.mcast_groups[i].ip, groups[j].ip, ip_len) == 0) { + removed = true; + break; } - next[next_count++] = s->mcast_groups[i]; } + if (removed) { + if (s->localPort) (void)udp_socket_apply_mcast_group(&s->bindSpec, &s->options.mcast_groups[i], false, &s->mcast_ifmasks[i]); + continue; + } + next_groups[next_count] = s->options.mcast_groups[i]; + next_ifmasks[next_count] = s->mcast_ifmasks[i]; + next_count++; } - if (next_count == s->mcast_count) { - if (next) release(next); + if (next_count == s->options.mcast_count) { + release(next_groups); + release(next_ifmasks); return SOCK_OK; } - if (!next_count && next) { - release(next); - next = NULL; - } - - net_l4_endpoint* old_groups = s->mcast_groups; - uint8_t old_count = s->mcast_count; - if (s->localPort) udp_socket_leave_mcast_groups(s); - - s->mcast_groups = next; - s->mcast_count = next_count; - int32_t rc = udp_socket_join_mcast_groups(s); - if (rc != SOCK_OK) { - udp_socket_leave_mcast_groups(s); - if (next) release(next); - s->mcast_groups = old_groups; - s->mcast_count = old_count; - s->options.mcast_groups = s->mcast_groups; - s->options.mcast_count = s->mcast_count; - s->options.flags |= SOCK_OPT_MCAST_JOIN; - if (s->localPort) udp_socket_join_mcast_groups(s); - return rc; + if (!next_count) { + release(next_groups); + release(next_ifmasks); + next_groups = NULL; + next_ifmasks = NULL; } - release(old_groups); - s->options.mcast_count = s->mcast_count; - s->options.mcast_groups = s->mcast_groups; - if (s->mcast_count) s->options.flags |= SOCK_OPT_MCAST_JOIN; + release((void*)s->options.mcast_groups); + release(s->mcast_ifmasks); + s->options.mcast_groups = next_groups; + s->mcast_ifmasks = next_ifmasks; + s->options.mcast_count = next_count; + if (s->options.mcast_count) s->options.flags |= SOCK_OPT_MCAST_JOIN; else s->options.flags &= ~SOCK_OPT_MCAST_JOIN; return SOCK_OK; } - case SOCK_OPT_RECV_TIMEOUT: case SOCK_OPT_SEND_TIMEOUT: - case SOCK_OPT_BUF_SIZE: + return SOCK_ERR_UNSUP; + case SOCK_OPT_RECV_TIMEOUT: case SOCK_OPT_DEBUG: case SOCK_OPT_DONTFRAG: case SOCK_OPT_BROADCAST_ALLOWED: case SOCK_OPT_TTL: return socket_common_options_set(&s->options, opt, value, len); + case SOCK_OPT_BUF_SIZE: + if (s->localPort) return SOCK_ERR_STATE; + return socket_common_options_set(&s->options, opt, value, len); default: return SOCK_ERR_INVAL; } @@ -777,19 +769,20 @@ int32_t socket_getopt_udp(socket_impl_t sh, int32_t opt, void* value, uint32_t* case SOCK_GET_OPT_TCP_NO_DELAY: case SOCK_GET_OPT_SEND_BUF_SIZE: case SOCK_GET_OPT_TCP_MAXSEG: + case SOCK_GET_OPT_TCP_SACK: case SOCK_GET_OPT_LINGER: case SOCK_GET_OPT_FILTER: + case SOCK_GET_OPT_SEND_TIMEOUT: case SOCK_GET_TCP_STATE: case SOCK_GET_TCP_MSS: case SOCK_GET_TCP_RTT_MS: case SOCK_GET_TCP_RETRANSMITS: return SOCK_ERR_UNSUP; case SOCK_GET_MCAST_GROUPS: { - uint32_t need = s->mcast_count * sizeof(net_l4_endpoint); - return socket_common_get_value(s->mcast_groups, need, value, len); + uint32_t need = s->options.mcast_count * sizeof(net_l4_endpoint); + return socket_common_get_value(s->options.mcast_groups, need, value, len); } case SOCK_GET_OPT_RECV_TIMEOUT: - case SOCK_GET_OPT_SEND_TIMEOUT: case SOCK_GET_OPT_BUF_SIZE: case SOCK_GET_OPT_DEBUG: case SOCK_GET_OPT_DONTFRAG: @@ -807,11 +800,15 @@ int32_t socket_getopt_udp(socket_impl_t sh, int32_t opt, void* value, uint32_t* return socket_common_get_value(&v, sizeof(v), value, len); } +//disable irq? int32_t socket_close_udp(socket_impl_t sh) { udp_socket_t* s = (udp_socket_t*)sh; if (!s) return SOCK_ERR_INVAL; + if (s->closed) return SOCK_OK; - udp_socket_leave_mcast_groups(s); + if (s->localPort && s->options.mcast_groups && s->mcast_ifmasks) { + for (uint32_t i = 0; i < s->options.mcast_count; ++i) udp_socket_apply_mcast_group(&s->bindSpec, &s->options.mcast_groups[i], false, &s->mcast_ifmasks[i]); + } netlog_socket_event_t ev = {0}; ev.comp = NETLOG_COMP_UDP; @@ -824,7 +821,9 @@ int32_t socket_close_udp(socket_impl_t sh) { if (s->rx_ring) { while (s->r_head != s->r_tail) { if (s->rx_ring[s->r_head].pkt) { - s->rx_bytes -= netpkt_len(s->rx_ring[s->r_head].pkt); + uint32_t pkt_len = netpkt_len(s->rx_ring[s->r_head].pkt); + if (s->rx_bytes >= pkt_len) s->rx_bytes -= pkt_len; + else s->rx_bytes = 0; netpkt_unref(s->rx_ring[s->r_head].pkt); } s->r_head = (s->r_head + 1) % s->ring_cap; @@ -832,13 +831,15 @@ int32_t socket_close_udp(socket_impl_t sh) { release(s->rx_ring); s->rx_ring = NULL; } - if (s->mcast_groups) { - release(s->mcast_groups); - s->mcast_groups = NULL; + if (s->options.mcast_groups) { + release((void*)s->options.mcast_groups); + s->options.mcast_groups = NULL; + } + if (s->mcast_ifmasks) { + release(s->mcast_ifmasks); + s->mcast_ifmasks = NULL; } - s->mcast_count = 0; s->options.mcast_count = 0; - s->options.mcast_groups = NULL; s->options.flags &= ~(SOCK_OPT_MCAST_JOIN | SOCK_OPT_MCAST_LEAVE); s->ring_cap = 0; s->r_head = 0; @@ -852,10 +853,12 @@ int32_t socket_close_udp(socket_impl_t sh) { } s->localPort = 0; memset(&s->bindSpec, 0, sizeof(s->bindSpec)); + s->bindSpec.kind = BIND_ANY; s->connected = false; s->remoteEP.port = 0; s->remoteEP.ver = IP_VER4; memset(s->remoteEP.ip, 0, sizeof(s->remoteEP.ip)); + s->closed = true; return SOCK_OK; } diff --git a/kernel/networking/transport_layer/socket_bind.c b/kernel/networking/transport_layer/socket_bind.c index 4b4f0723..a2333cd7 100644 --- a/kernel/networking/transport_layer/socket_bind.c +++ b/kernel/networking/transport_layer/socket_bind.c @@ -13,17 +13,16 @@ #define SOCKET_PORT_MIN_EPHEMERAL 49152u #define SOCKET_PORT_MAX_EPHEMERAL 65535u #define SOCKET_BIND_LIST_END 0xFFFF +#define SOCKET_BIND_FLAG_REUSEADDR (1u << 0) +#define SOCKET_BIND_FLAG_REUSEPORT (1u << 1) +#define SOCKET_BIND_FLAG_LISTENING (1u << 2) typedef struct socket_bind_entry { - bool used; - bool listening; - protocol_t protocol; uint16_t port; uint16_t next; uint16_t generation; + uint8_t flags; SockBindSpec spec; - uint32_t options; - uint16_t owner_pid; net_l4_endpoint remote; ksocket_t* socket; } socket_bind_entry_t; @@ -32,13 +31,6 @@ static socket_bind_entry_t bind_entries[SOCKET_BIND_MAX]; static hash_map_t* bind_map = NULL; static uint16_t bind_next_alloc = 0; -static void socket_bind_map_init(void) { - if (bind_map) return; - bind_map = hash_map_create(256); - bind_next_alloc = 0; - for (uint32_t i = 0; i < SOCKET_BIND_MAX; ++i) bind_entries[i].next = SOCKET_BIND_LIST_END; -} - static uint16_t socket_bind_head(protocol_t protocol, uint16_t port) { uint32_t key = ((uint32_t)protocol << 16) | port; void* value = hash_map_get(bind_map, &key, sizeof(key)); @@ -50,16 +42,16 @@ static bool socket_bind_set_head(protocol_t protocol, uint16_t port, uint16_t he uint32_t key = ((uint32_t)protocol << 16) | port; if (head == SOCKET_BIND_LIST_END) { void* old = NULL; - hash_map_remove(bind_map, &key, sizeof(key), &old); - return true; + return hash_map_remove(bind_map, &key, sizeof(key), &old); } return hash_map_put(bind_map, &key, sizeof(key), (void*)(uintptr_t)(head + 1)) >= 0; } -bool socket_bind_normalize_spec(SockBindSpec* spec) { +static bool socket_bind_normalize_spec(SockBindSpec* spec) { if (!spec) return false; if (spec->kind == BIND_L3 && !spec->l3_id && !spec->ifindex && !spec->ver && ipv6_is_unspecified(spec->ip)) { + memset(spec, 0, sizeof(*spec)); spec->kind = BIND_ANY; return true; } @@ -67,38 +59,58 @@ bool socket_bind_normalize_spec(SockBindSpec* spec) { if (spec->kind == BIND_L3) { if (!spec->l3_id) return false; if (!spec->ver) spec->ver = l3_is_v6_from_id(spec->l3_id) ? IP_VER6 : IP_VER4; - return spec->ver == IP_VER4 || spec->ver == IP_VER6; + if (spec->ver != IP_VER4 && spec->ver != IP_VER6) return false; + spec->ifindex = 0; + memset(spec->ip, 0, sizeof(spec->ip)); + return true; } - if (spec->kind == BIND_L2) return !spec->ver || spec->ver == IP_VER4 || spec->ver == IP_VER6; + if (spec->kind == BIND_L2) { + if (!spec->ifindex || (spec->ver && spec->ver != IP_VER4 && spec->ver != IP_VER6)) return false; + spec->l3_id = 0; + memset(spec->ip, 0, sizeof(spec->ip)); + return true; + } if (spec->kind == BIND_ANY) { - spec->ver = 0; + memset(spec, 0, sizeof(*spec)); + spec->kind = BIND_ANY; return true; } if (spec->kind == BIND_ANY4) { + memset(spec, 0, sizeof(*spec)); + spec->kind = BIND_ANY4; spec->ver = IP_VER4; return true; } if (spec->kind == BIND_ANY6) { + memset(spec, 0, sizeof(*spec)); + spec->kind = BIND_ANY6; spec->ver = IP_VER6; return true; } if (spec->kind != BIND_IP) return false; + spec->l3_id = 0; + spec->ifindex = 0; if (spec->ver == IP_VER4) { uint32_t ip = 0; memcpy(&ip, spec->ip, 4); if (ipv4_is_unspecified(ip)) { + memset(spec, 0, sizeof(*spec)); spec->kind = BIND_ANY4; + spec->ver = IP_VER4; return true; } if (ipv4_is_multicast(ip) || ipv4_is_limited_broadcast(ip)) return false; + memset(spec->ip + 4, 0, 12); return true; } if (spec->ver == IP_VER6) { if (ipv6_is_unspecified(spec->ip)) { + memset(spec, 0, sizeof(*spec)); spec->kind = BIND_ANY6; + spec->ver = IP_VER6; return true; } if (ipv6_is_multicast(spec->ip)) return false; @@ -106,6 +118,7 @@ bool socket_bind_normalize_spec(SockBindSpec* spec) { } if (!spec->ver && ipv6_is_unspecified(spec->ip)) { + memset(spec, 0, sizeof(*spec)); spec->kind = BIND_ANY; return true; } @@ -113,65 +126,90 @@ bool socket_bind_normalize_spec(SockBindSpec* spec) { return false; } -static bool socket_bind_l3_valid(ip_version_t ver, uint8_t l3_id) { - if (!l3_id) return false; - if (ver == IP_VER4) return ipv4_l3_is_active(l3_ipv4_find_by_id(l3_id)); - if (ver == IP_VER6) return ipv6_l3_is_ready(l3_ipv6_find_by_id(l3_id)); - return false; +bool socket_bind_prepare_spec(SockBindSpec* spec, protocol_t protocol) { + if (!spec || (protocol != PROTO_TCP && protocol != PROTO_UDP)) return false; + if (!socket_bind_normalize_spec(spec)) return false; + + if (spec->kind == BIND_ANY || spec->kind == BIND_ANY4 || spec->kind == BIND_ANY6) return true; + if (spec->kind == BIND_L2) { + l2_interface_t* l2 = l2_interface_find_by_index(spec->ifindex); + return l2 && l2->is_up; + } + + if (spec->kind == BIND_L3) { + if (spec->ver == IP_VER4) { + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(spec->l3_id); + if (!v4 || !v4->l2 || (protocol == PROTO_TCP ? !ipv4_l3_is_ready(v4) : !ipv4_l3_is_active(v4))) return false; + spec->ifindex = v4->l2->ifindex; + return true; + } + l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(spec->l3_id); + if (!v6 || !v6->l2 || (protocol == PROTO_TCP ? !ipv6_l3_is_tcp_usable(v6) : !ipv6_l3_is_ready(v6))) return false; + spec->ifindex = v6->l2->ifindex; + return true; + } + + if (spec->ver == IP_VER4) { + uint32_t ip = 0; + memcpy(&ip, spec->ip, 4); + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_ip(ip); + if (!v4 || !v4->l2 || (protocol == PROTO_TCP ? !ipv4_l3_is_ready(v4) : !ipv4_l3_is_active(v4))) return false; + spec->l3_id = v4->l3_id; + spec->ifindex = v4->l2->ifindex; + return true; + } + + l3_ipv6_interface_t* v6 = l3_ipv6_find_by_ip(spec->ip); + if (!v6 || !v6->l2 || (protocol == PROTO_TCP ? !ipv6_l3_is_tcp_usable(v6) : !ipv6_l3_is_ready(v6))) return false; + spec->l3_id = v6->l3_id; + spec->ifindex = v6->l2->ifindex; + return true; +} + +uint8_t socket_bind_match_score(const SockBindSpec* spec, ip_version_t ver, uint8_t l3_id, uint8_t ifindex, const void* ip_addr) { + if (!spec || !ip_addr || (ver != IP_VER4 && ver != IP_VER6)) return 0; + if ((spec->kind == BIND_L3 || spec->kind == BIND_L2) && spec->ver && spec->ver != ver) return 0; + + switch (spec->kind) { + case BIND_IP: + if (spec->ver != ver) return 0; + return memcmp(spec->ip, ip_addr, ver == IP_VER6 ? 16 : 4) == 0 ? 5 : 0; + case BIND_L3: + return spec->l3_id == l3_id ? 4 : 0; + case BIND_L2: + return spec->ifindex == ifindex ? 3 : 0; + case BIND_ANY4: + return ver == IP_VER4 ? 2 : 0; + case BIND_ANY6: + return ver == IP_VER6 ? 2 : 0; + case BIND_ANY: + return 1; + default: + return 0; + } } uint32_t socket_bind_select_l3(const SockBindSpec* spec, ip_version_t ver, uint8_t* out, uint32_t cap) { if (!spec || !out || !cap || (ver != IP_VER4 && ver != IP_VER6)) return 0; - uint32_t count = 0; SockBindSpec normal = *spec; if (!socket_bind_normalize_spec(&normal)) return 0; - if (normal.kind == BIND_ANY4 && ver != IP_VER4) return 0; - if (normal.kind == BIND_ANY6 && ver != IP_VER6) return 0; - if (normal.ver && normal.ver != ver && normal.kind != BIND_ANY && normal.kind != BIND_ANY4 && normal.kind != BIND_ANY6) return 0; - - if (normal.kind == BIND_L3) { - if (socket_bind_l3_valid(ver, normal.l3_id)) out[count++] = normal.l3_id; - return count; - } - - if (normal.kind == BIND_IP) { - if (ver == IP_VER4) { - uint32_t ip = 0; - memcpy(&ip, normal.ip, 4); - if (ipv4_is_multicast(ip) || ipv4_is_limited_broadcast(ip)) return 0; - if (!ipv4_is_unspecified(ip)) { - l3_ipv4_interface_t* v4 = l3_ipv4_find_by_ip(ip); - if (v4 && socket_bind_l3_valid(IP_VER4, v4->l3_id)) out[count++] = v4->l3_id; - return count; - } - } else { - if (ipv6_is_multicast(normal.ip)) return 0; - if (!ipv6_is_unspecified(normal.ip)) { - l3_ipv6_interface_t* v6 = l3_ipv6_find_by_ip(normal.ip); - if (v6 && socket_bind_l3_valid(IP_VER6, v6->l3_id)) out[count++] = v6->l3_id; - return count; - } - } - } - + uint32_t count = 0; for (uint8_t i = 0, n = l2_interface_count(); i < n && count < cap; ++i) { l2_interface_t* l2 = l2_interface_at(i); - if (!l2 || !l2->is_up) continue; - if (normal.kind == BIND_L2 && l2->ifindex != normal.ifindex) continue; - if (normal.kind != BIND_ANY && normal.kind != BIND_ANY4 && normal.kind != BIND_ANY6 && normal.kind != BIND_IP && normal.kind != BIND_L2) continue; + if (!l2) continue; if (ver == IP_VER4) { - for (int s = 0; s < MAX_IPV4_PER_INTERFACE && count < cap; ++s) { - l3_ipv4_interface_t* v4 = l2->l3_v4[s]; - if (!v4 || !socket_bind_l3_valid(IP_VER4, v4->l3_id)) continue; + for (uint32_t slot = 0; slot < MAX_IPV4_PER_INTERFACE && count < cap; ++slot) { + l3_ipv4_interface_t* v4 = l2->l3_v4[slot]; + if (!v4 || !socket_bind_match_score(&normal, ver, v4->l3_id, l2->ifindex, &v4->ip)) continue; out[count++] = v4->l3_id; } } else { - for (int s = 0; s < MAX_IPV6_PER_INTERFACE && count < cap; ++s) { - l3_ipv6_interface_t* v6 = l2->l3_v6[s]; - if (!v6 || !socket_bind_l3_valid(IP_VER6, v6->l3_id)) continue; + for (uint32_t slot = 0; slot < MAX_IPV6_PER_INTERFACE && count < cap; ++slot) { + l3_ipv6_interface_t* v6 = l2->l3_v6[slot]; + if (!v6 || !socket_bind_match_score(&normal, ver, v6->l3_id, l2->ifindex, v6->ip)) continue; out[count++] = v6->l3_id; } } @@ -200,44 +238,28 @@ static bool socket_bind_specs_overlap(const SockBindSpec* a, const SockBindSpec* return a->ifindex && b->ifindex && a->ifindex == b->ifindex; } -bool socket_bind_insert(ksocket_t* socket, protocol_t protocol, const SockBindSpec* spec, uint16_t port, uint32_t options, bool allow_reuse, socket_bind_token_t* out_token) { - if (out_token) *out_token = 0; - if (!socket || !spec || !port) return false; - if (protocol != PROTO_TCP && protocol != PROTO_UDP) return false; - - SockBindSpec normal = *spec; - if (!socket_bind_normalize_spec(&normal)) return false; - - if (normal.kind == BIND_L3) { - if (!socket_bind_l3_valid(normal.ver, normal.l3_id)) return false; - normal.ifindex = l3_ifindex_from_id(normal.l3_id); - } else if (normal.kind == BIND_L2) { - if (!l2_interface_find_by_index(normal.ifindex)) return false; - } else if (normal.kind == BIND_IP) { - if (normal.ver == IP_VER4) { - uint32_t ip = 0; - memcpy(&ip, normal.ip, 4); - l3_ipv4_interface_t* v4 = l3_ipv4_find_by_ip(ip); - if (!v4 || !v4->l2) return false; - normal.l3_id = v4->l3_id; - normal.ifindex = v4->l2->ifindex; - } else if (normal.ver == IP_VER6) { - l3_ipv6_interface_t* v6 = l3_ipv6_find_by_ip(normal.ip); - if (!v6 || !v6->l2) return false; - normal.l3_id = v6->l3_id; - normal.ifindex = v6->l2->ifindex; - } else return false; +static bool socket_bind_insert_prepared(ksocket_t* socket, protocol_t protocol, const SockBindSpec* spec, uint16_t port, uint32_t options, bool allow_reuse, socket_bind_token_t* out_token) { + if (!bind_map) { + hash_map_t* new_map = hash_map_create(SOCKET_BIND_MAX * 2u); + if (!new_map) return false; + + irq_flags_t init_irq = irq_save_disable(); + if (!bind_map) { + bind_next_alloc = 0; + for (uint32_t i = 0; i < SOCKET_BIND_MAX; ++i) bind_entries[i].next = SOCKET_BIND_LIST_END; + bind_map = new_map; + new_map = NULL; + } + irq_restore(init_irq); + if (new_map) hash_map_destroy(new_map); } - socket_bind_map_init(); - if (!bind_map) return false; - - uint16_t owner_pid = protocol == PROTO_UDP ? socket_core_pid(socket) : 0; - uint32_t reuse = options & SOCK_OPT_REUSEADDR; - if (protocol == PROTO_UDP) reuse |= options & SOCK_OPT_REUSEPORT; + uint8_t flags = 0; + if (options & SOCK_OPT_REUSEADDR) flags |= SOCKET_BIND_FLAG_REUSEADDR; + if (protocol == PROTO_UDP && (options & SOCK_OPT_REUSEPORT)) flags |= SOCKET_BIND_FLAG_REUSEPORT; irq_flags_t irq = irq_save_disable(); - if (protocol == PROTO_TCP && tcp_bind_conflicts(&normal, port, allow_reuse && reuse != 0)) { + if (protocol == PROTO_TCP && tcp_bind_conflicts(spec, port, allow_reuse && (flags & SOCKET_BIND_FLAG_REUSEADDR))) { irq_restore(irq); return false; } @@ -245,14 +267,14 @@ bool socket_bind_insert(ksocket_t* socket, protocol_t protocol, const SockBindSp uint16_t head = socket_bind_head(protocol, port); for (uint16_t idx = head; idx != SOCKET_BIND_LIST_END; idx = bind_entries[idx].next) { socket_bind_entry_t* other = &bind_entries[idx]; - if (!other->used || other->protocol != protocol || other->port != port) continue; - if (!socket_bind_specs_overlap(&normal, &other->spec)) continue; + if (!other->socket || other->port != port) continue; + if (!socket_bind_specs_overlap(spec, &other->spec)) continue; - bool allowed = allow_reuse && (reuse & SOCK_OPT_REUSEADDR) && (other->options & SOCK_OPT_REUSEADDR); + bool allowed = allow_reuse && (flags & SOCKET_BIND_FLAG_REUSEADDR) && (other->flags & SOCKET_BIND_FLAG_REUSEADDR); if (protocol == PROTO_UDP && !allowed) { - allowed = allow_reuse && owner_pid == other->owner_pid && (reuse & SOCK_OPT_REUSEPORT) && (other->options & SOCK_OPT_REUSEPORT); + allowed = allow_reuse && socket_core_pid(socket) == socket_core_pid(other->socket) && (flags & SOCKET_BIND_FLAG_REUSEPORT) && (other->flags & SOCKET_BIND_FLAG_REUSEPORT); } - if (protocol == PROTO_TCP && other->listening) allowed = false; + if (protocol == PROTO_TCP && (other->flags & SOCKET_BIND_FLAG_LISTENING)) allowed = false; if (!allowed) { irq_restore(irq); return false; @@ -262,7 +284,7 @@ bool socket_bind_insert(ksocket_t* socket, protocol_t protocol, const SockBindSp uint16_t idx = SOCKET_BIND_LIST_END; for (uint32_t i = 0; i < SOCKET_BIND_MAX; ++i) { uint16_t cand = (uint16_t)((bind_next_alloc + i) % SOCKET_BIND_MAX); - if (bind_entries[cand].used) continue; + if (bind_entries[cand].socket) continue; idx = cand; bind_next_alloc = (uint16_t)((cand + 1) % SOCKET_BIND_MAX); break; @@ -272,23 +294,20 @@ bool socket_bind_insert(ksocket_t* socket, protocol_t protocol, const SockBindSp return false; } - socket_bind_entry_t* e = &bind_entries[idx]; - uint16_t generation = (uint16_t)(e->generation + 1); + socket_bind_entry_t* entry = &bind_entries[idx]; + uint16_t generation = (uint16_t)(entry->generation + 1); if (!generation) generation = 1; - memset(e, 0, sizeof(*e)); - e->used = true; - e->protocol = protocol; - e->port = port; - e->generation = generation; - e->spec = normal; - e->options = reuse; - e->owner_pid = owner_pid; - e->socket = socket; - e->next = head; + memset(entry, 0, sizeof(*entry)); + entry->port = port; + entry->generation = generation; + entry->flags = flags; + entry->spec = *spec; + entry->socket = socket; + entry->next = head; if (!socket_bind_set_head(protocol, port, idx)) { - memset(e, 0, sizeof(*e)); - e->generation = generation; - e->next = SOCKET_BIND_LIST_END; + memset(entry, 0, sizeof(*entry)); + entry->generation = generation; + entry->next = SOCKET_BIND_LIST_END; irq_restore(irq); return false; } @@ -300,6 +319,13 @@ bool socket_bind_insert(ksocket_t* socket, protocol_t protocol, const SockBindSp return true; } +bool socket_bind_insert(ksocket_t* socket, protocol_t protocol, SockBindSpec* spec, uint16_t port, uint32_t options, bool allow_reuse, socket_bind_token_t* out_token) { + if (out_token) *out_token = 0; + if (!socket || !spec || !port) return false; + if (!socket_bind_prepare_spec(spec, protocol)) return false; + return socket_bind_insert_prepared(socket, protocol, spec, port, options, allow_reuse, out_token); +} + bool socket_bind_tcp_listen(socket_bind_token_t token) { if (!token) return false; @@ -307,17 +333,16 @@ bool socket_bind_tcp_listen(socket_bind_token_t token) { uint32_t generation = token >> 16; if (!idxplus || idxplus > SOCKET_BIND_MAX || !generation) return false; - socket_bind_map_init(); if (!bind_map) return false; uint16_t idx = (uint16_t)(idxplus - 1); irq_flags_t irq = irq_save_disable(); socket_bind_entry_t* entry = &bind_entries[idx]; - if (!entry->used || entry->generation != generation || entry->protocol != PROTO_TCP || !entry->socket) { + if (!entry->socket || entry->generation != generation || socket_core_protocol(entry->socket) != PROTO_TCP) { irq_restore(irq); return false; } - if (entry->listening) { + if (entry->flags & SOCKET_BIND_FLAG_LISTENING) { irq_restore(irq); return true; } @@ -325,13 +350,13 @@ bool socket_bind_tcp_listen(socket_bind_token_t token) { uint16_t head = socket_bind_head(PROTO_TCP, entry->port); for (uint16_t other_idx = head; other_idx != SOCKET_BIND_LIST_END; other_idx = bind_entries[other_idx].next) { socket_bind_entry_t* other = &bind_entries[other_idx]; - if (other_idx == idx || !other->used || !other->listening || other->protocol != PROTO_TCP || other->port != entry->port) continue; + if (other_idx == idx || !other->socket || !(other->flags & SOCKET_BIND_FLAG_LISTENING) || other->port != entry->port) continue; if (!socket_bind_specs_overlap(&entry->spec, &other->spec)) continue; irq_restore(irq); return false; } - entry->listening = true; + entry->flags |= SOCKET_BIND_FLAG_LISTENING; irq_restore(irq); return true; } @@ -346,31 +371,38 @@ void socket_bind_remove(socket_bind_token_t token) { uint16_t idx = (uint16_t)(idxplus - 1); ksocket_t* drop = NULL; - socket_bind_map_init(); if (!bind_map) return; irq_flags_t irq = irq_save_disable(); //TODO lock socket_bind_entry_t* e = &bind_entries[idx]; - if (e->used && e->generation == generation) { - uint16_t head = socket_bind_head(e->protocol, e->port); + if (e->socket && e->generation == generation) { + protocol_t protocol = socket_core_protocol(e->socket); + uint16_t head = socket_bind_head(protocol, e->port); uint16_t cur = head; uint16_t prev = SOCKET_BIND_LIST_END; + bool removed = false; while (cur != SOCKET_BIND_LIST_END) { if (cur == idx) { - if (prev == SOCKET_BIND_LIST_END) head = bind_entries[cur].next; - else bind_entries[prev].next = bind_entries[cur].next; - socket_bind_set_head(e->protocol, e->port, head); + if (prev == SOCKET_BIND_LIST_END) { + head = bind_entries[cur].next; + removed = socket_bind_set_head(protocol, e->port, head); + } else { + bind_entries[prev].next = bind_entries[cur].next; + removed = true; + } break; } prev = cur; cur = bind_entries[cur].next; } - drop = e->socket; - memset(e, 0, sizeof(*e)); - e->generation = generation; - e->next = SOCKET_BIND_LIST_END; - bind_next_alloc = idx; + if (removed) { + drop = e->socket; + memset(e, 0, sizeof(*e)); + e->generation = generation; + e->next = SOCKET_BIND_LIST_END; + bind_next_alloc = idx; + } } irq_restore(irq); @@ -387,17 +419,16 @@ void socket_bind_udp_set_remote(socket_bind_token_t token, const net_l4_endpoint uint16_t idx = (uint16_t)(idxplus-1); irq_flags_t irq = irq_save_disable(); socket_bind_entry_t* e = &bind_entries[idx]; - if (e->used && e->generation == generation && e->protocol == PROTO_UDP) { + if (e->socket && e->generation == generation && socket_core_protocol(e->socket) == PROTO_UDP) { memset(&e->remote, 0, sizeof(e->remote)); if (remote && remote->port && (remote->ver == IP_VER4 || remote->ver == IP_VER6)) e->remote = *remote; } irq_restore(irq); } -int socket_bind_alloc_ephemeral(ksocket_t* socket, protocol_t protocol, const SockBindSpec* spec, uint32_t options, socket_bind_token_t* out_token) { +int32_t socket_bind_alloc_ephemeral(ksocket_t* socket, protocol_t protocol, SockBindSpec* spec, uint32_t options, socket_bind_token_t* out_token) { if (out_token) *out_token = 0; - if (!socket || !spec) return -1; - if (protocol != PROTO_TCP && protocol != PROTO_UDP) return -1; + if (!socket || !spec || !socket_bind_prepare_spec(spec, protocol)) return -1; rng_t rng; rng_init_random(&rng); @@ -410,14 +441,14 @@ int socket_bind_alloc_ephemeral(ksocket_t* socket, protocol_t protocol, const So for (uint32_t i = 0; i < range; ++i) { uint16_t port = (uint16_t)(minp + ((first - minp + i) % range)); - if (socket_bind_insert(socket, protocol, spec, port, options, false, out_token)) return port; + if (socket_bind_insert_prepared(socket, protocol, spec, port, options, false, out_token)) return port; } return -1; } -int socket_bind_alloc_ephemeral_l3(ksocket_t* socket, protocol_t protocol, uint8_t l3_id, uint32_t options, socket_bind_token_t* out_token) { - if (!socket || !l3_id) return -1; +int32_t socket_bind_alloc_ephemeral_l3(ksocket_t* socket, protocol_t protocol, uint8_t l3_id, uint32_t options, SockBindSpec* out_spec, socket_bind_token_t* out_token) { + if (!socket || !l3_id || !out_spec) return -1; SockBindSpec spec; memset(&spec, 0, sizeof(spec)); @@ -425,30 +456,23 @@ int socket_bind_alloc_ephemeral_l3(ksocket_t* socket, protocol_t protocol, uint8 spec.ver = l3_is_v6_from_id(l3_id) ? IP_VER6 : IP_VER4; spec.l3_id = l3_id; - return socket_bind_alloc_ephemeral(socket, protocol, &spec, options, out_token); + int32_t port = socket_bind_alloc_ephemeral(socket, protocol, &spec, options, out_token); + if (port >= 0) *out_spec = spec; + return port; } ksocket_t* socket_bind_lookup(protocol_t protocol, ip_version_t ipver, uint8_t l3_id, uint8_t ifindex, const void* src_ip_addr, uint16_t src_port, const void* dst_ip_addr, uint16_t dst_port) { if (!dst_ip_addr || (protocol != PROTO_TCP && protocol != PROTO_UDP)) return NULL; if (ipver != IP_VER4 && ipver != IP_VER6) return NULL; - - socket_bind_map_init(); if (!bind_map) return NULL; - //are all these fields needed? - net_l4_endpoint src = {0}; - net_l4_endpoint dst = {0}; - src.ver = ipver; - src.port = src_port; - dst.ver = ipver; - dst.port = dst_port; - uint32_t ip_len = ipver == IP_VER6 ? 16 : 4; - if (src_ip_addr) memcpy(src.ip, src_ip_addr, ip_len); - memcpy(dst.ip, dst_ip_addr, ip_len); + + uint32_t ip_len = ipver == IP_VER6 ? 16u : 4u; + uint8_t reuseport_key[1 + sizeof(src_port) + sizeof(dst_port) + 32 + sizeof(socket_handle_t)]; + uint32_t reuseport_key_len = 0; irq_flags_t irq = irq_save_disable(); //TODO lock uint16_t head = socket_bind_head(protocol, dst_port); - int32_t best_score = 0; - uint32_t best_count = 0; + uint8_t best_score = 0; bool reuseport_group = false; uint16_t reuseport_owner = 0; uint64_t reuseport_hash = 0; @@ -457,33 +481,10 @@ ksocket_t* socket_bind_lookup(protocol_t protocol, ip_version_t ipver, uint8_t l for (uint16_t idx = head; idx != SOCKET_BIND_LIST_END; idx = bind_entries[idx].next) { socket_bind_entry_t* e = &bind_entries[idx]; - if (!e->used || e->protocol != protocol || e->port != dst_port || !e->socket || socket_core_is_closing(e->socket)) continue; - if (protocol == PROTO_TCP && !e->listening) continue; - if ((e->spec.kind == BIND_L3 || e->spec.kind == BIND_L2) && e->spec.ver && e->spec.ver != ipver) continue; - - int32_t score = 0; - switch (e->spec.kind) { - case BIND_IP: - if (e->spec.ver == ipver && memcmp(e->spec.ip, dst_ip_addr, ip_len) == 0) score = 5; - break; - case BIND_L3: - if (e->spec.l3_id == l3_id) score = 4; - break; - case BIND_L2: - if (e->spec.ifindex == ifindex) score = 3; - break; - case BIND_ANY4: - if (ipver == IP_VER4) score = 2; - break; - case BIND_ANY6: - if (ipver == IP_VER6) score = 2; - break; - case BIND_ANY: - score = 1; - break; - default: - break; - } + if (!e->socket || e->port != dst_port || socket_core_is_closing(e->socket)) continue; + if (protocol == PROTO_TCP && !(e->flags & SOCKET_BIND_FLAG_LISTENING)) continue; + + uint8_t score = socket_bind_match_score(&e->spec, ipver, l3_id, ifindex, dst_ip_addr); if (!score) continue; if (protocol == PROTO_UDP && e->remote.port) { @@ -493,31 +494,32 @@ ksocket_t* socket_bind_lookup(protocol_t protocol, ip_version_t ipver, uint8_t l } if (score < best_score) continue; - bool reuseport = protocol == PROTO_UDP && (e->options & SOCK_OPT_REUSEPORT); + bool reuseport = protocol == PROTO_UDP && (e->flags & SOCKET_BIND_FLAG_REUSEPORT); if (score > best_score) { best_score = score; - best_count = 1; reuseport_group = reuseport; - reuseport_owner = e->owner_pid; + reuseport_owner = socket_core_pid(e->socket); reuseport_hash = 0; first = e; reuseport_selected = NULL; - } else { - best_count++; - if (!reuseport || e->owner_pid != reuseport_owner) reuseport_group = false; - } + } else if (!reuseport || socket_core_pid(e->socket) != reuseport_owner) reuseport_group = false; + if (reuseport_group && reuseport) { + if (!reuseport_key_len) { + reuseport_key[reuseport_key_len++] = (uint8_t)ipver; + memcpy(reuseport_key + reuseport_key_len, &src_port, sizeof(src_port)); + reuseport_key_len += sizeof(src_port); + memcpy(reuseport_key + reuseport_key_len, &dst_port, sizeof(dst_port)); + reuseport_key_len += sizeof(dst_port); + if (src_ip_addr) memcpy(reuseport_key + reuseport_key_len, src_ip_addr, ip_len); + else memset(reuseport_key + reuseport_key_len, 0, ip_len); + reuseport_key_len += ip_len; + memcpy(reuseport_key + reuseport_key_len, dst_ip_addr, ip_len); + reuseport_key_len += ip_len; + } - if (reuseport) { - struct { - net_l4_endpoint src; - net_l4_endpoint dst; - socket_handle_t handle; - } key; - memset(&key, 0, sizeof(key)); - key.src = src; - key.dst = dst; - key.handle = socket_core_export_handle(e->socket); - uint64_t hash = hash_map_fnv1a64(&key, sizeof(key)); + socket_handle_t handle = socket_core_export_handle(e->socket); + memcpy(reuseport_key + reuseport_key_len, &handle, sizeof(handle)); + uint64_t hash = hash_map_fnv1a64(reuseport_key, reuseport_key_len + sizeof(handle)); if (!reuseport_selected || hash > reuseport_hash) { reuseport_selected = e; reuseport_hash = hash; @@ -525,7 +527,7 @@ ksocket_t* socket_bind_lookup(protocol_t protocol, ip_version_t ipver, uint8_t l } } - socket_bind_entry_t* selected = best_count > 1 && reuseport_group ? reuseport_selected : first; + socket_bind_entry_t* selected = reuseport_group && reuseport_selected ? reuseport_selected : first; ksocket_t* socket = selected ? selected->socket : NULL; if (socket) socket_core_ref(socket); irq_restore(irq); @@ -541,35 +543,10 @@ ksocket_t* socket_bind_udp_next_fanout(ip_version_t ipver, uint8_t l3_id, uint8_ irq_flags_t irq = irq_save_disable(); for (uint32_t i = start; i < SOCKET_BIND_MAX; ++i) { socket_bind_entry_t* e = &bind_entries[i]; - if (!e->used || e->protocol != PROTO_UDP || e->port != dst_port || !e->socket || socket_core_is_closing(e->socket)) continue; - if ((e->spec.kind == BIND_L3 || e->spec.kind == BIND_L2) && e->spec.ver && e->spec.ver != ipver) continue; + if (!e->socket || socket_core_protocol(e->socket) != PROTO_UDP || e->port != dst_port || socket_core_is_closing(e->socket)) continue; + if (!socket_bind_match_score(&e->spec, ipver, l3_id, ifindex, dst_ip_addr)) continue; - bool match = false; - switch (e->spec.kind) { - case BIND_IP: - match = e->spec.ver == ipver && memcmp(e->spec.ip, dst_ip_addr, ipver == IP_VER6 ? 16 : 4) == 0; - break; - case BIND_L3: - match = e->spec.l3_id == l3_id; - break; - case BIND_L2: - match = e->spec.ifindex == ifindex; - break; - case BIND_ANY4: - match = ipver == IP_VER4; - break; - case BIND_ANY6: - match = ipver == IP_VER6; - break; - case BIND_ANY: - match = true; - break; - default: - break; - } - if (!match) continue; - - *cursor = i++; + *cursor = i + 1; socket_core_ref(e->socket); irq_restore(irq); return e->socket; @@ -580,45 +557,60 @@ ksocket_t* socket_bind_udp_next_fanout(ip_version_t ipver, uint8_t l3_id, uint8_ return NULL; } +static bool socket_bind_build_tx_opts(const SockBindSpec* spec, ip_version_t ver, ip_tx_opts_t* tx, const ip_tx_opts_t** hint) { + if (!spec || !tx || !hint || (ver != IP_VER4 && ver != IP_VER6)) return false; + + SockBindSpec normal = *spec; + if (!socket_bind_normalize_spec(&normal)) return false; + if ((normal.kind == BIND_ANY4 && ver != IP_VER4) || (normal.kind == BIND_ANY6 && ver != IP_VER6)) return false; + if (normal.ver && normal.ver != ver && normal.kind != BIND_ANY && normal.kind != BIND_ANY4 && normal.kind != BIND_ANY6) return false; + + memset(tx, 0, sizeof(*tx)); + *hint = NULL; + if (normal.kind == BIND_ANY || normal.kind == BIND_ANY4 || normal.kind == BIND_ANY6) return true; + if (normal.kind == BIND_L2) { + l2_interface_t* l2 = l2_interface_find_by_index(normal.ifindex); + if (!l2 || !l2->is_up) return false; + tx->scope = IP_TX_BOUND_L2; + tx->index = normal.ifindex; + *hint = tx; + return true; + } + + uint8_t l3_id = normal.l3_id; + if (normal.kind == BIND_IP) { + if (ver == IP_VER4) { + uint32_t ip = 0; + memcpy(&ip, normal.ip, 4); + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_ip(ip); + if (!v4 || !v4->l2) return false; + l3_id = v4->l3_id; + } else { + l3_ipv6_interface_t* v6 = l3_ipv6_find_by_ip(normal.ip); + if (!v6 || !v6->l2) return false; + l3_id = v6->l3_id; + } + } else if (normal.kind != BIND_L3) return false; + + if (ver == IP_VER4) { + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(l3_id); + if (!v4 || !v4->l2) return false; + } else { + l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(l3_id); + if (!v6 || !v6->l2) return false; + } + tx->scope = IP_TX_BOUND_L3; + tx->index = l3_id; + *hint = tx; + return true; +} + bool socket_bind_build_ipv4_tx_plan(const SockBindSpec* spec, bool use_spec, uint32_t dst, ipv4_tx_plan_t* out) { if (!out) return false; ip_tx_opts_t tx; - memset(&tx, 0, sizeof(tx)); const ip_tx_opts_t* hint = NULL; - - if (use_spec) { - if (!spec) return 0; - - SockBindSpec normal = *spec; - if (!socket_bind_normalize_spec(&normal)) return false; - if (normal.kind == BIND_ANY6) return false; - if (normal.ver && normal.ver != IP_VER4 && normal.kind != BIND_ANY && normal.kind != BIND_ANY4) return false; - - if (normal.kind == BIND_L3) { - if (!socket_bind_l3_valid(IP_VER4, normal.l3_id)) return false; - tx.scope = IP_TX_BOUND_L3; - tx.index = normal.l3_id; - hint = &tx; - } else if (normal.kind == BIND_L2) { - l2_interface_t* l2 = l2_interface_find_by_index(normal.ifindex); - if (!l2 || !l2->is_up) return false; - tx.scope = IP_TX_BOUND_L2; - tx.index = normal.ifindex; - hint = &tx; - } else if (normal.kind == BIND_IP) { - uint32_t ip = 0; - memcpy(&ip, normal.ip, 4); - if (ipv4_is_multicast(ip) || ipv4_is_limited_broadcast(ip)) return false; - if (!ipv4_is_unspecified(ip)) { - l3_ipv4_interface_t* v4 = l3_ipv4_find_by_ip(ip); - if (!v4 || !socket_bind_l3_valid(IP_VER4, v4->l3_id)) return false; - tx.scope = IP_TX_BOUND_L3; - tx.index = v4->l3_id; - hint = &tx; - } - } else if (normal.kind != BIND_ANY && normal.kind != BIND_ANY4) return false; - } + if (use_spec && !socket_bind_build_tx_opts(spec, IP_VER4, &tx, &hint)) return false; if (!ipv4_build_tx_plan(dst, hint, out)) return false; return ipv4_tx_plan_valid(out); @@ -628,39 +620,8 @@ bool socket_bind_build_ipv6_tx_plan(const SockBindSpec* spec, bool use_spec, con if (!out || !dst) return false; ip_tx_opts_t tx; - memset(&tx, 0, sizeof(tx)); const ip_tx_opts_t* hint = NULL; - - if (use_spec) { - if (!spec) return false; - - SockBindSpec normal = *spec; - if (!socket_bind_normalize_spec(&normal)) return false; - if (normal.kind == BIND_ANY4) return false; - if (normal.ver && normal.ver != IP_VER6 && normal.kind != BIND_ANY && normal.kind != BIND_ANY6) return false; - - if (normal.kind == BIND_L3) { - if (!socket_bind_l3_valid(IP_VER6, normal.l3_id)) return false; - tx.scope = IP_TX_BOUND_L3; - tx.index = normal.l3_id; - hint = &tx; - } else if (normal.kind == BIND_L2) { - l2_interface_t* l2 = l2_interface_find_by_index(normal.ifindex); - if (!l2 || !l2->is_up) return false; - tx.scope = IP_TX_BOUND_L2; - tx.index = normal.ifindex; - hint = &tx; - } else if (normal.kind == BIND_IP) { - if (ipv6_is_multicast(normal.ip)) return false; - if (!ipv6_is_unspecified(normal.ip)) { - l3_ipv6_interface_t* v6 = l3_ipv6_find_by_ip(normal.ip); - if (!v6 || !socket_bind_l3_valid(IP_VER6, v6->l3_id)) return false; - tx.scope = IP_TX_BOUND_L3; - tx.index = v6->l3_id; - hint = &tx; - } - } else if (normal.kind != BIND_ANY && normal.kind != BIND_ANY6) return false; - } + if (use_spec && !socket_bind_build_tx_opts(spec, IP_VER6, &tx, &hint)) return false; if (!ipv6_build_tx_plan(dst, hint, out)) return false; return ipv6_tx_plan_valid(out); diff --git a/kernel/networking/transport_layer/socket_bind.h b/kernel/networking/transport_layer/socket_bind.h index 98c04110..9e2feaaf 100644 --- a/kernel/networking/transport_layer/socket_bind.h +++ b/kernel/networking/transport_layer/socket_bind.h @@ -14,13 +14,14 @@ extern "C" { #define SOCKET_BIND_MAX 1024 typedef uint32_t socket_bind_token_t; -bool socket_bind_normalize_spec(SockBindSpec* spec); -bool socket_bind_insert(ksocket_t* socket, protocol_t protocol, const SockBindSpec* spec, uint16_t port, uint32_t options, bool allow_reuse, socket_bind_token_t* out_token); +bool socket_bind_prepare_spec(SockBindSpec* spec, protocol_t protocol); +uint8_t socket_bind_match_score(const SockBindSpec* spec, ip_version_t ver, uint8_t l3_id, uint8_t ifindex, const void* ip_addr); +bool socket_bind_insert(ksocket_t* socket, protocol_t protocol, SockBindSpec* spec, uint16_t port, uint32_t options, bool allow_reuse, socket_bind_token_t* out_token); bool socket_bind_tcp_listen(socket_bind_token_t token); void socket_bind_remove(socket_bind_token_t token); void socket_bind_udp_set_remote(socket_bind_token_t token, const net_l4_endpoint* remote); -int socket_bind_alloc_ephemeral(ksocket_t* socket, protocol_t protocol, const SockBindSpec* spec, uint32_t options, socket_bind_token_t* out_token); -int socket_bind_alloc_ephemeral_l3(ksocket_t* socket, protocol_t protocol, uint8_t l3_id, uint32_t options, socket_bind_token_t* out_token); +int32_t socket_bind_alloc_ephemeral(ksocket_t* socket, protocol_t protocol, SockBindSpec* spec, uint32_t options, socket_bind_token_t* out_token); +int32_t socket_bind_alloc_ephemeral_l3(ksocket_t* socket, protocol_t protocol, uint8_t l3_id, uint32_t options, SockBindSpec* out_spec, socket_bind_token_t* out_token); ksocket_t* socket_bind_lookup(protocol_t protocol, ip_version_t ipver, uint8_t l3_id, uint8_t ifindex, const void* src_ip_addr, uint16_t src_port, const void* dst_ip_addr, uint16_t dst_port); ksocket_t* socket_bind_udp_next_fanout(ip_version_t ipver, uint8_t l3_id, uint8_t ifindex, const void* dst_ip_addr, uint16_t dst_port, uint32_t* cursor); diff --git a/kernel/networking/transport_layer/tcp.h b/kernel/networking/transport_layer/tcp.h index 7a435529..dbb1a0d6 100644 --- a/kernel/networking/transport_layer/tcp.h +++ b/kernel/networking/transport_layer/tcp.h @@ -90,7 +90,7 @@ bool tcp_get_ctx(uint16_t local_port, ip_version_t ver, const void *local_ip, co bool tcp_bind_conflicts(const SockBindSpec* spec, uint16_t port, bool reuseaddr); bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, tcp_data *flow_ctx, const SocketOptions* extra); -void tcp_flow_apply_socket_options(tcp_data *flow_ctx, const SocketOptions* extra); +void tcp_flow_apply_socket_options(tcp_data *flow_ctx, const SocketOptions* extra, uint32_t apply_mask); tcp_result_t tcp_flow_send(tcp_data *flow_ctx); tcp_result_t tcp_flow_flush(tcp_data *flow_ctx); @@ -106,9 +106,6 @@ void tcp_flow_on_app_read(tcp_data *flow_ctx, uint32_t bytes_read); void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_addr, uint8_t l3_id, netpkt_t* pkt); -void tcp_tick_all(uint32_t elapsed_ms); -int tcp_daemon_entry(int argc, char *argv[]); - #ifdef __cplusplus } #endif diff --git a/kernel/networking/transport_layer/tcp/tcp_core.c b/kernel/networking/transport_layer/tcp/tcp_core.c index 3793092e..6945eacd 100644 --- a/kernel/networking/transport_layer/tcp/tcp_core.c +++ b/kernel/networking/transport_layer/tcp/tcp_core.c @@ -10,6 +10,7 @@ #include "syscalls/syscalls.h" #include "networking/transport_layer/trans_utils.h" #include "networking/transport_layer/socket_core.h" +#include "networking/transport_layer/socket_bind.h" #include "exceptions/irq.h" tcp_flow_t *tcp_flows[MAX_TCP_FLOWS]; @@ -25,29 +26,52 @@ static uint16_t tcp_active_port_lower_bound(uint16_t local_port) { while (lo < hi) { uint16_t mid = lo + ((hi - lo) >> 1); tcp_flow_t *flow = tcp_flows[tcp_active_flows[mid]]; - if (flow->base.local_port < local_port) lo = (uint16_t)(mid + 1); + if (flow->base.local.port < local_port) lo = (uint16_t)(mid + 1); else hi = mid; } return lo; } -void tcp_active_insert_flow(tcp_flow_t *flow) { - if (!flow) return; +static bool tcp_flow_tuple_matches(const tcp_flow_t *flow, uint16_t local_port, ip_version_t ver, const void *local_ip, const void *remote_ip, uint16_t remote_port) { + if (!flow || !local_ip || !remote_ip || (ver != IP_VER4 && ver != IP_VER6)) return false; + if (flow->base.local.port != local_port || flow->base.remote.port != remote_port) return false; + if (flow->base.local.ver != ver || flow->base.remote.ver != ver) return false; + + size_t ip_len = ver == IP_VER6 ? 16 : 4; + if (memcmp(flow->base.local.ip, local_ip, ip_len) != 0) return false; + if (memcmp(flow->base.remote.ip, remote_ip, ip_len) != 0) return false; + return true; +} + +bool tcp_active_insert_flow(tcp_flow_t *flow) { + if (!flow) return false; irq_flags_t irq = irq_save_disable(); uint16_t slot = flow->base.slot; - if (slot >= MAX_TCP_FLOWS || tcp_flows[slot] != flow || flow->base.retired || tcp_active_count >= MAX_TCP_FLOWS) { + if (slot >= MAX_TCP_FLOWS || tcp_flows[slot] != flow || flow->base.retired || flow->base.active_pos != UINT16_MAX || tcp_active_count >= MAX_TCP_FLOWS) { irq_restore(irq); - return; + return false; } - uint16_t local_port = flow->base.local_port; + uint16_t local_port = flow->base.local.port; uint16_t pos = tcp_active_port_lower_bound(local_port); + for (uint16_t n = pos; n < tcp_active_count; ++n) { + uint16_t cur_slot = tcp_active_flows[n]; + tcp_flow_t *cur = cur_slot < MAX_TCP_FLOWS ? tcp_flows[cur_slot] : NULL; + if (!cur) continue; + if (cur->base.local.port > local_port) break; + if (cur->base.retired || cur->base.state == TCP_STATE_CLOSED) continue; + if (tcp_flow_tuple_matches(cur, local_port, flow->base.local.ver, flow->base.local.ip, flow->base.remote.ip, flow->base.remote.port)) { + irq_restore(irq); + return false; + } + } + while (pos < tcp_active_count) { uint16_t cur_slot = tcp_active_flows[pos]; tcp_flow_t *cur = cur_slot < MAX_TCP_FLOWS ? tcp_flows[cur_slot] : NULL; - uint16_t cur_port = cur ? cur->base.local_port : UINT16_MAX; + uint16_t cur_port = cur ? cur->base.local.port : UINT16_MAX; if (cur_port != local_port || cur_slot > slot) break; pos++; } @@ -62,36 +86,29 @@ void tcp_active_insert_flow(tcp_flow_t *flow) { flow->base.active_pos = pos; tcp_active_count++; irq_restore(irq); + return true; } -tcp_flow_t *tcp_flow_acquire_match(uint16_t local_port, ip_version_t ver, const void *local_ip, const void *remote_ip, uint16_t remote_port, int *out_idx){ +tcp_flow_t *tcp_flow_acquire_match(uint16_t local_port, ip_version_t ver, const void *local_ip, const void *remote_ip, uint16_t remote_port){ irq_flags_t irq = irq_save_disable(); uint16_t start = tcp_active_port_lower_bound(local_port); for (uint16_t n = start; n < tcp_active_count; n++){ - int i = tcp_active_flows[n]; - tcp_flow_t *f = i < MAX_TCP_FLOWS ? tcp_flows[i] : NULL; + uint16_t slot = tcp_active_flows[n]; + tcp_flow_t *f = slot < MAX_TCP_FLOWS ? tcp_flows[slot] : NULL; if (!f) continue; - if (f->base.local_port > local_port) break; + if (f->base.local.port > local_port) break; if (f->base.retired || f->base.state == TCP_STATE_CLOSED) continue; - if (f->base.local_port != local_port) continue; - - if (!remote_ip || !local_ip) continue; - if (f->base.local.ver != ver || f->base.remote.ver != ver) continue; - if (f->base.remote.port != remote_port) continue; + if (f->base.local.port != local_port) continue; - size_t l = (size_t)(ver == IP_VER6 ? 16 : 4); - if (memcmp(f->base.local.ip, local_ip, l) != 0) continue; - if (memcmp(f->base.remote.ip, remote_ip, l) != 0) continue; + if (!tcp_flow_tuple_matches(f, local_port, ver, local_ip, remote_ip, remote_port)) continue; if (!f->base.refs || f->base.refs == UINT16_MAX) break; f->base.refs++; - if (out_idx) *out_idx = i; irq_restore(irq); return f; } - if (out_idx) *out_idx = -1; irq_restore(irq); return NULL; } @@ -106,9 +123,10 @@ bool tcp_bind_conflicts(const SockBindSpec* spec, uint16_t port, bool reuseaddr) uint16_t slot = tcp_active_flows[n]; tcp_flow_t* flow = slot < MAX_TCP_FLOWS ? tcp_flows[slot] : NULL; if (!flow) continue; - if (flow->base.local_port > port) break; + if (flow->base.local.port > port) break; if (flow->base.retired || flow->base.state == TCP_STATE_CLOSED) continue; - if (flow->base.local_port != port || !tcp_flow_matches_bind(flow, spec)) continue; + uint8_t ifindex = l3_ifindex_from_id(flow->base.l3_id); + if (flow->base.local.port != port || !socket_bind_match_score(spec, flow->base.local.ver, flow->base.l3_id, ifindex, flow->base.local.ip)) continue; bool allowed = reuseaddr && flow->ip.reuseaddr; if (!allowed) { @@ -122,27 +140,13 @@ bool tcp_bind_conflicts(const SockBindSpec* spec, uint16_t port, bool reuseaddr) bool tcp_get_ctx(uint16_t local_port, ip_version_t ver, const void *local_ip, const void *remote_ip, uint16_t remote_port, tcp_data *out_ctx){ if (!out_ctx) return false; - tcp_flow_t *flow = tcp_flow_acquire_match(local_port, ver, local_ip, remote_ip, remote_port, NULL); + tcp_flow_t *flow = tcp_flow_acquire_match(local_port, ver, local_ip, remote_ip, remote_port); if (!flow) return false; *out_ctx = flow->base.ctx; tcp_flow_put(flow); return true; } -int tcp_flow_hold(tcp_flow_t *flow) { - if (!flow)return 0; - - irq_flags_t irq = irq_save_disable(); - if (flow->base.retired || !flow->base.refs || flow->base.refs == UINT16_MAX) { - irq_restore(irq); - return 0; - } - - flow->base.refs++; - irq_restore(irq); - return 1; -} - tcp_flow_t *tcp_flow_from_ctx(tcp_data *flow_ctx) { if (!flow_ctx) return NULL; if (flow_ctx->flow_index >= MAX_TCP_FLOWS) return NULL; @@ -159,44 +163,49 @@ tcp_flow_t *tcp_flow_from_ctx(tcp_data *flow_ctx) { return flow; } -void tcp_update_mss(tcp_flow_t *flow) { +void tcp_flow_apply_options(tcp_flow_t *flow, const SocketOptions* extra, uint32_t apply_mask) { if (!flow) return; - uint32_t local_mss = flow->tx.path_mss ? flow->tx.path_mss : TCP_DEFAULT_MSS; - if (flow->tx.configured_mss && flow->tx.configured_mss < local_mss) local_mss = flow->tx.configured_mss; - if (!local_mss) local_mss = 1; - flow->tx.advertised_mss = local_mss; - - uint32_t mss = local_mss; - if (flow->tx.peer_mss && flow->tx.peer_mss < mss) mss = flow->tx.peer_mss; - flow->tx.mss = mss; -} + SocketOptions defaults = {0}; + if (!extra) extra = &defaults; + uint32_t flags = extra->flags; + + if (apply_mask & (SOCK_OPT_KEEPALIVE | SOCK_OPT_KEEPALIVE_INTERVAL)) { + flow->timer.keepalive_on = (flags & SOCK_OPT_KEEPALIVE) ? 1 : 0; + flow->timer.keepalive_ms = flow->timer.keepalive_on ? (extra->keepalive_ms ? extra->keepalive_ms : SOCKET_DEFAULT_KEEPALIVE_MS) : 0; + flow->timer.keepalive_idle_ms = 0; + if (flow->base.active_pos != UINT16_MAX) tcp_daemon_kick(); + } -void tcp_flow_apply_socket_options(tcp_data *flow_ctx, const SocketOptions* extra) { - tcp_flow_t *flow = tcp_flow_from_ctx(flow_ctx); - if (!flow) return; + if (apply_mask & SOCK_OPT_TCP_NO_DELAY) { + flow->tx.nodelay = (flags & SOCK_OPT_TCP_NO_DELAY) ? 1 : 0; + if (flow->tx.nodelay && flow->tx.nagle_len) tcp_flush_nagle(flow, 1); + } - uint32_t flags = extra ? extra->flags : 0; - flow->timer.keepalive_on = (flags & SOCK_OPT_KEEPALIVE) ? 1 : 0; - flow->timer.keepalive_ms = flow->timer.keepalive_on ? (extra->keepalive_ms ? extra->keepalive_ms : SOCKET_DEFAULT_KEEPALIVE_MS) : 0; - flow->timer.keepalive_idle_ms = 0; + if (apply_mask & SOCK_OPT_SEND_BUF_SIZE) { + uint32_t queued_limit = (flags & SOCK_OPT_SEND_BUF_SIZE) && extra->send_buf_size ? extra->send_buf_size : TCP_TX_MAX_BYTES_PER_FLOW; + if (queued_limit > TCP_TX_MAX_BYTES_PER_FLOW) queued_limit = TCP_TX_MAX_BYTES_PER_FLOW; + flow->tx.queued_limit = queued_limit; + } - flow->tx.nodelay = (flags & SOCK_OPT_TCP_NO_DELAY) ? 1 : 0; - if (flow->tx.nodelay && flow->tx.nagle_len) tcp_flush_nagle(flow, 1); + if (apply_mask & SOCK_OPT_TCP_MAXSEG) { + flow->tx.configured_mss = (flags & SOCK_OPT_TCP_MAXSEG) && extra->tcp_maxseg ? extra->tcp_maxseg : 0; + tcp_update_mss(flow); + } - uint32_t queued_limit = (flags & SOCK_OPT_SEND_BUF_SIZE) && extra->send_buf_size ? extra->send_buf_size : TCP_TX_MAX_BYTES_PER_FLOW; - if (queued_limit > TCP_TX_MAX_BYTES_PER_FLOW) queued_limit = TCP_TX_MAX_BYTES_PER_FLOW; - flow->tx.queued_limit = queued_limit; - flow->tx.configured_mss = (flags & SOCK_OPT_TCP_MAXSEG) && extra->tcp_maxseg ? extra->tcp_maxseg : 0; - tcp_update_mss(flow); + if (apply_mask & SOCK_OPT_TTL) flow->ip.ttl = (flags & SOCK_OPT_TTL) ? extra->ttl : 0; + if (apply_mask & SOCK_OPT_DONTFRAG) flow->ip.dontfrag = (flags & SOCK_OPT_DONTFRAG) ? 1 : 0; + if (apply_mask & SOCK_OPT_REUSEADDR) flow->ip.reuseaddr = (flags & SOCK_OPT_REUSEADDR) ? 1 : 0; +} - flow->ip.ttl = (flags & SOCK_OPT_TTL) ? extra->ttl : 0; - flow->ip.dontfrag = (flags & SOCK_OPT_DONTFRAG) ? 1 : 0; - flow->ip.reuseaddr = (flags & SOCK_OPT_REUSEADDR) ? 1 : 0; +void tcp_flow_apply_socket_options(tcp_data *flow_ctx, const SocketOptions* extra, uint32_t apply_mask) { + tcp_flow_t *flow = tcp_flow_from_ctx(flow_ctx); + if (!flow) return; + tcp_flow_apply_options(flow, extra, apply_mask); tcp_flow_put(flow); } static void tcp_flow_clear_storage(tcp_flow_t *flow) { - for (int i = 0; i < TCP_MAX_TX_SEGS; i++) tcp_tx_seg_clear(flow, &flow->tx.txq[i]); + for (uint32_t i = 0; i < TCP_MAX_TX_SEGS; i++) tcp_tx_seg_clear(flow, &flow->tx.txq[i]); flow->tx.queued_bytes = 0; if (flow->tx.nagle_buf) { @@ -241,7 +250,7 @@ void tcp_flow_put(tcp_flow_t *f) { } f->base.refs--; - uint8_t free_now = f->base.refs == 0; + bool free_now = f->base.refs == 0; irq_restore(irq); if (!free_now) return; @@ -255,7 +264,7 @@ void tcp_enter_time_wait(tcp_flow_t *flow) { if (!flow) return; uint32_t timewait = 0; - int oldest = -1; + tcp_flow_t *oldest = NULL; uint32_t oldest_ms = 0; irq_flags_t irq = irq_save_disable(); @@ -264,16 +273,19 @@ void tcp_enter_time_wait(tcp_flow_t *flow) { tcp_flow_t *f = slot < MAX_TCP_FLOWS ? tcp_flows[slot] : NULL; if (!f || f->base.retired || f->base.state != TCP_TIME_WAIT) continue; timewait++; - if ((oldest < 0 || f->timer.time_wait_ms > oldest_ms) && f != flow) { - oldest = slot; + if ((!oldest || f->timer.time_wait_ms > oldest_ms) && f != flow) { + oldest = f; oldest_ms = f->timer.time_wait_ms; } } + if (timewait >= TCP_TIMEWAIT_MAX_GLOBAL && oldest && oldest->base.refs && oldest->base.refs != UINT16_MAX) oldest->base.refs++; + else oldest = NULL; irq_restore(irq); - if (timewait >= TCP_TIMEWAIT_MAX_GLOBAL && oldest >= 0) { + if (timewait >= TCP_TIMEWAIT_MAX_GLOBAL && oldest) { tcp_stats.timewait_reap_oldest++; tcp_free_flow(oldest); + tcp_flow_put(oldest); } flow->base.state = TCP_TIME_WAIT; @@ -287,9 +299,9 @@ tcp_flow_t *tcp_alloc_flow(void){ if (!f) return NULL; irq_flags_t irq = irq_save_disable(); - int slot = -1; - for (int n = 0; n < MAX_TCP_FLOWS; n++) { - uint16_t i = (uint16_t)((tcp_alloc_cursor+n) % MAX_TCP_FLOWS); + int32_t slot = -1; + for (uint32_t n = 0; n < MAX_TCP_FLOWS; n++) { + uint16_t i = (uint16_t)((tcp_alloc_cursor + n) % MAX_TCP_FLOWS); if (!tcp_flows[i]) { slot = i; tcp_alloc_cursor = (uint16_t)((i + 1) % MAX_TCP_FLOWS); @@ -305,11 +317,12 @@ tcp_flow_t *tcp_alloc_flow(void){ tcp_flows[slot] = f; f->base.slot = (uint16_t)slot; + f->base.active_pos = UINT16_MAX; f->base.generation = tcp_generation_next++; if (!tcp_generation_next) tcp_generation_next = 1; f->base.ctx.flow_index = f->base.slot; f->base.ctx.flow_generation = f->base.generation; - f->base.refs = 1; + f->base.refs = 2; irq_restore(irq); f->tx.rto = TCP_INIT_RTO; @@ -325,25 +338,24 @@ tcp_flow_t *tcp_alloc_flow(void){ return f; } -void tcp_free_flow(int idx) { - if (idx < 0 || idx >= MAX_TCP_FLOWS) return; - +void tcp_free_flow(tcp_flow_t *flow) { + if (!flow) return; irq_flags_t irq = irq_save_disable(); - tcp_flow_t *f = tcp_flows[idx]; - if (!f || f->base.retired) { + uint16_t slot = flow->base.slot; + if (slot >= MAX_TCP_FLOWS || tcp_flows[slot] != flow || flow->base.retired) { irq_restore(irq); return; } - f->base.retired = 1; - f->base.state = TCP_STATE_CLOSED; + flow->base.retired = 1; + flow->base.state = TCP_STATE_CLOSED; - if (tcp_active_count != 0) { - uint16_t pos = f->base.active_pos; + if (tcp_active_count != 0 && flow->base.active_pos != UINT16_MAX) { + uint16_t pos = flow->base.active_pos; - if (pos >= tcp_active_count || tcp_active_flows[pos] != (uint16_t)idx) { + if (pos >= tcp_active_count || tcp_active_flows[pos] != slot) { pos = 0; - while (pos < tcp_active_count && tcp_active_flows[pos] != (uint16_t)idx) pos++; + while (pos < tcp_active_count && tcp_active_flows[pos] != slot) pos++; } if (pos < tcp_active_count) { @@ -356,25 +368,24 @@ void tcp_free_flow(int idx) { tcp_active_flows[tcp_active_count] = 0; } - f->base.active_pos = 0; + flow->base.active_pos = UINT16_MAX; } - tcp_flows[idx] = NULL; - if ((uint16_t)idx < tcp_alloc_cursor) tcp_alloc_cursor = (uint16_t)idx; + tcp_flows[slot] = NULL; + if (slot < tcp_alloc_cursor) tcp_alloc_cursor = slot; irq_restore(irq); - tcp_flow_put(f); + tcp_flow_put(flow); } tcp_result_t tcp_flow_abort(tcp_data *flow_ctx) { tcp_flow_t *flow = tcp_flow_from_ctx(flow_ctx); if (!flow) return TCP_INVALID; - int slot = (int)flow->base.slot; tcp_state_t state = flow->base.state; - if (state != TCP_STATE_CLOSED && state != TCP_TIME_WAIT && state != TCP_SYN_SENT) tcp_send_reset(flow->base.local.ver, flow->base.local.ip, flow->base.remote.ip, flow->base.local_port, flow->base.remote.port, flow->tx.snd_nxt, 0, false); + if (state != TCP_STATE_CLOSED && state != TCP_TIME_WAIT && state != TCP_SYN_SENT) tcp_send_reset(flow->base.l3_id, flow->base.local.ver, flow->base.local.ip, flow->base.remote.ip, flow->base.local.port, flow->base.remote.port, flow->tx.snd_nxt, 0, false); - tcp_free_flow(slot); + tcp_free_flow(flow); tcp_flow_put(flow); return TCP_OK; } @@ -398,23 +409,25 @@ tcp_result_t tcp_flow_release_closed(tcp_data *flow_ctx) { tcp_flow_put(flow); return TCP_BUSY; } - int slot = (int)flow->base.slot; + tcp_free_flow(flow); tcp_flow_put(flow); - tcp_free_flow(slot); return TCP_OK; } bool tcp_send_segment(ip_version_t ver, const void *src_ip_addr, const void *dst_ip_addr, tcp_hdr_t *hdr, const uint8_t *opts, uint8_t opts_len, const uint8_t *payload, uint16_t payload_len, const ip_tx_opts_t *txp, uint8_t ttl, uint8_t dontfrag){ - if (!hdr) return false; + if (!hdr || !src_ip_addr || !dst_ip_addr) return false; + if (ver != IP_VER4 && ver != IP_VER6) return false; if (opts_len & 3u) return false; if (opts_len > 40u) return false; if ((opts_len && !opts) || (payload_len && !payload)) return false; + uint32_t ip_header_len = ver == IP_VER4 ? (uint32_t)sizeof(ipv4_hdr_t) : (uint32_t)sizeof(ipv6_hdr_t); + uint32_t max_tcp_len = ver == IP_VER4 ? UINT16_MAX - ip_header_len : UINT16_MAX; uint32_t tcp_len32 = (uint32_t)sizeof(tcp_hdr_t) + (uint32_t)opts_len + (uint32_t)payload_len; - if (tcp_len32 > NETPKT_MAX_ALLOC) return false; + if (tcp_len32 > max_tcp_len || tcp_len32 > NETPKT_MAX_ALLOC) return false; uint16_t tcp_len = (uint16_t)tcp_len32; - uint32_t headroom = (uint32_t)sizeof(eth_hdr_t) + (uint32_t)(ver == IP_VER4 ? sizeof(ipv4_hdr_t) : sizeof(ipv6_hdr_t)); + uint32_t headroom = (uint32_t)sizeof(eth_hdr_t) + ip_header_len; netpkt_t *pkt = netpkt_alloc(tcp_len, headroom, 0); if (!pkt) return false; uint8_t *segment = (uint8_t*)netpkt_put(pkt, tcp_len); @@ -443,17 +456,15 @@ bool tcp_send_segment(ip_version_t ver, const void *src_ip_addr, const void *dst h.checksum = tcp_checksum_ipv4(segment, tcp_len, s, d); memcpy(segment, &h, sizeof(h)); return ipv4_send_packet(d, PROTO_TCP, pkt, txp, ttl, dontfrag); - } else if (ver == IP_VER6){ - h.checksum = tcp_checksum_ipv6(segment, tcp_len, (const uint8_t *)src_ip_addr, (const uint8_t *)dst_ip_addr); - memcpy(segment, &h, sizeof(h)); - return ipv6_send_packet((const uint8_t *)dst_ip_addr, PROTO_TCP, pkt, txp, ttl, dontfrag); } - netpkt_unref(pkt); - return false; + h.checksum = tcp_checksum_ipv6(segment, tcp_len, (const uint8_t *)src_ip_addr, (const uint8_t *)dst_ip_addr); + memcpy(segment, &h, sizeof(h)); + return ipv6_send_packet((const uint8_t *)dst_ip_addr, PROTO_TCP, pkt, txp, ttl, dontfrag); } -void tcp_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){ +void tcp_send_reset(uint8_t l3_id, 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){ + if (!l3_id) return; tcp_hdr_t rst_hdr; rst_hdr.src_port = bswap16(src_port); @@ -472,17 +483,10 @@ void tcp_send_reset(ip_version_t ver, const void *src_ip_addr, const void *dst_i rst_hdr.window = 0; rst_hdr.urgent_ptr = 0; - if (ver == IP_VER4){ - ip_tx_opts_t tx; - - tcp_build_tx_opts_from_local_v4(src_ip_addr, &tx); - tcp_send_segment(IP_VER4, src_ip_addr, dst_ip_addr, &rst_hdr, NULL, 0, NULL, 0, &tx, 0, 0); - } else if (ver == IP_VER6){ - ip_tx_opts_t tx; - - tcp_build_tx_opts_from_local_v6(src_ip_addr, &tx); - tcp_send_segment(IP_VER6, src_ip_addr, dst_ip_addr, &rst_hdr, NULL, 0, NULL, 0, &tx, 0, 0); - } + ip_tx_opts_t tx; + tx.scope = IP_TX_BOUND_L3; + tx.index = l3_id; + (void)tcp_send_segment(ver, src_ip_addr, dst_ip_addr, &rst_hdr, NULL, 0, NULL, 0, &tx, 0, 0); } void tcp_rtt_update(tcp_flow_t *flow, uint32_t sample_ms){ @@ -524,14 +528,6 @@ bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, tcp_flow_t *flow = tcp_alloc_flow(); if (!flow) return false; - if (!tcp_flow_hold(flow)) { - tcp_free_flow((int)flow->base.slot); - return false; - } - - int idx = (int)flow->base.slot; - - flow->base.local_port = local_port; flow->base.l3_id = l3_id; flow->base.remote.ver = dst->ver; @@ -542,7 +538,7 @@ bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, l3_ipv4_interface_t *v4 = l3_ipv4_find_by_id(l3_id); if (!v4 || !v4->ip){ - tcp_free_flow(idx); + tcp_free_flow(flow); tcp_flow_put(flow); return false; } @@ -552,7 +548,7 @@ bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, l3_ipv6_interface_t *v6 = l3_ipv6_find_by_id(l3_id); if (!v6 || ipv6_is_unspecified(v6->ip)){ - tcp_free_flow(idx); + tcp_free_flow(flow); tcp_flow_put(flow); return false; } @@ -567,7 +563,7 @@ bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, flow->base.retries = TCP_SYN_RETRIES; rng_t rng; - rng_init_random(&rng); + rng_init_random(&rng); uint32_t iss = rng_next32(&rng); flow->base.ctx.sequence = iss; @@ -585,7 +581,7 @@ bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, flow->rx.rcv_adv_edge = 0; flow->tx.path_mss = tcp_calc_mss_for_l3(l3_id, dst->ver, dst->ip); flow->tx.peer_mss = 0; - tcp_flow_apply_socket_options(&flow->base.ctx, extra); + tcp_flow_apply_options(flow, extra, UINT32_MAX); if (flow->rx.rcv_wnd_max > 65535u) { flow->tx.ws_send = 8; @@ -602,7 +598,7 @@ bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, flow->rx.rcv_wnd = 0; flow->rx.rcv_adv_edge = flow->rx.rcv_nxt; flow->base.ctx.window = 0; - tcp_free_flow(idx); + tcp_free_flow(flow); tcp_flow_put(flow); return false; } @@ -630,12 +626,11 @@ bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, flow->timer.time_wait_ms = 0; flow->timer.fin_wait2_ms = 0; - tcp_active_insert_flow(flow); - tcp_tx_seg_t *seg = tcp_alloc_tx_seg(flow); + tcp_tx_seg_t *seg = tcp_alloc_tx_seg(flow, 0); if (!seg){ - tcp_free_flow(idx); + tcp_free_flow(flow); tcp_flow_put(flow); return false; } @@ -657,14 +652,12 @@ bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, flow->base.ctx.sequence = flow->tx.snd_nxt; flow->base.ctx.expected_ack = flow->tx.snd_nxt; - if (!tcp_send_from_seg(flow, seg)) { - tcp_free_flow(idx); + if (!tcp_active_insert_flow(flow) || !tcp_send_from_seg(flow, seg)) { + tcp_free_flow(flow); tcp_flow_put(flow); return false; } - tcp_daemon_kick(); - *flow_ctx = flow->base.ctx; tcp_flow_put(flow); return true; diff --git a/kernel/networking/transport_layer/tcp/tcp_internal.h b/kernel/networking/transport_layer/tcp/tcp_internal.h index ac352133..d0e78871 100644 --- a/kernel/networking/transport_layer/tcp/tcp_internal.h +++ b/kernel/networking/transport_layer/tcp/tcp_internal.h @@ -18,6 +18,7 @@ extern "C" { #endif #define TCP_DEFAULT_MSS 1460 +#define TCP_MAX_MSS (NETPKT_MAX_ALLOC - sizeof(ipv4_hdr_t) - sizeof(tcp_hdr_t)) #define TCP_PERSIST_PROBE_BUFSZ 1 #define TCP_SEQ_LT(a,b) ((int32_t)((uint32_t)(a) - (uint32_t)(b)) < 0) @@ -44,7 +45,7 @@ typedef struct { uint8_t sack_retransmitted; uint8_t opts[40]; uint32_t seq; - uint64_t len; + uint32_t len; netpkt_t *pkt; uint32_t payload_off; uint32_t timer_ms; @@ -57,7 +58,6 @@ typedef struct { } tcp_reass_seg_t; typedef struct { - uint16_t local_port; uint16_t slot; uint16_t active_pos; uint32_t generation; @@ -169,24 +169,22 @@ extern uint16_t tcp_active_count; void tcp_enter_time_wait(tcp_flow_t *flow); tcp_flow_t *tcp_alloc_flow(void); -void tcp_free_flow(int idx); -void tcp_active_insert_flow(tcp_flow_t *flow); -//TODO REUSEADDR can create flows that rx cant distinguish +void tcp_free_flow(tcp_flow_t *flow); +bool tcp_active_insert_flow(tcp_flow_t *flow); tcp_flow_t *tcp_flow_from_ctx(tcp_data *flow_ctx); -tcp_flow_t *tcp_flow_acquire_match(uint16_t local_port, ip_version_t ver, const void *local_ip, const void *remote_ip, uint16_t remote_port, int *out_idx); -int tcp_flow_hold(tcp_flow_t *flow); +tcp_flow_t *tcp_flow_acquire_match(uint16_t local_port, ip_version_t ver, const void *local_ip, const void *remote_ip, uint16_t remote_port); void tcp_flow_put(tcp_flow_t *flow); +void tcp_flow_apply_options(tcp_flow_t *flow, const SocketOptions* extra, uint32_t apply_mask); void tcp_rtt_update(tcp_flow_t *flow, uint32_t sample_ms); -void tcp_update_mss(tcp_flow_t *flow); -bool tcp_flow_matches_bind(const tcp_flow_t *flow, const SockBindSpec *spec); -tcp_tx_seg_t *tcp_alloc_tx_seg(tcp_flow_t *flow); +tcp_tx_seg_t *tcp_alloc_tx_seg(tcp_flow_t *flow, uint32_t reserve_slots); const uint8_t *tcp_tx_seg_payload_ptr(const tcp_tx_seg_t *seg); void tcp_tx_seg_clear(tcp_flow_t *flow, tcp_tx_seg_t *seg); bool tcp_send_from_seg(tcp_flow_t *flow, tcp_tx_seg_t *seg); -void tcp_send_ack_now(tcp_flow_t *flow); -int tcp_try_send_pending_fin(tcp_flow_t *flow); +bool tcp_send_flow_segment(tcp_flow_t *flow, tcp_hdr_t *hdr, const uint8_t *opts, uint8_t opts_len, const uint8_t *payload, uint16_t payload_len); +bool tcp_send_ack_now(tcp_flow_t *flow); +bool tcp_try_send_pending_fin(tcp_flow_t *flow); uint64_t tcp_flush_nagle(tcp_flow_t *flow, uint8_t force); static inline uint16_t tcp_checksum_ipv4(const void *segment, uint16_t seg_len, uint32_t src_ip, uint32_t dst_ip) { @@ -199,12 +197,10 @@ static inline uint16_t tcp_checksum_ipv6(const void *segment, uint16_t seg_len, } bool tcp_send_segment(ip_version_t ver, const void *src_ip_addr, const void *dst_ip_addr, tcp_hdr_t *hdr, const uint8_t *opts, uint8_t opts_len, const uint8_t *payload, uint16_t payload_len, const ip_tx_opts_t *txp, uint8_t ttl, uint8_t dontfrag); -void tcp_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); +void tcp_send_reset(uint8_t l3_id, 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_tx_seg_t *tcp_find_first_unacked(tcp_flow_t *flow); void tcp_cc_on_timeout(tcp_flow_t *f); -int tcp_has_pending_timers(void); - void tcp_daemon_kick(void); uint16_t tcp_calc_adv_wnd_field(tcp_flow_t *flow, uint8_t apply_scale); diff --git a/kernel/networking/transport_layer/tcp/tcp_limits.c b/kernel/networking/transport_layer/tcp/tcp_limits.c index f33e9cfb..24faea40 100644 --- a/kernel/networking/transport_layer/tcp/tcp_limits.c +++ b/kernel/networking/transport_layer/tcp/tcp_limits.c @@ -108,14 +108,3 @@ tcp_admit_result_t tcp_admit_syn(struct ksocket* listener, ip_version_t ver, con return TCP_ADMIT_OK; } - -tcp_admit_result_t tcp_admit_tx(tcp_flow_t *flow, uint32_t bytes, uint32_t free_slots) { - if (!flow) return TCP_ADMIT_TX_FLOW_BYTES; - if (free_slots <= TCP_TX_CONTROL_RESERVE_SEGS) return TCP_ADMIT_TX_FLOW_SEGS; - uint32_t limit = flow->tx.queued_limit ? flow->tx.queued_limit : TCP_TX_MAX_BYTES_PER_FLOW; - if (flow->tx.queued_bytes >= limit) return TCP_ADMIT_TX_FLOW_BYTES; - if (tcp_tx_global_bytes >= TCP_TX_MAX_BYTES_GLOBAL) return TCP_ADMIT_TX_GLOBAL_BYTES; - if (bytes && (flow->tx.queued_bytes > limit || bytes > limit - flow->tx.queued_bytes)) return TCP_ADMIT_TX_FLOW_BYTES; - if (bytes && (tcp_tx_global_bytes > TCP_TX_MAX_BYTES_GLOBAL || bytes > TCP_TX_MAX_BYTES_GLOBAL - tcp_tx_global_bytes)) return TCP_ADMIT_TX_GLOBAL_BYTES; - return TCP_ADMIT_OK; -} diff --git a/kernel/networking/transport_layer/tcp/tcp_limits.h b/kernel/networking/transport_layer/tcp/tcp_limits.h index 238bc32f..ab8075cf 100644 --- a/kernel/networking/transport_layer/tcp/tcp_limits.h +++ b/kernel/networking/transport_layer/tcp/tcp_limits.h @@ -39,9 +39,6 @@ typedef enum { TCP_ADMIT_SYN_LISTENER, TCP_ADMIT_SYN_SOURCE, TCP_ADMIT_FLOW_RESERVE, - TCP_ADMIT_TX_FLOW_BYTES, - TCP_ADMIT_TX_FLOW_SEGS, - TCP_ADMIT_TX_GLOBAL_BYTES, TCP_ADMIT_FLOW_TABLE_FULL } tcp_admit_result_t; @@ -70,7 +67,6 @@ extern tcp_stats_t tcp_stats; uint32_t tcp_clamp_rcvbuf(uint32_t size); tcp_admit_result_t tcp_admit_syn(struct ksocket *listener, ip_version_t ver, const void *src_ip); tcp_admit_result_t tcp_admit_ooo(struct tcp_flow *flow, uint32_t increase, uint32_t remaining_nodes); -tcp_admit_result_t tcp_admit_tx(struct tcp_flow *flow, uint32_t bytes, uint32_t free_slots); void tcp_account_ooo_add(uint32_t bytes, uint32_t segs); void tcp_account_ooo_remove(uint32_t bytes, uint32_t segs); void tcp_account_tx_add(struct tcp_flow *flow, uint32_t bytes); diff --git a/kernel/networking/transport_layer/tcp/tcp_rx.c b/kernel/networking/transport_layer/tcp/tcp_rx.c index b8bf116d..3f970c1a 100644 --- a/kernel/networking/transport_layer/tcp/tcp_rx.c +++ b/kernel/networking/transport_layer/tcp/tcp_rx.c @@ -12,7 +12,7 @@ #include "syscalls/syscalls.h" #include "../tcp.h" -static int16_t tcp_reass_count_ok(tcp_flow_t *flow) { +static bool tcp_reass_count_ok(tcp_flow_t *flow) { if (!flow) return 0; if (flow->rx.reass_count <= TCP_REASS_MAX_SEGS) return 1; @@ -23,7 +23,7 @@ static int16_t tcp_reass_count_ok(tcp_flow_t *flow) { flow->rx.sack_recent_left = 0; flow->rx.sack_recent_right = 0; - for (int i = 0; i < TCP_REASS_MAX_SEGS; i++) { + for (uint32_t i = 0; i < TCP_REASS_MAX_SEGS; i++) { flow->rx.reass[i].seq = 0; flow->rx.reass[i].end = 0; } @@ -31,7 +31,7 @@ static int16_t tcp_reass_count_ok(tcp_flow_t *flow) { return 0; } -static void tcp_reass_remove(tcp_flow_t *flow, int idx) { +static void tcp_reass_remove(tcp_flow_t *flow, int32_t idx) { if (!tcp_reass_count_ok(flow)) return; if (idx < 0 || idx >= flow->rx.reass_count) return; @@ -42,21 +42,21 @@ static void tcp_reass_remove(tcp_flow_t *flow, int idx) { else flow->rx.rcv_ooo_used = 0; tcp_account_ooo_remove(len, 1); - for (int i = idx; i + 1 < flow->rx.reass_count; i++) flow->rx.reass[i] = flow->rx.reass[i+1]; + for (int32_t i = idx; i + 1 < flow->rx.reass_count; i++) flow->rx.reass[i] = flow->rx.reass[i+1]; if (flow->rx.reass_count) flow->rx.reass_count--; flow->rx.reass[flow->rx.reass_count].seq = 0; flow->rx.reass[flow->rx.reass_count].end = 0; } -static int tcp_reass_drain_inseq(tcp_flow_t *flow) { - int advanced = 0; +static bool tcp_reass_drain_inseq(tcp_flow_t *flow) { + bool advanced = false; if (!tcp_reass_count_ok(flow)) return 0; for(;;){ - int idx = -1; + int32_t idx = -1; - for (int i = 0; i < flow->rx.reass_count; i++){ + for (int32_t i = 0; i < flow->rx.reass_count; i++){ if (flow->rx.reass[i].seq != flow->rx.rcv_nxt) continue; idx = i; break; @@ -74,7 +74,7 @@ static int tcp_reass_drain_inseq(tcp_flow_t *flow) { flow->rx.rcv_data_nxt = end; flow->base.ctx.ack = flow->rx.rcv_nxt; tcp_reass_remove(flow, idx); - advanced = 1; + advanced = true; } if (advanced) tcp_calc_adv_wnd_field(flow, 1); @@ -147,7 +147,7 @@ tcp_tx_seg_t *tcp_find_first_unacked(tcp_flow_t *flow) { tcp_tx_seg_t *best = NULL; uint32_t best_seq = 0; - for (int i = 0; i < TCP_MAX_TX_SEGS; i++){ + for (uint32_t i = 0; i < TCP_MAX_TX_SEGS; i++){ tcp_tx_seg_t *s = &flow->tx.txq[i]; if (!s->used) continue; @@ -164,10 +164,10 @@ tcp_tx_seg_t *tcp_find_first_unacked(tcp_flow_t *flow) { return best; } -static int tcp_apply_sack_blocks(tcp_flow_t *flow, const tcp_parsed_opts_t *opts) { +static bool tcp_apply_sack_blocks(tcp_flow_t *flow, const tcp_parsed_opts_t *opts) { if (!flow || !opts || !flow->tx.sack_ok || !opts->sack_count) return 0; - int changed = 0; + bool changed = false; for (uint32_t b = 0; b < opts->sack_count; b++) { uint32_t left = opts->sacks[b].left; uint32_t right = opts->sacks[b].right; @@ -176,7 +176,7 @@ static int tcp_apply_sack_blocks(tcp_flow_t *flow, const tcp_parsed_opts_t *opts if (TCP_SEQ_LEQ(right, flow->tx.snd_una)) continue; if (TCP_SEQ_GEQ(left, flow->tx.snd_nxt)) continue; - for (int i = 0; i < TCP_MAX_TX_SEGS; i++) { + for (uint32_t i = 0; i < TCP_MAX_TX_SEGS; i++) { tcp_tx_seg_t *s = &flow->tx.txq[i]; if (!s->used) continue; @@ -185,7 +185,7 @@ static int tcp_apply_sack_blocks(tcp_flow_t *flow, const tcp_parsed_opts_t *opts if (TCP_SEQ_LEQ(end, flow->tx.snd_una)) continue; if (TCP_SEQ_LT(s->seq, left) || TCP_SEQ_GT(end, right)) continue; - if (!s->sacked) changed = 1; + if (!s->sacked) changed = true; s->sacked = 1; } } @@ -199,7 +199,7 @@ static tcp_tx_seg_t *tcp_find_sack_retransmit(tcp_flow_t *flow) { uint32_t best_seq = 0; uint32_t highest_sacked = flow->tx.snd_una; - for (int i = 0; i < TCP_MAX_TX_SEGS; i++) { + for (uint32_t i = 0; i < TCP_MAX_TX_SEGS; i++) { tcp_tx_seg_t *s = &flow->tx.txq[i]; if (!s->used || !s->sacked) continue; @@ -211,7 +211,7 @@ static tcp_tx_seg_t *tcp_find_sack_retransmit(tcp_flow_t *flow) { if (highest_sacked == flow->tx.snd_una) return NULL; - for (int i = 0; i < TCP_MAX_TX_SEGS; i++) { + for (uint32_t i = 0; i < TCP_MAX_TX_SEGS; i++) { tcp_tx_seg_t *s = &flow->tx.txq[i]; if (!s->used)continue; @@ -245,7 +245,7 @@ void tcp_cc_on_timeout(tcp_flow_t *f){ f->tx.in_fast_recovery = 0; f->tx.recover = 0; - for (int i = 0; i < TCP_MAX_TX_SEGS; i++) { + for (uint32_t i = 0; i < TCP_MAX_TX_SEGS; i++) { f->tx.txq[i].sacked = 0; f->tx.txq[i].sack_retransmitted = 0; } @@ -291,8 +291,7 @@ static void tcp_cc_on_dupack(tcp_flow_t *f) { f->tx.cwnd += mss; tcp_tx_seg_t *s = tcp_find_sack_retransmit(f); - if (s) { - tcp_send_from_seg(f, s); + if (s && tcp_send_from_seg(f, s)) { s->sack_retransmitted = 1; s->retransmit_cnt++; s->timer_ms = 0; @@ -316,8 +315,7 @@ static void tcp_cc_on_dupack(tcp_flow_t *f) { tcp_tx_seg_t *s = tcp_find_sack_retransmit(f); if (!s) s = tcp_find_first_unacked(f); - if (s) { - tcp_send_from_seg(f, s); + if (s && tcp_send_from_seg(f, s)) { s->sack_retransmitted = 1; s->retransmit_cnt++; s->timer_ms = 0; @@ -379,10 +377,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a } tcp_parse_options(parsed_opts_len ? parsed_opts_buf : NULL, parsed_opts_len, &parsed_opts); - int idx = -1; - tcp_flow_t *flow = tcp_flow_acquire_match(dst_port, ipver, dst_ip_addr, src_ip_addr, src_port, &idx); - if (flow) flow->timer.keepalive_idle_ms = 0; - if (flow) flow->base.l3_id = l3_id; + tcp_flow_t *flow = tcp_flow_acquire_match(dst_port, ipver, dst_ip_addr, src_ip_addr, src_port); uint8_t ifx = 0; if (ipver == IP_VER4) { @@ -402,24 +397,31 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a } ifx = v6->l2->ifindex; } - + + if (flow) { + flow->timer.keepalive_idle_ms = 0; + if (flow->base.l3_id != l3_id) { + flow->base.l3_id = l3_id; + flow->tx.path_mss = tcp_calc_mss_for_l3(l3_id, ipver, src_ip_addr); + tcp_update_mss(flow); + } + } + if (flow && flow->base.state == TCP_TIME_WAIT && (flags & (1 << SYN_F)) && !(flags& ((1 << ACK_F) | (1 << RST_F) | (1 << FIN_F))) && data_len == 0) { ksocket_t* listener = socket_bind_lookup(PROTO_TCP, ipver, l3_id, ifx, src_ip_addr, src_port, dst_ip_addr, dst_port); if (listener) { socket_core_put(listener); - tcp_free_flow(idx); + tcp_free_flow(flow); tcp_flow_put(flow); flow = NULL; - idx = -1; } } if (flow && flow->base.state == TCP_SYN_RECEIVED && (flags & (1 << SYN_F)) && !(flags& ((1 << ACK_F) | (1 << RST_F))) && data_len == 0 && seq+1 == flow->rx.rcv_nxt) { - for (int i = 0; i < TCP_MAX_TX_SEGS; i++) { + for (uint32_t i = 0; i < TCP_MAX_TX_SEGS; i++) { tcp_tx_seg_t *seg = &flow->tx.txq[i]; if (!seg->used || !seg->syn) continue; - seg->timer_ms = 0; - tcp_send_from_seg(flow, seg); + if (tcp_send_from_seg(flow, seg)) seg->timer_ms = 0; break; } tcp_flow_put(flow); @@ -453,16 +455,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a netpkt_unref(pkt); return; } - if (!tcp_flow_hold(nf)) { - tcp_free_flow((int)nf->base.slot); - socket_core_put(listener); - netpkt_unref(pkt); - return; - } - flow = nf; - idx = (int)flow->base.slot; - flow->base.local_port = dst_port; flow->base.l3_id = l3_id; flow->base.remote.ver = ipver; @@ -527,7 +520,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a uint32_t rcvbuf = TCP_DEFAULT_RCV_BUF; if (listener_extra && (listener_extra->flags & SOCK_OPT_BUF_SIZE) && listener_extra->buf_size) rcvbuf = listener_extra->buf_size; flow->rx.rcv_wnd_max = tcp_clamp_rcvbuf(rcvbuf); - tcp_flow_apply_socket_options(&flow->base.ctx, listener_extra); + tcp_flow_apply_options(flow, listener_extra, UINT32_MAX); if (flow->rx.rcv_wnd_max > 65535u && pop.has_wscale) { flow->tx.ws_send = 8; flow->tx.ws_ok = 1; @@ -553,11 +546,10 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a flow->timer.fin_wait2_ms = 0; flow->base.listener = listener; listener = NULL; - tcp_active_insert_flow(flow); - tcp_tx_seg_t *seg = tcp_alloc_tx_seg(flow); + tcp_tx_seg_t *seg = tcp_alloc_tx_seg(flow, 0); if (!seg) { - tcp_free_flow(idx); + tcp_free_flow(flow); tcp_flow_put(flow); netpkt_unref(pkt); return; @@ -576,14 +568,12 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a seg->opts_len = tcp_build_syn_options(seg->opts, (uint16_t)flow->tx.advertised_mss, flow->tx.ws_ok ? flow->tx.ws_send : 0xff, flow->tx.sack_ok); flow->tx.snd_nxt = iss + 1; flow->base.ctx.sequence = flow->tx.snd_nxt; - if (!tcp_send_from_seg(flow, seg)) { - tcp_free_flow(idx); + if (!tcp_active_insert_flow(flow) || !tcp_send_from_seg(flow, seg)) { + tcp_free_flow(flow); tcp_flow_put(flow); netpkt_unref(pkt); return; } - - tcp_daemon_kick(); tcp_flow_put(flow); netpkt_unref(pkt); return; @@ -592,14 +582,14 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a if (listener) socket_core_put(listener); if (!(flags & (1u << RST_F))){ if (flags & (1u << ACK_F)){ - tcp_send_reset(ipver, dst_ip_addr, src_ip_addr, dst_port, src_port, ack, 0, false); + tcp_send_reset(l3_id, ipver, dst_ip_addr, src_ip_addr, dst_port, src_port, ack, 0, false); } else { uint32_t seg_len = data_len; if (flags & (1u << SYN_F)) seg_len++; if (flags & (1u << FIN_F)) seg_len++; - tcp_send_reset(ipver, dst_ip_addr, src_ip_addr, dst_port, src_port, seq, seq + seg_len, true); + tcp_send_reset(l3_id, ipver, dst_ip_addr, src_ip_addr, dst_port, src_port, seq, seq + seg_len, true); } } @@ -640,9 +630,6 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a flow->timer.persist_timer_ms = 0; flow->timer.persist_timeout_ms = 0; flow->timer.persist_probe_cnt = 0; - if (!old_wnd) tcp_daemon_kick(); - } else { - tcp_daemon_kick(); } uint8_t fin = (flags & (1u << FIN_F)) ? 1u : 0u; @@ -655,7 +642,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a flow->base.ctx.ack_received = ack; flow->tx.dup_acks = 0; - for (int i = 0; i < TCP_MAX_TX_SEGS; i++){ + for (uint32_t i = 0; i < TCP_MAX_TX_SEGS; i++){ tcp_tx_seg_t *s = &flow->tx.txq[i]; if (!s->used) continue; @@ -667,7 +654,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a } } - for (int i = 0; i < TCP_MAX_TX_SEGS; i++) { + for (uint32_t i = 0; i < TCP_MAX_TX_SEGS; i++) { tcp_tx_seg_t *s = &flow->tx.txq[i]; if (!s->used) continue; if (TCP_SEQ_GEQ(s->seq, ack)) continue; @@ -689,13 +676,13 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a netpkt_unref(pkt); return; } else if (flow->base.state == TCP_LAST_ACK && TCP_SEQ_GEQ(ack, flow->base.ctx.expected_ack)){ - tcp_free_flow(idx); + tcp_free_flow(flow); tcp_flow_put(flow); netpkt_unref(pkt); return; } } else if (ack == flow->tx.snd_una && data_len == 0 && !fin && !(flags & (1u << SYN_F))){ - int sack_changed = flow->tx.sack_ok && parsed_opts.sack_count ? tcp_apply_sack_blocks(flow, &parsed_opts) : 0; + bool sack_changed = flow->tx.sack_ok && parsed_opts.sack_count && tcp_apply_sack_blocks(flow, &parsed_opts); if (TCP_SEQ_LT(flow->tx.snd_una, flow->tx.snd_nxt) && (new_wnd == old_wnd || sack_changed)) { if (flow->tx.dup_acks < UINT8_MAX) flow->tx.dup_acks++; tcp_cc_on_dupack(flow); @@ -706,7 +693,10 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a } if (flow->tx.nagle_len) tcp_flush_nagle(flow, flow->tx.fin_tx_pending ? 1 : 0); - if (flow->tx.fin_tx_pending) tcp_try_send_pending_fin(flow); + if (flow->tx.fin_tx_pending) { + tcp_try_send_pending_fin(flow); + if (flow->tx.fin_tx_pending) tcp_daemon_kick(); + } uint32_t seg_seq = seq; @@ -736,6 +726,8 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a flow->tx.path_mss = tcp_calc_mss_for_l3(l3_id, ipver, src_ip_addr); flow->tx.peer_mss = pop.has_mss && pop.mss ? pop.mss : 0; tcp_update_mss(flow); + flow->tx.cwnd = flow->tx.mss * TCP_INIT_CWND_SEGS; + if (flow->tx.cwnd < flow->tx.mss) flow->tx.cwnd = flow->tx.mss; uint32_t new_wnd = window; if (flow->tx.ws_ok && flow->tx.ws_recv) new_wnd <<= flow->tx.ws_recv; @@ -756,29 +748,8 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a } (void)tcp_calc_adv_wnd_field(flow, 1); - tcp_hdr_t final_ack; - final_ack.src_port = bswap16(flow->base.local_port); - final_ack.dst_port = bswap16(flow->base.remote.port); - final_ack.sequence = bswap32(flow->base.ctx.sequence); - final_ack.ack = bswap32(flow->base.ctx.ack); - final_ack.flags = (uint8_t)(1u << ACK_F); - final_ack.window = flow->base.ctx.window; - final_ack.urgent_ptr = 0; - - if (flow->base.local.ver == IP_VER4) { - ip_tx_opts_t tx; - tcp_build_tx_opts_from_local_v4(flow->base.local.ip, &tx); - tcp_send_segment(IP_VER4, flow->base.local.ip, flow->base.remote.ip, &final_ack, NULL, 0, NULL, 0, &tx, flow->ip.ttl, flow->ip.dontfrag); - } else { - ip_tx_opts_t tx; - tcp_build_tx_opts_from_local_v6(flow->base.local.ip, &tx); - tcp_send_segment(IP_VER6, flow->base.local.ip, flow->base.remote.ip, &final_ack, NULL, 0, NULL, 0, &tx, flow->ip.ttl, flow->ip.dontfrag); - } - + (void)tcp_send_ack_now(flow); flow->base.state = TCP_ESTABLISHED; - flow->timer.delayed_ack_pending = 0; - flow->timer.delayed_ack_timer_ms = 0; - tcp_daemon_kick(); } else if (flags & (1u << RST_F)){ flow->base.state = TCP_STATE_CLOSED; } @@ -792,8 +763,8 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a if (!flow->rx.rcv_buf && flow->rx.rcv_wnd_max) { flow->rx.rcv_buf = (uintptr_t)zalloc(flow->rx.rcv_wnd_max); if (!flow->rx.rcv_buf) { - tcp_send_reset(ipver, dst_ip_addr, src_ip_addr, dst_port, src_port, 0, flow->base.ctx.ack, true); - tcp_free_flow(idx); + tcp_send_reset(l3_id, ipver, dst_ip_addr, src_ip_addr, dst_port, src_port, 0, flow->base.ctx.ack, true); + tcp_free_flow(flow); tcp_flow_put(flow); netpkt_unref(pkt); return; @@ -808,7 +779,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a if (!queued) { tcp_stats.acceptq_drop_full++; tcp_hdr_t rst_hdr; - rst_hdr.src_port = bswap16(flow->base.local_port); + rst_hdr.src_port = bswap16(flow->base.local.port); rst_hdr.dst_port = bswap16(flow->base.remote.port); rst_hdr.sequence = bswap32(flow->tx.snd_nxt); rst_hdr.ack = bswap32(flow->base.ctx.ack); @@ -816,17 +787,9 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a rst_hdr.window = 0; rst_hdr.urgent_ptr = 0; - if (flow->base.local.ver == IP_VER4) { - ip_tx_opts_t tx; - tcp_build_tx_opts_from_local_v4(flow->base.local.ip, &tx); - (void)tcp_send_segment(IP_VER4, flow->base.local.ip, flow->base.remote.ip, &rst_hdr, NULL, 0, NULL, 0, &tx, flow->ip.ttl, flow->ip.dontfrag); - } else if (flow->base.local.ver == IP_VER6) { - ip_tx_opts_t tx; - tcp_build_tx_opts_from_local_v6(flow->base.local.ip, &tx); - (void)tcp_send_segment(IP_VER6, flow->base.local.ip, flow->base.remote.ip, &rst_hdr, NULL, 0, NULL, 0, &tx, flow->ip.ttl, flow->ip.dontfrag); - } + (void)tcp_send_flow_segment(flow, &rst_hdr, NULL, 0, NULL, 0); - tcp_free_flow(idx); + tcp_free_flow(flow); tcp_flow_put(flow); netpkt_unref(pkt); return; @@ -843,7 +806,6 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a flow->timer.delayed_ack_timer_ms = 0; flow->base.ctx.ack_received = ack; - tcp_daemon_kick(); if (!data_len && !fin) { tcp_flow_put(flow); netpkt_unref(pkt); @@ -851,7 +813,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a } break; } else if (flags & (1u << RST_F)){ - tcp_free_flow(idx); + tcp_free_flow(flow); tcp_flow_put(flow); netpkt_unref(pkt); return; @@ -874,15 +836,14 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a return; } - tcp_free_flow(idx); + tcp_free_flow(flow); tcp_flow_put(flow); netpkt_unref(pkt); return; } - int need_ack = 0; - int ack_immediate = 0; - int ack_defer = 0; + bool need_ack = false; + bool ack_immediate = false; if (data_len || fin) { uint32_t rcv_nxt = flow->rx.rcv_nxt; @@ -895,8 +856,8 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a uint32_t orig_end = seg_seq + orig_data_len + (fin ? 1u : 0u); if (TCP_SEQ_LEQ(orig_end, rcv_nxt) || TCP_SEQ_GEQ(seg_seq, wnd_end)) { - need_ack = 1; - ack_immediate = 1; + need_ack = true; + ack_immediate = true; } else { if (fin_in) { if (TCP_SEQ_LT(fin_seq, rcv_nxt) || TCP_SEQ_GEQ(fin_seq, wnd_end)) fin_in = 0; @@ -923,8 +884,8 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a } if (!data_len && !fin_in){ - need_ack = 1; - ack_immediate = 1; + need_ack = true; + ack_immediate = true; } else if (seg_seq == flow->rx.rcv_nxt) { if (data_len){ uint32_t offer = data_len; @@ -957,10 +918,10 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a flow->base.ctx.ack = flow->rx.rcv_nxt; } else if (data_len) { (void)tcp_calc_adv_wnd_field(flow, 1); - ack_immediate = 1; + ack_immediate = true; } - if (accepted < data_len) ack_immediate = 1; + if (accepted < data_len) ack_immediate = true; } if (fin_in) { @@ -974,15 +935,15 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a else if (old == TCP_FIN_WAIT_1) flow->base.state = TCP_CLOSING; else if (old == TCP_FIN_WAIT_2) tcp_enter_time_wait(flow); - ack_immediate = 1; + ack_immediate = true; } else { flow->rx.fin_pending = 1; flow->rx.fin_seq = fin_seq; } } - if (tcp_reass_drain_inseq(flow)) ack_immediate = 1; - if (had_reass) ack_immediate = 1; + if (tcp_reass_drain_inseq(flow)) ack_immediate = true; + if (had_reass) ack_immediate = true; if (flow->rx.fin_pending && flow->rx.fin_seq == flow->rx.rcv_nxt){ flow->rx.fin_pending = 0; @@ -995,13 +956,12 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a else if (old == TCP_FIN_WAIT_1) flow->base.state = TCP_CLOSING; else if (old == TCP_FIN_WAIT_2) tcp_enter_time_wait(flow); - ack_immediate = 1; + ack_immediate = true; } (void)tcp_calc_adv_wnd_field(flow, 1); - if (!ack_immediate && data_len) ack_defer = 1; - need_ack = 1; + need_ack = true; } else { if (!(flow->base.state == TCP_FIN_WAIT_1 || flow->base.state == TCP_FIN_WAIT_2 || flow->base.state == TCP_CLOSING || flow->base.state == TCP_LAST_ACK || flow->base.state == TCP_TIME_WAIT) && data_len && flow->rx.rcv_buf && flow->rx.rcv_wnd_max) { uint32_t ooo_seq = seg_seq; @@ -1032,15 +992,15 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a uint32_t merged_start = start; uint32_t merged_end = end; uint32_t old_bytes = 0; - int16_t overlapping = 0; - int16_t covered = 0; + uint8_t overlapping = 0; + bool covered = false; - for (int i = 0; ooo_len && i < flow->rx.reass_count; i++) { + for (uint32_t i = 0; ooo_len && i < flow->rx.reass_count; i++) { tcp_reass_seg_t *r = &flow->rx.reass[i]; if (TCP_SEQ_LEQ(r->seq, start) && TCP_SEQ_GEQ(r->end, end)) { flow->rx.sack_recent_left = r->seq; flow->rx.sack_recent_right = r->end; - covered = 1; + covered = true; break; } @@ -1072,14 +1032,14 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a uint32_t cap = flow->rx.rcv_wnd_max; uint32_t pos = start % cap; uint32_t first = cap - pos; - int copied = 1; + bool copied = true; if (first > ooo_len) first = ooo_len; - if (first && !netpkt_copyout(pkt, ooo_data, rx + pos, first)) copied = 0; - if (copied && ooo_len > first && !netpkt_copyout(pkt, ooo_data + first, rx, ooo_len - first)) copied = 0; + if (first && !netpkt_copyout(pkt, ooo_data, rx + pos, first)) copied = false; + if (copied && ooo_len > first && !netpkt_copyout(pkt, ooo_data + first, rx, ooo_len - first)) copied = false; if (copied) { - for (int i = 0; i < flow->rx.reass_count;) { + for (uint32_t i = 0; i < flow->rx.reass_count;) { tcp_reass_seg_t *r = &flow->rx.reass[i]; if (TCP_SEQ_LT(r->end, merged_start) || TCP_SEQ_GT(r->seq, merged_end)) { i++; @@ -1090,7 +1050,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a } if (flow->rx.reass_count < TCP_REASS_MAX_SEGS) { - int pos_idx = flow->rx.reass_count; + uint32_t pos_idx = flow->rx.reass_count; while (pos_idx > 0 && TCP_SEQ_GT(flow->rx.reass[pos_idx - 1].seq, merged_start)) { flow->rx.reass[pos_idx] = flow->rx.reass[pos_idx - 1]; pos_idx--; @@ -1115,31 +1075,20 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a flow->rx.fin_seq = fin_seq; } - need_ack = 1; - ack_immediate = 1; + need_ack = true; + ack_immediate = true; } } } if (need_ack){ - if (ack_immediate){ - tcp_send_ack_now(flow); - } else if (ack_defer){ - if (!flow->timer.delayed_ack_pending){ - flow->timer.delayed_ack_pending = 1; - flow->timer.delayed_ack_timer_ms = 0; - tcp_daemon_kick(); - } else { - tcp_send_ack_now(flow); - } - } else { + if (ack_immediate) tcp_send_ack_now(flow); + else { if (!flow->timer.delayed_ack_pending){ flow->timer.delayed_ack_pending = 1; flow->timer.delayed_ack_timer_ms = 0; tcp_daemon_kick(); - } else { - tcp_send_ack_now(flow); - } + } else tcp_send_ack_now(flow); } } tcp_flow_put(flow); @@ -1156,7 +1105,7 @@ void tcp_flow_on_app_read(tcp_data *flow_ctx, uint32_t bytes_read){ uint16_t old_adv_field = flow->base.ctx.window; uint32_t old_nxt = flow->rx.rcv_nxt; - int advanced = tcp_reass_drain_inseq(flow); + bool advanced = tcp_reass_drain_inseq(flow); tcp_calc_adv_wnd_field(flow, 1); if (flow->base.state != TCP_STATE_CLOSED && flow->base.state != TCP_TIME_WAIT) { diff --git a/kernel/networking/transport_layer/tcp/tcp_timer.c b/kernel/networking/transport_layer/tcp/tcp_timer.c index 09c7154d..028dc820 100644 --- a/kernel/networking/transport_layer/tcp/tcp_timer.c +++ b/kernel/networking/transport_layer/tcp/tcp_timer.c @@ -2,32 +2,23 @@ #include "kernel_processes/kprocess_loader.h" #include "exceptions/irq.h" -static volatile int tcp_daemon_running = 0; +static volatile int tcp_daemon_running; +static int tcp_daemon_entry(int argc, char *argv[]); -static void tcp_timer_send_ack_segment(tcp_flow_t *f, uint32_t seq, const uint8_t *payload, uint16_t payload_len) { +static bool tcp_timer_send_ack_segment(tcp_flow_t *flow, uint32_t seq, const uint8_t *payload, uint16_t payload_len) { tcp_hdr_t hdr; - hdr.src_port = bswap16(f->base.local_port); - hdr.dst_port = bswap16(f->base.remote.port); + hdr.src_port = bswap16(flow->base.local.port); + hdr.dst_port = bswap16(flow->base.remote.port); hdr.sequence = bswap32(seq); - hdr.ack = bswap32(f->base.ctx.ack); + hdr.ack = bswap32(flow->base.ctx.ack); hdr.flags = (uint8_t)(1u << ACK_F); - hdr.window = tcp_calc_adv_wnd_field(f, 1); + hdr.window = tcp_calc_adv_wnd_field(flow, 1); hdr.urgent_ptr = 0; - - if (f->base.local.ver == IP_VER4) { - ip_tx_opts_t tx; - tcp_build_tx_opts_from_local_v4(f->base.local.ip, &tx); - (void)tcp_send_segment(IP_VER4, f->base.local.ip, f->base.remote.ip, &hdr, NULL, 0, payload, payload_len, &tx, f->ip.ttl, f->ip.dontfrag); - } else if (f->base.local.ver == IP_VER6) { - ip_tx_opts_t tx; - tcp_build_tx_opts_from_local_v6(f->base.local.ip, &tx); - (void)tcp_send_segment(IP_VER6, f->base.local.ip, f->base.remote.ip, &hdr, NULL, 0, payload, payload_len, &tx, f->ip.ttl, f->ip.dontfrag); - } + return tcp_send_flow_segment(flow, &hdr, NULL, 0, payload, payload_len); } + //TODO events void tcp_daemon_kick(void) { - if(!tcp_has_pending_timers()) return; - irq_flags_t irq_flags = irq_save_disable(); if(tcp_daemon_running){ irq_restore(irq_flags); @@ -36,206 +27,183 @@ void tcp_daemon_kick(void) { tcp_daemon_running = 1; irq_restore(irq_flags); - process_t *p = create_kernel_process("tcp_timer", tcp_daemon_entry, 0, 0); - if(!p){ + if (!create_kernel_process("tcp_timer", tcp_daemon_entry, 0, 0)) { irq_flags = irq_save_disable(); tcp_daemon_running = 0; irq_restore(irq_flags); } } -int tcp_has_pending_timers(void) { //TODO mhh this should be event driven to avoid MAX_TCP_FLOWS*TCP_MAX_TX_SEGS scans. - irq_flags_t irq = irq_save_disable(); - - for (uint16_t n = 0; n < tcp_active_count; n++) { - tcp_flow_t *f = tcp_flows[tcp_active_flows[n]]; - if (!f) continue; - if (f->base.retired || f->base.state == TCP_STATE_CLOSED) continue; - - if (f->base.state == TCP_TIME_WAIT || f->base.state == TCP_FIN_WAIT_2 || f->tx.fin_tx_pending || f->tx.nagle_len || f->timer.delayed_ack_pending || f->timer.persist_active || (!f->tx.fin_tx_pending && f->timer.keepalive_on && f->base.state == TCP_ESTABLISHED && f->timer.keepalive_ms)) { - irq_restore(irq); - return 1; - } +static int tcp_daemon_entry(int argc, char *argv[]) { + (void)argc; + (void)argv; + uint32_t last_tick_ms = (uint32_t)get_time(); - for (int j = 0; j < TCP_MAX_TX_SEGS; j++) { - tcp_tx_seg_t *seg = &f->tx.txq[j]; - if (!seg->used) continue; - uint32_t end_seq = seg->seq + seg->len + (seg->syn ? 1u : 0u) + (seg->fin ? 1u : 0u); - if (TCP_SEQ_GT(end_seq, f->tx.snd_una)) { - irq_restore(irq); - return 1; - } - } - } + while (true) { + uint32_t now_ms = (uint32_t)get_time(); + uint32_t elapsed_ms = now_ms - last_tick_ms; + last_tick_ms = now_ms; - irq_restore(irq); - return 0; -} + uint16_t active_slots[MAX_TCP_FLOWS]; + uint32_t active_generations[MAX_TCP_FLOWS]; + uint16_t active_count; -void tcp_tick_all(uint32_t elapsed_ms) { - for (uint16_t pos = tcp_active_count; pos > 0; pos--) { irq_flags_t irq = irq_save_disable(); - if (pos > tcp_active_count) { - irq_restore(irq); - continue; - } - - int i = tcp_active_flows[pos-1]; - tcp_flow_t *f = tcp_flows[i]; - if (!f || f->base.retired || f->base.state == TCP_STATE_CLOSED || !f->base.refs || f->base.refs == UINT16_MAX) { - irq_restore(irq); - continue; + active_count = tcp_active_count; + for (uint16_t i = 0; i < active_count; ++i) { + uint16_t slot = tcp_active_flows[i]; + tcp_flow_t* flow = slot < MAX_TCP_FLOWS ? tcp_flows[slot] : NULL; + active_slots[i] = slot; + active_generations[i] = flow ? flow->base.generation : 0; } - f->base.refs++; irq_restore(irq); - int retire_flow = 0; - - if (f->base.state == TCP_TIME_WAIT) { - f->timer.time_wait_ms += elapsed_ms; - if (f->timer.time_wait_ms >= TCP_2MSL_MS) retire_flow = 1; - } - - if (!retire_flow && f->base.state == TCP_FIN_WAIT_2) { - f->timer.fin_wait2_ms += elapsed_ms; - if (f->timer.fin_wait2_ms >= TCP_2MSL_MS) retire_flow = 1; - } - if (retire_flow) { - tcp_free_flow(i); - tcp_flow_put(f); - continue; - } + for (uint16_t pos = 0; pos < active_count; ++pos) { + irq = irq_save_disable(); + uint16_t slot = active_slots[pos]; + tcp_flow_t *flow = slot < MAX_TCP_FLOWS ? tcp_flows[slot] : NULL; + if (!flow || flow->base.generation != active_generations[pos] || flow->base.retired || flow->base.state == TCP_STATE_CLOSED || !flow->base.refs || flow->base.refs == UINT16_MAX) { + irq_restore(irq); + continue; + } + flow->base.refs++; + irq_restore(irq); + bool retire_flow = false; - if (f->timer.delayed_ack_pending) { - f->timer.delayed_ack_timer_ms += elapsed_ms; - if (f->timer.delayed_ack_timer_ms >= TCP_DELAYED_ACK_MS) tcp_send_ack_now(f); - } + if (flow->base.state == TCP_TIME_WAIT) { + flow->timer.time_wait_ms += elapsed_ms; + if (flow->timer.time_wait_ms >= TCP_2MSL_MS) retire_flow = true; + } - if (f->tx.nagle_len) { - if (f->tx.snd_nxt == f->tx.snd_una) tcp_flush_nagle(f, 1); - else { - f->tx.nagle_timer_ms += elapsed_ms; - if (f->tx.nagle_timer_ms >= TCP_NAGLE_TIMEOUT_MS) tcp_flush_nagle(f, 1); + if (!retire_flow && flow->base.state == TCP_FIN_WAIT_2) { + flow->timer.fin_wait2_ms += elapsed_ms; + if (flow->timer.fin_wait2_ms >= TCP_2MSL_MS) retire_flow = true; } - } - if (f->tx.fin_tx_pending) tcp_try_send_pending_fin(f); + if (retire_flow) { + tcp_free_flow(flow); + tcp_flow_put(flow); + continue; + } - if (!f->tx.fin_tx_pending && f->timer.keepalive_on && f->base.state == TCP_ESTABLISHED && f->timer.keepalive_ms) { - f->timer.keepalive_idle_ms += elapsed_ms; - if (f->timer.keepalive_idle_ms >= f->timer.keepalive_ms) { - uint32_t seq = f->tx.snd_nxt; - if (seq) seq -= 1; - tcp_timer_send_ack_segment(f, seq, NULL, 0); - f->timer.keepalive_idle_ms = 0; + if (flow->timer.delayed_ack_pending) { + flow->timer.delayed_ack_timer_ms += elapsed_ms; + if (flow->timer.delayed_ack_timer_ms >= TCP_DELAYED_ACK_MS) (void)tcp_send_ack_now(flow); } - } - if (f->tx.snd_wnd == 0 && (TCP_SEQ_GT(f->tx.snd_nxt, f->tx.snd_una) || f->tx.fin_tx_pending)) { - if (!f->timer.persist_active) { - f->timer.persist_active = 1; - f->timer.persist_timer_ms = 0; - f->timer.persist_probe_cnt = 0; - f->timer.persist_timeout_ms = TCP_PERSIST_MIN_MS; - } else { - f->timer.persist_timer_ms += elapsed_ms; - if (f->timer.persist_timer_ms >= f->timer.persist_timeout_ms) { - if (f->tx.fin_tx_pending && f->timer.persist_probe_cnt >= TCP_MAX_PERSIST_PROBES) { - retire_flow = 1; - } else { - tcp_tx_seg_t *best = tcp_find_first_unacked(f); - - uint8_t payload[1]; - const uint8_t *pp = NULL; - uint16_t pl = 0; - - uint32_t probe_seq = f->tx.snd_una; - if (!best && f->tx.fin_tx_pending && f->tx.snd_nxt == f->tx.snd_una && f->tx.snd_nxt) probe_seq = f->tx.snd_nxt-1; - - const uint8_t *best_payload = tcp_tx_seg_payload_ptr(best); - if (best && best_payload && best->len && TCP_SEQ_GEQ(probe_seq, best->seq) && TCP_SEQ_LT(probe_seq, best->seq + best->len)) { - payload[0] = best_payload[probe_seq - best->seq]; - pp = payload; - pl = 1; - } + if (flow->tx.nagle_len) { + if (flow->tx.snd_nxt == flow->tx.snd_una) { + if (!tcp_flush_nagle(flow, 1)) flow->tx.nagle_timer_ms = 0; + } else { + flow->tx.nagle_timer_ms += elapsed_ms; + if (flow->tx.nagle_timer_ms >= TCP_NAGLE_TIMEOUT_MS && !tcp_flush_nagle(flow, 1)) flow->tx.nagle_timer_ms = 0; + } + } - tcp_timer_send_ack_segment(f, probe_seq, pp, pl); + if (flow->tx.fin_tx_pending) (void)tcp_try_send_pending_fin(flow); - if (f->timer.persist_probe_cnt < UINT8_MAX) f->timer.persist_probe_cnt++; - f->timer.persist_timer_ms = 0; + if (!flow->tx.fin_tx_pending && flow->timer.keepalive_on && flow->base.state == TCP_ESTABLISHED && flow->timer.keepalive_ms) { + flow->timer.keepalive_idle_ms += elapsed_ms; + if (flow->timer.keepalive_idle_ms >= flow->timer.keepalive_ms) { + uint32_t seq = flow->tx.snd_nxt; + if (seq) seq--; + bool sent = tcp_timer_send_ack_segment(flow, seq, NULL, 0); + flow->timer.keepalive_idle_ms = sent || flow->timer.keepalive_ms <= TCP_MIN_RTO ? 0 : flow->timer.keepalive_ms - TCP_MIN_RTO; + } + } - if (f->timer.persist_timeout_ms < TCP_PERSIST_MAX_MS) { - uint32_t next = f->timer.persist_timeout_ms << 1; - if (next > TCP_PERSIST_MAX_MS) next = TCP_PERSIST_MAX_MS; - f->timer.persist_timeout_ms = next; + if (flow->tx.snd_wnd == 0 && (TCP_SEQ_GT(flow->tx.snd_nxt, flow->tx.snd_una) || flow->tx.fin_tx_pending)) { + if (!flow->timer.persist_active) { + flow->timer.persist_active = 1; + flow->timer.persist_timer_ms = 0; + flow->timer.persist_probe_cnt = 0; + flow->timer.persist_timeout_ms = TCP_PERSIST_MIN_MS; + } else { + flow->timer.persist_timer_ms += elapsed_ms; + if (flow->timer.persist_timer_ms >= flow->timer.persist_timeout_ms) { + if (flow->tx.fin_tx_pending && flow->timer.persist_probe_cnt >= TCP_MAX_PERSIST_PROBES) { + retire_flow = true; + } else { + tcp_tx_seg_t *best = tcp_find_first_unacked(flow); + + uint8_t payload[TCP_PERSIST_PROBE_BUFSZ]; + const uint8_t *probe_payload = NULL; + uint16_t probe_len = 0; + + uint32_t probe_seq = flow->tx.snd_una; + if (!best && flow->tx.fin_tx_pending && flow->tx.snd_nxt == flow->tx.snd_una && flow->tx.snd_nxt) probe_seq = flow->tx.snd_nxt - 1; + + const uint8_t *best_payload = tcp_tx_seg_payload_ptr(best); + if (best && best_payload && best->len && TCP_SEQ_GEQ(probe_seq, best->seq) && TCP_SEQ_LT(probe_seq, best->seq + best->len)) { + payload[0] = best_payload[probe_seq - best->seq]; + probe_payload = payload; + probe_len = 1; + } + + bool sent = tcp_timer_send_ack_segment(flow, probe_seq, probe_payload, probe_len); + flow->timer.persist_timer_ms = 0; + + if (sent) { + if (flow->timer.persist_probe_cnt < UINT8_MAX) flow->timer.persist_probe_cnt++; + if (flow->timer.persist_timeout_ms < TCP_PERSIST_MAX_MS) { + uint32_t next = flow->timer.persist_timeout_ms << 1; + if (next > TCP_PERSIST_MAX_MS) next = TCP_PERSIST_MAX_MS; + flow->timer.persist_timeout_ms = next; + } + } } } } + } else { + flow->timer.persist_active = 0; + flow->timer.persist_timer_ms = 0; + flow->timer.persist_timeout_ms = 0; + flow->timer.persist_probe_cnt = 0; } - } else { - f->timer.persist_active = 0; - f->timer.persist_timer_ms = 0; - f->timer.persist_timeout_ms = 0; - f->timer.persist_probe_cnt = 0; - } - - if (retire_flow) { - tcp_free_flow(i); - tcp_flow_put(f); - continue; - } - - for (int j = 0; j < TCP_MAX_TX_SEGS; j++) { - tcp_tx_seg_t *s = &f->tx.txq[j]; - if (!s->used) continue; - s->timer_ms += elapsed_ms; - if (s->timer_ms < s->timeout_ms) continue; - if (f->tx.snd_wnd == 0 && s->len > 0) continue; - - if (s->retransmit_cnt >= TCP_MAX_RETRANS) { - retire_flow = 1; - break; + if (retire_flow) { + tcp_free_flow(flow); + tcp_flow_put(flow); + continue; } - tcp_cc_on_timeout(f); - - tcp_send_from_seg(f, s); - - s->retransmit_cnt++; - s->timer_ms = 0; - - if (s->timeout_ms == 0) { - uint32_t rto = f->tx.rto ? f->tx.rto : TCP_INIT_RTO; - if (rto < TCP_MIN_RTO) rto = TCP_MIN_RTO; - s->timeout_ms = rto; - } else if (s->timeout_ms < TCP_MAX_RTO) { - uint32_t next = s->timeout_ms << 1; - if (next > TCP_MAX_RTO) next = TCP_MAX_RTO; - s->timeout_ms = next; + if (TCP_SEQ_GT(flow->tx.snd_nxt, flow->tx.snd_una)) { + for (uint32_t j = 0; j < TCP_MAX_TX_SEGS; j++) { + tcp_tx_seg_t *seg = &flow->tx.txq[j]; + if (!seg->used) continue; + + uint32_t end_seq = seg->seq + seg->len + (seg->syn ? 1u : 0u) + (seg->fin ? 1u : 0u); + if (!TCP_SEQ_GT(end_seq, flow->tx.snd_una)) continue; + seg->timer_ms += elapsed_ms; + if (seg->timer_ms >= seg->timeout_ms && !(flow->tx.snd_wnd == 0 && seg->len > 0)) { + if (seg->retransmit_cnt >= TCP_MAX_RETRANS) { + retire_flow = true; + break; + } + if (!tcp_send_from_seg(flow, seg)) seg->timer_ms = 0; + else { + tcp_cc_on_timeout(flow); + seg->retransmit_cnt++; + seg->timer_ms = 0; + if (!seg->timeout_ms) { + uint32_t rto = flow->tx.rto ? flow->tx.rto : TCP_INIT_RTO; + if (rto < TCP_MIN_RTO) rto = TCP_MIN_RTO; + seg->timeout_ms = rto; + } else if (seg->timeout_ms < TCP_MAX_RTO) { + uint32_t next = seg->timeout_ms << 1; + if (next > TCP_MAX_RTO) next = TCP_MAX_RTO; + seg->timeout_ms = next; + } + } + } + } } - } - if (retire_flow) { - tcp_free_flow(i); - tcp_flow_put(f); - continue; + if (retire_flow) tcp_free_flow(flow); + tcp_flow_put(flow); } - tcp_flow_put(f); - } -} - - -int tcp_daemon_entry(int argc, char *argv[]) { - (void)argc; - (void)argv; - - const uint32_t tick_ms = 25; - - while (1) { - if (tcp_has_pending_timers()) tcp_tick_all(tick_ms); - msleep(tick_ms); + msleep(10); } return 0; diff --git a/kernel/networking/transport_layer/tcp/tcp_tx.c b/kernel/networking/transport_layer/tcp/tcp_tx.c index 0809940d..823e64a3 100644 --- a/kernel/networking/transport_layer/tcp/tcp_tx.c +++ b/kernel/networking/transport_layer/tcp/tcp_tx.c @@ -13,7 +13,7 @@ const uint8_t *tcp_tx_seg_payload_ptr(const tcp_tx_seg_t *seg) { void tcp_tx_seg_clear(tcp_flow_t *flow, tcp_tx_seg_t *seg) { if (!seg) return; - uint32_t len = seg->len ? (uint32_t)seg->len : 0; + uint32_t len = seg->len; if (seg->pkt) netpkt_unref(seg->pkt); if (len && flow) tcp_account_tx_remove(flow, len); @@ -98,41 +98,44 @@ static uint64_t tcp_emit_data(tcp_flow_t *flow, const uint8_t *payload, uint64_t uint64_t remaining = payload_len; uint64_t sent_bytes = 0; - int first_segment = 1; + bool first_segment = true; while (remaining > 0 && can_send > 0) { - uint32_t free_slots = 0; - for (int i = 0; i < TCP_MAX_TX_SEGS; i++) if (!flow->tx.txq[i].used) free_slots++; - tcp_admit_result_t tx_admit = tcp_admit_tx(flow, 0, free_slots); - if (tx_admit != TCP_ADMIT_OK) { - if (tx_admit == TCP_ADMIT_TX_FLOW_SEGS) tcp_stats.tx_block_flow_segs++; - else if (tx_admit == TCP_ADMIT_TX_FLOW_BYTES) tcp_stats.tx_block_flow_bytes++; - else if (tx_admit == TCP_ADMIT_TX_GLOBAL_BYTES) tcp_stats.tx_block_global_bytes++; - break; - } - - uint64_t seg_len = remaining > can_send ? can_send : remaining; - if (flow->tx.mss && seg_len > flow->tx.mss) seg_len = flow->tx.mss; + uint64_t sendable = remaining > can_send ? can_send : remaining; + if (flow->tx.mss && sendable > flow->tx.mss) sendable = flow->tx.mss; + if (sendable > UINT32_MAX) sendable = UINT32_MAX; + uint32_t seg_len = (uint32_t)sendable; uint32_t tx_limit = flow->tx.queued_limit ? flow->tx.queued_limit : TCP_TX_MAX_BYTES_PER_FLOW; uint32_t flow_room = tx_limit > flow->tx.queued_bytes ? tx_limit - flow->tx.queued_bytes : 0; uint32_t global_room = tcp_tx_global_bytes < TCP_TX_MAX_BYTES_GLOBAL ? TCP_TX_MAX_BYTES_GLOBAL - tcp_tx_global_bytes : 0; + if (!flow_room) { + tcp_stats.tx_block_flow_bytes++; + break; + } + if (!global_room) { + tcp_stats.tx_block_global_bytes++; + break; + } if (seg_len > flow_room) seg_len = flow_room; if (seg_len > global_room) seg_len = global_room; if (!seg_len) break; - tcp_tx_seg_t *seg = tcp_alloc_tx_seg(flow); - if (!seg) break; + tcp_tx_seg_t *seg = tcp_alloc_tx_seg(flow, TCP_TX_CONTROL_RESERVE_SEGS); + if (!seg) { + tcp_stats.tx_block_flow_segs++; + break; + } - netpkt_t *payload_pkt = netpkt_alloc((uint32_t)seg_len, 0, 0); - uint8_t *payload_dst = payload_pkt ? (uint8_t*)netpkt_put(payload_pkt, (uint32_t)seg_len) : NULL; + netpkt_t *payload_pkt = netpkt_alloc(seg_len, 0, 0); + uint8_t *payload_dst = payload_pkt ? (uint8_t*)netpkt_put(payload_pkt, seg_len) : NULL; if (!payload_dst) { if (payload_pkt) netpkt_unref(payload_pkt); tcp_tx_seg_clear(flow, seg); break; } - memcpy(payload_dst, payload + sent_bytes, (uint32_t)seg_len); + memcpy(payload_dst, payload + sent_bytes, seg_len); seg->seq = flow->tx.snd_nxt; seg->len = seg_len; @@ -146,7 +149,7 @@ static uint64_t tcp_emit_data(tcp_flow_t *flow, const uint8_t *payload, uint64_t seg->rtt_sample = 0; if (!flow->tx.rtt_valid && first_segment) seg->rtt_sample = 1; - tcp_account_tx_add(flow, (uint32_t)seg_len); + tcp_account_tx_add(flow, seg_len); flow->tx.snd_nxt += seg_len; flow->base.ctx.sequence = flow->tx.snd_nxt; @@ -160,7 +163,7 @@ static uint64_t tcp_emit_data(tcp_flow_t *flow, const uint8_t *payload, uint64_t sent_bytes += seg_len; remaining -= seg_len; can_send -= seg_len; - first_segment = 0; + first_segment = false; } flow->base.ctx.sequence = flow->tx.snd_nxt; @@ -231,31 +234,44 @@ uint64_t tcp_flush_nagle(tcp_flow_t *flow, uint8_t force) { flow->tx.nagle_len = old_len - (uint32_t)sent; flow->tx.nagle_timer_ms = 0; flow->tx.nagle_flushing = 0; - tcp_daemon_kick(); return sent; } -tcp_tx_seg_t *tcp_alloc_tx_seg(tcp_flow_t *flow){ - if (!flow) return NULL; - for (int i = 0; i < TCP_MAX_TX_SEGS; i++) { - if (!flow->tx.txq[i].used) { - tcp_tx_seg_t *s = &flow->tx.txq[i]; - memset(s, 0, sizeof(*s)); - s->used = 1; - s->timeout_ms = flow->tx.rto ? flow->tx.rto : TCP_INIT_RTO; - return s; - } +tcp_tx_seg_t *tcp_alloc_tx_seg(tcp_flow_t *flow, uint32_t reserve_slots){ + if (!flow || reserve_slots >= TCP_MAX_TX_SEGS) return NULL; + + tcp_tx_seg_t *seg = NULL; + uint32_t available = 0; + for (uint32_t i = 0; i < TCP_MAX_TX_SEGS; i++) { + if (flow->tx.txq[i].used) continue; + if (!seg) seg = &flow->tx.txq[i]; + if (++available > reserve_slots) break; } - return NULL; + + if (!seg || available <= reserve_slots) return NULL; + memset(seg, 0, sizeof(*seg)); + seg->used = 1; + seg->timeout_ms = flow->tx.rto ? flow->tx.rto : TCP_INIT_RTO; + return seg; +} + +bool tcp_send_flow_segment(tcp_flow_t *flow, tcp_hdr_t *hdr, const uint8_t *opts, uint8_t opts_len, const uint8_t *payload, uint16_t payload_len) { + if (!flow || !hdr || !flow->base.l3_id) return false; + if (flow->base.local.ver != IP_VER4 && flow->base.local.ver != IP_VER6) return false; + if (flow->base.remote.ver != flow->base.local.ver) return false; + + ip_tx_opts_t tx; + tx.scope = IP_TX_BOUND_L3; + tx.index = flow->base.l3_id; + return tcp_send_segment(flow->base.local.ver, flow->base.local.ip, flow->base.remote.ip, hdr, opts, opts_len, payload, payload_len, &tx, flow->ip.ttl, flow->ip.dontfrag); } bool tcp_send_from_seg(tcp_flow_t *flow, tcp_tx_seg_t *seg){ if (!flow || !seg) return false; if (flow->base.retired || flow->base.state == TCP_STATE_CLOSED) return false; - if (flow) flow->timer.keepalive_idle_ms = 0; tcp_hdr_t hdr; - hdr.src_port = bswap16(flow->base.local_port); + hdr.src_port = bswap16(flow->base.local.port); hdr.dst_port = bswap16(flow->base.remote.port); hdr.sequence = bswap32(seg->seq); hdr.ack = bswap32(flow->base.ctx.ack); @@ -270,28 +286,19 @@ bool tcp_send_from_seg(tcp_flow_t *flow, tcp_tx_seg_t *seg){ hdr.urgent_ptr = 0; const uint8_t *opts = seg->opts_len ? seg->opts : NULL; - if (flow->base.local.ver == IP_VER4) { - ip_tx_opts_t tx; - tcp_build_tx_opts_from_local_v4(flow->base.local.ip, &tx); - bool ok = tcp_send_segment(IP_VER4, flow->base.local.ip, flow->base.remote.ip, &hdr, opts, seg->opts_len, tcp_tx_seg_payload_ptr(seg), (uint16_t)seg->len, &tx, flow->ip.ttl, flow->ip.dontfrag); - if (ok) tcp_daemon_kick(); - return ok; - } else if (flow->base.local.ver == IP_VER6) { - ip_tx_opts_t tx; - tcp_build_tx_opts_from_local_v6(flow->base.local.ip, &tx); - bool ok = tcp_send_segment(IP_VER6, flow->base.local.ip, flow->base.remote.ip, &hdr, opts, seg->opts_len, tcp_tx_seg_payload_ptr(seg), (uint16_t)seg->len, &tx, flow->ip.ttl, flow->ip.dontfrag); - if (ok) tcp_daemon_kick(); - return ok; - } - - return false; + bool arm_timer = !seg->timer_ms && !seg->retransmit_cnt; + bool ok = tcp_send_flow_segment(flow, &hdr, opts, seg->opts_len, tcp_tx_seg_payload_ptr(seg), (uint16_t)seg->len); + if (!ok) return false; + flow->timer.keepalive_idle_ms = 0; + if (arm_timer) tcp_daemon_kick(); + return true; } -void tcp_send_ack_now(tcp_flow_t *flow){ - if (!flow) return; +bool tcp_send_ack_now(tcp_flow_t *flow){ + if (!flow) return false; tcp_hdr_t ackhdr; - ackhdr.src_port = bswap16(flow->base.local_port); + ackhdr.src_port = bswap16(flow->base.local.port); ackhdr.dst_port = bswap16(flow->base.remote.port); ackhdr.sequence = bswap32(flow->base.ctx.sequence); ackhdr.ack = bswap32(flow->base.ctx.ack); @@ -321,11 +328,11 @@ void tcp_send_ack_now(tcp_flow_t *flow){ for (uint32_t i = 0; i < flow->rx.reass_count && n < TCP_SACK_MAX_BLOCKS; i++) { uint32_t left = flow->rx.reass[i].seq; uint32_t right = flow->rx.reass[i].end; - int duplicate = 0; + bool duplicate = false; for (uint32_t j = 0; j < n; j++) { if (blocks[j].left == left && blocks[j].right == right) { - duplicate = 1; + duplicate = true; break; } } @@ -359,35 +366,32 @@ void tcp_send_ack_now(tcp_flow_t *flow){ } } - if (flow->base.local.ver == IP_VER4) { - ip_tx_opts_t tx; - tcp_build_tx_opts_from_local_v4(flow->base.local.ip, &tx); - (void)tcp_send_segment(IP_VER4, flow->base.local.ip, flow->base.remote.ip, &ackhdr, opts_len ? opts : NULL, opts_len, NULL, 0, &tx, flow->ip.ttl, flow->ip.dontfrag); - } else if (flow->base.local.ver == IP_VER6) { - ip_tx_opts_t tx; - tcp_build_tx_opts_from_local_v6(flow->base.local.ip, &tx); - (void)tcp_send_segment(IP_VER6, flow->base.local.ip, flow->base.remote.ip, &ackhdr, opts_len ? opts : NULL, opts_len, NULL, 0, &tx, flow->ip.ttl, flow->ip.dontfrag); + if (!tcp_send_flow_segment(flow, &ackhdr, opts_len ? opts : NULL, opts_len, NULL, 0)) { + flow->timer.delayed_ack_pending = 1; + flow->timer.delayed_ack_timer_ms = 0; + tcp_daemon_kick(); + return false; } - flow->timer.delayed_ack_pending = 0; flow->timer.delayed_ack_timer_ms = 0; + return true; } -int tcp_try_send_pending_fin(tcp_flow_t *flow) { - if (!flow || !flow->tx.fin_tx_pending) return 0; - if (flow->base.state != TCP_ESTABLISHED&& flow->base.state != TCP_CLOSE_WAIT) return 0; +bool tcp_try_send_pending_fin(tcp_flow_t *flow) { + if (!flow || !flow->tx.fin_tx_pending) return false; + if (flow->base.state != TCP_ESTABLISHED&& flow->base.state != TCP_CLOSE_WAIT) return false; if (flow->tx.nagle_len) tcp_flush_nagle(flow, 1); - if (flow->tx.nagle_len) return 0; + if (flow->tx.nagle_len) return false; uint64_t in_flight = TCP_SEQ_GT(flow->tx.snd_nxt, flow->tx.snd_una) ? flow->tx.snd_nxt - flow->tx.snd_una : 0; uint32_t wnd = flow->tx.snd_wnd; uint32_t cwnd = flow->tx.cwnd ? flow->tx.cwnd : (flow->tx.mss ? flow->tx.mss : TCP_DEFAULT_MSS); uint32_t eff_wnd = wnd < cwnd ? wnd : cwnd; - if (eff_wnd == 0 || in_flight >= eff_wnd) return 0; - tcp_tx_seg_t *seg = tcp_alloc_tx_seg(flow); - if (!seg) return 0; + if (eff_wnd == 0 || in_flight >= eff_wnd) return false; + tcp_tx_seg_t *seg = tcp_alloc_tx_seg(flow, 0); + if (!seg) return false; seg->seq = flow->tx.snd_nxt; seg->len = 0; @@ -416,12 +420,10 @@ int tcp_try_send_pending_fin(tcp_flow_t *flow) { flow->base.state = old_state; flow->tx.fin_tx_pending = 1; tcp_tx_seg_clear(flow, seg); - tcp_daemon_kick(); - return 0; + return false; } - tcp_daemon_kick(); - return 1; + return true; } tcp_result_t tcp_flow_flush(tcp_data *flow_ctx){ @@ -440,7 +442,10 @@ tcp_result_t tcp_flow_flush(tcp_data *flow_ctx){ } if (rc == TCP_OK) { - if (flow->tx.fin_tx_pending) tcp_try_send_pending_fin(flow); + if (flow->tx.fin_tx_pending) { + tcp_try_send_pending_fin(flow); + if (flow->tx.fin_tx_pending) tcp_daemon_kick(); + } if (flow->tx.nagle_len) { tcp_daemon_kick(); rc = TCP_WOULDBLOCK; @@ -461,8 +466,8 @@ tcp_result_t tcp_flow_send(tcp_data *flow_ctx){ uint8_t *payload_ptr = (uint8_t *)flow_ctx->payload.ptr; uint64_t payload_len = flow_ctx->payload.size; flow_ctx->payload.size = 0; - int want_fin = (flags & (1u << FIN_F)) != 0; - int fin_queued = 0; + bool want_fin = (flags & (1u << FIN_F)) != 0; + bool fin_queued = false; uint64_t accepted = 0; tcp_result_t rc = TCP_OK; @@ -490,16 +495,16 @@ tcp_result_t tcp_flow_send(tcp_data *flow_ctx){ if (!flow->tx.nagle_len) { uint64_t n = tcp_emit_data(flow, payload_ptr, payload_len); accepted += n; - payload_ptr += n; payload_len -= n; } } if (rc == TCP_OK && want_fin && payload_len == 0) { flow->tx.fin_tx_pending = 1; - fin_queued = 1; + fin_queued = true; if (flow->tx.nagle_len) tcp_flush_nagle(flow, 1); tcp_try_send_pending_fin(flow); + if (flow->tx.fin_tx_pending) tcp_daemon_kick(); } flow_ctx->sequence = flow->tx.snd_nxt; @@ -537,7 +542,7 @@ tcp_result_t tcp_flow_close(tcp_data *flow_ctx){ flow->tx.fin_tx_pending = 1; if (flow->tx.nagle_len) tcp_flush_nagle(flow, 1); tcp_try_send_pending_fin(flow); - tcp_daemon_kick(); + if (flow->tx.fin_tx_pending) tcp_daemon_kick(); rc = TCP_OK; } diff --git a/kernel/networking/transport_layer/tcp/tcp_utils.c b/kernel/networking/transport_layer/tcp/tcp_utils.c index 70b31bf5..8c6eff90 100644 --- a/kernel/networking/transport_layer/tcp/tcp_utils.c +++ b/kernel/networking/transport_layer/tcp/tcp_utils.c @@ -3,33 +3,30 @@ #include "tcp_internal.h" #include "networking/interface_manager.h" -bool tcp_flow_matches_bind(const tcp_flow_t *flow, const SockBindSpec *spec) { - if (!flow || !spec) return false; - - ip_version_t ver = flow->base.local.ver; - if ((spec->kind == BIND_L3 || spec->kind == BIND_L2) && spec->ver && spec->ver != ver) return false; - if (spec->kind == BIND_ANY) return true; - if (spec->kind == BIND_ANY4) return ver == IP_VER4; - if (spec->kind == BIND_ANY6) return ver == IP_VER6; - if (spec->kind == BIND_IP) { - if (spec->ver != ver) return false; - return memcmp(spec->ip, flow->base.local.ip, ver == IP_VER6 ? 16 : 4) == 0; - } - if (spec->kind == BIND_L3) return spec->l3_id == flow->base.l3_id; - if (spec->kind == BIND_L2) return l3_ifindex_from_id(flow->base.l3_id) == spec->ifindex; - return false; +void tcp_update_mss(tcp_flow_t *flow) { + if (!flow) return; + uint32_t local_mss = flow->tx.path_mss ? flow->tx.path_mss : TCP_DEFAULT_MSS; + if (local_mss > TCP_MAX_MSS) local_mss = TCP_MAX_MSS; + if (flow->tx.configured_mss && flow->tx.configured_mss < local_mss) local_mss = flow->tx.configured_mss; + flow->tx.advertised_mss = local_mss; + + uint32_t mss = local_mss; + if (flow->tx.peer_mss && flow->tx.peer_mss < mss) mss = flow->tx.peer_mss; + flow->tx.mss = mss; } uint32_t tcp_calc_mss_for_l3(uint8_t l3_id, ip_version_t ver, const void *remote_ip){ uint32_t mtu = 1500; - l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(l3_id); - if (v6) mtu =v6->mtu ? v6->mtu : 1500; - - l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(l3_id); - if (v4) mtu = v4->runtime_opts_v4.mtu ? v4->runtime_opts_v4.mtu : 1500; + if (ver == IP_VER4) { + l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(l3_id); + if (v4) mtu = v4->runtime_opts_v4.mtu ? v4->runtime_opts_v4.mtu : 1500; + } else if (ver == IP_VER6) { + l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(l3_id); + if (v6) mtu =v6->mtu ? v6->mtu : 1500; + } else return 256; if (ver == IP_VER6 && remote_ip){ - uint16_t pmtu =ipv6_pmtu_get((const uint8_t*)remote_ip); + uint16_t pmtu = ipv6_pmtu_get((const uint8_t*)remote_ip); if (pmtu && pmtu < mtu) mtu = pmtu; } @@ -41,42 +38,6 @@ uint32_t tcp_calc_mss_for_l3(uint8_t l3_id, ip_version_t ver, const void *remote return mss; } -bool tcp_build_tx_opts_from_local_v4(const void *src_ip_addr, ip_tx_opts_t *out){ - if (!out) return false; - uint32_t src_ip = 0; - memcpy(&src_ip, src_ip_addr, sizeof(src_ip)); - l3_ipv4_interface_t *v4 = l3_ipv4_find_by_ip(src_ip); - if (v4) { - out->scope = IP_TX_BOUND_L3; - out->index = v4->l3_id; - } else { - out->scope = IP_TX_AUTO; - out->index = 0; - } - return true; -} - -bool tcp_build_tx_opts_from_l3(uint8_t l3_id, ip_tx_opts_t *out){ - if (!out) return false; - out->scope = IP_TX_BOUND_L3; - out->index = l3_id; - return true; -} - -bool tcp_build_tx_opts_from_local_v6(const void *src_ip_addr, ip_tx_opts_t *out){ - if (!out) return false; - const uint8_t *sip = (const uint8_t *)src_ip_addr; - l3_ipv6_interface_t *v6 = l3_ipv6_find_by_ip(sip); - if (v6 && v6->l2) { - out->scope = IP_TX_BOUND_L3; - out->index = v6->l3_id; - } else { - out->scope = IP_TX_AUTO; - out->index = 0; - } - return true; -} - void tcp_parse_options(const uint8_t *opts, uint32_t len, tcp_parsed_opts_t *out) { if (!out) return; diff --git a/kernel/networking/transport_layer/tcp/tcp_utils.h b/kernel/networking/transport_layer/tcp/tcp_utils.h index 41ffed40..ea71f19d 100644 --- a/kernel/networking/transport_layer/tcp/tcp_utils.h +++ b/kernel/networking/transport_layer/tcp/tcp_utils.h @@ -16,6 +16,8 @@ typedef struct { uint32_t right; } tcp_sack_block_t; +struct tcp_flow; + typedef struct { uint16_t mss; uint8_t wscale; @@ -29,12 +31,9 @@ typedef struct { void tcp_parse_options(const uint8_t *opts, uint32_t len, tcp_parsed_opts_t *out); uint8_t tcp_build_syn_options(uint8_t *out, uint16_t mss, uint8_t wscale, uint8_t sack_permitted); +void tcp_update_mss(struct tcp_flow *flow); uint32_t tcp_calc_mss_for_l3(uint8_t l3_id, ip_version_t ver, const void *remote_ip); -bool tcp_build_tx_opts_from_local_v4(const void *src_ip_addr, ip_tx_opts_t *out); -bool tcp_build_tx_opts_from_l3(uint8_t l3_id, ip_tx_opts_t *out); -bool tcp_build_tx_opts_from_local_v6(const void *src_ip_addr, ip_tx_opts_t *out); - #ifdef __cplusplus } #endif \ No newline at end of file diff --git a/kernel/networking/transport_layer/udp.c b/kernel/networking/transport_layer/udp.c index 5a9724fe..4c2d911a 100644 --- a/kernel/networking/transport_layer/udp.c +++ b/kernel/networking/transport_layer/udp.c @@ -12,6 +12,10 @@ #include "networking/internet_layer/ipv6_utils.h" size_t create_udp_segment(uintptr_t buf, const net_l4_endpoint *src, const net_l4_endpoint *dst, sizedptr payload) { + if (!buf || !src || !dst || src->ver != dst->ver) return 0; + if ((src->ver != IP_VER4 && src->ver != IP_VER6) || (payload.size && !payload.ptr)) return 0; + if (payload.size > UINT16_MAX - sizeof(udp_hdr_t)) return 0; + udp_hdr_t udp; udp.src_port = bswap16(src->port); udp.dst_port = bswap16(dst->port); @@ -20,46 +24,48 @@ size_t create_udp_segment(uintptr_t buf, const net_l4_endpoint *src, const net_l udp.checksum = 0; memcpy((void*)buf, &udp, sizeof(udp)); - memcpy((void *)(buf + sizeof(udp)), (void *)payload.ptr, payload.size); + if (payload.size) memcpy((void *)(buf + sizeof(udp)), (void *)payload.ptr, payload.size); + uint16_t checksum = 0; if (src->ver == IP_VER4) { - uint32_t s = 0; - uint32_t d = 0; - memcpy(&s, src->ip, 4); - memcpy(&d, dst->ip, 4); - udp.checksum = bswap16(checksum16_pipv4(s, d, PROTO_UDP, (const uint8_t *)buf, full_len)); - } else if (src->ver == IP_VER6) { - udp.checksum = bswap16(checksum16_pipv6(src->ip, dst->ip, PROTO_UDP, (const uint8_t *)buf, full_len)); - } + uint32_t src_ip = 0; + uint32_t dst_ip = 0; + memcpy(&src_ip, src->ip, 4); + memcpy(&dst_ip, dst->ip, 4); + checksum = checksum16_pipv4(src_ip, dst_ip, PROTO_UDP, (const uint8_t *)buf, full_len); + } else checksum = checksum16_pipv6(src->ip, dst->ip, PROTO_UDP, (const uint8_t *)buf, full_len); + udp.checksum = checksum ? bswap16(checksum) : UINT16_MAX; memcpy((void*)buf, &udp, sizeof(udp)); return full_len; } -void udp_send_segment(const net_l4_endpoint *src, const net_l4_endpoint *dst, sizedptr payload, const ip_tx_opts_t* tx_opts, uint8_t ttl, uint8_t dontfrag) { +bool udp_send_segment(const net_l4_endpoint *src, const net_l4_endpoint *dst, sizedptr payload, const ip_tx_opts_t* tx_opts, uint8_t ttl, uint8_t dontfrag) { + if (!src || !dst || src->ver != dst->ver) return false; + if ((src->ver != IP_VER4 && src->ver != IP_VER6) || (payload.size && !payload.ptr)) return false; + if (payload.size > UINT16_MAX - sizeof(udp_hdr_t)) return false; + uint32_t udp_len = (uint32_t)(sizeof(udp_hdr_t) + payload.size); uint32_t headroom = (uint32_t)sizeof(eth_hdr_t) + (uint32_t)(src->ver == IP_VER4 ? sizeof(ipv4_hdr_t) : sizeof(ipv6_hdr_t)); netpkt_t* pkt = netpkt_alloc(udp_len, headroom, 0); - if (!pkt) return; + if (!pkt) return false; void* buf = netpkt_put(pkt, udp_len); if (!buf) { netpkt_unref(pkt); - return; + return false; } - size_t written = create_udp_segment((uintptr_t)buf, src, dst, payload); + if (!create_udp_segment((uintptr_t)buf, src, dst, payload)) { + netpkt_unref(pkt); + return false; + } if (src->ver == IP_VER4) { uint32_t dst_ip = 0; memcpy(&dst_ip, dst->ip, 4); - (void)netpkt_trim(pkt, (uint32_t)written); - ipv4_send_packet(dst_ip, PROTO_UDP, pkt, tx_opts, ttl, dontfrag); - } else if (src->ver == IP_VER6) { - (void)netpkt_trim(pkt, (uint32_t)written); - ipv6_send_packet(dst->ip, PROTO_UDP, pkt, tx_opts, ttl, dontfrag); - } else { - netpkt_unref(pkt); + return ipv4_send_packet(dst_ip, PROTO_UDP, pkt, tx_opts, ttl, dontfrag); } + return ipv6_send_packet(dst->ip, PROTO_UDP, pkt, tx_opts, ttl, dontfrag); } bool udp_strip_header(const netpkt_t* pkt, udp_hdr_t* hdr, uint32_t* payload_off, uint32_t* payload_len) { @@ -85,6 +91,10 @@ void udp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a return; } + if (ipver == IP_VER6 && !hdr.checksum) { + netpkt_unref(pkt); + return; + } if (hdr.checksum) { if (ipver == IP_VER4) { uint32_t src_ip = 0; diff --git a/kernel/networking/transport_layer/udp.h b/kernel/networking/transport_layer/udp.h index 80552b88..16fa6b8e 100644 --- a/kernel/networking/transport_layer/udp.h +++ b/kernel/networking/transport_layer/udp.h @@ -20,7 +20,7 @@ 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 ip_tx_opts_t* tx_opts, uint8_t ttl, uint8_t dontfrag); +bool udp_send_segment(const net_l4_endpoint *src, const net_l4_endpoint *dst, sizedptr payload, const ip_tx_opts_t* tx_opts, uint8_t ttl, uint8_t dontfrag); void udp_input(ip_version_t ipver, const void *src_ip_addr, From 27745f7eb30d540e91768f79b1fe13c80a701b3a Mon Sep 17 00:00:00 2001 From: CodeAnarchist Date: Thu, 6 Aug 2026 20:04:44 +0200 Subject: [PATCH 38/39] [tcp/udp] add sack, dsack and udp sync --- .../networking/transport_layer/csocket_tcp.c | 12 +- .../networking/transport_layer/csocket_udp.c | 445 +++++++++++------- .../networking/transport_layer/socket_core.c | 78 ++- .../networking/transport_layer/tcp/tcp_core.c | 19 +- .../transport_layer/tcp/tcp_internal.h | 19 +- .../networking/transport_layer/tcp/tcp_rx.c | 389 ++++++++++++--- .../networking/transport_layer/tcp/tcp_tx.c | 14 +- 7 files changed, 693 insertions(+), 283 deletions(-) diff --git a/kernel/networking/transport_layer/csocket_tcp.c b/kernel/networking/transport_layer/csocket_tcp.c index 181c1af8..be23678b 100644 --- a/kernel/networking/transport_layer/csocket_tcp.c +++ b/kernel/networking/transport_layer/csocket_tcp.c @@ -114,7 +114,7 @@ static int32_t tcp_socket_connection_state(tcp_socket_t* s) { socket_impl_t socket_tcp_create(ksocket_t* owner, const SocketOptions* extra) { if (!owner) return NULL; - uint32_t supported = SOCK_OPT_KEEPALIVE | SOCK_OPT_KEEPALIVE_INTERVAL | SOCK_OPT_SEND_TIMEOUT | SOCK_OPT_RECV_TIMEOUT | SOCK_OPT_BUF_SIZE | SOCK_OPT_DEBUG | SOCK_OPT_DONTFRAG | SOCK_OPT_TTL | SOCK_OPT_SEND_BUF_SIZE | SOCK_OPT_TCP_NO_DELAY | SOCK_OPT_NONBLOCK | SOCK_OPT_DONTROUTE | SOCK_OPT_REUSEADDR | SOCK_OPT_TCP_MAXSEG | SOCK_OPT_LINGER; + uint32_t supported = SOCK_OPT_KEEPALIVE | SOCK_OPT_KEEPALIVE_INTERVAL | SOCK_OPT_SEND_TIMEOUT | SOCK_OPT_RECV_TIMEOUT | SOCK_OPT_BUF_SIZE | SOCK_OPT_DEBUG | SOCK_OPT_DONTFRAG | SOCK_OPT_TTL | SOCK_OPT_SEND_BUF_SIZE | SOCK_OPT_TCP_NO_DELAY | SOCK_OPT_NONBLOCK | SOCK_OPT_DONTROUTE | SOCK_OPT_REUSEADDR | SOCK_OPT_TCP_MAXSEG | SOCK_OPT_LINGER | SOCK_OPT_TCP_SACK | SOCK_OPT_TCP_DSACK; if (extra) { if (extra->flags & ~supported) return NULL; if ((extra->flags & SOCK_OPT_DEBUG) && extra->debug_level > SOCK_DBG_ALL) return NULL; @@ -130,6 +130,7 @@ socket_impl_t socket_tcp_create(ksocket_t* owner, const SocketOptions* extra) { s->remoteEP.ver = IP_VER4; s->bindSpec.kind = BIND_ANY; if (extra) s->options = *extra; + s->options.flags |= SOCK_OPT_TCP_SACK | SOCK_OPT_TCP_DSACK; if (!(s->options.flags & SOCK_OPT_BUF_SIZE)) { s->options.flags |= SOCK_OPT_BUF_SIZE; s->options.buf_size = TCP_DEFAULT_SOCKET_BUF; @@ -147,6 +148,7 @@ socket_impl_t socket_tcp_create(ksocket_t* owner, const SocketOptions* extra) { s->options.flags &= ~SOCK_OPT_LINGER; memset(&s->options.linger, 0, sizeof(s->options.linger)); } else s->options.linger.enabled = 1; + if (s->options.flags & SOCK_OPT_TCP_DSACK) s->options.flags |= SOCK_OPT_TCP_SACK; return s; } @@ -205,6 +207,10 @@ int32_t socket_setopt_tcp(socket_impl_t sh, int32_t opt, const void* value, uint } case SOCK_OPT_NONBLOCK: return socket_common_options_set(&s->options, opt, value, len); + case SOCK_OPT_TCP_SACK: + case SOCK_OPT_TCP_DSACK: + if (s->flow.flow_generation || s->listening) return SOCK_ERR_STATE; + return socket_common_options_set(&s->options, opt, value, len); case SOCK_OPT_TCP_MAXSEG: { if (!value || len != sizeof(uint32_t)) return SOCK_ERR_INVAL; uint32_t v = 0; @@ -230,7 +236,6 @@ int32_t socket_setopt_tcp(socket_impl_t sh, int32_t opt, const void* value, uint } return SOCK_OK; } - case SOCK_OPT_TCP_SACK: case SOCK_OPT_REUSEPORT: case SOCK_OPT_MCAST_JOIN: case SOCK_OPT_MCAST_LEAVE: @@ -365,7 +370,6 @@ int32_t socket_getopt_tcp(socket_impl_t sh, int32_t opt, void* value, uint32_t* case SOCK_GET_OPT_TCP_MAXSEG: v = s->options.tcp_maxseg; break; - case SOCK_GET_OPT_TCP_SACK: case SOCK_GET_OPT_REUSEPORT: case SOCK_GET_MCAST_GROUPS: case SOCK_GET_OPT_BROADCAST_ALLOWED: @@ -380,6 +384,8 @@ int32_t socket_getopt_tcp(socket_impl_t sh, int32_t opt, void* value, uint32_t* case SOCK_GET_OPT_NONBLOCK: case SOCK_GET_OPT_DONTROUTE: case SOCK_GET_OPT_REUSEADDR: + case SOCK_GET_OPT_TCP_SACK: + case SOCK_GET_OPT_TCP_DSACK: return socket_common_options_get(&s->options, opt, value, len); default: return SOCK_ERR_INVAL; diff --git a/kernel/networking/transport_layer/csocket_udp.c b/kernel/networking/transport_layer/csocket_udp.c index d81140c5..b042e8e7 100644 --- a/kernel/networking/transport_layer/csocket_udp.c +++ b/kernel/networking/transport_layer/csocket_udp.c @@ -14,10 +14,10 @@ #include "syscalls/syscalls.h" #include "std/memory.h" #include "alloc/allocate.h" -//TODO manage multicast membership join and leave with udp rx before freeing group storage #define UDP_DEFAULT_RING_CAP 64 #define UDP_MAX_RING_CAP 1024 +//what if MAX_L2_INTERFACES is larger than sizeof u16? typedef struct udp_rx_entry { netpkt_t* pkt; net_l4_endpoint src; @@ -54,8 +54,7 @@ static bool udp_socket_mcast_endpoint_valid(const net_l4_endpoint* ep) { } static bool udp_socket_mcast_match(udp_socket_t* s, ip_version_t ver, const void* dst_ip_addr) { - if (!s || !dst_ip_addr) return false; - if (!s->options.mcast_groups || !s->options.mcast_count) return false; + if (!s || !dst_ip_addr || !s->options.mcast_groups || !s->options.mcast_count) return false; for (uint32_t i = 0; i < s->options.mcast_count; ++i) { const net_l4_endpoint* group = &s->options.mcast_groups[i]; @@ -67,9 +66,7 @@ static bool udp_socket_mcast_match(udp_socket_t* s, ip_version_t ver, const void memcpy(&want, group->ip, 4); memcpy(&got, dst_ip_addr, 4); if (want == got) return true; - } else if (ver == IP_VER6) { - if (ipv6_cmp(group->ip, dst_ip_addr) == 0) return true; - } + } else if (ver == IP_VER6 && ipv6_cmp(group->ip, dst_ip_addr) == 0) return true; } return false; @@ -77,6 +74,7 @@ static bool udp_socket_mcast_match(udp_socket_t* s, ip_version_t ver, const void static int32_t udp_socket_apply_mcast_group(const SockBindSpec* spec, const net_l4_endpoint* group, bool joining, uint16_t* ifmask) { if (!spec || !group || !ifmask) return SOCK_ERR_INVAL; + if (joining && *ifmask) return SOCK_ERR_STATE; uint32_t v4_group = 0; if (group->ver == IP_VER4) { @@ -144,17 +142,17 @@ static int32_t udp_socket_apply_mcast_group(const SockBindSpec* spec, const net_ return SOCK_OK; } -static int32_t udp_socket_join_mcast_groups(udp_socket_t* s) { - if (!s || !s->options.mcast_groups || !s->options.mcast_count || !s->localPort) return SOCK_OK; +static int32_t udp_socket_join_mcast_groups(udp_socket_t* s, const SockBindSpec* spec) { + if (!s || !spec || !s->options.mcast_groups || !s->options.mcast_count) return SOCK_OK; if (!s->mcast_ifmasks) return SOCK_ERR_SYS; for (uint32_t i = 0; i < s->options.mcast_count; ++i) { - int32_t rc = udp_socket_apply_mcast_group(&s->bindSpec, &s->options.mcast_groups[i], true, &s->mcast_ifmasks[i]); + int32_t rc = udp_socket_apply_mcast_group(spec, &s->options.mcast_groups[i], true, &s->mcast_ifmasks[i]); if (rc == SOCK_OK) continue; while (i > 0) { --i; - (void)udp_socket_apply_mcast_group(&s->bindSpec, &s->options.mcast_groups[i], false, &s->mcast_ifmasks[i]); + (void)udp_socket_apply_mcast_group(spec, &s->options.mcast_groups[i], false, &s->mcast_ifmasks[i]); } return rc; } @@ -164,53 +162,61 @@ static int32_t udp_socket_join_mcast_groups(udp_socket_t* s) { static int32_t udp_socket_bind_l3(udp_socket_t* s, uint8_t l3_id) { if (!s || !s->ownerSocket || !l3_id) return SOCK_ERR_INVAL; + if (s->closed) return SOCK_ERR_STATE; if (s->localPort) return SOCK_OK; + SockBindSpec spec = {0}; socket_bind_token_t token = 0; - int32_t port = socket_bind_alloc_ephemeral_l3(s->ownerSocket, PROTO_UDP, l3_id, s->options.flags & (SOCK_OPT_REUSEADDR | SOCK_OPT_REUSEPORT), &s->bindSpec, &token); + int32_t port = socket_bind_alloc_ephemeral_l3(s->ownerSocket, PROTO_UDP, l3_id, s->options.flags & (SOCK_OPT_REUSEADDR | SOCK_OPT_REUSEPORT), &spec, &token); if (port < 0) return SOCK_ERR_NO_PORT; - s->localPort = (uint16_t)port; - s->bindToken = token; - if (s->connected) socket_bind_udp_set_remote(token, &s->remoteEP); + int32_t rc = udp_socket_join_mcast_groups(s, &spec); + if (rc != SOCK_OK) { + socket_bind_remove(token); + return rc; + } - int32_t rc = udp_socket_join_mcast_groups(s); - if (rc == SOCK_OK) return SOCK_OK; + if (s->connected) socket_bind_udp_set_remote(token, &s->remoteEP); - socket_bind_remove(token); - s->localPort = 0; - s->bindToken = 0; - memset(&s->bindSpec, 0, sizeof(s->bindSpec)); - s->bindSpec.kind = BIND_ANY; - return rc; + irq_flags_t irq = irq_save_disable(); + if (s->closed) { + irq_restore(irq); + socket_bind_remove(token); + return SOCK_ERR_STATE; + } + s->localPort = (uint16_t)port; + s->bindToken = token; + s->bindSpec = spec; + irq_restore(irq); + return SOCK_OK; } uint32_t socket_udp_input(ksocket_t* socket, ip_version_t ipver, uint8_t l3_id, const void* src_ip_addr, const void* dst_ip_addr, netpkt_t* pkt, uint16_t src_port, uint16_t dst_port) { - if (!socket || !pkt) return 0; - udp_socket_t* s = (udp_socket_t*)socket_core_impl(socket); - if (!s) return 0; - if (!s->bindToken || s->localPort != dst_port || !dst_ip_addr || !src_ip_addr) return 0; + if (!socket || !pkt || !src_ip_addr || !dst_ip_addr) return 0; if (ipver != IP_VER4 && ipver != IP_VER6) return 0; - if (ipver == IP_VER4) { - uint32_t dip = 0; - memcpy(&dip, dst_ip_addr, 4); - if (ipv4_is_multicast(dip) && !udp_socket_mcast_match(s, IP_VER4, dst_ip_addr)) return 0; - } else if (ipver == IP_VER6) { - if (ipv6_is_multicast(dst_ip_addr) && !udp_socket_mcast_match(s, IP_VER6, dst_ip_addr)) return 0; - } - - if (s->connected) { - if (s->remoteEP.ver != ipver || s->remoteEP.port != src_port) return 0; - if (ipver == IP_VER4 && memcmp(s->remoteEP.ip, src_ip_addr, 4) != 0) return 0; - if (ipver == IP_VER6 && ipv6_cmp(s->remoteEP.ip, src_ip_addr) != 0) return 0; - } + udp_socket_t* s = (udp_socket_t*)socket_core_impl(socket); + if (!s) return 0; uint32_t pkt_len = netpkt_len(pkt); uint32_t limit = UINT32_MAX; if ((s->options.flags & SOCK_OPT_BUF_SIZE) && s->options.buf_size) limit = s->options.buf_size; if (pkt_len > limit) return 0; + bool multicast = false; + if (ipver == IP_VER4) { + uint32_t dip = 0; + memcpy(&dip, dst_ip_addr, 4); + multicast = ipv4_is_multicast(dip); + } else multicast = ipv6_is_multicast(dst_ip_addr); + + udp_rx_entry_t entry = {0}; + entry.pkt = pkt; + make_ep(src_ip_addr, src_port, ipver, &entry.src); + entry.rx_spec.kind = BIND_L3; + entry.rx_spec.ver = ipver; + entry.rx_spec.l3_id = l3_id; + if (!s->rx_ring || !s->ring_cap) { uint32_t usable = UDP_DEFAULT_RING_CAP; if ((s->options.flags & SOCK_OPT_BUF_SIZE) && s->options.buf_size) { @@ -222,7 +228,7 @@ uint32_t socket_udp_input(ksocket_t* socket, ip_version_t ipver, uint8_t l3_id, udp_rx_entry_t* ring = (udp_rx_entry_t*)zalloc(sizeof(udp_rx_entry_t) * (usable+1)); if (!ring) return 0; irq_flags_t irq = irq_save_disable(); - if (!s->rx_ring) { + if (!s->closed && !s->rx_ring) { s->rx_ring = ring; s->ring_cap = usable + 1; ring = NULL; @@ -232,7 +238,22 @@ uint32_t socket_udp_input(ksocket_t* socket, ip_version_t ipver, uint8_t l3_id, } irq_flags_t irq = irq_save_disable(); - if (!s->rx_ring || !s->ring_cap || s->rx_bytes > limit - pkt_len) { + if (s->closed || s->localPort != dst_port || !s->rx_ring || !s->ring_cap) { + irq_restore(irq); + return 0; + } + + if (multicast && !udp_socket_mcast_match(s, ipver, dst_ip_addr)) { + irq_restore(irq); + return 0; + } + + if (s->connected && (s->remoteEP.ver != ipver || s->remoteEP.port != src_port || (ipver == IP_VER4 && memcmp(s->remoteEP.ip, src_ip_addr, 4) != 0) || (ipver == IP_VER6 && ipv6_cmp(s->remoteEP.ip, src_ip_addr) != 0))) { + irq_restore(irq); + return 0; + } + + if (s->rx_bytes > limit - pkt_len) { irq_restore(irq); return 0; } @@ -244,12 +265,7 @@ uint32_t socket_udp_input(ksocket_t* socket, ip_version_t ipver, uint8_t l3_id, } netpkt_ref(pkt); - s->rx_ring[s->r_tail].pkt = pkt; - make_ep(src_ip_addr, src_port, ipver, &s->rx_ring[s->r_tail].src); - memset(&s->rx_ring[s->r_tail].rx_spec, 0, sizeof(s->rx_ring[s->r_tail].rx_spec)); - s->rx_ring[s->r_tail].rx_spec.kind = BIND_L3; - s->rx_ring[s->r_tail].rx_spec.ver = ipver; - s->rx_ring[s->r_tail].rx_spec.l3_id = l3_id; + s->rx_ring[s->r_tail] = entry; s->rx_bytes += pkt_len; s->r_tail = nexti; irq_restore(irq); @@ -299,6 +315,7 @@ int32_t socket_bind_udp(socket_impl_t sh, const SockBindSpec* spec_in, uint16_t ev.bind_spec = *spec_in; netlog_socket_event(&s->options, &ev); + if (s->closed) return SOCK_ERR_STATE; if (s->localPort) return SOCK_ERR_BOUND; if (!s->ownerSocket) return SOCK_ERR_SYS; @@ -312,21 +329,24 @@ int32_t socket_bind_udp(socket_impl_t sh, const SockBindSpec* spec_in, uint16_t if (bind_port < 0) return SOCK_ERR_NO_PORT; } else if (!socket_bind_insert(s->ownerSocket, PROTO_UDP, &spec, port, s->options.flags & (SOCK_OPT_REUSEADDR | SOCK_OPT_REUSEPORT), true, &token)) return SOCK_ERR_BOUND; + int32_t rc = udp_socket_join_mcast_groups(s, &spec); + if (rc != SOCK_OK) { + socket_bind_remove(token); + return rc; + } + + if (s->connected) socket_bind_udp_set_remote(token, &s->remoteEP); + + irq_flags_t irq = irq_save_disable(); + if (s->closed) { + irq_restore(irq); + socket_bind_remove(token); + return SOCK_ERR_STATE; + } s->bindSpec = spec; s->bindToken = token; s->localPort = (uint16_t)bind_port; - if (s->connected) socket_bind_udp_set_remote(s->bindToken, &s->remoteEP); - - int32_t mcast_res = udp_socket_join_mcast_groups(s); - if (mcast_res != SOCK_OK) { - socket_bind_remove(s->bindToken); - s->localPort = 0; - memset(&s->bindSpec, 0, sizeof(s->bindSpec)); - s->bindSpec.kind = BIND_ANY; - s->bindToken = 0; - return mcast_res; - } - + irq_restore(irq); return SOCK_OK; } @@ -341,6 +361,7 @@ int32_t socket_connect_udp(socket_impl_t sh, const net_l4_endpoint* dst) { if (dst) ev.dst_ep = *dst; netlog_socket_event(&s->options, &ev); + if (s->closed) return SOCK_ERR_STATE; if (!dst || !dst->port) return SOCK_ERR_INVAL; if (dst->ver != IP_VER4 && dst->ver != IP_VER6) return SOCK_ERR_INVAL; @@ -356,15 +377,22 @@ int32_t socket_connect_udp(socket_impl_t sh, const net_l4_endpoint* dst) { if ((s->options.flags & SOCK_OPT_DONTROUTE) && !ipv6_tx_plan_onlink(&plan, dst->ip)) return SOCK_ERR_NO_ROUTE; } + if (s->bindToken) socket_bind_udp_set_remote(s->bindToken, dst); + irq_flags_t irq = irq_save_disable(); + if (s->closed) { + irq_restore(irq); + return SOCK_ERR_STATE; + } s->remoteEP = *dst; s->connected = true; - if (s->bindToken) socket_bind_udp_set_remote(s->bindToken, &s->remoteEP); + irq_restore(irq); return SOCK_OK; } int64_t socket_sendto_udp(socket_impl_t sh, const net_l4_endpoint* dst, const void* buf, uint64_t len) { udp_socket_t* s = (udp_socket_t*)sh; if (!s || (!buf && len)) return SOCK_ERR_INVAL; + if (s->closed) return SOCK_ERR_STATE; netlog_socket_event_t ev = {0}; ev.comp = NETLOG_COMP_UDP; @@ -515,48 +543,46 @@ int64_t socket_recvfrom_udp(socket_impl_t sh, void* buf, uint64_t len, net_l4_en ev.remote_ep = s->remoteEP; netlog_socket_event(&s->options, &ev); - irq_flags_t irq = irq_save_disable(); - bool ready = s->rx_ring && s->r_head != s->r_tail; - irq_restore(irq); - if (!ready) { - if (s->options.flags & SOCK_OPT_NONBLOCK) return SOCK_ERR_WOULDBLOCK; - - uint32_t start_ms = (uint32_t)get_time(); - while (1) { - irq = irq_save_disable(); - ready = s->rx_ring && s->r_head != s->r_tail; - irq_restore(irq); - if (ready) break; + uint32_t start_ms = 0; + netpkt_t* p = NULL; + net_l4_endpoint se = {0}; - if ((s->options.flags & SOCK_OPT_RECV_TIMEOUT) && s->options.recv_timeout_ms) { - uint32_t now_ms = get_time(); - uint32_t elapsed_ms = now_ms - start_ms; - if (elapsed_ms >= s->options.recv_timeout_ms) return SOCK_ERR_WOULDBLOCK; + while (1){ + irq_flags_t irq = irq_save_disable(); + if (s->rx_ring && s->r_head != s->r_tail) { + p = s->rx_ring[s->r_head].pkt; + se = s->rx_ring[s->r_head].src; + s->lastRxSpec = s->rx_ring[s->r_head].rx_spec; + memset(&s->rx_ring[s->r_head], 0, sizeof(s->rx_ring[s->r_head])); + s->r_head = (s->r_head + 1) % s->ring_cap; - uint32_t wait_ms = s->options.recv_timeout_ms - elapsed_ms; - if (wait_ms > 5) wait_ms = 5; - msleep(wait_ms); - }else msleep(5); + uint32_t pkt_len = p ? netpkt_len(p) : 0; + if (s->rx_bytes >= pkt_len) s->rx_bytes -= pkt_len; + else s->rx_bytes = 0; + irq_restore(irq); + break; } - } - irq = irq_save_disable(); - if (!s->rx_ring || s->r_head == s->r_tail) { + bool closed = s->closed; + uint32_t flags = s->options.flags; + uint32_t timeout_ms = s->options.recv_timeout_ms; irq_restore(irq); - return SOCK_ERR_WOULDBLOCK; - } - netpkt_t* p = s->rx_ring[s->r_head].pkt; - net_l4_endpoint se = s->rx_ring[s->r_head].src; - s->lastRxSpec = s->rx_ring[s->r_head].rx_spec; - memset(&s->rx_ring[s->r_head], 0, sizeof(s->rx_ring[s->r_head])); - s->r_head = (s->r_head + 1) % s->ring_cap; + if (closed) return 0; + if (flags & SOCK_OPT_NONBLOCK) return SOCK_ERR_WOULDBLOCK; + if ((flags & SOCK_OPT_RECV_TIMEOUT) && timeout_ms) { + uint32_t now_ms = (uint32_t)get_time(); + if (!start_ms) start_ms = now_ms; + uint32_t elapsed_ms = now_ms - start_ms; + if (elapsed_ms >= timeout_ms) return SOCK_ERR_WOULDBLOCK; + + uint32_t wait_ms = timeout_ms - elapsed_ms; + if (wait_ms > 5) wait_ms = 5; + msleep(wait_ms); + }else msleep(5); + } uint32_t pkt_len = p ? netpkt_len(p) : 0; - if (s->rx_bytes >= pkt_len) s->rx_bytes -= pkt_len; - else s->rx_bytes = 0; - irq_restore(irq); - uint32_t tocpy = pkt_len; if (tocpy > len) tocpy = (uint32_t)len; @@ -570,6 +596,7 @@ int64_t socket_recvfrom_udp(socket_impl_t sh, void* buf, uint64_t len, net_l4_en int32_t socket_setopt_udp(socket_impl_t sh, int32_t opt, const void* value, uint32_t len) { udp_socket_t* s = (udp_socket_t*)sh; if (!s) return SOCK_ERR_INVAL; + if (s->closed) return SOCK_ERR_STATE; switch ((uint32_t)opt) { case SOCK_OPT_KEEPALIVE: @@ -578,6 +605,7 @@ int32_t socket_setopt_udp(socket_impl_t sh, int32_t opt, const void* value, uint case SOCK_OPT_SEND_BUF_SIZE: case SOCK_OPT_TCP_MAXSEG: case SOCK_OPT_TCP_SACK: + case SOCK_OPT_TCP_DSACK: case SOCK_OPT_LINGER: case SOCK_OPT_FILTER: case SOCK_OPT_SPECIAL: @@ -585,10 +613,10 @@ int32_t socket_setopt_udp(socket_impl_t sh, int32_t opt, const void* value, uint case SOCK_OPT_REUSEADDR: case SOCK_OPT_REUSEPORT: if (s->localPort) return SOCK_ERR_STATE; - return socket_common_options_set(&s->options, opt, value, len); + break; case SOCK_OPT_NONBLOCK: case SOCK_OPT_DONTROUTE: - return socket_common_options_set(&s->options, opt, value, len); + break; case SOCK_OPT_MCAST_JOIN: { if (!value || !len || (len % sizeof(net_l4_endpoint)) != 0) return SOCK_ERR_INVAL; uint32_t count = len / sizeof(net_l4_endpoint); @@ -597,7 +625,9 @@ int32_t socket_setopt_udp(socket_impl_t sh, int32_t opt, const void* value, uint const net_l4_endpoint* groups = value; for (uint32_t i = 0; i < count; ++i) if (!udp_socket_mcast_endpoint_valid(&groups[i])) return SOCK_ERR_INVAL; - uint32_t capacity = s->options.mcast_count + count; + uint8_t old_count = s->options.mcast_count; + if (old_count && (!s->options.mcast_groups || !s->mcast_ifmasks)) return SOCK_ERR_SYS; + uint32_t capacity = old_count + count; net_l4_endpoint* next_groups = (net_l4_endpoint*)zalloc(sizeof(net_l4_endpoint) * capacity); uint16_t* next_ifmasks = (uint16_t*)zalloc(sizeof(uint16_t) * capacity); if (!next_groups || !next_ifmasks) { @@ -606,12 +636,12 @@ int32_t socket_setopt_udp(socket_impl_t sh, int32_t opt, const void* value, uint return SOCK_ERR_SYS; } - if (s->options.mcast_count) { - memcpy(next_groups, s->options.mcast_groups, sizeof(net_l4_endpoint) * s->options.mcast_count); - memcpy(next_ifmasks, s->mcast_ifmasks, sizeof(uint16_t) * s->options.mcast_count); + if (old_count) { + memcpy(next_groups, s->options.mcast_groups, sizeof(net_l4_endpoint) * old_count); + memcpy(next_ifmasks, s->mcast_ifmasks, sizeof(uint16_t) * old_count); } - uint8_t next_count = s->options.mcast_count; + uint8_t next_count = old_count; for (uint32_t i = 0; i < count; ++i) { bool exists = false; for (uint8_t j = 0; j < next_count; ++j) { @@ -629,7 +659,7 @@ int32_t socket_setopt_udp(socket_impl_t sh, int32_t opt, const void* value, uint if (s->localPort) { int32_t rc = udp_socket_apply_mcast_group(&s->bindSpec, &next_groups[next_count], true, &next_ifmasks[next_count]); if (rc != SOCK_OK) { - while (next_count > s->options.mcast_count) { + while (next_count > old_count) { next_count--; (void)udp_socket_apply_mcast_group(&s->bindSpec, &next_groups[next_count], false, &next_ifmasks[next_count]); } @@ -641,18 +671,23 @@ int32_t socket_setopt_udp(socket_impl_t sh, int32_t opt, const void* value, uint next_count++; } - if (next_count == s->options.mcast_count) { + if (next_count == old_count) { release(next_groups); release(next_ifmasks); return SOCK_OK; } - if (s->options.mcast_groups) release((void*)s->options.mcast_groups); - if (s->mcast_ifmasks) release(s->mcast_ifmasks); + irq_flags_t irq = irq_save_disable(); + net_l4_endpoint* old_groups = (net_l4_endpoint*)s->options.mcast_groups; + uint16_t* old_ifmasks = s->mcast_ifmasks; s->options.mcast_groups = next_groups; s->mcast_ifmasks = next_ifmasks; s->options.mcast_count = next_count; s->options.flags |= SOCK_OPT_MCAST_JOIN; + irq_restore(irq); + + if (old_groups) release(old_groups); + if (old_ifmasks) release(old_ifmasks); return SOCK_OK; } case SOCK_OPT_MCAST_LEAVE: { @@ -664,8 +699,10 @@ int32_t socket_setopt_udp(socket_impl_t sh, int32_t opt, const void* value, uint const net_l4_endpoint* groups = value; for (uint32_t i = 0; i < count; ++i) if (!udp_socket_mcast_endpoint_valid(&groups[i])) return SOCK_ERR_INVAL; - net_l4_endpoint* next_groups = (net_l4_endpoint*)zalloc(sizeof(net_l4_endpoint) * s->options.mcast_count); - uint16_t* next_ifmasks = (uint16_t*)zalloc(sizeof(uint16_t) * s->options.mcast_count); + uint8_t old_count = s->options.mcast_count; + if (!s->mcast_ifmasks) return SOCK_ERR_SYS; + net_l4_endpoint* next_groups = (net_l4_endpoint*)zalloc(sizeof(net_l4_endpoint) * old_count); + uint16_t* next_ifmasks = (uint16_t*)zalloc(sizeof(uint16_t) * old_count); if (!next_groups || !next_ifmasks) { if (next_groups) release(next_groups); if (next_ifmasks) release(next_ifmasks); @@ -673,7 +710,7 @@ int32_t socket_setopt_udp(socket_impl_t sh, int32_t opt, const void* value, uint } uint8_t next_count = 0; - for (uint8_t i = 0; i < s->options.mcast_count; ++i) { + for (uint8_t i = 0; i < old_count; ++i) { bool removed = false; for (uint32_t j = 0; j < count; ++j) { if (s->options.mcast_groups[i].ver != groups[j].ver) continue; @@ -683,16 +720,13 @@ int32_t socket_setopt_udp(socket_impl_t sh, int32_t opt, const void* value, uint break; } } - if (removed) { - if (s->localPort) (void)udp_socket_apply_mcast_group(&s->bindSpec, &s->options.mcast_groups[i], false, &s->mcast_ifmasks[i]); - continue; - } + if (removed) continue; next_groups[next_count] = s->options.mcast_groups[i]; next_ifmasks[next_count] = s->mcast_ifmasks[i]; next_count++; } - if (next_count == s->options.mcast_count) { + if (next_count == old_count) { release(next_groups); release(next_ifmasks); return SOCK_OK; @@ -705,13 +739,33 @@ int32_t socket_setopt_udp(socket_impl_t sh, int32_t opt, const void* value, uint next_ifmasks = NULL; } - release((void*)s->options.mcast_groups); - release(s->mcast_ifmasks); + irq_flags_t irq = irq_save_disable(); + net_l4_endpoint* old_groups = (net_l4_endpoint*)s->options.mcast_groups; + uint16_t* old_ifmasks = s->mcast_ifmasks; s->options.mcast_groups = next_groups; s->mcast_ifmasks = next_ifmasks; s->options.mcast_count = next_count; - if (s->options.mcast_count) s->options.flags |= SOCK_OPT_MCAST_JOIN; + if (next_count) s->options.flags |= SOCK_OPT_MCAST_JOIN; else s->options.flags &= ~SOCK_OPT_MCAST_JOIN; + irq_restore(irq); + + if (s->localPort) { + for (uint8_t i = 0; i < old_count; ++i) { + bool kept = false; + for (uint8_t j = 0; j < next_count; ++j) { + if (old_groups[i].ver != next_groups[j].ver) continue; + uint32_t ip_len = old_groups[i].ver == IP_VER4 ? 4 : 16; + if (memcmp(old_groups[i].ip, next_groups[j].ip, ip_len) == 0) { + kept = true; + break; + } + } + if (!kept) (void)udp_socket_apply_mcast_group(&s->bindSpec, &old_groups[i], false, &old_ifmasks[i]); + } + } + + release(old_groups); + release(old_ifmasks); return SOCK_OK; } case SOCK_OPT_SEND_TIMEOUT: @@ -721,13 +775,15 @@ int32_t socket_setopt_udp(socket_impl_t sh, int32_t opt, const void* value, uint case SOCK_OPT_DONTFRAG: case SOCK_OPT_BROADCAST_ALLOWED: case SOCK_OPT_TTL: - return socket_common_options_set(&s->options, opt, value, len); + break; case SOCK_OPT_BUF_SIZE: if (s->localPort) return SOCK_ERR_STATE; - return socket_common_options_set(&s->options, opt, value, len); + break; default: return SOCK_ERR_INVAL; } + + return socket_common_options_set(&s->options, opt, value, len); } int32_t socket_getopt_udp(socket_impl_t sh, int32_t opt, void* value, uint32_t* len) { @@ -735,17 +791,58 @@ int32_t socket_getopt_udp(socket_impl_t sh, int32_t opt, void* value, uint32_t* if (!s || !len) return SOCK_ERR_INVAL; switch ((uint32_t)opt) { - case SOCK_GET_REMOTE_ENDPOINT: - return socket_common_get_value(&s->remoteEP, sizeof(s->remoteEP), value, len); - case SOCK_GET_BIND_SPEC: - return socket_common_get_value(&s->bindSpec, sizeof(s->bindSpec), value, len); - case SOCK_GET_LAST_RX_SPEC: - return socket_common_get_value(&s->lastRxSpec, sizeof(s->lastRxSpec), value, len); + case SOCK_GET_REMOTE_ENDPOINT: { + irq_flags_t irq = irq_save_disable(); + net_l4_endpoint remote = s->remoteEP; + irq_restore(irq); + return socket_common_get_value(&remote, sizeof(remote), value, len); + } + case SOCK_GET_BIND_SPEC: { + irq_flags_t irq = irq_save_disable(); + SockBindSpec spec = s->bindSpec; + irq_restore(irq); + return socket_common_get_value(&spec, sizeof(spec), value, len); + } + case SOCK_GET_LAST_RX_SPEC: { + irq_flags_t irq = irq_save_disable(); + SockBindSpec spec = s->lastRxSpec; + irq_restore(irq); + return socket_common_get_value(&spec, sizeof(spec), value, len); + } + case SOCK_GET_MCAST_GROUPS: { + irq_flags_t irq = irq_save_disable(); + uint32_t need = s->options.mcast_count * sizeof(net_l4_endpoint); + if (!value) { + *len = need; + irq_restore(irq); + return SOCK_OK; + } + if (*len < need) { + irq_restore(irq); + return SOCK_ERR_INVAL; + } + if (need) memcpy(value, s->options.mcast_groups, need); + *len = need; + irq_restore(irq); + return SOCK_OK; + } + case SOCK_GET_OPT_RECV_TIMEOUT: + case SOCK_GET_OPT_BUF_SIZE: + case SOCK_GET_OPT_DEBUG: + case SOCK_GET_OPT_DONTFRAG: + case SOCK_GET_OPT_BROADCAST_ALLOWED: + case SOCK_GET_OPT_TTL: + case SOCK_GET_OPT_NONBLOCK: + case SOCK_GET_OPT_DONTROUTE: + case SOCK_GET_OPT_REUSEADDR: + case SOCK_GET_OPT_REUSEPORT: + return socket_common_options_get(&s->options, opt, value, len); default: break; } uint32_t v = 0; + irq_flags_t irq = irq_save_disable(); switch ((uint32_t)opt) { case SOCK_GET_BOUND: v = s->localPort != 0; @@ -754,6 +851,7 @@ int32_t socket_getopt_udp(socket_impl_t sh, int32_t opt, void* value, uint32_t* v = s->connected; break; case SOCK_GET_LISTENING: + irq_restore(irq); return SOCK_ERR_UNSUP; case SOCK_GET_LOCAL_PORT: v = s->localPort; @@ -770,6 +868,7 @@ int32_t socket_getopt_udp(socket_impl_t sh, int32_t opt, void* value, uint32_t* case SOCK_GET_OPT_SEND_BUF_SIZE: case SOCK_GET_OPT_TCP_MAXSEG: case SOCK_GET_OPT_TCP_SACK: + case SOCK_GET_OPT_TCP_DSACK: case SOCK_GET_OPT_LINGER: case SOCK_GET_OPT_FILTER: case SOCK_GET_OPT_SEND_TIMEOUT: @@ -777,88 +876,82 @@ int32_t socket_getopt_udp(socket_impl_t sh, int32_t opt, void* value, uint32_t* case SOCK_GET_TCP_MSS: case SOCK_GET_TCP_RTT_MS: case SOCK_GET_TCP_RETRANSMITS: + irq_restore(irq); return SOCK_ERR_UNSUP; - case SOCK_GET_MCAST_GROUPS: { - uint32_t need = s->options.mcast_count * sizeof(net_l4_endpoint); - return socket_common_get_value(s->options.mcast_groups, need, value, len); - } - case SOCK_GET_OPT_RECV_TIMEOUT: - case SOCK_GET_OPT_BUF_SIZE: - case SOCK_GET_OPT_DEBUG: - case SOCK_GET_OPT_DONTFRAG: - case SOCK_GET_OPT_BROADCAST_ALLOWED: - case SOCK_GET_OPT_TTL: - case SOCK_GET_OPT_NONBLOCK: - case SOCK_GET_OPT_DONTROUTE: - case SOCK_GET_OPT_REUSEADDR: - case SOCK_GET_OPT_REUSEPORT: - return socket_common_options_get(&s->options, opt, value, len); default: + irq_restore(irq); return SOCK_ERR_INVAL; } + irq_restore(irq); return socket_common_get_value(&v, sizeof(v), value, len); } -//disable irq? int32_t socket_close_udp(socket_impl_t sh) { udp_socket_t* s = (udp_socket_t*)sh; if (!s) return SOCK_ERR_INVAL; - if (s->closed) return SOCK_OK; - - if (s->localPort && s->options.mcast_groups && s->mcast_ifmasks) { - for (uint32_t i = 0; i < s->options.mcast_count; ++i) udp_socket_apply_mcast_group(&s->bindSpec, &s->options.mcast_groups[i], false, &s->mcast_ifmasks[i]); - } netlog_socket_event_t ev = {0}; ev.comp = NETLOG_COMP_UDP; ev.action = NETLOG_ACT_CLOSE; ev.pid = socket_core_pid(s->ownerSocket); + + irq_flags_t irq = irq_save_disable(); + if (s->closed) { + irq_restore(irq); + return SOCK_OK; + } + + udp_rx_entry_t* rx_ring = s->rx_ring; + uint32_t ring_cap = s->ring_cap; + uint32_t r_head = s->r_head; + uint32_t r_tail = s->r_tail; + net_l4_endpoint* mcast_groups = (net_l4_endpoint*)s->options.mcast_groups; + uint16_t* mcast_ifmasks = s->mcast_ifmasks; + uint8_t mcast_count = s->options.mcast_count; + socket_bind_token_t bind_token = s->bindToken; + SockBindSpec bind_spec = s->bindSpec; + SocketOptions options = s->options; ev.local_port = s->localPort; ev.remote_ep = s->remoteEP; - netlog_socket_event(&s->options, &ev); - if (s->rx_ring) { - while (s->r_head != s->r_tail) { - if (s->rx_ring[s->r_head].pkt) { - uint32_t pkt_len = netpkt_len(s->rx_ring[s->r_head].pkt); - if (s->rx_bytes >= pkt_len) s->rx_bytes -= pkt_len; - else s->rx_bytes = 0; - netpkt_unref(s->rx_ring[s->r_head].pkt); - } - s->r_head = (s->r_head + 1) % s->ring_cap; - } - release(s->rx_ring); - s->rx_ring = NULL; - } - if (s->options.mcast_groups) { - release((void*)s->options.mcast_groups); - s->options.mcast_groups = NULL; - } - if (s->mcast_ifmasks) { - release(s->mcast_ifmasks); - s->mcast_ifmasks = NULL; - } - s->options.mcast_count = 0; - s->options.flags &= ~(SOCK_OPT_MCAST_JOIN | SOCK_OPT_MCAST_LEAVE); + s->closed = true; + s->rx_ring = NULL; s->ring_cap = 0; s->r_head = 0; s->r_tail = 0; s->rx_bytes = 0; + s->options.mcast_groups = NULL; + s->mcast_ifmasks = NULL; + s->options.mcast_count = 0; + s->options.flags &= ~(SOCK_OPT_MCAST_JOIN | SOCK_OPT_MCAST_LEAVE); memset(&s->lastRxSpec, 0, sizeof(s->lastRxSpec)); s->lastRxSpec.kind = BIND_ANY; - if (s->bindToken) { - socket_bind_remove(s->bindToken); - s->bindToken = 0; - } + s->bindToken = 0; s->localPort = 0; memset(&s->bindSpec, 0, sizeof(s->bindSpec)); s->bindSpec.kind = BIND_ANY; s->connected = false; - s->remoteEP.port = 0; + memset(&s->remoteEP, 0, sizeof(s->remoteEP)); s->remoteEP.ver = IP_VER4; - memset(s->remoteEP.ip, 0, sizeof(s->remoteEP.ip)); - s->closed = true; + irq_restore(irq); + + if (bind_token) socket_bind_remove(bind_token); + netlog_socket_event(&options, &ev); + + if (ev.local_port && mcast_groups && mcast_ifmasks) { + for (uint32_t i = 0; i < mcast_count; ++i) (void)udp_socket_apply_mcast_group(&bind_spec, &mcast_groups[i], false, &mcast_ifmasks[i]); + } + + if (rx_ring && ring_cap) { + while (r_head != r_tail) { + if (rx_ring[r_head].pkt) netpkt_unref(rx_ring[r_head].pkt); + r_head = (r_head + 1) % ring_cap; + } + release(rx_ring); + } + if (mcast_groups) release(mcast_groups); + if (mcast_ifmasks) release(mcast_ifmasks); return SOCK_OK; } diff --git a/kernel/networking/transport_layer/socket_core.c b/kernel/networking/transport_layer/socket_core.c index 179438be..78e56470 100644 --- a/kernel/networking/transport_layer/socket_core.c +++ b/kernel/networking/transport_layer/socket_core.c @@ -210,68 +210,89 @@ int32_t socket_common_options_set(SocketOptions* opts, int32_t opt, const void* memcpy(&v, value, sizeof(v)); } else if (len || (opt != SOCK_OPT_DONTFRAG && opt != SOCK_OPT_BROADCAST_ALLOWED)) return SOCK_ERR_INVAL; + int32_t rc = SOCK_OK; + irq_flags_t irq = irq_save_disable(); switch ((uint32_t)opt) { case SOCK_OPT_RECV_TIMEOUT: opts->recv_timeout_ms = v; if (v) opts->flags |= SOCK_OPT_RECV_TIMEOUT; else opts->flags &= ~SOCK_OPT_RECV_TIMEOUT; - return SOCK_OK; + break; case SOCK_OPT_SEND_TIMEOUT: opts->send_timeout_ms = v; if (v) opts->flags |= SOCK_OPT_SEND_TIMEOUT; else opts->flags &= ~SOCK_OPT_SEND_TIMEOUT; - return SOCK_OK; + break; case SOCK_OPT_BUF_SIZE: - if (!v) return SOCK_ERR_INVAL; - opts->buf_size = v; - opts->flags |= SOCK_OPT_BUF_SIZE; - return SOCK_OK; + if (!v) rc = SOCK_ERR_INVAL; + else { + opts->buf_size = v; + opts->flags |= SOCK_OPT_BUF_SIZE; + } + break; case SOCK_OPT_DEBUG: - if (v > SOCK_DBG_ALL) return SOCK_ERR_INVAL; - opts->debug_level = (SockDebugLevel)v; - if (v) opts->flags |= SOCK_OPT_DEBUG; - else opts->flags &= ~SOCK_OPT_DEBUG; - return SOCK_OK; + if (v > SOCK_DBG_ALL) rc = SOCK_ERR_INVAL; + else { + opts->debug_level = (SockDebugLevel)v; + if (v) opts->flags |= SOCK_OPT_DEBUG; + else opts->flags &= ~SOCK_OPT_DEBUG; + } + break; case SOCK_OPT_DONTFRAG: if (v) opts->flags |= SOCK_OPT_DONTFRAG; else opts->flags &= ~SOCK_OPT_DONTFRAG; - return SOCK_OK; + break; case SOCK_OPT_BROADCAST_ALLOWED: if (v) opts->flags |= SOCK_OPT_BROADCAST_ALLOWED; else opts->flags &= ~SOCK_OPT_BROADCAST_ALLOWED; - return SOCK_OK; + break; case SOCK_OPT_NONBLOCK: if (v) opts->flags |= SOCK_OPT_NONBLOCK; else opts->flags &= ~SOCK_OPT_NONBLOCK; - return SOCK_OK; + break; case SOCK_OPT_DONTROUTE: if (v) opts->flags |= SOCK_OPT_DONTROUTE; else opts->flags &= ~SOCK_OPT_DONTROUTE; - return SOCK_OK; + break; case SOCK_OPT_REUSEADDR: if (v) opts->flags |= SOCK_OPT_REUSEADDR; else opts->flags &= ~SOCK_OPT_REUSEADDR; - return SOCK_OK; + break; case SOCK_OPT_REUSEPORT: if (v) opts->flags |= SOCK_OPT_REUSEPORT; else opts->flags &= ~SOCK_OPT_REUSEPORT; - return SOCK_OK; + break; + case SOCK_OPT_TCP_SACK: + if (v) opts->flags |= SOCK_OPT_TCP_SACK; + else opts->flags &= ~(SOCK_OPT_TCP_SACK | SOCK_OPT_TCP_DSACK); + break; + case SOCK_OPT_TCP_DSACK: + if (v) opts->flags |= SOCK_OPT_TCP_SACK | SOCK_OPT_TCP_DSACK; + else opts->flags &= ~SOCK_OPT_TCP_DSACK; + break; case SOCK_OPT_TTL: - if (v > 255) return SOCK_ERR_INVAL; - opts->ttl = (uint8_t)v; - if (v) opts->flags |= SOCK_OPT_TTL; - else opts->flags &= ~SOCK_OPT_TTL; - return SOCK_OK; + if (v > 255) rc = SOCK_ERR_INVAL; + else { + opts->ttl = (uint8_t)v; + if (v) opts->flags |= SOCK_OPT_TTL; + else opts->flags &= ~SOCK_OPT_TTL; + } + break; default: - return SOCK_ERR_INVAL; + rc = SOCK_ERR_INVAL; + break; } + irq_restore(irq); + return rc; } int32_t socket_common_options_get(const SocketOptions* opts, int32_t opt, void* value, uint32_t* len) { if (!opts || !len) return SOCK_ERR_INVAL; uint32_t v = 0; + int32_t rc = SOCK_OK; + irq_flags_t irq = irq_save_disable(); switch ((uint32_t)opt) { case SOCK_GET_OPT_RECV_TIMEOUT: v = opts->recv_timeout_ms; @@ -303,13 +324,22 @@ int32_t socket_common_options_get(const SocketOptions* opts, int32_t opt, void* case SOCK_GET_OPT_REUSEPORT: v = (opts->flags & SOCK_OPT_REUSEPORT) != 0; break; + case SOCK_GET_OPT_TCP_SACK: + v = (opts->flags & SOCK_OPT_TCP_SACK) != 0; + break; + case SOCK_GET_OPT_TCP_DSACK: + v = (opts->flags & SOCK_OPT_TCP_DSACK) != 0; + break; case SOCK_GET_OPT_TTL: v = opts->ttl; break; default: - return SOCK_ERR_INVAL; + rc = SOCK_ERR_INVAL; + break; } + irq_restore(irq); + if (rc != SOCK_OK) return rc; return socket_common_get_value(&v, sizeof(v), value, len); } diff --git a/kernel/networking/transport_layer/tcp/tcp_core.c b/kernel/networking/transport_layer/tcp/tcp_core.c index 6945eacd..f803c165 100644 --- a/kernel/networking/transport_layer/tcp/tcp_core.c +++ b/kernel/networking/transport_layer/tcp/tcp_core.c @@ -166,6 +166,7 @@ tcp_flow_t *tcp_flow_from_ctx(tcp_data *flow_ctx) { void tcp_flow_apply_options(tcp_flow_t *flow, const SocketOptions* extra, uint32_t apply_mask) { if (!flow) return; SocketOptions defaults = {0}; + defaults.flags = SOCK_OPT_TCP_SACK | SOCK_OPT_TCP_DSACK; if (!extra) extra = &defaults; uint32_t flags = extra->flags; @@ -192,6 +193,20 @@ void tcp_flow_apply_options(tcp_flow_t *flow, const SocketOptions* extra, uint32 tcp_update_mss(flow); } + if (apply_mask & (SOCK_OPT_TCP_SACK | SOCK_OPT_TCP_DSACK)) { + flow->tx.sack_enabled = (flags & SOCK_OPT_TCP_SACK) ? 1 : 0; + flow->tx.dsack_enabled = flow->tx.sack_enabled && (flags & SOCK_OPT_TCP_DSACK) ? 1 : 0; + if (!flow->tx.sack_enabled) { + flow->tx.sack_ok = 0; + flow->tx.sack_range_count = 0; + flow->tx.sack_retransmitted_count = 0; + flow->tx.sack_rescue_sent = 0; + flow->rx.dsack_pending = 0; + flow->rx.dsack_left = 0; + flow->rx.dsack_right = 0; + } + } + if (apply_mask & SOCK_OPT_TTL) flow->ip.ttl = (flags & SOCK_OPT_TTL) ? extra->ttl : 0; if (apply_mask & SOCK_OPT_DONTFRAG) flow->ip.dontfrag = (flags & SOCK_OPT_DONTFRAG) ? 1 : 0; if (apply_mask & SOCK_OPT_REUSEADDR) flow->ip.reuseaddr = (flags & SOCK_OPT_REUSEADDR) ? 1 : 0; @@ -333,6 +348,8 @@ tcp_flow_t *tcp_alloc_flow(void){ f->tx.peer_mss = 0; f->tx.advertised_mss = TCP_DEFAULT_MSS; f->tx.mss = TCP_DEFAULT_MSS; + f->tx.sack_enabled = 1; + f->tx.dsack_enabled = 1; f->tx.cwnd = f->tx.mss * TCP_INIT_CWND_SEGS; f->tx.ssthresh = TCP_RECV_WINDOW; return f; @@ -592,7 +609,7 @@ bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, flow->tx.ws_recv = 0; flow->tx.ws_ok = 0; } - flow->tx.sack_ok = 1; + flow->tx.sack_ok = flow->tx.sack_enabled; flow->rx.rcv_buf = (uintptr_t)zalloc(flow->rx.rcv_wnd_max); if (!flow->rx.rcv_buf) { flow->rx.rcv_wnd = 0; diff --git a/kernel/networking/transport_layer/tcp/tcp_internal.h b/kernel/networking/transport_layer/tcp/tcp_internal.h index d0e78871..05ac94d4 100644 --- a/kernel/networking/transport_layer/tcp/tcp_internal.h +++ b/kernel/networking/transport_layer/tcp/tcp_internal.h @@ -33,6 +33,12 @@ extern "C" { #define TCP_NAGLE_FLUSH_THRESHOLD TCP_DEFAULT_MSS #define TCP_NAGLE_TIMEOUT_MS 10 #define TCP_CONNECT_TIMEOUT_MS 10000 +#define TCP_SACK_SCOREBOARD_MAX 32 + +typedef struct { + uint32_t left; + uint32_t right; +} tcp_sack_range_t; typedef struct { uint8_t used; @@ -41,8 +47,6 @@ typedef struct { uint8_t rtt_sample; uint8_t retransmit_cnt; uint8_t opts_len; - uint8_t sacked; - uint8_t sack_retransmitted; uint8_t opts[40]; uint32_t seq; uint32_t len; @@ -96,7 +100,15 @@ typedef struct { uint8_t in_fast_recovery; uint32_t recover; uint32_t cwnd_acc; + uint32_t configured_mss; + uint8_t sack_enabled; + uint8_t dsack_enabled; + tcp_sack_range_t sack_ranges[TCP_SACK_SCOREBOARD_MAX]; + tcp_sack_range_t sack_retransmitted_ranges[TCP_SACK_SCOREBOARD_MAX]; + uint8_t sack_range_count; + uint8_t sack_retransmitted_count; + uint8_t sack_rescue_sent; uint8_t nagle_flushing; uint8_t nagle_appending; @@ -121,6 +133,9 @@ typedef struct { uint32_t rcv_ooo_used; uint32_t sack_recent_left; uint32_t sack_recent_right; + uint8_t dsack_pending; + uint32_t dsack_left; + uint32_t dsack_right; uint32_t rcv_wnd; uint32_t rcv_wnd_max; uint32_t rcv_adv_edge; diff --git a/kernel/networking/transport_layer/tcp/tcp_rx.c b/kernel/networking/transport_layer/tcp/tcp_rx.c index 3f970c1a..d7b1e4f5 100644 --- a/kernel/networking/transport_layer/tcp/tcp_rx.c +++ b/kernel/networking/transport_layer/tcp/tcp_rx.c @@ -164,70 +164,267 @@ tcp_tx_seg_t *tcp_find_first_unacked(tcp_flow_t *flow) { return best; } -static bool tcp_apply_sack_blocks(tcp_flow_t *flow, const tcp_parsed_opts_t *opts) { - if (!flow || !opts || !flow->tx.sack_ok || !opts->sack_count) return 0; +static int tcp_sack_insert(tcp_sack_range_t *ranges, uint8_t *count, uint32_t left, uint32_t right) { //b + if (!ranges || !count || TCP_SEQ_LEQ(right, left)) return 0; + + tcp_sack_range_t merged[TCP_SACK_SCOREBOARD_MAX + 1]; + uint8_t old_count = *count; + uint8_t out = 0; + bool inserted = false; + + for (uint8_t i = 0; i < old_count; i++) { + tcp_sack_range_t current = ranges[i]; + if (TCP_SEQ_LT(current.right, left)) { + merged[out++] = current; + continue; + } - bool changed = false; - for (uint32_t b = 0; b < opts->sack_count; b++) { - uint32_t left = opts->sacks[b].left; - uint32_t right = opts->sacks[b].right; + if (TCP_SEQ_LT(right, current.left)) { + if (!inserted) { + merged[out].left = left; + merged[out].right = right; + out++; + inserted = true; + } + merged[out++] = current; + continue; + } + + if (TCP_SEQ_LT(current.left, left)) left = current.left; + if (TCP_SEQ_GT(current.right, right)) right = current.right; + } - if (TCP_SEQ_LEQ(right, left)) continue; - if (TCP_SEQ_LEQ(right, flow->tx.snd_una)) continue; - if (TCP_SEQ_GEQ(left, flow->tx.snd_nxt)) continue; + if (!inserted) { + merged[out].left = left; + merged[out].right = right; + out++; + } - for (uint32_t i = 0; i < TCP_MAX_TX_SEGS; i++) { - tcp_tx_seg_t *s = &flow->tx.txq[i]; + uint8_t first = out > TCP_SACK_SCOREBOARD_MAX ? 1 : 0; + uint8_t next_count = out - first; + if (old_count == next_count && memcmp(ranges, &merged[first], sizeof(tcp_sack_range_t)*next_count) == 0) return 0; - if (!s->used) continue; + memcpy(ranges, &merged[first], sizeof(tcp_sack_range_t) * next_count); + *count = next_count; + return 1; +} - uint32_t end = s->seq + s->len + (s->syn ? 1 : 0) + (s->fin ? 1 : 0); - if (TCP_SEQ_LEQ(end, flow->tx.snd_una)) continue; - if (TCP_SEQ_LT(s->seq, left) || TCP_SEQ_GT(end, right)) continue; +static void tcp_sack_trim_ranges(tcp_sack_range_t *ranges, uint8_t *count, uint32_t ack) { + if (!ranges || !count) return; - if (!s->sacked) changed = true; - s->sacked = 1; - } + uint8_t out = 0; + for (uint8_t i = 0; i < *count; i++) { + uint32_t left = ranges[i].left; + uint32_t right = ranges[i].right; + + if (TCP_SEQ_LEQ(right, ack)) continue; + if (TCP_SEQ_LT(left, ack)) left = ack; + ranges[out].left = left; + ranges[out].right = right; + out++; } - return changed; + *count = out; } -static tcp_tx_seg_t *tcp_find_sack_retransmit(tcp_flow_t *flow) { - if (!flow) return NULL; - tcp_tx_seg_t *best = NULL; - uint32_t best_seq = 0; - uint32_t highest_sacked = flow->tx.snd_una; +static bool tcp_sack_is_lost(tcp_flow_t *flow, uint32_t right) { + if (!flow) return false; - for (uint32_t i = 0; i < TCP_MAX_TX_SEGS; i++) { - tcp_tx_seg_t *s = &flow->tx.txq[i]; + uint32_t high_sacked = flow->tx.sack_range_count ? flow->tx.sack_ranges[flow->tx.sack_range_count - 1].right : flow->tx.snd_una; + if (TCP_SEQ_GEQ(right, high_sacked)) return false; - if (!s->used || !s->sacked) continue; + uint32_t bytes = 0; + uint32_t blocks = 0; + uint32_t mss = flow->tx.mss ? flow->tx.mss : TCP_DEFAULT_MSS; - uint32_t end = s->seq + s->len + (s->syn ? 1u : 0u) + (s->fin ? 1u : 0u); - if (TCP_SEQ_LEQ(end, flow->tx.snd_una)) continue; - if (TCP_SEQ_GT(end, highest_sacked)) highest_sacked = end; + for (uint8_t i = 0; i < flow->tx.sack_range_count; i++) { + tcp_sack_range_t *range = &flow->tx.sack_ranges[i]; + if (TCP_SEQ_LEQ(range->right, right)) continue; + + uint32_t left = TCP_SEQ_LT(range->left, right) ? right : range->left; + if (TCP_SEQ_GT(range->right, left)) bytes += range->right - left; + blocks++; } - if (highest_sacked == flow->tx.snd_una) return NULL; + return blocks >= 3 || bytes >= 3 * mss; +} - for (uint32_t i = 0; i < TCP_MAX_TX_SEGS; i++) { - tcp_tx_seg_t *s = &flow->tx.txq[i]; +static bool tcp_sack_select_retransmit(tcp_flow_t *flow, bool force, bool rescue, tcp_tx_seg_t **out_seg, uint32_t *out_left, uint32_t *out_right) { + if (!flow || !out_seg || !out_left || !out_right) return false; + + tcp_tx_seg_t *best_seg = NULL; + uint32_t best_left = 0; + uint32_t best_right = 0; + uint32_t limit = rescue ? (flow->tx.recover ? flow->tx.recover : flow->tx.snd_nxt) : (flow->tx.sack_range_count ? flow->tx.sack_ranges[flow->tx.sack_range_count-1].right : flow->tx.snd_una); //illeggibile + uint32_t mss = flow->tx.mss ? flow->tx.mss : TCP_DEFAULT_MSS; + + for (int i = 0; i < TCP_MAX_TX_SEGS; i++) { + tcp_tx_seg_t *seg = &flow->tx.txq[i]; + if (!seg->used || seg->syn || seg->fin || !seg->len) continue; + + uint32_t start = seg->seq; + uint32_t end = seg->seq + (uint32_t)seg->len; + if (TCP_SEQ_LEQ(end, flow->tx.snd_una) || TCP_SEQ_GEQ(start, limit)) continue; + if (TCP_SEQ_LT(start, flow->tx.snd_una)) start = flow->tx.snd_una; + if (TCP_SEQ_GT(end, limit)) end = limit; + + uint32_t cursor = start; + for (uint8_t r = 0; r <= flow->tx.sack_range_count && TCP_SEQ_LT(cursor, end); r++) { + uint32_t hole_right = end; + if (r < flow->tx.sack_range_count){ + tcp_sack_range_t *range = &flow->tx.sack_ranges[r]; + if (TCP_SEQ_LEQ(range->right, cursor)) continue; + if (TCP_SEQ_LEQ(range->left, cursor)) { + cursor = range->right; + continue; + } + if (TCP_SEQ_LT(range->left, hole_right)) hole_right = range->left; + } + + if (TCP_SEQ_GT(hole_right, cursor) && (rescue || force || tcp_sack_is_lost(flow, hole_right))) { + uint32_t left = cursor; + uint32_t right = hole_right; + + if (rescue) { + if (right - left > mss) left = right - mss; + if (!best_seg || TCP_SEQ_GT(right, best_right)) { + best_seg = seg; + best_left = left; + best_right = right; + } + } else { + for (uint8_t n = 0; n < flow->tx.sack_retransmitted_count; n++) { + tcp_sack_range_t *range = &flow->tx.sack_retransmitted_ranges[n]; + if (TCP_SEQ_LEQ(range->right, left)) continue; + if (TCP_SEQ_GT(range->left, left)) { + if (TCP_SEQ_LT(range->left, right)) right = range->left; + break; + } + left = range->right; + if (TCP_SEQ_GEQ(left, hole_right)) break; + } + + if (TCP_SEQ_GT(right, left)) { + if (right - left > mss) right = left + mss; + if (!best_seg || TCP_SEQ_LT(left, best_left)) { + best_seg = seg; + best_left = left; + best_right = right; + } + } + } + } - if (!s->used)continue; - if (s->sacked || s->sack_retransmitted) continue; + if (r < flow->tx.sack_range_count && TCP_SEQ_GT(flow->tx.sack_ranges[r].right, cursor)) cursor = flow->tx.sack_ranges[r].right; + else break; + } + } - uint32_t end = s->seq + s->len + (s->syn ? 1 : 0) + (s->fin ? 1 : 0); - if (TCP_SEQ_LEQ(end, flow->tx.snd_una)) continue; - if (TCP_SEQ_GEQ(s->seq, highest_sacked)) continue; + if (!best_seg) return false; + *out_seg = best_seg; + *out_left = best_left; + *out_right = best_right; + return true; +} - if (!best || TCP_SEQ_LT(s->seq, best_seq)) { - best = s; - best_seq = s->seq; +static void tcp_sack_recovery_send(tcp_flow_t *flow, bool force_first) { + if (!flow || !flow->tx.sack_ok) return; + + uint32_t pipe = 0; + for (int i = 0; i < TCP_MAX_TX_SEGS; i++) { + tcp_tx_seg_t *seg = &flow->tx.txq[i]; + + if (!seg->used) continue; + + uint32_t left = seg->seq; + uint32_t right = seg->seq + (uint32_t)seg->len + seg->syn + seg->fin; + if (TCP_SEQ_LEQ(right, flow->tx.snd_una)) continue; + if (TCP_SEQ_LT(left, flow->tx.snd_una)) left = flow->tx.snd_una; + + uint32_t cursor = left; + for (uint8_t r = 0; r <= flow->tx.sack_range_count && TCP_SEQ_LT(cursor, right); r++) { + uint32_t hole_right = right; + if (r < flow->tx.sack_range_count) { + tcp_sack_range_t *range = &flow->tx.sack_ranges[r]; + if (TCP_SEQ_LEQ(range->right, cursor)) continue; + if (TCP_SEQ_LEQ(range->left, cursor)) { + cursor = range->right; + continue; + } + if (TCP_SEQ_LT(range->left, hole_right)) hole_right = range->left; + } + + if (TCP_SEQ_GT(hole_right, cursor)) { + if (tcp_sack_is_lost(flow, hole_right)) { + uint32_t retransmitted = 0; + for (uint8_t n = 0; n < flow->tx.sack_retransmitted_count; n++) { + tcp_sack_range_t *range = &flow->tx.sack_retransmitted_ranges[n]; + if (TCP_SEQ_GEQ(cursor, range->right) || TCP_SEQ_GEQ(range->left, hole_right)) continue; + + uint32_t start = TCP_SEQ_GT(cursor, range->left) ? cursor : range->left; + uint32_t end = TCP_SEQ_LT(hole_right, range->right) ? hole_right : range->right; + if (TCP_SEQ_GT(end, start)) retransmitted += end - start; + } + + uint32_t hole_len = hole_right - cursor; + pipe += retransmitted > hole_len ? hole_len : retransmitted; + } else { + pipe += hole_right - cursor; + } + } + + if (r < flow->tx.sack_range_count && TCP_SEQ_GT(flow->tx.sack_ranges[r].right, cursor)) cursor = flow->tx.sack_ranges[r].right; + else break; } } - return best; + uint32_t cwnd = flow->tx.cwnd ? flow->tx.cwnd : (flow->tx.mss ? flow->tx.mss : TCP_DEFAULT_MSS); + + for (uint32_t sent = 0; sent < TCP_SACK_SCOREBOARD_MAX && pipe < cwnd; sent++) { + tcp_tx_seg_t *seg = NULL; + uint32_t left = 0; + uint32_t right = 0; + bool rescue = false; + if (!tcp_sack_select_retransmit(flow, force_first, false, &seg, &left, &right)) { + if (flow->tx.sack_rescue_sent || !tcp_sack_select_retransmit(flow, false, true, &seg, &left, &right)) break; + rescue = true; + } + uint32_t len = right - left; + if (!len || len > cwnd - pipe) break; + tcp_tx_seg_t retransmit = { + .seq = left, + .len = len, + .pkt = seg->pkt, + .payload_off = seg->payload_off + (left - seg->seq) + }; + if (!tcp_send_from_seg(flow, &retransmit)) break; + + tcp_sack_insert(flow->tx.sack_retransmitted_ranges, &flow->tx.sack_retransmitted_count, left, right); + if (rescue) flow->tx.sack_rescue_sent = 1; + seg->retransmit_cnt++; + seg->timer_ms = 0; + pipe += len; + force_first = false; + } +} + +static int tcp_apply_sack_blocks(tcp_flow_t *flow, const tcp_parsed_opts_t *opts) { + if (!flow || !opts || !flow->tx.sack_ok || !opts->sack_count) return 0; + + int changed = 0; + for (uint32_t b = 0; b < opts->sack_count; b++) { + uint32_t left = opts->sacks[b].left; + uint32_t right = opts->sacks[b].right; + if (TCP_SEQ_LEQ(right, left)) continue; + if (TCP_SEQ_LEQ(right, flow->tx.snd_una)) continue; + if (TCP_SEQ_GEQ(left, flow->tx.snd_nxt)) continue; + if (TCP_SEQ_LT(left, flow->tx.snd_una)) left = flow->tx.snd_una; + if (TCP_SEQ_GT(right, flow->tx.snd_nxt)) right = flow->tx.snd_nxt; + if (TCP_SEQ_LEQ(right, left)) continue; + changed |= tcp_sack_insert(flow->tx.sack_ranges, &flow->tx.sack_range_count, left, right); + } + + return changed; } void tcp_cc_on_timeout(tcp_flow_t *f){ @@ -245,10 +442,9 @@ void tcp_cc_on_timeout(tcp_flow_t *f){ f->tx.in_fast_recovery = 0; f->tx.recover = 0; - for (uint32_t i = 0; i < TCP_MAX_TX_SEGS; i++) { - f->tx.txq[i].sacked = 0; - f->tx.txq[i].sack_retransmitted = 0; - } + f->tx.sack_range_count = 0; + f->tx.sack_retransmitted_count = 0; + f->tx.sack_rescue_sent = 0; } static void tcp_cc_on_new_ack(tcp_flow_t *f, uint32_t ack) { @@ -262,6 +458,9 @@ static void tcp_cc_on_new_ack(tcp_flow_t *f, uint32_t ack) { f->tx.in_fast_recovery = 0; f->tx.dup_acks = 0; f->tx.cwnd_acc = 0; + f->tx.sack_range_count = 0; + f->tx.sack_retransmitted_count = 0; + f->tx.sack_rescue_sent = 0; return; } @@ -289,14 +488,14 @@ static void tcp_cc_on_dupack(tcp_flow_t *f) { if (f->tx.in_fast_recovery){ f->tx.cwnd += mss; - - tcp_tx_seg_t *s = tcp_find_sack_retransmit(f); - if (s && tcp_send_from_seg(f, s)) { - s->sack_retransmitted = 1; - s->retransmit_cnt++; - s->timer_ms = 0; + if (f->tx.sack_ok) tcp_sack_recovery_send(f, false); + else { + tcp_tx_seg_t *seg = tcp_find_first_unacked(f); + if (seg && tcp_send_from_seg(f, seg)) { + seg->retransmit_cnt++; + seg->timer_ms = 0; + } } - return; } @@ -312,13 +511,16 @@ static void tcp_cc_on_dupack(tcp_flow_t *f) { f->tx.recover = f->tx.snd_nxt; f->tx.cwnd = f->tx.ssthresh + 3u * mss; f->tx.in_fast_recovery = 1; + f->tx.sack_retransmitted_count = 0; + f->tx.sack_rescue_sent = 0; - tcp_tx_seg_t *s = tcp_find_sack_retransmit(f); - if (!s) s = tcp_find_first_unacked(f); - if (s && tcp_send_from_seg(f, s)) { - s->sack_retransmitted = 1; - s->retransmit_cnt++; - s->timer_ms = 0; + if (f->tx.sack_ok) tcp_sack_recovery_send(f, true); + else { + tcp_tx_seg_t *seg = tcp_find_first_unacked(f); + if (seg && tcp_send_from_seg(f, seg)) { + seg->retransmit_cnt++; + seg->timer_ms = 0; + } } } @@ -485,8 +687,6 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a flow->tx.ws_recv = 0; } - flow->tx.sack_ok = pop.sack_permitted ? 1 : 0; - flow->tx.path_mss = tcp_calc_mss_for_l3(l3_id, ipver, src_ip_addr); flow->tx.peer_mss = pop.has_mss && pop.mss ? pop.mss : 0; flow->base.ctx.flags = 0; @@ -521,6 +721,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a if (listener_extra && (listener_extra->flags & SOCK_OPT_BUF_SIZE) && listener_extra->buf_size) rcvbuf = listener_extra->buf_size; flow->rx.rcv_wnd_max = tcp_clamp_rcvbuf(rcvbuf); tcp_flow_apply_options(flow, listener_extra, UINT32_MAX); + flow->tx.sack_ok = flow->tx.sack_enabled && pop.sack_permitted; if (flow->rx.rcv_wnd_max > 65535u && pop.has_wscale) { flow->tx.ws_send = 8; flow->tx.ws_ok = 1; @@ -643,28 +844,42 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a flow->tx.dup_acks = 0; for (uint32_t i = 0; i < TCP_MAX_TX_SEGS; i++){ - tcp_tx_seg_t *s = &flow->tx.txq[i]; - if (!s->used) continue; + tcp_tx_seg_t *seg = &flow->tx.txq[i]; + if (!seg->used || TCP_SEQ_GEQ(seg->seq, ack)) continue; - uint32_t s_end = s->seq + s->len + (s->syn ? 1u : 0u) + (s->fin ? 1u : 0u); + if (seg->rtt_sample && seg->retransmit_cnt == 0) { + tcp_rtt_update(flow, seg->timer_ms); + seg->rtt_sample = 0; + } - if (TCP_SEQ_LEQ(s_end, ack)){ - if (s->rtt_sample && s->retransmit_cnt == 0) tcp_rtt_update(flow, s->timer_ms); - tcp_tx_seg_clear(flow, s); + uint32_t seq = seg->seq; + if (seg->syn && TCP_SEQ_GT(ack, seq)) { + seg->syn = 0; + seq++; } - } - for (uint32_t i = 0; i < TCP_MAX_TX_SEGS; i++) { - tcp_tx_seg_t *s = &flow->tx.txq[i]; - if (!s->used) continue; - if (TCP_SEQ_GEQ(s->seq, ack)) continue; + if (seg->len && TCP_SEQ_GT(ack, seq)) { + uint32_t n = ack - seq; + if (n > seg->len) n = (uint32_t)seg->len; + seg->payload_off += n; + seg->len -= n; + seq += n; + tcp_account_tx_remove(flow, n); + } + if (seg->fin && TCP_SEQ_GT(ack, seq)) { + seg->fin = 0; + seq++; + } - s->sacked = 0; - s->sack_retransmitted = 0; + seg->seq = seq; + if (!seg->syn && !seg->fin && !seg->len) tcp_tx_seg_clear(flow, seg); } + tcp_sack_trim_ranges(flow->tx.sack_ranges, &flow->tx.sack_range_count, ack); + tcp_sack_trim_ranges(flow->tx.sack_retransmitted_ranges, &flow->tx.sack_retransmitted_count, ack); if (flow->tx.sack_ok && parsed_opts.sack_count) tcp_apply_sack_blocks(flow, &parsed_opts); if (TCP_SEQ_GT(ack, prev_una)) tcp_cc_on_new_ack(flow, ack); + if (flow->tx.in_fast_recovery && flow->tx.sack_ok) tcp_sack_recovery_send(flow, false); if (flow->base.state == TCP_FIN_WAIT_1 && TCP_SEQ_GEQ(ack, flow->base.ctx.expected_ack)){ flow->base.state = TCP_FIN_WAIT_2; @@ -721,7 +936,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a flow->tx.ws_recv = 0; } - flow->tx.sack_ok = pop.sack_permitted ? 1 : 0; + flow->tx.sack_ok = flow->tx.sack_enabled && pop.sack_permitted; flow->tx.path_mss = tcp_calc_mss_for_l3(l3_id, ipver, src_ip_addr); flow->tx.peer_mss = pop.has_mss && pop.mss ? pop.mss : 0; @@ -855,6 +1070,16 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a uint32_t fin_seq = seg_seq + orig_data_len; uint32_t orig_end = seg_seq + orig_data_len + (fin ? 1u : 0u); + if (flow->tx.sack_ok && flow->tx.dsack_enabled && orig_data_len && TCP_SEQ_LT(seg_seq, rcv_nxt)) { + uint32_t duplicate_right = seg_seq + orig_data_len; + if (TCP_SEQ_GT(duplicate_right, rcv_nxt)) duplicate_right = rcv_nxt; + if (TCP_SEQ_GT(duplicate_right, seg_seq)) { + flow->rx.dsack_pending = 1; + flow->rx.dsack_left = seg_seq; + flow->rx.dsack_right = duplicate_right; + } + } + if (TCP_SEQ_LEQ(orig_end, rcv_nxt) || TCP_SEQ_GEQ(seg_seq, wnd_end)) { need_ack = true; ack_immediate = true; @@ -1000,11 +1225,25 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a if (TCP_SEQ_LEQ(r->seq, start) && TCP_SEQ_GEQ(r->end, end)) { flow->rx.sack_recent_left = r->seq; flow->rx.sack_recent_right = r->end; + if (flow->tx.sack_ok && flow->tx.dsack_enabled) { + flow->rx.dsack_pending = 1; + flow->rx.dsack_left = start; + flow->rx.dsack_right = end; + } covered = true; break; } if (TCP_SEQ_LT(r->end, merged_start) || TCP_SEQ_GT(r->seq, merged_end)) continue; + if (flow->tx.sack_ok && flow->tx.dsack_enabled && !flow->rx.dsack_pending) { + uint32_t duplicate_left = TCP_SEQ_GT(start, r->seq) ? start : r->seq; + uint32_t duplicate_right = TCP_SEQ_LT(end, r->end) ? end : r->end; + if (TCP_SEQ_GT(duplicate_right, duplicate_left)) { + flow->rx.dsack_pending = 1; + flow->rx.dsack_left = duplicate_left; + flow->rx.dsack_right = duplicate_right; + } + } if (TCP_SEQ_LT(r->seq, merged_start)) merged_start = r->seq; if (TCP_SEQ_GT(r->end, merged_end)) merged_end = r->end; diff --git a/kernel/networking/transport_layer/tcp/tcp_tx.c b/kernel/networking/transport_layer/tcp/tcp_tx.c index 823e64a3..d046cce7 100644 --- a/kernel/networking/transport_layer/tcp/tcp_tx.c +++ b/kernel/networking/transport_layer/tcp/tcp_tx.c @@ -309,11 +309,17 @@ bool tcp_send_ack_now(tcp_flow_t *flow){ uint8_t opts[64]; uint8_t opts_len = 0; - if (flow->tx.sack_ok && flow->rx.reass_count > 0) { + if (flow->tx.sack_ok && (flow->rx.reass_count > 0 || (flow->tx.dsack_enabled && flow->rx.dsack_pending))) { tcp_sack_block_t blocks[TCP_SACK_MAX_BLOCKS]; uint32_t n = 0; - if (flow->rx.sack_recent_right > flow->rx.sack_recent_left) { + if (flow->tx.dsack_enabled && flow->rx.dsack_pending && TCP_SEQ_GT(flow->rx.dsack_right, flow->rx.dsack_left)) { + blocks[n].left = flow->rx.dsack_left; + blocks[n].right = flow->rx.dsack_right; + n++; + } + + if (n < TCP_SACK_MAX_BLOCKS && TCP_SEQ_GT(flow->rx.sack_recent_right, flow->rx.sack_recent_left)) { for (uint32_t i = 0; i < flow->rx.reass_count; i++) { if (TCP_SEQ_GT(flow->rx.reass[i].seq, flow->rx.sack_recent_left)) continue; if (TCP_SEQ_LT(flow->rx.reass[i].end, flow->rx.sack_recent_right)) continue; @@ -372,6 +378,10 @@ bool tcp_send_ack_now(tcp_flow_t *flow){ tcp_daemon_kick(); return false; } + + flow->rx.dsack_pending = 0; + flow->rx.dsack_left = 0; + flow->rx.dsack_right = 0; flow->timer.delayed_ack_pending = 0; flow->timer.delayed_ack_timer_ms = 0; return true; From 17bb12d07649c3470e337d5daa7501070a396d58 Mon Sep 17 00:00:00 2001 From: CodeAnarchist Date: Sun, 9 Aug 2026 12:38:56 +0200 Subject: [PATCH 39/39] [tcp] improve recovery, flow control and conn handling --- .../networking/transport_layer/csocket_ctrl.c | 1 + .../transport_layer/csocket_packet.c | 1 + .../networking/transport_layer/csocket_raw.c | 1 + .../networking/transport_layer/csocket_tcp.c | 59 +- .../networking/transport_layer/csocket_udp.c | 1 + kernel/networking/transport_layer/tcp.h | 18 +- .../networking/transport_layer/tcp/tcp_core.c | 32 +- .../transport_layer/tcp/tcp_internal.h | 30 +- .../networking/transport_layer/tcp/tcp_rx.c | 903 ++++++++++++------ .../transport_layer/tcp/tcp_timer.c | 128 +-- .../networking/transport_layer/tcp/tcp_tx.c | 184 +++- .../transport_layer/tcp/tcp_utils.c | 13 +- .../transport_layer/tcp/tcp_utils.h | 1 + shared | 2 +- 14 files changed, 896 insertions(+), 478 deletions(-) diff --git a/kernel/networking/transport_layer/csocket_ctrl.c b/kernel/networking/transport_layer/csocket_ctrl.c index f409341e..aa7d2c51 100644 --- a/kernel/networking/transport_layer/csocket_ctrl.c +++ b/kernel/networking/transport_layer/csocket_ctrl.c @@ -76,6 +76,7 @@ int32_t socket_getopt_ctrl(socket_impl_t sh, int32_t opt, void* value, uint32_t* case SOCK_GET_TCP_MSS: case SOCK_GET_TCP_RTT_MS: case SOCK_GET_TCP_RETRANSMITS: + case SOCK_GET_TCP_URGENT_REMAINING: return SOCK_ERR_UNSUP; default: return SOCK_ERR_INVAL; diff --git a/kernel/networking/transport_layer/csocket_packet.c b/kernel/networking/transport_layer/csocket_packet.c index c4973a81..9e6e8f45 100644 --- a/kernel/networking/transport_layer/csocket_packet.c +++ b/kernel/networking/transport_layer/csocket_packet.c @@ -255,6 +255,7 @@ int32_t socket_getopt_packet(socket_impl_t sh, int32_t opt, void* value, uint32_ case SOCK_GET_TCP_MSS: case SOCK_GET_TCP_RTT_MS: case SOCK_GET_TCP_RETRANSMITS: + case SOCK_GET_TCP_URGENT_REMAINING: return SOCK_ERR_UNSUP; default: return SOCK_ERR_INVAL; diff --git a/kernel/networking/transport_layer/csocket_raw.c b/kernel/networking/transport_layer/csocket_raw.c index ceaa3be7..ebdcc1aa 100644 --- a/kernel/networking/transport_layer/csocket_raw.c +++ b/kernel/networking/transport_layer/csocket_raw.c @@ -277,6 +277,7 @@ int32_t socket_getopt_raw(socket_impl_t sh, int32_t opt, void* value, uint32_t* case SOCK_GET_TCP_MSS: case SOCK_GET_TCP_RTT_MS: case SOCK_GET_TCP_RETRANSMITS: + case SOCK_GET_TCP_URGENT_REMAINING: case SOCK_GET_MCAST_GROUPS: return SOCK_ERR_UNSUP; case SOCK_GET_OPT_RECV_TIMEOUT: diff --git a/kernel/networking/transport_layer/csocket_tcp.c b/kernel/networking/transport_layer/csocket_tcp.c index be23678b..e92a7b9c 100644 --- a/kernel/networking/transport_layer/csocket_tcp.c +++ b/kernel/networking/transport_layer/csocket_tcp.c @@ -16,7 +16,6 @@ #include "exceptions/irq.h" #define TCP_MAX_BACKLOG 128 -#define TCP_DEFAULT_SOCKET_BUF (256 * 1024) typedef struct tcp_socket { uint16_t localPort; @@ -103,7 +102,7 @@ static int32_t tcp_socket_connection_state(tcp_socket_t* s) { } if (state == TCP_STATE_CLOSED || state == TCP_TIME_WAIT) { - tcp_flow_release_closed(&s->flow); + if (tcp_flow_release_closed(&s->flow) == TCP_BUSY) return 0; tcp_socket_reset_connection(s, false); return -1; } @@ -133,7 +132,7 @@ socket_impl_t socket_tcp_create(ksocket_t* owner, const SocketOptions* extra) { s->options.flags |= SOCK_OPT_TCP_SACK | SOCK_OPT_TCP_DSACK; if (!(s->options.flags & SOCK_OPT_BUF_SIZE)) { s->options.flags |= SOCK_OPT_BUF_SIZE; - s->options.buf_size = TCP_DEFAULT_SOCKET_BUF; + s->options.buf_size = TCP_DEFAULT_RCV_BUF; } s->options.buf_size = tcp_clamp_rcvbuf(s->options.buf_size); @@ -355,6 +354,14 @@ int32_t socket_getopt_tcp(socket_impl_t sh, int32_t opt, void* value, uint32_t* } break; } + case SOCK_GET_TCP_URGENT_REMAINING: { + tcp_flow_t* flow = tcp_flow_from_ctx(&s->flow); + if (flow) { + if (flow->rx.urg_valid && TCP_SEQ_GT(flow->rx.urg_seq, flow->rx.rcv_base)) v = flow->rx.urg_seq - flow->rx.rcv_base; + tcp_flow_put(flow); + } + break; + } case SOCK_GET_OPT_KEEPALIVE: v = (s->options.flags & SOCK_OPT_KEEPALIVE) != 0; break; @@ -513,8 +520,7 @@ int32_t socket_connect_tcp(socket_impl_t sh, const net_l4_endpoint* dst) { if (s->listening) return SOCK_ERR_STATE; if (!dst || !dst->port) return SOCK_ERR_INVAL; if (s->flow.flow_generation) { - if (s->remoteEP.port && - (s->remoteEP.ver != dst->ver || s->remoteEP.port != dst->port || memcmp(s->remoteEP.ip, dst->ip, dst->ver == IP_VER6 ? 16 : 4) != 0)) return SOCK_ERR_STATE; + if (s->remoteEP.port && (s->remoteEP.ver != dst->ver || s->remoteEP.port != dst->port || memcmp(s->remoteEP.ip, dst->ip, dst->ver == IP_VER6 ? 16 : 4) != 0)) return SOCK_ERR_STATE; } else { if (s->connected) return SOCK_ERR_STATE; @@ -522,6 +528,7 @@ int32_t socket_connect_tcp(socket_impl_t sh, const net_l4_endpoint* dst) { uint8_t chosen_l3 = 0; if (d.ver == IP_VER6) { + if (ipv6_is_unspecified(d.ip) || ipv6_is_multicast(d.ip)) return SOCK_ERR_INVAL; ipv6_tx_plan_t p6; if (!socket_bind_build_ipv6_tx_plan(&s->bindSpec, s->localPort != 0, d.ip, &p6)) return SOCK_ERR_NO_ROUTE; if ((s->options.flags & SOCK_OPT_DONTROUTE) && !ipv6_tx_plan_onlink(&p6, d.ip)) return SOCK_ERR_NO_ROUTE; @@ -529,6 +536,7 @@ int32_t socket_connect_tcp(socket_impl_t sh, const net_l4_endpoint* dst) { } else if (d.ver == IP_VER4) { uint32_t dip = 0; memcpy(&dip, d.ip, 4); + if (ipv4_is_unspecified(dip) || ipv4_is_multicast(dip) || ipv4_is_limited_broadcast(dip)) return SOCK_ERR_INVAL; ipv4_tx_plan_t p4; if (!socket_bind_build_ipv4_tx_plan(&s->bindSpec, s->localPort != 0, dip, &p4)) return SOCK_ERR_NO_ROUTE; if ((s->options.flags & SOCK_OPT_DONTROUTE) && !ipv4_tx_plan_onlink(&p4, dip)) return SOCK_ERR_NO_ROUTE; @@ -540,6 +548,9 @@ int32_t socket_connect_tcp(socket_impl_t sh, const net_l4_endpoint* dst) { if (d.ver == IP_VER4) { l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(chosen_l3); if (!ipv4_l3_is_ready(v4)) return SOCK_ERR_SYS; + uint32_t dip = 0; + memcpy(&dip, d.ip, 4); + if (ipv4_is_directed_broadcast(v4->ip, v4->mask, dip)) return SOCK_ERR_INVAL; } else if (d.ver == IP_VER6) { l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(chosen_l3); if (!ipv6_l3_is_tcp_usable(v6)) return SOCK_ERR_SYS; @@ -630,23 +641,7 @@ int64_t socket_send_tcp(socket_impl_t sh, const void* buf, uint64_t len) { s->flow.flags = (1u << PSH_F) | (1u << ACK_F); tcp_result_t res = tcp_flow_send(&s->flow); - if (res != TCP_OK && res != TCP_WOULDBLOCK) { - switch (res) { - case TCP_BUSY: - case TCP_TIMEOUT: - return SOCK_ERR_WOULDBLOCK; - case TCP_UNIMPLEMENT: - return SOCK_ERR_UNSUP; - case TCP_INVALID: - case TCP_RESET: - case TCP_DISCONNECT: - return SOCK_ERR_STATE; - case TCP_CSUM_ERR: - return SOCK_ERR_PROTO; - default: - return SOCK_ERR_SYS; - } - } + if (res != TCP_OK && res != TCP_WOULDBLOCK) return res == TCP_INVALID ? SOCK_ERR_STATE : SOCK_ERR_SYS; if (s->flow.payload.size) return (int64_t)s->flow.payload.size; if (s->options.flags & SOCK_OPT_NONBLOCK) return SOCK_ERR_WOULDBLOCK; @@ -727,8 +722,11 @@ int32_t socket_close_tcp(socket_impl_t sh) { else { bool closed = tcp_flow_is_closed(&s->flow); if (connection_state > 0 && !closed) { - tcp_flow_flush(&s->flow); - tcp_flow_close(&s->flow); + tcp_result_t flush_rc = tcp_flow_flush(&s->flow); + if (flush_rc == TCP_OK || flush_rc == TCP_WOULDBLOCK) { + tcp_result_t close_rc = tcp_flow_close(&s->flow); + if (close_rc != TCP_OK && close_rc != TCP_INVALID) tcp_flow_abort(&s->flow); + } else if (flush_rc != TCP_INVALID) tcp_flow_abort(&s->flow); closed = tcp_flow_is_closed(&s->flow); } @@ -742,7 +740,10 @@ int32_t socket_close_tcp(socket_impl_t sh) { closed = tcp_flow_is_closed(&s->flow); } - if (closed) tcp_flow_release_closed(&s->flow); + if (closed) { + tcp_result_t release_rc = tcp_flow_release_closed(&s->flow); + if (release_rc != TCP_OK && release_rc != TCP_INVALID && release_rc != TCP_BUSY) tcp_flow_abort(&s->flow); + } } } } @@ -775,7 +776,13 @@ int32_t socket_close_tcp(socket_impl_t sh) { void socket_destroy_tcp(socket_impl_t sh) { tcp_socket_t* s = (tcp_socket_t*)sh; if (!s) return; - socket_close_tcp(s); + int32_t rc = socket_close_tcp(s); + if (rc == SOCK_ERR_WOULDBLOCK) { + s->options.flags &= ~(SOCK_OPT_NONBLOCK | SOCK_OPT_LINGER); + memset(&s->options.linger, 0, sizeof(s->options.linger)); + rc = socket_close_tcp(s); + } + if (rc != SOCK_OK) tcp_socket_reset_connection(s, true); release(s); } diff --git a/kernel/networking/transport_layer/csocket_udp.c b/kernel/networking/transport_layer/csocket_udp.c index b042e8e7..75304611 100644 --- a/kernel/networking/transport_layer/csocket_udp.c +++ b/kernel/networking/transport_layer/csocket_udp.c @@ -876,6 +876,7 @@ int32_t socket_getopt_udp(socket_impl_t sh, int32_t opt, void* value, uint32_t* case SOCK_GET_TCP_MSS: case SOCK_GET_TCP_RTT_MS: case SOCK_GET_TCP_RETRANSMITS: + case SOCK_GET_TCP_URGENT_REMAINING: irq_restore(irq); return SOCK_ERR_UNSUP; default: diff --git a/kernel/networking/transport_layer/tcp.h b/kernel/networking/transport_layer/tcp.h index dbb1a0d6..78f4382a 100644 --- a/kernel/networking/transport_layer/tcp.h +++ b/kernel/networking/transport_layer/tcp.h @@ -19,17 +19,12 @@ extern "C" { #define URG_F 5 #define ECE_F 6 #define CWR_F 7 - +//todo ECE_F CWR_F rfc 3168 8311 9293 typedef enum { TCP_OK = 0, - TCP_RETRY = 1, - TCP_RESET = 2, - TCP_TIMEOUT = -2, - TCP_CSUM_ERR = -3, TCP_INVALID = -4, TCP_WOULDBLOCK = -5, TCP_DISCONNECT = -6, - TCP_UNIMPLEMENT = -10, TCP_BUSY = -11, } tcp_result_t; @@ -74,17 +69,16 @@ typedef enum { } tcp_state_t; #define MAX_TCP_FLOWS 512 -#define TCP_SYN_RETRIES 5 #define TCP_RECV_WINDOW 65535 #define TCP_MAX_TX_SEGS 128 -#define TCP_INIT_RTO 200 +#define TCP_INIT_RTO 1000 #define TCP_MIN_RTO 200 #define TCP_MAX_RTO 60000 +#define TCP_SYN_DATA_RTO 3000 #define TCP_MSL_MS 30000 -#define SOCKET_DEFAULT_KEEPALIVE_MS 60000 +#define SOCKET_DEFAULT_KEEPALIVE_MS (2u * 60u * 60u * 1000u) #define TCP_2MSL_MS (2 * TCP_MSL_MS) #define TCP_MAX_RETRANS 8 -#define TCP_MAX_PERSIST_PROBES 8 bool tcp_get_ctx(uint16_t local_port, ip_version_t ver, const void *local_ip, const void *remote_ip, uint16_t remote_port, tcp_data *out_ctx); bool tcp_bind_conflicts(const SockBindSpec* spec, uint16_t port, bool reuseaddr); @@ -95,15 +89,13 @@ void tcp_flow_apply_socket_options(tcp_data *flow_ctx, const SocketOptions* extr tcp_result_t tcp_flow_send(tcp_data *flow_ctx); tcp_result_t tcp_flow_flush(tcp_data *flow_ctx); tcp_result_t tcp_flow_close(tcp_data *flow_ctx); -tcp_result_t tcp_flow_abort(tcp_data *flow_ctx); +void tcp_flow_abort(tcp_data *flow_ctx); bool tcp_flow_is_closed(tcp_data *flow_ctx); tcp_result_t tcp_flow_release_closed(tcp_data *flow_ctx); int64_t tcp_flow_read(tcp_data *flow_ctx, void *buf, uint64_t len); uint32_t tcp_flow_readable(tcp_data *flow_ctx); bool tcp_flow_recv_closed(tcp_data *flow_ctx); -void tcp_flow_on_app_read(tcp_data *flow_ctx, uint32_t bytes_read); - void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_addr, uint8_t l3_id, netpkt_t* pkt); #ifdef __cplusplus diff --git a/kernel/networking/transport_layer/tcp/tcp_core.c b/kernel/networking/transport_layer/tcp/tcp_core.c index f803c165..fbffff18 100644 --- a/kernel/networking/transport_layer/tcp/tcp_core.c +++ b/kernel/networking/transport_layer/tcp/tcp_core.c @@ -179,7 +179,7 @@ void tcp_flow_apply_options(tcp_flow_t *flow, const SocketOptions* extra, uint32 if (apply_mask & SOCK_OPT_TCP_NO_DELAY) { flow->tx.nodelay = (flags & SOCK_OPT_TCP_NO_DELAY) ? 1 : 0; - if (flow->tx.nodelay && flow->tx.nagle_len) tcp_flush_nagle(flow, 1); + if (flow->tx.nodelay && flow->tx.nagle_len && flow->tx.snd_wnd > 0 && !tcp_flush_nagle(flow, 1)) tcp_daemon_kick(); } if (apply_mask & SOCK_OPT_SEND_BUF_SIZE) { @@ -233,6 +233,7 @@ static void tcp_flow_clear_storage(tcp_flow_t *flow) { flow->tx.nagle_timer_ms = 0; flow->tx.nagle_flushing = 0; flow->tx.nagle_appending = 0; + flow->tx.nagle_psh = 0; if (flow->rx.reass_count || flow->rx.rcv_ooo_used) tcp_account_ooo_remove(flow->rx.rcv_ooo_used, flow->rx.reass_count); if (flow->rx.rcv_buf) { @@ -350,7 +351,7 @@ tcp_flow_t *tcp_alloc_flow(void){ f->tx.mss = TCP_DEFAULT_MSS; f->tx.sack_enabled = 1; f->tx.dsack_enabled = 1; - f->tx.cwnd = f->tx.mss * TCP_INIT_CWND_SEGS; + f->tx.cwnd = tcp_initial_cwnd(f->tx.mss); f->tx.ssthresh = TCP_RECV_WINDOW; return f; } @@ -395,16 +396,15 @@ void tcp_free_flow(tcp_flow_t *flow) { tcp_flow_put(flow); } -tcp_result_t tcp_flow_abort(tcp_data *flow_ctx) { +void tcp_flow_abort(tcp_data *flow_ctx) { tcp_flow_t *flow = tcp_flow_from_ctx(flow_ctx); - if (!flow) return TCP_INVALID; + if (!flow) return; tcp_state_t state = flow->base.state; if (state != TCP_STATE_CLOSED && state != TCP_TIME_WAIT && state != TCP_SYN_SENT) tcp_send_reset(flow->base.l3_id, flow->base.local.ver, flow->base.local.ip, flow->base.remote.ip, flow->base.local.port, flow->base.remote.port, flow->tx.snd_nxt, 0, false); tcp_free_flow(flow); tcp_flow_put(flow); - return TCP_OK; } bool tcp_flow_is_closed(tcp_data *flow_ctx) { @@ -541,7 +541,7 @@ void tcp_rtt_update(tcp_flow_t *flow, uint32_t sample_ms){ } bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, tcp_data *flow_ctx, const SocketOptions* extra){ - if (!dst || !flow_ctx) return false; + if (!dst || !flow_ctx || !local_port || !dst->port || (dst->ver != IP_VER4 && dst->ver != IP_VER6)) return false; tcp_flow_t *flow = tcp_alloc_flow(); if (!flow) return false; @@ -577,7 +577,7 @@ bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, } flow->base.state = TCP_SYN_SENT; - flow->base.retries = TCP_SYN_RETRIES; + flow->base.active_open = 1; rng_t rng; rng_init_random(&rng); @@ -610,16 +610,8 @@ bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, flow->tx.ws_ok = 0; } flow->tx.sack_ok = flow->tx.sack_enabled; - flow->rx.rcv_buf = (uintptr_t)zalloc(flow->rx.rcv_wnd_max); - if (!flow->rx.rcv_buf) { - flow->rx.rcv_wnd = 0; - flow->rx.rcv_adv_edge = flow->rx.rcv_nxt; - flow->base.ctx.window = 0; - tcp_free_flow(flow); - tcp_flow_put(flow); - return false; - } - (void)tcp_calc_adv_wnd_field(flow, 1); + flow->rx.rcv_buf = 0; + tcp_update_adv_wnd(flow, 1); flow->base.ctx.options.ptr = 0; flow->base.ctx.options.size = 0; @@ -634,11 +626,12 @@ bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, flow->tx.snd_nxt = iss; flow->tx.snd_wnd = 0; - flow->tx.cwnd = flow->tx.mss * TCP_INIT_CWND_SEGS; + flow->tx.cwnd = tcp_initial_cwnd(flow->tx.mss); flow->tx.ssthresh = TCP_RECV_WINDOW; flow->tx.dup_acks = 0; flow->tx.in_fast_recovery = 0; flow->tx.recover = 0; + flow->tx.recover_valid = 0; flow->tx.cwnd_acc = 0; flow->timer.time_wait_ms = 0; @@ -654,7 +647,10 @@ bool tcp_handshake_l3(uint8_t l3_id, uint16_t local_port, net_l4_endpoint *dst, seg->syn = 1; seg->fin = 0; + seg->psh = 0; + seg->persist = 0; seg->rtt_sample = 1; + flow->tx.rtt_sample_pending = 1; seg->retransmit_cnt = 0; seg->seq = flow->tx.snd_nxt; seg->len = 0; diff --git a/kernel/networking/transport_layer/tcp/tcp_internal.h b/kernel/networking/transport_layer/tcp/tcp_internal.h index 05ac94d4..f47eb6ed 100644 --- a/kernel/networking/transport_layer/tcp/tcp_internal.h +++ b/kernel/networking/transport_layer/tcp/tcp_internal.h @@ -18,8 +18,9 @@ extern "C" { #endif #define TCP_DEFAULT_MSS 1460 +#define TCP_DEFAULT_PEER_MSS_IPV4 536 +#define TCP_DEFAULT_PEER_MSS_IPV6 1220 #define TCP_MAX_MSS (NETPKT_MAX_ALLOC - sizeof(ipv4_hdr_t) - sizeof(tcp_hdr_t)) -#define TCP_PERSIST_PROBE_BUFSZ 1 #define TCP_SEQ_LT(a,b) ((int32_t)((uint32_t)(a) - (uint32_t)(b)) < 0) #define TCP_SEQ_LEQ(a,b) ((int32_t)((uint32_t)(a) - (uint32_t)(b)) <= 0) @@ -29,11 +30,11 @@ extern "C" { #define TCP_DELAYED_ACK_MS 10 #define TCP_PERSIST_MIN_MS 500 #define TCP_PERSIST_MAX_MS 60000 -#define TCP_INIT_CWND_SEGS 10 #define TCP_NAGLE_FLUSH_THRESHOLD TCP_DEFAULT_MSS #define TCP_NAGLE_TIMEOUT_MS 10 #define TCP_CONNECT_TIMEOUT_MS 10000 #define TCP_SACK_SCOREBOARD_MAX 32 +#define TCP_CHALLENGE_ACK_INTERVAL_MS 100 typedef struct { uint32_t left; @@ -44,6 +45,8 @@ typedef struct { uint8_t used; uint8_t syn; uint8_t fin; + uint8_t psh; + uint8_t persist; uint8_t rtt_sample; uint8_t retransmit_cnt; uint8_t opts_len; @@ -53,6 +56,7 @@ typedef struct { netpkt_t *pkt; uint32_t payload_off; uint32_t timer_ms; + uint32_t rtt_timer_ms; uint32_t timeout_ms; } tcp_tx_seg_t; @@ -70,20 +74,23 @@ typedef struct { uint8_t l3_id; tcp_state_t state; tcp_data ctx; - uint8_t retries; uint16_t refs; uint8_t retired; + uint8_t active_open; struct ksocket *listener; } tcp_flow_base_t; typedef struct { uint32_t snd_wnd; + uint32_t snd_wl1; + uint32_t snd_wl2; uint32_t snd_una; uint32_t snd_nxt; uint32_t srtt; uint32_t rttvar; uint32_t rto; uint8_t rtt_valid; + uint8_t rtt_sample_pending; uint32_t cwnd; uint32_t ssthresh; @@ -98,6 +105,7 @@ typedef struct { uint8_t sack_ok; uint8_t dup_acks; uint8_t in_fast_recovery; + uint8_t recover_valid; uint32_t recover; uint32_t cwnd_acc; @@ -113,11 +121,14 @@ typedef struct { uint8_t nagle_flushing; uint8_t nagle_appending; uint8_t nodelay; + uint8_t nagle_psh; + uint8_t data_tx_valid; uintptr_t nagle_buf; uint32_t nagle_len; uint32_t nagle_cap; uint32_t nagle_timer_ms; + uint32_t last_data_tx_ms; tcp_tx_seg_t txq[TCP_MAX_TX_SEGS]; uint32_t queued_bytes; @@ -134,6 +145,7 @@ typedef struct { uint32_t sack_recent_left; uint32_t sack_recent_right; uint8_t dsack_pending; + uint8_t urg_valid; uint32_t dsack_left; uint32_t dsack_right; uint32_t rcv_wnd; @@ -144,6 +156,7 @@ typedef struct { uint8_t reass_count; uint8_t fin_pending; uint32_t fin_seq; + uint32_t urg_seq; } tcp_flow_rx_t; typedef struct { @@ -151,7 +164,6 @@ typedef struct { uint32_t fin_wait2_ms; uint8_t persist_active; - uint8_t persist_probe_cnt; uint32_t persist_timer_ms; uint32_t persist_timeout_ms; @@ -161,6 +173,9 @@ typedef struct { uint8_t keepalive_on; uint32_t keepalive_ms; uint32_t keepalive_idle_ms; + + uint8_t challenge_ack_valid; + uint32_t challenge_ack_last_ms; } tcp_flow_timer_t; typedef struct { @@ -197,9 +212,10 @@ tcp_tx_seg_t *tcp_alloc_tx_seg(tcp_flow_t *flow, uint32_t reserve_slots); const uint8_t *tcp_tx_seg_payload_ptr(const tcp_tx_seg_t *seg); void tcp_tx_seg_clear(tcp_flow_t *flow, tcp_tx_seg_t *seg); bool tcp_send_from_seg(tcp_flow_t *flow, tcp_tx_seg_t *seg); +bool tcp_retransmit_seg(tcp_flow_t *flow, tcp_tx_seg_t *seg); bool tcp_send_flow_segment(tcp_flow_t *flow, tcp_hdr_t *hdr, const uint8_t *opts, uint8_t opts_len, const uint8_t *payload, uint16_t payload_len); -bool tcp_send_ack_now(tcp_flow_t *flow); -bool tcp_try_send_pending_fin(tcp_flow_t *flow); +void tcp_send_ack_now(tcp_flow_t *flow); +void tcp_try_send_pending_fin(tcp_flow_t *flow); uint64_t tcp_flush_nagle(tcp_flow_t *flow, uint8_t force); static inline uint16_t tcp_checksum_ipv4(const void *segment, uint16_t seg_len, uint32_t src_ip, uint32_t dst_ip) { @@ -217,7 +233,7 @@ tcp_tx_seg_t *tcp_find_first_unacked(tcp_flow_t *flow); void tcp_cc_on_timeout(tcp_flow_t *f); void tcp_daemon_kick(void); -uint16_t tcp_calc_adv_wnd_field(tcp_flow_t *flow, uint8_t apply_scale); +void tcp_update_adv_wnd(tcp_flow_t *flow, uint8_t apply_scale); #ifdef __cplusplus } diff --git a/kernel/networking/transport_layer/tcp/tcp_rx.c b/kernel/networking/transport_layer/tcp/tcp_rx.c index d7b1e4f5..dbf736a7 100644 --- a/kernel/networking/transport_layer/tcp/tcp_rx.c +++ b/kernel/networking/transport_layer/tcp/tcp_rx.c @@ -12,10 +12,17 @@ #include "syscalls/syscalls.h" #include "../tcp.h" +static void tcp_note_dsack(tcp_flow_t *flow, uint32_t left, uint32_t right) { + if (!flow || !flow->tx.sack_ok || !flow->tx.dsack_enabled || !TCP_SEQ_GT(right, left)) return; + flow->rx.dsack_pending = 1; + flow->rx.dsack_left = left; + flow->rx.dsack_right = right; +} + static bool tcp_reass_count_ok(tcp_flow_t *flow) { - if (!flow) return 0; + if (!flow) return false; - if (flow->rx.reass_count <= TCP_REASS_MAX_SEGS) return 1; + if (flow->rx.reass_count <= TCP_REASS_MAX_SEGS) return true; if (flow->rx.reass_count || flow->rx.rcv_ooo_used) tcp_account_ooo_remove(flow->rx.rcv_ooo_used, flow->rx.reass_count); flow->rx.reass_count = 0; @@ -28,7 +35,7 @@ static bool tcp_reass_count_ok(tcp_flow_t *flow) { flow->rx.reass[i].end = 0; } - return 0; + return false; } static void tcp_reass_remove(tcp_flow_t *flow, int32_t idx) { @@ -51,7 +58,7 @@ static void tcp_reass_remove(tcp_flow_t *flow, int32_t idx) { static bool tcp_reass_drain_inseq(tcp_flow_t *flow) { bool advanced = false; - if (!tcp_reass_count_ok(flow)) return 0; + if (!tcp_reass_count_ok(flow)) return false; for(;;){ int32_t idx = -1; @@ -77,7 +84,7 @@ static bool tcp_reass_drain_inseq(tcp_flow_t *flow) { advanced = true; } - if (advanced) tcp_calc_adv_wnd_field(flow, 1); + if (advanced) tcp_update_adv_wnd(flow, 1); return advanced; } @@ -106,12 +113,34 @@ int64_t tcp_flow_read(tcp_data *flow_ctx, void *buf, uint64_t len) { if (n > first) memcpy(dst + first, rx, n - first); flow->rx.rcv_base += n; + if (flow->rx.urg_valid && TCP_SEQ_GEQ(flow->rx.rcv_base, flow->rx.urg_seq)) { + flow->rx.urg_valid = 0; + flow->rx.urg_seq = 0; + } rc = n; } } - + + if (rc > 0) { + uint32_t old_edge = flow->rx.rcv_adv_edge; + uint16_t old_adv_field = flow->base.ctx.window; + uint32_t old_nxt = flow->rx.rcv_nxt; + + bool advanced = tcp_reass_drain_inseq(flow); + if (!advanced) tcp_update_adv_wnd(flow, 1); + + if (flow->base.state != TCP_STATE_CLOSED && flow->base.state != TCP_TIME_WAIT) { + uint32_t threshold = flow->tx.mss ? flow->tx.mss : TCP_DEFAULT_MSS; + uint32_t half = flow->rx.rcv_wnd_max >> 1; + if (half && half < threshold) threshold = half; + if (!threshold) threshold = 1; + + uint32_t delta = TCP_SEQ_GT(flow->rx.rcv_adv_edge, old_edge) ? flow->rx.rcv_adv_edge - old_edge : 0; + if (advanced || flow->rx.rcv_nxt != old_nxt || (!old_adv_field && flow->base.ctx.window) || delta >= threshold) tcp_send_ack_now(flow); + } + } + tcp_flow_put(flow); - if (rc > 0) tcp_flow_on_app_read(flow_ctx, (uint32_t)rc); return rc; } @@ -164,8 +193,97 @@ tcp_tx_seg_t *tcp_find_first_unacked(tcp_flow_t *flow) { return best; } -static int tcp_sack_insert(tcp_sack_range_t *ranges, uint8_t *count, uint32_t left, uint32_t right) { //b - if (!ranges || !count || TCP_SEQ_LEQ(right, left)) return 0; +static bool tcp_segment_acceptable(const tcp_flow_t *flow, uint32_t seq, uint32_t seg_len) { + if (!flow) return false; + + uint32_t rcv_nxt = flow->rx.rcv_nxt; + uint32_t rcv_wnd = flow->rx.rcv_wnd; + if (!rcv_wnd) return !seg_len && seq == rcv_nxt; + + uint32_t wnd_end = rcv_nxt + rcv_wnd; + if (!seg_len) return TCP_SEQ_GEQ(seq, rcv_nxt) && TCP_SEQ_LT(seq, wnd_end); + + uint32_t last = seq + seg_len - 1; + return (TCP_SEQ_GEQ(seq, rcv_nxt) && TCP_SEQ_LT(seq, wnd_end)) || (TCP_SEQ_GEQ(last, rcv_nxt) && TCP_SEQ_LT(last, wnd_end)); +} + +static void tcp_send_challenge_ack(tcp_flow_t *flow) { + if (!flow) return; + + uint32_t now = (uint32_t)get_time(); + if (flow->timer.challenge_ack_valid && now - flow->timer.challenge_ack_last_ms < TCP_CHALLENGE_ACK_INTERVAL_MS) return; + tcp_send_ack_now(flow); + if (flow->timer.delayed_ack_pending) return; + + flow->timer.challenge_ack_valid = 1; + flow->timer.challenge_ack_last_ms = now; +} + +static bool tcp_prepare_rcv_buffer(tcp_flow_t *flow) { + if (!flow || !flow->rx.rcv_wnd_max) return false; + if (flow->rx.rcv_buf) return true; + + flow->rx.rcv_buf = (uintptr_t)zalloc(flow->rx.rcv_wnd_max); + if (!flow->rx.rcv_buf) return false; + + flow->rx.rcv_base = flow->rx.rcv_nxt; + flow->rx.rcv_data_nxt = flow->rx.rcv_nxt; + tcp_update_adv_wnd(flow, 1); + return true; +} + +static bool tcp_acknowledge_segments(tcp_flow_t *flow, uint32_t ack) { + if (!flow) return false; + + bool syn_retransmitted = false; + for (uint32_t i = 0; i < TCP_MAX_TX_SEGS; i++){ + tcp_tx_seg_t *seg = &flow->tx.txq[i]; + if (!seg->used || TCP_SEQ_GEQ(seg->seq, ack)) continue; + + uint32_t end_seq = seg->seq + seg->len + (seg->syn ? 1u : 0u) + (seg->fin ? 1u : 0u); + if (seg->rtt_sample && TCP_SEQ_GEQ(ack, end_seq)) { + if (seg->retransmit_cnt == 0) tcp_rtt_update(flow, seg->rtt_timer_ms); + flow->tx.rtt_sample_pending = 0; + seg->rtt_sample = 0; + } + + uint32_t seq = seg->seq; + if (seg->syn && TCP_SEQ_GT(ack, seq)) { + if (seg->retransmit_cnt) syn_retransmitted = true; + seg->syn = 0; + seq++; + } + + if (seg->len && TCP_SEQ_GT(ack, seq)) { + uint32_t n = ack - seq; + if (n > seg->len) n = (uint32_t)seg->len; + seg->payload_off += n; + seg->len -= n; + seq += n; + tcp_account_tx_remove(flow, n); + } + if (seg->fin && TCP_SEQ_GT(ack, seq)) { + seg->fin = 0; + seq++; + } + + seg->seq = seq; + if (!seg->syn && !seg->fin && !seg->len) tcp_tx_seg_clear(flow, seg); + } + + return syn_retransmitted; +} + +static void tcp_restart_retransmit_timer(tcp_flow_t *flow) { + tcp_tx_seg_t *seg = tcp_find_first_unacked(flow); + if (!seg) return; + + seg->timer_ms = 0; + seg->timeout_ms = flow->tx.rto ? flow->tx.rto : TCP_INIT_RTO; +} + +static bool tcp_sack_insert(tcp_sack_range_t *ranges, uint8_t *count, uint32_t left, uint32_t right) { + if (!ranges || !count || TCP_SEQ_LEQ(right, left)) return false; tcp_sack_range_t merged[TCP_SACK_SCOREBOARD_MAX + 1]; uint8_t old_count = *count; @@ -202,11 +320,11 @@ static int tcp_sack_insert(tcp_sack_range_t *ranges, uint8_t *count, uint32_t le uint8_t first = out > TCP_SACK_SCOREBOARD_MAX ? 1 : 0; uint8_t next_count = out - first; - if (old_count == next_count && memcmp(ranges, &merged[first], sizeof(tcp_sack_range_t)*next_count) == 0) return 0; + if (old_count == next_count && memcmp(ranges, &merged[first], sizeof(tcp_sack_range_t)*next_count) == 0) return false; memcpy(ranges, &merged[first], sizeof(tcp_sack_range_t) * next_count); *count = next_count; - return 1; + return true; } static void tcp_sack_trim_ranges(tcp_sack_range_t *ranges, uint8_t *count, uint32_t ack) { @@ -255,7 +373,7 @@ static bool tcp_sack_select_retransmit(tcp_flow_t *flow, bool force, bool rescue tcp_tx_seg_t *best_seg = NULL; uint32_t best_left = 0; uint32_t best_right = 0; - uint32_t limit = rescue ? (flow->tx.recover ? flow->tx.recover : flow->tx.snd_nxt) : (flow->tx.sack_range_count ? flow->tx.sack_ranges[flow->tx.sack_range_count-1].right : flow->tx.snd_una); //illeggibile + uint32_t limit = rescue ? (flow->tx.recover ? flow->tx.recover : flow->tx.snd_nxt) : (flow->tx.sack_range_count ? flow->tx.sack_ranges[flow->tx.sack_range_count-1].right : flow->tx.snd_una); uint32_t mss = flow->tx.mss ? flow->tx.mss : TCP_DEFAULT_MSS; for (int i = 0; i < TCP_MAX_TX_SEGS; i++) { @@ -327,9 +445,39 @@ static bool tcp_sack_select_retransmit(tcp_flow_t *flow, bool force, bool rescue return true; } +static bool tcp_sack_retransmit_range(tcp_flow_t *flow, tcp_tx_seg_t *seg, uint32_t left, uint32_t right, bool rescue) { + if (!flow || !seg || TCP_SEQ_LEQ(right, left)) return false; + + tcp_tx_seg_t retransmit = { + .seq = left, + .len = right - left, + .pkt = seg->pkt, + .payload_off = seg->payload_off + (left - seg->seq), + .psh = seg->psh && right == seg->seq + seg->len + }; + if (!tcp_send_from_seg(flow, &retransmit)) return false; + + for (uint32_t i = 0; i < TCP_MAX_TX_SEGS; i++) flow->tx.txq[i].rtt_sample = 0; + flow->tx.rtt_sample_pending = 0; + bool tracked = tcp_sack_insert(flow->tx.sack_retransmitted_ranges, &flow->tx.sack_retransmitted_count, left, right); + if (rescue) flow->tx.sack_rescue_sent = 1; + if (seg->retransmit_cnt < UINT8_MAX) seg->retransmit_cnt++; + seg->timer_ms = 0; + seg->rtt_timer_ms = 0; + seg->timeout_ms = flow->tx.rto ? flow->tx.rto : TCP_INIT_RTO; + return tracked; +} + static void tcp_sack_recovery_send(tcp_flow_t *flow, bool force_first) { if (!flow || !flow->tx.sack_ok) return; + if (force_first) { + tcp_tx_seg_t *seg = NULL; + uint32_t left = 0; + uint32_t right = 0; + if (!tcp_sack_select_retransmit(flow, true, false, &seg, &left, &right) || !tcp_sack_retransmit_range(flow, seg, left, right, false)) return; + } + uint32_t pipe = 0; for (int i = 0; i < TCP_MAX_TX_SEGS; i++) { tcp_tx_seg_t *seg = &flow->tx.txq[i]; @@ -361,9 +509,9 @@ static void tcp_sack_recovery_send(tcp_flow_t *flow, bool force_first) { tcp_sack_range_t *range = &flow->tx.sack_retransmitted_ranges[n]; if (TCP_SEQ_GEQ(cursor, range->right) || TCP_SEQ_GEQ(range->left, hole_right)) continue; - uint32_t start = TCP_SEQ_GT(cursor, range->left) ? cursor : range->left; - uint32_t end = TCP_SEQ_LT(hole_right, range->right) ? hole_right : range->right; - if (TCP_SEQ_GT(end, start)) retransmitted += end - start; + uint32_t range_left = TCP_SEQ_GT(cursor, range->left) ? cursor : range->left; + uint32_t range_right = TCP_SEQ_LT(hole_right, range->right) ? hole_right : range->right; + if (TCP_SEQ_GT(range_right, range_left)) retransmitted += range_right - range_left; } uint32_t hole_len = hole_right - cursor; @@ -385,33 +533,21 @@ static void tcp_sack_recovery_send(tcp_flow_t *flow, bool force_first) { uint32_t left = 0; uint32_t right = 0; bool rescue = false; - if (!tcp_sack_select_retransmit(flow, force_first, false, &seg, &left, &right)) { + if (!tcp_sack_select_retransmit(flow, false, false, &seg, &left, &right)) { if (flow->tx.sack_rescue_sent || !tcp_sack_select_retransmit(flow, false, true, &seg, &left, &right)) break; rescue = true; } uint32_t len = right - left; if (!len || len > cwnd - pipe) break; - tcp_tx_seg_t retransmit = { - .seq = left, - .len = len, - .pkt = seg->pkt, - .payload_off = seg->payload_off + (left - seg->seq) - }; - if (!tcp_send_from_seg(flow, &retransmit)) break; - - tcp_sack_insert(flow->tx.sack_retransmitted_ranges, &flow->tx.sack_retransmitted_count, left, right); - if (rescue) flow->tx.sack_rescue_sent = 1; - seg->retransmit_cnt++; - seg->timer_ms = 0; + if (!tcp_sack_retransmit_range(flow, seg, left, right, rescue)) break; pipe += len; - force_first = false; } } -static int tcp_apply_sack_blocks(tcp_flow_t *flow, const tcp_parsed_opts_t *opts) { - if (!flow || !opts || !flow->tx.sack_ok || !opts->sack_count) return 0; +static bool tcp_apply_sack_blocks(tcp_flow_t *flow, const tcp_parsed_opts_t *opts) { + if (!flow || !opts || !flow->tx.sack_ok || !opts->sack_count) return false; - int changed = 0; + bool changed = false; for (uint32_t b = 0; b < opts->sack_count; b++) { uint32_t left = opts->sacks[b].left; uint32_t right = opts->sacks[b].right; @@ -440,15 +576,17 @@ void tcp_cc_on_timeout(tcp_flow_t *f){ f->tx.cwnd_acc = 0; f->tx.dup_acks = 0; f->tx.in_fast_recovery = 0; - f->tx.recover = 0; + f->tx.recover = f->tx.snd_nxt; + f->tx.recover_valid = 1; f->tx.sack_range_count = 0; f->tx.sack_retransmitted_count = 0; f->tx.sack_rescue_sent = 0; } -static void tcp_cc_on_new_ack(tcp_flow_t *f, uint32_t ack) { +static void tcp_cc_on_new_ack(tcp_flow_t *f, uint32_t ack, uint32_t prev_una) { uint32_t mss = f->tx.mss ? f->tx.mss : TCP_DEFAULT_MSS; + uint32_t acked = ack - prev_una; if (f->tx.in_fast_recovery){ if (TCP_SEQ_GEQ(ack, f->tx.recover)){ @@ -456,6 +594,7 @@ static void tcp_cc_on_new_ack(tcp_flow_t *f, uint32_t ack) { if (f->tx.cwnd < mss) f->tx.cwnd = mss; f->tx.in_fast_recovery = 0; + f->tx.recover_valid = 0; f->tx.dup_acks = 0; f->tx.cwnd_acc = 0; f->tx.sack_range_count = 0; @@ -464,42 +603,57 @@ static void tcp_cc_on_new_ack(tcp_flow_t *f, uint32_t ack) { return; } - f->tx.cwnd = f->tx.ssthresh; - if (f->tx.cwnd < mss) f->tx.cwnd = mss; + if (f->tx.sack_ok) { + f->tx.cwnd = f->tx.ssthresh; + if (f->tx.cwnd < mss) f->tx.cwnd = mss; + return; + } + + uint32_t cwnd = acked < f->tx.cwnd ? f->tx.cwnd - acked : 0; + if (acked >= mss) { + uint32_t room = UINT32_MAX - cwnd; + cwnd += room < mss ? room : mss; + } + if (cwnd < mss) cwnd = mss; + f->tx.cwnd = cwnd; + + tcp_tx_seg_t *seg = tcp_find_first_unacked(f); + if (seg && !tcp_retransmit_seg(f, seg)) tcp_restart_retransmit_timer(f); return; } + if (f->tx.recover_valid && TCP_SEQ_GEQ(ack, f->tx.recover)) f->tx.recover_valid = 0; + if (f->tx.cwnd < f->tx.ssthresh){ - f->tx.cwnd += mss; + uint32_t inc = acked < mss ? acked : mss; + f->tx.cwnd += inc; if (f->tx.cwnd < mss) f->tx.cwnd = mss; return; } - uint32_t denom = f->tx.cwnd ? f->tx.cwnd : 1u; - uint32_t inc = (mss * mss) / denom; - - if (inc == 0) inc = 1; - - f->tx.cwnd += inc; + uint64_t acc = (uint64_t) f->tx.cwnd_acc + acked; + if (acc >= f->tx.cwnd) { + acc -= f->tx.cwnd; + f->tx.cwnd += mss; + } + f->tx.cwnd_acc = acc > UINT32_MAX ? UINT32_MAX : (uint32_t)acc; } static void tcp_cc_on_dupack(tcp_flow_t *f) { uint32_t mss = f->tx.mss ? f->tx.mss : TCP_DEFAULT_MSS; if (f->tx.in_fast_recovery){ - f->tx.cwnd += mss; - if (f->tx.sack_ok) tcp_sack_recovery_send(f, false); - else { - tcp_tx_seg_t *seg = tcp_find_first_unacked(f); - if (seg && tcp_send_from_seg(f, seg)) { - seg->retransmit_cnt++; - seg->timer_ms = 0; - } + if (f->tx.sack_ok) { + tcp_sack_recovery_send(f, false); + return; } + uint32_t room = UINT32_MAX - f->tx.cwnd; + f->tx.cwnd += room < mss ? room : mss; return; } if (f->tx.dup_acks != 3) return; + if (f->tx.recover_valid && TCP_SEQ_LEQ(f->tx.snd_una, f->tx.recover)) return; uint32_t flight = f->tx.snd_nxt - f->tx.snd_una; uint32_t half = flight / 2; @@ -509,7 +663,12 @@ static void tcp_cc_on_dupack(tcp_flow_t *f) { f->tx.ssthresh = half; f->tx.recover = f->tx.snd_nxt; - f->tx.cwnd = f->tx.ssthresh + 3u * mss; + f->tx.recover_valid = 1; + if (f->tx.sack_ok) f->tx.cwnd = f->tx.ssthresh; + else { + uint64_t recovery_cwnd = (uint64_t)f->tx.ssthresh + 3u * mss; + f->tx.cwnd = recovery_cwnd > UINT32_MAX ? UINT32_MAX : (uint32_t)recovery_cwnd; + } f->tx.in_fast_recovery = 1; f->tx.sack_retransmitted_count = 0; f->tx.sack_rescue_sent = 0; @@ -517,15 +676,16 @@ static void tcp_cc_on_dupack(tcp_flow_t *f) { if (f->tx.sack_ok) tcp_sack_recovery_send(f, true); else { tcp_tx_seg_t *seg = tcp_find_first_unacked(f); - if (seg && tcp_send_from_seg(f, seg)) { - seg->retransmit_cnt++; - seg->timer_ms = 0; - } + if (seg && !tcp_retransmit_seg(f, seg)) tcp_restart_retransmit_timer(f); } } void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_addr, uint8_t l3_id, netpkt_t* pkt) { if (!pkt) return; + if (!src_ip_addr || !dst_ip_addr || (ipver != IP_VER4 && ipver != IP_VER6)) { + netpkt_unref(pkt); + return; + } const uint8_t* segment = (const uint8_t*)netpkt_data(pkt); uint32_t len = netpkt_len(pkt); if (len < sizeof(tcp_hdr_t)) { @@ -561,6 +721,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a uint32_t ack = bswap32(hdr.ack); uint8_t flags = hdr.flags; uint16_t window = bswap16(hdr.window); + uint16_t urgent_ptr = bswap16(hdr.urgent_ptr); uint8_t hdr_len = (uint8_t)((hdr.data_offset_reserved >> 4) * 4); if (hdr_len < sizeof(tcp_hdr_t) || len < hdr_len) { @@ -589,6 +750,17 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a netpkt_unref(pkt); return; } + uint32_t src_ip = 0; + uint32_t dst_ip = 0; + memcpy(&src_ip, src_ip_addr, sizeof(src_ip)); + memcpy(&dst_ip, dst_ip_addr, sizeof(dst_ip)); + bool dst_invalid = ipv4_is_unspecified(dst_ip) || ipv4_is_multicast(dst_ip) || ipv4_is_limited_broadcast(dst_ip) || ipv4_is_directed_broadcast(v4->ip, v4->mask, dst_ip); + bool src_invalid = ipv4_is_unspecified(src_ip) || ipv4_is_multicast(src_ip) || ipv4_is_limited_broadcast(src_ip) || ipv4_is_directed_broadcast(v4->ip, v4->mask, src_ip); + if (src_invalid || dst_invalid) { + if (flow) tcp_flow_put(flow); + netpkt_unref(pkt); + return; + } ifx = v4->l2->ifindex; } else { l3_ipv6_interface_t *v6 = l3_ipv6_find_by_id(l3_id); @@ -597,11 +769,15 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a netpkt_unref(pkt); return; } + if (ipv6_is_unspecified((const uint8_t*)dst_ip_addr) || ipv6_is_multicast((const uint8_t*)dst_ip_addr) || ipv6_is_unspecified((const uint8_t*)src_ip_addr) || ipv6_is_multicast((const uint8_t*)src_ip_addr)) { + if (flow) tcp_flow_put(flow); + netpkt_unref(pkt); + return; + } ifx = v6->l2->ifindex; } if (flow) { - flow->timer.keepalive_idle_ms = 0; if (flow->base.l3_id != l3_id) { flow->base.l3_id = l3_id; flow->tx.path_mss = tcp_calc_mss_for_l3(l3_id, ipver, src_ip_addr); @@ -609,26 +785,65 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a } } - if (flow && flow->base.state == TCP_TIME_WAIT && (flags & (1 << SYN_F)) && !(flags& ((1 << ACK_F) | (1 << RST_F) | (1 << FIN_F))) && data_len == 0) { - ksocket_t* listener = socket_bind_lookup(PROTO_TCP, ipver, l3_id, ifx, src_ip_addr, src_port, dst_ip_addr, dst_port); - if (listener) { - socket_core_put(listener); - tcp_free_flow(flow); - tcp_flow_put(flow); - flow = NULL; - } - } + if (flow) { + switch (flow->base.state) { + case TCP_TIME_WAIT: + if ((flags & (1 << SYN_F)) && !(flags& ((1 << ACK_F) | (1 << RST_F) | (1 << FIN_F))) && data_len == 0) { + ksocket_t* listener = socket_bind_lookup(PROTO_TCP, ipver, l3_id, ifx, src_ip_addr, src_port, dst_ip_addr, dst_port); + if (listener) { + socket_core_put(listener); + tcp_free_flow(flow); + tcp_flow_put(flow); + flow = NULL; + } + } - if (flow && flow->base.state == TCP_SYN_RECEIVED && (flags & (1 << SYN_F)) && !(flags& ((1 << ACK_F) | (1 << RST_F))) && data_len == 0 && seq+1 == flow->rx.rcv_nxt) { - for (uint32_t i = 0; i < TCP_MAX_TX_SEGS; i++) { - tcp_tx_seg_t *seg = &flow->tx.txq[i]; - if (!seg->used || !seg->syn) continue; - if (tcp_send_from_seg(flow, seg)) seg->timer_ms = 0; - break; + break; + + case TCP_SYN_RECEIVED: + if ((flags & (1 << SYN_F)) && !(flags& ((1 << RST_F) | (1 << FIN_F))) && data_len == 0 && seq+1 == flow->rx.rcv_nxt && (!(flags & (1 << ACK_F)) || (flow->base.active_open && TCP_SEQ_GT(ack, flow->tx.snd_una) && TCP_SEQ_LEQ(ack, flow->tx.snd_nxt)))) { + if ((flags & (1 << ACK_F)) && flow->base.active_open && TCP_SEQ_GT(ack, flow->tx.snd_una) && TCP_SEQ_LEQ(ack, flow->tx.snd_nxt)) { + if (!tcp_prepare_rcv_buffer(flow)) { + tcp_send_reset(l3_id, ipver, dst_ip_addr, src_ip_addr, dst_port, src_port, ack, 0, false); + tcp_free_flow(flow); + tcp_flow_put(flow); + netpkt_unref(pkt); + return; + } + + bool syn_retransmitted = tcp_acknowledge_segments(flow, ack); + if (syn_retransmitted && !flow->tx.rtt_valid && flow->tx.rto < TCP_SYN_DATA_RTO) flow->tx.rto = TCP_SYN_DATA_RTO; + flow->tx.snd_una = ack; + flow->tx.snd_wnd = window; + flow->tx.snd_wl1 = seq; + flow->tx.snd_wl2 = ack; + flow->base.ctx.ack_received = ack; + flow->base.ctx.sequence = flow->tx.snd_nxt; + flow->base.ctx.flags = 0; + flow->base.state = TCP_ESTABLISHED; + flow->base.active_open = 0; + flow->timer.keepalive_idle_ms = 0; + flow->timer.delayed_ack_pending = 0; + flow->timer.delayed_ack_timer_ms = 0; + tcp_restart_retransmit_timer(flow); + tcp_send_ack_now(flow); + } else if (!(flags & (1u << ACK_F))) { + for (uint32_t i = 0; i < TCP_MAX_TX_SEGS; i++) { + tcp_tx_seg_t *seg = &flow->tx.txq[i]; + if (!seg->used || !seg->syn) continue; + if (!tcp_retransmit_seg(flow, seg)) tcp_restart_retransmit_timer(flow); + break; + } + } + tcp_flow_put(flow); + netpkt_unref(pkt); + return; + } + break; + + default: + break; } - tcp_flow_put(flow); - netpkt_unref(pkt); - return; } if (!flow){ @@ -671,7 +886,6 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a flow->base.local.port = dst_port; flow->base.state = TCP_SYN_RECEIVED; - flow->base.retries = TCP_SYN_RETRIES; tcp_parsed_opts_t pop = parsed_opts; @@ -688,7 +902,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a } flow->tx.path_mss = tcp_calc_mss_for_l3(l3_id, ipver, src_ip_addr); - flow->tx.peer_mss = pop.has_mss && pop.mss ? pop.mss : 0; + flow->tx.peer_mss = pop.has_mss && pop.mss ? pop.mss : (ipver == IP_VER6 ? TCP_DEFAULT_PEER_MSS_IPV6 : TCP_DEFAULT_PEER_MSS_IPV4); flow->base.ctx.flags = 0; flow->base.ctx.options.ptr = 0; flow->base.ctx.options.size = 0; @@ -706,9 +920,9 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a flow->base.ctx.expected_ack = iss + 1; flow->base.ctx.ack_received = 0; - uint32_t new_wnd = window; - if (flow->tx.ws_ok && flow->tx.ws_recv) new_wnd <<= flow->tx.ws_recv; - flow->tx.snd_wnd = new_wnd; + flow->tx.snd_wnd = window; + flow->tx.snd_wl1 = seq; + flow->tx.snd_wl2 = 0; flow->timer.persist_active = 0; flow->timer.persist_timer_ms = 0; @@ -733,14 +947,14 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a flow->rx.sack_recent_right = 0; flow->rx.rcv_buf = 0; flow->rx.rcv_adv_edge = flow->rx.rcv_nxt + flow->rx.rcv_wnd_max; - tcp_calc_adv_wnd_field(flow, flow->tx.ws_ok ? 1 : 0); + tcp_update_adv_wnd(flow, flow->tx.ws_ok ? 1 : 0); - flow->tx.cwnd = flow->tx.mss * TCP_INIT_CWND_SEGS; - if (flow->tx.cwnd < flow->tx.mss) flow->tx.cwnd = flow->tx.mss; + flow->tx.cwnd = tcp_initial_cwnd(flow->tx.mss); flow->tx.ssthresh = TCP_RECV_WINDOW; flow->tx.dup_acks = 0; flow->tx.in_fast_recovery = 0; flow->tx.recover = 0; + flow->tx.recover_valid = 0; flow->tx.cwnd_acc = 0; flow->timer.time_wait_ms = 0; @@ -758,7 +972,10 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a seg->syn = 1; seg->fin = 0; + seg->psh = 0; + seg->persist = 0; seg->rtt_sample = 1; + flow->tx.rtt_sample_pending = 1; seg->retransmit_cnt = 0; seg->seq = iss; seg->len = 0; @@ -798,133 +1015,69 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a return; } - if (flow->base.state == TCP_TIME_WAIT){ - if (flags & (1u << RST_F)) { - tcp_flow_put(flow); - netpkt_unref(pkt); - return; - } - - uint32_t seg_len = data_len; - - if (flags & (1u << SYN_F)) seg_len++; - if (flags & (1u << FIN_F)) seg_len++; - - uint32_t seg_end = seq + seg_len; - - if (TCP_SEQ_LEQ(seq, flow->rx.rcv_nxt) && TCP_SEQ_GEQ(seg_end, flow->rx.rcv_nxt)){ - flow->timer.time_wait_ms = 0; - tcp_send_ack_now(flow); - } + uint8_t fin = (flags & (1 << FIN_F)) ? 1 : 0; + uint32_t seg_seq = seq; + bool handshake_ack_processed = false; - tcp_flow_put(flow); - netpkt_unref(pkt); - return; - } - uint32_t old_wnd = flow->tx.snd_wnd; - uint32_t new_wnd = window; - if (flow->tx.ws_ok && flow->tx.ws_recv) new_wnd <<= flow->tx.ws_recv; - flow->tx.snd_wnd = new_wnd; - - if (flow->tx.snd_wnd > 0){ - flow->timer.persist_active = 0; - flow->timer.persist_timer_ms = 0; - flow->timer.persist_timeout_ms = 0; - flow->timer.persist_probe_cnt = 0; - } + switch (flow->base.state) { + case TCP_TIME_WAIT: + if (flags & (1u << RST_F)) { + tcp_flow_put(flow); + netpkt_unref(pkt); + return; + } - uint8_t fin = (flags & (1u << FIN_F)) ? 1u : 0u; + uint32_t time_wait_seg_len = data_len; - if (flags & (1u << ACK_F)){ - if (TCP_SEQ_GT(ack, flow->tx.snd_una) && TCP_SEQ_LEQ(ack, flow->tx.snd_nxt)){ - uint32_t prev_una = flow->tx.snd_una; + if (flags & (1u << SYN_F)) time_wait_seg_len++; + if (flags & (1u << FIN_F)) time_wait_seg_len++; - flow->tx.snd_una = ack; - flow->base.ctx.ack_received = ack; - flow->tx.dup_acks = 0; + uint32_t seg_end = seq + time_wait_seg_len; - for (uint32_t i = 0; i < TCP_MAX_TX_SEGS; i++){ - tcp_tx_seg_t *seg = &flow->tx.txq[i]; - if (!seg->used || TCP_SEQ_GEQ(seg->seq, ack)) continue; + if (TCP_SEQ_LEQ(seq, flow->rx.rcv_nxt) && TCP_SEQ_GEQ(seg_end, flow->rx.rcv_nxt)){ + flow->timer.time_wait_ms = 0; + tcp_send_ack_now(flow); + } - if (seg->rtt_sample && seg->retransmit_cnt == 0) { - tcp_rtt_update(flow, seg->timer_ms); - seg->rtt_sample = 0; - } + tcp_flow_put(flow); + netpkt_unref(pkt); + return; - uint32_t seq = seg->seq; - if (seg->syn && TCP_SEQ_GT(ack, seq)) { - seg->syn = 0; - seq++; - } + case TCP_SYN_SENT: + bool ack_set = (flags & (1u << ACK_F)) != 0; + bool ack_acceptable = false; - if (seg->len && TCP_SEQ_GT(ack, seq)) { - uint32_t n = ack - seq; - if (n > seg->len) n = (uint32_t)seg->len; - seg->payload_off += n; - seg->len -= n; - seq += n; - tcp_account_tx_remove(flow, n); - } - if (seg->fin && TCP_SEQ_GT(ack, seq)) { - seg->fin = 0; - seq++; + if (ack_set) { + if (TCP_SEQ_LEQ(ack, flow->tx.snd_una) || TCP_SEQ_GT(ack, flow->tx.snd_nxt)) { + if (!(flags & (1u << RST_F))) tcp_send_reset(l3_id, ipver, dst_ip_addr, src_ip_addr, dst_port, src_port, ack, 0, false); + tcp_flow_put(flow); + netpkt_unref(pkt); + return; } - - seg->seq = seq; - if (!seg->syn && !seg->fin && !seg->len) tcp_tx_seg_clear(flow, seg); + ack_acceptable = true; } - tcp_sack_trim_ranges(flow->tx.sack_ranges, &flow->tx.sack_range_count, ack); - tcp_sack_trim_ranges(flow->tx.sack_retransmitted_ranges, &flow->tx.sack_retransmitted_count, ack); - if (flow->tx.sack_ok && parsed_opts.sack_count) tcp_apply_sack_blocks(flow, &parsed_opts); - if (TCP_SEQ_GT(ack, prev_una)) tcp_cc_on_new_ack(flow, ack); - if (flow->tx.in_fast_recovery && flow->tx.sack_ok) tcp_sack_recovery_send(flow, false); - - if (flow->base.state == TCP_FIN_WAIT_1 && TCP_SEQ_GEQ(ack, flow->base.ctx.expected_ack)){ - flow->base.state = TCP_FIN_WAIT_2; - flow->timer.fin_wait2_ms = 0; - tcp_daemon_kick(); - } else if (flow->base.state == TCP_CLOSING && TCP_SEQ_GEQ(ack, flow->base.ctx.expected_ack)){ - tcp_enter_time_wait(flow); + if (flags & (1u << RST_F)) { + if (ack_acceptable) tcp_free_flow(flow); tcp_flow_put(flow); netpkt_unref(pkt); return; - } else if (flow->base.state == TCP_LAST_ACK && TCP_SEQ_GEQ(ack, flow->base.ctx.expected_ack)){ - tcp_free_flow(flow); + } + + if (!(flags & (1u << SYN_F))) { tcp_flow_put(flow); netpkt_unref(pkt); return; } - } else if (ack == flow->tx.snd_una && data_len == 0 && !fin && !(flags & (1u << SYN_F))){ - bool sack_changed = flow->tx.sack_ok && parsed_opts.sack_count && tcp_apply_sack_blocks(flow, &parsed_opts); - if (TCP_SEQ_LT(flow->tx.snd_una, flow->tx.snd_nxt) && (new_wnd == old_wnd || sack_changed)) { - if (flow->tx.dup_acks < UINT8_MAX) flow->tx.dup_acks++; - tcp_cc_on_dupack(flow); - } - } else { - flow->tx.dup_acks = 0; - } - } - if (flow->tx.nagle_len) tcp_flush_nagle(flow, flow->tx.fin_tx_pending ? 1 : 0); - if (flow->tx.fin_tx_pending) { - tcp_try_send_pending_fin(flow); - if (flow->tx.fin_tx_pending) tcp_daemon_kick(); - } - - uint32_t seg_seq = seq; - - switch (flow->base.state){ - case TCP_SYN_SENT: - if ((flags & (1u << SYN_F)) && (flags & (1u << ACK_F)) && ack == flow->base.ctx.expected_ack){ + flow->timer.keepalive_idle_ms = 0; flow->base.ctx.ack = seq + 1; flow->rx.rcv_nxt = seq + 1; - flow->base.ctx.ack_received = ack; - flow->tx.snd_una = ack; - flow->tx.snd_nxt = flow->base.ctx.sequence; - flow->base.ctx.sequence = flow->tx.snd_nxt; - flow->base.ctx.flags = 0; + flow->rx.rcv_base = flow->rx.rcv_nxt; + flow->rx.rcv_data_nxt = flow->rx.rcv_nxt; + flow->rx.rcv_ooo_used = 0; + flow->rx.sack_recent_left = 0; + flow->rx.sack_recent_right = 0; tcp_parsed_opts_t pop = parsed_opts; @@ -939,122 +1092,293 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a flow->tx.sack_ok = flow->tx.sack_enabled && pop.sack_permitted; flow->tx.path_mss = tcp_calc_mss_for_l3(l3_id, ipver, src_ip_addr); - flow->tx.peer_mss = pop.has_mss && pop.mss ? pop.mss : 0; + flow->tx.peer_mss = pop.has_mss && pop.mss ? pop.mss : (ipver == IP_VER6 ? TCP_DEFAULT_PEER_MSS_IPV6 : TCP_DEFAULT_PEER_MSS_IPV4); tcp_update_mss(flow); - flow->tx.cwnd = flow->tx.mss * TCP_INIT_CWND_SEGS; - if (flow->tx.cwnd < flow->tx.mss) flow->tx.cwnd = flow->tx.mss; + flow->tx.cwnd = tcp_initial_cwnd(flow->tx.mss); + flow->tx.snd_wnd = window; + flow->tx.snd_wl1 = seq; + flow->tx.snd_wl2 = ack_set ? ack : 0; + tcp_update_adv_wnd(flow, 1); + + if (!ack_acceptable) { + flow->base.state = TCP_SYN_RECEIVED; + for (uint32_t i = 0; i < TCP_MAX_TX_SEGS; i++) { + tcp_tx_seg_t *seg = &flow->tx.txq[i]; + if (!seg->used || !seg->syn) continue; + if (!tcp_retransmit_seg(flow, seg)) tcp_restart_retransmit_timer(flow); + break; + } + tcp_flow_put(flow); + netpkt_unref(pkt); + return; + } - uint32_t new_wnd = window; - if (flow->tx.ws_ok && flow->tx.ws_recv) new_wnd <<= flow->tx.ws_recv; - flow->tx.snd_wnd = new_wnd; + if (!tcp_prepare_rcv_buffer(flow)) { + tcp_send_reset(l3_id, ipver, dst_ip_addr, src_ip_addr, dst_port, src_port, ack, 0, false); + tcp_free_flow(flow); + tcp_flow_put(flow); + netpkt_unref(pkt); + return; + } + bool syn_retransmitted = tcp_acknowledge_segments(flow, ack); + if (syn_retransmitted && !flow->tx.rtt_valid && flow->tx.rto < TCP_SYN_DATA_RTO) flow->tx.rto = TCP_SYN_DATA_RTO; + flow->tx.snd_una = ack; + flow->base.ctx.ack_received = ack; + flow->base.ctx.sequence = flow->tx.snd_nxt; + flow->base.ctx.flags = 0; + flow->base.state = TCP_ESTABLISHED; + flow->base.active_open = 0; + flow->timer.delayed_ack_pending = 0; + flow->timer.delayed_ack_timer_ms = 0; + tcp_restart_retransmit_timer(flow); + tcp_send_ack_now(flow); - flow->rx.rcv_base = flow->rx.rcv_nxt; - flow->rx.rcv_data_nxt = flow->rx.rcv_nxt; - flow->rx.rcv_ooo_used = 0; - flow->rx.sack_recent_left = 0; - flow->rx.sack_recent_right = 0; - if (!flow->rx.rcv_buf || !flow->rx.rcv_wnd_max) { - flow->rx.rcv_wnd = 0; - flow->rx.rcv_adv_edge = flow->rx.rcv_nxt; - flow->base.ctx.window = 0; + if (!data_len && !fin) { tcp_flow_put(flow); netpkt_unref(pkt); return; } - (void)tcp_calc_adv_wnd_field(flow, 1); - (void)tcp_send_ack_now(flow); - flow->base.state = TCP_ESTABLISHED; - } else if (flags & (1u << RST_F)){ - flow->base.state = TCP_STATE_CLOSED; - } + seg_seq = seq + 1; + if (!tcp_segment_acceptable(flow, seg_seq, data_len + fin)) { + tcp_send_ack_now(flow); + tcp_flow_put(flow); + netpkt_unref(pkt); + return; + } - tcp_flow_put(flow); - netpkt_unref(pkt); - return; + handshake_ack_processed = true; + break; - case TCP_SYN_RECEIVED: - if ((flags & (1u << ACK_F)) && !(flags & (1u << SYN_F)) && !(flags & (1u << RST_F)) && ack == flow->base.ctx.expected_ack){ - if (!flow->rx.rcv_buf && flow->rx.rcv_wnd_max) { - flow->rx.rcv_buf = (uintptr_t)zalloc(flow->rx.rcv_wnd_max); - if (!flow->rx.rcv_buf) { - tcp_send_reset(l3_id, ipver, dst_ip_addr, src_ip_addr, dst_port, src_port, 0, flow->base.ctx.ack, true); - tcp_free_flow(flow); - tcp_flow_put(flow); - netpkt_unref(pkt); - return; - } - flow->rx.rcv_base = flow->rx.rcv_nxt; - flow->rx.rcv_data_nxt = flow->rx.rcv_nxt; - tcp_calc_adv_wnd_field(flow, 1); + + case TCP_SYN_RECEIVED: + uint32_t syn_seg_len = data_len + ((flags & (1u << SYN_F)) ? 1 : 0) + fin; + bool seq_acceptable = tcp_segment_acceptable(flow, seq, syn_seg_len); + + if (flags & (1u << RST_F)) { + if (seq == flow->rx.rcv_nxt) tcp_free_flow(flow); + else if (tcp_segment_acceptable(flow, seq, 0)) tcp_send_challenge_ack(flow); + tcp_flow_put(flow); + netpkt_unref(pkt); + return; } - uint32_t queued = 0; - if (flow->base.listener) queued = tcp_accept_enqueue(flow->base.listener, ipver, src_ip_addr, dst_ip_addr, src_port, dst_port); - if (!queued) { - tcp_stats.acceptq_drop_full++; - tcp_hdr_t rst_hdr; - rst_hdr.src_port = bswap16(flow->base.local.port); - rst_hdr.dst_port = bswap16(flow->base.remote.port); - rst_hdr.sequence = bswap32(flow->tx.snd_nxt); - rst_hdr.ack = bswap32(flow->base.ctx.ack); - rst_hdr.flags = (uint8_t)((1 << RST_F) | (1 << ACK_F)); - rst_hdr.window = 0; - rst_hdr.urgent_ptr = 0; - - (void)tcp_send_flow_segment(flow, &rst_hdr, NULL, 0, NULL, 0); + if (flags & (1u << SYN_F)) { + tcp_send_challenge_ack(flow); + tcp_flow_put(flow); + netpkt_unref(pkt); + return; + } + + if (!seq_acceptable) { + tcp_send_ack_now(flow); + tcp_flow_put(flow); + netpkt_unref(pkt); + return; + } + + if (!(flags & (1u << ACK_F))) { + tcp_flow_put(flow); + netpkt_unref(pkt); + return; + } + if (TCP_SEQ_LEQ(ack, flow->tx.snd_una) || TCP_SEQ_GT(ack, flow->tx.snd_nxt)) { + tcp_send_reset(l3_id, ipver, dst_ip_addr, src_ip_addr, dst_port, src_port, ack, 0, false); + tcp_flow_put(flow); + netpkt_unref(pkt); + return; + } + + flow->timer.keepalive_idle_ms = 0; + + if (!tcp_prepare_rcv_buffer(flow)) { + tcp_send_reset(l3_id, ipver, dst_ip_addr, src_ip_addr, dst_port, src_port, 0, flow->base.ctx.ack, true); tcp_free_flow(flow); tcp_flow_put(flow); netpkt_unref(pkt); return; } - if (flow->base.listener) { - socket_core_put(flow->base.listener); - flow->base.listener = NULL; + if (!flow->base.active_open) { + uint32_t queued = 0; + if (flow->base.listener) queued = tcp_accept_enqueue(flow->base.listener, ipver, src_ip_addr, dst_ip_addr, src_port, dst_port); + if (!queued) { + tcp_stats.acceptq_drop_full++; + tcp_hdr_t rst_hdr; + rst_hdr.src_port = bswap16(flow->base.local.port); + rst_hdr.dst_port = bswap16(flow->base.remote.port); + rst_hdr.sequence = bswap32(flow->tx.snd_nxt); + rst_hdr.ack = bswap32(flow->base.ctx.ack); + rst_hdr.flags = (uint8_t)((1 << RST_F) | (1 << ACK_F)); + rst_hdr.window = 0; + rst_hdr.urgent_ptr = 0; + + (void)tcp_send_flow_segment(flow, &rst_hdr, NULL, 0, NULL, 0); + + tcp_free_flow(flow); + tcp_flow_put(flow); + netpkt_unref(pkt); + return; + } + + if (flow->base.listener) { + socket_core_put(flow->base.listener); + flow->base.listener = NULL; + } } - flow->base.ctx.sequence = flow->tx.snd_nxt; + + bool syn_retransmitted_recv = tcp_acknowledge_segments(flow, ack); + if (syn_retransmitted_recv && !flow->tx.rtt_valid && flow->tx.rto < TCP_SYN_DATA_RTO) flow->tx.rto = TCP_SYN_DATA_RTO; flow->tx.snd_una = ack; + flow->base.ctx.sequence = flow->tx.snd_nxt; + flow->base.ctx.ack_received = ack; + flow->base.ctx.flags = 0; flow->base.state = TCP_ESTABLISHED; + flow->base.active_open = 0; flow->timer.delayed_ack_pending = 0; flow->timer.delayed_ack_timer_ms = 0; - flow->base.ctx.ack_received = ack; + + uint32_t new_wnd = window; + if (flow->tx.ws_ok && flow->tx.ws_recv) new_wnd = new_wnd << flow->tx.ws_recv; + flow->tx.snd_wnd = new_wnd; + flow->tx.snd_wl1 = seq; + flow->tx.snd_wl2 = ack; + tcp_restart_retransmit_timer(flow); if (!data_len && !fin) { tcp_flow_put(flow); netpkt_unref(pkt); return; } + + handshake_ack_processed = true; break; - } else if (flags & (1u << RST_F)){ - tcp_free_flow(flow); + + + default: + break; + } + + if (!handshake_ack_processed) { + uint32_t seg_len = data_len + ((flags & (1u << SYN_F)) ? 1u : 0u) + fin; + + if (flags & (1u << RST_F)){ + if (seq == flow->rx.rcv_nxt) tcp_free_flow(flow); + else if (tcp_segment_acceptable(flow, seq, 0)) tcp_send_challenge_ack(flow); tcp_flow_put(flow); netpkt_unref(pkt); return; } - tcp_flow_put(flow); - netpkt_unref(pkt); - return; + if (flags & (1u << SYN_F)) { + tcp_send_challenge_ack(flow); + tcp_flow_put(flow); + netpkt_unref(pkt); + return; + } - default: - break; - } + if (!tcp_segment_acceptable(flow, seq, seg_len)) { + if (data_len && TCP_SEQ_LT(seq, flow->rx.rcv_nxt)) { + uint32_t duplicate_right = seq + data_len; + if (TCP_SEQ_GT(duplicate_right, flow->rx.rcv_nxt)) duplicate_right = flow->rx.rcv_nxt; + tcp_note_dsack(flow, seq, duplicate_right); + } + tcp_send_ack_now(flow); + tcp_flow_put(flow); + netpkt_unref(pkt); + return; + } - if (flags & (1u << RST_F)) { - bool rst_acceptable = flow->rx.rcv_wnd ? (TCP_SEQ_GEQ(seq, flow->rx.rcv_nxt) && TCP_SEQ_LT(seq, flow->rx.rcv_nxt + flow->rx.rcv_wnd)) : (seq == flow->rx.rcv_nxt); - if (!rst_acceptable) { + if (!(flags & (1u << ACK_F))) { + tcp_flow_put(flow); + netpkt_unref(pkt); + return; + } + + if (TCP_SEQ_GT(ack, flow->tx.snd_nxt)) { tcp_send_ack_now(flow); tcp_flow_put(flow); netpkt_unref(pkt); return; } - tcp_free_flow(flow); - tcp_flow_put(flow); - netpkt_unref(pkt); - return; + flow->timer.keepalive_idle_ms = 0; + uint32_t old_wnd = flow->tx.snd_wnd; + uint32_t new_wnd = window; + if (flow->tx.ws_ok && flow->tx.ws_recv) new_wnd <<= flow->tx.ws_recv; + if (TCP_SEQ_GEQ(ack, flow->tx.snd_una) && (TCP_SEQ_LT(flow->tx.snd_wl1, seq) || (flow->tx.snd_wl1 == seq && TCP_SEQ_LEQ(flow->tx.snd_wl2, ack)))) { + flow->tx.snd_wnd = new_wnd; + flow->tx.snd_wl1 = seq; + flow->tx.snd_wl2 = ack; + if (new_wnd) { + flow->timer.persist_active = 0; + flow->timer.persist_timer_ms = 0; + flow->timer.persist_timeout_ms = 0; + } + } + + if (TCP_SEQ_GT(ack, flow->tx.snd_una)) { + uint32_t prev_una = flow->tx.snd_una; + + flow->tx.snd_una = ack; + flow->base.ctx.ack_received = ack; + flow->tx.dup_acks = 0; + bool syn_retransmitted = tcp_acknowledge_segments(flow, ack); + if (syn_retransmitted && !flow->tx.rtt_valid && flow->tx.rto < TCP_SYN_DATA_RTO) flow->tx.rto = TCP_SYN_DATA_RTO; + + tcp_sack_trim_ranges(flow->tx.sack_ranges, &flow->tx.sack_range_count, ack); + tcp_sack_trim_ranges(flow->tx.sack_retransmitted_ranges, &flow->tx.sack_retransmitted_count, ack); + if (flow->tx.sack_ok && parsed_opts.sack_count) (void)tcp_apply_sack_blocks(flow, &parsed_opts); + tcp_cc_on_new_ack(flow, ack, prev_una); + tcp_restart_retransmit_timer(flow); + if (flow->tx.in_fast_recovery && flow->tx.sack_ok) tcp_sack_recovery_send(flow, false); + + if (flow->base.state == TCP_FIN_WAIT_1 && TCP_SEQ_GEQ(ack, flow->base.ctx.expected_ack)) { + flow->base.state = TCP_FIN_WAIT_2; + flow->timer.fin_wait2_ms = 0; + tcp_daemon_kick(); + } else if (flow->base.state == TCP_CLOSING && TCP_SEQ_GEQ(ack, flow->base.ctx.expected_ack)) { + tcp_enter_time_wait(flow); + tcp_flow_put(flow); + netpkt_unref(pkt); + return; + } else if (flow->base.state == TCP_LAST_ACK && TCP_SEQ_GEQ(ack, flow->base.ctx.expected_ack)) { + tcp_free_flow(flow); + tcp_flow_put(flow); + netpkt_unref(pkt); + return; + } + } else if (ack == flow->tx.snd_una && data_len == 0 && !fin) { + bool sack_changed = flow->tx.sack_ok && parsed_opts.sack_count && tcp_apply_sack_blocks(flow, &parsed_opts); + if (old_wnd && flow->tx.snd_wnd && TCP_SEQ_LT(flow->tx.snd_una, flow->tx.snd_nxt) && (flow->tx.snd_wnd == old_wnd || sack_changed)) { + if (flow->tx.dup_acks < UINT8_MAX) flow->tx.dup_acks++; + tcp_cc_on_dupack(flow); + } + } else flow->tx.dup_acks = 0; + } + + if (flags & (1 << URG_F)) { + //do we really need this in 2026? + uint32_t urg_seq = seq + urgent_ptr; + if (TCP_SEQ_GT(urg_seq, flow->rx.rcv_base) && (!flow->rx.urg_valid || TCP_SEQ_GT(urg_seq, flow->rx.urg_seq))) { + flow->rx.urg_valid = 1; + flow->rx.urg_seq = urg_seq; + } + } + + if (flow->tx.snd_wnd > 0) { + tcp_tx_seg_t *best = tcp_find_first_unacked(flow); + if (best && best->persist) { + best->persist = 0; + if (!tcp_retransmit_seg(flow, best)) tcp_restart_retransmit_timer(flow); + } + } + + if (flow->tx.nagle_len && flow->tx.snd_wnd > 0 && !tcp_flush_nagle(flow, flow->tx.fin_tx_pending ? 1 : 0)) tcp_daemon_kick(); + if (flow->tx.fin_tx_pending) { + tcp_try_send_pending_fin(flow); + if (flow->tx.fin_tx_pending) tcp_daemon_kick(); } bool need_ack = false; @@ -1069,15 +1393,12 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a uint8_t had_reass = flow->rx.reass_count ? 1 : 0; uint32_t fin_seq = seg_seq + orig_data_len; uint32_t orig_end = seg_seq + orig_data_len + (fin ? 1u : 0u); + bool discard_payload = flow->base.state == TCP_FIN_WAIT_1 || flow->base.state == TCP_FIN_WAIT_2 || flow->base.state == TCP_CLOSING || flow->base.state == TCP_LAST_ACK || flow->base.state == TCP_TIME_WAIT; - if (flow->tx.sack_ok && flow->tx.dsack_enabled && orig_data_len && TCP_SEQ_LT(seg_seq, rcv_nxt)) { + if (orig_data_len && TCP_SEQ_LT(seg_seq, rcv_nxt)) { uint32_t duplicate_right = seg_seq + orig_data_len; if (TCP_SEQ_GT(duplicate_right, rcv_nxt)) duplicate_right = rcv_nxt; - if (TCP_SEQ_GT(duplicate_right, seg_seq)) { - flow->rx.dsack_pending = 1; - flow->rx.dsack_left = seg_seq; - flow->rx.dsack_right = duplicate_right; - } + tcp_note_dsack(flow, seg_seq, duplicate_right); } if (TCP_SEQ_LEQ(orig_end, rcv_nxt) || TCP_SEQ_GEQ(seg_seq, wnd_end)) { @@ -1122,7 +1443,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a uint32_t accepted = 0; uint32_t stored = 0; - if (offer && (flow->base.state == TCP_FIN_WAIT_1 || flow->base.state == TCP_FIN_WAIT_2 || flow->base.state == TCP_CLOSING || flow->base.state == TCP_LAST_ACK || flow->base.state == TCP_TIME_WAIT)) accepted = offer; + if (offer && discard_payload) accepted = offer; else if (offer && flow->rx.rcv_buf && flow->rx.rcv_wnd_max) { uint8_t *rx = (uint8_t *)flow->rx.rcv_buf; uint32_t cap = flow->rx.rcv_wnd_max; @@ -1142,7 +1463,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a if (stored) flow->rx.rcv_data_nxt = flow->rx.rcv_nxt; flow->base.ctx.ack = flow->rx.rcv_nxt; } else if (data_len) { - (void)tcp_calc_adv_wnd_field(flow, 1); + tcp_update_adv_wnd(flow, 1); ack_immediate = true; } @@ -1184,11 +1505,11 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a ack_immediate = true; } - (void)tcp_calc_adv_wnd_field(flow, 1); + tcp_update_adv_wnd(flow, 1); need_ack = true; } else { - if (!(flow->base.state == TCP_FIN_WAIT_1 || flow->base.state == TCP_FIN_WAIT_2 || flow->base.state == TCP_CLOSING || flow->base.state == TCP_LAST_ACK || flow->base.state == TCP_TIME_WAIT) && data_len && flow->rx.rcv_buf && flow->rx.rcv_wnd_max) { + if (!discard_payload && data_len && flow->rx.rcv_buf && flow->rx.rcv_wnd_max) { uint32_t ooo_seq = seg_seq; uint32_t ooo_data = payload; uint32_t ooo_len = data_len; @@ -1225,11 +1546,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a if (TCP_SEQ_LEQ(r->seq, start) && TCP_SEQ_GEQ(r->end, end)) { flow->rx.sack_recent_left = r->seq; flow->rx.sack_recent_right = r->end; - if (flow->tx.sack_ok && flow->tx.dsack_enabled) { - flow->rx.dsack_pending = 1; - flow->rx.dsack_left = start; - flow->rx.dsack_right = end; - } + tcp_note_dsack(flow, start, end); covered = true; break; } @@ -1238,11 +1555,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a if (flow->tx.sack_ok && flow->tx.dsack_enabled && !flow->rx.dsack_pending) { uint32_t duplicate_left = TCP_SEQ_GT(start, r->seq) ? start : r->seq; uint32_t duplicate_right = TCP_SEQ_LT(end, r->end) ? end : r->end; - if (TCP_SEQ_GT(duplicate_right, duplicate_left)) { - flow->rx.dsack_pending = 1; - flow->rx.dsack_left = duplicate_left; - flow->rx.dsack_right = duplicate_right; - } + tcp_note_dsack(flow, duplicate_left, duplicate_right); } if (TCP_SEQ_LT(r->seq, merged_start)) merged_start = r->seq; @@ -1302,7 +1615,7 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a tcp_account_ooo_add(merged_end - merged_start, 1); flow->rx.sack_recent_left = merged_start; flow->rx.sack_recent_right = merged_end; - tcp_calc_adv_wnd_field(flow, 1); + tcp_update_adv_wnd(flow, 1); } } } @@ -1333,29 +1646,3 @@ void tcp_input(ip_version_t ipver, const void *src_ip_addr, const void *dst_ip_a tcp_flow_put(flow); netpkt_unref(pkt); } - -void tcp_flow_on_app_read(tcp_data *flow_ctx, uint32_t bytes_read){ - if (!flow_ctx || bytes_read == 0) return; - - tcp_flow_t *flow = tcp_flow_from_ctx(flow_ctx); - if (!flow) return; - - uint32_t old_edge = flow->rx.rcv_adv_edge; - uint16_t old_adv_field = flow->base.ctx.window; - uint32_t old_nxt = flow->rx.rcv_nxt; - - bool advanced = tcp_reass_drain_inseq(flow); - tcp_calc_adv_wnd_field(flow, 1); - - if (flow->base.state != TCP_STATE_CLOSED && flow->base.state != TCP_TIME_WAIT) { - uint32_t threshold = flow->tx.mss ? flow->tx.mss : TCP_DEFAULT_MSS; - uint32_t half = flow->rx.rcv_wnd_max >> 1; - if (half && half < threshold) threshold = half; - if (!threshold) threshold = 1; - - uint32_t delta = TCP_SEQ_GT(flow->rx.rcv_adv_edge, old_edge) ? flow->rx.rcv_adv_edge - old_edge : 0; - if (advanced || flow->rx.rcv_nxt != old_nxt || (!old_adv_field && flow->base.ctx.window) || delta >= threshold) tcp_send_ack_now(flow); - } - - tcp_flow_put(flow); -} diff --git a/kernel/networking/transport_layer/tcp/tcp_timer.c b/kernel/networking/transport_layer/tcp/tcp_timer.c index 028dc820..741dccf5 100644 --- a/kernel/networking/transport_layer/tcp/tcp_timer.c +++ b/kernel/networking/transport_layer/tcp/tcp_timer.c @@ -12,7 +12,8 @@ static bool tcp_timer_send_ack_segment(tcp_flow_t *flow, uint32_t seq, const uin hdr.sequence = bswap32(seq); hdr.ack = bswap32(flow->base.ctx.ack); hdr.flags = (uint8_t)(1u << ACK_F); - hdr.window = tcp_calc_adv_wnd_field(flow, 1); + tcp_update_adv_wnd(flow, 1); + hdr.window = flow->base.ctx.window; hdr.urgent_ptr = 0; return tcp_send_flow_segment(flow, &hdr, NULL, 0, payload, payload_len); } @@ -50,6 +51,11 @@ static int tcp_daemon_entry(int argc, char *argv[]) { irq_flags_t irq = irq_save_disable(); active_count = tcp_active_count; + if (!active_count) { + tcp_daemon_running = 0; + irq_restore(irq); + return 0; + } for (uint16_t i = 0; i < active_count; ++i) { uint16_t slot = tcp_active_flows[i]; tcp_flow_t* flow = slot < MAX_TCP_FLOWS ? tcp_flows[slot] : NULL; @@ -88,10 +94,21 @@ static int tcp_daemon_entry(int argc, char *argv[]) { if (flow->timer.delayed_ack_pending) { flow->timer.delayed_ack_timer_ms += elapsed_ms; - if (flow->timer.delayed_ack_timer_ms >= TCP_DELAYED_ACK_MS) (void)tcp_send_ack_now(flow); + if (flow->timer.delayed_ack_timer_ms >= TCP_DELAYED_ACK_MS) tcp_send_ack_now(flow); + } + + if (flow->tx.snd_wnd > 0) { + tcp_tx_seg_t *best = tcp_find_first_unacked(flow); + if (best && best->persist) { + best->persist = 0; + if (!tcp_retransmit_seg(flow, best)) { + best->timer_ms = 0; + best->timeout_ms = flow->tx.rto ? flow->tx.rto : TCP_INIT_RTO; + } + } } - if (flow->tx.nagle_len) { + if (flow->tx.nagle_len && flow->tx.snd_wnd > 0) { if (flow->tx.snd_nxt == flow->tx.snd_una) { if (!tcp_flush_nagle(flow, 1)) flow->tx.nagle_timer_ms = 0; } else { @@ -100,9 +117,9 @@ static int tcp_daemon_entry(int argc, char *argv[]) { } } - if (flow->tx.fin_tx_pending) (void)tcp_try_send_pending_fin(flow); + if (flow->tx.fin_tx_pending) tcp_try_send_pending_fin(flow); - if (!flow->tx.fin_tx_pending && flow->timer.keepalive_on && flow->base.state == TCP_ESTABLISHED && flow->timer.keepalive_ms) { + if (!flow->tx.fin_tx_pending && !flow->tx.nagle_len && flow->timer.keepalive_on && flow->base.state == TCP_ESTABLISHED && flow->timer.keepalive_ms && flow->tx.snd_nxt == flow->tx.snd_una) { flow->timer.keepalive_idle_ms += elapsed_ms; if (flow->timer.keepalive_idle_ms >= flow->timer.keepalive_ms) { uint32_t seq = flow->tx.snd_nxt; @@ -112,44 +129,43 @@ static int tcp_daemon_entry(int argc, char *argv[]) { } } - if (flow->tx.snd_wnd == 0 && (TCP_SEQ_GT(flow->tx.snd_nxt, flow->tx.snd_una) || flow->tx.fin_tx_pending)) { + bool persist_state = flow->base.state == TCP_ESTABLISHED || flow->base.state == TCP_CLOSE_WAIT || flow->base.state == TCP_FIN_WAIT_1 || flow->base.state == TCP_FIN_WAIT_2 || flow->base.state == TCP_CLOSING || flow->base.state == TCP_LAST_ACK; + if (persist_state && flow->tx.snd_wnd == 0 && (TCP_SEQ_GT(flow->tx.snd_nxt, flow->tx.snd_una) || flow->tx.nagle_len || flow->tx.fin_tx_pending)) { if (!flow->timer.persist_active) { flow->timer.persist_active = 1; flow->timer.persist_timer_ms = 0; - flow->timer.persist_probe_cnt = 0; - flow->timer.persist_timeout_ms = TCP_PERSIST_MIN_MS; + flow->timer.persist_timeout_ms = flow->tx.rto ? flow->tx.rto : TCP_INIT_RTO; + if (flow->timer.persist_timeout_ms < TCP_PERSIST_MIN_MS) flow->timer.persist_timeout_ms = TCP_PERSIST_MIN_MS; + if (flow->timer.persist_timeout_ms > TCP_PERSIST_MAX_MS) flow->timer.persist_timeout_ms = TCP_PERSIST_MAX_MS; } else { flow->timer.persist_timer_ms += elapsed_ms; if (flow->timer.persist_timer_ms >= flow->timer.persist_timeout_ms) { - if (flow->tx.fin_tx_pending && flow->timer.persist_probe_cnt >= TCP_MAX_PERSIST_PROBES) { - retire_flow = true; - } else { - tcp_tx_seg_t *best = tcp_find_first_unacked(flow); - - uint8_t payload[TCP_PERSIST_PROBE_BUFSZ]; - const uint8_t *probe_payload = NULL; - uint16_t probe_len = 0; - - uint32_t probe_seq = flow->tx.snd_una; - if (!best && flow->tx.fin_tx_pending && flow->tx.snd_nxt == flow->tx.snd_una && flow->tx.snd_nxt) probe_seq = flow->tx.snd_nxt - 1; - - const uint8_t *best_payload = tcp_tx_seg_payload_ptr(best); - if (best && best_payload && best->len && TCP_SEQ_GEQ(probe_seq, best->seq) && TCP_SEQ_LT(probe_seq, best->seq + best->len)) { - payload[0] = best_payload[probe_seq - best->seq]; - probe_payload = payload; - probe_len = 1; - } + tcp_tx_seg_t *best = tcp_find_first_unacked(flow); + + uint8_t payload = 0; + const uint8_t *probe_payload = NULL; + uint16_t probe_len = 0; - bool sent = tcp_timer_send_ack_segment(flow, probe_seq, probe_payload, probe_len); - flow->timer.persist_timer_ms = 0; + uint32_t probe_seq = flow->tx.snd_una; + if (!best && flow->tx.fin_tx_pending && flow->tx.snd_nxt == flow->tx.snd_una && flow->tx.snd_nxt) probe_seq = flow->tx.snd_nxt - 1; + + const uint8_t *best_payload = tcp_tx_seg_payload_ptr(best); + if (best && best_payload && best->len && TCP_SEQ_GEQ(probe_seq, best->seq) && TCP_SEQ_LT(probe_seq, best->seq + best->len)) { + payload = best_payload[probe_seq - best->seq]; + probe_payload = &payload; + probe_len = 1; + } - if (sent) { - if (flow->timer.persist_probe_cnt < UINT8_MAX) flow->timer.persist_probe_cnt++; - if (flow->timer.persist_timeout_ms < TCP_PERSIST_MAX_MS) { - uint32_t next = flow->timer.persist_timeout_ms << 1; - if (next > TCP_PERSIST_MAX_MS) next = TCP_PERSIST_MAX_MS; - flow->timer.persist_timeout_ms = next; - } + bool sent = false; + if (!best && flow->tx.nagle_len) sent = tcp_flush_nagle(flow, 1) != 0; + else sent = tcp_timer_send_ack_segment(flow, probe_seq, probe_payload, probe_len); + flow->timer.persist_timer_ms = 0; + + if (sent) { + if (flow->timer.persist_timeout_ms < TCP_PERSIST_MAX_MS) { + uint32_t next = flow->timer.persist_timeout_ms << 1; + if (next > TCP_PERSIST_MAX_MS) next = TCP_PERSIST_MAX_MS; + flow->timer.persist_timeout_ms = next; } } } @@ -158,42 +174,32 @@ static int tcp_daemon_entry(int argc, char *argv[]) { flow->timer.persist_active = 0; flow->timer.persist_timer_ms = 0; flow->timer.persist_timeout_ms = 0; - flow->timer.persist_probe_cnt = 0; - } - - if (retire_flow) { - tcp_free_flow(flow); - tcp_flow_put(flow); - continue; } if (TCP_SEQ_GT(flow->tx.snd_nxt, flow->tx.snd_una)) { for (uint32_t j = 0; j < TCP_MAX_TX_SEGS; j++) { - tcp_tx_seg_t *seg = &flow->tx.txq[j]; - if (!seg->used) continue; + tcp_tx_seg_t *sample = &flow->tx.txq[j]; + if (!sample->used || !sample->rtt_sample || sample->persist) continue; + sample->rtt_timer_ms += elapsed_ms; + } - uint32_t end_seq = seg->seq + seg->len + (seg->syn ? 1u : 0u) + (seg->fin ? 1u : 0u); - if (!TCP_SEQ_GT(end_seq, flow->tx.snd_una)) continue; + tcp_tx_seg_t *seg = tcp_find_first_unacked(flow); + if (seg && !(flow->tx.snd_wnd == 0 && !seg->syn)) { seg->timer_ms += elapsed_ms; - if (seg->timer_ms >= seg->timeout_ms && !(flow->tx.snd_wnd == 0 && seg->len > 0)) { - if (seg->retransmit_cnt >= TCP_MAX_RETRANS) { - retire_flow = true; - break; - } - if (!tcp_send_from_seg(flow, seg)) seg->timer_ms = 0; + if (seg->timer_ms >= seg->timeout_ms) { + if (seg->retransmit_cnt >= TCP_MAX_RETRANS) retire_flow = true; + else if (!tcp_retransmit_seg(flow, seg)) seg->timer_ms = 0; else { tcp_cc_on_timeout(flow); - seg->retransmit_cnt++; - seg->timer_ms = 0; - if (!seg->timeout_ms) { - uint32_t rto = flow->tx.rto ? flow->tx.rto : TCP_INIT_RTO; - if (rto < TCP_MIN_RTO) rto = TCP_MIN_RTO; - seg->timeout_ms = rto; - } else if (seg->timeout_ms < TCP_MAX_RTO) { - uint32_t next = seg->timeout_ms << 1; + uint32_t rto = flow->tx.rto ? flow->tx.rto : TCP_INIT_RTO; + if (rto < TCP_MIN_RTO) rto = TCP_MIN_RTO; + if (rto < TCP_MAX_RTO) { + uint32_t next = rto << 1; if (next > TCP_MAX_RTO) next = TCP_MAX_RTO; - seg->timeout_ms = next; + rto = next; } + flow->tx.rto = rto; + seg->timeout_ms = rto; } } } diff --git a/kernel/networking/transport_layer/tcp/tcp_tx.c b/kernel/networking/transport_layer/tcp/tcp_tx.c index d046cce7..d734662d 100644 --- a/kernel/networking/transport_layer/tcp/tcp_tx.c +++ b/kernel/networking/transport_layer/tcp/tcp_tx.c @@ -14,14 +14,15 @@ void tcp_tx_seg_clear(tcp_flow_t *flow, tcp_tx_seg_t *seg) { if (!seg) return; uint32_t len = seg->len; + if (seg->rtt_sample && flow) flow->tx.rtt_sample_pending = 0; if (seg->pkt) netpkt_unref(seg->pkt); if (len && flow) tcp_account_tx_remove(flow, len); memset(seg, 0, sizeof(*seg)); } -uint16_t tcp_calc_adv_wnd_field(tcp_flow_t *flow, uint8_t apply_scale) { - if (!flow) return 0; +void tcp_update_adv_wnd(tcp_flow_t *flow, uint8_t apply_scale) { + if (!flow) return; uint32_t shift = 0; if (apply_scale && flow->tx.ws_ok && flow->tx.ws_send) shift = flow->tx.ws_send; @@ -32,7 +33,7 @@ uint16_t tcp_calc_adv_wnd_field(tcp_flow_t *flow, uint8_t apply_scale) { if (flow->rx.rcv_buf && flow->rx.rcv_wnd_max) { hard_edge = flow->rx.rcv_base + flow->rx.rcv_wnd_max; if (TCP_SEQ_LT(hard_edge, flow->rx.rcv_nxt)) hard_edge = flow->rx.rcv_nxt; - } else if (flow->base.state == TCP_SYN_RECEIVED && flow->rx.rcv_wnd_max) hard_edge = flow->rx.rcv_nxt + flow->rx.rcv_wnd_max; + } else if ((flow->base.state == TCP_SYN_SENT || flow->base.state == TCP_SYN_RECEIVED) && flow->rx.rcv_wnd_max) hard_edge = flow->rx.rcv_nxt + flow->rx.rcv_wnd_max; if (TCP_SEQ_GT(hard_edge, accept_edge)) { uint32_t adv = hard_edge - flow->rx.rcv_nxt; @@ -74,7 +75,6 @@ uint16_t tcp_calc_adv_wnd_field(tcp_flow_t *flow, uint8_t apply_scale) { flow->rx.rcv_wnd = accept_adv; flow->rx.rcv_adv_edge = accept_edge; flow->base.ctx.window = (uint16_t)field; - return (uint16_t)field; } @@ -84,21 +84,32 @@ static uint32_t tcp_nagle_threshold(tcp_flow_t *flow) { return threshold; } -static uint64_t tcp_emit_data(tcp_flow_t *flow, const uint8_t *payload, uint64_t payload_len) { +static uint64_t tcp_emit_data(tcp_flow_t *flow, const uint8_t *payload, uint64_t payload_len, uint8_t push_partial) { if (!flow || (!payload && payload_len)) return 0; if (flow->base.state != TCP_ESTABLISHED && flow->base.state != TCP_CLOSE_WAIT) return 0; uint64_t in_flight = TCP_SEQ_GT(flow->tx.snd_nxt, flow->tx.snd_una) ? flow->tx.snd_nxt - flow->tx.snd_una : 0; + if (flow->tx.data_tx_valid && !in_flight) { + uint32_t idle_ms = (uint32_t)get_time() - flow->tx.last_data_tx_ms; + uint32_t rto = flow->tx.rto ? flow->tx.rto : TCP_INIT_RTO; + if (idle_ms >= rto) { + uint32_t iw = tcp_initial_cwnd(flow->tx.mss); + if (flow->tx.cwnd > iw) flow->tx.cwnd = iw; + flow->tx.cwnd_acc = 0; + } + } + uint32_t wnd = flow->tx.snd_wnd; uint32_t cwnd = flow->tx.cwnd ? flow->tx.cwnd : (flow->tx.mss ? flow->tx.mss : TCP_DEFAULT_MSS); uint32_t eff_wnd = wnd < cwnd ? wnd : cwnd; uint64_t can_send = 0; if (in_flight < eff_wnd) can_send = eff_wnd - in_flight; + bool persist_only = !can_send && !wnd && !in_flight && payload_len; + if (persist_only) can_send = 1; uint64_t remaining = payload_len; uint64_t sent_bytes = 0; - bool first_segment = true; while (remaining > 0 && can_send > 0) { uint64_t sendable = remaining > can_send ? can_send : remaining; @@ -143,12 +154,17 @@ static uint64_t tcp_emit_data(tcp_flow_t *flow, const uint8_t *payload, uint64_t seg->payload_off = 0; seg->syn = 0; seg->fin = 0; + seg->psh = push_partial && seg_len == remaining; + seg->persist = persist_only; seg->timer_ms = 0; seg->timeout_ms = flow->tx.rto ? flow->tx.rto : TCP_INIT_RTO; seg->retransmit_cnt = 0; seg->rtt_sample = 0; - if (!flow->tx.rtt_valid && first_segment) seg->rtt_sample = 1; + if (!persist_only && !flow->tx.rtt_sample_pending) { + seg->rtt_sample = 1; + flow->tx.rtt_sample_pending = 1; + } tcp_account_tx_add(flow, seg_len); flow->tx.snd_nxt += seg_len; flow->base.ctx.sequence = flow->tx.snd_nxt; @@ -163,7 +179,18 @@ static uint64_t tcp_emit_data(tcp_flow_t *flow, const uint8_t *payload, uint64_t sent_bytes += seg_len; remaining -= seg_len; can_send -= seg_len; - first_segment = false; + if (persist_only) { + flow->timer.persist_active = 1; + flow->timer.persist_timer_ms = 0; + if (!flow->timer.persist_timeout_ms) { + uint32_t timeout = flow->tx.rto ? flow->tx.rto : TCP_INIT_RTO; + if (timeout < TCP_PERSIST_MIN_MS) timeout = TCP_PERSIST_MIN_MS; + if (timeout > TCP_PERSIST_MAX_MS) timeout = TCP_PERSIST_MAX_MS; + flow->timer.persist_timeout_ms = timeout; + } + tcp_daemon_kick(); + break; + } } flow->base.ctx.sequence = flow->tx.snd_nxt; @@ -214,18 +241,16 @@ uint64_t tcp_flush_nagle(tcp_flow_t *flow, uint8_t force) { flow->tx.nagle_flushing = 1; uint32_t old_len = flow->tx.nagle_len; - uint64_t sent = tcp_emit_data(flow, (const uint8_t*)flow->tx.nagle_buf, old_len); + uint64_t sent = tcp_emit_data(flow, (const uint8_t*)flow->tx.nagle_buf, old_len, flow->tx.nagle_psh); if (!sent) { flow->tx.nagle_flushing = 0; return 0; } if (sent >= old_len) { - release((void*)flow->tx.nagle_buf); - flow->tx.nagle_buf = 0; flow->tx.nagle_len = 0; - flow->tx.nagle_cap = 0; flow->tx.nagle_timer_ms = 0; + flow->tx.nagle_psh = 0; flow->tx.nagle_flushing = 0; return sent; } @@ -280,9 +305,11 @@ bool tcp_send_from_seg(tcp_flow_t *flow, tcp_tx_seg_t *seg){ if (!(flow->base.state == TCP_SYN_SENT && seg->syn && flow->base.ctx.ack == 0)) flags |= (uint8_t)(1u << ACK_F); if (seg->syn) flags |= (uint8_t)(1u << SYN_F); if (seg->fin) flags |= (uint8_t)(1u << FIN_F); + if (seg->psh && seg->len) flags |= (uint8_t)(1u << PSH_F); hdr.flags = flags; - hdr.window = tcp_calc_adv_wnd_field(flow, seg->syn ? 0 : 1); + tcp_update_adv_wnd(flow, seg->syn ? 0 : 1); + hdr.window = flow->base.ctx.window; hdr.urgent_ptr = 0; const uint8_t *opts = seg->opts_len ? seg->opts : NULL; @@ -290,12 +317,30 @@ bool tcp_send_from_seg(tcp_flow_t *flow, tcp_tx_seg_t *seg){ bool ok = tcp_send_flow_segment(flow, &hdr, opts, seg->opts_len, tcp_tx_seg_payload_ptr(seg), (uint16_t)seg->len); if (!ok) return false; flow->timer.keepalive_idle_ms = 0; + if (seg->len) { + flow->tx.data_tx_valid = 1; + flow->tx.last_data_tx_ms = (uint32_t)get_time(); + } if (arm_timer) tcp_daemon_kick(); return true; } -bool tcp_send_ack_now(tcp_flow_t *flow){ - if (!flow) return false; +bool tcp_retransmit_seg(tcp_flow_t *flow, tcp_tx_seg_t *seg) { + if (!flow || !seg || !seg->used) return false; + if (!tcp_send_from_seg(flow, seg)) return false; + + for (uint32_t i = 0; i < TCP_MAX_TX_SEGS; i++) flow->tx.txq[i].rtt_sample = 0; + flow->tx.rtt_sample_pending = 0; + + if (seg->retransmit_cnt < UINT8_MAX) seg->retransmit_cnt++; + seg->timer_ms = 0; + seg->rtt_timer_ms = 0; + seg->timeout_ms = flow->tx.rto ? flow->tx.rto : TCP_INIT_RTO; + return true; +} + +void tcp_send_ack_now(tcp_flow_t *flow){ + if (!flow) return; tcp_hdr_t ackhdr; ackhdr.src_port = bswap16(flow->base.local.port); @@ -303,7 +348,8 @@ bool tcp_send_ack_now(tcp_flow_t *flow){ ackhdr.sequence = bswap32(flow->base.ctx.sequence); ackhdr.ack = bswap32(flow->base.ctx.ack); ackhdr.flags = (uint8_t)(1u << ACK_F); - ackhdr.window = tcp_calc_adv_wnd_field(flow, 1); + tcp_update_adv_wnd(flow, 1); + ackhdr.window = flow->base.ctx.window; ackhdr.urgent_ptr = 0; uint8_t opts[64]; @@ -376,7 +422,7 @@ bool tcp_send_ack_now(tcp_flow_t *flow){ flow->timer.delayed_ack_pending = 1; flow->timer.delayed_ack_timer_ms = 0; tcp_daemon_kick(); - return false; + return; } flow->rx.dsack_pending = 0; @@ -384,24 +430,37 @@ bool tcp_send_ack_now(tcp_flow_t *flow){ flow->rx.dsack_right = 0; flow->timer.delayed_ack_pending = 0; flow->timer.delayed_ack_timer_ms = 0; - return true; } -bool tcp_try_send_pending_fin(tcp_flow_t *flow) { - if (!flow || !flow->tx.fin_tx_pending) return false; - if (flow->base.state != TCP_ESTABLISHED&& flow->base.state != TCP_CLOSE_WAIT) return false; +void tcp_try_send_pending_fin(tcp_flow_t *flow) { + if (!flow || !flow->tx.fin_tx_pending) return; + + tcp_state_t next_state; + switch (flow->base.state) { + case TCP_ESTABLISHED: + next_state = TCP_FIN_WAIT_1; + break; + case TCP_CLOSE_WAIT: + next_state = TCP_LAST_ACK; + break; + default: + return; + } - if (flow->tx.nagle_len) tcp_flush_nagle(flow, 1); - if (flow->tx.nagle_len) return false; + if (flow->tx.nagle_len && flow->tx.snd_wnd == 0) return; + if (flow->tx.nagle_len) { + uint64_t flushed = tcp_flush_nagle(flow, 1); + if (!flushed || flow->tx.nagle_len) return; + } uint64_t in_flight = TCP_SEQ_GT(flow->tx.snd_nxt, flow->tx.snd_una) ? flow->tx.snd_nxt - flow->tx.snd_una : 0; uint32_t wnd = flow->tx.snd_wnd; uint32_t cwnd = flow->tx.cwnd ? flow->tx.cwnd : (flow->tx.mss ? flow->tx.mss : TCP_DEFAULT_MSS); uint32_t eff_wnd = wnd < cwnd ? wnd : cwnd; - if (eff_wnd == 0 || in_flight >= eff_wnd) return false; + if (eff_wnd == 0 || in_flight >= eff_wnd) return; tcp_tx_seg_t *seg = tcp_alloc_tx_seg(flow, 0); - if (!seg) return false; + if (!seg) return; seg->seq = flow->tx.snd_nxt; seg->len = 0; @@ -409,10 +468,16 @@ bool tcp_try_send_pending_fin(tcp_flow_t *flow) { seg->payload_off = 0; seg->syn = 0; seg->fin = 1; + seg->psh = 0; + seg->persist = 0; seg->timer_ms = 0; seg->timeout_ms = flow->tx.rto ? flow->tx.rto : TCP_INIT_RTO; seg->retransmit_cnt = 0; seg->rtt_sample = 0; + if (!flow->tx.rtt_sample_pending) { + seg->rtt_sample = 1; + flow->tx.rtt_sample_pending = 1; + } tcp_state_t old_state = flow->base.state; flow->tx.snd_nxt += 1; @@ -420,8 +485,7 @@ bool tcp_try_send_pending_fin(tcp_flow_t *flow) { flow->base.ctx.sequence = flow->tx.snd_nxt; flow->tx.fin_tx_pending = 0; - if (old_state == TCP_ESTABLISHED) flow->base.state = TCP_FIN_WAIT_1; - else flow->base.state = TCP_LAST_ACK; + flow->base.state = next_state; if (!tcp_send_from_seg(flow, seg)) { flow->tx.snd_nxt -= 1; @@ -430,10 +494,8 @@ bool tcp_try_send_pending_fin(tcp_flow_t *flow) { flow->base.state = old_state; flow->tx.fin_tx_pending = 1; tcp_tx_seg_clear(flow, seg); - return false; + return; } - - return true; } tcp_result_t tcp_flow_flush(tcp_data *flow_ctx){ @@ -445,7 +507,7 @@ tcp_result_t tcp_flow_flush(tcp_data *flow_ctx){ tcp_result_t rc = TCP_OK; if (flow->base.state != TCP_ESTABLISHED && flow->base.state != TCP_CLOSE_WAIT && flow->base.state != TCP_FIN_WAIT_1 && flow->base.state != TCP_FIN_WAIT_2) rc = TCP_INVALID; - while (rc == TCP_OK && flow->tx.nagle_len) { + while (rc == TCP_OK && flow->tx.nagle_len && flow->tx.snd_wnd > 0) { uint32_t before = flow->tx.nagle_len; uint64_t sent = tcp_flush_nagle(flow, 1); if (!sent || flow->tx.nagle_len == before) break; @@ -476,34 +538,66 @@ tcp_result_t tcp_flow_send(tcp_data *flow_ctx){ uint8_t *payload_ptr = (uint8_t *)flow_ctx->payload.ptr; uint64_t payload_len = flow_ctx->payload.size; flow_ctx->payload.size = 0; + if (payload_len && !payload_ptr) { + tcp_flow_put(flow); + return TCP_INVALID; + } bool want_fin = (flags & (1u << FIN_F)) != 0; + bool want_push = (flags & (1u << PSH_F)) != 0; bool fin_queued = false; uint64_t accepted = 0; tcp_result_t rc = TCP_OK; + bool zero_window_hold = false; if (flow->base.state != TCP_ESTABLISHED && flow->base.state != TCP_CLOSE_WAIT) rc = TCP_INVALID; - if (rc == TCP_OK && !flow->tx.nodelay && payload_len && flow->tx.nagle_len) { + if (rc == TCP_OK && payload_len && flow->tx.snd_wnd == 0 && flow->tx.snd_nxt == flow->tx.snd_una) { + zero_window_hold = true; + if (!flow->tx.nagle_len) { + uint64_t n = tcp_nagle_append(flow, payload_ptr, 1); + if (n && want_push && n == payload_len) flow->tx.nagle_psh = 1; + accepted += n; + payload_ptr += n; + payload_len -= n; + if (n) { + if (!flow->timer.persist_active) { + flow->timer.persist_active = 1; + flow->timer.persist_timer_ms = 0; + } + if (!flow->timer.persist_timeout_ms) { + uint32_t timeout = flow->tx.rto ? flow->tx.rto : TCP_INIT_RTO; + if (timeout < TCP_PERSIST_MIN_MS) timeout = TCP_PERSIST_MIN_MS; + if (timeout > TCP_PERSIST_MAX_MS) timeout = TCP_PERSIST_MAX_MS; + flow->timer.persist_timeout_ms = timeout; + } + tcp_daemon_kick(); + } + } + } + + if (rc == TCP_OK && !zero_window_hold && !flow->tx.nodelay && payload_len && flow->tx.nagle_len) { uint64_t n = tcp_nagle_append(flow, payload_ptr, payload_len); + if (n && want_push && n == payload_len) flow->tx.nagle_psh = 1; accepted += n; payload_ptr += n; payload_len -= n; - if (flow->tx.nagle_len >= tcp_nagle_threshold(flow)) tcp_flush_nagle(flow, 1); + if (flow->tx.nagle_len >= tcp_nagle_threshold(flow) && !tcp_flush_nagle(flow, 1)) tcp_daemon_kick(); } uint64_t in_flight = TCP_SEQ_GT(flow->tx.snd_nxt, flow->tx.snd_una) ? flow->tx.snd_nxt - flow->tx.snd_una : 0; - if (rc == TCP_OK && !flow->tx.nodelay && payload_len && payload_len < tcp_nagle_threshold(flow) && in_flight != 0) { + if (rc == TCP_OK && !zero_window_hold && !flow->tx.nodelay && payload_len && payload_len < tcp_nagle_threshold(flow) && in_flight != 0) { uint64_t n = tcp_nagle_append(flow, payload_ptr, payload_len); + if (n && want_push && n == payload_len) flow->tx.nagle_psh = 1; accepted += n; payload_ptr += n; payload_len -= n; - if (flow->tx.nagle_len >= tcp_nagle_threshold(flow)) tcp_flush_nagle(flow, 1); + if (flow->tx.nagle_len >= tcp_nagle_threshold(flow) && !tcp_flush_nagle(flow, 1)) tcp_daemon_kick(); } - if (rc == TCP_OK && payload_len) { - if (flow->tx.nagle_len) tcp_flush_nagle(flow, 1); + if (rc == TCP_OK && !zero_window_hold && payload_len) { + if (flow->tx.nagle_len && !tcp_flush_nagle(flow, 1)) tcp_daemon_kick(); if (!flow->tx.nagle_len) { - uint64_t n = tcp_emit_data(flow, payload_ptr, payload_len); + uint64_t n = tcp_emit_data(flow, payload_ptr, payload_len, want_push ? 1 : 0); accepted += n; payload_len -= n; } @@ -511,8 +605,9 @@ tcp_result_t tcp_flow_send(tcp_data *flow_ctx){ if (rc == TCP_OK && want_fin && payload_len == 0) { flow->tx.fin_tx_pending = 1; + if (flow->tx.nagle_len) flow->tx.nagle_psh = 1; fin_queued = true; - if (flow->tx.nagle_len) tcp_flush_nagle(flow, 1); + if (flow->tx.nagle_len && flow->tx.snd_wnd > 0 && !tcp_flush_nagle(flow, 1)) tcp_daemon_kick(); tcp_try_send_pending_fin(flow); if (flow->tx.fin_tx_pending) tcp_daemon_kick(); } @@ -526,9 +621,11 @@ tcp_result_t tcp_flow_send(tcp_data *flow_ctx){ else if (accepted || fin_queued) rc = TCP_OK; else { if (flow->tx.snd_wnd == 0) { - flow->timer.persist_active = 1; - flow->timer.persist_timer_ms = 0; - if (flow->timer.persist_timeout_ms == 0) flow->timer.persist_timeout_ms = TCP_PERSIST_MIN_MS; + if (!flow->timer.persist_active) { + flow->timer.persist_active = 1; + flow->timer.persist_timer_ms = 0; + } + if (flow->timer.persist_timeout_ms == 0) flow->timer.persist_timeout_ms = flow->tx.rto ? flow->tx.rto : TCP_INIT_RTO; if (flow->timer.persist_timeout_ms < TCP_PERSIST_MIN_MS) flow->timer.persist_timeout_ms = TCP_PERSIST_MIN_MS; if (flow->timer.persist_timeout_ms > TCP_PERSIST_MAX_MS) flow->timer.persist_timeout_ms = TCP_PERSIST_MAX_MS; tcp_daemon_kick(); @@ -550,7 +647,8 @@ tcp_result_t tcp_flow_close(tcp_data *flow_ctx){ tcp_result_t rc = TCP_INVALID; if (flow->base.state == TCP_ESTABLISHED || flow->base.state == TCP_CLOSE_WAIT) { flow->tx.fin_tx_pending = 1; - if (flow->tx.nagle_len) tcp_flush_nagle(flow, 1); + if (flow->tx.nagle_len) flow->tx.nagle_psh = 1; + if (flow->tx.nagle_len && flow->tx.snd_wnd > 0 && !tcp_flush_nagle(flow, 1)) tcp_daemon_kick(); tcp_try_send_pending_fin(flow); if (flow->tx.fin_tx_pending) tcp_daemon_kick(); rc = TCP_OK; diff --git a/kernel/networking/transport_layer/tcp/tcp_utils.c b/kernel/networking/transport_layer/tcp/tcp_utils.c index 8c6eff90..62d1e676 100644 --- a/kernel/networking/transport_layer/tcp/tcp_utils.c +++ b/kernel/networking/transport_layer/tcp/tcp_utils.c @@ -3,6 +3,16 @@ #include "tcp_internal.h" #include "networking/interface_manager.h" +#define TCP_INIT_CWND_SEGS 10u + +uint32_t tcp_initial_cwnd(uint32_t mss) { + if (!mss) mss = TCP_DEFAULT_MSS; + uint32_t ten_mss = mss * TCP_INIT_CWND_SEGS; + uint32_t lower = 2 * mss; + if (lower < 14600u) lower = 14600u; + return ten_mss < lower ? ten_mss : lower; +} + void tcp_update_mss(tcp_flow_t *flow) { if (!flow) return; uint32_t local_mss = flow->tx.path_mss ? flow->tx.path_mss : TCP_DEFAULT_MSS; @@ -16,13 +26,14 @@ void tcp_update_mss(tcp_flow_t *flow) { } uint32_t tcp_calc_mss_for_l3(uint8_t l3_id, ip_version_t ver, const void *remote_ip){ + //TODO propagate icmp4/6 pmtu and errors uint32_t mtu = 1500; if (ver == IP_VER4) { l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(l3_id); if (v4) mtu = v4->runtime_opts_v4.mtu ? v4->runtime_opts_v4.mtu : 1500; } else if (ver == IP_VER6) { l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(l3_id); - if (v6) mtu =v6->mtu ? v6->mtu : 1500; + if (v6) mtu = v6->mtu ? v6->mtu : 1500; } else return 256; if (ver == IP_VER6 && remote_ip){ diff --git a/kernel/networking/transport_layer/tcp/tcp_utils.h b/kernel/networking/transport_layer/tcp/tcp_utils.h index ea71f19d..498a5c43 100644 --- a/kernel/networking/transport_layer/tcp/tcp_utils.h +++ b/kernel/networking/transport_layer/tcp/tcp_utils.h @@ -33,6 +33,7 @@ uint8_t tcp_build_syn_options(uint8_t *out, uint16_t mss, uint8_t wscale, uint8_ void tcp_update_mss(struct tcp_flow *flow); uint32_t tcp_calc_mss_for_l3(uint8_t l3_id, ip_version_t ver, const void *remote_ip); +uint32_t tcp_initial_cwnd(uint32_t mss); #ifdef __cplusplus } diff --git a/shared b/shared index ee12c278..75729218 160000 --- a/shared +++ b/shared @@ -1 +1 @@ -Subproject commit ee12c278e322a1ca14b58a81b7bb6e2377c43251 +Subproject commit 7572921822891eaacaf744a6e5daa754b3358afb