diff --git a/Makefile b/Makefile index 993aa303..795268d8 100644 --- a/Makefile +++ b/Makefile @@ -51,7 +51,10 @@ clean: $(MAKE) -C modules $@ $(MAKE) -C libs $@ @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/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/fw/fw_cfg.c b/kernel/fw/fw_cfg.c index 6257d8d4..5ec2a23d 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" @@ -44,7 +45,6 @@ void fw_cfg_dma_operation(void* dest, uint32_t size, uint32_t ctrl) { }; write64(PHYS_TO_VIRT(FW_CFG_DMA), __builtin_bswap64(pt_va_to_pa(&access))); - __asm__("isb"); if (!wait(&access.control, __builtin_bswap32(~0x1), false, 2000)){ @@ -71,7 +71,7 @@ bool fw_find_file(const char* search, struct fw_cfg_file *file) { if (!fw_cfg_check()) return false; - + u32 count = 0; fw_cfg_dma_read(&count, sizeof(count), FW_LIST_DIRECTORY); diff --git a/kernel/memory/addr.c b/kernel/memory/addr.c index e7bde8dc..f8905d46 100644 --- a/kernel/memory/addr.c +++ b/kernel/memory/addr.c @@ -1,4 +1,5 @@ #include "addr.h" +#include "va_layout.h" #include "va_layout.h" diff --git a/kernel/memory/page_allocator.c b/kernel/memory/page_allocator.c index 78919279..d9a85c20 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/application_layer/csocket_http_client.c b/kernel/networking/application_layer/csocket_http_client.c new file mode 100644 index 00000000..46968e73 --- /dev/null +++ b/kernel/networking/application_layer/csocket_http_client.c @@ -0,0 +1,465 @@ +#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" +#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; + SocketOptions log_opts; + SocketOptions tcp_opts; + 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) { + 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); + } + + 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); + } + + 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) (void)http_socket_close(&cli->sock, true); + 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(); + HTTPSocketTimeoutState body_timeout = {body_start_ms, body_start_ms}; + + while (chunk_result == HTTP_PARSE_INCOMPLETE) { + 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); + } + + if (chunk_result != HTTP_PARSE_OK) { + http_chunked_decoder_free(&dec); + http_response_free(&resp); + string_free(buf); + if (cli->sock) (void)http_socket_close(&cli->sock, true); + 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) (void)http_socket_close(&cli->sock, true); + 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) (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); + + if (copied < need) { + 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); + } + } + + 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(); + HTTPSocketTimeoutState body_timeout = {body_start_ms, body_start_ms}; + while (body.length <= cli->policy.common.max_body_bytes) { + 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) (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) (void)http_socket_close(&cli->sock, true); + return http_client_error_response(SOCK_ERR_PROTO); + } + string_append_bytes(&body, tmp, (uint32_t)r); + } + + 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; + http_socket_fill_log_endpoints(cli->sock, &ev); + 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) { + 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 && (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 { + (void)http_socket_close(&cli->sock, true); + } + + 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) { + http_socket_fill_log_endpoints(cli->sock, &ev); + 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 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; + cli->tcp_opts = *extra; + cli->tcp_opts.flags &= ~SOCK_OPT_DEBUG; + } + + return cli; +} + +void http_client_destroy(http_client_handle_t h) { + if (!h) return; + HTTPClient* cli = (HTTPClient*)h; + if (cli->sock) (void)http_socket_close(&cli->sock, true); + if (cli->origin.domain.mem_length) string_free(cli->origin.domain); + 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 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; + ev.action = NETLOG_ACT_HTTP_SEND_REQUEST; + ev.pid = get_current_proc_pid(); + ev.u0 = out_len; + ev.i0 = sent; + http_socket_fill_log_endpoints(cli->sock, &ev); + + 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; + 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) { + 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); + curr.headers_common.fields.host = (string){0}; + } + + 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); + 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); + + 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); + 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) http_socket_fill_log_endpoints(cli->sock, &ev); + + 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->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_client.cpp b/kernel/networking/application_layer/csocket_http_client.cpp deleted file mode 100644 index 2fe47744..00000000 --- a/kernel/networking/application_layer/csocket_http_client.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#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) { - HTTPClient* cli = new HTTPClient(pid, extra); - 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_connect_ex(http_client_handle_t h, uint8_t dst_kind, const void* dst, uint16_t port) { - if (!h || !dst) return (int32_t)SOCK_ERR_INVAL; - HTTPClient *cli = reinterpret_cast(h); - return cli->connect(static_cast(dst_kind), dst, 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_client.h b/kernel/networking/application_layer/csocket_http_client.h index 30bd1088..5c541ab9 100644 --- a/kernel/networking/application_layer/csocket_http_client.h +++ b/kernel/networking/application_layer/csocket_http_client.h @@ -8,10 +8,12 @@ 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(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); -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_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_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 new file mode 100644 index 00000000..de6ef8f1 --- /dev/null +++ b/kernel/networking/application_layer/csocket_http_server.c @@ -0,0 +1,530 @@ +#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" +#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; + SocketOptions log_opts; + SocketOptions tcp_opts; + HTTPServerPolicy policy; +} HTTPServer; + +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; + srv->tcp_opts = *extra; + srv->tcp_opts.flags &= ~SOCK_OPT_DEBUG; + } + + srv->tcp_opts.flags |= SOCK_OPT_NONBLOCK | SOCK_OPT_REUSEADDR; + + return srv; +} + +void http_server_destroy(http_server_handle_t h) { + if (!h) return; + HTTPServer* srv = (HTTPServer*)h; + if (srv->sock) (void)http_socket_close(&srv->sock, true); + 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_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; + int32_t 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; + //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) { + http_socket_close(&child, true); + return NULL; + } + + HTTPConnection* conn = (HTTPConnection*)zalloc(sizeof(HTTPConnection)); + if (!conn) { + http_socket_close(&child, true); + 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; + http_socket_fill_log_endpoints(child, &ev); + 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]; + int32_t hdr_end = -1; + uint32_t consumed = 0; + bool bad_request = false; + HttpError reject_status = HTTP_BAD_REQUEST; + char* body_copy = NULL; + + 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; + } else { + string_free(buf); + return req; + } + } + + if (!bad_request && (!buf.data || hdr_end < 2)) { + bad_request = true; + reject_status = HTTP_BAD_REQUEST; + } + + 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 (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; + } + } + + 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(); + HTTPSocketTimeoutState body_timeout = {body_start_ms, body_start_ms}; + consumed = body_start + used; + + while (chunk_result == HTTP_PARSE_INCOMPLETE) { + 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); + 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); + + 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; + } + } + } + + 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) (void)http_socket_close(&conn->client, true); + + 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; + http_socket_fill_log_endpoints(conn->client, &ev); + + 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(); + 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; + 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; + } + } + + 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 > body_off) { + const uint8_t* body = (const uint8_t*)res->body.data; + 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; + 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; + 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) { + 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; +} + +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); + release(conn); + return 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) 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); + 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 afe75eba..00000000 --- a/kernel/networking/application_layer/csocket_http_server.cpp +++ /dev/null @@ -1,68 +0,0 @@ -#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) { - HTTPServer* srv = new HTTPServer(pid, extra); - 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_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); - TCPSocket* cli = srv->accept(); - return reinterpret_cast(cli); -} - -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); - 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); - 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; - 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/csocket_http_server.h b/kernel/networking/application_layer/csocket_http_server.h index 0b673473..59a7d81f 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(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); int32_t http_server_bind(http_server_handle_t srv, const struct SockBindSpec *spec, uint16_t port); 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 5b4d6ece..a2c082d9 100644 --- a/kernel/networking/application_layer/dhcp_daemon.c +++ b/kernel/networking/application_layer/dhcp_daemon.c @@ -7,17 +7,18 @@ #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/link_layer/link_utils.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" #include "networking/interface_manager.h" #include "networking/network.h" -#include "networking/internet_layer/ipv4_route.h" #include "syscalls/syscalls.h" typedef enum { @@ -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; @@ -49,12 +50,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) { @@ -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 (!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--; + continue; + } i++; } uint8_t n = l2_interface_count(); @@ -106,10 +96,15 @@ 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; - int idx; - if (find_state(v4->l3_id, &idx)) 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) { + found = true; + break; + } + } + if (found) continue; dhcp_if_state_t st; memset(&st, 0, sizeof(st)); st.ifindex = l2->ifindex; @@ -122,15 +117,24 @@ 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 = udp_socket_create(SOCK_ROLE_SERVER, g_pid_dhcpd, 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); + continue; + } SockBindSpec spec; memset(&spec, 0, sizeof(spec)); spec.kind = BIND_L3; + spec.ver = IP_VER4; spec.l3_id = st.l3_id; - if (socket_bind_udp_ex(st.sock, &spec, 68) != SOCK_OK) { - socket_destroy_udp(st.sock); + if (bind_socket(st.sock, &spec, 68) != SOCK_OK) { + close_socket(st.sock); continue; } st.retry_left_ms = 0; @@ -140,83 +144,75 @@ 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]; net_l4_endpoint src; memset(&src, 0, sizeof(src)); - int64_t r = socket_recvfrom_udp_ex(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; } + 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]; net_l4_endpoint src; memset(&src, 0, sizeof(src)); - int64_t r = socket_recvfrom_udp_ex(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; } + 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; } -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; @@ -306,29 +302,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) { @@ -338,21 +317,12 @@ 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); - socket_sendto_udp_ex(st->sock, 0, &dst, 0, (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); - socket_sendto_udp_ex(st->sock, 0, &dst, 0, (const void*)pkt.ptr, pkt.size); + 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); } @@ -361,7 +331,7 @@ static void dhcp_send_renew_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 = st->server_ip_net; @@ -369,10 +339,10 @@ 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); - socket_sendto_udp_ex(st->sock, 0, &dst, 0, (const void*)pkt.ptr, pkt.size); + 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); } @@ -381,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; @@ -389,10 +359,10 @@ 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); - socket_sendto_udp_ex(st->sock, 0, &dst, 0, (const void*)pkt.ptr, pkt.size); + 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); } @@ -401,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; @@ -409,14 +379,20 @@ 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); - socket_sendto_udp_ex(st->sock, 0, &dst, 0, (const void*)pkt.ptr, pkt.size); + 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); } @@ -430,44 +406,48 @@ 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; - schedule_retry(st); + st->retry_left_ms = dhcp_next_backoff_ms(st); break; } 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; - 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); + 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); - 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; @@ -491,19 +471,15 @@ 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); - 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); @@ -515,28 +491,18 @@ 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); - 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; } @@ -566,8 +532,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.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 46333545..2bcf21be 100644 --- a/kernel/networking/application_layer/dhcpv6_daemon.c +++ b/kernel/networking/application_layer/dhcpv6_daemon.c @@ -5,17 +5,19 @@ #include "syscalls/syscalls.h" #include "process/scheduler.h" #include "math/rng.h" +#include "random/random.h" #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" #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, @@ -37,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; @@ -72,42 +74,31 @@ 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; } 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); -} - -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; + 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) { @@ -135,6 +126,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; @@ -181,7 +177,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) { @@ -189,20 +185,15 @@ 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); 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) close_socket(rb->sock); free_sized(rb, sizeof(*rb)); } } @@ -247,9 +238,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; @@ -267,9 +256,12 @@ 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 = udp_socket_create(SOCKET_SERVER, g_dhcpv6_pid, NULL); + b->sock = create_socket(PROTO_UDP, &(SocketOptions){.flags = SOCK_OPT_NONBLOCK}); if (!b->sock) { free_sized(b, sizeof(*b)); continue; @@ -281,15 +273,15 @@ 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) { - socket_destroy_udp(b->sock); + if (bind_socket(b->sock, &spec, DHCPV6_CLIENT_PORT) != SOCK_OK) { + close_socket(b->sock); b->sock = 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); @@ -316,9 +308,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; @@ -355,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) { @@ -411,8 +397,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; @@ -435,9 +420,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; } @@ -480,12 +463,11 @@ 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)) { - memcpy(v6->runtime_opts_v6.dns[0], v6->gateway, 16); + ipv6_cpy(v6->runtime_opts_v6.dns[0], v6->gateway); } } @@ -497,6 +479,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); @@ -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_ex(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_ex(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); @@ -596,12 +584,11 @@ 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)) { - memcpy(v6->runtime_opts_v6.dns[0], v6->gateway, 16); + ipv6_cpy(v6->runtime_opts_v6.dns[0], v6->gateway); } } @@ -646,12 +633,11 @@ 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)) { - memcpy(v6->runtime_opts_v6.dns[0], v6->gateway, 16); + ipv6_cpy(v6->runtime_opts_v6.dns[0], v6->gateway); } } @@ -667,12 +653,11 @@ 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)) { - memcpy(v6->runtime_opts_v6.dns[0], v6->gateway, 16); + ipv6_cpy(v6->runtime_opts_v6.dns[0], v6->gateway); } } @@ -719,12 +704,11 @@ 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)) { - memcpy(v6->runtime_opts_v6.dns[0], v6->gateway, 16); + ipv6_cpy(v6->runtime_opts_v6.dns[0], v6->gateway); } } @@ -764,15 +748,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); } } } @@ -786,12 +762,9 @@ 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)); - 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/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 d9262a63..534795ad 100644 --- a/kernel/networking/application_layer/dns/dns.c +++ b/kernel/networking/application_layer/dns/dns.c @@ -6,280 +6,99 @@ #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 +#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 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(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 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); + rng_init_random(&rng); uint16_t message_id = (uint16_t)(rng_next32(&rng) & 0xFFFF); - wr_be16(request_buffer+0, message_id); - wr_be16(request_buffer+2, 0x0100); - wr_be16(request_buffer+4, 1); - uint32_t offset = 12; - 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 = 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_ex(sock, response_buffer, sizeof(response_buffer), &source); - bool ok_src = false; + int64_t received = receive_from_socket(sock, response_buffer, sizeof(response_buffer), &source); 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; + 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){ - if (l3_ipv4_find_by_id(l3_id)) { - l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(l3_id); - if (!v4) return false; +static bool pick_dns_on_l3(uint8_t l3_id, net_l4_endpoint* out_primary, net_l4_endpoint* out_secondary) { + 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 != 0) || (s != 0); + 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; } @@ -290,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; } @@ -312,21 +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; - 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; + 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; } @@ -337,209 +147,154 @@ 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; + 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; - 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); + 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_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); + + 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); } + + close_socket(sock); 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..e5863269 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/transport_layer/csocket.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..c9baeb00 100644 --- a/kernel/networking/application_layer/dns/dns_daemon.c +++ b/kernel/networking/application_layer/dns/dns_daemon.c @@ -5,80 +5,150 @@ #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/ipv4_utils.h" #include "networking/internet_layer/ipv6_utils.h" +#include "networking/interface_manager.h" #include "std/memory.h" -#include "net/socket_types.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 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 void* group) { - 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); - - socket_handle_t s = udp_socket_create(SOCK_ROLE_SERVER, g_pid_dnsd, &opt); - if(!s) return 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; +} - SockBindSpec spec; - memset(&spec, 0, sizeof(spec)); - spec.kind = BIND_ANY; +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 (!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) { + have_socket = true; + break; + } + } + if (have_socket) continue; + + socket_handle_t s = create_socket(PROTO_UDP, &(SocketOptions){.flags = SOCK_OPT_TTL | SOCK_OPT_NONBLOCK, .ttl = 255}); + 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++; + } - if(socket_bind_udp_ex(s, &spec, DNS_SD_MDNS_PORT) != SOCK_OK){ - socket_destroy_udp(s); - return 0; + 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 (!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) { + have_socket = true; + break; + } + } + if (have_socket) continue; + + socket_handle_t s = create_socket(PROTO_UDP, &(SocketOptions){.flags = SOCK_OPT_TTL | SOCK_OPT_NONBLOCK, .ttl = 255}); + 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++; + } } - - return s; } 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); + 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]; 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_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) { - 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 (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); + for (uint8_t sidx = 0; sidx < g_mdns_count; sidx++) { + socket_handle_t s = g_mdns[sidx].sock; + 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; + 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,(const uint8_t*)&mdns_v4,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 ca386c53..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_udp.h" +#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 d4a29226..42f63937 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 = dns_wire_build_query(request_buffer, sizeof(request_buffer), 0, name, qtype, false); + if (!offset) return DNS_ERR_FORMAT; - 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; - - int64_t sent = socket_sendto_udp_ex(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; 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; + 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]; + 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,92 @@ 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 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(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; + return DNS_OK; + } + } + + sock = mdns_socket_handle_for(IP_VER6); + if (sock) { + uint8_t group6[16]; + net_l4_endpoint dst; + 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; + 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; + } 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..76407db8 --- /dev/null +++ b/kernel/networking/application_layer/dns/dns_wire.c @@ -0,0 +1,316 @@ +#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; + 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; + 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 (skip) return true; + 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 (!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; + } + 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..13cc514a 100644 --- a/kernel/networking/application_layer/dns/mdns_responder.c +++ b/kernel/networking/application_layer/dns/mdns_responder.c @@ -2,9 +2,11 @@ #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_udp.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" @@ -16,6 +18,10 @@ #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" typedef struct { bool used; @@ -30,13 +36,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; @@ -47,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; @@ -69,69 +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 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 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; @@ -143,49 +90,41 @@ 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; - socket_sendto_udp_ex(sock, DST_ENDPOINT, &dst, 0, (void*)pkt, pkt_len); + if (!dst.port) dst.port = DNS_MDNS_PORT; + send_to_socket(sock, &dst, pkt, pkt_len); return; } 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; - socket_sendto_udp_ex(sock, DST_ENDPOINT, &dst, 0, (void*)pkt, pkt_len); + dst.port = DNS_MDNS_PORT; + 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; } @@ -193,20 +132,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; } } @@ -216,7 +150,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; } @@ -224,7 +157,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; } @@ -232,7 +164,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; } @@ -246,12 +177,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; @@ -263,9 +192,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; } @@ -301,22 +228,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; @@ -381,256 +308,122 @@ 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(); - 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; - strncpy(e->target, target, sizeof(e->target)); - e->expire_ms = now + (uint64_t)ttl_s * 1000; - return; - } - - 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_SD_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; +static mdns_cache_entry_t *mdns_cache_entry_for(const char *name, uint16_t rrtype, bool create) { + if (!name) return NULL; - uint64_t now = get_time(); + 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; - if (e->rrtype != DNS_SD_TYPE_SRV) continue; - if (strncmp(e->name, name, 256) != 0) continue; - e->port = port; - strncpy(e->target, target, sizeof(e->target)); - e->expire_ms = now + (uint64_t)ttl_s * 1000; - return; + if (!e->type) { + if (!empty) empty = e; + continue; + } + 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_SD_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(); - 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 (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_SD_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) { if (!name) return false; if (!out_ip) return false; - uint32_t oct[4]; - memset(oct, 0, sizeof(oct)); + char norm[DNS_WIRE_MAX_NAME]; + if (!dns_wire_name_normalize(name, norm, sizeof(norm))) return false; - const char *p = name; - for (int i = 0; i < 4; i++) { + uint8_t ip[4]; + const char *p = norm; + for (int i = 3; i >= 0; i--) { + const char *label = p; uint32_t v = 0; - uint32_t digits = 0; - while (*p >= '0' && *p <= '9') { - 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 (*p++ != '.') return false; - if (strncmp(p, "in-addr.arpa", 12) != 0) return false; - - uint32_t a = oct[3]; - uint32_t b = oct[2]; - uint32_t c = oct[1]; - uint32_t d = oct[0]; - - *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; } -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); + 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; + + 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); + 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); + 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; } - } 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; + 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; + } } } 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; } 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; } @@ -648,8 +441,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 +461,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 +539,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 +550,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; @@ -736,7 +562,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++) { @@ -765,33 +592,34 @@ 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 (mdns_current_ipv6(ip6) && !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); + 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; + 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]; + if (mdns_current_ipv6(ip6_cache)) dns_cache_put_ip(g_mdns_fqdn, DNS_TYPE_AAAA, ip6_cache, ttl_ms); + } } g_mdns_host_announce_left--; @@ -813,12 +641,13 @@ 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); - if (sock6 && 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) { @@ -833,6 +662,29 @@ 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) return; if (!mcast_ip) return; @@ -841,71 +693,99 @@ 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; + 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]; 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 || 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; 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; 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; } } - 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); + 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; } } } - 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 +801,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 +826,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; } } @@ -983,6 +860,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; @@ -990,11 +868,9 @@ 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 (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/dns/mdns_responder.h b/kernel/networking/application_layer/dns/mdns_responder.h index be14dd07..211d7935 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 @@ -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 eebf2698..82d29e36 100644 --- a/kernel/networking/application_layer/http.c +++ b/kernel/networking/application_layer/http.c @@ -1,63 +1,320 @@ #include "http.h" #include "std/string.h" #include "std/memory.h" -#include "syscalls/syscalls.h" -string http_header_builder(const HTTPHeadersCommon *C, const HTTPHeader *H, uint32_t N){ - string out = string_repeat('\0', 0); +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 = true, + }; + return p; +} - if (C->type.length){ - string_append_bytes(&out, "Content-Type: ", 14); - string_append_bytes(&out, C->type.data, C->type.length); - string_append_bytes(&out, "\r\n", 2); +HTTPPolicy http_policy_apply_options(HTTPPolicy p, const HTTPPolicyOptions *options) { + if (!options) return p; + + 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; +} + +HTTPServerPolicy http_server_policy_from_options(const HTTPServerPolicyOptions *options) { + 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, + .send_error_body = true + }; + + if (!options) return p; + + 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; +} + +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; + + 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; +} + +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"; + case HTTP_METHOD_UNKNOWN: return "UNKNOWN"; + default: return "UNKNOWN"; } +} + +bool http_method_allowed(uint32_t mask, HTTPMethod method) { + if ((uint32_t)method >= 32) return false; + return (mask & (1u << method)) != 0; +} - string tmp = string_format("Content-Length: %i\r\n", (int)C->length); - string_append_bytes(&out, tmp.data, tmp.length); - string_free(tmp); +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 + }; - if (C->date.length){ - string_append_bytes(&out, "Date: ", 6); - string_append_bytes(&out, C->date.data, C->date.length); + 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); + 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"; + 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"; + 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"; + 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"; + 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: + 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; + 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}; + + 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 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 { + 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; + out->method = HTTP_METHOD_UNKNOWN; + } + + 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; + + uint32_t code = 0; + 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; + while (i < len && buf[i] == ' ') i++; + out->reason_off = i; + out->reason_len = len - i; + return HTTP_PARSE_OK; +} + +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){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){ + 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->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; @@ -65,12 +322,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->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}; } @@ -80,88 +338,249 @@ 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, - HTTPHeadersCommon *C, - HTTPHeader **out_extra, - uint32_t *out_extra_count) -{ - *C = (HTTPHeadersCommon){0}; +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}; +} - uint32_t max_lines = 0; - for (uint32_t i = 0; i + 1 < len; i++){ - if (buf[i]=='\r' && buf[i+1]=='\n') max_lines++; - } +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; + if (len > p.max_header_bytes) return HTTP_PARSE_TOO_LARGE; + + *C = (HTTPHeadersCommon){0}; + *out_extra = NULL; + *out_extra_count = 0; HTTPHeader *extras = NULL; - if (max_lines){ - extras = (HTTPHeader*)(uintptr_t)malloc(sizeof(*extras) * max_lines); - if (!extras){ - *out_extra = NULL; - *out_extra_count = 0; - return; - } - } + uint32_t extra_cap = 0; + HTTPParseResult result = HTTP_PARSE_OK; uint32_t extra_i = 0; + uint32_t header_i = 0; uint32_t pos = 0; + bool seen_host = false; - 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 < 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 (result != HTTP_PARSE_OK) break; + + if (eol == pos) break; - if (eol == pos){ - pos += 2; + 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){ - pos = eol + 2; - continue; + if (sep == eol || sep == pos) { + result = HTTP_PARSE_BAD_FORMAT; + break; } 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++; + 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; + bool handled = false; + + if (key_len > p.max_header_key_len || val_len > p.max_header_value_len) { + result = HTTP_PARSE_TOO_LARGE; + break; + } - uint32_t val_len = eol - 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); + uint32_t parsed = 0; + if (ok) { + memcpy(len_buf, buf + val_start, val_len); + len_buf[val_len] = 0; + ok = parse_uint32_dec_exact(len_buf, &parsed); + } - 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 (!ok || (C->framing.has_content_length && C->fields.content_length != parsed)) { + C->bad_content_length = 1; + result = HTTP_PARSE_BAD_CONTENT_LENGTH; + } else { + C->framing.has_content_length = 1; + C->fields.content_length = parsed; + } + 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) { + 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; + 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.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) { + 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){ + 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 (copy_len == 14 && strcmp_case(key_tmp, "content-length", true) == 0){ - C->length = (uint32_t)parse_int_u64(buf + val_start, val_len); - } - 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 (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); + 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 (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 (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); @@ -169,100 +588,228 @@ void http_header_parser(const char *buf, uint32_t len, } } + if (!has_crlf) break; 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); + http_headers_common_free(C); + *out_extra = NULL; + *out_extra_count = 0; + return result; + } + 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; + return result; } - if (extra_i == max_lines){ + if (extra_i == p.max_header_count){ *out_extra = extras; *out_extra_count = extra_i; - return; + return result; } - 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; + 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); - } - free_sized(extras, sizeof(*extras) * max_lines); + http_headers_extra_free(extras, extra_i); *out_extra = NULL; *out_extra_count = 0; + return HTTP_PARSE_TOO_LARGE; +} + +static void http_append_chunked_body(string *out, uintptr_t ptr, uint32_t len) { + if (!out) return; + + if (len) { + 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); + } + + 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]); + 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.length || R->method == HTTP_METHOD_POST || R->method == HTTP_METHOD_PUT)) { + common.fields.content_length = R->body.length; + 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->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); - } + 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; } 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 = R->body.length; + common.framing.has_content_length = 1; + } + 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); + 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->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, (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; } 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; } -sizedptr http_get_payload(sizedptr header){ - if (!header.ptr || header.size < 4) return (sizedptr){0}; +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){0}; + dec->body = (string){0}; + dec->trailers_buf = (string){0}; +} - int start = find_crlfcrlf((char*)header.ptr, header.size); - if (start < 0) return (sizedptr){0}; +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; + + 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++; + } + string_free(dec->line); + 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; + 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; + } + } 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; + } + 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; + } + } 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; + } - return (sizedptr){ - header.ptr + (uint32_t)(start + 4), - header.size - (uint32_t)(start + 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 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); + } } - return (string){0}; + 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 06163a76..9a4616cb 100644 --- a/kernel/networking/application_layer/http.h +++ b/kernel/networking/application_layer/http.h @@ -7,47 +7,239 @@ 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 +#define HTTP_DEFAULT_MAX_KEEPALIVE_REQUESTS 16 +#define HTTP_DEFAULT_MAX_REDIRECTS 5 + typedef enum { HTTP_METHOD_GET, HTTP_METHOD_POST, HTTP_METHOD_PUT, - HTTP_METHOD_DELETE + HTTP_METHOD_DELETE, + HTTP_METHOD_HEAD, + HTTP_METHOD_OPTIONS, + HTTP_METHOD_UNKNOWN } 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, + 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_INCOMPLETE +} HTTPParseResult; + typedef enum { + HTTP_CONTINUE = 100, HTTP_OK = 200, + HTTP_PARTIAL_CONTENT = 206, + 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, HTTP_NOT_FOUND = 404, + HTTP_METHOD_NOT_ALLOWED = 405, + 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_DEBUG = 800, + HTTP_VERSION_NOT_SUPPORTED = 505, } 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 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; + 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; +} HTTPPolicy; + +typedef struct { + 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 { + HTTPServerPolicy value; + uint32_t flags; + uint32_t common_flags; +} HTTPServerPolicyOptions; + +typedef struct { + HTTPPolicy common; + uint32_t max_redirects; + bool follow_redirects; + bool allow_close_delimited; +} HTTPClientPolicy; + +typedef struct { + HTTPClientPolicy value; + uint32_t flags; + uint32_t common_flags; +} HTTPClientPolicyOptions; + +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; } HTTPHeader; typedef struct { - uint32_t length; - string type; - string date; + string content_type; string connection; - string keep_alive; string host; - string content_type; + 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 connection_close; + uint8_t connection_keep_alive; + 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; - sizedptr body; + string body; } HTTPRequestMsg; typedef struct { @@ -56,20 +248,54 @@ typedef struct { HTTPHeadersCommon headers_common; HTTPHeader *extra_headers; uint32_t extra_header_count; - sizedptr body; + string 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_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); +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); + uint32_t extra_count, + HTTPHeaderBuildKind kind, + HTTPMethod method, + uint32_t status_code); -void http_header_parser(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); +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); +void http_request_free(HTTPRequestMsg *req); +void http_response_free(HTTPResponseMsg *res); string http_request_builder(const HTTPRequestMsg *req); @@ -77,10 +303,6 @@ string http_response_builder(const HTTPResponseMsg *res); 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/http_webserver.c b/kernel/networking/application_layer/http_webserver.c new file mode 100644 index 00000000..e6c9fe2d --- /dev/null +++ b/kernel/networking/application_layer/http_webserver.c @@ -0,0 +1,337 @@ +#include "http_webserver.h" +#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" + +typedef struct { + const HTTPRoute *route; + const HTTPRoute *method_route; + uint32_t methods; +} HTTPWebRouteMatch; + +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 = url_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 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"; + 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; + + 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}; + } + + 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) { + 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 *web_file) { + if (!ctx || !web_file || !web_file->fs_path) return SOCK_ERR_INVAL; + + fs_stat st = {0}; + 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 || (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); + } + + 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->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); + } + 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); + } + } + + 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; +} + +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/application_layer/ntp.c b/kernel/networking/application_layer/ntp.c index 60406243..a89ce8d2 100644 --- a/kernel/networking/application_layer/ntp.c +++ b/kernel/networking/application_layer/ntp.c @@ -1,11 +1,13 @@ #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" #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" @@ -109,8 +111,8 @@ 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); - int64_t sent = socket_sendto_udp_ex(sock, DST_ENDPOINT, &dst, 0, &p, sizeof(p)); + 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; *tx_ntp64_be_out = tx_be; @@ -153,8 +155,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]; @@ -237,8 +238,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 = udp_socket_create(0, (uint32_t)get_current_proc_pid(), NULL); - if (sock == 0) return NTP_ERR_SOCKET; + 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; 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_ex(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..cb5f9efa 100644 --- a/kernel/networking/application_layer/ntp_daemon.c +++ b/kernel/networking/application_layer/ntp_daemon.c @@ -12,7 +12,6 @@ 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 @@ -27,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; @@ -43,8 +40,7 @@ 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); - 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 ffec242e..b27ffc7f 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" { @@ -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 9f17b3e9..277d21cf 100644 --- a/kernel/networking/application_layer/sntp.c +++ b/kernel/networking/application_layer/sntp.c @@ -1,11 +1,13 @@ #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" #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" @@ -50,8 +52,8 @@ 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); - int64_t sent = socket_sendto_udp_ex(sock, DST_ENDPOINT, &dst, 0, &p, sizeof(p)); + 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; return SNTP_OK; @@ -67,8 +69,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]; @@ -86,8 +87,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 = udp_socket_create(0, (uint32_t)get_current_proc_pid(), NULL); - if (sock == 0) return SNTP_ERR_SOCKET; + 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; @@ -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_ex(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..2172a1fe 100644 --- a/kernel/networking/application_layer/sntp_daemon.c +++ b/kernel/networking/application_layer/sntp_daemon.c @@ -14,7 +14,6 @@ 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) @@ -28,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; @@ -42,8 +39,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 = udp_socket_create(0, g_pid_sntp, NULL); - 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 8ea56ac1..589d7bba 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" { @@ -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 deleted file mode 100644 index be410252..00000000 --- a/kernel/networking/application_layer/socket_http_client.hpp +++ /dev/null @@ -1,226 +0,0 @@ -#pragma once -#include "console/kio.h" -#include "networking/transport_layer/socket_tcp.hpp" -#include "http.h" -#include "std/std.h" -#include "net/socket_types.h" - -class HTTPClient { -private: - uint16_t pid; - TCPSocket* sock; - SocketExtraOptions log_opts; - SocketExtraOptions* tcp_extra; - -public: - explicit HTTPClient(uint16_t pid_, const SocketExtraOptions* extra) : pid(pid_), sock(nullptr), log_opts{}, tcp_extra(nullptr) { - if (extra) log_opts = *extra; - - const SocketExtraOptions* tcp_ptr = extra; - if (extra && (log_opts.flags & SOCK_OPT_DEBUG)) { - tcp_extra = (SocketExtraOptions*)malloc(sizeof(SocketExtraOptions)); - if (tcp_extra) { - *tcp_extra = *extra; - tcp_extra->flags &= ~SOCK_OPT_DEBUG; - tcp_ptr = tcp_extra; - } - } - - sock = (TCPSocket*)malloc(sizeof(TCPSocket)); - if (sock) new (sock) TCPSocket(SOCK_ROLE_CLIENT, pid, tcp_ptr); - } - - ~HTTPClient() {close();} - - 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; - - netlog_socket_event_t ev{}; - 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; - ev.i0 = r; - - if (sock) { - ev.local_port = sock->get_local_port(); - ev.remote_ep = sock->get_remote_ep(); - if (ev.remote_ep.ver) ev.dst_ep = ev.remote_ep; - } - - netlog_socket_event(&log_opts, &ev); - return r; - } - - HTTPResponseMsg send_request(const HTTPRequestMsg& req) { - 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; - 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 (sent < 0) { - resp.status_code = (HttpError)sent; - return resp; - } - - string buf = string_repeat('\0', 0); - char tmp[512]; - int hdr_end = -1; - - while (hdr_end < 0) { - int64_t r = sock->recv(tmp, sizeof(tmp)); - if (r == TCP_WOULDBLOCK) { - 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; - } - string_append_bytes(&buf, tmp, (uint32_t)r); - 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; - } - resp.status_code = (HttpError)code; - while (j < (uint32_t)hdr_end && buf.data[j]==' ') ++j; - if (j < (uint32_t)hdr_end) { - uint32_t rlen = hdr_end - j; - resp.reason = string_repeat('\0', 0); - string_append_bytes(&resp.reason, buf.data+j, rlen); - } - - HTTPHeader *extras = nullptr; - uint32_t extra_count = 0; - int status_line_end = strindex((char*)buf.data, "\r\n"); - http_header_parser( - (char*)buf.data + status_line_end + 2, - buf.length - (uint32_t)(status_line_end + 2), - &resp.headers_common, - &extras, - &extra_count); - resp.extra_headers = extras; - resp.extra_header_count = extra_count; - - 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 (have > 0) { - char *body_copy = (char*)malloc(have); - if (body_copy) { - memcpy(body_copy, buf.data + body_start, have); - resp.body.ptr = (uintptr_t)body_copy; - resp.body.size = have; - } - } - - 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); - return resp; - } - - 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(); - } - - netlog_socket_event(&log_opts, &ev); - - if (sock) sock->~TCPSocket(); - if (sock) free_sized(sock, sizeof(TCPSocket)); - sock = nullptr; - - if (tcp_extra) free_sized(tcp_extra, sizeof(SocketExtraOptions)); - tcp_extra = nullptr; - - 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 9e888288..00000000 --- a/kernel/networking/application_layer/socket_http_server.hpp +++ /dev/null @@ -1,270 +0,0 @@ -#pragma once -#include "console/kio.h" -#include "networking/transport_layer/socket_tcp.hpp" -#include "http.h" -#include "std/std.h" -#include "net/socket_types.h" - - -class HTTPServer { -private: - uint16_t pid; - TCPSocket* sock; - SocketExtraOptions log_opts; - SocketExtraOptions* tcp_extra; - -public: - explicit HTTPServer(uint16_t pid_, const SocketExtraOptions* extra) : pid(pid_), sock(nullptr), log_opts{}, tcp_extra(nullptr) { - if (extra) log_opts = *extra; - - const SocketExtraOptions* tcp_ptr = extra; - if (extra && (log_opts.flags & SOCK_OPT_DEBUG)) { - tcp_extra = (SocketExtraOptions*)malloc(sizeof(SocketExtraOptions)); - if (tcp_extra) { - *tcp_extra = *extra; - tcp_extra->flags &= ~SOCK_OPT_DEBUG; - tcp_ptr = tcp_extra; - } - } - - sock = (TCPSocket*)malloc(sizeof(TCPSocket)); - if (sock) new (sock) TCPSocket(SOCK_ROLE_SERVER, pid, tcp_ptr); - } - - ~HTTPServer() { close(); } - - int32_t bind(const SockBindSpec& spec, uint16_t port) { - uint16_t p = port; - int32_t r = sock ? sock->bind(spec, p) : SOCK_ERR_STATE; - - netlog_socket_event_t ev{}; - ev.comp = NETLOG_COMP_HTTP_SERVER; - ev.action = NETLOG_ACT_BIND; - ev.pid = 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 ? sock->listen(b) : SOCK_ERR_STATE; - - netlog_socket_event_t ev{}; - ev.comp = NETLOG_COMP_HTTP_SERVER; - ev.action = NETLOG_ACT_LISTEN; - ev.pid = pid; - ev.u0 = (uint32_t)b; - ev.i0 = r; - netlog_socket_event(&log_opts, &ev); - return r; - } - - TCPSocket* accept() { - TCPSocket* c = sock ? sock->accept() : nullptr; - 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); - } - return c; - } - - HTTPRequestMsg recv_request(TCPSocket* client) { - HTTPRequestMsg req{}; - if (!client) return req; - - string buf = string_repeat('\0', 0); - char tmp[512]; - int hdr_end = -1; - - while (hdr_end < 0) { - int64_t r = client->recv(tmp, sizeof(tmp)); - if (r == TCP_WOULDBLOCK) { - msleep(10); - continue; - } - if (r <= 0) { - string_free(buf); - return req; - } - string_append_bytes(&buf, tmp, (uint32_t)r); - 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; - } - - uint32_t p = 0; - while (p + 1u < line_end && buf.data[p] == '\r' && buf.data[p + 1u] == '\n') - p += 2; - - 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 req.method = HTTP_METHOD_GET; - - uint32_t j = (i < line_end) ? (i + 1u) : line_end; - uint32_t path_start = j; - while (j < line_end && buf.data[j] != ' ') ++j; - req.path = string_repeat('\0', 0); - string_append_bytes(&req.path, buf.data + path_start, j - path_start); - - 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; - } - } 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; - } - } - - 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.length; - - if (need > 0) { - while (have < need) { - int64_t r = client->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 (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; - } - } - - netlog_socket_event_t ev{}; - ev.comp = NETLOG_COMP_HTTP_SERVER; - ev.action = NETLOG_ACT_HTTP_RECV_REQUEST; - ev.pid = pid; - 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(); - - 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(buf); - return req; - } - - int32_t send_response(TCPSocket* client, const HTTPResponseMsg& res) { - if (!client) return SOCK_ERR_STATE; - uint32_t code = (uint32_t)res.status_code; - string out = http_response_builder(&res); - uint32_t out_len = out.length; - uint32_t off = 0; - 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); - 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_SERVER; - ev.action = NETLOG_ACT_HTTP_SEND_RESPONSE; - ev.pid = pid; - ev.u0 = code; - ev.u1 = out_len; - ev.i0 = sent; - ev.local_port = client->get_local_port(); - ev.remote_ep = client->get_remote_ep(); - netlog_socket_event(&log_opts, &ev); - - string_free(out); - 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 = pid; - ev.i0 = r; - if (sock) { - ev.local_port = sock->get_local_port(); - ev.remote_ep = sock->get_remote_ep(); - } - netlog_socket_event(&log_opts, &ev); - - if (sock) sock->~TCPSocket(); - if (sock) free_sized(sock, sizeof(TCPSocket)); - sock = nullptr; - - if (tcp_extra) free_sized(tcp_extra, sizeof(SocketExtraOptions)); - 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.c b/kernel/networking/application_layer/ssdp.c index 96fa265b..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; @@ -61,9 +70,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/application_layer/ssdp_daemon.c b/kernel/networking/application_layer/ssdp_daemon.c index 8771dcd5..c8c9593d 100644 --- a/kernel/networking/application_layer/ssdp_daemon.c +++ b/kernel/networking/application_layer/ssdp_daemon.c @@ -5,11 +5,15 @@ #include "std/string.h" #include "syscalls/syscalls.h" #include "net/network_types.h" -#include "networking/transport_layer/csocket_udp.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 "networking/transport_layer/trans_utils.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 @@ -17,30 +21,41 @@ 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 #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; 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; @@ -48,22 +63,16 @@ 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) { - 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); - 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) { - 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_ex(s6, DST_ENDPOINT, &dst, 0, msg.data, msg.length); + string msg = ssdp_build_notify(alive, ssdp_sockets[i].ver == IP_VER6); + net_l4_endpoint dst; + 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); } } @@ -71,47 +80,83 @@ static void ssdp_send_notify(socket_handle_t s4, socket_handle_t s6, 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); - SocketExtraOptions opt4 = (SocketExtraOptions){0}; - opt4.flags = SOCK_OPT_MCAST_JOIN; - opt4.mcast_ver = IP_VER4; - memcpy(opt4.mcast_group, &ssdp_host_v4, 4); + 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 (!ipv4_l3_is_ready(v4) || v4->is_localhost) continue; + + socket_handle_t s = create_socket(PROTO_UDP, &(SocketOptions){.flags = SOCK_OPT_TTL | SOCK_OPT_NONBLOCK, .ttl = 2}); + 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; + } - SocketExtraOptions opt6 = (SocketExtraOptions){0}; - opt6.flags = SOCK_OPT_MCAST_JOIN; - opt6.mcast_ver = IP_VER6; - memcpy(opt6.mcast_group, ssdp_host_v6, 16); + 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++; + } - uint16_t pid = get_current_proc_pid(); + 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 (!ipv6_l3_is_ready(v6) || v6->is_localhost) continue; - socket_handle_t s4 = udp_socket_create(SOCK_ROLE_SERVER, pid, &opt4); - socket_handle_t s6 = udp_socket_create(SOCK_ROLE_SERVER, pid, &opt6); + socket_handle_t s = create_socket(PROTO_UDP, &(SocketOptions){.flags = SOCK_OPT_TTL | SOCK_OPT_NONBLOCK, .ttl = 2}); + if (!s) continue; - struct SockBindSpec spec = (struct SockBindSpec){0}; - spec.kind = BIND_ANY; - if (s4 && socket_bind_udp_ex(s4, &spec, 1900) < 0) { - socket_close_udp(s4); - socket_destroy_udp(s4); - s4 = 0; - } - if (s6 && socket_bind_udp_ex(s6, &spec, 1900) < 0) { - socket_close_udp(s6); - socket_destroy_udp(s6); - s6 = 0; + 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 && !s6) 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; @@ -120,25 +165,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 (s4) { - int64_t r4 = socket_recvfrom_udp_ex(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_ex(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)); + 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 == SOCK_ERR_WOULDBLOCK) break; + if (r < 0) break; + if (!r) continue; + buf[r] = 0; + if (ssdp_is_msearch(buf, (int)r)) ssdp_schedule_response(s, &src, ssdp_parse_mx_ms(buf, (int)r)); } } @@ -158,8 +199,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) (void)socket_sendto_udp_ex(sock, DST_ENDPOINT, &ssdp_pending[i].dst, 0, 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/drivers/loopback/loopback_driver.cpp b/kernel/networking/drivers/loopback/loopback_driver.cpp index 7314e651..0495d402 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,38 +13,26 @@ 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::handle_sent_packet(){} +void LoopbackDriver::enable_verbose(){} -void LoopbackDriver::enable_verbose(){ verbose = true; } - -bool LoopbackDriver::send_packet(sizedptr packet){ - if (!packet.ptr || !packet.size) 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 c110301b..5e7076a8 100644 --- a/kernel/networking/drivers/loopback/loopback_driver.hpp +++ b/kernel/networking/drivers/loopback/loopback_driver.hpp @@ -9,11 +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; - void handle_sent_packet() override; + netpkt_t* handle_receive_packet() override; void enable_verbose() override; - bool send_packet(sizedptr 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; @@ -22,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_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/drivers/net_driver.hpp b/kernel/networking/drivers/net_driver.hpp index 2d083da1..1a206d41 100644 --- a/kernel/networking/drivers/net_driver.hpp +++ b/kernel/networking/drivers/net_driver.hpp @@ -1,17 +1,25 @@ #pragma once #include "types.h" #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; 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 void handle_sent_packet() = 0; + virtual netpkt_t* handle_receive_packet() = 0; + virtual void handle_sent_packet() {} + virtual void complete_rx_batch(){} + virtual void complete_tx_batch(){} virtual void enable_verbose() = 0; - virtual bool send_packet(sizedptr 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 24b2cee9..4ce06969 100644 --- a/kernel/networking/drivers/virtio_net_pci/virtio_net_pci.cpp +++ b/kernel/networking/drivers/virtio_net_pci/virtio_net_pci.cpp @@ -12,10 +12,7 @@ #define TRANSMIT_QUEUE 1 #define CONTROL_QUEUE 2 -constexpr uint32_t RX_BUF_SIZE = PAGE_SIZE; -constexpr uint16_t RX_CHAIN_SEGS = 4; - -void* g_rx_pool = nullptr; +constexpr uint32_t RX_BUF_SIZE = 2048; #define kprintfv(fmt, ...) \ ({ \ @@ -42,6 +39,25 @@ 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) { + 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); + 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; @@ -84,7 +100,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); @@ -93,10 +108,20 @@ 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; rx_qsz = 0; + rx_pool = nullptr; + rx_notify_pending = false; + tx_desc = nullptr; + tx_avail = nullptr; + tx_used = nullptr; + tx_qsz = 0; + tx_avail_shadow_idx = 0; + tx_pending = nullptr; + tx_free_head = UINT16_MAX; memset(&vnp_net_dev, 0, sizeof(vnp_net_dev)); kprintfv("[virtio-net] probing pci_addr=%x",(uintptr_t)addr); @@ -128,9 +153,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)){ @@ -139,8 +165,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; @@ -160,37 +190,25 @@ 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; - } - - 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; + //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; - for (uint16_t c = 0; c < chain_count; c++) { - uint16_t head = (uint16_t)(c * RX_CHAIN_SEGS); - - 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); + 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; + rx_desc[di].next = 0; - 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[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++; } @@ -200,11 +218,30 @@ 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; + 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 = (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); + //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_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; kprintfv("[virtio-net] TX vector=%u",vnp_net_dev.common_cfg->queue_msix_vector); @@ -215,7 +252,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; @@ -224,15 +260,21 @@ 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; + } + uint16_t rx_mtu_cap = (uint16_t)(RX_BUF_SIZE - header_size - 14); + if (mtu > rx_mtu_cap) mtu = rx_mtu_cap; - 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; @@ -249,7 +291,10 @@ bool VirtioNetDriver::init_at(uint64_t addr, uint32_t irq_base_vector) { } vnp_net_dev.common_cfg->device_status |= VIRTIO_STATUS_DRIVER_OK; - select_queue(&vnp_net_dev, RECEIVE_QUEUE); + asm volatile ("dsb sy" ::: "memory"); + + virtio_notify_queue(&vnp_net_dev, RECEIVE_QUEUE); + if (ctrl_vq && ctrl_rx) (void)sync_multicast((const uint8_t*)0, 0); return true; } @@ -282,150 +327,121 @@ 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}; -} +netpkt_t* VirtioNetDriver::handle_receive_packet(){ + if (!rx_qsz || !rx_desc || !rx_used || !rx_avail || !rx_pool) return nullptr; -sizedptr VirtioNetDriver::handle_receive_packet(){ - uint32_t desc_index = 0; - uint32_t total_len = 0; - uint16_t num_buffers = 1; - - disable_interrupt(); - 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(); - return (sizedptr){0,0}; + 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; } - asm volatile ("dmb ishld" ::: "memory"); - uint16_t new_idx = used->idx; - if (new_idx == last_used_receive_idx) { - enable_interrupt(); - 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); - 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); - enable_interrupt(); - 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); - enable_interrupt(); - 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) { + virtio_net_rx_free(this, buf); + return nullptr; } - enable_interrupt(); - 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(); - 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); - enable_interrupt(); - return (sizedptr){0,0}; + 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); + return nullptr; } - 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; - } - } + return pkt; +} - disable_interrupt(); - uint16_t aidx = avail->idx; - avail->ring[aidx % qsz] = (uint16_t)desc_index; +void VirtioNetDriver::complete_rx_batch() { + irq_flags_t flags = irq_save_disable(); + bool pending = rx_notify_pending; + rx_notify_pending = false; + irq_restore(flags); + if (!pending) return; 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); - enable_interrupt(); - - if (remaining != 0) { - kfree(out_buf, payload_len); - return (sizedptr){0,0}; - } - - return (sizedptr){ (uintptr_t)out_buf, payload_len }; + virtio_notify_queue(&vnp_net_dev, RECEIVE_QUEUE); } 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; + + uint16_t used_end = tx_used->idx; + asm volatile ("dmb ishld" ::: "memory"); + + while (last_used_sent_idx != used_end) { + uint16_t ring_index = (uint16_t)(last_used_sent_idx % tx_qsz); + uint32_t used_id = tx_used->ring[ring_index].id; + last_used_sent_idx++; + + 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 = tx_free_head; + tx_free_head = desc_index; + + netpkt_unref(packet); + } } -bool VirtioNetDriver::send_packet(sizedptr packet){ - if (!packet.ptr || !packet.size) 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; - disable_interrupt(); - select_queue(&vnp_net_dev, TRANSMIT_QUEUE); + if (tx_free_head == UINT16_MAX) { + kprintfv("[virtio-net] tx queue full len=%u", (unsigned)netpkt_len(packet)); + return NETDEV_TX_BUSY; + } - if ((size_t)header_size <= packet.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; + 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) { + 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; + 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; + + uint16_t avail_idx = tx_avail_shadow_idx; + tx_avail->ring[avail_idx % tx_qsz] = desc_index; + 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() { + 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) { @@ -433,8 +449,6 @@ bool VirtioNetDriver::sync_multicast(const uint8_t* macs, uint32_t count) { if (!ctrl_rx) return true; if (!macs && count) return false; - disable_interrupt(); - bool ok = true; uint8_t v0 = 0; @@ -449,7 +463,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) { - enable_interrupt(); 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 +475,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); - enable_interrupt(); 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..26c9bbba 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]; @@ -56,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; @@ -67,21 +63,33 @@ 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 complete_rx_batch() override; + void complete_tx_batch() override; + netdev_tx_result_t send_packet(netpkt_t* packet) override; private: + friend void virtio_net_rx_free(void* ctx, uintptr_t base); 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; + uint16_t rx_used_batch_end = 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_avail_shadow_idx = 0; + netpkt_t** tx_pending = nullptr; + uint16_t tx_free_head = UINT16_MAX; bool verbose = false; - bool mrg_rxbuf = false; bool ctrl_vq = false; bool ctrl_rx = false; diff --git a/kernel/networking/interface_manager.c b/kernel/networking/interface_manager.c index 3858c095..c537fca3 100644 --- a/kernel/networking/interface_manager.c +++ b/kernel/networking/interface_manager.c @@ -1,10 +1,11 @@ #include "interface_manager.h" #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" -#include "networking/port_manager.h" #include "process/scheduler.h" #include "memory/page_allocator.h" #include "networking/internet_layer/ipv4_utils.h" @@ -13,14 +14,12 @@ #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]; 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,11 +32,14 @@ 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[MAX_IPV4_L3_INTERFACES]; +static v6_slot_t g_v6[MAX_IPV6_L3_INTERFACES]; -static v4_slot_t g_v4[V4_POOL_SIZE]; -static v6_slot_t g_v6[V6_POOL_SIZE]; +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; @@ -48,7 +50,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; @@ -91,15 +93,9 @@ 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(); - 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; @@ -135,6 +131,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; } @@ -159,43 +156,64 @@ 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; +} + +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]; + 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++; } } @@ -212,9 +230,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 +249,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 +272,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,56 +290,60 @@ 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; } static bool v4_ip_exists_anywhere(uint32_t ip){ - for (int i=0;il2 || x->l2->ifindex != ifindex) continue; if (x->mode == IPV4_CFG_DISABLED) continue; uint32_t m = (x->mask==0)?mask:((mask==0)?x->mask:((x->mask < mask)?x->mask:mask)); - if (ipv4_net(ip, m) == ipv4_net(x->ip, m)) return true; + 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; } static bool v6_ip_exists_anywhere(const uint8_t ip[16]){ if (ipv6_is_unspecified(ip)) return false; - for (int i=0;il2 || x->l2->ifindex != ifindex) continue; if (x->cfg == IPV6_CFG_DISABLE) continue; if (ipv6_is_unspecified(x->ip)) continue; uint8_t minp = (x->prefix_len < prefix_len) ? x->prefix_len : prefix_len; - int eq = 1; - int fb = minp/8, rb = minp%8; - for (int b=0;bip[b]) {eq=0;break;} } - if (eq && rb){ - uint8_t m=(uint8_t)(0xFF<<(8-rb)); - if ( (ip[fb]&m) != (x->ip[fb]&m) ) eq=0; - } - if (eq) return true; + if (ipv6_common_prefix_len(ip, x->ip) >= minp) return true; } return false; } @@ -327,6 +368,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; @@ -336,7 +378,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;iruntime_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])); @@ -368,29 +412,13 @@ 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++; - 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]); return n->l3_id; } @@ -414,15 +442,28 @@ bool l3_ipv4_update(uint8_t l3_id, uint32_t ip, uint32_t mask, uint32_t gw, ipv4 if (ipv4_is_network_address(ip, mask)) return false; if (ipv4_is_broadcast_address(ip, mask)) return false; if (ip != n->ip && v4_ip_exists_anywhere(ip)) return false; - for (int i = 0; i < V4_POOL_SIZE; i++){ + 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; 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; + 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; @@ -443,14 +484,26 @@ 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); 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]); + + if (l3_changed) n->epoch = net_interface_mark_changed(); return true; } @@ -462,20 +515,17 @@ bool l3_ipv4_remove_from_interface(uint8_t l3_id){ if (l2->ipv4_count <= 1) return false; int g = -1; - for (int i=0;iport_manager) { - kfree(n->port_manager, sizeof(port_manager_t)); - n->port_manager = NULL; - } uint8_t slot = l3_slot_from_id(l3_id); if (slot < MAX_IPV4_PER_INTERFACE && l2->l3_v4[slot] == n){ l2->l3_v4[slot] = NULL; if (l2->ipv4_count) l2->ipv4_count--; + net_interface_mark_changed(); } if (n->routing_table) { @@ -498,13 +548,15 @@ l3_ipv4_interface_t* l3_ipv4_find_by_id(uint8_t l3_id){ return l2->l3_v4[loc]; } l3_ipv4_interface_t* l3_ipv4_find_by_ip(uint32_t ip){ - for (int i=0;iipv6_mcast_count; if (prefix_len > 128) return 0; int placeholder_ll = 0; @@ -532,7 +584,7 @@ uint8_t l3_ipv6_add_to_interface(uint8_t ifindex, const uint8_t ip[16], uint8_t if (!ipv6_is_linklocal(ip)) return 0; } if (!ipv6_is_unspecified(ip) && !placeholder_ll && v6_ip_exists_anywhere(ip)) return 0; - for (int i=0;iifindex != ifindex) continue; if (ipv6_is_linklocal(g_v6[i].node.ip) && g_v6[i].node.cfg != IPV6_CFG_DISABLE) return 0; @@ -550,13 +602,13 @@ 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; } } } if (!is_loop){ bool has_lla=false; - for (int i=0;il2 || x->l2->ifindex != ifindex) continue; @@ -577,7 +629,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;il3_id = make_l3_id_v6(l2->ifindex, (uint8_t)loc); + n->epoch = net_interface_mark_changed(); 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){ + 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); } 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); } @@ -662,6 +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 (!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; } @@ -671,12 +713,15 @@ 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; + 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;il2 || x->l2->ifindex != l2->ifindex) continue; @@ -689,7 +734,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 || 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; @@ -729,6 +757,7 @@ bool l3_ipv6_update(uint8_t l3_id, const uint8_t ip[16], uint8_t prefix_len, con uint8_t old_ip[16]; ipv6_cpy(old_ip, n->ip); + uint8_t old_prefix_len = n->prefix_len; n->cfg = cfg; n->kind = kind; @@ -758,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); } @@ -796,8 +825,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 { @@ -806,7 +844,9 @@ bool l3_ipv6_update(uint8_t l3_id, const uint8_t ip[16], uint8_t prefix_len, con n->routing_table = NULL; } } + 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; } @@ -816,7 +856,7 @@ bool l3_ipv6_remove_from_interface(uint8_t l3_id){ l2_interface_t *l2 = n->l2; if (!l2) return false; if ((n->kind & IPV6_ADDRK_LINK_LOCAL)){ - for (int i=0;il2 || x->l2->ifindex != l2->ifindex) continue; @@ -826,20 +866,17 @@ bool l3_ipv6_remove_from_interface(uint8_t l3_id){ if (l2->ipv6_count <= 1) return false; int g = -1; - for (int i=0;iport_manager) { - kfree(n->port_manager, sizeof(port_manager_t)); - n->port_manager = NULL; - } uint8_t slot = l3_slot_from_id(l3_id); if (slot < MAX_IPV6_PER_INTERFACE && l2->l3_v6[slot] == n){ l2->l3_v6[slot] = NULL; if (l2->ipv6_count) l2->ipv6_count--; + net_interface_mark_changed(); } if (n->routing_table){ @@ -860,7 +897,7 @@ bool l3_ipv6_set_enabled(uint8_t l3_id, bool enable){ } else { if ((n->kind & IPV6_ADDRK_LINK_LOCAL)){ l2_interface_t *l2 = n->l2; - for (int i=0;il2 || x->l2->ifindex != l2->ifindex) continue; @@ -871,6 +908,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; } } @@ -885,7 +923,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;iifindex, 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); @@ -949,13 +986,13 @@ void ifmgr_autoconfig_l2(uint8_t ifindex){ bool has_lla=false; bool has_gua=false; - for (int i=0;il2 || 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; @@ -966,22 +1003,17 @@ 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); - (void)l2_ipv6_mcast_join(ifindex, m); } 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_STATELESS, IPV6_ADDRK_GLOBAL); } } @@ -994,23 +1026,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; } @@ -1021,61 +1056,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 < V6_POOL_SIZE; 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/interface_manager.h b/kernel/networking/interface_manager.h index 7786b16d..5d265806 100644 --- a/kernel/networking/interface_manager.h +++ b/kernel/networking/interface_manager.h @@ -1,42 +1,20 @@ #pragma once #include "types.h" -#include "networking/port_manager.h" +#include "net/interface_types.h" #ifdef __cplusplus 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 - -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; +#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 struct l2_interface; struct l3_ipv4_interface; @@ -68,13 +46,16 @@ 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; typedef struct l3_ipv4_interface { uint8_t l3_id; + uint32_t epoch; uint32_t ip; uint32_t mask; uint32_t gw; @@ -83,7 +64,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; @@ -110,6 +90,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]; @@ -128,7 +109,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; @@ -155,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); @@ -183,15 +164,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/icmp.c b/kernel/networking/internet_layer/icmp.c index fc4a91fc..78eb9b62 100644 --- a/kernel/networking/internet_layer/icmp.c +++ b/kernel/networking/internet_layer/icmp.c @@ -1,207 +1,58 @@ #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 "syscalls/syscalls.h" +#include "networking/transport_layer/csocket_raw.h" +#include "networking/transport_layer/trans_utils.h" -#define MAX_PENDING 16 +void icmp_input(uint8_t ifindex, netpkt_t* pkt, uint32_t src_ip, uint32_t dst_ip) { + if (!pkt) return; -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; -} 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].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; - 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) { - 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; - 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; - uintptr_t buf = (uintptr_t)malloc(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 = checksum16((uint16_t*)pkt, (len+1)/2); - 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) { - 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; - } - - 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; - } - - 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); - g_pending[slot].in_use = false; - return false; - } - void* p = netpkt_put(pkt, tot_len); - if (!p) { + uint32_t len = netpkt_len(pkt); + if (len < 8) { netpkt_unref(pkt); - free_sized((void*)buf, 8 + 56); - g_pending[slot].in_use = false; - return false; + return; } - memcpy(p, (const void*)buf, tot_len); - free_sized((void*)buf, 8 + 56); - 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(); - 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; - } - bool ok = (g_pending[slot].rx_type == ICMP_ECHO_REPLY); - g_pending[slot].in_use = false; - return ok; - } - - uint32_t now = (uint32_t)get_time(); - if (now - start >= timeout_ms) break; - msleep(5); + 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; } - - if (out) { - out->rtt_ms = 0; - out->status = PING_TIMEOUT; - out->icmp_type = 0xFF; - out->icmp_code = 0xFF; - out->responder_ip = 0; + if (checksum16(raw, len) != 0) { + netpkt_unref(pkt); + return; } - g_pending[slot].in_use = false; - return false; -} - -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); + socket_raw_input_v4(PROTO_ICMP, ifindex, src_ip, dst_ip, pkt); + uint8_t type = hdr[0]; + 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; 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); + rp->checksum = bswap16(checksum16(rp, rlen)); 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) { @@ -214,34 +65,20 @@ 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; } 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; - 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; - 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; } + + netpkt_unref(pkt); } diff --git a/kernel/networking/internet_layer/icmp.h b/kernel/networking/internet_layer/icmp.h index 08ff1223..0ec36e02 100644 --- a/kernel/networking/internet_layer/icmp.h +++ b/kernel/networking/internet_layer/icmp.h @@ -1,33 +1,20 @@ #pragma once #include "types.h" #include "net/network_types.h" +#include "networking/netpkt.h" #ifdef __cplusplus 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 { - 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; + 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 struct __attribute__((packed)) { uint8_t type; @@ -38,17 +25,7 @@ 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); -void icmp_input(uintptr_t ptr, uint32_t len, 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 4b1ca415..0ea021a2 100644 --- a/kernel/networking/internet_layer/icmpv6.c +++ b/kernel/networking/internet_layer/icmpv6.c @@ -7,9 +7,8 @@ #include "networking/link_layer/eth.h" #include "networking/link_layer/ndp.h" #include "networking/internet_layer/mld.h" -#include "syscalls/syscalls.h" - -#define MAX_PENDING 16 +#include "networking/transport_layer/csocket_raw.h" +#include "networking/transport_layer/trans_utils.h" typedef struct __attribute__((packed)) { icmpv6_hdr_t hdr; @@ -17,51 +16,6 @@ typedef struct __attribute__((packed)) { uint16_t seq; } icmpv6_echo_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; - uint8_t rx_src_ip[16]; -} 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].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); - 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) { - 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); - return; - } - } -} - 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; @@ -74,14 +28,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 = PROTO_ICMPV6; + ip6.hop_limit = hop_limit ? hop_limit : 64; + 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); return eth_send_frame_on(ifindex, ETHERTYPE_IPV6, dst_mac, pkt); @@ -90,7 +45,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); @@ -101,214 +56,92 @@ 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)) { - free_sized((void*)buf, icmp_len); + if (!ipv6_build_tx_plan(dst_ip, 0, &plan)) { + release((void*)buf); 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); - free_sized((void*)buf, icmp_len); + release((void*)buf); 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 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; +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; } - 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); - - 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)) { + 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 false; - } - e->hdr.checksum = bswap16(checksum16_pipv6(plan.src_ip, dst_ip, 58, (const uint8_t*)buf, len)); - - ipv6_send_packet(dst_ip, 58, pkt, (const ipv6_tx_opts_t*)tx_opts_or_null, hop_limit ? hop_limit : 64, 0); - 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) { - 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; } - - 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; + const icmpv6_hdr_t *h = &hdr; + if (h->code != 0 && (h->type == ICMPV6_ECHO_REQUEST || h->type == ICMPV6_ECHO_REPLY)) { + netpkt_unref(pkt); + return; } - 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; - 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; - } - 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; - } - - bool ok = (g_pending[slot].rx_type == ICMPV6_ECHO_REPLY); - g_pending[slot].in_use = false; - return ok; - } - - uint32_t now = (uint32_t)get_time(); - if (now - start >= timeout_ms) break; - msleep(5); + uint16_t calc = bswap16(checksum16_pipv6(src_ip, dst_ip, PROTO_ICMPV6, icmp, icmp_len)); + if (calc != 0) { + netpkt_unref(pkt); + return; } - if (out) { - out->rtt_ms = 0; - out->status = PING_TIMEOUT; - out->icmp_type = 0xFF; - out->icmp_code = 0xFF; - memset(out->responder_ip, 0, 16); + if ((h->type == 133 || h->type == 134 || h->type == 135 || h->type == 136 || h->type == 137) && hop_limit != 255) { + netpkt_unref(pkt); + return; } - g_pending[slot].in_use = false; - 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 - 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); - if ((v >>28) != 6) return false; - if (inner->next_header != 58) return false; - - const uint8_t *inner_icmp = (const uint8_t*)(inner + 1); - 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; - - 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], const uint8_t *icmp, uint32_t icmp_len) { - if (!ifindex || !src_ip || !dst_ip || !icmp || icmp_len < sizeof(icmpv6_hdr_t)) 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; - - uint16_t calc = bswap16(checksum16_pipv6(src_ip, dst_ip, 58, icmp, icmp_len)); - if (calc != 0) return; - - if ((h->type == 133 || h->type == 134 || h->type == 135 || h->type == 136 || h->type == 137) && hop_limit != 255) 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, 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; - const icmpv6_echo_t *e = (const icmpv6_echo_t*)icmp; - 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; } 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); - - 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; } + + 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..2caaac73 100644 --- a/kernel/networking/internet_layer/icmpv6.h +++ b/kernel/networking/internet_layer/icmpv6.h @@ -2,18 +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; @@ -21,17 +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], const uint8_t *icmp, uint32_t icmp_len); -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); +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_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 42833c45..4360494e 100644 --- a/kernel/networking/internet_layer/igmp.c +++ b/kernel/networking/internet_layer/igmp.c @@ -3,8 +3,10 @@ #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" #include "std/memory.h" #include "std/string.h" #include "syscalls/syscalls.h" @@ -40,33 +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* h = (igmp_hdr_t*)netpkt_put(pkt, sizeof(igmp_hdr_t)); - if (!h) { + igmp_hdr_t* igmp = (igmp_hdr_t*)netpkt_put(pkt, sizeof(igmp_hdr_t)); + if (!igmp) { netpkt_unref(pkt); return false; } - h->type = type; - h->max_resp_time = 0; - h->group = bswap32(group); - h->checksum = 0; - h->checksum = checksum16((const uint16_t*)h, sizeof(igmp_hdr_t)/2); + igmp->type = type; + igmp->max_resp_time = 0; + igmp->group = bswap32(group); + igmp->checksum = 0; + igmp->checksum = bswap16(checksum16(igmp, sizeof(*igmp))); - ipv4_tx_opts_t tx; + 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; @@ -77,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; @@ -92,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; @@ -109,9 +110,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; } @@ -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) { @@ -196,42 +195,65 @@ 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 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; +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; + } + if (checksum16(p, sizeof(igmp_hdr_t)) != 0) { + netpkt_unref(pkt); + return; + } - uint8_t type = h->type; - uint32_t group = bswap32(h->group); + socket_raw_input_v4(PROTO_IGMP, ifindex, src, dst, pkt); - uint32_t max_resp_ds = (uint32_t)h->max_resp_time; + uint8_t type = hdr[0]; + uint32_t group = rd_be32(hdr + 4); - if (type != IGMP_TYPE_QUERY) return; + uint32_t max_resp_ds = (uint32_t)hdr[1]; + + 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 a8e7edf5..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" @@ -12,369 +13,201 @@ #include "ipv4_utils.h" #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; -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_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; + if (ipv4_is_limited_broadcast(dst_ip)) { + if (!opts || opts->scope != IP_TX_BOUND_L3) { + netpkt_unref(pkt); + 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; + src_v4 = l3_ipv4_find_by_id(opts->index); + if (!ipv4_l3_is_active(src_v4)) { + netpkt_unref(pkt); + return false; + } - 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; -} + ifx = src_v4->l2->ifindex; + src_ip = src_v4->ip; + nh = IPV4_LIMITED_BROADCAST; + } else { + ipv4_tx_plan_t plan; + if (!ipv4_build_tx_plan(dst_ip, opts, &plan)) { + netpkt_unref(pkt); + return false; + } -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; } + src_v4 = l3_ipv4_find_by_id(plan.l3_id); + if (!ipv4_l3_is_ready(src_v4)) { + netpkt_unref(pkt); + return false; } - } - 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; + ifx = src_v4->l2->ifindex; + src_ip = plan.src_ip; - 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; -} + uint32_t route_nh = 0; + bool have_nh = false; -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); - - 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 (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 (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 (!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 (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; - } + if (!have_nh && src_v4->gw) { + nh = src_v4->gw; + have_nh = 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; + if (!have_nh) { + 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 pick_broadcast_bound_l2(ifindex, out_ifx, out_src, out_nh); + l2_interface_t* l2 = src_v4->l2; + 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; - l2_interface_t* l2 = l2_interface_find_by_index(ifindex); - if (!l2) return false; + 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) mac_clear(dst_mac); + else need_arp = true; - 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; + 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; - 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; + if (total <= (uint32_t)mtu) { + void* hdrp = netpkt_push(pkt, hdr_len); + if (!hdrp) { + 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); + ipv4_hdr_t ip; + 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++); + 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 = 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); } - return pick_route_global(dst, out_ifx, out_src, out_nh); -} - -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) { - if (!pkt || !netpkt_len(pkt)) { - if (pkt) netpkt_unref(pkt); - return; + if (dontfrag || (uint32_t)mtu < hdr_len + 8) { + netpkt_unref(pkt); + return false; } - uint8_t ifx = 0; - uint32_t src_ip = 0; - uint32_t nh = 0; - if (!pick_route(dst_ip, opts, &ifx, &src_ip, &nh)) { + uint32_t max_chunk = ((uint32_t)mtu - hdr_len) / 8 * 8; + if (!max_chunk) { netpkt_unref(pkt); - return; + return false; } - uint8_t dst_mac[6]; - bool is_dbcast = false; - l2_interface_t* l2 = l2_interface_find_by_index(ifx); - if (l2) { - for (int s = 0; s < MAX_IPV4_PER_INTERFACE; ++s) { - l3_ipv4_interface_t* v4 = l2->l3_v4[s]; - if (!v4 || v4->mode == IPV4_CFG_DISABLED) continue; - if (v4->mask && ipv4_broadcast_calc(v4->ip, v4->mask) == dst_ip) { is_dbcast = true; break; } - } - } + uint16_t ident = g_ip_ident++; + uint32_t off = 0; + const uint8_t* data = (const uint8_t*)netpkt_data(pkt); + bool ok = true; - 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; + 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; + + netpkt_t* fpkt = netpkt_alloc(frame_len, sizeof(eth_hdr_t), 0); + if (!fpkt) { + ok = false; + break; } - } - 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; + void* buf = netpkt_put(fpkt, frame_len); + if (!buf) { + netpkt_unref(fpkt); + ok = false; break; } - } - uint32_t hdr_len = IP_IHL_NOOPTS * 4; - uint32_t seg_len = netpkt_len(pkt); - void* hdrp = netpkt_push(pkt, hdr_len); - if (!hdrp) { - netpkt_unref(pkt); - return; + ipv4_hdr_t ip; + 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); + 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 = bswap16(checksum16(&ip, hdr_len)); + + 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; } - uint32_t total = hdr_len + seg_len; - if (dontfrag && total > (uint32_t)mtu) { - netpkt_unref(pkt); - return; - } - ipv4_hdr_t* ip = (ipv4_hdr_t*)hdrp; - 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((const uint16_t*)ip, hdr_len / 2); - - eth_send_frame_on(ifx, ETHERTYPE_IPV4, dst_mac, pkt); + netpkt_unref(pkt); + return ok && off == seg_len; } -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; - - ipv4_hdr_t* ip = (ipv4_hdr_t*)ip_ptr; - uint8_t ver = (uint8_t)(ip->version_ihl >> 4); - uint8_t ihl = (uint8_t)(ip->version_ihl & 0x0F); - if (ver != IP_VERSION_4) return; - if (ihl < IP_IHL_NOOPTS) return; - - uint32_t hdr_len = (uint32_t)ihl * 4; - if (ip_len < hdr_len) return; - - uint16_t saved = ip->header_checksum; - ip->header_checksum = 0; - if (checksum16((const uint16_t*)ip, hdr_len / 2) != saved) { - ip->header_checksum = saved; - return; - } - ip->header_checksum = saved; - - 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; - - uintptr_t l4 = ip_ptr + hdr_len; - uint32_t l4_len = (uint32_t)ip_totlen - hdr_len; - - uint32_t src = bswap32(ip->src_ip); - uint32_t dst = bswap32(ip->dst_ip); - - if (ifindex && src) { - uint8_t mac_old[6]; - bool had = arp_table_get_for_l2((uint8_t)ifindex, src, mac_old); - if (!had || memcmp(mac_old, src_mac, 6) != 0) { - arp_table_put_for_l2((uint8_t)ifindex, src, src_mac, 180000, false); - } else { - arp_table_put_for_l2((uint8_t)ifindex, src, mac_old, 180000, false); - } - } - - uint8_t proto = ip->protocol; - +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; @@ -382,54 +215,45 @@ void ipv4_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { 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; } - 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) { - 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; - 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) { - uint8_t l3id = cand[0]->l3_id; - switch (proto) { - case 1: icmp_input(l4, l4_len, src, dst); break; - case 6: tcp_input(IP_VER4, &src, &dst, l3id, l4, l4_len); break; - case 17: udp_input(IP_VER4, &src, &dst, l3id, l4, l4_len); break; - default: break; - } - return; - } else { - for (int i = 0; i < ccount; ++i) { - uint8_t l3id = cand[i]->l3_id; - switch (proto) { - case 1: icmp_input(l4, l4_len, src, dst); break; - case 6: tcp_input(IP_VER4, &src, &dst, l3id, l4, l4_len); break; - case 17: udp_input(IP_VER4, &src, &dst, l3id, l4, l4_len); break; - default: break; - } - } - return; + if (dst == IPV4_LIMITED_BROADCAST) { + 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((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); + else netpkt_unref(l4pkt); } + return; } int match_count = 0; @@ -437,54 +261,152 @@ 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) { - 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, l4_off, l4_len); + if (!l4pkt) return; + 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); + 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) { - case 1: icmp_input(l4, l4_len, src, dst); break; - case 6: tcp_input(IP_VER4, &src, &dst, l3id, l4, l4_len); break; - case 17: udp_input(IP_VER4, &src, &dst, l3id, l4, l4_len); break; - default: break; - } - } + if (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((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); + else netpkt_unref(l4pkt); } 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) { - 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; - } + 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; + + netpkt_t* l4pkt = netpkt_view(pkt, l4_off, l4_len); + if (!l4pkt) return; + 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); + return; + } +} + +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; + + 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_VER4) return; + if (ihl < IP_IHL_NOOPTS) return; + + uint32_t hdr_len = (uint32_t)ihl * 4; + if (ip_len < hdr_len) return; + + uint8_t hdr_copy[60]; + if (!netpkt_copyout(pkt, 0, hdr_copy, hdr_len)) return; + if (checksum16(hdr_copy, hdr_len) != 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[MAC_ADDR_LEN]; + bool had = arp_table_get_for_l2((uint8_t)ifindex, src, mac_old); + 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); + } + + 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; } - if (any_dbcast) 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 54638eac..f494859c 100644 --- a/kernel/networking/internet_layer/ipv4.h +++ b/kernel/networking/internet_layer/ipv4.h @@ -4,11 +4,9 @@ #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 -#define IP_VERSION_4 4 #define IP_TTL_DEFAULT 64 #ifdef __cplusplus @@ -28,11 +26,9 @@ 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; -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); -void ipv4_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]); +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[MAC_ADDR_LEN]); #ifdef __cplusplus } diff --git a/kernel/networking/internet_layer/ipv4_route.c b/kernel/networking/internet_layer/ipv4_route.c index 71b67e0a..84627673 100644 --- a/kernel/networking/internet_layer/ipv4_route.c +++ b/kernel/networking/internet_layer/ipv4_route.c @@ -1,40 +1,52 @@ #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; +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 void ipv4_rt_bump(ipv4_rt_table_t* t) { + if (!t) return; + t->epoch++; + if (!t->epoch) t->epoch = 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 ipv4_tx_plan_valid(const ipv4_tx_plan_t* plan) { + 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_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, const uint8_t* allowed_l3, int allowed_n, ipv4_tx_plan_t* out){ +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->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)) return false; + 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; - out->fixed_opts.scope = IP_TX_BOUND_L3; - out->fixed_opts.index = id; return true; } @@ -46,8 +58,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 (!l3_allowed(v4->l3_id, allowed_l3, allowed_n)) continue; + if (!ipv4_l3_is_ready(v4) || ipv4_is_loopback(dst) != v4->is_localhost) continue; cand[n++] = v4->l3_id; } } else { @@ -57,8 +68,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 (!l3_allowed(v4->l3_id, allowed_l3, allowed_n)) continue; + if (!ipv4_l3_is_ready(v4) || ipv4_is_loopback(dst) != v4->is_localhost) continue; cand[n++] = v4->l3_id; } } @@ -70,55 +80,50 @@ 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 (!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; - out->fixed_opts.scope = IP_TX_BOUND_L3; - out->fixed_opts.index = chosen; 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; } - return n; -} -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* 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; - memset(t, 0, sizeof(*t)); + t->owner_l3_id = owner_l3_id; + t->epoch = 1; 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) { 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; } @@ -128,12 +133,27 @@ 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; } } 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; @@ -144,7 +164,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; @@ -184,34 +204,36 @@ 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; 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){ + 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 20b3c9c3..e3d00b53 100644 --- a/kernel/networking/internet_layer/ipv4_route.h +++ b/kernel/networking/internet_layer/ipv4_route.h @@ -17,12 +17,15 @@ 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); 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); @@ -31,11 +34,13 @@ 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; - 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_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/ipv4_utils.c b/kernel/networking/internet_layer/ipv4_utils.c index cc7825da..ad3b4513 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,9 +45,22 @@ 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); + return ((mask | (mask - 1u)) == IPV4_LIMITED_BROADCAST); } int ipv4_prefix_len(uint32_t mask) { @@ -75,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; } @@ -99,46 +98,32 @@ 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) { 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 6fe4da8c..4718f347 100644 --- a/kernel/networking/internet_layer/ipv4_utils.h +++ b/kernel/networking/internet_layer/ipv4_utils.h @@ -1,7 +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" { @@ -19,6 +23,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); @@ -34,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 05b2cd6a..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" @@ -13,13 +14,14 @@ #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" #define IPV6_MIN_MTU 1280u #define PMTU_CACHE_SIZE 16 #define REASS_SLOTS 8 - typedef struct { uint8_t used; uint8_t dst[16]; @@ -38,8 +40,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]; @@ -47,9 +47,7 @@ typedef struct { uint16_t first_pkt_len; uint8_t _pad1[2]; uint8_t first_pkt[1280]; - - uint8_t *buf; - uint8_t bitmap[2048]; + net_fragbuf_t frag; } reass_slot_t; typedef struct __attribute__((packed)) { @@ -109,7 +107,7 @@ 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); + net_fragbuf_free(&s->frag); memset(s, 0, sizeof(*s)); } @@ -124,7 +122,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; @@ -132,285 +130,84 @@ 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); - 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); - 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]) { - 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; - } +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; } - 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; + ipv6_tx_plan_t plan; + if (!ipv6_build_tx_plan(dst, opts, &plan)) { + netpkt_unref(pkt); + return false; + } - if (out_ifx) *out_ifx = v6->l2->ifindex; - if (out_src) ipv6_cpy(out_src, v6->ip); + l3_ipv6_interface_t* src_v6 = l3_ipv6_find_by_id(plan.l3_id); + if (!ipv6_l3_is_ready(src_v6)) { + netpkt_unref(pkt); + return false; + } + 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 (!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 (ipv6_is_linklocal(src) && !ipv6_is_linklocal(dst) && !ipv6_is_multicast(dst)) { + netpkt_unref(pkt); + return false; } - 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) { + if (!ipv6_is_linklocal(dst) && !ipv6_is_multicast(dst)) { uint8_t via[16] = {0}; - int pl = -1; - int met =0x7FFF; + int route_pl = -1; + bool have_nh = false; - if (v6->routing_table && ipv6_rt_lookup_in((const ipv6_rt_table_t*)v6->routing_table, dst, via, &pl, &met)) { + 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 (!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; + 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; } + have_nh = 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); - } + 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 (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); - } - 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) { - if (!dst || !pkt || !netpkt_len(pkt)) { - if (pkt) netpkt_unref(pkt); - return; - } - - uint8_t ifx = 0; - uint8_t src[16] = {0}; - uint8_t nh[16] = {0}; - - l3_ipv6_interface_t* src_v6 = NULL; - - if (!pick_route(dst, opts, &ifx, src, nh)) { - netpkt_unref(pkt); - return; - } - - if (!ipv6_is_unspecified(src)) { - l2_interface_t* l2 = l2_interface_find_by_index(ifx); - if (!l2) { - netpkt_unref(pkt); - return; - } - - 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; - } - if (v6->dad_state != IPV6_DAD_OK) { - netpkt_unref(pkt); - return; - } - ok = 1; - src_v6 = v6; - break; - } - if (!ok) { + if (!have_nh) { netpkt_unref(pkt); - return; + return false; } } - if (ipv6_is_linklocal(src) && !ipv6_is_linklocal(dst) && !ipv6_is_multicast(dst)) { - netpkt_unref(pkt); - return; - } - + l2_interface_t* l2 = src_v6->l2; uint8_t dst_mac[6]; - l2_interface_t* l2 = l2_interface_find_by_index(ifx); + 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 (!ndp_resolve_on(ifx, nh, dst_mac, 200)) { - netpkt_unref(pkt); - return; - } - - 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; + 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; uint16_t pmtu = ipv6_pmtu_get(dst); - if (pmtu && pmtu 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; + ipv6_cpy(ip6.src, src); + ipv6_cpy(ip6.dst, dst); + memcpy(hdrp, &ip6, sizeof(ip6)); - eth_send_frame_on(ifx, ETHERTYPE_IPV6, dst_mac, pkt); - return; + if (need_ndp) return ndp_send_or_queue_on(ifx, nh, pkt); + return eth_send_frame_on(ifx, ETHERTYPE_IPV6, dst_mac, pkt); } - if (dontfrag) { + uint32_t frag_hdr_len = sizeof(ipv6_frag_hdr_t); + if (dontfrag || (uint32_t)mtu < hdr_len + frag_hdr_len + 8u) { netpkt_unref(pkt); - return; - } - - 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; - 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; 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; + while (off < seg_len) { + uint32_t remain = seg_len - off; uint32_t chunk = (remain > 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; 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; } - 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); + 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; } netpkt_unref(pkt); + return ok && off == seg_len; } -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; } @@ -545,10 +343,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*)ip_ptr; + ipv6_hdr_t ip6_; + ipv6_hdr_t* ip6 = &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(); @@ -556,7 +355,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); @@ -573,15 +372,14 @@ 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); 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; @@ -592,29 +390,30 @@ 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; - 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; - 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; - 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; + 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); 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); + 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)) { @@ -626,7 +425,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); } @@ -643,7 +442,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); } @@ -651,7 +450,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 > NET_FRAGBUF_DEFAULT_MAX_LEN) return; reass_slot_t* s = NULL; uint32_t now = (uint32_t)get_time(); @@ -665,8 +464,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; @@ -678,9 +476,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 = (uint8_t*)malloc(2048u * 8u); - if (!t->buf) return; - t->used = 1; t->ifindex = (uint8_t)ifindex; t->ident = ident; @@ -689,33 +484,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; - memset(t->first_src_mac, 0, 6); + net_fragbuf_init(&t->frag); + mac_clear(t->first_src_mac); 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) { @@ -724,25 +503,26 @@ 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; } 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; } } @@ -756,7 +536,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); } @@ -768,65 +548,63 @@ 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); + mac_copy(s->first_src_mac, src_mac); s->have_first = 1; } - memcpy(s->buf + off, frag, frag_len); - - 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; + if (!net_fragbuf_add(&s->frag, pkt, frag_off, off, frag_len, more)) { + reass_free(s); + return; + } + s->last_update_ms = now; - s->last_update_ms = (uint32_t)get_time(); + if (!net_fragbuf_complete(&s->frag)) return; - if (!more) { - s->have_last = 1; - s->total_len = off + frag_len; - } + uint32_t payload_off = 0; + uint32_t payload_size = s->frag.total_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; - } - } + netpkt_t* reassembled = net_fragbuf_take_packet(&s->frag); + if (!reassembled) { + reass_free(s); + return; } - if (!complete) return; - - uintptr_t payload_ptr = (uintptr_t)s->buf; - uint32_t payload_size = s->total_len; - if (!ipv6_skip_ext_headers(&inner_nh, &payload_ptr, &payload_size)) { + 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); + 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); 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; 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) { + netpkt_unref(reassembled); reass_free(s); return; } @@ -840,17 +618,22 @@ void ipv6_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { } } if (!joined) { + netpkt_unref(reassembled); reass_free(s); return; } 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); + 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 == 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); } + netpkt_unref(reassembled); reass_free(s); return; } @@ -865,16 +648,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 == 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); + } } - + 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); + 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; } @@ -885,8 +673,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; @@ -903,15 +690,18 @@ 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 (ip6->next_header) { - case 17: - udp_input(IP_VER6, ip6->src, ip6->dst, v6->l3_id, l4, l4_len); + switch (nh) { + netpkt_t* l4pkt; + 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: - tcp_input(IP_VER6, ip6->src, ip6->dst, v6->l3_id, l4, l4_len); + 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; default: break; @@ -930,12 +720,15 @@ void ipv6_input(uint16_t ifindex, netpkt_t* pkt, const uint8_t src_mac[6]) { } if (match_count >= 1) { - switch (ip6->next_header) { - case 6: - tcp_input(IP_VER6, ip6->src, ip6->dst, match_l3id, l4, l4_len); + switch (nh) { + netpkt_t* l4pkt; + 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: - udp_input(IP_VER6, ip6->src, ip6->dst, match_l3id, l4, l4_len); + 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; default: break; diff --git a/kernel/networking/internet_layer/ipv6.h b/kernel/networking/internet_layer/ipv6.h index a8e8fd36..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; -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 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 201bcea3..fb330892 100644 --- a/kernel/networking/internet_layer/ipv6_route.c +++ b/kernel/networking/internet_layer/ipv6_route.c @@ -5,45 +5,60 @@ #include "networking/interface_manager.h" #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 (!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; - 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; 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; +bool ipv6_tx_plan_valid(const ipv6_tx_plan_t* plan) { + 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; + 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, const uint8_t* allowed_l3, int allowed_n, ipv6_tx_plan_t* out) { +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; memset(out, 0, sizeof(*out)); - 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; 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; - memcpy(out->src_ip, v6->ip, 16); - out->fixed_opts.scope = IP_TX_BOUND_L3; - out->fixed_opts.index = id; + out->l3_epoch = v6->epoch; + ipv6_cpy(out->src_ip, v6->ip); return true; } @@ -56,7 +71,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 { @@ -66,8 +80,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; } } @@ -82,56 +95,55 @@ bool ipv6_build_tx_plan(const uint8_t dst[16], const ip_tx_opts_t* hint, const u 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->fixed_opts.scope = IP_TX_BOUND_L3; - out->fixed_opts.index = chosen; + out->l3_epoch = v6->epoch; + ipv6_cpy(out->src_ip, v6->ip); 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 = malloc(sizeof(*t)); +ipv6_rt_table_t* ipv6_rt_create(uint8_t owner_l3_id) { + ipv6_rt_table_t* t = zalloc(sizeof(*t)); if (!t) return 0; - memset(t, 0, sizeof(*t)); + t->owner_l3_id = owner_l3_id; + t->epoch = 1; 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) { 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; } @@ -140,9 +152,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; } } @@ -150,6 +163,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; @@ -160,26 +187,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; @@ -189,13 +198,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; @@ -207,53 +216,24 @@ 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); } 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]; - 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); } } @@ -265,9 +245,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; @@ -281,11 +259,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; } @@ -294,12 +274,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 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 +61,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 +122,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 +139,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..4140e041 100644 --- a/kernel/networking/internet_layer/ipv6_utils.h +++ b/kernel/networking/internet_layer/ipv6_utils.h @@ -1,5 +1,7 @@ #pragma once #include "types.h" +#include "net/interface_types.h" +#include "networking/interface_manager.h" #ifdef __cplusplus extern "C" { @@ -23,9 +25,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 901a69b4..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" @@ -112,12 +113,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; @@ -133,22 +134,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) { @@ -225,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; } @@ -295,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; } @@ -318,25 +314,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; @@ -361,9 +361,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; } @@ -371,10 +370,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 a1534b3c..9f92ba60 100644 --- a/kernel/networking/link_layer/arp.c +++ b/kernel/networking/link_layer/arp.c @@ -1,18 +1,25 @@ #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 "console/kio.h" #include "networking/internet_layer/ipv4.h" #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 +#define ARP_DAD_PROBES 3 +#define ARP_DAD_WAIT_MS 150 + +struct arp_table { arp_entry_t entries[ARP_TABLE_MAX]; uint8_t init; -} arp_table_t; +}; static uint16_t g_arp_pid = 0xFFFF; @@ -21,176 +28,333 @@ 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*)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) return; + for (int i = 0; i < (int)N_ARR(t->entries); i++) arp_entry_clear(&t->entries[i]); + release(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; } 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; i < (int)N_ARR(t->entries); i++) { + if (t->entries[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; i < (int)N_ARR(t->entries); i++) if (t->entries[i].state == ARP_STATE_UNUSED) return i; return -1; } -void arp_table_put_for_l2(uint8_t ifindex, uint32_t ip, const uint8_t mac[6], uint32_t ttl_ms, bool is_static){ +static int arp_find_replacement(arp_table_t* t) { + int best = -1; + uint32_t best_ttl = UINT32_MAX; + if (!t) return -1; + + 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; + 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[MAC_ADDR_LEN], 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; + 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; + 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]){ +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) return false; - for (int i=0;ientries[i].ip == ip){ - memcpy(mac_out, t->entries[i].mac, 6); - return true; - } + if (!t || !mac_out) return false; + + for (int i = 0; i < (int)N_ARR(t->entries); 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; + mac_copy(mac_out, e->mac); + return true; } return false; } -void arp_table_tick_for_l2(uint8_t ifindex, uint32_t ms){ +bool arp_table_delete_for_l2(uint8_t ifindex, uint32_t ip) { 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; - } - } + 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; } -void arp_tick_all(uint32_t ms){ - for (uint8_t i=1;i<=MAX_L2_INTERFACES;i++){ - l2_interface_t* l2 = l2_interface_find_by_index(i); - if (!l2) continue; - if (!l2->arp_table) continue; - arp_table_tick_for_l2(i, ms); +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 < (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; } + 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;sl3_v4); s++) { l3_ipv4_interface_t* v4 = l2->l3_v4[s]; - if (!v4) continue; - if (v4->mode == IPV4_CFG_DISABLED) continue; - if (v4->ip && v4->mask){ + 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; + if (a == b)return v4->ip; } } - for (int s=0;sl3_v4); s++) { l3_ipv4_interface_t* v4 = l2->l3_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; } -bool arp_resolve_on(uint8_t ifindex, uint32_t ip, uint8_t mac_out[6], uint32_t timeout_ms){ - if (ip == 0xFFFFFFFFu){ - memset(mac_out, 0xFF, 6); - return true; +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; i < (int)N_ARR(t->entries); i++){ + 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, pick_spa_for_l2(ifindex, e->ip), 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); } - 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; +} + +void arp_tick_all(uint32_t ms){ + for (uint8_t i = 1; i <= MAX_L2_INTERFACES; i++){ + l2_interface_t* l2 = l2_interface_find_by_index(i); + if (!l2) continue; + if (!l2->arp_table) continue; + arp_table_tick_for_l2(i, ms); } - return false; } -void arp_send_request_on(uint8_t ifindex, uint32_t target_ip){ +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[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); + + arp_table_t* t = l2_arp(ifindex); + if (!t || !ip) { + netpkt_unref(pkt); + 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, pick_spa_for_l2(ifindex, ip), ip); + } + + return true; +} + +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); - uint8_t dst_mac[6] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}; + if (!local_mac || !target_ip) return false; + 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); - hdr.sender_ip = bswap32(spa); + 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); - 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[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++) { + 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 (!v4) continue; - if (v4->mode == IPV4_CFG_DISABLED) continue; + if (!ipv4_l3_is_ready(v4)) continue; if (v4->ip == ip) return true; } 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 = 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); + 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); @@ -205,43 +369,32 @@ 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[MAC_ADDR_LEN], netpkt_t* pkt) { + if (!pkt || !src_mac) return; + if (netpkt_len(pkt) < (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); + arp_hdr_t 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); - arp_table_put_for_l2((uint8_t)ifindex, sender_ip, hdr->sender_mac, 180000, false); + 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) { - 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); - } + if (l2_has_ip((uint8_t)ifindex, target_ip)) arp_send_reply_on((uint8_t)ifindex, &hdr, src_mac); } } -void arp_set_pid(uint16_t pid){ g_arp_pid = pid; } uint16_t arp_get_pid(void){ return g_arp_pid; } int arp_daemon_entry(int argc, char* argv[]){ (void)argc; (void)argv; - arp_set_pid(get_current_proc_pid()); - const uint32_t tick_ms = 10000; + g_arp_pid = get_current_proc_pid(); + 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 badfb6ee..23d6f122 100644 --- a/kernel/networking/link_layer/arp.h +++ b/kernel/networking/link_layer/arp.h @@ -1,33 +1,48 @@ #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" { #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; 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; - 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; @@ -36,17 +51,19 @@ 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); 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); +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, netpkt_t* pkt); +void arp_input(uint16_t ifindex, const uint8_t src_mac[MAC_ADDR_LEN], 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/eth.c b/kernel/networking/link_layer/eth.c index e50400fc..5e03e840 100644 --- a/kernel/networking/link_layer/eth.c +++ b/kernel/networking/link_layer/eth.c @@ -6,38 +6,45 @@ #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) { - eth_hdr_t* eth =(eth_hdr_t*)p; +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, dst_mac, 6); - memcpy(eth->src_mac, src_mac, 6); - eth->ethertype = bswap16(type); + 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); } -uint16_t eth_parse_type(uintptr_t ptr){ - const eth_hdr_t* eth = (const eth_hdr_t*)ptr; - return bswap16(eth->ethertype); +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){ - const eth_hdr_t* eth = (const eth_hdr_t*)ptr; - return eth->src_mac; +bool eth_src(const netpkt_t* pkt, uint8_t out[MAC_ADDR_LEN]){ + if (!pkt || !out) return false; + return netpkt_copyout(pkt, 6u, out, MAC_ADDR_LEN); } -const uint8_t* eth_dst(uintptr_t ptr){ - const eth_hdr_t* eth = (const eth_hdr_t*)ptr; - return eth->dst_mac; +bool eth_dst(const netpkt_t* pkt, uint8_t out[MAC_ADDR_LEN]){ + if (!pkt || !out) return false; + 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); 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); @@ -46,36 +53,34 @@ 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; } 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; + if (!netpkt_pull(pkt, (uint32_t)sizeof(eth_hdr_t))) return; - uint16_t type = eth_parse_type(frame_ptr); - const uint8_t* src_mac = eth_src(frame_ptr); + uint16_t type = bswap16(eth.ethertype); switch (type) { case ETHERTYPE_ARP: - arp_input(ifindex, pkt); + arp_input(ifindex, eth.src_mac, pkt); break; case ETHERTYPE_IPV4: - if (!netpkt_pull(pkt, (uint32_t)sizeof(eth_hdr_t))) break; - ipv4_input(ifindex, pkt, src_mac); + ipv4_input(ifindex, pkt, eth.src_mac); break; case ETHERTYPE_IPV6: - if (!netpkt_pull(pkt, (uint32_t)sizeof(eth_hdr_t))) break; - ipv6_input(ifindex, pkt, src_mac); + ipv6_input(ifindex, pkt, eth.src_mac); break; - case ETHERTYPE_VLAN1Q: //TODO vlan + case ETHERTYPE_VLAN1Q: break; - case ETHERTYPE_VLAN1AD: //TODO vlan + case ETHERTYPE_VLAN1AD: break; default: break; diff --git a/kernel/networking/link_layer/eth.h b/kernel/networking/link_layer/eth.h index 38dc465b..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(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[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 dfa878d8..e8c4d6b4 100644 --- a/kernel/networking/link_layer/ndp.c +++ b/kernel/networking/link_layer/ndp.c @@ -1,4 +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" @@ -10,6 +12,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]; @@ -71,6 +74,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; @@ -92,7 +107,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); @@ -102,7 +116,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; } @@ -116,7 +130,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); @@ -159,27 +173,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) { @@ -193,8 +201,7 @@ static void apply_ra_policy(uint32_t now_ms, l2_interface_t* l2) { v6->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); } @@ -208,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]; @@ -217,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)) { @@ -228,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); @@ -241,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); @@ -257,7 +265,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++) { @@ -267,7 +274,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; @@ -291,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, zero16, 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); @@ -306,22 +313,34 @@ 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); + 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; - if (memcmp(slot->ip, zero16, 16) == 0) slot->timestamp_created = now_ms; + 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); } +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*)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 +348,9 @@ 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)); + 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); } static ndp_table_impl_t* l2_ndp(uint8_t ifindex) { @@ -344,7 +365,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; @@ -358,62 +379,72 @@ 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->static_entry) continue; + 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) { +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; 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]; - 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); + mac_copy(e->mac, mac); e->state = NDP_STATE_REACHABLE; 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) { + 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]) { +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; @@ -422,18 +453,42 @@ 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); + mac_copy(mac_out, e->mac); return true; } 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) { + 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)malloc(plen); + uintptr_t buf = (uintptr_t)zalloc(plen ? plen : 1u); if (!buf) return false; icmpv6_na_t* na = (icmpv6_na_t*)buf; @@ -446,29 +501,29 @@ 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; 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, 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); - 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); - 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; @@ -477,7 +532,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)); @@ -485,20 +540,20 @@ 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]; 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); 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 +583,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; @@ -542,16 +597,16 @@ 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, 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); 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) { @@ -561,17 +616,15 @@ 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); + 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); } } @@ -590,6 +643,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; @@ -665,77 +719,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 && ipv6_cmp(e->ip, next_hop) != 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; + ipv6_cpy(e->ip, next_hop); + mac_clear(e->mac); + 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]) { @@ -767,17 +812,21 @@ 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; - const icmpv6_ns_t* ns = (const icmpv6_ns_t*)icmp; - if (ipv6_is_multicast(ns->target)) return; + icmpv6_ns_t 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); if (!l2) return; @@ -789,7 +838,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; } @@ -809,17 +858,25 @@ 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, false); uint8_t src_my[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 (v6->dad_state != IPV6_DAD_OK) continue; + if (!ipv6_l3_is_ready(v6)) 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 +887,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; + 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); @@ -846,7 +904,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; @@ -859,8 +917,16 @@ 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); + 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,87 +934,46 @@ 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) { - 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 && 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) { - memcpy(e->ip, na->target, 16); - memcpy(e->mac, src_mac, 6); + ipv6_cpy(e->ip, na.target); + 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; 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; + 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; - 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); + mac_copy(e->mac, src_mac); 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); } } } @@ -959,27 +984,40 @@ 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; } 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; + 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); - 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; - 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; - const uint8_t* opt = (const uint8_t*)(ra + 1); + 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); @@ -990,28 +1028,31 @@ 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; 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; + 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; - 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); + 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); + 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) { @@ -1032,10 +1073,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}; - - 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) memset(a0, 0, sizeof(a0)); + if (addr_count >= 2 && !netpkt_copyout(pkt, opt_off + 24u, a1, sizeof(a1))) break; + else if (addr_count < 2) memset(a1, 0, sizeof(a1)); l3_ipv6_interface_t* slot = NULL; @@ -1046,7 +1088,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; @@ -1078,16 +1120,16 @@ 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); } } } - opt += opt_size; + opt_off += opt_size; opt_len -= opt_size; } @@ -1100,9 +1142,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; @@ -1172,12 +1212,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 { @@ -1186,8 +1225,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/link_layer/ndp.h b/kernel/networking/link_layer/ndp.h index 85b3e5ee..6d799f16 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" { @@ -10,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, @@ -25,9 +28,13 @@ 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; + uint8_t static_entry; uint32_t router_lifetime_ms; } ndp_entry_t; @@ -36,11 +43,14 @@ 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); +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_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/net_fragbuf.c b/kernel/networking/net_fragbuf.c new file mode 100644 index 00000000..d7d07215 --- /dev/null +++ b/kernel/networking/net_fragbuf.c @@ -0,0 +1,74 @@ +#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 = fb->data ? (uint8_t*)reallocate(fb->data, new_cap) : (uint8_t*)zalloc(new_cap); + if (!new_data) return false; + 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/net_logger/net_logger.c b/kernel/networking/net_logger/net_logger.c index 2ab6b449..6bbb3743 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"; @@ -49,19 +39,13 @@ 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 ""; } } -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) { +void netlog_socket_event(const SocketOptions* extra, const netlog_socket_event_t* e) { if (!extra) return; if (!e) return; @@ -97,7 +81,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; } @@ -123,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; } @@ -142,8 +121,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; } @@ -185,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; } @@ -235,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); @@ -247,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; } @@ -327,10 +299,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/net_logger/net_logger.h b/kernel/networking/net_logger/net_logger.h index 17dedc27..b9517df3 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; @@ -53,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/netpkt.c b/kernel/networking/netpkt.c index d69695ea..659ce411 100644 --- a/kernel/networking/netpkt.c +++ b/kernel/networking/netpkt.c @@ -1,10 +1,18 @@ #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 +#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 +#define NETPKT_HEADROOM_SLACK 32u typedef struct netpkt_buf netpkt_buf_t; @@ -53,13 +61,129 @@ 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 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); + *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); + 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; + } -static uintptr_t g_spare_page; + 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; @@ -82,25 +206,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; @@ -114,9 +233,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; } @@ -125,15 +241,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; @@ -142,7 +255,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; @@ -178,8 +290,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) { @@ -232,8 +343,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; } } } @@ -243,66 +352,52 @@ 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; - - 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; + 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; - uint32_t cap = (uint32_t)cap64; - if ((uint64_t)new_head+(uint64_t)p->len > (uint64_t)cap) return false; + uint64_t cap64 = new_alloc ? new_alloc : 1; - void* mem = 0; - bool from_spare = false; - 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; + 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 > NETPKT_MAX_STORAGE_BYTES)return false; - 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; - } + cap = (uint32_t)cap64; } + if ((uint64_t)new_head+(uint64_t)p->len > (uint64_t)cap) return false; + + uint32_t flags = 0; + 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 (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; - nb->flags = 0; + nb->flags = flags; 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; @@ -310,133 +405,86 @@ 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, ob->alloc); - } 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) { + 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) 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; - - uint32_t cap = (uint32_t)cap64; - - void* mem = 0; - bool from_spare = false; - 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 0; - if (g_netpkt_page_bytes > (uint64_t)NETPKT_MAX_PAGE_BYTES - (uint64_t)cap) 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; - return 0; - } + 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; } - 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) { - 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; - } + uint32_t flags = 0; + void* mem = netpkt_payload_alloc(cap, &flags); + if (!mem) return 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; } 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; - 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 (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; - } - return 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; + 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; - - 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 (!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(); + 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; + } b->base = base; b->alloc = alloc_size; @@ -445,13 +493,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); - return 0; - } - p->buf = b; p->off = 0; p->cap = alloc_size; @@ -459,22 +500,29 @@ 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; - - uint64_t end = (uint64_t)off+(uint64_t)len; - if (end > (uint64_t)parent->len) return 0; + irq_flags_t irq = irq_save_disable(); + if (!parent->buf || off > parent->len || len > parent->len - off) { + 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; + 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; + } parent->buf->refs++; @@ -485,55 +533,55 @@ 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; + + 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--; + irq_restore(irq); return; } 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, b->alloc); - } 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) { @@ -546,56 +594,83 @@ 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; + + memcpy(dst, (const void*)src, len); + return true; +} + uint32_t netpkt_headroom(const netpkt_t* p) { return p ? p->head : 0; } 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->buf) return false; - if (p->flags & NETPKT_F_VIEW) return false; - if (need > NETPKT_MAX_ALLOC) 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)) { + 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->head >= need && p->buf->refs == 1) { + irq_restore(irq); + return true; } - uint32_t tail = netpkt_tailroom(p); - uint32_t new_head = need; + 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; + } - 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; + 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); - return netpkt_realloc_to(p, new_head, (uint32_t)alloc); + if (alloc < min) alloc = min; + 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->buf) return false; - if (p->flags & NETPKT_F_VIEW) return false; - if (need > NETPKT_MAX_ALLOC) 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)) { + 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); + 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 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); + uint64_t min = (uint64_t)p->cap + (uint64_t)(need > tail ? need - tail : 0); + irq_restore(irq); if (alloc < min) alloc = min; - if (alloc > (uint64_t)NETPKT_MAX_ALLOC) return false; - - return netpkt_realloc_to(p, p->head, (uint32_t)alloc); + 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) { @@ -603,85 +678,60 @@ 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; } 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; - - 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; - - 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; } bool netpkt_trim(netpkt_t* p, uint32_t new_len) { if (!p) return false; - if (new_len > p->len) 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; - - 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; - - 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_flags_t irq = irq_save_disable(); + if (new_len > p->len) { + irq_restore(irq); + return false; } - + p->len = new_len; + irq_restore(irq); return true; } diff --git a/kernel/networking/netpkt.h b/kernel/networking/netpkt.h index 1f03c27f..394fa6a8 100644 --- a/kernel/networking/netpkt.h +++ b/kernel/networking/netpkt.h @@ -8,9 +8,9 @@ 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_ALLOC 65535u #define NETPKT_MAX_PAGE_BYTES (32ull * 1024ull * 1024ull) netpkt_t* netpkt_alloc(uint32_t data_capacity, uint32_t headroom, uint32_t tailroom); @@ -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.cpp b/kernel/networking/network.cpp index c1b7e87e..37d97c19 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(system_module *mod) { } 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) { @@ -67,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 d1278479..5f26c1a7 100644 --- a/kernel/networking/network.h +++ b/kernel/networking/network.h @@ -6,13 +6,13 @@ extern "C" { #include "types.h" #include "net/network_types.h" +#include "networking/netpkt.h" #include "files/system_module.h" #define NET_IRQ_BASE 40 //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(); @@ -20,9 +20,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 5118c31d..ed990ebf 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,9 +12,12 @@ #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" -#define RX_INTR_BATCH_LIMIT 64 +#define TASK_RX_QUANTUM 64 +#define TASK_TX_QUANTUM 64 #define TASK_RX_BATCH_LIMIT 256 #define TASK_TX_BATCH_LIMIT 256 @@ -35,12 +36,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; } } @@ -65,113 +60,82 @@ 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; - NetDriver* driver = nics[nic_id].drv; - if (!driver) return; - driver->handle_sent_packet(); -} - -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; + if (!pkt || !netpkt_len(pkt)) return false; + if (!nics[nic_id].drv) return false; - sizedptr pkt = driver->allocate_packet(frame.size); - if (!pkt.ptr) return false; + irq_flags_t flags = irq_save_disable(); + int pushed = nics[nic_id].tx.push(pkt); + irq_restore(flags); - uint16_t hs = nics[nic_id].hdr_sz; - void* dst = (void*)(pkt.ptr + hs); - memcpy(dst, (const void*)frame.ptr, frame.size); - - if (!nics[nic_id].tx.push(pkt)) { - free_frame(pkt); - nics[nic_id].tx_dropped++; - return false; - } - nics[nic_id].tx_produced++; - return true; + return pushed != 0; } int NetworkDispatch::net_task() { - set_net_pid(get_current_proc_pid()); + g_net_pid = get_current_proc_pid(); for (;;) { bool did_work = false; for (size_t n = 0; n < nic_num; ++n) { NetDriver* driver = nics[n].drv; - if (driver) { - 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; + if (!driver) continue; + + 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; } - nics[n].rx_produced++; - } - } - 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, NULL, 0); - if (np) { - eth_input(nics[n].ifindex, np); - netpkt_unref(np); + + socket_packet_input(nics[n].ifindex, pkt); + if (netpkt_len(pkt) >= sizeof(eth_hdr_t)) eth_input(nics[n].ifindex, pkt); + netpkt_unref(pkt); + rx_processed++; + rx_round++; } - free_frame(pkt); - nics[n].rx_consumed++; - processed++; - } - if (processed) did_work = true; - } + 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); - 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) { - if (nics[n].tx.is_empty()) break; - sizedptr pkt{0,0}; - if (!nics[n].tx.pop(pkt)) break; - if (!driver->send_packet(pkt)) { - free_frame(pkt); - nics[n].tx_dropped++; + 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); + tx_processed++; + tx_round++; } - nics[n].tx_consumed++; - processed++; + driver->complete_tx_batch(); + if (!rx_round && !tx_round) break; } - if (processed) did_work = true; + if (rx_processed || tx_processed) did_work = true; } if (!did_work) msleep(1);//TODO: manage it with an event } } -void NetworkDispatch::set_net_pid(uint16_t pid) -{ - g_net_pid = pid; -} - uint16_t NetworkDispatch::get_net_pid() const { return g_net_pid; @@ -235,17 +199,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) free_sized((void*)f.ptr, f.size); -} - bool NetworkDispatch::register_all_from_bus() { int n = net_bus_count(); if (n <= 0) return false; @@ -305,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/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/networking/port_manager.c b/kernel/networking/port_manager.c deleted file mode 100644 index 0af1a7ba..00000000 --- a/kernel/networking/port_manager.c +++ /dev/null @@ -1,107 +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; -} diff --git a/kernel/networking/port_manager.h b/kernel/networking/port_manager.h deleted file mode 100644 index e2cde304..00000000 --- a/kernel/networking/port_manager.h +++ /dev/null @@ -1,63 +0,0 @@ -#pragma once -#include "types.h" -#include "net/network_types.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, - uintptr_t frame_ptr, - uint32_t frame_len, - 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); - -#ifdef __cplusplus -} -#endif diff --git a/kernel/networking/processes/net_proc.c b/kernel/networking/processes/net_proc.c index c9554089..d415d08b 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" @@ -12,338 +11,246 @@ #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/csocket.h" #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" -#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); - if (!sock) + 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); return 0; + } 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 (socket_sendto_udp_ex(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; } 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) { - recvd = socket_recvfrom_udp_ex(sock, recv_buf, sizeof(recv_buf), &src); - if (recvd > 0) - break; - msleep(INTERVAL_MS); - waited += INTERVAL_MS; + while (waited < PROBE_TIMEOUT_MS) { + 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; 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 (!ipv4_l3_is_ready(v4) || v4->is_localhost) continue; + if (!ipv4_is_loopback(v4->ip)) return 1; + } - if (req->body.ptr && req->body.size) free_sized((void*)req->body.ptr, req->body.size); + for (uint8_t j = 0; j < MAX_IPV6_PER_INTERFACE; j++) { + l3_ipv6_interface_t *v6 = l2->l3_v6[j]; + if (!ipv6_l3_is_ready(v6) || v6->is_localhost) continue; + if (!ipv6_is_loopback(v6->ip)) 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; - opt.flags = SOCK_OPT_DEBUG; - http_server_handle_t srv = http_server_create(pid, &opt); - if (!srv) { - stop_current_process(1); - return; - } - struct SockBindSpec spec = {0}; - spec.kind = BIND_ANY; - if (http_server_bind(srv, &spec, 80) < 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", 80, "path=/"); - - static const 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 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?

