#include <fcntl.h>
#include <fnmatch.h>
#include <time.h>
-#include <netdb.h>
+#include <libgen.h>
#include <sys/utsname.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
-#include <netpacket/packet.h>
#include <ifaddrs.h>
#include <net/if_arp.h>
#include <linux/filter.h>
#include <linux/if_vlan.h>
+#include <linux/if_packet.h>
#include <linux/sockios.h>
-#include <linux/ethtool.h>
#ifdef USE_SNMP
#include <net-snmp/net-snmp-config.h>
void usage(void);
int lldpd_iface_init(struct lldpd *, struct lldpd_hardware *);
+int lldpd_iface_init_vlan(struct lldpd *, struct lldpd_vif *);
+void lldpd_iface_init_mtu(struct lldpd *, struct lldpd_hardware *);
int lldpd_iface_close(struct lldpd *, struct lldpd_hardware *);
void lldpd_iface_multicast(struct lldpd *, const char *, int);
{ 0x6, 0, 0, 0x0000ffff }, \
{ 0x6, 0, 0, 0x00000000 },
struct sock_filter lldpd_filter_lldp_f[] = { LLDPD_FILTER_LLDP_F };
-/* "(ether dst 01:00:0c:cc:cc:cc) or (vlan and ether dst 01:00:0c:cc:cc:cc)" */
+#ifdef ENABLE_FDP
+/* "ether dst 01:e0:52:cc:cc:cc" */
+#define LLDPD_FILTER_FDP_F \
+ { 0x20, 0, 0, 0x00000002 }, \
+ { 0x15, 0, 3, 0x52cccccc }, \
+ { 0x28, 0, 0, 0x00000000 }, \
+ { 0x15, 0, 1, 0x000001e0 }, \
+ { 0x6, 0, 0, 0x0000ffff }, \
+ { 0x6, 0, 0, 0x00000000 },
+struct sock_filter lldpd_filter_fdp_f[] = { LLDPD_FILTER_FDP_F };
+#endif /* ENABLE_FDP */
+#ifdef ENABLE_CDP
+/* "ether dst 01:00:0c:cc:cc:cc" */
#define LLDPD_FILTER_CDP_F \
- { 0x20, 0, 0, 0x00000002 }, \
- { 0x15, 0, 2, 0x0ccccccc }, \
- { 0x28, 0, 0, 0x00000000 }, \
- { 0x15, 6, 0, 0x00000100 }, \
- { 0x28, 0, 0, 0x0000000c }, \
- { 0x15, 0, 5, 0x00008100 }, \
- { 0x20, 0, 0, 0x00000002 }, \
- { 0x15, 0, 3, 0x0ccccccc }, \
- { 0x28, 0, 0, 0x00000000 }, \
- { 0x15, 0, 1, 0x00000100 }, \
- { 0x6, 0, 0, 0x0000ffff }, \
- { 0x6, 0, 0, 0x00000000 },
+ { 0x20, 0, 0, 0x00000002 }, \
+ { 0x15, 0, 3, 0x0ccccccc }, \
+ { 0x28, 0, 0, 0x00000000 }, \
+ { 0x15, 0, 1, 0x00000100 }, \
+ { 0x6, 0, 0, 0x0000ffff }, \
+ { 0x6, 0, 0, 0x00000000 },
struct sock_filter lldpd_filter_cdp_f[] = { LLDPD_FILTER_CDP_F };
+#endif /* ENABLE_CDP */
+#ifdef ENABLE_SONMP
/* "ether dst 01:00:81:00:01:00" */
#define LLDPD_FILTER_SONMP_F \
{ 0x20, 0, 0, 0x00000002 }, \
{ 0x6, 0, 0, 0x0000ffff }, \
{ 0x6, 0, 0, 0x00000000 },
struct sock_filter lldpd_filter_sonmp_f[] = { LLDPD_FILTER_SONMP_F };
+#endif /* ENABLE_SONMP */
+#ifdef ENABLE_EDP
/* "ether dst 00:e0:2b:00:00:00" */
#define LLDPD_FILTER_EDP_F \
{ 0x20, 0, 0, 0x00000002 }, \
{ 0x6, 0, 0, 0x0000ffff }, \
{ 0x6, 0, 0, 0x00000000 },
struct sock_filter lldpd_filter_edp_f[] = { LLDPD_FILTER_EDP_F };
+#endif /* ENABLE_EDP */
#define LLDPD_FILTER_ANY_F \
{ 0x28, 0, 0, 0x0000000c }, \
{ 0x15, 0, 4, 0x000088cc }, \
{ 0x20, 0, 0, 0x00000002 }, \
{ 0x15, 0, 2, 0xc200000e }, \
{ 0x28, 0, 0, 0x00000000 }, \
- { 0x15, 8, 9, 0x00000180 }, \
+ { 0x15, 11, 12, 0x00000180 }, \
{ 0x20, 0, 0, 0x00000002 }, \
{ 0x15, 0, 2, 0x2b000000 }, \
{ 0x28, 0, 0, 0x00000000 }, \
- { 0x15, 4, 5, 0x000000e0 }, \
+ { 0x15, 7, 8, 0x000000e0 }, \
{ 0x15, 1, 0, 0x0ccccccc }, \
- { 0x15, 0, 3, 0x81000100 }, \
+ { 0x15, 0, 2, 0x81000100 }, \
+ { 0x28, 0, 0, 0x00000000 }, \
+ { 0x15, 3, 4, 0x00000100 }, \
+ { 0x15, 0, 3, 0x52cccccc }, \
{ 0x28, 0, 0, 0x00000000 }, \
- { 0x15, 0, 1, 0x00000100 }, \
+ { 0x15, 0, 1, 0x000001e0 }, \
{ 0x6, 0, 0, 0x0000ffff }, \
{ 0x6, 0, 0, 0x00000000 },
struct sock_filter lldpd_filter_any_f[] = { LLDPD_FILTER_ANY_F };
{
{ LLDPD_MODE_LLDP, 1, "LLDP", ' ', lldp_send, lldp_decode, NULL,
LLDP_MULTICAST_ADDR, lldpd_filter_lldp_f, sizeof(lldpd_filter_lldp_f) },
+#ifdef ENABLE_CDP
{ LLDPD_MODE_CDPV1, 0, "CDPv1", 'c', cdpv1_send, cdp_decode, cdpv1_guess,
CDP_MULTICAST_ADDR, lldpd_filter_cdp_f, sizeof(lldpd_filter_cdp_f) },
{ LLDPD_MODE_CDPV2, 0, "CDPv2", 'c', cdpv2_send, cdp_decode, cdpv2_guess,
CDP_MULTICAST_ADDR, lldpd_filter_cdp_f, sizeof(lldpd_filter_cdp_f) },
+#endif
+#ifdef ENABLE_SONMP
{ LLDPD_MODE_SONMP, 0, "SONMP", 's', sonmp_send, sonmp_decode, NULL,
SONMP_MULTICAST_ADDR, lldpd_filter_sonmp_f, sizeof(lldpd_filter_sonmp_f) },
+#endif
+#ifdef ENABLE_EDP
{ LLDPD_MODE_EDP, 0, "EDP", 'e', edp_send, edp_decode, NULL,
EDP_MULTICAST_ADDR, lldpd_filter_edp_f, sizeof(lldpd_filter_edp_f) },
+#endif
+#ifdef ENABLE_FDP
+ { LLDPD_MODE_FDP, 0, "FDP", 'f', fdp_send, cdp_decode, NULL,
+ FDP_MULTICAST_ADDR, lldpd_filter_fdp_f, sizeof(lldpd_filter_fdp_f) },
+#endif
{ 0, 0, "any", ' ', NULL, NULL, NULL,
{0,0,0,0,0,0}, lldpd_filter_any_f, sizeof(lldpd_filter_any_f) }
};
struct lldpd_hardware *);
struct lldpd_hardware *lldpd_port_add(struct lldpd *, struct ifaddrs *);
void lldpd_loop(struct lldpd *);
-void lldpd_hangup(int);
void lldpd_shutdown(int);
void lldpd_exit();
void lldpd_send_all(struct lldpd *);
int lldpd_guess_type(struct lldpd *, char *, int);
void lldpd_decode(struct lldpd *, char *, int,
struct lldpd_hardware *, int);
-void lldpd_handle_client(struct lldpd *, struct lldpd_client *,
- char *, int);
-
-void lldpd_handle_none(struct lldpd *, struct hmsg *,
- struct hmsg *);
-void lldpd_handle_get_interfaces(struct lldpd *, struct hmsg *,
- struct hmsg *);
-void lldpd_handle_get_port_related(struct lldpd *, struct hmsg *,
- struct hmsg *);
-void lldpd_handle_shutdown(struct lldpd *, struct hmsg *,
- struct hmsg *);
-
-struct client_handle {
- enum hmsg_type type;
- void (*handle)(struct lldpd*, struct hmsg*, struct hmsg*);
-};
-
-struct client_handle client_handles[] = {
- { HMSG_NONE, lldpd_handle_none },
- { HMSG_GET_INTERFACES, lldpd_handle_get_interfaces },
- { HMSG_GET_CHASSIS, lldpd_handle_get_port_related },
- { HMSG_GET_PORT, lldpd_handle_get_port_related },
- { HMSG_GET_VLANS, lldpd_handle_get_port_related },
- { HMSG_SHUTDOWN, lldpd_handle_shutdown },
- { 0, NULL } };
+#ifdef ENABLE_LLDPMED
+void lldpd_med(struct lldpd_chassis *);
+#endif
char **saved_argv;
usage(void)
{
extern const char *__progname;
-#ifndef USE_SNMP
- fprintf(stderr, "usage: %s [-d] [-c] [-s] [-e] [-p|-P] [-m ip]\n", __progname);
-#else /* USE_SNMP */
- fprintf(stderr, "usage: %s [-d] [-c] [-s] [-e] [-p|-P] [-m ip] [-x]\n", __progname);
-#endif /* USE_SNMP */
+ fprintf(stderr, "usage: %s [options]\n", __progname);
+ fprintf(stderr, "see manual page lldpd(8) for more information\n");
exit(1);
}
-int
-lldpd_iface_init(struct lldpd *global, struct lldpd_hardware *hardware)
+void
+lldpd_iface_init_mtu(struct lldpd *global, struct lldpd_hardware *hardware)
{
- struct sockaddr_ll sa;
struct ifreq ifr;
- int master; /* Bond device */
- char if_bond[IFNAMSIZ];
- int un = 1;
- short int filter;
/* get MTU */
memset(&ifr, 0, sizeof(ifr));
LLOG_WARN("unable to get MTU of %s, using 1500", hardware->h_ifname);
hardware->h_mtu = 1500;
} else
- hardware->h_mtu = ifr.ifr_mtu;
-
- /* Open listening socket to receive/send frames */
- if ((hardware->h_raw = socket(PF_PACKET, SOCK_RAW,
- htons(ETH_P_ALL))) < 0)
- return errno;
- memset(&sa, 0, sizeof(sa));
- sa.sll_family = AF_PACKET;
- sa.sll_protocol = 0;
- sa.sll_ifindex = if_nametoindex(hardware->h_ifname);
- if (bind(hardware->h_raw, (struct sockaddr*)&sa, sizeof(sa)) < 0)
- return errno;
+ hardware->h_mtu = hardware->h_lport.p_mfs = ifr.ifr_mtu;
+}
+
+int
+lldpd_iface_init_vlan(struct lldpd *global, struct lldpd_vif *vif)
+{
+ int status;
+ short int filter;
+
+ lldpd_iface_init_mtu(global, (struct lldpd_hardware*)vif);
+ status = priv_iface_init((struct lldpd_hardware*)vif, -1);
+ if (status != 0)
+ return status;
+
+ if (global->g_multi)
+ filter = LLDPD_MODE_ANY;
+ else
+ filter = LLDPD_MODE_LLDP;
+
+ if (lldpd_iface_switchto(global, filter,
+ (struct lldpd_hardware*)vif) == -1) {
+ LLOG_WARNX("unable to apply filter");
+ return ENETDOWN;
+ }
+
+ lldpd_iface_multicast(global, vif->vif_ifname, 0);
+
+ LLOG_DEBUG("vlan interface %s initialized (fd=%d)", vif->vif_ifname,
+ vif->vif_raw);
+ return 0;
+}
+
+int
+lldpd_iface_init(struct lldpd *global, struct lldpd_hardware *hardware)
+{
+ int master; /* Bond device */
+ char if_bond[IFNAMSIZ];
+ int status;
+ short int filter;
+
+ lldpd_iface_init_mtu(global, hardware);
+ status = priv_iface_init(hardware, -1);
+ if (status != 0)
+ return status;
if ((master = iface_is_enslaved(global, hardware->h_ifname)) != -1) {
/* With bonding device, we need to listen on the bond ! */
hardware->h_raw_real = hardware->h_raw;
hardware->h_master = master;
hardware->h_raw = -1;
- if ((hardware->h_raw = socket(PF_PACKET, SOCK_RAW,
- htons(ETH_P_ALL))) < 0)
- return errno;
- memset(&sa, 0, sizeof(sa));
- sa.sll_family = AF_PACKET;
- sa.sll_protocol = 0;
- sa.sll_ifindex = master;
- if (bind(hardware->h_raw, (struct sockaddr*)&sa,
- sizeof(sa)) < 0)
- return errno;
- /* With bonding, we need to listen to bond device. We use
- * setsockopt() PACKET_ORIGDEV to get physical device instead of
- * bond device */
- if (setsockopt(hardware->h_raw, SOL_PACKET,
- PACKET_ORIGDEV, &un, sizeof(un)) == -1) {
- LLOG_WARN("unable to setsockopt for master bonding device of %s. "
- "You will get inaccurate results",
- hardware->h_ifname);
- }
+ status = priv_iface_init(hardware, master);
+ if (status != 0) {
+ close(hardware->h_raw_real);
+ if (hardware->h_raw != -1)
+ close(hardware->h_raw);
+ return status;
+ }
}
if (global->g_multi)
void
lldpd_iface_multicast(struct lldpd *global, const char *name, int remove)
{
- struct ifreq ifr;
- int i;
+ int i, rc;
for (i=0; global->g_protocols[i].mode != 0; i++) {
if (!global->g_protocols[i].enabled) continue;
- memset(&ifr, 0, sizeof(ifr));
- strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
- memcpy(ifr.ifr_hwaddr.sa_data,
- global->g_protocols[i].mac, ETH_ALEN);
- if (ioctl(global->g_sock, (remove)?SIOCDELMULTI:SIOCADDMULTI,
- &ifr) < 0) {
- if (errno == ENOENT)
- return;
- LLOG_INFO("unable to %s %s address to multicast filter for %s",
- (remove)?"delete":"add",
- global->g_protocols[i].name,
- name);
+ if ((rc = priv_iface_multicast(name,
+ global->g_protocols[i].mac, !remove)) != 0) {
+ errno = rc;
+ if (errno != ENOENT)
+ LLOG_INFO("unable to %s %s address to multicast filter for %s",
+ (remove)?"delete":"add",
+ global->g_protocols[i].name,
+ name);
}
}
}
return 0;
}
-
+#ifdef ENABLE_DOT1
void
lldpd_vlan_cleanup(struct lldpd_port *port)
{
free(vlan);
}
}
+#endif
void
lldpd_port_cleanup(struct lldpd_port *port)
{
+#ifdef ENABLE_LLDPMED
+ int i;
+ for (i=0; i < LLDPMED_LOCFORMAT_LAST; i++)
+ free(port->p_med_location[i].data);
+#endif
+#ifdef ENABLE_DOT1
lldpd_vlan_cleanup(port);
+#endif
free(port->p_id);
free(port->p_descr);
free(port);
void
lldpd_chassis_cleanup(struct lldpd_chassis *chassis)
{
+#ifdef ENABLE_LLDPMED
+ free(chassis->c_med_hw);
+ free(chassis->c_med_fw);
+ free(chassis->c_med_sn);
+ free(chassis->c_med_manuf);
+ free(chassis->c_med_model);
+ free(chassis->c_med_asset);
+#endif
free(chassis->c_id);
free(chassis->c_name);
free(chassis->c_descr);
}
}
+void
+lldpd_hardware_cleanup(struct lldpd_hardware *hardware)
+{
+#ifdef ENABLE_DOT1
+ lldpd_vlan_cleanup(&hardware->h_lport);
+#endif
+ free(hardware->h_proto_macs);
+ free(hardware->h_llastframe);
+ free(hardware);
+}
+
void
lldpd_cleanup(struct lldpd *cfg)
{
struct lldpd_hardware *hardware, *hardware_next;
+ struct lldpd_vif *vif, *vif_next;
for (hardware = TAILQ_FIRST(&cfg->g_hardware); hardware != NULL;
hardware = hardware_next) {
if (hardware->h_flags == 0) {
TAILQ_REMOVE(&cfg->g_hardware, hardware, h_entries);
lldpd_iface_close(cfg, hardware);
- lldpd_vlan_cleanup(&hardware->h_lport);
lldpd_remote_cleanup(cfg, hardware, 1);
- free(hardware->h_proto_macs);
- free(hardware->h_llastframe);
- free(hardware);
+ lldpd_hardware_cleanup(hardware);
} else if (hardware->h_rchassis != NULL) {
if (time(NULL) - hardware->h_rlastupdate >
hardware->h_rchassis->c_ttl) {
}
}
}
+ for (vif = TAILQ_FIRST(&cfg->g_vif); vif != NULL;
+ vif = vif_next) {
+ vif_next = TAILQ_NEXT(vif, vif_entries);
+ if (vif->vif_flags == 0) {
+ TAILQ_REMOVE(&cfg->g_vif, vif, vif_entries);
+ lldpd_iface_close(cfg, (struct lldpd_hardware*)vif);
+ free(vif);
+ }
+ }
+}
+
+struct lldpd_vif *
+lldpd_port_add_vlan(struct lldpd *cfg, struct ifaddrs *ifa)
+{
+ struct lldpd_vif *vif;
+ struct lldpd_hardware *hardware;
+ struct vlan_ioctl_args ifv;
+
+ TAILQ_FOREACH(vif, &cfg->g_vif, vif_entries) {
+ if (strcmp(vif->vif_ifname, ifa->ifa_name) == 0)
+ break;
+ }
+
+ if (vif == NULL) {
+ if ((vif = (struct lldpd_vif *)
+ calloc(1, sizeof(struct lldpd_vif))) == NULL)
+ return NULL;
+ vif->vif_raw = -1;
+ vif->vif_raw_real = -1;
+ }
+ strlcpy(vif->vif_ifname, ifa->ifa_name, sizeof(vif->vif_ifname));
+ vif->vif_flags = ifa->ifa_flags;
+
+ if (vif->vif_raw == -1) {
+
+ if (lldpd_iface_init_vlan(cfg, vif) != 0) {
+ free(vif);
+ return NULL;
+ }
+
+ /* Find the real interface */
+ vif->vif_real = NULL;
+ TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
+ memset(&ifv, 0, sizeof(ifv));
+ ifv.cmd = GET_VLAN_REALDEV_NAME_CMD;
+ strlcpy(ifv.device1, ifa->ifa_name, sizeof(ifv.device1));
+ if ((ioctl(cfg->g_sock, SIOCGIFVLAN, &ifv) >= 0) &&
+ (strncmp(hardware->h_ifname,
+ ifv.u.device2,
+ sizeof(ifv.u.device2)) == 0))
+ vif->vif_real = hardware;
+ }
+ if (vif->vif_real == NULL) {
+ LLOG_WARNX("unable to find real interface for %s",
+ ifa->ifa_name);
+ free(vif);
+ return NULL;
+ }
+
+ TAILQ_INSERT_TAIL(&cfg->g_vif, vif, vif_entries);
+ }
+
+ return vif;
}
struct lldpd_hardware *
lldpd_port_add(struct lldpd *cfg, struct ifaddrs *ifa)
{
+#if defined (ENABLE_DOT1) || defined (ENABLE_DOT3)
struct ifaddrs *oifap, *oifa;
+#endif
struct lldpd_hardware *hardware;
struct lldpd_port *port;
+#ifdef ENABLE_DOT1
struct lldpd_vlan *vlan;
- struct ifreq ifr;
struct vlan_ioctl_args ifv;
+#endif
+#ifdef ENABLE_DOT3
struct ethtool_cmd ethc;
+#endif
u_int8_t *lladdr;
TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
hardware->h_raw_real = -1;
hardware->h_start_probe = 0;
hardware->h_proto_macs = (u_int8_t*)calloc(cfg->g_multi+1, ETH_ALEN);
+#ifdef ENABLE_LLDPMED
+ hardware->h_lport.p_med_cap_enabled = LLDPMED_CAP_CAP;
+ if (!cfg->g_noinventory)
+ hardware->h_lport.p_med_cap_enabled |= LLDPMED_CAP_IV;
+#endif
+#ifdef ENABLE_DOT1
TAILQ_INIT(&hardware->h_lport.p_vlans);
} else {
lldpd_vlan_cleanup(&hardware->h_lport);
+#endif
}
port = &hardware->h_lport;
strlcpy(hardware->h_ifname, ifa->ifa_name, sizeof(hardware->h_ifname));
lladdr = (u_int8_t*)(((struct sockaddr_ll *)ifa->ifa_addr)->sll_addr);
memcpy(&hardware->h_lladdr, lladdr, sizeof(hardware->h_lladdr));
+ iface_get_permanent_mac(cfg, hardware);
port->p_id_subtype = LLDP_PORTID_SUBTYPE_LLADDR;
- port->p_id = (char*)hardware->h_lladdr;
+ if ((port->p_id = calloc(1, sizeof(hardware->h_lladdr))) == NULL)
+ fatal(NULL);
+ memcpy(port->p_id, hardware->h_lladdr, sizeof(hardware->h_lladdr));
port->p_id_len = sizeof(hardware->h_lladdr);
port->p_descr = hardware->h_ifname;
}
/* Get VLANS and aggregation status */
+#if defined (ENABLE_DOT3) || defined (ENABLE_DOT1)
if (getifaddrs(&oifap) != 0)
fatal("lldpd_port_add: failed to get interface list");
for (oifa = oifap; oifa != NULL; oifa = oifa->ifa_next) {
+#ifdef ENABLE_DOT1
/* Check if we already have checked this one */
int skip = 0;
TAILQ_FOREACH(vlan, &port->p_vlans, v_entries) {
skip = 1;
}
if (skip) continue;
+#endif
/* Aggregation check */
- if (iface_is_bond_slave(cfg, hardware->h_ifname, oifa->ifa_name))
+#ifdef ENABLE_DOT3
+ if (iface_is_bond_slave(cfg, hardware->h_ifname, oifa->ifa_name, NULL))
port->p_aggregid = if_nametoindex(oifa->ifa_name);
-
+#endif
+
+#ifdef ENABLE_DOT1
/* VLAN check */
memset(&ifv, 0, sizeof(ifv));
ifv.cmd = GET_VLAN_REALDEV_NAME_CMD;
strlcpy(ifv.device1, oifa->ifa_name, sizeof(ifv.device1));
if ((ioctl(cfg->g_sock, SIOCGIFVLAN, &ifv) >= 0) &&
- ((iface_is_bond_slave(cfg, hardware->h_ifname, ifv.u.device2)) ||
+ ((iface_is_bond_slave(cfg, hardware->h_ifname, ifv.u.device2, NULL)) ||
(strncmp(hardware->h_ifname, ifv.u.device2, sizeof(ifv.u.device2)) == 0))) {
if ((vlan = (struct lldpd_vlan *)
calloc(1, sizeof(struct lldpd_vlan))) == NULL)
continue;
- if (asprintf(&vlan->v_name, "%s", oifa->ifa_name) == -1) {
+ if ((vlan->v_name = strdup(oifa->ifa_name)) == NULL) {
free(vlan);
continue;
}
TAILQ_INSERT_TAIL(&port->p_vlans, vlan, v_entries);
}
}
+#endif
}
freeifaddrs(oifap);
+#endif
+#ifdef ENABLE_DOT3
/* MAC/PHY */
- memset(&ifr, 0, sizeof(ifr));
- memset(ðc, 0, sizeof(ethc));
- strlcpy(ifr.ifr_name, hardware->h_ifname, sizeof(ifr.ifr_name));
- ifr.ifr_data = (caddr_t)ðc;
- ethc.cmd = ETHTOOL_GSET;
- if (ioctl(cfg->g_sock, SIOCETHTOOL, &ifr) == 0) {
+ if (priv_ethtool(hardware->h_ifname, ðc) == 0) {
int j;
int advertised_ethtool_to_rfc3636[][2] = {
{ADVERTISED_10baseT_Half, LLDP_DOT3_LINK_AUTONEG_10BASE_T},
break;
}
if (ethc.port == PORT_AUI) port->p_mau_type = LLDP_DOT3_MAU_AUI;
- }
+ } else
+ LLOG_DEBUG("unable to get eth info for %s", hardware->h_ifname);
+#endif
if (!INTERFACE_OPENED(hardware)) {
if (lldpd_iface_init(cfg, hardware) != 0) {
- lldpd_vlan_cleanup(&hardware->h_lport);
- free(hardware->h_lladdr);
- free(hardware->h_proto_macs);
- free(hardware);
+ LLOG_WARN("unable to initialize %s", hardware->h_ifname);
+ lldpd_hardware_cleanup(hardware);
return (NULL);
}
struct lldpd_hardware *ohardware, *firstnull = NULL, *older = NULL;
int guess = LLDPD_MODE_LLDP;
+ /* Discard VLAN frames */
+ if ((s >= sizeof(struct ieee8023)) &&
+ (((struct ieee8023*)frame)->size == htons(ETHERTYPE_VLAN)))
+ return;
+
if ((hardware->h_rlastframe != NULL) &&
(hardware->h_rlastframe->size == s) &&
(memcmp(hardware->h_rlastframe->frame, frame, s) == 0)) {
return;
}
-void
-lldpd_handle_client(struct lldpd *cfg, struct lldpd_client *client,
- char *buffer, int n)
-{
- struct hmsg *h; /* Reception */
- struct hmsg *t; /* Sending */
- struct client_handle *ch;
-
- if (n < sizeof(struct hmsg_hdr)) {
- LLOG_WARNX("too short message request received");
- return;
- }
- h = (struct hmsg *)buffer;
- n -= sizeof(struct hmsg_hdr);
- if (n != h->hdr.len) {
- LLOG_WARNX("incorrect message size received from %d",
- h->hdr.pid);
- return;
- }
-
- if ((t = (struct hmsg*)calloc(1, MAX_HMSGSIZE)) == NULL) {
- LLOG_WARNX("unable to allocate memory to answer to %d",
- h->hdr.pid);
- return;
- }
- ctl_msg_init(t, h->hdr.type);
- for (ch = client_handles; ch->handle != NULL; ch++) {
- if (ch->type == h->hdr.type) {
- ch->handle(cfg, h, t);
- if (t->hdr.len == -1) {
- t->hdr.len = 0;
- t->hdr.type = HMSG_NONE;
- }
- if (ctl_msg_send(client->fd, t) == -1)
- LLOG_WARN("unable to send answer to client %d",
- h->hdr.pid);
- free(t);
- return;
- }
- }
-
- LLOG_WARNX("unknown message request (%d) received from %d",
- h->hdr.type, h->hdr.pid);
- free(t);
- return;
-}
-
-void
-lldpd_handle_shutdown(struct lldpd *cfg, struct hmsg *r, struct hmsg *s)
-{
- LLOG_INFO("received shutdown request from client %d",
- r->hdr.pid);
- exit(0);
-}
-
-void
-lldpd_handle_none(struct lldpd *cfg, struct hmsg *r, struct hmsg *s)
-{
- LLOG_INFO("received noop request from client %d",
- r->hdr.pid);
- s->hdr.len = -1;
-}
-
-void
-lldpd_handle_get_interfaces(struct lldpd *cfg, struct hmsg *r, struct hmsg *s)
-{
- struct lldpd_interface *iff, *iff_next;
- struct lldpd_hardware *hardware;
- void *p;
-
- /* Build the list of interfaces */
- TAILQ_HEAD(, lldpd_interface) ifs;
- TAILQ_INIT(&ifs);
- TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
- if ((iff = (struct lldpd_interface*)malloc(sizeof(
- struct lldpd_interface))) == NULL)
- fatal(NULL);
- iff->name = hardware->h_ifname;
- TAILQ_INSERT_TAIL(&ifs, iff, next);
- }
-
- p = &s->data;
- if (ctl_msg_pack_list(STRUCT_LLDPD_INTERFACE, &ifs,
- sizeof(struct lldpd_interface), s, &p) == -1) {
- LLOG_WARNX("unable to pack list of interfaces");
- s->hdr.len = -1;
- }
-
- /* Free the temporary list */
- for (iff = TAILQ_FIRST(&ifs);
- iff != NULL;
- iff = iff_next) {
- iff_next = TAILQ_NEXT(iff, next);
- TAILQ_REMOVE(&ifs, iff, next);
- free(iff);
- }
-}
-
-void
-lldpd_handle_get_port_related(struct lldpd *cfg, struct hmsg *r, struct hmsg *s)
-{
- char *ifname;
- struct lldpd_hardware *hardware;
- void *p;
-
- ifname = (char*)(&r->data);
- if (ifname[r->hdr.len - 1] != 0) {
- LLOG_WARNX("bad message format for get port related message");
- s->hdr.len = -1;
- return;
- }
- TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
- if (strncmp(ifname, hardware->h_ifname, IFNAMSIZ) == 0) {
- if ((hardware->h_rport == NULL) ||
- (hardware->h_rchassis == NULL)) {
- s->hdr.len = 0;
- s->hdr.type = HMSG_NONE;
- return;
- }
- p = &s->data;
- switch (r->hdr.type) {
- case HMSG_GET_VLANS:
- if (ctl_msg_pack_list(STRUCT_LLDPD_VLAN,
- &hardware->h_rport->p_vlans,
- sizeof(struct lldpd_vlan), s, &p) == -1) {
- LLOG_WARNX("unable to send vlans information for "
- "interface %s for %d", ifname, r->hdr.pid);
- s->hdr.len = -1;
- return;
- }
- break;
- case HMSG_GET_PORT:
- if (ctl_msg_pack_structure(STRUCT_LLDPD_PORT,
- hardware->h_rport,
- sizeof(struct lldpd_port), s, &p) == -1) {
- LLOG_WARNX("unable to send port information for "
- "interface %s for %d", ifname, r->hdr.pid);
- s->hdr.len = -1;
- return;
- }
- break;
- case HMSG_GET_CHASSIS:
- if (ctl_msg_pack_structure(STRUCT_LLDPD_CHASSIS,
- hardware->h_rchassis,
- sizeof(struct lldpd_chassis), s, &p) == -1) {
- LLOG_WARNX("unable to send chassis information for "
- "interface %s for %d", ifname, r->hdr.pid);
- s->hdr.len = -1;
- return;
- }
- break;
- default:
- LLOG_WARNX("don't know what to do");
- s->hdr.len = -1;
- return;
- }
- return;
- }
- }
- LLOG_WARNX("requested interface %s by %d was not found",
- ifname, r->hdr.pid);
- s->hdr.len = -1;
- return;
-}
-
void
lldpd_recv_all(struct lldpd *cfg)
{
struct lldpd_hardware *hardware;
+ struct lldpd_vif *vif;
struct lldpd_client *client, *client_next;
fd_set rfds;
struct timeval tv;
nfds = hardware->h_raw_real;
}
}
+ TAILQ_FOREACH(vif, &cfg->g_vif, vif_entries) {
+ if ((vif->vif_flags & IFF_UP) == 0)
+ continue;
+ FD_SET(vif->vif_raw, &rfds);
+ if (nfds < vif->vif_raw)
+ nfds = vif->vif_raw;
+ }
TAILQ_FOREACH(client, &cfg->g_clients, next) {
FD_SET(client->fd, &rfds);
if (nfds < client->fd)
snmp_timeout();
}
#endif /* USE_SNMP */
+ TAILQ_FOREACH(vif, &cfg->g_vif, vif_entries) {
+ if (!FD_ISSET(vif->vif_raw, &rfds))
+ continue;
+ if ((buffer = (char *)malloc(
+ vif->vif_mtu)) == NULL) {
+ LLOG_WARN("failed to alloc reception buffer");
+ continue;
+ }
+ fromlen = sizeof(from);
+ if ((n = recvfrom(vif->vif_raw,
+ buffer,
+ vif->vif_mtu, 0,
+ (struct sockaddr *)&from,
+ &fromlen)) == -1) {
+ LLOG_WARN("error while receiving frame on vlan %s",
+ vif->vif_ifname);
+ vif->vif_real->h_rx_discarded_cnt++;
+ free(buffer);
+ continue;
+ }
+ if (from.sll_pkttype == PACKET_OUTGOING) {
+ free(buffer);
+ continue;
+ }
+ if (!((cfg->g_multi) &&
+ (vif->vif_real->h_mode != LLDPD_MODE_ANY) &&
+ (lldpd_guess_type(cfg, buffer, n) !=
+ vif->vif_real->h_mode))) {
+ vif->vif_real->h_rx_cnt++;
+ lldpd_decode(cfg, buffer, n, vif->vif_real, 0);
+ }
+
+ free(buffer);
+ }
TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
/* We could have received something on _real_
* interface. However, even in this case, this could be
continue;
}
if (n > 0)
- lldpd_handle_client(cfg, client, buffer, n);
+ client_handle_client(cfg, client, buffer, n);
else
ctl_close(cfg, client->fd); /* Will use TAILQ_REMOVE ! */
free(buffer);
lldpd_send_all(struct lldpd *cfg)
{
struct lldpd_hardware *hardware;
- int i;
+ u_int8_t saved_lladdr[ETHER_ADDR_LEN];
+ int i, altermac;
+
cfg->g_lastsent = time(NULL);
TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
/* Ignore if interface is down */
if ((hardware->h_flags & IFF_UP) == 0)
continue;
+ /* When sending on inactive slaves, just send using a 0:0:0:0:0:0 address */
+ altermac = 0;
+ if ((hardware->h_raw_real > 0) &&
+ (!iface_is_slave_active(cfg, hardware->h_master,
+ hardware->h_ifname))) {
+ altermac = 1;
+ memcpy(saved_lladdr, hardware->h_lladdr, ETHER_ADDR_LEN);
+ memset(hardware->h_lladdr, 0, ETHER_ADDR_LEN);
+ }
+
for (i=0; cfg->g_protocols[i].mode != 0; i++) {
if (!cfg->g_protocols[i].enabled)
continue;
(cfg->g_protocols[i].mode == LLDPD_MODE_LLDP))
cfg->g_protocols[i].send(cfg, &cfg->g_lchassis, hardware);
}
+ /* Restore MAC if needed */
+ if (altermac)
+ memcpy(hardware->h_lladdr, saved_lladdr, ETHER_ADDR_LEN);
}
}
+#ifdef ENABLE_LLDPMED
+void
+lldpd_med(struct lldpd_chassis *chassis)
+{
+ free(chassis->c_med_hw);
+ free(chassis->c_med_fw);
+ free(chassis->c_med_sn);
+ free(chassis->c_med_manuf);
+ free(chassis->c_med_model);
+ free(chassis->c_med_asset);
+ chassis->c_med_hw = dmi_hw();
+ chassis->c_med_fw = dmi_fw();
+ chassis->c_med_sn = dmi_sn();
+ chassis->c_med_manuf = dmi_manuf();
+ chassis->c_med_model = dmi_model();
+ chassis->c_med_asset = dmi_asset();
+}
+#endif
+
void
lldpd_loop(struct lldpd *cfg)
{
struct ifaddrs *ifap, *ifa;
struct sockaddr_ll *sdl;
struct lldpd_hardware *hardware;
+ struct lldpd_vif *vif;
int f;
char status;
struct utsname *un;
- struct hostent *hp;
+ char *hp;
/* Set system name and description */
if ((un = (struct utsname*)malloc(sizeof(struct utsname))) == NULL)
fatal(NULL);
if (uname(un) != 0)
fatal("failed to get system information");
- if ((hp = gethostbyname(un->nodename)) == NULL)
+ if ((hp = priv_gethostbyname()) == NULL)
fatal("failed to get system name");
free(cfg->g_lchassis.c_name);
free(cfg->g_lchassis.c_descr);
- if (asprintf(&cfg->g_lchassis.c_name, "%s",
- hp->h_name) == -1)
- fatal("failed to set system name");
+ if ((cfg->g_lchassis.c_name = strdup(hp)) == NULL)
+ fatal(NULL);
if (asprintf(&cfg->g_lchassis.c_descr, "%s %s %s %s",
un->sysname, un->release, un->version, un->machine) == -1)
fatal("failed to set system description");
- free(un);
/* Check forwarding */
cfg->g_lchassis.c_cap_enabled = 0;
- if ((f = open("/proc/sys/net/ipv4/ip_forward", 0)) >= 0) {
- if ((read(f, &status, 1) == 1) && (status == '1'))
+ if ((f = priv_open("/proc/sys/net/ipv4/ip_forward")) >= 0) {
+ if ((read(f, &status, 1) == 1) && (status == '1')) {
cfg->g_lchassis.c_cap_enabled = LLDP_CAP_ROUTER;
+ }
close(f);
}
+#ifdef ENABLE_LLDPMED
+ if (cfg->g_lchassis.c_cap_available & LLDP_CAP_TELEPHONE)
+ cfg->g_lchassis.c_cap_enabled |= LLDP_CAP_TELEPHONE;
+ lldpd_med(&cfg->g_lchassis);
+ free(cfg->g_lchassis.c_med_sw);
+ cfg->g_lchassis.c_med_sw = strdup(un->release);
+#endif
+ free(un);
TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries)
hardware->h_flags = 0;
+ TAILQ_FOREACH(vif, &cfg->g_vif, vif_entries)
+ vif->vif_flags = 0;
if (getifaddrs(&ifap) != 0)
fatal("lldpd_loop: failed to get interface list");
}
}
+ if (ifa->ifa_addr == NULL ||
+ ifa->ifa_addr->sa_family != PF_PACKET)
+ continue;
+
+ sdl = (struct sockaddr_ll *)ifa->ifa_addr;
+ if (sdl->sll_hatype != ARPHRD_ETHER || !sdl->sll_halen)
+ continue;
+
if (iface_is_bridge(cfg, ifa->ifa_name)) {
cfg->g_lchassis.c_cap_enabled |= LLDP_CAP_BRIDGE;
continue;
(iface_is_bond(cfg, ifa->ifa_name)))
continue;
- if (ifa->ifa_addr == NULL ||
- ifa->ifa_addr->sa_family != PF_PACKET)
- continue;
-
if (!(ifa->ifa_flags & IFF_MULTICAST))
continue;
- sdl = (struct sockaddr_ll *)ifa->ifa_addr;
- if (sdl->sll_hatype != ARPHRD_ETHER || !sdl->sll_halen)
- continue;
-
if (iface_is_wireless(cfg, ifa->ifa_name))
cfg->g_lchassis.c_cap_enabled |= LLDP_CAP_WLAN;
-
if (lldpd_port_add(cfg, ifa) == NULL)
LLOG_WARNX("failed to allocate port %s, skip it",
ifa->ifa_name);
}
+ /* Handle VLAN */
+ if (cfg->g_listen_vlans) {
+ for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
+ if ((iface_is_vlan(cfg, ifa->ifa_name)) &&
+ (lldpd_port_add_vlan(cfg, ifa) == NULL)) {
+ LLOG_WARNX("unable to allocate vlan %s, skip it",
+ ifa->ifa_name);
+ }
+ }
+ }
+
freeifaddrs(ifap);
lldpd_cleanup(cfg);
lldpd_recv_all(cfg);
}
-void
-lldpd_hangup(int sig)
-{
- /* Re-execute */
- LLOG_INFO("sighup received, reloading");
- lldpd_exit();
- execv(saved_argv[0], saved_argv);
-}
-
void
lldpd_shutdown(int sig)
{
lldpd_exit()
{
struct lldpd_hardware *hardware;
- ctl_cleanup(gcfg->g_ctl, LLDPD_CTL_SOCKET);
+ struct lldpd_vif *vif;
+ close(gcfg->g_ctl);
+ priv_ctl_cleanup();
TAILQ_FOREACH(hardware, &gcfg->g_hardware, h_entries) {
if (INTERFACE_OPENED(hardware))
lldpd_iface_close(gcfg, hardware);
}
+ TAILQ_FOREACH(vif, &gcfg->g_vif, vif_entries) {
+ if (vif->vif_raw != -1)
+ lldpd_iface_close(gcfg, (struct lldpd_hardware*)vif);
+ }
#ifdef USE_SNMP
if (gcfg->g_snmp)
agent_shutdown();
main(int argc, char *argv[])
{
struct lldpd *cfg;
- int ch, snmp = 0, debug = 0;
+ int ch, debug = 0;
+#ifdef USE_SNMP
+ int snmp = 0;
+#endif
char *mgmtp = NULL;
- char *popt, opts[] = "dxm:p:@ ";
- int probe = 0, i, found;
+ char *popt, opts[] = "vdxm:p:M:i@ ";
+ int probe = 0, i, found, vlan = 0;
+#ifdef ENABLE_LLDPMED
+ int lldpmed = 0, noinventory = 0;
+#endif
saved_argv = argv;
*popt = '\0';
while ((ch = getopt(argc, argv, opts)) != -1) {
switch (ch) {
+ case 'v':
+ vlan = 1;
+ break;
case 'd':
debug++;
break;
case 'm':
mgmtp = optarg;
break;
+#ifdef ENABLE_LLDPMED
+ case 'M':
+ lldpmed = atoi(optarg);
+ if ((lldpmed < 1) || (lldpmed > 4)) {
+ fprintf(stderr, "-M requires an argument between 1 and 4\n");
+ usage();
+ }
+ break;
+ case 'i':
+ noinventory = 1;
+ break;
+#else
+ case 'M':
+ case 'i':
+ case 'P':
+ fprintf(stderr, "LLDP-MED support is not built-in\n");
+ usage();
+ break;
+#endif
case 'p':
probe = atoi(optarg);
break;
case 'x':
+#ifdef USE_SNMP
snmp = 1;
+#else
+ fprintf(stderr, "SNMP support is not built-in\n");
+ usage();
+#endif
break;
default:
found = 0;
usage();
}
}
-
+
log_init(debug);
+ if (!debug) {
+ int pid;
+ char *spid;
+ if (daemon(0, 0) != 0)
+ fatal("failed to detach daemon");
+ if ((pid = open(LLDPD_PID_FILE,
+ O_TRUNC | O_CREAT | O_WRONLY, 0644)) == -1)
+ fatal("unable to open pid file " LLDPD_PID_FILE);
+ if (asprintf(&spid, "%d\n", getpid()) == -1)
+ fatal("unable to create pid file " LLDPD_PID_FILE);
+ if (write(pid, spid, strlen(spid)) == -1)
+ fatal("unable to write pid file " LLDPD_PID_FILE);
+ free(spid);
+ close(pid);
+ }
+
+ priv_init(PRIVSEP_CHROOT);
+
if (probe == 0) probe = LLDPD_TTL;
if ((cfg = (struct lldpd *)
calloc(1, sizeof(struct lldpd))) == NULL)
fatal(NULL);
- if (mgmtp != NULL)
- cfg->g_mgmt_pattern = mgmtp;
+ cfg->g_mgmt_pattern = mgmtp;
+ cfg->g_listen_vlans = vlan;
/* Get ioctl socket */
if ((cfg->g_sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
/* Set system capabilities */
cfg->g_lchassis.c_cap_available = LLDP_CAP_BRIDGE | LLDP_CAP_WLAN |
LLDP_CAP_ROUTER;
+#ifdef ENABLE_LLDPMED
+ if (lldpmed > 0) {
+ if (lldpmed == LLDPMED_CLASS_III)
+ cfg->g_lchassis.c_cap_available |= LLDP_CAP_TELEPHONE;
+ cfg->g_lchassis.c_med_type = lldpmed;
+ cfg->g_lchassis.c_med_cap_available = LLDPMED_CAP_CAP |
+ LLDPMED_CAP_IV | LLDPMED_CAP_LOCATION;
+ cfg->g_noinventory = noinventory;
+ }
+#endif
/* Set TTL */
cfg->g_lchassis.c_ttl = LLDPD_TTL;
cfg->g_multi--;
TAILQ_INIT(&cfg->g_hardware);
+ TAILQ_INIT(&cfg->g_vif);
#ifdef USE_SNMP
if (snmp) {
#endif /* USE_SNMP */
/* Create socket */
- if ((cfg->g_ctl = ctl_create(cfg, LLDPD_CTL_SOCKET)) == -1)
- fatal("unable to create control socket " LLDPD_CTL_SOCKET);
+ if ((cfg->g_ctl = priv_ctl_create(cfg)) == -1)
+ fatalx("unable to create control socket " LLDPD_CTL_SOCKET);
+ TAILQ_INIT(&cfg->g_clients);
- if (!debug && daemon(0, 0) != 0) {
- ctl_cleanup(cfg->g_ctl, LLDPD_CTL_SOCKET);
- fatal("failed to detach daemon");
- }
gcfg = cfg;
if (atexit(lldpd_exit) != 0) {
- ctl_cleanup(cfg->g_ctl, LLDPD_CTL_SOCKET);
+ close(cfg->g_ctl);
+ priv_ctl_cleanup();
fatal("unable to set exit function");
}
- if (!debug) {
- int pid;
- char *spid;
- if ((pid = open(LLDPD_PID_FILE,
- O_TRUNC | O_CREAT | O_WRONLY)) == -1)
- fatal("unable to open pid file " LLDPD_PID_FILE);
- if (asprintf(&spid, "%d\n", getpid()) == -1)
- fatal("unable to create pid file " LLDPD_PID_FILE);
- if (write(pid, spid, strlen(spid)) == -1)
- fatal("unable to write pid file " LLDPD_PID_FILE);
- free(spid);
- close(pid);
- }
/* Signal handling */
- signal(SIGHUP, lldpd_hangup);
+ signal(SIGHUP, lldpd_shutdown);
signal(SIGINT, lldpd_shutdown);
signal(SIGTERM, lldpd_shutdown);