lacp: New "lacp-heartbeat" mode.
[openvswitch] / vswitchd / bridge.c
index 8b82de80f7ed8a7172ef88cdb0227eb88a5d1826..d883596de83c0e129c088974516cd4554d6495d8 100644 (file)
@@ -185,6 +185,11 @@ struct bridge {
 
     /* Port mirroring. */
     struct mirror *mirrors[MAX_MIRRORS];
+
+    /* Synthetic local port if necessary. */
+    struct ovsrec_port synth_local_port;
+    struct ovsrec_interface synth_local_iface;
+    struct ovsrec_interface *synth_local_ifacep;
 };
 
 /* List of all bridges. */
@@ -264,6 +269,7 @@ static void iface_update_qos(struct iface *, const struct ovsrec_qos *);
 static void iface_update_cfm(struct iface *);
 static bool iface_refresh_cfm_stats(struct iface *iface);
 static bool iface_get_carrier(const struct iface *);
+static bool iface_is_synthetic(const struct iface *);
 
 static void shash_from_ovs_idl_map(char **keys, char **values, size_t n,
                                    struct shash *);
@@ -658,6 +664,13 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
                     dpif_port = NULL;
                 }
                 if (iface) {
+                    if (iface->port->bond) {
+                        /* The bond has a pointer to the netdev, so remove it
+                         * from the bond before closing the netdev.  The slave
+                         * will get added back to the bond later, after a new
+                         * netdev is available. */
+                        bond_slave_unregister(iface->port->bond, iface);
+                    }
                     netdev_close(iface->netdev);
                     iface->netdev = NULL;
                 }
@@ -1140,6 +1153,10 @@ iface_refresh_status(struct iface *iface)
     int64_t mtu_64;
     int error;
 
+    if (iface_is_synthetic(iface)) {
+        return;
+    }
+
     shash_init(&sh);
 
     if (!netdev_get_status(iface->netdev, &sh)) {
@@ -1282,6 +1299,10 @@ iface_refresh_stats(struct iface *iface)
 
     struct netdev_stats stats;
 
+    if (iface_is_synthetic(iface)) {
+        return;
+    }
+
     /* Intentionally ignore return value, since errors will set 'stats' to
      * all-1s, and we will deal with that correctly below. */
     netdev_get_stats(iface->netdev, &stats);
@@ -1696,10 +1717,14 @@ bridge_destroy(struct bridge *br)
     if (br) {
         struct port *port, *next;
         int error;
+        int i;
 
         HMAP_FOR_EACH_SAFE (port, next, hmap_node, &br->ports) {
             port_destroy(port);
         }
+        for (i = 0; i < MAX_MIRRORS; i++) {
+            mirror_destroy(br->mirrors[i]);
+        }
         list_remove(&br->node);
         ofproto_destroy(br->ofproto);
         error = dpif_delete(br->dpif);
@@ -1712,6 +1737,7 @@ bridge_destroy(struct bridge *br)
         hmap_destroy(&br->ifaces);
         hmap_destroy(&br->ports);
         shash_destroy(&br->iface_by_name);
+        free(br->synth_local_iface.type);
         free(br->name);
         free(br);
     }
@@ -1837,22 +1863,28 @@ bridge_reconfigure_one(struct bridge *br)
                       br->name, name);
         }
     }
