]>
Commit | Line | Data |
---|---|---|
be53a5c4 DF |
1 | /* |
2 | * Copyright (c) 2014 WindRiver, Inc. | |
3 | * Copyright (c) 2015 Avaya, Inc. | |
4 | * | |
5 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | * you may not use this file except in compliance with the License. | |
7 | * You may obtain a copy of the License at: | |
8 | * | |
9 | * http://www.apache.org/licenses/LICENSE-2.0 | |
10 | * | |
11 | * Unless required by applicable law or agreed to in writing, software | |
12 | * distributed under the License is distributed on an "AS IS" BASIS, | |
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | * See the License for the specific language governing permissions and | |
15 | * limitations under the License. | |
16 | */ | |
17 | ||
18 | /* Implementation of Auto Attach. | |
19 | * Based on sample implementation in 802.1ab. Above copyright and license | |
20 | * applies to all modifications. | |
21 | * Limitations: | |
22 | * - No support for multiple bridge. | |
23 | * - Auto Attach state machine not implemented. | |
24 | * - Auto Attach and LLDP code are bundled together. The plan is to decoupled | |
25 | * them. | |
26 | */ | |
27 | ||
28 | #include <config.h> | |
29 | #include "ovs-lldp.h" | |
30 | #include <arpa/inet.h> | |
31 | #include <inttypes.h> | |
32 | #include <netinet/in.h> | |
33 | #include <stdbool.h> | |
34 | #include <stdlib.h> | |
35 | #include <sys/types.h> | |
36 | #include "dynamic-string.h" | |
37 | #include "flow.h" | |
38 | #include "list.h" | |
39 | #include "lldp/lldpd.h" | |
40 | #include "lldp/lldpd-structs.h" | |
41 | #include "netdev.h" | |
42 | #include "ofpbuf.h" | |
43 | #include "openvswitch/types.h" | |
44 | #include "packets.h" | |
45 | #include "poll-loop.h" | |
46 | #include "smap.h" | |
47 | #include "unixctl.h" | |
48 | #include "util.h" | |
49 | #include "openvswitch/vlog.h" | |
50 | ||
51 | VLOG_DEFINE_THIS_MODULE(ovs_lldp); | |
52 | ||
53 | #define LLDP_PROTOCOL_ID 0x0000 | |
54 | #define LLDP_PROTOCOL_VERSION 0x00 | |
55 | #define LLDP_TYPE_CONFIG 0x00 | |
56 | #define LLDP_CHASSIS_TTL 120 | |
57 | #define ETH_TYPE_LLDP 0x88cc | |
58 | #define MINIMUM_ETH_PACKET_SIZE 68 | |
59 | ||
60 | #define AA_STATUS_MULTIPLE \ | |
61 | AA_STATUS(ACTIVE,2,Active) \ | |
62 | AA_STATUS(REJECT_GENERIC,3,Reject (Generic)) \ | |
63 | AA_STATUS(REJECT_AA_RES_NOTAVAIL,4,Reject (AA resources unavailable)) \ | |
64 | AA_STATUS(REJECT_INVALID,6,Reject (Invalid)) \ | |
65 | AA_STATUS(REJECT_VLAN_RES_UNAVAIL,8,Reject (VLAN resources unavailable)) \ | |
66 | AA_STATUS(REJECT_VLAN_APP_ISSUE,9,Reject (Application interaction issue)) \ | |
67 | AA_STATUS(PENDING,255,Pending) | |
68 | ||
69 | enum aa_status { | |
70 | #define AA_STATUS(NAME, VALUE, STR) AA_STATUS_##NAME = VALUE, | |
71 | AA_STATUS_MULTIPLE | |
72 | #undef AA_STATUS | |
73 | AA_STATUS_N_MULTIPLE | |
74 | }; | |
75 | ||
76 | /* Internal structure for an Auto Attach mapping. | |
77 | */ | |
78 | struct aa_mapping_internal { | |
79 | struct hmap_node hmap_node_isid; | |
80 | struct hmap_node hmap_node_aux; | |
81 | int64_t isid; | |
82 | int64_t vlan; | |
83 | void *aux; | |
84 | enum aa_status status; | |
85 | }; | |
86 | ||
87 | static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER; | |
88 | ||
89 | /* Hash map of all LLDP instances keyed by name (port at the moment). | |
90 | */ | |
91 | static struct hmap all_lldps__ = HMAP_INITIALIZER(&all_lldps__); | |
92 | static struct hmap *const all_lldps OVS_GUARDED_BY(mutex) = &all_lldps__; | |
93 | ||
94 | /* Hash map of all the Auto Attach mappings. Global at the moment (but will | |
95 | * be per bridge). Used when adding a new port to a bridge so that we can | |
96 | * properly install all the configured mapping on the port and export them | |
97 | * To the Auto Attach server via LLDP. | |
98 | */ | |
99 | static struct hmap all_mappings__ = HMAP_INITIALIZER(&all_mappings__); | |
100 | static struct hmap *const all_mappings OVS_GUARDED_BY(mutex) = &all_mappings__; | |
101 | ||
102 | static struct lldp_aa_element_system_id system_id_null; | |
103 | ||
104 | /* Convert an array to an integer. I-SID are stored in an array of bytes | |
105 | * in the LLDP hardware structrure. | |
106 | */ | |
107 | static uint32_t | |
108 | array_to_int(uint8_t *array, size_t len) | |
109 | { | |
110 | uint32_t res = 0; | |
111 | unsigned int i = 0; | |
112 | ||
113 | ovs_assert(len <= sizeof(uint32_t)); | |
114 | ||
115 | for (i = 0; i < len; i++) { | |
116 | res = res | (array[len - i - 1] << (i * 8)); | |
117 | } | |
118 | ||
119 | return res; | |
120 | } | |
121 | ||
122 | /* Convert an integer to an array of byte. | |
123 | */ | |
124 | static void | |
125 | int_to_array(uint8_t *array, size_t len, uint32_t value) | |
126 | { | |
127 | unsigned int i; | |
128 | ||
129 | ovs_assert(len <= sizeof(uint32_t)); | |
130 | ||
131 | for (i = 0; i < len; i++) { | |
132 | array[len - i - 1] = value >> (8 * i); | |
133 | } | |
134 | } | |
135 | ||
136 | /* Convert an LLDP chassis ID to a string. | |
137 | */ | |
138 | static void | |
139 | chassisid_to_string(uint8_t *array, size_t len, char **str) | |
140 | { | |
141 | unsigned int i; | |
142 | ||
143 | *str = xmalloc(len * 3); | |
144 | ||
145 | for (i = 0; i < len; i++) { | |
146 | snprintf(&(*str)[i * 3], 4, "%02x:", array[i]); | |
147 | } | |
148 | (*str)[(i * 3) - 1] = '\0'; | |
149 | } | |
150 | ||
151 | /* Find an Auto Attach mapping keyed by I-SID. | |
152 | */ | |
153 | static struct aa_mapping_internal * | |
154 | mapping_find_by_isid(struct lldp *lldp, const uint64_t isid) | |
155 | OVS_REQUIRES(mutex) | |
156 | { | |
157 | struct aa_mapping_internal *m; | |
158 | ||
159 | HMAP_FOR_EACH_IN_BUCKET (m, | |
160 | hmap_node_isid, | |
161 | hash_bytes(&isid, sizeof isid, 0), | |
162 | &lldp->mappings_by_isid) { | |
163 | if (isid == m->isid) { | |
164 | return m; | |
165 | } | |
166 | } | |
167 | ||
168 | return NULL; | |
169 | } | |
170 | ||
171 | /* Find an Auto Attach mapping keyed by aux. aux is an opaque pointer created | |
172 | * by the bridge that refers to an OVSDB mapping record. | |
173 | */ | |
174 | static struct aa_mapping_internal * | |
175 | mapping_find_by_aux(struct lldp *lldp, const void *aux) OVS_REQUIRES(mutex) | |
176 | { | |
177 | struct aa_mapping_internal *m; | |
178 | ||
179 | HMAP_FOR_EACH_IN_BUCKET (m, hmap_node_aux, hash_pointer(aux, 0), | |
180 | &lldp->mappings_by_aux) { | |
181 | if (aux == m->aux) { | |
182 | return m; | |
183 | } | |
184 | } | |
185 | ||
186 | return NULL; | |
187 | } | |
188 | ||
189 | /* Convert an Auto Attach request status to a string. | |
190 | */ | |
191 | static char * | |
192 | aa_status_to_str(uint8_t status) | |
193 | { | |
194 | switch (status) { | |
195 | #define AA_STATUS(NAME, VALUE, STR) case AA_STATUS_##NAME: return #STR; | |
196 | AA_STATUS_MULTIPLE | |
197 | #undef AA_STATUS | |
198 | default: return "Undefined"; | |
199 | } | |
200 | } | |
201 | ||
202 | /* Display LLDP and Auto Attach statistics. | |
203 | */ | |
204 | static void | |
205 | aa_print_lldp_and_aa_stats(struct ds *ds, struct lldp *lldp) | |
206 | OVS_REQUIRES(mutex) | |
207 | { | |
208 | struct lldpd_hardware *hw; | |
209 | ||
210 | ds_put_format(ds, "Statistics: %s\n", lldp->name); | |
211 | ||
212 | if (!lldp->lldpd) { | |
213 | return; | |
214 | } | |
215 | ||
216 | LIST_FOR_EACH (hw, h_entries, &lldp->lldpd->g_hardware.h_entries) { | |
217 | ds_put_format(ds, "\ttx cnt: %"PRIu64"\n", hw->h_tx_cnt); | |
218 | ds_put_format(ds, "\trx cnt: %"PRIu64"\n", hw->h_rx_cnt); | |
219 | ds_put_format(ds, "\trx discarded cnt: %"PRIu64"\n", | |
220 | hw->h_rx_discarded_cnt); | |
221 | ds_put_format(ds, "\trx unrecognized cnt: %"PRIu64"\n", | |
222 | hw->h_rx_unrecognized_cnt); | |
223 | ds_put_format(ds, "\tageout cnt: %"PRIu64"\n", hw->h_ageout_cnt); | |
224 | ds_put_format(ds, "\tinsert cnt: %"PRIu64"\n", hw->h_insert_cnt); | |
225 | ds_put_format(ds, "\tdelete cnt: %"PRIu64"\n", hw->h_delete_cnt); | |
226 | ds_put_format(ds, "\tdrop cnt: %"PRIu64"\n", hw->h_drop_cnt); | |
227 | } | |
228 | } | |
229 | ||
230 | static void | |
231 | aa_print_element_status_port(struct ds *ds, struct lldpd_hardware *hw) | |
232 | { | |
233 | struct lldpd_port *port; | |
234 | ||
235 | LIST_FOR_EACH (port, p_entries, &hw->h_rports.p_entries) { | |
236 | if (memcmp(&port->p_element.system_id, | |
237 | &system_id_null, | |
238 | sizeof port->p_element.system_id)) { | |
239 | static char *none_str = "<None>"; | |
240 | char *id = none_str, *descr = none_str, *system = none_str; | |
241 | ||
242 | if (port->p_chassis) { | |
243 | if (port->p_chassis->c_id_len > 0) { | |
244 | chassisid_to_string((uint8_t *) port->p_chassis->c_id, | |
245 | port->p_chassis->c_id_len, &id); | |
246 | } | |
247 | ||
248 | descr = port->p_chassis->c_descr | |
249 | ? port->p_chassis->c_descr : none_str; | |
250 | } | |
251 | ||
252 | chassisid_to_string((uint8_t *) &port->p_element.system_id, | |
253 | sizeof port->p_element.system_id, &system); | |
254 | ||
255 | ds_put_format(ds, | |
256 | "\tAuto Attach Primary Server Id: %s\n", | |
257 | id); | |
258 | ds_put_format(ds, | |
259 | "\tAuto Attach Primary Server Descr: %s\n", | |
260 | descr); | |
261 | ds_put_format(ds, | |
262 | "\tAuto Attach Primary Server System Id: %s\n", | |
263 | system); | |
264 | ||
265 | free(id); | |
266 | free(system); | |
267 | } | |
268 | } | |
269 | } | |
270 | ||
271 | /* Auto Attach server broadcast an LLDP message periodically. Display | |
272 | * the discovered server. | |
273 | */ | |
274 | static void | |
275 | aa_print_element_status(struct ds *ds, struct lldp *lldp) OVS_REQUIRES(mutex) | |
276 | { | |
277 | struct lldpd_hardware *hw; | |
278 | ||
279 | ds_put_format(ds, "LLDP: %s\n", lldp->name); | |
280 | ||
281 | if (!lldp->lldpd) { | |
282 | return; | |
283 | } | |
284 | ||
285 | LIST_FOR_EACH (hw, h_entries, &lldp->lldpd->g_hardware.h_entries) { | |
286 | aa_print_element_status_port(ds, hw); | |
287 | } | |
288 | } | |
289 | ||
290 | static void | |
291 | aa_print_isid_status_port_isid(struct lldp *lldp, struct lldpd_port *port) | |
292 | OVS_REQUIRES(mutex) | |
293 | { | |
294 | struct lldpd_aa_isid_vlan_maps_tlv *mapping; | |
295 | ||
296 | if (list_is_empty(&port->p_isid_vlan_maps.m_entries)) { | |
297 | return; | |
298 | } | |
299 | ||
300 | LIST_FOR_EACH (mapping, m_entries, &port->p_isid_vlan_maps.m_entries) { | |
301 | uint32_t isid = array_to_int(mapping->isid_vlan_data.isid, | |
302 | sizeof mapping->isid_vlan_data.isid); | |
303 | struct aa_mapping_internal *m = mapping_find_by_isid(lldp, isid); | |
304 | ||
305 | VLOG_INFO("h_rport: isid=%u, vlan=%u, status=%d", | |
306 | isid, | |
307 | mapping->isid_vlan_data.vlan, | |
308 | mapping->isid_vlan_data.status); | |
309 | ||
310 | /* Update the status of our internal state for the mapping. | |
311 | */ | |
312 | if (m) { | |
313 | VLOG_INFO("Setting status for ISID=%u to %u", | |
314 | isid, | |
315 | mapping->isid_vlan_data.status); | |
316 | m->status = mapping->isid_vlan_data.status; | |
317 | } else { | |
318 | VLOG_WARN("Couldn't find mapping for I-SID=%u", isid); | |
319 | } | |
320 | } | |
321 | } | |
322 | ||
323 | static void | |
324 | aa_print_isid_status_port(struct lldp *lldp, struct lldpd_hardware *hw) | |
325 | OVS_REQUIRES(mutex) | |
326 | { | |
327 | struct lldpd_port *port; | |
328 | ||
329 | LIST_FOR_EACH (port, p_entries, &hw->h_rports.p_entries) { | |
330 | aa_print_isid_status_port_isid(lldp, port); | |
331 | } | |
332 | } | |
333 | ||
334 | /* The Auto Attach server will broadcast the status of the configured mappings | |
335 | * via LLDP. Display the status. | |
336 | */ | |
337 | static void | |
338 | aa_print_isid_status(struct ds *ds, struct lldp *lldp) OVS_REQUIRES(mutex) | |
339 | { | |
340 | struct lldpd_hardware *hw; | |
341 | struct aa_mapping_internal *m; | |
342 | ||
343 | if (!lldp->lldpd) { | |
344 | return; | |
345 | } | |
346 | ||
347 | ds_put_format(ds, "LLDP: %s\n", lldp->name); | |
348 | ||
349 | LIST_FOR_EACH (hw, h_entries, &lldp->lldpd->g_hardware.h_entries) { | |
350 | aa_print_isid_status_port(lldp, hw); | |
351 | } | |
352 | ||
353 | ds_put_format(ds, "%-8s %-4s %-11s %-8s\n", | |
354 | "I-SID", | |
355 | "VLAN", | |
356 | "Source", | |
357 | "Status"); | |
358 | ds_put_format(ds, "-------- ---- ----------- --------\n"); | |
359 | ||
360 | HMAP_FOR_EACH (m, hmap_node_isid, &lldp->mappings_by_isid) { | |
361 | ds_put_format(ds, "%-8ld %-4ld %-11s %-11s\n", | |
362 | (long int) m->isid, | |
363 | (long int) m->vlan, | |
364 | "Switch", | |
365 | aa_status_to_str(m->status)); | |
366 | } | |
367 | } | |
368 | ||
369 | static void | |
370 | aa_unixctl_status(struct unixctl_conn *conn, int argc OVS_UNUSED, | |
371 | const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) | |
372 | OVS_EXCLUDED(mutex) | |
373 | { | |
374 | struct lldp *lldp; | |
375 | struct ds ds = DS_EMPTY_INITIALIZER; | |
376 | ||
377 | ovs_mutex_lock(&mutex); | |
378 | ||
379 | HMAP_FOR_EACH (lldp, hmap_node, all_lldps) { | |
380 | aa_print_element_status(&ds, lldp); | |
381 | } | |
382 | unixctl_command_reply(conn, ds_cstr(&ds)); | |
383 | ds_destroy(&ds); | |
384 | ||
385 | ovs_mutex_unlock(&mutex); | |
386 | } | |
387 | ||
388 | static void | |
389 | aa_unixctl_show_isid(struct unixctl_conn *conn, int argc OVS_UNUSED, | |
390 | const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) | |
391 | OVS_EXCLUDED(mutex) | |
392 | { | |
393 | struct lldp *lldp; | |
394 | struct ds ds = DS_EMPTY_INITIALIZER; | |
395 | ||
396 | ovs_mutex_lock(&mutex); | |
397 | ||
398 | HMAP_FOR_EACH (lldp, hmap_node, all_lldps) { | |
399 | aa_print_isid_status(&ds, lldp); | |
400 | } | |
401 | unixctl_command_reply(conn, ds_cstr(&ds)); | |
402 | ds_destroy(&ds); | |
403 | ||
404 | ovs_mutex_unlock(&mutex); | |
405 | } | |
406 | ||
407 | static void | |
408 | aa_unixctl_statistics(struct unixctl_conn *conn, int argc OVS_UNUSED, | |
409 | const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) | |
410 | OVS_EXCLUDED(mutex) | |
411 | { | |
412 | struct ds ds = DS_EMPTY_INITIALIZER; | |
413 | struct lldp *lldp; | |
414 | ||
415 | ovs_mutex_lock(&mutex); | |
416 | ||
417 | /* Cycle through all ports and dump the stats for each one */ | |
418 | HMAP_FOR_EACH (lldp, hmap_node, all_lldps) { | |
419 | aa_print_lldp_and_aa_stats(&ds, lldp); | |
420 | } | |
421 | ||
422 | ovs_mutex_unlock(&mutex); | |
423 | ||
424 | unixctl_command_reply(conn, ds_cstr(&ds)); | |
425 | } | |
426 | ||
427 | /* An Auto Attach mapping was configured. Populate the corresponding | |
428 | * structures in the LLDP hardware. | |
429 | */ | |
430 | static void | |
431 | update_mapping_on_lldp(struct lldp *lldp, struct lldpd_hardware *hardware, | |
432 | struct aa_mapping_internal *m) | |
433 | { | |
434 | struct lldpd_aa_isid_vlan_maps_tlv *lm = xzalloc(sizeof *lm); | |
435 | ||
436 | if (hardware->h_ifname) { | |
437 | VLOG_INFO("\t\t hardware->h_ifname=%s", hardware->h_ifname); | |
438 | } | |
439 | ||
440 | int_to_array(lm->isid_vlan_data.isid, | |
441 | ARRAY_SIZE(lm->isid_vlan_data.isid), | |
442 | (uint32_t) m->isid); | |
443 | lm->isid_vlan_data.vlan = m->vlan; | |
444 | ||
445 | list_push_back(&hardware->h_lport.p_isid_vlan_maps.m_entries, | |
446 | &lm->m_entries); | |
447 | ||
448 | /* TODO Should be done in the Auto Attach state machine when a mapping goes | |
449 | * from "pending" to "active". | |
450 | */ | |
451 | { | |
452 | struct bridge_aa_vlan *node = xmalloc(sizeof *node); | |
453 | ||
454 | node->port_name = xstrdup(hardware->h_ifname); | |
455 | node->vlan = m->vlan; | |
456 | node->oper = BRIDGE_AA_VLAN_OPER_ADD; | |
457 | ||
458 | list_push_back(&lldp->active_mapping_queue, &node->list_node); | |
459 | } | |
460 | } | |
461 | ||
462 | /* Bridge will poll the list of VLAN that needs to be auto configure based on | |
463 | * the Auto Attach mappings that have been exchanged with the server. | |
464 | */ | |
465 | int | |
466 | aa_get_vlan_queued(struct ovs_list *list) | |
467 | { | |
468 | struct lldp *lldp; | |
469 | ||
470 | ovs_mutex_lock(&mutex); | |
471 | ||
472 | HMAP_FOR_EACH (lldp, hmap_node, all_lldps) { | |
473 | struct bridge_aa_vlan *node, *node_next; | |
474 | ||
475 | LIST_FOR_EACH_SAFE (node, | |
476 | node_next, | |
477 | list_node, | |
478 | &lldp->active_mapping_queue) { | |
479 | struct bridge_aa_vlan *copy; | |
480 | ||
481 | copy = xmalloc(sizeof *copy); | |
482 | copy->port_name = xstrdup(node->port_name); | |
483 | copy->vlan = node->vlan; | |
484 | copy->oper = node->oper; | |
485 | ||
486 | list_push_back(list, ©->list_node); | |
487 | ||
488 | /* Cleanup */ | |
489 | list_remove(&node->list_node); | |
490 | free(node->port_name); | |
491 | free(node); | |
492 | } | |
493 | } | |
494 | ||
495 | ovs_mutex_unlock(&mutex); | |
496 | ||
497 | return 0; | |
498 | } | |
499 | ||
500 | /* Bridge will poll whether or not VLAN have been auto-configured. | |
501 | */ | |
502 | unsigned int | |
503 | aa_get_vlan_queue_size(void) | |
504 | { | |
505 | struct lldp *lldp; | |
506 | unsigned int size = 0; | |
507 | ||
508 | ovs_mutex_lock(&mutex); | |
509 | ||
510 | HMAP_FOR_EACH (lldp, hmap_node, all_lldps) { | |
511 | size += list_size(&lldp->active_mapping_queue); | |
512 | } | |
513 | ||
514 | ovs_mutex_unlock(&mutex); | |
515 | ||
516 | return size; | |
517 | } | |
518 | ||
519 | /* Configure Auto Attach. | |
520 | */ | |
521 | int | |
522 | aa_configure(const struct aa_settings *s) | |
523 | { | |
524 | struct lldp *lldp; | |
525 | ||
526 | ovs_mutex_lock(&mutex); | |
527 | ||
528 | /* TODO Change all instances for now */ | |
529 | HMAP_FOR_EACH (lldp, hmap_node, all_lldps) { | |
530 | struct lldpd_chassis *chassis; | |
531 | ||
532 | LIST_FOR_EACH (chassis, list, &lldp->lldpd->g_chassis.list) { | |
533 | /* System Description */ | |
534 | if (chassis->c_descr) { | |
535 | free(chassis->c_descr); | |
536 | } | |
537 | chassis->c_descr = s->system_description[0] ? | |
538 | xstrdup(s->system_description) : xstrdup(PACKAGE_STRING); | |
539 | ||
540 | /* System Name */ | |
541 | if (chassis->c_name) { | |
542 | free(chassis->c_name); | |
543 | } | |
544 | chassis->c_name = xstrdup(s->system_name); | |
545 | } | |
546 | } | |
547 | ||
548 | ovs_mutex_unlock(&mutex); | |
549 | ||
550 | return 0; | |
551 | } | |
552 | ||
553 | /* Add a new Auto Attach mapping. | |
554 | */ | |
555 | int | |
556 | aa_mapping_register(void *aux, const struct aa_mapping_settings *s) | |
557 | { | |
558 | struct aa_mapping_internal *bridge_m; | |
559 | struct lldp *lldp; | |
560 | ||
561 | VLOG_INFO("Adding mapping ISID=%ld, VLAN=%ld, aux=%p", (long int) s->isid, | |
562 | (long int) s->vlan, aux); | |
563 | ||
564 | ovs_mutex_lock(&mutex); | |
565 | ||
566 | /* TODO These mappings should be stores per bridge. This is used | |
567 | * When a port is added. Auto Attach mappings need to be added on this | |
568 | * port. | |
569 | */ | |
570 | bridge_m = xzalloc(sizeof *bridge_m); | |
571 | bridge_m->isid = s->isid; | |
572 | bridge_m->vlan = s->vlan; | |
573 | bridge_m->aux = aux; | |
574 | bridge_m->status = AA_STATUS_PENDING; | |
575 | hmap_insert(all_mappings, | |
576 | &bridge_m->hmap_node_isid, | |
577 | hash_bytes((const void *) &bridge_m->isid, | |
578 | sizeof bridge_m->isid, | |
579 | 0)); | |
580 | ||
581 | /* Update mapping on the all the LLDP instances. */ | |
582 | HMAP_FOR_EACH (lldp, hmap_node, all_lldps) { | |
583 | struct lldpd_hardware *hw; | |
584 | struct aa_mapping_internal *m; | |
585 | ||
586 | VLOG_INFO("\t lldp->name=%s", lldp->name); | |
587 | ||
588 | if (mapping_find_by_isid(lldp, s->isid)) { | |
589 | continue; | |
590 | } | |
591 | ||
592 | m = xzalloc(sizeof *m); | |
593 | m->isid = s->isid; | |
594 | m->vlan = s->vlan; | |
595 | m->status = AA_STATUS_PENDING; | |
596 | m->aux = aux; | |
597 | hmap_insert(&lldp->mappings_by_isid, | |
598 | &m->hmap_node_isid, | |
599 | hash_bytes((const void *) &m->isid, | |
600 | sizeof m->isid, | |
601 | 0)); | |
602 | hmap_insert(&lldp->mappings_by_aux, | |
603 | &m->hmap_node_aux, | |
604 | hash_pointer(m->aux, 0)); | |
605 | ||
606 | /* Configure the mapping on each port of the LLDP stack. */ | |
607 | LIST_FOR_EACH (hw, h_entries, &lldp->lldpd->g_hardware.h_entries) { | |
608 | update_mapping_on_lldp(lldp, hw, m); | |
609 | } | |
610 | } | |
611 | ||
612 | ovs_mutex_unlock(&mutex); | |
613 | ||
614 | return 0; | |
615 | } | |
616 | ||
617 | static void | |
618 | aa_mapping_unregister_mapping(struct lldp *lldp, | |
619 | struct lldpd_hardware *hw, | |
620 | struct aa_mapping_internal *m) | |
621 | { | |
622 | struct lldpd_aa_isid_vlan_maps_tlv *lm, *lm_next; | |
623 | ||
624 | LIST_FOR_EACH_SAFE (lm, | |
625 | lm_next, | |
626 | m_entries, | |
627 | &hw->h_lport.p_isid_vlan_maps.m_entries) { | |
628 | uint32_t isid = array_to_int(lm->isid_vlan_data.isid, | |
629 | sizeof lm->isid_vlan_data.isid); | |
630 | ||
631 | if (isid == (uint32_t) m->isid) { | |
632 | VLOG_INFO("\t\t Removing lport, isid=%u, vlan=%u", | |
633 | isid, | |
634 | lm->isid_vlan_data.vlan); | |
635 | ||
636 | list_remove(&lm->m_entries); | |
637 | ||
638 | /* TODO Should be done in the AA SM when a mapping goes | |
639 | * from "pending" to "active". | |
640 | */ | |
641 | { | |
642 | struct bridge_aa_vlan *node = xmalloc(sizeof *node); | |
643 | ||
644 | node->port_name = xstrdup(hw->h_ifname); | |
645 | node->vlan = (uint32_t) m->vlan; | |
646 | node->oper = BRIDGE_AA_VLAN_OPER_REMOVE; | |
647 | ||
648 | list_push_back(&lldp->active_mapping_queue, &node->list_node); | |
649 | } | |
650 | ||
651 | break; | |
652 | } | |
653 | } | |
654 | } | |
655 | ||
656 | /* Remove an existing Auto Attach mapping. | |
657 | */ | |
658 | int | |
659 | aa_mapping_unregister(void *aux) | |
660 | { | |
661 | struct lldp *lldp; | |
662 | ||
663 | VLOG_INFO("Removing mapping aux=%p", aux); | |
664 | ||
665 | ovs_mutex_lock(&mutex); | |
666 | ||
667 | HMAP_FOR_EACH (lldp, hmap_node, all_lldps) { | |
668 | struct lldpd_hardware *hw; | |
669 | struct aa_mapping_internal *m = mapping_find_by_aux(lldp, aux); | |
670 | int64_t isid_tmp = -1, vlan_tmp = -1; | |
671 | ||
672 | /* Remove from internal hash tables. */ | |
673 | if (m) { | |
674 | struct aa_mapping_internal *p = | |
675 | mapping_find_by_isid(lldp, m->isid); | |
676 | ||
677 | isid_tmp = m->isid; | |
678 | vlan_tmp = m->vlan; | |
679 | VLOG_INFO("\t Removing mapping ISID=%ld, VLAN=%ld (lldp->name=%s)", | |
680 | (long int) m->isid, (long int) m->vlan, lldp->name); | |
681 | ||
682 | if (p) { | |
683 | hmap_remove(&lldp->mappings_by_isid, &p->hmap_node_isid); | |
684 | } | |
685 | ||
686 | hmap_remove(&lldp->mappings_by_aux, &m->hmap_node_aux); | |
687 | free(m); | |
688 | ||
689 | /* Remove from all the lldp instances */ | |
690 | LIST_FOR_EACH (hw, h_entries, &lldp->lldpd->g_hardware.h_entries) { | |
691 | if (hw->h_ifname) { | |
692 | VLOG_INFO("\t\t hardware->h_ifname=%s", hw->h_ifname); | |
693 | } | |
694 | ||
695 | aa_mapping_unregister_mapping(lldp, hw, m); | |
696 | } | |
697 | ||
698 | if (isid_tmp >= 0 && vlan_tmp >= 0) { | |
699 | /* Remove from the all_mappings */ | |
700 | HMAP_FOR_EACH (m, hmap_node_isid, all_mappings) { | |
701 | if (m && isid_tmp == m->isid && vlan_tmp == m->vlan) { | |
702 | hmap_remove(all_mappings, &m->hmap_node_isid); | |
703 | break; | |
704 | } | |
705 | } | |
706 | } | |
707 | } | |
708 | } | |
709 | ||
710 | ovs_mutex_unlock(&mutex); | |
711 | ||
712 | return 0; | |
713 | } | |
714 | ||
715 | void | |
716 | lldp_init(void) | |
717 | { | |
718 | unixctl_command_register("autoattach/status", "[bridge]", 0, 1, | |
719 | aa_unixctl_status, NULL); | |
720 | unixctl_command_register("autoattach/show-isid", "[bridge]", 0, 1, | |
721 | aa_unixctl_show_isid, NULL); | |
722 | unixctl_command_register("autoattach/statistics", "[bridge]", 0, 1, | |
723 | aa_unixctl_statistics, NULL); | |
724 | } | |
725 | ||
726 | /* Returns true if 'lldp' should process packets from 'flow'. Sets | |
727 | * fields in 'wc' that were used to make the determination. | |
728 | */ | |
729 | bool | |
730 | lldp_should_process_flow(const struct flow *flow) | |
731 | { | |
732 | return (flow->dl_type == htons(ETH_TYPE_LLDP)); | |
733 | } | |
734 | ||
735 | ||
736 | /* Process an LLDP packet that was received on a bridge port. | |
737 | */ | |
738 | void | |
739 | lldp_process_packet(struct lldp *lldp, const struct ofpbuf *p) | |
740 | { | |
741 | if (lldp) { | |
742 | lldpd_recv(lldp->lldpd, | |
743 | (struct lldpd_hardware *) | |
744 | lldp->lldpd->g_hardware.h_entries.next, | |
745 | (char *) p->data_, | |
746 | p->size_); | |
747 | } | |
748 | } | |
749 | ||
750 | /* This code is called periodically to check if the LLDP module has an LLDP | |
751 | * message it wishes to send. It is called several times every second. | |
752 | */ | |
753 | bool | |
754 | lldp_should_send_packet(struct lldp *cfg) OVS_EXCLUDED(mutex) | |
755 | { | |
756 | bool ret; | |
757 | ||
758 | ovs_mutex_lock(&mutex); | |
759 | ret = timer_expired(&cfg->tx_timer); | |
760 | ovs_mutex_unlock(&mutex); | |
761 | ||
762 | return ret; | |
763 | } | |
764 | ||
765 | /* Returns the next wake up time. | |
766 | */ | |
767 | long long int | |
768 | lldp_wake_time(const struct lldp *lldp) OVS_EXCLUDED(mutex) | |
769 | { | |
770 | long long int retval; | |
771 | ||
772 | if (!lldp) { | |
773 | return LLONG_MAX; | |
774 | } | |
775 | ||
776 | ovs_mutex_lock(&mutex); | |
777 | retval = lldp->tx_timer.t; | |
778 | ovs_mutex_unlock(&mutex); | |
779 | ||
780 | return retval; | |
781 | } | |
782 | ||
783 | /* Put the monitor thread to sleep until it's next wake time. | |
784 | */ | |
785 | long long int | |
786 | lldp_wait(struct lldp *lldp) OVS_EXCLUDED(mutex) | |
787 | { | |
788 | long long int wake_time = lldp_wake_time(lldp); | |
789 | poll_timer_wait_until(wake_time); | |
790 | return wake_time; | |
791 | } | |
792 | ||
793 | /* Prepare the LLDP packet to be sent on a bridge port. | |
794 | */ | |
795 | void | |
796 | lldp_put_packet(struct lldp *lldp, struct ofpbuf *packet, | |
797 | uint8_t eth_src[ETH_ADDR_LEN]) OVS_EXCLUDED(mutex) | |
798 | { | |
799 | struct lldpd *mylldpd = lldp->lldpd; | |
800 | struct lldpd_hardware *hw = (struct lldpd_hardware *) | |
801 | mylldpd->g_hardware.h_entries.next; | |
802 | uint32_t lldp_size = 0; | |
803 | static const uint8_t eth_addr_lldp[6] = | |
804 | {0x01, 0x80, 0xC2, 0x00, 0x00, 0x0e}; | |
805 | ||
806 | ovs_mutex_lock(&mutex); | |
807 | ||
808 | eth_compose(packet, eth_addr_lldp, eth_src, ETH_TYPE_LLDP, 0); | |
809 | ||
810 | lldp_size = lldpd_send(hw, packet); | |
811 | if (lldp_size + ETH_HEADER_LEN < MINIMUM_ETH_PACKET_SIZE) { | |
812 | lldp_size = MINIMUM_ETH_PACKET_SIZE; | |
813 | } | |
814 | ||
815 | timer_set_duration(&lldp->tx_timer, lldp->lldpd->g_config.c_tx_interval); | |
816 | ovs_mutex_unlock(&mutex); | |
817 | } | |
818 | ||
819 | /* Configures the LLDP stack. | |
820 | */ | |
821 | bool | |
822 | lldp_configure(struct lldp *lldp) OVS_EXCLUDED(mutex) | |
823 | { | |
824 | if (lldp) { | |
825 | ovs_mutex_lock(&mutex); | |
826 | timer_set_expired(&lldp->tx_timer); | |
827 | timer_set_duration(&lldp->tx_timer, LLDP_DEFAULT_TRANSMIT_INTERVAL_MS); | |
828 | lldp->lldpd->g_config.c_tx_interval = | |
829 | LLDP_DEFAULT_TRANSMIT_INTERVAL_MS; | |
830 | ovs_mutex_unlock(&mutex); | |
831 | } | |
832 | ||
833 | return true; | |
834 | } | |
835 | ||
836 | /* Create an LLDP stack instance. At the moment there is one per bridge port. | |
837 | */ | |
838 | struct lldp * | |
839 | lldp_create(const struct netdev *netdev, | |
840 | const uint32_t mtu, | |
841 | const struct smap *cfg) OVS_EXCLUDED(mutex) | |
842 | { | |
843 | struct lldp *lldp; | |
844 | struct lldpd_chassis *lchassis; | |
845 | struct lldpd_hardware *hw; | |
846 | struct aa_mapping_internal *m; | |
847 | ||
848 | if (!cfg || !smap_get_bool(cfg, "enable", false)) { | |
849 | return NULL; | |
850 | } | |
851 | ||
852 | lldp = xzalloc(sizeof *lldp); | |
853 | lldp->name = xstrdup(netdev_get_name(netdev)); | |
854 | lldp->lldpd = xzalloc(sizeof *lldp->lldpd); | |
855 | ||
856 | hmap_init(&lldp->mappings_by_isid); | |
857 | hmap_init(&lldp->mappings_by_aux); | |
858 | list_init(&lldp->active_mapping_queue); | |
859 | ||
860 | lchassis = xzalloc(sizeof *lchassis); | |
861 | lchassis->c_cap_available = LLDP_CAP_BRIDGE; | |
862 | lchassis->c_cap_enabled = LLDP_CAP_BRIDGE; | |
863 | lchassis->c_id_subtype = LLDP_CHASSISID_SUBTYPE_LLADDR; | |
864 | lchassis->c_id_len = ETH_ADDR_LEN; | |
865 | lchassis->c_id = xmalloc(ETH_ADDR_LEN); | |
866 | netdev_get_etheraddr(netdev, (uint8_t *) lchassis->c_id); | |
867 | ||
868 | list_init(&lchassis->c_mgmt.m_entries); | |
869 | lchassis->c_ttl = lldp->lldpd->g_config.c_tx_interval * | |
870 | lldp->lldpd->g_config.c_tx_hold; | |
871 | lchassis->c_ttl = LLDP_CHASSIS_TTL; | |
872 | lldpd_assign_cfg_to_protocols(lldp->lldpd); | |
873 | list_init(&lldp->lldpd->g_chassis.list); | |
874 | list_push_back(&lldp->lldpd->g_chassis.list, &lchassis->list); | |
875 | ||
876 | if ((hw = lldpd_alloc_hardware(lldp->lldpd, | |
877 | (char *) netdev_get_name(netdev), | |
878 | 0)) == NULL) { | |
879 | VLOG_WARN("Unable to allocate space for %s", | |
880 | (char *) netdev_get_name(netdev)); | |
881 | out_of_memory(); | |
882 | } | |
883 | ||
884 | ovs_refcount_init(&lldp->ref_cnt); | |
885 | #ifndef _WIN32 | |
886 | hw->h_flags |= IFF_RUNNING; | |
887 | #endif | |
888 | hw->h_mtu = mtu; | |
889 | hw->h_lport.p_id_subtype = LLDP_PORTID_SUBTYPE_IFNAME; | |
890 | hw->h_lport.p_id = xstrdup(netdev_get_name(netdev)); | |
891 | ||
892 | /* p_id is not necessarily a null terminated string. */ | |
893 | hw->h_lport.p_id_len = strlen(netdev_get_name(netdev)); | |
894 | ||
895 | /* Auto Attach element tlv */ | |
896 | hw->h_lport.p_element.type = LLDP_TLV_AA_ELEM_TYPE_TAG_CLIENT; | |
897 | hw->h_lport.p_element.mgmt_vlan = 0; | |
898 | memcpy(&hw->h_lport.p_element.system_id.system_mac, | |
899 | lchassis->c_id, lchassis->c_id_len); | |
900 | hw->h_lport.p_element.system_id.conn_type = | |
901 | LLDP_TLV_AA_ELEM_CONN_TYPE_SINGLE; | |
902 | ||
903 | hw->h_lport.p_element.system_id.smlt_id = 0; | |
904 | hw->h_lport.p_element.system_id.mlt_id[0] = 0; | |
905 | hw->h_lport.p_element.system_id.mlt_id[1] = 0; | |
906 | ||
907 | list_init(&hw->h_lport.p_isid_vlan_maps.m_entries); | |
908 | list_init(&lldp->lldpd->g_hardware.h_entries); | |
909 | list_push_back(&lldp->lldpd->g_hardware.h_entries, &hw->h_entries); | |
910 | ||
911 | ovs_mutex_lock(&mutex); | |
912 | ||
913 | /* Update port with Auto Attach mappings configured. */ | |
914 | HMAP_FOR_EACH (m, hmap_node_isid, all_mappings) { | |
915 | struct aa_mapping_internal *p; | |
916 | ||
917 | if (mapping_find_by_isid(lldp, m->isid)) { | |
918 | continue; | |
919 | } | |
920 | ||
921 | p = xmemdup(m, sizeof *p); | |
922 | hmap_insert(&lldp->mappings_by_isid, | |
923 | &p->hmap_node_isid, | |
924 | hash_bytes((const void *) &p->isid, | |
925 | sizeof p->isid, | |
926 | 0)); | |
927 | hmap_insert(&lldp->mappings_by_aux, | |
928 | &p->hmap_node_aux, | |
929 | hash_pointer(p->aux, 0)); | |
930 | ||
931 | update_mapping_on_lldp(lldp, hw, p); | |
932 | } | |
933 | ||
934 | hmap_insert(all_lldps, &lldp->hmap_node, | |
935 | hash_string(netdev_get_name(netdev), 0)); | |
936 | ||
937 | ovs_mutex_unlock(&mutex); | |
938 | ||
939 | return lldp; | |
940 | } | |
941 | ||
942 | ||
943 | struct lldp * | |
944 | lldp_create_dummy(void) | |
945 | { | |
946 | struct lldp *lldp; | |
947 | struct lldpd_chassis *lchassis; | |
948 | struct lldpd_hardware *hw; | |
949 | ||
950 | lldp = xzalloc(sizeof *lldp); | |
951 | lldp->name = "dummy-lldp"; | |
952 | lldp->lldpd = xzalloc(sizeof *lldp->lldpd); | |
953 | ||
954 | hmap_init(&lldp->mappings_by_isid); | |
955 | hmap_init(&lldp->mappings_by_aux); | |
956 | list_init(&lldp->active_mapping_queue); | |
957 | ||
958 | lchassis = xzalloc(sizeof *lchassis); | |
959 | lchassis->c_cap_available = LLDP_CAP_BRIDGE; | |
960 | lchassis->c_cap_enabled = LLDP_CAP_BRIDGE; | |
961 | lchassis->c_id_subtype = LLDP_CHASSISID_SUBTYPE_LLADDR; | |
962 | lchassis->c_id_len = ETH_ADDR_LEN; | |
963 | lchassis->c_id = xmalloc(ETH_ADDR_LEN); | |
964 | ||
965 | list_init(&lchassis->c_mgmt.m_entries); | |
966 | lchassis->c_ttl = LLDP_CHASSIS_TTL; | |
967 | lldpd_assign_cfg_to_protocols(lldp->lldpd); | |
968 | list_init(&lldp->lldpd->g_chassis.list); | |
969 | list_push_back(&lldp->lldpd->g_chassis.list, &lchassis->list); | |
970 | ||
971 | if ((hw = lldpd_alloc_hardware(lldp->lldpd, | |
972 | "dummy-hw", | |
973 | 0)) == NULL) { | |
974 | VLOG_WARN("Unable to allocate space for dummy-hw"); | |
975 | out_of_memory(); | |
976 | } | |
977 | ||
978 | ovs_refcount_init(&lldp->ref_cnt); | |
979 | #ifndef _WIN32 | |
980 | hw->h_flags |= IFF_RUNNING; | |
981 | #endif | |
982 | hw->h_mtu = 1500; | |
983 | hw->h_lport.p_id_subtype = LLDP_PORTID_SUBTYPE_IFNAME; | |
984 | hw->h_lport.p_id = "dummy-port"; | |
985 | ||
986 | /* p_id is not necessarily a null terminated string. */ | |
987 | hw->h_lport.p_id_len = strlen(hw->h_lport.p_id); | |
988 | ||
989 | /* Auto Attach element tlv */ | |
990 | hw->h_lport.p_element.type = LLDP_TLV_AA_ELEM_TYPE_TAG_CLIENT; | |
991 | hw->h_lport.p_element.mgmt_vlan = 0; | |
992 | memcpy(&hw->h_lport.p_element.system_id.system_mac, | |
993 | lchassis->c_id, lchassis->c_id_len); | |
994 | hw->h_lport.p_element.system_id.conn_type = | |
995 | LLDP_TLV_AA_ELEM_CONN_TYPE_SINGLE; | |
996 | hw->h_lport.p_element.system_id.smlt_id = 0; | |
997 | hw->h_lport.p_element.system_id.mlt_id[0] = 0; | |
998 | hw->h_lport.p_element.system_id.mlt_id[1] = 0; | |
999 | ||
1000 | list_init(&hw->h_lport.p_isid_vlan_maps.m_entries); | |
1001 | list_init(&lldp->lldpd->g_hardware.h_entries); | |
1002 | list_push_back(&lldp->lldpd->g_hardware.h_entries, &hw->h_entries); | |
1003 | ||
1004 | return lldp; | |
1005 | } | |
1006 | ||
1007 | /* Unreference a specific LLDP instance. | |
1008 | */ | |
1009 | void | |
1010 | lldp_unref(struct lldp *lldp) | |
1011 | { | |
1012 | if (!lldp) { | |
1013 | return; | |
1014 | } | |
1015 | ||
1016 | ovs_mutex_lock(&mutex); | |
1017 | if (ovs_refcount_unref_relaxed(&lldp->ref_cnt) != 1) { | |
1018 | ovs_mutex_unlock(&mutex); | |
1019 | return; | |
1020 | } | |
1021 | ||
1022 | hmap_remove(all_lldps, &lldp->hmap_node); | |
1023 | ovs_mutex_unlock(&mutex); | |
1024 | ||
1025 | lldpd_cleanup(lldp->lldpd); | |
1026 | free(lldp->lldpd); | |
1027 | free(lldp->name); | |
1028 | free(lldp); | |
1029 | } | |
1030 | ||
1031 | /* Unreference a specific LLDP instance. | |
1032 | */ | |
1033 | struct lldp * | |
1034 | lldp_ref(const struct lldp *lldp_) | |
1035 | { | |
1036 | struct lldp *lldp = CONST_CAST(struct lldp *, lldp_); | |
1037 | if (lldp) { | |
1038 | ovs_refcount_ref(&lldp->ref_cnt); | |
1039 | } | |
1040 | return lldp; | |
1041 | } |