[ipv6] Make router solicits block until complete
authorMatthew Iselin <matthew@theiselins.net>
Sat, 25 Jun 2011 02:45:11 +0000 (12:45 +1000)
committerMarty Connor <mdc@etherboot.org>
Thu, 21 Jul 2011 02:19:23 +0000 (22:19 -0400)
Also return a status code to the caller

Note: does not yet time out if no router advertisements are received.

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

index 23b7b56..6040258 100644 (file)
@@ -65,8 +65,6 @@ int icmp6_rx ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src,
 
 int icmp6_send_solicit ( struct net_device *netdev, struct in6_addr *src, struct in6_addr *dest );
 
-int icmp6_send_rsolicit ( struct net_device *netdev );
-
 int icmp6_send_advert ( struct net_device *netdev, struct in6_addr *src, struct in6_addr *dest );
 
 #endif /* _GPXE_ICMP6_H */
index be387ce..b8a00d7 100644 (file)
@@ -11,6 +11,7 @@
 #include <gpxe/tcpip.h>
 
 struct icmp6_net_protocol;
+struct job_interface;
 
 #define NDP_STATE_INVALID 0
 #define NDP_STATE_INCOMPLETE 1
@@ -19,6 +20,15 @@ struct icmp6_net_protocol;
 #define NDP_STATE_PROBE 4
 #define NDP_STATE_STALE 5
 
+#define RSOLICIT_STATE_INVALID 0
+#define RSOLICIT_STATE_PENDING 1
+#define RSOLICIT_STATE_COMPLETE        2
+#define RSOLICIT_STATE_ALMOST  3
+
+#define RSOLICIT_CODE_NONE     0
+#define RSOLICIT_CODE_MANAGED  1
+#define RSOLICIT_CODE_OTHERCONF        2
+
 #define NDP_OPTION_SOURCE_LL        1
 #define NDP_OPTION_TARGET_LL        2
 #define NDP_OPTION_PREFIX_INFO      3
@@ -90,6 +100,8 @@ 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_process_radvert ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src,
                          struct sockaddr_tcpip *st_dest, struct net_device *netdev,
                          struct icmp6_net_protocol *net_protocol );
index f24b98b..ac4e308 100644 (file)
@@ -67,48 +67,6 @@ int icmp6_send_solicit ( struct net_device *netdev, struct in6_addr *src __unuse
                         netdev, &nsolicit->csum );
 }
 
-/**
- * Send router solicitation packet
- *
- * @v netdev   Network device
- * @v src      Source address
- * @v dest     Destination address
- *
- * This function prepares a neighbour solicitation packet and sends it to the
- * network layer.
- */
-int icmp6_send_rsolicit ( struct net_device *netdev ) {
-       union {
-               struct sockaddr_in6 sin6;
-               struct sockaddr_tcpip st;
-       } st_dest;
-       struct router_solicit *solicit;
-       struct io_buffer *iobuf = alloc_iob ( sizeof ( *solicit ) + MIN_IOB_LEN );
-       
-       iob_reserve ( iobuf, MAX_HDR_LEN );
-       solicit = iob_put ( iobuf, sizeof ( *solicit ) );
-
-       /* Fill up the headers */
-       memset ( solicit, 0, sizeof ( *solicit ) );
-       solicit->type = ICMP6_ROUTER_SOLICIT;
-       solicit->code = 0;
-       
-       /* Partial checksum */
-       solicit->csum = 0;
-       solicit->csum = tcpip_chksum ( solicit, sizeof ( *solicit ) );
-
-       /* Solicited multicast address - FF02::2 (all routers on local network) */
-       memset(&st_dest.sin6, 0, sizeof(st_dest.sin6));
-       st_dest.sin6.sin_family = AF_INET6;
-       st_dest.sin6.sin6_addr.in6_u.u6_addr8[0] = 0xff;
-       st_dest.sin6.sin6_addr.in6_u.u6_addr8[1] = 0x2;
-       st_dest.sin6.sin6_addr.in6_u.u6_addr8[15] = 0x2;
-
-       /* Send packet over IP6 */
-       return ipv6_tx ( iobuf, &icmp6_protocol, NULL, &st_dest.st,
-                        netdev, &solicit->csum );
-}
-
 /**
  * Send neighbour advertisement packet
  *
index 6cd2813..dbe943c 100644 (file)
@@ -9,6 +9,7 @@
 #include <gpxe/icmp6.h>
 #include <gpxe/ip6.h>
 #include <gpxe/netdevice.h>
+#include <gpxe/job.h>
 
 /** @file
  *
@@ -31,6 +32,20 @@ struct ndp_entry {
        int state;
 };
 
+/** A pending router solicitation. */
+struct pending_rsolicit {
+       /** Network device for the solicit. */
+       struct net_device *netdev;
+       /** State of the solicitation. */
+       int state;
+       /** Status code after handling the solicit. */
+       int code;
+       /** Job control interface */
+       struct job_interface job;
+       /** Reference counter */
+       struct refcnt refcnt;
+};
+
 /** Number of entries in the neighbour cache table */
 #define NUM_NDP_ENTRIES 4
 
