From e1afdd85edfd5d2113262d1570c56d2dc6e97f97 Mon Sep 17 00:00:00 2001
From: Jiri Popelka <jpopelka@redhat.com>
Date: Tue, 16 Dec 2014 17:35:39 +0100
Subject: [PATCH] DHCP Option 97 - Client Machine Identifier - pxe-client-id

rfc4578#section-2.3:

   The format of the option is:

                Code  Len  Type  Machine Identifier
               +----+-----+----+-----+ . . . +-----+
               | 97 |  n  |  t |     | . . . |     |
               +----+-----+----+-----+ . . . +-----+

   Octet "t" describes the type of the machine identifier in the
   remaining octets in this option. 0 (zero) is the only value defined
   for this octet at the present time, and it describes the remaining
   octets as a 16-octet Globally Unique Identifier (GUID).  Octet "n" is
   17 for type 0.

This patch is its server part.
---
 common/options.c    | 27 ++++++++++++++++++++-------
 common/tables.c     |  3 ++-
 includes/dhcp.h     |  1 +
 server/dhcp.c       | 19 +++++++++++++++++++
 server/dhcpd.conf.5 |  9 ++++++---
 server/failover.c   |  3 +++
 server/mdb.c        |  5 +++--
 7 files changed, 54 insertions(+), 13 deletions(-)

