summaryrefslogtreecommitdiff
path: root/usr
diff options
context:
space:
mode:
Diffstat (limited to 'usr')
-rw-r--r--usr/src/Makefile.lint2
-rw-r--r--usr/src/cmd/cmd-inet/etc/sock2path3
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/Makefile6
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/bridged/Makefile60
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/bridged/bridge.xml109
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/bridged/dlpi.c245
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/bridged/door.c216
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/bridged/events.c639
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/bridged/global.h108
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/bridged/main.c251
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/bridged/rstp.c564
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/Makefile11
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.h3
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_bpdu.c95
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ether.c205
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_isis.c99
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_trill.c117
-rw-r--r--usr/src/cmd/dladm/Makefile8
-rw-r--r--usr/src/cmd/dladm/dladm.c1545
-rw-r--r--usr/src/cmd/dladm/dladm.xcl150
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/genunix.c2
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/net.c424
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/net.h6
-rw-r--r--usr/src/cmd/rcm_daemon/Makefile.com10
-rw-r--r--usr/src/cmd/rcm_daemon/common/bridge_rcm.c898
-rw-r--r--usr/src/cmd/svc/milestone/net-nwam3
-rw-r--r--usr/src/cmd/svc/milestone/net-physical3
-rw-r--r--usr/src/cmd/svc/shell/net_include.sh55
-rw-r--r--usr/src/cmd/truss/codes.c1
-rw-r--r--usr/src/cmd/truss/systable.c5
-rw-r--r--usr/src/lib/Makefile2
-rw-r--r--usr/src/lib/libdladm/Makefile7
-rw-r--r--usr/src/lib/libdladm/Makefile.com2
-rw-r--r--usr/src/lib/libdladm/common/libdladm.c9
-rw-r--r--usr/src/lib/libdladm/common/libdladm.h8
-rw-r--r--usr/src/lib/libdladm/common/libdladm_impl.h7
-rw-r--r--usr/src/lib/libdladm/common/libdlbridge.c1577
-rw-r--r--usr/src/lib/libdladm/common/libdlbridge.h137
-rw-r--r--usr/src/lib/libdladm/common/libdllink.h9
-rw-r--r--usr/src/lib/libdladm/common/libdlvnic.c49
-rw-r--r--usr/src/lib/libdladm/common/linkprop.c508
-rw-r--r--usr/src/lib/libdladm/common/llib-ldladm1
-rw-r--r--usr/src/lib/libdladm/common/mapfile-vers24
-rw-r--r--usr/src/lib/libdladm/common/usage.c1
-rw-r--r--usr/src/lib/libdladm/libdladm.xcl21
-rw-r--r--usr/src/lib/librstp/Makefile60
-rw-r--r--usr/src/lib/librstp/Makefile.com53
-rw-r--r--usr/src/lib/librstp/THIRDPARTYLICENSE.descrip1
-rw-r--r--usr/src/lib/librstp/common/COPYING504
-rw-r--r--usr/src/lib/librstp/common/ChangeLog62
-rw-r--r--usr/src/lib/librstp/common/README37
-rw-r--r--usr/src/lib/librstp/common/README.CVS.HOWTO22
-rw-r--r--usr/src/lib/librstp/common/README.authors3
-rw-r--r--usr/src/lib/librstp/common/README.files58
-rw-r--r--usr/src/lib/librstp/common/README.news19
-rw-r--r--usr/src/lib/librstp/common/TODO22
-rw-r--r--usr/src/lib/librstp/common/base.h198
-rw-r--r--usr/src/lib/librstp/common/choose.h42
-rw-r--r--usr/src/lib/librstp/common/edge.c115
-rw-r--r--usr/src/lib/librstp/common/edge.h38
-rw-r--r--usr/src/lib/librstp/common/llib-lrstp30
-rw-r--r--usr/src/lib/librstp/common/mapfile-vers71
-rw-r--r--usr/src/lib/librstp/common/migrate.c119
-rw-r--r--usr/src/lib/librstp/common/migrate.h37
-rw-r--r--usr/src/lib/librstp/common/p2p.c90
-rw-r--r--usr/src/lib/librstp/common/p2p.h37
-rw-r--r--usr/src/lib/librstp/common/pcost.c132
-rw-r--r--usr/src/lib/librstp/common/pcost.h37
-rw-r--r--usr/src/lib/librstp/common/port.c253
-rw-r--r--usr/src/lib/librstp/common/port.h185
-rw-r--r--usr/src/lib/librstp/common/portinfo.c509
-rw-r--r--usr/src/lib/librstp/common/portinfo.h40
-rw-r--r--usr/src/lib/librstp/common/rolesel.c400
-rw-r--r--usr/src/lib/librstp/common/rolesel.h41
-rw-r--r--usr/src/lib/librstp/common/roletrns.c431
-rw-r--r--usr/src/lib/librstp/common/roletrns.h37
-rw-r--r--usr/src/lib/librstp/common/statmch.c123
-rw-r--r--usr/src/lib/librstp/common/statmch.h89
-rw-r--r--usr/src/lib/librstp/common/stp_bpdu.h90
-rw-r--r--usr/src/lib/librstp/common/stp_in.c1026
-rw-r--r--usr/src/lib/librstp/common/stp_in.h227
-rw-r--r--usr/src/lib/librstp/common/stp_to.h48
-rw-r--r--usr/src/lib/librstp/common/stp_vectors.h90
-rw-r--r--usr/src/lib/librstp/common/stpm.c360
-rw-r--r--usr/src/lib/librstp/common/stpm.h126
-rw-r--r--usr/src/lib/librstp/common/stpmgmt.c161
-rw-r--r--usr/src/lib/librstp/common/sttrans.c140
-rw-r--r--usr/src/lib/librstp/common/sttrans.h38
-rw-r--r--usr/src/lib/librstp/common/times.c80
-rw-r--r--usr/src/lib/librstp/common/times.h50
-rw-r--r--usr/src/lib/librstp/common/topoch.c227
-rw-r--r--usr/src/lib/librstp/common/topoch.h37
-rw-r--r--usr/src/lib/librstp/common/transmit.c368
-rw-r--r--usr/src/lib/librstp/common/transmit.h38
-rw-r--r--usr/src/lib/librstp/common/uid_stp.h202
-rw-r--r--usr/src/lib/librstp/common/vector.c183
-rw-r--r--usr/src/lib/librstp/common/vector.h79
-rw-r--r--usr/src/lib/librstp/i386/Makefile29
-rw-r--r--usr/src/lib/librstp/sparc/Makefile29
-rw-r--r--usr/src/pkgdefs/Makefile3
-rw-r--r--usr/src/pkgdefs/SUNWbridger/Makefile35
-rw-r--r--usr/src/pkgdefs/SUNWbridger/pkginfo.tmpl57
-rw-r--r--usr/src/pkgdefs/SUNWbridger/prototype_com50
-rw-r--r--usr/src/pkgdefs/SUNWbridger/prototype_i38647
-rw-r--r--usr/src/pkgdefs/SUNWbridger/prototype_sparc47
-rw-r--r--usr/src/pkgdefs/SUNWbridgeu/Makefile38
-rw-r--r--usr/src/pkgdefs/SUNWbridgeu/pkginfo.tmpl57
-rw-r--r--usr/src/pkgdefs/SUNWbridgeu/prototype_com50
-rw-r--r--usr/src/pkgdefs/SUNWbridgeu/prototype_i38647
-rw-r--r--usr/src/pkgdefs/SUNWbridgeu/prototype_sparc47
-rw-r--r--usr/src/pkgdefs/SUNWckr/prototype_com1
-rw-r--r--usr/src/pkgdefs/SUNWckr/prototype_i3864
-rw-r--r--usr/src/pkgdefs/SUNWckr/prototype_sparc2
-rw-r--r--usr/src/pkgdefs/SUNWcnetr/postinstall8
-rw-r--r--usr/src/pkgdefs/SUNWdladmint/Makefile37
-rw-r--r--usr/src/pkgdefs/SUNWdladmint/pkginfo.tmpl53
-rw-r--r--usr/src/pkgdefs/SUNWdladmint/prototype_com61
-rw-r--r--usr/src/pkgdefs/SUNWdladmint/prototype_i38650
-rw-r--r--usr/src/pkgdefs/SUNWdladmint/prototype_sparc50
-rw-r--r--usr/src/pkgdefs/SUNWhea/prototype_com2
-rw-r--r--usr/src/pkgdefs/common_files/i.devpolicy2
-rw-r--r--usr/src/pkgdefs/common_files/i.minorperm_i3861
-rw-r--r--usr/src/pkgdefs/common_files/i.minorperm_sparc1
-rw-r--r--usr/src/pkgdefs/common_files/i.sock2path5
-rw-r--r--usr/src/pkgdefs/etc/exception_list_i38640
-rw-r--r--usr/src/pkgdefs/etc/exception_list_sparc50
-rw-r--r--usr/src/tools/opensolaris/license-list1
-rw-r--r--usr/src/uts/common/Makefile.files4
-rw-r--r--usr/src/uts/common/io/bridge.c3527
-rw-r--r--usr/src/uts/common/io/bridge.conf27
-rw-r--r--usr/src/uts/common/io/dld/dld_drv.c3
-rw-r--r--usr/src/uts/common/io/dld/dld_str.c57
-rw-r--r--usr/src/uts/common/io/dls/dls_link.c17
-rw-r--r--usr/src/uts/common/io/mac/mac.c205
-rw-r--r--usr/src/uts/common/io/mac/mac_bcast.c8
-rw-r--r--usr/src/uts/common/io/mac/mac_client.c81
-rw-r--r--usr/src/uts/common/io/mac/mac_datapath_setup.c29
-rw-r--r--usr/src/uts/common/io/mac/mac_provider.c144
-rw-r--r--usr/src/uts/common/io/mac/mac_sched.c8
-rw-r--r--usr/src/uts/common/io/trill.c1746
-rw-r--r--usr/src/uts/common/io/trill_impl.h145
-rw-r--r--usr/src/uts/common/net/Makefile2
-rw-r--r--usr/src/uts/common/net/bridge.h117
-rw-r--r--usr/src/uts/common/net/bridge_impl.h259
-rw-r--r--usr/src/uts/common/net/trill.h174
-rw-r--r--usr/src/uts/common/sys/dld_impl.h2
-rw-r--r--usr/src/uts/common/sys/dld_ioc.h2
-rw-r--r--usr/src/uts/common/sys/dlpi.h1
-rw-r--r--usr/src/uts/common/sys/dls_mgmt.h8
-rw-r--r--usr/src/uts/common/sys/ethernet.h1
-rw-r--r--usr/src/uts/common/sys/mac.h30
-rw-r--r--usr/src/uts/common/sys/mac_client.h6
-rw-r--r--usr/src/uts/common/sys/mac_impl.h79
-rw-r--r--usr/src/uts/common/sys/mac_provider.h1
-rw-r--r--usr/src/uts/common/sys/socket.h4
-rw-r--r--usr/src/uts/intel/Makefile.intel.shared2
-rw-r--r--usr/src/uts/intel/bridge/Makefile89
-rw-r--r--usr/src/uts/intel/os/device_policy1
-rw-r--r--usr/src/uts/intel/os/minor_perm1
-rw-r--r--usr/src/uts/intel/os/name_to_major1
-rw-r--r--usr/src/uts/intel/trill/Makefile88
-rw-r--r--usr/src/uts/sparc/Makefile.sparc.shared1
-rw-r--r--usr/src/uts/sparc/bridge/Makefile89
-rw-r--r--usr/src/uts/sparc/os/device_policy1
-rw-r--r--usr/src/uts/sparc/os/minor_perm1
-rw-r--r--usr/src/uts/sparc/os/name_to_major1
-rw-r--r--usr/src/uts/sparc/trill/Makefile88
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, &ether_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