diff options
Diffstat (limited to 'usr')
167 files changed, 23271 insertions, 353 deletions
diff --git a/usr/src/Makefile.lint b/usr/src/Makefile.lint index 480dd36483..9b7d236939 100644 --- a/usr/src/Makefile.lint +++ b/usr/src/Makefile.lint @@ -71,6 +71,7 @@ COMMON_SUBDIRS = \ cmd/cmd-inet/lib \ cmd/cmd-inet/sbin \ cmd/cmd-inet/usr.bin \ + cmd/cmd-inet/usr.lib/bridged \ cmd/cmd-inet/usr.lib/dsvclockd \ cmd/cmd-inet/usr.lib/in.dhcpd \ cmd/cmd-inet/usr.lib/in.mpathd \ @@ -404,6 +405,7 @@ COMMON_SUBDIRS = \ lib/librcm \ lib/librdc \ lib/librestart \ + lib/librstp \ lib/librt \ lib/libscf \ lib/libsec \ diff --git a/usr/src/cmd/cmd-inet/etc/sock2path b/usr/src/cmd/cmd-inet/etc/sock2path index aba55bb652..cfcfe8bc4e 100644 --- a/usr/src/cmd/cmd-inet/etc/sock2path +++ b/usr/src/cmd/cmd-inet/etc/sock2path @@ -17,7 +17,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. # # socket configuration information @@ -53,3 +53,4 @@ 28 2 0 /dev/nca 29 4 1 /dev/spdsock + 31 1 0 trill diff --git a/usr/src/cmd/cmd-inet/usr.lib/Makefile b/usr/src/cmd/cmd-inet/usr.lib/Makefile index 4bc772a574..8c1e5198ee 100644 --- a/usr/src/cmd/cmd-inet/usr.lib/Makefile +++ b/usr/src/cmd/cmd-inet/usr.lib/Makefile @@ -19,13 +19,11 @@ # CDDL HEADER END # # -# Copyright 2007 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" -# -SUBDIRS= dhcp dsvclockd in.chargend in.daytimed \ +SUBDIRS= bridged dhcp dsvclockd in.chargend in.daytimed \ in.discardd in.echod in.dhcpd in.mpathd in.ndpd \ in.ripngd in.timed inetd mdnsd ncaconfd pppoe \ slpd wanboot wpad diff --git a/usr/src/cmd/cmd-inet/usr.lib/bridged/Makefile b/usr/src/cmd/cmd-inet/usr.lib/bridged/Makefile new file mode 100644 index 0000000000..4d3129c1ec --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.lib/bridged/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. +# +# cmd/cmd-inet/usr.lib/bridged/Makefile +# + +PROG= bridged +MANIFEST= bridge.xml +OBJS= dlpi.o door.o events.o main.o rstp.o +SRCS= $(OBJS:%.o=%.c) + +include ../../../Makefile.cmd + +ROOTMANIFESTDIR= $(ROOTSVCNETWORK) + +.KEEP_STATE: + +all: $(PROG) + +LDLIBS += -lsocket -lrstp -ldlpi -ldladm -lumem +CFLAGS += $(CCVERBOSE) +CPPFLAGS += -D__SUN__ + +.PARALLEL: $(OBJS) + +$(PROG): $(OBJS) + $(LINK.c) $(OBJS) -o $@ $(LDLIBS) + $(POST_PROCESS) + +include ../Makefile.lib + +install: $(PROG) $(ROOTLIBPROG) $(ROOTMANIFEST) + +clean: + $(RM) $(OBJS) + +lint: lint_SRCS + +include ../../../Makefile.targ diff --git a/usr/src/cmd/cmd-inet/usr.lib/bridged/bridge.xml b/usr/src/cmd/cmd-inet/usr.lib/bridged/bridge.xml new file mode 100644 index 0000000000..3c9d0b5695 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.lib/bridged/bridge.xml @@ -0,0 +1,109 @@ +<?xml version="1.0"?> +<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1"> +<!-- + Copyright 2009 Sun Microsystems, Inc. All rights reserved. + Use is subject to license terms. + + 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 + + NOTE: This service manifest is not editable; its contents will + be overwritten by package or patch operations, including + operating system upgrade. Make customizations in a different + file. + + Service manifest for bridging. +--> + +<service_bundle type='manifest' name='SUNWbridge:bridge'> + +<service + name='network/bridge' + type='service' + version='1'> + + <dependency + name='filesystem' + grouping='require_all' + restart_on='error' + type='service'> + <service_fmri value='svc:/system/filesystem/local'/> + </dependency> + + <dependency + name='datalink' + grouping='require_all' + restart_on='error' + type='service'> + <service_fmri value='svc:/network/datalink-management'/> + </dependency> + + <exec_method + type='method' + name='stop' + exec=':kill' + timeout_seconds='60' > + </exec_method> + + <exec_method + type='method' + name='start' + exec='/usr/lib/bridged %i' + timeout_seconds='60' > + </exec_method> + + <exec_method + type='method' + name='refresh' + exec=':kill -HUP' + timeout_seconds='60' > + </exec_method> + + <property_group + name='config' + type='application'> + <stability value='Evolving' /> + <propval name='priority' type='count' value='32768' /> + <propval name='max-age' type='count' value='5120' /> + <propval name='hello-time' type='count' value='512' /> + <propval name='forward-delay' type='count' value='3840' /> + <propval name='force-protocol' type='count' value='3' /> + <propval name='debug' type='boolean' value='false' /> + <propval name='table-maximum' type='count' value='10000' /> + </property_group> + + <stability value='Unstable' /> + + <template> + <common_name> + <loctext xml:lang='C'>bridge</loctext> + </common_name> + <description> + <loctext xml:lang='C'> +The "bridge" service provides Ethernet bridging and related protocols. + </loctext> + </description> + <documentation> + <manpage title='bridged' section='1M' + manpath='/usr/share/man' /> + </documentation> + </template> +</service> + +</service_bundle> diff --git a/usr/src/cmd/cmd-inet/usr.lib/bridged/dlpi.c b/usr/src/cmd/cmd-inet/usr.lib/bridged/dlpi.c new file mode 100644 index 0000000000..c464d44041 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.lib/bridged/dlpi.c @@ -0,0 +1,245 @@ +/* + * 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. + */ + +/* + * bridged - bridging control daemon. This module provides DLPI-specific + * functions for interface to libdlpi. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <sys/types.h> +#include <syslog.h> +#include <stropts.h> +#include <stp_in.h> +#include <net/if_types.h> +#include <net/if_dl.h> +#include <sys/ethernet.h> +#include <sys/pfmod.h> + +#include "global.h" + +static const uchar_t bridge_group_address[] = BRIDGE_GROUP_ADDRESS; + +static const ushort_t bpdu_filter[] = { + ENF_PUSHWORD | 0, /* check for 1:80:c2:0:0:0 dest. */ + ENF_PUSHLIT | ENF_CAND, +#ifdef _BIG_ENDIAN + 0x0180, +#else + 0x8001, +#endif + ENF_PUSHWORD | 1, + ENF_PUSHLIT | ENF_CAND, +#ifdef _BIG_ENDIAN + 0xC200, +#else + 0x00C2, +#endif + ENF_PUSHWORD | 2, + ENF_PUSHZERO | ENF_CAND, + ENF_PUSHWORD | 7, /* check for SSAP/DSAP 42 42 */ + ENF_PUSHLIT | ENF_CAND, + 0x4242, +}; + +/* + * Because we're called by dlpi_recv(), we're called with the engine lock held. + */ +/*ARGSUSED*/ +static void +dlpi_notify(dlpi_handle_t dlpi, dlpi_notifyinfo_t *info, void *arg) +{ + struct portdata *port = arg; + int rc; + + switch (info->dni_note) { + case DL_NOTE_SPEED: + /* libdlpi gives us Kbps, and we want Mbps */ + if (port->speed == info->dni_speed / 1000) + break; + port->speed = info->dni_speed / 1000; + if ((rc = STP_IN_changed_port_speed(port->port_index, + port->speed)) != 0) + syslog(LOG_ERR, "STP can't change port speed on %s: %s", + port->name, STP_IN_get_error_explanation(rc)); + break; + + case DL_NOTE_PHYS_ADDR: + if (memcmp(info->dni_physaddr, port->mac_addr, ETHERADDRL) != 0) + rstp_change_mac(port, info->dni_physaddr); + break; + + case DL_NOTE_LINK_DOWN: + if (!port->phys_status) + break; + port->phys_status = B_FALSE; + if (!port->admin_status || protect != DLADM_BRIDGE_PROT_STP || + port->sdu_failed) + break; + if ((rc = STP_IN_enable_port(port->port_index, False)) != 0) + syslog(LOG_ERR, "STP can't disable port %s: %s", + port->name, STP_IN_get_error_explanation(rc)); + break; + + case DL_NOTE_LINK_UP: + if (port->phys_status) + break; + port->phys_status = B_TRUE; + if (!port->admin_status || protect != DLADM_BRIDGE_PROT_STP || + port->sdu_failed) { + port->bpdu_protect = B_FALSE; + break; + } + /* + * If we're not running STP, and the link state has just come + * up, then clear out any protection shutdown state, and allow + * us to forward again. + */ + if (port->admin_non_stp && port->bpdu_protect) { + port->bpdu_protect = B_FALSE; + enable_forwarding(port); + } + if ((rc = STP_IN_enable_port(port->port_index, True)) != 0) + syslog(LOG_ERR, "STP can't enable port %s: %s", + port->name, STP_IN_get_error_explanation(rc)); + break; + } +} + +boolean_t +port_dlpi_open(const char *portname, struct portdata *port, + datalink_class_t class) +{ + uchar_t addrbuf[DLPI_PHYSADDR_MAX]; + size_t alen = DLPI_PHYSADDR_MAX; + int rc; + char addrstr[ETHERADDRL * 3]; + + /* + * We use DLPI 'raw' mode so that we get access to the received + * Ethernet 802 length field. libdlpi otherwise eats this value. Note + * that 'raw' mode support is required in order to use snoop, so it's + * expected to be common, even if it's not documented. + */ + rc = dlpi_open(portname, &port->dlpi, DLPI_RAW); + if (rc != DLPI_SUCCESS) { + syslog(LOG_ERR, "can't open %s: %s", portname, + dlpi_strerror(rc)); + return (B_FALSE); + } + + port->phys_status = B_TRUE; + port->sdu_failed = B_FALSE; + port->bpdu_protect = B_FALSE; + + /* + * Now that the driver is open, we can get at least the initial value + * of the interface speed. We need to do this before establishing the + * notify callback, so that it can update us later. + */ + get_dladm_speed(port); + + /* + * Save off the libdlpi port name, as it's dynamically allocated, and + * the name we're passed is not. + */ + port->name = dlpi_linkname(port->dlpi); + + /* + * We can't bind SAP 0 or enable multicast on an etherstub. It's ok, + * though, because there's no real hardware involved. + */ + if (class != DATALINK_CLASS_ETHERSTUB) { + if ((rc = dlpi_bind(port->dlpi, 0, NULL)) != DLPI_SUCCESS) { + syslog(LOG_ERR, "can't bind %s: %s", portname, + dlpi_strerror(rc)); + return (B_FALSE); + } + if ((rc = dlpi_enabmulti(port->dlpi, bridge_group_address, + sizeof (bridge_group_address))) != DLPI_SUCCESS) { + syslog(LOG_ERR, "can't enable multicast on %s: %s", + portname, dlpi_strerror(rc)); + return (B_FALSE); + } + } + + if ((rc = dlpi_enabnotify(port->dlpi, + DL_NOTE_PHYS_ADDR | DL_NOTE_LINK_DOWN | DL_NOTE_LINK_UP | + DL_NOTE_SPEED, dlpi_notify, port, &port->notifyid)) != + DLPI_SUCCESS) { + syslog(LOG_WARNING, "no DLPI notification on %s: %s", portname, + dlpi_strerror(rc)); + } + + rc = dlpi_get_physaddr(port->dlpi, DL_CURR_PHYS_ADDR, addrbuf, &alen); + if (rc != DLPI_SUCCESS) { + syslog(LOG_ERR, "unable to get MAC address on %s: %s", + port->name, dlpi_strerror(rc)); + return (B_FALSE); + } + if (alen != ETHERADDRL) { + syslog(LOG_ERR, "bad MAC address length %d on %s", + alen, port->name); + return (B_FALSE); + } + (void) memcpy(port->mac_addr, addrbuf, ETHERADDRL); + + if (class != DATALINK_CLASS_ETHERSTUB) { + int fd = dlpi_fd(port->dlpi); + int lowflag = 1; + + if (strioctl(fd, DLIOCLOWLINK, &lowflag, sizeof (lowflag)) != 0) + syslog(LOG_WARNING, "low-link notify failed on %s: %m", + portname); + if (ioctl(fd, I_PUSH, "pfmod") == 0) { + struct packetfilt pf; + + pf.Pf_Priority = 0; + pf.Pf_FilterLen = sizeof (bpdu_filter) / + sizeof (*bpdu_filter); + (void) memcpy(pf.Pf_Filter, bpdu_filter, + sizeof (bpdu_filter)); + if (strioctl(fd, PFIOCSETF, &pf, sizeof (pf)) == -1) + syslog(LOG_WARNING, + "pfil ioctl failed on %s: %m", portname); + } else { + syslog(LOG_WARNING, "pfil push failed on %s: %m", + portname); + } + } + + if (debugging) { + (void) _link_ntoa(port->mac_addr, addrstr, ETHERADDRL, + IFT_OTHER); + syslog(LOG_DEBUG, "got MAC address %s on %s", addrstr, + port->name); + } + + return (B_TRUE); +} diff --git a/usr/src/cmd/cmd-inet/usr.lib/bridged/door.c b/usr/src/cmd/cmd-inet/usr.lib/bridged/door.c new file mode 100644 index 0000000000..53afc57218 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.lib/bridged/door.c @@ -0,0 +1,216 @@ +/* + * 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. + */ + +/* + * bridged - bridging control daemon. This module provides the door-based + * interface used by user applications to gather bridge status information. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <syslog.h> +#include <door.h> +#include <errno.h> +#include <alloca.h> +#include <libdlpi.h> +#include <libdlbridge.h> +#include <stp_in.h> +#include <net/bridge.h> + +#include "global.h" + +#define DOOR_DIRMODE 0755 +#define DOOR_FILEMODE 0444 + +static int door_fd = -1; +static char doorname[MAXPATHLEN]; + +/*ARGSUSED*/ +static void +bridge_door_server(void *cookie, char *argp, size_t arg_size, door_desc_t *dp, + uint_t ndesc) +{ + /* LINTED: alignment */ + bridge_door_cmd_t *bdc = (bridge_door_cmd_t *)argp; + int retv = EINVAL; + bridge_door_cfg_t bdcf; + UID_STP_STATE_T smstate; + UID_STP_PORT_CFG_T portcfg; + UID_STP_PORT_STATE_T portstate; + struct portdata *pdp; + int twoints[2]; + + if (arg_size < sizeof (*bdc) || lock_engine() != 0) { + (void) door_return((char *)&retv, sizeof (retv), NULL, 0); + return; + } + + switch (bdc->bdc_type) { + case bdcBridgeGetConfig: + if ((retv = STP_IN_stpm_get_cfg(0, &bdcf.bdcf_cfg)) != 0) + break; + bdcf.bdcf_prot = protect; + unlock_engine(); + (void) door_return((char *)&bdcf, sizeof (bdcf), NULL, 0); + return; + + case bdcBridgeGetState: + if ((retv = STP_IN_stpm_get_state(0, &smstate)) != 0) + break; + unlock_engine(); + (void) door_return((char *)&smstate, sizeof (smstate), NULL, 0); + return; + + case bdcBridgeGetPorts: { + datalink_id_t *dlp; + int *rbuf; + size_t rlen; + int i, nports; + + if (nextport == 0) { + twoints[0] = 0; + rbuf = twoints; + rlen = sizeof (twoints); + } else { + rlen = sizeof (int) + nextport * sizeof (datalink_id_t); + rbuf = alloca(rlen); + dlp = (datalink_id_t *)(rbuf + 1); + for (i = nports = 0; i < nextport; i++) { + if (allports[i]->kern_added) + dlp[nports++] = allports[i]->linkid; + } + rbuf[0] = nports; + rlen = sizeof (int) + nports * sizeof (datalink_id_t); + } + unlock_engine(); + (void) door_return((char *)rbuf, rlen, NULL, 0); + return; + } + + case bdcBridgeGetRefreshCount: + twoints[0] = refresh_count; + twoints[1] = 0; + unlock_engine(); + (void) door_return((char *)twoints, sizeof (twoints), NULL, 0); + return; + + case bdcPortGetConfig: + if ((pdp = find_by_linkid(bdc->bdc_linkid)) == NULL) + break; + retv = STP_IN_port_get_cfg(0, pdp->port_index, &portcfg); + if (retv != 0) + break; + unlock_engine(); + (void) door_return((char *)&portcfg, sizeof (portcfg), NULL, 0); + return; + + case bdcPortGetState: + if ((pdp = find_by_linkid(bdc->bdc_linkid)) == NULL) + break; + portstate.port_no = pdp->port_index; + if ((retv = STP_IN_port_get_state(0, &portstate)) != 0) + break; + if (pdp->sdu_failed) + portstate.state = UID_PORT_BADSDU; + else if (protect != DLADM_BRIDGE_PROT_STP) + portstate.state = UID_PORT_NON_STP; + else if (pdp->admin_non_stp && pdp->bpdu_protect) + portstate.state = UID_PORT_DISABLED; + unlock_engine(); + (void) door_return((char *)&portstate, sizeof (portstate), + NULL, 0); + return; + + case bdcPortGetForwarding: + if ((pdp = find_by_linkid(bdc->bdc_linkid)) == NULL) + break; + twoints[0] = pdp->admin_status ? 1 : 0; + twoints[1] = 0; + unlock_engine(); + (void) door_return((char *)twoints, sizeof (twoints), NULL, 0); + return; + } + unlock_engine(); + (void) door_return((char *)&retv, sizeof (retv), NULL, 0); +} + +static void +cleanup_door(void) +{ + if (door_fd != -1) { + (void) door_revoke(door_fd); + door_fd = -1; + } + if (doorname[0] != '\0') { + (void) unlink(doorname); + doorname[0] = '\0'; + } +} + +void +init_door(void) +{ + int fd; + + /* Make sure that the control directory exists */ + (void) mkdir(DOOR_DIRNAME, DOOR_DIRMODE); + + /* Each instance gets a separate door. */ + (void) snprintf(doorname, sizeof (doorname), "%s/%s", DOOR_DIRNAME, + instance_name); + + /* Do a low-overhead "touch" on the file that will be the door node. */ + fd = open(doorname, + O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW | O_NONBLOCK, + DOOR_FILEMODE); + if (fd != -1) { + (void) close(fd); + } else if (errno != EEXIST) { + syslog(LOG_ERR, "unable to create control door node: %m"); + exit(EXIT_FAILURE); + } + + (void) atexit(cleanup_door); + + /* Create the door. */ + door_fd = door_create(bridge_door_server, NULL, + DOOR_REFUSE_DESC | DOOR_NO_CANCEL); + if (door_fd == -1) { + syslog(LOG_ERR, "unable to create control door: %m"); + exit(EXIT_FAILURE); + } + + /* Attach the door to the file. */ + (void) fdetach(doorname); + if (fattach(door_fd, doorname) == -1) { + syslog(LOG_ERR, "unable to attach control door: %m"); + exit(EXIT_FAILURE); + } +} diff --git a/usr/src/cmd/cmd-inet/usr.lib/bridged/events.c b/usr/src/cmd/cmd-inet/usr.lib/bridged/events.c new file mode 100644 index 0000000000..fe64506d1e --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.lib/bridged/events.c @@ -0,0 +1,639 @@ +/* + * 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. + */ + +/* + * bridged - bridging control daemon. This module handles events and general + * port-related operations. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <sys/types.h> +#include <syslog.h> +#include <libdlpi.h> +#include <libdladm.h> +#include <libdllink.h> +#include <libdlbridge.h> +#include <libdlvlan.h> +#include <libdlstat.h> +#include <stp_in.h> +#include <stp_vectors.h> +#include <net/if_types.h> +#include <net/bridge.h> +#include <sys/ethernet.h> + +#include "global.h" + +int refresh_count = 1; /* never zero */ +dladm_bridge_prot_t protect = DLADM_BRIDGE_PROT_STP; + +/* + * The 'allports' array is an array of pointers to the struct portdata + * structures. We reallocate 'allports' as needed, but the portdata must + * remain where it's initially allocated, because libdlpi's notification + * mechanism has a copy of a pointer to this structure. + */ +uint_t nextport; +struct portdata **allports; + +/* Port allocation increment (arbitrary) */ +#define ALLOCINCR 10 +static uint_t numports; + +static datalink_id_t main_linkid; + +int control_fd; + +static void +linkdown(void) +{ + (void) dladm_destroy_datalink_id(dlhandle, main_linkid, + DLADM_OPT_ACTIVE); +} + +void +open_bridge_control(void) +{ + bridge_newbridge_t bnb; + dladm_status_t status; + char buf[DLADM_STRSIZE]; + + if ((control_fd = open(BRIDGE_CTLPATH, O_RDWR | O_NONBLOCK)) == -1) { + perror(BRIDGE_CTLPATH); + exit(EXIT_FAILURE); + } + (void) snprintf(bnb.bnb_name, sizeof (bnb.bnb_name), "%s0", + instance_name); + status = dladm_name2info(dlhandle, bnb.bnb_name, &bnb.bnb_linkid, NULL, + NULL, NULL); + if (status != DLADM_STATUS_OK) { + (void) fprintf(stderr, "bridged: %s: %s\n", bnb.bnb_name, + dladm_status2str(status, buf)); + exit(EXIT_FAILURE); + } + if (strioctl(control_fd, BRIOC_NEWBRIDGE, &bnb, sizeof (bnb)) == -1) { + perror("NEWBRIDGE"); + exit(EXIT_FAILURE); + } + main_linkid = bnb.bnb_linkid; + if (strioctl(control_fd, BRIOC_TABLEMAX, &tablemax, + sizeof (tablemax)) == -1) { + syslog(LOG_ERR, "cannot set table max %lu on bridge %s: %m", + tablemax, instance_name); + exit(EXIT_FAILURE); + } + /* + * This covers for any previous incarnation where we might have crashed + * or been SIGKILL'd and failed to take down the datalink. + */ + linkdown(); + (void) atexit(linkdown); + status = dladm_up_datalink_id(dlhandle, bnb.bnb_linkid); + if (status != DLADM_STATUS_OK) { + (void) fprintf(stderr, "bridged: %s link up: %s\n", + bnb.bnb_name, dladm_status2str(status, buf)); + exit(EXIT_FAILURE); + } +} + +struct portdata * +find_by_linkid(datalink_id_t linkid) +{ + int i; + struct portdata *port; + + for (i = 0; i < nextport; i++) { + port = allports[i]; + if (port->linkid == linkid) + return (port); + } + return (NULL); +} + +/*ARGSUSED2*/ +static int +set_vlan(dladm_handle_t handle, datalink_id_t linkid, void *arg) +{ + struct portdata *port; + dladm_status_t status; + dladm_vlan_attr_t vinfo; + char pointless[DLADM_STRSIZE]; + bridge_vlanenab_t bve; + + status = dladm_vlan_info(handle, linkid, &vinfo, DLADM_OPT_ACTIVE); + if (status != DLADM_STATUS_OK) { + syslog(LOG_DEBUG, "can't get VLAN info on link ID %u: %s", + linkid, dladm_status2str(status, pointless)); + return (DLADM_WALK_CONTINUE); + } + + port = find_by_linkid(vinfo.dv_linkid); + if (port == NULL || !port->kern_added) + return (DLADM_WALK_CONTINUE); + + bve.bve_linkid = port->linkid; + bve.bve_vlan = vinfo.dv_vid; + bve.bve_onoff = B_TRUE; + if (strioctl(control_fd, BRIOC_VLANENAB, &bve, sizeof (bve)) == -1) { + syslog(LOG_ERR, "unable to enable VLAN %d on linkid %u: %m", + vinfo.dv_vid, port->linkid); + return (DLADM_WALK_TERMINATE); + } else { + return (DLADM_WALK_CONTINUE); + } +} + +/* + * If the named port already exists, then update its configuration. If it + * doesn't, then create and enable it. + */ +static void +update_port(int vlan_id, const char *portname, datalink_id_t linkid, + datalink_class_t class) +{ + int posn; + struct portdata *port; + struct pollfd *fds; + int port_index; + struct { + datalink_id_t linkid; + char linkname[MAXLINKNAMELEN]; + } adddata; + bridge_setpvid_t bsv; + uint_t propval, valcnt; + dladm_status_t status; + + for (posn = 0; posn < nextport; posn++) { + if (allports[posn]->linkid == linkid) + break; + } + + /* If we need to allocate more array space, then do so in chunks. */ + if (posn >= numports) { + struct portdata **newarr; + + newarr = realloc(allports, + sizeof (*newarr) * (nextport + ALLOCINCR)); + if (newarr != NULL) + allports = newarr; + fds = realloc(fdarray, + sizeof (*fds) * (nextport + ALLOCINCR + FDOFFSET)); + if (fds != NULL) + fdarray = fds; + if (newarr == NULL || fds == NULL) { + syslog(LOG_ERR, "unable to add %s; no memory", + portname); + return; + } + numports = nextport + ALLOCINCR; + } + + port_index = posn + 1; + fds = fdarray + posn + FDOFFSET; + + /* If our linkid search ran to the end, then this is a new port. */ + if (posn == nextport) { + if ((port = calloc(1, sizeof (*port))) == NULL) { + syslog(LOG_ERR, "unable to add %s; no memory", + portname); + return; + } + allports[posn] = port; + port->vlan_id = vlan_id; + port->linkid = linkid; + port->port_index = port_index; + port->phys_status = B_TRUE; + port->admin_status = B_TRUE; + port->state = BLS_BLOCKLISTEN; + nextport++; + } else { + /* Located port by linkid; we're just updating existing data */ + port = allports[posn]; + + /* + * If it changed name, then close and reopen so we log under + * the most current name for this port. + */ + if (port->name != NULL && strcmp(portname, port->name) != 0) { + if (port->dlpi != NULL) + dlpi_close(port->dlpi); + port->dlpi = NULL; + port->name = NULL; + fds->fd = -1; + fds->events = 0; + } + } + + /* + * If the port is not yet attached to the bridge in the kernel, then do + * that now. + */ + if (!port->kern_added) { + adddata.linkid = linkid; + (void) strlcpy(adddata.linkname, portname, + sizeof (adddata.linkname)); + if (strioctl(control_fd, BRIOC_ADDLINK, &adddata, + sizeof (adddata.linkid) + strlen(adddata.linkname)) == -1) { + syslog(LOG_ERR, "cannot bridge %s: %m", portname); + goto failure; + } + port->kern_added = B_TRUE; + } + + port->referenced = B_TRUE; + + valcnt = 1; + status = dladm_get_linkprop_values(dlhandle, linkid, + DLADM_PROP_VAL_PERSISTENT, "forward", &propval, &valcnt); + if (status == DLADM_STATUS_OK) + port->admin_status = propval; + + bsv.bsv_vlan = 1; + status = dladm_get_linkprop_values(dlhandle, linkid, + DLADM_PROP_VAL_PERSISTENT, "default_tag", &propval, &valcnt); + if (status == DLADM_STATUS_OK) + bsv.bsv_vlan = propval; + + bsv.bsv_linkid = linkid; + if (strioctl(control_fd, BRIOC_SETPVID, &bsv, sizeof (bsv)) == -1) { + syslog(LOG_ERR, "can't set PVID on %s: %m", portname); + goto failure; + } + + if (port->dlpi == NULL) { + if (!port_dlpi_open(portname, port, class)) + goto failure; + fds->fd = dlpi_fd(port->dlpi); + fds->events = POLLIN; + } + + if (rstp_add_port(port)) + return; + +failure: + if (port->dlpi != NULL) { + dlpi_close(port->dlpi); + port->dlpi = NULL; + port->name = NULL; + fds->fd = -1; + fds->events = 0; + } + if (port->kern_added) { + if (strioctl(control_fd, BRIOC_REMLINK, &port->linkid, + sizeof (port->linkid)) == -1) + syslog(LOG_ERR, "cannot remove from bridge %s: %m", + portname); + else + port->kern_added = B_FALSE; + } + if (posn + 1 == nextport) { + free(port); + nextport--; + } +} + +/*ARGSUSED2*/ +static int +update_link(dladm_handle_t handle, datalink_id_t linkid, void *arg) +{ + dladm_status_t status; + dladm_conf_t conf; + char bridge[MAXLINKNAMELEN], linkname[MAXLINKNAMELEN]; + char pointless[DLADM_STRSIZE]; + datalink_class_t class; + + status = dladm_read_conf(handle, linkid, &conf); + if (status != DLADM_STATUS_OK) { + syslog(LOG_DEBUG, "can't get status on link ID %u: %s", linkid, + dladm_status2str(status, pointless)); + return (DLADM_WALK_CONTINUE); + } + + status = dladm_bridge_getlink(handle, linkid, bridge, sizeof (bridge)); + if (status == DLADM_STATUS_OK && strcmp(bridge, instance_name) == 0) { + status = dladm_datalink_id2info(handle, linkid, NULL, &class, + NULL, linkname, sizeof (linkname)); + if (status == DLADM_STATUS_OK) { + update_port(0, linkname, linkid, class); + } else { + syslog(LOG_ERR, "unable to get link info for ID %u: %s", + linkid, dladm_status2str(status, pointless)); + } + } else if (debugging) { + if (status != DLADM_STATUS_OK) + syslog(LOG_DEBUG, + "unable to get bridge data for ID %u: %s", + linkid, dladm_status2str(status, pointless)); + else + syslog(LOG_DEBUG, "link ID %u is on bridge %s, not %s", + linkid, bridge, instance_name); + } + dladm_destroy_conf(handle, conf); + return (DLADM_WALK_CONTINUE); +} + +/* + * Refresh action - reread configuration properties. + */ +static void +handle_refresh(int sigfd) +{ + int i; + struct portdata *pdp; + struct pollfd *fdp; + char buf[16]; + dladm_status_t status; + boolean_t new_debug; + uint32_t new_tablemax; + + /* Drain signal events from pipe */ + if (sigfd != -1) + (void) read(sigfd, buf, sizeof (buf)); + + status = dladm_bridge_get_privprop(instance_name, &new_debug, + &new_tablemax); + if (status == DLADM_STATUS_OK) { + if (debugging && !new_debug) + syslog(LOG_DEBUG, "disabling debugging"); + debugging = new_debug; + if (new_tablemax != tablemax) { + syslog(LOG_DEBUG, "changed tablemax from %lu to %lu", + tablemax, new_tablemax); + if (strioctl(control_fd, BRIOC_TABLEMAX, &new_tablemax, + sizeof (tablemax)) == -1) + syslog(LOG_ERR, "cannot set table max " + "%lu on bridge %s: %m", tablemax, + instance_name); + else + tablemax = new_tablemax; + } + } else { + syslog(LOG_ERR, "%s: unable to refresh bridge properties: %s", + instance_name, dladm_status2str(status, buf)); + } + + rstp_refresh(); + + for (i = 0; i < nextport; i++) + allports[i]->referenced = B_FALSE; + + /* + * libdladm doesn't guarantee anything about link ordering in a walk, + * so we do this walk twice: once to pick up the ports, and a second + * time to get the enabled VLANs on all ports. + */ + (void) dladm_walk_datalink_id(update_link, dlhandle, NULL, + DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE); + + (void) dladm_walk_datalink_id(set_vlan, dlhandle, NULL, + DATALINK_CLASS_VLAN, DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE); + + /* + * If any ports now show up as unreferenced, then they've been removed + * from the configuration. + */ + for (i = 0; i < nextport; i++) { + pdp = allports[i]; + fdp = fdarray + i + FDOFFSET; + if (!pdp->referenced) { + if (pdp->stp_added) { + (void) STP_IN_port_remove(pdp->vlan_id, + pdp->port_index); + pdp->stp_added = B_FALSE; + } + if (pdp->dlpi != NULL) { + dlpi_close(pdp->dlpi); + pdp->dlpi = NULL; + pdp->name = NULL; + fdp->fd = -1; + fdp->events = 0; + } + if (pdp->kern_added) { + if (strioctl(control_fd, BRIOC_REMLINK, + &pdp->linkid, sizeof (pdp->linkid)) == -1) + syslog(LOG_ERR, "cannot remove linkid " + "%u from bridge %s: %m", + pdp->linkid, instance_name); + pdp->kern_added = B_FALSE; + } + } + } + + if (++refresh_count == 0) + refresh_count = 1; +} + +/* + * Handle messages on the common control stream. This currently just deals + * with port SDU mismatches. + */ +static void +handle_control(void) +{ + bridge_ctl_t bc; + ssize_t retv; + struct portdata *port; + int rc; + + retv = read(control_fd, &bc, sizeof (bc)); + if (retv != sizeof (bc)) + return; + if ((port = find_by_linkid(bc.bc_linkid)) == NULL) + return; + if (port->sdu_failed == bc.bc_failed) + return; + port->sdu_failed = bc.bc_failed; + if (!port->phys_status || !port->admin_status || + protect != DLADM_BRIDGE_PROT_STP) + return; + if (port->admin_non_stp) { + bridge_setstate_t bss; + + bss.bss_linkid = port->linkid; + bss.bss_state = !port->sdu_failed && !port->bpdu_protect ? + BLS_FORWARDING : BLS_BLOCKLISTEN; + if (strioctl(control_fd, BRIOC_SETSTATE, &bss, + sizeof (bss)) == -1) { + syslog(LOG_ERR, "cannot set STP state on %s: %m", + port->name); + } + } + if ((rc = STP_IN_enable_port(port->port_index, !bc.bc_failed)) != 0) + syslog(LOG_ERR, "STP can't %s port %s for SDU failure: %s", + port->name, bc.bc_failed ? "disable" : "enable", + STP_IN_get_error_explanation(rc)); +} + +static void +receive_packet(struct portdata *port) +{ + int rc; + size_t buflen; + uint16_t buffer[ETHERMAX / sizeof (uint16_t)]; + struct ether_header *eh; + char sender[ETHERADDRL * 3]; + + buflen = sizeof (buffer); + rc = dlpi_recv(port->dlpi, NULL, NULL, buffer, &buflen, 1, NULL); + if (rc != DLPI_SUCCESS) { + if (rc != DLPI_ETIMEDOUT) + syslog(LOG_ERR, "receive failure on %s: %s", port->name, + dlpi_strerror(rc)); + return; + } + + /* + * If we're administratively disabled, then don't deliver packets to + * the STP state machine. It will re-enable the port because it uses + * the same variable for both link status and administrative state. + */ + if (!port->admin_status || protect != DLADM_BRIDGE_PROT_STP) { + if (debugging) + syslog(LOG_DEBUG, + "discard BPDU on non-forwarding interface %s", + port->name); + return; + } + + /* + * There's a mismatch between the librstp and libdlpi expectations on + * receive. librstp wants the packet to start with the 802 length + * field, not the destination address. + */ + eh = (struct ether_header *)buffer; + rc = STP_IN_check_bpdu_header((BPDU_T *)&eh->ether_type, buflen); + + /* + * Note that we attempt to avoid calling the relatively expensive + * _link_ntoa function unless we're going to use the result. In normal + * usage, we don't need this string. + */ + if (rc == 0) { + if (port->admin_non_stp && !port->bpdu_protect) { + bridge_setstate_t bss; + + (void) _link_ntoa(eh->ether_shost.ether_addr_octet, + sender, ETHERADDRL, IFT_OTHER); + syslog(LOG_WARNING, "unexpected BPDU on %s from %s; " + "forwarding disabled", port->name, sender); + port->bpdu_protect = B_TRUE; + bss.bss_linkid = port->linkid; + bss.bss_state = BLS_BLOCKLISTEN; + if (strioctl(control_fd, BRIOC_SETSTATE, &bss, + sizeof (bss)) == -1) { + syslog(LOG_ERR, "cannot set STP state on " + "%s: %m", port->name); + } + return; + } + if (debugging) { + (void) _link_ntoa(eh->ether_shost.ether_addr_octet, + sender, ETHERADDRL, IFT_OTHER); + syslog(LOG_DEBUG, "got BPDU from %s on %s; %d bytes", + sender, port->name, buflen); + } + rc = STP_IN_rx_bpdu(port->vlan_id, port->port_index, + (BPDU_T *)&eh->ether_type, buflen); + } + if (rc != 0) { + (void) _link_ntoa(eh->ether_shost.ether_addr_octet, sender, + ETHERADDRL, IFT_OTHER); + syslog(LOG_DEBUG, + "discarded malformed packet on %s from %s: %s", + port->name, sender, STP_IN_get_error_explanation(rc)); + } +} + +void +get_dladm_speed(struct portdata *port) +{ + dladm_status_t status; + uint64_t ifspeed; + + status = dladm_get_single_mac_stat(dlhandle, port->linkid, "ifspeed", + KSTAT_DATA_UINT64, &ifspeed); + if (status == DLADM_STATUS_OK && ifspeed != 0) + port->speed = ifspeed / 1000000; + else + port->speed = 10UL; +} + +void +enable_forwarding(struct portdata *port) +{ + bridge_setstate_t bss; + + bss.bss_linkid = port->linkid; + bss.bss_state = BLS_FORWARDING; + if (strioctl(control_fd, BRIOC_SETSTATE, &bss, sizeof (bss)) == -1) + syslog(LOG_ERR, "cannot set STP state on %s: %m", port->name); +} + +void +event_loop(void) +{ + int i; + hrtime_t last_time, now; + int tout; + + if (lock_engine() != 0) { + syslog(LOG_ERR, "mutex lock"); + exit(EXIT_FAILURE); + } + + /* Bootstrap configuration */ + handle_refresh(-1); + + last_time = gethrtime(); + while (!shutting_down) { + now = gethrtime(); + if (now - last_time >= 1000000000ll) { + (void) STP_IN_one_second(); + tout = 1000; + last_time = now; + } else { + tout = 1000 - (now - last_time) / 1000000ll; + } + unlock_engine(); + (void) poll(fdarray, nextport + FDOFFSET, tout); + if (lock_engine() != 0) { + syslog(LOG_ERR, "mutex lock"); + exit(EXIT_FAILURE); + } + if (fdarray[0].revents & POLLIN) + handle_refresh(fdarray[0].fd); + if (fdarray[1].revents & POLLIN) + handle_control(); + for (i = 0; i < nextport; i++) { + if (fdarray[i + FDOFFSET].revents & POLLIN) + receive_packet(allports[i]); + } + } + unlock_engine(); +} diff --git a/usr/src/cmd/cmd-inet/usr.lib/bridged/global.h b/usr/src/cmd/cmd-inet/usr.lib/bridged/global.h new file mode 100644 index 0000000000..9b10e78ad0 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.lib/bridged/global.h @@ -0,0 +1,108 @@ +/* + * 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 _BRIDGED_GLOBAL_H +#define _BRIDGED_GLOBAL_H + +/* + * Globally visible symbols within the "bridged" bridging daemon + */ + +#include <sys/types.h> +#include <sys/ethernet.h> +#include <net/bridge.h> +#include <libdlpi.h> +#include <libdladm.h> +#include <libdlbridge.h> + +#ifdef __cplusplus +extern "C" { +#endif + +struct portdata { + int vlan_id; + int port_index; + unsigned int speed; + boolean_t phys_status; /* physical layer status */ + boolean_t admin_status; /* administrative status */ + boolean_t kern_added; /* set when added to kernel bridge */ + boolean_t stp_added; /* set when added to STP machine */ + boolean_t referenced; /* used for refresh */ + boolean_t sdu_failed; /* set for non-matching max SDU */ + boolean_t admin_non_stp; /* copy of STP library config */ + boolean_t bpdu_protect; /* BPDU seen when non-STP */ + bridge_state_t state; + dlpi_handle_t dlpi; + dlpi_notifyid_t notifyid; + datalink_id_t linkid; + const char *name; + uchar_t mac_addr[ETHERADDRL]; +}; + +/* Number of reserved (internal) fdarray entries */ +#define FDOFFSET 2 + +/* main.c */ +extern int lock_engine(void); +extern void unlock_engine(void); +extern ssize_t strioctl(int, int, void *, size_t); +extern struct portdata *find_by_linkid(datalink_id_t); +extern void get_dladm_speed(struct portdata *); +extern void enable_forwarding(struct portdata *); +extern boolean_t debugging; +extern uint32_t tablemax; +extern const char *instance_name; +extern dladm_handle_t dlhandle; +extern boolean_t shutting_down; +extern struct pollfd *fdarray; + +/* door.c */ +extern void init_door(void); + +/* dlpi.c */ +extern boolean_t port_dlpi_open(const char *, struct portdata *, + datalink_class_t); + +/* rstp.c */ +extern void rstp_init(void); +extern void rstp_refresh(void); +extern void rstp_change_mac(struct portdata *, const unsigned char *); +extern boolean_t rstp_add_port(struct portdata *); + +/* events.c */ +extern void open_bridge_control(void); +extern void event_loop(void); +extern int refresh_count; +extern dladm_bridge_prot_t protect; +extern uint_t nextport; +extern struct portdata **allports; +extern int control_fd; + +#ifdef __cplusplus +} +#endif + +#endif /* _BRIDGED_GLOBAL_H */ diff --git a/usr/src/cmd/cmd-inet/usr.lib/bridged/main.c b/usr/src/cmd/cmd-inet/usr.lib/bridged/main.c new file mode 100644 index 0000000000..2e5eab4122 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.lib/bridged/main.c @@ -0,0 +1,251 @@ +/* + * 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. + */ + +/* + * bridged - bridging control daemon. + */ + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <pthread.h> +#include <string.h> +#include <signal.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <syslog.h> +#include <locale.h> +#include <stropts.h> + +#include "global.h" + +boolean_t debugging; +uint32_t tablemax; +const char *instance_name = "default"; + +struct pollfd *fdarray; + +dladm_handle_t dlhandle; + +boolean_t shutting_down; + +static pthread_t sighand; + +/* + * engine_lock is held while the main loop is busy calling librstp functions. + * Door threads take the lock to protect the library from reentrancy. + */ +static pthread_mutex_t engine_lock = PTHREAD_MUTEX_INITIALIZER; + +/* + * These wrapper functions allow the other components in the daemon to remain + * ignorant of pthreads details. + */ +int +lock_engine(void) +{ + return (pthread_mutex_lock(&engine_lock)); +} + +void +unlock_engine(void) +{ + (void) pthread_mutex_unlock(&engine_lock); +} + +/* + * Utility function for STREAMS ioctls. + */ +ssize_t +strioctl(int fd, int cmd, void *buf, size_t buflen) +{ + int retv; + struct strioctl ic; + + ic.ic_cmd = cmd; + ic.ic_timout = 0; + ic.ic_dp = buf; + ic.ic_len = buflen; + if ((retv = ioctl(fd, I_STR, &ic)) != 0) + return (retv); + else + return (ic.ic_len); +} + +static void +daemonize(void) +{ + pid_t pid; + + /* + * A little bit of magic here. By the first fork+setsid, we + * disconnect from our current controlling terminal and become + * a session group leader. By forking again without calling + * setsid again, we make certain that we are not the session + * group leader and can never reacquire a controlling terminal. + */ + if ((pid = fork()) == (pid_t)-1) { + syslog(LOG_ERR, "fork 1 failed"); + exit(EXIT_FAILURE); + } + if (pid != 0) { + (void) wait(NULL); + _exit(EXIT_SUCCESS); + } + if (setsid() == (pid_t)-1) { + syslog(LOG_ERR, "setsid"); + exit(EXIT_FAILURE); + } + if ((pid = fork()) == (pid_t)-1) { + syslog(LOG_ERR, "fork 2 failed"); + exit(EXIT_FAILURE); + } + if (pid != 0) + _exit(EXIT_SUCCESS); + (void) chdir("/"); + (void) umask(022); +} + +static void * +sighandler(void *arg) +{ + sigset_t sigset; + int sig; + int sigfd = (int)(uintptr_t)arg; + + (void) sigfillset(&sigset); + + for (;;) { + sig = sigwait(&sigset); + switch (sig) { + case SIGHUP: + (void) write(sigfd, "", 1); + break; + + default: + if (debugging) + syslog(LOG_NOTICE, "%s signal, shutting down", + strsignal(sig)); + shutting_down = B_TRUE; + break; + } + + /* if we're shutting down, exit this thread */ + if (shutting_down) + return (NULL); + } +} + +static void +init_signalhandling(void) +{ + pthread_attr_t attr; + int err; + sigset_t new; + int fildes[2]; + + if ((fdarray = malloc(FDOFFSET * sizeof (struct pollfd))) == NULL) { + syslog(LOG_ERR, "unable to allocate fdarray: %m"); + exit(EXIT_FAILURE); + } + if (pipe(fildes) != 0) { + syslog(LOG_ERR, "unable to create signal pipe: %m"); + exit(EXIT_FAILURE); + } + fdarray[0].fd = fildes[0]; + fdarray[0].events = POLLIN; + assert(control_fd != -1); + fdarray[1].fd = control_fd; + fdarray[1].events = POLLIN; + + (void) sigfillset(&new); + (void) pthread_sigmask(SIG_BLOCK, &new, NULL); + (void) pthread_attr_init(&attr); + (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + err = pthread_create(&sighand, &attr, sighandler, + (void *)(uintptr_t)fildes[1]); + if (err != 0) { + syslog(LOG_ERR, "cannot create signal handling thread: %s", + strerror(err)); + exit(EXIT_FAILURE); + } + (void) pthread_attr_destroy(&attr); +} + +int +main(int argc, char **argv) +{ + dladm_status_t status; + char buf[DLADM_STRSIZE]; + + (void) setlocale(LC_ALL, ""); + (void) textdomain(TEXT_DOMAIN); + + shutting_down = B_FALSE; + openlog("bridged", LOG_PID | LOG_NDELAY, LOG_DAEMON); + + if (argc != 2) { + syslog(LOG_ERR, "instance name is required"); + exit(EXIT_FAILURE); + } + + instance_name = argv[1]; + + if ((status = dladm_open(&dlhandle)) != DLADM_STATUS_OK) { + syslog(LOG_ERR, "%s: unable to open datalink control: %s", + instance_name, dladm_status2str(status, buf)); + exit(EXIT_FAILURE); + } + + status = dladm_bridge_get_privprop(instance_name, &debugging, + &tablemax); + if (status != DLADM_STATUS_OK) { + syslog(LOG_ERR, "%s: unable to read properties: %s", + instance_name, dladm_status2str(status, buf)); + exit(EXIT_FAILURE); + } + + /* Get the properties once so that we have the right initial values */ + rstp_init(); + + open_bridge_control(); + + daemonize(); + + init_signalhandling(); + init_door(); + + if (debugging) + syslog(LOG_INFO, "bridged started: instance %s", instance_name); + + event_loop(); + (void) pthread_cancel(sighand); + (void) pthread_join(sighand, NULL); + + return (0); +} diff --git a/usr/src/cmd/cmd-inet/usr.lib/bridged/rstp.c b/usr/src/cmd/cmd-inet/usr.lib/bridged/rstp.c new file mode 100644 index 0000000000..27032cb3b9 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.lib/bridged/rstp.c @@ -0,0 +1,564 @@ +/* + * 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. + */ + +/* + * bridged - bridging control daemon. This module provides functions related + * to the librstp (Rapid Spanning Tree Protocol) library. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdarg.h> +#include <string.h> +#include <sys/types.h> +#include <syslog.h> +#include <kstat.h> +#include <libdlpi.h> +#include <libdladm.h> +#include <libdllink.h> +#include <libdlstat.h> +#include <stp_in.h> +#include <stp_vectors.h> +#include <net/if_types.h> +#include <net/bridge.h> +#include <sys/ethernet.h> + +#include "global.h" + +/* current engine configuration; access protected by engine_lock */ +static UID_STP_CFG_T uid_cfg; + +/* + * Our implementation doesn't have per-VLAN forwarding entries, so we just + * flush by the port. If port number is zero, then flush entries. + */ +/*ARGSUSED1*/ +static int +flush_lt(int port_index, int vlan_id, LT_FLASH_TYPE_T type, char *reason) +{ + struct portdata *pd; + const char *portname; + bridge_flushfwd_t bff; + + if (port_index > nextport || port_index < 0) + return (0); + + if (port_index == 0) { + type = LT_FLASH_ONLY_THE_PORT; + portname = "all"; + bff.bff_linkid = DATALINK_INVALID_LINKID; + } else { + pd = allports[port_index - 1]; + portname = pd->name; + bff.bff_linkid = pd->linkid; + } + + if (debugging) { + syslog(LOG_DEBUG, "flush forwarding %s %s: %s", + type == LT_FLASH_ONLY_THE_PORT ? "to" : "except for", + portname, reason); + } + + bff.bff_exclude = (type == LT_FLASH_ALL_PORTS_EXCLUDE_THIS); + + /* + * If flushing fails, we can't return. The only safe thing to do is to + * tear down the bridge so that we're not harming the network. + */ + if (strioctl(control_fd, BRIOC_FLUSHFWD, &bff, sizeof (bff)) == -1) { + syslog(LOG_ERR, "cannot flush forwarding entries on %s %s: %m", + instance_name, portname); + unlock_engine(); + exit(EXIT_FAILURE); + } + + return (0); +} + +static void +get_port_mac(int port_index, unsigned char *mac) +{ + struct portdata *pd; + + if (port_index > nextport || port_index <= 0) + return; + + pd = allports[port_index - 1]; + (void) memcpy(mac, pd->mac_addr, ETHERADDRL); +} + +/* Returns speed in megabits per second */ +static unsigned long +get_port_oper_speed(unsigned int port_index) +{ + if (port_index > nextport || port_index == 0) + return (1000UL); + else + return (allports[port_index - 1]->speed); +} + +static int +get_port_link_status(int port_index) +{ + struct portdata *pd; + + if (port_index > nextport || port_index <= 0) { + return (0); + } else { + pd = allports[port_index - 1]; + return (pd->phys_status && pd->admin_status && + protect == DLADM_BRIDGE_PROT_STP && !pd->sdu_failed ? + 1 : 0); + } +} + +static int +get_duplex(int port_index) +{ + struct portdata *pd; + link_duplex_t link_duplex; + dladm_status_t status; + + if (port_index > nextport || port_index <= 0) + return (False); + + pd = allports[port_index - 1]; + status = dladm_get_single_mac_stat(dlhandle, pd->linkid, "link_duplex", + KSTAT_DATA_UINT32, &link_duplex); + + if (status == DLADM_STATUS_OK && link_duplex == LINK_DUPLEX_FULL) + return (True); + else + return (False); +} + +static const char * +bls_state(bridge_state_t bstate) +{ + switch (bstate) { + case BLS_LEARNING: + return ("learning"); + case BLS_FORWARDING: + return ("forwarding"); + default: + return ("block/listen"); + } +} + +/*ARGSUSED1*/ +static int +set_port_state(int port_index, int vlan_id, RSTP_PORT_STATE state) +{ + struct portdata *pd; + bridge_setstate_t bss; + + if (port_index > nextport || port_index <= 0) + return (1); + + pd = allports[port_index - 1]; + + if (debugging) + syslog(LOG_DEBUG, "setting port state on port %d (%s) to %d", + port_index, pd->name, state); + switch (state) { + case UID_PORT_LEARNING: + bss.bss_state = BLS_LEARNING; + break; + case UID_PORT_FORWARDING: + bss.bss_state = BLS_FORWARDING; + break; + default: + bss.bss_state = BLS_BLOCKLISTEN; + break; + } + bss.bss_linkid = pd->linkid; + if (strioctl(control_fd, BRIOC_SETSTATE, &bss, sizeof (bss)) == -1) { + syslog(LOG_ERR, "cannot set STP state on %s from %s to %s: %m", + pd->name, bls_state(pd->state), bls_state(bss.bss_state)); + /* + * If we've been unsuccessful in disabling forwarding, then the + * only safe thing to do is to make the daemon exit, so that + * the kernel will be forced to destroy the bridge state and + * terminate all forwarding. + */ + if (pd->state == BLS_FORWARDING && + bss.bss_state != BLS_FORWARDING) { + unlock_engine(); + exit(EXIT_FAILURE); + } + } else { + pd->state = bss.bss_state; + } + return (0); +} + +/* + * Our hardware doesn't actually do anything different when STP is enabled or + * disabled, so this function does nothing. It would be possible to open and + * close the DLPI stream here, if such a thing were necessary. + */ +static int +set_hardware_mode(int vlan_id, UID_STP_MODE_T mode) +{ + if (debugging) + syslog(LOG_DEBUG, "setting hardware mode on vlan %d to %d", + vlan_id, mode); + return (0); +} + +/*ARGSUSED1*/ +static int +tx_bpdu(int port_index, int vlan_id, unsigned char *bpdu, size_t bpdu_len) +{ + struct portdata *pdp; + int rc; + + if (port_index > nextport || port_index <= 0) + return (1); + + pdp = allports[port_index - 1]; + rc = dlpi_send(pdp->dlpi, NULL, 0, bpdu, bpdu_len, NULL); + if (rc == DLPI_SUCCESS) { + if (debugging) + syslog(LOG_DEBUG, "transmitted %d byte BPDU on %s", + bpdu_len, pdp->name); + return (0); + } else { + syslog(LOG_WARNING, "failed to send to %s: %s", pdp->name, + dlpi_strerror(rc)); + return (1); + } +} + +static const char * +get_port_name(int port_index) +{ + if (port_index > nextport || port_index <= 0) + return ("unknown"); + else + return (allports[port_index - 1]->name); +} + +/*ARGSUSED*/ +static int +get_init_stpm_cfg(int vlan_id, UID_STP_CFG_T *cfg) +{ + /* under engine_lock because it's a callback from the engine */ + *cfg = uid_cfg; + return (0); +} + +/*ARGSUSED*/ +static int +get_init_port_cfg(int vlan_id, int port_index, UID_STP_PORT_CFG_T *cfg) +{ + struct portdata *pdp; + uint_t propval, valcnt; + datalink_id_t linkid; + dladm_status_t status; + + if (port_index > nextport || port_index <= 0) + return (1); + + pdp = allports[port_index - 1]; + + cfg->field_mask = 0; + cfg->port_priority = DEF_PORT_PRIO; + cfg->admin_non_stp = DEF_ADMIN_NON_STP; + cfg->admin_edge = DEF_ADMIN_EDGE; + cfg->admin_port_path_cost = ADMIN_PORT_PATH_COST_AUTO; + cfg->admin_point2point = DEF_P2P; + + valcnt = 1; + linkid = pdp->linkid; + status = dladm_get_linkprop_values(dlhandle, linkid, + DLADM_PROP_VAL_PERSISTENT, "stp_priority", &propval, &valcnt); + if (status == DLADM_STATUS_OK) { + cfg->port_priority = propval; + cfg->field_mask |= PT_CFG_PRIO; + } + status = dladm_get_linkprop_values(dlhandle, linkid, + DLADM_PROP_VAL_PERSISTENT, "stp", &propval, &valcnt); + if (status == DLADM_STATUS_OK) { + cfg->admin_non_stp = !propval; + cfg->field_mask |= PT_CFG_NON_STP; + } + status = dladm_get_linkprop_values(dlhandle, linkid, + DLADM_PROP_VAL_PERSISTENT, "stp_edge", &propval, &valcnt); + if (status == DLADM_STATUS_OK) { + cfg->admin_edge = propval; + cfg->field_mask |= PT_CFG_EDGE; + } + status = dladm_get_linkprop_values(dlhandle, linkid, + DLADM_PROP_VAL_PERSISTENT, "stp_cost", &propval, &valcnt); + if (status == DLADM_STATUS_OK) { + cfg->admin_port_path_cost = propval; + cfg->field_mask |= PT_CFG_COST; + } + status = dladm_get_linkprop_values(dlhandle, linkid, + DLADM_PROP_VAL_PERSISTENT, "stp_p2p", &propval, &valcnt); + if (status == DLADM_STATUS_OK) { + cfg->admin_point2point = propval; + cfg->field_mask |= PT_CFG_P2P; + } + + /* + * mcheck is special. It is actually a command, but the 802 documents + * define it as a variable that spontaneously resets itself. We need + * to handle that behavior here. + */ + status = dladm_get_linkprop_values(dlhandle, linkid, + DLADM_PROP_VAL_PERSISTENT, "stp_mcheck", &propval, &valcnt); + if (status == DLADM_STATUS_OK && propval != 0) { + char *pval = "0"; + + cfg->field_mask |= PT_CFG_MCHECK; + (void) dladm_set_linkprop(dlhandle, linkid, "stp_mcheck", &pval, + 1, DLADM_OPT_ACTIVE|DLADM_OPT_PERSIST|DLADM_OPT_NOREFRESH); + } + + pdp->admin_non_stp = cfg->admin_non_stp; + if (!pdp->admin_non_stp) + pdp->bpdu_protect = B_FALSE; + + return (0); +} + +static void +trace(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vsyslog(LOG_DEBUG, fmt, ap); + va_end(ap); +} + +static STP_VECTORS_T stp_vectors = { + flush_lt, + get_port_mac, + get_port_oper_speed, + get_port_link_status, + get_duplex, + set_port_state, + set_hardware_mode, + tx_bpdu, + get_port_name, + get_init_stpm_cfg, + get_init_port_cfg, + trace +}; + +void +rstp_init(void) +{ + dladm_status_t status; + char buf[DLADM_STRSIZE]; + + STP_IN_init(&stp_vectors); + status = dladm_bridge_get_properties(instance_name, &uid_cfg, &protect); + if (status != DLADM_STATUS_OK) { + syslog(LOG_ERR, "%s: unable to read properties: %s", + instance_name, dladm_status2str(status, buf)); + exit(EXIT_FAILURE); + } +} + +/* + * This is called by a normal refresh operation. It gets the engine properties + * and resets. + */ +void +rstp_refresh(void) +{ + dladm_status_t status; + int rc; + char buf[DLADM_STRSIZE]; + UID_STP_CFG_T new_cfg; + dladm_bridge_prot_t new_prot; + + status = dladm_bridge_get_properties(instance_name, &new_cfg, + &new_prot); + if (status != DLADM_STATUS_OK) { + syslog(LOG_ERR, "%s: unable to refresh bridge properties: %s", + instance_name, dladm_status2str(status, buf)); + } else { + if (debugging && (protect != new_prot || + uid_cfg.stp_enabled != new_cfg.stp_enabled)) { + syslog(LOG_DEBUG, "loop protection %s->%s, STP %d->%d", + dladm_bridge_prot2str(protect), + dladm_bridge_prot2str(new_prot), + uid_cfg.stp_enabled, new_cfg.stp_enabled); + } + + /* + * The engine doesn't take kindly to parameter changes while + * running. Disable first if we must do this. + */ + if (uid_cfg.stp_enabled && + memcmp(&uid_cfg, &new_cfg, sizeof (uid_cfg)) != 0) { + syslog(LOG_DEBUG, "resetting state machine"); + uid_cfg.stp_enabled = STP_DISABLED; + rc = STP_IN_stpm_set_cfg(0, &uid_cfg); + if (rc != 0) + syslog(LOG_ERR, "STP machine reset config: %s", + STP_IN_get_error_explanation(rc)); + } + + uid_cfg = new_cfg; + protect = new_prot; + rc = STP_IN_stpm_set_cfg(0, &uid_cfg); + if (rc != 0) + syslog(LOG_ERR, "STP machine set config: %s", + STP_IN_get_error_explanation(rc)); + } +} + +/* + * This is called when a port changes its MAC address. If it's the main port, + * the one that supplies us our bridge ID, then we must choose a new ID, and to + * do that we shut the bridge down and bring it back up. + */ +void +rstp_change_mac(struct portdata *port, const unsigned char *newaddr) +{ + unsigned short prio; + unsigned char mac[ETHERADDRL]; + int rc; + char curid[ETHERADDRL * 3]; + char newmac[ETHERADDRL * 3]; + + (void) _link_ntoa(port->mac_addr, curid, ETHERADDRL, IFT_OTHER); + (void) _link_ntoa(newaddr, newmac, ETHERADDRL, IFT_OTHER); + STP_IN_get_bridge_id(port->vlan_id, &prio, mac); + if (memcmp(port->mac_addr, mac, ETHERADDRL) == 0) { + syslog(LOG_NOTICE, "bridge ID must change: ID %s on %s changed " + "to %s", curid, port->name, newmac); + uid_cfg.stp_enabled = STP_DISABLED; + if ((rc = STP_IN_stpm_set_cfg(0, &uid_cfg)) != 0) + syslog(LOG_ERR, "STP machine set config: %s", + STP_IN_get_error_explanation(rc)); + (void) memcpy(port->mac_addr, newaddr, ETHERADDRL); + uid_cfg.stp_enabled = STP_ENABLED; + if ((rc = STP_IN_stpm_set_cfg(0, &uid_cfg)) != 0) + syslog(LOG_ERR, "STP machine set config: %s", + STP_IN_get_error_explanation(rc)); + } else { + syslog(LOG_DEBUG, + "MAC address on %s changed from %s to %s", port->name, + curid, newmac); + (void) memcpy(port->mac_addr, newaddr, ETHERADDRL); + } +} + +boolean_t +rstp_add_port(struct portdata *port) +{ + int rc; + UID_STP_PORT_CFG_T portcfg; + bridge_vlanenab_t bve; + bridge_setstate_t bss; + + if (!port->stp_added && + (rc = STP_IN_port_add(port->vlan_id, port->port_index)) != 0) { + syslog(LOG_ERR, "STP add %s %d: %s", port->name, + port->port_index, STP_IN_get_error_explanation(rc)); + return (B_FALSE); + } + port->stp_added = B_TRUE; + + /* guaranteed to succeed at this point */ + (void) get_init_port_cfg(port->vlan_id, port->port_index, &portcfg); + + /* + * Restore state when reenabling STP engine, set fixed state when + * disabling. For TRILL, we don't control forwarding at all, but we + * need to turn off our controls for TRILL to do its thing. + */ + bss.bss_linkid = port->linkid; + if (protect != DLADM_BRIDGE_PROT_STP) { + bss.bss_state = port->state = BLS_BLOCKLISTEN; + } else if (portcfg.admin_non_stp) { + bss.bss_state = port->admin_status && !port->sdu_failed && + !port->bpdu_protect ? BLS_FORWARDING : BLS_BLOCKLISTEN; + } else { + bss.bss_state = port->state; + } + if (strioctl(control_fd, BRIOC_SETSTATE, &bss, sizeof (bss)) == -1) { + syslog(LOG_ERR, "cannot set STP state on %s: %m", port->name); + goto failure; + } + + rc = STP_IN_enable_port(port->port_index, + port->admin_status && port->phys_status && !port->sdu_failed && + protect == DLADM_BRIDGE_PROT_STP); + if (rc != 0) { + syslog(LOG_ERR, "STP enable %s %d: %s", port->name, + port->port_index, STP_IN_get_error_explanation(rc)); + goto failure; + } + + if (debugging) { + rc = STP_IN_dbg_set_port_trace("all", True, 0, + port->port_index); + } else { + /* return to default debug state */ + rc = STP_IN_dbg_set_port_trace("all", False, 0, + port->port_index); + if (rc == 0) + rc = STP_IN_dbg_set_port_trace("sttrans", True, 0, + port->port_index); + } + if (rc != 0) { + syslog(LOG_ERR, "STP trace %s %d: %s", port->name, + port->port_index, STP_IN_get_error_explanation(rc)); + goto failure; + } + + /* Clear out the kernel's allowed VLAN set; second walk will set */ + bve.bve_linkid = port->linkid; + bve.bve_vlan = 0; + bve.bve_onoff = B_FALSE; + if (strioctl(control_fd, BRIOC_VLANENAB, &bve, sizeof (bve)) == -1) { + syslog(LOG_ERR, "unable to disable VLANs on %s: %m", + port->name); + goto failure; + } + + if ((rc = STP_IN_port_set_cfg(0, port->port_index, &portcfg)) != 0) { + syslog(LOG_ERR, "STP port configure %s %d: %s", port->name, + port->port_index, STP_IN_get_error_explanation(rc)); + goto failure; + } + + return (B_TRUE); + +failure: + (void) STP_IN_port_remove(port->vlan_id, port->port_index); + port->stp_added = B_FALSE; + return (B_FALSE); +} diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/Makefile b/usr/src/cmd/cmd-inet/usr.sbin/snoop/Makefile index f2c16c8c21..98427af6d3 100644 --- a/usr/src/cmd/cmd-inet/usr.sbin/snoop/Makefile +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/Makefile @@ -20,27 +20,26 @@ # -# -# Copyright 2008 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" -# PROG= snoop OBJS= nfs4_xdr.o snoop.o snoop_aarp.o snoop_adsp.o snoop_aecho.o \ snoop_apple.o snoop_arp.o snoop_atp.o snoop_bparam.o \ + snoop_bpdu.o \ snoop_capture.o snoop_dhcp.o snoop_dhcpv6.o snoop_display.o \ snoop_dns.o snoop_ether.o \ snoop_filter.o snoop_http.o snoop_icmp.o snoop_igmp.o snoop_ip.o \ - snoop_ipaddr.o snoop_ipsec.o snoop_ldap.o snoop_mip.o snoop_mount.o \ + snoop_ipaddr.o snoop_ipsec.o snoop_isis.o \ + snoop_ldap.o snoop_mip.o snoop_mount.o \ snoop_nbp.o snoop_netbios.o snoop_nfs.o snoop_nfs3.o snoop_nfs4.o \ snoop_nfs_acl.o snoop_nis.o snoop_nisplus.o snoop_nlm.o snoop_ntp.o \ snoop_pf.o snoop_ospf.o snoop_ospf6.o snoop_pmap.o snoop_ppp.o \ snoop_pppoe.o snoop_rip.o snoop_rip6.o snoop_rpc.o snoop_rpcprint.o \ snoop_rpcsec.o snoop_rport.o snoop_rquota.o snoop_rstat.o snoop_rtmp.o \ snoop_sctp.o snoop_slp.o snoop_smb.o snoop_socks.o snoop_solarnet.o \ - snoop_tcp.o snoop_tftp.o snoop_udp.o snoop_zip.o + snoop_tcp.o snoop_tftp.o snoop_trill.o snoop_udp.o snoop_zip.o SRCS= $(OBJS:.o=.c) HDRS= snoop.h snoop_mip.h at.h snoop_ospf.h snoop_ospf6.h diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.h b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.h index afca9161d8..9eb27bafaa 100644 --- a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.h +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.h @@ -253,6 +253,9 @@ struct rip6; extern int interpret_rip6(int, struct rip6 *, int); extern int interpret_socks_call(int, char *, int); extern int interpret_socks_reply(int, char *, int); +extern int interpret_trill(int, struct ether_header **, char *, int *); +extern int interpret_isis(int, char *, int, boolean_t); +extern int interpret_bpdu(int, char *, int); extern void init_ldap(void); extern boolean_t arp_for_ether(char *, struct ether_addr *); extern char *ether_ouiname(uint32_t); diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_bpdu.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_bpdu.c new file mode 100644 index 0000000000..89aa7750d3 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_bpdu.c @@ -0,0 +1,95 @@ +/* + * 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 <sys/socket.h> +#include <sys/ethernet.h> + +#include <snoop.h> + +struct conf_bpdu { + uchar_t cb_protid[2]; /* Protocol Identifier */ + uchar_t cb_protvers; /* Protocol Version Identifier */ + uchar_t cb_type; /* BPDU Type */ + uchar_t cb_flags; /* BPDU Flags */ + uchar_t cb_rootid[8]; /* Root Identifier */ + uchar_t cb_rootcost[4]; /* Root Path Cost */ + uchar_t cb_bridgeid[8]; /* Bridge Identifier */ + uchar_t cb_portid[2]; /* Port Identifier */ + uchar_t cb_messageage[2]; /* Message Age */ + uchar_t cb_maxage[2]; /* Max Age */ + uchar_t cb_hello[2]; /* Hello Time */ + uchar_t cb_fwddelay[2]; /* Forward Delay */ +}; + +#define BPDU_TYPE_CONF 0 +#define BPDU_TYPE_RCONF 2 +#define BPDU_TYPE_TCNOTIF 0x80 + +int +interpret_bpdu(int flags, char *data, int dlen) +{ + struct conf_bpdu *cb; + const char *pdutype; + + if (dlen < 4) { + (void) snprintf(get_sum_line(), MAXLINE, + "BPDU (short packet)"); + return (0); + } + + cb = (struct conf_bpdu *)data; + + if (flags & F_SUM) { + (void) snprintf(get_sum_line(), MAXLINE, + "Bridge PDU T:%d L:%d", cb->cb_type, dlen); + } + + if (flags & F_DTAIL) { + show_header("Bridge-PDU: ", + "Bridge PDU Frame", dlen); + show_space(); + switch (cb->cb_type) { + case BPDU_TYPE_CONF: + pdutype = "Configuration"; + break; + case BPDU_TYPE_RCONF: + pdutype = "Rapid Configuration"; + break; + case BPDU_TYPE_TCNOTIF: + pdutype = "TC Notification"; + break; + default: + pdutype = "?"; + break; + } + (void) snprintf(get_line(0, 0), get_line_remain(), + "PDU type = %d (%s)", cb->cb_type, pdutype); + show_trailer(); + } + return (0); +} diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ether.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ether.c index 0fca6702f2..d2e9b8fe42 100644 --- a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ether.c +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ether.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. */ @@ -46,6 +46,7 @@ #include <limits.h> #include <inet/ip.h> #include <inet/ip6.h> +#include <net/trill.h> #include "at.h" #include "snoop.h" @@ -106,6 +107,7 @@ char *print_smtclass(); struct ether_addr ether_broadcast = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; static char *data; /* current data buffer */ static int datalen; /* current data buffer length */ +static const struct ether_addr all_isis_rbridges = ALL_ISIS_RBRIDGES; uint_t interpret_ether(flags, e, elen, origlen) @@ -113,14 +115,15 @@ interpret_ether(flags, e, elen, origlen) struct ether_header *e; int elen, origlen; { - char *off; + uchar_t *off, *ieeestart; int len; int ieee8023 = 0; extern char *dst_name; int ethertype; - boolean_t data_copied = B_FALSE; struct ether_vlan_extinfo *evx = NULL; int blen = MAX(origlen, ETHERMTU); + boolean_t trillpkt = B_FALSE; + uint16_t tci = 0; if (data != NULL && datalen != 0 && datalen < blen) { free(data); @@ -133,6 +136,7 @@ interpret_ether(flags, e, elen, origlen) pr_err("Warning: malloc failure"); datalen = blen; } +inner_pkt: if (origlen < 14) { if (flags & F_SUM) (void) sprintf(get_sum_line(), @@ -160,19 +164,7 @@ interpret_ether(flags, e, elen, origlen) * the rest of the packet in order to align it. */ len = elen - sizeof (struct ether_header); - off = (char *)(e + 1); - if (ethertype <= 1514) { - /* - * Fake out the IEEE 802.3 packets. - * Should be DSAP=0xAA, SSAP=0xAA, control=0x03 - * then three padding bytes of zero, - * followed by a normal ethernet-type packet. - */ - ieee8023 = ntohs(e->ether_type); - ethertype = ntohs(*(ushort_t *)(off + 6)); - off += 8; - len -= 8; - } + off = (uchar_t *)(e + 1); if (ethertype == ETHERTYPE_VLAN) { if (origlen < sizeof (struct ether_vlan_header)) { @@ -195,16 +187,27 @@ interpret_ether(flags, e, elen, origlen) len -= sizeof (struct ether_vlan_extinfo); ethertype = ntohs(evx->ether_type); + tci = ntohs(evx->ether_tci); } - /* - * We cannot trust the length field in the header to be correct. - * But we should continue to process the packet. Then user can - * notice something funny in the header. - */ - if (len > 0 && (off + len <= (char *)e + elen)) { - (void) memcpy(data, off, len); - data_copied = B_TRUE; + if (ethertype <= 1514) { + /* + * Fake out the IEEE 802.3 packets. + * Should be DSAP=0xAA, SSAP=0xAA, control=0x03 + * then three padding bytes of zero (OUI), + * followed by a normal ethernet-type packet. + */ + ieee8023 = ethertype; + ieeestart = off; + if (off[0] == 0xAA && off[1] == 0xAA) { + ethertype = ntohs(*(ushort_t *)(off + 6)); + off += 8; + len -= 8; + } else { + ethertype = 0; + off += 3; + len -= 3; + } } if (flags & F_SUM) { @@ -216,58 +219,105 @@ interpret_ether(flags, e, elen, origlen) */ set_vlan_id(0); if (evx == NULL) { - (void) sprintf(get_sum_line(), - "ETHER Type=%04X (%s), size=%d bytes", - ethertype, print_ethertype(ethertype), - origlen); + if (ethertype == 0 && ieee8023 > 0) { + (void) sprintf(get_sum_line(), + "ETHER 802.3 SSAP %02X DSAP %02X, " + "size=%d bytes", ieeestart[0], ieeestart[1], + origlen); + } else { + (void) sprintf(get_sum_line(), + "ETHER Type=%04X (%s), size=%d bytes", + ethertype, print_ethertype(ethertype), + origlen); + } } else { - (void) sprintf(get_sum_line(), - "ETHER Type=%04X (%s), VLAN ID=%hu, size=%d " - "bytes", ethertype, print_ethertype(ethertype), - VLAN_ID(ntohs(evx->ether_tci)), origlen); + if (ethertype == 0 && ieee8023 > 0) { + (void) sprintf(get_sum_line(), + "ETHER 802.3 SSAP %02X DSAP %02X, " + "VLAN ID=%hu, size=%d bytes", ieeestart[0], + ieeestart[1], VLAN_ID(tci), origlen); + } else { + (void) sprintf(get_sum_line(), + "ETHER Type=%04X (%s), VLAN ID=%hu, " + "size=%d bytes", ethertype, + print_ethertype(ethertype), VLAN_ID(tci), + origlen); + } if (!(flags & F_ALLSUM)) - set_vlan_id(VLAN_ID(ntohs(evx->ether_tci))); + set_vlan_id(VLAN_ID(tci)); } } if (flags & F_DTAIL) { - show_header("ETHER: ", "Ether Header", elen); - show_space(); - (void) sprintf(get_line(0, 0), - "Packet %d arrived at %d:%02d:%d.%05d", - pi_frame, - pi_time_hour, pi_time_min, pi_time_sec, - pi_time_usec / 10); - (void) sprintf(get_line(0, 0), - "Packet size = %d bytes", - elen, elen); - (void) sprintf(get_line(0, 6), - "Destination = %s, %s", - printether(&e->ether_dhost), - print_etherinfo(&e->ether_dhost)); - (void) sprintf(get_line(6, 6), - "Source = %s, %s", - printether(&e->ether_shost), - print_etherinfo(&e->ether_shost)); - if (ieee8023 > 0) { - (void) sprintf(get_line(12, 2), - "IEEE 802.3 length = %d bytes", ieee8023); - } - if (evx != NULL) { - (void) sprintf(get_line(0, 0), - "VLAN ID = %hu", VLAN_ID(ntohs(evx->ether_tci))); - (void) sprintf(get_line(0, 0), - "VLAN Priority = %hu", VLAN_PRI(ntohs(evx->ether_tci))); - } - (void) sprintf(get_line(12, 2), - "Ethertype = %04X (%s)", - ethertype, print_ethertype(ethertype)); - show_space(); + show_header("ETHER: ", "Ether Header", elen); + show_space(); + if (!trillpkt) { + (void) sprintf(get_line(0, 0), + "Packet %d arrived at %d:%02d:%d.%05d", + pi_frame, + pi_time_hour, pi_time_min, pi_time_sec, + pi_time_usec / 10); + (void) sprintf(get_line(0, 0), + "Packet size = %d bytes", + elen, elen); + } + (void) sprintf(get_line(0, 6), + "Destination = %s, %s", + printether(&e->ether_dhost), + print_etherinfo(&e->ether_dhost)); + (void) sprintf(get_line(6, 6), + "Source = %s, %s", + printether(&e->ether_shost), + print_etherinfo(&e->ether_shost)); + if (evx != NULL) { + (void) sprintf(get_line(0, 0), + "VLAN ID = %hu", VLAN_ID(tci)); + (void) sprintf(get_line(0, 0), + "VLAN Priority = %hu", VLAN_PRI(tci)); + } + if (ieee8023 > 0) { + (void) sprintf(get_line(12, 2), + "IEEE 802.3 length = %d bytes", ieee8023); + /* Print LLC only for non-TCP/IP packets */ + if (ethertype == 0) { + (void) snprintf(get_line(0, 0), + get_line_remain(), + "SSAP = %02X, DSAP = %02X, CTRL = %02X", + ieeestart[0], ieeestart[1], ieeestart[2]); + } + } + if (ethertype != 0 || ieee8023 == 0) + (void) sprintf(get_line(12, 2), + "Ethertype = %04X (%s)", + ethertype, print_ethertype(ethertype)); + show_space(); } - /* Go to the next protocol layer only if data have been copied. */ - if (data_copied) { + /* + * We cannot trust the length field in the header to be correct. + * But we should continue to process the packet. Then user can + * notice something funny in the header. + * Go to the next protocol layer only if data have been + * copied. + */ + if (len > 0 && (off + len <= (uchar_t *)e + elen)) { + (void) memmove(data, off, len); + + if (!trillpkt && ethertype == ETHERTYPE_TRILL) { + ethertype = interpret_trill(flags, &e, data, &len); + /* Decode inner Ethernet frame */ + if (ethertype != 0) { + evx = NULL; + trillpkt = B_TRUE; + (void) memmove(data, e, len); + e = (struct ether_header *)data; + origlen = len; + elen = len; + goto inner_pkt; + } + } + switch (ethertype) { case ETHERTYPE_IP: (void) interpret_ip(flags, (struct ip *)data, len); @@ -290,7 +340,19 @@ interpret_ether(flags, e, elen, origlen) case ETHERTYPE_AT: interpret_at(flags, (struct ddp_hdr *)data, len); break; - default: + case 0: + if (ieee8023 == 0) + break; + switch (ieeestart[0]) { + case 0xFE: + interpret_isis(flags, data, len, + memcmp(&e->ether_dhost, &all_isis_rbridges, + sizeof (struct ether_addr)) == 0); + break; + case 0x42: + interpret_bpdu(flags, data, len); + break; + } break; } } @@ -343,6 +405,7 @@ ETHERTYPE_REVARP, "RARP", ETHERTYPE_IPV6, "IPv6", ETHERTYPE_PPPOED, "PPPoE Discovery", ETHERTYPE_PPPOES, "PPPoE Session", +ETHERTYPE_TRILL, "TRILL", /* end of popular entries */ ETHERTYPE_PUP, "Xerox PUP", 0x0201, "Xerox PUP", @@ -1260,6 +1323,7 @@ struct block_type { 0x0000A7, "Network Computing Devices (NCD X-terminal)", 0x08005A, "IBM", 0x0000AC, "Apollo", +0x0180C2, "Standard MAC Group Address", /* end of popular entries */ 0x000002, "BBN", 0x000010, "Sytek", @@ -1437,16 +1501,13 @@ print_etherinfo(eaddr) if (memcmp(eaddr, ðer_broadcast, sizeof (struct ether_addr)) == 0) return ("(broadcast)"); - if (eaddr->ether_addr_octet[0] & 1) - return ("(multicast)"); - addr = ntohl(addr); /* make it right for little-endians */ ename = ether_ouiname(addr); if (ename != NULL) return (ename); else - return (""); + return ((eaddr->ether_addr_octet[0] & 1) ? "(multicast)" : ""); } static uchar_t endianswap[] = { diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_isis.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_isis.c new file mode 100644 index 0000000000..be7cae5223 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_isis.c @@ -0,0 +1,99 @@ +/* + * 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 <sys/socket.h> +#include <sys/ethernet.h> +#include <sys/vlan.h> +#include <net/trill.h> + +#include <snoop.h> + +#define PDUTYPE_OFFSET 4 +#define PDUTYPE_HELLO1 15 +#define PDUTYPE_HELLO2 16 +#define PDUTYPE_HELLOP2P 17 +#define PDUTYPE_LSP1 18 +#define PDUTYPE_LSP2 20 +#define PDUTYPE_CSN1 24 +#define PDUTYPE_CSN2 25 +#define PDUTYPE_PSN1 26 +#define PDUTYPE_PSN2 27 + +int +interpret_isis(int flags, char *data, int dlen, boolean_t istrill) +{ + uint8_t pdutypenum; + char *pdutype; + + pdutypenum = *(data+ PDUTYPE_OFFSET); + switch (pdutypenum) { + case PDUTYPE_HELLO1: + case PDUTYPE_HELLO2: + pdutype = "Hello"; + break; + case PDUTYPE_HELLOP2P: + pdutype = "P2P Hello"; + break; + case PDUTYPE_LSP1: + case PDUTYPE_LSP2: + pdutype = "Link State"; + break; + case PDUTYPE_CSN1: + case PDUTYPE_CSN2: + pdutype = "CSN"; + break; + case PDUTYPE_PSN1: + case PDUTYPE_PSN2: + pdutype = "PSN"; + break; + default: + pdutype = "Unknown"; + break; + } + + if (flags & F_SUM) { + (void) snprintf(get_sum_line(), MAXLINE, + "%s %s L:%d", istrill ? "Core TRILL IS-IS" : "IS-IS", + pdutype, dlen); + } + + if (flags & F_DTAIL) { + if (istrill) { + show_header("TRILL-IS-IS: ", + "Core TRILL IS-IS Frame", dlen); + } else { + show_header("IS-IS: ", + "IS-IS Frame", dlen); + } + show_space(); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Frame type = %02X (%s)", pdutypenum, pdutype); + show_trailer(); + } + return (0); +} diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_trill.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_trill.c new file mode 100644 index 0000000000..51876bcce8 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_trill.c @@ -0,0 +1,117 @@ +/* + * 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 <sys/socket.h> +#include <sys/ethernet.h> +#include <sys/vlan.h> +#include <net/trill.h> + +#include <snoop.h> + +int +interpret_trill(int flags, struct ether_header **e, char *data, int *alen) +{ + trill_header_t *trillhdr; + struct ether_header *inner_ethhdr; + struct ether_vlan_header *inner_ethvlanhdr; + uint16_t ethertype; + int dlen = *alen; + size_t optslen; + size_t trillhdrlen; + + if (dlen < sizeof (trill_header_t)) { + (void) snprintf(get_sum_line(), MAXLINE, + "TRILL (short packet)"); + return (0); + } + + trillhdr = (trill_header_t *)data; + optslen = GET_TRILL_OPTS_LEN(trillhdr) * sizeof (uint32_t); + + if (flags & F_DTAIL) { + show_header("TRILL: ", "TRILL Data Frame", dlen); + show_space(); + + (void) snprintf(get_line(0, 0), get_line_remain(), + "Egress nickname = %d", + ntohs(trillhdr->th_egressnick)); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Ingress nickname = %d", + ntohs(trillhdr->th_ingressnick)); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Hop count = %d", trillhdr->th_hopcount); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Multi-destination = %d", trillhdr->th_multidest); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Options Len = %d bytes", optslen); + show_trailer(); + } + + trillhdrlen = sizeof (trill_header_t) + optslen; + + if (dlen < trillhdrlen) { + (void) snprintf(get_sum_line(), MAXLINE, + "TRILL (options truncated)"); + return (0); + } + + dlen -= trillhdrlen; + + if (dlen < sizeof (struct ether_header)) { + (void) snprintf(get_sum_line(), MAXLINE, + "TRILL (missing required inner MAC)"); + return (0); + } + + inner_ethhdr = (struct ether_header *)(data + trillhdrlen); + if (inner_ethhdr->ether_type != htons(ETHERTYPE_VLAN)) { + (void) snprintf(get_sum_line(), MAXLINE, + "TRILL (inner VLAN missing; ethertype %X)", + ntohs(inner_ethhdr->ether_type)); + return (0); + } + + inner_ethvlanhdr = (struct ether_vlan_header *)inner_ethhdr; + ethertype = ntohs(inner_ethvlanhdr->ether_type); + + if (flags & F_SUM) { + (void) snprintf(get_sum_line(), MAXLINE, + "TRILL D:%d S:%d HC:%d M:%d O:%d L:%d VLAN:%d %s", + ntohs(trillhdr->th_egressnick), + ntohs(trillhdr->th_ingressnick), + trillhdr->th_hopcount, + trillhdr->th_multidest, + optslen, + dlen, VLAN_ID(inner_ethvlanhdr->ether_tci), + print_ethertype(ethertype)); + } + + *alen = dlen; + *e = inner_ethhdr; + return (ethertype); +} diff --git a/usr/src/cmd/dladm/Makefile b/usr/src/cmd/dladm/Makefile index 230e856861..f336828605 100644 --- a/usr/src/cmd/dladm/Makefile +++ b/usr/src/cmd/dladm/Makefile @@ -22,7 +22,6 @@ # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# PROG= dladm CFGFILES= secobj.conf @@ -36,9 +35,16 @@ include ../Makefile.cmd XGETFLAGS += -a -x $(PROG).xcl LDLIBS += -L$(ROOT)/lib -lsocket LDLIBS += -ldladm -ldlpi -lkstat -lsecdb -lbsm -linetutil -ldevinfo +LDLIBS += $(ZLAZYLOAD) -lrstp $(ZNOLAZYLOAD) + +# For headers from librstp. +LINTFLAGS += -erroff=E_TRAILING_COMMA_IN_ENUM $(ROOTCFGDIR)/secobj.conf := FILEMODE= 600 +lint := ZLAZYLOAD= +lint := ZNOLAZYLOAD= + .KEEP_STATE: all: $(ROOTFS_PROG) diff --git a/usr/src/cmd/dladm/dladm.c b/usr/src/cmd/dladm/dladm.c index 80a62f328f..a2cef21b09 100644 --- a/usr/src/cmd/dladm/dladm.c +++ b/usr/src/cmd/dladm/dladm.c @@ -25,6 +25,7 @@ #include <stdio.h> #include <ctype.h> +#include <dlfcn.h> #include <locale.h> #include <signal.h> #include <stdarg.h> @@ -55,6 +56,7 @@ #include <libdlvnic.h> #include <libdlether.h> #include <libdlsim.h> +#include <libdlbridge.h> #include <libinetutil.h> #include <bsm/adt.h> #include <bsm/adt_event.h> @@ -66,6 +68,7 @@ #include <arpa/inet.h> #include <net/if_types.h> #include <stddef.h> +#include <stp_in.h> #include <ofmt.h> #define MAXPORT 256 @@ -84,6 +87,27 @@ #define WIFI_CMD_SHOW 0x00000002 #define WIFI_CMD_ALL (WIFI_CMD_SCAN | WIFI_CMD_SHOW) +/* No larger than pktsum_t */ +typedef struct brsum_s { + uint64_t drops; + uint64_t forward_dir; + uint64_t forward_mb; + uint64_t forward_unk; + uint64_t recv; + uint64_t sent; +} brsum_t; + +/* No larger than pktsum_t */ +typedef struct brlsum_s { + uint32_t cfgbpdu; + uint32_t tcnbpdu; + uint32_t rstpbpdu; + uint32_t txbpdu; + uint64_t drops; + uint64_t recv; + uint64_t xmit; +} brlsum_t; + typedef struct show_state { boolean_t ls_firstonly; boolean_t ls_donefirst; @@ -166,6 +190,8 @@ static cmdfunc_t do_create_etherstub, do_delete_etherstub, do_show_etherstub; static cmdfunc_t do_create_simnet, do_modify_simnet; static cmdfunc_t do_delete_simnet, do_show_simnet, do_up_simnet; static cmdfunc_t do_show_usage; +static cmdfunc_t do_create_bridge, do_modify_bridge, do_delete_bridge; +static cmdfunc_t do_add_bridge, do_remove_bridge, do_show_bridge; static void do_up_vnic_common(int, char **, const char *, boolean_t); @@ -290,6 +316,33 @@ static cmd_t cmds[] = { { "delete-simnet", do_delete_simnet, NULL }, { "show-simnet", do_show_simnet, NULL }, { "up-simnet", do_up_simnet, NULL }, + { "create-bridge", do_create_bridge, + " create-bridge [-R <root-dir>] [-P <protect>] " + "[-p <priority>]\n" + "\t\t [-m <max-age>] [-h <hello-time>] [-d <forward-delay>]\n" + "\t\t [-f <force-protocol>] [-l <link>]... <bridge>" }, + { "modify-bridge", do_modify_bridge, + " modify-bridge [-R <root-dir>] [-P <protect>] " + "[-p <priority>]\n" + "\t\t [-m <max-age>] [-h <hello-time>] [-d <forward-delay>]\n" + "\t\t [-f <force-protocol>] <bridge>" }, + { "delete-bridge", do_delete_bridge, + " delete-bridge [-R <root-dir>] <bridge>" }, + { "add-bridge", do_add_bridge, + " add-bridge [-R <root-dir>] -l <link> [-l <link>]... " + "<bridge>" }, + { "remove-bridge", do_remove_bridge, + " remove-bridge [-R <root-dir>] -l <link> [-l <link>]... " + "<bridge>" }, + { "show-bridge", do_show_bridge, + " show-bridge [-p] [-o <field>,...] [-s [-i <interval>]] " + "[<bridge>]\n" + " show-bridge -l [-p] [-o <field>,...] [-s [-i <interval>]]" + " <bridge>\n" + " show-bridge -f [-p] [-o <field>,...] [-s [-i <interval>]]" + " <bridge>\n" + " show-bridge -t [-p] [-o <field>,...] [-s [-i <interval>]]" + " <bridge>\n" }, { "show-usage", do_show_usage, " show-usage [-a] [-d | -F <format>] " "[-s <DD/MM/YYYY,HH:MM:SS>]\n" @@ -399,6 +452,30 @@ static const struct option simnet_lopts[] = { { 0, 0, 0, 0 } }; +static const struct option bridge_lopts[] = { + { "protect", required_argument, 0, 'P' }, + { "root-dir", required_argument, 0, 'R' }, + { "forward-delay", required_argument, 0, 'd' }, + { "force-protocol", required_argument, 0, 'f' }, + { "hello-time", required_argument, 0, 'h' }, + { "link", required_argument, 0, 'l' }, + { "max-age", required_argument, 0, 'm' }, + { "priority", required_argument, 0, 'p' }, + { NULL, NULL, 0, 0 } +}; + +static const struct option bridge_show_lopts[] = { + { "forwarding", no_argument, 0, 'f' }, + { "interval", required_argument, 0, 'i' }, + { "link", no_argument, 0, 'l' }, + { "output", required_argument, 0, 'o' }, + { "parsable", no_argument, 0, 'p' }, + { "parseable", no_argument, 0, 'p' }, + { "statistics", no_argument, 0, 's' }, + { "trill", no_argument, 0, 't' }, + { 0, 0, 0, 0 } +}; + /* * structures for 'dladm show-ether' */ @@ -481,6 +558,7 @@ typedef struct link_fields_buf_s { char link_class[DLADM_STRSIZE]; char link_mtu[11]; char link_state[DLADM_STRSIZE]; + char link_bridge[MAXLINKNAMELEN]; char link_over[MAXLINKNAMELEN]; char link_phys_state[DLADM_STRSIZE]; char link_phys_media[DLADM_STRSIZE]; @@ -504,6 +582,8 @@ static ofmt_field_t link_fields[] = { offsetof(link_fields_buf_t, link_mtu), print_default_cb}, { "STATE", 9, offsetof(link_fields_buf_t, link_state), print_default_cb}, +{ "BRIDGE", 11, + offsetof(link_fields_buf_t, link_bridge), print_default_cb}, { "OVER", DLPI_LINKNAME_MAX, offsetof(link_fields_buf_t, link_over), print_default_cb}, { NULL, 0, 0, NULL}} @@ -927,6 +1007,235 @@ static ofmt_field_t usage_l_fields[] = { { NULL, 0, 0, NULL}} ; +/* + * structures for 'dladm show-bridge'. These are based on sections 14.8.1.1.3 + * and 14.8.1.2.2 of IEEE 802.1D-2004. + */ +typedef struct bridge_fields_buf_s { + char bridge_name[MAXLINKNAMELEN]; /* 14.4.1.2.3(b) */ + char bridge_protect[7]; /* stp or trill */ + char bridge_address[24]; /* 17.18.3, 7.12.5, 14.4.1.2.3(a) */ + char bridge_priority[7]; /* 17.18.3 9.2.5 - only upper 4 bits */ + char bridge_bmaxage[7]; /* 17.18.4 configured */ + char bridge_bhellotime[7]; /* 17.18.4 configured */ + char bridge_bfwddelay[7]; /* 17.18.4 configured */ + char bridge_forceproto[3]; /* 17.13.4 configured */ + char bridge_tctime[12]; /* 14.8.1.1.3(b) */ + char bridge_tccount[12]; /* 17.17.8 */ + char bridge_tchange[12]; /* 17.17.8 */ + char bridge_desroot[24]; /* 17.18.6 priority "/" MAC */ + char bridge_rootcost[12]; /* 17.18.6 */ + char bridge_rootport[12]; /* 17.18.6 */ + char bridge_maxage[7]; /* 17.18.7 for root */ + char bridge_hellotime[7]; /* 17.13.6 for root */ + char bridge_fwddelay[7]; /* 17.13.5 for root */ + char bridge_holdtime[12]; /* 17.13.12 for root */ +} bridge_fields_buf_t; + +static ofmt_field_t bridge_fields[] = { +/* name, field width, offset, callback */ +{ "BRIDGE", 12, + offsetof(bridge_fields_buf_t, bridge_name), print_default_cb }, +{ "PROTECT", 8, + offsetof(bridge_fields_buf_t, bridge_protect), print_default_cb }, +{ "ADDRESS", 19, + offsetof(bridge_fields_buf_t, bridge_address), print_default_cb }, +{ "PRIORITY", 9, + offsetof(bridge_fields_buf_t, bridge_priority), print_default_cb }, +{ "BMAXAGE", 8, + offsetof(bridge_fields_buf_t, bridge_bmaxage), print_default_cb }, +{ "BHELLOTIME", 11, + offsetof(bridge_fields_buf_t, bridge_bhellotime), print_default_cb }, +{ "BFWDDELAY", 10, + offsetof(bridge_fields_buf_t, bridge_bfwddelay), print_default_cb }, +{ "FORCEPROTO", 11, + offsetof(bridge_fields_buf_t, bridge_forceproto), print_default_cb }, +{ "TCTIME", 10, + offsetof(bridge_fields_buf_t, bridge_tctime), print_default_cb }, +{ "TCCOUNT", 10, + offsetof(bridge_fields_buf_t, bridge_tccount), print_default_cb }, +{ "TCHANGE", 10, + offsetof(bridge_fields_buf_t, bridge_tchange), print_default_cb }, +{ "DESROOT", 23, + offsetof(bridge_fields_buf_t, bridge_desroot), print_default_cb }, +{ "ROOTCOST", 11, + offsetof(bridge_fields_buf_t, bridge_rootcost), print_default_cb }, +{ "ROOTPORT", 11, + offsetof(bridge_fields_buf_t, bridge_rootport), print_default_cb }, +{ "MAXAGE", 8, + offsetof(bridge_fields_buf_t, bridge_maxage), print_default_cb }, +{ "HELLOTIME", 10, + offsetof(bridge_fields_buf_t, bridge_hellotime), print_default_cb }, +{ "FWDDELAY", 9, + offsetof(bridge_fields_buf_t, bridge_fwddelay), print_default_cb }, +{ "HOLDTIME", 9, + offsetof(bridge_fields_buf_t, bridge_holdtime), print_default_cb }, +{ NULL, 0, 0, NULL}}; + +/* + * structures for 'dladm show-bridge -l'. These are based on 14.4.1.2.3 and + * 14.8.2.1.3 of IEEE 802.1D-2004. + */ +typedef struct bridge_link_fields_buf_s { + char bridgel_link[MAXLINKNAMELEN]; + char bridgel_index[7]; /* 14.4.1.2.3(d1) */ + char bridgel_state[11]; /* 14.8.2.1.3(b) */ + char bridgel_uptime[7]; /* 14.8.2.1.3(a) */ + char bridgel_opercost[7] /* 14.8.2.1.3(d) */; + char bridgel_operp2p[4]; /* 14.8.2.1.3(p) */ + char bridgel_operedge[4]; /* 14.8.2.1.3(k) */ + char bridgel_desroot[23]; /* 14.8.2.1.3(e) */ + char bridgel_descost[12]; /* 14.8.2.1.3(f) */ + char bridgel_desbridge[23]; /* 14.8.2.1.3(g) */ + char bridgel_desport[7]; /* 14.8.2.1.3(h) */ + char bridgel_tcack[4]; /* 14.8.2.1.3(i) */ +} bridge_link_fields_buf_t; + +static ofmt_field_t bridge_link_fields[] = { +/* name, field width, offset, callback */ +{ "LINK", 12, + offsetof(bridge_link_fields_buf_t, bridgel_link), print_default_cb }, +{ "INDEX", 8, + offsetof(bridge_link_fields_buf_t, bridgel_index), print_default_cb }, +{ "STATE", 12, + offsetof(bridge_link_fields_buf_t, bridgel_state), print_default_cb }, +{ "UPTIME", 8, + offsetof(bridge_link_fields_buf_t, bridgel_uptime), print_default_cb }, +{ "OPERCOST", 9, + offsetof(bridge_link_fields_buf_t, bridgel_opercost), print_default_cb }, +{ "OPERP2P", 8, + offsetof(bridge_link_fields_buf_t, bridgel_operp2p), print_default_cb }, +{ "OPEREDGE", 9, + offsetof(bridge_link_fields_buf_t, bridgel_operedge), print_default_cb }, +{ "DESROOT", 22, + offsetof(bridge_link_fields_buf_t, bridgel_desroot), print_default_cb }, +{ "DESCOST", 11, + offsetof(bridge_link_fields_buf_t, bridgel_descost), print_default_cb }, +{ "DESBRIDGE", 22, + offsetof(bridge_link_fields_buf_t, bridgel_desbridge), print_default_cb }, +{ "DESPORT", 8, + offsetof(bridge_link_fields_buf_t, bridgel_desport), print_default_cb }, +{ "TCACK", 6, + offsetof(bridge_link_fields_buf_t, bridgel_tcack), print_default_cb }, +{ NULL, 0, 0, NULL}}; + +/* + * structures for 'dladm show-bridge -s'. These are not based on IEEE + * 802.1D-2004. + */ +#define ULONG_DIG (((sizeof (ulong_t) * NBBY) * 3 / 10) + 1) +#define UINT64_DIG (((sizeof (uint64_t) * NBBY) * 3 / 10) + 1) +typedef struct bridge_statfields_buf_s { + char bridges_name[MAXLINKNAMELEN]; + char bridges_drops[UINT64_DIG]; + char bridges_forwards[UINT64_DIG]; + char bridges_mbcast[UINT64_DIG]; + char bridges_unknown[UINT64_DIG]; + char bridges_recv[UINT64_DIG]; + char bridges_sent[UINT64_DIG]; +} bridge_statfields_buf_t; + +static ofmt_field_t bridge_statfields[] = { +/* name, field width, offset, callback */ +{ "BRIDGE", 12, + offsetof(bridge_statfields_buf_t, bridges_name), print_default_cb }, +{ "DROPS", 12, + offsetof(bridge_statfields_buf_t, bridges_drops), print_default_cb }, +{ "FORWARDS", 12, + offsetof(bridge_statfields_buf_t, bridges_forwards), print_default_cb }, +{ "MBCAST", 12, + offsetof(bridge_statfields_buf_t, bridges_mbcast), print_default_cb }, +{ "UNKNOWN", 12, + offsetof(bridge_statfields_buf_t, bridges_unknown), print_default_cb }, +{ "RECV", 12, + offsetof(bridge_statfields_buf_t, bridges_recv), print_default_cb }, +{ "SENT", 12, + offsetof(bridge_statfields_buf_t, bridges_sent), print_default_cb }, +{ NULL, 0, 0, NULL}}; + +/* + * structures for 'dladm show-bridge -s -l'. These are based in part on + * section 14.6.1.1.3 of IEEE 802.1D-2004. + */ +typedef struct bridge_link_statfields_buf_s { + char bridgels_link[MAXLINKNAMELEN]; + char bridgels_cfgbpdu[ULONG_DIG]; + char bridgels_tcnbpdu[ULONG_DIG]; + char bridgels_rstpbpdu[ULONG_DIG]; + char bridgels_txbpdu[ULONG_DIG]; + char bridgels_drops[UINT64_DIG]; /* 14.6.1.1.3(d) */ + char bridgels_recv[UINT64_DIG]; /* 14.6.1.1.3(a) */ + char bridgels_xmit[UINT64_DIG]; /* 14.6.1.1.3(c) */ +} bridge_link_statfields_buf_t; + +static ofmt_field_t bridge_link_statfields[] = { +/* name, field width, offset, callback */ +{ "LINK", 12, + offsetof(bridge_link_statfields_buf_t, bridgels_link), print_default_cb }, +{ "CFGBPDU", 9, + offsetof(bridge_link_statfields_buf_t, bridgels_cfgbpdu), + print_default_cb }, +{ "TCNBPDU", 9, + offsetof(bridge_link_statfields_buf_t, bridgels_tcnbpdu), + print_default_cb }, +{ "RSTPBPDU", 9, + offsetof(bridge_link_statfields_buf_t, bridgels_rstpbpdu), + print_default_cb }, +{ "TXBPDU", 9, + offsetof(bridge_link_statfields_buf_t, bridgels_txbpdu), print_default_cb }, +{ "DROPS", 9, + offsetof(bridge_link_statfields_buf_t, bridgels_drops), print_default_cb }, +{ "RECV", 9, + offsetof(bridge_link_statfields_buf_t, bridgels_recv), print_default_cb }, +{ "XMIT", 9, + offsetof(bridge_link_statfields_buf_t, bridgels_xmit), print_default_cb }, +{ NULL, 0, 0, NULL}}; + +/* + * structures for 'dladm show-bridge -f'. These are based in part on + * section 14.7.6.3.3 of IEEE 802.1D-2004. + */ +typedef struct bridge_fwd_fields_buf_s { + char bridgef_dest[18]; /* 14.7.6.3.3(a) */ + char bridgef_age[8]; + char bridgef_flags[6]; + char bridgef_output[MAXLINKNAMELEN]; /* 14.7.6.3.3(c) */ +} bridge_fwd_fields_buf_t; + +static ofmt_field_t bridge_fwd_fields[] = { +/* name, field width, offset, callback */ +{ "DEST", 17, + offsetof(bridge_fwd_fields_buf_t, bridgef_dest), print_default_cb }, +{ "AGE", 7, + offsetof(bridge_fwd_fields_buf_t, bridgef_age), print_default_cb }, +{ "FLAGS", 6, + offsetof(bridge_fwd_fields_buf_t, bridgef_flags), print_default_cb }, +{ "OUTPUT", 12, + offsetof(bridge_fwd_fields_buf_t, bridgef_output), print_default_cb }, +{ NULL, 0, 0, NULL}}; + +/* + * structures for 'dladm show-bridge -t'. + */ +typedef struct bridge_trill_fields_buf_s { + char bridget_nick[6]; + char bridget_flags[6]; + char bridget_link[MAXLINKNAMELEN]; + char bridget_nexthop[18]; +} bridge_trill_fields_buf_t; + +static ofmt_field_t bridge_trill_fields[] = { +/* name, field width, offset, callback */ +{ "NICK", 5, + offsetof(bridge_trill_fields_buf_t, bridget_nick), print_default_cb }, +{ "FLAGS", 6, + offsetof(bridge_trill_fields_buf_t, bridget_flags), print_default_cb }, +{ "LINK", 12, + offsetof(bridge_trill_fields_buf_t, bridget_link), print_default_cb }, +{ "NEXTHOP", 17, + offsetof(bridge_trill_fields_buf_t, bridget_nexthop), print_default_cb }, +{ NULL, 0, 0, NULL}}; + static char *progname; static sig_atomic_t signalled; @@ -1460,8 +1769,8 @@ done: dladm_free_props(proplist); if (status != DLADM_STATUS_OK) { if (status == DLADM_STATUS_NONOTIF) { - die_dlerr(status, "not all links have link up/down " - "detection; must use -f (see dladm(1M))\n"); + die("not all links have link up/down detection; must " + "use -f (see dladm(1M))"); } else { die_dlerr(status, "create operation failed"); } @@ -1630,8 +1939,8 @@ done: dladm_close(handle); exit(ENOTSUP); } else if (status == DLADM_STATUS_NONOTIF) { - die_dlerr(status, "not all links have link up/down " - "detection; must use -f (see dladm(1M))\n"); + die("not all links have link up/down detection; must " + "use -f (see dladm(1M))"); } else { die_dlerr(status, "add operation failed"); } @@ -1925,9 +2234,23 @@ do_create_vlan(int argc, char *argv[], const char *use) != DLADM_STATUS_OK) die("invalid vlan property"); - if ((status = dladm_vlan_create(handle, vlan, dev_linkid, vid, proplist, - flags, &linkid)) != DLADM_STATUS_OK) { - die_dlerr(status, "create operation over %s failed", link); + status = dladm_vlan_create(handle, vlan, dev_linkid, vid, proplist, + flags, &linkid); + switch (status) { + case DLADM_STATUS_OK: + break; + + case DLADM_STATUS_NOTSUP: + die("VLAN over '%s' may require lowered MTU; must use -f (see " + "dladm(1M))", link); + break; + + case DLADM_STATUS_LINKBUSY: + die("VLAN over '%s' may not use default_tag ID", link); + break; + + default: + die_dlerr(status, "create operation failed"); } } @@ -2125,11 +2448,26 @@ print_link_topology(show_state_t *state, datalink_id_t linkid, datalink_class_t class, link_fields_buf_t *lbuf) { uint32_t flags = state->ls_flags; - dladm_status_t status = DLADM_STATUS_OK; + dladm_status_t status; char tmpbuf[MAXLINKNAMELEN]; lbuf->link_over[0] = '\0'; + lbuf->link_bridge[0] = '\0'; + + switch (class) { + case DATALINK_CLASS_AGGR: + case DATALINK_CLASS_PHYS: + case DATALINK_CLASS_ETHERSTUB: + status = dladm_bridge_getlink(handle, linkid, lbuf->link_bridge, + sizeof (lbuf->link_bridge)); + if (status == DLADM_STATUS_OK) + break; + if (status != DLADM_STATUS_NOTFOUND) + return (status); + break; + } + status = DLADM_STATUS_OK; switch (class) { case DATALINK_CLASS_VLAN: { dladm_vlan_attr_t vinfo; @@ -2146,6 +2484,8 @@ print_link_topology(show_state_t *state, datalink_id_t linkid, dladm_aggr_grp_attr_t ginfo; int i; + lbuf->link_over[0] = '\0'; + status = dladm_aggr_info(handle, linkid, &ginfo, flags); if (status != DLADM_STATUS_OK) break; @@ -2158,10 +2498,8 @@ print_link_topology(show_state_t *state, datalink_id_t linkid, status = dladm_datalink_id2info(handle, ginfo.lg_ports[i].lp_linkid, NULL, NULL, NULL, tmpbuf, sizeof (tmpbuf)); - if (status != DLADM_STATUS_OK) { - free(ginfo.lg_ports); + if (status != DLADM_STATUS_OK) break; - } (void) strlcat(lbuf->link_over, tmpbuf, sizeof (lbuf->link_over)); if (i != (ginfo.lg_nports - 1)) { @@ -2184,6 +2522,38 @@ print_link_topology(show_state_t *state, datalink_id_t linkid, break; } + case DATALINK_CLASS_BRIDGE: { + datalink_id_t *dlp; + uint_t i, nports; + + status = dladm_datalink_id2info(handle, linkid, NULL, NULL, + NULL, tmpbuf, sizeof (tmpbuf)); + if (status != DLADM_STATUS_OK) + break; + if (tmpbuf[0] != '\0') + tmpbuf[strlen(tmpbuf) - 1] = '\0'; + dlp = dladm_bridge_get_portlist(tmpbuf, &nports); + if (dlp == NULL) { + status = DLADM_STATUS_BADVAL; + break; + } + lbuf->link_over[0] = '\0'; + for (i = 0; i < nports; i++) { + status = dladm_datalink_id2info(handle, dlp[i], NULL, + NULL, NULL, tmpbuf, sizeof (tmpbuf)); + if (status != DLADM_STATUS_OK) + break; + (void) strlcat(lbuf->link_over, tmpbuf, + sizeof (lbuf->link_over)); + if (i != nports - 1) { + (void) strlcat(lbuf->link_over, " ", + sizeof (lbuf->link_over)); + } + } + dladm_bridge_free_portlist(dlp); + break; + } + case DATALINK_CLASS_SIMNET: { dladm_simnet_attr_t slinfo; @@ -2867,8 +3237,8 @@ do_show_link(int argc, char *argv[], const char *use) dladm_status_t status; boolean_t o_arg = B_FALSE; char *fields_str = NULL; - char *all_active_fields = "link,class,mtu,state,over"; - char *all_inactive_fields = "link,class,over"; + char *all_active_fields = "link,class,mtu,state,bridge,over"; + char *all_inactive_fields = "link,class,bridge,over"; char *allstat_fields = "link,ipackets,rbytes,ierrors,opackets,obytes,oerrors"; ofmt_handle_t ofmt; @@ -5849,22 +6219,6 @@ show_linkprop_onelink(dladm_handle_t hdl, datalink_id_t linkid, void *arg) return (DLADM_WALK_CONTINUE); } -static dladm_status_t -set_linkprop_persist(datalink_id_t linkid, const char *prop_name, - char **prop_val, uint_t val_cnt, boolean_t reset) -{ - dladm_status_t status; - - status = dladm_set_linkprop(handle, linkid, prop_name, prop_val, - val_cnt, DLADM_OPT_PERSIST); - - if (status != DLADM_STATUS_OK) { - warn_dlerr(status, "cannot persistently %s link property '%s'", - reset ? "reset" : "set", prop_name); - } - return (status); -} - static int reset_one_linkprop(dladm_handle_t dh, datalink_id_t linkid, const char *propname, void *arg) @@ -5873,23 +6227,14 @@ reset_one_linkprop(dladm_handle_t dh, datalink_id_t linkid, dladm_status_t status; status = dladm_set_linkprop(dh, linkid, propname, NULL, 0, - DLADM_OPT_ACTIVE); + DLADM_OPT_ACTIVE | (statep->ls_temp ? 0 : DLADM_OPT_PERSIST)); if (status != DLADM_STATUS_OK && status != DLADM_STATUS_PROPRDONLY && status != DLADM_STATUS_NOTSUP) { warn_dlerr(status, "cannot reset link property '%s' on '%s'", propname, statep->ls_name); - } - if (!statep->ls_temp) { - dladm_status_t s; - - s = set_linkprop_persist(linkid, propname, NULL, 0, - statep->ls_reset); - if (s != DLADM_STATUS_OK) - status = s; - } - if (status != DLADM_STATUS_OK) statep->ls_status = status; + } return (DLADM_WALK_CONTINUE); } @@ -5970,7 +6315,6 @@ set_linkprop(int argc, char **argv, boolean_t reset, const char *use) dladm_arg_info_t *aip = &proplist->al_info[i]; char **val; uint_t count; - dladm_status_t s; if (reset) { val = NULL; @@ -5985,19 +6329,11 @@ set_linkprop(int argc, char **argv, boolean_t reset, const char *use) continue; } } - s = dladm_set_linkprop(handle, linkid, aip->ai_name, val, count, - DLADM_OPT_ACTIVE); - if (s == DLADM_STATUS_OK) { - if (!temp) { - s = set_linkprop_persist(linkid, - aip->ai_name, val, count, reset); - if (s != DLADM_STATUS_OK) - status = s; - } - continue; - } - status = s; - switch (s) { + status = dladm_set_linkprop(handle, linkid, aip->ai_name, val, + count, DLADM_OPT_ACTIVE | (temp ? 0 : DLADM_OPT_PERSIST)); + switch (status) { + case DLADM_STATUS_OK: + break; case DLADM_STATUS_NOTFOUND: warn("invalid link property '%s'", aip->ai_name); break; @@ -6006,6 +6342,7 @@ set_linkprop(int argc, char **argv, boolean_t reset, const char *use) char *ptr, *lim; char **propvals = NULL; uint_t valcnt = DLADM_MAX_PROP_VALCNT; + dladm_status_t s; ptr = malloc((sizeof (char *) + DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT + @@ -6770,6 +7107,1106 @@ do_init_secobj(int argc, char **argv, const char *use) die_dlerr(status, "secure object initialization failed"); } +enum bridge_func { + brCreate, brAdd, brModify +}; + +static void +create_modify_add_bridge(int argc, char **argv, const char *use, + enum bridge_func func) +{ + int option; + uint_t n, i, nlink; + uint32_t flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST; + char *altroot = NULL; + char *links[MAXPORT]; + datalink_id_t linkids[MAXPORT]; + dladm_status_t status; + const char *bridge; + UID_STP_CFG_T cfg, cfg_old; + dladm_bridge_prot_t brprot = DLADM_BRIDGE_PROT_UNKNOWN; + dladm_bridge_prot_t brprot_old; + + /* Set up the default configuration values */ + 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; + + nlink = opterr = 0; + while ((option = getopt_long(argc, argv, ":P:R:d:f:h:l:m:p:", + bridge_lopts, NULL)) != -1) { + switch (option) { + case 'P': + if (func == brAdd) + die_opterr(optopt, option, use); + status = dladm_bridge_str2prot(optarg, &brprot); + if (status != DLADM_STATUS_OK) + die_dlerr(status, "protection %s", optarg); + break; + case 'R': + altroot = optarg; + break; + case 'd': + if (func == brAdd) + die_opterr(optopt, option, use); + if (cfg.field_mask & BR_CFG_DELAY) + die("forwarding delay set more than once"); + if (!str2int(optarg, &cfg.forward_delay) || + cfg.forward_delay < MIN_BR_FWDELAY || + cfg.forward_delay > MAX_BR_FWDELAY) + die("incorrect forwarding delay"); + cfg.field_mask |= BR_CFG_DELAY; + break; + case 'f': + if (func == brAdd) + die_opterr(optopt, option, use); + if (cfg.field_mask & BR_CFG_FORCE_VER) + die("force protocol set more than once"); + if (!str2int(optarg, &cfg.force_version) || + cfg.force_version < 0) + die("incorrect force protocol"); + cfg.field_mask |= BR_CFG_FORCE_VER; + break; + case 'h': + if (func == brAdd) + die_opterr(optopt, option, use); + if (cfg.field_mask & BR_CFG_HELLO) + die("hello time set more than once"); + if (!str2int(optarg, &cfg.hello_time) || + cfg.hello_time < MIN_BR_HELLOT || + cfg.hello_time > MAX_BR_HELLOT) + die("incorrect hello time"); + cfg.field_mask |= BR_CFG_HELLO; + break; + case 'l': + if (func == brModify) + die_opterr(optopt, option, use); + if (nlink >= MAXPORT) + die("too many links specified"); + links[nlink++] = optarg; + break; + case 'm': + if (func == brAdd) + die_opterr(optopt, option, use); + if (cfg.field_mask & BR_CFG_AGE) + die("max age set more than once"); + if (!str2int(optarg, &cfg.max_age) || + cfg.max_age < MIN_BR_MAXAGE || + cfg.max_age > MAX_BR_MAXAGE) + die("incorrect max age"); + cfg.field_mask |= BR_CFG_AGE; + break; + case 'p': + if (func == brAdd) + die_opterr(optopt, option, use); + if (cfg.field_mask & BR_CFG_PRIO) + die("priority set more than once"); + if (!str2int(optarg, &cfg.bridge_priority) || + cfg.bridge_priority < MIN_BR_PRIO || + cfg.bridge_priority > MAX_BR_PRIO) + die("incorrect priority"); + cfg.bridge_priority &= 0xF000; + cfg.field_mask |= BR_CFG_PRIO; + break; + default: + die_opterr(optopt, option, use); + break; + } + } + + /* get the bridge name (required last argument) */ + if (optind != (argc-1)) + usage(); + + bridge = argv[optind]; + if (!dladm_valid_bridgename(bridge)) + die("invalid bridge name '%s'", bridge); + + /* + * Get the current properties, if any, and merge in with changes. This + * is necessary (even with the field_mask feature) so that the + * value-checking macros will produce the right results with proposed + * changes to existing configuration. We only need it for those + * parameters, though. + */ + (void) dladm_bridge_get_properties(bridge, &cfg_old, &brprot_old); + if (brprot == DLADM_BRIDGE_PROT_UNKNOWN) + brprot = brprot_old; + if (!(cfg.field_mask & BR_CFG_AGE)) + cfg.max_age = cfg_old.max_age; + if (!(cfg.field_mask & BR_CFG_HELLO)) + cfg.hello_time = cfg_old.hello_time; + if (!(cfg.field_mask & BR_CFG_DELAY)) + cfg.forward_delay = cfg_old.forward_delay; + + if (!CHECK_BRIDGE_CONFIG(cfg)) { + warn("illegal forward delay / max age / hello time " + "combination"); + if (NO_MAXAGE(cfg)) { + die("no max age possible: need forward delay >= %d or " + "hello time <= %d", MIN_FWDELAY_NOM(cfg), + MAX_HELLOTIME_NOM(cfg)); + } else if (SMALL_MAXAGE(cfg)) { + if (CAPPED_MAXAGE(cfg)) + die("max age too small: need age >= %d and " + "<= %d or hello time <= %d", + MIN_MAXAGE(cfg), MAX_MAXAGE(cfg), + MAX_HELLOTIME(cfg)); + else + die("max age too small: need age >= %d or " + "hello time <= %d", + MIN_MAXAGE(cfg), MAX_HELLOTIME(cfg)); + } else if (FLOORED_MAXAGE(cfg)) { + die("max age too large: need age >= %d and <= %d or " + "forward delay >= %d", + MIN_MAXAGE(cfg), MAX_MAXAGE(cfg), + MIN_FWDELAY(cfg)); + } else { + die("max age too large: need age <= %d or forward " + "delay >= %d", + MAX_MAXAGE(cfg), MIN_FWDELAY(cfg)); + } + } + + if (altroot != NULL) + altroot_cmd(altroot, argc, argv); + + for (n = 0; n < nlink; n++) { + datalink_class_t class; + uint32_t media; + char pointless[DLADM_STRSIZE]; + + if (dladm_name2info(handle, links[n], &linkids[n], NULL, &class, + &media) != DLADM_STATUS_OK) + die("invalid link name '%s'", links[n]); + if (class & ~(DATALINK_CLASS_PHYS | DATALINK_CLASS_AGGR | + DATALINK_CLASS_ETHERSTUB | DATALINK_CLASS_SIMNET)) + die("%s %s cannot be bridged", + dladm_class2str(class, pointless), links[n]); + if (media != DL_ETHER && media != DL_100VG && + media != DL_ETH_CSMA && media != DL_100BT) + die("%s interface %s cannot be bridged", + dladm_media2str(media, pointless), links[n]); + } + + if (func == brCreate) + flags |= DLADM_OPT_CREATE; + + if (func != brAdd) { + status = dladm_bridge_configure(handle, bridge, &cfg, brprot, + flags); + if (status != DLADM_STATUS_OK) + die_dlerr(status, "create operation failed"); + } + + status = DLADM_STATUS_OK; + for (n = 0; n < nlink; n++) { + status = dladm_bridge_setlink(handle, linkids[n], bridge); + if (status != DLADM_STATUS_OK) + break; + } + + if (n >= nlink) { + /* + * We were successful. If we're creating a new bridge, then + * there's just one more step: enabling. If we're modifying or + * just adding links, then we're done. + */ + if (func != brCreate || + (status = dladm_bridge_enable(bridge)) == DLADM_STATUS_OK) + return; + } + + /* clean up the partial configuration */ + for (i = 0; i < n; i++) + (void) dladm_bridge_setlink(handle, linkids[i], ""); + + /* if failure for brCreate, then delete the bridge */ + if (func == brCreate) + (void) dladm_bridge_delete(handle, bridge, flags); + + if (n < nlink) + die_dlerr(status, "unable to add link %s to bridge %s", + links[n], bridge); + else + die_dlerr(status, "unable to enable bridge %s", bridge); +} + +static void +do_create_bridge(int argc, char **argv, const char *use) +{ + create_modify_add_bridge(argc, argv, use, brCreate); +} + +static void +do_modify_bridge(int argc, char **argv, const char *use) +{ + create_modify_add_bridge(argc, argv, use, brModify); +} + +static void +do_add_bridge(int argc, char **argv, const char *use) +{ + create_modify_add_bridge(argc, argv, use, brAdd); +} + +static void +do_delete_bridge(int argc, char **argv, const char *use) +{ + char option; + char *altroot = NULL; + uint32_t flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST; + dladm_status_t status; + + opterr = 0; + while ((option = getopt_long(argc, argv, ":R:", bridge_lopts, NULL)) != + -1) { + switch (option) { + case 'R': + altroot = optarg; + break; + default: + die_opterr(optopt, option, use); + break; + } + } + + /* get the bridge name (required last argument) */ + if (optind != (argc-1)) + usage(); + + if (altroot != NULL) + altroot_cmd(altroot, argc, argv); + + status = dladm_bridge_delete(handle, argv[optind], flags); + if (status != DLADM_STATUS_OK) + die_dlerr(status, "delete operation failed"); +} + +static void +do_remove_bridge(int argc, char **argv, const char *use) +{ + char option; + uint_t n, nlink; + char *links[MAXPORT]; + datalink_id_t linkids[MAXPORT]; + char *altroot = NULL; + dladm_status_t status; + boolean_t removed_one; + + nlink = opterr = 0; + while ((option = getopt_long(argc, argv, ":R:l:", bridge_lopts, + NULL)) != -1) { + switch (option) { + case 'R': + altroot = optarg; + break; + case 'l': + if (nlink >= MAXPORT) + die("too many links specified"); + links[nlink++] = optarg; + break; + default: + die_opterr(optopt, option, use); + break; + } + } + + if (nlink == 0) + usage(); + + /* get the bridge name (required last argument) */ + if (optind != (argc-1)) + usage(); + + if (altroot != NULL) + altroot_cmd(altroot, argc, argv); + + for (n = 0; n < nlink; n++) { + char bridge[MAXLINKNAMELEN]; + + if (dladm_name2info(handle, links[n], &linkids[n], NULL, NULL, + NULL) != DLADM_STATUS_OK) + die("invalid link name '%s'", links[n]); + status = dladm_bridge_getlink(handle, linkids[n], bridge, + sizeof (bridge)); + if (status != DLADM_STATUS_OK && + status != DLADM_STATUS_NOTFOUND) { + die_dlerr(status, "cannot get bridge status on %s", + links[n]); + } + if (status == DLADM_STATUS_NOTFOUND || + strcmp(bridge, argv[optind]) != 0) + die("link %s is not on bridge %s", links[n], + argv[optind]); + } + + removed_one = B_FALSE; + for (n = 0; n < nlink; n++) { + status = dladm_bridge_setlink(handle, linkids[n], ""); + if (status == DLADM_STATUS_OK) { + removed_one = B_TRUE; + } else { + warn_dlerr(status, + "cannot remove link %s from bridge %s", + links[n], argv[optind]); + } + } + if (!removed_one) + die("unable to remove any links from bridge %s", argv[optind]); +} + +static void +fmt_int(char *buf, size_t buflen, int value, int runvalue, + boolean_t printstar) +{ + (void) snprintf(buf, buflen, "%d", value); + if (value != runvalue && printstar) + (void) strlcat(buf, "*", buflen); +} + +static void +fmt_bridge_id(char *buf, size_t buflen, UID_BRIDGE_ID_T *bid) +{ + (void) snprintf(buf, buflen, "%u/%x:%x:%x:%x:%x:%x", bid->prio, + bid->addr[0], bid->addr[1], bid->addr[2], bid->addr[3], + bid->addr[4], bid->addr[5]); +} + +static dladm_status_t +print_bridge(show_state_t *state, datalink_id_t linkid, + bridge_fields_buf_t *bbuf) +{ + char link[MAXLINKNAMELEN]; + datalink_class_t class; + uint32_t flags; + dladm_status_t status; + UID_STP_CFG_T smfcfg, runcfg; + UID_STP_STATE_T stpstate; + dladm_bridge_prot_t smfprot, runprot; + + if ((status = dladm_datalink_id2info(handle, linkid, &flags, &class, + NULL, link, sizeof (link))) != DLADM_STATUS_OK) + return (status); + + if (!(state->ls_flags & flags)) + return (DLADM_STATUS_NOTFOUND); + + /* Convert observability node name back to bridge name */ + if (!dladm_observe_to_bridge(link)) + return (DLADM_STATUS_NOTFOUND); + (void) strlcpy(bbuf->bridge_name, link, sizeof (bbuf->bridge_name)); + + /* + * If the running value differs from the one in SMF, and parsable + * output is not requested, then we show the running value with an + * asterisk. + */ + (void) dladm_bridge_get_properties(bbuf->bridge_name, &smfcfg, + &smfprot); + (void) dladm_bridge_run_properties(bbuf->bridge_name, &runcfg, + &runprot); + (void) snprintf(bbuf->bridge_protect, sizeof (bbuf->bridge_protect), + "%s%s", state->ls_parsable || smfprot == runprot ? "" : "*", + dladm_bridge_prot2str(runprot)); + fmt_int(bbuf->bridge_priority, sizeof (bbuf->bridge_priority), + smfcfg.bridge_priority, runcfg.bridge_priority, + !state->ls_parsable && (runcfg.field_mask & BR_CFG_AGE)); + fmt_int(bbuf->bridge_bmaxage, sizeof (bbuf->bridge_bmaxage), + smfcfg.max_age, runcfg.max_age, + !state->ls_parsable && (runcfg.field_mask & BR_CFG_AGE)); + fmt_int(bbuf->bridge_bhellotime, + sizeof (bbuf->bridge_bhellotime), smfcfg.hello_time, + runcfg.hello_time, + !state->ls_parsable && (runcfg.field_mask & BR_CFG_HELLO)); + fmt_int(bbuf->bridge_bfwddelay, sizeof (bbuf->bridge_bfwddelay), + smfcfg.forward_delay, runcfg.forward_delay, + !state->ls_parsable && (runcfg.field_mask & BR_CFG_DELAY)); + fmt_int(bbuf->bridge_forceproto, sizeof (bbuf->bridge_forceproto), + smfcfg.force_version, runcfg.force_version, + !state->ls_parsable && (runcfg.field_mask & BR_CFG_FORCE_VER)); + fmt_int(bbuf->bridge_holdtime, sizeof (bbuf->bridge_holdtime), + smfcfg.hold_time, runcfg.hold_time, + !state->ls_parsable && (runcfg.field_mask & BR_CFG_HOLD_TIME)); + + if (dladm_bridge_state(bbuf->bridge_name, &stpstate) == + DLADM_STATUS_OK) { + fmt_bridge_id(bbuf->bridge_address, + sizeof (bbuf->bridge_address), &stpstate.bridge_id); + (void) snprintf(bbuf->bridge_tctime, + sizeof (bbuf->bridge_tctime), "%lu", + stpstate.timeSince_Topo_Change); + (void) snprintf(bbuf->bridge_tccount, + sizeof (bbuf->bridge_tccount), "%lu", + stpstate.Topo_Change_Count); + (void) snprintf(bbuf->bridge_tchange, + sizeof (bbuf->bridge_tchange), "%u", stpstate.Topo_Change); + fmt_bridge_id(bbuf->bridge_desroot, + sizeof (bbuf->bridge_desroot), &stpstate.designated_root); + (void) snprintf(bbuf->bridge_rootcost, + sizeof (bbuf->bridge_rootcost), "%lu", + stpstate.root_path_cost); + (void) snprintf(bbuf->bridge_rootport, + sizeof (bbuf->bridge_rootport), "%u", stpstate.root_port); + (void) snprintf(bbuf->bridge_maxage, + sizeof (bbuf->bridge_maxage), "%d", stpstate.max_age); + (void) snprintf(bbuf->bridge_hellotime, + sizeof (bbuf->bridge_hellotime), "%d", stpstate.hello_time); + (void) snprintf(bbuf->bridge_fwddelay, + sizeof (bbuf->bridge_fwddelay), "%d", + stpstate.forward_delay); + } + return (DLADM_STATUS_OK); +} + +static dladm_status_t +print_bridge_stats(show_state_t *state, datalink_id_t linkid, + bridge_statfields_buf_t *bbuf) +{ + char link[MAXLINKNAMELEN]; + datalink_class_t class; + uint32_t flags; + dladm_status_t status; + kstat_ctl_t *kcp; + kstat_t *ksp; + brsum_t *brsum = (brsum_t *)&state->ls_prevstats; + brsum_t newval; + +#ifndef lint + /* This is a compile-time assertion; optimizer normally fixes this */ + extern void brsum_t_is_too_large(void); + + if (sizeof (*brsum) > sizeof (state->ls_prevstats)) + brsum_t_is_too_large(); +#endif + + if (state->ls_firstonly) { + if (state->ls_donefirst) + return (DLADM_WALK_CONTINUE); + state->ls_donefirst = B_TRUE; + } else { + bzero(brsum, sizeof (*brsum)); + } + bzero(&newval, sizeof (newval)); + + if ((status = dladm_datalink_id2info(handle, linkid, &flags, &class, + NULL, link, sizeof (link))) != DLADM_STATUS_OK) + return (status); + + if (!(state->ls_flags & flags)) + return (DLADM_STATUS_NOTFOUND); + + if ((kcp = kstat_open()) == NULL) { + warn("kstat open operation failed"); + return (DLADM_STATUS_OK); + } + if ((ksp = kstat_lookup(kcp, "bridge", 0, link)) != NULL && + kstat_read(kcp, ksp, NULL) != -1) { + if (dladm_kstat_value(ksp, "drops", KSTAT_DATA_UINT64, + &newval.drops) == DLADM_STATUS_OK) { + (void) snprintf(bbuf->bridges_drops, + sizeof (bbuf->bridges_drops), "%llu", + newval.drops - brsum->drops); + } + if (dladm_kstat_value(ksp, "forward_direct", KSTAT_DATA_UINT64, + &newval.forward_dir) == DLADM_STATUS_OK) { + (void) snprintf(bbuf->bridges_forwards, + sizeof (bbuf->bridges_forwards), "%llu", + newval.forward_dir - brsum->forward_dir); + } + if (dladm_kstat_value(ksp, "forward_mbcast", KSTAT_DATA_UINT64, + &newval.forward_mb) == DLADM_STATUS_OK) { + (void) snprintf(bbuf->bridges_mbcast, + sizeof (bbuf->bridges_mbcast), "%llu", + newval.forward_mb - brsum->forward_mb); + } + if (dladm_kstat_value(ksp, "forward_unknown", KSTAT_DATA_UINT64, + &newval.forward_unk) == DLADM_STATUS_OK) { + (void) snprintf(bbuf->bridges_unknown, + sizeof (bbuf->bridges_unknown), "%llu", + newval.forward_unk - brsum->forward_unk); + } + if (dladm_kstat_value(ksp, "recv", KSTAT_DATA_UINT64, + &newval.recv) == DLADM_STATUS_OK) { + (void) snprintf(bbuf->bridges_recv, + sizeof (bbuf->bridges_recv), "%llu", + newval.recv - brsum->recv); + } + if (dladm_kstat_value(ksp, "sent", KSTAT_DATA_UINT64, + &newval.sent) == DLADM_STATUS_OK) { + (void) snprintf(bbuf->bridges_sent, + sizeof (bbuf->bridges_sent), "%llu", + newval.sent - brsum->sent); + } + } + (void) kstat_close(kcp); + + /* Convert observability node name back to bridge name */ + if (!dladm_observe_to_bridge(link)) + return (DLADM_STATUS_NOTFOUND); + (void) strlcpy(bbuf->bridges_name, link, sizeof (bbuf->bridges_name)); + + *brsum = newval; + + return (DLADM_STATUS_OK); +} + +/* + * This structure carries around extra state information for the show-bridge + * command and allows us to use common support functions. + */ +typedef struct { + show_state_t state; + boolean_t show_stats; + const char *bridge; +} show_brstate_t; + +/* ARGSUSED */ +static int +show_bridge(dladm_handle_t handle, datalink_id_t linkid, void *arg) +{ + show_brstate_t *brstate = arg; + void *buf; + + if (brstate->show_stats) { + bridge_statfields_buf_t bbuf; + + bzero(&bbuf, sizeof (bbuf)); + brstate->state.ls_status = print_bridge_stats(&brstate->state, + linkid, &bbuf); + buf = &bbuf; + } else { + bridge_fields_buf_t bbuf; + + bzero(&bbuf, sizeof (bbuf)); + brstate->state.ls_status = print_bridge(&brstate->state, linkid, + &bbuf); + buf = &bbuf; + } + if (brstate->state.ls_status == DLADM_STATUS_OK) + ofmt_print(brstate->state.ls_ofmt, buf); + return (DLADM_WALK_CONTINUE); +} + +static void +fmt_bool(char *buf, size_t buflen, int val) +{ + (void) strlcpy(buf, val ? "yes" : "no", buflen); +} + +static dladm_status_t +print_bridge_link(show_state_t *state, datalink_id_t linkid, + bridge_link_fields_buf_t *bbuf) +{ + datalink_class_t class; + uint32_t flags; + dladm_status_t status; + UID_STP_PORT_STATE_T stpstate; + + status = dladm_datalink_id2info(handle, linkid, &flags, &class, NULL, + bbuf->bridgel_link, sizeof (bbuf->bridgel_link)); + if (status != DLADM_STATUS_OK) + return (status); + + if (!(state->ls_flags & flags)) + return (DLADM_STATUS_NOTFOUND); + + if (dladm_bridge_link_state(handle, linkid, &stpstate) == + DLADM_STATUS_OK) { + (void) snprintf(bbuf->bridgel_index, + sizeof (bbuf->bridgel_index), "%u", stpstate.port_no); + if (dlsym(RTLD_PROBE, "STP_IN_state2str")) { + (void) strlcpy(bbuf->bridgel_state, + STP_IN_state2str(stpstate.state), + sizeof (bbuf->bridgel_state)); + } else { + (void) snprintf(bbuf->bridgel_state, + sizeof (bbuf->bridgel_state), "%u", + stpstate.state); + } + (void) snprintf(bbuf->bridgel_uptime, + sizeof (bbuf->bridgel_uptime), "%lu", stpstate.uptime); + (void) snprintf(bbuf->bridgel_opercost, + sizeof (bbuf->bridgel_opercost), "%lu", + stpstate.oper_port_path_cost); + fmt_bool(bbuf->bridgel_operp2p, sizeof (bbuf->bridgel_operp2p), + stpstate.oper_point2point); + fmt_bool(bbuf->bridgel_operedge, + sizeof (bbuf->bridgel_operedge), stpstate.oper_edge); + fmt_bridge_id(bbuf->bridgel_desroot, + sizeof (bbuf->bridgel_desroot), &stpstate.designated_root); + (void) snprintf(bbuf->bridgel_descost, + sizeof (bbuf->bridgel_descost), "%lu", + stpstate.designated_cost); + fmt_bridge_id(bbuf->bridgel_desbridge, + sizeof (bbuf->bridgel_desbridge), + &stpstate.designated_bridge); + (void) snprintf(bbuf->bridgel_desport, + sizeof (bbuf->bridgel_desport), "%u", + stpstate.designated_port); + fmt_bool(bbuf->bridgel_tcack, sizeof (bbuf->bridgel_tcack), + stpstate.top_change_ack); + } + return (DLADM_STATUS_OK); +} + +static dladm_status_t +print_bridge_link_stats(show_state_t *state, datalink_id_t linkid, + bridge_link_statfields_buf_t *bbuf) +{ + datalink_class_t class; + uint32_t flags; + dladm_status_t status; + UID_STP_PORT_STATE_T stpstate; + kstat_ctl_t *kcp; + kstat_t *ksp; + char bridge[MAXLINKNAMELEN]; + char kstatname[MAXLINKNAMELEN*2 + 1]; + brlsum_t *brlsum = (brlsum_t *)&state->ls_prevstats; + brlsum_t newval; + +#ifndef lint + /* This is a compile-time assertion; optimizer normally fixes this */ + extern void brlsum_t_is_too_large(void); + + if (sizeof (*brlsum) > sizeof (state->ls_prevstats)) + brlsum_t_is_too_large(); +#endif + + if (state->ls_firstonly) { + if (state->ls_donefirst) + return (DLADM_WALK_CONTINUE); + state->ls_donefirst = B_TRUE; + } else { + bzero(brlsum, sizeof (*brlsum)); + } + bzero(&newval, sizeof (newval)); + + status = dladm_datalink_id2info(handle, linkid, &flags, &class, NULL, + bbuf->bridgels_link, sizeof (bbuf->bridgels_link)); + if (status != DLADM_STATUS_OK) + return (status); + + if (!(state->ls_flags & flags)) + return (DLADM_STATUS_NOTFOUND); + + if (dladm_bridge_link_state(handle, linkid, &stpstate) == + DLADM_STATUS_OK) { + newval.cfgbpdu = stpstate.rx_cfg_bpdu_cnt; + newval.tcnbpdu = stpstate.rx_tcn_bpdu_cnt; + newval.rstpbpdu = stpstate.rx_rstp_bpdu_cnt; + newval.txbpdu = stpstate.txCount; + + (void) snprintf(bbuf->bridgels_cfgbpdu, + sizeof (bbuf->bridgels_cfgbpdu), "%lu", + newval.cfgbpdu - brlsum->cfgbpdu); + (void) snprintf(bbuf->bridgels_tcnbpdu, + sizeof (bbuf->bridgels_tcnbpdu), "%lu", + newval.tcnbpdu - brlsum->tcnbpdu); + (void) snprintf(bbuf->bridgels_rstpbpdu, + sizeof (bbuf->bridgels_rstpbpdu), "%lu", + newval.rstpbpdu - brlsum->rstpbpdu); + (void) snprintf(bbuf->bridgels_txbpdu, + sizeof (bbuf->bridgels_txbpdu), "%lu", + newval.txbpdu - brlsum->txbpdu); + } + + if ((status = dladm_bridge_getlink(handle, linkid, bridge, + sizeof (bridge))) != DLADM_STATUS_OK) + goto bls_out; + (void) snprintf(kstatname, sizeof (kstatname), "%s0-%s", bridge, + bbuf->bridgels_link); + if ((kcp = kstat_open()) == NULL) { + warn("kstat open operation failed"); + goto bls_out; + } + if ((ksp = kstat_lookup(kcp, "bridge", 0, kstatname)) != NULL && + kstat_read(kcp, ksp, NULL) != -1) { + if (dladm_kstat_value(ksp, "drops", KSTAT_DATA_UINT64, + &newval.drops) != -1) { + (void) snprintf(bbuf->bridgels_drops, + sizeof (bbuf->bridgels_drops), "%llu", + newval.drops - brlsum->drops); + } + if (dladm_kstat_value(ksp, "recv", KSTAT_DATA_UINT64, + &newval.recv) != -1) { + (void) snprintf(bbuf->bridgels_recv, + sizeof (bbuf->bridgels_recv), "%llu", + newval.recv - brlsum->recv); + } + if (dladm_kstat_value(ksp, "xmit", KSTAT_DATA_UINT64, + &newval.xmit) != -1) { + (void) snprintf(bbuf->bridgels_xmit, + sizeof (bbuf->bridgels_xmit), "%llu", + newval.xmit - brlsum->xmit); + } + } + (void) kstat_close(kcp); +bls_out: + *brlsum = newval; + + return (status); +} + +static void +show_bridge_link(datalink_id_t linkid, show_brstate_t *brstate) +{ + void *buf; + + if (brstate->show_stats) { + bridge_link_statfields_buf_t bbuf; + + bzero(&bbuf, sizeof (bbuf)); + brstate->state.ls_status = print_bridge_link_stats( + &brstate->state, linkid, &bbuf); + buf = &bbuf; + } else { + bridge_link_fields_buf_t bbuf; + + bzero(&bbuf, sizeof (bbuf)); + brstate->state.ls_status = print_bridge_link(&brstate->state, + linkid, &bbuf); + buf = &bbuf; + } + if (brstate->state.ls_status == DLADM_STATUS_OK) + ofmt_print(brstate->state.ls_ofmt, buf); +} + +/* ARGSUSED */ +static int +show_bridge_link_walk(dladm_handle_t handle, datalink_id_t linkid, void *arg) +{ + show_brstate_t *brstate = arg; + char bridge[MAXLINKNAMELEN]; + + if (dladm_bridge_getlink(handle, linkid, bridge, sizeof (bridge)) == + DLADM_STATUS_OK && strcmp(bridge, brstate->bridge) == 0) { + show_bridge_link(linkid, brstate); + } + return (DLADM_WALK_CONTINUE); +} + +static void +show_bridge_fwd(dladm_handle_t handle, bridge_listfwd_t *blf, + show_state_t *state) +{ + bridge_fwd_fields_buf_t bbuf; + + bzero(&bbuf, sizeof (bbuf)); + (void) snprintf(bbuf.bridgef_dest, sizeof (bbuf.bridgef_dest), + "%s", ether_ntoa((struct ether_addr *)blf->blf_dest)); + if (blf->blf_is_local) { + (void) strlcpy(bbuf.bridgef_flags, "L", + sizeof (bbuf.bridgef_flags)); + } else { + (void) snprintf(bbuf.bridgef_age, sizeof (bbuf.bridgef_age), + "%2d.%03d", blf->blf_ms_age / 1000, blf->blf_ms_age % 1000); + if (blf->blf_trill_nick != 0) { + (void) snprintf(bbuf.bridgef_output, + sizeof (bbuf.bridgef_output), "%u", + blf->blf_trill_nick); + } + } + if (blf->blf_linkid != DATALINK_INVALID_LINKID && + blf->blf_trill_nick == 0) { + state->ls_status = dladm_datalink_id2info(handle, + blf->blf_linkid, NULL, NULL, NULL, bbuf.bridgef_output, + sizeof (bbuf.bridgef_output)); + } + if (state->ls_status == DLADM_STATUS_OK) + ofmt_print(state->ls_ofmt, &bbuf); +} + +static void +show_bridge_trillnick(trill_listnick_t *tln, show_state_t *state) +{ + bridge_trill_fields_buf_t bbuf; + + bzero(&bbuf, sizeof (bbuf)); + (void) snprintf(bbuf.bridget_nick, sizeof (bbuf.bridget_nick), + "%u", tln->tln_nick); + if (tln->tln_ours) { + (void) strlcpy(bbuf.bridget_flags, "L", + sizeof (bbuf.bridget_flags)); + } else { + state->ls_status = dladm_datalink_id2info(handle, + tln->tln_linkid, NULL, NULL, NULL, bbuf.bridget_link, + sizeof (bbuf.bridget_link)); + (void) snprintf(bbuf.bridget_nexthop, + sizeof (bbuf.bridget_nexthop), "%s", + ether_ntoa((struct ether_addr *)tln->tln_nexthop)); + } + if (state->ls_status == DLADM_STATUS_OK) + ofmt_print(state->ls_ofmt, &bbuf); +} + +static void +do_show_bridge(int argc, char **argv, const char *use) +{ + int option; + enum { + bridgeMode, linkMode, fwdMode, trillMode + } op_mode = bridgeMode; + uint32_t flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST; + boolean_t parsable = B_FALSE; + datalink_id_t linkid = DATALINK_ALL_LINKID; + int interval = 0; + show_brstate_t brstate; + dladm_status_t status; + char *fields_str = NULL; + /* default: bridge-related data */ + char *all_fields = "bridge,protect,address,priority,bmaxage," + "bhellotime,bfwddelay,forceproto,tctime,tccount,tchange," + "desroot,rootcost,rootport,maxage,hellotime,fwddelay,holdtime"; + char *default_fields = "bridge,protect,address,priority," + "desroot"; + char *all_statfields = "bridge,drops,forwards,mbcast," + "unknown,recv,sent"; + char *default_statfields = "bridge,drops,forwards,mbcast," + "unknown"; + /* -l: link-related data */ + char *all_link_fields = "link,index,state,uptime,opercost," + "operp2p,operedge,desroot,descost,desbridge,desport,tcack"; + char *default_link_fields = "link,state,uptime,desroot"; + char *all_link_statfields = "link,cfgbpdu,tcnbpdu,rstpbpdu," + "txbpdu,drops,recv,xmit"; + char *default_link_statfields = "link,drops,recv,xmit"; + /* -f: bridge forwarding table related data */ + char *default_fwd_fields = "dest,age,flags,output"; + /* -t: TRILL nickname table related data */ + char *default_trill_fields = "nick,flags,link,nexthop"; + char *default_str; + char *all_str; + ofmt_field_t *field_arr; + ofmt_handle_t ofmt; + ofmt_status_t oferr; + uint_t ofmtflags = 0; + + bzero(&brstate, sizeof (brstate)); + + opterr = 0; + while ((option = getopt_long(argc, argv, ":fi:lo:pst", + bridge_show_lopts, NULL)) != -1) { + switch (option) { + case 'f': + if (op_mode != bridgeMode && op_mode != fwdMode) + die("-f is incompatible with -l or -t"); + op_mode = fwdMode; + break; + case 'i': + if (interval != 0) + die_optdup(option); + if (!str2int(optarg, &interval) || interval == 0) + die("invalid interval value '%s'", optarg); + break; + case 'l': + if (op_mode != bridgeMode && op_mode != linkMode) + die("-l is incompatible with -f or -t"); + op_mode = linkMode; + break; + case 'o': + fields_str = optarg; + break; + case 'p': + if (parsable) + die_optdup(option); + parsable = B_TRUE; + break; + case 's': + if (brstate.show_stats) + die_optdup(option); + brstate.show_stats = B_TRUE; + break; + case 't': + if (op_mode != bridgeMode && op_mode != trillMode) + die("-t is incompatible with -f or -l"); + op_mode = trillMode; + break; + default: + die_opterr(optopt, option, use); + break; + } + } + + if (interval != 0 && !brstate.show_stats) + die("the -i option can be used only with -s"); + + if ((op_mode == fwdMode || op_mode == trillMode) && brstate.show_stats) + die("the -f/-t and -s options cannot be used together"); + + /* get the bridge name (optional last argument) */ + if (optind == (argc-1)) { + char lname[MAXLINKNAMELEN]; + uint32_t lnkflg; + datalink_class_t class; + + brstate.bridge = argv[optind]; + (void) snprintf(lname, sizeof (lname), "%s0", brstate.bridge); + if ((status = dladm_name2info(handle, lname, &linkid, &lnkflg, + &class, NULL)) != DLADM_STATUS_OK) { + die_dlerr(status, "bridge %s is not valid", + brstate.bridge); + } + + if (class != DATALINK_CLASS_BRIDGE) + die("%s is not a bridge", brstate.bridge); + + if (!(lnkflg & flags)) { + die_dlerr(DLADM_STATUS_BADARG, + "bridge %s is temporarily removed", brstate.bridge); + } + } else if (optind != argc) { + usage(); + } else if (op_mode != bridgeMode) { + die("bridge name required for -l, -f, or -t"); + return; + } + + brstate.state.ls_parsable = parsable; + brstate.state.ls_flags = flags; + brstate.state.ls_firstonly = (interval != 0); + + switch (op_mode) { + case bridgeMode: + if (brstate.show_stats) { + default_str = default_statfields; + all_str = all_statfields; + field_arr = bridge_statfields; + } else { + default_str = default_fields; + all_str = all_fields; + field_arr = bridge_fields; + } + break; + + case linkMode: + if (brstate.show_stats) { + default_str = default_link_statfields; + all_str = all_link_statfields; + field_arr = bridge_link_statfields; + } else { + default_str = default_link_fields; + all_str = all_link_fields; + field_arr = bridge_link_fields; + } + break; + + case fwdMode: + default_str = all_str = default_fwd_fields; + field_arr = bridge_fwd_fields; + break; + + case trillMode: + default_str = all_str = default_trill_fields; + field_arr = bridge_trill_fields; + break; + } + + if (fields_str == NULL) + fields_str = default_str; + else if (strcasecmp(fields_str, "all") == 0) + fields_str = all_str; + + if (parsable) + ofmtflags |= OFMT_PARSABLE; + oferr = ofmt_open(fields_str, field_arr, ofmtflags, 0, &ofmt); + dladm_ofmt_check(oferr, brstate.state.ls_parsable, ofmt); + brstate.state.ls_ofmt = ofmt; + + for (;;) { + brstate.state.ls_donefirst = B_FALSE; + switch (op_mode) { + case bridgeMode: + if (linkid == DATALINK_ALL_LINKID) { + (void) dladm_walk_datalink_id(show_bridge, + handle, &brstate, DATALINK_CLASS_BRIDGE, + DATALINK_ANY_MEDIATYPE, flags); + } else { + (void) show_bridge(handle, linkid, &brstate); + if (brstate.state.ls_status != + DLADM_STATUS_OK) { + die_dlerr(brstate.state.ls_status, + "failed to show bridge %s", + brstate.bridge); + } + } + break; + + case linkMode: { + datalink_id_t *dlp; + uint_t i, nlinks; + + dlp = dladm_bridge_get_portlist(brstate.bridge, + &nlinks); + if (dlp != NULL) { + for (i = 0; i < nlinks; i++) + show_bridge_link(dlp[i], &brstate); + dladm_bridge_free_portlist(dlp); + } else if (errno == ENOENT) { + /* bridge not running; iterate on libdladm */ + (void) dladm_walk_datalink_id( + show_bridge_link_walk, handle, + &brstate, DATALINK_CLASS_PHYS | + DATALINK_CLASS_AGGR | + DATALINK_CLASS_ETHERSTUB, + DATALINK_ANY_MEDIATYPE, flags); + } else { + die("unable to get port list for bridge %s: %s", + brstate.bridge, strerror(errno)); + } + break; + } + + case fwdMode: { + bridge_listfwd_t *blf; + uint_t i, nfwd; + + blf = dladm_bridge_get_fwdtable(handle, brstate.bridge, + &nfwd); + if (blf == NULL) { + die("unable to get forwarding entries for " + "bridge %s", brstate.bridge); + } else { + for (i = 0; i < nfwd; i++) + show_bridge_fwd(handle, blf + i, + &brstate.state); + dladm_bridge_free_fwdtable(blf); + } + break; + } + + case trillMode: { + trill_listnick_t *tln; + uint_t i, nnick; + + tln = dladm_bridge_get_trillnick(brstate.bridge, + &nnick); + if (tln == NULL) { + if (errno == ENOENT) + die("bridge %s is not running TRILL", + brstate.bridge); + else + die("unable to get TRILL nickname " + "entries for bridge %s", + brstate.bridge); + } else { + for (i = 0; i < nnick; i++) + show_bridge_trillnick(tln + i, + &brstate.state); + dladm_bridge_free_trillnick(tln); + } + break; + } + } + if (interval == 0) + break; + (void) sleep(interval); + } +} + /* * "-R" option support. It is used for live upgrading. Append dladm commands * to a upgrade script which will be run when the alternative root boots up: diff --git a/usr/src/cmd/dladm/dladm.xcl b/usr/src/cmd/dladm/dladm.xcl index 2c74c5fbf4..44dc98a1e1 100644 --- a/usr/src/cmd/dladm/dladm.xcl +++ b/usr/src/cmd/dladm/dladm.xcl @@ -23,24 +23,17 @@ # # -msgid "" -msgid "\t%-10llu" -msgid "\t%-6.1f" -msgid "\t-" -msgid "--" -msgid "--," -msgid "\tipackets rbytes opackets obytes " -msgid "\n" -msgid " " msgid " " msgid " %-18s" msgid " MACADDRESS" +msgid " " msgid " %-18s" msgid " MACADDRTYPE" msgid " dev=%s" msgid " mac_addr=%s" msgid " speed=%u" msgid " vid=%d\n" +msgid "" msgid "%%ipkts %%opkts\n" msgid "%-*s" msgid "%-10llu" @@ -56,22 +49,32 @@ msgid "%-6.1f" msgid "%-6d\n" msgid "%-8llu" msgid "%-8llu\n" +msgid "%2d.%03d" msgid "%5u Mbps" msgid "%c----" msgid "%d" msgid "%d%c-%c" msgid "%llu" -msgid "%s" -msgid "%s\n" +msgid "%lu" msgid "%s " +msgid "%s" +msgid "%s%s" msgid "%s," +msgid "%s0" +msgid "%s0-%s" msgid "%s: " msgid "%s=\"%s\"" +msgid "%s\n" msgid "%sfdx" msgid "%shdx" msgid "%u" +msgid "%u/%x:%x:%x:%x:%x:%x" msgid "%uMb" +msgid "%x:%x:%x:%x:%x:%x" +msgid "*" msgid "," +msgid "--" +msgid "--," msgid "-R" msgid "-f" msgid "-fh" @@ -79,7 +82,10 @@ msgid "-h" msgid "/" msgid "/%s" msgid "/%s/%s" +msgid "/%s/etc/dladm/datalink.conf" msgid "/sbin/dladm " +msgid "/var/svc/profile/upgrade" +msgid "/var/svc/profile/upgrade_datalink" msgid "0x" msgid "100M" msgid "10M" @@ -95,6 +101,7 @@ msgid ":d:l:R:t" msgid ":d:l:R:tf" msgid ":e:i:a:m:b:s:k:T:c" msgid ":f:c:R:t" +msgid ":fi:lo:pst" msgid ":o:p" msgid ":p:R:t" msgid ":p:cPo:" @@ -108,64 +115,128 @@ msgid "ADDRESS" msgid "ADDRPOLICY" msgid "ADT_dladm_create_secobj" msgid "ADT_dladm_delete_secobj" +msgid "AGE" msgid "AGGREGATABLE" msgid "AUTH" msgid "AUTO" +msgid "BANDWIDTH" +msgid "BFWDDELAY" +msgid "BHELLOTIME" +msgid "BMAXAGE" +msgid "BRIDGE" +msgid "BSSID" msgid "BSSID/IBSSID" msgid "BSSTYPE" +msgid "CFGBPDU" msgid "CLASS" msgid "CLIENT" +msgid "CLIENTS" msgid "COLL" msgid "DEFAULT" msgid "DEFAULTED" +msgid "DESBRIDGE" +msgid "DESCOST" +msgid "DESPORT" +msgid "DESROOT" +msgid "DEST" msgid "DEVICE" msgid "DIST" +msgid "DROPS" msgid "DUPLEX" +msgid "DURATION" +msgid "END" msgid "ESSID" msgid "EXPIRED" msgid "FLAGS" +msgid "FORCEPROTO" +msgid "FORWARDS" +msgid "FWDDELAY" +msgid "GROUP" +msgid "GROUPTYPE" +msgid "HELLOTIME" +msgid "HOLDTIME" +msgid "IBSSID" msgid "IERRORS" +msgid "INDEX" msgid "INUSE" msgid "IPACKETS" msgid "IPKTDIST" +msgid "L" msgid "LACPACTIVITY" msgid "LACPTIMER" msgid "LINK" -msgid "LINK\n" msgid "LINKID" +msgid "LINK\n" +msgid "MACADDRESS" +msgid "MACADDRTYPE" +msgid "MAXAGE" +msgid "MBCAST" msgid "MEDIA" msgid "MODE" msgid "MTU" msgid "Mb" msgid "NAME" +msgid "NEXTHOP" +msgid "NICK" msgid "OBJECT" msgid "OBYTES" msgid "OERRORS" msgid "OPACKETS" +msgid "OPERCOST" +msgid "OPEREDGE" +msgid "OPERP2P" msgid "OPKTDIST" +msgid "OTHERLINK" +msgid "OUTPUT" msgid "OVER" msgid "PAUSE" +msgid "PERM" msgid "POLICY" msgid "PORT" msgid "PORTSTATE" msgid "POSSIBLE" +msgid "PRIORITY" msgid "PROPERTY" +msgid "PROTECT" msgid "PTYPE" msgid "RBYTES" +msgid "RECV" msgid "REM_FAULT" +msgid "RINGS" +msgid "ROOTCOST" +msgid "ROOTPORT" +msgid "RSTPBPDU" msgid "SEC" +msgid "SENT" msgid "SLOT" msgid "SPEED" msgid "SPEED-DUPLEX" +msgid "START" msgid "STATE" msgid "STATUS" +msgid "STP_IN_state2str" msgid "STRENGTH" msgid "SYNC" +msgid "TCACK" +msgid "TCCOUNT" +msgid "TCHANGE" +msgid "TCNBPDU" +msgid "TCTIME" +msgid "TXBPDU" msgid "Total" +msgid "UNKNOWN" +msgid "UPTIME" msgid "VALUE" msgid "VID" +msgid "XMIT" +msgid "\n" +msgid "\t%-10llu" +msgid "\t%-6.1f" +msgid "\t-" +msgid "\tipackets rbytes opackets obytes " msgid "a+" msgid "add-aggr" +msgid "add-bridge" msgid "address" msgid "addrpolicy" msgid "adt_alloc_event (%s): %s" @@ -185,6 +256,16 @@ msgid "all-links" msgid "auth" msgid "auto" msgid "bi" +msgid "bridge" +msgid "bridge,drops,forwards,mbcast," + "unknown" +msgid "bridge,drops,forwards,mbcast," + "unknown,recv,sent" +msgid "bridge,protect,address,priority,bmaxage," + "bhellotime,bfwddelay,forceproto,tctime,tccount,tchange," + "desroot,rootcost,rootport,maxage,hellotime,fwddelay,holdtime" +msgid "bridge,protect,address,priority," + "desroot" msgid "bssid" msgid "bsstype" msgid "bw-limit" @@ -203,36 +284,50 @@ msgid "connect-wifi" msgid "continuous" msgid "cpus" msgid "create-aggr" +msgid "create-bridge" msgid "create-etherstub" msgid "create-ibss" msgid "create-secobj" +msgid "create-simnet" msgid "create-vlan" msgid "create-vnic" msgid "current" msgid "default" msgid "defaulted" msgid "delete-aggr" +msgid "delete-bridge" msgid "delete-etherstub" msgid "delete-phys" msgid "delete-secobj" +msgid "delete-simnet" msgid "delete-vlan" msgid "delete-vnic" +msgid "dest,age,flags,output" msgid "dev" msgid "device" msgid "disconnect-wifi" msgid "dist" msgid "down-vnic" +msgid "drops" msgid "duplex" msgid "essid" msgid "expired" msgid "extended" msgid "fault" msgid "file" -msgid "fixed" msgid "fixed (%s)" +msgid "fixed" msgid "flags" +msgid "force-protocol" msgid "forcible" msgid "forever" +msgid "format" +msgid "forward-delay" +msgid "forward_direct" +msgid "forward_mbcast" +msgid "forward_unknown" +msgid "forwarding" +msgid "hello-time" msgid "ibssid" msgid "ierrors" msgid "ifspeed" @@ -250,11 +345,16 @@ msgid "lacp-timer" msgid "lacpactivity" msgid "lacptimer" msgid "link" -msgid "link,class,mtu,state,over" -msgid "link,class,over" +msgid "link,cfgbpdu,tcnbpdu,rstpbpdu," + "txbpdu,drops,recv,xmit" +msgid "link,class,bridge,over" +msgid "link,class,mtu,state,bridge,over" msgid "link,device,media,flags" +msgid "link,drops,recv,xmit" msgid "link,essid,bssid,sec,strength,mode,speed" msgid "link,essid,bssid,sec,strength,mode,speed,bsstype" +msgid "link,index,state,uptime,opercost," + "operp2p,operedge,desroot,descost,desbridge,desport,tcack" msgid "link,ipackets,rbytes,ierrors,opackets,obytes,oerrors" msgid "link,media,state,speed,duplex,device" msgid "link,policy,addrpolicy,lacpactivity,lacptimer,flags" @@ -266,6 +366,7 @@ msgid "link,ptype,state,auto,speed-duplex,pause" msgid "link,ptype,state,auto,speed-duplex,pause,rem_fault" msgid "link,slot,address,inuse,client" msgid "link,state,speed,duplex" +msgid "link,state,uptime,desroot" msgid "link,status,essid,sec,strength,mode,speed" msgid "link,status,essid,sec,strength,mode,speed,auth,bssid,bsstype" msgid "link,vid,over,flags" @@ -285,10 +386,14 @@ msgid "lp_rem_fault" msgid "mac" msgid "mac-address" msgid "mac-prefix" +msgid "max-age" msgid "media" msgid "mode" msgid "modify-aggr" +msgid "modify-bridge" +msgid "modify-simnet" msgid "mtu" +msgid "nick,flags,link,nexthop" msgid "no" msgid "none" msgid "o:px" @@ -301,10 +406,11 @@ msgid "opackets" msgid "opktdist" msgid "output" msgid "over" -msgid "parseable" msgid "parsable" +msgid "parseable" msgid "pause" msgid "pd:si:" +msgid "peer" msgid "peeradv" msgid "persistent" msgid "policy" @@ -312,23 +418,29 @@ msgid "port" msgid "portstate" msgid "possible" msgid "primary" +msgid "priority" msgid "prop" msgid "property" +msgid "protect" msgid "ptype" msgid "r" msgid "random" msgid "rbytes" +msgid "recv" msgid "rem_fault" msgid "remove-aggr" +msgid "remove-bridge" msgid "rename-link" msgid "reset" msgid "reset-linkprop" msgid "root-dir" msgid "scan-wifi" msgid "sec" +msgid "sent" msgid "set" msgid "set-linkprop" msgid "show-aggr" +msgid "show-bridge" msgid "show-dev" msgid "show-ether" msgid "show-etherstub" @@ -337,6 +449,7 @@ msgid "show-linkmap" msgid "show-linkprop" msgid "show-phys" msgid "show-secobj" +msgid "show-simnet" msgid "show-usage" msgid "show-vlan" msgid "show-vnic" @@ -344,9 +457,11 @@ msgid "show-wifi" msgid "slot" msgid "speed" msgid "speed-duplex" +msgid "start" msgid "state" msgid "statistics" msgid "status" +msgid "stop" msgid "strength" msgid "sync" msgid "tR:" @@ -354,13 +469,16 @@ msgid "tR:d:m:n:p:r:v:" msgid "tdps:e:f:" msgid "temporary" msgid "timeout" +msgid "trill" msgid "tx" msgid "unicast" msgid "unknown" msgid "up-aggr" +msgid "up-simnet" msgid "up-vlan" msgid "up-vnic" msgid "value" msgid "vid" msgid "vlan-id" +msgid "xmit" msgid "yes" diff --git a/usr/src/cmd/mdb/common/modules/genunix/genunix.c b/usr/src/cmd/mdb/common/modules/genunix/genunix.c index 465bfcfe8b..28b2794519 100644 --- a/usr/src/cmd/mdb/common/modules/genunix/genunix.c +++ b/usr/src/cmd/mdb/common/modules/genunix/genunix.c @@ -4488,6 +4488,8 @@ static const mdb_dcmd_t dcmds[] = { modent_help }, /* from net.c */ + { "dladm", "?<sub-command> [flags]", "show data link information", + dladm, dladm_help }, { "mi", ":[-p] [-d | -m]", "filter and display MI object or payload", mi }, { "netstat", "[-arv] [-f inet | inet6 | unix] [-P tcp | udp | icmp]", diff --git a/usr/src/cmd/mdb/common/modules/genunix/net.c b/usr/src/cmd/mdb/common/modules/genunix/net.c index 3516278510..d9f4717d7e 100644 --- a/usr/src/cmd/mdb/common/modules/genunix/net.c +++ b/usr/src/cmd/mdb/common/modules/genunix/net.c @@ -49,6 +49,9 @@ #include <inet/rawip_impl.h> #include <inet/mi.h> #include <fs/sockfs/socktpi_impl.h> +#include <net/bridge_impl.h> +#include <io/trill_impl.h> +#include <sys/mac_impl.h> #define ADDR_V6_WIDTH 23 #define ADDR_V4_WIDTH 15 @@ -1254,3 +1257,424 @@ out: mdb_free(cbdata, sizeof (netstat_cb_data_t)); return (status); } + +/* + * "::dladm show-bridge" support + */ +typedef struct { + uint_t opt_l; + uint_t opt_f; + uint_t opt_t; + const char *name; + clock_t lbolt; + boolean_t found; + uint_t nlinks; + uint_t nfwd; + + /* + * These structures are kept inside the 'args' for allocation reasons. + * They're all large data structures (over 1K), and may cause the stack + * to explode. mdb and kmdb will fail in these cases, and thus we + * allocate them from the heap. + */ + trill_inst_t ti; + bridge_link_t bl; + mac_impl_t mi; +} show_bridge_args_t; + +static void +show_vlans(const uint8_t *vlans) +{ + int i, bit; + uint8_t val; + int rstart = -1, rnext = -1; + + for (i = 0; i < BRIDGE_VLAN_ARR_SIZE; i++) { + val = vlans[i]; + if (i == 0) + val &= ~1; + while ((bit = mdb_ffs(val)) != 0) { + bit--; + val &= ~(1 << bit); + bit += i * sizeof (*vlans) * NBBY; + if (bit != rnext) { + if (rnext != -1 && rstart + 1 != rnext) + mdb_printf("-%d", rnext - 1); + if (rstart != -1) + mdb_printf(","); + mdb_printf("%d", bit); + rstart = bit; + } + rnext = bit + 1; + } + } + if (rnext != -1 && rstart + 1 != rnext) + mdb_printf("-%d", rnext - 1); + mdb_printf("\n"); +} + +/* + * This callback is invoked by a walk of the links attached to a bridge. If + * we're showing link details, then they're printed here. If not, then we just + * count up the links for the bridge summary. + */ +static int +do_bridge_links(uintptr_t addr, const void *data, void *ptr) +{ + show_bridge_args_t *args = ptr; + const bridge_link_t *blp = data; + char macaddr[ETHERADDRL * 3]; + const char *name; + + args->nlinks++; + + if (!args->opt_l) + return (WALK_NEXT); + + if (mdb_vread(&args->mi, sizeof (args->mi), + (uintptr_t)blp->bl_mh) == -1) { + mdb_warn("cannot read mac data at %p", blp->bl_mh); + name = "?"; + } else { + name = args->mi.mi_name; + } + + mdb_mac_addr(blp->bl_local_mac, ETHERADDRL, macaddr, + sizeof (macaddr)); + + mdb_printf("%-?p %-16s %-17s %03X %-4d ", addr, name, macaddr, + blp->bl_flags, blp->bl_pvid); + + if (blp->bl_trilldata == NULL) { + switch (blp->bl_state) { + case BLS_BLOCKLISTEN: + name = "BLOCK"; + break; + case BLS_LEARNING: + name = "LEARN"; + break; + case BLS_FORWARDING: + name = "FWD"; + break; + default: + name = "?"; + } + mdb_printf("%-5s ", name); + show_vlans(blp->bl_vlans); + } else { + show_vlans(blp->bl_afs); + } + + return (WALK_NEXT); +} + +/* + * It seems a shame to duplicate this code, but merging it with the link + * printing code above is more trouble than it would be worth. + */ +static void +print_link_name(show_bridge_args_t *args, uintptr_t addr, char sep) +{ + const char *name; + + if (mdb_vread(&args->bl, sizeof (args->bl), addr) == -1) { + mdb_warn("cannot read bridge link at %p", addr); + return; + } + + if (mdb_vread(&args->mi, sizeof (args->mi), + (uintptr_t)args->bl.bl_mh) == -1) { + name = "?"; + } else { + name = args->mi.mi_name; + } + + mdb_printf("%s%c", name, sep); +} + +static int +do_bridge_fwd(uintptr_t addr, const void *data, void *ptr) +{ + show_bridge_args_t *args = ptr; + const bridge_fwd_t *bfp = data; + char macaddr[ETHERADDRL * 3]; + int i; +#define MAX_FWD_LINKS 16 + bridge_link_t *links[MAX_FWD_LINKS]; + uint_t nlinks; + + args->nfwd++; + + if (!args->opt_f) + return (WALK_NEXT); + + if ((nlinks = bfp->bf_nlinks) > MAX_FWD_LINKS) + nlinks = MAX_FWD_LINKS; + + if (mdb_vread(links, sizeof (links[0]) * nlinks, + (uintptr_t)bfp->bf_links) == -1) { + mdb_warn("cannot read bridge forwarding links at %p", + bfp->bf_links); + return (WALK_ERR); + } + + mdb_mac_addr(bfp->bf_dest, ETHERADDRL, macaddr, sizeof (macaddr)); + + mdb_printf("%-?p %-17s ", addr, macaddr); + if (bfp->bf_flags & BFF_LOCALADDR) + mdb_printf("%-7s", "[self]"); + else + mdb_printf("t-%-5d", args->lbolt - bfp->bf_lastheard); + mdb_printf(" %-7u ", bfp->bf_refs); + + if (bfp->bf_trill_nick != 0) { + mdb_printf("%d\n", bfp->bf_trill_nick); + } else { + for (i = 0; i < bfp->bf_nlinks; i++) { + print_link_name(args, (uintptr_t)links[i], + i == bfp->bf_nlinks - 1 ? '\n' : ' '); + } + } + + return (WALK_NEXT); +} + +static int +do_show_bridge(uintptr_t addr, const void *data, void *ptr) +{ + show_bridge_args_t *args = ptr; + bridge_inst_t bi; + const bridge_inst_t *bip; + trill_node_t tn; + trill_sock_t tsp; + trill_nickinfo_t tni; + char bname[MAXLINKNAMELEN]; + char macaddr[ETHERADDRL * 3]; + char *cp; + uint_t nnicks; + int i; + + if (data != NULL) { + bip = data; + } else { + if (mdb_vread(&bi, sizeof (bi), addr) == -1) { + mdb_warn("cannot read bridge instance at %p", addr); + return (WALK_ERR); + } + bip = &bi; + } + + (void) strncpy(bname, bip->bi_name, sizeof (bname) - 1); + bname[MAXLINKNAMELEN - 1] = '\0'; + cp = bname + strlen(bname); + if (cp > bname && cp[-1] == '0') + cp[-1] = '\0'; + + if (args->name != NULL && strcmp(args->name, bname) != 0) + return (WALK_NEXT); + + args->found = B_TRUE; + args->nlinks = args->nfwd = 0; + + if (args->opt_l) { + mdb_printf("%-?s %-16s %-17s %3s %-4s ", "ADDR", "LINK", + "MAC-ADDR", "FLG", "PVID"); + if (bip->bi_trilldata == NULL) + mdb_printf("%-5s %s\n", "STATE", "VLANS"); + else + mdb_printf("%s\n", "FWD-VLANS"); + } + + if (!args->opt_f && !args->opt_t && + mdb_pwalk("list", do_bridge_links, args, + addr + offsetof(bridge_inst_t, bi_links)) != DCMD_OK) + return (WALK_ERR); + + if (args->opt_f) + mdb_printf("%-?s %-17s %-7s %-7s %s\n", "ADDR", "DEST", "TIME", + "REFS", "OUTPUT"); + + if (!args->opt_l && !args->opt_t && + mdb_pwalk("avl", do_bridge_fwd, args, + addr + offsetof(bridge_inst_t, bi_fwd)) != DCMD_OK) + return (WALK_ERR); + + nnicks = 0; + if (bip->bi_trilldata != NULL && !args->opt_l && !args->opt_f) { + if (mdb_vread(&args->ti, sizeof (args->ti), + (uintptr_t)bip->bi_trilldata) == -1) { + mdb_warn("cannot read trill instance at %p", + bip->bi_trilldata); + return (WALK_ERR); + } + if (args->opt_t) + mdb_printf("%-?s %-5s %-17s %s\n", "ADDR", + "NICK", "NEXT-HOP", "LINK"); + for (i = 0; i < RBRIDGE_NICKNAME_MAX; i++) { + if (args->ti.ti_nodes[i] == NULL) + continue; + if (args->opt_t) { + if (mdb_vread(&tn, sizeof (tn), + (uintptr_t)args->ti.ti_nodes[i]) == -1) { + mdb_warn("cannot read trill node %d at " + "%p", i, args->ti.ti_nodes[i]); + return (WALK_ERR); + } + if (mdb_vread(&tni, sizeof (tni), + (uintptr_t)tn.tn_ni) == -1) { + mdb_warn("cannot read trill node info " + "%d at %p", i, tn.tn_ni); + return (WALK_ERR); + } + mdb_mac_addr(tni.tni_adjsnpa, ETHERADDRL, + macaddr, sizeof (macaddr)); + if (tni.tni_nick == args->ti.ti_nick) { + (void) strcpy(macaddr, "[self]"); + } + mdb_printf("%-?p %-5u %-17s ", + args->ti.ti_nodes[i], tni.tni_nick, + macaddr); + if (tn.tn_tsp != NULL) { + if (mdb_vread(&tsp, sizeof (tsp), + (uintptr_t)tn.tn_tsp) == -1) { + mdb_warn("cannot read trill " + "socket info at %p", + tn.tn_tsp); + return (WALK_ERR); + } + if (tsp.ts_link != NULL) { + print_link_name(args, + (uintptr_t)tsp.ts_link, + '\n'); + continue; + } + } + mdb_printf("--\n"); + } else { + nnicks++; + } + } + } else { + if (args->opt_t) + mdb_printf("bridge is not running TRILL\n"); + } + + if (!args->opt_l && !args->opt_f && !args->opt_t) { + mdb_printf("%-?p %-7s %-16s %-7u %-7u", addr, + bip->bi_trilldata == NULL ? "stp" : "trill", bname, + args->nlinks, args->nfwd); + if (bip->bi_trilldata != NULL) + mdb_printf(" %-7u %u\n", nnicks, args->ti.ti_nick); + else + mdb_printf(" %-7s %s\n", "--", "--"); + } + return (WALK_NEXT); +} + +static int +dladm_show_bridge(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) +{ + show_bridge_args_t *args; + GElf_Sym sym; + int i; + + args = mdb_zalloc(sizeof (*args), UM_SLEEP); + + i = mdb_getopts(argc, argv, + 'l', MDB_OPT_SETBITS, 1, &args->opt_l, + 'f', MDB_OPT_SETBITS, 1, &args->opt_f, + 't', MDB_OPT_SETBITS, 1, &args->opt_t, + NULL); + + argc -= i; + argv += i; + + if (argc > 1 || (argc == 1 && argv[0].a_type != MDB_TYPE_STRING)) { + mdb_free(args, sizeof (*args)); + return (DCMD_USAGE); + } + if (argc == 1) + args->name = argv[0].a_un.a_str; + + if (mdb_readvar(&args->lbolt, + mdb_prop_postmortem ? "panic_lbolt" : "lbolt") == -1) { + mdb_warn("failed to read lbolt"); + goto err; + } + + if (flags & DCMD_ADDRSPEC) { + if (args->name != NULL) { + mdb_printf("bridge name and address are mutually " + "exclusive\n"); + goto err; + } + if (!args->opt_l && !args->opt_f && !args->opt_t) + mdb_printf("%-?s %-7s %-16s %-7s %-7s\n", "ADDR", + "PROTECT", "NAME", "NLINKS", "NFWD"); + if (do_show_bridge(addr, NULL, args) != WALK_NEXT) + goto err; + mdb_free(args, sizeof (*args)); + return (DCMD_OK); + } else { + if ((args->opt_l || args->opt_f || args->opt_t) && + args->name == NULL) { + mdb_printf("need bridge name or address with -[lft]\n"); + goto err; + } + if (mdb_lookup_by_obj("bridge", "inst_list", &sym) == -1) { + mdb_warn("failed to find 'bridge`inst_list'"); + goto err; + } + if (!args->opt_l && !args->opt_f && !args->opt_t) + mdb_printf("%-?s %-7s %-16s %-7s %-7s %-7s %s\n", + "ADDR", "PROTECT", "NAME", "NLINKS", "NFWD", + "NNICKS", "NICK"); + if (mdb_pwalk("list", do_show_bridge, args, + (uintptr_t)sym.st_value) != DCMD_OK) + goto err; + if (!args->found && args->name != NULL) { + mdb_printf("bridge instance %s not found\n", + args->name); + goto err; + } + mdb_free(args, sizeof (*args)); + return (DCMD_OK); + } + +err: + mdb_free(args, sizeof (*args)); + return (DCMD_ERR); +} + +/* + * Support for the "::dladm" dcmd + */ +int +dladm(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) +{ + if (argc < 1 || argv[0].a_type != MDB_TYPE_STRING) + return (DCMD_USAGE); + + /* + * This could be a bit more elaborate, once we support more of the + * dladm show-* subcommands. + */ + argc--; + argv++; + if (strcmp(argv[-1].a_un.a_str, "show-bridge") == 0) + return (dladm_show_bridge(addr, flags, argc, argv)); + + return (DCMD_USAGE); +} + +void +dladm_help(void) +{ + mdb_printf("Subcommands:\n" + " show-bridge [-flt] [<name>]\n" + "\t Show bridge information; -l for links and -f for " + "forwarding\n" + "\t entries, and -t for TRILL nicknames. Address is required " + "if name\n" + "\t is not specified.\n"); +} diff --git a/usr/src/cmd/mdb/common/modules/genunix/net.h b/usr/src/cmd/mdb/common/modules/genunix/net.h index 59df026bd4..f2d441e78c 100644 --- a/usr/src/cmd/mdb/common/modules/genunix/net.h +++ b/usr/src/cmd/mdb/common/modules/genunix/net.h @@ -19,15 +19,13 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _NET_H #define _NET_H -#pragma ident "%Z%%M% %I% %E% SMI" - #ifdef __cplusplus extern "C" { #endif @@ -56,6 +54,8 @@ extern int udp_stacks_walk_step(mdb_walk_state_t *); extern int sonode(uintptr_t, uint_t, int, const mdb_arg_t *); extern int mi(uintptr_t, uint_t, int, const mdb_arg_t *); extern int netstat(uintptr_t, uint_t, int, const mdb_arg_t *); +extern int dladm(uintptr_t, uint_t, int, const mdb_arg_t *); +extern void dladm_help(void); #ifdef __cplusplus } diff --git a/usr/src/cmd/rcm_daemon/Makefile.com b/usr/src/cmd/rcm_daemon/Makefile.com index c72efbeed2..d5a7c72ff7 100644 --- a/usr/src/cmd/rcm_daemon/Makefile.com +++ b/usr/src/cmd/rcm_daemon/Makefile.com @@ -56,7 +56,8 @@ COMMON_MOD_SRC = \ $(COMMON)/pool_rcm.c \ $(COMMON)/mpxio_rcm.c \ $(COMMON)/ip_anon_rcm.c \ - $(COMMON)/svm_rcm.c + $(COMMON)/svm_rcm.c \ + $(COMMON)/bridge_rcm.c sparc_MOD_SRC = $(COMMON)/ttymux_rcm.c @@ -79,7 +80,8 @@ COMMON_MOD_OBJ = \ pool_rcm.o \ mpxio_rcm.o \ ip_anon_rcm.o \ - svm_rcm.o + svm_rcm.o \ + bridge_rcm.o sparc_MOD_OBJ = ttymux_rcm.o @@ -98,7 +100,8 @@ COMMON_RCM_MODS = \ SUNW_pool_rcm.so \ SUNW_mpxio_rcm.so \ SUNW_ip_anon_rcm.so \ - SUNW_svm_rcm.so + SUNW_svm_rcm.so \ + SUNW_bridge_rcm.so sparc_RCM_MODS = SUNW_ttymux_rcm.so @@ -128,6 +131,7 @@ SUNW_vnic_rcm.so := LDLIBS_MODULES += -L$(ROOT)/lib -ldladm SUNW_aggr_rcm.so := LDLIBS_MODULES += -L$(ROOT)/lib -ldladm SUNW_ip_rcm.so := LDLIBS_MODULES += -L$(ROOT)/lib -linetutil -ldladm -lipmp SUNW_ip_anon_rcm.so := LDLIBS_MODULES += -L$(ROOT)/lib -linetutil +SUNW_bridge_rcm.so := LDLIBS_MODULES += -L$(ROOT)/lib -ldladm LDLIBS += -lgen -lelf -lrcm -lnvpair -ldevinfo -lnsl -lsocket diff --git a/usr/src/cmd/rcm_daemon/common/bridge_rcm.c b/usr/src/cmd/rcm_daemon/common/bridge_rcm.c new file mode 100644 index 0000000000..60188c2fda --- /dev/null +++ b/usr/src/cmd/rcm_daemon/common/bridge_rcm.c @@ -0,0 +1,898 @@ +/* + * 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. + */ + +/* + * This RCM module adds support to the RCM framework for Bridge links + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <sys/types.h> +#include <synch.h> +#include <assert.h> +#include <strings.h> +#include "rcm_module.h" +#include <libintl.h> +#include <libdllink.h> +#include <libdlbridge.h> +#include <libdlpi.h> + +/* + * Definitions + */ +#ifndef lint +#define _(x) gettext(x) +#else +#define _(x) x +#endif + +/* Some generic well-knowns and defaults used in this module */ +#define RCM_LINK_PREFIX "SUNW_datalink" /* RCM datalink name prefix */ +#define RCM_LINK_RESOURCE_MAX (13 + LINKID_STR_WIDTH) + +/* Bridge Cache state flags */ +typedef enum { + CACHE_NODE_STALE = 0x1, /* stale cached data */ + CACHE_NODE_NEW = 0x2, /* new cached nodes */ + CACHE_NODE_OFFLINED = 0x4 /* nodes offlined */ +} cache_node_state_t; + +/* Network Cache lookup options */ +#define CACHE_NO_REFRESH 0x1 /* cache refresh not needed */ +#define CACHE_REFRESH 0x2 /* refresh cache */ + +/* Cache element */ +typedef struct link_cache { + struct link_cache *vc_next; /* next cached resource */ + struct link_cache *vc_prev; /* prev cached resource */ + char *vc_resource; /* resource name */ + datalink_id_t vc_linkid; /* linkid */ + cache_node_state_t vc_state; /* cache state flags */ + char vc_bridge[MAXLINKNAMELEN]; +} link_cache_t; + +/* + * Global cache for network Bridges + */ +static link_cache_t cache_head; +static link_cache_t cache_tail; +static mutex_t cache_lock; +static boolean_t events_registered = B_FALSE; + +static dladm_handle_t dld_handle = NULL; + +/* + * RCM module interface prototypes + */ +static int bridge_register(rcm_handle_t *); +static int bridge_unregister(rcm_handle_t *); +static int bridge_get_info(rcm_handle_t *, char *, id_t, uint_t, + char **, char **, nvlist_t *, rcm_info_t **); +static int bridge_suspend(rcm_handle_t *, char *, id_t, + timespec_t *, uint_t, char **, rcm_info_t **); +static int bridge_resume(rcm_handle_t *, char *, id_t, uint_t, + char **, rcm_info_t **); +static int bridge_offline(rcm_handle_t *, char *, id_t, uint_t, + char **, rcm_info_t **); +static int bridge_undo_offline(rcm_handle_t *, char *, id_t, + uint_t, char **, rcm_info_t **); +static int bridge_remove(rcm_handle_t *, char *, id_t, uint_t, + char **, rcm_info_t **); +static int bridge_notify_event(rcm_handle_t *, char *, id_t, + uint_t, char **, nvlist_t *, rcm_info_t **); +static int bridge_configure(rcm_handle_t *, datalink_id_t); + +/* Module private routines */ +static void cache_free(void); +static int cache_update(rcm_handle_t *); +static void cache_remove(link_cache_t *); +static void node_free(link_cache_t *); +static void cache_insert(link_cache_t *); +static link_cache_t *cache_lookup(rcm_handle_t *, char *, uint_t); +static char *bridge_usage(link_cache_t *); +static void bridge_log_err(datalink_id_t, char **, char *); + +/* Module-Private data */ +static struct rcm_mod_ops bridge_ops = +{ + RCM_MOD_OPS_VERSION, + bridge_register, + bridge_unregister, + bridge_get_info, + bridge_suspend, + bridge_resume, + bridge_offline, + bridge_undo_offline, + bridge_remove, + NULL, + NULL, + bridge_notify_event +}; + +/* + * rcm_mod_init() - Update registrations, and return the ops structure. + */ +struct rcm_mod_ops * +rcm_mod_init(void) +{ + dladm_status_t status; + char errmsg[DLADM_STRSIZE]; + + rcm_log_message(RCM_TRACE1, "Bridge: mod_init\n"); + + cache_head.vc_next = &cache_tail; + cache_head.vc_prev = NULL; + cache_tail.vc_prev = &cache_head; + cache_tail.vc_next = NULL; + (void) mutex_init(&cache_lock, 0, NULL); + + if ((status = dladm_open(&dld_handle)) != DLADM_STATUS_OK) { + rcm_log_message(RCM_WARNING, + "Bridge: cannot open datalink handle: %s\n", + dladm_status2str(status, errmsg)); + return (NULL); + } + + /* Return the ops vectors */ + return (&bridge_ops); +} + +/* + * rcm_mod_info() - Return a string describing this module. + */ +const char * +rcm_mod_info(void) +{ + rcm_log_message(RCM_TRACE1, "Bridge: mod_info\n"); + + return ("Bridge module version 1.0"); +} + +/* + * rcm_mod_fini() - Destroy the network Bridge cache. + */ +int +rcm_mod_fini(void) +{ + rcm_log_message(RCM_TRACE1, "Bridge: mod_fini\n"); + + /* + * Note that bridge_unregister() does not seem to be called anywhere, + * therefore we free the cache nodes here. In theory we should call + * rcm_register_interest() for each node before we free it, but the + * framework does not provide the rcm_handle to allow us to do so. + */ + cache_free(); + (void) mutex_destroy(&cache_lock); + + dladm_close(dld_handle); + return (RCM_SUCCESS); +} + +/* + * bridge_register() - Make sure the cache is properly sync'ed, and its + * registrations are in order. + */ +static int +bridge_register(rcm_handle_t *hd) +{ + int retv; + + rcm_log_message(RCM_TRACE1, "Bridge: register\n"); + + if ((retv = cache_update(hd)) != RCM_SUCCESS) + return (retv); + + /* + * Need to register interest in all new resources + * getting attached, so we get attach event notifications + */ + if (!events_registered) { + retv = rcm_register_event(hd, RCM_RESOURCE_LINK_NEW, 0, NULL); + if (retv != RCM_SUCCESS) { + rcm_log_message(RCM_ERROR, + _("Bridge: failed to register %s\n"), + RCM_RESOURCE_LINK_NEW); + } else { + rcm_log_message(RCM_DEBUG, "Bridge: registered %s\n", + RCM_RESOURCE_LINK_NEW); + events_registered = B_TRUE; + } + } + + return (retv); +} + +/* + * bridge_unregister() - Walk the cache, unregistering all the links. + */ +static int +bridge_unregister(rcm_handle_t *hd) +{ + link_cache_t *node; + int retv = RCM_SUCCESS; + + rcm_log_message(RCM_TRACE1, "Bridge: unregister\n"); + + /* Walk the cache, unregistering everything */ + (void) mutex_lock(&cache_lock); + node = cache_head.vc_next; + while (node != &cache_tail) { + retv = rcm_unregister_interest(hd, node->vc_resource, 0); + if (retv != RCM_SUCCESS) + break; + cache_remove(node); + node_free(node); + node = cache_head.vc_next; + } + (void) mutex_unlock(&cache_lock); + if (retv != RCM_SUCCESS) { + rcm_log_message(RCM_ERROR, + _("Bridge: failed to unregister %s\n"), node->vc_resource); + return (retv); + } + + /* + * Unregister interest in all new resources + */ + if (events_registered) { + retv = rcm_unregister_event(hd, RCM_RESOURCE_LINK_NEW, 0); + if (retv != RCM_SUCCESS) { + rcm_log_message(RCM_ERROR, + _("Bridge: failed to unregister %s\n"), + RCM_RESOURCE_LINK_NEW); + } else { + rcm_log_message(RCM_DEBUG, "Bridge: unregistered %s\n", + RCM_RESOURCE_LINK_NEW); + events_registered = B_FALSE; + } + } + + return (retv); +} + +/* + * bridge_offline() - Offline the bridge on a specific link. + */ +static int +bridge_offline(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags, + char **errorp, rcm_info_t **info) +{ + link_cache_t *node; + dladm_status_t status; + + rcm_log_message(RCM_TRACE1, "Bridge: offline(%s)\n", rsrc); + + /* Lock the cache and lookup the resource */ + (void) mutex_lock(&cache_lock); + node = cache_lookup(hd, rsrc, CACHE_REFRESH); + if (node == NULL) { + /* should not happen because the resource is registered. */ + bridge_log_err(DATALINK_INVALID_LINKID, errorp, + "unrecognized resource"); + (void) mutex_unlock(&cache_lock); + return (RCM_SUCCESS); + } + + /* Check if it's a query */ + if (flags & RCM_QUERY) { + rcm_log_message(RCM_TRACE1, + "Bridge: offline query succeeded(%s)\n", rsrc); + (void) mutex_unlock(&cache_lock); + return (RCM_SUCCESS); + } + + status = dladm_bridge_setlink(dld_handle, node->vc_linkid, ""); + if (status != DLADM_STATUS_OK) { + bridge_log_err(node->vc_linkid, errorp, "offline failed"); + (void) mutex_unlock(&cache_lock); + return (RCM_FAILURE); + } + + node->vc_state |= CACHE_NODE_OFFLINED; + + rcm_log_message(RCM_TRACE1, "Bridge: Offline succeeded(%s %s)\n", rsrc, + node->vc_bridge); + (void) mutex_unlock(&cache_lock); + return (RCM_SUCCESS); +} + +/* + * bridge_undo_offline() - Undo offline of a previously offlined node. + */ +/*ARGSUSED*/ +static int +bridge_undo_offline(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags, + char **errorp, rcm_info_t **info) +{ + link_cache_t *node; + dladm_status_t status; + char errmsg[DLADM_STRSIZE]; + + rcm_log_message(RCM_TRACE1, "Bridge: online(%s)\n", rsrc); + + (void) mutex_lock(&cache_lock); + node = cache_lookup(hd, rsrc, CACHE_NO_REFRESH); + if (node == NULL) { + bridge_log_err(DATALINK_INVALID_LINKID, errorp, "no such link"); + (void) mutex_unlock(&cache_lock); + errno = ENOENT; + return (RCM_FAILURE); + } + + /* Check if no attempt should be made to online the link here */ + if (!(node->vc_state & CACHE_NODE_OFFLINED)) { + bridge_log_err(node->vc_linkid, errorp, "link not offlined"); + (void) mutex_unlock(&cache_lock); + errno = ENOTSUP; + return (RCM_SUCCESS); + } + + /* + * Try to bring on an offlined bridge link. + */ + status = dladm_bridge_setlink(dld_handle, node->vc_linkid, + node->vc_bridge); + if (status != DLADM_STATUS_OK) { + /* + * Print a warning message. + */ + rcm_log_message(RCM_WARNING, + _("Bridge: Bridge online failed %u %s: %s\n"), + node->vc_linkid, node->vc_bridge, + dladm_status2str(status, errmsg)); + } + + node->vc_state &= ~CACHE_NODE_OFFLINED; + rcm_log_message(RCM_TRACE1, "Bridge: online succeeded(%s)\n", rsrc); + (void) mutex_unlock(&cache_lock); + return (RCM_SUCCESS); +} + +/* + * bridge_get_info() - Gather usage information for this resource. + */ +/*ARGSUSED*/ +int +bridge_get_info(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags, + char **usagep, char **errorp, nvlist_t *props, rcm_info_t **info) +{ + link_cache_t *node; + + rcm_log_message(RCM_TRACE1, "Bridge: get_info(%s)\n", rsrc); + + (void) mutex_lock(&cache_lock); + node = cache_lookup(hd, rsrc, CACHE_REFRESH); + if (node == NULL) { + rcm_log_message(RCM_INFO, + _("Bridge: get_info(%s) unrecognized resource\n"), rsrc); + (void) mutex_unlock(&cache_lock); + errno = ENOENT; + return (RCM_FAILURE); + } + + *usagep = bridge_usage(node); + (void) mutex_unlock(&cache_lock); + if (*usagep == NULL) { + /* most likely malloc failure */ + rcm_log_message(RCM_ERROR, + _("Bridge: get_info(%s) malloc failure\n"), rsrc); + (void) mutex_unlock(&cache_lock); + errno = ENOMEM; + return (RCM_FAILURE); + } + + /* Set client/role properties */ + (void) nvlist_add_string(props, RCM_CLIENT_NAME, "Bridge"); + + rcm_log_message(RCM_TRACE1, "Bridge: get_info(%s) info = %s\n", + rsrc, *usagep); + return (RCM_SUCCESS); +} + +/* + * bridge_suspend() - Nothing to do, always okay + */ +/*ARGSUSED*/ +static int +bridge_suspend(rcm_handle_t *hd, char *rsrc, id_t id, timespec_t *interval, + uint_t flags, char **errorp, rcm_info_t **info) +{ + rcm_log_message(RCM_TRACE1, "Bridge: suspend(%s)\n", rsrc); + return (RCM_SUCCESS); +} + +/* + * bridge_resume() - Nothing to do, always okay + */ +/*ARGSUSED*/ +static int +bridge_resume(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags, + char **errorp, rcm_info_t **info) +{ + rcm_log_message(RCM_TRACE1, "Bridge: resume(%s)\n", rsrc); + return (RCM_SUCCESS); +} + +/* + * bridge_remove() - remove a resource from cache + */ +/*ARGSUSED*/ +static int +bridge_remove(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags, + char **errorp, rcm_info_t **info) +{ + link_cache_t *node; + + rcm_log_message(RCM_TRACE1, "Bridge: remove(%s)\n", rsrc); + + (void) mutex_lock(&cache_lock); + node = cache_lookup(hd, rsrc, CACHE_NO_REFRESH); + if (node == NULL) { + rcm_log_message(RCM_INFO, + _("Bridge: remove(%s) unrecognized resource\n"), rsrc); + (void) mutex_unlock(&cache_lock); + errno = ENOENT; + return (RCM_FAILURE); + } + + /* remove the cached entry for the resource */ + rcm_log_message(RCM_TRACE2, + "Bridge: remove succeeded(%s, %s)\n", rsrc, node->vc_bridge); + cache_remove(node); + (void) mutex_unlock(&cache_lock); + + node_free(node); + return (RCM_SUCCESS); +} + +/* + * bridge_notify_event - Project private implementation to receive new resource + * events. It intercepts all new resource events. If the + * new resource is a network resource, pass up a notify + * for it too. The new resource need not be cached, since + * it is done at register again. + */ +/*ARGSUSED*/ +static int +bridge_notify_event(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags, + char **errorp, nvlist_t *nvl, rcm_info_t **info) +{ + nvpair_t *nvp = NULL; + datalink_id_t linkid; + uint64_t id64; + int rv, lastrv; + + rcm_log_message(RCM_TRACE1, "Bridge: notify_event(%s)\n", rsrc); + + if (strcmp(rsrc, RCM_RESOURCE_LINK_NEW) != 0) { + bridge_log_err(DATALINK_INVALID_LINKID, errorp, + "unrecognized event"); + errno = EINVAL; + return (RCM_FAILURE); + } + + /* Update cache to reflect latest Bridges */ + if ((lastrv = cache_update(hd)) != RCM_SUCCESS) { + bridge_log_err(DATALINK_INVALID_LINKID, errorp, + "private Cache update failed"); + return (lastrv); + } + + /* + * Try best to recover all configuration. + */ + rcm_log_message(RCM_DEBUG, "Bridge: process_nvlist\n"); + while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { + if (strcmp(nvpair_name(nvp), RCM_NV_LINKID) != 0) + continue; + + if (nvpair_value_uint64(nvp, &id64) != 0) { + bridge_log_err(DATALINK_INVALID_LINKID, errorp, + "cannot get linkid"); + lastrv = RCM_FAILURE; + continue; + } + + linkid = (datalink_id_t)id64; + if ((rv = bridge_configure(hd, linkid)) != RCM_SUCCESS) { + bridge_log_err(linkid, errorp, "configuring failed"); + lastrv = rv; + } + } + + rcm_log_message(RCM_TRACE1, + "Bridge: notify_event: link configuration complete\n"); + return (lastrv); +} + +/* + * bridge_usage - Determine the usage of a link. + * The returned buffer is owned by caller, and the caller + * must free it up when done. + */ +static char * +bridge_usage(link_cache_t *node) +{ + char *buf; + const char *fmt; + char errmsg[DLADM_STRSIZE]; + char name[MAXLINKNAMELEN]; + char bridge[MAXLINKNAMELEN]; + dladm_status_t status; + + rcm_log_message(RCM_TRACE2, "Bridge: usage(%s)\n", node->vc_resource); + + assert(MUTEX_HELD(&cache_lock)); + + status = dladm_datalink_id2info(dld_handle, node->vc_linkid, NULL, + NULL, NULL, name, sizeof (name)); + + if (status != DLADM_STATUS_OK) { + rcm_log_message(RCM_ERROR, + _("Bridge: usage(%s) get link name failure(%s)\n"), + node->vc_resource, dladm_status2str(status, errmsg)); + return (NULL); + } + + (void) dladm_bridge_getlink(dld_handle, node->vc_linkid, bridge, + sizeof (bridge)); + + if (node->vc_state & CACHE_NODE_OFFLINED) + fmt = _("%1$s offlined"); + else if (bridge[0] == '\0') + fmt = _("%1$s not bridged"); + else + fmt = _("%1$s bridge: %2$s"); + + (void) asprintf(&buf, fmt, name, bridge); + + rcm_log_message(RCM_TRACE2, "Bridge: usage (%s) info = %s\n", + node->vc_resource, buf); + + return (buf); +} + +/* + * Cache management routines, all cache management functions should be + * be called with cache_lock held. + */ + +/* + * cache_lookup() - Get a cache node for a resource. + * Call with cache lock held. + * + * This ensures that the cache is consistent with the system state and + * returns a pointer to the cache element corresponding to the resource. + */ +static link_cache_t * +cache_lookup(rcm_handle_t *hd, char *rsrc, uint_t options) +{ + link_cache_t *node; + + rcm_log_message(RCM_TRACE2, "Bridge: cache lookup(%s)\n", rsrc); + + assert(MUTEX_HELD(&cache_lock)); + if (options & CACHE_REFRESH) { + /* drop lock since update locks cache again */ + (void) mutex_unlock(&cache_lock); + (void) cache_update(hd); + (void) mutex_lock(&cache_lock); + } + + node = cache_head.vc_next; + for (; node != &cache_tail; node = node->vc_next) { + if (strcmp(rsrc, node->vc_resource) == 0) { + rcm_log_message(RCM_TRACE2, + "Bridge: cache lookup succeeded(%s, %s)\n", rsrc, + node->vc_bridge); + return (node); + } + } + return (NULL); +} + +/* + * node_free - Free a node from the cache + */ +static void +node_free(link_cache_t *node) +{ + if (node != NULL) { + free(node->vc_resource); + free(node); + } +} + +/* + * cache_insert - Insert a resource node in cache + */ +static void +cache_insert(link_cache_t *node) +{ + assert(MUTEX_HELD(&cache_lock)); + + /* insert at the head for best performance */ + node->vc_next = cache_head.vc_next; + node->vc_prev = &cache_head; + + node->vc_next->vc_prev = node; + node->vc_prev->vc_next = node; +} + +/* + * cache_remove() - Remove a resource node from cache. + */ +static void +cache_remove(link_cache_t *node) +{ + assert(MUTEX_HELD(&cache_lock)); + node->vc_next->vc_prev = node->vc_prev; + node->vc_prev->vc_next = node->vc_next; + node->vc_next = NULL; + node->vc_prev = NULL; +} + +typedef struct bridge_update_arg_s { + rcm_handle_t *hd; + int retval; +} bridge_update_arg_t; + +/* + * bridge_update() - Update physical interface properties + */ +static int +bridge_update(dladm_handle_t handle, datalink_id_t linkid, void *arg) +{ + bridge_update_arg_t *bua = arg; + rcm_handle_t *hd = bua->hd; + link_cache_t *node; + char *rsrc; + dladm_status_t status; + char errmsg[DLADM_STRSIZE]; + char bridge[MAXLINKNAMELEN]; + int ret = RCM_FAILURE; + + rcm_log_message(RCM_TRACE2, "Bridge: bridge_update(%u)\n", linkid); + + assert(MUTEX_HELD(&cache_lock)); + status = dladm_bridge_getlink(dld_handle, linkid, bridge, + sizeof (bridge)); + if (status != DLADM_STATUS_OK) { + rcm_log_message(RCM_TRACE1, + "Bridge: no bridge information for %u (%s)\n", + linkid, dladm_status2str(status, errmsg)); + return (DLADM_WALK_CONTINUE); + } + + (void) asprintf(&rsrc, "%s/%u", RCM_LINK_PREFIX, linkid); + if (rsrc == NULL) { + rcm_log_message(RCM_ERROR, + _("Bridge: allocation failure: %s %u: %s\n"), + bridge, linkid, strerror(errno)); + goto done; + } + + node = cache_lookup(hd, rsrc, CACHE_NO_REFRESH); + if (node != NULL) { + rcm_log_message(RCM_DEBUG, "Bridge: %s already registered\n", + rsrc); + free(rsrc); + node->vc_state &= ~CACHE_NODE_STALE; + } else { + rcm_log_message(RCM_DEBUG, + "Bridge: %s is a new resource (bridge %s)\n", + rsrc, bridge); + if ((node = calloc(1, sizeof (link_cache_t))) == NULL) { + free(rsrc); + rcm_log_message(RCM_ERROR, _("Bridge: calloc: %s\n"), + strerror(errno)); + goto done; + } + + node->vc_resource = rsrc; + node->vc_linkid = linkid; + (void) strlcpy(node->vc_bridge, bridge, + sizeof (node->vc_bridge)); + node->vc_state |= CACHE_NODE_NEW; + cache_insert(node); + } + + rcm_log_message(RCM_TRACE3, "Bridge: bridge_update: succeeded(%u %s)\n", + linkid, node->vc_bridge); + ret = RCM_SUCCESS; +done: + bua->retval = ret; + return (ret == RCM_SUCCESS ? DLADM_WALK_CONTINUE : + DLADM_WALK_TERMINATE); +} + +/* + * cache_update() - Update cache with latest interface info + */ +static int +cache_update(rcm_handle_t *hd) +{ + link_cache_t *node, *nnode; + int rv, lastrv; + bridge_update_arg_t bua; + + rcm_log_message(RCM_TRACE2, "Bridge: cache_update\n"); + + (void) mutex_lock(&cache_lock); + + /* first we walk the entire cache, marking each entry stale */ + node = cache_head.vc_next; + for (; node != &cache_tail; node = node->vc_next) + node->vc_state |= CACHE_NODE_STALE; + + /* now walk the links and update all of the entries */ + bua.hd = hd; + bua.retval = RCM_SUCCESS; + (void) dladm_walk_datalink_id(bridge_update, dld_handle, &bua, + DATALINK_CLASS_AGGR | DATALINK_CLASS_PHYS | + DATALINK_CLASS_ETHERSTUB, DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE); + lastrv = bua.retval; + + /* + * Continue to delete all stale nodes from the cache even if the walk + * above failed. Unregister links that are not offlined and still in + * the cache. + */ + for (node = cache_head.vc_next; node != &cache_tail; node = nnode) { + nnode = node->vc_next; + + if (node->vc_state & CACHE_NODE_STALE) { + (void) rcm_unregister_interest(hd, node->vc_resource, + 0); + rcm_log_message(RCM_DEBUG, + "Bridge: unregistered %s %s\n", + node->vc_resource, node->vc_bridge); + cache_remove(node); + node_free(node); + continue; + } + + if (!(node->vc_state & CACHE_NODE_NEW)) + continue; + + rv = rcm_register_interest(hd, node->vc_resource, 0, NULL); + if (rv != RCM_SUCCESS) { + rcm_log_message(RCM_ERROR, + _("Bridge: failed to register %s\n"), + node->vc_resource); + lastrv = rv; + } else { + rcm_log_message(RCM_DEBUG, "Bridge: registered %s\n", + node->vc_resource); + node->vc_state &= ~CACHE_NODE_NEW; + } + } + + (void) mutex_unlock(&cache_lock); + return (lastrv); +} + +/* + * cache_free() - Empty the cache + */ +static void +cache_free(void) +{ + link_cache_t *node; + + rcm_log_message(RCM_TRACE2, "Bridge: cache_free\n"); + + (void) mutex_lock(&cache_lock); + node = cache_head.vc_next; + while (node != &cache_tail) { + cache_remove(node); + node_free(node); + node = cache_head.vc_next; + } + (void) mutex_unlock(&cache_lock); +} + +/* + * bridge_log_err() - RCM error log wrapper + */ +static void +bridge_log_err(datalink_id_t linkid, char **errorp, char *errmsg) +{ + char link[MAXLINKNAMELEN]; + char errstr[DLADM_STRSIZE]; + dladm_status_t status; + char *error; + + link[0] = '\0'; + if (linkid != DATALINK_INVALID_LINKID) { + char rsrc[RCM_LINK_RESOURCE_MAX]; + + (void) snprintf(rsrc, sizeof (rsrc), "%s/%u", + RCM_LINK_PREFIX, linkid); + + rcm_log_message(RCM_ERROR, _("Bridge: %s(%s)\n"), errmsg, rsrc); + if ((status = dladm_datalink_id2info(dld_handle, linkid, NULL, + NULL, NULL, link, sizeof (link))) != DLADM_STATUS_OK) { + rcm_log_message(RCM_WARNING, + _("Bridge: cannot get link name for (%s) %s\n"), + rsrc, dladm_status2str(status, errstr)); + } + } else { + rcm_log_message(RCM_ERROR, _("Bridge: %s\n"), errmsg); + } + + if (link[0] != '\0') + (void) asprintf(&error, _("Bridge: %s(%s)"), errmsg, link); + else + (void) asprintf(&error, _("Bridge: %s"), errmsg); + + if (errorp != NULL) + *errorp = error; +} + +/* + * bridge_configure() - Configure bridge on a physical link after it attaches + */ +static int +bridge_configure(rcm_handle_t *hd, datalink_id_t linkid) +{ + char rsrc[RCM_LINK_RESOURCE_MAX]; + link_cache_t *node; + char bridge[MAXLINKNAMELEN]; + + /* Check for the bridge links in the cache */ + (void) snprintf(rsrc, sizeof (rsrc), "%s/%u", RCM_LINK_PREFIX, linkid); + + rcm_log_message(RCM_TRACE2, "Bridge: bridge_configure(%s)\n", rsrc); + + /* Check if the link is new or was previously offlined */ + (void) mutex_lock(&cache_lock); + if (((node = cache_lookup(hd, rsrc, CACHE_REFRESH)) != NULL) && + (!(node->vc_state & CACHE_NODE_OFFLINED))) { + rcm_log_message(RCM_TRACE2, + "Bridge: Skipping configured interface(%s)\n", rsrc); + (void) mutex_unlock(&cache_lock); + return (RCM_SUCCESS); + } + (void) mutex_unlock(&cache_lock); + + /* clear out previous bridge, if any */ + if (dladm_bridge_getlink(dld_handle, linkid, bridge, sizeof (bridge)) == + DLADM_STATUS_OK) { + if (bridge[0] != '\0') + (void) dladm_bridge_setlink(dld_handle, linkid, ""); + } + + /* now set up the new one */ + if (node != NULL && node->vc_bridge[0] != '\0' && + dladm_bridge_setlink(dld_handle, linkid, node->vc_bridge) != + DLADM_STATUS_OK) + return (RCM_FAILURE); + else + return (RCM_SUCCESS); +} diff --git a/usr/src/cmd/svc/milestone/net-nwam b/usr/src/cmd/svc/milestone/net-nwam index f475438473..ac47343dd7 100644 --- a/usr/src/cmd/svc/milestone/net-nwam +++ b/usr/src/cmd/svc/milestone/net-nwam @@ -44,6 +44,9 @@ case "$1" in if smf_is_globalzone; then net_reconfigure || exit $SMF_EXIT_ERR_CONFIG + # Update PVID on interfaces configured with VLAN 1 + update_pvid + # # Upgrade handling. The upgrade file consists of a series # of dladm(1M) commands. Note that after we are done, we diff --git a/usr/src/cmd/svc/milestone/net-physical b/usr/src/cmd/svc/milestone/net-physical index 15b91edc9d..7fabad7938 100644 --- a/usr/src/cmd/svc/milestone/net-physical +++ b/usr/src/cmd/svc/milestone/net-physical @@ -46,6 +46,9 @@ smf_netstrategy if smf_is_globalzone; then net_reconfigure || exit $SMF_EXIT_ERR_CONFIG + # Update PVID on interfaces configured with VLAN 1 + update_pvid + # # Upgrade handling. The upgrade file consists of a series of dladm(1M) # commands. Note that after we are done, we cannot rename the upgrade diff --git a/usr/src/cmd/svc/shell/net_include.sh b/usr/src/cmd/svc/shell/net_include.sh index e8d1c75f62..e8379c48c1 100644 --- a/usr/src/cmd/svc/shell/net_include.sh +++ b/usr/src/cmd/svc/shell/net_include.sh @@ -715,3 +715,58 @@ net_reconfigure () /sbin/dladm init-phys return 0 } + +# +# Check for use of the default "Port VLAN Identifier" (PVID) -- VLAN 1. +# If there is one for a given interface, then warn the user and force the +# PVID to zero (if it's not already set). We do this by generating a list +# of interfaces with VLAN 1 in use first, and then parsing out the +# corresponding base datalink entries to check for ones without a +# "default_tag" property. +# +update_pvid() +{ + datalink=/etc/dladm/datalink.conf + + ( + # Find datalinks using VLAN 1 explicitly + # configured by dladm + /usr/bin/nawk ' + /^#/ || NF < 2 { next } + { linkdata[$1]=$2; } + /;vid=int,1;/ { + sub(/.*;linkover=int,/, "", $2); + sub(/;.*/, "", $2); + link=linkdata[$2]; + sub(/name=string,/, "", link); + sub(/;.*/, "", link); + print link; + }' $datalink + ) | ( /usr/bin/sort -u; echo END; cat $datalink ) | /usr/bin/nawk ' + /^END$/ { state=1; } + state == 0 { usingpvid[++nusingpvid]=$1; next; } + /^#/ || NF < 2 { next; } + { + # If it is already present and has a tag set, + # then believe it. + if (!match($2, /;default_tag=/)) + next; + sub(/name=string,/, "", $2); + sub(/;.*/, "", $2); + for (i = 1; i <= nusingpvid; i++) { + if (usingpvid[i] == $2) + usingpvid[i]=""; + } + } + END { + for (i = 1; i <= nusingpvid; i++) { + if (usingpvid[i] != "") { + printf("Warning: default VLAN tag set to 0" \ + " on %s\n", usingpvid[i]); + cmd=sprintf("dladm set-linkprop -p " \ + "default_tag=0 %s\n", usingpvid[i]); + system(cmd); + } + } + }' +} diff --git a/usr/src/cmd/truss/codes.c b/usr/src/cmd/truss/codes.c index eaedc42e8c..1202809233 100644 --- a/usr/src/cmd/truss/codes.c +++ b/usr/src/cmd/truss/codes.c @@ -414,6 +414,7 @@ const struct ioc { { (uint_t)DLIOCRAW, "DLIOCRAW", NULL }, { (uint_t)DLIOCNATIVE, "DLIOCNATIVE", NULL }, { (uint_t)DLIOCIPNETINFO, "DLIOCIPNETINFO", NULL}, + { (uint_t)DLIOCLOWLINK, "DLIOCLOWLINK", NULL }, { (uint_t)LDOPEN, "LDOPEN", NULL }, { (uint_t)LDCLOSE, "LDCLOSE", NULL }, diff --git a/usr/src/cmd/truss/systable.c b/usr/src/cmd/truss/systable.c index 60b2950681..c98c4be557 100644 --- a/usr/src/cmd/truss/systable.c +++ b/usr/src/cmd/truss/systable.c @@ -1499,9 +1499,10 @@ const char * const afcodes[] = { "KEY", /* 27 */ "NCA", /* 28 */ "POLICY", /* 29 */ - "RDS" /* 30 */ + "RDS", /* 30 */ + "TRILL" /* 31 */ }; -#if MAX_AFCODES != 31 +#if MAX_AFCODES != 32 #error Need to update address-family table #endif 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) diff --git a/usr/src/pkgdefs/Makefile b/usr/src/pkgdefs/Makefile index 9d63fbb553..11138c47fc 100644 --- a/usr/src/pkgdefs/Makefile +++ b/usr/src/pkgdefs/Makefile @@ -206,6 +206,8 @@ COMMON_SUBDIRS= \ SUNWbipr \ SUNWbnur \ SUNWbnuu \ + SUNWbridger \ + SUNWbridgeu \ SUNWbsr \ SUNWbsu \ SUNWbtool \ @@ -232,6 +234,7 @@ COMMON_SUBDIRS= \ SUNWdhcsb \ SUNWdhcsr \ SUNWdhcsu \ + SUNWdladmint \ SUNWdmfe \ SUNWdmgtr \ SUNWdmgtu \ diff --git a/usr/src/pkgdefs/SUNWbridger/Makefile b/usr/src/pkgdefs/SUNWbridger/Makefile new file mode 100644 index 0000000000..d95e3f82c6 --- /dev/null +++ b/usr/src/pkgdefs/SUNWbridger/Makefile @@ -0,0 +1,35 @@ +# +# 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 ../Makefile.com + +DATAFILES += depend i.manifest r.manifest + +.KEEP_STATE: + +all: $(FILES) +install: all pkg + +include ../Makefile.targ diff --git a/usr/src/pkgdefs/SUNWbridger/pkginfo.tmpl b/usr/src/pkgdefs/SUNWbridger/pkginfo.tmpl new file mode 100644 index 0000000000..e156c0be19 --- /dev/null +++ b/usr/src/pkgdefs/SUNWbridger/pkginfo.tmpl @@ -0,0 +1,57 @@ +# +# 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. +# + +# +# This required package information file describes characteristics of the +# package, such as package abbreviation, full package name, package version, +# and package architecture. +# +PKG="SUNWbridger" +NAME="IEEE 802 Bridging Support (Root)" +ARCH="ISA" +VERSION="ONVERS,REV=0.0.0" +SUNW_PRODNAME="SunOS" +SUNW_PRODVERS="RELEASE/VERSION" +SUNW_PKGTYPE="root" +MAXINST="1000" +CATEGORY="system" +DESC="Datalink layer bridging support" +VENDOR="Sun Microsystems, Inc." +HOTLINE="Please contact your local service provider" +EMAIL="" +CLASSES="none manifest" +BASEDIR=/ +SUNW_PKGVERS="1.0" +SUNW_PKG_ALLZONES="true" +SUNW_PKG_HOLLOW="false" +SUNW_PKG_THISZONE="false" +#VSTOCK="<reserved by Release Engineering for package part #>" +#ISTATES="<developer defined>" +#RSTATES='<developer defined>' +#ULIMIT="<developer defined>" +#ORDER="<developer defined>" +#PSTAMP="<developer defined>" +#INTONLY="<developer defined>" diff --git a/usr/src/pkgdefs/SUNWbridger/prototype_com b/usr/src/pkgdefs/SUNWbridger/prototype_com new file mode 100644 index 0000000000..82bf20bde7 --- /dev/null +++ b/usr/src/pkgdefs/SUNWbridger/prototype_com @@ -0,0 +1,50 @@ +# +# 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. +# +# This required package information file contains a list of package contents. +# The 'pkgmk' command uses this file to identify the contents of a package +# and their location on the development machine when building the package. +# Can be created via a text editor or through use of the 'pkgproto' command. + +#!search <pathname pathname ...> # where to find pkg objects +#!include <filename> # include another 'prototype' file +#!default <mode> <owner> <group> # default used if not specified on entry +#!<param>=<value> # puts parameter in pkg environment + +# packaging files +i pkginfo +i copyright +i depend +i i.manifest +i r.manifest +# +# source locations relative to the prototype file +# +# SUNWbridger +# +d none var 755 root sys +d none var/svc 755 root sys +d none var/svc/manifest 755 root sys +d none var/svc/manifest/network 755 root sys +f manifest var/svc/manifest/network/bridge.xml 444 root sys diff --git a/usr/src/pkgdefs/SUNWbridger/prototype_i386 b/usr/src/pkgdefs/SUNWbridger/prototype_i386 new file mode 100644 index 0000000000..fa4348ae13 --- /dev/null +++ b/usr/src/pkgdefs/SUNWbridger/prototype_i386 @@ -0,0 +1,47 @@ +# +# 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. +# +# This required package information file contains a list of package contents. +# The 'pkgmk' command uses this file to identify the contents of a package +# and their location on the development machine when building the package. +# Can be created via a text editor or through use of the 'pkgproto' command. + +#!search <pathname pathname ...> # where to find pkg objects +#!include <filename> # include another 'prototype' file +#!default <mode> <owner> <group> # default used if not specified on entry +#!<param>=<value> # puts parameter in pkg environment + +# +# Include ISA independent files (prototype_com) +# +!include prototype_com +# +# +# +# List files which are I386 specific here +# +# source locations relative to the prototype file +# +# +# SUNWbridger +# diff --git a/usr/src/pkgdefs/SUNWbridger/prototype_sparc b/usr/src/pkgdefs/SUNWbridger/prototype_sparc new file mode 100644 index 0000000000..c2ec3c20a5 --- /dev/null +++ b/usr/src/pkgdefs/SUNWbridger/prototype_sparc @@ -0,0 +1,47 @@ +# +# 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. +# +# This required package information file contains a list of package contents. +# The 'pkgmk' command uses this file to identify the contents of a package +# and their location on the development machine when building the package. +# Can be created via a text editor or through use of the 'pkgproto' command. + +#!search <pathname pathname ...> # where to find pkg objects +#!include <filename> # include another 'prototype' file +#!default <mode> <owner> <group> # default used if not specified on entry +#!<param>=<value> # puts parameter in pkg environment + +# +# Include ISA independent files (prototype_com) +# +!include prototype_com +# +# +# +# List files which are SPARC specific here +# +# source locations relative to the prototype file +# +# +# SUNWbridger +# diff --git a/usr/src/pkgdefs/SUNWbridgeu/Makefile b/usr/src/pkgdefs/SUNWbridgeu/Makefile new file mode 100644 index 0000000000..eace737427 --- /dev/null +++ b/usr/src/pkgdefs/SUNWbridgeu/Makefile @@ -0,0 +1,38 @@ +# +# 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 ../Makefile.com + +DATAFILES += depend +LICENSEFILES = ../../lib/librstp/THIRDPARTYLICENSE +CDDL= + +.KEEP_STATE: + +all: $(FILES) +install: all pkg + +include ../Makefile.targ diff --git a/usr/src/pkgdefs/SUNWbridgeu/pkginfo.tmpl b/usr/src/pkgdefs/SUNWbridgeu/pkginfo.tmpl new file mode 100644 index 0000000000..42718ff446 --- /dev/null +++ b/usr/src/pkgdefs/SUNWbridgeu/pkginfo.tmpl @@ -0,0 +1,57 @@ +# +# 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. +# + +# +# This required package information file describes characteristics of the +# package, such as package abbreviation, full package name, package version, +# and package architecture. +# +PKG="SUNWbridgeu" +NAME="IEEE 802 Bridging Support (Usr)" +ARCH="ISA" +VERSION="ONVERS,REV=0.0.0" +SUNW_PRODNAME="SunOS" +SUNW_PRODVERS="RELEASE/VERSION" +SUNW_PKGTYPE="usr" +MAXINST="1000" +CATEGORY="system" +DESC="Datalink layer bridging support" +VENDOR="Sun Microsystems, Inc." +HOTLINE="Please contact your local service provider" +EMAIL="" +CLASSES="none" +BASEDIR=/ +SUNW_PKGVERS="1.0" +SUNW_PKG_ALLZONES="true" +SUNW_PKG_HOLLOW="false" +SUNW_PKG_THISZONE="false" +#VSTOCK="<reserved by Release Engineering for package part #>" +#ISTATES="<developer defined>" +#RSTATES='<developer defined>' +#ULIMIT="<developer defined>" +#ORDER="<developer defined>" +#PSTAMP="<developer defined>" +#INTONLY="<developer defined>" diff --git a/usr/src/pkgdefs/SUNWbridgeu/prototype_com b/usr/src/pkgdefs/SUNWbridgeu/prototype_com new file mode 100644 index 0000000000..958bbdd736 --- /dev/null +++ b/usr/src/pkgdefs/SUNWbridgeu/prototype_com @@ -0,0 +1,50 @@ +# +# 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. +# +# This required package information file contains a list of package contents. +# The 'pkgmk' command uses this file to identify the contents of a package +# and their location on the development machine when building the package. +# Can be created via a text editor or through use of the 'pkgproto' command. + +#!search <pathname pathname ...> # where to find pkg objects +#!include <filename> # include another 'prototype' file +#!default <mode> <owner> <group> # default used if not specified on entry +#!<param>=<value> # puts parameter in pkg environment + +# packaging files +i pkginfo +i copyright +i depend +# +# source locations relative to the prototype file +# +# SUNWbridgeu +# +d none usr 755 root sys +d none usr/lib 755 root bin +f none usr/lib/bridged 555 root bin +f none usr/lib/librstp.so.1 755 root bin +d none usr/lib/rcm 755 root bin +d none usr/lib/rcm/modules 755 root bin +f none usr/lib/rcm/modules/SUNW_bridge_rcm.so 555 root bin diff --git a/usr/src/pkgdefs/SUNWbridgeu/prototype_i386 b/usr/src/pkgdefs/SUNWbridgeu/prototype_i386 new file mode 100644 index 0000000000..a4e0ea69f5 --- /dev/null +++ b/usr/src/pkgdefs/SUNWbridgeu/prototype_i386 @@ -0,0 +1,47 @@ +# +# 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. +# +# This required package information file contains a list of package contents. +# The 'pkgmk' command uses this file to identify the contents of a package +# and their location on the development machine when building the package. +# Can be created via a text editor or through use of the 'pkgproto' command. + +#!search <pathname pathname ...> # where to find pkg objects +#!include <filename> # include another 'prototype' file +#!default <mode> <owner> <group> # default used if not specified on entry +#!<param>=<value> # puts parameter in pkg environment + +# +# Include ISA independent files (prototype_com) +# +!include prototype_com +# +# +# +# List files which are I386 specific here +# +# source locations relative to the prototype file +# +# +# SUNWbridgeu +# diff --git a/usr/src/pkgdefs/SUNWbridgeu/prototype_sparc b/usr/src/pkgdefs/SUNWbridgeu/prototype_sparc new file mode 100644 index 0000000000..aa5fcf8b16 --- /dev/null +++ b/usr/src/pkgdefs/SUNWbridgeu/prototype_sparc @@ -0,0 +1,47 @@ +# +# 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. +# +# This required package information file contains a list of package contents. +# The 'pkgmk' command uses this file to identify the contents of a package +# and their location on the development machine when building the package. +# Can be created via a text editor or through use of the 'pkgproto' command. + +#!search <pathname pathname ...> # where to find pkg objects +#!include <filename> # include another 'prototype' file +#!default <mode> <owner> <group> # default used if not specified on entry +#!<param>=<value> # puts parameter in pkg environment + +# +# Include ISA independent files (prototype_com) +# +!include prototype_com +# +# +# +# List files which are SPARC specific here +# +# source locations relative to the prototype file +# +# +# SUNWbridgeu +# diff --git a/usr/src/pkgdefs/SUNWckr/prototype_com b/usr/src/pkgdefs/SUNWckr/prototype_com index f69ddf9391..282a0ce818 100644 --- a/usr/src/pkgdefs/SUNWckr/prototype_com +++ b/usr/src/pkgdefs/SUNWckr/prototype_com @@ -72,6 +72,7 @@ d none kernel/drv 755 root sys f none kernel/drv/aggr.conf 644 root sys f none kernel/drv/arp.conf 644 root sys f none kernel/drv/bl.conf 644 root sys +f none kernel/drv/bridge.conf 644 root sys f none kernel/drv/clone.conf 644 root sys f none kernel/drv/cn.conf 644 root sys f none kernel/drv/conskbd.conf 644 root sys diff --git a/usr/src/pkgdefs/SUNWckr/prototype_i386 b/usr/src/pkgdefs/SUNWckr/prototype_i386 index 8a0cdd82c4..1b29fa83a3 100644 --- a/usr/src/pkgdefs/SUNWckr/prototype_i386 +++ b/usr/src/pkgdefs/SUNWckr/prototype_i386 @@ -73,6 +73,7 @@ f none kernel/drv/amd_iommu.conf 644 root sys f none kernel/drv/bl 755 root sys f none kernel/drv/bmc 755 root sys f none kernel/drv/bmc.conf 644 root sys +f none kernel/drv/bridge 755 root sys f none kernel/drv/bscbus 755 root sys f none kernel/drv/bscbus.conf 644 root sys f none kernel/drv/bscv 755 root sys @@ -298,6 +299,7 @@ f none kernel/drv/amd64/acpi_toshiba 755 root sys f none kernel/drv/amd64/amd_iommu 755 root sys f none kernel/drv/amd64/bl 755 root sys f none kernel/drv/amd64/bmc 755 root sys +f none kernel/drv/amd64/bridge 755 root sys f none kernel/drv/amd64/bscbus 755 root sys f none kernel/drv/amd64/bscv 755 root sys f none kernel/drv/amd64/clone 755 root sys @@ -511,9 +513,11 @@ l none kernel/socketmod/rts=../../kernel/drv/rts l none kernel/socketmod/tcp=../../kernel/drv/tcp l none kernel/socketmod/udp=../../kernel/drv/udp f none kernel/socketmod/socksctp 755 root sys +f none kernel/socketmod/trill 755 root sys d none kernel/socketmod/amd64 755 root sys l none kernel/socketmod/amd64/icmp=../../../kernel/drv/amd64/icmp l none kernel/socketmod/amd64/rts=../../../kernel/drv/amd64/rts l none kernel/socketmod/amd64/tcp=../../../kernel/drv/amd64/tcp l none kernel/socketmod/amd64/udp=../../../kernel/drv/amd64/udp f none kernel/socketmod/amd64/socksctp 755 root sys +f none kernel/socketmod/amd64/trill 755 root sys diff --git a/usr/src/pkgdefs/SUNWckr/prototype_sparc b/usr/src/pkgdefs/SUNWckr/prototype_sparc index 334dda12f0..f35c4068ed 100644 --- a/usr/src/pkgdefs/SUNWckr/prototype_sparc +++ b/usr/src/pkgdefs/SUNWckr/prototype_sparc @@ -71,6 +71,7 @@ f none kernel/drv/sparcv9/aggr 755 root sys f none kernel/drv/sparcv9/arp 755 root sys f none kernel/drv/sparcv9/bl 755 root sys f none kernel/drv/sparcv9/bpp 755 root sys +f none kernel/drv/sparcv9/bridge 755 root sys f none kernel/drv/sparcv9/clone 755 root sys f none kernel/drv/sparcv9/cn 755 root sys f none kernel/drv/sparcv9/conskbd 755 root sys @@ -272,3 +273,4 @@ l none kernel/socketmod/sparcv9/rts=../../../kernel/drv/sparcv9/rts l none kernel/socketmod/sparcv9/tcp=../../../kernel/drv/sparcv9/tcp l none kernel/socketmod/sparcv9/udp=../../../kernel/drv/sparcv9/udp f none kernel/socketmod/sparcv9/socksctp 755 root sys +f none kernel/socketmod/sparcv9/trill 755 root sys diff --git a/usr/src/pkgdefs/SUNWcnetr/postinstall b/usr/src/pkgdefs/SUNWcnetr/postinstall index 1cfa89902f..3d8ce9735e 100644 --- a/usr/src/pkgdefs/SUNWcnetr/postinstall +++ b/usr/src/pkgdefs/SUNWcnetr/postinstall @@ -165,6 +165,14 @@ do if [ "$vid" != "0" ]; then echo dladm create-vlan -l $phys$inst -v $vid \ $ifname >> ${PKG_INSTALL_ROOT}/$UPGRADE_SCRIPT + if [ "$vid" != "1" ]; then + continue + fi + # If default PVID VLAN 1 is in use then warn + # the user and force PVID to zero. + echo "Warning: default VLAN tag set to 0 on $ifname" + echo dladm set-linkprop -p default_tag=0 \ + $ifname >> ${PKG_INSTALL_ROOT}/$UPGRADE_SCRIPT fi fi done diff --git a/usr/src/pkgdefs/SUNWdladmint/Makefile b/usr/src/pkgdefs/SUNWdladmint/Makefile new file mode 100644 index 0000000000..315bf62591 --- /dev/null +++ b/usr/src/pkgdefs/SUNWdladmint/Makefile @@ -0,0 +1,37 @@ +# +# 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 ../Makefile.com + +DATAFILES += depend + +.KEEP_STATE: + +all: $(FILES) + +install: all pkg + +include ../Makefile.targ diff --git a/usr/src/pkgdefs/SUNWdladmint/pkginfo.tmpl b/usr/src/pkgdefs/SUNWdladmint/pkginfo.tmpl new file mode 100644 index 0000000000..d1098cf7e3 --- /dev/null +++ b/usr/src/pkgdefs/SUNWdladmint/pkginfo.tmpl @@ -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. +# +# +# This required package information file describes characteristics of the +# package, such as package abbreviation, full package name, package version, +# and package architecture. +# +PKG="SUNWdladmint" +NAME="Datalink Administration Internal Files" +ARCH="ISA" +VERSION="ONVERS,REV=0.0.0" +SUNW_PRODNAME="SunOS" +SUNW_PRODVERS="RELEASE/VERSION" +SUNW_PKGTYPE="root" +MAXINST="1000" +CATEGORY="system" +DESC="Datalink administration internal files" +VENDOR="Sun Microsystems, Inc." +HOTLINE="Please contact your local service provider" +EMAIL="" +CLASSES="none" +BASEDIR=/ +SUNW_PKGVERS="1.0" +#VSTOCK="<reserved by Release Engineering for package part #>" +#ISTATES="<developer defined>" +#RSTATES='<developer defined>' +#ULIMIT="<developer defined>" +#ORDER="<developer defined>" +#PSTAMP="<developer defined>" +#INTONLY="<developer defined>" diff --git a/usr/src/pkgdefs/SUNWdladmint/prototype_com b/usr/src/pkgdefs/SUNWdladmint/prototype_com new file mode 100644 index 0000000000..a26a671f24 --- /dev/null +++ b/usr/src/pkgdefs/SUNWdladmint/prototype_com @@ -0,0 +1,61 @@ +# +# 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. +# +# This required package information file contains a list of package contents. +# The 'pkgmk' command uses this file to identify the contents of a package +# and their location on the development machine when building the package. +# Can be created via a text editor or through use of the 'pkgproto' command. +# + +#!search <pathname pathname ...> # where to find pkg objects +#!include <filename> # include another 'prototype' file +#!default <mode> <owner> <group> # default used if not specified on entry +#!<param>=<value> # puts parameter in pkg environment + +# packaging files +i pkginfo +i copyright +i depend +# +# source locations relative to the prototype file +# +# SUNWdladmint +# +d none usr 755 root sys +d none usr/include 755 root bin +f none usr/include/libdladm.h 644 root bin +f none usr/include/libdllink.h 644 root bin +f none usr/include/libdlvlan.h 644 root bin +f none usr/include/libdlbridge.h 644 root bin +f none usr/include/uid_stp.h 644 root bin +d none usr/include/sys 755 root bin +f none usr/include/sys/dld.h 644 root bin +f none usr/include/sys/dls_mgmt.h 644 root bin +f none usr/include/sys/mac.h 644 root bin +f none usr/include/sys/mac_flow.h 644 root bin +d none lib 755 root bin +s none lib/libdladm.so=./libdladm.so.1 +f none lib/llib-ldladm 644 root bin +f none lib/llib-ldladm.ln 644 root bin diff --git a/usr/src/pkgdefs/SUNWdladmint/prototype_i386 b/usr/src/pkgdefs/SUNWdladmint/prototype_i386 new file mode 100644 index 0000000000..f19e33b065 --- /dev/null +++ b/usr/src/pkgdefs/SUNWdladmint/prototype_i386 @@ -0,0 +1,50 @@ +# +# 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. +# +# This required package information file contains a list of package contents. +# The 'pkgmk' command uses this file to identify the contents of a package +# and their location on the development machine when building the package. +# Can be created via a text editor or through use of the 'pkgproto' command. +# + +#!search <pathname pathname ...> # where to find pkg objects +#!include <filename> # include another 'prototype' file +#!default <mode> <owner> <group> # default used if not specified on entry +#!<param>=<value> # puts parameter in pkg environment + +# +# Include ISA independent files (prototype_com) +# +!include prototype_com +# +# List files that are i386 specific here +# +# source locations relative to the prototype file +# +# SUNWdladmint +# +d none lib/amd64 755 root bin +s none lib/amd64/libdladm.so=./libdladm.so.1 +f none lib/amd64/llib-ldladm.ln 644 root bin diff --git a/usr/src/pkgdefs/SUNWdladmint/prototype_sparc b/usr/src/pkgdefs/SUNWdladmint/prototype_sparc new file mode 100644 index 0000000000..a830eeb4df --- /dev/null +++ b/usr/src/pkgdefs/SUNWdladmint/prototype_sparc @@ -0,0 +1,50 @@ +# +# 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. +# +# This required package information file contains a list of package contents. +# The 'pkgmk' command uses this file to identify the contents of a package +# and their location on the development machine when building the package. +# Can be created via a text editor or through use of the 'pkgproto' command. +# + +#!search <pathname pathname ...> # where to find pkg objects +#!include <filename> # include another 'prototype' file +#!default <mode> <owner> <group> # default used if not specified on entry +#!<param>=<value> # puts parameter in pkg environment + +# +# Include ISA independent files (prototype_com) +# +!include prototype_com +# +# List files that are SPARC specific here +# +# source locations relative to the prototype file +# +# SUNWdladmint +# +d none lib/sparcv9 755 root bin +s none lib/sparcv9/libdladm.so=./libdladm.so.1 +f none lib/sparcv9/llib-ldladm.ln 644 root bin diff --git a/usr/src/pkgdefs/SUNWhea/prototype_com b/usr/src/pkgdefs/SUNWhea/prototype_com index b7689d5aa5..47b32ddde2 100644 --- a/usr/src/pkgdefs/SUNWhea/prototype_com +++ b/usr/src/pkgdefs/SUNWhea/prototype_com @@ -355,6 +355,7 @@ f none usr/include/ndbm.h 644 root bin f none usr/include/ndpd.h 644 root bin d none usr/include/net 755 root bin f none usr/include/net/af.h 644 root bin +f none usr/include/net/bridge.h 644 root bin f none usr/include/net/if.h 644 root bin f none usr/include/net/if_arp.h 644 root bin f none usr/include/net/if_dl.h 644 root bin @@ -367,6 +368,7 @@ f none usr/include/net/pppio.h 644 root bin f none usr/include/net/vjcompress.h 644 root bin f none usr/include/net/route.h 644 root bin f none usr/include/net/radix.h 644 root bin +f none usr/include/net/trill.h 644 root bin f none usr/include/netconfig.h 644 root bin f none usr/include/netdb.h 644 root bin f none usr/include/netdir.h 644 root bin diff --git a/usr/src/pkgdefs/common_files/i.devpolicy b/usr/src/pkgdefs/common_files/i.devpolicy index 695c2c3160..74776ffe3e 100644 --- a/usr/src/pkgdefs/common_files/i.devpolicy +++ b/usr/src/pkgdefs/common_files/i.devpolicy @@ -50,7 +50,7 @@ do rm -f $dest.$$ # potential additions - additions="keysock icmp icmp6 ipnet ipsecah ipsecesp openeepr random spdsock ipf pfil scsi_vhci" + additions="bridge keysock icmp icmp6 ipnet ipsecah ipsecesp openeepr random spdsock ipf pfil scsi_vhci" for dev in $additions do diff --git a/usr/src/pkgdefs/common_files/i.minorperm_i386 b/usr/src/pkgdefs/common_files/i.minorperm_i386 index 9572480f9a..9f4bc70380 100644 --- a/usr/src/pkgdefs/common_files/i.minorperm_i386 +++ b/usr/src/pkgdefs/common_files/i.minorperm_i386 @@ -389,6 +389,7 @@ xenbus:* fm:* amd_iommu:* xpvtap:* +clone:bridge EOF } diff --git a/usr/src/pkgdefs/common_files/i.minorperm_sparc b/usr/src/pkgdefs/common_files/i.minorperm_sparc index b8480f9070..733937e054 100644 --- a/usr/src/pkgdefs/common_files/i.minorperm_sparc +++ b/usr/src/pkgdefs/common_files/i.minorperm_sparc @@ -350,6 +350,7 @@ vscan:* nsmb:* bmc:bmc fm:* +clone:bridge EOF } diff --git a/usr/src/pkgdefs/common_files/i.sock2path b/usr/src/pkgdefs/common_files/i.sock2path index 87e585cb0d..340991fe00 100644 --- a/usr/src/pkgdefs/common_files/i.sock2path +++ b/usr/src/pkgdefs/common_files/i.sock2path @@ -76,6 +76,11 @@ do echo >> $dest grep '/dev/spdsock' $src >> $dest fi + grep 'trill' $dest > /dev/null 2>&1 + if [ $? != 0 ] ; then + echo >> $dest + grep 'trill' $src >> $dest + fi grep "^#" $dest | awk '{ if ($5=="Path") {print $0 "|Module"} else {print $0}}' > /tmp/i.$$ diff --git a/usr/src/pkgdefs/etc/exception_list_i386 b/usr/src/pkgdefs/etc/exception_list_i386 index e8a62d448c..1f881159c6 100644 --- a/usr/src/pkgdefs/etc/exception_list_i386 +++ b/usr/src/pkgdefs/etc/exception_list_i386 @@ -88,16 +88,12 @@ usr/lib/libdhcpsvc.so i386 # usr/include/sys/aggr.h i386 usr/include/sys/aggr_impl.h i386 -usr/include/sys/dld.h i386 usr/include/sys/dld_impl.h i386 usr/include/sys/dld_ioc.h i386 usr/include/sys/dls.h i386 -usr/include/sys/dls_mgmt.h i386 usr/include/sys/dls_impl.h i386 -usr/include/sys/mac.h i386 usr/include/sys/mac_client.h i386 usr/include/sys/mac_client_impl.h i386 -usr/include/sys/mac_flow.h i386 usr/include/sys/mac_flow_impl.h i386 usr/include/sys/mac_impl.h i386 usr/include/sys/mac_provider.h i386 @@ -105,27 +101,17 @@ usr/include/sys/mac_soft_ring.h i386 # # Private GLDv3 userland libraries and headers # -usr/include/sys/vnic.h i386 -usr/include/sys/vnic_impl.h i386 -usr/include/libdladm.h i386 -usr/include/libdlether.h i386 usr/include/libdladm_impl.h i386 -usr/include/libdllink.h i386 usr/include/libdlaggr.h i386 -usr/include/libdlwlan.h i386 -usr/include/libdlwlan_impl.h i386 -usr/include/libdlvnic.h i386 +usr/include/libdlether.h i386 usr/include/libdlflow.h i386 usr/include/libdlflow_impl.h i386 -usr/include/libdlvlan.h i386 usr/include/libdlmgmt.h i386 -usr/include/libdlstat.h i386 usr/include/libdlsim.h i386 -lib/libdladm.so i386 -lib/llib-ldladm.ln i386 -lib/amd64/libdladm.so i386 -lib/amd64/llib-ldladm.ln i386 -lib/llib-ldladm i386 +usr/include/libdlstat.h i386 +usr/include/libdlvnic.h i386 +usr/include/libdlwlan.h i386 +usr/include/libdlwlan_impl.h i386 # # Virtual Network Interface Card (VNIC) # @@ -668,18 +654,23 @@ usr/lib/devfsadm/linkmod/SUNW_ieee1394_link.so i386 usr/include/sys/1394/ieee1212.h i386 usr/include/sys/1394/t1394.h i386 usr/include/sys/1394/id1394.h i386 +# # Private net80211 headers usr/include/sys/net80211.h i386 usr/include/sys/net80211_crypto.h i386 usr/include/sys/net80211_ht.h i386 usr/include/sys/net80211_proto.h i386 +usr/include/net/wpa.h i386 # # PPPoE files not delivered to customers. usr/include/net/pppoe.h i386 usr/include/net/sppptun.h i386 # +# Simnet usr/include/net/simnet.h i386 -usr/include/net/wpa.h i386 +# +# Bridging internal data structures +usr/include/net/bridge_impl.h i386 # # The ses driver is not currently delivered on Intel # @@ -1263,3 +1254,12 @@ usr/include/rpcsvc/idmap_prot.x i386 # Private idmap directory API usr/include/directory.h i386 +# librstp is private for bridging +# +usr/include/stp_bpdu.h i386 +usr/include/stp_in.h i386 +usr/include/stp_vectors.h i386 +usr/lib/librstp.so i386 +usr/lib/llib-lrstp i386 +usr/lib/llib-lrstp.ln i386 + diff --git a/usr/src/pkgdefs/etc/exception_list_sparc b/usr/src/pkgdefs/etc/exception_list_sparc index 2837635c65..aafc7af9d0 100644 --- a/usr/src/pkgdefs/etc/exception_list_sparc +++ b/usr/src/pkgdefs/etc/exception_list_sparc @@ -77,16 +77,12 @@ usr/lib/libdhcpsvc.so sparc # usr/include/sys/aggr.h sparc usr/include/sys/aggr_impl.h sparc -usr/include/sys/dld.h sparc usr/include/sys/dld_impl.h sparc usr/include/sys/dld_ioc.h sparc usr/include/sys/dls.h sparc -usr/include/sys/dls_mgmt.h sparc usr/include/sys/dls_impl.h sparc -usr/include/sys/mac.h sparc usr/include/sys/mac_client.h sparc usr/include/sys/mac_client_impl.h sparc -usr/include/sys/mac_flow.h sparc usr/include/sys/mac_flow_impl.h sparc usr/include/sys/mac_impl.h sparc usr/include/sys/mac_provider.h sparc @@ -94,27 +90,17 @@ usr/include/sys/mac_soft_ring.h sparc # # Private GLDv3 userland libraries and headers # -usr/include/sys/vnic.h sparc -usr/include/sys/vnic_impl.h sparc -usr/include/libdladm.h sparc -usr/include/libdlether.h sparc usr/include/libdladm_impl.h sparc -usr/include/libdllink.h sparc usr/include/libdlaggr.h sparc +usr/include/libdlether.h sparc usr/include/libdlflow.h sparc usr/include/libdlflow_impl.h sparc -usr/include/libdlwlan.h sparc -usr/include/libdlwlan_impl.h sparc -usr/include/libdlvnic.h sparc -usr/include/libdlvlan.h sparc usr/include/libdlmgmt.h sparc -usr/include/libdlstat.h sparc usr/include/libdlsim.h sparc -lib/libdladm.so sparc -lib/llib-ldladm.ln sparc -lib/sparcv9/libdladm.so sparc -lib/sparcv9/llib-ldladm.ln sparc -lib/llib-ldladm sparc +usr/include/libdlstat.h sparc +usr/include/libdlvnic.h sparc +usr/include/libdlwlan.h sparc +usr/include/libdlwlan_impl.h sparc # # Virtual Network Interface Card (VNIC) # @@ -668,18 +654,24 @@ var/svc/manifest/network/ssh.xml sparc usr/platform/sun4u/include/sys/fc_plat.h sparc usr/platform/sun4u/include/sys/fcode.h sparc # +# Private net80211 headers +usr/include/sys/net80211.h sparc +usr/include/sys/net80211_crypto.h sparc +usr/include/sys/net80211_ht.h sparc +usr/include/sys/net80211_proto.h sparc +# +usr/include/net/wpa.h sparc +# # PPPoE files not delivered to customers. # usr/include/net/pppoe.h sparc usr/include/net/sppptun.h sparc # +# Simnet usr/include/net/simnet.h sparc -usr/include/net/wpa.h sparc -# Private net80211 headers -usr/include/sys/net80211.h sparc -usr/include/sys/net80211_crypto.h sparc -usr/include/sys/net80211_ht.h sparc -usr/include/sys/net80211_proto.h sparc +# +# Bridging internal data structures +usr/include/net/bridge_impl.h sparc # # User<->kernel interface used by cfgadm/USB only # @@ -1362,3 +1354,11 @@ usr/include/rpcsvc/idmap_prot.x sparc # Private idmap directory API usr/include/directory.h sparc +# librstp is private for bridging +# +usr/include/stp_bpdu.h sparc +usr/include/stp_in.h sparc +usr/include/stp_vectors.h sparc +usr/lib/librstp.so sparc +usr/lib/llib-lrstp sparc +usr/lib/llib-lrstp.ln sparc diff --git a/usr/src/tools/opensolaris/license-list b/usr/src/tools/opensolaris/license-list index ecfa5c3cd6..02941496e6 100644 --- a/usr/src/tools/opensolaris/license-list +++ b/usr/src/tools/opensolaris/license-list @@ -100,6 +100,7 @@ usr/src/lib/libpkg/THIRDPARTYLICENSE usr/src/lib/libpp/THIRDPARTYLICENSE usr/src/lib/libresolv/THIRDPARTYLICENSE usr/src/lib/libresolv2/THIRDPARTYLICENSE +usr/src/lib/librstp/THIRDPARTYLICENSE usr/src/lib/libsasl/THIRDPARTYLICENSE usr/src/lib/libshell/THIRDPARTYLICENSE usr/src/lib/libsmbfs/smb/THIRDPARTYLICENSE.apple diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files index 591a6a37ad..84c15a6db2 100644 --- a/usr/src/uts/common/Makefile.files +++ b/usr/src/uts/common/Makefile.files @@ -1679,6 +1679,8 @@ DLPISTUB_OBJS += dlpistub.o SDP_OBJS += sdpddi.o +TRILL_OBJS += trill.o + CTF_OBJS += ctf_create.o ctf_decl.o ctf_error.o ctf_hash.o ctf_labels.o \ ctf_lookup.o ctf_open.o ctf_types.o ctf_util.o ctf_subr.o ctf_mod.o @@ -1750,6 +1752,8 @@ SFE_OBJS += sfe.o sfe_util.o BFE_OBJS += bfe.o +BRIDGE_OBJS += bridge.o + DDA_OBJS += dda.o DMD_OBJS += dmd.o diff --git a/usr/src/uts/common/io/bridge.c b/usr/src/uts/common/io/bridge.c new file mode 100644 index 0000000000..42f85aaed6 --- /dev/null +++ b/usr/src/uts/common/io/bridge.c @@ -0,0 +1,3527 @@ +/* + * 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. + */ + +/* + * This module implements a STREAMS driver that provides layer-two (Ethernet) + * bridging functionality. The STREAMS interface is used to provide + * observability (snoop/wireshark) and control, but not for interface plumbing. + */ + +#include <sys/types.h> +#include <sys/bitmap.h> +#include <sys/cmn_err.h> +#include <sys/conf.h> +#include <sys/ddi.h> +#include <sys/errno.h> +#include <sys/kstat.h> +#include <sys/modctl.h> +#include <sys/note.h> +#include <sys/param.h> +#include <sys/policy.h> +#include <sys/sdt.h> +#include <sys/stat.h> +#include <sys/stream.h> +#include <sys/stropts.h> +#include <sys/strsun.h> +#include <sys/sunddi.h> +#include <sys/sysmacros.h> +#include <sys/systm.h> +#include <sys/time.h> +#include <sys/dlpi.h> +#include <sys/dls.h> +#include <sys/mac_ether.h> +#include <sys/mac_provider.h> +#include <sys/mac_client_priv.h> +#include <sys/mac_impl.h> +#include <sys/vlan.h> +#include <net/bridge.h> +#include <net/bridge_impl.h> +#include <net/trill.h> + +/* + * Locks and reference counts: object lifetime and design. + * + * bridge_mac_t + * Bridge mac (snoop) instances are in bmac_list, which is protected by + * bmac_rwlock. They're allocated by bmac_alloc and freed by bridge_timer(). + * Every bridge_inst_t has a single bridge_mac_t, but when bridge_inst_t goes + * away, the bridge_mac_t remains until either all of the users go away + * (detected by a timer) or until the instance is picked up again by the same + * bridge starting back up. + * + * bridge_inst_t + * Bridge instances are in inst_list, which is protected by inst_lock. + * They're allocated by inst_alloc() and freed by inst_free(). After + * allocation, an instance is placed in inst_list, and the reference count is + * incremented to represent this. That reference is decremented when the + * BIF_SHUTDOWN flag is set, and no new increments may occur. When the last + * reference is freed, the instance is removed from the list. + * + * Bridge instances have lists of links and an AVL tree of forwarding + * entries. Each of these structures holds one reference on the bridge + * instance. These lists and tree are protected by bi_rwlock. + * + * bridge_stream_t + * Bridge streams are allocated by stream_alloc() and freed by stream_free(). + * These streams are created when "bridged" opens /dev/bridgectl, and are + * used to create new bridge instances (via BRIOC_NEWBRIDGE) and control the + * links on the bridge. When a stream closes, the bridge instance created is + * destroyed. There's at most one bridge instance for a given control + * stream. + * + * bridge_link_t + * Links are allocated by bridge_add_link() and freed by link_free(). The + * bi_links list holds a reference to the link. When the BLF_DELETED flag is + * set, that reference is dropped. The link isn't removed from the list + * until the last reference drops. Each forwarding entry that uses a given + * link holds a reference, as does each thread transmitting a packet via the + * link. The MAC layer calls in via bridge_ref_cb() to hold a reference on + * a link when transmitting. + * + * It's important that once BLF_DELETED is set, there's no way for the + * reference count to increase again. If it can, then the link may be + * double-freed. The BLF_FREED flag is intended for use with assertions to + * guard against this in testing. + * + * bridge_fwd_t + * Bridge forwarding entries are allocated by bridge_recv_cb() and freed by + * fwd_free(). The bi_fwd AVL tree holds one reference to the entry. Unlike + * other data structures, the reference is dropped when the entry is removed + * from the tree by fwd_delete(), and the BFF_INTREE flag is removed. Each + * thread that's forwarding a packet to a known destination holds a reference + * to a forwarding entry. + * + * TRILL notes: + * + * The TRILL module does all of its I/O through bridging. It uses references + * on the bridge_inst_t and bridge_link_t structures, and has seven entry + * points and four callbacks. One entry point is for setting the callbacks + * (bridge_trill_register_cb). There are four entry points for taking bridge + * and link references (bridge_trill_{br,ln}{ref,unref}). The final two + * entry points are for decapsulated packets from TRILL (bridge_trill_decaps) + * that need to be bridged locally, and for TRILL-encapsulated output packets + * (bridge_trill_output). + * + * The four callbacks comprise two notification functions for bridges and + * links being deleted, one function for raw received TRILL packets, and one + * for bridge output to non-local TRILL destinations (tunnel entry). + */ + +/* + * Ethernet reserved multicast addresses for TRILL; used also in TRILL module. + */ +const uint8_t all_isis_rbridges[] = ALL_ISIS_RBRIDGES; +static const uint8_t all_esadi_rbridges[] = ALL_ESADI_RBRIDGES; +const uint8_t bridge_group_address[] = BRIDGE_GROUP_ADDRESS; + +static const char *inst_kstats_list[] = { KSINST_NAMES }; +static const char *link_kstats_list[] = { KSLINK_NAMES }; + +#define KREF(p, m, vn) p->m.vn.value.ui64 +#define KINCR(p, m, vn) ++KREF(p, m, vn) +#define KDECR(p, m, vn) --KREF(p, m, vn) + +#define KIPINCR(p, vn) KINCR(p, bi_kstats, vn) +#define KIPDECR(p, vn) KDECR(p, bi_kstats, vn) +#define KLPINCR(p, vn) KINCR(p, bl_kstats, vn) + +#define KIINCR(vn) KIPINCR(bip, vn) +#define KIDECR(vn) KIPDECR(bip, vn) +#define KLINCR(vn) KLPINCR(blp, vn) + +#define Dim(x) (sizeof (x) / sizeof (*(x))) + +/* Amount of overhead added when encapsulating with VLAN headers */ +#define VLAN_INCR (sizeof (struct ether_vlan_header) - \ + sizeof (struct ether_header)) + +static dev_info_t *bridge_dev_info; +static major_t bridge_major; +static ddi_taskq_t *bridge_taskq; + +/* + * These are the bridge instance management data structures. The mutex lock + * protects the list of bridge instances. A reference count is then used on + * each instance to determine when to free it. We use mac_minor_hold() to + * allocate minor_t values, which are used both for self-cloning /dev/net/ + * device nodes as well as client streams. Minor node 0 is reserved for the + * allocation control node. + */ +static list_t inst_list; +static kcondvar_t inst_cv; /* Allows us to wait for shutdown */ +static kmutex_t inst_lock; + +static krwlock_t bmac_rwlock; +static list_t bmac_list; + +/* Wait for taskq entries that use STREAMS */ +static kcondvar_t stream_ref_cv; +static kmutex_t stream_ref_lock; + +static timeout_id_t bridge_timerid; +static clock_t bridge_scan_interval; +static clock_t bridge_fwd_age; + +static bridge_inst_t *bridge_find_name(const char *); +static void bridge_timer(void *); +static void bridge_unref(bridge_inst_t *); + +static const uint8_t zero_addr[ETHERADDRL] = { 0 }; + +/* Global TRILL linkage */ +static trill_recv_pkt_t trill_recv_fn; +static trill_encap_pkt_t trill_encap_fn; +static trill_br_dstr_t trill_brdstr_fn; +static trill_ln_dstr_t trill_lndstr_fn; + +/* special settings to accommodate DLD flow control; see dld_str.c */ +static struct module_info bridge_dld_modinfo = { + 0, /* mi_idnum */ + "bridge", /* mi_idname */ + 0, /* mi_minpsz */ + INFPSZ, /* mi_maxpsz */ + 1, /* mi_hiwat */ + 0 /* mi_lowat */ +}; + +static struct qinit bridge_dld_rinit = { + NULL, /* qi_putp */ + NULL, /* qi_srvp */ + dld_open, /* qi_qopen */ + dld_close, /* qi_qclose */ + NULL, /* qi_qadmin */ + &bridge_dld_modinfo, /* qi_minfo */ + NULL /* qi_mstat */ +}; + +static struct qinit bridge_dld_winit = { + (int (*)())dld_wput, /* qi_putp */ + (int (*)())dld_wsrv, /* qi_srvp */ + NULL, /* qi_qopen */ + NULL, /* qi_qclose */ + NULL, /* qi_qadmin */ + &bridge_dld_modinfo, /* qi_minfo */ + NULL /* qi_mstat */ +}; + +static int bridge_ioc_listfwd(void *, intptr_t, int, cred_t *, int *); + +/* GLDv3 control ioctls used by Bridging */ +static dld_ioc_info_t bridge_ioc_list[] = { + {BRIDGE_IOC_LISTFWD, DLDCOPYINOUT, sizeof (bridge_listfwd_t), + bridge_ioc_listfwd, NULL}, +}; + +/* + * Given a bridge mac pointer, get a ref-held pointer to the corresponding + * bridge instance, if any. We must hold the global bmac_rwlock so that + * bm_inst doesn't slide out from under us. + */ +static bridge_inst_t * +mac_to_inst(const bridge_mac_t *bmp) +{ + bridge_inst_t *bip; + + rw_enter(&bmac_rwlock, RW_READER); + if ((bip = bmp->bm_inst) != NULL) + atomic_inc_uint(&bip->bi_refs); + rw_exit(&bmac_rwlock); + return (bip); +} + +static void +link_sdu_fail(bridge_link_t *blp, boolean_t failed, mblk_t **mlist) +{ + mblk_t *mp; + bridge_ctl_t *bcp; + bridge_link_t *blcmp; + bridge_inst_t *bip; + bridge_mac_t *bmp; + + if (failed) { + if (blp->bl_flags & BLF_SDUFAIL) + return; + blp->bl_flags |= BLF_SDUFAIL; + } else { + if (!(blp->bl_flags & BLF_SDUFAIL)) + return; + blp->bl_flags &= ~BLF_SDUFAIL; + } + + /* + * If this link is otherwise up, then check if there are any other + * non-failed non-down links. If not, then we control the state of the + * whole bridge. + */ + bip = blp->bl_inst; + bmp = bip->bi_mac; + if (blp->bl_linkstate != LINK_STATE_DOWN) { + for (blcmp = list_head(&bip->bi_links); blcmp != NULL; + blcmp = list_next(&bip->bi_links, blcmp)) { + if (blp != blcmp && + !(blcmp->bl_flags & (BLF_DELETED|BLF_SDUFAIL)) && + blcmp->bl_linkstate != LINK_STATE_DOWN) + break; + } + if (blcmp == NULL) { + bmp->bm_linkstate = failed ? LINK_STATE_DOWN : + LINK_STATE_UP; + mac_link_redo(bmp->bm_mh, bmp->bm_linkstate); + } + } + + /* + * If we're becoming failed, then the link's current true state needs + * to be reflected upwards to this link's clients. If we're becoming + * unfailed, then we get the state of the bridge instead on all + * clients. + */ + if (failed) { + if (bmp->bm_linkstate != blp->bl_linkstate) + mac_link_redo(blp->bl_mh, blp->bl_linkstate); + } else { + mac_link_redo(blp->bl_mh, bmp->bm_linkstate); + } + + /* get the current mblk we're going to send up */ + if ((mp = blp->bl_lfailmp) == NULL && + (mp = allocb(sizeof (bridge_ctl_t), BPRI_MED)) == NULL) + return; + + /* get a new one for next time */ + blp->bl_lfailmp = allocb(sizeof (bridge_ctl_t), BPRI_MED); + + /* if none for next time, then report only failures */ + if (blp->bl_lfailmp == NULL && !failed) { + blp->bl_lfailmp = mp; + return; + } + + /* LINTED: alignment */ + bcp = (bridge_ctl_t *)mp->b_rptr; + bcp->bc_linkid = blp->bl_linkid; + bcp->bc_failed = failed; + mp->b_wptr = (uchar_t *)(bcp + 1); + mp->b_next = *mlist; + *mlist = mp; +} + +/* + * Send control messages (link SDU changes) using the stream to the + * bridge instance daemon. + */ +static void +send_up_messages(bridge_inst_t *bip, mblk_t *mp) +{ + mblk_t *mnext; + queue_t *rq; + + rq = bip->bi_control->bs_wq; + rq = OTHERQ(rq); + while (mp != NULL) { + mnext = mp->b_next; + mp->b_next = NULL; + putnext(rq, mp); + mp = mnext; + } +} + +/* ARGSUSED */ +static int +bridge_m_getstat(void *arg, uint_t stat, uint64_t *val) +{ + return (ENOTSUP); +} + +static int +bridge_m_start(void *arg) +{ + bridge_mac_t *bmp = arg; + + bmp->bm_flags |= BMF_STARTED; + return (0); +} + +static void +bridge_m_stop(void *arg) +{ + bridge_mac_t *bmp = arg; + + bmp->bm_flags &= ~BMF_STARTED; +} + +/* ARGSUSED */ +static int +bridge_m_setpromisc(void *arg, boolean_t on) +{ + return (0); +} + +/* ARGSUSED */ +static int +bridge_m_multicst(void *arg, boolean_t add, const uint8_t *mca) +{ + return (0); +} + +/* ARGSUSED */ +static int +bridge_m_unicst(void *arg, const uint8_t *macaddr) +{ + return (ENOTSUP); +} + +static mblk_t * +bridge_m_tx(void *arg, mblk_t *mp) +{ + _NOTE(ARGUNUSED(arg)); + freemsgchain(mp); + return (NULL); +} + +/* ARGSUSED */ +static int +bridge_ioc_listfwd(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp) +{ + bridge_listfwd_t *blf = karg; + bridge_inst_t *bip; + bridge_fwd_t *bfp, match; + avl_index_t where; + + bip = bridge_find_name(blf->blf_name); + if (bip == NULL) + return (ENOENT); + + bcopy(blf->blf_dest, match.bf_dest, ETHERADDRL); + match.bf_flags |= BFF_VLANLOCAL; + rw_enter(&bip->bi_rwlock, RW_READER); + if ((bfp = avl_find(&bip->bi_fwd, &match, &where)) == NULL) + bfp = avl_nearest(&bip->bi_fwd, where, AVL_AFTER); + else + bfp = AVL_NEXT(&bip->bi_fwd, bfp); + if (bfp == NULL) { + bzero(blf, sizeof (*blf)); + } else { + bcopy(bfp->bf_dest, blf->blf_dest, ETHERADDRL); + blf->blf_trill_nick = bfp->bf_trill_nick; + blf->blf_ms_age = + drv_hztousec(lbolt - bfp->bf_lastheard) / 1000; + blf->blf_is_local = + (bfp->bf_flags & BFF_LOCALADDR) != 0; + blf->blf_linkid = bfp->bf_links[0]->bl_linkid; + } + rw_exit(&bip->bi_rwlock); + bridge_unref(bip); + return (0); +} + +static int +bridge_m_setprop(void *arg, const char *pr_name, mac_prop_id_t pr_num, + uint_t pr_valsize, const void *pr_val) +{ + bridge_mac_t *bmp = arg; + bridge_inst_t *bip; + bridge_link_t *blp; + int err; + uint_t maxsdu; + mblk_t *mlist; + + _NOTE(ARGUNUSED(pr_name)); + switch (pr_num) { + case MAC_PROP_MTU: + if (pr_valsize < sizeof (bmp->bm_maxsdu)) { + err = EINVAL; + break; + } + (void) bcopy(pr_val, &maxsdu, sizeof (maxsdu)); + if (maxsdu == bmp->bm_maxsdu) { + err = 0; + } else if ((bip = mac_to_inst(bmp)) == NULL) { + err = ENXIO; + } else { + rw_enter(&bip->bi_rwlock, RW_WRITER); + mlist = NULL; + for (blp = list_head(&bip->bi_links); blp != NULL; + blp = list_next(&bip->bi_links, blp)) { + if (blp->bl_flags & BLF_DELETED) + continue; + if (blp->bl_maxsdu == maxsdu) + link_sdu_fail(blp, B_FALSE, &mlist); + else if (blp->bl_maxsdu == bmp->bm_maxsdu) + link_sdu_fail(blp, B_TRUE, &mlist); + } + rw_exit(&bip->bi_rwlock); + bmp->bm_maxsdu = maxsdu; + (void) mac_maxsdu_update(bmp->bm_mh, maxsdu); + send_up_messages(bip, mlist); + bridge_unref(bip); + err = 0; + } + break; + + default: + err = ENOTSUP; + break; + } + return (err); +} + +static int +bridge_m_getprop(void *arg, const char *pr_name, mac_prop_id_t pr_num, + uint_t pr_flags, uint_t pr_valsize, void *pr_val, uint_t *perm) +{ + bridge_mac_t *bmp = arg; + int err = 0; + + _NOTE(ARGUNUSED(pr_name)); + switch (pr_num) { + case MAC_PROP_MTU: { + mac_propval_range_t range; + + if (!(pr_flags & MAC_PROP_POSSIBLE)) + return (ENOTSUP); + if (pr_valsize < sizeof (mac_propval_range_t)) + return (EINVAL); + range.mpr_count = 1; + range.mpr_type = MAC_PROPVAL_UINT32; + range.range_uint32[0].mpur_min = + range.range_uint32[0].mpur_max = bmp->bm_maxsdu; + bcopy(&range, pr_val, sizeof (range)); + *perm = MAC_PROP_PERM_RW; + break; + } + case MAC_PROP_STATUS: + if (pr_valsize < sizeof (bmp->bm_linkstate)) { + err = EINVAL; + } else { + bcopy(&bmp->bm_linkstate, pr_val, + sizeof (&bmp->bm_linkstate)); + *perm = MAC_PROP_PERM_READ; + } + break; + + default: + err = ENOTSUP; + break; + } + return (err); +} + +static mac_callbacks_t bridge_m_callbacks = { + MC_SETPROP | MC_GETPROP, + bridge_m_getstat, + bridge_m_start, + bridge_m_stop, + bridge_m_setpromisc, + bridge_m_multicst, + bridge_m_unicst, + bridge_m_tx, + NULL, /* ioctl */ + NULL, /* getcapab */ + NULL, /* open */ + NULL, /* close */ + bridge_m_setprop, + bridge_m_getprop +}; + +/* + * Create kstats from a list. + */ +static kstat_t * +kstat_setup(kstat_named_t *knt, const char **names, int nstat, + const char *unitname) +{ + kstat_t *ksp; + int i; + + for (i = 0; i < nstat; i++) + kstat_named_init(&knt[i], names[i], KSTAT_DATA_UINT64); + + ksp = kstat_create_zone("bridge", 0, unitname, "net", + KSTAT_TYPE_NAMED, nstat, KSTAT_FLAG_VIRTUAL, GLOBAL_ZONEID); + if (ksp != NULL) { + ksp->ks_data = knt; + kstat_install(ksp); + } + return (ksp); +} + +/* + * Find an existing bridge_mac_t structure or allocate a new one for the given + * bridge instance. This creates the mac driver instance that snoop can use. + */ +static int +bmac_alloc(bridge_inst_t *bip, bridge_mac_t **bmacp) +{ + bridge_mac_t *bmp, *bnew; + mac_register_t *mac; + int err; + + *bmacp = NULL; + if ((mac = mac_alloc(MAC_VERSION)) == NULL) + return (EINVAL); + + bnew = kmem_zalloc(sizeof (*bnew), KM_SLEEP); + + rw_enter(&bmac_rwlock, RW_WRITER); + for (bmp = list_head(&bmac_list); bmp != NULL; + bmp = list_next(&bmac_list, bmp)) { + if (strcmp(bip->bi_name, bmp->bm_name) == 0) { + ASSERT(bmp->bm_inst == NULL); + bmp->bm_inst = bip; + rw_exit(&bmac_rwlock); + kmem_free(bnew, sizeof (*bnew)); + mac_free(mac); + *bmacp = bmp; + return (0); + } + } + + mac->m_type_ident = MAC_PLUGIN_IDENT_ETHER; + mac->m_driver = bnew; + mac->m_dip = bridge_dev_info; + mac->m_instance = (uint_t)-1; + mac->m_src_addr = (uint8_t *)zero_addr; + mac->m_callbacks = &bridge_m_callbacks; + + /* + * Note that the SDU limits are irrelevant, as nobody transmits on the + * bridge node itself. It's mainly for monitoring but we allow + * setting the bridge MTU for quick transition of all links part of the + * bridge to a new MTU. + */ + mac->m_min_sdu = 1; + mac->m_max_sdu = 1500; + err = mac_register(mac, &bnew->bm_mh); + mac_free(mac); + if (err != 0) { + rw_exit(&bmac_rwlock); + kmem_free(bnew, sizeof (*bnew)); + return (err); + } + + bnew->bm_inst = bip; + (void) strcpy(bnew->bm_name, bip->bi_name); + if (list_is_empty(&bmac_list)) { + bridge_timerid = timeout(bridge_timer, NULL, + bridge_scan_interval); + } + list_insert_tail(&bmac_list, bnew); + rw_exit(&bmac_rwlock); + + /* + * Mark the MAC as unable to go "active" so that only passive clients + * (such as snoop) can bind to it. + */ + mac_no_active(bnew->bm_mh); + *bmacp = bnew; + return (0); +} + +/* + * Disconnect the given bridge_mac_t from its bridge instance. The bridge + * instance is going away. The mac instance can't go away until the clients + * are gone (see bridge_timer). + */ +static void +bmac_disconnect(bridge_mac_t *bmp) +{ + bridge_inst_t *bip; + + bmp->bm_linkstate = LINK_STATE_DOWN; + mac_link_redo(bmp->bm_mh, LINK_STATE_DOWN); + + rw_enter(&bmac_rwlock, RW_READER); + bip = bmp->bm_inst; + bip->bi_mac = NULL; + bmp->bm_inst = NULL; + rw_exit(&bmac_rwlock); +} + +/* This is used by the avl trees to sort forwarding table entries */ +static int +fwd_compare(const void *addr1, const void *addr2) +{ + const bridge_fwd_t *fwd1 = addr1; + const bridge_fwd_t *fwd2 = addr2; + int diff = memcmp(fwd1->bf_dest, fwd2->bf_dest, ETHERADDRL); + + if (diff != 0) + return (diff > 0 ? 1 : -1); + + if ((fwd1->bf_flags ^ fwd2->bf_flags) & BFF_VLANLOCAL) { + if (fwd1->bf_vlanid > fwd2->bf_vlanid) + return (1); + else if (fwd1->bf_vlanid < fwd2->bf_vlanid) + return (-1); + } + return (0); +} + +static void +inst_free(bridge_inst_t *bip) +{ + ASSERT(bip->bi_mac == NULL); + rw_destroy(&bip->bi_rwlock); + list_destroy(&bip->bi_links); + cv_destroy(&bip->bi_linkwait); + avl_destroy(&bip->bi_fwd); + if (bip->bi_ksp != NULL) + kstat_delete(bip->bi_ksp); + kmem_free(bip, sizeof (*bip)); +} + +static bridge_inst_t * +inst_alloc(const char *bridge) +{ + bridge_inst_t *bip; + + bip = kmem_zalloc(sizeof (*bip), KM_SLEEP); + bip->bi_refs = 1; + (void) strcpy(bip->bi_name, bridge); + rw_init(&bip->bi_rwlock, NULL, RW_DRIVER, NULL); + list_create(&bip->bi_links, sizeof (bridge_link_t), + offsetof(bridge_link_t, bl_node)); + cv_init(&bip->bi_linkwait, NULL, CV_DRIVER, NULL); + avl_create(&bip->bi_fwd, fwd_compare, sizeof (bridge_fwd_t), + offsetof(bridge_fwd_t, bf_node)); + return (bip); +} + +static bridge_inst_t * +bridge_find_name(const char *bridge) +{ + bridge_inst_t *bip; + + mutex_enter(&inst_lock); + for (bip = list_head(&inst_list); bip != NULL; + bip = list_next(&inst_list, bip)) { + if (!(bip->bi_flags & BIF_SHUTDOWN) && + strcmp(bridge, bip->bi_name) == 0) { + atomic_inc_uint(&bip->bi_refs); + break; + } + } + mutex_exit(&inst_lock); + + return (bip); +} + +static int +bridge_create(datalink_id_t linkid, const char *bridge, bridge_inst_t **bipc) +{ + bridge_inst_t *bip, *bipnew; + bridge_mac_t *bmp = NULL; + int err; + + *bipc = NULL; + bipnew = inst_alloc(bridge); + + mutex_enter(&inst_lock); +lookup_retry: + for (bip = list_head(&inst_list); bip != NULL; + bip = list_next(&inst_list, bip)) { + if (strcmp(bridge, bip->bi_name) == 0) + break; + } + + /* This should not take long; if it does, we've got a design problem */ + if (bip != NULL && (bip->bi_flags & BIF_SHUTDOWN)) { + cv_wait(&inst_cv, &inst_lock); + goto lookup_retry; + } + + if (bip != NULL) { + /* We weren't expecting to find anything */ + bip = NULL; + err = EEXIST; + } else { + bip = bipnew; + bipnew = NULL; + list_insert_tail(&inst_list, bip); + } + + mutex_exit(&inst_lock); + if (bip == NULL) + goto fail; + + bip->bi_ksp = kstat_setup((kstat_named_t *)&bip->bi_kstats, + inst_kstats_list, Dim(inst_kstats_list), bip->bi_name); + + err = bmac_alloc(bip, &bmp); + if ((bip->bi_mac = bmp) == NULL) + goto fail_create; + + /* + * bm_inst is set, so the timer cannot yank the DLS rug from under us. + * No extra locking is needed here. + */ + if (!(bmp->bm_flags & BMF_DLS)) { + if ((err = dls_devnet_create(bmp->bm_mh, linkid)) != 0) + goto fail_create; + bmp->bm_flags |= BMF_DLS; + } + + bip->bi_dev = makedevice(bridge_major, mac_minor(bmp->bm_mh)); + *bipc = bip; + return (0); + +fail_create: + if (bmp != NULL) + bmac_disconnect(bip->bi_mac); + bipnew = bip; +fail: + ASSERT(bipnew->bi_trilldata == NULL); + bipnew->bi_flags |= BIF_SHUTDOWN; + inst_free(bipnew); + return (err); +} + +static void +bridge_unref(bridge_inst_t *bip) +{ + if (atomic_dec_uint_nv(&bip->bi_refs) == 0) { + ASSERT(bip->bi_flags & BIF_SHUTDOWN); + /* free up mac for reuse before leaving global list */ + if (bip->bi_mac != NULL) + bmac_disconnect(bip->bi_mac); + mutex_enter(&inst_lock); + list_remove(&inst_list, bip); + cv_broadcast(&inst_cv); + mutex_exit(&inst_lock); + inst_free(bip); + } +} + +/* + * Stream instances are used only for allocating bridges and serving as a + * control node. They serve no data-handling function. + */ +static bridge_stream_t * +stream_alloc(void) +{ + bridge_stream_t *bsp; + minor_t mn; + + if ((mn = mac_minor_hold(B_FALSE)) == 0) + return (NULL); + bsp = kmem_zalloc(sizeof (*bsp), KM_SLEEP); + bsp->bs_minor = mn; + return (bsp); +} + +static void +stream_free(bridge_stream_t *bsp) +{ + mac_minor_rele(bsp->bs_minor); + kmem_free(bsp, sizeof (*bsp)); +} + +/* Reference hold/release functions for STREAMS-related taskq */ +static void +stream_ref(bridge_stream_t *bsp) +{ + mutex_enter(&stream_ref_lock); + bsp->bs_taskq_cnt++; + mutex_exit(&stream_ref_lock); +} + +static void +stream_unref(bridge_stream_t *bsp) +{ + mutex_enter(&stream_ref_lock); + if (--bsp->bs_taskq_cnt == 0) + cv_broadcast(&stream_ref_cv); + mutex_exit(&stream_ref_lock); +} + +static void +link_free(bridge_link_t *blp) +{ + bridge_inst_t *bip = blp->bl_inst; + + ASSERT(!(blp->bl_flags & BLF_FREED)); + blp->bl_flags |= BLF_FREED; + if (blp->bl_ksp != NULL) + kstat_delete(blp->bl_ksp); + if (blp->bl_lfailmp != NULL) + freeb(blp->bl_lfailmp); + cv_destroy(&blp->bl_trillwait); + mutex_destroy(&blp->bl_trilllock); + kmem_free(blp, sizeof (*blp)); + /* Don't unreference the bridge until the MAC is closed */ + bridge_unref(bip); +} + +static void +link_unref(bridge_link_t *blp) +{ + if (atomic_dec_uint_nv(&blp->bl_refs) == 0) { + bridge_inst_t *bip = blp->bl_inst; + + ASSERT(blp->bl_flags & BLF_DELETED); + rw_enter(&bip->bi_rwlock, RW_WRITER); + list_remove(&bip->bi_links, blp); + rw_exit(&bip->bi_rwlock); + if (bip->bi_trilldata != NULL && list_is_empty(&bip->bi_links)) + cv_broadcast(&bip->bi_linkwait); + link_free(blp); + } +} + +static bridge_fwd_t * +fwd_alloc(const uint8_t *addr, uint_t nlinks, uint16_t nick) +{ + bridge_fwd_t *bfp; + + bfp = kmem_zalloc(sizeof (*bfp) + (nlinks * sizeof (bridge_link_t *)), + KM_NOSLEEP); + if (bfp != NULL) { + bcopy(addr, bfp->bf_dest, ETHERADDRL); + bfp->bf_lastheard = lbolt; + bfp->bf_maxlinks = nlinks; + bfp->bf_links = (bridge_link_t **)(bfp + 1); + bfp->bf_trill_nick = nick; + } + return (bfp); +} + +static bridge_fwd_t * +fwd_find(bridge_inst_t *bip, const uint8_t *addr, uint16_t vlanid) +{ + bridge_fwd_t *bfp, *vbfp; + bridge_fwd_t match; + + bcopy(addr, match.bf_dest, ETHERADDRL); + match.bf_flags = 0; + rw_enter(&bip->bi_rwlock, RW_READER); + if ((bfp = avl_find(&bip->bi_fwd, &match, NULL)) != NULL) { + if (bfp->bf_vlanid != vlanid && bfp->bf_vcnt > 0) { + match.bf_vlanid = vlanid; + match.bf_flags = BFF_VLANLOCAL; + vbfp = avl_find(&bip->bi_fwd, &match, NULL); + if (vbfp != NULL) + bfp = vbfp; + } + atomic_inc_uint(&bfp->bf_refs); + } + rw_exit(&bip->bi_rwlock); + return (bfp); +} + +static void +fwd_free(bridge_fwd_t *bfp) +{ + uint_t i; + bridge_inst_t *bip = bfp->bf_links[0]->bl_inst; + + KIDECR(bki_count); + for (i = 0; i < bfp->bf_nlinks; i++) + link_unref(bfp->bf_links[i]); + kmem_free(bfp, + sizeof (*bfp) + bfp->bf_maxlinks * sizeof (bridge_link_t *)); +} + +static void +fwd_unref(bridge_fwd_t *bfp) +{ + if (atomic_dec_uint_nv(&bfp->bf_refs) == 0) { + ASSERT(!(bfp->bf_flags & BFF_INTREE)); + fwd_free(bfp); + } +} + +static void +fwd_delete(bridge_fwd_t *bfp) +{ + bridge_inst_t *bip; + bridge_fwd_t *bfpzero; + + if (bfp->bf_flags & BFF_INTREE) { + ASSERT(bfp->bf_nlinks > 0); + bip = bfp->bf_links[0]->bl_inst; + rw_enter(&bip->bi_rwlock, RW_WRITER); + /* Another thread could beat us to this */ + if (bfp->bf_flags & BFF_INTREE) { + avl_remove(&bip->bi_fwd, bfp); + bfp->bf_flags &= ~BFF_INTREE; + if (bfp->bf_flags & BFF_VLANLOCAL) { + bfp->bf_flags &= ~BFF_VLANLOCAL; + bfpzero = avl_find(&bip->bi_fwd, bfp, NULL); + if (bfpzero != NULL && bfpzero->bf_vcnt > 0) + bfpzero->bf_vcnt--; + } + rw_exit(&bip->bi_rwlock); + fwd_unref(bfp); /* no longer in avl tree */ + } else { + rw_exit(&bip->bi_rwlock); + } + } +} + +static boolean_t +fwd_insert(bridge_inst_t *bip, bridge_fwd_t *bfp) +{ + avl_index_t idx; + boolean_t retv; + + rw_enter(&bip->bi_rwlock, RW_WRITER); + if (!(bip->bi_flags & BIF_SHUTDOWN) && + avl_numnodes(&bip->bi_fwd) < bip->bi_tablemax && + avl_find(&bip->bi_fwd, bfp, &idx) == NULL) { + avl_insert(&bip->bi_fwd, bfp, idx); + bfp->bf_flags |= BFF_INTREE; + atomic_inc_uint(&bfp->bf_refs); /* avl entry */ + retv = B_TRUE; + } else { + retv = B_FALSE; + } + rw_exit(&bip->bi_rwlock); + return (retv); +} + +static void +fwd_update_local(bridge_link_t *blp, const uint8_t *oldaddr, + const uint8_t *newaddr) +{ + bridge_inst_t *bip = blp->bl_inst; + bridge_fwd_t *bfp, *bfnew; + bridge_fwd_t match; + avl_index_t idx; + boolean_t drop_ref = B_FALSE; + + if (bcmp(oldaddr, newaddr, ETHERADDRL) == 0) + return; + + if (bcmp(oldaddr, zero_addr, ETHERADDRL) == 0) + goto no_old_addr; + + /* + * Find the previous entry, and remove our link from it. + */ + bcopy(oldaddr, match.bf_dest, ETHERADDRL); + rw_enter(&bip->bi_rwlock, RW_WRITER); + if ((bfp = avl_find(&bip->bi_fwd, &match, NULL)) != NULL) { + int i; + + /* + * See if we're in the list, and remove if so. + */ + for (i = 0; i < bfp->bf_nlinks; i++) { + if (bfp->bf_links[i] == blp) { + /* + * We assume writes are atomic, so no special + * MT handling is needed. The list length is + * decremented first, and then we remove + * entries. + */ + bfp->bf_nlinks--; + for (; i < bfp->bf_nlinks; i++) + bfp->bf_links[i] = bfp->bf_links[i + 1]; + drop_ref = B_TRUE; + break; + } + } + /* If no more links, then remove and free up */ + if (bfp->bf_nlinks == 0) { + avl_remove(&bip->bi_fwd, bfp); + bfp->bf_flags &= ~BFF_INTREE; + } else { + bfp = NULL; + } + } + rw_exit(&bip->bi_rwlock); + if (bfp != NULL) + fwd_unref(bfp); /* no longer in avl tree */ + + /* + * Now get the new link address and add this link to the list. The + * list should be of length 1 unless the user has configured multiple + * NICs with the same address. (That's an incorrect configuration, but + * we support it anyway.) + */ +no_old_addr: + bfp = NULL; + if ((bip->bi_flags & BIF_SHUTDOWN) || + bcmp(newaddr, zero_addr, ETHERADDRL) == 0) + goto no_new_addr; + + bcopy(newaddr, match.bf_dest, ETHERADDRL); + rw_enter(&bip->bi_rwlock, RW_WRITER); + if ((bfp = avl_find(&bip->bi_fwd, &match, &idx)) == NULL) { + bfnew = fwd_alloc(newaddr, 1, RBRIDGE_NICKNAME_NONE); + if (bfnew != NULL) + KIINCR(bki_count); + } else if (bfp->bf_nlinks < bfp->bf_maxlinks) { + /* special case: link fits in existing entry */ + bfnew = bfp; + } else { + bfnew = fwd_alloc(newaddr, bfp->bf_nlinks + 1, + RBRIDGE_NICKNAME_NONE); + if (bfnew != NULL) { + KIINCR(bki_count); + avl_remove(&bip->bi_fwd, bfp); + bfp->bf_flags &= ~BFF_INTREE; + bfnew->bf_nlinks = bfp->bf_nlinks; + bcopy(bfp->bf_links, bfnew->bf_links, + bfp->bf_nlinks * sizeof (bfp)); + /* reset the idx value due to removal above */ + (void) avl_find(&bip->bi_fwd, &match, &idx); + } + } + + if (bfnew != NULL) { + bfnew->bf_links[bfnew->bf_nlinks++] = blp; + if (drop_ref) + drop_ref = B_FALSE; + else + atomic_inc_uint(&blp->bl_refs); /* bf_links entry */ + + if (bfnew != bfp) { + /* local addresses are not subject to table limits */ + avl_insert(&bip->bi_fwd, bfnew, idx); + bfnew->bf_flags |= (BFF_INTREE | BFF_LOCALADDR); + atomic_inc_uint(&bfnew->bf_refs); /* avl entry */ + } + } + rw_exit(&bip->bi_rwlock); + +no_new_addr: + /* + * If we found an existing entry and we replaced it with a new one, + * then drop the table reference from the old one. We removed it from + * the AVL tree above. + */ + if (bfnew != NULL && bfp != NULL && bfnew != bfp) + fwd_unref(bfp); + + /* Account for removed entry. */ + if (drop_ref) + link_unref(blp); +} + +static void +bridge_new_unicst(bridge_link_t *blp) +{ + uint8_t new_mac[ETHERADDRL]; + + mac_unicast_primary_get(blp->bl_mh, new_mac); + fwd_update_local(blp, blp->bl_local_mac, new_mac); + bcopy(new_mac, blp->bl_local_mac, ETHERADDRL); +} + +/* + * We must shut down a link prior to freeing it, and doing that requires + * blocking to wait for running MAC threads while holding a reference. This is + * run from a taskq to accomplish proper link shutdown followed by reference + * drop. + */ +static void +link_shutdown(void *arg) +{ + bridge_link_t *blp = arg; + mac_handle_t mh = blp->bl_mh; + bridge_inst_t *bip; + bridge_fwd_t *bfp, *bfnext; + avl_tree_t fwd_scavenge; + int i; + + /* + * This link is being destroyed. Notify TRILL now that it's no longer + * possible to send packets. Data packets may still arrive until TRILL + * calls bridge_trill_lnunref. + */ + if (blp->bl_trilldata != NULL) + trill_lndstr_fn(blp->bl_trilldata, blp); + + if (blp->bl_flags & BLF_PROM_ADDED) + (void) mac_promisc_remove(blp->bl_mphp); + + if (blp->bl_flags & BLF_SET_BRIDGE) + mac_bridge_clear(mh, (mac_handle_t)blp); + + if (blp->bl_flags & BLF_MARGIN_ADDED) { + mac_notify_remove(blp->bl_mnh, B_TRUE); + (void) mac_margin_remove(mh, blp->bl_margin); + } + + /* Tell the clients the real link state when we leave */ + mac_link_redo(blp->bl_mh, + mac_stat_get(blp->bl_mh, MAC_STAT_LOWLINK_STATE)); + + /* Destroy all of the forwarding entries related to this link */ + avl_create(&fwd_scavenge, fwd_compare, sizeof (bridge_fwd_t), + offsetof(bridge_fwd_t, bf_node)); + bip = blp->bl_inst; + rw_enter(&bip->bi_rwlock, RW_WRITER); + bfnext = avl_first(&bip->bi_fwd); + while ((bfp = bfnext) != NULL) { + bfnext = AVL_NEXT(&bip->bi_fwd, bfp); + for (i = 0; i < bfp->bf_nlinks; i++) { + if (bfp->bf_links[i] == blp) + break; + } + if (i >= bfp->bf_nlinks) + continue; + if (bfp->bf_nlinks > 1) { + /* note that this can't be the last reference */ + link_unref(blp); + bfp->bf_nlinks--; + for (; i < bfp->bf_nlinks; i++) + bfp->bf_links[i] = bfp->bf_links[i + 1]; + } else { + ASSERT(bfp->bf_flags & BFF_INTREE); + avl_remove(&bip->bi_fwd, bfp); + bfp->bf_flags &= ~BFF_INTREE; + avl_add(&fwd_scavenge, bfp); + } + } + rw_exit(&bip->bi_rwlock); + bfnext = avl_first(&fwd_scavenge); + while ((bfp = bfnext) != NULL) { + bfnext = AVL_NEXT(&fwd_scavenge, bfp); + avl_remove(&fwd_scavenge, bfp); + fwd_unref(bfp); + } + avl_destroy(&fwd_scavenge); + + if (blp->bl_flags & BLF_CLIENT_OPEN) + mac_client_close(blp->bl_mch, 0); + + mac_close(mh); + + /* + * We are now completely removed from the active list, so drop the + * reference (see bridge_add_link). + */ + link_unref(blp); +} + +static void +shutdown_inst(bridge_inst_t *bip) +{ + bridge_link_t *blp, *blnext; + bridge_fwd_t *bfp; + + mutex_enter(&inst_lock); + if (bip->bi_flags & BIF_SHUTDOWN) { + mutex_exit(&inst_lock); + return; + } + + /* + * Once on the inst_list, the bridge instance must not leave that list + * without having the shutdown flag set first. When the shutdown flag + * is set, we own the list reference, so we must drop it before + * returning. + */ + bip->bi_flags |= BIF_SHUTDOWN; + mutex_exit(&inst_lock); + + bip->bi_control = NULL; + + rw_enter(&bip->bi_rwlock, RW_READER); + blnext = list_head(&bip->bi_links); + while ((blp = blnext) != NULL) { + blnext = list_next(&bip->bi_links, blp); + if (!(blp->bl_flags & BLF_DELETED)) { + blp->bl_flags |= BLF_DELETED; + (void) ddi_taskq_dispatch(bridge_taskq, link_shutdown, + blp, DDI_SLEEP); + } + } + while ((bfp = avl_first(&bip->bi_fwd)) != NULL) { + atomic_inc_uint(&bfp->bf_refs); + rw_exit(&bip->bi_rwlock); + fwd_delete(bfp); + fwd_unref(bfp); + rw_enter(&bip->bi_rwlock, RW_READER); + } + rw_exit(&bip->bi_rwlock); + + /* + * This bridge is being destroyed. Notify TRILL once all of the + * links are all gone. + */ + mutex_enter(&inst_lock); + while (bip->bi_trilldata != NULL && !list_is_empty(&bip->bi_links)) + cv_wait(&bip->bi_linkwait, &inst_lock); + mutex_exit(&inst_lock); + if (bip->bi_trilldata != NULL) + trill_brdstr_fn(bip->bi_trilldata, bip); + + bridge_unref(bip); +} + +/* + * This is called once by the TRILL module when it starts up. It just sets the + * global TRILL callback function pointers -- data transmit/receive and bridge + * and link destroy notification. There's only one TRILL module, so only one + * registration is needed. + * + * TRILL should call this function with NULL pointers before unloading. It + * must not do so before dropping all references to bridges and links. We + * assert that this is true on debug builds. + */ +void +bridge_trill_register_cb(trill_recv_pkt_t recv_fn, trill_encap_pkt_t encap_fn, + trill_br_dstr_t brdstr_fn, trill_ln_dstr_t lndstr_fn) +{ +#ifdef DEBUG + if (recv_fn == NULL && trill_recv_fn != NULL) { + bridge_inst_t *bip; + bridge_link_t *blp; + + mutex_enter(&inst_lock); + for (bip = list_head(&inst_list); bip != NULL; + bip = list_next(&inst_list, bip)) { + ASSERT(bip->bi_trilldata == NULL); + rw_enter(&bip->bi_rwlock, RW_READER); + for (blp = list_head(&bip->bi_links); blp != NULL; + blp = list_next(&bip->bi_links, blp)) { + ASSERT(blp->bl_trilldata == NULL); + } + rw_exit(&bip->bi_rwlock); + } + mutex_exit(&inst_lock); + } +#endif + trill_recv_fn = recv_fn; + trill_encap_fn = encap_fn; + trill_brdstr_fn = brdstr_fn; + trill_lndstr_fn = lndstr_fn; +} + +/* + * This registers the TRILL instance pointer with a bridge. Before this + * pointer is set, the forwarding, TRILL receive, and bridge destructor + * functions won't be called. + * + * TRILL holds a reference on a bridge with this call. It must free the + * reference by calling the unregister function below. + */ +bridge_inst_t * +bridge_trill_brref(const char *bname, void *ptr) +{ + char bridge[MAXLINKNAMELEN]; + bridge_inst_t *bip; + + (void) snprintf(bridge, MAXLINKNAMELEN, "%s0", bname); + bip = bridge_find_name(bridge); + if (bip != NULL) { + ASSERT(bip->bi_trilldata == NULL && ptr != NULL); + bip->bi_trilldata = ptr; + } + return (bip); +} + +void +bridge_trill_brunref(bridge_inst_t *bip) +{ + ASSERT(bip->bi_trilldata != NULL); + bip->bi_trilldata = NULL; + bridge_unref(bip); +} + +/* + * TRILL calls this function when referencing a particular link on a bridge. + * + * It holds a reference on the link, so TRILL must clear out the reference when + * it's done with the link (on unbinding). + */ +bridge_link_t * +bridge_trill_lnref(bridge_inst_t *bip, datalink_id_t linkid, void *ptr) +{ + bridge_link_t *blp; + + ASSERT(ptr != NULL); + rw_enter(&bip->bi_rwlock, RW_READER); + for (blp = list_head(&bip->bi_links); blp != NULL; + blp = list_next(&bip->bi_links, blp)) { + if (!(blp->bl_flags & BLF_DELETED) && + blp->bl_linkid == linkid && blp->bl_trilldata == NULL) { + blp->bl_trilldata = ptr; + blp->bl_flags &= ~BLF_TRILLACTIVE; + (void) memset(blp->bl_afs, 0, sizeof (blp->bl_afs)); + atomic_inc_uint(&blp->bl_refs); + break; + } + } + rw_exit(&bip->bi_rwlock); + return (blp); +} + +void +bridge_trill_lnunref(bridge_link_t *blp) +{ + mutex_enter(&blp->bl_trilllock); + ASSERT(blp->bl_trilldata != NULL); + blp->bl_trilldata = NULL; + blp->bl_flags &= ~BLF_TRILLACTIVE; + while (blp->bl_trillthreads > 0) + cv_wait(&blp->bl_trillwait, &blp->bl_trilllock); + mutex_exit(&blp->bl_trilllock); + (void) memset(blp->bl_afs, 0xff, sizeof (blp->bl_afs)); + link_unref(blp); +} + +/* + * This periodic timer performs three functions: + * 1. It scans the list of learned forwarding entries, and removes ones that + * haven't been heard from in a while. The time limit is backed down if + * we're above the configured table limit. + * 2. It walks the links and decays away the bl_learns counter. + * 3. It scans the observability node entries looking for ones that can be + * freed up. + */ +/* ARGSUSED */ +static void +bridge_timer(void *arg) +{ + bridge_inst_t *bip; + bridge_fwd_t *bfp, *bfnext; + bridge_mac_t *bmp, *bmnext; + bridge_link_t *blp; + int err; + datalink_id_t tmpid; + avl_tree_t fwd_scavenge; + clock_t age_limit; + uint32_t ldecay; + + avl_create(&fwd_scavenge, fwd_compare, sizeof (bridge_fwd_t), + offsetof(bridge_fwd_t, bf_node)); + mutex_enter(&inst_lock); + for (bip = list_head(&inst_list); bip != NULL; + bip = list_next(&inst_list, bip)) { + if (bip->bi_flags & BIF_SHUTDOWN) + continue; + rw_enter(&bip->bi_rwlock, RW_WRITER); + /* compute scaled maximum age based on table limit */ + if (avl_numnodes(&bip->bi_fwd) > bip->bi_tablemax) + bip->bi_tshift++; + else + bip->bi_tshift = 0; + if ((age_limit = bridge_fwd_age >> bip->bi_tshift) == 0) { + if (bip->bi_tshift != 0) + bip->bi_tshift--; + age_limit = 1; + } + bfnext = avl_first(&bip->bi_fwd); + while ((bfp = bfnext) != NULL) { + bfnext = AVL_NEXT(&bip->bi_fwd, bfp); + if (!(bfp->bf_flags & BFF_LOCALADDR) && + (lbolt - bfp->bf_lastheard) > age_limit) { + ASSERT(bfp->bf_flags & BFF_INTREE); + avl_remove(&bip->bi_fwd, bfp); + bfp->bf_flags &= ~BFF_INTREE; + avl_add(&fwd_scavenge, bfp); + } + } + for (blp = list_head(&bip->bi_links); blp != NULL; + blp = list_next(&bip->bi_links, blp)) { + ldecay = mac_get_ldecay(blp->bl_mh); + if (ldecay >= blp->bl_learns) + blp->bl_learns = 0; + else + atomic_add_int(&blp->bl_learns, -(int)ldecay); + } + rw_exit(&bip->bi_rwlock); + bfnext = avl_first(&fwd_scavenge); + while ((bfp = bfnext) != NULL) { + bfnext = AVL_NEXT(&fwd_scavenge, bfp); + avl_remove(&fwd_scavenge, bfp); + KIINCR(bki_expire); + fwd_unref(bfp); /* drop tree reference */ + } + } + mutex_exit(&inst_lock); + avl_destroy(&fwd_scavenge); + + /* + * Scan the bridge_mac_t entries and try to free up the ones that are + * no longer active. This must be done by polling, as neither DLS nor + * MAC provides a driver any sort of positive control over clients. + */ + rw_enter(&bmac_rwlock, RW_WRITER); + bmnext = list_head(&bmac_list); + while ((bmp = bmnext) != NULL) { + bmnext = list_next(&bmac_list, bmp); + + /* ignore active bridges */ + if (bmp->bm_inst != NULL) + continue; + + if (bmp->bm_flags & BMF_DLS) { + err = dls_devnet_destroy(bmp->bm_mh, &tmpid, B_FALSE); + ASSERT(err == 0 || err == EBUSY); + if (err == 0) + bmp->bm_flags &= ~BMF_DLS; + } + + if (!(bmp->bm_flags & BMF_DLS)) { + err = mac_unregister(bmp->bm_mh); + ASSERT(err == 0 || err == EBUSY); + if (err == 0) { + list_remove(&bmac_list, bmp); + kmem_free(bmp, sizeof (*bmp)); + } + } + } + if (list_is_empty(&bmac_list)) { + bridge_timerid = 0; + } else { + bridge_timerid = timeout(bridge_timer, NULL, + bridge_scan_interval); + } + rw_exit(&bmac_rwlock); +} + +static int +bridge_open(queue_t *rq, dev_t *devp, int oflag, int sflag, cred_t *credp) +{ + bridge_stream_t *bsp; + + if (rq->q_ptr != NULL) + return (0); + + if (sflag & MODOPEN) + return (EINVAL); + + /* + * Check the minor node number being opened. This tells us which + * bridge instance the user wants. + */ + if (getminor(*devp) != 0) { + /* + * This is a regular DLPI stream for snoop or the like. + * Redirect it through DLD. + */ + rq->q_qinfo = &bridge_dld_rinit; + OTHERQ(rq)->q_qinfo = &bridge_dld_winit; + return (dld_open(rq, devp, oflag, sflag, credp)); + } else { + /* + * Allocate the bridge control stream structure. + */ + if ((bsp = stream_alloc()) == NULL) + return (ENOSR); + rq->q_ptr = WR(rq)->q_ptr = (caddr_t)bsp; + bsp->bs_wq = WR(rq); + *devp = makedevice(getmajor(*devp), bsp->bs_minor); + qprocson(rq); + return (0); + } +} + +/* + * This is used only for bridge control streams. DLPI goes through dld + * instead. + */ +static int +bridge_close(queue_t *rq) +{ + bridge_stream_t *bsp = rq->q_ptr; + bridge_inst_t *bip; + + /* + * Wait for any stray taskq (add/delete link) entries related to this + * stream to leave the system. + */ + mutex_enter(&stream_ref_lock); + while (bsp->bs_taskq_cnt != 0) + cv_wait(&stream_ref_cv, &stream_ref_lock); + mutex_exit(&stream_ref_lock); + + qprocsoff(rq); + if ((bip = bsp->bs_inst) != NULL) + shutdown_inst(bip); + rq->q_ptr = WR(rq)->q_ptr = NULL; + stream_free(bsp); + if (bip != NULL) + bridge_unref(bip); + + return (0); +} + +static void +bridge_learn(bridge_link_t *blp, const uint8_t *saddr, uint16_t ingress_nick, + uint16_t vlanid) +{ + bridge_inst_t *bip = blp->bl_inst; + bridge_fwd_t *bfp, *bfpnew; + int i; + boolean_t replaced = B_FALSE; + + /* Ignore multi-destination address used as source; it's nonsense. */ + if (*saddr & 1) + return; + + /* + * If the source is known, then check whether it belongs on this link. + * If not, and this isn't a fixed local address, then we've detected a + * move. If it's not known, learn it. + */ + if ((bfp = fwd_find(bip, saddr, vlanid)) != NULL) { + /* + * If the packet has a fixed local source address, then there's + * nothing we can learn. We must quit. If this was a received + * packet, then the sender has stolen our address, but there's + * nothing we can do. If it's a transmitted packet, then + * that's the normal case. + */ + if (bfp->bf_flags & BFF_LOCALADDR) { + fwd_unref(bfp); + return; + } + + /* + * Check if the link (and TRILL sender, if any) being used is + * among the ones registered for this address. If so, then + * this is information that we already know. + */ + if (bfp->bf_trill_nick == ingress_nick) { + for (i = 0; i < bfp->bf_nlinks; i++) { + if (bfp->bf_links[i] == blp) { + bfp->bf_lastheard = lbolt; + fwd_unref(bfp); + return; + } + } + } + } + + /* + * Note that we intentionally "unlearn" things that appear to be under + * attack on this link. The forwarding cache is a negative thing for + * security -- it disables reachability as a performance optimization + * -- so leaving out entries optimizes for success and defends against + * the attack. Thus, the bare increment without a check in the delete + * code above is right. (And it's ok if we skid over the limit a + * little, so there's no syncronization needed on the test.) + */ + if (blp->bl_learns >= mac_get_llimit(blp->bl_mh)) { + if (bfp != NULL) { + if (bfp->bf_vcnt == 0) + fwd_delete(bfp); + fwd_unref(bfp); + } + return; + } + + atomic_inc_uint(&blp->bl_learns); + + if ((bfpnew = fwd_alloc(saddr, 1, ingress_nick)) == NULL) { + if (bfp != NULL) + fwd_unref(bfp); + return; + } + KIINCR(bki_count); + + if (bfp != NULL) { + /* + * If this is a new destination for the same VLAN, then delete + * so that we can update. If it's a different VLAN, then we're + * not going to delete the original. Split off instead into an + * IVL entry. + */ + if (bfp->bf_vlanid == vlanid) { + /* save the count of IVL duplicates */ + bfpnew->bf_vcnt = bfp->bf_vcnt; + + /* entry deletes count as learning events */ + atomic_inc_uint(&blp->bl_learns); + + /* destroy and create anew; node moved */ + fwd_delete(bfp); + replaced = B_TRUE; + KIINCR(bki_moved); + } else { + bfp->bf_vcnt++; + bfpnew->bf_flags |= BFF_VLANLOCAL; + } + fwd_unref(bfp); + } + bfpnew->bf_links[0] = blp; + bfpnew->bf_nlinks = 1; + atomic_inc_uint(&blp->bl_refs); /* bf_links entry */ + if (!fwd_insert(bip, bfpnew)) + fwd_free(bfpnew); + else if (!replaced) + KIINCR(bki_source); +} + +/* + * Process the VLAN headers for output on a given link. There are several + * cases (noting that we don't map VLANs): + * 1. The input packet is good as it is; either + * a. It has no tag, and output has same PVID + * b. It has a non-zero priority-only tag for PVID, and b_band is same + * c. It has a tag with VLAN different from PVID, and b_band is same + * 2. The tag must change: non-zero b_band is different from tag priority + * 3. The packet has a tag and should not (VLAN same as PVID, b_band zero) + * 4. The packet has no tag and needs one: + * a. VLAN ID same as PVID, but b_band is non-zero + * b. VLAN ID different from PVID + * We exclude case 1 first, then modify the packet. Note that output packets + * get a priority set by the mblk, not by the header, because QoS in bridging + * requires priority recalculation at each node. + * + * The passed-in tci is the "impossible" value 0xFFFF when no tag is present. + */ +static mblk_t * +reform_vlan_header(mblk_t *mp, uint16_t vlanid, uint16_t tci, uint16_t pvid) +{ + boolean_t source_has_tag = (tci != 0xFFFF); + mblk_t *mpcopy; + size_t mlen, minlen; + struct ether_vlan_header *evh; + int pri; + + /* This helps centralize error handling in the caller. */ + if (mp == NULL) + return (mp); + + /* No forwarded packet can have hardware checksum enabled */ + DB_CKSUMFLAGS(mp) = 0; + + /* Get the no-modification cases out of the way first */ + if (!source_has_tag && vlanid == pvid) /* 1a */ + return (mp); + + pri = VLAN_PRI(tci); + if (source_has_tag && mp->b_band == pri) { + if (vlanid != pvid) /* 1c */ + return (mp); + if (pri != 0 && VLAN_ID(tci) == 0) /* 1b */ + return (mp); + } + + /* + * We now know that we must modify the packet. Prepare for that. Note + * that if a tag is present, the caller has already done a pullup for + * the VLAN header, so we're good to go. + */ + if (MBLKL(mp) < sizeof (struct ether_header)) { + mpcopy = msgpullup(mp, sizeof (struct ether_header)); + if (mpcopy == NULL) { + freemsg(mp); + return (NULL); + } + mp = mpcopy; + } + if (DB_REF(mp) > 1 || !IS_P2ALIGNED(mp->b_rptr, sizeof (uint16_t)) || + (!source_has_tag && MBLKTAIL(mp) < VLAN_INCR)) { + minlen = mlen = MBLKL(mp); + if (!source_has_tag) + minlen += VLAN_INCR; + ASSERT(minlen >= sizeof (struct ether_vlan_header)); + /* + * We're willing to copy some data to avoid fragmentation, but + * not a lot. + */ + if (minlen > 256) + minlen = sizeof (struct ether_vlan_header); + mpcopy = allocb(minlen, BPRI_MED); + if (mpcopy == NULL) { + freemsg(mp); + return (NULL); + } + if (mlen <= minlen) { + /* We toss the first mblk when we can. */ + bcopy(mp->b_rptr, mpcopy->b_rptr, mlen); + mpcopy->b_wptr += mlen; + mpcopy->b_cont = mp->b_cont; + freeb(mp); + } else { + /* If not, then just copy what we need */ + if (!source_has_tag) + minlen = sizeof (struct ether_header); + bcopy(mp->b_rptr, mpcopy->b_rptr, minlen); + mpcopy->b_wptr += minlen; + mpcopy->b_cont = mp; + mp->b_rptr += minlen; + } + mp = mpcopy; + } + + /* LINTED: pointer alignment */ + evh = (struct ether_vlan_header *)mp->b_rptr; + if (source_has_tag) { + if (mp->b_band == 0 && vlanid == pvid) { /* 3 */ + evh->ether_tpid = evh->ether_type; + mlen = MBLKL(mp); + if (mlen > sizeof (struct ether_vlan_header)) + ovbcopy(mp->b_rptr + + sizeof (struct ether_vlan_header), + mp->b_rptr + sizeof (struct ether_header), + mlen - sizeof (struct ether_vlan_header)); + mp->b_wptr -= VLAN_INCR; + } else { /* 2 */ + if (vlanid == pvid) + vlanid = VLAN_ID_NONE; + tci = VLAN_TCI(mp->b_band, ETHER_CFI, vlanid); + evh->ether_tci = htons(tci); + } + } else { + /* case 4: no header present, but one is needed */ + mlen = MBLKL(mp); + if (mlen > sizeof (struct ether_header)) + ovbcopy(mp->b_rptr + sizeof (struct ether_header), + mp->b_rptr + sizeof (struct ether_vlan_header), + mlen - sizeof (struct ether_header)); + mp->b_wptr += VLAN_INCR; + ASSERT(mp->b_wptr <= DB_LIM(mp)); + if (vlanid == pvid) + vlanid = VLAN_ID_NONE; + tci = VLAN_TCI(mp->b_band, ETHER_CFI, vlanid); + evh->ether_type = evh->ether_tpid; + evh->ether_tpid = htons(ETHERTYPE_VLAN); + evh->ether_tci = htons(tci); + } + return (mp); +} + +/* Record VLAN information and strip header if requested . */ +static void +update_header(mblk_t *mp, mac_header_info_t *hdr_info, boolean_t striphdr) +{ + if (hdr_info->mhi_bindsap == ETHERTYPE_VLAN) { + struct ether_vlan_header *evhp; + uint16_t ether_type; + + /* LINTED: alignment */ + evhp = (struct ether_vlan_header *)mp->b_rptr; + hdr_info->mhi_istagged = B_TRUE; + hdr_info->mhi_tci = ntohs(evhp->ether_tci); + if (striphdr) { + /* + * For VLAN tagged frames update the ether_type + * in hdr_info before stripping the header. + */ + ether_type = ntohs(evhp->ether_type); + hdr_info->mhi_origsap = ether_type; + hdr_info->mhi_bindsap = (ether_type > ETHERMTU) ? + ether_type : DLS_SAP_LLC; + mp->b_rptr = (uchar_t *)(evhp + 1); + } + } else { + hdr_info->mhi_istagged = B_FALSE; + hdr_info->mhi_tci = VLAN_ID_NONE; + if (striphdr) + mp->b_rptr += sizeof (struct ether_header); + } +} + +/* + * Return B_TRUE if we're allowed to send on this link with the given VLAN ID. + */ +static boolean_t +bridge_can_send(bridge_link_t *blp, uint16_t vlanid) +{ + ASSERT(vlanid != VLAN_ID_NONE); + if (blp->bl_flags & BLF_DELETED) + return (B_FALSE); + if (blp->bl_trilldata == NULL && blp->bl_state != BLS_FORWARDING) + return (B_FALSE); + return (BRIDGE_VLAN_ISSET(blp, vlanid) && BRIDGE_AF_ISSET(blp, vlanid)); +} + +/* + * This function scans the bridge forwarding tables in order to forward a given + * packet. If the packet either doesn't need forwarding (the current link is + * correct) or the current link needs a copy as well, then the packet is + * returned to the caller. + * + * If a packet has been decapsulated from TRILL, then it must *NOT* reenter a + * TRILL tunnel. If the destination points there, then drop instead. + */ +static mblk_t * +bridge_forward(bridge_link_t *blp, mac_header_info_t *hdr_info, mblk_t *mp, + uint16_t vlanid, uint16_t tci, boolean_t from_trill, boolean_t is_xmit) +{ + mblk_t *mpsend, *mpcopy; + bridge_inst_t *bip = blp->bl_inst; + bridge_link_t *blpsend, *blpnext; + bridge_fwd_t *bfp; + uint_t i; + boolean_t selfseen = B_FALSE; + void *tdp; + const uint8_t *daddr = hdr_info->mhi_daddr; + + /* + * Check for the IEEE "reserved" multicast addresses. Messages sent to + * these addresses are used for link-local control (STP and pause), and + * are never forwarded or redirected. + */ + if (daddr[0] == 1 && daddr[1] == 0x80 && daddr[2] == 0xc2 && + daddr[3] == 0 && daddr[4] == 0 && (daddr[5] & 0xf0) == 0) { + if (from_trill) { + freemsg(mp); + mp = NULL; + } + return (mp); + } + + if ((bfp = fwd_find(bip, daddr, vlanid)) != NULL) { + + /* + * If trill indicates a destination for this node, then it's + * clearly not intended for local delivery. We must tell TRILL + * to encapsulate, as long as we didn't just decapsulate it. + */ + if (bfp->bf_trill_nick != RBRIDGE_NICKNAME_NONE) { + /* + * Error case: can't reencapsulate if the protocols are + * working correctly. + */ + if (from_trill) { + freemsg(mp); + return (NULL); + } + mutex_enter(&blp->bl_trilllock); + if ((tdp = blp->bl_trilldata) != NULL) { + blp->bl_trillthreads++; + mutex_exit(&blp->bl_trilllock); + update_header(mp, hdr_info, B_FALSE); + if (is_xmit) + mp = mac_fix_cksum(mp); + /* all trill data frames have Inner.VLAN */ + mp = reform_vlan_header(mp, vlanid, tci, 0); + if (mp == NULL) { + KIINCR(bki_drops); + fwd_unref(bfp); + return (NULL); + } + trill_encap_fn(tdp, blp, hdr_info, mp, + bfp->bf_trill_nick); + mutex_enter(&blp->bl_trilllock); + if (--blp->bl_trillthreads == 0 && + blp->bl_trilldata == NULL) + cv_broadcast(&blp->bl_trillwait); + } + mutex_exit(&blp->bl_trilllock); + + /* if TRILL has been disabled, then kill this stray */ + if (tdp == NULL) { + freemsg(mp); + fwd_delete(bfp); + } + fwd_unref(bfp); + return (NULL); + } + + /* find first link we can send on */ + for (i = 0; i < bfp->bf_nlinks; i++) { + blpsend = bfp->bf_links[i]; + if (blpsend == blp) + selfseen = B_TRUE; + else if (bridge_can_send(blpsend, vlanid)) + break; + } + + while (i < bfp->bf_nlinks) { + blpsend = bfp->bf_links[i]; + for (i++; i < bfp->bf_nlinks; i++) { + blpnext = bfp->bf_links[i]; + if (blpnext == blp) + selfseen = B_TRUE; + else if (bridge_can_send(blpnext, vlanid)) + break; + } + if (i == bfp->bf_nlinks && !selfseen) { + mpsend = mp; + mp = NULL; + } else { + mpsend = copymsg(mp); + } + + if (!from_trill && is_xmit) + mpsend = mac_fix_cksum(mpsend); + + mpsend = reform_vlan_header(mpsend, vlanid, tci, + blpsend->bl_pvid); + if (mpsend == NULL) { + KIINCR(bki_drops); + continue; + } + + KIINCR(bki_forwards); + /* + * No need to bump up the link reference count, as + * the forwarding entry itself holds a reference to + * the link. + */ + if (bfp->bf_flags & BFF_LOCALADDR) { + mac_rx_common(blpsend->bl_mh, NULL, mpsend); + } else { + KLPINCR(blpsend, bkl_xmit); + MAC_RING_TX(blpsend->bl_mh, NULL, mpsend, + mpsend); + freemsg(mpsend); + } + } + /* + * Handle a special case: if we're transmitting to the original + * link, then check whether the localaddr flag is set. If it + * is, then receive instead. This doesn't happen with ordinary + * bridging, but does happen often with TRILL decapsulation. + */ + if (mp != NULL && is_xmit && (bfp->bf_flags & BFF_LOCALADDR)) { + mac_rx_common(blp->bl_mh, NULL, mp); + mp = NULL; + } + fwd_unref(bfp); + } else { + /* + * TRILL has two cases to handle. If the packet is off the + * wire (not from TRILL), then we need to send up into the + * TRILL module to have the distribution tree computed. If the + * packet is from TRILL (decapsulated), then we're part of the + * distribution tree, and we need to copy the packet on member + * interfaces. + * + * Thus, the from TRILL case is identical to the STP case. + */ + if (!from_trill && blp->bl_trilldata != NULL) { + mutex_enter(&blp->bl_trilllock); + if ((tdp = blp->bl_trilldata) != NULL) { + blp->bl_trillthreads++; + mutex_exit(&blp->bl_trilllock); + if ((mpsend = copymsg(mp)) != NULL) { + update_header(mpsend, + hdr_info, B_FALSE); + /* + * all trill data frames have + * Inner.VLAN + */ + mpsend = reform_vlan_header(mpsend, + vlanid, tci, 0); + if (mpsend == NULL) { + KIINCR(bki_drops); + } else { + trill_encap_fn(tdp, blp, + hdr_info, mpsend, + RBRIDGE_NICKNAME_NONE); + } + } + mutex_enter(&blp->bl_trilllock); + if (--blp->bl_trillthreads == 0 && + blp->bl_trilldata == NULL) + cv_broadcast(&blp->bl_trillwait); + } + mutex_exit(&blp->bl_trilllock); + } + + /* + * This is an unknown destination, so flood. + */ + rw_enter(&bip->bi_rwlock, RW_READER); + for (blpnext = list_head(&bip->bi_links); blpnext != NULL; + blpnext = list_next(&bip->bi_links, blpnext)) { + if (blpnext == blp) + selfseen = B_TRUE; + else if (bridge_can_send(blpnext, vlanid)) + break; + } + if (blpnext != NULL) + atomic_inc_uint(&blpnext->bl_refs); + rw_exit(&bip->bi_rwlock); + while ((blpsend = blpnext) != NULL) { + rw_enter(&bip->bi_rwlock, RW_READER); + for (blpnext = list_next(&bip->bi_links, blpsend); + blpnext != NULL; + blpnext = list_next(&bip->bi_links, blpnext)) { + if (blpnext == blp) + selfseen = B_TRUE; + else if (bridge_can_send(blpnext, vlanid)) + break; + } + if (blpnext != NULL) + atomic_inc_uint(&blpnext->bl_refs); + rw_exit(&bip->bi_rwlock); + if (blpnext == NULL && !selfseen) { + mpsend = mp; + mp = NULL; + } else { + mpsend = copymsg(mp); + } + + if (!from_trill && is_xmit) + mpsend = mac_fix_cksum(mpsend); + + mpsend = reform_vlan_header(mpsend, vlanid, tci, + blpsend->bl_pvid); + if (mpsend == NULL) { + KIINCR(bki_drops); + continue; + } + + if (hdr_info->mhi_dsttype == MAC_ADDRTYPE_UNICAST) + KIINCR(bki_unknown); + else + KIINCR(bki_mbcast); + KLPINCR(blpsend, bkl_xmit); + if ((mpcopy = copymsg(mpsend)) != NULL) + mac_rx_common(blpsend->bl_mh, NULL, mpcopy); + MAC_RING_TX(blpsend->bl_mh, NULL, mpsend, mpsend); + freemsg(mpsend); + link_unref(blpsend); + } + } + + /* + * At this point, if np is non-NULL, it means that the caller needs to + * continue on the selected link. + */ + return (mp); +} + +/* + * Extract and validate the VLAN information for a given packet. This checks + * conformance with the rules for use of the PVID on the link, and for the + * allowed (configured) VLAN set. + * + * Returns B_TRUE if the packet passes, B_FALSE if it fails. + */ +static boolean_t +bridge_get_vlan(bridge_link_t *blp, mac_header_info_t *hdr_info, mblk_t *mp, + uint16_t *vlanidp, uint16_t *tcip) +{ + uint16_t tci, vlanid; + + if (hdr_info->mhi_bindsap == ETHERTYPE_VLAN) { + ptrdiff_t tpos = offsetof(struct ether_vlan_header, ether_tci); + ptrdiff_t mlen; + + /* + * Extract the VLAN ID information, regardless of alignment, + * and without a pullup. This isn't attractive, but we do this + * to avoid having to deal with the pointers stashed in + * hdr_info moving around or having the caller deal with a new + * mblk_t pointer. + */ + while (mp != NULL) { + mlen = MBLKL(mp); + if (mlen > tpos && mlen > 0) + break; + tpos -= mlen; + mp = mp->b_cont; + } + if (mp == NULL) + return (B_FALSE); + tci = mp->b_rptr[tpos] << 8; + if (++tpos >= mlen) { + do { + mp = mp->b_cont; + } while (mp != NULL && MBLKL(mp) == 0); + if (mp == NULL) + return (B_FALSE); + tpos = 0; + } + tci |= mp->b_rptr[tpos]; + + vlanid = VLAN_ID(tci); + if (VLAN_CFI(tci) != ETHER_CFI || vlanid > VLAN_ID_MAX) + return (B_FALSE); + if (vlanid == VLAN_ID_NONE || vlanid == blp->bl_pvid) + goto input_no_vlan; + if (!BRIDGE_VLAN_ISSET(blp, vlanid)) + return (B_FALSE); + } else { + tci = 0xFFFF; +input_no_vlan: + /* + * If PVID is set to zero, then untagged traffic is not + * supported here. Do not learn or forward. + */ + if ((vlanid = blp->bl_pvid) == VLAN_ID_NONE) + return (B_FALSE); + } + + *tcip = tci; + *vlanidp = vlanid; + return (B_TRUE); +} + +/* + * Handle MAC notifications. + */ +static void +bridge_notify_cb(void *arg, mac_notify_type_t note_type) +{ + bridge_link_t *blp = arg; + + switch (note_type) { + case MAC_NOTE_UNICST: + bridge_new_unicst(blp); + break; + + case MAC_NOTE_SDU_SIZE: { + uint_t maxsdu; + bridge_inst_t *bip = blp->bl_inst; + bridge_mac_t *bmp = bip->bi_mac; + boolean_t notify = B_FALSE; + mblk_t *mlist = NULL; + + mac_sdu_get(blp->bl_mh, NULL, &maxsdu); + rw_enter(&bip->bi_rwlock, RW_READER); + if (list_prev(&bip->bi_links, blp) == NULL && + list_next(&bip->bi_links, blp) == NULL) { + notify = (maxsdu != bmp->bm_maxsdu); + bmp->bm_maxsdu = maxsdu; + } + blp->bl_maxsdu = maxsdu; + if (maxsdu != bmp->bm_maxsdu) + link_sdu_fail(blp, B_TRUE, &mlist); + else if (notify) + (void) mac_maxsdu_update(bmp->bm_mh, maxsdu); + rw_exit(&bip->bi_rwlock); + send_up_messages(bip, mlist); + break; + } + } +} + +/* + * This is called by the MAC layer. As with the transmit side, we're right in + * the data path for all I/O on this port, so if we don't need to forward this + * packet anywhere, we have to send it upwards via mac_rx_common. + */ +static void +bridge_recv_cb(mac_handle_t mh, mac_resource_handle_t rsrc, mblk_t *mpnext) +{ + mblk_t *mp, *mpcopy; + bridge_link_t *blp = (bridge_link_t *)mh; + bridge_inst_t *bip = blp->bl_inst; + bridge_mac_t *bmp = bip->bi_mac; + mac_header_info_t hdr_info; + uint16_t vlanid, tci; + boolean_t trillmode = B_FALSE; + + KIINCR(bki_recv); + KLINCR(bkl_recv); + + /* + * Regardless of state, check for inbound TRILL packets when TRILL is + * active. These are pulled out of band and sent for TRILL handling. + */ + if (blp->bl_trilldata != NULL) { + void *tdp; + mblk_t *newhead; + mblk_t *tail = NULL; + + mutex_enter(&blp->bl_trilllock); + if ((tdp = blp->bl_trilldata) != NULL) { + blp->bl_trillthreads++; + mutex_exit(&blp->bl_trilllock); + trillmode = B_TRUE; + newhead = mpnext; + while ((mp = mpnext) != NULL) { + boolean_t raw_isis, bridge_group; + + mpnext = mp->b_next; + + /* + * If the header isn't readable, then leave on + * the list and continue. + */ + if (mac_header_info(blp->bl_mh, mp, + &hdr_info) != 0) { + tail = mp; + continue; + } + + /* + * The TRILL document specifies that, on + * Ethernet alone, IS-IS packets arrive with + * LLC rather than Ethertype, and using a + * specific destination address. We must check + * for that here. Also, we need to give BPDUs + * to TRILL for processing. + */ + raw_isis = bridge_group = B_FALSE; + if (hdr_info.mhi_dsttype == + MAC_ADDRTYPE_MULTICAST) { + if (memcmp(hdr_info.mhi_daddr, + all_isis_rbridges, ETHERADDRL) == 0) + raw_isis = B_TRUE; + else if (memcmp(hdr_info.mhi_daddr, + bridge_group_address, ETHERADDRL) == + 0) + bridge_group = B_TRUE; + } + if (!raw_isis && !bridge_group && + hdr_info.mhi_bindsap != ETHERTYPE_TRILL && + (hdr_info.mhi_bindsap != ETHERTYPE_VLAN || + /* LINTED: alignment */ + ((struct ether_vlan_header *)mp->b_rptr)-> + ether_type != htons(ETHERTYPE_TRILL))) { + tail = mp; + continue; + } + + /* + * We've got TRILL input. Remove from the list + * and send up through the TRILL module. (Send + * a copy through promiscuous receive just to + * support snooping on TRILL. Order isn't + * preserved strictly, but that doesn't matter + * here.) + */ + if (tail != NULL) + tail->b_next = mpnext; + mp->b_next = NULL; + if (mp == newhead) + newhead = mpnext; + mac_trill_snoop(blp->bl_mh, mp); + update_header(mp, &hdr_info, B_TRUE); + /* + * On raw IS-IS and BPDU frames, we have to + * make sure that the length is trimmed + * properly. We use origsap in order to cope + * with jumbograms for IS-IS. (Regular mac + * can't.) + */ + if (raw_isis || bridge_group) { + size_t msglen = msgdsize(mp); + + if (msglen > hdr_info.mhi_origsap) { + (void) adjmsg(mp, + hdr_info.mhi_origsap - + msglen); + } else if (msglen < + hdr_info.mhi_origsap) { + freemsg(mp); + continue; + } + } + trill_recv_fn(tdp, blp, rsrc, mp, &hdr_info); + } + mpnext = newhead; + mutex_enter(&blp->bl_trilllock); + if (--blp->bl_trillthreads == 0 && + blp->bl_trilldata == NULL) + cv_broadcast(&blp->bl_trillwait); + } + mutex_exit(&blp->bl_trilllock); + if (mpnext == NULL) + return; + } + + /* + * If this is a TRILL RBridge, then just check whether this link is + * used at all for forwarding. If not, then we're done. + */ + if (trillmode) { + if (!(blp->bl_flags & BLF_TRILLACTIVE) || + (blp->bl_flags & BLF_SDUFAIL)) { + mac_rx_common(blp->bl_mh, rsrc, mpnext); + return; + } + } else { + /* + * For regular (STP) bridges, if we're in blocking or listening + * state, then do nothing. We don't learn or forward until + * told to do so. + */ + if (blp->bl_state == BLS_BLOCKLISTEN) { + mac_rx_common(blp->bl_mh, rsrc, mpnext); + return; + } + } + + /* + * Send a copy of the message chain up to the observability node users. + * For TRILL, we must obey the VLAN AF rules, so we go packet-by- + * packet. + */ + if (!trillmode && blp->bl_state == BLS_FORWARDING && + (bmp->bm_flags & BMF_STARTED) && + (mp = copymsgchain(mpnext)) != NULL) { + mac_rx(bmp->bm_mh, NULL, mp); + } + + /* + * We must be in learning or forwarding state, or using TRILL on a link + * with one or more VLANs active. For each packet in the list, process + * the source address, and then attempt to forward. + */ + while ((mp = mpnext) != NULL) { + mpnext = mp->b_next; + mp->b_next = NULL; + + /* + * If we can't decode the header or if the header specifies a + * multicast source address (impossible!), then don't bother + * learning or forwarding, but go ahead and forward up the + * stack for subsequent processing. + */ + if (mac_header_info(blp->bl_mh, mp, &hdr_info) != 0 || + (hdr_info.mhi_saddr[0] & 1) != 0) { + KIINCR(bki_drops); + KLINCR(bkl_drops); + mac_rx_common(blp->bl_mh, rsrc, mp); + continue; + } + + /* + * Extract and validate the VLAN ID for this packet. + */ + if (!bridge_get_vlan(blp, &hdr_info, mp, &vlanid, &tci) || + !BRIDGE_AF_ISSET(blp, vlanid)) { + mac_rx_common(blp->bl_mh, rsrc, mp); + continue; + } + + if (trillmode) { + /* + * Special test required by TRILL document: must + * discard frames with outer address set to ESADI. + */ + if (memcmp(hdr_info.mhi_daddr, all_esadi_rbridges, + ETHERADDRL) == 0) { + mac_rx_common(blp->bl_mh, rsrc, mp); + continue; + } + + /* + * If we're in TRILL mode, then the call above to get + * the VLAN ID has also checked that we're the + * appointed forwarder, so report that we're handling + * this packet to any observability node users. + */ + if ((bmp->bm_flags & BMF_STARTED) && + (mpcopy = copymsg(mp)) != NULL) + mac_rx(bmp->bm_mh, NULL, mpcopy); + } + + /* + * First process the source address and learn from it. For + * TRILL, we learn only if we're the appointed forwarder. + */ + bridge_learn(blp, hdr_info.mhi_saddr, RBRIDGE_NICKNAME_NONE, + vlanid); + + /* + * Now check whether we're forwarding and look up the + * destination. If we can forward, do so. + */ + if (trillmode || blp->bl_state == BLS_FORWARDING) { + mp = bridge_forward(blp, &hdr_info, mp, vlanid, tci, + B_FALSE, B_FALSE); + } + if (mp != NULL) + mac_rx_common(blp->bl_mh, rsrc, mp); + } +} + + +/* ARGSUSED */ +static mblk_t * +bridge_xmit_cb(mac_handle_t mh, mac_ring_handle_t rh, mblk_t *mpnext) +{ + bridge_link_t *blp = (bridge_link_t *)mh; + bridge_inst_t *bip = blp->bl_inst; + bridge_mac_t *bmp = bip->bi_mac; + mac_header_info_t hdr_info; + uint16_t vlanid, tci; + mblk_t *mp, *mpcopy; + boolean_t trillmode; + + trillmode = blp->bl_trilldata != NULL; + + /* + * If we're using STP and we're in blocking or listening state, or if + * we're using TRILL and no VLANs are active, then behave as though the + * bridge isn't here at all, and send on the local link alone. + */ + if ((!trillmode && blp->bl_state == BLS_BLOCKLISTEN) || + (trillmode && + (!(blp->bl_flags & BLF_TRILLACTIVE) || + (blp->bl_flags & BLF_SDUFAIL)))) { + KIINCR(bki_sent); + KLINCR(bkl_xmit); + MAC_RING_TX(blp->bl_mh, rh, mpnext, mp); + return (mp); + } + + /* + * Send a copy of the message up to the observability node users. + * TRILL needs to check on a packet-by-packet basis. + */ + if (!trillmode && blp->bl_state == BLS_FORWARDING && + (bmp->bm_flags & BMF_STARTED) && + (mp = copymsgchain(mpnext)) != NULL) { + mac_rx(bmp->bm_mh, NULL, mp); + } + + while ((mp = mpnext) != NULL) { + mpnext = mp->b_next; + mp->b_next = NULL; + + if (mac_header_info(blp->bl_mh, mp, &hdr_info) != 0) { + freemsg(mp); + continue; + } + + /* + * Extract and validate the VLAN ID for this packet. + */ + if (!bridge_get_vlan(blp, &hdr_info, mp, &vlanid, &tci) || + !BRIDGE_AF_ISSET(blp, vlanid)) { + freemsg(mp); + continue; + } + + /* + * If we're using TRILL, then we've now validated that we're + * the forwarder for this VLAN, so go ahead and let + * observability node users know about the packet. + */ + if (trillmode && (bmp->bm_flags & BMF_STARTED) && + (mpcopy = copymsg(mp)) != NULL) { + mac_rx(bmp->bm_mh, NULL, mpcopy); + } + + /* + * We have to learn from our own transmitted packets, because + * there may be a Solaris DLPI raw sender (who can specify his + * own source address) using promiscuous mode for receive. The + * mac layer information won't (and can't) tell us everything + * we need to know. + */ + bridge_learn(blp, hdr_info.mhi_saddr, RBRIDGE_NICKNAME_NONE, + vlanid); + + /* attempt forwarding */ + if (trillmode || blp->bl_state == BLS_FORWARDING) { + mp = bridge_forward(blp, &hdr_info, mp, vlanid, tci, + B_FALSE, B_TRUE); + } + if (mp != NULL) { + MAC_RING_TX(blp->bl_mh, rh, mp, mp); + if (mp == NULL) { + KIINCR(bki_sent); + KLINCR(bkl_xmit); + } + } + /* + * If we get stuck, then stop. Don't let the user's output + * packets get out of order. (More importantly: don't try to + * bridge the same packet multiple times if flow control is + * asserted.) + */ + if (mp != NULL) { + mp->b_next = mpnext; + break; + } + } + return (mp); +} + +/* + * This is called by TRILL when it decapsulates an packet, and we must forward + * locally. On failure, we just drop. + * + * Note that the ingress_nick reported by TRILL must not represent this local + * node. + */ +void +bridge_trill_decaps(bridge_link_t *blp, mblk_t *mp, uint16_t ingress_nick) +{ + mac_header_info_t hdr_info; + uint16_t vlanid, tci; + bridge_inst_t *bip = blp->bl_inst; /* used by macros */ + mblk_t *mpcopy; + + if (mac_header_info(blp->bl_mh, mp, &hdr_info) != 0) { + freemsg(mp); + return; + } + + /* Extract VLAN ID for this packet. */ + if (hdr_info.mhi_bindsap == ETHERTYPE_VLAN) { + struct ether_vlan_header *evhp; + + /* LINTED: alignment */ + evhp = (struct ether_vlan_header *)mp->b_rptr; + tci = ntohs(evhp->ether_tci); + vlanid = VLAN_ID(tci); + } else { + /* Inner VLAN headers are required in TRILL data packets */ + DTRACE_PROBE3(bridge__trill__decaps__novlan, bridge_link_t *, + blp, mblk_t *, mp, uint16_t, ingress_nick); + freemsg(mp); + return; + } + + /* Learn the location of this sender in the RBridge network */ + bridge_learn(blp, hdr_info.mhi_saddr, ingress_nick, vlanid); + + /* attempt forwarding */ + mp = bridge_forward(blp, &hdr_info, mp, vlanid, tci, B_TRUE, B_TRUE); + if (mp != NULL) { + if (bridge_can_send(blp, vlanid)) { + /* Deliver a copy locally as well */ + if ((mpcopy = copymsg(mp)) != NULL) + mac_rx_common(blp->bl_mh, NULL, mpcopy); + MAC_RING_TX(blp->bl_mh, NULL, mp, mp); + } + if (mp == NULL) { + KIINCR(bki_sent); + KLINCR(bkl_xmit); + } else { + freemsg(mp); + } + } +} + +/* + * This function is used by TRILL _only_ to transmit TRILL-encapsulated + * packets. It sends on a single underlying link and does not bridge. + */ +mblk_t * +bridge_trill_output(bridge_link_t *blp, mblk_t *mp) +{ + bridge_inst_t *bip = blp->bl_inst; /* used by macros */ + + mac_trill_snoop(blp->bl_mh, mp); + MAC_RING_TX(blp->bl_mh, NULL, mp, mp); + if (mp == NULL) { + KIINCR(bki_sent); + KLINCR(bkl_xmit); + } + return (mp); +} + +/* + * Set the "appointed forwarder" flag array for this link. TRILL controls + * forwarding on a VLAN basis. The "trillactive" flag is an optimization for + * the forwarder. + */ +void +bridge_trill_setvlans(bridge_link_t *blp, const uint8_t *arr) +{ + int i; + uint_t newflags = 0; + + for (i = 0; i < BRIDGE_VLAN_ARR_SIZE; i++) { + if ((blp->bl_afs[i] = arr[i]) != 0) + newflags = BLF_TRILLACTIVE; + } + blp->bl_flags = (blp->bl_flags & ~BLF_TRILLACTIVE) | newflags; +} + +void +bridge_trill_flush(bridge_link_t *blp, uint16_t vlan, boolean_t dotrill) +{ + bridge_inst_t *bip = blp->bl_inst; + bridge_fwd_t *bfp, *bfnext; + avl_tree_t fwd_scavenge; + int i; + + _NOTE(ARGUNUSED(vlan)); + + avl_create(&fwd_scavenge, fwd_compare, sizeof (bridge_fwd_t), + offsetof(bridge_fwd_t, bf_node)); + rw_enter(&bip->bi_rwlock, RW_WRITER); + bfnext = avl_first(&bip->bi_fwd); + while ((bfp = bfnext) != NULL) { + bfnext = AVL_NEXT(&bip->bi_fwd, bfp); + if (bfp->bf_flags & BFF_LOCALADDR) + continue; + if (dotrill) { + /* port doesn't matter if we're flushing TRILL */ + if (bfp->bf_trill_nick == RBRIDGE_NICKNAME_NONE) + continue; + } else { + if (bfp->bf_trill_nick != RBRIDGE_NICKNAME_NONE) + continue; + for (i = 0; i < bfp->bf_nlinks; i++) { + if (bfp->bf_links[i] == blp) + break; + } + if (i >= bfp->bf_nlinks) + continue; + } + ASSERT(bfp->bf_flags & BFF_INTREE); + avl_remove(&bip->bi_fwd, bfp); + bfp->bf_flags &= ~BFF_INTREE; + avl_add(&fwd_scavenge, bfp); + } + rw_exit(&bip->bi_rwlock); + bfnext = avl_first(&fwd_scavenge); + while ((bfp = bfnext) != NULL) { + bfnext = AVL_NEXT(&fwd_scavenge, bfp); + avl_remove(&fwd_scavenge, bfp); + fwd_unref(bfp); + } + avl_destroy(&fwd_scavenge); +} + +/* + * Let the mac module take or drop a reference to a bridge link. When this is + * called, the mac module is holding the mi_bridge_lock, so the link cannot be + * in the process of entering or leaving a bridge. + */ +static void +bridge_ref_cb(mac_handle_t mh, boolean_t hold) +{ + bridge_link_t *blp = (bridge_link_t *)mh; + + if (hold) + atomic_inc_uint(&blp->bl_refs); + else + link_unref(blp); +} + +/* + * Handle link state changes reported by the mac layer. This acts as a filter + * for link state changes: if a link is reporting down, but there are other + * links still up on the bridge, then the state is changed to "up." When the + * last link goes down, all are marked down, and when the first link goes up, + * all are marked up. (Recursion is avoided by the use of the "redo" function.) + * + * We treat unknown as equivalent to "up." + */ +static link_state_t +bridge_ls_cb(mac_handle_t mh, link_state_t newls) +{ + bridge_link_t *blp = (bridge_link_t *)mh; + bridge_link_t *blcmp; + bridge_inst_t *bip; + bridge_mac_t *bmp; + + if (newls != LINK_STATE_DOWN && blp->bl_linkstate != LINK_STATE_DOWN || + (blp->bl_flags & (BLF_DELETED|BLF_SDUFAIL))) { + blp->bl_linkstate = newls; + return (newls); + } + + /* + * Scan first to see if there are any other non-down links. If there + * are, then we're done. Otherwise, if all others are down, then the + * state of this link is the state of the bridge. + */ + bip = blp->bl_inst; + rw_enter(&bip->bi_rwlock, RW_WRITER); + for (blcmp = list_head(&bip->bi_links); blcmp != NULL; + blcmp = list_next(&bip->bi_links, blcmp)) { + if (blcmp != blp && + !(blcmp->bl_flags & (BLF_DELETED|BLF_SDUFAIL)) && + blcmp->bl_linkstate != LINK_STATE_DOWN) + break; + } + + if (blcmp != NULL) { + /* + * If there are other links that are considered up, then tell + * the caller that the link is actually still up, regardless of + * this link's underlying state. + */ + blp->bl_linkstate = newls; + newls = LINK_STATE_UP; + } else if (blp->bl_linkstate != newls) { + /* + * If we've found no other 'up' links, and this link has + * changed state, then report the new state of the bridge to + * all other clients. + */ + blp->bl_linkstate = newls; + for (blcmp = list_head(&bip->bi_links); blcmp != NULL; + blcmp = list_next(&bip->bi_links, blcmp)) { + if (blcmp != blp && !(blcmp->bl_flags & BLF_DELETED)) + mac_link_redo(blcmp->bl_mh, newls); + } + bmp = bip->bi_mac; + if ((bmp->bm_linkstate = newls) != LINK_STATE_DOWN) + bmp->bm_linkstate = LINK_STATE_UP; + mac_link_redo(bmp->bm_mh, bmp->bm_linkstate); + } + rw_exit(&bip->bi_rwlock); + return (newls); +} + +static void +bridge_add_link(void *arg) +{ + mblk_t *mp = arg; + bridge_stream_t *bsp; + bridge_inst_t *bip, *bipt; + bridge_mac_t *bmp; + datalink_id_t linkid; + int err; + mac_handle_t mh; + uint_t maxsdu; + bridge_link_t *blp = NULL, *blpt; + const mac_info_t *mip; + boolean_t macopen = B_FALSE; + char linkname[MAXLINKNAMELEN]; + char kstatname[KSTAT_STRLEN]; + int i; + link_state_t linkstate; + mblk_t *mlist; + + bsp = (bridge_stream_t *)mp->b_next; + mp->b_next = NULL; + bip = bsp->bs_inst; + /* LINTED: alignment */ + linkid = *(datalink_id_t *)mp->b_cont->b_rptr; + + /* + * First make sure that there is no other bridge that has this link. + * We don't want to overlap operations from two bridges; the MAC layer + * supports only one bridge on a given MAC at a time. + * + * We rely on the fact that there's just one taskq thread for the + * bridging module: once we've checked for a duplicate, we can drop the + * lock, because no other thread could possibly be adding another link + * until we're done. + */ + mutex_enter(&inst_lock); + for (bipt = list_head(&inst_list); bipt != NULL; + bipt = list_next(&inst_list, bipt)) { + rw_enter(&bipt->bi_rwlock, RW_READER); + for (blpt = list_head(&bipt->bi_links); blpt != NULL; + blpt = list_next(&bipt->bi_links, blpt)) { + if (linkid == blpt->bl_linkid) + break; + } + rw_exit(&bipt->bi_rwlock); + if (blpt != NULL) + break; + } + mutex_exit(&inst_lock); + if (bipt != NULL) { + err = EBUSY; + goto fail; + } + + if ((err = mac_open_by_linkid(linkid, &mh)) != 0) + goto fail; + macopen = B_TRUE; + + /* we bridge only Ethernet */ + mip = mac_info(mh); + if (mip->mi_media != DL_ETHER) { + err = ENOTSUP; + goto fail; + } + + /* + * Get the current maximum SDU on this interface. If there are other + * links on the bridge, then this one must match, or it errors out. + * Otherwise, the first link becomes the standard for the new bridge. + */ + mac_sdu_get(mh, NULL, &maxsdu); + bmp = bip->bi_mac; + if (list_is_empty(&bip->bi_links)) { + bmp->bm_maxsdu = maxsdu; + (void) mac_maxsdu_update(bmp->bm_mh, maxsdu); + } + + /* figure the kstat name; also used as the mac client name */ + i = MBLKL(mp->b_cont) - sizeof (datalink_id_t); + if (i < 0 || i >= MAXLINKNAMELEN) + i = MAXLINKNAMELEN - 1; + bcopy(mp->b_cont->b_rptr + sizeof (datalink_id_t), linkname, i); + linkname[i] = '\0'; + (void) snprintf(kstatname, sizeof (kstatname), "%s-%s", bip->bi_name, + linkname); + + if ((blp = kmem_zalloc(sizeof (*blp), KM_NOSLEEP)) == NULL) { + err = ENOMEM; + goto fail; + } + blp->bl_lfailmp = allocb(sizeof (bridge_ctl_t), BPRI_MED); + if (blp->bl_lfailmp == NULL) { + kmem_free(blp, sizeof (*blp)); + err = ENOMEM; + goto fail; + } + + atomic_inc_uint(&bip->bi_refs); + blp->bl_inst = bip; + blp->bl_mh = mh; + blp->bl_linkid = linkid; + blp->bl_maxsdu = maxsdu; + cv_init(&blp->bl_trillwait, NULL, CV_DRIVER, NULL); + mutex_init(&blp->bl_trilllock, NULL, MUTEX_DRIVER, NULL); + (void) memset(blp->bl_afs, 0xff, sizeof (blp->bl_afs)); + + err = mac_client_open(mh, &blp->bl_mch, kstatname, 0); + if (err != 0) + goto fail; + blp->bl_flags |= BLF_CLIENT_OPEN; + + err = mac_margin_add(mh, &blp->bl_margin, B_TRUE); + if (err != 0) + goto fail; + blp->bl_flags |= BLF_MARGIN_ADDED; + + blp->bl_mnh = mac_notify_add(mh, bridge_notify_cb, blp); + + err = mac_bridge_set(mh, (mac_handle_t)blp); + if (err != 0) + goto fail; + blp->bl_flags |= BLF_SET_BRIDGE; + + err = mac_promisc_add(blp->bl_mch, MAC_CLIENT_PROMISC_ALL, NULL, + blp, &blp->bl_mphp, MAC_PROMISC_FLAGS_NO_TX_LOOP); + if (err != 0) + goto fail; + blp->bl_flags |= BLF_PROM_ADDED; + + bridge_new_unicst(blp); + + blp->bl_ksp = kstat_setup((kstat_named_t *)&blp->bl_kstats, + link_kstats_list, Dim(link_kstats_list), kstatname); + + /* + * The link holds a reference to the bridge instance, so that the + * instance can't go away before the link is freed. The insertion into + * bi_links holds a reference on the link. When marking as removed + * from bi_links (BLF_DELETED), drop the reference on the link. When + * freeing the link, drop the reference on the instance. + */ + rw_enter(&bip->bi_rwlock, RW_WRITER); + list_insert_tail(&bip->bi_links, blp); + atomic_inc_uint(&blp->bl_refs); + + /* + * If the new link is no good on this bridge, then let the daemon know + * about the problem. + */ + mlist = NULL; + if (maxsdu != bmp->bm_maxsdu) + link_sdu_fail(blp, B_TRUE, &mlist); + rw_exit(&bip->bi_rwlock); + send_up_messages(bip, mlist); + + /* + * Trigger a link state update so that if this link is the first one + * "up" in the bridge, then we notify everyone. This triggers a trip + * through bridge_ls_cb. + */ + linkstate = mac_stat_get(mh, MAC_STAT_LOWLINK_STATE); + blp->bl_linkstate = LINK_STATE_DOWN; + mac_link_update(mh, linkstate); + + /* + * We now need to report back to the stream that invoked us, and then + * drop the reference on the stream that we're holding. + */ + miocack(bsp->bs_wq, mp, 0, 0); + stream_unref(bsp); + return; + +fail: + if (blp == NULL) { + if (macopen) + mac_close(mh); + } else { + link_shutdown(blp); + link_free(blp); + } + miocnak(bsp->bs_wq, mp, 0, err); + stream_unref(bsp); +} + +static void +bridge_rem_link(void *arg) +{ + mblk_t *mp = arg; + bridge_stream_t *bsp; + bridge_inst_t *bip; + bridge_mac_t *bmp; + datalink_id_t linkid; + bridge_link_t *blp, *blsave; + boolean_t found; + mblk_t *mlist; + + bsp = (bridge_stream_t *)mp->b_next; + mp->b_next = NULL; + bip = bsp->bs_inst; + /* LINTED: alignment */ + linkid = *(datalink_id_t *)mp->b_cont->b_rptr; + + /* + * We become reader here so that we can loop over the other links and + * deliver link up/down notification. + */ + rw_enter(&bip->bi_rwlock, RW_READER); + found = B_FALSE; + for (blp = list_head(&bip->bi_links); blp != NULL; + blp = list_next(&bip->bi_links, blp)) { + if (blp->bl_linkid == linkid && + !(blp->bl_flags & BLF_DELETED)) { + blp->bl_flags |= BLF_DELETED; + (void) ddi_taskq_dispatch(bridge_taskq, link_shutdown, + blp, DDI_SLEEP); + found = B_TRUE; + break; + } + } + + /* + * Check if this link is up and the remainder of the links are all + * down. + */ + if (blp != NULL && blp->bl_linkstate != LINK_STATE_DOWN) { + for (blp = list_head(&bip->bi_links); blp != NULL; + blp = list_next(&bip->bi_links, blp)) { + if (blp->bl_linkstate != LINK_STATE_DOWN && + !(blp->bl_flags & (BLF_DELETED|BLF_SDUFAIL))) + break; + } + if (blp == NULL) { + for (blp = list_head(&bip->bi_links); blp != NULL; + blp = list_next(&bip->bi_links, blp)) { + if (!(blp->bl_flags & BLF_DELETED)) + mac_link_redo(blp->bl_mh, + LINK_STATE_DOWN); + } + bmp = bip->bi_mac; + bmp->bm_linkstate = LINK_STATE_DOWN; + mac_link_redo(bmp->bm_mh, LINK_STATE_DOWN); + } + } + + /* + * Check if there's just one working link left on the bridge. If so, + * then that link is now authoritative for bridge MTU. + */ + blsave = NULL; + for (blp = list_head(&bip->bi_links); blp != NULL; + blp = list_next(&bip->bi_links, blp)) { + if (!(blp->bl_flags & BLF_DELETED)) { + if (blsave == NULL) + blsave = blp; + else + break; + } + } + mlist = NULL; + bmp = bip->bi_mac; + if (blsave != NULL && blp == NULL && + blsave->bl_maxsdu != bmp->bm_maxsdu) { + bmp->bm_maxsdu = blsave->bl_maxsdu; + (void) mac_maxsdu_update(bmp->bm_mh, blsave->bl_maxsdu); + link_sdu_fail(blsave, B_FALSE, &mlist); + } + rw_exit(&bip->bi_rwlock); + send_up_messages(bip, mlist); + + if (found) + miocack(bsp->bs_wq, mp, 0, 0); + else + miocnak(bsp->bs_wq, mp, 0, ENOENT); + stream_unref(bsp); +} + +/* + * This function intentionally returns with bi_rwlock held; it is intended for + * quick checks and updates. + */ +static bridge_link_t * +enter_link(bridge_inst_t *bip, datalink_id_t linkid) +{ + bridge_link_t *blp; + + rw_enter(&bip->bi_rwlock, RW_READER); + for (blp = list_head(&bip->bi_links); blp != NULL; + blp = list_next(&bip->bi_links, blp)) { + if (blp->bl_linkid == linkid && !(blp->bl_flags & BLF_DELETED)) + break; + } + return (blp); +} + +static void +bridge_ioctl(queue_t *wq, mblk_t *mp) +{ + bridge_stream_t *bsp = wq->q_ptr; + bridge_inst_t *bip; + struct iocblk *iop; + int rc = EINVAL; + int len = 0; + bridge_link_t *blp; + cred_t *cr; + + /* LINTED: alignment */ + iop = (struct iocblk *)mp->b_rptr; + + /* + * For now, all of the bridge ioctls are privileged. + */ + if ((cr = msg_getcred(mp, NULL)) == NULL) + cr = iop->ioc_cr; + if (cr != NULL && secpolicy_net_config(cr, B_FALSE) != 0) { + miocnak(wq, mp, 0, EPERM); + return; + } + + switch (iop->ioc_cmd) { + case BRIOC_NEWBRIDGE: { + bridge_newbridge_t *bnb; + + if (bsp->bs_inst != NULL || + (rc = miocpullup(mp, sizeof (bridge_newbridge_t))) != 0) + break; + /* LINTED: alignment */ + bnb = (bridge_newbridge_t *)mp->b_cont->b_rptr; + bnb->bnb_name[MAXNAMELEN-1] = '\0'; + if ((rc = bridge_create(bnb->bnb_linkid, + bnb->bnb_name, &bip)) != 0) + break; + + rw_enter(&bip->bi_rwlock, RW_WRITER); + if (bip->bi_control != NULL) { + rw_exit(&bip->bi_rwlock); + bridge_unref(bip); + rc = EBUSY; + } else { + atomic_inc_uint(&bip->bi_refs); + bsp->bs_inst = bip; /* stream holds reference */ + bip->bi_control = bsp; + rw_exit(&bip->bi_rwlock); + rc = 0; + } + break; + } + + case BRIOC_ADDLINK: + if ((bip = bsp->bs_inst) == NULL || + (rc = miocpullup(mp, sizeof (datalink_id_t))) != 0) + break; + /* + * We cannot perform the action in this thread, because we're + * not in process context, and we may already be holding + * MAC-related locks. Place the request on taskq. + */ + mp->b_next = (mblk_t *)bsp; + stream_ref(bsp); + (void) ddi_taskq_dispatch(bridge_taskq, bridge_add_link, mp, + DDI_SLEEP); + return; + + case BRIOC_REMLINK: + if ((bip = bsp->bs_inst) == NULL || + (rc = miocpullup(mp, sizeof (datalink_id_t))) != 0) + break; + /* + * We cannot perform the action in this thread, because we're + * not in process context, and we may already be holding + * MAC-related locks. Place the request on taskq. + */ + mp->b_next = (mblk_t *)bsp; + stream_ref(bsp); + (void) ddi_taskq_dispatch(bridge_taskq, bridge_rem_link, mp, + DDI_SLEEP); + return; + + case BRIOC_SETSTATE: { + bridge_setstate_t *bss; + + if ((bip = bsp->bs_inst) == NULL || + (rc = miocpullup(mp, sizeof (*bss))) != 0) + break; + /* LINTED: alignment */ + bss = (bridge_setstate_t *)mp->b_cont->b_rptr; + if ((blp = enter_link(bip, bss->bss_linkid)) == NULL) { + rc = ENOENT; + } else { + rc = 0; + blp->bl_state = bss->bss_state; + } + rw_exit(&bip->bi_rwlock); + break; + } + + case BRIOC_SETPVID: { + bridge_setpvid_t *bsv; + + if ((bip = bsp->bs_inst) == NULL || + (rc = miocpullup(mp, sizeof (*bsv))) != 0) + break; + /* LINTED: alignment */ + bsv = (bridge_setpvid_t *)mp->b_cont->b_rptr; + if (bsv->bsv_vlan > VLAN_ID_MAX) + break; + if ((blp = enter_link(bip, bsv->bsv_linkid)) == NULL) { + rc = ENOENT; + } else if (blp->bl_pvid == bsv->bsv_vlan) { + rc = 0; + } else { + rc = 0; + BRIDGE_VLAN_CLR(blp, blp->bl_pvid); + blp->bl_pvid = bsv->bsv_vlan; + if (blp->bl_pvid != 0) + BRIDGE_VLAN_SET(blp, blp->bl_pvid); + } + rw_exit(&bip->bi_rwlock); + break; + } + + case BRIOC_VLANENAB: { + bridge_vlanenab_t *bve; + + if ((bip = bsp->bs_inst) == NULL || + (rc = miocpullup(mp, sizeof (*bve))) != 0) + break; + /* LINTED: alignment */ + bve = (bridge_vlanenab_t *)mp->b_cont->b_rptr; + if (bve->bve_vlan > VLAN_ID_MAX) + break; + if ((blp = enter_link(bip, bve->bve_linkid)) == NULL) { + rc = ENOENT; + } else { + rc = 0; + /* special case: vlan 0 means "all" */ + if (bve->bve_vlan == 0) { + (void) memset(blp->bl_vlans, + bve->bve_onoff ? ~0 : 0, + sizeof (blp->bl_vlans)); + BRIDGE_VLAN_CLR(blp, 0); + if (blp->bl_pvid != 0) + BRIDGE_VLAN_SET(blp, blp->bl_pvid); + } else if (bve->bve_vlan == blp->bl_pvid) { + rc = EINVAL; + } else if (bve->bve_onoff) { + BRIDGE_VLAN_SET(blp, bve->bve_vlan); + } else { + BRIDGE_VLAN_CLR(blp, bve->bve_vlan); + } + } + rw_exit(&bip->bi_rwlock); + break; + } + + case BRIOC_FLUSHFWD: { + bridge_flushfwd_t *bff; + bridge_fwd_t *bfp, *bfnext; + avl_tree_t fwd_scavenge; + int i; + + if ((bip = bsp->bs_inst) == NULL || + (rc = miocpullup(mp, sizeof (*bff))) != 0) + break; + /* LINTED: alignment */ + bff = (bridge_flushfwd_t *)mp->b_cont->b_rptr; + rw_enter(&bip->bi_rwlock, RW_WRITER); + /* This case means "all" */ + if (bff->bff_linkid == DATALINK_INVALID_LINKID) { + blp = NULL; + } else { + for (blp = list_head(&bip->bi_links); blp != NULL; + blp = list_next(&bip->bi_links, blp)) { + if (blp->bl_linkid == bff->bff_linkid && + !(blp->bl_flags & BLF_DELETED)) + break; + } + if (blp == NULL) { + rc = ENOENT; + rw_exit(&bip->bi_rwlock); + break; + } + } + avl_create(&fwd_scavenge, fwd_compare, sizeof (bridge_fwd_t), + offsetof(bridge_fwd_t, bf_node)); + bfnext = avl_first(&bip->bi_fwd); + while ((bfp = bfnext) != NULL) { + bfnext = AVL_NEXT(&bip->bi_fwd, bfp); + if (bfp->bf_flags & BFF_LOCALADDR) + continue; + if (blp != NULL) { + for (i = 0; i < bfp->bf_maxlinks; i++) { + if (bfp->bf_links[i] == blp) + break; + } + /* + * If the link is there and we're excluding, + * then skip. If the link is not there and + * we're doing only that link, then skip. + */ + if ((i < bfp->bf_maxlinks) == bff->bff_exclude) + continue; + } + ASSERT(bfp->bf_flags & BFF_INTREE); + avl_remove(&bip->bi_fwd, bfp); + bfp->bf_flags &= ~BFF_INTREE; + avl_add(&fwd_scavenge, bfp); + } + rw_exit(&bip->bi_rwlock); + bfnext = avl_first(&fwd_scavenge); + while ((bfp = bfnext) != NULL) { + bfnext = AVL_NEXT(&fwd_scavenge, bfp); + avl_remove(&fwd_scavenge, bfp); + fwd_unref(bfp); /* drop tree reference */ + } + avl_destroy(&fwd_scavenge); + break; + } + + case BRIOC_TABLEMAX: + if ((bip = bsp->bs_inst) == NULL || + (rc = miocpullup(mp, sizeof (uint32_t))) != 0) + break; + /* LINTED: alignment */ + bip->bi_tablemax = *(uint32_t *)mp->b_cont->b_rptr; + break; + } + + if (rc == 0) + miocack(wq, mp, len, 0); + else + miocnak(wq, mp, 0, rc); +} + +static void +bridge_wput(queue_t *wq, mblk_t *mp) +{ + switch (DB_TYPE(mp)) { + case M_IOCTL: + bridge_ioctl(wq, mp); + break; + case M_FLUSH: + if (*mp->b_rptr & FLUSHW) + *mp->b_rptr &= ~FLUSHW; + if (*mp->b_rptr & FLUSHR) + qreply(wq, mp); + else + freemsg(mp); + break; + default: + freemsg(mp); + break; + } +} + +/* + * This function allocates the main data structures for the bridge driver and + * connects us into devfs. + */ +static void +bridge_inst_init(void) +{ + bridge_scan_interval = 5 * drv_usectohz(1000000); + bridge_fwd_age = 25 * drv_usectohz(1000000); + + rw_init(&bmac_rwlock, NULL, RW_DRIVER, NULL); + list_create(&bmac_list, sizeof (bridge_mac_t), + offsetof(bridge_mac_t, bm_node)); + list_create(&inst_list, sizeof (bridge_inst_t), + offsetof(bridge_inst_t, bi_node)); + cv_init(&inst_cv, NULL, CV_DRIVER, NULL); + mutex_init(&inst_lock, NULL, MUTEX_DRIVER, NULL); + cv_init(&stream_ref_cv, NULL, CV_DRIVER, NULL); + mutex_init(&stream_ref_lock, NULL, MUTEX_DRIVER, NULL); + + mac_bridge_vectors(bridge_xmit_cb, bridge_recv_cb, bridge_ref_cb, + bridge_ls_cb); +} + +/* + * This function disconnects from devfs and destroys all data structures in + * preparation for unload. It's assumed that there are no active bridge + * references left at this point. + */ +static void +bridge_inst_fini(void) +{ + mac_bridge_vectors(NULL, NULL, NULL, NULL); + if (bridge_timerid != 0) + (void) untimeout(bridge_timerid); + rw_destroy(&bmac_rwlock); + list_destroy(&bmac_list); + list_destroy(&inst_list); + cv_destroy(&inst_cv); + mutex_destroy(&inst_lock); + cv_destroy(&stream_ref_cv); + mutex_destroy(&stream_ref_lock); +} + +/* + * bridge_attach() + * + * Description: + * Attach bridge driver to the system. + */ +static int +bridge_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) +{ + if (cmd != DDI_ATTACH) + return (DDI_FAILURE); + + if (ddi_create_minor_node(dip, BRIDGE_CTL, S_IFCHR, 0, DDI_PSEUDO, + CLONE_DEV) == DDI_FAILURE) { + return (DDI_FAILURE); + } + + if (dld_ioc_register(BRIDGE_IOC, bridge_ioc_list, + DLDIOCCNT(bridge_ioc_list)) != 0) { + ddi_remove_minor_node(dip, BRIDGE_CTL); + return (DDI_FAILURE); + } + + bridge_dev_info = dip; + bridge_major = ddi_driver_major(dip); + bridge_taskq = ddi_taskq_create(dip, "bridge", 1, TASKQ_DEFAULTPRI, 0); + return (DDI_SUCCESS); +} + +/* + * bridge_detach() + * + * Description: + * Detach an interface to the system. + */ +static int +bridge_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) +{ + if (cmd != DDI_DETACH) + return (DDI_FAILURE); + + ddi_remove_minor_node(dip, NULL); + ddi_taskq_destroy(bridge_taskq); + bridge_dev_info = NULL; + return (DDI_SUCCESS); +} + +/* + * bridge_info() + * + * Description: + * Translate "dev_t" to a pointer to the associated "dev_info_t". + */ +/* ARGSUSED */ +static int +bridge_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, + void **result) +{ + int rc; + + switch (infocmd) { + case DDI_INFO_DEVT2DEVINFO: + if (bridge_dev_info == NULL) { + rc = DDI_FAILURE; + } else { + *result = (void *)bridge_dev_info; + rc = DDI_SUCCESS; + } + break; + case DDI_INFO_DEVT2INSTANCE: + *result = NULL; + rc = DDI_SUCCESS; + break; + default: + rc = DDI_FAILURE; + break; + } + return (rc); +} + +static struct module_info bridge_modinfo = { + 2105, /* mi_idnum */ + "bridge", /* mi_idname */ + 0, /* mi_minpsz */ + 16384, /* mi_maxpsz */ + 65536, /* mi_hiwat */ + 128 /* mi_lowat */ +}; + +static struct qinit bridge_rinit = { + NULL, /* qi_putp */ + NULL, /* qi_srvp */ + bridge_open, /* qi_qopen */ + bridge_close, /* qi_qclose */ + NULL, /* qi_qadmin */ + &bridge_modinfo, /* qi_minfo */ + NULL /* qi_mstat */ +}; + +static struct qinit bridge_winit = { + (int (*)())bridge_wput, /* qi_putp */ + NULL, /* qi_srvp */ + NULL, /* qi_qopen */ + NULL, /* qi_qclose */ + NULL, /* qi_qadmin */ + &bridge_modinfo, /* qi_minfo */ + NULL /* qi_mstat */ +}; + +static struct streamtab bridge_tab = { + &bridge_rinit, /* st_rdinit */ + &bridge_winit /* st_wrinit */ +}; + +/* No STREAMS perimeters; we do all our own locking */ +DDI_DEFINE_STREAM_OPS(bridge_ops, nulldev, nulldev, bridge_attach, + bridge_detach, nodev, bridge_info, D_NEW | D_MP, &bridge_tab, + ddi_quiesce_not_supported); + +static struct modldrv modldrv = { + &mod_driverops, + "bridging driver", + &bridge_ops +}; + +static struct modlinkage modlinkage = { + MODREV_1, + (void *)&modldrv, + NULL +}; + +int +_init(void) +{ + int retv; + + bridge_inst_init(); + if ((retv = mod_install(&modlinkage)) != 0) + bridge_inst_fini(); + return (retv); +} + +int +_fini(void) +{ + int retv; + + rw_enter(&bmac_rwlock, RW_READER); + retv = list_is_empty(&bmac_list) ? 0 : EBUSY; + rw_exit(&bmac_rwlock); + if (retv == 0 && + (retv = mod_remove(&modlinkage)) == 0) + bridge_inst_fini(); + return (retv); +} + +int +_info(struct modinfo *modinfop) +{ + return (mod_info(&modlinkage, modinfop)); +} diff --git a/usr/src/uts/common/io/bridge.conf b/usr/src/uts/common/io/bridge.conf new file mode 100644 index 0000000000..e46704666a --- /dev/null +++ b/usr/src/uts/common/io/bridge.conf @@ -0,0 +1,27 @@ +# +# 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. +# + +name="bridge" parent="pseudo" instance=0; diff --git a/usr/src/uts/common/io/dld/dld_drv.c b/usr/src/uts/common/io/dld/dld_drv.c index 332cc8d054..1aa50821fe 100644 --- a/usr/src/uts/common/io/dld/dld_drv.c +++ b/usr/src/uts/common/io/dld/dld_drv.c @@ -1190,7 +1190,8 @@ static dld_ioc_modentry_t dld_ioc_modtable[] = { {DLD_IOC, "dld", drv_ioc_list, DLDIOCCNT(drv_ioc_list)}, {AGGR_IOC, "aggr", NULL, 0}, {VNIC_IOC, "vnic", NULL, 0}, - {SIMNET_IOC, "simnet", NULL, 0} + {SIMNET_IOC, "simnet", NULL, 0}, + {BRIDGE_IOC, "bridge", NULL, 0} }; #define DLDIOC_CNT \ (sizeof (dld_ioc_modtable) / sizeof (dld_ioc_modentry_t)) diff --git a/usr/src/uts/common/io/dld/dld_str.c b/usr/src/uts/common/io/dld/dld_str.c index 8f91b1a10c..99b42ba546 100644 --- a/usr/src/uts/common/io/dld/dld_str.c +++ b/usr/src/uts/common/io/dld/dld_str.c @@ -54,6 +54,7 @@ static void ioc_native(dld_str_t *, mblk_t *); static void ioc_margin(dld_str_t *, mblk_t *); static void ioc_raw(dld_str_t *, mblk_t *); static void ioc_fast(dld_str_t *, mblk_t *); +static void ioc_lowlink(dld_str_t *, mblk_t *); static void ioc(dld_str_t *, mblk_t *); static void dld_ioc(dld_str_t *, mblk_t *); static void dld_wput_nondata(dld_str_t *, mblk_t *); @@ -913,7 +914,8 @@ str_mdata_raw_put(dld_str_t *dsp, mblk_t *mp) * but both pri and VID are 0. */ pri = VLAN_PRI(mhi.mhi_tci); - if (mhi.mhi_istagged && (pri == 0) && (vid == VLAN_ID_NONE)) + if (mhi.mhi_istagged && !mhi.mhi_ispvid && pri == 0 && + vid == VLAN_ID_NONE) goto discard; /* @@ -1708,12 +1710,38 @@ str_notify(void *arg, mac_notify_type_t type) str_notify_phys_addr(dsp, addr); break; + case MAC_NOTE_LOWLINK: case MAC_NOTE_LINK: /* + * LOWLINK refers to the actual link status. For links that + * are not part of a bridge instance LOWLINK and LINK state + * are the same. But for a link part of a bridge instance + * LINK state refers to the aggregate link status: "up" when + * at least one link part of the bridge is up and is "down" + * when all links part of the bridge are down. + * + * Clients can request to be notified of the LOWLINK state + * using the DLIOCLOWLINK ioctl. Clients such as the bridge + * daemon request lowlink state changes and upper layer clients + * receive notifications of the aggregate link state changes + * which is the default when requesting LINK UP/DOWN state + * notifications. + */ + + /* + * Check that the notification type matches the one that we + * want. If we want lower-level link notifications, and this + * is upper, or if we want upper and this is lower, then + * ignore. + */ + if ((type == MAC_NOTE_LOWLINK) != dsp->ds_lowlink) + break; + /* * This notification is sent every time the MAC driver * updates the link state. */ - switch (mac_client_stat_get(mch, MAC_STAT_LINK_STATE)) { + switch (mac_client_stat_get(mch, dsp->ds_lowlink ? + MAC_STAT_LOWLINK_STATE : MAC_STAT_LINK_STATE)) { case LINK_STATE_UP: { uint64_t speed; /* @@ -1759,6 +1787,7 @@ str_notify(void *arg, mac_notify_type_t type) str_notify_fastpath_flush(dsp); break; + /* Unused notifications */ case MAC_NOTE_MARGIN: break; @@ -1927,6 +1956,9 @@ dld_ioc(dld_str_t *dsp, mblk_t *mp) case DLIOCHDRINFO: ioc_fast(dsp, mp); break; + case DLIOCLOWLINK: + ioc_lowlink(dsp, mp); + break; default: ioc(dsp, mp); } @@ -2116,6 +2148,27 @@ failed: } /* + * DLIOCLOWLINK: request actual link state changes. When the + * link is part of a bridge instance the client receives actual + * link state changes and not the aggregate link status. Used by + * the bridging daemon (bridged) for proper RSTP operation. + */ +static void +ioc_lowlink(dld_str_t *dsp, mblk_t *mp) +{ + queue_t *q = dsp->ds_wq; + int err; + + if ((err = miocpullup(mp, sizeof (int))) != 0) { + miocnak(q, mp, 0, err); + } else { + /* LINTED: alignment */ + dsp->ds_lowlink = *(boolean_t *)mp->b_cont->b_rptr; + miocack(q, mp, 0, 0); + } +} + +/* * Catch-all handler. */ static void diff --git a/usr/src/uts/common/io/dls/dls_link.c b/usr/src/uts/common/io/dls/dls_link.c index 59f6ad611f..3d0359b9d6 100644 --- a/usr/src/uts/common/io/dls/dls_link.c +++ b/usr/src/uts/common/io/dls/dls_link.c @@ -390,12 +390,13 @@ i_dls_link_rx(void *arg, mac_resource_handle_t mrh, mblk_t *mp, /* * Don't pass the packets up if they are tagged * packets and: - * - their VID and priority are both zero (invalid + * - their VID and priority are both zero and the + * original packet isn't using the PVID (invalid * packets). * - their sap is ETHERTYPE_VLAN and their VID is * zero as they have already been sent upstreams. */ - if ((vid == VLAN_ID_NONE && + if ((vid == VLAN_ID_NONE && !mhi.mhi_ispvid && VLAN_PRI(mhi.mhi_tci) == 0) || (mhi.mhi_bindsap == ETHERTYPE_VLAN && vid == VLAN_ID_NONE)) { @@ -1016,6 +1017,7 @@ int dls_link_header_info(dls_link_t *dlp, mblk_t *mp, mac_header_info_t *mhip) { boolean_t is_ethernet = (dlp->dl_mip->mi_media == DL_ETHER); + uint16_t pvid = mac_get_pvid(dlp->dl_mh); int err = 0; /* @@ -1031,6 +1033,7 @@ dls_link_header_info(dls_link_t *dlp, mblk_t *mp, mac_header_info_t *mhip) * mac_header_info_t as returned by mac_header_info() is * ETHERTYPE_VLAN. We need to grab the ethertype from the VLAN header. */ + mhip->mhi_ispvid = B_FALSE; if (is_ethernet && (mhip->mhi_bindsap == ETHERTYPE_VLAN)) { struct ether_vlan_header *evhp; uint16_t sap; @@ -1057,6 +1060,16 @@ dls_link_header_info(dls_link_t *dlp, mblk_t *mp, mac_header_info_t *mhip) mhip->mhi_istagged = B_TRUE; freemsg(tmp); + /* + * If this port has a non-zero PVID, then we have to lie to the + * caller about the VLAN ID. It's always zero on receive for + * that VLAN. + */ + if (pvid != VLAN_ID_NONE && VLAN_ID(mhip->mhi_tci) == pvid) { + mhip->mhi_tci &= ~(VLAN_ID_MASK << VLAN_ID_SHIFT); + mhip->mhi_ispvid = B_TRUE; + } + if (VLAN_CFI(mhip->mhi_tci) != ETHER_CFI) return (EINVAL); } else { diff --git a/usr/src/uts/common/io/mac/mac.c b/usr/src/uts/common/io/mac/mac.c index 21982219b9..151fb6c765 100644 --- a/usr/src/uts/common/io/mac/mac.c +++ b/usr/src/uts/common/io/mac/mac.c @@ -350,6 +350,16 @@ static kmutex_t i_mactype_lock; int mac_tx_percpu_cnt; int mac_tx_percpu_cnt_max = 128; +/* + * Call back functions for the bridge module. These are guaranteed to be valid + * when holding a reference on a link or when holding mip->mi_bridge_lock and + * mi_bridge_link is non-NULL. + */ +mac_bridge_tx_t mac_bridge_tx_cb; +mac_bridge_rx_t mac_bridge_rx_cb; +mac_bridge_ref_t mac_bridge_ref_cb; +mac_bridge_ls_t mac_bridge_ls_cb; + static int i_mac_constructor(void *, void *, int); static void i_mac_destructor(void *, void *); static int i_mac_ring_ctor(void *, void *, int); @@ -473,7 +483,6 @@ i_mac_constructor(void *buf, void *arg, int kmflag) bzero(buf, sizeof (mac_impl_t)); mip->mi_linkstate = LINK_STATE_UNKNOWN; - mip->mi_nclients = 0; mutex_init(&mip->mi_lock, NULL, MUTEX_DRIVER, NULL); rw_init(&mip->mi_rw_lock, NULL, RW_DRIVER, NULL); @@ -485,6 +494,9 @@ i_mac_constructor(void *buf, void *arg, int kmflag) cv_init(&mip->mi_notify_cb_info.mcbi_cv, NULL, CV_DRIVER, NULL); mip->mi_promisc_cb_info.mcbi_lockp = &mip->mi_promisc_lock; cv_init(&mip->mi_promisc_cb_info.mcbi_cv, NULL, CV_DRIVER, NULL); + + mutex_init(&mip->mi_bridge_lock, NULL, MUTEX_DEFAULT, NULL); + return (0); } @@ -533,6 +545,8 @@ i_mac_destructor(void *buf, void *arg) mutex_destroy(&mip->mi_notify_lock); cv_destroy(&mip->mi_notify_cb_info.mcbi_cv); mutex_destroy(&mip->mi_ring_lock); + + ASSERT(mip->mi_bridge_link == NULL); } /* ARGSUSED */ @@ -1552,10 +1566,8 @@ mac_hwring_tx(mac_ring_handle_t rh, mblk_t *mp) mac_ring_t *ring = (mac_ring_t *)rh; mac_ring_info_t *info = &ring->mr_info; - ASSERT(ring->mr_type == MAC_RING_TYPE_TX); - ASSERT(ring->mr_state >= MR_INUSE); - ASSERT(info->mri_tx != NULL); - + ASSERT(ring->mr_type == MAC_RING_TYPE_TX && + ring->mr_state >= MR_INUSE); return (info->mri_tx(info->mri_driver, mp)); } @@ -2694,33 +2706,16 @@ done: } /* - * Returns TRUE when the specified property is intended for the MAC framework, - * as opposed to driver defined properties. - */ -static boolean_t -mac_is_macprop(mac_prop_t *macprop) -{ - switch (macprop->mp_id) { - case MAC_PROP_MAXBW: - case MAC_PROP_PRIO: - case MAC_PROP_BIND_CPU: - return (B_TRUE); - default: - return (B_FALSE); - } -} - -/* * mac_set_prop() sets mac or hardware driver properties: - * mac properties include maxbw, priority, and cpu binding list. Driver - * properties are private properties to the hardware, such as mtu, speed - * etc. + * MAC resource properties include maxbw, priority, and cpu binding list. + * Driver properties are private properties to the hardware, such as mtu + * and speed. There's one other MAC property -- the PVID. * If the property is a driver property, mac_set_prop() calls driver's callback * function to set it. - * If the property is a mac property, mac_set_prop() invokes mac_set_resources() - * which will cache the property value in mac_impl_t and may call - * mac_client_set_resource() to update property value of the primary mac client, - * if it exists. + * If the property is a mac resource property, mac_set_prop() invokes + * mac_set_resources() which will cache the property value in mac_impl_t and + * may call mac_client_set_resource() to update property value of the primary + * mac client, if it exists. */ int mac_set_prop(mac_handle_t mh, mac_prop_t *macprop, void *val, uint_t valsize) @@ -2730,17 +2725,27 @@ mac_set_prop(mac_handle_t mh, mac_prop_t *macprop, void *val, uint_t valsize) ASSERT(MAC_PERIM_HELD(mh)); - /* If it is mac property, call mac_set_resources() */ - if (mac_is_macprop(macprop)) { + switch (macprop->mp_id) { + case MAC_PROP_MAXBW: + case MAC_PROP_PRIO: + case MAC_PROP_BIND_CPU: { mac_resource_props_t mrp; + /* If it is mac property, call mac_set_resources() */ if (valsize < sizeof (mac_resource_props_t)) return (EINVAL); - bzero(&mrp, sizeof (mac_resource_props_t)); bcopy(val, &mrp, sizeof (mrp)); - return (mac_set_resources(mh, &mrp)); + err = mac_set_resources(mh, &mrp); + break; } - switch (macprop->mp_id) { + + case MAC_PROP_PVID: + if (valsize < sizeof (uint16_t) || + (mip->mi_state_flags & MIS_IS_VNIC)) + return (EINVAL); + err = mac_set_pvid(mh, *(uint16_t *)val); + break; + case MAC_PROP_MTU: { uint32_t mtu; @@ -2750,6 +2755,25 @@ mac_set_prop(mac_handle_t mh, mac_prop_t *macprop, void *val, uint_t valsize) err = mac_set_mtu(mh, mtu, NULL); break; } + + case MAC_PROP_LLIMIT: + case MAC_PROP_LDECAY: { + uint32_t learnval; + + if (valsize < sizeof (learnval) || + (mip->mi_state_flags & MIS_IS_VNIC)) + return (EINVAL); + bcopy(val, &learnval, sizeof (learnval)); + if (learnval == 0 && macprop->mp_id == MAC_PROP_LDECAY) + return (EINVAL); + if (macprop->mp_id == MAC_PROP_LLIMIT) + mip->mi_llimit = learnval; + else + mip->mi_ldecay = learnval; + err = 0; + break; + } + default: /* For other driver properties, call driver's callback */ if (mip->mi_callbacks->mc_callbacks & MC_SETPROP) { @@ -2780,19 +2804,38 @@ mac_get_prop(mac_handle_t mh, mac_prop_t *macprop, void *val, uint_t valsize, is_getprop = (mip->mi_callbacks->mc_callbacks & MC_GETPROP); is_setprop = (mip->mi_callbacks->mc_callbacks & MC_SETPROP); - /* If mac property, read from cache */ - if (mac_is_macprop(macprop)) { + switch (macprop->mp_id) { + case MAC_PROP_MAXBW: + case MAC_PROP_PRIO: + case MAC_PROP_BIND_CPU: { mac_resource_props_t mrp; + /* If mac property, read from cache */ if (valsize < sizeof (mac_resource_props_t)) return (EINVAL); - bzero(&mrp, sizeof (mac_resource_props_t)); mac_get_resources(mh, &mrp); bcopy(&mrp, val, sizeof (mac_resource_props_t)); return (0); } - switch (macprop->mp_id) { + case MAC_PROP_PVID: + if (valsize < sizeof (uint16_t) || + (mip->mi_state_flags & MIS_IS_VNIC)) + return (EINVAL); + *(uint16_t *)val = mac_get_pvid(mh); + return (0); + + case MAC_PROP_LLIMIT: + case MAC_PROP_LDECAY: + if (valsize < sizeof (uint32_t) || + (mip->mi_state_flags & MIS_IS_VNIC)) + return (EINVAL); + if (macprop->mp_id == MAC_PROP_LLIMIT) + bcopy(&mip->mi_llimit, val, sizeof (mip->mi_llimit)); + else + bcopy(&mip->mi_ldecay, val, sizeof (mip->mi_ldecay)); + return (0); + case MAC_PROP_MTU: { uint32_t sdu; mac_propval_range_t range; @@ -3464,6 +3507,35 @@ mac_release_tx_ring(mac_ring_handle_t rh) } /* + * This is the entry point for packets transmitted through the bridging code. + * If no bridge is in place, MAC_RING_TX transmits using tx ring. The 'rh' + * pointer may be NULL to select the default ring. + */ +mblk_t * +mac_bridge_tx(mac_impl_t *mip, mac_ring_handle_t rh, mblk_t *mp) +{ + mac_handle_t mh; + + /* + * Once we take a reference on the bridge link, the bridge + * module itself can't unload, so the callback pointers are + * stable. + */ + mutex_enter(&mip->mi_bridge_lock); + if ((mh = mip->mi_bridge_link) != NULL) + mac_bridge_ref_cb(mh, B_TRUE); + mutex_exit(&mip->mi_bridge_lock); + if (mh == NULL) { + MAC_RING_TX(mip, rh, mp, mp); + } else { + mp = mac_bridge_tx_cb(mh, rh, mp); + mac_bridge_ref_cb(mh, B_FALSE); + } + + return (mp); +} + +/* * Find a ring from its index. */ mac_ring_t * @@ -5267,3 +5339,60 @@ mac_client_tx_notify(mac_client_handle_t mch, mac_tx_notify_t callb_func, return ((mac_tx_notify_handle_t)mtnfp); } + +void +mac_bridge_vectors(mac_bridge_tx_t txf, mac_bridge_rx_t rxf, + mac_bridge_ref_t reff, mac_bridge_ls_t lsf) +{ + mac_bridge_tx_cb = txf; + mac_bridge_rx_cb = rxf; + mac_bridge_ref_cb = reff; + mac_bridge_ls_cb = lsf; +} + +int +mac_bridge_set(mac_handle_t mh, mac_handle_t link) +{ + mac_impl_t *mip = (mac_impl_t *)mh; + int retv; + + mutex_enter(&mip->mi_bridge_lock); + if (mip->mi_bridge_link == NULL) { + mip->mi_bridge_link = link; + retv = 0; + } else { + retv = EBUSY; + } + mutex_exit(&mip->mi_bridge_lock); + if (retv == 0) { + mac_poll_state_change(mh, B_FALSE); + mac_capab_update(mh); + } + return (retv); +} + +/* + * Disable bridging on the indicated link. + */ +void +mac_bridge_clear(mac_handle_t mh, mac_handle_t link) +{ + mac_impl_t *mip = (mac_impl_t *)mh; + + mutex_enter(&mip->mi_bridge_lock); + ASSERT(mip->mi_bridge_link == link); + mip->mi_bridge_link = NULL; + mutex_exit(&mip->mi_bridge_lock); + mac_poll_state_change(mh, B_TRUE); + mac_capab_update(mh); +} + +void +mac_no_active(mac_handle_t mh) +{ + mac_impl_t *mip = (mac_impl_t *)mh; + + i_mac_perim_enter(mip); + mip->mi_state_flags |= MIS_NO_ACTIVE; + i_mac_perim_exit(mip); +} diff --git a/usr/src/uts/common/io/mac/mac_bcast.c b/usr/src/uts/common/io/mac/mac_bcast.c index eaac168aaf..2f17228e06 100644 --- a/usr/src/uts/common/io/mac/mac_bcast.c +++ b/usr/src/uts/common/io/mac/mac_bcast.c @@ -236,14 +236,12 @@ mac_bcast_send(void *arg1, void *arg2, mblk_t *mp_chain, boolean_t is_loopback) * so we need to send a copy of the packet to the * underlying NIC so that it can be sent on the wire. */ - mblk_t *rest; - src_mcip->mci_stat_multixmt++; src_mcip->mci_stat_brdcstxmt++; - rest = MAC_RING_TX_DEFAULT(mip, mp_chain); - if (rest != NULL) - freemsgchain(rest); + MAC_TX(mip, mip->mi_default_tx_ring, mp_chain, B_FALSE); + if (mp_chain != NULL) + freemsgchain(mp_chain); } else { freemsgchain(mp_chain); } diff --git a/usr/src/uts/common/io/mac/mac_client.c b/usr/src/uts/common/io/mac/mac_client.c index a6f1930f92..3ac7774895 100644 --- a/usr/src/uts/common/io/mac/mac_client.c +++ b/usr/src/uts/common/io/mac/mac_client.c @@ -569,6 +569,9 @@ mac_client_stat_get(mac_client_handle_t mch, uint_t stat) case MAC_STAT_PROMISC: val = mac_stat_get((mac_handle_t)mip, MAC_STAT_PROMISC); break; + case MAC_STAT_LOWLINK_STATE: + val = mac_stat_get((mac_handle_t)mip, MAC_STAT_LOWLINK_STATE); + break; case MAC_STAT_IFSPEED: val = mac_client_ifspeed(mcip); break; @@ -645,6 +648,8 @@ mac_stat_get(mac_handle_t mh, uint_t stat) return (mip->mi_linkstate == LINK_STATE_UP); case MAC_STAT_PROMISC: return (mip->mi_devpromisc != 0); + case MAC_STAT_LOWLINK_STATE: + return (mip->mi_lowlinkstate); default: ASSERT(B_FALSE); } @@ -1828,6 +1833,8 @@ mac_get_passive_primary_client(mac_impl_t *mip) * Note also the tuple (MAC address, VID) must be unique * for the MAC clients defined on top of the same underlying MAC * instance, unless the MAC_UNICAST_NODUPCHECK is specified. + * + * In no case can a client use the PVID for the MAC, if the MAC has one set. */ int i_mac_unicast_add(mac_client_handle_t mch, uint8_t *mac_addr, uint16_t flags, @@ -1851,6 +1858,13 @@ i_mac_unicast_add(mac_client_handle_t mch, uint8_t *mac_addr, uint16_t flags, ASSERT(!((mip->mi_state_flags & MIS_IS_VNIC) && (vid != 0))); /* + * Check for an attempted use of the current Port VLAN ID, if enabled. + * No client may use it. + */ + if (mip->mi_pvid != 0 && vid == mip->mi_pvid) + return (EBUSY); + + /* * Check whether it's the primary client and flag it. */ if (!(mcip->mci_state_flags & MCIS_IS_VNIC) && is_primary && vid == 0) @@ -1945,8 +1959,9 @@ i_mac_unicast_add(mac_client_handle_t mch, uint8_t *mac_addr, uint16_t flags, * - this is an exclusive active mac client but * a. there is already active mac clients exist, or * b. fastpath streams are already plumbed on this legacy device + * - the mac creator has disallowed active mac clients. */ - if (mip->mi_state_flags & MIS_EXCLUSIVE) { + if (mip->mi_state_flags & (MIS_EXCLUSIVE|MIS_NO_ACTIVE)) { if (fastpath_disabled) mac_fastpath_enable((mac_handle_t)mip); return (EBUSY); @@ -2832,7 +2847,8 @@ mac_tx(mac_client_handle_t mch, mblk_t *mp_chain, uintptr_t hint, obytes = (mp_chain->b_cont == NULL ? MBLKL(mp_chain) : msgdsize(mp_chain)); - MAC_TX(mip, srs_tx->st_arg2, mp_chain, mcip); + MAC_TX(mip, srs_tx->st_arg2, mp_chain, + ((mcip->mci_state_flags & MCIS_SHARE_BOUND) != 0)); if (mp_chain == NULL) { cookie = NULL; @@ -3299,6 +3315,10 @@ mac_promisc_dispatch(mac_impl_t *mip, mblk_t *mp_chain, */ continue; + /* this client doesn't need any packets (bridge) */ + if (mpip->mpi_fn == NULL) + continue; + /* * For an ethernet MAC, don't displatch a multicast * packet to a non-PROMISC_ALL callbacks unless the VID @@ -3412,14 +3432,18 @@ mac_info_get(const char *name, mac_info_t *minfop) /* * To get the capabilities that MAC layer cares about, such as rings, factory - * mac address, vnic or not, it should directly invoke this function + * mac address, vnic or not, it should directly invoke this function. If the + * link is part of a bridge, then the only "capability" it has is the inability + * to do zero copy. */ boolean_t i_mac_capab_get(mac_handle_t mh, mac_capab_t cap, void *cap_data) { mac_impl_t *mip = (mac_impl_t *)mh; - if (mip->mi_callbacks->mc_callbacks & MC_GETCAPAB) + if (mip->mi_bridge_link != NULL) + return (cap == MAC_CAPAB_NO_ZCOPY); + else if (mip->mi_callbacks->mc_callbacks & MC_GETCAPAB) return (mip->mi_getcapab(mip->mi_driver, cap, cap_data)); else return (B_FALSE); @@ -3674,6 +3698,55 @@ mac_get_resources(mac_handle_t mh, mac_resource_props_t *mrp) bcopy(&mip->mi_resource_props, mrp, sizeof (mac_resource_props_t)); } +int +mac_set_pvid(mac_handle_t mh, uint16_t pvid) +{ + mac_impl_t *mip = (mac_impl_t *)mh; + mac_client_impl_t *mcip; + mac_unicast_impl_t *muip; + + i_mac_perim_enter(mip); + if (pvid != 0) { + for (mcip = mip->mi_clients_list; mcip != NULL; + mcip = mcip->mci_client_next) { + for (muip = mcip->mci_unicast_list; muip != NULL; + muip = muip->mui_next) { + if (muip->mui_vid == pvid) { + i_mac_perim_exit(mip); + return (EBUSY); + } + } + } + } + mip->mi_pvid = pvid; + i_mac_perim_exit(mip); + return (0); +} + +uint16_t +mac_get_pvid(mac_handle_t mh) +{ + mac_impl_t *mip = (mac_impl_t *)mh; + + return (mip->mi_pvid); +} + +uint32_t +mac_get_llimit(mac_handle_t mh) +{ + mac_impl_t *mip = (mac_impl_t *)mh; + + return (mip->mi_llimit); +} + +uint32_t +mac_get_ldecay(mac_handle_t mh) +{ + mac_impl_t *mip = (mac_impl_t *)mh; + + return (mip->mi_ldecay); +} + /* * Rename a mac client, its flow, and the kstat. */ diff --git a/usr/src/uts/common/io/mac/mac_datapath_setup.c b/usr/src/uts/common/io/mac/mac_datapath_setup.c index dc5b51cb80..379e488ee2 100644 --- a/usr/src/uts/common/io/mac/mac_datapath_setup.c +++ b/usr/src/uts/common/io/mac/mac_datapath_setup.c @@ -1358,6 +1358,10 @@ mac_client_update_classifier(mac_client_impl_t *mcip, boolean_t enable) rx_func = enable_classifier ? mac_rx_srs_subflow_process : mac_rx_srs_process; + /* Tell mac_srs_poll_state_change to disable polling if necessary */ + if (mip->mi_state_flags & MIS_POLL_DISABLE) + enable_classifier = B_TRUE; + /* * If receive function has already been configured correctly for * current subflow configuration, do nothing. @@ -1979,7 +1983,8 @@ mac_srs_create(mac_client_impl_t *mcip, flow_entry_t *flent, uint32_t srs_type, ring->mr_classify_type = MAC_HW_CLASSIFIER; ring->mr_flag |= MR_INCIPIENT; - if (FLOW_TAB_EMPTY(mcip->mci_subflow_tab) && mac_poll_enable) + if (!(mcip->mci_mip->mi_state_flags & MIS_POLL_DISABLE) && + FLOW_TAB_EMPTY(mcip->mci_subflow_tab) && mac_poll_enable) mac_srs->srs_state |= SRS_POLLING_CAPAB; mac_srs->srs_poll_thr = thread_create(NULL, 0, @@ -3388,3 +3393,25 @@ mac_fanout_recompute(mac_impl_t *mip) } i_mac_perim_exit(mip); } + +/* + * Given a MAC, change the polling state for all its MAC clients. 'enable' is + * B_TRUE to enable polling or B_FALSE to disable. Polling is enabled by + * default. + */ +void +mac_poll_state_change(mac_handle_t mh, boolean_t enable) +{ + mac_impl_t *mip = (mac_impl_t *)mh; + mac_client_impl_t *mcip; + + i_mac_perim_enter(mip); + if (enable) + mip->mi_state_flags &= ~MIS_POLL_DISABLE; + else + mip->mi_state_flags |= MIS_POLL_DISABLE; + for (mcip = mip->mi_clients_list; mcip != NULL; + mcip = mcip->mci_client_next) + mac_client_update_classifier(mcip, B_TRUE); + i_mac_perim_exit(mip); +} diff --git a/usr/src/uts/common/io/mac/mac_provider.c b/usr/src/uts/common/io/mac/mac_provider.c index 0db8e9e97c..408482ce45 100644 --- a/usr/src/uts/common/io/mac/mac_provider.c +++ b/usr/src/uts/common/io/mac/mac_provider.c @@ -63,21 +63,16 @@ static void i_mac_notify_thread(void *); typedef void (*mac_notify_default_cb_fn_t)(mac_impl_t *); -typedef struct mac_notify_default_cb_s { - mac_notify_type_t mac_notify_type; - mac_notify_default_cb_fn_t mac_notify_cb_fn; -}mac_notify_default_cb_t; - -mac_notify_default_cb_t mac_notify_cb_list[] = { - { MAC_NOTE_LINK, mac_fanout_recompute}, - { MAC_NOTE_UNICST, NULL}, - { MAC_NOTE_TX, NULL}, - { MAC_NOTE_DEVPROMISC, NULL}, - { MAC_NOTE_FASTPATH_FLUSH, NULL}, - { MAC_NOTE_SDU_SIZE, NULL}, - { MAC_NOTE_MARGIN, NULL}, - { MAC_NOTE_CAPAB_CHG, NULL}, - { MAC_NNOTE, NULL}, +static const mac_notify_default_cb_fn_t mac_notify_cb_list[MAC_NNOTE] = { + mac_fanout_recompute, /* MAC_NOTE_LINK */ + NULL, /* MAC_NOTE_UNICST */ + NULL, /* MAC_NOTE_TX */ + NULL, /* MAC_NOTE_DEVPROMISC */ + NULL, /* MAC_NOTE_FASTPATH_FLUSH */ + NULL, /* MAC_NOTE_SDU_SIZE */ + NULL, /* MAC_NOTE_MARGIN */ + NULL, /* MAC_NOTE_CAPAB_CHG */ + NULL /* MAC_NOTE_LOWLINK */ }; /* @@ -189,6 +184,13 @@ mac_register(mac_register_t *mregp, mac_handle_t *mhp) mip->mi_clients_list = NULL; mip->mi_nclients = 0; + /* Set the default IEEE Port VLAN Identifier */ + mip->mi_pvid = 1; + + /* Default bridge link learning protection values */ + mip->mi_llimit = 1000; + mip->mi_ldecay = 200; + driver = (char *)ddi_driver_name(mip->mi_dip); /* Construct the MAC name as <drvname><instance> */ @@ -537,7 +539,7 @@ mac_unregister(mac_handle_t mh) } mip->mi_mmrp = NULL; - mip->mi_linkstate = LINK_STATE_UNKNOWN; + mip->mi_linkstate = mip->mi_lowlinkstate = LINK_STATE_UNKNOWN; kmem_free(mip->mi_info.mi_unicst_addr, mip->mi_type->mt_addr_length); mip->mi_info.mi_unicst_addr = NULL; @@ -577,6 +579,8 @@ mac_unregister(mac_handle_t mh) mip->mi_state_flags = 0; mac_unregister_priv_prop(mip); + + ASSERT(mip->mi_bridge_link == NULL); kmem_cache_free(i_mac_impl_cachep, mip); return (0); @@ -607,12 +611,59 @@ mac_rx_ring(mac_handle_t mh, mac_ring_handle_t mrh, mblk_t *mp_chain, } /* - * This function is invoked for each packet received by the underlying - * driver. + * This function is invoked for each packet received by the underlying driver. */ void mac_rx(mac_handle_t mh, mac_resource_handle_t mrh, mblk_t *mp_chain) { + mac_impl_t *mip = (mac_impl_t *)mh; + + /* + * Check if the link is part of a bridge. If not, then we don't need + * to take the lock to remain consistent. Make this common case + * lock-free and tail-call optimized. + */ + if (mip->mi_bridge_link == NULL) { + mac_rx_common(mh, mrh, mp_chain); + } else { + /* + * Once we take a reference on the bridge link, the bridge + * module itself can't unload, so the callback pointers are + * stable. + */ + mutex_enter(&mip->mi_bridge_lock); + if ((mh = mip->mi_bridge_link) != NULL) + mac_bridge_ref_cb(mh, B_TRUE); + mutex_exit(&mip->mi_bridge_lock); + if (mh == NULL) { + mac_rx_common((mac_handle_t)mip, mrh, mp_chain); + } else { + mac_bridge_rx_cb(mh, mrh, mp_chain); + mac_bridge_ref_cb(mh, B_FALSE); + } + } +} + +/* + * Special case function: this allows snooping of packets transmitted and + * received by TRILL. By design, they go directly into the TRILL module. + */ +void +mac_trill_snoop(mac_handle_t mh, mblk_t *mp) +{ + mac_impl_t *mip = (mac_impl_t *)mh; + + if (mip->mi_promisc_list != NULL) + mac_promisc_dispatch(mip, mp, NULL); +} + +/* + * This is the upward reentry point for packets arriving from the bridging + * module and from mac_rx for links not part of a bridge. + */ +void +mac_rx_common(mac_handle_t mh, mac_resource_handle_t mrh, mblk_t *mp_chain) +{ mac_impl_t *mip = (mac_impl_t *)mh; mac_ring_t *mr = (mac_ring_t *)mrh; mac_soft_ring_set_t *mac_srs; @@ -733,10 +784,31 @@ mac_link_update(mac_handle_t mh, link_state_t link) /* * Save the link state. */ + mip->mi_lowlinkstate = link; + + /* + * Send a MAC_NOTE_LOWLINK notification. This tells the notification + * thread to deliver both lower and upper notifications. + */ + i_mac_notify(mip, MAC_NOTE_LOWLINK); +} + +/* + * Notify the MAC layer about a link state change due to bridging. + */ +void +mac_link_redo(mac_handle_t mh, link_state_t link) +{ + mac_impl_t *mip = (mac_impl_t *)mh; + + /* + * Save the link state. + */ mip->mi_linkstate = link; /* - * Send a MAC_NOTE_LINK notification. + * Send a MAC_NOTE_LINK notification. Only upper notifications are + * made. */ i_mac_notify(mip, MAC_NOTE_LINK); } @@ -846,10 +918,10 @@ i_mac_log_link_state(mac_impl_t *mip) /* * If no change, then it is not interesting. */ - if (mip->mi_lastlinkstate == mip->mi_linkstate) + if (mip->mi_lastlowlinkstate == mip->mi_lowlinkstate) return; - switch (mip->mi_linkstate) { + switch (mip->mi_lowlinkstate) { case LINK_STATE_UP: if (mip->mi_type->mt_ops.mtops_ops & MTOPS_LINK_DETAILS) { char det[200]; @@ -867,7 +939,7 @@ i_mac_log_link_state(mac_impl_t *mip) /* * Only transitions from UP to DOWN are interesting */ - if (mip->mi_lastlinkstate != LINK_STATE_UNKNOWN) + if (mip->mi_lastlowlinkstate != LINK_STATE_UNKNOWN) cmn_err(CE_NOTE, "!%s link down", mip->mi_name); break; @@ -877,7 +949,7 @@ i_mac_log_link_state(mac_impl_t *mip) */ break; } - mip->mi_lastlinkstate = mip->mi_linkstate; + mip->mi_lastlowlinkstate = mip->mi_lowlinkstate; } /* @@ -919,10 +991,28 @@ i_mac_notify_thread(void *arg) mutex_exit(mcbi->mcbi_lockp); /* - * Log link changes. + * Log link changes on the actual link, but then do reports on + * synthetic state (if part of a bridge). */ - if ((bits & (1 << MAC_NOTE_LINK)) != 0) + if ((bits & (1 << MAC_NOTE_LOWLINK)) != 0) { + link_state_t newstate; + mac_handle_t mh; + i_mac_log_link_state(mip); + newstate = mip->mi_lowlinkstate; + if (mip->mi_bridge_link != NULL) { + mutex_enter(&mip->mi_bridge_lock); + if ((mh = mip->mi_bridge_link) != NULL) { + newstate = mac_bridge_ls_cb(mh, + newstate); + } + mutex_exit(&mip->mi_bridge_lock); + } + if (newstate != mip->mi_linkstate) { + mip->mi_linkstate = newstate; + bits |= 1 << MAC_NOTE_LINK; + } + } /* * Do notification callbacks for each notification type. @@ -932,8 +1022,8 @@ i_mac_notify_thread(void *arg) continue; } - if (mac_notify_cb_list[type].mac_notify_cb_fn) - mac_notify_cb_list[type].mac_notify_cb_fn(mip); + if (mac_notify_cb_list[type] != NULL) + (*mac_notify_cb_list[type])(mip); /* * Walk the list of notifications. diff --git a/usr/src/uts/common/io/mac/mac_sched.c b/usr/src/uts/common/io/mac/mac_sched.c index 1c67968c5f..74de880f3d 100644 --- a/usr/src/uts/common/io/mac/mac_sched.c +++ b/usr/src/uts/common/io/mac/mac_sched.c @@ -3279,7 +3279,9 @@ mac_tx_send(mac_client_handle_t mch, mac_ring_handle_t ring, mblk_t *mp_chain, msgdsize(mp)); CHECK_VID_AND_ADD_TAG(mp); - MAC_TX(mip, ring, mp, src_mcip); + MAC_TX(mip, ring, mp, + ((src_mcip->mci_state_flags & MCIS_SHARE_BOUND) != + 0)); /* * If the driver is out of descriptors and does a @@ -3406,7 +3408,9 @@ mac_tx_send(mac_client_handle_t mch, mac_ring_handle_t ring, mblk_t *mp_chain, * Unknown destination, send via the underlying * NIC. */ - MAC_TX(mip, ring, mp, src_mcip); + MAC_TX(mip, ring, mp, + ((src_mcip->mci_state_flags & MCIS_SHARE_BOUND) != + 0)); if (mp != NULL) { /* * Adjust for the last packet that diff --git a/usr/src/uts/common/io/trill.c b/usr/src/uts/common/io/trill.c new file mode 100644 index 0000000000..9f00a62fba --- /dev/null +++ b/usr/src/uts/common/io/trill.c @@ -0,0 +1,1746 @@ +/* + * 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. + */ + +/* + * This module supports AF_TRILL sockets and TRILL layer-2 forwarding. + */ + +#include <sys/strsubr.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/modctl.h> +#include <sys/cmn_err.h> +#include <sys/tihdr.h> +#include <sys/strsun.h> +#include <sys/policy.h> +#include <sys/ethernet.h> +#include <sys/vlan.h> +#include <net/trill.h> +#include <net/if_dl.h> +#include <sys/mac.h> +#include <sys/mac_client.h> +#include <sys/mac_provider.h> +#include <sys/mac_client_priv.h> +#include <sys/sdt.h> +#include <sys/dls.h> +#include <sys/sunddi.h> + +#include "trill_impl.h" + +static void trill_del_all(trill_inst_t *, boolean_t); +static int trill_del_nick(trill_inst_t *, uint16_t, boolean_t); +static void trill_stop_recv(trill_sock_t *); +static void trill_ctrl_input(trill_sock_t *, mblk_t *, const uint8_t *, + uint16_t); +static trill_node_t *trill_node_lookup(trill_inst_t *, uint16_t); +static void trill_node_unref(trill_inst_t *, trill_node_t *); +static void trill_sock_unref(trill_sock_t *); +static void trill_kstats_init(trill_sock_t *, const char *); + +static list_t trill_inst_list; +static krwlock_t trill_inst_rwlock; + +static sock_lower_handle_t trill_create(int, int, int, sock_downcalls_t **, + uint_t *, int *, int, cred_t *); + +static smod_reg_t sinfo = { + SOCKMOD_VERSION, + "trill", + SOCK_UC_VERSION, + SOCK_DC_VERSION, + trill_create, + NULL, +}; + +/* modldrv structure */ +static struct modlsockmod sockmod = { + &mod_sockmodops, "AF_TRILL socket module", &sinfo +}; + +/* modlinkage structure */ +static struct modlinkage ml = { + MODREV_1, + &sockmod, + NULL +}; + +#define VALID_NICK(n) ((n) != RBRIDGE_NICKNAME_NONE && \ + (n) != RBRIDGE_NICKNAME_UNUSED) + +static mblk_t * +create_trill_header(trill_sock_t *tsock, mblk_t *mp, const uint8_t *daddr, + boolean_t trill_hdr_ok, boolean_t multidest, uint16_t tci, + size_t msglen) +{ + int extra_hdr_len; + struct ether_vlan_header *ethvlanhdr; + mblk_t *hdr_mp; + uint16_t etype; + + etype = msglen > 0 ? (uint16_t)msglen : ETHERTYPE_TRILL; + + /* When sending on the PVID, we must not give a VLAN ID */ + if (tci == tsock->ts_link->bl_pvid) + tci = TRILL_NO_TCI; + + /* + * Create new Ethernet header and include additional space + * for writing TRILL header and/or VLAN tag. + */ + extra_hdr_len = (trill_hdr_ok ? 0 : sizeof (trill_header_t)) + + (tci != TRILL_NO_TCI ? sizeof (struct ether_vlan_extinfo) : 0); + hdr_mp = mac_header(tsock->ts_link->bl_mh, daddr, + tci != TRILL_NO_TCI ? ETHERTYPE_VLAN : etype, mp, extra_hdr_len); + if (hdr_mp == NULL) { + freemsg(mp); + return (NULL); + } + + if (tci != TRILL_NO_TCI) { + /* LINTED: alignment */ + ethvlanhdr = (struct ether_vlan_header *)hdr_mp->b_rptr; + ethvlanhdr->ether_tci = htons(tci); + ethvlanhdr->ether_type = htons(etype); + hdr_mp->b_wptr += sizeof (struct ether_vlan_extinfo); + } + + if (!trill_hdr_ok) { + trill_header_t *thp; + /* LINTED: alignment */ + thp = (trill_header_t *)hdr_mp->b_wptr; + (void) memset(thp, 0, sizeof (trill_header_t)); + thp->th_hopcount = TRILL_DEFAULT_HOPS; + thp->th_multidest = (multidest ? 1:0); + hdr_mp->b_wptr += sizeof (trill_header_t); + } + + hdr_mp->b_cont = mp; + return (hdr_mp); +} + +/* + * TRILL local recv function. TRILL data frames that should be received + * by the local system are decapsulated here and passed to bridging for + * learning and local system receive. Only called when we are the forwarder + * on the link (multi-dest frames) or the frame was destined for us. + */ +static void +trill_recv_local(trill_sock_t *tsock, mblk_t *mp, uint16_t ingressnick) +{ + struct ether_header *inner_ethhdr; + + /* LINTED: alignment */ + inner_ethhdr = (struct ether_header *)mp->b_rptr; + DTRACE_PROBE1(trill__recv__local, struct ether_header *, inner_ethhdr); + + DB_CKSUMFLAGS(mp) = 0; + /* + * Transmit the decapsulated frame on the link via Bridging. + * Bridging does source address learning and appropriate forwarding. + */ + bridge_trill_decaps(tsock->ts_link, mp, ingressnick); + KSPINCR(tks_decap); +} + +/* + * Determines the outgoing link to reach a RBridge having the given nick + * Assumes caller has acquired the trill instance rwlock. + */ +static trill_sock_t * +find_trill_link(trill_inst_t *tip, datalink_id_t linkid) +{ + trill_sock_t *tsp = NULL; + + ASSERT(RW_LOCK_HELD(&tip->ti_rwlock)); + for (tsp = list_head(&tip->ti_socklist); tsp != NULL; + tsp = list_next(&tip->ti_socklist, tsp)) { + if (tsp->ts_link != NULL && tsp->ts_link->bl_linkid == linkid) { + ASSERT(tsp->ts_link->bl_mh != NULL); + ASSERT(!(tsp->ts_flags & TSF_SHUTDOWN)); + atomic_inc_uint(&tsp->ts_refs); + break; + } + } + return (tsp); +} + +/* + * TRILL destination forwarding function. Transmits the TRILL data packet + * to the next-hop, adjacent RBridge. Consumes passed mblk_t. + */ +static void +trill_dest_fwd(trill_inst_t *tip, mblk_t *fwd_mp, uint16_t adj_nick, + boolean_t has_trill_hdr, boolean_t multidest, uint16_t dtnick) +{ + trill_node_t *adj; + trill_sock_t *tsock = NULL; + trill_header_t *trillhdr; + struct ether_header *ethhdr; + int ethtype; + int ethhdrlen; + + adj = trill_node_lookup(tip, adj_nick); + if (adj == NULL || ((tsock = adj->tn_tsp) == NULL)) + goto dest_fwd_fail; + + ASSERT(tsock->ts_link != NULL); + ASSERT(!(tsock->ts_flags & TSF_SHUTDOWN)); + ASSERT(adj->tn_ni != NULL); + + DTRACE_PROBE3(trill__dest__fwd, uint16_t, adj_nick, trill_node_t, + adj, trill_sock_t, tsock); + + /* + * For broadcast links by using the dest address of + * the RBridge to forward the frame should result in + * savings. When the link is a bridged LAN or there are + * many end stations the frame will not always be flooded. + */ + fwd_mp = create_trill_header(tsock, fwd_mp, adj->tn_ni->tni_adjsnpa, + has_trill_hdr, multidest, tsock->ts_desigvlan, 0); + if (fwd_mp == NULL) + goto dest_fwd_fail; + + /* LINTED: alignment */ + ethhdr = (struct ether_header *)fwd_mp->b_rptr; + ethtype = ntohs(ethhdr->ether_type); + ASSERT(ethtype == ETHERTYPE_VLAN || ethtype == ETHERTYPE_TRILL); + + /* Pullup Ethernet and TRILL header (w/o TRILL options) */ + ethhdrlen = sizeof (struct ether_header) + + (ethtype == ETHERTYPE_VLAN ? sizeof (struct ether_vlan_extinfo):0); + if (!pullupmsg(fwd_mp, ethhdrlen + sizeof (trill_header_t))) + goto dest_fwd_fail; + /* LINTED: alignment */ + trillhdr = (struct trill_header *)(fwd_mp->b_rptr + ethhdrlen); + + /* Update TRILL header with ingress and egress nicks for new frames */ + if (!has_trill_hdr) { + /* We are creating a new TRILL frame */ + trillhdr->th_egressnick = (multidest ? dtnick:adj_nick); + rw_enter(&tip->ti_rwlock, RW_READER); + trillhdr->th_ingressnick = tip->ti_nick; + rw_exit(&tip->ti_rwlock); + if (!VALID_NICK(trillhdr->th_ingressnick)) + goto dest_fwd_fail; + } + + /* Set hop count and update header in packet */ + ASSERT(trillhdr->th_hopcount != 0); + trillhdr->th_hopcount--; + + /* Clear checksum flag and transmit frame on the link */ + DB_CKSUMFLAGS(fwd_mp) = 0; + DTRACE_PROBE1(trill__dest__fwd__tx, trill_header_t *, &trillhdr); + fwd_mp = bridge_trill_output(tsock->ts_link, fwd_mp); + if (fwd_mp == NULL) { + KSPINCR(tks_sent); + KSPINCR(tks_forward); + } else { + freemsg(fwd_mp); + KSPINCR(tks_drops); + } + trill_node_unref(tip, adj); + return; + +dest_fwd_fail: + if (adj != NULL) + trill_node_unref(tip, adj); + if (tsock != NULL) + KSPINCR(tks_drops); + freemsg(fwd_mp); +} + +/* + * TRILL multi-destination forwarding. Transmits the packet to the adjacencies + * on the distribution tree determined by the egress nick. Source addr (saddr) + * is NULL for new TRILL packets originating from us. + */ +static void +trill_multidest_fwd(trill_inst_t *tip, mblk_t *mp, uint16_t egressnick, + uint16_t ingressnick, boolean_t is_trill_pkt, const uint8_t *saddr, + int inner_vlan, boolean_t free_mblk) +{ + int idx; + uint16_t adjnick; + trill_node_t *dest; + trill_node_t *adj; + mblk_t *fwd_mp; + boolean_t nicksaved = B_FALSE; + uint16_t adjnicksaved; + + /* Lookup the egress nick info, this is the DT root */ + if ((dest = trill_node_lookup(tip, egressnick)) == NULL) + goto fail_multidest_fwd; + + /* Send a copy to all our adjacencies on the DT root */ + ASSERT(dest->tn_ni); + for (idx = 0; idx < dest->tn_ni->tni_adjcount; idx++) { + + /* Check for a valid adjacency node */ + adjnick = TNI_ADJNICK(dest->tn_ni, idx); + if (!VALID_NICK(adjnick) || ingressnick == adjnick || + ((adj = trill_node_lookup(tip, adjnick)) == NULL)) + continue; + + /* Do not forward back to adjacency that sent the pkt to us */ + ASSERT(adj->tn_ni != NULL); + if ((saddr != NULL) && + (memcmp(adj->tn_ni->tni_adjsnpa, saddr, + ETHERADDRL) == 0)) { + trill_node_unref(tip, adj); + continue; + } + + /* Check if adj is marked as reaching inner VLAN downstream */ + if ((inner_vlan != VLAN_ID_NONE) && + !TRILL_VLANISSET(TNI_VLANFILTERMAP(dest->tn_ni, idx), + inner_vlan)) { + trill_node_unref(tip, adj); + DTRACE_PROBE4(trill__multi__dest__fwd__vlanfiltered, + uint16_t, adjnick, uint16_t, ingressnick, + uint16_t, egressnick, int, inner_vlan); + continue; + } + + trill_node_unref(tip, adj); + + /* + * Save the nick and look ahead to see if we should forward the + * frame to more adjacencies. We avoid doing a copy for this + * nick and use the passed mblk when we can consume the passed + * mblk. + */ + if (free_mblk && !nicksaved) { + adjnicksaved = adjnick; + nicksaved = B_TRUE; + continue; + } + + fwd_mp = copymsg(mp); + if (fwd_mp == NULL) + break; + DTRACE_PROBE2(trill__multi__dest__fwd, uint16_t, + adjnick, uint16_t, ingressnick); + trill_dest_fwd(tip, fwd_mp, adjnick, is_trill_pkt, + B_TRUE, egressnick); + } + trill_node_unref(tip, dest); + + if (nicksaved) { + ASSERT(free_mblk); + DTRACE_PROBE2(trill__multi__dest__fwd, uint16_t, + adjnicksaved, uint16_t, ingressnick); + trill_dest_fwd(tip, mp, adjnicksaved, is_trill_pkt, + B_TRUE, egressnick); + return; + } + +fail_multidest_fwd: + DTRACE_PROBE2(trill__multi__dest__fwd__fail, uint16_t, + egressnick, uint16_t, ingressnick); + if (free_mblk) { + freemsg(mp); + } +} + +/* + * TRILL data receive function. Forwards the received frame if necessary + * and also determines if the received frame should be consumed locally. + * Consumes passed mblk. + */ +static void +trill_recv(trill_sock_t *tsock, mblk_t *mp, const uint8_t *mpsaddr) +{ + trill_header_t *trillhdr; + trill_node_t *dest = NULL; + trill_node_t *source = NULL; + trill_node_t *adj; + uint16_t ournick, adjnick, treeroot; + struct ether_header *ethhdr; + trill_inst_t *tip = tsock->ts_tip; + uint8_t srcaddr[ETHERADDRL]; + size_t trillhdrlen; + int inner_vlan = VLAN_ID_NONE; + int tci; + int idx; + size_t min_size; + + /* Copy Ethernet source address before modifying packet */ + (void) memcpy(srcaddr, mpsaddr, ETHERADDRL); + + /* Pull up TRILL header if necessary. */ + min_size = sizeof (trill_header_t); + if ((MBLKL(mp) < min_size || + !IS_P2ALIGNED(mp->b_rptr, TRILL_HDR_ALIGN)) && + !pullupmsg(mp, min_size)) + goto fail; + + /* LINTED: alignment */ + trillhdr = (trill_header_t *)mp->b_rptr; + if (trillhdr->th_version != TRILL_PROTOCOL_VERS) { + DTRACE_PROBE1(trill__recv__wrongversion, + trill_header_t *, trillhdr); + goto fail; + } + + /* Drop if unknown or invalid nickname */ + if (!VALID_NICK(trillhdr->th_egressnick) || + !VALID_NICK(trillhdr->th_ingressnick)) { + DTRACE_PROBE1(trill__recv__invalidnick, + trill_header_t *, trillhdr); + goto fail; + } + + rw_enter(&tip->ti_rwlock, RW_READER); + ournick = tip->ti_nick; + treeroot = tip->ti_treeroot; + rw_exit(&tip->ti_rwlock); + /* Drop if we received a packet with our nick as ingress */ + if (trillhdr->th_ingressnick == ournick) + goto fail; + + /* Re-pull any TRILL options and inner Ethernet header */ + min_size += GET_TRILL_OPTS_LEN(trillhdr) * sizeof (uint32_t) + + sizeof (struct ether_header); + if (MBLKL(mp) < min_size) { + if (!pullupmsg(mp, min_size)) + goto fail; + /* LINTED: alignment */ + trillhdr = (trill_header_t *)mp->b_rptr; + } + trillhdrlen = sizeof (trill_header_t) + + (GET_TRILL_OPTS_LEN(trillhdr) * sizeof (uint32_t)); + + /* + * Get the inner Ethernet header, plus the inner VLAN header if there + * is one. + */ + /* LINTED: alignment */ + ethhdr = (struct ether_header *)(mp->b_rptr + trillhdrlen); + if (ethhdr->ether_type == htons(ETHERTYPE_VLAN)) { + min_size += sizeof (struct ether_vlan_extinfo); + if (MBLKL(mp) < min_size) { + if (!pullupmsg(mp, min_size)) + goto fail; + /* LINTED: alignment */ + trillhdr = (trill_header_t *)mp->b_rptr; + /* LINTED: alignment */ + ethhdr = (struct ether_header *)(mp->b_rptr + + trillhdrlen); + } + + tci = ntohs(((struct ether_vlan_header *)ethhdr)->ether_tci); + inner_vlan = VLAN_ID(tci); + } + + /* Known/single destination forwarding. */ + if (!trillhdr->th_multidest) { + + /* Inner MacDA must be unicast */ + if (ethhdr->ether_dhost.ether_addr_octet[0] & 1) + goto fail; + + /* Ingress and Egress nicks must be different */ + if (trillhdr->th_egressnick == trillhdr->th_ingressnick) + goto fail; + + DTRACE_PROBE1(trill__recv__singledest, + trill_header_t *, trillhdr); + if (trillhdr->th_egressnick == ournick) { + mp->b_rptr += trillhdrlen; + trill_recv_local(tsock, mp, trillhdr->th_ingressnick); + } else if (trillhdr->th_hopcount > 0) { + trill_dest_fwd(tip, mp, trillhdr->th_egressnick, + B_TRUE, B_FALSE, RBRIDGE_NICKNAME_NONE); + } else { + goto fail; + } + return; + } + + /* + * Multi-destination frame: perform checks verifying we have + * received a valid multi-destination frame before receiving the + * frame locally and forwarding the frame to other RBridges. + * + * Check if we received this multi-destination frame on a + * adjacency in the distribution tree indicated by the frame's + * egress nickname. + */ + if ((dest = trill_node_lookup(tip, trillhdr->th_egressnick)) == NULL) + goto fail; + for (idx = 0; idx < dest->tn_ni->tni_adjcount; idx++) { + adjnick = TNI_ADJNICK(dest->tn_ni, idx); + if ((adj = trill_node_lookup(tip, adjnick)) == NULL) + continue; + if (memcmp(adj->tn_ni->tni_adjsnpa, srcaddr, ETHERADDRL) == 0) { + trill_node_unref(tip, adj); + break; + } + trill_node_unref(tip, adj); + } + + if (idx >= dest->tn_ni->tni_adjcount) { + DTRACE_PROBE2(trill__recv__multidest__adjcheckfail, + trill_header_t *, trillhdr, trill_node_t *, dest); + goto fail; + } + + /* + * Reverse path forwarding check. Check if the ingress RBridge + * that has forwarded the frame advertised the use of the + * distribution tree specified in the egress nick. + */ + if ((source = trill_node_lookup(tip, trillhdr->th_ingressnick)) == NULL) + goto fail; + for (idx = 0; idx < source->tn_ni->tni_dtrootcount; idx++) { + if (TNI_DTROOTNICK(source->tn_ni, idx) == + trillhdr->th_egressnick) + break; + } + + if (idx >= source->tn_ni->tni_dtrootcount) { + /* + * Allow receipt of forwarded frame with the highest + * tree root RBridge as the egress RBridge when the + * ingress RBridge has not advertised the use of any + * distribution trees. + */ + if (source->tn_ni->tni_dtrootcount != 0 || + trillhdr->th_egressnick != treeroot) { + DTRACE_PROBE3( + trill__recv__multidest__rpfcheckfail, + trill_header_t *, trillhdr, trill_node_t *, + source, trill_inst_t *, tip); + goto fail; + } + } + + /* Check hop count before doing any forwarding */ + if (trillhdr->th_hopcount == 0) + goto fail; + + /* Forward frame using the distribution tree specified by egress nick */ + DTRACE_PROBE2(trill__recv__multidest, trill_header_t *, + trillhdr, trill_node_t *, source); + trill_node_unref(tip, source); + trill_node_unref(tip, dest); + + /* Tell forwarding not to free if we're the link forwarder. */ + trill_multidest_fwd(tip, mp, trillhdr->th_egressnick, + trillhdr->th_ingressnick, B_TRUE, srcaddr, inner_vlan, + B_FALSE); + + /* + * Send de-capsulated frame locally if we are the link forwarder (also + * does bridge learning). + */ + mp->b_rptr += trillhdrlen; + trill_recv_local(tsock, mp, trillhdr->th_ingressnick); + KSPINCR(tks_recv); + return; + +fail: + DTRACE_PROBE2(trill__recv__multidest__fail, mblk_t *, mp, + trill_sock_t *, tsock); + if (dest != NULL) + trill_node_unref(tip, dest); + if (source != NULL) + trill_node_unref(tip, source); + freemsg(mp); + KSPINCR(tks_drops); +} + +static void +trill_stop_recv(trill_sock_t *tsock) +{ + mutex_enter(&tsock->ts_socklock); +stop_retry: + if (tsock->ts_state == TS_UNBND || tsock->ts_link == NULL) { + mutex_exit(&tsock->ts_socklock); + return; + } + + /* + * If another thread is closing the socket then wait. Our callers + * expect us to return only after the socket is closed. + */ + if (tsock->ts_flags & TSF_CLOSEWAIT) { + cv_wait(&tsock->ts_sockclosewait, &tsock->ts_socklock); + goto stop_retry; + } + + /* + * Set state and flags to block new bind or close calls + * while we close the socket. + */ + tsock->ts_flags |= TSF_CLOSEWAIT; + + /* Wait until all AF_TRILL socket transmit operations are done */ + while (tsock->ts_sockthreadcount > 0) + cv_wait(&tsock->ts_sockthreadwait, &tsock->ts_socklock); + + /* + * We are guaranteed to be the only thread closing on the + * socket while the TSF_CLOSEWAIT flag is set, all others cv_wait + * for us to finish. + */ + ASSERT(tsock->ts_link != NULL); + if (tsock->ts_ksp != NULL) + kstat_delete(tsock->ts_ksp); + + /* + * Release lock before bridge_trill_lnunref to prevent deadlock + * between trill_ctrl_input thread waiting to acquire ts_socklock + * and bridge_trill_lnunref waiting for the trill thread to finish. + */ + mutex_exit(&tsock->ts_socklock); + + /* + * Release TRILL link reference from Bridging. On return from + * bridge_trill_lnunref we can be sure there are no active TRILL data + * threads for this link. + */ + bridge_trill_lnunref(tsock->ts_link); + + /* Set socket as unbound & wakeup threads waiting for socket to close */ + mutex_enter(&tsock->ts_socklock); + ASSERT(tsock->ts_link != NULL); + tsock->ts_link = NULL; + tsock->ts_state = TS_UNBND; + tsock->ts_flags &= ~TSF_CLOSEWAIT; + cv_broadcast(&tsock->ts_sockclosewait); + mutex_exit(&tsock->ts_socklock); +} + +static int +trill_start_recv(trill_sock_t *tsock, const struct sockaddr *sa, socklen_t len) +{ + struct sockaddr_dl *lladdr = (struct sockaddr_dl *)sa; + datalink_id_t linkid; + int err = 0; + + if (len != sizeof (*lladdr)) + return (EINVAL); + + mutex_enter(&tsock->ts_socklock); + if (tsock->ts_tip == NULL || tsock->ts_state != TS_UNBND) { + err = EINVAL; + goto bind_error; + } + + if (tsock->ts_flags & TSF_CLOSEWAIT || tsock->ts_link != NULL) { + err = EBUSY; + goto bind_error; + } + + (void) memcpy(&(tsock->ts_lladdr), lladdr, + sizeof (struct sockaddr_dl)); + (void) memcpy(&linkid, tsock->ts_lladdr.sdl_data, + sizeof (datalink_id_t)); + + tsock->ts_link = bridge_trill_lnref(tsock->ts_tip->ti_binst, + linkid, tsock); + if (tsock->ts_link == NULL) { + err = EINVAL; + goto bind_error; + } + + trill_kstats_init(tsock, tsock->ts_tip->ti_bridgename); + tsock->ts_state = TS_IDLE; + +bind_error: + mutex_exit(&tsock->ts_socklock); + return (err); +} + +static int +trill_do_unbind(trill_sock_t *tsock) +{ + /* If a bind has not been done, we can't unbind. */ + if (tsock->ts_state != TS_IDLE) + return (EINVAL); + + trill_stop_recv(tsock); + return (0); +} + +static void +trill_instance_unref(trill_inst_t *tip) +{ + rw_enter(&trill_inst_rwlock, RW_WRITER); + rw_enter(&tip->ti_rwlock, RW_WRITER); + if (atomic_dec_uint_nv(&tip->ti_refs) == 0) { + list_remove(&trill_inst_list, tip); + rw_exit(&tip->ti_rwlock); + rw_exit(&trill_inst_rwlock); + if (tip->ti_binst != NULL) + bridge_trill_brunref(tip->ti_binst); + list_destroy(&tip->ti_socklist); + rw_destroy(&tip->ti_rwlock); + kmem_free(tip, sizeof (*tip)); + } else { + rw_exit(&tip->ti_rwlock); + rw_exit(&trill_inst_rwlock); + } +} + +/* + * This is called when the bridge module receives a TRILL-encapsulated packet + * on a given link or a packet identified as "TRILL control." We must verify + * that it's for us (it almost certainly will be), and then either decapsulate + * (if it's to our nickname), forward (if it's to someone else), or send up one + * of the sockets (if it's control traffic). + * + * Sadly, on Ethernet, the control traffic is identified by Outer.MacDA, and + * not by TRILL header information. + */ +static void +trill_recv_pkt_cb(void *lptr, bridge_link_t *blp, mac_resource_handle_t rsrc, + mblk_t *mp, mac_header_info_t *hdr_info) +{ + trill_sock_t *tsock = lptr; + + _NOTE(ARGUNUSED(rsrc)); + + ASSERT(tsock->ts_tip != NULL); + ASSERT(tsock->ts_link != NULL); + ASSERT(!(tsock->ts_flags & TSF_SHUTDOWN)); + + /* + * Only receive packet if the source address is not multicast (which is + * bogus). + */ + if (hdr_info->mhi_saddr[0] & 1) + goto discard; + + /* + * Check if this is our own packet reflected back. It should not be. + */ + if (bcmp(hdr_info->mhi_saddr, blp->bl_local_mac, ETHERADDRL) == 0) + goto discard; + + /* Only receive unicast packet if addressed to us */ + if (hdr_info->mhi_dsttype == MAC_ADDRTYPE_UNICAST && + bcmp(hdr_info->mhi_daddr, blp->bl_local_mac, ETHERADDRL) != 0) + goto discard; + + if (hdr_info->mhi_bindsap == ETHERTYPE_TRILL) { + /* TRILL data packets */ + trill_recv(tsock, mp, hdr_info->mhi_saddr); + } else { + /* Design constraint for cheap IS-IS/BPDU comparison */ + ASSERT(all_isis_rbridges[4] != bridge_group_address[4]); + /* Send received control packet upstream */ + trill_ctrl_input(tsock, mp, hdr_info->mhi_saddr, + hdr_info->mhi_daddr[4] == all_isis_rbridges[4] ? + hdr_info->mhi_tci : TRILL_TCI_BPDU); + } + + return; + +discard: + freemsg(mp); + KSPINCR(tks_drops); +} + +/* + * This is called when the bridge module discovers that the destination address + * for a packet is not local -- it's through some remote node. We must verify + * that the remote node isn't our nickname (it shouldn't be), add a TRILL + * header, and then use the IS-IS data to determine which link and which + * next-hop RBridge should be used for output. We then transmit on that link. + * + * The egress_nick is RBRIDGE_NICKNAME_NONE for the "unknown destination" case. + */ +static void +trill_encap_pkt_cb(void *lptr, bridge_link_t *blp, mac_header_info_t *hdr_info, + mblk_t *mp, uint16_t egress_nick) +{ + uint16_t ournick; + uint16_t dtnick; + trill_node_t *self = NULL; + trill_sock_t *tsock = lptr; + trill_inst_t *tip = tsock->ts_tip; + int vlan = VLAN_ID_NONE; + + _NOTE(ARGUNUSED(blp)); + ASSERT(hdr_info->mhi_bindsap != ETHERTYPE_TRILL); + + /* egress_nick = RBRIDGE_NICKNAME_NONE is valid */ + if (egress_nick != RBRIDGE_NICKNAME_NONE && !VALID_NICK(egress_nick)) + goto discard; + + /* Check if our own nick is valid before we do any forwarding */ + rw_enter(&tip->ti_rwlock, RW_READER); + ournick = tip->ti_nick; + dtnick = tip->ti_treeroot; + rw_exit(&tip->ti_rwlock); + if (!VALID_NICK(ournick)) + goto discard; + + /* + * For Multi-Destination forwarding determine our choice of + * root distribution tree. If we didn't choose a distribution + * tree (dtroots_count=0) then we use the highest priority tree + * root (t_treeroot) else we drop the packet without forwarding. + */ + if (egress_nick == RBRIDGE_NICKNAME_NONE) { + if ((self = trill_node_lookup(tip, ournick)) == NULL) + goto discard; + + /* + * Use the first DT configured for now. In future we + * should have DT selection code here. + */ + if (self->tn_ni->tni_dtrootcount > 0) { + dtnick = TNI_DTROOTNICK(self->tn_ni, 0); + } + + trill_node_unref(tip, self); + if (!VALID_NICK(dtnick)) { + DTRACE_PROBE(trill__fwd__packet__nodtroot); + goto discard; + } + } + + /* + * Retrieve VLAN ID of the native frame used for VLAN + * pruning of multi-destination frames. + */ + if (hdr_info->mhi_istagged) { + vlan = VLAN_ID(hdr_info->mhi_tci); + } + + DTRACE_PROBE2(trill__fwd__packet, mac_header_info_t *, hdr_info, + uint16_t, egress_nick); + if (egress_nick == RBRIDGE_NICKNAME_NONE) { + trill_multidest_fwd(tip, mp, dtnick, + ournick, B_FALSE, NULL, vlan, B_TRUE); + } else { + trill_dest_fwd(tip, mp, egress_nick, B_FALSE, B_FALSE, + RBRIDGE_NICKNAME_NONE); + } + KSPINCR(tks_encap); + return; + +discard: + freemsg(mp); +} + +/* + * This is called when the bridge module has completely torn down a bridge + * instance and all of the attached links. We need to make the TRILL instance + * go away at this point. + */ +static void +trill_br_dstr_cb(void *bptr, bridge_inst_t *bip) +{ + trill_inst_t *tip = bptr; + + _NOTE(ARGUNUSED(bip)); + rw_enter(&tip->ti_rwlock, RW_WRITER); + if (tip->ti_binst != NULL) + bridge_trill_brunref(tip->ti_binst); + tip->ti_binst = NULL; + rw_exit(&tip->ti_rwlock); +} + +/* + * This is called when the bridge module is tearing down a link, but before the + * actual tear-down starts. When this function returns, we must make sure that + * we will not initiate any new transmits on this link. + */ +static void +trill_ln_dstr_cb(void *lptr, bridge_link_t *blp) +{ + trill_sock_t *tsock = lptr; + + _NOTE(ARGUNUSED(blp)); + trill_stop_recv(tsock); +} + +static void +trill_init(void) +{ + list_create(&trill_inst_list, sizeof (trill_inst_t), + offsetof(trill_inst_t, ti_instnode)); + rw_init(&trill_inst_rwlock, NULL, RW_DRIVER, NULL); + bridge_trill_register_cb(trill_recv_pkt_cb, trill_encap_pkt_cb, + trill_br_dstr_cb, trill_ln_dstr_cb); +} + +static void +trill_fini(void) +{ + bridge_trill_register_cb(NULL, NULL, NULL, NULL); + rw_destroy(&trill_inst_rwlock); + list_destroy(&trill_inst_list); +} + +/* Loadable module configuration entry points */ +int +_init(void) +{ + int rc; + + trill_init(); + if ((rc = mod_install(&ml)) != 0) + trill_fini(); + return (rc); +} + +int +_info(struct modinfo *modinfop) +{ + return (mod_info(&ml, modinfop)); +} + +int +_fini(void) +{ + int rc; + + rw_enter(&trill_inst_rwlock, RW_READER); + rc = list_is_empty(&trill_inst_list) ? 0 : EBUSY; + rw_exit(&trill_inst_rwlock); + if (rc == 0 && ((rc = mod_remove(&ml)) == 0)) + trill_fini(); + return (rc); +} + +static void +trill_kstats_init(trill_sock_t *tsock, const char *bname) +{ + int i; + char kstatname[KSTAT_STRLEN]; + kstat_named_t *knt; + static const char *sock_kstats_list[] = { TRILL_KSSOCK_NAMES }; + char link_name[MAXNAMELEN]; + int num; + int err; + + bzero(link_name, sizeof (link_name)); + if ((err = dls_mgmt_get_linkinfo(tsock->ts_link->bl_linkid, link_name, + NULL, NULL, NULL)) != 0) { + cmn_err(CE_WARN, "%s: trill_kstats_init: error %d retrieving" + " linkinfo for linkid:%d", "trill", err, + tsock->ts_link->bl_linkid); + return; + } + + bzero(kstatname, sizeof (kstatname)); + (void) snprintf(kstatname, sizeof (kstatname), "%s-%s", + bname, link_name); + + num = sizeof (sock_kstats_list) / sizeof (*sock_kstats_list); + for (i = 0; i < num; i++) { + knt = (kstat_named_t *)&(tsock->ts_kstats); + kstat_named_init(&knt[i], sock_kstats_list[i], + KSTAT_DATA_UINT64); + } + + tsock->ts_ksp = kstat_create_zone("trill", 0, kstatname, "sock", + KSTAT_TYPE_NAMED, num, KSTAT_FLAG_VIRTUAL, GLOBAL_ZONEID); + if (tsock->ts_ksp != NULL) { + tsock->ts_ksp->ks_data = &tsock->ts_kstats; + kstat_install(tsock->ts_ksp); + } +} + +static trill_sock_t * +trill_do_open(int flags) +{ + trill_sock_t *tsock; + int kmflag = ((flags & SOCKET_NOSLEEP)) ? KM_NOSLEEP:KM_SLEEP; + + tsock = kmem_zalloc(sizeof (trill_sock_t), kmflag); + if (tsock != NULL) { + tsock->ts_state = TS_UNBND; + tsock->ts_refs++; + mutex_init(&tsock->ts_socklock, NULL, MUTEX_DRIVER, NULL); + cv_init(&tsock->ts_sockthreadwait, NULL, CV_DRIVER, NULL); + cv_init(&tsock->ts_sockclosewait, NULL, CV_DRIVER, NULL); + } + return (tsock); +} + +static int +trill_find_bridge(trill_sock_t *tsock, const char *bname, boolean_t can_create) +{ + trill_inst_t *tip, *newtip = NULL; + + /* Allocate some memory (speculatively) before taking locks */ + if (can_create) + newtip = kmem_zalloc(sizeof (*tip), KM_NOSLEEP); + + rw_enter(&trill_inst_rwlock, RW_WRITER); + for (tip = list_head(&trill_inst_list); tip != NULL; + tip = list_next(&trill_inst_list, tip)) { + if (strcmp(tip->ti_bridgename, bname) == 0) + break; + } + if (tip == NULL) { + if (!can_create || newtip == NULL) { + rw_exit(&trill_inst_rwlock); + return (can_create ? ENOMEM : ENOENT); + } + + tip = newtip; + newtip = NULL; + (void) strcpy(tip->ti_bridgename, bname); + + /* Register TRILL instance with bridging */ + tip->ti_binst = bridge_trill_brref(bname, tip); + if (tip->ti_binst == NULL) { + rw_exit(&trill_inst_rwlock); + kmem_free(tip, sizeof (*tip)); + return (ENOENT); + } + + rw_init(&tip->ti_rwlock, NULL, RW_DRIVER, NULL); + list_create(&tip->ti_socklist, sizeof (trill_sock_t), + offsetof(trill_sock_t, ts_socklistnode)); + list_insert_tail(&trill_inst_list, tip); + } + atomic_inc_uint(&tip->ti_refs); + rw_exit(&trill_inst_rwlock); + + /* If we didn't need the preallocated memory, then discard now. */ + if (newtip != NULL) + kmem_free(newtip, sizeof (*newtip)); + + rw_enter(&tip->ti_rwlock, RW_WRITER); + list_insert_tail(&(tip->ti_socklist), tsock); + tsock->ts_tip = tip; + rw_exit(&tip->ti_rwlock); + return (0); +} + +static void +trill_clear_bridge(trill_sock_t *tsock) +{ + trill_inst_t *tip; + + if ((tip = tsock->ts_tip) == NULL) + return; + rw_enter(&tip->ti_rwlock, RW_WRITER); + list_remove(&tip->ti_socklist, tsock); + if (list_is_empty(&tip->ti_socklist)) + trill_del_all(tip, B_TRUE); + rw_exit(&tip->ti_rwlock); +} + +static void +trill_sock_unref(trill_sock_t *tsock) +{ + if (atomic_dec_uint_nv(&tsock->ts_refs) == 0) { + mutex_destroy(&tsock->ts_socklock); + cv_destroy(&tsock->ts_sockthreadwait); + cv_destroy(&tsock->ts_sockclosewait); + kmem_free(tsock, sizeof (trill_sock_t)); + } +} + +static void +trill_do_close(trill_sock_t *tsock) +{ + trill_inst_t *tip; + + tip = tsock->ts_tip; + trill_stop_recv(tsock); + /* Remove socket from TRILL instance socket list */ + trill_clear_bridge(tsock); + tsock->ts_flags |= TSF_SHUTDOWN; + trill_sock_unref(tsock); + if (tip != NULL) + trill_instance_unref(tip); +} + +static void +trill_del_all(trill_inst_t *tip, boolean_t lockheld) +{ + int i; + + if (!lockheld) + rw_enter(&tip->ti_rwlock, RW_WRITER); + for (i = RBRIDGE_NICKNAME_MIN; i < RBRIDGE_NICKNAME_MAX; i++) { + if (tip->ti_nodes[i] != NULL) + (void) trill_del_nick(tip, i, B_TRUE); + } + if (!lockheld) + rw_exit(&tip->ti_rwlock); +} + +static void +trill_node_free(trill_node_t *nick_entry) +{ + trill_nickinfo_t *tni; + + tni = nick_entry->tn_ni; + kmem_free(tni, TNI_TOTALSIZE(tni)); + kmem_free(nick_entry, sizeof (trill_node_t)); +} + +static void +trill_node_unref(trill_inst_t *tip, trill_node_t *tnp) +{ + if (atomic_dec_uint_nv(&tnp->tn_refs) == 0) { + if (tnp->tn_tsp != NULL) + trill_sock_unref(tnp->tn_tsp); + trill_node_free(tnp); + (void) atomic_dec_uint_nv(&tip->ti_nodecount); + } +} + +static trill_node_t * +trill_node_lookup(trill_inst_t *tip, uint16_t nick) +{ + trill_node_t *nick_entry; + + if (!VALID_NICK(nick)) + return (NULL); + rw_enter(&tip->ti_rwlock, RW_READER); + nick_entry = tip->ti_nodes[nick]; + if (nick_entry != NULL) { + atomic_inc_uint(&nick_entry->tn_refs); + } + rw_exit(&tip->ti_rwlock); + return (nick_entry); +} + +static int +trill_del_nick(trill_inst_t *tip, uint16_t nick, boolean_t lockheld) +{ + trill_node_t *nick_entry; + int rc = ENOENT; + + if (!lockheld) + rw_enter(&tip->ti_rwlock, RW_WRITER); + if (VALID_NICK(nick)) { + nick_entry = tip->ti_nodes[nick]; + if (nick_entry != NULL) { + trill_node_unref(tip, nick_entry); + tip->ti_nodes[nick] = NULL; + rc = 0; + } + } + if (!lockheld) + rw_exit(&tip->ti_rwlock); + return (rc); +} + +static int +trill_add_nick(trill_inst_t *tip, void *arg, boolean_t self, int mode) +{ + uint16_t nick; + int size; + trill_node_t *tnode; + trill_nickinfo_t tnihdr; + + /* First make sure we have at least the header available */ + if (ddi_copyin(arg, &tnihdr, sizeof (trill_nickinfo_t), mode) != 0) + return (EFAULT); + + nick = tnihdr.tni_nick; + if (!VALID_NICK(nick)) { + DTRACE_PROBE1(trill__add__nick__bad, trill_nickinfo_t *, + &tnihdr); + return (EINVAL); + } + + size = TNI_TOTALSIZE(&tnihdr); + if (size > TNI_MAXSIZE) + return (EINVAL); + tnode = kmem_zalloc(sizeof (trill_node_t), KM_SLEEP); + tnode->tn_ni = kmem_zalloc(size, KM_SLEEP); + if (ddi_copyin(arg, tnode->tn_ni, size, mode) != 0) { + kmem_free(tnode->tn_ni, size); + kmem_free(tnode, sizeof (trill_node_t)); + return (EFAULT); + } + + tnode->tn_refs++; + rw_enter(&tip->ti_rwlock, RW_WRITER); + if (tip->ti_nodes[nick] != NULL) + (void) trill_del_nick(tip, nick, B_TRUE); + + if (self) { + tip->ti_nick = nick; + } else { + tnode->tn_tsp = find_trill_link(tip, + tnode->tn_ni->tni_linkid); + } + DTRACE_PROBE2(trill__add__nick, trill_node_t *, tnode, + uint16_t, nick); + tip->ti_nodes[nick] = tnode; + tip->ti_nodecount++; + rw_exit(&tip->ti_rwlock); + return (0); +} + +static int +trill_do_ioctl(trill_sock_t *tsock, int cmd, void *arg, int mode) +{ + int error = 0; + trill_inst_t *tip = tsock->ts_tip; + + switch (cmd) { + case TRILL_DESIGVLAN: { + uint16_t desigvlan; + + if (ddi_copyin(arg, &desigvlan, sizeof (desigvlan), mode) != 0) + return (EFAULT); + tsock->ts_desigvlan = desigvlan; + break; + } + case TRILL_VLANFWDER: { + uint8_t vlans[TRILL_VLANS_ARRSIZE]; + + if (tsock->ts_link == NULL) + return (EINVAL); + if ((ddi_copyin(arg, vlans, sizeof (vlans), mode)) != 0) + return (EFAULT); + bridge_trill_setvlans(tsock->ts_link, vlans); + break; + } + case TRILL_SETNICK: + if (tip == NULL) + return (EINVAL); + error = trill_add_nick(tip, arg, B_TRUE, mode); + break; + + case TRILL_GETNICK: + if (tip == NULL) + return (EINVAL); + rw_enter(&tip->ti_rwlock, RW_READER); + if (ddi_copyout(&tip->ti_nick, arg, sizeof (tip->ti_nick), + mode) != 0) + error = EFAULT; + rw_exit(&tip->ti_rwlock); + break; + + case TRILL_ADDNICK: + if (tip == NULL) + break; + error = trill_add_nick(tip, arg, B_FALSE, mode); + break; + + case TRILL_DELNICK: { + uint16_t delnick; + + if (tip == NULL) + break; + if (ddi_copyin(arg, &delnick, sizeof (delnick), mode) != 0) + return (EFAULT); + error = trill_del_nick(tip, delnick, B_FALSE); + break; + } + case TRILL_DELALL: + if (tip == NULL) + break; + trill_del_all(tip, B_FALSE); + break; + + case TRILL_TREEROOT: { + uint16_t treeroot; + + if (tip == NULL) + break; + if (ddi_copyin(arg, &treeroot, sizeof (treeroot), mode) != 0) + return (EFAULT); + if (!VALID_NICK(treeroot)) + return (EINVAL); + rw_enter(&tip->ti_rwlock, RW_WRITER); + tip->ti_treeroot = treeroot; + rw_exit(&tip->ti_rwlock); + break; + } + case TRILL_HWADDR: + if (tsock->ts_link == NULL) + break; + if (ddi_copyout(tsock->ts_link->bl_local_mac, arg, ETHERADDRL, + mode) != 0) + return (EFAULT); + break; + + case TRILL_NEWBRIDGE: { + char bname[MAXLINKNAMELEN]; + + if (tsock->ts_state != TS_UNBND) + return (ENOTSUP); + /* ts_tip can only be set once */ + if (tip != NULL) + return (EEXIST); + if (ddi_copyin(arg, bname, sizeof (bname), mode) != 0) + return (EFAULT); + bname[MAXLINKNAMELEN-1] = '\0'; + error = trill_find_bridge(tsock, bname, B_TRUE); + break; + } + + case TRILL_GETBRIDGE: { + char bname[MAXLINKNAMELEN]; + + /* ts_tip can only be set once */ + if (tip != NULL) + return (EEXIST); + if (ddi_copyin(arg, bname, sizeof (bname), mode) != 0) + return (EFAULT); + bname[MAXLINKNAMELEN - 1] = '\0'; + error = trill_find_bridge(tsock, bname, B_FALSE); + break; + } + + case TRILL_LISTNICK: { + trill_listnick_t tln; + trill_node_t *tnp; + trill_nickinfo_t *tnip; + uint16_t nick; + + if (tip == NULL) + return (EINVAL); + if (ddi_copyin(arg, &tln, sizeof (tln), mode) != 0) + return (EFAULT); + nick = tln.tln_nick; + if (nick >= RBRIDGE_NICKNAME_MAX) { + error = EINVAL; + break; + } + rw_enter(&tip->ti_rwlock, RW_READER); + while (++nick < RBRIDGE_NICKNAME_MAX) { + if ((tnp = tip->ti_nodes[nick]) != NULL) { + tnip = tnp->tn_ni; + ASSERT(nick == tnip->tni_nick); + tln.tln_nick = nick; + bcopy(tnip->tni_adjsnpa, tln.tln_nexthop, + ETHERADDRL); + tln.tln_ours = nick == tip->ti_nick; + if (tln.tln_ours || tnp->tn_tsp == NULL) { + tln.tln_linkid = + DATALINK_INVALID_LINKID; + } else { + tln.tln_linkid = + tnp->tn_tsp->ts_link->bl_linkid; + } + break; + } + } + rw_exit(&tip->ti_rwlock); + if (nick >= RBRIDGE_NICKNAME_MAX) + bzero(&tln, sizeof (tln)); + if (ddi_copyout(&tln, arg, sizeof (tln), mode) != 0) + return (EFAULT); + break; + } + + /* + * Port flush: this is used when we lose AF on a port. We must discard + * all regular bridge forwarding entries on this port with the + * indicated VLAN. + */ + case TRILL_PORTFLUSH: { + uint16_t vlan = (uint16_t)(uintptr_t)arg; + + if (tsock->ts_link == NULL) + return (EINVAL); + bridge_trill_flush(tsock->ts_link, vlan, B_FALSE); + break; + } + + /* + * Nick flush: this is used when we lose AF on a port. We must discard + * all bridge TRILL forwarding entries on this port with the indicated + * VLAN. + */ + case TRILL_NICKFLUSH: { + uint16_t vlan = (uint16_t)(uintptr_t)arg; + + if (tsock->ts_link == NULL) + return (EINVAL); + bridge_trill_flush(tsock->ts_link, vlan, B_TRUE); + break; + } + + case TRILL_GETMTU: + if (tsock->ts_link == NULL) + break; + if (ddi_copyout(&tsock->ts_link->bl_maxsdu, arg, + sizeof (uint_t), mode) != 0) + return (EFAULT); + break; + + default: + error = ENOTSUP; + break; + } + + return (error); +} + +/* + * Sends received packet back upstream on the TRILL socket. + * Consumes passed mblk_t. + */ +static void +trill_ctrl_input(trill_sock_t *tsock, mblk_t *mp, const uint8_t *saddr, + uint16_t tci) +{ + int udi_size; + mblk_t *mp1; + struct T_unitdata_ind *tudi; + struct sockaddr_dl *sdl; + char *lladdr; + int error; + + ASSERT(!(tsock->ts_flags & TSF_SHUTDOWN)); + if (tsock->ts_flow_ctrld) { + freemsg(mp); + KSPINCR(tks_drops); + return; + } + + udi_size = sizeof (struct T_unitdata_ind) + + sizeof (struct sockaddr_dl); + mp1 = allocb(udi_size, BPRI_MED); + if (mp1 == NULL) { + freemsg(mp); + KSPINCR(tks_drops); + return; + } + + mp1->b_cont = mp; + mp = mp1; + mp->b_datap->db_type = M_PROTO; + /* LINTED: alignment */ + tudi = (struct T_unitdata_ind *)mp->b_rptr; + mp->b_wptr = (uchar_t *)tudi + udi_size; + + tudi->PRIM_type = T_UNITDATA_IND; + tudi->SRC_length = sizeof (struct sockaddr_dl); + tudi->SRC_offset = sizeof (struct T_unitdata_ind); + tudi->OPT_length = 0; + tudi->OPT_offset = sizeof (struct T_unitdata_ind) + + sizeof (struct sockaddr_dl); + + /* Information of the link on which packet was received. */ + sdl = (struct sockaddr_dl *)&tudi[1]; + (void) memset(sdl, 0, sizeof (struct sockaddr_dl)); + sdl->sdl_family = AF_TRILL; + + /* LINTED: alignment */ + *(datalink_id_t *)sdl->sdl_data = tsock->ts_link->bl_linkid; + sdl->sdl_nlen = sizeof (tsock->ts_link->bl_linkid); + + lladdr = LLADDR(sdl); + (void) memcpy(lladdr, saddr, ETHERADDRL); + lladdr += ETHERADDRL; + sdl->sdl_alen = ETHERADDRL; + + /* LINTED: alignment */ + *(uint16_t *)lladdr = tci; + sdl->sdl_slen = sizeof (uint16_t); + + DTRACE_PROBE2(trill__ctrl__input, trill_sock_t *, tsock, mblk_t *, mp); + (*tsock->ts_conn_upcalls->su_recv)(tsock->ts_conn_upper_handle, + mp, msgdsize(mp), 0, &error, NULL); + + if (error == ENOSPC) { + mutex_enter(&tsock->ts_socklock); + (*tsock->ts_conn_upcalls->su_recv)(tsock->ts_conn_upper_handle, + NULL, 0, 0, &error, NULL); + if (error == ENOSPC) + tsock->ts_flow_ctrld = B_TRUE; + mutex_exit(&tsock->ts_socklock); + KSPINCR(tks_drops); + } else if (error != 0) { + KSPINCR(tks_drops); + } else { + KSPINCR(tks_recv); + } + + DTRACE_PROBE2(trill__ctrl__input__done, trill_sock_t *, + tsock, int, error); +} + +/* ARGSUSED */ +static void +trill_activate(sock_lower_handle_t proto_handle, + sock_upper_handle_t sock_handle, sock_upcalls_t *sock_upcalls, + int flags, cred_t *cr) +{ + trill_sock_t *tsock = (trill_sock_t *)proto_handle; + struct sock_proto_props sopp; + + tsock->ts_conn_upcalls = sock_upcalls; + tsock->ts_conn_upper_handle = sock_handle; + + sopp.sopp_flags = SOCKOPT_WROFF | SOCKOPT_RCVHIWAT | + SOCKOPT_RCVLOWAT | SOCKOPT_MAXADDRLEN | SOCKOPT_MAXPSZ | + SOCKOPT_MAXBLK | SOCKOPT_MINPSZ; + sopp.sopp_wroff = 0; + sopp.sopp_rxhiwat = SOCKET_RECVHIWATER; + sopp.sopp_rxlowat = SOCKET_RECVLOWATER; + sopp.sopp_maxaddrlen = sizeof (struct sockaddr_dl); + sopp.sopp_maxpsz = INFPSZ; + sopp.sopp_maxblk = INFPSZ; + sopp.sopp_minpsz = 0; + (*tsock->ts_conn_upcalls->su_set_proto_props)( + tsock->ts_conn_upper_handle, &sopp); +} + +/* ARGSUSED */ +static int +trill_close(sock_lower_handle_t proto_handle, int flags, cred_t *cr) +{ + trill_sock_t *tsock = (trill_sock_t *)proto_handle; + + trill_do_close(tsock); + return (0); +} + +/* ARGSUSED */ +static int +trill_bind(sock_lower_handle_t proto_handle, struct sockaddr *sa, + socklen_t len, cred_t *cr) +{ + int error; + trill_sock_t *tsock = (trill_sock_t *)proto_handle; + + if (sa == NULL) + error = trill_do_unbind(tsock); + else + error = trill_start_recv(tsock, sa, len); + + return (error); +} + +/* ARGSUSED */ +static int +trill_connect(sock_lower_handle_t proto_handle, const struct sockaddr *sa, + socklen_t len, sock_connid_t *id, cred_t *cr) +{ + return (EOPNOTSUPP); +} + +/* ARGSUSED */ +static int +trill_send(sock_lower_handle_t proto_handle, mblk_t *mp, struct nmsghdr *msg, + cred_t *cr) +{ + trill_sock_t *tsock = (trill_sock_t *)proto_handle; + struct sockaddr_dl *laddr; + uint16_t tci; + + ASSERT(DB_TYPE(mp) == M_DATA); + ASSERT(!(tsock->ts_flags & TSF_SHUTDOWN)); + + if (msg->msg_name == NULL || msg->msg_namelen != sizeof (*laddr)) + goto eproto; + + /* + * The name is a datalink_id_t, the address is an Ethernet address, and + * the selector value is the VLAN ID. + */ + laddr = (struct sockaddr_dl *)msg->msg_name; + if (laddr->sdl_nlen != sizeof (datalink_id_t) || + laddr->sdl_alen != ETHERADDRL || + (laddr->sdl_slen != sizeof (tci) && laddr->sdl_slen != 0)) + goto eproto; + + mutex_enter(&tsock->ts_socklock); + if (tsock->ts_state != TS_IDLE || tsock->ts_link == NULL) { + mutex_exit(&tsock->ts_socklock); + goto eproto; + } + atomic_inc_uint(&tsock->ts_sockthreadcount); + mutex_exit(&tsock->ts_socklock); + + /* + * Safe to dereference VLAN now, as we've checked the user's specified + * values, and alignment is now guaranteed. + */ + if (laddr->sdl_slen == 0) { + tci = TRILL_NO_TCI; + } else { + /* LINTED: alignment */ + tci = *(uint16_t *)(LLADDR(laddr) + ETHERADDRL); + } + + mp = create_trill_header(tsock, mp, (const uchar_t *)LLADDR(laddr), + B_TRUE, B_FALSE, tci, msgdsize(mp)); + if (mp != NULL) { + mp = bridge_trill_output(tsock->ts_link, mp); + if (mp == NULL) { + KSPINCR(tks_sent); + } else { + freemsg(mp); + KSPINCR(tks_drops); + } + } + + /* Wake up any threads blocking on us */ + if (atomic_dec_uint_nv(&tsock->ts_sockthreadcount) == 0) + cv_broadcast(&tsock->ts_sockthreadwait); + return (0); + +eproto: + freemsg(mp); + KSPINCR(tks_drops); + return (EPROTO); +} + +/* ARGSUSED */ +static int +trill_ioctl(sock_lower_handle_t proto_handle, int cmd, intptr_t arg, + int mode, int32_t *rvalp, cred_t *cr) +{ + trill_sock_t *tsock = (trill_sock_t *)proto_handle; + int rc; + + switch (cmd) { + /* List of unprivileged TRILL ioctls */ + case TRILL_GETNICK: + case TRILL_GETBRIDGE: + case TRILL_LISTNICK: + break; + default: + if (secpolicy_dl_config(cr) != 0) + return (EPERM); + break; + } + + /* Lock ensures socket state is unchanged during ioctl handling */ + mutex_enter(&tsock->ts_socklock); + rc = trill_do_ioctl(tsock, cmd, (void *)arg, mode); + mutex_exit(&tsock->ts_socklock); + return (rc); +} + +/* ARGSUSED */ +static int +trill_accept(sock_lower_handle_t lproto_handle, + sock_lower_handle_t eproto_handle, sock_upper_handle_t sock_handle, + cred_t *cr) +{ + return (EINVAL); +} + +/* ARGSUSED */ +static int +trill_listen(sock_lower_handle_t proto_handle, int backlog, cred_t *cr) +{ + return (EINVAL); +} + +static void +trill_clr_flowctrl(sock_lower_handle_t proto_handle) +{ + trill_sock_t *tsock = (trill_sock_t *)proto_handle; + + mutex_enter(&tsock->ts_socklock); + tsock->ts_flow_ctrld = B_FALSE; + mutex_exit(&tsock->ts_socklock); +} + +/* ARGSUSED */ +static int +trill_getsockname(sock_lower_handle_t proto_handle, struct sockaddr *addr, + socklen_t *addrlen, cred_t *cr) +{ + return (EOPNOTSUPP); +} + +/* ARGSUSED */ +static int +trill_getpeername(sock_lower_handle_t proto_handle, struct sockaddr *addr, + socklen_t *addrlen, cred_t *cr) +{ + return (EOPNOTSUPP); +} + +/* ARGSUSED */ +static int +trill_getsockopt(sock_lower_handle_t proto_handle, int level, int option_name, + void *optvalp, socklen_t *optlen, cred_t *cr) +{ + return (EOPNOTSUPP); +} + +/* ARGSUSED */ +static int +trill_setsockopt(sock_lower_handle_t proto_handle, int level, int option_name, + const void *optvalp, socklen_t optlen, cred_t *cr) +{ + return (EOPNOTSUPP); +} + +/* ARGSUSED */ +static int +trill_shutdown(sock_lower_handle_t proto_handle, int how, cred_t *cr) +{ + return (EOPNOTSUPP); +} + +static sock_downcalls_t sock_trill_downcalls = { + trill_activate, /* sd_activate */ + trill_accept, /* sd_accept */ + trill_bind, /* sd_bind */ + trill_listen, /* sd_listen */ + trill_connect, /* sd_connect */ + trill_getpeername, /* sd_getpeername */ + trill_getsockname, /* sd_getsockname */ + trill_getsockopt, /* sd_getsockopt */ + trill_setsockopt, /* sd_setsockopt */ + trill_send, /* sd_send */ + NULL, /* sd_send_uio */ + NULL, /* sd_recv_uio */ + NULL, /* sd_poll */ + trill_shutdown, /* sd_shutdown */ + trill_clr_flowctrl, /* sd_setflowctrl */ + trill_ioctl, /* sd_ioctl */ + trill_close /* sd_close */ +}; + +/* ARGSUSED */ +static sock_lower_handle_t +trill_create(int family, int type, int proto, sock_downcalls_t **sock_downcalls, + uint_t *smodep, int *errorp, int flags, cred_t *credp) +{ + trill_sock_t *tsock; + + if (family != AF_TRILL || type != SOCK_DGRAM || proto != 0) { + *errorp = EPROTONOSUPPORT; + return (NULL); + } + + *sock_downcalls = &sock_trill_downcalls; + *smodep = SM_ATOMIC; + tsock = trill_do_open(flags); + *errorp = (tsock != NULL) ? 0:ENOMEM; + return ((sock_lower_handle_t)tsock); +} diff --git a/usr/src/uts/common/io/trill_impl.h b/usr/src/uts/common/io/trill_impl.h new file mode 100644 index 0000000000..9648d28df3 --- /dev/null +++ b/usr/src/uts/common/io/trill_impl.h @@ -0,0 +1,145 @@ +/* + * 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 _TRILL_IMPL_H +#define _TRILL_IMPL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <sys/list.h> +#include <net/trill.h> +#include <sys/mac.h> +#include <sys/kstat.h> +#include <sys/rwlock.h> +#include <net/bridge_impl.h> +#include <net/if_dl.h> + +#define TRILL_KSSOCK_NAMES "recv", "sent", "drops", "encap", "decap", "forward" + +/* kstats per TRILL socket */ +typedef struct trill_kssock_s { + kstat_named_t tks_recv; /* packets received */ + kstat_named_t tks_sent; /* packets sent through */ + kstat_named_t tks_drops; /* packets dropped */ + kstat_named_t tks_encap; /* packets encapsulated */ + kstat_named_t tks_decap; /* packets decapsulated */ + kstat_named_t tks_forward; /* packets forwarded */ +} trill_kssock_t; + +#define KSPINCR(stat) ++(tsock->ts_kstats.stat.value.ui64) + +#define TRILL_NO_TCI 0 /* No VLAN tag */ +#define TRILL_VLANS_ARRSIZE ((1<<12)/NBBY) +#define TRILL_VLANBIT(v) ((v) % NBBY) +#define TRILL_VLANBYTE(v) ((v)/NBBY) +#define TRILL_VLANISSET(l, v) ((l)[TRILL_VLANBYTE(v)] & (1<<TRILL_VLANBIT(v))) + +struct trill_node_s; + +/* + * TRILL instance structure, one for each TRILL instance running in + * support of a bridge instance. Members ti_bridgename and ti_binst + * refer to the specific bridge instance. The bridge instance in + * question must be online before we can support and rely on it. + * We rely on the bridge instance for TRILL sockets to transmit and + * receive TRILL packets. Each TRILL instance holds the TRILL + * forwarding and nick database in ti_nodes. trill_inst_rwlock + * protects changes to the TRILL instances list. Within each TRILL + * instance the ti_rwlock protects changes to the structure. A refcount + * (ti_refs) helps in destroying the TRILL instance when all TRILL + * sockets part of the instance are shutdown. + */ +typedef struct trill_s { + list_node_t ti_instnode; + uint16_t ti_nick; /* our nickname */ + uint16_t ti_treeroot; /* tree root nickname */ + struct trill_node_s *ti_nodes[RBRIDGE_NICKNAME_MAX]; + uint_t ti_nodecount; + list_t ti_socklist; + char ti_bridgename[MAXLINKNAMELEN]; + krwlock_t ti_rwlock; + uint_t ti_refs; + bridge_inst_t *ti_binst; +} trill_inst_t; + +/* + * TRILL socket structure. IS-IS daemon opens a TRILL socket for + * each broadcast link the TRILL IS-IS protocol instance is + * running on. TRILL specific link properties, state and stats + * are stored as well. ts_vlanfwder indicates whether the RBridges + * is the designated forwarder on the link for a particular VLAN. + * A refcount (ts_refs) ensures the last consumer (TRILL module + * or the IS-IS daemon) destroys the socket. + */ +typedef struct trillsocket_s { + list_node_t ts_socklistnode; + uint8_t ts_state; + bridge_link_t *ts_link; + struct sockaddr_dl ts_lladdr; + uint16_t ts_desigvlan; + kstat_t *ts_ksp; + trill_kssock_t ts_kstats; + trill_inst_t *ts_tip; + uint_t ts_refs; + uint_t ts_flags; + sock_upcalls_t *ts_conn_upcalls; /* Upcalls to sockfs */ + sock_upper_handle_t ts_conn_upper_handle; /* sonode */ + boolean_t ts_flow_ctrld; + kmutex_t ts_socklock; + uint_t ts_sockthreadcount; + kcondvar_t ts_sockthreadwait; + kcondvar_t ts_sockclosewait; +} trill_sock_t; + +/* + * TRILL socket flags (ts_flags). TSF_SHUTDOWN indicates the TRILL socket + * owner (IS-IS daemon process) had done a close on the socket and other + * consumers (TRILL threads) should not pass any packets downstream. + * TSF_CLOSEWAIT indicates socket close is in progress. + */ +#define TSF_SHUTDOWN 0x0001 +#define TSF_CLOSEWAIT 0x0002 + +/* + * TRILL node information structure. Holds information to reach the + * TRILL node and other RBridge information specified in trill_nick_info_t + */ +typedef struct trill_node_s { + trill_sock_t *tn_tsp; + trill_nickinfo_t *tn_ni; + uint_t tn_refs; +} trill_node_t; + +/* Limit to alloc max 1MB per trill_nickinfo_t received from user daemon */ +#define TNI_MAXSIZE (1<<30) + +#ifdef __cplusplus +} +#endif + +#endif /* _TRILL_IMPL_H */ diff --git a/usr/src/uts/common/net/Makefile b/usr/src/uts/common/net/Makefile index 288be848d6..b5341f74c0 100644 --- a/usr/src/uts/common/net/Makefile +++ b/usr/src/uts/common/net/Makefile @@ -28,7 +28,7 @@ include ../../../Makefile.master HDRS= af.h if.h if_arp.h if_dl.h if_types.h route.h pfkeyv2.h pfpolicy.h \ ppp-comp.h ppp_defs.h pppio.h vjcompress.h sppptun.h pppoe.h radix.h \ - wpa.h simnet.h + wpa.h simnet.h bridge.h bridge_impl.h trill.h ROOTDIRS= $(ROOT)/usr/include/net diff --git a/usr/src/uts/common/net/bridge.h b/usr/src/uts/common/net/bridge.h new file mode 100644 index 0000000000..47249a36f5 --- /dev/null +++ b/usr/src/uts/common/net/bridge.h @@ -0,0 +1,117 @@ +/* + * 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 _NET_BRIDGE_H +#define _NET_BRIDGE_H + +/* + * Private communication interface between bridging related daemons and kernel + * layer-two (Ethernet) bridging module. + */ + +#include <sys/param.h> +#include <sys/dld.h> +#include <sys/dld_ioc.h> +#include <sys/ethernet.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* Specified by IEEE 802.1d */ +#define BRIDGE_GROUP_ADDRESS { 0x01, 0x80, 0xC2, 0, 0, 0 } + +/* The constant below is "BRG" in hex. */ +#define _BRIOC(n) (0x42524700 + (n)) + +#define BRIOC_NEWBRIDGE _BRIOC(1) /* Create bridge; bridge_newbridge_t */ +#define BRIOC_ADDLINK _BRIOC(2) /* Add link to bridge; linkid+name */ +#define BRIOC_REMLINK _BRIOC(3) /* Remove link from bridge; linkid */ +#define BRIOC_SETSTATE _BRIOC(4) /* bridge_setstate_t */ +#define BRIOC_SETPVID _BRIOC(5) /* bridge_setpvid_t */ +#define BRIOC_VLANENAB _BRIOC(6) /* bridge_vlanenab_t */ +#define BRIOC_FLUSHFWD _BRIOC(7) /* bridge_flushfwd_t */ +#define BRIOC_LISTFWD _BRIOC(8) /* bridge_listfwd_t */ +#define BRIOC_TABLEMAX _BRIOC(8) /* uint32_t */ + +#define BRIDGE_CTL "bridgectl" +#define BRIDGE_CTLPATH "/dev/" BRIDGE_CTL + +typedef struct bridge_newbridge_s { + datalink_id_t bnb_linkid; /* bridge link ID */ + char bnb_name[MAXNAMELEN]; /* bridge name */ +} bridge_newbridge_t; + +typedef enum bridge_state_e { + BLS_BLOCKLISTEN, /* blocking or listening state */ + BLS_LEARNING, /* learning state */ + BLS_FORWARDING /* forwarding state */ +} bridge_state_t; + +typedef struct bridge_setstate_s { + datalink_id_t bss_linkid; + bridge_state_t bss_state; +} bridge_setstate_t; + +typedef struct bridge_setpvid_s { + datalink_id_t bsv_linkid; + uint_t bsv_vlan; +} bridge_setpvid_t; + +typedef struct bridge_vlanenab_s { + datalink_id_t bve_linkid; + uint_t bve_vlan; + boolean_t bve_onoff; +} bridge_vlanenab_t; + +typedef struct bridge_flushfwd_s { + datalink_id_t bff_linkid; + boolean_t bff_exclude; +} bridge_flushfwd_t; + +typedef struct bridge_listfwd_s { + char blf_name[MAXNAMELEN]; /* bridge name */ + ether_addr_t blf_dest; + uint16_t blf_trill_nick; + uint_t blf_ms_age; + boolean_t blf_is_local; + datalink_id_t blf_linkid; +} bridge_listfwd_t; + +/* Upward control messages */ +typedef struct bridge_ctl_s { + datalink_id_t bc_linkid; + boolean_t bc_failed; /* Max SDU mismatch */ +} bridge_ctl_t; + +/* GLDv3 control ioctls used by Bridging */ +#define BRIDGE_IOC_LISTFWD BRIDGEIOC(1) + +#ifdef __cplusplus +} +#endif + +#endif /* _NET_BRIDGE_H */ diff --git a/usr/src/uts/common/net/bridge_impl.h b/usr/src/uts/common/net/bridge_impl.h new file mode 100644 index 0000000000..a25f63286a --- /dev/null +++ b/usr/src/uts/common/net/bridge_impl.h @@ -0,0 +1,259 @@ +/* + * 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 _BRIDGE_IMPL_H +#define _BRIDGE_IMPL_H + +/* + * These are the internal data structures used by the layer-two (Ethernet) + * bridging module. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <sys/list.h> +#include <sys/sysmacros.h> +#include <sys/avl.h> +#include <sys/queue.h> +#include <sys/kstat.h> +#include <sys/ksynch.h> +#include <sys/ethernet.h> +#include <sys/dld.h> +#include <sys/mac.h> +#include <sys/mac_client.h> +#include <sys/vlan.h> +#include <net/bridge.h> + +#define KSINST_NAMES "recv", "sent", "drops", \ + "forward_direct", "forward_unknown", "forward_mbcast", \ + "learn_source", "learn_moved", "learn_expire", "learn_size" +typedef struct bridge_ksinst_s { + kstat_named_t bki_recv; /* packets received */ + kstat_named_t bki_sent; /* packets sent through */ + kstat_named_t bki_drops; /* packets dropped (untowardly) */ + kstat_named_t bki_forwards; /* packets forwarded */ + kstat_named_t bki_unknown; /* packets forwarded (unknown dest) */ + kstat_named_t bki_mbcast; /* packets forwarded (multi/bcast) */ + kstat_named_t bki_source; /* source addresses learned */ + kstat_named_t bki_moved; /* source addresses moved */ + kstat_named_t bki_expire; /* source addresses expired */ + kstat_named_t bki_count; /* source addresses known */ +} bridge_ksinst_t; + +#define KSLINK_NAMES "recv", "xmit", "drops" +typedef struct bridge_kslink_s { + kstat_named_t bkl_recv; /* packets received */ + kstat_named_t bkl_xmit; /* packets transmitted */ + kstat_named_t bkl_drops; /* packets dropped */ +} bridge_kslink_t; + +/* + * There's one instance structure and one observability mac node for each + * bridge. Each open non-DLPI stream gets a 'stream' structure; these are used + * for bridge instance allocation and control. Each link on the bridge has a + * link structure. Finally, the bridge has a table of learned forwarding + * entries, each with a list of outputs, which are either links or TRILL + * nicknames. + * + * The mac structure lives as long as the dls and mac layers are busy. It can + * outlive the bridge instance and be picked up again (by name) if the instance + * is restarted. + */ + +struct bridge_mac_s; +struct bridge_stream_s; + +typedef struct bridge_inst_s { + list_node_t bi_node; + dev_t bi_dev; + uint_t bi_flags; + uint_t bi_refs; + uint32_t bi_tablemax; + uint_t bi_tshift; + krwlock_t bi_rwlock; + list_t bi_links; + kcondvar_t bi_linkwait; + avl_tree_t bi_fwd; + kstat_t *bi_ksp; + struct bridge_stream_s *bi_control; + struct bridge_mac_s *bi_mac; + void *bi_trilldata; + char bi_name[MAXLINKNAMELEN]; + bridge_ksinst_t bi_kstats; +} bridge_inst_t; + +#define BIF_SHUTDOWN 0x0001 /* control stream has closed */ + +/* + * The bridge MAC structure has the same lifetime as an observability node. + * It's created when a bridge instance is allocated, but is not freed when the + * instance is removed because there's no way for a MAC client to guarantee + * that all users have disappeared. + */ +typedef struct bridge_mac_s { + list_node_t bm_node; + mac_handle_t bm_mh; + bridge_inst_t *bm_inst; + uint_t bm_flags; /* BMF_* below */ + uint_t bm_maxsdu; + link_state_t bm_linkstate; + char bm_name[MAXLINKNAMELEN]; +} bridge_mac_t; + +#define BMF_DLS 0x0001 /* dls monitor node created */ +#define BMF_STARTED 0x0002 /* snoop-like client is present */ + +/* + * Bridge streams are used only for instance allocation and control. + */ +typedef struct bridge_stream_s { + bridge_inst_t *bs_inst; + queue_t *bs_wq; /* write-side queue for stream */ + minor_t bs_minor; + uint_t bs_taskq_cnt; /* taskq references */ +} bridge_stream_t; + +/* + * These macros are used to set and test link membership in particular VLANs. + * This membership is used to determine how to forward packets between + * interfaces. + */ + +#define BRIDGE_VLAN_ARR_SIZE \ + (P2ROUNDUP(VLAN_ID_MAX, NBBY) / NBBY) + +#define BRIDGE_VLAN_ISSET(l, v) ((l)->bl_vlans[(v) / NBBY] & \ + (1 << ((v) % NBBY))) + +#define BRIDGE_VLAN_SET(l, v) ((l)->bl_vlans[(v) / NBBY] |= \ + (1 << ((v) % NBBY))) + +#define BRIDGE_VLAN_CLR(l, v) ((l)->bl_vlans[(v) / NBBY] &= \ + ~(1 << ((v) % NBBY))) + +#define BRIDGE_AF_ISSET(l, v) ((l)->bl_afs[(v) / NBBY] & \ + (1 << ((v) % NBBY))) + +/* + * This structure represents a link attached to a bridge. VLAN membership + * information is kept here; when forwarding, we must look at the membership of + * the input link and the output to determine when to update the packet + * contents and when to discard. + */ +typedef struct bridge_link_s { + list_node_t bl_node; + uint_t bl_refs; + datalink_id_t bl_linkid; /* allocated link ID for bridge */ + bridge_state_t bl_state; /* blocking/learning/forwarding */ + uint_t bl_pvid; /* VLAN ID for untagged traffic */ + uint_t bl_flags; /* BLF_* below */ + uint_t bl_learns; /* learning limit */ + mac_handle_t bl_mh; + mac_client_handle_t bl_mch; + uint32_t bl_margin; + uint_t bl_maxsdu; + mac_unicast_handle_t bl_mah; + mac_notify_handle_t bl_mnh; + mac_promisc_handle_t bl_mphp; + bridge_inst_t *bl_inst; /* backpointer to bridge instance */ + kstat_t *bl_ksp; + void *bl_trilldata; + mblk_t *bl_lfailmp; /* preallocated */ + link_state_t bl_linkstate; + uint_t bl_trillthreads; + kcondvar_t bl_trillwait; + kmutex_t bl_trilllock; + uint8_t bl_local_mac[ETHERADDRL]; + uint8_t bl_vlans[BRIDGE_VLAN_ARR_SIZE]; + uint8_t bl_afs[BRIDGE_VLAN_ARR_SIZE]; + bridge_kslink_t bl_kstats; +} bridge_link_t; + +#define BLF_DELETED 0x0001 /* waiting for last reference to go */ +#define BLF_CLIENT_OPEN 0x0002 /* MAC client opened */ +#define BLF_MARGIN_ADDED 0x0004 /* MAC margin added */ +#define BLF_SET_BRIDGE 0x0008 /* MAC in bridging mode */ +#define BLF_PROM_ADDED 0x0010 /* MAC promiscuous added */ +#define BLF_FREED 0x0020 /* free has begun; debug assertion */ +#define BLF_TRILLACTIVE 0x0040 /* in active forwarding use */ +#define BLF_SDUFAIL 0x0080 /* has mismatched SDU */ + +/* + * This represents a learned forwarding entry. These are generally created and + * refreshed on demand as we learn about nodes through source MAC addresses we + * see. They're destroyed when they age away. For forwarding, we look up the + * destination address in an AVL tree, and the entry found tells us where the + * that source must live. + */ +typedef struct bridge_fwd_s { + avl_node_t bf_node; + uchar_t bf_dest[ETHERADDRL]; + uint16_t bf_trill_nick; /* destination nickname */ + clock_t bf_lastheard; /* time we last heard from this node */ + uint_t bf_flags; /* BFF_* below */ + uint_t bf_refs; + uint16_t bf_vlanid; /* VLAN ID for IVL */ + uint16_t bf_vcnt; /* number of duplicates */ + uint_t bf_nlinks; /* number of links in bf_links */ + uint_t bf_maxlinks; /* allocated size of link array */ + bridge_link_t **bf_links; +} bridge_fwd_t; + +#define BFF_INTREE 0x0001 +#define BFF_LOCALADDR 0x0002 /* address is known to mac layer */ +#define BFF_VLANLOCAL 0x0004 /* set if duplicate for IVL */ + +/* TRILL linkage */ +typedef void (*trill_recv_pkt_t)(void *, bridge_link_t *, mac_resource_handle_t, + mblk_t *, mac_header_info_t *); +typedef void (*trill_encap_pkt_t)(void *, bridge_link_t *, mac_header_info_t *, + mblk_t *, uint16_t); +typedef void (*trill_br_dstr_t)(void *, bridge_inst_t *); +typedef void (*trill_ln_dstr_t)(void *, bridge_link_t *); + +extern void bridge_trill_register_cb(trill_recv_pkt_t, trill_encap_pkt_t, + trill_br_dstr_t, trill_ln_dstr_t); +extern bridge_inst_t *bridge_trill_brref(const char *, void *); +extern void bridge_trill_brunref(bridge_inst_t *); +extern bridge_link_t *bridge_trill_lnref(bridge_inst_t *, datalink_id_t, + void *); +extern void bridge_trill_lnunref(bridge_link_t *); +extern void bridge_trill_decaps(bridge_link_t *, mblk_t *, uint16_t); +extern mblk_t *bridge_trill_output(bridge_link_t *, mblk_t *); +extern void bridge_trill_setvlans(bridge_link_t *, const uint8_t *); +extern void bridge_trill_flush(bridge_link_t *, uint16_t, boolean_t); + +/* Ethernet multicast address; constant stored in bridge module */ +extern const uint8_t all_isis_rbridges[]; +extern const uint8_t bridge_group_address[]; + +#ifdef __cplusplus +} +#endif + +#endif /* _BRIDGE_IMPL_H */ diff --git a/usr/src/uts/common/net/trill.h b/usr/src/uts/common/net/trill.h new file mode 100644 index 0000000000..4200557133 --- /dev/null +++ b/usr/src/uts/common/net/trill.h @@ -0,0 +1,174 @@ +/* + * 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 _NET_TRILL_H +#define _NET_TRILL_H + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/ethernet.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* Various well-known Ethernet addresses used by TRILL */ +#define ALL_RBRIDGES { 0x01, 0x80, 0xC2, 0x00, 0x02, 0x00 } +#define ALL_ISIS_RBRIDGES { 0x01, 0x80, 0xC2, 0x00, 0x02, 0x01 } +#define ALL_ESADI_RBRIDGES { 0x01, 0x80, 0xC2, 0x00, 0x02, 0x02 } + +#define TRILL_PROTOCOL_VERS 0 /* th_version */ +#define TRILL_DEFAULT_HOPS 21 /* th_hopcount */ + +/* Nickname range */ +#define RBRIDGE_NICKNAME_MIN 0x0000 +#define RBRIDGE_NICKNAME_MAX 0xFFFF + +/* Define well-known nicknames */ +#define RBRIDGE_NICKNAME_NONE RBRIDGE_NICKNAME_MIN +#define RBRIDGE_NICKNAME_MINRES 0xFFC0 +#define RBRIDGE_NICKNAME_MAXRES (RBRIDGE_NICKNAME_MAX - 1) +#define RBRIDGE_NICKNAME_UNUSED RBRIDGE_NICKNAME_MAX + +#define MIN_RBRIDGE_RANDOM_NICKNAME (RBRIDGE_NICKNAME_NONE + 1) +#define MAX_RBRIDGE_RANDOM_NICKNAME (RBRIDGE_NICKNAME_MINRES - 1) + +/* AF_TRILL IOCTL codes */ +#define TRILL_BASE (0x54524c00) /* base (TRL in hex) */ +#define TRILL_SETNICK (TRILL_BASE + 0) /* trill_node_t */ +#define TRILL_GETNICK (TRILL_BASE + 1) /* uint16_t */ +#define TRILL_ADDNICK (TRILL_BASE + 2) /* trill_node_t */ +#define TRILL_DELNICK (TRILL_BASE + 3) /* uint16_t */ +#define TRILL_DELALL (TRILL_BASE + 4) /* void */ +#define TRILL_HWADDR (TRILL_BASE + 5) /* uint8_t[ETHERADDRL] */ +#define TRILL_TREEROOT (TRILL_BASE + 6) /* uint16_t */ +#define TRILL_NEWBRIDGE (TRILL_BASE + 7) /* char[MAXLINKNAMELEN] */ +#define TRILL_VLANFWDER (TRILL_BASE + 8) /* uint8_t[TRILL_VLANS_ARRSIZE] */ +#define TRILL_DESIGVLAN (TRILL_BASE + 9) /* uint16_t */ +#define TRILL_LISTNICK (TRILL_BASE + 10) /* trill_listnick_t */ +#define TRILL_GETBRIDGE (TRILL_BASE + 11) /* char[MAXLINKNAMELEN] */ +#define TRILL_PORTFLUSH (TRILL_BASE + 12) /* uint16_t */ +#define TRILL_NICKFLUSH (TRILL_BASE + 13) /* uint16_t */ +#define TRILL_GETMTU (TRILL_BASE + 14) /* uint_t * */ + +typedef struct trill_header { +#ifdef _BIT_FIELDS_HTOL + uint8_t th_version : 2; + uint8_t th_reserved : 2; + uint8_t th_multidest : 1; + uint8_t th_optslen_hi : 3; +#else + uint8_t th_optslen_hi : 3; + uint8_t th_multidest : 1; + uint8_t th_reserved : 2; + uint8_t th_version : 2; +#endif + +#ifdef _BIT_FIELDS_HTOL + uint8_t th_optslen_lo : 2; + uint8_t th_hopcount : 6; +#else + uint8_t th_hopcount : 6; + uint8_t th_optslen_lo : 2; +#endif + uint16_t th_egressnick; + uint16_t th_ingressnick; +} trill_header_t; + +#define TRILL_HDR_ALIGN (sizeof (uint16_t)) + +#define SET_TRILL_OPTS_LEN(hdr_p, val) \ + do { \ + (hdr_p)->th_optslen_lo = (val)&0x03; \ + (hdr_p)->th_optslen_hi = (val)>>2; \ + _NOTE(CONSTANTCONDITION) \ + } while (0) + +#define GET_TRILL_OPTS_LEN(hdr_p) \ + ((hdr_p)->th_optslen_lo|((hdr_p)->th_optslen_hi<<2)) + +/* RBridge nick and tree information (*variable* size) */ +typedef struct trill_nickinfo_s { + /* Nickname of the RBridge */ + uint16_t tni_nick; + /* Next-hop SNPA address to reach this RBridge */ + ether_addr_t tni_adjsnpa; + /* Link on our system to use to reach next-hop */ + datalink_id_t tni_linkid; + /* Num of *our* adjacencies on a tree rooted at this RBridge */ + uint16_t tni_adjcount; + /* Num of distribution tree root nicks chosen by this RBridge */ + uint16_t tni_dtrootcount; + /* + * Variable size bytes to store adjacency nicks, distribution + * tree roots and VLAN filter lists. Adjacency nicks and + * distribution tree roots are 16-bit fields. + * + * Number of VLAN filter lists is equal to tni_adjcount as + * the VLAN filter list is one per adjacency in each DT. + * VLAN filter list is a 512 byte bitmap with the set of VLANs + * that are reachable downstream via the adjacency. + */ +} trill_nickinfo_t; + +typedef struct trill_listnick_s { + uint16_t tln_nick; + ether_addr_t tln_nexthop; + datalink_id_t tln_linkid; + boolean_t tln_ours; +} trill_listnick_t; + +/* Access the adjacency nick list at the end of trill_nickinfo_t */ +#define TNI_ADJNICKSPTR(v) ((uint16_t *)((trill_nickinfo_t *)(v)+1)) +#define TNI_ADJNICK(v, n) (TNI_ADJNICKSPTR(v)[(n)]) + +/* Access the DT root nick list in trill_nickinfo_t after adjacency nicks */ +#define TNI_DTROOTNICKSPTR(v) (TNI_ADJNICKSPTR(v)+(v)->tni_adjcount) +#define TNI_DTROOTNICK(v, n) (TNI_DTROOTNICKSPTR(v)[(n)]) + +/* Access the VLAN filter list in trill_nickinfo_t after DT Roots */ +#define TNI_VLANFILTERSPTR(v) (TNI_DTROOTNICKSPTR(v)+(v)->tni_dtrootcount) +#define TNI_VLANFILTERMAP(v, n) \ + (((uint8_t *)(TNI_VLANFILTERSPTR(v)))+((n)*((1<<12)/NBBY))) + +#define TNI_TOTALSIZE(v) (sizeof (trill_nickinfo_t) + \ + (sizeof (uint16_t) * (v)->tni_adjcount) + \ + (sizeof (uint16_t) * (v)->tni_dtrootcount) + \ + (((1<<12)/NBBY) * (v)->tni_adjcount)) + +/* + * This is a special value used in the sockaddr_dl "selector" field to denote + * that the packet represents a Bridging PDU. The core STP instance is not + * defined on a VLAN, so this overload is safe. All other selector values are + * used for TRILL IS-IS PDUs to indicate VLAN ID. + */ +#define TRILL_TCI_BPDU 0xFFFF + +#ifdef __cplusplus +} +#endif + +#endif /* _NET_TRILL_H */ diff --git a/usr/src/uts/common/sys/dld_impl.h b/usr/src/uts/common/sys/dld_impl.h index 68caa4f459..c244204727 100644 --- a/usr/src/uts/common/sys/dld_impl.h +++ b/usr/src/uts/common/sys/dld_impl.h @@ -217,6 +217,8 @@ struct dld_str_s { /* Protected by */ * driver private data set by the driver when calling dld_str_open(). */ void *ds_private; + + boolean_t ds_lowlink; /* SL */ }; diff --git a/usr/src/uts/common/sys/dld_ioc.h b/usr/src/uts/common/sys/dld_ioc.h index 64af1f1ab9..ead129064a 100644 --- a/usr/src/uts/common/sys/dld_ioc.h +++ b/usr/src/uts/common/sys/dld_ioc.h @@ -57,12 +57,14 @@ extern "C" { #define AGGR_IOC 0x0A66 #define VNIC_IOC 0x0171 #define SIMNET_IOC 0x5132 +#define BRIDGE_IOC 0xB81D /* GLDv3 modules use these macros to generate unique ioctl commands */ #define DLDIOC(cmdid) DLD_IOC_CMD(DLD_IOC, (cmdid)) #define AGGRIOC(cmdid) DLD_IOC_CMD(AGGR_IOC, (cmdid)) #define VNICIOC(cmdid) DLD_IOC_CMD(VNIC_IOC, (cmdid)) #define SIMNETIOC(cmdid) DLD_IOC_CMD(SIMNET_IOC, (cmdid)) +#define BRIDGEIOC(cmdid) DLD_IOC_CMD(BRIDGE_IOC, (cmdid)) #ifdef _KERNEL diff --git a/usr/src/uts/common/sys/dlpi.h b/usr/src/uts/common/sys/dlpi.h index 11293ac6d3..d717afa968 100644 --- a/usr/src/uts/common/sys/dlpi.h +++ b/usr/src/uts/common/sys/dlpi.h @@ -49,6 +49,7 @@ extern "C" { #define DLIOCNATIVE (DLIOC|2) /* Native traffic mode */ #define DLIOCMARGININFO (DLIOC|3) /* margin size info */ #define DLIOCIPNETINFO (DLIOC|4) /* ipnet header */ +#define DLIOCLOWLINK (DLIOC|5) /* low-level link up/down */ #define DLIOCHDRINFO (DLIOC|10) /* IP fast-path */ #define DL_IOC_HDR_INFO DLIOCHDRINFO diff --git a/usr/src/uts/common/sys/dls_mgmt.h b/usr/src/uts/common/sys/dls_mgmt.h index 7a042608fe..28de456053 100644 --- a/usr/src/uts/common/sys/dls_mgmt.h +++ b/usr/src/uts/common/sys/dls_mgmt.h @@ -27,7 +27,7 @@ #define _DLS_MGMT_H #include <sys/types.h> -#include <sys/dld.h> +#include <sys/param.h> /* * Data-Link Services Module @@ -43,12 +43,14 @@ typedef enum { DATALINK_CLASS_AGGR = 0x04, DATALINK_CLASS_VNIC = 0x08, DATALINK_CLASS_ETHERSTUB = 0x10, - DATALINK_CLASS_SIMNET = 0x20 + DATALINK_CLASS_SIMNET = 0x20, + DATALINK_CLASS_BRIDGE = 0x40 } datalink_class_t; #define DATALINK_CLASS_ALL (DATALINK_CLASS_PHYS | \ DATALINK_CLASS_VLAN | DATALINK_CLASS_AGGR | DATALINK_CLASS_VNIC | \ - DATALINK_CLASS_ETHERSTUB | DATALINK_CLASS_SIMNET) + DATALINK_CLASS_ETHERSTUB | DATALINK_CLASS_SIMNET | \ + DATALINK_CLASS_BRIDGE) /* * A combination of flags and media. diff --git a/usr/src/uts/common/sys/ethernet.h b/usr/src/uts/common/sys/ethernet.h index eb1b686540..d7a6600660 100644 --- a/usr/src/uts/common/sys/ethernet.h +++ b/usr/src/uts/common/sys/ethernet.h @@ -92,6 +92,7 @@ struct ether_vlan_extinfo { #define ETHERTYPE_PPPOES (0x8864) /* PPPoE Session Stage */ #define ETHERTYPE_EAPOL (0x888e) /* EAPOL protocol */ #define ETHERTYPE_RSN_PREAUTH (0x88c7) /* RSN PRE-Authentication */ +#define ETHERTYPE_TRILL (0x88c8) /* TBD. TRILL frame */ #define ETHERTYPE_FCOE (0x8906) /* FCoE */ #define ETHERTYPE_MAX (0xffff) /* Max valid ethernet type */ diff --git a/usr/src/uts/common/sys/mac.h b/usr/src/uts/common/sys/mac.h index 32b5ab7c7a..5bcf9d90f8 100644 --- a/usr/src/uts/common/sys/mac.h +++ b/usr/src/uts/common/sys/mac.h @@ -208,6 +208,9 @@ typedef enum { MAC_PROP_TAGMODE, MAC_PROP_ADV_10GFDX_CAP, MAC_PROP_EN_10GFDX_CAP, + MAC_PROP_PVID, + MAC_PROP_LLIMIT, + MAC_PROP_LDECAY, MAC_PROP_PRIVATE = -1 } mac_prop_id_t; @@ -243,7 +246,8 @@ typedef enum { enum mac_mod_stat { MAC_STAT_LINK_STATE, MAC_STAT_LINK_UP, - MAC_STAT_PROMISC + MAC_STAT_PROMISC, + MAC_STAT_LOWLINK_STATE }; /* @@ -328,6 +332,13 @@ typedef struct mac_capab_aggr_s { int (*mca_unicst)(void *, const uint8_t *); } mac_capab_aggr_t; +/* Bridge transmit and receive function signatures */ +typedef mblk_t *(*mac_bridge_tx_t)(mac_handle_t, mac_ring_handle_t, mblk_t *); +typedef void (*mac_bridge_rx_t)(mac_handle_t, mac_resource_handle_t, mblk_t *); +typedef void (*mac_bridge_ref_t)(mac_handle_t, boolean_t); +typedef link_state_t (*mac_bridge_ls_t)(mac_handle_t, link_state_t); + +/* must change mac_notify_cb_list[] in mac_provider.c if this is changed */ typedef enum { MAC_NOTE_LINK, MAC_NOTE_UNICST, @@ -337,6 +348,7 @@ typedef enum { MAC_NOTE_SDU_SIZE, MAC_NOTE_MARGIN, MAC_NOTE_CAPAB_CHG, + MAC_NOTE_LOWLINK, MAC_NNOTE /* must be the last entry */ } mac_notify_type_t; @@ -400,6 +412,7 @@ typedef struct mac_header_info_s { mac_addrtype_t mhi_dsttype; uint16_t mhi_tci; boolean_t mhi_istagged; + boolean_t mhi_ispvid; } mac_header_info_t; /* @@ -588,6 +601,7 @@ extern int mac_margin_add(mac_handle_t, uint32_t *, boolean_t); extern int mac_fastpath_disable(mac_handle_t); extern void mac_fastpath_enable(mac_handle_t); +extern void mac_no_active(mac_handle_t); extern mactype_register_t *mactype_alloc(uint_t); extern void mactype_free(mactype_register_t *); @@ -610,6 +624,20 @@ extern mac_handle_t mac_get_lower_mac_handle(mac_handle_t); extern uint64_t mac_pkt_hash(uint_t, mblk_t *, uint8_t, boolean_t); +/* + * Bridging linkage + */ +extern void mac_rx_common(mac_handle_t, + mac_resource_handle_t, mblk_t *); +extern int mac_bridge_set(mac_handle_t, mac_handle_t); +extern void mac_bridge_clear(mac_handle_t, mac_handle_t); +extern void mac_bridge_vectors(mac_bridge_tx_t, + mac_bridge_rx_t, mac_bridge_ref_t, + mac_bridge_ls_t); + +/* special case function for TRILL observability */ +extern void mac_trill_snoop(mac_handle_t, mblk_t *); + #endif /* _KERNEL */ #ifdef __cplusplus diff --git a/usr/src/uts/common/sys/mac_client.h b/usr/src/uts/common/sys/mac_client.h index c7b1aa7edc..f31357b0b9 100644 --- a/usr/src/uts/common/sys/mac_client.h +++ b/usr/src/uts/common/sys/mac_client.h @@ -167,6 +167,12 @@ extern int mac_client_set_resources(mac_client_handle_t, extern void mac_client_get_resources(mac_client_handle_t, mac_resource_props_t *); +/* bridging-related interfaces */ +extern int mac_set_pvid(mac_handle_t, uint16_t); +extern uint16_t mac_get_pvid(mac_handle_t); +extern uint32_t mac_get_llimit(mac_handle_t); +extern uint32_t mac_get_ldecay(mac_handle_t); + extern int mac_share_capable(mac_handle_t); extern int mac_share_bind(mac_client_handle_t, uint64_t, uint64_t *); extern void mac_share_unbind(mac_client_handle_t); diff --git a/usr/src/uts/common/sys/mac_impl.h b/usr/src/uts/common/sys/mac_impl.h index a93335606f..cd662948d5 100644 --- a/usr/src/uts/common/sys/mac_impl.h +++ b/usr/src/uts/common/sys/mac_impl.h @@ -29,6 +29,7 @@ #include <sys/modhash.h> #include <sys/mac_client.h> #include <sys/mac_provider.h> +#include <sys/note.h> #include <net/if.h> #include <sys/mac_flow_impl.h> #include <netinet/ip6.h> @@ -259,23 +260,49 @@ struct mac_group_s { #define MAC_DEFAULT_GROUP(mh) (((mac_impl_t *)mh)->mi_rx_groups) -#define MAC_RING_TX_DEFAULT(mip, mp) \ - ((mip->mi_default_tx_ring == NULL) ? \ - mip->mi_tx(mip->mi_driver, mp) : \ - mac_hwring_tx(mip->mi_default_tx_ring, mp)) +#define MAC_RING_TX(mhp, rh, mp, rest) { \ + mac_ring_handle_t mrh = rh; \ + mac_impl_t *mimpl = (mac_impl_t *)mhp; \ + /* \ + * Send packets through a selected tx ring, or through the \ + * default handler if there is no selected ring. \ + */ \ + if (mrh == NULL) \ + mrh = mimpl->mi_default_tx_ring; \ + if (mrh == NULL) { \ + rest = mimpl->mi_tx(mimpl->mi_driver, mp); \ + } else { \ + rest = mac_hwring_tx(mrh, mp); \ + } \ +} -#define MAC_TX(mip, ring, mp, mcip) { \ +/* + * This is the final stop before reaching the underlying driver + * or aggregation, so this is where the bridging hook is implemented. + * Packets that are bridged will return through mac_bridge_tx(), with + * rh nulled out if the bridge chooses to send output on a different + * link due to forwarding. + */ +#define MAC_TX(mip, rh, mp, share_bound) { \ + /* \ + * If there is a bound Hybrid I/O share, send packets through \ + * the default tx ring. (When there's a bound Hybrid I/O share, \ + * the tx rings of this client are mapped in the guest domain \ + * and not accessible from here.) \ + */ \ + _NOTE(CONSTANTCONDITION) \ + if (share_bound) \ + rh = NULL; \ /* \ - * If the MAC client has a bound Hybrid I/O share, \ - * send the packet through the default tx ring, since \ - * the tx rings of this client are now mapped in the \ - * guest domain and not accessible from this domain. \ + * Grab the proper transmit pointer and handle. Special \ + * optimization: we can test mi_bridge_link itself atomically, \ + * and if that indicates no bridge send packets through tx ring.\ */ \ - if ((mcip->mci_state_flags & MCIS_SHARE_BOUND) != 0 || \ - (ring == NULL)) \ - mp = MAC_RING_TX_DEFAULT(mip, mp); \ - else \ - mp = mac_hwring_tx(ring, mp); \ + if (mip->mi_bridge_link == NULL) { \ + MAC_RING_TX(mip, rh, mp, mp); \ + } else { \ + mp = mac_bridge_tx(mip, rh, mp); \ + } \ } /* mci_tx_flag */ @@ -355,7 +382,8 @@ struct mac_impl_s { uint32_t mi_ref; /* i_mac_impl_lock */ uint_t mi_active; /* SL */ link_state_t mi_linkstate; /* none */ - link_state_t mi_lastlinkstate; /* none */ + link_state_t mi_lowlinkstate; /* none */ + link_state_t mi_lastlowlinkstate; /* none */ uint_t mi_devpromisc; /* SL */ kmutex_t mi_lock; uint8_t mi_addr[MAXMACADDRLEN]; /* mi_rw_lock */ @@ -454,6 +482,7 @@ struct mac_impl_s { * applied to the MAC client when it is created. */ mac_resource_props_t mi_resource_props; /* SL */ + uint16_t mi_pvid; /* SL */ minor_t mi_minor; /* WO */ uint32_t mi_oref; /* SL */ @@ -473,6 +502,15 @@ struct mac_impl_s { */ mac_capab_share_t mi_share_capab; + /* + * Bridging hooks and limit values. Uses mutex and reference counts + * (bridging only) for data path. Limits need no synchronization. + */ + mac_handle_t mi_bridge_link; + kmutex_t mi_bridge_lock; + uint32_t mi_llimit; + uint32_t mi_ldecay; + /* This should be the last block in this structure */ #ifdef DEBUG #define MAC_PERIM_STACK_DEPTH 15 @@ -489,6 +527,8 @@ struct mac_impl_s { #define MIS_EXCLUSIVE 0x0010 #define MIS_EXCLUSIVE_HELD 0x0020 #define MIS_LEGACY 0x0040 +#define MIS_NO_ACTIVE 0x0080 +#define MIS_POLL_DISABLE 0x0100 #define mi_getstat mi_callbacks->mc_getstat #define mi_start mi_callbacks->mc_start @@ -586,6 +626,7 @@ extern int mac_group_remmac(mac_group_t *, const uint8_t *); extern int mac_rx_group_add_flow(mac_client_impl_t *, flow_entry_t *, mac_group_t *); extern mblk_t *mac_hwring_tx(mac_ring_handle_t, mblk_t *); +extern mblk_t *mac_bridge_tx(mac_impl_t *, mac_ring_handle_t, mblk_t *); extern mac_ring_t *mac_reserve_tx_ring(mac_impl_t *, mac_ring_t *); extern void mac_release_tx_ring(mac_ring_handle_t); extern mac_group_t *mac_reserve_tx_group(mac_impl_t *, mac_share_handle_t); @@ -686,6 +727,14 @@ extern void mac_rx_group_remove_client(mac_group_t *, mac_client_impl_t *) extern int i_mac_group_add_ring(mac_group_t *, mac_ring_t *, int); extern void i_mac_group_rem_ring(mac_group_t *, mac_ring_t *, boolean_t); +extern void mac_poll_state_change(mac_handle_t, boolean_t); + +/* Global callbacks into the bridging module (when loaded) */ +extern mac_bridge_tx_t mac_bridge_tx_cb; +extern mac_bridge_rx_t mac_bridge_rx_cb; +extern mac_bridge_ref_t mac_bridge_ref_cb; +extern mac_bridge_ls_t mac_bridge_ls_cb; + #ifdef __cplusplus } #endif diff --git a/usr/src/uts/common/sys/mac_provider.h b/usr/src/uts/common/sys/mac_provider.h index 9caf0db25b..2444272ef9 100644 --- a/usr/src/uts/common/sys/mac_provider.h +++ b/usr/src/uts/common/sys/mac_provider.h @@ -458,6 +458,7 @@ extern void mac_rx(mac_handle_t, mac_resource_handle_t, extern void mac_rx_ring(mac_handle_t, mac_ring_handle_t, mblk_t *, uint64_t); extern void mac_link_update(mac_handle_t, link_state_t); +extern void mac_link_redo(mac_handle_t, link_state_t); extern void mac_unicst_update(mac_handle_t, const uint8_t *); extern void mac_tx_update(mac_handle_t); diff --git a/usr/src/uts/common/sys/socket.h b/usr/src/uts/common/sys/socket.h index 4e3b2b5778..cc51ec3380 100644 --- a/usr/src/uts/common/sys/socket.h +++ b/usr/src/uts/common/sys/socket.h @@ -248,8 +248,9 @@ struct linger { #define AF_NCA 28 /* NCA socket */ #define AF_POLICY 29 /* Security Policy DB socket */ #define AF_INET_OFFLOAD 30 /* Sun private; do not use */ +#define AF_TRILL 31 /* TRILL interface */ -#define AF_MAX 30 +#define AF_MAX 31 /* * Protocol families, same as address families for now. @@ -287,6 +288,7 @@ struct linger { #define PF_NCA AF_NCA #define PF_POLICY AF_POLICY #define PF_INET_OFFLOAD AF_INET_OFFLOAD /* Sun private; do not use */ +#define PF_TRILL AF_TRILL #define PF_MAX AF_MAX diff --git a/usr/src/uts/intel/Makefile.intel.shared b/usr/src/uts/intel/Makefile.intel.shared index 8550073d37..7f70f8d3e2 100644 --- a/usr/src/uts/intel/Makefile.intel.shared +++ b/usr/src/uts/intel/Makefile.intel.shared @@ -212,6 +212,7 @@ DRV_KMODS_32 += audiovia97 DRV_KMODS += bl DRV_KMODS += bge DRV_KMODS += bofi +DRV_KMODS += bridge DRV_KMODS += bscbus DRV_KMODS += bscv DRV_KMODS += chxge @@ -333,6 +334,7 @@ DRV_KMODS += tcp6 DRV_KMODS += tl DRV_KMODS += tnf DRV_KMODS += tpm +DRV_KMODS += trill DRV_KMODS += udp DRV_KMODS += udp6 DRV_KMODS += ucode diff --git a/usr/src/uts/intel/bridge/Makefile b/usr/src/uts/intel/bridge/Makefile new file mode 100644 index 0000000000..c06180ffb4 --- /dev/null +++ b/usr/src/uts/intel/bridge/Makefile @@ -0,0 +1,89 @@ +# +# 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. +# +# This makefile drives the production of the layer-two (Ethernet) +# bridging driver module in x86/x64 systems +# + +# +# Path to the base of the uts directory tree (usually /usr/src/uts). +# +UTSBASE = ../.. + +# +# Define the module and object file sets. +# +MODULE = bridge +OBJECTS = $(BRIDGE_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(BRIDGE_OBJS:%.o=$(LINTS_DIR)/%.ln) +ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) +CONF_SRCDIR = $(UTSBASE)/common/io + +# +# Include common rules. +# +include $(UTSBASE)/intel/Makefile.intel + +# +# Define targets +# +ALL_TARGET = $(BINARY) $(SRC_CONFFILE) +LINT_TARGET = $(MODULE).lint +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE) + +# +# lint pass one enforcement +# +CFLAGS += $(CCVERBOSE) + +# +# Driver depends on MAC, DLS, and DLD +# +LDFLAGS += -dy -Nmisc/mac -Nmisc/dls -Ndrv/dld -Nfs/dev + +# +# Default build targets. +# +.KEEP_STATE: + +def: $(DEF_DEPS) + +all: $(ALL_DEPS) + +clean: $(CLEAN_DEPS) + +clobber: $(CLOBBER_DEPS) + +lint: $(LINT_DEPS) + +modlintlib: $(MODLINTLIB_DEPS) + +clean.lint: $(CLEAN_LINT_DEPS) + +install: $(INSTALL_DEPS) + +# +# Include common targets. +# +include $(UTSBASE)/intel/Makefile.targ diff --git a/usr/src/uts/intel/os/device_policy b/usr/src/uts/intel/os/device_policy index e98752f377..94e0d0de13 100644 --- a/usr/src/uts/intel/os/device_policy +++ b/usr/src/uts/intel/os/device_policy @@ -49,6 +49,7 @@ keysock read_priv_set=sys_ip_config write_priv_set=sys_ip_config ipsecah read_priv_set=sys_ip_config write_priv_set=sys_ip_config ipsecesp read_priv_set=sys_ip_config write_priv_set=sys_ip_config spdsock read_priv_set=sys_ip_config write_priv_set=sys_ip_config +bridge read_priv_set=net_rawaccess write_priv_set=net_rawaccess # # IP observability device access permission diff --git a/usr/src/uts/intel/os/minor_perm b/usr/src/uts/intel/os/minor_perm index be895e09c9..3e6df05c75 100644 --- a/usr/src/uts/intel/os/minor_perm +++ b/usr/src/uts/intel/os/minor_perm @@ -201,3 +201,4 @@ xenbus:* 0666 root sys fm:* 0644 root sys amd_iommu:* 0644 root sys xpvtap:* 0666 root sys +clone:bridge 0666 root sys diff --git a/usr/src/uts/intel/os/name_to_major b/usr/src/uts/intel/os/name_to_major index a981d77d7a..83e7825871 100644 --- a/usr/src/uts/intel/os/name_to_major +++ b/usr/src/uts/intel/os/name_to_major @@ -156,3 +156,4 @@ ipnet 261 intel_nhm 262 simnet 263 acpinex 264 +bridge 265 diff --git a/usr/src/uts/intel/trill/Makefile b/usr/src/uts/intel/trill/Makefile new file mode 100644 index 0000000000..b3c4999604 --- /dev/null +++ b/usr/src/uts/intel/trill/Makefile @@ -0,0 +1,88 @@ +# +# 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 +# +# +# uts/intel/trill/Makefile +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# This makefile drives the production of the TRILL socket kernel +# module. +# +# intel architecture dependent +# + +# +# Path to the base of the uts directory tree (usually /usr/src/uts). +# +UTSBASE = ../.. + +# +# Define the module and object file sets. +# +MODULE = trill +OBJECTS = $(TRILL_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(TRILL_OBJS:%.o=$(LINTS_DIR)/%.ln) +ROOTMODULE = $(ROOT_SOCK_DIR)/$(MODULE) + +# +# Include common rules. +# +include $(UTSBASE)/intel/Makefile.intel + +# +# Define targets +# +ALL_TARGET = $(BINARY) +LINT_TARGET = $(MODULE).lint +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) + +# +# Overrides. +# +CFLAGS += $(CCVERBOSE) +LDFLAGS += -dy -Ndrv/bridge -Nmisc/mac -Nmisc/dls -Nfs/sockfs + +# +# Default build targets. +# +.KEEP_STATE: + +def: $(DEF_DEPS) + +all: $(ALL_DEPS) + +clean: $(CLEAN_DEPS) + +clobber: $(CLOBBER_DEPS) + +lint: $(LINT_DEPS) + +modlintlib: $(MODLINTLIB_DEPS) + +clean.lint: $(CLEAN_LINT_DEPS) + +install: $(INSTALL_DEPS) + +# +# Include common targets. +# +include $(UTSBASE)/intel/Makefile.targ diff --git a/usr/src/uts/sparc/Makefile.sparc.shared b/usr/src/uts/sparc/Makefile.sparc.shared index 16a7a69a80..156718e0f0 100644 --- a/usr/src/uts/sparc/Makefile.sparc.shared +++ b/usr/src/uts/sparc/Makefile.sparc.shared @@ -226,6 +226,7 @@ DRV_KMODS += vscan DRV_KMODS += nsmb DRV_KMODS += fm DRV_KMODS += nulldriver +DRV_KMODS += bridge trill # # Don't build some of these for OpenSolaris, since they will be diff --git a/usr/src/uts/sparc/bridge/Makefile b/usr/src/uts/sparc/bridge/Makefile new file mode 100644 index 0000000000..0693e8861c --- /dev/null +++ b/usr/src/uts/sparc/bridge/Makefile @@ -0,0 +1,89 @@ +# +# 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. +# +# This makefile drives the production of the layer-two (Ethernet) +# bridging driver module in sparc systems +# + +# +# Path to the base of the uts directory tree (usually /usr/src/uts). +# +UTSBASE = ../.. + +# +# Define the module and object file sets. +# +MODULE = bridge +OBJECTS = $(BRIDGE_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(BRIDGE_OBJS:%.o=$(LINTS_DIR)/%.ln) +ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) +CONF_SRCDIR = $(UTSBASE)/common/io + +# +# Include common rules. +# +include $(UTSBASE)/sparc/Makefile.sparc + +# +# Define targets +# +ALL_TARGET = $(BINARY) $(SRC_CONFFILE) +LINT_TARGET = $(MODULE).lint +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE) + +# +# lint pass one enforcement +# +CFLAGS += $(CCVERBOSE) + +# +# Driver depends on MAC, DLS, and DLD +# +LDFLAGS += -dy -Nmisc/mac -Nmisc/dls -Ndrv/dld -Nfs/dev + +# +# Default build targets. +# +.KEEP_STATE: + +def: $(DEF_DEPS) + +all: $(ALL_DEPS) + +clean: $(CLEAN_DEPS) + +clobber: $(CLOBBER_DEPS) + +lint: $(LINT_DEPS) + +modlintlib: $(MODLINTLIB_DEPS) + +clean.lint: $(CLEAN_LINT_DEPS) + +install: $(INSTALL_DEPS) + +# +# Include common targets. +# +include $(UTSBASE)/sparc/Makefile.targ diff --git a/usr/src/uts/sparc/os/device_policy b/usr/src/uts/sparc/os/device_policy index 7ffa5988cf..5f5bea1ad5 100644 --- a/usr/src/uts/sparc/os/device_policy +++ b/usr/src/uts/sparc/os/device_policy @@ -51,6 +51,7 @@ keysock read_priv_set=sys_ip_config write_priv_set=sys_ip_config ipsecah read_priv_set=sys_ip_config write_priv_set=sys_ip_config ipsecesp read_priv_set=sys_ip_config write_priv_set=sys_ip_config spdsock read_priv_set=sys_ip_config write_priv_set=sys_ip_config +bridge read_priv_set=net_rawaccess write_priv_set=net_rawaccess # # IP observability device access permission diff --git a/usr/src/uts/sparc/os/minor_perm b/usr/src/uts/sparc/os/minor_perm index aeba76bb83..d0b59a7f0a 100644 --- a/usr/src/uts/sparc/os/minor_perm +++ b/usr/src/uts/sparc/os/minor_perm @@ -190,3 +190,4 @@ sdp:sdp 0666 root sys nsmb:* 0666 root sys bmc:bmc 0666 root sys fm:* 0644 root sys +clone:bridge 0666 root sys diff --git a/usr/src/uts/sparc/os/name_to_major b/usr/src/uts/sparc/os/name_to_major index f384fa995d..a3dca78f1f 100644 --- a/usr/src/uts/sparc/os/name_to_major +++ b/usr/src/uts/sparc/os/name_to_major @@ -228,3 +228,4 @@ nulldriver 280 ipnet 281 dcpc 282 simnet 283 +bridge 284 diff --git a/usr/src/uts/sparc/trill/Makefile b/usr/src/uts/sparc/trill/Makefile new file mode 100644 index 0000000000..5181ee76a7 --- /dev/null +++ b/usr/src/uts/sparc/trill/Makefile @@ -0,0 +1,88 @@ +# +# 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 +# +# +# uts/sparc/trill/Makefile +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# This makefile drives the production of the TRILL socket kernel +# module. +# +# sparc architecture dependent +# + +# +# Path to the base of the uts directory tree (usually /usr/src/uts). +# +UTSBASE = ../.. + +# +# Define the module and object file sets. +# +MODULE = trill +OBJECTS = $(TRILL_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(TRILL_OBJS:%.o=$(LINTS_DIR)/%.ln) +ROOTMODULE = $(ROOT_SOCK_DIR)/$(MODULE) + +# +# Include common rules. +# +include $(UTSBASE)/sparc/Makefile.sparc + +# +# Define targets +# +ALL_TARGET = $(BINARY) +LINT_TARGET = $(MODULE).lint +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) + +# +# Overrides. +# +CFLAGS += $(CCVERBOSE) +LDFLAGS += -dy -Ndrv/bridge -Nmisc/mac -Nmisc/dls -Nfs/sockfs + +# +# Default build targets. +# +.KEEP_STATE: + +def: $(DEF_DEPS) + +all: $(ALL_DEPS) + +clean: $(CLEAN_DEPS) + +clobber: $(CLOBBER_DEPS) + +lint: $(LINT_DEPS) + +modlintlib: $(MODLINTLIB_DEPS) + +clean.lint: $(CLEAN_LINT_DEPS) + +install: $(INSTALL_DEPS) + +# +# Include common targets. +# +include $(UTSBASE)/sparc/Makefile.targ |