1
2
3
4
5
6
7#include "common.h"
8
9#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
10unsigned dhcp_verbose;
11#endif
12
13const uint8_t MAC_BCAST_ADDR[6] ALIGN2 = {
14 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
15};
16
17
18
19
20
21const struct dhcp_optflag dhcp_optflags[] = {
22
23 { OPTION_IP | OPTION_REQ, 0x01 },
24 { OPTION_S32 , 0x02 },
25 { OPTION_IP | OPTION_LIST | OPTION_REQ, 0x03 },
26
27
28 { OPTION_IP | OPTION_LIST | OPTION_REQ, 0x06 },
29
30
31 { OPTION_IP | OPTION_LIST , 0x09 },
32 { OPTION_STRING | OPTION_REQ, 0x0c },
33 { OPTION_U16 , 0x0d },
34 { OPTION_STRING | OPTION_REQ, 0x0f },
35 { OPTION_IP , 0x10 },
36 { OPTION_STRING , 0x11 },
37 { OPTION_U8 , 0x17 },
38 { OPTION_U16 , 0x1a },
39 { OPTION_IP | OPTION_REQ, 0x1c },
40 { OPTION_IP_PAIR | OPTION_LIST , 0x21 },
41 { OPTION_STRING , 0x28 },
42 { OPTION_IP | OPTION_LIST , 0x29 },
43 { OPTION_IP | OPTION_LIST | OPTION_REQ, 0x2a },
44 { OPTION_IP | OPTION_LIST , 0x2c },
45 { OPTION_U32 , 0x33 },
46 { OPTION_IP , 0x36 },
47 { OPTION_STRING , 0x38 },
48
49 { OPTION_STRING , 0x42 },
50 { OPTION_STRING , 0x43 },
51
52
53#if ENABLE_FEATURE_UDHCP_RFC3397
54 { OPTION_DNS_STRING | OPTION_LIST , 0x77 },
55 { OPTION_SIP_SERVERS , 0x78 },
56#endif
57 { OPTION_STATIC_ROUTES , 0x79 },
58#if ENABLE_FEATURE_UDHCP_8021Q
59 { OPTION_U16 , 0x84 },
60 { OPTION_U8 , 0x85 },
61#endif
62 { OPTION_STATIC_ROUTES , 0xf9 },
63 { OPTION_STRING , 0xfc },
64
65
66
67
68
69
70
71
72 { OPTION_IP , 0x32 },
73 { OPTION_U8 , 0x35 },
74 { OPTION_U16 , 0x39 },
75
76
77
78
79 { 0, 0 }
80};
81
82
83
84
85
86
87const char dhcp_option_strings[] ALIGN1 =
88 "subnet" "\0"
89 "timezone" "\0"
90 "router" "\0"
91
92
93 "dns" "\0"
94
95
96 "lprsrv" "\0"
97 "hostname" "\0"
98 "bootsize" "\0"
99 "domain" "\0"
100 "swapsrv" "\0"
101 "rootpath" "\0"
102 "ipttl" "\0"
103 "mtu" "\0"
104 "broadcast" "\0"
105 "routes" "\0"
106 "nisdomain" "\0"
107 "nissrv" "\0"
108 "ntpsrv" "\0"
109 "wins" "\0"
110 "lease" "\0"
111 "serverid" "\0"
112 "message" "\0"
113 "tftp" "\0"
114 "bootfile" "\0"
115
116#if ENABLE_FEATURE_UDHCP_RFC3397
117 "search" "\0"
118
119
120 "sipsrv" "\0"
121#endif
122
123
124 "staticroutes" "\0"
125#if ENABLE_FEATURE_UDHCP_8021Q
126 "vlanid" "\0"
127 "vlanpriority" "\0"
128#endif
129 "msstaticroutes""\0"
130 "wpad" "\0"
131 ;
132
133
134
135
136
137
138
139
140
141const uint8_t dhcp_option_lengths[] ALIGN1 = {
142 [OPTION_IP] = 4,
143 [OPTION_IP_PAIR] = 8,
144
145 [OPTION_STRING] = 1,
146#if ENABLE_FEATURE_UDHCP_RFC3397
147 [OPTION_DNS_STRING] = 1,
148 [OPTION_SIP_SERVERS] = 1,
149#endif
150 [OPTION_U8] = 1,
151 [OPTION_U16] = 2,
152
153 [OPTION_U32] = 4,
154 [OPTION_S32] = 4,
155
156 [OPTION_STATIC_ROUTES] = 5,
157};
158
159
160#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 2
161static void log_option(const char *pfx, const uint8_t *opt)
162{
163 if (dhcp_verbose >= 2) {
164 char buf[256 * 2 + 2];
165 *bin2hex(buf, (void*) (opt + OPT_DATA), opt[OPT_LEN]) = '\0';
166 bb_info_msg("%s: 0x%02x %s", pfx, opt[OPT_CODE], buf);
167 }
168}
169#else
170# define log_option(pfx, opt) ((void)0)
171#endif
172
173unsigned FAST_FUNC udhcp_option_idx(const char *name)
174{
175 int n = index_in_strings(dhcp_option_strings, name);
176 if (n >= 0)
177 return n;
178
179 {
180 char buf[sizeof(dhcp_option_strings)];
181 char *d = buf;
182 const char *s = dhcp_option_strings;
183 while (s < dhcp_option_strings + sizeof(dhcp_option_strings) - 2) {
184 *d++ = (*s == '\0' ? ' ' : *s);
185 s++;
186 }
187 *d = '\0';
188 bb_error_msg_and_die("unknown option '%s', known options: %s", name, buf);
189 }
190}
191
192
193uint8_t* FAST_FUNC udhcp_get_option(struct dhcp_packet *packet, int code)
194{
195 uint8_t *optionptr;
196 int len;
197 int rem;
198 int overload = 0;
199 enum {
200 FILE_FIELD101 = FILE_FIELD * 0x101,
201 SNAME_FIELD101 = SNAME_FIELD * 0x101,
202 };
203
204
205 optionptr = packet->options;
206 rem = sizeof(packet->options);
207 while (1) {
208 if (rem <= 0) {
209 bb_error_msg("bad packet, malformed option field");
210 return NULL;
211 }
212 if (optionptr[OPT_CODE] == DHCP_PADDING) {
213 rem--;
214 optionptr++;
215 continue;
216 }
217 if (optionptr[OPT_CODE] == DHCP_END) {
218 if ((overload & FILE_FIELD101) == FILE_FIELD) {
219
220 overload |= FILE_FIELD101;
221 optionptr = packet->file;
222 rem = sizeof(packet->file);
223 continue;
224 }
225 if ((overload & SNAME_FIELD101) == SNAME_FIELD) {
226
227 overload |= SNAME_FIELD101;
228 optionptr = packet->sname;
229 rem = sizeof(packet->sname);
230 continue;
231 }
232 break;
233 }
234 len = 2 + optionptr[OPT_LEN];
235 rem -= len;
236 if (rem < 0)
237 continue;
238
239 if (optionptr[OPT_CODE] == code) {
240 log_option("Option found", optionptr);
241 return optionptr + OPT_DATA;
242 }
243
244 if (optionptr[OPT_CODE] == DHCP_OPTION_OVERLOAD) {
245 overload |= optionptr[OPT_DATA];
246
247 }
248 optionptr += len;
249 }
250
251
252 log3("Option 0x%02x not found", code);
253 return NULL;
254}
255
256
257int FAST_FUNC udhcp_end_option(uint8_t *optionptr)
258{
259 int i = 0;
260
261 while (optionptr[i] != DHCP_END) {
262 if (optionptr[i] != DHCP_PADDING)
263 i += optionptr[i + OPT_LEN] + OPT_DATA-1;
264 i++;
265 }
266 return i;
267}
268
269
270
271
272void FAST_FUNC udhcp_add_binary_option(struct dhcp_packet *packet, uint8_t *addopt)
273{
274 unsigned len;
275 uint8_t *optionptr = packet->options;
276 unsigned end = udhcp_end_option(optionptr);
277
278 len = OPT_DATA + addopt[OPT_LEN];
279
280 if (end + len + 1 >= DHCP_OPTIONS_BUFSIZE) {
281
282 bb_error_msg("option 0x%02x did not fit into the packet",
283 addopt[OPT_CODE]);
284 return;
285 }
286 log_option("Adding option", addopt);
287 memcpy(optionptr + end, addopt, len);
288 optionptr[end + len] = DHCP_END;
289}
290
291
292void FAST_FUNC udhcp_add_simple_option(struct dhcp_packet *packet, uint8_t code, uint32_t data)
293{
294 const struct dhcp_optflag *dh;
295
296 for (dh = dhcp_optflags; dh->code; dh++) {
297 if (dh->code == code) {
298 uint8_t option[6], len;
299
300 option[OPT_CODE] = code;
301 len = dhcp_option_lengths[dh->flags & OPTION_TYPE_MASK];
302 option[OPT_LEN] = len;
303 if (BB_BIG_ENDIAN)
304 data <<= 8 * (4 - len);
305
306 move_to_unaligned32(&option[OPT_DATA], data);
307 udhcp_add_binary_option(packet, option);
308 return;
309 }
310 }
311
312 bb_error_msg("can't add option 0x%02x", code);
313}
314
315
316struct option_set* FAST_FUNC udhcp_find_option(struct option_set *opt_list, uint8_t code)
317{
318 while (opt_list && opt_list->data[OPT_CODE] < code)
319 opt_list = opt_list->next;
320
321 if (opt_list && opt_list->data[OPT_CODE] == code)
322 return opt_list;
323 return NULL;
324}
325
326
327int FAST_FUNC udhcp_str2nip(const char *str, void *arg)
328{
329 len_and_sockaddr *lsa;
330
331 lsa = host_and_af2sockaddr(str, 0, AF_INET);
332 if (!lsa)
333 return 0;
334 *(uint32_t*)arg = lsa->u.sin.sin_addr.s_addr;
335 free(lsa);
336 return 1;
337}
338
339
340
341
342
343
344
345static char *allocate_tempopt_if_needed(
346 const struct dhcp_optflag *optflag,
347 char *buffer,
348 int *length_p)
349{
350 char *allocated = NULL;
351 if ((optflag->flags & OPTION_TYPE_MASK) == OPTION_BIN) {
352 const char *end;
353 allocated = xstrdup(buffer);
354 end = hex2bin(allocated, buffer, 255);
355 if (errno)
356 bb_error_msg_and_die("malformed hex string '%s'", buffer);
357 *length_p = end - allocated;
358 }
359 return allocated;
360}
361
362static NOINLINE void attach_option(
363 struct option_set **opt_list,
364 const struct dhcp_optflag *optflag,
365 char *buffer,
366 int length)
367{
368 struct option_set *existing, *new, **curr;
369 char *allocated = NULL;
370
371 existing = udhcp_find_option(*opt_list, optflag->code);
372 if (!existing) {
373 log2("Attaching option %02x to list", optflag->code);
374 allocated = allocate_tempopt_if_needed(optflag, buffer, &length);
375#if ENABLE_FEATURE_UDHCP_RFC3397
376 if ((optflag->flags & OPTION_TYPE_MASK) == OPTION_DNS_STRING) {
377
378 allocated = buffer = (char *)dname_enc(NULL, 0, buffer, &length);
379 }
380#endif
381
382 new = xmalloc(sizeof(*new));
383 new->data = xmalloc(length + OPT_DATA);
384 new->data[OPT_CODE] = optflag->code;
385 new->data[OPT_LEN] = length;
386 memcpy(new->data + OPT_DATA, (allocated ? allocated : buffer), length);
387
388 curr = opt_list;
389 while (*curr && (*curr)->data[OPT_CODE] < optflag->code)
390 curr = &(*curr)->next;
391
392 new->next = *curr;
393 *curr = new;
394 goto ret;
395 }
396
397 if (optflag->flags & OPTION_LIST) {
398 unsigned old_len;
399
400
401 log2("Attaching option %02x to existing member of list", optflag->code);
402 allocated = allocate_tempopt_if_needed(optflag, buffer, &length);
403 old_len = existing->data[OPT_LEN];
404#if ENABLE_FEATURE_UDHCP_RFC3397
405 if ((optflag->flags & OPTION_TYPE_MASK) == OPTION_DNS_STRING) {
406
407 allocated = buffer = (char *)dname_enc(existing->data + OPT_DATA, old_len, buffer, &length);
408 }
409#endif
410 if (old_len + length < 255) {
411
412
413 existing->data = xrealloc(existing->data, OPT_DATA + 1 + old_len + length);
414 if ((optflag->flags & OPTION_TYPE_MASK) == OPTION_STRING) {
415
416 existing->data[OPT_DATA + old_len] = ' ';
417 old_len++;
418 }
419 memcpy(existing->data + OPT_DATA + old_len, buffer, length);
420 existing->data[OPT_LEN] = old_len + length;
421 }
422 }
423
424 ret:
425 free(allocated);
426}
427
428int FAST_FUNC udhcp_str2optset(const char *const_str, void *arg)
429{
430 struct option_set **opt_list = arg;
431 char *opt, *val, *endptr;
432 char *str;
433 const struct dhcp_optflag *optflag;
434 struct dhcp_optflag bin_optflag;
435 unsigned optcode;
436 int retval, length;
437 char buffer[8] ALIGNED(4);
438 uint16_t *result_u16 = (uint16_t *) buffer;
439 uint32_t *result_u32 = (uint32_t *) buffer;
440
441
442 str = (char *) const_str;
443 opt = strtok(str, " \t=");
444 if (!opt)
445 return 0;
446
447 optcode = bb_strtou(opt, NULL, 0);
448 if (!errno && optcode < 255) {
449
450 bin_optflag.flags = OPTION_BIN;
451 bin_optflag.code = optcode;
452 optflag = &bin_optflag;
453 } else {
454 optflag = &dhcp_optflags[udhcp_option_idx(opt)];
455 }
456
457 retval = 0;
458 do {
459 val = strtok(NULL, ", \t");
460 if (!val)
461 break;
462 length = dhcp_option_lengths[optflag->flags & OPTION_TYPE_MASK];
463 retval = 0;
464 opt = buffer;
465 switch (optflag->flags & OPTION_TYPE_MASK) {
466 case OPTION_IP:
467 retval = udhcp_str2nip(val, buffer);
468 break;
469 case OPTION_IP_PAIR:
470 retval = udhcp_str2nip(val, buffer);
471 val = strtok(NULL, ", \t/-");
472 if (!val)
473 retval = 0;
474 if (retval)
475 retval = udhcp_str2nip(val, buffer + 4);
476 break;
477 case OPTION_STRING:
478#if ENABLE_FEATURE_UDHCP_RFC3397
479 case OPTION_DNS_STRING:
480#endif
481 length = strnlen(val, 254);
482 if (length > 0) {
483 opt = val;
484 retval = 1;
485 }
486 break;
487
488
489
490
491
492
493 case OPTION_U8:
494 buffer[0] = strtoul(val, &endptr, 0);
495 retval = (endptr[0] == '\0');
496 break;
497
498
499
500 case OPTION_U16: {
501 unsigned long tmp = strtoul(val, &endptr, 0);
502 *result_u16 = htons(tmp);
503 retval = (endptr[0] == '\0' );
504 break;
505 }
506
507
508
509
510
511
512 case OPTION_U32: {
513 unsigned long tmp = strtoul(val, &endptr, 0);
514 *result_u32 = htonl(tmp);
515 retval = (endptr[0] == '\0');
516 break;
517 }
518 case OPTION_S32: {
519 long tmp = strtol(val, &endptr, 0);
520 *result_u32 = htonl(tmp);
521 retval = (endptr[0] == '\0');
522 break;
523 }
524 case OPTION_BIN:
525 opt = val;
526 retval = 1;
527 default:
528 break;
529 }
530 if (retval)
531 attach_option(opt_list, optflag, opt, length);
532 } while (retval && optflag->flags & OPTION_LIST);
533
534 return retval;
535}
536