- ds_init(&results);
- ofproto_get_all_flows(br->ofproto, &results);
-
- unixctl_command_reply(conn, 200, ds_cstr(&results));
- ds_destroy(&results);
-}
-
-/* "bridge/reconnect [BRIDGE]": makes BRIDGE drop all of its controller
- * connections and reconnect. If BRIDGE is not specified, then all bridges
- * drop their controller connections and reconnect. */
-static void
-bridge_unixctl_reconnect(struct unixctl_conn *conn,
- const char *args, void *aux OVS_UNUSED)
-{
- struct bridge *br;
- if (args[0] != '\0') {
- br = bridge_lookup(args);
- if (!br) {
- unixctl_command_reply(conn, 501, "Unknown bridge");
- return;
- }
- ofproto_reconnect_controllers(br->ofproto);
- } else {
- LIST_FOR_EACH (br, node, &all_bridges) {
- ofproto_reconnect_controllers(br->ofproto);
- }
- }
- unixctl_command_reply(conn, 200, NULL);
-}
-
-static int
-bridge_run_one(struct bridge *br)
-{
- struct port *port;
- int error;
-
- error = ofproto_run1(br->ofproto);
- if (error) {
- return error;
- }
-
- mac_learning_run(br->ml, ofproto_get_revalidate_set(br->ofproto));
-
- HMAP_FOR_EACH (port, hmap_node, &br->ports) {
- port_run(port);
- }
-
- error = ofproto_run2(br->ofproto, br->flush);
- br->flush = false;
-
- return error;
-}
-
-static size_t
-bridge_get_controllers(const struct bridge *br,
- struct ovsrec_controller ***controllersp)
-{
- struct ovsrec_controller **controllers;
- size_t n_controllers;
-
- controllers = br->cfg->controller;
- n_controllers = br->cfg->n_controller;
-
- if (n_controllers == 1 && !strcmp(controllers[0]->target, "none")) {
- controllers = NULL;
- n_controllers = 0;
- }
-
- if (controllersp) {
- *controllersp = controllers;
- }
- return n_controllers;
-}
-
-static void
-bridge_reconfigure_one(struct bridge *br)
-{
- enum ofproto_fail_mode fail_mode;
- struct svec snoops, old_snoops;
- struct port *port, *next;
- struct shash_node *node;
- struct shash new_ports;
- size_t i;
-
- /* Collect new ports. */
- shash_init(&new_ports);
- for (i = 0; i < br->cfg->n_ports; i++) {
- const char *name = br->cfg->ports[i]->name;
- if (!shash_add_once(&new_ports, name, br->cfg->ports[i])) {
- VLOG_WARN("bridge %s: %s specified twice as bridge port",
- 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];
- 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);
- }
- }
-
- /* Get rid of deleted ports.
- * Get rid of deleted interfaces on ports that still exist. */
- HMAP_FOR_EACH_SAFE (port, next, hmap_node, &br->ports) {
- const struct ovsrec_port *port_cfg;
-
- port_cfg = shash_find_data(&new_ports, port->name);
- if (!port_cfg) {
- port_destroy(port);
- } else {
- port_del_ifaces(port, port_cfg);
- }
- }
-
- /* Create new ports.
- * Add new interfaces to existing ports.
- * Reconfigure existing ports. */
- SHASH_FOR_EACH (node, &new_ports) {
- struct port *port = port_lookup(br, node->name);
- if (!port) {
- port = port_create(br, node->name);
- }
-
- port_reconfigure(port, node->data);
- if (!port->n_ifaces) {
- VLOG_WARN("bridge %s: port %s has no interfaces, dropping",
- br->name, port->name);
- port_destroy(port);
- }
- }
- shash_destroy(&new_ports);
-
- /* Set the fail-mode */
- fail_mode = !br->cfg->fail_mode
- || !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. */
- svec_init(&snoops);
- svec_add_nocopy(&snoops, xasprintf("punix:%s/%s.snoop",
- ovs_rundir(), br->name));
- svec_init(&old_snoops);
- ofproto_get_snoops(br->ofproto, &old_snoops);
- if (!svec_equal(&snoops, &old_snoops)) {
- ofproto_set_snoops(br->ofproto, &snoops);
- }
- svec_destroy(&snoops);
- svec_destroy(&old_snoops);
-
- mirror_reconfigure(br);
-}
-
-/* Initializes 'oc' appropriately as a management service controller for
- * 'br'.
- *
- * The caller must free oc->target when it is no longer needed. */
-static void
-bridge_ofproto_controller_for_mgmt(const struct bridge *br,
- struct ofproto_controller *oc)
-{
- oc->target = xasprintf("punix:%s/%s.mgmt", ovs_rundir(), br->name);
- oc->max_backoff = 0;
- oc->probe_interval = 60;
- oc->band = OFPROTO_OUT_OF_BAND;
- oc->rate_limit = 0;
- oc->burst_limit = 0;
-}
-
-/* Converts ovsrec_controller 'c' into an ofproto_controller in 'oc'. */
-static void
-bridge_ofproto_controller_from_ovsrec(const struct ovsrec_controller *c,
- struct ofproto_controller *oc)
-{
- oc->target = c->target;
- oc->max_backoff = c->max_backoff ? *c->max_backoff / 1000 : 8;
- oc->probe_interval = c->inactivity_probe ? *c->inactivity_probe / 1000 : 5;
- oc->band = (!c->connection_mode || !strcmp(c->connection_mode, "in-band")
- ? OFPROTO_IN_BAND : OFPROTO_OUT_OF_BAND);
- oc->rate_limit = c->controller_rate_limit ? *c->controller_rate_limit : 0;
- oc->burst_limit = (c->controller_burst_limit
- ? *c->controller_burst_limit : 0);
-}
-
-/* Configures the IP stack for 'br''s local interface properly according to the
- * configuration in 'c'. */
-static void
-bridge_configure_local_iface_netdev(struct bridge *br,
- struct ovsrec_controller *c)
-{
- struct netdev *netdev;
- struct in_addr mask, gateway;
-
- struct iface *local_iface;
- struct in_addr ip;
-
- /* If there's no local interface or no IP address, give up. */
- local_iface = iface_from_dp_ifidx(br, ODPP_LOCAL);
- if (!local_iface || !c->local_ip || !inet_aton(c->local_ip, &ip)) {
- return;
- }
-
- /* Bring up the local interface. */
- netdev = local_iface->netdev;
- netdev_turn_flags_on(netdev, NETDEV_UP, true);
-
- /* Configure the IP address and netmask. */
- if (!c->local_netmask
- || !inet_aton(c->local_netmask, &mask)
- || !mask.s_addr) {
- mask.s_addr = guess_netmask(ip.s_addr);
- }
- if (!netdev_set_in4(netdev, ip, mask)) {
- VLOG_INFO("bridge %s: configured IP address "IP_FMT", netmask "IP_FMT,
- br->name, IP_ARGS(&ip.s_addr), IP_ARGS(&mask.s_addr));
- }
-
- /* Configure the default gateway. */
- if (c->local_gateway
- && inet_aton(c->local_gateway, &gateway)
- && gateway.s_addr) {
- if (!netdev_add_router(netdev, gateway)) {
- VLOG_INFO("bridge %s: configured gateway "IP_FMT,
- br->name, IP_ARGS(&gateway.s_addr));
- }
- }
-}
-
-static void
-bridge_reconfigure_remotes(struct bridge *br,
- const struct sockaddr_in *managers,
- size_t n_managers)
-{
- const char *disable_ib_str, *queue_id_str;
- bool disable_in_band = false;
- int queue_id;
-
- struct ovsrec_controller **controllers;
- size_t n_controllers;
- bool had_primary;
-
- struct ofproto_controller *ocs;
- size_t n_ocs;
- size_t i;
-
- /* Check if we should disable in-band control on this bridge. */
- disable_ib_str = bridge_get_other_config(br->cfg, "disable-in-band");
- if (disable_ib_str && !strcmp(disable_ib_str, "true")) {
- disable_in_band = true;
- }
-
- /* Set OpenFlow queue ID for in-band control. */
- queue_id_str = bridge_get_other_config(br->cfg, "in-band-queue");
- queue_id = queue_id_str ? strtol(queue_id_str, NULL, 10) : -1;
- ofproto_set_in_band_queue(br->ofproto, queue_id);
-
- if (disable_in_band) {
- ofproto_set_extra_in_band_remotes(br->ofproto, NULL, 0);
- } 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);
-
- ocs = xmalloc((n_controllers + 1) * sizeof *ocs);
- n_ocs = 0;
-
- bridge_ofproto_controller_for_mgmt(br, &ocs[n_ocs++]);
- for (i = 0; i < n_controllers; i++) {
- struct ovsrec_controller *c = controllers[i];
-
- if (!strncmp(c->target, "punix:", 6)
- || !strncmp(c->target, "unix:", 5)) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-
- /* 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);
- continue;
- }
-
- bridge_configure_local_iface_netdev(br, c);
- bridge_ofproto_controller_from_ovsrec(c, &ocs[n_ocs]);
- if (disable_in_band) {
- ocs[n_ocs].band = OFPROTO_OUT_OF_BAND;
- }
- n_ocs++;
- }
-
- 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
-bridge_get_all_ifaces(const struct bridge *br, struct shash *ifaces)
-{
- struct port *port;
-
- shash_init(ifaces);
- HMAP_FOR_EACH (port, hmap_node, &br->ports) {
- struct iface *iface;
-
- LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
- shash_add_once(ifaces, iface->name, iface);
- }
- if (port->n_ifaces > 1 && port->cfg->bond_fake_iface) {
- shash_add_once(ifaces, port->name, NULL);
- }
- }
-}
-
-/* For robustness, in case the administrator moves around datapath ports behind
- * our back, we re-check all the datapath port numbers here.
- *
- * This function will set the 'dp_ifidx' members of interfaces that have
- * disappeared to -1, so only call this function from a context where those
- * 'struct iface's will be removed from the bridge. Otherwise, the -1
- * 'dp_ifidx'es will cause trouble later when we try to send them to the
- * datapath, which doesn't support UINT16_MAX+1 ports. */
-static void
-bridge_fetch_dp_ifaces(struct bridge *br)
-{
- struct dpif_port_dump dump;
- struct dpif_port dpif_port;
- struct port *port;
-
- /* Reset all interface numbers. */
- HMAP_FOR_EACH (port, hmap_node, &br->ports) {
- struct iface *iface;
-
- LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
- iface->dp_ifidx = -1;
- }
- }
- hmap_clear(&br->ifaces);
-
- DPIF_PORT_FOR_EACH (&dpif_port, &dump, br->dpif) {
- struct iface *iface = iface_lookup(br, dpif_port.name);
- if (iface) {
- 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);
- } else {
- iface->dp_ifidx = dpif_port.port_no;
- hmap_insert(&br->ifaces, &iface->dp_ifidx_node,
- hash_int(iface->dp_ifidx, 0));
- }
-
- iface_set_ofport(iface->cfg,
- (iface->dp_ifidx >= 0
- ? odp_port_to_ofp_port(iface->dp_ifidx)
- : -1));
- }
- }
-}
-\f
-/* Bridge packet processing functions. */
-
-static bool
-bond_is_tcp_hash(const struct port *port)
-{
- return port->bond_mode == BM_TCP && lacp_negotiated(port->lacp);
-}
-
-static int
-bond_hash_src(const uint8_t mac[ETH_ADDR_LEN], uint16_t vlan)
-{
- return hash_bytes(mac, ETH_ADDR_LEN, vlan) & BOND_MASK;
-}
-
-static int bond_hash_tcp(const struct flow *flow, uint16_t vlan)
-{
- struct flow hash_flow;
-
- memcpy(&hash_flow, flow, sizeof hash_flow);
- hash_flow.vlan_tci = 0;
-
- /* The symmetric quality of this hash function is not required, but
- * flow_hash_symmetric_l4 already exists, and is sufficient for our
- * purposes, so we use it out of convenience. */
- return flow_hash_symmetric_l4(&hash_flow, vlan) & BOND_MASK;
-}
-
-static struct bond_entry *
-lookup_bond_entry(const struct port *port, const struct flow *flow,
- uint16_t vlan)
-{
- assert(port->bond_mode != BM_AB);
-
- if (bond_is_tcp_hash(port)) {
- return &port->bond_hash[bond_hash_tcp(flow, vlan)];
- } else {
- return &port->bond_hash[bond_hash_src(flow->dl_src, vlan)];
- }
-}
-
-static struct iface *
-bond_choose_iface(const struct port *port)
-{
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
- struct iface *best_down_slave;
- struct iface *iface;
-
- best_down_slave = NULL;
- LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
- if (iface->enabled) {
- return iface;
- } else if ((!best_down_slave
- || iface->delay_expires < best_down_slave->delay_expires)
- && lacp_slave_may_enable(port->lacp, iface)) {
- best_down_slave = iface;
- }
- }
-
- if (best_down_slave) {
- VLOG_INFO_RL(&rl, "interface %s: skipping remaining %lli ms updelay "
- "since no other interface is up",
- best_down_slave->name,
- best_down_slave->delay_expires - time_msec());
- bond_enable_slave(best_down_slave, true);
- }
-
- return best_down_slave;
-}
-
-static bool
-choose_output_iface(const struct port *port, const struct flow *flow,
- uint16_t vlan, uint16_t *dp_ifidx, tag_type *tags)
-{
- struct iface *iface;
-
- assert(port->n_ifaces);
- if (port->n_ifaces == 1) {
- iface = port_get_an_iface(port);
- } else if (port->bond_mode == BM_AB) {
- iface = port->active_iface;
- if (!iface) {
- *tags |= port->no_ifaces_tag;
- return false;
- }
- } else {
- struct bond_entry *e = lookup_bond_entry(port, flow, vlan);
- if (!e->iface || !e->iface->enabled) {
- /* XXX select interface properly. The current interface selection
- * is only good for testing the rebalancing code. */
- e->iface = bond_choose_iface(port);
- if (!e->iface) {
- *tags |= port->no_ifaces_tag;
- return false;
- }
- e->tag = tag_create_random();
- }
- *tags |= e->tag;
- iface = e->iface;
- }
- *dp_ifidx = iface->dp_ifidx;
- *tags |= iface->tag; /* Currently only used for bonding. */
- return true;
-}
-
-static void
-bond_link_status_update(struct iface *iface)
-{
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
- struct port *port = iface->port;
- bool up = iface->up && lacp_slave_may_enable(port->lacp, iface);
- int updelay, downdelay;
-
- updelay = port->updelay;
- downdelay = port->downdelay;
-
- if (lacp_negotiated(port->lacp)) {
- downdelay = 0;
- updelay = 0;
- }
-
- if ((up == iface->enabled) == (iface->delay_expires == LLONG_MAX)) {
- /* Nothing to do. */
- return;
- }
- VLOG_INFO_RL(&rl, "interface %s: link state %s",
- iface->name, up ? "up" : "down");
- if (up == iface->enabled) {
- iface->delay_expires = LLONG_MAX;
- VLOG_INFO_RL(&rl, "interface %s: will not be %s",
- iface->name, up ? "disabled" : "enabled");
- } else if (up && !port->active_iface) {
- bond_enable_slave(iface, true);
- if (updelay) {
- VLOG_INFO_RL(&rl, "interface %s: skipping %d ms updelay since no "
- "other interface is up", iface->name, updelay);
- }
- } else {
- int delay = up ? updelay : downdelay;
- iface->delay_expires = time_msec() + delay;
- if (delay) {
- VLOG_INFO_RL(&rl,
- "interface %s: will be %s if it stays %s for %d ms",
- iface->name,
- up ? "enabled" : "disabled",
- up ? "up" : "down",
- delay);
- }
- }
-}
-
-static void
-bond_choose_active_iface(struct port *port)
-{
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
-
- port->active_iface = bond_choose_iface(port);
- if (port->active_iface) {
- VLOG_INFO_RL(&rl, "port %s: active interface is now %s",
- port->name, port->active_iface->name);
- } else {
- VLOG_WARN_RL(&rl, "port %s: all ports disabled, no active interface",
- port->name);
- }
-}
-
-static void
-bond_enable_slave(struct iface *iface, bool enable)
-{
- struct port *port = iface->port;
- struct bridge *br = port->bridge;
-
- /* This acts as a recursion check. If the act of disabling a slave
- * causes a different slave to be enabled, the flag will allow us to
- * skip redundant work when we reenter this function. It must be
- * cleared on exit to keep things safe with multiple bonds. */
- static bool moving_active_iface = false;
-
- iface->delay_expires = LLONG_MAX;
- if (enable == iface->enabled) {
- return;
- }
-
- iface->enabled = enable;
- if (!iface->enabled) {
- VLOG_WARN("interface %s: disabled", iface->name);
- ofproto_revalidate(br->ofproto, iface->tag);
- if (iface == port->active_iface) {
- /* Disabling a slave can lead to another slave being immediately
- * enabled if there will be no active slaves but one is waiting
- * on an updelay. In this case we do not need to run most of the
- * code for the newly enabled slave since there was no period
- * without an active slave and it is redundant with the disabling
- * path. */
- moving_active_iface = true;
- bond_choose_active_iface(port);
- }
- bond_send_learning_packets(port);
- } else {
- VLOG_WARN("interface %s: enabled", iface->name);
- if (!port->active_iface && !moving_active_iface) {
- ofproto_revalidate(br->ofproto, port->no_ifaces_tag);
- bond_choose_active_iface(port);
- bond_send_learning_packets(port);
- }
- iface->tag = tag_create_random();
- }
-
- moving_active_iface = false;
-}
-
-/* Attempts to make the sum of the bond slaves' statistics appear on the fake
- * bond interface. */
-static void
-bond_update_fake_iface_stats(struct port *port)
-{
- struct netdev_stats bond_stats;
- struct netdev *bond_dev;
- struct iface *iface;
-
- memset(&bond_stats, 0, sizeof bond_stats);
-
- LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
- struct netdev_stats slave_stats;
-
- if (!netdev_get_stats(iface->netdev, &slave_stats)) {
- /* XXX: We swap the stats here because they are swapped back when
- * reported by the internal device. The reason for this is
- * internal devices normally represent packets going into the system
- * but when used as fake bond device they represent packets leaving
- * the system. We really should do this in the internal device
- * itself because changing it here reverses the counts from the
- * perspective of the switch. However, the internal device doesn't
- * know what type of device it represents so we have to do it here
- * for now. */
- bond_stats.tx_packets += slave_stats.rx_packets;
- bond_stats.tx_bytes += slave_stats.rx_bytes;
- bond_stats.rx_packets += slave_stats.tx_packets;
- bond_stats.rx_bytes += slave_stats.tx_bytes;
- }
- }
-
- if (!netdev_open_default(port->name, &bond_dev)) {
- netdev_set_stats(bond_dev, &bond_stats);
- netdev_close(bond_dev);
- }
-}
-
-static void
-bond_run(struct port *port)
-{
- struct iface *iface;
-
- if (port->n_ifaces < 2) {
- return;
- }
-
- LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
- bond_link_status_update(iface);
- }
-
- LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
- if (time_msec() >= iface->delay_expires) {
- bond_enable_slave(iface, !iface->enabled);
- }
- }
-
- if (port->bond_fake_iface
- && time_msec() >= port->bond_next_fake_iface_update) {
- bond_update_fake_iface_stats(port);
- port->bond_next_fake_iface_update = time_msec() + 1000;
- }
-}
-
-static void
-bond_wait(struct port *port)
-{
- struct iface *iface;
-
- if (port->n_ifaces < 2) {
- return;
- }
-
- LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
- if (iface->delay_expires != LLONG_MAX) {
- poll_timer_wait_until(iface->delay_expires);
- }
- }
-
- if (port->bond_fake_iface) {
- poll_timer_wait_until(port->bond_next_fake_iface_update);
- }
-}
-
-static bool
-set_dst(struct dst *dst, const struct flow *flow,
- const struct port *in_port, const struct port *out_port,
- tag_type *tags)
-{
- dst->vlan = (out_port->vlan >= 0 ? OFP_VLAN_NONE
- : in_port->vlan >= 0 ? in_port->vlan
- : flow->vlan_tci == 0 ? OFP_VLAN_NONE
- : vlan_tci_to_vid(flow->vlan_tci));
- return choose_output_iface(out_port, flow, dst->vlan,
- &dst->dp_ifidx, tags);
-}
-
-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)
-{
- BUILD_ASSERT_DECL(sizeof(unsigned int) >= sizeof(mask));
- return ffs(mask);
-}
-
-static void
-dst_set_init(struct dst_set *set)
-{
- set->dsts = set->builtin;
- set->n = 0;
- set->allocated = ARRAY_SIZE(set->builtin);
-}
-
-static void
-dst_set_add(struct dst_set *set, const struct dst *dst)
-{
- if (set->n >= set->allocated) {
- size_t new_allocated;
- struct dst *new_dsts;
-
- new_allocated = set->allocated * 2;
- new_dsts = xmalloc(new_allocated * sizeof *new_dsts);
- memcpy(new_dsts, set->dsts, set->n * sizeof *new_dsts);
-
- dst_set_free(set);
-
- set->dsts = new_dsts;
- set->allocated = new_allocated;
- }
- set->dsts[set->n++] = *dst;
-}
-
-static void
-dst_set_free(struct dst_set *set)
-{
- if (set->dsts != set->builtin) {
- free(set->dsts);
- }
-}
-
-static bool
-dst_is_duplicate(const struct dst_set *set, const struct dst *test)
-{
- size_t i;
- for (i = 0; i < set->n; i++) {
- if (set->dsts[i].vlan == test->vlan
- && set->dsts[i].dp_ifidx == test->dp_ifidx) {
- return true;
- }
- }
- return false;
-}
-
-static bool
-port_trunks_vlan(const struct port *port, uint16_t vlan)
-{
- return (port->vlan < 0
- && (!port->trunks || bitmap_is_set(port->trunks, vlan)));
-}
-
-static bool
-port_includes_vlan(const struct port *port, uint16_t vlan)