vswitchd: Don't keep track of mac in struct iface.
[cascardo/ovs.git] / vswitchd / bridge.c
index ab55658..c41c132 100644 (file)
@@ -71,17 +71,18 @@ struct dst {
 extern uint64_t mgmt_id;
 
 struct iface {
+    /* These members are always valid. */
     struct port *port;          /* Containing port. */
     size_t port_ifidx;          /* Index within containing port. */
-
     char *name;                 /* Host network device name. */
-    int dp_ifidx;               /* Index within kernel datapath. */
-
-    uint8_t mac[ETH_ADDR_LEN];  /* Ethernet address (all zeros if unknowns). */
-
     tag_type tag;               /* Tag associated with this interface. */
-    bool enabled;               /* May be chosen for flows? */
     long long delay_expires;    /* Time after which 'enabled' may change. */
+
+    /* These members are valid only after bridge_reconfigure() causes them to
+     * be initialized.*/
+    int dp_ifidx;               /* Index within kernel datapath. */
+    struct netdev *netdev;      /* Network device. */
+    bool enabled;               /* May be chosen for flows? */
 };
 
 #define BOND_MASK 0xff
@@ -206,6 +207,8 @@ static uint64_t bridge_pick_datapath_id(struct bridge *,
                                         const char *devname);
 static uint64_t dpid_from_hash(const void *, size_t nbytes);
 
+static void bridge_unixctl_fdb_show(struct unixctl_conn *, const char *args);
+
 static void bond_init(void);
 static void bond_run(struct bridge *);
 static void bond_wait(struct bridge *);
@@ -221,6 +224,7 @@ static struct port *port_from_dp_ifidx(const struct bridge *,
                                        uint16_t dp_ifidx);
 static void port_update_bond_compat(struct port *);
 static void port_update_vlan_compat(struct port *);
+static void port_update_bonding(struct port *);
 
 static void mirror_create(struct bridge *, const char *name);
 static void mirror_destroy(struct mirror *);
@@ -278,6 +282,8 @@ bridge_init(void)
     struct svec dpif_names;
     size_t i;
 
+    unixctl_command_register("fdb/show", bridge_unixctl_fdb_show);
+
     dp_enumerate(&dpif_names);
     for (i = 0; i < dpif_names.n; i++) {
         const char *dpif_name = dpif_names.names[i];
@@ -352,12 +358,84 @@ bridge_configure_ssl(void)
 }
 #endif
 
+/* iterate_and_prune_ifaces() callback function that opens the network device
+ * for 'iface', if it is not already open, and retrieves the interface's MAC
+ * address and carrier status. */
+static bool
+init_iface_netdev(struct bridge *br UNUSED, struct iface *iface,
+                  void *aux UNUSED)
+{
+    if (iface->netdev) {
+        return true;
+    } else if (!netdev_open(iface->name, NETDEV_ETH_TYPE_NONE,
+                            &iface->netdev)) {
+        netdev_get_carrier(iface->netdev, &iface->enabled);
+        return true;
+    } else {
+        /* If the network device can't be opened, then we're not going to try
+         * to do anything with this interface. */
+        return false;
+    }
+}
+
+static bool
+check_iface_dp_ifidx(struct bridge *br, struct iface *iface,
+                     void *local_ifacep_)
+{
+    struct iface **local_ifacep = local_ifacep_;
+
+    if (iface->dp_ifidx >= 0) {
+        if (iface->dp_ifidx == ODPP_LOCAL) {
+            *local_ifacep = iface;
+        }
+        VLOG_DBG("%s has interface %s on port %d",
+                 dpif_name(br->dpif),
+                 iface->name, iface->dp_ifidx);
+        return true;
+    } else {
+        VLOG_ERR("%s interface not in %s, dropping",
+                 iface->name, dpif_name(br->dpif));
+        return false;
+    }
+}
+
+/* Calls 'cb' for each interfaces in 'br', passing along the 'aux' argument.
+ * Deletes from 'br' all the interfaces for which 'cb' returns false, and then
+ * deletes from 'br' any ports that no longer have any interfaces. */
+static void
+iterate_and_prune_ifaces(struct bridge *br,
+                         bool (*cb)(struct bridge *, struct iface *,
+                                    void *aux),
+                         void *aux)
+{
+    size_t i, j;
+
+    for (i = 0; i < br->n_ports; ) {
+        struct port *port = br->ports[i];
+        for (j = 0; j < port->n_ifaces; ) {
+            struct iface *iface = port->ifaces[j];
+            if (cb(br, iface, aux)) {
+                j++;
+            } else {
+                iface_destroy(iface);
+            }
+        }
+
+        if (port->n_ifaces) {
+            i++;
+        } else  {
+            VLOG_ERR("%s port has no interfaces, dropping", port->name);
+            port_destroy(port);
+        }
+    }
+}
+
 void
 bridge_reconfigure(void)
 {
     struct svec old_br, new_br;
     struct bridge *br, *next;
-    size_t i, j;
+    size_t i;
 
     COVERAGE_INC(bridge_reconfigure);
 
@@ -468,32 +546,10 @@ bridge_reconfigure(void)
         struct svec nf_hosts;
 
         bridge_fetch_dp_ifaces(br);
-        for (i = 0; i < br->n_ports; ) {
-            struct port *port = br->ports[i];
+        iterate_and_prune_ifaces(br, init_iface_netdev, NULL);
 
-            for (j = 0; j < port->n_ifaces; ) {
-                struct iface *iface = port->ifaces[j];
-                if (iface->dp_ifidx < 0) {
-                    VLOG_ERR("%s interface not in %s, dropping",
-                             iface->name, dpif_name(br->dpif));
-                    iface_destroy(iface);
-                } else {
-                    if (iface->dp_ifidx == ODPP_LOCAL) {
-                        local_iface = iface;
-                    }
-                    VLOG_DBG("%s has interface %s on port %d",
-                             dpif_name(br->dpif),
-                             iface->name, iface->dp_ifidx);
-                    j++;
-                }
-            }
-            if (!port->n_ifaces) {
-                VLOG_ERR("%s port has no interfaces, dropping", port->name);
-                port_destroy(port);
-                continue;
-            }
-            i++;
-        }
+        local_iface = NULL;
+        iterate_and_prune_ifaces(br, check_iface_dp_ifidx, &local_iface);
 
         /* Pick local port hardware address, datapath ID. */
         bridge_pick_local_hw_addr(br, ea, &devname);
@@ -556,6 +612,7 @@ bridge_reconfigure(void)
         for (i = 0; i < br->n_ports; i++) {
             struct port *port = br->ports[i];
             port_update_vlan_compat(port);
+            port_update_bonding(port);
         }
     }
     LIST_FOR_EACH (br, struct bridge, node, &all_bridges) {
@@ -772,6 +829,35 @@ bridge_flush(struct bridge *br)
     }
 }
 \f
