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