"; - 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){ - msleep(50); - 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); - } - - 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; - } - - http_server_send_response(srv, conn, &res); - http_connection_close(conn); - free_request(&req); - } + 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) { + 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); + print("[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); - if (!cli) { - kprintf("[HTTP] http_client_create FAIL"); - return; - } + http_client_handle_t cli = http_client_create(NULL, NULL); + if (!cli) return; 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_ex(cli, DST_ENDPOINT, &e, 0); - if (rc < 0) { + if (http_client_connect_endpoint(cli, &e) < 0) { http_client_destroy(cli); return; } 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) { - char *body_str = (char*)malloc(resp.body.size + 1); + if ((int)resp.status_code < 0) print("[HTTP] request FAIL status=%i", (int)resp.status_code); + 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'; - 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); + 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); } } http_client_close(cli); http_client_destroy(cli); - if (resp.body.ptr && resp.body.size) free_sized((void*)resp.body.ptr, resp.body.size); - - 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; + http_response_free(&resp); + http_request_free(&req); } 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); + timer_datetime_to_string(&now_dt_utc, s, sizeof(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()); + timer_datetime_to_string(&now_dt_loc, s, sizeof(s)); + print("[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,36 +259,32 @@ 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 (!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; + + net_l4_endpoint srv = (net_l4_endpoint){0}; + if (udp_probe_server(probe_ip, PROBE_PORT, &srv)) { + test_http(&srv); + tested_any = 1; + } } } - run_http_server(); if (!tested_any) { net_l4_endpoint srv = (net_l4_endpoint){0}; uint32_t fallback = (192<<24)|(168<<16)|(1<<8)|255; - if (udp_probe_server(fallback, 8080, &srv)) - test_http(&srv); - else - kprintf("[NET] could not find update server"); + if (udp_probe_server(fallback, PROBE_PORT, &srv)) test_http(&srv); } -} -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()) { - 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; } @@ -395,12 +298,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()) { - kprintf("[NET] ip ready, starting net_test"); + if (net_has_ready_address()) { + print("[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/networking/transport_layer/csocket.c b/kernel/networking/transport_layer/csocket.c index d9d19078..a2a01b9d 100644 --- a/kernel/networking/transport_layer/csocket.c +++ b/kernel/networking/transport_layer/csocket.c @@ -1,205 +1,216 @@ #include "csocket.h" +#include "csocket_ctrl.h" +#include "csocket_packet.h" +#include "csocket_raw.h" #include "csocket_tcp.h" #include "csocket_udp.h" +#include "socket_core.h" +#include "process/scheduler.h" +#include "std/memory.h" #include "memory/page_allocator.h" #include "console/kio.h" #include "data/struct/hashmap.h" #include "alloc/allocate.h" -uint16_t socket_ids; +socket_handle_t create_socket(protocol_t protocol, const SocketOptions* extra){ + SocketOptions default_extra = {}; + if (!extra) extra = &default_extra; + + SocketSpecialKind special_kind = extra->special_kind; + 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(); + ksocket_t* socket = NULL; + if (!socket_core_alloc(protocol, special_kind, pid, &socket)) return 0; + + 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, 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, extra); + destroy = socket_destroy_tcp; + 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 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); + return 0; + } + + if (!socket_core_attach_impl(socket, impl, destroy, close, setopt, getopt)) { + destroy(impl); + socket_core_close_socket(socket); + return 0; + } + return socket_core_export_handle(socket); +} + +int32_t bind_socket(socket_handle_t handle, const SockBindSpec *spec_in, uint16_t port){ + SockBindSpec spec; + memset(&spec, 0, sizeof(spec)); + if (spec_in) spec = *spec_in; + else spec.kind = BIND_ANY; -void *sock_mem_page; + ksocket_t* socket = socket_core_get(handle, get_current_proc_pid()); + if (!socket) return SOCK_ERR_INVAL; -hash_map_t *map; + 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); -typedef struct ksock_handle_t { - uint16_t id; - net_l4_endpoint connection; - protocol_t protocol; - void* sh; - uint16_t pid; -} ksock_handle_t; + socket_core_put(socket); -void* csock_alloc(size_t size){ - return allocate(sock_mem_page, size, page_alloc); + return res; } -static inline void check_mem(){ - if (!sock_mem_page) - sock_mem_page = page_alloc(MEM_PRIV_KERNEL); - if (!map) - map = hash_map_create_alloc(128, csock_alloc, release); +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_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); + + socket_core_put(socket); + return res; } -bool create_socket(Socket_Role role, protocol_t protocol, const SocketExtraOptions* extra, uint16_t pid, SocketHandle *out_handle){ - check_mem(); - 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; - } - if (!in_handle){ - kprintf("[SOCKET] failed to create socket for %i",pid); - 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*)csock_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"); - return false; - } - return true; +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; + + 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_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); + + socket_core_put(socket); + return res; } -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 = csock_alloc(sizeof(SockBindSpec)); - 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_ex(sh->sh, spec, port); - break; - } +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; + + int64_t res = SOCK_ERR_PROTO; + 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); 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; +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; + + 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_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); + + 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_ex(sh->sh, dst_kind, dst, port, buf, len); - break; - } - return 0; +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); + 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_ex(sh->sh, buf, len, out_src); - break; - } - return 0; +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); + 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; +int32_t close_socket(socket_handle_t handle){ + return socket_core_close_handle(handle, get_current_proc_pid()); +} + +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; + + int32_t res = SOCK_ERR_PROTO; + 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; } -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"); +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; } - 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; + if (socket_core_protocol(listener) != PROTO_TCP) { + socket_core_put(listener); + return 0; } - return 0; -} -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; - } - 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 0; } + + socket_handle_t child_handle = socket_core_export_handle(child); + socket_core_put(child); + socket_core_put(listener); + 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 0434c30b..c8027e48 100644 --- a/kernel/networking/transport_layer/csocket.h +++ b/kernel/networking/transport_layer/csocket.h @@ -3,14 +3,27 @@ #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); +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); -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 +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); + +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 +} +#endif \ No newline at end of file diff --git a/kernel/networking/transport_layer/csocket_ctrl.c b/kernel/networking/transport_layer/csocket_ctrl.c new file mode 100644 index 00000000..aa7d2c51 --- /dev/null +++ b/kernel/networking/transport_layer/csocket_ctrl.c @@ -0,0 +1,126 @@ +#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 & ~SOCK_OPT_SPECIAL)) 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: + 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: + case SOCK_GET_TCP_URGENT_REMAINING: + 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..9e6e8f45 --- /dev/null +++ b/kernel/networking/transport_layer/csocket_packet.c @@ -0,0 +1,407 @@ +#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" +#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; +} + +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 | 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)); + 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; + 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_NONBLOCK) s->options.flags |= SOCK_OPT_NONBLOCK; + 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; + } + 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; + 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: + 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); + 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; + } +} + +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); + case SOCK_GET_OPT_FILTER: + return socket_common_get_value(&s->options.packet_filter, sizeof(s->options.packet_filter), 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: + case SOCK_GET_OPT_NONBLOCK: + 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: + 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: + case SOCK_GET_TCP_URGENT_REMAINING: + 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_NONBLOCK) 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; + + 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); + } + } + + 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) 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(); + 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; + 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; + 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; + if (pkt_len > limit) { + socket_core_put(s->ownerSocket); + continue; + } + + irq = irq_save_disable(); + 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){ + 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_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 new file mode 100644 index 00000000..ebdcc1aa --- /dev/null +++ b/kernel/networking/transport_layer/csocket_raw.c @@ -0,0 +1,593 @@ +#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 "networking/transport_layer/trans_utils.h" +#include "networking/transport_layer/socket_bind.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; + SockBindSpec rx_spec; +} raw_rx_entry_t; + +typedef struct raw_socket { + ksocket_t* ownerSocket; + SocketOptions options; + bool registered; + 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; + 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_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; + 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_FILTER; + else s->options.flags &= ~SOCK_OPT_FILTER; + return SOCK_OK; +} + +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) { + 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->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; + } + 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; + 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; + 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 + + 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)); + 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) { + 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) { + if (extra->debug_level > SOCK_DBG_ALL) { + release(s); + return NULL; + } + 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_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; + } + if (extra->flags & SOCK_OPT_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(); + 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: + 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); + case SOCK_OPT_SPECIAL: + return SOCK_ERR_UNSUP; + 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: + return socket_common_get_value(&s->remote_ep, sizeof(s->remote_ep), value, len); + 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); + case SOCK_GET_OPT_FILTER: + return socket_common_get_value(&s->options.raw_filter, sizeof(s->options.raw_filter), value, len); + 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_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: + 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_TCP_URGENT_REMAINING: + case SOCK_GET_MCAST_GROUPS: + return SOCK_ERR_UNSUP; + case SOCK_GET_OPT_RECV_TIMEOUT: + 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: + case SOCK_GET_OPT_BROADCAST_ALLOWED: + return SOCK_ERR_UNSUP; + default: + return SOCK_ERR_INVAL; + } + + return socket_common_get_value(&v, sizeof(v), value, len); +} + +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; + 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) { + 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_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_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; + + 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; +} + +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->remote_ep.port = 0; + 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 || s->bind_spec.kind == BIND_IP)) { + 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)); + 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) { + 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)); + } + + 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->rx_count != 0; + irq_restore(irq); + if (ready) break; + 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_count != 0; + irq_restore(irq); + if (ready) break; + + 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); + } + } + + irq_flags_t irq = irq_save_disable(); + 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->last_rx_spec = s->rx[pos].rx_spec; + s->rx_bytes -= pkt_len; + s->rx[pos].data = NULL; + s->rx[pos].len = 0; + s->rx_head = (uint8_t)((s->rx_head + 1) % RAW_RX_RING_CAP); + s->rx_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; + 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(); + 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 != 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; + } + 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 (raw_enqueue(s, &src_ep, &rx_spec, pkt, len)) delivered = true; + 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; + 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(); + 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; + } + 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; + } + 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 (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_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_tcp.c b/kernel/networking/transport_layer/csocket_tcp.c new file mode 100644 index 00000000..e92a7b9c --- /dev/null +++ b/kernel/networking/transport_layer/csocket_tcp.c @@ -0,0 +1,871 @@ +#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/interface_manager.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" +#include "exceptions/irq.h" + +#define TCP_MAX_BACKLOG 128 + +typedef struct tcp_socket { + uint16_t localPort; + net_l4_endpoint remoteEP; + bool connected; + ksocket_t* ownerSocket; + SocketOptions options; + SockBindSpec bindSpec; + socket_bind_token_t bindToken; + tcp_data flow; + ksocket_t** pending; + int32_t backlogCap; + int32_t backlogLen; + bool listening; + bool closed; +} tcp_socket_t; + +static int32_t tcp_socket_backlog_len(tcp_socket_t* s) { + if (!s) return 0; + + irq_flags_t irq = irq_save_disable(); + int32_t len = s->backlogLen; + irq_restore(irq); + return len; +} + +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(); + if (idx < 0 || idx >= s->backlogLen) { + irq_restore(irq); + return NULL; + } + + ksocket_t* client = s->pending[idx]; + 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) { + tcp_socket_reset_connection(s, false); + return -1; + } + + tcp_flow_t* flow = tcp_flow_from_ctx(&s->flow); + if (!flow) { + tcp_socket_reset_connection(s, false); + 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) { + if (tcp_flow_release_closed(&s->flow) == TCP_BUSY) return 0; + tcp_socket_reset_connection(s, false); + 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 | 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; + 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; + + s->ownerSocket = owner; + 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_RCV_BUF; + } + + s->options.buf_size = tcp_clamp_rcvbuf(s->options.buf_size); + 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; + if (s->options.flags & SOCK_OPT_TCP_DSACK) s->options.flags |= SOCK_OPT_TCP_SACK; + 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->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, (uint32_t)opt); + 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->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, (uint32_t)opt); + 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->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, (uint32_t)opt); + 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->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, (uint32_t)opt); + 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_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; + memcpy(&v, value, sizeof(v)); + 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, (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)); + 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_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_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, (uint32_t)opt); + 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; + + 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: { + 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); + } + 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; + } + + uint32_t v = 0; + switch ((uint32_t)opt) { + case SOCK_GET_BOUND: + v = s->localPort != 0; + break; + case SOCK_GET_CONNECTED: + v = tcp_socket_connection_state(s) > 0; + break; + case SOCK_GET_LISTENING: + v = s->listening; + break; + case SOCK_GET_LOCAL_PORT: + v = s->localPort; + break; + 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 (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; + } + 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; + 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_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: + return SOCK_ERR_UNSUP; + 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_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; + } + + 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) { + 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->options, &ev); + + if (s->localPort) return SOCK_ERR_BOUND; + if (!s->ownerSocket) return SOCK_ERR_SYS; + + SockBindSpec spec = *spec_in; + if (!socket_bind_prepare_spec(&spec, PROTO_TCP)) return SOCK_ERR_INVAL; + + 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); + if (bind_port < 0) return SOCK_ERR_NO_PORT; + } 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; + 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; + + 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(); + 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; + 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 < valid_old_len; ++i) { + if (!old_pending[i]) continue; + tcp_socket_abort_pending(old_pending[i]); + } + if (old_pending) release(old_pending); + 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; + + uint32_t start_ms = get_time(); + while (tcp_socket_backlog_len(s) == 0) { + 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); + } + + 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 (client && client->flow.flow_generation) return owner; + + if (owner) { + tcp_socket_abort_pending(owner); + } + } + + return NULL; +} + +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->options, &ev); + + 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) { + 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; + chosen_l3 = p6.l3_id; + } 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; + chosen_l3 = p4.l3_id; + } else return SOCK_ERR_INVAL; + + if (!chosen_l3) return SOCK_ERR_NO_ROUTE; + + 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; + } 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; + 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; + ephemeral_allocated = true; + } + + memset(&s->flow, 0, sizeof(s->flow)); + 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) { + socket_bind_remove(s->bindToken); + s->bindToken = 0; + } + s->localPort = 0; + memset(&s->bindSpec, 0, sizeof(s->bindSpec)); + s->bindSpec.kind = BIND_ANY; + } + return SOCK_ERR_SYS; + } + + s->remoteEP = d; + } + + 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) { + tcp_socket_reset_connection(s, true); + return SOCK_ERR_WOULDBLOCK; + } + msleep(5); + } + + 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->options, &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->options, &ev); + + 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; + + uint32_t chunk = len > UINT32_MAX ? UINT32_MAX : (uint32_t)len; + 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) 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; + + 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); + } +} + +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->options, &ev); + + if (!buf || !len) return 0; + 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; + + //TODO add receive low water mark support when socket events exist + uint32_t start_ms = (uint32_t)get_time(); + while (1) { + 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); + } +} + +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; + 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->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 && !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_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); + } + + 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_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); + } + } + } + } + + if (s->pending) { + for (int32_t i = 0; i < s->backlogLen; ++i) { + if (!s->pending[i]) continue; + tcp_socket_abort_pending(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->bindSpec.kind = BIND_ANY; + tcp_socket_reset_connection(s, false); + s->closed = true; + return SOCK_OK; +} + +void socket_destroy_tcp(socket_impl_t sh) { + tcp_socket_t* s = (tcp_socket_t*)sh; + if (!s) return; + 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); +} + +const SocketOptions* socket_tcp_options(socket_impl_t sh) { + tcp_socket_t* s = (tcp_socket_t*)sh; + if (!s) return NULL; + 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) { + 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 (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); + + 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); + if (!closed || readable) { + ++i; + continue; + } + } + + owner = tcp_socket_pop_pending_at(s, i); + if (owner) tcp_socket_abort_pending(owner); + } + if (tcp_socket_backlog_len(s) >= s->backlogCap) return 0; + + ksocket_t* child_owner = NULL; + 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, 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; + memset(child->remoteEP.ip, 0, sizeof(child->remoteEP.ip)); + if (ipver == IP_VER4) memcpy(child->remoteEP.ip, src_ip_addr, 4); + 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) 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); + 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)) { + tcp_socket_reset_connection(child, true); + socket_destroy_tcp(child); + socket_core_close_socket(child_owner); + return 0; + } + + child->connected = true; + socket_core_ref(child_owner); + + irq_flags_t irq = irq_save_disable(); + if (s->backlogLen >= s->backlogCap) { + irq_restore(irq); + tcp_socket_abort_pending(child_owner); + return 0; + } + s->pending[s->backlogLen++] = child_owner; + irq_restore(irq); + return 1; +} diff --git a/kernel/networking/transport_layer/csocket_tcp.cpp b/kernel/networking/transport_layer/csocket_tcp.cpp deleted file mode 100644 index 63659d87..00000000 --- a/kernel/networking/transport_layer/csocket_tcp.cpp +++ /dev/null @@ -1,88 +0,0 @@ -#include "networking/transport_layer/socket_tcp.hpp" -#include "networking/transport_layer/socket.hpp" -#include "csocket_tcp.h" - -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)); -} - -int32_t socket_bind_tcp_ex(socket_handle_t sh, const SockBindSpec* spec, uint16_t port) { - if (!sh || !spec) return SOCK_ERR_INVAL; - return reinterpret_cast(sh)->bind(*spec, port); -} - -int32_t socket_listen_tcp(socket_handle_t sh, int32_t backlog) { - if (!sh) return SOCK_ERR_INVAL; - return reinterpret_cast(sh)->listen(backlog); -} - -socket_handle_t socket_accept_tcp(socket_handle_t sh) { - if (!sh) return nullptr; - TCPSocket* srv = reinterpret_cast(sh); - TCPSocket* client = srv->accept(); - return reinterpret_cast(client); -} - -int32_t socket_connect_tcp_ex(socket_handle_t sh, uint8_t dst_kind, const void* dst, uint16_t port) { - if (!sh || !dst) return SOCK_ERR_INVAL; - return reinterpret_cast(sh)->connect(static_cast(dst_kind), dst, port); -} - -int64_t socket_send_tcp(socket_handle_t sh, const void* buf, uint64_t len) { - if (!sh) return SOCK_ERR_INVAL; - return reinterpret_cast(sh)->send(buf, len); -} - -int64_t socket_recv_tcp(socket_handle_t sh, void* buf, uint64_t len) { - if (!sh) return SOCK_ERR_INVAL; - return reinterpret_cast(sh)->recv(buf, len); -} - -int32_t socket_close_tcp(socket_handle_t sh) { - if (!sh) return SOCK_ERR_INVAL; - return reinterpret_cast(sh)->close(); -} - -void socket_destroy_tcp(socket_handle_t sh) { - if (!sh) return; - delete reinterpret_cast(sh); -} - -uint16_t socket_get_local_port_tcp(socket_handle_t sh) { - if (!sh) return 0; - return reinterpret_cast(sh)->get_local_port(); -} - -uint16_t socket_get_remote_port_tcp(socket_handle_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) { - if (!sh || !out) return; - *out = reinterpret_cast(sh)->get_remote_ep(); -} - -uint8_t socket_get_protocol_tcp(socket_handle_t sh) { - if (!sh) return 0xFF; - return reinterpret_cast(sh)->get_protocol(); -} - -uint8_t socket_get_role_tcp(socket_handle_t sh) { - if (!sh) return 0xFF; - return reinterpret_cast(sh)->get_role(); -} - -bool socket_is_bound_tcp(socket_handle_t sh) { - if (!sh) return false; - return reinterpret_cast(sh)->is_bound(); -} - -bool socket_is_connected_tcp(socket_handle_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..e75d549e 100644 --- a/kernel/networking/transport_layer/csocket_tcp.h +++ b/kernel/networking/transport_layer/csocket_tcp.h @@ -1,32 +1,26 @@ #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_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); +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); +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 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); #ifdef __cplusplus } diff --git a/kernel/networking/transport_layer/csocket_udp.c b/kernel/networking/transport_layer/csocket_udp.c new file mode 100644 index 00000000..75304611 --- /dev/null +++ b/kernel/networking/transport_layer/csocket_udp.c @@ -0,0 +1,964 @@ +#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" +#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 "syscalls/syscalls.h" +#include "std/memory.h" +#include "alloc/allocate.h" +#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; + SockBindSpec rx_spec; +} udp_rx_entry_t; + +typedef struct udp_socket { + uint16_t localPort; + net_l4_endpoint remoteEP; + bool connected; + ksocket_t* ownerSocket; + SocketOptions options; + SockBindSpec bindSpec; + SockBindSpec lastRxSpec; + socket_bind_token_t bindToken; + udp_rx_entry_t* rx_ring; + 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) { + 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 || !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]; + 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 && ipv6_cmp(group->ip, dst_ip_addr) == 0) return true; + } + + return false; +} + +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) { + 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 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) { + 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; + } + + if (!l2->ifindex || l2->ifindex > MAX_L2_INTERFACES) continue; + targets |= (uint16_t)(1u << (l2->ifindex - 1)); + } + + 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, 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(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(spec, &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->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), &spec, &token); + if (port < 0) return SOCK_ERR_NO_PORT; + + 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->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 || !src_ip_addr || !dst_ip_addr) return 0; + if (ipver != IP_VER4 && ipver != IP_VER6) 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) { + usable = s->options.buf_size / MAX_PACKET_SIZE; + if (usable < 4) usable = 4; + if (usable > UDP_MAX_RING_CAP) usable = UDP_MAX_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->closed && !s->rx_ring) { + 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->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; + } + + uint32_t nexti = (s->r_tail + 1) % s->ring_cap; + if (nexti == s->r_head) { + irq_restore(irq); + return 0; + } + + netpkt_ref(pkt); + s->rx_ring[s->r_tail] = entry; + s->rx_bytes += pkt_len; + s->r_tail = nexti; + irq_restore(irq); + return pkt_len; +} + +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_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; + 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->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); + 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) { + 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->options, &ev); + + if (s->closed) return SOCK_ERR_STATE; + if (s->localPort) return SOCK_ERR_BOUND; + if (!s->ownerSocket) return SOCK_ERR_SYS; + + SockBindSpec spec = *spec_in; + if (!socket_bind_prepare_spec(&spec, PROTO_UDP)) return SOCK_ERR_INVAL; + + 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); + 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; + irq_restore(irq); + 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->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; + + 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; + } + + 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; + 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; + 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->options, &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 (!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; + if (d.ver == IP_VER6 && ipv6_cmp(d.ip, s->remoteEP.ip) != 0) return SOCK_ERR_STATE; + } + + 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; + 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->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; + 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->options.flags & SOCK_OPT_BROADCAST_ALLOWED)) return SOCK_ERR_PERM; + } + + if (bcast_v4) { + 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); + + ip_tx_opts_t tx; + tx.scope = IP_TX_BOUND_L3; + tx.index = chosen_l3; + + if (!udp_send_segment(&src, &d, pay, &tx, ttl, dontfrag)) return SOCK_ERR_SYS; + return (int64_t)len; + } + + 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; + + 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_NO_ROUTE; + + 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); + + ip_tx_opts_t tx; + tx.scope = IP_TX_BOUND_L3; + tx.index = plan.l3_id; + + if (!udp_send_segment(&src, &d, pay, &tx, ttl, dontfrag)) return SOCK_ERR_SYS; + 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 != 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_NO_ROUTE; + + 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); + + ip_tx_opts_t tx; + tx.scope = IP_TX_BOUND_L3; + tx.index = plan.l3_id; + + if (!udp_send_segment(&src, &d, pay, &tx, ttl, dontfrag)) return SOCK_ERR_SYS; + 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->options, &ev); + + uint32_t start_ms = 0; + netpkt_t* p = NULL; + net_l4_endpoint se = {0}; + + 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 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; + } + + bool closed = s->closed; + uint32_t flags = s->options.flags; + uint32_t timeout_ms = s->options.recv_timeout_ms; + irq_restore(irq); + + 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; + 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; + if (s->closed) return SOCK_ERR_STATE; + + 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_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: + return SOCK_ERR_UNSUP; + case SOCK_OPT_REUSEADDR: + case SOCK_OPT_REUSEPORT: + if (s->localPort) return SOCK_ERR_STATE; + break; + case SOCK_OPT_NONBLOCK: + case SOCK_OPT_DONTROUTE: + 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); + 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 < count; ++i) if (!udp_socket_mcast_endpoint_valid(&groups[i])) return SOCK_ERR_INVAL; + + 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) { + if (next_groups) release(next_groups); + if (next_ifmasks) release(next_ifmasks); + return SOCK_ERR_SYS; + } + + 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 = old_count; + for (uint32_t i = 0; i < count; ++i) { + bool exists = false; + for (uint8_t j = 0; j < next_count; ++j) { + 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) 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 > old_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 == old_count) { + release(next_groups); + release(next_ifmasks); + return SOCK_OK; + } + + 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: { + 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->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; + + 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); + return SOCK_ERR_SYS; + } + + uint8_t next_count = 0; + 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; + 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; + } + } + 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 == old_count) { + release(next_groups); + release(next_ifmasks); + return SOCK_OK; + } + + if (!next_count) { + release(next_groups); + release(next_ifmasks); + next_groups = NULL; + next_ifmasks = NULL; + } + + 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 (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: + 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: + break; + case SOCK_OPT_BUF_SIZE: + if (s->localPort) return SOCK_ERR_STATE; + 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) { + udp_socket_t* s = (udp_socket_t*)sh; + if (!s || !len) return SOCK_ERR_INVAL; + + switch ((uint32_t)opt) { + 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; + break; + case SOCK_GET_CONNECTED: + v = s->connected; + break; + case SOCK_GET_LISTENING: + irq_restore(irq); + return SOCK_ERR_UNSUP; + 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_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: + case SOCK_GET_TCP_STATE: + 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: + irq_restore(irq); + return SOCK_ERR_INVAL; + } + + irq_restore(irq); + return socket_common_get_value(&v, sizeof(v), value, len); +} + +int32_t socket_close_udp(socket_impl_t sh) { + 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_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; + + 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; + s->bindToken = 0; + s->localPort = 0; + memset(&s->bindSpec, 0, sizeof(s->bindSpec)); + s->bindSpec.kind = BIND_ANY; + s->connected = false; + memset(&s->remoteEP, 0, sizeof(s->remoteEP)); + s->remoteEP.ver = IP_VER4; + 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; +} + +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); +} diff --git a/kernel/networking/transport_layer/csocket_udp.cpp b/kernel/networking/transport_layer/csocket_udp.cpp deleted file mode 100644 index 3dc04248..00000000 --- a/kernel/networking/transport_layer/csocket_udp.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#include "networking/transport_layer/socket_udp.hpp" -#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" int32_t socket_bind_udp_ex(socket_handle_t sh, const SockBindSpec* spec, uint16_t port) { - if (!sh || !spec) return SOCK_ERR_INVAL; - return reinterpret_cast(sh)->bind(*spec, port); -} - -extern "C" int64_t socket_sendto_udp_ex(socket_handle_t sh, uint8_t dst_kind, const void* dst, uint16_t port, const void* buf, uint64_t len) { - if (!sh || !dst || !buf || !len) return SOCK_ERR_INVAL; - return reinterpret_cast(sh)->sendto(static_cast(dst_kind), dst, port, buf, len); -} - -extern "C" int64_t socket_recvfrom_udp_ex(socket_handle_t sh, void* buf, uint64_t len, net_l4_endpoint* out_src) { - if (!sh || !buf || !len) return 0; - return reinterpret_cast(sh)->recvfrom(buf, len, out_src); -} - -extern "C" int32_t socket_close_udp(socket_handle_t sh) { - if (!sh) return SOCK_ERR_INVAL; - return reinterpret_cast(sh)->close(); -} - -extern "C" void socket_destroy_udp(socket_handle_t sh) { - if (!sh) return; - delete reinterpret_cast(sh); -} - -extern "C" uint16_t socket_get_local_port_udp(socket_handle_t sh) { - if (!sh) return 0; - return reinterpret_cast(sh)->get_local_port(); -} - -extern "C" uint16_t socket_get_remote_port_udp(socket_handle_t sh) { - if (!sh) return 0; - return reinterpret_cast(sh)->get_remote_port(); -} - -extern "C" void socket_get_remote_ep_udp(socket_handle_t sh, net_l4_endpoint* out) { - if (!sh || !out) return; - *out = reinterpret_cast(sh)->get_remote_ep(); -} - -extern "C" uint8_t socket_get_protocol_udp(socket_handle_t sh) { - if (!sh) return 0xFF; - return reinterpret_cast(sh)->get_protocol(); -} - -extern "C" uint8_t socket_get_role_udp(socket_handle_t sh) { - if (!sh) return 0xFF; - return reinterpret_cast(sh)->get_role(); -} - -extern "C" bool socket_is_bound_udp(socket_handle_t sh) { - if (!sh) return false; - return reinterpret_cast(sh)->is_bound(); -} - -extern "C" bool socket_is_connected_udp(socket_handle_t sh) { - if (!sh) return false; - return reinterpret_cast(sh)->is_connected(); -} diff --git a/kernel/networking/transport_layer/csocket_udp.h b/kernel/networking/transport_layer/csocket_udp.h index 479fefcf..f6208b8e 100644 --- a/kernel/networking/transport_layer/csocket_udp.h +++ b/kernel/networking/transport_layer/csocket_udp.h @@ -1,30 +1,24 @@ #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_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_close_udp(socket_handle_t sh); -void socket_destroy_udp(socket_handle_t sh); - -uint16_t socket_get_local_port_udp(socket_handle_t sh); -uint16_t socket_get_remote_port_udp(socket_handle_t sh); -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); +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); +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, 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/net_ctrl.c b/kernel/networking/transport_layer/net_ctrl.c new file mode 100644 index 00000000..58eeac2b --- /dev/null +++ b/kernel/networking/transport_layer/net_ctrl.c @@ -0,0 +1,648 @@ +#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/link_utils.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[MAC_ADDR_LEN]; + 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_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)) { + 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 && !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; +} + +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)); + 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]; + 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); + mac_copy(info.mac, ne[j].mac); + 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, 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; + 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); + 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; +} + +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: + status = net_ctrl_route_apply(&attrs, true); + break; + case NET_CTRL_OP_UPD: + status = net_ctrl_route_apply(&attrs, false); + 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: + status = net_ctrl_neigh_set(&attrs, true); + break; + case NET_CTRL_OP_UPD: + status = net_ctrl_neigh_set(&attrs, false); + 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 diff --git a/kernel/networking/transport_layer/socket.hpp b/kernel/networking/transport_layer/socket.hpp deleted file mode 100644 index 2c7a3030..00000000 --- a/kernel/networking/transport_layer/socket.hpp +++ /dev/null @@ -1,101 +0,0 @@ -#pragma once -#include "types.h" -#include "net/network_types.h" -#include "networking/port_manager.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 - -class Socket { -protected: - static constexpr int SOCK_MAX_L3 = 32; - - uint16_t localPort = 0; - net_l4_endpoint remoteEP = { IP_VER4, {0}, 0 }; - - uint8_t proto = 0; - uint8_t role = 0; - bool bound = false; - bool connected = false; - uint16_t pid = 0; - - SocketExtraOptions extraOpts = {}; - - 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) { - 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(); } - - 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(); - } - 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; } - uint8_t get_protocol() const { return proto; } - uint8_t get_role() const { return role; } - uint16_t get_pid() const { return pid; } - bool is_bound() const { return bound; } - bool is_connected() const { return connected; } - - ip_version_t get_remote_ip_version() const { return remoteEP.ver; } - const uint8_t* get_remote_ip_bytes() const { return remoteEP.ip; } - const net_l4_endpoint& get_remote_endpoint() const { return remoteEP; } - - int get_bound_l3_count() const { return bound_l3_count; } - uint8_t get_bound_l3_id(int idx) const { return (idx >= 0 && idx < bound_l3_count) ? bound_l3[idx] : 0; } -}; - -#endif diff --git a/kernel/networking/transport_layer/socket_bind.c b/kernel/networking/transport_layer/socket_bind.c new file mode 100644 index 00000000..a2333cd7 --- /dev/null +++ b/kernel/networking/transport_layer/socket_bind.c @@ -0,0 +1,628 @@ +#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" +#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 +#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 { + uint16_t port; + uint16_t next; + uint16_t generation; + uint8_t flags; + SockBindSpec spec; + net_l4_endpoint remote; + 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 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; + 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; +} + +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; + } + + 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; + 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) { + 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) { + 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; + return true; + } + + if (!spec->ver && ipv6_is_unspecified(spec->ip)) { + memset(spec, 0, sizeof(*spec)); + spec->kind = BIND_ANY; + return true; + } + + 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; + + SockBindSpec normal = *spec; + if (!socket_bind_normalize_spec(&normal)) return 0; + + 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) continue; + + if (ver == IP_VER4) { + 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 (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; + } + } + } + + return count; +} + +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; +} + +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); + } + + 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(spec, port, allow_reuse && (flags & SOCKET_BIND_FLAG_REUSEADDR))) { + 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->socket || other->port != port) continue; + if (!socket_bind_specs_overlap(spec, &other->spec)) continue; + + bool allowed = allow_reuse && (flags & SOCKET_BIND_FLAG_REUSEADDR) && (other->flags & SOCKET_BIND_FLAG_REUSEADDR); + if (protocol == PROTO_UDP && !allowed) { + 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->flags & SOCKET_BIND_FLAG_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); + if (bind_entries[cand].socket) continue; + idx = cand; + bind_next_alloc = (uint16_t)((cand + 1) % SOCKET_BIND_MAX); + break; + } + if (idx == SOCKET_BIND_LIST_END) { + irq_restore(irq); + return false; + } + + socket_bind_entry_t* entry = &bind_entries[idx]; + uint16_t generation = (uint16_t)(entry->generation + 1); + if (!generation) generation = 1; + 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(entry, 0, sizeof(*entry)); + entry->generation = generation; + entry->next = SOCKET_BIND_LIST_END; + irq_restore(irq); + return false; + } + + socket_core_ref(socket); + if (out_token) *out_token = ((uint32_t)generation << 16) | (uint32_t)(idx + 1); + + irq_restore(irq); + 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; + + uint32_t idxplus = (uint16_t)token; + uint32_t generation = token >> 16; + if (!idxplus || idxplus > SOCKET_BIND_MAX || !generation) return false; + + 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->socket || entry->generation != generation || socket_core_protocol(entry->socket) != PROTO_TCP) { + irq_restore(irq); + return false; + } + if (entry->flags & SOCKET_BIND_FLAG_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->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->flags |= SOCKET_BIND_FLAG_LISTENING; + irq_restore(irq); + return true; +} + +void socket_bind_remove(socket_bind_token_t token) { + 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); + ksocket_t* drop = NULL; + + if (!bind_map) return; + + irq_flags_t irq = irq_save_disable(); //TODO lock + socket_bind_entry_t* e = &bind_entries[idx]; + 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; + 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; + } + + 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); + + if (drop) socket_core_put(drop); +} + +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->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); +} + +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 || !socket_bind_prepare_spec(spec, protocol)) return -1; + + 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 + 1; + uint32_t first = minp + (seed % range); + + for (uint32_t i = 0; i < range; ++i) { + uint16_t port = (uint16_t)(minp + ((first - minp + i) % range)); + if (socket_bind_insert_prepared(socket, protocol, spec, port, options, false, out_token)) return port; + } + + 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)); + spec.kind = BIND_L3; + spec.ver = l3_is_v6_from_id(l3_id) ? IP_VER6 : IP_VER4; + spec.l3_id = l3_id; + + 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; + if (!bind_map) return NULL; + + 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); + uint8_t best_score = 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->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) { + 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->flags & SOCKET_BIND_FLAG_REUSEPORT); + if (score > best_score) { + best_score = score; + reuseport_group = reuseport; + reuseport_owner = socket_core_pid(e->socket); + reuseport_hash = 0; + first = e; + reuseport_selected = NULL; + } 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; + } + + 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; + } + } + } + + 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); + 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->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; + + *cursor = i + 1; + socket_core_ref(e->socket); + irq_restore(irq); + return e->socket; + } + + *cursor = SOCKET_BIND_MAX; + irq_restore(irq); + 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; + const ip_tx_opts_t* hint = NULL; + 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); +} + +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; + const ip_tx_opts_t* hint = NULL; + 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); +} \ 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..9e2feaaf --- /dev/null +++ b/kernel/networking/transport_layer/socket_bind.h @@ -0,0 +1,35 @@ +#pragma once + +#include "types.h" +#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" { +#endif + +#define SOCKET_BIND_MAX 1024 +typedef uint32_t socket_bind_token_t; + +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); +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); + +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 +} +#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..78e56470 --- /dev/null +++ b/kernel/networking/transport_layer/socket_core.c @@ -0,0 +1,370 @@ +#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; + 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; + 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 uint64_t generations[SOCKET_MAX_OPEN]; + +bool socket_core_alloc(protocol_t protocol, SocketSpecialKind special_kind, uint16_t pid, ksocket_t** out_socket) { + if (!out_socket) 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; + } + + uint64_t gen = generations[id] + 1; + if (!gen) gen = 1; + generations[id] = gen; + + socket->id = id; + socket->generation = gen; + socket->pid = pid; + socket->protocol = protocol; + socket->special_kind = special_kind; + 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) { + 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); + + return true; +} + +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[id]; + if (!socket || !socket->visible || socket->closing || socket->generation != generation || (pid && socket->pid != pid)) { + 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; + + irq_flags_t irq = irq_save_disable(); + if (socket->closing) { + irq_restore(irq); + return SOCK_OK; + } + socket->closing = true; + irq_restore(irq); + + 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; +} + +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); + return ret; +} + +int32_t socket_core_set_option(ksocket_t* socket, int32_t opt, const void* value, uint32_t len) { + 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 || !len) return SOCK_ERR_INVAL; + if (!socket->getopt || !socket->impl) return SOCK_ERR_PROTO; + + 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); + + 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 = data_len; + return SOCK_OK; + } + if (*len < data_len) return SOCK_ERR_INVAL; + if (data_len) memcpy(value, data, data_len); + *len = data_len; + return SOCK_OK; +} + +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; + 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; + + 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; + 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; + break; + case SOCK_OPT_BUF_SIZE: + 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) 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; + break; + case SOCK_OPT_BROADCAST_ALLOWED: + if (v) opts->flags |= SOCK_OPT_BROADCAST_ALLOWED; + else opts->flags &= ~SOCK_OPT_BROADCAST_ALLOWED; + break; + case SOCK_OPT_NONBLOCK: + if (v) opts->flags |= SOCK_OPT_NONBLOCK; + else opts->flags &= ~SOCK_OPT_NONBLOCK; + break; + + case SOCK_OPT_DONTROUTE: + if (v) opts->flags |= SOCK_OPT_DONTROUTE; + else opts->flags &= ~SOCK_OPT_DONTROUTE; + break; + case SOCK_OPT_REUSEADDR: + if (v) opts->flags |= SOCK_OPT_REUSEADDR; + else opts->flags &= ~SOCK_OPT_REUSEADDR; + break; + case SOCK_OPT_REUSEPORT: + if (v) opts->flags |= SOCK_OPT_REUSEPORT; + else opts->flags &= ~SOCK_OPT_REUSEPORT; + 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) 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: + 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; + break; + case SOCK_GET_OPT_SEND_TIMEOUT: + v = opts->send_timeout_ms; + break; + case SOCK_GET_OPT_BUF_SIZE: + v = opts->buf_size; + break; + case SOCK_GET_OPT_DEBUG: + v = opts->debug_level; + break; + case SOCK_GET_OPT_DONTFRAG: + v = (opts->flags & SOCK_OPT_DONTFRAG) != 0; + break; + 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_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_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: + rc = SOCK_ERR_INVAL; + break; + } + irq_restore(irq); + + if (rc != SOCK_OK) return rc; + return socket_common_get_value(&v, sizeof(v), 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; +} + +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; +} + + +bool socket_core_is_closing(const ksocket_t* socket) { + return !socket || socket->closing; +} + +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 new file mode 100644 index 00000000..33e8de1f --- /dev/null +++ b/kernel/networking/transport_layer/socket_core.h @@ -0,0 +1,44 @@ +#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 +#define SOCKET_HANDLE_INDEX_BITS 20 + +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); +void socket_core_put(ksocket_t* socket); +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_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); +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); + +#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..9cd7d6d1 --- /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 deleted file mode 100755 index 2c749317..00000000 --- a/kernel/networking/transport_layer/socket_tcp.hpp +++ /dev/null @@ -1,724 +0,0 @@ -#pragma once - -#include "std/memory.h" -#include "std/string.h" -#include "socket.hpp" -#include "networking/transport_layer/tcp.h" -#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" -#include "networking/internet_layer/ipv6_utils.h" -#include "networking/transport_layer/trans_utils.h" -#include "syscalls/syscalls.h" - -static constexpr int TCP_MAX_BACKLOG = 8; -static constexpr dns_server_sel_t TCP_DNS_SEL = DNS_USE_BOTH; -static constexpr uint32_t TCP_DNS_TIMEOUT_MS = 3000; - -class TCPSocket : public Socket { - inline static TCPSocket* s_list_head = nullptr; - - static constexpr uint32_t TCP_RING_CAP = 256 * 1024; - RingBuffer ring; - tcp_data* flow = nullptr; - - TCPSocket* 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->is_localhost) return false; - if (v4->mode == IPV4_CFG_DISABLED) return false; - if (v4->ip == 0) return false; - if (!v4->port_manager) return false; - return true; - } - - static bool 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; - 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, uintptr_t frame_ptr, uint32_t frame_len, uint16_t src_port, uint16_t dst_port) { - if (frame_len == 0){ - for (TCPSocket* srv = s_list_head; srv; srv = srv->next){ - if (srv->role != SOCK_ROLE_SERVER) continue; - if (!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; - if (v4->ip == *(const uint32_t*)dst_ip_addr) { - 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; - if (srv->backlogLen >= srv->backlogCap) break; - - TCPSocket* child = new TCPSocket(SOCK_ROLE_CLIENT, srv->pid, &srv->extraOpts); - - child->localPort = dst_port; - child->connected = true; - - child->remoteEP.ver = ipver; - memset(child->remoteEP.ip, 0, 16); - if (ipver == IP_VER4) memcpy(child->remoteEP.ip, src_ip_addr, 4); - else memcpy(child->remoteEP.ip, src_ip_addr, 16); - child->remoteEP.port = src_port; - - uint8_t l3id = 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; - if (v4->ip == *(const uint32_t*)dst_ip_addr) { - 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); - } - } - - 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(); - - srv->pending[srv->backlogLen++] = child; - break; - } - 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; - if (v4->ip == *(const uint32_t*)dst_ip_addr) { - 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) { - if (*(const uint32_t*)s->remoteEP.ip != *(const uint32_t*)src_ip_addr) continue; - } else { - if (memcmp(s->remoteEP.ip, src_ip_addr, 16) != 0) continue; - } - - return s->on_receive(frame_ptr, frame_len); - } - - return 0; - } - - uint32_t on_receive(uintptr_t ptr, uint32_t len) { - 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){ - if (it == this) { - return; - } - } - next = s_list_head; - s_list_head = this; - } - - void remove_from_list() { - TCPSocket** cur = &s_list_head; - while (*cur) { - if (*cur == this) { - *cur = (*cur)->next; - break; - } - cur = &((*cur)->next); - } - next = nullptr; - } - - void do_unbind_one(uint8_t l3_id, uint16_t port, uint16_t pid_) override { - (void)pid_; - if (role != SOCK_ROLE_SERVER) return; - (void)tcp_unbind_l3(l3_id, port, pid); - } - - bool add_all_l3_on_l2(uint8_t ifindex, 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; - } - } - - 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) { - 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; - } - } - - 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; - } - -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; - } - - if (!extraOpts.buf_size) extraOpts.buf_size = TCP_RING_CAP; - if (extraOpts.buf_size > TCP_RING_CAP) extraOpts.buf_size = TCP_RING_CAP; - insert_in_list(); - } - - ~TCPSocket() override { - remove_from_list(); - close(); - } - - 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 = pid; - ev.u0 = port; - ev.bind_spec = spec_in; - netlog_socket_event(&extraOpts, &ev); - if (role != SOCK_ROLE_SERVER) return SOCK_ERR_PERM; - if (bound) return SOCK_ERR_BOUND; - - 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 = is_valid_v4_l3_for_bind(v4); - bool ok6 = 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, 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; - } 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; - } else return SOCK_ERR_INVAL; - } else if (spec.kind == BIND_ANY){ - if (!add_all_l3_any(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; - - 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; - backlogLen = 0; - return SOCK_OK; - } - - TCPSocket* accept(){ - const int max_iters = 100; - int iter = 0; - - while (backlogLen == 0){ - if (++iter > max_iters) return nullptr; - msleep(10); - } - - TCPSocket* client = pending[0]; - - for (int i = 1; i < backlogLen; ++i) pending[i - 1] = pending[i]; - pending[--backlogLen] = nullptr; - - return client; - } - - int32_t connect(SockDstKind kind, const void* dst, uint16_t port) { - 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; - netlog_socket_event(&extraOpts, &ev); - if (role != SOCK_ROLE_CLIENT) return SOCK_ERR_PERM; - if (connected) return SOCK_ERR_STATE; - if (!dst) return SOCK_ERR_INVAL; - - net_l4_endpoint d{}; - 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; - } 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; - - if (localPort == 0) { - int p = tcp_alloc_ephemeral_l3(chosen_l3, pid, dispatch); - if (p < 0) return SOCK_ERR_NO_PORT; - localPort = (uint16_t)p; - } - - clear_bound_l3(); - add_bound_l3(chosen_l3); - bound = true; - - tcp_data ctx_copy{}; - if (!tcp_handshake_l3(chosen_l3, localPort, &d, &ctx_copy, 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; - connected = true; - netlog_socket_event_t ev1{}; - ev1.comp = NETLOG_COMP_TCP; - ev1.action = NETLOG_ACT_CONNECTED; - ev1.pid = pid; - 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 = pid; - ev.u0 = (uint32_t)len; - 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; - return TCP_WOULDBLOCK; - } - - int64_t recv(void* buf, uint64_t len){ - netlog_socket_event_t ev{}; - ev.comp = NETLOG_COMP_TCP; - ev.action = NETLOG_ACT_RECV; - ev.pid = pid; - ev.u0 = (uint32_t)len; - ev.local_port = localPort; - ev.remote_ep = remoteEP; - netlog_socket_event(&extraOpts, &ev); - if (!buf || !len) return 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; - } - if (connected) return TCP_WOULDBLOCK; - return 0; - } - - int32_t close() override { - netlog_socket_event_t ev{}; - ev.comp = NETLOG_COMP_TCP; - ev.action = NETLOG_ACT_CLOSE; - ev.pid = pid; - ev.local_port = localPort; - ev.remote_ep = remoteEP; - netlog_socket_event(&extraOpts, &ev); - if (connected && flow){ - tcp_flow_close(flow); - connected = false; - flow = nullptr; - } - - ring.clear(); - for (int i = 0; i < backlogLen; ++i) delete pending[i]; - backlogLen = 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/socket_udp.hpp b/kernel/networking/transport_layer/socket_udp.hpp deleted file mode 100644 index da1aede9..00000000 --- a/kernel/networking/transport_layer/socket_udp.hpp +++ /dev/null @@ -1,778 +0,0 @@ -#pragma once -#include "socket.hpp" -#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" -#include "syscalls/syscalls.h" -#include "networking/internet_layer/ipv4_utils.h" -#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" - -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; - } - - 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; - if (!v6->port_manager) return false; - return true; - } - - 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; - } - - 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); - - 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); - - 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) 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, uintptr_t frame_ptr, uint32_t frame_len, uint16_t src_port, uint16_t dst_port) { - UDPSocket* first = nullptr; - uint32_t ret = frame_len; - 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); - } - - 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) { - 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; - } - - 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); - 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); - r_head = (r_head + 1) % UDP_RING_CAP; - } - - ring[r_tail].ptr = ptr; - ring[r_tail].size = len; - rx_bytes += len; - - src_eps[r_tail].ver = ver; - memset(src_eps[r_tail].ip, 0, 16); - - if (ver == IP_VER4) { - uint32_t v4 = *(const uint32_t*)src_ip_addr; - memcpy(src_eps[r_tail].ip, &v4, 4); - } else if (ver == IP_VER6) { - memcpy(src_eps[r_tail].ip, src_ip_addr, 16); - } - - src_eps[r_tail].port = src_port; - - r_tail = nexti; - remoteEP = src_eps[(r_tail + UDP_RING_CAP - 1) % UDP_RING_CAP]; - } - - 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, 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; - } - - 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) { - 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; - - 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; - } - } - return n > 0; - } - - void do_unbind_one(uint8_t l3_id, uint16_t port, uint16_t pid_) override { - (void)pid_; - udp_unbind_l3(l3_id, port, pid); - } - - static bool 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; - } - -public: - UDPSocket(uint8_t r, uint32_t pid_, const SocketExtraOptions* extra = nullptr) : Socket(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); - 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(); - } - - 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 = pid; - ev.u0 = port; - ev.bind_spec = spec_in; - netlog_socket_event(&extraOpts, &ev); - if (role != SOCK_ROLE_SERVER) return SOCK_ERR_PERM; - if (bound) return SOCK_ERR_BOUND; - - 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 = is_valid_v4_l3_for_bind(v4); - bool ok6 = 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; - } 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; - } 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; - } else { - return SOCK_ERR_INVAL; - } - } else if (spec.kind == BIND_ANY) { - if (!add_all_l3_any(ids, n)) return SOCK_ERR_INVAL; - } else { - return SOCK_ERR_INVAL; - } - - if (n == 0) return SOCK_ERR_INVAL; - - uint8_t dedup[SOCK_MAX_L3]; - int m = 0; - for (int i = 0; i < 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; - } - - 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)) { - 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); - } - } - } 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_join(v6->l2->ifindex, extraOpts.mcast_group); - } - } - } - } - - localPort = port; - bound = true; - return SOCK_OK; - } - - int64_t sendto(SockDstKind kind, const void* dst, uint16_t port, 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; - } - - 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 is_bcast = false; - if (dip == 0xFFFFFFFFu) is_bcast = true; - else { - uint8_t dummy = 0; - if (is_dbcast(dip, &dummy)) is_bcast = true; - } - - if (is_bcast) { - if (dip == 0xFFFFFFFFu) { - if (bound_l3_count == 0) return SOCK_ERR_SYS; - - for (int i = 0; i < bound_l3_count; ++i) { - uint8_t bl3 = bound_l3[i]; - l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(bl3); - if (!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; - } - - 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 = bl3; - - 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 db_l3 = 0; - if (!is_dbcast(dip, &db_l3)) 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; - } - - 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 = db_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_l3_count == 0) return SOCK_ERR_SYS; - - for (int i = 0; i < bound_l3_count; ++i) { - uint8_t bl3 = bound_l3[i]; - l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(bl3); - if (!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; - } - - (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); - memcpy(src.ip, &v4->ip, 4); - src.port = localPort; - - ipv4_tx_opts_t tx; - tx.scope = IP_TX_BOUND_L3; - 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); - } - - remoteEP = d; - 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; - - ipv4_tx_plan_t plan; - if (!ipv4_build_tx_plan(dip, nullptr, n_allowed ? allowed_v4 : nullptr, n_allowed, &plan)) return SOCK_ERR_SYS; - - uint8_t chosen_l3 = plan.l3_id; - l3_ipv4_interface_t* v4 = l3_ipv4_find_by_id(chosen_l3); - if (!is_valid_v4_l3_for_bind(v4)) return SOCK_ERR_SYS; - - if (!bound) { - int p = udp_alloc_ephemeral_l3(chosen_l3, pid, dispatch); - if (p < 0) return SOCK_ERR_NO_PORT; - localPort = (uint16_t)p; - add_bound_l3(chosen_l3); - bound = true; - } else if (localPort == 0) { - int p = udp_alloc_ephemeral_l3(chosen_l3, pid, dispatch); - if (p < 0) return SOCK_ERR_NO_PORT; - localPort = (uint16_t)p; - } - - 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_scope_t)plan.fixed_opts.scope; - 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) return SOCK_ERR_BOUND; - if (!localPort) return SOCK_ERR_BOUND; - if (bound_l3_count == 0) return SOCK_ERR_SYS; - - for (int i = 0; i < bound_l3_count; ++i) { - uint8_t bl3 = bound_l3[i]; - l3_ipv6_interface_t* v6 = l3_ipv6_find_by_id(bl3); - if (!is_valid_v6_l3_for_bind(v6)) 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 = bl3; - - 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_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; - - 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; - - 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 = udp_alloc_ephemeral_l3(chosen_l3, pid, dispatch); - if (p < 0) return SOCK_ERR_NO_PORT; - localPort = (uint16_t)p; - add_bound_l3(chosen_l3); - bound = true; - } else if (localPort == 0) { - int p = udp_alloc_ephemeral_l3(chosen_l3, pid, dispatch); - if (p < 0) return SOCK_ERR_NO_PORT; - localPort = (uint16_t)p; - } - - 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_scope_t)plan.fixed_opts.scope; - 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; - } - - 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 = pid; - 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; - - sizedptr p = ring[r_head]; - net_l4_endpoint se = src_eps[r_head]; - r_head = (r_head + 1) % UDP_RING_CAP; - rx_bytes -= p.size; - - uint32_t tocpy = p.size; - if (tocpy > len) tocpy = (uint32_t)len; - - memcpy(buf, (void*)p.ptr, tocpy); - if (src) *src = se; - - free_sized((void*)p.ptr, p.size); - remoteEP = se; - return tocpy; - } - - int32_t close() override { - netlog_socket_event_t ev{}; - ev.comp = NETLOG_COMP_UDP; - ev.action = NETLOG_ACT_CLOSE; - ev.pid = pid; - ev.local_port = localPort; - ev.remote_ep = remoteEP; - 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); - r_head = (r_head + 1) % UDP_RING_CAP; - } - 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.h b/kernel/networking/transport_layer/tcp.h index 3498771f..78f4382a 100644 --- a/kernel/networking/transport_layer/tcp.h +++ b/kernel/networking/transport_layer/tcp.h @@ -1,8 +1,8 @@ #pragma once -#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" @@ -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; @@ -54,6 +49,9 @@ typedef struct { sizedptr payload; uint32_t expected_ack; uint32_t ack_received; + uint16_t flow_index; + uint16_t flow_reserved; + uint32_t flow_generation; } tcp_data; typedef enum { @@ -71,37 +69,34 @@ typedef enum { } tcp_state_t; #define MAX_TCP_FLOWS 512 -#define TCP_SYN_RETRIES 5 -#define TCP_DATA_RETRIES 5 -#define TCP_RETRY_TIMEOUT_MS 200 #define TCP_RECV_WINDOW 65535 -#define TCP_MAX_TX_SEGS 16 -#define TCP_INIT_RTO 200 +#define TCP_MAX_TX_SEGS 128 +#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 (2u * 60u * 60u * 1000u) #define TCP_2MSL_MS (2 * TCP_MSL_MS) #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); -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); +bool tcp_bind_conflicts(const SockBindSpec* spec, uint16_t port, bool reuseaddr); -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); +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, uint32_t apply_mask); 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); +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_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_tick_all(uint32_t elapsed_ms); -int tcp_daemon_entry(int argc, char *argv[]); +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 3d6f9a3b..fbffff18 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" @@ -7,155 +6,445 @@ #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 "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]; +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; +} -int find_flow(uint16_t local_port, ip_version_t ver, const void *local_ip, const void *remote_ip, uint16_t remote_port){ - for (int i = 0; i < MAX_TCP_FLOWS; i++){ - tcp_flow_t *f = tcp_flows[i]; - if (!f) continue; +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; - if (f->state == TCP_STATE_CLOSED) continue; - if (f->local_port != local_port) continue; - - if (f->state == TCP_LISTEN){ - if (remote_ip || remote_port) continue; - if (f->local.ver && f->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]){ - unspec = 0; - break; - } - } - if (unspec) return i; - if (memcmp(f->local.ip, local_ip, l) == 0) return i; - continue; - } + 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; +} - if (!remote_ip) continue; - if (!local_ip) continue; - if (f->remote.ver != ver) continue; - if (f->remote.port != remote_port) continue; +bool tcp_active_insert_flow(tcp_flow_t *flow) { + if (!flow) return false; - 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; + 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 || flow->base.active_pos != UINT16_MAX || tcp_active_count >= MAX_TCP_FLOWS) { + irq_restore(irq); + return false; + } - return i; + 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; + } } - return -1; + 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); + return true; } -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){ - int idx = find_flow(local_port, ver, local_ip, remote_ip, 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){ + 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++){ + 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.retired || f->base.state == TCP_STATE_CLOSED) continue; + if (f->base.local.port != local_port) continue; - if (idx < 0) return NULL; - return &tcp_flows[idx]->ctx; + 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++; + irq_restore(irq); + return f; + } + + irq_restore(irq); + return NULL; } -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); - - s->used = 0; - s->syn = 0; - s->fin = 0; - s->rtt_sample = 0; - s->retransmit_cnt = 0; - s->seq = 0; - s->len = 0; - s->buf = 0; - s->timer_ms = 0; - s->timeout_ms = 0; +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; + 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) { + 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); + if (!flow) return false; + *out_ctx = flow->base.ctx; + tcp_flow_put(flow); + return true; } -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); - } +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; - f->reass[i].seq = 0; - f->reass[i].end = 0; - f->reass[i].buf = 0; + irq_flags_t irq = irq_save_disable(); + tcp_flow_t *flow = tcp_flows[flow_ctx->flow_index]; + 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; } - f->reass_count = 0; - f->rcv_buf_used = 0; + flow->base.refs++; + irq_restore(irq); + return flow; } -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)); - if (!f) return NULL; - memset(f, 0, sizeof(tcp_flow_t)); - tcp_flows[i] = f; +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; + + 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(); + } - f->rto = TCP_INIT_RTO; - f->rcv_wnd_max = TCP_DEFAULT_RCV_BUF; - f->rcv_wnd = f->rcv_wnd_max; + 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 && flow->tx.snd_wnd > 0 && !tcp_flush_nagle(flow, 1)) tcp_daemon_kick(); + } - f->mss = TCP_DEFAULT_MSS; - f->cwnd = f->mss; - f->ssthresh = TCP_RECV_WINDOW; + 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; + } - clear_reass(f); - clear_txq(f); + 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); + } - return f; + 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; + } } - return NULL; + 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; } -void tcp_free_flow(int idx) { - if (idx < 0 || idx >= MAX_TCP_FLOWS) return; +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 (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) { + 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; + 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) { + 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; + } +} - tcp_flow_t *f = tcp_flows[idx]; +void tcp_flow_put(tcp_flow_t *f) { if (!f) return; - clear_txq(f); - clear_reass(f); + irq_flags_t irq = irq_save_disable(); + if (!f->base.refs) { + irq_restore(irq); + return; + } + + f->base.refs--; + bool free_now = f->base.refs == 0; + irq_restore(irq); + + if (!free_now) return; + tcp_flow_clear_storage(f); memset(f, 0, sizeof(*f)); + release(f); +} - f->state = TCP_STATE_CLOSED; +void tcp_enter_time_wait(tcp_flow_t *flow) { + if (!flow) return; + + uint32_t timewait = 0; + tcp_flow_t *oldest = NULL; + uint32_t oldest_ms = 0; + + irq_flags_t irq = irq_save_disable(); + 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 || 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) { + tcp_stats.timewait_reap_oldest++; + tcp_free_flow(oldest); + tcp_flow_put(oldest); + } - f->rto = TCP_INIT_RTO; + flow->base.state = TCP_TIME_WAIT; + flow->timer.time_wait_ms = 0; + tcp_flow_clear_storage(flow); + tcp_daemon_kick(); +} - f->rcv_wnd_max = TCP_DEFAULT_RCV_BUF; - f->rcv_wnd = f->rcv_wnd_max; +tcp_flow_t *tcp_alloc_flow(void){ + tcp_flow_t *f = (tcp_flow_t *)zalloc(sizeof(tcp_flow_t)); + if (!f) return NULL; + + irq_flags_t irq = irq_save_disable(); + 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); + break; + } + } - f->mss = TCP_DEFAULT_MSS; - f->cwnd = f->mss; - f->ssthresh = TCP_RECV_WINDOW; + if (slot < 0 || tcp_active_count >= MAX_TCP_FLOWS) { + irq_restore(irq); + release(f); + return NULL; + } - free_sized(f, sizeof(*f)); - tcp_flows[idx] = NULL; + 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 = 2; + 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.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.sack_enabled = 1; + f->tx.dsack_enabled = 1; + f->tx.cwnd = tcp_initial_cwnd(f->tx.mss); + f->tx.ssthresh = TCP_RECV_WINDOW; + return f; +} + +void tcp_free_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) { + irq_restore(irq); + return; + } + + flow->base.retired = 1; + flow->base.state = TCP_STATE_CLOSED; + + 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] != slot) { + pos = 0; + while (pos < tcp_active_count && tcp_active_flows[pos] != slot) pos++; + } + + if (pos < tcp_active_count) { + 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; + } + + flow->base.active_pos = UINT16_MAX; + } + + tcp_flows[slot] = NULL; + if (slot < tcp_alloc_cursor) tcp_alloc_cursor = slot; + irq_restore(irq); + + tcp_flow_put(flow); +} + +void tcp_flow_abort(tcp_data *flow_ctx) { + tcp_flow_t *flow = tcp_flow_from_ctx(flow_ctx); + 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); +} + +bool tcp_flow_is_closed(tcp_data *flow_ctx) { + tcp_flow_t *flow = tcp_flow_from_ctx(flow_ctx); + if (!flow) return true; + 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_TIME_WAIT) { + tcp_flow_put(flow); + return TCP_OK; + } + if (flow->base.state != TCP_STATE_CLOSED) { + tcp_flow_put(flow); + return TCP_BUSY; + } + tcp_free_flow(flow); + tcp_flow_put(flow); + 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; - - uint16_t tcp_len = (uint16_t)(sizeof(tcp_hdr_t) + opts_len + payload_len); - uint32_t headroom = (uint32_t)sizeof(eth_hdr_t) + (uint32_t)(ver == IP_VER4 ? sizeof(ipv4_hdr_t) : sizeof(ipv6_hdr_t)); + 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 > 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) + 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); @@ -176,23 +465,23 @@ 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; - - ((tcp_hdr_t *)segment)->checksum = tcp_checksum_ipv4(segment, tcp_len, s, d); - 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); - ipv6_send_packet((const uint8_t *)dst_ip_addr, 6, pkt, (const ipv6_tx_opts_t *)txp, ttl, dontfrag); - return true; + uint32_t s = 0; + uint32_t d = 0; + memcpy(&s, src_ip_addr, sizeof(s)); + memcpy(&d, dst_ip_addr, sizeof(d)); + + 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); } - 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); @@ -211,336 +500,178 @@ 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){ - ipv4_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); - } else if (ver == IP_VER6){ - ipv6_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); - } + 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){ 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; -} - -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->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_buf_used = 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; - } else { - f->ws_send = 0; - f->ws_recv = 0; - f->ws_ok = 0; - } - f->sack_ok = 1; - - (void)tcp_calc_adv_wnd_field(f, 1); - - f->ctx.options.ptr = 0; - f->ctx.options.size = 0; - f->ctx.payload.ptr = 0; - f->ctx.payload.size = 0; - - f->ctx.expected_ack = 0; - f->ctx.ack_received = 0; - - f->time_wait_ms = 0; - f->fin_wait2_ms = 0; - } - - return true; + flow->tx.rto = rto; } -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); -} - -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->state==TCP_LISTEN && f->local_port==port && f->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; +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 || !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; + flow->base.l3_id = l3_id; - 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; - - flow->local_port = local_port; - flow->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); if (!v4 || !v4->ip){ - tcp_free_flow(idx); + tcp_free_flow(flow); + tcp_flow_put(flow); 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); if (!v6 || ipv6_is_unspecified(v6->ip)){ - tcp_free_flow(idx); + tcp_free_flow(flow); + tcp_flow_put(flow); 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.active_open = 1; 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->ctx.sequence = iss; - flow->ctx.ack = 0; - - flow->rcv_nxt = 0; - flow->rcv_buf_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; - - 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; + 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; + flow->tx.path_mss = tcp_calc_mss_for_l3(l3_id, dst->ver, dst->ip); + flow->tx.peer_mss = 0; + tcp_flow_apply_options(flow, extra, UINT32_MAX); + + 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->tx.sack_ok = flow->tx.sack_enabled; + flow->rx.rcv_buf = 0; + tcp_update_adv_wnd(flow, 1); - (void)tcp_calc_adv_wnd_field(flow, 1); + 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->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->base.ctx.flags = (uint8_t)(1u << SYN_F); + flow->base.ctx.expected_ack = iss + 1; + flow->base.ctx.ack_received = 0; - flow->ctx.options.ptr = 0; - flow->ctx.options.size = 0; - flow->ctx.payload.ptr = 0; - flow->ctx.payload.size = 0; + flow->tx.snd_una = iss; + flow->tx.snd_nxt = iss; + flow->tx.snd_wnd = 0; - flow->ctx.flags = (uint8_t)(1u << SYN_F); - flow->ctx.expected_ack = iss + 1; - flow->ctx.ack_received = 0; + 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->snd_una = iss; - flow->snd_nxt = iss; - flow->snd_wnd = 0; + flow->timer.time_wait_ms = 0; + flow->timer.fin_wait2_ms = 0; - 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; - - clear_reass(flow); - clear_txq(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; } 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->snd_nxt; + 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->rto ? flow->rto : TCP_INIT_RTO; - - tcp_hdr_t syn_hdr; + seg->timeout_ms = flow->tx.rto ? flow->tx.rto : TCP_INIT_RTO; - 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; + 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); - 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); + flow->tx.snd_nxt += 1; + flow->base.ctx.sequence = flow->tx.snd_nxt; + flow->base.ctx.expected_ack = flow->tx.snd_nxt; - 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); - } - - flow->snd_nxt += 1; - flow->ctx.sequence = flow->snd_nxt; - flow->ctx.expected_ack = flow->snd_nxt; - - tcp_daemon_kick(); - - 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); - - while (waited < max_wait){ - if (flow->state == TCP_ESTABLISHED){ - tcp_data *ctx = tcp_get_ctx(local_port, dst->ver, flow->local.ip, dst->ip, dst->port); - if (!ctx) return false; - - *flow_ctx = *ctx; - return true; - } - - if (flow->state == TCP_STATE_CLOSED){ - tcp_free_flow(idx); - return false; - } - - msleep(interval); - waited += interval; + if (!tcp_active_insert_flow(flow) || !tcp_send_from_seg(flow, seg)) { + tcp_free_flow(flow); + tcp_flow_put(flow); + return false; } - tcp_free_flow(idx); - return false; + *flow_ctx = flow->base.ctx; + tcp_flow_put(flow); + return true; } \ 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 b11bb212..f47eb6ed 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" @@ -12,66 +11,93 @@ #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_DEFAULT_MSS 1460 -#define TCP_DEFAULT_RCV_BUF (256u * 1024u) -#define TCP_PERSIST_PROBE_BUFSZ 1 +#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_DELAYED_ACK_MS 200 +#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 +#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; + uint32_t right; +} tcp_sack_range_t; 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; + uint8_t opts[40]; uint32_t seq; - uint64_t len; - uintptr_t buf; + uint32_t len; + netpkt_t *pkt; + uint32_t payload_off; uint32_t timer_ms; + uint32_t rtt_timer_ms; uint32_t timeout_ms; } tcp_tx_seg_t; typedef struct { uint32_t seq; uint32_t end; - uintptr_t buf; } tcp_reass_seg_t; 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; + 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; - uint32_t time_wait_ms; - uint32_t fin_wait2_ms; - - uint32_t rcv_nxt; - uint32_t rcv_buf_used; - uint32_t rcv_wnd; - uint32_t rcv_wnd_max; - uint32_t rcv_adv_edge; + uint8_t rtt_sample_pending; 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; @@ -79,59 +105,135 @@ 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; + + 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; + 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; - uint8_t persist_active; - uint8_t persist_probe_cnt; - uint32_t persist_timer_ms; - uint32_t persist_timeout_ms; + 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; - uint8_t delayed_ack_pending; - uint32_t delayed_ack_timer_ms; +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; + uint8_t dsack_pending; + uint8_t urg_valid; + uint32_t dsack_left; + uint32_t dsack_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; uint32_t fin_seq; + uint32_t urg_seq; +} tcp_flow_rx_t; + +typedef struct { + uint32_t time_wait_ms; + uint32_t fin_wait2_ms; + + uint8_t persist_active; + 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; + + uint8_t challenge_ack_valid; + uint32_t challenge_ack_last_ms; +} tcp_flow_timer_t; + +typedef struct { + uint8_t ttl; + uint8_t dontfrag; + uint8_t reuseaddr; +} tcp_flow_ip_t; + +typedef struct tcp_flow { + 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; + +void tcp_enter_time_wait(tcp_flow_t *flow); tcp_flow_t *tcp_alloc_flow(void); -void tcp_free_flow(int idx); +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); +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); -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); +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); 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) { - 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); } 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); +void tcp_update_adv_wnd(tcp_flow_t *flow, uint8_t apply_scale); #ifdef __cplusplus } 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..24faea40 --- /dev/null +++ b/kernel/networking/transport_layer/tcp/tcp_limits.c @@ -0,0 +1,110 @@ +#include "tcp_internal.h" +#include "exceptions/irq.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 > 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(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; + size_t ip_len = (size_t)(ver == IP_VER6 ? 16 : 4); + + 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* 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); + + if (active_count >= MAX_TCP_FLOWS) return TCP_ADMIT_FLOW_TABLE_FULL; + + 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; +} 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..ab8075cf --- /dev/null +++ b/kernel/networking/transport_layer/tcp/tcp_limits.h @@ -0,0 +1,77 @@ +#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_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 + +struct tcp_flow; +struct ksocket; + +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_FLOW_RESERVE, + 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 syn_drop_flow_reserve; + uint64_t tx_block_flow_bytes; + uint64_t tx_block_flow_segs; + uint64_t tx_block_global_bytes; + 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(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); +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); +void tcp_account_tx_remove(struct tcp_flow *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 1d5deb66..dbf736a7 100644 --- a/kernel/networking/transport_layer/tcp/tcp_rx.c +++ b/kernel/networking/transport_layer/tcp/tcp_rx.c @@ -1,746 +1,1417 @@ #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/interface_manager.h" #include "networking/internet_layer/ipv4.h" #include "networking/internet_layer/ipv6.h" #include "networking/internet_layer/ipv4_utils.h" #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" -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; - } - } +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; +} - 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 (flow->rcv_buf_used >= olen) flow->rcv_buf_used -= olen; - else flow->rcv_buf_used = 0; +static bool tcp_reass_count_ok(tcp_flow_t *flow) { + if (!flow) return false; - 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--; + 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; + flow->rx.rcv_ooo_used = 0; + flow->rx.sack_recent_left = 0; + flow->rx.sack_recent_right = 0; + + for (uint32_t i = 0; i < TCP_REASS_MAX_SEGS; i++) { + flow->rx.reass[i].seq = 0; + flow->rx.reass[i].end = 0; } + + return false; } -static void tcp_reass_insert(tcp_flow_t *flow, uint32_t seq, const uint8_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 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; + + uint32_t len = 0; + 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); - 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; + for (int32_t i = idx; i + 1 < flow->rx.reass_count; i++) flow->rx.reass[i] = flow->rx.reass[i+1]; - 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; + 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; +} - uint32_t orig_seq = seq; - uint32_t start = seq; - uint32_t end = seq + len; +static bool tcp_reass_drain_inseq(tcp_flow_t *flow) { + bool advanced = false; + if (!tcp_reass_count_ok(flow)) return false; for(;;){ - int changed = 0; + int32_t idx = -1; - 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; + 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; + } - if (end <= rs || start >= re) continue; - if (start >= rs && end <= re) return; + if (idx < 0) break; - if (start <= rs && end >= re) { - uint32_t olen = re - rs; + uint32_t end = flow->rx.reass[idx].end; + if (TCP_SEQ_LEQ(end, flow->rx.rcv_nxt)) { + tcp_reass_remove(flow, idx); + continue; + } - if (r->buf && olen) free_sized((void *)r->buf, olen); + 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 = true; + } - 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--; + if (advanced) tcp_update_adv_wnd(flow, 1); + return advanced; +} - flow->rcv_buf_used -= olen; - changed = 1; - break; +int64_t tcp_flow_read(tcp_data *flow_ctx, void *buf, uint64_t len) { + if (!buf || !len) return 0; + + tcp_flow_t *flow = tcp_flow_from_ctx(flow_ctx); + if (!flow) return TCP_DISCONNECT; + + 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; + 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 (start < rs && end > rs && end <= re) { - end = rs; - len = end - start; - changed = 1; - break; - } + 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; - if (start >= rs && start < re && end > re){ - start = re; - len = end - start; - changed = 1; - break; - } + 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); + return rc; +} + +uint32_t tcp_flow_readable(tcp_data *flow_ctx) { + tcp_flow_t *flow = tcp_flow_from_ctx(flow_ctx); + 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; +} - if (!changed) break; - if (!len) return; +bool tcp_flow_recv_closed(tcp_data *flow_ctx) { + tcp_flow_t *flow = tcp_flow_from_ctx(flow_ctx); + if (!flow) return true; + + 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; } - 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; + tcp_flow_put(flow); + return closed; +} - uintptr_t buf = (uintptr_t)malloc(len); - if (!buf) return; +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; - uint32_t offset = start - orig_seq; - memcpy((void *)buf, data + offset, len); + for (uint32_t i = 0; i < TCP_MAX_TX_SEGS; i++){ + tcp_tx_seg_t *s = &flow->tx.txq[i]; - int pos = flow->reass_count; - while (pos > 0 && flow->reass[pos - 1].seq > start){ - flow->reass[pos] = flow->reass[pos - 1]; - pos--; + if (!s->used) continue; + + 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 (!best || TCP_SEQ_LT(s->seq, best_seq)){ + best = s; + best_seq = s->seq; + } } - flow->reass[pos].seq = start; - flow->reass[pos].end = start + len; - flow->reass[pos].buf = buf; - flow->reass_count++; + return best; +} + +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; - flow->rcv_buf_used += len; + uint32_t wnd_end = rcv_nxt + rcv_wnd; + if (!seg_len) return TCP_SEQ_GEQ(seq, rcv_nxt) && TCP_SEQ_LT(seq, wnd_end); - (void)tcp_calc_adv_wnd_field(flow, 1); + 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_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) { - uint32_t rcv_nxt = flow->rcv_nxt; +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; - for(;;){ - int idx = -1; + flow->timer.challenge_ack_valid = 1; + flow->timer.challenge_ack_last_ms = now; +} - for (int i = 0; i < flow->reass_count; i++){ - if (flow->reass[i].seq != rcv_nxt) continue; - idx = i; - break; +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; } - if (idx < 0) break; + uint32_t seq = seg->seq; + if (seg->syn && TCP_SEQ_GT(ack, seq)) { + if (seg->retransmit_cnt) syn_retransmitted = true; + seg->syn = 0; + seq++; + } - tcp_reass_seg_t *seg = &flow->reass[idx]; - uint32_t seg_len = seg->end - seg->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); + } - 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--; + 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; + 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; + } + + 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 (pm) { - port_recv_handler_t h = port_get_handler(pm, PROTO_TCP, dst_port); - uint32_t accepted = seg_len; + if (!inserted) { + merged[out].left = left; + merged[out].right = right; + out++; + } - if (h) accepted = h(ifx, ipver, src_ip_addr, dst_ip_addr, seg->buf, seg_len, src_port, dst_port); - if (accepted > seg_len) accepted = seg_len; + 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 false; - 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); + memcpy(ranges, &merged[first], sizeof(tcp_sack_range_t) * next_count); + *count = next_count; + return true; +} - if (flow->rcv_buf_used >= seg_len) flow->rcv_buf_used -= seg_len; - else flow->rcv_buf_used = 0; +static void tcp_sack_trim_ranges(tcp_sack_range_t *ranges, uint8_t *count, uint32_t ack) { + if (!ranges || !count) return; - rcv_nxt += seg_len; + uint8_t out = 0; + for (uint8_t i = 0; i < *count; i++) { + uint32_t left = ranges[i].left; + uint32_t right = ranges[i].right; - 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; - } + if (TCP_SEQ_LEQ(right, ack)) continue; + if (TCP_SEQ_LT(left, ack)) left = ack; + ranges[out].left = left; + ranges[out].right = right; + out++; + } - break; - } + *count = out; +} - if (accepted < seg_len) { - uint32_t rem = seg_len - accepted; - uintptr_t newbuf = (uintptr_t)malloc(rem); - if (!newbuf) break; +static bool tcp_sack_is_lost(tcp_flow_t *flow, uint32_t right) { + if (!flow) return false; - memcpy((void *)newbuf, ((const uint8_t *)seg->buf) + accepted, rem); - if (seg->buf) free_sized((void *)seg->buf, seg_len); + 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; - seg->buf = newbuf; - seg->seq += accepted; + uint32_t bytes = 0; + uint32_t blocks = 0; + uint32_t mss = flow->tx.mss ? flow->tx.mss : TCP_DEFAULT_MSS; - if (flow->rcv_buf_used >= accepted) flow->rcv_buf_used -= accepted; - else flow->rcv_buf_used = 0; + 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; - rcv_nxt += accepted; + uint32_t left = TCP_SEQ_LT(range->left, right) ? right : range->left; + if (TCP_SEQ_GT(range->right, left)) bytes += range->right - left; + blocks++; + } - flow->rcv_nxt = rcv_nxt; - flow->ctx.ack = rcv_nxt; + return blocks >= 3 || bytes >= 3 * mss; +} - (void)tcp_calc_adv_wnd_field(flow, 1); - continue; +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); + 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 (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; } + } + + if (!best_seg) return false; + *out_seg = best_seg; + *out_left = best_left; + *out_right = best_right; + return true; +} - rcv_nxt += seg_len; +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; +} - if (seg->buf) free_sized((void *)seg->buf, seg_len); - if (flow->rcv_buf_used >= seg_len) flow->rcv_buf_used -= seg_len; - else flow->rcv_buf_used = 0; +static void tcp_sack_recovery_send(tcp_flow_t *flow, bool force_first) { + if (!flow || !flow->tx.sack_ok) return; - 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--; + 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; } - flow->rcv_nxt = rcv_nxt; - flow->ctx.ack = rcv_nxt; + 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; + } - (void)tcp_calc_adv_wnd_field(flow, 1); -} + 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; -tcp_tx_seg_t *tcp_find_first_unacked(tcp_flow_t *flow) { - tcp_tx_seg_t *best = NULL; - uint32_t best_seq = 0; + 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; + } - for (int i = 0; i < TCP_MAX_TX_SEGS; i++){ - tcp_tx_seg_t *s = &flow->txq[i]; + uint32_t hole_len = hole_right - cursor; + pipe += retransmitted > hole_len ? hole_len : retransmitted; + } else { + pipe += hole_right - cursor; + } + } - if (!s->used) 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 ? 1u : 0u) + (s->fin ? 1u : 0u); - if (end <= flow->snd_una) continue; + uint32_t cwnd = flow->tx.cwnd ? flow->tx.cwnd : (flow->tx.mss ? flow->tx.mss : TCP_DEFAULT_MSS); - if (!best || s->seq < best_seq){ - best = s; - best_seq = s->seq; + 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, 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; + if (!tcp_sack_retransmit_range(flow, seg, left, right, rescue)) break; + pipe += len; + } +} + +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; + + 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_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 best; + return changed; } 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 = 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; 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 = 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) { - uint32_t mss = f->mss ? f->mss : TCP_DEFAULT_MSS; - - if (f->in_fast_recovery){ - if (ack >= f->recover){ - f->cwnd = f->ssthresh; - if (f->cwnd < mss) f->cwnd = mss; +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)){ + f->tx.cwnd = f->tx.ssthresh; + 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; + f->tx.sack_retransmitted_count = 0; + f->tx.sack_rescue_sent = 0; + return; + } - f->in_fast_recovery = 0; - f->dup_acks = 0; - f->cwnd_acc = 0; + if (f->tx.sack_ok) { + f->tx.cwnd = f->tx.ssthresh; + if (f->tx.cwnd < mss) f->tx.cwnd = mss; return; } - f->cwnd = f->ssthresh; - if (f->cwnd < mss) f->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; - if (f->cwnd < f->ssthresh){ - f->cwnd += mss; - if (f->cwnd < mss) f->cwnd = mss; + tcp_tx_seg_t *seg = tcp_find_first_unacked(f); + if (seg && !tcp_retransmit_seg(f, seg)) tcp_restart_retransmit_timer(f); return; } - uint32_t denom = f->cwnd ? f->cwnd : 1u; - uint32_t inc = (mss * mss) / denom; + if (f->tx.recover_valid && TCP_SEQ_GEQ(ack, f->tx.recover)) f->tx.recover_valid = 0; - if (inc == 0) inc = 1; + if (f->tx.cwnd < f->tx.ssthresh){ + uint32_t inc = acked < mss ? acked : mss; + f->tx.cwnd += inc; + if (f->tx.cwnd < mss) f->tx.cwnd = mss; + return; + } - f->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->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){ + 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->dup_acks != 3) 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->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; - - tcp_tx_seg_t *s = tcp_find_first_unacked(f); - if (s) { - tcp_send_from_seg(f, s); - s->retransmit_cnt++; - s->timer_ms = 0; + f->tx.ssthresh = half; + f->tx.recover = f->tx.snd_nxt; + 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; + + 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_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, uintptr_t ptr, uint32_t len) { - if (len < sizeof(tcp_hdr_t)) return; - - tcp_hdr_t *hdr = (tcp_hdr_t *)ptr; - - uint16_t recv_checksum = hdr->checksum; - hdr->checksum = 0; - - uint16_t calc; - - 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); +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)) { + netpkt_unref(pkt); + return; + } - hdr->checksum = recv_checksum; - if (recv_checksum != calc) return; + tcp_hdr_t hdr; + if (!netpkt_copyout(pkt, 0, &hdr, sizeof(hdr))) { + netpkt_unref(pkt); + 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); + 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*)segment, (uint16_t)len, src_ip, dst_ip) != 0) { + netpkt_unref(pkt); + return; + } + } else { + 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; + } + } - uint8_t hdr_len = (uint8_t)((hdr->data_offset_reserved >> 4) * 4); - if (len < hdr_len) 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 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) { + netpkt_unref(pkt); + return; + } uint32_t data_len = len - hdr_len; - 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; + 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); + + tcp_flow_t *flow = tcp_flow_acquire_match(dst_port, ipver, dst_ip_addr, src_ip_addr, src_port); - 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) return; - pm = ifmgr_pm_v4(l3_id); + if (!v4 || !v4->l2) { + if (flow) tcp_flow_put(flow); + 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); - if (!v6 || !v6->l2) return; - pm = ifmgr_pm_v6(l3_id); + if (!v6 || !v6->l2) { + if (flow) tcp_flow_put(flow); + 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 (!pm) 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 (flow) { + 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 ((flags & (1u << SYN_F)) && !(flags & (1u << ACK_F)) && 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->state != TCP_SYN_RECEIVED) continue; - 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; - - tcp_flow_t *lf = tcp_flows[listen_idx]; - tcp_flow_t *nf = tcp_alloc_flow(); - if (!nf) return; + 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; + } + } - flow = nf; - for (int k = 0; k < MAX_TCP_FLOWS; k++) { - if (tcp_flows[k] == nf) { - idx = k; - 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; + } + } + + if (!flow){ + 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); + + 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_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); + return; } - flow->local_port = dst_port; - flow->l3_id = l3_id; + const SocketOptions* listener_extra = socket_tcp_options(socket_core_impl(listener)); + tcp_flow_t *nf = tcp_alloc_flow(); + if (!nf) { + socket_core_put(listener); + netpkt_unref(pkt); + return; + } + flow = nf; + 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; - 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); + 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 = 0; + flow->tx.ws_recv = 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; } 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; - - 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->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.path_mss = tcp_calc_mss_for_l3(l3_id, ipver, src_ip_addr); + 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; + 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->ctx.ack = seq + 1; - flow->rcv_nxt = seq + 1; - - flow->ctx.expected_ack = iss + 1; - flow->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_buf_used = 0; - uint16_t synack_wnd = 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; - 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; - - 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; - - 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); - - 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->base.ctx.sequence = iss; + flow->tx.snd_una = iss; + flow->tx.snd_nxt = iss; + + flow->base.ctx.ack = seq + 1; + flow->rx.rcv_nxt = seq + 1; + + flow->base.ctx.expected_ack = iss + 1; + flow->base.ctx.ack_received = 0; + 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; + flow->timer.persist_timeout_ms = 0; + + flow->timer.delayed_ack_pending = 0; + flow->timer.delayed_ack_timer_ms = 0; + + 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_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; + } + 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_update_adv_wnd(flow, flow->tx.ws_ok ? 1 : 0); + + 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; + flow->timer.fin_wait2_ms = 0; + flow->base.listener = listener; + listener = NULL; + + tcp_tx_seg_t *seg = tcp_alloc_tx_seg(flow, 0); + if (!seg) { + tcp_free_flow(flow); + tcp_flow_put(flow); + netpkt_unref(pkt); + return; } - tcp_daemon_kick(); + 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; + 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.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_active_insert_flow(flow) || !tcp_send_from_seg(flow, seg)) { + tcp_free_flow(flow); + tcp_flow_put(flow); + netpkt_unref(pkt); + return; + } + tcp_flow_put(flow); + 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); + 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); } } + netpkt_unref(pkt); return; } - if (flow->state == TCP_TIME_WAIT){ - if (flags & (1u << RST_F)) return; + uint8_t fin = (flags & (1 << FIN_F)) ? 1 : 0; + uint32_t seg_seq = seq; + bool handshake_ack_processed = false; + + switch (flow->base.state) { + case TCP_TIME_WAIT: + if (flags & (1u << RST_F)) { + tcp_flow_put(flow); + netpkt_unref(pkt); + return; + } + + uint32_t time_wait_seg_len = data_len; + + if (flags & (1u << SYN_F)) time_wait_seg_len++; + if (flags & (1u << FIN_F)) time_wait_seg_len++; + + uint32_t seg_end = seq + time_wait_seg_len; - uint32_t seg_len = data_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); + } - if (flags & (1u << SYN_F)) seg_len++; - if (flags & (1u << FIN_F)) seg_len++; + tcp_flow_put(flow); + netpkt_unref(pkt); + return; - uint32_t seg_end = seq + seg_len; + case TCP_SYN_SENT: + bool ack_set = (flags & (1u << ACK_F)) != 0; + bool ack_acceptable = false; - if (seq <= flow->rcv_nxt && seg_end >= flow->rcv_nxt){ - flow->time_wait_ms = 0; + 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; + } + ack_acceptable = true; + } + + if (flags & (1u << RST_F)) { + if (ack_acceptable) tcp_free_flow(flow); + tcp_flow_put(flow); + netpkt_unref(pkt); + return; + } + + if (!(flags & (1u << SYN_F))) { + tcp_flow_put(flow); + netpkt_unref(pkt); + return; + } + + flow->timer.keepalive_idle_ms = 0; + flow->base.ctx.ack = seq + 1; + flow->rx.rcv_nxt = seq + 1; + 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; + + 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->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 : (ipver == IP_VER6 ? TCP_DEFAULT_PEER_MSS_IPV6 : TCP_DEFAULT_PEER_MSS_IPV4); + tcp_update_mss(flow); + 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; + } + + 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); - } - return; - } - uint32_t new_wnd = window; - if (flow->ws_ok && flow->ws_recv) new_wnd <<= flow->ws_recv; - flow->snd_wnd = new_wnd; + if (!data_len && !fin) { + tcp_flow_put(flow); + netpkt_unref(pkt); + return; + } - if (flow->snd_wnd > 0){ - flow->persist_active = 0; - flow->persist_timer_ms = 0; - flow->persist_timeout_ms = 0; - flow->persist_probe_cnt = 0; - } else { - tcp_daemon_kick(); - } + 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; + } - uint8_t fin = (flags & (1u << FIN_F)) ? 1u : 0u; + handshake_ack_processed = true; + break; - if (flags & (1u << ACK_F)){ - if (ack > flow->snd_una && ack <= flow->snd_nxt){ - uint32_t prev_una = flow->snd_una; - flow->snd_una = ack; - flow->ctx.ack_received = ack; - flow->dup_acks = 0; + 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); - for (int i = 0; i < TCP_MAX_TX_SEGS; i++){ - tcp_tx_seg_t *s = &flow->txq[i]; - if (!s->used) continue; + 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 s_end = s->seq + s->len + (s->syn ? 1u : 0u) + (s->fin ? 1u : 0u); + if (flags & (1u << SYN_F)) { + tcp_send_challenge_ack(flow); + tcp_flow_put(flow); + netpkt_unref(pkt); + return; + } - if (s_end <= ack){ - if (s->rtt_sample && s->retransmit_cnt == 0) tcp_rtt_update(flow, s->timer_ms); + if (!seq_acceptable) { + tcp_send_ack_now(flow); + tcp_flow_put(flow); + netpkt_unref(pkt); + return; + } - if (s->buf && s->len) free_sized((void *)s->buf, s->len); + if (!(flags & (1u << ACK_F))) { + tcp_flow_put(flow); + netpkt_unref(pkt); + return; + } - s->used = 0; - s->buf = 0; - s->len = 0; - } + 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; } - if (ack > prev_una) tcp_cc_on_new_ack(flow, ack); + flow->timer.keepalive_idle_ms = 0; - if (flow->state == TCP_FIN_WAIT_1 && ack >= flow->ctx.expected_ack){ - flow->state = TCP_FIN_WAIT_2; - flow->fin_wait2_ms = 0; - tcp_daemon_kick(); - } else if ((flow->state == TCP_LAST_ACK || flow->state == TCP_CLOSING) && ack >= flow->ctx.expected_ack){ - tcp_free_flow(idx); + 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; } - } else if (ack == flow->snd_una && data_len == 0 && !fin){ - if (flow->dup_acks < UINT8_MAX) flow->dup_acks++; - tcp_cc_on_dupack(flow); - } else { - flow->dup_acks = 0; - } - } - uint32_t seg_seq = seq; + 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; + } - switch (flow->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; - - 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); - - 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->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->mss = m; - } else { - flow->mss = tcp_calc_mss_for_l3(l3_id, ipver, src_ip_addr); + if (flow->base.listener) { + socket_core_put(flow->base.listener); + flow->base.listener = NULL; + } } + 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; + uint32_t new_wnd = window; - if (flow->ws_ok && flow->ws_recv) new_wnd <<= flow->ws_recv; - flow->snd_wnd = new_wnd; - - (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.flags = (uint8_t)(1u << ACK_F); - final_ack.window = flow->ctx.window; - final_ack.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); - 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); - } 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); + 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; } - flow->state = TCP_ESTABLISHED; - flow->delayed_ack_pending = 0; - flow->delayed_ack_timer_ms = 0; - tcp_daemon_kick(); - } else if (flags & (1u << RST_F)){ - flow->state = TCP_STATE_CLOSED; + handshake_ack_processed = true; + break; + + + 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; } - return; + if (flags & (1u << SYN_F)) { + tcp_send_challenge_ack(flow); + tcp_flow_put(flow); + 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){ - flow->ctx.sequence += 1; - 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 (!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; + } - 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 (!(flags & (1u << ACK_F))) { + tcp_flow_put(flow); + netpkt_unref(pkt); + return; + } - tcp_daemon_kick(); - } else if (flags & (1u << RST_F)){ - tcp_free_flow(idx); + if (TCP_SEQ_GT(ack, flow->tx.snd_nxt)) { + tcp_send_ack_now(flow); + tcp_flow_put(flow); + netpkt_unref(pkt); + return; } - 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; + } + } - default: - break; + 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 & (1u << RST_F)) { - tcp_free_flow(idx); - return; + 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; + } } - int need_ack = 0; - int ack_immediate = 0; - int ack_defer = 0; + 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; + bool ack_immediate = false; 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); + 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 (orig_end <= rcv_nxt || seg_seq >= wnd_end) { - need_ack = 1; - ack_immediate = 1; + 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; + tcp_note_dsack(flow, seg_seq, duplicate_right); + } + + if (TCP_SEQ_LEQ(orig_end, rcv_nxt) || TCP_SEQ_GEQ(seg_seq, wnd_end)) { + need_ack = true; + ack_immediate = true; } 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; } - const uint8_t *payload = (const uint8_t *)(ptr + hdr_len); + 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; @@ -754,169 +1425,224 @@ 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){ - need_ack = 1; - ack_immediate = 1; - } else if (seg_seq == flow->rcv_nxt) { + need_ack = true; + ack_immediate = true; + } else if (seg_seq == flow->rx.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; - - 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 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 (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; - if (offer && h) accepted = h(ifx, ipver, src_ip_addr, dst_ip_addr, (uintptr_t)payload, offer, src_port, dst_port); - if (accepted > offer) accepted = offer; - - if (h && accepted == 0 && data_len) { - (void)tcp_calc_adv_wnd_field(flow, 1); - need_ack = 1; - ack_immediate = 1; + uint32_t stored = 0; + 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; + 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 && (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) { + 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) { + tcp_update_adv_wnd(flow, 1); + ack_immediate = true; } + + if (accepted < data_len) ack_immediate = true; } if (fin_in) { - if (flow->rcv_nxt == fin_seq) { - flow->rcv_nxt += 1; - flow->ctx.ack = flow->rcv_nxt; - - tcp_state_t old = flow->state; - - if (old == TCP_ESTABLISHED) flow->state = TCP_CLOSE_WAIT; - else if (old == TCP_FIN_WAIT_1) flow->state = TCP_CLOSING; - else if (old == TCP_FIN_WAIT_2 || old == TCP_CLOSING || old == TCP_LAST_ACK) { - flow->state = TCP_TIME_WAIT; - flow->time_wait_ms = 0; - tcp_daemon_kick(); - } + 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->base.state; - ack_immediate = 1; + 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) tcp_enter_time_wait(flow); + + ack_immediate = true; } else { - flow->fin_pending = 1; - flow->fin_seq = fin_seq; + flow->rx.fin_pending = 1; + flow->rx.fin_seq = fin_seq; } } - tcp_reass_drain_inseq(flow, pm, ifx, ipver, src_ip_addr, dst_ip_addr, src_port, dst_port); + if (tcp_reass_drain_inseq(flow)) ack_immediate = true; + if (had_reass) ack_immediate = true; - 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; - else if (old == TCP_FIN_WAIT_2 || old == TCP_CLOSING || old == TCP_LAST_ACK) { - flow->state = TCP_TIME_WAIT; - flow->time_wait_ms = 0; - tcp_daemon_kick(); - } + 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) tcp_enter_time_wait(flow); - ack_immediate = 1; + ack_immediate = true; } - (void)tcp_calc_adv_wnd_field(flow, 1); + tcp_update_adv_wnd(flow, 1); - if (!ack_immediate && data_len) ack_defer = 1; - need_ack = 1; + need_ack = true; } 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 (!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; + + 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 { + ooo_seq += d; + ooo_data += d; + ooo_len -= d; + } + } + + 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 (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) { + uint32_t start = ooo_seq; + uint32_t end = ooo_seq + ooo_len; + uint32_t merged_start = start; + uint32_t merged_end = end; + uint32_t old_bytes = 0; + uint8_t overlapping = 0; + bool covered = false; + + 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; + tcp_note_dsack(flow, start, 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; + tcp_note_dsack(flow, duplicate_left, 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; + 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 remaining_nodes = overlapping < flow->rx.reass_count ? (uint32_t)(flow->rx.reass_count - overlapping) : 0; + + 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++; + 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) { + 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; + bool copied = true; + + if (first > ooo_len) first = ooo_len; + 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 (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++; + continue; + } + + tcp_reass_remove(flow,i); + } + + if (flow->rx.reass_count < TCP_REASS_MAX_SEGS) { + 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--; + } + + 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; + tcp_account_ooo_add(merged_end - merged_start, 1); + flow->rx.sack_recent_left = merged_start; + flow->rx.sack_recent_right = merged_end; + tcp_update_adv_wnd(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; - 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->delayed_ack_pending){ - flow->delayed_ack_pending = 1; - flow->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 (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); - } - } - } -} - -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; - } - } - if (!flow) return; - - if (bytes_read > flow->rcv_buf_used) bytes_read = flow->rcv_buf_used; - flow->rcv_buf_used -= bytes_read; - - port_manager_t *pm = NULL; - uint8_t ifx = 0; - - 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 tcp_send_ack_now(flow); } - } 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 (pm) { - tcp_reass_drain_inseq(flow, pm, 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) { - (void)tcp_calc_adv_wnd_field(flow, 1); - tcp_send_ack_now(flow); } + tcp_flow_put(flow); + netpkt_unref(pkt); } diff --git a/kernel/networking/transport_layer/tcp/tcp_timer.c b/kernel/networking/transport_layer/tcp/tcp_timer.c index b3a92597..741dccf5 100644 --- a/kernel/networking/transport_layer/tcp/tcp_timer.c +++ b/kernel/networking/transport_layer/tcp/tcp_timer.c @@ -2,235 +2,215 @@ #include "kernel_processes/kprocess_loader.h" #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 -void tcp_daemon_kick(void) { - if(!tcp_has_pending_timers()) return; +static volatile int tcp_daemon_running; +static int tcp_daemon_entry(int argc, char *argv[]); + +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(flow->base.local.port); + hdr.dst_port = bswap16(flow->base.remote.port); + hdr.sequence = bswap32(seq); + hdr.ack = bswap32(flow->base.ctx.ack); + hdr.flags = (uint8_t)(1u << ACK_F); + 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); +} - disable_interrupt(); +//TODO events +void tcp_daemon_kick(void) { + 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(); + if (!create_kernel_process("tcp_timer", tcp_daemon_entry, 0, 0)) { + 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]; - if (!f) continue; - if (f->state == TCP_STATE_CLOSED) continue; - - if (f->state == TCP_TIME_WAIT) return 1; - if (f->state == TCP_FIN_WAIT_2) 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; - - 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; +static int tcp_daemon_entry(int argc, char *argv[]) { + (void)argc; + (void)argv; + uint32_t last_tick_ms = (uint32_t)get_time(); + + while (true) { + uint32_t now_ms = (uint32_t)get_time(); + uint32_t elapsed_ms = now_ms - last_tick_ms; + last_tick_ms = now_ms; + + uint16_t active_slots[MAX_TCP_FLOWS]; + uint32_t active_generations[MAX_TCP_FLOWS]; + uint16_t active_count; + + irq_flags_t irq = irq_save_disable(); + active_count = tcp_active_count; + if (!active_count) { + tcp_daemon_running = 0; + irq_restore(irq); + return 0; } - } - - 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; + active_slots[i] = slot; + active_generations[i] = flow ? flow->base.generation : 0; + } + irq_restore(irq); + + 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; -void tcp_tick_all(uint32_t elapsed_ms) { - for (int i = 0; i < MAX_TCP_FLOWS; i++) { - tcp_flow_t *f = tcp_flows[i]; - if (!f) continue; - if (f->state == TCP_STATE_CLOSED) continue; + 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->state == TCP_TIME_WAIT) { - f->time_wait_ms += elapsed_ms; - if (f->time_wait_ms >= TCP_2MSL_MS) { - tcp_free_flow(i); - continue; + 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->state == TCP_FIN_WAIT_2) { - f->fin_wait2_ms += elapsed_ms; - if (f->fin_wait2_ms >= TCP_2MSL_MS) { - tcp_free_flow(i); + if (retire_flow) { + tcp_free_flow(flow); + tcp_flow_put(flow); 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->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; - hdr.src_port = bswap16(f->local_port); - hdr.dst_port = bswap16(f->remote.port); - uint32_t seq = f->snd_nxt; - if (seq) seq -= 1; - hdr.sequence = bswap32(seq); - hdr.ack = bswap32(f->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) { - 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) { - 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); - } - f->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) tcp_send_ack_now(flow); } - } - if (f->snd_wnd == 0 && f->snd_nxt > f->snd_una) { - 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; - } 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); - } - continue; + 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; } - 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); - - uint8_t payload[1]; - const uint8_t *pp = NULL; - uint16_t pl = 0; + } + } - uint32_t probe_seq = f->snd_una; + 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 { + 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; + } + } - 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)); - pp = payload; - pl = 1; - } + if (flow->tx.fin_tx_pending) tcp_try_send_pending_fin(flow); - hdr.sequence = bswap32(probe_seq); - hdr.ack = bswap32(f->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) { - 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) { - 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); - } + 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; + 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->persist_probe_cnt < UINT8_MAX) f->persist_probe_cnt++; - f->persist_timer_ms = 0; + 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_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) { + 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; + + 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 (f->persist_timeout_ms < TCP_PERSIST_MAX_MS) { - uint32_t next = f->persist_timeout_ms << 1; - if (next > TCP_PERSIST_MAX_MS) next = TCP_PERSIST_MAX_MS; - f->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; + } + } } } + } else { + flow->timer.persist_active = 0; + flow->timer.persist_timer_ms = 0; + flow->timer.persist_timeout_ms = 0; } - } else { - f->persist_active = 0; - f->persist_timer_ms = 0; - f->persist_timeout_ms = 0; - f->persist_probe_cnt = 0; - } - - for (int j = 0; j < TCP_MAX_TX_SEGS; j++) { - tcp_tx_seg_t *s = &f->txq[j]; - if (!s->used) continue; - s->timer_ms += elapsed_ms; - if (s->timer_ms < s->timeout_ms) 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 *sample = &flow->tx.txq[j]; + if (!sample->used || !sample->rtt_sample || sample->persist) continue; + sample->rtt_timer_ms += elapsed_ms; + } - if (s->retransmit_cnt >= TCP_MAX_RETRANS) { - tcp_free_flow(i); - break; + 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) { + 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); + 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; + rto = next; + } + flow->tx.rto = rto; + seg->timeout_ms = rto; + } + } + } } - 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->rto ? f->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 (retire_flow) tcp_free_flow(flow); + tcp_flow_put(flow); } - } -} - -int tcp_daemon_entry(int argc, char *argv[]) { - (void)argc; - (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; - } - msleep(tick_ms); + msleep(10); } - 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 7f453581..d734662d 100644 --- a/kernel/networking/transport_layer/tcp/tcp_tx.c +++ b/kernel/networking/transport_layer/tcp/tcp_tx.c @@ -1,157 +1,414 @@ #include "tcp_internal.h" -uint16_t tcp_calc_adv_wnd_field(tcp_flow_t *flow, uint8_t apply_scale) { - if (!flow) return 0; +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 quantum = 1; - if (apply_scale && flow->ws_ok && flow->ws_send) quantum = 1u << flow->ws_send; + 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); - uint32_t maxw = flow->rcv_wnd_max; - uint32_t used = flow->rcv_buf_used; - uint32_t freew = maxw > used ? maxw - used: 0; + memset(seg, 0, sizeof(*seg)); +} - uint32_t free_q = quantum == 1 ? freew : (freew & ~(quantum - 1)); +void tcp_update_adv_wnd(tcp_flow_t *flow, uint8_t apply_scale) { + if (!flow) return; - 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; + uint32_t shift = 0; + if (apply_scale && flow->tx.ws_ok && flow->tx.ws_send) shift = flow->tx.ws_send; + 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 (TCP_SEQ_LT(hard_edge, flow->rx.rcv_nxt)) hard_edge = flow->rx.rcv_nxt; + } 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; + + 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 = 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; + } - uint32_t adv = flow->rcv_adv_edge - flow->rcv_nxt; + 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; + } - 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; + uint32_t new_edge = flow->rx.rcv_nxt + actual; + if (actual && TCP_SEQ_GT(new_edge, accept_edge)) accept_edge = new_edge; + } } - flow->rcv_wnd = adv; - flow->rcv_adv_edge = flow->rcv_nxt + adv; - flow->ctx.window = (uint16_t)field; - return (uint16_t)field; + 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; + + if (shift) field >>= shift; + if (field > 65535) field = 65535; + + flow->rx.rcv_wnd = accept_adv; + flow->rx.rcv_adv_edge = accept_edge; + flow->base.ctx.window = (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->tx.mss? flow->tx.mss : TCP_NAGLE_FLUSH_THRESHOLD; + if (!threshold) threshold = 1; + return threshold; } -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]; - s->used = 1; - s->syn = 0; - s->fin = 0; - s->rtt_sample = 0; - s->retransmit_cnt = 0; - s->seq = 0; - s->len = 0; - s->buf = 0; - s->timer_ms = 0; - s->timeout_ms = flow->rto ? flow->rto : TCP_INIT_RTO; +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; + + while (remaining > 0 && can_send > 0) { + 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, TCP_TX_CONTROL_RESERVE_SEGS); + if (!seg) { + tcp_stats.tx_block_flow_segs++; + break; + } + + 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, seg_len); + + seg->seq = flow->tx.snd_nxt; + seg->len = seg_len; + seg->pkt = payload_pkt; + 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 (!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; + + if (!tcp_send_from_seg(flow, seg)) { + flow->tx.snd_nxt -= seg_len; + flow->base.ctx.sequence = flow->tx.snd_nxt; + tcp_tx_seg_clear(flow, seg); + break; + } + + sent_bytes += seg_len; + remaining -= seg_len; + can_send -= seg_len; + 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(); - return s; + break; } } - return NULL; + + 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->tx.nagle_flushing || flow->tx.nagle_appending) return 0; + + flow->tx.nagle_appending = 1; + + uint32_t cap = tcp_nagle_threshold(flow); + if (flow->tx.nagle_len >= cap) { + flow->tx.nagle_appending = 0; + return 0; + } + + if (!flow->tx.nagle_buf || flow->tx.nagle_cap < 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; + } + + flow->tx.nagle_buf = nb; + flow->tx.nagle_cap = cap; + } + + uint64_t n = payload_len; + uint32_t room = flow->tx.nagle_cap - flow->tx.nagle_len; + if (n > room)n = room; + + 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->tx.nagle_len || !flow->tx.nagle_buf) return 0; + if (flow->tx.nagle_flushing || flow->tx.nagle_appending) 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 (!force && in_flight && flow->tx.nagle_lentx.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, flow->tx.nagle_psh); + if (!sent) { + flow->tx.nagle_flushing = 0; + return 0; + } + + if (sent >= old_len) { + flow->tx.nagle_len = 0; + flow->tx.nagle_timer_ms = 0; + flow->tx.nagle_psh = 0; + flow->tx.nagle_flushing = 0; + return sent; + } + + 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; + return sent; } -void tcp_send_from_seg(tcp_flow_t *flow, tcp_tx_seg_t *seg){ - if (flow) flow->keepalive_idle_ms = 0; +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; + } + + 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; 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); + 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; - - 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); - } 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); + const uint8_t *opts = seg->opts_len ? seg->opts : NULL; + + 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 (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; +} - tcp_daemon_kick(); +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->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); + tcp_update_adv_wnd(flow, 1); + ackhdr.window = flow->base.ctx.window; ackhdr.urgent_ptr = 0; uint8_t opts[64]; uint8_t opts_len = 0; - opts_len = 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->sack_ok && flow->reass_count > 0) { - uint32_t n = flow->reass_count; - if (n > 4) n = 4; + 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; + + 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->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; + bool duplicate = false; + + for (uint32_t j = 0; j < n; j++) { + if (blocks[j].left == left && blocks[j].right == right) { + duplicate = true; + 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; - - 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); + uint32_t left = blocks[i].left; + uint32_t right = blocks[i].right; + + wr_be32(&opts[o], left); + wr_be32(&opts[o + 4], right); o += 8; } @@ -161,155 +418,242 @@ void tcp_send_ack_now(tcp_flow_t *flow){ } } - 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, &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) { - 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); + 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; } - flow->delayed_ack_pending = 0; - flow->delayed_ack_timer_ms = 0; - tcp_daemon_kick(); + 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; } -tcp_result_t tcp_flow_send(tcp_data *flow_ctx){ - if (!flow_ctx) return TCP_INVALID; +void tcp_try_send_pending_fin(tcp_flow_t *flow) { + if (!flow || !flow->tx.fin_tx_pending) return; - 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]; + tcp_state_t next_state; + switch (flow->base.state) { + case TCP_ESTABLISHED: + next_state = TCP_FIN_WAIT_1; break; - } - } - if (!flow) return TCP_INVALID; - - uint8_t flags = flow_ctx->flags; - uint8_t *payload_ptr = (uint8_t *)flow_ctx->payload.ptr; - uint64_t payload_len = flow_ctx->payload.size; - flow_ctx->payload.size = 0; - - if (flow->state != TCP_ESTABLISHED && !(flags & (1u << FIN_F))) { - if (!(flow->state == TCP_CLOSE_WAIT && (flags & (1u << FIN_F)))) return TCP_INVALID; + case TCP_CLOSE_WAIT: + next_state = TCP_LAST_ACK; + break; + default: + return; } - if (flow->snd_wnd == 0 && !(flags & (1u << FIN_F))) { - tcp_persist_arm(flow); - return TCP_WOULDBLOCK; + 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 = 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 = 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) eff_wnd = 1; - if (in_flight >= eff_wnd && !(flags & (1u << FIN_F))) return TCP_WOULDBLOCK; + if (eff_wnd == 0 || in_flight >= eff_wnd) return; + tcp_tx_seg_t *seg = tcp_alloc_tx_seg(flow, 0); + if (!seg) return; + + seg->seq = flow->tx.snd_nxt; + seg->len = 0; + seg->pkt = NULL; + 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; + } - uint64_t can_send = eff_wnd - in_flight; - if (can_send == 0 && !(flags & (1u << FIN_F))) return TCP_WOULDBLOCK; + 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; + + flow->base.state = next_state; + + 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; + tcp_tx_seg_clear(flow, seg); + return; + } +} - uint64_t remaining = payload_len; - uint64_t sent_bytes = 0; - int first_segment = 1; +tcp_result_t tcp_flow_flush(tcp_data *flow_ctx){ + if (!flow_ctx) return TCP_INVALID; - 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; + tcp_flow_t *flow = tcp_flow_from_ctx(flow_ctx); + if (!flow) return TCP_INVALID; - tcp_tx_seg_t *seg = tcp_alloc_tx_seg(flow); - if (!seg) break; + 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; - uintptr_t buf = 0; - if (seg_len) { - buf = (uintptr_t)malloc(seg_len); - if (!buf) { seg->used = 0; break; } - memcpy((void *)buf, payload_ptr + sent_bytes, seg_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; + } + + if (rc == TCP_OK) { + 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; + } + } - 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_flow_put(flow); + return rc; +} - tcp_send_from_seg(flow, seg); +tcp_result_t tcp_flow_send(tcp_data *flow_ctx){ + if (!flow_ctx) return TCP_INVALID; - flow->snd_nxt += seg_len; - sent_bytes += seg_len; - remaining -= seg_len; - can_send -= seg_len; - first_segment = 0; - } + tcp_flow_t *flow = tcp_flow_from_ctx(flow_ctx); + if (!flow) return TCP_INVALID; - 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; + uint8_t flags = flow_ctx->flags; + 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 && 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(); + } + } + } - 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; + 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)) tcp_daemon_kick(); + } - tcp_send_from_seg(flow, seg); + 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 && !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)) tcp_daemon_kick(); + } - flow->snd_nxt += 1; - flow->ctx.expected_ack = flow->snd_nxt; + 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, want_push ? 1 : 0); + accepted += n; + payload_len -= n; + } } - flow_ctx->sequence = flow->snd_nxt; - flow->ctx.sequence = flow->snd_nxt; + 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 && 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(); + } - tcp_daemon_kick(); + flow_ctx->sequence = flow->tx.snd_nxt; + flow->base.ctx.sequence = flow->tx.snd_nxt; + + flow_ctx->payload.size = accepted; + 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) { + 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(); + } + rc = TCP_WOULDBLOCK; + } + } - flow_ctx->payload.size = sent_bytes; - return sent_bytes || (flags & (1u << FIN_F)) ? TCP_OK : TCP_WOULDBLOCK; + tcp_flow_put(flow); + return rc; } 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_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; - } - tcp_daemon_kick(); - return res; + 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) 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; } - return TCP_INVALID; + tcp_flow_put(flow); + return rc; } \ No newline at end of file diff --git a/kernel/networking/transport_layer/tcp/tcp_utils.c b/kernel/networking/transport_layer/tcp/tcp_utils.c index 8ce48329..62d1e676 100644 --- a/kernel/networking/transport_layer/tcp/tcp_utils.c +++ b/kernel/networking/transport_layer/tcp/tcp_utils.c @@ -1,15 +1,43 @@ #include "tcp_utils.h" +#include "std/memory.h" +#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; + 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){ + //TODO propagate icmp4/6 pmtu and errors 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; } @@ -21,40 +49,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, 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); - 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, ipv4_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){ - 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; @@ -63,6 +57,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; @@ -81,13 +77,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 (TCP_SEQ_LEQ(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; @@ -101,8 +116,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/tcp/tcp_utils.h b/kernel/networking/transport_layer/tcp/tcp_utils.h index 30e9c9b9..498a5c43 100644 --- a/kernel/networking/transport_layer/tcp/tcp_utils.h +++ b/kernel/networking/transport_layer/tcp/tcp_utils.h @@ -5,29 +5,35 @@ #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" { #endif +#define TCP_SACK_MAX_BLOCKS 4 +typedef struct { + uint32_t left; + uint32_t right; +} tcp_sack_block_t; + +struct tcp_flow; + 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); 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); +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, 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); +uint32_t tcp_initial_cwnd(uint32_t mss); #ifdef __cplusplus } 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/networking/transport_layer/udp.c b/kernel/networking/transport_layer/udp.c index 36e025e4..4c2d911a 100644 --- a/kernel/networking/transport_layer/udp.c +++ b/kernel/networking/transport_layer/udp.c @@ -1,158 +1,165 @@ #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/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" #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; -} +#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 = (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; + 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; - memcpy((void *)(buf + sizeof(*udp)), (void *)payload.ptr, payload.size); + 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; - if (src->ver == IP_VER4) { - uint32_t s = v4_u32_from_arr(src->ip); - uint32_t d = v4_u32_from_arr(dst->ip); - uint16_t csum = checksum16_pipv4(s, d, 0x11, (const uint8_t *)udp, full_len); - udp->checksum = bswap16(csum); - } else if (src->ver == IP_VER6) { - uint16_t csum = checksum16_pipv6(src->ip, dst->ip, 17, (const uint8_t *)udp, full_len); - udp->checksum = bswap16(csum); - } + memcpy((void*)buf, &udp, sizeof(udp)); + if (payload.size) memcpy((void *)(buf + sizeof(udp)), (void *)payload.ptr, payload.size); + uint16_t checksum = 0; + if (src->ver == IP_VER4) { + 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 = v4_u32_from_arr(dst->ip); - (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) { - (void)netpkt_trim(pkt, (uint32_t)written); - ipv6_send_packet(dst->ip, 0x11, pkt, (const ipv6_tx_opts_t*)tx_opts, ttl, dontfrag); - } else { - netpkt_unref(pkt); + uint32_t dst_ip = 0; + memcpy(&dst_ip, dst->ip, 4); + 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); } -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; +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 (sizedptr){ 0, 0 }; - } - return (sizedptr){ - .ptr = ptr + sizeof(udp_hdr_t), - .size = total - sizeof(udp_hdr_t) - }; + 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; - - udp_hdr_t *hdr = (udp_hdr_t *)ptr; +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; + 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_VER6 && !hdr.checksum) { + netpkt_unref(pkt); + return; + } + 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, PROTO_UDP, (const uint8_t*)netpkt_data(pkt), (uint16_t)(payload_len + sizeof(hdr))) != 0) { + netpkt_unref(pkt); + 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, PROTO_UDP, (const uint8_t*)netpkt_data(pkt), (uint32_t)(payload_len + sizeof(hdr))) != 0) { + netpkt_unref(pkt); + 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; - 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) return; + 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); - 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); - - uint8_t ifx = 0; - if (v4 && v4->l2) ifx = v4->l2->ifindex; - else if (v6 && v6->l2) ifx = v6->l2->ifindex; + if (!v4 && !v6) { + netpkt_unref(pkt); + return; + } - handler(ifx, ipver, src_ip_addr, dst_ip_addr, copy, pl.size, src_port, dst_port); + netpkt_t* plpkt = netpkt_view(pkt, payload_off, payload_len); + if (!plpkt) { + netpkt_unref(pkt); + return; } -} -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; -} + uint8_t ifx = 0; + if (v4 && v4->l2) ifx = v4->l2->ifindex; + else if (v6 && v6->l2) ifx = v6->l2->ifindex; -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 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); + } + } -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); + netpkt_unref(plpkt); + netpkt_unref(pkt); } -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 074012bf..16fa6b8e 100644 --- a/kernel/networking/transport_layer/udp.h +++ b/kernel/networking/transport_layer/udp.h @@ -1,8 +1,8 @@ #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" #ifdef __cplusplus extern "C" { @@ -20,18 +20,14 @@ 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, 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); -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 f9c35be9..96557f1a 100644 --- a/kernel/process/kernel_syscall_impl.c +++ b/kernel/process/kernel_syscall_impl.c @@ -89,37 +89,48 @@ 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, get_current_proc_pid(), out_handle); +socket_handle_t socket_create(protocol_t protocol, const SocketOptions* 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, get_current_proc_pid()); +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, SockDstKind dst_kind, void* dst, uint16_t port){ - return connect_socket(handle, dst_kind, dst, port, get_current_proc_pid()); +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, get_current_proc_pid()); +int32_t socket_listen(socket_handle_t handle, int32_t backlog){ + return listen_on(handle, backlog); } -bool socket_accept(SocketHandle *spec){ - accept_on_socket(spec, get_current_proc_pid()); - return 1; +socket_handle_t socket_accept(socket_handle_t handle){ + return accept_on_socket(handle); } -size_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(socket_handle_t handle, void *packet, size_t size){ + return send_on_socket(handle, packet, size); } -bool 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()); +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); } -int32_t socket_close(SocketHandle *handle){ - return close_socket(handle, get_current_proc_pid()); +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(socket_handle_t handle){ + return close_socket(handle); +} + +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(socket_handle_t 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/loading/dwarf.c b/kernel/process/loading/dwarf.c index f47e9e43..b866f876 100644 --- a/kernel/process/loading/dwarf.c +++ b/kernel/process/loading/dwarf.c @@ -275,14 +275,16 @@ 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 = (uptr)&hdr->unit_length + sizeof(hdr->unit_length) + read_unaligned32(&hdr->unit_length); + dwarf_debug_line_header hdr = {0}; + 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 (read_unaligned16(&hdr->version) != 5) { + if (hdr.version != 5) { kprintf("Only DWARF version 5 is supported"); return (debug_line_info){}; } @@ -292,7 +294,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,9 +303,8 @@ debug_line_info dwarf_decode_lines(uintptr_t ptr, size_t size, uintptr_t debug_l // for (int i = 0; i < 256; i++){ // if (files[i]) kprintf("File [%i] = %s",i,files[i]); // } + ptr = ptr + sizeof(hdr.unit_length) + sizeof(hdr.version) + sizeof(hdr.address_size) + sizeof(hdr.segment_selector) + sizeof(hdr.header_length) + hdr.header_length; - ptr = (uintptr_t)&hdr->header_length + sizeof(hdr->header_length) + read_unaligned32(&hdr->header_length); - uint8_t *end = (uint8_t*)unit_end; uint8_t *p = (uint8_t*)ptr; @@ -332,7 +333,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; @@ -344,7 +345,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; @@ -353,7 +354,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; } @@ -381,15 +382,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); @@ -414,12 +415,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; @@ -454,7 +455,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/process/syscall.c b/kernel/process/syscall.c index 87739089..e35f0241 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" @@ -245,99 +244,103 @@ u64 syscall_get_time(process_t *ctx, thread_t *current_thread){ } u64 syscall_socket_create(process_t *ctx, thread_t *current_thread){ - Socket_Role role = (Socket_Role)current_thread->PROC_X0; - protocol_t protocol = (protocol_t)current_thread->PROC_X1; - SYSCALL_ARG(const SocketExtraOptions, extra, PROC_X2, false); - SYSCALL_ARG(SocketHandle, out, PROC_X3, true); + protocol_t protocol = (protocol_t)current_thread->PROC_X0; + const SocketOptions* extra = NULL; + + if (current_thread->PROC_X1) { + SYSCALL_ARG(SocketOptions, user_extra, PROC_X1, false); + if (user_extra->mcast_count) { + if (!user_extra->mcast_groups) return 0; + if (!validate_address(ctx, current_thread, (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, ctx->id, out); + return create_socket(protocol, extra); } u64 syscall_socket_bind(process_t *ctx, thread_t *current_thread){ - SYSCALL_ARG(SocketHandle,handle,PROC_X0, true); - ip_version_t ip_version = (ip_version_t)current_thread->PROC_X1; + socket_handle_t handle = (socket_handle_t)current_thread->PROC_X0; + const SockBindSpec* spec = NULL; + + if (current_thread->PROC_X1){ + SYSCALL_ARG(SockBindSpec, user_spec, PROC_X1, false); + spec = user_spec; + } + uint16_t port = (uint16_t)current_thread->PROC_X2; - return bind_socket(handle, port, ip_version, ctx->id); + return bind_socket(handle, spec, port); } u64 syscall_socket_connect(process_t *ctx, thread_t *current_thread){ - uint8_t dst_kind = (uint8_t)current_thread->PROC_X1; - uint16_t port = (uint16_t)current_thread->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); + socket_handle_t handle = (socket_handle_t)current_thread->PROC_X0; + SYSCALL_ARG(net_l4_endpoint, ep, PROC_X1, false); + return connect_socket(handle, ep); } u64 syscall_socket_listen(process_t *ctx, thread_t *current_thread){ - SYSCALL_ARG(SocketHandle,handle, PROC_X0, true); + socket_handle_t handle = (socket_handle_t)current_thread->PROC_X0; int32_t backlog = (int32_t)current_thread->PROC_X1; - return listen_on(handle, backlog, ctx->id); + return listen_on(handle, backlog); } u64 syscall_socket_accept(process_t *ctx, thread_t *current_thread){ - SYSCALL_ARG(SocketHandle,handle, PROC_X0, true); - accept_on_socket(handle, ctx->id); - return 1; + socket_handle_t handle = (socket_handle_t)current_thread->PROC_X0; + return accept_on_socket(handle); } u64 syscall_socket_send(process_t *ctx, thread_t *current_thread){ - uint8_t dst_kind = (uint8_t)current_thread->PROC_X1; - uint16_t port = (uint16_t)current_thread->PROC_X3; - size_t size = (size_t)current_thread->regs[5]; - - 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; - } + size_t size = (size_t)current_thread->PROC_X2; - if (!size) return 0; - - uint64_t alloc_size = (size + 0xFFF) & ~0xFFFULL; + socket_handle_t handle = (socket_handle_t)current_thread->PROC_X0; + SYSCALL_ARG_SIZE(void, buf, size, PROC_X1, false); + return send_on_socket(handle, buf, size); +} - SYSCALL_ARG_SIZE(void, kbuf, alloc_size, PROC_X4, true); - if (!kbuf) return 0; +u64 syscall_socket_send_to(process_t *ctx, thread_t *current_thread) { + size_t size = (size_t)current_thread->PROC_X3; + socket_handle_t handle = (socket_handle_t)current_thread->PROC_X0; + SYSCALL_ARG(net_l4_endpoint, dst, PROC_X1, false); + SYSCALL_ARG_SIZE(void, buf, size, PROC_X2, false); - 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, thread_t *current_thread){ size_t size = (size_t)current_thread->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); + socket_handle_t handle = (socket_handle_t)current_thread->PROC_X0; + SYSCALL_ARG_SIZE(void, buf, size, PROC_X1, true); + + net_l4_endpoint* src = NULL; + if (current_thread->PROC_X3) { + src = (net_l4_endpoint*)current_thread->PROC_X3; + if (!validate_address(ctx, current_thread, (uptr)src, sizeof(net_l4_endpoint), true)) return 0; + } - 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, thread_t *current_thread){ - SYSCALL_ARG(SocketHandle,handle, PROC_X0, true); - return close_socket(handle, ctx->id); + socket_handle_t handle = (socket_handle_t)current_thread->PROC_X0; + return close_socket(handle); +} + +u64 syscall_socket_setopt(process_t *ctx, thread_t *current_thread){ + socket_handle_t handle = (socket_handle_t)current_thread->PROC_X0; + int32_t opt = (int32_t)current_thread->PROC_X1; + uint32_t len = (uint32_t)current_thread->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, thread_t *current_thread){ + socket_handle_t handle = (socket_handle_t)current_thread->PROC_X0; + int32_t opt = (int32_t)current_thread->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 @@ -537,9 +540,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/theme/theme.c b/kernel/theme/theme.c index 4cb7bc7f..aaaa8870 100644 --- a/kernel/theme/theme.c +++ b/kernel/theme/theme.c @@ -39,7 +39,7 @@ system_config_t system_config = { .default_pwd = DEFAULT_PWD, .system_name = SYSTEM_NAME, .app_directory = "boot", - .use_net = false, + .use_net = true, .preferred_screen_size = { 1920,1080 }, .headless = false, .use_login = false, diff --git a/kernel/tools/curl.c b/kernel/tools/curl.c new file mode 100644 index 00000000..ed6b58ab --- /dev/null +++ b/kernel/tools/curl.c @@ -0,0 +1,149 @@ +#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 { + char *url; + bool head_only; + bool follow; +} curl_opts_t; + +static int curl_fetch(char *url, 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.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) { + string_free(host); + string_free(path); + print("curl: socket create failed"); + return 4; + } + + 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); + 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_response_free(&resp); + http_headers_common_free(&req.headers_common); + http_client_destroy(cli); + string_free(host); + string_free(path); + return 6; + } + + if (head_only) { + HTTPResponseMsg head = resp; + head.body = (string){0}; + string raw = http_response_builder(&head); + print("%.*s", (int)raw.length, raw.data); + string_free(raw); + } else if (resp.body.data && resp.body.length) print("%.*s", (int)resp.body.length, (const char*)resp.body.data); + + http_response_free(&resp); + http_headers_common_free(&req.headers_common); + + 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 (!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] http://host/path"); + return 2; + } + + return curl_fetch(opts.url, 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/icmp_probe.c b/kernel/tools/icmp_probe.c new file mode 100644 index 00000000..92012c6c --- /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_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; + opt.raw_filter.rules[0].code = 0; + 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; + 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].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; + 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 157dd36a..c6365af5 100644 --- a/kernel/tools/ping.c +++ b/kernel/tools/ping.c @@ -1,21 +1,23 @@ #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" -//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/transport_layer/trans_utils.h" + +#define PING_MAX_REPLIES 16 + +typedef struct { + const char *host; + SockBindSpec bind; + bool bind_set; +} ping_addressing_t; typedef struct { ip_version_t ver; @@ -23,9 +25,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 +34,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]; @@ -56,173 +54,180 @@ 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; - 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->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->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; } 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->host; + 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); 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; + 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; + } - print("PING %s (%s) with 32 bytes of data:\n", host, ipstr); + 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); - - ipv4_tx_opts_t txo = {0}; - const ipv4_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)\n"); - return 2; - } - txo.index = (uint8_t)l3->l3_id; - txo.scope = IP_TX_BOUND_L3; - txop = &txo; - } + 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 = {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\n", ipstr, rtt); - } else { - print("%s\n", status_to_msg(res.status)); - } + 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 == 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]; + 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(ICMP_PROBE_TIMEOUT)); 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 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\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; + return received > 0 ? 0 : 1; } static int ping_v6(const ping_opts_t *o) { - const char *host = o->host; - - uint8_t dst6[16] ={0}; - bool is_lit = ipv6_parse(host, dst6); + const char *host = o->addr.host; + 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'\n",(int)dr, host); + 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:\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; 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); for (uint32_t i = 0; i < o->count; i++) { - ++sent; + 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\n", ipstr, rtt); - } else { - print("%s\n", status_to_msg(res.status)); - } + 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 == 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]; + 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(ICMP_PROBE_TIMEOUT)); 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 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\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,28 +236,18 @@ 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 ip|l2:id|l3:id] host"); return 2; } - if (opts.ver == IP_VER6 && opts.src_set) { - print("ping: -s is only supported for IPv4\n"); + 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.src_set) { - l3_ipv4_interface_t *l3 = l3_ipv4_find_by_ip(opts.src_ip); - if (!l3) { - char ssrc[16]; - ipv4_to_string(opts.src_ip, ssrc); - print("ping: invalid source %s (no local ip match)\n", 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 ip|l2:id|l3:id] host"); return 2; } diff --git a/kernel/tools/tools.c b/kernel/tools/tools.c index 4a00bc52..3b298215 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..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,9 +17,9 @@ 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; @@ -33,19 +29,17 @@ static bool parse_args(int argc, char *argv[], tr_opts_t *o) { 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,65 +76,52 @@ 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); 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; + dst_ip = r; } char dip[16]; char line[256]; - ipv4_to_string(dst, dip); - print("Tracing route to %s [%s]\n", o->host, 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\n", line); - - ipv4_tx_opts_t txo = (ipv4_tx_opts_t){0}; - const ipv4_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)\n", ssrc); - return 2; - } - txo.index = l3->l3_id; - txo.scope = IP_TX_BOUND_L3; - txop = &txo; - } + print("%s", line); 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,18 +132,16 @@ 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\n", line); + 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\n", dead_streak); + print("stopping after %u consecutive timeout hops", dead_streak); break; } } @@ -175,49 +150,55 @@ 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'\n", (int)dr, o->host); + 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)); - print("Tracing route to %s [%s]\n", o->host, 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); 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); 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,18 +209,16 @@ 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\n", line); + 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\n", dead_streak); + 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\n"); + 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; } diff --git a/kernel/virtio/virtio_pci.c b/kernel/virtio/virtio_pci.c index 76c1259d..d706d834 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; } @@ -223,26 +234,29 @@ 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; 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; *(volatile uint16_t*)((uintptr_t)dev->notify_cfg + (uint64_t)off * (uint64_t)mul) = value; } -bool virtio_send_nd(virtio_device *dev, const virtio_buf *bufs, uint16_t n) { +void virtio_notify(virtio_device *dev) { + if (!dev) return; + 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; @@ -276,6 +290,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; } diff --git a/kernel/virtio/virtio_pci.h b/kernel/virtio/virtio_pci.h index 2744b651..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 @@ -107,6 +109,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); 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 434ec1fd..fe0d6a80 100644 --- a/modules/MakefileModule +++ b/modules/MakefileModule @@ -13,11 +13,12 @@ endif CFLAGS := $(CFLAGS_BASE) $(BASE_FLAGS) CXXFLAGS := $(CXXFLAGS_BASE) $(BASE_FLAGS) -CLEAN_OBJS := $(shell find $(DRIVER_TARGET) -name "*.o") $(shell find ./common -name "*.o" 2>/dev/null) -CLEAN_DEPS := $(shell find $(DRIVER_TARGET) -name "*.d") $(shell find ./common -name "*.d" 2>/dev/null) -C_SRC := $(shell find $(DRIVER_TARGET) -name "*.c") $(shell find ./common -name "*.c" 2>/dev/null) -CPP_SRC := $(shell find $(DRIVER_TARGET) -name "*.cpp") $(shell find ./common -name "*.cpp" 2>/dev/null) -ASM_SRC := $(shell find $(DRIVER_TARGET) -name "*.S") $(shell find ./common -name "*.S" 2>/dev/null) +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/modules/graph/virt/ramfb.cpp b/modules/graph/virt/ramfb.cpp index 1bda5887..c7cc6dba 100644 --- a/modules/graph/virt/ramfb.cpp +++ b/modules/graph/virt/ramfb.cpp @@ -9,6 +9,14 @@ #include "sysregs.h" #include "memory/addr.h" +bool verbose = true; +#define kprintfv(fmt, ...) \ + ({ \ + if (verbose){\ + kprintf(fmt, ##__VA_ARGS__); \ + }\ + }) + typedef struct { uint64_t addr; uint32_t fourcc; @@ -39,26 +47,34 @@ bool RamFBGPUDriver::init(gpu_size preferred_screen_size){ // preferred_screen_size.height /= 2; // #endif 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 error] 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, @@ -87,7 +103,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 77ec8429..a30a7799 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 diff --git a/run_virt b/run_virt index fb8c6b07..789bd76f 100755 --- a/run_virt +++ b/run_virt @@ -30,8 +30,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" diff --git a/shared b/shared index 1442cef4..74aefd38 160000 --- a/shared +++ b/shared @@ -1 +1 @@ -Subproject commit 1442cef44fcf72143332558c5f86be9f7d9f6ca6 +Subproject commit 74aefd38abdc3291145a08da71bb37dd7b93e41a diff --git a/user/Makefile b/user/Makefile index 7de89320..2df59e1c 100644 --- a/user/Makefile +++ b/user/Makefile @@ -1,8 +1,9 @@ include ../common.mk -SUBDIRS := $(wildcard */.) -SUBCLEAN = $(addsuffix .clean,$(SUBDIRS)) -SUBDUMP = $(addsuffix .dump,$(SUBDIRS)) +USER_DIRS := $(sort $(dir $(wildcard */*.S */*.c */*.cpp))) +SUBDIRS := $(filter-out build/ resources/ %.red/,$(USER_DIRS)) +SUBCLEAN := $(addsuffix .clean,$(SUBDIRS)) +SUBDUMP := $(addsuffix .dump,$(SUBDIRS)) MAKEFILE := $(shell pwd)/UserMakefile all: $(SUBDIRS) @@ -11,7 +12,8 @@ $(SUBDIRS): $(MAKE) -f $(MAKEFILE) -C $@ clean: $(SUBCLEAN) - + $(RM) -r build *.red dump + dump: $(SUBDUMP) $(SUBCLEAN): %.clean: @@ -20,6 +22,4 @@ $(SUBCLEAN): %.clean: $(SUBDUMP): %.dump: $(MAKE) -f $(MAKEFILE) -C $* dump -clean: $(SUBCLEAN) - -.PHONY: all $(SUBDIRS) \ No newline at end of file +.PHONY: all clean dump $(SUBDIRS) $(SUBCLEAN) $(SUBDUMP) diff --git a/user/UserMakefile b/user/UserMakefile index da135fa9..5af046a9 100644 --- a/user/UserMakefile +++ b/user/UserMakefile @@ -20,7 +20,12 @@ LOCATION := ../../fs/redos/system/ .PHONY: prepare all clean +ifeq ($(strip $(OBJ)),) +all: + @echo "Skipping $(NAME): no source files" +else all: prepare $(PACKAGE)/$(TARGET) +endif prepare: mkdir -p resources @@ -29,14 +34,10 @@ prepare: cp -r resources $(PACKAGE) $(PACKAGE)/$(TARGET): ../../shared/libshared.a $(OBJ) - $(VLD) $(LDFLAGS) -o $(PACKAGE)/$(ELF) $(addprefix $(BUILD_DIR)/,$(notdir $(OBJ))) ../../shared/libshared.a + $(VLD) $(LDFLAGS) -o $(PACKAGE)/$(ELF) $(OBJ) ../../shared/libshared.a $(OBJCOPY) -O binary $(PACKAGE)/$(ELF) $@ cp -r $(PACKAGE) $(LOCATION) -$(BUILD_DIR)/%.o: %.S - @mkdir -p $(dir $@) - $(VAS) $(CFLAGS) -c $< -o $@ - $(BUILD_DIR)/%.o: %.c @mkdir -p $(dir $@) $(VCC) $(CFLAGS) -c -MMD -MP $< -o $@ @@ -50,6 +51,6 @@ clean: $(RM) -r $(PACKAGE) $(BUILD_DIR) dump: all - $(ARCH)objdump -S $(NAME).red/$(NAME).elf > dump + $(ARCH)objdump -S $(PACKAGE)/$(ELF) > dump -include $(DEP) \ No newline at end of file diff --git a/user/demo/default_process.c b/user/demo/default_process.c index ca841dc2..88449fea 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" #include "math/math.h" #include "draw/textdraw.h" #include "environment/env_types.h" @@ -54,22 +55,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 = zalloc(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); @@ -80,13 +83,171 @@ 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(sock, &rc, str, strlen(str))); - socket_close(&spec); + socket_close(sock); return 1; } +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; + + //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];