[ipv6] Modify router solicits to provide metadata
authorMatthew Iselin <matthew@theiselins.net>
Sat, 16 Jul 2011 12:38:27 +0000 (22:38 +1000)
committerMarty Connor <mdc@etherboot.org>
Thu, 21 Jul 2011 10:52:42 +0000 (06:52 -0400)
This patch allows router solicitation to return extra data to the
requestor, if requested, which allows DHCPv6 to assign a fully
routable address for use later.

Signed-off-by: Matthew Iselin <matthew@theiselins.net>
Signed-off-by: Marty Connor <mdc@etherboot.org>
src/include/gpxe/ndp.h
src/net/ndp.c
src/net/udp/dhcp6.c
src/usr/ip6mgmt.c

index b8a00d7..c189940 100644 (file)
@@ -35,6 +35,13 @@ struct job_interface;
 #define NDP_OPTION_REDIRECT         4
 #define NDP_OPTION_MTU              5
 
+struct rsolicit_info {
+       struct in6_addr router;
+       struct in6_addr prefix;
+       int prefix_length;
+       int no_address; /* No address assignment takes place via this adv. */
+};
+
 struct neighbour_solicit {
        uint8_t type;
        uint8_t code;
@@ -100,7 +107,9 @@ struct prefix_option
 int ndp_resolve ( struct net_device *netdev, struct in6_addr *src,
                  struct in6_addr *dest, void *dest_ll_addr );
 
-int ndp_send_rsolicit ( struct net_device *netdev, struct job_interface *job );
+int ndp_send_rsolicit ( struct net_device *netdev,
+                       struct job_interface *job,
+                       struct rsolicit_info *meta );
 
 int ndp_process_radvert ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src,
                          struct sockaddr_tcpip *st_dest, struct net_device *netdev,
index dbe943c..6ef2c2c 100644 (file)
@@ -44,6 +44,8 @@ struct pending_rsolicit {
        struct job_interface job;
        /** Reference counter */
        struct refcnt refcnt;
+       /** Metadata to fill when we receive an advertisement. */
+       struct rsolicit_info *meta;
 };
 
 /** Number of entries in the neighbour cache table */
@@ -165,6 +167,7 @@ add_solicit_entry ( struct net_device *netdev, int state ) {
        entry->netdev = netdev;
        entry->state = state;
        entry->code = RSOLICIT_CODE_NONE;
+       entry->meta = NULL;
        
        return entry;
 }
@@ -228,12 +231,16 @@ int ndp_resolve ( struct net_device *netdev, struct in6_addr *dest,
  *
  * @v netdev   Network device
  * @v src      Source address
+ * @v meta     (optional) Pointer to struct to fill with information
+ *             when an advertisement arrives.
  * @v dest     Destination address
  *
  * This function prepares a neighbour solicitation packet and sends it to the
  * network layer.
  */
-int ndp_send_rsolicit ( struct net_device *netdev, struct job_interface *job ) {
+int ndp_send_rsolicit ( struct net_device *netdev,
+                       struct job_interface *job,
+                       struct rsolicit_info *meta ) {
        union {
                struct sockaddr_in6 sin6;
                struct sockaddr_tcpip st;
@@ -264,6 +271,7 @@ int ndp_send_rsolicit ( struct net_device *netdev, struct job_interface *job ) {
        
        /* Add an entry for this solicitation. */
        entry = add_solicit_entry ( netdev, RSOLICIT_STATE_ALMOST );
+       entry->meta = meta;
        
        /* Set up a job for the solicit. */
        job_init ( &entry->job, &rsolicit_job_operations, &entry->refcnt );
@@ -301,10 +309,11 @@ int ndp_process_radvert ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src
        struct router_advert *radvert = iobuf->data;
        struct ndp_option *options = iobuf->data + sizeof(struct router_advert);
        struct in6_addr router_addr = ( ( struct sockaddr_in6 * ) st_src )->sin6_addr;
-       struct in6_addr host_addr;
+       struct in6_addr host_addr, prefix;
        int rc = -ENOENT;
        uint8_t prefix_len = 0;
        size_t offset = sizeof ( struct router_advert ), ll_size;
+       int can_autoconf = 0; /* Can we autoconfigure from the prefix? */
        
        /* Verify that there's a pending solicit */
        struct pending_rsolicit *pending = solicit_find_entry ( netdev );
@@ -346,7 +355,8 @@ int ndp_process_radvert ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src
                        }
 
                        /* Copy the prefix first and then add the EUI-64 */
-                       memcpy( &host_addr.s6_addr, opt->prefix, prefix_len / 8 );
+                       memcpy ( &prefix.s6_addr, opt->prefix, prefix_len / 8 );
+                       memcpy ( &host_addr.s6_addr, &prefix.s6_addr, prefix_len / 8 );
 
                        /* Create an IPv6 address for this station based on the prefix. */
                        ll_size = netdev->ll_protocol->ll_addr_len;
@@ -355,6 +365,13 @@ int ndp_process_radvert ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src
                        } else {
                                ipv6_generate_eui64 ( host_addr.s6_addr + 8, netdev->ll_addr );
                        }
+                       
+                       /* Get flags. */
+                       can_autoconf = opt->flags_rsvd & ( 1 << 6 );
+                       if ( ! can_autoconf )
+                               DBG ( "ndp: got a prefix, but can't use it for SLAAC\n" );
+                       else
+                               DBG ( "ndp: can use prefix for SLAAC\n" );
 
                        rc = 0;
                        }
@@ -385,17 +402,26 @@ int ndp_process_radvert ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src
                
                return 0;
        }
+       
+       /* Fill in information if we need to. */
+       if ( pending->meta != NULL ) {
+               DBG ( "ndp: filling meta information\n" );
+               pending->meta->router = router_addr;
+               pending->meta->prefix = prefix;
+               pending->meta->prefix_length = prefix_len;
+               pending->meta->no_address = ! can_autoconf;
+       }
 
        /* Configure a route based on this router if none exists. */
-       if ( net_protocol->check ( netdev, &host_addr ) ) {
+       if ( can_autoconf && net_protocol->check ( netdev, &host_addr ) ) {
                DBG ( "ndp: autoconfigured %s/%d via a router advertisement\n", inet6_ntoa( host_addr ), prefix_len);
 
-               add_ipv6_address ( netdev, host_addr, prefix_len, host_addr, router_addr );
-               
-               job_done ( &pending->job, pending->code );
-               
-               pending->state = RSOLICIT_STATE_INVALID;
+               add_ipv6_address ( netdev, prefix, prefix_len, host_addr, router_addr );
        }
+       
+       /* Completed without error. */
+       job_done ( &pending->job, pending->code );
+       pending->state = RSOLICIT_STATE_INVALID;
 
        return 0;
 }
index aed6dab..3ad2546 100644 (file)
@@ -34,11 +34,13 @@ FILE_LICENCE ( GPL2_OR_LATER );
 #include <gpxe/xfer.h>
 #include <gpxe/open.h>
 #include <gpxe/job.h>
+#include <gpxe/monojob.h>
 #include <gpxe/netdevice.h>
 #include <gpxe/features.h>
 #include <gpxe/retry.h>
 #include <gpxe/timer.h>
 #include <gpxe/settings.h>
+#include <gpxe/ndp.h>
 #include <gpxe/dhcp6.h>
 
 /* Get an option encapsulated inside another option. */
@@ -100,6 +102,9 @@ struct dhcp6_session {
        
        /** Settings to apply as a result of a DHCPv6 session. */
        struct settings *settings;
+       
+       /** Information about the router to use for address assignment. */
+       struct rsolicit_info router;
 };
 
 static struct dhcp6_session_state dhcp6_solicit;
@@ -353,20 +358,30 @@ int dhcp6_handle_option ( struct dhcp6_session *dhcp,
                        DBG ( "dhcp6: assigned address is %s\n", inet6_ntoa ( addr->addr ) );
                        
                        if ( completed ) {
-                               /* Store the completed IPv6 address. */
-                               store_setting ( parent,
-                                               &ip6_setting,
-                                               &addr->addr,
-                                               sizeof ( struct in6_addr ) );
-                               
-                               /* Add a fully-routable version now. */
-                               /* TODO: set address properly. */
-                               DBG ( "dhcp6: fixme, need to assign address properly\n" );
-                               add_ipv6_address ( dhcp->netdev,
-                                                  addr->addr,
-                                                  128,
-                                                  addr->addr,
-                                                  addr->addr );
+                               if ( dhcp->router.no_address ) {
+                                       /* Store the completed IPv6 address. */
+                                       store_setting ( parent,
+                                                       &ip6_setting,
+                                                       &addr->addr,
+                                                       sizeof ( struct in6_addr ) );
+                                       store_setting ( parent,
+                                                       &gateway6_setting,
+                                                       &dhcp->router,
+                                                       sizeof ( struct in6_addr ) );
+                                       store_setting ( parent,
+                                                       &prefix_setting,
+                                                       &dhcp->router.prefix_length,
+                                                       sizeof ( dhcp->router.prefix_length ) );
+                                       
+                                       /* Add a fully-routable version now. */
+                                       add_ipv6_address ( dhcp->netdev,
+                                                          dhcp->router.prefix,
+                                                          dhcp->router.prefix_length,
+                                                          addr->addr,
+                                                          dhcp->router.router );
+                               } else {
+                                       DBG ( "dhcp6: not adding an address as SLAAC has done that\n" );
+                               }
                        } else {
                                dhcp->offer = addr->addr;
                        }
@@ -391,6 +406,8 @@ int dhcp6_handle_option ( struct dhcp6_session *dhcp,
                        break;
                case DHCP6_OPT_DNS_DOMAINS:
                        DBG ( "dhcp6: DNS search domains option\n" );
+                       
+                       /* TODO: set DNS search domain, needs parsing though. */
                        break;
                case DHCP6_OPT_SERVERID:
                        /* Verify the DUID if we already store one. */
@@ -404,8 +421,7 @@ int dhcp6_handle_option ( struct dhcp6_session *dhcp,
                                        DBG ( "dhcp6: server DUID is valid\n" );
                                }
                        } else {
-                                       /* Grab in the server DUID for this session. */
-                               /* TODO: check for sane length */
+                               /* Grab in the server DUID for this session. */
                                dhcp->server_duid = malloc ( datalen );
                                dhcp->server_duid_len = datalen;
                                memcpy ( dhcp->server_duid, iobuf->data, datalen );
@@ -745,6 +761,15 @@ int start_dhcp6 ( struct job_interface *job, struct net_device *netdev, int only
        dhcp = zalloc ( sizeof ( *dhcp ) );
        if ( ! dhcp )
                return -ENOMEM;
+       
+       
+       /* Get information about routers on this network first. */
+       rc = ndp_send_rsolicit ( netdev, &monojob, &dhcp->router );
+       if ( rc != 0 )
+               DBG ( "dhcp6: can't find a router on the network, continuing\n" );
+       else
+               monojob_wait ( "" );
+       
        ref_init ( &dhcp->refcnt, dhcp6_free );
        job_init ( &dhcp->job, &dhcp6_job_operations, &dhcp->refcnt );
        xfer_init ( &dhcp->xfer, &dhcp6_xfer_operations, &dhcp->refcnt );
index afcbe39..21d30d6 100644 (file)
@@ -76,7 +76,7 @@ int ip6_autoconf ( struct net_device *netdev ) {
        add_ipv6_address ( netdev, ip6addr, 64, ip6addr, ip6zero );
        
        /* Solicit routers on the network. */
-       if ( ( rc = ndp_send_rsolicit ( netdev, &monojob ) ) == 0 ) {
+       if ( ( rc = ndp_send_rsolicit ( netdev, &monojob, NULL ) ) == 0 ) {
                rc = monojob_wait ( "" );
        }