bridge: Fix VLAN selection mirroring logic.
[sliver-openvswitch.git] / vswitchd / bridge.c
index 115c498..8b82de8 100644 (file)
@@ -147,6 +147,8 @@ struct port {
      * A bridge port for bonding has at least 2 interfaces. */
     struct list ifaces;         /* List of "struct iface"s. */
 
+    struct lacp *lacp;          /* NULL if LACP is not enabled. */
+
     /* Bonding info. */
     struct bond *bond;
 
@@ -196,10 +198,11 @@ static struct ovsdb_idl *idl;
 #define STATS_INTERVAL (5 * 1000) /* In milliseconds. */
 static long long int stats_timer = LLONG_MIN;
 
-/* Stores the time after which CFM statistics may be written to the database.
- * Only updated when changes to the database require rate limiting. */
-#define CFM_LIMIT_INTERVAL (1 * 1000) /* In milliseconds. */
-static long long int cfm_limiter = LLONG_MIN;
+/* Stores the time after which rate limited statistics may be written to the
+ * database.  Only updated when changes to the database require rate limiting.
+ */
+#define DB_LIMIT_INTERVAL (1 * 1000) /* In milliseconds. */
+static long long int db_limiter = LLONG_MIN;
 
 static struct bridge *bridge_create(const struct ovsrec_bridge *br_cfg);
 static void bridge_destroy(struct bridge *);
@@ -238,6 +241,7 @@ static struct port *port_lookup(const struct bridge *, const char *name);
 static struct iface *port_get_an_iface(const struct port *);
 static struct port *port_from_dp_ifidx(const struct bridge *,
                                        uint16_t dp_ifidx);
+static void port_reconfigure_lacp(struct port *);
 static void port_reconfigure_bond(struct port *);
 static void port_send_learning_packets(struct port *);
 
@@ -283,16 +287,54 @@ bridge_init(const char *remote)
     ovsdb_idl_omit_alert(idl, &ovsrec_open_vswitch_col_cur_cfg);
     ovsdb_idl_omit_alert(idl, &ovsrec_open_vswitch_col_statistics);
     ovsdb_idl_omit(idl, &ovsrec_open_vswitch_col_external_ids);
+    ovsdb_idl_omit(idl, &ovsrec_open_vswitch_col_ovs_version);
+    ovsdb_idl_omit(idl, &ovsrec_open_vswitch_col_db_version);
+    ovsdb_idl_omit(idl, &ovsrec_open_vswitch_col_system_type);
+    ovsdb_idl_omit(idl, &ovsrec_open_vswitch_col_system_version);
 
+    ovsdb_idl_omit_alert(idl, &ovsrec_bridge_col_datapath_id);
     ovsdb_idl_omit(idl, &ovsrec_bridge_col_external_ids);
 
     ovsdb_idl_omit(idl, &ovsrec_port_col_external_ids);
     ovsdb_idl_omit(idl, &ovsrec_port_col_fake_bridge);
 
+    ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_admin_state);
+    ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_duplex);
+    ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_link_speed);
+    ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_link_state);
+    ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_mtu);
     ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_ofport);
     ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_statistics);
+    ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_status);
     ovsdb_idl_omit(idl, &ovsrec_interface_col_external_ids);
 
+    ovsdb_idl_omit_alert(idl, &ovsrec_controller_col_is_connected);
+    ovsdb_idl_omit_alert(idl, &ovsrec_controller_col_role);
+    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);
+
+    ovsdb_idl_omit(idl, &ovsrec_mirror_col_external_ids);
+
+    ovsdb_idl_omit(idl, &ovsrec_netflow_col_external_ids);
+
+    ovsdb_idl_omit(idl, &ovsrec_sflow_col_external_ids);
+
+    ovsdb_idl_omit(idl, &ovsrec_manager_col_external_ids);
+    ovsdb_idl_omit(idl, &ovsrec_manager_col_inactivity_probe);
+    ovsdb_idl_omit(idl, &ovsrec_manager_col_is_connected);
+    ovsdb_idl_omit(idl, &ovsrec_manager_col_max_backoff);
+    ovsdb_idl_omit(idl, &ovsrec_manager_col_status);
+
+    ovsdb_idl_omit(idl, &ovsrec_ssl_col_external_ids);
+
     /* Register unixctl commands. */
     unixctl_command_register("fdb/show", bridge_unixctl_fdb_show, NULL);
     unixctl_command_register("cfm/show", cfm_unixctl_show, NULL);
