diff options
author | Michael Brown <mcb30@ipxe.org> | 2013-11-15 15:12:25 +0000 |
---|---|---|
committer | Michael Brown <mcb30@ipxe.org> | 2013-11-15 15:22:54 +0000 |
commit | 6b1eee04527969f21ab65245b67cf9efa4b4aea8 (patch) | |
tree | cc174ea4309f6decfdcd7d93891c038cf54dcc49 /src/net/ipv6.c | |
parent | 3f9a482b88d75081efb660e937e9631bbbf54c49 (diff) | |
download | ipxe-6b1eee04527969f21ab65245b67cf9efa4b4aea8.zip ipxe-6b1eee04527969f21ab65245b67cf9efa4b4aea8.tar.gz ipxe-6b1eee04527969f21ab65245b67cf9efa4b4aea8.tar.bz2 |
[ipv6] Separate the concepts of prefix and address creation
Allow for IPv6 routing table entries to be created for an on-link
prefix where a local address has not yet been assigned to the network
device.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src/net/ipv6.c')
-rw-r--r-- | src/net/ipv6.c | 204 |
1 files changed, 120 insertions, 84 deletions
diff --git a/src/net/ipv6.c b/src/net/ipv6.c index b9619a1..b4f33f0 100644 --- a/src/net/ipv6.c +++ b/src/net/ipv6.c @@ -68,6 +68,24 @@ static uint32_t ipv6col ( struct in6_addr *in ) { } /** + * Dump IPv6 routing table entry + * + * @v miniroute Routing table entry + */ +static inline __attribute__ (( always_inline )) void +ipv6_dump_miniroute ( struct ipv6_miniroute *miniroute ) { + struct net_device *netdev = miniroute->netdev; + + DBGC ( netdev, "IPv6 %s has %s %s/%d", netdev->name, + ( ( miniroute->flags & IPV6_HAS_ADDRESS ) ? + "address" : "prefix" ), + inet6_ntoa ( &miniroute->address ), miniroute->prefix_len ); + if ( miniroute->flags & IPV6_HAS_ROUTER ) + DBGC ( netdev, " router %s", inet6_ntoa ( &miniroute->router )); + DBGC ( netdev, "\n" ); +} + +/** * Check if network device has a specific IPv6 address * * @v netdev Network device @@ -79,6 +97,7 @@ int ipv6_has_addr ( struct net_device *netdev, struct in6_addr *addr ) { list_for_each_entry ( miniroute, &ipv6_miniroutes, list ) { if ( ( miniroute->netdev == netdev ) && + ( miniroute->flags & IPV6_HAS_ADDRESS ) && ( memcmp ( &miniroute->address, addr, sizeof ( miniroute->address ) ) == 0 ) ) { /* Found matching address */ @@ -109,31 +128,45 @@ static int ipv6_is_on_link ( struct ipv6_miniroute *miniroute, } /** - * Add IPv6 minirouting table entry + * Find IPv6 routing table entry for a given address * * @v netdev Network device * @v address IPv6 address + * @ret miniroute Routing table entry, or NULL if not found + */ +static struct ipv6_miniroute * ipv6_miniroute ( struct net_device *netdev, + struct in6_addr *address ) { + struct ipv6_miniroute *miniroute; + + list_for_each_entry ( miniroute, &ipv6_miniroutes, list ) { + if ( ( miniroute->netdev == netdev ) && + ipv6_is_on_link ( miniroute, address ) ) { + return miniroute; + } + } + return NULL; +} + +/** + * Add IPv6 routing table entry + * + * @v netdev Network device + * @v address IPv6 address (or prefix) * @v prefix_len Prefix length - * @v router Router address (or NULL) + * @v flags Flags * @ret miniroute Routing table entry, or NULL on failure */ -static struct ipv6_miniroute * __malloc -add_ipv6_miniroute ( struct net_device *netdev, struct in6_addr *address, - unsigned int prefix_len, struct in6_addr *router ) { +static struct ipv6_miniroute * ipv6_add_miniroute ( struct net_device *netdev, + struct in6_addr *address, + unsigned int prefix_len, + unsigned int flags ) { struct ipv6_miniroute *miniroute; uint8_t *prefix_mask; - DBGC ( netdev, "IPv6 add %s/%d ", inet6_ntoa ( address ), prefix_len ); - if ( router ) - DBGC ( netdev, "router %s ", inet6_ntoa ( router ) ); - DBGC ( netdev, "via %s\n", netdev->name ); - - /* Allocate and populate miniroute structure */ + /* Create routing table entry */ miniroute = zalloc ( sizeof ( *miniroute ) ); if ( ! miniroute ) return NULL; - - /* Record routing information */ miniroute->netdev = netdev_get ( netdev ); memcpy ( &miniroute->address, address, sizeof ( miniroute->address ) ); miniroute->prefix_len = prefix_len; @@ -144,41 +177,83 @@ add_ipv6_miniroute ( struct net_device *netdev, struct in6_addr *address, } 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; + + /* 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; + + /* Record router and add to start or end of list as appropriate */ + list_del ( &miniroute->list ); if ( router ) { - miniroute->has_router = 1; + 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 ) ); - } - - /* Add to end of list if we have a gateway, otherwise to start - * of list. - */ - if ( router ) { list_add_tail ( &miniroute->list, &ipv6_miniroutes ); } else { + changed = ( miniroute->flags & IPV6_HAS_ROUTER ); + miniroute->flags &= ~IPV6_HAS_ROUTER; list_add ( &miniroute->list, &ipv6_miniroutes ); } + if ( changed ) + ipv6_dump_miniroute ( miniroute ); - return miniroute; + return 0; } /** - * Delete IPv6 minirouting table entry + * Add IPv6 on-link address * - * @v miniroute Routing 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. */ -static void del_ipv6_miniroute ( struct ipv6_miniroute *miniroute ) { - struct net_device *netdev = miniroute->netdev; +int ipv6_set_address ( struct net_device *netdev, struct in6_addr *address ) { + struct ipv6_miniroute *miniroute; + int changed; + + /* Find routing table entry */ + miniroute = ipv6_miniroute ( netdev, address ); + if ( ! miniroute ) + return -EADDRNOTAVAIL; - DBGC ( netdev, "IPv6 del %s/%d ", inet6_ntoa ( &miniroute->address ), - miniroute->prefix_len ); - if ( miniroute->has_router ) - DBGC ( netdev, "router %s ", inet6_ntoa ( &miniroute->router )); - DBGC ( netdev, "via %s\n", netdev->name ); + /* 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 ); - netdev_put ( miniroute->netdev ); - list_del ( &miniroute->list ); - free ( miniroute ); + return 0; } /** @@ -200,6 +275,10 @@ static struct ipv6_miniroute * ipv6_route ( unsigned int scope_id, if ( ! netdev_is_open ( miniroute->netdev ) ) continue; + /* Skip routing table entries with no usable source address */ + if ( ! ( miniroute->flags & IPV6_HAS_ADDRESS ) ) + continue; + if ( IN6_IS_ADDR_LINKLOCAL ( *dest ) || IN6_IS_ADDR_MULTICAST ( *dest ) ) { @@ -221,7 +300,7 @@ static struct ipv6_miniroute * ipv6_route ( unsigned int scope_id, * address, and we have a default gateway, * then use this route. */ - if ( miniroute->has_router ) { + if ( miniroute->flags & IPV6_HAS_ROUTER ) { *dest = &miniroute->router; return miniroute; } @@ -920,53 +999,6 @@ struct setting_type setting_type_ipv6 __setting_type = { }; /** - * Perform IPv6 stateless address autoconfiguration (SLAAC) - * - * @v netdev Network device - * @v prefix Prefix - * @v prefix_len Prefix length - * @v router Router address (or NULL) - * @ret rc Return status code - */ -int ipv6_slaac ( struct net_device *netdev, struct in6_addr *prefix, - unsigned int prefix_len, struct in6_addr *router ) { - struct ipv6_miniroute *miniroute; - struct ipv6_miniroute *tmp; - struct in6_addr address; - int check_prefix_len; - int rc; - - /* Construct local address */ - memcpy ( &address, prefix, sizeof ( address ) ); - check_prefix_len = ipv6_eui64 ( &address, netdev ); - if ( check_prefix_len < 0 ) { - rc = check_prefix_len; - DBGC ( netdev, "IPv6 %s could not construct SLAAC address: " - "%s\n", netdev->name, strerror ( rc ) ); - return rc; - } - if ( check_prefix_len != ( int ) prefix_len ) { - DBGC ( netdev, "IPv6 %s incorrect SLAAC prefix length %d " - "(expected %d)\n", netdev->name, prefix_len, - check_prefix_len ); - return -EINVAL; - } - - /* Delete any existing SLAAC miniroutes for this prefix */ - list_for_each_entry_safe ( miniroute, tmp, &ipv6_miniroutes, list ) { - if ( ipv6_is_on_link ( miniroute, &address ) ) - del_ipv6_miniroute ( miniroute ); - } - - /* Add miniroute */ - miniroute = add_ipv6_miniroute ( netdev, &address, prefix_len, router ); - if ( ! miniroute ) - return -ENOMEM; - - return 0; -} - -/** * Create IPv6 network device * * @v netdev Network device @@ -989,7 +1021,8 @@ static int ipv6_probe ( struct net_device *netdev ) { } /* Create link-local address for this network device */ - miniroute = add_ipv6_miniroute ( netdev, &address, prefix_len, NULL ); + miniroute = ipv6_add_miniroute ( netdev, &address, prefix_len, + IPV6_HAS_ADDRESS ); if ( ! miniroute ) return -ENOMEM; @@ -1007,8 +1040,11 @@ static void ipv6_remove ( struct net_device *netdev ) { /* Delete all miniroutes for this network device */ list_for_each_entry_safe ( miniroute, tmp, &ipv6_miniroutes, list ) { - if ( miniroute->netdev == netdev ) - del_ipv6_miniroute ( miniroute ); + if ( miniroute->netdev == netdev ) { + netdev_put ( miniroute->netdev ); + list_del ( &miniroute->list ); + free ( miniroute ); + } } } |