]>
git.proxmox.com Git - ovs.git/blob - lib/lldp/lldpd.c
1 /* -*- mode: c; c-file-style: "openbsd" -*- */
3 * Copyright (c) 2015 Nicira, Inc.
4 * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
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.
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.
21 #include <sys/types.h>
22 #include <netinet/in.h>
23 #include <arpa/inet.h>
29 #include <sys/ioctl.h>
30 #include <sys/socket.h>
39 #include <sys/select.h>
40 #include <sys/utsname.h>
43 #include "openvswitch/list.h"
47 VLOG_DEFINE_THIS_MODULE(lldpd
);
49 static struct protocol protos
[] =
51 { LLDPD_MODE_LLDP
, 1, "LLDP", 'l', lldp_send
, lldp_decode
, NULL
,
52 LLDP_MULTICAST_ADDR
},
53 { 0, 0, "any", ' ', NULL
, NULL
, NULL
,
54 { { { 0,0,0,0,0,0 } } } }
57 void lldpd_assign_cfg_to_protocols(struct lldpd
*cfg
)
59 cfg
->g_protocols
= protos
;
62 struct lldpd_hardware
*
63 lldpd_get_hardware(struct lldpd
*cfg
, char *name
, int index
,
64 struct lldpd_ops
*ops
)
66 struct lldpd_hardware
*hw
;
68 LIST_FOR_EACH (hw
, h_entries
, &cfg
->g_hardware
) {
69 if (!strcmp(hw
->h_ifname
, name
) && hw
->h_ifindex
== index
70 && (!ops
|| ops
== hw
->h_ops
)) {
78 struct lldpd_hardware
*
79 lldpd_alloc_hardware(struct lldpd
*cfg
, char *name
, int index
)
81 struct lldpd_hardware
*hw
;
83 VLOG_DBG("allocate a new local hardware interface (%s)", name
);
85 hw
= xzalloc(sizeof *hw
);
87 ovs_strlcpy(hw
->h_ifname
, name
, sizeof hw
->h_ifname
);
88 hw
->h_ifindex
= index
;
89 hw
->h_lport
.p_chassis
= CONTAINER_OF(ovs_list_front(&cfg
->g_chassis
),
90 struct lldpd_chassis
, list
);
91 hw
->h_lport
.p_chassis
->c_refcount
++;
92 ovs_list_init(&hw
->h_rports
);
98 lldpd_alloc_mgmt(int family
, void *addrptr
, size_t addrsize
, u_int32_t iface
)
100 struct lldpd_mgmt
*mgmt
;
102 VLOG_DBG("allocate a new management address (family: %d)", family
);
104 if (family
<= LLDPD_AF_UNSPEC
|| family
>= LLDPD_AF_LAST
) {
105 errno
= EAFNOSUPPORT
;
108 if (addrsize
> LLDPD_MGMT_MAXADDRSIZE
) {
112 mgmt
= xzalloc(sizeof *mgmt
);
113 mgmt
->m_family
= family
;
114 memcpy(&mgmt
->m_addr
, addrptr
, addrsize
);
115 mgmt
->m_addrsize
= addrsize
;
116 mgmt
->m_iface
= iface
;
122 lldpd_hardware_cleanup(struct lldpd
*cfg
, struct lldpd_hardware
*hardware
)
124 VLOG_DBG("cleanup hardware port %s", hardware
->h_ifname
);
126 lldpd_port_cleanup(&hardware
->h_lport
, true);
127 if (hardware
->h_ops
&& hardware
->h_ops
->cleanup
) {
128 hardware
->h_ops
->cleanup(cfg
, hardware
);
134 lldpd_cleanup(struct lldpd
*cfg
)
136 struct lldpd_hardware
*hw
, *hw_next
;
137 struct lldpd_chassis
*chassis
, *chassis_next
;
139 VLOG_DBG("cleanup all ports");
141 LIST_FOR_EACH_SAFE (hw
, hw_next
, h_entries
, &cfg
->g_hardware
) {
143 ovs_list_remove(&hw
->h_entries
);
144 lldpd_remote_cleanup(hw
, NULL
, true);
145 lldpd_hardware_cleanup(cfg
, hw
);
147 lldpd_remote_cleanup(hw
, NULL
, false);
151 VLOG_DBG("cleanup all chassis");
153 LIST_FOR_EACH_SAFE (chassis
, chassis_next
, list
, &cfg
->g_chassis
) {
154 if (chassis
->c_refcount
== 0) {
155 ovs_list_remove(&chassis
->list
);
156 lldpd_chassis_cleanup(chassis
, 1);
161 /* Update chassis `ochassis' with values from `chassis'. The later one is not
162 * expected to be part of a list! It will also be wiped from memory.
165 lldpd_move_chassis(struct lldpd_chassis
*ochassis
,
166 struct lldpd_chassis
*chassis
)
168 struct lldpd_mgmt
*mgmt
;
169 int refcount
= ochassis
->c_refcount
;
170 int index
= ochassis
->c_index
;
171 struct ovs_list listcopy
;
173 /* We want to keep refcount, index and list stuff from the current chassis
175 memcpy(&listcopy
, &ochassis
->list
, sizeof listcopy
);
176 lldpd_chassis_cleanup(ochassis
, 0);
179 /* WARNING: this is a kludgy hack, we need in-place copy and cannot use
182 memcpy(ochassis
, chassis
, sizeof *ochassis
);
183 ovs_list_init(&ochassis
->c_mgmt
);
185 /* Copy of management addresses */
186 LIST_FOR_EACH_POP (mgmt
, m_entries
, &chassis
->c_mgmt
) {
187 ovs_list_insert(&ochassis
->c_mgmt
, &mgmt
->m_entries
);
190 /* Restore saved values */
191 ochassis
->c_refcount
= refcount
;
192 ochassis
->c_index
= index
;
193 memcpy(&ochassis
->list
, &listcopy
, sizeof ochassis
->list
);
195 /* Get rid of the new chassis */
200 lldpd_guess_type(struct lldpd
*cfg
, char *frame
, int s
)
204 if (s
< ETH_ADDR_LEN
) {
208 for (i
= 0; cfg
->g_protocols
[i
].mode
!= 0; i
++) {
209 if (!cfg
->g_protocols
[i
].enabled
) {
212 if (cfg
->g_protocols
[i
].guess
== NULL
) {
213 if (memcmp(frame
, &cfg
->g_protocols
[i
].mac
, ETH_ADDR_LEN
) == 0) {
214 VLOG_DBG("guessed protocol is %s (from MAC address)",
215 cfg
->g_protocols
[i
].name
);
216 return cfg
->g_protocols
[i
].mode
;
219 if (cfg
->g_protocols
[i
].guess(frame
, s
)) {
220 VLOG_DBG("guessed protocol is %s (from detector function)",
221 cfg
->g_protocols
[i
].name
);
222 return cfg
->g_protocols
[i
].mode
;
231 lldpd_decode(struct lldpd
*cfg
, char *frame
, int s
,
232 struct lldpd_hardware
*hw
)
235 struct lldpd_chassis
*chassis
, *ochassis
= NULL
;
236 struct lldpd_port
*port
, *oport
;
237 int guess
= LLDPD_MODE_LLDP
;
238 struct eth_header eheader
;
242 VLOG_DBG("decode a received frame on %s size %d", hw
->h_ifname
,s
);
244 if (s
< sizeof(struct eth_header
) + 4) {
245 /* Too short, just discard it */
249 /* Decapsulate VLAN frames */
250 memcpy(&eheader
, frame
, sizeof eheader
);
251 if (eheader
.eth_type
== htons(ETH_TYPE_VLAN
)) {
252 /* VLAN decapsulation means to shift 4 bytes left the frame from
253 * offset 2 * ETH_ADDR_LEN
255 memmove(frame
+ 2 * ETH_ADDR_LEN
, frame
+ 2 * ETH_ADDR_LEN
+ 4,
256 s
- 2 * ETH_ADDR_LEN
);
260 LIST_FOR_EACH (oport
, p_entries
, &hw
->h_rports
) {
261 if (oport
->p_lastframe
&&
262 oport
->p_lastframe
->size
== s
&&
263 !memcmp(oport
->p_lastframe
->frame
, frame
, s
)) {
264 /* Already received the same frame */
265 VLOG_DBG("duplicate frame, no need to decode");
266 oport
->p_lastupdate
= time_now();
271 guess
= lldpd_guess_type(cfg
, frame
, s
);
272 VLOG_DBG("guessed %d enabled:%d", guess
, cfg
->g_protocols
[0].enabled
);
274 for (i
= 0; cfg
->g_protocols
[i
].mode
!= 0; i
++) {
275 if (!cfg
->g_protocols
[i
].enabled
) {
278 if (cfg
->g_protocols
[i
].mode
== guess
) {
279 VLOG_DBG("using decode function for %s protocol",
280 cfg
->g_protocols
[i
].name
);
281 if (cfg
->g_protocols
[i
].decode(cfg
, frame
, s
, hw
, &chassis
, &port
)
283 VLOG_DBG("function for %s protocol did not "
285 cfg
->g_protocols
[i
].name
);
288 chassis
->c_protocol
= port
->p_protocol
= cfg
->g_protocols
[i
].mode
;
291 VLOG_DBG(" %"PRIuSIZE
"mode:%d enabled:%d",
292 i
, cfg
->g_protocols
[i
].mode
, cfg
->g_protocols
[i
].enabled
);
294 if (cfg
->g_protocols
[i
].mode
== 0) {
295 VLOG_DBG("unable to guess frame type on %s", hw
->h_ifname
);
299 /* Do we already have the same MSAP somewhere? */
300 VLOG_DBG("search for the same MSAP");
302 LIST_FOR_EACH (oport
, p_entries
, &hw
->h_rports
) {
303 if (port
->p_protocol
== oport
->p_protocol
) {
305 if (port
->p_id_subtype
== oport
->p_id_subtype
&&
306 port
->p_id_len
== oport
->p_id_len
&&
307 !memcmp(port
->p_id
, oport
->p_id
, port
->p_id_len
) &&
308 chassis
->c_id_subtype
== oport
->p_chassis
->c_id_subtype
&&
309 chassis
->c_id_len
== oport
->p_chassis
->c_id_len
&&
310 !memcmp(chassis
->c_id
, oport
->p_chassis
->c_id
,
311 chassis
->c_id_len
)) {
312 ochassis
= oport
->p_chassis
;
313 VLOG_DBG("MSAP is already known");
324 /* Do we have room for a new MSAP? */
325 if (!oport
&& cfg
->g_config
.c_max_neighbors
) {
326 if (count
== (cfg
->g_config
.c_max_neighbors
- 1)) {
327 VLOG_DBG("max neighbors %d reached for port %s, "
328 "dropping any new ones silently",
329 cfg
->g_config
.c_max_neighbors
,
331 } else if (count
> cfg
->g_config
.c_max_neighbors
- 1) {
332 VLOG_DBG("too many neighbors for port %s, drop this new one",
334 lldpd_port_cleanup(port
, true);
335 lldpd_chassis_cleanup(chassis
, true);
341 /* No, but do we already know the system? */
344 VLOG_DBG("MSAP is unknown, search for the chassis");
346 LIST_FOR_EACH (ochassis
, list
, &cfg
->g_chassis
) {
347 if ((chassis
->c_protocol
== ochassis
->c_protocol
) &&
348 (chassis
->c_id_subtype
== ochassis
->c_id_subtype
) &&
349 (chassis
->c_id_len
== ochassis
->c_id_len
) &&
350 (memcmp(chassis
->c_id
, ochassis
->c_id
,
351 chassis
->c_id_len
) == 0)) {
363 /* The port is known, remove it before adding it back */
364 ovs_list_remove(&oport
->p_entries
);
365 lldpd_port_cleanup(oport
, 1);
370 lldpd_move_chassis(ochassis
, chassis
);
373 /* Chassis not known, add it */
374 VLOG_DBG("unknown chassis, add it to the list");
375 chassis
->c_index
= ++cfg
->g_lastrid
;
376 chassis
->c_refcount
= 0;
377 ovs_list_push_back(&cfg
->g_chassis
, &chassis
->list
);
378 listsize
= ovs_list_size(&cfg
->g_chassis
);
379 VLOG_DBG("%"PRIuSIZE
" different systems are known", listsize
);
383 port
->p_lastchange
= port
->p_lastupdate
= time_now();
384 port
->p_lastframe
= xmalloc(s
+ sizeof(struct lldpd_frame
));
385 port
->p_lastframe
->size
= s
;
386 memcpy(port
->p_lastframe
->frame
, frame
, s
);
387 ovs_list_insert(&hw
->h_rports
, &port
->p_entries
);
389 port
->p_chassis
= chassis
;
390 port
->p_chassis
->c_refcount
++;
391 /* Several cases are possible :
392 * 1. chassis is new, its refcount was 0. It is now attached
393 * to this port, its refcount is 1.
394 * 2. chassis already exists and was attached to another
395 * port, we increase its refcount accordingly.
396 * 3. chassis already exists and was attached to the same
397 * port, its refcount was decreased with
398 * lldpd_port_cleanup() and is now increased again.
400 * In all cases, if the port already existed, it has been
401 * freed with lldpd_port_cleanup() and therefore, the refcount
402 * of the chassis that was attached to it is decreased.
404 i
= ovs_list_size(&hw
->h_rports
);
405 VLOG_DBG("%"PRIuSIZE
" neighbors for %s", i
, hw
->h_ifname
);
415 lldpd_hide_ports(struct lldpd
*cfg
,
416 struct lldpd_hardware
*hw
,
418 struct lldpd_port
*port
;
419 int protocols
[LLDPD_MODE_MAX
+ 1];
425 VLOG_DBG("apply smart filter for port %s", hw
->h_ifname
);
427 /* Compute the number of occurrences of each protocol */
428 for (i
= 0; i
<= LLDPD_MODE_MAX
; i
++) {
432 LIST_FOR_EACH (port
, p_entries
, &hw
->h_rports
) {
433 protocols
[port
->p_protocol
]++;
436 /* Turn the protocols[] array into an array of
437 * enabled/disabled protocols. 1 means enabled, 0
440 min
= (unsigned int) - 1;
441 for (i
= 0; i
<= LLDPD_MODE_MAX
; i
++) {
442 if (protocols
[i
] && (protocols
[i
] < min
)) {
446 for (i
= 0; i
<= LLDPD_MODE_MAX
; i
++) {
447 if (protocols
[i
] == min
&& !found
) {
448 /* If we need a tie breaker, we take the first protocol only */
449 if (cfg
->g_config
.c_smart
& mask
&
450 (SMART_OUTGOING_ONE_PROTO
| SMART_INCOMING_ONE_PROTO
)) {
459 /* We set the p_hidden flag to 1 if the protocol is disabled */
460 LIST_FOR_EACH (port
, p_entries
, &hw
->h_rports
) {
461 if (mask
== SMART_OUTGOING
) {
462 port
->p_hidden_out
= protocols
[port
->p_protocol
] ? false : true;
464 port
->p_hidden_in
= protocols
[port
->p_protocol
] ? false : true;
468 /* If we want only one neighbor, we take the first one */
469 if (cfg
->g_config
.c_smart
& mask
&
470 (SMART_OUTGOING_ONE_NEIGH
| SMART_INCOMING_ONE_NEIGH
)) {
473 LIST_FOR_EACH (port
, p_entries
, &hw
->h_rports
) {
474 if (mask
== SMART_OUTGOING
) {
476 port
->p_hidden_out
= true;
478 if (!port
->p_hidden_out
) {
482 if (mask
== SMART_INCOMING
) {
484 port
->p_hidden_in
= true;
486 if (!port
->p_hidden_in
) {
493 /* Print a debug message summarizing the operation */
494 for (i
= 0; i
<= LLDPD_MODE_MAX
; i
++) {
499 LIST_FOR_EACH (port
, p_entries
, &hw
->h_rports
) {
500 if (!((mask
== SMART_OUTGOING
&& port
->p_hidden_out
) ||
501 (mask
== SMART_INCOMING
&& port
->p_hidden_in
))) {
503 protocols
[port
->p_protocol
] = 1;
509 for (i
= 0; cfg
->g_protocols
[i
].mode
!= 0; i
++) {
510 if (cfg
->g_protocols
[i
].enabled
&&
511 protocols
[cfg
->g_protocols
[i
].mode
]) {
513 strlen(cfg
->g_protocols
[i
].name
) + 3 > sizeof(buffer
)) {
514 /* Unlikely, our buffer is too small */
515 memcpy(buffer
+ sizeof(buffer
) - 4, "...", 4);
519 strncat(buffer
, ", ", 2);
520 strncat(buffer
, cfg
->g_protocols
[i
].name
,
521 strlen(cfg
->g_protocols
[i
].name
));
525 VLOG_DBG("%s: %s: %d visible neighbors (out of %d)",
527 (mask
== SMART_OUTGOING
) ? "out filter" : "in filter",
529 VLOG_DBG("%s: protocols: %s",
530 hw
->h_ifname
, buffer
[0] ? buffer
: "(none)");
533 /* Hide unwanted ports depending on smart mode set by the user */
535 lldpd_hide_all(struct lldpd
*cfg
)
537 struct lldpd_hardware
*hw
;
539 if (!cfg
->g_config
.c_smart
) {
543 VLOG_DBG("apply smart filter results on all ports");
545 LIST_FOR_EACH (hw
, h_entries
, &cfg
->g_hardware
) {
546 if (cfg
->g_config
.c_smart
& SMART_INCOMING_FILTER
) {
547 lldpd_hide_ports(cfg
, hw
, SMART_INCOMING
);
549 if (cfg
->g_config
.c_smart
& SMART_OUTGOING_FILTER
) {
550 lldpd_hide_ports(cfg
, hw
, SMART_OUTGOING
);
556 lldpd_recv(struct lldpd
*cfg
,
557 struct lldpd_hardware
*hw
,
563 VLOG_DBG("receive a frame on %s", hw
->h_ifname
);
564 if (cfg
->g_config
.c_paused
) {
565 VLOG_DBG("paused, ignore the frame on %s", hw
->h_ifname
);
569 VLOG_DBG("decode received frame on %s h_rx_cnt=%" PRIu64
,
570 hw
->h_ifname
, hw
->h_rx_cnt
);
571 lldpd_decode(cfg
, buffer
, n
, hw
);
572 lldpd_hide_all(cfg
); /* Immediatly hide */
576 lldpd_send(struct lldpd_hardware
*hw
, struct dp_packet
*p
)
578 struct lldpd
*cfg
= hw
->h_cfg
;
579 struct lldpd_port
*port
;
583 if (cfg
->g_config
.c_receiveonly
|| cfg
->g_config
.c_paused
) {
587 if ((hw
->h_flags
& IFF_RUNNING
) == 0) {
592 for (i
= 0; cfg
->g_protocols
[i
].mode
!= 0; i
++) {
593 if (!cfg
->g_protocols
[i
].enabled
) {
597 /* We send only if we have at least one remote system
598 * speaking this protocol or if the protocol is forced */
599 if (cfg
->g_protocols
[i
].enabled
> 1) {
600 if ((lldp_size
= cfg
->g_protocols
[i
].send(cfg
, hw
, p
)) != -E2BIG
) {
604 VLOG_DBG("send PDU on %s failed E2BIG", hw
->h_ifname
);
609 LIST_FOR_EACH (port
, p_entries
, &hw
->h_rports
) {
610 /* If this remote port is disabled, we don't consider it */
611 if (port
->p_hidden_out
) {
614 if (port
->p_protocol
== cfg
->g_protocols
[i
].mode
) {
615 VLOG_DBG("send PDU on %s with protocol %s",
616 hw
->h_ifname
, cfg
->g_protocols
[i
].name
);
617 lldp_size
= cfg
->g_protocols
[i
].send(cfg
, hw
, p
);
625 /* Nothing was sent for this port, let's speak the first
626 * available protocol.
628 for (i
= 0; cfg
->g_protocols
[i
].mode
!= 0; i
++) {
629 if (!cfg
->g_protocols
[i
].enabled
) {
632 VLOG_DBG("fallback to protocol %s for %s",
633 cfg
->g_protocols
[i
].name
, hw
->h_ifname
);
634 lldp_size
= cfg
->g_protocols
[i
].send(cfg
, hw
, p
);
637 if (cfg
->g_protocols
[i
].mode
== 0) {
638 VLOG_WARN("no protocol enabled, dunno what to send");