+ /* Hashes. */
+ for (be = port->bond_hash; be <= &port->bond_hash[BOND_MASK]; be++) {
+ int hash = be - port->bond_hash;
+ struct mac_entry *me;
+
+ if (be->iface_idx != j) {
+ continue;
+ }
+
+ ds_put_format(&ds, "\thash %d: %"PRIu64" kB load\n",
+ hash, be->tx_bytes / 1024);
+
+ /* MACs. */
+ LIST_FOR_EACH (me, struct mac_entry, lru_node,
+ &port->bridge->ml->lrus) {
+ uint16_t dp_ifidx;
+ tag_type tags = 0;
+ if (bond_hash(me->mac) == hash
+ && me->port != port->port_idx
+ && choose_output_iface(port, me->mac, &dp_ifidx, &tags)
+ && dp_ifidx == iface->dp_ifidx)
+ {
+ ds_put_format(&ds, "\t\t"ETH_ADDR_FMT"\n",
+ ETH_ADDR_ARGS(me->mac));
+ }
+ }
+ }
+ }
+ unixctl_command_reply(conn, 200, ds_cstr(&ds));
+ ds_destroy(&ds);
+}
+
+static void
+bond_unixctl_migrate(struct unixctl_conn *conn, const char *args_,
+ void *aux UNUSED)
+{
+ char *args = (char *) args_;
+ char *save_ptr = NULL;
+ char *bond_s, *hash_s, *slave_s;
+ uint8_t mac[ETH_ADDR_LEN];
+ struct port *port;
+ struct iface *iface;
+ struct bond_entry *entry;
+ int hash;
+
+ bond_s = strtok_r(args, " ", &save_ptr);
+ hash_s = strtok_r(NULL, " ", &save_ptr);
+ slave_s = strtok_r(NULL, " ", &save_ptr);
+ if (!slave_s) {
+ unixctl_command_reply(conn, 501,
+ "usage: bond/migrate BOND HASH SLAVE");
+ return;
+ }
+
+ port = bond_find(bond_s);
+ if (!port) {
+ unixctl_command_reply(conn, 501, "no such bond");
+ return;
+ }
+
+ 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;
+ } else {
+ unixctl_command_reply(conn, 501, "bad hash");
+ return;
+ }
+
+ 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 migrate to disabled slave");
+ return;
+ }
+
+ entry = &port->bond_hash[hash];
+ 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");
+}
+
+static void
+bond_unixctl_set_active_slave(struct unixctl_conn *conn, const char *args_,
+ void *aux UNUSED)
+{
+ 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/set-active-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;
+ }
+
+ 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 UNUSED)
+{
+ enable_slave(conn, args, true);
+}
+
+static void
+bond_unixctl_disable_slave(struct unixctl_conn *conn, const char *args,
+ void *aux UNUSED)
+{
+ enable_slave(conn, args, false);
+}
+
+static void
+bond_unixctl_hash(struct unixctl_conn *conn, const char *args,
+ void *aux 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;
+
+ VLOG_INFO("created port %s on bridge %s", port->name, br->name);
+ bridge_flush(br);
+
+ return port;
+}
+
+static void
+port_reconfigure(struct port *port, const struct ovsrec_port *cfg)
+{
+ struct shash old_ifaces, new_ifaces;
+ struct shash_node *node;
+ unsigned long *trunks;
+ int vlan;
+ size_t i;
+
+ port->cfg = cfg;
+
+ /* Collect old and new interfaces. */
+ shash_init(&old_ifaces);
+ shash_init(&new_ifaces);
+ for (i = 0; i < port->n_ifaces; i++) {
+ shash_add(&old_ifaces, port->ifaces[i]->name, port->ifaces[i]);
+ }
+ for (i = 0; i < cfg->n_interfaces; i++) {
+ const char *name = cfg->interfaces[i]->name;
+ if (!shash_add_once(&new_ifaces, name, cfg->interfaces[i])) {
+ VLOG_WARN("port %s: %s specified twice as port interface",
+ port->name, name);
+ }
+ }
+ port->updelay = cfg->bond_updelay;
+ if (port->updelay < 0) {
+ port->updelay = 0;
+ }
+ port->updelay = cfg->bond_downdelay;
+ if (port->downdelay < 0) {
+ port->downdelay = 0;
+ }
+
+ /* Get rid of deleted interfaces and add new interfaces. */
+ SHASH_FOR_EACH (node, &old_ifaces) {
+ if (!shash_find(&new_ifaces, node->name)) {
+ iface_destroy(node->data);
+ }
+ }
+ SHASH_FOR_EACH (node, &new_ifaces) {
+ const struct ovsrec_interface *if_cfg = node->data;
+ struct iface *iface;
+
+ iface = shash_find_data(&old_ifaces, if_cfg->name);
+ if (!iface) {
+ iface = iface_create(port, if_cfg);
+ }
+ iface->cfg = if_cfg;
+ }
+
+ /* Get VLAN tag. */