Beispiel #1
0
void
send_v6_request(dhcp_smach_t *dsmp)
{
	dhcp_pkt_t *dpkt;
	dhcpv6_ia_na_t d6in;

	dpkt = init_pkt(dsmp, DHCPV6_MSG_REQUEST);
	(void) add_pkt_opt(dpkt, DHCPV6_OPT_SERVERID, dsmp->dsm_serverid,
	    dsmp->dsm_serveridlen);

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

	/* Add required Option Request option */
	(void) add_pkt_prl(dpkt, dsmp);

	(void) send_pkt_v6(dsmp, dpkt, dsmp->dsm_server, stop_requesting,
	    DHCPV6_REQ_TIMEOUT, DHCPV6_REQ_MAX_RT);

	/* For DHCPv6, state switch cannot fail */
	(void) set_smach_state(dsmp, REQUESTING);
}
Beispiel #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);
}
Beispiel #3
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);
}
Beispiel #4
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);
}
Beispiel #5
0
boolean_t
dhcp_extending(dhcp_smach_t *dsmp)
{
	dhcp_pkt_t		*dpkt;

	stop_pkt_retransmission(dsmp);

	/*
	 * We change state here because this function is also called when
	 * adopting a lease and on demand by the user.
	 */
	if (dsmp->dsm_state == BOUND) {
		dsmp->dsm_neg_hrtime	= gethrtime();
		dsmp->dsm_bad_offers	= 0;
		dsmp->dsm_sent		= 0;
		dsmp->dsm_received	= 0;
		/* Bound->renew can't fail */
		(void) set_smach_state(dsmp, RENEWING);
	}

	dhcpmsg(MSG_DEBUG, "dhcp_extending: sending request on %s",
	    dsmp->dsm_name);

	if (dsmp->dsm_isv6) {
		dhcp_lease_t *dlp;
		dhcp_lif_t *lif;
		uint_t nlifs;
		uint_t irt, mrt;

		/*
		 * Start constructing the Renew/Rebind message.  Only Renew has
		 * a server ID, as we still think our server might be
		 * reachable.
		 */
		if (dsmp->dsm_state == RENEWING) {
			dpkt = init_pkt(dsmp, DHCPV6_MSG_RENEW);
			(void) add_pkt_opt(dpkt, DHCPV6_OPT_SERVERID,
			    dsmp->dsm_serverid, dsmp->dsm_serveridlen);
			irt = DHCPV6_REN_TIMEOUT;
			mrt = DHCPV6_REN_MAX_RT;
		} else {
			dpkt = init_pkt(dsmp, DHCPV6_MSG_REBIND);
			irt = DHCPV6_REB_TIMEOUT;
			mrt = DHCPV6_REB_MAX_RT;
		}

		/*
		 * Loop over the leases, and add an IA_NA for each and an
		 * IAADDR for each address.
		 */
		for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) {
			lif = dlp->dl_lifs;
			for (nlifs = dlp->dl_nlifs; nlifs > 0;
			    nlifs--, lif = lif->lif_next) {
				(void) add_pkt_lif(dpkt, lif,
				    DHCPV6_STAT_SUCCESS, NULL);
			}
		}

		/* Add required Option Request option */
		(void) add_pkt_prl(dpkt, dsmp);

		return (send_pkt_v6(dsmp, dpkt, dsmp->dsm_server,
		    stop_extending, irt, mrt));
	} else {
		dhcp_lif_t *lif = dsmp->dsm_lif;
		ipaddr_t server;

		/* assemble the DHCPREQUEST message. */
		dpkt = init_pkt(dsmp, REQUEST);
		dpkt->pkt->ciaddr.s_addr = lif->lif_addr;

		/*
		 * The max dhcp message size option is set to the interface
		 * max, minus the size of the udp and ip headers.
		 */
		(void) add_pkt_opt16(dpkt, CD_MAX_DHCP_SIZE,
		    htons(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);
		/*
		 * dsm_reqhost was set for this state machine in
		 * dhcp_selecting() if the REQUEST_HOSTNAME option was set and
		 * a host name was found.
		 */
		if (!dhcp_add_fqdn_opt(dpkt, dsmp) &&
		    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);

		IN6_V4MAPPED_TO_IPADDR(&dsmp->dsm_server, server);
		return (send_pkt(dsmp, dpkt, server, stop_extending));
	}
}
Beispiel #6
0
static void
dhcp_init_reboot_v6(dhcp_smach_t *dsmp)
{
	dhcp_pkt_t *dpkt;
	dhcpv6_option_t *d6o, *d6so, *popt;
	uint_t olen, solen;
	dhcpv6_ia_na_t d6in;
	dhcpv6_iaaddr_t d6ia;
	char *obase;

	/*
	 * Assemble a Confirm message based on the current ack.
	 */

	dpkt = init_pkt(dsmp, DHCPV6_MSG_CONFIRM);

	/*
	 * Loop over and copy IA_NAs and IAADDRs we have in our last ack.  This
	 * is what we'll be requesting.
	 */
	d6o = NULL;
	while ((d6o = dhcpv6_pkt_option(dsmp->dsm_ack, d6o, DHCPV6_OPT_IA_NA,
	    &olen)) != NULL) {

		/*
		 * Copy in IA_NA option from the ack.  Note that we use zero
		 * for all timers in accordance with RFC 3315.  (It would make
		 * some sense to say what we think the current timers are as
		 * a hint to the server, but the RFC doesn't agree.)
		 */
		if (olen < sizeof (dhcpv6_ia_na_t))
			continue;
		(void) memcpy(&d6in, d6o, sizeof (d6in));
		d6in.d6in_t1 = 0;
		d6in.d6in_t2 = 0;
		popt = add_pkt_opt(dpkt, DHCPV6_OPT_IA_NA,
		    (char *)&d6in + sizeof (*d6o),
		    sizeof (d6in) - sizeof (*d6o));
		if (popt == NULL)
			goto failure;

		/*
		 * Now loop over the IAADDR suboptions and add those.
		 */
		obase = (char *)d6o + sizeof (dhcpv6_ia_na_t);
		olen -= sizeof (dhcpv6_ia_na_t);
		d6so = NULL;
		while ((d6so = dhcpv6_find_option(obase, olen, d6so,
		    DHCPV6_OPT_IAADDR, &solen)) != NULL) {
			if (solen < sizeof (dhcpv6_iaaddr_t))
				continue;
			(void) memcpy(&d6ia, d6so, sizeof (d6ia));
			d6ia.d6ia_preflife = 0;
			d6ia.d6ia_vallife = 0;
			if (add_pkt_subopt(dpkt, popt, DHCPV6_OPT_IAADDR,
			    (char *)&d6ia + sizeof (*d6so),
			    sizeof (d6ia) - sizeof (*d6so)) == NULL)
				goto failure;
		}
	}

	/* Add required Option Request option */
	(void) add_pkt_prl(dpkt, dsmp);

	(void) send_pkt_v6(dsmp, dpkt, ipv6_all_dhcp_relay_and_servers,
	    stop_init_reboot, DHCPV6_CNF_TIMEOUT, DHCPV6_CNF_MAX_RT);

	return;

failure:
	if (!set_start_timer(dsmp))
		dhcp_selecting(dsmp);
}
Beispiel #7
0
int
dhcp_extending(struct ifslist *ifsp)
{
	dhcp_pkt_t		*dpkt;

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

	dhcpmsg(MSG_DEBUG, "dhcp_extending: registering dhcp_acknak on %s",
	    ifsp->if_name);

	if (register_acknak(ifsp) == 0) {

		ipc_action_finish(ifsp, DHCP_IPC_E_MEMORY);
		async_finish(ifsp);

		dhcpmsg(MSG_WARNING, "dhcp_extending: cannot register "
		    "dhcp_acknak for %s, not sending renew request",
		    ifsp->if_name);

		return (0);
	}

	/*
	 * 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(ifsp, REQUEST);
	dpkt->pkt->ciaddr.s_addr = ifsp->if_addr.s_addr;

	add_pkt_opt16(dpkt, CD_MAX_DHCP_SIZE, htons(ifsp->if_max -
			sizeof (struct udpiphdr)));
	add_pkt_opt32(dpkt, CD_LEASE_TIME, htonl(DHCP_PERM));

	add_pkt_opt(dpkt, CD_CLASS_ID, class_id, class_id_len);
	add_pkt_opt(dpkt, CD_REQUEST_LIST, ifsp->if_prl, ifsp->if_prllen);
	/*
	 * if_reqhost was set for this interface in dhcp_selecting()
	 * if the REQUEST_HOSTNAME option was set and a host name was
	 * found.
	 */
	if (ifsp->if_reqhost != NULL) {
		add_pkt_opt(dpkt, CD_HOSTNAME, ifsp->if_reqhost,
		    strlen(ifsp->if_reqhost));
	}
	add_pkt_opt(dpkt, CD_END, NULL, 0);

	/*
	 * if we can't send the packet, leave the event handler registered
	 * anyway, since we're not expecting to get any other types of
	 * packets in other than ACKs/NAKs anyway.
	 */

	return (send_pkt(ifsp, dpkt, ifsp->if_server.s_addr, NULL));
}