diff options
Diffstat (limited to 'usr/src/lib/libdladm/common/libdlbridge.c')
| -rw-r--r-- | usr/src/lib/libdladm/common/libdlbridge.c | 1577 |
1 files changed, 1577 insertions, 0 deletions
diff --git a/usr/src/lib/libdladm/common/libdlbridge.c b/usr/src/lib/libdladm/common/libdlbridge.c new file mode 100644 index 0000000000..6b432b5560 --- /dev/null +++ b/usr/src/lib/libdladm/common/libdlbridge.c @@ -0,0 +1,1577 @@ +/* + * 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 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include <stdio.h> +#include <sys/types.h> +#include <string.h> +#include <fcntl.h> +#include <unistd.h> +#include <stropts.h> +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> +#include <door.h> +#include <sys/mman.h> +#include <libscf.h> +#include <libscf_priv.h> +#include <libdllink.h> +#include <libdlbridge.h> +#include <libdladm_impl.h> +#include <stp_in.h> +#include <net/bridge.h> +#include <net/trill.h> +#include <sys/socket.h> + +/* + * Bridge Administration Library. + * + * This library is used by administration tools such as dladm(1M) to configure + * bridges, and by the bridge daemon to retrieve configuration information. + */ + +#define BRIDGE_SVC_NAME "network/bridge" +#define TRILL_SVC_NAME "network/routing/trill" + +#define DEFAULT_TIMEOUT 60000000 +#define INIT_WAIT_USECS 50000 +#define MAXPORTS 256 + +typedef struct scf_state { + scf_handle_t *ss_handle; + scf_instance_t *ss_inst; + scf_service_t *ss_svc; + scf_snapshot_t *ss_snap; + scf_propertygroup_t *ss_pg; + scf_property_t *ss_prop; +} scf_state_t; + +static void +shut_down_scf(scf_state_t *sstate) +{ + scf_instance_destroy(sstate->ss_inst); + (void) scf_handle_unbind(sstate->ss_handle); + scf_handle_destroy(sstate->ss_handle); +} + +static char * +alloc_fmri(const char *service, const char *instance_name) +{ + ssize_t max_fmri; + char *fmri; + + /* If the limit is unknown, then use an arbitrary value */ + if ((max_fmri = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH)) == -1) + max_fmri = 1024; + if ((fmri = malloc(max_fmri)) != NULL) { + (void) snprintf(fmri, max_fmri, "svc:/%s:%s", service, + instance_name); + } + return (fmri); +} + +/* + * Start up SCF and bind the requested instance alone. + */ +static int +bind_instance(const char *service, const char *instance_name, + scf_state_t *sstate) +{ + char *fmri = NULL; + + (void) memset(sstate, 0, sizeof (*sstate)); + + if ((sstate->ss_handle = scf_handle_create(SCF_VERSION)) == NULL) + return (-1); + + if (scf_handle_bind(sstate->ss_handle) != 0) + goto failure; + sstate->ss_inst = scf_instance_create(sstate->ss_handle); + if (sstate->ss_inst == NULL) + goto failure; + + fmri = alloc_fmri(service, instance_name); + + if (scf_handle_decode_fmri(sstate->ss_handle, fmri, NULL, NULL, + sstate->ss_inst, NULL, NULL, + SCF_DECODE_FMRI_REQUIRE_INSTANCE) != 0) + goto failure; + free(fmri); + return (0); + +failure: + free(fmri); + shut_down_scf(sstate); + return (-1); +} + +/* + * Start up SCF and an exact FMRI. This is used for creating new instances and + * enable/disable actions. + */ +static dladm_status_t +exact_instance(const char *fmri, scf_state_t *sstate) +{ + dladm_status_t status; + + (void) memset(sstate, 0, sizeof (*sstate)); + + if ((sstate->ss_handle = scf_handle_create(SCF_VERSION)) == NULL) + return (DLADM_STATUS_NOMEM); + + status = DLADM_STATUS_FAILED; + if (scf_handle_bind(sstate->ss_handle) != 0) + goto failure; + sstate->ss_svc = scf_service_create(sstate->ss_handle); + if (sstate->ss_svc == NULL) + goto failure; + if (scf_handle_decode_fmri(sstate->ss_handle, fmri, NULL, + sstate->ss_svc, NULL, NULL, NULL, SCF_DECODE_FMRI_EXACT) != 0) { + if (scf_error() == SCF_ERROR_NOT_FOUND) + status = DLADM_STATUS_OPTMISSING; + goto failure; + } + sstate->ss_inst = scf_instance_create(sstate->ss_handle); + if (sstate->ss_inst == NULL) + goto failure; + return (DLADM_STATUS_OK); + +failure: + shut_down_scf(sstate); + return (status); +} + +static void +drop_composed(scf_state_t *sstate) +{ + scf_property_destroy(sstate->ss_prop); + scf_pg_destroy(sstate->ss_pg); + scf_snapshot_destroy(sstate->ss_snap); +} + +/* + * This function sets up a composed view of the configuration information for + * the specified instance. When this is done, the get_property() function + * should be able to return individual parameters. + */ +static int +get_composed_properties(const char *lpg, boolean_t snap, scf_state_t *sstate) +{ + sstate->ss_snap = NULL; + sstate->ss_pg = NULL; + sstate->ss_prop = NULL; + + if (snap) { + sstate->ss_snap = scf_snapshot_create(sstate->ss_handle); + if (sstate->ss_snap == NULL) + goto failure; + if (scf_instance_get_snapshot(sstate->ss_inst, "running", + sstate->ss_snap) != 0) + goto failure; + } + if ((sstate->ss_pg = scf_pg_create(sstate->ss_handle)) == NULL) + goto failure; + if (scf_instance_get_pg_composed(sstate->ss_inst, sstate->ss_snap, lpg, + sstate->ss_pg) != 0) + goto failure; + if ((sstate->ss_prop = scf_property_create(sstate->ss_handle)) == + NULL) + goto failure; + return (0); + +failure: + drop_composed(sstate); + return (-1); +} + +static int +get_count(const char *lprop, scf_state_t *sstate, uint64_t *answer) +{ + scf_value_t *val; + int retv; + + if (scf_pg_get_property(sstate->ss_pg, lprop, sstate->ss_prop) != 0) + return (-1); + if ((val = scf_value_create(sstate->ss_handle)) == NULL) + return (-1); + + if (scf_property_get_value(sstate->ss_prop, val) == 0 && + scf_value_get_count(val, answer) == 0) + retv = 0; + else + retv = -1; + scf_value_destroy(val); + return (retv); +} + +static int +get_boolean(const char *lprop, scf_state_t *sstate, boolean_t *answer) +{ + scf_value_t *val; + int retv; + uint8_t bval; + + if (scf_pg_get_property(sstate->ss_pg, lprop, sstate->ss_prop) != 0) + return (-1); + if ((val = scf_value_create(sstate->ss_handle)) == NULL) + return (-1); + + if (scf_property_get_value(sstate->ss_prop, val) == 0 && + scf_value_get_boolean(val, &bval) == 0) { + retv = 0; + *answer = bval != 0; + } else { + retv = -1; + } + scf_value_destroy(val); + return (retv); +} + +static dladm_status_t +bridge_door_call(const char *instname, bridge_door_type_t dtype, + datalink_id_t linkid, void **bufp, size_t inlen, size_t *buflenp, + boolean_t is_list) +{ + char doorname[MAXPATHLEN]; + int did, retv, etmp; + bridge_door_cmd_t *bdc; + door_arg_t arg; + + (void) snprintf(doorname, sizeof (doorname), "%s/%s", DOOR_DIRNAME, + instname); + + /* Knock on the door */ + did = open(doorname, O_RDONLY | O_NOFOLLOW | O_NONBLOCK); + if (did == -1) + return (dladm_errno2status(errno)); + + if ((bdc = malloc(sizeof (*bdc) + inlen)) == NULL) { + (void) close(did); + return (DLADM_STATUS_NOMEM); + } + bdc->bdc_type = dtype; + bdc->bdc_linkid = linkid; + if (inlen != 0) + (void) memcpy(bdc + 1, *bufp, inlen); + + (void) memset(&arg, 0, sizeof (arg)); + arg.data_ptr = (char *)bdc; + arg.data_size = sizeof (*bdc) + inlen; + arg.rbuf = *bufp; + arg.rsize = *buflenp; + + /* The door_call function doesn't restart, so take care of that */ + do { + errno = 0; + if ((retv = door_call(did, &arg)) == 0) + break; + } while (errno == EINTR); + + /* If we get an unexpected response, then return an error */ + if (retv == 0) { + /* The daemon returns a single int for errors */ + /* LINTED: pointer alignment */ + if (arg.data_size == sizeof (int) && *(int *)arg.rbuf != 0) { + retv = -1; + /* LINTED: pointer alignment */ + errno = *(int *)arg.rbuf; + } + /* Terminated daemon returns with zero data */ + if (arg.data_size == 0) { + retv = -1; + errno = EBADF; + } + } + + if (retv == 0) { + if (arg.rbuf != *bufp) { + if (is_list) { + void *newp; + + newp = realloc(*bufp, arg.data_size); + if (newp == NULL) { + retv = -1; + } else { + *bufp = newp; + (void) memcpy(*bufp, arg.rbuf, + arg.data_size); + } + } + (void) munmap(arg.rbuf, arg.rsize); + } + if (is_list) { + *buflenp = arg.data_size; + } else if (arg.data_size != *buflenp || arg.rbuf != *bufp) { + errno = EINVAL; + retv = -1; + } + } + + etmp = errno; + (void) close(did); + + /* Revoked door is the same as no door at all */ + if (etmp == EBADF) + etmp = ENOENT; + + return (retv == 0 ? DLADM_STATUS_OK : dladm_errno2status(etmp)); +} + +/* + * Wrapper function for making per-port calls. + */ +static dladm_status_t +port_door_call(dladm_handle_t handle, datalink_id_t linkid, + bridge_door_type_t dtype, void *buf, size_t inlen, size_t buflen) +{ + char bridge[MAXLINKNAMELEN]; + dladm_status_t status; + + status = dladm_bridge_getlink(handle, linkid, bridge, sizeof (bridge)); + if (status != DLADM_STATUS_OK) + return (status); + return (bridge_door_call(bridge, dtype, linkid, &buf, inlen, &buflen, + B_FALSE)); +} + +static dladm_status_t +bridge_refresh(const char *bridge) +{ + dladm_status_t status; + int twoints[2]; + void *bdptr; + size_t buflen; + char *fmri; + int refresh_count; + + buflen = sizeof (twoints); + bdptr = twoints; + status = bridge_door_call(bridge, bdcBridgeGetRefreshCount, + DATALINK_INVALID_LINKID, &bdptr, 0, &buflen, B_FALSE); + if (status == DLADM_STATUS_NOTFOUND) + return (DLADM_STATUS_OK); + if (status != DLADM_STATUS_OK) + return (status); + refresh_count = twoints[0]; + if ((fmri = alloc_fmri(BRIDGE_SVC_NAME, bridge)) == NULL) + return (DLADM_STATUS_NOMEM); + status = smf_refresh_instance(fmri) == 0 ? + DLADM_STATUS_OK : DLADM_STATUS_FAILED; + free(fmri); + if (status == DLADM_STATUS_OK) { + int i = 0; + + /* + * SMF doesn't give any synchronous behavior or dependency + * ordering for refresh operations, so we have to invent our + * own mechanism here. Get the refresh counter from the + * daemon, and wait for it to change. It's not pretty, but + * it's sufficient. + */ + while (++i <= 10) { + buflen = sizeof (twoints); + bdptr = twoints; + status = bridge_door_call(bridge, + bdcBridgeGetRefreshCount, DATALINK_INVALID_LINKID, + &bdptr, 0, &buflen, B_FALSE); + if (status != DLADM_STATUS_OK) + break; + if (twoints[0] != refresh_count) + break; + (void) usleep(100000); + } + fmri = alloc_fmri(TRILL_SVC_NAME, bridge); + if (fmri == NULL) + return (DLADM_STATUS_NOMEM); + status = smf_refresh_instance(fmri) == 0 || + scf_error() == SCF_ERROR_NOT_FOUND ? + DLADM_STATUS_OK : DLADM_STATUS_FAILED; + free(fmri); + } + return (status); +} + +/* + * Look up bridge property values from SCF and return them. + */ +dladm_status_t +dladm_bridge_get_properties(const char *instance_name, UID_STP_CFG_T *cfg, + dladm_bridge_prot_t *brprotp) +{ + scf_state_t sstate; + uint64_t value; + boolean_t trill_enabled; + + cfg->field_mask = 0; + cfg->bridge_priority = DEF_BR_PRIO; + cfg->max_age = DEF_BR_MAXAGE; + cfg->hello_time = DEF_BR_HELLOT; + cfg->forward_delay = DEF_BR_FWDELAY; + cfg->force_version = DEF_FORCE_VERS; + + (void) strlcpy(cfg->vlan_name, instance_name, sizeof (cfg->vlan_name)); + + *brprotp = DLADM_BRIDGE_PROT_STP; + + /* It's ok for this to be missing; it's installed separately */ + if (bind_instance(TRILL_SVC_NAME, instance_name, &sstate) == 0) { + trill_enabled = B_FALSE; + if (get_composed_properties(SCF_PG_GENERAL, B_FALSE, &sstate) == + 0) { + (void) get_boolean(SCF_PROPERTY_ENABLED, &sstate, + &trill_enabled); + if (trill_enabled) + *brprotp = DLADM_BRIDGE_PROT_TRILL; + drop_composed(&sstate); + } + if (get_composed_properties(SCF_PG_GENERAL_OVR, B_FALSE, + &sstate) == 0) { + (void) get_boolean(SCF_PROPERTY_ENABLED, &sstate, + &trill_enabled); + if (trill_enabled) + *brprotp = DLADM_BRIDGE_PROT_TRILL; + drop_composed(&sstate); + } + shut_down_scf(&sstate); + } + + cfg->stp_enabled = (*brprotp == DLADM_BRIDGE_PROT_STP) ? + STP_ENABLED : STP_DISABLED; + cfg->field_mask |= BR_CFG_STATE; + + if (bind_instance(BRIDGE_SVC_NAME, instance_name, &sstate) != 0) + return (DLADM_STATUS_REPOSITORYINVAL); + + if (get_composed_properties("config", B_TRUE, &sstate) != 0) { + shut_down_scf(&sstate); + return (DLADM_STATUS_REPOSITORYINVAL); + } + + if (get_count("priority", &sstate, &value) == 0) { + cfg->bridge_priority = value; + cfg->field_mask |= BR_CFG_PRIO; + } + if (get_count("max-age", &sstate, &value) == 0) { + cfg->max_age = value / IEEE_TIMER_SCALE; + cfg->field_mask |= BR_CFG_AGE; + } + if (get_count("hello-time", &sstate, &value) == 0) { + cfg->hello_time = value / IEEE_TIMER_SCALE; + cfg->field_mask |= BR_CFG_HELLO; + } + if (get_count("forward-delay", &sstate, &value) == 0) { + cfg->forward_delay = value / IEEE_TIMER_SCALE; + cfg->field_mask |= BR_CFG_DELAY; + } + if (get_count("force-protocol", &sstate, &value) == 0) { + cfg->force_version = value; + cfg->field_mask |= BR_CFG_FORCE_VER; + } + + drop_composed(&sstate); + shut_down_scf(&sstate); + return (DLADM_STATUS_OK); +} + +/* + * Retrieve special non-settable and undocumented parameters. + */ +dladm_status_t +dladm_bridge_get_privprop(const char *instance_name, boolean_t *debugp, + uint32_t *tablemaxp) +{ + scf_state_t sstate; + uint64_t value; + + *debugp = B_FALSE; + *tablemaxp = 10000; + + if (bind_instance(BRIDGE_SVC_NAME, instance_name, &sstate) != 0) + return (DLADM_STATUS_REPOSITORYINVAL); + + if (get_composed_properties("config", B_TRUE, &sstate) != 0) { + shut_down_scf(&sstate); + return (DLADM_STATUS_REPOSITORYINVAL); + } + + (void) get_boolean("debug", &sstate, debugp); + if (get_count("table-maximum", &sstate, &value) == 0) + *tablemaxp = (uint32_t)value; + + drop_composed(&sstate); + shut_down_scf(&sstate); + return (DLADM_STATUS_OK); +} + +static boolean_t +set_count_property(scf_handle_t *handle, scf_transaction_t *tran, + const char *propname, uint64_t propval) +{ + scf_transaction_entry_t *entry; + scf_value_t *value = NULL; + + if ((entry = scf_entry_create(handle)) == NULL) + return (B_FALSE); + + if ((value = scf_value_create(handle)) == NULL) + goto out; + if (scf_transaction_property_new(tran, entry, propname, + SCF_TYPE_COUNT) != 0 && + scf_transaction_property_change(tran, entry, propname, + SCF_TYPE_COUNT) != 0) + goto out; + scf_value_set_count(value, propval); + if (scf_entry_add_value(entry, value) == 0) + return (B_TRUE); + +out: + if (value != NULL) + scf_value_destroy(value); + + scf_entry_destroy_children(entry); + scf_entry_destroy(entry); + + return (B_FALSE); +} + +static boolean_t +set_string_property(scf_handle_t *handle, scf_transaction_t *tran, + const char *propname, const char *propval) +{ + scf_transaction_entry_t *entry; + scf_value_t *value = NULL; + + if ((entry = scf_entry_create(handle)) == NULL) + return (B_FALSE); + + if ((value = scf_value_create(handle)) == NULL) + goto out; + if (scf_transaction_property_new(tran, entry, propname, + SCF_TYPE_ASTRING) != 0 && + scf_transaction_property_change(tran, entry, propname, + SCF_TYPE_ASTRING) != 0) + goto out; + if (scf_value_set_astring(value, propval) != 0) + goto out; + if (scf_entry_add_value(entry, value) == 0) + return (B_TRUE); + +out: + if (value != NULL) + scf_value_destroy(value); + + scf_entry_destroy_children(entry); + scf_entry_destroy(entry); + + return (B_FALSE); +} + +static boolean_t +set_fmri_property(scf_handle_t *handle, scf_transaction_t *tran, + const char *propname, const char *propval) +{ + scf_transaction_entry_t *entry; + scf_value_t *value = NULL; + + if ((entry = scf_entry_create(handle)) == NULL) + return (B_FALSE); + + if ((value = scf_value_create(handle)) == NULL) + goto out; + if (scf_transaction_property_new(tran, entry, propname, + SCF_TYPE_FMRI) != 0 && + scf_transaction_property_change(tran, entry, propname, + SCF_TYPE_FMRI) != 0) + goto out; + if (scf_value_set_from_string(value, SCF_TYPE_FMRI, propval) != 0) + goto out; + if (scf_entry_add_value(entry, value) == 0) + return (B_TRUE); + +out: + if (value != NULL) + scf_value_destroy(value); + + scf_entry_destroy_children(entry); + scf_entry_destroy(entry); + + return (B_FALSE); +} + +static dladm_status_t +dladm_bridge_persist_conf(dladm_handle_t handle, const char *link, + datalink_id_t linkid) +{ + dladm_conf_t conf = DLADM_INVALID_CONF; + dladm_status_t status; + + status = dladm_create_conf(handle, link, linkid, DATALINK_CLASS_BRIDGE, + DL_ETHER, &conf); + if (status == DLADM_STATUS_OK) { + /* + * Create the datalink entry for the bridge. Note that all of + * the real configuration information is in SMF. + */ + status = dladm_write_conf(handle, conf); + dladm_destroy_conf(handle, conf); + } + return (status); +} + +/* Convert bridge protection option string to dladm_bridge_prot_t */ +dladm_status_t +dladm_bridge_str2prot(const char *str, dladm_bridge_prot_t *brprotp) +{ + if (strcmp(str, "stp") == 0) + *brprotp = DLADM_BRIDGE_PROT_STP; + else if (strcmp(str, "trill") == 0) + *brprotp = DLADM_BRIDGE_PROT_TRILL; + else + return (DLADM_STATUS_BADARG); + return (DLADM_STATUS_OK); +} + +/* Convert bridge protection option from dladm_bridge_prot_t to string */ +const char * +dladm_bridge_prot2str(dladm_bridge_prot_t brprot) +{ + switch (brprot) { + case DLADM_BRIDGE_PROT_STP: + return ("stp"); + case DLADM_BRIDGE_PROT_TRILL: + return ("trill"); + default: + return ("unknown"); + } +} + +static dladm_status_t +enable_instance(const char *service_name, const char *instance) +{ + dladm_status_t status; + char *fmri = alloc_fmri(service_name, instance); + + if (fmri == NULL) + return (DLADM_STATUS_NOMEM); + status = smf_enable_instance(fmri, 0) == 0 ? + DLADM_STATUS_OK : DLADM_STATUS_FAILED; + free(fmri); + return (status); +} + +/* + * Shut down a possibly-running service instance. If this is a permanent + * change, then delete it from the system. + */ +static dladm_status_t +shut_down_instance(const char *service_name, const char *instance, + uint32_t flags) +{ + dladm_status_t status; + char *fmri = alloc_fmri(service_name, instance); + char *state; + scf_state_t sstate; + + if (fmri == NULL) + return (DLADM_STATUS_NOMEM); + + if (smf_disable_instance(fmri, + flags & DLADM_OPT_PERSIST ? 0 : SMF_TEMPORARY) == 0) { + useconds_t usecs, umax; + + /* If we can disable, then wait for it to happen. */ + umax = DEFAULT_TIMEOUT; + for (usecs = INIT_WAIT_USECS; umax != 0; umax -= usecs) { + state = smf_get_state(fmri); + if (state != NULL && + strcmp(state, SCF_STATE_STRING_DISABLED) == 0) + break; + free(state); + usecs *= 2; + if (usecs > umax) + usecs = umax; + (void) usleep(usecs); + } + if (umax == 0) { + state = smf_get_state(fmri); + if (state != NULL && + strcmp(state, SCF_STATE_STRING_DISABLED) == 0) + umax = 1; + } + free(state); + status = umax != 0 ? DLADM_STATUS_OK : DLADM_STATUS_FAILED; + } else if (scf_error() == SCF_ERROR_NOT_FOUND) { + free(fmri); + return (DLADM_STATUS_OK); + } else { + status = DLADM_STATUS_FAILED; + } + + free(fmri); + if (status == DLADM_STATUS_OK && (flags & DLADM_OPT_PERSIST) && + bind_instance(service_name, instance, &sstate) == 0) { + (void) scf_instance_delete(sstate.ss_inst); + shut_down_scf(&sstate); + } + + return (status); +} + +static dladm_status_t +disable_trill(const char *instance, uint32_t flags) +{ + return (shut_down_instance(TRILL_SVC_NAME, instance, flags)); +} + +/* + * To enable TRILL, we must create a new instance of the TRILL service, then + * add proper dependencies to it, and finally mark it as enabled. The + * dependencies will keep it from going on-line until the bridge is running. + */ +static dladm_status_t +enable_trill(const char *instance) +{ + dladm_status_t status = DLADM_STATUS_FAILED; + char *fmri = NULL; + scf_state_t sstate; + scf_transaction_t *tran = NULL; + boolean_t new_instance = B_FALSE; + boolean_t new_pg = B_FALSE; + int rv; + + /* + * This check is here in case the user has installed and then removed + * the package. SMF should remove the manifest, but currently does + * not. + */ + if (access("/usr/sbin/trilld", F_OK) != 0) + return (DLADM_STATUS_OPTMISSING); + + if ((status = exact_instance(TRILL_SVC_NAME, &sstate)) != + DLADM_STATUS_OK) + goto out; + + status = DLADM_STATUS_FAILED; + if (scf_service_get_instance(sstate.ss_svc, instance, sstate.ss_inst) != + 0) { + if (scf_service_add_instance(sstate.ss_svc, instance, + sstate.ss_inst) != 0) + goto out; + new_instance = B_TRUE; + } + + if ((tran = scf_transaction_create(sstate.ss_handle)) == NULL) + goto out; + + if ((sstate.ss_pg = scf_pg_create(sstate.ss_handle)) == NULL) + goto out; + + if (scf_instance_get_pg(sstate.ss_inst, "bridging", + sstate.ss_pg) == 0) { + status = DLADM_STATUS_OK; + goto out; + } + + if ((fmri = alloc_fmri(BRIDGE_SVC_NAME, instance)) == NULL) + goto out; + + if (scf_instance_add_pg(sstate.ss_inst, "bridging", + SCF_GROUP_DEPENDENCY, 0, sstate.ss_pg) != 0) + goto out; + + new_pg = B_TRUE; + do { + if (scf_transaction_start(tran, sstate.ss_pg) != 0) + goto out; + + if (!set_string_property(sstate.ss_handle, tran, + SCF_PROPERTY_GROUPING, SCF_DEP_REQUIRE_ALL)) + goto out; + if (!set_string_property(sstate.ss_handle, tran, + SCF_PROPERTY_RESTART_ON, SCF_DEP_RESET_ON_RESTART)) + goto out; + if (!set_string_property(sstate.ss_handle, tran, + SCF_PROPERTY_TYPE, "service")) + goto out; + if (!set_fmri_property(sstate.ss_handle, tran, + SCF_PROPERTY_ENTITIES, fmri)) + goto out; + + rv = scf_transaction_commit(tran); + scf_transaction_reset(tran); + if (rv == 0 && scf_pg_update(sstate.ss_pg) == -1) + goto out; + } while (rv == 0); + if (rv != 1) + goto out; + + status = DLADM_STATUS_OK; + +out: + free(fmri); + if (tran != NULL) { + scf_transaction_destroy_children(tran); + scf_transaction_destroy(tran); + } + + if (status != DLADM_STATUS_OK && new_pg) + (void) scf_pg_delete(sstate.ss_pg); + + drop_composed(&sstate); + + /* + * If we created an instance and then failed, then remove the instance + * from the system. + */ + if (status != DLADM_STATUS_OK && new_instance) + (void) scf_instance_delete(sstate.ss_inst); + + shut_down_scf(&sstate); + + if (status == DLADM_STATUS_OK) + status = enable_instance(TRILL_SVC_NAME, instance); + + return (status); +} + +/* + * Create a new bridge or modify an existing one. Update the SMF configuration + * and add links. + * + * Input timer values are in IEEE scaled (* 256) format. + */ +dladm_status_t +dladm_bridge_configure(dladm_handle_t handle, const char *name, + const UID_STP_CFG_T *cfg, dladm_bridge_prot_t brprot, uint32_t flags) +{ + dladm_status_t status; + scf_state_t sstate; + scf_transaction_t *tran = NULL; + boolean_t new_instance = B_FALSE; + boolean_t new_pg = B_FALSE; + datalink_id_t linkid = DATALINK_INVALID_LINKID; + char linkname[MAXLINKNAMELEN]; + int rv; + + if (!dladm_valid_bridgename(name)) + return (DLADM_STATUS_FAILED); + + if (flags & DLADM_OPT_CREATE) { + /* + * This check is here in case the user has installed and then + * removed the package. SMF should remove the manifest, but + * currently does not. + */ + if (access("/usr/lib/bridged", F_OK) != 0) + return (DLADM_STATUS_OPTMISSING); + + (void) snprintf(linkname, sizeof (linkname), "%s0", name); + status = dladm_create_datalink_id(handle, linkname, + DATALINK_CLASS_BRIDGE, DL_ETHER, + flags & (DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST), &linkid); + if (status != DLADM_STATUS_OK) + return (status); + + if ((flags & DLADM_OPT_PERSIST) && + (status = dladm_bridge_persist_conf(handle, linkname, + linkid) != DLADM_STATUS_OK)) + goto dladm_fail; + } + + if (brprot == DLADM_BRIDGE_PROT_TRILL) + status = enable_trill(name); + else + status = disable_trill(name, flags); + if (status != DLADM_STATUS_OK) + goto dladm_fail; + + if ((status = exact_instance(BRIDGE_SVC_NAME, &sstate)) != + DLADM_STATUS_OK) + goto out; + + /* set up for a series of scf calls */ + status = DLADM_STATUS_FAILED; + + if (scf_service_get_instance(sstate.ss_svc, name, sstate.ss_inst) == + 0) { + if (flags & DLADM_OPT_CREATE) { + status = DLADM_STATUS_EXIST; + goto out; + } + } else { + if (!(flags & DLADM_OPT_CREATE)) { + status = DLADM_STATUS_NOTFOUND; + goto out; + } + if (scf_service_add_instance(sstate.ss_svc, name, + sstate.ss_inst) != 0) + goto out; + new_instance = B_TRUE; + } + + if ((tran = scf_transaction_create(sstate.ss_handle)) == NULL) + goto out; + + if (cfg->field_mask & BR_CFG_ALL) { + if ((sstate.ss_pg = scf_pg_create(sstate.ss_handle)) == NULL) + goto out; + if (scf_instance_add_pg(sstate.ss_inst, "config", + SCF_GROUP_APPLICATION, 0, sstate.ss_pg) == 0) { + new_pg = B_TRUE; + } else if (scf_instance_get_pg(sstate.ss_inst, "config", + sstate.ss_pg) != 0) { + goto out; + } + do { + if (scf_transaction_start(tran, sstate.ss_pg) != 0) + goto out; + + if ((cfg->field_mask & BR_CFG_PRIO) && + !set_count_property(sstate.ss_handle, tran, + "priority", cfg->bridge_priority)) + goto out; + if ((cfg->field_mask & BR_CFG_AGE) && + !set_count_property(sstate.ss_handle, tran, + "max-age", cfg->max_age * IEEE_TIMER_SCALE)) + goto out; + if ((cfg->field_mask & BR_CFG_HELLO) && + !set_count_property(sstate.ss_handle, tran, + "hello-time", cfg->hello_time * IEEE_TIMER_SCALE)) + goto out; + if ((cfg->field_mask & BR_CFG_DELAY) && + !set_count_property(sstate.ss_handle, tran, + "forward-delay", + cfg->forward_delay * IEEE_TIMER_SCALE)) + goto out; + if ((cfg->field_mask & BR_CFG_FORCE_VER) && + !set_count_property(sstate.ss_handle, tran, + "force-protocol", cfg->force_version)) + goto out; + + rv = scf_transaction_commit(tran); + scf_transaction_reset(tran); + if (rv == 0 && scf_pg_update(sstate.ss_pg) == -1) + goto out; + } while (rv == 0); + if (rv != 1) + goto out; + } + + /* + * If we're modifying an existing and running bridge, then tell the + * daemon to update the requested values. + */ + if ((flags & DLADM_OPT_ACTIVE) && !(flags & DLADM_OPT_CREATE)) + status = bridge_refresh(name); + else + status = DLADM_STATUS_OK; + +out: + if (tran != NULL) { + scf_transaction_destroy_children(tran); + scf_transaction_destroy(tran); + } + + if (status != DLADM_STATUS_OK && new_pg) + (void) scf_pg_delete(sstate.ss_pg); + + drop_composed(&sstate); + + /* + * If we created an instance and then failed, then remove the instance + * from the system. + */ + if (status != DLADM_STATUS_OK && new_instance) + (void) scf_instance_delete(sstate.ss_inst); + + shut_down_scf(&sstate); + + /* + * Remove the bridge linkid if we've allocated one in this function but + * we've failed to set up the SMF properties. + */ +dladm_fail: + if (status != DLADM_STATUS_OK && linkid != DATALINK_INVALID_LINKID) { + (void) dladm_remove_conf(handle, linkid); + (void) dladm_destroy_datalink_id(handle, linkid, flags); + } + + return (status); +} + +/* + * Enable a newly-created bridge in SMF by creating "general/enabled" and + * deleting any "general_ovr/enabled" (used for temporary services). + */ +dladm_status_t +dladm_bridge_enable(const char *name) +{ + return (enable_instance(BRIDGE_SVC_NAME, name)); +} + +/* + * Set a link as a member of a bridge, or remove bridge membership. If the + * DLADM_OPT_CREATE flag is set, then we assume that the daemon isn't running. + * In all other cases, we must tell the daemon to add or delete the link in + * order to stay in sync. + */ +dladm_status_t +dladm_bridge_setlink(dladm_handle_t handle, datalink_id_t linkid, + const char *bridge) +{ + dladm_status_t status; + dladm_conf_t conf; + char oldbridge[MAXLINKNAMELEN]; + boolean_t has_oldbridge; + boolean_t changed = B_FALSE; + + if (*bridge != '\0' && !dladm_valid_bridgename(bridge)) + return (DLADM_STATUS_FAILED); + + if ((status = dladm_read_conf(handle, linkid, &conf)) != + DLADM_STATUS_OK) + return (status); + + has_oldbridge = B_FALSE; + status = dladm_get_conf_field(handle, conf, FBRIDGE, oldbridge, + sizeof (oldbridge)); + if (status == DLADM_STATUS_OK) { + /* + * Don't allow a link to be reassigned directly from one bridge + * to another. It must be removed first. + */ + if (*oldbridge != '\0' && *bridge != '\0') { + status = DLADM_STATUS_EXIST; + goto out; + } + has_oldbridge = B_TRUE; + } else if (status != DLADM_STATUS_NOTFOUND) { + goto out; + } + + if (*bridge != '\0') { + status = dladm_set_conf_field(handle, conf, FBRIDGE, + DLADM_TYPE_STR, bridge); + changed = B_TRUE; + } else if (has_oldbridge) { + status = dladm_unset_conf_field(handle, conf, FBRIDGE); + changed = B_TRUE; + } else { + status = DLADM_STATUS_OK; + goto out; + } + if (status == DLADM_STATUS_OK) + status = dladm_write_conf(handle, conf); + +out: + dladm_destroy_conf(handle, conf); + if (changed && status == DLADM_STATUS_OK) { + if (bridge[0] == '\0') + bridge = oldbridge; + status = bridge_refresh(bridge); + } + return (status); +} + +/* + * Get the name of the bridge of which the given linkid is a member. + */ +dladm_status_t +dladm_bridge_getlink(dladm_handle_t handle, datalink_id_t linkid, char *bridge, + size_t bridgelen) +{ + dladm_status_t status; + dladm_conf_t conf; + + if ((status = dladm_read_conf(handle, linkid, &conf)) != + DLADM_STATUS_OK) + return (status); + + *bridge = '\0'; + status = dladm_get_conf_field(handle, conf, FBRIDGE, bridge, bridgelen); + if (status == DLADM_STATUS_OK && *bridge == '\0') + status = DLADM_STATUS_NOTFOUND; + + dladm_destroy_conf(handle, conf); + return (status); +} + +dladm_status_t +dladm_bridge_refresh(dladm_handle_t handle, datalink_id_t linkid) +{ + char bridge[MAXLINKNAMELEN]; + dladm_status_t status; + + status = dladm_bridge_getlink(handle, linkid, bridge, sizeof (bridge)); + if (status == DLADM_STATUS_NOTFOUND) + return (DLADM_STATUS_OK); + if (status == DLADM_STATUS_OK) + status = bridge_refresh(bridge); + return (status); +} + +typedef struct bridge_held_arg_s { + const char *bha_bridge; + boolean_t bha_isheld; +} bridge_held_arg_t; + +static int +i_dladm_bridge_is_held(dladm_handle_t handle, datalink_id_t linkid, void *arg) +{ + dladm_status_t status = DLADM_STATUS_FAILED; + dladm_conf_t conf; + char bridge[MAXLINKNAMELEN]; + bridge_held_arg_t *bha = arg; + + if ((status = dladm_read_conf(handle, linkid, &conf)) != + DLADM_STATUS_OK) + return (DLADM_WALK_CONTINUE); + status = dladm_get_conf_field(handle, conf, FBRIDGE, bridge, + sizeof (bridge)); + if (status == DLADM_STATUS_OK && strcmp(bha->bha_bridge, bridge) == 0) { + bha->bha_isheld = B_TRUE; + dladm_destroy_conf(handle, conf); + return (DLADM_WALK_TERMINATE); + } else { + dladm_destroy_conf(handle, conf); + return (DLADM_WALK_CONTINUE); + } +} + +/* + * Delete a previously created bridge. + */ +dladm_status_t +dladm_bridge_delete(dladm_handle_t handle, const char *bridge, uint32_t flags) +{ + datalink_id_t linkid; + datalink_class_t class; + dladm_status_t status; + char linkname[MAXLINKNAMELEN]; + + if (!dladm_valid_bridgename(bridge)) + return (DLADM_STATUS_LINKINVAL); + + /* Get the datalink ID for this bridge */ + (void) snprintf(linkname, sizeof (linkname), "%s0", bridge); + if (dladm_name2info(handle, linkname, &linkid, NULL, NULL, NULL) != + DLADM_STATUS_OK) + linkid = DATALINK_INVALID_LINKID; + else if (dladm_datalink_id2info(handle, linkid, NULL, &class, NULL, + NULL, 0) != DLADM_STATUS_OK) + linkid = DATALINK_INVALID_LINKID; + else if (class != DATALINK_CLASS_BRIDGE) + return (DLADM_STATUS_BADARG); + + if ((flags & DLADM_OPT_ACTIVE) && linkid == DATALINK_INVALID_LINKID) + return (DLADM_STATUS_BADARG); + + if (flags & DLADM_OPT_PERSIST) { + bridge_held_arg_t arg; + + arg.bha_bridge = bridge; + arg.bha_isheld = B_FALSE; + + /* + * See whether there are any persistent links using this + * bridge. If so, we fail the operation. + */ + (void) dladm_walk_datalink_id(i_dladm_bridge_is_held, handle, + &arg, DATALINK_CLASS_PHYS | DATALINK_CLASS_AGGR | + DATALINK_CLASS_ETHERSTUB | DATALINK_CLASS_SIMNET, + DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST); + if (arg.bha_isheld) + return (DLADM_STATUS_LINKBUSY); + } + + if ((status = disable_trill(bridge, flags)) != DLADM_STATUS_OK) + goto out; + + /* Disable or remove the SMF instance */ + status = shut_down_instance(BRIDGE_SVC_NAME, bridge, flags); + if (status != DLADM_STATUS_OK) + goto out; + + if (flags & DLADM_OPT_ACTIVE) { + /* + * Delete ACTIVE linkprop now that daemon is gone. + */ + (void) dladm_set_linkprop(handle, linkid, NULL, NULL, 0, + DLADM_OPT_ACTIVE); + (void) dladm_destroy_datalink_id(handle, linkid, + DLADM_OPT_ACTIVE); + } + + if (flags & DLADM_OPT_PERSIST) { + (void) dladm_remove_conf(handle, linkid); + (void) dladm_destroy_datalink_id(handle, linkid, + DLADM_OPT_PERSIST); + } + +out: + + return (status); +} + +/* Check if given name is valid for bridges */ +boolean_t +dladm_valid_bridgename(const char *bridge) +{ + size_t len = strnlen(bridge, MAXLINKNAMELEN); + const char *cp; + + if (len == MAXLINKNAMELEN) + return (B_FALSE); + + /* + * The bridge name cannot start or end with a digit. + */ + if (isdigit(bridge[0]) || isdigit(bridge[len - 1])) + return (B_FALSE); + + /* + * The legal characters within a bridge name are: + * alphanumeric (a-z, A-Z, 0-9), and the underscore ('_'). + */ + for (cp = bridge; *cp != '\0'; cp++) { + if (!isalnum(*cp) && *cp != '_') + return (B_FALSE); + } + + return (B_TRUE); +} + +/* + * Convert a bridge-related observability node name back into the name of the + * bridge. Returns B_FALSE without making changes if the input name is not in + * a legal format. + */ +boolean_t +dladm_observe_to_bridge(char *link) +{ + int llen; + + llen = strnlen(link, MAXLINKNAMELEN); + if (llen < 2 || link[llen - 1] != '0' || isdigit(link[llen - 2])) + return (B_FALSE); + link[llen - 1] = '\0'; + return (B_TRUE); +} + +/* + * Get bridge property values from the running daemon and return them in a + * common structure. + */ +dladm_status_t +dladm_bridge_run_properties(const char *instname, UID_STP_CFG_T *smcfg, + dladm_bridge_prot_t *brprotp) +{ + dladm_status_t status; + bridge_door_cfg_t bdcf; + bridge_door_cfg_t *bdcfp = &bdcf; + size_t buflen = sizeof (bdcf); + + status = bridge_door_call(instname, bdcBridgeGetConfig, + DATALINK_INVALID_LINKID, (void **)&bdcfp, 0, &buflen, B_FALSE); + if (status == DLADM_STATUS_OK) { + *smcfg = bdcfp->bdcf_cfg; + *brprotp = bdcfp->bdcf_prot; + } else { + smcfg->field_mask = 0; + *brprotp = DLADM_BRIDGE_PROT_STP; + } + return (status); +} + +/* + * Get bridge state from the running daemon and return in structure borrowed + * from librstp. + */ +dladm_status_t +dladm_bridge_state(const char *instname, UID_STP_STATE_T *statep) +{ + size_t buflen = sizeof (*statep); + + return (bridge_door_call(instname, bdcBridgeGetState, + DATALINK_INVALID_LINKID, (void **)&statep, 0, &buflen, B_FALSE)); +} + +/* Returns list of ports (datalink_id_t values) assigned to a bridge instance */ +datalink_id_t * +dladm_bridge_get_portlist(const char *instname, uint_t *nports) +{ + size_t buflen = sizeof (int) + MAXPORTS * sizeof (datalink_id_t); + int *rbuf; + + if ((rbuf = malloc(buflen)) == NULL) + return (NULL); + if (bridge_door_call(instname, bdcBridgeGetPorts, + DATALINK_INVALID_LINKID, (void **)&rbuf, 0, &buflen, B_TRUE) != + DLADM_STATUS_OK) { + free(rbuf); + return (NULL); + } else { + /* + * Returns an array of datalink_id_t values for all the ports + * part of the bridge instance. First entry in the array is the + * number of ports. + */ + *nports = *rbuf; + return ((datalink_id_t *)(rbuf + 1)); + } +} + +void +dladm_bridge_free_portlist(datalink_id_t *dlp) +{ + free((int *)dlp - 1); +} + +/* Retrieve Bridge port configuration values */ +dladm_status_t +dladm_bridge_get_port_cfg(dladm_handle_t handle, datalink_id_t linkid, + int field, int *valuep) +{ + UID_STP_PORT_CFG_T portcfg; + dladm_status_t status; + + status = port_door_call(handle, linkid, bdcPortGetConfig, &portcfg, + 0, sizeof (portcfg)); + if (status != DLADM_STATUS_OK) + return (status); + + switch (field) { + case PT_CFG_COST: + *valuep = portcfg.admin_port_path_cost; + break; + case PT_CFG_PRIO: + *valuep = portcfg.port_priority; + break; + case PT_CFG_P2P: + *valuep = portcfg.admin_point2point; + break; + case PT_CFG_EDGE: + *valuep = portcfg.admin_edge; + break; + case PT_CFG_NON_STP: + *valuep = !portcfg.admin_non_stp; + break; + case PT_CFG_MCHECK: + *valuep = (portcfg.field_mask & PT_CFG_MCHECK) ? 1 : 0; + break; + } + return (status); +} + +/* Retreive Bridge port status (disabled, bad SDU etc.) */ +dladm_status_t +dladm_bridge_link_state(dladm_handle_t handle, datalink_id_t linkid, + UID_STP_PORT_STATE_T *spsp) +{ + return (port_door_call(handle, linkid, bdcPortGetState, spsp, 0, + sizeof (*spsp))); +} + +/* Retrieve Bridge forwarding status of the given link */ +dladm_status_t +dladm_bridge_get_forwarding(dladm_handle_t handle, datalink_id_t linkid, + uint_t *valuep) +{ + int twoints[2]; + dladm_status_t status; + + status = port_door_call(handle, linkid, bdcPortGetForwarding, twoints, + 0, sizeof (twoints)); + if (status == DLADM_STATUS_OK) + *valuep = twoints[0]; + return (status); +} + +/* Retrieve Bridge forwarding table entries */ +bridge_listfwd_t * +dladm_bridge_get_fwdtable(dladm_handle_t handle, const char *bridge, + uint_t *nfwd) +{ + bridge_listfwd_t *blf = NULL, *newblf, blfread; + uint_t nblf = 0, maxblf = 0; + static uint8_t zero_addr[ETHERADDRL]; + int rc; + + (void) memset(&blfread, 0, sizeof (blfread)); + (void) snprintf(blfread.blf_name, sizeof (blfread.blf_name), + "%s0", bridge); + for (;;) { + if (nblf >= maxblf) { + maxblf = maxblf == 0 ? 64 : (maxblf << 1); + newblf = realloc(blf, maxblf * sizeof (*blf)); + if (newblf == NULL) { + free(blf); + blf = NULL; + break; + } + blf = newblf; + } + rc = ioctl(dladm_dld_fd(handle), BRIDGE_IOC_LISTFWD, &blfread); + if (rc != 0) { + free(blf); + blf = NULL; + break; + } + if (memcmp(blfread.blf_dest, zero_addr, ETHERADDRL) == 0) + break; + blf[nblf++] = blfread; + } + if (blf != NULL) + *nfwd = nblf; + return (blf); +} + +void +dladm_bridge_free_fwdtable(bridge_listfwd_t *blf) +{ + free(blf); +} + +/* Retrieve list of TRILL nicknames from the TRILL module */ +trill_listnick_t * +dladm_bridge_get_trillnick(const char *bridge, uint_t *nnick) +{ + int fd; + char brcopy[MAXLINKNAMELEN]; + trill_listnick_t *tln = NULL, *newtln, tlnread; + uint_t ntln = 0, maxtln = 0; + + if ((fd = socket(PF_TRILL, SOCK_DGRAM, 0)) == -1) + return (NULL); + (void) strlcpy(brcopy, bridge, sizeof (brcopy)); + if (ioctl(fd, TRILL_GETBRIDGE, &brcopy) < 0) { + (void) close(fd); + return (NULL); + } + (void) memset(&tlnread, 0, sizeof (tlnread)); + for (;;) { + if (ntln >= maxtln) { + maxtln = maxtln == 0 ? 64 : (maxtln << 1); + newtln = realloc(tln, maxtln * sizeof (*tln)); + if (newtln == NULL) { + free(tln); + tln = NULL; + break; + } + tln = newtln; + } + if (ioctl(fd, TRILL_LISTNICK, &tlnread) == -1) { + free(tln); + tln = NULL; + break; + } + if (tlnread.tln_nick == 0) + break; + tln[ntln++] = tlnread; + } + (void) close(fd); + if (tln != NULL) + *nnick = ntln; + return (tln); +} + +void +dladm_bridge_free_trillnick(trill_listnick_t *tln) +{ + free(tln); +} + +/* Retrieve any stored TRILL nickname from TRILL SMF service */ +uint16_t +dladm_bridge_get_nick(const char *bridge) +{ + scf_state_t sstate; + uint64_t value; + uint16_t nickname = RBRIDGE_NICKNAME_NONE; + + if (bind_instance(TRILL_SVC_NAME, bridge, &sstate) != 0) + return (nickname); + + if (get_composed_properties("config", B_TRUE, &sstate) == 0 && + get_count("nickname", &sstate, &value) == 0) + nickname = value; + shut_down_scf(&sstate); + return (nickname); +} + +/* Stores TRILL nickname in SMF configuraiton for the TRILL service */ +void +dladm_bridge_set_nick(const char *bridge, uint16_t nick) +{ + scf_state_t sstate; + scf_transaction_t *tran = NULL; + boolean_t new_pg = B_FALSE; + int rv = 0; + char *fmri; + + if (exact_instance(TRILL_SVC_NAME, &sstate) != DLADM_STATUS_OK) + return; + + if (scf_service_get_instance(sstate.ss_svc, bridge, sstate.ss_inst) != + 0) + goto out; + if ((tran = scf_transaction_create(sstate.ss_handle)) == NULL) + goto out; + if ((sstate.ss_pg = scf_pg_create(sstate.ss_handle)) == NULL) + goto out; + if (scf_instance_add_pg(sstate.ss_inst, "config", + SCF_GROUP_APPLICATION, 0, sstate.ss_pg) == 0) { + new_pg = B_TRUE; + } else if (scf_instance_get_pg(sstate.ss_inst, "config", + sstate.ss_pg) != 0) { + goto out; + } + do { + if (scf_transaction_start(tran, sstate.ss_pg) != 0) + goto out; + if (!set_count_property(sstate.ss_handle, tran, "nickname", + nick)) + goto out; + rv = scf_transaction_commit(tran); + scf_transaction_reset(tran); + if (rv == 0 && scf_pg_update(sstate.ss_pg) == -1) + goto out; + } while (rv == 0); + +out: + if (tran != NULL) { + scf_transaction_destroy_children(tran); + scf_transaction_destroy(tran); + } + + if (rv != 1 && new_pg) + (void) scf_pg_delete(sstate.ss_pg); + + drop_composed(&sstate); + shut_down_scf(&sstate); + if (rv == 1 && (fmri = alloc_fmri(TRILL_SVC_NAME, bridge)) != NULL) { + (void) smf_refresh_instance(fmri); + free(fmri); + } +} |
