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 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <unistd.h> 29 #include <sys/types.h> 30 #include <sys/stat.h> 31 #include <stdlib.h> 32 #include <netinet/in.h> /* struct in_addr */ 33 #include <netinet/dhcp.h> 34 #include <signal.h> 35 #include <sys/dlpi.h> 36 #include <sys/sockio.h> 37 #include <sys/socket.h> 38 #include <errno.h> 39 #include <net/route.h> 40 #include <net/if_arp.h> 41 #include <string.h> 42 #include <dhcpmsg.h> 43 #include <ctype.h> 44 #include <netdb.h> 45 #include <fcntl.h> 46 #include <stdio.h> 47 48 #include "states.h" 49 #include "agent.h" 50 #include "interface.h" 51 #include "util.h" 52 #include "packet.h" 53 #include "defaults.h" 54 55 /* 56 * this file contains utility functions that have no real better home 57 * of their own. they can largely be broken into six categories: 58 * 59 * o conversion functions -- functions to turn integers into strings, 60 * or to convert between units of a similar measure. 61 * 62 * o ipc-related functions -- functions to simplify the generation of 63 * ipc messages to the agent's clients. 64 * 65 * o signal-related functions -- functions to clean up the agent when 66 * it receives a signal. 67 * 68 * o routing table manipulation functions 69 * 70 * o acknak handler functions 71 * 72 * o true miscellany -- anything else 73 */ 74 75 /* 76 * pkt_type_to_string(): stringifies a packet type 77 * 78 * input: uchar_t: a DHCP packet type value, as defined in RFC2131 79 * output: const char *: the stringified packet type 80 */ 81 82 const char * 83 pkt_type_to_string(uchar_t type) 84 { 85 /* 86 * note: the ordering here allows direct indexing of the table 87 * based on the RFC2131 packet type value passed in. 88 */ 89 90 static const char *types[] = { 91 "BOOTP", "DISCOVER", "OFFER", "REQUEST", "DECLINE", 92 "ACK", "NAK", "RELEASE", "INFORM" 93 }; 94 95 if (type >= (sizeof (types) / sizeof (*types)) || types[type] == NULL) 96 return ("<unknown>"); 97 98 return (types[type]); 99 } 100 101 /* 102 * dlpi_to_arp(): converts DLPI datalink types into ARP datalink types 103 * 104 * input: uchar_t: the DLPI datalink type 105 * output: uchar_t: the ARP datalink type (0 if no corresponding code) 106 */ 107 108 uchar_t 109 dlpi_to_arp(uchar_t dlpi_type) 110 { 111 switch (dlpi_type) { 112 113 case DL_ETHER: 114 return (1); 115 116 case DL_FRAME: 117 return (15); 118 119 case DL_ATM: 120 return (16); 121 122 case DL_HDLC: 123 return (17); 124 125 case DL_FC: 126 return (18); 127 128 case DL_CSMACD: /* ieee 802 networks */ 129 case DL_TPB: 130 case DL_TPR: 131 case DL_METRO: 132 case DL_FDDI: 133 return (6); 134 case DL_IB: 135 return (ARPHRD_IB); 136 } 137 138 return (0); 139 } 140 141 /* 142 * monosec_to_string(): converts a monosec_t into a date string 143 * 144 * input: monosec_t: the monosec_t to convert 145 * output: const char *: the corresponding date string 146 */ 147 148 const char * 149 monosec_to_string(monosec_t monosec) 150 { 151 time_t time = monosec_to_time(monosec); 152 char *time_string = ctime(&time); 153 154 /* strip off the newline -- ugh, why, why, why.. */ 155 time_string[strlen(time_string) - 1] = '\0'; 156 return (time_string); 157 } 158 159 /* 160 * monosec(): returns a monotonically increasing time in seconds that 161 * is not affected by stime(2) or adjtime(2). 162 * 163 * input: void 164 * output: monosec_t: the number of seconds since some time in the past 165 */ 166 167 monosec_t 168 monosec(void) 169 { 170 return (gethrtime() / NANOSEC); 171 } 172 173 /* 174 * monosec_to_time(): converts a monosec_t into real wall time 175 * 176 * input: monosec_t: the absolute monosec_t to convert 177 * output: time_t: the absolute time that monosec_t represents in wall time 178 */ 179 180 time_t 181 monosec_to_time(monosec_t abs_monosec) 182 { 183 return (abs_monosec - monosec()) + time(NULL); 184 } 185 186 /* 187 * send_ok_reply(): sends an "ok" reply to a request and closes the ipc 188 * connection 189 * 190 * input: dhcp_ipc_request_t *: the request to reply to 191 * int *: the ipc connection file descriptor (set to -1 on return) 192 * output: void 193 * note: the request is freed (thus the request must be on the heap). 194 */ 195 196 void 197 send_ok_reply(dhcp_ipc_request_t *request, int *control_fd) 198 { 199 send_error_reply(request, 0, control_fd); 200 } 201 202 /* 203 * send_error_reply(): sends an "error" reply to a request and closes the ipc 204 * connection 205 * 206 * input: dhcp_ipc_request_t *: the request to reply to 207 * int: the error to send back on the ipc connection 208 * int *: the ipc connection file descriptor (set to -1 on return) 209 * output: void 210 * note: the request is freed (thus the request must be on the heap). 211 */ 212 213 void 214 send_error_reply(dhcp_ipc_request_t *request, int error, int *control_fd) 215 { 216 send_data_reply(request, control_fd, error, DHCP_TYPE_NONE, NULL, NULL); 217 } 218 219 /* 220 * send_data_reply(): sends a reply to a request and closes the ipc connection 221 * 222 * input: dhcp_ipc_request_t *: the request to reply to 223 * int *: the ipc connection file descriptor (set to -1 on return) 224 * int: the status to send back on the ipc connection (zero for 225 * success, DHCP_IPC_E_* otherwise). 226 * dhcp_data_type_t: the type of the payload in the reply 227 * void *: the payload for the reply, or NULL if there is no payload 228 * size_t: the size of the payload 229 * output: void 230 * note: the request is freed (thus the request must be on the heap). 231 */ 232 233 void 234 send_data_reply(dhcp_ipc_request_t *request, int *control_fd, 235 int error, dhcp_data_type_t type, void *buffer, size_t size) 236 { 237 dhcp_ipc_reply_t *reply; 238 239 if (*control_fd == -1) 240 return; 241 242 reply = dhcp_ipc_alloc_reply(request, error, buffer, size, type); 243 if (reply == NULL) 244 dhcpmsg(MSG_ERR, "send_data_reply: cannot allocate reply"); 245 246 else if (dhcp_ipc_send_reply(*control_fd, reply) != 0) 247 dhcpmsg(MSG_ERR, "send_data_reply: dhcp_ipc_send_reply"); 248 249 /* 250 * free the request since we've now used it to send our reply. 251 * we can also close the socket since the reply has been sent. 252 */ 253 254 free(reply); 255 free(request); 256 (void) dhcp_ipc_close(*control_fd); 257 *control_fd = -1; 258 } 259 260 /* 261 * print_server_msg(): prints a message from a DHCP server 262 * 263 * input: struct ifslist *: the interface the message came in on 264 * DHCP_OPT *: the option containing the string to display 265 * output: void 266 */ 267 268 void 269 print_server_msg(struct ifslist *ifsp, DHCP_OPT *p) 270 { 271 dhcpmsg(MSG_INFO, "%s: message from server: %.*s", ifsp->if_name, 272 p->len, p->value); 273 } 274 275 /* 276 * alrm_exit(): Signal handler for SIGARLM. terminates grandparent. 277 * 278 * input: int: signal the handler was called with. 279 * 280 * output: void 281 */ 282 283 static void 284 alrm_exit(int sig) 285 { 286 int exitval; 287 288 if (sig == SIGALRM && grandparent != 0) 289 exitval = EXIT_SUCCESS; 290 else 291 exitval = EXIT_FAILURE; 292 293 _exit(exitval); 294 } 295 296 /* 297 * daemonize(): daemonizes the process 298 * 299 * input: void 300 * output: int: 1 on success, 0 on failure 301 */ 302 303 int 304 daemonize(void) 305 { 306 /* 307 * We've found that adoption takes sufficiently long that 308 * a dhcpinfo run after dhcpagent -a is started may occur 309 * before the agent is ready to process the request. 310 * The result is an error message and an unhappy user. 311 * 312 * The initial process now sleeps for DHCP_ADOPT_SLEEP, 313 * unless interrupted by a SIGALRM, in which case it 314 * exits immediately. This has the effect that the 315 * grandparent doesn't exit until the dhcpagent is ready 316 * to process requests. This defers the the balance of 317 * the system start-up script processing until the 318 * dhcpagent is ready to field requests. 319 * 320 * grandparent is only set for the adopt case; other 321 * cases do not require the wait. 322 */ 323 324 if (grandparent != 0) 325 (void) signal(SIGALRM, alrm_exit); 326 327 switch (fork()) { 328 329 case -1: 330 return (0); 331 332 case 0: 333 if (grandparent != 0) 334 (void) signal(SIGALRM, SIG_DFL); 335 336 /* 337 * setsid() makes us lose our controlling terminal, 338 * and become both a session leader and a process 339 * group leader. 340 */ 341 342 (void) setsid(); 343 344 /* 345 * under POSIX, a session leader can accidentally 346 * (through open(2)) acquire a controlling terminal if 347 * it does not have one. just to be safe, fork again 348 * so we are not a session leader. 349 */ 350 351 switch (fork()) { 352 353 case -1: 354 return (0); 355 356 case 0: 357 (void) signal(SIGHUP, SIG_IGN); 358 (void) chdir("/"); 359 (void) umask(022); 360 closefrom(0); 361 break; 362 363 default: 364 _exit(EXIT_SUCCESS); 365 } 366 break; 367 368 default: 369 if (grandparent != 0) { 370 (void) signal(SIGCHLD, SIG_IGN); 371 dhcpmsg(MSG_DEBUG, "dhcpagent: daemonize: " 372 "waiting for adoption to complete."); 373 if (sleep(DHCP_ADOPT_SLEEP) == 0) { 374 dhcpmsg(MSG_WARNING, "dhcpagent: daemonize: " 375 "timed out awaiting adoption."); 376 } 377 } 378 _exit(EXIT_SUCCESS); 379 } 380 381 return (1); 382 } 383 384 /* 385 * update_default_route(): update the interface's default route 386 * 387 * input: int: the type of message; either RTM_ADD or RTM_DELETE 388 * struct in_addr: the default gateway to use 389 * const char *: the interface associated with the route 390 * int: any additional flags (besides RTF_STATIC and RTF_GATEWAY) 391 * output: int: 1 on success, 0 on failure 392 */ 393 394 static int 395 update_default_route(const char *ifname, int type, struct in_addr *gateway_nbo, 396 int flags) 397 { 398 static int rtsock_fd = -1; 399 struct { 400 struct rt_msghdr rm_mh; 401 struct sockaddr_in rm_dst; 402 struct sockaddr_in rm_gw; 403 struct sockaddr_in rm_mask; 404 struct sockaddr_dl rm_ifp; 405 } rtmsg; 406 407 if (rtsock_fd == -1) { 408 rtsock_fd = socket(PF_ROUTE, SOCK_RAW, 0); 409 if (rtsock_fd == -1) { 410 dhcpmsg(MSG_ERR, "update_default_route: " 411 "cannot create routing socket"); 412 return (0); 413 } 414 } 415 416 (void) memset(&rtmsg, 0, sizeof (rtmsg)); 417 rtmsg.rm_mh.rtm_version = RTM_VERSION; 418 rtmsg.rm_mh.rtm_msglen = sizeof (rtmsg); 419 rtmsg.rm_mh.rtm_type = type; 420 rtmsg.rm_mh.rtm_pid = getpid(); 421 rtmsg.rm_mh.rtm_flags = RTF_GATEWAY | RTF_STATIC | flags; 422 rtmsg.rm_mh.rtm_addrs = RTA_GATEWAY | RTA_DST | RTA_NETMASK | RTA_IFP; 423 424 rtmsg.rm_gw.sin_family = AF_INET; 425 rtmsg.rm_gw.sin_addr = *gateway_nbo; 426 427 rtmsg.rm_dst.sin_family = AF_INET; 428 rtmsg.rm_dst.sin_addr.s_addr = htonl(INADDR_ANY); 429 430 rtmsg.rm_mask.sin_family = AF_INET; 431 rtmsg.rm_mask.sin_addr.s_addr = htonl(0); 432 433 rtmsg.rm_ifp.sdl_family = AF_LINK; 434 rtmsg.rm_ifp.sdl_index = if_nametoindex(ifname); 435 436 return (write(rtsock_fd, &rtmsg, sizeof (rtmsg)) == sizeof (rtmsg)); 437 } 438 439 /* 440 * add_default_route(): add the default route to the given gateway 441 * 442 * input: const char *: the name of the interface associated with the route 443 * struct in_addr: the default gateway to add 444 * output: int: 1 on success, 0 on failure 445 */ 446 447 int 448 add_default_route(const char *ifname, struct in_addr *gateway_nbo) 449 { 450 if (strchr(ifname, ':') != NULL) /* see README */ 451 return (1); 452 453 return (update_default_route(ifname, RTM_ADD, gateway_nbo, RTF_UP)); 454 } 455 456 /* 457 * del_default_route(): deletes the default route to the given gateway 458 * 459 * input: const char *: the name of the interface associated with the route 460 * struct in_addr: if not INADDR_ANY, the default gateway to remove 461 * output: int: 1 on success, 0 on failure 462 */ 463 464 int 465 del_default_route(const char *ifname, struct in_addr *gateway_nbo) 466 { 467 if (strchr(ifname, ':') != NULL) 468 return (1); 469 470 if (gateway_nbo->s_addr == htonl(INADDR_ANY)) /* no router */ 471 return (1); 472 473 return (update_default_route(ifname, RTM_DELETE, gateway_nbo, 0)); 474 } 475 476 /* 477 * inactivity_shutdown(): shuts down agent if there are no interfaces to manage 478 * 479 * input: iu_tq_t *: unused 480 * void *: unused 481 * output: void 482 */ 483 484 /* ARGSUSED */ 485 void 486 inactivity_shutdown(iu_tq_t *tqp, void *arg) 487 { 488 if (ifs_count() > 0) /* shouldn't happen, but... */ 489 return; 490 491 iu_stop_handling_events(eh, DHCP_REASON_INACTIVITY, NULL, NULL); 492 } 493 494 /* 495 * graceful_shutdown(): shuts down the agent gracefully 496 * 497 * input: int: the signal that caused graceful_shutdown to be called 498 * output: void 499 */ 500 501 void 502 graceful_shutdown(int sig) 503 { 504 iu_stop_handling_events(eh, (sig == SIGTERM ? DHCP_REASON_TERMINATE : 505 DHCP_REASON_SIGNAL), drain_script, NULL); 506 } 507 508 /* 509 * register_acknak(): registers dhcp_acknak() to be called back when ACK or 510 * NAK packets are received on a given interface 511 * 512 * input: struct ifslist *: the interface to register for 513 * output: int: 1 on success, 0 on failure 514 */ 515 516 int 517 register_acknak(struct ifslist *ifsp) 518 { 519 iu_event_id_t ack_id, ack_bcast_id = -1; 520 521 /* 522 * having an acknak id already registered isn't impossible; 523 * handle the situation as gracefully as possible. 524 */ 525 526 if (ifsp->if_acknak_id != -1) { 527 dhcpmsg(MSG_DEBUG, "register_acknak: acknak id pending, " 528 "attempting to cancel"); 529 if (unregister_acknak(ifsp) == 0) 530 return (0); 531 } 532 533 switch (ifsp->if_state) { 534 535 case BOUND: 536 case REBINDING: 537 case RENEWING: 538 539 ack_bcast_id = iu_register_event(eh, ifsp->if_sock_fd, POLLIN, 540 dhcp_acknak, ifsp); 541 542 if (ack_bcast_id == -1) { 543 dhcpmsg(MSG_WARNING, "register_acknak: cannot " 544 "register to receive socket broadcasts"); 545 return (0); 546 } 547 548 ack_id = iu_register_event(eh, ifsp->if_sock_ip_fd, POLLIN, 549 dhcp_acknak, ifsp); 550 break; 551 552 default: 553 ack_id = iu_register_event(eh, ifsp->if_dlpi_fd, POLLIN, 554 dhcp_acknak, ifsp); 555 break; 556 } 557 558 if (ack_id == -1) { 559 dhcpmsg(MSG_WARNING, "register_acknak: cannot register event"); 560 (void) iu_unregister_event(eh, ack_bcast_id, NULL); 561 return (0); 562 } 563 564 ifsp->if_acknak_id = ack_id; 565 hold_ifs(ifsp); 566 567 ifsp->if_acknak_bcast_id = ack_bcast_id; 568 if (ifsp->if_acknak_bcast_id != -1) { 569 hold_ifs(ifsp); 570 dhcpmsg(MSG_DEBUG, "register_acknak: registered broadcast id " 571 "%d", ack_bcast_id); 572 } 573 574 dhcpmsg(MSG_DEBUG, "register_acknak: registered acknak id %d", ack_id); 575 return (1); 576 } 577 578 /* 579 * unregister_acknak(): unregisters dhcp_acknak() to be called back 580 * 581 * input: struct ifslist *: the interface to unregister for 582 * output: int: 1 on success, 0 on failure 583 */ 584 585 int 586 unregister_acknak(struct ifslist *ifsp) 587 { 588 if (ifsp->if_acknak_id != -1) { 589 590 if (iu_unregister_event(eh, ifsp->if_acknak_id, NULL) == 0) { 591 dhcpmsg(MSG_DEBUG, "unregister_acknak: cannot " 592 "unregister acknak id %d on %s", 593 ifsp->if_acknak_id, ifsp->if_name); 594 return (0); 595 } 596 597 dhcpmsg(MSG_DEBUG, "unregister_acknak: unregistered acknak id " 598 "%d", ifsp->if_acknak_id); 599 600 ifsp->if_acknak_id = -1; 601 (void) release_ifs(ifsp); 602 } 603 604 if (ifsp->if_acknak_bcast_id != -1) { 605 606 if (iu_unregister_event(eh, ifsp->if_acknak_bcast_id, NULL) 607 == 0) { 608 dhcpmsg(MSG_DEBUG, "unregister_acknak: cannot " 609 "unregister broadcast id %d on %s", 610 ifsp->if_acknak_id, ifsp->if_name); 611 return (0); 612 } 613 614 dhcpmsg(MSG_DEBUG, "unregister_acknak: unregistered " 615 "broadcast id %d", ifsp->if_acknak_bcast_id); 616 617 ifsp->if_acknak_bcast_id = -1; 618 (void) release_ifs(ifsp); 619 } 620 621 return (1); 622 } 623 624 /* 625 * bind_sock(): binds a socket to a given IP address and port number 626 * 627 * input: int: the socket to bind 628 * in_port_t: the port number to bind to, host byte order 629 * in_addr_t: the address to bind to, host byte order 630 * output: int: 1 on success, 0 on failure 631 */ 632 633 int 634 bind_sock(int fd, in_port_t port_hbo, in_addr_t addr_hbo) 635 { 636 struct sockaddr_in sin; 637 int on = 1; 638 639 (void) memset(&sin, 0, sizeof (struct sockaddr_in)); 640 sin.sin_family = AF_INET; 641 sin.sin_port = htons(port_hbo); 642 sin.sin_addr.s_addr = htonl(addr_hbo); 643 644 (void) setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (int)); 645 646 return (bind(fd, (struct sockaddr *)&sin, sizeof (sin)) == 0); 647 } 648 649 /* 650 * valid_hostname(): check whether a string is a valid hostname 651 * 652 * input: const char *: the string to verify as a hostname 653 * output: boolean_t: B_TRUE if the string is a valid hostname 654 * 655 * Note that we accept both host names beginning with a digit and 656 * those containing hyphens. Neither is strictly legal according 657 * to the RFCs, but both are in common practice, so we endeavour 658 * to not break what customers are using. 659 */ 660 661 static boolean_t 662 valid_hostname(const char *hostname) 663 { 664 unsigned int i; 665 666 for (i = 0; hostname[i] != '\0'; i++) { 667 668 if (isalpha(hostname[i]) || isdigit(hostname[i]) || 669 (((hostname[i] == '-') || (hostname[i] == '.')) && (i > 0))) 670 continue; 671 672 return (B_FALSE); 673 } 674 675 return (i > 0); 676 } 677 678 /* 679 * iffile_to_hostname(): return the hostname contained on a line of the form 680 * 681 * [ ^I]*inet[ ^I]+hostname[\n]*\0 682 * 683 * in the file located at the specified path 684 * 685 * input: const char *: the path of the file to look in for the hostname 686 * output: const char *: the hostname at that path, or NULL on failure 687 */ 688 689 #define IFLINE_MAX 1024 /* maximum length of a hostname.<if> line */ 690 691 const char * 692 iffile_to_hostname(const char *path) 693 { 694 FILE *fp; 695 static char ifline[IFLINE_MAX]; 696 697 fp = fopen(path, "r"); 698 if (fp == NULL) 699 return (NULL); 700 701 /* 702 * /etc/hostname.<if> may contain multiple ifconfig commands, but each 703 * such command is on a separate line (see the "while read ifcmds" code 704 * in /etc/init.d/inetinit). Thus we will read the file a line at a 705 * time, searching for a line of the form 706 * 707 * [ ^I]*inet[ ^I]+hostname[\n]*\0 708 * 709 * extract the host name from it, and check it for validity. 710 */ 711 while (fgets(ifline, sizeof (ifline), fp) != NULL) { 712 char *p; 713 714 if ((p = strstr(ifline, "inet")) != NULL) { 715 if ((p != ifline) && !isspace(p[-1])) { 716 (void) fclose(fp); 717 return (NULL); 718 } 719 p += 4; /* skip over "inet" and expect spaces or tabs */ 720 if ((*p == '\n') || (*p == '\0')) { 721 (void) fclose(fp); 722 return (NULL); 723 } 724 if (isspace(*p)) { 725 char *nlptr; 726 727 /* no need to read more of the file */ 728 (void) fclose(fp); 729 730 while (isspace(*p)) 731 p++; 732 if ((nlptr = strrchr(p, '\n')) != NULL) 733 *nlptr = '\0'; 734 if (strlen(p) > MAXHOSTNAMELEN) { 735 dhcpmsg(MSG_WARNING, 736 "iffile_to_hostname:" 737 " host name too long"); 738 return (NULL); 739 } 740 if (valid_hostname(p)) { 741 return (p); 742 } else { 743 dhcpmsg(MSG_WARNING, 744 "iffile_to_hostname:" 745 " host name not valid"); 746 return (NULL); 747 } 748 } else { 749 (void) fclose(fp); 750 return (NULL); 751 } 752 } 753 } 754 755 (void) fclose(fp); 756 return (NULL); 757 } 758