]> git.ipfire.org Git - thirdparty/lldpd.git/commitdiff
Initial import
authorVincent Bernat <vbernat@wanadooportails.com>
Wed, 15 Oct 2008 07:27:31 +0000 (09:27 +0200)
committerVincent Bernat <vbernat@wanadooportails.com>
Wed, 15 Oct 2008 07:27:31 +0000 (09:27 +0200)
37 files changed:
Makefile.am [new file with mode: 0644]
README [new file with mode: 0644]
configure.ac [new file with mode: 0644]
debian/changelog [new file with mode: 0644]
debian/compat [new file with mode: 0644]
debian/control [new file with mode: 0644]
debian/copyright [new file with mode: 0644]
debian/lldpd.default [new file with mode: 0644]
debian/lldpd.init.d [new file with mode: 0644]
debian/rules [new file with mode: 0755]
man/Makefile.am [new file with mode: 0644]
man/lldpctl.8 [new file with mode: 0644]
man/lldpd.8 [new file with mode: 0644]
misc/test-addmulti.c [new file with mode: 0644]
misc/test-align.c [new file with mode: 0644]
misc/test-parselldp.c [new file with mode: 0644]
misc/test-snmp.c [new file with mode: 0644]
src/Makefile.am [new file with mode: 0644]
src/agent.c [new file with mode: 0644]
src/cdp.c [new file with mode: 0644]
src/cdp.h [new file with mode: 0644]
src/compat.h [new file with mode: 0644]
src/ctl.c [new file with mode: 0644]
src/edp.c [new file with mode: 0644]
src/edp.h [new file with mode: 0644]
src/features.c [new file with mode: 0644]
src/iov.c [new file with mode: 0644]
src/llc.h [new file with mode: 0644]
src/lldp.c [new file with mode: 0644]
src/lldp.h [new file with mode: 0644]
src/lldpctl.c [new file with mode: 0644]
src/lldpd.c [new file with mode: 0644]
src/lldpd.h [new file with mode: 0644]
src/log.c [new file with mode: 0644]
src/sonmp.c [new file with mode: 0644]
src/sonmp.h [new file with mode: 0644]
src/strlcpy.c [new file with mode: 0644]

