Beispiel #1
0
static PKT_LIST *
select_best(dhcp_smach_t *dsmp)
{
	PKT_LIST	*current = dsmp->dsm_recv_pkt_list;
	PKT_LIST	*next, *best = NULL;
	int		points, best_points = -1;

	/*
	 * pick out the best offer.  point system.
	 * what's important for IPv4?
	 *
	 *	0) DHCP (30 points)
	 *	1) no option overload
	 *	2) encapsulated vendor option (80 points)
	 *	3) non-null sname and siaddr fields
	 *	4) non-null file field
	 *	5) hostname (5 points)
	 *	6) subnetmask (1 point)
	 *	7) router (1 point)
	 */

	for (; current != NULL; current = next) {
		next = current->next;

		points = current->isv6 ?
		    compute_points_v6(current, dsmp) :
		    compute_points_v4(current);

		/*
		 * Just discard any unacceptable entries we encounter.
		 */
		if (points == -1) {
			remque(current);
			free_pkt_entry(current);
			continue;
		}

		dhcpmsg(MSG_DEBUG, "select_best: OFFER had %d points", points);

		/* Special case: stop now and select */
		if (points == -2) {
			best = current;
			break;
		}

		if (points >= best_points) {
			best_points = points;
			best = current;
		}
	}

	if (best != NULL) {
		dhcpmsg(MSG_DEBUG, "select_best: most points: %d", best_points);
		remque(best);
	} else {
		dhcpmsg(MSG_DEBUG, "select_best: no valid OFFER/BOOTP reply");
	}

	return (best);
}
Beispiel #2
0
void
dhcp_restart(dhcp_smach_t *dsmp)
{
	if (dsmp->dsm_state == INFORM_SENT || dsmp->dsm_state == INFORMATION)
		return;

	/*
	 * As we're returning to INIT state, we need to discard any leases we
	 * may have, and (for v4) canonize the LIF.  There's a bit of tension
	 * between keeping around a possibly still working address, and obeying
	 * the RFCs.  A more elaborate design would be to mark the addresses as
	 * DEPRECATED, and then start a removal timer.  Such a design would
	 * probably compromise testing.
	 */
	deprecate_leases(dsmp);

	if (!set_start_timer(dsmp)) {
		dhcpmsg(MSG_ERROR, "dhcp_restart: cannot schedule dhcp_start, "
		    "reverting to INIT state on %s", dsmp->dsm_name);

		(void) set_smach_state(dsmp, INIT);
		dsmp->dsm_dflags |= DHCP_IF_FAILED;
		ipc_action_finish(dsmp, DHCP_IPC_E_MEMORY);
	} else {
		dhcpmsg(MSG_DEBUG, "dhcp_restart: restarting DHCP on %s",
		    dsmp->dsm_name);
	}
}
Beispiel #3
0
/* ARGSUSED */
void
dhcp_packet_lif(iu_eh_t *ehp, int fd, short events, iu_event_id_t id,
    void *arg)
{
	dhcp_lif_t	*lif = arg;
	PKT_LIST	*plp;
	uchar_t		recv_type;
	const char	*pname;
	uint_t		xid;
	dhcp_smach_t	*dsmp;

	if ((plp = recv_pkt(fd, lif->lif_max, B_FALSE)) == NULL)
		return;

	recv_type = pkt_recv_type(plp);
	pname = pkt_type_to_string(recv_type, B_FALSE);

	if (!pkt_v4_match(recv_type,
	    DHCP_PACK | DHCP_PNAK | DHCP_PUNTYPED | DHCP_POFFER)) {
		dhcpmsg(MSG_VERBOSE, "dhcp_packet_lif: ignored v4 %s packet "
		    "received via LIF %s", pname, lif->lif_name);
		free_pkt_entry(plp);
		return;
	}

	/*
	 * Find the corresponding state machine.
	 */
	xid = pkt_get_xid(plp->pkt, B_FALSE);
	for (dsmp = lookup_smach_by_xid(xid, NULL, B_FALSE); dsmp != NULL;
	    dsmp = lookup_smach_by_xid(xid, dsmp, B_FALSE)) {
		if (dsmp->dsm_lif == lif)
			break;
	}

	if (dsmp == NULL)
		goto drop;

	if (pkt_v4_match(recv_type, DHCP_PACK|DHCP_PNAK)) {
		/*
		 * We've got an ACK/NAK; make sure it's acceptable and cancel
		 * the REQUEST retransmissions.
		 */
		accept_v4_acknak(dsmp, plp);
	} else {
		if (is_bound_state(dsmp->dsm_state))
			goto drop;
		/*
		 * Must be an OFFER or a BOOTP message: enqueue it for later
		 * processing by select_best().
		 */
		pkt_smach_enqueue(dsmp, plp);
	}
	return;
drop:
	dhcpmsg(MSG_VERBOSE, "dhcp_packet_lif: ignored %s packet xid "
	    "%x received via LIF %s; %s", pname, xid, lif->lif_name,
	    dsmp == NULL ? "unknown state machine" : "bound");
	free_pkt_entry(plp);
}
Beispiel #4
0
void
send_data_reply(ipc_action_t *ia, int error, dhcp_data_type_t type,
    const void *buffer, size_t size)
{
	dhcp_ipc_reply_t	*reply;
	int retval;

	if (ia->ia_fd == -1 || ia->ia_request == NULL)
		return;

	reply = dhcp_ipc_alloc_reply(ia->ia_request, error, buffer, size,
	    type);
	if (reply == NULL) {
		dhcpmsg(MSG_ERR, "send_data_reply: cannot allocate reply");

	} else if ((retval = dhcp_ipc_send_reply(ia->ia_fd, reply)) != 0) {
		dhcpmsg(MSG_ERROR, "send_data_reply: dhcp_ipc_send_reply: %s",
		    dhcp_ipc_strerror(retval));
	}

	/*
	 * free the request since we've now used it to send our reply.
	 * we can also close the socket since the reply has been sent.
	 */

	free(reply);
	free(ia->ia_request);
	if (ia->ia_eid != -1)
		(void) iu_unregister_event(eh, ia->ia_eid, NULL);
	(void) dhcp_ipc_close(ia->ia_fd);
	ia->ia_request = NULL;
	ia->ia_fd = -1;
	ia->ia_eid = -1;
}
Beispiel #5
0
void
server_unicast_option(dhcp_smach_t *dsmp, PKT_LIST *plp)
{
	const dhcpv6_option_t *d6o;
	uint_t olen;

	d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_UNICAST, &olen);
	olen -= sizeof (*d6o);
	/* LINTED: no consequent */
	if (d6o == NULL) {
		/* No Server Unicast option specified */
	} else if (olen != sizeof (dsmp->dsm_server)) {
		dhcpmsg(MSG_WARNING, "server_unicast_option: %s has Server "
		    "Unicast option with bad length",
		    pkt_type_to_string(pkt_recv_type(plp), B_TRUE));
	} else {
		in6_addr_t addr;

		(void) memcpy(&addr, d6o + 1, olen);
		if (IN6_IS_ADDR_UNSPECIFIED(&addr)) {
			dhcpmsg(MSG_WARNING, "server_unicast_option: unicast "
			    "to unspecified address ignored");
		} else if (IN6_IS_ADDR_MULTICAST(&addr)) {
			dhcpmsg(MSG_WARNING, "server_unicast_option: unicast "
			    "to multicast address ignored");
		} else if (IN6_IS_ADDR_V4COMPAT(&addr) ||
		    IN6_IS_ADDR_V4MAPPED(&addr)) {
			dhcpmsg(MSG_WARNING, "server_unicast_option: unicast "
			    "to invalid address ignored");
		} else {
			dsmp->dsm_server = addr;
		}
	}
}
Beispiel #6
0
/* ARGSUSED */
void
dhcp_renew(iu_tq_t *tqp, void *arg)
{
	dhcp_lease_t *dlp = arg;
	dhcp_smach_t *dsmp = dlp->dl_smach;
	uint32_t	t2;

	dhcpmsg(MSG_VERBOSE, "dhcp_renew: T1 timer expired on %s",
	    dsmp->dsm_name);

	dlp->dl_t1.dt_id = -1;

	if (dsmp->dsm_state == RENEWING || dsmp->dsm_state == REBINDING) {
		dhcpmsg(MSG_DEBUG, "dhcp_renew: already renewing");
		release_lease(dlp);
		return;
	}

	/*
	 * Sanity check: don't send packets if we're past T2, or if we're
	 * extremely close.
	 */

	t2 = dsmp->dsm_curstart_monosec + dlp->dl_t2.dt_start;
	if (monosec() + TOO_CLOSE >= t2) {
		dhcpmsg(MSG_DEBUG, "dhcp_renew: %spast T2 on %s",
		    monosec() > t2 ? "" : "almost ", dsmp->dsm_name);
		release_lease(dlp);
		return;
	}

	/*
	 * If there isn't an async event pending, or if we can cancel the one
	 * that's there, then try to renew by sending an extension request.  If
	 * that fails, we'll try again when the next timer fires.
	 */
	if (!async_cancel(dsmp) || !async_start(dsmp, DHCP_EXTEND, B_FALSE) ||
	    !dhcp_extending(dsmp)) {
		if (monosec() + RETRY_DELAY < t2) {
			/*
			 * Try again in RETRY_DELAY seconds; user command
			 * should be gone.
			 */
			init_timer(&dlp->dl_t1, RETRY_DELAY);
			(void) set_smach_state(dsmp, BOUND);
			if (!schedule_lease_timer(dlp, &dlp->dl_t1,
			    dhcp_renew)) {
				dhcpmsg(MSG_INFO, "dhcp_renew: unable to "
				    "reschedule renewal around user command "
				    "on %s; will wait for rebind",
				    dsmp->dsm_name);
			}
		} else {
			dhcpmsg(MSG_DEBUG, "dhcp_renew: user busy on %s; will "
			    "wait for rebind", dsmp->dsm_name);
		}
	}
	release_lease(dlp);
}
Beispiel #7
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);
}
Beispiel #8
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);
}
Beispiel #9
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);
}
Beispiel #10
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);
}
Beispiel #11
0
/* ARGSUSED */
static void
accept_event(iu_eh_t *ehp, int fd, short events, iu_event_id_t id, void *arg)
{
	int	client_fd;
	int	is_priv;

	if (dhcp_ipc_accept(fd, &client_fd, &is_priv) != 0) {
		dhcpmsg(MSG_ERR, "accept_event: accept on ipc socket");
		return;
	}

	if (iu_register_event(eh, client_fd, POLLIN, ipc_event,
	    (void *)is_priv) == -1) {
		dhcpmsg(MSG_ERROR, "accept_event: cannot register ipc socket "
		    "for callback");
	}
}
Beispiel #12
0
static int
dhcp_finish_expire(dhcp_smach_t *dsmp, void *arg)
{
	dhcp_lif_t *lif = arg;
	dhcp_lease_t *dlp;

	dhcpmsg(MSG_DEBUG, "lease expired on %s; removing", lif->lif_name);

	dlp = lif->lif_lease;
	unplumb_lif(lif);
	if (dlp->dl_nlifs == 0)
		remove_lease(dlp);
	release_lif(lif);

	/* If some valid leases remain, then drive on */
	if (dsmp->dsm_leases != NULL) {
		dhcpmsg(MSG_DEBUG,
		    "dhcp_finish_expire: some leases remain on %s",
		    dsmp->dsm_name);
		return (1);
	}

	(void) remove_hostconf(dsmp->dsm_name, dsmp->dsm_isv6);

	dhcpmsg(MSG_INFO, "last lease expired on %s -- restarting DHCP",
	    dsmp->dsm_name);

	/*
	 * in the case where the lease is less than DHCP_REBIND_MIN
	 * seconds, we will never enter dhcp_renew() and thus the packet
	 * counters will not be reset.  in that case, reset them here.
	 */

	if (dsmp->dsm_state == BOUND) {
		dsmp->dsm_bad_offers	= 0;
		dsmp->dsm_sent		= 0;
		dsmp->dsm_received	= 0;
	}

	deprecate_leases(dsmp);

	/* reset_smach() in dhcp_selecting() will clean up any leftover state */
	dhcp_selecting(dsmp);

	return (1);
}
Beispiel #13
0
void
print_server_msg(dhcp_smach_t *dsmp, const char *msg, uint_t msglen)
{
	if (msglen > 0) {
		dhcpmsg(MSG_INFO, "%s: message from server: %.*s",
		    dsmp->dsm_name, msglen, msg);
	}
}
Beispiel #14
0
/*
 * Sanity check that dsvcd_request_t `req' (which is `reqsize' bytes long)
 * is a correctly formed request; if not, return an error which will be
 * returned to the door caller.
 */
