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