[ipxe-devel] [PATCH 2/2] ibft: IPv6 support

Hannes Reinecke hare at suse.de
Mon Feb 5 08:38:21 UTC 2018


The iBFT table is defined to be used with both, IPv4 and IPv6.
So update the iBFT handling to work with IPv6 addresses, too.
This patch updates the iBFT handling to extract work with
miniroutes to get the correct information for both IPv4 and IPv6.

Signed-off-by: Hannes Reinecke <hare at suse.de>
---
 src/drivers/block/ibft.c    | 141 ++++++++++++++++++++++++++++++++++----------
 src/include/ipxe/ibft.h     |  13 +++-
 src/include/ipxe/ip.h       |   2 +
 src/include/ipxe/settings.h |   2 +
 src/net/ipv4.c              |   4 +-
 5 files changed, 128 insertions(+), 34 deletions(-)

diff --git a/src/drivers/block/ibft.c b/src/drivers/block/ibft.c
index f9918363a..a07ee7531 100644
--- a/src/drivers/block/ibft.c
+++ b/src/drivers/block/ibft.c
@@ -40,6 +40,8 @@ FILE_LICENCE ( BSD2 );
 #include <ipxe/ethernet.h>
 #include <ipxe/vlan.h>
 #include <ipxe/tcpip.h>
+#include <ipxe/ip.h>
+#include <ipxe/ipv6.h>
 #include <ipxe/dhcp.h>
 #include <ipxe/iscsi.h>
 #include <ipxe/ibft.h>
@@ -81,6 +83,13 @@ static inline size_t ibft_align ( size_t len ) {
 	return ( ( len + IBFT_ALIGN - 1 ) & ~( IBFT_ALIGN - 1 ) );
 }
 
+static int ibft_ipaddr_is_ipv6 ( struct ibft_ipaddr *ipaddr )
+{
+	uint8_t prefix[12] = { 0, 0, 0, 0, 0, 0,
+			       0, 0, 0, 0, 0xff, 0xff };
+	return (memcmp(ipaddr->raw, &prefix, sizeof (prefix) ));
+}
+
 /**
  * Fill in an IP address field within iBFT
  *
@@ -90,8 +99,22 @@ static inline size_t ibft_align ( size_t len ) {
 static void ibft_set_ipaddr ( struct ibft_ipaddr *ipaddr, struct in_addr in ) {
 	memset ( ipaddr, 0, sizeof ( *ipaddr ) );
 	if ( in.s_addr ) {
-		ipaddr->in = in;
-		ipaddr->ones = 0xffff;
+		ipaddr->in.in = in;
+		ipaddr->in.ones = 0xffff;
+	}
+}
+
+/**
+ * Fill in an IPv6 address field within iBFT
+ *
+ * @v ipaddr           IP address field
+ * @v in6              IPv6 address
+ */
+static void ibft_set_ip6addr ( struct ibft_ipaddr *ipaddr,
+			       struct in6_addr in6 ) {
+	memset ( ipaddr, 0, sizeof ( *ipaddr ) );
+	if ( in6.s6_addr ) {
+		ipaddr->in6 = in6;
 	}
 }
 
@@ -117,6 +140,27 @@ static void ibft_set_ipaddr_setting ( struct settings *settings,
 }
 
 /**
+ * Fill in an IPv6 address within iBFT from configuration setting
+ *
+ * @v settings         Parent settings block, or NULL
+ * @v ipaddr           address field
+ * @v setting          Configuration setting
+ * @v count            Maximum number of IP addresses
+ */
+static void ibft_set_ip6addr_setting ( struct settings *settings,
+				       struct ibft_ipaddr *ipaddr,
+				       const struct setting *setting,
+				       unsigned int count ) {
+	struct in6_addr in6[count];
+	unsigned int i;
+
+	fetch_ipv6_array_setting ( settings, setting, in6, count );
+	for ( i = 0 ; i < count ; i++ ) {
+		ibft_set_ip6addr ( &ipaddr[i], in6[i] );
+	}
+}
+
+/**
  * Read IP address from iBFT (for debugging)
  *
  * @v strings		iBFT string block descriptor
@@ -124,7 +168,10 @@ static void ibft_set_ipaddr_setting ( struct settings *settings,
  * @ret ipaddr		IP address string
  */
 static const char * ibft_ipaddr ( struct ibft_ipaddr *ipaddr ) {
-	return inet_ntoa ( ipaddr->in );
+	if  ( ibft_ipaddr_is_ipv6 ( ipaddr ) )
+		return inet6_ntoa ( &ipaddr->in6 );
+	else
+		return inet_ntoa ( ipaddr->in.in );
 }
 
 /**
@@ -233,22 +280,22 @@ static const char * ibft_string ( struct ibft_strings *strings,
 }
 
 /**
- * Check if network device is required for the iBFT
+ * Return target address from the iBFT network device
  *
  * @v netdev		Network device
- * @ret is_required	Network device is required
+ * @ret st_target	Destination address for the target
  */
