lacp: Implement custom timing mode.
[openvswitch] / vswitchd / bridge.c
index 9b918af5e371c89c9c55a4e5c422884d66367a0a..f5918be0332940f12de56c0f8a9ddf0ee86b5415 100644 (file)
@@ -198,10 +198,11 @@ static struct ovsdb_idl *idl;
 #define STATS_INTERVAL (5 * 1000) /* In milliseconds. */
 static long long int stats_timer = LLONG_MIN;
 
-/* Stores the time after which CFM statistics may be written to the database.
- * Only updated when changes to the database require rate limiting. */
-#define CFM_LIMIT_INTERVAL (1 * 1000) /* In milliseconds. */
-static long long int cfm_limiter = LLONG_MIN;
+/* Stores the time after which rate limited statistics may be written to the
+ * database.  Only updated when changes to the database require rate limiting.
+ */
+#define DB_LIMIT_INTERVAL (1 * 1000) /* In milliseconds. */
+static long long int db_limiter = LLONG_MIN;
 
 static struct bridge *bridge_create(const struct ovsrec_bridge *br_cfg);
 static void bridge_destroy(struct bridge *);
@@ -443,9 +444,7 @@ set_iface_properties(struct bridge *br OVS_UNUSED, struct iface *iface,
 
     /* Set MAC address of internal interfaces other than the local
      * interface. */
-    if (iface->dp_ifidx != ODPP_LOCAL && !strcmp(iface->type, "internal")) {
-        iface_set_mac(iface);
-    }
+    iface_set_mac(iface);
 
     return true;
 }
@@ -1232,6 +1231,27 @@ iface_refresh_cfm_stats(struct iface *iface)
     return changed;
 }
 
+static bool
+iface_refresh_lacp_stats(struct iface *iface)
+{
+    bool *db_current = iface->cfg->lacp_current;
+    bool changed = false;
+
+    if (iface->port->lacp) {
+        bool current = lacp_slave_is_current(iface->port->lacp, iface);
+
+        if (!db_current || *db_current != current) {
+            changed = true;
+            ovsrec_interface_set_lacp_current(iface->cfg, &current, 1);
+        }
+    } else if (db_current) {
+        changed = true;
+        ovsrec_interface_set_lacp_current(iface->cfg, NULL, 0);
+    }
+
+    return changed;
+}
+
 static void
 iface_refresh_stats(struct iface *iface)
 {
@@ -1420,7 +1440,7 @@ bridge_run(void)
         stats_timer = time_msec() + STATS_INTERVAL;
     }
 
-    if (time_msec() >= cfm_limiter) {
+    if (time_msec() >= db_limiter) {
         struct ovsdb_idl_txn *txn;
         bool changed = false;
 
@@ -1433,12 +1453,13 @@ bridge_run(void)
 
                 LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
                     changed = iface_refresh_cfm_stats(iface) || changed;
+                    changed = iface_refresh_lacp_stats(iface) || changed;
                 }
             }
         }
 
         if (changed) {
-            cfm_limiter = time_msec() + CFM_LIMIT_INTERVAL;
+            db_limiter = time_msec() + DB_LIMIT_INTERVAL;
         }
 
         ovsdb_idl_txn_commit(txn);
@@ -1463,8 +1484,8 @@ bridge_wait(void)
     ovsdb_idl_wait(idl);
     poll_timer_wait_until(stats_timer);
 
-    if (cfm_limiter > time_msec()) {
-        poll_timer_wait_until(cfm_limiter);
+    if (db_limiter > time_msec()) {
+        poll_timer_wait_until(db_limiter);
     }
 }
 
