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 <sys/types.h> 29 #include <stdlib.h> 30 #include <assert.h> 31 #include <errno.h> 32 #include <locale.h> 33 #include <string.h> 34 #include <unistd.h> 35 #include <signal.h> 36 #include <stdio.h> 37 #include <stdio_ext.h> 38 #include <dhcp_hostconf.h> 39 #include <dhcp_symbol.h> 40 #include <dhcpagent_ipc.h> 41 #include <dhcpmsg.h> 42 #include <netinet/dhcp.h> 43 44 #include "async.h" 45 #include "agent.h" 46 #include "script_handler.h" 47 #include "util.h" 48 #include "class_id.h" 49 #include "states.h" 50 #include "packet.h" 51 52 #ifndef TEXT_DOMAIN 53 #define TEXT_DOMAIN "SYS_TEST" 54 #endif 55 56 iu_timer_id_t inactivity_id; 57 int class_id_len = 0; 58 char *class_id; 59 iu_eh_t *eh; 60 iu_tq_t *tq; 61 pid_t grandparent; 62 63 static boolean_t shutdown_started = B_FALSE; 64 static boolean_t do_adopt = B_FALSE; 65 static unsigned int debug_level = 0; 66 static iu_eh_callback_t accept_event, ipc_event; 67 68 /* 69 * The ipc_cmd_allowed[] table indicates which IPC commands are allowed in 70 * which states; a non-zero value indicates the command is permitted. 71 * 72 * START is permitted if the interface is fresh, or if we are in the process 73 * of trying to obtain a lease (as a convenience to save the administrator 74 * from having to do an explicit DROP). EXTEND, RELEASE, and GET_TAG require 75 * a lease to be obtained in order to make sense. INFORM is permitted if the 76 * interface is fresh or has an INFORM in progress or previously done on it -- 77 * otherwise a DROP or RELEASE is first required. PING and STATUS always make 78 * sense and thus are always permitted, as is DROP in order to permit the 79 * administrator to always bail out. 80 */ 81 static int ipc_cmd_allowed[DHCP_NSTATES][DHCP_NIPC] = { 82 /* D E P R S S I G */ 83 /* R X I E T T N E */ 84 /* O T N L A A F T */ 85 /* P E G E R T O _ */ 86 /* . N . A T U R T */ 87 /* . D . S . S M A */ 88 /* . . . E . . . G */ 89 /* INIT */ { 1, 0, 1, 0, 1, 1, 1, 0 }, 90 /* SELECTING */ { 1, 0, 1, 0, 1, 1, 0, 0 }, 91 /* REQUESTING */ { 1, 0, 1, 0, 1, 1, 0, 0 }, 92 /* BOUND */ { 1, 1, 1, 1, 0, 1, 0, 1 }, 93 /* RENEWING */ { 1, 1, 1, 1, 0, 1, 0, 1 }, 94 /* REBINDING */ { 1, 1, 1, 1, 0, 1, 0, 1 }, 95 /* INFORMATION */ { 1, 0, 1, 0, 0, 1, 1, 1 }, 96 /* INIT_REBOOT */ { 1, 0, 1, 0, 1, 1, 0, 0 }, 97 /* ADOPTING */ { 1, 0, 1, 0, 0, 1, 0, 0 }, 98 /* INFORM_SENT */ { 1, 0, 1, 0, 0, 1, 1, 0 } 99 }; 100 101 int 102 main(int argc, char **argv) 103 { 104 boolean_t is_daemon = B_TRUE; 105 boolean_t is_verbose = B_FALSE; 106 int ipc_fd; 107 int c; 108 struct rlimit rl; 109 110 /* 111 * -l is ignored for compatibility with old agent. 112 */ 113 114 while ((c = getopt(argc, argv, "vd:l:fa")) != EOF) { 115 116 switch (c) { 117 118 case 'a': 119 do_adopt = B_TRUE; 120 grandparent = getpid(); 121 break; 122 123 case 'd': 124 debug_level = strtoul(optarg, NULL, 0); 125 break; 126 127 case 'f': 128 is_daemon = B_FALSE; 129 break; 130 131 case 'v': 132 is_verbose = B_TRUE; 133 break; 134 135 case '?': 136 (void) fprintf(stderr, "usage: %s [-a] [-d n] [-f] [-v]" 137 "\n", argv[0]); 138 return (EXIT_FAILURE); 139 140 default: 141 break; 142 } 143 } 144 145 (void) setlocale(LC_ALL, ""); 146 (void) textdomain(TEXT_DOMAIN); 147 148 if (geteuid() != 0) { 149 dhcpmsg_init(argv[0], B_FALSE, is_verbose, debug_level); 150 dhcpmsg(MSG_ERROR, "must be super-user"); 151 dhcpmsg_fini(); 152 return (EXIT_FAILURE); 153 } 154 155 if (is_daemon && daemonize() == 0) { 156 dhcpmsg_init(argv[0], B_FALSE, is_verbose, debug_level); 157 dhcpmsg(MSG_ERR, "cannot become daemon, exiting"); 158 dhcpmsg_fini(); 159 return (EXIT_FAILURE); 160 } 161 162 dhcpmsg_init(argv[0], is_daemon, is_verbose, debug_level); 163 (void) atexit(dhcpmsg_fini); 164 165 tq = iu_tq_create(); 166 eh = iu_eh_create(); 167 168 if (eh == NULL || tq == NULL) { 169 errno = ENOMEM; 170 dhcpmsg(MSG_ERR, "cannot create timer queue or event handler"); 171 return (EXIT_FAILURE); 172 } 173 174 /* 175 * ignore most signals that could be reasonably generated. 176 */ 177 178 (void) signal(SIGTERM, graceful_shutdown); 179 (void) signal(SIGQUIT, graceful_shutdown); 180 (void) signal(SIGPIPE, SIG_IGN); 181 (void) signal(SIGUSR1, SIG_IGN); 182 (void) signal(SIGUSR2, SIG_IGN); 183 (void) signal(SIGINT, SIG_IGN); 184 (void) signal(SIGHUP, SIG_IGN); 185 (void) signal(SIGCHLD, SIG_IGN); 186 187 /* 188 * upon SIGTHAW we need to refresh any non-infinite leases. 189 */ 190 191 (void) iu_eh_register_signal(eh, SIGTHAW, refresh_ifslist, NULL); 192 193 class_id = get_class_id(); 194 if (class_id != NULL) 195 class_id_len = strlen(class_id); 196 else 197 dhcpmsg(MSG_WARNING, "get_class_id failed, continuing " 198 "with no vendor class id"); 199 200 /* 201 * the inactivity timer is enabled any time there are no 202 * interfaces under DHCP control. if DHCP_INACTIVITY_WAIT 203 * seconds transpire without an interface under DHCP control, 204 * the agent shuts down. 205 */ 206 207 inactivity_id = iu_schedule_timer(tq, DHCP_INACTIVITY_WAIT, 208 inactivity_shutdown, NULL); 209 210 /* 211 * max out the number available descriptors, just in case.. 212 */ 213 214 rl.rlim_cur = RLIM_INFINITY; 215 rl.rlim_max = RLIM_INFINITY; 216 if (setrlimit(RLIMIT_NOFILE, &rl) == -1) 217 dhcpmsg(MSG_ERR, "setrlimit failed"); 218 219 (void) enable_extended_FILE_stdio(-1, -1); 220 221 /* 222 * create the ipc channel that the agent will listen for 223 * requests on, and register it with the event handler so that 224 * `accept_event' will be called back. 225 */ 226 227 switch (dhcp_ipc_init(&ipc_fd)) { 228 229 case 0: 230 break; 231 232 case DHCP_IPC_E_BIND: 233 dhcpmsg(MSG_ERROR, "dhcp_ipc_init: cannot bind to port " 234 "%i (agent already running?)", IPPORT_DHCPAGENT); 235 return (EXIT_FAILURE); 236 237 default: 238 dhcpmsg(MSG_ERROR, "dhcp_ipc_init failed"); 239 return (EXIT_FAILURE); 240 } 241 242 if (iu_register_event(eh, ipc_fd, POLLIN, accept_event, 0) == -1) { 243 dhcpmsg(MSG_ERR, "cannot register ipc fd for messages"); 244 return (EXIT_FAILURE); 245 } 246 247 /* 248 * if the -a (adopt) option was specified, try to adopt the 249 * kernel-managed interface before we start. Our grandparent 250 * will be waiting for us to finish this, so signal him when 251 * we're done. 252 */ 253 254 if (do_adopt) { 255 int result; 256 257 result = dhcp_adopt(); 258 259 if (grandparent != (pid_t)0) { 260 dhcpmsg(MSG_DEBUG, "adoption complete, signalling " 261 "parent (%i) to exit.", grandparent); 262 (void) kill(grandparent, SIGALRM); 263 } 264 265 if (result == 0) 266 return (EXIT_FAILURE); 267 } 268 269 /* 270 * enter the main event loop; this is where all the real work 271 * takes place (through registering events and scheduling timers). 272 * this function only returns when the agent is shutting down. 273 */ 274 275 switch (iu_handle_events(eh, tq)) { 276 277 case -1: 278 dhcpmsg(MSG_WARNING, "iu_handle_events exited abnormally"); 279 break; 280 281 case DHCP_REASON_INACTIVITY: 282 dhcpmsg(MSG_INFO, "no interfaces to manage, shutting down..."); 283 break; 284 285 case DHCP_REASON_TERMINATE: 286 dhcpmsg(MSG_INFO, "received SIGTERM, shutting down..."); 287 break; 288 289 case DHCP_REASON_SIGNAL: 290 dhcpmsg(MSG_WARNING, "received unexpected signal, shutting " 291 "down..."); 292 break; 293 } 294 295 (void) iu_eh_unregister_signal(eh, SIGTHAW, NULL); 296 297 iu_eh_destroy(eh); 298 iu_tq_destroy(tq); 299 300 return (EXIT_SUCCESS); 301 } 302 303 /* 304 * drain_script(): event loop callback during shutdown 305 * 306 * input: eh_t *: unused 307 * void *: unused 308 * output: boolean_t: B_TRUE if event loop should exit; B_FALSE otherwise 309 */ 310 311 /* ARGSUSED */ 312 boolean_t 313 drain_script(iu_eh_t *ehp, void *arg) 314 { 315 if (shutdown_started == B_FALSE) { 316 shutdown_started = B_TRUE; 317 if (do_adopt == B_FALSE) /* see 4291141 */ 318 nuke_ifslist(B_TRUE); 319 } 320 return (script_count == 0); 321 } 322 323 /* 324 * accept_event(): accepts a new connection on the ipc socket and registers 325 * to receive its messages with the event handler 326 * 327 * input: iu_eh_t *: unused 328 * int: the file descriptor in the iu_eh_t * the connection came in on 329 * (other arguments unused) 330 * output: void 331 */ 332 333 /* ARGSUSED */ 334 static void 335 accept_event(iu_eh_t *ehp, int fd, short events, iu_event_id_t id, void *arg) 336 { 337 int client_fd; 338 int is_priv; 339 340 if (dhcp_ipc_accept(fd, &client_fd, &is_priv) != 0) { 341 dhcpmsg(MSG_ERR, "accept_event: accept on ipc socket"); 342 return; 343 } 344 345 if (iu_register_event(eh, client_fd, POLLIN, ipc_event, 346 (void *)is_priv) == -1) { 347 dhcpmsg(MSG_ERROR, "accept_event: cannot register ipc socket " 348 "for callback"); 349 } 350 } 351 352 /* 353 * ipc_event(): processes incoming ipc requests 354 * 355 * input: iu_eh_t *: unused 356 * int: the file descriptor in the iu_eh_t * the request came in on 357 * short: unused 358 * iu_event_id_t: unused 359 * void *: indicates whether the request is from a privileged client 360 * output: void 361 */ 362 363 /* ARGSUSED */ 364 static void 365 ipc_event(iu_eh_t *ehp, int fd, short events, iu_event_id_t id, void *arg) 366 { 367 dhcp_ipc_request_t *request; 368 struct ifslist *ifsp, *primary_ifsp; 369 int error, is_priv = (int)arg; 370 PKT_LIST *plp[2]; 371 dhcp_ipc_type_t cmd; 372 373 (void) iu_unregister_event(eh, id, NULL); 374 375 if (dhcp_ipc_recv_request(fd, &request, DHCP_IPC_REQUEST_WAIT) != 0) { 376 dhcpmsg(MSG_ERROR, "ipc_event: dhcp_ipc_recv_request failed"); 377 (void) dhcp_ipc_close(fd); 378 return; 379 } 380 381 cmd = DHCP_IPC_CMD(request->message_type); 382 if (cmd >= DHCP_NIPC) { 383 send_error_reply(request, DHCP_IPC_E_CMD_UNKNOWN, &fd); 384 return; 385 } 386 387 /* return EPERM for any of the privileged actions */ 388 389 if (!is_priv) { 390 switch (cmd) { 391 392 case DHCP_STATUS: 393 case DHCP_PING: 394 case DHCP_GET_TAG: 395 break; 396 397 default: 398 dhcpmsg(MSG_WARNING, "ipc_event: privileged ipc " 399 "command (%i) attempted on %s", cmd, 400 request->ifname); 401 402 send_error_reply(request, DHCP_IPC_E_PERM, &fd); 403 return; 404 } 405 } 406 407 /* 408 * try to locate the ifs associated with this command. if the 409 * command is DHCP_START or DHCP_INFORM, then if there isn't 410 * an ifs already, make one (there may already be one from a 411 * previous failed attempt to START or INFORM). otherwise, 412 * verify the interface is still valid. 413 */ 414 415 ifsp = lookup_ifs(request->ifname); 416 417 switch (cmd) { 418 419 case DHCP_START: /* FALLTHRU */ 420 case DHCP_INFORM: 421 /* 422 * it's possible that the interface already exists, but 423 * has been abandoned. usually in those cases we should 424 * return DHCP_IPC_E_UNKIF, but that makes little sense 425 * in the case of "start" or "inform", so just ignore 426 * the abandoned interface and start over anew. 427 */ 428 429 if (ifsp != NULL && verify_ifs(ifsp) == 0) 430 ifsp = NULL; 431 432 /* 433 * as part of initializing the ifs, insert_ifs() 434 * creates a DLPI stream at ifsp->if_dlpi_fd. 435 */ 436 437 if (ifsp == NULL) { 438 ifsp = insert_ifs(request->ifname, B_FALSE, &error); 439 if (ifsp == NULL) { 440 send_error_reply(request, error, &fd); 441 return; 442 } 443 } 444 break; 445 446 default: 447 if (ifsp == NULL) { 448 if (request->ifname[0] == '\0') 449 error = DHCP_IPC_E_NOPRIMARY; 450 else 451 error = DHCP_IPC_E_UNKIF; 452 453 send_error_reply(request, error, &fd); 454 return; 455 } 456 break; 457 } 458 459 if (verify_ifs(ifsp) == 0) { 460 send_error_reply(request, DHCP_IPC_E_UNKIF, &fd); 461 return; 462 } 463 464 if (ifsp->if_dflags & DHCP_IF_BOOTP) { 465 switch (cmd) { 466 467 case DHCP_EXTEND: 468 case DHCP_RELEASE: 469 case DHCP_INFORM: 470 send_error_reply(request, DHCP_IPC_E_BOOTP, &fd); 471 return; 472 473 default: 474 break; 475 } 476 } 477 478 /* 479 * verify that the interface is in a state which will allow the 480 * command. we do this up front so that we can return an error 481 * *before* needlessly cancelling an in-progress transaction. 482 */ 483 484 if (!ipc_cmd_allowed[ifsp->if_state][cmd]) { 485 send_error_reply(request, DHCP_IPC_E_OUTSTATE, &fd); 486 return; 487 } 488 489 if ((request->message_type & DHCP_PRIMARY) && is_priv) { 490 if ((primary_ifsp = lookup_ifs("")) != NULL) 491 primary_ifsp->if_dflags &= ~DHCP_IF_PRIMARY; 492 ifsp->if_dflags |= DHCP_IF_PRIMARY; 493 } 494 495 /* 496 * current design dictates that there can be only one 497 * outstanding transaction per interface -- this simplifies 498 * the code considerably and also fits well with RFC2131. 499 * it is worth classifying the different DHCP commands into 500 * synchronous (those which we will handle now and be done 501 * with) and asynchronous (those which require transactions 502 * and will be completed at an indeterminate time in the 503 * future): 504 * 505 * DROP: removes the agent's management of an interface. 506 * asynchronous as the script program may be invoked. 507 * 508 * PING: checks to see if the agent controls an interface. 509 * synchronous, since no packets need to be sent 510 * to the DHCP server. 511 * 512 * STATUS: returns information about the an interface. 513 * synchronous, since no packets need to be sent 514 * to the DHCP server. 515 * 516 * RELEASE: releases the agent's management of an interface 517 * and brings the interface down. asynchronous as 518 * the script program may be invoked. 519 * 520 * EXTEND: renews a lease. asynchronous, since the agent 521 * needs to wait for an ACK, etc. 522 * 523 * START: starts DHCP on an interface. asynchronous since 524 * the agent needs to wait for OFFERs, ACKs, etc. 525 * 526 * INFORM: obtains configuration parameters for an externally 527 * configured interface. asynchronous, since the 528 * agent needs to wait for an ACK. 529 * 530 * notice that EXTEND, INFORM, START, DROP and RELEASE are 531 * asynchronous. notice also that asynchronous commands may 532 * occur from within the agent -- for instance, the agent 533 * will need to do implicit EXTENDs to extend the lease. in 534 * order to make the code simpler, the following rules apply 535 * for asynchronous commands: 536 * 537 * there can only be one asynchronous command at a time per 538 * interface. the current asynchronous command is managed by 539 * the async_* api: async_start(), async_finish(), 540 * async_timeout(), async_cancel(), and async_pending(). 541 * async_start() starts management of a new asynchronous 542 * command on an interface, which should only be done after 543 * async_pending() is called to check that there are no 544 * pending asynchronous commands on that interface. when the 545 * command is completed, async_finish() should be called. all 546 * asynchronous commands have an associated timer, which calls 547 * async_timeout() when it times out. if async_timeout() 548 * decides that the asynchronous command should be cancelled 549 * (see below), it calls async_cancel() to attempt 550 * cancellation. 551 * 552 * asynchronous commands started by a user command have an 553 * associated ipc_action which provides the agent with 554 * information for how to get in touch with the user command 555 * when the action completes. these ipc_action records also 556 * have an associated timeout which may be infinite. 557 * ipc_action_start() should be called when starting an 558 * asynchronous command requested by a user, which sets up the 559 * timer and keeps track of the ipc information (file 560 * descriptor, request type). when the asynchronous command 561 * completes, ipc_action_finish() should be called to return a 562 * command status code to the user and close the ipc 563 * connection). if the command does not complete before the 564 * timer fires, ipc_action_timeout() is called which closes 565 * the ipc connection and returns DHCP_IPC_E_TIMEOUT to the 566 * user. note that independent of ipc_action_timeout(), 567 * ipc_action_finish() should be called. 568 * 569 * on a case-by-case basis, here is what happens (per interface): 570 * 571 * o when an asynchronous command is requested, then 572 * async_pending() is called to see if there is already 573 * an asynchronous event. if so, the command does not 574 * proceed, and if there is an associated ipc_action, 575 * the user command is sent DHCP_IPC_E_PEND. 576 * 577 * o otherwise, the the transaction is started with 578 * async_start(). if the transaction is on behalf 579 * of a user, ipc_action_start() is called to keep 580 * track of the ipc information and set up the 581 * ipc_action timer. 582 * 583 * o if the command completes normally and before a 584 * timeout fires, then async_finish() is called. 585 * if there was an associated ipc_action, 586 * ipc_action_finish() is called to complete it. 587 * 588 * o if the command fails before a timeout fires, then 589 * async_finish() is called, and the interface is 590 * is returned to a known state based on the command. 591 * if there was an associated ipc_action, 592 * ipc_action_finish() is called to complete it. 593 * 594 * o if the ipc_action timer fires before command 595 * completion, then DHCP_IPC_E_TIMEOUT is returned to 596 * the user. however, the transaction continues to 597 * be carried out asynchronously. 598 * 599 * o if async_timeout() fires before command completion, 600 * then if the command was internal to the agent, it 601 * is cancelled. otherwise, if it was a user command, 602 * then if the user is still waiting for the command 603 * to complete, the command continues and async_timeout() 604 * is rescheduled. 605 */ 606 607 switch (cmd) { 608 609 case DHCP_DROP: /* FALLTHRU */ 610 case DHCP_RELEASE: /* FALLTHRU */ 611 case DHCP_EXTEND: /* FALLTHRU */ 612 case DHCP_INFORM: /* FALLTHRU */ 613 case DHCP_START: 614 /* 615 * if shutdown request has been received, send back an error. 616 */ 617 if (shutdown_started) { 618 send_error_reply(request, DHCP_IPC_E_OUTSTATE, &fd); 619 return; 620 } 621 622 if (async_pending(ifsp)) { 623 send_error_reply(request, DHCP_IPC_E_PEND, &fd); 624 return; 625 } 626 627 if (ipc_action_start(ifsp, request, fd) == 0) { 628 dhcpmsg(MSG_WARNING, "ipc_event: ipc_action_start " 629 "failed for %s", ifsp->if_name); 630 send_error_reply(request, DHCP_IPC_E_MEMORY, &fd); 631 return; 632 } 633 634 if (async_start(ifsp, cmd, B_TRUE) == 0) { 635 ipc_action_finish(ifsp, DHCP_IPC_E_MEMORY); 636 return; 637 } 638 break; 639 640 default: 641 break; 642 } 643 644 switch (cmd) { 645 646 case DHCP_DROP: 647 (void) script_start(ifsp, EVENT_DROP, dhcp_drop, NULL, NULL); 648 return; 649 650 case DHCP_EXTEND: 651 (void) dhcp_extending(ifsp); 652 break; 653 654 case DHCP_GET_TAG: { 655 dhcp_optnum_t optnum; 656 DHCP_OPT *opt = NULL; 657 boolean_t did_alloc = B_FALSE; 658 PKT_LIST *ack = ifsp->if_ack; 659 660 /* 661 * verify the request makes sense. 662 */ 663 664 if (request->data_type != DHCP_TYPE_OPTNUM || 665 request->data_length != sizeof (dhcp_optnum_t)) { 666 send_error_reply(request, DHCP_IPC_E_PROTO, &fd); 667 return; 668 } 669 670 (void) memcpy(&optnum, request->buffer, sizeof (dhcp_optnum_t)); 671 load_option: 672 switch (optnum.category) { 673 674 case DSYM_SITE: /* FALLTHRU */ 675 case DSYM_STANDARD: 676 if (optnum.code <= DHCP_LAST_OPT) 677 opt = ack->opts[optnum.code]; 678 break; 679 680 case DSYM_VENDOR: 681 /* 682 * the test against VS_OPTION_START is broken up into 683 * two tests to avoid compiler warnings under intel. 684 */ 685 686 if ((optnum.code > VS_OPTION_START || 687 optnum.code == VS_OPTION_START) && 688 optnum.code <= VS_OPTION_END) 689 opt = ack->vs[optnum.code]; 690 break; 691 692 case DSYM_FIELD: 693 if (optnum.code + optnum.size > sizeof (PKT)) 694 break; 695 696 /* + 2 to account for option code and length byte */ 697 opt = malloc(optnum.size + 2); 698 if (opt == NULL) { 699 send_error_reply(request, DHCP_IPC_E_MEMORY, 700 &fd); 701 return; 702 } 703 704 did_alloc = B_TRUE; 705 opt->len = optnum.size; 706 opt->code = optnum.code; 707 (void) memcpy(&opt->value, (caddr_t)ack->pkt + 708 opt->code, opt->len); 709 710 break; 711 712 default: 713 send_error_reply(request, DHCP_IPC_E_PROTO, &fd); 714 return; 715 } 716 717 /* 718 * return the option payload, if there was one. the "+ 2" 719 * accounts for the option code number and length byte. 720 */ 721 722 if (opt != NULL) { 723 send_data_reply(request, &fd, 0, DHCP_TYPE_OPTION, opt, 724 opt->len + 2); 725 726 if (did_alloc) 727 free(opt); 728 return; 729 } else if (ack != ifsp->if_orig_ack) { 730 /* 731 * There wasn't any definition for the option in the 732 * current ack, so now retry with the original ack if 733 * the original ack is not the current ack. 734 */ 735 ack = ifsp->if_orig_ack; 736 goto load_option; 737 } 738 739 /* 740 * note that an "okay" response is returned either in 741 * the case of an unknown option or a known option 742 * with no payload. this is okay (for now) since 743 * dhcpinfo checks whether an option is valid before 744 * ever performing ipc with the agent. 745 */ 746 747 send_ok_reply(request, &fd); 748 return; 749 } 750 751 case DHCP_INFORM: 752 dhcp_inform(ifsp); 753 /* next destination: dhcp_acknak() */ 754 return; 755 756 case DHCP_PING: 757 if (ifsp->if_dflags & DHCP_IF_FAILED) 758 send_error_reply(request, DHCP_IPC_E_FAILEDIF, &fd); 759 else 760 send_ok_reply(request, &fd); 761 return; 762 763 case DHCP_RELEASE: 764 (void) script_start(ifsp, EVENT_RELEASE, dhcp_release, 765 "Finished with lease.", NULL); 766 return; 767 768 case DHCP_START: 769 (void) canonize_ifs(ifsp); 770 771 /* 772 * if we have a valid hostconf lying around, then jump 773 * into INIT_REBOOT. if it fails, we'll end up going 774 * through the whole selecting() procedure again. 775 */ 776 777 error = read_hostconf(ifsp->if_name, plp, 2); 778 if (error != -1) { 779 ifsp->if_orig_ack = ifsp->if_ack = plp[0]; 780 if (error > 1) { 781 /* 782 * Return indicated we had more than one packet 783 * second one is the original ack. Older 784 * versions of the agent wrote only one ack 785 * to the file, we now keep both the first 786 * ack as well as the last one. 787 */ 788 ifsp->if_orig_ack = plp[1]; 789 } 790 dhcp_init_reboot(ifsp); 791 /* next destination: dhcp_acknak() */ 792 return; 793 } 794 795 /* 796 * if not debugging, wait for a few seconds before 797 * going into SELECTING. 798 */ 799 800 if (debug_level == 0) { 801 if (iu_schedule_timer_ms(tq, 802 lrand48() % DHCP_SELECT_WAIT, dhcp_start, ifsp) 803 != -1) { 804 hold_ifs(ifsp); 805 /* next destination: dhcp_start() */ 806 return; 807 } 808 } 809 810 dhcp_selecting(ifsp); 811 /* next destination: dhcp_requesting() */ 812 return; 813 814 case DHCP_STATUS: { 815 dhcp_status_t status; 816 817 status.if_began = monosec_to_time(ifsp->if_curstart_monosec); 818 819 if (ifsp->if_lease == DHCP_PERM) { 820 status.if_t1 = DHCP_PERM; 821 status.if_t2 = DHCP_PERM; 822 status.if_lease = DHCP_PERM; 823 } else { 824 status.if_t1 = status.if_began + ifsp->if_t1; 825 status.if_t2 = status.if_began + ifsp->if_t2; 826 status.if_lease = status.if_began + ifsp->if_lease; 827 } 828 829 status.version = DHCP_STATUS_VER; 830 status.if_state = ifsp->if_state; 831 status.if_dflags = ifsp->if_dflags; 832 status.if_sent = ifsp->if_sent; 833 status.if_recv = ifsp->if_received; 834 status.if_bad_offers = ifsp->if_bad_offers; 835 836 (void) strlcpy(status.if_name, ifsp->if_name, IFNAMSIZ); 837 838 send_data_reply(request, &fd, 0, DHCP_TYPE_STATUS, &status, 839 sizeof (dhcp_status_t)); 840 return; 841 } 842 843 default: 844 return; 845 } 846 } 847