lib/json.h \
lib/jsonrpc.c \
lib/jsonrpc.h \
+ lib/lacp.c \
+ lib/lacp.h \
lib/leak-checker.c \
lib/leak-checker.h \
lib/learning-switch.c \
--- /dev/null
+/* Copyright (c) 2011 Nicira Networks
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+#include "lacp.h"
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include "dynamic-string.h"
+#include "hash.h"
+#include "hmap.h"
+#include "ofpbuf.h"
+#include "packets.h"
+#include "poll-loop.h"
+#include "timeval.h"
+#include "unixctl.h"
+#include "vlog.h"
+
+VLOG_DEFINE_THIS_MODULE(lacp);
+
+enum slave_status {
+ LACP_CURRENT, /* Current State. Partner up to date. */
+ LACP_EXPIRED, /* Expired State. Partner out of date. */
+ LACP_DEFAULTED, /* Defaulted State. No partner. */
+};
+
+struct lacp {
+ struct list node; /* Node in all_lacps list. */
+ char *name; /* Name of this lacp object. */
+ uint8_t sys_id[ETH_ADDR_LEN]; /* System ID. */
+ uint16_t sys_priority; /* System Priority. */
+ bool active; /* Active or Passive. */
+
+ struct hmap slaves; /* Slaves this LACP object controls. */
+ struct slave *key_slave; /* Slave whose ID will be the aggregation key. */
+
+ bool negotiated; /* True if LACP negotiations were successful. */
+ bool update; /* True if lacp_update() needs to be called. */
+};
+
+struct slave {
+ void *aux; /* Handle used to identify this slave. */
+ struct hmap_node node; /* Node in master's slaves map. */
+
+ struct lacp *lacp; /* LACP object containing this slave. */
+ uint16_t port_id; /* Port ID. */
+ uint16_t port_priority; /* Port Priority. */
+ 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 actor; /* Actor information. */
+ struct lacp_info partner; /* Partner information. */
+ long long int tx; /* Next message transmission time. */
+ long long int rx; /* Expected message receive time. */
+};
+
+static struct list all_lacps = LIST_INITIALIZER(&all_lacps);
+
+static void lacp_update_attached(struct lacp *);
+
+static void slave_destroy(struct slave *);
+static void slave_set_defaulted(struct slave *);
+static void slave_set_expired(struct slave *);
+static void slave_update_actor(struct slave *);
+static void slave_get_priority(struct slave *, struct lacp_info *priority);
+static bool slave_may_tx(const struct slave *);
+static struct slave *slave_lookup(const struct lacp *, const void *slave);
+
+static void lacp_unixctl_show(struct unixctl_conn *, const char *args,
+ void *aux);
+
+/* Initializes the lacp module. */
+void
+lacp_init(void)
+{
+ unixctl_command_register("lacp/show", lacp_unixctl_show, NULL);
+}
+
+/* Creates a LACP object. */
+struct lacp *
+lacp_create(void)
+{
+ struct lacp *lacp;
+
+ lacp = xzalloc(sizeof *lacp);
+ hmap_init(&lacp->slaves);
+ list_push_back(&all_lacps, &lacp->node);
+ return lacp;
+}
+
+/* Destroys 'lacp' and its slaves. Does nothing if 'lacp' is NULL. */
+void
+lacp_destroy(struct lacp *lacp)
+{
+ if (lacp) {
+ struct slave *slave, *next;
+
+ HMAP_FOR_EACH_SAFE (slave, next, node, &lacp->slaves) {
+ slave_destroy(slave);
+ }
+
+ hmap_destroy(&lacp->slaves);
+ list_remove(&lacp->node);
+ free(lacp->name);
+ free(lacp);
+ }
+}
+
+/* Configures 'lacp' with the given 'name', 'sys_id', 'sys_priority', and
+ * 'active' parameters. */
+void
+lacp_configure(struct lacp *lacp, const char *name,
+ uint8_t sys_id[ETH_ADDR_LEN], uint16_t sys_priority,
+ bool active)
+{
+ if (!lacp->name || strcmp(name, lacp->name)) {
+ free(lacp->name);
+ lacp->name = xstrdup(name);
+ }
+
+ memcpy(lacp->sys_id, sys_id, ETH_ADDR_LEN);
+ lacp->sys_priority = sys_priority;
+ lacp->active = active;
+}
+
+/* Processes 'pdu', a parsed LACP packet received on 'slave_'. This function
+ * should be called on all packets received on 'slave_' with Ethernet Type
+ * ETH_TYPE_LACP and parsable by parse_lacp_packet(). */
+void
+lacp_process_pdu(struct lacp *lacp, const void *slave_,
+ const struct lacp_pdu *pdu)
+{
+ struct slave *slave = slave_lookup(lacp, slave_);
+
+ slave->status = LACP_CURRENT;
+ slave->rx = time_msec() + LACP_SLOW_TIME_RX;
+
+ /* Check if our partner has incorrect information about our current state.
+ * If so update them. */
+ slave_update_actor(slave);
+ if (memcmp(&slave->actor, &pdu->partner, sizeof pdu->partner)) {
+ slave->tx = LLONG_MIN;
+ }
+
+ /* Update our information about our partner if it's out of date. This may
+ * cause priorities to change so re-calculate attached status of all
+ * slaves. */
+ if (memcmp(&slave->partner, &pdu->actor, sizeof pdu->actor)) {
+ lacp->update = true;
+ slave->partner = pdu->actor;
+ }
+}
+
+/* Returns true if 'lacp' has successfully negotiated with its partner. False
+ * if 'lacp' is NULL. */
+bool
+lacp_negotiated(const struct lacp *lacp)
+{
+ return lacp ? lacp->negotiated : false;
+}
+
+/* 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. */
+void
+lacp_slave_register(struct lacp *lacp, void *slave_, const char *name,
+ uint16_t port_id, uint16_t port_priority)
+{
+ struct slave *slave = slave_lookup(lacp, slave_);
+
+ if (!slave) {
+ slave = xzalloc(sizeof *slave);
+ slave->lacp = lacp;
+ slave->aux = slave_;
+ hmap_insert(&lacp->slaves, &slave->node, hash_pointer(slave_, 0));
+ slave_set_defaulted(slave);
+
+ if (!lacp->key_slave) {
+ lacp->key_slave = slave;
+ }
+ }
+
+ if (!slave->name || strcmp(name, slave->name)) {
+ free(slave->name);
+ slave->name = xstrdup(name);
+ }
+
+ if (slave->port_id != port_id || slave->port_priority != port_priority) {
+
+ slave->port_id = port_id;
+ slave->port_priority = port_priority;
+
+ slave->tx = LLONG_MIN;
+ lacp->update = true;
+
+ if (lacp->active || lacp->negotiated) {
+ slave_set_expired(slave);
+ }
+ }
+}
+
+/* Unregisters 'slave_' with 'lacp'. */
+void
+lacp_slave_unregister(struct lacp *lacp, const void *slave_)
+{
+ struct slave *slave = slave_lookup(lacp, slave_);
+
+ if (slave) {
+ slave_destroy(slave);
+ }
+}
+
+/* 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)
+{
+ struct slave *slave = slave_lookup(lacp, slave_);
+
+ if (slave->enabled != enabled) {
+ slave->enabled = enabled;
+ slave->tx = LLONG_MIN;
+ }
+}
+
+/* This function should be called whenever the carrier status of 'slave_' has
+ * changed. */
+void
+lacp_slave_carrier_changed(const struct lacp *lacp, const void *slave_)
+{
+ struct slave *slave = slave_lookup(lacp, slave_);
+
+ slave->tx = LLONG_MIN;
+ if (slave->status == LACP_CURRENT || slave->lacp->active) {
+ slave_set_expired(slave);
+ }
+}
+
+/* This function should be called before enabling 'slave_' to send or receive
+ * traffic. If it returns false, 'slave_' should not enabled. As a
+ * convenience, returns true if 'lacp' is NULL. */
+bool
+lacp_slave_may_enable(const struct lacp *lacp, const void *slave_)
+{
+ if (lacp) {
+ struct slave *slave = slave_lookup(lacp, slave_);
+
+ /* The slave may be enabled if it's attached to an aggregator and its
+ * partner is synchronized. The only exception is defaulted slaves.
+ * They are not required to have synchronized partners because they
+ * have no partners at all. They will only be attached if negotiations
+ * failed on all slaves in the bond. */
+ return slave->attached && (slave->partner.state & LACP_STATE_SYNC
+ || slave->status == LACP_DEFAULTED);
+ } else {
+ return true;
+ }
+}
+
+/* This function should be called periodically to update 'lacp'. */
+void
+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 (slave->status == LACP_CURRENT) {
+ slave_set_expired(slave);
+ } else if (slave->status == LACP_EXPIRED) {
+ slave_set_defaulted(slave);
+ }
+ }
+ slave_update_actor(slave);
+ }
+
+ if (lacp->update) {
+ lacp_update_attached(lacp);
+ }
+
+ HMAP_FOR_EACH (slave, node, &lacp->slaves) {
+ struct lacp_pdu pdu;
+
+ if (time_msec() < slave->tx || !slave_may_tx(slave)) {
+ continue;
+ }
+
+ compose_lacp_pdu(&slave->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);
+ }
+}
+
+/* Causes poll_block() to wake up when lacp_run() needs to be called again. */
+void
+lacp_wait(struct lacp *lacp)
+{
+ struct slave *slave;
+
+ HMAP_FOR_EACH (slave, node, &lacp->slaves) {
+ if (slave_may_tx(slave)) {
+ poll_timer_wait_until(slave->tx);
+ }
+
+ if (slave->status != LACP_DEFAULTED) {
+ poll_timer_wait_until(slave->rx);
+ }
+ }
+}
+\f
+/* Static Helpers. */
+
+/* Updates the attached status of all slaves controlled b 'lacp' and sets its
+ * negotiated parameter to true if any slaves are attachable. */
+static void
+lacp_update_attached(struct lacp *lacp)
+{
+ struct slave *lead, *slave;
+ struct lacp_info lead_pri;
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 10);
+
+ lacp->update = false;
+
+ lead = NULL;
+ HMAP_FOR_EACH (slave, node, &lacp->slaves) {
+ struct lacp_info pri;
+
+ slave->attached = true;
+
+ /* XXX: In the future allow users to configure the expected system ID.
+ * For now just special case loopback. */
+ if (eth_addr_equals(slave->partner.sys_id, slave->actor.sys_id)) {
+ VLOG_WARN_RL(&rl, "slave %s: Loopback detected. Slave is "
+ "connected to its own bond", slave->name);
+ slave->attached = false;
+ continue;
+ }
+
+ if (slave->status == LACP_DEFAULTED) {
+ continue;
+ }
+
+ slave_get_priority(slave, &pri);
+
+ if (!lead || memcmp(&pri, &lead_pri, sizeof pri) < 0) {
+ lead = slave;
+ lead_pri = pri;
+ }
+ }
+
+ lacp->negotiated = lead != NULL;
+
+ if (lead) {
+ HMAP_FOR_EACH (slave, node, &lacp->slaves) {
+ if (slave->status == LACP_DEFAULTED
+ || lead->partner.key != slave->partner.key
+ || !eth_addr_equals(lead->partner.sys_id,
+ slave->partner.sys_id)) {
+ slave->attached = false;
+ }
+ }
+ }
+}
+
+static void
+slave_destroy(struct slave *slave)
+{
+ if (slave) {
+ struct lacp *lacp = slave->lacp;
+
+ lacp->update = true;
+ hmap_remove(&lacp->slaves, &slave->node);
+
+ if (lacp->key_slave == slave) {
+ struct hmap_node *slave_node = hmap_first(&lacp->slaves);
+
+ if (slave_node) {
+ lacp->key_slave = CONTAINER_OF(slave_node, struct slave, node);
+ } else {
+ lacp->key_slave = NULL;
+ }
+ }
+
+ free(slave->name);
+ free(slave);
+ }
+}
+
+static void
+slave_set_defaulted(struct slave *slave)
+{
+ memset(&slave->partner, 0, sizeof slave->partner);
+
+ slave->tx = LLONG_MIN;
+ slave->lacp->update = true;
+ slave->status = LACP_DEFAULTED;
+}
+
+static void
+slave_set_expired(struct slave *slave)
+{
+ slave->status = LACP_EXPIRED;
+ slave->partner.state |= LACP_STATE_TIME;
+ slave->partner.state &= ~LACP_STATE_SYNC;
+
+ slave->rx = time_msec() + LACP_FAST_TIME_RX;
+ slave->tx = LLONG_MIN;
+}
+
+static void
+slave_update_actor(struct slave *slave)
+{
+ uint8_t state = 0;
+
+ if (slave->lacp->active) {
+ state |= LACP_STATE_ACT;
+ }
+
+ if (slave->attached) {
+ state |= LACP_STATE_SYNC;
+ }
+
+ if (slave->status == LACP_DEFAULTED) {
+ state |= LACP_STATE_DEF;
+ }
+
+ if (slave->status == LACP_EXPIRED) {
+ state |= LACP_STATE_EXP;
+ }
+
+ if (hmap_count(&slave->lacp->slaves) > 1) {
+ state |= LACP_STATE_AGG;
+ }
+
+ if (slave->enabled) {
+ state |= LACP_STATE_COL | LACP_STATE_DIST;
+ }
+
+ slave->actor.state = state;
+ slave->actor.key = htons(slave->lacp->key_slave->port_id);
+ slave->actor.port_priority = htons(slave->port_priority);
+ slave->actor.port_id = htons(slave->port_id);
+ slave->actor.sys_priority = htons(slave->lacp->sys_priority);
+ memcpy(&slave->actor.sys_id, slave->lacp->sys_id, ETH_ADDR_LEN);
+}
+
+/* Given 'slave', populates 'priority' with data representing its LACP link
+ * priority. If two priority objects populated by this function are compared
+ * using memcmp, the higher priority link will be less than the lower priority
+ * link. */
+static void
+slave_get_priority(struct slave *slave, struct lacp_info *priority)
+{
+ uint16_t partner_priority, actor_priority;
+
+ /* Choose the lacp_info of the higher priority system by comparing their
+ * system priorities and mac addresses. */
+ actor_priority = ntohs(slave->actor.sys_priority);
+ partner_priority = ntohs(slave->partner.sys_priority);
+ if (actor_priority < partner_priority) {
+ *priority = slave->actor;
+ } else if (partner_priority < actor_priority) {
+ *priority = slave->partner;
+ } else if (eth_addr_compare_3way(slave->actor.sys_id,
+ slave->partner.sys_id) < 0) {
+ *priority = slave->actor;
+ } else {
+ *priority = slave->partner;
+ }
+
+ /* Key and state are not used in priority comparisons. */
+ priority->key = 0;
+ priority->state = 0;
+}
+
+static bool
+slave_may_tx(const struct slave *slave)
+{
+ return slave->lacp->active || slave->status != LACP_DEFAULTED;
+}
+
+static struct slave *
+slave_lookup(const struct lacp *lacp, const void *slave_)
+{
+ struct slave *slave;
+
+ HMAP_FOR_EACH_IN_BUCKET (slave, node, hash_pointer(slave_, 0),
+ &lacp->slaves) {
+ if (slave->aux == slave_) {
+ return slave;
+ }
+ }
+
+ return NULL;
+}
+\f
+static struct lacp *
+lacp_find(const char *name)
+{
+ struct lacp *lacp;
+
+ LIST_FOR_EACH (lacp, node, &all_lacps) {
+ if (!strcmp(lacp->name, name)) {
+ return lacp;
+ }
+ }
+
+ return NULL;
+}
+
+static void
+ds_put_lacp_state(struct ds *ds, uint8_t state)
+{
+ if (state & LACP_STATE_ACT) {
+ ds_put_cstr(ds, "activity ");
+ }
+
+ if (state & LACP_STATE_TIME) {
+ ds_put_cstr(ds, "timeout ");
+ }
+
+ if (state & LACP_STATE_AGG) {
+ ds_put_cstr(ds, "aggregation ");
+ }
+
+ if (state & LACP_STATE_SYNC) {
+ ds_put_cstr(ds, "synchronized ");
+ }
+
+ if (state & LACP_STATE_COL) {
+ ds_put_cstr(ds, "collecting ");
+ }
+
+ if (state & LACP_STATE_DIST) {
+ ds_put_cstr(ds, "distributing ");
+ }
+
+ if (state & LACP_STATE_DEF) {
+ ds_put_cstr(ds, "defaulted ");
+ }
+
+ if (state & LACP_STATE_EXP) {
+ ds_put_cstr(ds, "expired ");
+ }
+}
+
+static void
+lacp_unixctl_show(struct unixctl_conn *conn,
+ const char *args, void *aux OVS_UNUSED)
+{
+ struct ds ds = DS_EMPTY_INITIALIZER;
+ struct lacp *lacp;
+ struct slave *slave;
+
+ lacp = lacp_find(args);
+ if (!lacp) {
+ unixctl_command_reply(conn, 501, "no such lacp object");
+ return;
+ }
+
+ 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, "\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: ");
+ if (lacp->key_slave) {
+ ds_put_format(&ds, "%u", lacp->key_slave->port_id);
+ } else {
+ ds_put_cstr(&ds, "none");
+ }
+ ds_put_cstr(&ds, "\n");
+
+ HMAP_FOR_EACH (slave, node, &lacp->slaves) {
+ char *status;
+
+ slave_update_actor(slave);
+ switch (slave->status) {
+ case LACP_CURRENT:
+ status = "current";
+ break;
+ case LACP_EXPIRED:
+ status = "expired";
+ break;
+ case LACP_DEFAULTED:
+ status = "defaulted";
+ break;
+ default:
+ 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, "\tport_id: %u\n", slave->port_id);
+ ds_put_format(&ds, "\tport_priority: %u\n", slave->port_priority);
+
+ ds_put_format(&ds, "\n\tactor sys_id: " ETH_ADDR_FMT "\n",
+ ETH_ADDR_ARGS(slave->actor.sys_id));
+ ds_put_format(&ds, "\tactor sys_priority: %u\n",
+ ntohs(slave->actor.sys_priority));
+ ds_put_format(&ds, "\tactor port_id: %u\n",
+ ntohs(slave->actor.port_id));
+ ds_put_format(&ds, "\tactor port_priority: %u\n",
+ ntohs(slave->actor.port_priority));
+ ds_put_format(&ds, "\tactor key: %u\n",
+ ntohs(slave->actor.key));
+ ds_put_cstr(&ds, "\tactor state: ");
+ ds_put_lacp_state(&ds, slave->actor.state);
+ ds_put_cstr(&ds, "\n\n");
+
+ ds_put_format(&ds, "\tpartner sys_id: " ETH_ADDR_FMT "\n",
+ ETH_ADDR_ARGS(slave->partner.sys_id));
+ ds_put_format(&ds, "\tpartner sys_priority: %u\n",
+ ntohs(slave->partner.sys_priority));
+ ds_put_format(&ds, "\tpartner port_id: %u\n",
+ ntohs(slave->partner.port_id));
+ ds_put_format(&ds, "\tpartner port_priority: %u\n",
+ ntohs(slave->partner.port_priority));
+ ds_put_format(&ds, "\tpartner key: %u\n",
+ ntohs(slave->partner.key));
+ ds_put_cstr(&ds, "\tpartner state: ");
+ ds_put_lacp_state(&ds, slave->partner.state);
+ ds_put_cstr(&ds, "\n");
+ }
+
+ unixctl_command_reply(conn, 200, ds_cstr(&ds));
+ ds_destroy(&ds);
+}
--- /dev/null
+/*
+ * Copyright (c) 2011 Nicira Networks.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LACP_H
+#define LACP_H 1
+
+#include <stdbool.h>
+#include <stdint.h>
+
+struct ds;
+struct lacp;
+struct lacp_pdu;
+struct ofpbuf;
+
+/* Function called when a LACP PDU is ready to be sent out the given slave */
+typedef void lacp_send_pdu(void *slave, const struct lacp_pdu *);
+
+void lacp_init(void);
+struct lacp *lacp_create(void);
+void lacp_destroy(struct lacp *);
+void lacp_configure(struct lacp *, const char *name, uint8_t sys_id[8],
+ uint16_t sys_priority, bool active);
+void lacp_process_pdu(struct lacp *, const void *slave,
+ const struct lacp_pdu *);
+bool lacp_negotiated(const struct lacp *);
+
+void lacp_slave_register(struct lacp *, void *slave_, const char *name,
+ uint16_t port_id, uint16_t port_priority);
+void lacp_slave_unregister(struct lacp *, const void *slave);
+void lacp_slave_enable(struct lacp *lacp, void *slave_, bool enabled);
+void lacp_slave_carrier_changed(const struct lacp *, const void *slave);
+bool lacp_slave_may_enable(const struct lacp *, const void *slave);
+
+void lacp_run(struct lacp *, lacp_send_pdu *);
+void lacp_wait(struct lacp *);
+
+#endif /* lacp.h */
#include "hash.h"
#include "hmap.h"
#include "jsonrpc.h"
+#include "lacp.h"
#include "list.h"
#include "mac-learning.h"
#include "netdev.h"
static void dst_set_add(struct dst_set *, const struct dst *);
static void dst_set_free(struct dst_set *);
-enum lacp_status {
- LACP_CURRENT = 0x01, /* Current State. */
- LACP_EXPIRED = 0x02, /* Expired State. */
- LACP_DEFAULTED = 0x04, /* Partner is defaulted. */
- LACP_ATTACHED = 0x08, /* Attached. Interface may be choosen for flows. */
-};
-
struct iface {
/* These members are always valid. */
struct port *port; /* Containing port. */
const struct ovsrec_interface *cfg;
/* LACP information. */
- enum lacp_status lacp_status; /* LACP status. */
- uint16_t lacp_priority; /* LACP port priority. */
- struct lacp_info lacp_actor; /* LACP actor information. */
- struct lacp_info lacp_partner; /* LACP partner information. */
- long long int lacp_tx; /* Next LACP message transmission time. */
- long long int lacp_rx; /* Next LACP message receive time. */
+ uint16_t lacp_priority; /* LACP port priority. */
};
#define BOND_MASK 0xff
int out_vlan;
};
-/* Flags for a port's lacp member. */
-#define LACP_ACTIVE 0x01 /* LACP is in active mode. */
-#define LACP_PASSIVE 0x02 /* LACP is in passive mode. */
-#define LACP_NEGOTIATED 0x04 /* LACP has successfully negotiated. */
-
#define FLOOD_PORT ((struct port *) 1) /* The 'flood' output port. */
struct port {
struct bridge *bridge;
long long int bond_next_fake_iface_update; /* Time of next update. */
/* LACP information. */
- int lacp; /* LACP status flags. 0 if LACP is off. */
- uint16_t lacp_key; /* LACP aggregation key. */
+ struct lacp *lacp; /* LACP object. NULL if LACP is disabled. */
+ bool lacp_active; /* True if LACP is active */
uint16_t lacp_priority; /* LACP system priority. */
- bool lacp_need_update; /* Need to update attached interfaces? */
/* SLB specific bonding info. */
struct bond_entry *bond_hash; /* An array of (BOND_MASK + 1) elements. */
static unixctl_cb_func bridge_unixctl_fdb_show;
static unixctl_cb_func qos_unixctl_show;
-static void lacp_run(struct port *);
-static void lacp_wait(struct port *);
-static void lacp_process_packet(const struct ofpbuf *, struct iface *);
-
static void bond_init(void);
static void bond_run(struct port *);
static void bond_wait(struct port *);
static void iface_update_cfm(struct iface *);
static void iface_refresh_cfm_stats(struct iface *iface);
static void iface_send_packet(struct iface *, struct ofpbuf *packet);
-static uint8_t iface_get_lacp_state(const struct iface *);
-static void iface_get_lacp_priority(struct iface *, struct lacp_info *);
-static void iface_set_lacp_defaulted(struct iface *);
-static void iface_set_lacp_expired(struct iface *);
static void iface_update_carrier(struct iface *, bool carrier);
static void shash_from_ovs_idl_map(char **keys, char **values, size_t n,
NULL);
unixctl_command_register("bridge/reconnect", bridge_unixctl_reconnect,
NULL);
+ lacp_init();
bond_init();
}
static bool
bond_is_tcp_hash(const struct port *port)
{
- return port->bond_mode == BM_TCP && port->lacp & LACP_NEGOTIATED;
+ return port->bond_mode == BM_TCP && lacp_negotiated(port->lacp);
}
static int
if (iface->enabled) {
return i;
} else if (iface->delay_expires < next_delay_expiration
- && (iface->lacp_status & LACP_ATTACHED
- || !(port->lacp & LACP_NEGOTIATED))) {
+ && lacp_slave_may_enable(port->lacp, iface)) {
best_down_slave = i;
next_delay_expiration = iface->delay_expires;
}
{
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
struct port *port = iface->port;
- bool up = iface->up;
+ bool up = iface->up && lacp_slave_may_enable(port->lacp, iface);
int updelay, downdelay;
updelay = port->updelay;
downdelay = port->downdelay;
- if (iface->port->lacp & LACP_NEGOTIATED) {
+ if (lacp_negotiated(port->lacp)) {
downdelay = 0;
updelay = 0;
}
- if (iface->port->lacp && up) {
- /* The interface is up if it's attached to an aggregator and its
- * partner is synchronized. The only exception is defaulted links.
- * They are not required to have synchronized partners because they
- * have no partners at all. However, they will only be attached if
- * negotiations failed on all interfaces in the bond. */
- up = iface->lacp_status & LACP_ATTACHED
- && (iface->lacp_partner.state & LACP_STATE_SYNC
- || iface->lacp_status & LACP_DEFAULTED);
- }
-
-
if ((up == iface->enabled) == (iface->delay_expires == LLONG_MAX)) {
/* Nothing to do. */
return;
}
/* When using LACP, do not accept packets from disabled interfaces. */
- if (in_port->lacp & LACP_NEGOTIATED && !in_iface->enabled) {
+ if (lacp_negotiated(in_port->lacp) && !in_iface->enabled) {
return false;
}
/* Packets received on non-LACP bonds need special attention to avoid
* duplicates. */
- if (in_port->n_ifaces > 1 && !(in_port->lacp & LACP_NEGOTIATED)) {
+ if (in_port->n_ifaces > 1 && !lacp_negotiated(in_port->lacp)) {
int src_idx;
bool is_grat_arp_locked;
return false;
} else if (flow->dl_type == htons(ETH_TYPE_LACP)) {
- if (iface && packet) {
- COVERAGE_INC(bridge_process_lacp);
- lacp_process_packet(packet, iface);
+ if (iface && iface->port->lacp && packet) {
+ const struct lacp_pdu *pdu = parse_lacp_packet(packet);
+
+ if (pdu) {
+ COVERAGE_INC(bridge_process_lacp);
+ lacp_process_pdu(iface->port->lacp, iface, pdu);
+ }
}
return false;
}
bridge_account_checkpoint_ofhook_cb,
};
\f
-/* LACP functions. */
-
-static void
-lacp_process_packet(const struct ofpbuf *packet, struct iface *iface)
-{
- const struct lacp_pdu *pdu;
-
- if (!iface->port->lacp) {
- return;
- }
-
- pdu = parse_lacp_packet(packet);
- if (!pdu) {
- return;
- }
-
- iface->lacp_status |= LACP_CURRENT;
- iface->lacp_status &= ~(LACP_EXPIRED | LACP_DEFAULTED);
- iface->lacp_rx = time_msec() + LACP_SLOW_TIME_RX;
-
- iface->lacp_actor.state = iface_get_lacp_state(iface);
- if (memcmp(&iface->lacp_actor, &pdu->partner, sizeof pdu->partner)) {
- iface->lacp_tx = 0;
- }
-
- if (memcmp(&iface->lacp_partner, &pdu->actor, sizeof pdu->actor)) {
- iface->port->lacp_need_update = true;
- iface->lacp_partner = pdu->actor;
- }
-}
-
-static void
-lacp_update_ifaces(struct port *port)
-{
- size_t i;
- struct iface *lead;
- struct lacp_info lead_pri;
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 10);
-
- port->lacp_need_update = false;
- COVERAGE_INC(bridge_lacp_update);
-
- if (!port->lacp) {
- return;
- }
-
- VLOG_DBG_RL(&rl, "port %s: re-evaluating LACP link status", port->name);
-
- lead = NULL;
- for (i = 0; i < port->n_ifaces; i++) {
- struct iface *iface = port->ifaces[i];
- struct lacp_info pri;
-
- iface->lacp_status |= LACP_ATTACHED;
- ofproto_revalidate(port->bridge->ofproto, iface->tag);
-
- /* Don't allow loopback interfaces to send traffic or lead. */
- if (eth_addr_equals(iface->lacp_partner.sys_id,
- iface->lacp_actor.sys_id)) {
- VLOG_WARN_RL(&rl, "iface %s: Loopback detected. Interface is "
- "connected to its own bridge", iface->name);
- iface->lacp_status &= ~LACP_ATTACHED;
- continue;
- }
-
- if (iface->lacp_status & LACP_DEFAULTED) {
- continue;
- }
-
- iface_get_lacp_priority(iface, &pri);
-
- if (!lead || memcmp(&pri, &lead_pri, sizeof pri) < 0) {
- lead = iface;
- lead_pri = pri;
- }
- }
-
- if (!lead) {
- port->lacp &= ~LACP_NEGOTIATED;
- return;
- }
-
- port->lacp |= LACP_NEGOTIATED;
-
- for (i = 0; i < port->n_ifaces; i++) {
- struct iface *iface = port->ifaces[i];
-
- if (iface->lacp_status & LACP_DEFAULTED
- || lead->lacp_partner.key != iface->lacp_partner.key
- || !eth_addr_equals(lead->lacp_partner.sys_id,
- iface->lacp_partner.sys_id)) {
- iface->lacp_status &= ~LACP_ATTACHED;
- }
- }
-}
-
-static bool
-lacp_iface_may_tx(const struct iface *iface)
-{
- return iface->port->lacp & LACP_ACTIVE
- || iface->lacp_status & (LACP_CURRENT | LACP_EXPIRED);
-}
-
-static void
-lacp_run(struct port *port)
-{
- size_t i;
- struct ofpbuf packet;
-
- if (!port->lacp) {
- return;
- }
-
- ofpbuf_init(&packet, ETH_HEADER_LEN + LACP_PDU_LEN);
-
- for (i = 0; i < port->n_ifaces; i++) {
- struct iface *iface = port->ifaces[i];
-
- if (time_msec() > iface->lacp_rx) {
- if (iface->lacp_status & LACP_CURRENT) {
- iface_set_lacp_expired(iface);
- } else if (iface->lacp_status & LACP_EXPIRED) {
- iface_set_lacp_defaulted(iface);
- }
- }
- }
-
- if (port->lacp_need_update) {
- lacp_update_ifaces(port);
- }
-
- for (i = 0; i < port->n_ifaces; i++) {
- struct iface *iface = port->ifaces[i];
- uint8_t ea[ETH_ADDR_LEN];
- int error;
-
- if (time_msec() < iface->lacp_tx || !lacp_iface_may_tx(iface)) {
- continue;
- }
-
- error = netdev_get_etheraddr(iface->netdev, ea);
- if (!error) {
- struct lacp_pdu pdu;
-
- iface->lacp_actor.state = iface_get_lacp_state(iface);
- compose_lacp_pdu(&iface->lacp_actor, &iface->lacp_partner, &pdu);
- compose_lacp_packet(&packet, ea, &pdu);
- iface_send_packet(iface, &packet);
- } else {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 10);
- VLOG_ERR_RL(&rl, "iface %s: failed to obtain Ethernet address "
- "(%s)", iface->name, strerror(error));
- }
-
- iface->lacp_tx = time_msec() +
- (iface->lacp_partner.state & LACP_STATE_TIME
- ? LACP_FAST_TIME_TX
- : LACP_SLOW_TIME_TX);
- }
- ofpbuf_uninit(&packet);
-}
-
-static void
-lacp_wait(struct port *port)
-{
- size_t i;
-
- if (!port->lacp) {
- return;
- }
-
- for (i = 0; i < port->n_ifaces; i++) {
- struct iface *iface = port->ifaces[i];
-
- if (lacp_iface_may_tx(iface)) {
- poll_timer_wait_until(iface->lacp_tx);
- }
-
- if (iface->lacp_status & (LACP_CURRENT | LACP_EXPIRED)) {
- poll_timer_wait_until(iface->lacp_rx);
- }
- }
-}
-\f
/* Bonding functions. */
/* Statistics for a single interface on a bonded port, used for load-based
return NULL;
}
-static void
-ds_put_lacp_state(struct ds *ds, uint8_t state)
-{
- if (state & LACP_STATE_ACT) {
- ds_put_cstr(ds, "activity ");
- }
-
- if (state & LACP_STATE_TIME) {
- ds_put_cstr(ds, "timeout ");
- }
-
- if (state & LACP_STATE_AGG) {
- ds_put_cstr(ds, "aggregation ");
- }
-
- if (state & LACP_STATE_SYNC) {
- ds_put_cstr(ds, "synchronized ");
- }
-
- if (state & LACP_STATE_COL) {
- ds_put_cstr(ds, "collecting ");
- }
-
- if (state & LACP_STATE_DIST) {
- ds_put_cstr(ds, "distributing ");
- }
-
- if (state & LACP_STATE_DEF) {
- ds_put_cstr(ds, "defaulted ");
- }
-
- if (state & LACP_STATE_EXP) {
- ds_put_cstr(ds, "expired ");
- }
-}
-
static void
bond_unixctl_show(struct unixctl_conn *conn,
const char *args, void *aux OVS_UNUSED)
if (port->lacp) {
ds_put_format(&ds, "lacp: %s\n",
- port->lacp & LACP_ACTIVE ? "active" : "passive");
+ port->lacp_active ? "active" : "passive");
} else {
ds_put_cstr(&ds, "lacp: off\n");
}
iface->delay_expires - time_msec());
}
- if (port->lacp) {
- ds_put_cstr(&ds, "\tstatus: ");
-
- if (iface->lacp_status & LACP_CURRENT) {
- ds_put_cstr(&ds, "current ");
- }
-
- if (iface->lacp_status & LACP_EXPIRED) {
- ds_put_cstr(&ds, "expired ");
- }
-
- if (iface->lacp_status & LACP_DEFAULTED) {
- ds_put_cstr(&ds, "defaulted ");
- }
-
- if (iface->lacp_status & LACP_ATTACHED) {
- ds_put_cstr(&ds, "attached ");
- }
-
- ds_put_cstr(&ds, "\n");
-
- ds_put_cstr(&ds, "\n\tactor sys_id: ");
- ds_put_format(&ds, ETH_ADDR_FMT,
- ETH_ADDR_ARGS(iface->lacp_actor.sys_id));
- ds_put_cstr(&ds, "\n");
-
- ds_put_format(&ds, "\tactor sys_priority: %u\n",
- ntohs(iface->lacp_actor.sys_priority));
-
- ds_put_format(&ds, "\tactor port_id: %u\n",
- ntohs(iface->lacp_actor.port_id));
-
- ds_put_format(&ds, "\tactor port_priority: %u\n",
- ntohs(iface->lacp_actor.port_priority));
-
- ds_put_format(&ds, "\tactor key: %u\n",
- ntohs(iface->lacp_actor.key));
-
- ds_put_cstr(&ds, "\tactor state: ");
- ds_put_lacp_state(&ds, iface_get_lacp_state(iface));
- ds_put_cstr(&ds, "\n\n");
-
- ds_put_cstr(&ds, "\tpartner sys_id: ");
- ds_put_format(&ds, ETH_ADDR_FMT,
- ETH_ADDR_ARGS(iface->lacp_partner.sys_id));
- ds_put_cstr(&ds, "\n");
-
- ds_put_format(&ds, "\tpartner sys_priority: %u\n",
- ntohs(iface->lacp_partner.sys_priority));
-
- ds_put_format(&ds, "\tpartner port_id: %u\n",
- ntohs(iface->lacp_partner.port_id));
-
- ds_put_format(&ds, "\tpartner port_priority: %u\n",
- ntohs(iface->lacp_partner.port_priority));
-
- ds_put_format(&ds, "\tpartner key: %u\n",
- ntohs(iface->lacp_partner.key));
-
- ds_put_cstr(&ds, "\tpartner state: ");
- ds_put_lacp_state(&ds, iface->lacp_partner.state);
- ds_put_cstr(&ds, "\n");
- }
-
if (port->bond_mode == BM_AB) {
continue;
}
\f
/* Port functions. */
+static void
+lacp_send_pdu_cb(void *aux, const struct lacp_pdu *pdu)
+{
+ struct iface *iface = aux;
+ uint8_t ea[ETH_ADDR_LEN];
+ int error;
+
+ error = netdev_get_etheraddr(iface->netdev, ea);
+ if (!error) {
+ struct ofpbuf packet;
+
+ ofpbuf_init(&packet, ETH_HEADER_LEN + LACP_PDU_LEN);
+ compose_lacp_packet(&packet, ea, pdu);
+ iface_send_packet(iface, &packet);
+ ofpbuf_uninit(&packet);
+ } else {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 10);
+ VLOG_ERR_RL(&rl, "iface %s: failed to obtain Ethernet address "
+ "(%s)", iface->name, strerror(error));
+ }
+}
+
static void
port_run(struct port *port)
{
port->miimon_next_update = time_msec() + port->miimon_interval;
}
- lacp_run(port);
+ if (port->lacp) {
+ for (i = 0; i < port->n_ifaces; i++) {
+ struct iface *iface = port->ifaces[i];
+ lacp_slave_enable(port->lacp, iface, iface->enabled);
+ }
+
+ lacp_run(port->lacp, lacp_send_pdu_cb);
+ }
+
bond_run(port);
for (i = 0; i < port->n_ifaces; i++) {
poll_timer_wait_until(port->miimon_next_update);
}
- lacp_wait(port);
+ if (port->lacp) {
+ lacp_wait(port->lacp);
+ }
+
bond_wait(port);
for (i = 0; i < port->n_ifaces; i++) {
if (!port->cfg->lacp) {
/* XXX when LACP implementation has been sufficiently tested, enable by
* default and make active on bonded ports. */
- port->lacp = 0;
+ lacp_destroy(port->lacp);
+ port->lacp = NULL;
} else if (!strcmp(port->cfg->lacp, "off")) {
- port->lacp = 0;
+ lacp_destroy(port->lacp);
+ port->lacp = NULL;
} else if (!strcmp(port->cfg->lacp, "active")) {
- port->lacp = LACP_ACTIVE;
+ if (!port->lacp) {
+ port->lacp = lacp_create();
+ }
+ port->lacp_active = true;
} else if (!strcmp(port->cfg->lacp, "passive")) {
- port->lacp = LACP_PASSIVE;
+ if (!port->lacp) {
+ port->lacp = lacp_create();
+ }
+ port->lacp_active = false;
} else {
VLOG_WARN("port %s: unknown LACP mode %s",
port->name, port->cfg->lacp);
- port->lacp = 0;
+ lacp_destroy(port->lacp);
+ port->lacp = NULL;
}
/* Get VLAN tag. */
static void
port_update_lacp(struct port *port)
{
- size_t i;
- bool key_changed;
-
- if (!port->lacp || port->n_ifaces < 1) {
- for (i = 0; i < port->n_ifaces; i++) {
- iface_set_lacp_defaulted(port->ifaces[i]);
- }
- return;
- }
-
- key_changed = true;
- for (i = 0; i < port->n_ifaces; i++) {
- struct iface *iface = port->ifaces[i];
+ if (port->lacp) {
+ size_t i;
- if (iface->dp_ifidx <= 0 || iface->dp_ifidx > UINT16_MAX) {
- port->lacp = 0;
- return;
- }
+ lacp_configure(port->lacp, port->name,
+ port->bridge->ea, port->lacp_priority,
+ port->lacp_active);
- if (iface->dp_ifidx == port->lacp_key) {
- key_changed = false;
+ for (i = 0; i < port->n_ifaces; i++) {
+ struct iface *iface = port->ifaces[i];
+ lacp_slave_register(port->lacp, iface, iface->name,
+ iface->dp_ifidx, iface->lacp_priority);
}
}
-
- if (key_changed) {
- port->lacp_key = port->ifaces[0]->dp_ifidx;
- }
-
- for (i = 0; i < port->n_ifaces; i++) {
- struct iface *iface = port->ifaces[i];
-
- iface->lacp_actor.sys_priority = htons(port->lacp_priority);
- memcpy(&iface->lacp_actor.sys_id, port->bridge->ea, ETH_ADDR_LEN);
-
- iface->lacp_actor.port_priority = htons(iface->lacp_priority);
- iface->lacp_actor.port_id = htons(iface->dp_ifidx);
- iface->lacp_actor.key = htons(port->lacp_key);
-
- iface->lacp_tx = 0;
- }
- port->lacp_need_update = true;
}
static void
\f
/* Interface functions. */
-static void
-iface_set_lacp_defaulted(struct iface *iface)
-{
- memset(&iface->lacp_partner, 0, sizeof iface->lacp_partner);
-
- iface->lacp_status |= LACP_DEFAULTED;
- iface->lacp_status &= ~(LACP_CURRENT | LACP_EXPIRED);
- iface->lacp_tx = 0;
- iface->port->lacp_need_update = true;
-}
-
-static void
-iface_set_lacp_expired(struct iface *iface)
-{
- iface->lacp_status &= ~LACP_CURRENT;
- iface->lacp_status |= LACP_EXPIRED;
- iface->lacp_partner.state |= LACP_STATE_TIME;
- iface->lacp_partner.state &= ~LACP_STATE_SYNC;
-
- iface->lacp_rx = time_msec() + LACP_FAST_TIME_RX;
- iface->lacp_tx = 0;
-}
-
-static uint8_t
-iface_get_lacp_state(const struct iface *iface)
-{
- uint8_t state = 0;
-
- if (iface->port->lacp & LACP_ACTIVE) {
- state |= LACP_STATE_ACT;
- }
-
- if (iface->lacp_status & LACP_ATTACHED) {
- state |= LACP_STATE_SYNC;
- }
-
- if (iface->lacp_status & LACP_DEFAULTED) {
- state |= LACP_STATE_DEF;
- }
-
- if (iface->lacp_status & LACP_EXPIRED) {
- state |= LACP_STATE_EXP;
- }
-
- if (iface->port->n_ifaces > 1) {
- state |= LACP_STATE_AGG;
- }
-
- if (iface->enabled) {
- state |= LACP_STATE_COL | LACP_STATE_DIST;
- }
-
- return state;
-}
-
-/* Given 'iface', populates 'priority' with data representing its LACP link
- * priority. If two priority objects populated by this function are compared
- * using memcmp, the higher priority link will be less than the lower priority
- * link. */
-static void
-iface_get_lacp_priority(struct iface *iface, struct lacp_info *priority)
-{
- uint16_t partner_priority, actor_priority;
-
- /* Choose the lacp_info of the higher priority system by comparing their
- * system priorities and mac addresses. */
- actor_priority = ntohs(iface->lacp_actor.sys_priority);
- partner_priority = ntohs(iface->lacp_partner.sys_priority);
- if (actor_priority < partner_priority) {
- *priority = iface->lacp_actor;
- } else if (partner_priority < actor_priority) {
- *priority = iface->lacp_partner;
- } else if (eth_addr_compare_3way(iface->lacp_actor.sys_id,
- iface->lacp_partner.sys_id) < 0) {
- *priority = iface->lacp_actor;
- } else {
- *priority = iface->lacp_partner;
- }
-
- /* Key and state are not used in priority comparisons. */
- priority->key = 0;
- priority->state = 0;
-}
-
static void
iface_send_packet(struct iface *iface, struct ofpbuf *packet)
{
iface->delay_expires = LLONG_MAX;
iface->netdev = NULL;
iface->cfg = if_cfg;
- iface_set_lacp_defaulted(iface);
-
- if (port->lacp & LACP_ACTIVE) {
- iface_set_lacp_expired(iface);
- }
shash_add_assert(&br->iface_by_name, iface->name, iface);
bool del_active = port->active_iface == iface->port_ifidx;
struct iface *del;
+ if (iface->port->lacp) {
+ lacp_slave_unregister(iface->port->lacp, iface);
+ }
+
if (port->monitor && iface->netdev) {
netdev_monitor_remove(port->monitor, iface->netdev);
}
}
iface->up = carrier;
-
- iface->lacp_tx = 0;
- if (iface->lacp_status & LACP_CURRENT) {
- iface_set_lacp_expired(iface);
+ if (iface->port->lacp) {
+ lacp_slave_carrier_changed(iface->port->lacp, iface);
}
}
the time to completion of an updelay or downdelay if one is in
progress, whether it is the active slave, the MAC hashes assigned to
the slave, and the MAC learning table entries that hash to each MAC.
+Any LACP information related to this bond may be found using the
+\fBlacp/show\fR command.
.IP "\fBbond/migrate\fR \fIport\fR \fIhash\fR \fIslave\fR"
Only valid for SLB bonds. Assigns a given MAC hash to a new slave.
\fIport\fR specifies the bond port, \fIhash\fR the MAC hash to be
Returns the hash value which would be used for \fImac\fR with \fIvlan\fR
if specified.
.
+.IP "\fBlacp/show\fR \fIport\fR"
+Lists all of the LACP related information about the given \fIport\fR:
+active or passive, aggregation key, system id, and system priority. Also
+lists information about each slave: whether it is enabled or disabled,
+whether it is attached or detached, port id and priority, actor
+information, and partner information.
+.
.so ofproto/ofproto-unixctl.man
.so lib/vlog-unixctl.man
.so lib/stress-unixctl.man