static int
check_door_req(dsvcd_request_t *req, size_t reqsize, size_t minsize)
{
	door_cred_t cred;

	if (req == NULL) {
		dhcpmsg(MSG_WARNING, "empty request, ignoring");
		return (DSVC_SYNCH_ERR);
	}

	/*
	 * Check credentials; we don't allow any non-super-user requests
	 * since this would open a denial-of-service hole (since a lock
	 * could be checked out indefinitely).
	 */
	if (door_cred(&cred) != 0) {
		dhcpmsg(MSG_WARNING, "request with unknown credentials");
		return (DSVC_ACCESS);
	}

	if (cred.dc_euid != 0) {
		dhcpmsg(MSG_WARNING, "request with non-super-user credentials");
		return (DSVC_ACCESS);
	}

	/*
	 * Check the version and size; we check this before checking the
	 * size of the request structure since an "incompatible version"
	 * message is more helpful than a "short request" message.
	 */
	if (reqsize > offsetof(dsvcd_request_t, rq_version) &&
	    req->rq_version != DSVCD_DOOR_VERSION) {
		dhcpmsg(MSG_WARNING, "request with unsupported version `%d'",
		    req->rq_version);
		return (DSVC_SYNCH_ERR);
	}

	if (reqsize < minsize) {
		dhcpmsg(MSG_VERBOSE, "short request (%d bytes, minimum %d "
		    "bytes)", reqsize, minsize);
		return (DSVC_SYNCH_ERR);
	}

	return (DSVC_SUCCESS);
}
Beispiel #15
0
/*
 * srealloc()  --  safe realloc()
 *
 * Always returns a valid pointer(if it returns at all).
 * If realloc() returns an error, a message is printed using the syslog()
 * function and the program aborts with a status of 1.
 * Unlike smalloc(), does not initialize the buffer to all zeros.
 *
 * Must be MT SAFE - called by threads other than the main thread.
 */
