[ipv6] Add "ipv6" command to perform stateless autoconfiguration
authorMatthew Iselin <matthew@theiselins.net>
Tue, 24 May 2011 10:53:00 +0000 (20:53 +1000)
committerMarty Connor <mdc@etherboot.org>
Tue, 19 Jul 2011 00:21:49 +0000 (20:21 -0400)
This command performs stateless autoconfiguration on a given network
interface. It does not yet assign the address to the netdevice.

Signed-off-by: Matthew Iselin <matthew@theiselins.net>
Signed-off-by: Marty Connor <mdc@etherboot.org>
src/config/config.c
src/config/general.h
src/hci/commands/ipv6_cmd.c [new file with mode: 0644]
src/include/usr/ip6mgmt.h [new file with mode: 0644]
src/usr/ip6mgmt.c [new file with mode: 0644]

index f468064..cf6d6ba 100644 (file)
@@ -238,6 +238,9 @@ REQUIRE_OBJECT ( digest_cmd );
 #ifdef PXE_CMD
 REQUIRE_OBJECT ( pxe_cmd );
 #endif
+#ifdef IPV6_CMD
+REQUIRE_OBJECT ( ipv6_cmd );
+#endif
 
 /*
  * Drag in miscellaneous objects
index b58dff9..0bb364e 100644 (file)
@@ -121,6 +121,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
 #undef TIME_CMD                /* Time commands */
 #undef DIGEST_CMD              /* Image crypto digest commands */
 //#undef       PXE_CMD                 /* PXE commands */
