diff options
| author | Michael Brown | 2025-06-10 17:55:18 +0200 |
|---|---|---|
| committer | Michael Brown | 2025-06-10 19:22:32 +0200 |
| commit | b5fb7353fa3856cee7e0a6760c2341ca617d6ef4 (patch) | |
| tree | dac63a18911fd3e71ada3ac4f5600cd5bddd75d6 /src/net | |
| parent | [ipv4] Extend routing mechanism to handle non-default routes (diff) | |
| download | ipxe-b5fb7353fa3856cee7e0a6760c2341ca617d6ef4.tar.gz ipxe-b5fb7353fa3856cee7e0a6760c2341ca617d6ef4.tar.xz ipxe-b5fb7353fa3856cee7e0a6760c2341ca617d6ef4.zip | |
[ipv4] Add support for classless static routes
Add support for RFC 3442 classless static routes provided via DHCP
option 121.
Originally-implemented-by: Hazel Smith <hazel.smith@leicester.ac.uk>
Originally-implemented-by: Raphael Pour <raphael.pour@hetzner.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src/net')
| -rw-r--r-- | src/net/ipv4.c | 165 | ||||
| -rw-r--r-- | src/net/udp/dhcp.c | 2 |
2 files changed, 123 insertions, 44 deletions
diff --git a/src/net/ipv4.c b/src/net/ipv4.c index ec96e4242..03517840b 100644 --- a/src/net/ipv4.c +++ b/src/net/ipv4.c @@ -135,35 +135,131 @@ static int ipv4_add_miniroute ( struct net_device *netdev, } /** + * Add static route minirouting table entries + * + * @v netdev Network device + * @v address IPv4 address + * @v routes Static routes + * @v len Length of static routes + * @ret rc Return status code + */ +static int ipv4_add_static ( struct net_device *netdev, struct in_addr address, + const void *routes, size_t len ) { + const struct { + struct in_addr address; + } __attribute__ (( packed )) *encoded; + struct in_addr netmask; + struct in_addr network; + struct in_addr gateway; + unsigned int width; + unsigned int masklen; + size_t remaining; + const void *data; + int rc; + + /* Parse and add static routes */ + for ( data = routes, remaining = len ; remaining ; ) { + + /* Extract subnet mask width */ + width = *( ( uint8_t * ) data ); + data++; + remaining--; + masklen = ( ( width + 7 ) / 8 ); + + /* Check remaining length */ + if ( ( masklen + sizeof ( gateway ) ) > remaining ) { + DBGC ( netdev, "IPv4 invalid static route:\n" ); + DBGC_HDA ( netdev, 0, routes, len ); + return -EINVAL; + } + + /* Calculate subnet mask */ + if ( width ) { + netmask.s_addr = htonl ( -1UL << ( 32 - width ) ); + } else { + netmask.s_addr = 0; + } + + /* Extract network address */ + encoded = data; + network.s_addr = ( encoded->address.s_addr & netmask.s_addr ); + data += masklen; + remaining -= masklen; + + /* Extract gateway address */ + encoded = data; + gateway.s_addr = encoded->address.s_addr; + data += sizeof ( gateway ); + remaining -= sizeof ( gateway ); + + /* Add route */ + if ( ( rc = ipv4_add_miniroute ( netdev, address, network, + netmask, gateway ) ) != 0 ) + return rc; + } + + return 0; +} + +/** * Add IPv4 minirouting table entries * * @v netdev Network device * @v address IPv4 address - * @v netmask Subnet mask - * @v gateway Gateway address (if any) * @ret rc Return status code */ static int ipv4_add_miniroutes ( struct net_device *netdev, - struct in_addr address, - struct in_addr netmask, - struct in_addr gateway ) { + struct in_addr address ) { + struct settings *settings = netdev_settings ( netdev ); struct in_addr none = { 0 }; + struct in_addr netmask; + struct in_addr gateway; struct in_addr network; + void *routes; + int len; int rc; + /* Get subnet mask */ + fetch_ipv4_setting ( settings, &netmask_setting, &netmask ); + + /* Calculate default netmask, if necessary */ + if ( ! netmask.s_addr ) { + if ( IN_IS_CLASSA ( address.s_addr ) ) { + netmask.s_addr = INADDR_NET_CLASSA; + } else if ( IN_IS_CLASSB ( address.s_addr ) ) { + netmask.s_addr = INADDR_NET_CLASSB; + } else if ( IN_IS_CLASSC ( address.s_addr ) ) { + netmask.s_addr = INADDR_NET_CLASSC; + } + } + + /* Get default gateway, if present */ + fetch_ipv4_setting ( settings, &gateway_setting, &gateway ); + + /* Get static routes, if present */ + len = fetch_raw_setting_copy ( settings, &static_route_setting, + &routes ); + /* Add local address */ network.s_addr = ( address.s_addr & netmask.s_addr ); if ( ( rc = ipv4_add_miniroute ( netdev, address, network, netmask, none ) ) != 0 ) - return rc; - - /* Add default gateway, if applicable */ - if ( gateway.s_addr && - ( ( rc = ipv4_add_miniroute ( netdev, address, none, none, - gateway ) ) != 0 ) ) - return rc; + goto done; + + /* Add static routes or default gateway, as applicable */ + if ( len >= 0 ) { + if ( ( rc = ipv4_add_static ( netdev, address, routes, + len ) ) != 0 ) + goto done; + } else if ( gateway.s_addr ) { + if ( ( rc = ipv4_add_miniroute ( netdev, address, none, none, + gateway ) ) != 0 ) + goto done; + } - return 0; + done: + free ( routes ); + return rc; } /** @@ -871,19 +967,24 @@ const struct setting gateway_setting __setting ( SETTING_IP4, gateway ) = { .type = &setting_type_ipv4, }; +/** Classless static routes setting */ +const struct setting static_route_setting __setting ( SETTING_IP4, + static_routes ) = { + .name = "static-routes", + .description = "Static routes", + .tag = DHCP_STATIC_ROUTES, + .type = &setting_type_hex, +}; + /** * Send gratuitous ARP, if applicable * * @v netdev Network device * @v address IPv4 address - * @v netmask Subnet mask - * @v gateway Gateway address (if any) * @ret rc Return status code */ static int ipv4_gratuitous_arp ( struct net_device *netdev, - struct in_addr address, - struct in_addr netmask __unused, - struct in_addr gateway __unused ) { + struct in_addr address ) { int rc; /* Do nothing if network device already has this IPv4 address */ @@ -910,14 +1011,10 @@ static int ipv4_gratuitous_arp ( struct net_device *netdev, * @ret rc Return status code */ static int ipv4_settings ( int ( * apply ) ( struct net_device *netdev, - struct in_addr address, - struct in_addr netmask, - struct in_addr gateway ) ) { + struct in_addr address ) ) { struct net_device *netdev; struct settings *settings; - struct in_addr address = { 0 }; - struct in_addr netmask = { 0 }; - struct in_addr gateway = { 0 }; + struct in_addr address; int rc; /* Process settings for each network device */ @@ -927,30 +1024,12 @@ static int ipv4_settings ( int ( * apply ) ( struct net_device *netdev, settings = netdev_settings ( netdev ); /* Get IPv4 address */ - address.s_addr = 0; fetch_ipv4_setting ( settings, &ip_setting, &address ); if ( ! address.s_addr ) continue; - /* Get subnet mask */ - fetch_ipv4_setting ( settings, &netmask_setting, &netmask ); - - /* Calculate default netmask, if necessary */ - if ( ! netmask.s_addr ) { - if ( IN_IS_CLASSA ( address.s_addr ) ) { - netmask.s_addr = INADDR_NET_CLASSA; - } else if ( IN_IS_CLASSB ( address.s_addr ) ) { - netmask.s_addr = INADDR_NET_CLASSB; - } else if ( IN_IS_CLASSC ( address.s_addr ) ) { - netmask.s_addr = INADDR_NET_CLASSC; - } - } - - /* Get default gateway, if present */ - fetch_ipv4_setting ( settings, &gateway_setting, &gateway ); - /* Apply settings */ - if ( ( rc = apply ( netdev, address, netmask, gateway ) ) != 0 ) + if ( ( rc = apply ( netdev, address ) ) != 0 ) return rc; } diff --git a/src/net/udp/dhcp.c b/src/net/udp/dhcp.c index 8e2e97f10..daa37b96b 100644 --- a/src/net/udp/dhcp.c +++ b/src/net/udp/dhcp.c @@ -94,7 +94,7 @@ static uint8_t dhcp_request_options_data[] = { DHCP_ROOT_PATH, DHCP_MTU, DHCP_NTP_SERVERS, DHCP_VENDOR_ENCAP, DHCP_VENDOR_CLASS_ID, DHCP_TFTP_SERVER_NAME, DHCP_BOOTFILE_NAME, - DHCP_DOMAIN_SEARCH, + DHCP_DOMAIN_SEARCH, DHCP_STATIC_ROUTES, 128, 129, 130, 131, 132, 133, 134, 135, /* for PXE */ DHCP_EB_ENCAP, DHCP_ISCSI_INITIATOR_IQN ), DHCP_END |
