ovs-parse-leaks: Fix typos in manpage.
[sliver-openvswitch.git] / lib / lacp.c
index de860e0..eaf01c3 100644 (file)
@@ -25,6 +25,7 @@
 #include "ofpbuf.h"
 #include "packets.h"
 #include "poll-loop.h"
+#include "timer.h"
 #include "timeval.h"
 #include "unixctl.h"
 #include "vlog.h"
@@ -47,8 +48,11 @@ struct lacp {
     struct hmap slaves;      /* Slaves this LACP object controls. */
     struct slave *key_slave; /* Slave whose ID will be the aggregation key. */
 
+    enum lacp_time lacp_time;  /* Fast, Slow or Custom LACP time. */
+    long long int custom_time; /* LACP_TIME_CUSTOM transmission rate. */
     bool negotiated;         /* True if LACP negotiations were successful. */
     bool update;             /* True if lacp_update() needs to be called. */
+    bool heartbeat;          /* LACP heartbeat mode. */
 };
 
 struct slave {
@@ -58,15 +62,15 @@ struct slave {
     struct lacp *lacp;            /* LACP object containing this slave. */
     uint16_t port_id;             /* Port ID. */
     uint16_t port_priority;       /* Port Priority. */
+    uint16_t key;                 /* Aggregation Key. 0 if default. */
     char *name;                   /* Name of this slave. */
 
     enum slave_status status;     /* Slave status. */
     bool attached;                /* Attached. Traffic may flow. */
-    bool enabled;                 /* Enabled. Traffic is flowing. */
     struct lacp_info partner;     /* Partner information. */
     struct lacp_info ntt_actor;   /* Used to decide if we Need To Transmit. */
-    long long int tx;             /* Next message transmission time. */
-    long long int rx;             /* Expected message receive time. */
+    struct timer tx;              /* Next message transmission timer. */
+    struct timer rx;              /* Expected message receive timer. */
 };
 
 static struct list all_lacps = LIST_INITIALIZER(&all_lacps);
@@ -85,6 +89,51 @@ static bool info_tx_equal(struct lacp_info *, struct lacp_info *);
 static void lacp_unixctl_show(struct unixctl_conn *, const char *args,
                               void *aux);
 
+/* Populates 'pdu' with a LACP PDU comprised of 'actor' and 'partner'. */
+void
+compose_lacp_pdu(const struct lacp_info *actor,
+                 const struct lacp_info *partner, struct lacp_pdu *pdu)
+{
+    memset(pdu, 0, sizeof *pdu);
+
+    pdu->subtype = 1;
+    pdu->version = 1;
+
+    pdu->actor_type = 1;
+    pdu->actor_len = 20;
+    pdu->actor = *actor;
+
+    pdu->partner_type = 2;
+    pdu->partner_len = 20;
+    pdu->partner = *partner;
+
+    pdu->collector_type = 3;
+    pdu->collector_len = 16;
+    pdu->collector_delay = htons(0);
+}
+
+/* Parses 'b' which represents a packet containing a LACP PDU.  This function
+ * returns NULL if 'b' is malformed, or does not represent a LACP PDU format
+ * supported by OVS.  Otherwise, it returns a pointer to the lacp_pdu contained
+ * within 'b'. */
+const struct lacp_pdu *
+parse_lacp_packet(const struct ofpbuf *b)
+{
+    const struct lacp_pdu *pdu;
+
+    pdu = ofpbuf_at(b, (uint8_t *)b->l3 - (uint8_t *)b->data, LACP_PDU_LEN);
+
+    if (pdu && pdu->subtype == 1
+        && pdu->actor_type == 1 && pdu->actor_len == 20
+        && pdu->partner_type == 2 && pdu->partner_len == 20) {
+        return pdu;
+    } else {
+        return NULL;
+    }
+}
+\f
+/* LACP Protocol Implementation. */
+
 /* Initializes the lacp module. */
 void
 lacp_init(void)
@@ -122,21 +171,35 @@ lacp_destroy(struct lacp *lacp)
     }
 }
 
-/* Configures 'lacp' with the given 'name', 'sys_id', 'sys_priority', and
- * 'active' parameters. */
+/* Configures 'lacp' with settings from 's'. */
 void
