diff options
Diffstat (limited to 'src/net/ipv4.c')
| -rw-r--r-- | src/net/ipv4.c | 274 |
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 */ |
