[ipxe-devel] [PATCH 10/11] ibft: IPv6 support
Hannes Reinecke
hare at suse.de
Thu May 7 09:25:05 UTC 2015
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 the net_device
from the routing tables, which also removes the long-standing
hack with 'last_used_netdev'.
Signed-off-by: Hannes Reinecke <hare at suse.de>
---
src/drivers/block/ibft.c | 212 ++++++++++++++++++++++++++++++++++----------
src/include/ipxe/ibft.h | 13 ++-
src/include/ipxe/ip.h | 1 +
src/include/ipxe/ipv6.h | 2 +
src/include/ipxe/settings.h | 2 +
src/net/ipv4.c | 2 +-
src/net/ipv6.c | 4 +-
7 files changed, 185 insertions(+), 51 deletions(-)
diff --git a/src/drivers/block/ibft.c b/src/drivers/block/ibft.c
index 6aabd76..47d0a4b 100644
--- a/src/drivers/block/ibft.c
+++ b/src/drivers/block/ibft.c
@@ -37,6 +37,8 @@ FILE_LICENCE ( BSD2 );
#include <ipxe/in.h>
#include <ipxe/netdevice.h>
#include <ipxe/ethernet.h>
+#include <ipxe/ip.h>
+#include <ipxe/ipv6.h>
#include <ipxe/vlan.h>
#include <ipxe/dhcp.h>
#include <ipxe/iscsi.h>
@@ -85,6 +87,13 @@ struct ibft_strings {
size_t len;
};
+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
*
@@ -94,8 +103,8 @@ struct ibft_strings {
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;
}
}
@@ -121,6 +130,41 @@ static void ibft_set_ipaddr_setting ( struct settings *settings,
}
/**
+ * 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;
+ }
+}
+
+/**
+ * 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
@@ -128,7 +172,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 );
}
/**
@@ -222,54 +269,43 @@ static const char * ibft_string ( struct ibft_strings *strings,
}
/**
- * Fill in NIC portion of iBFT
+ * Fill in IPv4 specific parts of the NIC portion of iBFT
*
* @v nic NIC portion of iBFT
- * @v strings iBFT string block descriptor
- * @v netdev Network device
- * @ret rc Return status code
+ * @v dest IPv4 target address
+ * @ret netdev Return network device
*/
-static int ibft_fill_nic ( struct ibft_nic *nic,
- struct ibft_strings *strings,
- struct net_device *netdev ) {
- struct ll_protocol *ll_protocol = netdev->ll_protocol;
+static struct net_device * ibft_fill_nic_ipv4 ( struct ibft_nic *nic,
+ struct sockaddr_in *dest ) {
+
+ struct ipv4_miniroute *route4 = ipv4_route(&dest->sin_addr);
+ struct settings *parent;
+ struct settings *origin;
struct in_addr netmask_addr = { 0 };
unsigned int netmask_count = 0;
- struct settings *parent = netdev_settings ( netdev );
- struct settings *origin;
- int rc;
-
- /* Fill in common header */
- nic->header.structure_id = IBFT_STRUCTURE_ID_NIC;
- nic->header.version = 1;
- nic->header.length = cpu_to_le16 ( sizeof ( *nic ) );
- nic->header.flags = ( IBFT_FL_NIC_BLOCK_VALID |
- IBFT_FL_NIC_FIRMWARE_BOOT_SELECTED );
/* Determine origin of IP address */
+ parent = netdev_settings(route4->netdev);
fetch_setting ( parent, &ip_setting, &origin, NULL, NULL, 0 );
+
nic->origin = ( ( origin == parent ) ?
IBFT_NIC_ORIGIN_MANUAL : IBFT_NIC_ORIGIN_DHCP );
DBG ( "iBFT NIC origin = %d\n", nic->origin );
/* Extract values from configuration settings */
- ibft_set_ipaddr_setting ( parent, &nic->ip_address, &ip_setting, 1 );
+ fetch_setting ( parent, &ip_setting, &origin, NULL, NULL, 0 );
+
+ ibft_set_ipaddr ( &nic->ip_address, route4->address );
DBG ( "iBFT NIC IP = %s\n", ibft_ipaddr ( &nic->ip_address ) );
- ibft_set_ipaddr_setting ( parent, &nic->gateway, &gateway_setting, 1 );
+ ibft_set_ipaddr ( &nic->gateway, route4->gateway );
DBG ( "iBFT NIC gateway = %s\n", ibft_ipaddr ( &nic->gateway ) );
ibft_set_ipaddr_setting ( NULL, &nic->dns[0], &dns_setting,
( sizeof ( nic->dns ) /
sizeof ( nic->dns[0] ) ) );
DBG ( "iBFT NIC DNS = %s", ibft_ipaddr ( &nic->dns[0] ) );
DBG ( ", %s\n", ibft_ipaddr ( &nic->dns[1] ) );
- if ( ( rc = ibft_set_string_setting ( NULL, strings, &nic->hostname,
- &hostname_setting ) ) != 0 )
- return rc;
- DBG ( "iBFT NIC hostname = %s\n",
- ibft_string ( strings, &nic->hostname ) );
-
/* Derive subnet mask prefix from subnet mask */
- fetch_ipv4_setting ( parent, &netmask_setting, &netmask_addr );
+ netmask_addr = route4->netmask;
while ( netmask_addr.s_addr ) {
if ( netmask_addr.s_addr & 0x1 )
netmask_count++;
@@ -277,10 +313,81 @@ static int ibft_fill_nic ( struct ibft_nic *nic,
}
nic->subnet_mask_prefix = netmask_count;
DBG ( "iBFT NIC subnet = /%d\n", nic->subnet_mask_prefix );
+ return route4->netdev;
+}
+
+/**
+ * Fill in IPv6 specific parts of the NIC portion of iBFT
+ *
+ * @v nic NIC portion of iBFT
+ * @v dest IPv6 destination address
+ * @ret rc Return network device
+ */
+static struct net_device * ibft_fill_nic_ipv6 ( struct ibft_nic *nic,
+ struct sockaddr_in6 * dest ) {
+ struct ipv6_miniroute *route6;
+ struct in6_addr *router = &dest->sin6_addr;
+ struct settings *parent;
+ struct settings *origin;
+
+ route6 = ipv6_route(dest->sin6_scope_id, &router);
+ parent = netdev_settings(route6->netdev);
+ fetch_setting ( parent, &ip6_setting, &origin, NULL, NULL, 0 );
+ nic->origin = ( ( origin == parent ) ?
+ IBFT_NIC_ORIGIN_MANUAL : IBFT_NIC_ORIGIN_DHCP );
+ DBG ( "iBFT NIC origin = %d\n", nic->origin );
+
+ /* Extract values from configuration settings */
+ ibft_set_ip6addr ( &nic->ip_address, route6->address );
+ DBG ( "iBFT NIC IP = %s\n", ibft_ipaddr ( &nic->ip_address ) );
+ ibft_set_ip6addr ( &nic->gateway, *router );
+ DBG ( "iBFT NIC gateway = %s\n", ibft_ipaddr ( &nic->gateway ) );
+ ibft_set_ip6addr_setting ( NULL, &nic->dns[0], &dns6_setting,
+ ( sizeof ( nic->dns ) /
+ sizeof ( nic->dns[0] ) ) );
+ DBG ( "iBFT NIC DNS = %s", ibft_ipaddr ( &nic->dns[0] ) );
+ DBG ( ", %s\n", ibft_ipaddr ( &nic->dns[1] ) );
+ nic->subnet_mask_prefix = route6->prefix_len;
+ DBG ( "iBFT NIC subnet = /%d\n", nic->subnet_mask_prefix );
+
+ return route6->netdev;
+}
+
+/**
+ * Fill in NIC portion of iBFT
+ *
+ * @v nic NIC portion of iBFT
+ * @v strings iBFT string block descriptor
+ * @v netdev Network device
+ * @ret rc Return status code
+ */
+static int ibft_fill_nic ( struct ibft_nic *nic,
+ struct ibft_strings *strings,
+ struct iscsi_session *iscsi ) {
+ struct sockaddr_in *sin_target =
+ ( struct sockaddr_in * ) &iscsi->target_sockaddr;
+ struct sockaddr_in6 *sin6_target =
+ ( struct sockaddr_in6 * ) &iscsi->target_sockaddr;
+ struct net_device *netdev;
+ struct ll_protocol *ll_protocol;
+ int rc;
+
+ /* Fill in common header */
+ nic->header.structure_id = IBFT_STRUCTURE_ID_NIC;
+ nic->header.version = 1;
+ nic->header.length = cpu_to_le16 ( sizeof ( *nic ) );
+ nic->header.flags = ( IBFT_FL_NIC_BLOCK_VALID |
+ IBFT_FL_NIC_FIRMWARE_BOOT_SELECTED );
+
+ if (sin_target->sin_family == AF_INET)
+ netdev = ibft_fill_nic_ipv4( nic, sin_target );
+ else
+ netdev = ibft_fill_nic_ipv6( nic, sin6_target );
/* Extract values from net-device configuration */
nic->vlan = cpu_to_le16 ( vlan_tag ( netdev ) );
DBG ( "iBFT NIC VLAN = %02x\n", le16_to_cpu ( nic->vlan ) );
+ ll_protocol = netdev->ll_protocol;
if ( ( rc = ll_protocol->eth_addr ( netdev->ll_addr,
nic->mac_address ) ) != 0 ) {
DBG ( "Could not determine iBFT MAC: %s\n", strerror ( rc ) );
@@ -290,6 +397,12 @@ static int ibft_fill_nic ( struct ibft_nic *nic,
nic->pci_bus_dev_func = cpu_to_le16 ( netdev->dev->desc.location );
DBG ( "iBFT NIC PCI = %04x\n", le16_to_cpu ( nic->pci_bus_dev_func ) );
+ if ( ( rc = ibft_set_string_setting ( NULL, strings, &nic->hostname,
+ &hostname_setting ) ) != 0 )
+ return rc;
+ DBG ( "iBFT NIC hostname = %s\n",
+ ibft_string ( strings, &nic->hostname ) );
+
return 0;
}
@@ -404,8 +517,16 @@ static int ibft_fill_target ( struct ibft_target *target,
struct iscsi_session *iscsi ) {
struct sockaddr_in *sin_target =
( struct sockaddr_in * ) &iscsi->target_sockaddr;
+ struct sockaddr_in6 *sin6_target =
+ ( struct sockaddr_in6 * ) &iscsi->target_sockaddr;
int rc;
+ if (sin6_target->sin6_family != AF_INET6 &&
+ sin_target->sin_family != AF_INET) {
+ DBG ( "iBFT invalid IP address type %d\n",
+ sin6_target->sin6_family );
+ return -EPROTONOSUPPORT;
+ }
/* Fill in common header */
target->header.structure_id = IBFT_STRUCTURE_ID_TARGET;
target->header.version = 1;
@@ -414,10 +535,19 @@ 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 );
- DBG ( "iBFT target IP = %s\n", ibft_ipaddr ( &target->ip_address ) );
- target->socket = cpu_to_le16 ( ntohs ( sin_target->sin_port ) );
- DBG ( "iBFT target port = %d\n", target->socket );
+ if (sin_target->sin_family == AF_INET) {
+ ibft_set_ipaddr ( &target->ip_address, sin_target->sin_addr );
+ DBG ( "iBFT target IP = %s\n",
+ ibft_ipaddr ( &target->ip_address ) );
+ target->socket = cpu_to_le16 ( ntohs ( sin_target->sin_port ) );
+ DBG ( "iBFT target port = %d\n", target->socket );
+ } else {
+ ibft_set_ip6addr ( &target->ip_address, sin6_target->sin6_addr );
+ DBG ( "iBFT target IP = %s\n",
+ ibft_ipaddr ( &target->ip_address ) );
+ target->socket = cpu_to_le16 ( ntohs ( sin6_target->sin6_port ) );
+ DBG ( "iBFT target port = %d\n", target->socket );
+ }
memcpy ( &target->boot_lun, &iscsi->lun, sizeof ( target->boot_lun ) );
DBG ( "iBFT target boot LUN = " SCSI_LUN_FORMAT "\n",
SCSI_LUN_DATA ( target->boot_lun ) );
@@ -453,19 +583,8 @@ int ibft_describe ( struct iscsi_session *iscsi,
.offset = offsetof ( typeof ( *ibft ), strings ),
.len = len,
};
- struct net_device *netdev;
int rc;
- /* Ugly hack. Now that we have a generic interface mechanism
- * that can support ioctls, we can potentially eliminate this.
- */
- netdev = last_opened_netdev();
- if ( ! netdev ) {
- DBGC ( iscsi, "iSCSI %p cannot guess network device\n",
- iscsi );
- return -ENODEV;
- }
-
/* Fill in ACPI header */
ibft->table.acpi.signature = cpu_to_le32 ( IBFT_SIG );
ibft->table.acpi.length = cpu_to_le32 ( len );
@@ -484,7 +603,8 @@ int ibft_describe ( struct iscsi_session *iscsi,
cpu_to_le16 ( offsetof ( typeof ( *ibft ), target ) );
/* Fill in NIC, Initiator and Target blocks */
- if ( ( rc = ibft_fill_nic ( &ibft->nic, &strings, netdev ) ) != 0 )
+ if ( ( rc = ibft_fill_nic ( &ibft->nic, &strings,
+ iscsi ) ) != 0 )
return rc;
if ( ( rc = ibft_fill_initiator ( &ibft->initiator, &strings,
iscsi ) ) != 0 )
diff --git a/src/include/ipxe/ibft.h b/src/include/ipxe/ibft.h
index 35f1510..756197a 100644
--- a/src/include/ipxe/ibft.h
+++ b/src/include/ipxe/ibft.h
@@ -63,14 +63,23 @@ struct ibft_string {
ibft_off_t offset;
} __attribute__ (( packed ));
-/** An IP address within the iBFT */
-struct ibft_ipaddr {
+/** iBFT IPv4 address representation */
+struct ibft_inaddr {
/** Reserved; must be zero */
uint16_t zeroes[5];
/** Must be 0xffff if IPv4 address is present, otherwise zero */
uint16_t ones;
/** The IPv4 address, or zero if not present */
struct in_addr in;
+};
+
+/** An IP address within the iBFT */
+struct ibft_ipaddr {
+ union {
+ struct ibft_inaddr in;
+ struct in6_addr in6;
+ uint8_t raw[16];
+ };
} __attribute__ (( packed ));
/**
diff --git a/src/include/ipxe/ip.h b/src/include/ipxe/ip.h
index 285be6d..bbec8b7 100644
--- a/src/include/ipxe/ip.h
+++ b/src/include/ipxe/ip.h
@@ -75,6 +75,7 @@ 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 ( 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/ipv6.h b/src/include/ipxe/ipv6.h
index dd71df5..d36b9f9 100644
--- a/src/include/ipxe/ipv6.h
+++ b/src/include/ipxe/ipv6.h
@@ -255,5 +255,7 @@ extern int format_ipv6_setting ( const struct setting_type *type,
extern int ipv6_ll_route ( struct net_device *netdev );
struct ipv6_miniroute * ipv6_miniroute ( struct net_device *netdev,
struct in6_addr *address );
+extern struct ipv6_miniroute * ipv6_route ( unsigned int scope_id,
+ struct in6_addr **dest );
#endif /* _IPXE_IPV6_H */
diff --git a/src/include/ipxe/settings.h b/src/include/ipxe/settings.h
index 8249ec0..ac46518 100644
--- a/src/include/ipxe/settings.h
+++ b/src/include/ipxe/settings.h
@@ -431,6 +431,8 @@ dns_setting __setting ( SETTING_IP_EXTRA, dns );
extern const struct setting
ip6_setting __setting ( SETTING_IP_EXTRA, ip6 );
extern const struct setting
+dns6_setting __setting ( SETTING_IP_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 ed22c4d..adba96f 100644
--- a/src/net/ipv4.c
+++ b/src/net/ipv4.c
@@ -146,7 +146,7 @@ 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 ( struct in_addr *dest ) {
+struct ipv4_miniroute * ipv4_route ( struct in_addr *dest ) {
struct ipv4_miniroute *miniroute;
int local;
int has_gw;
diff --git a/src/net/ipv6.c b/src/net/ipv6.c
index 7298372..9f82fe4 100644
--- a/src/net/ipv6.c
+++ b/src/net/ipv6.c
@@ -311,8 +311,8 @@ int ipv6_set_address ( struct net_device *netdev, struct in6_addr *address ) {
* @ret dest Next hop destination address
* @ret miniroute Routing table entry to use, or NULL if no route
*/
-static struct ipv6_miniroute * ipv6_route ( unsigned int scope_id,
- struct in6_addr **dest ) {
+struct ipv6_miniroute * ipv6_route ( unsigned int scope_id,
+ struct in6_addr **dest ) {
struct ipv6_miniroute *miniroute;
/* Find first usable route in routing table */
--
1.8.4.5
More information about the ipxe-devel
mailing list