-static int ibft_netdev_is_required ( struct net_device *netdev ) {
+static struct sockaddr_tcpip * ibft_target_addr ( struct net_device *netdev ) {
 	struct iscsi_session *iscsi;
 	struct sockaddr_tcpip *st_target;
 
 	list_for_each_entry ( iscsi, &ibft_model.descs, desc.list ) {
 		st_target = ( struct sockaddr_tcpip * ) &iscsi->target_sockaddr;
 		if ( tcpip_netdev ( st_target ) == netdev )
-			return 1;
+			return st_target;
 	}
 
-	return 0;
+	return NULL;
 }
 
 /**
@@ -261,7 +308,8 @@ static int ibft_netdev_is_required ( struct net_device *netdev ) {
  */
 static int ibft_fill_nic ( struct ibft_nic *nic,
 			   struct ibft_strings *strings,
-			   struct net_device *netdev ) {
+			   struct sockaddr_tcpip *st_target ) {
+	struct net_device *netdev = tcpip_netdev ( st_target );
 	struct ll_protocol *ll_protocol = netdev->ll_protocol;
 	struct in_addr netmask_addr = { 0 };
 	unsigned int netmask_count = 0;
@@ -284,16 +332,45 @@ static int ibft_fill_nic ( struct ibft_nic *nic,
 	DBG ( "iBFT NIC %d origin = %d\n", nic->header.index, nic->origin );
 
 	/* Extract values from configuration settings */
-	ibft_set_ipaddr_setting ( parent, &nic->ip_address, &ip_setting, 1 );
+	if ( st_target->st_family == AF_INET ) {
+		struct ipv4_miniroute *route4;
+		struct sockaddr_in *sin_dest =
+			( struct sockaddr_in * ) st_target;
+
+		route4 = ipv4_route( sin_dest->sin_scope_id,
+				     &sin_dest->sin_addr);
+		ibft_set_ipaddr ( &nic->ip_address, route4->address );
+		ibft_set_ipaddr ( &nic->gateway, route4->gateway );
+		ibft_set_ipaddr_setting ( NULL, &nic->dns[0], &dns_setting,
+					  ( sizeof ( nic->dns ) /
+					    sizeof ( nic->dns[0] ) ) );
+		ibft_set_ipaddr_setting ( parent, &nic->dhcp,
+					  &dhcp_server_setting, 1 );
+		/* Derive subnet mask prefix from subnet mask */
+		netmask_addr = route4->netmask;
+		while ( netmask_addr.s_addr ) {
+			if ( netmask_addr.s_addr & 0x1 )
+				netmask_count++;
+			netmask_addr.s_addr >>= 1;
+		}
+		nic->subnet_mask_prefix = netmask_count;
+	} else {
+		struct sockaddr_in6 *sin6_dest =
+			( struct sockaddr_in6 * ) st_target;
+		struct ipv6_miniroute *route6;
+		struct in6_addr *dest = &sin6_dest->sin6_addr;
+		route6 = ipv6_route ( sin6_dest->sin6_scope_id, &dest );
+		ibft_set_ip6addr  ( &nic->ip_address, route6->address );
+		ibft_set_ip6addr ( &nic->gateway, route6->router );
+		ibft_set_ip6addr_setting ( NULL, &nic->dns[0], &dns6_setting,
+					   ( sizeof ( nic->dns ) /
+					     sizeof ( nic->dns[0] ) ) );
+		nic->subnet_mask_prefix = route6->prefix_len;
+	}
 	DBG ( "iBFT NIC %d IP = %s\n",
 	      nic->header.index, ibft_ipaddr ( &nic->ip_address ) );
-	ibft_set_ipaddr_setting ( parent, &nic->gateway, &gateway_setting, 1 );
 	DBG ( "iBFT NIC %d gateway = %s\n",
 	      nic->header.index, ibft_ipaddr ( &nic->gateway ) );
-	ibft_set_ipaddr_setting ( NULL, &nic->dns[0], &dns_setting,
-				  ( sizeof ( nic->dns ) /
-				    sizeof ( nic->dns[0] ) ) );
-	ibft_set_ipaddr_setting ( parent, &nic->dhcp, &dhcp_server_setting, 1 );
 	DBG ( "iBFT NIC %d DNS = %s",
 	      nic->header.index, ibft_ipaddr ( &nic->dns[0] ) );
 	DBG ( ", %s\n", ibft_ipaddr ( &nic->dns[1] ) );
@@ -303,14 +380,6 @@ static int ibft_fill_nic ( struct ibft_nic *nic,
 	DBG ( "iBFT NIC %d hostname = %s\n",
 	      nic->header.index, ibft_string ( strings, &nic->hostname ) );
 
-	/* Derive subnet mask prefix from subnet mask */
-	fetch_ipv4_setting ( parent, &netmask_setting, &netmask_addr );
-	while ( netmask_addr.s_addr ) {
-		if ( netmask_addr.s_addr & 0x1 )
-			netmask_count++;
-		netmask_addr.s_addr >>= 1;
-	}
-	nic->subnet_mask_prefix = netmask_count;
 	DBG ( "iBFT NIC %d subnet = /%d\n",
 	      nic->header.index, nic->subnet_mask_prefix );
 
@@ -393,7 +462,7 @@ static int ibft_fill_target_nic_association ( struct ibft_target *target,
 			      netdev->name );
 			return 0;
 		}
-		if ( ! ibft_netdev_is_required ( netdev ) )
+		if ( ! ibft_target_addr ( netdev ) )
 			continue;
 		target->nic_association++;
 	}
@@ -485,8 +554,6 @@ static int ibft_fill_target ( struct ibft_target *target,
 			      struct iscsi_session *iscsi ) {
 	struct sockaddr_tcpip *st_target =
 		( struct sockaddr_tcpip * ) &iscsi->target_sockaddr;
-	struct sockaddr_in *sin_target =
-		( struct sockaddr_in * ) &iscsi->target_sockaddr;
 	int rc;
 
 	/* Fill in common header */
@@ -497,10 +564,23 @@ static int ibft_fill_target ( struct ibft_target *target,
 				 IBFT_FL_TARGET_FIRMWARE_BOOT_SELECTED );
 
 	/* Fill in Target values */
-	ibft_set_ipaddr ( &target->ip_address, sin_target->sin_addr );
+	if ( st_target->st_family == AF_INET ) {
+		struct sockaddr_in *sin_target =
+			( struct sockaddr_in * ) &iscsi->target_sockaddr;
+
+		ibft_set_ipaddr ( &target->ip_address, sin_target->sin_addr );
+		target->socket = cpu_to_le16 ( ntohs ( sin_target->sin_port ) );
+	} else {
+		struct sockaddr_in6 *sin6_target =
+			( struct sockaddr_in6 * ) &iscsi->target_sockaddr;
+
+		ibft_set_ip6addr ( &target->ip_address,
+				   sin6_target->sin6_addr );
+		target->socket =
+			cpu_to_le16 ( ntohs ( sin6_target->sin6_port ) );
+	}
 	DBG ( "iBFT target %d IP = %s\n",
 	      target->header.index, ibft_ipaddr ( &target->ip_address ) );
-	target->socket = cpu_to_le16 ( ntohs ( st_target->st_port ) );
 	DBG ( "iBFT target %d port = %d\n",
 	      target->header.index, target->socket );
 	memcpy ( &target->boot_lun, &iscsi->lun, sizeof ( target->boot_lun ) );
@@ -554,6 +634,7 @@ static int ibft_install ( int ( * install ) ( struct acpi_header *acpi ) ) {
 	struct ibft_target *target;
 	struct ibft_strings strings;
 	struct acpi_header *acpi;
+	struct sockaddr_tcpip *st_target;
 	void *data;
 	unsigned int targets = 0;
 	unsigned int pairs = 0;
@@ -623,13 +704,13 @@ static int ibft_install ( int ( * install ) ( struct acpi_header *acpi ) ) {
 	/* Fill in NIC blocks */
 	i = 0;
 	for_each_netdev ( netdev ) {
-		if ( ! ibft_netdev_is_required ( netdev ) )
+		if ( ! ( st_target = ibft_target_addr ( netdev ) ) )
 			continue;
 		assert ( i < pairs );
 		table->control.pair[i].nic = nic_offset;
 		nic = ( data + nic_offset );
 		nic->header.index = i;
-		if ( ( rc = ibft_fill_nic ( nic, &strings, netdev ) ) != 0 )
+		if ( ( rc = ibft_fill_nic ( nic, &strings, st_target ) ) != 0 )
 			goto err_nic;
 		i++;
 		nic_offset += ibft_align ( sizeof ( *nic ) );
diff --git a/src/include/ipxe/ibft.h b/src/include/ipxe/ibft.h
index 51ce781a6..4be763890 100644
--- a/src/include/ipxe/ibft.h
+++ b/src/include/ipxe/ibft.h
@@ -66,8 +66,8 @@ struct ibft_string {
 	ibft_off_t offset;
 } __attribute__ (( packed ));
 
-/** An IP address within the iBFT */
-struct ibft_ipaddr {
+/** iBFT IPv address representation */
+struct ibft_inaddr {
 	/** Reserved; must be zero */
 	uint16_t zeroes[5];
 	/** Must be 0xffff if IPv4 address is present, otherwise zero */
@@ -76,6 +76,15 @@ struct ibft_ipaddr {
 	struct in_addr in;
 } __attribute__ (( packed ));
 
+/** An IP address within iBFT */
+struct ibft_ipaddr {
+	union {
+		struct ibft_inaddr in;
+		struct in6_addr in6;
+		uint8_t raw[16];
+	};
+} __attribute__ (( packed ));
+
 /**
  * iBFT structure header
  *
diff --git a/src/include/ipxe/ip.h b/src/include/ipxe/ip.h
index 285be6dcd..144d2539e 100644
--- a/src/include/ipxe/ip.h
+++ b/src/include/ipxe/ip.h
@@ -75,6 +75,8 @@ extern struct list_head ipv4_miniroutes;
 extern struct net_protocol ipv4_protocol __net_protocol;
 
 extern int ipv4_has_any_addr ( struct net_device *netdev );
+extern struct ipv4_miniroute * ipv4_route ( unsigned int scope_id,
+					    struct in_addr *dest );
 extern int parse_ipv4_setting ( const struct setting_type *type,
 				const char *value, void *buf, size_t len );
 extern int format_ipv4_setting ( const struct setting_type *type,
diff --git a/src/include/ipxe/settings.h b/src/include/ipxe/settings.h
index f463e6674..e7085a6b3 100644
--- a/src/include/ipxe/settings.h
+++ b/src/include/ipxe/settings.h
@@ -444,6 +444,8 @@ len6_setting __setting ( SETTING_IP6, len6 );
 extern const struct setting
 gateway6_setting __setting ( SETTING_IP6, gateway6 );
 extern const struct setting
+dns6_setting __setting ( SETTING_IP6_EXTRA, dns6 );
+extern const struct setting
 hostname_setting __setting ( SETTING_HOST, hostname );
 extern const struct setting
 domain_setting __setting ( SETTING_IP_EXTRA, domain );
diff --git a/src/net/ipv4.c b/src/net/ipv4.c
index b9ce5e7f7..a7f8ab6cc 100644
--- a/src/net/ipv4.c
+++ b/src/net/ipv4.c
@@ -147,8 +147,8 @@ static void del_ipv4_miniroute ( struct ipv4_miniroute *miniroute ) {
  * If the route requires use of a gateway, the next hop destination
  * address will be overwritten with the gateway address.
  */
-static struct ipv4_miniroute * ipv4_route ( unsigned int scope_id,
-					    struct in_addr *dest ) {
+struct ipv4_miniroute * ipv4_route ( unsigned int scope_id,
+				     struct in_addr *dest ) {
 	struct ipv4_miniroute *miniroute;
 
 	/* Find first usable route in routing table */
-- 
2.13.6




More information about the ipxe-devel mailing list