static boolean_t stop_init_reboot(dhcp_smach_t *dsmp, unsigned int n_requests) { if (dsmp->dsm_isv6) { uint_t nowabs, maxabs; nowabs = gethrtime() / (NANOSEC / MILLISEC); maxabs = dsmp->dsm_neg_hrtime / (NANOSEC / MILLISEC) + DHCPV6_CNF_MAX_RD; if (nowabs < maxabs) { /* Cap the timer based on the maximum */ if (nowabs + dsmp->dsm_send_timeout > maxabs) dsmp->dsm_send_timeout = maxabs - nowabs; return (B_FALSE); } } else { if (n_requests < DHCP_MAX_REQUESTS) return (B_FALSE); } if (df_get_bool(dsmp->dsm_name, dsmp->dsm_isv6, DF_VERIFIED_LEASE_ONLY)) { dhcpmsg(MSG_INFO, "unable to verify existing lease on %s; restarting", dsmp->dsm_name); dhcp_selecting(dsmp); return (B_TRUE); } if (dsmp->dsm_isv6) { dhcpmsg(MSG_INFO, "no Reply to Confirm, using remainder of " "existing lease on %s", dsmp->dsm_name); } else { dhcpmsg(MSG_INFO, "no ACK/NAK to INIT_REBOOT REQUEST, " "using remainder of existing lease on %s", dsmp->dsm_name); } /* * We already stuck our old ack in dsmp->dsm_ack and relativized the * packet times, so we can just pretend that the server sent it to us * and move to bound. If that fails, fall back to selecting. */ if (dhcp_bound(dsmp, NULL)) { if (dsmp->dsm_isv6) { if (!save_server_id(dsmp, dsmp->dsm_ack)) goto failure; server_unicast_option(dsmp, dsmp->dsm_ack); } } else { failure: dhcpmsg(MSG_INFO, "unable to use saved lease on %s; restarting", dsmp->dsm_name); dhcp_selecting(dsmp); } return (B_TRUE); }
static void dhcp_init_reboot_v4(dhcp_smach_t *dsmp) { dhcp_pkt_t *dpkt; const char *reqhost; char hostfile[PATH_MAX + 1]; /* * assemble DHCPREQUEST message. The max dhcp message size * option is set to the interface max, minus the size of the udp and * ip headers. */ dpkt = init_pkt(dsmp, REQUEST); (void) add_pkt_opt32(dpkt, CD_REQUESTED_IP_ADDR, dsmp->dsm_ack->pkt->yiaddr.s_addr); (void) add_pkt_opt32(dpkt, CD_LEASE_TIME, htonl(DHCP_PERM)); (void) add_pkt_opt16(dpkt, CD_MAX_DHCP_SIZE, htons(dsmp->dsm_lif->lif_pif->pif_max - sizeof (struct udpiphdr))); if (class_id_len != 0) (void) add_pkt_opt(dpkt, CD_CLASS_ID, class_id, class_id_len); (void) add_pkt_prl(dpkt, dsmp); /* * Set CD_HOSTNAME option if REQUEST_HOSTNAME is set and a hostname * is found in /etc/hostname.<ifname> */ if (df_get_bool(dsmp->dsm_name, dsmp->dsm_isv6, DF_REQUEST_HOSTNAME)) { (void) snprintf(hostfile, sizeof (hostfile), "/etc/hostname.%s", dsmp->dsm_name); if ((reqhost = iffile_to_hostname(hostfile)) != NULL) { dhcpmsg(MSG_DEBUG, "dhcp_selecting: host %s", reqhost); if ((dsmp->dsm_reqhost = strdup(reqhost)) != NULL) (void) add_pkt_opt(dpkt, CD_HOSTNAME, dsmp->dsm_reqhost, strlen(dsmp->dsm_reqhost)); else dhcpmsg(MSG_WARNING, "dhcp_selecting: cannot" " allocate memory for host name option"); } else { dhcpmsg(MSG_DEBUG, "dhcp_selecting: no hostname for %s", dsmp->dsm_name); } } (void) add_pkt_opt(dpkt, CD_END, NULL, 0); (void) send_pkt(dsmp, dpkt, htonl(INADDR_BROADCAST), stop_init_reboot); }
int main(int argc, char **argv) { boolean_t is_daemon = B_TRUE; boolean_t is_verbose; int ipc_fd; int c; int aware = RTAW_UNDER_IPMP; struct rlimit rl; debug_level = df_get_int("", B_FALSE, DF_DEBUG_LEVEL); is_verbose = df_get_bool("", B_FALSE, DF_VERBOSE); /* * -l is ignored for compatibility with old agent. */ while ((c = getopt(argc, argv, "vd:l:fa")) != EOF) { switch (c) { case 'a': do_adopt = B_TRUE; grandparent = getpid(); break; case 'd': debug_level = strtoul(optarg, NULL, 0); break; case 'f': is_daemon = B_FALSE; break; case 'v': is_verbose = B_TRUE; break; case '?': (void) fprintf(stderr, "usage: %s [-a] [-d n] [-f] [-v]" "\n", argv[0]); return (EXIT_FAILURE); default: break; } } (void) setlocale(LC_ALL, ""); (void) textdomain(TEXT_DOMAIN); if (geteuid() != 0) { dhcpmsg_init(argv[0], B_FALSE, is_verbose, debug_level); dhcpmsg(MSG_ERROR, "must be super-user"); dhcpmsg_fini(); return (EXIT_FAILURE); } if (is_daemon && daemonize() == 0) { dhcpmsg_init(argv[0], B_FALSE, is_verbose, debug_level); dhcpmsg(MSG_ERR, "cannot become daemon, exiting"); dhcpmsg_fini(); return (EXIT_FAILURE); } /* * Seed the random number generator, since we're going to need it * to set transaction id's and for exponential backoff. */ srand48(gethrtime() ^ gethostid() ^ getpid()); dhcpmsg_init(argv[0], is_daemon, is_verbose, debug_level); (void) atexit(dhcpmsg_fini); tq = iu_tq_create(); eh = iu_eh_create(); if (eh == NULL || tq == NULL) { errno = ENOMEM; dhcpmsg(MSG_ERR, "cannot create timer queue or event handler"); return (EXIT_FAILURE); } /* * ignore most signals that could be reasonably generated. */ (void) signal(SIGTERM, graceful_shutdown); (void) signal(SIGQUIT, graceful_shutdown); (void) signal(SIGPIPE, SIG_IGN); (void) signal(SIGUSR1, SIG_IGN); (void) signal(SIGUSR2, SIG_IGN); (void) signal(SIGINT, SIG_IGN); (void) signal(SIGHUP, SIG_IGN); (void) signal(SIGCHLD, SIG_IGN); /* * upon SIGTHAW we need to refresh any non-infinite leases. */ (void) iu_eh_register_signal(eh, SIGTHAW, refresh_smachs, NULL); class_id = get_class_id(); if (class_id != NULL) class_id_len = strlen(class_id); else dhcpmsg(MSG_WARNING, "get_class_id failed, continuing " "with no vendor class id"); /* * the inactivity timer is enabled any time there are no * interfaces under DHCP control. if DHCP_INACTIVITY_WAIT * seconds transpire without an interface under DHCP control, * the agent shuts down. */ inactivity_id = iu_schedule_timer(tq, DHCP_INACTIVITY_WAIT, inactivity_shutdown, NULL); /* * max out the number available descriptors, just in case.. */ rl.rlim_cur = RLIM_INFINITY; rl.rlim_max = RLIM_INFINITY; if (setrlimit(RLIMIT_NOFILE, &rl) == -1) dhcpmsg(MSG_ERR, "setrlimit failed"); (void) enable_extended_FILE_stdio(-1, -1); /* * Create and bind default IP sockets used to control interfaces and to * catch stray packets. */ if (!dhcp_ip_default()) return (EXIT_FAILURE); /* * create the ipc channel that the agent will listen for * requests on, and register it with the event handler so that * `accept_event' will be called back. */ switch (dhcp_ipc_init(&ipc_fd)) { case 0: break; case DHCP_IPC_E_BIND: dhcpmsg(MSG_ERROR, "dhcp_ipc_init: cannot bind to port " "%i (agent already running?)", IPPORT_DHCPAGENT); return (EXIT_FAILURE); default: dhcpmsg(MSG_ERROR, "dhcp_ipc_init failed"); return (EXIT_FAILURE); } if (iu_register_event(eh, ipc_fd, POLLIN, accept_event, 0) == -1) { dhcpmsg(MSG_ERR, "cannot register ipc fd for messages"); return (EXIT_FAILURE); } /* * Create the global routing socket. This is used for monitoring * interface transitions, so that we learn about the kernel's Duplicate * Address Detection status, and for inserting and removing default * routes as learned from DHCP servers. Both v4 and v6 are handed * with this one socket. */ rtsock_fd = socket(PF_ROUTE, SOCK_RAW, 0); if (rtsock_fd == -1) { dhcpmsg(MSG_ERR, "cannot open routing socket"); return (EXIT_FAILURE); } /* * We're IPMP-aware and can manage IPMP test addresses, so issue * RT_AWARE to get routing socket messages for interfaces under IPMP. */ if (setsockopt(rtsock_fd, SOL_ROUTE, RT_AWARE, &aware, sizeof (aware)) == -1) { dhcpmsg(MSG_ERR, "cannot set RT_AWARE on routing socket"); return (EXIT_FAILURE); } if (iu_register_event(eh, rtsock_fd, POLLIN, rtsock_event, 0) == -1) { dhcpmsg(MSG_ERR, "cannot register routing socket for messages"); return (EXIT_FAILURE); } /* * if the -a (adopt) option was specified, try to adopt the * kernel-managed interface before we start. */ if (do_adopt && !dhcp_adopt()) return (EXIT_FAILURE); /* * For DHCPv6, we own all of the interfaces marked DHCPRUNNING. As * we're starting operation here, if there are any of those interfaces * lingering around, they're strays, and need to be removed. * * It might be nice to save these addresses off somewhere -- for both * v4 and v6 -- and use them as hints for later negotiation. */ remove_v6_strays(); /* * enter the main event loop; this is where all the real work * takes place (through registering events and scheduling timers). * this function only returns when the agent is shutting down. */ switch (iu_handle_events(eh, tq)) { case -1: dhcpmsg(MSG_WARNING, "iu_handle_events exited abnormally"); break; case DHCP_REASON_INACTIVITY: dhcpmsg(MSG_INFO, "no interfaces to manage, shutting down..."); break; case DHCP_REASON_TERMINATE: dhcpmsg(MSG_INFO, "received SIGTERM, shutting down..."); break; case DHCP_REASON_SIGNAL: dhcpmsg(MSG_WARNING, "received unexpected signal, shutting " "down..."); break; } (void) iu_eh_unregister_signal(eh, SIGTHAW, NULL); iu_eh_destroy(eh); iu_tq_destroy(tq); return (EXIT_SUCCESS); }