vswitchd: Choose the bridge local port MAC address intelligently.
authorBen Pfaff <blp@nicira.com>
Tue, 3 Mar 2009 19:44:24 +0000 (11:44 -0800)
committerBen Pfaff <blp@nicira.com>
Tue, 3 Mar 2009 21:51:21 +0000 (13:51 -0800)
Fixes bug #928, "We should have a consistent model for representing the
nic/mac address to xenserver."

vswitchd/bridge.c
vswitchd/vswitchd.conf.5

index 29d6b534c2ccbb5aafee53f284581911c5ce2da4..64a45c05ada36432b4bb45bef8bb528bf168b197 100644 (file)
@@ -150,6 +150,7 @@ struct bridge {
     int flow_idle_time;         /* Idle time for flows we set up. */
     bool sent_config_request;   /* Successfully sent config request? */
     bool sent_features_request; /* Successfully sent features request? */
+    uint8_t default_ea[ETH_ADDR_LEN]; /* Default MAC. */
 
     /* Support for remote controllers. */
     char *controller;           /* NULL if there is no remote controller;
@@ -211,6 +212,8 @@ static void bridge_get_all_ifaces(const struct bridge *, struct svec *ifaces);
 static bool bridge_is_backlogged(const struct bridge *);
 static void bridge_fetch_dp_ifaces(struct bridge *);
 static void bridge_flush(struct bridge *);
+static void bridge_pick_local_hw_addr(struct bridge *,
+                                      struct iface *local_iface);
 
 static void bridge_process_msg(struct bridge *, struct ofpbuf *);
 static void revalidate_flow(struct bridge *, struct ft_flow *);
@@ -392,6 +395,9 @@ bridge_reconfigure(void)
                              br->dp_idx, iface->name, iface->dp_ifidx);
                     j++;
                 }
+                if (!strcmp(iface->name, br->name)) {
+                    bridge_pick_local_hw_addr(br, iface);
+                }
             }
             if (!port->n_ifaces) {
                 VLOG_ERR("%s port has no interfaces, dropping", port->name);
@@ -406,6 +412,81 @@ bridge_reconfigure(void)
     }
 }
 
+static void
+bridge_set_local_hw_addr(struct bridge *br, struct iface *local_iface,
+                         const uint8_t ea[ETH_ADDR_LEN])
+{
+    int error = netdev_nodev_set_etheraddr(local_iface->name, ea);
+    if (error) {
+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+        VLOG_ERR_RL(&rl, "bridge %s: failed to set bridge Ethernet "
+                    "address: %s", br->name, strerror(error));
+    }
+}
+
+static void
+bridge_pick_local_hw_addr(struct bridge *br, struct iface *local_iface)
+{
+    uint8_t ea[ETH_ADDR_LEN];
+    uint64_t requested_ea;
+    size_t i, j;
+    int error;
+
+    /* Did the user request a particular MAC? */
+    requested_ea = cfg_get_mac(0, "bridge.%s.mac", br->name);
+    if (requested_ea) {
+        eth_addr_from_uint64(requested_ea, ea);
+        if (eth_addr_is_multicast(ea)) {
+            VLOG_ERR("bridge %s: cannot set MAC address to multicast "
+                     "address "ETH_ADDR_FMT, br->name, ETH_ADDR_ARGS(ea));
+        } else if (eth_addr_is_zero(ea)) {
+            VLOG_ERR("bridge %s: cannot set MAC address to zero", br->name);
+        } else {
+            bridge_set_local_hw_addr(br, local_iface, ea);
+            return;
+        }
+    }
+
+    /* Otherwise choose the minimum MAC address among all of the interfaces.
+     * (Xen uses FE:FF:FF:FF:FF:FF for virtual interfaces so this will get the
+     * MAC of the physical interface in such an environment.) */
+    memset(ea, 0xff, sizeof ea);
+    for (i = 0; i < br->n_ports; i++) {
+        struct port *port = br->ports[i];
+        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];
+            if (iface == local_iface) {
+                continue;
+            }
+            error = netdev_nodev_get_etheraddr(iface->name, iface_ea);
+            if (!error) {
+                if (!eth_addr_is_multicast(iface_ea) &&
+                    !eth_addr_is_reserved(iface_ea) &&
+                    memcmp(iface_ea, ea, ETH_ADDR_LEN) < 0) {
+                    memcpy(ea, iface_ea, ETH_ADDR_LEN);
+                }
+            } else {
+                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));
+            }
+        }
+    }
+    if (eth_addr_is_broadcast(ea)) {
+        memcpy(ea, br->default_ea, ETH_ADDR_LEN);
+        VLOG_WARN("bridge %s: using default bridge Ethernet "
+                  "address "ETH_ADDR_FMT, br->name, ETH_ADDR_ARGS(ea));
+    } else {
+        VLOG_DBG("bridge %s: using bridge Ethernet address "ETH_ADDR_FMT,
+                 br->name, ETH_ADDR_ARGS(ea));
+    }
+    bridge_set_local_hw_addr(br, local_iface, ea);
+}
+
 void
 bridge_run(void)
 {
@@ -476,6 +557,7 @@ bridge_create(const char *name)
     br->flow_idle_time = 5;
     br->sent_config_request = false;
     br->sent_features_request = false;
+    eth_addr_random(br->default_ea);
 
     svec_init(&br->secchan_opts);
 
index d5a8c9ca4f1608bcab21b31dd76583273ca95e1d..2632726bc39f632d5fbdb08a6b9e4829586c6c8f 100644 (file)
@@ -64,6 +64,19 @@ A bridge (switch) with a given \fIname\fR is configured by specifying
 the names of its network devices as values for key
 \fBbridge.\fIname\fB.port\fR.
 .PP
+A bridge with a given \fIname\fR always has an associated network
+device with the same \fIname\fR.  This network device may be included
+in the bridge, by specifying it as one of the values for key
+\fBbridge.\fIname\fB.port\fR, or it may be omitted.  If it is
+included, then its MAC address is by default the lowest-numbered MAC
+address among the other bridge ports, ignoring bridge ports that are
+used as port mirroring destinations (see "Port mirroring", below).  To
+use a specific MAC address instead, set \fBbridge.\fIname\fB.mac\fR to
+a MAC address in the format
+\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fR, where each
+\fIx\fR is a hex digit.  If no valid MAC address can be determined
+either of these ways, then a MAC address is chosen randomly.
+.PP
 The following syntax defines a bridge named \fBmybr\fR, configured
 with network devices \fBeth0\fR, \fBeth1\fR, and \fBeth2\fR:
 .RS