-lacp_configure(struct lacp *lacp, const char *name,
-               uint8_t sys_id[ETH_ADDR_LEN], uint16_t sys_priority,
-               bool active)
+lacp_configure(struct lacp *lacp, const struct lacp_settings *s)
 {
-    if (!lacp->name || strcmp(name, lacp->name)) {
+    if (!lacp->name || strcmp(s->name, lacp->name)) {
         free(lacp->name);
-        lacp->name = xstrdup(name);
+        lacp->name = xstrdup(s->name);
+    }
+
+    if (!eth_addr_equals(lacp->sys_id, s->id)
+        || lacp->sys_priority != s->priority
+        || lacp->heartbeat != s->heartbeat) {
+        memcpy(lacp->sys_id, s->id, ETH_ADDR_LEN);
+        lacp->sys_priority = s->priority;
+        lacp->heartbeat = s->heartbeat;
+        lacp->update = true;
     }
 
-    memcpy(lacp->sys_id, sys_id, ETH_ADDR_LEN);
-    lacp->sys_priority = sys_priority;
-    lacp->active = active;
+    lacp->active = s->active;
+    lacp->lacp_time = s->lacp_time;
+    lacp->custom_time = MAX(TIME_UPDATE_INTERVAL, s->custom_time);
+}
+
+/* Returns true if 'lacp' is configured in active mode, false if 'lacp' is
+ * configured for passive mode. */
+bool
+lacp_is_active(const struct lacp *lacp)
+{
+    return lacp->active;
 }
 
 /* Processes 'pdu', a parsed LACP packet received on 'slave_'.  This function
@@ -147,9 +210,23 @@ lacp_process_pdu(struct lacp *lacp, const void *slave_,
                  const struct lacp_pdu *pdu)
 {
     struct slave *slave = slave_lookup(lacp, slave_);
+    long long int tx_rate;
+
+    switch (lacp->lacp_time) {
+    case LACP_TIME_FAST:
+        tx_rate = LACP_FAST_TIME_TX;
+        break;
+    case LACP_TIME_SLOW:
+        tx_rate = LACP_SLOW_TIME_TX;
+        break;
+    case LACP_TIME_CUSTOM:
+        tx_rate = lacp->custom_time;
+        break;
+    default: NOT_REACHED();
+    }
 
     slave->status = LACP_CURRENT;
-    slave->rx = time_msec() + LACP_SLOW_TIME_RX;
+    timer_set_duration(&slave->rx, LACP_RX_MULTIPLIER * tx_rate);
 
     slave->ntt_actor = pdu->partner;
 
@@ -172,10 +249,10 @@ lacp_negotiated(const struct lacp *lacp)
 
 /* Registers 'slave_' as subordinate to 'lacp'.  This should be called at least
  * once per slave in a LACP managed bond.  Should also be called whenever a
- * slave's name, port_id, or port_priority change. */
+ * slave's settings change. */
 void
-lacp_slave_register(struct lacp *lacp, void *slave_, const char *name,
-                    uint16_t port_id, uint16_t port_priority)
+lacp_slave_register(struct lacp *lacp, void *slave_,
+                    const struct lacp_slave_settings *s)
 {
     struct slave *slave = slave_lookup(lacp, slave_);
 
@@ -191,15 +268,17 @@ lacp_slave_register(struct lacp *lacp, void *slave_, const char *name,
         }
     }
 
-    if (!slave->name || strcmp(name, slave->name)) {
+    if (!slave->name || strcmp(s->name, slave->name)) {
         free(slave->name);
-        slave->name = xstrdup(name);
+        slave->name = xstrdup(s->name);
     }
 
-    if (slave->port_id != port_id || slave->port_priority != port_priority) {
-
-        slave->port_id = port_id;
-        slave->port_priority = port_priority;
+    if (slave->port_id != s->id
+        || slave->port_priority != s->priority
+        || slave->key != s->key) {
+        slave->port_id = s->id;
+        slave->port_priority = s->priority;
+        slave->key = s->key;
 
         lacp->update = true;
 
@@ -217,19 +296,10 @@ lacp_slave_unregister(struct lacp *lacp, const void *slave_)
 
     if (slave) {
         slave_destroy(slave);
+        lacp->update = true;
     }
 }
 
-/* Should be called regularly to indicate whether 'slave_' is enabled.  An
- * enabled slave is allowed to send and receive traffic.  Generally a slave
- * should not be enabled if its carrier is down, or lacp_slave_may_enable()
- * indicates it should not be enabled. */
-void
-lacp_slave_enable(struct lacp *lacp, void *slave_, bool enabled)
-{
-   slave_lookup(lacp, slave_)->enabled = enabled;
-}
-
 /* This function should be called whenever the carrier status of 'slave_' has
  * changed. */
 void
@@ -263,6 +333,23 @@ lacp_slave_may_enable(const struct lacp *lacp, const void *slave_)
     }
 }
 
+/* Returns the port ID used for 'slave_' in LACP communications. */
+uint16_t
+lacp_slave_get_port_id(const struct lacp *lacp, const void *slave_)
+{
+    struct slave *slave = slave_lookup(lacp, slave_);
+    return slave->port_id;
+}
+
+/* Returns true if partner information on 'slave_' is up to date.  'slave_'
+ * not being current, generally indicates a connectivity problem, or a
+ * misconfigured (or broken) partner. */
+bool
+lacp_slave_is_current(const struct lacp *lacp, const void *slave_)
+{
+    return slave_lookup(lacp, slave_)->status != LACP_DEFAULTED;
+}
+
 /* This function should be called periodically to update 'lacp'. */
 void
 lacp_run(struct lacp *lacp, lacp_send_pdu *send_pdu)
@@ -270,7 +357,7 @@ lacp_run(struct lacp *lacp, lacp_send_pdu *send_pdu)
     struct slave *slave;
 
     HMAP_FOR_EACH (slave, node, &lacp->slaves) {
-        if (time_msec() >= slave->rx) {
+        if (timer_expired(&slave->rx)) {
             if (slave->status == LACP_CURRENT) {
                 slave_set_expired(slave);
             } else if (slave->status == LACP_EXPIRED) {
@@ -293,17 +380,23 @@ lacp_run(struct lacp *lacp, lacp_send_pdu *send_pdu)
 
         slave_get_actor(slave, &actor);
 
-        if (time_msec() >= slave->tx
+        if (timer_expired(&slave->tx)
             || !info_tx_equal(&actor, &slave->ntt_actor)) {
+            long long int duration;
 
             slave->ntt_actor = actor;
             compose_lacp_pdu(&actor, &slave->partner, &pdu);
             send_pdu(slave->aux, &pdu);
 
-            slave->tx = time_msec() +
-                (slave->partner.state & LACP_STATE_TIME
-                 ? LACP_FAST_TIME_TX
-                 : LACP_SLOW_TIME_TX);
+            if (lacp->lacp_time == LACP_TIME_CUSTOM) {
+                duration = lacp->custom_time;
+            } else {
+                duration = (slave->partner.state & LACP_STATE_TIME
+                            ? LACP_FAST_TIME_TX
+                            : LACP_SLOW_TIME_TX);
+            }
+
+            timer_set_duration(&slave->tx, duration);
         }
     }
 }
@@ -316,18 +409,18 @@ lacp_wait(struct lacp *lacp)
 
     HMAP_FOR_EACH (slave, node, &lacp->slaves) {
         if (slave_may_tx(slave)) {
-            poll_timer_wait_until(slave->tx);
+            timer_wait(&slave->tx);
         }
 
         if (slave->status != LACP_DEFAULTED) {
-            poll_timer_wait_until(slave->rx);
+            timer_wait(&slave->rx);
         }
     }
 }
 \f
 /* Static Helpers. */
 
-/* Updates the attached status of all slaves controlled b 'lacp' and sets its
+/* Updates the attached status of all slaves controlled by 'lacp' and sets its
  * negotiated parameter to true if any slaves are attachable. */
 static void
 lacp_update_attached(struct lacp *lacp)
@@ -336,6 +429,13 @@ lacp_update_attached(struct lacp *lacp)
     struct lacp_info lead_pri;
     static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 10);
 
+    if (lacp->heartbeat) {
+        HMAP_FOR_EACH (slave, node, &lacp->slaves) {
+            slave->attached = slave->status != LACP_DEFAULTED;
+        }
+        return;
+    }
+
     lacp->update = false;
 
     lead = NULL;
@@ -415,22 +515,35 @@ slave_set_defaulted(struct slave *slave)
 static void
 slave_set_expired(struct slave *slave)
 {
+    struct lacp *lacp = slave->lacp;
+
     slave->status = LACP_EXPIRED;
     slave->partner.state |= LACP_STATE_TIME;
     slave->partner.state &= ~LACP_STATE_SYNC;
 
-    slave->rx = time_msec() + LACP_FAST_TIME_RX;
+    /* The spec says we should wait LACP_RX_MULTIPLIER * LACP_FAST_TIME_TX.
+     * This doesn't make sense when using custom times which can be much
+     * smaller than LACP_FAST_TIME. */
+    timer_set_duration(&slave->rx, (lacp->lacp_time == LACP_TIME_CUSTOM
+                                    ? lacp->custom_time
+                                    : LACP_RX_MULTIPLIER * LACP_FAST_TIME_TX));
 }
 
 static void
 slave_get_actor(struct slave *slave, struct lacp_info *actor)
 {
+    struct lacp *lacp = slave->lacp;
+    uint16_t key;
     uint8_t state = 0;
 
-    if (slave->lacp->active) {
+    if (lacp->active) {
         state |= LACP_STATE_ACT;
     }
 
+    if (lacp->lacp_time != LACP_TIME_SLOW) {
+        state |= LACP_STATE_TIME;
+    }
+
     if (slave->attached) {
         state |= LACP_STATE_SYNC;
     }
@@ -443,20 +556,25 @@ slave_get_actor(struct slave *slave, struct lacp_info *actor)
         state |= LACP_STATE_EXP;
     }
 
-    if (hmap_count(&slave->lacp->slaves) > 1) {
+    if (lacp->heartbeat || hmap_count(&lacp->slaves) > 1) {
         state |= LACP_STATE_AGG;
     }
 
-    if (slave->enabled) {
+    if (slave->attached || !lacp->negotiated) {
         state |= LACP_STATE_COL | LACP_STATE_DIST;
     }
 
+    key = lacp->key_slave->key;
+    if (!key) {
+        key = lacp->key_slave->port_id;
+    }
+
     actor->state = state;
-    actor->key = htons(slave->lacp->key_slave->port_id);
+    actor->key = htons(key);
     actor->port_priority = htons(slave->port_priority);
     actor->port_id = htons(slave->port_id);
-    actor->sys_priority = htons(slave->lacp->sys_priority);
-    memcpy(&actor->sys_id, slave->lacp->sys_id, ETH_ADDR_LEN);
+    actor->sys_priority = htons(lacp->sys_priority);
+    memcpy(&actor->sys_id, lacp->sys_id, ETH_ADDR_LEN);
 }
 
 /* Given 'slave', populates 'priority' with data representing its LACP link
@@ -596,9 +714,16 @@ lacp_unixctl_show(struct unixctl_conn *conn,
     }
 
     ds_put_format(&ds, "lacp: %s\n", lacp->name);
-    ds_put_format(&ds, "\tstatus: %s %s\n",
-                  lacp->active ? "active" : "passive",
-                  lacp->negotiated ? "negotiated" : "");
+
+    ds_put_format(&ds, "\tstatus: %s", lacp->active ? "active" : "passive");
+    if (lacp->heartbeat) {
+        ds_put_cstr(&ds, " heartbeat");
+    }
+    if (lacp->negotiated) {
+        ds_put_cstr(&ds, " negotiated");
+    }
+    ds_put_cstr(&ds, "\n");
+
     ds_put_format(&ds, "\tsys_id: " ETH_ADDR_FMT "\n", ETH_ADDR_ARGS(lacp->sys_id));
     ds_put_format(&ds, "\tsys_priority: %u\n", lacp->sys_priority);
     ds_put_cstr(&ds, "\taggregation key: ");
@@ -609,6 +734,21 @@ lacp_unixctl_show(struct unixctl_conn *conn,
     }
     ds_put_cstr(&ds, "\n");
 
+    ds_put_cstr(&ds, "\tlacp_time: ");
+    switch (lacp->lacp_time) {
+    case LACP_TIME_FAST:
+        ds_put_cstr(&ds, "fast\n");
+        break;
+    case LACP_TIME_SLOW:
+        ds_put_cstr(&ds, "slow\n");
+        break;
+    case LACP_TIME_CUSTOM:
+        ds_put_format(&ds, "custom (%lld)\n", lacp->custom_time);
+        break;
+    default:
+        ds_put_cstr(&ds, "unknown\n");
+    }
+
     HMAP_FOR_EACH (slave, node, &lacp->slaves) {
         char *status;
         struct lacp_info actor;
@@ -628,9 +768,8 @@ lacp_unixctl_show(struct unixctl_conn *conn,
             NOT_REACHED();
         }
 
-        ds_put_format(&ds, "\nslave: %s: %s %s %s\n", slave->name, status,
-                      slave->attached ? "attached" : "detached",
-                      slave->enabled ? "enabled" : "disabled");
+        ds_put_format(&ds, "\nslave: %s: %s %s\n", slave->name, status,
+                      slave->attached ? "attached" : "detached");
         ds_put_format(&ds, "\tport_id: %u\n", slave->port_id);
         ds_put_format(&ds, "\tport_priority: %u\n", slave->port_priority);