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