cfm: Remove Maintenance_Point and Monitor tables.
authorEthan Jackson <ethan@nicira.com>
Thu, 12 May 2011 22:28:43 +0000 (15:28 -0700)
committerEthan Jackson <ethan@nicira.com>
Fri, 20 May 2011 22:53:29 +0000 (15:53 -0700)
In an effort to make CFM easier to understand and configure, this
patch removes the Maintenance_Point and Monitor tables from the
database.  As a consequence, users will only be able to configure
one remote maintenance point.  Furthermore, before this patch each
remote maintenance point maintained its own separate fault flag in
the database.  This flag is no longer reported, users will need to
infer the fault status from the global CFM fault flag.

ChangeLog
lib/cfm.c
lib/cfm.h
ofproto/ofproto.c
ofproto/ofproto.h
utilities/ovs-vsctl.8.in
utilities/ovs-vsctl.c
vswitchd/bridge.c
vswitchd/vswitch.gv
vswitchd/vswitch.ovsschema
vswitchd/vswitch.xml

index bd0c40d..3c76a78 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -5,6 +5,8 @@ post v1.1.0
     - Feature removals:
       - Dropped support for "tun_id_from_cookie" OpenFlow extension.
        (Use the extensible match extensions instead.)
+      - Removed the Maintenance_Point and Monitor tables in an effort
+        to simplify 802.1ag configuration.
 
 v1.1.0 - 05 Apr 2011
 ------------------------
index e0a5121..05c2382 100644 (file)
--- a/lib/cfm.c
+++ b/lib/cfm.c
@@ -49,6 +49,18 @@ struct cfm_internal {
 
     struct timer tx_timer;    /* Send CCM when expired. */
     struct timer fault_timer; /* Check for faults when expired. */
+
+    struct hmap remote_mps; /* Expected remote MPs. */
+};
+
+/* Remote MPs represent foreign network entities that are configured to have
+ * the same MAID as this CFM instance. */
+struct remote_mp {
+    uint16_t mpid;         /* The Maintenance Point ID of this 'remote_mp'. */
+    struct hmap_node node; /* Node in 'remote_mps' map. */
+
+    bool recv;           /* CCM was received since last fault check. */
+    bool fault;          /* Indicates a connectivity fault. */
 };
 
 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
@@ -180,7 +192,7 @@ cfm_create(void)
     cfmi = xzalloc(sizeof *cfmi);
     cfm  = &cfmi->cfm;
 
-    hmap_init(&cfm->remote_mps);
+    hmap_init(&cfmi->remote_mps);
     cfm_generate_maid(cfmi);
     list_push_back(&all_cfms, &cfmi->list_node);
     return cfm;
@@ -196,12 +208,12 @@ cfm_destroy(struct cfm *cfm)
         return;
     }
 
-    HMAP_FOR_EACH_SAFE (rmp, rmp_next, node, &cfm->remote_mps) {
-        hmap_remove(&cfm->remote_mps, &rmp->node);
+    HMAP_FOR_EACH_SAFE (rmp, rmp_next, node, &cfmi->remote_mps) {
+        hmap_remove(&cfmi->remote_mps, &rmp->node);
         free(rmp);
     }
 
-    hmap_destroy(&cfm->remote_mps);
+    hmap_destroy(&cfmi->remote_mps);
     list_remove(&cfmi->list_node);
     free(cfmi);
 }
@@ -217,7 +229,7 @@ cfm_run(struct cfm *cfm)
         struct remote_mp *rmp;
 
         cfm->fault = false;
