Exemplo n.º 1
0
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);
}
Exemplo n.º 2
0
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);
}
Exemplo n.º 3
0
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);
}