[gPXE-devel] [PATCH 16/31] [ipv6] implement router advertisement handling for autoconfiguration

matthew at theiselins.net matthew at theiselins.net
Fri Jul 8 10:28:25 EDT 2011


From: Matthew Iselin <matthew at theiselins.net>

Signed-off-by: Matthew Iselin <matthew at theiselins.net>
---
 src/include/gpxe/icmp6.h |   17 ---------
 src/include/gpxe/ndp.h   |   22 +++++++++++-
 src/net/icmpv6.c         |    2 +-
 src/net/ndp.c            |   90 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 112 insertions(+), 19 deletions(-)

diff --git a/src/include/gpxe/icmp6.h b/src/include/gpxe/icmp6.h
index 4a1f136..6040258 100644
--- a/src/include/gpxe/icmp6.h
+++ b/src/include/gpxe/icmp6.h
@@ -55,23 +55,6 @@ struct icmp6_header {
 	/* Message body */
 };
 
-struct router_solicit {
-	uint8_t type;
-	uint8_t code;
-	uint16_t csum;
-	uint32_t reserved;
-};
-
-struct router_advert {
-	uint8_t type;
-	uint8_t code;
-	uint16_t csum;
-	uint16_t lifetime;
-	uint16_t hops_flags;
-	uint32_t reachable_time;
-	uint32_t retrans_time;
-};
-
 #define ICMP6_FLAGS_ROUTER 0x80
 #define ICMP6_FLAGS_SOLICITED 0x40
 #define ICMP6_FLAGS_OVERRIDE 0x20
diff --git a/src/include/gpxe/ndp.h b/src/include/gpxe/ndp.h
index 88cdb8e..6830362 100644
--- a/src/include/gpxe/ndp.h
+++ b/src/include/gpxe/ndp.h
@@ -41,6 +41,23 @@ struct neighbour_advert {
 	struct in6_addr target;
 };
 
+struct router_solicit {
+	uint8_t type;
+	uint8_t code;
+	uint16_t csum;
+	uint32_t reserved;
+};
+
+struct router_advert {
+	uint8_t type;
+	uint8_t code;
+	uint16_t csum;
+	uint16_t lifetime;
+	uint16_t hops_flags;
+	uint32_t reachable_time;
+	uint32_t retrans_time;
+};
+
 struct ndp_option
 {
 	uint8_t type;
@@ -66,11 +83,14 @@ struct prefix_option
 	uint8_t prefix[16];
 };
 
+#define RADVERT_MANAGED		0x100
+#define RADVERT_OTHERCONF	0x101
+
 int ndp_resolve ( struct net_device *netdev, struct in6_addr *src,
 		  struct in6_addr *dest, void *dest_ll_addr );
 
 int ndp_process_radvert ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src,
-			  struct sockaddr_tcpip *st_dest,
+			  struct sockaddr_tcpip *st_dest, struct net_device *netdev,
 			  struct icmp6_net_protocol *net_protocol );
 
 int ndp_process_nadvert ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src,
diff --git a/src/net/icmpv6.c b/src/net/icmpv6.c
index cd52456..fb4d9ea 100644
--- a/src/net/icmpv6.c
+++ b/src/net/icmpv6.c
@@ -210,7 +210,7 @@ int icmp6_rx ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src,
 	/* Process the ICMP header */
 	switch ( icmp6hdr->type ) {
 	case ICMP6_ROUTER_ADVERT:
-	    return ndp_process_radvert ( iobuf, st_src, st_dest, icmp6_net_protocol );
+	    return ndp_process_radvert ( iobuf, st_src, st_dest, netdev, icmp6_net_protocol );
 	case ICMP6_NSOLICIT:
 		return ndp_process_nsolicit ( iobuf, st_src, st_dest, netdev, icmp6_net_protocol );
 	case ICMP6_NADVERT:
diff --git a/src/net/ndp.c b/src/net/ndp.c
index 0eb27ff..35747dd 100644
--- a/src/net/ndp.c
+++ b/src/net/ndp.c
@@ -1,5 +1,6 @@
 #include <stdint.h>
 #include <string.h>
+#include <stdlib.h>
 #include <byteswap.h>
 #include <errno.h>
 #include <gpxe/if_ether.h>
@@ -140,6 +141,95 @@ int ndp_resolve ( struct net_device *netdev, struct in6_addr *dest,
 }
 
 /**
+ * Process Router Advertisement
+ *
+ * @v iobuf I/O buffer containing the data.
+ * @v st_src Address of the source station.
+ * @v st_dest Address of the destination station. Typically FF02::1.
+ */
+int ndp_process_radvert ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src,
+			  struct sockaddr_tcpip *st_dest __unused, struct net_device *netdev,
+			  struct icmp6_net_protocol *net_protocol __unused ) {
+	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;
+	int rc = -ENOENT;
+	uint8_t prefix_len = 0;
+	size_t offset = sizeof ( struct router_advert ), ll_size;
+	
+	memset ( &host_addr, 0, sizeof ( host_addr ) );
+	
+	/* Verify that we shouldn't be trying DHCPv6 instead. */
+	if ( ntohs ( radvert->hops_flags ) & RADVERT_MANAGED ) {
+		DBG ( "ndp: router advertisement suggests DHCPv6\n" );
+		return 0;
+	}
+	
+	/* Parse options. */
+	while ( offset < iob_len( iobuf ) ) {
+	
+	    switch ( options->type ) {
+	    case NDP_OPTION_PREFIX_INFO:
+	        {
+	        struct prefix_option *opt = (struct prefix_option *) options;
+	        
+	        prefix_len = opt->prefix_len;
+	        
+	        if ( prefix_len % 8 ) {
+	        	/* FIXME: non-aligned prefixes unhandled */
+	        	DBG ( "ndp: prefix length is unaligned, connectivity may suffer.\n" );
+	        }
+	        
+	        if ( prefix_len > 64 ) {
+	        	/* > 64-bit prefix shouldn't happen. */
+	        	DBG ( "ndp: prefix length is quite long, connectivity may suffer.\n" );
+	        }
+	        
+	        /* Create an IPv6 address for this station based on the prefix. */
+		ll_size = netdev->ll_protocol->ll_addr_len;
+		if ( ll_size < 6 ) {
+			memcpy ( host_addr.s6_addr + (8 - ll_size), netdev->ll_addr, ll_size );
+		} else {
+			/* Create an EUI-64 identifier. */
+			memcpy( host_addr.s6_addr + 8, netdev->ll_addr, 3 );
+			memcpy( host_addr.s6_addr + 8 + 5, netdev->ll_addr + 3, 3 );
+			host_addr.s6_addr[11] = 0xFF;
+			host_addr.s6_addr[12] = 0xFE;
+		
+			/* Designate that this is in fact an EUI-64. */
+			host_addr.s6_addr[8] |= 0x2;
+		}
+	        
+	        memcpy( &host_addr.s6_addr, opt->prefix, prefix_len / 8 );
+	        
+	        rc = 0;
+	        }
+	        break;
+        default:
+		DBG ( "unhandled ndp option %d\n", options->type );
+	    }
+	
+	    offset += options->length * 8;
+	    options = (struct ndp_option *) (iobuf->data + offset);
+	}
+	
+	if ( rc ) {
+		DBG ( "ndp: couldn't generate a prefix from a router advertisement\n" );
+		return 0;
+	}
+	
+	/* Configure a route based on this router if none exists. */
+	if ( 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 );
+	}
+	
+	return 0;
+}
+
+/**
  * Process neighbour advertisement
  *
  * @v iobuf	I/O buffer
-- 
1.7.2.5



More information about the gPXE-devel mailing list