+#define IPV6_CMD               /* IPv6 commands */
 
 /*
  * Error message tables to include
diff --git a/src/hci/commands/ipv6_cmd.c b/src/hci/commands/ipv6_cmd.c
new file mode 100644 (file)
index 0000000..e794e6c
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2011 Matthew Iselin <matthew@theiselins.net>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stddef.h>
+#include <string.h>
+#include <assert.h>
+#include <getopt.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/in.h>
+#include <gpxe/command.h>
+#include <usr/ip6mgmt.h>
+#include <usr/ifmgmt.h>
+
+/** @file
+ *
+ * IPv6 management commands
+ *
+ */
+
+/**
+ * "ipv6" command syntax message
+ *
+ * @v argv             Argument list
+ */
+static void ipv6_syntax ( char **argv ) {
+       printf ( "Usage:\n"
+                "  %s <interface> [<interface> ...]\n"
+                "  %s any\n"
+                "\n"
+                "Prepare a network interface for use with IPv6\n",
+                argv[0], argv[0] );
+}
+
+
+/**
+ * Attempt to configure a device with an IPv6 link-local address
+ *
+ * @v netdev           Device to configure
+ * @ret rc             Exit code
+ */
+static int ipv6_one_device ( struct net_device *netdev ) {
+       int rc;
+
+       /* Perform autoconfiguration */
+       if ( ( rc = ip6_autoconf ( netdev ) ) != 0 ) {
+               /* Close the device on error to avoid out-of-memory */
+               netdev_close ( netdev );
+
+               printf ( "Could not configure %s: %s\n", netdev->name,
+                        strerror ( rc ) );
+               return 1;
+       }
+
+       return 0;
+}
+
+/**
+ * Call ipv6_one_device() for each name in argv
+ *
+ * @v argc             Number of devices
+ * @v argv             List of device names
+ * @ret rc             Exit code
+ */
+static int ipv6_each_device_name ( int argc, char **argv ) {
+       int i;
+       char *netdev_name;
+       struct net_device *netdev;
+
+       for ( i = 0; i < argc; i++ ) {
+               netdev_name = argv[i];
+               netdev = find_netdev ( netdev_name );
+
+               if ( ! netdev ) {
+                       printf ( "No such interface: %s\n", netdev_name );
+                       continue;
+               }
+
+               if ( ipv6_one_device ( netdev ) == 0 )
+                       return 0;
+       }
+
+       printf ( "Could not configure any interface.\n" );
+       return 1;
+}
+
+/**
+ * Call ipv6_one_device() for each device in net_devices
+ *
+ * @ret rc             Exit code
+ */
+static int ipv6_each_device ( void ) {
+       struct net_device *netdev;
+
+       for_each_netdev ( netdev ) {
+               if ( ipv6_one_device ( netdev ) == 0 )
+                       return 0;
+       }
+
+       printf ( "Could not configure any interface.\n" );
+       return 1;
+}
+
+/**
+ * The "ipv6" command
+ *
+ * @v argc             Argument count
+ * @v argv             Argument list
+ * @ret rc             Exit code
+ */
+static int ipv6_exec ( int argc __unused, char **argv __unused ) {
+       static struct option longopts[] = {
+               { "help", 0, NULL, 'h' },
+               { NULL, 0, NULL, 0 },
+       };
+       int c;
+
+       /* Parse options */
+       while ( ( c = getopt_long ( argc, argv, "h", longopts, NULL ) ) >= 0 ){
+               switch ( c ) {
+               case 'h':
+                       /* Display help text */
+               default:
+                       /* Unrecognised/invalid option */
+                       ipv6_syntax ( argv );
+                       return 1;
+               }
+       }
+
+       /* Need one or more interface names remaining after the options */
+       if ( ( argc - optind ) < 1 ) {
+               ipv6_syntax ( argv );
+               return 1;
+       }
+
+       if ( strcmp ( argv[optind], "any" ) == 0 )
+               return ipv6_each_device();
+
+       return ipv6_each_device_name ( argc - optind, argv + optind );
+}
+
+static int dhcp6_exec ( int argc __unused, char **argv __unused ) {
+       printf ( "DHCPv6 is not yet implemented.\n" );
+       return 0;
+}
+
+/** IPv6 management commands */
+struct command ipv6_commands[] __command = {
+       {
+               .name = "ipv6",
+               .exec = ipv6_exec,
+       },
+       {
+               .name = "dhcp6",
+               .exec = dhcp6_exec,
+       }
+};
+
+
diff --git a/src/include/usr/ip6mgmt.h b/src/include/usr/ip6mgmt.h
new file mode 100644 (file)
index 0000000..9c17285
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef _USR_IPV6MGMT_H
+#define _USR_IPV6MGMT_H
+
+/** @file
+ *
+ * IPv6 management
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+struct net_device;
+
+extern int ip6_autoconf ( struct net_device *netdev );
+
+#endif /* _USR_IPV6MGMT_H */
+
diff --git a/src/usr/ip6mgmt.c b/src/usr/ip6mgmt.c
new file mode 100644 (file)
index 0000000..0809c7b
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2011 Matthew Iselin <matthew@theiselins.net>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/in.h>
+#include <gpxe/ip6.h>
+#include <gpxe/monojob.h>
+#include <gpxe/process.h>
+#include <usr/ifmgmt.h>
+#include <usr/ip6mgmt.h>
+
+#define LINK_WAIT_MS   15000
+
+/* Maximum length of the link-layer address we'll insert as an EUI-64. */
+#define AUTOCONF_LL_MAX        6
+
+int ip6_autoconf ( struct net_device *netdev __unused ) {
+       struct in6_addr ip6addr;
+       size_t ll_size;
+       int rc;
+
+       /* Check we can open the interface first */
+       if ( ( rc = ifopen ( netdev ) ) != 0 )
+               return rc;
+
+       /* Wait for link-up */
+       if ( ( rc = iflinkwait ( netdev, LINK_WAIT_MS ) ) != 0 )
+               return rc;
+       
+       /* Create the host ID part of the IPv6 address from the Link-Layer
+        * address on the netdevice. */
+       memset ( &ip6addr, 0, sizeof (struct in6_addr) );
+       
+       ll_size = netdev->ll_protocol->ll_addr_len;
+       if ( ll_size < 6 ) {
+               memcpy ( ip6addr.s6_addr + (8 - ll_size), netdev->ll_addr, ll_size );
+       } else {
+               /* Create an EUI-64 identifier. */
+               memcpy( ip6addr.s6_addr + 8, netdev->ll_addr, 3 );
+               memcpy( ip6addr.s6_addr + 8 + 5, netdev->ll_addr + 3, 3 );
+               ip6addr.s6_addr[11] = 0xFF;
+               ip6addr.s6_addr[12] = 0xFE;
+               
+               /* Designate that this is in fact an EUI-64. */
+               ip6addr.s6_addr[8] |= 0x2;
+       }
+       
+       /* Fill in the link-local prefix. */
+       ip6addr.s6_addr[0] = 0xFE;
+       ip6addr.s6_addr[1] = 0x80;
+       
+       DBG( "ip6 autoconfig address is %s\n", inet6_ntoa(ip6addr) );
+       
+       return 0;
+}
+