1#include <stdio.h>
2#include <string.h>
3#include <core.h>
4#include <sys/cpu.h>
5#include <lwip/opt.h>		/* DNS_MAX_SERVERS */
6#include <dprintf.h>
7#include "pxe.h"
8
9char LocalDomain[256];
10
11int over_load;
12uint8_t uuid_type;
13uint8_t uuid[16];
14
15static void subnet_mask(const void *data, int opt_len)
16{
17    if (opt_len != 4)
18	return;
19    IPInfo.netmask = *(const uint32_t *)data;
20}
21
22static void router(const void *data, int opt_len)
23{
24    if (opt_len != 4)
25	return;
26    IPInfo.gateway = *(const uint32_t *)data;
27}
28
29static void dns_servers(const void *data, int opt_len)
30{
31    const uint32_t *dp = data;
32    int num = 0;
33
34    while (num < DNS_MAX_SERVERS) {
35	uint32_t ip;
36
37	if (opt_len < 4)
38	    break;
39
40	opt_len -= 4;
41	ip = *dp++;
42	if (ip_ok(ip))
43	    dns_server[num++] = ip;
44    }
45    while (num < DNS_MAX_SERVERS)
46	dns_server[num++] = 0;
47}
48
49static void local_domain(const void *data, int opt_len)
50{
51    memcpy(LocalDomain, data, opt_len);
52    LocalDomain[opt_len] = 0;
53}
54
55static void vendor_encaps(const void *data, int opt_len)
56{
57    /* Only recognize PXELINUX options */
58    parse_dhcp_options(data, opt_len, 208);
59}
60
61static void option_overload(const void *data, int opt_len)
62{
63    if (opt_len != 1)
64	return;
65    over_load = *(uint8_t *)data;
66}
67
68static void server(const void *data, int opt_len)
69{
70    uint32_t ip;
71
72    if (opt_len != 4)
73	return;
74
75    if (IPInfo.serverip)
76        return;
77
78    ip = *(uint32_t *)data;
79    if (ip_ok(ip))
80        IPInfo.serverip = ip;
81}
82
83static void client_identifier(const void *data, int opt_len)
84{
85    if (opt_len > MAC_MAX || opt_len < 2 ||
86        MAC_len != (opt_len >> 8) ||
87        *(uint8_t *)data != MAC_type)
88        return;
89
90    opt_len --;
91    MAC_len = opt_len & 0xff;
92    memcpy(MAC, data+1, opt_len);
93    MAC[opt_len] = 0;
94}
95
96static void bootfile_name(const void *data, int opt_len)
97{
98    memcpy(boot_file, data, opt_len);
99    boot_file[opt_len] = 0;
100}
101
102static void uuid_client_identifier(const void *data, int opt_len)
103{
104    int type = *(const uint8_t *)data;
105    if (opt_len != 17 || type != 0 || have_uuid)
106        return;
107
108    have_uuid = true;
109    uuid_type = type;
110    memcpy(uuid, data+1, 16);
111}
112
113static void pxelinux_configfile(const void *data, int opt_len)
114{
115    DHCPMagic |= 2;
116    memcpy(ConfigName, data, opt_len);
117    ConfigName[opt_len] = 0;
118}
119
120static void pxelinux_pathprefix(const void *data, int opt_len)
121{
122    DHCPMagic |= 4;
123    memcpy(path_prefix, data, opt_len);
124    path_prefix[opt_len] = 0;
125}
126
127static void pxelinux_reboottime(const void *data, int opt_len)
128{
129    if (opt_len != 4)
130        return;
131
132    RebootTime = ntohl(*(const uint32_t *)data);
133    DHCPMagic |= 8;     /* Got reboot time */
134}
135
136
137struct dhcp_options {
138    int opt_num;
139    void (*fun)(const void *, int);
140};
141
142static const struct dhcp_options dhcp_opts[] = {
143    {1,   subnet_mask},
144    {3,   router},
145    {6,   dns_servers},
146    {15,  local_domain},
147    {43,  vendor_encaps},
148    {52,  option_overload},
149    {54,  server},
150    {61,  client_identifier},
151    {67,  bootfile_name},
152    {97,  uuid_client_identifier},
153    {209, pxelinux_configfile},
154    {210, pxelinux_pathprefix},
155    {211, pxelinux_reboottime}
156};
157
158/*
159 * Parse a sequence of DHCP options, pointed to by _option_;
160 * -- some DHCP servers leave option fields unterminated
161 * in violation of the spec.
162 *
163 * filter  contains the minimum value for the option to recognize
164 * -- this is used to restrict parsing to PXELINUX-specific options only.
165 */
166void parse_dhcp_options(const void *option, int size, uint8_t opt_filter)
167{
168    int opt_num;
169    int opt_len;
170    const int opt_entries = sizeof(dhcp_opts) / sizeof(dhcp_opts[0]);
171    int i = 0;
172    const uint8_t *p = option;
173    const struct dhcp_options *opt;
174
175    /* The only 1-byte options are 00 and FF, neither of which matter */
176    while (size >= 2) {
177        opt_num = *p++;
178	size--;
179
180        if (opt_num == 0)
181            continue;
182        if (opt_num == 0xff)
183            break;
184
185        /* Anything else will have a length field */
186        opt_len = *p++; /* c  <- option lenght */
187        size -= opt_len + 1;
188        if (size < 0)
189            break;
190
191	dprintf("DHCP: option %d, len %d\n", opt_num, opt_len);
192
193	if (opt_num >= opt_filter) {
194	    opt = dhcp_opts;
195	    for (i = 0; i < opt_entries; i++) {
196		if (opt_num == opt->opt_num) {
197		    opt->fun(p, opt_len);
198		    break;
199		}
200		opt++;
201	    }
202	}
203
204        /* parse next */
205        p += opt_len;
206    }
207}
208
209/*
210 * parse_dhcp
211 *
212 * Parse a DHCP packet.  This includes dealing with "overloaded"
213 * option fields (see RFC 2132, section 9.3)
214 *
215 * This should fill in the following global variables, if the
216 * information is present:
217 *
218 * MyIP		- client IP address
219 * server_ip	- boot server IP address
220 * net_mask	- network mask
221 * gate_way	- default gateway router IP
222 * boot_file	- boot file name
223 * DNSServers	- DNS server IPs
224 * LocalDomain	- Local domain name
225 * MAC_len, MAC	- Client identifier, if MAC_len == 0
226 *
227 */
228void parse_dhcp(const void *pkt, size_t pkt_len)
229{
230    const struct bootp_t *dhcp = (const struct bootp_t *)pkt;
231    int opt_len;
232
233    IPInfo.ipver = 4;		/* This is IPv4 only for now... */
234
235    over_load = 0;
236    if (ip_ok(dhcp->yip))
237        IPInfo.myip = dhcp->yip;
238
239    if (ip_ok(dhcp->sip))
240        IPInfo.serverip = dhcp->sip;
241
242    opt_len = (char *)dhcp + pkt_len - (char *)&dhcp->options;
243    if (opt_len && (dhcp->option_magic == BOOTP_OPTION_MAGIC))
244        parse_dhcp_options(&dhcp->options, opt_len, 0);
245
246    if (over_load & 1)
247        parse_dhcp_options(&dhcp->bootfile, 128, 0);
248    else if (dhcp->bootfile[0])
249	strcpy(boot_file, dhcp->bootfile);
250
251    if (over_load & 2)
252        parse_dhcp_options(dhcp->sname, 64, 0);
253}
254