X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Flacp.c;h=374d915a94549e27c6b513d3b7d4b1da30d26ef1;hb=51c14ddd8df9617e641748a98ac52b78fd19290a;hp=7d68661150be7fbbeedf8662e5d8e6809d463c0a;hpb=6aa7430820bb327b65de0e02baf928ed663bef2b;p=sliver-openvswitch.git diff --git a/lib/lacp.c b/lib/lacp.c index 7d6866115..374d915a9 100644 --- a/lib/lacp.c +++ b/lib/lacp.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011 Nicira Networks +/* Copyright (c) 2011 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,12 +25,63 @@ #include "ofpbuf.h" #include "packets.h" #include "poll-loop.h" +#include "shash.h" +#include "timer.h" #include "timeval.h" #include "unixctl.h" #include "vlog.h" VLOG_DEFINE_THIS_MODULE(lacp); +/* Masks for lacp_info state member. */ +#define LACP_STATE_ACT 0x01 /* Activity. Active or passive? */ +#define LACP_STATE_TIME 0x02 /* Timeout. Short or long timeout? */ +#define LACP_STATE_AGG 0x04 /* Aggregation. Is the link is bondable? */ +#define LACP_STATE_SYNC 0x08 /* Synchronization. Is the link in up to date? */ +#define LACP_STATE_COL 0x10 /* Collecting. Is the link receiving frames? */ +#define LACP_STATE_DIST 0x20 /* Distributing. Is the link sending frames? */ +#define LACP_STATE_DEF 0x40 /* Defaulted. Using default partner info? */ +#define LACP_STATE_EXP 0x80 /* Expired. Using expired partner info? */ + +#define LACP_FAST_TIME_TX 1000 /* Fast transmission rate. */ +#define LACP_SLOW_TIME_TX 30000 /* Slow transmission rate. */ +#define LACP_RX_MULTIPLIER 3 /* Multiply by TX rate to get RX rate. */ + +#define LACP_INFO_LEN 15 +struct lacp_info { + ovs_be16 sys_priority; /* System priority. */ + uint8_t sys_id[ETH_ADDR_LEN]; /* System ID. */ + ovs_be16 key; /* Operational key. */ + ovs_be16 port_priority; /* Port priority. */ + ovs_be16 port_id; /* Port ID. */ + uint8_t state; /* State mask. See LACP_STATE macros. */ +} __attribute__((packed)); +BUILD_ASSERT_DECL(LACP_INFO_LEN == sizeof(struct lacp_info)); + +#define LACP_PDU_LEN 110 +struct lacp_pdu { + uint8_t subtype; /* Always 1. */ + uint8_t version; /* Always 1. */ + + uint8_t actor_type; /* Always 1. */ + uint8_t actor_len; /* Always 20. */ + struct lacp_info actor; /* LACP actor information. */ + uint8_t z1[3]; /* Reserved. Always 0. */ + + uint8_t partner_type; /* Always 2. */ + uint8_t partner_len; /* Always 20. */ + struct lacp_info partner; /* LACP partner information. */ + uint8_t z2[3]; /* Reserved. Always 0. */ + + uint8_t collector_type; /* Always 3. */ + uint8_t collector_len; /* Always 16. */ + ovs_be16 collector_delay; /* Maximum collector delay. Set to UINT16_MAX. */ + uint8_t z3[64]; /* Combination of several fields. Always 0. */ +} __attribute__((packed)); +BUILD_ASSERT_DECL(LACP_PDU_LEN == sizeof(struct lacp_pdu)); + +/* Implementation. */ + enum slave_status { LACP_CURRENT, /* Current State. Partner up to date. */ LACP_EXPIRED, /* Expired State. Partner out of date. */ @@ -47,6 +98,7 @@ struct lacp { struct hmap slaves; /* Slaves this LACP object controls. */ struct slave *key_slave; /* Slave whose ID will be the aggregation key. */ + bool fast; /* True if using fast probe interval. */ bool negotiated; /* True if LACP negotiations were successful. */ bool update; /* True if lacp_update() needs to be called. */ }; @@ -58,15 +110,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 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. */ + struct lacp_info ntt_actor; /* Used to decide if we Need To Transmit. */ + struct timer tx; /* Next message transmission timer. */ + struct timer rx; /* Expected message receive timer. */ }; static struct list all_lacps = LIST_INITIALIZER(&all_lacps); @@ -76,19 +128,65 @@ 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_actor(struct slave *, struct lacp_info *actor); 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 bool info_tx_equal(struct lacp_info *, struct lacp_info *); + +static unixctl_cb_func lacp_unixctl_show; + +/* Populates 'pdu' with a LACP PDU comprised of 'actor' and 'partner'. */ +static 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'. */ +static const struct lacp_pdu * +parse_lacp_packet(const struct ofpbuf *b) +{ + const struct lacp_pdu *pdu; -static void lacp_unixctl_show(struct unixctl_conn *, const char *args, - void *aux); + 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; + } +} + +/* LACP Protocol Implementation. */ /* Initializes the lacp module. */ void lacp_init(void) { - unixctl_command_register("lacp/show", lacp_unixctl_show, NULL); + unixctl_command_register("lacp/show", "[port]", 0, 1, + lacp_unixctl_show, NULL); } /* Creates a LACP object. */ @@ -121,41 +219,59 @@ 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)) { + assert(!eth_addr_is_zero(s->id)); + + if (!lacp->name || strcmp(s->name, lacp->name)) { free(lacp->name); - lacp->name = xstrdup(name); + lacp->name = xstrdup(s->name); } - memcpy(lacp->sys_id, sys_id, ETH_ADDR_LEN); - lacp->sys_priority = sys_priority; - lacp->active = active; + if (!eth_addr_equals(lacp->sys_id, s->id) + || lacp->sys_priority != s->priority) { + memcpy(lacp->sys_id, s->id, ETH_ADDR_LEN); + lacp->sys_priority = s->priority; + lacp->update = true; + } + + lacp->active = s->active; + lacp->fast = s->fast; } -/* 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(). */ +/* 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 'packet' which was received on 'slave_'. This function should be + * called on all packets received on 'slave_' with Ethernet Type ETH_TYPE_LACP. + */ void -lacp_process_pdu(struct lacp *lacp, const void *slave_, - const struct lacp_pdu *pdu) +lacp_process_packet(struct lacp *lacp, const void *slave_, + const struct ofpbuf *packet) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); struct slave *slave = slave_lookup(lacp, slave_); + const struct lacp_pdu *pdu; + long long int tx_rate; + + pdu = parse_lacp_packet(packet); + if (!pdu) { + VLOG_WARN_RL(&rl, "%s: received an unparsable LACP PDU.", lacp->name); + return; + } slave->status = LACP_CURRENT; - slave->rx = time_msec() + LACP_SLOW_TIME_RX; + tx_rate = lacp->fast ? LACP_FAST_TIME_TX : LACP_SLOW_TIME_TX; + timer_set_duration(&slave->rx, LACP_RX_MULTIPLIER * tx_rate); - /* 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; - } + slave->ntt_actor = pdu->partner; /* 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 @@ -166,20 +282,25 @@ lacp_process_pdu(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 * 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_); @@ -195,17 +316,18 @@ 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; - slave->tx = LLONG_MIN; lacp->update = true; if (lacp->active || lacp->negotiated) { @@ -222,35 +344,30 @@ 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. */ +/* This function should be called whenever the carrier status of 'slave_' has + * changed. If 'lacp' is null, this function has no effect.*/ void -lacp_slave_enable(struct lacp *lacp, void *slave_, bool enabled) +lacp_slave_carrier_changed(const struct lacp *lacp, const void *slave_) { - struct slave *slave = slave_lookup(lacp, slave_); + if (lacp) { + struct slave *slave = slave_lookup(lacp, slave_); - if (slave->enabled != enabled) { - slave->enabled = enabled; - slave->tx = LLONG_MIN; + if (slave->status == LACP_CURRENT || slave->lacp->active) { + slave_set_expired(slave); + } } } -/* 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_) +static bool +slave_may_enable__(struct slave *slave) { - struct slave *slave = slave_lookup(lacp, slave_); - - slave->tx = LLONG_MIN; - if (slave->status == LACP_CURRENT || slave->lacp->active) { - slave_set_expired(slave); - } + /* The slave may be enabled if it's attached to an aggregator and its + * partner is synchronized.*/ + return slave->attached && (slave->partner.state & LACP_STATE_SYNC); } /* This function should be called before enabling 'slave_' to send or receive @@ -260,20 +377,29 @@ 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); + return slave_may_enable__(slave_lookup(lacp, slave_)); } else { return true; } } +/* 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) @@ -281,14 +407,13 @@ 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) { slave_set_defaulted(slave); } } - slave_update_actor(slave); } if (lacp->update) { @@ -296,19 +421,29 @@ lacp_run(struct lacp *lacp, lacp_send_pdu *send_pdu) } HMAP_FOR_EACH (slave, node, &lacp->slaves) { - struct lacp_pdu pdu; + struct lacp_info actor; - if (time_msec() < slave->tx || !slave_may_tx(slave)) { + if (!slave_may_tx(slave)) { continue; } - compose_lacp_pdu(&slave->actor, &slave->partner, &pdu); - send_pdu(slave->aux, &pdu); + slave_get_actor(slave, &actor); + + if (timer_expired(&slave->tx) + || !info_tx_equal(&actor, &slave->ntt_actor)) { + long long int duration; + struct lacp_pdu pdu; - slave->tx = time_msec() + - (slave->partner.state & LACP_STATE_TIME - ? LACP_FAST_TIME_TX - : LACP_SLOW_TIME_TX); + slave->ntt_actor = actor; + compose_lacp_pdu(&actor, &slave->partner, &pdu); + send_pdu(slave->aux, &pdu, sizeof pdu); + + duration = (slave->partner.state & LACP_STATE_TIME + ? LACP_FAST_TIME_TX + : LACP_SLOW_TIME_TX); + + timer_set_duration(&slave->tx, duration); + } } } @@ -320,18 +455,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); } } } /* 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) @@ -346,14 +481,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->actor.sys_id)) { + 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; } @@ -361,6 +495,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) { @@ -373,8 +508,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; @@ -412,7 +546,6 @@ 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; } @@ -424,19 +557,24 @@ slave_set_expired(struct slave *slave) slave->partner.state |= LACP_STATE_TIME; slave->partner.state &= ~LACP_STATE_SYNC; - slave->rx = time_msec() + LACP_FAST_TIME_RX; - slave->tx = LLONG_MIN; + timer_set_duration(&slave->rx, LACP_RX_MULTIPLIER * LACP_FAST_TIME_TX); } static void -slave_update_actor(struct slave *slave) +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->fast) { + state |= LACP_STATE_TIME; + } + if (slave->attached) { state |= LACP_STATE_SYNC; } @@ -449,20 +587,25 @@ slave_update_actor(struct slave *slave) state |= LACP_STATE_EXP; } - if (hmap_count(&slave->lacp->slaves) > 1) { + if (hmap_count(&lacp->slaves) > 1) { state |= LACP_STATE_AGG; } - if (slave->enabled) { + if (slave->attached || !lacp->negotiated) { 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); + key = lacp->key_slave->key; + if (!key) { + key = lacp->key_slave->port_id; + } + + actor->state = state; + actor->key = htons(key); + actor->port_priority = htons(slave->port_priority); + actor->port_id = htons(slave->port_id); + 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 @@ -476,15 +619,15 @@ slave_get_priority(struct slave *slave, struct lacp_info *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); + actor_priority = slave->lacp->sys_priority; partner_priority = ntohs(slave->partner.sys_priority); if (actor_priority < partner_priority) { - *priority = slave->actor; + slave_get_actor(slave, priority); } else if (partner_priority < actor_priority) { *priority = slave->partner; - } else if (eth_addr_compare_3way(slave->actor.sys_id, + } else if (eth_addr_compare_3way(slave->lacp->sys_id, slave->partner.sys_id) < 0) { - *priority = slave->actor; + slave_get_actor(slave, priority); } else { *priority = slave->partner; } @@ -514,6 +657,28 @@ slave_lookup(const struct lacp *lacp, const void *slave_) return NULL; } + +/* Two lacp_info structures are tx_equal if and only if they do not differ in + * ways which would require a lacp_pdu transmission. */ +static bool +info_tx_equal(struct lacp_info *a, struct lacp_info *b) +{ + + /* LACP specification dictates that we transmit whenever the actor and + * remote_actor differ in the following fields: Port, Port Priority, + * System, System Priority, Aggregation Key, Activity State, Timeout State, + * Sync State, and Aggregation State. The state flags are most likely to + * change so are checked first. */ + return !((a->state ^ b->state) & (LACP_STATE_ACT + | LACP_STATE_TIME + | LACP_STATE_SYNC + | LACP_STATE_AGG)) + && a->port_id == b->port_id + && a->port_priority == b->port_priority + && a->key == b->key + && a->sys_priority == b->sys_priority + && eth_addr_equals(a->sys_id, b->sys_id); +} static struct lacp * lacp_find(const char *name) @@ -533,70 +698,82 @@ static void ds_put_lacp_state(struct ds *ds, uint8_t state) { if (state & LACP_STATE_ACT) { - ds_put_cstr(ds, "activity "); + ds_put_cstr(ds, " activity"); } if (state & LACP_STATE_TIME) { - ds_put_cstr(ds, "timeout "); + ds_put_cstr(ds, " timeout"); } if (state & LACP_STATE_AGG) { - ds_put_cstr(ds, "aggregation "); + ds_put_cstr(ds, " aggregation"); } if (state & LACP_STATE_SYNC) { - ds_put_cstr(ds, "synchronized "); + ds_put_cstr(ds, " synchronized"); } if (state & LACP_STATE_COL) { - ds_put_cstr(ds, "collecting "); + ds_put_cstr(ds, " collecting"); } if (state & LACP_STATE_DIST) { - ds_put_cstr(ds, "distributing "); + ds_put_cstr(ds, " distributing"); } if (state & LACP_STATE_DEF) { - ds_put_cstr(ds, "defaulted "); + ds_put_cstr(ds, " defaulted"); } if (state & LACP_STATE_EXP) { - ds_put_cstr(ds, "expired "); + ds_put_cstr(ds, " expired"); } } static void -lacp_unixctl_show(struct unixctl_conn *conn, - const char *args, void *aux OVS_UNUSED) +lacp_print_details(struct ds *ds, struct lacp *lacp) { - struct ds ds = DS_EMPTY_INITIALIZER; - struct lacp *lacp; + struct shash slave_shash = SHASH_INITIALIZER(&slave_shash); + const struct shash_node **sorted_slaves = NULL; + struct slave *slave; + int i; - lacp = lacp_find(args); - if (!lacp) { - unixctl_command_reply(conn, 501, "no such lacp object"); - return; + ds_put_format(ds, "---- %s ----\n", lacp->name); + ds_put_format(ds, "\tstatus: %s", lacp->active ? "active" : "passive"); + if (lacp->negotiated) { + ds_put_cstr(ds, " negotiated"); } + ds_put_cstr(ds, "\n"); - 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: "); + 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); + ds_put_format(ds, "%u", lacp->key_slave->port_id); + } else { + ds_put_cstr(ds, "none"); + } + ds_put_cstr(ds, "\n"); + + ds_put_cstr(ds, "\tlacp_time: "); + if (lacp->fast) { + ds_put_cstr(ds, "fast\n"); } else { - ds_put_cstr(&ds, "none"); + ds_put_cstr(ds, "slow\n"); } - ds_put_cstr(&ds, "\n"); HMAP_FOR_EACH (slave, node, &lacp->slaves) { + shash_add(&slave_shash, slave->name, slave); + } + sorted_slaves = shash_sort(&slave_shash); + + for (i = 0; i < shash_count(&slave_shash); i++) { char *status; + struct lacp_info actor; - slave_update_actor(slave); + slave = sorted_slaves[i]->data; + slave_get_actor(slave, &actor); switch (slave->status) { case LACP_CURRENT: status = "current"; @@ -611,41 +788,66 @@ 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, "\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", + 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); + ds_put_format(ds, "\tmay_enable: %s\n", (slave_may_enable__(slave) + ? "true" : "false")); + + ds_put_format(ds, "\n\tactor sys_id: " ETH_ADDR_FMT "\n", + ETH_ADDR_ARGS(actor.sys_id)); + ds_put_format(ds, "\tactor sys_priority: %u\n", + ntohs(actor.sys_priority)); + ds_put_format(ds, "\tactor port_id: %u\n", + ntohs(actor.port_id)); + ds_put_format(ds, "\tactor port_priority: %u\n", + ntohs(actor.port_priority)); + ds_put_format(ds, "\tactor key: %u\n", + ntohs(actor.key)); + ds_put_cstr(ds, "\tactor state:"); + ds_put_lacp_state(ds, 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", + ds_put_format(ds, "\tpartner sys_priority: %u\n", ntohs(slave->partner.sys_priority)); - ds_put_format(&ds, "\tpartner port_id: %u\n", + ds_put_format(ds, "\tpartner port_id: %u\n", ntohs(slave->partner.port_id)); - ds_put_format(&ds, "\tpartner port_priority: %u\n", + ds_put_format(ds, "\tpartner port_priority: %u\n", ntohs(slave->partner.port_priority)); - ds_put_format(&ds, "\tpartner key: %u\n", + 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"); + ds_put_cstr(ds, "\tpartner state:"); + ds_put_lacp_state(ds, slave->partner.state); + ds_put_cstr(ds, "\n"); + } + + shash_destroy(&slave_shash); + free(sorted_slaves); +} + +static void +lacp_unixctl_show(struct unixctl_conn *conn, int argc, const char *argv[], + void *aux OVS_UNUSED) +{ + struct ds ds = DS_EMPTY_INITIALIZER; + struct lacp *lacp; + + if (argc > 1) { + lacp = lacp_find(argv[1]); + if (!lacp) { + unixctl_command_reply_error(conn, "no such lacp object"); + return; + } + lacp_print_details(&ds, lacp); + } else { + LIST_FOR_EACH (lacp, node, &all_lacps) { + lacp_print_details(&ds, lacp); + } } - unixctl_command_reply(conn, 200, ds_cstr(&ds)); + unixctl_command_reply(conn, ds_cstr(&ds)); ds_destroy(&ds); }