summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/include/gpxe/dhcp.h2
-rw-r--r--src/net/udp/dhcp.c54
-rw-r--r--src/usr/dhcpmgmt.c8
3 files changed, 52 insertions, 12 deletions
diff --git a/src/include/gpxe/dhcp.h b/src/include/gpxe/dhcp.h
index 74741d96..2d9f8853 100644
--- a/src/include/gpxe/dhcp.h
+++ b/src/include/gpxe/dhcp.h
@@ -601,6 +601,8 @@ struct dhcphdr {
/** Setting block name used for BootServerDHCP responses */
#define PXEBS_SETTINGS_NAME "pxebs"
+extern void * dhcp_chaddr ( struct net_device *netdev, uint8_t *hlen,
+ uint16_t *flags );
extern int dhcp_create_packet ( struct dhcp_packet *dhcppkt,
struct net_device *netdev, uint8_t msgtype,
const void *options, size_t options_len,
diff --git a/src/net/udp/dhcp.c b/src/net/udp/dhcp.c
index 8d072250..5f741377 100644
--- a/src/net/udp/dhcp.c
+++ b/src/net/udp/dhcp.c
@@ -827,6 +827,46 @@ static struct dhcp_session_state dhcp_state_pxebs = {
*/
/**
+ * Construct DHCP client hardware address field and broadcast flag
+ *
+ * @v netdev Network device
+ * @v hlen DHCP hardware address length to fill in
+ * @v flags DHCP flags to fill in
+ * @ret chaddr DHCP client hardware address
+ */
+void * dhcp_chaddr ( struct net_device *netdev, uint8_t *hlen,
+ uint16_t *flags ) {
+ struct ll_protocol *ll_protocol = netdev->ll_protocol;
+ typeof ( ( ( struct dhcphdr * ) NULL )->chaddr ) chaddr;
+
+ /* If the link-layer address cannot fit into the chaddr field
+ * (as is the case for IPoIB) then try using the hardware
+ * address instead. If we do this, set the broadcast flag,
+ * since chaddr then does not represent a valid link-layer
+ * address for the return path.
+ *
+ * If even the hardware address is too large, use an empty
+ * chaddr field and set the broadcast flag.
+ *
+ * This goes against RFC4390, but RFC4390 mandates that we use
+ * a DHCP client identifier that conforms with RFC4361, which
+ * we cannot do without either persistent (NIC-independent)
+ * storage, or by eliminating the hardware address completely
+ * from the DHCP packet, which seems unfriendly to users.
+ */
+ if ( ( *hlen = ll_protocol->ll_addr_len ) <= sizeof ( chaddr ) ) {
+ return netdev->ll_addr;
+ }
+ *flags = htons ( BOOTP_FL_BROADCAST );
+ if ( ( *hlen = ll_protocol->hw_addr_len ) <= sizeof ( chaddr ) ) {
+ return netdev->hw_addr;
+ } else {
+ *hlen = 0;
+ return NULL;
+ }
+}
+
+/**
* Create a DHCP packet
*
* @v dhcppkt DHCP packet structure to fill in
@@ -846,7 +886,7 @@ int dhcp_create_packet ( struct dhcp_packet *dhcppkt,
const void *options, size_t options_len,
void *data, size_t max_len ) {
struct dhcphdr *dhcphdr = data;
- unsigned int hlen;
+ void *chaddr;
int rc;
/* Sanity check */
@@ -859,16 +899,8 @@ int dhcp_create_packet ( struct dhcp_packet *dhcppkt,
dhcphdr->magic = htonl ( DHCP_MAGIC_COOKIE );
dhcphdr->htype = ntohs ( netdev->ll_protocol->ll_proto );
dhcphdr->op = dhcp_op[msgtype];
- /* If hardware length exceeds the chaddr field length, don't
- * use the chaddr field. This is as per RFC4390.
- */
- hlen = netdev->ll_protocol->ll_addr_len;
- if ( hlen > sizeof ( dhcphdr->chaddr ) ) {
- hlen = 0;
- dhcphdr->flags = htons ( BOOTP_FL_BROADCAST );
- }
- dhcphdr->hlen = hlen;
- memcpy ( dhcphdr->chaddr, netdev->ll_addr, hlen );
+ chaddr = dhcp_chaddr ( netdev, &dhcphdr->hlen, &dhcphdr->flags );
+ memcpy ( dhcphdr->chaddr, chaddr, dhcphdr->hlen );
memcpy ( dhcphdr->options, options, options_len );
/* Initialise DHCP packet structure */
diff --git a/src/usr/dhcpmgmt.c b/src/usr/dhcpmgmt.c
index d98aa9f4..aa969856 100644
--- a/src/usr/dhcpmgmt.c
+++ b/src/usr/dhcpmgmt.c
@@ -37,6 +37,9 @@ FILE_LICENCE ( GPL2_OR_LATER );
*/
int dhcp ( struct net_device *netdev ) {
+ uint8_t *chaddr;
+ uint8_t hlen;
+ uint16_t flags;
int rc;
/* Check we can open the interface first */
@@ -48,7 +51,10 @@ int dhcp ( struct net_device *netdev ) {
return rc;
/* Perform DHCP */
- printf ( "DHCP (%s %s)", netdev->name, netdev_addr ( netdev ) );
+ chaddr = dhcp_chaddr ( netdev, &hlen, &flags );
+ printf ( "DHCP (%s ", netdev->name );
+ while ( hlen-- )
+ printf ( "%02x%c", *(chaddr++), ( hlen ? ':' : ')' ) );
if ( ( rc = start_dhcp ( &monojob, netdev ) ) == 0 )
rc = monojob_wait ( "" );