X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Flacp.c;h=244b86e687e77081c6bc8e9e91b8a4739f83e9eb;hb=0e15264f96e3caff662c7998cc739a3dd5c1c0f2;hp=3fe5eff320a1fbb56c8fa2dbea3dcf5dedb9182b;hpb=cc8d12f9364760456c86e92378d529c0dd9aaa66;p=sliver-openvswitch.git diff --git a/lib/lacp.c b/lib/lacp.c index 3fe5eff32..244b86e68 100644 --- a/lib/lacp.c +++ b/lib/lacp.c @@ -25,6 +25,7 @@ #include "ofpbuf.h" #include "packets.h" #include "poll-loop.h" +#include "shash.h" #include "timer.h" #include "timeval.h" #include "unixctl.h" @@ -32,6 +33,55 @@ 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. */ @@ -86,11 +136,10 @@ 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 void lacp_unixctl_show(struct unixctl_conn *, const char *args, - void *aux); +static unixctl_cb_func lacp_unixctl_show; /* Populates 'pdu' with a LACP PDU comprised of 'actor' and 'partner'. */ -void +static void compose_lacp_pdu(const struct lacp_info *actor, const struct lacp_info *partner, struct lacp_pdu *pdu) { @@ -116,7 +165,7 @@ compose_lacp_pdu(const struct lacp_info *actor, * 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 * +static const struct lacp_pdu * parse_lacp_packet(const struct ofpbuf *b) { const struct lacp_pdu *pdu; @@ -138,7 +187,8 @@ parse_lacp_packet(const struct ofpbuf *b) 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. */ @@ -175,6 +225,8 @@ lacp_destroy(struct lacp *lacp) void lacp_configure(struct lacp *lacp, const struct lacp_settings *s) { + assert(!eth_addr_is_zero(s->id)); + if (!lacp->name || strcmp(s->name, lacp->name)) { free(lacp->name); lacp->name = xstrdup(s->name); @@ -191,7 +243,9 @@ lacp_configure(struct lacp *lacp, const struct lacp_settings *s) lacp->active = s->active; lacp->lacp_time = s->lacp_time; - lacp->custom_time = MAX(TIME_UPDATE_INTERVAL, s->custom_time); + lacp->custom_time = (s->lacp_time == LACP_TIME_CUSTOM + ? MAX(TIME_UPDATE_INTERVAL, s->custom_time) + : 0); } /* Returns true if 'lacp' is configured in active mode, false if 'lacp' is @@ -202,16 +256,24 @@ lacp_is_active(const struct lacp *lacp) return lacp->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(). */ +/* 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; + } + switch (lacp->lacp_time) { case LACP_TIME_FAST: tx_rate = LACP_FAST_TIME_TX; @@ -347,7 +409,7 @@ lacp_slave_get_port_id(const struct lacp *lacp, const void *slave_) bool lacp_slave_is_current(const struct lacp *lacp, const void *slave_) { - return slave_lookup(lacp, slave_)->status == LACP_CURRENT; + return slave_lookup(lacp, slave_)->status != LACP_DEFAULTED; } /* This function should be called periodically to update 'lacp'. */ @@ -371,7 +433,6 @@ 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 (!slave_may_tx(slave)) { @@ -383,10 +444,11 @@ lacp_run(struct lacp *lacp, lacp_send_pdu *send_pdu) if (timer_expired(&slave->tx) || !info_tx_equal(&actor, &slave->ntt_actor)) { long long int duration; + struct lacp_pdu pdu; slave->ntt_actor = actor; compose_lacp_pdu(&actor, &slave->partner, &pdu); - send_pdu(slave->aux, &pdu); + send_pdu(slave->aux, &pdu, sizeof pdu); if (lacp->lacp_time == LACP_TIME_CUSTOM) { duration = lacp->custom_time; @@ -667,92 +729,92 @@ 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 slave *slave; - - lacp = lacp_find(args); - if (!lacp) { - unixctl_command_reply(conn, 501, "no such lacp object"); - return; - } + struct shash slave_shash = SHASH_INITIALIZER(&slave_shash); + const struct shash_node **sorted_slaves = NULL; - ds_put_format(&ds, "lacp: %s\n", lacp->name); + struct slave *slave; + int i; - ds_put_format(&ds, "\tstatus: %s", lacp->active ? "active" : "passive"); + ds_put_format(ds, "---- %s ----\n", lacp->name); + ds_put_format(ds, "\tstatus: %s", lacp->active ? "active" : "passive"); if (lacp->heartbeat) { - ds_put_cstr(&ds, " heartbeat"); + ds_put_cstr(ds, " heartbeat"); } if (lacp->negotiated) { - ds_put_cstr(&ds, " negotiated"); + ds_put_cstr(ds, " negotiated"); } - ds_put_cstr(&ds, "\n"); + 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: "); + 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, "none"); } - ds_put_cstr(&ds, "\n"); + ds_put_cstr(ds, "\n"); - ds_put_cstr(&ds, "\tlacp_time: "); + ds_put_cstr(ds, "\tlacp_time: "); switch (lacp->lacp_time) { case LACP_TIME_FAST: - ds_put_cstr(&ds, "fast\n"); + ds_put_cstr(ds, "fast\n"); break; case LACP_TIME_SLOW: - ds_put_cstr(&ds, "slow\n"); + ds_put_cstr(ds, "slow\n"); break; case LACP_TIME_CUSTOM: - ds_put_format(&ds, "custom (%lld)\n", lacp->custom_time); + ds_put_format(ds, "custom (%lld)\n", lacp->custom_time); break; default: - ds_put_cstr(&ds, "unknown\n"); + ds_put_cstr(ds, "unknown\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 = sorted_slaves[i]->data; slave_get_actor(slave, &actor); switch (slave->status) { case LACP_CURRENT: @@ -768,38 +830,62 @@ lacp_unixctl_show(struct unixctl_conn *conn, NOT_REACHED(); } - ds_put_format(&ds, "\nslave: %s: %s %s\n", slave->name, status, + 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, "\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", + 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", + ds_put_format(ds, "\tactor sys_priority: %u\n", ntohs(actor.sys_priority)); - ds_put_format(&ds, "\tactor port_id: %u\n", + ds_put_format(ds, "\tactor port_id: %u\n", ntohs(actor.port_id)); - ds_put_format(&ds, "\tactor port_priority: %u\n", + ds_put_format(ds, "\tactor port_priority: %u\n", ntohs(actor.port_priority)); - ds_put_format(&ds, "\tactor key: %u\n", + 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_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", + 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(conn, 501, "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));