@@ -301,6 +343,7 @@ bridge_init(const char *remote)
                              NULL);
     unixctl_command_register("bridge/reconnect", bridge_unixctl_reconnect,
                              NULL);
+    lacp_init();
     bond_init();
 }
 
@@ -401,9 +444,7 @@ set_iface_properties(struct bridge *br OVS_UNUSED, struct iface *iface,
 
     /* Set MAC address of internal interfaces other than the local
      * interface. */
-    if (iface->dp_ifidx != ODPP_LOCAL && !strcmp(iface->type, "internal")) {
-        iface_set_mac(iface);
-    }
+    iface_set_mac(iface);
 
     return true;
 }
@@ -844,6 +885,7 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
         HMAP_FOR_EACH (port, hmap_node, &br->ports) {
             struct iface *iface;
 
+            port_reconfigure_lacp(port);
             port_reconfigure_bond(port);
 
             LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
@@ -855,8 +897,13 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
         iterate_and_prune_ifaces(br, set_iface_properties, NULL);
     }
 
+    /* Some reconfiguration operations require the bridge to have been run at
+     * least once.  */
     LIST_FOR_EACH (br, node, &all_bridges) {
         struct iface *iface;
+
+        bridge_run_one(br);
+
         HMAP_FOR_EACH (iface, dp_ifidx_node, &br->ifaces) {
             iface_update_cfm(iface);
         }
@@ -1184,6 +1231,27 @@ iface_refresh_cfm_stats(struct iface *iface)
     return changed;
 }
 
+static bool
+iface_refresh_lacp_stats(struct iface *iface)
+{
+    bool *db_current = iface->cfg->lacp_current;
+    bool changed = false;
+
+    if (iface->port->lacp) {
+        bool current = lacp_slave_is_current(iface->port->lacp, iface);
+
+        if (!db_current || *db_current != current) {
+            changed = true;
+            ovsrec_interface_set_lacp_current(iface->cfg, &current, 1);
+        }
+    } else if (db_current) {
+        changed = true;
+        ovsrec_interface_set_lacp_current(iface->cfg, NULL, 0);
+    }
+
+    return changed;
+}
+
 static void
 iface_refresh_stats(struct iface *iface)
 {
@@ -1372,7 +1440,7 @@ bridge_run(void)
         stats_timer = time_msec() + STATS_INTERVAL;
     }
 
-    if (time_msec() >= cfm_limiter) {
+    if (time_msec() >= db_limiter) {
         struct ovsdb_idl_txn *txn;
         bool changed = false;
 
@@ -1385,12 +1453,13 @@ bridge_run(void)
 
                 LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
                     changed = iface_refresh_cfm_stats(iface) || changed;
+                    changed = iface_refresh_lacp_stats(iface) || changed;
                 }
             }
         }
 
         if (changed) {
-            cfm_limiter = time_msec() + CFM_LIMIT_INTERVAL;
+            db_limiter = time_msec() + DB_LIMIT_INTERVAL;
         }
 
         ovsdb_idl_txn_commit(txn);
@@ -1415,8 +1484,8 @@ bridge_wait(void)
     ovsdb_idl_wait(idl);
     poll_timer_wait_until(stats_timer);
 
-    if (cfm_limiter > time_msec()) {
-        poll_timer_wait_until(cfm_limiter);
+    if (db_limiter > time_msec()) {
+        poll_timer_wait_until(db_limiter);
     }
 }
 
@@ -2098,51 +2167,6 @@ set_dst(struct dst *dst, const struct flow *flow,
     return dst->iface != NULL;
 }
 
