Changeset 2725 in MondoRescue for branches/2.2.9/mindi-busybox/networking/udhcp/common.c
- Timestamp:
- Feb 25, 2011, 9:26:54 PM (13 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/2.2.9/mindi-busybox/networking/udhcp/common.c
r1765 r2725 1 1 /* vi: set sw=4 ts=4: */ 2 /* common.c 2 /* 3 * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001 3 4 * 4 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.5 * Licensed under GPLv2, see file LICENSE in this source tree. 5 6 */ 6 7 7 #include "common.h" 8 9 #if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1 10 unsigned dhcp_verbose; 11 #endif 8 12 9 13 const uint8_t MAC_BCAST_ADDR[6] ALIGN2 = { 10 14 0xff, 0xff, 0xff, 0xff, 0xff, 0xff 11 15 }; 16 17 /* Supported options are easily added here. 18 * See RFC2132 for more options. 19 * OPTION_REQ: these options are requested by udhcpc (unless -o). 20 */ 21 const struct dhcp_optflag dhcp_optflags[] = { 22 /* flags code */ 23 { OPTION_IP | OPTION_REQ, 0x01 }, /* DHCP_SUBNET */ 24 { OPTION_S32 , 0x02 }, /* DHCP_TIME_OFFSET */ 25 { OPTION_IP | OPTION_LIST | OPTION_REQ, 0x03 }, /* DHCP_ROUTER */ 26 // { OPTION_IP | OPTION_LIST , 0x04 }, /* DHCP_TIME_SERVER */ 27 // { OPTION_IP | OPTION_LIST , 0x05 }, /* DHCP_NAME_SERVER */ 28 { OPTION_IP | OPTION_LIST | OPTION_REQ, 0x06 }, /* DHCP_DNS_SERVER */ 29 // { OPTION_IP | OPTION_LIST , 0x07 }, /* DHCP_LOG_SERVER */ 30 // { OPTION_IP | OPTION_LIST , 0x08 }, /* DHCP_COOKIE_SERVER */ 31 { OPTION_IP | OPTION_LIST , 0x09 }, /* DHCP_LPR_SERVER */ 32 { OPTION_STRING | OPTION_REQ, 0x0c }, /* DHCP_HOST_NAME */ 33 { OPTION_U16 , 0x0d }, /* DHCP_BOOT_SIZE */ 34 { OPTION_STRING | OPTION_REQ, 0x0f }, /* DHCP_DOMAIN_NAME */ 35 { OPTION_IP , 0x10 }, /* DHCP_SWAP_SERVER */ 36 { OPTION_STRING , 0x11 }, /* DHCP_ROOT_PATH */ 37 { OPTION_U8 , 0x17 }, /* DHCP_IP_TTL */ 38 { OPTION_U16 , 0x1a }, /* DHCP_MTU */ 39 { OPTION_IP | OPTION_REQ, 0x1c }, /* DHCP_BROADCAST */ 40 { OPTION_IP_PAIR | OPTION_LIST , 0x21 }, /* DHCP_ROUTES */ 41 { OPTION_STRING , 0x28 }, /* DHCP_NIS_DOMAIN */ 42 { OPTION_IP | OPTION_LIST , 0x29 }, /* DHCP_NIS_SERVER */ 43 { OPTION_IP | OPTION_LIST | OPTION_REQ, 0x2a }, /* DHCP_NTP_SERVER */ 44 { OPTION_IP | OPTION_LIST , 0x2c }, /* DHCP_WINS_SERVER */ 45 { OPTION_U32 , 0x33 }, /* DHCP_LEASE_TIME */ 46 { OPTION_IP , 0x36 }, /* DHCP_SERVER_ID */ 47 { OPTION_STRING , 0x38 }, /* DHCP_ERR_MESSAGE */ 48 //TODO: must be combined with 'sname' and 'file' handling: 49 { OPTION_STRING , 0x42 }, /* DHCP_TFTP_SERVER_NAME */ 50 { OPTION_STRING , 0x43 }, /* DHCP_BOOT_FILE */ 51 //TODO: not a string, but a set of LASCII strings: 52 // { OPTION_STRING , 0x4D }, /* DHCP_USER_CLASS */ 53 #if ENABLE_FEATURE_UDHCP_RFC3397 54 { OPTION_DNS_STRING | OPTION_LIST , 0x77 }, /* DHCP_DOMAIN_SEARCH */ 55 { OPTION_SIP_SERVERS , 0x78 }, /* DHCP_SIP_SERVERS */ 56 #endif 57 { OPTION_STATIC_ROUTES , 0x79 }, /* DHCP_STATIC_ROUTES */ 58 { OPTION_STATIC_ROUTES , 0xf9 }, /* DHCP_MS_STATIC_ROUTES */ 59 { OPTION_STRING , 0xfc }, /* DHCP_WPAD */ 60 61 /* Options below have no match in dhcp_option_strings[], 62 * are not passed to dhcpc scripts, and cannot be specified 63 * with "option XXX YYY" syntax in dhcpd config file. 64 * These entries are only used internally by udhcp[cd] 65 * to correctly encode options into packets. 66 */ 67 68 { OPTION_IP , 0x32 }, /* DHCP_REQUESTED_IP */ 69 { OPTION_U8 , 0x35 }, /* DHCP_MESSAGE_TYPE */ 70 { OPTION_U16 , 0x39 }, /* DHCP_MAX_SIZE */ 71 //looks like these opts will work just fine even without these defs: 72 // { OPTION_STRING , 0x3c }, /* DHCP_VENDOR */ 73 // /* not really a string: */ 74 // { OPTION_STRING , 0x3d }, /* DHCP_CLIENT_ID */ 75 { 0, 0 } /* zeroed terminating entry */ 76 }; 77 78 /* Used for converting options from incoming packets to env variables 79 * for udhcpc stript, and for setting options for udhcpd via 80 * "opt OPTION_NAME OPTION_VALUE" directives in udhcpd.conf file. 81 */ 82 /* Must match dhcp_optflags[] order */ 83 const char dhcp_option_strings[] ALIGN1 = 84 "subnet" "\0" /* DHCP_SUBNET */ 85 "timezone" "\0" /* DHCP_TIME_OFFSET */ 86 "router" "\0" /* DHCP_ROUTER */ 87 // "timesrv" "\0" /* DHCP_TIME_SERVER */ 88 // "namesrv" "\0" /* DHCP_NAME_SERVER */ 89 "dns" "\0" /* DHCP_DNS_SERVER */ 90 // "logsrv" "\0" /* DHCP_LOG_SERVER */ 91 // "cookiesrv" "\0" /* DHCP_COOKIE_SERVER */ 92 "lprsrv" "\0" /* DHCP_LPR_SERVER */ 93 "hostname" "\0" /* DHCP_HOST_NAME */ 94 "bootsize" "\0" /* DHCP_BOOT_SIZE */ 95 "domain" "\0" /* DHCP_DOMAIN_NAME */ 96 "swapsrv" "\0" /* DHCP_SWAP_SERVER */ 97 "rootpath" "\0" /* DHCP_ROOT_PATH */ 98 "ipttl" "\0" /* DHCP_IP_TTL */ 99 "mtu" "\0" /* DHCP_MTU */ 100 "broadcast" "\0" /* DHCP_BROADCAST */ 101 "routes" "\0" /* DHCP_ROUTES */ 102 "nisdomain" "\0" /* DHCP_NIS_DOMAIN */ 103 "nissrv" "\0" /* DHCP_NIS_SERVER */ 104 "ntpsrv" "\0" /* DHCP_NTP_SERVER */ 105 "wins" "\0" /* DHCP_WINS_SERVER */ 106 "lease" "\0" /* DHCP_LEASE_TIME */ 107 "serverid" "\0" /* DHCP_SERVER_ID */ 108 "message" "\0" /* DHCP_ERR_MESSAGE */ 109 "tftp" "\0" /* DHCP_TFTP_SERVER_NAME */ 110 "bootfile" "\0" /* DHCP_BOOT_FILE */ 111 // "userclass" "\0" /* DHCP_USER_CLASS */ 112 #if ENABLE_FEATURE_UDHCP_RFC3397 113 "search" "\0" /* DHCP_DOMAIN_SEARCH */ 114 // doesn't work in udhcpd.conf since OPTION_SIP_SERVERS 115 // is not handled yet by "string->option" conversion code: 116 "sipsrv" "\0" /* DHCP_SIP_SERVERS */ 117 #endif 118 // doesn't work in udhcpd.conf since OPTION_STATIC_ROUTES 119 // is not handled yet by "string->option" conversion code: 120 "staticroutes" "\0"/* DHCP_STATIC_ROUTES */ 121 "msstaticroutes""\0"/* DHCP_MS_STATIC_ROUTES */ 122 "wpad" "\0" /* DHCP_WPAD */ 123 ; 124 125 /* Lengths of the option types in binary form. 126 * Used by: 127 * udhcp_str2optset: to determine how many bytes to allocate. 128 * xmalloc_optname_optval: to estimate string length 129 * from binary option length: (option[LEN] / dhcp_option_lengths[opt_type]) 130 * is the number of elements, multiply in by one element's string width 131 * (len_of_option_as_string[opt_type]) and you know how wide string you need. 132 */ 133 const uint8_t dhcp_option_lengths[] ALIGN1 = { 134 [OPTION_IP] = 4, 135 [OPTION_IP_PAIR] = 8, 136 // [OPTION_BOOLEAN] = 1, 137 [OPTION_STRING] = 1, /* ignored by udhcp_str2optset */ 138 #if ENABLE_FEATURE_UDHCP_RFC3397 139 [OPTION_DNS_STRING] = 1, /* ignored by both udhcp_str2optset and xmalloc_optname_optval */ 140 [OPTION_SIP_SERVERS] = 1, 141 #endif 142 [OPTION_U8] = 1, 143 [OPTION_U16] = 2, 144 // [OPTION_S16] = 2, 145 [OPTION_U32] = 4, 146 [OPTION_S32] = 4, 147 /* Just like OPTION_STRING, we use minimum length here */ 148 [OPTION_STATIC_ROUTES] = 5, 149 }; 150 151 152 #if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 2 153 static void log_option(const char *pfx, const uint8_t *opt) 154 { 155 if (dhcp_verbose >= 2) { 156 char buf[256 * 2 + 2]; 157 *bin2hex(buf, (void*) (opt + OPT_DATA), opt[OPT_LEN]) = '\0'; 158 bb_info_msg("%s: 0x%02x %s", pfx, opt[OPT_CODE], buf); 159 } 160 } 161 #else 162 # define log_option(pfx, opt) ((void)0) 163 #endif 164 165 unsigned FAST_FUNC udhcp_option_idx(const char *name) 166 { 167 int n = index_in_strings(dhcp_option_strings, name); 168 if (n >= 0) 169 return n; 170 171 { 172 char buf[sizeof(dhcp_option_strings)]; 173 char *d = buf; 174 const char *s = dhcp_option_strings; 175 while (s < dhcp_option_strings + sizeof(dhcp_option_strings) - 2) { 176 *d++ = (*s == '\0' ? ' ' : *s); 177 s++; 178 } 179 *d = '\0'; 180 bb_error_msg_and_die("unknown option '%s', known options: %s", name, buf); 181 } 182 } 183 184 /* Get an option with bounds checking (warning, result is not aligned) */ 185 uint8_t* FAST_FUNC udhcp_get_option(struct dhcp_packet *packet, int code) 186 { 187 uint8_t *optionptr; 188 int len; 189 int rem; 190 int overload = 0; 191 enum { 192 FILE_FIELD101 = FILE_FIELD * 0x101, 193 SNAME_FIELD101 = SNAME_FIELD * 0x101, 194 }; 195 196 /* option bytes: [code][len][data1][data2]..[dataLEN] */ 197 optionptr = packet->options; 198 rem = sizeof(packet->options); 199 while (1) { 200 if (rem <= 0) { 201 bb_error_msg("bad packet, malformed option field"); 202 return NULL; 203 } 204 if (optionptr[OPT_CODE] == DHCP_PADDING) { 205 rem--; 206 optionptr++; 207 continue; 208 } 209 if (optionptr[OPT_CODE] == DHCP_END) { 210 if ((overload & FILE_FIELD101) == FILE_FIELD) { 211 /* can use packet->file, and didn't look at it yet */ 212 overload |= FILE_FIELD101; /* "we looked at it" */ 213 optionptr = packet->file; 214 rem = sizeof(packet->file); 215 continue; 216 } 217 if ((overload & SNAME_FIELD101) == SNAME_FIELD) { 218 /* can use packet->sname, and didn't look at it yet */ 219 overload |= SNAME_FIELD101; /* "we looked at it" */ 220 optionptr = packet->sname; 221 rem = sizeof(packet->sname); 222 continue; 223 } 224 break; 225 } 226 len = 2 + optionptr[OPT_LEN]; 227 rem -= len; 228 if (rem < 0) 229 continue; /* complain and return NULL */ 230 231 if (optionptr[OPT_CODE] == code) { 232 log_option("Option found", optionptr); 233 return optionptr + OPT_DATA; 234 } 235 236 if (optionptr[OPT_CODE] == DHCP_OPTION_OVERLOAD) { 237 overload |= optionptr[OPT_DATA]; 238 /* fall through */ 239 } 240 optionptr += len; 241 } 242 243 /* log3 because udhcpc uses it a lot - very noisy */ 244 log3("Option 0x%02x not found", code); 245 return NULL; 246 } 247 248 /* Return the position of the 'end' option (no bounds checking) */ 249 int FAST_FUNC udhcp_end_option(uint8_t *optionptr) 250 { 251 int i = 0; 252 253 while (optionptr[i] != DHCP_END) { 254 if (optionptr[i] != DHCP_PADDING) 255 i += optionptr[i + OPT_LEN] + OPT_DATA-1; 256 i++; 257 } 258 return i; 259 } 260 261 /* Add an option (supplied in binary form) to the options. 262 * Option format: [code][len][data1][data2]..[dataLEN] 263 */ 264 void FAST_FUNC udhcp_add_binary_option(struct dhcp_packet *packet, uint8_t *addopt) 265 { 266 unsigned len; 267 uint8_t *optionptr = packet->options; 268 unsigned end = udhcp_end_option(optionptr); 269 270 len = OPT_DATA + addopt[OPT_LEN]; 271 /* end position + (option code/length + addopt length) + end option */ 272 if (end + len + 1 >= DHCP_OPTIONS_BUFSIZE) { 273 //TODO: learn how to use overflow option if we exhaust packet->options[] 274 bb_error_msg("option 0x%02x did not fit into the packet", 275 addopt[OPT_CODE]); 276 return; 277 } 278 log_option("Adding option", addopt); 279 memcpy(optionptr + end, addopt, len); 280 optionptr[end + len] = DHCP_END; 281 } 282 283 /* Add an one to four byte option to a packet */ 284 void FAST_FUNC udhcp_add_simple_option(struct dhcp_packet *packet, uint8_t code, uint32_t data) 285 { 286 const struct dhcp_optflag *dh; 287 288 for (dh = dhcp_optflags; dh->code; dh++) { 289 if (dh->code == code) { 290 uint8_t option[6], len; 291 292 option[OPT_CODE] = code; 293 len = dhcp_option_lengths[dh->flags & OPTION_TYPE_MASK]; 294 option[OPT_LEN] = len; 295 if (BB_BIG_ENDIAN) 296 data <<= 8 * (4 - len); 297 /* Assignment is unaligned! */ 298 move_to_unaligned32(&option[OPT_DATA], data); 299 udhcp_add_binary_option(packet, option); 300 return; 301 } 302 } 303 304 bb_error_msg("can't add option 0x%02x", code); 305 } 306 307 /* Find option 'code' in opt_list */ 308 struct option_set* FAST_FUNC udhcp_find_option(struct option_set *opt_list, uint8_t code) 309 { 310 while (opt_list && opt_list->data[OPT_CODE] < code) 311 opt_list = opt_list->next; 312 313 if (opt_list && opt_list->data[OPT_CODE] == code) 314 return opt_list; 315 return NULL; 316 } 317 318 /* Parse string to IP in network order */ 319 int FAST_FUNC udhcp_str2nip(const char *str, void *arg) 320 { 321 len_and_sockaddr *lsa; 322 323 lsa = host_and_af2sockaddr(str, 0, AF_INET); 324 if (!lsa) 325 return 0; 326 *(uint32_t*)arg = lsa->u.sin.sin_addr.s_addr; 327 free(lsa); 328 return 1; 329 } 330 331 /* udhcp_str2optset: 332 * Parse string option representation to binary form and add it to opt_list. 333 * Called to parse "udhcpc -x OPTNAME:OPTVAL" 334 * and to parse udhcpd.conf's "opt OPTNAME OPTVAL" directives. 335 */ 336 /* helper for the helper */ 337 static char *allocate_tempopt_if_needed( 338 const struct dhcp_optflag *optflag, 339 char *buffer, 340 int *length_p) 341 { 342 char *allocated = NULL; 343 if ((optflag->flags & OPTION_TYPE_MASK) == OPTION_BIN) { 344 const char *end; 345 allocated = xstrdup(buffer); /* more than enough */ 346 end = hex2bin(allocated, buffer, 255); 347 if (errno) 348 bb_error_msg_and_die("malformed hex string '%s'", buffer); 349 *length_p = end - allocated; 350 } 351 return allocated; 352 } 353 /* helper: add an option to the opt_list */ 354 static NOINLINE void attach_option( 355 struct option_set **opt_list, 356 const struct dhcp_optflag *optflag, 357 char *buffer, 358 int length) 359 { 360 struct option_set *existing, *new, **curr; 361 char *allocated = NULL; 362 363 existing = udhcp_find_option(*opt_list, optflag->code); 364 if (!existing) { 365 log2("Attaching option %02x to list", optflag->code); 366 allocated = allocate_tempopt_if_needed(optflag, buffer, &length); 367 #if ENABLE_FEATURE_UDHCP_RFC3397 368 if ((optflag->flags & OPTION_TYPE_MASK) == OPTION_DNS_STRING) { 369 /* reuse buffer and length for RFC1035-formatted string */ 370 allocated = buffer = (char *)dname_enc(NULL, 0, buffer, &length); 371 } 372 #endif 373 /* make a new option */ 374 new = xmalloc(sizeof(*new)); 375 new->data = xmalloc(length + OPT_DATA); 376 new->data[OPT_CODE] = optflag->code; 377 new->data[OPT_LEN] = length; 378 memcpy(new->data + OPT_DATA, (allocated ? allocated : buffer), length); 379 380 curr = opt_list; 381 while (*curr && (*curr)->data[OPT_CODE] < optflag->code) 382 curr = &(*curr)->next; 383 384 new->next = *curr; 385 *curr = new; 386 goto ret; 387 } 388 389 if (optflag->flags & OPTION_LIST) { 390 unsigned old_len; 391 392 /* add it to an existing option */ 393 log2("Attaching option %02x to existing member of list", optflag->code); 394 allocated = allocate_tempopt_if_needed(optflag, buffer, &length); 395 old_len = existing->data[OPT_LEN]; 396 #if ENABLE_FEATURE_UDHCP_RFC3397 397 if ((optflag->flags & OPTION_TYPE_MASK) == OPTION_DNS_STRING) { 398 /* reuse buffer and length for RFC1035-formatted string */ 399 allocated = buffer = (char *)dname_enc(existing->data + OPT_DATA, old_len, buffer, &length); 400 } 401 #endif 402 if (old_len + length < 255) { 403 /* actually 255 is ok too, but adding a space can overlow it */ 404 405 existing->data = xrealloc(existing->data, OPT_DATA + 1 + old_len + length); 406 if ((optflag->flags & OPTION_TYPE_MASK) == OPTION_STRING) { 407 /* add space separator between STRING options in a list */ 408 existing->data[OPT_DATA + old_len] = ' '; 409 old_len++; 410 } 411 memcpy(existing->data + OPT_DATA + old_len, buffer, length); 412 existing->data[OPT_LEN] = old_len + length; 413 } /* else, ignore the data, we could put this in a second option in the future */ 414 } /* else, ignore the new data */ 415 416 ret: 417 free(allocated); 418 } 419 420 int FAST_FUNC udhcp_str2optset(const char *const_str, void *arg) 421 { 422 struct option_set **opt_list = arg; 423 char *opt, *val, *endptr; 424 char *str; 425 const struct dhcp_optflag *optflag; 426 struct dhcp_optflag bin_optflag; 427 unsigned optcode; 428 int retval, length; 429 char buffer[8] ALIGNED(4); 430 uint16_t *result_u16 = (uint16_t *) buffer; 431 uint32_t *result_u32 = (uint32_t *) buffer; 432 433 /* Cheat, the only *const* str possible is "" */ 434 str = (char *) const_str; 435 opt = strtok(str, " \t="); 436 if (!opt) 437 return 0; 438 439 optcode = bb_strtou(opt, NULL, 0); 440 if (!errno && optcode < 255) { 441 /* Raw (numeric) option code */ 442 bin_optflag.flags = OPTION_BIN; 443 bin_optflag.code = optcode; 444 optflag = &bin_optflag; 445 } else { 446 optflag = &dhcp_optflags[udhcp_option_idx(opt)]; 447 } 448 449 retval = 0; 450 do { 451 val = strtok(NULL, ", \t"); 452 if (!val) 453 break; 454 length = dhcp_option_lengths[optflag->flags & OPTION_TYPE_MASK]; 455 retval = 0; 456 opt = buffer; /* new meaning for variable opt */ 457 switch (optflag->flags & OPTION_TYPE_MASK) { 458 case OPTION_IP: 459 retval = udhcp_str2nip(val, buffer); 460 break; 461 case OPTION_IP_PAIR: 462 retval = udhcp_str2nip(val, buffer); 463 val = strtok(NULL, ", \t/-"); 464 if (!val) 465 retval = 0; 466 if (retval) 467 retval = udhcp_str2nip(val, buffer + 4); 468 break; 469 case OPTION_STRING: 470 #if ENABLE_FEATURE_UDHCP_RFC3397 471 case OPTION_DNS_STRING: 472 #endif 473 length = strnlen(val, 254); 474 if (length > 0) { 475 opt = val; 476 retval = 1; 477 } 478 break; 479 // case OPTION_BOOLEAN: { 480 // static const char no_yes[] ALIGN1 = "no\0yes\0"; 481 // buffer[0] = retval = index_in_strings(no_yes, val); 482 // retval++; /* 0 - bad; 1: "no" 2: "yes" */ 483 // break; 484 // } 485 case OPTION_U8: 486 buffer[0] = strtoul(val, &endptr, 0); 487 retval = (endptr[0] == '\0'); 488 break; 489 /* htonX are macros in older libc's, using temp var 490 * in code below for safety */ 491 /* TODO: use bb_strtoX? */ 492 case OPTION_U16: { 493 unsigned long tmp = strtoul(val, &endptr, 0); 494 *result_u16 = htons(tmp); 495 retval = (endptr[0] == '\0' /*&& tmp < 0x10000*/); 496 break; 497 } 498 // case OPTION_S16: { 499 // long tmp = strtol(val, &endptr, 0); 500 // *result_u16 = htons(tmp); 501 // retval = (endptr[0] == '\0'); 502 // break; 503 // } 504 case OPTION_U32: { 505 unsigned long tmp = strtoul(val, &endptr, 0); 506 *result_u32 = htonl(tmp); 507 retval = (endptr[0] == '\0'); 508 break; 509 } 510 case OPTION_S32: { 511 long tmp = strtol(val, &endptr, 0); 512 *result_u32 = htonl(tmp); 513 retval = (endptr[0] == '\0'); 514 break; 515 } 516 case OPTION_BIN: /* handled in attach_option() */ 517 opt = val; 518 retval = 1; 519 default: 520 break; 521 } 522 if (retval) 523 attach_option(opt_list, optflag, opt, length); 524 } while (retval && optflag->flags & OPTION_LIST); 525 526 return retval; 527 }
Note:
See TracChangeset
for help on using the changeset viewer.