diff options
| author | Rishi Srivatsavai <Rishi.Srivatsavai@Sun.COM> | 2009-09-10 15:11:49 -0400 |
|---|---|---|
| committer | Rishi Srivatsavai <Rishi.Srivatsavai@Sun.COM> | 2009-09-10 15:11:49 -0400 |
| commit | 4eaa471005973e11a6110b69fe990530b3b95a38 (patch) | |
| tree | 3aca4c2ad771c935bfa146d4a6abbe51ee464a57 /usr/src/lib | |
| parent | d5688513c1985c22c27675da44b5b691f123818e (diff) | |
| download | illumos-joyent-4eaa471005973e11a6110b69fe990530b3b95a38.tar.gz | |
PSARC 2007/596 RBridges: Routing Bridges
PSARC 2008/055 Solaris Bridging
PSARC 2009/344 Bridging Updates
6223953 Solaris should provide layer 2 bridging
6770623 bogus error messages generated by dladm should be cleaned up
Diffstat (limited to 'usr/src/lib')
69 files changed, 9843 insertions, 42 deletions
diff --git a/usr/src/lib/Makefile b/usr/src/lib/Makefile index 2c23ad27e3..7f523ccaa8 100644 --- a/usr/src/lib/Makefile +++ b/usr/src/lib/Makefile @@ -259,6 +259,7 @@ SUBDIRS += \ libima \ libsun_ima \ mpapi \ + librstp \ $($(MACH)_SUBDIRS) i386_SUBDIRS= \ @@ -441,6 +442,7 @@ HDRSUBDIRS= \ librestart \ librpcsvc \ librsm \ + librstp \ libsasl \ libsec \ libshell \ diff --git a/usr/src/lib/libdladm/Makefile b/usr/src/lib/libdladm/Makefile index e0304b7894..7765c65ab8 100644 --- a/usr/src/lib/libdladm/Makefile +++ b/usr/src/lib/libdladm/Makefile @@ -29,7 +29,7 @@ include $(SRC)/lib/Makefile.lib HDRS = libdladm.h libdladm_impl.h libdllink.h libdlaggr.h \ libdlwlan.h libdlwlan_impl.h libdlvnic.h libdlvlan.h \ libdlmgmt.h libdlflow.h libdlflow_impl.h libdlstat.h \ - libdlether.h libdlsim.h + libdlether.h libdlsim.h libdlbridge.h HDRDIR = common @@ -42,9 +42,10 @@ MSGFILES = common/libdladm.c common/linkprop.c common/secobj.c \ common/libdlwlan.c common/libdlvnic.c \ common/libdlvlan.c common/libdlmgmt.c \ common/flowattr.c common/flowprop.c \ - common/propfuncs.c common/libdlflow.c \ + common/propfuncs.c common/libdlflow.c \ common/libdlstat.c common/flowattr.c \ - common/libdlether.c common/libdlsim.c + common/libdlether.c common/libdlsim.c \ + common/libdlbridge.c XGETFLAGS = -a -x libdladm.xcl diff --git a/usr/src/lib/libdladm/Makefile.com b/usr/src/lib/libdladm/Makefile.com index 99eafef501..1ad7a7c961 100644 --- a/usr/src/lib/libdladm/Makefile.com +++ b/usr/src/lib/libdladm/Makefile.com @@ -28,7 +28,7 @@ VERS = .1 OBJECTS = libdladm.o secobj.o linkprop.o libdllink.o libdlaggr.o \ libdlwlan.o libdlvnic.o libdlmgmt.o libdlvlan.o \ flowattr.o flowprop.o propfuncs.o libdlflow.o libdlstat.o \ - usage.o libdlether.o libdlsim.o + usage.o libdlether.o libdlsim.o libdlbridge.o include ../../Makefile.lib diff --git a/usr/src/lib/libdladm/common/libdladm.c b/usr/src/lib/libdladm/common/libdladm.c index 06ff5970a0..90d1d7fdaf 100644 --- a/usr/src/lib/libdladm/common/libdladm.c +++ b/usr/src/lib/libdladm/common/libdladm.c @@ -345,6 +345,12 @@ dladm_status2str(dladm_status_t status, char *buf) case DLADM_STATUS_NO_HWRINGS: s = "request hw rings failed"; break; + case DLADM_STATUS_PERMONLY: + s = "change must be persistent"; + break; + case DLADM_STATUS_OPTMISSING: + s = "optional software not installed"; + break; default: s = "<unknown error>"; break; @@ -570,6 +576,9 @@ dladm_class2str(datalink_class_t class, char *buf) case DATALINK_CLASS_SIMNET: s = "simnet"; break; + case DATALINK_CLASS_BRIDGE: + s = "bridge"; + break; default: s = "unknown"; break; diff --git a/usr/src/lib/libdladm/common/libdladm.h b/usr/src/lib/libdladm/common/libdladm.h index 66178afd44..919c207cd0 100644 --- a/usr/src/lib/libdladm/common/libdladm.h +++ b/usr/src/lib/libdladm/common/libdladm.h @@ -66,6 +66,9 @@ extern "C" { * * - DLADM_OPT_HWRINGS: * Requires a hardware group of rings when creating a vnic. + * + * - DLADM_OPT_NOREFRESH: + * Do not refresh the daemon after setting parameter (used by STP mcheck). */ #define DLADM_OPT_ACTIVE 0x00000001 #define DLADM_OPT_PERSIST 0x00000002 @@ -75,6 +78,7 @@ extern "C" { #define DLADM_OPT_ANCHOR 0x00000020 #define DLADM_OPT_VLAN 0x00000040 #define DLADM_OPT_HWRINGS 0x00000080 +#define DLADM_OPT_NOREFRESH 0x00000100 #define DLADM_WALK_TERMINATE 0 #define DLADM_WALK_CONTINUE -1 @@ -148,7 +152,9 @@ typedef enum { DLADM_STATUS_NOTDEFINED, DLADM_STATUS_BADPROP, DLADM_STATUS_MINMAXBW, - DLADM_STATUS_NO_HWRINGS + DLADM_STATUS_NO_HWRINGS, + DLADM_STATUS_PERMONLY, + DLADM_STATUS_OPTMISSING } dladm_status_t; typedef enum { diff --git a/usr/src/lib/libdladm/common/libdladm_impl.h b/usr/src/lib/libdladm/common/libdladm_impl.h index b6d1913f82..774d86b0ac 100644 --- a/usr/src/lib/libdladm/common/libdladm_impl.h +++ b/usr/src/lib/libdladm/common/libdladm_impl.h @@ -26,6 +26,8 @@ #ifndef _LIBDLADM_IMPL_H #define _LIBDLADM_IMPL_H +#include <sys/mac.h> +#include <sys/mac_flow.h> #include <libdladm.h> #include <stdio.h> @@ -154,6 +156,11 @@ typedef struct resource_prop_s { rp_extractf_t *rp_extract; } resource_prop_t; +/* + * Set for bridged links only + */ +#define FBRIDGE "bridge" /* string */ + #ifdef __cplusplus } #endif 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); + } +} diff --git a/usr/src/lib/libdladm/common/libdlbridge.h b/usr/src/lib/libdladm/common/libdlbridge.h new file mode 100644 index 0000000000..b3e91a7488 --- /dev/null +++ b/usr/src/lib/libdladm/common/libdlbridge.h @@ -0,0 +1,137 @@ +/* + * 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. + */ + +#ifndef _LIBDLBRIDGE_H +#define _LIBDLBRIDGE_H + +/* + * This file includes structures, macros and routines used by bridge + * administration. + */ + +#include <sys/types.h> +#include <libdladm.h> +#include <uid_stp.h> +#include <net/bridge.h> +#include <net/trill.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + DLADM_BRIDGE_PROT_UNKNOWN = 0, /* internal only */ + DLADM_BRIDGE_PROT_STP, + DLADM_BRIDGE_PROT_TRILL +} dladm_bridge_prot_t; + +/* Utility functions to accept bridge protection options */ +extern dladm_status_t dladm_bridge_str2prot(const char *, + dladm_bridge_prot_t *); +extern const char *dladm_bridge_prot2str(dladm_bridge_prot_t); + +/* Retrieve bridge properties from SMF */ +extern dladm_status_t dladm_bridge_get_properties(const char *, + UID_STP_CFG_T *, dladm_bridge_prot_t *); +extern dladm_status_t dladm_bridge_run_properties(const char *, + UID_STP_CFG_T *, dladm_bridge_prot_t *); + +/* Create new bridge and configure SMF properties */ +extern dladm_status_t dladm_bridge_configure(dladm_handle_t, const char *, + const UID_STP_CFG_T *, dladm_bridge_prot_t, uint32_t); + +/* Enable a newly created bridge in SMF */ +extern dladm_status_t dladm_bridge_enable(const char *); +/* Delete a previously created bridge */ +extern dladm_status_t dladm_bridge_delete(dladm_handle_t, const char *, + uint32_t); + +/* Retrieve bridge state from running bridge daemon and get bridge port list */ +extern dladm_status_t dladm_bridge_state(const char *, UID_STP_STATE_T *); +extern datalink_id_t *dladm_bridge_get_portlist(const char *, uint_t *); +extern void dladm_bridge_free_portlist(datalink_id_t *); + +/* Set/remove bridge link membership and retreive bridge from member link */ +extern dladm_status_t dladm_bridge_setlink(dladm_handle_t, datalink_id_t, + const char *); +extern dladm_status_t dladm_bridge_getlink(dladm_handle_t, datalink_id_t, + char *, size_t); + +/* Retrieve bridge port status */ +extern dladm_status_t dladm_bridge_link_state(dladm_handle_t, datalink_id_t, + UID_STP_PORT_STATE_T *); +/* Check valid bridge name */ +extern boolean_t dladm_valid_bridgename(const char *); +/* Convert bridge observability node name to bridge name */ +extern boolean_t dladm_observe_to_bridge(char *); +/* Retrieve bridge forwarding table entries */ +extern bridge_listfwd_t *dladm_bridge_get_fwdtable(dladm_handle_t, const char *, + uint_t *); +extern void dladm_bridge_free_fwdtable(bridge_listfwd_t *); + +/* Retrive TRILL nicknames list */ +extern trill_listnick_t *dladm_bridge_get_trillnick(const char *, uint_t *); +extern void dladm_bridge_free_trillnick(trill_listnick_t *); +/* Store and retrieve TRILL nickname from TRILL SMF service configuration */ +extern uint16_t dladm_bridge_get_nick(const char *); +extern void dladm_bridge_set_nick(const char *, uint16_t); +/* Retrieve undocumented private properties from bridge SMF service config */ +extern dladm_status_t dladm_bridge_get_privprop(const char *, + boolean_t *, uint32_t *); + +/* Internal to libdladm */ +extern dladm_status_t dladm_bridge_get_port_cfg(dladm_handle_t, datalink_id_t, + int, int *); +extern dladm_status_t dladm_bridge_get_forwarding(dladm_handle_t, + datalink_id_t, uint_t *); +extern dladm_status_t dladm_bridge_refresh(dladm_handle_t, datalink_id_t); + +/* Bridge connection; used only between libdladm and bridged for status */ +#define DOOR_DIRNAME "/var/run/bridge_door" +typedef enum bridge_door_type_e { + bdcBridgeGetConfig, + bdcBridgeGetState, + bdcBridgeGetPorts, + bdcBridgeGetRefreshCount, + bdcPortGetConfig, + bdcPortGetState, + bdcPortGetForwarding +} bridge_door_type_t; + +typedef struct bridge_door_cmd_s { + bridge_door_type_t bdc_type; + datalink_id_t bdc_linkid; +} bridge_door_cmd_t; + +typedef struct bridge_door_cfg_s { + UID_STP_CFG_T bdcf_cfg; + dladm_bridge_prot_t bdcf_prot; +} bridge_door_cfg_t; + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBDLBRIDGE_H */ diff --git a/usr/src/lib/libdladm/common/libdllink.h b/usr/src/lib/libdladm/common/libdllink.h index 50b73ca540..6e3b0c97a3 100644 --- a/usr/src/lib/libdladm/common/libdllink.h +++ b/usr/src/lib/libdladm/common/libdllink.h @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -34,9 +34,9 @@ #include <stdio.h> #include <sys/types.h> #include <sys/param.h> +#include <sys/mac.h> +#include <sys/dld.h> #include <libdladm.h> -#include <libdladm_impl.h> -#include <sys/mac_flow.h> #ifdef __cplusplus extern "C" { @@ -128,6 +128,9 @@ extern dladm_status_t dladm_set_linkprop(dladm_handle_t, datalink_id_t, const char *, char **, uint_t, uint_t); extern dladm_status_t dladm_get_linkprop(dladm_handle_t, datalink_id_t, dladm_prop_type_t, const char *, char **, uint_t *); +extern dladm_status_t dladm_get_linkprop_values(dladm_handle_t, datalink_id_t, + dladm_prop_type_t, const char *, uint_t *, + uint_t *); extern dladm_status_t dladm_walk_linkprop(dladm_handle_t, datalink_id_t, void *, int (*)(dladm_handle_t, datalink_id_t, const char *, void *)); diff --git a/usr/src/lib/libdladm/common/libdlvnic.c b/usr/src/lib/libdladm/common/libdlvnic.c index 3e83a6e6a2..2984c42dcc 100644 --- a/usr/src/lib/libdladm/common/libdlvnic.c +++ b/usr/src/lib/libdladm/common/libdlvnic.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -39,6 +39,7 @@ #include <sys/dld.h> #include <libdladm_impl.h> #include <libdllink.h> +#include <libdlbridge.h> #include <libdlvnic.h> /* @@ -340,8 +341,6 @@ dladm_vnic_str2macaddrtype(const char *str, vnic_mac_addr_type_t *val) return (DLADM_STATUS_BADARG); } - - /* * Create a new VNIC / VLAN. Update the configuration file and bring it up. */ @@ -361,6 +360,8 @@ dladm_vnic_create(dladm_handle_t handle, const char *vnic, datalink_id_t linkid, boolean_t is_vlan; boolean_t is_etherstub; int i; + boolean_t vnic_created = B_FALSE; + boolean_t conf_set = B_FALSE; /* * Sanity test arguments. @@ -453,16 +454,16 @@ dladm_vnic_create(dladm_handle_t handle, const char *vnic, datalink_id_t linkid, status = i_dladm_vnic_create_sys(handle, &attr); if (status != DLADM_STATUS_OK) goto done; + vnic_created = B_TRUE; /* Save vnic configuration and its properties */ if (!(flags & DLADM_OPT_PERSIST)) goto done; status = dladm_vnic_persist_conf(handle, name, &attr, class); - if (status != DLADM_STATUS_OK) { - (void) i_dladm_vnic_delete_sys(handle, vnic_id); + if (status != DLADM_STATUS_OK) goto done; - } + conf_set = B_TRUE; if (proplist != NULL) { for (i = 0; i < proplist->al_count; i++) { @@ -474,15 +475,14 @@ dladm_vnic_create(dladm_handle_t handle, const char *vnic, datalink_id_t linkid, if (status != DLADM_STATUS_OK) break; } - - if (status != DLADM_STATUS_OK) { - (void) dladm_remove_conf(handle, vnic_id); - (void) i_dladm_vnic_delete_sys(handle, vnic_id); - } } done: if (status != DLADM_STATUS_OK) { + if (conf_set) + (void) dladm_remove_conf(handle, vnic_id); + if (vnic_created) + (void) i_dladm_vnic_delete_sys(handle, vnic_id); (void) dladm_destroy_datalink_id(handle, vnic_id, flags); } else { if (vnic_id_out != NULL) @@ -490,6 +490,14 @@ done: if (mac_slot != NULL) *mac_slot = attr.va_mac_slot; } + + if (is_vlan) { + dladm_status_t stat2; + + stat2 = dladm_bridge_refresh(handle, linkid); + if (status == DLADM_STATUS_OK && stat2 != DLADM_STATUS_OK) + status = stat2; + } return (status); } @@ -501,6 +509,7 @@ dladm_vnic_delete(dladm_handle_t handle, datalink_id_t linkid, uint32_t flags) { dladm_status_t status; datalink_class_t class; + dladm_vnic_attr_t attr; if (flags == 0) return (DLADM_STATUS_BADARG); @@ -519,6 +528,10 @@ dladm_vnic_delete(dladm_handle_t handle, datalink_id_t linkid, uint32_t flags) } if ((flags & DLADM_OPT_ACTIVE) != 0) { + status = dladm_vnic_info(handle, linkid, &attr, + DLADM_OPT_ACTIVE); + if (status != DLADM_STATUS_OK) + return (status); status = i_dladm_vnic_delete_sys(handle, linkid); if (status == DLADM_STATUS_OK) { (void) dladm_set_linkprop(handle, linkid, NULL, NULL, 0, @@ -535,7 +548,7 @@ dladm_vnic_delete(dladm_handle_t handle, datalink_id_t linkid, uint32_t flags) DLADM_OPT_PERSIST); (void) dladm_remove_conf(handle, linkid); } - return (DLADM_STATUS_OK); + return (dladm_bridge_refresh(handle, linkid)); } static const char * @@ -699,14 +712,12 @@ i_dladm_vnic_up(dladm_handle_t handle, datalink_id_t linkid, void *arg) } status = i_dladm_vnic_create_sys(handle, &attr); - if (status != DLADM_STATUS_OK) - goto done; - - if ((status = dladm_up_datalink_id(handle, linkid)) != - DLADM_STATUS_OK) { - (void) i_dladm_vnic_delete_sys(handle, linkid); - goto done; + if (status == DLADM_STATUS_OK) { + status = dladm_up_datalink_id(handle, linkid); + if (status != DLADM_STATUS_OK) + (void) i_dladm_vnic_delete_sys(handle, linkid); } + done: *statusp = status; return (DLADM_WALK_CONTINUE); diff --git a/usr/src/lib/libdladm/common/linkprop.c b/usr/src/lib/libdladm/common/linkprop.c index 044b963e45..b0c0c32f45 100644 --- a/usr/src/lib/libdladm/common/linkprop.c +++ b/usr/src/lib/libdladm/common/linkprop.c @@ -56,6 +56,9 @@ #include <sys/ethernet.h> #include <net/wpa.h> #include <sys/sysmacros.h> +#include <sys/vlan.h> +#include <libdlbridge.h> +#include <stp_in.h> /* * The linkprop get() callback. @@ -133,15 +136,18 @@ static pd_getf_t do_get_zone, do_get_autopush, do_get_rate_mod, i_dladm_binary_get, i_dladm_uint32_get, i_dladm_flowctl_get, i_dladm_maxbw_get, i_dladm_cpus_get, i_dladm_priority_get, - i_dladm_tagmode_get, i_dladm_range_get; + i_dladm_tagmode_get, i_dladm_range_get, + get_stp_prop, get_bridge_forward, + get_bridge_pvid; static pd_setf_t do_set_zone, do_set_rate_prop, do_set_powermode_prop, do_set_radio_prop, - i_dladm_set_public_prop, do_set_res, do_set_cpus; + i_dladm_set_public_prop, do_set_res, do_set_cpus, + set_stp_prop, set_bridge_forward, set_bridge_pvid; static pd_checkf_t do_check_zone, do_check_autopush, do_check_rate, - i_dladm_defmtu_check, do_check_maxbw, do_check_cpus, - do_check_priority; + i_dladm_uint32_check, do_check_maxbw, do_check_cpus, + do_check_priority, check_stp_prop, check_bridge_pvid; static dladm_status_t i_dladm_speed_get(dladm_handle_t, prop_desc_t *, datalink_id_t, char **, uint_t *, uint_t, uint_t *); @@ -173,8 +179,9 @@ struct prop_desc { uint_t pd_noptval; /* - * callback to set link property; - * set to NULL if this property is read-only + * callback to set link property; set to NULL if this property is + * read-only and may be called before or after permanent update; see + * flags. */ pd_setf_t *pd_set; @@ -199,6 +206,7 @@ struct prop_desc { uint_t pd_flags; #define PD_TEMPONLY 0x1 /* property is temporary only */ #define PD_CHECK_ALLOC 0x2 /* alloc vd_val as part of pd_check */ +#define PD_AFTER_PERM 0x4 /* pd_set after db update; no temporary */ /* * indicate link classes this property applies to. */ @@ -321,10 +329,31 @@ static link_attr_t link_attr[] = { { MAC_PROP_TAGMODE, sizeof (link_tagmode_t), "tagmode"}, + { MAC_PROP_PVID, sizeof (uint16_t), "default_tag"}, + + { MAC_PROP_LLIMIT, sizeof (uint32_t), "learn_limit"}, + + { MAC_PROP_LDECAY, sizeof (uint32_t), "learn_decay"}, + { MAC_PROP_PRIVATE, 0, "driver-private"} }; +typedef struct bridge_public_prop_s { + const char *bpp_name; + int bpp_code; +} bridge_public_prop_t; + +static const bridge_public_prop_t bridge_prop[] = { + { "stp", PT_CFG_NON_STP }, + { "stp_priority", PT_CFG_PRIO }, + { "stp_cost", PT_CFG_COST }, + { "stp_edge", PT_CFG_EDGE }, + { "stp_p2p", PT_CFG_P2P }, + { "stp_mcheck", PT_CFG_MCHECK }, + { NULL, 0 } +}; + static val_desc_t link_duplex_vals[] = { { "half", LINK_DUPLEX_HALF }, { "full", LINK_DUPLEX_HALF } @@ -365,6 +394,12 @@ static val_desc_t dladm_wlan_powermode_vals[] = { { "max", DLADM_WLAN_PM_MAX } }; +static val_desc_t stp_p2p_vals[] = { + { "true", P2P_FORCE_TRUE }, + { "false", P2P_FORCE_FALSE }, + { "auto", P2P_AUTO } +}; + #define VALCNT(vals) (sizeof ((vals)) / sizeof (val_desc_t)) #define RESET_VAL ((uintptr_t)-1) @@ -418,7 +453,7 @@ static prop_desc_t prop_table[] = { { "mtu", { "", 0 }, NULL, 0, i_dladm_set_public_prop, i_dladm_range_get, - i_dladm_uint32_get, i_dladm_defmtu_check, 0, DATALINK_CLASS_ALL, + i_dladm_uint32_get, i_dladm_uint32_check, 0, DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE }, { "flowctrl", { "", 0 }, @@ -516,7 +551,63 @@ static prop_desc_t prop_table[] = { i_dladm_set_public_prop, NULL, i_dladm_tagmode_get, NULL, 0, DATALINK_CLASS_PHYS | DATALINK_CLASS_AGGR | DATALINK_CLASS_VNIC, - DL_ETHER } + DL_ETHER }, + + { "forward", { "1", 1 }, + link_01_vals, VALCNT(link_01_vals), + set_bridge_forward, NULL, get_bridge_forward, NULL, PD_AFTER_PERM, + DATALINK_CLASS_ALL & ~DATALINK_CLASS_VNIC, DL_ETHER }, + + { "default_tag", { "1", 1 }, NULL, 0, + set_bridge_pvid, NULL, get_bridge_pvid, check_bridge_pvid, + 0, DATALINK_CLASS_PHYS|DATALINK_CLASS_AGGR| + DATALINK_CLASS_ETHERSTUB|DATALINK_CLASS_SIMNET, DL_ETHER }, + + { "learn_limit", { "1000", 1000 }, NULL, 0, + i_dladm_set_public_prop, NULL, i_dladm_uint32_get, + i_dladm_uint32_check, 0, + DATALINK_CLASS_PHYS|DATALINK_CLASS_AGGR| + DATALINK_CLASS_ETHERSTUB|DATALINK_CLASS_SIMNET, DL_ETHER }, + + { "learn_decay", { "200", 200 }, NULL, 0, + i_dladm_set_public_prop, NULL, i_dladm_uint32_get, + i_dladm_uint32_check, 0, + DATALINK_CLASS_PHYS|DATALINK_CLASS_AGGR| + DATALINK_CLASS_ETHERSTUB|DATALINK_CLASS_SIMNET, DL_ETHER }, + + { "stp", { "1", 1 }, + link_01_vals, VALCNT(link_01_vals), + set_stp_prop, NULL, get_stp_prop, NULL, PD_AFTER_PERM, + DATALINK_CLASS_PHYS|DATALINK_CLASS_AGGR| + DATALINK_CLASS_ETHERSTUB|DATALINK_CLASS_SIMNET, DL_ETHER }, + + { "stp_priority", { "128", 128 }, NULL, 0, + set_stp_prop, NULL, get_stp_prop, check_stp_prop, PD_AFTER_PERM, + DATALINK_CLASS_PHYS|DATALINK_CLASS_AGGR| + DATALINK_CLASS_ETHERSTUB|DATALINK_CLASS_SIMNET, DL_ETHER }, + + { "stp_cost", { "auto", 0 }, NULL, 0, + set_stp_prop, NULL, get_stp_prop, check_stp_prop, PD_AFTER_PERM, + DATALINK_CLASS_PHYS|DATALINK_CLASS_AGGR| + DATALINK_CLASS_ETHERSTUB|DATALINK_CLASS_SIMNET, DL_ETHER }, + + { "stp_edge", { "1", 1 }, + link_01_vals, VALCNT(link_01_vals), + set_stp_prop, NULL, get_stp_prop, NULL, PD_AFTER_PERM, + DATALINK_CLASS_PHYS|DATALINK_CLASS_AGGR| + DATALINK_CLASS_ETHERSTUB|DATALINK_CLASS_SIMNET, DL_ETHER }, + + { "stp_p2p", { "auto", P2P_AUTO }, + stp_p2p_vals, VALCNT(stp_p2p_vals), + set_stp_prop, NULL, get_stp_prop, NULL, PD_AFTER_PERM, + DATALINK_CLASS_PHYS|DATALINK_CLASS_AGGR| + DATALINK_CLASS_ETHERSTUB|DATALINK_CLASS_SIMNET, DL_ETHER }, + + { "stp_mcheck", { "0", 0 }, + link_01_vals, VALCNT(link_01_vals), + set_stp_prop, NULL, get_stp_prop, check_stp_prop, PD_AFTER_PERM, + DATALINK_CLASS_PHYS|DATALINK_CLASS_AGGR| + DATALINK_CLASS_ETHERSTUB|DATALINK_CLASS_SIMNET, DL_ETHER }, }; #define DLADM_MAX_PROPS (sizeof (prop_table) / sizeof (prop_desc_t)) @@ -658,7 +749,12 @@ i_dladm_set_single_prop(dladm_handle_t handle, datalink_id_t linkid, return (status); } } - status = pdp->pd_set(handle, pdp, linkid, vdp, cnt, flags, media); + if (pdp->pd_flags & PD_AFTER_PERM) + status = (flags & DLADM_OPT_PERSIST) ? DLADM_STATUS_OK : + DLADM_STATUS_PERMONLY; + else + status = pdp->pd_set(handle, pdp, linkid, vdp, cnt, flags, + media); if (needfree) { for (i = 0; i < cnt; i++) free((void *)((val_desc_t *)vdp + i)->vd_val); @@ -744,6 +840,21 @@ dladm_set_linkprop(dladm_handle_t handle, datalink_id_t linkid, if (flags & DLADM_OPT_PERSIST) { status = i_dladm_set_linkprop_db(handle, linkid, prop_name, prop_val, val_cnt); + + if (status == DLADM_STATUS_OK && (flags & DLADM_OPT_ACTIVE)) { + prop_desc_t *pdp = prop_table; + int i; + + for (i = 0; i < DLADM_MAX_PROPS; i++, pdp++) { + if (!(pdp->pd_flags & PD_AFTER_PERM)) + continue; + if (prop_name != NULL && + strcasecmp(prop_name, pdp->pd_name) != 0) + continue; + status = pdp->pd_set(handle, pdp, linkid, NULL, + 0, flags, 0); + } + } } return (status); } @@ -927,6 +1038,140 @@ dladm_get_linkprop(dladm_handle_t handle, datalink_id_t linkid, return (status); } +/* + * Get linkprop of the given specific link and run any possible conversion + * of the values using the check function for the property. Fails if the + * check function doesn't succeed for the property value. + */ +dladm_status_t +dladm_get_linkprop_values(dladm_handle_t handle, datalink_id_t linkid, + dladm_prop_type_t type, const char *prop_name, uint_t *ret_val, + uint_t *val_cntp) +{ + dladm_status_t status; + datalink_class_t class; + uint_t media; + prop_desc_t *pdp; + uint_t dld_flags; + int valc, i; + char **prop_val; + uint_t perm_flags; + + if (linkid == DATALINK_INVALID_LINKID || prop_name == NULL || + ret_val == NULL || val_cntp == NULL || *val_cntp == 0) + return (DLADM_STATUS_BADARG); + + for (pdp = prop_table; pdp < prop_table + DLADM_MAX_PROPS; pdp++) + if (strcasecmp(prop_name, pdp->pd_name) == 0) + break; + + if (pdp == prop_table + DLADM_MAX_PROPS) + return (DLADM_STATUS_NOTFOUND); + + if (pdp->pd_flags & PD_CHECK_ALLOC) + return (DLADM_STATUS_BADARG); + + status = dladm_datalink_id2info(handle, linkid, NULL, &class, &media, + NULL, 0); + if (status != DLADM_STATUS_OK) + return (status); + + if (!(pdp->pd_class & class)) + return (DLADM_STATUS_BADARG); + + if (!DATALINK_MEDIA_ACCEPTED(pdp->pd_dmedia, media)) + return (DLADM_STATUS_BADARG); + + prop_val = malloc(*val_cntp * sizeof (*prop_val) + + *val_cntp * DLADM_PROP_VAL_MAX); + if (prop_val == NULL) + return (DLADM_STATUS_NOMEM); + for (valc = 0; valc < *val_cntp; valc++) + prop_val[valc] = (char *)(prop_val + *val_cntp) + + valc * DLADM_PROP_VAL_MAX; + + dld_flags = (type == DLADM_PROP_VAL_DEFAULT) ? MAC_PROP_DEFAULT : 0; + + switch (type) { + case DLADM_PROP_VAL_CURRENT: + status = pdp->pd_get(handle, pdp, linkid, prop_val, val_cntp, + media, dld_flags, &perm_flags); + break; + + case DLADM_PROP_VAL_DEFAULT: + /* + * If defaults are not defined for the property, + * pd_defval.vd_name should be null. If the driver + * has to be contacted for the value, vd_name should + * be the empty string (""). Otherwise, dladm will + * just print whatever is in the table. + */ + if (pdp->pd_defval.vd_name == NULL) { + status = DLADM_STATUS_NOTSUP; + break; + } + + if (pdp->pd_defval.vd_name[0] != '\0') { + *val_cntp = 1; + *ret_val = pdp->pd_defval.vd_val; + free(prop_val); + return (DLADM_STATUS_OK); + } + status = pdp->pd_get(handle, pdp, linkid, prop_val, val_cntp, + media, dld_flags, &perm_flags); + break; + + case DLADM_PROP_VAL_PERSISTENT: + if (pdp->pd_flags & PD_TEMPONLY) + status = DLADM_STATUS_TEMPONLY; + else + status = i_dladm_get_linkprop_db(handle, linkid, + prop_name, prop_val, val_cntp); + break; + + default: + status = DLADM_STATUS_BADARG; + break; + } + + if (status == DLADM_STATUS_OK) { + if (pdp->pd_check != NULL) { + val_desc_t *vdp; + + vdp = malloc(sizeof (val_desc_t) * *val_cntp); + if (vdp == NULL) + status = DLADM_STATUS_NOMEM; + else + status = pdp->pd_check(handle, pdp, linkid, + prop_val, *val_cntp, vdp, media); + if (status == DLADM_STATUS_OK) { + for (valc = 0; valc < *val_cntp; valc++) + ret_val[valc] = vdp[valc].vd_val; + } + free(vdp); + } else { + for (valc = 0; valc < *val_cntp; valc++) { + for (i = 0; i < pdp->pd_noptval; i++) { + if (strcmp(pdp->pd_optval[i].vd_name, + prop_val[valc]) == 0) { + ret_val[valc] = + pdp->pd_optval[i].vd_val; + break; + } + } + if (i == pdp->pd_noptval) { + status = DLADM_STATUS_FAILED; + break; + } + } + } + } + + free(prop_val); + + return (status); +} + /*ARGSUSED*/ static int i_dladm_init_one_prop(dladm_handle_t handle, datalink_id_t linkid, @@ -2390,13 +2635,13 @@ i_dladm_get_public_prop(dladm_handle_t handle, datalink_id_t linkid, /* ARGSUSED */ static dladm_status_t -i_dladm_defmtu_check(dladm_handle_t handle, prop_desc_t *pdp, +i_dladm_uint32_check(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid, char **prop_val, uint_t val_cnt, val_desc_t *v, datalink_media_t media) { if (val_cnt != 1) return (DLADM_STATUS_BADVAL); - v->vd_val = atoi(prop_val[0]); + v->vd_val = strtoul(prop_val[0], NULL, 0); return (DLADM_STATUS_OK); } @@ -2852,6 +3097,247 @@ i_dladm_getset_defval(dladm_handle_t handle, prop_desc_t *pdp, return (status); } +/* ARGSUSED */ +static dladm_status_t +get_stp_prop(dladm_handle_t handle, struct prop_desc *pd, datalink_id_t linkid, + char **prop_val, uint_t *val_cnt, datalink_media_t media, uint_t flags, + uint_t *perm_flags) +{ + const bridge_public_prop_t *bpp; + dladm_status_t retv; + int val, i; + + if (flags != 0) + return (DLADM_STATUS_NOTSUP); + *perm_flags = MAC_PROP_PERM_RW; + *val_cnt = 1; + for (bpp = bridge_prop; bpp->bpp_name != NULL; bpp++) + if (strcmp(bpp->bpp_name, pd->pd_name) == 0) + break; + retv = dladm_bridge_get_port_cfg(handle, linkid, bpp->bpp_code, &val); + /* If the daemon isn't running, then return the persistent value */ + if (retv == DLADM_STATUS_NOTFOUND) { + if (i_dladm_get_linkprop_db(handle, linkid, pd->pd_name, + prop_val, val_cnt) != DLADM_STATUS_OK) + (void) strlcpy(*prop_val, pd->pd_defval.vd_name, + DLADM_PROP_VAL_MAX); + return (DLADM_STATUS_OK); + } + if (retv != DLADM_STATUS_OK) { + (void) strlcpy(*prop_val, "?", DLADM_PROP_VAL_MAX); + return (retv); + } + if (val == pd->pd_defval.vd_val && pd->pd_defval.vd_name[0] != '\0') { + (void) strlcpy(*prop_val, pd->pd_defval.vd_name, + DLADM_PROP_VAL_MAX); + return (DLADM_STATUS_OK); + } + for (i = 0; i < pd->pd_noptval; i++) { + if (val == pd->pd_optval[i].vd_val) { + (void) strlcpy(*prop_val, pd->pd_optval[i].vd_name, + DLADM_PROP_VAL_MAX); + return (DLADM_STATUS_OK); + } + } + (void) snprintf(*prop_val, DLADM_PROP_VAL_MAX, "%u", (unsigned)val); + return (DLADM_STATUS_OK); +} + +/* ARGSUSED1 */ +static dladm_status_t +set_stp_prop(dladm_handle_t handle, prop_desc_t *pd, datalink_id_t linkid, + val_desc_t *vdp, uint_t val_cnt, uint_t flags, datalink_media_t media) +{ + /* + * Special case for mcheck: the daemon resets the value to zero, and we + * don't want the daemon to refresh itself; it leads to deadlock. + */ + if (flags & DLADM_OPT_NOREFRESH) + return (DLADM_STATUS_OK); + + /* Tell the running daemon, if any */ + return (dladm_bridge_refresh(handle, linkid)); +} + +/* + * This is used only for stp_priority, stp_cost, and stp_mcheck. + */ +/* ARGSUSED */ +static dladm_status_t +check_stp_prop(dladm_handle_t handle, struct prop_desc *pd, + datalink_id_t linkid, char **prop_val, uint_t val_cnt, val_desc_t *vdp, + datalink_media_t media) +{ + char *cp; + boolean_t iscost; + + if (val_cnt != 1) + return (DLADM_STATUS_BADVALCNT); + + if (prop_val == NULL) { + vdp->vd_val = 0; + } else { + /* Only stp_priority and stp_cost use this function */ + iscost = strcmp(pd->pd_name, "stp_cost") == 0; + + if (iscost && strcmp(prop_val[0], "auto") == 0) { + /* Illegal value 0 is allowed to mean "automatic" */ + vdp->vd_val = 0; + } else { + errno = 0; + vdp->vd_val = strtoul(prop_val[0], &cp, 0); + if (errno != 0 || *cp != '\0') + return (DLADM_STATUS_BADVAL); + } + } + + if (iscost) { + return (vdp->vd_val > 65535 ? DLADM_STATUS_BADVAL : + DLADM_STATUS_OK); + } else { + if (vdp->vd_val > 255) + return (DLADM_STATUS_BADVAL); + /* + * If the user is setting stp_mcheck non-zero, then (per the + * IEEE management standards and UNH testing) we need to check + * whether this link is part of a bridge that is running RSTP. + * If it's not, then setting the flag is an error. Note that + * errors are intentionally discarded here; it's the value + * that's the problem -- it's not a bad value, merely one that + * can't be used now. + */ + if (strcmp(pd->pd_name, "stp_mcheck") == 0 && + vdp->vd_val != 0) { + char bridge[MAXLINKNAMELEN]; + UID_STP_CFG_T cfg; + dladm_bridge_prot_t brprot; + + if (dladm_bridge_getlink(handle, linkid, bridge, + sizeof (bridge)) != DLADM_STATUS_OK || + dladm_bridge_get_properties(bridge, &cfg, + &brprot) != DLADM_STATUS_OK) + return (DLADM_STATUS_FAILED); + if (cfg.force_version <= 1) + return (DLADM_STATUS_FAILED); + } + return (DLADM_STATUS_OK); + } +} + +/* ARGSUSED */ +static dladm_status_t +get_bridge_forward(dladm_handle_t handle, struct prop_desc *pd, + datalink_id_t linkid, char **prop_val, uint_t *val_cnt, + datalink_media_t media, uint_t flags, uint_t *perm_flags) +{ + dladm_status_t retv; + uint_t val; + + if (flags != 0) + return (DLADM_STATUS_NOTSUP); + *perm_flags = MAC_PROP_PERM_RW; + *val_cnt = 1; + retv = dladm_bridge_get_forwarding(handle, linkid, &val); + if (retv == DLADM_STATUS_NOTFOUND) { + if (i_dladm_get_linkprop_db(handle, linkid, pd->pd_name, + prop_val, val_cnt) != DLADM_STATUS_OK) + (void) strlcpy(*prop_val, pd->pd_defval.vd_name, + DLADM_PROP_VAL_MAX); + return (DLADM_STATUS_OK); + } + if (retv == DLADM_STATUS_OK) + (void) snprintf(*prop_val, DLADM_PROP_VAL_MAX, "%u", val); + else + (void) strlcpy(*prop_val, "?", DLADM_PROP_VAL_MAX); + return (retv); +} + +/* ARGSUSED */ +static dladm_status_t +set_bridge_forward(dladm_handle_t handle, prop_desc_t *pd, datalink_id_t linkid, + val_desc_t *vdp, uint_t val_cnt, uint_t flags, datalink_media_t media) +{ + /* Tell the running daemon, if any */ + return (dladm_bridge_refresh(handle, linkid)); +} + +/* ARGSUSED */ +static dladm_status_t +get_bridge_pvid(dladm_handle_t handle, struct prop_desc *pd, + datalink_id_t linkid, char **prop_val, uint_t *val_cnt, + datalink_media_t media, uint_t flags, uint_t *perm_flags) +{ + dladm_status_t status; + dld_ioc_macprop_t *dip; + uint16_t pvid; + + if (flags != 0) + return (DLADM_STATUS_NOTSUP); + *perm_flags = MAC_PROP_PERM_RW; + *val_cnt = 1; + dip = i_dladm_buf_alloc_by_id(sizeof (uint16_t), linkid, MAC_PROP_PVID, + 0, &status); + if (dip == NULL) + return (status); + status = i_dladm_macprop(handle, dip, B_FALSE); + if (status == DLADM_STATUS_OK) { + (void) memcpy(&pvid, dip->pr_val, sizeof (pvid)); + (void) snprintf(*prop_val, DLADM_PROP_VAL_MAX, "%u", pvid); + } else { + (void) strlcpy(*prop_val, "?", DLADM_PROP_VAL_MAX); + } + free(dip); + return (status); +} + +/* ARGSUSED */ +static dladm_status_t +set_bridge_pvid(dladm_handle_t handle, prop_desc_t *pd, datalink_id_t linkid, + val_desc_t *vdp, uint_t val_cnt, uint_t flags, datalink_media_t media) +{ + dladm_status_t status; + dld_ioc_macprop_t *dip; + uint16_t pvid; + + dip = i_dladm_buf_alloc_by_id(sizeof (uint16_t), linkid, MAC_PROP_PVID, + 0, &status); + if (dip == NULL) + return (status); + pvid = vdp->vd_val; + (void) memcpy(dip->pr_val, &pvid, sizeof (pvid)); + status = i_dladm_macprop(handle, dip, B_TRUE); + free(dip); + if (status != DLADM_STATUS_OK) + return (status); + + /* Tell the running daemon, if any */ + return (dladm_bridge_refresh(handle, linkid)); +} + +/* ARGSUSED */ +static dladm_status_t +check_bridge_pvid(dladm_handle_t handle, struct prop_desc *pd, + datalink_id_t linkid, char **prop_val, uint_t val_cnt, val_desc_t *vdp, + datalink_media_t media) +{ + char *cp; + + if (val_cnt != 1) + return (DLADM_STATUS_BADVALCNT); + + if (prop_val == NULL) { + vdp->vd_val = 1; + } else { + errno = 0; + vdp->vd_val = strtoul(prop_val[0], &cp, 0); + if (errno != 0 || *cp != '\0') + return (DLADM_STATUS_BADVAL); + } + + return (vdp->vd_val > VLAN_ID_MAX ? DLADM_STATUS_BADVAL : + DLADM_STATUS_OK); +} + dladm_status_t i_dladm_wlan_param(dladm_handle_t handle, datalink_id_t linkid, void *buf, mac_prop_id_t cmd, size_t len, boolean_t set) diff --git a/usr/src/lib/libdladm/common/llib-ldladm b/usr/src/lib/libdladm/common/llib-ldladm index c95e0a5ba3..cc379d19b6 100644 --- a/usr/src/lib/libdladm/common/llib-ldladm +++ b/usr/src/lib/libdladm/common/llib-ldladm @@ -36,3 +36,4 @@ #include <libdlstat.h> #include <libdlether.h> #include <libdlsim.h> +#include <libdlbridge.h> diff --git a/usr/src/lib/libdladm/common/mapfile-vers b/usr/src/lib/libdladm/common/mapfile-vers index e3353bd99b..048b809751 100644 --- a/usr/src/lib/libdladm/common/mapfile-vers +++ b/usr/src/lib/libdladm/common/mapfile-vers @@ -54,6 +54,7 @@ SUNWprivate_1.1 { dladm_mac_walk; dladm_init_linkprop; dladm_get_linkprop; + dladm_get_linkprop_values; dladm_set_linkprop; dladm_walk_linkprop; dladm_attr_is_linkprop; @@ -194,7 +195,6 @@ SUNWprivate_1.1 { dladm_get_single_mac_stat; dladm_stats_total; dladm_stats_diff; - dladm_ether_info; dladm_ether_autoneg2str; dladm_ether_pause2str; @@ -206,6 +206,28 @@ SUNWprivate_1.1 { dladm_simnet_delete; dladm_simnet_info; dladm_simnet_up; + dladm_bridge_str2prot; + dladm_bridge_prot2str; + dladm_bridge_get_properties; + dladm_bridge_run_properties; + dladm_bridge_configure; + dladm_bridge_enable; + dladm_bridge_delete; + dladm_bridge_state; + dladm_bridge_get_portlist; + dladm_bridge_free_portlist; + dladm_bridge_setlink; + dladm_bridge_getlink; + dladm_bridge_link_state; + dladm_valid_bridgename; + dladm_observe_to_bridge; + dladm_bridge_get_fwdtable; + dladm_bridge_free_fwdtable; + dladm_bridge_get_trillnick; + dladm_bridge_free_trillnick; + dladm_bridge_get_nick; + dladm_bridge_set_nick; + dladm_bridge_get_privprop; local: *; }; diff --git a/usr/src/lib/libdladm/common/usage.c b/usr/src/lib/libdladm/common/usage.c index b78f3dff7a..82a13e4f5f 100644 --- a/usr/src/lib/libdladm/common/usage.c +++ b/usr/src/lib/libdladm/common/usage.c @@ -29,6 +29,7 @@ #include <strings.h> #include <exacct.h> #include <net/if.h> +#include <sys/ethernet.h> #include <libdladm.h> #define TIMEBUFLEN 20 diff --git a/usr/src/lib/libdladm/libdladm.xcl b/usr/src/lib/libdladm/libdladm.xcl index 9a51f37a67..5070c9457c 100644 --- a/usr/src/lib/libdladm/libdladm.xcl +++ b/usr/src/lib/libdladm/libdladm.xcl @@ -19,11 +19,9 @@ # CDDL HEADER END # # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "%Z%%M% %I% %E% SMI" -# msgid " \n\t" msgid "" msgid "%02x" @@ -32,13 +30,30 @@ msgid "%s%c" msgid "%s%d" msgid "%s/%s" msgid "%s/%s.new" +msgid "%s0" msgid "%s=" msgid "%s\n" msgid "%s\t" msgid "/" +msgid "/dev/net/%s0" msgid "/tmp/%s.lock" +msgid "/usr/lib/bridged" +msgid "/usr/sbin/trilld" msgid "0x" +msgid "bridging" +msgid "config" +msgid "debug" +msgid "force-protocol" +msgid "forward-delay" +msgid "hello-time" +msgid "max-age" +msgid "nickname" msgid "r" msgid "r+" +msgid "running" +msgid "service" +msgid "stp" +msgid "table-maximum" +msgid "trill" msgid "w" msgid "wep" diff --git a/usr/src/lib/librstp/Makefile b/usr/src/lib/librstp/Makefile new file mode 100644 index 0000000000..b94d9944a3 --- /dev/null +++ b/usr/src/lib/librstp/Makefile @@ -0,0 +1,60 @@ +# +# 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 $(SRC)/lib/Makefile.lib + +HDRS = stp_bpdu.h stp_in.h stp_vectors.h uid_stp.h +HDRDIR = common +SUBDIRS = $(MACH) + +all := TARGET = all +clean := TARGET = clean +clobber := TARGET = clobber +install := TARGET = install +lint := TARGET = lint + +.KEEP_STATE: + +all clean clobber install lint: $(SUBDIRS) + +install: THIRDPARTYLICENSE + +THIRDPARTYLICENSE: common/COPYING + $(RM) $@ + $(CP) $? $@ + +CLOBBERFILES += THIRDPARTYLICENSE + +install_h: $(ROOTHDRS) + +check: + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: + +include $(SRC)/Makefile.msg.targ +include $(SRC)/lib/Makefile.targ diff --git a/usr/src/lib/librstp/Makefile.com b/usr/src/lib/librstp/Makefile.com new file mode 100644 index 0000000000..de23f29906 --- /dev/null +++ b/usr/src/lib/librstp/Makefile.com @@ -0,0 +1,53 @@ +# +# 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. +# + +LIBRARY = librstp.a +VERS = .1 +OBJECTS = edge.o migrate.o p2p.o pcost.o port.o portinfo.o rolesel.o \ + roletrns.o statmch.o stp_in.o stpm.o stpmgmt.o sttrans.o \ + times.o topoch.o transmit.o vector.o + +include ../../Makefile.lib + +LIBS = $(DYNLIB) $(LINTLIB) + +SRCDIR = ../common +SRCS = $(OBJECTS:%.o=$(SRCDIR)/%.c) + +$(LINTLIB):= SRCS = $(SRCDIR)/$(LINTSRC) + +LDLIBS += -lc + +CFLAGS += $(CCVERBOSE) +CPPFLAGS += -I$(SRCDIR) -D__SUN__ -D__STP_INTERNAL__ + +.KEEP_STATE: + +all: $(LIBS) + +lint: lintcheck + +include ../../Makefile.targ diff --git a/usr/src/lib/librstp/THIRDPARTYLICENSE.descrip b/usr/src/lib/librstp/THIRDPARTYLICENSE.descrip new file mode 100644 index 0000000000..578adad180 --- /dev/null +++ b/usr/src/lib/librstp/THIRDPARTYLICENSE.descrip @@ -0,0 +1 @@ +SPANNING TREE SOFTWARE diff --git a/usr/src/lib/librstp/common/COPYING b/usr/src/lib/librstp/common/COPYING new file mode 100644 index 0000000000..223ede7de3 --- /dev/null +++ b/usr/src/lib/librstp/common/COPYING @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + <one line to give the library's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + <signature of Ty Coon>, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/usr/src/lib/librstp/common/ChangeLog b/usr/src/lib/librstp/common/ChangeLog new file mode 100644 index 0000000000..ef8fa2342f --- /dev/null +++ b/usr/src/lib/librstp/common/ChangeLog @@ -0,0 +1,62 @@ +ChangeLog +========= +james.d.carlson@sun.com + - Removed bits and pieces not needed for use as a stand-alone library. + - Fixed two unreachable statements (statmch.c and stpm.c). + - Wrote Makefile for use with ON. + - Changed rstplib into a real library; expecting the caller to provide + symbol definitions is just wrong. + +## +- $Log: ChangeLog,v $ +- Revision 1.7 2002/01/20 07:34:36 ralex +- - 'clilib' has been changed for 'readline' version 4.2 (thanks to +- Michael Rozhavsky <mike@nbase.co.il>) +- - 'The bug in clilib has been fixed +- +- Revision 1.6 2002/01/09 06:58:08 ralex +- The first version is to outcome +- +- Revision 1.5 2001/11/29 09:54:31 ralex +- - The bug in "Port Role Transitions state machine" has been +- fixed (hop to the DESIGNATED_LISTEN) +- - Defaults for 'bridge' and 'ports' configuration have +- been moved; order of the initialization has been changed +- +- Revision 1.4 2001/11/26 08:02:12 ralex +- All management entities from 14.8.1 & 14.8.2 are now supported +- +- Revision 1.3 2001/11/21 14:32:27 ralex +- The file ChangeLog has been 'synchrinyzed' +- +- Revision 1.2 2001/11/21 14:22:12 ralex +- - In the librstp.a : drastic change in Port Role Selection +- state machine as a result of posting +- of Mick Seaman and implementation of 802.1y Z1 +- - In libcli.c : readline completion works; for it +- the structure CMD_DSCR_T has been redisgned +- and the languages (both in 'mngr' and in 'bridge') +- have been changed +- +- Revision 1.1 2001/11/14 14:10:44 ralex +- - All per Port variables have been moved from the State +- Machines into the Port instance (it made the state +- machines much more clear) +- +- - In libcli.a instead of stupid fgets() function we use +- now readline (thanks to Michel Roshavsky) +- +- - 'mcheck' support +- +- - 'nonStp' support (I know, that it is out the standard, +- but it seems to be useful (see a discussion on +- http://www1.ietf.org/mail-archive/working-groups/bridge/current/msg00038.html) +- and our customers demand it +- +- - The function rolesel.c has been drastically fixed, IMHO +- closer to the standard +- +- - Nicer output + +## + diff --git a/usr/src/lib/librstp/common/README b/usr/src/lib/librstp/common/README new file mode 100644 index 0000000000..abcdb9e114 --- /dev/null +++ b/usr/src/lib/librstp/common/README @@ -0,0 +1,37 @@ + + +The Rapid Spanning Tree Library project contains a full implementation +of 802.1s as an library with API. There is two processes, using this +library: 'bridge' & 'mngr'. First simulates RSTP bridge behavior, second +is dedicated to link/unlink 'bridges' into virtual RSTP domain (VRSTPD). +Both 'bridge' & 'mngr' has its own simple CLI like language of commands; +these commands allow to manage the VRSTPD. There are tools to trace +state machine transitions and get traps about drastic changes. + +Purpose: studying, debugging, development. + +The library may be used in real bridges/routers while bounding to a real +system depending environment. + +To run: +1. In one shell run 'mngr' +./mngr +You will get prompt of 'mngr'; type '?' and get full help of +'mngr' commands. + +2. In another shell run bridge instance. +./bridge +You will get prompt of 'bridge'; type '?' and get full help +of bridge management commands. + +3. You may (and should) run a number of bridge instances, each in its +separate shell (this way you will be able manage them). + +For example, if there were two bridge instances, you may see examples +of the dialog in files mngr.txt, B5055.txt and B5056.txt. + +Note: prompt both of 'mngr' and of 'bridge' instance contains time stamp, +while all these process run onto the same computer, these time stamps +are synchronized. + + diff --git a/usr/src/lib/librstp/common/README.CVS.HOWTO b/usr/src/lib/librstp/common/README.CVS.HOWTO new file mode 100644 index 0000000000..987724d00e --- /dev/null +++ b/usr/src/lib/librstp/common/README.CVS.HOWTO @@ -0,0 +1,22 @@ +Anonymous CVS Access + +This project's SourceForge CVS repository can be checked out through +anonymous (pserver) CVS with the following instruction set. The module +you wish to check out must be specified as the modulename. When prompted +for a password for anonymous, simply press the Enter key. + +cvs -d:pserver:anonymous@cvs.rstplib.sourceforge.net:/cvsroot/rstplib login + +cvs -z3 -d:pserver:anonymous@cvs.rstplib.sourceforge.net:/cvsroot/rstplib co modulename + +Updates from within the module's directory do not need the -d parameter. + +Developer CVS Access via SSH + +Only project developers can access the CVS tree via this method. SSH1 must +be installed on your client machine. Substitute modulename and developername +with the proper values. Enter your site password when prompted. + +export CVS_RSH=ssh + +cvs -z3 -d:ext:developername@cvs.rstplib.sourceforge.net:/cvsroot/rstplib co modulename diff --git a/usr/src/lib/librstp/common/README.authors b/usr/src/lib/librstp/common/README.authors new file mode 100644 index 0000000000..71dc74ec09 --- /dev/null +++ b/usr/src/lib/librstp/common/README.authors @@ -0,0 +1,3 @@ +Alex Rozin <alexr@nbase.co.il> +Michael Rozhavsky <mike@nbase.co.il> + diff --git a/usr/src/lib/librstp/common/README.files b/usr/src/lib/librstp/common/README.files new file mode 100644 index 0000000000..f08ea7d503 --- /dev/null +++ b/usr/src/lib/librstp/common/README.files @@ -0,0 +1,58 @@ +This guide describes the list of the files of the project. +========================================================== + +There are two target binaries: mngr & bridge +o The first is a simplest tools to connect/disconnect + bridges and check their current connection. These its + functions are managed from command line simple language + and use the library libcli.a (see below). Beside it mngr + serves to transport BPDU messages between bridges; for + this purpose mngr uses the library libuid.a + The source code of the mngr: file mngr.c + +o The second is a simulation of virtual RSTP bridge. It + accepts two types of messages: UID_CNTRL & UID_BPDU (see + file uid.h). This program is linked with the same two + libraries libcli.a & libuid.a; beside it uses a system + independent librstp.a: implementation of Rapid Spanning + Tree (802.1w) - see below. + The source code of the bridge: files bridge.c,stp_cli.c, + stp_to.c + * bridge.c - simulates the main bridge behavior + * stp_cli.c - consists from command line functions + * stp_to.c - API, that librstp.a uses for its purposes. + The management communication between bridge and librstp.a + uses structures and definitions from the header uid_stp.h + +o libcli.a - library for command line features. It has only + one file cli.c, the API is described in the header cli.h. + +o libuid.a - the 'transport' library: the source code you + may find in the file uid_sock.c and in the two headers: + uid.h & uid_sock.h + +o (so far, so good) librstp.a - it is a heart of the project + Actually, it implements 802.1w state machines. Files + stpm.c - the RSTP instance (some reflection of Port0) + port.c - the RSTP port instance + portinfo.c - Port Information State Machine, 17.21 + rolesel.c - Port Role Selection State Machine, 17.22 + roletrns.c - Port Role Transition State Machine, 17.23 + sttrans.c - Port State Transition State Machine, 17.24 + topoch.c - Topology Change State Machine, 17.25 + migrate.c - Port Protocol Migration State Machine, 17.26 + transmit.c - Port Transmit State Machine 17.27 + pcost.c - Path Cost Resolution State Machine + edge.c - operEdge Port Resolution State Machine + p2p.c - operPointToPoit Resolution State Machine + statmch.c - generic state machine implementation + vector.c - Priority Vectors manipulations + times.c - Times manipulations + stp_in.c - API for calls from outside. + sttrans.c - API for calls from outside (dedicated for creation + deleting, starting & stopping the RSTP instance) less + relevant to the project. + + + + diff --git a/usr/src/lib/librstp/common/README.news b/usr/src/lib/librstp/common/README.news new file mode 100644 index 0000000000..5ac0d573e9 --- /dev/null +++ b/usr/src/lib/librstp/common/README.news @@ -0,0 +1,19 @@ +- All per Port variables have been moved from the State + Machines into the Port instance (it made the state + machines much more clear) + +- In libcli.a instead of stupid fgets() function we use + now readline (thanks to Michel Roshavsky) + +- 'mcheck' support + +- 'nonStp' support (I know, that it is out the standard, + but it seems to be useful (see a discussion on +http://www1.ietf.org/mail-archive/working-groups/bridge/current/msg00038.html) + and our customers demand it + +- The function rolesel.c has been drastically fixed, IMHO + closer to the standard + +- Nicer output + diff --git a/usr/src/lib/librstp/common/TODO b/usr/src/lib/librstp/common/TODO new file mode 100644 index 0000000000..7037509731 --- /dev/null +++ b/usr/src/lib/librstp/common/TODO @@ -0,0 +1,22 @@ +This guide describes our plans. +============================== + +Volunteers are welcomed ! +========================== + +1. To support a full set of management features + from 14.8 (like timeSince_Topo_Change, 14.8.1.1.3.b) + +2. To send traps about topology changes (as call to stp_to.c) + +3. To rewrite edge.c for more exact correspondence with the + standard (now portEnabled variable is realized in 'dirty' way), + simulate MAC Operational and MAC Enabled. + +4. To support SNMP management via AgentX + +5. To move the project in he direction toward 802.1s (MSTP) + + + + diff --git a/usr/src/lib/librstp/common/base.h b/usr/src/lib/librstp/common/base.h new file mode 100644 index 0000000000..f00324349e --- /dev/null +++ b/usr/src/lib/librstp/common/base.h @@ -0,0 +1,198 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* Mutual RSTP definitions */ + +#ifndef _STP_BASE_H__ +#define _STP_BASE_H__ + +#include <stdlib.h> +#include <string.h> + +#define STP_DBG 1 + +#if defined(__LINUX__) || defined(__SUN__) +# include <stddef.h> +# include <stdio.h> +# include <netinet/in.h> +# include "uid_stp.h" +#else +# include <psos.h> +# include "comdef.h" +# include "comdef.x" +# include "Bitmap/bitmap.h" +# include "Bitmap/bitmap.x" +# include "Ui/uid_stp.h" +#endif + +#ifndef INOUT +# define IN /* consider as comments near 'input' parameters */ +# define OUT /* consider as comments near 'output' parameters */ +# define INOUT /* consider as comments near 'input/output' parameters */ +#endif + +#ifndef Zero +# define Zero 0 +# define One 1 +#endif + +#ifndef Bool +# define Bool int +# define False 0 +# define True 1 +#endif + +#include "stp_bpdu.h" +#include "vector.h" +#include "times.h" + +#define RSTP_ERRORS { \ + CHOOSE(STP_OK), \ + CHOOSE(STP_Cannot_Find_Vlan), \ + CHOOSE(STP_Implicit_Instance_Create_Failed), \ + CHOOSE(STP_Small_Bridge_Priority), \ + CHOOSE(STP_Large_Bridge_Priority), \ + CHOOSE(STP_Small_Hello_Time), \ + CHOOSE(STP_Large_Hello_Time), \ + CHOOSE(STP_Small_Max_Age), \ + CHOOSE(STP_Large_Max_Age), \ + CHOOSE(STP_Small_Forward_Delay), \ + CHOOSE(STP_Large_Forward_Delay), \ + CHOOSE(STP_Forward_Delay_And_Max_Age_Are_Inconsistent),\ + CHOOSE(STP_Hello_Time_And_Max_Age_Are_Inconsistent), \ + CHOOSE(STP_Vlan_Had_Not_Yet_Been_Created), \ + CHOOSE(STP_Port_Is_Absent_In_The_Vlan), \ + CHOOSE(STP_Big_len8023_Format), \ + CHOOSE(STP_Small_len8023_Format), \ + CHOOSE(STP_len8023_Format_Gt_Len), \ + CHOOSE(STP_Not_Proper_802_3_Packet), \ + CHOOSE(STP_Invalid_Protocol), \ + CHOOSE(STP_Invalid_Version), \ + CHOOSE(STP_Had_Not_Yet_Been_Enabled_On_The_Vlan), \ + CHOOSE(STP_Cannot_Create_Instance_For_Vlan), \ + CHOOSE(STP_Cannot_Create_Instance_For_Port), \ + CHOOSE(STP_Invalid_Bridge_Priority), \ + CHOOSE(STP_There_Are_No_Ports), \ + CHOOSE(STP_Cannot_Compute_Bridge_Prio), \ + CHOOSE(STP_Another_Error), \ + CHOOSE(STP_Nothing_To_Do), \ + CHOOSE(STP_No_Such_State_Machine), \ + CHOOSE(STP_LAST_DUMMY) \ +} + +#define CHOOSE(a) a +typedef enum RSTP_ERRORS RSTP_ERRORS_T; +#undef CHOOSE + +#if !defined(__LINUX__) && !defined(__SUN__) +extern char* strdup (const char *s); + +extern USHORT Ntohs (USHORT n); +extern ULONG Htonl (ULONG h); +extern USHORT Htons (USHORT h); +extern ULONG Ntohl (ULONG n); + +#define htonl Htonl +#define htons Htons +#define ntohl Ntohl +#define ntohs Ntohs + +#endif + +#if defined(__LINUX__) || defined(__SUN__) +#ifdef STP_DBG +#define STP_FATAL(TXT, MSG, EXCOD) \ + {stp_trace ("FATAL:%s failed: %s:%d", TXT, MSG, EXCOD); \ + exit (EXCOD);} +#else +#define STP_FATAL(TXT, MSG, EXCOD) \ + abort(); +#endif +#else +#define STP_FATAL(TXT, MSG, EXCOD) \ + printf("FATAL: %s code %s:%d\n", TXT, MSG, EXCOD) +#endif + +#define STP_MALLOC(PTR, TYPE, MSG) \ + { \ + PTR = (TYPE*) calloc (1, sizeof (TYPE)); \ + if (! PTR) { \ + STP_FATAL("malloc", MSG, -6); \ + } \ + } + +#define STP_FREE(PTR, MSG) \ + { \ + if (! PTR) { \ + STP_FATAL("free", MSG, -66); \ + } \ + free (PTR); \ + PTR = NULL; \ + } + +#define STP_STRDUP(PTR, SRC, MSG) \ + { \ + PTR = strdup (SRC); \ + if (! PTR) { \ + STP_FATAL("strdup", MSG, -7); \ + } \ + } + +#define STP_NEW_IN_LIST(WHAT, TYPE, LIST, MSG) \ + { \ + STP_MALLOC(WHAT, TYPE, MSG); \ + WHAT->next = LIST; \ + LIST = WHAT; \ + } + +/* for debug trace messages */ + +#ifdef STP_DBG +#if defined(__LINUX__) +extern char* sprint_time_stump (void); +#define stp_trace(F, B...) printf("%s:" F "\n", sprint_time_stump(), ##B) +#elif defined(__SUN__) +#define stp_trace (*stp_vectors->trace) +#else +extern ULONG stp_trace (const char* fmt, ...); +#endif +#else /* !STP_DBG */ +#define stp_trace(F, B...) ((void)0) +#endif /* STP_DBG */ + + +/* Inner usage definitions & functions */ + +#if defined(__LINUX__) || defined(__SUN__) +# define RSTP_INIT_CRITICAL_PATH_PROTECTIO +# define RSTP_CRITICAL_PATH_START +# define RSTP_CRITICAL_PATH_END +#else +# define RSTP_INIT_CRITICAL_PATH_PROTECTIO STP_OUT_psos_init_semaphore () +# define RSTP_CRITICAL_PATH_START STP_OUT_psos_close_semaphore () +# define RSTP_CRITICAL_PATH_END STP_OUT_psos_open_semaphore () + extern void STP_OUT_psos_init_semaphore (void); + extern void STP_OUT_psos_close_semaphore (void); + extern void STP_OUT_psos_open_semaphore (void); +#endif + +#endif /* _STP_BASE_H__ */ diff --git a/usr/src/lib/librstp/common/choose.h b/usr/src/lib/librstp/common/choose.h new file mode 100644 index 0000000000..7f614faed1 --- /dev/null +++ b/usr/src/lib/librstp/common/choose.h @@ -0,0 +1,42 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +#ifndef _STP_CHOOSE_H__ +#define _STP_CHOOSE_H__ + +/* State machines states & debug tools. Sorry, if these are no readable enogth :( */ + +#define CHOOSE(a) a +typedef enum STATES THE_STATE_T; +#undef CHOOSE + +char * GET_STATE_NAME (int state) +{ +#define CHOOSE(a) #a +static char *state_names[] = STATES; +#undef CHOOSE + + if (BEGIN == state) return "Begin"; + return state_names[state]; +} + +#endif /* _STP_CHOOSE_H__ */ diff --git a/usr/src/lib/librstp/common/edge.c b/usr/src/lib/librstp/common/edge.c new file mode 100644 index 0000000000..8b3c3029e6 --- /dev/null +++ b/usr/src/lib/librstp/common/edge.c @@ -0,0 +1,115 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* Note: this state mashine distinkts from described in P802.1t Clause 18. */ +/* I am ready to discuss it */ + +#include "base.h" +#include "stpm.h" +#include "stp_vectors.h" + +#define STATES { \ + CHOOSE(DISABLED), \ + CHOOSE(DETECTED), \ + CHOOSE(DELAYED), \ + CHOOSE(RESOLVED) \ +} + +#define GET_STATE_NAME STP_edge_get_state_name +#include "choose.h" + +#define DEFAULT_LINK_DELAY 3 + +void +STP_edge_enter_state (STATE_MACH_T *s) +{ + register PORT_T *port = s->owner.port; + + switch (s->State) { + case BEGIN: + break; + case DISABLED: + port->operEdge = port->adminEdge; + port->wasInitBpdu = False; + port->lnkWhile = 0; + port->portEnabled = False; + break; + case DETECTED: + port->portEnabled = True; + port->lnkWhile = port->LinkDelay; + port->operEdge = False; + break; + case DELAYED: + break; + case RESOLVED: + if (! port->wasInitBpdu) { + port->operEdge = port->adminEdge; + } + break; + } +} + +Bool +STP_edge_check_conditions (STATE_MACH_T *s) +{ + register PORT_T *port = s->owner.port; + + /* If we're disabled, then stay that way. */ + if (!port->adminEnable) { + if (s->State == DISABLED) + return False; + else + return STP_hop_2_state (s, DISABLED); + } + + switch (s->State) { + case BEGIN: + return STP_hop_2_state (s, DISABLED); + case DISABLED: + if (port->adminEnable) { + return STP_hop_2_state (s, DETECTED); + } + break; + case DETECTED: + return STP_hop_2_state (s, DELAYED); + case DELAYED: + if (port->wasInitBpdu) { +#ifdef STP_DBG + if (s->debug) + stp_trace ("port %s 'edge' resolved by BPDU", port->port_name); +#endif + return STP_hop_2_state (s, RESOLVED); + } + + if (! port->lnkWhile) { +#ifdef STP_DBG + if (s->debug) + stp_trace ("port %s 'edge' resolved by timer", port->port_name); +#endif + return STP_hop_2_state (s, RESOLVED); + } + break; + case RESOLVED: + break; + } + return False; +} diff --git a/usr/src/lib/librstp/common/edge.h b/usr/src/lib/librstp/common/edge.h new file mode 100644 index 0000000000..e41264d1e0 --- /dev/null +++ b/usr/src/lib/librstp/common/edge.h @@ -0,0 +1,38 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* Note: this state mashine distinkts from described in P802.1t Clause 18. */ +/* I am ready to discuss it */ + +#ifndef _STP_EDGE_H__ +#define _STP_EDGE_H__ + +void +STP_edge_enter_state (STATE_MACH_T* s); + +Bool +STP_edge_check_conditions (STATE_MACH_T* s); + +char* +STP_edge_get_state_name (int state); + +#endif /* _STP_EDGE_H__ */ diff --git a/usr/src/lib/librstp/common/llib-lrstp b/usr/src/lib/librstp/common/llib-lrstp new file mode 100644 index 0000000000..e80ecd4156 --- /dev/null +++ b/usr/src/lib/librstp/common/llib-lrstp @@ -0,0 +1,30 @@ +/* + * 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. + */ + +/* LINTLIBRARY */ +/* PROTOLIB1 */ + +#include <stp_in.h> +#include <stp_vectors.h> diff --git a/usr/src/lib/librstp/common/mapfile-vers b/usr/src/lib/librstp/common/mapfile-vers new file mode 100644 index 0000000000..f3b7876a5a --- /dev/null +++ b/usr/src/lib/librstp/common/mapfile-vers @@ -0,0 +1,71 @@ +# +# 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. +# + +# +# MAPFILE HEADER START +# +# WARNING: STOP NOW. DO NOT MODIFY THIS FILE. +# Object versioning must comply with the rules detailed in +# +# usr/src/lib/README.mapfiles +# +# You should not be making modifications here until you've read the most current +# copy of that file. If you need help, contact a gatekeeper for guidance. +# +# MAPFILE HEADER END +# + +SUNWprivate_1.1 { + global: + STP_IN_init; + STP_IN_stpm_create; + STP_IN_stpm_delete; + STP_IN_stop_all; + STP_IN_delete_all; + STP_IN_get_is_stpm_enabled; + STP_IN_stpm_get_vlan_id_by_name; + STP_IN_stpm_get_name_by_vlan_id; + STP_IN_get_error_explanation; + STP_IN_stpm_get_cfg; + STP_IN_stpm_get_state; + STP_IN_port_get_cfg; + STP_IN_port_set_cfg; + STP_IN_port_get_state; + STP_IN_stpm_set_cfg; + STP_IN_one_second; + STP_IN_enable_port; + STP_IN_changed_port_speed; + STP_IN_changed_port_duplex; + STP_IN_check_bpdu_header; + STP_IN_rx_bpdu; + STP_IN_dbg_set_port_trace; + STP_IN_port_add; + STP_IN_port_remove; + STP_IN_get_bridge_id; + STP_IN_state2str; + local: + *; +}; diff --git a/usr/src/lib/librstp/common/migrate.c b/usr/src/lib/librstp/common/migrate.c new file mode 100644 index 0000000000..08540d3a24 --- /dev/null +++ b/usr/src/lib/librstp/common/migrate.c @@ -0,0 +1,119 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* Port Protocol Migration state machine : 17.26 */ + +#include "base.h" +#include "stpm.h" + +#define STATES { \ + CHOOSE(INIT), \ + CHOOSE(SEND_RSTP), \ + CHOOSE(SENDING_RSTP), \ + CHOOSE(SEND_STP), \ + CHOOSE(SENDING_STP) \ +} + +#define GET_STATE_NAME STP_migrate_get_state_name +#include "choose.h" + +#define MigrateTime 3 /* 17,16.4 */ + +void +STP_migrate_enter_state (STATE_MACH_T* this) +{ + register PORT_T* port = this->owner.port; + + switch (this->State) { + case BEGIN: + case INIT: + port->initPm = True; + port->mcheck = False; + break; + case SEND_RSTP: + port->mdelayWhile = MigrateTime; + port->mcheck = port->initPm = False; + port->sendRSTP = True; + break; + case SENDING_RSTP: + port->rcvdRSTP = port->rcvdSTP = False; + break; + case SEND_STP: + port->mdelayWhile = MigrateTime; + port->sendRSTP = False; + port->initPm = False; + break; + case SENDING_STP: + port->rcvdRSTP = port->rcvdSTP = False; + break; + } +} + +Bool +STP_migrate_check_conditions (STATE_MACH_T* this) +{ + register PORT_T* port = this->owner.port; + + if ((!port->portEnabled && !port->initPm) || BEGIN == this->State) + return STP_hop_2_state (this, INIT); + + switch (this->State) { + case INIT: + if (port->portEnabled) { + return STP_hop_2_state (this, (port->owner->ForceVersion >= 2) ? + SEND_RSTP : SEND_STP); + } + break; + case SEND_RSTP: + return STP_hop_2_state (this, SENDING_RSTP); + case SENDING_RSTP: + if (port->mcheck) + return STP_hop_2_state (this, SEND_RSTP); + if (port->mdelayWhile && + (port->rcvdSTP || port->rcvdRSTP)) { + return STP_hop_2_state (this, SENDING_RSTP); + } + + if (!port->mdelayWhile && port->rcvdSTP) { + return STP_hop_2_state (this, SEND_STP); + } + + if (port->owner->ForceVersion < 2) { + return STP_hop_2_state (this, SEND_STP); + } + + break; + case SEND_STP: + return STP_hop_2_state (this, SENDING_STP); + case SENDING_STP: + if (port->mcheck) + return STP_hop_2_state (this, SEND_RSTP); + if (port->mdelayWhile && + (port->rcvdSTP || port->rcvdRSTP)) + return STP_hop_2_state (this, SENDING_STP); + if (!port->mdelayWhile && port->rcvdRSTP) + return STP_hop_2_state (this, SEND_RSTP); + break; + } + return False; +} + diff --git a/usr/src/lib/librstp/common/migrate.h b/usr/src/lib/librstp/common/migrate.h new file mode 100644 index 0000000000..0c38606a4e --- /dev/null +++ b/usr/src/lib/librstp/common/migrate.h @@ -0,0 +1,37 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* Port Protocol Migration state machine : 17.26 */ + +#ifndef _STP_MIGRATE_H__ +#define _STP_MIGRATE_H__ + +void +STP_migrate_enter_state (STATE_MACH_T* s); + +Bool +STP_migrate_check_conditions (STATE_MACH_T* s); + +char* +STP_migrate_get_state_name (int state); + +#endif /* _STP_MIGRATE_H__ */ diff --git a/usr/src/lib/librstp/common/p2p.c b/usr/src/lib/librstp/common/p2p.c new file mode 100644 index 0000000000..7cb7b381eb --- /dev/null +++ b/usr/src/lib/librstp/common/p2p.c @@ -0,0 +1,90 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* Point To Point MAC mode selection machine : 6.4.3, 6.5.1 */ + +#include "base.h" +#include "stpm.h" +#include "stp_to.h" /* for STP_OUT_get_duplex */ + +#define STATES { \ + CHOOSE(INIT), \ + CHOOSE(RECOMPUTE), \ + CHOOSE(STABLE) \ +} + +#define GET_STATE_NAME STP_p2p_get_state_name +#include "choose.h" + +static Bool +computeP2P (PORT_T *port) +{ + switch (port->adminPointToPointMac) { + case P2P_FORCE_TRUE: + return True; + case P2P_FORCE_FALSE: + return False; + default: + case P2P_AUTO: + return STP_OUT_get_duplex (port->port_index); + } +} + +void +STP_p2p_enter_state (STATE_MACH_T* s) +{ + register PORT_T* port = s->owner.port; + + switch (s->State) { + case BEGIN: + case INIT: + port->p2p_recompute = True; + break; + case RECOMPUTE: + port->operPointToPointMac = computeP2P (port); + port->p2p_recompute = False; + break; + case STABLE: + break; + } +} + +Bool +STP_p2p_check_conditions (STATE_MACH_T* s) +{ + register PORT_T* port = s->owner.port; + + switch (s->State) { + case BEGIN: + case INIT: + return STP_hop_2_state (s, STABLE); + case RECOMPUTE: + return STP_hop_2_state (s, STABLE); + case STABLE: + if (port->p2p_recompute) { + return STP_hop_2_state (s, RECOMPUTE); + } + break; + } + return False; +} + diff --git a/usr/src/lib/librstp/common/p2p.h b/usr/src/lib/librstp/common/p2p.h new file mode 100644 index 0000000000..ea892ab4a6 --- /dev/null +++ b/usr/src/lib/librstp/common/p2p.h @@ -0,0 +1,37 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* Point To Point MAC mode selection machine : 6.4.3, 6.5.1 */ + +#ifndef _STP_P2P_H__ +#define _STP_P2P_H__ + +void +STP_p2p_enter_state (STATE_MACH_T* s); + +Bool +STP_p2p_check_conditions (STATE_MACH_T* s); + +char* +STP_p2p_get_state_name (int state); + +#endif /* _STP_P2P_H__ */ diff --git a/usr/src/lib/librstp/common/pcost.c b/usr/src/lib/librstp/common/pcost.c new file mode 100644 index 0000000000..72dd144043 --- /dev/null +++ b/usr/src/lib/librstp/common/pcost.c @@ -0,0 +1,132 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* Path Cost monitoring state machine */ + +#include "base.h" +#include "stpm.h" +#include "stp_to.h" /* for STP_OUT_get_port_oper_speed */ + +#define STATES { \ + CHOOSE(AUTO), \ + CHOOSE(FORSE), \ + CHOOSE(STABLE) \ +} + +#define GET_STATE_NAME STP_pcost_get_state_name +#include "choose.h" + +static long +computeAutoPCost (STATE_MACH_T *this) +{ + long lret; + register PORT_T* port = this->owner.port; + + if (port->usedSpeed < 10L) { /* < 10Mb/s */ + lret = 20000000; + } else if (port->usedSpeed <= 10L) { /* 10 Mb/s */ + lret = 2000000; + } else if (port->usedSpeed <= 100L) { /* 100 Mb/s */ + lret = 200000; + } else if (port->usedSpeed <= 1000L) { /* 1 Gb/s */ + lret = 20000; + } else if (port->usedSpeed <= 10000L) { /* 10 Gb/s */ + lret = 2000; + } else if (port->usedSpeed <= 100000L) { /* 100 Gb/s */ + lret = 200; + } else if (port->usedSpeed <= 1000000L) { /* 1 GTb/s */ + lret = 20; + } else if (port->usedSpeed <= 10000000L) { /* 10 Tb/s */ + lret = 2; + } else /* ??? */ { /* > Tb/s */ + lret = 1; + } +#ifdef STP_DBG + if (port->pcost->debug) { + stp_trace ("usedSpeed=%lu lret=%ld", port->usedSpeed, lret); + } +#endif + + return lret; +} + +/* ARGSUSED */ +static void +updPortPathCost (STATE_MACH_T *this) +{ +} + +void +STP_pcost_enter_state (STATE_MACH_T *this) +{ + register PORT_T* port = this->owner.port; + + switch (this->State) { + case BEGIN: + break; + case AUTO: + port->operSpeed = STP_OUT_get_port_oper_speed (port->port_index); +#ifdef STP_DBG + if (port->pcost->debug) { + stp_trace ("AUTO:operSpeed=%lu", port->operSpeed); + } +#endif + port->usedSpeed = port->operSpeed; + port->operPCost = computeAutoPCost (this); + break; + case FORSE: + port->operPCost = port->adminPCost; + port->usedSpeed = (unsigned long)-1; + break; + case STABLE: + updPortPathCost (this); + break; + } +} + +Bool +STP_pcost_check_conditions (STATE_MACH_T* this) +{ + register PORT_T* port = this->owner.port; + + switch (this->State) { + case BEGIN: + return STP_hop_2_state (this, AUTO); + case AUTO: + return STP_hop_2_state (this, STABLE); + case FORSE: + return STP_hop_2_state (this, STABLE); + case STABLE: + if (ADMIN_PORT_PATH_COST_AUTO == port->adminPCost && + port->operSpeed != port->usedSpeed) { + return STP_hop_2_state (this, AUTO); + } + + if (ADMIN_PORT_PATH_COST_AUTO != port->adminPCost && + port->operPCost != port->adminPCost) { + return STP_hop_2_state (this, FORSE); + } + break; + } + return False; +} + diff --git a/usr/src/lib/librstp/common/pcost.h b/usr/src/lib/librstp/common/pcost.h new file mode 100644 index 0000000000..cb19ade3bc --- /dev/null +++ b/usr/src/lib/librstp/common/pcost.h @@ -0,0 +1,37 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* Path Cost monitoring state machine */ + +#ifndef _STP_PCOST_H__ +#define _STP_PCOST_H__ + +void +STP_pcost_enter_state (STATE_MACH_T* s); + +Bool +STP_pcost_check_conditions (STATE_MACH_T* s); + +char* +STP_pcost_get_state_name (int state); + +#endif /* _STP_PCOST_H__ */ diff --git a/usr/src/lib/librstp/common/port.c b/usr/src/lib/librstp/common/port.c new file mode 100644 index 0000000000..30ba26a72e --- /dev/null +++ b/usr/src/lib/librstp/common/port.c @@ -0,0 +1,253 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* STP PORT instance : 17.18, 17.15 */ + +#include "base.h" +#include "stpm.h" +#include "stp_in.h" + +/* #include "rolesel.h" */ +#include "portinfo.h" +#include "roletrns.h" +#include "sttrans.h" +#include "topoch.h" +#include "migrate.h" +#include "transmit.h" +#include "p2p.h" +#include "pcost.h" +#include "edge.h" + +#include "stp_to.h" /* for STP_OUT_get_port_name & STP_OUT_get_port_link_status */ + +int port_trace_flags; + +PORT_T * +STP_port_create (STPM_T* stpm, int port_index) +{ + PORT_T* this; + UID_STP_PORT_CFG_T port_cfg; + register int iii; + unsigned short port_prio; + + /* check, if the port has just been added */ + for (this = stpm->ports; this; this = this->next) { + if (this->port_index == port_index) { + return NULL; + } + } + + STP_NEW_IN_LIST(this, PORT_T, stpm->ports, "port create"); + + this->owner = stpm; + this->machines = NULL; + this->port_index = port_index; + this->port_name = strdup (STP_OUT_get_port_name (port_index)); + this->uptime = 0; + + STP_OUT_get_init_port_cfg (stpm->vlan_id, port_index, &port_cfg); + port_prio = port_cfg.port_priority; + this->admin_non_stp = port_cfg.admin_non_stp; + this->adminEdge = port_cfg.admin_edge; + this->adminPCost = port_cfg.admin_port_path_cost; + this->adminPointToPointMac = port_cfg.admin_point2point; + + this->LinkDelay = DEF_LINK_DELAY; + this->port_id = (port_prio << 8) + port_index; + + iii = 0; + this->timers[iii++] = &this->fdWhile; + this->timers[iii++] = &this->helloWhen; + this->timers[iii++] = &this->mdelayWhile; + this->timers[iii++] = &this->rbWhile; + this->timers[iii++] = &this->rcvdInfoWhile; + this->timers[iii++] = &this->rrWhile; + this->timers[iii++] = &this->tcWhile; + this->timers[iii++] = &this->txCount; + this->timers[iii++] = &this->lnkWhile; + + /* create and bind port state machines */ + STP_STATE_MACH_IN_LIST(topoch); + + STP_STATE_MACH_IN_LIST(migrate); + + STP_STATE_MACH_IN_LIST(p2p); + + STP_STATE_MACH_IN_LIST(edge); + + STP_STATE_MACH_IN_LIST(pcost) + + STP_STATE_MACH_IN_LIST(info); + + STP_STATE_MACH_IN_LIST(roletrns); + + STP_STATE_MACH_IN_LIST(sttrans); + + STP_STATE_MACH_IN_LIST(transmit); + +#ifdef STP_DBG + +#if 0 + this->roletrns->ignoreHop2State = 14; /* DESIGNATED_PORT; */ + this->info->ignoreHop2State = 3; /* CURRENT */ + this->transmit->ignoreHop2State = 3; /* IDLE */ + this->edge->ignoreHop2State = 0; /* DISABLED; */ +#endif + +#if 0 + this->info->debug = 1; + this->pcost->debug = 1; + this->p2p->debug = 1; + this->edge->debug = 1; + this->migrate->debug = 1; + this->sttrans->debug = 1; + this->topoch->debug = 1; + this->roletrns->debug = 1; +#endif + this->sttrans->debug = 1; + +#endif + return this; +} + +void +STP_port_init (PORT_T* this, STPM_T* stpm, Bool check_link) +{ + if (check_link) { + this->adminEnable = STP_OUT_get_port_link_status (this->port_index); + STP_VECT_create (&this->designPrio, + &stpm->BrId, + 0, + &stpm->BrId, + this->port_id, + this->port_id); + STP_copy_times (&this->designTimes, &stpm->rootTimes); + } + + /* reset timers */ + this->fdWhile = + this->helloWhen = + this->mdelayWhile = + this->rbWhile = + this->rcvdInfoWhile = + this->rrWhile = + this->tcWhile = + this->txCount = 0; + + this->msgPortRole = RSTP_PORT_ROLE_UNKN; + this->selectedRole = DisabledPort; + this->sendRSTP = True; + this->operSpeed = STP_OUT_get_port_oper_speed (this->port_index); + this->p2p_recompute = True; +} + +void +STP_port_delete (PORT_T* this) +{ + STPM_T* stpm; + register PORT_T* prev; + register PORT_T* tmp; + register STATE_MACH_T* stater; + register void* pv; + + stpm = this->owner; + + free (this->port_name); + for (stater = this->machines; stater; ) { + pv = (void*) stater->next; + STP_state_mach_delete (stater); + stater = (STATE_MACH_T*) pv; + } + + prev = NULL; + for (tmp = stpm->ports; tmp; tmp = tmp->next) { + if (tmp->port_index == this->port_index) { + if (prev) { + prev->next = this->next; + } else { + stpm->ports = this->next; + } + STP_FREE(this, "stp instance"); + break; + } + prev = tmp; + } +} + +int +STP_port_rx_bpdu (PORT_T* this, BPDU_T* bpdu, size_t len) +{ + STP_info_rx_bpdu (this, bpdu, len); + + return 0; +} + +#ifdef STP_DBG +int STP_port_trace_state_machine (PORT_T* this, char* mach_name, int enadis) +{ + STATE_MACH_T *stater; + int nmatch = 0; + + for (stater = this->machines; stater; stater = stater->next) { + if (! strcmp (mach_name, "all") || ! strcmp (mach_name, stater->name)) { + if (stater->debug != enadis) + { + stp_trace ("port %s on %s trace %-8s (was %s) now %s", + this->port_name, this->owner->name, + stater->name, + stater->debug ? " enabled" :"disabled", + enadis ? " enabled" :"disabled"); + } + stater->debug = enadis; + nmatch++; + } + } + + if (nmatch == 0) { + stp_trace("port %s no such state machine as '%s'", this->port_name, + mach_name); + return STP_No_Such_State_Machine; + } + + return 0; +} + +void STP_port_trace_flags (char* title, PORT_T* this) +{ + unsigned long flag = 0L; + + if (!port_trace_flags) return; + + if (this->reRoot) flag |= 0x000001L; + if (this->sync) flag |= 0x000002L; + if (this->synced) flag |= 0x000004L; + + if (this->proposed) flag |= 0x000010L; + if (this->proposing) flag |= 0x000020L; + if (this->agreed) flag |= 0x000040L; + if (this->updtInfo) flag |= 0x000080L; + + if (this->operEdge) flag |= 0x000100L; + stp_trace (" %-12s: flags=0x%04lx fdWhile=%d port=%s", title, flag, this->fdWhile, this->port_name); +} + +#endif diff --git a/usr/src/lib/librstp/common/port.h b/usr/src/lib/librstp/common/port.h new file mode 100644 index 0000000000..03556eade1 --- /dev/null +++ b/usr/src/lib/librstp/common/port.h @@ -0,0 +1,185 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* STP PORT instance : 17.18, 17.15 */ + +#ifndef _STP_PORT_H__ +#define _STP_PORT_H__ + +#include "statmch.h" + +#define TIMERS_NUMBER 9 +typedef unsigned int PORT_TIMER_T; + +typedef enum { + Mine, + Aged, + Received, + Disabled +} INFO_IS_T; + +typedef enum { + SuperiorDesignateMsg, + RepeatedDesignateMsg, + ConfirmedRootMsg, + OtherMsg +} RCVD_MSG_T; + +typedef enum { + DisabledPort = 0, + AlternatePort, + BackupPort, + RootPort, + DesignatedPort, + NonStpPort +} PORT_ROLE_T; + +typedef struct port_t { + struct port_t* next; + + /* per Port state machines */ + STATE_MACH_T* info; /* 17.21 */ + STATE_MACH_T* roletrns; /* 17.23 */ + STATE_MACH_T* sttrans; /* 17.24 */ + STATE_MACH_T* topoch; /* 17.25 */ + STATE_MACH_T* migrate; /* 17.26 */ + STATE_MACH_T* transmit; /* 17.26 */ + STATE_MACH_T* p2p; /* 6.4.3, 6.5.1 */ + STATE_MACH_T* edge; /* */ + STATE_MACH_T* pcost; /* */ + + STATE_MACH_T* machines; /* list of machines */ + + struct stpm_t* owner; /* Bridge, that this port belongs to */ + + /* per port Timers */ + PORT_TIMER_T fdWhile; /* 17.15.1 */ + PORT_TIMER_T helloWhen; /* 17.15.2 */ + PORT_TIMER_T mdelayWhile; /* 17.15.3 */ + PORT_TIMER_T rbWhile; /* 17.15.4 */ + PORT_TIMER_T rcvdInfoWhile;/* 17.15.5 */ + PORT_TIMER_T rrWhile; /* 17.15.6 */ + PORT_TIMER_T tcWhile; /* 17.15.7 */ + PORT_TIMER_T txCount; /* 17.18.40 */ + PORT_TIMER_T lnkWhile; + + PORT_TIMER_T* timers[TIMERS_NUMBER]; /*list of timers */ + + Bool agreed; /* 17.18.1 */ + PRIO_VECTOR_T designPrio; /* 17.18.2 */ + TIMEVALUES_T designTimes; /* 17.18.3 */ + Bool forward; /* 17.18.4 */ + Bool forwarding; /* 17.18.5 */ + INFO_IS_T infoIs; /* 17.18.6 */ + Bool initPm; /* 17.18.7 */ + Bool learn; /* 17.18.8 */ + Bool learning; /* 17.18.9 */ + Bool mcheck; /* 17.18.10 */ + PRIO_VECTOR_T msgPrio; /* 17.18.11 */ + TIMEVALUES_T msgTimes; /* 17.18.12 */ + Bool newInfo; /* 17.18.13 */ + Bool operEdge; /* 17.18.14 */ + Bool adminEdge; /* 17.18.14 */ + Bool portEnabled; /* 17.18.15 */ + PORT_ID port_id; /* 17.18.16 */ + PRIO_VECTOR_T portPrio; /* 17.18.17 */ + TIMEVALUES_T portTimes; /* 17.18.18 */ + Bool proposed; /* 17.18.19 */ + Bool proposing; /* 17.18.20 */ + Bool rcvdBpdu; /* 17.18.21 */ + RCVD_MSG_T rcvdMsg; /* 17.18.22 */ + Bool rcvdRSTP; /* 17/18.23 */ + Bool rcvdSTP; /* 17.18.24 */ + Bool rcvdTc; /* 17.18.25 */ + Bool rcvdTcAck; /* 17.18.26 */ + Bool rcvdTcn; /* 17.18.27 */ + Bool reRoot; /* 17.18.28 */ + Bool reselect; /* 17.18.29 */ + PORT_ROLE_T role; /* 17.18.30 */ + Bool selected; /* 17.18.31 */ + PORT_ROLE_T selectedRole; /* 17.18.32 */ + Bool sendRSTP; /* 17.18.33 */ + Bool sync; /* 17.18.34 */ + Bool synced; /* 17.18.35 */ + Bool tc; /* 17.18.36 */ + Bool tcAck; /* 17.18.37 */ + Bool tcProp; /* 17.18.38 */ + + Bool updtInfo; /* 17.18.41 */ + + /* message information */ + unsigned char msgBpduVersion; + unsigned char msgBpduType; + unsigned char msgPortRole; + unsigned char msgFlags; + + unsigned long adminPCost; /* may be ADMIN_PORT_PATH_COST_AUTO */ + unsigned long operPCost; + unsigned long operSpeed; + unsigned long usedSpeed; + int LinkDelay; /* TBD: LinkDelay may be managed ? */ + Bool adminEnable; /* 'has LINK' */ + Bool wasInitBpdu; + Bool admin_non_stp; + + Bool p2p_recompute; + Bool operPointToPointMac; + ADMIN_P2P_T adminPointToPointMac; + + /* statistics */ + unsigned long rx_cfg_bpdu_cnt; + unsigned long rx_rstp_bpdu_cnt; + unsigned long rx_tcn_bpdu_cnt; + + unsigned long uptime; /* 14.8.2.1.3.a */ + + int port_index; + char* port_name; + +#ifdef STP_DBG + unsigned int skip_rx; + unsigned int skip_tx; +#endif +} PORT_T; + +PORT_T* +STP_port_create (struct stpm_t* stpm, int port_index); + +void +STP_port_delete (PORT_T* this); + +int +STP_port_rx_bpdu (PORT_T* this, BPDU_T* bpdu, size_t len); + +void +STP_port_init (PORT_T* this, struct stpm_t* stpm, Bool check_link); + +#ifdef STP_DBG +int +STP_port_trace_state_machine (PORT_T* this, char* mach_name, int enadis); + +void +STP_port_trace_flags (char* title, PORT_T* this); +#endif + +#endif /* _STP_PORT_H__ */ + diff --git a/usr/src/lib/librstp/common/portinfo.c b/usr/src/lib/librstp/common/portinfo.c new file mode 100644 index 0000000000..67af0b3320 --- /dev/null +++ b/usr/src/lib/librstp/common/portinfo.c @@ -0,0 +1,509 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +#include "base.h" +#include "stpm.h" +#include "stp_vectors.h" + +/* The Port Information State Machine : 17.21 */ + +#define STATES { \ + CHOOSE(DISABLED), \ + CHOOSE(ENABLED), \ + CHOOSE(AGED), \ + CHOOSE(UPDATE), \ + CHOOSE(CURRENT), \ + CHOOSE(RECEIVE), \ + CHOOSE(SUPERIOR), \ + CHOOSE(REPEAT), \ + CHOOSE(AGREEMENT) \ +} + +#define GET_STATE_NAME STP_info_get_state_name +#include "choose.h" + +#if 0 /* for debug */ +void +_stp_dump (char* title, unsigned char* buff, int len) +{ + register int iii; + + stp_trace ("\n%s:", title); + for (iii = 0; iii < len; iii++) { + if (! (iii % 24)) stp_trace ("\n%6d:", iii); + if (! (iii % 8)) stp_trace (" "); + stp_trace ("%02lx", (unsigned long) buff[iii]); + } + stp_trace ("\n"); +} +#endif + +static RCVD_MSG_T +rcvBpdu (STATE_MACH_T* this) +{/* 17.19.8 */ + int bridcmp; + register PORT_T* port = this->owner.port; + + if (port->msgBpduType == BPDU_TOPO_CHANGE_TYPE) { +#ifdef STP_DBG + if (this->debug) { + stp_trace ("%s", "rcvBpdu: OtherMsg:BPDU_TOPO_CHANGE_TYPE"); + } +#endif + return OtherMsg; + } + + port->msgPortRole = RSTP_PORT_ROLE_UNKN; + + if (BPDU_RSTP == port->msgBpduType) { + port->msgPortRole = (port->msgFlags & PORT_ROLE_MASK) >> PORT_ROLE_OFFS; + } + + if (RSTP_PORT_ROLE_DESGN == port->msgPortRole || + BPDU_CONFIG_TYPE == port->msgBpduType) { + bridcmp = STP_VECT_compare_vector (&port->msgPrio, &port->portPrio); + + if (bridcmp < 0 || + (! STP_VECT_compare_bridge_id (&port->msgPrio.design_bridge, + &port->portPrio.design_bridge) && + port->msgPrio.design_port == port->portPrio.design_port && + STP_compare_times (&port->msgTimes, &port->portTimes))) { +#ifdef STP_DBG + if (this->debug) { + stp_trace ("rcvBpdu: SuperiorDesignateMsg:bridcmp=%d", (int) bridcmp); + } +#endif + return SuperiorDesignateMsg; + } + } + + if (BPDU_CONFIG_TYPE == port->msgBpduType || + RSTP_PORT_ROLE_DESGN == port->msgPortRole) { + if (! STP_VECT_compare_vector (&port->msgPrio, + &port->portPrio) && + ! STP_compare_times (&port->msgTimes, &port->portTimes)) { +#ifdef STP_DBG + if (this->debug) { + stp_trace ("%s", "rcvBpdu: RepeatedDesignateMsg"); + } +#endif + return RepeatedDesignateMsg; + } + } + + if (RSTP_PORT_ROLE_ROOT == port->msgBpduType && + port->operPointToPointMac && + ! STP_VECT_compare_bridge_id (&port->msgPrio.design_bridge, + &port->portPrio.design_bridge) && + AGREEMENT_BIT & port->msgFlags) { +#ifdef STP_DBG + if (this->debug) { + stp_trace ("%s", "rcvBpdu: ConfirmedRootMsg"); + } +#endif + return ConfirmedRootMsg; + } + +#ifdef STP_DBG + if (this->debug) { + if (RSTP_PORT_ROLE_ROOT == port->msgBpduType) { + if (!port->operPointToPointMac) { + stp_trace("rcvBpdu: OtherMsg: not point-to-point MAC"); + } else if (STP_VECT_compare_bridge_id (&port->msgPrio.design_bridge, + &port->portPrio.design_bridge)) { + STP_VECT_br_id_print("rcvBpdu: OtherMsg: msgPrio", &port->msgPrio.design_bridge, True); + STP_VECT_br_id_print("rcvBpdu: portPrio", &port->portPrio.design_bridge, True); + } else { + stp_trace("rcvBpdu: OtherMsg: agreement bit not set"); + } + } else { + stp_trace ("rcvBpdu: OtherMsg: type %d", port->msgBpduType); + } + } +#endif + return OtherMsg; +} + +/* ARGSUSED */ +static Bool +recordProposed (STATE_MACH_T* this, char* reason) +{/* 17.19.9 */ + register PORT_T* port = this->owner.port; + + if (RSTP_PORT_ROLE_DESGN == port->msgPortRole && + (PROPOSAL_BIT & port->msgFlags) && + port->operPointToPointMac) { + return True; + } + return False; +} + +static void +setTcFlags (STATE_MACH_T* this) +{/* 17.19.13 */ + register PORT_T* port = this->owner.port; + + if (BPDU_TOPO_CHANGE_TYPE == port->msgBpduType) { +#ifdef STP_DBG + if (this->debug) { + stp_trace ("port %s rx rcvdTcn", port->port_name); + } +#endif + port->rcvdTcn = True; + } else { + if (TOPOLOGY_CHANGE_BIT & port->msgFlags) { +#ifdef STP_DBG + if (this->debug) { + stp_trace ("(%s-%s) rx rcvdTc 0X%lx", + port->owner->name, port->port_name, + (unsigned long) port->msgFlags); + } +#endif + port->rcvdTc = True; + } + if (TOPOLOGY_CHANGE_ACK_BIT & port->msgFlags) { +#ifdef STP_DBG + if (this->debug) { + stp_trace ("port %s rx rcvdTcAck 0X%lx", + port->port_name, + (unsigned long) port->msgFlags); + } +#endif + port->rcvdTcAck = True; + } + } +} + +static void +updtBPDUVersion (STATE_MACH_T* this) +{/* 17.19.18 */ + register PORT_T* port = this->owner.port; + + if (BPDU_TOPO_CHANGE_TYPE == port->msgBpduType) { + port->rcvdSTP = True; + } + + if (port->msgBpduVersion < 2) { + port->rcvdSTP = True; + } + + if (BPDU_RSTP == port->msgBpduType) { + /* port->port->owner->ForceVersion >= NORMAL_RSTP + we have checked in STP_info_rx_bpdu */ + port->rcvdRSTP = True; + } +} + +static void +updtRcvdInfoWhile (STATE_MACH_T* this) +{/* 17.19.19 */ + register int eff_age, dm, dt; + register int hello3; + register PORT_T* port = this->owner.port; + + eff_age = ( + port->portTimes.MaxAge) / 16; + if (eff_age < 1) eff_age = 1; + eff_age += port->portTimes.MessageAge; + + if (eff_age <= port->portTimes.MaxAge) { + hello3 = 3 * port->portTimes.HelloTime; + dm = port->portTimes.MaxAge - eff_age; + if (dm > hello3) + dt = hello3; + else + dt = dm; + port->rcvdInfoWhile = dt; +/**** + stp_trace ("ma=%d eff_age=%d dm=%d dt=%d p=%s", + (int) port->portTimes.MessageAge, + (int) eff_age, (int) dm, (int) dt, port->port_name); +****/ + } else { + port->rcvdInfoWhile = 0; +/****/ +#ifdef STP_DBG + /*if (this->debug) */ + { + stp_trace ("port %s: MaxAge=%d MessageAge=%d HelloTime=%d rcvdInfoWhile=null !", + port->port_name, + (int) port->portTimes.MaxAge, + (int) port->portTimes.MessageAge, + (int) port->portTimes.HelloTime); + } +#endif +/****/ + } +} + + +/* ARGSUSED */ +void +STP_info_rx_bpdu (PORT_T* port, struct stp_bpdu_t* bpdu, size_t len) +{ +#if 0 + _stp_dump ("\nall BPDU", ((unsigned char*) bpdu) - 12, len + 12); + _stp_dump ("ETH_HEADER", (unsigned char*) &bpdu->eth, 5); + _stp_dump ("BPDU_HEADER", (unsigned char*) &bpdu->hdr, 4); + stp_trace ("protocol=%02x%02x version=%02x bpdu_type=%02x\n", + bpdu->hdr.protocol[0], bpdu->hdr.protocol[1], + bpdu->hdr.version, bpdu->hdr.bpdu_type); + + _stp_dump ("\nBPDU_BODY", (unsigned char*) &bpdu->body, sizeof (BPDU_BODY_T) + 2); + stp_trace ("flags=%02x\n", bpdu->body.flags); + _stp_dump ("root_id", bpdu->body.root_id, 8); + _stp_dump ("root_path_cost", bpdu->body.root_path_cost, 4); + _stp_dump ("bridge_id", bpdu->body.bridge_id, 8); + _stp_dump ("port_id", bpdu->body.port_id, 2); + _stp_dump ("message_age", bpdu->body.message_age, 2); + _stp_dump ("max_age", bpdu->body.max_age, 2); + _stp_dump ("hello_time", bpdu->body.hello_time, 2); + _stp_dump ("forward_delay", bpdu->body.forward_delay, 2); + _stp_dump ("ver_1_len", bpdu->ver_1_len, 2); +#endif + + /* check bpdu type */ + switch (bpdu->hdr.bpdu_type) { + case BPDU_CONFIG_TYPE: + port->rx_cfg_bpdu_cnt++; +#ifdef STP_DBG + if (port->info->debug) + stp_trace ("CfgBpdu on port %s", port->port_name); +#endif + if (port->admin_non_stp) return; + port->rcvdBpdu = True; + break; + case BPDU_TOPO_CHANGE_TYPE: + port->rx_tcn_bpdu_cnt++; +#ifdef STP_DBG + if (port->info->debug) + stp_trace ("TcnBpdu on port %s", port->port_name); +#endif + if (port->admin_non_stp) return; + port->rcvdBpdu = True; + port->msgBpduVersion = bpdu->hdr.version; + port->msgBpduType = bpdu->hdr.bpdu_type; + return; + default: + stp_trace ("RX undef bpdu type=%d", (int) bpdu->hdr.bpdu_type); + return; + case BPDU_RSTP: + port->rx_rstp_bpdu_cnt++; + if (port->admin_non_stp) return; + if (port->owner->ForceVersion >= NORMAL_RSTP) { + port->rcvdBpdu = True; + } else { + return; + } +#ifdef STP_DBG + if (port->info->debug) + stp_trace ("BPDU_RSTP on port %s", port->port_name); +#endif + break; + } + + port->msgBpduVersion = bpdu->hdr.version; + port->msgBpduType = bpdu->hdr.bpdu_type; + port->msgFlags = bpdu->body.flags; + + /* 17.18.11 */ + STP_VECT_get_vector (&bpdu->body, &port->msgPrio); + port->msgPrio.bridge_port = port->port_id; + + /* 17.18.12 */ + STP_get_times (&bpdu->body, &port->msgTimes); + + /* 17.18.25, 17.18.26 : see setTcFlags() */ +} + +void STP_info_enter_state (STATE_MACH_T* this) +{ + register PORT_T* port = this->owner.port; + + switch (this->State) { + case BEGIN: + port->rcvdMsg = OtherMsg; + port->msgBpduType = (unsigned char)-1; + port->msgPortRole = RSTP_PORT_ROLE_UNKN; + port->msgFlags = 0; + + /* clear port statistics */ + port->rx_cfg_bpdu_cnt = + port->rx_rstp_bpdu_cnt = + port->rx_tcn_bpdu_cnt = 0; + /* FALLTHRU */ + case DISABLED: + port->rcvdBpdu = port->rcvdRSTP = port->rcvdSTP = False; + port->updtInfo = port->proposing = False; /* In DISABLED */ + port->agreed = port->proposed = False; + port->rcvdInfoWhile = 0; + port->infoIs = Disabled; + port->reselect = True; + port->selected = False; + break; + case ENABLED: /* IEEE 802.1y, 17.21, Z.14 */ + STP_VECT_copy (&port->portPrio, &port->designPrio); + STP_copy_times (&port->portTimes, &port->designTimes); + break; + case AGED: + port->infoIs = Aged; + port->reselect = True; + port->selected = False; + break; + case UPDATE: + STP_VECT_copy (&port->portPrio, &port->designPrio); + STP_copy_times (&port->portTimes, &port->designTimes); + port->updtInfo = False; + port->agreed = port->synced = False; /* In UPDATE */ + port->proposed = port->proposing = False; /* in UPDATE */ + port->infoIs = Mine; + port->newInfo = True; +#ifdef STP_DBG + if (this->debug) { + STP_VECT_br_id_print ("updated: portPrio.design_bridge", + &port->portPrio.design_bridge, True); + STP_VECT_br_id_print ("updated: portPrio.root_bridge", + &port->portPrio.root_bridge, True); + } +#endif + break; + case CURRENT: + break; + case RECEIVE: + port->rcvdMsg = rcvBpdu (this); + updtBPDUVersion (this); + setTcFlags (this); + port->rcvdBpdu = False; + break; + case SUPERIOR: + STP_VECT_copy (&port->portPrio, &port->msgPrio); + STP_copy_times (&port->portTimes, &port->msgTimes); + updtRcvdInfoWhile (this); +#if 1 /* due 802.1y, Z.7 */ + port->agreed = False; /* deleted due 802.y in SUPERIOR */ + port->synced = False; /* due 802.y deleted in SUPERIOR */ +#endif + port->proposing = False; /* in SUPERIOR */ + port->proposed = recordProposed (this, "SUPERIOR"); + port->infoIs = Received; + port->reselect = True; + port->selected = False; +#ifdef STP_DBG + if (this->debug) { + STP_VECT_br_id_print ("stored: portPrio.design_bridge", + &port->portPrio.design_bridge, True); + STP_VECT_br_id_print ("stored: portPrio.root_bridge", + &port->portPrio.root_bridge, True); + stp_trace ("proposed=%d on port %s", + (int) port->proposed, port->port_name); + } +#endif + break; + case REPEAT: + port->proposed = recordProposed (this, "REPEAT"); + updtRcvdInfoWhile (this); + break; + case AGREEMENT: +#ifdef STP_DBG + if (port->roletrns->debug) { + stp_trace ("(%s-%s) rx AGREEMENT flag !", + port->owner->name, port->port_name); + } +#endif + + port->agreed = True; + port->proposing = False; /* In AGREEMENT */ + break; + } + +} + +Bool STP_info_check_conditions (STATE_MACH_T* this) +{ + register PORT_T* port = this->owner.port; + + if ((! port->portEnabled && port->infoIs != Disabled) || BEGIN == this->State) { + return STP_hop_2_state (this, DISABLED); + } + + switch (this->State) { + case DISABLED: + if (port->updtInfo) { + return STP_hop_2_state (this, DISABLED); + } + if (port->portEnabled && port->selected) { + return STP_hop_2_state (this, ENABLED); + } + if (port->rcvdBpdu) { + return STP_hop_2_state (this, DISABLED); + } + break; + case ENABLED: /* IEEE 802.1y, 17.21, Z.14 */ + return STP_hop_2_state (this, AGED); + break; + case AGED: + if (port->selected && port->updtInfo) { + return STP_hop_2_state (this, UPDATE); + } + break; + case UPDATE: + return STP_hop_2_state (this, CURRENT); + break; + case CURRENT: + if (port->selected && port->updtInfo) { + return STP_hop_2_state (this, UPDATE); + } + + if (Received == port->infoIs && + ! port->rcvdInfoWhile && + ! port->updtInfo && + ! port->rcvdBpdu) { + return STP_hop_2_state (this, AGED); + } + if (port->rcvdBpdu && !port->updtInfo) { + return STP_hop_2_state (this, RECEIVE); + } + break; + case RECEIVE: + switch (port->rcvdMsg) { + case SuperiorDesignateMsg: + return STP_hop_2_state (this, SUPERIOR); + case RepeatedDesignateMsg: + return STP_hop_2_state (this, REPEAT); + case ConfirmedRootMsg: + return STP_hop_2_state (this, AGREEMENT); + default: + return STP_hop_2_state (this, CURRENT); + } + break; + case SUPERIOR: + return STP_hop_2_state (this, CURRENT); + break; + case REPEAT: + return STP_hop_2_state (this, CURRENT); + break; + case AGREEMENT: + return STP_hop_2_state (this, CURRENT); + break; + } + + return False; +} diff --git a/usr/src/lib/librstp/common/portinfo.h b/usr/src/lib/librstp/common/portinfo.h new file mode 100644 index 0000000000..ef1dceb4ac --- /dev/null +++ b/usr/src/lib/librstp/common/portinfo.h @@ -0,0 +1,40 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* The Port Information State Machine : 17.21 */ + +#ifndef _STP_INFOR_H__ +#define _STP_INFOR_H__ + +void +STP_info_enter_state (STATE_MACH_T* s); + +Bool +STP_info_check_conditions (STATE_MACH_T* s); + +void +STP_info_rx_bpdu (PORT_T* this, struct stp_bpdu_t* bpdu, size_t len); + +char* +STP_info_get_state_name (int state); + +#endif /* _STP_INFOR_H__ */ diff --git a/usr/src/lib/librstp/common/rolesel.c b/usr/src/lib/librstp/common/rolesel.c new file mode 100644 index 0000000000..871622d354 --- /dev/null +++ b/usr/src/lib/librstp/common/rolesel.c @@ -0,0 +1,400 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* Port Role Selection state machine : 17.22 */ + +#include "base.h" +#include "stpm.h" +#include "stp_vectors.h" + +#define STATES { \ + CHOOSE(INIT_BRIDGE), \ + CHOOSE(ROLE_SELECTION) \ +} + +#define GET_STATE_NAME STP_rolesel_get_state_name +#include "choose.h" + +#if 0 +void stp_dbg_break_point (PORT_T * port, STPM_T* stpm) +{ +} +#endif + +static Bool +_is_backup_port (PORT_T* port, STPM_T* this) +{ + if (!STP_VECT_compare_bridge_id + (&port->portPrio.design_bridge, &this->BrId)) { +#if 0 /* def STP_DBG */ + if (port->info->debug) { + STP_VECT_br_id_print ("portPrio.design_bridge", + &port->portPrio.design_bridge, True); + STP_VECT_br_id_print (" this->BrId", + &this->BrId, True); + } + stp_dbg_break_point (port, this); +#endif + return True; + } else { + return False; + } +} + +/* ARGSUSED */ +static void +setRoleSelected (char* reason, STPM_T* stpm, PORT_T* port, + PORT_ROLE_T newRole) +{ +#ifdef STP_DBG + char* new_role_name; +#endif + + port->selectedRole = newRole; + + if (newRole == port->role) + return; + + switch (newRole) { + case DisabledPort: +#ifdef STP_DBG + new_role_name = "Disabled"; +#endif + break; + case AlternatePort: +#ifdef STP_DBG + new_role_name = "Alternate"; +#endif + break; + case BackupPort: +#ifdef STP_DBG + new_role_name = "Backup"; +#endif + break; + case RootPort: +#ifdef STP_DBG + new_role_name = "Root"; +#endif + break; + case DesignatedPort: +#ifdef STP_DBG + new_role_name = "Designated"; +#endif + break; + case NonStpPort: +#ifdef STP_DBG + new_role_name = "NonStp"; +#endif + port->role = newRole; + break; + default: +#ifdef STP_DBG + stp_trace ("%s-%s:port %s => Unknown (%d ?)", + reason, stpm->name, port->port_name, (int) newRole); +#else + abort(); +#endif + return; + } + +#ifdef STP_DBG + if (port->roletrns->debug) + stp_trace ("%s(%s-%s) => %s", + reason, stpm->name, port->port_name, new_role_name); +#endif +} + +static void +updtRoleDisableBridge (STPM_T* this) +{ /* 17.10.20 */ + register PORT_T *port; + + for (port = this->ports; port; port = port->next) { + port->selectedRole = DisabledPort; + } +} + +static void +clearReselectBridge (STPM_T* this) +{ /* 17.19.1 */ + register PORT_T *port; + + for (port = this->ports; port; port = port->next) { + port->reselect = False; + } +} + +static void +updtRootPrio (STATE_MACH_T* this) +{ + PRIO_VECTOR_T rootPathPrio; /* 17.4.2.2 */ + register PORT_T *port; + register STPM_T *stpm; + register unsigned int dm; + + stpm = this->owner.stpm; + + for (port = stpm->ports; port; port = port->next) { + if (port->admin_non_stp) { + continue; + } + + if (Disabled == port->infoIs) + continue; + if (Aged == port->infoIs) + continue; + if (Mine == port->infoIs) { +#if 0 /* def STP_DBG */ + stp_dbg_break_point (port); /* for debugger break point */ +#endif + continue; + } + + STP_VECT_copy (&rootPathPrio, &port->portPrio); + rootPathPrio.root_path_cost += port->operPCost; + + if (STP_VECT_compare_vector (&rootPathPrio, &stpm->rootPrio) < 0) { + STP_VECT_copy (&stpm->rootPrio, &rootPathPrio); + STP_copy_times (&stpm->rootTimes, &port->portTimes); + dm = (8 + stpm->rootTimes.MaxAge) / 16; + if (!dm) + dm = 1; + stpm->rootTimes.MessageAge += dm; +#ifdef STP_DBG + if (port->roletrns->debug) + stp_trace ("updtRootPrio: dm=%d rootTimes.MessageAge=%d on port %s", + (int) dm, (int) stpm->rootTimes.MessageAge, + port->port_name); +#endif + } + } +} + +static void +updtRolesBridge (STATE_MACH_T* this) +{ /* 17.19.21 */ + register PORT_T* port; + register STPM_T* stpm; +#ifdef STP_DBG + PORT_ID old_root_port; /* for tracing of root port changing */ +#endif + + stpm = this->owner.stpm; +#ifdef STP_DBG + old_root_port = stpm->rootPortId; +#endif + + STP_VECT_create (&stpm->rootPrio, &stpm->BrId, 0, &stpm->BrId, 0, 0); + STP_copy_times (&stpm->rootTimes, &stpm->BrTimes); + stpm->rootPortId = 0; + + updtRootPrio (this); + + for (port = stpm->ports; port; port = port->next) { + if (port->admin_non_stp) { + continue; + } + STP_VECT_create (&port->designPrio, + &stpm->rootPrio.root_bridge, + stpm->rootPrio.root_path_cost, + &stpm->BrId, port->port_id, port->port_id); + STP_copy_times (&port->designTimes, &stpm->rootTimes); + +#if 0 +#ifdef STP_DBG + if (port->roletrns->debug) { + STP_VECT_br_id_print ("ch:designPrio.design_bridge", + &port->designPrio.design_bridge, True); + } +#endif +#endif + } + + stpm->rootPortId = stpm->rootPrio.bridge_port; + +#ifdef STP_DBG + if (old_root_port != stpm->rootPortId) { + if (! stpm->rootPortId) { + stp_trace ("bridge %s became root", stpm->name); + } else { + stp_trace ("bridge %s new root port: %s", + stpm->name, + STP_stpm_get_port_name_by_id (stpm, stpm->rootPortId)); + } + } +#endif + + for (port = stpm->ports; port; port = port->next) { + if (port->admin_non_stp) { + setRoleSelected ("Non", stpm, port, NonStpPort); + port->forward = port->learn = True; + continue; + } + + switch (port->infoIs) { + case Disabled: + setRoleSelected ("Dis", stpm, port, DisabledPort); + break; + case Aged: + setRoleSelected ("Age", stpm, port, DesignatedPort); + port->updtInfo = True; + break; + case Mine: + setRoleSelected ("Mine", stpm, port, DesignatedPort); + if (0 != STP_VECT_compare_vector (&port->portPrio, + &port->designPrio) || + 0 != STP_compare_times (&port->portTimes, + &port->designTimes)) { + port->updtInfo = True; + } + break; + case Received: + if (stpm->rootPortId == port->port_id) { + setRoleSelected ("Rec", stpm, port, RootPort); + } else if (STP_VECT_compare_vector (&port->designPrio, &port->portPrio) < 0) { + /* Note: this important piece has been inserted after + * discussion with Mick Sieman and reading 802.1y Z1 */ + setRoleSelected ("Rec", stpm, port, DesignatedPort); + port->updtInfo = True; + break; + } else { + if (_is_backup_port (port, stpm)) { + setRoleSelected ("rec", stpm, port, BackupPort); + } else { + setRoleSelected ("rec", stpm, port, AlternatePort); + } + } + port->updtInfo = False; + break; + default: + stp_trace ("undef infoIs=%d", (int) port->infoIs); + break; + } + } + +} + + +static Bool +setSelectedBridge (STPM_T* this) +{ + register PORT_T* port; + + for (port = this->ports; port; port = port->next) { + if (port->reselect) { +#ifdef STP_DBG + stp_trace ("setSelectedBridge: TRUE=reselect on port %s", port->port_name); +#endif + return False; + } + } + + for (port = this->ports; port; port = port->next) { + port->selected = True; + } + + return True; +} + +void +STP_rolesel_enter_state (STATE_MACH_T* this) +{ + STPM_T* stpm; + + stpm = this->owner.stpm; + + switch (this->State) { + case BEGIN: + case INIT_BRIDGE: + updtRoleDisableBridge (stpm); + break; + case ROLE_SELECTION: + clearReselectBridge (stpm); + updtRolesBridge (this); + (void) setSelectedBridge (stpm); + break; + } +} + +Bool +STP_rolesel_check_conditions (STATE_MACH_T* s) +{ + STPM_T* stpm; + register PORT_T* port; + + /* + * This doesn't look right. Why should we hop state twice in a single check + * condition call? It means we can never perform the enter-state action for + * INIT_BRIDGE. + */ +#ifdef carlsonj_removed + if (BEGIN == s->State) { + (void) STP_hop_2_state (s, INIT_BRIDGE); + } +#endif + + switch (s->State) { + case BEGIN: + return STP_hop_2_state (s, INIT_BRIDGE); + case INIT_BRIDGE: + return STP_hop_2_state (s, ROLE_SELECTION); + case ROLE_SELECTION: + stpm = s->owner.stpm; + for (port = stpm->ports; port; port = port->next) { + if (port->reselect) { + /* stp_trace ("reselect on port %s", port->port_name); */ + return STP_hop_2_state (s, ROLE_SELECTION); + } + } + break; + } + + return False; +} + +void +STP_rolesel_update_stpm (STPM_T* this) +{ + register PORT_T* port; + PRIO_VECTOR_T rootPathPrio; /* 17.4.2.2 */ + + stp_trace ("%s", "??? STP_rolesel_update_stpm ???"); + STP_VECT_create (&rootPathPrio, &this->BrId, 0, &this->BrId, 0, 0); + + if (!this->rootPortId || + STP_VECT_compare_vector (&rootPathPrio, &this->rootPrio) < 0) { + STP_VECT_copy (&this->rootPrio, &rootPathPrio); + } + + for (port = this->ports; port; port = port->next) { + STP_VECT_create (&port->designPrio, + &this->rootPrio.root_bridge, + this->rootPrio.root_path_cost, + &this->BrId, port->port_id, port->port_id); + if (Received != port->infoIs || this->rootPortId == port->port_id) { + STP_VECT_copy (&port->portPrio, &port->designPrio); + } + port->reselect = True; + port->selected = False; + } +} + diff --git a/usr/src/lib/librstp/common/rolesel.h b/usr/src/lib/librstp/common/rolesel.h new file mode 100644 index 0000000000..38dad55c30 --- /dev/null +++ b/usr/src/lib/librstp/common/rolesel.h @@ -0,0 +1,41 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* Port Role Selection state machine : 17.22 */ + +#ifndef _STP_ROLES_SELECT_H +#define _STP_ROLES_SELECT_H + +void +STP_rolesel_enter_state (STATE_MACH_T* s); + +Bool +STP_rolesel_check_conditions (STATE_MACH_T* s); + +void +STP_rolesel_update_stpm (struct stpm_t* this); + +char* +STP_rolesel_get_state_name (int state); + +#endif /* _STP_ROLES_SELECT_H */ + diff --git a/usr/src/lib/librstp/common/roletrns.c b/usr/src/lib/librstp/common/roletrns.c new file mode 100644 index 0000000000..d6684d3958 --- /dev/null +++ b/usr/src/lib/librstp/common/roletrns.c @@ -0,0 +1,431 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* Port Role Transitions state machine : 17.24 */ + +#include "base.h" + +#include "stpm.h" + +#define STATES { \ + CHOOSE(INIT_PORT), \ + CHOOSE(BLOCK_PORT), \ + CHOOSE(BLOCKED_PORT), \ + CHOOSE(BACKUP_PORT), \ + CHOOSE(ROOT_PROPOSED), \ + CHOOSE(ROOT_AGREED), \ + CHOOSE(REROOT), \ + CHOOSE(ROOT_PORT), \ + CHOOSE(REROOTED), \ + CHOOSE(ROOT_LEARN), \ + CHOOSE(ROOT_FORWARD), \ + CHOOSE(DESIGNATED_PROPOSE), \ + CHOOSE(DESIGNATED_SYNCED), \ + CHOOSE(DESIGNATED_RETIRED), \ + CHOOSE(DESIGNATED_PORT), \ + CHOOSE(DESIGNATED_LISTEN), \ + CHOOSE(DESIGNATED_LEARN), \ + CHOOSE(DESIGNATED_FORWARD) \ +} + +#define GET_STATE_NAME STP_roletrns_get_state_name +#include "choose.h" + +static void +setSyncBridge (STATE_MACH_T *this) +{ + register PORT_T* port; + + for (port = this->owner.port->owner->ports; port; port = port->next) { + port->sync = True; /* in ROOT_PROPOSED (setSyncBridge) */ + } +} + +static void +setReRootBridge (STATE_MACH_T *this) +{ + register PORT_T* port; + + for (port = this->owner.port->owner->ports; port; port = port->next) { + port->reRoot = True; /* In setReRootBridge */ + } +} + +static Bool +compute_all_synced (PORT_T* this) +{ + register PORT_T* port; + + for (port = this->owner->ports; port; port = port->next) { + if (port->port_index == this->port_index) continue; + if (! port->synced) { + return False; + } + } + + return True; +} + +static Bool +compute_re_rooted (PORT_T* this) +{ + register PORT_T* port; + + for (port = this->owner->ports; port; port = port->next) { + if (port->port_index == this->port_index) continue; + if (port->rrWhile) { + return False; + } + } + return True; +} + +void +STP_roletrns_enter_state (STATE_MACH_T* this) +{ + register PORT_T* port = this->owner.port; + register STPM_T* stpm; + + stpm = port->owner; + + switch (this->State) { + case BEGIN: + case INIT_PORT: +#if 0 /* due 802.1y Z.4 */ + port->role = DisabledPort; +#else + port->role = port->selectedRole = DisabledPort; + port->reselect = True; +#endif + port->synced = False; /* in INIT */ + port->sync = True; /* in INIT */ + port->reRoot = True; /* in INIT_PORT */ + port->rrWhile = stpm->rootTimes.ForwardDelay; + port->fdWhile = stpm->rootTimes.ForwardDelay; + port->rbWhile = 0; +#ifdef STP_DBG + if (this->debug) + STP_port_trace_flags ("after init", port); +#endif + break; + case BLOCK_PORT: + port->role = port->selectedRole; + port->learn = + port->forward = False; + break; + case BLOCKED_PORT: + port->fdWhile = stpm->rootTimes.ForwardDelay; + port->synced = True; /* In BLOCKED_PORT */ + port->rrWhile = 0; + port->sync = port->reRoot = False; /* BLOCKED_PORT */ + break; + case BACKUP_PORT: + port->rbWhile = 2 * stpm->rootTimes.HelloTime; + break; + + /* 17.23.2 */ + case ROOT_PROPOSED: + setSyncBridge (this); + port->proposed = False; +#ifdef STP_DBG + if (this->debug) + STP_port_trace_flags ("ROOT_PROPOSED", port); +#endif + break; + case ROOT_AGREED: + port->proposed = port->sync = False; /* in ROOT_AGREED */ + port->synced = True; /* In ROOT_AGREED */ + port->newInfo = True; +#ifdef STP_DBG + if (this->debug) + STP_port_trace_flags ("ROOT_AGREED", port); +#endif + break; + case REROOT: + setReRootBridge (this); +#ifdef STP_DBG + if (this->debug) + STP_port_trace_flags ("REROOT", port); +#endif + break; + case ROOT_PORT: + port->role = RootPort; + port->rrWhile = stpm->rootTimes.ForwardDelay; +#ifdef STP_DBG + if (this->debug) + STP_port_trace_flags ("ROOT_PORT", port); +#endif + break; + case REROOTED: + port->reRoot = False; /* In REROOTED */ +#ifdef STP_DBG + if (this->debug) + STP_port_trace_flags ("REROOTED", port); +#endif + break; + case ROOT_LEARN: + port->fdWhile = stpm->rootTimes.ForwardDelay; + port->learn = True; +#ifdef STP_DBG + if (this->debug) + STP_port_trace_flags ("ROOT_LEARN", port); +#endif + break; + case ROOT_FORWARD: + port->fdWhile = 0; + port->forward = True; +#ifdef STP_DBG + if (this->debug) + STP_port_trace_flags ("ROOT_FORWARD", port); +#endif + break; + + /* 17.23.3 */ + case DESIGNATED_PROPOSE: + port->proposing = True; /* in DESIGNATED_PROPOSE */ + port->newInfo = True; +#ifdef STP_DBG + if (this->debug) + STP_port_trace_flags ("DESIGNATED_PROPOSE", port); +#endif + break; + case DESIGNATED_SYNCED: + port->rrWhile = 0; + port->synced = True; /* DESIGNATED_SYNCED */ + port->sync = False; /* DESIGNATED_SYNCED */ +#ifdef STP_DBG + if (this->debug) + STP_port_trace_flags ("DESIGNATED_SYNCED", port); +#endif + break; + case DESIGNATED_RETIRED: + port->reRoot = False; /* DESIGNATED_RETIRED */ +#ifdef STP_DBG + if (this->debug) + STP_port_trace_flags ("DESIGNATED_RETIRED", port); +#endif + break; + case DESIGNATED_PORT: + port->role = DesignatedPort; +#ifdef STP_DBG + if (this->debug) + STP_port_trace_flags ("DESIGNATED_PORT", port); +#endif + break; + case DESIGNATED_LISTEN: + port->learn = port->forward = False; + port->fdWhile = stpm->rootTimes.ForwardDelay; +#ifdef STP_DBG + if (this->debug) + STP_port_trace_flags ("DESIGNATED_LISTEN", port); +#endif + break; + case DESIGNATED_LEARN: + port->learn = True; + port->fdWhile = stpm->rootTimes.ForwardDelay; +#ifdef STP_DBG + if (this->debug) + STP_port_trace_flags ("DESIGNATED_LEARN", port); +#endif + break; + case DESIGNATED_FORWARD: + port->forward = True; + port->fdWhile = 0; +#ifdef STP_DBG + if (this->debug) + STP_port_trace_flags ("DESIGNATED_FORWARD", port); +#endif + break; + }; +} + +Bool +STP_roletrns_check_conditions (STATE_MACH_T* this) +{ + register PORT_T *port = this->owner.port; + register STPM_T *stpm; + Bool allSynced; + Bool allReRooted; + + stpm = port->owner; + + if (BEGIN == this->State) { + return STP_hop_2_state (this, INIT_PORT); + } + + if (port->role != port->selectedRole && + port->selected && + ! port->updtInfo) { + switch (port->selectedRole) { + case DisabledPort: + case AlternatePort: + case BackupPort: +#if 0 /* def STP_DBG */ + if (this->debug) { + stp_trace ("hop to BLOCK_PORT role=%d selectedRole=%d", + (int) port->role, (int) port->selectedRole); + } +#endif + return STP_hop_2_state (this, BLOCK_PORT); + case RootPort: + return STP_hop_2_state (this, ROOT_PORT); + case DesignatedPort: + return STP_hop_2_state (this, DESIGNATED_PORT); + default: + return False; + } + } + + switch (this->State) { + /* 17.23.1 */ + case INIT_PORT: + return STP_hop_2_state (this, BLOCK_PORT); + case BLOCK_PORT: + if (!port->selected || port->updtInfo) break; + if (!port->learning && !port->forwarding) { + return STP_hop_2_state (this, BLOCKED_PORT); + } + break; + case BLOCKED_PORT: + if (!port->selected || port->updtInfo) break; + if (port->fdWhile != stpm->rootTimes.ForwardDelay || + port->sync || + port->reRoot || + !port->synced) { + return STP_hop_2_state (this, BLOCKED_PORT); + } + if (port->rbWhile != 2 * stpm->rootTimes.HelloTime && + port->role == BackupPort) { + return STP_hop_2_state (this, BACKUP_PORT); + } + break; + case BACKUP_PORT: + return STP_hop_2_state (this, BLOCKED_PORT); + + /* 17.23.2 */ + case ROOT_PROPOSED: + return STP_hop_2_state (this, ROOT_PORT); + case ROOT_AGREED: + return STP_hop_2_state (this, ROOT_PORT); + case REROOT: + return STP_hop_2_state (this, ROOT_PORT); + case ROOT_PORT: + if (!port->selected || port->updtInfo) break; + if (!port->forward && !port->reRoot) { + return STP_hop_2_state (this, REROOT); + } + allSynced = compute_all_synced (port); + if ((port->proposed && allSynced) || + (!port->synced && allSynced)) { + return STP_hop_2_state (this, ROOT_AGREED); + } + if (port->proposed && !port->synced) { + return STP_hop_2_state (this, ROOT_PROPOSED); + } + + allReRooted = compute_re_rooted (port); + if ((!port->fdWhile || + ((allReRooted && !port->rbWhile) && stpm->ForceVersion >=2)) && + port->learn && !port->forward) { + return STP_hop_2_state (this, ROOT_FORWARD); + } + if ((!port->fdWhile || + ((allReRooted && !port->rbWhile) && stpm->ForceVersion >=2)) && + !port->learn) { + return STP_hop_2_state (this, ROOT_LEARN); + } + + if (port->reRoot && port->forward) { + return STP_hop_2_state (this, REROOTED); + } + if (port->rrWhile != stpm->rootTimes.ForwardDelay) { + return STP_hop_2_state (this, ROOT_PORT); + } + break; + case REROOTED: + return STP_hop_2_state (this, ROOT_PORT); + case ROOT_LEARN: + return STP_hop_2_state (this, ROOT_PORT); + case ROOT_FORWARD: + return STP_hop_2_state (this, ROOT_PORT); + + /* 17.23.3 */ + case DESIGNATED_PROPOSE: + return STP_hop_2_state (this, DESIGNATED_PORT); + case DESIGNATED_SYNCED: + return STP_hop_2_state (this, DESIGNATED_PORT); + case DESIGNATED_RETIRED: + return STP_hop_2_state (this, DESIGNATED_PORT); + case DESIGNATED_PORT: + if (!port->selected || port->updtInfo) break; + + if (!port->forward && !port->agreed && !port->proposing && !port->operEdge) { + return STP_hop_2_state (this, DESIGNATED_PROPOSE); + } + + if (!port->rrWhile && port->reRoot) { + return STP_hop_2_state (this, DESIGNATED_RETIRED); + } + + if (!port->learning && !port->forwarding && !port->synced) { + return STP_hop_2_state (this, DESIGNATED_SYNCED); + } + + if (port->agreed && !port->synced) { + return STP_hop_2_state (this, DESIGNATED_SYNCED); + } + if (port->operEdge && !port->synced) { + return STP_hop_2_state (this, DESIGNATED_SYNCED); + } + if (port->sync && port->synced) { + return STP_hop_2_state (this, DESIGNATED_SYNCED); + } + + if ((!port->fdWhile || port->agreed || port->operEdge) && + (!port->rrWhile || !port->reRoot) && + !port->sync && + (port->learn && !port->forward)) { + return STP_hop_2_state (this, DESIGNATED_FORWARD); + } + if ((!port->fdWhile || port->agreed || port->operEdge) && + (!port->rrWhile || !port->reRoot) && + !port->sync && !port->learn) { + return STP_hop_2_state (this, DESIGNATED_LEARN); + } + if (((port->sync && !port->synced) || + (port->reRoot && port->rrWhile)) && + !port->operEdge && (port->learn || port->forward)) { + return STP_hop_2_state (this, DESIGNATED_LISTEN); + } + break; + case DESIGNATED_LISTEN: + return STP_hop_2_state (this, DESIGNATED_PORT); + case DESIGNATED_LEARN: + return STP_hop_2_state (this, DESIGNATED_PORT); + case DESIGNATED_FORWARD: + return STP_hop_2_state (this, DESIGNATED_PORT); + }; + + return False; +} + + diff --git a/usr/src/lib/librstp/common/roletrns.h b/usr/src/lib/librstp/common/roletrns.h new file mode 100644 index 0000000000..6ba116dfa8 --- /dev/null +++ b/usr/src/lib/librstp/common/roletrns.h @@ -0,0 +1,37 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* Port Role Transitions state machine : 17.24 */ + +#ifndef _STP_ROLES_TRANSIT_H__ +#define _STP_ROLES_TRANSIT_H__ + +void +STP_roletrns_enter_state (STATE_MACH_T* s); + +Bool +STP_roletrns_check_conditions (STATE_MACH_T* s); + +char* STP_roletrns_get_state_name (int state); + +#endif /* _STP_ROLES_TRANSIT_H__ */ + diff --git a/usr/src/lib/librstp/common/statmch.c b/usr/src/lib/librstp/common/statmch.c new file mode 100644 index 0000000000..7f4c1f3a27 --- /dev/null +++ b/usr/src/lib/librstp/common/statmch.c @@ -0,0 +1,123 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* Generic (abstract) state machine : 17.13, 17.14 */ + +#include "base.h" +#include "statmch.h" +#include "stp_vectors.h" + +#if STP_DBG +# include "stpm.h" +#endif + +STATE_MACH_T * +STP_state_mach_create (void (*concreteEnterState) (STATE_MACH_T*), + Bool (*concreteCheckCondition) (STATE_MACH_T*), + char *(*concreteGetStatName) (int), + void *owner, char *name) +{ + STATE_MACH_T *this; + + STP_MALLOC(this, STATE_MACH_T, "state machine"); + + this->State = BEGIN; + this->name = (char*) strdup (name); + this->changeState = False; +#if STP_DBG + this->debug = False; + this->ignoreHop2State = BEGIN; +#endif + this->concreteEnterState = concreteEnterState; + this->concreteCheckCondition = concreteCheckCondition; + this->concreteGetStatName = concreteGetStatName; + this->owner.owner = owner; + + return this; +} + +void +STP_state_mach_delete (STATE_MACH_T *this) +{ + free (this->name); + STP_FREE(this, "state machine"); +} + +Bool +STP_check_condition (STATE_MACH_T* this) +{ + Bool bret; + + bret = (*(this->concreteCheckCondition)) (this); + if (bret) { + this->changeState = True; + } + + return bret; +} + +Bool +STP_change_state (STATE_MACH_T* this) +{ + register int number_of_loops; + + for (number_of_loops = 0; ; number_of_loops++) { + if (! this->changeState) break; + (*(this->concreteEnterState)) (this); + this->changeState = False; + (void) STP_check_condition (this); + } + + return number_of_loops; +} + +Bool +STP_hop_2_state (STATE_MACH_T* this, unsigned int new_state) +{ +#ifdef STP_DBG + switch (this->debug) { + case 0: break; + case 1: + if (new_state == this->State || new_state == this->ignoreHop2State) break; + stp_trace ("%-8s(%s-%s): %s=>%s", + this->name, + *this->owner.port->owner->name ? this->owner.port->owner->name : "Glbl", + this->owner.port->port_name, + (*(this->concreteGetStatName)) (this->State), + (*(this->concreteGetStatName)) (new_state)); + break; + case 2: + if (new_state == this->State) break; + stp_trace ("%s(%s): %s=>%s", + this->name, + *this->owner.stpm->name ? this->owner.stpm->name : "Glbl", + (*(this->concreteGetStatName)) (this->State), + (*(this->concreteGetStatName)) (new_state)); + break; + } +#endif + + this->State = new_state; + this->changeState = True; + return True; +} + diff --git a/usr/src/lib/librstp/common/statmch.h b/usr/src/lib/librstp/common/statmch.h new file mode 100644 index 0000000000..e083fdbce7 --- /dev/null +++ b/usr/src/lib/librstp/common/statmch.h @@ -0,0 +1,89 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* Generic (abstract state machine) state machine : 17.13, 17.14 */ + +#ifndef _STP_STATER_H__ +#define _STP_STATER_H__ + +#define BEGIN 9999 /* distinct from any valid state */ + +#define STP_DBG 1 + +typedef struct state_mach_t { + struct state_mach_t* next; + + char* name; /* for debugging */ +#ifdef STP_DBG + char debug; /* 0- no dbg, 1 - port, 2 - stpm */ + unsigned int ignoreHop2State; +#endif + + Bool changeState; + unsigned int State; + + void (* concreteEnterState) (struct state_mach_t * ); + Bool (* concreteCheckCondition) (struct state_mach_t * ); + char* (* concreteGetStatName) (int); + union { + struct stpm_t* stpm; + struct port_t* port; + void * owner; + } owner; + +} STATE_MACH_T; + +#define STP_STATE_MACH_IN_LIST(WHAT) \ + { \ + STATE_MACH_T* abstr; \ + \ + abstr = STP_state_mach_create (STP_##WHAT##_enter_state, \ + STP_##WHAT##_check_conditions, \ + STP_##WHAT##_get_state_name, \ + this, \ + #WHAT); \ + abstr->next = this->machines; \ + this->machines = abstr; \ + this->WHAT = abstr; \ + } + + +STATE_MACH_T * +STP_state_mach_create (void (* concreteEnterState) (STATE_MACH_T*), + Bool (* concreteCheckCondition) (STATE_MACH_T*), + char * (* concreteGetStatName) (int), + void* owner, char* name); + +void +STP_state_mach_delete (STATE_MACH_T* this); + +Bool +STP_check_condition (STATE_MACH_T* this); + +Bool +STP_change_state (STATE_MACH_T* this); + +Bool +STP_hop_2_state (STATE_MACH_T* this, unsigned int new_state); + +#endif /* _STP_STATER_H__ */ + diff --git a/usr/src/lib/librstp/common/stp_bpdu.h b/usr/src/lib/librstp/common/stp_bpdu.h new file mode 100644 index 0000000000..02773d5e88 --- /dev/null +++ b/usr/src/lib/librstp/common/stp_bpdu.h @@ -0,0 +1,90 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* BPDU formats: 9.1 - 9.3, 17.28 */ + +#ifndef _STP_BPDU_H__ +#define _STP_BPDU_H__ + +#define MIN_BPDU 7 +#define BPDU_L_SAP 0x42 +#define LLC_UI 0x03 +#define BPDU_PROTOCOL_ID 0x0000 +#define BPDU_VERSION_ID 0x00 +#define BPDU_VERSION_RAPID_ID 0x02 + +#define BPDU_TOPO_CHANGE_TYPE 0x80 +#define BPDU_CONFIG_TYPE 0x00 +#define BPDU_RSTP 0x02 + +#define TOPOLOGY_CHANGE_BIT 0x01 +#define PROPOSAL_BIT 0x02 +#define PORT_ROLE_OFFS 2 /* 0x04 & 0x08 */ +#define PORT_ROLE_MASK (0x03 << PORT_ROLE_OFFS) +#define LEARN_BIT 0x10 +#define FORWARD_BIT 0x20 +#define AGREEMENT_BIT 0x40 +#define TOPOLOGY_CHANGE_ACK_BIT 0x80 + +#define RSTP_PORT_ROLE_UNKN 0x00 +#define RSTP_PORT_ROLE_ALTBACK 0x01 +#define RSTP_PORT_ROLE_ROOT 0x02 +#define RSTP_PORT_ROLE_DESGN 0x03 + +typedef struct mac_header_t { + unsigned char dst_mac[6]; + unsigned char src_mac[6]; +} MAC_HEADER_T; + +typedef struct eth_header_t { + unsigned char len8023[2]; + unsigned char dsap; + unsigned char ssap; + unsigned char llc; +} ETH_HEADER_T; + +typedef struct bpdu_header_t { + unsigned char protocol[2]; + unsigned char version; + unsigned char bpdu_type; +} BPDU_HEADER_T; + +typedef struct bpdu_body_t { + unsigned char flags; + unsigned char root_id[8]; + unsigned char root_path_cost[4]; + unsigned char bridge_id[8]; + unsigned char port_id[2]; + unsigned char message_age[2]; + unsigned char max_age[2]; + unsigned char hello_time[2]; + unsigned char forward_delay[2]; +} BPDU_BODY_T; + +typedef struct stp_bpdu_t { + ETH_HEADER_T eth; + BPDU_HEADER_T hdr; + BPDU_BODY_T body; + unsigned char ver_1_len[2]; +} BPDU_T; + +#endif /* _STP_BPDU_H__ */ diff --git a/usr/src/lib/librstp/common/stp_in.c b/usr/src/lib/librstp/common/stp_in.c new file mode 100644 index 0000000000..f8bd6954f0 --- /dev/null +++ b/usr/src/lib/librstp/common/stp_in.c @@ -0,0 +1,1026 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* This file contains API from an operation system to the RSTP library */ + +#include "base.h" +#include "stpm.h" +#include "stp_in.h" +#include "stp_to.h" + +#define _stp_in_stpm_enable stp_in_stpm_enable + +STP_VECTORS_T *stp_vectors; + +void * +stp_in_stpm_create (int vlan_id, char* name, int* err_code) +{ + register STPM_T* this; + + /* stp_trace ("stp_in_stpm_create(%s)", name); */ + this = stpapi_stpm_find (vlan_id); + if (this) { /* it had just been created :( */ + *err_code = STP_Nothing_To_Do; + return this; + } + + this = STP_stpm_create (vlan_id, name); + if (! this) { /* can't create stpm :( */ + *err_code = STP_Cannot_Create_Instance_For_Vlan; + return NULL; + } + + *err_code = STP_OK; + return this; +} + +int +_stp_in_stpm_enable (int vlan_id, char* name, + UID_STP_MODE_T admin_state) +{ + register STPM_T* this; + Bool created_here = False; + int rc, err_code; + + /* stp_trace ("_stp_in_stpm_enable(%s)", name); */ + this = stpapi_stpm_find (vlan_id); + + if (STP_DISABLED != admin_state) { + if (! vlan_id) { /* STP_IN_stop_all (); */ + register STPM_T* stpm; + + for (stpm = STP_stpm_get_the_list (); stpm; stpm = stpm->next) { + if (STP_DISABLED != stpm->admin_state) { + STP_OUT_set_hardware_mode (stpm->vlan_id, STP_DISABLED); + (void) STP_stpm_enable (stpm, STP_DISABLED); + } + } + } + } + + if (! this) { /* it had not yet been created */ + if (STP_ENABLED == admin_state) {/* try to create it */ + stp_trace ("implicit create to vlan '%s'", name); + this = stp_in_stpm_create (vlan_id, name, &err_code); + if (! this) { + stp_trace ("implicit create to vlan '%s' failed", name); + return STP_Implicit_Instance_Create_Failed; + } + created_here = True; + } else {/* try to disable nothing ? */ + return 0; + } + } + + if (this->admin_state == admin_state) { /* nothing to do :) */ + return 0; + } + + rc = STP_stpm_enable (this, admin_state); + if (! rc) { + STP_OUT_set_hardware_mode (vlan_id, admin_state); + } + + if (rc && created_here) { + STP_stpm_delete (this); + } + + return rc; +} + + +STPM_T * +stpapi_stpm_find (int vlan_id) +{ + register STPM_T* this; + + for (this = STP_stpm_get_the_list (); this; this = this->next) + if (vlan_id == this->vlan_id) + return this; + + return NULL; +} + +static PORT_T * +_stpapi_port_find (STPM_T* this, int port_index) +{ + register PORT_T* port; + + for (port = this->ports; port; port = port->next) + if (port_index == port->port_index) { + return port; + } + + return NULL; +} + + +static void +_conv_br_id_2_uid (IN BRIDGE_ID* f, OUT UID_BRIDGE_ID_T* t) +{ + (void) memcpy (t, f, sizeof (UID_BRIDGE_ID_T)); +} + +static int +_check_stpm_config (IN UID_STP_CFG_T* uid_cfg) +{ + if (uid_cfg->bridge_priority < MIN_BR_PRIO) { + stp_trace ("%d bridge_priority small", (int) uid_cfg->bridge_priority); + return STP_Small_Bridge_Priority; + } + + if (uid_cfg->bridge_priority > MAX_BR_PRIO) { + stp_trace ("%d bridge_priority large", (int) uid_cfg->bridge_priority); + return STP_Large_Bridge_Priority; + } + + if (uid_cfg->hello_time < MIN_BR_HELLOT) { + stp_trace ("%d hello_time small", (int) uid_cfg->hello_time); + return STP_Small_Hello_Time; + } + + if (uid_cfg->hello_time > MAX_BR_HELLOT) { + stp_trace ("%d hello_time large", (int) uid_cfg->hello_time); + return STP_Large_Hello_Time; + } + + if (uid_cfg->max_age < MIN_BR_MAXAGE) { + stp_trace ("%d max_age small", (int) uid_cfg->max_age); + return STP_Small_Max_Age; + } + + if (uid_cfg->max_age > MAX_BR_MAXAGE) { + stp_trace ("%d max_age large", (int) uid_cfg->max_age); + return STP_Large_Max_Age; + } + + if (uid_cfg->forward_delay < MIN_BR_FWDELAY) { + stp_trace ("%d forward_delay small", (int) uid_cfg->forward_delay); + return STP_Small_Forward_Delay; + } + + if (uid_cfg->forward_delay > MAX_BR_FWDELAY) { + stp_trace ("%d forward_delay large", (int) uid_cfg->forward_delay); + return STP_Large_Forward_Delay; + } + + if (2 * (uid_cfg->forward_delay - 1) < uid_cfg->max_age) { + return STP_Forward_Delay_And_Max_Age_Are_Inconsistent; + } + + if (uid_cfg->max_age < 2 * (uid_cfg->hello_time + 1)) { + return STP_Hello_Time_And_Max_Age_Are_Inconsistent; + } + + return 0; +} + +static void +_stp_in_enable_port_on_stpm (STPM_T* stpm, int port_index, Bool enable) +{ + register PORT_T* port; + + port = _stpapi_port_find (stpm, port_index); + if (! port) return; + if (port->portEnabled == enable) {/* nothing to do :) */ + return; + } + + port->uptime = 0; + if (enable) { /* clear port statistics */ + port->rx_cfg_bpdu_cnt = + port->rx_rstp_bpdu_cnt = + port->rx_tcn_bpdu_cnt = 0; + } + +#ifdef STP_DBG + if (port->edge->debug) { + stp_trace ("Port %s became '%s' adminEdge=%c", + port->port_name, enable ? "enable" : "disable", + port->adminEdge ? 'Y' : 'N'); + } +#endif + + port->adminEnable = enable; + STP_port_init (port, stpm, False); + + port->reselect = True; + port->selected = False; +} + +void +STP_IN_init (STP_VECTORS_T *vectors) +{ + RSTP_INIT_CRITICAL_PATH_PROTECTIO; + stp_vectors = vectors; +} + +int +STP_IN_stpm_get_cfg (IN int vlan_id, OUT UID_STP_CFG_T* uid_cfg) +{ + register STPM_T* this; + + uid_cfg->field_mask = 0; + + RSTP_CRITICAL_PATH_START; + this = stpapi_stpm_find (vlan_id); + + if (!this) { /* it had not yet been created :( */ + RSTP_CRITICAL_PATH_END; + return STP_Vlan_Had_Not_Yet_Been_Created; + } + + if (this->admin_state != STP_DISABLED) { + uid_cfg->field_mask |= BR_CFG_STATE; + } + uid_cfg->stp_enabled = this->admin_state; + + if (this->ForceVersion != 2) { + uid_cfg->field_mask |= BR_CFG_FORCE_VER; + } + uid_cfg->force_version = this->ForceVersion; + + if (this->BrId.prio != DEF_BR_PRIO) { + uid_cfg->field_mask |= BR_CFG_PRIO; + } + uid_cfg->bridge_priority = this->BrId.prio; + + if (this->BrTimes.MaxAge != DEF_BR_MAXAGE) { + uid_cfg->field_mask |= BR_CFG_AGE; + } + uid_cfg->max_age = this->BrTimes.MaxAge; + + if (this->BrTimes.HelloTime != DEF_BR_HELLOT) { + uid_cfg->field_mask |= BR_CFG_HELLO; + } + uid_cfg->hello_time = this->BrTimes.HelloTime; + + if (this->BrTimes.ForwardDelay != DEF_BR_FWDELAY) { + uid_cfg->field_mask |= BR_CFG_DELAY; + } + uid_cfg->forward_delay = this->BrTimes.ForwardDelay; + + uid_cfg->hold_time = TxHoldCount; + + RSTP_CRITICAL_PATH_END; + return 0; +} + +int +STP_IN_port_get_cfg (int vlan_id, int port_index, UID_STP_PORT_CFG_T* uid_cfg) +{ + register STPM_T* this; + register PORT_T* port; + + RSTP_CRITICAL_PATH_START; + this = stpapi_stpm_find (vlan_id); + + if (!this) { /* it had not yet been created :( */ + RSTP_CRITICAL_PATH_END; + return STP_Vlan_Had_Not_Yet_Been_Created; + } + + port = _stpapi_port_find (this, port_index); + if (! port) {/* port is absent in the stpm :( */ + RSTP_CRITICAL_PATH_END; + return STP_Port_Is_Absent_In_The_Vlan; + } + + uid_cfg->field_mask = 0; + + uid_cfg->port_priority = port->port_id >> 8; + if (uid_cfg->port_priority != DEF_PORT_PRIO) + uid_cfg->field_mask |= PT_CFG_PRIO; + + uid_cfg->admin_port_path_cost = port->adminPCost; + if (uid_cfg->admin_port_path_cost != ADMIN_PORT_PATH_COST_AUTO) + uid_cfg->field_mask |= PT_CFG_COST; + + uid_cfg->admin_point2point = port->adminPointToPointMac; + if (uid_cfg->admin_point2point != DEF_P2P) + uid_cfg->field_mask |= PT_CFG_P2P; + + uid_cfg->admin_edge = port->adminEdge; + if (uid_cfg->admin_edge != DEF_ADMIN_EDGE) + uid_cfg->field_mask |= PT_CFG_EDGE; + + uid_cfg->admin_non_stp = port->admin_non_stp; + if (uid_cfg->admin_non_stp != DEF_ADMIN_NON_STP) + uid_cfg->field_mask |= PT_CFG_NON_STP; + + if (port->mcheck) + uid_cfg->field_mask |= PT_CFG_MCHECK; + + RSTP_CRITICAL_PATH_END; + return 0; +} + +int +STP_IN_port_get_state (IN int vlan_id, INOUT UID_STP_PORT_STATE_T* entry) +{ + register STPM_T* this; + register PORT_T* port; + + RSTP_CRITICAL_PATH_START; + this = stpapi_stpm_find (vlan_id); + + if (!this) { /* it had not yet been created :( */ + RSTP_CRITICAL_PATH_END; + return STP_Vlan_Had_Not_Yet_Been_Created; + } + + port = _stpapi_port_find (this, entry->port_no); + if (! port) {/* port is absent in the stpm :( */ + RSTP_CRITICAL_PATH_END; + return STP_Port_Is_Absent_In_The_Vlan; + } + + entry->port_id = port->port_id; + if (DisabledPort == port->role) { + entry->state = UID_PORT_DISABLED; + } else if (! port->forward && ! port->learn) { + entry->state = UID_PORT_DISCARDING; + } else if (! port->forward && port->learn) { + entry->state = UID_PORT_LEARNING; + } else { + entry->state = UID_PORT_FORWARDING; + } + + entry->uptime = port->uptime; + entry->path_cost = port->operPCost; + _conv_br_id_2_uid (&port->portPrio.root_bridge, &entry->designated_root); + entry->designated_cost = port->portPrio.root_path_cost; + _conv_br_id_2_uid (&port->portPrio.design_bridge, &entry->designated_bridge); + entry->designated_port = port->portPrio.design_port; + + switch (port->role) { + case DisabledPort: entry->role = ' '; break; + case AlternatePort: entry->role = 'A'; break; + case BackupPort: entry->role = 'B'; break; + case RootPort: entry->role = 'R'; break; + case DesignatedPort: entry->role = 'D'; break; + case NonStpPort: entry->role = '-'; break; + default: entry->role = '?'; break; + } + + if (DisabledPort == port->role || NonStpPort == port->role) { + (void) memset (&entry->designated_root, 0, sizeof (UID_BRIDGE_ID_T)); + (void) memset (&entry->designated_bridge, 0, sizeof (UID_BRIDGE_ID_T)); + entry->designated_cost = 0; + entry->designated_port = port->port_id; + } + + if (DisabledPort == port->role) { + entry->oper_point2point = (P2P_FORCE_FALSE == port->adminPointToPointMac) ? 0 : 1; + entry->oper_edge = port->adminEdge; + entry->oper_stp_neigb = 0; + } else { + entry->oper_point2point = port->operPointToPointMac ? 1 : 0; + entry->oper_edge = port->operEdge ? 1 : 0; + entry->oper_stp_neigb = port->sendRSTP ? 0 : 1; + } + entry->oper_port_path_cost = port->operPCost; + + entry->rx_cfg_bpdu_cnt = port->rx_cfg_bpdu_cnt; + entry->rx_rstp_bpdu_cnt = port->rx_rstp_bpdu_cnt; + entry->rx_tcn_bpdu_cnt = port->rx_tcn_bpdu_cnt; + + entry->fdWhile = port->fdWhile; /* 17.15.1 */ + entry->helloWhen = port->helloWhen; /* 17.15.2 */ + entry->mdelayWhile = port->mdelayWhile; /* 17.15.3 */ + entry->rbWhile = port->rbWhile; /* 17.15.4 */ + entry->rcvdInfoWhile = port->rcvdInfoWhile;/* 17.15.5 */ + entry->rrWhile = port->rrWhile; /* 17.15.6 */ + entry->tcWhile = port->tcWhile; /* 17.15.7 */ + entry->txCount = port->txCount; /* 17.18.40 */ + entry->lnkWhile = port->lnkWhile; + + entry->rcvdInfoWhile = port->rcvdInfoWhile; + entry->top_change_ack = port->tcAck; + entry->tc = port->tc; + + RSTP_CRITICAL_PATH_END; + return 0; +} + +int +STP_IN_stpm_get_state (IN int vlan_id, OUT UID_STP_STATE_T* entry) +{ + register STPM_T* this; + + RSTP_CRITICAL_PATH_START; + this = stpapi_stpm_find (vlan_id); + + if (!this) { /* it had not yet been created :( */ + RSTP_CRITICAL_PATH_END; + return STP_Vlan_Had_Not_Yet_Been_Created; + } + + (void) strncpy (entry->vlan_name, this->name, NAME_LEN); + entry->vlan_id = this->vlan_id; + _conv_br_id_2_uid (&this->rootPrio.root_bridge, &entry->designated_root); + entry->root_path_cost = this->rootPrio.root_path_cost; + entry->root_port = this->rootPortId; + entry->max_age = this->rootTimes.MaxAge; + entry->forward_delay = this->rootTimes.ForwardDelay; + entry->hello_time = this->rootTimes.HelloTime; + + _conv_br_id_2_uid (&this->BrId, &entry->bridge_id); + + entry->stp_enabled = this->admin_state; + + entry->timeSince_Topo_Change = this->timeSince_Topo_Change; + entry->Topo_Change_Count = this->Topo_Change_Count; + entry->Topo_Change = this->Topo_Change; + + RSTP_CRITICAL_PATH_END; + return 0; +} + +int +STP_IN_stpm_get_name_by_vlan_id (int vlan_id, char* name, size_t buffsize) +{ + register STPM_T* stpm; + int iret = -1; + + RSTP_CRITICAL_PATH_START; + for (stpm = STP_stpm_get_the_list (); stpm; stpm = stpm->next) { + if (vlan_id == stpm->vlan_id) { + if (stpm->name) + (void) strncpy (name, stpm->name, buffsize); + else + (void) memset (name, 0, buffsize); + iret = 0; + break; + } + } + RSTP_CRITICAL_PATH_END; + return iret; +} + +int /* call it, when link Up/Down */ +STP_IN_enable_port (int port_index, Bool enable) +{ + register STPM_T* stpm; + + RSTP_CRITICAL_PATH_START; + if (! enable) { +#ifdef STP_DBG + stp_trace("%s (p%02d, all, %s, '%s')", + "clearFDB", (int) port_index, "this port", "disable port"); +#endif + STP_OUT_flush_lt (port_index, 0, LT_FLASH_ONLY_THE_PORT, "disable port"); + } + + for (stpm = STP_stpm_get_the_list (); stpm; stpm = stpm->next) { + if (STP_ENABLED != stpm->admin_state) continue; + + _stp_in_enable_port_on_stpm (stpm, port_index, enable); + /* STP_stpm_update (stpm);*/ + } + + RSTP_CRITICAL_PATH_END; + return 0; +} + +int /* call it, when port speed has been changed, speed in Kb/s */ +STP_IN_changed_port_speed (int port_index, long speed) +{ + register STPM_T* stpm; + register PORT_T* port; + + RSTP_CRITICAL_PATH_START; + for (stpm = STP_stpm_get_the_list (); stpm; stpm = stpm->next) { + if (STP_ENABLED != stpm->admin_state) continue; + + port = _stpapi_port_find (stpm, port_index); + if (! port) continue; + port->operSpeed = speed; +#ifdef STP_DBG + if (port->pcost->debug) { + stp_trace ("changed operSpeed=%lu", port->operSpeed); + } +#endif + + port->reselect = True; + port->selected = False; + } + RSTP_CRITICAL_PATH_END; + return 0; +} + +int /* call it, when port duplex mode has been changed */ +STP_IN_changed_port_duplex (int port_index) +{ + register STPM_T* stpm; + register PORT_T* port; + + RSTP_CRITICAL_PATH_START; + for (stpm = STP_stpm_get_the_list (); stpm; stpm = stpm->next) { + if (STP_ENABLED != stpm->admin_state) continue; + + port = _stpapi_port_find (stpm, port_index); + if (! port) continue; +#ifdef STP_DBG + if (port->p2p->debug) { + stp_trace ("STP_IN_changed_port_duplex(%s)", port->port_name); + } +#endif + port->p2p_recompute = True; + port->reselect = True; + port->selected = False; + } + RSTP_CRITICAL_PATH_END; + return 0; +} + +int +STP_IN_check_bpdu_header (BPDU_T* bpdu, size_t len) +{ + unsigned short len8023; + + /* LINTED: alignment */ + len8023 = ntohs (*(unsigned short*) bpdu->eth.len8023); + if (len8023 > 1500) {/* big len8023 format :( */ + return STP_Big_len8023_Format; + } + + if (len8023 < MIN_BPDU) { /* small len8023 format :( */ + return STP_Small_len8023_Format; + } + + if (len8023 + 14 > len) { /* len8023 format gt len :( */ + return STP_len8023_Format_Gt_Len; + } + + if (bpdu->eth.dsap != BPDU_L_SAP || + bpdu->eth.ssap != BPDU_L_SAP || + bpdu->eth.llc != LLC_UI) { + /* this is not a proper 802.3 pkt! :( */ + return STP_Not_Proper_802_3_Packet; + } + + if (bpdu->hdr.protocol[0] || bpdu->hdr.protocol[1]) { + return STP_Invalid_Protocol; + } + +#if 0 + if (bpdu->hdr.version != BPDU_VERSION_ID) { + return STP_Invalid_Version; + } +#endif + /* see also 9.3.4: think & TBD :( */ + return 0; +} + +#ifdef STP_DBG +int dbg_rstp_deny = 0; +#endif + + +int +STP_IN_rx_bpdu (int vlan_id, int port_index, BPDU_T* bpdu, size_t len) +{ + register PORT_T* port; + register STPM_T* this; + int iret; + +#ifdef STP_DBG + if (1 == dbg_rstp_deny) { + return 0; + } +#endif + + RSTP_CRITICAL_PATH_START; + this = stpapi_stpm_find (vlan_id); + if (! this) { /* the stpm had not yet been created :( */ + RSTP_CRITICAL_PATH_END; + return STP_Vlan_Had_Not_Yet_Been_Created; + } + + if (STP_DISABLED == this->admin_state) {/* the stpm had not yet been enabled :( */ + RSTP_CRITICAL_PATH_END; + return STP_Had_Not_Yet_Been_Enabled_On_The_Vlan; + } + + port = _stpapi_port_find (this, port_index); + if (! port) {/* port is absent in the stpm :( */ +#ifdef STP_DBG + stp_trace ("RX bpdu vlan_id=%d port=%d port is absent in the stpm :(", (int) vlan_id, (int) port_index); +#endif + RSTP_CRITICAL_PATH_END; + return STP_Port_Is_Absent_In_The_Vlan; + } + +#ifdef STP_DBG + if (port->skip_rx > 0) { + if (1 == port->skip_rx) + stp_trace ("port %s stop rx skipping", + port->port_name); + else + stp_trace ("port %s skip rx %d", + port->port_name, port->skip_rx); + port->skip_rx--; + RSTP_CRITICAL_PATH_END; + return STP_Nothing_To_Do; + } +#endif + + if (port->operEdge && ! port->lnkWhile && port->portEnabled) { +#ifdef STP_DBG + if (port->topoch->debug) { + stp_trace ("port %s tc=TRUE by operEdge", port->port_name); + } +#endif + port->tc = True; /* IEEE 802.1y, 17.30 */ + } + +/* port link change indication will come later :( */ + if (! port->portEnabled && + STP_OUT_get_port_link_status (port->port_index)) { + _stp_in_enable_port_on_stpm (this, port->port_index, True); + } + +#ifdef STP_DBG + if (port->edge->debug && port->operEdge) { + stp_trace ("port %s not operEdge !", port->port_name); + } +#endif + + port->operEdge = False; + port->wasInitBpdu = True; + + iret = STP_port_rx_bpdu (port, bpdu, len); + (void) STP_stpm_update (this); + RSTP_CRITICAL_PATH_END; + + return iret; +} + +int +STP_IN_one_second (void) +{ + register STPM_T* stpm; + register int dbg_cnt = 0; + + RSTP_CRITICAL_PATH_START; + for (stpm = STP_stpm_get_the_list (); stpm; stpm = stpm->next) { + if (STP_ENABLED == stpm->admin_state) { + /* stp_trace ("STP_IN_one_second vlan_id=%d", (int) stpm->vlan_id); */ + STP_stpm_one_second (stpm); + dbg_cnt++; + } + } + + RSTP_CRITICAL_PATH_END; + + return dbg_cnt; +} + +int +STP_IN_stpm_set_cfg (IN int vlan_id, + IN UID_STP_CFG_T* uid_cfg) +{ + int rc = 0, prev_prio, err_code; + Bool created_here, enabled_here; + register STPM_T* this; + UID_STP_CFG_T old; + + /* stp_trace ("STP_IN_stpm_set_cfg"); */ + if (0 != STP_IN_stpm_get_cfg (vlan_id, &old)) { + STP_OUT_get_init_stpm_cfg (vlan_id, &old); + } + + RSTP_CRITICAL_PATH_START; + if (BR_CFG_PRIO & uid_cfg->field_mask) { + old.bridge_priority = uid_cfg->bridge_priority; + } + + if (BR_CFG_AGE & uid_cfg->field_mask) { + old.max_age = uid_cfg->max_age; + } + + if (BR_CFG_HELLO & uid_cfg->field_mask) { + old.hello_time = uid_cfg->hello_time; + } + + if (BR_CFG_DELAY & uid_cfg->field_mask) { + old.forward_delay = uid_cfg->forward_delay; + } + + if (BR_CFG_FORCE_VER & uid_cfg->field_mask) { + old.force_version = uid_cfg->force_version; + } + + rc = _check_stpm_config (&old); + if (0 != rc) { + stp_trace ("_check_stpm_config failed %d", (int) rc); + RSTP_CRITICAL_PATH_END; + return rc; + } + + if ((BR_CFG_STATE & uid_cfg->field_mask) && + (STP_DISABLED == uid_cfg->stp_enabled)) { + rc = _stp_in_stpm_enable (vlan_id, uid_cfg->vlan_name, STP_DISABLED); + if (0 != rc) { + stp_trace ("can't disable rc=%d", (int) rc); + RSTP_CRITICAL_PATH_END; + return rc; + } + uid_cfg->field_mask &= ~BR_CFG_STATE; + if (! uid_cfg->field_mask) { + RSTP_CRITICAL_PATH_END; + return 0; + } + } + + /* get current state */ + this = stpapi_stpm_find (vlan_id); + created_here = False; + enabled_here = False; + if (! this) { /* it had not yet been created */ + this = stp_in_stpm_create (vlan_id, uid_cfg->vlan_name, &err_code); + if (! this) { + RSTP_CRITICAL_PATH_END; + return err_code; + } + } + + prev_prio = this->BrId.prio; + this->BrId.prio = old.bridge_priority; + if (STP_ENABLED == this->admin_state) { + if (0 != STP_stpm_check_bridge_priority (this)) { + this->BrId.prio = prev_prio; + stp_trace ("%s", "STP_stpm_check_bridge_priority failed"); + RSTP_CRITICAL_PATH_END; + return STP_Invalid_Bridge_Priority; + } + } + + this->BrTimes.MaxAge = old.max_age; + this->BrTimes.HelloTime = old.hello_time; + this->BrTimes.ForwardDelay = old.forward_delay; + this->ForceVersion = (PROTOCOL_VERSION_T) old.force_version; + + if ((BR_CFG_STATE & uid_cfg->field_mask) && + STP_DISABLED != uid_cfg->stp_enabled && + STP_DISABLED == this->admin_state) { + rc = _stp_in_stpm_enable (vlan_id, uid_cfg->vlan_name, uid_cfg->stp_enabled); + if (0 != rc) { + stp_trace ("%s", "cannot enable"); + if (created_here) { + STP_stpm_delete (this); + } + RSTP_CRITICAL_PATH_END; + return rc; + } + enabled_here = True; + } + + if (! enabled_here && STP_DISABLED != this->admin_state) { + STP_stpm_update_after_bridge_management (this); + } + RSTP_CRITICAL_PATH_END; + return 0; +} + +int +STP_IN_port_set_cfg (IN int vlan_id, IN int port_index, + IN UID_STP_PORT_CFG_T* uid_cfg) +{ + register STPM_T* this; + register PORT_T* port; + + RSTP_CRITICAL_PATH_START; + this = stpapi_stpm_find (vlan_id); + if (! this) { /* it had not yet been created :( */ + RSTP_CRITICAL_PATH_END; + stp_trace ("RSTP instance with tag %d hasn't been created\n", vlan_id); + return STP_Vlan_Had_Not_Yet_Been_Created; + } + + port = _stpapi_port_find (this, port_index); + if (! port) {/* port is absent in the stpm :( */ + return STP_Port_Is_Absent_In_The_Vlan; + } + + if (PT_CFG_MCHECK & uid_cfg->field_mask) { + if (this->ForceVersion >= NORMAL_RSTP) + port->mcheck = True; + } + + if (PT_CFG_COST & uid_cfg->field_mask) { + port->adminPCost = uid_cfg->admin_port_path_cost; + } + + if (PT_CFG_PRIO & uid_cfg->field_mask) { + port->port_id = (uid_cfg->port_priority << 8) + port_index; + } + + if (PT_CFG_P2P & uid_cfg->field_mask) { + port->adminPointToPointMac = uid_cfg->admin_point2point; + port->p2p_recompute = True; + } + + if (PT_CFG_EDGE & uid_cfg->field_mask) { + port->adminEdge = uid_cfg->admin_edge; + port->operEdge = port->adminEdge; +#ifdef STP_DBG + if (port->edge->debug) { + stp_trace ("port %s is operEdge=%c in STP_IN_port_set_cfg", + port->port_name, + port->operEdge ? 'Y' : 'n'); + } +#endif + } + + if (PT_CFG_NON_STP & uid_cfg->field_mask) { +#ifdef STP_DBG + if (port->roletrns->debug && port->admin_non_stp != uid_cfg->admin_non_stp) { + stp_trace ("port %s is adminNonStp=%c in STP_IN_port_set_cfg", + port->port_name, + uid_cfg->admin_non_stp ? 'Y' : 'n'); + } +#endif + port->admin_non_stp = uid_cfg->admin_non_stp; + } + +#ifdef STP_DBG + if (PT_CFG_DBG_SKIP_RX & uid_cfg->field_mask) { + port->skip_rx = uid_cfg->skip_rx; + } + + if (PT_CFG_DBG_SKIP_TX & uid_cfg->field_mask) { + port->skip_tx = uid_cfg->skip_tx; + } + +#endif + + port->reselect = True; + port->selected = False; + + (void) STP_stpm_update (this); + + RSTP_CRITICAL_PATH_END; + + return 0; +} + +#ifdef STP_DBG +int + +STP_IN_dbg_set_port_trace (char* mach_name, int enadis, + int vlan_id, int port_no) +{ + register STPM_T* this; + register PORT_T* port; + int rc; + + RSTP_CRITICAL_PATH_START; + this = stpapi_stpm_find (vlan_id); + if (! this) { /* it had not yet been created :( */ + RSTP_CRITICAL_PATH_END; + stp_trace ("RSTP instance with tag %d hasn't been created\n", vlan_id); + return STP_Vlan_Had_Not_Yet_Been_Created; + } + + port = _stpapi_port_find (this, port_no); + if (! port) {/* port is absent in the stpm :( */ + return STP_Port_Is_Absent_In_The_Vlan; + } + rc = STP_port_trace_state_machine (port, mach_name, enadis); + + RSTP_CRITICAL_PATH_END; + + return rc; +} + +#endif + +const char* +STP_IN_get_error_explanation (int rstp_err_no) +{ +#define CHOOSE(a) #a +static char* rstp_error_names[] = RSTP_ERRORS; +#undef CHOOSE + if (rstp_err_no < STP_OK) { + return "Too small error code :("; + } + if (rstp_err_no >= STP_LAST_DUMMY) { + return "Too big error code :("; + } + + return rstp_error_names[rstp_err_no]; +} + +int +STP_IN_port_add(int vlan_id, int port_index) +{ + STPM_T *this; + PORT_T *port; + int rc = STP_OK; + + RSTP_CRITICAL_PATH_START; + this = stpapi_stpm_find (vlan_id); + + if (!this) { /* it had not yet been created :( */ + RSTP_CRITICAL_PATH_END; + return STP_Vlan_Had_Not_Yet_Been_Created; + } + + port = this->ports; + + if (! STP_port_create (this, port_index)) { + /* can't add port :( */ + stp_trace ("can't create port %d", port_index); + RSTP_CRITICAL_PATH_END; + return STP_Cannot_Create_Instance_For_Port; + } + + if (!port) + rc = STP_stpm_start (this); + + RSTP_CRITICAL_PATH_END; + + return rc; +} + +int +STP_IN_port_remove(int vlan_id, int port_index) +{ + STPM_T *this; + PORT_T *port; + + RSTP_CRITICAL_PATH_START; + this = stpapi_stpm_find (vlan_id); + + if (!this) { /* it had not yet been created :( */ + RSTP_CRITICAL_PATH_END; + return STP_Vlan_Had_Not_Yet_Been_Created; + } + + port = _stpapi_port_find (this, port_index); + if (! port) {/* port is absent in the stpm :( */ + RSTP_CRITICAL_PATH_END; + return STP_Port_Is_Absent_In_The_Vlan; + } + + STP_port_delete (port); + + if (!this->ports) + STP_stpm_stop (this); + RSTP_CRITICAL_PATH_END; + + return STP_OK; +} + +void +STP_IN_get_bridge_id(int vlan_id, unsigned short *priority, unsigned char *mac) +{ + STPM_T *this; + + RSTP_CRITICAL_PATH_START; + this = stpapi_stpm_find (vlan_id); + *priority = this->BrId.prio; + (void) memcpy(mac, this->BrId.addr, 6); + RSTP_CRITICAL_PATH_END; +} + +const char * +STP_IN_state2str(RSTP_PORT_STATE state) +{ + switch (state) { + case UID_PORT_DISABLED: + return ("disabled"); + case UID_PORT_DISCARDING: + return ("discarding"); + case UID_PORT_LEARNING: + return ("learning"); + case UID_PORT_FORWARDING: + return ("forwarding"); + case UID_PORT_NON_STP: + return ("non-stp"); + case UID_PORT_BADSDU: /* synthetic state used by daemon */ + return ("bad-mtu"); + } + return ("unknown"); +} diff --git a/usr/src/lib/librstp/common/stp_in.h b/usr/src/lib/librstp/common/stp_in.h new file mode 100644 index 0000000000..cf98175092 --- /dev/null +++ b/usr/src/lib/librstp/common/stp_in.h @@ -0,0 +1,227 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + + /* This file contains prototypes for API from an operation + system to the RSTP */ + +#ifndef _STP_API_H__ +#define _STP_API_H__ + +#include <sys/types.h> + +#define STP_DBG 1 + +/************************ + * Common base constants + ************************/ + +#ifndef INOUT +# define IN /* consider as comments near 'input' parameters */ +# define OUT /* consider as comments near 'output' parameters */ +# define INOUT /* consider as comments near 'input/output' parameters */ +#endif + +#ifndef Zero +# define Zero 0 +# define One 1 +#endif + +#ifndef Bool +# define Bool int +# define False 0 +# define True 1 +#endif + +/******************************************** + * constants: default values and linitations + *********************************************/ + +/* bridge configuration */ + +#define DEF_BR_PRIO 32768 +#define MIN_BR_PRIO 0 +#define MAX_BR_PRIO 61440 + +#define DEF_BR_HELLOT 2 +#define MIN_BR_HELLOT 1 +#define MAX_BR_HELLOT 10 + +#define DEF_BR_MAXAGE 20 +#define MIN_BR_MAXAGE 6 +#define MAX_BR_MAXAGE 40 + +#define DEF_BR_FWDELAY 15 +#define MIN_BR_FWDELAY 4 +#define MAX_BR_FWDELAY 30 + +#define IEEE_TIMER_SCALE 256 + +/* Note that this works with unscaled values */ +#define CHECK_BRIDGE_CONFIG(cfg) \ + (2 * (cfg.forward_delay - 1) >= cfg.max_age && \ + cfg.max_age >= 2 * (cfg.hello_time + 1)) + +/* + * These macros provide limits and tests for displaying comprehensible errors. + */ +#define NO_MAXAGE(cfg) ((cfg.forward_delay - 1) < (cfg.hello_time + 1)) +#define MIN_FWDELAY_NOM(cfg) \ + (cfg.hello_time < MIN_BR_FWDELAY - 2 ? MIN_BR_FWDELAY : \ + cfg.hello_time + 2) +#define MAX_HELLOTIME_NOM(cfg) \ + (cfg.forward_delay > MAX_BR_HELLOT + 2 ? MAX_BR_HELLOT : \ + cfg.forward_delay - 2) + +#define SMALL_MAXAGE(cfg) (cfg.max_age < 2 * (cfg.hello_time + 1)) +#define MIN_MAXAGE(cfg) \ + (cfg.hello_time < (MIN_BR_MAXAGE / 2 - 1) ? MIN_BR_MAXAGE : \ + (2 * (cfg.hello_time + 1))) +#define MAX_HELLOTIME(cfg) \ + (cfg.max_age > 2 * (MAX_BR_HELLOT + 1) ? MAX_BR_HELLOT : \ + (cfg.max_age / 2 - 1)) + +#define MIN_FWDELAY(cfg) (cfg.max_age / 2 + 1) +#define MAX_MAXAGE(cfg) \ + (cfg.forward_delay > (MAX_BR_MAXAGE / 2 + 1) ? MAX_BR_MAXAGE : \ + (2 * (cfg.forward_delay - 1))) + +#define CAPPED_MAXAGE(cfg) (cfg.forward_delay < (MAX_BR_MAXAGE / 2 + 1)) +#define FLOORED_MAXAGE(cfg) (cfg.hello_time > (MIN_BR_MAXAGE / 2 - 1)) + +#define DEF_FORCE_VERS 2 /* NORMAL_RSTP */ + +/* port configuration */ + +#define DEF_PORT_PRIO 128 +#define MIN_PORT_PRIO 0 +#define MAX_PORT_PRIO 240 /* in steps of 16 */ + +#define DEF_ADMIN_NON_STP False +#define DEF_ADMIN_EDGE True +#define DEF_LINK_DELAY 3 /* see edge.c */ +#define DEF_P2P P2P_AUTO + +#include <uid_stp.h> +#include <stp_bpdu.h> + +#ifndef __STPM_T__ +#define __STPM_T__ +struct stpm_t; +typedef struct stpm_t STPM_T; +#endif +#ifndef __STP_VECTORS_T__ +#define __STP_VECTORS_T__ +struct stp_vectors; +typedef struct stp_vectors STP_VECTORS_T; +#endif + +/* Section 1: Create/Delete/Start/Stop the RSTP instance */ + +void /* init the engine */ +STP_IN_init (STP_VECTORS_T *vectors); + +int +STP_IN_stpm_create (int vlan_id, char* name); + +int +STP_IN_stpm_delete (int vlan_id); + +int +STP_IN_port_add (int vlan_id, int port_index); + +int +STP_IN_port_remove (int vlan_id, int port_index); + +int +STP_IN_stop_all (void); + +int +STP_IN_delete_all (void); + +/* Section 2. "Get" management */ + +Bool +STP_IN_get_is_stpm_enabled (int vlan_id); + +int +STP_IN_stpm_get_vlan_id_by_name (char* name, int* vlan_id); + +int +STP_IN_stpm_get_name_by_vlan_id (int vlan_id, char* name, size_t buffsize); + +const char* +STP_IN_get_error_explanation (int rstp_err_no); + +int +STP_IN_stpm_get_cfg (int vlan_id, UID_STP_CFG_T* uid_cfg); + +int +STP_IN_stpm_get_state (int vlan_id, UID_STP_STATE_T* entry); + +int +STP_IN_port_get_cfg (int vlan_id, int port_index, UID_STP_PORT_CFG_T* uid_cfg); + +int +STP_IN_port_get_state (int vlan_id, UID_STP_PORT_STATE_T* entry); + +const char * +STP_IN_state2str(RSTP_PORT_STATE); + +/* Section 3. "Set" management */ + +int +STP_IN_stpm_set_cfg (int vlan_id, + UID_STP_CFG_T* uid_cfg); + +int +STP_IN_port_set_cfg (int vlan_id, int port_index, + UID_STP_PORT_CFG_T* uid_cfg); + +#ifdef STP_DBG +int STP_IN_dbg_set_port_trace (char *mach_name, int enadis, + int vlan_id, int port_index); +#endif + +/* Section 4. RSTP functionality events */ + +int +STP_IN_one_second (void); + +int /* for Link UP/DOWN */ +STP_IN_enable_port (int port_index, Bool enable); + +int /* call it, when port speed has been changed, speed in Kb/s */ +STP_IN_changed_port_speed (int port_index, long speed); + +int /* call it, when current port duplex mode has been changed */ +STP_IN_changed_port_duplex (int port_index); + +int +STP_IN_check_bpdu_header (BPDU_T* bpdu, size_t len); + +int +STP_IN_rx_bpdu (int vlan_id, int port_index, BPDU_T* bpdu, size_t len); + +void +STP_IN_get_bridge_id(int vlan_id, unsigned short *priority, unsigned char *mac); + +#endif /* _STP_API_H__ */ diff --git a/usr/src/lib/librstp/common/stp_to.h b/usr/src/lib/librstp/common/stp_to.h new file mode 100644 index 0000000000..e002044039 --- /dev/null +++ b/usr/src/lib/librstp/common/stp_to.h @@ -0,0 +1,48 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* This file contains prototypes for system dependent API + from the RSTP to an operation system */ + +#ifndef _STP_OUT_H__ +#define _STP_OUT_H__ + +#include "stp_vectors.h" +#define STP_OUT_flush_lt (*stp_vectors->flush_lt) +#define STP_OUT_get_port_mac (*stp_vectors->get_port_mac) +#define STP_OUT_get_port_oper_speed (*stp_vectors->get_port_oper_speed) +#define STP_OUT_get_port_link_status (*stp_vectors->get_port_link_status) +#define STP_OUT_get_duplex (*stp_vectors->get_duplex) +#ifdef STRONGLY_SPEC_802_1W +#define STP_OUT_set_learning (*stp_vectors->set_learning) +#define STP_OUT_set_forwarding (*stp_vectors->set_forwarding) +#else +#define STP_OUT_set_port_state (*stp_vectors->set_port_state) +#endif +#define STP_OUT_set_hardware_mode (*stp_vectors->set_hardware_mode) +#define STP_OUT_tx_bpdu (*stp_vectors->tx_bpdu) +#define STP_OUT_get_port_name (*stp_vectors->get_port_name) +#define STP_OUT_get_init_stpm_cfg (*stp_vectors->get_init_stpm_cfg) +#define STP_OUT_get_init_port_cfg (*stp_vectors->get_init_port_cfg) + +#endif /* _STP_OUT_H__ */ + diff --git a/usr/src/lib/librstp/common/stp_vectors.h b/usr/src/lib/librstp/common/stp_vectors.h new file mode 100644 index 0000000000..e18e244b17 --- /dev/null +++ b/usr/src/lib/librstp/common/stp_vectors.h @@ -0,0 +1,90 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +#ifndef __STP_VECTORS_H__ +#define __STP_VECTORS_H__ + +/* This file defines callback vectors to be supplied by library user */ + +/* In the best case: clean all Learning entries with + the vlan_id and the port (if 'exclude'=0) or for all ports, + exclude the port (if ''exclude'=1). If 'port'=0, delete all + entries with the vlan_id, don't care to 'exclude' */ +typedef enum { + LT_FLASH_ALL_PORTS_EXCLUDE_THIS, + LT_FLASH_ONLY_THE_PORT +} LT_FLASH_TYPE_T; + +struct stp_vectors { + int (*flush_lt)(IN int port_index, IN int vlan_id, + IN LT_FLASH_TYPE_T type, IN char* reason); + + /* for bridge id calculation */ + void (*get_port_mac) (IN int port_index, OUT unsigned char* mac); + + unsigned long (*get_port_oper_speed) (IN unsigned int portNo); + + /* 1- Up, 0- Down */ + int (*get_port_link_status) (IN int port_index); + + /* 1- Full, 0- Half */ + int (*get_duplex) (IN int port_index); + +#ifdef STRONGLY_SPEC_802_1W + int (*set_learning) (IN int port_index, IN int vlan_id, IN int enable); + int (*set_forwarding) (IN int port_index, IN int vlan_id, + IN int enable); +#else +/* + * In many kinds of hardware the state of ports may + * be changed with another method + */ + int (*set_port_state) (IN int port_index, IN int vlan_id, + IN RSTP_PORT_STATE state); +#endif + + int (*set_hardware_mode) (int vlan_id, UID_STP_MODE_T mode); + int (*tx_bpdu) (IN int port_index, IN int vlan_id, + IN unsigned char* bpdu, + IN size_t bpdu_len); + const char *(*get_port_name) (IN int port_index); + int (*get_init_stpm_cfg) (IN int vlan_id, + INOUT UID_STP_CFG_T* cfg); + int (*get_init_port_cfg) (IN int vlan_id, + IN int port_index, + INOUT UID_STP_PORT_CFG_T* cfg); + void (*trace)(const char *fmt, ...); +}; + +#ifndef __STP_VECTORS_T__ +#define __STP_VECTORS_T__ +typedef struct stp_vectors STP_VECTORS_T; +#endif + +#ifdef __STP_INTERNAL__ +extern STP_VECTORS_T *stp_vectors; +#endif + +#endif /* __STP_VECTORS_H__ */ diff --git a/usr/src/lib/librstp/common/stpm.c b/usr/src/lib/librstp/common/stpm.c new file mode 100644 index 0000000000..af1e4aa70e --- /dev/null +++ b/usr/src/lib/librstp/common/stpm.c @@ -0,0 +1,360 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + + /* STP machine instance : bridge per VLAN: 17.17 */ + +#include "base.h" +#include "stpm.h" +#include "stp_to.h" /* for STP_OUT_flush_lt */ + +static STPM_T *bridges = NULL; + +static int +_stp_stpm_init_machine (STATE_MACH_T* this) +{ + this->State = BEGIN; + (*(this->concreteEnterState)) (this); + return 0; +} + +static int +_stp_stpm_iterate_machines (STPM_T* this, + int (*iter_callb) (STATE_MACH_T*), + Bool exit_on_non_zero_ret) +{ + register STATE_MACH_T* stater; + register PORT_T* port; + int iret, mret = 0; + + /* state machines per bridge */ + for (stater = this->machines; stater; stater = stater->next) { + iret = (*iter_callb) (stater); + if (exit_on_non_zero_ret && iret) + return iret; + else + mret += iret; + } + + /* state machines per port */ + for (port = this->ports; port; port = port->next) { + for (stater = port->machines; stater; stater = stater->next) { + iret = (*iter_callb) (stater); + if (exit_on_non_zero_ret && iret) + return iret; + else + mret += iret; + } + } + + return mret; +} + +void +_stp_stpm_init_data (STPM_T* this) +{ + STP_VECT_create (&this->rootPrio, + &this->BrId, + 0, + &this->BrId, + 0, 0); + + this->BrTimes.MessageAge = 0; + + STP_copy_times (&this->rootTimes, &this->BrTimes); +} + +static unsigned char +_check_topoch (STPM_T* this) +{ + register PORT_T* port; + + for (port = this->ports; port; port = port->next) { + if (port->tcWhile) { + return 1; + } + } + return 0; +} + +void +STP_stpm_one_second (STPM_T* param) +{ + STPM_T* this = (STPM_T*) param; + register PORT_T* port; + register int iii; + + if (STP_ENABLED != this->admin_state) return; + + for (port = this->ports; port; port = port->next) { + for (iii = 0; iii < TIMERS_NUMBER; iii++) { + if (*(port->timers[iii]) > 0) { + (*port->timers[iii])--; + } + } + port->uptime++; + } + + (void) STP_stpm_update (this); + this->Topo_Change = _check_topoch (this); + if (this->Topo_Change) { + this->Topo_Change_Count++; + this->timeSince_Topo_Change = 0; + } else { + this->Topo_Change_Count = 0; + this->timeSince_Topo_Change++; + } +} + +STPM_T* +STP_stpm_create (int vlan_id, char* name) +{ + STPM_T* this; + + STP_NEW_IN_LIST(this, STPM_T, bridges, "stp instance"); + + this->admin_state = STP_DISABLED; + + this->vlan_id = vlan_id; + if (name) { + STP_STRDUP(this->name, name, "stp bridge name"); + } + + this->machines = NULL; + this->ports = NULL; + + STP_STATE_MACH_IN_LIST(rolesel); + +#ifdef STP_DBG + /* this->rolesel->debug = 2; */ +#endif + + return this; +} + +int +STP_stpm_enable (STPM_T* this, UID_STP_MODE_T admin_state) +{ + int rc = 0; + + if (admin_state == this->admin_state) { + /* nothing to do :) */ + return 0; + } + + if (STP_ENABLED == admin_state) { + if (this->ports) + rc = STP_stpm_start (this); + this->admin_state = admin_state; + } else { + this->admin_state = admin_state; + STP_stpm_stop (this); + } + + return rc; +} + +void +STP_stpm_delete (STPM_T* this) +{ + register STPM_T* tmp; + register STPM_T* prev; + register STATE_MACH_T* stater; + register PORT_T* port; + register void* pv; + + (void) STP_stpm_enable (this, STP_DISABLED); + + for (stater = this->machines; stater; ) { + pv = (void*) stater->next; + STP_state_mach_delete (stater); + this->machines = stater = (STATE_MACH_T*) pv; + } + + for (port = this->ports; port; ) { + pv = (void*) port->next; + STP_port_delete (port); + this->ports = port = (PORT_T*) pv; + } + + prev = NULL; + for (tmp = bridges; tmp; tmp = tmp->next) { + if (tmp->vlan_id == this->vlan_id) { + if (prev) { + prev->next = this->next; + } else { + bridges = this->next; + } + + if (this->name) + STP_FREE(this->name, "stp bridge name"); + STP_FREE(this, "stp instance"); + break; + } + prev = tmp; + } +} + +int +STP_stpm_start (STPM_T* this) +{ + register PORT_T* port; + + if (! this->ports) { /* there are not any ports :( */ + return STP_There_Are_No_Ports; + } + + if (! STP_compute_bridge_id (this)) {/* can't compute bridge id ? :( */ + return STP_Cannot_Compute_Bridge_Prio; + } + + /* check, that the stpm has unique bridge Id */ + if (0 != STP_stpm_check_bridge_priority (this)) { + /* there is an enabled bridge with same ID :( */ + return STP_Invalid_Bridge_Priority; + } + + _stp_stpm_init_data (this); + + for (port = this->ports; port; port = port->next) { + STP_port_init (port, this, True); + } + +#ifndef STRONGLY_SPEC_802_1W + /* A. see comment near STRONGLY_SPEC_802_1W in topoch.c */ + /* B. port=0 here means: delete for all ports */ +#ifdef STP_DBG + stp_trace("%s (all, start stpm)", + "clearFDB"); +#endif + + STP_OUT_flush_lt (0, this->vlan_id, LT_FLASH_ONLY_THE_PORT, "start stpm"); +#endif + + (void) _stp_stpm_iterate_machines (this, _stp_stpm_init_machine, False); + (void) STP_stpm_update (this); + + return 0; +} + +/* ARGSUSED */ +void +STP_stpm_stop (STPM_T* this) +{ +} + +int +STP_stpm_update (STPM_T* this) /* returns number of loops */ +{ + register Bool need_state_change; + register int number_of_loops = 0; + + need_state_change = False; + + for (;;) {/* loop until not need changes */ + need_state_change = _stp_stpm_iterate_machines (this, + STP_check_condition, + True); + if (! need_state_change) break; + + number_of_loops++; + /* here we know, that at least one stater must be + updated (it has changed state) */ + number_of_loops += _stp_stpm_iterate_machines (this, + STP_change_state, + False); + + } + + return number_of_loops; +} + +BRIDGE_ID * +STP_compute_bridge_id (STPM_T* this) +{ + register PORT_T* port; + register PORT_T* min_num_port = NULL; + int port_index = 0; + + for (port = this->ports; port; port = port->next) { + if (! port_index || port->port_index < port_index) { + min_num_port = port; + port_index = port->port_index; + } + } + + if (! min_num_port) return NULL; /* IMHO, it may not be */ + + STP_OUT_get_port_mac (min_num_port->port_index, this->BrId.addr); + + return &this->BrId; +} + +STPM_T* +STP_stpm_get_the_list (void) +{ + return bridges; +} + +void +STP_stpm_update_after_bridge_management (STPM_T* this) +{ + register PORT_T* port; + + for (port = this->ports; port; port = port->next) { + port->reselect = True; + port->selected = False; + } +} + +int +STP_stpm_check_bridge_priority (STPM_T* this) +{ + register STPM_T* oth; + + for (oth = bridges; oth; oth = oth->next) { + if (STP_ENABLED == oth->admin_state && oth != this && + ! STP_VECT_compare_bridge_id (&this->BrId, &oth->BrId)) { + return STP_Invalid_Bridge_Priority; + } + } + + return 0; +} + +const char* +STP_stpm_get_port_name_by_id (STPM_T* this, PORT_ID port_id) +{ + register PORT_T* port; + + for (port = this->ports; port; port = port->next) { + if (port_id == port->port_id) { + return port->port_name; + } + } + + return "Undef?"; +} + + + + + diff --git a/usr/src/lib/librstp/common/stpm.h b/usr/src/lib/librstp/common/stpm.h new file mode 100644 index 0000000000..62db3928fc --- /dev/null +++ b/usr/src/lib/librstp/common/stpm.h @@ -0,0 +1,126 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* STP machine instance : bridge per VLAN: 17.17 */ +/* The Clause 17.13 points: "NOTE:The operation of the Bridge as a whole can + * be represented by the interaction between Bridge Ports specified, + * and by parameters of the Bridge stored in ‘Port 0’. This removes the + * need for any ‘per Bridge’ specification elements, and helps ensure + * the minimum dependencies between Bridge Ports. This in turn supports + * the development of implementations that scale well with increasing + * numbers of Bridge Ports. This shift of focus to ‘per Port operation’ + * for the RSTP is supported by underlying technical changes from the + * Spanning Tree Algorithm and Protocol (Clause 8):" + * Newetheless, it seems to me, the behaviour of of the bridge, its variables + * and functions are so distinct from Port, that I decided to design Bridge + * instance. I called this object 'stpm' from STP machine. I'd like to see + * another procedural model, more corresponding to this note on Clause 17.13 */ + +#ifndef _STP_MACHINE_H__ +#define _STP_MACHINE_H__ + +#include "port.h" +#include "rolesel.h" + +#define TxHoldCount 3 /* 17.16.6, 17.28.2(Table 17-5) */ + +typedef enum {/* 17.12, 17.16.1 */ + FORCE_STP_COMPAT = 0, + NORMAL_RSTP = 2 +} PROTOCOL_VERSION_T; + +struct stpm_t { + struct stpm_t* next; + + struct port_t* ports; + + /* The only "per bridge" state machine */ + STATE_MACH_T* rolesel; /* the Port Role Selection State machione: 17.22 */ + STATE_MACH_T* machines; + + /* variables */ + PROTOCOL_VERSION_T ForceVersion; /* 17.12, 17.16.1 */ + BRIDGE_ID BrId; /* 17.17.2 */ + TIMEVALUES_T BrTimes; /* 17.17.4 */ + PORT_ID rootPortId; /* 17.17.5 */ + PRIO_VECTOR_T rootPrio; /* 17.17.6 */ + TIMEVALUES_T rootTimes; /* 17.17.7 */ + + int vlan_id; /* let's say: tag */ + char* name; /* name of the VLAN, maily for debugging */ + UID_STP_MODE_T admin_state; /* STP_DISABLED or STP_ENABLED; type see in UiD */ + + unsigned long timeSince_Topo_Change; /* 14.8.1.1.3.b */ + unsigned long Topo_Change_Count; /* 14.8.1.1.3.c */ + unsigned char Topo_Change; /* 14.8.1.1.3.d */ +}; + +#ifndef __STPM_T__ +#define __STPM_T__ +typedef struct stpm_t STPM_T; +#endif + +/* Functions prototypes */ + +void +STP_stpm_one_second (STPM_T* param); + +STPM_T* +STP_stpm_create (int vlan_id, char* name); + +int +STP_stpm_enable (STPM_T* this, UID_STP_MODE_T admin_state); + +void +STP_stpm_delete (STPM_T* this); + +int +STP_stpm_start (STPM_T* this); + +void +STP_stpm_stop (STPM_T* this); + +int +STP_stpm_update (STPM_T* this); + +BRIDGE_ID * +STP_compute_bridge_id (STPM_T* this); + +STPM_T * +STP_stpm_get_the_list (void); + +void +STP_stpm_update_after_bridge_management (STPM_T* this); + +int +STP_stpm_check_bridge_priority (STPM_T* this); + +const char* +STP_stpm_get_port_name_by_id (STPM_T* this, PORT_ID port_id); + +STPM_T* stpapi_stpm_find (int vlan_id); + +int stp_in_stpm_enable (int vlan_id, char* name, + UID_STP_MODE_T admin_state); +void* stp_in_stpm_create (int vlan_id, char *name, int *err_code); + +#endif /* _STP_MACHINE_H__ */ diff --git a/usr/src/lib/librstp/common/stpmgmt.c b/usr/src/lib/librstp/common/stpmgmt.c new file mode 100644 index 0000000000..83f214be3c --- /dev/null +++ b/usr/src/lib/librstp/common/stpmgmt.c @@ -0,0 +1,161 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* This file contains API from an operation system to the RSTP library */ + +#include "base.h" +#include "stpm.h" +#include "stp_in.h" /* for bridge defaults */ +#include "stp_to.h" + + +int +STP_IN_stpm_create (int vlan_id, char* name) +{ + register STPM_T* this; + int err_code; + UID_STP_CFG_T init_cfg; + + stp_trace ("STP_IN_stpm_create(%s)", name); + + init_cfg.field_mask = BR_CFG_ALL; + STP_OUT_get_init_stpm_cfg (vlan_id, &init_cfg); + init_cfg.field_mask = 0; + + RSTP_CRITICAL_PATH_START; + this = stp_in_stpm_create (vlan_id, name, &err_code); + if (this) { + this->BrId.prio = init_cfg.bridge_priority; + this->BrTimes.MaxAge = init_cfg.max_age; + this->BrTimes.HelloTime = init_cfg.hello_time; + this->BrTimes.ForwardDelay = init_cfg.forward_delay; + this->ForceVersion = (PROTOCOL_VERSION_T) init_cfg.force_version; + } + + RSTP_CRITICAL_PATH_END; + return err_code; +} + +int +STP_IN_stpm_delete (int vlan_id) +{ + register STPM_T* this; + int iret = 0; + + RSTP_CRITICAL_PATH_START; + this = stpapi_stpm_find (vlan_id); + + if (! this) { /* it had not yet been created :( */ + iret = STP_Vlan_Had_Not_Yet_Been_Created; + } else { + + if (STP_ENABLED == this->admin_state) { + if (0 != STP_stpm_enable (this, STP_DISABLED)) {/* can't disable :( */ + iret = STP_Another_Error; + } else + STP_OUT_set_hardware_mode (vlan_id, STP_DISABLED); + } + + if (0 == iret) { + STP_stpm_delete (this); + } + } + RSTP_CRITICAL_PATH_END; + return iret; +} + +int +STP_IN_stpm_get_vlan_id_by_name (char* name, int* vlan_id) +{ + register STPM_T* stpm; + int iret = STP_Cannot_Find_Vlan; + + RSTP_CRITICAL_PATH_START; + for (stpm = STP_stpm_get_the_list (); stpm; stpm = stpm->next) { + if (stpm->name && ! strcmp (stpm->name, name)) { + *vlan_id = stpm->vlan_id; + iret = 0; + break; + } + } + RSTP_CRITICAL_PATH_END; + + return iret; +} + + +Bool +STP_IN_get_is_stpm_enabled (int vlan_id) +{ + STPM_T* this; + Bool iret = False; + + RSTP_CRITICAL_PATH_START; + this = stpapi_stpm_find (vlan_id); + + if (this) { + if (this->admin_state == STP_ENABLED) { + iret = True; + } +#ifdef notdef + } else { + ; /* it had not yet been created :( */ +#endif + } + + RSTP_CRITICAL_PATH_END; + return iret; +} + +int +STP_IN_stop_all (void) +{ + register STPM_T* stpm; + + RSTP_CRITICAL_PATH_START; + + for (stpm = STP_stpm_get_the_list (); stpm; stpm = stpm->next) { + if (STP_DISABLED != stpm->admin_state) { + STP_OUT_set_hardware_mode (stpm->vlan_id, STP_DISABLED); + (void) STP_stpm_enable (stpm, STP_DISABLED); + } + } + + RSTP_CRITICAL_PATH_END; + return 0; +} + +int +STP_IN_delete_all (void) +{ + register STPM_T* stpm; + + RSTP_CRITICAL_PATH_START; + for (stpm = STP_stpm_get_the_list (); stpm; stpm = stpm->next) { + (void) STP_stpm_enable (stpm, STP_DISABLED); + STP_stpm_delete (stpm); + } + + RSTP_CRITICAL_PATH_END; + return 0; +} + diff --git a/usr/src/lib/librstp/common/sttrans.c b/usr/src/lib/librstp/common/sttrans.c new file mode 100644 index 0000000000..e1754b2b8e --- /dev/null +++ b/usr/src/lib/librstp/common/sttrans.c @@ -0,0 +1,140 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* Port State Transition state machine : 17.24 */ + +#include "base.h" +#include "stpm.h" +#include "stp_to.h" + +#define STATES { \ + CHOOSE(DISCARDING), \ + CHOOSE(LEARNING), \ + CHOOSE(FORWARDING) \ +} + +#define GET_STATE_NAME STP_sttrans_get_state_name +#include "choose.h" + + +#ifdef STRONGLY_SPEC_802_1W +static Bool +disableLearning (STATE_MACH_T *this) +{ + register PORT_T *port = this->owner.port; + + return STP_OUT_set_learning (port->port_index, port->owner->vlan_id, False); +} + +static Bool +enableLearning (STATE_MACH_T *this) +{ + register PORT_T *port = this->owner.port; + + return STP_OUT_set_learning (port->port_index, port->owner->vlan_id, True); +} + +static Bool +disableForwarding (STATE_MACH_T *this) +{ + register PORT_T *port = this->owner.port; + + return STP_OUT_set_forwarding (port->port_index, port->owner->vlan_id, False); +} + +static Bool +enableForwarding (STATE_MACH_T *this) +{ + register PORT_T *port = this->owner.port; + + return STP_OUT_set_forwarding (port->port_index, port->owner->vlan_id, True); +} +#endif + +void +STP_sttrans_enter_state (STATE_MACH_T *this) +{ + register PORT_T *port = this->owner.port; + + switch (this->State) { + case BEGIN: + case DISCARDING: + port->learning = False; + port->forwarding = False; +#ifdef STRONGLY_SPEC_802_1W + disableLearning (this); + disableForwarding (this); +#else + STP_OUT_set_port_state (port->port_index, port->owner->vlan_id, UID_PORT_DISCARDING); +#endif + break; + case LEARNING: + port->learning = True; +#ifdef STRONGLY_SPEC_802_1W + enableLearning (this); +#else + STP_OUT_set_port_state (port->port_index, port->owner->vlan_id, UID_PORT_LEARNING); +#endif + break; + case FORWARDING: + port->tc = !port->operEdge; + port->forwarding = True; +#ifdef STRONGLY_SPEC_802_1W + enableForwarding (this); +#else + STP_OUT_set_port_state (port->port_index, port->owner->vlan_id, UID_PORT_FORWARDING); +#endif + break; + } + +} + +Bool +STP_sttrans_check_conditions (STATE_MACH_T *this) +{ + register PORT_T *port = this->owner.port; + + switch (this->State) { + case BEGIN: + return STP_hop_2_state (this, DISCARDING); + case DISCARDING: + if (port->learn) { + return STP_hop_2_state (this, LEARNING); + } + break; + case LEARNING: + if (port->forward) { + return STP_hop_2_state (this, FORWARDING); + } + if (!port->learn) { + return STP_hop_2_state (this, DISCARDING); + } + break; + case FORWARDING: + if (!port->forward) { + return STP_hop_2_state (this, DISCARDING); + } + break; + } + + return False; +} diff --git a/usr/src/lib/librstp/common/sttrans.h b/usr/src/lib/librstp/common/sttrans.h new file mode 100644 index 0000000000..da0b81ed84 --- /dev/null +++ b/usr/src/lib/librstp/common/sttrans.h @@ -0,0 +1,38 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* Port State Transition state machine : 17.24 */ + +#ifndef _STP_STATE_TRANSIT_H__ +#define _STP_STATE_TRANSIT_H__ + +void +STP_sttrans_enter_state (STATE_MACH_T* s); + +Bool +STP_sttrans_check_conditions (STATE_MACH_T* s); + +char* +STP_sttrans_get_state_name (int state); + +#endif /* _STP_STATE_TRANSIT_H__ */ + diff --git a/usr/src/lib/librstp/common/times.c b/usr/src/lib/librstp/common/times.c new file mode 100644 index 0000000000..b9eae3ceee --- /dev/null +++ b/usr/src/lib/librstp/common/times.c @@ -0,0 +1,80 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* "Times" API : bridgeTime, rootTimes, portTimes, designatedTimes, msgTimes */ + +#include "base.h" + +int +STP_compare_times (IN TIMEVALUES_T *t1, IN TIMEVALUES_T *t2) +{ + if (t1->MessageAge < t2->MessageAge) return -1; + if (t1->MessageAge > t2->MessageAge) return 1; + + if (t1->MaxAge < t2->MaxAge) return -2; + if (t1->MaxAge > t2->MaxAge) return 2; + + if (t1->ForwardDelay < t2->ForwardDelay) return -3; + if (t1->ForwardDelay > t2->ForwardDelay) return 3; + + if (t1->HelloTime < t2->HelloTime) return -4; + if (t1->HelloTime > t2->HelloTime) return 4; + + return 0; +} + +void +STP_get_times (IN BPDU_BODY_T *b, OUT TIMEVALUES_T *v) +{ + /* LINTED: alignment */ + v->MessageAge = ntohs (*((unsigned short*) b->message_age)) >> 8; + /* LINTED: alignment */ + v->MaxAge = ntohs (*((unsigned short*) b->max_age)) >> 8; + /* LINTED: alignment */ + v->ForwardDelay = ntohs (*((unsigned short*) b->forward_delay)) >> 8; + /* LINTED: alignment */ + v->HelloTime = ntohs (*((unsigned short*) b->hello_time)) >> 8; +} + +void +STP_set_times (IN TIMEVALUES_T *v, OUT BPDU_BODY_T *b) +{ + unsigned short mt; + #define STP_SET_TIME(f, t) \ + mt = htons (f << 8); \ + (void) memcpy (t, &mt, 2); + + STP_SET_TIME(v->MessageAge, b->message_age); + STP_SET_TIME(v->MaxAge, b->max_age); + STP_SET_TIME(v->ForwardDelay, b->forward_delay); + STP_SET_TIME(v->HelloTime, b->hello_time); +} + +void +STP_copy_times (OUT TIMEVALUES_T *t, IN TIMEVALUES_T *f) +{ + t->MessageAge = f->MessageAge; + t->MaxAge = f->MaxAge; + t->ForwardDelay = f->ForwardDelay; + t->HelloTime = f->HelloTime; +} + diff --git a/usr/src/lib/librstp/common/times.h b/usr/src/lib/librstp/common/times.h new file mode 100644 index 0000000000..4bdc1e28df --- /dev/null +++ b/usr/src/lib/librstp/common/times.h @@ -0,0 +1,50 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* "Times" API : bridgeTime, rootTimes, portTimes, designatedTimes, msgTimes */ + +#ifndef _RSTP_TIMES_H__ +#define _RSTP_TIMES_H__ + +typedef struct timevalues_t { + unsigned short MessageAge; + unsigned short MaxAge; + unsigned short ForwardDelay; + unsigned short HelloTime; +} TIMEVALUES_T; + +int +STP_compare_times (IN TIMEVALUES_T* t1, IN TIMEVALUES_T* t2); + +void +STP_get_times (IN BPDU_BODY_T* b, OUT TIMEVALUES_T* v); + +void +STP_set_times (IN TIMEVALUES_T* v, OUT BPDU_BODY_T* b); + +void +STP_copy_times (OUT TIMEVALUES_T* t, IN TIMEVALUES_T* f); + +#endif /* _RSTP_TIMES_H__ */ + + + diff --git a/usr/src/lib/librstp/common/topoch.c b/usr/src/lib/librstp/common/topoch.c new file mode 100644 index 0000000000..1926bcfe44 --- /dev/null +++ b/usr/src/lib/librstp/common/topoch.c @@ -0,0 +1,227 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* Topolgy Change state machine : 17.25 */ + +#include "base.h" +#include "stpm.h" +#include "stp_to.h" /* for STP_OUT_flush_lt */ + +#define STATES { \ + CHOOSE(INIT), \ + CHOOSE(INACTIVE), \ + CHOOSE(TCACTIVE), \ + CHOOSE(DETECTED), \ + CHOOSE(NOTIFIED_TC), \ + CHOOSE(PROPAGATING), \ + CHOOSE(ACKNOWLEDGED), \ + CHOOSE(NOTIFIED_TCN) \ +} + +#define GET_STATE_NAME STP_topoch_get_state_name +#include "choose.h" + +#ifndef STRONGLY_SPEC_802_1W +/* + * In many kinds of hardware the function + * STP_OUT_flush_lt is a) is very hard and b) cannot + * delete learning emtries per port. The alternate + * method may be used: we don't care operEdge flag here, + * but clean learning table once for TopologyChange + * for all ports, except the received port. I am ready to discuss :( + * See below word STRONGLY_SPEC_802_1W + */ +#else +static Bool +flush (STATE_MACH_T *this, char* reason) /* 17.19.9 */ +{ + register PORT_T* port = this->owner.port; + Bool bret; + + if (port->operEdge) return True; + if (this->debug) { + stp_trace("%s (%s, %s, %s, '%s')", + "flush", port->port_name, port->owner->name, + LT_FLASH_ONLY_THE_PORT == type ? "this port" : "other ports", + reason); + } + + bret = STP_OUT_flush_lt (port->port_index, port->owner->vlan_id, + LT_FLASH_ONLY_THE_PORT, reason); +} +#endif + +static void +setTcPropBridge (STATE_MACH_T* this, char* reason) /* 17.19.14 */ +{ + register PORT_T* port = this->owner.port; + register PORT_T* tmp; + + for (tmp = port->owner->ports; tmp; tmp = tmp->next) { + if (tmp->port_index != port->port_index) + tmp->tcProp = True; + } + +#ifndef STRONGLY_SPEC_802_1W +#ifdef STP_DBG + if (this->debug) { + stp_trace("%s (%s, %s, %s, '%s')", + "clearFDB", port->port_name, port->owner->name, + "other ports", reason); + } +#endif + + STP_OUT_flush_lt (port->port_index, port->owner->vlan_id, + LT_FLASH_ALL_PORTS_EXCLUDE_THIS, reason); +#endif +} + +static unsigned int +newTcWhile (STATE_MACH_T* this) /* 17.19.7 */ +{ + register PORT_T* port = this->owner.port; + + if (port->sendRSTP && port->operPointToPointMac) { + return 2 * port->owner->rootTimes.HelloTime; + } + return port->owner->rootTimes.MaxAge; +} + +void +STP_topoch_enter_state (STATE_MACH_T* this) +{ + register PORT_T* port = this->owner.port; + + switch (this->State) { + case BEGIN: + case INIT: +#ifdef STRONGLY_SPEC_802_1W + flush (this, "topoch INIT"); +#endif + port->tcWhile = 0; + port->tc = + port->tcProp = + port->tcAck = False; + break; + case INACTIVE: + port->rcvdTc = + port->rcvdTcn = + port->rcvdTcAck = port->tc = port->tcProp = False; + break; + case TCACTIVE: + break; + case DETECTED: + port->tcWhile = newTcWhile (this); +#ifdef STP_DBG + if (this->debug) + stp_trace("DETECTED: tcWhile=%d on port %s", + port->tcWhile, port->port_name); +#endif + setTcPropBridge (this, "DETECTED"); + port->tc = False; + break; + case NOTIFIED_TC: + port->rcvdTcn = port->rcvdTc = False; + if (port->role == DesignatedPort) { + port->tcAck = True; + } + setTcPropBridge (this, "NOTIFIED_TC"); + break; + case PROPAGATING: + port->tcWhile = newTcWhile (this); +#ifdef STP_DBG + if (this->debug) + stp_trace("PROPAGATING: tcWhile=%d on port %s", + port->tcWhile, port->port_name); +#endif +#ifdef STRONGLY_SPEC_802_1W + flush (this, "topoch PROPAGATING"); +#endif + port->tcProp = False; + break; + case ACKNOWLEDGED: + port->tcWhile = 0; +#ifdef STP_DBG + if (this->debug) + stp_trace("ACKNOWLEDGED: tcWhile=%d on port %s", + port->tcWhile, port->port_name); +#endif + port->rcvdTcAck = False; + break; + case NOTIFIED_TCN: + port->tcWhile = newTcWhile (this); +#ifdef STP_DBG + if (this->debug) + stp_trace("NOTIFIED_TCN: tcWhile=%d on port %s", + port->tcWhile, port->port_name); +#endif + break; + }; +} + +Bool +STP_topoch_check_conditions (STATE_MACH_T* this) +{ + register PORT_T* port = this->owner.port; + + if (BEGIN == this->State) { + return STP_hop_2_state (this, INIT); + } + + switch (this->State) { + case INIT: + return STP_hop_2_state (this, INACTIVE); + case INACTIVE: + if (port->role == RootPort || port->role == DesignatedPort) + return STP_hop_2_state (this, TCACTIVE); + if (port->rcvdTc || port->rcvdTcn || port->rcvdTcAck || + port->tc || port->tcProp) + return STP_hop_2_state (this, INACTIVE); + break; + case TCACTIVE: + if (port->role != RootPort && (port->role != DesignatedPort)) + return STP_hop_2_state (this, INIT); + if (port->tc) + return STP_hop_2_state (this, DETECTED); + if (port->rcvdTcn) + return STP_hop_2_state (this, NOTIFIED_TCN); + if (port->rcvdTc) + return STP_hop_2_state (this, NOTIFIED_TC); + if (port->tcProp && !port->operEdge) + return STP_hop_2_state (this, PROPAGATING); + if (port->rcvdTcAck) + return STP_hop_2_state (this, ACKNOWLEDGED); + break; + case DETECTED: + return STP_hop_2_state (this, TCACTIVE); + case NOTIFIED_TC: + return STP_hop_2_state (this, TCACTIVE); + case PROPAGATING: + return STP_hop_2_state (this, TCACTIVE); + case ACKNOWLEDGED: + return STP_hop_2_state (this, TCACTIVE); + case NOTIFIED_TCN: + return STP_hop_2_state (this, NOTIFIED_TC); + }; + return False; +} + diff --git a/usr/src/lib/librstp/common/topoch.h b/usr/src/lib/librstp/common/topoch.h new file mode 100644 index 0000000000..8b7324c425 --- /dev/null +++ b/usr/src/lib/librstp/common/topoch.h @@ -0,0 +1,37 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* Topolgy Change state machine : 17.25 */ + +#ifndef _STP_TOPO_CHANGE_H__ +#define _STP_TOPO_CHANGE_H__ + +void +STP_topoch_enter_state (STATE_MACH_T* s); + +Bool +STP_topoch_check_conditions (STATE_MACH_T* s); + +char* STP_topoch_get_state_name (int state); + +#endif /* _STP_TOPO_CHANGE_H__ */ + diff --git a/usr/src/lib/librstp/common/transmit.c b/usr/src/lib/librstp/common/transmit.c new file mode 100644 index 0000000000..cead2143e8 --- /dev/null +++ b/usr/src/lib/librstp/common/transmit.c @@ -0,0 +1,368 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* Port Transmit state machine : 17.27 */ + +#include "base.h" +#include "stpm.h" +#include "stp_to.h" /* for STP_OUT_get_port_mac & STP_OUT_tx_bpdu */ + +#define BPDU_LEN8023_OFF 12 + +#define STATES { \ + CHOOSE(TRANSMIT_INIT), \ + CHOOSE(TRANSMIT_PERIODIC), \ + CHOOSE(IDLE), \ + CHOOSE(TRANSMIT_CONFIG), \ + CHOOSE(TRANSMIT_TCN), \ + CHOOSE(TRANSMIT_RSTP) \ +} + +#define GET_STATE_NAME STP_transmit_get_state_name +#include "choose.h" + +#define MIN_FRAME_LENGTH 64 + + +typedef struct tx_tcn_bpdu_t { + MAC_HEADER_T mac; + ETH_HEADER_T eth; + BPDU_HEADER_T hdr; +} TCN_BPDU_T; + +typedef struct tx_stp_bpdu_t { + MAC_HEADER_T mac; + ETH_HEADER_T eth; + BPDU_HEADER_T hdr; + BPDU_BODY_T body; +} CONFIG_BPDU_T; + +typedef struct tx_rstp_bpdu_t { + MAC_HEADER_T mac; + ETH_HEADER_T eth; + BPDU_HEADER_T hdr; + BPDU_BODY_T body; + unsigned char ver_1_length[2]; +} RSTP_BPDU_T; + + +static RSTP_BPDU_T bpdu_packet = { + {/* MAC_HEADER_T */ + {0x01, 0x80, 0xc2, 0x00, 0x00, 0x00}, /* dst_mac */ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00} /* src_mac */ + }, + { /* ETH_HEADER_T */ + {0x00, 0x00}, /* len8023 */ + BPDU_L_SAP, BPDU_L_SAP, LLC_UI /* dsap, ssap, llc */ + }, + {/* BPDU_HEADER_T */ + {0x00, 0x00}, /* protocol */ + BPDU_VERSION_ID, 0x00 /* version, bpdu_type */ + }, + { + 0x00, /* flags; */ + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, /* root_id[8]; */ + {0x00,0x00,0x00,0x00}, /* root_path_cost[4]; */ + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, /* bridge_id[8]; */ + {0x00,0x00}, /* port_id[2]; */ + {0x00,0x00}, /* message_age[2]; */ + {0x00,0x00}, /* max_age[2]; */ + {0x00,0x00}, /* hello_time[2]; */ + {0x00,0x00}, /* forward_delay[2]; */ + }, + {0x00,0x00}, /* ver_1_length[2]; */ +}; + +static size_t +build_bpdu_header (int port_index, + unsigned char bpdu_type, + unsigned short pkt_len) +{ + unsigned short len8023; + + STP_OUT_get_port_mac (port_index, bpdu_packet.mac.src_mac); + + bpdu_packet.hdr.bpdu_type = bpdu_type; + bpdu_packet.hdr.version = (BPDU_RSTP == bpdu_type) ? + BPDU_VERSION_RAPID_ID : + BPDU_VERSION_ID; + + /* NOTE: I suppose, that sizeof(unsigned short)=2 ! */ + len8023 = htons ((unsigned short) (pkt_len + 3)); + (void) memcpy (&bpdu_packet.eth.len8023, &len8023, 2); + + if (pkt_len < MIN_FRAME_LENGTH) pkt_len = MIN_FRAME_LENGTH; + return pkt_len; +} + +static int +txTcn (STATE_MACH_T* this) +{ /* 17.19.17 (page 68) & 9.3.2 (page 25) */ + register size_t pkt_len; + register int port_index, vlan_id; + +#ifdef STP_DBG + if (this->owner.port->skip_tx > 0) { + if (1 == this->owner.port->skip_tx) + stp_trace ("port %s stop tx skipping", + this->owner.port->port_name); + this->owner.port->skip_tx--; + return STP_Nothing_To_Do; + } +#endif + + if (this->owner.port->admin_non_stp) return 1; + port_index = this->owner.port->port_index; + vlan_id = this->owner.port->owner->vlan_id; + + pkt_len = build_bpdu_header (port_index, + BPDU_TOPO_CHANGE_TYPE, + sizeof (BPDU_HEADER_T)); + +#ifdef STP_DBG + if (this->debug) + stp_trace ("port %s txTcn", this->owner.port->port_name); +#endif + return STP_OUT_tx_bpdu (port_index, vlan_id, + (unsigned char *) &bpdu_packet, + pkt_len); +} + +static void +build_config_bpdu (PORT_T* port, Bool set_topo_ack_flag) +{ + bpdu_packet.body.flags = 0; + if (port->tcWhile) { +#ifdef STP_DBG + if (port->topoch->debug) + stp_trace ("tcWhile=%d =>tx TOPOLOGY_CHANGE_BIT to port %s", + (int) port->tcWhile, port->port_name); +#endif + bpdu_packet.body.flags |= TOPOLOGY_CHANGE_BIT; + } + + if (set_topo_ack_flag && port->tcAck) { + bpdu_packet.body.flags |= TOPOLOGY_CHANGE_ACK_BIT; + } + + STP_VECT_set_vector (&port->portPrio, &bpdu_packet.body); + STP_set_times (&port->portTimes, &bpdu_packet.body); +} + +static int +txConfig (STATE_MACH_T* this) +{/* 17.19.15 (page 67) & 9.3.1 (page 23) */ + register size_t pkt_len; + register PORT_T* port = NULL; + register int port_index, vlan_id; + +#ifdef STP_DBG + if (this->owner.port->skip_tx > 0) { + if (1 == this->owner.port->skip_tx) + stp_trace ("port %s stop tx skipping", + this->owner.port->port_name); + this->owner.port->skip_tx--; + return STP_Nothing_To_Do; + } +#endif + + port = this->owner.port; + if (port->admin_non_stp) return 1; + port_index = port->port_index; + vlan_id = port->owner->vlan_id; + + pkt_len = build_bpdu_header (port->port_index, + BPDU_CONFIG_TYPE, + sizeof (BPDU_HEADER_T) + sizeof (BPDU_BODY_T)); + build_config_bpdu (port, True); + +#ifdef STP_DBG + if (this->debug) + stp_trace ("port %s txConfig flags=0X%lx", + port->port_name, + (unsigned long) bpdu_packet.body.flags); +#endif + return STP_OUT_tx_bpdu (port_index, vlan_id, + (unsigned char *) &bpdu_packet, + pkt_len); +} + +static int +txRstp (STATE_MACH_T* this) +{/* 17.19.16 (page 68) & 9.3.3 (page 25) */ + register size_t pkt_len; + register PORT_T* port = NULL; + register int port_index, vlan_id; + unsigned char role; + +#ifdef STP_DBG + if (this->owner.port->skip_tx > 0) { + if (1 == this->owner.port->skip_tx) + stp_trace ("port %s stop tx skipping", + this->owner.port->port_name); + else + stp_trace ("port %s skip tx %d", + this->owner.port->port_name, this->owner.port->skip_tx); + + this->owner.port->skip_tx--; + return STP_Nothing_To_Do; + } +#endif + + port = this->owner.port; + if (port->admin_non_stp) return 1; + port_index = port->port_index; + vlan_id = port->owner->vlan_id; + + pkt_len = build_bpdu_header (port->port_index, + BPDU_RSTP, + sizeof (BPDU_HEADER_T) + sizeof (BPDU_BODY_T) + 2); + build_config_bpdu (port, False); + + switch (port->selectedRole) { + default: + case DisabledPort: + role = RSTP_PORT_ROLE_UNKN; + break; + case AlternatePort: + role = RSTP_PORT_ROLE_ALTBACK; + break; + case BackupPort: + role = RSTP_PORT_ROLE_ALTBACK; + break; + case RootPort: + role = RSTP_PORT_ROLE_ROOT; + break; + case DesignatedPort: + role = RSTP_PORT_ROLE_DESGN; + break; + } + + bpdu_packet.body.flags |= (role << PORT_ROLE_OFFS); + + if (port->synced) { +#if 0 /* def STP_DBG */ + if (port->roletrns->debug) + stp_trace ("tx AGREEMENT_BIT to port %s", port->port_name); +#endif + bpdu_packet.body.flags |= AGREEMENT_BIT; + } + + if (port->proposing) { +#if 0 /* def STP_DBG */ + if (port->roletrns->debug) + stp_trace ("tx PROPOSAL_BIT to port %s", port->port_name); +#endif + bpdu_packet.body.flags |= PROPOSAL_BIT; + } + +#ifdef STP_DBG + if (this->debug) + stp_trace ("port %s txRstp flags=0X%lx", + port->port_name, + (unsigned long) bpdu_packet.body.flags); +#endif + + return STP_OUT_tx_bpdu (port_index, vlan_id, + (unsigned char *) &bpdu_packet, + pkt_len); +} + +void +STP_transmit_enter_state (STATE_MACH_T* this) +{ + register PORT_T* port = this->owner.port; + + switch (this->State) { + case BEGIN: + case TRANSMIT_INIT: + port->newInfo = False; + port->helloWhen = 0; + port->txCount = 0; + break; + case TRANSMIT_PERIODIC: + port->newInfo = port->newInfo || + ((port->role == DesignatedPort) || + ((port->role == RootPort) && port->tcWhile)); + port->helloWhen = port->owner->rootTimes.HelloTime; + break; + case IDLE: + break; + case TRANSMIT_CONFIG: + port->newInfo = False; + (void) txConfig (this); + port->txCount++; + port->tcAck = False; + break; + case TRANSMIT_TCN: + port->newInfo = False; + (void) txTcn (this); + port->txCount++; + break; + case TRANSMIT_RSTP: + port->newInfo = False; + (void) txRstp (this); + port->txCount++; + port->tcAck = False; + break; + }; +} + +Bool +STP_transmit_check_conditions (STATE_MACH_T* this) +{ + register PORT_T* port = this->owner.port; + + switch (this->State) { + case BEGIN: + return STP_hop_2_state (this, TRANSMIT_INIT); + case TRANSMIT_INIT: + return STP_hop_2_state (this, IDLE); + case TRANSMIT_PERIODIC: + return STP_hop_2_state (this, IDLE); + case IDLE: + if (!port->helloWhen) return STP_hop_2_state (this, TRANSMIT_PERIODIC); + if (!port->sendRSTP && port->newInfo && + (port->txCount < TxHoldCount) && + (port->role == DesignatedPort) && + port->helloWhen) + return STP_hop_2_state (this, TRANSMIT_CONFIG); + if (!port->sendRSTP && port->newInfo && + (port->txCount < TxHoldCount) && + (port->role == RootPort) && + port->helloWhen) + return STP_hop_2_state (this, TRANSMIT_TCN); + if (port->sendRSTP && port->newInfo && + (port->txCount < TxHoldCount) && + ((port->role == RootPort) || + (port->role == DesignatedPort))) + return STP_hop_2_state (this, TRANSMIT_RSTP); + break; + case TRANSMIT_CONFIG: + return STP_hop_2_state (this, IDLE); + case TRANSMIT_TCN: + return STP_hop_2_state (this, IDLE); + case TRANSMIT_RSTP: + return STP_hop_2_state (this, IDLE); + }; + return False; +} diff --git a/usr/src/lib/librstp/common/transmit.h b/usr/src/lib/librstp/common/transmit.h new file mode 100644 index 0000000000..620013ce5a --- /dev/null +++ b/usr/src/lib/librstp/common/transmit.h @@ -0,0 +1,38 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* Port Transmit state machine : 17.27 */ + +#ifndef _STP_TRANSMIT_H__ +#define _STP_TRANSMIT_H__ + +void +STP_transmit_enter_state (STATE_MACH_T* s); + +Bool +STP_transmit_check_conditions (STATE_MACH_T* s); + +char* +STP_transmit_get_state_name (int state); + +#endif /* _STP_TRANSMIT_H__ */ + diff --git a/usr/src/lib/librstp/common/uid_stp.h b/usr/src/lib/librstp/common/uid_stp.h new file mode 100644 index 0000000000..a960081f59 --- /dev/null +++ b/usr/src/lib/librstp/common/uid_stp.h @@ -0,0 +1,202 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* External management communication API definitions */ + +#ifndef _UID_STP_H__ +#define _UID_STP_H__ + +#define STP_DBG 1 + +#define NAME_LEN 20 + +typedef enum { + STP_DISABLED, + STP_ENABLED +} UID_STP_MODE_T; + +typedef struct { + unsigned short prio; + unsigned char addr[6]; +} UID_BRIDGE_ID_T; + +typedef struct { + char vlan_name[NAME_LEN]; /* name of the VLAN, key of the bridge */ + char action; /* 1-create, 0- delete */ +} UID_STP_BR_CTRL_T; + +#define BR_CFG_STATE (1L << 0) +#define BR_CFG_PRIO (1L << 1) +#define BR_CFG_AGE (1L << 2) +#define BR_CFG_HELLO (1L << 3) +#define BR_CFG_DELAY (1L << 4) +#define BR_CFG_FORCE_VER (1L << 5) +#define BR_CFG_AGE_MODE (1L << 6) +#define BR_CFG_AGE_TIME (1L << 7) +#define BR_CFG_HOLD_TIME (1L << 8) +#define BR_CFG_ALL BR_CFG_STATE | \ + BR_CFG_PRIO | \ + BR_CFG_AGE | \ + BR_CFG_HELLO | \ + BR_CFG_DELAY | \ + BR_CFG_FORCE_VER | \ + BR_CFG_AGE_MODE | \ + BR_CFG_AGE_TIME | \ + BR_CFG_HOLD_TIME + +typedef struct { + /* service data */ + unsigned long field_mask; /* which fields to change */ + UID_STP_MODE_T stp_enabled; + char vlan_name[NAME_LEN]; /* name of the VLAN, key of the bridge */ + + /* protocol data */ + int bridge_priority; + int max_age; + int hello_time; + int forward_delay; + int force_version; + int hold_time; +} UID_STP_CFG_T; + +typedef struct { + /* service data */ + char vlan_name[NAME_LEN]; /* name of the VLAN, key of the bridge */ + unsigned long vlan_id; + UID_STP_MODE_T stp_enabled; + + /* protocol data */ + UID_BRIDGE_ID_T designated_root; + unsigned long root_path_cost; + + unsigned long timeSince_Topo_Change; /* 14.8.1.1.3.b: TBD */ + unsigned long Topo_Change_Count; /* 14.8.1.1.3.c: TBD */ + unsigned char Topo_Change; /* 14.8.1.1.3.d: TBD */ + + unsigned short root_port; + int max_age; + int hello_time; + int forward_delay; + UID_BRIDGE_ID_T bridge_id; +} UID_STP_STATE_T; + +typedef enum { + UID_PORT_DISABLED = 0, + UID_PORT_DISCARDING, + UID_PORT_LEARNING, + UID_PORT_FORWARDING, + UID_PORT_NON_STP, + UID_PORT_BADSDU +} RSTP_PORT_STATE; + +typedef unsigned short UID_PORT_ID; + +typedef enum { + P2P_FORCE_TRUE, + P2P_FORCE_FALSE, + P2P_AUTO +} ADMIN_P2P_T; + +#define PT_CFG_STATE (1L << 0) +#define PT_CFG_COST (1L << 1) +#define PT_CFG_PRIO (1L << 2) +#define PT_CFG_P2P (1L << 3) +#define PT_CFG_EDGE (1L << 4) +#define PT_CFG_MCHECK (1L << 5) +#define PT_CFG_NON_STP (1L << 6) +#ifdef STP_DBG +#define PT_CFG_DBG_SKIP_RX (1L << 16) +#define PT_CFG_DBG_SKIP_TX (1L << 17) +#endif + +#define PT_CFG_ALL PT_CFG_STATE | \ + PT_CFG_COST | \ + PT_CFG_PRIO | \ + PT_CFG_P2P | \ + PT_CFG_EDGE | \ + PT_CFG_MCHECK | \ + PT_CFG_NON_STP + +#define ADMIN_PORT_PATH_COST_AUTO 0 + +typedef struct { + /* service data */ + unsigned long field_mask; /* which fields to change */ + char vlan_name[NAME_LEN]; /* name of the VLAN, key of the bridge */ + + /* protocol data */ + int port_priority; + unsigned long admin_port_path_cost; /* ADMIN_PORT_PATH_COST_AUTO - auto sence */ + ADMIN_P2P_T admin_point2point; + unsigned char admin_edge; + unsigned char admin_non_stp; /* 1- doesn't participate in STP, 1 - regular */ +#ifdef STP_DBG + unsigned int skip_rx; + unsigned int skip_tx; +#endif + +} UID_STP_PORT_CFG_T; + +typedef struct { + /* service data */ + char vlan_name[NAME_LEN]; /* name of the VLAN, key of the bridge */ + unsigned int port_no; /* key of the entry */ + + /* protocol data */ + UID_PORT_ID port_id; + RSTP_PORT_STATE state; + unsigned long path_cost; + + UID_BRIDGE_ID_T designated_root; + unsigned long designated_cost; + UID_BRIDGE_ID_T designated_bridge; + UID_PORT_ID designated_port; + +#if 0 + int infoIs; + unsigned short handshake_flags; +#endif + + unsigned long rx_cfg_bpdu_cnt; + unsigned long rx_rstp_bpdu_cnt; + unsigned long rx_tcn_bpdu_cnt; + int fdWhile; /* 17.15.1 */ + int helloWhen; /* 17.15.2 */ + int mdelayWhile; /* 17.15.3 */ + int rbWhile; /* 17.15.4 */ + int rcvdInfoWhile;/* 17.15.5 */ + int rrWhile; /* 17.15.6 */ + int tcWhile; /* 17.15.7 */ + int txCount; /* 17.18.40 */ + int lnkWhile; + + unsigned long uptime; /* 14.8.2.1.3.a */ + unsigned long oper_port_path_cost; + unsigned char role; + unsigned char oper_point2point; + unsigned char oper_edge; + unsigned char oper_stp_neigb; + unsigned char top_change_ack; + unsigned char tc; +} UID_STP_PORT_STATE_T; + +#endif diff --git a/usr/src/lib/librstp/common/vector.c b/usr/src/lib/librstp/common/vector.c new file mode 100644 index 0000000000..a268b92319 --- /dev/null +++ b/usr/src/lib/librstp/common/vector.c @@ -0,0 +1,183 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* STP priority vectors API : 17.4.2 */ + +#include "base.h" +#include "stp_bpdu.h" +#include "vector.h" +#include "stp_vectors.h" + +int +STP_VECT_compare_bridge_id (BRIDGE_ID* b1, BRIDGE_ID* b2) +{ + if (b1->prio < b2->prio) + return -1; + + if (b1->prio > b2->prio) + return 1; + return memcmp (b1->addr, b2->addr, 6); +} + +void +STP_VECT_copy (OUT PRIO_VECTOR_T* t, IN PRIO_VECTOR_T* f) +{ + (void) memcpy (t, f, sizeof (PRIO_VECTOR_T)); +} + +void +STP_VECT_create (OUT PRIO_VECTOR_T* t, + IN BRIDGE_ID* root_br, + IN unsigned long root_path_cost, + IN BRIDGE_ID* design_bridge, + IN PORT_ID design_port, + IN PORT_ID bridge_port) +{ + (void) memcpy (&t->root_bridge, root_br, sizeof (BRIDGE_ID)); + t->root_path_cost = root_path_cost; + (void) memcpy (&t->design_bridge, design_bridge, sizeof (BRIDGE_ID)); + t->design_port = design_port; + t->bridge_port = bridge_port; +} + +int +STP_VECT_compare_vector (PRIO_VECTOR_T* v1, PRIO_VECTOR_T* v2) +{ + int bridcmp; + + bridcmp = STP_VECT_compare_bridge_id (&v1->root_bridge, &v2->root_bridge); + if (bridcmp < 0) return bridcmp; + + if (! bridcmp) { + bridcmp = v1->root_path_cost - v2->root_path_cost; + if (bridcmp < 0) return bridcmp; + if (! bridcmp) { + bridcmp = STP_VECT_compare_bridge_id (&v1->design_bridge, &v2->design_bridge); + if (bridcmp < 0) return bridcmp; + if (! bridcmp) { + bridcmp = v1->design_port - v2->design_port; + if (bridcmp < 0) return bridcmp; + if (! bridcmp) + return v1->bridge_port - v2->bridge_port; + } + } + } + + return bridcmp; +} + +static unsigned short +stp_vect_get_short (IN unsigned char* f) +{ + /* LINTED: alignment */ + return ntohs (*(unsigned short *)f); +} + +static void +stp_vect_set_short (IN unsigned short f, OUT unsigned char* t) +{ + /* LINTED: alignment */ + *(unsigned short *)t = htons (f); +} + +static void +stp_vect_get_bridge_id (IN unsigned char* c_br, OUT BRIDGE_ID* bridge_id) +{ + bridge_id->prio = stp_vect_get_short (c_br); + (void) memcpy (bridge_id->addr, c_br + 2, 6); +} + +static void +stp_vect_set_bridge_id (IN BRIDGE_ID* bridge_id, OUT unsigned char* c_br) +{ + stp_vect_set_short (bridge_id->prio, c_br); + (void) memcpy (c_br + 2, bridge_id->addr, 6); +} + +void +STP_VECT_get_vector (IN BPDU_BODY_T* b, OUT PRIO_VECTOR_T* v) +{ + stp_vect_get_bridge_id (b->root_id, &v->root_bridge); + + /* LINTED: alignment */ + v->root_path_cost = ntohl (*((long*) b->root_path_cost)); + + stp_vect_get_bridge_id (b->bridge_id, &v->design_bridge); + + v->design_port = stp_vect_get_short (b->port_id); +} + +void +STP_VECT_set_vector (IN PRIO_VECTOR_T* v, OUT BPDU_BODY_T* b) +{ + unsigned long root_path_cost; + + stp_vect_set_bridge_id (&v->root_bridge, b->root_id); + + root_path_cost = htonl (v->root_path_cost); + (void) memcpy (b->root_path_cost, &root_path_cost, 4); + + stp_vect_set_bridge_id (&v->design_bridge, b->bridge_id); + + stp_vect_set_short (v->design_port, b->port_id); +} + +#ifdef STP_DBG + +/*ARGSUSED*/ +void +STP_VECT_br_id_print (IN char *title, IN BRIDGE_ID* br_id, IN Bool cr) +{ + stp_trace ("%s=%04lX-%02x%02x%02x%02x%02x%02x", + title, + (unsigned long) br_id->prio, + (unsigned char) br_id->addr[0], + (unsigned char) br_id->addr[1], + (unsigned char) br_id->addr[2], + (unsigned char) br_id->addr[3], + (unsigned char) br_id->addr[4], + (unsigned char) br_id->addr[5]); +#ifndef __SUN__ + stp_trace (cr ? "\n" : " "); +#endif +} + +void +STP_VECT_print (IN char *title, IN PRIO_VECTOR_T *v) +{ + stp_trace ("%s:", title); + STP_VECT_br_id_print ("rootBr", &v->root_bridge, False); + +/**** + stp_trace (" rpc=%ld ", (long) v->root_path_cost); +****/ + + STP_VECT_br_id_print ("designBr", &v->design_bridge, False); + +/****/ + stp_trace (" dp=%lx bp=%lx ", + (unsigned long) v->design_port, + (unsigned long) v->bridge_port); +/***********/ + stp_trace ("\n"); +} +#endif diff --git a/usr/src/lib/librstp/common/vector.h b/usr/src/lib/librstp/common/vector.h new file mode 100644 index 0000000000..917d4a64c7 --- /dev/null +++ b/usr/src/lib/librstp/common/vector.h @@ -0,0 +1,79 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* STP priority vectors API : 17.4.2 */ + +#ifndef _PRIO_VECTOR_H__ +#define _PRIO_VECTOR_H__ + +#define STP_DBG 1 + +typedef struct bridge_id +{ + unsigned short prio; + unsigned char addr[6]; +} BRIDGE_ID; + +typedef unsigned short PORT_ID; + +typedef struct prio_vector_t { + BRIDGE_ID root_bridge; + unsigned long root_path_cost; + BRIDGE_ID design_bridge; + PORT_ID design_port; + PORT_ID bridge_port; +} PRIO_VECTOR_T; + +void +STP_VECT_create (OUT PRIO_VECTOR_T* t, + IN BRIDGE_ID* root_br, + IN unsigned long root_path_cost, + IN BRIDGE_ID* design_bridge, + IN PORT_ID design_port, + IN PORT_ID bridge_port); +void +STP_VECT_copy (OUT PRIO_VECTOR_T* t, IN PRIO_VECTOR_T* f); + +int +STP_VECT_compare_bridge_id (IN BRIDGE_ID* b1, IN BRIDGE_ID* b2); + +int +STP_VECT_compare_vector (IN PRIO_VECTOR_T* v1, IN PRIO_VECTOR_T* v2); + +void +STP_VECT_get_vector (IN BPDU_BODY_T* b, OUT PRIO_VECTOR_T* v); + +void +STP_VECT_set_vector (IN PRIO_VECTOR_T* v, OUT BPDU_BODY_T* b); + +#ifdef STP_DBG +void +STP_VECT_print (IN char* title, IN PRIO_VECTOR_T* v); + +void +STP_VECT_br_id_print (IN char *title, IN BRIDGE_ID* br_id, IN Bool cr); + +#endif + +#endif /* _PRIO_VECTOR_H__ */ + + diff --git a/usr/src/lib/librstp/i386/Makefile b/usr/src/lib/librstp/i386/Makefile new file mode 100644 index 0000000000..946d8867f9 --- /dev/null +++ b/usr/src/lib/librstp/i386/Makefile @@ -0,0 +1,29 @@ +# +# 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. +# +# lib/librstp/i386/Makefile + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/librstp/sparc/Makefile b/usr/src/lib/librstp/sparc/Makefile new file mode 100644 index 0000000000..07093c0afe --- /dev/null +++ b/usr/src/lib/librstp/sparc/Makefile @@ -0,0 +1,29 @@ +# +# 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. +# +# lib/librstp/sparc/Makefile + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) |