void *
srealloc(void *arg, uint_t nbytes)
{
	if ((arg = realloc(arg, nbytes)) == NULL) {
		dhcpmsg(LOG_ERR, "Cannot allocate memory (%s), exiting\n",
		    strerror(errno));
		exit(1);
	}
	return (arg);
}
Beispiel #16
0
/* ARGSUSED */
void
inactivity_shutdown(iu_tq_t *tqp, void *arg)
{
	if (smach_count() > 0)	/* shouldn't happen, but... */
		return;

	dhcpmsg(MSG_VERBOSE, "inactivity_shutdown: timed out");

	iu_stop_handling_events(eh, DHCP_REASON_INACTIVITY, NULL, NULL);
}
Beispiel #17
0
/* ARGSUSED */
static boolean_t
stop_extending(dhcp_smach_t *dsmp, unsigned int n_requests)
{
	dhcp_lease_t *dlp;

	/*
	 * If we're renewing and rebind time is soon approaching, then don't
	 * schedule
	 */
	if (dsmp->dsm_state == RENEWING) {
		monosec_t t2;

		t2 = 0;
		for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) {
			if (dlp->dl_t2.dt_start > t2)
				t2 = dlp->dl_t2.dt_start;
		}
		t2 += dsmp->dsm_curstart_monosec;
		if (monosec() + TOO_CLOSE >= t2) {
			dhcpmsg(MSG_DEBUG, "stop_extending: %spast T2 on %s",
			    monosec() > t2 ? "" : "almost ", dsmp->dsm_name);
			return (B_TRUE);
		}
	}

	/*
	 * Note that returning B_TRUE cancels both this transmission and the
	 * one that would occur at dsm_send_timeout, and that for v4 we cut the
	 * time in half for each retransmission.  Thus we check here against
	 * half of the minimum.
	 */
	if (!dsmp->dsm_isv6 &&
	    dsmp->dsm_send_timeout < DHCP_REBIND_MIN * MILLISEC / 2) {
		dhcpmsg(MSG_DEBUG, "stop_extending: next retry would be in "
		    "%d.%03d; stopping", dsmp->dsm_send_timeout / MILLISEC,
		    dsmp->dsm_send_timeout % MILLISEC);
		return (B_TRUE);
	}

	/* Otherwise, w stop only when the next timer (rebind, expire) fires */
	return (B_FALSE);
}
Beispiel #18
0
static boolean_t
check_main_lif(dhcp_smach_t *dsmp, const struct ifa_msghdr *ifam, int msglen)
{
	dhcp_lif_t *lif = dsmp->dsm_lif;
	struct lifreq lifr;

	/*
	 * Get the real (64 bit) logical interface flags.  Note that the
	 * routing socket message has flags, but these are just the lower 32
	 * bits.
	 */
	(void) memset(&lifr, 0, sizeof (lifr));
	(void) strlcpy(lifr.lifr_name, lif->lif_name, sizeof (lifr.lifr_name));
	if (ioctl(v6_sock_fd, SIOCGLIFFLAGS, &lifr) == -1) {
		/*
		 * Failing to retrieve flags means that the interface is gone.
		 * Our state machine is now trash.
		 */
		if (errno == ENXIO) {
			dhcpmsg(MSG_INFO, "%s has been removed; abandoning",
			    lif->lif_name);
		} else {
			dhcpmsg(MSG_ERR,
			    "unable to retrieve interface flags on %s",
			    lif->lif_name);
		}
		return (B_FALSE);
	} else if (!check_rtm_addr(ifam, msglen, B_TRUE, &lif->lif_v6addr)) {
		/*
		 * If the message is not about this logical interface,
		 * then just ignore it.
		 */
		return (B_TRUE);
	} else if (lifr.lifr_flags & IFF_DUPLICATE) {
		dhcpmsg(MSG_ERROR, "interface %s has duplicate address",
		    lif->lif_name);
		return (B_FALSE);
	} else {
		return (B_TRUE);
	}
}
Beispiel #19
0
void
write_lease_to_hostconf(dhcp_smach_t *dsmp)
{
	PKT_LIST *plp[2];
	const char *hcfile;

	hcfile = ifname_to_hostconf(dsmp->dsm_name, dsmp->dsm_isv6);
	plp[0] = dsmp->dsm_ack;
	plp[1] = dsmp->dsm_orig_ack;
	if (write_hostconf(dsmp->dsm_name, plp, 2,
	    monosec_to_time(dsmp->dsm_curstart_monosec),
	    dsmp->dsm_isv6) != -1) {
		dhcpmsg(MSG_DEBUG, "wrote lease to %s", hcfile);
	} else if (errno == EROFS) {
		dhcpmsg(MSG_DEBUG, "%s is on a read-only file "
		    "system; not saving lease", hcfile);
	} else {
		dhcpmsg(MSG_ERR, "cannot write %s (reboot will "
		    "not use cached configuration)", hcfile);
	}
}
Beispiel #20
0
/*
 * smalloc()  --  safe malloc()
 *
 * Always returns a valid pointer(if it returns at all).  The allocated
 * memory is initialized to all zeros.  If malloc() returns an error, a
 * message is printed using the syslog() function and the program aborts
 * with a status of 1.
 *
 * Must be MT SAFE - called by threads other than the main thread.
 */
