llp.c revision 6ba597c56d749c61b4f783157f63196d7b2445f0
/*
* 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
* or http://www.opensolaris.org/os/licensing.
* 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.
*/
/*
* This file is here for legacy support.
*/
#include <atomic.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <libdllink.h>
#include <libscf.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <libnwam.h>
#include "known_wlans.h"
#include "llp.h"
#include "ncu.h"
#include "util.h"
/*
* This file formerly contained the routines that manipulate Link Layer
* Profiles (aka LLPs) and various support functions. Now only code
* necessary for parsing the legacy /etc/nwam/llp file on upgrade is included,
* since this legacy configuration needs to be translated into the User NCP.
*/
#define OUR_OLD_DHCP_WAIT_TIME_PROP_NAME "dhcp_wait_time"
#define OUR_OLD_USE_NET_SVC_PROP_NAME "use_net_svc"
#define OUR_OLD_IDLE_TIME_PROP_NAME "idle_time"
static struct qelem llp_list;
/*
* Global variable to hold the highest priority. Need to use the atomic
* integer arithmetic functions to update it.
*/
static uint32_t llp_highest_pri;
/* Specifies if static address has been configured in /etc/nwam/llp */
static boolean_t static_configured = B_FALSE;
static enum interface_type
find_if_type(const char *name)
{
uint32_t media;
enum interface_type type;
if (name == NULL) {
nlog(LOG_DEBUG, "find_if_type: no ifname; "
"returning IF_UNKNOWN");
return (IF_UNKNOWN);
}
type = IF_WIRED;
if (dladm_name2info(dld_handle, name, NULL, NULL, NULL, &media) !=
DLADM_STATUS_OK) {
if (strncmp(name, "ip.tun", 6) == 0 ||
strncmp(name, "ip6.tun", 7) == 0 ||
strncmp(name, "ip.6to4tun", 10) == 0)
/*
* We'll need to update our tunnel detection once
* the clearview/tun project is integrated; tunnel
* names won't necessarily be ip.tunN.
*/
type = IF_TUN;
} else if (media == DL_WIFI) {
type = IF_WIRELESS;
}
return (type);
}
static void
llp_list_free(void)
{
llp_t *llp;
while (llp_list.q_forw != &llp_list) {
llp = (llp_t *)llp_list.q_forw;
remque(&llp->llp_links);
free(llp->llp_ipv6addrstr);
free(llp->llp_ipv4addrstr);
free(llp);
}
}
static void
initialize_llp(void)
{
llp_list.q_forw = llp_list.q_back = &llp_list;
}
static llp_t *
llp_lookup(const char *link)
{
llp_t *llp;
if (link == NULL)
return (NULL);
for (llp = (llp_t *)llp_list.q_forw; llp != (llp_t *)&llp_list;
llp = (llp_t *)llp->llp_links.q_forw) {
if (strcmp(link, llp->llp_lname) == 0)
break;
}
if (llp == (llp_t *)&llp_list)
llp = NULL;
return (llp);
}
/*
* Create the named LLP with default settings. Called only in main thread.
*/
static llp_t *
llp_add(const char *name)
{
llp_t *llp;
if ((llp = calloc(1, sizeof (llp_t))) == NULL) {
nlog(LOG_ERR, "llp_add: cannot allocate LLP: %m");
return (NULL);
}
if (strlcpy(llp->llp_lname, name, sizeof (llp->llp_lname)) >=
sizeof (llp->llp_lname)) {
nlog(LOG_ERR, "llp_add: linkname '%s' too long; ignoring entry",
name);
free(llp);
return (NULL);
}
llp->llp_fileorder = llp->llp_pri =
atomic_add_32_nv(&llp_highest_pri, 1);
llp->llp_ipv4src = IPV4SRC_DHCP;
llp->llp_type = find_if_type(llp->llp_lname);
llp->llp_ipv6onlink = B_TRUE;
/*
* should be a no-op, but for now, make sure we only
* create llps for wired and wireless interfaces.
*/
if (llp->llp_type != IF_WIRED && llp->llp_type != IF_WIRELESS) {
nlog(LOG_ERR, "llp_add: wrong type of interface for %s", name);
free(llp);
return (NULL);
}
insque(&llp->llp_links, llp_list.q_back);
nlog(LOG_DEBUG, "llp_add: "
"created llp for link %s, priority %d", llp->llp_lname,
llp->llp_pri);
return (llp);
}
static int
parse_llp_config(void)
{
static const char STATICSTR[] = "static";
static const char DHCP[] = "dhcp";
static const char IPV6[] = "ipv6";
static const char NOIPV6[] = "noipv6";
static const char PRIORITY[] = "priority";
FILE *fp;
char line[LINE_MAX];
char *cp, *lasts, *lstr, *srcstr, *addrstr;
int lnum;
llp_t *llp;
initialize_llp();
fp = fopen(LLPFILE, "r+");
if (fp == NULL) {
if (errno == ENOENT)
return (errno);
nlog(LOG_ERR, "parse_llp_config: "
"open legacy LLP config file: %m");
return (-1);
}
for (lnum = 1; fgets(line, sizeof (line), fp) != NULL; lnum++) {
if (line[strlen(line) - 1] == '\n')
line[strlen(line) - 1] = '\0';
cp = line;
while (isspace(*cp))
cp++;
if (*cp == '#' || *cp == '\0')
continue;
nlog(LOG_DEBUG, "parse_llp_config: "
"parsing legacy LLP conf file line %d...", lnum);
if (((lstr = strtok_r(cp, " \t", &lasts)) == NULL) ||
((srcstr = strtok_r(NULL, " \t", &lasts)) == NULL)) {
nlog(LOG_ERR, "parse_llp_config: line %d: "
"not enough tokens; ignoring entry", lnum);
continue;
}
if ((llp = llp_lookup(lstr)) == NULL &&
(llp = llp_add(lstr)) == NULL) {
nlog(LOG_ERR, "parse_llp_config: line %d: "
"cannot add entry", lnum);
continue;
}
if (strcasecmp(srcstr, STATICSTR) == 0) {
if ((addrstr = strtok_r(NULL, " \t", &lasts)) == NULL ||
atoi(addrstr) == 0) { /* crude check for number */
nlog(LOG_ERR, "parse_llp_config: line %d: "
"missing ipaddr for static config", lnum);
} else if ((addrstr = strdup(addrstr)) == NULL) {
nlog(LOG_ERR, "parse_llp_config: line %d: "
"cannot save address", lnum);
} else {
free(llp->llp_ipv4addrstr);
llp->llp_ipv4src = IPV4SRC_STATIC;
llp->llp_ipv4addrstr = addrstr;
}
} else if (strcasecmp(srcstr, DHCP) == 0) {
llp->llp_ipv4src = IPV4SRC_DHCP;
} else if (strcasecmp(srcstr, IPV6) == 0) {
llp->llp_ipv6onlink = B_TRUE;
if ((addrstr = strtok_r(NULL, " \t", &lasts)) == NULL) {
(void) 0;
} else if ((addrstr = strdup(addrstr)) == NULL) {
nlog(LOG_ERR, "parse_llp_config: line %d: "
"cannot save address", lnum);
} else {
free(llp->llp_ipv6addrstr);
llp->llp_ipv6addrstr = addrstr;
}
} else if (strcasecmp(srcstr, NOIPV6) == 0) {
llp->llp_ipv6onlink = B_FALSE;
} else if (strcasecmp(srcstr, PRIORITY) == 0) {
if ((addrstr = strtok_r(NULL, " \t", &lasts)) == NULL) {
nlog(LOG_ERR,
"parse_llp_config: line %d: "
"missing priority value", lnum);
} else {
llp->llp_pri = atoi(addrstr);
}
} else {
nlog(LOG_ERR, "parse_llp_config: line %d: "
"unrecognized field '%s'", lnum, srcstr);
}
}
(void) fclose(fp);
return (0);
}
/*
* Translate legacy LLP config into the user NCP.
*/
static int
upgrade_llp_config(void)
{
llp_t *wp;
nwam_ncp_handle_t user_ncp;
nwam_ncu_handle_t phys_ncu = NULL, ip_ncu = NULL;
nwam_error_t err;
uint64_t uintval;
char *strval;
const char *prop;
switch (parse_llp_config()) {
case -1:
return (0);
case ENOENT:
return (ENOENT);
default:
break;
}
if ((err = nwam_ncp_create(NWAM_NCP_NAME_USER, 0, &user_ncp))
!= NWAM_SUCCESS) {
nlog(LOG_ERR, "upgrade_llp_config: "
"could not create user NCP: %s", nwam_strerror(err));
llp_list_free();
return (0);
}
nlog(LOG_DEBUG, "upgrade_llp_config: walking llp list");
for (wp = (llp_t *)llp_list.q_forw; wp != (llp_t *)&llp_list;
wp = (llp_t *)wp->llp_links.q_forw) {
nlog(LOG_DEBUG, "upgrade_llp_config: "
"upgrading llp %s", wp->llp_lname);
if (nwam_ncu_create(user_ncp, wp->llp_lname,
NWAM_NCU_TYPE_INTERFACE, NWAM_NCU_CLASS_IP, &ip_ncu)
!= NWAM_SUCCESS ||
nwam_ncu_create(user_ncp, wp->llp_lname, NWAM_NCU_TYPE_LINK,
NWAM_NCU_CLASS_PHYS, &phys_ncu) != NWAM_SUCCESS) {
nlog(LOG_ERR, "upgrade_llp_config: llp %s: "
"could not create NCUs: %s", wp->llp_lname,
nwam_strerror(err));
break;
}
/* Link NCU properties */
prop = NWAM_NCU_PROP_ACTIVATION_MODE;
uintval = NWAM_ACTIVATION_MODE_PRIORITIZED;
if ((err = nwamd_set_ncu_uint(phys_ncu, &uintval, 1, prop))
!= NWAM_SUCCESS)
break;
prop = NWAM_NCU_PROP_PRIORITY_MODE;
uintval = NWAM_PRIORITY_MODE_EXCLUSIVE;
if ((err = nwamd_set_ncu_uint(phys_ncu, &uintval, 1, prop))
!= NWAM_SUCCESS)
break;
prop = NWAM_NCU_PROP_PRIORITY_GROUP;
uintval = wp->llp_pri;
if ((err = nwamd_set_ncu_uint(phys_ncu, &uintval, 1, prop))
!= NWAM_SUCCESS)
break;
/* IP NCU properties */
if (wp->llp_ipv4addrstr != NULL) {
/* Set v4 address and specify static addrsrc */
prop = NWAM_NCU_PROP_IPV4_ADDRSRC;
uintval = NWAM_ADDRSRC_STATIC;
if ((err = nwamd_set_ncu_uint(ip_ncu, &uintval, 1,
prop)) != NWAM_SUCCESS)
break;
prop = NWAM_NCU_PROP_IPV4_ADDR;
strval = wp->llp_ipv4addrstr;
if ((err = nwamd_set_ncu_string(ip_ncu, &strval, 1,
prop)) != NWAM_SUCCESS)
break;
static_configured = B_TRUE;
}
if (wp->llp_ipv6addrstr != NULL) {
/* Set v6 address and specify static addrsrc */
prop = NWAM_NCU_PROP_IPV6_ADDRSRC;
uintval = NWAM_ADDRSRC_STATIC;
if ((err = nwamd_set_ncu_uint(ip_ncu, &uintval, 1,
prop)) != NWAM_SUCCESS)
break;
prop = NWAM_NCU_PROP_IPV6_ADDR;
strval = wp->llp_ipv6addrstr;
if ((err = nwamd_set_ncu_string(ip_ncu, &strval, 1,
prop)) != NWAM_SUCCESS)
break;
static_configured = B_TRUE;
}
if (!wp->llp_ipv6onlink) {
prop = NWAM_NCU_PROP_IP_VERSION;
uintval = IPV4_VERSION;
if ((err = nwamd_set_ncu_uint(ip_ncu, &uintval, 1,
prop)) != NWAM_SUCCESS)
break;
}
if ((err = nwam_ncu_commit(ip_ncu, 0)) != NWAM_SUCCESS ||
(err = nwam_ncu_commit(phys_ncu, 0)) != NWAM_SUCCESS) {
nlog(LOG_ERR, "upgrade_llp_config: llp %s: "
"could not commit NCUs: %s", wp->llp_lname,
nwam_strerror(err));
/* Schedule a retry - root filesystem may be readonly */
llp_list_free();
return (EAGAIN);
}
nwam_ncu_free(ip_ncu);
nwam_ncu_free(phys_ncu);
}
if (err != NWAM_SUCCESS) {
nlog(LOG_ERR, "upgrade_llp_config: llp %s: "
"could not set value for property %s: %s", wp->llp_lname,
prop, nwam_strerror(err));
nwam_ncu_free(ip_ncu);
nwam_ncu_free(phys_ncu);
exit(EXIT_FAILURE);
}
llp_list_free();
return (0);
}
/*
* Upgrade legacy llp and known_wifi_nets files. Note - it is possible that
* the root filesystem is not writable at this point, so we need to schedule
* a retry of the upgrade operation in the event that committing the new
* config fails.
*/
/* ARGSUSED0 */
void
nwamd_handle_upgrade(nwamd_event_t event)
{
nwamd_event_t upgrade_event;
uint64_t dhcp_wait_time, idle_time;
boolean_t use_net_svc;
switch (upgrade_llp_config()) {
case -1:
case ENOENT:
/* Nothing readable to upgrade */
break;
case EAGAIN:
/*
* Schedule retry in NWAMD_READONLY_RETRY_INTERVAL seconds
* as root fs may be readonly.
*
* The upgrade event is of type NCU, but has no associated
* object (we use the event type to map to the appropriate
* event/method mappings, so to find the NCU upgrade event
* method we specify type NCU while not specifying an
* object since all NCUs have to be upgraded.
*/
upgrade_event = nwamd_event_init(NWAM_EVENT_TYPE_UPGRADE,
NWAM_OBJECT_TYPE_NCP, 0, NULL);
if (upgrade_event == NULL)
return;
nwamd_event_enqueue_timed(upgrade_event,
NWAMD_READONLY_RETRY_INTERVAL);
return;
default:
break;
}
/*
* If static_configured is set, then at least one static address is
* configured in /etc/nwam/llp. Enable the User NCP in this case.
*/
if (static_configured) {
nlog(LOG_DEBUG, "nwamd_handle_upgrade: "
"static address configured, enabling User NCP");
(void) pthread_mutex_lock(&active_ncp_mutex);
(void) strlcpy(active_ncp, NWAM_NCP_NAME_USER,
NWAM_MAX_NAME_LEN);
(void) pthread_mutex_unlock(&active_ncp_mutex);
}
/* upgrade /etc/nwam/known_wifi_nets */
upgrade_known_wifi_nets_config();
/*
* SMF property nwamd/dhcp_wait_time in Phase 0/0.5 has been
* replaced by nwamd/ncu_wait_time property. If the dhcp_wait_time
* property exists (which means it has been changed by the user),
* set its value to ncu_wait_time and remove the property.
*/
if (nwamd_lookup_count_property(OUR_FMRI, OUR_PG,
OUR_OLD_DHCP_WAIT_TIME_PROP_NAME, &dhcp_wait_time) == 0) {
(void) nwamd_set_count_property(OUR_FMRI, OUR_PG,
OUR_NCU_WAIT_TIME_PROP_NAME, dhcp_wait_time);
(void) nwamd_delete_scf_property(OUR_FMRI, OUR_PG,
OUR_OLD_DHCP_WAIT_TIME_PROP_NAME);
nlog(LOG_DEBUG, "nwamd_handle_upgrade: "
"converted '%s' to '%s' with value of %lld",
OUR_OLD_DHCP_WAIT_TIME_PROP_NAME,
OUR_NCU_WAIT_TIME_PROP_NAME, dhcp_wait_time);
}
/*
* If the user has changed Phase 0/0.5 properties that don't exist in
* Phase 1, manifest-import reports a warning; but those properties are
* not removed. nwamd/use_net_svc and nwamd/idle_time are two
* properties that don't exist in Phase 1. If they exist, remove them.
*/
if (nwamd_lookup_count_property(OUR_FMRI, OUR_PG,
OUR_OLD_IDLE_TIME_PROP_NAME, &idle_time) == 0) {
(void) nwamd_delete_scf_property(OUR_FMRI, OUR_PG,
OUR_OLD_IDLE_TIME_PROP_NAME);
}
if (nwamd_lookup_boolean_property(OUR_FMRI, OUR_PG,
OUR_OLD_USE_NET_SVC_PROP_NAME, &use_net_svc) == 0) {
(void) nwamd_delete_scf_property(OUR_FMRI, OUR_PG,
OUR_OLD_USE_NET_SVC_PROP_NAME);
}
nlog(LOG_DEBUG, "nwamd_handle_upgrade: "
"creating version property, setting to 1\n");
(void) nwamd_set_count_property(OUR_FMRI, OUR_PG,
OUR_VERSION_PROP_NAME, 1U);
(void) smf_refresh_instance(OUR_FMRI);
}