diff --git a/Makefile.am b/Makefile.am
new file mode 100644 (file)
index 0000000..c28b8c1
--- /dev/null
@@ -0,0 +1,2 @@
+SUBDIRS = src man
+dist_doc_DATA = README
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..d2c14e2
--- /dev/null
+++ b/README
@@ -0,0 +1,68 @@
+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/
diff --git a/configure.ac b/configure.ac
new file mode 100644 (file)
index 0000000..bd3f956
--- /dev/null
@@ -0,0 +1,66 @@
+#                                               -*- 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
diff --git a/debian/changelog b/debian/changelog
new file mode 100644 (file)
index 0000000..e2a2dfa
--- /dev/null
@@ -0,0 +1,5 @@
+lldpd (0+20080718cvs) UNRELEASED; urgency=low
+
+  * Initial release
+
+ -- Vincent Bernat <bernat@debian.org>  Fri, 18 Jul 2008 11:20:34 +0200
diff --git a/debian/compat b/debian/compat
new file mode 100644 (file)
index 0000000..7ed6ff8
--- /dev/null
@@ -0,0 +1 @@
+5
diff --git a/debian/control b/debian/control
new file mode 100644 (file)
index 0000000..70292a3
--- /dev/null
@@ -0,0 +1,22 @@
+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.
diff --git a/debian/copyright b/debian/copyright
new file mode 100644 (file)
index 0000000..ae18ad4
--- /dev/null
@@ -0,0 +1,19 @@
+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`.
diff --git a/debian/lldpd.default b/debian/lldpd.default
new file mode 100644 (file)
index 0000000..3acacfd
--- /dev/null
@@ -0,0 +1,2 @@
+# Uncomment to start SNMP subagent and enable CDP, SONMP and EDP protocol
+#DAEMON_ARGS="-x -c -s -e"
diff --git a/debian/lldpd.init.d b/debian/lldpd.init.d
new file mode 100644 (file)
index 0000000..3ece7f1
--- /dev/null
@@ -0,0 +1,102 @@
+#! /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
+
+:
diff --git a/debian/rules b/debian/rules
new file mode 100755 (executable)
index 0000000..dcc6c38
--- /dev/null
@@ -0,0 +1,6 @@
+#!/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
diff --git a/man/Makefile.am b/man/Makefile.am
new file mode 100644 (file)
index 0000000..cda81b3
--- /dev/null
@@ -0,0 +1 @@
+man_MANS = lldpd.8 lldpctl.8
diff --git a/man/lldpctl.8 b/man/lldpctl.8
new file mode 100644 (file)
index 0000000..cd0a6e7
--- /dev/null
@@ -0,0 +1,55 @@
+.\" 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 .
diff --git a/man/lldpd.8 b/man/lldpd.8
new file mode 100644 (file)
index 0000000..3f21fc9
--- /dev/null
@@ -0,0 +1,106 @@
+.\" 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 .
diff --git a/misc/test-addmulti.c b/misc/test-addmulti.c
new file mode 100644 (file)
index 0000000..4b290ee
--- /dev/null
@@ -0,0 +1,25 @@
+#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;
+}
diff --git a/misc/test-align.c b/misc/test-align.c
new file mode 100644 (file)
index 0000000..f3e9a26
--- /dev/null
@@ -0,0 +1,54 @@
+/* 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);
+
diff --git a/misc/test-parselldp.c b/misc/test-parselldp.c
new file mode 100644 (file)
index 0000000..b250309
--- /dev/null
@@ -0,0 +1,356 @@
+#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;
+}
diff --git a/misc/test-snmp.c b/misc/test-snmp.c
new file mode 100644 (file)
index 0000000..9ae78f1
--- /dev/null
@@ -0,0 +1,463 @@
+/* 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;
+}
+
+
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644 (file)
index 0000000..e84f5bf
--- /dev/null
@@ -0,0 +1,13 @@
+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
diff --git a/src/agent.c b/src/agent.c
new file mode 100644 (file)
index 0000000..44f5aea
--- /dev/null
@@ -0,0 +1,814 @@
+/*
+ * 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");
+}
diff --git a/src/cdp.c b/src/cdp.c
new file mode 100644 (file)
index 0000000..8d13489
--- /dev/null
+++ b/src/cdp.c
@@ -0,0 +1,454 @@
+/*
+ * 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 = &cap;
+       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);
+}
diff --git a/src/cdp.h b/src/cdp.h
new file mode 100644 (file)
index 0000000..634655c
--- /dev/null
+++ b/src/cdp.h
@@ -0,0 +1,69 @@
+/*
+ * 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 */
+
diff --git a/src/compat.h b/src/compat.h
new file mode 100644 (file)
index 0000000..8e1528f
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * 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
diff --git a/src/ctl.c b/src/ctl.c
new file mode 100644 (file)
index 0000000..0c74d8d
--- /dev/null
+++ b/src/ctl.c
@@ -0,0 +1,539 @@
+/*
+ * 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;
+}
diff --git a/src/edp.c b/src/edp.c
new file mode 100644 (file)
index 0000000..0275074
--- /dev/null
+++ b/src/edp.c
@@ -0,0 +1,463 @@
+/*
+ * 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;
+}
diff --git a/src/edp.h b/src/edp.h
new file mode 100644 (file)
index 0000000..5e7aec2
--- /dev/null
+++ b/src/edp.h
@@ -0,0 +1,74 @@
+/*
+ * 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 */
diff --git a/src/features.c b/src/features.c
new file mode 100644 (file)
index 0000000..06b872d
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+ * 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;
+}
diff --git a/src/iov.c b/src/iov.c
new file mode 100644 (file)
index 0000000..c78b6f9
--- /dev/null
+++ b/src/iov.c
@@ -0,0 +1,74 @@
+/*
+ * 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);
+}
diff --git a/src/llc.h b/src/llc.h
new file mode 100644 (file)
index 0000000..002ccf1
--- /dev/null
+++ b/src/llc.h
@@ -0,0 +1,35 @@
+/*
+ * 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
diff --git a/src/lldp.c b/src/lldp.c
new file mode 100644 (file)
index 0000000..1ff5ede
--- /dev/null
@@ -0,0 +1,584 @@
+/*
+ * 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 = &cap;
+       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;
+}
diff --git a/src/lldp.h b/src/lldp.h
new file mode 100644 (file)
index 0000000..54ef45e
--- /dev/null
@@ -0,0 +1,233 @@
+/*
+ * 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 */
diff --git a/src/lldpctl.c b/src/lldpctl.c
new file mode 100644 (file)
index 0000000..4279df7
--- /dev/null
@@ -0,0 +1,457 @@
+/*
+ * 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;
+}
diff --git a/src/lldpd.c b/src/lldpd.c
new file mode 100644 (file)
index 0000000..e4161b5
--- /dev/null
@@ -0,0 +1,1474 @@
+/*
+ * 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(&ethc, 0, sizeof(ethc));
+       strlcpy(ifr.ifr_name, hardware->h_ifname, sizeof(ifr.ifr_name));
+       ifr.ifr_data = (caddr_t)&ethc;
+       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);
+}
diff --git a/src/lldpd.h b/src/lldpd.h
new file mode 100644 (file)
index 0000000..3051a90
--- /dev/null
@@ -0,0 +1,292 @@
+/*
+ * 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 */
diff --git a/src/log.c b/src/log.c
new file mode 100644 (file)
index 0000000..7fa90bb
--- /dev/null
+++ b/src/log.c
@@ -0,0 +1,161 @@
+/*     $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);
+}
diff --git a/src/sonmp.c b/src/sonmp.c
new file mode 100644 (file)
index 0000000..19067c7
--- /dev/null
@@ -0,0 +1,336 @@
+/*
+ * 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;
+}
diff --git a/src/sonmp.h b/src/sonmp.h
new file mode 100644 (file)
index 0000000..0b173bc
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * 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 */
diff --git a/src/strlcpy.c b/src/strlcpy.c
new file mode 100644 (file)
index 0000000..d32b659
--- /dev/null
@@ -0,0 +1,51 @@
+/*     $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 */
+}