ofproto-dpif: Do not mirror L2 multicast switch protocols to VLANs.
[sliver-openvswitch.git] / vswitchd / bridge.c
index a95a458..adc3e9f 100644 (file)
@@ -226,6 +226,11 @@ struct bridge {
 
     /* Port mirroring. */
     struct mirror *mirrors[MAX_MIRRORS];
+
+    /* Synthetic local port if necessary. */
+    struct ovsrec_port synth_local_port;
+    struct ovsrec_interface synth_local_iface;
+    struct ovsrec_interface *synth_local_ifacep;
 };
 
 /* List of all bridges. */
@@ -312,6 +317,7 @@ static void iface_update_cfm(struct iface *);
 static bool iface_refresh_cfm_stats(struct iface *iface);
 static void iface_update_carrier(struct iface *);
 static bool iface_get_carrier(const struct iface *);
+static bool iface_is_synthetic(const struct iface *);
 
 static void shash_from_ovs_idl_map(char **keys, char **values, size_t n,
                                    struct shash *);
@@ -1193,6 +1199,10 @@ iface_refresh_status(struct iface *iface)
     int64_t mtu_64;
     int error;
 
+    if (iface_is_synthetic(iface)) {
+        return;
+    }
+
     shash_init(&sh);
 
     if (!netdev_get_status(iface->netdev, &sh)) {
@@ -1314,6 +1324,10 @@ iface_refresh_stats(struct iface *iface)
 
     struct netdev_stats stats;
 
+    if (iface_is_synthetic(iface)) {
+        return;
+    }
+
     /* Intentionally ignore return value, since errors will set 'stats' to
      * all-1s, and we will deal with that correctly below. */
     netdev_get_stats(iface->netdev, &stats);
@@ -1728,10 +1742,14 @@ bridge_destroy(struct bridge *br)
     if (br) {
         struct port *port, *next;
         int error;
+        int i;
 
         HMAP_FOR_EACH_SAFE (port, next, hmap_node, &br->ports) {
             port_destroy(port);
         }
+        for (i = 0; i < MAX_MIRRORS; i++) {
+            mirror_destroy(br->mirrors[i]);
+        }
         list_remove(&br->node);
         ofproto_destroy(br->ofproto);
         error = dpif_delete(br->dpif);
@@ -1744,6 +1762,7 @@ bridge_destroy(struct bridge *br)
         hmap_destroy(&br->ifaces);
         hmap_destroy(&br->ports);
         shash_destroy(&br->iface_by_name);
+        free(br->synth_local_iface.type);
         free(br->name);
         free(br);
     }
@@ -1869,22 +1888,28 @@ bridge_reconfigure_one(struct bridge *br)
                       br->name, name);
         }
     }