diff --git a/common/options.c b/common/options.c
index faeb3ef..61b9de8 100644
--- a/common/options.c
+++ b/common/options.c
@@ -4271,13 +4271,26 @@ int validate_packet(struct packet *packet)
 				"a future version of ISC DHCP will reject this");
 		}
 	} else {
-		/* 
-		 * If hlen is 0 we don't have any identifier, we warn the user
-		 * but continue processing the packet as we can.
-		 */
-		if (packet->raw->hlen == 0) {
-			log_debug("Received DHCPv4 packet without client-id"
-				  " option and empty hlen field.");
+		oc = lookup_option (&dhcp_universe, packet->options,
+				    DHO_PXE_CLIENT_ID);
+		if (oc) {
+			/* Let's check if pxe-client-id is sane */
+			if ((oc->data.len < 2) ||
+			    (oc->data.data[0] == '\0' &&
+			     oc->data.len != 17)) {
+				log_debug("Dropped DHCPv4 packet with wrong "
+				    "(len == %d) pxe-client-id", oc->data.len);
+				return (0);
+			}
+		} else {
+			/*
+			 * If hlen is 0 we don't have any identifier, we warn the user
+			 * but continue processing the packet as we can.
+			 */
+			if (packet->raw->hlen == 0) {
+				log_debug("Received DHCPv4 packet without client-id"
+						" option and empty hlen field.");
+			}
 		}
 	}
 
diff --git a/common/tables.c b/common/tables.c
index 7508cd6..70896ca 100644
--- a/common/tables.c
+++ b/common/tables.c
@@ -196,8 +196,9 @@ static struct option dhcp_options[] = {
 	/* Defined by RFC 4578 */
 	{ "pxe-system-type", "S",		&dhcp_universe,  93, 1 },
 	{ "pxe-interface-id", "BBB",		&dhcp_universe,  94, 1 },
-	{ "pxe-client-id", "BX",		&dhcp_universe,  97, 1 },
 #endif
+	{ "pxe-client-id", "BX",		&dhcp_universe,  97, 1 },
+
 	{ "uap-servers", "t",			&dhcp_universe,  98, 1 },
 #if defined(RFC4776_OPTIONS)
         { "geoconf-civic", "X",                 &dhcp_universe, 99, 1 },
diff --git a/includes/dhcp.h b/includes/dhcp.h
index f916468..299fa4a 100644
--- a/includes/dhcp.h
+++ b/includes/dhcp.h
@@ -157,6 +157,7 @@ struct dhcp_packet {
 #define DHO_AUTHENTICATE			90  /* RFC3118, was 210 */
 #define DHO_CLIENT_LAST_TRANSACTION_TIME	91
 #define DHO_ASSOCIATED_IP			92
+#define DHO_PXE_CLIENT_ID			97  /* RFC4578 */
 #define DHO_SUBNET_SELECTION			118 /* RFC3011! */
 #define DHO_DOMAIN_SEARCH			119 /* RFC3397 */
 #define DHO_VIVCO_SUBOPTIONS			124
diff --git a/server/dhcp.c b/server/dhcp.c
index 7c5b7c7..09d911b 100644
--- a/server/dhcp.c
+++ b/server/dhcp.c
@@ -165,6 +165,10 @@ dhcp (struct packet *packet) {
 			oc = lookup_option (&dhcp_universe, packet -> options,
 					    DHO_DHCP_CLIENT_IDENTIFIER);
 			if (!oc)
+				oc = lookup_option (&dhcp_universe,
+						    packet -> options,
+						    DHO_PXE_CLIENT_ID);
+			if (!oc)
 				goto nolease;
 
 			memset (&data, 0, sizeof data);
@@ -731,6 +735,9 @@ void dhcprelease (packet, ms_nulltp)
 
 	oc = lookup_option (&dhcp_universe, packet -> options,
 			    DHO_DHCP_CLIENT_IDENTIFIER);
+	if (!oc)
+		oc = lookup_option (&dhcp_universe, packet -> options,
+				    DHO_PXE_CLIENT_ID);
 	memset (&data, 0, sizeof data);
 	if (oc &&
 	    evaluate_option_cache (&data, packet, (struct lease *)0,
@@ -1153,6 +1160,9 @@ void dhcpinform (packet, ms_nulltp)
          */
 	oc = lookup_option(&dhcp_universe, packet->options,
 			   DHO_DHCP_CLIENT_IDENTIFIER);
+	if (!oc)
+		oc = lookup_option (&dhcp_universe, packet -> options,
+				    DHO_PXE_CLIENT_ID);
 	memset(&d1, 0, sizeof(d1));
 	if (oc &&
 	    evaluate_option_cache(&d1, packet, NULL, NULL,
@@ -2196,6 +2206,9 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp)
 		   can be used. */
 		oc = lookup_option (&dhcp_universe, packet -> options,
 				    DHO_DHCP_CLIENT_IDENTIFIER);
+		if (!oc)
+			oc = lookup_option (&dhcp_universe, packet -> options,
+					    DHO_PXE_CLIENT_ID);
 		if (oc &&
 		    evaluate_option_cache (&d1, packet, lease,
 					   (struct client_state *)0,
@@ -2777,6 +2790,9 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp)
 		/* Record the uid, if given... */
 		oc = lookup_option (&dhcp_universe, packet -> options,
 				    DHO_DHCP_CLIENT_IDENTIFIER);
+		if (!oc)
+			oc = lookup_option (&dhcp_universe, packet -> options,
+					    DHO_PXE_CLIENT_ID);
 		if (oc &&
 		    evaluate_option_cache(&d1, packet, lease, NULL,
 					  packet->options, state->options,
@@ -3807,6 +3823,9 @@ int find_lease (struct lease **lp,
 	   specified unique client identifier. */
 	oc = lookup_option (&dhcp_universe, packet -> options,
 			    DHO_DHCP_CLIENT_IDENTIFIER);
+	if (!oc)
+		oc = lookup_option (&dhcp_universe, packet -> options,
+				    DHO_PXE_CLIENT_ID);
 	memset (&client_identifier, 0, sizeof client_identifier);
 	if (oc &&
 	    evaluate_option_cache (&client_identifier,
diff --git a/server/dhcpd.conf.5 b/server/dhcpd.conf.5
index e437ce3..2dffca3 100644
--- a/server/dhcpd.conf.5
+++ b/server/dhcpd.conf.5
@@ -1576,10 +1576,12 @@ should be a name identifying the host.  If a \fIhostname\fR option is
 not specified for the host, \fIhostname\fR is used.
 .PP
 \fIHost\fR declarations are matched to actual DHCP or BOOTP clients
-by matching the \fRdhcp-client-identifier\fR option specified in the
+by matching the \fIdhcp-client-identifier\fR or \fIpxe-client-id\fR
+options specified in the
 \fIhost\fR declaration to the one supplied by the client, or, if the
 \fIhost\fR declaration or the client does not provide a
-\fRdhcp-client-identifier\fR option, by matching the \fIhardware\fR
+\fIdhcp-client-identifier\fR or \fIpxe-client-id\fR options,
+by matching the \fIhardware\fR
 parameter in the \fIhost\fR declaration to the network hardware
 address supplied by the client.  BOOTP clients do not normally
 provide a \fIdhcp-client-identifier\fR, so the hardware address must
@@ -1591,7 +1593,8 @@ to identify hosts.
 .PP
 Please be aware that
 .B only
-the \fIdhcp-client-identifier\fR option and the hardware address can be
+the \fIdhcp-client-identifier\fR and \fIpxe-client-id\fR
+options and the hardware address can be
 used to match a host declaration, or the \fIhost-identifier option\fR
 parameter for DHCPv6 servers.  For example, it is not possible to
 match a host declaration to a \fIhost-name\fR option.  This is
diff --git a/server/failover.c b/server/failover.c
index 2486a73..fc11e92 100644
--- a/server/failover.c
+++ b/server/failover.c
@@ -5938,6 +5938,9 @@ int load_balance_mine (struct packet *packet, dhcp_failover_state_t *state)
 
 	oc = lookup_option(&dhcp_universe, packet->options,
 			   DHO_DHCP_CLIENT_IDENTIFIER);
+	if (!oc)
+		oc = lookup_option(&dhcp_universe, packet -> options,
+				    DHO_PXE_CLIENT_ID);
 	memset(&ds, 0, sizeof ds);
 	if (oc &&
 	    evaluate_option_cache(&ds, packet, NULL, NULL,
diff --git a/server/mdb.c b/server/mdb.c
index 425340b..11e6e60 100644
--- a/server/mdb.c
+++ b/server/mdb.c
@@ -130,8 +130,9 @@ static int find_uid_statement (struct executable_statement *esp,
 	    esp -> data.option &&
 	    (esp -> data.option -> option -> universe ==
 	     &dhcp_universe) &&
-	    (esp -> data.option -> option -> code ==
-	     DHO_DHCP_CLIENT_IDENTIFIER)) {
+	    ((esp -> data.option -> option -> code ==
+						DHO_DHCP_CLIENT_IDENTIFIER) ||
+	     (esp -> data.option -> option -> code == DHO_PXE_CLIENT_ID))) {
 		if (condp) {
 			log_error ("dhcp client identifier may not be %s",
 				   "specified conditionally.");
-- 
2.1.0