-static void
-swap_dst(struct dst *p, struct dst *q)
-{
-    struct dst tmp = *p;
-    *p = *q;
-    *q = tmp;
-}
-
-/* Moves all the dsts with vlan == 'vlan' to the front of the 'n_dsts' in
- * 'dsts'.  (This may help performance by reducing the number of VLAN changes
- * that we push to the datapath.  We could in fact fully sort the array by
- * vlan, but in most cases there are at most two different vlan tags so that's
- * possibly overkill.) */
-static void
-partition_dsts(struct dst_set *set, int vlan)
-{
-    struct dst *first = set->dsts;
-    struct dst *last = set->dsts + set->n;
-
-    while (first != last) {
-        /* Invariants:
-         *      - All dsts < first have vlan == 'vlan'.
-         *      - All dsts >= last have vlan != 'vlan'.
-         *      - first < last. */
-        while (first->vlan == vlan) {
-            if (++first == last) {
-                return;
-            }
-        }
-
-        /* Same invariants, plus one additional:
-         *      - first->vlan != vlan.
-         */
-        while (last[-1].vlan != vlan) {
-            if (--last == first) {
-                return;
-            }
-        }
-
-        /* Same invariants, plus one additional:
-         *      - last[-1].vlan == vlan.*/
-        swap_dst(first++, --last);
-    }
-}
-
 static int
 mirror_mask_ffs(mirror_mask_t mask)
 {
@@ -2237,14 +2261,7 @@ compose_dsts(const struct bridge *br, const struct flow *flow, uint16_t vlan,
              const struct port *in_port, const struct port *out_port,
              struct dst_set *set, tag_type *tags, uint16_t *nf_output_iface)
 {
-    mirror_mask_t mirrors = in_port->src_mirrors;
     struct dst dst;
-    int flow_vlan;
-
-    flow_vlan = vlan_tci_to_vid(flow->vlan_tci);
-    if (flow_vlan == 0) {
-        flow_vlan = OFP_VLAN_NONE;
-    }
 
     if (out_port == FLOOD_PORT) {
         struct port *port;
@@ -2255,7 +2272,6 @@ compose_dsts(const struct bridge *br, const struct flow *flow, uint16_t vlan,
                 && port_includes_vlan(port, vlan)
                 && !port->is_mirror_output_port
                 && set_dst(&dst, flow, in_port, port, tags)) {
-                mirrors |= port->dst_mirrors;
                 dst_set_add(set, &dst);
             }
         }
@@ -2263,12 +2279,37 @@ compose_dsts(const struct bridge *br, const struct flow *flow, uint16_t vlan,
     } else if (out_port && set_dst(&dst, flow, in_port, out_port, tags)) {
         dst_set_add(set, &dst);
         *nf_output_iface = dst.iface->dp_ifidx;
-        mirrors |= out_port->dst_mirrors;
+    }
+}
+
+static void
+compose_mirror_dsts(const struct bridge *br, const struct flow *flow,
+                    uint16_t vlan, const struct port *in_port,
+                    struct dst_set *set, tag_type *tags)
+{
+    mirror_mask_t mirrors;
+    int flow_vlan;
+    size_t i;
+
+    mirrors = in_port->src_mirrors;
+    for (i = 0; i < set->n; i++) {
+        mirrors |= set->dsts[i].iface->port->dst_mirrors;
+    }
+
+    if (!mirrors) {
+        return;
+    }
+
+    flow_vlan = vlan_tci_to_vid(flow->vlan_tci);
+    if (flow_vlan == 0) {
+        flow_vlan = OFP_VLAN_NONE;
     }
 
     while (mirrors) {
         struct mirror *m = br->mirrors[mirror_mask_ffs(mirrors) - 1];
         if (!m->n_vlans || vlan_is_mirrored(m, vlan)) {
+            struct dst dst;
+
             if (m->out_port) {
                 if (set_dst(&dst, flow, in_port, m->out_port, tags)
                     && !dst_is_duplicate(set, &dst)) {
@@ -2305,8 +2346,6 @@ compose_dsts(const struct bridge *br, const struct flow *flow, uint16_t vlan,
         }
         mirrors &= mirrors - 1;
     }
-
-    partition_dsts(set, flow_vlan);
 }
 
 static void
@@ -2315,20 +2354,33 @@ compose_actions(struct bridge *br, const struct flow *flow, uint16_t vlan,
                 tag_type *tags, struct ofpbuf *actions,
                 uint16_t *nf_output_iface)
 {
+    uint16_t initial_vlan, cur_vlan;
+    const struct dst *dst;
     struct dst_set set;
-    uint16_t cur_vlan;
-    size_t i;
 
     dst_set_init(&set);
     compose_dsts(br, flow, vlan, in_port, out_port, &set, tags,
                  nf_output_iface);
+    compose_mirror_dsts(br, flow, vlan, in_port, &set, tags);
 
-    cur_vlan = vlan_tci_to_vid(flow->vlan_tci);
-    if (cur_vlan == 0) {
-        cur_vlan = OFP_VLAN_NONE;
+    /* Output all the packets we can without having to change the VLAN. */
+    initial_vlan = vlan_tci_to_vid(flow->vlan_tci);
+    if (initial_vlan == 0) {
+        initial_vlan = OFP_VLAN_NONE;
     }
-    for (i = 0; i < set.n; i++) {
-        const struct dst *dst = &set.dsts[i];
+    for (dst = set.dsts; dst < &set.dsts[set.n]; dst++) {
+        if (dst->vlan != initial_vlan) {
+            continue;
+        }
+        nl_msg_put_u32(actions, ODP_ACTION_ATTR_OUTPUT, dst->iface->dp_ifidx);
+    }
+
+    /* Then output the rest. */
+    cur_vlan = initial_vlan;
+    for (dst = set.dsts; dst < &set.dsts[set.n]; dst++) {
+        if (dst->vlan == initial_vlan) {
+            continue;
+        }
         if (dst->vlan != cur_vlan) {
             if (dst->vlan == OFP_VLAN_NONE) {
                 nl_msg_put_flag(actions, ODP_ACTION_ATTR_STRIP_VLAN);
@@ -2342,6 +2394,7 @@ compose_actions(struct bridge *br, const struct flow *flow, uint16_t vlan,
         }
         nl_msg_put_u32(actions, ODP_ACTION_ATTR_OUTPUT, dst->iface->dp_ifidx);
     }
+
     dst_set_free(&set);
 }
 
@@ -2603,8 +2656,11 @@ bridge_special_ofhook_cb(const struct flow *flow,
     iface = iface_from_dp_ifidx(br, flow->in_port);
 
     if (flow->dl_type == htons(ETH_TYPE_LACP)) {
-        if (iface && iface->port->bond && packet) {
-            bond_process_lacp(iface->port->bond, iface, packet);
+        if (iface && iface->port->lacp && packet) {
+            const struct lacp_pdu *pdu = parse_lacp_packet(packet);
+            if (pdu) {
+                lacp_process_pdu(iface->port->lacp, iface, pdu);
+            }
         }
         return false;
     }
@@ -2667,21 +2723,90 @@ bridge_account_checkpoint_ofhook_cb(void *br_)
     }
 }
 
+static uint16_t
+bridge_autopath_ofhook_cb(const struct flow *flow, uint32_t ofp_port,
+                          tag_type *tags, void *br_)
+{
+    struct bridge *br = br_;
+    uint16_t odp_port = ofp_port_to_odp_port(ofp_port);
+    struct port *port = port_from_dp_ifidx(br, odp_port);
+    uint16_t ret;
+
+    if (!port) {
+        ret = ODPP_NONE;
+    } else if (list_is_short(&port->ifaces)) {
+        ret = odp_port;
+    } else {
+        struct iface *iface;
+
+        /* Autopath does not support VLAN hashing. */
+        iface = bond_choose_output_slave(port->bond, flow,
+                                         OFP_VLAN_NONE, tags);
+        ret = iface ? iface->dp_ifidx : ODPP_NONE;
+    }
+
+    return odp_port_to_ofp_port(ret);
+}
+
 static struct ofhooks bridge_ofhooks = {
     bridge_normal_ofhook_cb,
     bridge_special_ofhook_cb,
     bridge_account_flow_ofhook_cb,
     bridge_account_checkpoint_ofhook_cb,
+    bridge_autopath_ofhook_cb,
 };
 \f
 /* Port functions. */
 
+static void
+lacp_send_pdu_cb(void *iface_, const struct lacp_pdu *pdu)
+{
+    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 10);
+    struct iface *iface = iface_;
+    uint8_t ea[ETH_ADDR_LEN];
+    int error;
+
+    error = netdev_get_etheraddr(iface->netdev, ea);
+    if (!error) {
+        struct lacp_pdu *packet_pdu;
+        struct ofpbuf packet;
+
+        ofpbuf_init(&packet, 0);
+        packet_pdu = eth_compose(&packet, eth_addr_lacp, ea, ETH_TYPE_LACP,
+                                 sizeof *packet_pdu);
+        *packet_pdu = *pdu;
+        error = netdev_send(iface->netdev, &packet);
+        if (error) {
+            VLOG_WARN_RL(&rl, "port %s: sending LACP PDU on iface %s failed "
+                         "(%s)", iface->port->name, iface->name,
+                         strerror(error));
+        }
+        ofpbuf_uninit(&packet);
+    } else {
+        VLOG_ERR_RL(&rl, "port %s: cannot obtain Ethernet address of iface "
+                    "%s (%s)", iface->port->name, iface->name,
+                    strerror(error));
+    }
+}
+
 static void
 port_run(struct port *port)
 {
+    if (port->lacp) {
+        lacp_run(port->lacp, lacp_send_pdu_cb);
+    }
+
     if (port->bond) {
+        struct iface *iface;
+
+        LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
+            bool may_enable = lacp_slave_may_enable(port->lacp, iface);
+            bond_slave_set_lacp_may_enable(port->bond, iface, may_enable);
+        }
+
         bond_run(port->bond,
-                 ofproto_get_revalidate_set(port->bridge->ofproto));
+                 ofproto_get_revalidate_set(port->bridge->ofproto),
+                 lacp_negotiated(port->lacp));
         if (bond_should_send_learning_packets(port->bond)) {
             port_send_learning_packets(port);
         }
@@ -2691,6 +2816,10 @@ port_run(struct port *port)
 static void
 port_wait(struct port *port)
 {
+    if (port->lacp) {
+        lacp_wait(port->lacp);
+    }
+
     if (port->bond) {
         bond_wait(port->bond);
     }
@@ -2913,6 +3042,8 @@ port_destroy(struct port *port)
 
         VLOG_INFO("destroyed port %s on bridge %s", port->name, br->name);
 
+        bond_destroy(port->bond);
+        lacp_destroy(port->lacp);
         port_flush_macs(port);
 
         bitmap_free(port->trunks);
@@ -2964,29 +3095,8 @@ enable_lacp(struct port *port, bool *activep)
     }
 }
 
-static struct lacp_settings *
-port_reconfigure_bond_lacp(struct port *port, struct lacp_settings *s)
-{
-    if (!enable_lacp(port, &s->active)) {
-        return NULL;
-    }
-
-    s->name = port->name;
-    memcpy(s->id, port->bridge->ea, ETH_ADDR_LEN);
-    s->priority = atoi(get_port_other_config(port->cfg, "lacp-system-priority",
-                                             "0"));
-    s->fast = !strcmp(get_port_other_config(port->cfg, "lacp-time", "slow"),
-                      "fast");
-
-    if (s->priority <= 0 || s->priority > UINT16_MAX) {
-        /* Prefer bondable links if unspecified. */
-        s->priority = UINT16_MAX - !list_is_short(&port->ifaces);
-    }
-    return s;
-}
-
 static void
-iface_reconfigure_bond(struct iface *iface)
+iface_reconfigure_lacp(struct iface *iface)
 {
     struct lacp_slave_settings s;
     int priority;
@@ -2997,13 +3107,49 @@ iface_reconfigure_bond(struct iface *iface)
                         iface->cfg, "lacp-port-priority", "0"));
     s.priority = (priority >= 0 && priority <= UINT16_MAX
                   ? priority : UINT16_MAX);
-    bond_slave_register(iface->port->bond, iface, iface->netdev, &s);
+    lacp_slave_register(iface->port->lacp, iface, &s);
+}
+
+static void
+port_reconfigure_lacp(struct port *port)
+{
+    static struct lacp_settings s;
+    struct iface *iface;
+    int priority;
+
+    if (!enable_lacp(port, &s.active)) {
+        lacp_destroy(port->lacp);
+        port->lacp = NULL;
+        return;
+    }
+
+    s.name = port->name;
+    memcpy(s.id, port->bridge->ea, ETH_ADDR_LEN);
+
+    /* Prefer bondable links if unspecified. */
+    priority = atoi(get_port_other_config(port->cfg, "lacp-system-priority",
+                                          "0"));
+    s.priority = (priority > 0 && priority <= UINT16_MAX
+                  ? priority
+                  : UINT16_MAX - !list_is_short(&port->ifaces));
+
+    s.fast = !strcmp(get_port_other_config(port->cfg, "lacp-time", "slow"),
+                     "fast");
+
+    if (!port->lacp) {
+        port->lacp = lacp_create();
+    }
+
+    lacp_configure(port->lacp, &s);
+
+    LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
+        iface_reconfigure_lacp(iface);
+    }
 }
 
 static void
 port_reconfigure_bond(struct port *port)
 {
-    struct lacp_settings lacp_settings;
     struct bond_settings s;
     const char *detect_s;
     struct iface *iface;
@@ -3049,16 +3195,21 @@ port_reconfigure_bond(struct port *port)
     }
 
     s.fake_iface = port->cfg->bond_fake_iface;
-    s.lacp = port_reconfigure_bond_lacp(port, &lacp_settings);
 
     if (!port->bond) {
         port->bond = bond_create(&s);
     } else {
-        bond_reconfigure(port->bond, &s);
+        if (bond_reconfigure(port->bond, &s)) {
+            bridge_flush(port->bridge);
+        }
     }
 
     LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
-        iface_reconfigure_bond(iface);
+        uint16_t stable_id = (port->lacp
+                              ? lacp_slave_get_port_id(port->lacp, iface)
+                              : iface->dp_ifidx);
+        bond_slave_register(iface->port->bond, iface, stable_id,
+                            iface->netdev);
     }
 }
 
@@ -3131,6 +3282,10 @@ iface_destroy(struct iface *iface)
             bond_slave_unregister(port->bond, iface);
         }
 
