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_STRING , 0xd1 },
66 { OPTION_STRING , 0xd2 },
67 { OPTION_6RD , 0xd4 },
68 { OPTION_STATIC_ROUTES | OPTION_LIST , 0xf9 },
69 { OPTION_STRING , 0xfc },
70
71
72
73
74
75
76
77
78 { OPTION_IP , 0x32 },
79 { OPTION_U8 , 0x35 },
80 { OPTION_U16 , 0x39 },
81
82
83
84
85 { 0, 0 }
86};
87
88
89
90
91
92
93const char dhcp_option_strings[] ALIGN1 =
94 "subnet" "\0"
95 "timezone" "\0"
96 "router" "\0"
97
98
99 "dns" "\0"
100
101
102 "lprsrv" "\0"
103 "hostname" "\0"
104 "bootsize" "\0"
105 "domain" "\0"
106 "swapsrv" "\0"
107 "rootpath" "\0"
108 "ipttl" "\0"
109 "mtu" "\0"
110 "broadcast" "\0"
111 "routes" "\0"
112 "nisdomain" "\0"
113 "nissrv" "\0"
114 "ntpsrv" "\0"
115 "wins" "\0"
116 "lease" "\0"
117 "serverid" "\0"
118 "message" "\0"
119 "tftp" "\0"
120 "bootfile" "\0"
121
122#if ENABLE_FEATURE_UDHCP_RFC3397
123 "search" "\0"
124
125
126 "sipsrv" "\0"
127#endif
128 "staticroutes" "\0"
129#if ENABLE_FEATURE_UDHCP_8021Q
130 "vlanid" "\0"
131 "vlanpriority" "\0"
132#endif
133 "pxeconffile" "\0"
134 "pxepathprefix" "\0"
135 "ip6rd" "\0"
136 "msstaticroutes""\0"
137 "wpad" "\0"
138 ;
139
140
141
142
143
144
145
146
147
148const uint8_t dhcp_option_lengths[] ALIGN1 = {
149 [OPTION_IP] = 4,
150 [OPTION_IP_PAIR] = 8,
151
152 [OPTION_STRING] = 1,
153 [OPTION_STRING_HOST] = 1,
154#if ENABLE_FEATURE_UDHCP_RFC3397
155 [OPTION_DNS_STRING] = 1,
156 [OPTION_SIP_SERVERS] = 1,
157#endif
158 [OPTION_U8] = 1,
159 [OPTION_U16] = 2,
160
161 [OPTION_U32] = 4,
162 [OPTION_S32] = 4,
163
164 [OPTION_STATIC_ROUTES] = 5,
165 [OPTION_6RD] = 22,
166};
167
168
169#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 2
170static void log_option(const char *pfx, const uint8_t *opt)
171{
172 if (dhcp_verbose >= 2) {
173 char buf[256 * 2 + 2];
174 *bin2hex(buf, (void*) (opt + OPT_DATA), opt[OPT_LEN]) = '\0';
175 bb_info_msg("%s: 0x%02x %s", pfx, opt[OPT_CODE], buf);
176 }
177}
178#else
179# define log_option(pfx, opt) ((void)0)
180#endif
181
182unsigned FAST_FUNC udhcp_option_idx(const char *name)
183{
184 int n = index_in_strings(dhcp_option_strings, name);
185 if (n >= 0)
186 return n;
187
188 {
189 char buf[sizeof(dhcp_option_strings)];
190 char *d = buf;
191 const char *s = dhcp_option_strings;
192 while (s < dhcp_option_strings + sizeof(dhcp_option_strings) - 2) {
193 *d++ = (*s == '\0' ? ' ' : *s);
194 s++;
195 }
196 *d = '\0';
197 bb_error_msg_and_die("unknown option '%s', known options: %s", name, buf);
198 }
199}
200
201
202uint8_t* FAST_FUNC udhcp_get_option(struct dhcp_packet *packet, int code)
203{
204 uint8_t *optionptr;
205 int len;
206 int rem;
207 int overload = 0;
208 enum {
209 FILE_FIELD101 = FILE_FIELD * 0x101,
210 SNAME_FIELD101 = SNAME_FIELD * 0x101,
211 };
212
213
214 optionptr = packet->options;
215 rem = sizeof(packet->options);
216 while (1) {
217 if (rem <= 0) {
218 bb_error_msg("bad packet, malformed option field");
219 return NULL;
220 }
221 if (optionptr[OPT_CODE] == DHCP_PADDING) {
222 rem--;
223 optionptr++;
224 continue;
225 }
226 if (optionptr[OPT_CODE] == DHCP_END) {
227 if ((overload & FILE_FIELD101) == FILE_FIELD) {
228
229 overload |= FILE_FIELD101;
230 optionptr = packet->file;
231 rem = sizeof(packet->file);
232 continue;
233 }
234 if ((overload & SNAME_FIELD101) == SNAME_FIELD) {
235
236 overload |= SNAME_FIELD101;
237 optionptr = packet->sname;
238 rem = sizeof(packet->sname);
239 continue;
240 }
241 break;
242 }
243 len = 2 + optionptr[OPT_LEN];
244 rem -= len;
245 if (rem < 0)
246 continue;
247
248 if (optionptr[OPT_CODE] == code) {
249 log_option("Option found", optionptr);
250 return optionptr + OPT_DATA;
251 }
252
253 if (optionptr[OPT_CODE] == DHCP_OPTION_OVERLOAD) {
254 overload |= optionptr[OPT_DATA];
255
256 }
257 optionptr += len;
258 }
259
260
261 log3("Option 0x%02x not found", code);
262 return NULL;
263}
264
265
266int FAST_FUNC udhcp_end_option(uint8_t *optionptr)
267{
268 int i = 0;
269
270 while (optionptr[i] != DHCP_END) {
271 if (optionptr[i] != DHCP_PADDING)
272 i += optionptr[i + OPT_LEN] + OPT_DATA-1;
273 i++;
274 }
275 return i;
276}
277
278
279
280
281void FAST_FUNC udhcp_add_binary_option(struct dhcp_packet *packet, uint8_t *addopt)
282{
283 unsigned len;
284 uint8_t *optionptr = packet->options;
285 unsigned end = udhcp_end_option(optionptr);
286
287 len = OPT_DATA + addopt[OPT_LEN];
288
289 if (end + len + 1 >= DHCP_OPTIONS_BUFSIZE) {
290
291 bb_error_msg("option 0x%02x did not fit into the packet",
292 addopt[OPT_CODE]);
293 return;
294 }
295 log_option("Adding option", addopt);
296 memcpy(optionptr + end, addopt, len);
297 optionptr[end + len] = DHCP_END;
298}
299
300
301void FAST_FUNC udhcp_add_simple_option(struct dhcp_packet *packet, uint8_t code, uint32_t data)
302{
303 const struct dhcp_optflag *dh;
304
305 for (dh = dhcp_optflags; dh->code; dh++) {
306 if (dh->code == code) {
307 uint8_t option[6], len;
308
309 option[OPT_CODE] = code;
310 len = dhcp_option_lengths[dh->flags & OPTION_TYPE_MASK];
311 option[OPT_LEN] = len;
312 if (BB_BIG_ENDIAN)
313 data <<= 8 * (4 - len);
314
315 move_to_unaligned32(&option[OPT_DATA], data);
316 udhcp_add_binary_option(packet, option);
317 return;
318 }
319 }
320
321 bb_error_msg("can't add option 0x%02x", code);
322}
323
324
325struct option_set* FAST_FUNC udhcp_find_option(struct option_set *opt_list, uint8_t code)
326{
327 while (opt_list && opt_list->data[OPT_CODE] < code)
328 opt_list = opt_list->next;
329
330 if (opt_list && opt_list->data[OPT_CODE] == code)
331 return opt_list;
332 return NULL;
333}
334
335
336int FAST_FUNC udhcp_str2nip(const char *str, void *arg)
337{
338 len_and_sockaddr *lsa;
339
340 lsa = host_and_af2sockaddr(str, 0, AF_INET);
341 if (!lsa)
342 return 0;
343
344 move_to_unaligned32((uint32_t*)arg, lsa->u.sin.sin_addr.s_addr);
345 free(lsa);
346 return 1;
347}
348
349
350
351
352
353
354
355static char *allocate_tempopt_if_needed(
356 const struct dhcp_optflag *optflag,
357 char *buffer,
358 int *length_p)
359{
360 char *allocated = NULL;
361 if ((optflag->flags & OPTION_TYPE_MASK) == OPTION_BIN) {
362 const char *end;
363 allocated = xstrdup(buffer);
364 end = hex2bin(allocated, buffer, 255);
365 if (errno)
366 bb_error_msg_and_die("malformed hex string '%s'", buffer);
367 *length_p = end - allocated;
368 }
369 return allocated;
370}
371
372static NOINLINE void attach_option(
373 struct option_set **opt_list,
374 const struct dhcp_optflag *optflag,
375 char *buffer,
376 int length)
377{
378 struct option_set *existing;
379 char *allocated;
380
381 allocated = allocate_tempopt_if_needed(optflag, buffer, &length);
382#if ENABLE_FEATURE_UDHCP_RFC3397
383 if ((optflag->flags & OPTION_TYPE_MASK) == OPTION_DNS_STRING) {
384
385 allocated = buffer = (char *)dname_enc(NULL, 0, buffer, &length);
386 }
387#endif
388
389 existing = udhcp_find_option(*opt_list, optflag->code);
390 if (!existing) {
391 struct option_set *new, **curr;
392
393
394 log2("Attaching option %02x to list", optflag->code);
395 new = xmalloc(sizeof(*new));
396 new->data = xmalloc(length + OPT_DATA);
397 new->data[OPT_CODE] = optflag->code;
398 new->data[OPT_LEN] = length;
399 memcpy(new->data + OPT_DATA, (allocated ? allocated : buffer), length);
400
401 curr = opt_list;
402 while (*curr && (*curr)->data[OPT_CODE] < optflag->code)
403 curr = &(*curr)->next;
404
405 new->next = *curr;
406 *curr = new;
407 goto ret;
408 }
409
410 if (optflag->flags & OPTION_LIST) {
411 unsigned old_len;
412
413
414 log2("Attaching option %02x to existing member of list", optflag->code);
415 old_len = existing->data[OPT_LEN];
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, (allocated ? allocated : 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