/*
* 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
*/
/*
*/
#include <assert.h>
#include <dhcpagent_ipc.h>
#include <dhcp_inittab.h>
#include <dhcp_symbol.h>
#include <dhcpagent_util.h>
#include <errno.h>
#include <execinfo.h>
#include <libnwam.h>
#include <stdlib.h>
#include <strings.h>
#include <ucontext.h>
#include <unistd.h>
#include <libscf.h>
#include "conditions.h"
#include "events.h"
#include "ncp.h"
#include "ncu.h"
#include "objects.h"
#include "util.h"
/*
* ncu_ip.c - contains routines that are IP interface-specific for NCUs.
*/
static void nwamd_down_interface(const char *, ipadm_addr_type_t, const char *);
/*
* Given a sockaddr representation of an IPv4 or IPv6 address returns the
* string representation. Note that 'sockaddr' should point at the correct
* sockaddr structure for the address family (sockaddr_in for AF_INET or
* sockaddr_in6 for AF_INET6) or alternatively at a sockaddr_storage
* structure.
*/
static const char *
{
const char *straddr;
return (NULL);
/* LINTED E_BAD_PTR_CAST_ALIGN */
/* LINTED E_BAD_PTR_CAST_ALIGN */
len);
} else {
return (NULL);
}
}
void
{
linkname);
"for link %s, cannot propogate %s event", linkname,
return;
}
if (ncu->ncu_enabled) {
if (ip_ncu->nwamd_object_aux_state ==
"nwamd_propogate_link_up_down_to_ip: will not "
"propogate link %s event as IP NCU %s is being "
} else {
"nwamd_propogate_link_up_down_to_ip: propogating "
"link %s event to interface %s",
up ?
}
} else {
"nwamd_propogate_link_up_down_to_ip: not propogating "
"link %s event to interface %s, IP NCU is disabled",
}
}
/*
* Returns the value associated with the given symbol for the given
* interface. The interface may be NULL, in which case the primary
* interface is used.
* This function substitutes the need to call dhcpinfo(1), thus it is
* very similar to the implementation of dhcpinfo(1).
* When multiple values need to be returned (e.g., nameservers), they
* are separated by a space ' '.
*/
char *
{
int err;
/* if interface is not given, change it to empty string */
ifname = "";
/* find code and category in dhcp_inittab(4) */
sym_name);
goto fail;
}
/* allocate request */
sizeof (dhcp_optnum_t), DHCP_TYPE_OPTNUM);
goto fail;
}
/* make the request */
}
/* get data from the reply */
if (opt_len == 0) {
goto fail;
}
/* check protocol error */
goto fail;
}
opt_len -= 2;
/* decode the data into ascii */
goto fail;
}
return (value);
fail:
return (NULL);
}
void
{
sizeof (str)));
}
sizeof (str)));
}
}
/*
* Returns the nwamd_if_address structure for the given static address,
* NULL if not found.
*/
static struct nwamd_if_address *
{
continue;
return (nifap);
}
return (NULL);
}
/*
* Returns the nwamd_if_address structure representing the non-static address
* in the NCU. For IPv6, both stateless and stateful (DHCPv6) share the same
* nwamd_if_address. Will only return the nwamd_if_address if the relevant
* address is configured (v4 DHCP, v6 either stateless or stateless) for the
* NCU. Returns NULL if the structure is not found.
*/
static struct nwamd_if_address *
{
continue;
return (nifap);
return (nifap);
}
}
return (NULL);
}
/*
* Returns the nwamd_if_address structure that configured the given address,
* NULL if not found.
*/
static struct nwamd_if_address *
const nwamd_ncu_t *ncu)
{
return (nifap);
}
return (NULL);
}
/*
* Are one or more static addresses configured?
*/
{
struct nwamd_if_address *n;
if (n->ipaddr_atype != IPADM_ADDR_STATIC)
continue;
n->configured)
return (B_TRUE);
}
return (B_FALSE);
}
/*
* Is DHCP probably managing an address on this index. We decide that it is
* probably managing an address if there is an interface with IFF_DHCP set
* that isn't in our set of static addresses. Note that IFF_DHCP gets set
* on static addresses when we do a dhcp inform and if that list has changed
* recently then the result of this function could be erronous.
*/
{
0, 0)) != IPADM_SUCCESS) {
"ipadm_addr_info failed for %s: %s",
return (B_FALSE);
}
/*
* WARNING: This memcpy() assumes knowledge of the
* implementation of getifaddrs() and that it always
* uses sockaddr_storage as the backing store for
* address information, thus making it possible to
* copy the entire structure rather than do it on
* the size of the sockaddr according to family.
* This assumption is made elsewhere in this file.
*/
/* is this address an expected static one? */
continue;
/*
* For IPv4, DHCPRUNNING flag is set when dhcpagent is in
* the process of getting an address, but doesn't have one
* yet (interface has 0.0.0.0). For IPv6, DHCPRUNNING flag
* is set on the link-local address if trying to get a
* stateful address. In both cases, consider the interface
* as not being managed by DHCP and skip checking of flags.
*/
INADDR_ANY) ||
continue;
}
if (flags & IFF_DHCPRUNNING) {
/*
* If we get here we have an address that has the
* DHCP flag set and isn't an expected static address.
*/
break;
}
}
return (rv);
}
/*
* Return B_TRUE if IPv4 is requested in the given NCU.
*/
static boolean_t
{
if (u_if->nwamd_if_dhcp_requested) {
} else {
struct nwamd_if_address *n;
n->ipaddr_atype == IPADM_ADDR_STATIC)
break;
}
if (n != NULL)
}
return (anyv4_requested);
}
/*
* Returns B_TRUE if IPv6 is requested in the given NCU.
*/
static boolean_t
{
if (u_if->nwamd_if_stateful_requested ||
} else {
struct nwamd_if_address *n;
n->ipaddr_atype == IPADM_ADDR_STATIC)
break;
}
if (n != NULL)
}
return (anyv6_requested);
}
/*
* Bring up the ncu if we have the right combination of requested configuration
* and actual configuration and up is true, or bring down the ncu if no
* addresses are configured, and up is false.
*/
static void
{
char *name;
/*
* If V4 with or without V6 is configured then one of its interfaces
* needs to be up for the ncu to come online. If only V6 is requested
* then one of its interfaces needs to be up for the ncu to come online.
*/
if (nwamd_v4_requested(ncu)) {
ncu_online = B_TRUE;
} else if (nwamd_v6_requested(ncu)) {
stateless_running(ncu) ||
ncu_online = B_TRUE;
}
NWAM_SUCCESS) {
"nwam_ncu_name_to_typed_name failed");
return;
}
if (ncu_online && up) {
"bringing %s up", name);
} else if (!ncu_online && !up) {
"bringing %s down", name);
}
}
static void
{
}
static void
{
}
static boolean_t
{
0, 0)) != IPADM_SUCCESS) {
"ipadm_addr_info failed for %s: %s",
return (B_FALSE);
}
continue;
if (flags & STATELESS_RUNNING) {
break;
}
}
return (rv);
}
/*
* Returns the addrinfo associated with the given address. There is always
* only one addrinfo for each address.
*/
static boolean_t
{
if (ipstatus != IPADM_SUCCESS) {
"ipadm_addr_info failed for %s: %s",
return (B_FALSE);
}
/*
* If addresses match, rearrange pointers so that addrinfo
* does not contain a, and return a.
*/
else
break;
}
}
}
/*
* Returns B_TRUE if the addrinfo associated with the given ipaddr using its
* aobjname is found. An addrinfo list is created and returned in ainfo.
* Stateless and stateful IPv6 addrinfo have the same aobjname, thus the need
* to create a list of addrinfo.
*/
static boolean_t
{
if (ipstatus != IPADM_SUCCESS)
return (B_FALSE);
if (ipstatus != IPADM_SUCCESS) {
"ipadm_addr_info failed for %s: %s",
return (B_FALSE);
}
/* If aobjnames match, rearrange pointers to create new list */
else
else
} else {
}
}
}
/*
* Add the address provided in the nwamd_if_address. If DHCP is required,
* start DHCP. If a static address is configured, create the address; then do
* a DHCP_INFORM (in a separate thread) to get other networking configuration
* parameters. RTM_NEWADDRs - translated into IF_STATE events - will then
* finish the job of bringing the NCU online.
*/
static boolean_t
{
/*
* To make getting a DHCP address asynchronous, call
* ipadm_create_addr() in a new thread.
*/
"adding IPv4 DHCP address on %s", ifname);
} else {
"ipadm_create_addr failed on %s: %s",
return (B_FALSE);
}
/*
* When creating a static address, ipadm_create_addr() returns
* SUCCESS even if duplicate address is detected. Retrieve
* the addrinfo to get the flags.
*/
/*
* Since we are configuring a static address, there
* will be just *ONE* addrinfo with the aobjname in
* nifa->ipaddr.
*/
&addrinfo)) {
"could not find addrinfo on %s", ifname);
return (B_FALSE);
}
if (flags & IFF_DUPLICATE) {
char *object_name;
"duplicate address detected on %s", ifname);
== NWAM_SUCCESS) {
} else {
"could not create state event "
"for %s: %s",
}
return (B_FALSE);
}
/*
* Do DHCP_INFORM using async ipadm_refresh_addr().
* Only need to do this once per interface, and we
* do *not* need to do it if we are also getting a
* dhcp lease; so we only send the INFORM if the
* passed-in flag says to, and we clear the flag
* once we've initiated the INFORM transaction.
*/
if (*do_inform) {
}
}
}
return (B_TRUE);
}
/*
* Adds addresses for the given NCU.
*/
void
{
/* only need an inform if we're not also getting a dhcp lease */
if (nifap->configured)
continue;
&do_inform);
}
}
/*
* This event tells us that an interface address has appeared or disappeared,
* or that the interface flags on an interface have changed.
*/
void
{
return;
}
/* Ensure object is in correct state to handle IF state events */
switch (state) {
if (aux_state != NWAM_AUX_STATE_IF_WAITING_FOR_ADDR &&
"if %s is in invalid aux state %s for IF_STATE "
return;
}
break;
case NWAM_STATE_ONLINE:
/*
* We can get addresses from DHCP after we've taken the interface down.
* We deal with those below.
*/
case NWAM_STATE_OFFLINE:
break;
default:
"if %s is in invalid state %s for IF_STATE events",
return;
}
"nwamd_ncu_handle_if_state_event: addr %s %s",
/*
* Need to get flags for this interface. Get the addrinfo for
* the address that generated this IF_STATE event.
*/
if (addr_added) {
/*
* Address was added. Find the addrinfo for this
* address and the nwamd_if_address corresponding to
* this address.
*/
"nwamd_ncu_handle_if_state_event: "
"addrinfo doesn't exist for %s", addrstr);
goto valid_done;
}
sizeof (ai_addr));
/*
* If nwamd_if_address is not found, then this address
* isn't one that nwamd created. Remove it.
*/
"nwamd_ncu_handle_if_state_event: "
"address %s not managed by nwam added, "
"removing it", addrstr);
goto valid_done;
}
/* check flags to determine how intf is configured */
/* copy the configured address into nwamd_if_address */
if (stateless_running) {
sizeof (struct sockaddr_storage));
} else {
sizeof (struct sockaddr_storage));
}
} else {
/*
* Address was removed. Find the nwamd_if_address
* that configured this address.
*/
"nwamd_ncu_handle_if_state_event: "
"address %s not managed by nwam removed, "
"nothing to do", addrstr);
goto valid_done;
}
&ai)) {
sizeof (stor));
/*
* Since multiple addrinfo can have
* the same ipaddr, find the one for
* the address that generated this
* state event.
*/
sizeof (ai_addr));
addrinfo = a;
}
/*
* Stateful and stateless IPv6
* addrinfo have the same aobjname.
* Use the flags to determine which
* address is present in the system.
*/
}
}
}
}
/* Set the flags in the event for listeners */
/*
* Check for failure due to CR 6745448: if we get a
* report that an address has been deleted, then check
* for interface up, datalink down, and actual address
* non-zero. If that combination is seen, then this is
* a DHCP cached lease, and we need to remove it from
* the system, or it'll louse up the kernel routes
* (which aren't smart enough to avoid dead
* interfaces).
*/
== INADDR_ANY && aip != 0) {
struct sockaddr_in *a;
a = (struct sockaddr_in *)aip;
!(flags & IFF_RUNNING) &&
"nwamd_ncu_handle_if_state_event: "
"bug workaround: clear out addr "
"%s on %s", nwamd_sockaddr2str
sizeof (astr)),
}
goto valid_done;
}
}
/*
* If we received an RTM_NEWADDR and the IFF_UP flags has not
* been set, ignore this IF_STATE event. Once the IFF_UP flag
* is set, we'll get another RTM_NEWADDR message.
*/
"address %s added on %s without IFF_UP flag (%x), "
"ignoring IF_STATE event",
goto valid_done;
}
/*
* Has the address really been removed? Sometimes spurious
* RTM_DELADDRs are generated, so we need to ensure that
* the address is really gone. If IFF_DUPLICATE is set,
* we're getting the RTM_DELADDR due to DAD, so don't test
* in that case.
*/
"nwamd_ncu_handle_if_state_event: "
"address %s is not really gone from %s, "
"ignoring IF_STATE event",
goto valid_done;
}
}
if (addr_added) {
/*
* Address has been added.
*
* We need to make sure that we really want to keep
* this address. There is a race where we requested an
* address but by the time we got here we don't really
* want it and need to remove it.
*
* Once we decide we want the address adjust the ncu
* state accordingly. For example if this address is
* enough move online.
*/
} else if (u_if->nwamd_if_stateful_requested &&
} else if (u_if->nwamd_if_stateless_requested &&
} else if (!static_addr) {
/*
* This is something we didn't expect. Remove
* the address.
*/
goto valid_done;
}
/*
* The address looks valid so mark configured and
* move online if we either have a v4 address if
* v4 is configured or a v6 address if only v6 is
* configured.
*/
if (state != NWAM_STATE_ONLINE)
/*
* DHCP information. We might have to restore it first
* in case it is in maintenance.
*/
"refreshing %s as we may have other "
"DHCP information", NET_LOC_FMRI);
(void) smf_restore_instance(NET_LOC_FMRI);
if (smf_refresh_instance(NET_LOC_FMRI) != 0) {
"nwamd_ncu_handle_if_state_"
"event: refresh of %s "
"failed", NET_LOC_FMRI);
}
} else if (state == NWAM_STATE_ONLINE ||
/*
* Address has been removed. Only pay attention to
* disappearing addresses if we are online or coming
* online.
*
* Undo whatever configuration is necessary. Note
* that this may or may not cause the NCU to go down.
* We can get RTM_DELADDRs for duplicate addresses
* so deal with this seperately.
*/
/*
* The address is already gone. When looking
* for the addrinfo (using aobjname in
* ipaddr), we found addrinfo for either one
* or both stateless and stateful. Using the
* flags we determined whether each was
* configured or not. Update the flags here
* accordingly.
*/
}
if (flags & IFF_DUPLICATE) {
"nwamd_ncu_handle_if_state_event: "
"duplicate address detected on %s",
} else {
}
}
}
}
void
{
return;
}
}
/*
* Remove the address in the given aobjname. IPADM_OPT_RELEASE is specified
* for a DHCP address and specifies that the DHCP lease should also be released.
* ifname is only used for nlog().
*/
static void
const char *ifname)
{
"ipadm_delete_addr failed on %s: %s",
}
}
static void
{
}
static void
{
!= IPADM_SUCCESS)
return;
}
}
static void
{
ifname);
if (plumb) {
} else {
/* release DHCP address, if any */
}
if (ipstatus != IPADM_SUCCESS) {
"%s %s failed for %s: %s",
}
}
/* Unset flags */
if (!plumb) {
switch (af) {
case AF_INET:
break;
case AF_INET6:
break;
}
}
}
void
{
/*
* We get all posssible privs by calling nwamd_deescalate(). During
* because we don't have access to /etc/security/device_policy yet.
*/
}
void
{
}
static void *
{
char *name;
int retries = 0;
/* Make sure the NCU is in appropriate state for DHCP command */
return (NULL);
}
return (NULL);
}
switch (type) {
case DHCP_INFORM:
{
sizeof (aobjname))) != IPADM_SUCCESS) {
"ipadm_get_aobjname failed for %s: %s",
goto done;
}
break;
}
case DHCP_START:
break;
default:
goto done;
}
if (ipstatus == IPADM_DHCP_IPC_TIMEOUT) {
/*
* DHCP timed out: for DHCP_START requests, change state for
* this NCU and euqueue event to check NCU priority-groups;
* for DHCP_INFORM requests, nothing to do.
*/
if (type == DHCP_START) {
char *object_name;
"start_dhcp: DHCP_START timed out for %s", name);
!= NWAM_SUCCESS) {
"nwam_ncu_name_to_typed_name failed for %s",
name);
goto done;
}
} else {
"start_dhcp: DHCP_INFORM timed out for %s", name);
}
} else if ((ipstatus == IPADM_DHCP_IPC_ERROR ||
/*
* Retry DHCP request as we may have been unplumbing as part
* of the configuration phase.
*/
"retrying in %d sec",
(void) sleep(NWAMD_DHCP_RETRY_WAIT_TIME);
goto retry;
} else if (ipstatus != IPADM_SUCCESS) {
}
done:
return (NULL);
}
static void
{
"dhcp request");
return;
}
(void) pthread_attr_init(&attr);
(void) pthread_attr_destroy(&attr);
return;
}
(void) pthread_attr_destroy(&attr);
}