states.c revision d04ccbb3f3163ae5962a8b7465d9796bff6ca434
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* This module contains core functions for managing DHCP state machine
* instances.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdlib.h>
#include <search.h>
#include <string.h>
#include <ctype.h>
#include <dhcpmsg.h>
#include <dhcpagent_util.h>
#include <dhcp_stable.h>
#include "agent.h"
#include "states.h"
#include "interface.h"
#include "defaults.h"
#include "script_handler.h"
static uint_t global_smach_count;
static uchar_t *global_duid;
static size_t global_duidlen;
static void remove_smach(dhcp_smach_t *);
/*
* iaid_retry(): attempt to write LIF IAID again
*
* input: iu_tq_t *: ignored
* void *: pointer to LIF
* output: none
*/
/* ARGSUSED */
static void
{
"iaid_retry: unable to write out IAID for %s",
} else {
iaid_retry, lif);
}
} else {
}
}
/*
* insert_smach(): Create a state machine instance on a given logical
* interface. The state machine holds the caller's LIF
* reference on success, and frees it on failure.
*
* input: dhcp_lif_t *: logical interface name
* int *: set to DHCP_IPC_E_* if creation fails
* output: dhcp_smach_t *: state machine instance
*/
{
const char *prl;
return (NULL);
}
/*
* Now that we have a controlling LIF, we need to assign an IAID to
* that LIF.
*/
/*
* If this is a logical interface, then use an arbitrary seed
* value. Otherwise, use the ifIndex.
*/
"insert_smach: manufactured IAID %u for v%d %s",
}
if (isv6) {
/*
* With DHCPv6, we do all of our I/O using the common
* v6_sock_fd. There's no need for per-interface file
* descriptors because we have IPV6_PKTINFO.
*/
} else {
&dsmp->dsm_server);
/*
* With IPv4 DHCP, we start off doing our I/O via DLPI, so open
* that up now.
*/
/* This will also dispose of the LIF */
return (NULL);
}
}
/*
* initialize the parameter request list, if there is one.
*/
} else {
int i;
if (prl[i] == ',')
dsmp->dsm_prllen++;
}
"parameter request list for %s (continuing)",
} else {
prl++;
if (*prl == '\0')
break;
}
}
}
/*
* If there is no primary of this type, and there is one of the other,
* then make this one primary if it's on the same named PIF.
*/
"insert_smach: making %s primary for v%d",
}
}
/*
* We now have at least one state machine running, so cancel any
* running inactivity timer.
*/
if (inactivity_id != -1 &&
inactivity_id = -1;
return (dsmp);
}
/*
* hold_smach(): acquires a hold on a state machine
*
* input: dhcp_smach_t *: the state machine to acquire a hold on
* output: void
*/
void
{
dsmp->dsm_hold_count++;
}
/*
* free_smach(): frees the memory occupied by a state machine
*
* input: dhcp_smach_t *: the DHCP state machine to free
* output: void
*/
static void
{
/* no big deal if this fails */
}
}
/*
* release_smach(): releases a hold previously acquired on a state machine.
* If the hold count reaches 0, the state machine is freed.
*
* input: dhcp_smach_t *: the state machine entry to release the hold on
* output: void
*/
void
{
if (dsmp->dsm_hold_count == 0) {
return;
}
return;
}
if (--dsmp->dsm_hold_count == 0) {
} else {
}
}
/*
* next_smach(): state machine iterator function
*
* input: dhcp_smach_t *: current state machine (or NULL for list start)
* boolean_t: B_TRUE if DHCPv6, B_FALSE otherwise
* output: dhcp_smach_t *: next state machine in list
*/
{
return (lif->lif_smachs);
}
} else {
}
return (lif->lif_smachs);
}
}
return (NULL);
}
/*
* primary_smach(): loop through all state machines of the given type (v4 or
* v6) in the system, and locate the one that's primary.
*
* input: boolean_t: B_TRUE for IPv6
* output: dhcp_smach_t *: the primary state machine
*/
{
break;
}
return (dsmp);
}
/*
* make_primary(): designate a given state machine as being the primary
* instance on the primary interface. Note that the user often
* thinks in terms of a primary "interface" (rather than just
* an instance), so we go to lengths here to keep v4 and v6 in
* sync.
*
* input: dhcp_smach_t *: the primary state machine
* output: none
*/
void
{
/*
* Find the primary for the other protocol.
*/
/*
* If it's on a different interface, then cancel that. If it's on the
* same interface, then we're done.
*/
if (alt_primary != NULL) {
return;
}
/*
* We need a new primary for the other protocol. If the PIF exists,
* there must be at least one state machine. Just choose the first for
* consistency with insert_smach().
*/
}
}
/*
* lookup_smach(): finds a state machine by name and type; used for dispatching
* user commands.
*
* input: const char *: the name of the state machine
* boolean_t: B_TRUE if DHCPv6, B_FALSE otherwise
* output: dhcp_smach_t *: the state machine found
*/
{
break;
}
return (dsmp);
}
/*
* lookup_smach_by_uindex(): iterate through running state machines by
* truncated interface index.
*
* input: uint16_t: the interface index (truncated)
* dhcp_smach_t *: the previous state machine, or NULL for start
* boolean_t: B_TRUE for DHCPv6, B_FALSE for IPv4 DHCP
* output: dhcp_smach_t *: next state machine, or NULL at end of list
*/
{
/*
* If the user gives us a state machine, then check that the next one
* available is on the same physical interface. If so, then go ahead
* and return that.
*/
return (NULL);
return (dsmp);
} else {
/* Otherwise, start at the beginning of the list */
}
/*
* Find the next physical interface with the same truncated interface
* index, and return the first state machine on that. If there are no
* more physical interfaces that match, then we're done.
*/
do {
return (NULL);
break;
}
return (dsmp);
}
/*
* lookup_smach_by_xid(): iterate through running state machines by transaction
* id. Transaction ID zero means "all state machines."
*
* input: uint32_t: the transaction id to look up
* dhcp_smach_t *: the previous state machine, or NULL for start
* boolean_t: B_TRUE if DHCPv6, B_FALSE otherwise
* output: dhcp_smach_t *: next state machine, or NULL at end of list
*/
{
if (xid == 0 ||
break;
}
return (dsmp);
}
/*
* lookup_smach_by_event(): find a state machine busy with a particular event
* ID. This is used only for error handling.
*
* input: iu_event_id_t: the event id to look up
* output: dhcp_smach_t *: matching state machine, or NULL if none
*/
{
for (;;) {
return (dsmp);
}
if (isv6)
break;
}
return (dsmp);
}
/*
* cancel_offer_timer(): stop the offer polling timer on a given state machine
*
* input: dhcp_smach_t *: state machine on which to stop polling for offers
* output: none
*/
void
{
int retval;
if (retval == 1)
}
}
/*
* cancel_smach_timers(): stop all of the timers related to a given state
* machine, including lease and LIF expiry.
*
* input: dhcp_smach_t *: state machine to cancel
* output: none
*/
static void
{
}
}
/*
* remove_smach(): removes a given state machine from the system. marks it
* for being freed (but may not actually free it).
*
* input: dhcp_smach_t *: the state machine to remove
* output: void
*/
static void
{
return;
/*
* if we have long term timers, cancel them so that state machine
* resources can be reclaimed in a reasonable amount of time.
*/
/* Drop the hold that the LIF's state machine list had on us */
}
/*
* finished_smach(): we're finished with a given state machine; remove it from
* the system and tell the user (who may have initiated the
* removal process). Note that we remove it from the system
* first to allow back-to-back drop and create invocations.
*
* input: dhcp_smach_t *: the state machine to remove
* int: error for IPC
* output: void
*/
void
{
else
(void) async_cancel(dsmp);
}
/*
* set_smach_state(): changes state and updates I/O
*
* input: dhcp_smach_t *: the state machine to change
* DHCPSTATE: the new state
* output: boolean_t: B_TRUE on success, B_FALSE on failure
*/
{
"set_smach_state: changing from %s to %s on %s",
/*
* When we're in a bound state for IPv4, we receive our
* packets through our LIF. Otherwise, we receive them
* through DLPI. Make sure the right one is connected.
* For IPv6, no such change is necessary.
*/
return (B_FALSE);
}
return (B_FALSE);
}
}
}
return (B_TRUE);
}
/*
* duid_retry(): attempt to write DUID again
*
* input: iu_tq_t *: ignored
* void *: ignored
* output: none
*/
/* ARGSUSED */
static void
{
"duid_retry: unable to write out DUID");
} else {
}
}
}
/*
* get_smach_cid(): gets the client ID for a given state machine.
*
* input: dhcp_smach_t *: the state machine to set up
* output: int: DHCP_IPC_SUCCESS or one of DHCP_IPC_E_* on failure.
*/
int
{
const char *value;
/*
* Look in defaults file for the client-id. If present, this takes
* precedence over all other forms of ID.
*/
/*
* The Client ID string can have one of three basic forms:
* <decimal>,<data...>
* 0x<hex...>
* <string...>
*
* The first form is an RFC 3315 DUID. This is legal for both
* IPv4 DHCP and DHCPv6. For IPv4, an RFC 4361 Client ID is
* constructed from this value.
*
* The second and third forms are legal for IPv4 only. This is
* a raw Client ID, in hex or ASCII string format.
*/
char *cp;
errno = 0;
duidtype > 65535) {
"DUID type in %s", value);
goto no_specified_id;
}
switch (duidtype) {
case DHCPV6_DUID_LL:
case DHCPV6_DUID_LLT: {
int num;
char chr;
errno = 0;
subtype > 65535) {
"cannot parse MAC type in %s",
value);
goto no_specified_id;
}
if (*cp == ':')
break;
}
if (duidtype == DHCPV6_DUID_LL) {
client_id_len += sizeof (*dllt);
goto alloc_failure;
} else {
client_id_len += sizeof (*dll);
goto alloc_failure;
}
num = 0;
} else if (chr == ':') {
num = 0;
} else {
break;
}
}
break;
}
case DHCPV6_DUID_EN: {
errno = 0;
"cannot parse enterprise in %s",
value);
goto no_specified_id;
}
goto alloc_failure;
&client_id_len) != 0) {
"cannot parse hex string in %s",
value);
goto no_specified_id;
}
break;
}
default:
goto alloc_failure;
&client_id_len) != 0) {
"cannot parse hex string in %s",
value);
goto no_specified_id;
}
break;
}
}
return (DHCP_IPC_SUCCESS);
}
"get_smach_cid: client ID for %s invalid: %s",
/* skip past the 0x and convert the value to binary */
value += 2;
goto alloc_failure;
&client_id_len) == 0) {
return (DHCP_IPC_SUCCESS);
}
} else {
goto alloc_failure;
return (DHCP_IPC_SUCCESS);
}
}
/*
* There was either no user-specified Client ID value, or we were
* unable to parse it. We need to determine if a Client ID is required
* and, if so, generate one.
*
* If it's IPv4 and not a logical interface, then we need to preserve
* construction.
*/
/*
* This comes from the DHCP over IPoIB specification.
* In the absence of an user specified client id, IPoIB
* automatically uses the required format, with the
* unique 4 octet value set to 0 (since IPoIB driver
* allows only a single interface on a port with a
* specific GID to belong to an IP subnet (PSARC
* 2001/289, FWARC 2002/702).
*
* Type Client-Identifier
* +-----+-----+-----+-----+-----+----....----+
* | 0 | 0 (4 octets) | GID (16 octets)|
* +-----+-----+-----+-----+-----+----....----+
*/
goto alloc_failure;
/*
* Pick the GID from the mac address. The format
* of the hardware address is:
* +-----+-----+-----+-----+----....----+
* | QPN (4 octets) | GID (16 octets)|
* +-----+-----+-----+-----+----....----+
*/
}
return (DHCP_IPC_SUCCESS);
}
/*
* Now check for a saved DUID. If there is one, then use it. If there
* isn't, then generate a new one. For IPv4, we need to construct the
* RFC 4361 Client ID with this value and the LIF's IAID.
*/
if (global_duid == NULL &&
if (global_duid == NULL)
goto alloc_failure;
}
goto alloc_failure;
} else {
goto alloc_failure;
}
return (DHCP_IPC_SUCCESS);
return (DHCP_IPC_E_MEMORY);
}
/*
* smach_count(): returns the number of state machines running
*
* input: void
* output: uint_t: the number of state machines
*/
smach_count(void)
{
return (global_smach_count);
}
/*
* remove_default_routes(): removes a state machine's default routes
*
* input: dhcp_smach_t *: the state machine whose default routes need to be
* removed
* output: void
*/
void
{
int idx;
"removed %s from %s",
} else {
"unable to remove %s from %s",
}
}
dsmp->dsm_nrouters = 0;
}
}
/*
* reset_smach(): resets a state machine to its initial state
*
* input: dhcp_smach_t *: the state machine to reset
* output: void
*/
void
{
} else {
&dsmp->dsm_server);
}
}
/*
* refresh_smach(): refreshes a given state machine, as though awakened from
* hibernation or by lower layer "link up."
*
* input: dhcp_smach_t *: state machine to refresh
* output: void
*/
void
{
}
}
/*
* refresh_smachs(): refreshes all finite leases under DHCP control
*
* input: iu_eh_t *: unused
* int: unused
* void *: unused
* output: void
*/
/* ARGSUSED */
void
{
for (;;) {
}
if (isv6)
break;
}
}
/*
* nuke_smach_list(): delete the state machine list. For use when the
* dhcpagent is exiting.
*
* input: none
* output: none
*/
void
nuke_smach_list(void)
{
for (;;) {
int status;
/*
* Stop a script if it is not for DROP or
* RELEASE
*/
0) {
continue;
}
}
/*
* If the script is started by script_start, dhcp_drop
* and dhcp_release should and will only be called
* after the script exits.
*/
continue;
}
if (status == 1)
continue;
}
}
if (isv6)
break;
}
}
/*
* insert_lease(): Create a lease structure on a given state machine. The
* lease holds a reference to the state machine.
*
* input: dhcp_smach_t *: state machine
* output: dhcp_lease_t *: newly-created lease
*/
{
return (NULL);
return (dlp);
}
/*
* hold_lease(): acquires a hold on a lease
*
* input: dhcp_lease_t *: the lease to acquire a hold on
* output: void
*/
void
{
dlp->dl_hold_count++;
}
/*
* release_lease(): releases a hold previously acquired on a lease.
* If the hold count reaches 0, the lease is freed.
*
* input: dhcp_lease_t *: the lease to release the hold on
* output: void
*/
void
{
if (dlp->dl_hold_count == 0) {
return;
}
return;
}
if (--dlp->dl_hold_count == 0) {
"release_lease: freeing lease on state machine %s",
} else {
"release_lease: hold count on lease for %s: %d",
}
}
/*
* remove_lease(): removes a given lease from the state machine and drops the
* state machine's hold on the lease.
*
* input: dhcp_lease_t *: the lease to remove
* output: void
*/
void
{
if (dlp->dl_removed) {
} else {
"remove_lease: removed lease from state machine %s",
}
}
}
/*
* cancel_lease_timer(): cancels a lease-related timer
*
* input: dhcp_lease_t *: the lease to operate on
* dhcp_timer_t *: the timer to cancel
* output: void
*/
static void
{
return;
if (cancel_timer(dt)) {
} else {
"cancel_lease_timer: cannot cancel timer");
}
}
/*
* cancel_lease_timers(): cancels an lease's pending timers
*
* input: dhcp_lease_t *: the lease to operate on
* output: void
*/
void
{
}
/*
* schedule_lease_timer(): schedules a lease-related timer
*
* input: dhcp_lease_t *: the lease to operate on
* dhcp_timer_t *: the timer to schedule
* iu_tq_callback_t *: the callback to call upon firing
* output: boolean_t: B_TRUE if the timer was scheduled successfully
*/
{
/*
* If there's a timer running, cancel it and release its lease
* reference.
*/
if (!cancel_timer(dt))
return (B_FALSE);
}
return (B_TRUE);
} else {
"schedule_lease_timer: cannot schedule timer");
return (B_FALSE);
}
}
/*
* deprecate_leases(): remove all of the leases from a given state machine
*
* input: dhcp_smach_t *: the state machine
* output: none
*/
void
{
/*
* note that due to infelicities in the routing code, any default
* routes must be removed prior to canonizing or deprecating the LIF.
*/
}
/*
* verify_smach(): if the state machine is in a bound state, then verify the
* standing of the configured interfaces. Abandon those that
* the user has modified. If we end up with no valid leases,
* then just terminate the state machine.
*
* input: dhcp_smach_t *: the state machine
* output: boolean_t: B_TRUE if the state machine is still valid.
* note: assumes caller holds a state machine reference; as with most
* callback functions.
*/
{
return (B_FALSE);
}
/*
* If this is DHCPv4, then verify the main LIF.
*/
goto smach_terminate;
}
/*
* If we're not in one of the bound states, then there are no LIFs to
* verify here.
*/
return (B_TRUE);
}
if (!verify_lif(lif)) {
/*
* User has manipulated the interface. Even
* if we plumbed it, we must now disown it.
*/
}
}
}
/*
* If there are leases left, then everything's ok.
*/
return (B_TRUE);
}
return (B_FALSE);
}