aboutsummaryrefslogtreecommitdiffstats
path: root/gpxe/src/net
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2009-02-17 20:17:17 -0800
committerH. Peter Anvin <hpa@zytor.com>2009-02-17 20:17:17 -0800
commitd0c6656a62113b913948361779d6298fe76f6e61 (patch)
treeefa2541a1abae4760717c6db421ea818114ab6f7 /gpxe/src/net
parent85b92a462dab7ce36c48614ea18314f8fc83ca9c (diff)
downloadsyslinux.git-d0c6656a62113b913948361779d6298fe76f6e61.tar.gz
syslinux.git-d0c6656a62113b913948361779d6298fe76f6e61.tar.xz
syslinux.git-d0c6656a62113b913948361779d6298fe76f6e61.zip
Update gPXE to version 0.9.6+ 277b84c6e7d49f3cf01c855007f591de8c7cb75f
Update gPXE to version 0.9.6+, from commit 277b84c6e7d49f3cf01c855007f591de8c7cb75f in the main gPXE repository. The only differences is src/config/general.h which has a few protocols added, and src/arch/i386/prefix/boot1a.S which was called boot1a.s in the upstream repository.
Diffstat (limited to 'gpxe/src/net')
-rw-r--r--gpxe/src/net/aoe.c209
-rw-r--r--gpxe/src/net/arp.c4
-rw-r--r--gpxe/src/net/dhcppkt.c78
-rw-r--r--gpxe/src/net/ethernet.c61
-rw-r--r--gpxe/src/net/fakedhcp.c43
-rw-r--r--gpxe/src/net/icmp.c101
-rw-r--r--gpxe/src/net/icmpv6.c2
-rw-r--r--gpxe/src/net/infiniband.c351
-rw-r--r--gpxe/src/net/ipv4.c27
-rw-r--r--gpxe/src/net/ipv6.c1
-rw-r--r--gpxe/src/net/netdevice.c88
-rw-r--r--gpxe/src/net/retry.c12
-rw-r--r--gpxe/src/net/tcp.c23
-rw-r--r--gpxe/src/net/tcp/ftp.c63
-rw-r--r--gpxe/src/net/tcp/http.c45
-rw-r--r--gpxe/src/net/tcp/iscsi.c2
-rw-r--r--gpxe/src/net/tcpip.c9
-rw-r--r--gpxe/src/net/tls.c214
-rw-r--r--gpxe/src/net/udp.c38
-rw-r--r--gpxe/src/net/udp/dhcp.c1431
-rw-r--r--gpxe/src/net/udp/dns.c76
-rw-r--r--gpxe/src/net/udp/slam.c4
-rw-r--r--gpxe/src/net/udp/tftp.c67
23 files changed, 1949 insertions, 1000 deletions
diff --git a/gpxe/src/net/aoe.c b/gpxe/src/net/aoe.c
index e3f84e5a..08887fe0 100644
--- a/gpxe/src/net/aoe.c
+++ b/gpxe/src/net/aoe.c
@@ -64,8 +64,13 @@ static void aoe_free ( struct refcnt *refcnt ) {
static void aoe_done ( struct aoe_session *aoe, int rc ) {
/* Record overall command status */
- aoe->command->cb.cmd_stat = aoe->status;
- aoe->command = NULL;
+ if ( aoe->command ) {
+ aoe->command->cb.cmd_stat = aoe->status;
+ aoe->command = NULL;
+ }
+
+ /* Stop retransmission timer */
+ stop_timer ( &aoe->timer );
/* Mark operation as complete */
aoe->rc = rc;
@@ -84,9 +89,11 @@ static int aoe_send_command ( struct aoe_session *aoe ) {
struct ata_command *command = aoe->command;
struct io_buffer *iobuf;
struct aoehdr *aoehdr;
- struct aoecmd *aoecmd;
+ union aoecmd *aoecmd;
+ struct aoeata *aoeata;
unsigned int count;
unsigned int data_out_len;
+ unsigned int aoecmdlen;
/* Fail immediately if we have no netdev to send on */
if ( ! aoe->netdev ) {
@@ -94,46 +101,81 @@ static int aoe_send_command ( struct aoe_session *aoe ) {
return -ENETUNREACH;
}
+ /* If we are transmitting anything that requires a response,
+ * start the retransmission timer. Do this before attempting
+ * to allocate the I/O buffer, in case allocation itself
+ * fails.
+ */
+ start_timer ( &aoe->timer );
+
/* Calculate count and data_out_len for this subcommand */
- count = command->cb.count.native;
- if ( count > AOE_MAX_COUNT )
- count = AOE_MAX_COUNT;
- data_out_len = ( command->data_out ? ( count * ATA_SECTOR_SIZE ) : 0 );
+ switch ( aoe->aoe_cmd_type ) {
+ case AOE_CMD_ATA:
+ count = command->cb.count.native;
+ if ( count > AOE_MAX_COUNT )
+ count = AOE_MAX_COUNT;
+ data_out_len = ( command->data_out ?
+ ( count * ATA_SECTOR_SIZE ) : 0 );
+ aoecmdlen = sizeof ( aoecmd->ata );
+ break;
+ case AOE_CMD_CONFIG:
+ count = 0;
+ data_out_len = 0;
+ aoecmdlen = sizeof ( aoecmd->cfg );
+ break;
+ default:
+ return -ENOTSUP;
+ }
/* Create outgoing I/O buffer */
- iobuf = alloc_iob ( ETH_HLEN + sizeof ( *aoehdr ) + sizeof ( *aoecmd ) +
- data_out_len );
+ iobuf = alloc_iob ( ETH_HLEN + sizeof ( *aoehdr ) +
+ aoecmdlen + data_out_len );
+
if ( ! iobuf )
return -ENOMEM;
iob_reserve ( iobuf, ETH_HLEN );
aoehdr = iob_put ( iobuf, sizeof ( *aoehdr ) );
- aoecmd = iob_put ( iobuf, sizeof ( *aoecmd ) );
- memset ( aoehdr, 0, ( sizeof ( *aoehdr ) + sizeof ( *aoecmd ) ) );
+ aoecmd = iob_put ( iobuf, aoecmdlen );
+ memset ( aoehdr, 0, ( sizeof ( *aoehdr ) + aoecmdlen ) );
/* Fill AoE header */
aoehdr->ver_flags = AOE_VERSION;
aoehdr->major = htons ( aoe->major );
aoehdr->minor = aoe->minor;
+ aoehdr->command = aoe->aoe_cmd_type;
aoehdr->tag = htonl ( ++aoe->tag );
- /* Fill AoE command */
- linker_assert ( AOE_FL_DEV_HEAD == ATA_DEV_SLAVE, __fix_ata_h__ );
- aoecmd->aflags = ( ( command->cb.lba48 ? AOE_FL_EXTENDED : 0 ) |
- ( command->cb.device & ATA_DEV_SLAVE ) |
- ( data_out_len ? AOE_FL_WRITE : 0 ) );
- aoecmd->err_feat = command->cb.err_feat.bytes.cur;
- aoecmd->count = count;
- aoecmd->cmd_stat = command->cb.cmd_stat;
- aoecmd->lba.u64 = cpu_to_le64 ( command->cb.lba.native );
- if ( ! command->cb.lba48 )
- aoecmd->lba.bytes[3] |= ( command->cb.device & ATA_DEV_MASK );
-
- /* Fill data payload */
- copy_from_user ( iob_put ( iobuf, data_out_len ), command->data_out,
- aoe->command_offset, data_out_len );
+ /* Fill AoE payload */
+ switch ( aoe->aoe_cmd_type ) {
+ case AOE_CMD_ATA:
+ /* Fill AoE command */
+ aoeata = &aoecmd->ata;
+ linker_assert ( AOE_FL_DEV_HEAD == ATA_DEV_SLAVE,
+ __fix_ata_h__ );
+ aoeata->aflags = ( ( command->cb.lba48 ? AOE_FL_EXTENDED : 0 )|
+ ( command->cb.device & ATA_DEV_SLAVE ) |
+ ( data_out_len ? AOE_FL_WRITE : 0 ) );
+ aoeata->err_feat = command->cb.err_feat.bytes.cur;
+ aoeata->count = count;
+ aoeata->cmd_stat = command->cb.cmd_stat;
+ aoeata->lba.u64 = cpu_to_le64 ( command->cb.lba.native );
+ if ( ! command->cb.lba48 )
+ aoeata->lba.bytes[3] |=
+ ( command->cb.device & ATA_DEV_MASK );
+
+ /* Fill data payload */
+ copy_from_user ( iob_put ( iobuf, data_out_len ),
+ command->data_out, aoe->command_offset,
+ data_out_len );
+ break;
+ case AOE_CMD_CONFIG:
+ /* Nothing to do */
+ break;
+ default:
+ assert ( 0 );
+ }
/* Send packet */
- start_timer ( &aoe->timer );
return net_tx ( iobuf, aoe->netdev, &aoe_protocol, aoe->target );
}
@@ -155,38 +197,46 @@ static void aoe_timer_expired ( struct retry_timer *timer, int fail ) {
}
/**
- * Handle AoE response
+ * Handle AoE configuration command response
*
* @v aoe AoE session
- * @v aoehdr AoE header
+ * @v ll_source Link-layer source address
* @ret rc Return status code
*/
-static int aoe_rx_response ( struct aoe_session *aoe, struct aoehdr *aoehdr,
- unsigned int len ) {
- struct aoecmd *aoecmd = aoehdr->arg.command;
+static int aoe_rx_cfg ( struct aoe_session *aoe, const void *ll_source ) {
+
+ /* Record target MAC address */
+ memcpy ( aoe->target, ll_source, sizeof ( aoe->target ) );
+ DBGC ( aoe, "AoE %p target MAC address %s\n",
+ aoe, eth_ntoa ( aoe->target ) );
+
+ /* Mark config request as complete */
+ aoe_done ( aoe, 0 );
+
+ return 0;
+}
+
+/**
+ * Handle AoE ATA command response
+ *
+ * @v aoe AoE session
+ * @v aoeata AoE ATA command
+ * @v len Length of AoE ATA command
+ * @ret rc Return status code
+ */
+static int aoe_rx_ata ( struct aoe_session *aoe, struct aoeata *aoeata,
+ size_t len ) {
struct ata_command *command = aoe->command;
unsigned int rx_data_len;
unsigned int count;
unsigned int data_len;
-
+
/* Sanity check */
- if ( len < ( sizeof ( *aoehdr ) + sizeof ( *aoecmd ) ) ) {
+ if ( len < sizeof ( *aoeata ) ) {
/* Ignore packet; allow timer to trigger retransmit */
return -EINVAL;
}
- rx_data_len = ( len - sizeof ( *aoehdr ) - sizeof ( *aoecmd ) );
-
- /* Stop retry timer. After this point, every code path must
- * either terminate the AoE operation via aoe_done(), or
- * transmit a new packet.
- */
- stop_timer ( &aoe->timer );
-
- /* Check for fatal errors */
- if ( aoehdr->ver_flags & AOE_FL_ERROR ) {
- aoe_done ( aoe, -EIO );
- return 0;
- }
+ rx_data_len = ( len - sizeof ( *aoeata ) );
/* Calculate count and data_len for this subcommand */
count = command->cb.count.native;
@@ -195,14 +245,14 @@ static int aoe_rx_response ( struct aoe_session *aoe, struct aoehdr *aoehdr,
data_len = count * ATA_SECTOR_SIZE;
/* Merge into overall ATA status */
- aoe->status |= aoecmd->cmd_stat;
+ aoe->status |= aoeata->cmd_stat;
/* Copy data payload */
if ( command->data_in ) {
if ( rx_data_len > data_len )
rx_data_len = data_len;
copy_to_user ( command->data_in, aoe->command_offset,
- aoecmd->data, rx_data_len );
+ aoeata->data, rx_data_len );
}
/* Update ATA command and offset */
@@ -217,6 +267,7 @@ static int aoe_rx_response ( struct aoe_session *aoe, struct aoehdr *aoehdr,
}
/* Transmit next portion of request */
+ stop_timer ( &aoe->timer );
aoe_send_command ( aoe );
return 0;
@@ -231,15 +282,15 @@ static int aoe_rx_response ( struct aoe_session *aoe, struct aoehdr *aoehdr,
* @ret rc Return status code
*
*/
-static int aoe_rx ( struct io_buffer *iobuf, struct net_device *netdev __unused,
+static int aoe_rx ( struct io_buffer *iobuf,
+ struct net_device *netdev __unused,
const void *ll_source ) {
struct aoehdr *aoehdr = iobuf->data;
- unsigned int len = iob_len ( iobuf );
struct aoe_session *aoe;
int rc = 0;
/* Sanity checks */
- if ( len < sizeof ( *aoehdr ) ) {
+ if ( iob_len ( iobuf ) < sizeof ( *aoehdr ) ) {
rc = -EINVAL;
goto done;
}
@@ -251,6 +302,7 @@ static int aoe_rx ( struct io_buffer *iobuf, struct net_device *netdev __unused,
/* Ignore AoE requests that we happen to see */
goto done;
}
+ iob_pull ( iobuf, sizeof ( *aoehdr ) );
/* Demultiplex amongst active AoE sessions */
list_for_each_entry ( aoe, &aoe_sessions, list ) {
@@ -260,8 +312,22 @@ static int aoe_rx ( struct io_buffer *iobuf, struct net_device *netdev __unused,
continue;
if ( ntohl ( aoehdr->tag ) != aoe->tag )
continue;
- memcpy ( aoe->target, ll_source, sizeof ( aoe->target ) );
- rc = aoe_rx_response ( aoe, aoehdr, len );
+ if ( aoehdr->ver_flags & AOE_FL_ERROR ) {
+ aoe_done ( aoe, -EIO );
+ break;
+ }
+ switch ( aoehdr->command ) {
+ case AOE_CMD_ATA:
+ rc = aoe_rx_ata ( aoe, iobuf->data, iob_len ( iobuf ));
+ break;
+ case AOE_CMD_CONFIG:
+ rc = aoe_rx_cfg ( aoe, ll_source );
+ break;
+ default:
+ DBGC ( aoe, "AoE %p ignoring command %02x\n",
+ aoe, aoehdr->command );
+ break;
+ }
break;
}
@@ -293,6 +359,32 @@ static int aoe_command ( struct ata_device *ata,
aoe->command = command;
aoe->status = 0;
aoe->command_offset = 0;
+ aoe->aoe_cmd_type = AOE_CMD_ATA;
+
+ aoe_send_command ( aoe );
+
+ aoe->rc = -EINPROGRESS;
+ while ( aoe->rc == -EINPROGRESS )
+ step();
+ rc = aoe->rc;
+
+ return rc;
+}
+
+
+/**
+ * Issue AoE config query for AoE target discovery
+ *
+ * @v aoe AoE session
+ * @ret rc Return status code
+ */
+static int aoe_discover ( struct aoe_session *aoe ) {
+ int rc;
+
+ aoe->status = 0;
+ aoe->aoe_cmd_type = AOE_CMD_CONFIG;
+ aoe->command = NULL;
+
aoe_send_command ( aoe );
aoe->rc = -EINPROGRESS;
@@ -367,6 +459,15 @@ int aoe_attach ( struct ata_device *ata, struct net_device *netdev,
ata->backend = ref_get ( &aoe->refcnt );
ata->command = aoe_command;
list_add ( &aoe->list, &aoe_sessions );
+
+ /* Send discovery packet to find the target MAC address.
+ * Ideally, this ought to be done asynchronously, but the
+ * block device interface does not yet support asynchronous
+ * operation.
+ */
+ if ( ( rc = aoe_discover( aoe ) ) != 0 )
+ goto err;
+
return 0;
err:
diff --git a/gpxe/src/net/arp.c b/gpxe/src/net/arp.c
index 011d4fef..ba9ebf48 100644
--- a/gpxe/src/net/arp.c
+++ b/gpxe/src/net/arp.c
@@ -265,8 +265,8 @@ static int arp_rx ( struct io_buffer *iobuf, struct net_device *netdev,
memcpy ( arp_sender_ha ( arphdr ), netdev->ll_addr, arphdr->ar_hln );
/* Send reply */
- net_tx ( iobuf, netdev, &arp_protocol, arp_target_ha (arphdr ) );
- iobuf = NULL;
+ net_tx ( iob_disown ( iobuf ), netdev, &arp_protocol,
+ arp_target_ha ( arphdr ) );
done:
free_iob ( iobuf );
diff --git a/gpxe/src/net/dhcppkt.c b/gpxe/src/net/dhcppkt.c
index c8bf215b..1f2d373c 100644
--- a/gpxe/src/net/dhcppkt.c
+++ b/gpxe/src/net/dhcppkt.c
@@ -32,6 +32,12 @@
*
*/
+/****************************************************************************
+ *
+ * DHCP packet raw interface
+ *
+ */
+
/**
* Calculate used length of an IPv4 field within a DHCP packet
*
@@ -193,21 +199,79 @@ int dhcppkt_fetch ( struct dhcp_packet *dhcppkt, unsigned int tag,
return dhcpopt_fetch ( &dhcppkt->options, tag, data, len );
}
+/****************************************************************************
+ *
+ * DHCP packet settings interface
+ *
+ */
+
+/**
+ * Store value of DHCP setting
+ *
+ * @v settings Settings block
+ * @v setting Setting to store
+ * @v data Setting data, or NULL to clear setting
+ * @v len Length of setting data
+ * @ret rc Return status code
+ */
+static int dhcppkt_settings_store ( struct settings *settings,
+ struct setting *setting,
+ const void *data, size_t len ) {
+ struct dhcp_packet *dhcppkt =
+ container_of ( settings, struct dhcp_packet, settings );
+
+ return dhcppkt_store ( dhcppkt, setting->tag, data, len );
+}
+
+/**
+ * Fetch value of DHCP setting
+ *
+ * @v settings Settings block, or NULL to search all blocks
+ * @v setting Setting to fetch
+ * @v data Buffer to fill with setting data
+ * @v len Length of buffer
+ * @ret len Length of setting data, or negative error
+ */
+static int dhcppkt_settings_fetch ( struct settings *settings,
+ struct setting *setting,
+ void *data, size_t len ) {
+ struct dhcp_packet *dhcppkt =
+ container_of ( settings, struct dhcp_packet, settings );
+
+ return dhcppkt_fetch ( dhcppkt, setting->tag, data, len );
+}
+
+/** DHCP settings operations */
+static struct settings_operations dhcppkt_settings_operations = {
+ .store = dhcppkt_settings_store,
+ .fetch = dhcppkt_settings_fetch,
+};
+
+/****************************************************************************
+ *
+ * Constructor
+ *
+ */
+
/**
- * Initialise prepopulated DHCP packet
+ * Initialise DHCP packet
*
- * @v dhcppkt Uninitialised DHCP packet
- * @v data Memory for DHCP packet data
- * @v max_len Length of memory for DHCP packet data
+ * @v dhcppkt DHCP packet structure to fill in
+ * @v data DHCP packet raw data
+ * @v max_len Length of raw data buffer
*
- * The memory content must already be filled with valid DHCP options.
- * A zeroed block counts as a block of valid DHCP options.
+ * Initialise a DHCP packet structure from a data buffer containing a
+ * DHCP packet.
*/
-void dhcppkt_init ( struct dhcp_packet *dhcppkt, void *data, size_t len ) {
+void dhcppkt_init ( struct dhcp_packet *dhcppkt, struct dhcphdr *data,
+ size_t len ) {
dhcppkt->dhcphdr = data;
dhcppkt->max_len = len;
dhcpopt_init ( &dhcppkt->options, &dhcppkt->dhcphdr->options,
( len - offsetof ( struct dhcphdr, options ) ) );
dhcppkt->len = ( offsetof ( struct dhcphdr, options ) +
dhcppkt->options.len );
+ settings_init ( &dhcppkt->settings,
+ &dhcppkt_settings_operations, &dhcppkt->refcnt,
+ DHCP_SETTINGS_NAME, 0 );
}
diff --git a/gpxe/src/net/ethernet.c b/gpxe/src/net/ethernet.c
index 7b1c496f..b16135a9 100644
--- a/gpxe/src/net/ethernet.c
+++ b/gpxe/src/net/ethernet.c
@@ -24,6 +24,7 @@
#include <assert.h>
#include <gpxe/if_arp.h>
#include <gpxe/if_ether.h>
+#include <gpxe/in.h>
#include <gpxe/netdevice.h>
#include <gpxe/iobuf.h>
#include <gpxe/ethernet.h>
@@ -41,19 +42,19 @@ static uint8_t eth_broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
* Add Ethernet link-layer header
*
* @v iobuf I/O buffer
- * @v netdev Network device
- * @v net_protocol Network-layer protocol
* @v ll_dest Link-layer destination address
+ * @v ll_source Source link-layer address
+ * @v net_proto Network-layer protocol, in network-byte order
+ * @ret rc Return status code
*/
-static int eth_push ( struct io_buffer *iobuf, struct net_device *netdev,
- struct net_protocol *net_protocol,
- const void *ll_dest ) {
+static int eth_push ( struct io_buffer *iobuf, const void *ll_dest,
+ const void *ll_source, uint16_t net_proto ) {
struct ethhdr *ethhdr = iob_push ( iobuf, sizeof ( *ethhdr ) );
/* Build Ethernet header */
memcpy ( ethhdr->h_dest, ll_dest, ETH_ALEN );
- memcpy ( ethhdr->h_source, netdev->ll_addr, ETH_ALEN );
- ethhdr->h_protocol = net_protocol->net_proto;
+ memcpy ( ethhdr->h_source, ll_source, ETH_ALEN );
+ ethhdr->h_protocol = net_proto;
return 0;
}
@@ -62,14 +63,13 @@ static int eth_push ( struct io_buffer *iobuf, struct net_device *netdev,
* Remove Ethernet link-layer header
*
* @v iobuf I/O buffer
- * @v netdev Network device
- * @v net_proto Network-layer protocol, in network-byte order
- * @v ll_source Source link-layer address
+ * @ret ll_dest Link-layer destination address
+ * @ret ll_source Source link-layer address
+ * @ret net_proto Network-layer protocol, in network-byte order
* @ret rc Return status code
*/
-static int eth_pull ( struct io_buffer *iobuf,
- struct net_device *netdev __unused,
- uint16_t *net_proto, const void **ll_source ) {
+static int eth_pull ( struct io_buffer *iobuf, const void **ll_dest,
+ const void **ll_source, uint16_t *net_proto ) {
struct ethhdr *ethhdr = iobuf->data;
/* Sanity check */
@@ -83,8 +83,9 @@ static int eth_pull ( struct io_buffer *iobuf,
iob_pull ( iobuf, sizeof ( *ethhdr ) );
/* Fill in required fields */
- *net_proto = ethhdr->h_protocol;
+ *ll_dest = ethhdr->h_dest;
*ll_source = ethhdr->h_source;
+ *net_proto = ethhdr->h_protocol;
return 0;
}
@@ -92,8 +93,8 @@ static int eth_pull ( struct io_buffer *iobuf,
/**
* Transcribe Ethernet address
*
- * @v ll_addr Link-layer address
- * @ret string Link-layer address in human-readable format
+ * @v ll_addr Link-layer address
+ * @ret string Link-layer address in human-readable format
*/
const char * eth_ntoa ( const void *ll_addr ) {
static char buf[18]; /* "00:00:00:00:00:00" */
@@ -105,6 +106,33 @@ const char * eth_ntoa ( const void *ll_addr ) {
return buf;
}
+/**
+ * Hash multicast address
+ *
+ * @v af Address family
+ * @v net_addr Network-layer address
+ * @v ll_addr Link-layer address to fill in
+ * @ret rc Return status code
+ */
+static int eth_mc_hash ( unsigned int af, const void *net_addr,
+ void *ll_addr ) {
+ const uint8_t *net_addr_bytes = net_addr;
+ uint8_t *ll_addr_bytes = ll_addr;
+
+ switch ( af ) {
+ case AF_INET:
+ ll_addr_bytes[0] = 0x01;
+ ll_addr_bytes[1] = 0x00;
+ ll_addr_bytes[2] = 0x5e;
+ ll_addr_bytes[3] = net_addr_bytes[1] & 0x7f;
+ ll_addr_bytes[4] = net_addr_bytes[2];
+ ll_addr_bytes[5] = net_addr_bytes[3];
+ return 0;
+ default:
+ return -ENOTSUP;
+ }
+}
+
/** Ethernet protocol */
struct ll_protocol ethernet_protocol __ll_protocol = {
.name = "Ethernet",
@@ -115,4 +143,5 @@ struct ll_protocol ethernet_protocol __ll_protocol = {
.push = eth_push,
.pull = eth_pull,
.ntoa = eth_ntoa,
+ .mc_hash = eth_mc_hash,
};
diff --git a/gpxe/src/net/fakedhcp.c b/gpxe/src/net/fakedhcp.c
index 60264756..0518789c 100644
--- a/gpxe/src/net/fakedhcp.c
+++ b/gpxe/src/net/fakedhcp.c
@@ -111,8 +111,8 @@ int create_fakedhcpdiscover ( struct net_device *netdev,
struct in_addr ciaddr = { 0 };
int rc;
- if ( ( rc = dhcp_create_request ( &dhcppkt, netdev, ciaddr, NULL, data,
- max_len ) ) != 0 ) {
+ if ( ( rc = dhcp_create_request ( &dhcppkt, netdev, DHCPDISCOVER,
+ ciaddr, data, max_len ) ) != 0 ) {
DBG ( "Could not create DHCPDISCOVER: %s\n",
strerror ( rc ) );
return rc;
@@ -137,7 +137,7 @@ int create_fakedhcpack ( struct net_device *netdev,
int rc;
/* Create base DHCPACK packet */
- if ( ( rc = dhcp_create_packet ( &dhcppkt, netdev, DHCPACK, NULL,
+ if ( ( rc = dhcp_create_packet ( &dhcppkt, netdev, DHCPACK, NULL, 0,
data, max_len ) ) != 0 ) {
DBG ( "Could not create DHCPACK: %s\n", strerror ( rc ) );
return rc;
@@ -163,7 +163,7 @@ int create_fakedhcpack ( struct net_device *netdev,
}
/**
- * Create ProxyDHCPACK packet
+ * Create fake PXE Boot Server ACK packet
*
* @v netdev Network device
* @v data Buffer for DHCP packet
@@ -172,30 +172,41 @@ int create_fakedhcpack ( struct net_device *netdev,
*
* Used by external code.
*/
-int create_fakeproxydhcpack ( struct net_device *netdev,
- void *data, size_t max_len ) {
+int create_fakepxebsack ( struct net_device *netdev,
+ void *data, size_t max_len ) {
struct dhcp_packet dhcppkt;
- struct settings *settings;
+ struct settings *proxy_settings;
+ struct settings *pxebs_settings;
int rc;
- /* Identify ProxyDHCP settings */
- settings = find_settings ( PROXYDHCP_SETTINGS_NAME );
-
- /* No ProxyDHCP settings => use normal DHCPACK */
- if ( ! settings )
+ /* Identify available settings */
+ proxy_settings = find_settings ( PROXYDHCP_SETTINGS_NAME );
+ pxebs_settings = find_settings ( PXEBS_SETTINGS_NAME );
+ if ( ( ! proxy_settings ) && ( ! pxebs_settings ) ) {
+ /* No PXE boot server; return the regular DHCPACK */
return create_fakedhcpack ( netdev, data, max_len );
+ }
/* Create base DHCPACK packet */
- if ( ( rc = dhcp_create_packet ( &dhcppkt, netdev, DHCPACK, NULL,
+ if ( ( rc = dhcp_create_packet ( &dhcppkt, netdev, DHCPACK, NULL, 0,
data, max_len ) ) != 0 ) {
- DBG ( "Could not create ProxyDHCPACK: %s\n",
+ DBG ( "Could not create PXE BS ACK: %s\n",
strerror ( rc ) );
return rc;
}
/* Merge in ProxyDHCP options */
- if ( ( rc = copy_settings ( &dhcppkt, settings ) ) != 0 ) {
- DBG ( "Could not set ProxyDHCPACK settings: %s\n",
+ if ( proxy_settings &&
+ ( ( rc = copy_settings ( &dhcppkt, proxy_settings ) ) != 0 ) ) {
+ DBG ( "Could not copy ProxyDHCP settings: %s\n",
+ strerror ( rc ) );
+ return rc;
+ }
+
+ /* Merge in BootServerDHCP options, if present */
+ if ( pxebs_settings &&
+ ( ( rc = copy_settings ( &dhcppkt, pxebs_settings ) ) != 0 ) ) {
+ DBG ( "Could not copy PXE BS settings: %s\n",
strerror ( rc ) );
return rc;
}
diff --git a/gpxe/src/net/icmp.c b/gpxe/src/net/icmp.c
new file mode 100644
index 00000000..3e45c1f6
--- /dev/null
+++ b/gpxe/src/net/icmp.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2009 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * 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.
+ */
+
+#include <string.h>
+#include <errno.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/in.h>
+#include <gpxe/tcpip.h>
+#include <gpxe/icmp.h>
+
+/** @file
+ *
+ * ICMP protocol
+ *
+ */
+
+struct tcpip_protocol icmp_protocol __tcpip_protocol;
+
+/**
+ * Process a received packet
+ *
+ * @v iobuf I/O buffer
+ * @v st_src Partially-filled source address
+ * @v st_dest Partially-filled destination address
+ * @v pshdr_csum Pseudo-header checksum
+ * @ret rc Return status code
+ */
+static int icmp_rx ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src,
+ struct sockaddr_tcpip *st_dest,
+ uint16_t pshdr_csum __unused ) {
+ struct icmp_header *icmp = iobuf->data;
+ size_t len = iob_len ( iobuf );
+ unsigned int csum;
+ int rc;
+
+ /* Sanity check */
+ if ( len < sizeof ( *icmp ) ) {
+ DBG ( "ICMP packet too short at %zd bytes (min %zd bytes)\n",
+ len, sizeof ( *icmp ) );
+ rc = -EINVAL;
+ goto done;
+ }
+
+ /* Verify checksum */
+ csum = tcpip_chksum ( icmp, len );
+ if ( csum != 0 ) {
+ DBG ( "ICMP checksum incorrect (is %04x, should be 0000)\n",
+ csum );
+ DBG_HD ( icmp, len );
+ rc = -EINVAL;
+ goto done;
+ }
+
+ /* We respond only to pings */
+ if ( icmp->type != ICMP_ECHO_REQUEST ) {
+ DBG ( "ICMP ignoring type %d\n", icmp->type );
+ rc = 0;
+ goto done;
+ }
+
+ DBG ( "ICMP responding to ping\n" );
+
+ /* Change type to response and recalculate checksum */
+ icmp->type = ICMP_ECHO_RESPONSE;
+ icmp->chksum = 0;
+ icmp->chksum = tcpip_chksum ( icmp, len );
+
+ /* Transmit the response */
+ if ( ( rc = tcpip_tx ( iob_disown ( iobuf ), &icmp_protocol, st_dest,
+ st_src, NULL, NULL ) ) != 0 ) {
+ DBG ( "ICMP could not transmit ping response: %s\n",
+ strerror ( rc ) );
+ goto done;
+ }
+
+ done:
+ free_iob ( iobuf );
+ return rc;
+}
+
+/** ICMP TCP/IP protocol */
+struct tcpip_protocol icmp_protocol __tcpip_protocol = {
+ .name = "ICMP",
+ .rx = icmp_rx,
+ .tcpip_proto = IP_ICMP,
+};
diff --git a/gpxe/src/net/icmpv6.c b/gpxe/src/net/icmpv6.c
index 7b7146c2..237fc4a6 100644
--- a/gpxe/src/net/icmpv6.c
+++ b/gpxe/src/net/icmpv6.c
@@ -60,7 +60,7 @@ int icmp6_send_solicit ( struct net_device *netdev, struct in6_addr *src __unuse
st_dest.sin6.sin6_addr.in6_u.u6_addr8[13] = 0xff;
/* Send packet over IP6 */
- return tcpip_tx ( iobuf, &icmp6_protocol, &st_dest.st,
+ return tcpip_tx ( iobuf, &icmp6_protocol, NULL, &st_dest.st,
NULL, &nsolicit->csum );
}
diff --git a/gpxe/src/net/infiniband.c b/gpxe/src/net/infiniband.c
index ab76742e..d79bdc2c 100644
--- a/gpxe/src/net/infiniband.c
+++ b/gpxe/src/net/infiniband.c
@@ -46,10 +46,12 @@ struct list_head ib_devices = LIST_HEAD_INIT ( ib_devices );
*
* @v ibdev Infiniband device
* @v num_cqes Number of completion queue entries
+ * @v op Completion queue operations
* @ret cq New completion queue
*/
-struct ib_completion_queue * ib_create_cq ( struct ib_device *ibdev,
- unsigned int num_cqes ) {
+struct ib_completion_queue *
+ib_create_cq ( struct ib_device *ibdev, unsigned int num_cqes,
+ struct ib_completion_queue_operations *op ) {
struct ib_completion_queue *cq;
int rc;
@@ -58,22 +60,28 @@ struct ib_completion_queue * ib_create_cq ( struct ib_device *ibdev,
/* Allocate and initialise data structure */
cq = zalloc ( sizeof ( *cq ) );
if ( ! cq )
- return NULL;
+ goto err_alloc_cq;
cq->num_cqes = num_cqes;
INIT_LIST_HEAD ( &cq->work_queues );
+ cq->op = op;
/* Perform device-specific initialisation and get CQN */
if ( ( rc = ibdev->op->create_cq ( ibdev, cq ) ) != 0 ) {
DBGC ( ibdev, "IBDEV %p could not initialise completion "
"queue: %s\n", ibdev, strerror ( rc ) );
- free ( cq );
- return NULL;
+ goto err_dev_create_cq;
}
DBGC ( ibdev, "IBDEV %p created %d-entry completion queue %p (%p) "
"with CQN %#lx\n", ibdev, num_cqes, cq,
ib_cq_get_drvdata ( cq ), cq->cqn );
return cq;
+
+ ibdev->op->destroy_cq ( ibdev, cq );
+ err_dev_create_cq:
+ free ( cq );
+ err_alloc_cq:
+ return NULL;
}
/**
@@ -120,7 +128,9 @@ struct ib_queue_pair * ib_create_qp ( struct ib_device *ibdev,
( num_recv_wqes * sizeof ( qp->recv.iobufs[0] ) ) );
qp = zalloc ( total_size );
if ( ! qp )
- return NULL;
+ goto err_alloc_qp;
+ qp->ibdev = ibdev;
+ list_add ( &qp->list, &ibdev->qps );
qp->qkey = qkey;
qp->send.qp = qp;
qp->send.is_send = 1;
@@ -134,15 +144,13 @@ struct ib_queue_pair * ib_create_qp ( struct ib_device *ibdev,
qp->recv.num_wqes = num_recv_wqes;
qp->recv.iobufs = ( ( ( void * ) qp ) + sizeof ( *qp ) +
( num_send_wqes * sizeof ( qp->send.iobufs[0] ) ));
+ INIT_LIST_HEAD ( &qp->mgids );
/* Perform device-specific initialisation and get QPN */
if ( ( rc = ibdev->op->create_qp ( ibdev, qp ) ) != 0 ) {
DBGC ( ibdev, "IBDEV %p could not initialise queue pair: "
"%s\n", ibdev, strerror ( rc ) );
- list_del ( &qp->send.list );
- list_del ( &qp->recv.list );
- free ( qp );
- return NULL;
+ goto err_dev_create_qp;
}
DBGC ( ibdev, "IBDEV %p created queue pair %p (%p) with QPN %#lx\n",
@@ -154,6 +162,15 @@ struct ib_queue_pair * ib_create_qp ( struct ib_device *ibdev,
ibdev, qp->qpn, num_recv_wqes, qp->recv.iobufs,
( ( ( void * ) qp ) + total_size ) );
return qp;
+
+ ibdev->op->destroy_qp ( ibdev, qp );
+ err_dev_create_qp:
+ list_del ( &qp->send.list );
+ list_del ( &qp->recv.list );
+ list_del ( &qp->list );
+ free ( qp );
+ err_alloc_qp:
+ return NULL;
}
/**
@@ -190,15 +207,80 @@ int ib_modify_qp ( struct ib_device *ibdev, struct ib_queue_pair *qp,
* @v qp Queue pair
*/
void ib_destroy_qp ( struct ib_device *ibdev, struct ib_queue_pair *qp ) {
+ struct io_buffer *iobuf;
+ unsigned int i;
+
DBGC ( ibdev, "IBDEV %p destroying QPN %#lx\n",
ibdev, qp->qpn );
+
+ assert ( list_empty ( &qp->mgids ) );
+
+ /* Perform device-specific destruction */
ibdev->op->destroy_qp ( ibdev, qp );
+
+ /* Complete any remaining I/O buffers with errors */
+ for ( i = 0 ; i < qp->send.num_wqes ; i++ ) {
+ if ( ( iobuf = qp->send.iobufs[i] ) != NULL )
+ ib_complete_send ( ibdev, qp, iobuf, -ECANCELED );
+ }
+ for ( i = 0 ; i < qp->recv.num_wqes ; i++ ) {
+ if ( ( iobuf = qp->recv.iobufs[i] ) != NULL ) {
+ ib_complete_recv ( ibdev, qp, NULL, iobuf,
+ -ECANCELED );
+ }
+ }
+
+ /* Remove work queues from completion queue */
list_del ( &qp->send.list );
list_del ( &qp->recv.list );
+
+ /* Free QP */
+ list_del ( &qp->list );
free ( qp );
}
/**
+ * Find queue pair by QPN
+ *
+ * @v ibdev Infiniband device
+ * @v qpn Queue pair number
+ * @ret qp Queue pair, or NULL
+ */
+struct ib_queue_pair * ib_find_qp_qpn ( struct ib_device *ibdev,
+ unsigned long qpn ) {
+ struct ib_queue_pair *qp;
+
+ list_for_each_entry ( qp, &ibdev->qps, list ) {
+ if ( qp->qpn == qpn )
+ return qp;
+ }
+ return NULL;
+}
+
+/**
+ * Find queue pair by multicast GID
+ *
+ * @v ibdev Infiniband device
+ * @v gid Multicast GID
+ * @ret qp Queue pair, or NULL
+ */
+struct ib_queue_pair * ib_find_qp_mgid ( struct ib_device *ibdev,
+ struct ib_gid *gid ) {
+ struct ib_queue_pair *qp;
+ struct ib_multicast_gid *mgid;
+
+ list_for_each_entry ( qp, &ibdev->qps, list ) {
+ list_for_each_entry ( mgid, &qp->mgids, list ) {
+ if ( memcmp ( &mgid->gid, gid,
+ sizeof ( mgid->gid ) ) == 0 ) {
+ return qp;
+ }
+ }
+ }
+ return NULL;
+}
+
+/**
* Find work queue belonging to completion queue
*
* @v cq Completion queue
@@ -217,142 +299,194 @@ struct ib_work_queue * ib_find_wq ( struct ib_completion_queue *cq,
return NULL;
}
-/***************************************************************************
- *
- * Management datagram operations
- *
- ***************************************************************************
- */
-
/**
- * Get port information
+ * Post send work queue entry
*
* @v ibdev Infiniband device
- * @v port_info Port information datagram to fill in
+ * @v qp Queue pair
+ * @v av Address vector
+ * @v iobuf I/O buffer
* @ret rc Return status code
*/
-static int ib_get_port_info ( struct ib_device *ibdev,
- struct ib_mad_port_info *port_info ) {
- struct ib_mad_hdr *hdr = &port_info->mad_hdr;
+int ib_post_send ( struct ib_device *ibdev, struct ib_queue_pair *qp,
+ struct ib_address_vector *av,
+ struct io_buffer *iobuf ) {
int rc;
- /* Construct MAD */
- memset ( port_info, 0, sizeof ( *port_info ) );
- hdr->base_version = IB_MGMT_BASE_VERSION;
- hdr->mgmt_class = IB_MGMT_CLASS_SUBN_LID_ROUTED;
- hdr->class_version = 1;
- hdr->method = IB_MGMT_METHOD_GET;
- hdr->attr_id = htons ( IB_SMP_ATTR_PORT_INFO );
- hdr->attr_mod = htonl ( ibdev->port );
-
- if ( ( rc = ib_mad ( ibdev, hdr, sizeof ( *port_info ) ) ) != 0 ) {
- DBGC ( ibdev, "IBDEV %p could not get port info: %s\n",
- ibdev, strerror ( rc ) );
+ /* Check queue fill level */
+ if ( qp->send.fill >= qp->send.num_wqes ) {
+ DBGC ( ibdev, "IBDEV %p QPN %#lx send queue full\n",
+ ibdev, qp->qpn );
+ return -ENOBUFS;
+ }
+
+ /* Post to hardware */
+ if ( ( rc = ibdev->op->post_send ( ibdev, qp, av, iobuf ) ) != 0 ) {
+ DBGC ( ibdev, "IBDEV %p QPN %#lx could not post send WQE: "
+ "%s\n", ibdev, qp->qpn, strerror ( rc ) );
return rc;
}
+
+ qp->send.fill++;
return 0;
}
/**
- * Get GUID information
+ * Post receive work queue entry
*
* @v ibdev Infiniband device
- * @v guid_info GUID information datagram to fill in
+ * @v qp Queue pair
+ * @v iobuf I/O buffer
* @ret rc Return status code
*/
-static int ib_get_guid_info ( struct ib_device *ibdev,
- struct ib_mad_guid_info *guid_info ) {
- struct ib_mad_hdr *hdr = &guid_info->mad_hdr;
+int ib_post_recv ( struct ib_device *ibdev, struct ib_queue_pair *qp,
+ struct io_buffer *iobuf ) {
int rc;
- /* Construct MAD */
- memset ( guid_info, 0, sizeof ( *guid_info ) );
- hdr->base_version = IB_MGMT_BASE_VERSION;
- hdr->mgmt_class = IB_MGMT_CLASS_SUBN_LID_ROUTED;
- hdr->class_version = 1;
- hdr->method = IB_MGMT_METHOD_GET;
- hdr->attr_id = htons ( IB_SMP_ATTR_GUID_INFO );
+ /* Check queue fill level */
+ if ( qp->recv.fill >= qp->recv.num_wqes ) {
+ DBGC ( ibdev, "IBDEV %p QPN %#lx receive queue full\n",
+ ibdev, qp->qpn );
+ return -ENOBUFS;
+ }
- if ( ( rc = ib_mad ( ibdev, hdr, sizeof ( *guid_info ) ) ) != 0 ) {
- DBGC ( ibdev, "IBDEV %p could not get GUID info: %s\n",
- ibdev, strerror ( rc ) );
+ /* Post to hardware */
+ if ( ( rc = ibdev->op->post_recv ( ibdev, qp, iobuf ) ) != 0 ) {
+ DBGC ( ibdev, "IBDEV %p QPN %#lx could not post receive WQE: "
+ "%s\n", ibdev, qp->qpn, strerror ( rc ) );
return rc;
}
+
+ qp->recv.fill++;
return 0;
}
/**
- * Get partition key table
+ * Complete send work queue entry
+ *
+ * @v ibdev Infiniband device
+ * @v qp Queue pair
+ * @v iobuf I/O buffer
+ * @v rc Completion status code
+ */
+void ib_complete_send ( struct ib_device *ibdev, struct ib_queue_pair *qp,
+ struct io_buffer *iobuf, int rc ) {
+ qp->send.cq->op->complete_send ( ibdev, qp, iobuf, rc );
+ qp->send.fill--;
+}
+
+/**
+ * Complete receive work queue entry
+ *
+ * @v ibdev Infiniband device
+ * @v qp Queue pair
+ * @v av Address vector
+ * @v iobuf I/O buffer
+ * @v rc Completion status code
+ */
+void ib_complete_recv ( struct ib_device *ibdev, struct ib_queue_pair *qp,
+ struct ib_address_vector *av,
+ struct io_buffer *iobuf, int rc ) {
+ qp->recv.cq->op->complete_recv ( ibdev, qp, av, iobuf, rc );
+ qp->recv.fill--;
+}
+
+/**
+ * Open port
*
* @v ibdev Infiniband device
- * @v guid_info Partition key table datagram to fill in
* @ret rc Return status code
*/
-static int ib_get_pkey_table ( struct ib_device *ibdev,
- struct ib_mad_pkey_table *pkey_table ) {
- struct ib_mad_hdr *hdr = &pkey_table->mad_hdr;
+int ib_open ( struct ib_device *ibdev ) {
int rc;
- /* Construct MAD */
- memset ( pkey_table, 0, sizeof ( *pkey_table ) );
- hdr->base_version = IB_MGMT_BASE_VERSION;
- hdr->mgmt_class = IB_MGMT_CLASS_SUBN_LID_ROUTED;
- hdr->class_version = 1;
- hdr->method = IB_MGMT_METHOD_GET;
- hdr->attr_id = htons ( IB_SMP_ATTR_PKEY_TABLE );
-
- if ( ( rc = ib_mad ( ibdev, hdr, sizeof ( *pkey_table ) ) ) != 0 ) {
- DBGC ( ibdev, "IBDEV %p could not get pkey table: %s\n",
- ibdev, strerror ( rc ) );
- return rc;
+ /* Open device if this is the first requested opening */
+ if ( ibdev->open_count == 0 ) {
+ if ( ( rc = ibdev->op->open ( ibdev ) ) != 0 )
+ return rc;
}
+
+ /* Increment device open request counter */
+ ibdev->open_count++;
+
return 0;
}
/**
- * Get MAD parameters
+ * Close port
*
* @v ibdev Infiniband device
+ */
+void ib_close ( struct ib_device *ibdev ) {
+
+ /* Decrement device open request counter */
+ ibdev->open_count--;
+
+ /* Close device if this was the last remaining requested opening */
+ if ( ibdev->open_count == 0 )
+ ibdev->op->close ( ibdev );
+}
+
+/**
+ * Attach to multicast group
+ *
+ * @v ibdev Infiniband device
+ * @v qp Queue pair
+ * @v gid Multicast GID
* @ret rc Return status code
*/
-static int ib_get_mad_params ( struct ib_device *ibdev ) {
- union {
- /* This union exists just to save stack space */
- struct ib_mad_port_info port_info;
- struct ib_mad_guid_info guid_info;
- struct ib_mad_pkey_table pkey_table;
- } u;
+int ib_mcast_attach ( struct ib_device *ibdev, struct ib_queue_pair *qp,
+ struct ib_gid *gid ) {
+ struct ib_multicast_gid *mgid;
int rc;
- /* Port info gives us the link state, the first half of the
- * port GID and the SM LID.
- */
- if ( ( rc = ib_get_port_info ( ibdev, &u.port_info ) ) != 0 )
- return rc;
- ibdev->link_up = ( ( u.port_info.port_state__link_speed_supported
- & 0xf ) == 4 );
- memcpy ( &ibdev->port_gid.u.bytes[0], u.port_info.gid_prefix, 8 );
- ibdev->sm_lid = ntohs ( u.port_info.mastersm_lid );
+ /* Add to software multicast GID list */
+ mgid = zalloc ( sizeof ( *mgid ) );
+ if ( ! mgid ) {
+ rc = -ENOMEM;
+ goto err_alloc_mgid;
+ }
+ memcpy ( &mgid->gid, gid, sizeof ( mgid->gid ) );
+ list_add ( &mgid->list, &qp->mgids );
- /* GUID info gives us the second half of the port GID */
- if ( ( rc = ib_get_guid_info ( ibdev, &u.guid_info ) ) != 0 )
- return rc;
- memcpy ( &ibdev->port_gid.u.bytes[8], u.guid_info.gid_local, 8 );
+ /* Add to hardware multicast GID list */
+ if ( ( rc = ibdev->op->mcast_attach ( ibdev, qp, gid ) ) != 0 )
+ goto err_dev_mcast_attach;
- /* Get partition key */
- if ( ( rc = ib_get_pkey_table ( ibdev, &u.pkey_table ) ) != 0 )
- return rc;
- ibdev->pkey = ntohs ( u.pkey_table.pkey[0][0] );
+ return 0;
- DBGC ( ibdev, "IBDEV %p port GID is %08lx:%08lx:%08lx:%08lx\n",
- ibdev, htonl ( ibdev->port_gid.u.dwords[0] ),
- htonl ( ibdev->port_gid.u.dwords[1] ),
- htonl ( ibdev->port_gid.u.dwords[2] ),
- htonl ( ibdev->port_gid.u.dwords[3] ) );
+ err_dev_mcast_attach:
+ list_del ( &mgid->list );
+ free ( mgid );
+ err_alloc_mgid:
+ return rc;
+}
- return 0;
+/**
+ * Detach from multicast group
+ *
+ * @v ibdev Infiniband device
+ * @v qp Queue pair
+ * @v gid Multicast GID
+ */
+void ib_mcast_detach ( struct ib_device *ibdev, struct ib_queue_pair *qp,
+ struct ib_gid *gid ) {
+ struct ib_multicast_gid *mgid;
+
+ /* Remove from hardware multicast GID list */
+ ibdev->op->mcast_detach ( ibdev, qp, gid );
+
+ /* Remove from software multicast GID list */
+ list_for_each_entry ( mgid, &qp->mgids, list ) {
+ if ( memcmp ( &mgid->gid, gid, sizeof ( mgid->gid ) ) == 0 ) {
+ list_del ( &mgid->list );
+ free ( mgid );
+ break;
+ }
+ }
}
+
/***************************************************************************
*
* Event queues
@@ -366,14 +500,6 @@ static int ib_get_mad_params ( struct ib_device *ibdev ) {
* @v ibdev Infiniband device
*/
void ib_link_state_changed ( struct ib_device *ibdev ) {
- int rc;
-
- /* Update MAD parameters */
- if ( ( rc = ib_get_mad_params ( ibdev ) ) != 0 ) {
- DBGC ( ibdev, "IBDEV %p could not update MAD parameters: %s\n",
- ibdev, strerror ( rc ) );
- return;
- }
/* Notify IPoIB of link state change */
ipoib_link_state_changed ( ibdev );
@@ -420,6 +546,9 @@ struct ib_device * alloc_ibdev ( size_t priv_size ) {
if ( ibdev ) {
drv_priv = ( ( ( void * ) ibdev ) + sizeof ( *ibdev ) );
ib_set_drvdata ( ibdev, drv_priv );
+ INIT_LIST_HEAD ( &ibdev->qps );
+ ibdev->lid = IB_LID_NONE;
+ ibdev->pkey = IB_PKEY_NONE;
}
return ibdev;
}
@@ -437,14 +566,6 @@ int register_ibdev ( struct ib_device *ibdev ) {
ibdev_get ( ibdev );
list_add_tail ( &ibdev->list, &ib_devices );
- /* Open link */
- if ( ( rc = ib_open ( ibdev ) ) != 0 )
- goto err_open;
-
- /* Get MAD parameters */
- if ( ( rc = ib_get_mad_params ( ibdev ) ) != 0 )
- goto err_get_mad_params;
-
/* Add IPoIB device */
if ( ( rc = ipoib_probe ( ibdev ) ) != 0 ) {
DBGC ( ibdev, "IBDEV %p could not add IPoIB device: %s\n",
@@ -457,9 +578,6 @@ int register_ibdev ( struct ib_device *ibdev ) {
return 0;
err_ipoib_probe:
- err_get_mad_params:
- ib_close ( ibdev );
- err_open:
list_del ( &ibdev->list );
ibdev_put ( ibdev );
return rc;
@@ -474,7 +592,6 @@ void unregister_ibdev ( struct ib_device *ibdev ) {
/* Close device */
ipoib_remove ( ibdev );
- ib_close ( ibdev );
/* Remove from device list */
list_del ( &ibdev->list );
diff --git a/gpxe/src/net/ipv4.c b/gpxe/src/net/ipv4.c
index 82a13c33..8668d44b 100644
--- a/gpxe/src/net/ipv4.c
+++ b/gpxe/src/net/ipv4.c
@@ -188,7 +188,7 @@ static struct io_buffer * ipv4_reassemble ( struct io_buffer * iobuf ) {
free_iob ( iobuf );
/** Check if the fragment series is over */
- if ( !iphdr->frags & IP_MASK_MOREFRAGS ) {
+ if ( ! ( iphdr->frags & IP_MASK_MOREFRAGS ) ) {
iobuf = fragbuf->frag_iob;
free_fragbuf ( fragbuf );
return iobuf;
@@ -266,7 +266,6 @@ static uint16_t ipv4_pshdr_chksum ( struct io_buffer *iobuf, uint16_t csum ) {
static int ipv4_ll_addr ( struct in_addr dest, struct in_addr src,
struct net_device *netdev, uint8_t *ll_dest ) {
struct ll_protocol *ll_protocol = netdev->ll_protocol;
- uint8_t *dest_bytes = ( ( uint8_t * ) &dest );
if ( dest.s_addr == INADDR_BROADCAST ) {
/* Broadcast address */
@@ -274,17 +273,7 @@ static int ipv4_ll_addr ( struct in_addr dest, struct in_addr src,
ll_protocol->ll_addr_len );
return 0;
} else if ( IN_MULTICAST ( ntohl ( dest.s_addr ) ) ) {
- /* Special case: IPv4 multicast over Ethernet. This
- * code may need to be generalised once we find out
- * what happens for other link layers.
- */
- ll_dest[0] = 0x01;
- ll_dest[1] = 0x00;
- ll_dest[2] = 0x5e;
- ll_dest[3] = dest_bytes[1] & 0x7f;
- ll_dest[4] = dest_bytes[2];
- ll_dest[5] = dest_bytes[3];
- return 0;
+ return ll_protocol->mc_hash ( AF_INET, &dest, ll_dest );
} else {
/* Unicast address: resolve via ARP */
return arp_resolve ( netdev, &ipv4_protocol, &dest,
@@ -297,6 +286,7 @@ static int ipv4_ll_addr ( struct in_addr dest, struct in_addr src,
*
* @v iobuf I/O buffer
* @v tcpip Transport-layer protocol
+ * @v st_src Source network-layer address
* @v st_dest Destination network-layer address
* @v netdev Network device to use if no route found, or NULL
* @v trans_csum Transport-layer checksum to complete, or NULL
@@ -306,10 +296,12 @@ static int ipv4_ll_addr ( struct in_addr dest, struct in_addr src,
*/
static int ipv4_tx ( struct io_buffer *iobuf,
struct tcpip_protocol *tcpip_protocol,
+ struct sockaddr_tcpip *st_src,
struct sockaddr_tcpip *st_dest,
struct net_device *netdev,
uint16_t *trans_csum ) {
struct iphdr *iphdr = iob_push ( iobuf, sizeof ( *iphdr ) );
+ struct sockaddr_in *sin_src = ( ( struct sockaddr_in * ) st_src );
struct sockaddr_in *sin_dest = ( ( struct sockaddr_in * ) st_dest );
struct ipv4_miniroute *miniroute;
struct in_addr next_hop;
@@ -328,7 +320,11 @@ static int ipv4_tx ( struct io_buffer *iobuf,
/* Use routing table to identify next hop and transmitting netdev */
next_hop = iphdr->dest;
- if ( ( miniroute = ipv4_route ( &next_hop ) ) ) {
+ if ( sin_src )
+ iphdr->src = sin_src->sin_addr;
+ if ( ( next_hop.s_addr != INADDR_BROADCAST ) &&
+ ( ! IN_MULTICAST ( ntohl ( next_hop.s_addr ) ) ) &&
+ ( ( miniroute = ipv4_route ( &next_hop ) ) != NULL ) ) {
iphdr->src = miniroute->address;
netdev = miniroute->netdev;
}
@@ -631,3 +627,6 @@ static int ipv4_create_routes ( void ) {
struct settings_applicator ipv4_settings_applicator __settings_applicator = {
.apply = ipv4_create_routes,
};
+
+/* Drag in ICMP */
+REQUIRE_OBJECT ( icmp );
diff --git a/gpxe/src/net/ipv6.c b/gpxe/src/net/ipv6.c
index 3407d538..f7308bb4 100644
--- a/gpxe/src/net/ipv6.c
+++ b/gpxe/src/net/ipv6.c
@@ -176,6 +176,7 @@ void ipv6_dump ( struct ip6_header *ip6hdr ) {
*/
static int ipv6_tx ( struct io_buffer *iobuf,
struct tcpip_protocol *tcpip,
+ struct sockaddr_tcpip *st_src __unused,
struct sockaddr_tcpip *st_dest,
struct net_device *netdev,
uint16_t *trans_csum ) {
diff --git a/gpxe/src/net/netdevice.c b/gpxe/src/net/netdevice.c
index 3721b334..9e142d27 100644
--- a/gpxe/src/net/netdevice.c
+++ b/gpxe/src/net/netdevice.c
@@ -45,6 +45,48 @@ static struct net_protocol net_protocols_end[0]
/** List of network devices */
struct list_head net_devices = LIST_HEAD_INIT ( net_devices );
+/** List of open network devices, in reverse order of opening */
+struct list_head open_net_devices = LIST_HEAD_INIT ( open_net_devices );
+
+/**
+ * Record network device statistic
+ *
+ * @v stats Network device statistics
+ * @v rc Status code
+ */
+static void netdev_record_stat ( struct net_device_stats *stats, int rc ) {
+ struct net_device_error *error;
+ struct net_device_error *least_common_error;
+ unsigned int i;
+
+ /* If this is not an error, just update the good counter */
+ if ( rc == 0 ) {
+ stats->good++;
+ return;
+ }
+
+ /* Update the bad counter */
+ stats->bad++;
+
+ /* Locate the appropriate error record */
+ least_common_error = &stats->errors[0];
+ for ( i = 0 ; i < ( sizeof ( stats->errors ) /
+ sizeof ( stats->errors[0] ) ) ; i++ ) {
+ error = &stats->errors[i];
+ /* Update matching record, if found */
+ if ( error->rc == rc ) {
+ error->count++;
+ return;
+ }
+ if ( error->count < least_common_error->count )
+ least_common_error = error;
+ }
+
+ /* Overwrite the least common error record */
+ least_common_error->rc = rc;
+ least_common_error->count = 1;
+}
+
/**
* Transmit raw packet via network device
*
@@ -91,12 +133,11 @@ void netdev_tx_complete_err ( struct net_device *netdev,
struct io_buffer *iobuf, int rc ) {
/* Update statistics counter */
+ netdev_record_stat ( &netdev->tx_stats, rc );
if ( rc == 0 ) {
- netdev->stats.tx_ok++;
DBGC ( netdev, "NETDEV %p transmission %p complete\n",
netdev, iobuf );
} else {
- netdev->stats.tx_err++;
DBGC ( netdev, "NETDEV %p transmission %p failed: %s\n",
netdev, iobuf, strerror ( rc ) );
}
@@ -158,7 +199,7 @@ void netdev_rx ( struct net_device *netdev, struct io_buffer *iobuf ) {
list_add_tail ( &iobuf->list, &netdev->rx_queue );
/* Update statistics counter */
- netdev->stats.rx_ok++;
+ netdev_record_stat ( &netdev->rx_stats, 0 );
}
/**
@@ -183,7 +224,7 @@ void netdev_rx_err ( struct net_device *netdev,
free_iob ( iobuf );
/* Update statistics counter */
- netdev->stats.rx_err++;
+ netdev_record_stat ( &netdev->rx_stats, rc );
}
/**
@@ -268,7 +309,7 @@ struct net_device * alloc_netdev ( size_t priv_size ) {
INIT_LIST_HEAD ( &netdev->rx_queue );
settings_init ( netdev_settings ( netdev ),
&netdev_settings_operations, &netdev->refcnt,
- netdev->name );
+ netdev->name, 0 );
netdev->priv = ( ( ( void * ) netdev ) + sizeof ( *netdev ) );
}
return netdev;
@@ -330,6 +371,10 @@ int netdev_open ( struct net_device *netdev ) {
/* Mark as opened */
netdev->state |= NETDEV_OPEN;
+
+ /* Add to head of open devices list */
+ list_add ( &netdev->open_list, &open_net_devices );
+
return 0;
}
@@ -355,6 +400,9 @@ void netdev_close ( struct net_device *netdev ) {
/* Mark as closed */
netdev->state &= ~NETDEV_OPEN;
+
+ /* Remove from open devices list */
+ list_del ( &netdev->open_list );
}
/**
@@ -425,6 +473,22 @@ struct net_device * find_netdev_by_location ( unsigned int bus_type,
}
/**
+ * Get most recently opened network device
+ *
+ * @ret netdev Most recently opened network device, or NULL
+ */
+struct net_device * last_opened_netdev ( void ) {
+ struct net_device *netdev;
+
+ list_for_each_entry ( netdev, &open_net_devices, open_list ) {
+ assert ( netdev->state & NETDEV_OPEN );
+ return netdev;
+ }
+
+ return NULL;
+}
+
+/**
* Transmit network-layer packet
*
* @v iobuf I/O buffer
@@ -439,6 +503,7 @@ struct net_device * find_netdev_by_location ( unsigned int bus_type,
*/
int net_tx ( struct io_buffer *iobuf, struct net_device *netdev,
struct net_protocol *net_protocol, const void *ll_dest ) {
+ struct ll_protocol *ll_protocol = netdev->ll_protocol;
int rc;
/* Force a poll on the netdevice to (potentially) clear any
@@ -449,8 +514,8 @@ int net_tx ( struct io_buffer *iobuf, struct net_device *netdev,
netdev_poll ( netdev );
/* Add link-layer header */
- if ( ( rc = netdev->ll_protocol->push ( iobuf, netdev, net_protocol,
- ll_dest ) ) != 0 ) {
+ if ( ( rc = ll_protocol->push ( iobuf, ll_dest, netdev->ll_addr,
+ net_protocol->net_proto ) ) != 0 ) {
free_iob ( iobuf );
return rc;
}
@@ -495,8 +560,9 @@ static void net_step ( struct process *process __unused ) {
struct net_device *netdev;
struct io_buffer *iobuf;
struct ll_protocol *ll_protocol;
- uint16_t net_proto;
+ const void *ll_dest;
const void *ll_source;
+ uint16_t net_proto;
int rc;
/* Poll and process each network device */
@@ -519,9 +585,9 @@ static void net_step ( struct process *process __unused ) {
/* Remove link-layer header */
ll_protocol = netdev->ll_protocol;
- if ( ( rc = ll_protocol->pull ( iobuf, netdev,
- &net_proto,
- &ll_source ) ) != 0 ) {
+ if ( ( rc = ll_protocol->pull ( iobuf, &ll_dest,
+ &ll_source,
+ &net_proto ) ) != 0 ) {
free_iob ( iobuf );
continue;
}
diff --git a/gpxe/src/net/retry.c b/gpxe/src/net/retry.c
index 2a645c97..cd793a7f 100644
--- a/gpxe/src/net/retry.c
+++ b/gpxe/src/net/retry.c
@@ -55,9 +55,10 @@ static LIST_HEAD ( timers );
* be stopped and the timer's callback function will be called.
*/
void start_timer ( struct retry_timer *timer ) {
- if ( ! timer_running ( timer ) )
+ if ( ! timer->running )
list_add ( &timer->list, &timers );
timer->start = currticks();
+ timer->running = 1;
/* 0 means "use default timeout" */
if ( timer->min_timeout == 0 )
@@ -82,6 +83,8 @@ void start_timer ( struct retry_timer *timer ) {
void start_timer_fixed ( struct retry_timer *timer, unsigned long timeout ) {
start_timer ( timer );
timer->timeout = timeout;
+ DBG2 ( "Timer %p expiry time changed to %ld\n",
+ timer, ( timer->start + timer->timeout ) );
}
/**
@@ -97,12 +100,12 @@ void stop_timer ( struct retry_timer *timer ) {
unsigned long runtime;
/* If timer was already stopped, do nothing */
- if ( ! timer_running ( timer ) )
+ if ( ! timer->running )
return;
list_del ( &timer->list );
runtime = ( now - timer->start );
- timer->start = 0;
+ timer->running = 0;
DBG2 ( "Timer %p stopped at time %ld (ran for %ld)\n",
timer, now, runtime );
@@ -144,8 +147,9 @@ static void timer_expired ( struct retry_timer *timer ) {
/* Stop timer without performing RTT calculations */
DBG2 ( "Timer %p stopped at time %ld on expiry\n",
timer, currticks() );
+ assert ( timer->running );
list_del ( &timer->list );
- timer->start = 0;
+ timer->running = 0;
timer->count++;
/* Back off the timeout value */
diff --git a/gpxe/src/net/tcp.c b/gpxe/src/net/tcp.c
index df87fc14..6bcd193c 100644
--- a/gpxe/src/net/tcp.c
+++ b/gpxe/src/net/tcp.c
@@ -478,12 +478,12 @@ static int tcp_xmit ( struct tcp_connection *tcp, int force_send ) {
tcphdr->seq = htonl ( tcp->snd_seq );
tcphdr->ack = htonl ( tcp->rcv_ack );
tcphdr->hlen = ( ( payload - iobuf->data ) << 2 );
- tcphdr->flags = flags;
+ tcphdr->flags = ( flags | TCP_PSH );
tcphdr->win = htons ( tcp->rcv_win );
tcphdr->csum = tcpip_chksum ( iobuf->data, iob_len ( iobuf ) );
/* Dump header */
- DBGC ( tcp, "TCP %p TX %d->%d %08lx..%08lx %08lx %4zd",
+ DBGC ( tcp, "TCP %p TX %d->%d %08x..%08zx %08x %4zd",
tcp, ntohs ( tcphdr->src ), ntohs ( tcphdr->dest ),
ntohl ( tcphdr->seq ), ( ntohl ( tcphdr->seq ) + seq_len ),
ntohl ( tcphdr->ack ), len );
@@ -491,7 +491,7 @@ static int tcp_xmit ( struct tcp_connection *tcp, int force_send ) {
DBGC ( tcp, "\n" );
/* Transmit packet */
- return tcpip_tx ( iobuf, &tcp_protocol, &tcp->peer, NULL,
+ return tcpip_tx ( iobuf, &tcp_protocol, NULL, &tcp->peer, NULL,
&tcphdr->csum );
}
@@ -564,7 +564,7 @@ static int tcp_xmit_reset ( struct tcp_connection *tcp,
tcphdr->csum = tcpip_chksum ( iobuf->data, iob_len ( iobuf ) );
/* Dump header */
- DBGC ( tcp, "TCP %p TX %d->%d %08lx..%08lx %08lx %4d",
+ DBGC ( tcp, "TCP %p TX %d->%d %08x..%08x %08x %4d",
tcp, ntohs ( tcphdr->src ), ntohs ( tcphdr->dest ),
ntohl ( tcphdr->seq ), ( ntohl ( tcphdr->seq ) ),
ntohl ( tcphdr->ack ), 0 );
@@ -572,7 +572,7 @@ static int tcp_xmit_reset ( struct tcp_connection *tcp,
DBGC ( tcp, "\n" );
/* Transmit packet */
- return tcpip_tx ( iobuf, &tcp_protocol, st_dest,
+ return tcpip_tx ( iobuf, &tcp_protocol, NULL, st_dest,
NULL, &tcphdr->csum );
}
@@ -702,8 +702,8 @@ static int tcp_rx_ack ( struct tcp_connection *tcp, uint32_t ack,
/* Ignore duplicate or out-of-range ACK */
if ( ack_len > tcp->snd_sent ) {
- DBGC ( tcp, "TCP %p received ACK for [%08lx,%08lx), "
- "sent only [%08lx,%08lx)\n", tcp, tcp->snd_seq,
+ DBGC ( tcp, "TCP %p received ACK for [%08x,%08zx), "
+ "sent only [%08x,%08x)\n", tcp, tcp->snd_seq,
( tcp->snd_seq + ack_len ), tcp->snd_seq,
( tcp->snd_seq + tcp->snd_sent ) );
return -EINVAL;
@@ -894,7 +894,7 @@ static int tcp_rx ( struct io_buffer *iobuf,
len = iob_len ( iobuf );
/* Dump header */
- DBGC ( tcp, "TCP %p RX %d<-%d %08lx %08lx..%08lx %4zd",
+ DBGC ( tcp, "TCP %p RX %d<-%d %08x %08x..%08zx %4zd",
tcp, ntohs ( tcphdr->dest ), ntohs ( tcphdr->src ),
ntohl ( tcphdr->ack ), ntohl ( tcphdr->seq ),
( ntohl ( tcphdr->seq ) + len +
@@ -1033,7 +1033,7 @@ static size_t tcp_xfer_window ( struct xfer_interface *xfer ) {
*
* @v xfer Data transfer interface
* @v iobuf Datagram I/O buffer
- * @v meta Data transfer metadata, or NULL
+ * @v meta Data transfer metadata
* @ret rc Return status code
*/
static int tcp_xfer_deliver_iob ( struct xfer_interface *xfer,
@@ -1070,12 +1070,13 @@ static struct xfer_interface_operations tcp_xfer_operations = {
/** TCP socket opener */
struct socket_opener tcp_socket_opener __socket_opener = {
- .semantics = SOCK_STREAM,
+ .semantics = TCP_SOCK_STREAM,
.family = AF_INET,
.open = tcp_open,
};
-char TCP_SOCK_STREAM[1];
+/** Linkage hack */
+int tcp_sock_stream = TCP_SOCK_STREAM;
/**
* Open TCP URI
diff --git a/gpxe/src/net/tcp/ftp.c b/gpxe/src/net/tcp/ftp.c
index 3b88f7b6..445e32bb 100644
--- a/gpxe/src/net/tcp/ftp.c
+++ b/gpxe/src/net/tcp/ftp.c
@@ -109,23 +109,39 @@ static void ftp_done ( struct ftp_request *ftp, int rc ) {
*
*/
+/** An FTP control channel string */
+struct ftp_control_string {
+ /** Literal portion */
+ const char *literal;
+ /** Variable portion
+ *
+ * @v ftp FTP request
+ * @ret string Variable portion of string
+ */
+ const char * ( *variable ) ( struct ftp_request *ftp );
+};
+
/**
- * FTP control channel strings
+ * Retrieve FTP pathname
*
- * These are used as printf() format strings. Since only one of them
- * (RETR) takes an argument, we always supply that argument to the
- * snprintf() call.
+ * @v ftp FTP request
+ * @ret path FTP pathname
*/
-static const char * ftp_strings[] = {
- [FTP_CONNECT] = NULL,
- [FTP_USER] = "USER anonymous\r\n",
- [FTP_PASS] = "PASS etherboot@etherboot.org\r\n",
- [FTP_TYPE] = "TYPE I\r\n",
- [FTP_PASV] = "PASV\r\n",
- [FTP_RETR] = "RETR %s\r\n",
- [FTP_WAIT] = NULL,
- [FTP_QUIT] = "QUIT\r\n",
- [FTP_DONE] = NULL,
+static const char * ftp_uri_path ( struct ftp_request *ftp ) {
+ return ftp->uri->path;
+}
+
+/** FTP control channel strings */
+static struct ftp_control_string ftp_strings[] = {
+ [FTP_CONNECT] = { NULL, NULL },
+ [FTP_USER] = { "USER anonymous", NULL },
+ [FTP_PASS] = { "PASS etherboot@etherboot.org", NULL },
+ [FTP_TYPE] = { "TYPE I", NULL },
+ [FTP_PASV] = { "PASV", NULL },
+ [FTP_RETR] = { "RETR ", ftp_uri_path },
+ [FTP_WAIT] = { NULL, NULL },
+ [FTP_QUIT] = { "QUIT", NULL },
+ [FTP_DONE] = { NULL, NULL },
};
/**
@@ -178,18 +194,23 @@ static void ftp_parse_value ( char **text, uint8_t *value, size_t len ) {
*
*/
static void ftp_next_state ( struct ftp_request *ftp ) {
+ struct ftp_control_string *ftp_string;
+ const char *literal;
+ const char *variable;
/* Move to next state */
if ( ftp->state < FTP_DONE )
ftp->state++;
/* Send control string if needed */
- if ( ftp_strings[ftp->state] != NULL ) {
- DBGC ( ftp, "FTP %p sending ", ftp );
- DBGC ( ftp, ftp_strings[ftp->state], ftp->uri->path );
- xfer_printf ( &ftp->control, ftp_strings[ftp->state],
- ftp->uri->path );
- }
+ ftp_string = &ftp_strings[ftp->state];
+ literal = ftp_string->literal;
+ variable = ( ftp_string->variable ?
+ ftp_string->variable ( ftp ) : "" );
+ if ( literal ) {
+ DBGC ( ftp, "FTP %p sending %s%s\n", ftp, literal, variable );
+ xfer_printf ( &ftp->control, "%s%s\r\n", literal, variable );
+ }
}
/**
@@ -359,7 +380,7 @@ static void ftp_data_closed ( struct xfer_interface *data, int rc ) {
*
* @v xfer FTP data channel interface
* @v iobuf I/O buffer
- * @v meta Data transfer metadata, or NULL
+ * @v meta Data transfer metadata
* @ret rc Return status code
*/
static int ftp_data_deliver_iob ( struct xfer_interface *data,
diff --git a/gpxe/src/net/tcp/http.c b/gpxe/src/net/tcp/http.c
index 4dc1ab73..93ccfd3b 100644
--- a/gpxe/src/net/tcp/http.c
+++ b/gpxe/src/net/tcp/http.c
@@ -41,6 +41,7 @@
#include <gpxe/process.h>
#include <gpxe/linebuf.h>
#include <gpxe/features.h>
+#include <gpxe/base64.h>
#include <gpxe/http.h>
FEATURE ( FEATURE_PROTOCOL, "HTTP", DHCP_EB_FEATURE_HTTP, 1 );
@@ -142,6 +143,8 @@ static int http_response_to_rc ( unsigned int response ) {
return -ENOENT;
case 403:
return -EPERM;
+ case 401:
+ return -EACCES;
default:
return -EIO;
}
@@ -318,7 +321,7 @@ static int http_rx_data ( struct http_request *http,
*
* @v socket Transport layer interface
* @v iobuf I/O buffer
- * @v meta Data transfer metadata, or NULL
+ * @v meta Data transfer metadata
* @ret rc Return status code
*/
static int http_socket_deliver_iob ( struct xfer_interface *socket,
@@ -340,8 +343,7 @@ static int http_socket_deliver_iob ( struct xfer_interface *socket,
/* Once we're into the data phase, just fill
* the data buffer
*/
- rc = http_rx_data ( http, iobuf );
- iobuf = NULL;
+ rc = http_rx_data ( http, iob_disown ( iobuf ) );
goto done;
case HTTP_RX_RESPONSE:
case HTTP_RX_HEADER:
@@ -388,18 +390,55 @@ static void http_step ( struct process *process ) {
const char *path = http->uri->path;
const char *host = http->uri->host;
const char *query = http->uri->query;
+ const char *user = http->uri->user;
+ const char *password =
+ ( http->uri->password ? http->uri->password : "" );
+ size_t user_pw_len = ( user ? ( strlen ( user ) + 1 /* ":" */ +
+ strlen ( password ) ) : 0 );
+ size_t user_pw_base64_len = base64_encoded_len ( user_pw_len );
+ char user_pw[ user_pw_len + 1 /* NUL */ ];
+ char user_pw_base64[ user_pw_base64_len + 1 /* NUL */ ];
int rc;
if ( xfer_window ( &http->socket ) ) {
+
+ /* We want to execute only once */
process_del ( &http->process );
+
+ /* Construct authorisation, if applicable */
+ if ( user ) {
+ char *buf = user_pw;
+ ssize_t remaining = sizeof ( user_pw );
+ size_t len;
+
+ /* URI-decode the username and password */
+ len = uri_decode ( user, buf, remaining );
+ buf += len;
+ remaining -= len;
+ *(remaining--, buf++) = ':';
+ len = uri_decode ( password, buf, remaining );
+ buf += len;
+ remaining -= len;
+ assert ( remaining >= 0 );
+
+ /* Base64-encode the "user:password" string */
+ base64_encode ( user_pw, user_pw_base64 );
+ }
+
+ /* Send GET request */
if ( ( rc = xfer_printf ( &http->socket,
"GET %s%s%s HTTP/1.0\r\n"
"User-Agent: gPXE/" VERSION "\r\n"
+ "%s%s%s"
"Host: %s\r\n"
"\r\n",
( path ? path : "/" ),
( query ? "?" : "" ),
( query ? query : "" ),
+ ( user ?
+ "Authorization: Basic " : "" ),
+ ( user ? user_pw_base64 : "" ),
+ ( user ? "\r\n" : "" ),
host ) ) != 0 ) {
http_done ( http, rc );
}
diff --git a/gpxe/src/net/tcp/iscsi.c b/gpxe/src/net/tcp/iscsi.c
index e9e36449..aa99db71 100644
--- a/gpxe/src/net/tcp/iscsi.c
+++ b/gpxe/src/net/tcp/iscsi.c
@@ -1305,7 +1305,7 @@ static int iscsi_rx_bhs ( struct iscsi_session *iscsi, const void *data,
size_t len, size_t remaining __unused ) {
memcpy ( &iscsi->rx_bhs.bytes[iscsi->rx_offset], data, len );
if ( ( iscsi->rx_offset + len ) >= sizeof ( iscsi->rx_bhs ) ) {
- DBGC2 ( iscsi, "iSCSI %p received PDU opcode %#x len %#lx\n",
+ DBGC2 ( iscsi, "iSCSI %p received PDU opcode %#x len %#x\n",
iscsi, iscsi->rx_bhs.common.opcode,
ISCSI_DATA_LEN ( iscsi->rx_bhs.common.lengths ) );
}
diff --git a/gpxe/src/net/tcpip.c b/gpxe/src/net/tcpip.c
index 1bc8d1a3..d4542b05 100644
--- a/gpxe/src/net/tcpip.c
+++ b/gpxe/src/net/tcpip.c
@@ -64,14 +64,15 @@ int tcpip_rx ( struct io_buffer *iobuf, uint8_t tcpip_proto,
*
* @v iobuf I/O buffer
* @v tcpip_protocol Transport-layer protocol
+ * @v st_src Source address, or NULL to use route default
* @v st_dest Destination address
* @v netdev Network device to use if no route found, or NULL
* @v trans_csum Transport-layer checksum to complete, or NULL
* @ret rc Return status code
*/
int tcpip_tx ( struct io_buffer *iobuf, struct tcpip_protocol *tcpip_protocol,
- struct sockaddr_tcpip *st_dest, struct net_device *netdev,
- uint16_t *trans_csum ) {
+ struct sockaddr_tcpip *st_src, struct sockaddr_tcpip *st_dest,
+ struct net_device *netdev, uint16_t *trans_csum ) {
struct tcpip_net_protocol *tcpip_net;
/* Hand off packet to the appropriate network-layer protocol */
@@ -79,8 +80,8 @@ int tcpip_tx ( struct io_buffer *iobuf, struct tcpip_protocol *tcpip_protocol,
tcpip_net < tcpip_net_protocols_end ; tcpip_net++ ) {
if ( tcpip_net->sa_family == st_dest->st_family ) {
DBG ( "TCP/IP sending %s packet\n", tcpip_net->name );
- return tcpip_net->tx ( iobuf, tcpip_protocol, st_dest,
- netdev, trans_csum );
+ return tcpip_net->tx ( iobuf, tcpip_protocol, st_src,
+ st_dest, netdev, trans_csum );
}
}
diff --git a/gpxe/src/net/tls.c b/gpxe/src/net/tls.c
index 834686fb..f5bff7a4 100644
--- a/gpxe/src/net/tls.c
+++ b/gpxe/src/net/tls.c
@@ -36,6 +36,8 @@
#include <gpxe/xfer.h>
#include <gpxe/open.h>
#include <gpxe/filter.h>
+#include <gpxe/asn1.h>
+#include <gpxe/x509.h>
#include <gpxe/tls.h>
static int tls_send_plaintext ( struct tls_session *tls, unsigned int type,
@@ -43,6 +45,33 @@ static int tls_send_plaintext ( struct tls_session *tls, unsigned int type,
static void tls_clear_cipher ( struct tls_session *tls,
struct tls_cipherspec *cipherspec );
+/******************************************************************************
+ *
+ * Utility functions
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Extract 24-bit field value
+ *
+ * @v field24 24-bit field
+ * @ret value Field value
+ *
+ * TLS uses 24-bit integers in several places, which are awkward to
+ * parse in C.
+ */
+static unsigned long tls_uint24 ( uint8_t field24[3] ) {
+ return ( ( field24[0] << 16 ) + ( field24[1] << 8 ) + field24[2] );
+}
+
+/******************************************************************************
+ *
+ * Cleanup functions
+ *
+ ******************************************************************************
+ */
+
/**
* Free TLS session
*
@@ -57,8 +86,7 @@ static void free_tls ( struct refcnt *refcnt ) {
tls_clear_cipher ( tls, &tls->tx_cipherspec_pending );
tls_clear_cipher ( tls, &tls->rx_cipherspec );
tls_clear_cipher ( tls, &tls->rx_cipherspec_pending );
- free ( tls->rsa_mod );
- free ( tls->rsa_pub_exp );
+ x509_free_rsa_public_key ( &tls->rsa );
free ( tls->rx_data );
/* Free TLS structure itself */
@@ -270,16 +298,16 @@ static void tls_generate_master_secret ( struct tls_session *tls ) {
DBGC_HD ( tls, &tls->pre_master_secret,
sizeof ( tls->pre_master_secret ) );
DBGC ( tls, "TLS %p client random bytes:\n", tls );
- DBGC_HD ( tls, &tls->client_random, sizeof ( tls->server_random ) );
+ DBGC_HD ( tls, &tls->client_random, sizeof ( tls->client_random ) );
DBGC ( tls, "TLS %p server random bytes:\n", tls );
DBGC_HD ( tls, &tls->server_random, sizeof ( tls->server_random ) );
- tls_prf_label ( tls, tls->pre_master_secret,
+ tls_prf_label ( tls, &tls->pre_master_secret,
sizeof ( tls->pre_master_secret ),
- tls->master_secret, sizeof ( tls->master_secret ),
+ &tls->master_secret, sizeof ( tls->master_secret ),
"master secret",
- tls->client_random, sizeof ( tls->client_random ),
- tls->server_random, sizeof ( tls->server_random ) );
+ &tls->client_random, sizeof ( tls->client_random ),
+ &tls->server_random, sizeof ( tls->server_random ) );
DBGC ( tls, "TLS %p generated master secret:\n", tls );
DBGC_HD ( tls, &tls->master_secret, sizeof ( tls->master_secret ) );
@@ -304,10 +332,10 @@ static int tls_generate_keys ( struct tls_session *tls ) {
int rc;
/* Generate key block */
- tls_prf_label ( tls, tls->master_secret, sizeof ( tls->master_secret ),
+ tls_prf_label ( tls, &tls->master_secret, sizeof ( tls->master_secret ),
key_block, sizeof ( key_block ), "key expansion",
- tls->server_random, sizeof ( tls->server_random ),
- tls->client_random, sizeof ( tls->client_random ) );
+ &tls->server_random, sizeof ( tls->server_random ),
+ &tls->client_random, sizeof ( tls->client_random ) );
/* Split key block into portions */
key = key_block;
@@ -604,7 +632,7 @@ static int tls_send_client_hello ( struct tls_session *tls ) {
htonl ( sizeof ( hello ) -
sizeof ( hello.type_length ) ) );
hello.version = htons ( TLS_VERSION_TLS_1_0 );
- memcpy ( &hello.random, tls->client_random, sizeof ( hello.random ) );
+ memcpy ( &hello.random, &tls->client_random, sizeof ( hello.random ) );
hello.cipher_suite_len = htons ( sizeof ( hello.cipher_suites ) );
hello.cipher_suites[0] = htons ( TLS_RSA_WITH_AES_128_CBC_SHA );
hello.cipher_suites[1] = htons ( TLS_RSA_WITH_AES_256_CBC_SHA );
@@ -622,8 +650,8 @@ static int tls_send_client_hello ( struct tls_session *tls ) {
static int tls_send_client_key_exchange ( struct tls_session *tls ) {
/* FIXME: Hack alert */
RSA_CTX *rsa_ctx;
- RSA_pub_key_new ( &rsa_ctx, tls->rsa_mod, tls->rsa_mod_len,
- tls->rsa_pub_exp, tls->rsa_pub_exp_len );
+ RSA_pub_key_new ( &rsa_ctx, tls->rsa.modulus, tls->rsa.modulus_len,
+ tls->rsa.exponent, tls->rsa.exponent_len );
struct {
uint32_t type_length;
uint16_t encrypted_pre_master_secret_len;
@@ -641,9 +669,9 @@ static int tls_send_client_key_exchange ( struct tls_session *tls ) {
DBGC ( tls, "RSA encrypting plaintext, modulus, exponent:\n" );
DBGC_HD ( tls, &tls->pre_master_secret,
sizeof ( tls->pre_master_secret ) );
- DBGC_HD ( tls, tls->rsa_mod, tls->rsa_mod_len );
- DBGC_HD ( tls, tls->rsa_pub_exp, tls->rsa_pub_exp_len );
- RSA_encrypt ( rsa_ctx, tls->pre_master_secret,
+ DBGC_HD ( tls, tls->rsa.modulus, tls->rsa.modulus_len );
+ DBGC_HD ( tls, tls->rsa.exponent, tls->rsa.exponent_len );
+ RSA_encrypt ( rsa_ctx, ( const uint8_t * ) &tls->pre_master_secret,
sizeof ( tls->pre_master_secret ),
key_xchg.encrypted_pre_master_secret, 0 );
DBGC ( tls, "RSA encrypt done. Ciphertext:\n" );
@@ -685,7 +713,7 @@ static int tls_send_finished ( struct tls_session *tls ) {
htonl ( sizeof ( finished ) -
sizeof ( finished.type_length ) ) );
tls_verify_handshake ( tls, digest );
- tls_prf_label ( tls, tls->master_secret, sizeof ( tls->master_secret ),
+ tls_prf_label ( tls, &tls->master_secret, sizeof ( tls->master_secret ),
finished.verify_data, sizeof ( finished.verify_data ),
"client finished", digest, sizeof ( digest ) );
@@ -761,17 +789,16 @@ static int tls_new_alert ( struct tls_session *tls, void *data, size_t len ) {
}
/**
- * Receive new Server Hello record
+ * Receive new Server Hello handshake record
*
* @v tls TLS session
- * @v data Plaintext record
- * @v len Length of plaintext record
+ * @v data Plaintext handshake record
+ * @v len Length of plaintext handshake record
* @ret rc Return status code
*/
static int tls_new_server_hello ( struct tls_session *tls,
void *data, size_t len ) {
struct {
- uint32_t type_length;
uint16_t version;
uint8_t random[32];
uint8_t session_id_len;
@@ -802,7 +829,7 @@ static int tls_new_server_hello ( struct tls_session *tls,
}
/* Copy out server random bytes */
- memcpy ( tls->server_random, hello_a->random,
+ memcpy ( &tls->server_random, &hello_a->random,
sizeof ( tls->server_random ) );
/* Select cipher suite */
@@ -818,72 +845,74 @@ static int tls_new_server_hello ( struct tls_session *tls,
}
/**
- * Receive new Certificate record
+ * Receive new Certificate handshake record
*
* @v tls TLS session
- * @v data Plaintext record
- * @v len Length of plaintext record
+ * @v data Plaintext handshake record
+ * @v len Length of plaintext handshake record
* @ret rc Return status code
*/
static int tls_new_certificate ( struct tls_session *tls,
void *data, size_t len ) {
struct {
- uint32_t type_length;
uint8_t length[3];
- uint8_t first_cert_length[3];
- uint8_t asn1_start[0];
+ uint8_t certificates[0];
} __attribute__ (( packed )) *certificate = data;
- uint8_t *cert = certificate->asn1_start;
- int offset = 0;
-
- /* FIXME */
- (void) len;
-
- if (asn1_next_obj(cert, &offset, ASN1_SEQUENCE) < 0 ||
- asn1_next_obj(cert, &offset, ASN1_SEQUENCE) < 0 ||
- asn1_skip_obj(cert, &offset, ASN1_EXPLICIT_TAG) ||
- asn1_skip_obj(cert, &offset, ASN1_INTEGER) ||
- asn1_skip_obj(cert, &offset, ASN1_SEQUENCE) ||
- asn1_skip_obj(cert, &offset, ASN1_SEQUENCE) ||
- asn1_skip_obj(cert, &offset, ASN1_SEQUENCE) ||
- asn1_skip_obj(cert, &offset, ASN1_SEQUENCE) ||
- asn1_next_obj(cert, &offset, ASN1_SEQUENCE) < 0 ||
- asn1_skip_obj(cert, &offset, ASN1_SEQUENCE) ||
- asn1_next_obj(cert, &offset, ASN1_BIT_STRING) < 0) {
- DBGC ( tls, "TLS %p invalid certificate\n", tls );
- DBGC_HD ( tls, cert + offset, 64 );
- return -EPERM;
- }
-
- offset++;
-
- if (asn1_next_obj(cert, &offset, ASN1_SEQUENCE) < 0) {
- DBGC ( tls, "TLS %p invalid certificate\n", tls );
- DBGC_HD ( tls, cert + offset, 64 );
- return -EPERM;
+ struct {
+ uint8_t length[3];
+ uint8_t certificate[0];
+ } __attribute__ (( packed )) *element =
+ ( ( void * ) certificate->certificates );
+ size_t elements_len = tls_uint24 ( certificate->length );
+ void *end = ( certificate->certificates + elements_len );
+ struct asn1_cursor cursor;
+ int rc;
+
+ /* Sanity check */
+ if ( end != ( data + len ) ) {
+ DBGC ( tls, "TLS %p received overlength Server Certificate\n",
+ tls );
+ DBGC_HD ( tls, data, len );
+ return -EINVAL;
}
-
- tls->rsa_mod_len = asn1_get_int(cert, &offset, &tls->rsa_mod);
- tls->rsa_pub_exp_len = asn1_get_int(cert, &offset, &tls->rsa_pub_exp);
-
- DBGC_HD ( tls, tls->rsa_mod, tls->rsa_mod_len );
- DBGC_HD ( tls, tls->rsa_pub_exp, tls->rsa_pub_exp_len );
- return 0;
+ /* Traverse certificate chain */
+ do {
+ cursor.data = element->certificate;
+ cursor.len = tls_uint24 ( element->length );
+ if ( ( cursor.data + cursor.len ) > end ) {
+ DBGC ( tls, "TLS %p received corrupt Server "
+ "Certificate\n", tls );
+ DBGC_HD ( tls, data, len );
+ return -EINVAL;
+ }
+
+ // HACK
+ if ( ( rc = x509_rsa_public_key ( &cursor,
+ &tls->rsa ) ) != 0 ) {
+ DBGC ( tls, "TLS %p cannot determine RSA public key: "
+ "%s\n", tls, strerror ( rc ) );
+ return rc;
+ }
+ return 0;
+
+ element = ( cursor.data + cursor.len );
+ } while ( element != end );
+
+ return -EINVAL;
}
/**
- * Receive new Server Hello Done record
+ * Receive new Server Hello Done handshake record
*
* @v tls TLS session
- * @v data Plaintext record
- * @v len Length of plaintext record
+ * @v data Plaintext handshake record
+ * @v len Length of plaintext handshake record
* @ret rc Return status code
*/
static int tls_new_server_hello_done ( struct tls_session *tls,
void *data, size_t len ) {
struct {
- uint32_t type_length;
char next[0];
} __attribute__ (( packed )) *hello_done = data;
void *end = hello_done->next;
@@ -910,11 +939,11 @@ static int tls_new_server_hello_done ( struct tls_session *tls,
}
/**
- * Receive new Finished record
+ * Receive new Finished handshake record
*
* @v tls TLS session
- * @v data Plaintext record
- * @v len Length of plaintext record
+ * @v data Plaintext handshake record
+ * @v len Length of plaintext handshake record
* @ret rc Return status code
*/
static int tls_new_finished ( struct tls_session *tls,
@@ -937,33 +966,47 @@ static int tls_new_finished ( struct tls_session *tls,
*/
static int tls_new_handshake ( struct tls_session *tls,
void *data, size_t len ) {
- uint8_t *type = data;
+ struct {
+ uint8_t type;
+ uint8_t length[3];
+ uint8_t payload[0];
+ } __attribute__ (( packed )) *handshake = data;
+ void *payload = &handshake->payload;
+ size_t payload_len = tls_uint24 ( handshake->length );
+ void *end = ( payload + payload_len );
int rc;
- switch ( *type ) {
+ /* Sanity check */
+ if ( end != ( data + len ) ) {
+ DBGC ( tls, "TLS %p received overlength Handshake\n", tls );
+ DBGC_HD ( tls, data, len );
+ return -EINVAL;
+ }
+
+ switch ( handshake->type ) {
case TLS_SERVER_HELLO:
- rc = tls_new_server_hello ( tls, data, len );
+ rc = tls_new_server_hello ( tls, payload, payload_len );
break;
case TLS_CERTIFICATE:
- rc = tls_new_certificate ( tls, data, len );
+ rc = tls_new_certificate ( tls, payload, payload_len );
break;
case TLS_SERVER_HELLO_DONE:
- rc = tls_new_server_hello_done ( tls, data, len );
+ rc = tls_new_server_hello_done ( tls, payload, payload_len );
break;
case TLS_FINISHED:
- rc = tls_new_finished ( tls, data, len );
+ rc = tls_new_finished ( tls, payload, payload_len );
break;
default:
DBGC ( tls, "TLS %p ignoring handshake type %d\n",
- tls, *type );
+ tls, handshake->type );
rc = 0;
break;
}
/* Add to handshake digest (except for Hello Requests, which
- * are explicitly excludede).
+ * are explicitly excluded).
*/
- if ( *type != TLS_HELLO_REQUEST )
+ if ( handshake->type != TLS_HELLO_REQUEST )
tls_add_handshake ( tls, data, len );
return rc;
@@ -1710,13 +1753,12 @@ int add_tls ( struct xfer_interface *xfer, struct xfer_interface **next ) {
tls_clear_cipher ( tls, &tls->tx_cipherspec_pending );
tls_clear_cipher ( tls, &tls->rx_cipherspec );
tls_clear_cipher ( tls, &tls->rx_cipherspec_pending );
- *( ( uint32_t * ) tls->client_random ) = 0; /* GMT Unix time */
- tls_generate_random ( ( tls->client_random + 4 ),
- ( sizeof ( tls->client_random ) - 4 ) );
- *( ( uint16_t * ) tls->pre_master_secret )
- = htons ( TLS_VERSION_TLS_1_0 );
- tls_generate_random ( ( tls->pre_master_secret + 2 ),
- ( sizeof ( tls->pre_master_secret ) - 2 ) );
+ tls->client_random.gmt_unix_time = 0;
+ tls_generate_random ( &tls->client_random.random,
+ ( sizeof ( tls->client_random.random ) ) );
+ tls->pre_master_secret.version = htons ( TLS_VERSION_TLS_1_0 );
+ tls_generate_random ( &tls->pre_master_secret.random,
+ ( sizeof ( tls->pre_master_secret.random ) ) );
digest_init ( &md5_algorithm, tls->handshake_md5_ctx );
digest_init ( &sha1_algorithm, tls->handshake_sha1_ctx );
tls->tx_state = TLS_TX_CLIENT_HELLO;
diff --git a/gpxe/src/net/udp.c b/gpxe/src/net/udp.c
index 407ea14d..c3a1eba3 100644
--- a/gpxe/src/net/udp.c
+++ b/gpxe/src/net/udp.c
@@ -182,13 +182,13 @@ static void udp_close ( struct udp_connection *udp, int rc ) {
*
* @v udp UDP connection
* @v iobuf I/O buffer
- * @v src_port Source port, or 0 to use default
+ * @v src Source address, or NULL to use default
* @v dest Destination address, or NULL to use default
* @v netdev Network device, or NULL to use default
* @ret rc Return status code
*/
static int udp_tx ( struct udp_connection *udp, struct io_buffer *iobuf,
- unsigned int src_port, struct sockaddr_tcpip *dest,
+ struct sockaddr_tcpip *src, struct sockaddr_tcpip *dest,
struct net_device *netdev ) {
struct udp_header *udphdr;
size_t len;
@@ -201,8 +201,8 @@ static int udp_tx ( struct udp_connection *udp, struct io_buffer *iobuf,
}
/* Fill in default values if not explicitly provided */
- if ( ! src_port )
- src_port = udp->local.st_port;
+ if ( ! src )
+ src = &udp->local;
if ( ! dest )
dest = &udp->peer;
@@ -210,7 +210,7 @@ static int udp_tx ( struct udp_connection *udp, struct io_buffer *iobuf,
udphdr = iob_push ( iobuf, sizeof ( *udphdr ) );
len = iob_len ( iobuf );
udphdr->dest = dest->st_port;
- udphdr->src = src_port;
+ udphdr->src = src->st_port;
udphdr->len = htons ( len );
udphdr->chksum = 0;
udphdr->chksum = tcpip_chksum ( udphdr, len );
@@ -221,7 +221,7 @@ static int udp_tx ( struct udp_connection *udp, struct io_buffer *iobuf,
ntohs ( udphdr->len ) );
/* Send it to the next layer for processing */
- if ( ( rc = tcpip_tx ( iobuf, &udp_protocol, dest, netdev,
+ if ( ( rc = tcpip_tx ( iobuf, &udp_protocol, src, dest, netdev,
&udphdr->chksum ) ) != 0 ) {
DBGC ( udp, "UDP %p could not transmit packet: %s\n",
udp, strerror ( rc ) );
@@ -328,8 +328,7 @@ static int udp_rx ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src,
memset ( &meta, 0, sizeof ( meta ) );
meta.src = ( struct sockaddr * ) st_src;
meta.dest = ( struct sockaddr * ) st_dest;
- rc = xfer_deliver_iob_meta ( &udp->xfer, iobuf, &meta );
- iobuf = NULL;
+ rc = xfer_deliver_iob_meta ( &udp->xfer, iob_disown ( iobuf ), &meta );
done:
free_iob ( iobuf );
@@ -391,7 +390,7 @@ static struct io_buffer * udp_alloc_iob ( struct xfer_interface *xfer,
*
* @v xfer Data transfer interface
* @v iobuf Datagram I/O buffer
- * @v meta Data transfer metadata, or NULL
+ * @v meta Data transfer metadata
* @ret rc Return status code
*/
static int udp_xfer_deliver_iob ( struct xfer_interface *xfer,
@@ -399,22 +398,10 @@ static int udp_xfer_deliver_iob ( struct xfer_interface *xfer,
struct xfer_metadata *meta ) {
struct udp_connection *udp =
container_of ( xfer, struct udp_connection, xfer );
- struct sockaddr_tcpip *src;
- struct sockaddr_tcpip *dest = NULL;
- struct net_device *netdev = NULL;
- unsigned int src_port = 0;
-
- /* Apply xfer metadata */
- if ( meta ) {
- src = ( struct sockaddr_tcpip * ) meta->src;
- if ( src )
- src_port = src->st_port;
- dest = ( struct sockaddr_tcpip * ) meta->dest;
- netdev = meta->netdev;
- }
/* Transmit data, if possible */
- udp_tx ( udp, iobuf, src_port, dest, netdev );
+ udp_tx ( udp, iobuf, ( ( struct sockaddr_tcpip * ) meta->src ),
+ ( ( struct sockaddr_tcpip * ) meta->dest ), meta->netdev );
return 0;
}
@@ -438,12 +425,13 @@ static struct xfer_interface_operations udp_xfer_operations = {
/** UDP socket opener */
struct socket_opener udp_socket_opener __socket_opener = {
- .semantics = SOCK_DGRAM,
+ .semantics = UDP_SOCK_DGRAM,
.family = AF_INET,
.open = udp_open,
};
-char UDP_SOCK_DGRAM[1];
+/** Linkage hack */
+int udp_sock_dgram = UDP_SOCK_DGRAM;
/**
* Open UDP URI
diff --git a/gpxe/src/net/udp/dhcp.c b/gpxe/src/net/udp/dhcp.c
index ab751cd5..ab843ce1 100644
--- a/gpxe/src/net/udp/dhcp.c
+++ b/gpxe/src/net/udp/dhcp.c
@@ -19,6 +19,7 @@
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
+#include <ctype.h>
#include <errno.h>
#include <assert.h>
#include <byteswap.h>
@@ -32,12 +33,12 @@
#include <gpxe/tcpip.h>
#include <gpxe/ip.h>
#include <gpxe/uuid.h>
-#include <gpxe/dhcp.h>
#include <gpxe/timer.h>
#include <gpxe/settings.h>
#include <gpxe/dhcp.h>
#include <gpxe/dhcpopts.h>
#include <gpxe/dhcppkt.h>
+#include <gpxe/features.h>
/** @file
*
@@ -45,6 +46,9 @@
*
*/
+struct dhcp_session;
+static int dhcp_tx ( struct dhcp_session *dhcp );
+
/**
* DHCP operation types
*
@@ -73,6 +77,8 @@ static uint8_t dhcp_request_options_data[] = {
DHCP_STRING ( 'P', 'X', 'E', 'C', 'l', 'i', 'e', 'n', 't', ':',
'A', 'r', 'c', 'h', ':', '0', '0', '0', '0', '0', ':',
'U', 'N', 'D', 'I', ':', '0', '0', '2', '0', '0', '1' ),
+ DHCP_USER_CLASS_ID,
+ DHCP_STRING ( 'g', 'P', 'X', 'E' ),
DHCP_PARAMETER_REQUEST_LIST,
DHCP_OPTION ( DHCP_SUBNET_MASK, DHCP_ROUTERS, DHCP_DNS_SERVERS,
DHCP_LOG_SERVERS, DHCP_HOST_NAME, DHCP_DOMAIN_NAME,
@@ -82,44 +88,28 @@ static uint8_t dhcp_request_options_data[] = {
DHCP_END
};
-/** Options common to all DHCP requests */
-static struct dhcp_options dhcp_request_options = {
- .data = dhcp_request_options_data,
- .max_len = sizeof ( dhcp_request_options_data ),
- .len = sizeof ( dhcp_request_options_data ),
-};
-
/** DHCP feature codes */
static uint8_t dhcp_features[0] __table_start ( uint8_t, dhcp_features );
static uint8_t dhcp_features_end[0] __table_end ( uint8_t, dhcp_features );
-/** DHCP network device descriptor */
-struct dhcp_netdev_desc {
- /** Bus type ID */
- uint8_t type;
- /** Vendor ID */
- uint16_t vendor;
- /** Device ID */
- uint16_t device;
-} __attribute__ (( packed ));
-
-/** DHCP client identifier */
-struct dhcp_client_id {
- /** Link-layer protocol */
- uint8_t ll_proto;
- /** Link-layer address */
- uint8_t ll_addr[MAX_LL_ADDR_LEN];
-} __attribute__ (( packed ));
-
-/** DHCP client UUID */
-struct dhcp_client_uuid {
- /** Identifier type */
- uint8_t type;
- /** UUID */
- union uuid uuid;
-} __attribute__ (( packed ));
-
-#define DHCP_CLIENT_UUID_TYPE 0
+/** Version number feature */
+FEATURE_VERSION ( VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH );
+
+/** DHCP server address setting */
+struct setting dhcp_server_setting __setting = {
+ .name = "dhcp-server",
+ .description = "DHCP server address",
+ .tag = DHCP_SERVER_IDENTIFIER,
+ .type = &setting_type_ipv4,
+};
+
+/** DHCP user class setting */
+struct setting user_class_setting __setting = {
+ .name = "user-class",
+ .description = "User class identifier",
+ .tag = DHCP_USER_CLASS_ID,
+ .type = &setting_type_string,
+};
/**
* Name a DHCP packet type
@@ -161,211 +151,658 @@ static uint32_t dhcp_xid ( struct net_device *netdev ) {
/****************************************************************************
*
- * DHCP settings
+ * DHCP session
*
*/
-/** A DHCP settings block */
-struct dhcp_settings {
+struct dhcp_session;
+
+/** DHCP session state operations */
+struct dhcp_session_state {
+ /** State name */
+ const char *name;
+ /**
+ * Construct transmitted packet
+ *
+ * @v dhcp DHCP session
+ * @v dhcppkt DHCP packet
+ * @v peer Destination address
+ */
+ int ( * tx ) ( struct dhcp_session *dhcp,
+ struct dhcp_packet *dhcppkt,
+ struct sockaddr_in *peer );
+ /** Handle received packet
+ *
+ * @v dhcp DHCP session
+ * @v dhcppkt DHCP packet
+ * @v peer DHCP server address
+ * @v msgtype DHCP message type
+ * @v server_id DHCP server ID
+ */
+ void ( * rx ) ( struct dhcp_session *dhcp,
+ struct dhcp_packet *dhcppkt,
+ struct sockaddr_in *peer,
+ uint8_t msgtype, struct in_addr server_id );
+ /** Handle timer expiry
+ *
+ * @v dhcp DHCP session
+ */
+ void ( * expired ) ( struct dhcp_session *dhcp );
+ /** Transmitted message type */
+ uint8_t tx_msgtype;
+ /** Apply minimum timeout */
+ uint8_t apply_min_timeout;
+};
+
+static struct dhcp_session_state dhcp_state_discover;
+static struct dhcp_session_state dhcp_state_request;
+static struct dhcp_session_state dhcp_state_proxy;
+static struct dhcp_session_state dhcp_state_pxebs;
+
+/** A DHCP session */
+struct dhcp_session {
/** Reference counter */
struct refcnt refcnt;
- /** DHCP packet */
- struct dhcp_packet dhcppkt;
- /** Setting interface */
- struct settings settings;
+ /** Job control interface */
+ struct job_interface job;
+ /** Data transfer interface */
+ struct xfer_interface xfer;
+
+ /** Network device being configured */
+ struct net_device *netdev;
+ /** Local socket address */
+ struct sockaddr_in local;
+ /** State of the session */
+ struct dhcp_session_state *state;
+
+ /** Offered IP address */
+ struct in_addr offer;
+ /** DHCP server */
+ struct in_addr server;
+ /** DHCP offer priority */
+ int priority;
+
+ /** ProxyDHCP protocol extensions should be ignored */
+ int no_pxedhcp;
+ /** ProxyDHCP server */
+ struct in_addr proxy_server;
+ /** ProxyDHCP server priority */
+ int proxy_priority;
+
+ /** PXE Boot Server type */
+ uint16_t pxe_type;
+ /** List of PXE Boot Servers to attempt */
+ struct in_addr *pxe_attempt;
+ /** List of PXE Boot Servers to accept */
+ struct in_addr *pxe_accept;
+
+ /** Retransmission timer */
+ struct retry_timer timer;
+ /** Start time of the current state (in ticks) */
+ unsigned long start;
};
/**
- * Increment reference count on DHCP settings block
+ * Free DHCP session
*
- * @v dhcpset DHCP settings block
- * @ret dhcpset DHCP settings block
+ * @v refcnt Reference counter
*/
-static inline __attribute__ (( always_inline )) struct dhcp_settings *
-dhcpset_get ( struct dhcp_settings *dhcpset ) {
- ref_get ( &dhcpset->refcnt );
- return dhcpset;
+static void dhcp_free ( struct refcnt *refcnt ) {
+ struct dhcp_session *dhcp =
+ container_of ( refcnt, struct dhcp_session, refcnt );
+
+ netdev_put ( dhcp->netdev );
+ free ( dhcp );
}
/**
- * Decrement reference count on DHCP settings block
+ * Mark DHCP session as complete
*
- * @v dhcpset DHCP settings block
+ * @v dhcp DHCP session
+ * @v rc Return status code
*/
-static inline __attribute__ (( always_inline )) void
-dhcpset_put ( struct dhcp_settings *dhcpset ) {
- ref_put ( &dhcpset->refcnt );
+static void dhcp_finished ( struct dhcp_session *dhcp, int rc ) {
+
+ /* Block futher incoming messages */
+ job_nullify ( &dhcp->job );
+ xfer_nullify ( &dhcp->xfer );
+
+ /* Stop retry timer */
+ stop_timer ( &dhcp->timer );
+
+ /* Free resources and close interfaces */
+ xfer_close ( &dhcp->xfer, rc );
+ job_done ( &dhcp->job, rc );
}
/**
- * Store value of DHCP setting
+ * Transition to new DHCP session state
*
- * @v settings Settings block
- * @v setting Setting to store
- * @v data Setting data, or NULL to clear setting
- * @v len Length of setting data
- * @ret rc Return status code
+ * @v dhcp DHCP session
+ * @v state New session state
*/
-static int dhcpset_store ( struct settings *settings, struct setting *setting,
- const void *data, size_t len ) {
- struct dhcp_settings *dhcpset =
- container_of ( settings, struct dhcp_settings, settings );
+static void dhcp_set_state ( struct dhcp_session *dhcp,
+ struct dhcp_session_state *state ) {
- return dhcppkt_store ( &dhcpset->dhcppkt, setting->tag, data, len );
+ DBGC ( dhcp, "DHCP %p entering %s state\n", dhcp, state->name );
+ dhcp->state = state;
+ dhcp->start = currticks();
+ stop_timer ( &dhcp->timer );
+ dhcp->timer.min_timeout =
+ ( state->apply_min_timeout ? DHCP_MIN_TIMEOUT : 0 );
+ dhcp->timer.max_timeout = DHCP_MAX_TIMEOUT;
+ start_timer_nodelay ( &dhcp->timer );
}
+/****************************************************************************
+ *
+ * DHCP state machine
+ *
+ */
+
/**
- * Fetch value of DHCP setting
+ * Construct transmitted packet for DHCP discovery
*
- * @v settings Settings block, or NULL to search all blocks
- * @v setting Setting to fetch
- * @v data Buffer to fill with setting data
- * @v len Length of buffer
- * @ret len Length of setting data, or negative error
+ * @v dhcp DHCP session
+ * @v dhcppkt DHCP packet
+ * @v peer Destination address
*/
-static int dhcpset_fetch ( struct settings *settings, struct setting *setting,
- void *data, size_t len ) {
- struct dhcp_settings *dhcpset =
- container_of ( settings, struct dhcp_settings, settings );
+static int dhcp_discovery_tx ( struct dhcp_session *dhcp,
+ struct dhcp_packet *dhcppkt __unused,
+ struct sockaddr_in *peer ) {
+
+ DBGC ( dhcp, "DHCP %p DHCPDISCOVER\n", dhcp );
+
+ /* Set server address */
+ peer->sin_addr.s_addr = INADDR_BROADCAST;
+ peer->sin_port = htons ( BOOTPS_PORT );
- return dhcppkt_fetch ( &dhcpset->dhcppkt, setting->tag, data, len );
+ return 0;
}
-/** DHCP settings operations */
-static struct settings_operations dhcpset_settings_operations = {
- .store = dhcpset_store,
- .fetch = dhcpset_fetch,
-};
+/**
+ * Handle received packet during DHCP discovery
+ *
+ * @v dhcp DHCP session
+ * @v dhcppkt DHCP packet
+ * @v peer DHCP server address
+ * @v msgtype DHCP message type
+ * @v server_id DHCP server ID
+ */
+static void dhcp_discovery_rx ( struct dhcp_session *dhcp,
+ struct dhcp_packet *dhcppkt,
+ struct sockaddr_in *peer, uint8_t msgtype,
+ struct in_addr server_id ) {
+ struct in_addr ip;
+ char vci[9]; /* "PXEClient" */
+ int vci_len;
+ int has_pxeclient;
+ int8_t priority = 0;
+ uint8_t no_pxedhcp = 0;
+ unsigned long elapsed;
+
+ DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp,
+ dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ),
+ ntohs ( peer->sin_port ) );
+ if ( server_id.s_addr != peer->sin_addr.s_addr )
+ DBGC ( dhcp, " (%s)", inet_ntoa ( server_id ) );
+
+ /* Identify offered IP address */
+ ip = dhcppkt->dhcphdr->yiaddr;
+ if ( ip.s_addr )
+ DBGC ( dhcp, " for %s", inet_ntoa ( ip ) );
+
+ /* Identify "PXEClient" vendor class */
+ vci_len = dhcppkt_fetch ( dhcppkt, DHCP_VENDOR_CLASS_ID,
+ vci, sizeof ( vci ) );
+ has_pxeclient = ( ( vci_len >= ( int ) sizeof ( vci ) ) &&
+ ( strncmp ( "PXEClient", vci, sizeof (vci) ) == 0 ));
+ if ( has_pxeclient )
+ DBGC ( dhcp, " pxe" );
+
+ /* Identify priority */
+ dhcppkt_fetch ( dhcppkt, DHCP_EB_PRIORITY, &priority,
+ sizeof ( priority ) );
+ if ( priority )
+ DBGC ( dhcp, " pri %d", priority );
+
+ /* Identify ignore-PXE flag */
+ dhcppkt_fetch ( dhcppkt, DHCP_EB_NO_PXEDHCP, &no_pxedhcp,
+ sizeof ( no_pxedhcp ) );
+ if ( no_pxedhcp )
+ DBGC ( dhcp, " nopxe" );
+ DBGC ( dhcp, "\n" );
+
+ /* Select as DHCP offer, if applicable */
+ if ( ip.s_addr && ( peer->sin_port == htons ( BOOTPS_PORT ) ) &&
+ ( ( msgtype == DHCPOFFER ) || ( ! msgtype /* BOOTP */ ) ) &&
+ ( priority >= dhcp->priority ) ) {
+ dhcp->offer = ip;
+ dhcp->server = server_id;
+ dhcp->priority = priority;
+ dhcp->no_pxedhcp = no_pxedhcp;
+ }
+
+ /* Select as ProxyDHCP offer, if applicable */
+ if ( has_pxeclient && ( msgtype == DHCPOFFER ) &&
+ ( priority >= dhcp->proxy_priority ) ) {
+ dhcp->proxy_server = server_id;
+ dhcp->proxy_priority = priority;
+ }
+
+ /* We can exit the discovery state when we have a valid
+ * DHCPOFFER, and either:
+ *
+ * o The DHCPOFFER instructs us to ignore ProxyDHCPOFFERs, or
+ * o We have a valid ProxyDHCPOFFER, or
+ * o We have allowed sufficient time for ProxyDHCPOFFERs.
+ */
+
+ /* If we don't yet have a DHCPOFFER, do nothing */
+ if ( ! dhcp->offer.s_addr )
+ return;
+
+ /* If we can't yet transition to DHCPREQUEST, do nothing */
+ elapsed = ( currticks() - dhcp->start );
+ if ( ! ( dhcp->no_pxedhcp || dhcp->proxy_server.s_addr ||
+ ( elapsed > PROXYDHCP_MAX_TIMEOUT ) ) )
+ return;
+
+ /* Transition to DHCPREQUEST */
+ dhcp_set_state ( dhcp, &dhcp_state_request );
+}
/**
- * Create DHCP setting block
+ * Handle timer expiry during DHCP discovery
*
- * @v dhcphdr DHCP packet
- * @v len Length of DHCP packet
- * @ret dhcpset DHCP settings block
+ * @v dhcp DHCP session
*/
-static struct dhcp_settings * dhcpset_create ( const struct dhcphdr *dhcphdr,
- size_t len ) {
- struct dhcp_settings *dhcpset;
- void *data;
-
- dhcpset = zalloc ( sizeof ( *dhcpset ) + len );
- if ( dhcpset ) {
- data = ( ( ( void * ) dhcpset ) + sizeof ( *dhcpset ) );
- memcpy ( data, dhcphdr, len );
- dhcppkt_init ( &dhcpset->dhcppkt, data, len );
- settings_init ( &dhcpset->settings,
- &dhcpset_settings_operations, &dhcpset->refcnt,
- DHCP_SETTINGS_NAME );
+static void dhcp_discovery_expired ( struct dhcp_session *dhcp ) {
+ unsigned long elapsed = ( currticks() - dhcp->start );
+
+ /* Give up waiting for ProxyDHCP before we reach the failure point */
+ if ( dhcp->offer.s_addr && ( elapsed > PROXYDHCP_MAX_TIMEOUT ) ) {
+ dhcp_set_state ( dhcp, &dhcp_state_request );
+ return;
}
- return dhcpset;
+
+ /* Otherwise, retransmit current packet */
+ dhcp_tx ( dhcp );
}
-/** DHCP server address setting */
-struct setting dhcp_server_setting __setting = {
- .name = "dhcp-server",
- .description = "DHCP server address",
- .tag = DHCP_SERVER_IDENTIFIER,
- .type = &setting_type_ipv4,
+/** DHCP discovery state operations */
+static struct dhcp_session_state dhcp_state_discover = {
+ .name = "discovery",
+ .tx = dhcp_discovery_tx,
+ .rx = dhcp_discovery_rx,
+ .expired = dhcp_discovery_expired,
+ .tx_msgtype = DHCPDISCOVER,
+ .apply_min_timeout = 1,
};
-/****************************************************************************
+/**
+ * Construct transmitted packet for DHCP request
*
- * DHCP session
+ * @v dhcp DHCP session
+ * @v dhcppkt DHCP packet
+ * @v peer Destination address
+ */
+static int dhcp_request_tx ( struct dhcp_session *dhcp,
+ struct dhcp_packet *dhcppkt,
+ struct sockaddr_in *peer ) {
+ int rc;
+
+ DBGC ( dhcp, "DHCP %p DHCPREQUEST to %s:%d",
+ dhcp, inet_ntoa ( dhcp->server ), BOOTPS_PORT );
+ DBGC ( dhcp, " for %s\n", inet_ntoa ( dhcp->offer ) );
+
+ /* Set server ID */
+ if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_SERVER_IDENTIFIER,
+ &dhcp->server,
+ sizeof ( dhcp->server ) ) ) != 0 )
+ return rc;
+
+ /* Set requested IP address */
+ if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_REQUESTED_ADDRESS,
+ &dhcp->offer,
+ sizeof ( dhcp->offer ) ) ) != 0 )
+ return rc;
+
+ /* Set server address */
+ peer->sin_addr.s_addr = INADDR_BROADCAST;
+ peer->sin_port = htons ( BOOTPS_PORT );
+
+ return 0;
+}
+
+/**
+ * Handle received packet during DHCP request
+ *
+ * @v dhcp DHCP session
+ * @v dhcppkt DHCP packet
+ * @v peer DHCP server address
+ * @v msgtype DHCP message type
+ * @v server_id DHCP server ID
+ */
+static void dhcp_request_rx ( struct dhcp_session *dhcp,
+ struct dhcp_packet *dhcppkt,
+ struct sockaddr_in *peer, uint8_t msgtype,
+ struct in_addr server_id ) {
+ struct in_addr ip;
+ struct settings *parent;
+ int rc;
+
+ DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp,
+ dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ),
+ ntohs ( peer->sin_port ) );
+ if ( server_id.s_addr != peer->sin_addr.s_addr )
+ DBGC ( dhcp, " (%s)", inet_ntoa ( server_id ) );
+
+ /* Identify leased IP address */
+ ip = dhcppkt->dhcphdr->yiaddr;
+ if ( ip.s_addr )
+ DBGC ( dhcp, " for %s", inet_ntoa ( ip ) );
+ DBGC ( dhcp, "\n" );
+
+ /* Filter out unacceptable responses */
+ if ( peer->sin_port != htons ( BOOTPS_PORT ) )
+ return;
+ if ( msgtype /* BOOTP */ && ( msgtype != DHCPACK ) )
+ return;
+ if ( server_id.s_addr != dhcp->server.s_addr )
+ return;
+
+ /* Record assigned address */
+ dhcp->local.sin_addr = ip;
+
+ /* Register settings */
+ parent = netdev_settings ( dhcp->netdev );
+ if ( ( rc = register_settings ( &dhcppkt->settings, parent ) ) != 0 ){
+ DBGC ( dhcp, "DHCP %p could not register settings: %s\n",
+ dhcp, strerror ( rc ) );
+ dhcp_finished ( dhcp, rc );
+ return;
+ }
+
+ /* Start ProxyDHCPREQUEST if applicable */
+ if ( dhcp->proxy_server.s_addr && ( ! dhcp->no_pxedhcp ) ) {
+ dhcp_set_state ( dhcp, &dhcp_state_proxy );
+ return;
+ }
+
+ /* Terminate DHCP */
+ dhcp_finished ( dhcp, 0 );
+}
+
+/**
+ * Handle timer expiry during DHCP discovery
*
+ * @v dhcp DHCP session
*/
+static void dhcp_request_expired ( struct dhcp_session *dhcp ) {
+
+ /* Retransmit current packet */
+ dhcp_tx ( dhcp );
+}
-/** DHCP session states */
-enum dhcp_session_state {
- /** Sending DHCPDISCOVERs, collecting DHCPOFFERs and ProxyDHCPOFFERs */
- DHCP_STATE_DISCOVER = 0,
- /** Sending DHCPREQUESTs, waiting for DHCPACK */
- DHCP_STATE_REQUEST,
- /** Sending ProxyDHCPREQUESTs, waiting for ProxyDHCPACK */
- DHCP_STATE_PROXYREQUEST,
+/** DHCP request state operations */
+static struct dhcp_session_state dhcp_state_request = {
+ .name = "request",
+ .tx = dhcp_request_tx,
+ .rx = dhcp_request_rx,
+ .expired = dhcp_request_expired,
+ .tx_msgtype = DHCPREQUEST,
+ .apply_min_timeout = 0,
};
/**
- * Name a DHCP session state
+ * Construct transmitted packet for ProxyDHCP request
+ *
+ * @v dhcp DHCP session
+ * @v dhcppkt DHCP packet
+ * @v peer Destination address
+ */
+static int dhcp_proxy_tx ( struct dhcp_session *dhcp,
+ struct dhcp_packet *dhcppkt,
+ struct sockaddr_in *peer ) {
+ int rc;
+
+ DBGC ( dhcp, "DHCP %p ProxyDHCP REQUEST to %s:%d\n",
+ dhcp, inet_ntoa ( dhcp->proxy_server ), PXE_PORT );
+
+ /* Set server ID */
+ if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_SERVER_IDENTIFIER,
+ &dhcp->proxy_server,
+ sizeof ( dhcp->proxy_server ) ) ) != 0 )
+ return rc;
+
+ /* Set server address */
+ peer->sin_addr = dhcp->proxy_server;
+ peer->sin_port = htons ( PXE_PORT );
+
+ return 0;
+}
+
+/**
+ * Handle received packet during ProxyDHCP request
*
- * @v state DHCP session state
- * @ret string DHCP session state name
+ * @v dhcp DHCP session
+ * @v dhcppkt DHCP packet
+ * @v peer DHCP server address
+ * @v msgtype DHCP message type
+ * @v server_id DHCP server ID
*/
-static inline const char * dhcp_state_name ( enum dhcp_session_state state ) {
- switch ( state ) {
- case DHCP_STATE_DISCOVER: return "DHCPDISCOVER";
- case DHCP_STATE_REQUEST: return "DHCPREQUEST";
- case DHCP_STATE_PROXYREQUEST: return "ProxyDHCPREQUEST";
- default: return "<invalid>";
+static void dhcp_proxy_rx ( struct dhcp_session *dhcp,
+ struct dhcp_packet *dhcppkt,
+ struct sockaddr_in *peer, uint8_t msgtype,
+ struct in_addr server_id ) {
+ int rc;
+
+ DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp,
+ dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ),
+ ntohs ( peer->sin_port ) );
+ if ( server_id.s_addr != peer->sin_addr.s_addr )
+ DBGC ( dhcp, " (%s)", inet_ntoa ( server_id ) );
+ DBGC ( dhcp, "\n" );
+
+ /* Filter out unacceptable responses */
+ if ( peer->sin_port != htons ( PXE_PORT ) )
+ return;
+ if ( msgtype != DHCPACK )
+ return;
+ if ( server_id.s_addr /* Linux PXE server omits server ID */ &&
+ ( server_id.s_addr != dhcp->proxy_server.s_addr ) )
+ return;
+
+ /* Register settings */
+ dhcppkt->settings.name = PROXYDHCP_SETTINGS_NAME;
+ if ( ( rc = register_settings ( &dhcppkt->settings, NULL ) ) != 0 ) {
+ DBGC ( dhcp, "DHCP %p could not register settings: %s\n",
+ dhcp, strerror ( rc ) );
+ dhcp_finished ( dhcp, rc );
+ return;
}
+
+ /* Terminate DHCP */
+ dhcp_finished ( dhcp, 0 );
}
-/** A DHCP session */
-struct dhcp_session {
- /** Reference counter */
- struct refcnt refcnt;
- /** Job control interface */
- struct job_interface job;
- /** Data transfer interface */
- struct xfer_interface xfer;
+/**
+ * Handle timer expiry during ProxyDHCP request
+ *
+ * @v dhcp DHCP session
+ */
+static void dhcp_proxy_expired ( struct dhcp_session *dhcp ) {
+ unsigned long elapsed = ( currticks() - dhcp->start );
- /** Network device being configured */
- struct net_device *netdev;
+ /* Give up waiting for ProxyDHCP before we reach the failure point */
+ if ( elapsed > PROXYDHCP_MAX_TIMEOUT ) {
+ dhcp_finished ( dhcp, 0 );
+ return;
+ }
- /** State of the session
- *
- * This is a value for the @c DHCP_MESSAGE_TYPE option
- * (e.g. @c DHCPDISCOVER).
- */
- enum dhcp_session_state state;
- /** DHCPOFFER obtained during DHCPDISCOVER */
- struct dhcp_settings *dhcpoffer;
- /** ProxyDHCPOFFER obtained during DHCPDISCOVER */
- struct dhcp_settings *proxydhcpoffer;
- /** Retransmission timer */
- struct retry_timer timer;
- /** Start time of the current state (in ticks) */
- unsigned long start;
+ /* Retransmit current packet */
+ dhcp_tx ( dhcp );
+}
+
+/** ProxyDHCP request state operations */
+static struct dhcp_session_state dhcp_state_proxy = {
+ .name = "ProxyDHCP",
+ .tx = dhcp_proxy_tx,
+ .rx = dhcp_proxy_rx,
+ .expired = dhcp_proxy_expired,
+ .tx_msgtype = DHCPREQUEST,
+ .apply_min_timeout = 0,
};
/**
- * Free DHCP session
+ * Construct transmitted packet for PXE Boot Server Discovery
*
- * @v refcnt Reference counter
+ * @v dhcp DHCP session
+ * @v dhcppkt DHCP packet
+ * @v peer Destination address
*/
-static void dhcp_free ( struct refcnt *refcnt ) {
- struct dhcp_session *dhcp =
- container_of ( refcnt, struct dhcp_session, refcnt );
+static int dhcp_pxebs_tx ( struct dhcp_session *dhcp,
+ struct dhcp_packet *dhcppkt,
+ struct sockaddr_in *peer ) {
+ struct dhcp_pxe_boot_menu_item menu_item = { 0, 0 };
+ int rc;
- netdev_put ( dhcp->netdev );
- dhcpset_put ( dhcp->dhcpoffer );
- dhcpset_put ( dhcp->proxydhcpoffer );
- free ( dhcp );
+ DBGC ( dhcp, "DHCP %p PXEBS REQUEST to %s:%d for type %d\n",
+ dhcp, inet_ntoa ( *(dhcp->pxe_attempt) ), PXE_PORT,
+ ntohs ( dhcp->pxe_type ) );
+
+ /* Set boot menu item */
+ menu_item.type = dhcp->pxe_type;
+ if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_PXE_BOOT_MENU_ITEM,
+ &menu_item, sizeof ( menu_item ) ) ) != 0 )
+ return rc;
+
+ /* Set server address */
+ peer->sin_addr = *(dhcp->pxe_attempt);
+ peer->sin_port = htons ( PXE_PORT );
+
+ return 0;
}
/**
- * Mark DHCP session as complete
+ * Check to see if PXE Boot Server address is acceptable
*
* @v dhcp DHCP session
- * @v rc Return status code
+ * @v bs Boot Server address
+ * @ret accept Boot Server is acceptable
*/
-static void dhcp_finished ( struct dhcp_session *dhcp, int rc ) {
+static int dhcp_pxebs_accept ( struct dhcp_session *dhcp,
+ struct in_addr bs ) {
+ struct in_addr *accept;
+
+ /* Accept if we have no acceptance filter */
+ if ( ! dhcp->pxe_accept )
+ return 1;
+
+ /* Scan through acceptance list */
+ for ( accept = dhcp->pxe_accept ; accept->s_addr ; accept++ ) {
+ if ( accept->s_addr == bs.s_addr )
+ return 1;
+ }
- /* Block futher incoming messages */
- job_nullify ( &dhcp->job );
- xfer_nullify ( &dhcp->xfer );
+ DBGC ( dhcp, "DHCP %p rejecting server %s\n",
+ dhcp, inet_ntoa ( bs ) );
+ return 0;
+}
- /* Stop retry timer */
- stop_timer ( &dhcp->timer );
+/**
+ * Handle received packet during PXE Boot Server Discovery
+ *
+ * @v dhcp DHCP session
+ * @v dhcppkt DHCP packet
+ * @v peer DHCP server address
+ * @v msgtype DHCP message type
+ * @v server_id DHCP server ID
+ */
+static void dhcp_pxebs_rx ( struct dhcp_session *dhcp,
+ struct dhcp_packet *dhcppkt,
+ struct sockaddr_in *peer, uint8_t msgtype,
+ struct in_addr server_id ) {
+ struct dhcp_pxe_boot_menu_item menu_item = { 0, 0 };
+ int rc;
- /* Free resources and close interfaces */
- xfer_close ( &dhcp->xfer, rc );
- job_done ( &dhcp->job, rc );
+ DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp,
+ dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ),
+ ntohs ( peer->sin_port ) );
+ if ( server_id.s_addr != peer->sin_addr.s_addr )
+ DBGC ( dhcp, " (%s)", inet_ntoa ( server_id ) );
+
+ /* Identify boot menu item */
+ dhcppkt_fetch ( dhcppkt, DHCP_PXE_BOOT_MENU_ITEM,
+ &menu_item, sizeof ( menu_item ) );
+ if ( menu_item.type )
+ DBGC ( dhcp, " for type %d", ntohs ( menu_item.type ) );
+ DBGC ( dhcp, "\n" );
+
+ /* Filter out unacceptable responses */
+ if ( peer->sin_port != htons ( PXE_PORT ) )
+ return;
+ if ( msgtype != DHCPACK )
+ return;
+ if ( menu_item.type != dhcp->pxe_type )
+ return;
+ if ( ! dhcp_pxebs_accept ( dhcp, ( server_id.s_addr ?
+ server_id : peer->sin_addr ) ) )
+ return;
+
+ /* Register settings */
+ dhcppkt->settings.name = PXEBS_SETTINGS_NAME;
+ if ( ( rc = register_settings ( &dhcppkt->settings, NULL ) ) != 0 ) {
+ DBGC ( dhcp, "DHCP %p could not register settings: %s\n",
+ dhcp, strerror ( rc ) );
+ dhcp_finished ( dhcp, rc );
+ return;
+ }
+
+ /* Terminate DHCP */
+ dhcp_finished ( dhcp, 0 );
}
+/**
+ * Handle timer expiry during PXE Boot Server Discovery
+ *
+ * @v dhcp DHCP session
+ */
+static void dhcp_pxebs_expired ( struct dhcp_session *dhcp ) {
+ unsigned long elapsed = ( currticks() - dhcp->start );
+
+ /* Give up waiting before we reach the failure point, and fail
+ * over to the next server in the attempt list
+ */
+ if ( elapsed > PXEBS_MAX_TIMEOUT ) {
+ dhcp->pxe_attempt++;
+ if ( dhcp->pxe_attempt->s_addr ) {
+ dhcp_set_state ( dhcp, &dhcp_state_pxebs );
+ return;
+ } else {
+ dhcp_finished ( dhcp, -ETIMEDOUT );
+ return;
+ }
+ }
+
+ /* Retransmit current packet */
+ dhcp_tx ( dhcp );
+}
+
+/** PXE Boot Server Discovery state operations */
+static struct dhcp_session_state dhcp_state_pxebs = {
+ .name = "PXEBS",
+ .tx = dhcp_pxebs_tx,
+ .rx = dhcp_pxebs_rx,
+ .expired = dhcp_pxebs_expired,
+ .tx_msgtype = DHCPREQUEST,
+ .apply_min_timeout = 1,
+};
+
/****************************************************************************
*
- * Data transfer interface
+ * Packet construction
*
*/
@@ -376,24 +813,23 @@ static void dhcp_finished ( struct dhcp_session *dhcp, int rc ) {
* @v netdev Network device
* @v msgtype DHCP message type
* @v options Initial options to include (or NULL)
+ * @v options_len Length of initial options
* @v data Buffer for DHCP packet
* @v max_len Size of DHCP packet buffer
* @ret rc Return status code
*
- * Creates a DHCP packet in the specified buffer, and fills out a @c
- * dhcp_packet structure.
+ * Creates a DHCP packet in the specified buffer, and initialise a
+ * DHCP packet structure.
*/
int dhcp_create_packet ( struct dhcp_packet *dhcppkt,
struct net_device *netdev, uint8_t msgtype,
- struct dhcp_options *options,
+ const void *options, size_t options_len,
void *data, size_t max_len ) {
struct dhcphdr *dhcphdr = data;
- size_t options_len;
unsigned int hlen;
int rc;
/* Sanity check */
- options_len = ( options ? options->len : 0 );
if ( max_len < ( sizeof ( *dhcphdr ) + options_len ) )
return -ENOSPC;
@@ -413,7 +849,7 @@ int dhcp_create_packet ( struct dhcp_packet *dhcppkt,
}
dhcphdr->hlen = hlen;
memcpy ( dhcphdr->chaddr, netdev->ll_addr, hlen );
- memcpy ( dhcphdr->options, options->data, options_len );
+ memcpy ( dhcphdr->options, options, options_len );
/* Initialise DHCP packet structure */
memset ( dhcppkt, 0, sizeof ( *dhcppkt ) );
@@ -432,30 +868,32 @@ int dhcp_create_packet ( struct dhcp_packet *dhcppkt,
*
* @v dhcppkt DHCP packet structure to fill in
* @v netdev Network device
+ * @v msgtype DHCP message type
* @v ciaddr Client IP address
- * @v offer DHCP offer, if applicable
* @v data Buffer for DHCP packet
* @v max_len Size of DHCP packet buffer
* @ret rc Return status code
+ *
+ * Creates a DHCP request packet in the specified buffer, and
+ * initialise a DHCP packet structure.
*/
int dhcp_create_request ( struct dhcp_packet *dhcppkt,
- struct net_device *netdev, struct in_addr ciaddr,
- struct dhcp_packet *offer,
- void *data, size_t max_len ) {
+ struct net_device *netdev, unsigned int msgtype,
+ struct in_addr ciaddr, void *data, size_t max_len ) {
struct device_description *desc = &netdev->dev->desc;
struct dhcp_netdev_desc dhcp_desc;
struct dhcp_client_id client_id;
struct dhcp_client_uuid client_uuid;
- unsigned int msgtype;
size_t dhcp_features_len;
size_t ll_addr_len;
+ ssize_t len;
int rc;
/* Create DHCP packet */
- msgtype = ( offer ? DHCPREQUEST : DHCPDISCOVER );
if ( ( rc = dhcp_create_packet ( dhcppkt, netdev, msgtype,
- &dhcp_request_options, data,
- max_len ) ) != 0 ) {
+ dhcp_request_options_data,
+ sizeof ( dhcp_request_options_data ),
+ data, max_len ) ) != 0 ) {
DBG ( "DHCP could not create DHCP packet: %s\n",
strerror ( rc ) );
return rc;
@@ -464,32 +902,6 @@ int dhcp_create_request ( struct dhcp_packet *dhcppkt,
/* Set client IP address */
dhcppkt->dhcphdr->ciaddr = ciaddr;
- /* Copy any required options from previous server repsonse */
- if ( offer ) {
- struct in_addr server = { 0 };
- struct in_addr *ip = &offer->dhcphdr->yiaddr;
-
- /* Copy server identifier, if present */
- if ( ( dhcppkt_fetch ( offer, DHCP_SERVER_IDENTIFIER, &server,
- sizeof ( server ) ) >= 0 ) &&
- ( ( rc = dhcppkt_store ( dhcppkt, DHCP_SERVER_IDENTIFIER,
- &server,
- sizeof ( server ) ) ) != 0 ) ) {
- DBG ( "DHCP could not set server ID: %s\n",
- strerror ( rc ) );
- return rc;
- }
-
- /* Copy requested IP address, if present */
- if ( ( ip->s_addr != 0 ) &&
- ( ( rc = dhcppkt_store ( dhcppkt, DHCP_REQUESTED_ADDRESS,
- ip, sizeof ( *ip ) ) ) != 0 ) ) {
- DBG ( "DHCP could not set requested address: %s\n",
- strerror ( rc ) );
- return rc;
- }
- }
-
/* Add options to identify the feature list */
dhcp_features_len = ( dhcp_features_end - dhcp_features );
if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_EB_ENCAP, dhcp_features,
@@ -526,8 +938,8 @@ int dhcp_create_request ( struct dhcp_packet *dhcppkt,
/* Add client UUID, if we have one. Required for PXE. */
client_uuid.type = DHCP_CLIENT_UUID_TYPE;
- if ( ( rc = fetch_uuid_setting ( NULL, &uuid_setting,
- &client_uuid.uuid ) ) >= 0 ) {
+ if ( ( len = fetch_uuid_setting ( NULL, &uuid_setting,
+ &client_uuid.uuid ) ) >= 0 ) {
if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_CLIENT_UUID,
&client_uuid,
sizeof ( client_uuid ) ) ) != 0 ) {
@@ -537,9 +949,29 @@ int dhcp_create_request ( struct dhcp_packet *dhcppkt,
}
}
+ /* Add user class, if we have one. */
+ if ( ( len = fetch_setting_len ( NULL, &user_class_setting ) ) >= 0 ) {
+ char user_class[len];
+ fetch_setting ( NULL, &user_class_setting, user_class,
+ sizeof ( user_class ) );
+ if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_USER_CLASS_ID,
+ &user_class,
+ sizeof ( user_class ) ) ) != 0 ) {
+ DBG ( "DHCP could not set user class: %s\n",
+ strerror ( rc ) );
+ return rc;
+ }
+ }
+
return 0;
}
+/****************************************************************************
+ *
+ * Data transfer interface
+ *
+ */
+
/**
* Transmit DHCP request
*
@@ -547,18 +979,17 @@ int dhcp_create_request ( struct dhcp_packet *dhcppkt,
* @ret rc Return status code
*/
static int dhcp_tx ( struct dhcp_session *dhcp ) {
- static struct sockaddr_in proxydhcp_server = {
+ static struct sockaddr_in peer = {
.sin_family = AF_INET,
- .sin_port = htons ( PROXYDHCP_PORT ),
};
struct xfer_metadata meta = {
.netdev = dhcp->netdev,
+ .src = ( struct sockaddr * ) &dhcp->local,
+ .dest = ( struct sockaddr * ) &peer,
};
struct io_buffer *iobuf;
+ uint8_t msgtype = dhcp->state->tx_msgtype;
struct dhcp_packet dhcppkt;
- struct dhcp_packet *offer = NULL;
- struct in_addr ciaddr = { 0 };
- int check_len;
int rc;
/* Start retry timer. Do this first so that failures to
@@ -566,54 +997,31 @@ static int dhcp_tx ( struct dhcp_session *dhcp ) {
*/
start_timer ( &dhcp->timer );
- /* Determine packet contents based on current state */
- switch ( dhcp->state ) {
- case DHCP_STATE_DISCOVER:
- DBGC ( dhcp, "DHCP %p transmitting DHCPDISCOVER\n", dhcp );
- break;
- case DHCP_STATE_REQUEST:
- DBGC ( dhcp, "DHCP %p transmitting DHCPREQUEST\n", dhcp );
- assert ( dhcp->dhcpoffer );
- offer = &dhcp->dhcpoffer->dhcppkt;
- break;
- case DHCP_STATE_PROXYREQUEST:
- DBGC ( dhcp, "DHCP %p transmitting ProxyDHCPREQUEST\n", dhcp );
- assert ( dhcp->dhcpoffer );
- assert ( dhcp->proxydhcpoffer );
- offer = &dhcp->proxydhcpoffer->dhcppkt;
- ciaddr = dhcp->dhcpoffer->dhcppkt.dhcphdr->yiaddr;
- check_len = dhcppkt_fetch ( offer, DHCP_SERVER_IDENTIFIER,
- &proxydhcp_server.sin_addr,
- sizeof(proxydhcp_server.sin_addr));
- meta.dest = ( struct sockaddr * ) &proxydhcp_server;
- assert ( ciaddr.s_addr != 0 );
- assert ( proxydhcp_server.sin_addr.s_addr != 0 );
- assert ( check_len == sizeof ( proxydhcp_server.sin_addr ) );
- break;
- default:
- assert ( 0 );
- break;
- }
-
/* Allocate buffer for packet */
iobuf = xfer_alloc_iob ( &dhcp->xfer, DHCP_MIN_LEN );
if ( ! iobuf )
return -ENOMEM;
- /* Create DHCP packet in temporary buffer */
- if ( ( rc = dhcp_create_request ( &dhcppkt, dhcp->netdev,
- ciaddr, offer, iobuf->data,
+ /* Create basic DHCP packet in temporary buffer */
+ if ( ( rc = dhcp_create_request ( &dhcppkt, dhcp->netdev, msgtype,
+ dhcp->local.sin_addr, iobuf->data,
iob_tailroom ( iobuf ) ) ) != 0 ) {
DBGC ( dhcp, "DHCP %p could not construct DHCP request: %s\n",
dhcp, strerror ( rc ) );
goto done;
}
+ /* Fill in packet based on current state */
+ if ( ( rc = dhcp->state->tx ( dhcp, &dhcppkt, &peer ) ) != 0 ) {
+ DBGC ( dhcp, "DHCP %p could not fill DHCP request: %s\n",
+ dhcp, strerror ( rc ) );
+ goto done;
+ }
+
/* Transmit the packet */
iob_put ( iobuf, dhcppkt.len );
- rc = xfer_deliver_iob_meta ( &dhcp->xfer, iobuf, &meta );
- iobuf = NULL;
- if ( rc != 0 ) {
+ if ( ( rc = xfer_deliver_iob_meta ( &dhcp->xfer, iob_disown ( iobuf ),
+ &meta ) ) != 0 ) {
DBGC ( dhcp, "DHCP %p could not transmit UDP packet: %s\n",
dhcp, strerror ( rc ) );
goto done;
@@ -625,246 +1033,6 @@ static int dhcp_tx ( struct dhcp_session *dhcp ) {
}
/**
- * Transition to new DHCP session state
- *
- * @v dhcp DHCP session
- * @v state New session state
- */
-static void dhcp_set_state ( struct dhcp_session *dhcp,
- enum dhcp_session_state state ) {
- DBGC ( dhcp, "DHCP %p entering %s state\n",
- dhcp, dhcp_state_name ( state ) );
- dhcp->state = state;
- dhcp->start = currticks();
- start_timer_nodelay ( &dhcp->timer );
-}
-
-/**
- * Store received DHCPOFFER
- *
- * @v dhcp DHCP session
- * @v dhcpoffer Received DHCPOFFER
- * @v stored_dhcpoffer Location to store DHCPOFFER
- *
- * The DHCPOFFER will be stored in place of the existing stored
- * DHCPOFFER if its priority is equal to or greater than the stored
- * DHCPOFFER.
- */
-static void dhcp_store_dhcpoffer ( struct dhcp_session *dhcp,
- struct dhcp_settings *dhcpoffer,
- struct dhcp_settings **stored_dhcpoffer ) {
- uint8_t stored_priority = 0;
- uint8_t priority = 0;
-
- /* Get priorities of the two DHCPOFFERs */
- if ( *stored_dhcpoffer ) {
- dhcppkt_fetch ( &(*stored_dhcpoffer)->dhcppkt,
- DHCP_EB_PRIORITY, &stored_priority,
- sizeof ( stored_priority ) );
- }
- dhcppkt_fetch ( &dhcpoffer->dhcppkt, DHCP_EB_PRIORITY, &priority,
- sizeof ( priority ) );
-
- /* Replace stored offer only if priority is equal or greater */
- if ( priority >= stored_priority ) {
- if ( *stored_dhcpoffer ) {
- DBGC ( dhcp, "DHCP %p stored DHCPOFFER %p discarded\n",
- dhcp, *stored_dhcpoffer );
- }
- DBGC ( dhcp, "DHCP %p received DHCPOFFER %p stored\n",
- dhcp, dhcpoffer );
- dhcpset_put ( *stored_dhcpoffer );
- *stored_dhcpoffer = dhcpset_get ( dhcpoffer );
- }
-}
-
-/**
- * Handle received DHCPOFFER
- *
- * @v dhcp DHCP session
- * @v dhcpoffer Received DHCPOFFER
- */
-static void dhcp_rx_dhcpoffer ( struct dhcp_session *dhcp,
- struct dhcp_settings *dhcpoffer ) {
- struct in_addr server_id = { 0 };
- char vci[9]; /* "PXEClient" */
- int len;
- uint8_t ignore_proxy = 0;
- unsigned long elapsed;
-
- /* Check for presence of DHCP server ID */
- if ( dhcppkt_fetch ( &dhcpoffer->dhcppkt, DHCP_SERVER_IDENTIFIER,
- &server_id, sizeof ( server_id ) )
- != sizeof ( server_id ) ) {
- DBGC ( dhcp, "DHCP %p received DHCPOFFER %p missing server "
- "identifier\n", dhcp, dhcpoffer );
- /* Could be a valid BOOTP offer; do not abort processing */
- }
-
- /* If there is an IP address, it's a normal DHCPOFFER */
- if ( dhcpoffer->dhcppkt.dhcphdr->yiaddr.s_addr != 0 ) {
- DBGC ( dhcp, "DHCP %p received DHCPOFFER %p from %s has IP "
- "address\n",
- dhcp, dhcpoffer, inet_ntoa ( server_id ) );
- dhcp_store_dhcpoffer ( dhcp, dhcpoffer, &dhcp->dhcpoffer );
- }
-
- /* If there is a "PXEClient" vendor class ID, it's a
- * ProxyDHCPOFFER. Note that it could be both a normal
- * DHCPOFFER and a ProxyDHCPOFFER.
- */
- len = dhcppkt_fetch ( &dhcpoffer->dhcppkt, DHCP_VENDOR_CLASS_ID,
- vci, sizeof ( vci ) );
- if ( ( server_id.s_addr != 0 ) &&
- ( len >= ( int ) sizeof ( vci ) ) &&
- ( strncmp ( "PXEClient", vci, sizeof ( vci ) ) == 0 ) ) {
- DBGC ( dhcp, "DHCP %p received DHCPOFFER %p from %s is a "
- "ProxyDHCPOFFER\n",
- dhcp, dhcpoffer, inet_ntoa ( server_id ) );
- dhcp_store_dhcpoffer ( dhcp, dhcpoffer,
- &dhcp->proxydhcpoffer );
- }
-
- /* We can transition to making the DHCPREQUEST when we have a
- * valid DHCPOFFER, and either:
- *
- * o The DHCPOFFER instructs us to not wait for ProxyDHCP, or
- * o We have a valid ProxyDHCPOFFER, or
- * o We have allowed sufficient time for ProxyDHCPOFFERs.
- */
-
- /* If we don't yet have a DHCPOFFER, do nothing */
- if ( ! dhcp->dhcpoffer )
- return;
-
- /* If the DHCPOFFER instructs us to ignore ProxyDHCP, discard
- * any ProxyDHCPOFFER
- */
- dhcppkt_fetch ( &dhcp->dhcpoffer->dhcppkt, DHCP_EB_NO_PROXYDHCP,
- &ignore_proxy, sizeof ( ignore_proxy ) );
- if ( ignore_proxy && dhcp->proxydhcpoffer ) {
- DBGC ( dhcp, "DHCP %p discarding ProxyDHCPOFFER\n", dhcp );
- dhcpset_put ( dhcp->proxydhcpoffer );
- dhcp->proxydhcpoffer = NULL;
- }
-
- /* If we can't yet transition to DHCPREQUEST, do nothing */
- elapsed = ( currticks() - dhcp->start );
- if ( ! ( ignore_proxy || dhcp->proxydhcpoffer ||
- ( elapsed > PROXYDHCP_WAIT_TIME ) ) )
- return;
-
- /* Transition to DHCPREQUEST */
- dhcp_set_state ( dhcp, DHCP_STATE_REQUEST );
-}
-
-/**
- * Store received DHCPACK
- *
- * @v dhcp DHCP session
- * @v dhcpack Received DHCPACK
- *
- * The DHCPACK will be registered as a settings block.
- */
-static int dhcp_store_dhcpack ( struct dhcp_session *dhcp,
- struct dhcp_settings *dhcpack,
- struct settings *parent ) {
- struct settings *settings = &dhcpack->settings;
- struct settings *old_settings;
- int rc;
-
- /* Unregister any old settings obtained via DHCP */
- if ( ( old_settings = find_child_settings ( parent, settings->name ) ))
- unregister_settings ( old_settings );
-
- /* Register new settings */
- if ( ( rc = register_settings ( settings, parent ) ) != 0 ) {
- DBGC ( dhcp, "DHCP %p could not register settings: %s\n",
- dhcp, strerror ( rc ) );
- dhcp_finished ( dhcp, rc ); /* This is a fatal error */
- return rc;
- }
-
- return 0;
-}
-
-/**
- * Handle received DHCPACK
- *
- * @v dhcp DHCP session
- * @v dhcpack Received DHCPACK
- */
-static void dhcp_rx_dhcpack ( struct dhcp_session *dhcp,
- struct dhcp_settings *dhcpack ) {
- struct settings *parent;
- struct in_addr offer_server_id = { 0 };
- struct in_addr ack_server_id = { 0 };
- int rc;
-
- /* Verify server ID matches */
- assert ( dhcp->dhcpoffer != NULL );
- dhcppkt_fetch ( &dhcp->dhcpoffer->dhcppkt, DHCP_SERVER_IDENTIFIER,
- &offer_server_id, sizeof ( offer_server_id ) );
- dhcppkt_fetch ( &dhcpack->dhcppkt, DHCP_SERVER_IDENTIFIER,
- &ack_server_id, sizeof ( ack_server_id ) );
- if ( offer_server_id.s_addr != ack_server_id.s_addr ) {
- DBGC ( dhcp, "DHCP %p ignoring DHCPACK with wrong server ID "
- "%s\n", dhcp, inet_ntoa ( ack_server_id ) );
- return;
- }
-
- /* Register settings */
- parent = netdev_settings ( dhcp->netdev );
- if ( ( rc = dhcp_store_dhcpack ( dhcp, dhcpack, parent ) ) !=0 )
- return;
-
- /* If we have a ProxyDHCPOFFER, transition to PROXYDHCPREQUEST */
- if ( dhcp->proxydhcpoffer ) {
- dhcp->timer.min_timeout = 0;
- dhcp_set_state ( dhcp, DHCP_STATE_PROXYREQUEST );
- return;
- }
-
- /* Terminate DHCP */
- dhcp_finished ( dhcp, 0 );
-}
-
-/**
- * Handle received ProxyDHCPACK
- *
- * @v dhcp DHCP session
- * @v proxydhcpack Received ProxyDHCPACK
- */
-static void dhcp_rx_proxydhcpack ( struct dhcp_session *dhcp,
- struct dhcp_settings *proxydhcpack ) {
- struct in_addr offer_server_id = { 0 };
- struct in_addr ack_server_id = { 0 };
- int rc;
-
- /* Verify server ID matches */
- assert ( dhcp->proxydhcpoffer != NULL );
- dhcppkt_fetch ( &dhcp->proxydhcpoffer->dhcppkt, DHCP_SERVER_IDENTIFIER,
- &offer_server_id, sizeof ( offer_server_id ) );
- dhcppkt_fetch ( &proxydhcpack->dhcppkt, DHCP_SERVER_IDENTIFIER,
- &ack_server_id, sizeof ( ack_server_id ) );
- if ( offer_server_id.s_addr != ack_server_id.s_addr ) {
- DBGC ( dhcp, "DHCP %p ignoring ProxyDHCPACK with wrong server "
- "ID %s\n", dhcp, inet_ntoa ( ack_server_id ) );
- return;
- }
-
- /* Rename settings */
- proxydhcpack->settings.name = PROXYDHCP_SETTINGS_NAME;
-
- /* Register settings */
- if ( ( rc = dhcp_store_dhcpack ( dhcp, proxydhcpack, NULL ) ) != 0 )
- return;
-
- /* Terminate DHCP */
- dhcp_finished ( dhcp, 0 );
-}
-
-/**
* Receive new data
*
* @v xfer Data transfer interface
@@ -877,79 +1045,63 @@ static int dhcp_deliver_iob ( struct xfer_interface *xfer,
struct xfer_metadata *meta ) {
struct dhcp_session *dhcp =
container_of ( xfer, struct dhcp_session, xfer );
- struct sockaddr_tcpip *st_src;
- unsigned int src_port;
- struct dhcp_settings *dhcpset;
+ struct sockaddr_in *peer;
+ size_t data_len;
+ struct dhcp_packet *dhcppkt;
struct dhcphdr *dhcphdr;
uint8_t msgtype = 0;
+ struct in_addr server_id = { 0 };
int rc = 0;
/* Sanity checks */
- if ( ! meta ) {
- DBGC ( dhcp, "DHCP %p received packet without metadata\n",
- dhcp );
- rc = -EINVAL;
- goto err_no_meta;
- }
if ( ! meta->src ) {
DBGC ( dhcp, "DHCP %p received packet without source port\n",
dhcp );
rc = -EINVAL;
goto err_no_src;
}
- st_src = ( struct sockaddr_tcpip * ) meta->src;
- src_port = st_src->st_port;
+ peer = ( struct sockaddr_in * ) meta->src;
- /* Convert packet into a DHCP settings block */
- dhcpset = dhcpset_create ( iobuf->data, iob_len ( iobuf ) );
- if ( ! dhcpset ) {
- DBGC ( dhcp, "DHCP %p could not store DHCP packet\n", dhcp );
+ /* Create a DHCP packet containing the I/O buffer contents.
+ * Whilst we could just use the original buffer in situ, that
+ * would waste the unused space in the packet buffer, and also
+ * waste a relatively scarce fully-aligned I/O buffer.
+ */
+ data_len = iob_len ( iobuf );
+ dhcppkt = zalloc ( sizeof ( *dhcppkt ) + data_len );
+ if ( ! dhcppkt ) {
rc = -ENOMEM;
- goto err_dhcpset_create;
+ goto err_alloc_dhcppkt;
}
- dhcphdr = dhcpset->dhcppkt.dhcphdr;
+ dhcphdr = ( ( ( void * ) dhcppkt ) + sizeof ( *dhcppkt ) );
+ memcpy ( dhcphdr, iobuf->data, data_len );
+ dhcppkt_init ( dhcppkt, dhcphdr, data_len );
/* Identify message type */
- dhcppkt_fetch ( &dhcpset->dhcppkt, DHCP_MESSAGE_TYPE, &msgtype,
+ dhcppkt_fetch ( dhcppkt, DHCP_MESSAGE_TYPE, &msgtype,
sizeof ( msgtype ) );
- DBGC ( dhcp, "DHCP %p received %s %p from port %d\n", dhcp,
- dhcp_msgtype_name ( msgtype ), dhcpset, ntohs ( src_port ) );
+
+ /* Identify server ID */
+ dhcppkt_fetch ( dhcppkt, DHCP_SERVER_IDENTIFIER,
+ &server_id, sizeof ( server_id ) );
/* Check for matching transaction ID */
if ( dhcphdr->xid != dhcp_xid ( dhcp->netdev ) ) {
- DBGC ( dhcp, "DHCP %p received %s %p has bad transaction ID\n",
- dhcp, dhcp_msgtype_name ( msgtype ), dhcpset );
+ DBGC ( dhcp, "DHCP %p %s from %s:%d has bad transaction "
+ "ID\n", dhcp, dhcp_msgtype_name ( msgtype ),
+ inet_ntoa ( peer->sin_addr ),
+ ntohs ( peer->sin_port ) );
rc = -EINVAL;
goto err_xid;
};
/* Handle packet based on current state */
- switch ( dhcp->state ) {
- case DHCP_STATE_DISCOVER:
- if ( ( ( msgtype == DHCPOFFER ) || ( msgtype == DHCPNONE ) ) &&
- ( src_port == htons ( BOOTPS_PORT ) ) )
- dhcp_rx_dhcpoffer ( dhcp, dhcpset );
- break;
- case DHCP_STATE_REQUEST:
- if ( ( ( msgtype == DHCPACK ) || ( msgtype == DHCPNONE ) ) &&
- ( src_port == htons ( BOOTPS_PORT ) ) )
- dhcp_rx_dhcpack ( dhcp, dhcpset );
- break;
- case DHCP_STATE_PROXYREQUEST:
- if ( ( msgtype == DHCPACK ) &&
- ( src_port == htons ( PROXYDHCP_PORT ) ) )
- dhcp_rx_proxydhcpack ( dhcp, dhcpset );
- break;
- default:
- assert ( 0 );
- break;
- }
+ dhcp->state->rx ( dhcp, dhcppkt, peer, msgtype, server_id );
err_xid:
- dhcpset_put ( dhcpset );
- err_dhcpset_create:
+ dhcppkt_put ( dhcppkt );
+ err_alloc_dhcppkt:
err_no_src:
- err_no_meta:
free_iob ( iobuf );
return rc;
}
@@ -973,7 +1125,6 @@ static struct xfer_interface_operations dhcp_xfer_operations = {
static void dhcp_timer_expired ( struct retry_timer *timer, int fail ) {
struct dhcp_session *dhcp =
container_of ( timer, struct dhcp_session, timer );
- unsigned long elapsed = ( currticks() - dhcp->start );
/* If we have failed, terminate DHCP */
if ( fail ) {
@@ -981,19 +1132,8 @@ static void dhcp_timer_expired ( struct retry_timer *timer, int fail ) {
return;
}
- /* Give up waiting for ProxyDHCP before we reach the failure point */
- if ( dhcp->dhcpoffer && ( elapsed > PROXYDHCP_WAIT_TIME ) ) {
- if ( dhcp->state == DHCP_STATE_DISCOVER ) {
- dhcp_set_state ( dhcp, DHCP_STATE_REQUEST );
- return;
- } else if ( dhcp->state == DHCP_STATE_PROXYREQUEST ) {
- dhcp_finished ( dhcp, 0 );
- return;
- }
- }
-
- /* Otherwise, retransmit current packet */
- dhcp_tx ( dhcp );
+ /* Handle timer expiry based on current state */
+ dhcp->state->expired ( dhcp );
}
/****************************************************************************
@@ -1024,32 +1164,33 @@ static struct job_interface_operations dhcp_job_operations = {
/****************************************************************************
*
- * Instantiator
+ * Instantiators
+ *
+ */
+
+/**
+ * DHCP peer address for socket opening
*
+ * This is a dummy address; the only useful portion is the socket
+ * family (so that we get a UDP connection). The DHCP client will set
+ * the IP address and source port explicitly on each transmission.
*/
+static struct sockaddr dhcp_peer = {
+ .sa_family = AF_INET,
+};
/**
- * Start DHCP on a network device
+ * Start DHCP state machine on a network device
*
* @v job Job control interface
* @v netdev Network device
- * @v register_options DHCP option block registration routine
* @ret rc Return status code
*
- * Starts DHCP on the specified network device. If successful, the @c
- * register_options() routine will be called with the acquired
- * options.
+ * Starts DHCP on the specified network device. If successful, the
+ * DHCPACK (and ProxyDHCPACK, if applicable) will be registered as
+ * option sources.
*/
int start_dhcp ( struct job_interface *job, struct net_device *netdev ) {
- static struct sockaddr_in server = {
- .sin_family = AF_INET,
- .sin_addr.s_addr = INADDR_BROADCAST,
- .sin_port = htons ( BOOTPS_PORT ),
- };
- static struct sockaddr_in client = {
- .sin_family = AF_INET,
- .sin_port = htons ( BOOTPC_PORT ),
- };
struct dhcp_session *dhcp;
int rc;
@@ -1061,19 +1202,163 @@ int start_dhcp ( struct job_interface *job, struct net_device *netdev ) {
job_init ( &dhcp->job, &dhcp_job_operations, &dhcp->refcnt );
xfer_init ( &dhcp->xfer, &dhcp_xfer_operations, &dhcp->refcnt );
dhcp->netdev = netdev_get ( netdev );
+ dhcp->local.sin_family = AF_INET;
+ dhcp->local.sin_port = htons ( BOOTPC_PORT );
dhcp->timer.expired = dhcp_timer_expired;
- dhcp->timer.min_timeout = DHCP_MIN_TIMEOUT;
- dhcp->timer.max_timeout = DHCP_MAX_TIMEOUT;
- dhcp->start = currticks();
/* Instantiate child objects and attach to our interfaces */
- if ( ( rc = xfer_open_socket ( &dhcp->xfer, SOCK_DGRAM,
- ( struct sockaddr * ) &server,
- ( struct sockaddr * ) &client ) ) != 0 )
+ if ( ( rc = xfer_open_socket ( &dhcp->xfer, SOCK_DGRAM, &dhcp_peer,
+ ( struct sockaddr * ) &dhcp->local ) ) != 0 )
goto err;
- /* Start timer to initiate initial DHCPREQUEST */
- start_timer_nodelay ( &dhcp->timer );
+ /* Enter DHCPDISCOVER state */
+ dhcp_set_state ( dhcp, &dhcp_state_discover );
+
+ /* Attach parent interface, mortalise self, and return */
+ job_plug_plug ( &dhcp->job, job );
+ ref_put ( &dhcp->refcnt );
+ return 0;
+
+ err:
+ dhcp_finished ( dhcp, rc );
+ ref_put ( &dhcp->refcnt );
+ return rc;
+}
+
+/**
+ * Retrieve list of PXE boot servers for a given server type
+ *
+ * @v dhcp DHCP session
+ * @v raw DHCP PXE boot server list
+ * @v raw_len Length of DHCP PXE boot server list
+ * @v ip IP address list to fill in
+ *
+ * The caller must ensure that the IP address list has sufficient
+ * space.
+ */
+static void pxebs_list ( struct dhcp_session *dhcp, void *raw,
+ size_t raw_len, struct in_addr *ip ) {
+ struct dhcp_pxe_boot_server *server = raw;
+ size_t server_len;
+ unsigned int i;
+
+ while ( raw_len ) {
+ if ( raw_len < sizeof ( *server ) ) {
+ DBGC ( dhcp, "DHCP %p malformed PXE server list\n",
+ dhcp );
+ break;
+ }
+ server_len = offsetof ( typeof ( *server ),
+ ip[ server->num_ip ] );
+ if ( raw_len < server_len ) {
+ DBGC ( dhcp, "DHCP %p malformed PXE server list\n",
+ dhcp );
+ break;
+ }
+ if ( server->type == dhcp->pxe_type ) {
+ for ( i = 0 ; i < server->num_ip ; i++ )
+ *(ip++) = server->ip[i];
+ }
+ server = ( ( ( void * ) server ) + server_len );
+ raw_len -= server_len;
+ }
+}
+
+/**
+ * Start PXE Boot Server Discovery on a network device
+ *
+ * @v job Job control interface
+ * @v netdev Network device
+ * @v pxe_type PXE server type
+ * @ret rc Return status code
+ *
+ * Starts PXE Boot Server Discovery on the specified network device.
+ * If successful, the Boot Server ACK will be registered as an option
+ * source.
+ */
+int start_pxebs ( struct job_interface *job, struct net_device *netdev,
+ unsigned int pxe_type ) {
+ struct setting pxe_discovery_control_setting =
+ { .tag = DHCP_PXE_DISCOVERY_CONTROL };
+ struct setting pxe_boot_servers_setting =
+ { .tag = DHCP_PXE_BOOT_SERVERS };
+ struct setting pxe_boot_server_mcast_setting =
+ { .tag = DHCP_PXE_BOOT_SERVER_MCAST };
+ ssize_t pxebs_list_len;
+ struct dhcp_session *dhcp;
+ struct in_addr *ip;
+ unsigned int pxe_discovery_control;
+ int rc;
+
+ /* Get upper bound for PXE boot server IP address list */
+ pxebs_list_len = fetch_setting_len ( NULL, &pxe_boot_servers_setting );
+ if ( pxebs_list_len < 0 )
+ pxebs_list_len = 0;
+
+ /* Allocate and initialise structure */
+ dhcp = zalloc ( sizeof ( *dhcp ) + sizeof ( *ip ) /* mcast */ +
+ sizeof ( *ip ) /* bcast */ + pxebs_list_len +
+ sizeof ( *ip ) /* terminator */ );
+ if ( ! dhcp )
+ return -ENOMEM;
+ dhcp->refcnt.free = dhcp_free;
+ job_init ( &dhcp->job, &dhcp_job_operations, &dhcp->refcnt );
+ xfer_init ( &dhcp->xfer, &dhcp_xfer_operations, &dhcp->refcnt );
+ dhcp->netdev = netdev_get ( netdev );
+ dhcp->local.sin_family = AF_INET;
+ fetch_ipv4_setting ( netdev_settings ( netdev ), &ip_setting,
+ &dhcp->local.sin_addr );
+ dhcp->local.sin_port = htons ( BOOTPC_PORT );
+ dhcp->pxe_type = htons ( pxe_type );
+ dhcp->timer.expired = dhcp_timer_expired;
+
+ /* Construct PXE boot server IP address lists */
+ pxe_discovery_control =
+ fetch_uintz_setting ( NULL, &pxe_discovery_control_setting );
+ ip = ( ( ( void * ) dhcp ) + sizeof ( *dhcp ) );
+ dhcp->pxe_attempt = ip;
+ if ( ! ( pxe_discovery_control & PXEBS_NO_MULTICAST ) ) {
+ fetch_ipv4_setting ( NULL, &pxe_boot_server_mcast_setting, ip);
+ if ( ip->s_addr )
+ ip++;
+ }
+ if ( ! ( pxe_discovery_control & PXEBS_NO_BROADCAST ) )
+ (ip++)->s_addr = INADDR_BROADCAST;
+ if ( pxe_discovery_control & PXEBS_NO_UNKNOWN_SERVERS )
+ dhcp->pxe_accept = ip;
+ if ( pxebs_list_len ) {
+ uint8_t buf[pxebs_list_len];
+
+ fetch_setting ( NULL, &pxe_boot_servers_setting,
+ buf, sizeof ( buf ) );
+ pxebs_list ( dhcp, buf, sizeof ( buf ), ip );
+ }
+ if ( ! dhcp->pxe_attempt->s_addr ) {
+ DBGC ( dhcp, "DHCP %p has no PXE boot servers for type %04x\n",
+ dhcp, pxe_type );
+ rc = -EINVAL;
+ goto err;
+ }
+
+ /* Dump out PXE server lists */
+ DBGC ( dhcp, "DHCP %p attempting", dhcp );
+ for ( ip = dhcp->pxe_attempt ; ip->s_addr ; ip++ )
+ DBGC ( dhcp, " %s", inet_ntoa ( *ip ) );
+ DBGC ( dhcp, "\n" );
+ if ( dhcp->pxe_accept ) {
+ DBGC ( dhcp, "DHCP %p accepting", dhcp );
+ for ( ip = dhcp->pxe_accept ; ip->s_addr ; ip++ )
+ DBGC ( dhcp, " %s", inet_ntoa ( *ip ) );
+ DBGC ( dhcp, "\n" );
+ }
+
+ /* Instantiate child objects and attach to our interfaces */
+ if ( ( rc = xfer_open_socket ( &dhcp->xfer, SOCK_DGRAM, &dhcp_peer,
+ ( struct sockaddr * ) &dhcp->local ) ) != 0 )
+ goto err;
+
+ /* Enter PXEBS state */
+ dhcp_set_state ( dhcp, &dhcp_state_pxebs );
/* Attach parent interface, mortalise self, and return */
job_plug_plug ( &dhcp->job, job );
diff --git a/gpxe/src/net/udp/dns.c b/gpxe/src/net/udp/dns.c
index 1bcdbc7e..a498aefc 100644
--- a/gpxe/src/net/udp/dns.c
+++ b/gpxe/src/net/udp/dns.c
@@ -22,6 +22,7 @@
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
+#include <stdio.h>
#include <errno.h>
#include <byteswap.h>
#include <gpxe/refcnt.h>
@@ -47,6 +48,9 @@ static struct sockaddr_tcpip nameserver = {
.st_port = htons ( DNS_PORT ),
};
+/** The local domain */
+static char *localdomain;
+
/** A DNS request */
struct dns_request {
/** Reference counter */
@@ -180,6 +184,27 @@ static union dns_rr_info * dns_find_rr ( struct dns_request *dns,
}
/**
+ * Append DHCP domain name if available and name is not fully qualified
+ *
+ * @v string Name as a NUL-terminated string
+ * @ret fqdn Fully-qualified domain name, malloc'd copy
+ *
+ * The caller must free fqdn which is allocated even if the name is already
+ * fully qualified.
+ */
+static char * dns_qualify_name ( const char *string ) {
+ char *fqdn;
+
+ /* Leave unchanged if already fully-qualified or no local domain */
+ if ( ( ! localdomain ) || ( strchr ( string, '.' ) != 0 ) )
+ return strdup ( string );
+
+ /* Append local domain to name */
+ asprintf ( &fqdn, "%s.%s", string, localdomain );
+ return fqdn;
+}
+
+/**
* Convert a standard NUL-terminated string to a DNS name
*
* @v string Name as a NUL-terminated string
@@ -452,19 +477,30 @@ static struct xfer_interface_operations dns_socket_operations = {
static int dns_resolv ( struct resolv_interface *resolv,
const char *name, struct sockaddr *sa ) {
struct dns_request *dns;
+ char *fqdn;
int rc;
/* Fail immediately if no DNS servers */
if ( ! nameserver.st_family ) {
DBG ( "DNS not attempting to resolve \"%s\": "
"no DNS servers\n", name );
- return -ENXIO;
+ rc = -ENXIO;
+ goto err_no_nameserver;
+ }
+
+ /* Ensure fully-qualified domain name if DHCP option was given */
+ fqdn = dns_qualify_name ( name );
+ if ( ! fqdn ) {
+ rc = -ENOMEM;
+ goto err_qualify_name;
}
/* Allocate DNS structure */
dns = zalloc ( sizeof ( *dns ) );
- if ( ! dns )
- return -ENOMEM;
+ if ( ! dns ) {
+ rc = -ENOMEM;
+ goto err_alloc_dns;
+ }
resolv_init ( &dns->resolv, &null_resolv_ops, &dns->refcnt );
xfer_init ( &dns->socket, &dns_socket_operations, &dns->refcnt );
dns->timer.expired = dns_timer_expired;
@@ -474,7 +510,7 @@ static int dns_resolv ( struct resolv_interface *resolv,
dns->query.dns.flags = htons ( DNS_FLAG_QUERY | DNS_FLAG_OPCODE_QUERY |
DNS_FLAG_RD );
dns->query.dns.qdcount = htons ( 1 );
- dns->qinfo = ( void * ) dns_make_name ( name, dns->query.payload );
+ dns->qinfo = ( void * ) dns_make_name ( fqdn, dns->query.payload );
dns->qinfo->qtype = htons ( DNS_TYPE_A );
dns->qinfo->qclass = htons ( DNS_CLASS_IN );
@@ -484,7 +520,7 @@ static int dns_resolv ( struct resolv_interface *resolv,
NULL ) ) != 0 ) {
DBGC ( dns, "DNS %p could not open socket: %s\n",
dns, strerror ( rc ) );
- goto err;
+ goto err_open_socket;
}
/* Send first DNS packet */
@@ -493,10 +529,15 @@ static int dns_resolv ( struct resolv_interface *resolv,
/* Attach parent interface, mortalise self, and return */
resolv_plug_plug ( &dns->resolv, resolv );
ref_put ( &dns->refcnt );
+ free ( fqdn );
return 0;
- err:
+ err_open_socket:
+ err_alloc_dns:
ref_put ( &dns->refcnt );
+ err_qualify_name:
+ free ( fqdn );
+ err_no_nameserver:
return rc;
}
@@ -521,12 +562,20 @@ struct setting dns_setting __setting = {
.type = &setting_type_ipv4,
};
+/** Domain name setting */
+struct setting domain_setting __setting = {
+ .name = "domain",
+ .description = "Local domain",
+ .tag = DHCP_DOMAIN_NAME,
+ .type = &setting_type_string,
+};
+
/**
- * Apply nameserver setting
+ * Apply DNS settings
*
* @ret rc Return status code
*/
-static int apply_nameserver_setting ( void ) {
+static int apply_dns_settings ( void ) {
struct sockaddr_in *sin_nameserver =
( struct sockaddr_in * ) &nameserver;
int len;
@@ -538,10 +587,15 @@ static int apply_nameserver_setting ( void ) {
inet_ntoa ( sin_nameserver->sin_addr ) );
}
+ /* Get local domain DHCP option */
+ if ( ( len = fetch_string_setting_copy ( NULL, &domain_setting,
+ &localdomain ) ) >= 0 )
+ DBG ( "DNS local domain %s\n", localdomain );
+
return 0;
}
-/** Nameserver setting applicator */
-struct settings_applicator nameserver_applicator __settings_applicator = {
- .apply = apply_nameserver_setting,
+/** DNS settings applicator */
+struct settings_applicator dns_applicator __settings_applicator = {
+ .apply = apply_dns_settings,
};
diff --git a/gpxe/src/net/udp/slam.c b/gpxe/src/net/udp/slam.c
index cb0dfa5c..6add99bc 100644
--- a/gpxe/src/net/udp/slam.c
+++ b/gpxe/src/net/udp/slam.c
@@ -219,7 +219,7 @@ static int slam_put_value ( struct slam_request *slam,
*/
len = ( ( flsl ( value ) + 10 ) / 8 );
if ( len >= iob_tailroom ( iobuf ) ) {
- DBGC2 ( slam, "SLAM %p cannot add %d-byte value\n",
+ DBGC2 ( slam, "SLAM %p cannot add %zd-byte value\n",
slam, len );
return -ENOBUFS;
}
@@ -380,7 +380,7 @@ static int slam_pull_value ( struct slam_request *slam,
len = ( *data >> 5 );
if ( ( len == 0 ) ||
( value && ( len > sizeof ( *value ) ) ) ) {
- DBGC ( slam, "SLAM %p invalid value length %d bytes\n",
+ DBGC ( slam, "SLAM %p invalid value length %zd bytes\n",
slam, len );
return -EINVAL;
}
diff --git a/gpxe/src/net/udp/tftp.c b/gpxe/src/net/udp/tftp.c
index 8fdb3714..19525f79 100644
--- a/gpxe/src/net/udp/tftp.c
+++ b/gpxe/src/net/udp/tftp.c
@@ -45,6 +45,15 @@
FEATURE ( FEATURE_PROTOCOL, "TFTP", DHCP_EB_FEATURE_TFTP, 1 );
+/* TFTP-specific error codes */
+#define ETFTP_INVALID_BLKSIZE EUNIQ_01
+#define ETFTP_INVALID_TSIZE EUNIQ_02
+#define ETFTP_MC_NO_PORT EUNIQ_03
+#define ETFTP_MC_NO_MC EUNIQ_04
+#define ETFTP_MC_INVALID_MC EUNIQ_05
+#define ETFTP_MC_INVALID_IP EUNIQ_06
+#define ETFTP_MC_INVALID_PORT EUNIQ_07
+
/**
* A TFTP request
*
@@ -504,7 +513,7 @@ static int tftp_process_blksize ( struct tftp_request *tftp,
if ( *end ) {
DBGC ( tftp, "TFTP %p got invalid blksize \"%s\"\n",
tftp, value );
- return -EINVAL;
+ return -( EINVAL | ETFTP_INVALID_BLKSIZE );
}
DBGC ( tftp, "TFTP %p blksize=%d\n", tftp, tftp->blksize );
@@ -526,7 +535,7 @@ static int tftp_process_tsize ( struct tftp_request *tftp,
if ( *end ) {
DBGC ( tftp, "TFTP %p got invalid tsize \"%s\"\n",
tftp, value );
- return -EINVAL;
+ return -( EINVAL | ETFTP_INVALID_TSIZE );
}
DBGC ( tftp, "TFTP %p tsize=%ld\n", tftp, tftp->tsize );
@@ -560,13 +569,13 @@ static int tftp_process_multicast ( struct tftp_request *tftp,
port = strchr ( addr, ',' );
if ( ! port ) {
DBGC ( tftp, "TFTP %p multicast missing port,mc\n", tftp );
- return -EINVAL;
+ return -( EINVAL | ETFTP_MC_NO_PORT );
}
*(port++) = '\0';
mc = strchr ( port, ',' );
if ( ! mc ) {
DBGC ( tftp, "TFTP %p multicast missing mc\n", tftp );
- return -EINVAL;
+ return -( EINVAL | ETFTP_MC_NO_MC );
}
*(mc++) = '\0';
@@ -575,7 +584,7 @@ static int tftp_process_multicast ( struct tftp_request *tftp,
tftp->flags &= ~TFTP_FL_SEND_ACK;
if ( *mc_end ) {
DBGC ( tftp, "TFTP %p multicast invalid mc %s\n", tftp, mc );
- return -EINVAL;
+ return -( EINVAL | ETFTP_MC_INVALID_MC );
}
DBGC ( tftp, "TFTP %p is%s the master client\n",
tftp, ( ( tftp->flags & TFTP_FL_SEND_ACK ) ? "" : " not" ) );
@@ -584,7 +593,7 @@ static int tftp_process_multicast ( struct tftp_request *tftp,
if ( inet_aton ( addr, &socket.sin.sin_addr ) == 0 ) {
DBGC ( tftp, "TFTP %p multicast invalid IP address "
"%s\n", tftp, addr );
- return -EINVAL;
+ return -( EINVAL | ETFTP_MC_INVALID_IP );
}
DBGC ( tftp, "TFTP %p multicast IP address %s\n",
tftp, inet_ntoa ( socket.sin.sin_addr ) );
@@ -592,7 +601,7 @@ static int tftp_process_multicast ( struct tftp_request *tftp,
if ( *port_end ) {
DBGC ( tftp, "TFTP %p multicast invalid port %s\n",
tftp, port );
- return -EINVAL;
+ return -( EINVAL | ETFTP_MC_INVALID_PORT );
}
DBGC ( tftp, "TFTP %p multicast port %d\n",
tftp, ntohs ( socket.sin.sin_port ) );
@@ -732,6 +741,11 @@ static int tftp_rx_data ( struct tftp_request *tftp,
rc = -EINVAL;
goto done;
}
+ if ( data->block == 0 ) {
+ DBGC ( tftp, "TFTP %p received data block 0\n", tftp );
+ rc = -EINVAL;
+ goto done;
+ }
/* Extract data */
block = ( ntohs ( data->block ) - 1 );
@@ -749,9 +763,8 @@ static int tftp_rx_data ( struct tftp_request *tftp,
memset ( &meta, 0, sizeof ( meta ) );
meta.whence = SEEK_SET;
meta.offset = offset;
- rc = xfer_deliver_iob_meta ( &tftp->xfer, iobuf, &meta );
- iobuf = NULL;
- if ( rc != 0 ) {
+ if ( ( rc = xfer_deliver_iob_meta ( &tftp->xfer, iob_disown ( iobuf ),
+ &meta ) ) != 0 ) {
DBGC ( tftp, "TFTP %p could not deliver data: %s\n",
tftp, strerror ( rc ) );
goto done;
@@ -826,7 +839,7 @@ static int tftp_rx_error ( struct tftp_request *tftp, void *buf, size_t len ) {
*
* @v tftp TFTP connection
* @v iobuf I/O buffer
- * @v meta Transfer metadata, or NULL
+ * @v meta Transfer metadata
* @ret rc Return status code
*/
static int tftp_rx ( struct tftp_request *tftp,
@@ -843,11 +856,6 @@ static int tftp_rx ( struct tftp_request *tftp,
"%zd\n", tftp, len );
goto done;
}
- if ( ! meta ) {
- DBGC ( tftp, "TFTP %p received packet without metadata\n",
- tftp );
- goto done;
- }
if ( ! meta->src ) {
DBGC ( tftp, "TFTP %p received packet without source port\n",
tftp );
@@ -873,8 +881,7 @@ static int tftp_rx ( struct tftp_request *tftp,
rc = tftp_rx_oack ( tftp, iobuf->data, len );
break;
case htons ( TFTP_DATA ):
- rc = tftp_rx_data ( tftp, iobuf );
- iobuf = NULL;
+ rc = tftp_rx_data ( tftp, iob_disown ( iobuf ) );
break;
case htons ( TFTP_ERROR ):
rc = tftp_rx_error ( tftp, iobuf->data, len );
@@ -895,7 +902,7 @@ static int tftp_rx ( struct tftp_request *tftp,
*
* @v socket Transport layer interface
* @v iobuf I/O buffer
- * @v meta Transfer metadata, or NULL
+ * @v meta Transfer metadata
* @ret rc Return status code
*/
static int tftp_socket_deliver_iob ( struct xfer_interface *socket,
@@ -939,7 +946,7 @@ static struct xfer_interface_operations tftp_socket_operations = {
*
* @v mc_socket Multicast transport layer interface
* @v iobuf I/O buffer
- * @v meta Transfer metadata, or NULL
+ * @v meta Transfer metadata
* @ret rc Return status code
*/
static int tftp_mc_socket_deliver_iob ( struct xfer_interface *mc_socket,
@@ -977,11 +984,29 @@ static void tftp_xfer_close ( struct xfer_interface *xfer, int rc ) {
tftp_done ( tftp, rc );
}
+/**
+ * Check flow control window
+ *
+ * @v xfer Data transfer interface
+ * @ret len Length of window
+ */
+static size_t tftp_xfer_window ( struct xfer_interface *xfer ) {
+ struct tftp_request *tftp =
+ container_of ( xfer, struct tftp_request, xfer );
+
+ /* We abuse this data-xfer method to convey the blocksize to
+ * the caller. This really should be done using some kind of
+ * stat() method, but we don't yet have the facility to do
+ * that.
+ */
+ return tftp->blksize;
+}
+
/** TFTP data transfer interface operations */
static struct xfer_interface_operations tftp_xfer_operations = {
.close = tftp_xfer_close,
.vredirect = ignore_xfer_vredirect,
- .window = unlimited_xfer_window,
+ .window = tftp_xfer_window,
.alloc_iob = default_xfer_alloc_iob,
.deliver_iob = xfer_deliver_as_raw,
.deliver_raw = ignore_xfer_deliver_raw,