From 6b83cf559d4b9e361d0d211b440d09f94eda195b Mon Sep 17 00:00:00 2001 From: Timo Gatsonides Date: Sat, 3 Jan 2026 00:22:56 +0100 Subject: [PATCH 1/3] net: add DHCP Option 121 (Classless Static Routes) support Implement RFC 3442 classless static routes received via DHCP: - Add IPv4 static route table with longest-prefix-match lookup - Parse Option 121 from DHCP ACK via LWIP_HOOK_DHCP_PARSE_OPTION - Consult static routes during IP routing via LWIP_HOOK_IP4_ROUTE_SRC - Ignore Option 3 (Router) when Option 121 is present per RFC 3442 - Request Option 121 in DHCP parameter request list - Clean up routes on DHCP release/stop Note: vendor/lwip/src/core/ipv4/dhcp.c also requires modification (see Makefile). --- Makefile | 2 +- platform/pc/Makefile | 2 + platform/riscv-virt/Makefile | 2 + platform/virt/Makefile | 2 + src/net/dhcp_option121.c | 213 ++++++++++++++++++++++++++++ src/net/dhcp_option121.h | 69 +++++++++ src/net/ip4_route_table.c | 261 +++++++++++++++++++++++++++++++++++ src/net/ip4_route_table.h | 84 +++++++++++ src/net/lwipopts.h | 36 +++++ src/net/net.c | 4 + 10 files changed, 674 insertions(+), 1 deletion(-) create mode 100644 src/net/dhcp_option121.c create mode 100644 src/net/dhcp_option121.h create mode 100644 src/net/ip4_route_table.c create mode 100644 src/net/ip4_route_table.h diff --git a/Makefile b/Makefile index 8234ac016..1bf3a6d74 100644 --- a/Makefile +++ b/Makefile @@ -54,7 +54,7 @@ endif THIRD_PARTY= $(ACPICA_DIR)/.vendored $(LWIPDIR)/.vendored $(MBEDTLS_DIR)/.vendored $(ACPICA_DIR)/.vendored: GITFLAGS= --depth 1 https://github.com/acpica/acpica.git -b R09_30_21 -$(LWIPDIR)/.vendored: GITFLAGS= --depth 1 https://github.com/nanovms/lwip.git -b STABLE-2_1_x +$(LWIPDIR)/.vendored: GITFLAGS= --depth 1 https://github.com/tpjg/lwip.git -b STABLE-2_1_x $(MBEDTLS_DIR)/.vendored: GITFLAGS= --depth 1 https://github.com/nanovms/mbedtls.git kernel: $(THIRD_PARTY) contgen diff --git a/platform/pc/Makefile b/platform/pc/Makefile index 67f0bfdd9..4740144a8 100644 --- a/platform/pc/Makefile +++ b/platform/pc/Makefile @@ -52,6 +52,8 @@ SRCS-kernel.elf= \ $(SRCDIR)/kernel/vdso-now.c \ $(SRCDIR)/kernel/vmem_heap.c \ $(SRCDIR)/net/direct.c \ + $(SRCDIR)/net/dhcp_option121.c \ + $(SRCDIR)/net/ip4_route_table.c \ $(SRCDIR)/net/net.c \ $(SRCDIR)/net/netsyscall.c \ $(RUNTIME) \ diff --git a/platform/riscv-virt/Makefile b/platform/riscv-virt/Makefile index 3e59bc388..c211ef0dc 100644 --- a/platform/riscv-virt/Makefile +++ b/platform/riscv-virt/Makefile @@ -48,6 +48,8 @@ SRCS-kernel.elf= \ $(SRCDIR)/kernel/vdso-now.c \ $(SRCDIR)/kernel/vmem_heap.c \ $(SRCDIR)/net/direct.c \ + $(SRCDIR)/net/dhcp_option121.c \ + $(SRCDIR)/net/ip4_route_table.c \ $(SRCDIR)/net/net.c \ $(SRCDIR)/net/netsyscall.c \ $(RUNTIME) \ diff --git a/platform/virt/Makefile b/platform/virt/Makefile index fc870390c..eece331d2 100644 --- a/platform/virt/Makefile +++ b/platform/virt/Makefile @@ -74,6 +74,8 @@ SRCS-kernel.elf= \ $(SRCDIR)/kernel/vdso-now.c \ $(SRCDIR)/kernel/vmem_heap.c \ $(SRCDIR)/net/direct.c \ + $(SRCDIR)/net/dhcp_option121.c \ + $(SRCDIR)/net/ip4_route_table.c \ $(SRCDIR)/net/net.c \ $(SRCDIR)/net/netsyscall.c \ $(RUNTIME) \ diff --git a/src/net/dhcp_option121.c b/src/net/dhcp_option121.c new file mode 100644 index 000000000..e5b972e15 --- /dev/null +++ b/src/net/dhcp_option121.c @@ -0,0 +1,213 @@ +/* + * DHCP Option 121 (Classless Static Routes) Parser - RFC 3442 + * + * Parses classless static routes from DHCP responses and populates + * the IPv4 static route table. + */ + +#include +#include +#include +#include +#include + +/* + * Global to track the current netif during DHCP receive processing. + * Set by dhcp_recv() before calling dhcp_parse_reply() so that + * ip_current_netif() can return the correct value for hooks. + * + * This is needed because nanos's lwIP modifications pass ip_data + * as a parameter rather than using a global, but the DHCP option + * hook still uses ip_current_netif(). + * + * Safe because lwIP runs in single-threaded context (NO_SYS=1) and + * DHCP packets are processed sequentially in the network timer context. + */ +struct netif *nanos_dhcp_current_netif; + +/* Per-interface tracking of whether Option 121 was received */ +static u8_t option121_received[DHCP_OPTION121_MAX_NETIFS]; + +/* Spinlock for thread-safe access to option121_received state */ +static struct spinlock option121_lock; + +/* Get a safe index for the option121_received array, returns -1 if out of bounds */ +static inline int get_netif_idx(struct netif *netif) +{ + if (netif == NULL) + return -1; + u8_t idx = netif_get_index(netif); + if (idx == 0 || idx > DHCP_OPTION121_MAX_NETIFS) + return -1; + return idx - 1; +} + +/* Calculate significant octets for a prefix length: ceil(prefix_len / 8) */ +static inline u8_t prefix_to_octets(u8_t prefix_len) +{ + return (prefix_len + 7) / 8; +} + +void dhcp_option121_init(void) +{ + spin_lock_init(&option121_lock); + runtime_memset(option121_received, 0, sizeof(option121_received)); +} + +int dhcp_parse_option121(struct netif *netif, struct pbuf *p, + u16_t offset, u8_t len) +{ + int routes_parsed = 0; + u16_t pos = offset; + u16_t end; + int netif_idx; + + if (netif == NULL || p == NULL) + return ERR_ARG; /* ERR_ARG is already negative */ + + netif_idx = get_netif_idx(netif); + if (netif_idx < 0) + return ERR_ARG; + + /* overflow check */ + if (offset > 0xFFFF - len) + return ERR_ARG; + end = offset + len; + + /* empty option is valid but contains no routes */ + if (len == 0) + return 0; + + /* + * Clear any existing DHCP routes for this interface first. + * This ensures we replace old routes with new ones on lease renewal. + */ + ip4_route_remove_dhcp(netif); + + /* + * Parse each route entry in the option. + * Format per RFC 3442: + * 1 byte: prefix length (0-32) + * N bytes: significant octets of destination (N = ceil(prefix_len/8)) + * 4 bytes: gateway IP address + */ + while (pos < end) { + u8_t prefix_len; + u8_t significant_octets; + ip4_addr_t dest; + ip4_addr_t gateway; + u8_t dest_bytes[4] = {0, 0, 0, 0}; + + /* read prefix length */ + if (pbuf_copy_partial(p, &prefix_len, 1, pos) != 1) + break; + pos++; + + if (prefix_len > 32) + break; + + significant_octets = prefix_to_octets(prefix_len); + + /* check we have enough data remaining */ + if (pos + significant_octets + 4 > end) + break; + + /* read destination network (significant octets only) */ + if (significant_octets > 0) { + if (pbuf_copy_partial(p, dest_bytes, significant_octets, pos) + != significant_octets) + break; + } + pos += significant_octets; + + /* + * Reconstruct destination address. + * The significant octets are the high-order bytes of the address. + * dest_bytes is already zero-initialized for non-significant octets. + */ + dest.addr = (dest_bytes[0] << 24) | (dest_bytes[1] << 16) | + (dest_bytes[2] << 8) | dest_bytes[3]; + dest.addr = lwip_htonl(dest.addr); + + /* apply mask to ensure destination is properly masked (RFC 3442) */ + dest.addr &= ip4_prefix_to_mask(prefix_len); + + /* read gateway address - already in network byte order */ + if (pbuf_copy_partial(p, &gateway.addr, 4, pos) != 4) + break; + pos += 4; + + /* + * Validate gateway address: + * - Reject multicast/broadcast (invalid as next-hop) + * - 0.0.0.0 is valid per RFC 3442: means destination is on-link + * (directly reachable on this interface without a gateway) + */ + if (ip4_addr_ismulticast(&gateway) || + ip4_addr_isbroadcast(&gateway, netif)) + continue; + + /* add route to table */ + if (ip4_route_add(&dest, prefix_len, &gateway, netif, + IP4_ROUTE_FLAG_DHCP) == ERR_OK) { + routes_parsed++; + } + } + + /* + * Mark that this interface received Option 121. + * This is used to determine whether to ignore Option 3 (Router). + */ + if (routes_parsed > 0) { + spin_lock(&option121_lock); + option121_received[netif_idx] = 1; + spin_unlock(&option121_lock); + } + + return routes_parsed; +} + +boolean dhcp_option121_received(struct netif *netif) +{ + boolean result = false; + int netif_idx = get_netif_idx(netif); + + if (netif_idx < 0) + return false; + + spin_lock(&option121_lock); + result = (option121_received[netif_idx] != 0); + spin_unlock(&option121_lock); + + return result; +} + +void dhcp_option121_clear(struct netif *netif) +{ + int netif_idx = get_netif_idx(netif); + + if (netif_idx < 0) + return; + + spin_lock(&option121_lock); + option121_received[netif_idx] = 0; + spin_unlock(&option121_lock); + + ip4_route_remove_dhcp(netif); +} + +void nanos_dhcp_parse_option_hook(struct netif *netif, struct dhcp *dhcp, + u8_t state, struct dhcp_msg *msg, + u8_t msg_type, u8_t option, u8_t len, + struct pbuf *p, u16_t offset) +{ + LWIP_UNUSED_ARG(dhcp); + LWIP_UNUSED_ARG(state); + LWIP_UNUSED_ARG(msg); + + /* only process Option 121 from DHCP ACK messages */ + if (option == DHCP_OPTION_CLASSLESS_STATIC_ROUTE && + msg_type == DHCP_ACK) { + dhcp_parse_option121(netif, p, offset, len); + } +} diff --git a/src/net/dhcp_option121.h b/src/net/dhcp_option121.h new file mode 100644 index 000000000..53430c1b0 --- /dev/null +++ b/src/net/dhcp_option121.h @@ -0,0 +1,69 @@ +/* + * DHCP Option 121 (Classless Static Routes) Parser - RFC 3442 + * + * Parses DHCP Option 121 responses and populates the IPv4 static route + * table with classless static routes provided by the DHCP server. + */ + +#ifndef __DHCP_OPTION121_H__ +#define __DHCP_OPTION121_H__ + +#include +#include +#include + +/* DHCP Option 121 - Classless Static Route (RFC 3442) */ +#define DHCP_OPTION_CLASSLESS_STATIC_ROUTE 121 + +/* + * Maximum number of network interfaces we track for Option 121 state. + * Should match or exceed the number of interfaces in the system. + */ +#ifndef DHCP_OPTION121_MAX_NETIFS +#define DHCP_OPTION121_MAX_NETIFS 16 +#endif + +/* Initialize the Option 121 module - must be called before other functions */ +void dhcp_option121_init(void); + +/* + * Parse DHCP Option 121 data and add routes to the static route table. + * + * Parses variable-length encoding per RFC 3442: + * 1 byte: prefix length (0-32) + * N bytes: significant octets of destination (N = ceil(prefix_len/8)) + * 4 bytes: gateway IP address + * + * Routes are added with IP4_ROUTE_FLAG_DHCP flag. Existing DHCP routes + * for the interface are cleared first. + * + * Returns number of routes successfully added (>= 0), or negative error. + */ +int dhcp_parse_option121(struct netif *netif, struct pbuf *p, + u16_t offset, u8_t len); + +/* + * Check if Option 121 was received for an interface. + * Per RFC 3442, if Option 121 is present, Option 3 (Router) must be ignored. + */ +boolean dhcp_option121_received(struct netif *netif); + +/* + * Clear Option 121 state and routes for a network interface. + * Called when DHCP lease is released, interface is removed, etc. + */ +void dhcp_option121_clear(struct netif *netif); + +/* + * lwIP DHCP option parse hook (LWIP_HOOK_DHCP_PARSE_OPTION). + * Called by lwIP for each unknown DHCP option, parses Option 121. + */ +struct dhcp; +struct dhcp_msg; + +void nanos_dhcp_parse_option_hook(struct netif *netif, struct dhcp *dhcp, + u8_t state, struct dhcp_msg *msg, + u8_t msg_type, u8_t option, u8_t len, + struct pbuf *p, u16_t offset); + +#endif /* __DHCP_OPTION121_H__ */ diff --git a/src/net/ip4_route_table.c b/src/net/ip4_route_table.c new file mode 100644 index 000000000..18db89690 --- /dev/null +++ b/src/net/ip4_route_table.c @@ -0,0 +1,261 @@ +/* + * IPv4 static route table implementation + * + * Provides a fixed-size routing table with longest-prefix-match lookup, + * primarily for DHCP Option 121 (RFC 3442) classless static routes. + */ + +#include +#include +#include + +/* Route table storage - sorted by prefix_len descending for LPM */ +static struct ip4_route_entry route_table[LWIP_IPV4_NUM_ROUTE_ENTRIES]; + +/* Spinlock for thread-safe access */ +static struct spinlock route_lock; + +/* Number of active entries in the table */ +static int route_entry_count; + +/* Compute netmask from prefix length in network byte order */ +u32_t ip4_prefix_to_mask(u8_t prefix_len) +{ + if (prefix_len == 0) + return 0; + if (prefix_len >= 32) + return 0xFFFFFFFF; + return PP_HTONL(~((1UL << (32 - prefix_len)) - 1)); +} + +/* Check if a destination address matches a route entry */ +static inline int route_matches(const struct ip4_route_entry *entry, + const ip4_addr_t *dest) +{ + u32_t mask = ip4_prefix_to_mask(entry->prefix_len); + return (dest->addr & mask) == (entry->dest.addr & mask); +} + +/* Find insertion point to maintain sorted order (descending prefix_len) */ +static int find_insert_position(u8_t prefix_len) +{ + int i; + for (i = 0; i < route_entry_count; i++) { + if (prefix_len > route_table[i].prefix_len) + return i; + } + return route_entry_count; +} + +/* Find an existing route matching dest/prefix_len/netif, returns index or -1 */ +static int find_existing_route(const ip4_addr_t *dest, u8_t prefix_len, + struct netif *netif) +{ + u32_t mask = ip4_prefix_to_mask(prefix_len); + u32_t masked_dest = dest->addr & mask; + + for (int i = 0; i < route_entry_count; i++) { + if (route_table[i].prefix_len == prefix_len && + (route_table[i].dest.addr & mask) == masked_dest && + (netif == NULL || route_table[i].netif == netif)) { + return i; + } + } + return -1; +} + +void ip4_route_table_init(void) +{ + spin_lock_init(&route_lock); + runtime_memset((u8 *)route_table, 0, sizeof(route_table)); + route_entry_count = 0; +} + +err_t ip4_route_add(const ip4_addr_t *dest, u8_t prefix_len, + const ip4_addr_t *gateway, struct netif *netif, u8_t flags) +{ + err_t ret = ERR_OK; + + if (dest == NULL || gateway == NULL || netif == NULL) + return ERR_ARG; + if (prefix_len > IP4_MAX_PREFIX_LEN) + return ERR_ARG; + + spin_lock(&route_lock); + + /* check for existing route with same dest/prefix/netif and update it */ + int existing = find_existing_route(dest, prefix_len, netif); + if (existing >= 0) { + ip4_addr_copy(route_table[existing].gateway, *gateway); + route_table[existing].flags = flags; + goto out; + } + + if (route_entry_count >= LWIP_IPV4_NUM_ROUTE_ENTRIES) { + ret = ERR_MEM; + goto out; + } + + /* find insertion point to maintain sorted order */ + int pos = find_insert_position(prefix_len); + + /* shift entries down to make room */ + for (int i = route_entry_count; i > pos; i--) { + runtime_memcpy(&route_table[i], &route_table[i - 1], + sizeof(struct ip4_route_entry)); + } + + /* insert new entry */ + u32_t mask = ip4_prefix_to_mask(prefix_len); + route_table[pos].dest.addr = dest->addr & mask; + ip4_addr_copy(route_table[pos].gateway, *gateway); + route_table[pos].prefix_len = prefix_len; + route_table[pos].flags = flags; + route_table[pos].netif = netif; + route_entry_count++; + +out: + spin_unlock(&route_lock); + return ret; +} + +void ip4_route_remove(const ip4_addr_t *dest, u8_t prefix_len, struct netif *netif) +{ + if (dest == NULL || prefix_len > IP4_MAX_PREFIX_LEN) + return; + + spin_lock(&route_lock); + + int idx = find_existing_route(dest, prefix_len, netif); + if (idx >= 0) { + for (int i = idx; i < route_entry_count - 1; i++) { + runtime_memcpy(&route_table[i], &route_table[i + 1], + sizeof(struct ip4_route_entry)); + } + runtime_memset((u8 *)&route_table[route_entry_count - 1], 0, + sizeof(struct ip4_route_entry)); + route_entry_count--; + } + + spin_unlock(&route_lock); +} + +void ip4_route_remove_netif(struct netif *netif) +{ + if (netif == NULL) + return; + + spin_lock(&route_lock); + + int i = 0; + while (i < route_entry_count) { + if (route_table[i].netif == netif) { + for (int j = i; j < route_entry_count - 1; j++) { + runtime_memcpy(&route_table[j], &route_table[j + 1], + sizeof(struct ip4_route_entry)); + } + runtime_memset((u8 *)&route_table[route_entry_count - 1], 0, + sizeof(struct ip4_route_entry)); + route_entry_count--; + } else { + i++; + } + } + + spin_unlock(&route_lock); +} + +void ip4_route_remove_dhcp(struct netif *netif) +{ + if (netif == NULL) + return; + + spin_lock(&route_lock); + + int i = 0; + while (i < route_entry_count) { + if (route_table[i].netif == netif && + (route_table[i].flags & IP4_ROUTE_FLAG_DHCP)) { + for (int j = i; j < route_entry_count - 1; j++) { + runtime_memcpy(&route_table[j], &route_table[j + 1], + sizeof(struct ip4_route_entry)); + } + runtime_memset((u8 *)&route_table[route_entry_count - 1], 0, + sizeof(struct ip4_route_entry)); + route_entry_count--; + } else { + i++; + } + } + + spin_unlock(&route_lock); +} + +boolean ip4_route_find(const ip4_addr_t *dest, struct ip4_route_entry *out_entry) +{ + boolean found = false; + + if (dest == NULL) + return false; + + spin_lock(&route_lock); + + /* + * Table is sorted by prefix_len descending, so the first match + * is the longest prefix match. + */ + for (int i = 0; i < route_entry_count; i++) { + if (route_table[i].netif != NULL && route_matches(&route_table[i], dest)) { + if (out_entry != NULL) + runtime_memcpy(out_entry, &route_table[i], sizeof(*out_entry)); + found = true; + break; + } + } + + spin_unlock(&route_lock); + return found; +} + +struct netif *ip4_static_route(const ip4_addr_t *src, const ip4_addr_t *dest) +{ + struct ip4_route_entry entry; + LWIP_UNUSED_ARG(src); + + if (ip4_route_find(dest, &entry)) + return entry.netif; + return NULL; +} + +boolean ip4_get_gateway(const ip4_addr_t *dest, ip4_addr_t *out_gateway) +{ + struct ip4_route_entry entry; + + if (ip4_route_find(dest, &entry)) { + if (out_gateway != NULL) + ip4_addr_copy(*out_gateway, entry.gateway); + return true; + } + return false; +} + +/* + * Get read-only access to route table for debugging/netlink. + * Note: Caller must not hold route_lock. The returned pointer is valid + * but entries may change if routes are modified concurrently. + */ +const struct ip4_route_entry *ip4_get_route_table(int *count) +{ + if (count != NULL) + *count = route_entry_count; + return route_table; +} + +int ip4_route_count(void) +{ + int count; + spin_lock(&route_lock); + count = route_entry_count; + spin_unlock(&route_lock); + return count; +} diff --git a/src/net/ip4_route_table.h b/src/net/ip4_route_table.h new file mode 100644 index 000000000..6e05b21d6 --- /dev/null +++ b/src/net/ip4_route_table.h @@ -0,0 +1,84 @@ +/* + * IPv4 static route table for DHCP Option 121 (Classless Static Routes) + * + * Provides a static routing table for IPv4 with longest-prefix-match lookups. + * Primarily used to store routes learned from DHCP Option 121 (RFC 3442). + */ + +#ifndef __IP4_ROUTE_TABLE_H__ +#define __IP4_ROUTE_TABLE_H__ + +#include +#include +#include + +/* Maximum number of static routes (fixed-size to avoid dynamic allocation) */ +#ifndef LWIP_IPV4_NUM_ROUTE_ENTRIES +#define LWIP_IPV4_NUM_ROUTE_ENTRIES 16 +#endif + +#define IP4_MAX_PREFIX_LEN 32 + +/* Route entry flags */ +#define IP4_ROUTE_FLAG_NONE 0x00 +#define IP4_ROUTE_FLAG_DHCP 0x01 /* Route learned from DHCP Option 121 */ +#define IP4_ROUTE_FLAG_STATIC 0x02 /* Manually configured static route */ + +struct ip4_route_entry { + ip4_addr_t dest; /* Destination network address (masked) */ + ip4_addr_t gateway; /* Next-hop gateway IP address */ + u8_t prefix_len; /* CIDR prefix length (0-32) */ + u8_t flags; /* Route flags (IP4_ROUTE_FLAG_*) */ + struct netif *netif; /* Associated network interface (NULL = unused entry) */ +}; + +/* Compute netmask from prefix length in network byte order */ +u32_t ip4_prefix_to_mask(u8_t prefix_len); + +/* Initialize the route table - must be called before other functions */ +void ip4_route_table_init(void); + +/* + * Add a route to the static route table. + * Table is kept sorted by prefix length (longest first) for LPM lookups. + * Returns ERR_OK on success, ERR_MEM if table full, ERR_ARG if invalid args. + */ +err_t ip4_route_add(const ip4_addr_t *dest, u8_t prefix_len, + const ip4_addr_t *gateway, struct netif *netif, u8_t flags); + +/* Remove a specific route from the table */ +void ip4_route_remove(const ip4_addr_t *dest, u8_t prefix_len, struct netif *netif); + +/* Remove all routes associated with a network interface */ +void ip4_route_remove_netif(struct netif *netif); + +/* Remove all DHCP-learned routes for a network interface */ +void ip4_route_remove_dhcp(struct netif *netif); + +/* + * Find the best matching route for a destination address (longest prefix match). + * Copies route entry to out_entry if found. + * Returns true if route found, false otherwise. + */ +boolean ip4_route_find(const ip4_addr_t *dest, struct ip4_route_entry *out_entry); + +/* + * Route lookup hook for lwIP integration (LWIP_HOOK_IP4_ROUTE_SRC). + * Returns network interface to use, or NULL if no static route matches. + */ +struct netif *ip4_static_route(const ip4_addr_t *src, const ip4_addr_t *dest); + +/* + * Get the gateway address for a destination from the static route table. + * Copies gateway to out_gateway if found. + * Returns true if route found, false otherwise. + */ +boolean ip4_get_gateway(const ip4_addr_t *dest, ip4_addr_t *out_gateway); + +/* Get read-only access to route table for debugging/netlink reporting */ +const struct ip4_route_entry *ip4_get_route_table(int *count); + +/* Get the number of active routes in the table */ +int ip4_route_count(void); + +#endif /* __IP4_ROUTE_TABLE_H__ */ diff --git a/src/net/lwipopts.h b/src/net/lwipopts.h index a58ff75a1..e196cd21e 100644 --- a/src/net/lwipopts.h +++ b/src/net/lwipopts.h @@ -198,3 +198,39 @@ static inline int net_ip_input_hook(struct pbuf *pbuf, struct netif *input_netif return 1; return 0; } + +/* + * DHCP Option 121 (Classless Static Routes) support. + * + * The nanos lwIP modification passes ip_data as a parameter rather than + * using a global. However, dhcp_parse_reply() calls the option hook using + * ip_current_netif() which expects a global. We work around this by: + * 1. Defining a global that stores the current netif during DHCP recv + * 2. Providing ip_current_netif() macro to access it + * 3. Implementing the hook to use this netif + * + * The global is set by the UDP recv callback wrapper before calling dhcp_recv. + */ +extern struct netif *nanos_dhcp_current_netif; + +static inline struct netif *ip_current_netif(void) +{ + return nanos_dhcp_current_netif; +} + +struct dhcp; +struct dhcp_msg; +extern void nanos_dhcp_parse_option_hook(struct netif *netif, struct dhcp *dhcp, + u8_t state, struct dhcp_msg *msg, + u8_t msg_type, u8_t option, u8_t len, + struct pbuf *p, u16_t offset); +#define LWIP_HOOK_DHCP_PARSE_OPTION(netif, dhcp, state, msg, msg_type, option, len, pbuf, offset) \ + nanos_dhcp_parse_option_hook(netif, dhcp, state, msg, msg_type, option, len, pbuf, offset) + +/* + * Static route lookup hook for DHCP Option 121 routes. + * Called by lwIP's ip4_route() to check static routes before the default route. + */ +struct ip4_addr; +extern struct netif *ip4_static_route(const struct ip4_addr *src, const struct ip4_addr *dest); +#define LWIP_HOOK_IP4_ROUTE_SRC(src, dest) ip4_static_route(src, dest) diff --git a/src/net/net.c b/src/net/net.c index 41c00e83c..93432d1b3 100644 --- a/src/net/net.c +++ b/src/net/net.c @@ -1,6 +1,8 @@ #include #include #include +#include +#include /* Network interface flags */ #define IFF_UP (1 << 0) @@ -413,6 +415,8 @@ void init_net(kernel_heaps kh) { lwip_heap = kh->malloc; list_init(&net_complete_list); + ip4_route_table_init(); + dhcp_option121_init(); lwip_init(); BSS_RO_AFTER_INIT NETIF_DECLARE_EXT_CALLBACK(netif_callback); netif_add_ext_callback(&netif_callback, lwip_ext_callback); From 781dd53f876cfd7e61d6bb2a1d6d891a63c610a2 Mon Sep 17 00:00:00 2001 From: Timo Gatsonides Date: Fri, 20 Mar 2026 17:24:43 +0100 Subject: [PATCH 2/3] net: move DHCP Option 121 implementation into lwip Remove route table and option parser from nanos; now built as native lwip feature via LWIP_DHCP_CLASSLESS_STATIC_ROUTES in lwipopts.h. --- platform/pc/Makefile | 4 +- platform/riscv-virt/Makefile | 4 +- platform/virt/Makefile | 4 +- src/net/dhcp_option121.c | 213 ---------------------------- src/net/dhcp_option121.h | 69 --------- src/net/ip4_route_table.c | 261 ----------------------------------- src/net/ip4_route_table.h | 84 ----------- src/net/lwipopts.h | 30 +--- src/net/net.c | 4 - 9 files changed, 8 insertions(+), 665 deletions(-) delete mode 100644 src/net/dhcp_option121.c delete mode 100644 src/net/dhcp_option121.h delete mode 100644 src/net/ip4_route_table.c delete mode 100644 src/net/ip4_route_table.h diff --git a/platform/pc/Makefile b/platform/pc/Makefile index 4740144a8..099f80a9f 100644 --- a/platform/pc/Makefile +++ b/platform/pc/Makefile @@ -52,8 +52,6 @@ SRCS-kernel.elf= \ $(SRCDIR)/kernel/vdso-now.c \ $(SRCDIR)/kernel/vmem_heap.c \ $(SRCDIR)/net/direct.c \ - $(SRCDIR)/net/dhcp_option121.c \ - $(SRCDIR)/net/ip4_route_table.c \ $(SRCDIR)/net/net.c \ $(SRCDIR)/net/netsyscall.c \ $(RUNTIME) \ @@ -151,6 +149,8 @@ SRCS-lwip= \ $(LWIPDIR)/src/core/ipv4/ip4_addr.c \ $(LWIPDIR)/src/core/ipv4/ip4_frag.c \ $(LWIPDIR)/src/core/ipv4/ip4.c \ + $(LWIPDIR)/src/core/ipv4/ip4_route_table.c \ + $(LWIPDIR)/src/core/ipv4/dhcp_classless_route.c \ $(LWIPDIR)/src/core/ipv6/dhcp6.c \ $(LWIPDIR)/src/core/ipv6/ethip6.c \ $(LWIPDIR)/src/core/ipv6/icmp6.c \ diff --git a/platform/riscv-virt/Makefile b/platform/riscv-virt/Makefile index c211ef0dc..53a17b8af 100644 --- a/platform/riscv-virt/Makefile +++ b/platform/riscv-virt/Makefile @@ -48,8 +48,6 @@ SRCS-kernel.elf= \ $(SRCDIR)/kernel/vdso-now.c \ $(SRCDIR)/kernel/vmem_heap.c \ $(SRCDIR)/net/direct.c \ - $(SRCDIR)/net/dhcp_option121.c \ - $(SRCDIR)/net/ip4_route_table.c \ $(SRCDIR)/net/net.c \ $(SRCDIR)/net/netsyscall.c \ $(RUNTIME) \ @@ -110,6 +108,8 @@ SRCS-lwip= \ $(LWIPDIR)/src/core/ipv4/ip4_addr.c \ $(LWIPDIR)/src/core/ipv4/ip4_frag.c \ $(LWIPDIR)/src/core/ipv4/ip4.c \ + $(LWIPDIR)/src/core/ipv4/ip4_route_table.c \ + $(LWIPDIR)/src/core/ipv4/dhcp_classless_route.c \ $(LWIPDIR)/src/core/ipv6/dhcp6.c \ $(LWIPDIR)/src/core/ipv6/ethip6.c \ $(LWIPDIR)/src/core/ipv6/icmp6.c \ diff --git a/platform/virt/Makefile b/platform/virt/Makefile index eece331d2..f5eab9cff 100644 --- a/platform/virt/Makefile +++ b/platform/virt/Makefile @@ -74,8 +74,6 @@ SRCS-kernel.elf= \ $(SRCDIR)/kernel/vdso-now.c \ $(SRCDIR)/kernel/vmem_heap.c \ $(SRCDIR)/net/direct.c \ - $(SRCDIR)/net/dhcp_option121.c \ - $(SRCDIR)/net/ip4_route_table.c \ $(SRCDIR)/net/net.c \ $(SRCDIR)/net/netsyscall.c \ $(RUNTIME) \ @@ -138,6 +136,8 @@ SRCS-lwip= \ $(LWIPDIR)/src/core/ipv4/ip4_addr.c \ $(LWIPDIR)/src/core/ipv4/ip4_frag.c \ $(LWIPDIR)/src/core/ipv4/ip4.c \ + $(LWIPDIR)/src/core/ipv4/ip4_route_table.c \ + $(LWIPDIR)/src/core/ipv4/dhcp_classless_route.c \ $(LWIPDIR)/src/core/ipv6/dhcp6.c \ $(LWIPDIR)/src/core/ipv6/ethip6.c \ $(LWIPDIR)/src/core/ipv6/icmp6.c \ diff --git a/src/net/dhcp_option121.c b/src/net/dhcp_option121.c deleted file mode 100644 index e5b972e15..000000000 --- a/src/net/dhcp_option121.c +++ /dev/null @@ -1,213 +0,0 @@ -/* - * DHCP Option 121 (Classless Static Routes) Parser - RFC 3442 - * - * Parses classless static routes from DHCP responses and populates - * the IPv4 static route table. - */ - -#include -#include -#include -#include -#include - -/* - * Global to track the current netif during DHCP receive processing. - * Set by dhcp_recv() before calling dhcp_parse_reply() so that - * ip_current_netif() can return the correct value for hooks. - * - * This is needed because nanos's lwIP modifications pass ip_data - * as a parameter rather than using a global, but the DHCP option - * hook still uses ip_current_netif(). - * - * Safe because lwIP runs in single-threaded context (NO_SYS=1) and - * DHCP packets are processed sequentially in the network timer context. - */ -struct netif *nanos_dhcp_current_netif; - -/* Per-interface tracking of whether Option 121 was received */ -static u8_t option121_received[DHCP_OPTION121_MAX_NETIFS]; - -/* Spinlock for thread-safe access to option121_received state */ -static struct spinlock option121_lock; - -/* Get a safe index for the option121_received array, returns -1 if out of bounds */ -static inline int get_netif_idx(struct netif *netif) -{ - if (netif == NULL) - return -1; - u8_t idx = netif_get_index(netif); - if (idx == 0 || idx > DHCP_OPTION121_MAX_NETIFS) - return -1; - return idx - 1; -} - -/* Calculate significant octets for a prefix length: ceil(prefix_len / 8) */ -static inline u8_t prefix_to_octets(u8_t prefix_len) -{ - return (prefix_len + 7) / 8; -} - -void dhcp_option121_init(void) -{ - spin_lock_init(&option121_lock); - runtime_memset(option121_received, 0, sizeof(option121_received)); -} - -int dhcp_parse_option121(struct netif *netif, struct pbuf *p, - u16_t offset, u8_t len) -{ - int routes_parsed = 0; - u16_t pos = offset; - u16_t end; - int netif_idx; - - if (netif == NULL || p == NULL) - return ERR_ARG; /* ERR_ARG is already negative */ - - netif_idx = get_netif_idx(netif); - if (netif_idx < 0) - return ERR_ARG; - - /* overflow check */ - if (offset > 0xFFFF - len) - return ERR_ARG; - end = offset + len; - - /* empty option is valid but contains no routes */ - if (len == 0) - return 0; - - /* - * Clear any existing DHCP routes for this interface first. - * This ensures we replace old routes with new ones on lease renewal. - */ - ip4_route_remove_dhcp(netif); - - /* - * Parse each route entry in the option. - * Format per RFC 3442: - * 1 byte: prefix length (0-32) - * N bytes: significant octets of destination (N = ceil(prefix_len/8)) - * 4 bytes: gateway IP address - */ - while (pos < end) { - u8_t prefix_len; - u8_t significant_octets; - ip4_addr_t dest; - ip4_addr_t gateway; - u8_t dest_bytes[4] = {0, 0, 0, 0}; - - /* read prefix length */ - if (pbuf_copy_partial(p, &prefix_len, 1, pos) != 1) - break; - pos++; - - if (prefix_len > 32) - break; - - significant_octets = prefix_to_octets(prefix_len); - - /* check we have enough data remaining */ - if (pos + significant_octets + 4 > end) - break; - - /* read destination network (significant octets only) */ - if (significant_octets > 0) { - if (pbuf_copy_partial(p, dest_bytes, significant_octets, pos) - != significant_octets) - break; - } - pos += significant_octets; - - /* - * Reconstruct destination address. - * The significant octets are the high-order bytes of the address. - * dest_bytes is already zero-initialized for non-significant octets. - */ - dest.addr = (dest_bytes[0] << 24) | (dest_bytes[1] << 16) | - (dest_bytes[2] << 8) | dest_bytes[3]; - dest.addr = lwip_htonl(dest.addr); - - /* apply mask to ensure destination is properly masked (RFC 3442) */ - dest.addr &= ip4_prefix_to_mask(prefix_len); - - /* read gateway address - already in network byte order */ - if (pbuf_copy_partial(p, &gateway.addr, 4, pos) != 4) - break; - pos += 4; - - /* - * Validate gateway address: - * - Reject multicast/broadcast (invalid as next-hop) - * - 0.0.0.0 is valid per RFC 3442: means destination is on-link - * (directly reachable on this interface without a gateway) - */ - if (ip4_addr_ismulticast(&gateway) || - ip4_addr_isbroadcast(&gateway, netif)) - continue; - - /* add route to table */ - if (ip4_route_add(&dest, prefix_len, &gateway, netif, - IP4_ROUTE_FLAG_DHCP) == ERR_OK) { - routes_parsed++; - } - } - - /* - * Mark that this interface received Option 121. - * This is used to determine whether to ignore Option 3 (Router). - */ - if (routes_parsed > 0) { - spin_lock(&option121_lock); - option121_received[netif_idx] = 1; - spin_unlock(&option121_lock); - } - - return routes_parsed; -} - -boolean dhcp_option121_received(struct netif *netif) -{ - boolean result = false; - int netif_idx = get_netif_idx(netif); - - if (netif_idx < 0) - return false; - - spin_lock(&option121_lock); - result = (option121_received[netif_idx] != 0); - spin_unlock(&option121_lock); - - return result; -} - -void dhcp_option121_clear(struct netif *netif) -{ - int netif_idx = get_netif_idx(netif); - - if (netif_idx < 0) - return; - - spin_lock(&option121_lock); - option121_received[netif_idx] = 0; - spin_unlock(&option121_lock); - - ip4_route_remove_dhcp(netif); -} - -void nanos_dhcp_parse_option_hook(struct netif *netif, struct dhcp *dhcp, - u8_t state, struct dhcp_msg *msg, - u8_t msg_type, u8_t option, u8_t len, - struct pbuf *p, u16_t offset) -{ - LWIP_UNUSED_ARG(dhcp); - LWIP_UNUSED_ARG(state); - LWIP_UNUSED_ARG(msg); - - /* only process Option 121 from DHCP ACK messages */ - if (option == DHCP_OPTION_CLASSLESS_STATIC_ROUTE && - msg_type == DHCP_ACK) { - dhcp_parse_option121(netif, p, offset, len); - } -} diff --git a/src/net/dhcp_option121.h b/src/net/dhcp_option121.h deleted file mode 100644 index 53430c1b0..000000000 --- a/src/net/dhcp_option121.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * DHCP Option 121 (Classless Static Routes) Parser - RFC 3442 - * - * Parses DHCP Option 121 responses and populates the IPv4 static route - * table with classless static routes provided by the DHCP server. - */ - -#ifndef __DHCP_OPTION121_H__ -#define __DHCP_OPTION121_H__ - -#include -#include -#include - -/* DHCP Option 121 - Classless Static Route (RFC 3442) */ -#define DHCP_OPTION_CLASSLESS_STATIC_ROUTE 121 - -/* - * Maximum number of network interfaces we track for Option 121 state. - * Should match or exceed the number of interfaces in the system. - */ -#ifndef DHCP_OPTION121_MAX_NETIFS -#define DHCP_OPTION121_MAX_NETIFS 16 -#endif - -/* Initialize the Option 121 module - must be called before other functions */ -void dhcp_option121_init(void); - -/* - * Parse DHCP Option 121 data and add routes to the static route table. - * - * Parses variable-length encoding per RFC 3442: - * 1 byte: prefix length (0-32) - * N bytes: significant octets of destination (N = ceil(prefix_len/8)) - * 4 bytes: gateway IP address - * - * Routes are added with IP4_ROUTE_FLAG_DHCP flag. Existing DHCP routes - * for the interface are cleared first. - * - * Returns number of routes successfully added (>= 0), or negative error. - */ -int dhcp_parse_option121(struct netif *netif, struct pbuf *p, - u16_t offset, u8_t len); - -/* - * Check if Option 121 was received for an interface. - * Per RFC 3442, if Option 121 is present, Option 3 (Router) must be ignored. - */ -boolean dhcp_option121_received(struct netif *netif); - -/* - * Clear Option 121 state and routes for a network interface. - * Called when DHCP lease is released, interface is removed, etc. - */ -void dhcp_option121_clear(struct netif *netif); - -/* - * lwIP DHCP option parse hook (LWIP_HOOK_DHCP_PARSE_OPTION). - * Called by lwIP for each unknown DHCP option, parses Option 121. - */ -struct dhcp; -struct dhcp_msg; - -void nanos_dhcp_parse_option_hook(struct netif *netif, struct dhcp *dhcp, - u8_t state, struct dhcp_msg *msg, - u8_t msg_type, u8_t option, u8_t len, - struct pbuf *p, u16_t offset); - -#endif /* __DHCP_OPTION121_H__ */ diff --git a/src/net/ip4_route_table.c b/src/net/ip4_route_table.c deleted file mode 100644 index 18db89690..000000000 --- a/src/net/ip4_route_table.c +++ /dev/null @@ -1,261 +0,0 @@ -/* - * IPv4 static route table implementation - * - * Provides a fixed-size routing table with longest-prefix-match lookup, - * primarily for DHCP Option 121 (RFC 3442) classless static routes. - */ - -#include -#include -#include - -/* Route table storage - sorted by prefix_len descending for LPM */ -static struct ip4_route_entry route_table[LWIP_IPV4_NUM_ROUTE_ENTRIES]; - -/* Spinlock for thread-safe access */ -static struct spinlock route_lock; - -/* Number of active entries in the table */ -static int route_entry_count; - -/* Compute netmask from prefix length in network byte order */ -u32_t ip4_prefix_to_mask(u8_t prefix_len) -{ - if (prefix_len == 0) - return 0; - if (prefix_len >= 32) - return 0xFFFFFFFF; - return PP_HTONL(~((1UL << (32 - prefix_len)) - 1)); -} - -/* Check if a destination address matches a route entry */ -static inline int route_matches(const struct ip4_route_entry *entry, - const ip4_addr_t *dest) -{ - u32_t mask = ip4_prefix_to_mask(entry->prefix_len); - return (dest->addr & mask) == (entry->dest.addr & mask); -} - -/* Find insertion point to maintain sorted order (descending prefix_len) */ -static int find_insert_position(u8_t prefix_len) -{ - int i; - for (i = 0; i < route_entry_count; i++) { - if (prefix_len > route_table[i].prefix_len) - return i; - } - return route_entry_count; -} - -/* Find an existing route matching dest/prefix_len/netif, returns index or -1 */ -static int find_existing_route(const ip4_addr_t *dest, u8_t prefix_len, - struct netif *netif) -{ - u32_t mask = ip4_prefix_to_mask(prefix_len); - u32_t masked_dest = dest->addr & mask; - - for (int i = 0; i < route_entry_count; i++) { - if (route_table[i].prefix_len == prefix_len && - (route_table[i].dest.addr & mask) == masked_dest && - (netif == NULL || route_table[i].netif == netif)) { - return i; - } - } - return -1; -} - -void ip4_route_table_init(void) -{ - spin_lock_init(&route_lock); - runtime_memset((u8 *)route_table, 0, sizeof(route_table)); - route_entry_count = 0; -} - -err_t ip4_route_add(const ip4_addr_t *dest, u8_t prefix_len, - const ip4_addr_t *gateway, struct netif *netif, u8_t flags) -{ - err_t ret = ERR_OK; - - if (dest == NULL || gateway == NULL || netif == NULL) - return ERR_ARG; - if (prefix_len > IP4_MAX_PREFIX_LEN) - return ERR_ARG; - - spin_lock(&route_lock); - - /* check for existing route with same dest/prefix/netif and update it */ - int existing = find_existing_route(dest, prefix_len, netif); - if (existing >= 0) { - ip4_addr_copy(route_table[existing].gateway, *gateway); - route_table[existing].flags = flags; - goto out; - } - - if (route_entry_count >= LWIP_IPV4_NUM_ROUTE_ENTRIES) { - ret = ERR_MEM; - goto out; - } - - /* find insertion point to maintain sorted order */ - int pos = find_insert_position(prefix_len); - - /* shift entries down to make room */ - for (int i = route_entry_count; i > pos; i--) { - runtime_memcpy(&route_table[i], &route_table[i - 1], - sizeof(struct ip4_route_entry)); - } - - /* insert new entry */ - u32_t mask = ip4_prefix_to_mask(prefix_len); - route_table[pos].dest.addr = dest->addr & mask; - ip4_addr_copy(route_table[pos].gateway, *gateway); - route_table[pos].prefix_len = prefix_len; - route_table[pos].flags = flags; - route_table[pos].netif = netif; - route_entry_count++; - -out: - spin_unlock(&route_lock); - return ret; -} - -void ip4_route_remove(const ip4_addr_t *dest, u8_t prefix_len, struct netif *netif) -{ - if (dest == NULL || prefix_len > IP4_MAX_PREFIX_LEN) - return; - - spin_lock(&route_lock); - - int idx = find_existing_route(dest, prefix_len, netif); - if (idx >= 0) { - for (int i = idx; i < route_entry_count - 1; i++) { - runtime_memcpy(&route_table[i], &route_table[i + 1], - sizeof(struct ip4_route_entry)); - } - runtime_memset((u8 *)&route_table[route_entry_count - 1], 0, - sizeof(struct ip4_route_entry)); - route_entry_count--; - } - - spin_unlock(&route_lock); -} - -void ip4_route_remove_netif(struct netif *netif) -{ - if (netif == NULL) - return; - - spin_lock(&route_lock); - - int i = 0; - while (i < route_entry_count) { - if (route_table[i].netif == netif) { - for (int j = i; j < route_entry_count - 1; j++) { - runtime_memcpy(&route_table[j], &route_table[j + 1], - sizeof(struct ip4_route_entry)); - } - runtime_memset((u8 *)&route_table[route_entry_count - 1], 0, - sizeof(struct ip4_route_entry)); - route_entry_count--; - } else { - i++; - } - } - - spin_unlock(&route_lock); -} - -void ip4_route_remove_dhcp(struct netif *netif) -{ - if (netif == NULL) - return; - - spin_lock(&route_lock); - - int i = 0; - while (i < route_entry_count) { - if (route_table[i].netif == netif && - (route_table[i].flags & IP4_ROUTE_FLAG_DHCP)) { - for (int j = i; j < route_entry_count - 1; j++) { - runtime_memcpy(&route_table[j], &route_table[j + 1], - sizeof(struct ip4_route_entry)); - } - runtime_memset((u8 *)&route_table[route_entry_count - 1], 0, - sizeof(struct ip4_route_entry)); - route_entry_count--; - } else { - i++; - } - } - - spin_unlock(&route_lock); -} - -boolean ip4_route_find(const ip4_addr_t *dest, struct ip4_route_entry *out_entry) -{ - boolean found = false; - - if (dest == NULL) - return false; - - spin_lock(&route_lock); - - /* - * Table is sorted by prefix_len descending, so the first match - * is the longest prefix match. - */ - for (int i = 0; i < route_entry_count; i++) { - if (route_table[i].netif != NULL && route_matches(&route_table[i], dest)) { - if (out_entry != NULL) - runtime_memcpy(out_entry, &route_table[i], sizeof(*out_entry)); - found = true; - break; - } - } - - spin_unlock(&route_lock); - return found; -} - -struct netif *ip4_static_route(const ip4_addr_t *src, const ip4_addr_t *dest) -{ - struct ip4_route_entry entry; - LWIP_UNUSED_ARG(src); - - if (ip4_route_find(dest, &entry)) - return entry.netif; - return NULL; -} - -boolean ip4_get_gateway(const ip4_addr_t *dest, ip4_addr_t *out_gateway) -{ - struct ip4_route_entry entry; - - if (ip4_route_find(dest, &entry)) { - if (out_gateway != NULL) - ip4_addr_copy(*out_gateway, entry.gateway); - return true; - } - return false; -} - -/* - * Get read-only access to route table for debugging/netlink. - * Note: Caller must not hold route_lock. The returned pointer is valid - * but entries may change if routes are modified concurrently. - */ -const struct ip4_route_entry *ip4_get_route_table(int *count) -{ - if (count != NULL) - *count = route_entry_count; - return route_table; -} - -int ip4_route_count(void) -{ - int count; - spin_lock(&route_lock); - count = route_entry_count; - spin_unlock(&route_lock); - return count; -} diff --git a/src/net/ip4_route_table.h b/src/net/ip4_route_table.h deleted file mode 100644 index 6e05b21d6..000000000 --- a/src/net/ip4_route_table.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * IPv4 static route table for DHCP Option 121 (Classless Static Routes) - * - * Provides a static routing table for IPv4 with longest-prefix-match lookups. - * Primarily used to store routes learned from DHCP Option 121 (RFC 3442). - */ - -#ifndef __IP4_ROUTE_TABLE_H__ -#define __IP4_ROUTE_TABLE_H__ - -#include -#include -#include - -/* Maximum number of static routes (fixed-size to avoid dynamic allocation) */ -#ifndef LWIP_IPV4_NUM_ROUTE_ENTRIES -#define LWIP_IPV4_NUM_ROUTE_ENTRIES 16 -#endif - -#define IP4_MAX_PREFIX_LEN 32 - -/* Route entry flags */ -#define IP4_ROUTE_FLAG_NONE 0x00 -#define IP4_ROUTE_FLAG_DHCP 0x01 /* Route learned from DHCP Option 121 */ -#define IP4_ROUTE_FLAG_STATIC 0x02 /* Manually configured static route */ - -struct ip4_route_entry { - ip4_addr_t dest; /* Destination network address (masked) */ - ip4_addr_t gateway; /* Next-hop gateway IP address */ - u8_t prefix_len; /* CIDR prefix length (0-32) */ - u8_t flags; /* Route flags (IP4_ROUTE_FLAG_*) */ - struct netif *netif; /* Associated network interface (NULL = unused entry) */ -}; - -/* Compute netmask from prefix length in network byte order */ -u32_t ip4_prefix_to_mask(u8_t prefix_len); - -/* Initialize the route table - must be called before other functions */ -void ip4_route_table_init(void); - -/* - * Add a route to the static route table. - * Table is kept sorted by prefix length (longest first) for LPM lookups. - * Returns ERR_OK on success, ERR_MEM if table full, ERR_ARG if invalid args. - */ -err_t ip4_route_add(const ip4_addr_t *dest, u8_t prefix_len, - const ip4_addr_t *gateway, struct netif *netif, u8_t flags); - -/* Remove a specific route from the table */ -void ip4_route_remove(const ip4_addr_t *dest, u8_t prefix_len, struct netif *netif); - -/* Remove all routes associated with a network interface */ -void ip4_route_remove_netif(struct netif *netif); - -/* Remove all DHCP-learned routes for a network interface */ -void ip4_route_remove_dhcp(struct netif *netif); - -/* - * Find the best matching route for a destination address (longest prefix match). - * Copies route entry to out_entry if found. - * Returns true if route found, false otherwise. - */ -boolean ip4_route_find(const ip4_addr_t *dest, struct ip4_route_entry *out_entry); - -/* - * Route lookup hook for lwIP integration (LWIP_HOOK_IP4_ROUTE_SRC). - * Returns network interface to use, or NULL if no static route matches. - */ -struct netif *ip4_static_route(const ip4_addr_t *src, const ip4_addr_t *dest); - -/* - * Get the gateway address for a destination from the static route table. - * Copies gateway to out_gateway if found. - * Returns true if route found, false otherwise. - */ -boolean ip4_get_gateway(const ip4_addr_t *dest, ip4_addr_t *out_gateway); - -/* Get read-only access to route table for debugging/netlink reporting */ -const struct ip4_route_entry *ip4_get_route_table(int *count); - -/* Get the number of active routes in the table */ -int ip4_route_count(void); - -#endif /* __IP4_ROUTE_TABLE_H__ */ diff --git a/src/net/lwipopts.h b/src/net/lwipopts.h index e196cd21e..bdad84e18 100644 --- a/src/net/lwipopts.h +++ b/src/net/lwipopts.h @@ -199,36 +199,10 @@ static inline int net_ip_input_hook(struct pbuf *pbuf, struct netif *input_netif return 0; } -/* - * DHCP Option 121 (Classless Static Routes) support. - * - * The nanos lwIP modification passes ip_data as a parameter rather than - * using a global. However, dhcp_parse_reply() calls the option hook using - * ip_current_netif() which expects a global. We work around this by: - * 1. Defining a global that stores the current netif during DHCP recv - * 2. Providing ip_current_netif() macro to access it - * 3. Implementing the hook to use this netif - * - * The global is set by the UDP recv callback wrapper before calling dhcp_recv. - */ -extern struct netif *nanos_dhcp_current_netif; - -static inline struct netif *ip_current_netif(void) -{ - return nanos_dhcp_current_netif; -} - -struct dhcp; -struct dhcp_msg; -extern void nanos_dhcp_parse_option_hook(struct netif *netif, struct dhcp *dhcp, - u8_t state, struct dhcp_msg *msg, - u8_t msg_type, u8_t option, u8_t len, - struct pbuf *p, u16_t offset); -#define LWIP_HOOK_DHCP_PARSE_OPTION(netif, dhcp, state, msg, msg_type, option, len, pbuf, offset) \ - nanos_dhcp_parse_option_hook(netif, dhcp, state, msg, msg_type, option, len, pbuf, offset) +#define LWIP_DHCP_CLASSLESS_STATIC_ROUTES 1 /* - * Static route lookup hook for DHCP Option 121 routes. + * Static route lookup hook for DHCP Option 121 (Classless Static Routes). * Called by lwIP's ip4_route() to check static routes before the default route. */ struct ip4_addr; diff --git a/src/net/net.c b/src/net/net.c index 93432d1b3..41c00e83c 100644 --- a/src/net/net.c +++ b/src/net/net.c @@ -1,8 +1,6 @@ #include #include #include -#include -#include /* Network interface flags */ #define IFF_UP (1 << 0) @@ -415,8 +413,6 @@ void init_net(kernel_heaps kh) { lwip_heap = kh->malloc; list_init(&net_complete_list); - ip4_route_table_init(); - dhcp_option121_init(); lwip_init(); BSS_RO_AFTER_INIT NETIF_DECLARE_EXT_CALLBACK(netif_callback); netif_add_ext_callback(&netif_callback, lwip_ext_callback); From 8d128e087df04c8e7c80b14d075ef91f3b31a656 Mon Sep 17 00:00:00 2001 From: Timo Gatsonides Date: Fri, 20 Mar 2026 21:11:48 +0100 Subject: [PATCH 3/3] net: remove LWIP_HOOK_IP4_ROUTE_SRC, now wired directly in lwip --- src/net/lwipopts.h | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/net/lwipopts.h b/src/net/lwipopts.h index bdad84e18..e2596b5a2 100644 --- a/src/net/lwipopts.h +++ b/src/net/lwipopts.h @@ -200,11 +200,3 @@ static inline int net_ip_input_hook(struct pbuf *pbuf, struct netif *input_netif } #define LWIP_DHCP_CLASSLESS_STATIC_ROUTES 1 - -/* - * Static route lookup hook for DHCP Option 121 (Classless Static Routes). - * Called by lwIP's ip4_route() to check static routes before the default route. - */ -struct ip4_addr; -extern struct netif *ip4_static_route(const struct ip4_addr *src, const struct ip4_addr *dest); -#define LWIP_HOOK_IP4_ROUTE_SRC(src, dest) ip4_static_route(src, dest)