[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