1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * This file contains routines that are shared between the DHCP server 29 * implementation and BOOTP server compatibility. 30 */ 31 32 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <unistd.h> 36 #include <assert.h> 37 #include <sys/types.h> 38 #include <stdarg.h> 39 #include <errno.h> 40 #include <alloca.h> 41 #include <sys/socket.h> 42 #include <sys/sockio.h> 43 #include <net/if.h> 44 #include <sys/syslog.h> 45 #include <string.h> 46 #include <netinet/in.h> 47 #include <arpa/inet.h> 48 #include <netdb.h> 49 #include <netinet/dhcp.h> 50 #include <search.h> 51 #include <dhcp_symbol.h> 52 #include "dhcpd.h" 53 #include "per_dnet.h" 54 #include "interfaces.h" 55 #include <locale.h> 56 #include <resolv.h> 57 58 /* 59 * Get the client id. Sets cid and len. 60 */ 61 void 62 get_clnt_id(PKT_LIST *plp, uchar_t *cid, int cidlen, uchar_t *len) 63 { 64 DHCP_OPT *optp = plp->opts[CD_CLIENT_ID]; 65 66 /* 67 * If the DHCP client specified the client id option, use that, 68 * otherwise use the client's hardware type and hardware address. 69 */ 70 if (plp->opts[CD_DHCP_TYPE] != NULL && optp != NULL) { 71 /* DHCP client w/ client id */ 72 if (cidlen < optp->len) 73 *len = (uchar_t)cidlen; 74 else 75 *len = optp->len; 76 (void) memcpy(cid, optp->value, *len); 77 } else { 78 /* BOOTP client or DHCP client w/o client id. */ 79 *cid++ = plp->pkt->htype; 80 *len = plp->pkt->hlen + 1; 81 if (cidlen < *len) 82 *len = cidlen; 83 (void) memcpy(cid, plp->pkt->chaddr, *len); 84 } 85 } 86 87 /* 88 * Return a string representing an ASCII version of the client_id. 89 */ 90 char * 91 disp_cid(PKT_LIST *plp, char *bufp, int len) 92 { 93 DHCP_OPT *optp = plp->opts[CD_CLIENT_ID]; 94 uchar_t *cp; 95 uchar_t cplen; 96 uint_t tlen; 97 98 if (optp != (DHCP_OPT *)0) { 99 cp = optp->value; 100 cplen = optp->len; 101 } else { 102 cp = plp->pkt->chaddr; 103 cplen = plp->pkt->hlen; 104 } 105 106 tlen = len; 107 (void) octet_to_hexascii(cp, cplen, bufp, &tlen); 108 return (bufp); 109 } 110 111 /* 112 * Based on the contents of the PKT_LIST structure for an incoming 113 * packet, determine the net address and subnet mask identifying the 114 * dhcp-network database. This centralizes choices that were formerly 115 * made in the specific protocol routines. 116 */ 117 void 118 determine_network(IF *ifp, PKT_LIST *plp, struct in_addr *netp, 119 struct in_addr *subp) 120 { 121 /* 122 * For BOOTP, REQUEST, RELEASE, and INFORM packets, trust client's 123 * notion of IP address if ciaddr is set. Use it to figure out correct 124 * dhcp-network. 125 */ 126 netp->s_addr = plp->pkt->ciaddr.s_addr; 127 if (netp->s_addr != htonl(INADDR_ANY) && 128 (plp->opts[CD_DHCP_TYPE] == NULL || 129 (*plp->opts[CD_DHCP_TYPE]->value == REQUEST || 130 *plp->opts[CD_DHCP_TYPE]->value == RELEASE || 131 *plp->opts[CD_DHCP_TYPE]->value == INFORM))) { 132 /* 133 * Calculate client's default net mask, consult netmasks 134 * database to see if net is further subnetted. Use resulting 135 * subnet mask with client's address to produce dhcp-network 136 * database name. 137 */ 138 get_netmask(netp, subp); 139 } else 140 netp->s_addr = htonl(INADDR_ANY); 141 142 /* 143 * If no trusted IP address, examine giaddr. 144 */ 145 if (netp->s_addr == htonl(INADDR_ANY)) { 146 if (plp->pkt->giaddr.s_addr != htonl(INADDR_ANY)) { 147 netp->s_addr = plp->pkt->giaddr.s_addr; 148 /* 149 * Packet received thru a relay agent. Calculate the 150 * net's address using subnet mask and giaddr. 151 */ 152 get_netmask(netp, subp); 153 } else { 154 /* Locally connected net. */ 155 netp->s_addr = ifp->addr.s_addr; 156 subp->s_addr = ifp->mask.s_addr; 157 } 158 } 159 } 160 161 struct netmask_node; 162 163 typedef struct netmask_node { 164 struct in_addr net; /* cached network */ 165 struct in_addr mask; /* cached netmask */ 166 } NNODE; 167 168 static void *nroot; /* root of netmask tree */ 169 static time_t nroot_mtime; /* time for dynamic free */ 170 static time_t nroot_stamp; /* time for dynamic free */ 171 static rwlock_t nroot_rwlock; /* synchronization variable */ 172 173 /* 174 * nm_cmp() - determine whether key n1 is within range of net/mask n2 175 */ 176 static int 177 nm_cmp(const void *n1, const void *n2) 178 { 179 void *v1 = (void *) (((NNODE *)n1)->net.s_addr & 180 ((NNODE *)n2)->mask.s_addr); 181 void *v2 = (void *) ((NNODE *)n2)->net.s_addr; 182 183 return (memcmp(&v1, &v2, sizeof (struct in_addr))); 184 } 185 186 /* 187 * Given a network-order address, calculate client's default net mask. 188 * Consult local cache, then netmasks database to see if net is further 189 * subnetted. We'll only snag the first netmask that matches our criteria. 190 */ 191 void 192 get_netmask(struct in_addr *n_addrp, struct in_addr *s_addrp) 193 { 194 NNODE key; 195 NNODE *node; 196 NNODE **ret; 197 struct in_addr haddr; 198 199 assert(n_addrp != NULL && s_addrp != NULL); 200 201 /* 202 * First check locally maintained, incomplete cache. 203 */ 204 (void) rw_rdlock(&nroot_rwlock); 205 if (nroot != NULL) { 206 /* Delete expired tree. */ 207 if (nroot_mtime != reinit_time || nroot_stamp < time(NULL)) { 208 (void) rw_unlock(&nroot_rwlock); 209 (void) rw_wrlock(&nroot_rwlock); 210 while ((ret = (NNODE **)nroot) != NULL) { 211 node = *ret; 212 (void) tdelete(node, &nroot, nm_cmp); 213 free(node); 214 } 215 nroot_mtime = reinit_time; 216 nroot_stamp = time(NULL) + DHCP_NSS_TIME; 217 } else { 218 key.net.s_addr = ntohl(n_addrp->s_addr); 219 key.mask.s_addr = INADDR_ANY; 220 if ((ret = (NNODE **)tfind((void *)&key, 221 (void * const *)&nroot, nm_cmp)) != NULL) { 222 s_addrp->s_addr = htonl((*ret)->mask.s_addr); 223 (void) rw_unlock(&nroot_rwlock); 224 return; 225 } 226 } 227 } 228 229 /* 230 * Note: workaround for 4336124: single-thread access to 231 * nss search routines to avoid getting incorrect results. 232 */ 233 node = (NNODE *)smalloc(sizeof (NNODE)); 234 235 /* Convert to and from host order. */ 236 haddr.s_addr = ntohl(n_addrp->s_addr); 237 get_netmask4(&haddr, s_addrp); 238 node->mask.s_addr = s_addrp->s_addr; 239 node->net.s_addr = haddr.s_addr & node->mask.s_addr; 240 s_addrp->s_addr = htonl(s_addrp->s_addr); 241 242 /* While inserting check that another insert has not occurred. */ 243 ret = (NNODE **)tsearch((void *)node, &nroot, nm_cmp); 244 if (ret != NULL && *ret != node) 245 free(node); 246 247 (void) rw_unlock(&nroot_rwlock); 248 } 249 250 /* 251 * This function is charged with loading the options field with the 252 * configured and/or asked for options. Note that if the packet is too 253 * small to fit the options, then option overload is enabled. 254 * 255 * Note that the caller is expected to free any allocated ENCODE lists, 256 * with the exception of locally-allocated lists in the case where ecp is 257 * NULL, but vecp is not. In this case, the resultant ecp list (ecp == tvep) 258 * is freed locally. 259 * 260 * Returns: The actual size of the utilized packet buffer. 261 */ 262 263 int 264 load_options(int flags, PKT_LIST *c_plp, PKT *r_pktp, int replen, uchar_t *optp, 265 ENCODE *ecp, ENCODE *vecp) 266 { 267 ENCODE *ep, *prevep, *tvep = NULL; 268 ENCODE *router_ecp = NULL; 269 PKT *c_pktp = c_plp->pkt; 270 uchar_t cat; 271 ushort_t code; 272 uint_t vend_len; 273 uchar_t len, *vp, *vdata, *data, *endp, *main_optp, *opt_endp; 274 uchar_t overload = DHCP_OVRLD_CLR; 275 uchar_t using_overload = DHCP_OVRLD_CLR; 276 boolean_t srv_using_file = B_FALSE, clnt_ovrld_file = B_FALSE; 277 boolean_t echo_clnt_file; 278 279 if (c_plp->opts[CD_OPTION_OVERLOAD] != NULL && 280 *c_plp->opts[CD_OPTION_OVERLOAD]->value & DHCP_OVRLD_FILE) 281 clnt_ovrld_file = B_TRUE; 282 283 opt_endp = (uchar_t *)((uint_t)r_pktp->options + replen - 284 BASE_PKT_SIZE); 285 endp = opt_endp; 286 287 /* 288 * We handle vendor options by fabricating an ENCODE of type 289 * CD_VENDOR_SPEC, and setting its datafield equal to vecp. 290 * 291 * We assume we've been handed the proper class list. 292 */ 293 if (vecp != NULL && (flags & DHCP_NON_RFC1048) == 0) { 294 vend_len = 0; 295 for (ep = vecp, vend_len = 0; ep != NULL; ep = ep->next) 296 vend_len += (ep->len + 2); 297 298 if (vend_len != 0) { 299 if (vend_len > (uint_t)0xff) { 300 dhcpmsg(LOG_WARNING, 301 "Warning: Too much vendor data (> 255) to " 302 "encapsulate within option %d.\n", 303 CD_VENDOR_SPEC); 304 vend_len = (uint_t)0xff; 305 } 306 vdata = (uchar_t *)smalloc(vend_len); 307 308 for (vp = vdata, tvep = vecp; tvep != NULL && 309 (uchar_t *)(vp + tvep->len + 2) <= &vdata[vend_len]; 310 tvep = tvep->next) { 311 *vp++ = tvep->code; 312 *vp++ = tvep->len; 313 (void) memcpy(vp, tvep->data, tvep->len); 314 vp += tvep->len; 315 } 316 317 /* this make_encode *doesn't* copy data */ 318 tvep = make_encode(DSYM_VENDOR, CD_VENDOR_SPEC, 319 vend_len, vdata, ENC_DONT_COPY); 320 321 /* Tack it on the end of standard list. */ 322 for (ep = prevep = ecp; ep != NULL; ep = ep->next) 323 prevep = ep; 324 if (prevep != NULL) 325 prevep->next = tvep; 326 else 327 ecp = tvep; 328 } 329 } 330 331 /* 332 * Scan the options first to determine if we could potentially 333 * option overload. 334 */ 335 if (flags & DHCP_DHCP_CLNT) { 336 for (ep = ecp; ep != NULL; ep = ep->next) { 337 if (ep->category == DSYM_FIELD) 338 switch (ep->code) { 339 case CD_SNAME: 340 overload |= DHCP_OVRLD_SNAME; 341 break; 342 case CD_BOOTFILE: 343 overload |= DHCP_OVRLD_FILE; 344 srv_using_file = B_TRUE; 345 break; 346 } 347 } 348 } else { 349 /* BOOTP uses these fields for fixed parameters, no overload */ 350 overload = DHCP_OVRLD_ALL; 351 } 352 353 if (c_pktp->file[0] != '\0' && !clnt_ovrld_file && !srv_using_file) { 354 /* 355 * simply echo back client's boot file, and don't overload. 356 * if CD_BOOTPATH is set, we'll simply rewrite the r_pktp 357 * file field to include it along with the client's requested 358 * name during the load pass through the internal options. 359 * Here we let the overload code know we're not to overload 360 * the file field. 361 */ 362 (void) memcpy(r_pktp->file, c_pktp->file, 363 sizeof (r_pktp->file)); 364 overload |= DHCP_OVRLD_FILE; 365 echo_clnt_file = B_TRUE; 366 } else 367 echo_clnt_file = B_FALSE; 368 369 /* Now actually load the options! */ 370 for (ep = ecp; ep != NULL; ep = ep->next) { 371 cat = ep->category; 372 code = ep->code; 373 len = ep->len; 374 data = ep->data; 375 376 /* 377 * non rfc1048 clients can only get packet fields and 378 * the CD_BOOTPATH internal pseudo opt, which only potentially 379 * affects the file field. 380 */ 381 if ((flags & DHCP_NON_RFC1048) && 382 !(cat == DSYM_FIELD || (cat == DSYM_INTERNAL && 383 code == CD_BOOTPATH))) { 384 continue; 385 } 386 387 if ((flags & DHCP_SEND_LEASE) == 0 && 388 cat == DSYM_STANDARD && 389 (code == CD_T1_TIME || code == CD_T2_TIME || 390 code == CD_LEASE_TIME)) { 391 continue; 392 } 393 394 /* standard and site options */ 395 if (cat == DSYM_STANDARD || cat == DSYM_SITE || 396 cat == DSYM_VENDOR) { 397 398 uchar_t *need_optp; 399 400 /* 401 * This horrible kludge is necessary because the DHCP 402 * options RFCs require that the subnet option MUST 403 * precede the router option. To accomplish this, we 404 * 405 * inspect each of the standard options, waiting 406 * for CD_ROUTER to turn up (if it never does, 407 * no special handling is needed) 408 * 409 * search the remaining options for CD_SUBNETMASK 410 * If it occurs, we 411 * set router_ecp to indicate where to find 412 * the router option's values that we have 413 * not yet emitted 414 * 415 * reinitialize code, len, and data to emit 416 * the CD_SUBNETMASK option now 417 * 418 * when CD_SUBNETMASK is encountered, we 419 * reinitialize code, len, and data to emit 420 * the CD_ROUTER option 421 */ 422 if ((cat == DSYM_STANDARD) && (code == CD_ROUTER)) { 423 ENCODE *tp; 424 425 for (tp = ep->next; tp != NULL; tp = tp->next) 426 if ((tp->category == DSYM_STANDARD) && 427 (tp->code == CD_SUBNETMASK)) { 428 router_ecp = ep; 429 code = CD_SUBNETMASK; 430 len = tp->len; 431 data = tp->data; 432 } 433 } else if ((cat == DSYM_STANDARD) && 434 (code == CD_SUBNETMASK) && (router_ecp != NULL)) { 435 code = CD_ROUTER; 436 len = router_ecp->len; 437 data = router_ecp->data; 438 } 439 440 /* 441 * Keep an eye on option field. Option overload. Note 442 * that we need to keep track of the space necessary 443 * to place the Overload option in the options section 444 * (that's the 3 octets below.) The 2 octets cover the 445 * necessary code and len portion of the payload. 446 */ 447 if (using_overload == DHCP_OVRLD_CLR) { 448 /* 2 for code/len, 3 for overload option */ 449 need_optp = &optp[len + 2 + 3]; 450 } else { 451 /* Just need 2 for code/len */ 452 need_optp = &optp[len + 2]; 453 } 454 if (need_optp > endp) { 455 /* 456 * If overload is not possible, we will 457 * keep going, hoping to find an option 458 * that will fit in the remaining space, 459 * rather than just give up. 460 */ 461 if (overload != DHCP_OVRLD_ALL) { 462 if (using_overload == DHCP_OVRLD_CLR) { 463 *optp++ = CD_OPTION_OVERLOAD; 464 *optp++ = 1; 465 main_optp = optp; 466 } else { 467 if (optp < endp) 468 *optp = CD_END; 469 overload |= using_overload; 470 } 471 } 472 switch (overload) { 473 case DHCP_OVRLD_CLR: 474 /* great, can use both */ 475 /* FALLTHRU */ 476 case DHCP_OVRLD_FILE: 477 /* Can use sname. */ 478 optp = r_pktp->sname; 479 endp = r_pktp->file; 480 using_overload |= DHCP_OVRLD_SNAME; 481 break; 482 case DHCP_OVRLD_SNAME: 483 /* Using sname, can use file. */ 484 optp = r_pktp->file; 485 endp = r_pktp->cookie; 486 using_overload |= DHCP_OVRLD_FILE; 487 break; 488 } 489 } 490 /* Skip the option if it's too long to fit */ 491 if (len < (endp - optp - 1)) { 492 /* Load options. */ 493 *optp++ = (uchar_t)code; 494 *optp++ = len; 495 (void) memcpy(optp, data, len); 496 optp += len; 497 } 498 } else if (cat == DSYM_FIELD) { 499 /* packet field pseudo options */ 500 switch (code) { 501 case CD_SIADDR: 502 /* 503 * Configuration includes Boot server addr 504 */ 505 (void) memcpy((void *)&r_pktp->siaddr, data, 506 len); 507 break; 508 case CD_SNAME: 509 /* 510 * Configuration includes Boot server name 511 */ 512 (void) memcpy(r_pktp->sname, data, len); 513 break; 514 case CD_BOOTFILE: 515 /* 516 * Configuration includes boot file. 517 * Always authoritative. 518 */ 519 (void) memset(r_pktp->file, 0, 520 sizeof (r_pktp->file)); 521 (void) memcpy(r_pktp->file, data, len); 522 break; 523 default: 524 dhcpmsg(LOG_ERR, 525 "Unsettable DHCP packet field: %d\n", code); 526 break; 527 } 528 } else if (cat == DSYM_INTERNAL) { 529 /* Internal server pseudo options */ 530 switch (code) { 531 case CD_BOOTPATH: 532 /* 533 * Prefix for boot file. Only used if 534 * client provides bootfile and server doesn't 535 * specify one. Prepended on client's bootfile 536 * value. Otherwise ignored. 537 */ 538 if (echo_clnt_file) { 539 uchar_t alen, flen; 540 541 alen = sizeof (c_pktp->file); 542 flen = alen - 1; 543 if (c_pktp->file[flen] != '\0') 544 flen++; 545 else 546 flen = strlen( 547 (char *)c_pktp->file); 548 549 if ((len + flen + 1) > alen) { 550 char *bp = alloca(alen + 1); 551 char *bf = alloca(alen + 1); 552 (void) memcpy(bp, data, len); 553 bp[len] = '\0'; 554 (void) memcpy(bf, c_pktp->file, 555 flen); 556 bf[flen] = '\0'; 557 dhcpmsg(LOG_ERR, 558 "BootPath(%1$s) + " 559 "BootFile(%2$s) too " 560 "long: %3$d > %4$d\n", 561 bp, bf, (len + flen), alen); 562 } else { 563 (void) memcpy(r_pktp->file, 564 data, len); 565 r_pktp->file[len] = '/'; 566 (void) memcpy( 567 &r_pktp->file[len + 1], 568 c_pktp->file, flen); 569 } 570 } 571 break; 572 case CD_BOOL_HOSTNAME: 573 /* FALLTHRU */ 574 case CD_BOOL_LEASENEG: 575 /* FALLTHRU */ 576 case CD_BOOL_ECHO_VCLASS: 577 /* 578 * These pseudo opts have had their 579 * affect elsewhere, such as dhcp.c. 580 */ 581 break; 582 default: 583 dhcpmsg(LOG_ERR, 584 "Unknown Internal pseudo opt: %d\n", code); 585 break; 586 } 587 } else { 588 dhcpmsg(LOG_ERR, 589 "Unrecognized option with code: %d %d\n", cat, 590 code); 591 } 592 } 593 594 if (using_overload != DHCP_OVRLD_CLR) { 595 *main_optp++ = using_overload; 596 if (optp < endp) 597 *optp = CD_END; 598 } else 599 main_optp = optp; /* no overload */ 600 601 if (main_optp < opt_endp) 602 *main_optp++ = CD_END; 603 604 if (ecp == tvep) 605 free_encode_list(ecp); 606 607 return (BASE_PKT_SIZE + (uint_t)(main_optp - r_pktp->options)); 608 } 609 610 /* 611 * Reinitialize the dhcptab database, as a result of timeout or 612 * user signal. Note: if_head_mtx cannot be held by caller. 613 */ 614 void * 615 reinitialize(void *arg) 616 { 617 int totpkts; 618 IF *ifp; 619 thread_t *tp = (thread_t *)arg; 620 int err; 621 622 /* 623 * Got a signal to reinitialize 624 */ 625 626 if (verbose) 627 dhcpmsg(LOG_INFO, "Reinitializing server\n"); 628 629 if (!no_dhcptab) { 630 if (checktab() != 0) { 631 dhcpmsg(LOG_WARNING, 632 "WARNING: Cannot access dhcptab.\n"); 633 } else { 634 if ((err = readtab(PRESERVE_DHCPTAB)) != 0) { 635 dhcpmsg(LOG_ERR, 636 "Error reading dhcptab.\n"); 637 return ((void *)err); 638 } 639 } 640 } 641 642 /* 643 * Drop all pending offers, display interface statistics. 644 */ 645 if (verbose) { 646 (void) mutex_lock(&if_head_mtx); 647 for (ifp = if_head, totpkts = 0; ifp != NULL; ifp = ifp->next) { 648 (void) mutex_lock(&ifp->ifp_mtx); 649 disp_if_stats(ifp); 650 totpkts += ifp->received; 651 (void) mutex_unlock(&ifp->ifp_mtx); 652 } 653 (void) mutex_unlock(&if_head_mtx); 654 655 dhcpmsg(LOG_INFO, 656 "Total Packets received on all interfaces: %d\n", totpkts); 657 dhcpmsg(LOG_INFO, "Server reinitialized.\n"); 658 } 659 660 /* Default domain may have changed */ 661 if (res_ninit(&resolv_conf) == -1) 662 dhcpmsg(LOG_ERR, "Cannot acquire resolver configuration.\n"); 663 664 /* Release reinitialization thread */ 665 reinit_time = time(NULL); 666 *tp = NULL; 667 thr_exit(NULL); 668 669 return (NULL); 670 }