2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-31 06:15:47 +00:00

lacp: Require successful LACP negotiations when configured.

In the original Open vSwitch LACP implementation, when no slaves
found a LACP partner, the LACP module would attach all of them.
This allowed the LACP bond to fall back to a standard bond when
partnered with a non-LACP switch.  In practice, this has caused
confusion with marginal benefit, so this feature is removed with
this patch.

Signed-off-by: Ethan Jackson <ethan@nicira.com>
This commit is contained in:
Ethan Jackson
2011-11-28 13:54:08 -08:00
parent 26fbdf773f
commit bdebeece55
8 changed files with 85 additions and 61 deletions

2
NEWS
View File

@@ -1,6 +1,8 @@
post-v1.5.0
------------------------
- bonding
- LACP bonds no longer fall back to balance-slb when negotiations fail.
Instead they drop traffic.
- The default bond_mode changed from SLB to active-backup, to protect
unsuspecting users from the significant risks of SLB bonds (which are
documented in vswitchd/INTERNALS).

View File

@@ -26,6 +26,7 @@
#include "dynamic-string.h"
#include "flow.h"
#include "hmap.h"
#include "lacp.h"
#include "list.h"
#include "netdev.h"
#include "odp-util.h"
@@ -92,7 +93,7 @@ struct bond {
struct bond_slave *active_slave;
tag_type no_slaves_tag; /* Tag for flows when all slaves disabled. */
int updelay, downdelay; /* Delay before slave goes up/down, in ms. */
bool lacp_negotiated; /* LACP negotiations were successful. */
enum lacp_status lacp_status; /* Status of LACP negotiations. */
bool bond_revalidate; /* True if flows need revalidation. */
uint32_t basis; /* Basis for flow hash function. */
@@ -122,7 +123,6 @@ static void bond_enable_slave(struct bond_slave *, bool enable,
struct tag_set *);
static void bond_link_status_update(struct bond_slave *, struct tag_set *);
static void bond_choose_active_slave(struct bond *, struct tag_set *);
static bool bond_is_tcp_hash(const struct bond *);
static unsigned int bond_hash_src(const uint8_t mac[ETH_ADDR_LEN],
uint16_t vlan, uint32_t basis);
static unsigned int bond_hash_tcp(const struct flow *, uint16_t vlan,
@@ -402,13 +402,12 @@ bond_slave_set_may_enable(struct bond *bond, void *slave_, bool may_enable)
*
* The caller should check bond_should_send_learning_packets() afterward. */
void
bond_run(struct bond *bond, struct tag_set *tags, bool lacp_negotiated)
bond_run(struct bond *bond, struct tag_set *tags, enum lacp_status lacp_status)
{
struct bond_slave *slave;
bool is_tcp_hash = bond_is_tcp_hash(bond);
if (bond->lacp_negotiated != lacp_negotiated) {
bond->lacp_negotiated = lacp_negotiated;
if (bond->lacp_status != lacp_status) {
bond->lacp_status = lacp_status;
bond->bond_revalidate = true;
}
@@ -427,10 +426,6 @@ bond_run(struct bond *bond, struct tag_set *tags, bool lacp_negotiated)
bond->next_fake_iface_update = time_msec() + 1000;
}
if (is_tcp_hash != bond_is_tcp_hash(bond)) {
bond->bond_revalidate = true;
}
if (bond->bond_revalidate) {
bond->bond_revalidate = false;
@@ -488,8 +483,9 @@ bond_wait(struct bond *bond)
static bool
may_send_learning_packets(const struct bond *bond)
{
return !bond->lacp_negotiated && bond->balance != BM_AB &&
bond->active_slave;
return bond->lacp_status == LACP_DISABLED
&& bond->balance != BM_AB
&& bond->active_slave;
}
/* Returns true if 'bond' needs the client to send out packets to assist with
@@ -566,9 +562,14 @@ bond_check_admissibility(struct bond *bond, const void *slave_,
* assume the remote switch is aware of the bond and will "do the right
* thing". However, as a precaution we drop packets on disabled slaves
* because no correctly implemented partner switch should be sending
* packets to them. */
if (bond->lacp_negotiated) {
return slave->enabled ? BV_ACCEPT : BV_DROP;
* packets to them.
*
* If LACP is configured, but LACP negotiations have been unsuccessful, we
* drop all incoming traffic. */
switch (bond->lacp_status) {
case LACP_NEGOTIATED: return slave->enabled ? BV_ACCEPT : BV_DROP;
case LACP_CONFIGURED: return BV_DROP;
case LACP_DISABLED: break;
}
/* Drop all multicast packets on inactive slaves. */
@@ -595,10 +596,11 @@ bond_check_admissibility(struct bond *bond, const void *slave_,
return BV_ACCEPT;
case BM_TCP:
/* TCP balancing has degraded to SLB (otherwise the
* bond->lacp_negotiated check above would have processed this).
*
* Fall through. */
/* TCP balanced bonds require successful LACP negotiated. Based on the
* above check, LACP is off on this bond. Therfore, we drop all
* incoming traffic. */
return BV_DROP;
case BM_SLB:
/* Drop all packets for which we have learned a different input port,
* because we probably sent the packet on one slave and got it back on
@@ -950,11 +952,6 @@ bond_print_details(struct ds *ds, const struct bond *bond)
ds_put_format(ds, "bond_mode: %s\n",
bond_mode_to_string(bond->balance));
if (bond->balance != BM_AB) {
ds_put_format(ds, "bond-hash-algorithm: %s\n",
bond_is_tcp_hash(bond) ? "balance-tcp" : "balance-slb");
}
ds_put_format(ds, "bond-hash-basis: %"PRIu32"\n", bond->basis);
ds_put_format(ds, "updelay: %d ms\n", bond->updelay);
@@ -965,8 +962,21 @@ bond_print_details(struct ds *ds, const struct bond *bond)
bond->next_rebalance - time_msec());
}
ds_put_format(ds, "lacp_negotiated: %s\n",
bond->lacp_negotiated ? "true" : "false");
ds_put_cstr(ds, "lacp_status: ");
switch (bond->lacp_status) {
case LACP_NEGOTIATED:
ds_put_cstr(ds, "negotiated\n");
break;
case LACP_CONFIGURED:
ds_put_cstr(ds, "configured\n");
break;
case LACP_DISABLED:
ds_put_cstr(ds, "off\n");
break;
default:
ds_put_cstr(ds, "<unknown>\n");
break;
}
HMAP_FOR_EACH (slave, hmap_node, &bond->slaves) {
shash_add(&slave_shash, slave->name, slave);
@@ -1305,7 +1315,7 @@ bond_link_status_update(struct bond_slave *slave, struct tag_set *tags)
VLOG_INFO_RL(&rl, "interface %s: will not be %s",
slave->name, up ? "disabled" : "enabled");
} else {
int delay = (bond->lacp_negotiated ? 0
int delay = (bond->lacp_status != LACP_DISABLED ? 0
: up ? bond->updelay : bond->downdelay);
slave->delay_expires = time_msec() + delay;
if (delay) {
@@ -1324,13 +1334,6 @@ bond_link_status_update(struct bond_slave *slave, struct tag_set *tags)
}
}
static bool
bond_is_tcp_hash(const struct bond *bond)
{
return (bond->balance == BM_TCP && bond->lacp_negotiated)
|| bond->balance == BM_STABLE;
}
static unsigned int
bond_hash_src(const uint8_t mac[ETH_ADDR_LEN], uint16_t vlan, uint32_t basis)
{
@@ -1352,9 +1355,9 @@ bond_hash_tcp(const struct flow *flow, uint16_t vlan, uint32_t basis)
static unsigned int
bond_hash(const struct bond *bond, const struct flow *flow, uint16_t vlan)
{
assert(bond->balance != BM_AB);
assert(bond->balance == BM_TCP || bond->balance == BM_SLB);
return (bond_is_tcp_hash(bond)
return (bond->balance == BM_TCP
? bond_hash_tcp(flow, vlan, bond->basis)
: bond_hash_src(flow->dl_src, vlan, bond->basis));
}
@@ -1381,7 +1384,7 @@ choose_stb_slave(const struct bond *bond, const struct flow *flow,
best = NULL;
best_hash = 0;
flow_hash = bond_hash(bond, flow, vlan);
flow_hash = bond_hash_tcp(flow, vlan, bond->basis);
HMAP_FOR_EACH (slave, hmap_node, &bond->slaves) {
if (slave->enabled) {
uint32_t hash;
@@ -1403,14 +1406,26 @@ choose_output_slave(const struct bond *bond, const struct flow *flow,
{
struct bond_entry *e;
if (bond->lacp_status == LACP_CONFIGURED) {
/* LACP has been configured on this bond but negotiations were
* unsuccussful. Drop all traffic. */
return NULL;
}
switch (bond->balance) {
case BM_AB:
return bond->active_slave;
case BM_STABLE:
return choose_stb_slave(bond, flow, vlan);
case BM_SLB:
case BM_TCP:
if (bond->lacp_status != LACP_NEGOTIATED) {
/* Must have LACP negotiations for TCP balanced bonds. */
return NULL;
}
/* Fall Through. */
case BM_SLB:
e = lookup_bond_entry(bond, flow, vlan);
if (!e->slave || !e->slave->enabled) {
e->slave = CONTAINER_OF(hmap_random_node(&bond->slaves),

View File

@@ -26,6 +26,7 @@
struct flow;
struct netdev;
struct ofpbuf;
enum lacp_status;
/* How flows are balanced among bond slaves. */
enum bond_mode {
@@ -68,7 +69,7 @@ void bond_slave_register(struct bond *, void *slave_,
void bond_slave_set_netdev(struct bond *, void *slave_, struct netdev *);
void bond_slave_unregister(struct bond *, const void *slave);
void bond_run(struct bond *, struct tag_set *, bool lacp_negotiated);
void bond_run(struct bond *, struct tag_set *, enum lacp_status);
void bond_wait(struct bond *);
void bond_slave_set_may_enable(struct bond *, void *slave_, bool may_enable);

View File

@@ -301,12 +301,17 @@ lacp_process_packet(struct lacp *lacp, const void *slave_,
}
}
/* Returns true if 'lacp' has successfully negotiated with its partner. False
* if 'lacp' is NULL. */
bool
lacp_negotiated(const struct lacp *lacp)
/* Returns the lacp_status of the given 'lacp' object (which may be NULL). */
enum lacp_status
lacp_status(const struct lacp *lacp)
{
return lacp ? lacp->negotiated : false;
if (!lacp) {
return LACP_DISABLED;
} else if (lacp->negotiated) {
return LACP_NEGOTIATED;
} else {
return LACP_CONFIGURED;
}
}
/* Registers 'slave_' as subordinate to 'lacp'. This should be called at least
@@ -384,12 +389,8 @@ lacp_slave_may_enable(const struct lacp *lacp, const void *slave_)
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);
* partner is synchronized.*/
return slave->attached && (slave->partner.state & LACP_STATE_SYNC);
} else {
return true;
}
@@ -504,14 +505,13 @@ lacp_update_attached(struct lacp *lacp)
HMAP_FOR_EACH (slave, node, &lacp->slaves) {
struct lacp_info pri;
slave->attached = true;
slave->attached = false;
/* 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->lacp->sys_id)) {
VLOG_WARN_RL(&rl, "slave %s: Loopback detected. Slave is "
"connected to its own bond", slave->name);
slave->attached = false;
continue;
}
@@ -519,6 +519,7 @@ lacp_update_attached(struct lacp *lacp)
continue;
}
slave->attached = true;
slave_get_priority(slave, &pri);
if (!lead || memcmp(&pri, &lead_pri, sizeof pri) < 0) {
@@ -531,8 +532,7 @@ lacp_update_attached(struct lacp *lacp)
if (lead) {
HMAP_FOR_EACH (slave, node, &lacp->slaves) {
if (slave->status == LACP_DEFAULTED
|| lead->partner.key != slave->partner.key
if (lead->partner.key != slave->partner.key
|| !eth_addr_equals(lead->partner.sys_id,
slave->partner.sys_id)) {
slave->attached = false;

View File

@@ -29,6 +29,12 @@ enum lacp_time {
LACP_TIME_CUSTOM /* Nonstandard custom mode. */
};
enum lacp_status {
LACP_NEGOTIATED, /* Successful LACP negotations. */
LACP_CONFIGURED, /* LACP is enabled but not negotiated. */
LACP_DISABLED /* LACP is not enabled. */
};
struct lacp_settings {
char *name; /* Name (for debugging). */
uint8_t id[ETH_ADDR_LEN]; /* System ID. Must be nonzero. */
@@ -48,7 +54,7 @@ bool lacp_is_active(const struct lacp *);
void lacp_process_packet(struct lacp *, const void *slave,
const struct ofpbuf *packet);
bool lacp_negotiated(const struct lacp *);
enum lacp_status lacp_status(const struct lacp *);
struct lacp_slave_settings {
char *name; /* Name (for debugging). */

View File

@@ -1879,7 +1879,7 @@ bundle_run(struct ofbundle *bundle)
}
bond_run(bundle->bond, &bundle->ofproto->revalidate_set,
lacp_negotiated(bundle->lacp));
lacp_status(bundle->lacp));
if (bond_should_send_learning_packets(bundle->bond)) {
bundle_send_learning_packets(bundle);
}

View File

@@ -103,7 +103,7 @@ bond_mode: active-backup
bond-hash-basis: 0
updelay: 0 ms
downdelay: 0 ms
lacp_negotiated: true
lacp_status: negotiated
slave p1: disabled
may_enable: false

View File

@@ -757,8 +757,7 @@
<p>
The following modes require the upstream switch to support 802.3ad with
successful LACP negotiation. If LACP negotiation fails then
<code>balance-slb</code> style flow hashing is used as a fallback:
successful LACP negotiation:
</p>
<dl>
@@ -861,8 +860,9 @@
connected to. <code>active</code> ports are allowed to initiate LACP
negotiations. <code>passive</code> ports are allowed to participate
in LACP negotiations initiated by a remote switch, but not allowed to
initiate such negotiations themselves. Defaults to <code>off</code>
if unset.
initiate such negotiations themselves. If LACP is enabled on a port
whose partner switch does not support LACP, the bond will be
disabled. Defaults to <code>off</code> if unset.
</column>
<column name="other_config" key="lacp-system-id">