[gPXE-devel] [PATCH 29/31] [ipv6] make router solicits block until complete, and also return a status code to the caller
matthew at theiselins.net
matthew at theiselins.net
Fri Jul 8 10:28:38 EDT 2011
From: Matthew Iselin <matthew at theiselins.net>
Note: does not yet time out if no router advertisements are received.
Signed-off-by: Matthew Iselin <matthew at theiselins.net>
---
src/include/gpxe/icmp6.h | 2 -
src/include/gpxe/ndp.h | 12 +++
src/net/icmpv6.c | 42 -----------
src/net/ndp.c | 171 +++++++++++++++++++++++++++++++++++++++++++++-
src/usr/ip6mgmt.c | 18 ++++-
5 files changed, 197 insertions(+), 48 deletions(-)
diff --git a/src/include/gpxe/icmp6.h b/src/include/gpxe/icmp6.h
index 23b7b56..6040258 100644
--- a/src/include/gpxe/icmp6.h
+++ b/src/include/gpxe/icmp6.h
@@ -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 */
diff --git a/src/include/gpxe/ndp.h b/src/include/gpxe/ndp.h
index be387ce..b8a00d7 100644
--- a/src/include/gpxe/ndp.h
+++ b/src/include/gpxe/ndp.h
@@ -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 );
diff --git a/src/net/icmpv6.c b/src/net/icmpv6.c
index f24b98b..ac4e308 100644
--- a/src/net/icmpv6.c
+++ b/src/net/icmpv6.c
@@ -68,48 +68,6 @@ int icmp6_send_solicit ( struct net_device *netdev, struct in6_addr *src __unuse
}
/**
- * 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
*
* @v netdev Network device
diff --git a/src/net/ndp.c b/src/net/ndp.c
index be1fa94..e0c53d8 100644
--- a/src/net/ndp.c
+++ b/src/net/ndp.c
@@ -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
*
@@ -59,6 +105,24 @@ ndp_find_entry ( struct in6_addr *in6 ) {
}
/**
+ * 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
*
* @v netdev Network device
@@ -87,6 +151,25 @@ add_ndp_entry ( struct net_device *netdev, struct in6_addr *in6,
}
/**
+ * 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
*
* @v netdev Network device
@@ -141,6 +224,71 @@ int ndp_resolve ( struct net_device *netdev, struct in6_addr *dest,
}
/**
+ * 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
*
* @v iobuf I/O buffer containing the data.
@@ -158,12 +306,23 @@ int ndp_process_radvert ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src
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;
diff --git a/src/usr/ip6mgmt.c b/src/usr/ip6mgmt.c
index 9b8e9cb..2f84261 100644
--- a/src/usr/ip6mgmt.c
+++ b/src/usr/ip6mgmt.c
@@ -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;
}
--
1.7.2.5
More information about the gPXE-devel
mailing list