* 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;
/* 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 *);
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);
NULL);
unixctl_command_register("bridge/reconnect", bridge_unixctl_reconnect,
NULL);
+ lacp_init();
bond_init();
}
/* 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;
}
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) {
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);
}
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.
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)
{
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 (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);
}
- partition_dsts(&set, cur_vlan);
-
- for (i = 0; i < set.n; i++) {
- const struct dst *dst = &set.dsts[i];
+ /* 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);
}
nl_msg_put_u32(actions, ODP_ACTION_ATTR_OUTPUT, dst->iface->dp_ifidx);
}
+
dst_set_free(&set);
}
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;
}
}
}
+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);
}
static void
port_wait(struct port *port)
{
+ if (port->lacp) {
+ lacp_wait(port->lacp);
+ }
+
if (port->bond) {
bond_wait(port->bond);
}
port->cfg = cfg;
-
/* Add new interfaces and update 'cfg' member of existing ones. */
sset_init(&new_ifaces);
for (i = 0; i < cfg->n_interfaces; i++) {
const struct ovsrec_interface *if_cfg = cfg->interfaces[i];
struct iface *iface;
+ const char *type;
if (!sset_add(&new_ifaces, if_cfg->name)) {
VLOG_WARN("port %s: %s specified twice as port interface",
continue;
}
+ /* Determine interface type. The local port always has type
+ * "internal". Other ports take their type from the database and
+ * default to "system" if none is specified. */
+ type = (!strcmp(if_cfg->name, port->bridge->name) ? "internal"
+ : if_cfg->type[0] ? if_cfg->type
+ : "system");
+
iface = iface_lookup(port->bridge, if_cfg->name);
- if (iface) {
+ if (!strcmp(type, "null")) {
+ iface_destroy(iface);
+ continue;
+ } else if (iface) {
if (iface->port != port) {
VLOG_ERR("bridge %s: %s interface is on multiple ports, "
"removing from %s",
iface = iface_create(port, if_cfg);
}
- /* Determine interface type. The local port always has type
- * "internal". Other ports take their type from the database and
- * default to "system" if none is specified. */
- iface->type = (!strcmp(if_cfg->name, port->bridge->name) ? "internal"
- : if_cfg->type[0] ? if_cfg->type
- : "system");
+ iface->type = type;
}
sset_destroy(&new_ifaces);
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, key;
+
+ portid = atoi(get_interface_other_config(iface->cfg, "lacp-port-id", "0"));
+ priority = atoi(get_interface_other_config(iface->cfg,
+ "lacp-port-priority", "0"));
+ key = atoi(get_interface_other_config(iface->cfg, "lacp-aggregation-key",
+ "0"));
+
+ if (portid <= 0 || portid > UINT16_MAX) {
+ portid = iface->dp_ifidx;
}
- 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 (priority <= 0 || priority > UINT16_MAX) {
+ priority = UINT16_MAX;
+ }
- if (s->priority <= 0 || s->priority > UINT16_MAX) {
- /* Prefer bondable links if unspecified. */
- s->priority = UINT16_MAX - !list_is_short(&port->ifaces);
+ if (key < 0 || key > UINT16_MAX) {
+ key = 0;
}
- return s;
+
+ s.name = iface->name;
+ s.id = portid;
+ s.priority = priority;
+ s.key = key;
+ 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.heartbeat = !strcmp(get_port_other_config(port->cfg,
+ "lacp-heartbeat",
+ "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.up_delay = MAX(0, port->cfg->bond_updelay);
s.down_delay = MAX(0, port->cfg->bond_downdelay);
+ s.basis = atoi(get_port_other_config(port->cfg, "bond-hash-basis", "0"));
+
s.rebalance_interval = atoi(
get_port_other_config(port->cfg, "bond-rebalance-interval", "10000"));
if (s.rebalance_interval < 1000) {
}
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);
+ long long stable_id;
+
+ stable_id = atoll(get_interface_other_config(iface->cfg,
+ "bond-stable-id", "0"));
+
+ if (stable_id <= 0 || stable_id >= UINT32_MAX) {
+ stable_id = odp_port_to_ofp_port(iface->dp_ifidx);
+ }
+
+ bond_slave_register(iface->port->bond, iface, stable_id,
+ iface->netdev);
}
}
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) {
{
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. */
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;