+/* Bridge unixctl user interface functions. */
+static void
+bridge_unixctl_fdb_show(struct unixctl_conn *conn, const char *args)
+{
+    struct ds ds = DS_EMPTY_INITIALIZER;
+    const struct bridge *br;
+
+    br = bridge_lookup(args);
+    if (!br) {
+        unixctl_command_reply(conn, 501, "no such bridge");
+        return;
+    }
+
+    ds_put_cstr(&ds, " port  VLAN  MAC                Age\n");
+    if (br->ml) {
+        const struct mac_entry *e;
+        LIST_FOR_EACH (e, struct mac_entry, lru_node, &br->ml->lrus) {
+            if (e->port < 0 || e->port >= br->n_ports) {
+                continue;
+            }
+            ds_put_format(&ds, "%5d  %4d  "ETH_ADDR_FMT"  %3d\n",
+                          br->ports[e->port]->ifaces[0]->dp_ifidx,
+                          e->vlan, ETH_ADDR_ARGS(e->mac), mac_entry_age(e));
+        }
+    }
+    unixctl_command_reply(conn, 200, ds_cstr(&ds));
+    ds_destroy(&ds);
+}
+\f
 /* Bridge reconfiguration functions. */
 
 static struct bridge *
@@ -911,13 +997,29 @@ bridge_get_controller(const struct bridge *br)
     return controller && controller[0] ? controller : NULL;
 }
 
