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