Exemplo n.º 1
0
boolean_t
schedule_timer(dhcp_timer_t *dt, iu_tq_callback_t *cbfunc, void *arg)
{
	if (dt->dt_id != -1)
		return (B_FALSE);
	dt->dt_id = iu_schedule_timer(tq, dt->dt_start, cbfunc, arg);
	return (dt->dt_id != -1);
}
Exemplo n.º 2
0
boolean_t
ipc_action_start(dhcp_smach_t *dsmp, ipc_action_t *iareq)
{
	struct ipc_action *ia = &dsmp->dsm_ia;

	if (ia->ia_fd != -1 || ia->ia_tid != -1 || iareq->ia_fd == -1) {
		dhcpmsg(MSG_CRIT, "ipc_action_start: attempted restart on %s",
		    dsmp->dsm_name);
		return (B_FALSE);
	}

	if (!async_cancel(dsmp)) {
		dhcpmsg(MSG_WARNING, "ipc_action_start: unable to cancel "
		    "action on %s", dsmp->dsm_name);
		return (B_FALSE);
	}

	if (iareq->ia_request->timeout == DHCP_IPC_WAIT_DEFAULT)
		iareq->ia_request->timeout = DHCP_IPC_DEFAULT_WAIT;

	if (iareq->ia_request->timeout == DHCP_IPC_WAIT_FOREVER) {
		iareq->ia_tid = -1;
	} else {
		iareq->ia_tid = iu_schedule_timer(tq,
		    iareq->ia_request->timeout, ipc_action_timeout, dsmp);

		if (iareq->ia_tid == -1) {
			dhcpmsg(MSG_ERROR, "ipc_action_start: failed to set "
			    "timer for %s on %s",
			    dhcp_ipc_type_to_string(iareq->ia_cmd),
			    dsmp->dsm_name);
			return (B_FALSE);
		}

		hold_smach(dsmp);
	}

	*ia = *iareq;

	/* We've taken ownership, so the input request is now invalid */
	ipc_action_init(iareq);

	dhcpmsg(MSG_DEBUG, "ipc_action_start: started %s (command %d) on %s,"
	    " buffer length %u",
	    dhcp_ipc_type_to_string(ia->ia_cmd), ia->ia_cmd, dsmp->dsm_name,
	    ia->ia_request == NULL ? 0 : ia->ia_request->data_length);

	dsmp->dsm_dflags |= DHCP_IF_BUSY;

	/* This cannot fail due to the async_cancel above */
	(void) async_start(dsmp, ia->ia_cmd, B_TRUE);

	return (B_TRUE);
}
Exemplo n.º 3
0
/* ARGSUSED */
void
dhcp_expire(iu_tq_t *tqp, void *arg)
{
	struct ifslist	*ifsp = (struct ifslist *)arg;

	ifsp->if_timer[DHCP_LEASE_TIMER] = -1;

	if (check_ifs(ifsp) == 0) {
		(void) release_ifs(ifsp);
		return;
	}

	if (async_pending(ifsp))

		if (async_cancel(ifsp) == 0) {

			dhcpmsg(MSG_WARNING, "dhcp_expire: cannot cancel "
			    "current asynchronous command against %s",
			    ifsp->if_name);

			/*
			 * try to schedule ourselves for callback.
			 * we're really situation critical here
			 * there's not much hope for us if this fails.
			 */

			if (iu_schedule_timer(tq, DHCP_EXPIRE_WAIT, dhcp_expire,
			    ifsp) != -1) {
				hold_ifs(ifsp);
				return;
			}

			dhcpmsg(MSG_CRIT, "dhcp_expire: cannot reschedule "
			    "dhcp_expire to get called back, proceeding...");
		}

	/*
	 * just march on if this fails; at worst someone will be able
	 * to async_start() while we're actually busy with our own
	 * asynchronous transaction.  better than not having a lease.
	 */

	if (async_start(ifsp, DHCP_START, B_FALSE) == 0)
		dhcpmsg(MSG_WARNING, "dhcp_expire: cannot start asynchronous "
		    "transaction on %s, continuing...", ifsp->if_name);

	(void) script_start(ifsp, EVENT_EXPIRE, dhcp_restart_lease, NULL, NULL);
}
Exemplo n.º 4
0
int
main(int argc, char **argv)
{
	boolean_t	is_daemon  = B_TRUE;
	boolean_t	is_verbose = B_FALSE;
	int		ipc_fd;
	int		c;
	struct rlimit	rl;

	/*
	 * -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);
	}

	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_ifslist, 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 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.
	 */
	rtsock_fd = socket(PF_ROUTE, SOCK_RAW, AF_INET);
	if (rtsock_fd == -1) {
		dhcpmsg(MSG_ERR, "cannot open 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() == 0)
		return (EXIT_FAILURE);

	/*
	 * 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);
}
Exemplo n.º 5
0
void
dhcp_requesting(iu_tq_t *tqp, void *arg)
{
	dhcp_smach_t		*dsmp = arg;
	dhcp_pkt_t		*dpkt;
	PKT_LIST		*offer;
	lease_t			lease;
	boolean_t		isv6 = dsmp->dsm_isv6;

	/*
	 * We assume here that if tqp is set, then this means we're being
	 * called back by the offer wait timer.  If so, then drop our hold
	 * on the state machine.  Otherwise, cancel the timer if it's running.
	 */
	if (tqp != NULL) {
		dhcpmsg(MSG_VERBOSE,
		    "dhcp_requesting: offer wait timer on v%d %s",
		    isv6 ? 6 : 4, dsmp->dsm_name);
		dsmp->dsm_offer_timer = -1;
		if (!verify_smach(dsmp))
			return;
	} else {
		cancel_offer_timer(dsmp);
	}

	/*
	 * select the best OFFER; all others pitched.
	 */

	offer = select_best(dsmp);
	if (offer == NULL) {

		dhcpmsg(MSG_VERBOSE,
		    "no OFFERs/Advertisements on %s, waiting...",
		    dsmp->dsm_name);

		/*
		 * no acceptable OFFERs have come in.  reschedule
		 * ourself for callback.
		 */

		if ((dsmp->dsm_offer_timer = iu_schedule_timer(tq,
		    dsmp->dsm_offer_wait, dhcp_requesting, dsmp)) == -1) {

			/*
			 * ugh.  the best we can do at this point is
			 * revert back to INIT and wait for a user to
			 * restart us.
			 */

			dhcpmsg(MSG_WARNING, "dhcp_requesting: cannot "
			    "reschedule callback, reverting to INIT state on "
			    "%s", dsmp->dsm_name);

			stop_pkt_retransmission(dsmp);
			(void) set_smach_state(dsmp, INIT);
			dsmp->dsm_dflags |= DHCP_IF_FAILED;
			ipc_action_finish(dsmp, DHCP_IPC_E_MEMORY);
		} else {
			hold_smach(dsmp);
		}

		return;
	}

	/*
	 * With IPv4, the DHCPREQUEST packet we're about to transmit implicitly
	 * declines all other offers we've received.  We can no longer use any
	 * cached offers, so we must discard them now.  With DHCPv6, though,
	 * we're permitted to hang onto the advertisements (offers) and try
	 * them if the preferred one doesn't pan out.
	 */
	if (!isv6)
		free_pkt_list(&dsmp->dsm_recv_pkt_list);

	/* stop collecting packets. */

	stop_pkt_retransmission(dsmp);

	/*
	 * For IPv4, check to see whether we got an OFFER or a BOOTP packet.
	 * If we got a BOOTP packet, go to the BOUND state now.
	 */
	if (!isv6 && offer->opts[CD_DHCP_TYPE] == NULL) {
		free_pkt_list(&dsmp->dsm_recv_pkt_list);

		if (!set_smach_state(dsmp, REQUESTING)) {
			dhcp_restart(dsmp);
			return;
		}

		if (!dhcp_bound(dsmp, offer)) {
			dhcpmsg(MSG_WARNING, "dhcp_requesting: dhcp_bound "
			    "failed for %s", dsmp->dsm_name);
			dhcp_restart(dsmp);
			return;
		}

		return;
	}

	if (isv6) {
		const char *estr, *msg;
		const dhcpv6_option_t *d6o;
		uint_t olen, msglen;

		/* If there's a Status Code option, print the message */
		d6o = dhcpv6_pkt_option(offer, NULL, DHCPV6_OPT_STATUS_CODE,
		    &olen);
		(void) dhcpv6_status_code(d6o, olen, &estr, &msg, &msglen);
		print_server_msg(dsmp, msg, msglen);

		/* Copy in the Server ID (guaranteed to be present now) */
		if (!save_server_id(dsmp, offer))
			goto failure;

		/*
		 * Determine how to send this message.  If the Advertisement
		 * (offer) has the unicast option, then use the address
		 * specified in the option.  Otherwise, send via multicast.
		 */
		server_unicast_option(dsmp, offer);

		send_v6_request(dsmp);
	} else {
		/* if we got a message from the server, display it. */
		if (offer->opts[CD_MESSAGE] != NULL) {
			print_server_msg(dsmp,
			    (char *)offer->opts[CD_MESSAGE]->value,
			    offer->opts[CD_MESSAGE]->len);
		}

		/*
		 * assemble a DHCPREQUEST, with the ciaddr field set to 0,
		 * since we got here from the INIT state.
		 */

		dpkt = init_pkt(dsmp, REQUEST);

		/*
		 * Grab the lease out of the OFFER; we know it's valid because
		 * select_best() already checked.  The max dhcp message size
		 * option is set to the interface max, minus the size of the
		 * udp and ip headers.
		 */

		(void) memcpy(&lease, offer->opts[CD_LEASE_TIME]->value,
		    sizeof (lease_t));

		(void) add_pkt_opt32(dpkt, CD_LEASE_TIME, lease);
		(void) add_pkt_opt16(dpkt, CD_MAX_DHCP_SIZE,
		    htons(dsmp->dsm_lif->lif_max - sizeof (struct udpiphdr)));
		(void) add_pkt_opt32(dpkt, CD_REQUESTED_IP_ADDR,
		    offer->pkt->yiaddr.s_addr);
		(void) add_pkt_opt(dpkt, CD_SERVER_ID,
		    offer->opts[CD_SERVER_ID]->value,
		    offer->opts[CD_SERVER_ID]->len);

		if (class_id_len != 0) {
			(void) add_pkt_opt(dpkt, CD_CLASS_ID, class_id,
			    class_id_len);
		}
		(void) add_pkt_prl(dpkt, dsmp);

		/*
		 * dsm_reqhost was set for this state machine in
		 * dhcp_selecting() if the DF_REQUEST_HOSTNAME option set and a
		 * host name was found
		 */
		if (dsmp->dsm_reqhost != NULL) {
			(void) add_pkt_opt(dpkt, CD_HOSTNAME, dsmp->dsm_reqhost,
			    strlen(dsmp->dsm_reqhost));
		}
		(void) add_pkt_opt(dpkt, CD_END, NULL, 0);

		/*
		 * send out the REQUEST, trying retransmissions.  either a NAK
		 * or too many REQUEST attempts will revert us to SELECTING.
		 */

		if (!set_smach_state(dsmp, REQUESTING)) {
			dhcpmsg(MSG_ERROR, "dhcp_requesting: cannot switch to "
			    "REQUESTING state; reverting to INIT on %s",
			    dsmp->dsm_name);
			goto failure;
		}

		(void) send_pkt(dsmp, dpkt, htonl(INADDR_BROADCAST),
		    stop_requesting);
	}

	/* all done with the offer */
	free_pkt_entry(offer);

	return;

failure:
	dsmp->dsm_dflags |= DHCP_IF_FAILED;
	(void) set_smach_state(dsmp, INIT);
	ipc_action_finish(dsmp, DHCP_IPC_E_MEMORY);
	free_pkt_list(&dsmp->dsm_recv_pkt_list);
}
Exemplo n.º 6
0
void
dhcp_selecting(dhcp_smach_t *dsmp)
{
	dhcp_pkt_t		*dpkt;

	/*
	 * We first set up to collect OFFER/Advertise packets as they arrive.
	 * We then send out DISCOVER/Solicit probes.  Then we wait a
	 * user-tunable number of seconds before seeing if OFFERs/
	 * Advertisements have come in response to our DISCOVER/Solicit.  If
	 * none have come in, we continue to wait, sending out our DISCOVER/
	 * Solicit probes with exponential backoff.  If no OFFER/Advertisement
	 * is ever received, we will wait forever (note that since we're
	 * event-driven though, we're still able to service other state
	 * machines).
	 *
	 * Note that we do an reset_smach() here because we may be landing in
	 * dhcp_selecting() as a result of restarting DHCP, so the state
	 * machine may not be fresh.
	 */

	reset_smach(dsmp);
	if (!set_smach_state(dsmp, SELECTING)) {
		dhcpmsg(MSG_ERROR,
		    "dhcp_selecting: cannot switch to SELECTING state; "
		    "reverting to INIT on %s", dsmp->dsm_name);
		goto failed;

	}

	/* Remove the stale hostconf file, if there is any */
	(void) remove_hostconf(dsmp->dsm_name, dsmp->dsm_isv6);

	dsmp->dsm_offer_timer = iu_schedule_timer(tq,
	    dsmp->dsm_offer_wait, dhcp_requesting, dsmp);
	if (dsmp->dsm_offer_timer == -1) {
		dhcpmsg(MSG_ERROR, "dhcp_selecting: cannot schedule to read "
		    "%s packets", dsmp->dsm_isv6 ? "Advertise" : "OFFER");
		goto failed;
	}

	hold_smach(dsmp);

	/*
	 * Assemble and send the DHCPDISCOVER or Solicit message.
	 *
	 * If this fails, we'll wait for the select timer to go off
	 * before trying again.
	 */
	if (dsmp->dsm_isv6) {
		dhcpv6_ia_na_t d6in;

		if ((dpkt = init_pkt(dsmp, DHCPV6_MSG_SOLICIT)) == NULL) {
			dhcpmsg(MSG_ERROR, "dhcp_selecting: unable to set up "
			    "Solicit packet");
			return;
		}

		/* Add an IA_NA option for our controlling LIF */
		d6in.d6in_iaid = htonl(dsmp->dsm_lif->lif_iaid);
		d6in.d6in_t1 = htonl(0);
		d6in.d6in_t2 = htonl(0);
		(void) add_pkt_opt(dpkt, DHCPV6_OPT_IA_NA,
		    (dhcpv6_option_t *)&d6in + 1,
		    sizeof (d6in) - sizeof (dhcpv6_option_t));

		/* Option Request option for desired information */
		(void) add_pkt_prl(dpkt, dsmp);

		/* Enable Rapid-Commit */
		(void) add_pkt_opt(dpkt, DHCPV6_OPT_RAPID_COMMIT, NULL, 0);

		/* xxx add Reconfigure Accept */

		(void) send_pkt_v6(dsmp, dpkt, ipv6_all_dhcp_relay_and_servers,
		    stop_selecting, DHCPV6_SOL_TIMEOUT, DHCPV6_SOL_MAX_RT);
	} else {
		if ((dpkt = init_pkt(dsmp, DISCOVER)) == NULL) {
			dhcpmsg(MSG_ERROR, "dhcp_selecting: unable to set up "
			    "DISCOVER packet");
			return;
		}

		/*
		 * The max DHCP message size option is set to the interface
		 * MTU, minus the size of the UDP and IP headers.
		 */
		(void) add_pkt_opt16(dpkt, CD_MAX_DHCP_SIZE,
		    htons(dsmp->dsm_lif->lif_max - sizeof (struct udpiphdr)));
		(void) add_pkt_opt32(dpkt, CD_LEASE_TIME, htonl(DHCP_PERM));

		if (class_id_len != 0) {
			(void) add_pkt_opt(dpkt, CD_CLASS_ID, class_id,
			    class_id_len);
		}
		(void) add_pkt_prl(dpkt, dsmp);

		if (!dhcp_add_fqdn_opt(dpkt, dsmp))
			(void) dhcp_add_hostname_opt(dpkt, dsmp);

		(void) add_pkt_opt(dpkt, CD_END, NULL, 0);

		(void) send_pkt(dsmp, dpkt, htonl(INADDR_BROADCAST),
		    stop_selecting);
	}
	return;

failed:
	(void) set_smach_state(dsmp, INIT);
	dsmp->dsm_dflags |= DHCP_IF_FAILED;
	ipc_action_finish(dsmp, DHCP_IPC_E_MEMORY);
}
Exemplo n.º 7
0
/*
 * Registers the attributes of a running method passed as arguments so that
 * the method's termination is noticed and any further processing of the
 * associated instance is carried out. The function also sets up any
 * necessary timers so we can detect hung methods.
 * Returns -1 if either it failed to open the /proc psinfo file which is used
 * to monitor the method process, it failed to setup a required timer or
 * memory allocation failed; else 0.
 */
int
register_method(instance_t *ins, pid_t pid, ctid_t cid, instance_method_t mthd,
    char *proto_name)
{
	char		path[MAXPATHLEN];
	int		fd;
	method_el_t	*me;

	/* open /proc psinfo file of process to listen for POLLHUP events on */
	(void) snprintf(path, sizeof (path), "/proc/%u/psinfo", pid);
	for (;;) {
		if ((fd = open(path, O_RDONLY)) >= 0) {
			break;
		} else if (errno != EINTR) {
			/*
			 * Don't output an error for ENOENT; we get this
			 * if a method has gone away whilst we were stopped,
			 * and we're now trying to re-listen for it.
			 */
			if (errno != ENOENT) {
				error_msg(gettext("Failed to open %s: %s"),
				    path, strerror(errno));
			}
			return (-1);
		}
	}

	/* add method record to in-memory list */
	if ((me = calloc(1, sizeof (method_el_t))) == NULL) {
		error_msg(strerror(errno));
		(void) close(fd);
		return (-1);
	}
	me->fd = fd;
	me->inst = (instance_t *)ins;
	me->method = mthd;
	me->pid = pid;
	me->cid = cid;
	if (proto_name != NULL) {
		if ((me->proto_name = strdup(proto_name)) == NULL) {
			error_msg(strerror(errno));
			free(me);
			(void) close(fd);
			return (-1);
		}
	} else
		me->proto_name = NULL;

	/* register a timeout for the method, if required */
	if (mthd != IM_START) {
		method_info_t *mi = ins->config->methods[mthd];

		if (mi->timeout > 0) {
			assert(ins->timer_id == -1);
			ins->timer_id = iu_schedule_timer(timer_queue,
			    mi->timeout, method_timeout, me);
			if (ins->timer_id == -1) {
				error_msg(gettext(
				    "Failed to schedule method timeout"));
				if (me->proto_name != NULL)
					free(me->proto_name);
				free(me);
				(void) close(fd);
				return (-1);
			}
		}
	}

	/*
	 * Add fd of psinfo file to poll set, but pass 0 for events to
	 * poll for, so we should only get a POLLHUP event on the fd.
	 */
	if (set_pollfd(fd, 0) == -1) {
		cancel_inst_timer(ins);
		if (me->proto_name != NULL)
			free(me->proto_name);
		free(me);
		(void) close(fd);
		return (-1);
	}

	uu_list_node_init(me, &me->link, method_pool);
	(void) uu_list_insert_after(method_list, NULL, me);

	return (0);
}