+static bool
+check_duplicate_ifaces(struct bridge *br, struct iface *iface, void *ifaces_)
+{
+    struct svec *ifaces = ifaces_;
+    if (!svec_contains(ifaces, iface->name)) {
+        svec_add(ifaces, iface->name);
+        svec_sort(ifaces);
+        return true;
+    } else {
+        VLOG_ERR("bridge %s: %s interface is on multiple ports, "
+                 "removing from %s",
+                 br->name, iface->name, iface->port->name);
+        return false;
+    }
+}
+
 static void
 bridge_reconfigure_one(struct bridge *br)
 {
     struct svec old_ports, new_ports, ifaces;
     struct svec listeners, old_listeners;
     struct svec snoops, old_snoops;
-    size_t i, j;
+    size_t i;
 
     /* Collect old ports. */
     svec_init(&old_ports);
@@ -975,28 +1077,7 @@ bridge_reconfigure_one(struct bridge *br)
 
     /* Check and delete duplicate interfaces. */
     svec_init(&ifaces);
-    for (i = 0; i < br->n_ports; ) {
-        struct port *port = br->ports[i];
-        for (j = 0; j < port->n_ifaces; ) {
-            struct iface *iface = port->ifaces[j];
-            if (svec_contains(&ifaces, iface->name)) {
-                VLOG_ERR("bridge %s: %s interface is on multiple ports, "
-                         "removing from %s",
-                         br->name, iface->name, port->name);
-                iface_destroy(iface);
-            } else {
-                svec_add(&ifaces, iface->name);
-                svec_sort(&ifaces);
-                j++;
-            }
-        }
-        if (!port->n_ifaces) {
-            VLOG_ERR("%s port has no interfaces, dropping", port->name);
-            port_destroy(port);
-        } else {
-            i++;
-        }
-    }
+    iterate_and_prune_ifaces(br, check_duplicate_ifaces, &ifaces);
     svec_destroy(&ifaces);
 
     /* Delete all flows if we're switching from connected to standalone or vice
@@ -1067,10 +1148,15 @@ bridge_reconfigure_controller(struct bridge *br)
         int rate_limit, burst_limit;
 
         if (!strcmp(controller, "discover")) {
+            bool update_resolv_conf = true;
+
+            if (cfg_has("%s.update-resolv.conf", pfx)) {
+                update_resolv_conf = cfg_get_bool(0, "%s.update-resolv.conf",
+                        pfx);
+            }
             ofproto_set_discovery(br->ofproto, true,
                                   cfg_get_string(0, "%s.accept-regex", pfx),
-                                  cfg_get_bool(0, "%s.update-resolv.conf",
-                                               pfx));
+                                  update_resolv_conf);
         } else {
             char local_name[IF_NAMESIZE];
             struct netdev *netdev;
@@ -1107,7 +1193,7 @@ bridge_reconfigure_controller(struct bridge *br)
                     }
 
                     if (gateway.s_addr) {
-                        if (!netdev_add_router(gateway)) {
+                        if (!netdev_add_router(netdev, gateway)) {
                             VLOG_INFO("bridge %s: configured gateway "IP_FMT,
                                       br->name, IP_ARGS(&gateway.s_addr));
                         }
@@ -1332,6 +1418,10 @@ bond_link_status_update(struct iface *iface, bool carrier)
         iface->delay_expires = LLONG_MAX;
         VLOG_INFO_RL(&rl, "interface %s: will not be %s",
                      iface->name, carrier ? "disabled" : "enabled");
+    } else if (carrier && port->updelay && port->active_iface < 0) {
+        iface->delay_expires = time_msec();
+        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;
@@ -1375,7 +1465,7 @@ bond_enable_slave(struct iface *iface, bool enable)
 
     iface->enabled = enable;
     if (!iface->enabled) {
-        VLOG_WARN("interface %s: enabled", iface->name);
+        VLOG_WARN("interface %s: disabled", iface->name);
         ofproto_revalidate(br->ofproto, iface->tag);
         if (iface->port_ifidx == port->active_iface) {
             ofproto_revalidate(br->ofproto,
@@ -1384,7 +1474,7 @@ bond_enable_slave(struct iface *iface, bool enable)
         }
         bond_send_learning_packets(port);
     } else {
-        VLOG_WARN("interface %s: disabled", iface->name);
+        VLOG_WARN("interface %s: enabled", iface->name);
         if (port->active_iface < 0) {
             ofproto_revalidate(br->ofproto, port->no_ifaces_tag);
             bond_choose_active_iface(port);
@@ -1849,7 +1939,6 @@ bridge_port_changed_ofhook_cb(enum ofp_port_reason reason,
 
         bridge_flush(br);
     } else {
-        memcpy(iface->mac, opp->hw_addr, ETH_ADDR_LEN);
         if (port->n_ifaces > 1) {
             bool up = !(opp->state & OFPPS_LINK_DOWN);
             bond_link_status_update(iface, up);
@@ -2424,8 +2513,8 @@ bond_unixctl_migrate(struct unixctl_conn *conn, const char *args_)
         return;
     }
 
-    if (sscanf(hash_s, "%"SCNx8":%"SCNx8":%"SCNx8":%"SCNx8":%"SCNx8":%"SCNx8,
-               &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]) == 6) {
+    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;
@@ -2822,7 +2911,7 @@ port_update_bond_compat(struct port *port)
         if (slave->up) {
             bond.up = true;
         }
-        memcpy(slave->mac, iface->mac, ETH_ADDR_LEN);
+        netdev_get_etheraddr(iface->netdev, slave->mac);
     }
     proc_net_compat_update_bond(port->name, &bond);
     free(bond.slaves);
@@ -2852,7 +2941,8 @@ port_update_vlan_compat(struct port *port)
                 && p->n_ifaces
                 && (!vlandev_name || strcmp(p->name, vlandev_name) <= 0))
             {
-                const uint8_t *ea = p->ifaces[0]->mac;
+                uint8_t ea[ETH_ADDR_LEN];
+                netdev_get_etheraddr(p->ifaces[0]->netdev, ea);
                 if (!eth_addr_is_multicast(ea) &&
                     !eth_addr_is_reserved(ea) &&
                     !eth_addr_is_zero(ea)) {
@@ -2878,9 +2968,7 @@ iface_create(struct port *port, const char *name)
     iface->dp_ifidx = -1;
     iface->tag = tag_create_random();
     iface->delay_expires = LLONG_MAX;
-
-    netdev_nodev_get_etheraddr(name, iface->mac);
-    netdev_nodev_get_carrier(name, &iface->enabled);
+    iface->netdev = NULL;
 
     if (port->n_ifaces >= port->allocated_ifaces) {
         port->ifaces = x2nrealloc(port->ifaces, &port->allocated_ifaces,
@@ -2893,7 +2981,6 @@ iface_create(struct port *port, const char *name)
 
     VLOG_DBG("attached network device %s to port %s", iface->name, port->name);
 
-    port_update_bonding(port);
     bridge_flush(port->bridge);
 }
 
@@ -2913,6 +3000,7 @@ iface_destroy(struct iface *iface)
         del = port->ifaces[iface->port_ifidx] = port->ifaces[--port->n_ifaces];
         del->port_ifidx = iface->port_ifidx;
 
+        netdev_close(iface->netdev);
         free(iface->name);
         free(iface);
 
@@ -2922,7 +3010,6 @@ iface_destroy(struct iface *iface)
             bond_send_learning_packets(port);
         }
 
-        port_update_bonding(port);
         bridge_flush(port->bridge);
     }
 }
@@ -3261,23 +3348,25 @@ brstp_send_bpdu(struct ofpbuf *pkt, int port_no, void *br_)
     if (!iface) {
         VLOG_WARN_RL(&rl, "%s: cannot send BPDU on unknown port %d",
                      br->name, port_no);
-    } else if (eth_addr_is_zero(iface->mac)) {
-        VLOG_WARN_RL(&rl, "%s: cannot send BPDU on port %d with unknown MAC",
-                     br->name, port_no);
     } else {
-        union ofp_action action;
         struct eth_header *eth = pkt->l2;
-        flow_t flow;
 
-        memcpy(eth->eth_src, iface->mac, ETH_ADDR_LEN);
+        netdev_get_etheraddr(iface->netdev, eth->eth_src);
+        if (eth_addr_is_zero(eth->eth_src)) {
+            VLOG_WARN_RL(&rl, "%s: cannot send BPDU on port %d "
+                         "with unknown MAC", br->name, port_no);
+        } else {
+            union ofp_action action;
+            flow_t flow;
 
-        memset(&action, 0, sizeof action);
-        action.type = htons(OFPAT_OUTPUT);
-        action.output.len = htons(sizeof action);
-        action.output.port = htons(port_no);
+            memset(&action, 0, sizeof action);
+            action.type = htons(OFPAT_OUTPUT);
+            action.output.len = htons(sizeof action);
+            action.output.port = htons(port_no);
 
-        flow_extract(pkt, ODPP_NONE, &flow);
-        ofproto_send_packet(br->ofproto, &flow, &action, 1, pkt);
+            flow_extract(pkt, ODPP_NONE, &flow);
+            ofproto_send_packet(br->ofproto, &flow, &action, 1, pkt);
+        }
     }
     ofpbuf_delete(pkt);
 }