2015-02-20 14:17:09 -05:00
|
|
|
/* -*- mode: c; c-file-style: "openbsd" -*- */
|
|
|
|
/*
|
2015-03-03 15:30:46 -08:00
|
|
|
* Copyright (c) 2015 Nicira, Inc.
|
2015-02-20 14:17:09 -05:00
|
|
|
* Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
|
|
|
|
*
|
|
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
|
|
* copyright notice and this permission notice appear in all copies.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
#include "lldpd.h"
|
|
|
|
#include <arpa/inet.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <inttypes.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/wait.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#ifndef _WIN32
|
|
|
|
#include <grp.h>
|
|
|
|
#include <libgen.h>
|
|
|
|
#include <pwd.h>
|
|
|
|
#include <sys/select.h>
|
|
|
|
#include <sys/utsname.h>
|
|
|
|
#endif
|
|
|
|
#include "compiler.h"
|
2016-03-25 14:10:21 -07:00
|
|
|
#include "openvswitch/list.h"
|
2015-02-20 14:17:09 -05:00
|
|
|
#include "packets.h"
|
2015-03-03 15:34:07 -08:00
|
|
|
#include "timeval.h"
|
2015-02-20 14:17:09 -05:00
|
|
|
|
|
|
|
VLOG_DEFINE_THIS_MODULE(lldpd);
|
|
|
|
|
|
|
|
static struct protocol protos[] =
|
|
|
|
{
|
|
|
|
{ LLDPD_MODE_LLDP, 1, "LLDP", 'l', lldp_send, lldp_decode, NULL,
|
|
|
|
LLDP_MULTICAST_ADDR },
|
|
|
|
{ 0, 0, "any", ' ', NULL, NULL, NULL,
|
2015-08-28 14:55:11 -07:00
|
|
|
{ { { 0,0,0,0,0,0 } } } }
|
2015-02-20 14:17:09 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
void lldpd_assign_cfg_to_protocols(struct lldpd *cfg)
|
|
|
|
{
|
|
|
|
cfg->g_protocols = protos;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct lldpd_hardware *
|
|
|
|
lldpd_get_hardware(struct lldpd *cfg, char *name, int index,
|
|
|
|
struct lldpd_ops *ops)
|
|
|
|
{
|
|
|
|
struct lldpd_hardware *hw;
|
|
|
|
|
2015-02-22 16:55:34 -08:00
|
|
|
LIST_FOR_EACH (hw, h_entries, &cfg->g_hardware) {
|
2015-02-22 16:35:43 -08:00
|
|
|
if (!strcmp(hw->h_ifname, name) && hw->h_ifindex == index
|
|
|
|
&& (!ops || ops == hw->h_ops)) {
|
2015-02-20 14:17:09 -05:00
|
|
|
return hw;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct lldpd_hardware *
|
|
|
|
lldpd_alloc_hardware(struct lldpd *cfg, char *name, int index)
|
|
|
|
{
|
|
|
|
struct lldpd_hardware *hw;
|
|
|
|
|
|
|
|
VLOG_DBG("allocate a new local hardware interface (%s)", name);
|
|
|
|
|
2015-03-03 15:30:46 -08:00
|
|
|
hw = xzalloc(sizeof *hw);
|
2015-02-20 14:17:09 -05:00
|
|
|
hw->h_cfg = cfg;
|
|
|
|
ovs_strlcpy(hw->h_ifname, name, sizeof hw->h_ifname);
|
|
|
|
hw->h_ifindex = index;
|
2016-03-25 14:10:22 -07:00
|
|
|
hw->h_lport.p_chassis = CONTAINER_OF(ovs_list_front(&cfg->g_chassis),
|
2015-02-22 16:19:27 -08:00
|
|
|
struct lldpd_chassis, list);
|
2015-02-20 14:17:09 -05:00
|
|
|
hw->h_lport.p_chassis->c_refcount++;
|
2016-03-25 14:10:22 -07:00
|
|
|
ovs_list_init(&hw->h_rports);
|
2015-02-20 14:17:09 -05:00
|
|
|
|
|
|
|
return hw;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct lldpd_mgmt *
|
|
|
|
lldpd_alloc_mgmt(int family, void *addrptr, size_t addrsize, u_int32_t iface)
|
|
|
|
{
|
|
|
|
struct lldpd_mgmt *mgmt;
|
|
|
|
|
|
|
|
VLOG_DBG("allocate a new management address (family: %d)", family);
|
|
|
|
|
|
|
|
if (family <= LLDPD_AF_UNSPEC || family >= LLDPD_AF_LAST) {
|
|
|
|
errno = EAFNOSUPPORT;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (addrsize > LLDPD_MGMT_MAXADDRSIZE) {
|
|
|
|
errno = EOVERFLOW;
|
|
|
|
return NULL;
|
|
|
|
}
|
2015-03-03 15:30:46 -08:00
|
|
|
mgmt = xzalloc(sizeof *mgmt);
|
2015-02-20 14:17:09 -05:00
|
|
|
mgmt->m_family = family;
|
|
|
|
memcpy(&mgmt->m_addr, addrptr, addrsize);
|
|
|
|
mgmt->m_addrsize = addrsize;
|
|
|
|
mgmt->m_iface = iface;
|
|
|
|
|
|
|
|
return mgmt;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
lldpd_hardware_cleanup(struct lldpd *cfg, struct lldpd_hardware *hardware)
|
|
|
|
{
|
|
|
|
VLOG_DBG("cleanup hardware port %s", hardware->h_ifname);
|
|
|
|
|
2015-02-22 12:45:17 -08:00
|
|
|
lldpd_port_cleanup(&hardware->h_lport, true);
|
2015-02-20 14:17:09 -05:00
|
|
|
if (hardware->h_ops && hardware->h_ops->cleanup) {
|
|
|
|
hardware->h_ops->cleanup(cfg, hardware);
|
|
|
|
}
|
|
|
|
free(hardware);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
lldpd_cleanup(struct lldpd *cfg)
|
|
|
|
{
|
|
|
|
struct lldpd_hardware *hw, *hw_next;
|
|
|
|
struct lldpd_chassis *chassis, *chassis_next;
|
|
|
|
|
|
|
|
VLOG_DBG("cleanup all ports");
|
|
|
|
|
2015-02-22 16:55:34 -08:00
|
|
|
LIST_FOR_EACH_SAFE (hw, hw_next, h_entries, &cfg->g_hardware) {
|
2015-02-20 14:17:09 -05:00
|
|
|
if (!hw->h_flags) {
|
2016-03-25 14:10:22 -07:00
|
|
|
ovs_list_remove(&hw->h_entries);
|
2015-02-22 12:45:17 -08:00
|
|
|
lldpd_remote_cleanup(hw, NULL, true);
|
2015-02-20 14:17:09 -05:00
|
|
|
lldpd_hardware_cleanup(cfg, hw);
|
|
|
|
} else {
|
2015-02-22 12:45:17 -08:00
|
|
|
lldpd_remote_cleanup(hw, NULL, false);
|
2015-02-20 14:17:09 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
VLOG_DBG("cleanup all chassis");
|
|
|
|
|
2015-02-22 16:47:01 -08:00
|
|
|
LIST_FOR_EACH_SAFE (chassis, chassis_next, list, &cfg->g_chassis) {
|
2015-02-20 14:17:09 -05:00
|
|
|
if (chassis->c_refcount == 0) {
|
2016-03-25 14:10:22 -07:00
|
|
|
ovs_list_remove(&chassis->list);
|
2015-02-20 14:17:09 -05:00
|
|
|
lldpd_chassis_cleanup(chassis, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Update chassis `ochassis' with values from `chassis'. The later one is not
|
|
|
|
* expected to be part of a list! It will also be wiped from memory.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
lldpd_move_chassis(struct lldpd_chassis *ochassis,
|
|
|
|
struct lldpd_chassis *chassis)
|
|
|
|
{
|
2015-04-06 14:02:28 -07:00
|
|
|
struct lldpd_mgmt *mgmt;
|
2015-02-20 14:17:09 -05:00
|
|
|
int refcount = ochassis->c_refcount;
|
|
|
|
int index = ochassis->c_index;
|
|
|
|
struct ovs_list listcopy;
|
|
|
|
|
|
|
|
/* We want to keep refcount, index and list stuff from the current chassis
|
|
|
|
*/
|
|
|
|
memcpy(&listcopy, &ochassis->list, sizeof listcopy);
|
|
|
|
lldpd_chassis_cleanup(ochassis, 0);
|
|
|
|
|
|
|
|
/* Make the copy. */
|
|
|
|
/* WARNING: this is a kludgy hack, we need in-place copy and cannot use
|
|
|
|
* marshaling.
|
|
|
|
*/
|
|
|
|
memcpy(ochassis, chassis, sizeof *ochassis);
|
2016-03-25 14:10:22 -07:00
|
|
|
ovs_list_init(&ochassis->c_mgmt);
|
2015-02-20 14:17:09 -05:00
|
|
|
|
|
|
|
/* Copy of management addresses */
|
2015-04-06 14:02:28 -07:00
|
|
|
LIST_FOR_EACH_POP (mgmt, m_entries, &chassis->c_mgmt) {
|
2016-03-25 14:10:22 -07:00
|
|
|
ovs_list_insert(&ochassis->c_mgmt, &mgmt->m_entries);
|
2015-02-20 14:17:09 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Restore saved values */
|
|
|
|
ochassis->c_refcount = refcount;
|
|
|
|
ochassis->c_index = index;
|
|
|
|
memcpy(&ochassis->list, &listcopy, sizeof ochassis->list);
|
|
|
|
|
|
|
|
/* Get rid of the new chassis */
|
|
|
|
free(chassis);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
lldpd_guess_type(struct lldpd *cfg, char *frame, int s)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (s < ETH_ADDR_LEN) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; cfg->g_protocols[i].mode != 0; i++) {
|
|
|
|
if (!cfg->g_protocols[i].enabled) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (cfg->g_protocols[i].guess == NULL) {
|
2015-08-28 14:55:11 -07:00
|
|
|
if (memcmp(frame, &cfg->g_protocols[i].mac, ETH_ADDR_LEN) == 0) {
|
2015-02-20 14:17:09 -05:00
|
|
|
VLOG_DBG("guessed protocol is %s (from MAC address)",
|
|
|
|
cfg->g_protocols[i].name);
|
|
|
|
return cfg->g_protocols[i].mode;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (cfg->g_protocols[i].guess(frame, s)) {
|
|
|
|
VLOG_DBG("guessed protocol is %s (from detector function)",
|
|
|
|
cfg->g_protocols[i].name);
|
|
|
|
return cfg->g_protocols[i].mode;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
lldpd_decode(struct lldpd *cfg, char *frame, int s,
|
|
|
|
struct lldpd_hardware *hw)
|
|
|
|
{
|
|
|
|
size_t listsize, i;
|
|
|
|
struct lldpd_chassis *chassis, *ochassis = NULL;
|
|
|
|
struct lldpd_port *port, *oport;
|
|
|
|
int guess = LLDPD_MODE_LLDP;
|
|
|
|
struct eth_header eheader;
|
|
|
|
int count = 0;
|
2015-02-22 16:44:30 -08:00
|
|
|
bool found = false;
|
2015-02-20 14:17:09 -05:00
|
|
|
|
|
|
|
VLOG_DBG("decode a received frame on %s size %d", hw->h_ifname,s);
|
|
|
|
|
|
|
|
if (s < sizeof(struct eth_header) + 4) {
|
|
|
|
/* Too short, just discard it */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Decapsulate VLAN frames */
|
|
|
|
memcpy(&eheader, frame, sizeof eheader);
|
|
|
|
if (eheader.eth_type == htons(ETH_TYPE_VLAN)) {
|
|
|
|
/* VLAN decapsulation means to shift 4 bytes left the frame from
|
|
|
|
* offset 2 * ETH_ADDR_LEN
|
|
|
|
*/
|
|
|
|
memmove(frame + 2 * ETH_ADDR_LEN, frame + 2 * ETH_ADDR_LEN + 4,
|
|
|
|
s - 2 * ETH_ADDR_LEN);
|
|
|
|
s -= 4;
|
|
|
|
}
|
|
|
|
|
2015-02-22 13:39:54 -08:00
|
|
|
LIST_FOR_EACH (oport, p_entries, &hw->h_rports) {
|
2015-02-22 16:35:43 -08:00
|
|
|
if (oport->p_lastframe &&
|
|
|
|
oport->p_lastframe->size == s &&
|
|
|
|
!memcmp(oport->p_lastframe->frame, frame, s)) {
|
2015-02-20 14:17:09 -05:00
|
|
|
/* Already received the same frame */
|
|
|
|
VLOG_DBG("duplicate frame, no need to decode");
|
2015-03-03 15:34:07 -08:00
|
|
|
oport->p_lastupdate = time_now();
|
2015-02-20 14:17:09 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
guess = lldpd_guess_type(cfg, frame, s);
|
|
|
|
VLOG_DBG("guessed %d enabled:%d", guess, cfg->g_protocols[0].enabled);
|
|
|
|
|
|
|
|
for (i = 0; cfg->g_protocols[i].mode != 0; i++) {
|
|
|
|
if (!cfg->g_protocols[i].enabled) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (cfg->g_protocols[i].mode == guess) {
|
|
|
|
VLOG_DBG("using decode function for %s protocol",
|
|
|
|
cfg->g_protocols[i].name);
|
|
|
|
if (cfg->g_protocols[i].decode(cfg, frame, s, hw, &chassis, &port)
|
|
|
|
== -1) {
|
|
|
|
VLOG_DBG("function for %s protocol did not "
|
|
|
|
"decode this frame",
|
|
|
|
cfg->g_protocols[i].name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
chassis->c_protocol = port->p_protocol = cfg->g_protocols[i].mode;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
VLOG_DBG(" %"PRIuSIZE "mode:%d enabled:%d",
|
|
|
|
i, cfg->g_protocols[i].mode, cfg->g_protocols[i].enabled);
|
|
|
|
}
|
|
|
|
if (cfg->g_protocols[i].mode == 0) {
|
|
|
|
VLOG_DBG("unable to guess frame type on %s", hw->h_ifname);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Do we already have the same MSAP somewhere? */
|
|
|
|
VLOG_DBG("search for the same MSAP");
|
|
|
|
|
2015-02-22 13:39:54 -08:00
|
|
|
LIST_FOR_EACH (oport, p_entries, &hw->h_rports) {
|
2015-02-20 14:17:09 -05:00
|
|
|
if (port->p_protocol == oport->p_protocol) {
|
|
|
|
count++;
|
2015-02-22 16:35:43 -08:00
|
|
|
if (port->p_id_subtype == oport->p_id_subtype &&
|
|
|
|
port->p_id_len == oport->p_id_len &&
|
|
|
|
!memcmp(port->p_id, oport->p_id, port->p_id_len) &&
|
|
|
|
chassis->c_id_subtype == oport->p_chassis->c_id_subtype &&
|
|
|
|
chassis->c_id_len == oport->p_chassis->c_id_len &&
|
|
|
|
!memcmp(chassis->c_id, oport->p_chassis->c_id,
|
|
|
|
chassis->c_id_len)) {
|
2015-02-20 14:17:09 -05:00
|
|
|
ochassis = oport->p_chassis;
|
|
|
|
VLOG_DBG("MSAP is already known");
|
2015-02-22 16:44:30 -08:00
|
|
|
found = true;
|
2015-02-20 14:17:09 -05:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!found) {
|
|
|
|
oport = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Do we have room for a new MSAP? */
|
|
|
|
if (!oport && cfg->g_config.c_max_neighbors) {
|
|
|
|
if (count == (cfg->g_config.c_max_neighbors - 1)) {
|
|
|
|
VLOG_DBG("max neighbors %d reached for port %s, "
|
|
|
|
"dropping any new ones silently",
|
|
|
|
cfg->g_config.c_max_neighbors,
|
|
|
|
hw->h_ifname);
|
|
|
|
} else if (count > cfg->g_config.c_max_neighbors - 1) {
|
|
|
|
VLOG_DBG("too many neighbors for port %s, drop this new one",
|
|
|
|
hw->h_ifname);
|
2015-02-22 12:45:17 -08:00
|
|
|
lldpd_port_cleanup(port, true);
|
|
|
|
lldpd_chassis_cleanup(chassis, true);
|
2015-02-20 14:17:09 -05:00
|
|
|
free(port);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* No, but do we already know the system? */
|
|
|
|
if (!oport) {
|
2017-08-02 15:03:06 -07:00
|
|
|
found = false;
|
2015-02-20 14:17:09 -05:00
|
|
|
VLOG_DBG("MSAP is unknown, search for the chassis");
|
|
|
|
|
2015-02-22 16:47:01 -08:00
|
|
|
LIST_FOR_EACH (ochassis, list, &cfg->g_chassis) {
|
2015-02-20 14:17:09 -05:00
|
|
|
if ((chassis->c_protocol == ochassis->c_protocol) &&
|
|
|
|
(chassis->c_id_subtype == ochassis->c_id_subtype) &&
|
|
|
|
(chassis->c_id_len == ochassis->c_id_len) &&
|
|
|
|
(memcmp(chassis->c_id, ochassis->c_id,
|
|
|
|
chassis->c_id_len) == 0)) {
|
2015-02-22 16:44:30 -08:00
|
|
|
found = true;
|
2015-02-20 14:17:09 -05:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!found) {
|
|
|
|
ochassis = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (oport) {
|
|
|
|
/* The port is known, remove it before adding it back */
|
2016-03-25 14:10:22 -07:00
|
|
|
ovs_list_remove(&oport->p_entries);
|
2015-02-20 14:17:09 -05:00
|
|
|
lldpd_port_cleanup(oport, 1);
|
|
|
|
free(oport);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ochassis) {
|
|
|
|
lldpd_move_chassis(ochassis, chassis);
|
|
|
|
chassis = ochassis;
|
|
|
|
} else {
|
|
|
|
/* Chassis not known, add it */
|
|
|
|
VLOG_DBG("unknown chassis, add it to the list");
|
|
|
|
chassis->c_index = ++cfg->g_lastrid;
|
|
|
|
chassis->c_refcount = 0;
|
2016-03-25 14:10:22 -07:00
|
|
|
ovs_list_push_back(&cfg->g_chassis, &chassis->list);
|
|
|
|
listsize = ovs_list_size(&cfg->g_chassis);
|
2015-02-20 14:17:09 -05:00
|
|
|
VLOG_DBG("%"PRIuSIZE " different systems are known", listsize);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add port */
|
2015-03-03 15:34:07 -08:00
|
|
|
port->p_lastchange = port->p_lastupdate = time_now();
|
2015-03-03 15:30:46 -08:00
|
|
|
port->p_lastframe = xmalloc(s + sizeof(struct lldpd_frame));
|
|
|
|
port->p_lastframe->size = s;
|
|
|
|
memcpy(port->p_lastframe->frame, frame, s);
|
2016-03-25 14:10:22 -07:00
|
|
|
ovs_list_insert(&hw->h_rports, &port->p_entries);
|
2015-02-20 14:17:09 -05:00
|
|
|
|
|
|
|
port->p_chassis = chassis;
|
|
|
|
port->p_chassis->c_refcount++;
|
|
|
|
/* Several cases are possible :
|
|
|
|
* 1. chassis is new, its refcount was 0. It is now attached
|
|
|
|
* to this port, its refcount is 1.
|
|
|
|
* 2. chassis already exists and was attached to another
|
|
|
|
* port, we increase its refcount accordingly.
|
|
|
|
* 3. chassis already exists and was attached to the same
|
|
|
|
* port, its refcount was decreased with
|
|
|
|
* lldpd_port_cleanup() and is now increased again.
|
|
|
|
*
|
|
|
|
* In all cases, if the port already existed, it has been
|
|
|
|
* freed with lldpd_port_cleanup() and therefore, the refcount
|
|
|
|
* of the chassis that was attached to it is decreased.
|
|
|
|
*/
|
2016-03-25 14:10:22 -07:00
|
|
|
i = ovs_list_size(&hw->h_rports);
|
2015-02-20 14:17:09 -05:00
|
|
|
VLOG_DBG("%"PRIuSIZE " neighbors for %s", i, hw->h_ifname);
|
|
|
|
|
|
|
|
if (!oport) {
|
|
|
|
hw->h_insert_cnt++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
lldpd_hide_ports(struct lldpd *cfg,
|
|
|
|
struct lldpd_hardware *hw,
|
|
|
|
int mask) {
|
|
|
|
struct lldpd_port *port;
|
|
|
|
int protocols[LLDPD_MODE_MAX + 1];
|
|
|
|
char buffer[256];
|
2015-02-22 16:44:30 -08:00
|
|
|
bool found = false;
|
|
|
|
int i, j, k;
|
2015-02-20 14:17:09 -05:00
|
|
|
unsigned int min;
|
|
|
|
|
|
|
|
VLOG_DBG("apply smart filter for port %s", hw->h_ifname);
|
|
|
|
|
|
|
|
/* Compute the number of occurrences of each protocol */
|
|
|
|
for (i = 0; i <= LLDPD_MODE_MAX; i++) {
|
|
|
|
protocols[i] = 0;
|
|
|
|
}
|
|
|
|
|
2015-02-22 13:39:54 -08:00
|
|
|
LIST_FOR_EACH (port, p_entries, &hw->h_rports) {
|
2015-02-20 14:17:09 -05:00
|
|
|
protocols[port->p_protocol]++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Turn the protocols[] array into an array of
|
|
|
|
* enabled/disabled protocols. 1 means enabled, 0
|
|
|
|
* means disabled.
|
|
|
|
*/
|
|
|
|
min = (unsigned int) - 1;
|
|
|
|
for (i = 0; i <= LLDPD_MODE_MAX; i++) {
|
|
|
|
if (protocols[i] && (protocols[i] < min)) {
|
|
|
|
min = protocols[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (i = 0; i <= LLDPD_MODE_MAX; i++) {
|
2015-02-22 16:35:43 -08:00
|
|
|
if (protocols[i] == min && !found) {
|
2015-02-20 14:17:09 -05:00
|
|
|
/* If we need a tie breaker, we take the first protocol only */
|
|
|
|
if (cfg->g_config.c_smart & mask &
|
|
|
|
(SMART_OUTGOING_ONE_PROTO | SMART_INCOMING_ONE_PROTO)) {
|
2015-02-22 16:44:30 -08:00
|
|
|
found = true;
|
2015-02-20 14:17:09 -05:00
|
|
|
}
|
|
|
|
protocols[i] = 1;
|
|
|
|
} else {
|
|
|
|
protocols[i] = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We set the p_hidden flag to 1 if the protocol is disabled */
|
2015-02-22 13:39:54 -08:00
|
|
|
LIST_FOR_EACH (port, p_entries, &hw->h_rports) {
|
2015-02-20 14:17:09 -05:00
|
|
|
if (mask == SMART_OUTGOING) {
|
2015-02-22 16:44:30 -08:00
|
|
|
port->p_hidden_out = protocols[port->p_protocol] ? false : true;
|
2015-02-20 14:17:09 -05:00
|
|
|
} else {
|
2015-02-22 16:44:30 -08:00
|
|
|
port->p_hidden_in = protocols[port->p_protocol] ? false : true;
|
2015-02-20 14:17:09 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If we want only one neighbor, we take the first one */
|
|
|
|
if (cfg->g_config.c_smart & mask &
|
|
|
|
(SMART_OUTGOING_ONE_NEIGH | SMART_INCOMING_ONE_NEIGH)) {
|
2015-02-22 16:44:30 -08:00
|
|
|
found = false;
|
2015-02-20 14:17:09 -05:00
|
|
|
|
2015-02-22 13:39:54 -08:00
|
|
|
LIST_FOR_EACH (port, p_entries, &hw->h_rports) {
|
2015-02-20 14:17:09 -05:00
|
|
|
if (mask == SMART_OUTGOING) {
|
|
|
|
if (found) {
|
2015-02-22 16:44:30 -08:00
|
|
|
port->p_hidden_out = true;
|
2015-02-20 14:17:09 -05:00
|
|
|
}
|
|
|
|
if (!port->p_hidden_out) {
|
2015-02-22 16:44:30 -08:00
|
|
|
found = true;
|
2015-02-20 14:17:09 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (mask == SMART_INCOMING) {
|
|
|
|
if (found) {
|
2015-02-22 16:44:30 -08:00
|
|
|
port->p_hidden_in = true;
|
2015-02-20 14:17:09 -05:00
|
|
|
}
|
|
|
|
if (!port->p_hidden_in) {
|
2015-02-22 16:44:30 -08:00
|
|
|
found = true;
|
2015-02-20 14:17:09 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Print a debug message summarizing the operation */
|
|
|
|
for (i = 0; i <= LLDPD_MODE_MAX; i++) {
|
|
|
|
protocols[i] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
k = j = 0;
|
2015-02-22 13:39:54 -08:00
|
|
|
LIST_FOR_EACH (port, p_entries, &hw->h_rports) {
|
2015-02-22 16:35:43 -08:00
|
|
|
if (!((mask == SMART_OUTGOING && port->p_hidden_out) ||
|
|
|
|
(mask == SMART_INCOMING && port->p_hidden_in))) {
|
2015-02-20 14:17:09 -05:00
|
|
|
k++;
|
|
|
|
protocols[port->p_protocol] = 1;
|
|
|
|
}
|
|
|
|
j++;
|
|
|
|
}
|
|
|
|
|
|
|
|
buffer[0] = '\0';
|
|
|
|
for (i = 0; cfg->g_protocols[i].mode != 0; i++) {
|
|
|
|
if (cfg->g_protocols[i].enabled &&
|
|
|
|
protocols[cfg->g_protocols[i].mode]) {
|
|
|
|
if (strlen(buffer) +
|
|
|
|
strlen(cfg->g_protocols[i].name) + 3 > sizeof(buffer)) {
|
|
|
|
/* Unlikely, our buffer is too small */
|
|
|
|
memcpy(buffer + sizeof(buffer) - 4, "...", 4);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (buffer[0]) {
|
|
|
|
strncat(buffer, ", ", 2);
|
|
|
|
strncat(buffer, cfg->g_protocols[i].name,
|
|
|
|
strlen(cfg->g_protocols[i].name));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
VLOG_DBG("%s: %s: %d visible neighbors (out of %d)",
|
|
|
|
hw->h_ifname,
|
|
|
|
(mask == SMART_OUTGOING) ? "out filter" : "in filter",
|
|
|
|
k, j);
|
|
|
|
VLOG_DBG("%s: protocols: %s",
|
|
|
|
hw->h_ifname, buffer[0] ? buffer : "(none)");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Hide unwanted ports depending on smart mode set by the user */
|
|
|
|
static void
|
|
|
|
lldpd_hide_all(struct lldpd *cfg)
|
|
|
|
{
|
|
|
|
struct lldpd_hardware *hw;
|
|
|
|
|
|
|
|
if (!cfg->g_config.c_smart) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
VLOG_DBG("apply smart filter results on all ports");
|
|
|
|
|
2015-02-22 16:55:34 -08:00
|
|
|
LIST_FOR_EACH (hw, h_entries, &cfg->g_hardware) {
|
2015-02-20 14:17:09 -05:00
|
|
|
if (cfg->g_config.c_smart & SMART_INCOMING_FILTER) {
|
|
|
|
lldpd_hide_ports(cfg, hw, SMART_INCOMING);
|
|
|
|
}
|
|
|
|
if (cfg->g_config.c_smart & SMART_OUTGOING_FILTER) {
|
|
|
|
lldpd_hide_ports(cfg, hw, SMART_OUTGOING);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
lldpd_recv(struct lldpd *cfg,
|
|
|
|
struct lldpd_hardware *hw,
|
|
|
|
char *buffer,
|
|
|
|
size_t bufSize)
|
|
|
|
{
|
|
|
|
int n = bufSize;
|
|
|
|
|
|
|
|
VLOG_DBG("receive a frame on %s", hw->h_ifname);
|
|
|
|
if (cfg->g_config.c_paused) {
|
|
|
|
VLOG_DBG("paused, ignore the frame on %s", hw->h_ifname);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
hw->h_rx_cnt++;
|
|
|
|
VLOG_DBG("decode received frame on %s h_rx_cnt=%" PRIu64,
|
|
|
|
hw->h_ifname, hw->h_rx_cnt);
|
|
|
|
lldpd_decode(cfg, buffer, n, hw);
|
|
|
|
lldpd_hide_all(cfg); /* Immediatly hide */
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t
|
2015-02-20 14:17:10 -05:00
|
|
|
lldpd_send(struct lldpd_hardware *hw, struct dp_packet *p)
|
2015-02-20 14:17:09 -05:00
|
|
|
{
|
|
|
|
struct lldpd *cfg = hw->h_cfg;
|
|
|
|
struct lldpd_port *port;
|
|
|
|
int i, sent = 0;
|
|
|
|
int lldp_size = 0;
|
|
|
|
|
|
|
|
if (cfg->g_config.c_receiveonly || cfg->g_config.c_paused) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#ifndef _WIN32
|
|
|
|
if ((hw->h_flags & IFF_RUNNING) == 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
for (i = 0; cfg->g_protocols[i].mode != 0; i++) {
|
|
|
|
if (!cfg->g_protocols[i].enabled) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We send only if we have at least one remote system
|
|
|
|
* speaking this protocol or if the protocol is forced */
|
|
|
|
if (cfg->g_protocols[i].enabled > 1) {
|
2015-02-22 16:34:36 -08:00
|
|
|
if ((lldp_size = cfg->g_protocols[i].send(cfg, hw, p)) != -E2BIG) {
|
2015-02-20 14:17:09 -05:00
|
|
|
sent++;
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
VLOG_DBG("send PDU on %s failed E2BIG", hw->h_ifname);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-22 13:39:54 -08:00
|
|
|
LIST_FOR_EACH (port, p_entries, &hw->h_rports) {
|
2015-02-20 14:17:09 -05:00
|
|
|
/* If this remote port is disabled, we don't consider it */
|
|
|
|
if (port->p_hidden_out) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (port->p_protocol == cfg->g_protocols[i].mode) {
|
|
|
|
VLOG_DBG("send PDU on %s with protocol %s",
|
|
|
|
hw->h_ifname, cfg->g_protocols[i].name);
|
|
|
|
lldp_size = cfg->g_protocols[i].send(cfg, hw, p);
|
|
|
|
sent++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!sent) {
|
|
|
|
/* Nothing was sent for this port, let's speak the first
|
|
|
|
* available protocol.
|
|
|
|
*/
|
|
|
|
for (i = 0; cfg->g_protocols[i].mode != 0; i++) {
|
|
|
|
if (!cfg->g_protocols[i].enabled) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
VLOG_DBG("fallback to protocol %s for %s",
|
|
|
|
cfg->g_protocols[i].name, hw->h_ifname);
|
|
|
|
lldp_size = cfg->g_protocols[i].send(cfg, hw, p);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (cfg->g_protocols[i].mode == 0) {
|
|
|
|
VLOG_WARN("no protocol enabled, dunno what to send");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return lldp_size;
|
|
|
|
}
|