void *
smalloc(uint_t nbytes)
{
	char		*retvalue;

	if ((retvalue = calloc(nbytes, sizeof (char))) == NULL) {
		dhcpmsg(LOG_ERR, "Cannot allocate memory (%s), exiting\n",
		    strerror(errno));
		exit(1);
	}
	return (retvalue);
}
Beispiel #21
0
/* ARGSUSED */
static void
dhcp_start(iu_tq_t *tqp, void *arg)
{
	dhcp_smach_t	*dsmp = arg;

	dsmp->dsm_start_timer = -1;
	(void) set_smach_state(dsmp, INIT);
	if (verify_smach(dsmp)) {
		dhcpmsg(MSG_VERBOSE, "starting DHCP on %s", dsmp->dsm_name);
		dhcp_selecting(dsmp);
	}
}
Beispiel #22
0
void
dhcp_init_reboot(dhcp_smach_t *dsmp)
{
	dhcpmsg(MSG_VERBOSE,  "%s has cached configuration - entering "
	    "INIT_REBOOT", dsmp->dsm_name);

	if (!set_smach_state(dsmp, INIT_REBOOT)) {
		dhcpmsg(MSG_ERROR, "dhcp_init_reboot: cannot register to "
		    "collect ACK/NAK packets, reverting to INIT on %s",
		    dsmp->dsm_name);

		dsmp->dsm_dflags |= DHCP_IF_FAILED;
		(void) set_smach_state(dsmp, INIT);
		ipc_action_finish(dsmp, DHCP_IPC_E_MEMORY);
		return;
	}

	if (dsmp->dsm_isv6)
		dhcp_init_reboot_v6(dsmp);
	else
		dhcp_init_reboot_v4(dsmp);
}
Beispiel #23
0
void
ipc_action_finish(dhcp_smach_t *dsmp, int reason)
{
	struct ipc_action *ia = &dsmp->dsm_ia;

	dsmp->dsm_dflags &= ~DHCP_IF_BUSY;

	if (dsmp->dsm_ia.ia_fd == -1) {
		dhcpmsg(MSG_ERROR,
		    "ipc_action_finish: attempted to finish unknown action "
		    "on %s", dsmp->dsm_name);
		return;
	}

	dhcpmsg(MSG_DEBUG,
	    "ipc_action_finish: finished %s (command %d) on %s: %d",
	    dhcp_ipc_type_to_string(ia->ia_cmd), (int)ia->ia_cmd,
	    dsmp->dsm_name, reason);

	/*
	 * if we can't cancel this timer, we're really in the
	 * twilight zone.  however, as long as we don't drop the
	 * reference to the state machine, it shouldn't hurt us
	 */

	if (dsmp->dsm_ia.ia_tid != -1 &&
	    iu_cancel_timer(tq, dsmp->dsm_ia.ia_tid, NULL) == 1) {
		dsmp->dsm_ia.ia_tid = -1;
		release_smach(dsmp);
	}

	if (reason == 0)
		send_ok_reply(ia);
	else
		send_error_reply(ia, reason);

	async_finish(dsmp);
}
Beispiel #24
0
/*
 * Create an unlock descriptor for container `cn' -- returns an unlock
 * descriptor on success, or NULL on failure; the reason for failure is in
 * `retvalp'.  Since creating door descriptors is expensive, we keep a few
 * cache a small list of old descriptors around on a reclaim list and only
 * allocate a new one if the list is empty.
 */
