]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[ipv6] Create routing table based on IPv6 settings
authorMichael Brown <mcb30@ipxe.org>
Tue, 19 Jul 2016 16:49:50 +0000 (17:49 +0100)
committerMichael Brown <mcb30@ipxe.org>
Wed, 20 Jul 2016 12:02:44 +0000 (13:02 +0100)
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>
src/include/ipxe/ipv6.h
src/net/ipv6.c
src/net/ndp.c
src/net/udp/dhcpv6.c

index 4dd03f0583c60ca1e7835c3bb530476c8cc0cccc..0e5292fba183c77b5aef1d6957fe193ae0e78b06 100644 (file)
@@ -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,
index 04ba3d8bb5fe971f1791e86584caad4be1947a7c..d2a173120aac8df703bdd242083714a951c3da87 100644 (file)
@@ -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;
+       unsigned int remaining;
+       unsigned int i;
 
-       /* 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;
-}
+       /* Find or create routing table entry */
+       miniroute = ipv6_miniroute ( netdev, address );
+       if ( ! miniroute ) {
+
+               /* 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 );
 
-/**
- * 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;
+               /* Add to list of routes */
+               list_add ( &miniroute->list, &ipv6_miniroutes );
+       }
 
-       /* 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;
+       /* 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;
 
-       /* Record router and add to start or end of list as appropriate */
-       list_del ( &miniroute->list );
+       /* Set or update router, if applicable */
        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 ) );
+               miniroute->flags |= IPV6_HAS_ROUTER;
+               list_del ( &miniroute->list );
                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 );
 
+       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;
-
-       /* Find routing table entry */
-       miniroute = ipv6_miniroute ( netdev, address );
-       if ( ! miniroute )
-               return -EADDRNOTAVAIL;
+static void ipv6_del_miniroute ( struct ipv6_miniroute *miniroute ) {
 
-       /* 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 */
index a35a1219e12bec923e018be116b4a02ac5ccea55..21bbd99954f8e2fd741aba83fd606c8caecdbf0c 100644 (file)
@@ -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;
 }
index a2c69aaae46673637a925cb3d7a95f08ab59dfbf..253032e4e98c209764dcbcf826a82f38fb90a27f 100644 (file)
@@ -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 );