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
9 char LocalDomain[256];
10
11 int over_load;
12 uint8_t uuid_type;
13 uint8_t uuid[16];
14
subnet_mask(const void * data,int opt_len)15 static 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
router(const void * data,int opt_len)22 static 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
dns_servers(const void * data,int opt_len)29 static 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
local_domain(const void * data,int opt_len)49 static void local_domain(const void *data, int opt_len)
50 {
51 memcpy(LocalDomain, data, opt_len);
52 LocalDomain[opt_len] = 0;
53 }
54
vendor_encaps(const void * data,int opt_len)55 static 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
option_overload(const void * data,int opt_len)61 static 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
server(const void * data,int opt_len)68 static 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
client_identifier(const void * data,int opt_len)83 static 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
bootfile_name(const void * data,int opt_len)96 static 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
uuid_client_identifier(const void * data,int opt_len)102 static 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
pxelinux_configfile(const void * data,int opt_len)113 static 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
pxelinux_pathprefix(const void * data,int opt_len)120 static 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
pxelinux_reboottime(const void * data,int opt_len)127 static 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
137 struct dhcp_options {
138 int opt_num;
139 void (*fun)(const void *, int);
140 };
141
142 static 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 */
parse_dhcp_options(const void * option,int size,uint8_t opt_filter)166 void 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 */
parse_dhcp(const void * pkt,size_t pkt_len)228 void 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