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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <unistd.h> 30 #include <sys/types.h> 31 #include <sys/stat.h> 32 #include <stdlib.h> 33 #include <netinet/in.h> /* struct in_addr */ 34 #include <netinet/dhcp.h> 35 #include <signal.h> 36 #include <sys/dlpi.h> 37 #include <sys/sockio.h> 38 #include <sys/socket.h> 39 #include <errno.h> 40 #include <net/route.h> 41 #include <net/if_arp.h> 42 #include <string.h> 43 #include <dhcpmsg.h> 44 #include <ctype.h> 45 #include <netdb.h> 46 #include <fcntl.h> 47 #include <stdio.h> 48 49 #include "states.h" 50 #include "agent.h" 51 #include "interface.h" 52 #include "util.h" 53 #include "packet.h" 54 #include "defaults.h" 55 56 /* 57 * this file contains utility functions that have no real better home 58 * of their own. they can largely be broken into six categories: 59 * 60 * o conversion functions -- functions to turn integers into strings, 61 * or to convert between units of a similar measure. 62 * 63 * o ipc-related functions -- functions to simplify the generation of 64 * ipc messages to the agent's clients. 65 * 66 * o signal-related functions -- functions to clean up the agent when 67 * it receives a signal. 68 * 69 * o routing table manipulation functions 70 * 71 * o acknak handler functions 72 * 73 * o true miscellany -- anything else 74 */ 75 76 /* 77 * pkt_type_to_string(): stringifies a packet type 78 * 79 * input: uchar_t: a DHCP packet type value, as defined in RFC2131 80 * output: const char *: the stringified packet type 81 */ 82 83 const char * 84 pkt_type_to_string(uchar_t type) 85 { 86 /* 87 * note: the ordering here allows direct indexing of the table 88 * based on the RFC2131 packet type value passed in. 89 */ 90 91 static const char *types[] = { 92 "BOOTP", "DISCOVER", "OFFER", "REQUEST", "DECLINE", 93 "ACK", "NAK", "RELEASE", "INFORM" 94 }; 95 96 if (type >= (sizeof (types) / sizeof (*types)) || types[type] == NULL) 97 return ("<unknown>"); 98 99 return (types[type]); 100 } 101 102 /* 103 * dlpi_to_arp(): converts DLPI datalink types into ARP datalink types 104 * 105 * input: uchar_t: the DLPI datalink type 106 * output: uchar_t: the ARP datalink type (0 if no corresponding code) 107 */ 108 109 uchar_t 110 dlpi_to_arp(uchar_t dlpi_type) 111 { 112 switch (dlpi_type) { 113 114 case DL_ETHER: 115 return (1); 116 117 case DL_FRAME: 118 return (15); 119 120 case DL_ATM: 121 return (16); 122 123 case DL_HDLC: 124 return (17); 125 126 case DL_FC: 127 return (18); 128 129 case DL_CSMACD: /* ieee 802 networks */ 130 case DL_TPB: 131 case DL_TPR: 132 case DL_METRO: 133 case DL_FDDI: 134 return (6); 135 case DL_IB: 136 return (ARPHRD_IB); 137 } 138 139 return (0); 140 } 141 142 /* 143 * monosec_to_string(): converts a monosec_t into a date string 144 * 145 * input: monosec_t: the monosec_t to convert 146 * output: const char *: the corresponding date string 147 */ 148 149 const char * 150 monosec_to_string(monosec_t monosec) 151 { 152 time_t time = monosec_to_time(monosec); 153 char *time_string = ctime(&time); 154 155 /* strip off the newline -- ugh, why, why, why.. */ 156 time_string[strlen(time_string) - 1] = '\0'; 157 return (time_string); 158 } 159 160 /* 161 * monosec(): returns a monotonically increasing time in seconds that 162 * is not affected by stime(2) or adjtime(2). 163 * 164 * input: void 165 * output: monosec_t: the number of seconds since some time in the past 166 */ 167 168 monosec_t 169 monosec(void) 170 { 171 return (gethrtime() / NANOSEC); 172 } 173 174 /* 175 * monosec_to_time(): converts a monosec_t into real wall time 176 * 177 * input: monosec_t: the absolute monosec_t to convert 178 * output: time_t: the absolute time that monosec_t represents in wall time 179 */ 180 181 time_t 182 monosec_to_time(monosec_t abs_monosec) 183 { 184 return (abs_monosec - monosec()) + time(NULL); 185 } 186 187 /* 188 * send_ok_reply(): sends an "ok" reply to a request and closes the ipc 189 * connection 190 * 191 * input: dhcp_ipc_request_t *: the request to reply to 192 * int *: the ipc connection file descriptor (set to -1 on return) 193 * output: void 194 * note: the request is freed (thus the request must be on the heap). 195 */ 196 197 void 198 send_ok_reply(dhcp_ipc_request_t *request, int *control_fd) 199 { 200 send_error_reply(request, 0, control_fd); 201 } 202 203 /* 204 * send_error_reply(): sends an "error" reply to a request and closes the ipc 205 * connection 206 * 207 * input: dhcp_ipc_request_t *: the request to reply to 208 * int: the error to send back on the ipc connection 209 * int *: the ipc connection file descriptor (set to -1 on return) 210 * output: void 211 * note: the request is freed (thus the request must be on the heap). 212 */ 213 214 void 215 send_error_reply(dhcp_ipc_request_t *request, int error, int *control_fd) 216 { 217 send_data_reply(request, control_fd, error, DHCP_TYPE_NONE, NULL, NULL); 218 } 219 220 /* 221 * send_data_reply(): sends a reply to a request and closes the ipc connection 222 * 223 * input: dhcp_ipc_request_t *: the request to reply to 224 * int *: the ipc connection file descriptor (set to -1 on return) 225 * int: the status to send back on the ipc connection (zero for 226 * success, DHCP_IPC_E_* otherwise). 227 * dhcp_data_type_t: the type of the payload in the reply 228 * void *: the payload for the reply, or NULL if there is no payload 229 * size_t: the size of the payload 230 * output: void 231 * note: the request is freed (thus the request must be on the heap). 232 */ 233 234 void 235 send_data_reply(dhcp_ipc_request_t *request, int *control_fd, 236 int error, dhcp_data_type_t type, void *buffer, size_t size) 237 { 238 dhcp_ipc_reply_t *reply; 239 240 if (*control_fd == -1) 241 return; 242 243 reply = dhcp_ipc_alloc_reply(request, error, buffer, size, type); 244 if (reply == NULL) 245 dhcpmsg(MSG_ERR, "send_data_reply: cannot allocate reply"); 246 247 else if (dhcp_ipc_send_reply(*control_fd, reply) != 0) 248 dhcpmsg(MSG_ERR, "send_data_reply: dhcp_ipc_send_reply"); 249 250 /* 251 * free the request since we've now used it to send our reply. 252 * we can also close the socket since the reply has been sent. 253 */ 254 255 free(reply); 256 free(request); 257 (void) dhcp_ipc_close(*control_fd); 258 *control_fd = -1; 259 } 260 261 /* 262 * print_server_msg(): prints a message from a DHCP server 263 * 264 * input: struct ifslist *: the interface the message came in on 265 * DHCP_OPT *: the option containing the string to display 266 * output: void 267 */ 268 269 void 270 print_server_msg(struct ifslist *ifsp, DHCP_OPT *p) 271 { 272 dhcpmsg(MSG_INFO, "%s: message from server: %.*s", ifsp->if_name, 273 p->len, p->value); 274 } 275 276 /* 277 * alrm_exit(): Signal handler for SIGARLM. terminates grandparent. 278 * 279 * input: int: signal the handler was called with. 280 * 281 * output: void 282 */ 283 284 static void 285 alrm_exit(int sig) 286 { 287 int exitval; 288 289 if (sig == SIGALRM && grandparent != 0) 290 exitval = EXIT_SUCCESS; 291 else 292 exitval = EXIT_FAILURE; 293 294 _exit(exitval); 295 } 296 297 /* 298 * daemonize(): daemonizes the process 299 * 300 * input: void 301 * output: int: 1 on success, 0 on failure 302 */ 303 304 int 305 daemonize(void) 306 { 307 /* 308 * We've found that adoption takes sufficiently long that 309 * a dhcpinfo run after dhcpagent -a is started may occur 310 * before the agent is ready to process the request. 311 * The result is an error message and an unhappy user. 312 * 313 * The initial process now sleeps for DHCP_ADOPT_SLEEP, 314 * unless interrupted by a SIGALRM, in which case it 315 * exits immediately. This has the effect that the 316 * grandparent doesn't exit until the dhcpagent is ready 317 * to process requests. This defers the the balance of 318 * the system start-up script processing until the 319 * dhcpagent is ready to field requests. 320 * 321 * grandparent is only set for the adopt case; other 322 * cases do not require the wait. 323 */ 324 325 if (grandparent != 0) 326 (void) signal(SIGALRM, alrm_exit); 327 328 switch (fork()) { 329 330 case -1: 331 return (0); 332 333 case 0: 334 if (grandparent != 0) 335 (void) signal(SIGALRM, SIG_DFL); 336 337 /* 338 * setsid() makes us lose our controlling terminal, 339 * and become both a session leader and a process 340 * group leader. 341 */ 342 343 (void) setsid(); 344 345 /* 346 * under POSIX, a session leader can accidentally 347 * (through open(2)) acquire a controlling terminal if 348 * it does not have one. just to be safe, fork again 349 * so we are not a session leader. 350 */ 351 352 switch (fork()) { 353 354 case -1: 355 return (0); 356 357 case 0: 358 (void) signal(SIGHUP, SIG_IGN); 359 (void) chdir("/"); 360 (void) umask(022); 361 closefrom(0); 362 break; 363 364 default: 365 _exit(EXIT_SUCCESS); 366 } 367 break; 368 369 default: 370 if (grandparent != 0) { 371 (void) signal(SIGCHLD, SIG_IGN); 372 dhcpmsg(MSG_DEBUG, "dhcpagent: daemonize: " 373 "waiting for adoption to complete."); 374 if (sleep(DHCP_ADOPT_SLEEP) == 0) { 375 dhcpmsg(MSG_WARNING, "dhcpagent: daemonize: " 376 "timed out awaiting adoption."); 377 } 378 } 379 _exit(EXIT_SUCCESS); 380 } 381 382 return (1); 383 } 384 385 /* 386 * update_default_route(): update the interface's default route 387 * 388 * input: int: the type of message; either RTM_ADD or RTM_DELETE 389 * struct in_addr: the default gateway to use 390 * const char *: the interface associated with the route 391 * int: any additional flags (besides RTF_STATIC and RTF_GATEWAY) 392 * output: int: 1 on success, 0 on failure 393 */ 394 395 static int 396 update_default_route(const char *ifname, int type, struct in_addr *gateway_nbo, 397 int flags) 398 { 399 static int rtsock_fd = -1; 400 struct { 401 struct rt_msghdr rm_mh; 402 struct sockaddr_in rm_dst; 403 struct sockaddr_in rm_gw; 404 struct sockaddr_in rm_mask; 405 struct sockaddr_dl rm_ifp; 406 } rtmsg; 407 408 if (rtsock_fd == -1) { 409 rtsock_fd = socket(PF_ROUTE, SOCK_RAW, 0); 410 if (rtsock_fd == -1) { 411 dhcpmsg(MSG_ERR, "update_default_route: " 412 "cannot create routing socket"); 413 return (0); 414 } 415 } 416 417 (void) memset(&rtmsg, 0, sizeof (rtmsg)); 418 rtmsg.rm_mh.rtm_version = RTM_VERSION; 419 rtmsg.rm_mh.rtm_msglen = sizeof (rtmsg); 420 rtmsg.rm_mh.rtm_type = type; 421 rtmsg.rm_mh.rtm_pid = getpid(); 422 rtmsg.rm_mh.rtm_flags = RTF_GATEWAY | RTF_STATIC | flags; 423 rtmsg.rm_mh.rtm_addrs = RTA_GATEWAY | RTA_DST | RTA_NETMASK | RTA_IFP; 424 425 rtmsg.rm_gw.sin_family = AF_INET; 426 rtmsg.rm_gw.sin_addr = *gateway_nbo; 427 428 rtmsg.rm_dst.sin_family = AF_INET; 429 rtmsg.rm_dst.sin_addr.s_addr = htonl(INADDR_ANY); 430 431 rtmsg.rm_mask.sin_family = AF_INET; 432 rtmsg.rm_mask.sin_addr.s_addr = htonl(0); 433 434 rtmsg.rm_ifp.sdl_family = AF_LINK; 435 rtmsg.rm_ifp.sdl_index = if_nametoindex(ifname); 436 437 return (write(rtsock_fd, &rtmsg, sizeof (rtmsg)) == sizeof (rtmsg)); 438 } 439 440 /* 441 * add_default_route(): add the default route to the given gateway 442 * 443 * input: const char *: the name of the interface associated with the route 444 * struct in_addr: the default gateway to add 445 * output: int: 1 on success, 0 on failure 446 */ 447 448 int 449 add_default_route(const char *ifname, struct in_addr *gateway_nbo) 450 { 451 if (strchr(ifname, ':') != NULL) /* see README */ 452 return (1); 453 454 return (update_default_route(ifname, RTM_ADD, gateway_nbo, RTF_UP)); 455 } 456 457 /* 458 * del_default_route(): deletes the default route to the given gateway 459 * 460 * input: const char *: the name of the interface associated with the route 461 * struct in_addr: if not INADDR_ANY, the default gateway to remove 462 * output: int: 1 on success, 0 on failure 463 */ 464 465 int 466 del_default_route(const char *ifname, struct in_addr *gateway_nbo) 467 { 468 if (strchr(ifname, ':') != NULL) 469 return (1); 470 471 if (gateway_nbo->s_addr == htonl(INADDR_ANY)) /* no router */ 472 return (1); 473 474 return (update_default_route(ifname, RTM_DELETE, gateway_nbo, 0)); 475 } 476 477 /* 478 * inactivity_shutdown(): shuts down agent if there are no interfaces to manage 479 * 480 * input: iu_tq_t *: unused 481 * void *: unused 482 * output: void 483 */ 484 485 /* ARGSUSED */ 486 void 487 inactivity_shutdown(iu_tq_t *tqp, void *arg) 488 { 489 if (ifs_count() > 0) /* shouldn't happen, but... */ 490 return; 491 492 iu_stop_handling_events(eh, DHCP_REASON_INACTIVITY, NULL, NULL); 493 } 494 495 /* 496 * graceful_shutdown(): shuts down the agent gracefully 497 * 498 * input: int: the signal that caused graceful_shutdown to be called 499 * output: void 500 */ 501 502 void 503 graceful_shutdown(int sig) 504 { 505 iu_stop_handling_events(eh, sig, 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