]>
Commit | Line | Data |
---|---|---|
cb22974d | 1 | /* Copyright (c) 2011, 2012 Nicira, Inc. |
6aa74308 EJ |
2 | * |
3 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
4 | * you may not use this file except in compliance with the License. | |
5 | * You may obtain a copy of the License at: | |
6 | * | |
7 | * http://www.apache.org/licenses/LICENSE-2.0 | |
8 | * | |
9 | * Unless required by applicable law or agreed to in writing, software | |
10 | * distributed under the License is distributed on an "AS IS" BASIS, | |
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 | * See the License for the specific language governing permissions and | |
13 | * limitations under the License. | |
14 | */ | |
15 | ||
16 | #include <config.h> | |
17 | #include "lacp.h" | |
18 | ||
6aa74308 EJ |
19 | #include <stdlib.h> |
20 | ||
21 | #include "dynamic-string.h" | |
22 | #include "hash.h" | |
23 | #include "hmap.h" | |
24 | #include "ofpbuf.h" | |
25 | #include "packets.h" | |
26 | #include "poll-loop.h" | |
431d6a6a | 27 | #include "shash.h" |
20834809 | 28 | #include "timer.h" |
6aa74308 EJ |
29 | #include "timeval.h" |
30 | #include "unixctl.h" | |
31 | #include "vlog.h" | |
32 | ||
33 | VLOG_DEFINE_THIS_MODULE(lacp); | |
34 | ||
5f877369 EJ |
35 | /* Masks for lacp_info state member. */ |
36 | #define LACP_STATE_ACT 0x01 /* Activity. Active or passive? */ | |
37 | #define LACP_STATE_TIME 0x02 /* Timeout. Short or long timeout? */ | |
38 | #define LACP_STATE_AGG 0x04 /* Aggregation. Is the link is bondable? */ | |
39 | #define LACP_STATE_SYNC 0x08 /* Synchronization. Is the link in up to date? */ | |
40 | #define LACP_STATE_COL 0x10 /* Collecting. Is the link receiving frames? */ | |
41 | #define LACP_STATE_DIST 0x20 /* Distributing. Is the link sending frames? */ | |
42 | #define LACP_STATE_DEF 0x40 /* Defaulted. Using default partner info? */ | |
43 | #define LACP_STATE_EXP 0x80 /* Expired. Using expired partner info? */ | |
44 | ||
45 | #define LACP_FAST_TIME_TX 1000 /* Fast transmission rate. */ | |
46 | #define LACP_SLOW_TIME_TX 30000 /* Slow transmission rate. */ | |
47 | #define LACP_RX_MULTIPLIER 3 /* Multiply by TX rate to get RX rate. */ | |
48 | ||
49 | #define LACP_INFO_LEN 15 | |
50 | struct lacp_info { | |
51 | ovs_be16 sys_priority; /* System priority. */ | |
52 | uint8_t sys_id[ETH_ADDR_LEN]; /* System ID. */ | |
53 | ovs_be16 key; /* Operational key. */ | |
54 | ovs_be16 port_priority; /* Port priority. */ | |
55 | ovs_be16 port_id; /* Port ID. */ | |
56 | uint8_t state; /* State mask. See LACP_STATE macros. */ | |
57 | } __attribute__((packed)); | |
58 | BUILD_ASSERT_DECL(LACP_INFO_LEN == sizeof(struct lacp_info)); | |
59 | ||
60 | #define LACP_PDU_LEN 110 | |
61 | struct lacp_pdu { | |
62 | uint8_t subtype; /* Always 1. */ | |
63 | uint8_t version; /* Always 1. */ | |
64 | ||
65 | uint8_t actor_type; /* Always 1. */ | |
66 | uint8_t actor_len; /* Always 20. */ | |
67 | struct lacp_info actor; /* LACP actor information. */ | |
68 | uint8_t z1[3]; /* Reserved. Always 0. */ | |
69 | ||
70 | uint8_t partner_type; /* Always 2. */ | |
71 | uint8_t partner_len; /* Always 20. */ | |
72 | struct lacp_info partner; /* LACP partner information. */ | |
73 | uint8_t z2[3]; /* Reserved. Always 0. */ | |
74 | ||
75 | uint8_t collector_type; /* Always 3. */ | |
76 | uint8_t collector_len; /* Always 16. */ | |
77 | ovs_be16 collector_delay; /* Maximum collector delay. Set to UINT16_MAX. */ | |
78 | uint8_t z3[64]; /* Combination of several fields. Always 0. */ | |
79 | } __attribute__((packed)); | |
80 | BUILD_ASSERT_DECL(LACP_PDU_LEN == sizeof(struct lacp_pdu)); | |
81 | \f | |
82 | /* Implementation. */ | |
83 | ||
6aa74308 EJ |
84 | enum slave_status { |
85 | LACP_CURRENT, /* Current State. Partner up to date. */ | |
86 | LACP_EXPIRED, /* Expired State. Partner out of date. */ | |
87 | LACP_DEFAULTED, /* Defaulted State. No partner. */ | |
88 | }; | |
89 | ||
90 | struct lacp { | |
91 | struct list node; /* Node in all_lacps list. */ | |
92 | char *name; /* Name of this lacp object. */ | |
93 | uint8_t sys_id[ETH_ADDR_LEN]; /* System ID. */ | |
94 | uint16_t sys_priority; /* System Priority. */ | |
95 | bool active; /* Active or Passive. */ | |
96 | ||
97 | struct hmap slaves; /* Slaves this LACP object controls. */ | |
98 | struct slave *key_slave; /* Slave whose ID will be the aggregation key. */ | |
99 | ||
bf83f7c8 | 100 | bool fast; /* True if using fast probe interval. */ |
6aa74308 EJ |
101 | bool negotiated; /* True if LACP negotiations were successful. */ |
102 | bool update; /* True if lacp_update() needs to be called. */ | |
103 | }; | |
104 | ||
105 | struct slave { | |
106 | void *aux; /* Handle used to identify this slave. */ | |
107 | struct hmap_node node; /* Node in master's slaves map. */ | |
108 | ||
109 | struct lacp *lacp; /* LACP object containing this slave. */ | |
110 | uint16_t port_id; /* Port ID. */ | |
111 | uint16_t port_priority; /* Port Priority. */ | |
e1ce3f2d | 112 | uint16_t key; /* Aggregation Key. 0 if default. */ |
6aa74308 EJ |
113 | char *name; /* Name of this slave. */ |
114 | ||
115 | enum slave_status status; /* Slave status. */ | |
116 | bool attached; /* Attached. Traffic may flow. */ | |
6aa74308 | 117 | struct lacp_info partner; /* Partner information. */ |
77fdfa9b | 118 | struct lacp_info ntt_actor; /* Used to decide if we Need To Transmit. */ |
20834809 EJ |
119 | struct timer tx; /* Next message transmission timer. */ |
120 | struct timer rx; /* Expected message receive timer. */ | |
6aa74308 EJ |
121 | }; |
122 | ||
123 | static struct list all_lacps = LIST_INITIALIZER(&all_lacps); | |
124 | ||
125 | static void lacp_update_attached(struct lacp *); | |
126 | ||
127 | static void slave_destroy(struct slave *); | |
128 | static void slave_set_defaulted(struct slave *); | |
129 | static void slave_set_expired(struct slave *); | |
77fdfa9b | 130 | static void slave_get_actor(struct slave *, struct lacp_info *actor); |
6aa74308 EJ |
131 | static void slave_get_priority(struct slave *, struct lacp_info *priority); |
132 | static bool slave_may_tx(const struct slave *); | |
133 | static struct slave *slave_lookup(const struct lacp *, const void *slave); | |
77fdfa9b | 134 | static bool info_tx_equal(struct lacp_info *, struct lacp_info *); |
6aa74308 | 135 | |
0e15264f | 136 | static unixctl_cb_func lacp_unixctl_show; |
6aa74308 | 137 | |
81aee5f9 | 138 | /* Populates 'pdu' with a LACP PDU comprised of 'actor' and 'partner'. */ |
5f877369 | 139 | static void |
81aee5f9 EJ |
140 | compose_lacp_pdu(const struct lacp_info *actor, |
141 | const struct lacp_info *partner, struct lacp_pdu *pdu) | |
142 | { | |
143 | memset(pdu, 0, sizeof *pdu); | |
144 | ||
145 | pdu->subtype = 1; | |
146 | pdu->version = 1; | |
147 | ||
148 | pdu->actor_type = 1; | |
149 | pdu->actor_len = 20; | |
150 | pdu->actor = *actor; | |
151 | ||
152 | pdu->partner_type = 2; | |
153 | pdu->partner_len = 20; | |
154 | pdu->partner = *partner; | |
155 | ||
156 | pdu->collector_type = 3; | |
157 | pdu->collector_len = 16; | |
158 | pdu->collector_delay = htons(0); | |
159 | } | |
160 | ||
161 | /* Parses 'b' which represents a packet containing a LACP PDU. This function | |
162 | * returns NULL if 'b' is malformed, or does not represent a LACP PDU format | |
163 | * supported by OVS. Otherwise, it returns a pointer to the lacp_pdu contained | |
164 | * within 'b'. */ | |
5f877369 | 165 | static const struct lacp_pdu * |
81aee5f9 EJ |
166 | parse_lacp_packet(const struct ofpbuf *b) |
167 | { | |
168 | const struct lacp_pdu *pdu; | |
169 | ||
170 | pdu = ofpbuf_at(b, (uint8_t *)b->l3 - (uint8_t *)b->data, LACP_PDU_LEN); | |
171 | ||
172 | if (pdu && pdu->subtype == 1 | |
173 | && pdu->actor_type == 1 && pdu->actor_len == 20 | |
174 | && pdu->partner_type == 2 && pdu->partner_len == 20) { | |
175 | return pdu; | |
176 | } else { | |
177 | return NULL; | |
178 | } | |
179 | } | |
180 | \f | |
181 | /* LACP Protocol Implementation. */ | |
182 | ||
6aa74308 EJ |
183 | /* Initializes the lacp module. */ |
184 | void | |
185 | lacp_init(void) | |
186 | { | |
0e15264f BP |
187 | unixctl_command_register("lacp/show", "[port]", 0, 1, |
188 | lacp_unixctl_show, NULL); | |
6aa74308 EJ |
189 | } |
190 | ||
191 | /* Creates a LACP object. */ | |
192 | struct lacp * | |
193 | lacp_create(void) | |
194 | { | |
195 | struct lacp *lacp; | |
196 | ||
197 | lacp = xzalloc(sizeof *lacp); | |
198 | hmap_init(&lacp->slaves); | |
199 | list_push_back(&all_lacps, &lacp->node); | |
200 | return lacp; | |
201 | } | |
202 | ||
203 | /* Destroys 'lacp' and its slaves. Does nothing if 'lacp' is NULL. */ | |
204 | void | |
205 | lacp_destroy(struct lacp *lacp) | |
206 | { | |
207 | if (lacp) { | |
208 | struct slave *slave, *next; | |
209 | ||
210 | HMAP_FOR_EACH_SAFE (slave, next, node, &lacp->slaves) { | |
211 | slave_destroy(slave); | |
212 | } | |
213 | ||
214 | hmap_destroy(&lacp->slaves); | |
215 | list_remove(&lacp->node); | |
216 | free(lacp->name); | |
217 | free(lacp); | |
218 | } | |
219 | } | |
220 | ||
bb5bc6c0 | 221 | /* Configures 'lacp' with settings from 's'. */ |
6aa74308 | 222 | void |
bb5bc6c0 | 223 | lacp_configure(struct lacp *lacp, const struct lacp_settings *s) |
6aa74308 | 224 | { |
cb22974d | 225 | ovs_assert(!eth_addr_is_zero(s->id)); |
69fdadb1 | 226 | |
bb5bc6c0 | 227 | if (!lacp->name || strcmp(s->name, lacp->name)) { |
6aa74308 | 228 | free(lacp->name); |
bb5bc6c0 | 229 | lacp->name = xstrdup(s->name); |
6aa74308 EJ |
230 | } |
231 | ||
eb458893 | 232 | if (!eth_addr_equals(lacp->sys_id, s->id) |
b20a8f7c | 233 | || lacp->sys_priority != s->priority) { |
eb458893 EJ |
234 | memcpy(lacp->sys_id, s->id, ETH_ADDR_LEN); |
235 | lacp->sys_priority = s->priority; | |
236 | lacp->update = true; | |
237 | } | |
238 | ||
bb5bc6c0 | 239 | lacp->active = s->active; |
bf83f7c8 | 240 | lacp->fast = s->fast; |
6aa74308 EJ |
241 | } |
242 | ||
7a673515 BP |
243 | /* Returns true if 'lacp' is configured in active mode, false if 'lacp' is |
244 | * configured for passive mode. */ | |
245 | bool | |
246 | lacp_is_active(const struct lacp *lacp) | |
247 | { | |
248 | return lacp->active; | |
249 | } | |
250 | ||
5f877369 EJ |
251 | /* Processes 'packet' which was received on 'slave_'. This function should be |
252 | * called on all packets received on 'slave_' with Ethernet Type ETH_TYPE_LACP. | |
253 | */ | |
6aa74308 | 254 | void |
5f877369 EJ |
255 | lacp_process_packet(struct lacp *lacp, const void *slave_, |
256 | const struct ofpbuf *packet) | |
6aa74308 | 257 | { |
5f877369 | 258 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); |
6aa74308 | 259 | struct slave *slave = slave_lookup(lacp, slave_); |
5f877369 | 260 | const struct lacp_pdu *pdu; |
cdcf42c6 EJ |
261 | long long int tx_rate; |
262 | ||
5f877369 EJ |
263 | pdu = parse_lacp_packet(packet); |
264 | if (!pdu) { | |
265 | VLOG_WARN_RL(&rl, "%s: received an unparsable LACP PDU.", lacp->name); | |
266 | return; | |
267 | } | |
268 | ||
6aa74308 | 269 | slave->status = LACP_CURRENT; |
bf83f7c8 | 270 | tx_rate = lacp->fast ? LACP_FAST_TIME_TX : LACP_SLOW_TIME_TX; |
cdcf42c6 | 271 | timer_set_duration(&slave->rx, LACP_RX_MULTIPLIER * tx_rate); |
6aa74308 | 272 | |
77fdfa9b | 273 | slave->ntt_actor = pdu->partner; |
6aa74308 EJ |
274 | |
275 | /* Update our information about our partner if it's out of date. This may | |
276 | * cause priorities to change so re-calculate attached status of all | |
277 | * slaves. */ | |
278 | if (memcmp(&slave->partner, &pdu->actor, sizeof pdu->actor)) { | |
279 | lacp->update = true; | |
280 | slave->partner = pdu->actor; | |
281 | } | |
282 | } | |
283 | ||
bdebeece EJ |
284 | /* Returns the lacp_status of the given 'lacp' object (which may be NULL). */ |
285 | enum lacp_status | |
286 | lacp_status(const struct lacp *lacp) | |
6aa74308 | 287 | { |
bdebeece EJ |
288 | if (!lacp) { |
289 | return LACP_DISABLED; | |
290 | } else if (lacp->negotiated) { | |
291 | return LACP_NEGOTIATED; | |
292 | } else { | |
293 | return LACP_CONFIGURED; | |
294 | } | |
6aa74308 EJ |
295 | } |
296 | ||
297 | /* Registers 'slave_' as subordinate to 'lacp'. This should be called at least | |
298 | * once per slave in a LACP managed bond. Should also be called whenever a | |
bb5bc6c0 | 299 | * slave's settings change. */ |
6aa74308 | 300 | void |
bb5bc6c0 BP |
301 | lacp_slave_register(struct lacp *lacp, void *slave_, |
302 | const struct lacp_slave_settings *s) | |
6aa74308 EJ |
303 | { |
304 | struct slave *slave = slave_lookup(lacp, slave_); | |
305 | ||
306 | if (!slave) { | |
307 | slave = xzalloc(sizeof *slave); | |
308 | slave->lacp = lacp; | |
309 | slave->aux = slave_; | |
310 | hmap_insert(&lacp->slaves, &slave->node, hash_pointer(slave_, 0)); | |
311 | slave_set_defaulted(slave); | |
312 | ||
313 | if (!lacp->key_slave) { | |
314 | lacp->key_slave = slave; | |
315 | } | |
316 | } | |
317 | ||
bb5bc6c0 | 318 | if (!slave->name || strcmp(s->name, slave->name)) { |
6aa74308 | 319 | free(slave->name); |
bb5bc6c0 | 320 | slave->name = xstrdup(s->name); |
6aa74308 EJ |
321 | } |
322 | ||
e1ce3f2d EJ |
323 | if (slave->port_id != s->id |
324 | || slave->port_priority != s->priority | |
325 | || slave->key != s->key) { | |
bb5bc6c0 BP |
326 | slave->port_id = s->id; |
327 | slave->port_priority = s->priority; | |
e1ce3f2d | 328 | slave->key = s->key; |
6aa74308 | 329 | |
6aa74308 EJ |
330 | lacp->update = true; |
331 | ||
332 | if (lacp->active || lacp->negotiated) { | |
333 | slave_set_expired(slave); | |
334 | } | |
335 | } | |
336 | } | |
337 | ||
338 | /* Unregisters 'slave_' with 'lacp'. */ | |
339 | void | |
340 | lacp_slave_unregister(struct lacp *lacp, const void *slave_) | |
341 | { | |
342 | struct slave *slave = slave_lookup(lacp, slave_); | |
343 | ||
344 | if (slave) { | |
345 | slave_destroy(slave); | |
eb458893 | 346 | lacp->update = true; |
6aa74308 EJ |
347 | } |
348 | } | |
349 | ||
6aa74308 | 350 | /* This function should be called whenever the carrier status of 'slave_' has |
3e5b3fdb | 351 | * changed. If 'lacp' is null, this function has no effect.*/ |
6aa74308 EJ |
352 | void |
353 | lacp_slave_carrier_changed(const struct lacp *lacp, const void *slave_) | |
354 | { | |
3e5b3fdb EJ |
355 | if (lacp) { |
356 | struct slave *slave = slave_lookup(lacp, slave_); | |
6aa74308 | 357 | |
3e5b3fdb EJ |
358 | if (slave->status == LACP_CURRENT || slave->lacp->active) { |
359 | slave_set_expired(slave); | |
360 | } | |
6aa74308 EJ |
361 | } |
362 | } | |
363 | ||
29985e75 EJ |
364 | static bool |
365 | slave_may_enable__(struct slave *slave) | |
366 | { | |
367 | /* The slave may be enabled if it's attached to an aggregator and its | |
368 | * partner is synchronized.*/ | |
369 | return slave->attached && (slave->partner.state & LACP_STATE_SYNC); | |
370 | } | |
371 | ||
6aa74308 EJ |
372 | /* This function should be called before enabling 'slave_' to send or receive |
373 | * traffic. If it returns false, 'slave_' should not enabled. As a | |
374 | * convenience, returns true if 'lacp' is NULL. */ | |
375 | bool | |
376 | lacp_slave_may_enable(const struct lacp *lacp, const void *slave_) | |
377 | { | |
378 | if (lacp) { | |
29985e75 | 379 | return slave_may_enable__(slave_lookup(lacp, slave_)); |
6aa74308 EJ |
380 | } else { |
381 | return true; | |
382 | } | |
383 | } | |
384 | ||
b49b7df4 EJ |
385 | /* Returns the port ID used for 'slave_' in LACP communications. */ |
386 | uint16_t | |
387 | lacp_slave_get_port_id(const struct lacp *lacp, const void *slave_) | |
388 | { | |
389 | struct slave *slave = slave_lookup(lacp, slave_); | |
390 | return slave->port_id; | |
391 | } | |
392 | ||
44cb163f EJ |
393 | /* Returns true if partner information on 'slave_' is up to date. 'slave_' |
394 | * not being current, generally indicates a connectivity problem, or a | |
395 | * misconfigured (or broken) partner. */ | |
396 | bool | |
397 | lacp_slave_is_current(const struct lacp *lacp, const void *slave_) | |
398 | { | |
b6466afa | 399 | return slave_lookup(lacp, slave_)->status != LACP_DEFAULTED; |
44cb163f EJ |
400 | } |
401 | ||
6aa74308 EJ |
402 | /* This function should be called periodically to update 'lacp'. */ |
403 | void | |
404 | lacp_run(struct lacp *lacp, lacp_send_pdu *send_pdu) | |
405 | { | |
406 | struct slave *slave; | |
407 | ||
408 | HMAP_FOR_EACH (slave, node, &lacp->slaves) { | |
20834809 | 409 | if (timer_expired(&slave->rx)) { |
6aa74308 EJ |
410 | if (slave->status == LACP_CURRENT) { |
411 | slave_set_expired(slave); | |
412 | } else if (slave->status == LACP_EXPIRED) { | |
413 | slave_set_defaulted(slave); | |
414 | } | |
415 | } | |
6aa74308 EJ |
416 | } |
417 | ||
418 | if (lacp->update) { | |
419 | lacp_update_attached(lacp); | |
420 | } | |
421 | ||
422 | HMAP_FOR_EACH (slave, node, &lacp->slaves) { | |
77fdfa9b | 423 | struct lacp_info actor; |
6aa74308 | 424 | |
77fdfa9b | 425 | if (!slave_may_tx(slave)) { |
6aa74308 EJ |
426 | continue; |
427 | } | |
428 | ||
77fdfa9b EJ |
429 | slave_get_actor(slave, &actor); |
430 | ||
20834809 | 431 | if (timer_expired(&slave->tx) |
77fdfa9b | 432 | || !info_tx_equal(&actor, &slave->ntt_actor)) { |
cdcf42c6 | 433 | long long int duration; |
5f877369 | 434 | struct lacp_pdu pdu; |
77fdfa9b EJ |
435 | |
436 | slave->ntt_actor = actor; | |
437 | compose_lacp_pdu(&actor, &slave->partner, &pdu); | |
5f877369 | 438 | send_pdu(slave->aux, &pdu, sizeof pdu); |
6aa74308 | 439 | |
bf83f7c8 EJ |
440 | duration = (slave->partner.state & LACP_STATE_TIME |
441 | ? LACP_FAST_TIME_TX | |
442 | : LACP_SLOW_TIME_TX); | |
cdcf42c6 EJ |
443 | |
444 | timer_set_duration(&slave->tx, duration); | |
77fdfa9b | 445 | } |
6aa74308 EJ |
446 | } |
447 | } | |
448 | ||
449 | /* Causes poll_block() to wake up when lacp_run() needs to be called again. */ | |
450 | void | |
451 | lacp_wait(struct lacp *lacp) | |
452 | { | |
453 | struct slave *slave; | |
454 | ||
455 | HMAP_FOR_EACH (slave, node, &lacp->slaves) { | |
456 | if (slave_may_tx(slave)) { | |
20834809 | 457 | timer_wait(&slave->tx); |
6aa74308 EJ |
458 | } |
459 | ||
460 | if (slave->status != LACP_DEFAULTED) { | |
20834809 | 461 | timer_wait(&slave->rx); |
6aa74308 EJ |
462 | } |
463 | } | |
464 | } | |
465 | \f | |
466 | /* Static Helpers. */ | |
467 | ||
20601813 | 468 | /* Updates the attached status of all slaves controlled by 'lacp' and sets its |
6aa74308 EJ |
469 | * negotiated parameter to true if any slaves are attachable. */ |
470 | static void | |
471 | lacp_update_attached(struct lacp *lacp) | |
472 | { | |
473 | struct slave *lead, *slave; | |
474 | struct lacp_info lead_pri; | |
475 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 10); | |
476 | ||
477 | lacp->update = false; | |
478 | ||
479 | lead = NULL; | |
480 | HMAP_FOR_EACH (slave, node, &lacp->slaves) { | |
481 | struct lacp_info pri; | |
482 | ||
bdebeece | 483 | slave->attached = false; |
6aa74308 EJ |
484 | |
485 | /* XXX: In the future allow users to configure the expected system ID. | |
486 | * For now just special case loopback. */ | |
77fdfa9b | 487 | if (eth_addr_equals(slave->partner.sys_id, slave->lacp->sys_id)) { |
6aa74308 EJ |
488 | VLOG_WARN_RL(&rl, "slave %s: Loopback detected. Slave is " |
489 | "connected to its own bond", slave->name); | |
6aa74308 EJ |
490 | continue; |
491 | } | |
492 | ||
493 | if (slave->status == LACP_DEFAULTED) { | |
494 | continue; | |
495 | } | |
496 | ||
bdebeece | 497 | slave->attached = true; |
6aa74308 EJ |
498 | slave_get_priority(slave, &pri); |
499 | ||
500 | if (!lead || memcmp(&pri, &lead_pri, sizeof pri) < 0) { | |
501 | lead = slave; | |
502 | lead_pri = pri; | |
503 | } | |
504 | } | |
505 | ||
506 | lacp->negotiated = lead != NULL; | |
507 | ||
508 | if (lead) { | |
509 | HMAP_FOR_EACH (slave, node, &lacp->slaves) { | |
bdebeece | 510 | if (lead->partner.key != slave->partner.key |
6aa74308 EJ |
511 | || !eth_addr_equals(lead->partner.sys_id, |
512 | slave->partner.sys_id)) { | |
513 | slave->attached = false; | |
514 | } | |
515 | } | |
516 | } | |
517 | } | |
518 | ||
519 | static void | |
520 | slave_destroy(struct slave *slave) | |
521 | { | |
522 | if (slave) { | |
523 | struct lacp *lacp = slave->lacp; | |
524 | ||
525 | lacp->update = true; | |
526 | hmap_remove(&lacp->slaves, &slave->node); | |
527 | ||
528 | if (lacp->key_slave == slave) { | |
529 | struct hmap_node *slave_node = hmap_first(&lacp->slaves); | |
530 | ||
531 | if (slave_node) { | |
532 | lacp->key_slave = CONTAINER_OF(slave_node, struct slave, node); | |
533 | } else { | |
534 | lacp->key_slave = NULL; | |
535 | } | |
536 | } | |
537 | ||
538 | free(slave->name); | |
539 | free(slave); | |
540 | } | |
541 | } | |
542 | ||
543 | static void | |
544 | slave_set_defaulted(struct slave *slave) | |
545 | { | |
546 | memset(&slave->partner, 0, sizeof slave->partner); | |
547 | ||
6aa74308 EJ |
548 | slave->lacp->update = true; |
549 | slave->status = LACP_DEFAULTED; | |
550 | } | |
551 | ||
552 | static void | |
553 | slave_set_expired(struct slave *slave) | |
554 | { | |
555 | slave->status = LACP_EXPIRED; | |
556 | slave->partner.state |= LACP_STATE_TIME; | |
557 | slave->partner.state &= ~LACP_STATE_SYNC; | |
cdcf42c6 | 558 | |
bf83f7c8 | 559 | timer_set_duration(&slave->rx, LACP_RX_MULTIPLIER * LACP_FAST_TIME_TX); |
6aa74308 EJ |
560 | } |
561 | ||
562 | static void | |
77fdfa9b | 563 | slave_get_actor(struct slave *slave, struct lacp_info *actor) |
6aa74308 | 564 | { |
da2f7b8f | 565 | struct lacp *lacp = slave->lacp; |
e1ce3f2d | 566 | uint16_t key; |
6aa74308 EJ |
567 | uint8_t state = 0; |
568 | ||
da2f7b8f | 569 | if (lacp->active) { |
6aa74308 EJ |
570 | state |= LACP_STATE_ACT; |
571 | } | |
572 | ||
bf83f7c8 | 573 | if (lacp->fast) { |
269340fa EJ |
574 | state |= LACP_STATE_TIME; |
575 | } | |
576 | ||
6aa74308 EJ |
577 | if (slave->attached) { |
578 | state |= LACP_STATE_SYNC; | |
579 | } | |
580 | ||
581 | if (slave->status == LACP_DEFAULTED) { | |
582 | state |= LACP_STATE_DEF; | |
583 | } | |
584 | ||
585 | if (slave->status == LACP_EXPIRED) { | |
586 | state |= LACP_STATE_EXP; | |
587 | } | |
588 | ||
b20a8f7c | 589 | if (hmap_count(&lacp->slaves) > 1) { |
6aa74308 EJ |
590 | state |= LACP_STATE_AGG; |
591 | } | |
592 | ||
da2f7b8f | 593 | if (slave->attached || !lacp->negotiated) { |
6aa74308 EJ |
594 | state |= LACP_STATE_COL | LACP_STATE_DIST; |
595 | } | |
596 | ||
e1ce3f2d EJ |
597 | key = lacp->key_slave->key; |
598 | if (!key) { | |
599 | key = lacp->key_slave->port_id; | |
600 | } | |
601 | ||
77fdfa9b | 602 | actor->state = state; |
e1ce3f2d | 603 | actor->key = htons(key); |
77fdfa9b EJ |
604 | actor->port_priority = htons(slave->port_priority); |
605 | actor->port_id = htons(slave->port_id); | |
da2f7b8f EJ |
606 | actor->sys_priority = htons(lacp->sys_priority); |
607 | memcpy(&actor->sys_id, lacp->sys_id, ETH_ADDR_LEN); | |
6aa74308 EJ |
608 | } |
609 | ||
610 | /* Given 'slave', populates 'priority' with data representing its LACP link | |
611 | * priority. If two priority objects populated by this function are compared | |
612 | * using memcmp, the higher priority link will be less than the lower priority | |
613 | * link. */ | |
614 | static void | |
615 | slave_get_priority(struct slave *slave, struct lacp_info *priority) | |
616 | { | |
617 | uint16_t partner_priority, actor_priority; | |
618 | ||
619 | /* Choose the lacp_info of the higher priority system by comparing their | |
620 | * system priorities and mac addresses. */ | |
77fdfa9b | 621 | actor_priority = slave->lacp->sys_priority; |
6aa74308 EJ |
622 | partner_priority = ntohs(slave->partner.sys_priority); |
623 | if (actor_priority < partner_priority) { | |
77fdfa9b | 624 | slave_get_actor(slave, priority); |
6aa74308 EJ |
625 | } else if (partner_priority < actor_priority) { |
626 | *priority = slave->partner; | |
77fdfa9b | 627 | } else if (eth_addr_compare_3way(slave->lacp->sys_id, |
6aa74308 | 628 | slave->partner.sys_id) < 0) { |
77fdfa9b | 629 | slave_get_actor(slave, priority); |
6aa74308 EJ |
630 | } else { |
631 | *priority = slave->partner; | |
632 | } | |
633 | ||
634 | /* Key and state are not used in priority comparisons. */ | |
635 | priority->key = 0; | |
636 | priority->state = 0; | |
637 | } | |
638 | ||
639 | static bool | |
640 | slave_may_tx(const struct slave *slave) | |
641 | { | |
642 | return slave->lacp->active || slave->status != LACP_DEFAULTED; | |
643 | } | |
644 | ||
645 | static struct slave * | |
646 | slave_lookup(const struct lacp *lacp, const void *slave_) | |
647 | { | |
648 | struct slave *slave; | |
649 | ||
650 | HMAP_FOR_EACH_IN_BUCKET (slave, node, hash_pointer(slave_, 0), | |
651 | &lacp->slaves) { | |
652 | if (slave->aux == slave_) { | |
653 | return slave; | |
654 | } | |
655 | } | |
656 | ||
657 | return NULL; | |
658 | } | |
77fdfa9b EJ |
659 | |
660 | /* Two lacp_info structures are tx_equal if and only if they do not differ in | |
661 | * ways which would require a lacp_pdu transmission. */ | |
662 | static bool | |
663 | info_tx_equal(struct lacp_info *a, struct lacp_info *b) | |
664 | { | |
665 | ||
666 | /* LACP specification dictates that we transmit whenever the actor and | |
667 | * remote_actor differ in the following fields: Port, Port Priority, | |
668 | * System, System Priority, Aggregation Key, Activity State, Timeout State, | |
669 | * Sync State, and Aggregation State. The state flags are most likely to | |
670 | * change so are checked first. */ | |
671 | return !((a->state ^ b->state) & (LACP_STATE_ACT | |
672 | | LACP_STATE_TIME | |
673 | | LACP_STATE_SYNC | |
674 | | LACP_STATE_AGG)) | |
675 | && a->port_id == b->port_id | |
676 | && a->port_priority == b->port_priority | |
677 | && a->key == b->key | |
678 | && a->sys_priority == b->sys_priority | |
679 | && eth_addr_equals(a->sys_id, b->sys_id); | |
680 | } | |
6aa74308 EJ |
681 | \f |
682 | static struct lacp * | |
683 | lacp_find(const char *name) | |
684 | { | |
685 | struct lacp *lacp; | |
686 | ||
687 | LIST_FOR_EACH (lacp, node, &all_lacps) { | |
688 | if (!strcmp(lacp->name, name)) { | |
689 | return lacp; | |
690 | } | |
691 | } | |
692 | ||
693 | return NULL; | |
694 | } | |
695 | ||
696 | static void | |
697 | ds_put_lacp_state(struct ds *ds, uint8_t state) | |
698 | { | |
699 | if (state & LACP_STATE_ACT) { | |
1c1df7f1 | 700 | ds_put_cstr(ds, " activity"); |
6aa74308 EJ |
701 | } |
702 | ||
703 | if (state & LACP_STATE_TIME) { | |
1c1df7f1 | 704 | ds_put_cstr(ds, " timeout"); |
6aa74308 EJ |
705 | } |
706 | ||
707 | if (state & LACP_STATE_AGG) { | |
1c1df7f1 | 708 | ds_put_cstr(ds, " aggregation"); |
6aa74308 EJ |
709 | } |
710 | ||
711 | if (state & LACP_STATE_SYNC) { | |
1c1df7f1 | 712 | ds_put_cstr(ds, " synchronized"); |
6aa74308 EJ |
713 | } |
714 | ||
715 | if (state & LACP_STATE_COL) { | |
1c1df7f1 | 716 | ds_put_cstr(ds, " collecting"); |
6aa74308 EJ |
717 | } |
718 | ||
719 | if (state & LACP_STATE_DIST) { | |
1c1df7f1 | 720 | ds_put_cstr(ds, " distributing"); |
6aa74308 EJ |
721 | } |
722 | ||
723 | if (state & LACP_STATE_DEF) { | |
1c1df7f1 | 724 | ds_put_cstr(ds, " defaulted"); |
6aa74308 EJ |
725 | } |
726 | ||
727 | if (state & LACP_STATE_EXP) { | |
1c1df7f1 | 728 | ds_put_cstr(ds, " expired"); |
6aa74308 EJ |
729 | } |
730 | } | |
731 | ||
732 | static void | |
5dab8ece | 733 | lacp_print_details(struct ds *ds, struct lacp *lacp) |
6aa74308 | 734 | { |
431d6a6a EJ |
735 | struct shash slave_shash = SHASH_INITIALIZER(&slave_shash); |
736 | const struct shash_node **sorted_slaves = NULL; | |
737 | ||
6aa74308 | 738 | struct slave *slave; |
431d6a6a | 739 | int i; |
6aa74308 | 740 | |
5dab8ece JP |
741 | ds_put_format(ds, "---- %s ----\n", lacp->name); |
742 | ds_put_format(ds, "\tstatus: %s", lacp->active ? "active" : "passive"); | |
20601813 | 743 | if (lacp->negotiated) { |
5dab8ece | 744 | ds_put_cstr(ds, " negotiated"); |
20601813 | 745 | } |
5dab8ece | 746 | ds_put_cstr(ds, "\n"); |
20601813 | 747 | |
5dab8ece JP |
748 | ds_put_format(ds, "\tsys_id: " ETH_ADDR_FMT "\n", ETH_ADDR_ARGS(lacp->sys_id)); |
749 | ds_put_format(ds, "\tsys_priority: %u\n", lacp->sys_priority); | |
750 | ds_put_cstr(ds, "\taggregation key: "); | |
6aa74308 | 751 | if (lacp->key_slave) { |
ab3a09c8 AS |
752 | ds_put_format(ds, "%u", lacp->key_slave->key |
753 | ? lacp->key_slave->key | |
754 | : lacp->key_slave->port_id); | |
6aa74308 | 755 | } else { |
5dab8ece | 756 | ds_put_cstr(ds, "none"); |
6aa74308 | 757 | } |
5dab8ece | 758 | ds_put_cstr(ds, "\n"); |
6aa74308 | 759 | |
5dab8ece | 760 | ds_put_cstr(ds, "\tlacp_time: "); |
bf83f7c8 | 761 | if (lacp->fast) { |
5dab8ece | 762 | ds_put_cstr(ds, "fast\n"); |
bf83f7c8 | 763 | } else { |
5dab8ece | 764 | ds_put_cstr(ds, "slow\n"); |
cdcf42c6 EJ |
765 | } |
766 | ||
6aa74308 | 767 | HMAP_FOR_EACH (slave, node, &lacp->slaves) { |
431d6a6a EJ |
768 | shash_add(&slave_shash, slave->name, slave); |
769 | } | |
770 | sorted_slaves = shash_sort(&slave_shash); | |
771 | ||
772 | for (i = 0; i < shash_count(&slave_shash); i++) { | |
6aa74308 | 773 | char *status; |
77fdfa9b | 774 | struct lacp_info actor; |
6aa74308 | 775 | |
431d6a6a | 776 | slave = sorted_slaves[i]->data; |
77fdfa9b | 777 | slave_get_actor(slave, &actor); |
6aa74308 EJ |
778 | switch (slave->status) { |
779 | case LACP_CURRENT: | |
780 | status = "current"; | |
781 | break; | |
782 | case LACP_EXPIRED: | |
783 | status = "expired"; | |
784 | break; | |
785 | case LACP_DEFAULTED: | |
786 | status = "defaulted"; | |
787 | break; | |
788 | default: | |
789 | NOT_REACHED(); | |
790 | } | |
791 | ||
5dab8ece | 792 | ds_put_format(ds, "\nslave: %s: %s %s\n", slave->name, status, |
0f1a47f1 | 793 | slave->attached ? "attached" : "detached"); |
5dab8ece JP |
794 | ds_put_format(ds, "\tport_id: %u\n", slave->port_id); |
795 | ds_put_format(ds, "\tport_priority: %u\n", slave->port_priority); | |
29985e75 EJ |
796 | ds_put_format(ds, "\tmay_enable: %s\n", (slave_may_enable__(slave) |
797 | ? "true" : "false")); | |
6aa74308 | 798 | |
5dab8ece | 799 | ds_put_format(ds, "\n\tactor sys_id: " ETH_ADDR_FMT "\n", |
77fdfa9b | 800 | ETH_ADDR_ARGS(actor.sys_id)); |
5dab8ece | 801 | ds_put_format(ds, "\tactor sys_priority: %u\n", |
77fdfa9b | 802 | ntohs(actor.sys_priority)); |
5dab8ece | 803 | ds_put_format(ds, "\tactor port_id: %u\n", |
77fdfa9b | 804 | ntohs(actor.port_id)); |
5dab8ece | 805 | ds_put_format(ds, "\tactor port_priority: %u\n", |
77fdfa9b | 806 | ntohs(actor.port_priority)); |
5dab8ece | 807 | ds_put_format(ds, "\tactor key: %u\n", |
77fdfa9b | 808 | ntohs(actor.key)); |
1c1df7f1 | 809 | ds_put_cstr(ds, "\tactor state:"); |
5dab8ece JP |
810 | ds_put_lacp_state(ds, actor.state); |
811 | ds_put_cstr(ds, "\n\n"); | |
6aa74308 | 812 | |
5dab8ece | 813 | ds_put_format(ds, "\tpartner sys_id: " ETH_ADDR_FMT "\n", |
6aa74308 | 814 | ETH_ADDR_ARGS(slave->partner.sys_id)); |
5dab8ece | 815 | ds_put_format(ds, "\tpartner sys_priority: %u\n", |
6aa74308 | 816 | ntohs(slave->partner.sys_priority)); |
5dab8ece | 817 | ds_put_format(ds, "\tpartner port_id: %u\n", |
6aa74308 | 818 | ntohs(slave->partner.port_id)); |
5dab8ece | 819 | ds_put_format(ds, "\tpartner port_priority: %u\n", |
6aa74308 | 820 | ntohs(slave->partner.port_priority)); |
5dab8ece | 821 | ds_put_format(ds, "\tpartner key: %u\n", |
6aa74308 | 822 | ntohs(slave->partner.key)); |
1c1df7f1 | 823 | ds_put_cstr(ds, "\tpartner state:"); |
5dab8ece JP |
824 | ds_put_lacp_state(ds, slave->partner.state); |
825 | ds_put_cstr(ds, "\n"); | |
826 | } | |
431d6a6a EJ |
827 | |
828 | shash_destroy(&slave_shash); | |
829 | free(sorted_slaves); | |
5dab8ece JP |
830 | } |
831 | ||
832 | static void | |
0e15264f BP |
833 | lacp_unixctl_show(struct unixctl_conn *conn, int argc, const char *argv[], |
834 | void *aux OVS_UNUSED) | |
5dab8ece JP |
835 | { |
836 | struct ds ds = DS_EMPTY_INITIALIZER; | |
837 | struct lacp *lacp; | |
838 | ||
0e15264f BP |
839 | if (argc > 1) { |
840 | lacp = lacp_find(argv[1]); | |
5dab8ece | 841 | if (!lacp) { |
bde9f75d | 842 | unixctl_command_reply_error(conn, "no such lacp object"); |
5dab8ece JP |
843 | return; |
844 | } | |
845 | lacp_print_details(&ds, lacp); | |
846 | } else { | |
847 | LIST_FOR_EACH (lacp, node, &all_lacps) { | |
848 | lacp_print_details(&ds, lacp); | |
849 | } | |
6aa74308 EJ |
850 | } |
851 | ||
bde9f75d | 852 | unixctl_command_reply(conn, ds_cstr(&ds)); |
6aa74308 EJ |
853 | ds_destroy(&ds); |
854 | } |