#include "xenserver.h"
#include "vlog.h"
#include "sflow_api.h"
+#include "vlan-bitmap.h"
VLOG_DEFINE_THIS_MODULE(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. */
#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 *);
static void iface_update_cfm(struct iface *);
static bool iface_refresh_cfm_stats(struct iface *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 *);
/* 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;
}
dpif_port = NULL;
}
if (iface) {
+ if (iface->port->bond) {
+ /* The bond has a pointer to the netdev, so remove it
+ * from the bond before closing the netdev. The slave
+ * will get added back to the bond later, after a new
+ * netdev is available. */
+ bond_slave_unregister(iface->port->bond, iface);
+ }
netdev_close(iface->netdev);
iface->netdev = NULL;
}
int64_t mtu_64;
int error;
+ if (iface_is_synthetic(iface)) {
+ return;
+ }
+
shash_init(&sh);
if (!netdev_get_status(iface->netdev, &sh)) {
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, ¤t, 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)
{
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);
stats_timer = time_msec() + STATS_INTERVAL;
}
- if (time_msec() >= cfm_limiter) {
+ if (time_msec() >= db_limiter) {
struct ovsdb_idl_txn *txn;
bool changed = false;
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);
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);
}
}
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);
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);
}
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.
|| !strcmp(br->cfg->fail_mode, "standalone")
? OFPROTO_FAIL_STANDALONE
: OFPROTO_FAIL_SECURE;
- if (ofproto_get_fail_mode(br->ofproto) != fail_mode
- && !ofproto_has_primary_controller(br->ofproto)) {
- ofproto_flush_flows(br->ofproto);
- }
ofproto_set_fail_mode(br->ofproto, fail_mode);
- /* Delete all flows if we're switching from connected to standalone or vice
- * versa. (XXX Should we delete all flows if we are switching from one
- * controller to another?) */
-
/* Configure OpenFlow controller connection snooping. */
if (!ofproto_has_snoops(br->ofproto)) {
struct sset snoops;
struct ovsrec_controller **controllers;
size_t n_controllers;
- bool had_primary;
struct ofproto_controller *ocs;
size_t n_ocs;
} else {
ofproto_set_extra_in_band_remotes(br->ofproto, managers, n_managers);
}
- had_primary = ofproto_has_primary_controller(br->ofproto);
n_controllers = bridge_get_controllers(br, &controllers);
ofproto_set_controllers(br->ofproto, ocs, n_ocs);
free(ocs[0].target); /* From bridge_ofproto_controller_for_mgmt(). */
free(ocs);
-
- if (had_primary != ofproto_has_primary_controller(br->ofproto)) {
- ofproto_flush_flows(br->ofproto);
- }
-
- /* If there are no controllers and the bridge is in standalone
- * mode, set up a flow that matches every packet and directs
- * them to OFPP_NORMAL (which goes to us). Otherwise, the
- * switch is in secure mode and we won't pass any traffic until
- * a controller has been defined and it tells us to do so. */
- if (!n_controllers
- && ofproto_get_fail_mode(br->ofproto) == OFPROTO_FAIL_STANDALONE) {
- union ofp_action action;
- struct cls_rule rule;
-
- memset(&action, 0, sizeof action);
- action.type = htons(OFPAT_OUTPUT);
- action.output.len = htons(sizeof action);
- action.output.port = htons(OFPP_NORMAL);
- cls_rule_init_catchall(&rule, 0);
- ofproto_add_flow(br->ofproto, &rule, &action, 1);
- }
}
static void
static bool
port_trunks_vlan(const struct port *port, uint16_t vlan)
{
- return (port->vlan < 0
- && (!port->trunks || bitmap_is_set(port->trunks, vlan)));
+ return (port->vlan < 0 || vlan_bitmap_contains(port->trunks, vlan));
}
static bool
/* Get trunked VLANs. */
trunks = NULL;
if (vlan < 0 && cfg->n_trunks) {
- size_t n_errors;
-
- trunks = bitmap_allocate(4096);
- n_errors = 0;
- for (i = 0; i < cfg->n_trunks; i++) {
- int trunk = cfg->trunks[i];
- if (trunk >= 0) {
- bitmap_set1(trunks, trunk);
- } else {
- n_errors++;
- }
- }
- if (n_errors) {
- VLOG_ERR("port %s: invalid values for %zu trunk VLANs",
- port->name, cfg->n_trunks);
- }
- if (n_errors == cfg->n_trunks) {
+ trunks = vlan_bitmap_from_array(cfg->trunks, cfg->n_trunks);
+ if (!trunks) {
VLOG_ERR("port %s: no valid trunks, trunking all VLANs",
port->name);
- bitmap_free(trunks);
- trunks = NULL;
}
} else if (vlan >= 0 && cfg->n_trunks) {
VLOG_ERR("port %s: ignoring trunks in favor of implicit vlan",
port->name);
}
- if (trunks == NULL
- ? port->trunks != NULL
- : port->trunks == NULL || !bitmap_equal(trunks, port->trunks, 4096)) {
+ if (!vlan_bitmap_equal(trunks, port->trunks)) {
need_flush = true;
}
bitmap_free(port->trunks);
VLOG_INFO("destroyed port %s on bridge %s", port->name, br->name);
+ bond_destroy(port->bond);
lacp_destroy(port->lacp);
port_flush_macs(port);
iface_reconfigure_lacp(struct iface *iface)
{
struct lacp_slave_settings s;
- int priority;
+ int priority, portid;
+
+ portid = atoi(get_interface_other_config(iface->cfg, "lacp-port-id", "0"));
+ priority = atoi(get_interface_other_config(iface->cfg,
+ "lacp-port-priority", "0"));
+
+ if (portid <= 0 || portid > UINT16_MAX) {
+ portid = iface->dp_ifidx;
+ }
+
+ if (priority <= 0 || priority > UINT16_MAX) {
+ priority = UINT16_MAX;
+ }
s.name = iface->name;
- s.id = iface->dp_ifidx;
- priority = atoi(get_interface_other_config(
- iface->cfg, "lacp-port-priority", "0"));
- s.priority = (priority >= 0 && priority <= UINT16_MAX
- ? priority : UINT16_MAX);
+ s.id = portid;
+ s.priority = priority;
lacp_slave_register(iface->port->lacp, iface, &s);
}
{
static struct lacp_settings s;
struct iface *iface;
+ uint8_t sysid[ETH_ADDR_LEN];
+ const char *sysid_str;
+ const char *lacp_time;
+ long long int custom_time;
+ int priority;
if (!enable_lacp(port, &s.active)) {
lacp_destroy(port->lacp);
return;
}
+ sysid_str = get_port_other_config(port->cfg, "lacp-system-id", NULL);
+ if (sysid_str && eth_addr_from_string(sysid_str, sysid)) {
+ memcpy(s.id, sysid, ETH_ADDR_LEN);
+ } else {
+ memcpy(s.id, port->bridge->ea, ETH_ADDR_LEN);
+ }
+
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);
+ /* 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.strict = !strcmp(get_port_other_config(port->cfg, "lacp-strict",
+ "false"),
+ "true");
+
+ lacp_time = get_port_other_config(port->cfg, "lacp-time", "slow");
+ custom_time = atoi(lacp_time);
+ if (!strcmp(lacp_time, "fast")) {
+ s.lacp_time = LACP_TIME_FAST;
+ } else if (!strcmp(lacp_time, "slow")) {
+ s.lacp_time = LACP_TIME_SLOW;
+ } else if (custom_time > 0) {
+ s.lacp_time = LACP_TIME_CUSTOM;
+ s.custom_time = custom_time;
+ } else {
+ s.lacp_time = LACP_TIME_SLOW;
}
if (!port->lacp) {
{
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) {
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);
}
}
/* XXX */
return netdev_get_carrier(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. */
/* Update flooded vlans (for RSPAN). */
rspan_vlans = NULL;
if (br->cfg->n_flood_vlans) {
- rspan_vlans = bitmap_allocate(4096);
-
- for (i = 0; i < br->cfg->n_flood_vlans; i++) {
- int64_t vlan = br->cfg->flood_vlans[i];
- if (vlan >= 0 && vlan < 4096) {
- bitmap_set1(rspan_vlans, vlan);
- VLOG_INFO("bridge %s: disabling learning on vlan %"PRId64,
- br->name, vlan);
- } else {
- VLOG_ERR("bridge %s: invalid value %"PRId64 "for flood VLAN",
- br->name, vlan);
- }
- }
+ rspan_vlans = vlan_bitmap_from_array(br->cfg->flood_vlans,
+ br->cfg->n_flood_vlans);
}
if (mac_learning_set_flood_vlans(br->ml, rspan_vlans)) {
bridge_flush(br);
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);
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)
{
/* 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;