]> git.proxmox.com Git - mirror_ovs.git/blame - lib/lldp/lldpd.c
list: Remove lib/list.h completely.
[mirror_ovs.git] / lib / lldp / lldpd.c
CommitLineData
be53a5c4
DF
1/* -*- mode: c; c-file-style: "openbsd" -*- */
2/*
cc841966 3 * Copyright (c) 2015 Nicira, Inc.
be53a5c4
DF
4 * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <config.h>
20#include "lldpd.h"
21#include <arpa/inet.h>
22#include <errno.h>
23#include <fcntl.h>
24#include <inttypes.h>
25#include <signal.h>
26#include <stdio.h>
27#include <sys/ioctl.h>
28#include <sys/socket.h>
29#include <sys/stat.h>
30#include <sys/time.h>
31#include <sys/types.h>
32#include <sys/wait.h>
be53a5c4
DF
33#include <unistd.h>
34#ifndef _WIN32
35#include <grp.h>
36#include <libgen.h>
be53a5c4
DF
37#include <pwd.h>
38#include <sys/select.h>
39#include <sys/utsname.h>
40#endif
41#include "compiler.h"
b19bab5b 42#include "openvswitch/list.h"
be53a5c4 43#include "packets.h"
d9f3b69e 44#include "timeval.h"
be53a5c4
DF
45
46VLOG_DEFINE_THIS_MODULE(lldpd);
47
48static struct protocol protos[] =
49{
50 { LLDPD_MODE_LLDP, 1, "LLDP", 'l', lldp_send, lldp_decode, NULL,
51 LLDP_MULTICAST_ADDR },
52 { 0, 0, "any", ' ', NULL, NULL, NULL,
74ff3298 53 { { { 0,0,0,0,0,0 } } } }
be53a5c4
DF
54};
55
56void lldpd_assign_cfg_to_protocols(struct lldpd *cfg)
57{
58 cfg->g_protocols = protos;
59}
60
61struct lldpd_hardware *
62lldpd_get_hardware(struct lldpd *cfg, char *name, int index,
63 struct lldpd_ops *ops)
64{
65 struct lldpd_hardware *hw;
66
2cbb2d90 67 LIST_FOR_EACH (hw, h_entries, &cfg->g_hardware) {
f94d6e78
BP
68 if (!strcmp(hw->h_ifname, name) && hw->h_ifindex == index
69 && (!ops || ops == hw->h_ops)) {
be53a5c4
DF
70 return hw;
71 }
72 }
73
74 return NULL;
75}
76
77struct lldpd_hardware *
78lldpd_alloc_hardware(struct lldpd *cfg, char *name, int index)
79{
80 struct lldpd_hardware *hw;
81
82 VLOG_DBG("allocate a new local hardware interface (%s)", name);
83
cc841966 84 hw = xzalloc(sizeof *hw);
be53a5c4
DF
85 hw->h_cfg = cfg;
86 ovs_strlcpy(hw->h_ifname, name, sizeof hw->h_ifname);
87 hw->h_ifindex = index;
e87a8e67 88 hw->h_lport.p_chassis = CONTAINER_OF(list_front(&cfg->g_chassis),
937f71bf 89 struct lldpd_chassis, list);
be53a5c4 90 hw->h_lport.p_chassis->c_refcount++;
135706b3 91 list_init(&hw->h_rports);
be53a5c4
DF
92
93 return hw;
94}
95
96struct lldpd_mgmt *
97lldpd_alloc_mgmt(int family, void *addrptr, size_t addrsize, u_int32_t iface)
98{
99 struct lldpd_mgmt *mgmt;
100
101 VLOG_DBG("allocate a new management address (family: %d)", family);
102
103 if (family <= LLDPD_AF_UNSPEC || family >= LLDPD_AF_LAST) {
104 errno = EAFNOSUPPORT;
105 return NULL;
106 }
107 if (addrsize > LLDPD_MGMT_MAXADDRSIZE) {
108 errno = EOVERFLOW;
109 return NULL;
110 }
cc841966 111 mgmt = xzalloc(sizeof *mgmt);
be53a5c4
DF
112 mgmt->m_family = family;
113 memcpy(&mgmt->m_addr, addrptr, addrsize);
114 mgmt->m_addrsize = addrsize;
115 mgmt->m_iface = iface;
116
117 return mgmt;
118}
119
120void
121lldpd_hardware_cleanup(struct lldpd *cfg, struct lldpd_hardware *hardware)
122{
123 VLOG_DBG("cleanup hardware port %s", hardware->h_ifname);
124
02d593b1 125 lldpd_port_cleanup(&hardware->h_lport, true);
be53a5c4
DF
126 if (hardware->h_ops && hardware->h_ops->cleanup) {
127 hardware->h_ops->cleanup(cfg, hardware);
128 }
129 free(hardware);
130}
131
132void
133lldpd_cleanup(struct lldpd *cfg)
134{
135 struct lldpd_hardware *hw, *hw_next;
136 struct lldpd_chassis *chassis, *chassis_next;
137
138 VLOG_DBG("cleanup all ports");
139
2cbb2d90 140 LIST_FOR_EACH_SAFE (hw, hw_next, h_entries, &cfg->g_hardware) {
be53a5c4
DF
141 if (!hw->h_flags) {
142 list_remove(&hw->h_entries);
02d593b1 143 lldpd_remote_cleanup(hw, NULL, true);
be53a5c4
DF
144 lldpd_hardware_cleanup(cfg, hw);
145 } else {
02d593b1 146 lldpd_remote_cleanup(hw, NULL, false);
be53a5c4
DF
147 }
148 }
149
150 VLOG_DBG("cleanup all chassis");
151
e87a8e67 152 LIST_FOR_EACH_SAFE (chassis, chassis_next, list, &cfg->g_chassis) {
be53a5c4
DF
153 if (chassis->c_refcount == 0) {
154 list_remove(&chassis->list);
155 lldpd_chassis_cleanup(chassis, 1);
156 }
157 }
158}
159
160/* Update chassis `ochassis' with values from `chassis'. The later one is not
161 * expected to be part of a list! It will also be wiped from memory.
162 */
163static void
164lldpd_move_chassis(struct lldpd_chassis *ochassis,
165 struct lldpd_chassis *chassis)
166{
5f03c983 167 struct lldpd_mgmt *mgmt;
be53a5c4
DF
168 int refcount = ochassis->c_refcount;
169 int index = ochassis->c_index;
170 struct ovs_list listcopy;
171
172 /* We want to keep refcount, index and list stuff from the current chassis
173 */
174 memcpy(&listcopy, &ochassis->list, sizeof listcopy);
175 lldpd_chassis_cleanup(ochassis, 0);
176
177 /* Make the copy. */
178 /* WARNING: this is a kludgy hack, we need in-place copy and cannot use
179 * marshaling.
180 */
181 memcpy(ochassis, chassis, sizeof *ochassis);
32590179 182 list_init(&ochassis->c_mgmt);
be53a5c4
DF
183
184 /* Copy of management addresses */
5f03c983 185 LIST_FOR_EACH_POP (mgmt, m_entries, &chassis->c_mgmt) {
32590179 186 list_insert(&ochassis->c_mgmt, &mgmt->m_entries);
be53a5c4
DF
187 }
188
189 /* Restore saved values */
190 ochassis->c_refcount = refcount;
191 ochassis->c_index = index;
192 memcpy(&ochassis->list, &listcopy, sizeof ochassis->list);
193
194 /* Get rid of the new chassis */
195 free(chassis);
196}
197
198static int
199lldpd_guess_type(struct lldpd *cfg, char *frame, int s)
200{
201 int i;
202
203 if (s < ETH_ADDR_LEN) {
204 return -1;
205 }
206
207 for (i = 0; cfg->g_protocols[i].mode != 0; i++) {
208 if (!cfg->g_protocols[i].enabled) {
209 continue;
210 }
211 if (cfg->g_protocols[i].guess == NULL) {
74ff3298 212 if (memcmp(frame, &cfg->g_protocols[i].mac, ETH_ADDR_LEN) == 0) {
be53a5c4
DF
213 VLOG_DBG("guessed protocol is %s (from MAC address)",
214 cfg->g_protocols[i].name);
215 return cfg->g_protocols[i].mode;
216 }
217 } else {
218 if (cfg->g_protocols[i].guess(frame, s)) {
219 VLOG_DBG("guessed protocol is %s (from detector function)",
220 cfg->g_protocols[i].name);
221 return cfg->g_protocols[i].mode;
222 }
223 }
224 }
225
226 return -1;
227}
228
229static void
230lldpd_decode(struct lldpd *cfg, char *frame, int s,
231 struct lldpd_hardware *hw)
232{
233 size_t listsize, i;
234 struct lldpd_chassis *chassis, *ochassis = NULL;
235 struct lldpd_port *port, *oport;
236 int guess = LLDPD_MODE_LLDP;
237 struct eth_header eheader;
238 int count = 0;
8a42c2d6 239 bool found = false;
be53a5c4
DF
240
241 VLOG_DBG("decode a received frame on %s size %d", hw->h_ifname,s);
242
243 if (s < sizeof(struct eth_header) + 4) {
244 /* Too short, just discard it */
245 return;
246 }
247
248 /* Decapsulate VLAN frames */
249 memcpy(&eheader, frame, sizeof eheader);
250 if (eheader.eth_type == htons(ETH_TYPE_VLAN)) {
251 /* VLAN decapsulation means to shift 4 bytes left the frame from
252 * offset 2 * ETH_ADDR_LEN
253 */
254 memmove(frame + 2 * ETH_ADDR_LEN, frame + 2 * ETH_ADDR_LEN + 4,
255 s - 2 * ETH_ADDR_LEN);
256 s -= 4;
257 }
258
135706b3 259 LIST_FOR_EACH (oport, p_entries, &hw->h_rports) {
f94d6e78
BP
260 if (oport->p_lastframe &&
261 oport->p_lastframe->size == s &&
262 !memcmp(oport->p_lastframe->frame, frame, s)) {
be53a5c4
DF
263 /* Already received the same frame */
264 VLOG_DBG("duplicate frame, no need to decode");
d9f3b69e 265 oport->p_lastupdate = time_now();
be53a5c4
DF
266 return;
267 }
268 }
269
270 guess = lldpd_guess_type(cfg, frame, s);
271 VLOG_DBG("guessed %d enabled:%d", guess, cfg->g_protocols[0].enabled);
272
273 for (i = 0; cfg->g_protocols[i].mode != 0; i++) {
274 if (!cfg->g_protocols[i].enabled) {
275 continue;
276 }
277 if (cfg->g_protocols[i].mode == guess) {
278 VLOG_DBG("using decode function for %s protocol",
279 cfg->g_protocols[i].name);
280 if (cfg->g_protocols[i].decode(cfg, frame, s, hw, &chassis, &port)
281 == -1) {
282 VLOG_DBG("function for %s protocol did not "
283 "decode this frame",
284 cfg->g_protocols[i].name);
285 return;
286 }
287 chassis->c_protocol = port->p_protocol = cfg->g_protocols[i].mode;
288 break;
289 }
290 VLOG_DBG(" %"PRIuSIZE "mode:%d enabled:%d",
291 i, cfg->g_protocols[i].mode, cfg->g_protocols[i].enabled);
292 }
293 if (cfg->g_protocols[i].mode == 0) {
294 VLOG_DBG("unable to guess frame type on %s", hw->h_ifname);
295 return;
296 }
297
298 /* Do we already have the same MSAP somewhere? */
299 VLOG_DBG("search for the same MSAP");
300
135706b3 301 LIST_FOR_EACH (oport, p_entries, &hw->h_rports) {
be53a5c4
DF
302 if (port->p_protocol == oport->p_protocol) {
303 count++;
f94d6e78
BP
304 if (port->p_id_subtype == oport->p_id_subtype &&
305 port->p_id_len == oport->p_id_len &&
306 !memcmp(port->p_id, oport->p_id, port->p_id_len) &&
307 chassis->c_id_subtype == oport->p_chassis->c_id_subtype &&
308 chassis->c_id_len == oport->p_chassis->c_id_len &&
309 !memcmp(chassis->c_id, oport->p_chassis->c_id,
310 chassis->c_id_len)) {
be53a5c4
DF
311 ochassis = oport->p_chassis;
312 VLOG_DBG("MSAP is already known");
8a42c2d6 313 found = true;
be53a5c4
DF
314 break;
315 }
316 }
317 }
318
319 if (!found) {
320 oport = NULL;
321 }
322
323 /* Do we have room for a new MSAP? */
324 if (!oport && cfg->g_config.c_max_neighbors) {
325 if (count == (cfg->g_config.c_max_neighbors - 1)) {
326 VLOG_DBG("max neighbors %d reached for port %s, "
327 "dropping any new ones silently",
328 cfg->g_config.c_max_neighbors,
329 hw->h_ifname);
330 } else if (count > cfg->g_config.c_max_neighbors - 1) {
331 VLOG_DBG("too many neighbors for port %s, drop this new one",
332 hw->h_ifname);
02d593b1
BP
333 lldpd_port_cleanup(port, true);
334 lldpd_chassis_cleanup(chassis, true);
be53a5c4
DF
335 free(port);
336 return;
337 }
338 }
339
340 /* No, but do we already know the system? */
341 if (!oport) {
8a42c2d6 342 bool found = false;
be53a5c4
DF
343 VLOG_DBG("MSAP is unknown, search for the chassis");
344
e87a8e67 345 LIST_FOR_EACH (ochassis, list, &cfg->g_chassis) {
be53a5c4
DF
346 if ((chassis->c_protocol == ochassis->c_protocol) &&
347 (chassis->c_id_subtype == ochassis->c_id_subtype) &&
348 (chassis->c_id_len == ochassis->c_id_len) &&
349 (memcmp(chassis->c_id, ochassis->c_id,
350 chassis->c_id_len) == 0)) {
8a42c2d6 351 found = true;
be53a5c4
DF
352 break;
353 }
354 }
355
356 if (!found) {
357 ochassis = NULL;
358 }
359 }
360
361 if (oport) {
362 /* The port is known, remove it before adding it back */
363 list_remove(&oport->p_entries);
364 lldpd_port_cleanup(oport, 1);
365 free(oport);
366 }
367
368 if (ochassis) {
369 lldpd_move_chassis(ochassis, chassis);
370 chassis = ochassis;
371 } else {
372 /* Chassis not known, add it */
373 VLOG_DBG("unknown chassis, add it to the list");
374 chassis->c_index = ++cfg->g_lastrid;
375 chassis->c_refcount = 0;
e87a8e67
BP
376 list_push_back(&cfg->g_chassis, &chassis->list);
377 listsize = list_size(&cfg->g_chassis);
be53a5c4
DF
378 VLOG_DBG("%"PRIuSIZE " different systems are known", listsize);
379 }
380
381 /* Add port */
d9f3b69e 382 port->p_lastchange = port->p_lastupdate = time_now();
cc841966
BP
383 port->p_lastframe = xmalloc(s + sizeof(struct lldpd_frame));
384 port->p_lastframe->size = s;
385 memcpy(port->p_lastframe->frame, frame, s);
135706b3 386 list_insert(&hw->h_rports, &port->p_entries);
be53a5c4
DF
387
388 port->p_chassis = chassis;
389 port->p_chassis->c_refcount++;
390 /* Several cases are possible :
391 * 1. chassis is new, its refcount was 0. It is now attached
392 * to this port, its refcount is 1.
393 * 2. chassis already exists and was attached to another
394 * port, we increase its refcount accordingly.
395 * 3. chassis already exists and was attached to the same
396 * port, its refcount was decreased with
397 * lldpd_port_cleanup() and is now increased again.
398 *
399 * In all cases, if the port already existed, it has been
400 * freed with lldpd_port_cleanup() and therefore, the refcount
401 * of the chassis that was attached to it is decreased.
402 */
135706b3 403 i = list_size(&hw->h_rports);
be53a5c4
DF
404 VLOG_DBG("%"PRIuSIZE " neighbors for %s", i, hw->h_ifname);
405
406 if (!oport) {
407 hw->h_insert_cnt++;
408 }
409
410 return;
411}
412
413static void
414lldpd_hide_ports(struct lldpd *cfg,
415 struct lldpd_hardware *hw,
416 int mask) {
417 struct lldpd_port *port;
418 int protocols[LLDPD_MODE_MAX + 1];
419 char buffer[256];
8a42c2d6
BP
420 bool found = false;
421 int i, j, k;
be53a5c4
DF
422 unsigned int min;
423
424 VLOG_DBG("apply smart filter for port %s", hw->h_ifname);
425
426 /* Compute the number of occurrences of each protocol */
427 for (i = 0; i <= LLDPD_MODE_MAX; i++) {
428 protocols[i] = 0;
429 }
430
135706b3 431 LIST_FOR_EACH (port, p_entries, &hw->h_rports) {
be53a5c4
DF
432 protocols[port->p_protocol]++;
433 }
434
435 /* Turn the protocols[] array into an array of
436 * enabled/disabled protocols. 1 means enabled, 0
437 * means disabled.
438 */
439 min = (unsigned int) - 1;
440 for (i = 0; i <= LLDPD_MODE_MAX; i++) {
441 if (protocols[i] && (protocols[i] < min)) {
442 min = protocols[i];
443 }
444 }
445 for (i = 0; i <= LLDPD_MODE_MAX; i++) {
f94d6e78 446 if (protocols[i] == min && !found) {
be53a5c4
DF
447 /* If we need a tie breaker, we take the first protocol only */
448 if (cfg->g_config.c_smart & mask &
449 (SMART_OUTGOING_ONE_PROTO | SMART_INCOMING_ONE_PROTO)) {
8a42c2d6 450 found = true;
be53a5c4
DF
451 }
452 protocols[i] = 1;
453 } else {
454 protocols[i] = 0;
455 }
456 }
457
458 /* We set the p_hidden flag to 1 if the protocol is disabled */
135706b3 459 LIST_FOR_EACH (port, p_entries, &hw->h_rports) {
be53a5c4 460 if (mask == SMART_OUTGOING) {
8a42c2d6 461 port->p_hidden_out = protocols[port->p_protocol] ? false : true;
be53a5c4 462 } else {
8a42c2d6 463 port->p_hidden_in = protocols[port->p_protocol] ? false : true;
be53a5c4
DF
464 }
465 }
466
467 /* If we want only one neighbor, we take the first one */
468 if (cfg->g_config.c_smart & mask &
469 (SMART_OUTGOING_ONE_NEIGH | SMART_INCOMING_ONE_NEIGH)) {
8a42c2d6 470 found = false;
be53a5c4 471
135706b3 472 LIST_FOR_EACH (port, p_entries, &hw->h_rports) {
be53a5c4
DF
473 if (mask == SMART_OUTGOING) {
474 if (found) {
8a42c2d6 475 port->p_hidden_out = true;
be53a5c4
DF
476 }
477 if (!port->p_hidden_out) {
8a42c2d6 478 found = true;
be53a5c4
DF
479 }
480 }
481 if (mask == SMART_INCOMING) {
482 if (found) {
8a42c2d6 483 port->p_hidden_in = true;
be53a5c4
DF
484 }
485 if (!port->p_hidden_in) {
8a42c2d6 486 found = true;
be53a5c4
DF
487 }
488 }
489 }
490 }
491
492 /* Print a debug message summarizing the operation */
493 for (i = 0; i <= LLDPD_MODE_MAX; i++) {
494 protocols[i] = 0;
495 }
496
497 k = j = 0;
135706b3 498 LIST_FOR_EACH (port, p_entries, &hw->h_rports) {
f94d6e78
BP
499 if (!((mask == SMART_OUTGOING && port->p_hidden_out) ||
500 (mask == SMART_INCOMING && port->p_hidden_in))) {
be53a5c4
DF
501 k++;
502 protocols[port->p_protocol] = 1;
503 }
504 j++;
505 }
506
507 buffer[0] = '\0';
508 for (i = 0; cfg->g_protocols[i].mode != 0; i++) {
509 if (cfg->g_protocols[i].enabled &&
510 protocols[cfg->g_protocols[i].mode]) {
511 if (strlen(buffer) +
512 strlen(cfg->g_protocols[i].name) + 3 > sizeof(buffer)) {
513 /* Unlikely, our buffer is too small */
514 memcpy(buffer + sizeof(buffer) - 4, "...", 4);
515 break;
516 }
517 if (buffer[0]) {
518 strncat(buffer, ", ", 2);
519 strncat(buffer, cfg->g_protocols[i].name,
520 strlen(cfg->g_protocols[i].name));
521 }
522 }
523 }
524 VLOG_DBG("%s: %s: %d visible neighbors (out of %d)",
525 hw->h_ifname,
526 (mask == SMART_OUTGOING) ? "out filter" : "in filter",
527 k, j);
528 VLOG_DBG("%s: protocols: %s",
529 hw->h_ifname, buffer[0] ? buffer : "(none)");
530}
531
532/* Hide unwanted ports depending on smart mode set by the user */
533static void
534lldpd_hide_all(struct lldpd *cfg)
535{
536 struct lldpd_hardware *hw;
537
538 if (!cfg->g_config.c_smart) {
539 return;
540 }
541
542 VLOG_DBG("apply smart filter results on all ports");
543
2cbb2d90 544 LIST_FOR_EACH (hw, h_entries, &cfg->g_hardware) {
be53a5c4
DF
545 if (cfg->g_config.c_smart & SMART_INCOMING_FILTER) {
546 lldpd_hide_ports(cfg, hw, SMART_INCOMING);
547 }
548 if (cfg->g_config.c_smart & SMART_OUTGOING_FILTER) {
549 lldpd_hide_ports(cfg, hw, SMART_OUTGOING);
550 }
551 }
552}
553
554void
555lldpd_recv(struct lldpd *cfg,
556 struct lldpd_hardware *hw,
557 char *buffer,
558 size_t bufSize)
559{
560 int n = bufSize;
561
562 VLOG_DBG("receive a frame on %s", hw->h_ifname);
563 if (cfg->g_config.c_paused) {
564 VLOG_DBG("paused, ignore the frame on %s", hw->h_ifname);
565 return;
566 }
567 hw->h_rx_cnt++;
568 VLOG_DBG("decode received frame on %s h_rx_cnt=%" PRIu64,
569 hw->h_ifname, hw->h_rx_cnt);
570 lldpd_decode(cfg, buffer, n, hw);
571 lldpd_hide_all(cfg); /* Immediatly hide */
572}
573
574uint32_t
0477baa9 575lldpd_send(struct lldpd_hardware *hw, struct dp_packet *p)
be53a5c4
DF
576{
577 struct lldpd *cfg = hw->h_cfg;
578 struct lldpd_port *port;
579 int i, sent = 0;
580 int lldp_size = 0;
581
582 if (cfg->g_config.c_receiveonly || cfg->g_config.c_paused) {
583 return 0;
584 }
585#ifndef _WIN32
586 if ((hw->h_flags & IFF_RUNNING) == 0) {
587 return 0;
588 }
589#endif
590
591 for (i = 0; cfg->g_protocols[i].mode != 0; i++) {
592 if (!cfg->g_protocols[i].enabled) {
593 continue;
594 }
595
596 /* We send only if we have at least one remote system
597 * speaking this protocol or if the protocol is forced */
598 if (cfg->g_protocols[i].enabled > 1) {
a268e134 599 if ((lldp_size = cfg->g_protocols[i].send(cfg, hw, p)) != -E2BIG) {
be53a5c4
DF
600 sent++;
601 continue;
602 } else {
603 VLOG_DBG("send PDU on %s failed E2BIG", hw->h_ifname);
604 continue;
605 }
606 }
607
135706b3 608 LIST_FOR_EACH (port, p_entries, &hw->h_rports) {
be53a5c4
DF
609 /* If this remote port is disabled, we don't consider it */
610 if (port->p_hidden_out) {
611 continue;
612 }
613 if (port->p_protocol == cfg->g_protocols[i].mode) {
614 VLOG_DBG("send PDU on %s with protocol %s",
615 hw->h_ifname, cfg->g_protocols[i].name);
616 lldp_size = cfg->g_protocols[i].send(cfg, hw, p);
617 sent++;
618 break;
619 }
620 }
621 }
622
623 if (!sent) {
624 /* Nothing was sent for this port, let's speak the first
625 * available protocol.
626 */
627 for (i = 0; cfg->g_protocols[i].mode != 0; i++) {
628 if (!cfg->g_protocols[i].enabled) {
629 continue;
630 }
631 VLOG_DBG("fallback to protocol %s for %s",
632 cfg->g_protocols[i].name, hw->h_ifname);
633 lldp_size = cfg->g_protocols[i].send(cfg, hw, p);
634 break;
635 }
636 if (cfg->g_protocols[i].mode == 0) {
637 VLOG_WARN("no protocol enabled, dunno what to send");
638 }
639 }
640
641 return lldp_size;
642}