For network boot clients, dhcpd(8) can supply a filename for the initial
boot file for the client, which is something like pxeboot (or pxelinux.0).
EFI and BIOS clients need different boot files, though, so the server
needs to know what mode the client is booting in, in order to supply the
right filename.  RFC 4578 defines DHCP client option 93 for this purpose.

The ISC dhcpd approach to using this looks something like this:

  option arch code 93 = unsigned integer 16;

  if option arch = 00:00 {
    filename "bios/pxelinux.0";
  } elsif  option arch = 00:07 {
     filename "efi.x64/syslinux.efi";
  }

which seems a bit complicated, and also a lot of work to implement.  Instead
I propose adding 'efi-filename' (and 'efi32-filename', though I'm not sure
that's worth doing) next to the existing 'filename' statement and having
dhcpd itself interpret the option values to figure out which one should be
used.

I don't actually need this yet because we don't have many EFI-only client
machines, so we can just run everything in legacy mode, but some day this
may change.

Does this seem reasonable?

Index: conflex.c
===================================================================
RCS file: /cvs/src/usr.sbin/dhcpd/conflex.c,v
retrieving revision 1.16
diff -u -p -u -p -r1.16 conflex.c
--- conflex.c   6 Feb 2016 23:50:10 -0000       1.16
+++ conflex.c   23 Jan 2017 08:09:15 -0000
@@ -310,6 +310,8 @@ static const struct keywords {
        { "dynamic-bootp",                      TOK_DYNAMIC_BOOTP },
        { "dynamic-bootp-lease-cutoff",         TOK_DYNAMIC_BOOTP_LEASE_CUTOFF 
},
        { "dynamic-bootp-lease-length",         TOK_DYNAMIC_BOOTP_LEASE_LENGTH 
},
+       { "efi-filename",                       TOK_EFI64_FILENAME },
+       { "efi32-filename",                     TOK_EFI32_FILENAME },
        { "ends",                               TOK_ENDS },
        { "ethernet",                           TOK_ETHERNET },
        { "filename",                           TOK_FILENAME },
Index: confpars.c
===================================================================
RCS file: /cvs/src/usr.sbin/dhcpd/confpars.c,v
retrieving revision 1.28
diff -u -p -u -p -r1.28 confpars.c
--- confpars.c  17 Aug 2016 00:55:33 -0000      1.28
+++ confpars.c  23 Jan 2017 08:09:15 -0000
@@ -380,6 +380,14 @@ parse_statement(FILE *cfile, struct grou
                group->filename = parse_string(cfile);
                break;
 
+       case TOK_EFI32_FILENAME:
+               group->efi32_filename = parse_string(cfile);
+               break;
+
+       case TOK_EFI64_FILENAME:
+               group->efi64_filename = parse_string(cfile);
+               break;
+
        case TOK_SERVER_NAME:
                group->server_name = parse_string(cfile);
                break;
Index: dhcp.c
===================================================================
RCS file: /cvs/src/usr.sbin/dhcpd/dhcp.c,v
retrieving revision 1.52
diff -u -p -u -p -r1.52 dhcp.c
--- dhcp.c      24 Oct 2016 21:05:55 -0000      1.52
+++ dhcp.c      23 Jan 2017 08:09:15 -0000
@@ -699,6 +699,35 @@ nak_lease(struct packet *packet, struct 
            outgoing.packet_length, from, &to, NULL);
 }
 
+static const char *
+group_filename(struct group *group, struct packet *packet)
+{
+       u_int16_t arch, *opt;
+       int i;
+
+       opt = (u_int16_t *)packet->options[DHO_DHCP_CLIENT_ARCHITECTURE].data;
+       for (i = 0; i < packet->options[DHO_DHCP_CLIENT_ARCHITECTURE].len;
+           i += 2) {
+               arch = betoh16(*opt++);
+               switch (arch) {
+               case DCA_EFI_IA32:
+                       if (group->efi32_filename != NULL)
+                               return group->efi32_filename;
+                       break;
+                       
+               case DCA_EFI_BC:
+               case DCA_EFI_X86_64:
+                       if (group->efi64_filename != NULL)
+                               return group->efi64_filename;
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       return group->filename;
+}
+
 void
 ack_lease(struct packet *packet, struct lease *lease, unsigned int offer,
     time_t when)
@@ -707,6 +736,7 @@ ack_lease(struct packet *packet, struct 
        struct lease_state *state;
        time_t lease_time, offered_lease_time, max_lease_time, 
default_lease_time;
        struct class *vendor_class, *user_class;
+       const char *filename;
        int ulafdr, i;
 
        /* If we're already acking this lease, don't do it again. */
@@ -808,21 +838,20 @@ ack_lease(struct packet *packet, struct 
         * Choose a filename; first from the host_decl, if any, then from
         * the user class, then from the vendor class.
         */
-       if (lease->host && lease->host->group->filename)
-               strlcpy(state->filename, lease->host->group->filename,
-                   sizeof state->filename);
-       else if (user_class && user_class->group->filename)
-               strlcpy(state->filename, user_class->group->filename,
-                   sizeof state->filename);
-       else if (vendor_class && vendor_class->group->filename)
-               strlcpy(state->filename, vendor_class->group->filename,
-                   sizeof state->filename);
+       if (lease->host && (filename = group_filename(lease->host->group,
+           packet)))
+               strlcpy(state->filename, filename, sizeof state->filename);
+       else if (user_class && (filename = group_filename(user_class->group,
+           packet)))
+               strlcpy(state->filename, filename, sizeof state->filename);
+       else if (vendor_class && (filename = group_filename(vendor_class->group,
+           packet)))
+               strlcpy(state->filename, filename, sizeof state->filename);
        else if (packet->raw->file[0])
                strlcpy(state->filename, packet->raw->file,
                    sizeof state->filename);
-       else if (lease->subnet->group->filename)
-               strlcpy(state->filename, lease->subnet->group->filename,
-                   sizeof state->filename);
+       else if ((filename = group_filename(lease->subnet->group, packet)))
+               strlcpy(state->filename, filename, sizeof state->filename);
        else
                strlcpy(state->filename, "", sizeof state->filename);
 
Index: dhcp.h
===================================================================
RCS file: /cvs/src/usr.sbin/dhcpd/dhcp.h,v
retrieving revision 1.10
diff -u -p -u -p -r1.10 dhcp.h
--- dhcp.h      21 Jan 2014 03:07:51 -0000      1.10
+++ dhcp.h      23 Jan 2017 08:09:15 -0000
@@ -171,6 +171,7 @@ struct dhcp_packet {
 #define DHO_NDS_SERVERS                        85
 #define DHO_NDS_TREE_NAME              86
 #define DHO_NDS_CONTEXT                        87
+#define DHO_DHCP_CLIENT_ARCHITECTURE   93
 #define DHO_CLASSLESS_STATIC_ROUTES    121
 #define DHO_TFTP_CONFIG_FILE           144
 #define DHO_VOIP_CONFIGURATION_SERVER  150
@@ -192,3 +193,9 @@ struct dhcp_packet {
 #define RAI_CIRCUIT_ID 1
 #define RAI_REMOTE_ID  2
 #define RAI_AGENT_ID   3
+
+/* DHCP client architectures (RFC 4578) */
+#define DCA_X86_LEGACY 0
+#define DCA_EFI_IA32   6
+#define DCA_EFI_BC     7
+#define DCA_EFI_X86_64 9
Index: dhcpd.conf.5
===================================================================
RCS file: /cvs/src/usr.sbin/dhcpd/dhcpd.conf.5,v
retrieving revision 1.17
diff -u -p -u -p -r1.17 dhcpd.conf.5
--- dhcpd.conf.5        11 Jun 2015 12:48:32 -0000      1.17
+++ dhcpd.conf.5        23 Jan 2017 08:09:15 -0000
@@ -609,15 +609,28 @@ The
 statement may also be used for DHCP clients.
 .Pp
 The
-.Ic filename
-statement
+.Ic filename ,
+.Ic efi-filename ,
+and
+.Ic efi32-filename
+statements
 .Pp
 .D1 Ic filename Qq Ar filename ;
+.D1 Ic efi-filename Qq Ar filename ;
+.D1 Ic efi32-filename Qq Ar filename ;
 .Pp
 The
-.Ic filename
-statement can be used to specify the name of the initial boot file which
+.Ic filename ,
+.Ic efi-filename ,
+and
+.Ic efi32-filename
+statements can be used to specify the name of the initial boot file which
 is to be loaded by a client.
+If specified,
+.Ic efi-filename
+will be used for 64-bit EFI clients, and
+.Ic efi32-filename
+will be used for 32-bit EFI clients.
 The
 .Ar filename
 should be a filename recognizable to whatever file transfer protocol
Index: dhcpd.h
===================================================================
RCS file: /cvs/src/usr.sbin/dhcpd/dhcpd.h,v
retrieving revision 1.55
diff -u -p -u -p -r1.55 dhcpd.h
--- dhcpd.h     6 Oct 2016 16:12:43 -0000       1.55
+++ dhcpd.h     23 Jan 2017 08:09:15 -0000
@@ -211,6 +211,8 @@ struct group {
        time_t bootp_lease_length;
 
        char *filename;
+       char *efi32_filename;
+       char *efi64_filename;
        char *server_name;
        struct iaddr next_server;
 
Index: dhctoken.h
===================================================================
RCS file: /cvs/src/usr.sbin/dhcpd/dhctoken.h,v
retrieving revision 1.7
diff -u -p -u -p -r1.7 dhctoken.h
--- dhctoken.h  5 Dec 2013 22:31:35 -0000       1.7
+++ dhctoken.h  23 Jan 2017 08:09:15 -0000
@@ -91,6 +91,8 @@
 #define TOK_TOKEN_NOT                  334
 #define TOK_ALWAYS_REPLY_RFC1048       335
 #define TOK_IPSEC_TUNNEL               336
+#define TOK_EFI32_FILENAME             337
+#define TOK_EFI64_FILENAME             338
 
 #define is_identifier(x)       ((x) >= TOK_FIRST_TOKEN &&      \
                                 (x) != TOK_STRING &&   \

Reply via email to