- if (iface->cfg) {
- int64_t ofport = (iface->dp_ifidx >= 0
- ? odp_port_to_ofp_port(iface->dp_ifidx)
- : -1);
- ovsrec_interface_set_ofport(iface->cfg, &ofport, 1);
- }
- }
- }
- free(dpif_ports);
-}
-\f
-/* Bridge packet processing functions. */
-
-static int
-bond_hash(const uint8_t mac[ETH_ADDR_LEN])
-{
- return hash_bytes(mac, ETH_ADDR_LEN, 0) & BOND_MASK;
-}
-
-static struct bond_entry *
-lookup_bond_entry(const struct port *port, const uint8_t mac[ETH_ADDR_LEN])
-{
- return &port->bond_hash[bond_hash(mac)];
-}
-
-static int
-bond_choose_iface(const struct port *port)
-{
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
- size_t i, best_down_slave = -1;
- long long next_delay_expiration = LLONG_MAX;
-
- for (i = 0; i < port->n_ifaces; i++) {
- struct iface *iface = port->ifaces[i];
-
- if (iface->enabled) {
- return i;
- } else if (iface->delay_expires < next_delay_expiration) {
- best_down_slave = i;
- next_delay_expiration = iface->delay_expires;
- }
- }
-
- if (best_down_slave != -1) {
- struct iface *iface = port->ifaces[best_down_slave];
-
- VLOG_INFO_RL(&rl, "interface %s: skipping remaining %lli ms updelay "
- "since no other interface is up", iface->name,
- iface->delay_expires - time_msec());
- bond_enable_slave(iface, true);
- }
-
- return best_down_slave;
-}
-
-static bool
-choose_output_iface(const struct port *port, const uint8_t *dl_src,
- uint16_t *dp_ifidx, tag_type *tags)
-{
- struct iface *iface;
-
- assert(port->n_ifaces);
- if (port->n_ifaces == 1) {
- iface = port->ifaces[0];
- } else {
- struct bond_entry *e = lookup_bond_entry(port, dl_src);
- if (e->iface_idx < 0 || e->iface_idx >= port->n_ifaces
- || !port->ifaces[e->iface_idx]->enabled) {
- /* XXX select interface properly. The current interface selection
- * is only good for testing the rebalancing code. */
- e->iface_idx = bond_choose_iface(port);
- if (e->iface_idx < 0) {
- *tags |= port->no_ifaces_tag;
- return false;
- }
- e->iface_tag = tag_create_random();
- ((struct port *) port)->bond_compat_is_stale = true;
- }
- *tags |= e->iface_tag;
- iface = port->ifaces[e->iface_idx];
- }
- *dp_ifidx = iface->dp_ifidx;
- *tags |= iface->tag; /* Currently only used for bonding. */
- return true;
-}
-
-static void
-bond_link_status_update(struct iface *iface, bool carrier)
-{
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
- struct port *port = iface->port;
-
- if ((carrier == iface->enabled) == (iface->delay_expires == LLONG_MAX)) {
- /* Nothing to do. */
- return;
- }
- VLOG_INFO_RL(&rl, "interface %s: carrier %s",
- iface->name, carrier ? "detected" : "dropped");
- if (carrier == iface->enabled) {
- iface->delay_expires = LLONG_MAX;
- VLOG_INFO_RL(&rl, "interface %s: will not be %s",
- iface->name, carrier ? "disabled" : "enabled");
- } else if (carrier && port->active_iface < 0) {
- bond_enable_slave(iface, true);
- if (port->updelay) {
- VLOG_INFO_RL(&rl, "interface %s: skipping %d ms updelay since no "
- "other interface is up", iface->name, port->updelay);
- }
- } else {
- int delay = carrier ? port->updelay : port->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,
- carrier ? "enabled" : "disabled",
- carrier ? "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);
- port->active_iface_tag = tag_create_random();
- if (port->active_iface >= 0) {
- VLOG_INFO_RL(&rl, "port %s: active interface is now %s",
- port->name, port->ifaces[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_ifidx == port->active_iface) {
- ofproto_revalidate(br->ofproto,
- port->active_iface_tag);
-
- /* 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 < 0 && !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;
- port->bond_compat_is_stale = true;
-}
-
-static void
-bond_run(struct bridge *br)
-{
- size_t i, j;
-
- for (i = 0; i < br->n_ports; i++) {
- struct port *port = br->ports[i];
-
- if (port->n_ifaces >= 2) {
- for (j = 0; j < port->n_ifaces; j++) {
- struct iface *iface = port->ifaces[j];
- if (time_msec() >= iface->delay_expires) {
- bond_enable_slave(iface, !iface->enabled);
- }
- }
- }
-
- if (port->bond_compat_is_stale) {
- port->bond_compat_is_stale = false;
- port_update_bond_compat(port);
- }
- }
-}
-
-static void
-bond_wait(struct bridge *br)
-{
- size_t i, j;
-
- for (i = 0; i < br->n_ports; i++) {
- struct port *port = br->ports[i];
- if (port->n_ifaces < 2) {
- continue;
- }
- for (j = 0; j < port->n_ifaces; j++) {
- struct iface *iface = port->ifaces[j];
- if (iface->delay_expires != LLONG_MAX) {
- poll_timer_wait(iface->delay_expires - time_msec());
- }
- }
- }
-}
-
-static bool
-set_dst(struct dst *p, const flow_t *flow,
- const struct port *in_port, const struct port *out_port,
- tag_type *tags)
-{
- p->vlan = (out_port->vlan >= 0 ? OFP_VLAN_NONE
- : in_port->vlan >= 0 ? in_port->vlan
- : ntohs(flow->dl_vlan));
- return choose_output_iface(out_port, flow->dl_src, &p->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 *dsts, size_t n_dsts, int vlan)
-{
- struct dst *first = dsts;
- struct dst *last = dsts + n_dsts;
-
- 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 bool
-dst_is_duplicate(const struct dst *dsts, size_t n_dsts,
- const struct dst *test)
-{
- size_t i;
- for (i = 0; i < n_dsts; i++) {
- if (dsts[i].vlan == test->vlan && 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 && bitmap_is_set(port->trunks, vlan);
-}
-
-static bool
-port_includes_vlan(const struct port *port, uint16_t vlan)
-{
- return vlan == port->vlan || port_trunks_vlan(port, vlan);
-}
-
-static size_t
-compose_dsts(const struct bridge *br, const flow_t *flow, uint16_t vlan,
- const struct port *in_port, const struct port *out_port,
- struct dst dsts[], tag_type *tags, uint16_t *nf_output_iface)
-{
- mirror_mask_t mirrors = in_port->src_mirrors;
- struct dst *dst = dsts;
- size_t i;
-
- if (out_port == FLOOD_PORT) {
- /* XXX use ODP_FLOOD if no vlans or bonding. */
- /* XXX even better, define each VLAN as a datapath port group */
- for (i = 0; i < br->n_ports; i++) {
- struct port *port = br->ports[i];
- if (port != in_port && port_includes_vlan(port, vlan)
- && !port->is_mirror_output_port
- && set_dst(dst, flow, in_port, port, tags)) {
- mirrors |= port->dst_mirrors;
- dst++;
- }
- }
- *nf_output_iface = NF_OUT_FLOOD;
- } else if (out_port && set_dst(dst, flow, in_port, out_port, tags)) {
- *nf_output_iface = dst->dp_ifidx;
- mirrors |= out_port->dst_mirrors;
- dst++;
- }
-
- while (mirrors) {
- struct mirror *m = br->mirrors[mirror_mask_ffs(mirrors) - 1];
- if (!m->n_vlans || vlan_is_mirrored(m, vlan)) {
- if (m->out_port) {
- if (set_dst(dst, flow, in_port, m->out_port, tags)
- && !dst_is_duplicate(dsts, dst - dsts, dst)) {
- dst++;
- }
- } else {
- for (i = 0; i < br->n_ports; i++) {
- struct port *port = br->ports[i];
- if (port_includes_vlan(port, m->out_vlan)
- && set_dst(dst, flow, in_port, port, tags))
- {
- int flow_vlan;
-
- if (port->vlan < 0) {
- dst->vlan = m->out_vlan;
- }
- if (dst_is_duplicate(dsts, dst - dsts, dst)) {
- continue;
- }
-
- /* Use the vlan tag on the original flow instead of
- * the one passed in the vlan parameter. This ensures
- * that we compare the vlan from before any implicit
- * tagging tags place. This is necessary because
- * dst->vlan is the final vlan, after removing implicit
- * tags. */
- flow_vlan = ntohs(flow->dl_vlan);
- if (flow_vlan == 0) {
- flow_vlan = OFP_VLAN_NONE;
- }
- if (port == in_port && dst->vlan == flow_vlan) {
- /* Don't send out input port on same VLAN. */
- continue;
- }
- dst++;