-        HMAP_FOR_EACH (rmp, node, &cfm->remote_mps) {
+        HMAP_FOR_EACH (rmp, node, &cfmi->remote_mps) {
             rmp->fault = !rmp->recv;
             rmp->recv = false;
 
@@ -303,6 +315,7 @@ cfm_configure(struct cfm *cfm)
 void
 cfm_update_remote_mps(struct cfm *cfm, const uint16_t *mpids, size_t n_mpids)
 {
+    struct cfm_internal *cfmi = cfm_to_internal(cfm);
     size_t i;
     struct hmap new_rmps;
     struct remote_mp *rmp, *rmp_next;
@@ -317,8 +330,8 @@ cfm_update_remote_mps(struct cfm *cfm, const uint16_t *mpids, size_t n_mpids)
             continue;
         }
 
-        if ((rmp = lookup_remote_mp(&cfm->remote_mps, mpid))) {
-            hmap_remove(&cfm->remote_mps, &rmp->node);
+        if ((rmp = lookup_remote_mp(&cfmi->remote_mps, mpid))) {
+            hmap_remove(&cfmi->remote_mps, &rmp->node);
         } else {
             rmp = xzalloc(sizeof *rmp);
             rmp->mpid = mpid;
@@ -327,7 +340,7 @@ cfm_update_remote_mps(struct cfm *cfm, const uint16_t *mpids, size_t n_mpids)
         hmap_insert(&new_rmps, &rmp->node, hash_mpid(mpid));
     }
 
-    hmap_swap(&new_rmps, &cfm->remote_mps);
+    hmap_swap(&new_rmps, &cfmi->remote_mps);
 
     HMAP_FOR_EACH_SAFE (rmp, rmp_next, node, &new_rmps) {
         hmap_remove(&new_rmps, &rmp->node);
@@ -337,14 +350,6 @@ cfm_update_remote_mps(struct cfm *cfm, const uint16_t *mpids, size_t n_mpids)
     hmap_destroy(&new_rmps);
 }
 
-/* Finds a 'remote_mp' with 'mpid' in 'cfm'.  If no such 'remote_mp' exists
- * returns NULL. */
-const struct remote_mp *
-cfm_get_remote_mp(const struct cfm *cfm, uint16_t mpid)
-{
-    return lookup_remote_mp(&cfm->remote_mps, mpid);
-}
-
 /* Returns true if the CFM library should process packets from 'flow'. */
 bool
 cfm_should_process_flow(const struct flow *flow)
@@ -396,7 +401,7 @@ cfm_process_heartbeat(struct cfm *cfm, const struct ofpbuf *p)
         ccm_mpid = ntohs(ccm->mpid);
         ccm_interval = ccm->flags & 0x7;
 
-        rmp = lookup_remote_mp(&cfm->remote_mps, ccm_mpid);
+        rmp = lookup_remote_mp(&cfmi->remote_mps, ccm_mpid);
 
         if (rmp) {
             rmp->recv = true;
@@ -453,7 +458,7 @@ cfm_unixctl_show(struct unixctl_conn *conn,
                   timer_msecs_until_expired(&cfmi->fault_timer));
 
     ds_put_cstr(&ds, "\n");
-    HMAP_FOR_EACH (rmp, node, &cfmi->cfm.remote_mps) {
+    HMAP_FOR_EACH (rmp, node, &cfmi->remote_mps) {
         ds_put_format(&ds, "Remote MPID %"PRIu16": %s\n", rmp->mpid,
                       rmp->fault ? "fault" : "");
         ds_put_format(&ds, "\trecv since check: %s",
index 60e234e..5472cda 100644 (file)
--- a/lib/cfm.h
+++ b/lib/cfm.h
@@ -57,20 +57,9 @@ struct cfm {
     const char *name;           /* Name of this CFM object. */
 
     /* Statistics. */
-    struct hmap remote_mps;     /* Expected remote MPs. */
     bool fault;                 /* Indicates connectivity vaults. */
 };
 
-/* Remote MPs represent foreign network entities that are configured to have
- * the same MAID as this CFM instance. */
-struct remote_mp {
-    uint16_t mpid;         /* The Maintenance Point ID of this 'remote_mp'. */
-    struct hmap_node node; /* In 'cfm' 'remote_mps' or 'x_remote_mps'. */
-
-    bool recv;           /* CCM was received since last fault check. */
-    bool fault;          /* Indicates a connectivity fault. */
-};
-
 void cfm_init(void);
 
 struct cfm *cfm_create(void);
@@ -89,8 +78,6 @@ bool cfm_configure(struct cfm *);
 
 void cfm_update_remote_mps(struct cfm *, const uint16_t *mpid, size_t n_mpids);
 
-const struct remote_mp *cfm_get_remote_mp(const struct cfm *, uint16_t mpid);
-
 bool cfm_should_process_flow(const struct flow *);
 
 void cfm_process_heartbeat(struct cfm *, const struct ofpbuf *packet);
index be0fccc..6ba5561 100644 (file)
@@ -437,15 +437,14 @@ ofproto_port_clear_cfm(struct ofproto *ofproto, uint16_t ofp_port)
 }
 
 /* Configures connectivity fault management on 'ofp_port' in 'ofproto'.  Takes
- * basic configuration from the configuration members in 'cfm', and the set of
- * remote maintenance points from the 'n_remote_mps' elements in 'remote_mps'.
- * Ignores the statistics members of 'cfm'.
+ * basic configuration from the configuration members in 'cfm', and the remote
+ * maintenance point ID from  remote_mpid.  Ignores the statistics members of
+ * 'cfm'.
  *
  * This function has no effect if 'ofproto' does not have a port 'ofp_port'. */
 void
 ofproto_port_set_cfm(struct ofproto *ofproto, uint16_t ofp_port,
-                     const struct cfm *cfm,
-                     const uint16_t *remote_mps, size_t n_remote_mps)
+                     const struct cfm *cfm, uint16_t remote_mpid)
 {
     struct ofport *ofport;
     int error;
@@ -457,9 +456,11 @@ ofproto_port_set_cfm(struct ofproto *ofproto, uint16_t ofp_port,
         return;
     }
 
+    /* XXX: For configuration simplicity, we only support one remote_mpid
+     * outside of the CFM module.  It's not clear if this is the correct long
+     * term solution or not. */
     error = (ofproto->ofproto_class->set_cfm
-             ? ofproto->ofproto_class->set_cfm(ofport, cfm,
-                                               remote_mps, n_remote_mps)
+             ? ofproto->ofproto_class->set_cfm(ofport, cfm, &remote_mpid, 1)
              : EOPNOTSUPP);
     if (error) {
         VLOG_WARN("%s: CFM configuration on port %"PRIu16" (%s) failed (%s)",
index ea9f98b..125e977 100644 (file)
@@ -171,8 +171,7 @@ void ofproto_port_unregister(struct ofproto *, uint16_t ofp_port);
 
 void ofproto_port_clear_cfm(struct ofproto *, uint16_t ofp_port);
 void ofproto_port_set_cfm(struct ofproto *, uint16_t ofp_port,
-                          const struct cfm *,
-                          const uint16_t *remote_mps, size_t n_remote_mps);
+                          const struct cfm *, uint16_t remote_mpid);
 const struct cfm *ofproto_port_get_cfm(struct ofproto *, uint16_t ofp_port);
 int ofproto_port_is_lacp_current(struct ofproto *, uint16_t ofp_port);
 
index fcfec02..97944df 100644 (file)
@@ -483,11 +483,6 @@ identified by port name.
 .IP "\fBQueue\fR"
 Configuration for one queue within a \fBQoS\fR configuration.  Records
 may only be identified by UUID.
-.IP "\fBMonitor\fR"
-Connectivity Monitoring attached to an \fBInterface\fR.  Records may be
-identified by \fBInterface\fR name.
-.IP "\fBMaintenance_Point\fR"
-Maintenance Point managed by a \fBMonitor\fR.
 .IP "\fBMirror\fR"
 A port mirroring configuration attached to a bridge.  Records may be
 identified by mirror name.
@@ -783,19 +778,13 @@ their UUIDs, then "\fBovs\-vsctl destroy Queue \fIuuid1\fR
 \fIuuid2\fR" to destroy each of them.)
 .SS "Connectivity Monitoring"
 .PP
-Create a Monitor which manages a couple of remote Maintenance Points on eth0.
+Monitor connectivity to a remote maintenance point on eth0.
 .IP
-.B "ovs\-vsctl \-\- set Interface eth0 Monitor=@newmon \(rs"
-.IP
-.B "\-\- \-\-id=@newmon create Monitor mpid=1 remote_mps=@mp2,@mp3 \(rs"
-.IP
-.B "\-\- \-\-id=@mp2 create Maintenance_Point mpid=2 \(rs"
-.IP
-.B "\-\- \-\-id=@mp3 create Maintenance_Point mpid=3"
+.B "ovs\-vsctl set Interface eth0 cfm_mpid=1 cfm_remote_mpid=2"
 .PP
-Deconfigure the Monitor record from above:
+Deconfigure connectivity monitoring from above:
 .IP
-.B "ovs\-vsctl clear Interface eth0 Monitor"
+.B "ovs\-vsctl clear Interface eth0 cfm_mpid cfm_remote_mpid"
 .SS "NetFlow"
 .PP
 Configure bridge \fBbr0\fR to send NetFlow records to UDP port 5566 on
index 780d242..664b9da 100644 (file)
@@ -2115,16 +2115,6 @@ static const struct vsctl_table_class tables[] = {
      {{&ovsrec_table_port, &ovsrec_port_col_name, &ovsrec_port_col_qos},
       {NULL, NULL, NULL}}},
 
-    {&ovsrec_table_monitor,
-     {{&ovsrec_table_interface,
-       &ovsrec_interface_col_name,
-       &ovsrec_interface_col_monitor},
-      {NULL, NULL, NULL}}},
-
-    {&ovsrec_table_maintenance_point,
-     {{NULL, NULL, NULL},
-      {NULL, NULL, NULL}}},
-
     {&ovsrec_table_queue,
      {{NULL, NULL, NULL},
       {NULL, NULL, NULL}}},
index c7b0262..4f20e80 100644 (file)
@@ -244,10 +244,6 @@ bridge_init(const char *remote)
     ovsdb_idl_omit_alert(idl, &ovsrec_controller_col_status);
     ovsdb_idl_omit(idl, &ovsrec_controller_col_external_ids);
 
-    ovsdb_idl_omit_alert(idl, &ovsrec_maintenance_point_col_fault);
-
-    ovsdb_idl_omit_alert(idl, &ovsrec_monitor_col_fault);
-
     ovsdb_idl_omit(idl, &ovsrec_qos_col_external_ids);
 
     ovsdb_idl_omit(idl, &ovsrec_queue_col_external_ids);
@@ -1219,33 +1215,18 @@ iface_refresh_status(struct iface *iface)
 static bool
 iface_refresh_cfm_stats(struct iface *iface)
 {
-    const struct ovsrec_monitor *mon;
+    const struct ovsrec_interface *cfg = iface->cfg;
     const struct cfm *cfm;
     bool changed = false;
-    size_t i;
 
-    mon = iface->cfg->monitor;
     cfm = ofproto_port_get_cfm(iface->port->bridge->ofproto, iface->ofp_port);
 
-    if (!cfm || !mon) {
+    if (!cfm) {
         return false;
     }
 
-    for (i = 0; i < mon->n_remote_mps; i++) {
-        const struct ovsrec_maintenance_point *mp;
-        const struct remote_mp *rmp;
-
-        mp = mon->remote_mps[i];
-        rmp = cfm_get_remote_mp(cfm, mp->mpid);
-
-        if (mp->n_fault != 1 || mp->fault[0] != rmp->fault) {
-            ovsrec_maintenance_point_set_fault(mp, &rmp->fault, 1);
-            changed = true;
-        }
-    }
-
-    if (mon->n_fault != 1 || mon->fault[0] != cfm->fault) {
-        ovsrec_monitor_set_fault(mon, &cfm->fault, 1);
+    if (cfg->n_cfm_fault != 1 || cfg->cfm_fault[0] != cfm->fault) {
+        ovsrec_interface_set_cfm_fault(cfg, &cfm->fault, 1);
         changed = true;
     }
 
@@ -2501,30 +2482,25 @@ iface_configure_qos(struct iface *iface, const struct ovsrec_qos *qos)
 static void
 iface_configure_cfm(struct iface *iface)
 {
-    size_t i;
+    const struct ovsrec_interface *cfg = iface->cfg;
     struct cfm cfm;
-    uint16_t *remote_mps;
-    struct ovsrec_monitor *mon;
-
-    mon = iface->cfg->monitor;
 
-    if (!mon) {
+    if (!cfg->n_cfm_mpid || !cfg->n_cfm_remote_mpid) {
         ofproto_port_clear_cfm(iface->port->bridge->ofproto, iface->ofp_port);
         return;
     }
 
-    cfm.mpid     = mon->mpid;
-    cfm.interval = mon->interval ? *mon->interval : 1000;
+    cfm.mpid = *cfg->cfm_mpid;
     cfm.name = iface->name;
+    cfm.interval = atoi(get_interface_other_config(iface->cfg,
+                                                   "cfm_interval", "0"));
 
-    remote_mps = xzalloc(mon->n_remote_mps * sizeof *remote_mps);
-    for(i = 0; i < mon->n_remote_mps; i++) {
-        remote_mps[i] = mon->remote_mps[i]->mpid;
+    if (cfm.interval <= 0) {
+        cfm.interval = 1000;
     }
 
     ofproto_port_set_cfm(iface->port->bridge->ofproto, iface->ofp_port,
-                         &cfm, remote_mps, mon->n_remote_mps);
-    free(remote_mps);
+                         &cfm, *cfg->cfm_remote_mpid);
 }
 
 /* Read carrier or miimon status directly from 'iface''s netdev, according to
index b5c2b56..35d1bac 100644 (file)
@@ -11,8 +11,6 @@ digraph Open_vSwitch {
        Bridge -> NetFlow [label="netflow"];
        QoS [style=bold];
        QoS -> Queue [label="queues value"];
-       Monitor [];
-       Monitor -> Maintenance_Point [label="remote_mps"];
        sFlow [];
        Open_vSwitch [style=bold];
        Open_vSwitch -> Bridge [label="bridges"];
@@ -29,9 +27,7 @@ digraph Open_vSwitch {
        Mirror -> Port [style=dotted, constraint=false, label="output_port"];
        Mirror -> Port [style=dotted, constraint=false, label="select_dst_port"];
        Interface [];
-       Interface -> Monitor [label="monitor"];
        NetFlow [];
-       Maintenance_Point [];
        Port [];
        Port -> QoS [label="qos"];
        Port -> Interface [label="interfaces"];
index 0622512..4ad5c38 100644 (file)
@@ -1,6 +1,6 @@
 {"name": "Open_vSwitch",
- "version": "3.5.0",
- "cksum": "1684955806 14964",
+ "version": "4.0.0",
+ "cksum": "2606080158 14328",
  "tables": {
    "Open_vSwitch": {
      "columns": {
        "ofport": {
          "type": {"key": "integer", "min": 0, "max": 1},
          "ephemeral": true},
-       "monitor": {
+       "cfm_mpid": {
          "type": {
-           "key": {"type": "uuid", "refTable": "Monitor"},
+           "key": {"type": "integer", "minInteger": 1, "maxInteger": 8191},
+           "min": 0,
+           "max": 1}},
+       "cfm_remote_mpid": {
+         "type" : {
+           "key": { "type": "integer", "minInteger": 1, "maxInteger": 8191},
+           "min": 0,
+           "max": 1}},
+       "cfm_fault": {
+         "type": {
+           "key": { "type": "boolean"},
            "min": 0,
            "max": 1}},
        "lacp_current": {
        "mtu": {
          "type": {"key": "integer", "min": 0, "max": 1},
          "ephemeral": true}}},
-   "Monitor": {
-     "columns": {
-       "mpid": {
-         "type" : {
-           "key": { "type": "integer", "minInteger": 1, "maxInteger": 8191}}},
-       "interval": {
-         "type": {
-           "key": { "type": "integer", "minInteger": 100},
-           "min": 0,
-           "max": 1}},
-       "remote_mps": {
-         "type": {
-           "key": { "type": "uuid", "refTable": "Maintenance_Point"},
-           "min": 0,
-           "max": "unlimited"}},
-       "fault": {
-         "type": {
-           "key": { "type": "boolean"},
-           "min": 0,
-           "max": 1},
-         "ephemeral": true}}},
-   "Maintenance_Point": {
-     "columns": {
-       "mpid": {
-         "type" : {
-           "key": { "type": "integer", "minInteger": 1, "maxInteger": 8191}},
-         "mutable": false},
-       "fault": {
-         "type": {
-           "key": { "type": "boolean"},
-           "min": 0,
-           "max": 1},
-         "ephemeral": true}}},
    "QoS": {
      "columns": {
        "type": {
index f9a82cc..2148646 100644 (file)
       </column>
     </group>
 
-    <group title="Other Features">
+    <group title="Connectivity Fault Management">
+      <p>
+        802.1ag Connectivity Fault Management (CFM) allows a group of
+        Maintenance Points (MPs) called a Maintenance Association (MA) to
+        detect connectivity problems with each other.  MPs within a MA should
+        have complete and exclusive interconnectivity.  This is verified by
+        occasionally broadcasting Continuity Check Messages (CCMs) at a
+        configurable transmission interval.
+      </p>
+
+      <column name="cfm_mpid">
+        A Maintenance Point ID (MPID) uniquely identifies each endpoint within
+        a Maintenance Association.  The MPID is used to identify this endpoint
+        to other Maintenance Points in the MA.  Each end of a link being
+        monitored should have a different MPID.  Must be configured to enable
+        CFM on this <ref table="Interface"/>.
+      </column>
 
-      <column name="monitor">
-        Connectivity monitor configuration for this interface.
+      <column name="cfm_remote_mpid">
+        The MPID of the remote endpoint being monitored.  If this
+        <ref table="Interface"/> does not have connectivity to an endpoint
+        advertising the configured MPID, a fault is signalled.  Must be
+        configured to enable CFM on this <ref table="Interface"/>
       </column>
 
+      <column name="cfm_fault">
+        Indicates a connectivity fault triggered by an inability to receive
+        heartbeats from the remote endpoint.
+      </column>
+    </group>
+
+    <group title="Other Features">
+
       <column name="lacp_current">
         Boolean value indicating LACP status for this interface.  If true, this
         interface has current LACP information about its LACP partner.  This
       <column name="other_config">
         Key-value pairs for rarely used interface features.
         <dl>
+          <dt><code>cfm_interval</code></dt>
+          <dd> The transmission interval of CFM heartbeats in milliseconds.
+            Three missed heartbeat receptions indicate a connectivity fault.
+            Defaults to 1000ms. </dd>
           <dt><code>bond-stable-id</code></dt>
           <dd> A positive integer using in <code>stable</code> bond mode to
             make slave selection decisions.  Allocating
     </column>
   </table>
 
-  <table name="Monitor" title="Connectivity Monitor configuration">
-    <p>
-      A <ref table="Monitor"/> attaches to an <ref table="Interface"/> to
-      implement 802.1ag Connectivity Fault Management (CFM).  CFM allows a
-      group of Maintenance Points (MPs) called a Maintenance Association (MA)
-      to detect connectivity problems with each other.  MPs within a MA should
-      have complete and exclusive interconnectivity.  This is verified by
-      occasionally broadcasting Continuity Check Messages (CCMs) at a
-      configurable transmission interval.  A <ref table="Monitor"/> is
-      responsible for collecting data about other MPs in its MA and
-      broadcasting CCMs.
-    </p>
-
-    <group title="Monitor Configuration">
-      <column name="mpid">
-        A Maintenance Point ID (MPID) uniquely identifies each endpoint within
-        a Maintenance Association.  The MPID is used to identify this
-        <ref table="Monitor"/> to other endpoints in the MA.
-      </column>
-
-      <column name="remote_mps">
-        A set of <ref table="Maintenance_Points"/> which this
-        <ref table="Monitor"/> should have connectivity to.  If this
-        <ref table="Monitor"/> does not have connectivity to any MPs in this
-        set, or has connectivity to any MPs not in this set, a fault is
-        signaled.
-      </column>
-
-      <column name="interval">
-        The transmission interval of CCMs in milliseconds.  Three missed CCMs
-        indicate a connectivity fault.  Defaults to 1000ms.
-      </column>
-    </group>
-
-    <group title="Monitor Status">
-      <column name="fault">
-        Indicates a Connectivity Fault caused by a configuration error, a down
-        remote MP, or unexpected connectivity to a remote MAID or remote MP.
-      </column>
-    </group>
-  </table>
-
-  <table name="Maintenance_Point" title="Maintenance Point configuration">
-    <p>
-      A <ref table="Maintenance_Point"/> represents a MP which a
-      <ref table="Monitor"/> has or should have connectivity to.
-    </p>
-
-    <group title="Maintenance_Point Configuration">
-      <column name="mpid">
-        A Maintenance Point ID (MPID) uniquely identifies each endpoint within
-        a Maintenance Association. All MPs within a MA should have a unique
-        MPID.
-      </column>
-    </group>
-
-    <group title="Maintenance_Point Status">
-      <column name="fault">
-        Indicates a connectivity fault.
-      </column>
-    </group>
-  </table>
-
   <table name="Mirror" title="Port mirroring (SPAN/RSPAN).">
     <p>A port mirror within a <ref table="Bridge"/>.</p>
     <p>A port mirror configures a bridge to send selected frames to special