static dsvcd_unlock_desc_t *
ud_create(dsvcd_container_t *cn, int *retvalp)
{
	dsvcd_unlock_desc_t *ud;

	*retvalp = DSVC_SUCCESS;
	(void) mutex_lock(&ud_reclaim_lock);
	if (ud_reclaim_list != NULL) {
		ud = ud_reclaim_list;
		ud_reclaim_list = ud->ud_next;
		ud_reclaim_count--;
		(void) mutex_unlock(&ud_reclaim_lock);
	} else {
		(void) mutex_unlock(&ud_reclaim_lock);
		ud = malloc(sizeof (dsvcd_unlock_desc_t));
		if (ud == NULL) {
			dhcpmsg(MSG_WARNING, "cannot allocate unlock door "
			    "descriptor; denying %s lock request", cn->cn_id);
			*retvalp = DSVC_NO_MEMORY;
			return (NULL);
		}

		(void) mutex_init(&ud->ud_lock, USYNC_THREAD, NULL);
		ud->ud_fd = door_create((void (*)())svc_unlock, ud,
		    DOOR_UNREF_MULTI | DOOR_REFUSE_DESC | DOOR_NO_CANCEL);
		if (ud->ud_fd == -1) {
			dhcpmsg(MSG_WARNING, "cannot create unlock door; "
			    "denying %s lock request", cn->cn_id);
			free(ud);
			*retvalp = DSVC_NO_RESOURCES;
			return (NULL);
		}
	}

	ud->ud_next = NULL;
	ud->ud_cn = cn;
	return (ud);
}
Beispiel #25
0
static void
request_failed(dhcp_smach_t *dsmp)
{
	PKT_LIST *offer;

	dsmp->dsm_server = ipv6_all_dhcp_relay_and_servers;
	if ((offer = select_best(dsmp)) != NULL) {
		insque(offer, &dsmp->dsm_recv_pkt_list);
		dhcp_requesting(NULL, dsmp);
	} else {
		dhcpmsg(MSG_INFO, "no offers left on %s; restarting",
		    dsmp->dsm_name);
		dhcp_selecting(dsmp);
	}
}
Beispiel #26
0
static boolean_t
stop_requesting(dhcp_smach_t *dsmp, unsigned int n_requests)
{
	uint_t maxreq;

	maxreq = dsmp->dsm_isv6 ? DHCPV6_REQ_MAX_RC : DHCP_MAX_REQUESTS;
	if (n_requests >= maxreq) {

		dhcpmsg(MSG_INFO, "no ACK/NAK/Reply to REQUEST on %s",
		    dsmp->dsm_name);

		request_failed(dsmp);
		return (B_TRUE);
	} else {
		return (B_FALSE);
	}
}
Beispiel #27
0
/* ARGSUSED */
static void
doorserv_create(door_info_t *infop)
{
	void		*stackbase;
	unsigned int	stacksize = 32 * 1024;

	stackbase = stack_create(&stacksize);
	if (stackbase != NULL) {
		errno = thr_create(stackbase, stacksize, doorserv_thread, NULL,
		    THR_BOUND | THR_DETACHED, NULL);
		if (errno != 0) {
			dhcpmsg(MSG_ERR, "cannot create door server thread; "
			    "server thread pool will not be grown");
			stack_destroy(stackbase, stacksize);
		}
	}
}
Beispiel #28
0
/* ARGSUSED */
static void
ipc_action_timeout(iu_tq_t *tq, void *arg)
{
	dhcp_smach_t		*dsmp = arg;
	struct ipc_action	*ia = &dsmp->dsm_ia;

	dsmp->dsm_dflags &= ~DHCP_IF_BUSY;

	ia->ia_tid = -1;

	dhcpmsg(MSG_VERBOSE, "ipc timeout waiting for agent to complete "
	    "%s (command %d) for %s", dhcp_ipc_type_to_string(ia->ia_cmd),
	    ia->ia_cmd, dsmp->dsm_name);

	send_error_reply(ia, DHCP_IPC_E_TIMEOUT);

	async_finish(dsmp);
	release_smach(dsmp);
}
Beispiel #29
0
/*
 * Reap containers that have not been recently used.
 */
