summaryrefslogtreecommitdiffstats
path: root/src/net/ipv4.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/net/ipv4.c')
-rw-r--r--src/net/ipv4.c274
1 files changed, 205 insertions, 69 deletions
diff --git a/src/net/ipv4.c b/src/net/ipv4.c
index b91fa2ad0..f3dd44384 100644
--- a/src/net/ipv4.c
+++ b/src/net/ipv4.c
@@ -49,6 +49,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/* Unique IP datagram identification number (high byte) */
static uint8_t next_ident_high = 0;
@@ -77,17 +78,34 @@ static struct profiler ipv4_rx_profiler __profiler = { .name = "ipv4.rx" };
*
* @v netdev Network device
* @v address IPv4 address
+ * @v network Subnet address
* @v netmask Subnet mask
* @v gateway Gateway address (if any)
* @ret rc Return status code
*/
-static int add_ipv4_miniroute ( struct net_device *netdev,
- struct in_addr address, struct in_addr netmask,
+static int ipv4_add_miniroute ( struct net_device *netdev,
+ struct in_addr address,
+ struct in_addr network,
+ struct in_addr netmask,
struct in_addr gateway ) {
struct ipv4_miniroute *miniroute;
+ struct ipv4_miniroute *before;
+ struct in_addr hostmask;
+ struct in_addr broadcast;
+ /* Calculate host mask */
+ if ( gateway.s_addr || IN_IS_SMALL ( netmask.s_addr ) ) {
+ hostmask.s_addr = INADDR_NONE;
+ } else {
+ hostmask.s_addr = ~netmask.s_addr;
+ }
+ broadcast.s_addr = ( network.s_addr | hostmask.s_addr );
+
+ /* Print debugging information */
DBGC ( netdev, "IPv4 add %s", inet_ntoa ( address ) );
+ DBGC ( netdev, " for %s", inet_ntoa ( network ) );
DBGC ( netdev, "/%s ", inet_ntoa ( netmask ) );
+ DBGC ( netdev, "bc %s ", inet_ntoa ( broadcast ) );
if ( gateway.s_addr )
DBGC ( netdev, "gw %s ", inet_ntoa ( gateway ) );
DBGC ( netdev, "via %s\n", netdev->name );
@@ -102,30 +120,159 @@ static int add_ipv4_miniroute ( struct net_device *netdev,
/* Record routing information */
miniroute->netdev = netdev_get ( netdev );
miniroute->address = address;
+ miniroute->network = network;
miniroute->netmask = netmask;
+ miniroute->hostmask = hostmask;
miniroute->gateway = gateway;
-
- /* Add to end of list if we have a gateway, otherwise
- * to start of list.
- */
- if ( gateway.s_addr ) {
- list_add_tail ( &miniroute->list, &ipv4_miniroutes );
- } else {
- list_add ( &miniroute->list, &ipv4_miniroutes );
+
+ /* Add to routing table ahead of any less specific routes */
+ list_for_each_entry ( before, &ipv4_miniroutes, list ) {
+ if ( netmask.s_addr & ~before->netmask.s_addr )
+ break;
+ }
+ list_add_tail ( &miniroute->list, &before->list );
+
+ 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
+ *
+ * @v netdev Network device
+ * @v address IPv4 address
+ * @ret rc Return status code
+ */
+static int ipv4_add_miniroutes ( struct net_device *netdev,
+ 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 )
+ 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;
+ }
+
+ done:
+ free ( routes );
+ return rc;
+}
+
+/**
* Delete IPv4 minirouting table entry
*
* @v miniroute Routing table entry
*/
-static void del_ipv4_miniroute ( struct ipv4_miniroute *miniroute ) {
+static void ipv4_del_miniroute ( struct ipv4_miniroute *miniroute ) {
struct net_device *netdev = miniroute->netdev;
DBGC ( netdev, "IPv4 del %s", inet_ntoa ( miniroute->address ) );
+ DBGC ( netdev, " for %s", inet_ntoa ( miniroute->network ) );
DBGC ( netdev, "/%s ", inet_ntoa ( miniroute->netmask ) );
if ( miniroute->gateway.s_addr )
DBGC ( netdev, "gw %s ", inet_ntoa ( miniroute->gateway ) );
@@ -137,6 +284,19 @@ static void del_ipv4_miniroute ( struct ipv4_miniroute *miniroute ) {
}
/**
+ * Delete IPv4 minirouting table entries
+ *
+ */
+static void ipv4_del_miniroutes ( void ) {
+ struct ipv4_miniroute *miniroute;
+ struct ipv4_miniroute *tmp;
+
+ /* Delete all existing routes */
+ list_for_each_entry_safe ( miniroute, tmp, &ipv4_miniroutes, list )
+ ipv4_del_miniroute ( miniroute );
+}
+
+/**
* Perform IPv4 routing
*
* @v scope_id Destination address scope ID
@@ -147,8 +307,8 @@ static void del_ipv4_miniroute ( struct ipv4_miniroute *miniroute ) {
* If the route requires use of a gateway, the next hop destination
* address will be overwritten with the gateway address.
*/
-static struct ipv4_miniroute * ipv4_route ( unsigned int scope_id,
- struct in_addr *dest ) {
+struct ipv4_miniroute * ipv4_route ( unsigned int scope_id,
+ struct in_addr *dest ) {
struct ipv4_miniroute *miniroute;
/* Find first usable route in routing table */
@@ -160,27 +320,23 @@ static struct ipv4_miniroute * ipv4_route ( unsigned int scope_id,
if ( IN_IS_MULTICAST ( dest->s_addr ) ) {
- /* If destination is non-global, and the scope ID
- * matches this network device, then use this route.
+ /* If destination is non-global, and the scope
+ * ID matches this network device, then use
+ * the first matching route.
*/
if ( miniroute->netdev->scope_id == scope_id )
return miniroute;
} else {
- /* If destination is an on-link global
- * address, then use this route.
- */
- if ( ( ( dest->s_addr ^ miniroute->address.s_addr )
- & miniroute->netmask.s_addr ) == 0 )
- return miniroute;
-
- /* If destination is an off-link global
- * address, and we have a default gateway,
- * then use this route.
+ /* If destination is global, then use the
+ * first matching route (via its gateway if
+ * specified).
*/
- if ( miniroute->gateway.s_addr ) {
- *dest = miniroute->gateway;
+ if ( ( ( dest->s_addr ^ miniroute->network.s_addr )
+ & miniroute->netmask.s_addr ) == 0 ) {
+ if ( miniroute->gateway.s_addr )
+ *dest = miniroute->gateway;
return miniroute;
}
}
@@ -310,7 +466,7 @@ static int ipv4_tx ( struct io_buffer *iobuf,
struct sockaddr_in *sin_dest = ( ( struct sockaddr_in * ) st_dest );
struct ipv4_miniroute *miniroute;
struct in_addr next_hop;
- struct in_addr netmask = { .s_addr = 0 };
+ struct in_addr hostmask = { .s_addr = INADDR_NONE };
uint8_t ll_dest_buf[MAX_LL_ADDR_LEN];
const void *ll_dest;
int rc;
@@ -338,7 +494,7 @@ static int ipv4_tx ( struct io_buffer *iobuf,
( ( miniroute = ipv4_route ( sin_dest->sin_scope_id,
&next_hop ) ) != NULL ) ) {
iphdr->src = miniroute->address;
- netmask = miniroute->netmask;
+ hostmask = miniroute->hostmask;
netdev = miniroute->netdev;
}
if ( ! netdev ) {
@@ -373,7 +529,7 @@ static int ipv4_tx ( struct io_buffer *iobuf,
ntohs ( iphdr->chksum ) );
/* Calculate link-layer destination address, if possible */
- if ( ( ( next_hop.s_addr ^ INADDR_BROADCAST ) & ~netmask.s_addr ) == 0){
+ if ( ( ( ~next_hop.s_addr ) & hostmask.s_addr ) == 0 ) {
/* Broadcast address */
ipv4_stats.out_bcast_pkts++;
ll_dest = netdev->ll_broadcast;
@@ -408,7 +564,7 @@ static int ipv4_tx ( struct io_buffer *iobuf,
}
} else {
if ( ( rc = arp_tx ( iobuf, netdev, &ipv4_protocol, &next_hop,
- &iphdr->src, netdev->ll_addr ) ) != 0 ) {
+ &iphdr->src ) ) != 0 ) {
DBGC ( sin_dest->sin_addr, "IPv4 could not transmit "
"packet via %s: %s\n",
netdev->name, strerror ( rc ) );
@@ -812,19 +968,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 */
@@ -851,14 +1012,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 */
@@ -868,30 +1025,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;
}
@@ -903,20 +1042,17 @@ static int ipv4_settings ( int ( * apply ) ( struct net_device *netdev,
*
* @ret rc Return status code
*/
-static int ipv4_create_routes ( void ) {
- struct ipv4_miniroute *miniroute;
- struct ipv4_miniroute *tmp;
+static int ipv4_apply_routes ( void ) {
int rc;
/* Send gratuitous ARPs for any new IPv4 addresses */
ipv4_settings ( ipv4_gratuitous_arp );
/* Delete all existing routes */
- list_for_each_entry_safe ( miniroute, tmp, &ipv4_miniroutes, list )
- del_ipv4_miniroute ( miniroute );
+ ipv4_del_miniroutes();
- /* Create a route for each configured network device */
- if ( ( rc = ipv4_settings ( add_ipv4_miniroute ) ) != 0 )
+ /* Create routes for each configured network device */
+ if ( ( rc = ipv4_settings ( ipv4_add_miniroutes ) ) != 0 )
return rc;
return 0;
@@ -924,7 +1060,7 @@ static int ipv4_create_routes ( void ) {
/** IPv4 settings applicator */
struct settings_applicator ipv4_settings_applicator __settings_applicator = {
- .apply = ipv4_create_routes,
+ .apply = ipv4_apply_routes,
};
/* Drag in objects via ipv4_protocol */