X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=vswitchd%2Fbridge.c;h=a00799a11b66ab09a114831d741a08dca515da39;hb=b9e8b45a3936441bd0fd1fd156cae498bf72cac8;hp=fc2939b97b4741e1c8fe2f6a8e4526600d1841c9;hpb=e2ead27a7244d61e7f0f71f733f76118056f8981;p=openvswitch
diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c
index fc2939b9..a00799a1 100644
--- a/vswitchd/bridge.c
+++ b/vswitchd/bridge.c
@@ -1,28 +1,16 @@
/* Copyright (c) 2008, 2009 Nicira Networks
*
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
*
- * This program 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- *
- * In addition, as a special exception, Nicira Networks gives permission
- * to link the code of its release of vswitchd with the OpenSSL project's
- * "OpenSSL" library (or with modified versions of it that use the same
- * license as the "OpenSSL" library), and distribute the linked
- * executables. You must obey the GNU General Public License in all
- * respects for all of the code used other than "OpenSSL". If you modify
- * this file, you may extend this exception to your version of the file,
- * but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version.
+ * http://www.apache.org/licenses/LICENSE-2.0
*
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
#include
@@ -39,6 +27,7 @@
#include
#include
#include
+#include
#include
#include "bitmap.h"
#include "cfg.h"
@@ -141,6 +130,7 @@ struct port {
tag_type active_iface_tag; /* Tag for bcast flows. */
tag_type no_ifaces_tag; /* Tag for flows when all ifaces disabled. */
int updelay, downdelay; /* Delay before iface goes up/down, in ms. */
+ bool bond_compat_is_stale; /* Need to call port_update_bond_compat()? */
/* Port mirroring info. */
mirror_mask_t src_mirrors; /* Mirrors triggered when packet received. */
@@ -203,6 +193,7 @@ enum { DP_MAX = 256 };
static struct bridge *bridge_create(const char *name);
static void bridge_destroy(struct bridge *);
static struct bridge *bridge_lookup(const char *name);
+static void bridge_unixctl_dump_flows(struct unixctl_conn *, const char *);
static int bridge_run_one(struct bridge *);
static void bridge_reconfigure_one(struct bridge *);
static void bridge_reconfigure_controller(struct bridge *);
@@ -217,6 +208,8 @@ static uint64_t bridge_pick_datapath_id(struct bridge *,
const char *devname);
static uint64_t dpid_from_hash(const void *, size_t nbytes);
+static void bridge_unixctl_fdb_show(struct unixctl_conn *, const char *args);
+
static void bond_init(void);
static void bond_run(struct bridge *);
static void bond_wait(struct bridge *);
@@ -291,6 +284,8 @@ bridge_init(void)
bond_init();
+ unixctl_command_register("fdb/show", bridge_unixctl_fdb_show);
+
for (i = 0; i < DP_MAX; i++) {
struct dpif dpif;
char devname[16];
@@ -310,6 +305,8 @@ bridge_init(void)
}
}
+ unixctl_command_register("bridge/dump-flows", bridge_unixctl_dump_flows);
+
bridge_reconfigure();
}
@@ -335,6 +332,7 @@ bridge_configure_ssl(void)
static char *private_key_file;
static char *certificate_file;
static char *cacert_file;
+ struct stat s;
if (config_string_change("ssl.private-key", &private_key_file)) {
vconn_ssl_set_private_key_file(private_key_file);
@@ -344,7 +342,13 @@ bridge_configure_ssl(void)
vconn_ssl_set_certificate_file(certificate_file);
}
- if (config_string_change("ssl.ca-cert", &cacert_file)) {
+ /* We assume that even if the filename hasn't changed, if the CA cert
+ * file has been removed, that we want to move back into
+ * boot-strapping mode. This opens a small security hole, because
+ * the old certificate will still be trusted until vSwitch is
+ * restarted. We may want to address this in vconn's SSL library. */
+ if (config_string_change("ssl.ca-cert", &cacert_file)
+ || (stat(cacert_file, &s) && errno == ENOENT)) {
vconn_ssl_set_ca_cert_file(cacert_file,
cfg_get_bool(0, "ssl.bootstrap-ca-cert"));
}
@@ -457,9 +461,23 @@ bridge_reconfigure(void)
for (i = 0; i < add_ifaces.n; i++) {
const char *if_name = add_ifaces.names[i];
for (;;) {
- int internal = cfg_get_bool(0, "iface.%s.internal", if_name);
- int error = dpif_port_add(&br->dpif, if_name, next_port_no++,
- internal ? ODP_PORT_INTERNAL : 0);
+ bool internal;
+ int error;
+
+ /* It's an internal interface if it's marked that way, or if
+ * it's a bonded interface for which we're faking up a network
+ * device. */
+ internal = cfg_get_bool(0, "iface.%s.internal", if_name);
+ if (cfg_get_bool(0, "bonding.%s.fake-iface", if_name)) {
+ struct port *port = port_lookup(br, if_name);
+ if (port && port->n_ifaces > 1) {
+ internal = true;
+ }
+ }
+
+ /* Add to datapath. */
+ error = dpif_port_add(&br->dpif, if_name, next_port_no++,
+ internal ? ODP_PORT_INTERNAL : 0);
if (error != EEXIST) {
if (next_port_no >= 256) {
VLOG_ERR("ran out of valid port numbers on dp%u",
@@ -613,31 +631,75 @@ bridge_pick_local_hw_addr(struct bridge *br, uint8_t ea[ETH_ADDR_LEN],
memset(ea, 0xff, sizeof ea);
for (i = 0; i < br->n_ports; i++) {
struct port *port = br->ports[i];
+ uint8_t iface_ea[ETH_ADDR_LEN];
+ uint64_t iface_ea_u64;
+ struct iface *iface;
+
+ /* Mirror output ports don't participate. */
if (port->is_mirror_output_port) {
continue;
}
- for (j = 0; j < port->n_ifaces; j++) {
- struct iface *iface = port->ifaces[j];
- uint8_t iface_ea[ETH_ADDR_LEN];
+
+ /* Choose the MAC address to represent the port. */
+ iface_ea_u64 = cfg_get_mac(0, "port.%s.mac", port->name);
+ if (iface_ea_u64) {
+ /* User specified explicitly. */
+ eth_addr_from_uint64(iface_ea_u64, iface_ea);
+
+ /* Find the interface with this Ethernet address (if any) so that
+ * we can provide the correct devname to the caller. */
+ iface = NULL;
+ for (j = 0; j < port->n_ifaces; j++) {
+ struct iface *candidate = port->ifaces[j];
+ uint8_t candidate_ea[ETH_ADDR_LEN];
+ if (!netdev_nodev_get_etheraddr(candidate->name, candidate_ea)
+ && eth_addr_equals(iface_ea, candidate_ea)) {
+ iface = candidate;
+ }
+ }
+ } else {
+ /* Choose the interface whose MAC address will represent the port.
+ * The Linux kernel bonding code always chooses the MAC address of
+ * the first slave added to a bond, and the Fedora networking
+ * scripts always add slaves to a bond in alphabetical order, so
+ * for compatibility we choose the interface with the name that is
+ * first in alphabetical order. */
+ iface = port->ifaces[0];
+ for (j = 1; j < port->n_ifaces; j++) {
+ struct iface *candidate = port->ifaces[j];
+ if (strcmp(candidate->name, iface->name) < 0) {
+ iface = candidate;
+ }
+ }
+
+ /* The local port doesn't count (since we're trying to choose its
+ * MAC address anyway). Other internal ports don't count because
+ * we really want a physical MAC if we can get it, and internal
+ * ports typically have randomly generated MACs. */
if (iface->dp_ifidx == ODPP_LOCAL
|| cfg_get_bool(0, "iface.%s.internal", iface->name)) {
continue;
}
+
+ /* Grab MAC. */
error = netdev_nodev_get_etheraddr(iface->name, iface_ea);
- if (!error) {
- if (!eth_addr_is_multicast(iface_ea) &&
- !eth_addr_is_reserved(iface_ea) &&
- !eth_addr_is_zero(iface_ea) &&
- memcmp(iface_ea, ea, ETH_ADDR_LEN) < 0) {
- memcpy(ea, iface_ea, ETH_ADDR_LEN);
- *devname = iface->name;
- }
- } else {
+ if (error) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
VLOG_ERR_RL(&rl, "failed to obtain Ethernet address of %s: %s",
iface->name, strerror(error));
+ continue;
}
}
+
+ /* Compare against our current choice. */
+ if (!eth_addr_is_multicast(iface_ea) &&
+ !eth_addr_is_reserved(iface_ea) &&
+ !eth_addr_is_zero(iface_ea) &&
+ memcmp(iface_ea, ea, ETH_ADDR_LEN) < 0)
+ {
+ memcpy(ea, iface_ea, ETH_ADDR_LEN);
+ *devname = iface ? iface->name : NULL;
+ }
}
if (eth_addr_is_multicast(ea) || eth_addr_is_vif(ea)) {
memcpy(ea, br->default_ea, ETH_ADDR_LEN);
@@ -732,10 +794,10 @@ bridge_pick_datapath_id(struct bridge *br,
static uint64_t
dpid_from_hash(const void *data, size_t n)
{
- uint8_t hash[SHA1HashSize];
+ uint8_t hash[SHA1_DIGEST_SIZE];
BUILD_ASSERT_DECL(sizeof hash >= ETH_ADDR_LEN);
- SHA1Bytes(data, n, hash);
+ sha1_bytes(data, n, hash);
eth_addr_mark_random(hash);
return eth_addr_to_uint64(hash);
}
@@ -792,6 +854,35 @@ bridge_flush(struct bridge *br)
}
}
+/* Bridge unixctl user interface functions. */
+static void
+bridge_unixctl_fdb_show(struct unixctl_conn *conn, const char *args)
+{
+ struct ds ds = DS_EMPTY_INITIALIZER;
+ const struct bridge *br;
+
+ br = bridge_lookup(args);
+ if (!br) {
+ unixctl_command_reply(conn, 501, "no such bridge");
+ return;
+ }
+
+ ds_put_cstr(&ds, " port VLAN MAC Age\n");
+ if (br->ml) {
+ const struct mac_entry *e;
+ LIST_FOR_EACH (e, struct mac_entry, lru_node, &br->ml->lrus) {
+ if (e->port < 0 || e->port >= br->n_ports) {
+ continue;
+ }
+ ds_put_format(&ds, "%5d %4d "ETH_ADDR_FMT" %3d\n",
+ br->ports[e->port]->ifaces[0]->dp_ifidx,
+ e->vlan, ETH_ADDR_ARGS(e->mac), mac_entry_age(e));
+ }
+ }
+ unixctl_command_reply(conn, 200, ds_cstr(&ds));
+ ds_destroy(&ds);
+}
+
/* Bridge reconfiguration functions. */
static struct bridge *
@@ -897,6 +988,27 @@ bridge_get_datapathid(const char *name)
return br ? ofproto_get_datapath_id(br->ofproto) : 0;
}
+/* Handle requests for a listing of all flows known by the OpenFlow
+ * stack, including those normally hidden. */
+static void
+bridge_unixctl_dump_flows(struct unixctl_conn *conn, const char *args)
+{
+ struct bridge *br;
+ struct ds results;
+
+ br = bridge_lookup(args);
+ if (!br) {
+ unixctl_command_reply(conn, 501, "Unknown bridge");
+ return;
+ }
+
+ ds_init(&results);
+ ofproto_get_all_flows(br->ofproto, &results);
+
+ unixctl_command_reply(conn, 200, ds_cstr(&results));
+ ds_destroy(&results);
+}
+
static int
bridge_run_one(struct bridge *br)
{
@@ -1080,10 +1192,15 @@ bridge_reconfigure_controller(struct bridge *br)
int rate_limit, burst_limit;
if (!strcmp(controller, "discover")) {
+ bool update_resolv_conf = true;
+
+ if (cfg_has("%s.update-resolv.conf", pfx)) {
+ update_resolv_conf = cfg_get_bool(0, "%s.update-resolv.conf",
+ pfx);
+ }
ofproto_set_discovery(br->ofproto, true,
cfg_get_string(0, "%s.accept-regex", pfx),
- cfg_get_bool(0, "%s.update-resolv.conf",
- pfx));
+ update_resolv_conf);
} else {
struct netdev *netdev;
bool in_band;
@@ -1135,14 +1252,19 @@ bridge_reconfigure_controller(struct bridge *br)
|| !strcmp(fail_mode, "open")));
probe = cfg_get_int(0, "%s.inactivity-probe", pfx);
- ofproto_set_probe_interval(br->ofproto,
- probe ? probe : cfg_get_int(0, "mgmt.inactivity-probe"));
+ if (probe < 5) {
+ probe = cfg_get_int(0, "mgmt.inactivity-probe");
+ if (probe < 5) {
+ probe = 5;
+ }
+ }
+ ofproto_set_probe_interval(br->ofproto, probe);
max_backoff = cfg_get_int(0, "%s.max-backoff", pfx);
if (!max_backoff) {
max_backoff = cfg_get_int(0, "mgmt.max-backoff");
if (!max_backoff) {
- max_backoff = 15;
+ max_backoff = 8;
}
}
ofproto_set_max_backoff(br->ofproto, max_backoff);
@@ -1213,9 +1335,12 @@ bridge_get_all_ifaces(const struct bridge *br, struct svec *ifaces)
struct iface *iface = port->ifaces[j];
svec_add(ifaces, iface->name);
}
+ if (port->n_ifaces > 1
+ && cfg_get_bool(0, "bonding.%s.fake-iface", port->name)) {
+ svec_add(ifaces, port->name);
+ }
}
- svec_sort(ifaces);
- assert(svec_is_unique(ifaces));
+ svec_sort_unique(ifaces);
}
/* For robustness, in case the administrator moves around datapath ports behind
@@ -1310,6 +1435,7 @@ choose_output_iface(const struct port *port, const uint8_t *dl_src,
return false;
}
e->iface_tag = tag_create_random();
+ ((struct port *) port)->bond_compat_is_stale = true;
}
*tags |= e->iface_tag;
iface = port->ifaces[e->iface_idx];
@@ -1335,6 +1461,10 @@ bond_link_status_update(struct iface *iface, bool carrier)
iface->delay_expires = LLONG_MAX;
VLOG_INFO_RL(&rl, "interface %s: will not be %s",
iface->name, carrier ? "disabled" : "enabled");
+ } else if (carrier && port->updelay && port->active_iface < 0) {
+ iface->delay_expires = time_msec();
+ VLOG_INFO_RL(&rl, "interface %s: skipping %d ms updelay since no "
+ "other interface is up", iface->name, port->updelay);
} else {
int delay = carrier ? port->updelay : port->downdelay;
iface->delay_expires = time_msec() + delay;
@@ -1378,7 +1508,7 @@ bond_enable_slave(struct iface *iface, bool enable)
iface->enabled = enable;
if (!iface->enabled) {
- VLOG_WARN("interface %s: enabled", iface->name);
+ VLOG_WARN("interface %s: disabled", iface->name);
ofproto_revalidate(br->ofproto, iface->tag);
if (iface->port_ifidx == port->active_iface) {
ofproto_revalidate(br->ofproto,
@@ -1387,7 +1517,7 @@ bond_enable_slave(struct iface *iface, bool enable)
}
bond_send_learning_packets(port);
} else {
- VLOG_WARN("interface %s: disabled", iface->name);
+ VLOG_WARN("interface %s: enabled", iface->name);
if (port->active_iface < 0) {
ofproto_revalidate(br->ofproto, port->no_ifaces_tag);
bond_choose_active_iface(port);
@@ -1395,6 +1525,7 @@ bond_enable_slave(struct iface *iface, bool enable)
}
iface->tag = tag_create_random();
}
+ port_update_bond_compat(port);
}
static void
@@ -1404,6 +1535,12 @@ bond_run(struct bridge *br)
for (i = 0; i < br->n_ports; i++) {
struct port *port = br->ports[i];
+
+ if (port->bond_compat_is_stale) {
+ port->bond_compat_is_stale = false;
+ port_update_bond_compat(port);
+ }
+
if (port->n_ifaces < 2) {
continue;
}
@@ -1574,12 +1711,14 @@ compose_dsts(const struct bridge *br, const flow_t *flow, uint16_t vlan,
for (i = 0; i < br->n_ports; i++) {
struct port *port = br->ports[i];
if (port_includes_vlan(port, m->out_vlan)
- && set_dst(dst, flow, in_port, port, tags)
- && !dst_is_duplicate(dsts, dst - dsts, dst))
+ && set_dst(dst, flow, in_port, port, tags))
{
if (port->vlan < 0) {
dst->vlan = m->out_vlan;
}
+ if (dst_is_duplicate(dsts, dst - dsts, dst)) {
+ continue;
+ }
if (dst->dp_ifidx == flow->in_port
&& dst->vlan == vlan) {
/* Don't send out input port on same VLAN. */
@@ -1753,7 +1892,7 @@ process_flow(struct bridge *br, const flow_t *flow,
goto done;
} else {
/* Drop all multicast packets for which we have learned a different
- * input port, because we probably sent the packet on one slaves
+ * input port, because we probably sent the packet on one slave
* and got it back on the active slave. Broadcast ARP replies are
* an exception to this rule: the host has moved to another
* switch. */
@@ -2186,6 +2325,7 @@ bond_rebalance_port(struct port *port)
} else {
from++;
}
+ port->bond_compat_is_stale = true;
}
}
@@ -2211,10 +2351,7 @@ bond_send_learning_packets(struct port *port)
ofpbuf_init(&packet, 128);
error = n_packets = n_errors = 0;
LIST_FOR_EACH (e, struct mac_entry, lru_node, &br->ml->lrus) {
- static const char s[] = "Open vSwitch Bond Failover";
union ofp_action actions[2], *a;
- struct eth_header *eth;
- struct llc_snap_header *llc_snap;
uint16_t dp_ifidx;
tag_type tags = 0;
flow_t flow;
@@ -2225,23 +2362,6 @@ bond_send_learning_packets(struct port *port)
continue;
}
- /* Compose packet to send. */
- ofpbuf_clear(&packet);
- eth = ofpbuf_put_zeros(&packet, ETH_HEADER_LEN);
- llc_snap = ofpbuf_put_zeros(&packet, LLC_SNAP_HEADER_LEN);
- ofpbuf_put(&packet, s, sizeof s); /* Includes null byte. */
- ofpbuf_put(&packet, e->mac, ETH_ADDR_LEN);
-
- memcpy(eth->eth_dst, eth_addr_broadcast, ETH_ADDR_LEN);
- memcpy(eth->eth_src, e->mac, ETH_ADDR_LEN);
- eth->eth_type = htons(packet.size - ETH_HEADER_LEN);
-
- llc_snap->llc.llc_dsap = LLC_DSAP_SNAP;
- llc_snap->llc.llc_ssap = LLC_SSAP_SNAP;
- llc_snap->llc.llc_cntl = LLC_CNTL_SNAP;
- memcpy(llc_snap->snap.snap_org, "\x00\x23\x20", 3);
- llc_snap->snap.snap_type = htons(0xf177); /* Random number. */
-
/* Compose actions. */
memset(actions, 0, sizeof actions);
a = actions;
@@ -2258,6 +2378,8 @@ bond_send_learning_packets(struct port *port)
/* Send packet. */
n_packets++;
+ compose_benign_packet(&packet, "Open vSwitch Bond Failover", 0xf177,
+ e->mac);
flow_extract(&packet, ODPP_NONE, &flow);
retval = ofproto_send_packet(br->ofproto, &flow, actions, a - actions,
&packet);
@@ -2427,8 +2549,8 @@ bond_unixctl_migrate(struct unixctl_conn *conn, const char *args_)
return;
}
- if (sscanf(hash_s, "%"SCNx8":%"SCNx8":%"SCNx8":%"SCNx8":%"SCNx8":%"SCNx8,
- &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]) == 6) {
+ if (sscanf(hash_s, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(mac))
+ == ETH_ADDR_SCAN_COUNT) {
hash = bond_hash(mac);
} else if (strspn(hash_s, "0123456789") == strlen(hash_s)) {
hash = atoi(hash_s) & BOND_MASK;
@@ -2452,6 +2574,7 @@ bond_unixctl_migrate(struct unixctl_conn *conn, const char *args_)
ofproto_revalidate(port->bridge->ofproto, entry->iface_tag);
entry->iface_idx = iface->port_ifidx;
entry->iface_tag = tag_create_random();
+ port->bond_compat_is_stale = true;
unixctl_command_reply(conn, 200, "migrated");
}
@@ -2716,6 +2839,7 @@ port_destroy(struct port *port)
size_t i;
proc_net_compat_update_vlan(port->name, NULL, 0);
+ proc_net_compat_update_bond(port->name, NULL);
for (i = 0; i < MAX_MIRRORS; i++) {
struct mirror *m = br->mirrors[i];
@@ -2782,7 +2906,7 @@ port_update_bonding(struct port *port)
if (port->bond_hash) {
free(port->bond_hash);
port->bond_hash = NULL;
- proc_net_compat_update_bond(port->name, NULL);
+ port->bond_compat_is_stale = true;
}
} else {
if (!port->bond_hash) {
@@ -2797,36 +2921,52 @@ port_update_bonding(struct port *port)
port->no_ifaces_tag = tag_create_random();
bond_choose_active_iface(port);
}
- port_update_bond_compat(port);
+ port->bond_compat_is_stale = true;
}
}
static void
port_update_bond_compat(struct port *port)
{
+ struct compat_bond_hash compat_hashes[BOND_MASK + 1];
struct compat_bond bond;
size_t i;
if (port->n_ifaces < 2) {
+ proc_net_compat_update_bond(port->name, NULL);
return;
}
bond.up = false;
bond.updelay = port->updelay;
bond.downdelay = port->downdelay;
+
+ bond.n_hashes = 0;
+ bond.hashes = compat_hashes;
+ if (port->bond_hash) {
+ const struct bond_entry *e;
+ for (e = port->bond_hash; e <= &port->bond_hash[BOND_MASK]; e++) {
+ if (e->iface_idx >= 0 && e->iface_idx < port->n_ifaces) {
+ struct compat_bond_hash *cbh = &bond.hashes[bond.n_hashes++];
+ cbh->hash = e - port->bond_hash;
+ cbh->netdev_name = port->ifaces[e->iface_idx]->name;
+ }
+ }
+ }
+
bond.n_slaves = port->n_ifaces;
bond.slaves = xmalloc(port->n_ifaces * sizeof *bond.slaves);
for (i = 0; i < port->n_ifaces; i++) {
struct iface *iface = port->ifaces[i];
struct compat_bond_slave *slave = &bond.slaves[i];
slave->name = iface->name;
- slave->up = ((iface->enabled && iface->delay_expires == LLONG_MAX) ||
- (!iface->enabled && iface->delay_expires != LLONG_MAX));
+ slave->up = iface->enabled;
if (slave->up) {
bond.up = true;
}
memcpy(slave->mac, iface->mac, ETH_ADDR_LEN);
}
+
proc_net_compat_update_bond(port->name, &bond);
free(bond.slaves);
}
@@ -2882,8 +3022,17 @@ iface_create(struct port *port, const char *name)
iface->tag = tag_create_random();
iface->delay_expires = LLONG_MAX;
- netdev_nodev_get_etheraddr(name, iface->mac);
- netdev_nodev_get_carrier(name, &iface->enabled);
+ if (!cfg_get_bool(0, "iface.%s.internal", iface->name)) {
+ netdev_nodev_get_etheraddr(name, iface->mac);
+ netdev_nodev_get_carrier(name, &iface->enabled);
+ } else {
+ /* Internal interfaces are created later by the call to dpif_port_add()
+ * in bridge_reconfigure(). Until then, we can't obtain any
+ * information about them. (There's no real value in doing so, anyway,
+ * because the 'mac' and 'enabled' values are only used for interfaces
+ * that are bond slaves, and it doesn't normally make sense to bond an
+ * internal interface.) */
+ }
if (port->n_ifaces >= port->allocated_ifaces) {
port->ifaces = x2nrealloc(port->ifaces, &port->allocated_ifaces,
@@ -3152,6 +3301,7 @@ mirror_reconfigure_one(struct mirror *m)
int *vlans;
size_t i;
bool mirror_all_ports;
+ bool any_ports_specified;
/* Get output port. */
out_port_name = cfg_get_key(0, "mirror.%s.%s.output.port",
@@ -3190,11 +3340,18 @@ mirror_reconfigure_one(struct mirror *m)
cfg_get_all_keys(&src_ports, "%s.select.src-port", pfx);
cfg_get_all_keys(&dst_ports, "%s.select.dst-port", pfx);
cfg_get_all_keys(&ports, "%s.select.port", pfx);
+ any_ports_specified = src_ports.n || dst_ports.n || ports.n;
svec_append(&src_ports, &ports);
svec_append(&dst_ports, &ports);
svec_destroy(&ports);
prune_ports(m, &src_ports);
prune_ports(m, &dst_ports);
+ if (any_ports_specified && !src_ports.n && !dst_ports.n) {
+ VLOG_ERR("%s: none of the specified ports exist; "
+ "disabling port mirror %s", pfx, pfx);
+ mirror_destroy(m);
+ goto exit;
+ }
/* Get all the vlans, and drop duplicate and invalid vlans. */
svec_init(&vlan_strings);
@@ -3246,6 +3403,7 @@ mirror_reconfigure_one(struct mirror *m)
}
/* Clean up. */
+exit:
svec_destroy(&src_ports);
svec_destroy(&dst_ports);
free(pfx);