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 * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. 23 */ 24 25 #include <unistd.h> 26 #include <sys/types.h> 27 #include <sys/stat.h> 28 #include <stdlib.h> 29 #include <netinet/in.h> /* struct in_addr */ 30 #include <netinet/dhcp.h> 31 #include <signal.h> 32 #include <sys/socket.h> 33 #include <net/route.h> 34 #include <net/if_arp.h> 35 #include <string.h> 36 #include <dhcpmsg.h> 37 #include <ctype.h> 38 #include <netdb.h> 39 #include <fcntl.h> 40 #include <stdio.h> 41 #include <dhcp_hostconf.h> 42 43 #include "states.h" 44 #include "agent.h" 45 #include "interface.h" 46 #include "util.h" 47 #include "packet.h" 48 49 /* 50 * this file contains utility functions that have no real better home 51 * of their own. they can largely be broken into six categories: 52 * 53 * o conversion functions -- functions to turn integers into strings, 54 * or to convert between units of a similar measure. 55 * 56 * o time and timer functions -- functions to handle time measurement 57 * and events. 58 * 59 * o ipc-related functions -- functions to simplify the generation of 60 * ipc messages to the agent's clients. 61 * 62 * o signal-related functions -- functions to clean up the agent when 63 * it receives a signal. 64 * 65 * o routing table manipulation functions 66 * 67 * o true miscellany -- anything else 68 */ 69 70 /* 71 * pkt_type_to_string(): stringifies a packet type 72 * 73 * input: uchar_t: a DHCP packet type value, RFC 2131 or 3315 74 * boolean_t: B_TRUE if IPv6 75 * output: const char *: the stringified packet type 76 */ 77 78 const char * 79 pkt_type_to_string(uchar_t type, boolean_t isv6) 80 { 81 /* 82 * note: the ordering in these arrays allows direct indexing of the 83 * table based on the RFC packet type value passed in. 84 */ 85 86 static const char *v4types[] = { 87 "BOOTP", "DISCOVER", "OFFER", "REQUEST", "DECLINE", 88 "ACK", "NAK", "RELEASE", "INFORM" 89 }; 90 static const char *v6types[] = { 91 NULL, "SOLICIT", "ADVERTISE", "REQUEST", 92 "CONFIRM", "RENEW", "REBIND", "REPLY", 93 "RELEASE", "DECLINE", "RECONFIGURE", "INFORMATION-REQUEST", 94 "RELAY-FORW", "RELAY-REPL" 95 }; 96 97 if (isv6) { 98 if (type >= sizeof (v6types) / sizeof (*v6types) || 99 v6types[type] == NULL) 100 return ("<unknown>"); 101 else 102 return (v6types[type]); 103 } else { 104 if (type >= sizeof (v4types) / sizeof (*v4types) || 105 v4types[type] == NULL) 106 return ("<unknown>"); 107 else 108 return (v4types[type]); 109 } 110 } 111 112 /* 113 * monosec_to_string(): converts a monosec_t into a date string 114 * 115 * input: monosec_t: the monosec_t to convert 116 * output: const char *: the corresponding date string 117 */ 118 119 const char * 120 monosec_to_string(monosec_t monosec) 121 { 122 time_t time = monosec_to_time(monosec); 123 char *time_string = ctime(&time); 124 125 /* strip off the newline -- ugh, why, why, why.. */ 126 time_string[strlen(time_string) - 1] = '\0'; 127 return (time_string); 128 } 129 130 /* 131 * monosec(): returns a monotonically increasing time in seconds that 132 * is not affected by stime(2) or adjtime(2). 133 * 134 * input: void 135 * output: monosec_t: the number of seconds since some time in the past 136 */ 137 138 monosec_t 139 monosec(void) 140 { 141 return (gethrtime() / NANOSEC); 142 } 143 144 /* 145 * monosec_to_time(): converts a monosec_t into real wall time 146 * 147 * input: monosec_t: the absolute monosec_t to convert 148 * output: time_t: the absolute time that monosec_t represents in wall time 149 */ 150 151 time_t 152 monosec_to_time(monosec_t abs_monosec) 153 { 154 return (abs_monosec - monosec()) + time(NULL); 155 } 156 157 /* 158 * hrtime_to_monosec(): converts a hrtime_t to monosec_t 159 * 160 * input: hrtime_t: the time to convert 161 * output: monosec_t: the time in monosec_t 162 */ 163 164 monosec_t 165 hrtime_to_monosec(hrtime_t hrtime) 166 { 167 return (hrtime / NANOSEC); 168 } 169 170 /* 171 * print_server_msg(): prints a message from a DHCP server 172 * 173 * input: dhcp_smach_t *: the state machine the message is associated with 174 * const char *: the string to display 175 * uint_t: length of string 176 * output: void 177 */ 178 179 void 180 print_server_msg(dhcp_smach_t *dsmp, const char *msg, uint_t msglen) 181 { 182 if (msglen > 0) { 183 dhcpmsg(MSG_INFO, "%s: message from server: %.*s", 184 dsmp->dsm_name, msglen, msg); 185 } 186 } 187 188 /* 189 * alrm_exit(): Signal handler for SIGARLM. terminates grandparent. 190 * 191 * input: int: signal the handler was called with. 192 * 193 * output: void 194 */ 195 196 static void 197 alrm_exit(int sig) 198 { 199 int exitval; 200 201 if (sig == SIGALRM && grandparent != 0) 202 exitval = EXIT_SUCCESS; 203 else 204 exitval = EXIT_FAILURE; 205 206 _exit(exitval); 207 } 208 209 /* 210 * daemonize(): daemonizes the process 211 * 212 * input: void 213 * output: int: 1 on success, 0 on failure 214 */ 215 216 int 217 daemonize(void) 218 { 219 /* 220 * We've found that adoption takes sufficiently long that 221 * a dhcpinfo run after dhcpagent -a is started may occur 222 * before the agent is ready to process the request. 223 * The result is an error message and an unhappy user. 224 * 225 * The initial process now sleeps for DHCP_ADOPT_SLEEP, 226 * unless interrupted by a SIGALRM, in which case it 227 * exits immediately. This has the effect that the 228 * grandparent doesn't exit until the dhcpagent is ready 229 * to process requests. This defers the the balance of 230 * the system start-up script processing until the 231 * dhcpagent is ready to field requests. 232 * 233 * grandparent is only set for the adopt case; other 234 * cases do not require the wait. 235 */ 236 237 if (grandparent != 0) 238 (void) signal(SIGALRM, alrm_exit); 239 240 switch (fork()) { 241 242 case -1: 243 return (0); 244 245 case 0: 246 if (grandparent != 0) 247 (void) signal(SIGALRM, SIG_DFL); 248 249 /* 250 * setsid() makes us lose our controlling terminal, 251 * and become both a session leader and a process 252 * group leader. 253 */ 254 255 (void) setsid(); 256 257 /* 258 * under POSIX, a session leader can accidentally 259 * (through open(2)) acquire a controlling terminal if 260 * it does not have one. just to be safe, fork again 261 * so we are not a session leader. 262 */ 263 264 switch (fork()) { 265 266 case -1: 267 return (0); 268 269 case 0: 270 (void) signal(SIGHUP, SIG_IGN); 271 (void) chdir("/"); 272 (void) umask(022); 273 closefrom(0); 274 break; 275 276 default: 277 _exit(EXIT_SUCCESS); 278 } 279 break; 280 281 default: 282 if (grandparent != 0) { 283 (void) signal(SIGCHLD, SIG_IGN); 284 /* 285 * Note that we're not the agent here, so the DHCP 286 * logging subsystem hasn't been configured yet. 287 */ 288 syslog(LOG_DEBUG | LOG_DAEMON, "dhcpagent: daemonize: " 289 "waiting for adoption to complete."); 290 if (sleep(DHCP_ADOPT_SLEEP) == 0) { 291 syslog(LOG_WARNING | LOG_DAEMON, 292 "dhcpagent: daemonize: timed out awaiting " 293 "adoption."); 294 } 295 syslog(LOG_DEBUG | LOG_DAEMON, "dhcpagent: daemonize: " 296 "wait finished"); 297 } 298 _exit(EXIT_SUCCESS); 299 } 300 301 return (1); 302 } 303 304 /* 305 * update_default_route(): update the interface's default route 306 * 307 * input: int: the type of message; either RTM_ADD or RTM_DELETE 308 * struct in_addr: the default gateway to use 309 * const char *: the interface associated with the route 310 * int: any additional flags (besides RTF_STATIC and RTF_GATEWAY) 311 * output: boolean_t: B_TRUE on success, B_FALSE on failure 312 */ 313 314 static boolean_t 315 update_default_route(uint32_t ifindex, int type, struct in_addr *gateway_nbo, 316 int flags) 317 { 318 struct { 319 struct rt_msghdr rm_mh; 320 struct sockaddr_in rm_dst; 321 struct sockaddr_in rm_gw; 322 struct sockaddr_in rm_mask; 323 struct sockaddr_dl rm_ifp; 324 } rtmsg; 325 326 (void) memset(&rtmsg, 0, sizeof (rtmsg)); 327 rtmsg.rm_mh.rtm_version = RTM_VERSION; 328 rtmsg.rm_mh.rtm_msglen = sizeof (rtmsg); 329 rtmsg.rm_mh.rtm_type = type; 330 rtmsg.rm_mh.rtm_pid = getpid(); 331 rtmsg.rm_mh.rtm_flags = RTF_GATEWAY | RTF_STATIC | flags; 332 rtmsg.rm_mh.rtm_addrs = RTA_GATEWAY | RTA_DST | RTA_NETMASK | RTA_IFP; 333 334 rtmsg.rm_gw.sin_family = AF_INET; 335 rtmsg.rm_gw.sin_addr = *gateway_nbo; 336 337 rtmsg.rm_dst.sin_family = AF_INET; 338 rtmsg.rm_dst.sin_addr.s_addr = htonl(INADDR_ANY); 339 340 rtmsg.rm_mask.sin_family = AF_INET; 341 rtmsg.rm_mask.sin_addr.s_addr = htonl(0); 342 343 rtmsg.rm_ifp.sdl_family = AF_LINK; 344 rtmsg.rm_ifp.sdl_index = ifindex; 345 346 return (write(rtsock_fd, &rtmsg, sizeof (rtmsg)) == sizeof (rtmsg)); 347 } 348 349 /* 350 * add_default_route(): add the default route to the given gateway 351 * 352 * input: const char *: the name of the interface associated with the route 353 * struct in_addr: the default gateway to add 354 * output: boolean_t: B_TRUE on success, B_FALSE otherwise 355 */ 356 357 boolean_t 358 add_default_route(uint32_t ifindex, struct in_addr *gateway_nbo) 359 { 360 return (update_default_route(ifindex, RTM_ADD, gateway_nbo, RTF_UP)); 361 } 362 363 /* 364 * del_default_route(): deletes the default route to the given gateway 365 * 366 * input: const char *: the name of the interface associated with the route 367 * struct in_addr: if not INADDR_ANY, the default gateway to remove 368 * output: boolean_t: B_TRUE on success, B_FALSE on failure 369 */ 370 371 boolean_t 372 del_default_route(uint32_t ifindex, struct in_addr *gateway_nbo) 373 { 374 if (gateway_nbo->s_addr == htonl(INADDR_ANY)) /* no router */ 375 return (B_TRUE); 376 377 return (update_default_route(ifindex, RTM_DELETE, gateway_nbo, 0)); 378 } 379 380 /* 381 * inactivity_shutdown(): shuts down agent if there are no state machines left 382 * to manage 383 * 384 * input: iu_tq_t *: unused 385 * void *: unused 386 * output: void 387 */ 388 389 /* ARGSUSED */ 390 void 391 inactivity_shutdown(iu_tq_t *tqp, void *arg) 392 { 393 if (smach_count() > 0) /* shouldn't happen, but... */ 394 return; 395 396 dhcpmsg(MSG_VERBOSE, "inactivity_shutdown: timed out"); 397 398 iu_stop_handling_events(eh, DHCP_REASON_INACTIVITY, NULL, NULL); 399 } 400 401 /* 402 * graceful_shutdown(): shuts down the agent gracefully 403 * 404 * input: int: the signal that caused graceful_shutdown to be called 405 * output: void 406 */ 407 408 void 409 graceful_shutdown(int sig) 410 { 411 iu_stop_handling_events(eh, (sig == SIGTERM ? DHCP_REASON_TERMINATE : 412 DHCP_REASON_SIGNAL), drain_script, NULL); 413 } 414 415 /* 416 * bind_sock(): binds a socket to a given IP address and port number 417 * 418 * input: int: the socket to bind 419 * in_port_t: the port number to bind to, host byte order 420 * in_addr_t: the address to bind to, host byte order 421 * output: boolean_t: B_TRUE on success, B_FALSE on failure 422 */ 423 424 boolean_t 425 bind_sock(int fd, in_port_t port_hbo, in_addr_t addr_hbo) 426 { 427 struct sockaddr_in sin; 428 int on = 1; 429 430 (void) memset(&sin, 0, sizeof (struct sockaddr_in)); 431 sin.sin_family = AF_INET; 432 sin.sin_port = htons(port_hbo); 433 sin.sin_addr.s_addr = htonl(addr_hbo); 434 435 (void) setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (int)); 436 437 return (bind(fd, (struct sockaddr *)&sin, sizeof (sin)) == 0); 438 } 439 440 /* 441 * bind_sock_v6(): binds a socket to a given IP address and port number 442 * 443 * input: int: the socket to bind 444 * in_port_t: the port number to bind to, host byte order 445 * in6_addr_t: the address to bind to, network byte order 446 * output: boolean_t: B_TRUE on success, B_FALSE on failure 447 */ 448 449 boolean_t 450 bind_sock_v6(int fd, in_port_t port_hbo, const in6_addr_t *addr_nbo) 451 { 452 struct sockaddr_in6 sin6; 453 int on = 1; 454 455 (void) memset(&sin6, 0, sizeof (struct sockaddr_in6)); 456 sin6.sin6_family = AF_INET6; 457 sin6.sin6_port = htons(port_hbo); 458 if (addr_nbo != NULL) { 459 (void) memcpy(&sin6.sin6_addr, addr_nbo, 460 sizeof (sin6.sin6_addr)); 461 } 462 463 (void) setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (int)); 464 465 return (bind(fd, (struct sockaddr *)&sin6, sizeof (sin6)) == 0); 466 } 467 468 /* 469 * valid_hostname(): check whether a string is a valid hostname 470 * 471 * input: const char *: the string to verify as a hostname 472 * output: boolean_t: B_TRUE if the string is a valid hostname 473 * 474 * Note that we accept both host names beginning with a digit and 475 * those containing hyphens. Neither is strictly legal according 476 * to the RFCs, but both are in common practice, so we endeavour 477 * to not break what customers are using. 478 */ 479 480 static boolean_t 481 valid_hostname(const char *hostname) 482 { 483 unsigned int i; 484 485 for (i = 0; hostname[i] != '\0'; i++) { 486 487 if (isalpha(hostname[i]) || isdigit(hostname[i]) || 488 (((hostname[i] == '-') || (hostname[i] == '.')) && (i > 0))) 489 continue; 490 491 return (B_FALSE); 492 } 493 494 return (i > 0); 495 } 496 497 /* 498 * iffile_to_hostname(): return the hostname contained on a line of the form 499 * 500 * [ ^I]*inet[ ^I]+hostname[\n]*\0 501 * 502 * in the file located at the specified path 503 * 504 * input: const char *: the path of the file to look in for the hostname 505 * output: const char *: the hostname at that path, or NULL on failure 506 */ 507 508 #define IFLINE_MAX 1024 /* maximum length of a hostname.<if> line */ 509 510 const char * 511 iffile_to_hostname(const char *path) 512 { 513 FILE *fp; 514 static char ifline[IFLINE_MAX]; 515 516 fp = fopen(path, "r"); 517 if (fp == NULL) 518 return (NULL); 519 520 /* 521 * /etc/hostname.<if> may contain multiple ifconfig commands, but each 522 * such command is on a separate line (see the "while read ifcmds" code 523 * in /etc/init.d/inetinit). Thus we will read the file a line at a 524 * time, searching for a line of the form 525 * 526 * [ ^I]*inet[ ^I]+hostname[\n]*\0 527 * 528 * extract the host name from it, and check it for validity. 529 */ 530 while (fgets(ifline, sizeof (ifline), fp) != NULL) { 531 char *p; 532 533 if ((p = strstr(ifline, "inet")) != NULL) { 534 if ((p != ifline) && !isspace(p[-1])) { 535 (void) fclose(fp); 536 return (NULL); 537 } 538 p += 4; /* skip over "inet" and expect spaces or tabs */ 539 if ((*p == '\n') || (*p == '\0')) { 540 (void) fclose(fp); 541 return (NULL); 542 } 543 if (isspace(*p)) { 544 char *nlptr; 545 546 /* no need to read more of the file */ 547 (void) fclose(fp); 548 549 while (isspace(*p)) 550 p++; 551 if ((nlptr = strrchr(p, '\n')) != NULL) 552 *nlptr = '\0'; 553 if (strlen(p) > MAXHOSTNAMELEN) { 554 dhcpmsg(MSG_WARNING, 555 "iffile_to_hostname:" 556 " host name too long"); 557 return (NULL); 558 } 559 if (valid_hostname(p)) { 560 return (p); 561 } else { 562 dhcpmsg(MSG_WARNING, 563 "iffile_to_hostname:" 564 " host name not valid"); 565 return (NULL); 566 } 567 } else { 568 (void) fclose(fp); 569 return (NULL); 570 } 571 } 572 } 573 574 (void) fclose(fp); 575 return (NULL); 576 } 577 578 /* 579 * init_timer(): set up a DHCP timer 580 * 581 * input: dhcp_timer_t *: the timer to set up 582 * output: void 583 */ 584 585 void 586 init_timer(dhcp_timer_t *dt, lease_t startval) 587 { 588 dt->dt_id = -1; 589 dt->dt_start = startval; 590 } 591 592 /* 593 * cancel_timer(): cancel a DHCP timer 594 * 595 * input: dhcp_timer_t *: the timer to cancel 596 * output: boolean_t: B_TRUE on success, B_FALSE otherwise 597 */ 598 599 boolean_t 600 cancel_timer(dhcp_timer_t *dt) 601 { 602 if (dt->dt_id == -1) 603 return (B_TRUE); 604 605 if (iu_cancel_timer(tq, dt->dt_id, NULL) == 1) { 606 dt->dt_id = -1; 607 return (B_TRUE); 608 } 609 610 return (B_FALSE); 611 } 612 613 /* 614 * schedule_timer(): schedule a DHCP timer. Note that it must not be already 615 * running, and that we can't cancel here. If it were, and 616 * we did, we'd leak a reference to the callback argument. 617 * 618 * input: dhcp_timer_t *: the timer to schedule 619 * output: boolean_t: B_TRUE on success, B_FALSE otherwise 620 */ 621 622 boolean_t 623 schedule_timer(dhcp_timer_t *dt, iu_tq_callback_t *cbfunc, void *arg) 624 { 625 if (dt->dt_id != -1) 626 return (B_FALSE); 627 dt->dt_id = iu_schedule_timer(tq, dt->dt_start, cbfunc, arg); 628 return (dt->dt_id != -1); 629 } 630 631 /* 632 * dhcpv6_status_code(): report on a DHCPv6 status code found in an option 633 * buffer. 634 * 635 * input: const dhcpv6_option_t *: pointer to option 636 * uint_t: option length 637 * const char **: error string (nul-terminated) 638 * const char **: message from server (unterminated) 639 * uint_t *: length of server message 640 * output: int: -1 on error, or >= 0 for a DHCPv6 status code 641 */ 642 643 int 644 dhcpv6_status_code(const dhcpv6_option_t *d6o, uint_t olen, const char **estr, 645 const char **msg, uint_t *msglenp) 646 { 647 uint16_t status; 648 static const char *v6_status[] = { 649 NULL, 650 "Unknown reason", 651 "Server has no addresses available", 652 "Client record unavailable", 653 "Prefix inappropriate for link", 654 "Client must use multicast", 655 "No prefix available" 656 }; 657 static char sbuf[32]; 658 659 *estr = ""; 660 *msg = ""; 661 *msglenp = 0; 662 if (d6o == NULL) 663 return (0); 664 olen -= sizeof (*d6o); 665 if (olen < 2) { 666 *estr = "garbled status code"; 667 return (-1); 668 } 669 670 *msg = (const char *)(d6o + 1) + 2; 671 *msglenp = olen - 2; 672 673 (void) memcpy(&status, d6o + 1, sizeof (status)); 674 status = ntohs(status); 675 if (status > 0) { 676 if (status > DHCPV6_STAT_NOPREFIX) { 677 (void) snprintf(sbuf, sizeof (sbuf), "status %u", 678 status); 679 *estr = sbuf; 680 } else { 681 *estr = v6_status[status]; 682 } 683 } 684 return (status); 685 } 686 687 void 688 write_lease_to_hostconf(dhcp_smach_t *dsmp) 689 { 690 PKT_LIST *plp[2]; 691 const char *hcfile; 692 693 hcfile = ifname_to_hostconf(dsmp->dsm_name, dsmp->dsm_isv6); 694 plp[0] = dsmp->dsm_ack; 695 plp[1] = dsmp->dsm_orig_ack; 696 if (write_hostconf(dsmp->dsm_name, plp, 2, 697 monosec_to_time(dsmp->dsm_curstart_monosec), 698 dsmp->dsm_isv6) != -1) { 699 dhcpmsg(MSG_DEBUG, "wrote lease to %s", hcfile); 700 } else if (errno == EROFS) { 701 dhcpmsg(MSG_DEBUG, "%s is on a read-only file " 702 "system; not saving lease", hcfile); 703 } else { 704 dhcpmsg(MSG_ERR, "cannot write %s (reboot will " 705 "not use cached configuration)", hcfile); 706 } 707 } 708