summaryrefslogtreecommitdiffstats
path: root/contrib/syslinux-4.02/core/fs/pxe/dhcp_option.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/syslinux-4.02/core/fs/pxe/dhcp_option.c')
-rw-r--r--contrib/syslinux-4.02/core/fs/pxe/dhcp_option.c258
1 files changed, 258 insertions, 0 deletions
diff --git a/contrib/syslinux-4.02/core/fs/pxe/dhcp_option.c b/contrib/syslinux-4.02/core/fs/pxe/dhcp_option.c
new file mode 100644
index 0000000..50f2de0
--- /dev/null
+++ b/contrib/syslinux-4.02/core/fs/pxe/dhcp_option.c
@@ -0,0 +1,258 @@
+#include <stdio.h>
+#include <string.h>
+#include <core.h>
+#include <sys/cpu.h>
+#include "pxe.h"
+
+char LocalDomain[256];
+
+int over_load;
+uint8_t uuid_type;
+uint8_t uuid[16];
+
+static void parse_dhcp_options(const void *, int, uint8_t);
+
+static void subnet_mask(const void *data, int opt_len)
+{
+ if (opt_len != 4)
+ return;
+ IPInfo.netmask = *(const uint32_t *)data;
+}
+
+static void router(const void *data, int opt_len)
+{
+ if (opt_len != 4)
+ return;
+ IPInfo.gateway = *(const uint32_t *)data;
+}
+
+static void dns_servers(const void *data, int opt_len)
+{
+ const uint32_t *dp = data;
+ int num = 0;
+
+ while (num < DNS_MAX_SERVERS) {
+ uint32_t ip;
+
+ if (opt_len < 4)
+ break;
+
+ opt_len -= 4;
+ ip = *dp++;
+ if (ip_ok(ip))
+ dns_server[num++] = ip;
+ }
+ while (num < DNS_MAX_SERVERS)
+ dns_server[num++] = 0;
+}
+
+static void local_domain(const void *data, int opt_len)
+{
+ char buffer[256];
+ char *ld = LocalDomain;
+
+ memcpy(buffer, data, opt_len);
+ buffer[opt_len] = 0;
+
+ dns_mangle(&ld, buffer);
+}
+
+static void vendor_encaps(const void *data, int opt_len)
+{
+ /* Only recognize PXELINUX options */
+ parse_dhcp_options(data, opt_len, 208);
+}
+
+static void option_overload(const void *data, int opt_len)
+{
+ if (opt_len != 1)
+ return;
+ over_load = *(uint8_t *)data;
+}
+
+static void server(const void *data, int opt_len)
+{
+ uint32_t ip;
+
+ if (opt_len != 4)
+ return;
+
+ if (IPInfo.serverip)
+ return;
+
+ ip = *(uint32_t *)data;
+ if (ip_ok(ip))
+ IPInfo.serverip = ip;
+}
+
+static void client_identifier(const void *data, int opt_len)
+{
+ if (opt_len > MAC_MAX || opt_len < 2 ||
+ MAC_len != (opt_len >> 8) ||
+ *(uint8_t *)data != MAC_type)
+ return;
+
+ opt_len --;
+ MAC_len = opt_len & 0xff;
+ memcpy(MAC, data+1, opt_len);
+ MAC[opt_len] = 0;
+}
+
+static void bootfile_name(const void *data, int opt_len)
+{
+ memcpy(boot_file, data, opt_len);
+ boot_file[opt_len] = 0;
+}
+
+static void uuid_client_identifier(const void *data, int opt_len)
+{
+ int type = *(const uint8_t *)data;
+ if (opt_len != 17 || type != 0 || have_uuid)
+ return;
+
+ have_uuid = true;
+ uuid_type = type;
+ memcpy(uuid, data+1, 16);
+}
+
+static void pxelinux_configfile(const void *data, int opt_len)
+{
+ DHCPMagic |= 2;
+ memcpy(ConfigName, data, opt_len);
+ ConfigName[opt_len] = 0;
+}
+
+static void pxelinux_pathprefix(const void *data, int opt_len)
+{
+ DHCPMagic |= 4;
+ memcpy(path_prefix, data, opt_len);
+ path_prefix[opt_len] = 0;
+}
+
+static void pxelinux_reboottime(const void *data, int opt_len)
+{
+ if (opt_len != 4)
+ return;
+
+ RebootTime = ntohl(*(const uint32_t *)data);
+ DHCPMagic |= 8; /* Got reboot time */
+}
+
+
+struct dhcp_options {
+ int opt_num;
+ void (*fun)(const void *, int);
+};
+
+static const struct dhcp_options dhcp_opts[] = {
+ {1, subnet_mask},
+ {3, router},
+ {6, dns_servers},
+ {15, local_domain},
+ {43, vendor_encaps},
+ {52, option_overload},
+ {54, server},
+ {61, client_identifier},
+ {67, bootfile_name},
+ {97, uuid_client_identifier},
+ {209, pxelinux_configfile},
+ {210, pxelinux_pathprefix},
+ {211, pxelinux_reboottime}
+};
+
+/*
+ * Parse a sequence of DHCP options, pointed to by _option_;
+ * -- some DHCP servers leave option fields unterminated
+ * in violation of the spec.
+ *
+ * filter contains the minimum value for the option to recognize
+ * -- this is used to restrict parsing to PXELINUX-specific options only.
+ */
+static void parse_dhcp_options(const void *option, int size, uint8_t opt_filter)
+{
+ int opt_num;
+ int opt_len;
+ const int opt_entries = sizeof(dhcp_opts) / sizeof(dhcp_opts[0]);
+ int i = 0;
+ const uint8_t *p = option;
+ const struct dhcp_options *opt;
+
+ /* The only 1-byte options are 00 and FF, neither of which matter */
+ while (size >= 2) {
+ opt_num = *p++;
+ size--;
+
+ if (opt_num == 0)
+ continue;
+ if (opt_num == 0xff)
+ break;
+
+ /* Anything else will have a length field */
+ opt_len = *p++; /* c <- option lenght */
+ size -= opt_len + 1;
+ if (size < 0)
+ break;
+
+ if (opt_num >= opt_filter) {
+ opt = dhcp_opts;
+ for (i = 0; i < opt_entries; i++) {
+ if (opt_num == opt->opt_num) {
+ opt->fun(p, opt_len);
+ break;
+ }
+ opt++;
+ }
+ }
+
+ /* parse next */
+ p += opt_len;
+ }
+}
+
+/*
+ * parse_dhcp
+ *
+ * Parse a DHCP packet. This includes dealing with "overloaded"
+ * option fields (see RFC 2132, section 9.3)
+ *
+ * This should fill in the following global variables, if the
+ * information is present:
+ *
+ * MyIP - client IP address
+ * server_ip - boot server IP address
+ * net_mask - network mask
+ * gate_way - default gateway router IP
+ * boot_file - boot file name
+ * DNSServers - DNS server IPs
+ * LocalDomain - Local domain name
+ * MAC_len, MAC - Client identifier, if MAC_len == 0
+ *
+ * This assumes the DHCP packet is in "trackbuf".
+ *
+ */
+void parse_dhcp(int pkt_len)
+{
+ struct bootp_t *dhcp = (struct bootp_t *)trackbuf;
+ int opt_len;
+
+ IPInfo.ipv4 = 4; /* This is IPv4 only for now... */
+
+ over_load = 0;
+ if (ip_ok(dhcp->yip))
+ IPInfo.myip = dhcp->yip;
+
+ if (ip_ok(dhcp->sip))
+ IPInfo.serverip = dhcp->sip;
+
+ opt_len = (char *)dhcp + pkt_len - (char *)&dhcp->options;
+ if (opt_len && (dhcp->option_magic == BOOTP_OPTION_MAGIC))
+ parse_dhcp_options(&dhcp->options, opt_len, 0);
+
+ if (over_load & 1)
+ parse_dhcp_options(&dhcp->bootfile, 128, 0);
+ else if (dhcp->bootfile[0])
+ strcpy(boot_file, dhcp->bootfile);
+
+ if (over_load & 2)
+ parse_dhcp_options(dhcp->sname, 64, 0);
+}