1 /* -*- mode: c; c-file-style: "openbsd" -*- */
3 * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 #include <arpa/inet.h>
26 #include <sys/ioctl.h>
27 #include <sys/socket.h>
30 #include <sys/types.h>
37 #include <netinet/if_ether.h>
39 #include <sys/select.h>
40 #include <sys/utsname.h>
46 VLOG_DEFINE_THIS_MODULE(lldpd);
48 static struct protocol protos[] =
50 { LLDPD_MODE_LLDP, 1, "LLDP", 'l', lldp_send, lldp_decode, NULL,
51 LLDP_MULTICAST_ADDR },
52 { 0, 0, "any", ' ', NULL, NULL, NULL,
56 void lldpd_assign_cfg_to_protocols(struct lldpd *cfg)
58 cfg->g_protocols = protos;
61 struct lldpd_hardware *
62 lldpd_get_hardware(struct lldpd *cfg, char *name, int index,
63 struct lldpd_ops *ops)
65 struct lldpd_hardware *hw;
67 LIST_FOR_EACH (hw, h_entries, &cfg->g_hardware.h_entries) {
68 if ((strcmp(hw->h_ifname, name) == 0) &&
69 (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 if ((hw = (struct lldpd_hardware *) calloc(1, sizeof *hw)) == NULL) {
90 ovs_strlcpy(hw->h_ifname, name, sizeof hw->h_ifname);
91 hw->h_ifindex = index;
92 hw->h_lport.p_chassis = (struct lldpd_chassis *)
93 list_front(&cfg->g_chassis.list);
94 hw->h_lport.p_chassis->c_refcount++;
95 list_init(&hw->h_rports.p_entries);
101 lldpd_alloc_mgmt(int family, void *addrptr, size_t addrsize, u_int32_t iface)
103 struct lldpd_mgmt *mgmt;
105 VLOG_DBG("allocate a new management address (family: %d)", family);
107 if (family <= LLDPD_AF_UNSPEC || family >= LLDPD_AF_LAST) {
108 errno = EAFNOSUPPORT;
111 if (addrsize > LLDPD_MGMT_MAXADDRSIZE) {
115 mgmt = calloc(1, sizeof *mgmt);
120 mgmt->m_family = family;
121 memcpy(&mgmt->m_addr, addrptr, addrsize);
122 mgmt->m_addrsize = addrsize;
123 mgmt->m_iface = iface;
129 lldpd_hardware_cleanup(struct lldpd *cfg, struct lldpd_hardware *hardware)
131 VLOG_DBG("cleanup hardware port %s", hardware->h_ifname);
133 lldpd_port_cleanup(&hardware->h_lport, 1);
134 if (hardware->h_ops && hardware->h_ops->cleanup) {
135 hardware->h_ops->cleanup(cfg, hardware);
141 lldpd_cleanup(struct lldpd *cfg)
143 struct lldpd_hardware *hw, *hw_next;
144 struct lldpd_chassis *chassis, *chassis_next;
146 VLOG_DBG("cleanup all ports");
148 LIST_FOR_EACH_SAFE (hw, hw_next, h_entries, &cfg->g_hardware.h_entries) {
150 list_remove(&hw->h_entries);
151 lldpd_remote_cleanup(hw, NULL, 1);
152 lldpd_hardware_cleanup(cfg, hw);
154 lldpd_remote_cleanup(hw, NULL, 0);
158 VLOG_DBG("cleanup all chassis");
160 LIST_FOR_EACH_SAFE (chassis, chassis_next, list, &cfg->g_chassis.list) {
161 if (chassis->c_refcount == 0) {
162 list_remove(&chassis->list);
163 lldpd_chassis_cleanup(chassis, 1);
168 /* Update chassis `ochassis' with values from `chassis'. The later one is not
169 * expected to be part of a list! It will also be wiped from memory.
172 lldpd_move_chassis(struct lldpd_chassis *ochassis,
173 struct lldpd_chassis *chassis)
175 struct lldpd_mgmt *mgmt, *mgmt_next;
176 int refcount = ochassis->c_refcount;
177 int index = ochassis->c_index;
178 struct ovs_list listcopy;
180 /* We want to keep refcount, index and list stuff from the current chassis
182 memcpy(&listcopy, &ochassis->list, sizeof listcopy);
183 lldpd_chassis_cleanup(ochassis, 0);
186 /* WARNING: this is a kludgy hack, we need in-place copy and cannot use
189 memcpy(ochassis, chassis, sizeof *ochassis);
190 list_init(&ochassis->c_mgmt.m_entries);
192 /* Copy of management addresses */
193 LIST_FOR_EACH_SAFE (mgmt,
196 &chassis->c_mgmt.m_entries) {
197 list_remove(&mgmt->m_entries);
198 list_insert(&ochassis->c_mgmt.m_entries, &mgmt->m_entries);
201 /* Restore saved values */
202 ochassis->c_refcount = refcount;
203 ochassis->c_index = index;
204 memcpy(&ochassis->list, &listcopy, sizeof ochassis->list);
206 /* Get rid of the new chassis */
211 lldpd_guess_type(struct lldpd *cfg, char *frame, int s)
215 if (s < ETH_ADDR_LEN) {
219 for (i = 0; cfg->g_protocols[i].mode != 0; i++) {
220 if (!cfg->g_protocols[i].enabled) {
223 if (cfg->g_protocols[i].guess == NULL) {
224 if (memcmp(frame, cfg->g_protocols[i].mac, ETH_ADDR_LEN) == 0) {
225 VLOG_DBG("guessed protocol is %s (from MAC address)",
226 cfg->g_protocols[i].name);
227 return cfg->g_protocols[i].mode;
230 if (cfg->g_protocols[i].guess(frame, s)) {
231 VLOG_DBG("guessed protocol is %s (from detector function)",
232 cfg->g_protocols[i].name);
233 return cfg->g_protocols[i].mode;
242 lldpd_decode(struct lldpd *cfg, char *frame, int s,
243 struct lldpd_hardware *hw)
246 struct lldpd_chassis *chassis, *ochassis = NULL;
247 struct lldpd_port *port, *oport;
248 int guess = LLDPD_MODE_LLDP;
249 struct eth_header eheader;
253 VLOG_DBG("decode a received frame on %s size %d", hw->h_ifname,s);
255 if (s < sizeof(struct eth_header) + 4) {
256 /* Too short, just discard it */
260 /* Decapsulate VLAN frames */
261 memcpy(&eheader, frame, sizeof eheader);
262 if (eheader.eth_type == htons(ETH_TYPE_VLAN)) {
263 /* VLAN decapsulation means to shift 4 bytes left the frame from
264 * offset 2 * ETH_ADDR_LEN
266 memmove(frame + 2 * ETH_ADDR_LEN, frame + 2 * ETH_ADDR_LEN + 4,
267 s - 2 * ETH_ADDR_LEN);
271 LIST_FOR_EACH (oport, p_entries, &hw->h_rports.p_entries) {
272 if ((oport->p_lastframe != NULL) &&
273 (oport->p_lastframe->size == s) &&
274 (memcmp(oport->p_lastframe->frame, frame, s) == 0)) {
275 /* Already received the same frame */
276 VLOG_DBG("duplicate frame, no need to decode");
277 oport->p_lastupdate = time(NULL);
282 guess = lldpd_guess_type(cfg, frame, s);
283 VLOG_DBG("guessed %d enabled:%d", guess, cfg->g_protocols[0].enabled);
285 for (i = 0; cfg->g_protocols[i].mode != 0; i++) {
286 if (!cfg->g_protocols[i].enabled) {
289 if (cfg->g_protocols[i].mode == guess) {
290 VLOG_DBG("using decode function for %s protocol",
291 cfg->g_protocols[i].name);
292 if (cfg->g_protocols[i].decode(cfg, frame, s, hw, &chassis, &port)
294 VLOG_DBG("function for %s protocol did not "
296 cfg->g_protocols[i].name);
299 chassis->c_protocol = port->p_protocol = cfg->g_protocols[i].mode;
302 VLOG_DBG(" %"PRIuSIZE "mode:%d enabled:%d",
303 i, cfg->g_protocols[i].mode, cfg->g_protocols[i].enabled);
305 if (cfg->g_protocols[i].mode == 0) {
306 VLOG_DBG("unable to guess frame type on %s", hw->h_ifname);
310 /* Do we already have the same MSAP somewhere? */
311 VLOG_DBG("search for the same MSAP");
313 LIST_FOR_EACH (oport, p_entries, &hw->h_rports.p_entries) {
314 if (port->p_protocol == oport->p_protocol) {
316 if ((port->p_id_subtype == oport->p_id_subtype) &&
317 (port->p_id_len == oport->p_id_len) &&
318 (memcmp(port->p_id, oport->p_id, port->p_id_len) == 0) &&
319 (chassis->c_id_subtype == oport->p_chassis->c_id_subtype) &&
320 (chassis->c_id_len == oport->p_chassis->c_id_len) &&
321 (memcmp(chassis->c_id, oport->p_chassis->c_id,
322 chassis->c_id_len) == 0)) {
323 ochassis = oport->p_chassis;
324 VLOG_DBG("MSAP is already known");
335 /* Do we have room for a new MSAP? */
336 if (!oport && cfg->g_config.c_max_neighbors) {
337 if (count == (cfg->g_config.c_max_neighbors - 1)) {
338 VLOG_DBG("max neighbors %d reached for port %s, "
339 "dropping any new ones silently",
340 cfg->g_config.c_max_neighbors,
342 } else if (count > cfg->g_config.c_max_neighbors - 1) {
343 VLOG_DBG("too many neighbors for port %s, drop this new one",
345 lldpd_port_cleanup(port, 1);
346 lldpd_chassis_cleanup(chassis, 1);
352 /* No, but do we already know the system? */
355 VLOG_DBG("MSAP is unknown, search for the chassis");
357 LIST_FOR_EACH (ochassis, list, &cfg->g_chassis.list) {
358 if ((chassis->c_protocol == ochassis->c_protocol) &&
359 (chassis->c_id_subtype == ochassis->c_id_subtype) &&
360 (chassis->c_id_len == ochassis->c_id_len) &&
361 (memcmp(chassis->c_id, ochassis->c_id,
362 chassis->c_id_len) == 0)) {
374 /* The port is known, remove it before adding it back */
375 list_remove(&oport->p_entries);
376 lldpd_port_cleanup(oport, 1);
381 lldpd_move_chassis(ochassis, chassis);
384 /* Chassis not known, add it */
385 VLOG_DBG("unknown chassis, add it to the list");
386 chassis->c_index = ++cfg->g_lastrid;
387 chassis->c_refcount = 0;
388 list_push_back(&cfg->g_chassis.list, &chassis->list);
389 listsize = list_size(&cfg->g_chassis.list);
390 VLOG_DBG("%"PRIuSIZE " different systems are known", listsize);
394 port->p_lastchange = port->p_lastupdate = time(NULL);
395 if ((port->p_lastframe = malloc(s + sizeof(struct lldpd_frame))) != NULL) {
396 port->p_lastframe->size = s;
397 memcpy(port->p_lastframe->frame, frame, s);
399 list_insert(&hw->h_rports.p_entries, &port->p_entries);
401 port->p_chassis = chassis;
402 port->p_chassis->c_refcount++;
403 /* Several cases are possible :
404 * 1. chassis is new, its refcount was 0. It is now attached
405 * to this port, its refcount is 1.
406 * 2. chassis already exists and was attached to another
407 * port, we increase its refcount accordingly.
408 * 3. chassis already exists and was attached to the same
409 * port, its refcount was decreased with
410 * lldpd_port_cleanup() and is now increased again.
412 * In all cases, if the port already existed, it has been
413 * freed with lldpd_port_cleanup() and therefore, the refcount
414 * of the chassis that was attached to it is decreased.
416 /* coverity[use_after_free] TAILQ_REMOVE does the right thing */
417 i = list_size((struct ovs_list *) &hw->h_rports);
418 VLOG_DBG("%"PRIuSIZE " neighbors for %s", i, hw->h_ifname);
428 lldpd_hide_ports(struct lldpd *cfg,
429 struct lldpd_hardware *hw,
431 struct lldpd_port *port;
432 int protocols[LLDPD_MODE_MAX + 1];
434 int i, j, k, found = 0;
437 VLOG_DBG("apply smart filter for port %s", hw->h_ifname);
439 /* Compute the number of occurrences of each protocol */
440 for (i = 0; i <= LLDPD_MODE_MAX; i++) {
444 LIST_FOR_EACH (port, p_entries, &hw->h_rports.p_entries) {
445 protocols[port->p_protocol]++;
448 /* Turn the protocols[] array into an array of
449 * enabled/disabled protocols. 1 means enabled, 0
452 min = (unsigned int) - 1;
453 for (i = 0; i <= LLDPD_MODE_MAX; i++) {
454 if (protocols[i] && (protocols[i] < min)) {
458 for (i = 0; i <= LLDPD_MODE_MAX; i++) {
459 if ((protocols[i] == min) && !found) {
460 /* If we need a tie breaker, we take the first protocol only */
461 if (cfg->g_config.c_smart & mask &
462 (SMART_OUTGOING_ONE_PROTO | SMART_INCOMING_ONE_PROTO)) {
471 /* We set the p_hidden flag to 1 if the protocol is disabled */
472 LIST_FOR_EACH (port, p_entries, &hw->h_rports.p_entries) {
473 if (mask == SMART_OUTGOING) {
474 port->p_hidden_out = protocols[port->p_protocol] ? 0 : 1;
476 port->p_hidden_in = protocols[port->p_protocol] ? 0 : 1;
480 /* If we want only one neighbor, we take the first one */
481 if (cfg->g_config.c_smart & mask &
482 (SMART_OUTGOING_ONE_NEIGH | SMART_INCOMING_ONE_NEIGH)) {
485 LIST_FOR_EACH (port, p_entries, &hw->h_rports.p_entries) {
486 if (mask == SMART_OUTGOING) {
488 port->p_hidden_out = 1;
490 if (!port->p_hidden_out) {
494 if (mask == SMART_INCOMING) {
496 port->p_hidden_in = 1;
498 if (!port->p_hidden_in) {
505 /* Print a debug message summarizing the operation */
506 for (i = 0; i <= LLDPD_MODE_MAX; i++) {
511 LIST_FOR_EACH (port, p_entries, &hw->h_rports.p_entries) {
512 if (!(((mask == SMART_OUTGOING) && port->p_hidden_out) ||
513 ((mask == SMART_INCOMING) && port->p_hidden_in))) {
515 protocols[port->p_protocol] = 1;
521 for (i = 0; cfg->g_protocols[i].mode != 0; i++) {
522 if (cfg->g_protocols[i].enabled &&
523 protocols[cfg->g_protocols[i].mode]) {
525 strlen(cfg->g_protocols[i].name) + 3 > sizeof(buffer)) {
526 /* Unlikely, our buffer is too small */
527 memcpy(buffer + sizeof(buffer) - 4, "...", 4);
531 strncat(buffer, ", ", 2);
532 strncat(buffer, cfg->g_protocols[i].name,
533 strlen(cfg->g_protocols[i].name));
537 VLOG_DBG("%s: %s: %d visible neighbors (out of %d)",
539 (mask == SMART_OUTGOING) ? "out filter" : "in filter",
541 VLOG_DBG("%s: protocols: %s",
542 hw->h_ifname, buffer[0] ? buffer : "(none)");
545 /* Hide unwanted ports depending on smart mode set by the user */
547 lldpd_hide_all(struct lldpd *cfg)
549 struct lldpd_hardware *hw;
551 if (!cfg->g_config.c_smart) {
555 VLOG_DBG("apply smart filter results on all ports");
557 LIST_FOR_EACH (hw, h_entries, &cfg->g_hardware.h_entries) {
558 if (cfg->g_config.c_smart & SMART_INCOMING_FILTER) {
559 lldpd_hide_ports(cfg, hw, SMART_INCOMING);
561 if (cfg->g_config.c_smart & SMART_OUTGOING_FILTER) {
562 lldpd_hide_ports(cfg, hw, SMART_OUTGOING);
568 lldpd_recv(struct lldpd *cfg,
569 struct lldpd_hardware *hw,
575 VLOG_DBG("receive a frame on %s", hw->h_ifname);
576 if (cfg->g_config.c_paused) {
577 VLOG_DBG("paused, ignore the frame on %s", hw->h_ifname);
581 VLOG_DBG("decode received frame on %s h_rx_cnt=%" PRIu64,
582 hw->h_ifname, hw->h_rx_cnt);
583 lldpd_decode(cfg, buffer, n, hw);
584 lldpd_hide_all(cfg); /* Immediatly hide */
588 lldpd_send(struct lldpd_hardware *hw, struct dp_packet *p)
590 struct lldpd *cfg = hw->h_cfg;
591 struct lldpd_port *port;
595 if (cfg->g_config.c_receiveonly || cfg->g_config.c_paused) {
599 if ((hw->h_flags & IFF_RUNNING) == 0) {
604 for (i = 0; cfg->g_protocols[i].mode != 0; i++) {
605 if (!cfg->g_protocols[i].enabled) {
609 /* We send only if we have at least one remote system
610 * speaking this protocol or if the protocol is forced */
611 if (cfg->g_protocols[i].enabled > 1) {
612 if ((lldp_size = cfg->g_protocols[i].send(cfg, hw, p)) != E2BIG) {
616 VLOG_DBG("send PDU on %s failed E2BIG", hw->h_ifname);
621 LIST_FOR_EACH (port, p_entries, &hw->h_rports.p_entries) {
622 /* If this remote port is disabled, we don't consider it */
623 if (port->p_hidden_out) {
626 if (port->p_protocol == cfg->g_protocols[i].mode) {
627 VLOG_DBG("send PDU on %s with protocol %s",
628 hw->h_ifname, cfg->g_protocols[i].name);
629 lldp_size = cfg->g_protocols[i].send(cfg, hw, p);
637 /* Nothing was sent for this port, let's speak the first
638 * available protocol.
640 for (i = 0; cfg->g_protocols[i].mode != 0; i++) {
641 if (!cfg->g_protocols[i].enabled) {
644 VLOG_DBG("fallback to protocol %s for %s",
645 cfg->g_protocols[i].name, hw->h_ifname);
646 lldp_size = cfg->g_protocols[i].send(cfg, hw, p);
649 if (cfg->g_protocols[i].mode == 0) {
650 VLOG_WARN("no protocol enabled, dunno what to send");