@@ -40,6 +55,37 @@ static struct ndp_entry ndp_table[NUM_NDP_ENTRIES];
 
 static unsigned int next_new_ndp_entry = 0;
 
+/** The pending solicit table */
+static struct pending_rsolicit solicit_table[NUM_NDP_ENTRIES];
+#define solicit_table_end &solicit_table[NUM_NDP_ENTRIES]
+
+static unsigned int next_new_solicit_entry = 0;
+
+/**
+ * Handle kill() event received via job control interface
+ *
+ * @v job              Router solicit job control interface
+ */
+static void rsolicit_job_kill ( struct job_interface *job ) {
+       struct pending_rsolicit *entry =
+               container_of ( job, struct pending_rsolicit, job );
+
+       /* Terminate. */
+       entry->code = 0;
+       entry->state = RSOLICIT_STATE_INVALID;
+       
+       /* Clean up. */
+       job_nullify ( &entry->job );
+       job_done ( &entry->job, -ECANCELED );
+}
+
+/** Router solicit job control interface operations */
+static struct job_interface_operations rsolicit_job_operations = {
+       .done           = ignore_job_done,
+       .kill           = rsolicit_job_kill,
+       .progress       = ignore_job_progress,
+};
+
 /**
  * Find entry in the neighbour cache
  *
@@ -58,6 +104,24 @@ ndp_find_entry ( struct in6_addr *in6 ) {
        return NULL;
 }
 
+/**
+ * Find a pending router solicitation for an interface.
+ *
+ * @v netdev   Interface for the solicitation.
+ */
+static struct pending_rsolicit *
+solicit_find_entry ( struct net_device *netdev ) {
+       struct pending_rsolicit *entry;
+
+       for ( entry = solicit_table ; entry < solicit_table_end ; entry++ ) {
+               if ( ( entry->netdev == netdev ) &&
+                    ( entry->state == RSOLICIT_STATE_PENDING ) ) {
+                       return entry;
+               }
+       }
+       return NULL;
+}
+
 /**
  * Add NDP entry
  *
@@ -86,6 +150,25 @@ add_ndp_entry ( struct net_device *netdev, struct in6_addr *in6,
              netdev->ll_protocol->ntoa ( ndp->ll_addr ) );
 }
 
+/**
+ * Add pending solicit entry
+ * 
+ * @v netdev   Network device
+ * @v state    State of the entry - one of the RSOLICIT_STATE_XXX values
+ */
+static struct pending_rsolicit *
+add_solicit_entry ( struct net_device *netdev, int state ) {
+       struct pending_rsolicit *entry;
+       entry = &solicit_table[next_new_solicit_entry++ % NUM_NDP_ENTRIES];
+
+       /* Fill up entry */
+       entry->netdev = netdev;
+       entry->state = state;
+       entry->code = RSOLICIT_CODE_NONE;
+       
+       return entry;
+}
+
 /**
  * Resolve the link-layer address
  *
@@ -140,6 +223,71 @@ int ndp_resolve ( struct net_device *netdev, struct in6_addr *dest,
        return -ENOENT;
 }
 
+/**
+ * Send router solicitation packet
+ *
+ * @v netdev   Network device
+ * @v src      Source address
+ * @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 ) {
+       union {
+               struct sockaddr_in6 sin6;
+               struct sockaddr_tcpip st;
+       } st_dest;
+       struct router_solicit *solicit;
+       struct io_buffer *iobuf = alloc_iob ( sizeof ( *solicit ) + MIN_IOB_LEN );
+       struct pending_rsolicit *entry;
+       int rc = 0;
+       
+       iob_reserve ( iobuf, MAX_HDR_LEN );
+       solicit = iob_put ( iobuf, sizeof ( *solicit ) );
+
+       /* Fill up the headers */
+       memset ( solicit, 0, sizeof ( *solicit ) );
+       solicit->type = ICMP6_ROUTER_SOLICIT;
+       solicit->code = 0;
+       
+       /* Partial checksum */
+       solicit->csum = 0;
+       solicit->csum = tcpip_chksum ( solicit, sizeof ( *solicit ) );
+
+       /* Solicited multicast address - FF02::2 (all routers on local network) */
+       memset(&st_dest.sin6, 0, sizeof(st_dest.sin6));
+       st_dest.sin6.sin_family = AF_INET6;
+       st_dest.sin6.sin6_addr.in6_u.u6_addr8[0] = 0xff;
+       st_dest.sin6.sin6_addr.in6_u.u6_addr8[1] = 0x2;
+       st_dest.sin6.sin6_addr.in6_u.u6_addr8[15] = 0x2;
+       
+       /* Add an entry for this solicitation. */
+       entry = add_solicit_entry ( netdev, RSOLICIT_STATE_ALMOST );
+       
+       /* Set up a job for the solicit. */
+       job_init ( &entry->job, &rsolicit_job_operations, &entry->refcnt );
+
+       /* Send packet over IP6 */
+       rc = ipv6_tx ( iobuf, &icmp6_protocol, NULL, &st_dest.st,
+                      netdev, &solicit->csum );
+       
+       /* Return. */
+       if ( rc == 0 ) {
+               entry->state = RSOLICIT_STATE_PENDING;
+               
+               job_plug_plug ( &entry->job, job );
+               ref_put ( &entry->refcnt );
+               return 0;
+       } else {
+               entry->state = RSOLICIT_STATE_INVALID;
+               
+               rsolicit_job_kill ( &entry->job );
+               ref_put ( &entry->refcnt );
+               return rc;
+       }
+}
+
 /**
  * Process Router Advertisement
  *
@@ -157,13 +305,24 @@ int ndp_process_radvert ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src
        int rc = -ENOENT;
        uint8_t prefix_len = 0;
        size_t offset = sizeof ( struct router_advert ), ll_size;
+       
+       /* Verify that there's a pending solicit */
+       struct pending_rsolicit *pending = solicit_find_entry ( netdev );
+       if ( ! pending ) {
+               DBG ( "ndp: unsolicited router advertisement, ignoring\n" );
+               return rc;
+       }
 
        memset ( &host_addr, 0, sizeof ( host_addr ) );
 
