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 2005 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 <sys/types.h> 30 #include <stdlib.h> 31 #include <assert.h> 32 #include <errno.h> 33 #include <locale.h> 34 #include <string.h> 35 #include <unistd.h> 36 #include <signal.h> 37 #include <stdio.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 /* 220 * create the ipc channel that the agent will listen for 221 * requests on, and register it with the event handler so that 222 * `accept_event' will be called back. 223 */ 224 225 switch (dhcp_ipc_init(&ipc_fd)) { 226 227 case 0: 228 break; 229 230 case DHCP_IPC_E_BIND: 231 dhcpmsg(MSG_ERROR, "dhcp_ipc_init: cannot bind to port " 232 "%i (agent already running?)", IPPORT_DHCPAGENT); 233 return (EXIT_FAILURE); 234 235 default: 236 dhcpmsg(MSG_ERROR, "dhcp_ipc_init failed"); 237 return (EXIT_FAILURE); 238 } 239 240 if (iu_register_event(eh, ipc_fd, POLLIN, accept_event, 0) == -1) { 241 dhcpmsg(MSG_ERR, "cannot register ipc fd for messages"); 242 return (EXIT_FAILURE); 243 } 244 245 /* 246 * if the -a (adopt) option was specified, try to adopt the 247 * kernel-managed interface before we start. Our grandparent 248 * will be waiting for us to finish this, so signal him when 249 * we're done. 250 */ 251 252 if (do_adopt) { 253 int result; 254 255 result = dhcp_adopt(); 256 257 if (grandparent != (pid_t)0) { 258 dhcpmsg(MSG_DEBUG, "adoption complete, signalling " 259 "parent (%i) to exit.", grandparent); 260 (void) kill(grandparent, SIGALRM); 261 } 262 263 if (result == 0) 264 return (EXIT_FAILURE); 265 } 266 267 /* 268 * enter the main event loop; this is where all the real work 269 * takes place (through registering events and scheduling timers). 270 * this function only returns when the agent is shutting down. 271 */ 272 273 switch (iu_handle_events(eh, tq)) { 274 275 case -1: 276 dhcpmsg(MSG_WARNING, "iu_handle_events exited abnormally"); 277 break; 278 279 case DHCP_REASON_INACTIVITY: 280 dhcpmsg(MSG_INFO, "no interfaces to manage, shutting down..."); 281 break; 282 283 case DHCP_REASON_TERMINATE: 284 dhcpmsg(MSG_INFO, "received SIGTERM, shutting down..."); 285 break; 286 287 case DHCP_REASON_SIGNAL: 288 dhcpmsg(MSG_WARNING, "received unexpected signal, shutting " 289 "down..."); 290 break; 291 } 292 293 (void) iu_eh_unregister_signal(eh, SIGTHAW, NULL); 294 295 iu_eh_destroy(eh); 296 iu_tq_destroy(tq); 297 298 return (EXIT_SUCCESS); 299 } 300 301 /* 302 * drain_script(): event loop callback during shutdown 303 * 304 * input: eh_t *: unused 305 * void *: unused 306 * output: boolean_t: B_TRUE if event loop should exit; B_FALSE otherwise 307 */ 308 309 /* ARGSUSED */ 310 boolean_t 311 drain_script(iu_eh_t *ehp, void *arg) 312 { 313 if (shutdown_started == B_FALSE) { 314 shutdown_started = B_TRUE; 315 if (do_adopt == B_FALSE) /* see 4291141 */ 316 nuke_ifslist(B_TRUE); 317 } 318 return (script_count == 0); 319 } 320 321 /* 322 * accept_event(): accepts a new connection on the ipc socket and registers 323 * to receive its messages with the event handler 324 * 325 * input: iu_eh_t *: unused 326 * int: the file descriptor in the iu_eh_t * the connection came in on 327 * (other arguments unused) 328 * output: void 329 */ 330 331 /* ARGSUSED */ 332 static void 333 accept_event(iu_eh_t *ehp, int fd, short events, iu_event_id_t id, void *arg) 334 { 335 int client_fd; 336 int is_priv; 337 338 if (dhcp_ipc_accept(fd, &client_fd, &is_priv) != 0) { 339 dhcpmsg(MSG_ERR, "accept_event: accept on ipc socket"); 340 return; 341 } 342 343 if (iu_register_event(eh, client_fd, POLLIN, ipc_event, 344 (void *)is_priv) == -1) { 345 dhcpmsg(MSG_ERROR, "accept_event: cannot register ipc socket " 346 "for callback"); 347 } 348 } 349 350 /* 351 * ipc_event(): processes incoming ipc requests 352 * 353 * input: iu_eh_t *: unused 354 * int: the file descriptor in the iu_eh_t * the request came in on 355 * short: unused 356 * iu_event_id_t: unused 357 * void *: indicates whether the request is from a privileged client 358 * output: void 359 */ 360 361 /* ARGSUSED */ 362 static void 363 ipc_event(iu_eh_t *ehp, int fd, short events, iu_event_id_t id, void *arg) 364 { 365 dhcp_ipc_request_t *request; 366 struct ifslist *ifsp, *primary_ifsp; 367 int error, is_priv = (int)arg; 368 PKT_LIST *plp[2]; 369 dhcp_ipc_type_t cmd; 370 371 (void) iu_unregister_event(eh, id, NULL); 372 373 if (dhcp_ipc_recv_request(fd, &request, DHCP_IPC_REQUEST_WAIT) != 0) { 374 dhcpmsg(MSG_ERROR, "ipc_event: dhcp_ipc_recv_request failed"); 375 (void) dhcp_ipc_close(fd); 376 return; 377 } 378 379 cmd = DHCP_IPC_CMD(request->message_type); 380 if (cmd >= DHCP_NIPC) { 381 send_error_reply(request, DHCP_IPC_E_CMD_UNKNOWN, &fd); 382 return; 383 } 384 385 /* return EPERM for any of the privileged actions */ 386 387 if (!is_priv) { 388 switch (cmd) { 389 390 case DHCP_STATUS: 391 case DHCP_PING: 392 case DHCP_GET_TAG: 393 break; 394 395 default: 396 dhcpmsg(MSG_WARNING, "ipc_event: privileged ipc " 397 "command (%i) attempted on %s", cmd, 398 request->ifname); 399 400 send_error_reply(request, DHCP_IPC_E_PERM, &fd); 401 return; 402 } 403 } 404 405 /* 406 * try to locate the ifs associated with this command. if the 407 * command is DHCP_START or DHCP_INFORM, then if there isn't 408 * an ifs already, make one (there may already be one from a 409 * previous failed attempt to START or INFORM). otherwise, 410 * verify the interface is still valid. 411 */ 412 413 ifsp = lookup_ifs(request->ifname); 414 415 switch (cmd) { 416 417 case DHCP_START: /* FALLTHRU */ 418 case DHCP_INFORM: 419 /* 420 * it's possible that the interface already exists, but 421 * has been abandoned. usually in those cases we should 422 * return DHCP_IPC_E_UNKIF, but that makes little sense 423 * in the case of "start" or "inform", so just ignore 424 * the abandoned interface and start over anew. 425 */ 426 427 if (ifsp != NULL && verify_ifs(ifsp) == 0) 428 ifsp = NULL; 429 430 /* 431 * as part of initializing the ifs, insert_ifs() 432 * creates a DLPI stream at ifsp->if_dlpi_fd. 433 */ 434 435 if (ifsp == NULL) { 436 ifsp = insert_ifs(request->ifname, B_FALSE, &error); 437 if (ifsp == NULL) { 438 send_error_reply(request, error, &fd); 439 return; 440 } 441 } 442 break; 443 444 default: 445 if (ifsp == NULL) { 446 if (request->ifname[0] == '\0') 447 error = DHCP_IPC_E_NOPRIMARY; 448 else 449 error = DHCP_IPC_E_UNKIF; 450 451 send_error_reply(request, error, &fd); 452 return; 453 } 454 break; 455 } 456 457 if (verify_ifs(ifsp) == 0) { 458 send_error_reply(request, DHCP_IPC_E_UNKIF, &fd); 459 return; 460 } 461 462 if (ifsp->if_dflags & DHCP_IF_BOOTP) { 463 switch (cmd) { 464 465 case DHCP_EXTEND: 466 case DHCP_RELEASE: 467 case DHCP_INFORM: 468 send_error_reply(request, DHCP_IPC_E_BOOTP, &fd); 469 return; 470 471 default: 472 break; 473 } 474 } 475 476 /* 477 * verify that the interface is in a state which will allow the 478 * command. we do this up front so that we can return an error 479 * *before* needlessly cancelling an in-progress transaction. 480 */ 481 482 if (!ipc_cmd_allowed[ifsp->if_state][cmd]) { 483 send_error_reply(request, DHCP_IPC_E_OUTSTATE, &fd); 484 return; 485 } 486 487 if ((request->message_type & DHCP_PRIMARY) && is_priv) { 488 if ((primary_ifsp = lookup_ifs("")) != NULL) 489 primary_ifsp->if_dflags &= ~DHCP_IF_PRIMARY; 490 ifsp->if_dflags |= DHCP_IF_PRIMARY; 491 } 492 493 /* 494 * current design dictates that there can be only one 495 * outstanding transaction per interface -- this simplifies 496 * the code considerably and also fits well with RFC2131. 497 * it is worth classifying the different DHCP commands into 498 * synchronous (those which we will handle now and be done 499 * with) and asynchronous (those which require transactions 500 * and will be completed at an indeterminate time in the 501 * future): 502 * 503 * DROP: removes the agent's management of an interface. 504 * asynchronous as the script program may be invoked. 505 * 506 * PING: checks to see if the agent controls an interface. 507 * synchronous, since no packets need to be sent 508 * to the DHCP server. 509 * 510 * STATUS: returns information about the an interface. 511 * synchronous, since no packets need to be sent 512 * to the DHCP server. 513 * 514 * RELEASE: releases the agent's management of an interface 515 * and brings the interface down. asynchronous as 516 * the script program may be invoked. 517 * 518 * EXTEND: renews a lease. asynchronous, since the agent 519 * needs to wait for an ACK, etc. 520 * 521 * START: starts DHCP on an interface. asynchronous since 522 * the agent needs to wait for OFFERs, ACKs, etc. 523 * 524 * INFORM: obtains configuration parameters for an externally 525 * configured interface. asynchronous, since the 526 * agent needs to wait for an ACK. 527 * 528 * notice that EXTEND, INFORM, START, DROP and RELEASE are 529 * asynchronous. notice also that asynchronous commands may 530 * occur from within the agent -- for instance, the agent 531 * will need to do implicit EXTENDs to extend the lease. in 532 * order to make the code simpler, the following rules apply 533 * for asynchronous commands: 534 * 535 * there can only be one asynchronous command at a time per 536 * interface. the current asynchronous command is managed by 537 * the async_* api: async_start(), async_finish(), 538 * async_timeout(), async_cancel(), and async_pending(). 539 * async_start() starts management of a new asynchronous 540 * command on an interface, which should only be done after 541 * async_pending() is called to check that there are no 542 * pending asynchronous commands on that interface. when the 543 * command is completed, async_finish() should be called. all 544 * asynchronous commands have an associated timer, which calls 545 * async_timeout() when it times out. if async_timeout() 546 * decides that the asynchronous command should be cancelled 547 * (see below), it calls async_cancel() to attempt 548 * cancellation. 549 * 550 * asynchronous commands started by a user command have an 551 * associated ipc_action which provides the agent with 552 * information for how to get in touch with the user command 553 * when the action completes. these ipc_action records also 554 * have an associated timeout which may be infinite. 555 * ipc_action_start() should be called when starting an 556 * asynchronous command requested by a user, which sets up the 557 * timer and keeps track of the ipc information (file 558 * descriptor, request type). when the asynchronous command 559 * completes, ipc_action_finish() should be called to return a 560 * command status code to the user and close the ipc 561 * connection). if the command does not complete before the 562 * timer fires, ipc_action_timeout() is called which closes 563 * the ipc connection and returns DHCP_IPC_E_TIMEOUT to the 564 * user. note that independent of ipc_action_timeout(), 565 * ipc_action_finish() should be called. 566 * 567 * on a case-by-case basis, here is what happens (per interface): 568 * 569 * o when an asynchronous command is requested, then 570 * async_pending() is called to see if there is already 571 * an asynchronous event. if so, the command does not 572 * proceed, and if there is an associated ipc_action, 573 * the user command is sent DHCP_IPC_E_PEND. 574 * 575 * o otherwise, the the transaction is started with 576 * async_start(). if the transaction is on behalf 577 * of a user, ipc_action_start() is called to keep 578 * track of the ipc information and set up the 579 * ipc_action timer. 580 * 581 * o if the command completes normally and before a 582 * timeout fires, then async_finish() is called. 583 * if there was an associated ipc_action, 584 * ipc_action_finish() is called to complete it. 585 * 586 * o if the command fails before a timeout fires, then 587 * async_finish() is called, and the interface is 588 * is returned to a known state based on the command. 589 * if there was an associated ipc_action, 590 * ipc_action_finish() is called to complete it. 591 * 592 * o if the ipc_action timer fires before command 593 * completion, then DHCP_IPC_E_TIMEOUT is returned to 594 * the user. however, the transaction continues to 595 * be carried out asynchronously. 596 * 597 * o if async_timeout() fires before command completion, 598 * then if the command was internal to the agent, it 599 * is cancelled. otherwise, if it was a user command, 600 * then if the user is still waiting for the command 601 * to complete, the command continues and async_timeout() 602 * is rescheduled. 603 */ 604 605 switch (cmd) { 606 607 case DHCP_DROP: /* FALLTHRU */ 608 case DHCP_RELEASE: /* FALLTHRU */ 609 case DHCP_EXTEND: /* FALLTHRU */ 610 case DHCP_INFORM: /* FALLTHRU */ 611 case DHCP_START: 612 /* 613 * if shutdown request has been received, send back an error. 614 */ 615 if (shutdown_started) { 616 send_error_reply(request, DHCP_IPC_E_OUTSTATE, &fd); 617 return; 618 } 619 620 if (async_pending(ifsp)) { 621 send_error_reply(request, DHCP_IPC_E_PEND, &fd); 622 return; 623 } 624 625 if (ipc_action_start(ifsp, request, fd) == 0) { 626 dhcpmsg(MSG_WARNING, "ipc_event: ipc_action_start " 627 "failed for %s", ifsp->if_name); 628 send_error_reply(request, DHCP_IPC_E_MEMORY, &fd); 629 return; 630 } 631 632 if (async_start(ifsp, cmd, B_TRUE) == 0) { 633 ipc_action_finish(ifsp, DHCP_IPC_E_MEMORY); 634 return; 635 } 636 break; 637 638 default: 639 break; 640 } 641 642 switch (cmd) { 643 644 case DHCP_DROP: 645 (void) script_start(ifsp, EVENT_DROP, dhcp_drop, NULL, NULL); 646 return; 647 648 case DHCP_EXTEND: 649 (void) dhcp_extending(ifsp); 650 break; 651 652 case DHCP_GET_TAG: { 653 dhcp_optnum_t optnum; 654 DHCP_OPT *opt = NULL; 655 boolean_t did_alloc = B_FALSE; 656 PKT_LIST *ack = ifsp->if_ack; 657 658 /* 659 * verify the request makes sense. 660 */ 661 662 if (request->data_type != DHCP_TYPE_OPTNUM || 663 request->data_length != sizeof (dhcp_optnum_t)) { 664 send_error_reply(request, DHCP_IPC_E_PROTO, &fd); 665 return; 666 } 667 668 (void) memcpy(&optnum, request->buffer, sizeof (dhcp_optnum_t)); 669 load_option: 670 switch (optnum.category) { 671 672 case DSYM_SITE: /* FALLTHRU */ 673 case DSYM_STANDARD: 674 if (optnum.code <= DHCP_LAST_OPT) 675 opt = ack->opts[optnum.code]; 676 break; 677 678 case DSYM_VENDOR: 679 /* 680 * the test against VS_OPTION_START is broken up into 681 * two tests to avoid compiler warnings under intel. 682 */ 683 684 if ((optnum.code > VS_OPTION_START || 685 optnum.code == VS_OPTION_START) && 686 optnum.code <= VS_OPTION_END) 687 opt = ack->vs[optnum.code]; 688 break; 689 690 case DSYM_FIELD: 691 if (optnum.code + optnum.size > sizeof (PKT)) 692 break; 693 694 /* + 2 to account for option code and length byte */ 695 opt = malloc(optnum.size + 2); 696 if (opt == NULL) { 697 send_error_reply(request, DHCP_IPC_E_MEMORY, 698 &fd); 699 return; 700 } 701 702 did_alloc = B_TRUE; 703 opt->len = optnum.size; 704 opt->code = optnum.code; 705 (void) memcpy(&opt->value, (caddr_t)ack->pkt + 706 opt->code, opt->len); 707 708 break; 709 710 default: 711 send_error_reply(request, DHCP_IPC_E_PROTO, &fd); 712 return; 713 } 714 715 /* 716 * return the option payload, if there was one. the "+ 2" 717 * accounts for the option code number and length byte. 718 */ 719 720 if (opt != NULL) { 721 send_data_reply(request, &fd, 0, DHCP_TYPE_OPTION, opt, 722 opt->len + 2); 723 724 if (did_alloc) 725 free(opt); 726 return; 727 } else if (ack != ifsp->if_orig_ack) { 728 /* 729 * There wasn't any definition for the option in the 730 * current ack, so now retry with the original ack if 731 * the original ack is not the current ack. 732 */ 733 ack = ifsp->if_orig_ack; 734 goto load_option; 735 } 736 737 /* 738 * note that an "okay" response is returned either in 739 * the case of an unknown option or a known option 740 * with no payload. this is okay (for now) since 741 * dhcpinfo checks whether an option is valid before 742 * ever performing ipc with the agent. 743 */ 744 745 send_ok_reply(request, &fd); 746 return; 747 } 748 749 case DHCP_INFORM: 750 dhcp_inform(ifsp); 751 /* next destination: dhcp_acknak() */ 752 return; 753 754 case DHCP_PING: 755 if (ifsp->if_dflags & DHCP_IF_FAILED) 756 send_error_reply(request, DHCP_IPC_E_FAILEDIF, &fd); 757 else 758 send_ok_reply(request, &fd); 759 return; 760 761 case DHCP_RELEASE: 762 (void) script_start(ifsp, EVENT_RELEASE, dhcp_release, 763 "Finished with lease.", NULL); 764 return; 765 766 case DHCP_START: 767 assert(ifsp->if_state == INIT); 768 (void) canonize_ifs(ifsp); 769 770 /* 771 * if we have a valid hostconf lying around, then jump 772 * into INIT_REBOOT. if it fails, we'll end up going 773 * through the whole selecting() procedure again. 774 */ 775 776 error = read_hostconf(ifsp->if_name, plp, 2); 777 if (error != -1) { 778 ifsp->if_orig_ack = ifsp->if_ack = plp[0]; 779 if (error > 1) { 780 /* 781 * Return indicated we had more than one packet 782 * second one is the original ack. Older 783 * versions of the agent wrote only one ack 784 * to the file, we now keep both the first 785 * ack as well as the last one. 786 */ 787 ifsp->if_orig_ack = plp[1]; 788 } 789 dhcp_init_reboot(ifsp); 790 /* next destination: dhcp_acknak() */ 791 return; 792 } 793 794 /* 795 * if not debugging, wait for a few seconds before 796 * going into SELECTING. 797 */ 798 799 if (debug_level == 0) { 800 if (iu_schedule_timer_ms(tq, 801 lrand48() % DHCP_SELECT_WAIT, dhcp_start, ifsp) 802 != -1) { 803 hold_ifs(ifsp); 804 /* next destination: dhcp_start() */ 805 return; 806 } 807 } 808 809 dhcp_selecting(ifsp); 810 /* next destination: dhcp_requesting() */ 811 return; 812 813 case DHCP_STATUS: { 814 dhcp_status_t status; 815 816 status.if_began = monosec_to_time(ifsp->if_curstart_monosec); 817 818 if (ifsp->if_lease == DHCP_PERM) { 819 status.if_t1 = DHCP_PERM; 820 status.if_t2 = DHCP_PERM; 821 status.if_lease = DHCP_PERM; 822 } else { 823 status.if_t1 = status.if_began + ifsp->if_t1; 824 status.if_t2 = status.if_began + ifsp->if_t2; 825 status.if_lease = status.if_began + ifsp->if_lease; 826 } 827 828 status.version = DHCP_STATUS_VER; 829 status.if_state = ifsp->if_state; 830 status.if_dflags = ifsp->if_dflags; 831 status.if_sent = ifsp->if_sent; 832 status.if_recv = ifsp->if_received; 833 status.if_bad_offers = ifsp->if_bad_offers; 834 835 (void) strlcpy(status.if_name, ifsp->if_name, IFNAMSIZ); 836 837 send_data_reply(request, &fd, 0, DHCP_TYPE_STATUS, &status, 838 sizeof (dhcp_status_t)); 839 return; 840 } 841 842 default: 843 return; 844 } 845 } 846