--- /dev/null
+SUBDIRS = src man
+dist_doc_DATA = README
--- /dev/null
+lldpd: implementation of IEEE 802.1ab (LLDP)
+--------------------------------------------
+
+LLDP (Link Layer Discovery Protocol) is an industry standard protocol
+designed to supplant proprietary Link-Layer protocols such as
+Extreme's EDP (Extreme Discovery Protocol) and CDP (Cisco Discovery
+Protocol). The goal of LLDP is to provide an inter-vendor compatible
+mechanism to deliver Link-Layer notifications to adjacent network
+devices.
+
+lldpd implements both reception and sending. It also implements an
+SNMP subagent for net-snmp to get local and remote LLDP
+information. The LLDP MIB is partially implemented but the most useful
+tables are here.
+
+lldpd supports bridge, vlan and bonding. bonding need to be done on
+real physical devices, not on bridges, vlans, etc. However, vlans can
+be mapped on the bonding device. You can bridge vlan but not add vlans
+on bridges. More complex setups may give false results.
+
+lldpctl allows to query information collected through the command line.
+
+lldpd also implements CDP (Cisco Discovery Protocol), SONMP (Nortel
+Discovery Protocol) and EDP (Extreme Discovery Protocol). However,
+recent versions of IOS should support LLDP and most Extreme stuff
+support LLDP. When a EDP, CDP or SONMP frame is received on a given
+interface, lldpd starts sending EDP, CDP or SONMP frame on this
+interface. Informations collected through EDP/CDP/SONMP are integrated
+with other informations and can be queried with lldpctl or through
+SNMP.
+
+For bonding, you need 2.6.24 (in previous version, PACKET_ORIGDEV
+affected only non multicast packets). See:
+ http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=80feaacb8a6400a9540a961b6743c69a5896b937
+ http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=8032b46489e50ef8f3992159abd0349b5b8e476c
+
+Otherwise, real device will be choosen randomly.
+
+On 2.6.27, we are able to receive packets on real interface for bonded
+devices. This allows to get neighbor information on active/backup
+bonds. Without the 2.6.27, lldpd won't receive any information on
+inactive slaves. Here are the patchs (thanks to Joe Eykholt):
+ http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=0d7a3681232f545c6a59f77e60f7667673ef0e93
+ http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=cc9bd5cebc0825e0fabc0186ab85806a0891104f
+ http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=f982307f22db96201e41540295f24e8dcc10c78f
+
+More information:
+ http://en.wikipedia.org/wiki/LLDP
+ http://standards.ieee.org/getieee802/download/802.1AB-2005.pdf
+ http://wiki.wireshark.org/LinkLayerDiscoveryProtocol
+
+lldpd is distributed under the following license:
+ * Permission to use, copy, modify, and 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.
+
+OpenLLDP is another implementation of LLDP. It supports several OS
+(Windows, Mac OS X, *BSD) and LLDP-MED extensions but does not include
+an SNMP subagent or the support for CDP/SONMP.
+ http://openlldp.sourceforge.net/
--- /dev/null
+# -*- Autoconf -*-
+# Process this file with autoconf to produce a configure script.
+
+AC_PREREQ(2.61)
+AC_INIT(lldpd, 0.1, bernat@luffy.cx)
+AM_INIT_AUTOMAKE([foreign])
+AC_CONFIG_SRCDIR([src/lldpd.c])
+AC_CONFIG_HEADER([config.h])
+AC_CONFIG_FILES([Makefile src/Makefile man/Makefile])
+
+# Checks for programs.
+AC_PROG_CC
+
+# Checks for libraries.
+AC_ARG_WITH(snmp,
+ AC_HELP_STRING(
+ [--with-snmp],
+ [Enable the use of SNMP]
+ ),
+ [],
+ [with_snmp=no]
+)
+AM_CONDITIONAL([USE_SNMP], [test "${with_snmp}" != "no"])
+
+# Checks for header files.
+AC_CHECK_DECLS([TAILQ_FIRST, TAILQ_NEXT, TAILQ_FOREACH, TAILQ_EMPTY],[],[],[[#include <sys/queue.h>]])
+AC_CHECK_DECL([PACKET_ORIGDEV],[],[],[[#include <linux/if_packet.h>]])
+AC_CHECK_DECL([ADVERTISED_2500baseX_Full],[],[],[[#include <linux/ethtool.h>]])
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_C_CONST
+AC_CHECK_TYPES([int16_t, u_int16_t, int8_t, u_int8_t, int32_t, u_int32_t],[],[AC_MSG_ERROR([mandatory type not found])])
+
+# Checks for library functions.
+AC_REPLACE_FUNCS([strlcpy])
+
+AC_PROG_GCC_TRADITIONAL
+
+## NetSNMP
+NETSNMP_CONFIG=No
+if test "${with_snmp}" != "no"; then
+ AC_PATH_TOOL([NETSNMP_CONFIG], [net-snmp-config], No)
+fi
+if test x"${NETSNMP_CONFIG}" != xNo; then
+ NETSNMP_libs=`${NETSNMP_CONFIG} --agent-libs`
+
+ AC_CHECK_LIB([netsnmp], [snmp_register_callback],
+ AC_DEFINE_UNQUOTED([HAVE_NETSNMP], 1, [Define to indicate the Net-SNMP library])
+ AC_DEFINE_UNQUOTED([USE_SNMP], 1, [Define to indicate to enable SNMP support]),
+ [], ${NETSNMP_libs})
+
+ if test "${ac_cv_lib_netsnmp_snmp_register_callback}" = "yes"; then
+ AC_SUBST([NETSNMP_LIB],"${NETSNMP_libs}")
+ fi
+fi
+if test "${with_snmp}" != "no"; then
+ if test "${ac_cv_lib_netsnmp_snmp_register_callback}" != "yes"; then
+ AC_MSG_NOTICE([***])
+ AC_MSG_NOTICE([*** net-snmp libraries not found])
+ AC_MSG_NOTICE([*** Either correct the installation, or run configure])
+ AC_MSG_NOTICE([*** including --without-snmp])
+ exit 1
+ fi
+fi
+
+AC_OUTPUT
--- /dev/null
+lldpd (0+20080718cvs) UNRELEASED; urgency=low
+
+ * Initial release
+
+ -- Vincent Bernat <bernat@debian.org> Fri, 18 Jul 2008 11:20:34 +0200
--- /dev/null
+Source: lldpd
+Section: net
+Priority: optional
+Maintainer: Vincent Bernat <bernat@debian.org>
+Build-Depends: debhelper (>= 5), cdbs, autotools-dev, libsnmp15-dev | libsnmp9-dev | libsnmp-dev
+Standards-Version: 3.8.0
+
+Package: lldpd
+Architecture: any
+Depends: ${shlibs:Depends}, ${misc:Depends}
+Description: implementation of IEEE 802.1ab (LLDP)
+ This implementation provides LLDP sending and reception, supports
+ VLAN and includes an SNMP subagent that can interface to an SNMP
+ agent through AgentX protocol.
+ .
+ LLDP is an industry standard protocol designed to supplant
+ proprietary Link-Layer protocols such as Extreme's EDP (Extreme
+ Discovery Protocol) and CDP (Cisco Discovery Protocol). The goal of
+ LLDP is to provide an inter-vendor compatible mechanism to deliver
+ Link-Layer notifications to adjacent network devices.
+ .
+ This daemon is also able to deal with CDP, SONMP and EDP protocol.
--- /dev/null
+Files: *
+Copyright: © 2008 Vincent Bernat <bernat@luffy.cx>
+ © 2003, 2004 Henning Brauer <henning@openbsd.org>
+ © 2006 Pierre-Yves Ritschard <pyr@openbsd.org>
+License: MIT
+ Permission to use, copy, modify, and 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.
+
+On Debian systems, the complete text of the MIT can be found in
+`/usr/share/common-licenses/MIT`.
--- /dev/null
+# Uncomment to start SNMP subagent and enable CDP, SONMP and EDP protocol
+#DAEMON_ARGS="-x -c -s -e"
--- /dev/null
+#! /bin/sh
+### BEGIN INIT INFO
+# Provides: skeleton
+# Required-Start: $remote_fs $network
+# Required-Stop: $network $remote_fs
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: LLDP daemon
+### END INIT INFO
+
+# Do NOT "set -e"
+
+# PATH should only include /usr/* if it runs after the mountnfs.sh script
+PATH=/sbin:/usr/sbin:/bin:/usr/bin
+DESC="LLDP daemon"
+NAME=lldpd
+DAEMON=/usr/sbin/$NAME
+DAEMON_ARGS=""
+PIDFILE=/var/run/$NAME.pid
+SCRIPTNAME=/etc/init.d/$NAME
+
+# Exit if the package is not installed
+[ -x "$DAEMON" ] || exit 0
+
+# Read configuration variable file if it is present
+[ -r /etc/default/$NAME ] && . /etc/default/$NAME
+
+[ -f /lib/init/vars.sh ] && . /lib/init/vars.sh
+. /lib/lsb/init-functions
+
+do_start()
+{
+ start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
+ || return 1
+ start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \
+ $DAEMON_ARGS \
+ || return 2
+}
+
+do_stop()
+{
+ start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME
+ RETVAL="$?"
+ [ "$RETVAL" = 2 ] && return 2
+ start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
+ [ "$?" = 2 ] && return 2
+ rm -f $PIDFILE
+ return "$RETVAL"
+}
+
+do_reload() {
+ start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME
+ return 0
+}
+
+case "$1" in
+ start)
+ [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
+ do_start
+ case "$?" in
+ 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
+ 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
+ esac
+ ;;
+ stop)
+ [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
+ do_stop
+ case "$?" in
+ 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
+ 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
+ esac
+ ;;
+ reload)
+ log_daemon_msg "Reloading $DESC" "$NAME"
+ do_reload
+ log_end_msg $?
+ ;;
+ restart|force-reload)
+ log_daemon_msg "Restarting $DESC" "$NAME"
+ do_stop
+ case "$?" in
+ 0|1)
+ do_start
+ case "$?" in
+ 0) log_end_msg 0 ;;
+ 1) log_end_msg 1 ;; # Old process is still running
+ *) log_end_msg 1 ;; # Failed to start
+ esac
+ ;;
+ *)
+ # Failed to stop
+ log_end_msg 1
+ ;;
+ esac
+ ;;
+ *)
+ echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2
+ exit 3
+ ;;
+esac
+
+:
--- /dev/null
+#!/usr/bin/make -f
+
+include /usr/share/cdbs/1/rules/debhelper.mk
+include /usr/share/cdbs/1/class/autotools.mk
+
+DEB_CONFIGURE_EXTRA_FLAGS = --with-snmp
--- /dev/null
+man_MANS = lldpd.8 lldpctl.8
--- /dev/null
+.\" Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org>
+.\" Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
+.\"
+.\" Permission to use, copy, modify, and 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.
+.\"
+.Dd $Mdocdate: July 16 2008 $
+.Dt LLDPCTL 8
+.Os
+.Sh NAME
+.Nm lldpctl
+.Nd control the LLDP daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl d
+.Sh DESCRIPTION
+The
+.Nm
+program controls
+.Xr lldpd 8
+daemon.
+.Pp
+Currently,
+.Nm
+is only able to display the list of discovered neighbors along with
+some of their advertised capabilities.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl d
+Enable more debugging information.
+.El
+.Sh FILES
+.Bl -tag width "/var/run/lldpd.socketXX" -compact
+.It /var/run/lldpd.socket
+Unix-domain socket used for communication with
+.Xr lldpd 8 .
+.El
+.Sh SEE ALSO
+.Xr lldpd 8
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+program was written by
+.An Vincent Bernat Aq bernat@luffy.cx .
--- /dev/null
+.\" Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org>
+.\" Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
+.\"
+.\" Permission to use, copy, modify, and 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.
+.\"
+.Dd $Mdocdate: August 21 2008 $
+.Dt LLDPD 8
+.Os
+.Sh NAME
+.Nm lldpd
+.Nd LLDP daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl dxcse
+.Op Fl m Ar management
+.Op Fl p Ar probe time
+.Sh DESCRIPTION
+.Nm
+is a daemon able to receive and send
+.Em LLDP
+frames. The Link Layer Discovery Protocol is a vendor-neutral Layer 2
+protocol that allows a network device to advertise its identity and
+capabilities on the local network.
+.Pp
+.Nm
+also implements an SNMP subagent using AgentX protocol to interface to
+a regular SNMP agent like Net-SNMP. To enable this subagent, you need
+something like that in your
+.Xr snmpd.conf 5 :
+.Bd -literal -offset indent
+master agentx
+.Ed
+.Pp
+This daemon implements both reception and sending. It will collect
+various information to send LLDP frames to all Ethernet interfaces,
+including management address, speed and VLAN names.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl d
+Do not daemonize.
+If this option is specified,
+.Nm
+will run in the foreground and log to
+.Em stderr .
+This option can be specified many times to increase verbosity.
+.It Fl x
+Enable SNMP subagent
+With this option,
+.Nm
+will enable an SNMP subagent using AgentX protocol. This allows to get
+information about local system and remote systems through SNMP.
+.It Fl c
+Enable the support of CDP protocol to deal with Cisco routers that do
+not speak LLDP.
+.It Fl s
+Enable the support of SONMP protocol to deal with Nortel routers and
+switches that do not speak LLDP.
+.It Fl e
+Enable the support of EDP protocol to deal with Extreme routers and
+switches that do not speak LLDP.
+.It Fl m Ar management
+Specify the management address of this system.
+.Nm
+only sends one management address. It will use the first one that it
+finds or the one that you specify with this option. This option can
+use wildcards.
+.It Fl p Ar probe time
+Specify the time to wait (in seconds) before accepting a given
+protocol. This time will be used by
+.Nm
+to detect false positives like SONMP frames running through a switch
+only supporting CDP. This value is only used when multiple protocols
+are enabled.
+.El
+.Sh FILES
+.Bl -tag width "/var/run/lldpd.socketXX" -compact
+.It /var/run/lldpd.socket
+Unix-domain socket used for communication with
+.Xr lldpctl 8 .
+.El
+.Sh SEE ALSO
+.Xr lldpctl 8 ,
+.Xr snmpd 8
+.Sh HISTORY
+The
+.Nm
+program is inspired from a preliminary work of Reyk Floeter.
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+program was written by
+.An Pierre-Yves Ritschard Aq pyr@openbsd.org ,
+and
+.An Vincent Bernat Aq bernat@luffy.cx .
--- /dev/null
+#include <err.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <netpacket/packet.h>
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <arpa/inet.h>
+
+int main() {
+ int s;
+ struct ifreq ifr;
+ const unsigned char lldpaddr[] = {0x01,0x80,0xC2,0x00,0x00,0x0E};
+
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+ err(1, "Unable to open socket");
+ bzero(&ifr, sizeof(ifr));
+ strncpy(ifr.ifr_name, "lan", IFNAMSIZ);
+ bcopy(lldpaddr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
+
+ if (ioctl(s, SIOCADDMULTI, &ifr) < 0)
+ err(1, "Unable to ioctl");
+ return 0;
+}
--- /dev/null
+/* gcc -c test-align.c -S -o test-align.s */
+
+#include <sys/types.h>
+
+struct test1 {
+ char a;
+ char b;
+} ;
+
+struct test2 {
+ char a;
+ u_int16_t b;
+} ;
+
+struct test3 {
+ char a;
+ u_int32_t b;
+} ;
+
+struct test4 {
+ char a;
+ u_int64_t b;
+} ;
+
+struct test5 {
+ char a;
+ void *b;
+} ;
+
+struct test6 {
+ char a;
+ u_int16_t b;
+ char c;
+ char d;
+ char e;
+ char f;
+ char g;
+} ;
+
+
+struct test1 test_1 = { 10, 20};
+struct test2 test_2 = { 10, 20};
+struct test3 test_3 = { 10, 20};
+struct test4 test_4 = { 10, 20};
+struct test5 test_5 = { 10, (void*)&test_4};
+struct test6 test_6 = { 10, 15, 20, 30, 40, 50, 60};
+
+int word_align = sizeof(struct test2) - sizeof(u_int16_t);
+int long_align = sizeof(struct test3) - sizeof(u_int32_t);
+int long_long_align = sizeof(struct test4) - sizeof(u_int64_t);
+int pointer_align = sizeof(struct test5) - sizeof(void*);
+
+int size = sizeof(struct test6);
+
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <err.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <netpacket/packet.h>
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <arpa/inet.h>
+
+#include "../lldp.h"
+
+#define IF "lan"
+#define LLDPMAC {0x01,0x80,0xC2,0x00,0x00,0x0E}
+#define MTU 1500
+
+int set_multi(int add) {
+ int s;
+ struct ifreq ifr;
+ const char lldpaddr[] = LLDPMAC;
+
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+ return -1;
+ bzero(&ifr, sizeof(ifr));
+ strncpy(ifr.ifr_name, IF, IFNAMSIZ);
+ bcopy(lldpaddr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
+
+ if (ioctl(s, add ? SIOCADDMULTI : SIOCDELMULTI, &ifr) < 0)
+ return -1;
+ return 0;
+}
+
+void dump(const char *frame, int s) {
+ int i = 0;
+ while (s != i) {
+ printf("%02hhx ", frame[i++]);
+ if (i % 20 == 0)
+ printf("\n");
+ }
+ printf("\n");
+}
+
+int check_mac(const char *frame, int s) {
+ const char lldpaddr[] = LLDPMAC;
+ if (s < sizeof(lldpaddr))
+ return -1;
+ if (memcmp(frame, lldpaddr, sizeof(lldpaddr)) == 0) {
+ return 1;
+ }
+ return -1;
+}
+
+int check_protocol(const char *frame, int s) {
+ const char proto[] = { 0x88, 0xcc };
+ if (s < 2 * ETH_ALEN + 2)
+ return -1;
+ if (memcmp(frame + 2 * ETH_ALEN, proto, sizeof(proto)) == 0) {
+ return 1;
+ }
+ return -1;
+}
+
+int check_tlv_end(const char *frame, int s) {
+ if (s != 0) {
+ warnx("End of LLDPDU is too large (%d > 0)", s);
+ return -1;
+ }
+ return 0;
+}
+
+int check_tlv_chassisid(const char *frame, int s) {
+ u_int8_t subtype;
+ if (s < 2) {
+ warnx("Chassis ID TLV too small (%d < 2)", s);
+ return -1;
+ }
+ subtype = *(u_int8_t*)frame;
+ switch (subtype) {
+ case LLDP_CHASSISID_SUBTYPE_CHASSIS:
+ case LLDP_CHASSISID_SUBTYPE_IFALIAS:
+ case LLDP_CHASSISID_SUBTYPE_PORT:
+ case LLDP_CHASSISID_SUBTYPE_ADDR:
+ case LLDP_CHASSISID_SUBTYPE_IFNAME:
+ case LLDP_CHASSISID_SUBTYPE_LOCAL:
+ printf("Unhandled chassis ID type (%x)\n", subtype);
+ break;
+ case LLDP_CHASSISID_SUBTYPE_LLADDR:
+ if (s != 7) {
+ warnx("Incorrect MAC address size (%d != 6)", s-1);
+ return -1;
+ }
+ printf("Chassis mac address:\n %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n",
+ *(char *)(frame + 1),
+ *(char *)(frame + 2),
+ *(char *)(frame + 3),
+ *(char *)(frame + 4),
+ *(char *)(frame + 5),
+ *(char *)(frame + 6));
+ break;
+ default:
+ warnx("Unknown Chassis ID subtype (%x)", subtype);
+ }
+ return 0;
+}
+
+int check_tlv_portid(const char *frame, int s) {
+ u_int8_t subtype;
+ if (s < 2) {
+ warnx("Port ID TLV too small (%d < 2)", s);
+ return -1;
+ }
+ subtype = *(u_int8_t*)frame;
+ switch (subtype) {
+ case LLDP_PORTID_SUBTYPE_IFALIAS:
+ case LLDP_PORTID_SUBTYPE_PORT:
+ case LLDP_PORTID_SUBTYPE_ADDR:
+ case LLDP_PORTID_SUBTYPE_IFNAME:
+ case LLDP_PORTID_SUBTYPE_LOCAL:
+ printf("Unhandled Port ID type (%x)\n", subtype);
+ break;
+ case LLDP_PORTID_SUBTYPE_LLADDR:
+ if (s != 7) {
+ warnx("Incorrect MAC address size (%d != 6)", s-1);
+ return -1;
+ }
+ printf("Port mac address:\n %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n",
+ *(char *)(frame + 1),
+ *(char *)(frame + 2),
+ *(char *)(frame + 3),
+ *(char *)(frame + 4),
+ *(char *)(frame + 5),
+ *(char *)(frame + 6));
+ break;
+ default:
+ warnx("Unknown Port ID subtype (%x)", subtype);
+ }
+ return 0;
+}
+
+int check_tlv_ttl(const char *frame, int s) {
+ if (s != 2) {
+ warnx("Incorrect TTL length (%d != 2)", s);
+ return -1;
+ }
+ printf("TTL:\n %d\n", ntohs(*(u_int16_t*)frame));
+ return 0;
+}
+
+int check_tlv_simplestring(const char *frame, int s, char *what) {
+ char *desc;
+ if (s < 1) {
+ warnx("Incorrect %s length (%d < 1)", what, s);
+ return -1;
+ }
+ if (!(desc = (char *)malloc(s+1))) {
+ warnx("Not able to allocate memory");
+ return -1;
+ }
+ strncpy(desc, frame, s);
+ desc[s] = 0;
+ printf("%s:\n %s\n", what, desc);
+ free(desc);
+ return 0;
+}
+
+int check_tlv_portdescr(const char *frame, int s) {
+ return check_tlv_simplestring(frame, s, "Port description");
+}
+
+int check_tlv_systemname(const char *frame, int s) {
+ return check_tlv_simplestring(frame, s, "System name");
+}
+
+int check_tlv_systemdescr(const char *frame, int s) {
+ return check_tlv_simplestring(frame, s, "System description");
+}
+
+int check_tlv_systemcap(const char *frame, int s) {
+ if (s != 4) {
+ warnx("Incorrect system capabilities length (%d != 4)", s);
+ return -1;
+ }
+ printf("System capabilities (available/enabled):\n %x %x\n",
+ ntohs(*(u_int16_t*)frame),
+ ntohs(*(u_int16_t*)(frame+2)));
+ return 0;
+}
+
+int check_tlv_manaddr(const char *frame, int s) {
+ return 0;
+}
+
+int check_tlv_dot1(const char *frame, int s) {
+ int subtype;
+ int l;
+ char *vlanname;
+ if (s < 1) {
+ warnx("DOT1 frame too short (%d < 1)", s);
+ return -1;
+ }
+ subtype = *(u_int8_t*)frame;
+ switch (subtype) {
+ case LLDP_TLV_DOT1_PPVID:
+ case LLDP_TLV_DOT1_PI:
+ warnx("Unhandled dot1 subtype");
+ break;
+ case LLDP_TLV_DOT1_PVID:
+ if (s < 3) {
+ warnx("DOT1 PVID frame too short (%d < 3)", s);
+ return -1;
+ }
+ printf("PVID:\n %d\n", ntohs(*(u_int16_t*)(frame + 1)));
+ break;
+ case LLDP_TLV_DOT1_VLANNAME:
+ if (s < 4) {
+ warnx("DOT1 VLAN name frame too short (%d < 4)", s);
+ return -1;
+ }
+ l = *(u_int8_t*)(frame + 3);
+ if (s < 4 + l) {
+ warnx("DOT1 VLAN name frame too short (%d < 4 + %d)", s, l);
+ return -1;
+ }
+ vlanname = (char *)malloc(l + 1);
+ strncpy(vlanname, frame+4, l);
+ vlanname[l] = 0;
+ printf("VLAN name/id:\n %s/%d\n", vlanname, ntohs(*(u_int16_t*)(frame + 1)));
+ break;
+ default:
+ warnx("Unknown dot1 subtype (%d)", subtype);
+ return -1;
+ }
+ return 0;
+}
+
+int check_tlv_dot3(const char *frame, int s) {
+ warnx("Do nothing for dot3");
+ return 0;
+}
+
+int check_tlv_org(const char *frame, int s) {
+ char dot1[] = LLDP_TLV_ORG_DOT1;
+ char dot3[] = LLDP_TLV_ORG_DOT3;
+ if (s < 3) {
+ warnx("Frame too short (3)");
+ return -1;
+ }
+ if (memcmp(dot1, frame, 3) == 0)
+ return check_tlv_dot1(frame + 3, s - 3);
+ if (memcmp(dot3, frame, 3) == 0)
+ return check_tlv_dot3(frame + 3, s - 3);
+
+ warnx("Unknown org code");
+ return -1;
+}
+
+int check_tlv(const char *frame, int offset, int s) {
+ int (*sub_tlv[])(const char *frame, int s) = {
+ check_tlv_end,
+ check_tlv_chassisid,
+ check_tlv_portid,
+ check_tlv_ttl,
+ check_tlv_portdescr,
+ check_tlv_systemname,
+ check_tlv_systemdescr,
+ check_tlv_systemcap,
+ check_tlv_manaddr,
+ NULL };
+ int size;
+ int type;
+ int i = 0;
+ int rc = 0;
+
+ if (offset + 2 > s) {
+ warnx("Frame too short (1)");
+ return -1;
+ }
+ size = ntohs(*(u_int16_t*)(frame + offset)) & 0x1ff;
+ type = ntohs(*(u_int16_t*)(frame + offset)) >> 9;
+ if (offset + size > s) {
+ warnx("Frame too short (2)");
+ return -1;
+ }
+
+ switch (type) {
+ case LLDP_TLV_ORG:
+ rc = check_tlv_org(frame + offset + 2, size);
+ break;
+ default:
+ while (sub_tlv[i] != NULL) {
+ if (type == i) {
+ rc = (*sub_tlv[i])(frame + offset + 2, size);
+ break;
+ } else i++;
+ }
+ if (sub_tlv[i] == NULL) {
+ warnx("Unknown TLV type (%x)", type);
+ return -1;
+ }
+ }
+
+ if (rc < 0)
+ return rc;
+ return offset + size + 2;
+}
+
+int main() {
+ int s, l, i;
+ struct sockaddr_ll sa;
+ char frame[MTU];
+
+ if (set_multi(1) < 0)
+ err(1, "Unable to set multicast address");
+
+ if ((s = socket(PF_PACKET, SOCK_RAW, htons(0x88cc))) < 0) {
+ warn("Unable to create socket");
+ goto end;
+ }
+
+ bzero(&sa, sizeof(sa));
+ sa.sll_family = AF_PACKET;
+ sa.sll_protocol = 0;
+ sa.sll_ifindex = if_nametoindex(IF);
+ if (bind(s, (struct sockaddr*)&sa, sizeof(sa)) < 0) {
+ warn("Unable to bind");
+ goto end;
+ }
+
+ while (1) {
+ l = recv(s, frame, MTU, 0);
+ dump(frame, l);
+ if (check_mac(frame, l) < 0) {
+ warnx("Not LLDP MAC address");
+ continue;
+ }
+ if (check_protocol(frame, l) < 0) {
+ warnx("Not LLDP protocol");
+ continue;
+ }
+ i = 2 * ETH_ALEN + 2;
+ while (i < l) {
+ i = check_tlv(frame, i, l);
+ if (i < 0)
+ break;
+ }
+ }
+
+
+ end:
+ if (set_multi(0) < 0)
+ err(1, "Unable to unset multicast address");
+
+ return 0;
+}
--- /dev/null
+/* gcc -Wall $(net-snmp-config --base-cflags) test-snmp.c $(net-snmp-config --agent-libs) -o test-snmp */
+
+#include <sys/queue.h>
+
+#define USING_AGENTX_SUBAGENT_MODULE 1
+
+#include <net-snmp/net-snmp-config.h>
+#include <net-snmp/net-snmp-includes.h>
+#include <net-snmp/agent/net-snmp-agent-includes.h>
+#include <net-snmp/agent/snmp_vars.h>
+
+#include "../lldp.h"
+
+typedef struct lldpGlobal {
+ int32_t messageTxInterval;
+ int32_t messageTxHoldMultiplier;
+ int32_t reinitDelay;
+ int32_t txDelay;
+ int32_t notificationInterval;
+} lldpGlobal;
+
+struct lldpGlobal global = {
+ .messageTxInterval = 30,
+ .messageTxHoldMultiplier = 4,
+ .reinitDelay = 2,
+ .txDelay = 5,
+ .notificationInterval = 5
+};
+
+oid messageTxInterval_oid[] = {1, 0, 8802, 1, 1, 2, 1, 1, 1, 0};
+oid messageTxHoldMultiplier_oid[] = {1, 0, 8802, 1, 1, 2, 1, 1, 2, 0};
+oid reinitDelay_oid[] = {1, 0, 8802, 1, 1, 2, 1, 1, 3, 0};
+oid txDelay_oid[] = {1, 0, 8802, 1, 1, 2, 1, 1, 4, 0};
+oid notificationInterval_oid[] = {1, 0, 8802, 1, 1, 2, 1, 1, 5, 0};
+
+typedef struct lldpStats {
+ u_int32_t lastChangeTime;
+ u_int32_t inserts;
+ u_int32_t deletes;
+ u_int32_t drops;
+ u_int32_t ageouts;
+} lldpStats;
+
+struct lldpStats stats = {
+ .lastChangeTime = 4575120,
+ .inserts = 1451,
+ .deletes = 12,
+ .drops = 0,
+ .ageouts = 2
+};
+
+oid lastChangeTime_oid[] = {1, 0, 8802, 1, 1, 2, 1, 2, 1, 0};
+oid inserts_oid[] = {1, 0, 8802, 1, 1, 2, 1, 2, 2, 0};
+oid deletes_oid[] = {1, 0, 8802, 1, 1, 2, 1, 2, 3, 0};
+oid drops_oid[] = {1, 0, 8802, 1, 1, 2, 1, 2, 4, 0};
+oid ageouts_oid[] = {1, 0, 8802, 1, 1, 2, 1, 2, 5, 0};
+
+typedef struct lldpChassis {
+ int chassisIdSubtype;
+ u_int8_t chassisId[256];
+ int chassisId_len;
+ char sysName[256];
+ char sysDesc[256];
+ u_int8_t sysCapSupported;
+ u_int8_t sysCapEnabled;
+} lldpChassis;
+
+struct lldpChassis local = {
+ .chassisIdSubtype = LLDP_CHASSISID_SUBTYPE_LLADDR,
+ .chassisId = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 },
+ .chassisId_len = 6,
+ .sysName = "neo.luffy.cx",
+ .sysDesc = "Linux neo 2.6.25-2-amd64 #1 SMP Thu Jun 12 15:38:32 UTC 2008 x86_64 GNU/Linux",
+ .sysCapSupported = LLDP_CAP_BRIDGE | LLDP_CAP_WLAN | LLDP_CAP_ROUTER,
+ .sysCapEnabled = LLDP_CAP_ROUTER
+};
+
+oid chassisIdSubtype_oid[] = {1, 0, 8802, 1, 1, 2, 1, 3, 1, 0};
+oid chassisId_oid[] = {1, 0, 8802, 1, 1, 2, 1, 3, 2, 0};
+oid sysName_oid[] = {1, 0, 8802, 1, 1, 2, 1, 3, 3, 0};
+oid sysDesc_oid[] = {1, 0, 8802, 1, 1, 2, 1, 3, 4, 0};
+oid sysCapSupported_oid[] = {1, 0, 8802, 1, 1, 2, 1, 3, 5, 0};
+oid sysCapEnabled_oid[] = {1, 0, 8802, 1, 1, 2, 1, 3, 6, 0};
+
+typedef struct snmp_type {
+ u_char *value;
+ int *value_size;
+ int type;
+} snmp_type;
+
+struct lldpPort {
+ int portIdSubtype;
+ u_int8_t portId[256];
+ int portId_len;
+ char portDesc[256];
+} lldpPort;
+
+struct lldpRemote {
+ /* Index values */
+ u_int32_t lldpRemTimeMark;
+ int lldpRemLocalPortNum;
+ int lldpRemIndex;
+
+ struct lldpPort port;
+ struct lldpChassis remote;
+
+ TAILQ_ENTRY(lldpRemote) next;
+} lldpRemote;
+
+TAILQ_HEAD(, lldpRemote) r_entries;
+
+struct lldpRemote r1 = {
+ .lldpRemTimeMark = 4121,
+ .lldpRemLocalPortNum = 1,
+ .lldpRemIndex = 1,
+ .port = {
+ .portIdSubtype = LLDP_PORTID_SUBTYPE_LLADDR,
+ .portId = { 0x00, 0x05, 0x06, 0x07, 0x0a, 0x01 },
+ .portId_len = 6,
+ .portDesc = "eth0"
+ },
+ .remote = {
+ .chassisIdSubtype = LLDP_CHASSISID_SUBTYPE_LLADDR,
+ .chassisId = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x06 },
+ .chassisId_len = 6,
+ .sysName = "titi.luffy.cx",
+ .sysDesc = "Linux titi 2.6.25-2-amd64 #1 SMP Thu Jun 12 15:38:32 UTC 2008 x86_64 GNU/Linux",
+ .sysCapSupported = LLDP_CAP_BRIDGE | LLDP_CAP_WLAN | LLDP_CAP_ROUTER,
+ .sysCapEnabled = LLDP_CAP_ROUTER
+ }
+};
+
+struct lldpRemote r2 = {
+ .lldpRemTimeMark = 4127,
+ .lldpRemLocalPortNum = 3,
+ .lldpRemIndex = 2,
+ .port = {
+ .portIdSubtype = LLDP_PORTID_SUBTYPE_LLADDR,
+ .portId = { 0x00, 0x05, 0x06, 0x07, 0x0a, 0x03 },
+ .portId_len = 6,
+ .portDesc = "en4"
+ },
+ .remote = {
+ .chassisIdSubtype = LLDP_CHASSISID_SUBTYPE_LLADDR,
+ .chassisId = { 0x07, 0x01, 0x02, 0x03, 0x04, 0x06 },
+ .chassisId_len = 6,
+ .sysName = "tito.luffy.cx",
+ .sysDesc = "Linux tito 2.6.25-2-amd64 #1 SMP Thu Jun 12 15:38:32 UTC 2008 x86_64 GNU/Linux",
+ .sysCapSupported = LLDP_CAP_BRIDGE | LLDP_CAP_WLAN | LLDP_CAP_ROUTER,
+ .sysCapEnabled = LLDP_CAP_ROUTER
+ }
+};
+
+int lldpRemTable_handler(
+ netsnmp_mib_handler *handler,
+ netsnmp_handler_registration *reginfo,
+ netsnmp_agent_request_info *reqinfo,
+ netsnmp_request_info *requests) {
+
+ netsnmp_request_info *request;
+ netsnmp_table_request_info *table_info;
+ struct lldpRemote *table_entry;
+
+ switch (reqinfo->mode) {
+ case MODE_GET:
+ for (request=requests; request; request=request->next) {
+ table_entry = (struct lldpRemote *)
+ netsnmp_extract_iterator_context(request);
+ table_info = netsnmp_extract_table_info(request);
+
+ if (!table_entry) {
+ netsnmp_set_request_error(reqinfo, request,
+ SNMP_NOSUCHINSTANCE);
+ continue;
+ }
+
+ switch (table_info->colnum) {
+ case 4:
+ snmp_set_var_typed_integer(request->requestvb, ASN_INTEGER,
+ table_entry->remote.chassisIdSubtype);
+ break;
+ case 5:
+ snmp_set_var_typed_value(request->requestvb, ASN_OCTET_STR,
+ (u_char*)table_entry->remote.chassisId,
+ table_entry->remote.chassisId_len);
+ break;
+ case 6:
+ snmp_set_var_typed_integer(request->requestvb, ASN_INTEGER,
+ table_entry->port.portIdSubtype);
+ break;
+ case 7:
+ snmp_set_var_typed_value(request->requestvb, ASN_OCTET_STR,
+ (u_char*)table_entry->port.portId,
+ table_entry->port.portId_len);
+ break;
+ case 8:
+ snmp_set_var_typed_value(request->requestvb, ASN_OCTET_STR,
+ (u_char*)table_entry->port.portDesc,
+ strlen(table_entry->port.portDesc));
+ break;
+ case 9:
+ snmp_set_var_typed_value(request->requestvb, ASN_OCTET_STR,
+ (u_char*)table_entry->remote.sysName,
+ strlen(table_entry->remote.sysName));
+ break;
+ case 10:
+ snmp_set_var_typed_value(request->requestvb, ASN_OCTET_STR,
+ (u_char*)table_entry->remote.sysDesc,
+ strlen(table_entry->remote.sysDesc));
+ break;
+ case 11:
+ snmp_set_var_typed_value( request->requestvb, ASN_OCTET_STR,
+ (u_char*)&(table_entry->remote.sysCapSupported),
+ 1);
+ break;
+ case 12:
+ snmp_set_var_typed_value( request->requestvb, ASN_OCTET_STR,
+ (u_char*)&(table_entry->remote.sysCapEnabled),
+ 1);
+ break;
+ default:
+ netsnmp_set_request_error(reqinfo, request,
+ SNMP_NOSUCHOBJECT);
+ break;
+ }
+ }
+ break;
+
+ }
+ return SNMP_ERR_NOERROR;
+}
+
+netsnmp_variable_list *lldpRemTable_get_next_data_point(void **my_loop_context,
+ void **my_data_context,
+ netsnmp_variable_list *put_index_data,
+ netsnmp_iterator_info *mydata) {
+ struct lldpRemote *entry = (struct lldpRemote*)*my_loop_context;
+ netsnmp_variable_list *idx = put_index_data;
+
+ if (entry) {
+ snmp_set_var_typed_integer( idx, ASN_TIMETICKS, entry->lldpRemTimeMark );
+ idx = idx->next_variable;
+ snmp_set_var_typed_integer( idx, ASN_INTEGER, entry->lldpRemLocalPortNum );
+ idx = idx->next_variable;
+ snmp_set_var_typed_integer( idx, ASN_INTEGER, entry->lldpRemIndex );
+ idx = idx->next_variable;
+ *my_data_context = (void *)entry;
+ *my_loop_context = TAILQ_NEXT(entry, next);
+ return put_index_data;
+ } else {
+ return NULL;
+ }
+}
+
+netsnmp_variable_list *lldpRemTable_get_first_data_point(void **my_loop_context,
+ void **my_data_context,
+ netsnmp_variable_list *put_index_data,
+ netsnmp_iterator_info *mydata) {
+ *my_loop_context = TAILQ_FIRST(&r_entries);
+ return lldpRemTable_get_next_data_point(my_loop_context, my_data_context,
+ put_index_data, mydata);
+}
+
+void populate_r_entries() {
+ static oid lldpRemTable_oid[] = {1,0,8802,1,1,2,1,4,1};
+ size_t lldpRemTable_oid_len = OID_LENGTH(lldpRemTable_oid);
+ netsnmp_handler_registration *reg;
+ netsnmp_iterator_info *iinfo;
+ netsnmp_table_registration_info *table_info;
+
+ reg = netsnmp_create_handler_registration(
+ "lldpRemTable", lldpRemTable_handler,
+ lldpRemTable_oid, lldpRemTable_oid_len,
+ HANDLER_CAN_RONLY
+ );
+
+ table_info = SNMP_MALLOC_TYPEDEF( netsnmp_table_registration_info );
+ netsnmp_table_helper_add_indexes(table_info,
+ ASN_TIMETICKS, /* index: lldpRemTimeMark */
+ ASN_INTEGER, /* index: lldpRemLocalPortNum */
+ ASN_INTEGER, /* index: lldpRemIndex */
+ 0);
+ table_info->min_column = 1;
+ table_info->max_column = 12;
+
+ iinfo = SNMP_MALLOC_TYPEDEF( netsnmp_iterator_info );
+ iinfo->get_first_data_point = lldpRemTable_get_first_data_point;
+ iinfo->get_next_data_point = lldpRemTable_get_next_data_point;
+ iinfo->table_reginfo = table_info;
+
+ netsnmp_register_table_iterator( reg, iinfo );
+
+ TAILQ_INIT(&r_entries);
+ TAILQ_INSERT_TAIL(&r_entries, &r1, next);
+ TAILQ_INSERT_TAIL(&r_entries, &r2, next);
+}
+
+int netsnmp_instance_universal_handler(netsnmp_mib_handler *handler,
+ netsnmp_handler_registration *reginfo,
+ netsnmp_agent_request_info *reqinfo,
+ netsnmp_request_info *requests) {
+ struct snmp_type *st = (struct snmp_type *) handler->myvoid;
+ int size;
+
+ switch (reqinfo->mode) {
+ case MODE_GET:
+ if (st->value_size == NULL) {
+ /* Try to guess */
+ switch (st->type) {
+ case ASN_COUNTER:
+ case ASN_TIMETICKS:
+ case ASN_INTEGER:
+ case ASN_GAUGE:
+ size = 4;
+ break;
+ case ASN_OCTET_STR:
+ size = strlen((char*)st->value);
+ break;
+ default:
+ size = 1;
+ }
+ } else
+ size = *(st->value_size);
+ snmp_set_var_typed_value(requests->requestvb, st->type,
+ st->value, size);
+ break;
+ default:
+ snmp_log(LOG_ERR,
+ "netsnmp_instance_universal_handler: illegal mode\n");
+ netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_GENERR);
+ return SNMP_ERR_NOERROR;
+ }
+ if (handler->next && handler->next->access_method)
+ return netsnmp_call_next_handler(handler, reginfo, reqinfo,
+ requests);
+ return SNMP_ERR_NOERROR;
+}
+
+int netsnmp_register_read_only_universal_instance(const char *name,
+ oid * reg_oid,
+ size_t reg_oid_len,
+ void *value,
+ int *value_size,
+ int type) {
+ netsnmp_handler_registration *myreg;
+ struct snmp_type *st;
+
+ /* We will leak memory... */
+ st = (struct snmp_type *)malloc(sizeof(struct snmp_type));
+ st->value = value;
+ st->value_size = value_size;
+ st->type = type;
+
+ myreg =
+ netsnmp_create_handler_registration(name,
+ netsnmp_instance_universal_handler,
+ reg_oid, reg_oid_len,
+ HANDLER_CAN_RONLY);
+ myreg->handler->myvoid = (void *) st;
+
+ return netsnmp_register_read_only_instance(myreg);
+}
+
+#define REGISTER(variable, name, type) \
+ netsnmp_register_read_only_universal_instance(#name, \
+ name ## _oid, \
+ OID_LENGTH(name ## _oid), \
+ &variable.name, NULL, type)
+
+#define REGISTER_S(variable, name, type) \
+ netsnmp_register_read_only_universal_instance(#name, \
+ name ## _oid, \
+ OID_LENGTH(name ## _oid), \
+ &variable.name, &variable.name ## _len, type)
+
+#define REGISTER_FS(variable, name, size, type) \
+ netsnmp_register_read_only_universal_instance(#name, \
+ name ## _oid, \
+ OID_LENGTH(name ## _oid), \
+ &variable.name, &size, type)
+
+int one = 1;
+int two = 2;
+int three = 3;
+int interfaces[] = {1, 2, 3, 4};
+char stuff[] = { 0xf0 };
+
+void register_lldpPortConfig() {
+ int i;
+ static oid lldpPortConfigTable_oid[] = {1,0,8802,1,1,2,1,1,6};
+ size_t lldpPortConfigTable_oid_len = OID_LENGTH(lldpPortConfigTable_oid);
+ netsnmp_table_data_set *table_set;
+ netsnmp_table_row *row;
+ table_set = netsnmp_create_table_data_set("lldpPortConfigTable");
+ netsnmp_table_set_add_indexes(table_set,
+ ASN_INTEGER,
+ 0);
+ netsnmp_table_set_multi_add_default_row(table_set,
+ 2, ASN_INTEGER, 0, NULL, 0,
+ 3, ASN_INTEGER, 0, NULL, 0,
+ 4, ASN_OCTET_STR, 0, NULL, 0,
+ 0);
+ netsnmp_register_table_data_set(
+ netsnmp_create_handler_registration("lldpPortConfigTable", NULL,
+ lldpPortConfigTable_oid,
+ lldpPortConfigTable_oid_len,
+ HANDLER_CAN_RONLY),
+ table_set, NULL);
+ for (i=0; i < sizeof(interfaces)/sizeof(int); i++) {
+ row = netsnmp_create_table_data_row();
+ netsnmp_table_row_add_index(row, ASN_INTEGER, (u_char*)(&interfaces[i]), sizeof(int));
+ netsnmp_set_row_column(row, 2, ASN_INTEGER, (char*)&three, sizeof(three));
+ netsnmp_set_row_column(row, 3, ASN_INTEGER, (char*)&two, sizeof(two));
+ netsnmp_set_row_column(row, 4, ASN_OCTET_STR, stuff, 1);
+
+ netsnmp_table_dataset_add_row(table_set, row);
+ }
+ netsnmp_register_auto_data_table(table_set, NULL);
+
+}
+
+int main (int argc, char **argv) {
+
+ netsnmp_enable_subagent();
+ snmp_disable_log();
+ snmp_enable_stderrlog();
+
+ init_agent("lldpAgent");
+
+ REGISTER(global, messageTxInterval, ASN_INTEGER);
+ REGISTER(global, messageTxHoldMultiplier, ASN_INTEGER);
+ REGISTER(global, reinitDelay, ASN_INTEGER);
+ REGISTER(global, txDelay, ASN_INTEGER);
+ REGISTER(global, notificationInterval, ASN_INTEGER);
+
+ REGISTER(stats, lastChangeTime, ASN_TIMETICKS);
+ REGISTER(stats, inserts, ASN_GAUGE);
+ REGISTER(stats, deletes, ASN_GAUGE);
+ REGISTER(stats, drops, ASN_GAUGE);
+ REGISTER(stats, ageouts, ASN_GAUGE);
+
+ REGISTER(local, chassisIdSubtype, ASN_INTEGER);
+ REGISTER_S(local, chassisId, ASN_OCTET_STR);
+ REGISTER(local, sysName, ASN_OCTET_STR);
+ REGISTER(local, sysDesc, ASN_OCTET_STR);
+ REGISTER_FS(local, sysCapSupported, one, ASN_OCTET_STR);
+ REGISTER_FS(local, sysCapEnabled, one, ASN_OCTET_STR);
+
+ register_lldpPortConfig();
+
+ populate_r_entries();
+
+ init_snmp("lldpAgent");
+
+ while(1)
+ agent_check_and_process(1);
+
+ snmp_shutdown("lldpAgent");
+
+ return 0;
+}
+
+
--- /dev/null
+sbin_PROGRAMS = lldpd lldpctl
+
+COMMON = log.c ctl.c lldpd.h lldp.h cdp.h compat.h sonmp.h llc.h edp.h
+lldpd_SOURCES = lldpd.c lldp.c cdp.c sonmp.c edp.c iov.c features.c $(COMMON)
+lldpctl_SOURCES = lldpctl.c $(COMMON)
+
+lldpd_LDADD = @LIBOBJS@
+lldpctl_LDADD = @LIBOBJS@
+
+if USE_SNMP
+lldpd_SOURCES += agent.c
+lldpd_LDADD += @NETSNMP_LIB@
+endif
--- /dev/null
+/*
+ * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and 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 "lldpd.h"
+
+#include <net-snmp/net-snmp-config.h>
+#include <net-snmp/net-snmp-includes.h>
+#include <net-snmp/agent/net-snmp-agent-includes.h>
+#include <net-snmp/agent/snmp_vars.h>
+#include <net-snmp/agent/util_funcs.h>
+
+static oid lldp_oid[] = {1, 0, 8802, 1, 1, 2};
+
+/* For net-snmp */
+extern int register_sysORTable(oid *, size_t, const char *);
+extern int unregister_sysORTable(oid *, size_t);
+extern struct timeval starttime;
+
+/* Global variable because no way to pass it as argument. Should not be used
+ * elsewhere. */
+struct lldpd *scfg;
+
+static inline uint8_t
+swap_bits(uint8_t n)
+{
+ n = ((n&0xF0) >>4 ) | ( (n&0x0F) <<4);
+ n = ((n&0xCC) >>2 ) | ( (n&0x33) <<2);
+ n = ((n&0xAA) >>1 ) | ( (n&0x55) <<1);
+
+ return n;
+};
+
+struct lldpd_hardware*
+header_portindexed_table(struct variable *vp, oid *name, size_t *length,
+ int exact, size_t *var_len, WriteMethod **write_method)
+{
+ struct lldpd_hardware *hardware, *phardware = NULL;
+ unsigned int port, aport = 0, distance;
+
+ if (header_simple_table(vp, name, length, exact, var_len, write_method, -1))
+ return NULL;
+
+ port = name[*length - 1];
+ distance = -1;
+ TAILQ_FOREACH(hardware, &scfg->g_hardware, h_entries) {
+ if (INTERFACE_OPENED(hardware)) {
+ aport = if_nametoindex(hardware->h_ifname);
+ if (aport == port) {
+ /* Exact match */
+ return hardware;
+ }
+ if (aport < port)
+ continue;
+ if (aport - port < distance) {
+ phardware = hardware;
+ distance = aport - port;
+ }
+ }
+ }
+ if (phardware == NULL)
+ return NULL;
+ if (exact)
+ return NULL;
+ if (distance == -1)
+ return NULL;
+ aport = distance + port;
+ name[*length - 1] = aport;
+ return phardware;
+}
+
+struct lldpd_hardware*
+header_tprindexed_table(struct variable *vp, oid *name, size_t *length,
+ int exact, size_t *var_len, WriteMethod **write_method, int withip)
+{
+ struct lldpd_hardware *hardware, *phardware = NULL;
+ oid *target, current[9], best[9];
+ int result, target_len, oid_len;
+ int i;
+
+ if ((result = snmp_oid_compare(name, *length, vp->name, vp->namelen)) < 0) {
+ memcpy(name, vp->name, sizeof(oid) * vp->namelen);
+ *length = vp->namelen;
+ }
+
+ *write_method = 0;
+ *var_len = sizeof(long);
+
+ oid_len = (withip) ? 9:3;
+ for (i = 0; i < oid_len; i++) best[i] = MAX_SUBID;
+ target = &name[vp->namelen];
+ target_len = *length - vp->namelen;
+ TAILQ_FOREACH(hardware, &scfg->g_hardware, h_entries) {
+ if ((INTERFACE_OPENED(hardware)) && (hardware->h_rchassis != NULL)) {
+ if (withip && (hardware->h_rchassis->c_mgmt.s_addr == INADDR_ANY))
+ continue;
+ current[0] = (hardware->h_rlastchange - starttime.tv_sec)*100;
+ current[1] = if_nametoindex(hardware->h_ifname);
+ current[2] = hardware->h_rid;
+ if (withip) {
+ current[3] = 1;
+ current[4] = 4;
+ current[8] = hardware->h_rchassis->c_mgmt.s_addr >> 24;
+ current[7] = (hardware->h_rchassis->c_mgmt.s_addr & 0xffffff) >> 16;
+ current[6] = (hardware->h_rchassis->c_mgmt.s_addr & 0xffff) >> 8;
+ current[5] = hardware->h_rchassis->c_mgmt.s_addr & 0xff;
+ }
+ if ((result = snmp_oid_compare(current, oid_len, target,
+ target_len)) < 0)
+ continue;
+ if ((result == 0) && !exact)
+ continue;
+ if (result == 0)
+ return hardware;
+ if (snmp_oid_compare(current, oid_len, best, oid_len) < 0) {
+ memcpy(best, current, sizeof(oid) * oid_len);
+ phardware = hardware;
+ }
+ }
+ }
+ if (phardware == NULL)
+ return NULL;
+ if (exact)
+ return NULL;
+ for (i = 0; i < oid_len; i++)
+ if (best[i] != MAX_SUBID) break;
+ if (i == oid_len)
+ return NULL;
+ memcpy(target, best, sizeof(oid) * oid_len);
+ *length = vp->namelen + oid_len;
+
+ return phardware;
+}
+
+struct lldpd_vlan*
+header_pvindexed_table(struct variable *vp, oid *name, size_t *length,
+ int exact, size_t *var_len, WriteMethod **write_method)
+{
+ struct lldpd_hardware *hardware;
+ struct lldpd_vlan *vlan, *pvlan = NULL;
+ oid *target, current[2], best[2];
+ int result, target_len;
+
+ if ((result = snmp_oid_compare(name, *length, vp->name, vp->namelen)) < 0) {
+ memcpy(name, vp->name, sizeof(oid) * vp->namelen);
+ *length = vp->namelen;
+ }
+
+ *write_method = 0;
+ *var_len = sizeof(long);
+
+ best[0] = best[1] = MAX_SUBID;
+ target = &name[vp->namelen];
+ target_len = *length - vp->namelen;
+ TAILQ_FOREACH(hardware, &scfg->g_hardware, h_entries) {
+ if (INTERFACE_OPENED(hardware)) {
+ TAILQ_FOREACH(vlan, &hardware->h_lport.p_vlans, v_entries) {
+ current[0] = if_nametoindex(hardware->h_ifname);
+ current[1] = vlan->v_vid;
+ if ((result = snmp_oid_compare(current, 2, target,
+ target_len)) < 0)
+ continue;
+ if ((result == 0) && !exact)
+ continue;
+ if (result == 0)
+ return vlan;
+ if (snmp_oid_compare(current, 2, best, 2) < 0) {
+ memcpy(best, current, sizeof(oid) * 2);
+ pvlan = vlan;
+ }
+ }
+ }
+ }
+ if (pvlan == NULL)
+ return NULL;
+ if (exact)
+ return NULL;
+ if ((best[0] == best[1]) &&
+ (best[0] == MAX_SUBID))
+ return NULL;
+ memcpy(target, best, sizeof(oid) * 2);
+ *length = vp->namelen + 2;
+
+ return pvlan;
+}
+
+struct lldpd_vlan*
+header_tprvindexed_table(struct variable *vp, oid *name, size_t *length,
+ int exact, size_t *var_len, WriteMethod **write_method)
+{
+ struct lldpd_hardware *hardware;
+ struct lldpd_vlan *vlan, *pvlan = NULL;
+ oid *target, current[4], best[4];
+ int result, target_len;
+
+ if ((result = snmp_oid_compare(name, *length, vp->name, vp->namelen)) < 0) {
+ memcpy(name, vp->name, sizeof(oid) * vp->namelen);
+ *length = vp->namelen;
+ }
+
+ *write_method = 0;
+ *var_len = sizeof(long);
+
+ best[0] = best[1] = best[2] = best[3] = MAX_SUBID;
+ target = &name[vp->namelen];
+ target_len = *length - vp->namelen;
+ TAILQ_FOREACH(hardware, &scfg->g_hardware, h_entries) {
+ if ((INTERFACE_OPENED(hardware)) && (hardware->h_rport != NULL)) {
+ TAILQ_FOREACH(vlan, &hardware->h_rport->p_vlans, v_entries) {
+ current[0] = (hardware->h_rlastchange - starttime.tv_sec)*100;
+ current[1] = if_nametoindex(hardware->h_ifname);
+ current[2] = hardware->h_rid;
+ current[3] = vlan->v_vid;
+ if ((result = snmp_oid_compare(current, 4, target,
+ target_len)) < 0)
+ continue;
+ if ((result == 0) && !exact)
+ continue;
+ if (result == 0)
+ return vlan;
+ if (snmp_oid_compare(current, 4, best, 4) < 0) {
+ memcpy(best, current, sizeof(oid) * 4);
+ pvlan = vlan;
+ }
+ }
+ }
+ }
+ if (pvlan == NULL)
+ return NULL;
+ if (exact)
+ return NULL;
+ if ((best[0] == best[1]) && (best[1] == best[2]) &&
+ (best[2] == best[3]) && (best[0] == MAX_SUBID))
+ return NULL;
+ memcpy(target, best, sizeof(oid) * 4);
+ *length = vp->namelen + 4;
+
+ return pvlan;
+}
+
+/* Scalars */
+#define LLDP_SNMP_TXINTERVAL 1
+#define LLDP_SNMP_TXMULTIPLIER 2
+#define LLDP_SNMP_REINITDELAY 3
+#define LLDP_SNMP_TXDELAY 4
+#define LLDP_SNMP_NOTIFICATION 5
+#define LLDP_SNMP_LASTUPDATE 6
+#define LLDP_SNMP_STATS_INSERTS 7
+#define LLDP_SNMP_STATS_DELETES 8
+#define LLDP_SNMP_STATS_DROPS 9
+#define LLDP_SNMP_STATS_AGEOUTS 10
+/* Local chassis */
+#define LLDP_SNMP_LOCAL_CIDSUBTYPE 1
+#define LLDP_SNMP_LOCAL_CID 2
+#define LLDP_SNMP_LOCAL_SYSNAME 3
+#define LLDP_SNMP_LOCAL_SYSDESCR 4
+#define LLDP_SNMP_LOCAL_SYSCAP_SUP 5
+#define LLDP_SNMP_LOCAL_SYSCAP_ENA 6
+/* Stats */
+#define LLDP_SNMP_STATS_TX_PORTNUM 1
+#define LLDP_SNMP_STATS_TX 2
+#define LLDP_SNMP_STATS_RX_PORTNUM 3
+#define LLDP_SNMP_STATS_RX_DISCARDED 4
+#define LLDP_SNMP_STATS_RX_ERRORS 5
+#define LLDP_SNMP_STATS_RX 6
+#define LLDP_SNMP_STATS_RX_TLVDISCARDED 7
+#define LLDP_SNMP_STATS_RX_TLVUNRECOGNIZED 8
+#define LLDP_SNMP_STATS_RX_AGEOUTS 9
+/* Local ports */
+#define LLDP_SNMP_LOCAL_PORTNUM 1
+#define LLDP_SNMP_LOCAL_PIDSUBTYPE 2
+#define LLDP_SNMP_LOCAL_PID 3
+#define LLDP_SNMP_LOCAL_PORTDESC 4
+#define LLDP_SNMP_LOCAL_DOT3_AUTONEG_SUPPORT 5
+#define LLDP_SNMP_LOCAL_DOT3_AUTONEG_ENABLED 6
+#define LLDP_SNMP_LOCAL_DOT3_AUTONEG_ADVERTISED 7
+#define LLDP_SNMP_LOCAL_DOT3_AUTONEG_MAU 8
+#define LLDP_SNMP_LOCAL_DOT3_AGG_STATUS 9
+#define LLDP_SNMP_LOCAL_DOT3_AGG_ID 10
+/* Remote ports */
+#define LLDP_SNMP_REMOTE_CIDSUBTYPE 1
+#define LLDP_SNMP_REMOTE_CID 2
+#define LLDP_SNMP_REMOTE_PIDSUBTYPE 3
+#define LLDP_SNMP_REMOTE_PID 4
+#define LLDP_SNMP_REMOTE_PORTDESC 5
+#define LLDP_SNMP_REMOTE_SYSNAME 6
+#define LLDP_SNMP_REMOTE_SYSDESC 7
+#define LLDP_SNMP_REMOTE_SYSCAP_SUP 8
+#define LLDP_SNMP_REMOTE_SYSCAP_ENA 9
+#define LLDP_SNMP_REMOTE_DOT3_AUTONEG_SUPPORT 10
+#define LLDP_SNMP_REMOTE_DOT3_AUTONEG_ENABLED 11
+#define LLDP_SNMP_REMOTE_DOT3_AUTONEG_ADVERTISED 12
+#define LLDP_SNMP_REMOTE_DOT3_AUTONEG_MAU 13
+#define LLDP_SNMP_REMOTE_DOT3_AGG_STATUS 14
+#define LLDP_SNMP_REMOTE_DOT3_AGG_ID 15
+/* Local vlans */
+#define LLDP_SNMP_LOCAL_DOT1_VLANNAME 1
+/* Remote vlans */
+#define LLDP_SNMP_REMOTE_DOT1_VLANNAME 1
+/* Management address */
+#define LLDP_SNMP_LOCAL_ADDR_LEN 1
+#define LLDP_SNMP_LOCAL_ADDR_IFSUBTYPE 2
+#define LLDP_SNMP_LOCAL_ADDR_IFID 3
+#define LLDP_SNMP_LOCAL_ADDR_OID 4
+#define LLDP_SNMP_REMOTE_ADDR_IFSUBTYPE 5
+#define LLDP_SNMP_REMOTE_ADDR_IFID 6
+#define LLDP_SNMP_REMOTE_ADDR_OID 7
+
+static u_char*
+agent_h_scalars(struct variable *vp, oid *name, size_t *length,
+ int exact, size_t *var_len, WriteMethod **write_method)
+{
+ static unsigned long long_ret;
+ struct lldpd_hardware *hardware;
+
+ if (header_generic(vp, name, length, exact, var_len, write_method))
+ return NULL;
+
+ switch (vp->magic) {
+ case LLDP_SNMP_TXINTERVAL:
+ long_ret = scfg->g_delay;
+ return (u_char *)&long_ret;
+ case LLDP_SNMP_TXMULTIPLIER:
+ long_ret = scfg->g_lchassis.c_ttl / scfg->g_delay;
+ return (u_char *)&long_ret;
+ case LLDP_SNMP_REINITDELAY:
+ long_ret = 1;
+ return (u_char *)&long_ret;
+ case LLDP_SNMP_TXDELAY:
+ long_ret = LLDPD_TX_MSGDELAY;
+ return (u_char *)&long_ret;
+ case LLDP_SNMP_NOTIFICATION:
+ long_ret = 5;
+ return (u_char *)&long_ret;
+ case LLDP_SNMP_LASTUPDATE:
+ long_ret = 0;
+ TAILQ_FOREACH(hardware, &scfg->g_hardware, h_entries)
+ if (hardware->h_rlastchange > long_ret)
+ long_ret = hardware->h_rlastchange;
+ if (long_ret)
+ long_ret = (long_ret - starttime.tv_sec) * 100;
+ return (u_char *)&long_ret;
+ case LLDP_SNMP_STATS_INSERTS:
+ /* We assume this is equal to valid frames received on all ports */
+ long_ret = 0;
+ TAILQ_FOREACH(hardware, &scfg->g_hardware, h_entries)
+ long_ret += hardware->h_rx_cnt;
+ return (u_char *)&long_ret;
+ case LLDP_SNMP_STATS_AGEOUTS:
+ long_ret = 0;
+ TAILQ_FOREACH(hardware, &scfg->g_hardware, h_entries)
+ long_ret += hardware->h_rx_ageout_cnt;
+ return (u_char *)&long_ret;
+ case LLDP_SNMP_STATS_DELETES:
+ long_ret = 0;
+ TAILQ_FOREACH(hardware, &scfg->g_hardware, h_entries)
+ long_ret += hardware->h_rx_ageout_cnt +
+ hardware->h_rx_cnt?(hardware->h_rx_cnt - 1):0;
+ return (u_char *)&long_ret;
+ case LLDP_SNMP_STATS_DROPS:
+ /* We assume that we never have insufficient resources */
+ long_ret = 0;
+ return (u_char *)&long_ret;
+ default:
+ break;
+ }
+ return NULL;
+}
+
+static u_char*
+agent_h_local_chassis(struct variable *vp, oid *name, size_t *length,
+ int exact, size_t *var_len, WriteMethod **write_method)
+{
+ static uint8_t bit;
+ static unsigned long long_ret;
+
+ if (header_generic(vp, name, length, exact, var_len, write_method))
+ return NULL;
+
+ switch (vp->magic) {
+ case LLDP_SNMP_LOCAL_CIDSUBTYPE:
+ long_ret = scfg->g_lchassis.c_id_subtype;
+ return (u_char *)&long_ret;
+ case LLDP_SNMP_LOCAL_CID:
+ *var_len = scfg->g_lchassis.c_id_len;
+ return (u_char *)scfg->g_lchassis.c_id;
+ case LLDP_SNMP_LOCAL_SYSNAME:
+ *var_len = strlen(scfg->g_lchassis.c_name);
+ return (u_char *)scfg->g_lchassis.c_name;
+ case LLDP_SNMP_LOCAL_SYSDESCR:
+ *var_len = strlen(scfg->g_lchassis.c_descr);
+ return (u_char *)scfg->g_lchassis.c_descr;
+ case LLDP_SNMP_LOCAL_SYSCAP_SUP:
+ *var_len = 1;
+ bit = swap_bits(scfg->g_lchassis.c_cap_available);
+ return (u_char *)&bit;
+ case LLDP_SNMP_LOCAL_SYSCAP_ENA:
+ *var_len = 1;
+ bit = swap_bits(scfg->g_lchassis.c_cap_enabled);
+ return (u_char *)&bit;
+ default:
+ break;
+ }
+ return NULL;
+}
+
+static u_char*
+agent_h_stats(struct variable *vp, oid *name, size_t *length,
+ int exact, size_t *var_len, WriteMethod **write_method)
+{
+ static unsigned long long_ret;
+ struct lldpd_hardware *hardware;
+
+ if ((hardware = header_portindexed_table(vp, name, length,
+ exact, var_len, write_method)) == NULL)
+ return NULL;
+
+ switch (vp->magic) {
+ case LLDP_SNMP_STATS_TX:
+ long_ret = hardware->h_tx_cnt;
+ return (u_char *)&long_ret;
+ case LLDP_SNMP_STATS_RX:
+ long_ret = hardware->h_rx_cnt;
+ return (u_char *)&long_ret;
+ case LLDP_SNMP_STATS_RX_DISCARDED:
+ case LLDP_SNMP_STATS_RX_ERRORS:
+ long_ret = hardware->h_rx_discarded_cnt;
+ return (u_char *)&long_ret;
+ case LLDP_SNMP_STATS_RX_TLVDISCARDED:
+ case LLDP_SNMP_STATS_RX_TLVUNRECOGNIZED:
+ /* Not really handled */
+ long_ret = 0;
+ return (u_char *)&long_ret;
+ case LLDP_SNMP_STATS_RX_AGEOUTS:
+ long_ret = hardware->h_rx_ageout_cnt;
+ return (u_char *)&long_ret;
+ default:
+ break;
+ }
+ return NULL;
+}
+
+static u_char*
+agent_h_local_port(struct variable *vp, oid *name, size_t *length,
+ int exact, size_t *var_len, WriteMethod **write_method)
+{
+ static uint8_t bit;
+ struct lldpd_hardware *hardware;
+ static unsigned long long_ret;
+
+ if ((hardware = header_portindexed_table(vp, name, length,
+ exact, var_len, write_method)) == NULL)
+ return NULL;
+
+ switch (vp->magic) {
+ case LLDP_SNMP_LOCAL_PIDSUBTYPE:
+ long_ret = hardware->h_lport.p_id_subtype;
+ return (u_char *)&long_ret;
+ case LLDP_SNMP_LOCAL_PID:
+ *var_len = hardware->h_lport.p_id_len;
+ return (u_char *)hardware->h_lport.p_id;
+ case LLDP_SNMP_LOCAL_PORTDESC:
+ *var_len = strlen(hardware->h_lport.p_descr);
+ return (u_char *)hardware->h_lport.p_descr;
+ case LLDP_SNMP_LOCAL_DOT3_AUTONEG_SUPPORT:
+ long_ret = 2 - hardware->h_lport.p_autoneg_support;
+ return (u_char *)&long_ret;
+ case LLDP_SNMP_LOCAL_DOT3_AUTONEG_ENABLED:
+ long_ret = 2 - hardware->h_lport.p_autoneg_enabled;
+ return (u_char *)&long_ret;
+ case LLDP_SNMP_LOCAL_DOT3_AUTONEG_ADVERTISED:
+ *var_len = 2;
+ return (u_char *)&hardware->h_lport.p_autoneg_advertised;
+ case LLDP_SNMP_LOCAL_DOT3_AUTONEG_MAU:
+ long_ret = hardware->h_lport.p_mau_type;
+ return (u_char *)&long_ret;
+ case LLDP_SNMP_LOCAL_DOT3_AGG_STATUS:
+ bit = swap_bits((hardware->h_lport.p_aggregid > 0) ? 3 : 0);
+ *var_len = 1;
+ return (u_char *)&bit;
+ case LLDP_SNMP_LOCAL_DOT3_AGG_ID:
+ long_ret = hardware->h_lport.p_aggregid;
+ return (u_char *)&long_ret;
+ default:
+ break;
+ }
+ return NULL;
+}
+
+static u_char*
+agent_h_local_vlan(struct variable *vp, oid *name, size_t *length,
+ int exact, size_t *var_len, WriteMethod **write_method)
+{
+ struct lldpd_vlan *vlan;
+
+ if ((vlan = header_pvindexed_table(vp, name, length,
+ exact, var_len, write_method)) == NULL)
+ return NULL;
+
+ switch (vp->magic) {
+ case LLDP_SNMP_LOCAL_DOT1_VLANNAME:
+ *var_len = strlen(vlan->v_name);
+ return (u_char *)vlan->v_name;
+ default:
+ break;
+ }
+ return NULL;
+}
+
+static u_char*
+agent_h_remote_vlan(struct variable *vp, oid *name, size_t *length,
+ int exact, size_t *var_len, WriteMethod **write_method)
+{
+ struct lldpd_vlan *vlan;
+
+ if ((vlan = header_tprvindexed_table(vp, name, length,
+ exact, var_len, write_method)) == NULL)
+ return NULL;
+
+ switch (vp->magic) {
+ case LLDP_SNMP_REMOTE_DOT1_VLANNAME:
+ *var_len = strlen(vlan->v_name);
+ return (u_char *)vlan->v_name;
+ default:
+ break;
+ }
+ return NULL;
+}
+
+static u_char*
+agent_h_remote_port(struct variable *vp, oid *name, size_t *length,
+ int exact, size_t *var_len, WriteMethod **write_method)
+{
+ struct lldpd_hardware *hardware;
+ static uint8_t bit;
+ static unsigned long long_ret;
+
+ if ((hardware = header_tprindexed_table(vp, name, length,
+ exact, var_len, write_method, 0)) == NULL)
+ return NULL;
+
+ switch (vp->magic) {
+ case LLDP_SNMP_REMOTE_CIDSUBTYPE:
+ long_ret = hardware->h_rchassis->c_id_subtype;
+ return (u_char *)&long_ret;
+ case LLDP_SNMP_REMOTE_CID:
+ *var_len = hardware->h_rchassis->c_id_len;
+ return (u_char *)hardware->h_rchassis->c_id;
+ case LLDP_SNMP_REMOTE_PIDSUBTYPE:
+ long_ret = hardware->h_rport->p_id_subtype;
+ return (u_char *)&long_ret;
+ case LLDP_SNMP_REMOTE_PID:
+ *var_len = hardware->h_rport->p_id_len;
+ return (u_char *)hardware->h_rport->p_id;
+ case LLDP_SNMP_REMOTE_PORTDESC:
+ *var_len = strlen(hardware->h_rport->p_descr);
+ return (u_char *)hardware->h_rport->p_descr;
+ case LLDP_SNMP_REMOTE_SYSNAME:
+ *var_len = strlen(hardware->h_rchassis->c_name);
+ return (u_char *)hardware->h_rchassis->c_name;
+ case LLDP_SNMP_REMOTE_SYSDESC:
+ *var_len = strlen(hardware->h_rchassis->c_descr);
+ return (u_char *)hardware->h_rchassis->c_descr;
+ case LLDP_SNMP_REMOTE_SYSCAP_SUP:
+ *var_len = 1;
+ bit = swap_bits(hardware->h_rchassis->c_cap_available);
+ return (u_char *)&bit;
+ case LLDP_SNMP_REMOTE_SYSCAP_ENA:
+ *var_len = 1;
+ bit = swap_bits(hardware->h_rchassis->c_cap_enabled);
+ return (u_char *)&bit;
+ case LLDP_SNMP_REMOTE_DOT3_AUTONEG_SUPPORT:
+ long_ret = 2 - hardware->h_rport->p_autoneg_support;
+ return (u_char *)&long_ret;
+ case LLDP_SNMP_REMOTE_DOT3_AUTONEG_ENABLED:
+ long_ret = 2 - hardware->h_rport->p_autoneg_enabled;
+ return (u_char *)&long_ret;
+ case LLDP_SNMP_REMOTE_DOT3_AUTONEG_ADVERTISED:
+ *var_len = 2;
+ return (u_char *)&hardware->h_rport->p_autoneg_advertised;
+ case LLDP_SNMP_REMOTE_DOT3_AUTONEG_MAU:
+ long_ret = hardware->h_rport->p_mau_type;
+ return (u_char *)&long_ret;
+ case LLDP_SNMP_REMOTE_DOT3_AGG_STATUS:
+ bit = swap_bits((hardware->h_rport->p_aggregid > 0) ? 3 : 0);
+ *var_len = 1;
+ return (u_char *)&bit;
+ case LLDP_SNMP_REMOTE_DOT3_AGG_ID:
+ long_ret = hardware->h_rport->p_aggregid;
+ return (u_char *)&long_ret;
+ default:
+ break;
+ }
+ return NULL;
+}
+
+static u_char*
+agent_management(struct variable *vp, size_t *var_len, struct lldpd_chassis *chassis)
+{
+ static unsigned long int long_ret;
+ static oid zeroDotZero[2] = {0, 0};
+
+ switch (vp->magic) {
+ case LLDP_SNMP_LOCAL_ADDR_LEN:
+ long_ret = 5;
+ return (u_char*)&long_ret;
+ case LLDP_SNMP_LOCAL_ADDR_IFSUBTYPE:
+ case LLDP_SNMP_REMOTE_ADDR_IFSUBTYPE:
+ if (chassis->c_mgmt_if != 0)
+ long_ret = LLDP_MGMT_IFACE_IFINDEX;
+ else
+ long_ret = 1;
+ return (u_char*)&long_ret;
+ case LLDP_SNMP_LOCAL_ADDR_IFID:
+ case LLDP_SNMP_REMOTE_ADDR_IFID:
+ long_ret = chassis->c_mgmt_if;
+ return (u_char*)&long_ret;
+ case LLDP_SNMP_LOCAL_ADDR_OID:
+ case LLDP_SNMP_REMOTE_ADDR_OID:
+ *var_len = sizeof(zeroDotZero);
+ return (u_char*)zeroDotZero;
+ default:
+ break;
+ }
+ return NULL;
+}
+
+static u_char*
+agent_h_local_management(struct variable *vp, oid *name, size_t *length,
+ int exact, size_t *var_len, WriteMethod **write_method)
+{
+ oid *target, best[6];
+ int result, target_len;
+
+ if (scfg->g_lchassis.c_mgmt.s_addr == INADDR_ANY)
+ return NULL;
+
+ if ((result = snmp_oid_compare(name, *length, vp->name, vp->namelen)) < 0) {
+ memcpy(name, vp->name, sizeof(oid) * vp->namelen);
+ *length = vp->namelen;
+ }
+
+ *write_method = 0;
+ *var_len = sizeof(long);
+
+ target = &name[vp->namelen];
+ target_len = *length - vp->namelen;
+
+ best[0] = 1;
+ best[1] = 4;
+ best[5] = scfg->g_lchassis.c_mgmt.s_addr >> 24;
+ best[4] = (scfg->g_lchassis.c_mgmt.s_addr & 0xffffff) >> 16;
+ best[3] = (scfg->g_lchassis.c_mgmt.s_addr & 0xffff) >> 8;
+ best[2] = scfg->g_lchassis.c_mgmt.s_addr & 0xff;
+
+ if ((result = snmp_oid_compare(target, 6, best, 6)) < 0) {
+ if (exact)
+ return NULL;
+ memcpy(target, best, sizeof(oid) * 6);
+ *length = vp->namelen + 6;
+ } else if (exact && (result != 0))
+ return NULL;
+ else if (!exact && result == 0)
+ return NULL;
+
+ return agent_management(vp, var_len, &scfg->g_lchassis);
+}
+
+static u_char*
+agent_h_remote_management(struct variable *vp, oid *name, size_t *length,
+ int exact, size_t *var_len, WriteMethod **write_method)
+{
+ struct lldpd_hardware *hardware;
+
+ if ((hardware = header_tprindexed_table(vp, name, length,
+ exact, var_len, write_method, 1)) == NULL)
+ return NULL;
+
+ return agent_management(vp, var_len, hardware->h_rchassis);
+}
+
+static struct variable8 lldp_vars[] = {
+ /* Scalars */
+ {LLDP_SNMP_TXINTERVAL, ASN_INTEGER, RONLY, agent_h_scalars, 3, {1, 1, 1}},
+ {LLDP_SNMP_TXMULTIPLIER, ASN_INTEGER, RONLY, agent_h_scalars, 3, {1, 1, 2}},
+ {LLDP_SNMP_REINITDELAY, ASN_INTEGER, RONLY, agent_h_scalars, 3, {1, 1, 3}},
+ {LLDP_SNMP_TXDELAY, ASN_INTEGER, RONLY, agent_h_scalars, 3, {1, 1, 4}},
+ {LLDP_SNMP_NOTIFICATION, ASN_INTEGER, RONLY, agent_h_scalars, 3, {1, 1, 5}},
+ {LLDP_SNMP_LASTUPDATE, ASN_TIMETICKS, RONLY, agent_h_scalars, 3, {1, 2, 1}},
+ {LLDP_SNMP_STATS_INSERTS, ASN_GAUGE, RONLY, agent_h_scalars, 3, {1, 2, 2}},
+ {LLDP_SNMP_STATS_DELETES, ASN_GAUGE, RONLY, agent_h_scalars, 3, {1, 2, 3}},
+ {LLDP_SNMP_STATS_DROPS, ASN_GAUGE, RONLY, agent_h_scalars, 3, {1, 2, 4}},
+ {LLDP_SNMP_STATS_AGEOUTS, ASN_GAUGE, RONLY, agent_h_scalars, 3, {1, 2, 5}},
+ /* Local chassis */
+ {LLDP_SNMP_LOCAL_CIDSUBTYPE, ASN_INTEGER, RONLY, agent_h_local_chassis, 3, {1, 3, 1}},
+ {LLDP_SNMP_LOCAL_CID, ASN_OCTET_STR, RONLY, agent_h_local_chassis, 3, {1, 3, 2}},
+ {LLDP_SNMP_LOCAL_SYSNAME, ASN_OCTET_STR, RONLY, agent_h_local_chassis, 3, {1, 3, 3}},
+ {LLDP_SNMP_LOCAL_SYSDESCR, ASN_OCTET_STR, RONLY, agent_h_local_chassis, 3, {1, 3, 4}},
+ {LLDP_SNMP_LOCAL_SYSCAP_SUP, ASN_OCTET_STR, RONLY, agent_h_local_chassis, 3, {1, 3, 5}},
+ {LLDP_SNMP_LOCAL_SYSCAP_ENA, ASN_OCTET_STR, RONLY, agent_h_local_chassis, 3, {1, 3, 6}},
+ /* Stats */
+ {LLDP_SNMP_STATS_TX, ASN_COUNTER, RONLY, agent_h_stats, 5, {1, 2, 6, 1, 2}},
+ {LLDP_SNMP_STATS_RX_DISCARDED, ASN_COUNTER, RONLY, agent_h_stats, 5, {1, 2, 7, 1, 2}},
+ {LLDP_SNMP_STATS_RX_ERRORS, ASN_COUNTER, RONLY, agent_h_stats, 5, {1, 2, 7, 1, 3}},
+ {LLDP_SNMP_STATS_RX, ASN_COUNTER, RONLY, agent_h_stats, 5, {1, 2, 7, 1, 4}},
+ {LLDP_SNMP_STATS_RX_TLVDISCARDED, ASN_COUNTER, RONLY, agent_h_stats, 5, {1, 2, 7, 1, 5}},
+ {LLDP_SNMP_STATS_RX_TLVUNRECOGNIZED, ASN_COUNTER, RONLY, agent_h_stats, 5, {1, 2, 7, 1, 6}},
+ {LLDP_SNMP_STATS_RX_AGEOUTS, ASN_GAUGE, RONLY, agent_h_stats, 5, {1, 2, 7, 1, 7}},
+ /* Local ports */
+ {LLDP_SNMP_LOCAL_PIDSUBTYPE, ASN_INTEGER, RONLY, agent_h_local_port, 5, {1, 3, 7, 1, 2}},
+ {LLDP_SNMP_LOCAL_PID, ASN_OCTET_STR, RONLY, agent_h_local_port, 5, {1, 3, 7, 1, 3}},
+ {LLDP_SNMP_LOCAL_PORTDESC, ASN_OCTET_STR, RONLY, agent_h_local_port, 5, {1, 3, 7, 1, 4}},
+ {LLDP_SNMP_LOCAL_DOT3_AUTONEG_SUPPORT, ASN_INTEGER, RONLY, agent_h_local_port, 8,
+ {1, 5, 4623, 1, 2, 1, 1, 1}},
+ {LLDP_SNMP_LOCAL_DOT3_AUTONEG_ENABLED, ASN_INTEGER, RONLY, agent_h_local_port, 8,
+ {1, 5, 4623, 1, 2, 1, 1, 2}},
+ {LLDP_SNMP_LOCAL_DOT3_AUTONEG_ADVERTISED, ASN_OCTET_STR, RONLY, agent_h_local_port, 8,
+ {1, 5, 4623, 1, 2, 1, 1, 3}},
+ {LLDP_SNMP_LOCAL_DOT3_AUTONEG_MAU, ASN_INTEGER, RONLY, agent_h_local_port, 8,
+ {1, 5, 4623, 1, 2, 1, 1, 4}},
+ {LLDP_SNMP_LOCAL_DOT3_AGG_STATUS, ASN_OCTET_STR, RONLY, agent_h_local_port, 8,
+ {1, 5, 4623, 1, 2, 3, 1, 1}},
+ {LLDP_SNMP_LOCAL_DOT3_AGG_ID, ASN_INTEGER, RONLY, agent_h_local_port, 8,
+ {1, 5, 4623, 1, 2, 3, 1, 2}},
+ /* Remote ports */
+ {LLDP_SNMP_REMOTE_CIDSUBTYPE, ASN_INTEGER, RONLY, agent_h_remote_port, 5, {1, 4, 1, 1, 4}},
+ {LLDP_SNMP_REMOTE_CID, ASN_OCTET_STR, RONLY, agent_h_remote_port, 5, {1, 4, 1, 1, 5}},
+ {LLDP_SNMP_REMOTE_PIDSUBTYPE, ASN_INTEGER, RONLY, agent_h_remote_port, 5, {1, 4, 1, 1, 6}},
+ {LLDP_SNMP_REMOTE_PID, ASN_OCTET_STR, RONLY, agent_h_remote_port, 5, {1, 4, 1, 1, 7}},
+ {LLDP_SNMP_REMOTE_PORTDESC, ASN_OCTET_STR, RONLY, agent_h_remote_port, 5, {1, 4, 1, 1, 8}},
+ {LLDP_SNMP_REMOTE_SYSNAME, ASN_OCTET_STR, RONLY, agent_h_remote_port, 5, {1, 4, 1, 1, 9}},
+ {LLDP_SNMP_REMOTE_SYSDESC, ASN_OCTET_STR, RONLY, agent_h_remote_port, 5, {1, 4, 1, 1, 10}},
+ {LLDP_SNMP_REMOTE_SYSCAP_SUP, ASN_OCTET_STR, RONLY, agent_h_remote_port, 5, {1, 4, 1, 1, 11}},
+ {LLDP_SNMP_REMOTE_SYSCAP_ENA, ASN_OCTET_STR, RONLY, agent_h_remote_port, 5, {1, 4, 1, 1, 12}},
+ {LLDP_SNMP_REMOTE_DOT3_AUTONEG_SUPPORT, ASN_INTEGER, RONLY, agent_h_remote_port, 8,
+ {1, 5, 4623, 1, 3, 1, 1, 1}},
+ {LLDP_SNMP_REMOTE_DOT3_AUTONEG_ENABLED, ASN_INTEGER, RONLY, agent_h_remote_port, 8,
+ {1, 5, 4623, 1, 3, 1, 1, 2}},
+ {LLDP_SNMP_REMOTE_DOT3_AUTONEG_ADVERTISED, ASN_OCTET_STR, RONLY, agent_h_remote_port, 8,
+ {1, 5, 4623, 1, 3, 1, 1, 3}},
+ {LLDP_SNMP_REMOTE_DOT3_AUTONEG_MAU, ASN_INTEGER, RONLY, agent_h_remote_port, 8,
+ {1, 5, 4623, 1, 3, 1, 1, 4}},
+ {LLDP_SNMP_REMOTE_DOT3_AGG_STATUS, ASN_OCTET_STR, RONLY, agent_h_remote_port, 8,
+ {1, 5, 4623, 1, 3, 3, 1, 1}},
+ {LLDP_SNMP_REMOTE_DOT3_AGG_ID, ASN_INTEGER, RONLY, agent_h_remote_port, 8,
+ {1, 5, 4623, 1, 3, 3, 1, 2}},
+ /* Local vlans */
+ {LLDP_SNMP_LOCAL_DOT1_VLANNAME, ASN_OCTET_STR, RONLY, agent_h_local_vlan, 8,
+ {1, 5, 32962, 1, 2, 3, 1, 2}},
+ /* Remote vlans */
+ {LLDP_SNMP_REMOTE_DOT1_VLANNAME, ASN_OCTET_STR, RONLY, agent_h_remote_vlan, 8,
+ {1, 5, 32962, 1, 3, 3, 1, 2}},
+ /* Management address */
+ {LLDP_SNMP_LOCAL_ADDR_LEN, ASN_INTEGER, RONLY, agent_h_local_management, 5,
+ {1, 3, 8, 1, 3}},
+ {LLDP_SNMP_LOCAL_ADDR_IFSUBTYPE, ASN_INTEGER, RONLY, agent_h_local_management, 5,
+ {1, 3, 8, 1, 4}},
+ {LLDP_SNMP_LOCAL_ADDR_IFID, ASN_INTEGER, RONLY, agent_h_local_management, 5,
+ {1, 3, 8, 1, 5}},
+ {LLDP_SNMP_LOCAL_ADDR_OID, ASN_OBJECT_ID, RONLY, agent_h_local_management, 5,
+ {1, 3, 8, 1, 6}},
+ {LLDP_SNMP_REMOTE_ADDR_IFSUBTYPE, ASN_INTEGER, RONLY, agent_h_remote_management, 5,
+ {1, 4, 2, 1, 3}},
+ {LLDP_SNMP_REMOTE_ADDR_IFID, ASN_INTEGER, RONLY, agent_h_remote_management, 5,
+ {1, 4, 2, 1, 4}},
+ {LLDP_SNMP_REMOTE_ADDR_OID, ASN_OBJECT_ID, RONLY, agent_h_remote_management, 5,
+ {1, 4, 2, 1, 5}},
+};
+
+void
+agent_init(struct lldpd *cfg, int debug)
+{
+ int rc;
+ extern char *__progname;
+
+ LLOG_INFO("Enable SNMP subagent");
+ netsnmp_enable_subagent();
+ snmp_disable_log();
+ if (debug)
+ snmp_enable_stderrlog();
+ else
+ snmp_enable_syslog_ident(__progname, LOG_DAEMON);
+
+ scfg = cfg;
+
+ init_agent("lldpAgent");
+
+ REGISTER_MIB("lldp", lldp_vars, variable8, lldp_oid);
+
+ init_snmp("lldpAgent");
+
+ if ((rc = register_sysORTable(lldp_oid, OID_LENGTH(lldp_oid),
+ "lldpMIB implementation by lldpd")) != 0)
+ LLOG_WARNX("Unable to register to sysORTable (%d)", rc);
+}
+
+void
+agent_shutdown()
+{
+ unregister_sysORTable(lldp_oid, OID_LENGTH(lldp_oid));
+ snmp_shutdown("lldpAgent");
+}
--- /dev/null
+/*
+ * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and 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 "lldpd.h"
+
+#include <errno.h>
+#include <arpa/inet.h>
+
+int
+cdp_send(struct lldpd *global, struct lldpd_chassis *chassis,
+ struct lldpd_hardware *hardware, int version)
+{
+ struct cdp_header ch;
+ struct ethllc llc;
+ const u_int8_t mcastaddr[] = CDP_MULTICAST_ADDR;
+ const u_int8_t llcorg[] = LLC_ORG_CISCO;
+ struct iovec *iov = NULL;
+ struct cdp_tlv_head device;
+ struct cdp_tlv_head port;
+ struct cdp_tlv_head soft;
+ struct cdp_tlv_head platform;
+ struct cdp_tlv_address_head ah;
+ struct cdp_tlv_address_one ao;
+ struct cdp_tlv_capabilities cap;
+ unsigned int c = -1, i, len;
+
+#define IOV_NEW \
+ if ((iov = (struct iovec*)realloc(iov, (++c + 1) * \
+ sizeof(struct iovec))) == NULL) \
+ fatal(NULL);
+
+ /* Ether + LLC */
+ memset(&llc, 0, sizeof(llc));
+ memcpy(&llc.ether.shost, &hardware->h_lladdr,
+ sizeof(llc.ether.shost));
+ memcpy(&llc.ether.dhost, &mcastaddr,
+ sizeof(llc.ether.dhost));
+ llc.dsap = llc.ssap = 0xaa;
+ llc.control = 0x03;
+ memcpy(llc.org, llcorg, sizeof(llc.org));
+ llc.protoid = htons(LLC_PID_CDP);
+ IOV_NEW;
+ iov[c].iov_base = &llc;
+ iov[c].iov_len = sizeof(llc);
+
+ /* CDP header */
+ memset(&ch, 0, sizeof(ch));
+ ch.version = version;
+ ch.ttl = chassis->c_ttl;
+ IOV_NEW;
+ iov[c].iov_base = &ch;
+ iov[c].iov_len = sizeof(struct cdp_header);
+
+ /* Chassis ID */
+ memset(&device, 0, sizeof(device));
+ device.tlv_type = htons(CDP_TLV_CHASSIS);
+ device.tlv_len = htons(sizeof(device) + strlen(chassis->c_name));
+ IOV_NEW;
+ iov[c].iov_base = &device;
+ iov[c].iov_len = sizeof(device);
+ IOV_NEW;
+ iov[c].iov_base = chassis->c_name;
+ iov[c].iov_len = strlen(chassis->c_name);
+
+ /* Adresses */
+ memset(&ah, 0, sizeof(ah));
+ ah.head.tlv_type = htons(CDP_TLV_ADDRESSES);
+ ah.head.tlv_len = htons(sizeof(ah) + sizeof(ao));
+ ah.nb = htonl(1);
+ IOV_NEW;
+ iov[c].iov_base = &ah;
+ iov[c].iov_len = sizeof(ah);
+ memset(&ao, 0, sizeof(ao));
+ ao.ptype = 1;
+ ao.plen = 1;
+ ao.proto = CDP_ADDRESS_PROTO_IP;
+ ao.alen = htons(sizeof(struct in_addr));
+ memcpy(&ao.addr, &chassis->c_mgmt, sizeof(struct in_addr));
+ IOV_NEW;
+ iov[c].iov_base = &ao;
+ iov[c].iov_len = sizeof(ao);
+
+ /* Port ID */
+ memset(&port, 0, sizeof(port));
+ port.tlv_type = htons(CDP_TLV_PORT);
+ port.tlv_len = htons(sizeof(port) + strlen(hardware->h_lport.p_descr));
+ IOV_NEW;
+ iov[c].iov_base = &port;
+ iov[c].iov_len = sizeof(port);
+ IOV_NEW;
+ iov[c].iov_base = hardware->h_lport.p_descr;
+ iov[c].iov_len = strlen(hardware->h_lport.p_descr);
+
+ /* Capaibilities */
+ memset(&cap, 0, sizeof(cap));
+ cap.head.tlv_type = htons(CDP_TLV_CAPABILITIES);
+ cap.head.tlv_len = htons(sizeof(cap));
+ cap.cap = 0;
+ if (chassis->c_cap_enabled & LLDP_CAP_ROUTER)
+ cap.cap |= CDP_CAP_ROUTER;
+ if (chassis->c_cap_enabled & LLDP_CAP_BRIDGE)
+ cap.cap |= CDP_CAP_BRIDGE;
+ cap.cap = htonl(cap.cap);
+ IOV_NEW;
+ iov[c].iov_base = ∩
+ iov[c].iov_len = sizeof(cap);
+
+ /* Software version */
+ memset(&soft, 0, sizeof(soft));
+ soft.tlv_type = htons(CDP_TLV_SOFTWARE);
+ soft.tlv_len = htons(sizeof(soft) + strlen(chassis->c_descr));
+ IOV_NEW;
+ iov[c].iov_base = &soft;
+ iov[c].iov_len = sizeof(soft);
+ IOV_NEW;
+ iov[c].iov_base = chassis->c_descr;
+ iov[c].iov_len = strlen(chassis->c_descr);
+
+ /* Platform */
+ memset(&platform, 0, sizeof(platform));
+ platform.tlv_type = htons(CDP_TLV_PLATFORM);
+ platform.tlv_len = htons(sizeof(platform) + strlen("Linux"));
+ IOV_NEW;
+ iov[c].iov_base = &platform;
+ iov[c].iov_len = sizeof(platform);
+ IOV_NEW;
+ iov[c].iov_base = "Linux";
+ iov[c].iov_len = strlen("Linux");
+
+ c++;
+
+ /* Compute len and checksum */
+ len = 0;
+ for (i = 0; i < c; i++) {
+ len += iov[i].iov_len;
+ }
+ len -= sizeof(struct ieee8023);
+ llc.ether.size = htons(len);
+ ch.checksum = iov_checksum(&iov[1], c - 1, 1);
+
+ if (writev((hardware->h_raw_real > 0) ? hardware->h_raw_real :
+ hardware->h_raw, iov, c) == -1) {
+ LLOG_WARN("unable to send packet on real device for %s",
+ hardware->h_ifname);
+ free(iov);
+ return ENETDOWN;
+ }
+
+ hardware->h_tx_cnt++;
+
+ free(iov);
+ return 0;
+}
+
+int
+cdp_decode(struct lldpd *cfg, char *frame, int s,
+ struct lldpd_hardware *hardware,
+ struct lldpd_chassis **newchassis, struct lldpd_port **newport)
+{
+ struct lldpd_chassis *chassis;
+ struct lldpd_port *port;
+ struct ethllc *llc;
+ struct cdp_header *ch;
+ struct cdp_tlv_head *tlv;
+ struct cdp_tlv_address_head *ah;
+ struct cdp_tlv_address_one *ao;
+ struct iovec iov;
+ u_int16_t cksum;
+ char *software = NULL, *platform = NULL;
+ int software_len = 0, platform_len = 0;
+ const unsigned char cdpaddr[] = CDP_MULTICAST_ADDR;
+ int i, f, len, rlen;
+
+ if ((chassis = calloc(1, sizeof(struct lldpd_chassis))) == NULL) {
+ LLOG_WARN("failed to allocate remote chassis");
+ return -1;
+ }
+ if ((port = calloc(1, sizeof(struct lldpd_port))) == NULL) {
+ LLOG_WARN("failed to allocate remote port");
+ free(chassis);
+ return -1;
+ }
+ TAILQ_INIT(&port->p_vlans);
+
+ if (s < sizeof(struct ethllc) + sizeof(struct cdp_header)) {
+ LLOG_WARNX("too short frame received on %s", hardware->h_ifname);
+ goto malformed;
+ }
+
+ llc = (struct ethllc *)frame;
+ if (memcmp(&llc->ether.dhost, cdpaddr, sizeof(cdpaddr)) != 0) {
+ LLOG_INFO("frame not targeted at CDP multicast address received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ if (ntohs(llc->ether.size) > s - sizeof(struct ieee8023)) {
+ LLOG_WARNX("incorrect 802.3 frame size reported on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ if (llc->protoid != htons(LLC_PID_CDP)) {
+ LLOG_DEBUG("incorrect LLC protocol ID received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ f = sizeof(struct ethllc);
+ ch = (struct cdp_header *)(frame + f);
+ if ((ch->version != 1) && (ch->version != 2)) {
+ LLOG_WARNX("incorrect CDP version (%d) for frame received on %s",
+ ch->version, hardware->h_ifname);
+ goto malformed;
+ }
+ chassis->c_ttl = ntohs(ch->ttl);
+ iov.iov_len = s - f;
+ iov.iov_base = frame + f;
+ cksum = iov_checksum(&iov, 1, 1);
+ /* An off-by-one error may happen. Just ignore it */
+ if ((cksum != 0) && (cksum != 0xfffe)) {
+ LLOG_INFO("incorrect CDP checksum for frame received on %s (%d)",
+ hardware->h_ifname, cksum);
+ goto malformed;
+ }
+
+ f += sizeof(struct cdp_header);
+ while (f < s) {
+ if (f + sizeof(struct cdp_tlv_head) > s) {
+ LLOG_WARNX("CDP TLV header is too large for "
+ "frame received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ tlv = (struct cdp_tlv_head *)(frame + f);
+ len = ntohs(tlv->tlv_len) - sizeof(struct cdp_tlv_head);
+ if ((len < 0) || (f + sizeof(struct cdp_tlv_head) + len > s)) {
+ LLOG_WARNX("incorrect size in CDP TLV header for frame "
+ "received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ switch (ntohs(tlv->tlv_type)) {
+ case CDP_TLV_CHASSIS:
+ f += sizeof(struct cdp_tlv_head);
+ if ((chassis->c_name = (char *)calloc(1, len + 1)) == NULL) {
+ LLOG_WARN("unable to allocate memory for chassis name");
+ goto malformed;
+ }
+ memcpy(chassis->c_name, frame + f, len);
+ chassis->c_id_subtype = LLDP_CHASSISID_SUBTYPE_LOCAL;
+ if ((chassis->c_id = (char *)malloc(len)) == NULL) {
+ LLOG_WARN("unable to allocate memory for chassis ID");
+ goto malformed;
+ }
+ memcpy(chassis->c_id, frame + f, len);
+ chassis->c_id_len = len;
+ f += len;
+ break;
+ case CDP_TLV_ADDRESSES:
+ if (len < 4) {
+ LLOG_WARNX("incorrect size in CDP TLV header for frame "
+ "received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ ah = (struct cdp_tlv_address_head *)(frame + f);
+ f += sizeof(struct cdp_tlv_address_head);
+ len -= 4;
+ for (i = 0; i < ntohl(ah->nb); i++) {
+ if (len < sizeof(struct cdp_tlv_address_one) -
+ sizeof(struct in_addr)) {
+ LLOG_WARNX("incorrect size for address TLV in "
+ "frame received from %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ ao = (struct cdp_tlv_address_one *)(frame + f);
+ rlen = 2 + ao->plen + 2 + ntohs(ao->alen);
+ if (len < rlen) {
+ LLOG_WARNX("incorrect address size in TLV "
+ "received from %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ if ((ao->ptype == 1) && (ao->plen == 1) &&
+ (ao->proto == CDP_ADDRESS_PROTO_IP) &&
+ (ntohs(ao->alen) == sizeof(struct in_addr)) &&
+ (chassis->c_mgmt.s_addr == INADDR_ANY))
+ chassis->c_mgmt.s_addr = ao->addr.s_addr;
+ f += rlen;
+ len -= rlen;
+ }
+ if (len != 0) {
+ LLOG_WARNX("not enough addresses found in TLV "
+ "received from %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ break;
+ case CDP_TLV_PORT:
+ f += sizeof(struct cdp_tlv_head);
+ if ((port->p_descr = (char *)calloc(1, len + 1)) == NULL) {
+ LLOG_WARN("unable to allocate memory for port description");
+ goto malformed;
+ }
+ memcpy(port->p_descr, frame + f, len);
+ port->p_id_subtype = LLDP_PORTID_SUBTYPE_LLADDR;
+ if ((port->p_id = (char *)malloc(ETH_ALEN)) == NULL) {
+ LLOG_WARN("unable to allocate memory for port ID");
+ goto malformed;
+ }
+ memcpy(port->p_id, llc->ether.shost, ETH_ALEN);
+ port->p_id_len = ETH_ALEN;
+ f += len;
+ break;
+ case CDP_TLV_CAPABILITIES:
+ f += sizeof(struct cdp_tlv_head);
+ if (len != 4) {
+ LLOG_WARNX("incorrect size for capabilities TLV "
+ "on frame received from %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ if (ntohl(*(u_int32_t*)(frame + f)) & CDP_CAP_ROUTER)
+ chassis->c_cap_enabled |= LLDP_CAP_ROUTER;
+ if (ntohl(*(u_int32_t*)(frame + f)) & 0x0e)
+ chassis->c_cap_enabled |= LLDP_CAP_BRIDGE;
+ if (chassis->c_cap_enabled == 0)
+ chassis->c_cap_enabled = LLDP_CAP_STATION;
+ chassis->c_cap_available = chassis->c_cap_enabled;
+ f += 4;
+ break;
+ case CDP_TLV_SOFTWARE:
+ f += sizeof(struct cdp_tlv_head);
+ software_len = len;
+ software = (char *)(frame + f);
+ f += len;
+ break;
+ case CDP_TLV_PLATFORM:
+ f += sizeof(struct cdp_tlv_head);
+ platform_len = len;
+ platform = (char *)(frame + f);
+ f += len;
+ break;
+ default:
+ LLOG_DEBUG("unknown CDP TLV type (%d) received on %s",
+ ntohs(tlv->tlv_type), hardware->h_ifname);
+ f += sizeof(struct cdp_tlv_head) + len;
+ }
+ }
+ if (!software && platform) {
+ if ((chassis->c_descr = (char *)calloc(1,
+ platform_len + 1)) == NULL) {
+ LLOG_WARN("unable to allocate memory for chassis description");
+ goto malformed;
+ }
+ memcpy(chassis->c_descr, platform, platform_len);
+ } else if (software && !platform) {
+ if ((chassis->c_descr = (char *)calloc(1,
+ software_len + 1)) == NULL) {
+ LLOG_WARN("unable to allocate memory for chassis description");
+ goto malformed;
+ }
+ memcpy(chassis->c_descr, software, software_len);
+ } else if (software && platform) {
+#define CONCAT_PLATFORM " running on\n"
+ if ((chassis->c_descr = (char *)calloc(1,
+ software_len + platform_len +
+ strlen(CONCAT_PLATFORM) + 1)) == NULL) {
+ LLOG_WARN("unable to allocate memory for chassis description");
+ goto malformed;
+ }
+ memcpy(chassis->c_descr, platform, platform_len);
+ memcpy(chassis->c_descr + platform_len,
+ CONCAT_PLATFORM, strlen(CONCAT_PLATFORM));
+ memcpy(chassis->c_descr + platform_len + strlen(CONCAT_PLATFORM),
+ software, software_len);
+ }
+ if ((chassis->c_id == NULL) ||
+ (port->p_id == NULL) ||
+ (chassis->c_name == NULL) ||
+ (chassis->c_descr == NULL) ||
+ (port->p_descr == NULL) ||
+ (chassis->c_ttl == 0) ||
+ (chassis->c_cap_enabled == 0)) {
+ LLOG_WARNX("some mandatory tlv are missing for frame received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ *newchassis = chassis;
+ *newport = port;
+ return 1;
+
+malformed:
+ free(chassis->c_name);
+ free(chassis->c_id);
+ free(chassis->c_descr);
+ free(chassis);
+ free(port->p_id);
+ free(port->p_descr);
+ lldpd_vlan_cleanup(port);
+ free(port);
+ return -1;
+}
+
+int
+cdpv1_send(struct lldpd *global, struct lldpd_chassis *chassis,
+ struct lldpd_hardware *hardware)
+{
+ return cdp_send(global, chassis, hardware, 1);
+}
+
+int
+cdpv2_send(struct lldpd *global, struct lldpd_chassis *chassis,
+ struct lldpd_hardware *hardware)
+{
+ return cdp_send(global, chassis, hardware, 2);
+}
+
+int
+cdp_guess(char *frame, int len, int version)
+{
+ const u_int8_t mcastaddr[] = CDP_MULTICAST_ADDR;
+ struct cdp_header *ch;
+ if (len < sizeof(struct ethllc) + sizeof(struct cdp_header))
+ return 0;
+ if (memcmp(frame, mcastaddr, ETH_ALEN) != 0)
+ return 0;
+ ch = (struct cdp_header *)(frame + sizeof(struct ethllc));
+ return (ch->version == version);
+}
+
+int
+cdpv1_guess(char *frame, int len)
+{
+ return cdp_guess(frame, len, 1);
+}
+
+int
+cdpv2_guess(char *frame, int len)
+{
+ return cdp_guess(frame, len, 2);
+}
--- /dev/null
+/*
+ * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and 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.
+ */
+
+#ifndef _CDP_H
+#define _CDP_H
+
+#define CDP_MULTICAST_ADDR { \
+ 0x01, 0x00, 0x0c, 0xcc, 0xcc, 0xcc \
+}
+#define LLC_ORG_CISCO { 0x00, 0x00, 0x0c }
+#define LLC_PID_CDP 0x2000
+
+struct cdp_header {
+ u_int8_t version;
+ u_int8_t ttl;
+ u_int16_t checksum;
+} __attribute__ ((__packed__));
+
+struct cdp_tlv_head {
+ u_int16_t tlv_type;
+ u_int16_t tlv_len;
+} __attribute__ ((__packed__));
+
+enum {
+ CDP_TLV_CHASSIS = 1,
+ CDP_TLV_ADDRESSES = 2,
+ CDP_TLV_PORT = 3,
+ CDP_TLV_CAPABILITIES = 4,
+ CDP_TLV_SOFTWARE = 5,
+ CDP_TLV_PLATFORM = 6
+};
+
+struct cdp_tlv_address_head {
+ struct cdp_tlv_head head;
+ u_int32_t nb;
+} __attribute__ ((__packed__));
+
+struct cdp_tlv_address_one {
+ u_int8_t ptype; /* Should be 1 */
+ u_int8_t plen; /* Should be 1 */
+#define CDP_ADDRESS_PROTO_IP 0xcc
+ u_int8_t proto; /* 0xcc for IP */
+ u_int16_t alen; /* Should be 4 */
+ struct in_addr addr;
+} __attribute__ ((__packed__));
+
+struct cdp_tlv_capabilities {
+ struct cdp_tlv_head head;
+ u_int32_t cap;
+} __attribute__ ((__packed__));
+
+#define CDP_CAP_ROUTER 1
+#define CDP_CAP_BRIDGE 8
+
+#endif /* _CDP_H */
+
--- /dev/null
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)queue.h 8.5 (Berkeley) 8/20/94
+ */
+
+#if !HAVE_DECL_TAILQ_FIRST
+#define TAILQ_FIRST(head) ((head)->tqh_first)
+#endif
+
+#if !HAVE_DECL_TAILQ_NEXT
+#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+#endif
+
+#if !HAVE_DECL_TAILQ_FOREACH
+#define TAILQ_FOREACH(var, head, field) \
+ for ((var) = ((head)->tqh_first); \
+ (var); \
+ (var) = ((var)->field.tqe_next))
+#endif
+
+#if !HAVE_DECL_TAILQ_EMPTY
+#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
+#endif
+
+#if !HAVE_DECL_ADVERTISED_2500BASEX_Full
+#define ADVERTISED_2500baseX_Full (1 << 15)
+#endif
+
+#if !HAVE_DECL_PACKET_ORIGDEV
+#define PACKET_ORIGDEV 9
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and 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 "lldpd.h"
+
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+int
+ctl_create(struct lldpd *cfg, char *name)
+{
+ int s;
+ struct sockaddr_un su;
+ int rc;
+
+ if ((s = socket(PF_UNIX, SOCK_STREAM, 0)) == -1)
+ return -1;
+ su.sun_family = AF_UNIX;
+ strlcpy(su.sun_path, name, UNIX_PATH_MAX);
+ if (bind(s, (struct sockaddr *)&su, sizeof(struct sockaddr_un)) == -1) {
+ rc = errno; close(s); errno = rc;
+ return -1;
+ }
+ if (listen(s, 5) == -1) {
+ rc = errno; close(s); errno = rc;
+ return -1;
+ }
+ TAILQ_INIT(&cfg->g_clients);
+ return s;
+}
+
+int
+ctl_connect(char *name)
+{
+ int s;
+ struct sockaddr_un su;
+ int rc;
+
+ if ((s = socket(PF_UNIX, SOCK_STREAM, 0)) == -1)
+ return -1;
+ su.sun_family = AF_UNIX;
+ strlcpy(su.sun_path, name, UNIX_PATH_MAX);
+ if (connect(s, (struct sockaddr *)&su, sizeof(struct sockaddr_un)) == -1) {
+ rc = errno;
+ LLOG_WARN("unable to connect to socket " LLDPD_CTL_SOCKET);
+ errno = rc; return -1;
+ }
+ return s;
+}
+
+int
+ctl_accept(struct lldpd *cfg, int c)
+{
+ int s;
+ struct lldpd_client *lc;
+ if ((s = accept(c, NULL, NULL)) == -1) {
+ LLOG_WARN("unable to accept connection from socket");
+ return -1;
+ }
+ if ((lc = (struct lldpd_client *)malloc(sizeof(
+ struct lldpd_client))) == NULL) {
+ LLOG_WARN("failed to allocate memory for new client");
+ close(s);
+ return -1;
+ }
+ lc->fd = s;
+ TAILQ_INSERT_TAIL(&cfg->g_clients, lc, next);
+ return 1;
+}
+
+void
+ctl_msg_init(struct hmsg *t, enum hmsg_type type)
+{
+ t->hdr.type = type;
+ t->hdr.len = 0;
+ t->hdr.pid = getpid();
+}
+
+int
+ctl_msg_send(int fd, struct hmsg *t)
+{
+ return write(fd, t, t->hdr.len + sizeof(struct hmsg_hdr));
+}
+
+int
+ctl_msg_recv(int fd, struct hmsg *t)
+{
+ int n;
+ if ((n = read(fd, t, MAX_HMSGSIZE)) == -1) {
+ return -1;
+ }
+ if (n < sizeof(struct hmsg_hdr)) {
+ LLOG_WARNX("message received too short");
+ errno = 0;
+ return -1;
+ }
+ if (n != sizeof(struct hmsg_hdr) + t->hdr.len) {
+ LLOG_WARNX("message from %d seems to be truncated (or too large)",
+ t->hdr.pid);
+ errno = 0;
+ return -1;
+ }
+ return 1;
+}
+
+int
+ctl_close(struct lldpd *cfg, int c)
+{
+ struct lldpd_client *client, *client_next;
+ for (client = TAILQ_FIRST(&cfg->g_clients);
+ client != NULL;
+ client = client_next) {
+ client_next = TAILQ_NEXT(client, next);
+ if (client->fd == c) {
+ close(client->fd);
+ TAILQ_REMOVE(&cfg->g_clients, client, next);
+ free(client);
+ return 1;
+ }
+ }
+ /* Not found */
+ return -1;
+}
+
+void
+ctl_cleanup(int c, char *name)
+{
+ close(c);
+ if (unlink(name) == -1)
+ LLOG_WARN("unable to unlink %s", name);
+}
+
+/* Packing/unpacking */
+
+/* This structure is used to track memory allocation when unpacking */
+struct gc {
+ TAILQ_ENTRY(gc) next;
+ void *pointer;
+};
+TAILQ_HEAD(gc_l, gc);
+
+typedef struct { char c; int16_t x; } st_int16;
+typedef struct { char c; int32_t x; } st_int32;
+typedef struct { char c; void *x; } st_void_p;
+
+#define INT16_ALIGN (sizeof(st_int16) - sizeof(int16_t))
+#define INT32_ALIGN (sizeof(st_int32) - sizeof(int32_t))
+#define VOID_P_ALIGN (sizeof(st_void_p) - sizeof(void *))
+
+struct formatdef {
+ char format;
+ int size;
+ int alignment;
+ int (*pack)(struct hmsg*, void **, void *,
+ const struct formatdef *);
+ int (*unpack)(struct hmsg*, void **, void *,
+ const struct formatdef *, struct gc_l *);
+};
+
+/* void** is a pointer to a pointer to the end of struct hmsg*. It should be
+ * updated. void* is a pointer to the entity to pack */
+
+int
+_ctl_alloc_pointer(struct gc_l *pointers, void *pointer)
+{
+ struct gc *gpointer;
+ if (pointers != NULL) {
+ if ((gpointer = (struct gc *)calloc(1,
+ sizeof(struct gc))) == NULL) {
+ LLOG_WARN("unable to allocate memory for garbage collector");
+ return -1;
+ }
+ gpointer->pointer = pointer;
+ TAILQ_INSERT_TAIL(pointers, gpointer, next);
+ }
+ return 0;
+}
+
+void
+_ctl_free_pointers(struct gc_l *pointers, int listonly)
+{
+ struct gc *pointer, *pointer_next;
+ for (pointer = TAILQ_FIRST(pointers);
+ pointer != NULL;
+ pointer = pointer_next) {
+ pointer_next = TAILQ_NEXT(pointer, next);
+ TAILQ_REMOVE(pointers, pointer, next);
+ if (!listonly)
+ free(pointer->pointer);
+ free(pointer);
+ }
+}
+
+int
+pack_copy(struct hmsg *h, void **p, void *s,
+ const struct formatdef *ct)
+{
+ if (h->hdr.len + ct->size > MAX_HMSGSIZE - sizeof(struct hmsg_hdr)) {
+ LLOG_WARNX("message became too large");
+ return -1;
+ }
+ memcpy(*p, s, ct->size);
+ *p += ct->size;
+ h->hdr.len += ct->size;
+ return ct->size;
+}
+
+int
+unpack_copy(struct hmsg *h, void **p, void *s,
+ const struct formatdef *ct, struct gc_l *pointers)
+{
+ memcpy(s, *p, ct->size);
+ *p += ct->size;
+ return ct->size;
+}
+
+int
+pack_string(struct hmsg *h, void **p, void *s,
+ const struct formatdef *ct)
+{
+ int len = strlen(*(char**)s);
+ if (h->hdr.len + len + sizeof(int) > MAX_HMSGSIZE -
+ sizeof(struct hmsg_hdr)) {
+ LLOG_WARNX("message became too large");
+ return -1;
+ }
+ memcpy(*p, &len, sizeof(int));
+ *p += sizeof(int);
+ memcpy(*p, *(char **)s, len);
+ *p += len;
+ h->hdr.len += sizeof(int) + len;
+ return sizeof(int) + len;
+}
+
+int
+unpack_string(struct hmsg *h, void **p, void *s,
+ const struct formatdef *ct, struct gc_l *pointers)
+{
+ char *string;
+ int len = *(int*)*p;
+ *p += sizeof(int);
+ if ((string = (char *)calloc(1, len + 1)) == NULL) {
+ LLOG_WARNX("unable to allocate new string");
+ return -1;
+ }
+ if (_ctl_alloc_pointer(pointers, string) == -1) {
+ free(string);
+ return -1;
+ }
+ memcpy(string, *p, len);
+ *p += len;
+ memcpy(s, &string, sizeof(char *));
+ return sizeof(char*);
+}
+
+int
+pack_chars(struct hmsg *h, void **p, void *s,
+ const struct formatdef *ct)
+{
+ char *string;
+ int string_len;
+ string = *(char **)s;
+ s += sizeof(char *);
+ string_len = *(int *)s;
+
+ if (h->hdr.len + string_len + sizeof(int) > MAX_HMSGSIZE -
+ sizeof(struct hmsg_hdr)) {
+ LLOG_WARNX("message became too large");
+ return -1;
+ }
+ memcpy(*p, &string_len, sizeof(int));
+ *p += sizeof(int);
+ memcpy(*p, string, string_len);
+ *p += string_len;
+ h->hdr.len += sizeof(int) + string_len;
+ return sizeof(int) + string_len;
+}
+
+int
+unpack_chars(struct hmsg *h, void **p, void *s,
+ const struct formatdef *ct, struct gc_l *pointers)
+{
+ char *string;
+ struct {
+ char *string;
+ int len;
+ } reals __attribute__ ((__packed__));
+ int len = *(int*)*p;
+ *p += sizeof(int);
+ if ((string = (char *)malloc(len)) == NULL) {
+ LLOG_WARN("unable to allocate new string");
+ return -1;
+ }
+ if (_ctl_alloc_pointer(pointers, string) == -1) {
+ free(string);
+ return -1;
+ }
+ memcpy(string, *p, len);
+ *p += len;
+ reals.string = string;
+ reals.len = len;
+ memcpy(s, &reals, sizeof(reals));
+ return sizeof(char*);
+}
+
+int
+pack_zero(struct hmsg *h, void **p, void *s,
+ const struct formatdef *ct)
+{
+ if (h->hdr.len + ct->size > MAX_HMSGSIZE - sizeof(struct hmsg_hdr)) {
+ LLOG_WARNX("message became too large");
+ return -1;
+ }
+ memset(*p, 0, ct->size);
+ *p += ct->size;
+ h->hdr.len += ct->size;
+ return ct->size;
+}
+
+static struct formatdef conv_table[] = {
+ {'b', 1, 0,
+ pack_copy, unpack_copy},
+ {'w', 2, INT16_ALIGN,
+ pack_copy, unpack_copy},
+ {'l', 4, INT32_ALIGN,
+ pack_copy, unpack_copy},
+ /* Null terminated string */
+ {'s', sizeof(void*), VOID_P_ALIGN,
+ pack_string, unpack_string},
+ /* Pointer (is packed with 0) */
+ {'P', sizeof(void*), VOID_P_ALIGN,
+ pack_zero, unpack_copy},
+ /* A list (same as pointer), should be at the beginning */
+ {'L', sizeof(void*)*2, VOID_P_ALIGN,
+ pack_zero, unpack_copy},
+ /* Non null terminated string, followed by an int for the size */
+ {'C', sizeof(void*) + sizeof(int), VOID_P_ALIGN,
+ pack_chars, unpack_chars},
+ {0}
+};
+
+/* Lists can be packed only if the "next" member is the first one of the
+ * structure! No check is done for this. */
+struct fakelist_m {
+ TAILQ_ENTRY(fakelist_m) next;
+ void *data;
+};
+TAILQ_HEAD(fakelist_l, fakelist_m);
+
+int
+ctl_msg_pack_structure(char *format, void *structure, unsigned int size,
+ struct hmsg *h, void **p)
+{
+ char *f;
+ struct formatdef *ce;
+ unsigned int csize = 0;
+ uintptr_t offset;
+ int maxalign = 0;
+
+ for (f = format; *f != 0; f++) {
+ for (ce = conv_table;
+ ce->format != 0;
+ ce++) {
+ if (ce->format == *f)
+ break;
+ }
+ if (ce->format != *f) {
+ LLOG_WARNX("unknown format char %c in %s", *f,
+ format);
+ return -1;
+ }
+ if (ce->alignment != 0) {
+ maxalign = (maxalign>ce->alignment)?maxalign:ce->alignment;
+ offset = (uintptr_t)structure % ce->alignment;
+ if (offset != 0) {
+ structure = structure +
+ (ce->alignment - offset);
+ csize += ce->alignment - offset;
+ }
+ }
+ if (ce->pack(h, p, structure, ce) == -1) {
+ LLOG_WARNX("error while packing %c in %s", *f,
+ format);
+ return -1;
+ }
+ structure += ce->size;
+ csize += ce->size;
+ }
+
+ /* End padding */
+ if ((maxalign > 0) && (csize % maxalign != 0))
+ csize = csize + maxalign - (csize % maxalign);
+ if (size != csize) {
+ LLOG_WARNX("size of structure does not match its "
+ "declaration (%d vs %d)", size, csize);
+ return -1;
+ }
+ return 0;
+}
+
+int
+_ctl_msg_unpack_structure(char *format, void *structure, unsigned int size,
+ struct hmsg *h, void **p, struct gc_l *pointers)
+{
+ char *f;
+ struct formatdef *ce;
+ unsigned int csize = 0;
+ uintptr_t offset;
+ int maxalign = 0;
+
+ for (f = format; *f != 0; f++) {
+ for (ce = conv_table;
+ ce->format != 0;
+ ce++) {
+ if (ce->format == *f)
+ break;
+ }
+ if (ce->format != *f) {
+ LLOG_WARNX("unknown format char %c", *f);
+ return -1;
+ }
+ if (ce->alignment != 0) {
+ maxalign = (maxalign>ce->alignment)?maxalign:ce->alignment;
+ offset = (uintptr_t)structure % ce->alignment;
+ if (offset != 0) {
+ structure = structure +
+ (ce->alignment - offset);
+ csize += ce->alignment - offset;
+ }
+ }
+ csize += ce->size;
+ if (csize > size) {
+ LLOG_WARNX("size of structure is too small for given "
+ "format (%d vs %d)", size, csize);
+ return -1;
+ }
+
+ if (ce->unpack(h, p, structure, ce, pointers) == -1) {
+ LLOG_WARNX("error while unpacking %c", *f);
+ return -1;
+ }
+ structure += ce->size;
+ }
+
+ /* End padding */
+ if ((maxalign > 0) && (csize % maxalign != 0))
+ csize = csize + maxalign - (csize % maxalign);
+ if (size < csize) {
+ LLOG_WARNX("size of structure does not match its "
+ "declaration (%d vs %d)", size, csize);
+ return -1;
+ }
+ return 0;
+}
+
+int
+ctl_msg_unpack_structure(char *format, void *structure, unsigned int size,
+ struct hmsg *h, void **p)
+{
+ struct gc_l pointers;
+ int rc;
+ TAILQ_INIT(&pointers);
+ if ((rc = _ctl_msg_unpack_structure(format, structure, size,
+ h, p, &pointers)) == -1) {
+ LLOG_WARNX("unable to unpack structure, freeing");
+ _ctl_free_pointers(&pointers, 0);
+ return -1;
+ }
+ _ctl_free_pointers(&pointers, 1);
+ return rc;
+}
+
+int
+ctl_msg_pack_list(char *format, void *list, unsigned int size, struct hmsg *h, void **p)
+{
+ struct fakelist_m *member;
+ struct fakelist_l *flist = (struct fakelist_l *)list;
+ TAILQ_FOREACH(member, flist, next) {
+ if (ctl_msg_pack_structure(format, member, size, h, p) == -1) {
+ LLOG_WARNX("error while packing list, aborting");
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int
+ctl_msg_unpack_list(char *format, void *list, unsigned int size, struct hmsg *h, void **p)
+{
+ struct fakelist_m *member, *member_next;
+ struct gc_l pointers;
+ struct fakelist_l *flist = (struct fakelist_l *)list;
+ if (format[0] != 'L') {
+ LLOG_WARNX("to unpack a list, format should start with 'L'");
+ return -1;
+ }
+ TAILQ_INIT(flist);
+ TAILQ_INIT(&pointers);
+ while (*p - (void *)h - sizeof(struct hmsg_hdr) < h->hdr.len) {
+ if ((member = calloc(1, size)) == NULL) {
+ LLOG_WARN("unable to allocate memory for structure");
+ return -1;
+ }
+ if (_ctl_msg_unpack_structure(format, member, size,
+ h, p, &pointers) == -1) {
+ LLOG_WARNX("unable to unpack list, aborting");
+ free(member);
+ /* Free each list member */
+ for (member = TAILQ_FIRST(flist);
+ member != NULL;
+ member = member_next) {
+ member_next = TAILQ_NEXT(member, next);
+ TAILQ_REMOVE(flist, member, next);
+ free(member);
+ }
+ _ctl_free_pointers(&pointers, 0);
+ return -1;
+ }
+ TAILQ_INSERT_TAIL(flist, member, next);
+ }
+ _ctl_free_pointers(&pointers, 1);
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and 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 "lldpd.h"
+
+#include <stdio.h>
+#include <errno.h>
+#include <arpa/inet.h>
+#include <fnmatch.h>
+
+int seq = 0;
+
+int
+edp_send(struct lldpd *global, struct lldpd_chassis *chassis,
+ struct lldpd_hardware *hardware)
+{
+ struct edp_header eh;
+ struct ethllc llc;
+ const u_int8_t mcastaddr[] = EDP_MULTICAST_ADDR;
+ const u_int8_t llcorg[] = LLC_ORG_EXTREME;
+ struct iovec *iov = NULL;
+ struct edp_tlv_vlan *ovlan = NULL;
+ struct lldpd_vlan *vlan;
+ struct edp_tlv_head device;
+ struct edp_tlv_head null;
+ struct edp_tlv_info info;
+ u_int8_t edp_fakeversion[] = {7, 6, 4, 99};
+ unsigned int i, c, v, len, state = 0;
+ /* Subsequent XXX can be replaced by other values. We place
+ them here to ensure the position of "" to be a bit
+ invariant with version changes. */
+ char *deviceslot[] = { "eth", "veth", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "", NULL };
+
+#define IOV_NEW \
+ if ((iov = (struct iovec*)realloc(iov, (++c + 1) * \
+ sizeof(struct iovec))) == NULL) \
+ fatal(NULL);
+
+ while (state != 2) {
+ free(iov); iov = NULL;
+ free(ovlan); ovlan = NULL;
+ c = v = -1;
+
+ /* Ether + LLC */
+ memset(&llc, 0, sizeof(llc));
+ memcpy(&llc.ether.shost, &hardware->h_lladdr,
+ sizeof(llc.ether.shost));
+ memcpy(&llc.ether.dhost, &mcastaddr,
+ sizeof(llc.ether.dhost));
+ llc.dsap = llc.ssap = 0xaa;
+ llc.control = 0x03;
+ memcpy(llc.org, llcorg, sizeof(llc.org));
+ llc.protoid = htons(LLC_PID_EDP);
+ IOV_NEW;
+ iov[c].iov_base = &llc;
+ iov[c].iov_len = sizeof(llc);
+
+ /* EDP header */
+ memset(&eh, 0, sizeof(eh));
+ eh.version = 1;
+ eh.sequence = htons(seq++);
+ if ((chassis->c_id_len != ETH_ALEN) ||
+ (chassis->c_id_subtype != LLDP_CHASSISID_SUBTYPE_LLADDR)) {
+ LLOG_WARNX("local chassis does not use MAC address as chassis ID!?");
+ return EINVAL;
+ }
+ memcpy(&eh.mac, chassis->c_id, ETH_ALEN);
+ IOV_NEW;
+ iov[c].iov_base = &eh;
+ iov[c].iov_len = sizeof(eh);
+
+ switch (state) {
+ case 0:
+ /* Display TLV */
+ memset(&device, 0, sizeof(device));
+ device.tlv_marker = EDP_TLV_MARKER;
+ device.tlv_type = EDP_TLV_DISPLAY;
+ device.tlv_len = htons(sizeof(device) + strlen(chassis->c_name) + 1);
+ IOV_NEW;
+ iov[c].iov_base = &device;
+ iov[c].iov_len = sizeof(device);
+ IOV_NEW;
+ iov[c].iov_base = chassis->c_name;
+ iov[c].iov_len = strlen(chassis->c_name) + 1;
+
+ /* Info TLV */
+ memset(&info, 0, sizeof(info));
+ info.head.tlv_marker = EDP_TLV_MARKER;
+ info.head.tlv_type = EDP_TLV_INFO;
+ info.head.tlv_len = htons(sizeof(info));
+ for (i=0; deviceslot[i] != NULL; i++) {
+ if (strncmp(hardware->h_ifname, deviceslot[i],
+ strlen(deviceslot[i])) == 0) {
+ info.slot = htons(i);
+ info.port = htons(atoi(hardware->h_ifname +
+ strlen(deviceslot[i])));
+ break;
+ }
+ }
+ if (info.port == -1) {
+ info.slot = htons(8);
+ info.port = htons(if_nametoindex(hardware->h_ifname));
+ }
+ memcpy(info.version, edp_fakeversion, sizeof(info.version));
+ info.connections[0] = info.connections[1] = 0xff;
+ IOV_NEW;
+ iov[c].iov_base = &info;
+ iov[c].iov_len = sizeof(info);
+ break;
+ case 1:
+ v = 0;
+ TAILQ_FOREACH(vlan, &hardware->h_lport.p_vlans,
+ v_entries)
+ v++;
+ if (v == 0) {
+ v = -1;
+ break;
+ }
+ if ((ovlan = (struct edp_tlv_vlan*)malloc(
+ v*sizeof(struct edp_tlv_vlan))) == NULL) {
+ LLOG_WARN("no room for vlans");
+ v = -1;
+ }
+ TAILQ_FOREACH(vlan, &hardware->h_lport.p_vlans,
+ v_entries) {
+ v--;
+ memset(&ovlan[v], 0, sizeof(ovlan[v]));
+ ovlan[v].head.tlv_marker = EDP_TLV_MARKER;
+ ovlan[v].head.tlv_type = EDP_TLV_VLAN;
+ ovlan[v].head.tlv_len = htons(sizeof(ovlan[v]) +
+ strlen(vlan->v_name) + 1);
+ ovlan[v].vid = htons(vlan->v_vid);
+ IOV_NEW;
+ iov[c].iov_base = &ovlan[v];
+ iov[c].iov_len = sizeof(ovlan[v]);
+ IOV_NEW;
+ iov[c].iov_base = vlan->v_name;
+ iov[c].iov_len = strlen(vlan->v_name) + 1;
+ }
+ break;
+ }
+
+ if ((state == 1) && (v == -1)) /* No VLAN, no need to send another TLV */
+ break;
+
+ /* Null TLV */
+ memset(&null, 0, sizeof(null));
+ null.tlv_marker = EDP_TLV_MARKER;
+ null.tlv_type = EDP_TLV_NULL;
+ null.tlv_len = htons(sizeof(null));
+ IOV_NEW;
+ iov[c].iov_base = &null;
+ iov[c].iov_len = sizeof(null);
+
+ c++;
+
+ /* Compute len and checksum */
+ len = 0;
+ for (i = 0; i < c; i++) {
+ len += iov[i].iov_len;
+ }
+ len -= sizeof(struct ieee8023);
+ llc.ether.size = htons(len);
+ len = len + sizeof(struct ieee8023) - sizeof(struct ethllc);
+ eh.len = htons(len);
+ eh.checksum = iov_checksum(&iov[1], c - 1, 0);
+
+ if (writev((hardware->h_raw_real > 0) ? hardware->h_raw_real :
+ hardware->h_raw, iov, c) == -1) {
+ LLOG_WARN("unable to send packet on real device for %s",
+ hardware->h_ifname);
+ free(ovlan);
+ free(iov);
+ return ENETDOWN;
+ }
+
+ state++;
+ }
+
+ hardware->h_tx_cnt++;
+ free(ovlan);
+ free(iov);
+
+ return 0;
+}
+
+int
+edp_decode(struct lldpd *cfg, char *frame, int s,
+ struct lldpd_hardware *hardware,
+ struct lldpd_chassis **newchassis, struct lldpd_port **newport)
+{
+ struct lldpd_chassis *chassis;
+ struct lldpd_port *port;
+ struct ethllc *llc;
+ struct edp_header *eh;
+ struct edp_tlv_head *tlv;
+ struct edp_tlv_info *info;
+ struct edp_tlv_vlan *vlan;
+ struct lldpd_vlan *lvlan, *lvlan_next;
+ const unsigned char edpaddr[] = EDP_MULTICAST_ADDR;
+ struct iovec iov;
+ int f, len, gotend = 0, gotvlans = 0;
+
+ if ((chassis = calloc(1, sizeof(struct lldpd_chassis))) == NULL) {
+ LLOG_WARN("failed to allocate remote chassis");
+ return -1;
+ }
+ if ((port = calloc(1, sizeof(struct lldpd_port))) == NULL) {
+ LLOG_WARN("failed to allocate remote port");
+ free(chassis);
+ return -1;
+ }
+ TAILQ_INIT(&port->p_vlans);
+
+ if (s < sizeof(struct ethllc) + sizeof(struct edp_header)) {
+ LLOG_WARNX("too short frame received on %s", hardware->h_ifname);
+ goto malformed;
+ }
+
+ llc = (struct ethllc *)frame;
+ if (memcmp(&llc->ether.dhost, edpaddr, sizeof(edpaddr)) != 0) {
+ LLOG_INFO("frame not targeted at EDP multicast address received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ if (ntohs(llc->ether.size) > s - sizeof(struct ieee8023)) {
+ LLOG_WARNX("incorrect 802.3 frame size reported on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ if (llc->protoid != htons(LLC_PID_EDP)) {
+ LLOG_DEBUG("incorrect LLC protocol ID received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+
+ f = sizeof(struct ethllc);
+ eh = (struct edp_header *)(frame + f);
+ if (eh->version != 1) {
+ LLOG_WARNX("incorrect EDP version (%d) for frame received on %s",
+ eh->version, hardware->h_ifname);
+ goto malformed;
+ }
+ if (eh->idtype != htons(0)) {
+ LLOG_WARNX("incorrect device id type for frame received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ if (ntohs(eh->len) > s - f) {
+ LLOG_WARNX("incorrect size for EDP frame received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ chassis->c_ttl = LLDPD_TTL;
+ chassis->c_id_subtype = LLDP_CHASSISID_SUBTYPE_LLADDR;
+ chassis->c_id_len = ETH_ALEN;
+ if ((chassis->c_id = (char *)malloc(ETH_ALEN)) == NULL) {
+ LLOG_WARN("unable to allocate memory for chassis ID");
+ goto malformed;
+ }
+ memcpy(chassis->c_id, eh->mac, ETH_ALEN);
+ /* We ignore reserved bytes and sequence number */
+ iov.iov_len = ntohs(eh->len);
+ iov.iov_base = frame + f;
+ if (iov_checksum(&iov, 1, 0) != 0) {
+ LLOG_WARNX("incorrect EDP checksum for frame received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+
+ f += sizeof(struct edp_header);
+ while ((f < s) && !gotend) {
+ if (f + sizeof(struct edp_tlv_head) > s) {
+ LLOG_WARNX("EDP TLV header is too large for "
+ "frame received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ tlv = (struct edp_tlv_head *)(frame + f);
+ len = ntohs(tlv->tlv_len) - sizeof(struct edp_tlv_head);
+ if ((len < 0) || (f + sizeof(struct edp_tlv_head) + len > s)) {
+ LLOG_DEBUG("incorrect size in EDP TLV header for frame "
+ "received on %s",
+ hardware->h_ifname);
+ /* Some poor old Extreme Summit are quite bogus */
+ gotend = 1;
+ break;
+ }
+ f += sizeof(struct edp_tlv_head);
+ if (tlv->tlv_marker != EDP_TLV_MARKER) {
+ LLOG_WARNX("incorrect marker starting EDP TLV header for frame "
+ "received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ switch (tlv->tlv_type) {
+ case EDP_TLV_INFO:
+ if (len != sizeof(struct edp_tlv_info) -
+ sizeof(struct edp_tlv_head)) {
+ LLOG_WARNX("wrong size for EDP TLV info for frame "
+ "received on %s (%d vs %d)",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ info = (struct edp_tlv_info *)(frame + f -
+ sizeof(struct edp_tlv_head));
+ port->p_id_subtype = LLDP_PORTID_SUBTYPE_IFNAME;
+ if (asprintf(&port->p_id, "%d/%d",
+ ntohs(info->slot) + 1, ntohs(info->port) + 1) == -1) {
+ LLOG_WARN("unable to allocate memory for "
+ "port ID");
+ goto malformed;
+ }
+ port->p_id_len = strlen(port->p_id);
+ if (asprintf(&port->p_descr, "Slot %d / Port %d",
+ ntohs(info->slot) + 1, ntohs(info->port) + 1) == -1) {
+ LLOG_WARN("unable to allocate memory for "
+ "port description");
+ goto malformed;
+ }
+ if (asprintf(&chassis->c_descr,
+ "EDP enabled device, version %d.%d.%d.%d",
+ info->version[0], info->version[1],
+ info->version[2], info->version[3]) == -1) {
+ LLOG_WARN("unable to allocate memory for "
+ "chassis description");
+ goto malformed;
+ }
+ break;
+ case EDP_TLV_DISPLAY:
+ if ((chassis->c_name = (char *)calloc(1, len + 1)) == NULL) {
+ LLOG_WARN("unable to allocate memory for chassis "
+ "name");
+ goto malformed;
+ }
+ /* TLV display contains a lot of garbage */
+ strlcpy(chassis->c_name, frame + f, len);
+ break;
+ case EDP_TLV_NULL:
+ if (len != 0) {
+ LLOG_WARNX("null tlv with incorrect size in frame "
+ "received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ if (f != s)
+ LLOG_DEBUG("extra data after edp frame on %s",
+ hardware->h_ifname);
+ gotend = 1;
+ break;
+ case EDP_TLV_VLAN:
+ if (len < sizeof(struct edp_tlv_vlan) -
+ sizeof(struct edp_tlv_head)) {
+ LLOG_WARNX("wrong size for EDP TLV vlan for frame "
+ "received on %s (%d vs %d)",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ vlan = (struct edp_tlv_vlan *)(frame + f -
+ sizeof(struct edp_tlv_head));
+ if ((lvlan = (struct lldpd_vlan *)calloc(1,
+ sizeof(struct lldpd_vlan))) == NULL) {
+ LLOG_WARN("unable to allocate vlan");
+ goto malformed;
+ }
+ lvlan->v_vid = ntohs(vlan->vid);
+ if ((lvlan->v_name = (char *)calloc(1, len + 1 -
+ sizeof(struct edp_tlv_vlan) +
+ sizeof(struct edp_tlv_head))) == NULL) {
+ LLOG_WARN("unable to allocate vlan name");
+ goto malformed;
+ }
+ strlcpy(lvlan->v_name, frame + f + sizeof(struct edp_tlv_vlan) -
+ sizeof(struct edp_tlv_head), len -
+ sizeof(struct edp_tlv_vlan) +
+ sizeof(struct edp_tlv_head));
+ if (vlan->ip.s_addr != INADDR_ANY) {
+ if (chassis->c_mgmt.s_addr == INADDR_ANY)
+ chassis->c_mgmt.s_addr = vlan->ip.s_addr;
+ else
+ /* We need to guess the good one */
+ if (cfg->g_mgmt_pattern != NULL) {
+ /* We can try to use this to prefer an address */
+ char *ip;
+ ip = inet_ntoa(vlan->ip);
+ if (fnmatch(cfg->g_mgmt_pattern,
+ ip, 0) == 0)
+ chassis->c_mgmt.s_addr = vlan->ip.s_addr;
+ }
+ }
+ TAILQ_INSERT_TAIL(&port->p_vlans,
+ lvlan, v_entries);
+ gotvlans = 1;
+ break;
+ default:
+ LLOG_DEBUG("unknown EDP TLV type (%d) received on %s",
+ tlv->tlv_type, hardware->h_ifname);
+ }
+ f += len;
+ }
+ if ((chassis->c_id == NULL) ||
+ (port->p_id == NULL) ||
+ (chassis->c_name == NULL) ||
+ (chassis->c_descr == NULL) ||
+ (port->p_descr == NULL) ||
+ (gotend == 0)) {
+ if (gotvlans && gotend) {
+ /* VLAN can be sent in a separate frames. We need to add
+ * those vlans to an existing chassis */
+ if (hardware->h_rchassis &&
+ (hardware->h_rchassis->c_id_subtype == chassis->c_id_subtype) &&
+ (hardware->h_rchassis->c_id_len == chassis->c_id_len) &&
+ (memcmp(hardware->h_rchassis->c_id, chassis->c_id,
+ chassis->c_id_len) == 0)) {
+ /* We attach the VLANs to current hardware */
+ lldpd_vlan_cleanup(hardware->h_rport);
+ for (lvlan = TAILQ_FIRST(&port->p_vlans);
+ lvlan != NULL;
+ lvlan = lvlan_next) {
+ lvlan_next = TAILQ_NEXT(lvlan, v_entries);
+ TAILQ_REMOVE(&port->p_vlans, lvlan, v_entries);
+ TAILQ_INSERT_TAIL(&hardware->h_rport->p_vlans,
+ lvlan, v_entries);
+ }
+ /* And the IP address */
+ hardware->h_rchassis->c_mgmt.s_addr =
+ chassis->c_mgmt.s_addr;
+ }
+ /* We discard the remaining frame */
+ goto malformed;
+ }
+ LLOG_WARNX("some mandatory tlv are missing for frame received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ *newchassis = chassis;
+ *newport = port;
+ return 1;
+
+malformed:
+ free(chassis->c_id);
+ free(chassis->c_descr);
+ free(chassis->c_name);
+ free(chassis);
+ free(port->p_id);
+ free(port->p_descr);
+ lldpd_vlan_cleanup(port);
+ free(port);
+ return -1;
+}
--- /dev/null
+/*
+ * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and 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.
+ */
+
+#ifndef _EDP_H
+#define _EDP_H
+
+#define EDP_MULTICAST_ADDR { \
+ 0x00, 0xe0, 0x2b, 0x00, 0x00, 0x00 \
+}
+#define LLC_ORG_EXTREME { 0x00, 0xe0, 0x2b }
+#define LLC_PID_EDP 0x00bb
+
+#define EDP_TLV_MARKER 0x99
+
+#include "llc.h"
+
+struct edp_header {
+ u_int8_t version;
+ u_int8_t reserved;
+ u_int16_t len;
+ u_int16_t checksum;
+ u_int16_t sequence;
+ u_int16_t idtype; /* Should be 0 for MAC */
+ u_int8_t mac[ETH_ALEN];
+} __attribute__ ((__packed__));
+
+struct edp_tlv_head {
+ u_int8_t tlv_marker; /* 0x99 */
+ u_int8_t tlv_type;
+ u_int16_t tlv_len;
+} __attribute__ ((__packed__));
+
+enum {
+ EDP_TLV_NULL = 0,
+ EDP_TLV_DISPLAY = 1,
+ EDP_TLV_INFO = 2,
+ EDP_TLV_VLAN = 5,
+ EDP_TLV_ESRP = 8,
+};
+
+struct edp_tlv_info {
+ struct edp_tlv_head head;
+ u_int16_t slot;
+ u_int16_t port;
+ u_int16_t vchassis;
+ u_int8_t reserved[6];
+ u_int8_t version[4];
+ u_int8_t connections[16];
+} __attribute__ ((__packed__));
+
+#define EDP_VLAN_HAS_IP (1 << 8)
+struct edp_tlv_vlan {
+ struct edp_tlv_head head;
+ u_int8_t flags;
+ u_int8_t reserved1[1];
+ u_int16_t vid;
+ u_int8_t reserved2[4];
+ struct in_addr ip;
+} __attribute__ ((__packed__));
+
+#endif /* _EDP_H */
--- /dev/null
+/*
+ * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and 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.
+ */
+
+#define INCLUDE_LINUX_IF_H
+#include "lldpd.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <ifaddrs.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <arpa/inet.h>
+#include <net/if_arp.h>
+#include <linux/if_vlan.h>
+#include <linux/if_bonding.h>
+#include <linux/if_bridge.h>
+#include <linux/wireless.h>
+#include <linux/sockios.h>
+
+#define SYSFS_PATH_MAX 256
+#define SYSFS_CLASS_NET "/sys/class/net/"
+#define MAX_PORTS 1024
+
+/* net/if.h */
+extern unsigned int if_nametoindex (__const char *__ifname) __THROW;
+
+int
+old_iface_is_bridge(struct lldpd *cfg, const char *name)
+{
+ struct ifreq ifr;
+ struct __bridge_info i;
+ unsigned long args[4] = { BRCTL_GET_BRIDGE_INFO,
+ (unsigned long) &i, 0, 0 };
+ strlcpy(ifr.ifr_name, name, IFNAMSIZ);
+ ifr.ifr_data = (char *) &args;
+
+ if (ioctl(cfg->g_sock, SIOCDEVPRIVATE, &ifr) < 0)
+ return 0;
+ return 1;
+}
+
+int
+iface_is_bridge(struct lldpd *cfg, const char *name)
+{
+ char path[SYSFS_PATH_MAX];
+ int f;
+
+ if ((snprintf(path, SYSFS_PATH_MAX,
+ SYSFS_CLASS_NET "%s/" SYSFS_BRIDGE_FDB, name)) >= SYSFS_PATH_MAX)
+ LLOG_WARNX("path truncated");
+ if ((f = open(path, 0)) < 0) {
+ return old_iface_is_bridge(cfg, name);
+ }
+ close(f);
+ return 1;
+}
+
+int
+old_iface_is_bridged(struct lldpd *cfg, const char *name)
+{
+ int i;
+ int ifindex = if_nametoindex(name);
+ int ifindices[MAX_PORTS];
+ unsigned long args[4] = { BRCTL_GET_PORT_LIST,
+ (unsigned long)ifindices, MAX_PORTS, 0 };
+ struct ifreq ifr;
+ struct ifaddrs *ifap, *ifa;
+
+ if (getifaddrs(&ifap) != 0) {
+ LLOG_WARN("unable to get interface list");
+ return 0;
+ }
+
+ for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
+ memset(ifindices, 0, sizeof(ifindices));
+ strncpy(ifr.ifr_name, ifa->ifa_name, IFNAMSIZ);
+ ifr.ifr_data = (char *)&args;
+
+ if (ioctl(cfg->g_sock, SIOCDEVPRIVATE, &ifr) < 0)
+ /* Not a bridge */
+ continue;
+
+ for (i = 0; i < MAX_PORTS; i++) {
+ if (ifindices[i] == ifindex) {
+ freeifaddrs(ifap);
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int
+iface_is_bridged(struct lldpd *cfg, const char *name)
+{
+ char path[SYSFS_PATH_MAX];
+ int f;
+
+ if ((snprintf(path, SYSFS_PATH_MAX,
+ SYSFS_CLASS_NET "%s/" SYSFS_BRIDGE_PORT_ATTR,
+ name)) >= SYSFS_PATH_MAX)
+ LLOG_WARNX("path truncated");
+ if ((f = open(path, 0)) < 0) {
+ return old_iface_is_bridged(cfg, name);
+ }
+ close(f);
+ return 1;
+}
+
+int
+iface_is_vlan(struct lldpd *cfg, const char *name)
+{
+ struct vlan_ioctl_args ifv;
+ memset(&ifv, 0, sizeof(ifv));
+ ifv.cmd = GET_VLAN_REALDEV_NAME_CMD;
+ if ((strlcpy(ifv.device1, name, sizeof(ifv.device1))) >=
+ sizeof(ifv.device1))
+ LLOG_WARNX("device name truncated");
+ if (ioctl(cfg->g_sock, SIOCGIFVLAN, &ifv) >= 0)
+ return 1;
+ return 0;
+}
+
+int
+iface_is_wireless(struct lldpd *cfg, const char *name)
+{
+ struct iwreq iwr;
+ strlcpy(iwr.ifr_name, name, IFNAMSIZ);
+ if (ioctl(cfg->g_sock, SIOCGIWNAME, &iwr) >= 0)
+ return 1;
+ return 0;
+}
+
+int
+iface_is_bond(struct lldpd *cfg, const char *name)
+{
+ struct ifreq ifr;
+ struct ifbond ifb;
+ memset(&ifr, 0, sizeof(ifr));
+ memset(&ifb, 0, sizeof(ifb));
+ strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
+ ifr.ifr_data = &ifb;
+ if (ioctl(cfg->g_sock, SIOCBONDINFOQUERY, &ifr) >= 0)
+ return 1;
+ return 0;
+}
+
+int
+iface_is_bond_slave(struct lldpd *cfg, const char *slave, const char *master)
+{
+ struct ifreq ifr;
+ struct ifbond ifb;
+ struct ifslave ifs;
+ memset(&ifr, 0, sizeof(ifr));
+ memset(&ifb, 0, sizeof(ifb));
+ strlcpy(ifr.ifr_name, master, sizeof(ifr.ifr_name));
+ ifr.ifr_data = &ifb;
+ if (ioctl(cfg->g_sock, SIOCBONDINFOQUERY, &ifr) >= 0) {
+ while (ifb.num_slaves--) {
+ memset(&ifr, 0, sizeof(ifr));
+ memset(&ifs, 0, sizeof(ifs));
+ strlcpy(ifr.ifr_name, master, sizeof(ifr.ifr_name));
+ ifr.ifr_data = &ifs;
+ ifs.slave_id = ifb.num_slaves;
+ if ((ioctl(cfg->g_sock, SIOCBONDSLAVEINFOQUERY, &ifr) >= 0) &&
+ (strncmp(ifs.slave_name, slave, sizeof(ifs.slave_name)) == 0))
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int
+iface_is_enslaved(struct lldpd *cfg, const char *name)
+{
+ struct ifaddrs *ifap, *ifa;
+ int master;
+
+ if (getifaddrs(&ifap) != 0) {
+ LLOG_WARN("unable to get interface list");
+ return -1;
+ }
+ for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
+ if (iface_is_bond_slave(cfg, name, ifa->ifa_name)) {
+ master = if_nametoindex(ifa->ifa_name);
+ freeifaddrs(ifap);
+ return master;
+ }
+ }
+ freeifaddrs(ifap);
+ return -1;
+}
--- /dev/null
+/*
+ * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and 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 "lldpd.h"
+
+void
+iov_dump(struct lldpd_frame **buffer, struct iovec *iov, int count)
+{
+ int i;
+ int len = 0;
+ void *p;
+ for (i = 0; i < count; i++)
+ len += iov[i].iov_len;
+ if ((*buffer = (struct lldpd_frame *)malloc(len +
+ sizeof(int))) == NULL) {
+ LLOG_WARN("unable to allocate buffer");
+ return;
+ }
+ memcpy(*buffer, &len, sizeof(int));
+ p = (*buffer)->frame;
+ for (i = 0; i < count; i++) {
+ memcpy(p, iov[i].iov_base, iov[i].iov_len);
+ p += iov[i].iov_len;
+ }
+}
+
+u_int16_t
+iov_checksum(struct iovec *iov, int count, int cisco)
+{
+ unsigned int sum = 0, v = 0;
+ int len, oddbyte = 0, i;
+ u_char *cp;
+
+ /* We compute in network byte order */
+ for (i = 0; i < count; i++) {
+ len = iov[i].iov_len;
+ cp = iov[i].iov_base;
+ if (oddbyte) {
+ sum += (v << 8) + *cp++;
+ len--;
+ }
+ while ((len -= 2) >= 0) {
+ sum += *cp++ << 8;
+ sum += *cp++;
+ }
+ if ((oddbyte = len & 1) != 0)
+ v = *cp;
+ }
+ /* The remaining byte seems to be handled oddly by Cisco. Any hint about
+ * this is welcome. */
+ if (oddbyte) {
+ if (cisco)
+ sum += v;
+ else
+ sum += v << 8;
+ }
+ sum = (sum >> 16) + (sum & 0xffff);
+ sum += sum >> 16;
+ sum = ntohs(sum);
+ return (0xffff & ~sum);
+}
--- /dev/null
+/*
+ * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and 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.
+ */
+
+#ifndef _LLC_H
+#define _LLC_H
+
+struct ieee8023 {
+ u_int8_t dhost[ETH_ALEN]; /* destination eth addr */
+ u_int8_t shost[ETH_ALEN]; /* source ether addr */
+ u_int16_t size; /* packet type ID field */
+} __attribute__ ((__packed__));
+
+struct ethllc {
+ struct ieee8023 ether;
+ u_int8_t dsap; /* destination SAP */
+ u_int8_t ssap; /* source SAP */
+ u_int8_t control; /* LLC control field */
+ u_int8_t org[3];
+ u_int16_t protoid;
+} __attribute__ ((__packed__));
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and 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 "lldpd.h"
+
+#include <unistd.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <netpacket/packet.h>
+#include <linux/sockios.h>
+
+int
+lldp_send(struct lldpd *global, struct lldpd_chassis *chassis,
+ struct lldpd_hardware *hardware)
+{
+ struct ether_header eh;
+ const u_int8_t mcastaddr[] = LLDP_MULTICAST_ADDR;
+ const u_int8_t dot1[] = LLDP_TLV_ORG_DOT1;
+ const u_int8_t dot3[] = LLDP_TLV_ORG_DOT3;
+ struct iovec *iov = NULL;
+ struct lldp_vlan *ovlan = NULL;
+ struct lldp_id chid, pid;
+ struct lldp_ttl ttl;
+ struct lldp_end end;
+ struct lldp_string name;
+ struct lldp_string descr;
+ struct lldp_string str;
+ struct lldp_cap cap;
+ struct lldp_mgmt mgmt;
+ struct lldp_aggreg aggreg;
+ struct lldp_macphy macphy;
+ struct lldpd_vlan *vlan;
+ struct lldpd_port *port = &hardware->h_lport;
+ u_int c = -1, len = 0, v;
+ struct lldpd_frame *buffer;
+
+#define IOV_NEW \
+ if ((iov = (struct iovec*)realloc(iov, (++c + 1) * \
+ sizeof(struct iovec))) == NULL) \
+ fatal(NULL);
+
+ /* Ethernet header */
+ memset(&eh, 0, sizeof(eh));
+ memcpy(&eh.ether_shost, &hardware->h_lladdr,
+ sizeof(eh.ether_shost));
+ memcpy(&eh.ether_dhost, &mcastaddr,
+ sizeof(eh.ether_dhost));
+ eh.ether_type = htons(ETHERTYPE_LLDP);
+ IOV_NEW;
+ iov[c].iov_base = &eh;
+ iov[c].iov_len = sizeof(struct ether_header);
+
+ /* Chassis ID */
+ memset(&chid, 0, sizeof(chid));
+ len = chassis->c_id_len + sizeof(chid);
+ chid.tlv_head.type_len = LLDP_TLV_HEAD(LLDP_TLV_CHASSIS_ID,
+ len - sizeof(struct lldp_tlv_head));
+ chid.tlv_id_subtype = chassis->c_id_subtype;
+ IOV_NEW;
+ iov[c].iov_base = &chid;
+ iov[c].iov_len = sizeof(chid);
+ IOV_NEW;
+ iov[c].iov_base = chassis->c_id;
+ iov[c].iov_len = chassis->c_id_len;
+
+ /* Port ID */
+ memset(&pid, 0, sizeof(pid));
+ len = port->p_id_len + sizeof(pid);
+ pid.tlv_head.type_len = LLDP_TLV_HEAD(LLDP_TLV_PORT_ID,
+ len - sizeof(struct lldp_tlv_head));
+ pid.tlv_id_subtype = port->p_id_subtype;
+ IOV_NEW;
+ iov[c].iov_base = &pid;
+ iov[c].iov_len = sizeof(pid);
+ IOV_NEW;
+ iov[c].iov_base = port->p_id;
+ iov[c].iov_len = port->p_id_len;
+
+ /* Time to live */
+ memset(&ttl, 0, sizeof(ttl));
+ len = sizeof(ttl);
+ ttl.tlv_head.type_len = LLDP_TLV_HEAD(LLDP_TLV_TTL,
+ len - sizeof(struct lldp_tlv_head));
+ ttl.tlv_ttl = htons(chassis->c_ttl);
+ IOV_NEW;
+ iov[c].iov_base = &ttl;
+ iov[c].iov_len = sizeof(ttl);
+
+ /* System name */
+ memset(&name, 0, sizeof(name));
+ len = sizeof(name) + strlen(chassis->c_name);
+ name.tlv_head.type_len = LLDP_TLV_HEAD(LLDP_TLV_SYSTEM_NAME,
+ len - sizeof(struct lldp_tlv_head));
+ IOV_NEW;
+ iov[c].iov_base = &name;
+ iov[c].iov_len = sizeof(name);
+ IOV_NEW;
+ iov[c].iov_base = chassis->c_name;
+ iov[c].iov_len = strlen(chassis->c_name);
+
+ /* System description */
+ memset(&descr, 0, sizeof(descr));
+ len = sizeof(descr) + strlen(chassis->c_descr);
+ descr.tlv_head.type_len = LLDP_TLV_HEAD(LLDP_TLV_SYSTEM_DESCR,
+ len - sizeof(struct lldp_tlv_head));
+ IOV_NEW;
+ iov[c].iov_base = &descr;
+ iov[c].iov_len = sizeof(descr);
+ IOV_NEW;
+ iov[c].iov_base = chassis->c_descr;
+ iov[c].iov_len = strlen(chassis->c_descr);
+
+ /* System capabilities */
+ memset(&cap, 0, sizeof(cap));
+ len = sizeof(cap);
+ cap.tlv_head.type_len = LLDP_TLV_HEAD(LLDP_TLV_SYSTEM_CAP,
+ len - sizeof(struct lldp_tlv_head));
+ cap.tlv_cap_available = htons(chassis->c_cap_available);
+ cap.tlv_cap_enabled = htons(chassis->c_cap_enabled);
+ IOV_NEW;
+ iov[c].iov_base = ∩
+ iov[c].iov_len = len;
+
+ if (chassis->c_mgmt.s_addr != INADDR_ANY) {
+ /* Management address */
+ memset(&mgmt, 0, sizeof(mgmt));
+ len = sizeof(mgmt);
+ mgmt.tlv_head.type_len = LLDP_TLV_HEAD(LLDP_TLV_MGMT_ADDR,
+ len - sizeof(struct lldp_tlv_head));
+ mgmt.mgmt_len = sizeof(struct in_addr) + sizeof(u_int8_t);
+ mgmt.mgmt_subtype = LLDP_MGMT_ADDR_IP4;
+ memcpy(&mgmt.mgmt_addr, &chassis->c_mgmt,
+ sizeof(struct in_addr));
+
+ /* Interface port type, OID */
+ if (chassis->c_mgmt_if == 0)
+ mgmt.mgmt_iface_subtype =
+ LLDP_MGMT_IFACE_UNKNOWN;
+ else {
+ mgmt.mgmt_iface_subtype =
+ LLDP_MGMT_IFACE_IFINDEX;
+ mgmt.mgmt_iface_id =
+ htonl(chassis->c_mgmt_if);
+ }
+ IOV_NEW;
+ iov[c].iov_base = &mgmt;
+ iov[c].iov_len = len;
+ }
+
+ /* Port description */
+ memset(&str, 0, sizeof(str));
+ len = sizeof(str) + strlen(port->p_descr);
+ str.tlv_head.type_len = LLDP_TLV_HEAD(LLDP_TLV_PORT_DESCR,
+ len - sizeof(struct lldp_tlv_head));
+ IOV_NEW;
+ iov[c].iov_base = &str;
+ iov[c].iov_len = sizeof(str);
+ IOV_NEW;
+ iov[c].iov_base = port->p_descr;
+ iov[c].iov_len = strlen(port->p_descr);
+
+ /* VLANs */
+ v = 0;
+ TAILQ_FOREACH(vlan, &port->p_vlans, v_entries)
+ v++;
+ if ((v > 0) &&
+ ((ovlan = (struct lldp_vlan*)malloc(v*sizeof(struct lldp_vlan))) == NULL))
+ LLOG_WARN("no room for vlans");
+ else {
+ TAILQ_FOREACH(vlan, &port->p_vlans, v_entries) {
+ v--;
+ memset(&ovlan[v], 0, sizeof(ovlan[v]));
+ ovlan[v].tlv_head.type_len = LLDP_TLV_HEAD(LLDP_TLV_ORG,
+ sizeof(ovlan[v].tlv_org_id) +
+ sizeof(ovlan[v].tlv_org_subtype) + sizeof(ovlan[v].vid) +
+ sizeof(ovlan[v].len) + strlen(vlan->v_name));
+ memcpy(ovlan[v].tlv_org_id, dot1, sizeof(ovlan[v].tlv_org_id));
+ ovlan[v].tlv_org_subtype = LLDP_TLV_DOT1_VLANNAME;
+ ovlan[v].vid = htons(vlan->v_vid);
+ ovlan[v].len = strlen(vlan->v_name);
+ IOV_NEW;
+ iov[c].iov_base = &ovlan[v];
+ iov[c].iov_len = sizeof(ovlan[v]);
+ IOV_NEW;
+ iov[c].iov_base = vlan->v_name;
+ iov[c].iov_len = strlen(vlan->v_name);
+ }
+ }
+
+ /* Aggregation status */
+ memset(&aggreg, 0, sizeof(aggreg));
+ aggreg.tlv_head.type_len = LLDP_TLV_HEAD(LLDP_TLV_ORG,
+ sizeof(aggreg.tlv_org_id) +
+ sizeof(aggreg.tlv_org_subtype) +
+ sizeof(aggreg.status) + sizeof(aggreg.id));
+ memcpy(aggreg.tlv_org_id, dot3, sizeof(aggreg.tlv_org_id));
+ aggreg.tlv_org_subtype = LLDP_TLV_DOT3_LA;
+ aggreg.status = (port->p_aggregid) ? 3:1; /* Bit 0 = capability ; Bit 1 = status */
+ aggreg.id = htonl(port->p_aggregid);
+ IOV_NEW;
+ iov[c].iov_base = &aggreg;
+ iov[c].iov_len = sizeof(aggreg);
+
+ /* MAC/PHY */
+ memset(&macphy, 0, sizeof(macphy));
+ macphy.tlv_head.type_len = LLDP_TLV_HEAD(LLDP_TLV_ORG,
+ sizeof(macphy.tlv_org_id) +
+ sizeof(macphy.tlv_org_subtype) +
+ sizeof(macphy.autoneg) + sizeof(macphy.advertised) +
+ sizeof(macphy.mau));
+ memcpy(macphy.tlv_org_id, dot3, sizeof(macphy.tlv_org_id));
+ macphy.tlv_org_subtype = LLDP_TLV_DOT3_MAC;
+ macphy.autoneg = port->p_autoneg_support |
+ (port->p_autoneg_enabled << 1);
+ macphy.advertised = htons(port->p_autoneg_advertised);
+ macphy.mau = htons(port->p_mau_type);
+ IOV_NEW;
+ iov[c].iov_base = &macphy;
+ iov[c].iov_len = sizeof(macphy);
+
+ /* END */
+ memset(&end, 0, sizeof(end));
+ IOV_NEW;
+ iov[c].iov_base = &end;
+ iov[c].iov_len = sizeof(end);
+
+ c++;
+ if (!global->g_multi ||
+ (hardware->h_mode == LLDPD_MODE_ANY) ||
+ (hardware->h_mode == LLDPD_MODE_LLDP)) {
+
+ if (writev((hardware->h_raw_real > 0) ? hardware->h_raw_real :
+ hardware->h_raw, iov, c) == -1) {
+ LLOG_WARN("unable to send packet on real device for %s",
+ hardware->h_ifname);
+ free(iov);
+ free(ovlan);
+ return ENETDOWN;
+ }
+
+ hardware->h_tx_cnt++;
+ }
+
+ iov_dump(&buffer, iov, c);
+ free(iov);
+ free(ovlan);
+ if (buffer != NULL) {
+
+ /* We assume that LLDP frame is the reference */
+ if ((hardware->h_llastframe == NULL) ||
+ (hardware->h_llastframe->size != buffer->size) ||
+ (memcmp(hardware->h_llastframe->frame, buffer->frame,
+ buffer->size) != 0)) {
+ free(hardware->h_llastframe);
+ hardware->h_llastframe = buffer;
+ hardware->h_llastchange = time(NULL);
+ } else
+ free(buffer);
+ }
+
+ return 0;
+}
+
+int
+lldp_decode(struct lldpd *cfg, char *frame, int s,
+ struct lldpd_hardware *hardware,
+ struct lldpd_chassis **newchassis, struct lldpd_port **newport)
+{
+ struct lldpd_chassis *chassis;
+ struct lldpd_port *port;
+ struct ether_header *ether;
+ const char lldpaddr[] = LLDP_MULTICAST_ADDR;
+ const char dot1[] = LLDP_TLV_ORG_DOT1;
+ const char dot3[] = LLDP_TLV_ORG_DOT3;
+ int f; /* Current position in frame */
+ int size, type, subtype; /* TLV header */
+ char *b;
+ int gotend = 0;
+
+ if ((chassis = calloc(1, sizeof(struct lldpd_chassis))) == NULL) {
+ LLOG_WARN("failed to allocate remote chassis");
+ return -1;
+ }
+ if ((port = calloc(1, sizeof(struct lldpd_port))) == NULL) {
+ LLOG_WARN("failed to allocate remote port");
+ free(chassis);
+ return -1;
+ }
+ TAILQ_INIT(&port->p_vlans);
+
+ if (s < sizeof(struct ether_header)) {
+ LLOG_WARNX("too short frame received on %s", hardware->h_ifname);
+ goto malformed;
+ }
+ ether = (struct ether_header *)frame;
+ if (memcmp(ether->ether_dhost, lldpaddr, sizeof(lldpaddr)) != 0) {
+ LLOG_INFO("frame not targeted at LLDP multicast address received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ if (ETHERTYPE_LLDP != ntohs(ether->ether_type)) {
+ LLOG_INFO("non LLDP frame received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+
+ f = sizeof(struct ether_header);
+ while ((f < s) && (!gotend)) {
+ if (f + 2 > s) {
+ LLOG_WARNX("tlv header too short received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ size = ntohs(*(u_int16_t*)(frame + f)) & 0x1ff;
+ type = ntohs(*(u_int16_t*)(frame + f)) >> 9;
+ f += 2;
+ if (f + size > s) {
+ LLOG_WARNX("tlv header too short received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ switch (type) {
+ case LLDP_TLV_END:
+ if (size != 0) {
+ LLOG_WARNX("lldp end received with size not null on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ if (f != s)
+ LLOG_DEBUG("extra data after lldp end on %s",
+ hardware->h_ifname);
+ gotend = 1;
+ break;
+ case LLDP_TLV_CHASSIS_ID:
+ case LLDP_TLV_PORT_ID:
+ if (size < 2) {
+ LLOG_WARNX("tlv id too small received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ subtype = *(u_int8_t*)(frame + f);
+ f++;
+ if ((subtype == 0) || (subtype > 7)) {
+ LLOG_WARNX("unknown subtype for tlv id received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ if ((b = (char *)calloc(1, size - 1)) == NULL) {
+ LLOG_WARN("unable to allocate memory for id tlv "
+ "received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ memcpy(b, frame + f, size - 1);
+ if (type == LLDP_TLV_PORT_ID) {
+ port->p_id_subtype = subtype;
+ port->p_id = b;
+ port->p_id_len = size - 1;
+ } else {
+ chassis->c_id_subtype = subtype;
+ chassis->c_id = b;
+ chassis->c_id_len = size - 1;
+ }
+ f += size - 1;
+ break;
+ case LLDP_TLV_TTL:
+ if (size != 2) {
+ LLOG_WARNX("incorrect size for ttl tlv received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ chassis->c_ttl = ntohs(*(u_int16_t*)(frame + f));
+ f += 2;
+ break;
+ case LLDP_TLV_PORT_DESCR:
+ case LLDP_TLV_SYSTEM_NAME:
+ case LLDP_TLV_SYSTEM_DESCR:
+ if (size < 1) {
+ LLOG_WARNX("tlv string too short received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ if ((b = (char *)calloc(1, size + 1)) == NULL) {
+ LLOG_WARN("unable to allocate memory for string tlv "
+ "received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ memcpy(b, frame + f, size);
+ f += size;
+ if (type == LLDP_TLV_PORT_DESCR)
+ port->p_descr = b;
+ else if (type == LLDP_TLV_SYSTEM_NAME)
+ chassis->c_name = b;
+ else chassis->c_descr = b;
+ break;
+ case LLDP_TLV_SYSTEM_CAP:
+ if (size != 4) {
+ LLOG_WARNX("system cap tlv with incorrect size received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ chassis->c_cap_available = ntohs(*(u_int16_t*)(frame + f));
+ f += 2;
+ chassis->c_cap_enabled = ntohs(*(u_int16_t*)(frame + f));
+ f += 2;
+ break;
+ case LLDP_TLV_MGMT_ADDR:
+ if (size < 11) {
+ LLOG_WARNX("too short management tlv received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ if ((chassis->c_mgmt.s_addr == INADDR_ANY) &&
+ (*(u_int8_t*)(frame + f) == 5) &&
+ (*(u_int8_t*)(frame + f + 1) == 1)) {
+ /* We have an IPv4 address, we ignore anything else */
+ memcpy(&chassis->c_mgmt, frame + f + 2, sizeof(struct in_addr));
+ chassis->c_mgmt_if = 0;
+ /* We only handle ifIndex subtype */
+ if (*(u_int8_t*)(frame + f + 6) == LLDP_MGMT_IFACE_IFINDEX)
+ chassis->c_mgmt_if = ntohl(*(u_int32_t*)(frame + f + 7));
+ }
+ f += size;
+ break;
+ case LLDP_TLV_ORG:
+ if (size < 4) {
+ LLOG_WARNX("too short org tlv received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ if (memcmp(dot1, frame + f, 3) == 0) {
+ /* Dot1 */
+ if ((*(u_int8_t*)(frame + f + 3)) ==
+ LLDP_TLV_DOT1_VLANNAME) {
+ struct lldpd_vlan *vlan;
+ int vlan_len;
+
+ if ((size < 7) ||
+ (size != 7 + *(u_int8_t*)(frame + f + 6))) {
+ LLOG_WARNX("too short vlan tlv received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ f += 4;
+ if ((vlan = (struct lldpd_vlan *)calloc(1,
+ sizeof(struct lldpd_vlan))) == NULL) {
+ LLOG_WARN("unable to alloc vlan structure for "
+ "tlv received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ vlan->v_vid = ntohs(*(u_int16_t*)(frame + f));
+ f += 2;
+ vlan_len = *(u_int8_t*)(frame + f);
+ f += 1;
+ if ((vlan->v_name =
+ (char *)calloc(1, vlan_len + 1)) == NULL) {
+ LLOG_WARN("unable to alloc vlan name for "
+ "tlv received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ memcpy(vlan->v_name, frame + f,
+ vlan_len);
+ TAILQ_INSERT_TAIL(&port->p_vlans,
+ vlan, v_entries);
+ f += vlan_len;
+ } else
+ /* Unknown Dot1 TLV, ignore it */
+ f += size;
+ } else if (memcmp(dot3, frame + f, 3) == 0) {
+ /* Dot3 */
+ subtype = *(u_int8_t*)(frame + f + 3);
+ switch (subtype) {
+ case LLDP_TLV_DOT3_MAC:
+ f += 4;
+ if (size != 9) {
+ LLOG_WARNX("too short mac/phy tlv "
+ "received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ port->p_autoneg_support =
+ *(u_int8_t*)(frame + f) && 0x1;
+ port->p_autoneg_enabled =
+ *(u_int8_t*)(frame + f) && 0x2;
+ f += 1;
+ port->p_autoneg_advertised =
+ ntohs(*(u_int16_t*)(frame + f));
+ f += 2;
+ port->p_mau_type =
+ ntohs(*(u_int16_t*)(frame + f));
+ f += 2;
+ break;
+ case LLDP_TLV_DOT3_LA:
+ if (size != 9) {
+ LLOG_WARNX("too short aggreg tlv "
+ "received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ port->p_aggregid =
+ ntohl(*(u_int32_t*)(frame + f + 5));
+ f += 9;
+ break;
+ default:
+ /* Unknown Dot3 TLV, ignore it */
+ f += size;
+ }
+ } else {
+ LLOG_WARNX("unknown org tlv received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ break;
+ default:
+ LLOG_WARNX("unknown tlv (%d) received on %s",
+ type, hardware->h_ifname);
+ goto malformed;
+ }
+ }
+
+ /* Some random check */
+ if ((chassis->c_id == NULL) ||
+ (port->p_id == NULL) ||
+ (chassis->c_ttl == 0) ||
+ (gotend == 0)) {
+ LLOG_WARNX("some mandatory tlv are missing for frame received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+#define NOTRECEIVED "Not received"
+ if (chassis->c_name == NULL) {
+ if ((chassis->c_name = (char *)calloc(1, strlen(NOTRECEIVED) + 1)) == NULL) {
+ LLOG_WARNX("unable to allocate null chassis name");
+ goto malformed;
+ }
+ memcpy(chassis->c_name, NOTRECEIVED, strlen(NOTRECEIVED));
+ }
+ if (chassis->c_descr == NULL) {
+ if ((chassis->c_descr = (char *)calloc(1, strlen(NOTRECEIVED) + 1)) == NULL) {
+ LLOG_WARNX("unable to allocate null chassis description");
+ goto malformed;
+ }
+ memcpy(chassis->c_descr, NOTRECEIVED, strlen(NOTRECEIVED));
+ }
+ if (port->p_descr == NULL) {
+ if ((port->p_descr = (char *)calloc(1, strlen(NOTRECEIVED) + 1)) == NULL) {
+ LLOG_WARNX("unable to allocate null port description");
+ goto malformed;
+ }
+ memcpy(port->p_descr, NOTRECEIVED, strlen(NOTRECEIVED));
+ }
+ *newchassis = chassis;
+ *newport = port;
+ return 1;
+malformed:
+ free(chassis->c_id);
+ free(chassis->c_name);
+ free(chassis->c_descr);
+ free(chassis);
+ free(port->p_id);
+ free(port->p_descr);
+ lldpd_vlan_cleanup(port);
+ free(port);
+ return -1;
+}
--- /dev/null
+/*
+ * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and 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.
+ */
+
+#ifndef _LLDP_H
+#define _LLDP_H
+
+/* Should be defined in net/ethertypes.h */
+#ifndef ETHERTYPE_LLDP
+#define ETHERTYPE_LLDP 0x88cc
+#endif
+
+#define LLDP_MULTICAST_ADDR { \
+ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e \
+}
+
+#define LLDP_TLV_HEAD(type, len) htons(((type) << 9) | (len))
+struct lldp_tlv_head {
+ u_int16_t type_len;
+} __attribute__ ((__packed__));
+
+enum {
+ LLDP_TLV_END = 0,
+ LLDP_TLV_CHASSIS_ID = 1,
+ LLDP_TLV_PORT_ID = 2,
+ LLDP_TLV_TTL = 3,
+ LLDP_TLV_PORT_DESCR = 4,
+ LLDP_TLV_SYSTEM_NAME = 5,
+ LLDP_TLV_SYSTEM_DESCR = 6,
+ LLDP_TLV_SYSTEM_CAP = 7,
+ LLDP_TLV_MGMT_ADDR = 8,
+ LLDP_TLV_ORG = 127
+};
+
+#define LLDP_TLV_ORG_DOT1 {0x00, 0x80, 0xc2}
+#define LLDP_TLV_ORG_DOT3 {0x00, 0x12, 0x0f}
+
+enum {
+ LLDP_TLV_DOT1_PVID = 1,
+ LLDP_TLV_DOT1_PPVID = 2,
+ LLDP_TLV_DOT1_VLANNAME = 3,
+ LLDP_TLV_DOT1_PI = 4
+};
+
+enum {
+ LLDP_TLV_DOT3_MAC = 1,
+ LLDP_TLV_DOT3_POWER = 2,
+ LLDP_TLV_DOT3_LA = 3,
+ LLDP_TLV_DOT3_MFS = 4
+};
+
+/* Chassis ID or Port ID */
+struct lldp_id {
+ struct lldp_tlv_head tlv_head;
+ u_int8_t tlv_id_subtype;
+} __attribute__ ((__packed__));
+
+enum {
+ LLDP_CHASSISID_SUBTYPE_CHASSIS = 1,
+ LLDP_CHASSISID_SUBTYPE_IFALIAS = 2,
+ LLDP_CHASSISID_SUBTYPE_PORT = 3,
+ LLDP_CHASSISID_SUBTYPE_LLADDR = 4,
+ LLDP_CHASSISID_SUBTYPE_ADDR = 5,
+ LLDP_CHASSISID_SUBTYPE_IFNAME = 6,
+ LLDP_CHASSISID_SUBTYPE_LOCAL = 7
+};
+
+enum {
+ LLDP_PORTID_SUBTYPE_IFALIAS = 1,
+ LLDP_PORTID_SUBTYPE_PORT = 2,
+ LLDP_PORTID_SUBTYPE_LLADDR = 3,
+ LLDP_PORTID_SUBTYPE_ADDR = 4,
+ LLDP_PORTID_SUBTYPE_IFNAME = 5,
+ LLDP_PORTID_SUBTYPE_AGENTCID = 6,
+ LLDP_PORTID_SUBTYPE_LOCAL = 7
+};
+
+struct lldp_ttl {
+ struct lldp_tlv_head tlv_head;
+ u_int16_t tlv_ttl;
+} __attribute__ ((__packed__));
+
+struct lldp_string {
+ struct lldp_tlv_head tlv_head;
+} __attribute__ ((__packed__));
+
+struct lldp_cap {
+ struct lldp_tlv_head tlv_head;
+ u_int16_t tlv_cap_available;
+ u_int16_t tlv_cap_enabled;
+} __attribute__ ((__packed__));
+
+/* Operational MAU Type field, from RFC 3636 */
+#define LLDP_DOT3_MAU_AUI 1
+#define LLDP_DOT3_MAU_10BASE5 2
+#define LLDP_DOT3_MAU_FOIRL 3
+#define LLDP_DOT3_MAU_10BASE2 4
+#define LLDP_DOT3_MAU_10BASET 5
+#define LLDP_DOT3_MAU_10BASEFP 6
+#define LLDP_DOT3_MAU_10BASEFB 7
+#define LLDP_DOT3_MAU_10BASEFL 8
+#define LLDP_DOT3_MAU_10BROAD36 9
+#define LLDP_DOT3_MAU_10BASETHD 10
+#define LLDP_DOT3_MAU_10BASETFD 11
+#define LLDP_DOT3_MAU_10BASEFLHD 12
+#define LLDP_DOT3_MAU_10BASEFLDF 13
+#define LLDP_DOT3_MAU_10BASET4 14
+#define LLDP_DOT3_MAU_100BASETXHD 15
+#define LLDP_DOT3_MAU_100BASETXFD 16
+#define LLDP_DOT3_MAU_100BASEFXHD 17
+#define LLDP_DOT3_MAU_100BASEFXFD 18
+#define LLDP_DOT3_MAU_100BASET2HD 19
+#define LLDP_DOT3_MAU_100BASET2DF 20
+#define LLDP_DOT3_MAU_1000BASEXHD 21
+#define LLDP_DOT3_MAU_1000BASEXFD 22
+#define LLDP_DOT3_MAU_1000BASELXHD 23
+#define LLDP_DOT3_MAU_1000BASELXFD 24
+#define LLDP_DOT3_MAU_1000BASESXHD 25
+#define LLDP_DOT3_MAU_1000BASESXFD 26
+#define LLDP_DOT3_MAU_1000BASECXHD 27
+#define LLDP_DOT3_MAU_1000BASECXFD 28
+#define LLDP_DOT3_MAU_1000BASETHD 29
+#define LLDP_DOT3_MAU_1000BASETFD 30
+#define LLDP_DOT3_MAU_10GIGBASEX 31
+#define LLDP_DOT3_MAU_10GIGBASELX4 32
+#define LLDP_DOT3_MAU_10GIGBASER 33
+#define LLDP_DOT3_MAU_10GIGBASEER 34
+#define LLDP_DOT3_MAU_10GIGBASELR 35
+#define LLDP_DOT3_MAU_10GIGBASESR 36
+#define LLDP_DOT3_MAU_10GIGBASEW 37
+#define LLDP_DOT3_MAU_10GIGBASEEW 38
+#define LLDP_DOT3_MAU_10GIGBASELW 39
+#define LLDP_DOT3_MAU_10GIGBASESW 40
+
+/* PMD Auto-Negotiation Advertised Capability field, from RFC 3636 */
+#define LLDP_DOT3_LINK_AUTONEG_OTHER 0x8000
+#define LLDP_DOT3_LINK_AUTONEG_10BASE_T 0x4000
+#define LLDP_DOT3_LINK_AUTONEG_10BASET_FD 0x2000
+#define LLDP_DOT3_LINK_AUTONEG_100BASE_T4 0x1000
+#define LLDP_DOT3_LINK_AUTONEG_100BASE_TX 0x0800
+#define LLDP_DOT3_LINK_AUTONEG_100BASE_TXFD 0x0400
+#define LLDP_DOT3_LINK_AUTONEG_100BASE_T2 0x0200
+#define LLDP_DOT3_LINK_AUTONEG_100BASE_T2FD 0x0100
+#define LLDP_DOT3_LINK_AUTONEG_FDX_PAUSE 0x0080
+#define LLDP_DOT3_LINK_AUTONEG_FDX_APAUSE 0x0040
+#define LLDP_DOT3_LINK_AUTONEG_FDX_SPAUSE 0x0020
+#define LLDP_DOT3_LINK_AUTONEG_FDX_BPAUSE 0x0010
+#define LLDP_DOT3_LINK_AUTONEG_1000BASE_X 0x0008
+#define LLDP_DOT3_LINK_AUTONEG_1000BASE_XFD 0x0004
+#define LLDP_DOT3_LINK_AUTONEG_1000BASE_T 0x0002
+#define LLDP_DOT3_LINK_AUTONEG_1000BASE_TFD 0x0001
+
+#define LLDP_CAP_OTHER 0x01
+#define LLDP_CAP_REPEATER 0x02
+#define LLDP_CAP_BRIDGE 0x04
+#define LLDP_CAP_WLAN 0x08
+#define LLDP_CAP_ROUTER 0x10
+#define LLDP_CAP_TELEPHONE 0x20
+#define LLDP_CAP_DOCSIS 0x40
+#define LLDP_CAP_STATION 0x80
+
+/* see http://www.iana.org/assignments/address-family-numbers */
+enum {
+ LLDP_MGMT_ADDR_IP4 = 1,
+ LLDP_MGMT_ADDR_IP6 = 2
+};
+
+enum {
+ LLDP_MGMT_IFACE_UNKNOWN = 1,
+ LLDP_MGMT_IFACE_IFINDEX = 2,
+ LLDP_MGMT_IFACE_SYSPORT = 3
+};
+
+/* Supports only IPv4 */
+struct lldp_mgmt {
+ struct lldp_tlv_head tlv_head;
+ u_int8_t mgmt_len;
+ u_int8_t mgmt_subtype; /* Should be 1 */
+ struct in_addr mgmt_addr;
+ u_int8_t mgmt_iface_subtype;
+ u_int32_t mgmt_iface_id;
+ u_int8_t mgmt_oid_len;
+ u_int8_t mgmt_oid[0];
+} __attribute__ ((__packed__));
+
+struct lldp_org {
+ struct lldp_tlv_head tlv_head;
+ u_int8_t tlv_org_id[3];
+ u_int8_t tlv_org_subtype;
+} __attribute__ ((__packed__));
+
+struct lldp_vlan {
+ struct lldp_tlv_head tlv_head;
+ u_int8_t tlv_org_id[3];
+ u_int8_t tlv_org_subtype;
+ u_int16_t vid;
+ u_int8_t len;
+} __attribute__ ((__packed__));
+
+struct lldp_aggreg {
+ struct lldp_tlv_head tlv_head;
+ u_int8_t tlv_org_id[3];
+ u_int8_t tlv_org_subtype;
+ u_int8_t status;
+ u_int32_t id;
+} __attribute__ ((__packed__));
+
+struct lldp_macphy {
+ struct lldp_tlv_head tlv_head;
+ u_int8_t tlv_org_id[3];
+ u_int8_t tlv_org_subtype;
+ u_int8_t autoneg;
+ u_int16_t advertised;
+ u_int16_t mau;
+} __attribute__ ((__packed__));
+
+struct lldp_end {
+ struct lldp_tlv_head tlv_head;
+} __attribute__ ((__packed__));
+
+#endif /* _LLDP_H */
--- /dev/null
+/*
+ * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and 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 "lldpd.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <arpa/inet.h>
+
+void usage(void);
+
+TAILQ_HEAD(interfaces, lldpd_interface);
+TAILQ_HEAD(vlans, lldpd_vlan);
+
+struct value_string {
+ int value;
+ char *string;
+};
+
+static const struct value_string operational_mau_type_values[] = {
+ { 1, "AUI - no internal MAU, view from AUI" },
+ { 2, "10Base5 - thick coax MAU" },
+ { 3, "Foirl - FOIRL MAU" },
+ { 4, "10Base2 - thin coax MAU" },
+ { 5, "10BaseT - UTP MAU" },
+ { 6, "10BaseFP - passive fiber MAU" },
+ { 7, "10BaseFB - sync fiber MAU" },
+ { 8, "10BaseFL - async fiber MAU" },
+ { 9, "10Broad36 - broadband DTE MAU" },
+ { 10, "10BaseTHD - UTP MAU, half duplex mode" },
+ { 11, "10BaseTFD - UTP MAU, full duplex mode" },
+ { 12, "10BaseFLHD - async fiber MAU, half duplex mode" },
+ { 13, "10BaseFLDF - async fiber MAU, full duplex mode" },
+ { 14, "10BaseT4 - 4 pair category 3 UTP" },
+ { 15, "100BaseTXHD - 2 pair category 5 UTP, half duplex mode" },
+ { 16, "100BaseTXFD - 2 pair category 5 UTP, full duplex mode" },
+ { 17, "100BaseFXHD - X fiber over PMT, half duplex mode" },
+ { 18, "100BaseFXFD - X fiber over PMT, full duplex mode" },
+ { 19, "100BaseT2HD - 2 pair category 3 UTP, half duplex mode" },
+ { 20, "100BaseT2DF - 2 pair category 3 UTP, full duplex mode" },
+ { 21, "1000BaseXHD - PCS/PMA, unknown PMD, half duplex mode" },
+ { 22, "1000BaseXFD - PCS/PMA, unknown PMD, full duplex mode" },
+ { 23, "1000BaseLXHD - Fiber over long-wavelength laser, half duplex mode" },
+ { 24, "1000BaseLXFD - Fiber over long-wavelength laser, full duplex mode" },
+ { 25, "1000BaseSXHD - Fiber over short-wavelength laser, half duplex mode" },
+ { 26, "1000BaseSXFD - Fiber over short-wavelength laser, full duplex mode" },
+ { 27, "1000BaseCXHD - Copper over 150-Ohm balanced cable, half duplex mode" },
+ { 28, "1000BaseCXFD - Copper over 150-Ohm balanced cable, full duplex mode" },
+ { 29, "1000BaseTHD - Four-pair Category 5 UTP, half duplex mode" },
+ { 30, "1000BaseTFD - Four-pair Category 5 UTP, full duplex mode" },
+ { 31, "10GigBaseX - X PCS/PMA, unknown PMD." },
+ { 32, "10GigBaseLX4 - X fiber over WWDM optics" },
+ { 33, "10GigBaseR - R PCS/PMA, unknown PMD." },
+ { 34, "10GigBaseER - R fiber over 1550 nm optics" },
+ { 35, "10GigBaseLR - R fiber over 1310 nm optics" },
+ { 36, "10GigBaseSR - R fiber over 850 nm optics" },
+ { 37, "10GigBaseW - W PCS/PMA, unknown PMD." },
+ { 38, "10GigBaseEW - W fiber over 1550 nm optics" },
+ { 39, "10GigBaseLW - W fiber over 1310 nm optics" },
+ { 40, "10GigBaseSW - W fiber over 850 nm optics" },
+ { 0, NULL }
+};
+
+void
+usage(void)
+{
+ extern const char *__progname;
+
+ fprintf(stderr, "usage: %s [-d]\n", __progname);
+ exit(1);
+}
+
+static char*
+dump(void *data, int size, int max, char sep)
+{
+ int i;
+ size_t len;
+ static char *buffer = NULL;
+ static char truncation[] = "[...]";
+
+ free(buffer);
+ if (size > max)
+ len = max * 3 + sizeof(truncation) + 1;
+ else
+ len = size * 3;
+
+ if ((buffer = (char *)malloc(len)) == NULL)
+ fatal(NULL);
+
+ for (i = 0; (i < size) && (i < max); i++)
+ sprintf(buffer + i * 3, "%02x%c", *(u_int8_t*)(data + i), sep);
+ if (size > max)
+ sprintf(buffer + i * 3, "%s", truncation);
+ else
+ *(buffer + i*3 - 1) = 0;
+ return buffer;
+}
+
+
+void
+get_interfaces(int s, struct interfaces *ifs)
+{
+ void *p;
+ struct hmsg *h;
+
+ if ((h = (struct hmsg *)malloc(MAX_HMSGSIZE)) == NULL)
+ fatal(NULL);
+ ctl_msg_init(h, HMSG_GET_INTERFACES);
+ if (ctl_msg_send(s, h) == -1)
+ fatalx("get_interfaces: unable to send request");
+ if (ctl_msg_recv(s, h) == -1)
+ fatalx("get_interfaces: unable to receive answer");
+ if (h->hdr.type != HMSG_GET_INTERFACES)
+ fatalx("get_interfaces: unknown answer type received");
+ p = &h->data;
+ if (ctl_msg_unpack_list(STRUCT_LLDPD_INTERFACE,
+ ifs, sizeof(struct lldpd_interface), h, &p) == -1)
+ fatalx("get_interfaces: unable to retrieve the list of interfaces");
+}
+
+int
+get_vlans(int s, struct vlans *vls, char *interface)
+{
+ void *p;
+ struct hmsg *h;
+
+ if ((h = (struct hmsg *)malloc(MAX_HMSGSIZE)) == NULL)
+ fatal(NULL);
+ ctl_msg_init(h, HMSG_GET_VLANS);
+ h->hdr.len += strlcpy((char *)&h->data, interface,
+ MAX_HMSGSIZE - sizeof(struct hmsg_hdr)) + 1;
+ if (ctl_msg_send(s, h) == -1)
+ fatalx("get_vlans: unable to send request");
+ if (ctl_msg_recv(s, h) == -1)
+ fatalx("get_vlans: unable to receive answer");
+ if (h->hdr.type != HMSG_GET_VLANS)
+ fatalx("get_vlans: unknown answer type received");
+ p = &h->data;
+ if (ctl_msg_unpack_list(STRUCT_LLDPD_VLAN,
+ vls, sizeof(struct lldpd_vlan), h, &p) == -1)
+ fatalx("get_vlans: unable to retrieve the list of vlans");
+ return 1;
+}
+
+int
+get_chassis(int s, struct lldpd_chassis *chassis, char *interface)
+{
+ struct hmsg *h;
+ void *p;
+
+ if ((h = (struct hmsg *)malloc(MAX_HMSGSIZE)) == NULL)
+ fatal(NULL);
+ ctl_msg_init(h, HMSG_GET_CHASSIS);
+ h->hdr.len += strlcpy((char *)&h->data, interface,
+ MAX_HMSGSIZE - sizeof(struct hmsg_hdr)) + 1;
+ if (ctl_msg_send(s, h) == -1)
+ fatalx("get_chassis: unable to send request to get chassis");
+ if (ctl_msg_recv(s, h) == -1)
+ fatalx("get_chassis: unable to receive answer to get chassis");
+ if (h->hdr.type == HMSG_NONE)
+ /* No chassis */
+ return -1;
+ p = &h->data;
+ if (ctl_msg_unpack_structure(STRUCT_LLDPD_CHASSIS,
+ chassis, sizeof(struct lldpd_chassis), h, &p) == -1) {
+ LLOG_WARNX("unable to retrieve chassis for %s", interface);
+ fatalx("get_chassis: abort");
+ }
+ return 1;
+}
+
+int
+get_port(int s, struct lldpd_port *port, char *interface)
+{
+ struct hmsg *h;
+ void *p;
+
+ if ((h = (struct hmsg *)malloc(MAX_HMSGSIZE)) == NULL)
+ fatal(NULL);
+ ctl_msg_init(h, HMSG_GET_PORT);
+ h->hdr.len += strlcpy((char *)&h->data, interface,
+ MAX_HMSGSIZE - sizeof(struct hmsg_hdr)) + 1;
+ if (ctl_msg_send(s, h) == -1)
+ fatalx("get_port: unable to send request to get port");
+ if (ctl_msg_recv(s, h) == -1)
+ fatalx("get_port: unable to receive answer to get port");
+ if (h->hdr.type == HMSG_NONE)
+ /* No port */
+ return -1;
+ p = &h->data;
+ if (ctl_msg_unpack_structure(STRUCT_LLDPD_PORT,
+ port, sizeof(struct lldpd_port), h, &p) == -1) {
+ LLOG_WARNX("unable to retrieve port information for %s",
+ interface);
+ fatalx("get_chassis: abort");
+ }
+ return 1;
+}
+
+void
+display_cap(struct lldpd_chassis *chassis, u_int8_t bit, char *symbol)
+{
+ if (chassis->c_cap_available & bit)
+ printf("%s(%c) ", symbol,
+ (chassis->c_cap_enabled & bit)?'E':'d');
+}
+
+void
+pretty_print(char *string)
+{
+ char *s = NULL;
+ if (((s = index(string, '\n')) == NULL) && (strlen(string) < 60)) {
+ printf("%s\n", string);
+ return;
+ } else
+ printf("\n");
+ while (s != NULL) {
+ *s = '\0';
+ printf(" %s\n", string);
+ *s = '\n';
+ string = s + 1;
+ s = index(string, '\n');
+ }
+ printf(" %s\n", string);
+}
+
+void
+display_chassis(struct lldpd_chassis *chassis)
+{
+ char *cid;
+ if ((cid = (char *)malloc(chassis->c_id_len + 1)) == NULL)
+ fatal(NULL);
+ memcpy(cid, chassis->c_id, chassis->c_id_len);
+ cid[chassis->c_id_len] = 0;
+ switch (chassis->c_id_subtype) {
+ case LLDP_CHASSISID_SUBTYPE_IFNAME:
+ printf(" ChassisID: %s (ifName)\n", cid);
+ break;
+ case LLDP_CHASSISID_SUBTYPE_IFALIAS:
+ printf(" ChassisID: %s (ifAlias)\n", cid);
+ break;
+ case LLDP_CHASSISID_SUBTYPE_LOCAL:
+ printf(" ChassisID: %s (local)\n", cid);
+ break;
+ case LLDP_CHASSISID_SUBTYPE_LLADDR:
+ printf(" ChassisID: %s (MAC)\n",
+ dump(chassis->c_id, chassis->c_id_len, ETH_ALEN, ':'));
+ break;
+ case LLDP_CHASSISID_SUBTYPE_ADDR:
+ if (*(u_int8_t*)chassis->c_id == 1) {
+ printf(" ChassisID: %s (IP)\n",
+ inet_ntoa(*(struct in_addr*)(chassis->c_id +
+ 1)));
+ break;
+ }
+ case LLDP_CHASSISID_SUBTYPE_PORT:
+ case LLDP_CHASSISID_SUBTYPE_CHASSIS:
+ default:
+ printf(" ChassisID: %s (unhandled type)\n",
+ dump(chassis->c_id, chassis->c_id_len, 16, ' '));
+ }
+ printf(" SysName: %s\n", chassis->c_name);
+ printf(" SysDescr: "); pretty_print(chassis->c_descr);
+ printf(" MgmtIP: %s\n", inet_ntoa(chassis->c_mgmt));
+ printf(" Caps: ");
+ display_cap(chassis, LLDP_CAP_OTHER, "Other");
+ display_cap(chassis, LLDP_CAP_REPEATER, "Repeater");
+ display_cap(chassis, LLDP_CAP_BRIDGE, "Bridge");
+ display_cap(chassis, LLDP_CAP_WLAN, "Wlan");
+ display_cap(chassis, LLDP_CAP_TELEPHONE, "Tel");
+ display_cap(chassis, LLDP_CAP_DOCSIS, "Docsis");
+ display_cap(chassis, LLDP_CAP_STATION, "Station");
+ printf("\n");
+}
+
+void
+display_autoneg(struct lldpd_port *port, int bithd, int bitfd, char *desc)
+{
+ if (!((port->p_autoneg_advertised & bithd) ||
+ (port->p_autoneg_advertised & bitfd)))
+ return;
+ printf("%s ", desc);
+ if (port->p_autoneg_advertised & bithd) {
+ printf("(HD");
+ if (port->p_autoneg_advertised & bitfd) {
+ printf(", FD) ");
+ return;
+ }
+ printf(") ");
+ return;
+ }
+ printf("(FD) ");
+}
+
+void
+display_port(struct lldpd_port *port)
+{
+ char *pid;
+ int i;
+
+ if ((pid = (char *)malloc(port->p_id_len + 1)) == NULL)
+ fatal(NULL);
+ memcpy(pid, port->p_id, port->p_id_len);
+ pid[port->p_id_len] = 0;
+ switch (port->p_id_subtype) {
+ case LLDP_PORTID_SUBTYPE_IFNAME:
+ printf(" PortID: %s (ifName)\n", pid);
+ break;
+ case LLDP_PORTID_SUBTYPE_IFALIAS:
+ printf(" PortID: %s (ifAlias)\n", pid);
+ break;
+ case LLDP_PORTID_SUBTYPE_LOCAL:
+ printf(" PortID: %s (local)\n", pid);
+ break;
+ case LLDP_PORTID_SUBTYPE_LLADDR:
+ printf(" PortID: %s (MAC)\n",
+ dump(port->p_id, port->p_id_len, ETH_ALEN, ':'));
+ break;
+ case LLDP_PORTID_SUBTYPE_ADDR:
+ if (*(u_int8_t*)port->p_id == 1) {
+ printf(" PortID: %s (IP)\n",
+ inet_ntoa(*(struct in_addr*)(port->p_id +
+ 1)));
+ break;
+ }
+ case LLDP_PORTID_SUBTYPE_PORT:
+ case LLDP_PORTID_SUBTYPE_AGENTCID:
+ default:
+ printf(" ChassisID: %s (unhandled type)\n",
+ dump(port->p_id, port->p_id_len, 16, ' '));
+ }
+ printf(" PortDescr: "); pretty_print(port->p_descr);
+ if (port->p_aggregid)
+ printf("\n Port is aggregated. PortAggregID: %d\n",
+ port->p_aggregid);
+
+ printf("\n Autoneg: %ssupported/%senabled\n",
+ port->p_autoneg_support?"":"not ",
+ port->p_autoneg_enabled?"":"not ");
+ if (port->p_autoneg_enabled) {
+ printf(" PMD autoneg: ");
+ display_autoneg(port, LLDP_DOT3_LINK_AUTONEG_10BASE_T,
+ LLDP_DOT3_LINK_AUTONEG_10BASET_FD,
+ "10Base-T");
+ display_autoneg(port, LLDP_DOT3_LINK_AUTONEG_100BASE_TX,
+ LLDP_DOT3_LINK_AUTONEG_100BASE_TXFD,
+ "100Base-T");
+ display_autoneg(port, LLDP_DOT3_LINK_AUTONEG_100BASE_T2,
+ LLDP_DOT3_LINK_AUTONEG_100BASE_T2FD,
+ "100Base-T2");
+ display_autoneg(port, LLDP_DOT3_LINK_AUTONEG_1000BASE_X,
+ LLDP_DOT3_LINK_AUTONEG_1000BASE_XFD,
+ "100Base-X");
+ display_autoneg(port, LLDP_DOT3_LINK_AUTONEG_1000BASE_T,
+ LLDP_DOT3_LINK_AUTONEG_1000BASE_TFD,
+ "1000Base-T");
+ printf("\n");
+ }
+ printf(" MAU oper type: ");
+ for (i = 0; operational_mau_type_values[i].value != 0; i++) {
+ if (operational_mau_type_values[i].value ==
+ port->p_mau_type) {
+ printf("%s\n", operational_mau_type_values[i].string);
+ break;
+ }
+ }
+ if (operational_mau_type_values[i].value == 0)
+ printf("unknown (%d)\n", port->p_mau_type);
+}
+
+void
+display_vlans(struct lldpd_port *port)
+{
+ int i = 0;
+ struct lldpd_vlan *vlan;
+ TAILQ_FOREACH(vlan, &port->p_vlans, v_entries)
+ printf(" VLAN %4d: %-20s%c", vlan->v_vid, vlan->v_name,
+ (i++ % 2) ? '\n' : ' ');
+ if (i % 2)
+ printf("\n");
+}
+
+int
+main(int argc, char *argv[])
+{
+ int s;
+ int ch, debug = 1;
+ struct interfaces ifs;
+ struct vlans vls;
+ struct lldpd_interface *iff;
+ struct lldpd_chassis chassis;
+ struct lldpd_port port;
+ char sep[80];
+
+ /*
+ * Get and parse command line options
+ */
+ while ((ch = getopt(argc, argv, "d")) != -1) {
+ switch (ch) {
+ case 'd':
+ debug++;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ log_init(debug);
+ memset(sep, '-', 79);
+ sep[79] = 0;
+
+ if ((s = ctl_connect(LLDPD_CTL_SOCKET)) == -1)
+ fatalx("unable to connect to socket " LLDPD_CTL_SOCKET);
+ get_interfaces(s, &ifs);
+
+ printf("%s\n", sep);
+ printf(" LLDP neighbors\n");
+ printf("%s\n", sep);
+ TAILQ_FOREACH(iff, &ifs, next) {
+ if ((get_chassis(s, &chassis, iff->name) != -1) &&
+ (get_port(s, &port, iff->name) != -1)) {
+ printf("Interface: %s\n", iff->name);
+ display_chassis(&chassis);
+ printf("\n");
+ display_port(&port);
+ if (get_vlans(s, &vls, iff->name) != -1) {
+ memcpy(&port.p_vlans, &vls, sizeof(struct vlans));
+ if (!TAILQ_EMPTY(&port.p_vlans)) {
+ printf("\n");
+ display_vlans(&port);
+ }
+ }
+ printf("%s\n", sep);
+ }
+ }
+
+ close(s);
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and 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 "lldpd.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <fnmatch.h>
+#include <time.h>
+#include <netdb.h>
+#include <sys/utsname.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/select.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/sockios.h>
+#include <linux/ethtool.h>
+
+#ifdef USE_SNMP
+#include <net-snmp/net-snmp-config.h>
+#include <net-snmp/net-snmp-includes.h>
+#include <net-snmp/agent/net-snmp-agent-includes.h>
+#include <net-snmp/agent/snmp_vars.h>
+#endif /* USE_SNMP */
+
+void usage(void);
+
+int lldpd_iface_init(struct lldpd *, struct lldpd_hardware *);
+int lldpd_iface_close(struct lldpd *, struct lldpd_hardware *);
+void lldpd_iface_multicast(struct lldpd *, const char *, int);
+
+/* "ether proto 0x88cc and ether dst 01:80:c2:00:00:0e" */
+#define LLDPD_FILTER_LLDP_F \
+ { 0x28, 0, 0, 0x0000000c }, \
+ { 0x15, 0, 5, 0x000088cc }, \
+ { 0x20, 0, 0, 0x00000002 }, \
+ { 0x15, 0, 3, 0xc200000e }, \
+ { 0x28, 0, 0, 0x00000000 }, \
+ { 0x15, 0, 1, 0x00000180 }, \
+ { 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" */
+#define LLDPD_FILTER_CDP_F \
+ { 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 };
+/* "ether dst 01:00:81:00:01:00" */
+#define LLDPD_FILTER_SONMP_F \
+ { 0x20, 0, 0, 0x00000002 }, \
+ { 0x15, 0, 3, 0x81000100 }, \
+ { 0x28, 0, 0, 0x00000000 }, \
+ { 0x15, 0, 1, 0x00000100 }, \
+ { 0x6, 0, 0, 0x0000ffff }, \
+ { 0x6, 0, 0, 0x00000000 },
+struct sock_filter lldpd_filter_sonmp_f[] = { LLDPD_FILTER_SONMP_F };
+/* "ether dst 00:e0:2b:00:00:00" */
+#define LLDPD_FILTER_EDP_F \
+ { 0x20, 0, 0, 0x00000002 }, \
+ { 0x15, 0, 3, 0x2b000000 }, \
+ { 0x28, 0, 0, 0x00000000 }, \
+ { 0x15, 0, 1, 0x000000e0 }, \
+ { 0x6, 0, 0, 0x0000ffff }, \
+ { 0x6, 0, 0, 0x00000000 },
+struct sock_filter lldpd_filter_edp_f[] = { LLDPD_FILTER_EDP_F };
+#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 }, \
+ { 0x20, 0, 0, 0x00000002 }, \
+ { 0x15, 0, 2, 0x2b000000 }, \
+ { 0x28, 0, 0, 0x00000000 }, \
+ { 0x15, 4, 5, 0x000000e0 }, \
+ { 0x15, 1, 0, 0x0ccccccc }, \
+ { 0x15, 0, 3, 0x81000100 }, \
+ { 0x28, 0, 0, 0x00000000 }, \
+ { 0x15, 0, 1, 0x00000100 }, \
+ { 0x6, 0, 0, 0x0000ffff }, \
+ { 0x6, 0, 0, 0x00000000 },
+struct sock_filter lldpd_filter_any_f[] = { LLDPD_FILTER_ANY_F };
+
+struct protocol protos[] =
+{
+ { LLDPD_MODE_LLDP, 1, "LLDP", ' ', lldp_send, lldp_decode, NULL,
+ LLDP_MULTICAST_ADDR, lldpd_filter_lldp_f, sizeof(lldpd_filter_lldp_f) },
+ { 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) },
+ { LLDPD_MODE_SONMP, 0, "SONMP", 's', sonmp_send, sonmp_decode, NULL,
+ SONMP_MULTICAST_ADDR, lldpd_filter_sonmp_f, sizeof(lldpd_filter_sonmp_f) },
+ { LLDPD_MODE_EDP, 0, "EDP", 'e', edp_send, edp_decode, NULL,
+ EDP_MULTICAST_ADDR, lldpd_filter_edp_f, sizeof(lldpd_filter_edp_f) },
+ { 0, 0, "any", ' ', NULL, NULL, NULL,
+ {0,0,0,0,0,0}, lldpd_filter_any_f, sizeof(lldpd_filter_any_f) }
+};
+
+int lldpd_iface_switchto(struct lldpd *, short int,
+ 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 *);
+void lldpd_recv_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 } };
+
+char **saved_argv;
+
+void
+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 */
+ exit(1);
+}
+
+int
+lldpd_iface_init(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));
+ strlcpy(ifr.ifr_name, hardware->h_ifname, sizeof(ifr.ifr_name));
+ if (ioctl(global->g_sock, SIOCGIFMTU, (char*)&ifr) == -1) {
+ 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;
+
+ if ((master = iface_is_enslaved(global, hardware->h_ifname)) != -1) {
+ /* With bonding device, we need to listen on the bond ! */
+ if (if_indextoname(master, if_bond) == NULL) {
+ LLOG_WARN("unable to get index for interface %d (master of %s)",
+ master, hardware->h_ifname);
+ return ENETDOWN;
+ }
+ 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);
+ }
+ }
+
+ if (global->g_multi)
+ filter = LLDPD_MODE_ANY;
+ else
+ filter = LLDPD_MODE_LLDP;
+ if (lldpd_iface_switchto(global, filter, hardware) == -1) {
+ LLOG_WARNX("unable to apply filter");
+ return ENETDOWN;
+ }
+
+ if (master != -1)
+ lldpd_iface_multicast(global, if_bond, 0);
+ lldpd_iface_multicast(global, hardware->h_ifname, 0);
+
+ LLOG_DEBUG("interface %s initialized (fd=%d)", hardware->h_ifname,
+ hardware->h_raw);
+ return 0;
+}
+
+void
+lldpd_iface_multicast(struct lldpd *global, const char *name, int remove)
+{
+ struct ifreq ifr;
+ int i;
+
+ 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);
+ }
+ }
+}
+
+int
+lldpd_iface_close(struct lldpd *global, struct lldpd_hardware *hardware)
+{
+ char listen[IFNAMSIZ];
+
+ close(hardware->h_raw);
+ hardware->h_raw = -1;
+
+ if (hardware->h_raw_real > 0) {
+ if (if_indextoname(hardware->h_master, listen) == NULL) {
+ LLOG_WARN("unable to get index for interface %d",
+ hardware->h_master);
+ strlcpy(listen, hardware->h_ifname, sizeof(listen));
+ }
+ close(hardware->h_raw_real);
+ lldpd_iface_multicast(global, listen, 1);
+ }
+ strlcpy(listen, hardware->h_ifname, sizeof(listen));
+ lldpd_iface_multicast(global, listen, 1);
+
+ hardware->h_raw_real = -1;
+ return 0;
+}
+
+int
+lldpd_iface_switchto(struct lldpd *cfg, short int filter, struct lldpd_hardware *hardware)
+{
+ struct sock_fprog prog;
+ int i;
+
+ memset(&prog, 0, sizeof(prog));
+ for (i=0; cfg->g_protocols[i].mode != 0; i++) {
+ if (!cfg->g_protocols[i].enabled) continue;
+ if (cfg->g_protocols[i].mode == filter)
+ break;
+ }
+ prog.filter = cfg->g_protocols[i].filter;
+ prog.len = cfg->g_protocols[i].filterlen / sizeof(struct sock_filter);
+ if (setsockopt(hardware->h_raw, SOL_SOCKET, SO_ATTACH_FILTER,
+ &prog, sizeof(prog)) < 0) {
+ LLOG_WARN("unable to change filter for %s", hardware->h_ifname);
+ return -1;
+ }
+ if ((hardware->h_raw_real > 0) &&
+ (setsockopt(hardware->h_raw_real, SOL_SOCKET, SO_ATTACH_FILTER,
+ &prog, sizeof(prog)) < 0)) {
+ LLOG_WARN("unable to change filter for real device %s", hardware->h_ifname);
+ return -1;
+ }
+ return 0;
+}
+
+
+void
+lldpd_vlan_cleanup(struct lldpd_port *port)
+{
+ struct lldpd_vlan *vlan, *vlan_next;
+ for (vlan = TAILQ_FIRST(&port->p_vlans);
+ vlan != NULL;
+ vlan = vlan_next) {
+ free(vlan->v_name);
+ vlan_next = TAILQ_NEXT(vlan, v_entries);
+ TAILQ_REMOVE(&port->p_vlans, vlan, v_entries);
+ free(vlan);
+ }
+}
+
+void
+lldpd_port_cleanup(struct lldpd_port *port)
+{
+ lldpd_vlan_cleanup(port);
+ free(port->p_id);
+ free(port->p_descr);
+ free(port);
+}
+
+void
+lldpd_chassis_cleanup(struct lldpd_chassis *chassis)
+{
+ free(chassis->c_id);
+ free(chassis->c_name);
+ free(chassis->c_descr);
+ free(chassis);
+}
+
+void
+lldpd_remote_cleanup(struct lldpd *cfg, struct lldpd_hardware *hardware, int reset)
+{
+ if (hardware->h_rport != NULL) {
+ lldpd_port_cleanup(hardware->h_rport);
+ hardware->h_rport = NULL;
+ }
+ if (hardware->h_rchassis != NULL) {
+ lldpd_chassis_cleanup(hardware->h_rchassis);
+ hardware->h_rchassis = NULL;
+ }
+ hardware->h_rlastchange = hardware->h_rlastupdate = 0;
+ free(hardware->h_rlastframe);
+ hardware->h_rlastframe = NULL;
+ if (reset && cfg->g_multi) {
+ hardware->h_mode = LLDPD_MODE_ANY;
+ memset(hardware->h_proto_macs, 0, ETH_ALEN*(cfg->g_multi+1));
+ hardware->h_start_probe = 0;
+ lldpd_iface_switchto(cfg, LLDPD_MODE_ANY, hardware);
+ }
+}
+
+void
+lldpd_cleanup(struct lldpd *cfg)
+{
+ struct lldpd_hardware *hardware, *hardware_next;
+
+ for (hardware = TAILQ_FIRST(&cfg->g_hardware); hardware != NULL;
+ hardware = hardware_next) {
+ hardware_next = TAILQ_NEXT(hardware, h_entries);
+ 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);
+ } else if (hardware->h_rchassis != NULL) {
+ if (time(NULL) - hardware->h_rlastupdate >
+ hardware->h_rchassis->c_ttl) {
+ lldpd_remote_cleanup(cfg, hardware, 1);
+ hardware->h_rx_ageout_cnt++;
+ }
+ }
+ }
+}
+
+struct lldpd_hardware *
+lldpd_port_add(struct lldpd *cfg, struct ifaddrs *ifa)
+{
+ struct ifaddrs *oifap, *oifa;
+ struct lldpd_hardware *hardware;
+ struct lldpd_port *port;
+ struct lldpd_vlan *vlan;
+ struct ifreq ifr;
+ struct vlan_ioctl_args ifv;
+ struct ethtool_cmd ethc;
+ u_int8_t *lladdr;
+
+ TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
+ if (strcmp(hardware->h_ifname, ifa->ifa_name) == 0)
+ break;
+ }
+
+ if (hardware == NULL) {
+ if ((hardware = (struct lldpd_hardware *)
+ calloc(1, sizeof(struct lldpd_hardware))) == NULL)
+ return (NULL);
+ hardware->h_raw = -1;
+ hardware->h_raw_real = -1;
+ hardware->h_start_probe = 0;
+ hardware->h_proto_macs = (u_int8_t*)calloc(cfg->g_multi+1, ETH_ALEN);
+ TAILQ_INIT(&hardware->h_lport.p_vlans);
+ } else {
+ lldpd_vlan_cleanup(&hardware->h_lport);
+ }
+
+ port = &hardware->h_lport;
+ hardware->h_flags = ifa->ifa_flags;
+
+ 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));
+ port->p_id_subtype = LLDP_PORTID_SUBTYPE_LLADDR;
+ port->p_id = (char*)hardware->h_lladdr;
+ port->p_id_len = sizeof(hardware->h_lladdr);
+ port->p_descr = hardware->h_ifname;
+
+ if (cfg->g_lchassis.c_id == NULL) {
+ /* Use the first port's l2 addr as the chassis ID */
+ if ((cfg->g_lchassis.c_id = malloc(sizeof(hardware->h_lladdr))) == NULL)
+ fatal(NULL);
+ cfg->g_lchassis.c_id_subtype = LLDP_CHASSISID_SUBTYPE_LLADDR;
+ cfg->g_lchassis.c_id_len = sizeof(hardware->h_lladdr);
+ memcpy(cfg->g_lchassis.c_id,
+ hardware->h_lladdr, sizeof(hardware->h_lladdr));
+ }
+
+ /* Get VLANS and aggregation status */
+ if (getifaddrs(&oifap) != 0)
+ fatal("lldpd_port_add: failed to get interface list");
+ for (oifa = oifap; oifa != NULL; oifa = oifa->ifa_next) {
+ /* Check if we already have checked this one */
+ int skip = 0;
+ TAILQ_FOREACH(vlan, &port->p_vlans, v_entries) {
+ if (strcmp(vlan->v_name, oifa->ifa_name) == 0)
+ skip = 1;
+ }
+ if (skip) continue;
+
+ /* Aggregation check */
+ if (iface_is_bond_slave(cfg, hardware->h_ifname, oifa->ifa_name))
+ port->p_aggregid = if_nametoindex(oifa->ifa_name);
+
+ /* 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)) ||
+ (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) {
+ free(vlan);
+ continue;
+ }
+ memset(&ifv, 0, sizeof(ifv));
+ ifv.cmd = GET_VLAN_VID_CMD;
+ strlcpy(ifv.device1, oifa->ifa_name, sizeof(ifv.device1));
+ if (ioctl(cfg->g_sock, SIOCGIFVLAN, &ifv) < 0) {
+ /* Dunno what happened */
+ free(vlan->v_name);
+ free(vlan);
+ } else {
+ vlan->v_vid = ifv.u.VID;
+ TAILQ_INSERT_TAIL(&port->p_vlans, vlan, v_entries);
+ }
+ }
+ }
+ freeifaddrs(oifap);
+
+ /* 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) {
+ int j;
+ int advertised_ethtool_to_rfc3636[][2] = {
+ {ADVERTISED_10baseT_Half, LLDP_DOT3_LINK_AUTONEG_10BASE_T},
+ {ADVERTISED_10baseT_Full, LLDP_DOT3_LINK_AUTONEG_10BASET_FD},
+ {ADVERTISED_100baseT_Half, LLDP_DOT3_LINK_AUTONEG_100BASE_TX},
+ {ADVERTISED_100baseT_Full, LLDP_DOT3_LINK_AUTONEG_100BASE_TXFD},
+ {ADVERTISED_1000baseT_Half, LLDP_DOT3_LINK_AUTONEG_1000BASE_T},
+ {ADVERTISED_1000baseT_Full, LLDP_DOT3_LINK_AUTONEG_1000BASE_TFD},
+ {ADVERTISED_10000baseT_Full, LLDP_DOT3_LINK_AUTONEG_OTHER},
+ {ADVERTISED_Pause, LLDP_DOT3_LINK_AUTONEG_FDX_PAUSE},
+ {ADVERTISED_Asym_Pause, LLDP_DOT3_LINK_AUTONEG_FDX_APAUSE},
+ {ADVERTISED_2500baseX_Full, LLDP_DOT3_LINK_AUTONEG_OTHER},
+ {0,0}};
+
+ port->p_autoneg_support = (ethc.supported & SUPPORTED_Autoneg) ? 1 : 0;
+ port->p_autoneg_enabled = (ethc.autoneg == AUTONEG_DISABLE) ? 0 : 1;
+ for (j=0; advertised_ethtool_to_rfc3636[j][0]; j++) {
+ if (ethc.advertising & advertised_ethtool_to_rfc3636[j][0])
+ port->p_autoneg_advertised |= advertised_ethtool_to_rfc3636[j][1];
+ }
+ switch (ethc.speed) {
+ case SPEED_10:
+ port->p_mau_type = (ethc.duplex == DUPLEX_FULL) ? \
+ LLDP_DOT3_MAU_10BASETFD : LLDP_DOT3_MAU_10BASETHD;
+ if (ethc.port == PORT_BNC) port->p_mau_type = LLDP_DOT3_MAU_10BASE2;
+ if (ethc.port == PORT_FIBRE)
+ port->p_mau_type = (ethc.duplex == DUPLEX_FULL) ? \
+ LLDP_DOT3_MAU_10BASEFLDF : LLDP_DOT3_MAU_10BASEFLHD;
+ break;
+ case SPEED_100:
+ port->p_mau_type = (ethc.duplex == DUPLEX_FULL) ? \
+ LLDP_DOT3_MAU_100BASETXFD : LLDP_DOT3_MAU_100BASETXHD;
+ if (ethc.port == PORT_BNC)
+ port->p_mau_type = (ethc.duplex == DUPLEX_FULL) ? \
+ LLDP_DOT3_MAU_100BASET2DF : LLDP_DOT3_MAU_100BASET2HD;
+ if (ethc.port == PORT_FIBRE)
+ port->p_mau_type = (ethc.duplex == DUPLEX_FULL) ? \
+ LLDP_DOT3_MAU_100BASEFXFD : LLDP_DOT3_MAU_100BASEFXHD;
+ break;
+ case SPEED_1000:
+ port->p_mau_type = (ethc.duplex == DUPLEX_FULL) ? \
+ LLDP_DOT3_MAU_1000BASETFD : LLDP_DOT3_MAU_1000BASETHD;
+ if (ethc.port == PORT_FIBRE)
+ port->p_mau_type = (ethc.duplex == DUPLEX_FULL) ? \
+ LLDP_DOT3_MAU_1000BASEXFD : LLDP_DOT3_MAU_1000BASEXHD;
+ break;
+ case SPEED_10000:
+ port->p_mau_type = (ethc.port == PORT_FIBRE) ? \
+ LLDP_DOT3_MAU_10GIGBASEX : LLDP_DOT3_MAU_10GIGBASER;
+ break;
+ }
+ if (ethc.port == PORT_AUI) port->p_mau_type = LLDP_DOT3_MAU_AUI;
+ }
+
+ 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);
+ return (NULL);
+ }
+
+ TAILQ_INSERT_TAIL(&cfg->g_hardware, hardware, h_entries);
+ }
+
+ return (hardware);
+}
+
+int
+lldpd_guess_type(struct lldpd *cfg, char *frame, int s)
+{
+ int i;
+ if (s < ETH_ALEN)
+ 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) {
+ if (memcmp(frame, cfg->g_protocols[i].mac, ETH_ALEN) == 0)
+ return cfg->g_protocols[i].mode;
+ } else {
+ if (cfg->g_protocols[i].guess(frame, s))
+ return cfg->g_protocols[i].mode;
+ }
+ }
+ return -1;
+}
+
+void
+lldpd_decode(struct lldpd *cfg, char *frame, int s,
+ struct lldpd_hardware *hardware, int bond)
+{
+ int result = 0, i, j, candidatetonull;
+ u_int8_t nullmac[ETH_ALEN] = {0,0,0,0,0,0};
+ u_int8_t broadcastmac[ETH_ALEN] = {0xff,0xff,0xff,0xff,0xff,0xff};
+ struct lldpd_chassis *chassis;
+ struct lldpd_port *port;
+ struct lldpd_hardware *ohardware, *firstnull = NULL, *older = NULL;
+ int guess = LLDPD_MODE_LLDP;
+
+ if ((hardware->h_rlastframe != NULL) &&
+ (hardware->h_rlastframe->size == s) &&
+ (memcmp(hardware->h_rlastframe->frame, frame, s) == 0)) {
+ /* Already received the same frame */
+ hardware->h_rlastupdate = time(NULL);
+ return;
+ }
+
+ if (cfg->g_multi) {
+ if (hardware->h_mode == LLDPD_MODE_ANY)
+ guess = lldpd_guess_type(cfg, frame, s);
+ else
+ guess = hardware->h_mode;
+ for (i=0; cfg->g_protocols[i].mode != 0; i++) {
+ if (!cfg->g_protocols[i].enabled)
+ continue;
+ if (cfg->g_protocols[i].mode == guess) {
+ if ((result = cfg->g_protocols[i].decode(cfg, frame,
+ s, hardware, &chassis, &port)) == -1)
+ return;
+ break;
+ }
+ }
+ if (cfg->g_protocols[i].mode == 0) {
+ LLOG_INFO("unable to guess frame type");
+ return;
+ }
+ } else if (cfg->g_protocols[0].decode(cfg, frame, s, hardware,
+ &chassis, &port) == -1)
+ /* Nothing has been received */
+ return;
+
+ if (bond) {
+ /* Eh, wait ! The frame we just received was for a bonding
+ * device. We need to attach it to a real device. What is the
+ * best candidate? Drum rolling... */
+ TAILQ_FOREACH(ohardware, &cfg->g_hardware, h_entries) {
+ if (ohardware->h_master == hardware->h_master) {
+ /* Same bond */
+ if (ohardware->h_rchassis == NULL) {
+ candidatetonull = 1;
+ if (cfg->g_multi &&
+ (ohardware->h_mode == LLDPD_MODE_ANY)) {
+ for (i=j=0;
+ cfg->g_protocols[i].mode != 0;
+ i++) {
+ if (!cfg->g_protocols[i].enabled)
+ continue;
+ if ((cfg->g_protocols[i].mode == guess) &&
+ (memcmp(frame + ETH_ALEN,
+ ohardware->h_proto_macs + ETH_ALEN*j,
+ ETH_ALEN) == 0)) {
+ hardware = ohardware;
+ bond = 0;
+ break;
+ }
+ j++;
+ }
+ if (!bond) break;
+ if (firstnull != NULL) {
+ for (i=j=0;
+ cfg->g_protocols[i].mode != 0;
+ i++) {
+ if (!cfg->g_protocols[i].enabled)
+ continue;
+ if ((cfg->g_protocols[i].mode == guess) &&
+ (memcmp(nullmac,
+ ohardware->h_proto_macs +
+ ETH_ALEN*j,
+ ETH_ALEN) != 0)) {
+ /* We need to
+ * find a better
+ * candidate */
+ candidatetonull = 0;
+ break;
+ }
+ j++;
+ }
+ }
+ }
+ /* Ok, this is the first candidate if we
+ * don't find a matching chassis/port */
+ if (candidatetonull) firstnull = ohardware;
+ continue;
+ }
+ if ((older == NULL) ||
+ (older->h_rlastupdate > ohardware->h_rlastupdate))
+ /* If there is no matching chassis/port
+ * and no free hardware, we will use
+ * this one. */
+ older = ohardware;
+ if ((chassis->c_id_subtype !=
+ ohardware->h_rchassis->c_id_subtype) ||
+ (chassis->c_id_len != ohardware->h_rchassis->c_id_len) ||
+ (memcmp(chassis->c_id, ohardware->h_rchassis->c_id,
+ chassis->c_id_len) != 0) ||
+ (port->p_id_subtype != ohardware->h_rport->p_id_subtype) ||
+ (port->p_id_len != ohardware->h_rport->p_id_len) ||
+ (memcmp(port->p_id, ohardware->h_rport->p_id,
+ port->p_id_len) != 0))
+ continue;
+ /* We got a match! */
+ hardware = ohardware; /* We switch hardware */
+ bond = 0;
+ break;
+ }
+ }
+ if (bond) {
+ /* No match found */
+ if (firstnull != NULL)
+ hardware = firstnull;
+ else hardware = older;
+ }
+ }
+
+ if (cfg->g_multi &&
+ (hardware->h_mode == LLDPD_MODE_ANY)) {
+ u_int8_t *mac;
+ char *modename;
+ int filter;
+
+ for (i=j=0; cfg->g_protocols[i].mode != 0; i++) {
+ if (!cfg->g_protocols[i].enabled)
+ continue;
+ if (cfg->g_protocols[i].mode == guess) {
+ mac = hardware->h_proto_macs + ETH_ALEN*j;
+ modename = cfg->g_protocols[i].name;
+ filter = cfg->g_protocols[i].mode;
+ break;
+ }
+ j++;
+ }
+ if (cfg->g_protocols[i].mode == 0) {
+ LLOG_WARNX("should not be there");
+ goto cleanup;
+ }
+
+ if (hardware->h_start_probe == 0)
+ hardware->h_start_probe = time(NULL) - 1;
+ /* Handle switching respecting probe time */
+ if ((memcmp(mac, frame + ETH_ALEN, ETH_ALEN) == 0) &&
+ ((time(NULL) - hardware->h_start_probe) > cfg->g_probe_time) &&
+ /* Don't switch to this protocol if not LLDP and LLDP is
+ * a valid candidate */
+ ((filter == LLDPD_MODE_LLDP) ||
+ (memcmp(hardware->h_proto_macs,
+ broadcastmac, ETH_ALEN) == 0) ||
+ (memcmp(hardware->h_proto_macs,
+ nullmac, ETH_ALEN) == 0))) {
+ LLOG_INFO("switching to %s on port %s", modename,
+ hardware->h_ifname);
+ hardware->h_mode = guess;
+ lldpd_iface_switchto(cfg, filter, hardware);
+ } else {
+ /* Wait twice probe time to be able to receive packets of all kind */
+ if ((time(NULL) - hardware->h_start_probe) > cfg->g_probe_time * 2) {
+ LLOG_DEBUG("probe expired on %s, retry", hardware->h_ifname);
+ hardware->h_start_probe = 0;
+ memset(hardware->h_proto_macs, 0, ETH_ALEN*(cfg->g_multi+1));
+ goto cleanup;
+ }
+ if (memcmp(mac, broadcastmac, ETH_ALEN) == 0)
+ goto cleanup;
+ LLOG_INFO("received a %s frame on %s but wait for %d sec",
+ modename, hardware->h_ifname, cfg->g_probe_time - time(NULL) +
+ hardware->h_start_probe);
+ if (memcmp(mac, frame + ETH_ALEN, ETH_ALEN) == 0)
+ goto cleanup;
+ if (memcmp(mac, nullmac, ETH_ALEN) == 0) {
+ memcpy(mac, frame + ETH_ALEN, ETH_ALEN);
+ goto cleanup;
+ }
+ LLOG_INFO("several MAC for %s on %s, discarding %s for this interface",
+ modename, hardware->h_ifname, modename);
+ memcpy(mac, broadcastmac, ETH_ALEN);
+ goto cleanup;
+ }
+ }
+
+ result = 0;
+ if ((hardware->h_rchassis == NULL) ||
+ (chassis->c_id_subtype != hardware->h_rchassis->c_id_subtype) ||
+ (chassis->c_id_len != hardware->h_rchassis->c_id_len) ||
+ (memcmp(chassis->c_id, hardware->h_rchassis->c_id,
+ chassis->c_id_len) != 0))
+ result = 1;
+
+ /* We have our new frame */
+ lldpd_remote_cleanup(cfg, hardware, 0);
+ hardware->h_rport = port;
+ hardware->h_rchassis = chassis;
+ hardware->h_rlastchange = hardware->h_rlastupdate = time(NULL);
+
+ /* We remember this frame */
+ free(hardware->h_rlastframe);
+ if ((hardware->h_rlastframe = (struct lldpd_frame *)malloc(s +
+ sizeof(int))) != NULL) {
+ hardware->h_rlastframe->size = s;
+ memcpy(hardware->h_rlastframe->frame, frame, s);
+ }
+
+ if (result) {
+ /* This is a new remote system */
+ LLOG_DEBUG("we discovered a new remote system on %s",
+ hardware->h_ifname);
+ /* Do we already know this remote system? */
+ TAILQ_FOREACH(ohardware, &cfg->g_hardware, h_entries) {
+ if ((ohardware->h_ifname != hardware->h_ifname) &&
+ (ohardware->h_rchassis != NULL) &&
+ (ohardware->h_rchassis->c_id_subtype ==
+ chassis->c_id_subtype) &&
+ (ohardware->h_rchassis->c_id_len ==
+ chassis->c_id_len) &&
+ (memcmp(ohardware->h_rchassis->c_id,
+ chassis->c_id, chassis->c_id_len) == 0)) {
+ LLOG_DEBUG("but it was already on %s",
+ ohardware->h_ifname);
+ hardware->h_rid = ohardware->h_rid;
+ return;
+ }
+ }
+ hardware->h_rid = ++cfg->g_lastrid;
+ }
+ return;
+
+cleanup:
+ lldpd_chassis_cleanup(chassis);
+ lldpd_port_cleanup(port);
+ 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_client *client, *client_next;
+ fd_set rfds;
+ struct timeval tv;
+ struct sockaddr_ll from;
+ socklen_t fromlen;
+ int onreal;
+#ifdef USE_SNMP
+ int fakeblock = 0;
+ struct timeval *tvp = &tv;
+#endif
+ int rc, nfds, n, bond;
+ char *buffer;
+
+ do {
+ tv.tv_sec = cfg->g_delay - (time(NULL) - cfg->g_lastsent);
+ if (tv.tv_sec < 0)
+ tv.tv_sec = LLDPD_TX_DELAY;
+ if (tv.tv_sec >= cfg->g_delay)
+ tv.tv_sec = cfg->g_delay;
+ tv.tv_usec = 0;
+
+ FD_ZERO(&rfds);
+ nfds = -1;
+
+ TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
+ /* Ignore if interface is down */
+ if ((hardware->h_flags & IFF_UP) == 0)
+ continue;
+ FD_SET(hardware->h_raw, &rfds);
+ if (nfds < hardware->h_raw)
+ nfds = hardware->h_raw;
+ /* Listen to real interface too. In 2.6.27, we can
+ * receive packets if this is the slave interface. */
+ if (hardware->h_raw_real > 0) {
+ FD_SET(hardware->h_raw_real, &rfds);
+ if (nfds < hardware->h_raw_real)
+ nfds = hardware->h_raw_real;
+ }
+ }
+ TAILQ_FOREACH(client, &cfg->g_clients, next) {
+ FD_SET(client->fd, &rfds);
+ if (nfds < client->fd)
+ nfds = client->fd;
+ }
+ FD_SET(cfg->g_ctl, &rfds);
+ if (nfds < cfg->g_ctl)
+ nfds = cfg->g_ctl;
+
+#ifdef USE_SNMP
+ if (cfg->g_snmp)
+ snmp_select_info(&nfds, &rfds, tvp, &fakeblock);
+#endif /* USE_SNMP */
+ if (nfds == -1) {
+ sleep(cfg->g_delay);
+ return;
+ }
+
+ rc = select(nfds + 1, &rfds, NULL, NULL, &tv);
+ if (rc == -1) {
+ if (errno == EINTR)
+ continue;
+ LLOG_WARN("failure on select");
+ break;
+ }
+#ifdef USE_SNMP
+ if (cfg->g_snmp) {
+ if (rc > 0)
+ snmp_read(&rfds);
+ else if (rc == 0)
+ snmp_timeout();
+ }
+#endif /* USE_SNMP */
+ TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
+ /* We could have received something on _real_
+ * interface. However, even in this case, this could be
+ * just an outgoing packet. We will try to handle both
+ * cases, but maybe not in the same select. */
+ onreal = ((hardware->h_raw_real > 0) &&
+ (FD_ISSET(hardware->h_raw_real, &rfds)));
+ if (onreal || (FD_ISSET(hardware->h_raw, &rfds))) {
+ if ((buffer = (char *)malloc(
+ hardware->h_mtu)) == NULL) {
+ LLOG_WARN("failed to alloc reception buffer");
+ continue;
+ }
+ fromlen = sizeof(from);
+ if ((n = recvfrom(
+ onreal?hardware->h_raw_real:hardware->h_raw,
+ buffer,
+ hardware->h_mtu, 0,
+ (struct sockaddr *)&from,
+ &fromlen)) == -1) {
+ LLOG_WARN("error while receiving frame on %s",
+ hardware->h_ifname);
+ hardware->h_rx_discarded_cnt++;
+ free(buffer);
+ continue;
+ }
+ if (from.sll_pkttype == PACKET_OUTGOING) {
+ free(buffer);
+ continue;
+ }
+ bond = 0;
+ /* If received on real interface, we act like if
+ * this is not a bond! */
+ if (!onreal && (hardware->h_raw_real > 0)) {
+ /* Bonding. Is it for the correct
+ * physical interface ? */
+ if (from.sll_ifindex == hardware->h_master) {
+ /* It seems that we don't know from
+ which physical interface it comes
+ (kernel < 2.6.24 ?) */
+ bond = 1;
+ } else if (from.sll_ifindex !=
+ if_nametoindex(hardware->h_ifname)) {
+ free(buffer);
+ continue;
+ }
+ }
+ hardware->h_rx_cnt++;
+ lldpd_decode(cfg, buffer, n, hardware, bond);
+ free(buffer);
+ }
+
+ }
+ if (FD_ISSET(cfg->g_ctl, &rfds)) {
+ if (ctl_accept(cfg, cfg->g_ctl) == -1)
+ LLOG_WARN("unable to accept new client");
+ }
+ for (client = TAILQ_FIRST(&cfg->g_clients);
+ client != NULL;
+ client = client_next) {
+ client_next = TAILQ_NEXT(client, next);
+ if (FD_ISSET(client->fd, &rfds)) {
+ /* Got a message */
+ if ((buffer = (char *)malloc(MAX_HMSGSIZE)) ==
+ NULL) {
+ LLOG_WARN("failed to alloc reception buffer");
+ continue;
+ }
+ if ((n = recv(client->fd, buffer,
+ MAX_HMSGSIZE, 0)) == -1) {
+ LLOG_WARN("error while receiving message");
+ free(buffer);
+ continue;
+ }
+ if (n > 0)
+ lldpd_handle_client(cfg, client, buffer, n);
+ else
+ ctl_close(cfg, client->fd); /* Will use TAILQ_REMOVE ! */
+ free(buffer);
+ }
+ }
+
+#ifdef USE_SNMP
+ if (cfg->g_snmp) {
+ run_alarms();
+ netsnmp_check_outstanding_agent_requests();
+ }
+#endif /* USE_SNMP */
+ } while ((rc != 0) || (time(NULL) - cfg->g_lastsent < cfg->g_delay));
+}
+
+void
+lldpd_send_all(struct lldpd *cfg)
+{
+ struct lldpd_hardware *hardware;
+ int i;
+ 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;
+
+ for (i=0; cfg->g_protocols[i].mode != 0; i++) {
+ if (!cfg->g_protocols[i].enabled)
+ continue;
+ if ((hardware->h_mode == cfg->g_protocols[i].mode) ||
+ (cfg->g_protocols[i].mode == LLDPD_MODE_LLDP))
+ cfg->g_protocols[i].send(cfg, &cfg->g_lchassis, hardware);
+ }
+ }
+}
+
+void
+lldpd_loop(struct lldpd *cfg)
+{
+ struct ifaddrs *ifap, *ifa;
+ struct sockaddr_ll *sdl;
+ struct lldpd_hardware *hardware;
+ int f;
+ char status;
+ struct utsname *un;
+ struct hostent *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)
+ 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 (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'))
+ cfg->g_lchassis.c_cap_enabled = LLDP_CAP_ROUTER;
+ close(f);
+ }
+
+ TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries)
+ hardware->h_flags = 0;
+
+ if (getifaddrs(&ifap) != 0)
+ fatal("lldpd_loop: failed to get interface list");
+
+ cfg->g_lchassis.c_mgmt.s_addr = INADDR_ANY;
+ for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
+ if (cfg->g_lchassis.c_mgmt.s_addr == INADDR_ANY)
+ /* Get management address, if available */
+ if ((ifa->ifa_addr != NULL) &&
+ (ifa->ifa_addr->sa_family == AF_INET)) {
+ struct sockaddr_in *sa;
+ sa = (struct sockaddr_in *)ifa->ifa_addr;
+ if ((ntohl(*(u_int32_t*)&sa->sin_addr) != INADDR_LOOPBACK) &&
+ (cfg->g_mgmt_pattern == NULL)) {
+ memcpy(&cfg->g_lchassis.c_mgmt,
+ &sa->sin_addr,
+ sizeof(struct in_addr));
+ cfg->g_lchassis.c_mgmt_if = if_nametoindex(ifa->ifa_name);
+ }
+ else if (cfg->g_mgmt_pattern != NULL) {
+ char *ip;
+ ip = inet_ntoa(sa->sin_addr);
+ if (fnmatch(cfg->g_mgmt_pattern,
+ ip, 0) == 0) {
+ memcpy(&cfg->g_lchassis.c_mgmt,
+ &sa->sin_addr,
+ sizeof(struct in_addr));
+ cfg->g_lchassis.c_mgmt_if =
+ if_nametoindex(ifa->ifa_name);
+ }
+ }
+ }
+
+ if (iface_is_bridge(cfg, ifa->ifa_name)) {
+ cfg->g_lchassis.c_cap_enabled |= LLDP_CAP_BRIDGE;
+ continue;
+ }
+
+ if ((iface_is_vlan(cfg, ifa->ifa_name)) ||
+ (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);
+ }
+
+ freeifaddrs(ifap);
+
+ lldpd_cleanup(cfg);
+
+ lldpd_send_all(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)
+{
+ LLOG_INFO("signal received, exiting");
+ exit(0);
+}
+
+/* For signal handling */
+struct lldpd *gcfg = NULL;
+
+void
+lldpd_exit()
+{
+ struct lldpd_hardware *hardware;
+ ctl_cleanup(gcfg->g_ctl, LLDPD_CTL_SOCKET);
+ TAILQ_FOREACH(hardware, &gcfg->g_hardware, h_entries) {
+ if (INTERFACE_OPENED(hardware))
+ lldpd_iface_close(gcfg, hardware);
+ }
+#ifdef USE_SNMP
+ if (gcfg->g_snmp)
+ agent_shutdown();
+#endif /* USE_SNMP */
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct lldpd *cfg;
+ int ch, snmp = 0, debug = 0;
+ char *mgmtp = NULL;
+ char *popt, opts[] = "dxm:p:@ ";
+ int probe = 0, i, found;
+
+ saved_argv = argv;
+
+ /*
+ * Get and parse command line options
+ */
+ popt = index(opts, '@');
+ for (i=0; protos[i].mode != 0; i++) {
+ if (protos[i].enabled == 1) continue;
+ *(popt++) = protos[i].arg;
+ }
+ *popt = '\0';
+ while ((ch = getopt(argc, argv, opts)) != -1) {
+ switch (ch) {
+ case 'd':
+ debug++;
+ break;
+ case 'm':
+ mgmtp = optarg;
+ break;
+ case 'p':
+ probe = atoi(optarg);
+ break;
+ case 'x':
+ snmp = 1;
+ break;
+ default:
+ found = 0;
+ for (i=0; protos[i].mode != 0; i++) {
+ if (protos[i].enabled) continue;
+ if (ch == protos[i].arg) {
+ protos[i].enabled = 1;
+ found = 1;
+ }
+ }
+ if (!found)
+ usage();
+ }
+ }
+
+ log_init(debug);
+
+ 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;
+
+ /* Get ioctl socket */
+ if ((cfg->g_sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+ fatal("failed to get ioctl socket");
+ cfg->g_delay = LLDPD_TX_DELAY;
+
+ /* Set system capabilities */
+ cfg->g_lchassis.c_cap_available = LLDP_CAP_BRIDGE | LLDP_CAP_WLAN |
+ LLDP_CAP_ROUTER;
+
+ /* Set TTL */
+ cfg->g_lchassis.c_ttl = LLDPD_TTL;
+
+ cfg->g_protocols = protos;
+ cfg->g_probe_time = probe;
+ for (i=0; protos[i].mode != 0; i++)
+ if (protos[i].enabled) {
+ cfg->g_multi++;
+ LLOG_INFO("protocol %s enabled", protos[i].name);
+ } else
+ LLOG_INFO("protocol %s disabled", protos[i].name);
+ cfg->g_multi--;
+
+ TAILQ_INIT(&cfg->g_hardware);
+
+#ifdef USE_SNMP
+ if (snmp) {
+ cfg->g_snmp = 1;
+ agent_init(cfg, debug);
+ }
+#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 (!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);
+ 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(SIGINT, lldpd_shutdown);
+ signal(SIGTERM, lldpd_shutdown);
+
+ for (;;)
+ lldpd_loop(cfg);
+
+ return (0);
+}
--- /dev/null
+/*
+ * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and 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.
+ */
+
+#ifndef _LLDPD_H
+#define _LLDPD_H
+
+#if HAVE_CONFIG_H
+ #include <config.h>
+#endif
+
+#define _GNU_SOURCE 1
+#include <stdlib.h>
+#include <string.h>
+#include <sys/queue.h>
+#ifndef INCLUDE_LINUX_IF_H
+#include <net/if.h>
+#else
+#include <arpa/inet.h>
+#include <linux/if.h>
+#endif
+#include <net/ethernet.h>
+#include <netinet/in.h>
+
+#include "compat.h"
+#include "lldp.h"
+#include "cdp.h"
+#include "sonmp.h"
+#include "edp.h"
+
+#define LLDPD_TTL 120
+#define LLDPD_TX_DELAY 30
+#define LLDPD_TX_MSGDELAY 1
+#define LLDPD_CTL_SOCKET "/var/run/lldpd.socket"
+#define LLDPD_PID_FILE "/var/run/lldpd.pid"
+
+#define UNIX_PATH_MAX 108
+
+#define USING_AGENTX_SUBAGENT_MODULE 1
+
+struct lldpd_vlan {
+ TAILQ_ENTRY(lldpd_vlan) v_entries;
+ char *v_name;
+ u_int16_t v_vid;
+};
+#define STRUCT_LLDPD_VLAN "Lsw"
+
+struct lldpd_chassis {
+ u_int8_t c_id_subtype;
+ char *c_id;
+ int c_id_len;
+ char *c_name;
+ char *c_descr;
+
+ u_int16_t c_cap_available;
+ u_int16_t c_cap_enabled;
+
+ u_int16_t c_ttl;
+
+ struct in_addr c_mgmt;
+ u_int32_t c_mgmt_if;
+};
+#define STRUCT_LLDPD_CHASSIS "bCsswwwll"
+
+struct lldpd_port {
+ u_int8_t p_id_subtype;
+ char *p_id;
+ int p_id_len;
+ char *p_descr;
+
+ /* Dot3 stuff */
+ u_int32_t p_aggregid;
+ u_int8_t p_autoneg_support;
+ u_int8_t p_autoneg_enabled;
+ u_int16_t p_autoneg_advertised;
+ u_int16_t p_mau_type;
+
+ TAILQ_HEAD(, lldpd_vlan) p_vlans;
+};
+#define STRUCT_LLDPD_PORT "bCslbbwwPP"
+
+struct lldpd_frame {
+ int size;
+ unsigned char frame[];
+};
+
+struct lldpd_hardware {
+ TAILQ_ENTRY(lldpd_hardware) h_entries;
+
+#define INTERFACE_OPENED(x) ((x)->h_raw != -1)
+
+ int h_raw;
+ int h_raw_real; /* For bonding */
+ int h_master; /* For bonding */
+
+#define LLDPD_MODE_ANY 0
+#define LLDPD_MODE_LLDP 1
+#define LLDPD_MODE_CDPV1 2
+#define LLDPD_MODE_CDPV2 3
+#define LLDPD_MODE_SONMP 4
+#define LLDPD_MODE_EDP 5
+ int h_mode;
+
+ int h_flags;
+ int h_mtu;
+ char h_ifname[IFNAMSIZ];
+ u_int8_t h_lladdr[ETHER_ADDR_LEN];
+
+ u_int64_t h_tx_cnt;
+ u_int64_t h_rx_cnt;
+ u_int64_t h_rx_discarded_cnt;
+ u_int64_t h_rx_ageout_cnt;
+
+ u_int8_t *h_proto_macs;
+ time_t h_start_probe;
+
+ struct lldpd_port h_lport;
+ time_t h_llastchange;
+ struct lldpd_frame *h_llastframe;
+
+ time_t h_rlastchange;
+ time_t h_rlastupdate;
+ int h_rid;
+ struct lldpd_frame *h_rlastframe;
+ struct lldpd_port *h_rport;
+ struct lldpd_chassis *h_rchassis;
+};
+
+struct lldpd_interface {
+ TAILQ_ENTRY(lldpd_interface) next;
+ char *name;
+};
+#define STRUCT_LLDPD_INTERFACE "Ls"
+
+struct lldpd_client {
+ TAILQ_ENTRY(lldpd_client) next;
+ int fd;
+};
+
+#define PROTO_SEND_SIG struct lldpd *, struct lldpd_chassis *, struct lldpd_hardware *
+#define PROTO_DECODE_SIG struct lldpd *, char *, int, struct lldpd_hardware *, struct lldpd_chassis **, struct lldpd_port **
+#define PROTO_GUESS_SIG char *, int
+
+struct lldpd;
+struct protocol {
+ int mode; /* > 0 mode identifier (unique per protocol) */
+ int enabled; /* Is this protocol enabled? */
+ char *name; /* Name of protocol */
+ char arg; /* Argument to enable this protocol */
+ int(*send)(PROTO_SEND_SIG); /* How to send a frame */
+ int(*decode)(PROTO_DECODE_SIG); /* How to decode a frame */
+ int(*guess)(PROTO_GUESS_SIG); /* Can be NULL, use MAC address in this case */
+ u_int8_t mac[ETH_ALEN]; /* Destination MAC address used by this protocol */
+ struct sock_filter *filter; /* BPF filter */
+ size_t filterlen; /* Size of BPF filter */
+};
+
+struct lldpd {
+ int g_sock;
+ int g_delay;
+
+ struct protocol *g_protocols;
+ int g_multi; /* Set to 1 if multiple protocols */
+ int g_probe_time;
+
+ time_t g_lastsent;
+ int g_lastrid;
+#ifdef USE_SNMP
+ int g_snmp;
+#endif /* USE_SNMP */
+
+ /* Unix socket handling */
+ int g_ctl;
+ TAILQ_HEAD(, lldpd_client) g_clients;
+
+ char *g_mgmt_pattern;
+
+ struct lldpd_chassis g_lchassis;
+
+ TAILQ_HEAD(, lldpd_hardware) g_hardware;
+};
+
+enum hmsg_type {
+ HMSG_NONE,
+ HMSG_GET_INTERFACES,
+ HMSG_GET_CHASSIS,
+ HMSG_GET_PORT,
+ HMSG_GET_VLANS,
+ HMSG_SHUTDOWN
+};
+
+struct hmsg_hdr {
+ enum hmsg_type type;
+ int16_t len;
+ pid_t pid;
+} __attribute__ ((__packed__));
+
+struct hmsg {
+ struct hmsg_hdr hdr;
+ void *data;
+} __attribute__ ((__packed__));
+
+#define HMSG_HEADER_SIZE sizeof(struct hmsg_hdr)
+#define MAX_HMSGSIZE 8192
+
+/* lldpd.c */
+void lldpd_cleanup(struct lldpd *);
+void lldpd_vlan_cleanup(struct lldpd_port *);
+void lldpd_remote_cleanup(struct lldpd *, struct lldpd_hardware *, int);
+void lldpd_port_cleanup(struct lldpd_port *);
+void lldpd_chassis_cleanup(struct lldpd_chassis *);
+
+/* lldp.c */
+int lldp_send(PROTO_SEND_SIG);
+int lldp_decode(PROTO_DECODE_SIG);
+
+/* cdp.c */
+int cdpv1_send(PROTO_SEND_SIG);
+int cdpv2_send(PROTO_SEND_SIG);
+int cdp_decode(PROTO_DECODE_SIG);
+int cdpv1_guess(PROTO_GUESS_SIG);
+int cdpv2_guess(PROTO_GUESS_SIG);
+
+/* sonmp.c */
+int sonmp_send(PROTO_SEND_SIG);
+int sonmp_decode(PROTO_DECODE_SIG);
+
+/* edp.c */
+int edp_send(PROTO_SEND_SIG);
+int edp_decode(PROTO_DECODE_SIG);
+
+/* ctl.c */
+int ctl_create(struct lldpd *, char *);
+int ctl_connect(char *);
+void ctl_cleanup(int, char *);
+int ctl_accept(struct lldpd *, int);
+int ctl_close(struct lldpd *, int);
+void ctl_msg_init(struct hmsg *, enum hmsg_type);
+int ctl_msg_send(int, struct hmsg *);
+int ctl_msg_recv(int, struct hmsg *);
+int ctl_msg_pack_list(char *, void *, unsigned int, struct hmsg *, void **);
+int ctl_msg_unpack_list(char *, void *, unsigned int, struct hmsg *, void **);
+int ctl_msg_pack_structure(char *, void *, unsigned int, struct hmsg *, void **);
+int ctl_msg_unpack_structure(char *, void *, unsigned int, struct hmsg *, void **);
+
+/* features.c */
+int iface_is_bridge(struct lldpd *, const char *);
+int iface_is_bridged(struct lldpd *, const char *);
+int iface_is_wireless(struct lldpd *, const char *);
+int iface_is_vlan(struct lldpd *, const char *);
+int iface_is_bond(struct lldpd *, const char *);
+int iface_is_bond_slave(struct lldpd *,
+ const char *, const char *);
+int iface_is_enslaved(struct lldpd *, const char *);
+
+/* log.c */
+void log_init(int);
+void log_warn(const char *, ...);
+#define LLOG_WARN(x,...) log_warn("%s: " x, __FUNCTION__, ##__VA_ARGS__)
+void log_warnx(const char *, ...);
+#define LLOG_WARNX(x,...) log_warnx("%s: " x, __FUNCTION__, ##__VA_ARGS__)
+void log_info(const char *, ...);
+#define LLOG_INFO(x,...) log_info("%s: " x, __FUNCTION__, ##__VA_ARGS__)
+void log_debug(const char *, ...);
+#define LLOG_DEBUG(x,...) log_debug("%s: " x, __FUNCTION__, ##__VA_ARGS__)
+void fatal(const char *);
+void fatalx(const char *);
+
+/* agent.c */
+void agent_shutdown();
+void agent_init(struct lldpd *, int);
+
+/* strlcpy.c */
+size_t strlcpy(char *, const char *, size_t);
+
+/* iov.c */
+void iov_dump(struct lldpd_frame **, struct iovec *, int);
+u_int16_t iov_checksum(struct iovec *, int, int);
+
+#endif /* _LLDPD_H */
--- /dev/null
+/* $OpenBSD: log.c,v 1.11 2007/12/07 17:17:00 reyk Exp $ */
+
+/*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and 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 MIND, 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 "lldpd.h"
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <time.h>
+
+void log_init(int);
+void log_warn(const char *, ...);
+void log_warnx(const char *, ...);
+void log_info(const char *, ...);
+void log_debug(const char *, ...);
+void fatal(const char *);
+void fatalx(const char *);
+
+int debug;
+
+void vlog(int, const char *, va_list);
+void logit(int, const char *, ...);
+
+void
+log_init(int n_debug)
+{
+ extern char *__progname;
+
+ debug = n_debug;
+
+ if (!debug)
+ openlog(__progname, LOG_PID | LOG_NDELAY, LOG_DAEMON);
+
+ tzset();
+}
+
+void
+logit(int pri, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vlog(pri, fmt, ap);
+ va_end(ap);
+}
+
+void
+vlog(int pri, const char *fmt, va_list ap)
+{
+ char *nfmt;
+
+ if (debug) {
+ /* best effort in out of mem situations */
+ if (asprintf(&nfmt, "%s\n", fmt) == -1) {
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ } else {
+ vfprintf(stderr, nfmt, ap);
+ free(nfmt);
+ }
+ fflush(stderr);
+ } else
+ vsyslog(pri, fmt, ap);
+}
+
+
+void
+log_warn(const char *emsg, ...)
+{
+ char *nfmt;
+ va_list ap;
+
+ /* best effort to even work in out of memory situations */
+ if (emsg == NULL)
+ logit(LOG_CRIT, "%s", strerror(errno));
+ else {
+ va_start(ap, emsg);
+
+ if (asprintf(&nfmt, "%s: %s", emsg, strerror(errno)) == -1) {
+ /* we tried it... */
+ vlog(LOG_CRIT, emsg, ap);
+ logit(LOG_CRIT, "%s", strerror(errno));
+ } else {
+ vlog(LOG_CRIT, nfmt, ap);
+ free(nfmt);
+ }
+ va_end(ap);
+ }
+}
+
+void
+log_warnx(const char *emsg, ...)
+{
+ va_list ap;
+
+ va_start(ap, emsg);
+ vlog(LOG_CRIT, emsg, ap);
+ va_end(ap);
+}
+
+void
+log_info(const char *emsg, ...)
+{
+ va_list ap;
+
+ va_start(ap, emsg);
+ vlog(LOG_INFO, emsg, ap);
+ va_end(ap);
+}
+
+void
+log_debug(const char *emsg, ...)
+{
+ va_list ap;
+
+ if (debug > 1) {
+ va_start(ap, emsg);
+ vlog(LOG_DEBUG, emsg, ap);
+ va_end(ap);
+ }
+}
+
+void
+fatal(const char *emsg)
+{
+ if (emsg == NULL)
+ logit(LOG_CRIT, "fatal: %s", strerror(errno));
+ else
+ if (errno)
+ logit(LOG_CRIT, "fatal: %s: %s",
+ emsg, strerror(errno));
+ else
+ logit(LOG_CRIT, "fatal: %s", emsg);
+
+ exit(1);
+}
+
+void
+fatalx(const char *emsg)
+{
+ errno = 0;
+ fatal(emsg);
+}
--- /dev/null
+/*
+ * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and 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 "lldpd.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <arpa/inet.h>
+
+struct sonmp_chassis sonmp_chassis_types[] = {
+ {1, "unknown (via SONMP)"},
+ {2, "Nortel 3000"},
+ {3, "Nortel 3030"},
+ {4, "Nortel 2310"},
+ {5, "Nortel 2810"},
+ {6, "Nortel 2912"},
+ {7, "Nortel 2914"},
+ {8, "Nortel 271x"},
+ {9, "Nortel 2813"},
+ {10, "Nortel 2814"},
+ {11, "Nortel 2915"},
+ {12, "Nortel 5000"},
+ {13, "Nortel 2813SA"},
+ {14, "Nortel 2814SA"},
+ {15, "Nortel 810M"},
+ {16, "Nortel EtherCell"},
+ {17, "Nortel 5005"},
+ {18, "Alcatel Ethernet workgroup conc."},
+ {20, "Nortel 2715SA"},
+ {21, "Nortel 2486"},
+ {22, "Nortel 28000 series"},
+ {23, "Nortel 23000 series"},
+ {24, "Nortel 5DN00x series"},
+ {25, "BayStack Ethernet"},
+ {26, "Nortel 23100 series"},
+ {27, "Nortel 100Base-T Hub"},
+ {28, "Nortel 3000 Fast Ethernet"},
+ {29, "Nortel Orion switch"},
+ {30, "unknown"},
+ {31, "Nortel DDS "},
+ {32, "Nortel Centillion"},
+ {33, "Nortel Centillion"},
+ {34, "Nortel Centillion"},
+ {35, "BayStack 301"},
+ {36, "BayStack TokenRing Hub"},
+ {37, "Nortel FVC Multimedia Switch"},
+ {38, "Nortel Switch Node"},
+ {39, "BayStack 302 Switch"},
+ {40, "BayStack 350 Switch"},
+ {41, "BayStack 150 Ethernet Hub"},
+ {42, "Nortel Centillion 50N switch"},
+ {43, "Nortel Centillion 50T switch"},
+ {44, "BayStack 303 and 304 Switches"},
+ {45, "BayStack 200 Ethernet Hub"},
+ {46, "BayStack 250 10/100 Ethernet Hub"},
+ {48, "BayStack 450 10/100/1000 Switches"},
+ {49, "BayStack 410 10/100 Switches"},
+ {50, "Nortel Ethernet Routing 1200 L3 Switch"},
+ {51, "Nortel Ethernet Routing 1250 L3 Switch"},
+ {52, "Nortel Ethernet Routing 1100 L3 Switch"},
+ {53, "Nortel Ethernet Routing 1150 L3 Switch"},
+ {54, "Nortel Ethernet Routing 1050 L3 Switch"},
+ {55, "Nortel Ethernet Routing 1051 L3 Switch"},
+ {56, "Nortel Ethernet Routing 8610 L3 Switch"},
+ {57, "Nortel Ethernet Routing 8606 L3 Switch"},
+ {58, "Nortel Ethernet Routing Switch 8010"},
+ {59, "Nortel Ethernet Routing Switch 8006"},
+ {60, "BayStack 670 wireless access point"},
+ {61, "Nortel Ethernet Routing Switch 740 "},
+ {62, "Nortel Ethernet Routing Switch 750 "},
+ {63, "Nortel Ethernet Routing Switch 790"},
+ {64, "Nortel Business Policy Switch 2000 10/100 Switches"},
+ {65, "Nortel Ethernet Routing 8110 L2 Switch"},
+ {66, "Nortel Ethernet Routing 8106 L2 Switch"},
+ {67, "BayStack 3580 Gig Switch"},
+ {68, "BayStack 10 Power Supply Unit"},
+ {69, "BayStack 420 10/100 Switch"},
+ {70, "OPTera Metro 1200 Ethernet Service Module"},
+ {71, "Nortel Ethernet Routing Switch 8010co"},
+ {72, "Nortel Ethernet Routing 8610co L3 switch"},
+ {73, "Nortel Ethernet Routing 8110co L2 switch"},
+ {74, "Nortel Ethernet Routing 8003"},
+ {75, "Nortel Ethernet Routing 8603 L3 switch"},
+ {76, "Nortel Ethernet Routing 8103 L2 switch"},
+ {77, "BayStack 380 10/100/1000 Switch"},
+ {78, "Nortel Ethernet Switch 470-48T"},
+ {79, "OPTera Metro 1450 Ethernet Service Module"},
+ {80, "OPTera Metro 1400 Ethernet Service Module"},
+ {81, "Alteon Switch Family"},
+ {82, "Ethernet Switch 460-24T-PWR"},
+ {83, "OPTera Metro 8010 OPM L2 Switch"},
+ {84, "OPTera Metro 8010co OPM L2 Switch"},
+ {85, "OPTera Metro 8006 OPM L2 Switch"},
+ {86, "OPTera Metro 8003 OPM L2 Switch"},
+ {87, "Alteon 180e"},
+ {88, "Alteon AD3"},
+ {89, "Alteon 184"},
+ {90, "Alteon AD4"},
+ {91, "Nortel Ethernet Routing 1424 L3 switch"},
+ {92, "Nortel Ethernet Routing 1648 L3 switch"},
+ {93, "Nortel Ethernet Routing 1612 L3 switch"},
+ {94, "Nortel Ethernet Routing 1624 L3 switch "},
+ {95, "BayStack 380-24F Fiber 1000 Switch"},
+ {96, "Nortel Ethernet Routing Switch 5510-24T"},
+ {97, "Nortel Ethernet Routing Switch 5510-48T"},
+ {98, "Nortel Ethernet Switch 470-24T"},
+ {99, "Nortel Networks Wireless LAN Access Point 2220"},
+ {100, "Ethernet Routing RBS 2402 L3 switch"},
+ {101, "Alteon Application Switch 2424 "},
+ {102, "Alteon Application Switch 2224 "},
+ {103, "Alteon Application Switch 2208 "},
+ {104, "Alteon Application Switch 2216"},
+ {105, "Alteon Application Switch 3408"},
+ {106, "Alteon Application Switch 3416"},
+ {107, "Nortel Networks Wireless LAN SecuritySwitch 2250"},
+ {108, "Ethernet Switch 425-48T"},
+ {109, "Ethernet Switch 425-24T"},
+ {110, "Nortel Networks Wireless LAN Access Point 2221"},
+ {111, "Nortel Metro Ethernet Service Unit 24-T SPF switch"},
+ {112, "Nortel Metro Ethernet Service Unit 24-T LX DC switch"},
+ {113, "Nortel Ethernet Routing Switch 8300 10-slot chassis"},
+ {114, "Nortel Ethernet Routing Switch 8300 6-slot chassis"},
+ {115, "Nortel Ethernet Routing Switch 5520-24T-PWR"},
+ {116, "Nortel Ethernet Routing Switch 5520-48T-PWR"},
+ {117, "Nortel Networks VPN Gateway 3050"},
+ {118, "Alteon SSL 310 10/100"},
+ {119, "Alteon SSL 310 10/100 Fiber"},
+ {120, "Alteon SSL 310 10/100 FIPS"},
+ {121, "Alteon SSL 410 10/100/1000"},
+ {122, "Alteon SSL 410 10/100/1000 Fiber"},
+ {123, "Alteon Application Switch 2424-SSL"},
+ {124, "Nortel Ethernet Switch 325-24T"},
+ {125, "Nortel Ethernet Switch 325-24G"},
+ {126, "Nortel Networks Wireless LAN Access Point 2225"},
+ {127, "Nortel Networks Wireless LAN SecuritySwitch 2270"},
+ {128, "Nortel 24-port Ethernet Switch 470-24T-PWR"},
+ {129, "Nortel 48-port Ethernet Switch 470-48T-PWR"},
+ {130, "Nortel Ethernet Routing Switch 5530-24TFD"},
+ {131, "Nortel Ethernet Switch 3510-24T"},
+ {132, "Nortel Metro Ethernet Service Unit 12G AC L3 switch"},
+ {133, "Nortel Metro Ethernet Service Unit 12G DC L3 switch"},
+ {134, "Nortel Secure Access Switch"},
+ {135, "Networks VPN Gateway 3070"},
+ {136, "OPTera Metro 3500"},
+ {137, "SMB BES 1010 24T"},
+ {138, "SMB BES 1010 48T"},
+ {139, "SMB BES 1020 24T PWR"},
+ {140, "SMB BES 1020 48T PWR"},
+ {141, "SMB BES 2010 24T"},
+ {142, "SMB BES 2010 48T"},
+ {143, "SMB BES 2020 24T PWR"},
+ {144, "SMB BES 2020 48T PWR"},
+ {145, "SMB BES 110 24T"},
+ {146, "SMB BES 110 48T"},
+ {147, "SMB BES 120 24T PWR"},
+ {148, "SMB BES 120 48T PWR"},
+ {149, "SMB BES 210 24T"},
+ {150, "SMB BES 210 48T"},
+ {151, "SMB BES 220 24T PWR"},
+ {152, "SMB BES 220 48T PWR"},
+ {153, "OME 6500"},
+ {0, "unknown (via SONMP)"},
+};
+
+int
+sonmp_send(struct lldpd *global, struct lldpd_chassis *chassis,
+ struct lldpd_hardware *hardware)
+{
+ const u_int8_t mcastaddr[] = SONMP_MULTICAST_ADDR;
+ const u_int8_t llcorg[] = LLC_ORG_NORTEL;
+ struct sonmp frame;
+ memset(&frame, 0, sizeof(frame));
+ memcpy(&frame.llc.ether.shost, &hardware->h_lladdr,
+ sizeof(frame.llc.ether.shost));
+ memcpy(&frame.llc.ether.dhost, &mcastaddr,
+ sizeof(frame.llc.ether.dhost));
+ frame.llc.ether.size = htons(sizeof(struct sonmp) -
+ sizeof(struct ieee8023));
+ frame.llc.dsap = frame.llc.ssap = 0xaa;
+ frame.llc.control = 0x03;
+ memcpy(frame.llc.org, llcorg, sizeof(frame.llc.org));
+ frame.llc.protoid = htons(LLC_PID_SONMP_HELLO);
+ memcpy(&frame.addr, &chassis->c_mgmt, sizeof(struct in_addr));
+ frame.seg[2] = if_nametoindex(hardware->h_ifname);
+ frame.chassis = 1; /* Other */
+ frame.backplane = 12; /* Ethernet, Fast Ethernet and Gigabit */
+ frame.links = 1; /* Dunno what it is */
+ frame.state = SONMP_TOPOLOGY_NEW; /* Should work. We have no state */
+
+ if (write((hardware->h_raw_real > 0) ? hardware->h_raw_real :
+ hardware->h_raw, &frame, sizeof(struct sonmp)) == -1) {
+ LLOG_WARN("unable to send packet on real device for %s",
+ hardware->h_ifname);
+ return ENETDOWN;
+ }
+
+ frame.llc.protoid = htons(LLC_PID_SONMP_FLATNET);
+ frame.llc.ether.dhost[ETH_ALEN-1] = 1;
+
+ if (write((hardware->h_raw_real > 0) ? hardware->h_raw_real :
+ hardware->h_raw, &frame, sizeof(struct sonmp)) == -1) {
+ LLOG_WARN("unable to send second SONMP packet on real device for %s",
+ hardware->h_ifname);
+ return ENETDOWN;
+ }
+
+ hardware->h_tx_cnt++;
+ return 0;
+}
+
+int
+sonmp_decode(struct lldpd *cfg, char *frame, int s,
+ struct lldpd_hardware *hardware,
+ struct lldpd_chassis **newchassis, struct lldpd_port **newport)
+{
+ struct sonmp *f;
+ const u_int8_t mcastaddr[] = SONMP_MULTICAST_ADDR;
+ struct lldpd_chassis *chassis;
+ struct lldpd_port *port;
+ int i;
+
+ if ((chassis = calloc(1, sizeof(struct lldpd_chassis))) == NULL) {
+ LLOG_WARN("failed to allocate remote chassis");
+ return -1;
+ }
+ if ((port = calloc(1, sizeof(struct lldpd_port))) == NULL) {
+ LLOG_WARN("failed to allocate remote port");
+ free(chassis);
+ return -1;
+ }
+ TAILQ_INIT(&port->p_vlans);
+
+ if (s < sizeof(struct sonmp)) {
+ LLOG_WARNX("too short frame received on %s", hardware->h_ifname);
+ goto malformed;
+ }
+ f = (struct sonmp *)frame;
+ if (memcmp(f->llc.ether.dhost, mcastaddr,
+ sizeof(mcastaddr)) != 0) {
+ /* There is two multicast address. We just handle only one of
+ * them. */
+ goto malformed;
+ }
+ if (f->llc.protoid != htons(LLC_PID_SONMP_HELLO)) {
+ LLOG_DEBUG("incorrect LLC protocol ID received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+
+ chassis->c_id_subtype = LLDP_CHASSISID_SUBTYPE_ADDR;
+ if ((chassis->c_id = calloc(1, sizeof(struct in_addr) + 1)) == NULL) {
+ LLOG_WARN("unable to allocate memory for chassis id on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ chassis->c_id_len = sizeof(struct in_addr) + 1;
+ chassis->c_id[0] = 1;
+ memcpy(chassis->c_id + 1, &f->addr, sizeof(struct in_addr));
+ if (asprintf(&chassis->c_name, "%s", inet_ntoa(f->addr)) == -1) {
+ LLOG_WARNX("unable to write chassis name for %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ for (i=0; sonmp_chassis_types[i].type != 0; i++) {
+ if (sonmp_chassis_types[i].type == f->chassis)
+ break;
+ }
+ if (asprintf(&chassis->c_descr, "%s",
+ sonmp_chassis_types[i].description) == -1) {
+ LLOG_WARNX("unable to write chassis description for %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ memcpy(&chassis->c_mgmt, &f->addr, sizeof(struct in_addr));
+ chassis->c_ttl = LLDPD_TTL;
+
+ port->p_id_subtype = LLDP_PORTID_SUBTYPE_LOCAL;
+ if (asprintf(&port->p_id, "%02x-%02x-%02x",
+ f->seg[0], f->seg[1], f->seg[2]) == -1) {
+ LLOG_WARN("unable to allocate memory for port id on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ port->p_id_len = strlen(port->p_id);
+
+ if ((f->seg[0] == 0) && (f->seg[1] == 0)) {
+ if (asprintf(&port->p_descr, "port %d",
+ *(u_int8_t *)(&f->seg[2])) == -1) {
+ LLOG_WARNX("unable to write port description for %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ } else if (f->seg[0] == 0) {
+ if (asprintf(&port->p_descr, "port %d/%d",
+ *(u_int8_t *)(&f->seg[1]),
+ *(u_int8_t *)(&f->seg[2])) == -1) {
+ LLOG_WARNX("unable to write port description for %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ } else {
+ if (asprintf(&port->p_descr, "port %x:%x:%x",
+ f->seg[0], f->seg[1], f->seg[2]) == -1) {
+ LLOG_WARNX("unable to write port description for %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ }
+ *newchassis = chassis;
+ *newport = port;
+ return 1;
+
+malformed:
+ free(chassis->c_id);
+ free(chassis->c_name);
+ free(chassis->c_descr);
+ free(chassis);
+ free(port->p_id);
+ free(port->p_descr);
+ free(port);
+ return -1;
+}
--- /dev/null
+/*
+ * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and 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.
+ */
+
+#ifndef _SONMP_H
+#define _SONMP_H
+
+#define SONMP_MULTICAST_ADDR { \
+ 0x01, 0x00, 0x81, 0x00, 0x01, 0x00 \
+}
+#define LLC_ORG_NORTEL { 0x00, 0x00, 0x81 }
+#define LLC_PID_SONMP_HELLO 0x01a2
+#define LLC_PID_SONMP_FLATNET 0x01a1
+
+#include "llc.h"
+
+struct sonmp {
+ struct ethllc llc;
+ struct in_addr addr;
+ u_int8_t seg[3];
+ u_int8_t chassis;
+ u_int8_t backplane;
+ u_int8_t state;
+ u_int8_t links;
+} __attribute__ ((__packed__));
+
+struct sonmp_chassis {
+ int type;
+ char *description;
+};
+
+#define SONMP_TOPOLOGY_CHANGED 1
+#define SONMP_TOPOLOGY_UNCHANGED 2
+#define SONMP_TOPOLOGY_NEW 3
+
+#endif /* _SONMP_H */
--- /dev/null
+/* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $ */
+
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and 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 <sys/types.h>
+#include <string.h>
+
+/*
+ * Copy src to string dst of size siz. At most siz-1 characters
+ * will be copied. Always NUL terminates (unless siz == 0).
+ * Returns strlen(src); if retval >= siz, truncation occurred.
+ */
+size_t
+strlcpy(char *dst, const char *src, size_t siz)
+{
+ char *d = dst;
+ const char *s = src;
+ size_t n = siz;
+
+ /* Copy as many bytes as will fit */
+ if (n != 0) {
+ while (--n != 0) {
+ if ((*d++ = *s++) == '\0')
+ break;
+ }
+ }
+
+ /* Not enough room in dst, add NUL and traverse rest of src */
+ if (n == 0) {
+ if (siz != 0)
+ *d = '\0'; /* NUL-terminate dst */
+ while (*s++)
+ ;
+ }
+
+ return(s - src - 1); /* count does not include NUL */
+}