Merge citrix into master.
[sliver-openvswitch.git] / vswitchd / bridge.c
index 22fe19a..ea0641e 100644 (file)
@@ -131,6 +131,7 @@ struct port {
     tag_type active_iface_tag;  /* Tag for bcast flows. */
     tag_type no_ifaces_tag;     /* Tag for flows when all ifaces disabled. */
     int updelay, downdelay;     /* Delay before iface goes up/down, in ms. */
+    bool bond_compat_is_stale;  /* Need to call port_update_bond_compat()? */
 
     /* Port mirroring info. */
     mirror_mask_t src_mirrors;  /* Mirrors triggered when packet received. */
@@ -193,6 +194,7 @@ enum { DP_MAX = 256 };
 static struct bridge *bridge_create(const char *name);
 static void bridge_destroy(struct bridge *);
 static struct bridge *bridge_lookup(const char *name);
+static void bridge_unixctl_dump_flows(struct unixctl_conn *, const char *);
 static int bridge_run_one(struct bridge *);
 static void bridge_reconfigure_one(struct bridge *);
 static void bridge_reconfigure_controller(struct bridge *);
@@ -311,6 +313,8 @@ bridge_init(void)
         }
     }
 
+    unixctl_command_register("bridge/dump-flows", bridge_unixctl_dump_flows);
+
     bond_init();
     bridge_reconfigure();
 }