+    if (!shash_find(&new_ports, br->name)) {
+        struct dpif_port dpif_port;
+        char *type;
 
-    /* If we have a controller, then we need a local port.  Complain if the
-     * user didn't specify one.
-     *
-     * XXX perhaps we should synthesize a port ourselves in this case. */
-    if (bridge_get_controllers(br, NULL)) {
-        char local_name[IF_NAMESIZE];
-        int error;
+        VLOG_WARN("bridge %s: no port named %s, synthesizing one",
+                  br->name, br->name);
 
-        error = dpif_port_get_name(br->dpif, ODPP_LOCAL,
-                                   local_name, sizeof local_name);
-        if (!error && !shash_find(&new_ports, local_name)) {
-            VLOG_WARN("bridge %s: controller specified but no local port "
-                      "(port named %s) defined",
-                      br->name, local_name);
-        }
+        dpif_port_query_by_number(br->dpif, ODPP_LOCAL, &dpif_port);
+        type = xstrdup(dpif_port.type ? dpif_port.type : "internal");
+        dpif_port_destroy(&dpif_port);
+
+        br->synth_local_port.interfaces = &br->synth_local_ifacep;
+        br->synth_local_port.n_interfaces = 1;
+        br->synth_local_port.name = br->name;
+
+        br->synth_local_iface.name = br->name;
+        free(br->synth_local_iface.type);
+        br->synth_local_iface.type = type;
+
+        br->synth_local_ifacep = &br->synth_local_iface;
+
+        shash_add(&new_ports, br->name, &br->synth_local_port);
     }
 
     /* Get rid of deleted ports.
@@ -2919,12 +2951,12 @@ port_reconfigure(struct port *port, const struct ovsrec_port *cfg)
 
     port->cfg = cfg;
 
-
     /* Add new interfaces and update 'cfg' member of existing ones. */
     sset_init(&new_ifaces);
     for (i = 0; i < cfg->n_interfaces; i++) {
         const struct ovsrec_interface *if_cfg = cfg->interfaces[i];
         struct iface *iface;
+        const char *type;
 
         if (!sset_add(&new_ifaces, if_cfg->name)) {
             VLOG_WARN("port %s: %s specified twice as port interface",
@@ -2933,8 +2965,18 @@ port_reconfigure(struct port *port, const struct ovsrec_port *cfg)
             continue;
         }
 
+        /* Determine interface type.  The local port always has type
+         * "internal".  Other ports take their type from the database and
+         * default to "system" if none is specified. */
+        type = (!strcmp(if_cfg->name, port->bridge->name) ? "internal"
+                : if_cfg->type[0] ? if_cfg->type
+                : "system");
+
         iface = iface_lookup(port->bridge, if_cfg->name);
-        if (iface) {
+        if (!strcmp(type, "null")) {
+            iface_destroy(iface);
+            continue;
+        } else if (iface) {
             if (iface->port != port) {
                 VLOG_ERR("bridge %s: %s interface is on multiple ports, "
                          "removing from %s",
@@ -2946,12 +2988,7 @@ port_reconfigure(struct port *port, const struct ovsrec_port *cfg)
             iface = iface_create(port, if_cfg);
         }
 
-        /* Determine interface type.  The local port always has type
-         * "internal".  Other ports take their type from the database and
-         * default to "system" if none is specified. */
-        iface->type = (!strcmp(if_cfg->name, port->bridge->name) ? "internal"
-                       : if_cfg->type[0] ? if_cfg->type
-                       : "system");
+        iface->type = type;
     }
     sset_destroy(&new_ifaces);
 
@@ -3099,14 +3136,30 @@ static void
 iface_reconfigure_lacp(struct iface *iface)
 {
     struct lacp_slave_settings s;
-    int priority;
+    int priority, portid, key;
+
+    portid = atoi(get_interface_other_config(iface->cfg, "lacp-port-id", "0"));
+    priority = atoi(get_interface_other_config(iface->cfg,
+                                               "lacp-port-priority", "0"));
+    key = atoi(get_interface_other_config(iface->cfg, "lacp-aggregation-key",
+                                          "0"));
+
+    if (portid <= 0 || portid > UINT16_MAX) {
+        portid = iface->dp_ifidx;
+    }
+
+    if (priority <= 0 || priority > UINT16_MAX) {
+        priority = UINT16_MAX;
+    }
+
+    if (key < 0 || key > UINT16_MAX) {
+        key = 0;
+    }
 
     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;
+    s.key = key;
     lacp_slave_register(iface->port->lacp, iface, &s);
 }
 
@@ -3115,6 +3168,10 @@ 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)) {
@@ -3123,8 +3180,14 @@ 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);
 
     /* Prefer bondable links if unspecified. */
     priority = atoi(get_port_other_config(port->cfg, "lacp-system-priority",
@@ -3133,8 +3196,22 @@ port_reconfigure_lacp(struct port *port)
                   ? priority
                   : UINT16_MAX - !list_is_short(&port->ifaces));
 
-    s.fast = !strcmp(get_port_other_config(port->cfg, "lacp-time", "slow"),
-                     "fast");
+    s.heartbeat = !strcmp(get_port_other_config(port->cfg,
+                                                "lacp-heartbeat",
+                                                "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) {
         port->lacp = lacp_create();
@@ -3188,6 +3265,8 @@ port_reconfigure_bond(struct port *port)
 
     s.up_delay = MAX(0, port->cfg->bond_updelay);
     s.down_delay = MAX(0, port->cfg->bond_downdelay);
+    s.basis = atoi(get_port_other_config(port->cfg, "bond-hash-basis", "0"));
+
     s.rebalance_interval = atoi(
         get_port_other_config(port->cfg, "bond-rebalance-interval", "10000"));
     if (s.rebalance_interval < 1000) {
@@ -3205,9 +3284,15 @@ port_reconfigure_bond(struct port *port)
     }
 
     LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
-        uint16_t stable_id = (port->lacp
-                              ? lacp_slave_get_port_id(port->lacp, iface)
-                              : iface->dp_ifidx);
+        long long stable_id;
+
+        stable_id = atoll(get_interface_other_config(iface->cfg,
+                                                     "bond-stable-id", "0"));
+
+        if (stable_id <= 0 || stable_id >= UINT32_MAX) {
+            stable_id = odp_port_to_ofp_port(iface->dp_ifidx);
+        }
+
         bond_slave_register(iface->port->bond, iface, stable_id,
                             iface->netdev);
     }
@@ -3368,7 +3453,7 @@ iface_set_mac(struct iface *iface)
 static void
 iface_set_ofport(const struct ovsrec_interface *if_cfg, int64_t ofport)
 {
-    if (if_cfg) {
+    if (if_cfg && !ovsdb_idl_row_is_synthetic(&if_cfg->header_)) {
         ovsrec_interface_set_ofport(if_cfg, &ofport, 1);
     }
 }
@@ -3528,6 +3613,14 @@ iface_get_carrier(const struct iface *iface)
     /* XXX */
     return netdev_get_carrier(iface->netdev);
 }
+
+/* Returns true if 'iface' is synthetic, that is, if we constructed it locally
+ * instead of obtaining it from the database. */
+static bool
+iface_is_synthetic(const struct iface *iface)
+{
+    return ovsdb_idl_row_is_synthetic(&iface->cfg->header_);
+}
 \f
 /* Port mirroring. */