mirror of
https://github.com/ipxe/ipxe
synced 2026-04-16 03:00:10 +03:00
[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>
This commit is contained in:
@@ -344,6 +344,9 @@ struct dhcp_client_uuid {
|
|||||||
/** DNS domain search list */
|
/** DNS domain search list */
|
||||||
#define DHCP_DOMAIN_SEARCH 119
|
#define DHCP_DOMAIN_SEARCH 119
|
||||||
|
|
||||||
|
/** Classless static routes */
|
||||||
|
#define DHCP_STATIC_ROUTES 121
|
||||||
|
|
||||||
/** Etherboot-specific encapsulated options
|
/** Etherboot-specific encapsulated options
|
||||||
*
|
*
|
||||||
* This encapsulated options field is used to contain all options
|
* This encapsulated options field is used to contain all options
|
||||||
|
|||||||
@@ -437,6 +437,8 @@ netmask_setting __setting ( SETTING_IP4, netmask );
|
|||||||
extern const struct setting
|
extern const struct setting
|
||||||
gateway_setting __setting ( SETTING_IP4, gateway );
|
gateway_setting __setting ( SETTING_IP4, gateway );
|
||||||
extern const struct setting
|
extern const struct setting
|
||||||
|
static_route_setting __setting ( SETTING_IP4, static_routes );
|
||||||
|
extern const struct setting
|
||||||
dns_setting __setting ( SETTING_IP4_EXTRA, dns );
|
dns_setting __setting ( SETTING_IP4_EXTRA, dns );
|
||||||
extern const struct setting
|
extern const struct setting
|
||||||
ip6_setting __setting ( SETTING_IP6, ip6 );
|
ip6_setting __setting ( SETTING_IP6, ip6 );
|
||||||
|
|||||||
+121
-42
@@ -134,36 +134,132 @@ static int ipv4_add_miniroute ( struct net_device *netdev,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
* Add IPv4 minirouting table entries
|
||||||
*
|
*
|
||||||
* @v netdev Network device
|
* @v netdev Network device
|
||||||
* @v address IPv4 address
|
* @v address IPv4 address
|
||||||
* @v netmask Subnet mask
|
|
||||||
* @v gateway Gateway address (if any)
|
|
||||||
* @ret rc Return status code
|
* @ret rc Return status code
|
||||||
*/
|
*/
|
||||||
static int ipv4_add_miniroutes ( struct net_device *netdev,
|
static int ipv4_add_miniroutes ( struct net_device *netdev,
|
||||||
struct in_addr address,
|
struct in_addr address ) {
|
||||||
struct in_addr netmask,
|
struct settings *settings = netdev_settings ( netdev );
|
||||||
struct in_addr gateway ) {
|
|
||||||
struct in_addr none = { 0 };
|
struct in_addr none = { 0 };
|
||||||
|
struct in_addr netmask;
|
||||||
|
struct in_addr gateway;
|
||||||
struct in_addr network;
|
struct in_addr network;
|
||||||
|
void *routes;
|
||||||
|
int len;
|
||||||
int rc;
|
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 */
|
/* Add local address */
|
||||||
network.s_addr = ( address.s_addr & netmask.s_addr );
|
network.s_addr = ( address.s_addr & netmask.s_addr );
|
||||||
if ( ( rc = ipv4_add_miniroute ( netdev, address, network, netmask,
|
if ( ( rc = ipv4_add_miniroute ( netdev, address, network, netmask,
|
||||||
none ) ) != 0 )
|
none ) ) != 0 )
|
||||||
return rc;
|
goto done;
|
||||||
|
|
||||||
/* Add default gateway, if applicable */
|
/* Add static routes or default gateway, as applicable */
|
||||||
if ( gateway.s_addr &&
|
if ( len >= 0 ) {
|
||||||
( ( rc = ipv4_add_miniroute ( netdev, address, none, none,
|
if ( ( rc = ipv4_add_static ( netdev, address, routes,
|
||||||
gateway ) ) != 0 ) )
|
len ) ) != 0 )
|
||||||
return rc;
|
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,
|
.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
|
* Send gratuitous ARP, if applicable
|
||||||
*
|
*
|
||||||
* @v netdev Network device
|
* @v netdev Network device
|
||||||
* @v address IPv4 address
|
* @v address IPv4 address
|
||||||
* @v netmask Subnet mask
|
|
||||||
* @v gateway Gateway address (if any)
|
|
||||||
* @ret rc Return status code
|
* @ret rc Return status code
|
||||||
*/
|
*/
|
||||||
static int ipv4_gratuitous_arp ( struct net_device *netdev,
|
static int ipv4_gratuitous_arp ( struct net_device *netdev,
|
||||||
struct in_addr address,
|
struct in_addr address ) {
|
||||||
struct in_addr netmask __unused,
|
|
||||||
struct in_addr gateway __unused ) {
|
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
/* Do nothing if network device already has this IPv4 address */
|
/* 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
|
* @ret rc Return status code
|
||||||
*/
|
*/
|
||||||
static int ipv4_settings ( int ( * apply ) ( struct net_device *netdev,
|
static int ipv4_settings ( int ( * apply ) ( struct net_device *netdev,
|
||||||
struct in_addr address,
|
struct in_addr address ) ) {
|
||||||
struct in_addr netmask,
|
|
||||||
struct in_addr gateway ) ) {
|
|
||||||
struct net_device *netdev;
|
struct net_device *netdev;
|
||||||
struct settings *settings;
|
struct settings *settings;
|
||||||
struct in_addr address = { 0 };
|
struct in_addr address;
|
||||||
struct in_addr netmask = { 0 };
|
|
||||||
struct in_addr gateway = { 0 };
|
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
/* Process settings for each network device */
|
/* Process settings for each network device */
|
||||||
@@ -927,30 +1024,12 @@ static int ipv4_settings ( int ( * apply ) ( struct net_device *netdev,
|
|||||||
settings = netdev_settings ( netdev );
|
settings = netdev_settings ( netdev );
|
||||||
|
|
||||||
/* Get IPv4 address */
|
/* Get IPv4 address */
|
||||||
address.s_addr = 0;
|
|
||||||
fetch_ipv4_setting ( settings, &ip_setting, &address );
|
fetch_ipv4_setting ( settings, &ip_setting, &address );
|
||||||
if ( ! address.s_addr )
|
if ( ! address.s_addr )
|
||||||
continue;
|
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 */
|
/* Apply settings */
|
||||||
if ( ( rc = apply ( netdev, address, netmask, gateway ) ) != 0 )
|
if ( ( rc = apply ( netdev, address ) ) != 0 )
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -94,7 +94,7 @@ static uint8_t dhcp_request_options_data[] = {
|
|||||||
DHCP_ROOT_PATH, DHCP_MTU, DHCP_NTP_SERVERS,
|
DHCP_ROOT_PATH, DHCP_MTU, DHCP_NTP_SERVERS,
|
||||||
DHCP_VENDOR_ENCAP, DHCP_VENDOR_CLASS_ID,
|
DHCP_VENDOR_ENCAP, DHCP_VENDOR_CLASS_ID,
|
||||||
DHCP_TFTP_SERVER_NAME, DHCP_BOOTFILE_NAME,
|
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 */
|
128, 129, 130, 131, 132, 133, 134, 135, /* for PXE */
|
||||||
DHCP_EB_ENCAP, DHCP_ISCSI_INITIATOR_IQN ),
|
DHCP_EB_ENCAP, DHCP_ISCSI_INITIATOR_IQN ),
|
||||||
DHCP_END
|
DHCP_END
|
||||||
|
|||||||
@@ -214,6 +214,17 @@ TESTNET ( net4,
|
|||||||
{ "ip", "192.168.86.1" },
|
{ "ip", "192.168.86.1" },
|
||||||
{ "netmask", "255.255.240.0" } );
|
{ "netmask", "255.255.240.0" } );
|
||||||
|
|
||||||
|
/** net5: Static routes */
|
||||||
|
TESTNET ( net5,
|
||||||
|
{ "ip", "10.42.0.1" },
|
||||||
|
{ "netmask", "255.255.0.0" },
|
||||||
|
{ "gateway", "10.42.0.254" /* should be ignored */ },
|
||||||
|
{ "static-routes",
|
||||||
|
"19:0a:2b:2b:80:0a:2a:2b:2b:" /* 10.43.43.128/25 via 10.42.43.43 */
|
||||||
|
"10:c0:a8:0a:2a:c0:a8:" /* 192.168.0.0/16 via 10.42.192.168 */
|
||||||
|
"18:c0:a8:00:00:00:00:00:" /* 192.168.0.0/24 on-link */
|
||||||
|
"00:0a:2a:01:01" /* default via 10.42.1.1 */ } );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform IPv4 self-tests
|
* Perform IPv4 self-tests
|
||||||
*
|
*
|
||||||
@@ -327,6 +338,24 @@ static void ipv4_test_exec ( void ) {
|
|||||||
testnet_remove_ok ( &net2 );
|
testnet_remove_ok ( &net2 );
|
||||||
testnet_remove_ok ( &net1 );
|
testnet_remove_ok ( &net1 );
|
||||||
testnet_remove_ok ( &net0 );
|
testnet_remove_ok ( &net0 );
|
||||||
|
|
||||||
|
/* Static routes */
|
||||||
|
testnet_ok ( &net5 );
|
||||||
|
ipv4_route_ok ( "10.42.99.0", NULL,
|
||||||
|
"10.42.99.0", &net5, "10.42.0.1", 0 );
|
||||||
|
ipv4_route_ok ( "8.8.8.8", NULL,
|
||||||
|
"10.42.1.1", &net5, "10.42.0.1", 0 );
|
||||||
|
ipv4_route_ok ( "10.43.43.1", NULL,
|
||||||
|
"10.42.1.1", &net5, "10.42.0.1", 0 );
|
||||||
|
ipv4_route_ok ( "10.43.43.129", NULL,
|
||||||
|
"10.42.43.43", &net5, "10.42.0.1", 0 );
|
||||||
|
ipv4_route_ok ( "192.168.54.8", NULL,
|
||||||
|
"10.42.192.168", &net5, "10.42.0.1", 0 );
|
||||||
|
ipv4_route_ok ( "192.168.0.8", NULL,
|
||||||
|
"192.168.0.8", &net5, "10.42.0.1", 0 );
|
||||||
|
ipv4_route_ok ( "192.168.0.255", NULL,
|
||||||
|
"192.168.0.255", &net5, "10.42.0.1", 1 );
|
||||||
|
testnet_remove_ok ( &net5 );
|
||||||
}
|
}
|
||||||
|
|
||||||
/** IPv4 self-test */
|
/** IPv4 self-test */
|
||||||
|
|||||||
Reference in New Issue
Block a user