-       /* Verify that we shouldn't be trying DHCPv6 instead. */
+       /* Router advertisement flags */
        if ( ntohs ( radvert->hops_flags ) & RADVERT_MANAGED ) {
                DBG ( "ndp: router advertisement suggests DHCPv6\n" );
-               return 0;
+               pending->code |= RSOLICIT_CODE_MANAGED;
+       }
+       if ( ntohs ( radvert->hops_flags ) & RADVERT_OTHERCONF ) {
+               DBG ( "ndp: router advertisement suggests DHCPv6 for additional information\n" );
+               pending->code |= RSOLICIT_CODE_OTHERCONF;
        }
 
        /* Parse options. */
@@ -220,6 +379,10 @@ int ndp_process_radvert ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src
 
        if ( rc ) {
                DBG ( "ndp: couldn't generate a prefix from a router advertisement\n" );
+               pending->code = RSOLICIT_CODE_NONE; /* Clear flags. */
+               
+               job_done ( &pending->job, -ENOENT );
+               
                return 0;
        }
 
@@ -228,6 +391,10 @@ int ndp_process_radvert ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src
                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;
        }
 
        return 0;
index 9b8e9cb..2f84261 100644 (file)
@@ -73,8 +73,22 @@ int ip6_autoconf ( struct net_device *netdev ) {
        add_ipv6_address ( netdev, ip6addr, 10, ip6addr, ip6zero );
        
        /* Solicit routers on the network. */
-       icmp6_send_rsolicit ( netdev );
+       if ( ( rc = ndp_send_rsolicit ( netdev, &monojob ) ) == 0 ) {
+               rc = monojob_wait ( "" );
+       }
+       
+       if ( rc < 0 ) {
+               DBG ( "ipv6: router solicitation failed\n" );
+       } else {
+               if ( rc & RSOLICIT_CODE_MANAGED ) {
+                       DBG ( "ipv6: should use dhcp6 server\n" );
+               } else if ( rc & RSOLICIT_CODE_OTHERCONF ) {
+                       DBG ( "ipv6: some more info is available via dhcp6\n" );
+               } else {
+                       DBG ( "ipv6: autoconfiguration complete\n" );
+               }
+       }
        
-       return 0;
+       return rc;
 }