static void *
reaper(void *ds_table_raw)
{
	dsvcd_datastore_t	**ds_table;
	unsigned int		i, nreaped;

	ds_table = (dsvcd_datastore_t **)ds_table_raw;
	for (;;) {
		(void) sleep(DSVCD_REAP_INTERVAL);
		for (i = 0; ds_table[i] != NULL; i++) {
			nreaped = ds_reap_containers(ds_table[i],
			    DSVCD_REAP_THRESH);
			if (nreaped > 0) {
				dhcpmsg(MSG_VERBOSE, "reaped %u container "
				    "synchpoints from %s", nreaped,
				    ds_table[i]->ds_name);
			}
		}
	}
	/* NOTREACHED */
	return (NULL);
}
Beispiel #30
0
/* ARGSUSED */
static int
dhcp_restart_lease(struct ifslist *ifsp, const char *msg)
{
	dhcpmsg(MSG_INFO, "lease expired on %s -- restarting DHCP",
	    ifsp->if_name);

	/*
	 * in the case where the lease is less than DHCP_REBIND_MIN
	 * seconds, we will never enter dhcp_renew() and thus the packet
	 * counters will not be reset.  in that case, reset them here.
	 */

	if (ifsp->if_state == BOUND) {
		ifsp->if_bad_offers = 0;
		ifsp->if_sent	    = 0;
		ifsp->if_received   = 0;
	}

	(void) canonize_ifs(ifsp);

	/* reset_ifs() in dhcp_selecting() will clean up any leftover state */
	dhcp_selecting(ifsp);
	return (1);
}