mirror of https://github.com/ipxe/ipxe.git
[ipv6] Create routing table based on IPv6 settings
Use the IPv6 settings to construct the routing table, in a matter analogous to the construction of the IPv4 routing table. This allows for manual assignment of IPv6 addresses via e.g. set net0/ip6 2001:ba8:0:1d4::6950:5845 set net0/len6 64 set net0/gateway6 fe80::226:bff:fedd:d3c0 The prefix length ("len6") may be omitted, in which case a default prefix length of 64 will be assumed. Multiple IPv6 addresses may be assigned manually by implicitly creating child settings blocks. For example: set net0/ip6 2001:ba8:0:1d4::6950:5845 set net0.ula/ip6 fda4:2496:e992::6950:5845 Signed-off-by: Michael Brown <mcb30@ipxe.org>pull/56/head
parent
4ad3c73b30
commit
c34d1518eb
|
@ -25,6 +25,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
|||
/** IPv6 maximum hop limit */
|
||||
#define IPV6_HOP_LIMIT 0xff
|
||||
|
||||
/** IPv6 default prefix length */
|
||||
#define IPV6_DEFAULT_PREFIX_LEN 64
|
||||
|
||||
/** IPv6 maximum prefix length */
|
||||
#define IPV6_MAX_PREFIX_LEN 128
|
||||
|
||||
/** IPv6 header */
|
||||
struct ipv6_header {
|
||||
/** Version (4 bits), Traffic class (8 bits), Flow label (20 bits) */
|
||||
|
@ -258,10 +264,6 @@ extern struct list_head ipv6_miniroutes;
|
|||
extern struct net_protocol ipv6_protocol __net_protocol;
|
||||
|
||||
extern int ipv6_has_addr ( struct net_device *netdev, struct in6_addr *addr );
|
||||
extern int ipv6_set_prefix ( struct net_device *netdev, struct in6_addr *prefix,
|
||||
unsigned int prefix_len, struct in6_addr *router );
|
||||
extern int ipv6_set_address ( struct net_device *netdev,
|
||||
struct in6_addr *address );
|
||||
extern int parse_ipv6_setting ( const struct setting_type *type,
|
||||
const char *value, void *buf, size_t len );
|
||||
extern int format_ipv6_setting ( const struct setting_type *type,
|
||||
|
|
255
src/net/ipv6.c
255
src/net/ipv6.c
|
@ -164,107 +164,85 @@ static struct ipv6_miniroute * ipv6_miniroute ( struct net_device *netdev,
|
|||
* @v netdev Network device
|
||||
* @v address IPv6 address (or prefix)
|
||||
* @v prefix_len Prefix length
|
||||
* @v flags Flags
|
||||
* @ret miniroute Routing table entry, or NULL on failure
|
||||
* @v router Router address (if any)
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static struct ipv6_miniroute * ipv6_add_miniroute ( struct net_device *netdev,
|
||||
struct in6_addr *address,
|
||||
unsigned int prefix_len,
|
||||
unsigned int flags ) {
|
||||
static int ipv6_add_miniroute ( struct net_device *netdev,
|
||||
struct in6_addr *address,
|
||||
unsigned int prefix_len,
|
||||
struct in6_addr *router ) {
|
||||
struct ipv6_miniroute *miniroute;
|
||||
uint8_t *prefix_mask;
|
||||
|
||||
/* Create routing table entry */
|
||||
miniroute = zalloc ( sizeof ( *miniroute ) );
|
||||
if ( ! miniroute )
|
||||
return NULL;
|
||||
miniroute->netdev = netdev_get ( netdev );
|
||||
memcpy ( &miniroute->address, address, sizeof ( miniroute->address ) );
|
||||
miniroute->prefix_len = prefix_len;
|
||||
assert ( prefix_len <= ( 8 * sizeof ( miniroute->prefix_mask ) ) );
|
||||
for ( prefix_mask = miniroute->prefix_mask.s6_addr ; prefix_len >= 8 ;
|
||||
prefix_mask++, prefix_len -= 8 ) {
|
||||
*prefix_mask = 0xff;
|
||||
}
|
||||
if ( prefix_len )
|
||||
*prefix_mask <<= ( 8 - prefix_len );
|
||||
miniroute->flags = flags;
|
||||
list_add ( &miniroute->list, &ipv6_miniroutes );
|
||||
ipv6_dump_miniroute ( miniroute );
|
||||
|
||||
return miniroute;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define IPv6 on-link prefix
|
||||
*
|
||||
* @v netdev Network device
|
||||
* @v prefix IPv6 address prefix
|
||||
* @v prefix_len Prefix length
|
||||
* @v router Router address (or NULL)
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
int ipv6_set_prefix ( struct net_device *netdev, struct in6_addr *prefix,
|
||||
unsigned int prefix_len, struct in6_addr *router ) {
|
||||
struct ipv6_miniroute *miniroute;
|
||||
int changed;
|
||||
unsigned int remaining;
|
||||
unsigned int i;
|
||||
|
||||
/* Find or create routing table entry */
|
||||
miniroute = ipv6_miniroute ( netdev, prefix );
|
||||
if ( ! miniroute )
|
||||
miniroute = ipv6_add_miniroute ( netdev, prefix, prefix_len, 0);
|
||||
if ( ! miniroute )
|
||||
return -ENOMEM;
|
||||
miniroute = ipv6_miniroute ( netdev, address );
|
||||
if ( ! miniroute ) {
|
||||
|
||||
/* Record router and add to start or end of list as appropriate */
|
||||
list_del ( &miniroute->list );
|
||||
if ( router ) {
|
||||
changed = ( ( ! ( miniroute->flags & IPV6_HAS_ROUTER ) ) ||
|
||||
( memcmp ( &miniroute->router, router,
|
||||
sizeof ( miniroute->router ) ) != 0 ) );
|
||||
miniroute->flags |= IPV6_HAS_ROUTER;
|
||||
memcpy ( &miniroute->router, router,
|
||||
sizeof ( miniroute->router ) );
|
||||
list_add_tail ( &miniroute->list, &ipv6_miniroutes );
|
||||
} else {
|
||||
changed = ( miniroute->flags & IPV6_HAS_ROUTER );
|
||||
miniroute->flags &= ~IPV6_HAS_ROUTER;
|
||||
/* Create new routing table entry */
|
||||
miniroute = zalloc ( sizeof ( *miniroute ) );
|
||||
if ( ! miniroute )
|
||||
return -ENOMEM;
|
||||
miniroute->netdev = netdev_get ( netdev );
|
||||
memcpy ( &miniroute->address, address,
|
||||
sizeof ( miniroute->address ) );
|
||||
|
||||
/* Default to prefix length of 64 if none specified */
|
||||
if ( ! prefix_len )
|
||||
prefix_len = IPV6_DEFAULT_PREFIX_LEN;
|
||||
miniroute->prefix_len = prefix_len;
|
||||
assert ( prefix_len <= IPV6_MAX_PREFIX_LEN );
|
||||
|
||||
/* Construct prefix mask */
|
||||
remaining = prefix_len;
|
||||
for ( prefix_mask = miniroute->prefix_mask.s6_addr ;
|
||||
remaining >= 8 ; prefix_mask++, remaining -= 8 ) {
|
||||
*prefix_mask = 0xff;
|
||||
}
|
||||
if ( remaining )
|
||||
*prefix_mask <<= ( 8 - remaining );
|
||||
|
||||
/* Add to list of routes */
|
||||
list_add ( &miniroute->list, &ipv6_miniroutes );
|
||||
}
|
||||
if ( changed )
|
||||
ipv6_dump_miniroute ( miniroute );
|
||||
|
||||
/* Set or update address, if applicable */
|
||||
for ( i = 0 ; i < ( sizeof ( address->s6_addr32 ) /
|
||||
sizeof ( address->s6_addr32[0] ) ) ; i++ ) {
|
||||
if ( ( address->s6_addr32[i] &
|
||||
~miniroute->prefix_mask.s6_addr32[i] ) != 0 ) {
|
||||
memcpy ( &miniroute->address, address,
|
||||
sizeof ( miniroute->address ) );
|
||||
miniroute->flags |= IPV6_HAS_ADDRESS;
|
||||
}
|
||||
}
|
||||
if ( miniroute->prefix_len == IPV6_MAX_PREFIX_LEN )
|
||||
miniroute->flags |= IPV6_HAS_ADDRESS;
|
||||
|
||||
/* Set or update router, if applicable */
|
||||
if ( router ) {
|
||||
memcpy ( &miniroute->router, router,
|
||||
sizeof ( miniroute->router ) );
|
||||
miniroute->flags |= IPV6_HAS_ROUTER;
|
||||
list_del ( &miniroute->list );
|
||||
list_add_tail ( &miniroute->list, &ipv6_miniroutes );
|
||||
}
|
||||
|
||||
ipv6_dump_miniroute ( miniroute );
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add IPv6 on-link address
|
||||
* Delete IPv6 minirouting table entry
|
||||
*
|
||||
* @v netdev Network device
|
||||
* @v address IPv6 address
|
||||
* @ret rc Return status code
|
||||
*
|
||||
* An on-link prefix for the address must already exist.
|
||||
* @v miniroute Routing table entry
|
||||
*/
|
||||
int ipv6_set_address ( struct net_device *netdev, struct in6_addr *address ) {
|
||||
struct ipv6_miniroute *miniroute;
|
||||
int changed;
|
||||
static void ipv6_del_miniroute ( struct ipv6_miniroute *miniroute ) {
|
||||
|
||||
/* Find routing table entry */
|
||||
miniroute = ipv6_miniroute ( netdev, address );
|
||||
if ( ! miniroute )
|
||||
return -EADDRNOTAVAIL;
|
||||
|
||||
/* Record address */
|
||||
changed = ( ( ! ( miniroute->flags & IPV6_HAS_ADDRESS ) ) ||
|
||||
( memcmp ( &miniroute->address, address,
|
||||
sizeof ( miniroute->address ) ) != 0 ) );
|
||||
memcpy ( &miniroute->address, address, sizeof ( miniroute->address ) );
|
||||
miniroute->flags |= IPV6_HAS_ADDRESS;
|
||||
if ( changed )
|
||||
ipv6_dump_miniroute ( miniroute );
|
||||
|
||||
return 0;
|
||||
netdev_put ( miniroute->netdev );
|
||||
list_del ( &miniroute->list );
|
||||
free ( miniroute );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1198,65 +1176,98 @@ static int ipv6_register_settings ( struct net_device *netdev ) {
|
|||
return rc;
|
||||
}
|
||||
|
||||
/** IPv6 network device driver */
|
||||
struct net_driver ipv6_driver __net_driver = {
|
||||
.name = "IPv6",
|
||||
.probe = ipv6_register_settings,
|
||||
};
|
||||
|
||||
/**
|
||||
* Create IPv6 network device
|
||||
* Create IPv6 routing table based on configured settings
|
||||
*
|
||||
* @v netdev Network device
|
||||
* @v settings Settings block
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int ipv6_probe ( struct net_device *netdev ) {
|
||||
struct ipv6_miniroute *miniroute;
|
||||
struct in6_addr address;
|
||||
int prefix_len;
|
||||
static int ipv6_create_routes ( struct net_device *netdev,
|
||||
struct settings *settings ) {
|
||||
struct settings *child;
|
||||
struct settings *origin;
|
||||
struct in6_addr ip6_buf;
|
||||
struct in6_addr gateway6_buf;
|
||||
struct in6_addr *ip6 = &ip6_buf;
|
||||
struct in6_addr *gateway6 = &gateway6_buf;
|
||||
uint8_t len6;
|
||||
size_t len;
|
||||
int rc;
|
||||
|
||||
/* Construct link-local address from EUI-64 as per RFC 2464 */
|
||||
memset ( &address, 0, sizeof ( address ) );
|
||||
prefix_len = ipv6_link_local ( &address, netdev );
|
||||
if ( prefix_len < 0 ) {
|
||||
rc = prefix_len;
|
||||
DBGC ( netdev, "IPv6 %s could not construct link-local "
|
||||
"address: %s\n", netdev->name, strerror ( rc ) );
|
||||
/* First, create routing table for any child settings. We do
|
||||
* this depth-first and in reverse order so that the end
|
||||
* result reflects the relative priorities of the settings
|
||||
* blocks.
|
||||
*/
|
||||
list_for_each_entry_reverse ( child, &settings->children, siblings )
|
||||
ipv6_create_routes ( netdev, child );
|
||||
|
||||
/* Fetch IPv6 address, if any */
|
||||
len = fetch_setting ( settings, &ip6_setting, &origin, NULL,
|
||||
ip6, sizeof ( *ip6 ) );
|
||||
if ( ( len != sizeof ( *ip6 ) ) || ( origin != settings ) )
|
||||
return 0;
|
||||
|
||||
/* Fetch prefix length, if defined */
|
||||
len = fetch_setting ( settings, &len6_setting, &origin, NULL,
|
||||
&len6, sizeof ( len6 ) );
|
||||
if ( ( len != sizeof ( len6 ) ) || ( origin != settings ) )
|
||||
len6 = 0;
|
||||
if ( len6 > IPV6_MAX_PREFIX_LEN )
|
||||
len6 = IPV6_MAX_PREFIX_LEN;
|
||||
|
||||
/* Fetch gateway, if defined */
|
||||
len = fetch_setting ( settings, &gateway6_setting, &origin, NULL,
|
||||
gateway6, sizeof ( *gateway6 ) );
|
||||
if ( ( len != sizeof ( *gateway6 ) ) || ( origin != settings ) )
|
||||
gateway6 = NULL;
|
||||
|
||||
/* Create or update route */
|
||||
if ( ( rc = ipv6_add_miniroute ( netdev, ip6, len6, gateway6 ) ) != 0){
|
||||
DBGC ( netdev, "IPv6 %s could not add route: %s\n",
|
||||
netdev->name, strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Create link-local address for this network device */
|
||||
miniroute = ipv6_add_miniroute ( netdev, &address, prefix_len,
|
||||
IPV6_HAS_ADDRESS );
|
||||
if ( ! miniroute )
|
||||
return -ENOMEM;
|
||||
|
||||
/* Register link-local address settings */
|
||||
if ( ( rc = ipv6_register_settings ( netdev ) ) != 0 )
|
||||
return rc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy IPv6 network device
|
||||
* Create IPv6 routing table based on configured settings
|
||||
*
|
||||
* @v netdev Network device
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static void ipv6_remove ( struct net_device *netdev ) {
|
||||
static int ipv6_create_all_routes ( void ) {
|
||||
struct ipv6_miniroute *miniroute;
|
||||
struct ipv6_miniroute *tmp;
|
||||
struct net_device *netdev;
|
||||
struct settings *settings;
|
||||
int rc;
|
||||
|
||||
/* Delete all miniroutes for this network device */
|
||||
list_for_each_entry_safe ( miniroute, tmp, &ipv6_miniroutes, list ) {
|
||||
if ( miniroute->netdev == netdev ) {
|
||||
netdev_put ( miniroute->netdev );
|
||||
list_del ( &miniroute->list );
|
||||
free ( miniroute );
|
||||
}
|
||||
/* Delete all existing routes */
|
||||
list_for_each_entry_safe ( miniroute, tmp, &ipv6_miniroutes, list )
|
||||
ipv6_del_miniroute ( miniroute );
|
||||
|
||||
/* Create routes for each configured network device */
|
||||
for_each_netdev ( netdev ) {
|
||||
settings = netdev_settings ( netdev );
|
||||
if ( ( rc = ipv6_create_routes ( netdev, settings ) ) != 0 )
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** IPv6 network device driver */
|
||||
struct net_driver ipv6_driver __net_driver = {
|
||||
.name = "IPv6",
|
||||
.probe = ipv6_probe,
|
||||
.remove = ipv6_remove,
|
||||
/** IPv6 settings applicator */
|
||||
struct settings_applicator ipv6_settings_applicator __settings_applicator = {
|
||||
.apply = ipv6_create_all_routes,
|
||||
};
|
||||
|
||||
/* Drag in objects via ipv6_protocol */
|
||||
|
|
|
@ -342,11 +342,6 @@ ndp_rx_router_advertisement_prefix ( struct net_device *netdev,
|
|||
union ndp_option *option, size_t len ) {
|
||||
struct ndp_router_advertisement_header *radv = &ndp->radv;
|
||||
struct ndp_prefix_information_option *prefix_opt = &option->prefix;
|
||||
struct in6_addr *router = &sin6_src->sin6_addr;
|
||||
struct in6_addr address;
|
||||
struct ipv6conf *ipv6conf;
|
||||
int prefix_len;
|
||||
int rc;
|
||||
|
||||
/* Sanity check */
|
||||
if ( sizeof ( *prefix_opt ) > len ) {
|
||||
|
@ -355,59 +350,13 @@ ndp_rx_router_advertisement_prefix ( struct net_device *netdev,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Identify IPv6 configurator, if any */
|
||||
ipv6conf = ipv6conf_demux ( netdev );
|
||||
DBGC ( netdev, "NDP %s found %sdefault router %s ",
|
||||
netdev->name, ( radv->lifetime ? "" : "non-" ),
|
||||
inet6_ntoa ( &sin6_src->sin6_addr ) );
|
||||
DBGC ( netdev, "for %s-link %sautonomous prefix %s/%d%s\n",
|
||||
DBGC ( netdev, "for %s-link %sautonomous prefix %s/%d\n",
|
||||
( ( prefix_opt->flags & NDP_PREFIX_ON_LINK ) ? "on" : "off" ),
|
||||
( ( prefix_opt->flags & NDP_PREFIX_AUTONOMOUS ) ? "" : "non-" ),
|
||||
inet6_ntoa ( &prefix_opt->prefix ),
|
||||
prefix_opt->prefix_len, ( ipv6conf ? "" : " (ignored)" ) );
|
||||
|
||||
/* Do nothing unless IPv6 autoconfiguration is in progress */
|
||||
if ( ! ipv6conf )
|
||||
return 0;
|
||||
|
||||
/* Ignore off-link prefixes */
|
||||
if ( ! ( prefix_opt->flags & NDP_PREFIX_ON_LINK ) )
|
||||
return 0;
|
||||
|
||||
/* Define prefix */
|
||||
if ( ( rc = ipv6_set_prefix ( netdev, &prefix_opt->prefix,
|
||||
prefix_opt->prefix_len,
|
||||
( radv->lifetime ?
|
||||
router : NULL ) ) ) != 0 ) {
|
||||
DBGC ( netdev, "NDP %s could not define prefix %s/%d: %s\n",
|
||||
netdev->name, inet6_ntoa ( &prefix_opt->prefix ),
|
||||
prefix_opt->prefix_len, strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Perform stateless address autoconfiguration, if applicable */
|
||||
if ( prefix_opt->flags & NDP_PREFIX_AUTONOMOUS ) {
|
||||
memcpy ( &address, &prefix_opt->prefix, sizeof ( address ) );
|
||||
prefix_len = ipv6_eui64 ( &address, netdev );
|
||||
if ( prefix_len < 0 ) {
|
||||
rc = prefix_len;
|
||||
DBGC ( netdev, "NDP %s could not construct SLAAC "
|
||||
"address: %s\n", netdev->name, strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
if ( prefix_len != prefix_opt->prefix_len ) {
|
||||
DBGC ( netdev, "NDP %s incorrect SLAAC prefix length "
|
||||
"%d (expected %d)\n", netdev->name,
|
||||
prefix_opt->prefix_len, prefix_len );
|
||||
return -EINVAL;
|
||||
}
|
||||
if ( ( rc = ipv6_set_address ( netdev, &address ) ) != 0 ) {
|
||||
DBGC ( netdev, "NDP %s could not set address %s: %s\n",
|
||||
netdev->name, inet6_ntoa ( &address ),
|
||||
strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
inet6_ntoa ( &prefix_opt->prefix ), prefix_opt->prefix_len );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -462,8 +462,6 @@ enum dhcpv6_session_state_flags {
|
|||
DHCPV6_RX_RECORD_SERVER_ID = 0x04,
|
||||
/** Record received IPv6 address */
|
||||
DHCPV6_RX_RECORD_IAADDR = 0x08,
|
||||
/** Apply received IPv6 address */
|
||||
DHCPV6_RX_APPLY_IAADDR = 0x10,
|
||||
};
|
||||
|
||||
/** DHCPv6 request state */
|
||||
|
@ -471,7 +469,7 @@ static struct dhcpv6_session_state dhcpv6_request = {
|
|||
.tx_type = DHCPV6_REQUEST,
|
||||
.rx_type = DHCPV6_REPLY,
|
||||
.flags = ( DHCPV6_TX_IA_NA | DHCPV6_TX_IAADDR |
|
||||
DHCPV6_RX_RECORD_IAADDR | DHCPV6_RX_APPLY_IAADDR ),
|
||||
DHCPV6_RX_RECORD_IAADDR ),
|
||||
.next = NULL,
|
||||
};
|
||||
|
||||
|
@ -870,19 +868,6 @@ static int dhcpv6_rx ( struct dhcpv6_session *dhcpv6,
|
|||
dhcpv6->server_duid_len );
|
||||
}
|
||||
|
||||
/* Apply identity association address, if applicable */
|
||||
if ( dhcpv6->state->flags & DHCPV6_RX_APPLY_IAADDR ) {
|
||||
if ( ( rc = ipv6_set_address ( dhcpv6->netdev,
|
||||
&dhcpv6->lease ) ) != 0 ) {
|
||||
DBGC ( dhcpv6, "DHCPv6 %s could not apply %s: %s\n",
|
||||
dhcpv6->netdev->name,
|
||||
inet6_ntoa ( &dhcpv6->lease ), strerror ( rc ) );
|
||||
/* This is plausibly the error we want to return */
|
||||
dhcpv6->rc = rc;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
/* Transition to next state, if applicable */
|
||||
if ( dhcpv6->state->next ) {
|
||||
dhcpv6_set_state ( dhcpv6, dhcpv6->state->next );
|
||||
|
|
Loading…
Reference in New Issue