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);