@@ -2776,8 +2797,16 @@ port_run(struct port *port)
     }
 
     if (port->bond) {
+        struct iface *iface;
+
+        LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
+            bool may_enable = lacp_slave_may_enable(port->lacp, iface);
+            bond_slave_set_lacp_may_enable(port->bond, iface, may_enable);
+        }
+
         bond_run(port->bond,
-                 ofproto_get_revalidate_set(port->bridge->ofproto));
+                 ofproto_get_revalidate_set(port->bridge->ofproto),
+                 lacp_negotiated(port->lacp));
         if (bond_should_send_learning_packets(port->bond)) {
             port_send_learning_packets(port);
         }
@@ -3013,6 +3042,7 @@ port_destroy(struct port *port)
 
         VLOG_INFO("destroyed port %s on bridge %s", port->name, br->name);
 
+        bond_destroy(port->bond);
         lacp_destroy(port->lacp);
         port_flush_macs(port);
 
@@ -3069,14 +3099,23 @@ static void
 iface_reconfigure_lacp(struct iface *iface)
 {
     struct lacp_slave_settings s;
-    int priority;
+    int priority, portid;
+
+    portid = atoi(get_interface_other_config(iface->cfg, "lacp-port-id", "0"));
+    priority = atoi(get_interface_other_config(iface->cfg,
+                                               "lacp-port-priority", "0"));
+
+    if (portid <= 0 || portid > UINT16_MAX) {
+        portid = iface->dp_ifidx;
+    }
+
+    if (priority <= 0 || priority > UINT16_MAX) {
+        priority = UINT16_MAX;
+    }
 
     s.name = iface->name;
-    s.id = iface->dp_ifidx;
-    priority = atoi(get_interface_other_config(
-                        iface->cfg, "lacp-port-priority", "0"));
-    s.priority = (priority >= 0 && priority <= UINT16_MAX
-                  ? priority : UINT16_MAX);
+    s.id = portid;
+    s.priority = priority;
     lacp_slave_register(iface->port->lacp, iface, &s);
 }
 
@@ -3085,6 +3124,11 @@ port_reconfigure_lacp(struct port *port)
 {
     static struct lacp_settings s;
     struct iface *iface;
+    uint8_t sysid[ETH_ADDR_LEN];
+    const char *sysid_str;
+    const char *lacp_time;
+    long long int custom_time;
+    int priority;
 
     if (!enable_lacp(port, &s.active)) {
         lacp_destroy(port->lacp);
@@ -3092,16 +3136,37 @@ port_reconfigure_lacp(struct port *port)
         return;
     }
 
+    sysid_str = get_port_other_config(port->cfg, "lacp-system-id", NULL);
+    if (sysid_str && eth_addr_from_string(sysid_str, sysid)) {
+        memcpy(s.id, sysid, ETH_ADDR_LEN);
+    } else {
+        memcpy(s.id, port->bridge->ea, ETH_ADDR_LEN);
+    }
+
     s.name = port->name;
-    memcpy(s.id, port->bridge->ea, ETH_ADDR_LEN);
-    s.priority = atoi(get_port_other_config(port->cfg, "lacp-system-priority",
-                                            "0"));
-    s.fast = !strcmp(get_port_other_config(port->cfg, "lacp-time", "slow"),
-                     "fast");
 
-    if (s.priority <= 0 || s.priority > UINT16_MAX) {
-        /* Prefer bondable links if unspecified. */
-        s.priority = UINT16_MAX - !list_is_short(&port->ifaces);
+    /* Prefer bondable links if unspecified. */
+    priority = atoi(get_port_other_config(port->cfg, "lacp-system-priority",
+                                          "0"));
+    s.priority = (priority > 0 && priority <= UINT16_MAX
+                  ? priority
+                  : UINT16_MAX - !list_is_short(&port->ifaces));
+
+    s.strict = !strcmp(get_port_other_config(port->cfg, "lacp-strict",
+                                             "false"),
+                       "true");
+
+    lacp_time = get_port_other_config(port->cfg, "lacp-time", "slow");
+    custom_time = atoi(lacp_time);
+    if (!strcmp(lacp_time, "fast")) {
+        s.lacp_time = LACP_TIME_FAST;
+    } else if (!strcmp(lacp_time, "slow")) {
+        s.lacp_time = LACP_TIME_SLOW;
+    } else if (custom_time > 0) {
+        s.lacp_time = LACP_TIME_CUSTOM;
+        s.custom_time = custom_time;
+    } else {
+        s.lacp_time = LACP_TIME_SLOW;
     }
 
     if (!port->lacp) {
@@ -3163,7 +3228,6 @@ port_reconfigure_bond(struct port *port)
     }
 
     s.fake_iface = port->cfg->bond_fake_iface;
-    s.lacp = port->lacp;
 
     if (!port->bond) {
         port->bond = bond_create(&s);
@@ -3174,7 +3238,11 @@ port_reconfigure_bond(struct port *port)
     }
 
     LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
-        bond_slave_register(iface->port->bond, iface, iface->netdev);
+        uint16_t stable_id = (port->lacp
+                              ? lacp_slave_get_port_id(port->lacp, iface)
+                              : iface->dp_ifidx);
+        bond_slave_register(iface->port->bond, iface, stable_id,
+                            iface->netdev);
     }
 }
 
