Browse Source

[dhcp] Fall back to using the hardware address to populate the chaddr field

For IPoIB, the chaddr field is too small (16 bytes) to contain the
20-byte IPoIB link-layer address.  RFC4390 mandates that we should
pass an empty chaddr field and rely on the DHCP client identifier
instead.  This has many problems, not least of which is that a client
identifier containing an IPoIB link-layer address is not very useful
from the point of view of creating DHCP reservations, since the QPN
component is assigned at runtime and may vary between boots.

Leave the DHCP client identifier as-is, to avoid breaking existing
setups as far as possible, but expose the real hardware address (the
port GUID) via the DHCP chaddr field, using the broadcast flag to
instruct the DHCP server not to use this chaddr value as a link-layer
address.

This makes it possible (at least with ISC dhcpd) to create DHCP
reservations using host declarations such as:

    host duckling {
        fixed-address 10.252.252.99;
        hardware unknown-32 00:02:c9:02:00:25:a1:b5;
    }
tags/v0.9.8
Michael Brown 15 years ago
parent
commit
444d5550a7
3 changed files with 52 additions and 12 deletions
  1. 2
    0
      src/include/gpxe/dhcp.h
  2. 43
    11
      src/net/udp/dhcp.c
  3. 7
    1
      src/usr/dhcpmgmt.c

+ 2
- 0
src/include/gpxe/dhcp.h View File

@@ -601,6 +601,8 @@ struct dhcphdr {
601 601
 /** Setting block name used for BootServerDHCP responses */
602 602
 #define PXEBS_SETTINGS_NAME "pxebs"
603 603
 
604
+extern void * dhcp_chaddr ( struct net_device *netdev, uint8_t *hlen,
605
+			    uint16_t *flags );
604 606
 extern int dhcp_create_packet ( struct dhcp_packet *dhcppkt,
605 607
 				struct net_device *netdev, uint8_t msgtype,
606 608
 				const void *options, size_t options_len,

+ 43
- 11
src/net/udp/dhcp.c View File

@@ -826,6 +826,46 @@ static struct dhcp_session_state dhcp_state_pxebs = {
826 826
  *
827 827
  */
828 828
 
829
+/**
830
+ * Construct DHCP client hardware address field and broadcast flag
831
+ *
832
+ * @v netdev		Network device
833
+ * @v hlen		DHCP hardware address length to fill in
834
+ * @v flags		DHCP flags to fill in
835
+ * @ret chaddr		DHCP client hardware address
836
+ */
837
+void * dhcp_chaddr ( struct net_device *netdev, uint8_t *hlen,
838
+		     uint16_t *flags ) {
839
+	struct ll_protocol *ll_protocol = netdev->ll_protocol;
840
+	typeof ( ( ( struct dhcphdr * ) NULL )->chaddr ) chaddr;
841
+
842
+	/* If the link-layer address cannot fit into the chaddr field
843
+	 * (as is the case for IPoIB) then try using the hardware
844
+	 * address instead.  If we do this, set the broadcast flag,
845
+	 * since chaddr then does not represent a valid link-layer
846
+	 * address for the return path.
847
+	 *
848
+	 * If even the hardware address is too large, use an empty
849
+	 * chaddr field and set the broadcast flag.
850
+	 *
851
+	 * This goes against RFC4390, but RFC4390 mandates that we use
852
+	 * a DHCP client identifier that conforms with RFC4361, which
853
+	 * we cannot do without either persistent (NIC-independent)
854
+	 * storage, or by eliminating the hardware address completely
855
+	 * from the DHCP packet, which seems unfriendly to users.
856
+	 */
857
+	if ( ( *hlen = ll_protocol->ll_addr_len ) <= sizeof ( chaddr ) ) {
858
+		return netdev->ll_addr;
859
+	}
860
+	*flags = htons ( BOOTP_FL_BROADCAST );
861
+	if ( ( *hlen = ll_protocol->hw_addr_len ) <= sizeof ( chaddr ) ) {
862
+		return netdev->hw_addr;
863
+	} else {
864
+		*hlen = 0;
865
+		return NULL;
866
+	}
867
+}
868
+
829 869
 /**
830 870
  * Create a DHCP packet
831 871
  *
@@ -846,7 +886,7 @@ int dhcp_create_packet ( struct dhcp_packet *dhcppkt,
846 886
 			 const void *options, size_t options_len,
847 887
 			 void *data, size_t max_len ) {
848 888
 	struct dhcphdr *dhcphdr = data;
849
-	unsigned int hlen;
889
+	void *chaddr;
850 890
 	int rc;
851 891
 
852 892
 	/* Sanity check */
@@ -859,16 +899,8 @@ int dhcp_create_packet ( struct dhcp_packet *dhcppkt,
859 899
 	dhcphdr->magic = htonl ( DHCP_MAGIC_COOKIE );
860 900
 	dhcphdr->htype = ntohs ( netdev->ll_protocol->ll_proto );
861 901
 	dhcphdr->op = dhcp_op[msgtype];
862
-	/* If hardware length exceeds the chaddr field length, don't
863
-	 * use the chaddr field.  This is as per RFC4390.
864
-	 */
865
-	hlen = netdev->ll_protocol->ll_addr_len;
866
-	if ( hlen > sizeof ( dhcphdr->chaddr ) ) {
867
-		hlen = 0;
868
-		dhcphdr->flags = htons ( BOOTP_FL_BROADCAST );
869
-	}
870
-	dhcphdr->hlen = hlen;
871
-	memcpy ( dhcphdr->chaddr, netdev->ll_addr, hlen );
902
+	chaddr = dhcp_chaddr ( netdev, &dhcphdr->hlen, &dhcphdr->flags );
903
+	memcpy ( dhcphdr->chaddr, chaddr, dhcphdr->hlen );
872 904
 	memcpy ( dhcphdr->options, options, options_len );
873 905
 
874 906
 	/* Initialise DHCP packet structure */

+ 7
- 1
src/usr/dhcpmgmt.c View File

@@ -37,6 +37,9 @@ FILE_LICENCE ( GPL2_OR_LATER );
37 37
  */
38 38
 
39 39
 int dhcp ( struct net_device *netdev ) {
40
+	uint8_t *chaddr;
41
+	uint8_t hlen;
42
+	uint16_t flags;
40 43
 	int rc;
41 44
 
42 45
 	/* Check we can open the interface first */
@@ -48,7 +51,10 @@ int dhcp ( struct net_device *netdev ) {
48 51
 		return rc;
49 52
 
50 53
 	/* Perform DHCP */
51
-	printf ( "DHCP (%s %s)", netdev->name, netdev_addr ( netdev ) );
54
+	chaddr = dhcp_chaddr ( netdev, &hlen, &flags );
55
+	printf ( "DHCP (%s ", netdev->name );
56
+	while ( hlen-- )
57
+		printf ( "%02x%c", *(chaddr++), ( hlen ? ':' : ')' ) );
52 58
 	if ( ( rc = start_dhcp ( &monojob, netdev ) ) == 0 )
53 59
 		rc = monojob_wait ( "" );
54 60
 

Loading…
Cancel
Save