#include "xenserver.h"
#include "vlog.h"
#include "sflow_api.h"
+#include "vlan-bitmap.h"
VLOG_DEFINE_THIS_MODULE(bridge);
struct iface {
/* These members are always valid. */
struct list port_elem; /* Element in struct port's "ifaces" list. */
+ struct hmap_node name_node; /* In struct bridge's "iface_by_name" hmap. */
struct port *port; /* Containing port. */
char *name; /* Host network device name. */
tag_type tag; /* Tag associated with this interface. */
* 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;
/* OpenFlow switch processing. */
struct ofproto *ofproto; /* OpenFlow switch. */
- /* Kernel datapath information. */
- struct dpif *dpif; /* Datapath. */
- struct hmap ifaces; /* "struct iface"s indexed by dp_ifidx. */
-
/* Bridge ports. */
struct hmap ports; /* "struct port"s indexed by name. */
- struct shash iface_by_name; /* "struct iface"s indexed by name. */
+ struct hmap ifaces; /* "struct iface"s indexed by dp_ifidx. */
+ struct hmap iface_by_name; /* "struct iface"s indexed by name. */
/* Bonding. */
bool has_bonded_ports;
/* 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 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 *);
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 *);
NULL);
unixctl_command_register("bridge/reconnect", bridge_unixctl_reconnect,
NULL);
+ lacp_init();
bond_init();
}
sset_destroy(&dpif_types);
}
-/* Callback for iterate_and_prune_ifaces(). */
-static bool
-check_iface(struct bridge *br, struct iface *iface, void *aux OVS_UNUSED)
-{
- if (!iface->netdev) {
- /* We already reported a related error, don't bother duplicating it. */
- return false;
- }
-
- if (iface->dp_ifidx < 0) {
- VLOG_ERR("%s interface not in %s, dropping",
- iface->name, dpif_name(br->dpif));
- return false;
- }
-
- VLOG_DBG("%s has interface %s on port %d", dpif_name(br->dpif),
- iface->name, iface->dp_ifidx);
- return true;
-}
-
-/* Callback for iterate_and_prune_ifaces(). */
-static bool
-set_iface_properties(struct bridge *br OVS_UNUSED, struct iface *iface,
- void *aux OVS_UNUSED)
-{
- /* Set policing attributes. */
- netdev_set_policing(iface->netdev,
- iface->cfg->ingress_policing_rate,
- iface->cfg->ingress_policing_burst);
-
- /* 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);
- }
-
- return true;
-}
-
-/* Calls 'cb' for each interfaces in 'br', passing along the 'aux' argument.
- * Deletes from 'br' all the interfaces for which 'cb' returns false, and then
- * deletes from 'br' any ports that no longer have any interfaces. */
-static void
-iterate_and_prune_ifaces(struct bridge *br,
- bool (*cb)(struct bridge *, struct iface *,
- void *aux),
- void *aux)
-{
- struct port *port, *next_port;
-
- HMAP_FOR_EACH_SAFE (port, next_port, hmap_node, &br->ports) {
- struct iface *iface, *next_iface;
-
- LIST_FOR_EACH_SAFE (iface, next_iface, port_elem, &port->ifaces) {
- if (!cb(br, iface, aux)) {
- iface_set_ofport(iface->cfg, -1);
- iface_destroy(iface);
- }
- }
-
- if (list_is_empty(&port->ifaces)) {
- VLOG_WARN("%s port has no interfaces, dropping", port->name);
- port_destroy(port);
- }
- }
-}
-
/* Looks at the list of managers in 'ovs_cfg' and extracts their remote IP
* addresses and ports into '*managersp' and '*n_managersp'. The caller is
* responsible for freeing '*managersp' (with free()).
* that port already belongs to a different datapath, so we must do all
* port deletions before any port additions. */
LIST_FOR_EACH (br, node, &all_bridges) {
- struct dpif_port_dump dump;
+ struct ofproto_port ofproto_port;
+ struct ofproto_port_dump dump;
struct shash want_ifaces;
- struct dpif_port dpif_port;
bridge_get_all_ifaces(br, &want_ifaces);
- DPIF_PORT_FOR_EACH (&dpif_port, &dump, br->dpif) {
- if (!shash_find(&want_ifaces, dpif_port.name)
- && strcmp(dpif_port.name, br->name)) {
- int retval = dpif_port_del(br->dpif, dpif_port.port_no);
+ OFPROTO_PORT_FOR_EACH (&ofproto_port, &dump, br->ofproto) {
+ if (!shash_find(&want_ifaces, ofproto_port.name)
+ && strcmp(ofproto_port.name, br->name)) {
+ int retval = ofproto_port_del(br->ofproto,
+ ofproto_port.ofp_port);
if (retval) {
- VLOG_WARN("failed to remove %s interface from %s: %s",
- dpif_port.name, dpif_name(br->dpif),
- strerror(retval));
+ VLOG_WARN("bridge %s: failed to remove %s interface (%s)",
+ br->name, ofproto_port.name, strerror(retval));
}
}
}
}
LIST_FOR_EACH (br, node, &all_bridges) {
struct shash cur_ifaces, want_ifaces;
- struct dpif_port_dump dump;
- struct dpif_port dpif_port;
+ struct ofproto_port ofproto_port;
+ struct ofproto_port_dump dump;
/* Get the set of interfaces currently in this datapath. */
shash_init(&cur_ifaces);
- DPIF_PORT_FOR_EACH (&dpif_port, &dump, br->dpif) {
- struct dpif_port *port_info = xmalloc(sizeof *port_info);
- dpif_port_clone(port_info, &dpif_port);
- shash_add(&cur_ifaces, dpif_port.name, port_info);
+ OFPROTO_PORT_FOR_EACH (&ofproto_port, &dump, br->ofproto) {
+ struct ofproto_port *port_info = xmalloc(sizeof *port_info);
+ ofproto_port_clone(port_info, &ofproto_port);
+ shash_add(&cur_ifaces, ofproto_port.name, port_info);
}
/* Get the set of interfaces we want on this datapath. */
SHASH_FOR_EACH (node, &want_ifaces) {
const char *if_name = node->name;
struct iface *iface = node->data;
- struct dpif_port *dpif_port;
+ struct ofproto_port *ofproto_port;
const char *type;
int error;
type = iface ? iface->type : "internal";
- dpif_port = shash_find_data(&cur_ifaces, if_name);
+ ofproto_port = shash_find_data(&cur_ifaces, if_name);
/* If we have a port or a netdev already, and it's not the type we
* want, then delete the port (if any) and close the netdev (if
* any). */
- if ((dpif_port && strcmp(dpif_port->type, type))
+ if ((ofproto_port && strcmp(ofproto_port->type, type))
|| (iface && iface->netdev
&& strcmp(type, netdev_get_type(iface->netdev)))) {
- if (dpif_port) {
- error = ofproto_port_del(br->ofproto, dpif_port->port_no);
+ if (ofproto_port) {
+ error = ofproto_port_del(br->ofproto,
+ ofproto_port->ofp_port);
if (error) {
continue;
}
- dpif_port = NULL;
+ ofproto_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;
}
/* If the port doesn't exist or we don't have the netdev open,
* we need to do more work. */
- if (!dpif_port || (iface && !iface->netdev)) {
+ if (!ofproto_port || (iface && !iface->netdev)) {
struct netdev_options options;
struct netdev *netdev;
struct shash args;
}
/* Then add the port if we haven't already. */
- if (!dpif_port) {
- error = dpif_port_add(br->dpif, netdev, NULL);
+ if (!ofproto_port) {
+ error = ofproto_port_add(br->ofproto, netdev, NULL);
if (error) {
netdev_close(netdev);
if (error == EFBIG) {
- VLOG_ERR("ran out of valid port numbers on %s",
- dpif_name(br->dpif));
+ VLOG_ERR("bridge %s: out of valid port numbers",
+ br->name);
break;
} else {
- VLOG_WARN("failed to add %s interface to %s: %s",
- if_name, dpif_name(br->dpif),
- strerror(error));
+ VLOG_WARN("bridge %s: failed to add %s interface "
+ "(%s)",
+ br->name, if_name, strerror(error));
continue;
}
}
shash_destroy(&want_ifaces);
SHASH_FOR_EACH (node, &cur_ifaces) {
- struct dpif_port *port_info = node->data;
- dpif_port_destroy(port_info);
+ struct ofproto_port *port_info = node->data;
+ ofproto_port_destroy(port_info);
free(port_info);
}
shash_destroy(&cur_ifaces);
uint8_t ea[ETH_ADDR_LEN];
uint64_t dpid;
struct iface *local_iface;
+ struct port *port, *next_port;
struct iface *hw_addr_iface;
char *dpid_string;
/* Delete interfaces that cannot be opened.
*
- * From this point forward we are guaranteed that every "struct iface"
- * has nonnull 'netdev' and correct 'dp_ifidx'. */
- iterate_and_prune_ifaces(br, check_iface, NULL);
+ * Following this loop, every remaining "struct iface" has nonnull
+ * 'netdev' and correct 'dp_ifidx'. */
+ HMAP_FOR_EACH_SAFE (port, next_port, hmap_node, &br->ports) {
+ struct iface *iface, *next_iface;
+
+ LIST_FOR_EACH_SAFE (iface, next_iface, port_elem, &port->ifaces) {
+ if (iface->netdev && iface->dp_ifidx >= 0) {
+ VLOG_DBG("bridge %s: interface %s is on port %d",
+ br->name, iface->name, iface->dp_ifidx);
+ } else {
+ if (iface->netdev) {
+ VLOG_ERR("bridge %s: missing %s interface, dropping",
+ br->name, iface->name);
+ } else {
+ /* We already reported a related error, don't bother
+ * duplicating it. */
+ }
+
+ iface_set_ofport(iface->cfg, -1);
+ iface_destroy(iface);
+ }
+ }
+
+ if (list_is_empty(&port->ifaces)) {
+ VLOG_WARN("%s port has no interfaces, dropping", port->name);
+ port_destroy(port);
+ }
+ }
/* Pick local port hardware address, datapath ID. */
bridge_pick_local_hw_addr(br, ea, &hw_addr_iface);
memset(&opts, 0, sizeof opts);
- dpif_get_netflow_ids(br->dpif, &opts.engine_type, &opts.engine_id);
+ ofproto_get_netflow_ids(br->ofproto,
+ &opts.engine_type, &opts.engine_id);
if (nf_cfg->engine_type) {
opts.engine_type = *nf_cfg->engine_type;
}
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) {
iface_update_qos(iface, port->cfg->qos);
+ netdev_set_policing(iface->netdev,
+ iface->cfg->ingress_policing_rate,
+ iface->cfg->ingress_policing_burst);
+ iface_set_mac(iface);
}
}
}
- LIST_FOR_EACH (br, node, &all_bridges) {
- iterate_and_prune_ifaces(br, set_iface_properties, NULL);
- }
/* Some reconfiguration operations require the bridge to have been run at
* least once. */
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);
}
}
assert(!bridge_lookup(br_cfg->name));
br = xzalloc(sizeof *br);
- error = dpif_create_and_open(br_cfg->name, br_cfg->datapath_type,
- &br->dpif);
- if (error) {
- free(br);
- return NULL;
- }
-
error = ofproto_create(br_cfg->name, br_cfg->datapath_type, &bridge_ofhooks,
br, &br->ofproto);
if (error) {
VLOG_ERR("failed to create switch %s: %s", br_cfg->name,
strerror(error));
- dpif_delete(br->dpif);
- dpif_close(br->dpif);
free(br);
return NULL;
}
hmap_init(&br->ports);
hmap_init(&br->ifaces);
- shash_init(&br->iface_by_name);
+ hmap_init(&br->iface_by_name);
br->flush = false;
list_push_back(&all_bridges, &br->node);
- VLOG_INFO("created bridge %s on %s", br->name, dpif_name(br->dpif));
+ VLOG_INFO("bridge %s: created", br->name);
return br;
}
{
if (br) {
struct port *port, *next;
- int error;
+ int i;
HMAP_FOR_EACH_SAFE (port, next, hmap_node, &br->ports) {
port_destroy(port);
}
- list_remove(&br->node);
- ofproto_destroy(br->ofproto);
- error = dpif_delete(br->dpif);
- if (error && error != ENOENT) {
- VLOG_ERR("failed to delete %s: %s",
- dpif_name(br->dpif), strerror(error));
+ for (i = 0; i < MAX_MIRRORS; i++) {
+ mirror_destroy(br->mirrors[i]);
}
- dpif_close(br->dpif);
+ list_remove(&br->node);
+ ofproto_destroy_and_delete(br->ofproto);
mac_learning_destroy(br->ml);
hmap_destroy(&br->ifaces);
hmap_destroy(&br->ports);
- shash_destroy(&br->iface_by_name);
+ hmap_destroy(&br->iface_by_name);
+ free(br->synth_local_iface.type);
free(br->name);
free(br);
}
br->name, name);
}
}
-
- /* 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];
+ if (bridge_get_controllers(br, NULL)
+ && !shash_find(&new_ports, br->name)) {
+ struct ofproto_port local_port;
+ char *type;
int error;
- 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);
- }
+ VLOG_WARN("bridge %s: no port named %s, synthesizing one",
+ br->name, br->name);
+
+ error = ofproto_port_query_by_name(br->ofproto, br->name, &local_port);
+ type = xstrdup(error ? "internal" : local_port.type);
+ ofproto_port_destroy(&local_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);
/* Prevent remote ovsdb-server users from accessing arbitrary Unix
* domain sockets and overwriting arbitrary local files. */
- VLOG_ERR_RL(&rl, "%s: not adding Unix domain socket controller "
- "\"%s\" due to possibility for remote exploit",
- dpif_name(br->dpif), c->target);
+ VLOG_ERR_RL(&rl, "bridge %s: not adding Unix domain socket "
+ "controller \"%s\" due to possibility for remote "
+ "exploit", br->name, c->target);
continue;
}
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 void
bridge_fetch_dp_ifaces(struct bridge *br)
{
- struct dpif_port_dump dump;
- struct dpif_port dpif_port;
+ struct ofproto_port ofproto_port;
+ struct ofproto_port_dump dump;
struct port *port;
/* Reset all interface numbers. */
}
hmap_clear(&br->ifaces);
- DPIF_PORT_FOR_EACH (&dpif_port, &dump, br->dpif) {
- struct iface *iface = iface_lookup(br, dpif_port.name);
+ OFPROTO_PORT_FOR_EACH (&ofproto_port, &dump, br->ofproto) {
+ struct iface *iface = iface_lookup(br, ofproto_port.name);
if (iface) {
+ uint32_t odp_port = ofp_port_to_odp_port(ofproto_port.ofp_port);
+
if (iface->dp_ifidx >= 0) {
- VLOG_WARN("%s reported interface %s twice",
- dpif_name(br->dpif), dpif_port.name);
- } else if (iface_from_dp_ifidx(br, dpif_port.port_no)) {
- VLOG_WARN("%s reported interface %"PRIu16" twice",
- dpif_name(br->dpif), dpif_port.port_no);
+ VLOG_WARN("bridge %s: interface %s reported twice",
+ br->name, ofproto_port.name);
+ } else if (iface_from_dp_ifidx(br, odp_port)) {
+ VLOG_WARN("bridge %s: interface %"PRIu16" reported twice",
+ br->name, odp_port);
} else {
- iface->dp_ifidx = dpif_port.port_no;
+ iface->dp_ifidx = odp_port;
hmap_insert(&br->ifaces, &iface->dp_ifidx_node,
hash_int(iface->dp_ifidx, 0));
}
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
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;
}
\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);
}
static void
port_wait(struct port *port)
{
+ if (port->lacp) {
+ lacp_wait(port->lacp);
+ }
+
if (port->bond) {
bond_wait(port->bond);
}
/* 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);
bitmap_free(port->trunks);
}
}
-static struct lacp_settings *
-port_reconfigure_bond_lacp(struct port *port, struct lacp_settings *s)
+static void
+iface_reconfigure_lacp(struct iface *iface)
{
- if (!enable_lacp(port, &s->active)) {
- return NULL;
- }
+ struct lacp_slave_settings s;
+ 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"));
- 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 (portid <= 0 || portid > UINT16_MAX) {
+ portid = iface->dp_ifidx;
+ }
- if (s->priority <= 0 || s->priority > UINT16_MAX) {
- /* Prefer bondable links if unspecified. */
- s->priority = UINT16_MAX - !list_is_short(&port->ifaces);
+ if (priority <= 0 || priority > UINT16_MAX) {
+ priority = UINT16_MAX;
}
- return s;
+
+ s.name = iface->name;
+ s.id = portid;
+ s.priority = priority;
+ lacp_slave_register(iface->port->lacp, iface, &s);
}
static void
-iface_reconfigure_bond(struct iface *iface)
+port_reconfigure_lacp(struct port *port)
{
- struct lacp_slave_settings 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;
- 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);
- bond_slave_register(iface->port->bond, iface, iface->netdev, &s);
+ if (!enable_lacp(port, &s.active)) {
+ lacp_destroy(port->lacp);
+ port->lacp = NULL;
+ 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;
+
+ /* 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) {
+ 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;
}
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);
}
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);
}
}
iface->netdev = NULL;
iface->cfg = if_cfg;
- shash_add_assert(&br->iface_by_name, iface->name, iface);
+ hmap_insert(&br->iface_by_name, &iface->name_node, hash_string(name, 0));
list_push_back(&port->ifaces, &iface->port_elem);
bond_slave_unregister(port->bond, iface);
}
- shash_find_and_delete_assert(&br->iface_by_name, iface->name);
+ if (port->lacp) {
+ lacp_slave_unregister(port->lacp, iface);
+ }
if (iface->dp_ifidx >= 0) {
hmap_remove(&br->ifaces, &iface->dp_ifidx_node);
}
list_remove(&iface->port_elem);
+ hmap_remove(&br->iface_by_name, &iface->name_node);
netdev_close(iface->netdev);
static struct iface *
iface_lookup(const struct bridge *br, const char *name)
{
- return shash_find_data(&br->iface_by_name, name);
+ struct iface *iface;
+
+ HMAP_FOR_EACH_WITH_HASH (iface, name_node, hash_string(name, 0),
+ &br->iface_by_name) {
+ if (!strcmp(iface->name, name)) {
+ return iface;
+ }
+ }
+
+ return NULL;
}
static struct 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) {
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);
}
+ free(rspan_vlans);
}
static void
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;