bridge.c revision 4249d844b6c24a328b579c3d5ebc44595a61e046
/*
* 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
* 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
*/
#include <sys/sysmacros.h>
#include <sys/mac_ether.h>
#include <sys/mac_provider.h>
#include <sys/mac_client_priv.h>
#include <sys/mac_impl.h>
#include <net/bridge_impl.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().
* 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.
*/
static const char *inst_kstats_list[] = { KSINST_NAMES };
static const char *link_kstats_list[] = { KSLINK_NAMES };
#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
* device nodes as well as client streams. Minor node 0 is reserved for the
* allocation control node.
*/
static krwlock_t bmac_rwlock;
/* 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 *);
/* 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 */
};
/* GLDv3 control ioctls used by Bridging */
static dld_ioc_info_t bridge_ioc_list[] = {
};
/*
* 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 *
{
return (bip);
}
static void
{
if (failed) {
return;
} else {
return;
}
/*
* 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.
*/
break;
}
}
}
/*
* 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) {
} else {
}
/* get the current mblk we're going to send up */
return;
/* get a new one for next time */
/* if none for next time, then report only failures */
return;
}
/* LINTED: alignment */
}
/*
* Send control messages (link SDU changes) using the stream to the
* bridge instance daemon.
*/
static void
{
}
}
/* ARGSUSED */
static int
{
return (ENOTSUP);
}
static int
bridge_m_start(void *arg)
{
return (0);
}
static void
bridge_m_stop(void *arg)
{
}
/* ARGSUSED */
static int
{
return (0);
}
/* ARGSUSED */
static int
{
return (0);
}
/* ARGSUSED */
static int
{
return (ENOTSUP);
}
static mblk_t *
{
return (NULL);
}
/* ARGSUSED */
static int
{
return (ENOENT);
else
} else {
blf->blf_ms_age =
blf->blf_is_local =
}
return (0);
}
static int
{
int err;
switch (pr_num) {
case MAC_PROP_MTU:
break;
}
err = 0;
} else {
continue;
}
err = 0;
}
break;
default:
break;
}
return (err);
}
static int
{
int err = 0;
switch (pr_num) {
case MAC_PROP_MTU: {
if (!(pr_flags & MAC_PROP_POSSIBLE))
return (ENOTSUP);
if (pr_valsize < sizeof (mac_propval_range_t))
return (EINVAL);
*perm = MAC_PROP_PERM_RW;
break;
}
case MAC_PROP_STATUS:
} else {
sizeof (&bmp->bm_linkstate));
}
break;
default:
break;
}
return (err);
}
static mac_callbacks_t bridge_m_callbacks = {
NULL, /* ioctl */
NULL, /* getcapab */
NULL, /* open */
NULL, /* close */
};
/*
* Create kstats from a list.
*/
static kstat_t *
const char *unitname)
{
int i;
for (i = 0; i < nstat; i++)
}
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
{
int err;
return (EINVAL);
return (0);
}
}
/*
* 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.
*/
if (err != 0) {
return (err);
}
if (list_is_empty(&bmac_list)) {
}
/*
* Mark the MAC as unable to go "active" so that only passive clients
* (such as snoop) can bind to it.
*/
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
{
}
/* This is used by the avl trees to sort forwarding table entries */
static int
{
if (diff != 0)
return (1);
return (-1);
}
return (0);
}
static void
{
}
static bridge_inst_t *
inst_alloc(const char *bridge)
{
return (bip);
}
static bridge_inst_t *
bridge_find_name(const char *bridge)
{
break;
}
}
return (bip);
}
static int
{
int err;
break;
}
/* This should not take long; if it does, we've got a design problem */
goto lookup_retry;
}
/* We weren't expecting to find anything */
} else {
}
goto fail;
goto fail_create;
/*
* bm_inst is set, so the timer cannot yank the DLS rug from under us.
* No extra locking is needed here.
*/
goto fail_create;
}
return (0);
fail:
return (err);
}
static void
{
/* free up mac for reuse before leaving global list */
}
}
/*
* 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)
{
return (NULL);
return (bsp);
}
static void
{
}
static void
{
bsp->bs_taskq_cnt++;
}
static void
{
if (--bsp->bs_taskq_cnt == 0)
}
static void
{
/* Don't unreference the bridge until the MAC is closed */
}
static void
{
}
}
static bridge_fwd_t *
{
}
return (bfp);
}
static bridge_fwd_t *
{
}
}
return (bfp);
}
static void
{
uint_t i;
}
static void
{
}
}
static void
{
/* Another thread could beat us to this */
}
} else {
}
}
}
static boolean_t
{
} else {
}
return (retv);
}
static void
{
return;
goto no_old_addr;
/*
* Find the previous entry, and remove our link from it.
*/
int i;
/*
* See if we're in the list, and remove if so.
*/
/*
* We assume writes are atomic, so no special
* MT handling is needed. The list length is
* decremented first, and then we remove
* entries.
*/
break;
}
}
/* If no more links, then remove and free up */
} else {
}
}
/*
* 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.)
*/
goto no_new_addr;
/* special case: link fits in existing entry */
} else {
/* reset the idx value due to removal above */
}
}
if (drop_ref)
else
/* local addresses are not subject to table limits */
}
}
/*
* 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.
*/
/* Account for removed entry. */
if (drop_ref)
}
static void
{
}
/*
* 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)
{
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.
*/
}
/* Tell the clients the real link state when we leave */
/* Destroy all of the forwarding entries related to this link */
break;
}
continue;
/* note that this can't be the last reference */
} else {
}
}
}
/*
* We are now completely removed from the active list, so drop the
* reference (see bridge_add_link).
*/
}
static void
{
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.
*/
}
}
}
/*
* This bridge is being destroyed. Notify TRILL once all of the
* links are all gone.
*/
}
/*
* This is called once by the TRILL module when it starts up. It just sets the
* 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
{
#ifdef DEBUG
}
}
}
#endif
}
/*
* 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.
*/
{
char bridge[MAXLINKNAMELEN];
}
return (bip);
}
void
{
}
/*
* 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).
*/
{
break;
}
}
return (blp);
}
void
{
while (blp->bl_trillthreads > 0)
}
/*
* 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)
{
int err;
continue;
/* compute scaled maximum age based on table limit */
else
age_limit = 1;
}
}
}
else
}
}
}
/*
* 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.
*/
/* ignore active bridges */
continue;
if (err == 0)
}
if (err == 0) {
}
}
}
if (list_is_empty(&bmac_list)) {
bridge_timerid = 0;
} else {
}
}
static int
{
return (0);
return (EINVAL);
/*
* Check the minor node number being opened. This tells us which
* bridge instance the user wants.
*/
/*
* This is a regular DLPI stream for snoop or the like.
* Redirect it through DLD.
*/
} else {
/*
* Allocate the bridge control stream structure.
*/
return (ENOSR);
return (0);
}
}
/*
* This is used only for bridge control streams. DLPI goes through dld
* instead.
*/
static int
{
/*
* stream to leave the system.
*/
while (bsp->bs_taskq_cnt != 0)
return (0);
}
static void
{
int i;
/* 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 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.
*/
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.
*/
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.)
*/
}
return;
}
return;
}
/*
* 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.
*/
/* save the count of IVL duplicates */
/* entry deletes count as learning events */
/* destroy and create anew; node moved */
} else {
}
}
else if (!replaced)
}
/*
* 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 *
{
struct ether_vlan_header *evh;
int pri;
/* This helps centralize error handling in the caller. */
return (mp);
/* No forwarded packet can have hardware checksum enabled */
DB_CKSUMFLAGS(mp) = 0;
/* Get the no-modification cases out of the way first */
return (mp);
return (mp);
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.
*/
return (NULL);
}
}
if (!source_has_tag)
/*
* We're willing to copy some data to avoid fragmentation, but
* not a lot.
*/
if (minlen > 256)
minlen = sizeof (struct ether_vlan_header);
return (NULL);
}
/* We toss the first mblk when we can. */
} else {
/* If not, then just copy what we need */
if (!source_has_tag)
minlen = sizeof (struct ether_header);
}
}
/* LINTED: pointer alignment */
if (source_has_tag) {
if (mlen > sizeof (struct ether_vlan_header))
sizeof (struct ether_vlan_header),
mlen - sizeof (struct ether_vlan_header));
} else { /* 2 */
}
} else {
/* case 4: no header present, but one is needed */
if (mlen > sizeof (struct ether_header))
mlen - sizeof (struct ether_header));
}
return (mp);
}
/* Record VLAN information and strip header if requested . */
static void
{
struct ether_vlan_header *evhp;
/* LINTED: alignment */
if (striphdr) {
/*
* For VLAN tagged frames update the ether_type
* in hdr_info before stripping the header.
*/
}
} else {
if (striphdr)
}
}
/*
* Return B_TRUE if we're allowed to send on this link with the given VLAN ID.
*/
static boolean_t
{
return (B_FALSE);
return (B_FALSE);
}
/*
* 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 *
{
uint_t i;
void *tdp;
/*
* 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 (from_trill) {
}
return (mp);
}
/*
* 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.
*/
/*
* Error case: can't reencapsulate if the protocols are
* working correctly.
*/
if (from_trill) {
return (NULL);
}
blp->bl_trillthreads++;
if (is_xmit)
/* all trill data frames have Inner.VLAN */
return (NULL);
}
bfp->bf_trill_nick);
if (--blp->bl_trillthreads == 0 &&
}
/* if TRILL has been disabled, then kill this stray */
}
return (NULL);
}
/* find first link we can send on */
break;
}
break;
}
} else {
}
if (!from_trill && is_xmit)
continue;
}
/*
* No need to bump up the link reference count, as
* the forwarding entry itself holds a reference to
* the link.
*/
} else {
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.
*/
}
} 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.
*/
blp->bl_trillthreads++;
/*
* all trill data frames have
* Inner.VLAN
*/
} else {
}
}
if (--blp->bl_trillthreads == 0 &&
}
}
/*
* This is an unknown destination, so flood.
*/
break;
}
break;
}
} else {
}
if (!from_trill && is_xmit)
continue;
}
else
}
}
/*
* 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
{
/*
* 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.
*/
break;
}
return (B_FALSE);
do {
return (B_FALSE);
tpos = 0;
}
return (B_FALSE);
goto input_no_vlan;
return (B_FALSE);
} else {
tci = 0xFFFF;
/*
* If PVID is set to zero, then untagged traffic is not
* supported here. Do not learn or forward.
*/
return (B_FALSE);
}
return (B_TRUE);
}
/*
* Handle MAC notifications.
*/
static void
{
switch (note_type) {
case MAC_NOTE_UNICST:
break;
case MAC_NOTE_SDU_SIZE: {
}
else if (notify)
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
{
/*
* Regardless of state, check for inbound TRILL packets when TRILL is
* active. These are pulled out of band and sent for TRILL handling.
*/
void *tdp;
blp->bl_trillthreads++;
/*
* If the header isn't readable, then leave on
* the list and continue.
*/
&hdr_info) != 0) {
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.
*/
if (hdr_info.mhi_dsttype ==
all_isis_rbridges, ETHERADDRL) == 0)
0)
}
if (!raw_isis && !bridge_group &&
/* LINTED: alignment */
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.)
*/
/*
* 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) {
msglen);
} else if (msglen <
continue;
}
}
}
if (--blp->bl_trillthreads == 0 &&
}
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) {
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.
*/
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.
*/
}
/*
* 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.
*/
/*
* 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.
*/
continue;
}
/*
* Extract and validate the VLAN ID for this packet.
*/
continue;
}
if (trillmode) {
/*
* Special test required by TRILL document: must
* discard frames with outer address set to ESADI.
*/
ETHERADDRL) == 0) {
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.
*/
}
/*
* First process the source address and learn from it. For
* TRILL, we learn only if we're the appointed forwarder.
*/
vlanid);
/*
* Now check whether we're forwarding and look up the
* destination. If we can forward, do so.
*/
}
}
}
/* ARGSUSED */
static mblk_t *
{
/*
* 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.
*/
(trillmode &&
return (mp);
}
/*
* Send a copy of the message up to the observability node users.
* TRILL needs to check on a packet-by-packet basis.
*/
}
continue;
}
/*
* Extract and validate the VLAN ID for this packet.
*/
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.
*/
}
/*
* 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.
*/
vlanid);
/* attempt forwarding */
}
}
}
/*
* 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.)
*/
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
{
return;
}
/* Extract VLAN ID for this packet. */
struct ether_vlan_header *evhp;
/* LINTED: alignment */
} else {
/* Inner VLAN headers are required in TRILL data packets */
return;
}
/* Learn the location of this sender in the RBridge network */
/* attempt forwarding */
/* Deliver a copy locally as well */
}
} else {
}
}
}
/*
* 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 *
{
}
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
{
int i;
for (i = 0; i < BRIDGE_VLAN_ARR_SIZE; i++) {
}
}
void
{
int i;
continue;
if (dotrill) {
/* port doesn't matter if we're flushing TRILL */
continue;
} else {
continue;
break;
}
continue;
}
}
}
}
/*
* 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
{
if (hold)
else
}
/*
* 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
{
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.
*/
break;
}
/*
* 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.
*/
/*
* 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.
*/
}
}
return (newls);
}
static void
bridge_add_link(void *arg)
{
int err;
const mac_info_t *mip;
char linkname[MAXLINKNAMELEN];
char kstatname[KSTAT_STRLEN];
int i;
/* LINTED: alignment */
/*
* 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.
*/
break;
}
break;
}
goto fail;
}
goto fail;
/* we bridge only Ethernet */
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.
*/
}
/* figure the kstat name; also used as the mac client name */
if (i < 0 || i >= MAXLINKNAMELEN)
i = MAXLINKNAMELEN - 1;
linkname[i] = '\0';
linkname);
goto fail;
}
goto fail;
}
if (err != 0)
goto fail;
if (err != 0)
goto fail;
if (err != 0)
goto fail;
if (err != 0)
goto fail;
/*
* 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.
*/
/*
* If the new link is no good on this bridge, then let the daemon know
* about the problem.
*/
/*
* 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.
*/
/*
* We now need to report back to the stream that invoked us, and then
* drop the reference on the stream that we're holding.
*/
return;
fail:
if (macopen)
} else {
}
}
static void
bridge_rem_link(void *arg)
{
/* LINTED: alignment */
/*
* We become reader here so that we can loop over the other links and
*/
break;
}
}
/*
* Check if this link is up and the remainder of the links are all
* down.
*/
break;
}
}
}
}
/*
* Check if there's just one working link left on the bridge. If so,
* then that link is now authoritative for bridge MTU.
*/
else
break;
}
}
}
if (found)
else
}
/*
* This function intentionally returns with bi_rwlock held; it is intended for
* quick checks and updates.
*/
static bridge_link_t *
{
break;
}
return (blp);
}
static void
{
int len = 0;
/* LINTED: alignment */
/*
* For now, all of the bridge ioctls are privileged.
*/
return;
}
case BRIOC_NEWBRIDGE: {
break;
/* LINTED: alignment */
break;
} else {
rc = 0;
}
break;
}
case BRIOC_ADDLINK:
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.
*/
return;
case BRIOC_REMLINK:
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.
*/
return;
case BRIOC_SETSTATE: {
break;
/* LINTED: alignment */
} else {
rc = 0;
}
break;
}
case BRIOC_SETPVID: {
break;
/* LINTED: alignment */
break;
rc = 0;
} else {
rc = 0;
}
break;
}
case BRIOC_VLANENAB: {
break;
/* LINTED: alignment */
break;
} else {
rc = 0;
/* special case: vlan 0 means "all" */
BRIDGE_VLAN_CLR(blp, 0);
} else {
}
}
break;
}
case BRIOC_FLUSHFWD: {
int i;
break;
/* LINTED: alignment */
/* This case means "all" */
} else {
break;
}
break;
}
}
continue;
for (i = 0; i < bfp->bf_maxlinks; i++) {
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.
*/
continue;
}
}
}
break;
}
case BRIOC_TABLEMAX:
break;
/* LINTED: alignment */
break;
}
if (rc == 0)
else
}
static void
{
case M_IOCTL:
break;
case M_FLUSH:
else
break;
default:
break;
}
}
/*
* This function allocates the main data structures for the bridge driver and
* connects us into devfs.
*/
static void
bridge_inst_init(void)
{
}
/*
* 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)
{
if (bridge_timerid != 0)
(void) untimeout(bridge_timerid);
}
/*
* bridge_attach()
*
* Description:
* Attach bridge driver to the system.
*/
static int
{
if (cmd != DDI_ATTACH)
return (DDI_FAILURE);
CLONE_DEV) == DDI_FAILURE) {
return (DDI_FAILURE);
}
DLDIOCCNT(bridge_ioc_list)) != 0) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* bridge_detach()
*
* Description:
* Detach an interface to the system.
*/
static int
{
if (cmd != DDI_DETACH)
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
/*
* bridge_info()
*
* Description:
* Translate "dev_t" to a pointer to the associated "dev_info_t".
*/
/* ARGSUSED */
static int
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:
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 */
"bridging driver",
};
static struct modlinkage modlinkage = {
(void *)&modldrv,
};
int
_init(void)
{
int retv;
return (retv);
}
int
_fini(void)
{
int retv;
if (retv == 0 &&
return (retv);
}
int
{
}