@@ -526,9 +530,23 @@ bridge_reconfigure(void)
 
         for (i = 0; i < add_ifaces.n; i++) {
             const char *if_name = add_ifaces.names[i];
-            int internal = cfg_get_bool(0, "iface.%s.internal", if_name);
-            int flags = internal ? ODP_PORT_INTERNAL : 0;
-            int error = dpif_port_add(br->dpif, if_name, flags, NULL);
+            bool internal;
+            int error;
+
+            /* It's an internal interface if it's marked that way, or if
+             * it's a bonded interface for which we're faking up a network
+             * device. */
+            internal = cfg_get_bool(0, "iface.%s.internal", if_name);
+            if (cfg_get_bool(0, "bonding.%s.fake-iface", if_name)) {
+                struct port *port = port_lookup(br, if_name);
+                if (port && port->n_ifaces > 1) {
+                    internal = true;
+                }
+            }
+
+            /* Add to datapath. */
+            error = dpif_port_add(br->dpif, if_name,
+                                  internal ? ODP_PORT_INTERNAL : 0, NULL);
             if (error == EXFULL) {
                 VLOG_ERR("ran out of valid port numbers on %s",
                          dpif_name(br->dpif));
@@ -657,31 +675,74 @@ bridge_pick_local_hw_addr(struct bridge *br, uint8_t ea[ETH_ADDR_LEN],
     memset(ea, 0xff, sizeof ea);
     for (i = 0; i < br->n_ports; i++) {
         struct port *port = br->ports[i];
+        uint8_t iface_ea[ETH_ADDR_LEN];
+        uint64_t iface_ea_u64;
+        struct iface *iface;
+
+        /* Mirror output ports don't participate. */
         if (port->is_mirror_output_port) {
             continue;
         }
-        for (j = 0; j < port->n_ifaces; j++) {
-            struct iface *iface = port->ifaces[j];
-            uint8_t iface_ea[ETH_ADDR_LEN];
+
+        /* Choose the MAC address to represent the port. */
+        iface_ea_u64 = cfg_get_mac(0, "port.%s.mac", port->name);
+        if (iface_ea_u64) {
+            /* User specified explicitly. */
+            eth_addr_from_uint64(iface_ea_u64, iface_ea);
+
+            /* Find the interface with this Ethernet address (if any) so that
+             * we can provide the correct devname to the caller. */
+            iface = NULL;
+            for (j = 0; j < port->n_ifaces; j++) {
+                struct iface *candidate = port->ifaces[j];
+                uint8_t candidate_ea[ETH_ADDR_LEN];
+                if (!netdev_get_etheraddr(candidate->netdev, candidate_ea)
+                    && eth_addr_equals(iface_ea, candidate_ea)) {
+                    iface = candidate;
+                }
+            }
+        } else {
+            /* Choose the interface whose MAC address will represent the port.
+             * The Linux kernel bonding code always chooses the MAC address of
+             * the first slave added to a bond, and the Fedora networking
+             * scripts always add slaves to a bond in alphabetical order, so
+             * for compatibility we choose the interface with the name that is
+             * first in alphabetical order. */
+            iface = port->ifaces[0];
+            for (j = 1; j < port->n_ifaces; j++) {
+                struct iface *candidate = port->ifaces[j];
+                if (strcmp(candidate->name, iface->name) < 0) {
+                    iface = candidate;
+                }
+            }
+
+            /* The local port doesn't count (since we're trying to choose its
+             * MAC address anyway).  Other internal ports don't count because
+             * we really want a physical MAC if we can get it, and internal
+             * ports typically have randomly generated MACs. */
             if (iface->dp_ifidx == ODPP_LOCAL
                 || cfg_get_bool(0, "iface.%s.internal", iface->name)) {
                 continue;
             }
+
+            /* Grab MAC. */
             error = netdev_get_etheraddr(iface->netdev, iface_ea);
-            if (!error) {
-                if (!eth_addr_is_multicast(iface_ea) &&
-                    !eth_addr_is_reserved(iface_ea) &&
-                    !eth_addr_is_zero(iface_ea) &&
-                    memcmp(iface_ea, ea, ETH_ADDR_LEN) < 0) {
-                    memcpy(ea, iface_ea, ETH_ADDR_LEN);
-                    *hw_addr_iface = iface;
-                }
-            } else {
+            if (error) {
                 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
                 VLOG_ERR_RL(&rl, "failed to obtain Ethernet address of %s: %s",
                             iface->name, strerror(error));
+                continue;
             }
         }
+
+        /* Compare against our current choice. */
+        if (!eth_addr_is_multicast(iface_ea) &&
+            !eth_addr_is_reserved(iface_ea) &&
+            !eth_addr_is_zero(iface_ea) &&
+            memcmp(iface_ea, ea, ETH_ADDR_LEN) < 0)
+        {
+            *hw_addr_iface = iface;
+        }
     }
     if (eth_addr_is_multicast(ea) || eth_addr_is_vif(ea)) {
         memcpy(ea, br->default_ea, ETH_ADDR_LEN);
@@ -990,6 +1051,27 @@ bridge_get_datapathid(const char *name)
     return br ? ofproto_get_datapath_id(br->ofproto) : 0;
 }
 
+/* Handle requests for a listing of all flows known by the OpenFlow
+ * stack, including those normally hidden. */
+static void
+bridge_unixctl_dump_flows(struct unixctl_conn *conn, const char *args)
+{
+    struct bridge *br;
+    struct ds results;
+    
+    br = bridge_lookup(args);
+    if (!br) {
+        unixctl_command_reply(conn, 501, "Unknown bridge");
+        return;
+    }
+
+    ds_init(&results);
+    ofproto_get_all_flows(br->ofproto, &results);
+
+    unixctl_command_reply(conn, 200, ds_cstr(&results));
+    ds_destroy(&results);
+}
+
 static int
 bridge_run_one(struct bridge *br)
 {
@@ -1236,7 +1318,7 @@ bridge_reconfigure_controller(struct bridge *br)
         if (probe < 5) {
             probe = cfg_get_int(0, "mgmt.inactivity-probe");
             if (probe < 5) {
-                probe = 15;
+                probe = 5;
             }
         }
         ofproto_set_probe_interval(br->ofproto, probe);
@@ -1245,7 +1327,7 @@ bridge_reconfigure_controller(struct bridge *br)
         if (!max_backoff) {
             max_backoff = cfg_get_int(0, "mgmt.max-backoff");
             if (!max_backoff) {
-                max_backoff = 15;
+                max_backoff = 8;
             }
         }
         ofproto_set_max_backoff(br->ofproto, max_backoff);
@@ -1316,9 +1398,12 @@ bridge_get_all_ifaces(const struct bridge *br, struct svec *ifaces)
             struct iface *iface = port->ifaces[j];
             svec_add(ifaces, iface->name);
         }
+        if (port->n_ifaces > 1
+            && cfg_get_bool(0, "bonding.%s.fake-iface", port->name)) {
+            svec_add(ifaces, port->name);
+        }
     }
-    svec_sort(ifaces);
-    assert(svec_is_unique(ifaces));
+    svec_sort_unique(ifaces);
 }
 
 /* For robustness, in case the administrator moves around datapath ports behind
@@ -1413,6 +1498,7 @@ choose_output_iface(const struct port *port, const uint8_t *dl_src,
                 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];
@@ -1511,6 +1597,12 @@ bond_run(struct bridge *br)
 
     for (i = 0; i < br->n_ports; i++) {
         struct port *port = br->ports[i];
+
+        if (port->bond_compat_is_stale) {
+            port->bond_compat_is_stale = false;
+            port_update_bond_compat(port);
+        }
+
         if (port->n_ifaces < 2) {
             continue;
         }
@@ -2292,6 +2384,7 @@ bond_rebalance_port(struct port *port)
             } else {
                 from++;
             }
+            port->bond_compat_is_stale = true;
         }
     }
 
@@ -2558,6 +2651,7 @@ bond_unixctl_migrate(struct unixctl_conn *conn, const char *args_)
     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");
 }
 
@@ -2822,6 +2916,7 @@ port_destroy(struct port *port)
         size_t 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];
@@ -2888,7 +2983,7 @@ port_update_bonding(struct port *port)
         if (port->bond_hash) {
             free(port->bond_hash);
             port->bond_hash = NULL;
-            proc_net_compat_update_bond(port->name, NULL);
+            port->bond_compat_is_stale = true;
         }
     } else {
         if (!port->bond_hash) {
@@ -2903,23 +2998,39 @@ port_update_bonding(struct port *port)
             port->no_ifaces_tag = tag_create_random();
             bond_choose_active_iface(port);
         }
-        port_update_bond_compat(port);
+        port->bond_compat_is_stale = true;
     }
 }
 
 static void
 port_update_bond_compat(struct port *port)
 {
+    struct compat_bond_hash compat_hashes[BOND_MASK + 1];
     struct compat_bond bond;
     size_t i;
 
     if (port->n_ifaces < 2) {
+        proc_net_compat_update_bond(port->name, NULL);
         return;
     }
 
     bond.up = false;
     bond.updelay = port->updelay;
     bond.downdelay = port->downdelay;
+
+    bond.n_hashes = 0;
+    bond.hashes = compat_hashes;
+    if (port->bond_hash) {
+        const struct bond_entry *e;
+        for (e = port->bond_hash; e <= &port->bond_hash[BOND_MASK]; e++) {
+            if (e->iface_idx >= 0 && e->iface_idx < port->n_ifaces) {
+                struct compat_bond_hash *cbh = &bond.hashes[bond.n_hashes++];
+                cbh->hash = e - port->bond_hash;
+                cbh->netdev_name = port->ifaces[e->iface_idx]->name;
+            }
+        }
+    }
+
     bond.n_slaves = port->n_ifaces;
     bond.slaves = xmalloc(port->n_ifaces * sizeof *bond.slaves);
     for (i = 0; i < port->n_ifaces; i++) {
@@ -2933,6 +3044,7 @@ port_update_bond_compat(struct port *port)
         }
         netdev_get_etheraddr(iface->netdev, slave->mac);
     }
+
     proc_net_compat_update_bond(port->name, &bond);
     free(bond.slaves);
 }