@@ -3310,13 +3378,15 @@ iface_set_mac(struct iface *iface)
 {
     uint8_t ea[ETH_ADDR_LEN];
 
-    if (iface->cfg->mac && eth_addr_from_string(iface->cfg->mac, ea)) {
-        if (eth_addr_is_multicast(ea)) {
+    if (!strcmp(iface->type, "internal")
+        && iface->cfg->mac && eth_addr_from_string(iface->cfg->mac, ea)) {
+        if (iface->dp_ifidx == ODPP_LOCAL) {
+            VLOG_ERR("interface %s: ignoring mac in Interface record "
+                     "(use Bridge record to set local port's mac)",
+                     iface->name);
+        } else if (eth_addr_is_multicast(ea)) {
             VLOG_ERR("interface %s: cannot set MAC to multicast address",
                      iface->name);
-        } else if (iface->dp_ifidx == ODPP_LOCAL) {
-            VLOG_ERR("ignoring iface.%s.mac; use bridge.%s.mac instead",
-                     iface->name, iface->name);
         } else {
             int error = netdev_set_etheraddr(iface->netdev, ea);
             if (error) {
@@ -3597,6 +3667,7 @@ mirror_create(struct bridge *br, struct ovsrec_mirror *cfg)
     mac_learning_flush(br->ml);
 
     br->mirrors[i] = m = xzalloc(sizeof *m);
+    m->uuid = cfg->header_.uuid;
     m->bridge = br;
     m->idx = i;
     m->name = xstrdup(cfg->name);
@@ -3686,19 +3757,6 @@ vlan_is_mirrored(const struct mirror *m, int vlan)
     return false;
 }
 
-static bool
-port_trunks_any_mirrored_vlan(const struct mirror *m, const struct port *p)
-{
-    size_t i;
-
-    for (i = 0; i < m->n_vlans; i++) {
-        if (port_trunks_vlan(p, m->vlans[i])) {
-            return true;
-        }
-    }
-    return false;
-}
-
 static void
 mirror_reconfigure_one(struct mirror *m, struct ovsrec_mirror *cfg)
 {
@@ -3783,11 +3841,7 @@ mirror_reconfigure_one(struct mirror *m, struct ovsrec_mirror *cfg)
     /* Update ports. */
     mirror_bit = MIRROR_MASK_C(1) << m->idx;
     HMAP_FOR_EACH (port, hmap_node, &m->bridge->ports) {
-        if (sset_contains(&m->src_ports, port->name)
-            || (m->n_vlans
-                && (!port->vlan
-                    ? port_trunks_any_mirrored_vlan(m, port)
-                    : vlan_is_mirrored(m, port->vlan)))) {
+        if (sset_contains(&m->src_ports, port->name)) {
             port->src_mirrors |= mirror_bit;
         } else {
             port->src_mirrors &= ~mirror_bit;