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#if ENABLE_UDHCPC || ENABLE_UDHCPD
18
19
20
21
22const struct dhcp_optflag dhcp_optflags[] = {
23
24 { OPTION_IP | OPTION_REQ, 0x01 },
25 { OPTION_S32 , 0x02 },
26 { OPTION_IP | OPTION_LIST | OPTION_REQ, 0x03 },
27
28
29 { OPTION_IP | OPTION_LIST | OPTION_REQ, 0x06 },
30
31
32 { OPTION_IP | OPTION_LIST , 0x09 },
33 { OPTION_STRING_HOST | OPTION_REQ, 0x0c },
34 { OPTION_U16 , 0x0d },
35 { OPTION_STRING_HOST | OPTION_REQ, 0x0f },
36 { OPTION_IP , 0x10 },
37 { OPTION_STRING , 0x11 },
38 { OPTION_U8 , 0x17 },
39 { OPTION_U16 , 0x1a },
40
41
42
43 { OPTION_IP | OPTION_REQ, 0x1c },
44 { OPTION_IP_PAIR | OPTION_LIST , 0x21 },
45 { OPTION_STRING_HOST , 0x28 },
46 { OPTION_IP | OPTION_LIST , 0x29 },
47 { OPTION_IP | OPTION_LIST | OPTION_REQ, 0x2a },
48 { OPTION_IP | OPTION_LIST , 0x2c },
49 { OPTION_U32 , 0x33 },
50 { OPTION_IP , 0x36 },
51 { OPTION_STRING , 0x38 },
52
53 { OPTION_STRING_HOST , 0x42 },
54 { OPTION_STRING , 0x43 },
55
56
57 { OPTION_STRING , 0x64 },
58 { OPTION_STRING , 0x65 },
59#if ENABLE_FEATURE_UDHCP_RFC3397
60 { OPTION_DNS_STRING | OPTION_LIST , 0x77 },
61 { OPTION_SIP_SERVERS , 0x78 },
62#endif
63 { OPTION_STATIC_ROUTES | OPTION_LIST , 0x79 },
64#if ENABLE_FEATURE_UDHCP_8021Q
65 { OPTION_U16 , 0x84 },
66 { OPTION_U8 , 0x85 },
67#endif
68 { OPTION_STRING , 0xd1 },
69 { OPTION_STRING , 0xd2 },
70 { OPTION_U32 , 0xd3 },
71 { OPTION_6RD , 0xd4 },
72 { OPTION_STATIC_ROUTES | OPTION_LIST , 0xf9 },
73 { OPTION_STRING , 0xfc },
74
75
76
77
78
79
80
81
82 { OPTION_IP , 0x32 },
83 { OPTION_U8 , 0x35 },
84 { OPTION_U16 , 0x39 },
85
86
87
88
89 { 0, 0 }
90};
91
92
93
94
95
96
97const char dhcp_option_strings[] ALIGN1 =
98 "subnet" "\0"
99 "timezone" "\0"
100 "router" "\0"
101
102
103 "dns" "\0"
104
105
106 "lprsrv" "\0"
107 "hostname" "\0"
108 "bootsize" "\0"
109 "domain" "\0"
110 "swapsrv" "\0"
111 "rootpath" "\0"
112 "ipttl" "\0"
113 "mtu" "\0"
114 "broadcast" "\0"
115 "routes" "\0"
116 "nisdomain" "\0"
117 "nissrv" "\0"
118 "ntpsrv" "\0"
119 "wins" "\0"
120 "lease" "\0"
121 "serverid" "\0"
122 "message" "\0"
123 "tftp" "\0"
124 "bootfile" "\0"
125
126 "tzstr" "\0"
127 "tzdbstr" "\0"
128#if ENABLE_FEATURE_UDHCP_RFC3397
129 "search" "\0"
130
131
132 "sipsrv" "\0"
133#endif
134 "staticroutes" "\0"
135#if ENABLE_FEATURE_UDHCP_8021Q
136 "vlanid" "\0"
137 "vlanpriority" "\0"
138#endif
139 "pxeconffile" "\0"
140 "pxepathprefix" "\0"
141 "reboottime" "\0"
142 "ip6rd" "\0"
143 "msstaticroutes" "\0"
144 "wpad" "\0"
145 ;
146#endif
147
148
149
150
151
152
153
154
155
156const uint8_t dhcp_option_lengths[] ALIGN1 = {
157 [OPTION_IP] = 4,
158 [OPTION_IP_PAIR] = 8,
159
160 [OPTION_STRING] = 1,
161 [OPTION_STRING_HOST] = 1,
162#if ENABLE_FEATURE_UDHCP_RFC3397
163 [OPTION_DNS_STRING] = 1,
164 [OPTION_SIP_SERVERS] = 1,
165#endif
166 [OPTION_U8] = 1,
167 [OPTION_U16] = 2,
168
169 [OPTION_U32] = 4,
170 [OPTION_S32] = 4,
171
172 [OPTION_STATIC_ROUTES] = 5,
173 [OPTION_6RD] = 12,
174
175
176
177
178
179
180
181
182
183
184
185};
186
187
188#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 2
189static void log_option(const char *pfx, const uint8_t *opt)
190{
191 if (dhcp_verbose >= 2) {
192 char buf[256 * 2 + 2];
193 *bin2hex(buf, (void*) (opt + OPT_DATA), opt[OPT_LEN]) = '\0';
194 bb_info_msg("%s: 0x%02x %s", pfx, opt[OPT_CODE], buf);
195 }
196}
197#else
198# define log_option(pfx, opt) ((void)0)
199#endif
200
201unsigned FAST_FUNC udhcp_option_idx(const char *name, const char *option_strings)
202{
203 int n = index_in_strings(option_strings, name);
204 if (n >= 0)
205 return n;
206
207 {
208 char *buf, *d;
209 const char *s;
210
211 s = option_strings;
212 while (*s)
213 s += strlen(s) + 1;
214
215 d = buf = xzalloc(s - option_strings);
216 s = option_strings;
217 while (!(*s == '\0' && s[1] == '\0')) {
218 *d++ = (*s == '\0' ? ' ' : *s);
219 s++;
220 }
221 bb_error_msg_and_die("unknown option '%s', known options: %s", name, buf);
222 }
223}
224
225
226uint8_t* FAST_FUNC udhcp_get_option(struct dhcp_packet *packet, int code)
227{
228 uint8_t *optionptr;
229 int len;
230 int rem;
231 int overload = 0;
232 enum {
233 FILE_FIELD101 = FILE_FIELD * 0x101,
234 SNAME_FIELD101 = SNAME_FIELD * 0x101,
235 };
236
237
238 optionptr = packet->options;
239 rem = sizeof(packet->options);
240 while (1) {
241 if (rem <= 0) {
242 complain:
243 bb_error_msg("bad packet, malformed option field");
244 return NULL;
245 }
246
247
248 if (optionptr[OPT_CODE] == DHCP_PADDING) {
249 rem--;
250 optionptr++;
251 continue;
252 }
253 if (optionptr[OPT_CODE] == DHCP_END) {
254 if ((overload & FILE_FIELD101) == FILE_FIELD) {
255
256 overload |= FILE_FIELD101;
257 optionptr = packet->file;
258 rem = sizeof(packet->file);
259 continue;
260 }
261 if ((overload & SNAME_FIELD101) == SNAME_FIELD) {
262
263 overload |= SNAME_FIELD101;
264 optionptr = packet->sname;
265 rem = sizeof(packet->sname);
266 continue;
267 }
268 break;
269 }
270
271 if (rem <= OPT_LEN)
272 goto complain;
273 len = 2 + optionptr[OPT_LEN];
274 rem -= len;
275 if (rem < 0)
276 goto complain;
277
278 if (optionptr[OPT_CODE] == code) {
279 if (optionptr[OPT_LEN] == 0) {
280
281
282
283
284
285
286 goto complain;
287 }
288 log_option("option found", optionptr);
289 return optionptr + OPT_DATA;
290 }
291
292 if (optionptr[OPT_CODE] == DHCP_OPTION_OVERLOAD) {
293 if (len >= 3)
294 overload |= optionptr[OPT_DATA];
295
296 }
297 optionptr += len;
298 }
299
300
301 log3("option 0x%02x not found", code);
302 return NULL;
303}
304
305uint8_t* FAST_FUNC udhcp_get_option32(struct dhcp_packet *packet, int code)
306{
307 uint8_t *r = udhcp_get_option(packet, code);
308 if (r) {
309 if (r[-OPT_DATA + OPT_LEN] != 4)
310 r = NULL;
311 }
312 return r;
313}
314
315
316int FAST_FUNC udhcp_end_option(uint8_t *optionptr)
317{
318 int i = 0;
319
320 while (optionptr[i] != DHCP_END) {
321 if (optionptr[i] != DHCP_PADDING)
322 i += optionptr[i + OPT_LEN] + OPT_DATA-1;
323 i++;
324 }
325 return i;
326}
327
328
329
330
331void FAST_FUNC udhcp_add_binary_option(struct dhcp_packet *packet, uint8_t *addopt)
332{
333 unsigned len;
334 uint8_t *optionptr = packet->options;
335 unsigned end = udhcp_end_option(optionptr);
336
337 len = OPT_DATA + addopt[OPT_LEN];
338
339 if (end + len + 1 >= DHCP_OPTIONS_BUFSIZE) {
340
341 bb_error_msg("option 0x%02x did not fit into the packet",
342 addopt[OPT_CODE]);
343 return;
344 }
345 log_option("adding option", addopt);
346 memcpy(optionptr + end, addopt, len);
347 optionptr[end + len] = DHCP_END;
348}
349
350#if ENABLE_UDHCPC || ENABLE_UDHCPD
351
352void FAST_FUNC udhcp_add_simple_option(struct dhcp_packet *packet, uint8_t code, uint32_t data)
353{
354 const struct dhcp_optflag *dh;
355
356 for (dh = dhcp_optflags; dh->code; dh++) {
357 if (dh->code == code) {
358 uint8_t option[6], len;
359
360 option[OPT_CODE] = code;
361 len = dhcp_option_lengths[dh->flags & OPTION_TYPE_MASK];
362 option[OPT_LEN] = len;
363 if (BB_BIG_ENDIAN)
364 data <<= 8 * (4 - len);
365
366 move_to_unaligned32(&option[OPT_DATA], data);
367 udhcp_add_binary_option(packet, option);
368 return;
369 }
370 }
371
372 bb_error_msg("can't add option 0x%02x", code);
373}
374#endif
375
376
377struct option_set* FAST_FUNC udhcp_find_option(struct option_set *opt_list, uint8_t code)
378{
379 while (opt_list && opt_list->data[OPT_CODE] < code)
380 opt_list = opt_list->next;
381
382 if (opt_list && opt_list->data[OPT_CODE] == code)
383 return opt_list;
384 return NULL;
385}
386
387
388int FAST_FUNC udhcp_str2nip(const char *str, void *arg)
389{
390 len_and_sockaddr *lsa;
391
392 lsa = host_and_af2sockaddr(str, 0, AF_INET);
393 if (!lsa)
394 return 0;
395
396 move_to_unaligned32((uint32_t*)arg, lsa->u.sin.sin_addr.s_addr);
397 free(lsa);
398 return 1;
399}
400
401
402
403
404
405
406
407#if !ENABLE_UDHCPC6
408#define attach_option(opt_list, optflag, buffer, length, dhcpv6) \
409 attach_option(opt_list, optflag, buffer, length)
410#endif
411static NOINLINE void attach_option(
412 struct option_set **opt_list,
413 const struct dhcp_optflag *optflag,
414 char *buffer,
415 int length,
416 bool dhcpv6)
417{
418 IF_NOT_UDHCPC6(bool dhcpv6 = 0;)
419 struct option_set *existing;
420 char *allocated = NULL;
421
422 if ((optflag->flags & OPTION_TYPE_MASK) == OPTION_BIN) {
423 const char *end;
424 allocated = xstrdup(buffer);
425 end = hex2bin(allocated, buffer, 255);
426 if (errno)
427 bb_error_msg_and_die("malformed hex string '%s'", buffer);
428 length = end - allocated;
429 buffer = allocated;
430 }
431#if ENABLE_FEATURE_UDHCP_RFC3397
432 if ((optflag->flags & OPTION_TYPE_MASK) == OPTION_DNS_STRING) {
433
434 allocated = buffer = (char *)dname_enc(NULL, 0, buffer, &length);
435 }
436#endif
437
438 existing = udhcp_find_option(*opt_list, optflag->code);
439 if (!existing) {
440 struct option_set *new, **curr;
441
442
443 log2("attaching option %02x to list", optflag->code);
444 new = xmalloc(sizeof(*new));
445 if (!dhcpv6) {
446 new->data = xmalloc(length + OPT_DATA);
447 new->data[OPT_CODE] = optflag->code;
448 new->data[OPT_LEN] = length;
449 memcpy(new->data + OPT_DATA, buffer, length);
450 } else {
451 new->data = xmalloc(length + D6_OPT_DATA);
452 new->data[D6_OPT_CODE] = optflag->code >> 8;
453 new->data[D6_OPT_CODE + 1] = optflag->code & 0xff;
454 new->data[D6_OPT_LEN] = length >> 8;
455 new->data[D6_OPT_LEN + 1] = length & 0xff;
456 memcpy(new->data + D6_OPT_DATA, buffer,
457 length);
458 }
459
460 curr = opt_list;
461 while (*curr && (*curr)->data[OPT_CODE] < optflag->code)
462 curr = &(*curr)->next;
463
464 new->next = *curr;
465 *curr = new;
466 goto ret;
467 }
468
469 if (optflag->flags & OPTION_LIST) {
470 unsigned old_len;
471
472
473 log2("attaching option %02x to existing member of list", optflag->code);
474 old_len = existing->data[OPT_LEN];
475 if (old_len + length < 255) {
476
477
478 existing->data = xrealloc(existing->data, OPT_DATA + 1 + old_len + length);
479
480#if 0
481 if ((optflag->flags & OPTION_TYPE_MASK) == OPTION_STRING
482 || (optflag->flags & OPTION_TYPE_MASK) == OPTION_STRING_HOST
483 ) {
484
485 existing->data[OPT_DATA + old_len] = ' ';
486 old_len++;
487 }
488#endif
489
490 memcpy(existing->data + OPT_DATA + old_len, buffer, length);
491 existing->data[OPT_LEN] = old_len + length;
492 }
493 }
494
495 ret:
496 free(allocated);
497}
498
499int FAST_FUNC udhcp_str2optset(const char *const_str, void *arg,
500 const struct dhcp_optflag *optflags, const char *option_strings,
501 bool dhcpv6)
502{
503 struct option_set **opt_list = arg;
504 char *opt;
505 char *str;
506 const struct dhcp_optflag *optflag;
507 struct dhcp_optflag userdef_optflag;
508 unsigned optcode;
509 int retval;
510
511 char buffer[9] ALIGNED(4);
512 uint16_t *result_u16 = (uint16_t *) buffer;
513 uint32_t *result_u32 = (uint32_t *) buffer;
514
515
516 str = (char *) const_str;
517 opt = strtok(str, " \t=:");
518 if (!opt)
519 return 0;
520
521 optcode = bb_strtou(opt, NULL, 0);
522 if (!errno && optcode < 255) {
523
524
525
526
527 userdef_optflag.flags = OPTION_BIN;
528 userdef_optflag.code = optcode;
529 optflag = &userdef_optflag;
530 } else {
531 optflag = &optflags[udhcp_option_idx(opt, option_strings)];
532 }
533
534
535 retval = 0;
536 do {
537 int length;
538 char *val;
539
540 if (optflag->flags == OPTION_BIN) {
541 val = strtok(NULL, "");
542 trim(val);
543 } else
544 val = strtok(NULL, ", \t");
545 if (!val)
546 break;
547
548 length = dhcp_option_lengths[optflag->flags & OPTION_TYPE_MASK];
549 retval = 0;
550 opt = buffer;
551
552 switch (optflag->flags & OPTION_TYPE_MASK) {
553 case OPTION_IP:
554 retval = udhcp_str2nip(val, buffer);
555 break;
556 case OPTION_IP_PAIR:
557 retval = udhcp_str2nip(val, buffer);
558 val = strtok(NULL, ", \t/-");
559 if (!val)
560 retval = 0;
561 if (retval)
562 retval = udhcp_str2nip(val, buffer + 4);
563 break;
564 case_OPTION_STRING:
565 case OPTION_STRING:
566 case OPTION_STRING_HOST:
567#if ENABLE_FEATURE_UDHCP_RFC3397
568 case OPTION_DNS_STRING:
569#endif
570 length = strnlen(val, 254);
571 if (length > 0) {
572 opt = val;
573 retval = 1;
574 }
575 break;
576
577
578
579
580
581
582 case OPTION_U8:
583 buffer[0] = bb_strtou32(val, NULL, 0);
584 retval = (errno == 0);
585 break;
586
587
588
589 case OPTION_U16: {
590 uint32_t tmp = bb_strtou32(val, NULL, 0);
591 *result_u16 = htons(tmp);
592 retval = (errno == 0 );
593 break;
594 }
595
596
597
598
599
600
601 case OPTION_U32: {
602 uint32_t tmp = bb_strtou32(val, NULL, 0);
603 *result_u32 = htonl(tmp);
604 retval = (errno == 0);
605 break;
606 }
607 case OPTION_S32: {
608 int32_t tmp = bb_strtoi32(val, NULL, 0);
609 *result_u32 = htonl(tmp);
610 retval = (errno == 0);
611 break;
612 }
613 case OPTION_STATIC_ROUTES: {
614
615
616 unsigned mask;
617 char *slash = strchr(val, '/');
618 if (slash) {
619 *slash = '\0';
620 retval = udhcp_str2nip(val, buffer + 1);
621 buffer[0] = mask = bb_strtou(slash + 1, NULL, 10);
622 val = strtok(NULL, ", \t/-");
623 if (!val || mask > 32 || errno)
624 retval = 0;
625 if (retval) {
626 length = ((mask + 7) >> 3) + 5;
627 retval = udhcp_str2nip(val, buffer + (length - 4));
628 }
629 }
630 break;
631 }
632 case OPTION_BIN:
633
634 if (val[0] == '"' || val[0] == '\'') {
635 char delim = val[0];
636 char *end = last_char_is(val + 1, delim);
637 if (end) {
638 *end = '\0';
639 val++;
640 userdef_optflag.flags = OPTION_STRING;
641 goto case_OPTION_STRING;
642 }
643 }
644
645 opt = val;
646 retval = 1;
647 break;
648 default:
649 break;
650 }
651
652 if (retval)
653 attach_option(opt_list, optflag, opt, length, dhcpv6);
654 } while (retval && (optflag->flags & OPTION_LIST));
655
656 return retval;
657}
658
659
660int FAST_FUNC sprint_nip6(char *dest, const uint8_t *ip)
661{
662 char hexstrbuf[16 * 2];
663 bin2hex(hexstrbuf, (void*)ip, 16);
664 return sprintf(dest,
665 "%.4s:%.4s:%.4s:%.4s:%.4s:%.4s:%.4s:%.4s",
666
667 hexstrbuf + 0 * 4,
668 hexstrbuf + 1 * 4,
669 hexstrbuf + 2 * 4,
670 hexstrbuf + 3 * 4,
671 hexstrbuf + 4 * 4,
672 hexstrbuf + 5 * 4,
673 hexstrbuf + 6 * 4,
674 hexstrbuf + 7 * 4
675 );
676}
677