ncu_ip.c revision 38f140aaa4a32508dc5318744dd8d51ab84b23a5
/*
* 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 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#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 <inetcfg.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 *start_dhcp_thread(void *);
static void nwamd_down_interface(const char *, uint_t, int);
char *
{
ICFG_SUCCESS) {
return (NULL);
} else {
return (str);
}
}
static void
{
char str[INET6_ADDRSTRLEN];
}
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 *
{
char *value; /* return value */
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
nwamd_dhcp_release(const char *ifname)
{
int rc;
/* Now allocate and send the request */
return;
}
if (rc != 0) {
/* Fall back to drop request */
return;
}
}
}
static boolean_t
{
icfg_handle_t h, newh;
int rc;
struct sockaddr_in bcastaddr;
char str[INET6_ADDRSTRLEN];
return (B_FALSE);
}
/*
* When working with the physical interface, we need to be careful
* to set the prefixlen and broadcast addresses before setting the
* will confuse us into thinking we've lost the address we've just
* assigned.
*/
if (logical_if) {
sizeof (struct sockaddr_in) : sizeof (struct sockaddr_in6));
} else {
newh = h;
/* Make sure DHCP is no longer running */
if (flags & IFF_DHCPRUNNING) {
"turning off DHCP for %s", ifname);
}
}
/*
* Set interface IFF_UP if not already. Do this and
* these can trigger an RTM_DELADDR that makes it appear
* that the address has gone away.
*/
sizeof (struct sockaddr_in) :
sizeof (struct sockaddr_in6));
}
if (rc != ICFG_SUCCESS) {
goto out;
}
!= ICFG_SUCCESS) {
icfg_errmsg(rc));
/* Set broadcast address based on address, prefixlen */
/*LINTED*/
!= ICFG_SUCCESS) {
"icfg_set_broadcast(%s) failed on %s: %s",
icfg_errmsg(rc));
}
}
}
if (rc == ICFG_SUCCESS) {
} else {
ifname);
}
}
out:
/* Check if address was a duplicate */
char *object_name;
"duplicate address detected on %s", ifname);
} else {
"create state event for %s: %s", ifname,
nwam_strerror(err));
}
rc = ICFG_DAD_FOUND;
}
if (h != newh)
icfg_close(h);
return (rc == ICFG_SUCCESS);
}
void
{
char str[INET6_ADDRSTRLEN];
"route %s", nwamd_sockaddr_to_str
sizeof (str)));
}
"route %s", nwamd_sockaddr_to_str
sizeof (str)));
}
}
void
{
struct nwamd_dhcp_thread_arg *arg;
"for dhcp request");
return;
}
(void) pthread_attr_init(&attr);
(void) pthread_attr_destroy(&attr);
return;
}
(void) pthread_attr_destroy(&attr);
}
static boolean_t
{
return (B_FALSE);
case AF_INET:
/*LINTED*/
/*LINTED*/
sizeof (struct in_addr)) == 0);
case AF_INET6:
/*LINTED*/
/*LINTED*/
sizeof (struct in6_addr)) == 0);
default:
return (B_FALSE);
}
}
/*
* Returns the nwamd_if_address structure for the given static address,
* NULL if not found.
*/
static struct nwamd_if_address *
{
char str[INET6_ADDRSTRLEN];
return (n);
}
return (NULL);
}
/*
* Returns the nwamd_if_address structure representing the non-static address
* in the NCU. dhcp is used to detemrine if the DHCP (stateful for v6)
* family. dhcp should be B_TRUE if looking for v4. Will only return the
* nwamd_if_address if the relevant address is configured (v4 DHCP, v6
*
* Returns NULL if structure is not found.
*/
static struct nwamd_if_address *
{
if (!dhcp)
return (NULL);
return (n);
return (n);
else if (!dhcp && n->stateless_if &&
return (n);
}
}
}
return (NULL);
}
/*
* Sets "configured" nwam_if_address value for corresponding address.
* Used when we process IF_STATE events to handle RTM_NEWADDR/DELADDRs.
*/
static boolean_t
{
struct nwamd_if_address *n;
char str[INET6_ADDRSTRLEN];
if (n) {
n->configured = configured;
"address %s",
return (B_TRUE);
}
return (B_FALSE);
}
void
{
n->configured = B_FALSE;
}
}
/*
* Are one or more static addresses configured?
*/
{
struct nwamd_if_address *n;
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.
*/
{
int numif, i;
struct sockaddr_storage addr;
int prefixlen;
ICFG_SUCCESS) {
return (B_TRUE);
}
for (i = 0; i < numif; i++) {
continue;
continue;
/* is this address an expected static one? */
!= NULL) {
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;
}
(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);
}
static boolean_t
{
if (u_if->nwamd_if_dhcp_requested) {
} else {
struct nwamd_if_address *a;
for (a = u_if->nwamd_if_list;
a = a->next)
/* Empty loop body */;
if (a != NULL)
}
return (anyv4_requested);
}
static boolean_t
{
if (u_if->nwamd_if_stateful_requested ||
} else {
struct nwamd_if_address *a;
for (a = u_if->nwamd_if_list;
a = a->next)
/* Empty loop body */;
if (a != 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
{
}
/* Callback to find if DHCP is running on the interface index */
static int
{
int index;
return (0);
}
"flags_set_for_ifindex_cb: icfg_get_index failed");
icfg_close(h);
return (0);
}
icfg_close(h);
return (0);
}
"flags_set_for_ifindex_cb: icfg_get_flags failed");
}
icfg_close(h);
}
static int
{
}
/*
* Is autoconf running on the interface with specified ifindex?
*/
static boolean_t
{
stateless_running_for_ifindex_cb) != 0);
}
static boolean_t
{
int index;
return (B_FALSE);
}
return (B_FALSE);
}
return (stateless_running_for_ifindex(index));
}
void
{
struct nwamd_if_address *n;
int num_configured_v4 = 0;
/*
* Add static addresses. For IPv4, we only use the physical interface
* (i.e. not a logical interface) if DHCP has not been requested and
* this is the first address to be configured.
*/
continue;
case AF_INET:
add_logical_if = (num_configured_v4 > 0 ||
break;
case AF_INET6:
break;
}
}
}
static int
lifnum_from_ifname(const char *ifname)
{
lifstr++;
}
return (0);
}
/*
* Copies the ifname (with lifnum) associated with the given address.
* Returns B_TRUE if a match is found, B_FASLE otherwise.
*/
static boolean_t
{
struct sockaddr_in6 addr;
!= ICFG_SUCCESS) {
return (B_FALSE);
}
for (i = 0; i < numif; i++) {
continue;
}
} else {
/* Compare addresses */
return (B_TRUE);
}
}
}
return (B_FALSE);
}
/*
* 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;
}
struct nwam_event_if_state *if_state;
struct nwamd_if_address *nifa;
int lifnum;
"nwamd_ncu_handle_if_state_event: addr %s %s",
"added" : "removed");
/* determine the interface name with lifnum */
if (if_state->nwe_addr_added) {
/* figure out the ifname for the address */
"nwamd_ncu_handle_if_state_event:"
"could not find ifname for %s", addrstr);
goto exit;
}
} else {
/*
* Figure out the ifname that had the address that was
* removed. The address is already gone from the
* interface, so cannot walk the interface list.
*/
struct nwamd_if_address *n;
== NULL &&
== NULL) {
"nwamd_ncu_handle_if_state_event: "
"could not find nwamd_if_address for %s",
addrstr);
goto exit;
}
}
/*
* Get interface flags using nwe_ifname as it is logical
* interface name.
*/
goto exit;
}
/*
* If the interface is unplumbed, icfg_get_flags()
* will fail. Don't exit, continue with empty flags.
*/
if (if_state->nwe_addr_added) {
goto exit;
}
}
/*
* 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).
*/
/*LINTED*/
== INADDR_ANY) {
struct sockaddr_in s;
int pfxlen;
!(flags & IFF_RUNNING) &&
"clear out addr %s on %s",
(void) icfg_set_addr(ifh,
}
goto exit;
}
}
/*
* Has 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.
*/
struct sockaddr_storage ifaddr;
int plen;
sizeof (struct sockaddr_in6);
"nwamd_ncu_handle_if_state_event: "
"address %s is not really gone from %s, "
"ignoring IF_STATE event",
goto exit;
}
}
if (if_state->nwe_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.
*
* [Note that since we use DHCP inform on interfaces
* with static addresses that they will also have the
* DHCP flag set on the interface.]
*
* Once we decide we want the address adjust the ncu
* state accordingly. For example if this address is
* enough move online.
*/
/* Figure out if we want to keep this address. */
if (static_addr) {
} else if (u_if->nwamd_if_dhcp_requested &&
B_TRUE);
} else if (u_if->nwamd_if_stateful_requested &&
B_TRUE);
} else if (u_if->nwamd_if_stateless_requested &&
B_FALSE);
} else {
/*
* This is something we didn't expect. Remove
* it by unplumbing the logical interface.
*/
if (u_if->nwamd_if_dhcp_requested &&
if (lifnum == 0) {
} else {
family);
}
goto exit;
}
/*
* 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.
*/
B_TRUE);
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.
*/
if (static_addr) {
(void) update_address_configured_value(addr,
/*
* The address is already gone. I'm not sure
* how we figure out if this address is
* stateful (DHCP) or stateless. When we
* are managing IPv6 more explicitly this will
* have to be done more carefully.
*/
}
if (flags & IFF_DUPLICATE) {
"nwamd_ncu_handle_if_state_event: "
"duplicate address detected on %s",
} else {
}
}
}
exit:
}
void
{
return;
}
}
/*
* This function downs any logical interface and just zeros the address off of
* the physical interface (logical interface 0). If you want to unplumb 0 then
* you need to call nwamd_unplumb_interface() directly.
*/
static void
{
return;
}
if (rc != ICFG_SUCCESS) {
return;
}
if (lifnum == 0) {
struct sockaddr_in6 addr;
sizeof (struct sockaddr_in6)) != ICFG_SUCCESS)
} else {
} else {
}
}
icfg_close(h);
}
static void
{
return;
}
/*
* Before unplumbing, do a DHCP release if lifnum is 0. Otherwise
* dhcpagent can get confused.
*/
if (rc != ICFG_SUCCESS) {
icfg_errmsg(rc));
return;
}
if (rc != ICFG_SUCCESS) {
"%s %s failed for %s: %s",
}
} else if (plumb) {
"couldn't bring %s up",
h->ifh_interface.if_name);
} else {
"icfg_get_flags failed on %s",
h->ifh_interface.if_name);
}
}
if (!plumb) {
switch (af) {
case AF_INET:
break;
case AF_INET6:
break;
}
}
icfg_close(h);
}
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 *
start_dhcp_thread(void *arg)
{
struct nwamd_dhcp_thread_arg *thread_arg;
int timeout;
char *name;
/* Try starting agent, though it may already be there */
if (rc == -1) {
goto failed;
}
/* Now allocate and send the request */
goto failed;
}
if (rc != 0) {
goto failed;
}
if (rc != 0) {
goto failed;
}
/*
* DHCP timed out: change state for this NCU and enqueue
* event to check NCU priority-groups. Only care for
* DHCP requests (not informs).
*/
char *object_name;
name);
!= NWAM_SUCCESS) {
"nwam_ncu_name_to_typed_name failed");
goto failed;
}
goto failed;
} else if (rc == DHCP_IPC_E_RUNNING) {
/*
* DHCP is already running. Check if IP address is
* already configured on the interface.
*/
struct sockaddr_in sin;
name);
"icfg_open failed on %s", name);
goto failed;
}
/* Get address */
"icfg_get_addr failed on %s: %s",
goto bail;
}
/* Check if 0.0.0.0 */
name);
goto bail;
}
/* valid address exists, get the flags, index of intf */
"icfg_get_flags failed on %s", name);
goto bail;
}
"icfg_get_index failed on %s", name);
goto bail;
}
/* synthesize an IF_STATE event with the intf's flags */
bail:
icfg_close(h);
goto failed;
} else if ((rc == DHCP_IPC_E_SOCKET ||
/*
* Retry DHCP request as we may have been unplumbing
* as part of the configuration phase.
*/
rc == DHCP_IPC_E_INVIF ?
if (rc == DHCP_IPC_E_INVIF)
(void) sleep(NWAMD_DHCP_RETRY_WAIT_TIME);
goto retry;
} else {
goto failed;
}
}
/* If status was the command, then output the results */
}
}
return (NULL);
}
void
{
struct nwamd_dhcp_thread_arg *arg;
"for dhcp request");
return;
}
(void) pthread_attr_init(&attr);
(void) pthread_attr_destroy(&attr);
return;
}
(void) pthread_attr_destroy(&attr);
}