blob: 1fdcc7051b4d91a5a95ac8506e1add0adc8b8d5d [file] [log] [blame]
#include <stdio.h>
#include <string.h>
#include <core.h>
#include <sys/cpu.h>
#include <lwip/opt.h> /* DNS_MAX_SERVERS */
#include <dprintf.h>
#include "pxe.h"
char LocalDomain[256];
int over_load;
uint8_t uuid_type;
uint8_t uuid[16];
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)
{
memcpy(LocalDomain, data, opt_len);
LocalDomain[opt_len] = 0;
}
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.
*/
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;
dprintf("DHCP: option %d, len %d\n", opt_num, opt_len);
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
*
*/
void parse_dhcp(const void *pkt, size_t pkt_len)
{
const struct bootp_t *dhcp = (const struct bootp_t *)pkt;
int opt_len;
IPInfo.ipver = 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);
}