+ iface = port_lookup_iface(port, slave_s);
+ if (!iface) {
+ unixctl_command_reply(conn, 501, "no such slave");
+ return;
+ }
+
+ if (!iface->enabled) {
+ unixctl_command_reply(conn, 501, "cannot make disabled slave active");
+ return;
+ }
+
+ if (port->active_iface != iface->port_ifidx) {
+ ofproto_revalidate(port->bridge->ofproto, port->active_iface_tag);
+ port->active_iface = iface->port_ifidx;
+ port->active_iface_tag = tag_create_random();
+ VLOG_INFO("port %s: active interface is now %s",
+ port->name, iface->name);
+ bond_send_learning_packets(port);
+ unixctl_command_reply(conn, 200, "done");
+ } else {
+ unixctl_command_reply(conn, 200, "no change");
+ }
+}
+
+static void
+enable_slave(struct unixctl_conn *conn, const char *args_, bool enable)
+{
+ char *args = (char *) args_;
+ char *save_ptr = NULL;
+ char *bond_s, *slave_s;
+ struct port *port;
+ struct iface *iface;
+
+ bond_s = strtok_r(args, " ", &save_ptr);
+ slave_s = strtok_r(NULL, " ", &save_ptr);
+ if (!slave_s) {
+ unixctl_command_reply(conn, 501,
+ "usage: bond/enable/disable-slave BOND SLAVE");
+ return;
+ }
+
+ port = bond_find(bond_s);
+ if (!port) {
+ unixctl_command_reply(conn, 501, "no such bond");
+ return;
+ }
+
+ iface = port_lookup_iface(port, slave_s);
+ if (!iface) {
+ unixctl_command_reply(conn, 501, "no such slave");
+ return;
+ }
+
+ bond_enable_slave(iface, enable);
+ unixctl_command_reply(conn, 501, enable ? "enabled" : "disabled");
+}
+
+static void
+bond_unixctl_enable_slave(struct unixctl_conn *conn, const char *args,
+ void *aux OVS_UNUSED)
+{
+ enable_slave(conn, args, true);
+}
+
+static void
+bond_unixctl_disable_slave(struct unixctl_conn *conn, const char *args,
+ void *aux OVS_UNUSED)
+{
+ enable_slave(conn, args, false);
+}
+
+static void
+bond_unixctl_hash(struct unixctl_conn *conn, const char *args,
+ void *aux OVS_UNUSED)
+{
+ uint8_t mac[ETH_ADDR_LEN];
+ uint8_t hash;
+ char *hash_cstr;
+
+ if (sscanf(args, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(mac))
+ == ETH_ADDR_SCAN_COUNT) {
+ hash = bond_hash(mac);
+
+ hash_cstr = xasprintf("%u", hash);
+ unixctl_command_reply(conn, 200, hash_cstr);
+ free(hash_cstr);
+ } else {
+ unixctl_command_reply(conn, 501, "invalid mac");
+ }
+}
+
+static void
+bond_init(void)
+{
+ unixctl_command_register("bond/list", bond_unixctl_list, NULL);
+ unixctl_command_register("bond/show", bond_unixctl_show, NULL);
+ unixctl_command_register("bond/migrate", bond_unixctl_migrate, NULL);
+ unixctl_command_register("bond/set-active-slave",
+ bond_unixctl_set_active_slave, NULL);
+ unixctl_command_register("bond/enable-slave", bond_unixctl_enable_slave,
+ NULL);
+ unixctl_command_register("bond/disable-slave", bond_unixctl_disable_slave,
+ NULL);
+ unixctl_command_register("bond/hash", bond_unixctl_hash, NULL);
+}
+\f
+/* Port functions. */
+
+static struct port *
+port_create(struct bridge *br, const char *name)
+{
+ struct port *port;
+
+ port = xzalloc(sizeof *port);
+ port->bridge = br;
+ port->port_idx = br->n_ports;
+ port->vlan = -1;
+ port->trunks = NULL;
+ port->name = xstrdup(name);
+ port->active_iface = -1;
+
+ if (br->n_ports >= br->allocated_ports) {
+ br->ports = x2nrealloc(br->ports, &br->allocated_ports,
+ sizeof *br->ports);
+ }
+ br->ports[br->n_ports++] = port;
+ shash_add_assert(&br->port_by_name, port->name, port);
+
+ VLOG_INFO("created port %s on bridge %s", port->name, br->name);
+ bridge_flush(br);
+
+ return port;
+}
+
+static const char *
+get_port_other_config(const struct ovsrec_port *port, const char *key,
+ const char *default_value)
+{
+ const char *value;
+
+ value = get_ovsrec_key_value(&port->header_, &ovsrec_port_col_other_config,
+ key);
+ return value ? value : default_value;
+}
+
+static void
+port_del_ifaces(struct port *port, const struct ovsrec_port *cfg)
+{
+ struct shash new_ifaces;
+ size_t i;
+
+ /* Collect list of new interfaces. */
+ shash_init(&new_ifaces);
+ for (i = 0; i < cfg->n_interfaces; i++) {
+ const char *name = cfg->interfaces[i]->name;
+ shash_add_once(&new_ifaces, name, NULL);
+ }
+
+ /* Get rid of deleted interfaces. */
+ for (i = 0; i < port->n_ifaces; ) {
+ if (!shash_find(&new_ifaces, cfg->interfaces[i]->name)) {
+ iface_destroy(port->ifaces[i]);
+ } else {
+ i++;
+ }
+ }
+
+ shash_destroy(&new_ifaces);
+}
+
+static void
+port_reconfigure(struct port *port, const struct ovsrec_port *cfg)
+{
+ struct shash new_ifaces;
+ long long int next_rebalance;
+ unsigned long *trunks;
+ int vlan;
+ size_t i;
+
+ port->cfg = cfg;
+
+ /* Update settings. */
+ port->updelay = cfg->bond_updelay;
+ if (port->updelay < 0) {
+ port->updelay = 0;
+ }
+ port->updelay = cfg->bond_downdelay;
+ if (port->downdelay < 0) {
+ port->downdelay = 0;
+ }
+ port->bond_rebalance_interval = atoi(
+ get_port_other_config(cfg, "bond-rebalance-interval", "10000"));
+ if (port->bond_rebalance_interval < 1000) {
+ port->bond_rebalance_interval = 1000;
+ }
+ next_rebalance = time_msec() + port->bond_rebalance_interval;
+ if (port->bond_next_rebalance > next_rebalance) {
+ port->bond_next_rebalance = next_rebalance;
+ }
+
+ /* Add new interfaces and update 'cfg' member of existing ones. */
+ shash_init(&new_ifaces);
+ for (i = 0; i < cfg->n_interfaces; i++) {
+ const struct ovsrec_interface *if_cfg = cfg->interfaces[i];
+ struct iface *iface;
+
+ if (!shash_add_once(&new_ifaces, if_cfg->name, NULL)) {
+ VLOG_WARN("port %s: %s specified twice as port interface",
+ port->name, if_cfg->name);
+ continue;
+ }
+
+ iface = iface_lookup(port->bridge, if_cfg->name);
+ if (iface) {
+ if (iface->port != port) {
+ VLOG_ERR("bridge %s: %s interface is on multiple ports, "
+ "removing from %s",
+ port->bridge->name, if_cfg->name, iface->port->name);
+ continue;
+ }
+ iface->cfg = if_cfg;
+ } else {
+ iface_create(port, if_cfg);
+ }
+ }
+ shash_destroy(&new_ifaces);
+
+ /* Get VLAN tag. */
+ vlan = -1;
+ if (cfg->tag) {
+ if (port->n_ifaces < 2) {
+ vlan = *cfg->tag;
+ if (vlan >= 0 && vlan <= 4095) {
+ VLOG_DBG("port %s: assigning VLAN tag %d", port->name, vlan);
+ } else {
+ vlan = -1;
+ }
+ } else {
+ /* It's possible that bonded, VLAN-tagged ports make sense. Maybe
+ * they even work as-is. But they have not been tested. */
+ VLOG_WARN("port %s: VLAN tags not supported on bonded ports",
+ port->name);
+ }
+ }
+ if (port->vlan != vlan) {
+ port->vlan = vlan;
+ bridge_flush(port->bridge);
+ }
+
+ /* Get trunked VLANs. */
+ trunks = NULL;
+ if (vlan < 0 && cfg->n_trunks) {
+ size_t n_errors;
+ size_t i;
+
+ 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) {
+ 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)) {
+ bridge_flush(port->bridge);
+ }
+ bitmap_free(port->trunks);
+ port->trunks = trunks;
+}
+
+static void
+port_destroy(struct port *port)
+{
+ if (port) {
+ struct bridge *br = port->bridge;
+ struct port *del;
+ int 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];
+ if (m && m->out_port == port) {
+ mirror_destroy(m);
+ }
+ }
+
+ while (port->n_ifaces > 0) {
+ iface_destroy(port->ifaces[port->n_ifaces - 1]);
+ }
+
+ shash_find_and_delete_assert(&br->port_by_name, port->name);
+
+ del = br->ports[port->port_idx] = br->ports[--br->n_ports];
+ del->port_idx = port->port_idx;