+        if (port->lacp) {
+            lacp_slave_unregister(port->lacp, iface);
+        }
+
         shash_find_and_delete_assert(&br->iface_by_name, iface->name);
 
         if (iface->dp_ifidx >= 0) {
@@ -3190,13 +3345,15 @@ iface_set_mac(struct iface *iface)
 {
     uint8_t ea[ETH_ADDR_LEN];
 
-    if (iface->cfg->mac && eth_addr_from_string(iface->cfg->mac, ea)) {
-        if (eth_addr_is_multicast(ea)) {
+    if (!strcmp(iface->type, "internal")
+        && iface->cfg->mac && eth_addr_from_string(iface->cfg->mac, ea)) {
+        if (iface->dp_ifidx == ODPP_LOCAL) {
+            VLOG_ERR("interface %s: ignoring mac in Interface record "
+                     "(use Bridge record to set local port's mac)",
+                     iface->name);
+        } else if (eth_addr_is_multicast(ea)) {
             VLOG_ERR("interface %s: cannot set MAC to multicast address",
                      iface->name);
-        } else if (iface->dp_ifidx == ODPP_LOCAL) {
-            VLOG_ERR("ignoring iface.%s.mac; use bridge.%s.mac instead",
-                     iface->name, iface->name);
         } else {
             int error = netdev_set_etheraddr(iface->netdev, ea);
             if (error) {
@@ -3477,6 +3634,7 @@ mirror_create(struct bridge *br, struct ovsrec_mirror *cfg)
     mac_learning_flush(br->ml);
 
     br->mirrors[i] = m = xzalloc(sizeof *m);
+    m->uuid = cfg->header_.uuid;
     m->bridge = br;
     m->idx = i;
     m->name = xstrdup(cfg->name);
@@ -3566,19 +3724,6 @@ vlan_is_mirrored(const struct mirror *m, int vlan)
     return false;
 }
 
-static bool
-port_trunks_any_mirrored_vlan(const struct mirror *m, const struct port *p)
-{
-    size_t i;
-
-    for (i = 0; i < m->n_vlans; i++) {
-        if (port_trunks_vlan(p, m->vlans[i])) {
-            return true;
-        }
-    }
-    return false;
-}
-
 static void
 mirror_reconfigure_one(struct mirror *m, struct ovsrec_mirror *cfg)
 {
@@ -3663,11 +3808,7 @@ mirror_reconfigure_one(struct mirror *m, struct ovsrec_mirror *cfg)
     /* Update ports. */
     mirror_bit = MIRROR_MASK_C(1) << m->idx;
     HMAP_FOR_EACH (port, hmap_node, &m->bridge->ports) {
-        if (sset_contains(&m->src_ports, port->name)
-            || (m->n_vlans
-                && (!port->vlan
-                    ? port_trunks_any_mirrored_vlan(m, port)
-                    : vlan_is_mirrored(m, port->vlan)))) {
+        if (sset_contains(&m->src_ports, port->name)) {
             port->src_mirrors |= mirror_bit;
         } else {
             port->src_mirrors &= ~mirror_bit;