+    if (!shash_find(&new_ports, br->name)) {
+        struct dpif_port dpif_port;
+        char *type;
 
-    /* If we have a controller, then we need a local port.  Complain if the
-     * user didn't specify one.
-     *
-     * XXX perhaps we should synthesize a port ourselves in this case. */
-    if (bridge_get_controllers(br, NULL)) {
-        char local_name[IF_NAMESIZE];
-        int error;
+        VLOG_WARN("bridge %s: no port named %s, synthesizing one",
+                  br->name, br->name);
 
-        error = dpif_port_get_name(br->dpif, ODPP_LOCAL,
-                                   local_name, sizeof local_name);
-        if (!error && !shash_find(&new_ports, local_name)) {
-            VLOG_WARN("bridge %s: controller specified but no local port "
-                      "(port named %s) defined",
-                      br->name, local_name);
-        }
+        dpif_port_query_by_number(br->dpif, ODPP_LOCAL, &dpif_port);
+        type = xstrdup(dpif_port.type ? dpif_port.type : "internal");
+        dpif_port_destroy(&dpif_port);
+
+        br->synth_local_port.interfaces = &br->synth_local_ifacep;
+        br->synth_local_port.n_interfaces = 1;
+        br->synth_local_port.name = br->name;
+
+        br->synth_local_iface.name = br->name;
+        free(br->synth_local_iface.type);
+        br->synth_local_iface.type = type;
+
+        br->synth_local_ifacep = &br->synth_local_iface;
+
+        shash_add(&new_ports, br->name, &br->synth_local_port);
     }
 
     /* Get rid of deleted ports.
@@ -2631,6 +2656,47 @@ port_get_an_iface(const struct port *port)
     return CONTAINER_OF(list_front(&port->ifaces), struct iface, port_elem);
 }
 
+/* Returns true if a packet with Ethernet destination MAC 'dst' may be mirrored
+ * to a VLAN.  In general most packets may be mirrored but we want to drop
+ * protocols that may confuse switches. */
+static bool
+eth_dst_may_rspan(const uint8_t dst[ETH_ADDR_LEN])
+{
+    /* If you change this function's behavior, please update corresponding
+     * documentation in vswitch.xml at the same time. */
+    if (dst[0] != 0x01) {
+        /* All the currently banned MACs happen to start with 01 currently, so
+         * this is a quick way to eliminate most of the good ones. */
+    } else {
+        if (eth_addr_is_reserved(dst)) {
+            /* Drop STP, IEEE pause frames, and other reserved protocols
+             * (01-80-c2-00-00-0x). */
+            return false;
+        }
+
+        if (dst[0] == 0x01 && dst[1] == 0x00 && dst[2] == 0x0c) {
+            /* Cisco OUI. */
+            if ((dst[3] & 0xfe) == 0xcc &&
+                (dst[4] & 0xfe) == 0xcc &&
+                (dst[5] & 0xfe) == 0xcc) {
+                /* Drop the following protocols plus others following the same
+                   pattern:
+
+                   CDP, VTP, DTP, PAgP  (01-00-0c-cc-cc-cc)
+                   Spanning Tree PVSTP+ (01-00-0c-cc-cc-cd)
+                   STP Uplink Fast      (01-00-0c-cd-cd-cd) */
+                return false;
+            }
+
+            if (!(dst[3] | dst[4] | dst[5])) {
+                /* Drop Inter Switch Link packets (01-00-0c-00-00-00). */
+                return false;
+            }
+        }
+    }
+    return true;
+}
+
 static void
 compose_dsts(const struct bridge *br, const struct flow *flow, uint16_t vlan,
              const struct port *in_port, const struct port *out_port,
@@ -2673,7 +2739,7 @@ compose_dsts(const struct bridge *br, const struct flow *flow, uint16_t vlan,
                     && !dst_is_duplicate(set, &dst)) {
                     dst_set_add(set, &dst);
                 }
-            } else {
+            } else if (eth_dst_may_rspan(flow->dl_dst)) {
                 struct port *port;
 
                 HMAP_FOR_EACH (port, hmap_node, &br->ports) {
@@ -4512,7 +4578,7 @@ iface_set_mac(struct iface *iface)
 static void
 iface_set_ofport(const struct ovsrec_interface *if_cfg, int64_t ofport)
 {
-    if (if_cfg) {
+    if (if_cfg && !ovsdb_idl_row_is_synthetic(&if_cfg->header_)) {
         ovsrec_interface_set_ofport(if_cfg, &ofport, 1);
     }
 }
@@ -4607,7 +4673,7 @@ iface_update_carrier(struct iface *iface)
 static void
 iface_update_qos(struct iface *iface, const struct ovsrec_qos *qos)
 {
-    if (!qos || qos->type[0] == '\0') {
+    if (!qos || qos->type[0] == '\0' || qos->n_queues < 1) {
         netdev_set_qos(iface->netdev, NULL, NULL);
     } else {
         struct iface_delete_queues_cbdata cbdata;
@@ -4687,6 +4753,14 @@ iface_get_carrier(const struct iface *iface)
             ? netdev_get_carrier(iface->netdev)
             : netdev_get_miimon(iface->netdev));
 }
+
+/* Returns true if 'iface' is synthetic, that is, if we constructed it locally
+ * instead of obtaining it from the database. */
+static bool
+iface_is_synthetic(const struct iface *iface)
+{
+    return ovsdb_idl_row_is_synthetic(&iface->cfg->header_);
+}
 \f
 /* Port mirroring. */