]> git.ipfire.org Git - thirdparty/lldpd.git/commitdiff
Separate daemon and client code. Provide a client library.
authorVincent Bernat <bernat@luffy.cx>
Tue, 14 Aug 2012 15:06:23 +0000 (17:06 +0200)
committerVincent Bernat <bernat@luffy.cx>
Tue, 28 Aug 2012 19:46:42 +0000 (21:46 +0200)
In `daemon/`, we have what is exclusively used by lldpd. In `client/`,
we have what is exclusively used by lldpctl and in `lib/`, we have a
library that will talk to the daemon and can be used to build other
clients. lldpctl is using this library.

This is a mega commit.

There are also some extra little changes, notably, adding a line to
each file to set "openbsd" as the default C style.

79 files changed:
.gitignore
Makefile.am
NEWS
README.md
configure.ac
doxygen.am [new file with mode: 0644]
doxygen.cfg [new file with mode: 0644]
m4/ax_prog_doxygen.m4 [new file with mode: 0644]
man/lldpctl.8
src/Makefile.am
src/agent.h [deleted file]
src/client/Makefile.am [new file with mode: 0644]
src/client/actions.c [new file with mode: 0644]
src/client/client.h [moved from src/lldpctl.h with 69% similarity]
src/client/display.c [new file with mode: 0644]
src/client/kv_writer.c [moved from src/kv_writer.c with 97% similarity]
src/client/lldpctl.c [new file with mode: 0644]
src/client/text_writer.c [moved from src/text_writer.c with 97% similarity]
src/client/writer.h [moved from src/writer.h with 91% similarity]
src/client/xml_writer.c [moved from src/xml_writer.c with 97% similarity]
src/compat/Makefile.am [new file with mode: 0644]
src/compat/compat.h [moved from src/compat.h with 97% similarity]
src/compat/getifaddrs.c [moved from src/getifaddrs.c with 99% similarity]
src/compat/malloc.c [moved from src/malloc.c with 85% similarity]
src/compat/realloc.c [moved from src/realloc.c with 88% similarity]
src/compat/strlcpy.c [moved from src/strlcpy.c with 97% similarity]
src/ctl.c
src/ctl.h [new file with mode: 0644]
src/daemon/Makefile.am [new file with mode: 0644]
src/daemon/agent.c [moved from src/agent.c with 98% similarity]
src/daemon/agent.h [new file with mode: 0644]
src/daemon/agent_priv.c [moved from src/agent_priv.c with 99% similarity]
src/daemon/cdp.c [moved from src/cdp.c with 99% similarity]
src/daemon/cdp.h [moved from src/cdp.h with 97% similarity]
src/daemon/client.c [moved from src/client.c with 87% similarity]
src/daemon/dmi.c [moved from src/dmi.c with 97% similarity]
src/daemon/edp.c [moved from src/edp.c with 99% similarity]
src/daemon/edp.h [moved from src/edp.h with 96% similarity]
src/daemon/event.c [moved from src/event.c with 99% similarity]
src/daemon/frame.c [moved from src/frame.c with 96% similarity]
src/daemon/frame.h [moved from src/frame.h with 98% similarity]
src/daemon/interfaces.c [moved from src/interfaces.c with 99% similarity]
src/daemon/lldp-tlv.h [new file with mode: 0644]
src/daemon/lldp.c [moved from src/lldp.c with 94% similarity]
src/daemon/lldpd.c [moved from src/lldpd.c with 90% similarity]
src/daemon/lldpd.h [new file with mode: 0644]
src/daemon/main.c [moved from src/main.c with 65% similarity]
src/daemon/priv.c [moved from src/priv.c with 99% similarity]
src/daemon/privsep_fdpass.c [moved from src/privsep_fdpass.c with 98% similarity]
src/daemon/sonmp.c [moved from src/sonmp.c with 99% similarity]
src/daemon/sonmp.h [moved from src/sonmp.h with 96% similarity]
src/display.c [deleted file]
src/lib/Makefile.am [new file with mode: 0644]
src/lib/atom-private.c [new file with mode: 0644]
src/lib/atom.c [new file with mode: 0644]
src/lib/connection.c [new file with mode: 0644]
src/lib/errors.c [new file with mode: 0644]
src/lib/lldpctl.h [new file with mode: 0644]
src/lib/lldpctl.pc.in [new file with mode: 0644]
src/lib/private.h [new file with mode: 0644]
src/lldp-const.h [new file with mode: 0644]
src/lldp.h [deleted file]
src/lldpctl.c [deleted file]
src/lldpd-structs.c [new file with mode: 0644]
src/lldpd-structs.h [moved from src/lldpd.h with 53% similarity]
src/log.c
src/log.h [new file with mode: 0644]
src/marshal.c
src/marshal.h
tests/Makefile.am
tests/check_cdp.c
tests/check_edp.c
tests/check_ifaddrs.c
tests/check_lldp.c
tests/check_marshal.c
tests/check_snmp.c
tests/check_sonmp.c
tests/common.c
tests/common.h

index ae8f0de4f5df2cca7180188ec2968224e91c04e4..3e5acad714ac8a2d666cb35b6668efe486f6a3c3 100644 (file)
 # automake
 /Makefile.in
 /src/Makefile.in
+/src/client/Makefile.in
+/src/lib/Makefile.in
+/src/daemon/Makefile.in
+/src/compat/Makefile.in
+/src/Makefile.in
 /tests/Makefile.in
 /man/Makefile.in
 
index 097039135c948749b6f3bb7375b8cc345583cd99..7931805ca797bbf0562789579437034100af6dd8 100644 (file)
@@ -1,6 +1,11 @@
+include doxygen.am
+
 ACLOCAL_AMFLAGS = -I m4
-SUBDIRS = src man tests
+
+SUBDIRS      = src/compat src src/daemon src/lib src/client man tests
+EXTRA_DIST   = $(DX_CONFIG)
 DIST_SUBDIRS = $(SUBDIRS) libevent
+
 dist_doc_DATA = README.md NEWS
 
 # Build changelog from git history
@@ -18,3 +23,5 @@ $(distdir)/ChangeLog:
                        prev=$$tag ; \
                done > $@ ; \
        fi
+
+MOSTLYCLEANFILES = $(DX_CLEANFILES)
diff --git a/NEWS b/NEWS
index e26b5a17da6b7ac431c17af777c53dfc40612dac..98eca5f4b6ed81902ef00ff5d59d09753e30a777 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,10 @@
+lldpd (0.6.1)
+  * Features:
+    + Provide liblldpctl.so, a library to interface with lldpd. The
+      documentation is provided through Doxygen. See src/lib/lldpctl.h
+      which contains all the exported functions.
+    + Make lldpctl uses liblldpctl.so.
+
 lldpd (0.6)
   * Features:
     + Allow lldpctl to display hidden ports.
index c203d30012feaa46f8707ccb4dc9648a170f6d85..152a47e339987950c67ebf4684fec1deceedce52 100644 (file)
--- a/README.md
+++ b/README.md
@@ -5,6 +5,9 @@ lldpd: implementation of IEEE 802.1ab (LLDP)
 
   https://github.com/vincentbernat/lldpd/wiki
 
+Features
+--------
+
 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
@@ -22,6 +25,9 @@ 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.
 
+Installation
+------------
+
 To compile lldpd, use the following:
 
     ./configure
@@ -42,7 +48,7 @@ empty struct.h:
     touch src/struct.h
 
 If you are missing some headers or if some headers are incorrect
-(if_vlan.h and if_bonding.h on RHEL 2.1 for example), you can copy
+(`if_vlan.h` and `if_bonding.h` on RHEL 2.1 for example), you can copy
 some more current version (for example from Debian Lenny or from
 Fedora) in some directory like "extra-headers/":
 
@@ -68,6 +74,9 @@ root, not `_lldpd`!). If you get fuzzy timestamps from syslog, copy
 line. If you don't want to run it as root, just install it setuid or
 setgid `_lldpd`.
 
+Usage
+-----
+
 lldpd also implements CDP (Cisco Discovery Protocol), FDP (Foundry
 Discovery Protocol), SONMP (Nortel Discovery Protocol) and EDP
 (Extreme Discovery Protocol). However, recent versions of IOS should
@@ -110,6 +119,37 @@ More information:
  * http://standards.ieee.org/getieee802/download/802.1AB-2005.pdf
  * http://wiki.wireshark.org/LinkLayerDiscoveryProtocol
 
+Embedding
+---------
+
+To embed lldpd into an existing system, there are two point of entries:
+
+ 1. If your system does not use standard Linux interface, you can
+    support additional interfaces by implementing the appropriate
+    `struct lldpd_ops`. You can look at `src/daemon/interfaces.c` for
+    examples. You also need to implement an interface handler which is
+    a function that will take care of some interface (including the
+    regular ones if they need special treatment). This handler will be
+    responsible to instantiate the appropriate `struct lldpd_hardware`
+    structure and associate it to the appropriate `struct
+    lldpd_ops`. Again, there are examples in
+    `src/daemon/interfaces.c`. The handler should be added to the list
+    of handlers in `src/daemon/lldpd.c` in
+    `lldpd_update_localports()`.
+
+ 2. `lldpctl` provides a convenient way to query `lldpd`. It also
+    comes with various outputs, including XML which allows one to
+    parse its output for integration and automation purpose. Another
+    way is to use SNMP support. A third way is to write your own
+    controller using `liblldpctl.so`. Its API is described in
+    `src/lib/lldpctl.h`. The custom binary protocol between
+    `liblldpctl.so` and `lldpd` is not stable. Therefore, the library
+    should always be shipped with `lldpd`. On the other hand, programs
+    using `liblldpctl.so` can rely on the classic ABI rules.
+
+License
+-------
+
 lldpd is distributed under the ISC license:
 
  > Permission to use, copy, modify, and/or distribute this software for any
index 138a9de96a9adf699dd53f5e34e42b9854a0acd8..1749ee8e72385b49dbb73ce1696134149748a0ef 100644 (file)
@@ -14,9 +14,11 @@ AC_INIT([lldpd],
         [m4_esyscmd_s([git describe --tags --always 2> /dev/null || date +%F])],
         [bernat@luffy.cx])
 
-AC_CONFIG_SRCDIR([src/lldpd.c])
+AC_CONFIG_SRCDIR([src/log.c])
 AC_CONFIG_HEADER([config.h])
-AC_CONFIG_FILES([Makefile src/Makefile man/Makefile tests/Makefile])
+AC_CONFIG_FILES([Makefile src/Makefile src/compat/Makefile src/daemon/Makefile
+                         src/lib/Makefile src/lib/lldpctl.pc src/client/Makefile
+                         man/Makefile tests/Makefile])
 AC_CONFIG_MACRO_DIR([m4])
 
 # Configure automake
@@ -37,6 +39,18 @@ AC_PROG_CXX
 AM_PROG_CC_C_O
 AC_PROG_LIBTOOL
 
+# Doxygen
+DX_HTML_FEATURE(ON)
+DX_DOT_FEATURE(OFF)
+DX_CHM_FEATURE(OFF)
+DX_CHI_FEATURE(OFF)
+DX_MAN_FEATURE(OFF)
+DX_RTF_FEATURE(OFF)
+DX_XML_FEATURE(OFF)
+DX_PDF_FEATURE(ON)
+DX_PS_FEATURE(OFF)
+DX_INIT_DOXYGEN([lldpd], [doxygen.cfg], [doxygen])
+
 # Check some compiler flags
 AX_CFLAGS_GCC_OPTION([-fdiagnostics-show-option])
 AX_CFLAGS_GCC_OPTION([-std=gnu99])
@@ -113,6 +127,7 @@ AC_CACHE_SAVE
 lldp_CHECK___PROGNAME
 
 # Checks for library functions.
+AC_CONFIG_LIBOBJ_DIR([src/compat])
 AC_FUNC_MALLOC
 AC_FUNC_REALLOC
 AC_REPLACE_FUNCS([strlcpy])
diff --git a/doxygen.am b/doxygen.am
new file mode 100644 (file)
index 0000000..050ff6b
--- /dev/null
@@ -0,0 +1,186 @@
+# Copyright (C) 2004 Oren Ben-Kiki
+# This file is distributed under the same terms as the Automake macro files.
+
+# Generate automatic documentation using Doxygen. Goals and variables values
+# are controlled by the various DX_COND_??? conditionals set by autoconf.
+#
+# The provided goals are:
+# doxygen-doc: Generate all doxygen documentation.
+# doxygen-run: Run doxygen, which will generate some of the documentation
+#              (HTML, CHM, CHI, MAN, RTF, XML) but will not do the post
+#              processing required for the rest of it (PS, PDF, and some MAN).
+# doxygen-man: Rename some doxygen generated man pages.
+# doxygen-ps: Generate doxygen PostScript documentation.
+# doxygen-pdf: Generate doxygen PDF documentation.
+#
+# Note that by default these are not integrated into the automake goals. If
+# doxygen is used to generate man pages, you can achieve this integration by
+# setting man3_MANS to the list of man pages generated and then adding the
+# dependency:
+#
+#   $(man3_MANS): doxygen-doc
+#
+# This will cause make to run doxygen and generate all the documentation.
+#
+# The following variable is intended for use in Makefile.am:
+#
+# DX_CLEANFILES = everything to clean.
+#
+# This is usually added to MOSTLYCLEANFILES.
+
+## --------------------------------- ##
+## Format-independent Doxygen rules. ##
+## --------------------------------- ##
+
+if DX_COND_doc
+
+## ------------------------------- ##
+## Rules specific for HTML output. ##
+## ------------------------------- ##
+
+if DX_COND_html
+
+DX_CLEAN_HTML = @DX_DOCDIR@/html
+
+endif DX_COND_html
+
+## ------------------------------ ##
+## Rules specific for CHM output. ##
+## ------------------------------ ##
+
+if DX_COND_chm
+
+DX_CLEAN_CHM = @DX_DOCDIR@/chm
+
+if DX_COND_chi
+
+DX_CLEAN_CHI = @DX_DOCDIR@/@PACKAGE@.chi
+
+endif DX_COND_chi
+
+endif DX_COND_chm
+
+## ------------------------------ ##
+## Rules specific for MAN output. ##
+## ------------------------------ ##
+
+if DX_COND_man
+
+DX_CLEAN_MAN = @DX_DOCDIR@/man
+
+endif DX_COND_man
+
+## ------------------------------ ##
+## Rules specific for RTF output. ##
+## ------------------------------ ##
+
+if DX_COND_rtf
+
+DX_CLEAN_RTF = @DX_DOCDIR@/rtf
+
+endif DX_COND_rtf
+
+## ------------------------------ ##
+## Rules specific for XML output. ##
+## ------------------------------ ##
+
+if DX_COND_xml
+
+DX_CLEAN_XML = @DX_DOCDIR@/xml
+
+endif DX_COND_xml
+
+## ----------------------------- ##
+## Rules specific for PS output. ##
+## ----------------------------- ##
+
+if DX_COND_ps
+
+DX_CLEAN_PS = @DX_DOCDIR@/@PACKAGE@.ps
+
+DX_PS_GOAL = doxygen-ps
+
+doxygen-ps: @DX_DOCDIR@/@PACKAGE@.ps
+
+@DX_DOCDIR@/@PACKAGE@.ps: @DX_DOCDIR@/@PACKAGE@.tag
+       cd @DX_DOCDIR@/latex; \
+       rm -f *.aux *.toc *.idx *.ind *.ilg *.log *.out; \
+       $(DX_LATEX) refman.tex; \
+       $(MAKEINDEX_PATH) refman.idx; \
+       $(DX_LATEX) refman.tex; \
+       countdown=5; \
+       while $(DX_EGREP) 'Rerun (LaTeX|to get cross-references right)' \
+                         refman.log > /dev/null 2>&1 \
+          && test $$countdown -gt 0; do \
+           $(DX_LATEX) refman.tex; \
+           countdown=`expr $$countdown - 1`; \
+       done; \
+       $(DX_DVIPS) -o ../@PACKAGE@.ps refman.dvi
+
+endif DX_COND_ps
+
+## ------------------------------ ##
+## Rules specific for PDF output. ##
+## ------------------------------ ##
+
+if DX_COND_pdf
+
+DX_CLEAN_PDF = @DX_DOCDIR@/@PACKAGE@.pdf
+
+DX_PDF_GOAL = doxygen-pdf
+
+doxygen-pdf: @DX_DOCDIR@/@PACKAGE@.pdf
+
+@DX_DOCDIR@/@PACKAGE@.pdf: @DX_DOCDIR@/@PACKAGE@.tag
+       cd @DX_DOCDIR@/latex; \
+       rm -f *.aux *.toc *.idx *.ind *.ilg *.log *.out; \
+       $(DX_PDFLATEX) refman.tex; \
+       $(DX_MAKEINDEX) refman.idx; \
+       $(DX_PDFLATEX) refman.tex; \
+       countdown=5; \
+       while $(DX_EGREP) 'Rerun (LaTeX|to get cross-references right)' \
+                         refman.log > /dev/null 2>&1 \
+          && test $$countdown -gt 0; do \
+           $(DX_PDFLATEX) refman.tex; \
+           countdown=`expr $$countdown - 1`; \
+       done; \
+       mv refman.pdf ../@PACKAGE@.pdf
+
+endif DX_COND_pdf
+
+## ------------------------------------------------- ##
+## Rules specific for LaTeX (shared for PS and PDF). ##
+## ------------------------------------------------- ##
+
+if DX_COND_latex
+
+DX_CLEAN_LATEX = @DX_DOCDIR@/latex
+
+endif DX_COND_latex
+
+.PHONY: doxygen-run doxygen-doc $(DX_PS_GOAL) $(DX_PDF_GOAL) @DX_DOCDIR@/@PACKAGE@.tag
+
+.INTERMEDIATE: doxygen-run $(DX_PS_GOAL) $(DX_PDF_GOAL)
+
+doxygen-run: @DX_DOCDIR@/@PACKAGE@.tag
+
+doxygen-doc: doxygen-run $(DX_PS_GOAL) $(DX_PDF_GOAL)
+
+@DX_DOCDIR@/@PACKAGE@.tag: $(DX_CONFIG)
+       rm -rf @DX_DOCDIR@
+       $(DX_ENV) $(DX_DOXYGEN) $(srcdir)/$(DX_CONFIG)
+
+DX_CLEANFILES = \
+    @DX_DOCDIR@/@PACKAGE@.tag \
+    -r \
+    $(DX_CLEAN_HTML) \
+    $(DX_CLEAN_CHM) \
+    $(DX_CLEAN_CHI) \
+    $(DX_CLEAN_MAN) \
+    $(DX_CLEAN_RTF) \
+    $(DX_CLEAN_XML) \
+    $(DX_CLEAN_PS) \
+    $(DX_CLEAN_PDF) \
+    $(DX_CLEAN_LATEX)
+
+endif DX_COND_doc
diff --git a/doxygen.cfg b/doxygen.cfg
new file mode 100644 (file)
index 0000000..5614126
--- /dev/null
@@ -0,0 +1,305 @@
+# Doxyfile 1.7.6.1
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+DOXYFILE_ENCODING      = UTF-8
+PROJECT_NAME           = $(PROJECT)-$(VERSION)
+PROJECT_NUMBER         =
+PROJECT_BRIEF          =
+PROJECT_LOGO           =
+OUTPUT_DIRECTORY       = $(DOCDIR)
+CREATE_SUBDIRS         = NO
+OUTPUT_LANGUAGE        = English
+BRIEF_MEMBER_DESC      = YES
+REPEAT_BRIEF           = YES
+ABBREVIATE_BRIEF       =
+ALWAYS_DETAILED_SEC    = NO
+INLINE_INHERITED_MEMB  = NO
+FULL_PATH_NAMES        = YES
+STRIP_FROM_PATH        = $(SRCDIR)
+STRIP_FROM_INC_PATH    = $(SRCDIR)
+SHORT_NAMES            = NO
+JAVADOC_AUTOBRIEF      = YES
+QT_AUTOBRIEF           = NO
+MULTILINE_CPP_IS_BRIEF = NO
+INHERIT_DOCS           = YES
+SEPARATE_MEMBER_PAGES  = NO
+TAB_SIZE               = 8
+ALIASES                =
+TCL_SUBST              =
+OPTIMIZE_OUTPUT_FOR_C  = YES
+OPTIMIZE_OUTPUT_JAVA   = NO
+OPTIMIZE_FOR_FORTRAN   = NO
+OPTIMIZE_OUTPUT_VHDL   = NO
+EXTENSION_MAPPING      =
+BUILTIN_STL_SUPPORT    = NO
+CPP_CLI_SUPPORT        = NO
+SIP_SUPPORT            = NO
+IDL_PROPERTY_SUPPORT   = YES
+DISTRIBUTE_GROUP_DOC   = NO
+SUBGROUPING            = YES
+INLINE_GROUPED_CLASSES = NO
+INLINE_SIMPLE_STRUCTS  = NO
+TYPEDEF_HIDES_STRUCT   = NO
+SYMBOL_CACHE_SIZE      = 0
+LOOKUP_CACHE_SIZE      = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+EXTRACT_ALL            = YES
+EXTRACT_PRIVATE        = NO
+EXTRACT_STATIC         = NO
+EXTRACT_LOCAL_CLASSES  = NO
+EXTRACT_LOCAL_METHODS  = NO
+EXTRACT_ANON_NSPACES   = NO
+HIDE_UNDOC_MEMBERS     = NO
+HIDE_UNDOC_CLASSES     = NO
+HIDE_FRIEND_COMPOUNDS  = NO
+HIDE_IN_BODY_DOCS      = NO
+INTERNAL_DOCS          = NO
+CASE_SENSE_NAMES       = YES
+HIDE_SCOPE_NAMES       = NO
+SHOW_INCLUDE_FILES     = YES
+FORCE_LOCAL_INCLUDES   = NO
+INLINE_INFO            = YES
+SORT_MEMBER_DOCS       = YES
+SORT_BRIEF_DOCS        = NO
+SORT_MEMBERS_CTORS_1ST = NO
+SORT_GROUP_NAMES       = NO
+SORT_BY_SCOPE_NAME     = NO
+STRICT_PROTO_MATCHING  = NO
+GENERATE_TODOLIST      = YES
+GENERATE_TESTLIST      = YES
+GENERATE_BUGLIST       = YES
+GENERATE_DEPRECATEDLIST= YES
+ENABLED_SECTIONS       =
+MAX_INITIALIZER_LINES  = 30
+SHOW_USED_FILES        = YES
+SHOW_FILES             = YES
+SHOW_NAMESPACES        = YES
+FILE_VERSION_FILTER    =
+LAYOUT_FILE            =
+CITE_BIB_FILES         =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+QUIET                  = YES
+WARNINGS               = YES
+WARN_IF_UNDOCUMENTED   = YES
+WARN_IF_DOC_ERROR      = YES
+WARN_NO_PARAMDOC       = YES
+WARN_FORMAT            = "$file:$line: $text"
+WARN_LOGFILE           =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+INPUT                  = $(SRCDIR)
+INPUT_ENCODING         = UTF-8
+FILE_PATTERNS          = *.c *.h
+RECURSIVE              = YES
+EXCLUDE                = config.h test-glue.h
+EXCLUDE_SYMLINKS       = NO
+EXCLUDE_PATTERNS       = */tests/* */libevent/* */build/* *.git *.svn
+EXCLUDE_SYMBOLS        =
+EXAMPLE_PATH           =
+EXAMPLE_PATTERNS       =
+EXAMPLE_RECURSIVE      = NO
+IMAGE_PATH             =
+INPUT_FILTER           =
+FILTER_PATTERNS        =
+FILTER_SOURCE_FILES    = NO
+FILTER_SOURCE_PATTERNS =
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+SOURCE_BROWSER         = NO
+INLINE_SOURCES         = NO
+STRIP_CODE_COMMENTS    = YES
+REFERENCED_BY_RELATION = NO
+REFERENCES_RELATION    = NO
+REFERENCES_LINK_SOURCE = YES
+USE_HTAGS              = NO
+VERBATIM_HEADERS       = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+ALPHABETICAL_INDEX     = YES
+COLS_IN_ALPHA_INDEX    = 5
+IGNORE_PREFIX          =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+GENERATE_HTML          = $(GENERATE_HTML)
+HTML_OUTPUT            = html
+HTML_FILE_EXTENSION    = .html
+HTML_HEADER            =
+HTML_FOOTER            =
+HTML_STYLESHEET        =
+HTML_EXTRA_FILES       =
+HTML_COLORSTYLE_HUE    = 220
+HTML_COLORSTYLE_SAT    = 100
+HTML_COLORSTYLE_GAMMA  = 80
+HTML_TIMESTAMP         = YES
+HTML_DYNAMIC_SECTIONS  = NO
+GENERATE_DOCSET        = NO
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
+DOCSET_PUBLISHER_NAME  = Publisher
+GENERATE_HTMLHELP      = $(GENERATE_CHM)
+CHM_FILE               = ../$(PROJECT).chm
+HHC_LOCATION           = $(HHC_PATH)
+GENERATE_CHI           = $(GENERATE_CHI)
+CHM_INDEX_ENCODING     =
+BINARY_TOC             = NO
+TOC_EXPAND             = NO
+GENERATE_QHP           = NO
+QCH_FILE               =
+QHP_NAMESPACE          = org.doxygen.Project
+QHP_VIRTUAL_FOLDER     = doc
+QHP_CUST_FILTER_NAME   =
+QHP_CUST_FILTER_ATTRS  =
+QHP_SECT_FILTER_ATTRS  =
+QHG_LOCATION           =
+GENERATE_ECLIPSEHELP   = NO
+ECLIPSE_DOC_ID         = org.doxygen.Project
+DISABLE_INDEX          = NO
+GENERATE_TREEVIEW      = NO
+ENUM_VALUES_PER_LINE   = 4
+TREEVIEW_WIDTH         = 250
+EXT_LINKS_IN_WINDOW    = NO
+FORMULA_FONTSIZE       = 10
+FORMULA_TRANSPARENT    = YES
+USE_MATHJAX            = NO
+MATHJAX_RELPATH        = http://www.mathjax.org/mathjax
+MATHJAX_EXTENSIONS     =
+SEARCHENGINE           = YES
+SERVER_BASED_SEARCH    = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+GENERATE_LATEX         = $(GENERATE_LATEX)
+LATEX_OUTPUT           = latex
+LATEX_CMD_NAME         = latex
+MAKEINDEX_CMD_NAME     = makeindex
+COMPACT_LATEX          = NO
+PAPER_TYPE             = $(PAPER_SIZE)
+EXTRA_PACKAGES         =
+LATEX_HEADER           =
+LATEX_FOOTER           =
+PDF_HYPERLINKS         = YES
+USE_PDFLATEX           = YES
+LATEX_BATCHMODE        = YES
+LATEX_HIDE_INDICES     = NO
+LATEX_SOURCE_CODE      = NO
+LATEX_BIB_STYLE        = plain
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+GENERATE_RTF           = $(GENERATE_RTF)
+RTF_OUTPUT             = rtf
+COMPACT_RTF            = NO
+RTF_HYPERLINKS         = YES
+RTF_STYLESHEET_FILE    =
+RTF_EXTENSIONS_FILE    =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+GENERATE_MAN           = $(GENERATE_MAN)
+MAN_OUTPUT             = man
+MAN_EXTENSION          = .1
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+GENERATE_XML           = $(GENERATE_XML)
+XML_OUTPUT             = xml
+XML_SCHEMA             =
+XML_DTD                =
+XML_PROGRAMLISTING     = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+GENERATE_PERLMOD       = NO
+PERLMOD_LATEX          = NO
+PERLMOD_PRETTY         = YES
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+ENABLE_PREPROCESSING   = YES
+MACRO_EXPANSION        = YES
+EXPAND_ONLY_PREDEF     = YES
+SEARCH_INCLUDES        = YES
+INCLUDE_PATH           =
+INCLUDE_FILE_PATTERNS  =
+PREDEFINED             = "TAILQ_ENTRY(t)=struct t" \
+                        "TAILQ_HEAD(t,v)=struct v" \
+                        "SIMPLEQ_ENTRY(t)=struct t" \
+                        "SIMPLEQ_HEAD(t,v)=struct v" \
+                        "__attribute__(x)=" \
+                        DOCSTATIC
+EXPAND_AS_DEFINED      =
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+TAGFILES               =
+GENERATE_TAGFILE       = $(DOCDIR)/$(PROJECT).tag
+ALLEXTERNALS           = NO
+EXTERNAL_GROUPS        = YES
+PERL_PATH              = $(PERL_PATH)
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+CLASS_DIAGRAMS         = YES
+MSCGEN_PATH            =
+HIDE_UNDOC_RELATIONS   = YES
+HAVE_DOT               = $(HAVE_DOT)
+DOT_NUM_THREADS        = 0
+DOT_FONTNAME           = Helvetica
+DOT_FONTSIZE           = 10
+DOT_FONTPATH           =
+CLASS_GRAPH            = YES
+COLLABORATION_GRAPH    = YES
+GROUP_GRAPHS           = YES
+UML_LOOK               = NO
+TEMPLATE_RELATIONS     = NO
+INCLUDE_GRAPH          = YES
+INCLUDED_BY_GRAPH      = YES
+CALL_GRAPH             = NO
+CALLER_GRAPH           = NO
+GRAPHICAL_HIERARCHY    = YES
+DIRECTORY_GRAPH        = YES
+DOT_IMAGE_FORMAT       = svg
+INTERACTIVE_SVG        = YES
+DOT_PATH               = $(DOT_PATH)
+DOTFILE_DIRS           =
+MSCFILE_DIRS           =
+DOT_GRAPH_MAX_NODES    = 50
+MAX_DOT_GRAPH_DEPTH    = 0
+DOT_TRANSPARENT        = NO
+DOT_MULTI_TARGETS      = YES
+GENERATE_LEGEND        = YES
+DOT_CLEANUP            = YES
diff --git a/m4/ax_prog_doxygen.m4 b/m4/ax_prog_doxygen.m4
new file mode 100644 (file)
index 0000000..44b22b0
--- /dev/null
@@ -0,0 +1,532 @@
+# ===========================================================================
+#      http://www.gnu.org/software/autoconf-archive/ax_prog_doxygen.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   DX_INIT_DOXYGEN(PROJECT-NAME, DOXYFILE-PATH, [OUTPUT-DIR])
+#   DX_DOXYGEN_FEATURE(ON|OFF)
+#   DX_DOT_FEATURE(ON|OFF)
+#   DX_HTML_FEATURE(ON|OFF)
+#   DX_CHM_FEATURE(ON|OFF)
+#   DX_CHI_FEATURE(ON|OFF)
+#   DX_MAN_FEATURE(ON|OFF)
+#   DX_RTF_FEATURE(ON|OFF)
+#   DX_XML_FEATURE(ON|OFF)
+#   DX_PDF_FEATURE(ON|OFF)
+#   DX_PS_FEATURE(ON|OFF)
+#
+# DESCRIPTION
+#
+#   The DX_*_FEATURE macros control the default setting for the given
+#   Doxygen feature. Supported features are 'DOXYGEN' itself, 'DOT' for
+#   generating graphics, 'HTML' for plain HTML, 'CHM' for compressed HTML
+#   help (for MS users), 'CHI' for generating a seperate .chi file by the
+#   .chm file, and 'MAN', 'RTF', 'XML', 'PDF' and 'PS' for the appropriate
+#   output formats. The environment variable DOXYGEN_PAPER_SIZE may be
+#   specified to override the default 'a4wide' paper size.
+#
+#   By default, HTML, PDF and PS documentation is generated as this seems to
+#   be the most popular and portable combination. MAN pages created by
+#   Doxygen are usually problematic, though by picking an appropriate subset
+#   and doing some massaging they might be better than nothing. CHM and RTF
+#   are specific for MS (note that you can't generate both HTML and CHM at
+#   the same time). The XML is rather useless unless you apply specialized
+#   post-processing to it.
+#
+#   The macros mainly control the default state of the feature. The use can
+#   override the default by specifying --enable or --disable. The macros
+#   ensure that contradictory flags are not given (e.g.,
+#   --enable-doxygen-html and --enable-doxygen-chm,
+#   --enable-doxygen-anything with --disable-doxygen, etc.) Finally, each
+#   feature will be automatically disabled (with a warning) if the required
+#   programs are missing.
+#
+#   Once all the feature defaults have been specified, call DX_INIT_DOXYGEN
+#   with the following parameters: a one-word name for the project for use
+#   as a filename base etc., an optional configuration file name (the
+#   default is 'Doxyfile', the same as Doxygen's default), and an optional
+#   output directory name (the default is 'doxygen-doc').
+#
+#   Automake Support
+#
+#   The following is a template aminclude.am file for use with Automake.
+#   Make targets and variables values are controlled by the various
+#   DX_COND_* conditionals set by autoconf.
+#
+#   The provided targets are:
+#
+#     doxygen-doc: Generate all doxygen documentation.
+#
+#     doxygen-run: Run doxygen, which will generate some of the
+#                  documentation (HTML, CHM, CHI, MAN, RTF, XML)
+#                  but will not do the post processing required
+#                  for the rest of it (PS, PDF, and some MAN).
+#
+#     doxygen-man: Rename some doxygen generated man pages.
+#
+#     doxygen-ps:  Generate doxygen PostScript documentation.
+#
+#     doxygen-pdf: Generate doxygen PDF documentation.
+#
+#   Note that by default these are not integrated into the automake targets.
+#   If doxygen is used to generate man pages, you can achieve this
+#   integration by setting man3_MANS to the list of man pages generated and
+#   then adding the dependency:
+#
+#     $(man3_MANS): doxygen-doc
+#
+#   This will cause make to run doxygen and generate all the documentation.
+#
+#   The following variable is intended for use in Makefile.am:
+#
+#     DX_CLEANFILES = everything to clean.
+#
+#   Then add this variable to MOSTLYCLEANFILES.
+#
+#     ----- begin aminclude.am -------------------------------------
+#
+#     ## --------------------------------- ##
+#     ## Format-independent Doxygen rules. ##
+#     ## --------------------------------- ##
+#
+#     if DX_COND_doc
+#
+#     ## ------------------------------- ##
+#     ## Rules specific for HTML output. ##
+#     ## ------------------------------- ##
+#
+#     if DX_COND_html
+#
+#     DX_CLEAN_HTML = @DX_DOCDIR@/html
+#
+#     endif DX_COND_html
+#
+#     ## ------------------------------ ##
+#     ## Rules specific for CHM output. ##
+#     ## ------------------------------ ##
+#
+#     if DX_COND_chm
+#
+#     DX_CLEAN_CHM = @DX_DOCDIR@/chm
+#
+#     if DX_COND_chi
+#
+#     DX_CLEAN_CHI = @DX_DOCDIR@/@PACKAGE@.chi
+#
+#     endif DX_COND_chi
+#
+#     endif DX_COND_chm
+#
+#     ## ------------------------------ ##
+#     ## Rules specific for MAN output. ##
+#     ## ------------------------------ ##
+#
+#     if DX_COND_man
+#
+#     DX_CLEAN_MAN = @DX_DOCDIR@/man
+#
+#     endif DX_COND_man
+#
+#     ## ------------------------------ ##
+#     ## Rules specific for RTF output. ##
+#     ## ------------------------------ ##
+#
+#     if DX_COND_rtf
+#
+#     DX_CLEAN_RTF = @DX_DOCDIR@/rtf
+#
+#     endif DX_COND_rtf
+#
+#     ## ------------------------------ ##
+#     ## Rules specific for XML output. ##
+#     ## ------------------------------ ##
+#
+#     if DX_COND_xml
+#
+#     DX_CLEAN_XML = @DX_DOCDIR@/xml
+#
+#     endif DX_COND_xml
+#
+#     ## ----------------------------- ##
+#     ## Rules specific for PS output. ##
+#     ## ----------------------------- ##
+#
+#     if DX_COND_ps
+#
+#     DX_CLEAN_PS = @DX_DOCDIR@/@PACKAGE@.ps
+#
+#     DX_PS_GOAL = doxygen-ps
+#
+#     doxygen-ps: @DX_DOCDIR@/@PACKAGE@.ps
+#
+#     @DX_DOCDIR@/@PACKAGE@.ps: @DX_DOCDIR@/@PACKAGE@.tag
+#         cd @DX_DOCDIR@/latex; \
+#         rm -f *.aux *.toc *.idx *.ind *.ilg *.log *.out; \
+#         $(DX_LATEX) refman.tex; \
+#         $(MAKEINDEX_PATH) refman.idx; \
+#         $(DX_LATEX) refman.tex; \
+#         countdown=5; \
+#         while $(DX_EGREP) 'Rerun (LaTeX|to get cross-references right)' \
+#                           refman.log > /dev/null 2>&1 \
+#            && test $$countdown -gt 0; do \
+#             $(DX_LATEX) refman.tex; \
+#             countdown=`expr $$countdown - 1`; \
+#         done; \
+#         $(DX_DVIPS) -o ../@PACKAGE@.ps refman.dvi
+#
+#     endif DX_COND_ps
+#
+#     ## ------------------------------ ##
+#     ## Rules specific for PDF output. ##
+#     ## ------------------------------ ##
+#
+#     if DX_COND_pdf
+#
+#     DX_CLEAN_PDF = @DX_DOCDIR@/@PACKAGE@.pdf
+#
+#     DX_PDF_GOAL = doxygen-pdf
+#
+#     doxygen-pdf: @DX_DOCDIR@/@PACKAGE@.pdf
+#
+#     @DX_DOCDIR@/@PACKAGE@.pdf: @DX_DOCDIR@/@PACKAGE@.tag
+#         cd @DX_DOCDIR@/latex; \
+#         rm -f *.aux *.toc *.idx *.ind *.ilg *.log *.out; \
+#         $(DX_PDFLATEX) refman.tex; \
+#         $(DX_MAKEINDEX) refman.idx; \
+#         $(DX_PDFLATEX) refman.tex; \
+#         countdown=5; \
+#         while $(DX_EGREP) 'Rerun (LaTeX|to get cross-references right)' \
+#                           refman.log > /dev/null 2>&1 \
+#            && test $$countdown -gt 0; do \
+#             $(DX_PDFLATEX) refman.tex; \
+#             countdown=`expr $$countdown - 1`; \
+#         done; \
+#         mv refman.pdf ../@PACKAGE@.pdf
+#
+#     endif DX_COND_pdf
+#
+#     ## ------------------------------------------------- ##
+#     ## Rules specific for LaTeX (shared for PS and PDF). ##
+#     ## ------------------------------------------------- ##
+#
+#     if DX_COND_latex
+#
+#     DX_CLEAN_LATEX = @DX_DOCDIR@/latex
+#
+#     endif DX_COND_latex
+#
+#     .PHONY: doxygen-run doxygen-doc $(DX_PS_GOAL) $(DX_PDF_GOAL)
+#
+#     .INTERMEDIATE: doxygen-run $(DX_PS_GOAL) $(DX_PDF_GOAL)
+#
+#     doxygen-run: @DX_DOCDIR@/@PACKAGE@.tag
+#
+#     doxygen-doc: doxygen-run $(DX_PS_GOAL) $(DX_PDF_GOAL)
+#
+#     @DX_DOCDIR@/@PACKAGE@.tag: $(DX_CONFIG) $(pkginclude_HEADERS)
+#         rm -rf @DX_DOCDIR@
+#         $(DX_ENV) $(DX_DOXYGEN) $(srcdir)/$(DX_CONFIG)
+#
+#     DX_CLEANFILES = \
+#         @DX_DOCDIR@/@PACKAGE@.tag \
+#         -r \
+#         $(DX_CLEAN_HTML) \
+#         $(DX_CLEAN_CHM) \
+#         $(DX_CLEAN_CHI) \
+#         $(DX_CLEAN_MAN) \
+#         $(DX_CLEAN_RTF) \
+#         $(DX_CLEAN_XML) \
+#         $(DX_CLEAN_PS) \
+#         $(DX_CLEAN_PDF) \
+#         $(DX_CLEAN_LATEX)
+#
+#     endif DX_COND_doc
+#
+#     ----- end aminclude.am ---------------------------------------
+#
+# LICENSE
+#
+#   Copyright (c) 2009 Oren Ben-Kiki <oren@ben-kiki.org>
+#
+#   Copying and distribution of this file, with or without modification, are
+#   permitted in any medium without royalty provided the copyright notice
+#   and this notice are preserved. This file is offered as-is, without any
+#   warranty.
+
+#serial 12
+
+## ----------##
+## Defaults. ##
+## ----------##
+
+DX_ENV=""
+AC_DEFUN([DX_FEATURE_doc],  ON)
+AC_DEFUN([DX_FEATURE_dot],  OFF)
+AC_DEFUN([DX_FEATURE_man],  OFF)
+AC_DEFUN([DX_FEATURE_html], ON)
+AC_DEFUN([DX_FEATURE_chm],  OFF)
+AC_DEFUN([DX_FEATURE_chi],  OFF)
+AC_DEFUN([DX_FEATURE_rtf],  OFF)
+AC_DEFUN([DX_FEATURE_xml],  OFF)
+AC_DEFUN([DX_FEATURE_pdf],  ON)
+AC_DEFUN([DX_FEATURE_ps],   ON)
+
+## --------------- ##
+## Private macros. ##
+## --------------- ##
+
+# DX_ENV_APPEND(VARIABLE, VALUE)
+# ------------------------------
+# Append VARIABLE="VALUE" to DX_ENV for invoking doxygen.
+AC_DEFUN([DX_ENV_APPEND], [AC_SUBST([DX_ENV], ["$DX_ENV $1='$2'"])])
+
+# DX_DIRNAME_EXPR
+# ---------------
+# Expand into a shell expression prints the directory part of a path.
+AC_DEFUN([DX_DIRNAME_EXPR],
+         [[expr ".$1" : '\(\.\)[^/]*$' \| "x$1" : 'x\(.*\)/[^/]*$']])
+
+# DX_IF_FEATURE(FEATURE, IF-ON, IF-OFF)
+# -------------------------------------
+# Expands according to the M4 (static) status of the feature.
+AC_DEFUN([DX_IF_FEATURE], [ifelse(DX_FEATURE_$1, ON, [$2], [$3])])
+
+# DX_REQUIRE_PROG(VARIABLE, PROGRAM)
+# ----------------------------------
+# Require the specified program to be found for the DX_CURRENT_FEATURE to work.
+AC_DEFUN([DX_REQUIRE_PROG], [
+AC_PATH_TOOL([$1], [$2])
+if test "$DX_FLAG_[]DX_CURRENT_FEATURE$$1" = 1; then
+    AC_MSG_WARN([$2 not found - will not DX_CURRENT_DESCRIPTION])
+    AC_SUBST(DX_FLAG_[]DX_CURRENT_FEATURE, 0)
+fi
+])
+
+# DX_TEST_FEATURE(FEATURE)
+# ------------------------
+# Expand to a shell expression testing whether the feature is active.
+AC_DEFUN([DX_TEST_FEATURE], [test "$DX_FLAG_$1" = 1])
+
+# DX_CHECK_DEPEND(REQUIRED_FEATURE, REQUIRED_STATE)
+# -------------------------------------------------
+# Verify that a required features has the right state before trying to turn on
+# the DX_CURRENT_FEATURE.
+AC_DEFUN([DX_CHECK_DEPEND], [
+test "$DX_FLAG_$1" = "$2" \
+|| AC_MSG_ERROR([doxygen-DX_CURRENT_FEATURE ifelse([$2], 1,
+                            requires, contradicts) doxygen-DX_CURRENT_FEATURE])
+])
+
+# DX_CLEAR_DEPEND(FEATURE, REQUIRED_FEATURE, REQUIRED_STATE)
+# ----------------------------------------------------------
+# Turn off the DX_CURRENT_FEATURE if the required feature is off.
+AC_DEFUN([DX_CLEAR_DEPEND], [
+test "$DX_FLAG_$1" = "$2" || AC_SUBST(DX_FLAG_[]DX_CURRENT_FEATURE, 0)
+])
+
+# DX_FEATURE_ARG(FEATURE, DESCRIPTION,
+#                CHECK_DEPEND, CLEAR_DEPEND,
+#                REQUIRE, DO-IF-ON, DO-IF-OFF)
+# --------------------------------------------
+# Parse the command-line option controlling a feature. CHECK_DEPEND is called
+# if the user explicitly turns the feature on (and invokes DX_CHECK_DEPEND),
+# otherwise CLEAR_DEPEND is called to turn off the default state if a required
+# feature is disabled (using DX_CLEAR_DEPEND). REQUIRE performs additional
+# requirement tests (DX_REQUIRE_PROG). Finally, an automake flag is set and
+# DO-IF-ON or DO-IF-OFF are called according to the final state of the feature.
+AC_DEFUN([DX_ARG_ABLE], [
+    AC_DEFUN([DX_CURRENT_FEATURE], [$1])
+    AC_DEFUN([DX_CURRENT_DESCRIPTION], [$2])
+    AC_ARG_ENABLE(doxygen-$1,
+                  [AS_HELP_STRING(DX_IF_FEATURE([$1], [--disable-doxygen-$1],
+                                                      [--enable-doxygen-$1]),
+                                  DX_IF_FEATURE([$1], [don't $2], [$2]))],
+                  [
+case "$enableval" in
+#(
+y|Y|yes|Yes|YES)
+    AC_SUBST([DX_FLAG_$1], 1)
+    $3
+;; #(
+n|N|no|No|NO)
+    AC_SUBST([DX_FLAG_$1], 0)
+;; #(
+*)
+    AC_MSG_ERROR([invalid value '$enableval' given to doxygen-$1])
+;;
+esac
+], [
+AC_SUBST([DX_FLAG_$1], [DX_IF_FEATURE([$1], 1, 0)])
+$4
+])
+if DX_TEST_FEATURE([$1]); then
+    $5
+    :
+fi
+AM_CONDITIONAL(DX_COND_$1, DX_TEST_FEATURE([$1]))
+if DX_TEST_FEATURE([$1]); then
+    $6
+    :
+else
+    $7
+    :
+fi
+])
+
+## -------------- ##
+## Public macros. ##
+## -------------- ##
+
+# DX_XXX_FEATURE(DEFAULT_STATE)
+# -----------------------------
+AC_DEFUN([DX_DOXYGEN_FEATURE], [AC_DEFUN([DX_FEATURE_doc],  [$1])])
+AC_DEFUN([DX_DOT_FEATURE],     [AC_DEFUN([DX_FEATURE_dot], [$1])])
+AC_DEFUN([DX_MAN_FEATURE],     [AC_DEFUN([DX_FEATURE_man],  [$1])])
+AC_DEFUN([DX_HTML_FEATURE],    [AC_DEFUN([DX_FEATURE_html], [$1])])
+AC_DEFUN([DX_CHM_FEATURE],     [AC_DEFUN([DX_FEATURE_chm],  [$1])])
+AC_DEFUN([DX_CHI_FEATURE],     [AC_DEFUN([DX_FEATURE_chi],  [$1])])
+AC_DEFUN([DX_RTF_FEATURE],     [AC_DEFUN([DX_FEATURE_rtf],  [$1])])
+AC_DEFUN([DX_XML_FEATURE],     [AC_DEFUN([DX_FEATURE_xml],  [$1])])
+AC_DEFUN([DX_XML_FEATURE],     [AC_DEFUN([DX_FEATURE_xml],  [$1])])
+AC_DEFUN([DX_PDF_FEATURE],     [AC_DEFUN([DX_FEATURE_pdf],  [$1])])
+AC_DEFUN([DX_PS_FEATURE],      [AC_DEFUN([DX_FEATURE_ps],   [$1])])
+
+# DX_INIT_DOXYGEN(PROJECT, [CONFIG-FILE], [OUTPUT-DOC-DIR])
+# ---------------------------------------------------------
+# PROJECT also serves as the base name for the documentation files.
+# The default CONFIG-FILE is "Doxyfile" and OUTPUT-DOC-DIR is "doxygen-doc".
+AC_DEFUN([DX_INIT_DOXYGEN], [
+
+# Files:
+AC_SUBST([DX_PROJECT], [$1])
+AC_SUBST([DX_CONFIG], [ifelse([$2], [], Doxyfile, [$2])])
+AC_SUBST([DX_DOCDIR], [ifelse([$3], [], doxygen-doc, [$3])])
+
+# Environment variables used inside doxygen.cfg:
+DX_ENV_APPEND(SRCDIR, $srcdir)
+DX_ENV_APPEND(PROJECT, $DX_PROJECT)
+DX_ENV_APPEND(DOCDIR, $DX_DOCDIR)
+DX_ENV_APPEND(VERSION, $PACKAGE_VERSION)
+
+# Doxygen itself:
+DX_ARG_ABLE(doc, [generate any doxygen documentation],
+            [],
+            [],
+            [DX_REQUIRE_PROG([DX_DOXYGEN], doxygen)
+             DX_REQUIRE_PROG([DX_PERL], perl)],
+            [DX_ENV_APPEND(PERL_PATH, $DX_PERL)])
+
+# Dot for graphics:
+DX_ARG_ABLE(dot, [generate graphics for doxygen documentation],
+            [DX_CHECK_DEPEND(doc, 1)],
+            [DX_CLEAR_DEPEND(doc, 1)],
+            [DX_REQUIRE_PROG([DX_DOT], dot)],
+            [DX_ENV_APPEND(HAVE_DOT, YES)
+             DX_ENV_APPEND(DOT_PATH, [`DX_DIRNAME_EXPR($DX_DOT)`])],
+            [DX_ENV_APPEND(HAVE_DOT, NO)])
+
+# Man pages generation:
+DX_ARG_ABLE(man, [generate doxygen manual pages],
+            [DX_CHECK_DEPEND(doc, 1)],
+            [DX_CLEAR_DEPEND(doc, 1)],
+            [],
+            [DX_ENV_APPEND(GENERATE_MAN, YES)],
+            [DX_ENV_APPEND(GENERATE_MAN, NO)])
+
+# RTF file generation:
+DX_ARG_ABLE(rtf, [generate doxygen RTF documentation],
+            [DX_CHECK_DEPEND(doc, 1)],
+            [DX_CLEAR_DEPEND(doc, 1)],
+            [],
+            [DX_ENV_APPEND(GENERATE_RTF, YES)],
+            [DX_ENV_APPEND(GENERATE_RTF, NO)])
+
+# XML file generation:
+DX_ARG_ABLE(xml, [generate doxygen XML documentation],
+            [DX_CHECK_DEPEND(doc, 1)],
+            [DX_CLEAR_DEPEND(doc, 1)],
+            [],
+            [DX_ENV_APPEND(GENERATE_XML, YES)],
+            [DX_ENV_APPEND(GENERATE_XML, NO)])
+
+# (Compressed) HTML help generation:
+DX_ARG_ABLE(chm, [generate doxygen compressed HTML help documentation],
+            [DX_CHECK_DEPEND(doc, 1)],
+            [DX_CLEAR_DEPEND(doc, 1)],
+            [DX_REQUIRE_PROG([DX_HHC], hhc)],
+            [DX_ENV_APPEND(HHC_PATH, $DX_HHC)
+             DX_ENV_APPEND(GENERATE_HTML, YES)
+             DX_ENV_APPEND(GENERATE_HTMLHELP, YES)],
+            [DX_ENV_APPEND(GENERATE_HTMLHELP, NO)])
+
+# Seperate CHI file generation.
+DX_ARG_ABLE(chi, [generate doxygen seperate compressed HTML help index file],
+            [DX_CHECK_DEPEND(chm, 1)],
+            [DX_CLEAR_DEPEND(chm, 1)],
+            [],
+            [DX_ENV_APPEND(GENERATE_CHI, YES)],
+            [DX_ENV_APPEND(GENERATE_CHI, NO)])
+
+# Plain HTML pages generation:
+DX_ARG_ABLE(html, [generate doxygen plain HTML documentation],
+            [DX_CHECK_DEPEND(doc, 1) DX_CHECK_DEPEND(chm, 0)],
+            [DX_CLEAR_DEPEND(doc, 1) DX_CLEAR_DEPEND(chm, 0)],
+            [],
+            [DX_ENV_APPEND(GENERATE_HTML, YES)],
+            [DX_TEST_FEATURE(chm) || DX_ENV_APPEND(GENERATE_HTML, NO)])
+
+# PostScript file generation:
+DX_ARG_ABLE(ps, [generate doxygen PostScript documentation],
+            [DX_CHECK_DEPEND(doc, 1)],
+            [DX_CLEAR_DEPEND(doc, 1)],
+            [DX_REQUIRE_PROG([DX_LATEX], latex)
+             DX_REQUIRE_PROG([DX_MAKEINDEX], makeindex)
+             DX_REQUIRE_PROG([DX_DVIPS], dvips)
+             DX_REQUIRE_PROG([DX_EGREP], egrep)])
+
+# PDF file generation:
+DX_ARG_ABLE(pdf, [generate doxygen PDF documentation],
+            [DX_CHECK_DEPEND(doc, 1)],
+            [DX_CLEAR_DEPEND(doc, 1)],
+            [DX_REQUIRE_PROG([DX_PDFLATEX], pdflatex)
+             DX_REQUIRE_PROG([DX_MAKEINDEX], makeindex)
+             DX_REQUIRE_PROG([DX_EGREP], egrep)])
+
+# LaTeX generation for PS and/or PDF:
+AM_CONDITIONAL(DX_COND_latex, DX_TEST_FEATURE(ps) || DX_TEST_FEATURE(pdf))
+if DX_TEST_FEATURE(ps) || DX_TEST_FEATURE(pdf); then
+    DX_ENV_APPEND(GENERATE_LATEX, YES)
+else
+    DX_ENV_APPEND(GENERATE_LATEX, NO)
+fi
+
+# Paper size for PS and/or PDF:
+AC_ARG_VAR(DOXYGEN_PAPER_SIZE,
+           [a4wide (default), a4, letter, legal or executive])
+case "$DOXYGEN_PAPER_SIZE" in
+#(
+"")
+    AC_SUBST(DOXYGEN_PAPER_SIZE, "")
+;; #(
+a4wide|a4|letter|legal|executive)
+    DX_ENV_APPEND(PAPER_SIZE, $DOXYGEN_PAPER_SIZE)
+;; #(
+*)
+    AC_MSG_ERROR([unknown DOXYGEN_PAPER_SIZE='$DOXYGEN_PAPER_SIZE'])
+;;
+esac
+
+#For debugging:
+#echo DX_FLAG_doc=$DX_FLAG_doc
+#echo DX_FLAG_dot=$DX_FLAG_dot
+#echo DX_FLAG_man=$DX_FLAG_man
+#echo DX_FLAG_html=$DX_FLAG_html
+#echo DX_FLAG_chm=$DX_FLAG_chm
+#echo DX_FLAG_chi=$DX_FLAG_chi
+#echo DX_FLAG_rtf=$DX_FLAG_rtf
+#echo DX_FLAG_xml=$DX_FLAG_xml
+#echo DX_FLAG_pdf=$DX_FLAG_pdf
+#echo DX_FLAG_ps=$DX_FLAG_ps
+#echo DX_ENV=$DX_ENV
+])
index 18191fea492202f0414485f396090ec849c34d62..941bdc3c1486eb17d5cc7d084e518ff0762a9acc 100644 (file)
@@ -68,7 +68,7 @@ accepted:
 The format of
 .Ar location
 is
-.Ar 1:48.85667:N:2.2014:E:117.47:m:1
+.Ar 1:48.85667N:2.2014E:117.47:m:1
 The first digit is always
 .Ar 1 .
 It is followed by the latitude, a letter for the direction (
@@ -309,35 +309,36 @@ Power Device (power consumer)
 .It Ar source
 Valid sources are:
 .Bl -tag -width "X." -compact
-.It Sy 0
+.It Sy unknown
 Unknown
-.It Sy 1
-For PD, the power source is the PSE. For PSE, the power source is the
-primary power source.
-.It Sy 2
-For PD, the power source is a local source. For PSE, the power source
-is the backup power source or a power conservation mode is asked (the
-PSE may be running on UPS for example).
-.It Sy 3
-For PD, the power source is both the PSE and a local source. For PSE,
-this value should not be used.
+.It Sy primary
+For PSE, the power source is the primary power source.
+.It Sy backup
+For PSE, the power source is the backup power source or a power
+conservation mode is asked (the PSE may be running on UPS for
+example).
+.It Sy pse
+For PD, the power source is the PSE.
+.It Sy local
+For PD, the power source is a local source.
+.It Sy both
+For PD, the power source is both the PSE and a local source.
 .El
 .It Ar priority
 Four priorities are available:
 .Bl -tag -width "X." -compact
-.It Sy 0
+.It Sy unknown
 Unknown priority
-.It Sy 1
+.It Sy critical
 Critical
-.It Sy 2
+.It Sy high
 High
-.It Sy 3
+.It Sy low
 Low
 .El
 .It Ar value
-For PD, the power value is the total power in tenth of watts required
-by a PD device from the PSE device. This value should range from 0 to
-1023 tenth of watts.
+For PD, the power value is the total power in milliwatts required
+by a PD device from the PSE device.
 .El
 .It Fl o Ar poe
 Enable the transmission of Dot3 POE-MDI TLV for the given
@@ -381,11 +382,11 @@ Power Sourcing Entity (power provider)
 Power Device (power consumer)
 .El
 .It Ar powerpairs
-Valid sources are:
+Valid values are:
 .Bl -tag -width "X." -compact
-.It Sy 1
+.It Sy signal
 The signal pairs only are in use.
-.It Sy 2
+.It Sy spare
 The spare pairs only are in use.
 .El
 .It Ar class
@@ -425,17 +426,30 @@ given port.
 (and remaining values) are optional. They are only requested in
 conformance with 802.3at.
 .Ar type
-should be either 1 or 2. For the possible values of the next two
-fields, see the possible values of
-.Ar source
-and
-.Ar priority
-for LLDP-MED MDI/POE.
+should be either 1 or 2. For source, use one of the following values:
+Valid sources are:
+.Bl -tag -width "X." -compact
+.It Sy 0
+Unknown
+.It Sy 1
+For PD, the power source is the PSE. For PSE, the power source is the
+primary power source.
+.It Sy 2
+For PD, the power source is a local source. For PSE, the power source
+is the backup power source or a power conservation mode is asked (the
+PSE may be running on UPS for example).
+.It Sy 3
+For PD, the power source is both the PSE and a local source. For PSE,
+this value should not be used.
+.El
+For
+.Ar priority ,
+see what is done for LLDP-MED MDI/POE.
 .Ar requested
 and
 .Ar allocated
 are respectively the PD requested power value and the PSE allocated
-power value. This should be expressed in tenth of watts from 1 to 255.
+power value. This should be expressed in milliwatts.
 .El
 .Sh FILES
 .Bl -tag -width "/var/run/lldpd.socketXX" -compact
index 5107435adebde38796ed9731fccdcde3aa3d9506..c924341f11eea9f09824800d0dd1434e4cee7152 100644 (file)
@@ -1,38 +1,8 @@
-sbin_PROGRAMS = lldpd lldpctl
-noinst_LTLIBRARIES = liblldpd.la libcommon.la
+noinst_LTLIBRARIES = libcommon-daemon-lib.la libcommon-daemon-client.la
+include_HEADERS    = lldp-const.h
 
-## Convenience library for lldpd, lldpctl and tests
-libcommon_la_SOURCES = log.c ctl.c marshal.c marshal.h lldpd.h lldp.h cdp.h compat.h sonmp.h edp.h
-libcommon_la_LIBADD = @LTLIBOBJS@ 
+libcommon_daemon_lib_la_SOURCES = log.c log.h marshal.c marshal.h ctl.c ctl.h lldpd-structs.c lldpd-structs.h lldpd-const.h
+libcommon_daemon_lib_la_LIBADD  = compat/libcompat.la
 
-## Convenience library for lldpd and tests
-liblldpd_la_SOURCES = frame.h frame.c lldpd.c lldp.c cdp.c sonmp.c edp.c \
-       interfaces.c client.c priv.c privsep_fdpass.c dmi.c event.c
-liblldpd_la_CFLAGS = @LIBEVENT_CFLAGS@
-liblldpd_la_LIBADD = libcommon.la @LIBEVENT_LIBS@
-# Add SNMP support if needed
-if USE_SNMP
-liblldpd_la_SOURCES += agent.c agent_priv.c agent.h
-liblldpd_la_CFLAGS += @NETSNMP_CFLAGS@
-liblldpd_la_LIBADD += @NETSNMP_LIBS@
-endif
-
-## lldpd
-lldpd_SOURCES = main.c
-lldpd_LDADD = liblldpd.la @LIBEVENT_LDFLAGS@
-
-## lldpctl
-lldpctl_SOURCES = lldpctl.h lldpctl.c display.c writer.h text_writer.c kv_writer.c
-lldpctl_LDADD = libcommon.la
-if USE_XML
-lldpctl_SOURCES += xml_writer.c
-lldpctl_CFLAGS = @XML2_CFLAGS@
-lldpctl_LDADD  += @XML2_LIBS@
-endif
-
-## libevent
-if LIBEVENT_EMBEDDED
-event.c: $(top_builddir)/libevent/libevent.la
-$(top_builddir)/libevent/libevent.la: $(top_srcdir)/libevent/*.c $(top_srcdir)/libevent/*.h
-       (cd $(top_builddir)/libevent && $(MAKE))
-endif
+libcommon_daemon_client_la_SOURCES = log.c log.h lldpd-const.h
+libcommon_daemon_client_la_LIBADD  = compat/libcompat.la
diff --git a/src/agent.h b/src/agent.h
deleted file mode 100644 (file)
index 8a9f1d2..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef _AGENT_H
-#define _AGENT_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>
-
-static oid lldp_oid[] = {1, 0, 8802, 1, 1, 2};
-size_t agent_lldp_vars_size(void);
-
-#endif
diff --git a/src/client/Makefile.am b/src/client/Makefile.am
new file mode 100644 (file)
index 0000000..896dccd
--- /dev/null
@@ -0,0 +1,10 @@
+sbin_PROGRAMS = lldpctl
+
+lldpctl_SOURCES  = client.h lldpctl.c display.c writer.h text_writer.c kv_writer.c actions.c
+lldpctl_LDADD    = $(top_builddir)/src/libcommon-daemon-client.la $(top_builddir)/src/lib/liblldpctl.la
+
+if USE_XML
+lldpctl_SOURCES += xml_writer.c
+lldpctl_CFLAGS   = @XML2_CFLAGS@
+lldpctl_LDADD   += @XML2_LIBS@
+endif
diff --git a/src/client/actions.c b/src/client/actions.c
new file mode 100644 (file)
index 0000000..da903c5
--- /dev/null
@@ -0,0 +1,374 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <unistd.h>
+#include <string.h>
+
+#include "client.h"
+#include "../log.h"
+
+/* Helpers to parse a ':'-separated string. */
+static char*
+get_next(lldpctl_atom_t *atom, char *string,
+    const char *what, int mandatory)
+{
+       static char *e2 = NULL;
+       static char *e1 = NULL;
+       static char *saved_string = NULL;
+       static int pos;
+       if (e2 != NULL) {
+               *e2 = ':';
+               e1 = e2 + 1;
+       } else if (e1 != NULL) e1 = "";
+       if (e1 == NULL || (saved_string != string)) {
+               e1 = saved_string = string;
+               pos = 1;
+               e2 = NULL;
+       }
+
+
+       if (*e1 == '\0') {
+               if (mandatory)
+                       LLOG_WARNX("unable to find %s in `%s' at pos %d",
+                           what, string, pos);
+               return NULL;
+       }
+       e2 = strchr(e1, ':');
+       if (e2 != NULL) *e2 = '\0';
+       pos++;
+       return e1;
+}
+
+static int
+get_next_and_set(lldpctl_atom_t *atom, char *string,
+    const char *what, lldpctl_key_t key, int mandatory)
+{
+       char *e1 = get_next(atom, string, what, mandatory);
+       if (e1 == NULL) return -1;
+       if (lldpctl_atom_set_str(atom, key, e1) == NULL) {
+               LLOG_WARNX("unable to set %s. %s.", what,
+                       lldpctl_last_strerror(lldpctl_atom_get_connection(atom)));
+               return 0;
+       }
+       return 1;
+}
+
+/**
+ * Parse dot3 power string.
+ *
+ * @param value String describing the new value.
+ * @param power Atom to use to insert new values.
+ * @return 1 on success, 0 otherwise.
+ */
+static int
+parse_dot3_power(char *value, lldpctl_atom_t *power)
+{
+       int rc = 0;
+
+       if (get_next_and_set(power, value, "device type", lldpctl_k_dot3_power_devicetype, 1) != 1)
+               return 0;
+       if (get_next_and_set(power, value, "power support", lldpctl_k_dot3_power_supported, 1) != 1)
+               return 0;
+       if (get_next_and_set(power, value, "power enableness", lldpctl_k_dot3_power_enabled, 1) != 1)
+               return 0;
+       if (get_next_and_set(power, value, "pair control ability", lldpctl_k_dot3_power_paircontrol, 1) != 1)
+               return 0;
+       if (get_next_and_set(power, value, "power pairs", lldpctl_k_dot3_power_pairs, 1) != 1)
+               return 0;
+       if (get_next_and_set(power, value, "class", lldpctl_k_dot3_power_class, 1) != 1)
+               return 0;
+       rc = get_next_and_set(power, value, "power type", lldpctl_k_dot3_power_type, 0);
+       if (rc == 0) return 0;
+       if (rc == -1) return 1;
+
+       if (get_next_and_set(power, value, "power source", lldpctl_k_dot3_power_source, 1) != 1)
+               return 0;
+       if (get_next_and_set(power, value, "power priority", lldpctl_k_dot3_power_priority, 1) != 1)
+               return 0;
+       if (get_next_and_set(power, value, "power requested", lldpctl_k_dot3_power_requested, 1) != 1)
+               return 0;
+       if (get_next_and_set(power, value, "power allocated", lldpctl_k_dot3_power_allocated, 1) != 1)
+               return 0;
+
+       return 1;
+}
+
+/**
+ * Parse LLDP-MED power string.
+ *
+ * @param value String describing the new value.
+ * @param power Atom to use to insert new values.
+ * @return 1 on success, 0 otherwise.
+ */
+static int
+parse_med_power(char *value, lldpctl_atom_t *power)
+{
+       if (get_next_and_set(power, value, "device type", lldpctl_k_med_power_type, 1) != 1)
+               return 0;
+       if (get_next_and_set(power, value, "power source", lldpctl_k_med_power_source, 1) != 1)
+               return 0;
+       if (get_next_and_set(power, value, "power priority", lldpctl_k_med_power_priority, 1) != 1)
+               return 0;
+       if (get_next_and_set(power, value, "power value", lldpctl_k_med_power_val, 1) != 1)
+               return 0;
+
+       return 1;
+}
+
+/**
+ * Parse LLDP-MED policy string.
+ *
+ * @param value String describing the new value.
+ * @param power Atom to use to insert new values.
+ * @return 1 on success, 0 otherwise.
+ */
+static int
+parse_med_policy(char *value, lldpctl_atom_t *policy)
+{
+       if (get_next_and_set(policy, value, "application type", lldpctl_k_med_policy_type, 1) != 1)
+               return 0;
+       if (get_next_and_set(policy, value, "unknown flag", lldpctl_k_med_policy_unknown, 1) != 1)
+               return 0;
+       if (get_next_and_set(policy, value, "tagged flag", lldpctl_k_med_policy_tagged, 1) != 1)
+               return 0;
+       if (get_next_and_set(policy, value, "VLAN ID", lldpctl_k_med_policy_vid, 1) != 1)
+               return 0;
+       if (get_next_and_set(policy, value, "Layer 2 priority", lldpctl_k_med_policy_priority, 1) != 1)
+               return 0;
+       if (get_next_and_set(policy, value, "DSCP", lldpctl_k_med_policy_dscp, 1) != 1)
+               return 0;
+
+       return 1;
+}
+
+/**
+ * Parse LLDP-MED location string.
+ *
+ * @param value String describing the new value.
+ * @param power Atom to use to insert new values.
+ * @return 1 on success, 0 otherwise.
+ */
+static int
+parse_med_location(char *value, lldpctl_atom_t *location)
+{
+       int format, stop = 0;
+       lldpctl_atom_t *cael, *caels;
+       char *type;
+
+       if (get_next_and_set(location, value, "location format", lldpctl_k_med_location_format, 1) != 1)
+               return 0;
+       format = lldpctl_atom_get_int(location, lldpctl_k_med_location_format);
+       switch (format) {
+       case LLDP_MED_LOCFORMAT_COORD:
+               if (get_next_and_set(location, value, "latitude", lldpctl_k_med_location_latitude, 1) != 1)
+                       return 0;
+               if (get_next_and_set(location, value, "longitude", lldpctl_k_med_location_longitude, 1) != 1)
+                       return 0;
+               if (get_next_and_set(location, value, "altitude", lldpctl_k_med_location_altitude, 1) != 1)
+                       return 0;
+               if (get_next_and_set(location, value, "altitude unit", lldpctl_k_med_location_altitude_unit, 1) != 1)
+                       return 0;
+               if (get_next_and_set(location, value, "datum", lldpctl_k_med_location_geoid, 1) != 1)
+                       return 0;
+               return 1;
+       case LLDP_MED_LOCFORMAT_CIVIC:
+               if (get_next_and_set(location, value, "country", lldpctl_k_med_location_country, 1) != 1)
+                       return 0;
+               while ((type = get_next(location, value, "civic address type", 0)) != NULL &&
+                   !stop) {
+                       /* Next we have the element addresses */
+                       caels = lldpctl_atom_get(location, lldpctl_k_med_location_ca_elements);
+                       cael = lldpctl_atom_create(caels);
+
+                       if (lldpctl_atom_set_str(cael, lldpctl_k_med_civicaddress_type, type) != NULL) {
+                               if (get_next_and_set(cael, value, "civic address value",
+                                       lldpctl_k_med_civicaddress_value, 1) == 1) {
+                                       if (lldpctl_atom_set(location, lldpctl_k_med_location_ca_elements,
+                                               cael) == NULL) {
+                                               LLOG_WARNX("unable to add a civic address element. %s",
+                                                   lldpctl_last_strerror(lldpctl_atom_get_connection(location)));
+                                               stop = 1;
+                                       }
+                               } else stop = 1;
+                       } else {
+                               LLOG_WARNX("unable to set civic address type. %s.",
+                                   lldpctl_last_strerror(lldpctl_atom_get_connection(cael)));
+                               stop = 1;
+                       }
+
+                       lldpctl_atom_dec_ref(cael);
+                       lldpctl_atom_dec_ref(caels);
+               }
+               if (stop) return 0;
+               return 1;
+       case LLDP_MED_LOCFORMAT_ELIN:
+               if (get_next_and_set(location, value, "ELIN number", lldpctl_k_med_location_elin, 1) != 1)
+                       return 0;
+               return 1;
+       default:
+               LLOG_WARNX("unable to determine the requested location format");
+               return 0;
+       }
+
+       return 1;
+}
+
+/**
+ * Modify the interfaces specified on the command line.
+ *
+ * @param conn    Connection to lldpd.
+ * @param argc    Number of arguments.
+ * @param argv    Array of arguments.
+ * @param ifindex Index of the first non optional argument
+ */
+void
+modify_interfaces(lldpctl_conn_t *conn,
+    int argc, char **argv, int ifindex)
+{
+       int i, ch;
+       const char *iface_name;
+       lldpctl_atom_t *ifaces, *iface;
+       lldpctl_atom_t *port;
+
+       ifaces = lldpctl_get_interfaces(conn);
+       if (!ifaces) {
+               LLOG_WARNX("not able to get the list of interfaces: %s", lldpctl_strerror(lldpctl_last_error(conn)));
+               return;
+       }
+
+       lldpctl_atom_foreach(ifaces, iface) {
+               /* Only process specified interfaces or all interfaces if none
+                * is specified. */
+               iface_name = lldpctl_atom_get_str(iface,
+                   lldpctl_k_interface_name);
+               if (ifindex < argc) {
+                       for (i = ifindex; i < argc; i++)
+                               if (strcmp(argv[i],
+                                       iface_name) == 0)
+                                       break;
+                       if (i == argc)
+                               continue;
+               }
+
+               port      = lldpctl_get_port(iface);
+
+               optind = 1;
+               while ((ch = getopt(argc, argv, LLDPCTL_ARGS)) != -1) {
+                       lldpctl_atom_t *dot3_power;
+                       lldpctl_atom_t *med_power;
+                       lldpctl_atom_t *med_policy, *med_policies;
+                       lldpctl_atom_t *med_location, *med_locations;
+
+                       switch (ch) {
+                       case 'o':
+                               /* Dot3 power */
+                               dot3_power = lldpctl_atom_get(port, lldpctl_k_port_dot3_power);
+                               if (dot3_power == NULL) {
+                                       LLOG_WARNX("unable to set Dot3 power: support seems unavailable");
+                                       break;
+                               }
+                               if (parse_dot3_power(optarg, dot3_power)) {
+                                       if (lldpctl_atom_set(port, lldpctl_k_port_dot3_power,
+                                               dot3_power) == NULL)
+                                               LLOG_WARNX("unable to set Dot3 power. %s",
+                                                   lldpctl_strerror(lldpctl_last_error(conn)));
+                                       else
+                                               LLOG_INFO("Dot3 power has been set for port %s",
+                                                   iface_name);
+                               }
+                               lldpctl_atom_dec_ref(dot3_power);
+                               break;
+                       case 'O':
+                               /* LLDP-MED power */
+                               med_power = lldpctl_atom_get(port, lldpctl_k_port_med_power);
+                               if (med_power == NULL) {
+                                       LLOG_WARNX("unable to set LLDP-MED power: support seems unavailable");
+                                       break;
+                               }
+                               if (parse_med_power(optarg, med_power)) {
+                                       if (lldpctl_atom_set(port, lldpctl_k_port_med_power,
+                                               med_power) == NULL)
+                                               LLOG_WARNX("unable to set LLDP-MED power. %s",
+                                                   lldpctl_strerror(lldpctl_last_error(conn)));
+                                       else
+                                               LLOG_INFO("LLDP-MED power has been set for port %s",
+                                                   iface_name);
+                               }
+                               lldpctl_atom_dec_ref(med_power);
+                               break;
+                       case 'P':
+                               /* LLDP-MED network policy */
+                               med_policies = lldpctl_atom_get(port, lldpctl_k_port_med_policies);
+                               if (med_policies == NULL) {
+                                       LLOG_WARNX("unable to set LLDP-MED policy: support seems unavailable");
+                                       break;
+                               }
+                               /* We select the first policy. Since we will
+                                * modify the application type, it is not
+                                * necessary to select the one with the
+                                * appropriate index. */
+                               med_policy = lldpctl_atom_iter_value(med_policies,
+                                       lldpctl_atom_iter_next(med_policies,
+                                           lldpctl_atom_iter(med_policies)));
+                               if (parse_med_policy(optarg, med_policy)) {
+                                       if (lldpctl_atom_set(port, lldpctl_k_port_med_policies,
+                                               med_policy) == NULL)
+                                               LLOG_WARNX("unable to set LLDP-MED policy. %s",
+                                                   lldpctl_strerror(lldpctl_last_error(conn)));
+                                       else
+                                               LLOG_INFO("LLDP-MED policy has been set for port %s",
+                                                   iface_name);
+                               }
+                               lldpctl_atom_dec_ref(med_policy);
+                               lldpctl_atom_dec_ref(med_policies);
+                               break;
+                       case 'L':
+                               /* LLDP-MED location */
+                               med_locations = lldpctl_atom_get(port, lldpctl_k_port_med_locations);
+                               if (med_locations == NULL) {
+                                       LLOG_WARNX("unable to set LLDP-MED location: support seems unavailable");
+                                       break;
+                               }
+                               /* As for policy, we pick the first and it will
+                                * be reset when setting the format. No need to
+                                * pick the one with the appropriate index. */
+                               med_location = lldpctl_atom_iter_value(med_locations,
+                                   lldpctl_atom_iter_next(med_locations,
+                                       lldpctl_atom_iter(med_locations)));
+                               if (parse_med_location(optarg, med_location)) {
+                                       if (lldpctl_atom_set(port, lldpctl_k_port_med_locations,
+                                               med_location) == NULL)
+                                               LLOG_WARNX("unable to set LLDP-MED location. %s",
+                                                       lldpctl_strerror(lldpctl_last_error(conn)));
+                                       else
+                                               LLOG_INFO("LLDP-MED location has been set for port %s",
+                                                   iface_name);
+                               }
+                               lldpctl_atom_dec_ref(med_location);
+                               lldpctl_atom_dec_ref(med_locations);
+                               break;
+                       default:
+                               /* We shouldn't be here... */
+                               break;
+                       }
+               }
+
+               lldpctl_atom_dec_ref(port);
+       }
+
+       lldpctl_atom_dec_ref(ifaces);
+}
similarity index 69%
rename from src/lldpctl.h
rename to src/client/client.h
index c66a877f5b5f9b4d0eebed567d730695235d5c60..61f55f30212373cb3c4b8ab074740185c38fe4ee 100644 (file)
@@ -1,3 +1,4 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
 /*
  * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx>
  *
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#ifndef _LLDPCTL_H
-#define _LLDPCTL_H
+#ifndef _CLIENT_H
+#define _CLIENT_H
 
-#include "lldpd.h"
+#if HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include "../lib/lldpctl.h"
+#include "../lldp-const.h"
 #include "writer.h"
 
-/* lldpctl.c */
-struct lldpd_interface_list *get_interfaces(int);
-int  get_port(int, struct lldpd_port *, char *);
-struct lldpd_hardware *get_interface(int, char *);
+#define LLDPCTL_ARGS "hdvaf:L:P:O:o:"
 
 /* display.c */
-void display_interfaces(int, const char *, int, int, char **);
+void display_interfaces(lldpctl_conn_t *, const char *, int, int, char **);
+
+/* actions.c */
+void modify_interfaces(lldpctl_conn_t *, int, char **, int);
 
 #endif
diff --git a/src/client/display.c b/src/client/display.c
new file mode 100644 (file)
index 0000000..524f57b
--- /dev/null
@@ -0,0 +1,589 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <time.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <arpa/inet.h>
+
+#include "../log.h"
+#include "client.h"
+
+static void
+display_cap(struct writer * w, lldpctl_atom_t *chassis, u_int8_t bit, char *symbol)
+{
+       if (lldpctl_atom_get_int(chassis, lldpctl_k_chassis_cap_available) & bit) {
+               tag_start(w, "capability", "Capability");
+               tag_attr (w, "type", "", symbol );
+               tag_attr (w, "enabled", "",
+                   (lldpctl_atom_get_int(chassis, lldpctl_k_chassis_cap_enabled) & bit)?
+                   "on":"off");
+               tag_end  (w);
+       }
+}
+
+static void
+display_med_capability(struct writer *w, long int available, int cap,
+    const char *symbol)
+{
+       if (available & cap) {
+               tag_start(w, "capability", "Capability");
+               tag_attr(w, "type", "", symbol);
+               tag_end(w);
+       }
+}
+
+static char*
+totag(const char *value)
+{
+       int i;
+       static char *result = NULL;
+       free(result); result = NULL;
+       if (!value) return "none";
+       result = calloc(1, strlen(value));
+       if (!result) return "none";
+       for (i = 0; i < strlen(value); i++) {
+               switch (value[i]) {
+               case ' ': result[i] = '-'; break;
+               default: result[i] = tolower(value[i]); break;
+               }
+       }
+       return result;
+}
+
+static void
+display_med(struct writer *w, lldpctl_atom_t *port)
+{
+       lldpctl_atom_t *medpolicies, *medpolicy;
+       lldpctl_atom_t *medlocations, *medlocation;
+       lldpctl_atom_t *caelements, *caelement;
+       long int cap = lldpctl_atom_get_int(port, lldpctl_k_chassis_med_cap);
+       const char *type;
+
+       if (lldpctl_atom_get_int(port, lldpctl_k_chassis_med_type) <= 0)
+               return;
+
+       tag_start(w, "lldp-med", "LLDP-MED");
+
+       tag_datatag(w, "device-type", "Device Type",
+           lldpctl_atom_get_str(port, lldpctl_k_chassis_med_type));
+
+       display_med_capability(w, cap, LLDP_MED_CAP_CAP, "Capabilities");
+       display_med_capability(w, cap, LLDP_MED_CAP_POLICY, "Policy");
+       display_med_capability(w, cap, LLDP_MED_CAP_LOCATION, "Location");
+       display_med_capability(w, cap, LLDP_MED_CAP_MDI_PSE, "MDI/PSE");
+       display_med_capability(w, cap, LLDP_MED_CAP_MDI_PD, "MDI/PD");
+       display_med_capability(w, cap, LLDP_MED_CAP_IV, "Inventory");
+
+       /* LLDP MED policies */
+       medpolicies = lldpctl_atom_get(port, lldpctl_k_port_med_policies);
+       lldpctl_atom_foreach(medpolicies, medpolicy) {
+               if (lldpctl_atom_get_int(medpolicy,
+                       lldpctl_k_med_policy_type) <= 0) continue;
+
+               tag_start(w, "policy", "LLDP-MED Network Policy for");
+               tag_attr(w, "apptype", "", lldpctl_atom_get_str(medpolicy, lldpctl_k_med_policy_type));
+               tag_attr(w, "defined", "Defined",
+                   (lldpctl_atom_get_int(medpolicy,
+                       lldpctl_k_med_policy_unknown) > 0)?"no":"yes");
+
+               if (lldpctl_atom_get_int(medpolicy,
+                       lldpctl_k_med_policy_tagged) > 0) {
+                       int vid = lldpctl_atom_get_int(medpolicy,
+                           lldpctl_k_med_policy_vid);
+                       tag_start(w, "vlan", "VLAN");
+                       if (vid == 0) {
+                               tag_attr(w, "vid", "", "priority");
+                       } else if (vid == 4095) {
+                               tag_attr(w, "vid", "", "reserved");
+                       } else {
+                               tag_attr(w, "vid", "",
+                                   lldpctl_atom_get_str(medpolicy,
+                                       lldpctl_k_med_policy_vid));
+                       }
+                       tag_end(w);
+               }
+
+               tag_datatag(w, "priority", "Priority",
+                   lldpctl_atom_get_str(medpolicy,
+                       lldpctl_k_med_policy_priority));
+               tag_datatag(w, "dscp", "DSCP Value",
+                   lldpctl_atom_get_str(medpolicy,
+                       lldpctl_k_med_policy_dscp));
+
+               tag_end(w);
+       }
+       lldpctl_atom_dec_ref(medpolicies);
+
+       /* LLDP MED locations */
+       medlocations = lldpctl_atom_get(port, lldpctl_k_port_med_locations);
+       lldpctl_atom_foreach(medlocations, medlocation) {
+               int format = lldpctl_atom_get_int(medlocation,
+                   lldpctl_k_med_location_format);
+               if (format <= 0) continue;
+               tag_start(w, "location", "LLDP-MED Location Identification");
+               tag_attr(w, "type", "Type",
+                   lldpctl_atom_get_str(medlocation,
+                       lldpctl_k_med_location_format));
+
+               switch (format) {
+               case LLDP_MED_LOCFORMAT_COORD:
+                       tag_attr(w, "geoid", "Geoid",
+                           lldpctl_atom_get_str(medlocation,
+                               lldpctl_k_med_location_geoid));
+                       tag_datatag(w, "lat", "Latitude",
+                           lldpctl_atom_get_str(medlocation,
+                               lldpctl_k_med_location_latitude));
+                       tag_datatag(w, "lon", "Longitude",
+                           lldpctl_atom_get_str(medlocation,
+                               lldpctl_k_med_location_longitude));
+                       tag_start(w, "altitude", "Altitude");
+                       tag_attr(w, "unit", "", lldpctl_atom_get_str(medlocation,
+                               lldpctl_k_med_location_altitude_unit));
+                       tag_data(w, lldpctl_atom_get_str(medlocation,
+                               lldpctl_k_med_location_altitude));
+                       tag_end(w);
+                       break;
+               case LLDP_MED_LOCFORMAT_CIVIC:
+                       tag_datatag(w, "country", "Country",
+                           lldpctl_atom_get_str(medlocation,
+                               lldpctl_k_med_location_country));
+                       caelements = lldpctl_atom_get(medlocation,
+                           lldpctl_k_med_location_ca_elements);
+                       lldpctl_atom_foreach(caelements, caelement) {
+                               type = lldpctl_atom_get_str(caelement,
+                                   lldpctl_k_med_civicaddress_type);
+                               tag_datatag(w, totag(type), type,
+                                   lldpctl_atom_get_str(caelement,
+                                       lldpctl_k_med_civicaddress_value));
+                       }
+                       lldpctl_atom_dec_ref(caelements);
+                       break;
+               case LLDP_MED_LOCFORMAT_ELIN:
+                       tag_datatag(w, "ecs", "ECS ELIN",
+                           lldpctl_atom_get_str(medlocation,
+                               lldpctl_k_med_location_elin));
+                       break;
+               }
+
+               tag_end(w);
+       }
+       lldpctl_atom_dec_ref(medlocations);
+
+       /* LLDP MED power */
+       if (lldpctl_atom_get_int(port, lldpctl_k_med_power_type) > 0) {
+               tag_start(w, "poe", "Extended Power-over-Ethernet");
+
+               tag_datatag(w, "device-type", "Power Type & Source",
+                   lldpctl_atom_get_str(port, lldpctl_k_med_power_type));
+               tag_datatag(w, "source", "Power Source",
+                   lldpctl_atom_get_str(port, lldpctl_k_med_power_source));
+               tag_datatag(w, "priority", "Power priority",
+                   lldpctl_atom_get_str(port, lldpctl_k_med_power_priority));
+               tag_datatag(w, "power", "Power Value",
+                   lldpctl_atom_get_str(port, lldpctl_k_med_power_val));
+       }
+
+       /* LLDP MED inventory */
+       do {
+               const char *hw = lldpctl_atom_get_str(port,
+                   lldpctl_k_chassis_med_inventory_hw);
+               const char *sw = lldpctl_atom_get_str(port,
+                   lldpctl_k_chassis_med_inventory_sw);
+               const char *fw = lldpctl_atom_get_str(port,
+                   lldpctl_k_chassis_med_inventory_fw);
+               const char *sn = lldpctl_atom_get_str(port,
+                   lldpctl_k_chassis_med_inventory_sn);
+               const char *manuf = lldpctl_atom_get_str(port,
+                   lldpctl_k_chassis_med_inventory_manuf);
+               const char *model = lldpctl_atom_get_str(port,
+                   lldpctl_k_chassis_med_inventory_model);
+               const char *asset = lldpctl_atom_get_str(port,
+                   lldpctl_k_chassis_med_inventory_asset);
+               if (!(hw || sw || fw || sn ||
+                       manuf || model || asset)) break;
+
+               tag_start(w, "inventory", "Inventory");
+               tag_datatag(w, "hardware", "Hardware Revision", hw);
+               tag_datatag(w, "software", "Software Revision", sw);
+               tag_datatag(w, "firmware", "Firmware Revision", fw);
+               tag_datatag(w, "serial", "Serial Number", sn);
+               tag_datatag(w, "manufacturer", "Manufacturer", manuf);
+               tag_datatag(w, "model", "Model", model);
+               tag_datatag(w, "asset", "Asset ID", asset);
+               tag_end(w);
+       } while(0);
+
+       tag_end(w);
+}
+
+static void
+display_chassis(struct writer* w, lldpctl_atom_t* neighbor)
+{
+       lldpctl_atom_t *mgmts, *mgmt;
+
+       tag_start(w, "chassis", "Chassis");
+       tag_start(w, "id", "ChassisID");
+       tag_attr (w, "type", "",
+           lldpctl_atom_get_str(neighbor,
+               lldpctl_k_chassis_id_subtype));
+       tag_data(w, lldpctl_atom_get_str(neighbor,
+               lldpctl_k_chassis_id));
+       tag_end(w);
+       tag_datatag(w, "name", "SysName",
+           lldpctl_atom_get_str(neighbor, lldpctl_k_chassis_name));
+       tag_datatag(w, "descr", "SysDescr",
+           lldpctl_atom_get_str(neighbor, lldpctl_k_chassis_descr));
+
+       /* Management addresses */
+       mgmts = lldpctl_atom_get(neighbor, lldpctl_k_chassis_mgmt);
+       lldpctl_atom_foreach(mgmts, mgmt) {
+               tag_datatag(w, "mgmt-ip", "MgmtIP",
+                   lldpctl_atom_get_str(mgmt, lldpctl_k_mgmt_ip));
+       }
+       lldpctl_atom_dec_ref(mgmts);
+
+       /* Capabilities */
+       display_cap(w, neighbor, LLDP_CAP_OTHER, "Other");
+       display_cap(w, neighbor, LLDP_CAP_REPEATER, "Repeater");
+       display_cap(w, neighbor, LLDP_CAP_BRIDGE, "Bridge");
+       display_cap(w, neighbor, LLDP_CAP_ROUTER, "Router");
+       display_cap(w, neighbor, LLDP_CAP_WLAN, "Wlan");
+       display_cap(w, neighbor, LLDP_CAP_TELEPHONE, "Tel");
+       display_cap(w, neighbor, LLDP_CAP_DOCSIS, "Docsis");
+       display_cap(w, neighbor, LLDP_CAP_STATION, "Station");
+
+       tag_end(w);
+}
+
+static void
+display_autoneg(struct writer * w, int advertised, int bithd, int bitfd, char *desc)
+{
+       if (!((advertised & bithd) ||
+               (advertised & bitfd)))
+               return;
+
+       tag_start(w, "advertised", "Adv");
+       tag_attr(w, "type", "", desc);
+       tag_attr(w, "hd", "HD", (advertised & bithd)?"yes":"no");
+       tag_attr(w, "fd", "FD", (advertised)?"yes":"no");
+       tag_end (w);
+}
+
+static void
+display_port(struct writer *w, lldpctl_atom_t *port)
+{
+       tag_start(w, "port", "Port");
+       tag_start(w, "id", "PortID");
+       tag_attr (w, "type", "",
+           lldpctl_atom_get_str(port, lldpctl_k_port_id_subtype));
+       tag_data(w, lldpctl_atom_get_str(port, lldpctl_k_port_id));
+       tag_end(w);
+
+       tag_datatag(w, "descr", "PortDescr",
+           lldpctl_atom_get_str(port, lldpctl_k_port_descr));
+
+       /* Dot3 */
+       tag_datatag(w, "mfs", "MFS",
+           lldpctl_atom_get_str(port, lldpctl_k_port_dot3_mfs));
+       tag_datatag(w, "aggregation", " Port is aggregated. PortAggregID",
+           lldpctl_atom_get_str(port, lldpctl_k_port_dot3_aggregid));
+
+       do {
+               long int autoneg_support, autoneg_enabled, autoneg_advertised;
+               autoneg_support = lldpctl_atom_get_int(port,
+                   lldpctl_k_port_dot3_autoneg_support);
+               autoneg_enabled = lldpctl_atom_get_int(port,
+                   lldpctl_k_port_dot3_autoneg_enabled);
+               autoneg_advertised = lldpctl_atom_get_int(port,
+                   lldpctl_k_port_dot3_autoneg_advertised);
+               if (autoneg_support > 0 || autoneg_enabled > 0) {
+                       tag_start(w, "auto-negotiation", "PMD autoneg");
+                       tag_attr (w, "supported", "supported",
+                           (autoneg_support > 0)?"yes":"no");
+                       tag_attr (w, "enabled", "enabled",
+                           (autoneg_enabled > 0)?"yes":"no");
+
+                       if (autoneg_enabled > 0) {
+                               if (autoneg_advertised < 0)
+                                       autoneg_advertised = 0;
+                               display_autoneg(w, autoneg_advertised,
+                                   LLDP_DOT3_LINK_AUTONEG_10BASE_T,
+                                   LLDP_DOT3_LINK_AUTONEG_10BASET_FD,
+                                   "10Base-T");
+                               display_autoneg(w, autoneg_advertised,
+                                   LLDP_DOT3_LINK_AUTONEG_100BASE_TX,
+                                   LLDP_DOT3_LINK_AUTONEG_100BASE_TXFD,
+                                   "100Base-T");
+                               display_autoneg(w, autoneg_advertised,
+                                   LLDP_DOT3_LINK_AUTONEG_100BASE_T2,
+                                   LLDP_DOT3_LINK_AUTONEG_100BASE_T2FD,
+                                   "100Base-T2");
+                               display_autoneg(w, autoneg_advertised,
+                                   LLDP_DOT3_LINK_AUTONEG_1000BASE_X,
+                                   LLDP_DOT3_LINK_AUTONEG_1000BASE_XFD,
+                                   "100Base-X");
+                               display_autoneg(w, autoneg_advertised,
+                                   LLDP_DOT3_LINK_AUTONEG_1000BASE_T,
+                                   LLDP_DOT3_LINK_AUTONEG_1000BASE_TFD,
+                                   "1000Base-T");
+                       }
+                       tag_datatag(w, "current", "MAU oper type",
+                           lldpctl_atom_get_str(port, lldpctl_k_port_dot3_mautype));
+                       tag_end(w);
+               }
+       } while (0);
+
+       do {
+               lldpctl_atom_t *dot3_power = lldpctl_atom_get(port,
+                   lldpctl_k_port_dot3_power);
+               int devicetype = lldpctl_atom_get_int(dot3_power,
+                   lldpctl_k_dot3_power_devicetype);
+               if (devicetype > 0) {
+                       tag_start(w, "power", "MDI Power");
+                       tag_attr(w, "supported", "supported",
+                           (lldpctl_atom_get_int(dot3_power,
+                               lldpctl_k_dot3_power_supported) > 0)?"yes":"no");
+                       tag_attr(w, "enabled", "enabled",
+                           (lldpctl_atom_get_int(dot3_power,
+                               lldpctl_k_dot3_power_enabled) > 0)?"yes":"no");
+                       tag_attr(w, "paircontrol", "pair control",
+                           (lldpctl_atom_get_int(dot3_power,
+                               lldpctl_k_dot3_power_paircontrol) > 0)?"yes":"no");
+                       tag_start(w, "device-type", "Device type");
+                       tag_data(w, lldpctl_atom_get_str(dot3_power,
+                               lldpctl_k_dot3_power_devicetype));;
+                       tag_end(w);
+                       tag_start(w, "pairs", "Power pairs");
+                       tag_data(w, lldpctl_atom_get_str(dot3_power,
+                               lldpctl_k_dot3_power_pairs));
+                       tag_end(w);
+                       tag_start(w, "class", "Class");
+                       tag_data(w, lldpctl_atom_get_str(dot3_power,
+                               lldpctl_k_dot3_power_class));
+                       tag_end(w);
+
+                       /* 802.3at */
+                       if (lldpctl_atom_get_int(dot3_power,
+                               lldpctl_k_dot3_power_type) > LLDP_DOT3_POWER_8023AT_OFF) {
+                               tag_start(w, "power-type", "Power type");
+                               tag_data(w, lldpctl_atom_get_str(dot3_power,
+                                       lldpctl_k_dot3_power_type));
+                               tag_end(w);
+
+                               tag_start(w, "source", "Power Source");
+                               tag_data(w, lldpctl_atom_get_str(dot3_power,
+                                       lldpctl_k_dot3_power_source));
+                               tag_end(w);
+
+                               tag_start(w, "priority", "Power Priority");
+                               tag_data(w, lldpctl_atom_get_str(dot3_power,
+                                       lldpctl_k_dot3_power_priority));
+                               tag_end(w);
+
+                               tag_start(w, "requested", "PD requested power Value");
+                               tag_data(w, lldpctl_atom_get_str(dot3_power,
+                                       lldpctl_k_dot3_power_requested));
+                               tag_end(w);
+
+                               tag_start(w, "allocated", "PSE allocated power Value");
+                               tag_data(w, lldpctl_atom_get_str(dot3_power,
+                                       lldpctl_k_dot3_power_allocated));
+                               tag_end(w);
+                       }
+
+                       tag_end(w);
+               }
+               lldpctl_atom_dec_ref(dot3_power);
+       } while(0);
+
+       tag_end(w);
+}
+
+static void
+display_vlans(struct writer *w, lldpctl_atom_t *port)
+{
+       lldpctl_atom_t *vlans, *vlan;
+       int foundpvid = 0;
+       int pvid, vid;
+
+       pvid = lldpctl_atom_get_int(port,
+           lldpctl_k_port_vlan_pvid);
+
+       vlans = lldpctl_atom_get(port, lldpctl_k_port_vlans);
+       lldpctl_atom_foreach(vlans, vlan) {
+               vid = lldpctl_atom_get_int(vlan,
+                   lldpctl_k_vlan_id);
+               if (pvid == vid)
+                       foundpvid = 1;
+
+               tag_start(w, "vlan", "VLAN");
+               tag_attr(w, "vlan-id", "",
+                   lldpctl_atom_get_str(vlan, lldpctl_k_vlan_id));
+               if (pvid == vid)
+                       tag_attr(w, "pvid", "pvid", "yes");
+               tag_data(w, lldpctl_atom_get_str(vlan,
+                       lldpctl_k_vlan_name));
+               tag_end(w);
+       }
+       lldpctl_atom_dec_ref(vlans);
+
+       if (!foundpvid && pvid > 0) {
+               tag_start(w, "vlan", "VLAN");
+               tag_attr(w, "vlan-id", "",
+                   lldpctl_atom_get_str(port,
+                       lldpctl_k_port_vlan_pvid));
+               tag_attr(w, "pvid", "pvid", "yes");
+               tag_end(w);
+       }
+}
+
+static void
+display_ppvids(struct writer *w, lldpctl_atom_t *port)
+{
+       lldpctl_atom_t *ppvids, *ppvid;
+       ppvids = lldpctl_atom_get(port, lldpctl_k_port_ppvids);
+       lldpctl_atom_foreach(ppvids, ppvid) {
+               int status = lldpctl_atom_get_int(ppvid,
+                   lldpctl_k_ppvid_status);
+               tag_start(w, "ppvid", "PPVID");
+               if (lldpctl_atom_get_int(ppvid,
+                       lldpctl_k_ppvid_id) > 0)
+                       tag_attr(w, "value", "",
+                           lldpctl_atom_get_str(ppvid,
+                               lldpctl_k_ppvid_id));
+               tag_attr(w, "supported", "supported",
+                        (status & LLDP_PPVID_CAP_SUPPORTED)?"yes":"no");
+               tag_attr(w, "enabled", "enabled",
+                        (status & LLDP_PPVID_CAP_ENABLED)?"yes":"no");
+               tag_end(w);
+       }
+       lldpctl_atom_dec_ref(ppvids);
+}
+
+static void
+display_pids(struct writer *w, lldpctl_atom_t *port)
+{
+       lldpctl_atom_t *pids, *pid;
+       pids = lldpctl_atom_get(port, lldpctl_k_port_pis);
+       lldpctl_atom_foreach(pids, pid) {
+               const char *pi = lldpctl_atom_get_str(pid, lldpctl_k_pi_id);
+               if (pi && strlen(pi) > 0)
+                       tag_datatag(w, "pi", "PI", pi);
+       }
+       lldpctl_atom_dec_ref(pids);
+}
+
+static const char*
+display_age(time_t lastchange)
+{
+       static char sage[30];
+       int age = (int)(time(NULL) - lastchange);
+       if (snprintf(sage, sizeof(sage),
+               "%d day%s, %02d:%02d:%02d",
+               age / (60*60*24),
+               (age / (60*60*24) > 1)?"s":"",
+               (age / (60*60)) % 24,
+               (age / 60) % 60,
+               age % 60) >= sizeof(sage))
+               return "too much";
+       else
+               return sage;
+}
+
+void
+display_interfaces(lldpctl_conn_t *conn, const char *fmt, int hidden,
+    int argc, char *argv[])
+{
+       int i;
+       struct writer * w;
+       lldpctl_atom_t *iface_list;
+       lldpctl_atom_t *iface;
+       lldpctl_atom_t *port;
+       lldpctl_atom_t *neighbors;
+       lldpctl_atom_t *neighbor;
+
+       iface_list = lldpctl_get_interfaces(conn);
+       if (!iface_list) {
+               LLOG_WARNX("not able to get the list of interfaces: %s", lldpctl_strerror(lldpctl_last_error(conn)));
+               return;
+       }
+
+       if (strcmp(fmt, "plain") == 0) {
+               w = txt_init(stdout);
+       } else if (strcmp(fmt, "keyvalue") == 0) {
+               w = kv_init(stdout);
+       }
+#ifdef USE_XML
+       else if (strcmp(fmt,"xml") == 0 ) {
+               w = xml_init(stdout);
+       }
+#endif
+       else {
+               w = txt_init(stdout);
+       }
+
+       tag_start(w, "lldp", "LLDP neighbors");
+       lldpctl_atom_foreach(iface_list, iface) {
+               if (optind < argc) {
+                       for (i = optind; i < argc; i++)
+                               if (strcmp(argv[i],
+                                       lldpctl_atom_get_str(iface,
+                                           lldpctl_k_interface_name)) == 0)
+                                       break;
+                       if (i == argc)
+                               continue;
+               }
+               port      = lldpctl_get_port(iface);
+               neighbors = lldpctl_atom_get(port, lldpctl_k_port_neighbors);
+               lldpctl_atom_foreach(neighbors, neighbor) {
+                       if (!hidden &&
+                           lldpctl_atom_get_int(neighbor, lldpctl_k_port_hidden))
+                               continue;
+
+                       tag_start(w, "interface", "Interface");
+                       tag_attr(w, "name", "",
+                           lldpctl_atom_get_str(iface, lldpctl_k_interface_name));
+                       tag_attr(w, "via" , "via",
+                           lldpctl_atom_get_str(neighbor, lldpctl_k_port_protocol));
+                       tag_attr(w, "rid" , "RID",
+                           lldpctl_atom_get_str(neighbor, lldpctl_k_chassis_index));
+                       tag_attr(w, "age" , "Time",
+                           display_age(lldpctl_atom_get_int(neighbor, lldpctl_k_port_age)));
+
+                       display_chassis(w, neighbor);
+                       display_port(w, neighbor);
+                       display_vlans(w, neighbor);
+                       display_ppvids(w, neighbor);
+                       display_pids(w, neighbor);
+                       display_med(w, neighbor);
+
+                       tag_end(w);
+               }
+               lldpctl_atom_dec_ref(neighbors);
+               lldpctl_atom_dec_ref(port);
+       }
+       lldpctl_atom_dec_ref(iface_list);
+       tag_end(w);
+       w->finish(w);
+}
similarity index 97%
rename from src/kv_writer.c
rename to src/client/kv_writer.c
index ab7a87cd3dfa21a6af164c9a60875705ddfbc126..3897caaf029d30f83535a18ff100f016e550f144 100644 (file)
@@ -1,3 +1,4 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
 /*
  * Copyright (c) 2010 Andreas Hofmeister <andi@collax.com>
  *               2010 Vincent Bernat <bernat@luffy.cx>
@@ -20,7 +21,7 @@
 #include <string.h>
 
 #include "writer.h"
-#include "lldpd.h"
+#include "../log.h"
 
 #define SEP '.'
 
diff --git a/src/client/lldpctl.c b/src/client/lldpctl.c
new file mode 100644 (file)
index 0000000..ae7a4aa
--- /dev/null
@@ -0,0 +1,121 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <arpa/inet.h>
+
+#include "../log.h"
+#include "../ctl.h"
+#include "client.h"
+
+static void             usage(void);
+
+#ifdef HAVE___PROGNAME
+extern const char      *__progname;
+#else
+# define __progname "lldpctl"
+#endif
+
+
+static void
+usage(void)
+{
+       fprintf(stderr, "Usage:   %s [OPTIONS ...] [INTERFACES ...]\n", __progname);
+       fprintf(stderr, "Version: %s\n", PACKAGE_STRING);
+
+       fprintf(stderr, "\n");
+
+       fprintf(stderr, "-d          Enable more debugging information.\n");
+       fprintf(stderr, "-a          Display all remote ports, including hidden ones.\n");
+       fprintf(stderr, "-f format   Choose output format (plain, keyvalue or xml).\n");
+       fprintf(stderr, "-L location Enable the transmission of LLDP-MED location TLV for the\n");
+       fprintf(stderr, "            given interfaces. Can be repeated to enable the transmission\n");
+       fprintf(stderr, "            of the location in several formats.\n");
+       fprintf(stderr, "-P policy   Enable the transmission of LLDP-MED Network Policy TLVs\n");
+       fprintf(stderr, "            for the given interfaces. Can be repeated to specify\n");
+       fprintf(stderr, "            different policies.\n");
+       fprintf(stderr, "-O poe      Enable the transmission of LLDP-MED POE-MDI TLV\n");
+       fprintf(stderr, "            for the given interfaces.\n");
+       fprintf(stderr, "-o poe      Enable the transmission of Dot3 POE-MDI TLV\n");
+       fprintf(stderr, "            for the given interfaces.\n");
+
+       fprintf(stderr, "\n");
+
+       fprintf(stderr, "see manual page lldpctl(8) for more information\n");
+       exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+       int ch, debug = 1;
+       char * fmt = "plain";
+       int action = 0, hidden = 0;
+       lldpctl_conn_t *conn;
+
+       /* Get and parse command line options */
+       while ((ch = getopt(argc, argv, LLDPCTL_ARGS)) != -1) {
+               switch (ch) {
+               case 'h':
+                       usage();
+                       break;
+               case 'd':
+                       debug++;
+                       break;
+               case 'v':
+                       fprintf(stdout, "%s\n", PACKAGE_VERSION);
+                       exit(0);
+                       break;
+               case 'a':
+                       hidden = 1;
+                       break;
+               case 'f':
+                       fmt = optarg;
+                       break;
+               case 'L':
+               case 'P':
+               case 'O':
+               case 'o':
+                       action = 1;
+                       break;
+               default:
+                       usage();
+               }
+       }
+
+       log_init(debug, __progname);
+
+       if ((action != 0) && (getuid() != 0)) {
+               fatalx("mere mortals may not do that, 'root' privileges are required.");
+       }
+
+       conn = lldpctl_new(NULL, NULL, NULL);
+       if (conn == NULL) exit(EXIT_FAILURE);
+
+       if (!action) display_interfaces(conn, fmt, hidden, argc, argv);
+       else modify_interfaces(conn, argc, argv, optind);
+
+       lldpctl_release(conn);
+       return EXIT_SUCCESS;
+}
similarity index 97%
rename from src/text_writer.c
rename to src/client/text_writer.c
index b76cdcb58a8d60a30096b833d0bd95e82a841357..017dca20cd4e241b748bf6029e2b623c65739702 100644 (file)
@@ -1,3 +1,4 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
 /*
  * Copyright (c) 2010 Andreas Hofmeister <andi@collax.com>
  *
@@ -19,7 +20,7 @@
 #include <string.h>
 
 #include "writer.h"
-#include "lldpd.h"
+#include "../log.h"
 
 static char sep[] = "-------------------------------------------------------------------------------";
 
similarity index 91%
rename from src/writer.h
rename to src/client/writer.h
index b2479c35b9321da529b4fd4f131f20d84f45be3d..cedaee100bcc4e5f2a05e819766bde673c9640a3 100644 (file)
@@ -1,3 +1,4 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
 /*
  * Copyright (c) 2010 Andreas Hofmeister <andi@collax.com>
  *
@@ -32,7 +33,7 @@ struct writer {
 #define tag_attr(w,...)                w->attr(w,## __VA_ARGS__)
 #define tag_data(w,...)                w->data(w,## __VA_ARGS__)
 #define tag_end(w,...)         w->end(w,## __VA_ARGS__)
-#define tag_datatag(w,t,d,...) { w->start(w,t,d); w->data(w,## __VA_ARGS__); w->end(w); }
+#define tag_datatag(w,t,d,v)   do { if (!v) break; w->start(w,t,d); w->data(w,v); w->end(w); } while(0);
 
 extern struct writer * txt_init( FILE * );
 extern struct writer * kv_init( FILE * );
similarity index 97%
rename from src/xml_writer.c
rename to src/client/xml_writer.c
index de404fbaac4e86e962b1ae4fb1738b95c323aff4..9192fd705320990c212a112d017132527e4c4790 100644 (file)
@@ -1,3 +1,4 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
 /*
  * Copyright (c) 2010 Andreas Hofmeister <andi@collax.com>
  *
@@ -22,7 +23,7 @@
 #include <libxml/xmlwriter.h>
 
 #include "writer.h"
-#include "lldpd.h"
+#include "../log.h"
 
 struct xml_writer_private {
        xmlTextWriterPtr xw;
@@ -50,7 +51,6 @@ void xml_attr(struct writer * w, const char * tag, const char * descr, const cha
 
 void xml_data(struct writer * w, const char * data) {
        struct xml_writer_private * p = w->priv;
-
        if (xmlTextWriterWriteString(p->xw, BAD_CAST data) < 0 )
                LLOG_WARNX("cannot add '%s' as data to element", data);
 }
diff --git a/src/compat/Makefile.am b/src/compat/Makefile.am
new file mode 100644 (file)
index 0000000..6bf15b9
--- /dev/null
@@ -0,0 +1,4 @@
+noinst_LTLIBRARIES = libcompat.la
+
+libcompat_la_SOURCES = compat.h
+libcompat_la_LIBADD  = @LTLIBOBJS@
similarity index 97%
rename from src/compat.h
rename to src/compat/compat.h
index 6da5615c04d84765a6ce92d88affaa6457d504a7..957f3992af33df1685ccbe888e77dd9850f7e101 100644 (file)
@@ -1,3 +1,4 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
 /*
  * Copyright (c) 1991, 1993
  *     The Regents of the University of California.  All rights reserved.
  *     @(#)queue.h     8.5 (Berkeley) 8/20/94
  */
 
+#ifndef _COMPAT_H
+#define _COMPAT_H
+
+#include <stddef.h>
+
 #if !HAVE_DECL_TAILQ_FIRST
 #define        TAILQ_FIRST(head)               ((head)->tqh_first)
 #endif
@@ -139,3 +145,5 @@ void *malloc(size_t size);
 #if !HAVE_REALLOC
 void *realloc(void *ptr, size_t size);
 #endif
+
+#endif
similarity index 99%
rename from src/getifaddrs.c
rename to src/compat/getifaddrs.c
index 902078ece077f3c9805c60e6a3606f1a6941348b..b2fea1eb22b971a7d8c64e1cd3184a4e3dda69d1 100644 (file)
@@ -1,3 +1,4 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
 /* Stolen from:
    http://www.linux-ipv6.org/cvsweb/usagi/usagi/libinet6/ifaddrs.c?rev=1.17.4.2;content-type=text%2Fplain
 */
@@ -33,7 +34,7 @@
  * SUCH DAMAGE.
  */
 
-#include "lldpd.h"
+#include "compat.h"
 
 /* For compatibility */
 #undef IFA_NETMASK
similarity index 85%
rename from src/malloc.c
rename to src/compat/malloc.c
index 8ff490aab14028b5468bf1fd65f61c178066e317..5b97294de40945dcb4b7269369ce86a54021ee3d 100644 (file)
@@ -1,3 +1,4 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
 /* malloc replacement that can allocate 0 byte */
 
 #undef malloc
similarity index 88%
rename from src/realloc.c
rename to src/compat/realloc.c
index 8a89d4b36acfc46b02f29fcc7dfb827c18e7c1c9..c6aa351ca9df8dfe496fa5127acef0fdff58e8ca 100644 (file)
@@ -1,3 +1,4 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
 /* realloc replacement that can reallocate 0 byte or NULL pointers*/
 
 #undef realloc
similarity index 97%
rename from src/strlcpy.c
rename to src/compat/strlcpy.c
index c30566a7dbf7b237d09f4bf335842a6b4aef2933..f5f135afd35ab23b802dc83f4a10fe0e5d5eb5bb 100644 (file)
@@ -1,3 +1,4 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
 /*     $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $    */
 
 /*
index f7f0ddaeeb063beb0353accefbc4c17eadf80a35..c37f6d01e0a798291a7a798358d68dbe3c055092 100644 (file)
--- a/src/ctl.c
+++ b/src/ctl.c
@@ -1,3 +1,4 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
 /*
  * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
  *
@@ -14,8 +15,7 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include "lldpd.h"
-
+#include <stdlib.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <errno.h>
 #include <sys/socket.h>
 #include <sys/un.h>
 
+#include "ctl.h"
+#include "marshal.h"
+#include "log.h"
+#include "compat/compat.h"
+
+#define UNIX_PATH_MAX  108
+
+/**
+ * Create a new listening Unix socket for control protocol.
+ *
+ * @param name The name of the Unix socket.
+ * @return The socket when successful, -1 otherwise.
+ */
 int
 ctl_create(char *name)
 {
@@ -45,6 +58,12 @@ ctl_create(char *name)
        return s;
 }
 
+/**
+ * Connect to the control Unix socket.
+ *
+ * @param name The name of the Unix socket.
+ * @return The socket when successful, -1 otherwise.
+ */
 int
 ctl_connect(char *name)
 {
@@ -58,25 +77,51 @@ ctl_connect(char *name)
        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);
+               LLOG_WARN("unable to connect to socket %s", name);
                errno = rc; return -1;
        }
        return s;
 }
 
+/**
+ * Remove the control Unix socket.
+ *
+ * @param name The name of the Unix socket.
+ */
+void
+ctl_cleanup(char *name)
+{
+       if (unlink(name) == -1)
+               LLOG_WARN("unable to unlink %s", name);
+}
+
+/** Header for the control protocol.
+ *
+ * The protocol is pretty simple. We send a single message containing the
+ * provided message type with the message length, followed by the message
+ * content.
+ */
 struct hmsg_header {
        enum hmsg_type type;
        size_t         len;
 };
 
-/* The protocol is pretty simple. We send a single message containing the
- * provided message type with the message length, followed by the message
- * content. It is expected the message content to be serialized. */
+/**
+ * Send a message with the control protocol.
+ *
+ * @param fd   The file descriptor that should be used.
+ * @param type The message type to be sent.
+ * @param t    The buffer containing the message content. Can be @c NULL if the
+ *             message is empty.
+ * @param len  The length of the buffer containing the message content.
+ * @return     The number of bytes written or -1 in case of error.
+ */
 int
 ctl_msg_send(int fd, enum hmsg_type type, void *t, size_t len)
 {
        struct iovec iov[2];
        struct hmsg_header hdr;
+       memset(&hdr, 0, sizeof(struct hmsg_header));
        hdr.type = type;
        hdr.len  = len;
        iov[0].iov_base = &hdr;
@@ -86,6 +131,15 @@ ctl_msg_send(int fd, enum hmsg_type type, void *t, size_t len)
        return writev(fd, iov, t?2:1);
 }
 
+/**
+ * Receive a message with the control protocol.
+ *
+ * @param fd        The file descriptor that should be used.
+ * @param type[out] The type of the received message.
+ * @param t         The buffer containing the message content.
+ * @return  The size of the returned buffer. 0 if the message is empty. -1 if
+ *          there is an error.
+ */
 int
 ctl_msg_recv(int fd, enum hmsg_type *type, void **t)
 {
@@ -146,64 +200,144 @@ recv_error:
        return -1;
 }
 
+/**
+ * Serialize and "send" a structure through the control protocol.
+ *
+ * This function does not really send the message but outputs it to a buffer.
+ *
+ * @param output_buffer A pointer to a buffer to which the message will be
+ *                      appended. Can be @c NULL. In this case, the buffer will
+ *                      be allocated.
+ * @param output_len[in,out] The length of the provided buffer. Will be updated
+ *                           with the new length
+ * @param type  The type of message we want to send.
+ * @param t     The structure to be serialized and sent.
+ * @param mi    The appropriate marshal structure for serialization.
+ * @return -1 in case of failure, 0 in case of success.
+ */
 int
-ctl_msg_send_recv(int fd,
+ctl_msg_send_unserialized(uint8_t **output_buffer, size_t *output_len,
     enum hmsg_type type,
-    void *input, struct marshal_info *input_mi,
-    void **output, struct marshal_info *output_mi)
+    void *t, struct marshal_info *mi)
 {
-       int n, input_len = 0;
-       void *input_buffer = NULL;
-       void *serialized = NULL;
-       enum hmsg_type received_type;
-
-       /* Serialize */
-       if (input) {
-               input_len = marshal_serialize_(input_mi, input, &input_buffer, 0, NULL, 0);
-               if (input_len <= 0) {
-                       LLOG_WARNX("unable to serialize input data");
+       struct hmsg_header hdr;
+       size_t len = 0, newlen;
+       void *buffer = NULL;
+
+       if (t) {
+               len = marshal_serialize_(mi, t, &buffer, 0, NULL, 0);
+               if (len <= 0) {
+                       LLOG_WARNX("unable to serialize data");
                        return -1;
                }
        }
-       /* Send request */
-       if (ctl_msg_send(fd, type, input_buffer, input_len) == -1) {
-               LLOG_WARN("unable to send request");
-               goto send_recv_error;
-       }
-       free(input_buffer); input_buffer = NULL;
-       /* Receive answer */
-       if ((n = ctl_msg_recv(fd, &received_type, &serialized)) == -1)
-               goto send_recv_error;
-       /* Check type */
-       if (received_type != type) {
-               LLOG_WARNX("incorrect received message type (expected: %d, received: %d)",
-                   type, received_type);
-               goto send_recv_error;
+
+       newlen = len + sizeof(struct hmsg_header);
+
+       if (*output_buffer == NULL) {
+               *output_len = 0;
+               if ((*output_buffer = malloc(newlen)) == NULL) {
+                       LLOG_WARN("no memory available");
+                       free(buffer);
+                       return -1;
+               }
+       } else {
+               void *new = realloc(*output_buffer, *output_len + newlen);
+               if (new == NULL) {
+                       LLOG_WARN("no memory available");
+                       free(buffer);
+                       return -1;
+               }
+               *output_buffer = new;
        }
-       /* Unserialize */
-       if (output == NULL) {
-               free(serialized);
-               return 0;
+       memset(&hdr, 0, sizeof(struct hmsg_header));
+       hdr.type = type;
+       hdr.len  = len;
+       memcpy(*output_buffer + *output_len, &hdr, sizeof(struct hmsg_header));
+       if (t)
+               memcpy(*output_buffer + *output_len + sizeof(struct hmsg_header), buffer, len);
+       *output_len += newlen;
+       free(buffer);
+       return 0;
+}
+
+/**
+ * "Receive" and unserialize a structure through the control protocol.
+ *
+ * Like @c ctl_msg_send_unserialized(), this function uses buffer to receive the
+ * incoming message.
+ *
+ * @param input_buffer[in,out] The buffer with the incoming message. Will be
+ *                             updated once the message has been unserialized to
+ *                             point to the remaining of the message or will be
+ *                             freed if all the buffer has been consumed. Can be
+ *                             @c NULL.
+ * @param input_len[in,out]    The length of the provided buffer. Will be updated
+ *                             to the length of remaining data once the message
+ *                             has been unserialized.
+ * @param expected_type        The expected message type.
+ * @param t[out]               Will contain a pointer to the unserialized structure.
+ *                             Can be @c NULL if we don't want to store the
+ *                             answer.
+ * @param mi                   The appropriate marshal structure for unserialization.
+ *
+ * @return -1 in case of error, 0 in case of success and the number of bytes we
+ *         request to complete unserialization.
+ */
+size_t
+ctl_msg_recv_unserialized(uint8_t **input_buffer, size_t *input_len,
+    enum hmsg_type expected_type,
+    void **t, struct marshal_info *mi)
+{
+       struct hmsg_header *hdr;
+       int rc = -1;
+
+       if (*input_buffer == NULL ||
+           *input_len < sizeof(struct hmsg_header)) {
+               /* Not enough data. */
+               return sizeof(struct hmsg_header) - *input_len;
        }
-       if (n == 0) {
+       hdr = (struct hmsg_header *)*input_buffer;
+       if (hdr->len > (1<<15)) {
+               LLOG_WARNX("message received is too large");
+               /* We discard the whole buffer */
+               free(*input_buffer);
+               *input_buffer = NULL;
+               *input_len = 0;
+               return -1;
+       }
+       if (*input_len < sizeof(struct hmsg_header) + hdr->len) {
+               /* Not enough data. */
+               return sizeof(struct hmsg_header) + hdr->len - *input_len;
+       }
+       if (hdr->type != expected_type) {
+               LLOG_WARNX("incorrect received message type (expected: %d, received: %d)",
+                   expected_type, hdr->type);
+               goto end;
+       }
+
+       if (t && !hdr->len) {
                LLOG_WARNX("no payload available in answer");
-               goto send_recv_error;
+               goto end;
        }
-       if (marshal_unserialize_(output_mi, serialized, n, output, NULL, 0, 0) <= 0) {
-               LLOG_WARNX("unable to deserialize received data");
-               goto send_recv_error;
+       if (t) {
+               /* We have data to unserialize. */
+               if (marshal_unserialize_(mi, *input_buffer + sizeof(struct hmsg_header),
+                       hdr->len, t, NULL, 0, 0) <= 0) {
+                       LLOG_WARNX("unable to deserialize received data");
+                       goto end;
+               }
        }
-       /* All done. */
-       return 0;
-send_recv_error:
-       free(serialized);
-       free(input_buffer);
-       return -1;
-}
 
-void
-ctl_cleanup(char *name)
-{
-       if (unlink(name) == -1)
-               LLOG_WARN("unable to unlink %s", name);
+       rc = 0;
+end:
+       /* Discard input buffer */
+       *input_len -= sizeof(struct hmsg_header) + hdr->len;
+       if (*input_len == 0) {
+               free(*input_buffer);
+               *input_buffer = NULL;
+       } else
+               memmove(input_buffer, input_buffer + sizeof(struct hmsg_header) + hdr->len,
+                   *input_len);
+       return rc;
 }
diff --git a/src/ctl.h b/src/ctl.h
new file mode 100644 (file)
index 0000000..6f8b398
--- /dev/null
+++ b/src/ctl.h
@@ -0,0 +1,47 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _CTL_H
+#define _CTL_H
+
+#define LLDPD_CTL_SOCKET       "/var/run/lldpd.socket"
+
+#include <stdint.h>
+#include "marshal.h"
+
+enum hmsg_type {
+       NONE,
+       GET_INTERFACES,         /* Get list of interfaces */
+       GET_INTERFACE,          /* Get all information related to an interface */
+       SET_PORT,               /* Set port-related information (location, power, policy) */
+};
+
+/* ctl.c */
+int     ctl_create(char *);
+int     ctl_connect(char *);
+void    ctl_cleanup(char *);
+int     ctl_msg_send(int, enum hmsg_type, void *, size_t);
+int     ctl_msg_recv(int, enum hmsg_type *, void **);
+
+int     ctl_msg_send_unserialized(uint8_t **, size_t *,
+                                      enum hmsg_type,
+                                      void *, struct marshal_info *);
+size_t  ctl_msg_recv_unserialized(uint8_t **, size_t *,
+                                      enum hmsg_type,
+                                      void **, struct marshal_info *);
+
+#endif
diff --git a/src/daemon/Makefile.am b/src/daemon/Makefile.am
new file mode 100644 (file)
index 0000000..943297c
--- /dev/null
@@ -0,0 +1,28 @@
+sbin_PROGRAMS = lldpd
+
+noinst_LTLIBRARIES = liblldpd.la
+
+## Convenience library for lldpd and tests
+liblldpd_la_SOURCES  = frame.h frame.c lldpd.c lldp.c cdp.c sonmp.c edp.c
+liblldpd_la_SOURCES += interfaces.c client.c priv.c privsep_fdpass.c dmi.c
+liblldpd_la_SOURCES += event.c
+liblldpd_la_CFLAGS   = @LIBEVENT_CFLAGS@
+liblldpd_la_LIBADD   = $(top_builddir)/src/libcommon-daemon-client.la $(top_builddir)/src/libcommon-daemon-lib.la @LIBEVENT_LIBS@
+
+# Add SNMP support if needed
+if USE_SNMP
+liblldpd_la_SOURCES += agent.c agent_priv.c agent.h
+liblldpd_la_CFLAGS  += @NETSNMP_CFLAGS@
+liblldpd_la_LIBADD  += @NETSNMP_LIBS@
+endif
+
+## lldpd
+lldpd_SOURCES = main.c
+lldpd_LDADD   = liblldpd.la @LIBEVENT_LDFLAGS@
+
+## libevent
+if LIBEVENT_EMBEDDED
+event.c: $(top_builddir)/libevent/libevent.la
+$(top_builddir)/libevent/libevent.la: $(top_srcdir)/libevent/*.c $(top_srcdir)/libevent/*.h
+       (cd $(top_builddir)/libevent && $(MAKE))
+endif
similarity index 98%
rename from src/agent.c
rename to src/daemon/agent.c
index ed325f57d5e2187ddfa8968867d669a6dd6c55a5..7acbe6993d9f56797302686c3f6b5cc817e26b76 100644 (file)
@@ -1,3 +1,4 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
 /*
  * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
  *
@@ -166,7 +167,7 @@ header_pmedindexed_policy_table(struct variable *vp, oid *name, size_t *length,
 
        if (!header_index_init(vp, name, length, exact, var_len, write_method)) return NULL;
        TAILQ_FOREACH(hardware, &scfg->g_hardware, h_entries) {
-               for (i = 0; i < LLDPMED_APPTYPE_LAST; i++) {
+               for (i = 0; i < LLDP_MED_APPTYPE_LAST; i++) {
                        if (hardware->h_lport.p_med_policy[i].type != i+1)
                                continue;
                        index[0] = hardware->h_ifindex;
@@ -189,7 +190,7 @@ header_pmedindexed_location_table(struct variable *vp, oid *name, size_t *length
 
        if (!header_index_init(vp, name, length, exact, var_len, write_method)) return NULL;
        TAILQ_FOREACH(hardware, &scfg->g_hardware, h_entries) {
-               for (i = 0; i < LLDPMED_LOCFORMAT_LAST; i++) {
+               for (i = 0; i < LLDP_MED_LOCFORMAT_LAST; i++) {
                        if (hardware->h_lport.p_med_location[i].format != i+1)
                                continue;
                        index[0] = hardware->h_ifindex;
@@ -311,7 +312,7 @@ header_tprmedindexed_table(struct variable *vp, oid *name, size_t *length,
                        switch (variant) {
                        case TPR_VARIANT_MED_POLICY:
                                for (j = 0;
-                                    j < LLDPMED_APPTYPE_LAST;
+                                    j < LLDP_MED_APPTYPE_LAST;
                                     j++) {
                                        if (port->p_med_policy[j].type != j+1)
                                                continue;
@@ -326,7 +327,7 @@ header_tprmedindexed_table(struct variable *vp, oid *name, size_t *length,
                                break;
                        case TPR_VARIANT_MED_LOCATION:
                                for (j = 0;
-                                    j < LLDPMED_LOCFORMAT_LAST;
+                                    j < LLDP_MED_LOCFORMAT_LAST;
                                     j++) {
                                        if (port->p_med_location[j].format != j+1)
                                                continue;
@@ -659,9 +660,9 @@ agent_v_med_power(struct variable *vp, size_t *var_len, struct lldpd_med_power *
        switch (vp->magic) {
        case LLDP_SNMP_MED_POE_DEVICETYPE:
                switch (power->devicetype) {
-               case LLDPMED_POW_TYPE_PSE:
+               case LLDP_MED_POW_TYPE_PSE:
                        long_ret = 2; break;
-               case LLDPMED_POW_TYPE_PD:
+               case LLDP_MED_POW_TYPE_PD:
                        long_ret = 3; break;
                case 0:
                        long_ret = 4; break;
@@ -673,21 +674,21 @@ agent_v_med_power(struct variable *vp, size_t *var_len, struct lldpd_med_power *
        case LLDP_SNMP_MED_POE_PD_POWERVAL:
                if (((vp->magic == LLDP_SNMP_MED_POE_PSE_POWERVAL) &&
                        (power->devicetype ==
-                       LLDPMED_POW_TYPE_PSE)) ||
+                       LLDP_MED_POW_TYPE_PSE)) ||
                    ((vp->magic == LLDP_SNMP_MED_POE_PD_POWERVAL) &&
                        (power->devicetype ==
-                           LLDPMED_POW_TYPE_PD))) {
+                           LLDP_MED_POW_TYPE_PD))) {
                        long_ret = power->val;
                        return (u_char *)&long_ret;
                }
                break;
        case LLDP_SNMP_MED_POE_PSE_POWERSOURCE:
                if (power->devicetype ==
-                   LLDPMED_POW_TYPE_PSE) {
+                   LLDP_MED_POW_TYPE_PSE) {
                        switch (power->source) {
-                       case LLDPMED_POW_SOURCE_PRIMARY:
+                       case LLDP_MED_POW_SOURCE_PRIMARY:
                                long_ret = 2; break;
-                       case LLDPMED_POW_SOURCE_BACKUP:
+                       case LLDP_MED_POW_SOURCE_BACKUP:
                                long_ret = 3; break;
                        default:
                                long_ret = 1;
@@ -697,13 +698,13 @@ agent_v_med_power(struct variable *vp, size_t *var_len, struct lldpd_med_power *
                break;
        case LLDP_SNMP_MED_POE_PD_POWERSOURCE:
                if (power->devicetype ==
-                   LLDPMED_POW_TYPE_PD) {
+                   LLDP_MED_POW_TYPE_PD) {
                        switch (power->source) {
-                       case LLDPMED_POW_SOURCE_PSE:
+                       case LLDP_MED_POW_SOURCE_PSE:
                                long_ret = 2; break;
-                       case LLDPMED_POW_SOURCE_LOCAL:
+                       case LLDP_MED_POW_SOURCE_LOCAL:
                                long_ret = 3; break;
-                       case LLDPMED_POW_SOURCE_BOTH:
+                       case LLDP_MED_POW_SOURCE_BOTH:
                                long_ret = 4; break;
                        default:
                                long_ret = 1;
@@ -715,16 +716,16 @@ agent_v_med_power(struct variable *vp, size_t *var_len, struct lldpd_med_power *
        case LLDP_SNMP_MED_POE_PD_POWERPRIORITY:
                if (((vp->magic == LLDP_SNMP_MED_POE_PSE_POWERPRIORITY) &&
                        (power->devicetype ==
-                       LLDPMED_POW_TYPE_PSE)) ||
+                       LLDP_MED_POW_TYPE_PSE)) ||
                    ((vp->magic == LLDP_SNMP_MED_POE_PD_POWERPRIORITY) &&
                        (power->devicetype ==
-                           LLDPMED_POW_TYPE_PD))) {
+                           LLDP_MED_POW_TYPE_PD))) {
                        switch (power->priority) {
-                       case LLDPMED_POW_PRIO_CRITICAL:
+                       case LLDP_MED_POW_PRIO_CRITICAL:
                                long_ret = 2; break;
-                       case LLDPMED_POW_PRIO_HIGH:
+                       case LLDP_MED_POW_PRIO_HIGH:
                                long_ret = 3; break;
-                       case LLDPMED_POW_PRIO_LOW:
+                       case LLDP_MED_POW_PRIO_LOW:
                                long_ret = 4; break;
                        default:
                                long_ret = 1;
@@ -756,12 +757,12 @@ agent_h_local_med_power(struct variable *vp, oid *name, size_t *length,
           PD/PSE ports. */
        TAILQ_FOREACH(hardware, &scfg->g_hardware, h_entries) {
                if (hardware->h_lport.p_med_power.devicetype ==
-                   LLDPMED_POW_TYPE_PSE) {
+                   LLDP_MED_POW_TYPE_PSE) {
                        pse++;
                        if (pse == 1) /* Take this port as a reference */
                                power = &hardware->h_lport.p_med_power;
                } else if (hardware->h_lport.p_med_power.devicetype ==
-                          LLDPMED_POW_TYPE_PD) {
+                          LLDP_MED_POW_TYPE_PD) {
                        pse--;
                        if (pse == -1) /* Take this one instead */
                                power = &hardware->h_lport.p_med_power;
@@ -1102,10 +1103,10 @@ agent_v_ppvid(struct variable *vp, size_t *var_len, struct lldpd_ppvid *ppvid)
 
        switch (vp->magic) {
        case LLDP_SNMP_DOT1_PPVLAN_SUPPORTED:
-               long_ret = (ppvid->p_cap_status & LLDPD_PPVID_CAP_SUPPORTED)?1:2;
+               long_ret = (ppvid->p_cap_status & LLDP_PPVID_CAP_SUPPORTED)?1:2;
                return (u_char *)&long_ret;
        case LLDP_SNMP_DOT1_PPVLAN_ENABLED:
-               long_ret = (ppvid->p_cap_status & LLDPD_PPVID_CAP_ENABLED)?1:2;
+               long_ret = (ppvid->p_cap_status & LLDP_PPVID_CAP_ENABLED)?1:2;
                return (u_char *)&long_ret;
        default:
                break;
diff --git a/src/daemon/agent.h b/src/daemon/agent.h
new file mode 100644 (file)
index 0000000..74479a3
--- /dev/null
@@ -0,0 +1,29 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+* Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
+*
+* Permission to use, copy, modify, and/or distribute this software for any
+* purpose with or without fee is hereby granted, provided that the above
+* copyright notice and this permission notice appear in all copies.
+*
+* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#ifndef _AGENT_H
+#define _AGENT_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>
+
+static oid lldp_oid[] = {1, 0, 8802, 1, 1, 2};
+size_t agent_lldp_vars_size(void);
+
+#endif
similarity index 99%
rename from src/agent_priv.c
rename to src/daemon/agent_priv.c
index ab6b065a1d0f88984c9c86cacfdbe1585f7a6ec1..2b8cbc959e4b68b3fd87c02414332b287dd4794b 100644 (file)
@@ -1,3 +1,4 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
 /*
  * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
  *
similarity index 99%
rename from src/cdp.c
rename to src/daemon/cdp.c
index 8d168c395ac5f3eae1766f9aae34e23b9a66dc06..a433c68c6878e426f35cc4ccadbfca109c4016ad 100644 (file)
--- a/src/cdp.c
@@ -1,3 +1,4 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
 /*
  * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
  *
@@ -543,7 +544,7 @@ cdp_decode(struct lldpd *cfg, char *frame, int s,
 
 malformed:
        lldpd_chassis_cleanup(chassis, 1);
-       lldpd_port_cleanup(cfg, port, 1);
+       lldpd_port_cleanup(port, 1);
        free(port);
        return -1;
 }
similarity index 97%
rename from src/cdp.h
rename to src/daemon/cdp.h
index bae7f5e2ba22e95e5a38f47c068090b2c462236b..a84601ab2f59e1cddcf97397438f0b30fd2b57ea 100644 (file)
--- a/src/cdp.h
@@ -1,3 +1,4 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
 /*
  * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
  *
similarity index 87%
rename from src/client.c
rename to src/daemon/client.c
index 9b602e634dae55aa0e31e16503ad8ab7b321af11..909b3801a5b1db662f496060d58da10a4e60ef9b 100644 (file)
@@ -1,3 +1,4 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
 /*
  * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
  *
@@ -130,19 +131,21 @@ client_handle_set_port(struct lldpd *cfg, enum hmsg_type *type,
        TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries)
            if (!strcmp(hardware->h_ifname, set->ifname)) {
                    struct lldpd_port *port = &hardware->h_lport;
+                   (void)port;
 #ifdef ENABLE_LLDPMED
                    if (set->med_policy && set->med_policy->type > 0) {
-                           if (set->med_policy->type > LLDPMED_APPTYPE_LAST) {
+                           if (set->med_policy->type > LLDP_MED_APPTYPE_LAST) {
                                    LLOG_WARNX("invalid policy provided: %d",
                                        set->med_policy->type);
                                    goto set_port_finished;
                            }
                            memcpy(&port->p_med_policy[set->med_policy->type - 1],
                                set->med_policy, sizeof(struct lldpd_med_policy));
-                           port->p_med_cap_enabled |= LLDPMED_CAP_POLICY;
+                           port->p_med_cap_enabled |= LLDP_MED_CAP_POLICY;
                    }
                    if (set->med_location && set->med_location->format > 0) {
-                           if (set->med_location->format > LLDPMED_LOCFORMAT_LAST) {
+                           char *newdata = NULL;
+                           if (set->med_location->format > LLDP_MED_LOCFORMAT_LAST) {
                                    LLOG_WARNX("invalid location format provided: %d",
                                        set->med_location->format);
                                    goto set_port_finished;
@@ -151,20 +154,22 @@ client_handle_set_port(struct lldpd *cfg, enum hmsg_type *type,
                                &port->p_med_location[set->med_location->format - 1];
                            free(loc->data);
                            memcpy(loc, set->med_location, sizeof(struct lldpd_med_loc));
-                           if (!loc->data || !(loc->data = strdup(loc->data))) loc->data_len = 0;
-                           port->p_med_cap_enabled |= LLDPMED_CAP_LOCATION;
+                           if (!loc->data || !(newdata = malloc(loc->data_len))) loc->data_len = 0;
+                           if (newdata) memcpy(newdata, loc->data, loc->data_len);
+                           loc->data = newdata;
+                           port->p_med_cap_enabled |= LLDP_MED_CAP_LOCATION;
                    }
                    if (set->med_power) {
                            memcpy(&port->p_med_power, set->med_power,
                                sizeof(struct lldpd_med_power));
                            switch (set->med_power->devicetype) {
-                           case LLDPMED_POW_TYPE_PD:
-                                   port->p_med_cap_enabled |= LLDPMED_CAP_MDI_PD;
-                                   port->p_med_cap_enabled &= ~LLDPMED_CAP_MDI_PSE;
+                           case LLDP_MED_POW_TYPE_PD:
+                                   port->p_med_cap_enabled |= LLDP_MED_CAP_MDI_PD;
+                                   port->p_med_cap_enabled &= ~LLDP_MED_CAP_MDI_PSE;
                                    break;
-                           case LLDPMED_POW_TYPE_PSE:
-                                   port->p_med_cap_enabled |= LLDPMED_CAP_MDI_PSE;
-                                   port->p_med_cap_enabled &= ~LLDPMED_CAP_MDI_PD;
+                           case LLDP_MED_POW_TYPE_PSE:
+                                   port->p_med_cap_enabled |= LLDP_MED_CAP_MDI_PSE;
+                                   port->p_med_cap_enabled &= ~LLDP_MED_CAP_MDI_PD;
                                    break;
                            }
                    }
@@ -184,7 +189,7 @@ client_handle_set_port(struct lldpd *cfg, enum hmsg_type *type,
 set_port_finished:
        if (!ret) *type = NONE;
        free(set->ifname);
-#ifdef ENABLE_LLDPMED       
+#ifdef ENABLE_LLDPMED
        free(set->med_policy);
        if (set->med_location) free(set->med_location->data);
        free(set->med_location);
similarity index 97%
rename from src/dmi.c
rename to src/daemon/dmi.c
index 14aa895bdb29cd0e476fa1648a64039fe81ce8fa..ed364e0a2be2cb33d42c5f0e5340c5d9d57e0a44 100644 (file)
--- a/src/dmi.c
@@ -1,3 +1,4 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
 /*
  * Copyright (c) 2009 Vincent Bernat <bernat@luffy.cx>
  *
similarity index 99%
rename from src/edp.c
rename to src/daemon/edp.c
index b1c8a98bbd83beaa5a8d9af4769e01f5e92cf2b7..a3a23b2549c640832a822fd91a04a1442ba82053 100644 (file)
--- a/src/edp.c
@@ -1,3 +1,4 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
 /*
  * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
  *
@@ -495,7 +496,7 @@ edp_decode(struct lldpd *cfg, char *frame, int s,
 
 malformed:
        lldpd_chassis_cleanup(chassis, 1);
-       lldpd_port_cleanup(cfg, port, 1);
+       lldpd_port_cleanup(port, 1);
        free(port);
        return -1;
 }
similarity index 96%
rename from src/edp.h
rename to src/daemon/edp.h
index ffa92ddfb6d82bc8f570a091c71fdf1cd9e02755..e3cb7382b7caee4c2d09490f9fb5daa778d19ed7 100644 (file)
--- a/src/edp.h
@@ -1,3 +1,4 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
 /*
  * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
  *
similarity index 99%
rename from src/event.c
rename to src/daemon/event.c
index 376b1e2af2ceab39e7d0b20e578ac6f037bc8ee1..9d0d800582137c5772c45702d5886899a5d2db92 100644 (file)
@@ -1,3 +1,4 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
 /*
  * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx>
  *
similarity index 96%
rename from src/frame.c
rename to src/daemon/frame.c
index d091e596ad03cbd0df8bd7e4c1dac5d164d3f161..e1093ad3ac88d55acdfa68fca536688e79462f2b 100644 (file)
@@ -1,3 +1,4 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
 /*
  * Copyright (c) 2009 Vincent Bernat <bernat@luffy.cx>
  *
similarity index 98%
rename from src/frame.h
rename to src/daemon/frame.h
index 81249cc3bc8a6cff1869064a7416f78342882724..7bb1ea6f749034bcd2a54a382e97ffbb98c84417 100644 (file)
@@ -1,3 +1,4 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
 /*
  * Copyright (c) 2009 Vincent Bernat <bernat@luffy.cx>
  *
similarity index 99%
rename from src/interfaces.c
rename to src/daemon/interfaces.c
index a99125dd319ba11680bf05ce874cadeebf2de155..0a1c83336fe691e36362062e1991c96232c3c56a 100644 (file)
@@ -1,3 +1,4 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
 /*
  * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
  *
@@ -724,7 +725,7 @@ lldpd_ifh_eth(struct lldpd *cfg, struct ifaddrs *ifap)
                        TAILQ_INSERT_TAIL(&cfg->g_hardware, hardware, h_entries);
                } else {
                        if (hardware->h_flags) continue; /* Already seen this time */
-                       lldpd_port_cleanup(cfg, &hardware->h_lport, 0);
+                       lldpd_port_cleanup(&hardware->h_lport, 0);
                }
 
                hardware->h_flags = ifa->ifa_flags; /* Should be non-zero */
@@ -922,7 +923,7 @@ lldpd_ifh_bond(struct lldpd *cfg, struct ifaddrs *ifap)
                        if (hardware->h_flags) continue; /* Already seen this time */
                        memset(hardware->h_data, 0, IFNAMSIZ);
                        if_indextoname(master, hardware->h_data);
-                       lldpd_port_cleanup(cfg, &hardware->h_lport, 0);
+                       lldpd_port_cleanup(&hardware->h_lport, 0);
                }
                
                hardware->h_flags = ifa->ifa_flags;
diff --git a/src/daemon/lldp-tlv.h b/src/daemon/lldp-tlv.h
new file mode 100644 (file)
index 0000000..ab24486
--- /dev/null
@@ -0,0 +1,67 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _LLDP_TLV_H
+#define _LLDP_TLV_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_END           0
+#define LLDP_TLV_CHASSIS_ID    1
+#define LLDP_TLV_PORT_ID       2
+#define LLDP_TLV_TTL           3
+#define LLDP_TLV_PORT_DESCR    4
+#define LLDP_TLV_SYSTEM_NAME   5
+#define LLDP_TLV_SYSTEM_DESCR  6
+#define LLDP_TLV_SYSTEM_CAP    7
+#define LLDP_TLV_MGMT_ADDR     8
+#define LLDP_TLV_ORG           127
+
+#define LLDP_TLV_ORG_DOT1      {0x00, 0x80, 0xc2}
+#define LLDP_TLV_ORG_DOT3      {0x00, 0x12, 0x0f}
+#define LLDP_TLV_ORG_MED       {0x00, 0x12, 0xbb}
+
+#define LLDP_TLV_DOT1_PVID     1
+#define LLDP_TLV_DOT1_PPVID    2
+#define LLDP_TLV_DOT1_VLANNAME 3
+#define LLDP_TLV_DOT1_PI       4
+
+#define LLDP_TLV_DOT3_MAC      1
+#define LLDP_TLV_DOT3_POWER    2
+#define LLDP_TLV_DOT3_LA       3
+#define LLDP_TLV_DOT3_MFS      4
+
+#define LLDP_TLV_MED_CAP       1
+#define LLDP_TLV_MED_POLICY    2
+#define LLDP_TLV_MED_LOCATION  3
+#define LLDP_TLV_MED_MDI       4
+#define LLDP_TLV_MED_IV_HW     5
+#define LLDP_TLV_MED_IV_FW     6
+#define LLDP_TLV_MED_IV_SW     7
+#define LLDP_TLV_MED_IV_SN     8
+#define LLDP_TLV_MED_IV_MANUF  9
+#define LLDP_TLV_MED_IV_MODEL  10
+#define        LLDP_TLV_MED_IV_ASSET   11
+
+#endif
similarity index 94%
rename from src/lldp.c
rename to src/daemon/lldp.c
index c58f0d855d7d34deba23e9123dad2a5b9e9d761a..34c385412545dcd43659fb0c66cf1e98a48ba6c8 100644 (file)
@@ -1,3 +1,4 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
 /*
  * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
  *
@@ -324,7 +325,7 @@ lldp_send(struct lldpd *global,
                            goto toobig;                                \
                }
 
-               if (port->p_med_cap_enabled & LLDPMED_CAP_IV) {
+               if (port->p_med_cap_enabled & LLDP_MED_CAP_IV) {
                        LLDP_INVENTORY(chassis->c_med_hw,
                            LLDP_TLV_MED_IV_HW);
                        LLDP_INVENTORY(chassis->c_med_fw,
@@ -342,7 +343,7 @@ lldp_send(struct lldpd *global,
                }
 
                /* LLDP-MED location */
-               for (i = 0; i < LLDPMED_LOCFORMAT_LAST; i++) {
+               for (i = 0; i < LLDP_MED_LOCFORMAT_LAST; i++) {
                        if (port->p_med_location[i].format == i + 1) {
                                if (!(
                                      POKE_START_LLDP_TLV(LLDP_TLV_ORG) &&
@@ -357,7 +358,7 @@ lldp_send(struct lldpd *global,
                }
 
                /* LLDP-MED network policy */
-               for (i = 0; i < LLDPMED_APPTYPE_LAST; i++) {
+               for (i = 0; i < LLDP_MED_APPTYPE_LAST; i++) {
                        if (port->p_med_policy[i].type == i + 1) {
                                if (!(
                                      POKE_START_LLDP_TLV(LLDP_TLV_ORG) &&
@@ -377,8 +378,8 @@ lldp_send(struct lldpd *global,
                }
 
                /* LLDP-MED POE-MDI */
-               if ((port->p_med_power.devicetype == LLDPMED_POW_TYPE_PSE) ||
-                   (port->p_med_power.devicetype == LLDPMED_POW_TYPE_PD)) {
+               if ((port->p_med_power.devicetype == LLDP_MED_POW_TYPE_PSE) ||
+                   (port->p_med_power.devicetype == LLDP_MED_POW_TYPE_PD)) {
                        int devicetype = 0, source = 0;
                        if (!(
                              POKE_START_LLDP_TLV(LLDP_TLV_ORG) &&
@@ -386,21 +387,21 @@ lldp_send(struct lldpd *global,
                              POKE_UINT8(LLDP_TLV_MED_MDI)))
                                goto toobig;
                        switch (port->p_med_power.devicetype) {
-                       case LLDPMED_POW_TYPE_PSE:
+                       case LLDP_MED_POW_TYPE_PSE:
                                devicetype = 0;
                                switch (port->p_med_power.source) {
-                               case LLDPMED_POW_SOURCE_PRIMARY: source = 1; break;
-                               case LLDPMED_POW_SOURCE_BACKUP: source = 2; break;
-                               case LLDPMED_POW_SOURCE_RESERVED: source = 3; break;
+                               case LLDP_MED_POW_SOURCE_PRIMARY: source = 1; break;
+                               case LLDP_MED_POW_SOURCE_BACKUP: source = 2; break;
+                               case LLDP_MED_POW_SOURCE_RESERVED: source = 3; break;
                                default: source = 0; break;
                                }
                                break;
-                       case LLDPMED_POW_TYPE_PD:
+                       case LLDP_MED_POW_TYPE_PD:
                                devicetype = 1;
                                switch (port->p_med_power.source) {
-                               case LLDPMED_POW_SOURCE_PSE: source = 1; break;
-                               case LLDPMED_POW_SOURCE_LOCAL: source = 2; break;
-                               case LLDPMED_POW_SOURCE_BOTH: source = 3; break;
+                               case LLDP_MED_POW_SOURCE_PSE: source = 1; break;
+                               case LLDP_MED_POW_SOURCE_LOCAL: source = 2; break;
+                               case LLDP_MED_POW_SOURCE_BOTH: source = 3; break;
                                default: source = 0; break;
                                }
                                break;
@@ -808,13 +809,13 @@ lldp_decode(struct lldpd *cfg, char *frame, int s,
                                        chassis->c_med_cap_available = PEEK_UINT16;
                                        chassis->c_med_type = PEEK_UINT8;
                                        port->p_med_cap_enabled |=
-                                           LLDPMED_CAP_CAP;
+                                           LLDP_MED_CAP_CAP;
                                        break;
                                case LLDP_TLV_MED_POLICY:
                                        CHECK_TLV_SIZE(8, "LLDP-MED policy");
                                        policy = PEEK_UINT32;
                                        if (((policy >> 24) < 1) ||
-                                           ((policy >> 24) > LLDPMED_APPTYPE_LAST)) {
+                                           ((policy >> 24) > LLDP_MED_APPTYPE_LAST)) {
                                                LLOG_INFO("unknown policy field %d "
                                                    "received on %s",
                                                    policy,
@@ -834,13 +835,13 @@ lldp_decode(struct lldpd *cfg, char *frame, int s,
                                        port->p_med_policy[(policy >> 24) - 1].dscp =
                                            policy & 0x3F;
                                        port->p_med_cap_enabled |=
-                                           LLDPMED_CAP_POLICY;
+                                           LLDP_MED_CAP_POLICY;
                                        break;
                                case LLDP_TLV_MED_LOCATION:
                                        CHECK_TLV_SIZE(5, "LLDP-MED Location");
                                        loctype = PEEK_UINT8;
                                        if ((loctype < 1) ||
-                                           (loctype > LLDPMED_LOCFORMAT_LAST)) {
+                                           (loctype > LLDP_MED_LOCFORMAT_LAST)) {
                                                LLOG_INFO("unknown location type "
                                                    "received on %s",
                                                    hardware->h_ifname);
@@ -860,64 +861,64 @@ lldp_decode(struct lldpd *cfg, char *frame, int s,
                                            tlv_size - 5;
                                        port->p_med_location[loctype - 1].format = loctype;
                                        port->p_med_cap_enabled |=
-                                           LLDPMED_CAP_LOCATION;
+                                           LLDP_MED_CAP_LOCATION;
                                        break;
                                case LLDP_TLV_MED_MDI:
                                        CHECK_TLV_SIZE(7, "LLDP-MED PoE-MDI");
                                        power = PEEK_UINT8;
                                        switch (power & 0xC0) {
                                        case 0x0:
-                                               port->p_med_power.devicetype = LLDPMED_POW_TYPE_PSE;
+                                               port->p_med_power.devicetype = LLDP_MED_POW_TYPE_PSE;
                                                port->p_med_cap_enabled |=
-                                                   LLDPMED_CAP_MDI_PSE;
+                                                   LLDP_MED_CAP_MDI_PSE;
                                                switch (power & 0x30) {
                                                case 0x0:
                                                        port->p_med_power.source =
-                                                           LLDPMED_POW_SOURCE_UNKNOWN;
+                                                           LLDP_MED_POW_SOURCE_UNKNOWN;
                                                        break;
                                                case 0x10:
                                                        port->p_med_power.source =
-                                                           LLDPMED_POW_SOURCE_PRIMARY;
+                                                           LLDP_MED_POW_SOURCE_PRIMARY;
                                                        break;
                                                case 0x20:
                                                        port->p_med_power.source =
-                                                           LLDPMED_POW_SOURCE_BACKUP;
+                                                           LLDP_MED_POW_SOURCE_BACKUP;
                                                        break;
                                                default:
                                                        port->p_med_power.source =
-                                                           LLDPMED_POW_SOURCE_RESERVED;
+                                                           LLDP_MED_POW_SOURCE_RESERVED;
                                                }
                                                break;
                                        case 0x40:
-                                               port->p_med_power.devicetype = LLDPMED_POW_TYPE_PD;
+                                               port->p_med_power.devicetype = LLDP_MED_POW_TYPE_PD;
                                                port->p_med_cap_enabled |=
-                                                   LLDPMED_CAP_MDI_PD;
+                                                   LLDP_MED_CAP_MDI_PD;
                                                switch (power & 0x30) {
                                                case 0x0:
                                                        port->p_med_power.source =
-                                                           LLDPMED_POW_SOURCE_UNKNOWN;
+                                                           LLDP_MED_POW_SOURCE_UNKNOWN;
                                                        break;
                                                case 0x10:
                                                        port->p_med_power.source =
-                                                           LLDPMED_POW_SOURCE_PSE;
+                                                           LLDP_MED_POW_SOURCE_PSE;
                                                        break;
                                                case 0x20:
                                                        port->p_med_power.source =
-                                                           LLDPMED_POW_SOURCE_LOCAL;
+                                                           LLDP_MED_POW_SOURCE_LOCAL;
                                                        break;
                                                default:
                                                        port->p_med_power.source =
-                                                           LLDPMED_POW_SOURCE_BOTH;
+                                                           LLDP_MED_POW_SOURCE_BOTH;
                                                }
                                                break;
                                        default:
                                                port->p_med_power.devicetype =
-                                                   LLDPMED_POW_TYPE_RESERVED;
+                                                   LLDP_MED_POW_TYPE_RESERVED;
                                        }
                                        if (((power & 0x0F) < 0) ||
-                                           ((power & 0x0F) > LLDPMED_POW_PRIO_LOW))
+                                           ((power & 0x0F) > LLDP_MED_POW_PRIO_LOW))
                                                port->p_med_power.priority =
-                                                   LLDPMED_POW_PRIO_UNKNOWN;
+                                                   LLDP_MED_POW_PRIO_UNKNOWN;
                                        else
                                                port->p_med_power.priority =
                                                    power & 0x0F;
@@ -973,7 +974,7 @@ lldp_decode(struct lldpd *cfg, char *frame, int s,
                                                break;
                                        }
                                        port->p_med_cap_enabled |=
-                                           LLDPMED_CAP_IV;
+                                           LLDP_MED_CAP_IV;
                                        break;
                                default:
                                        /* Unknown LLDP MED, ignore it */
@@ -1034,7 +1035,7 @@ lldp_decode(struct lldpd *cfg, char *frame, int s,
        return 1;
 malformed:
        lldpd_chassis_cleanup(chassis, 1);
-       lldpd_port_cleanup(cfg, port, 1);
+       lldpd_port_cleanup(port, 1);
        free(port);
        return -1;
 }
similarity index 90%
rename from src/lldpd.c
rename to src/daemon/lldpd.c
index 1b75738a97c6f1883f2959c6cbdca7c70713b5dd..8ce719c28e53f32a2dc6571b0f57bb2d844436ed 100644 (file)
@@ -1,3 +1,4 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
 /*
  * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
  *
@@ -153,9 +154,9 @@ lldpd_alloc_hardware(struct lldpd *cfg, char *name)
 
 #ifdef ENABLE_LLDPMED
        if (LOCAL_CHASSIS(cfg)->c_med_cap_available) {
-               hardware->h_lport.p_med_cap_enabled = LLDPMED_CAP_CAP;
+               hardware->h_lport.p_med_cap_enabled = LLDP_MED_CAP_CAP;
                if (!cfg->g_noinventory)
-                       hardware->h_lport.p_med_cap_enabled |= LLDPMED_CAP_IV;
+                       hardware->h_lport.p_med_cap_enabled |= LLDP_MED_CAP_IV;
        }
 #endif
 #ifdef ENABLE_DOT1
@@ -168,76 +169,6 @@ lldpd_alloc_hardware(struct lldpd *cfg, char *name)
        return hardware;
 }
 
-#ifdef ENABLE_DOT1
-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_ppvid_cleanup(struct lldpd_port *port)
-{
-       struct lldpd_ppvid *ppvid, *ppvid_next;
-       for (ppvid = TAILQ_FIRST(&port->p_ppvids);
-           ppvid != NULL;
-           ppvid = ppvid_next) {
-               ppvid_next = TAILQ_NEXT(ppvid, p_entries);
-               TAILQ_REMOVE(&port->p_ppvids, ppvid, p_entries);
-               free(ppvid);
-       }
-}
-
-void
-lldpd_pi_cleanup(struct lldpd_port *port)
-{
-       struct lldpd_pi *pi, *pi_next;
-       for (pi = TAILQ_FIRST(&port->p_pids);
-           pi != NULL;
-           pi = pi_next) {
-               free(pi->p_pi);
-               pi_next = TAILQ_NEXT(pi, p_entries);
-               TAILQ_REMOVE(&port->p_pids, pi, p_entries);
-               free(pi);
-       }
-}
-#endif
-
-/* If `all' is true, clear all information, including information that
-   are not refreshed periodically. Port should be freed manually. */
-void
-lldpd_port_cleanup(struct lldpd *cfg, struct lldpd_port *port, int all)
-{
-#ifdef ENABLE_LLDPMED
-       int i;
-       if (all)
-               for (i=0; i < LLDPMED_LOCFORMAT_LAST; i++)
-                       free(port->p_med_location[i].data);
-#endif
-#ifdef ENABLE_DOT1
-       lldpd_vlan_cleanup(port);
-       lldpd_ppvid_cleanup(port);
-       lldpd_pi_cleanup(port);
-#endif
-       free(port->p_id);
-       free(port->p_descr);
-       if (all) {
-               free(port->p_lastframe);
-               if (port->p_chassis) { /* chassis may not have been attributed, yet */
-                       port->p_chassis->c_refcount--;
-                       port->p_chassis = NULL;
-               }
-       }
-}
-
 struct lldpd_mgmt *
 lldpd_alloc_mgmt(int family, void *addrptr, size_t addrsize, u_int32_t iface)
 {
@@ -264,66 +195,10 @@ lldpd_alloc_mgmt(int family, void *addrptr, size_t addrsize, u_int32_t iface)
        return mgmt;
 }
 
-void
-lldpd_chassis_mgmt_cleanup(struct lldpd_chassis *chassis)
-{
-       struct lldpd_mgmt *mgmt, *mgmt_next;
-       for (mgmt = TAILQ_FIRST(&chassis->c_mgmt);
-            mgmt != NULL;
-            mgmt = mgmt_next) {
-               mgmt_next = TAILQ_NEXT(mgmt, m_entries);
-               TAILQ_REMOVE(&chassis->c_mgmt, mgmt, m_entries);
-               free(mgmt);
-       }
-}
-
-void
-lldpd_chassis_cleanup(struct lldpd_chassis *chassis, int all)
-{
-#ifdef ENABLE_LLDPMED
-       free(chassis->c_med_hw);
-       free(chassis->c_med_sw);
-       free(chassis->c_med_fw);
-       free(chassis->c_med_sn);
-       free(chassis->c_med_manuf);
-       free(chassis->c_med_model);
-       free(chassis->c_med_asset);
-#endif
-       free(chassis->c_id);
-       free(chassis->c_name);
-       free(chassis->c_descr);
-       lldpd_chassis_mgmt_cleanup(chassis);
-       if (all)
-               free(chassis);
-}
-
-void
-lldpd_remote_cleanup(struct lldpd *cfg, struct lldpd_hardware *hardware, int all)
-{
-       struct lldpd_port *port, *port_next;
-       int del;
-       for (port = TAILQ_FIRST(&hardware->h_rports);
-            port != NULL;
-            port = port_next) {
-               port_next = TAILQ_NEXT(port, p_entries);
-               del = all;
-               if (!del &&
-                   (time(NULL) - port->p_lastupdate > port->p_chassis->c_ttl)) {
-                       hardware->h_rx_ageout_cnt++;
-                       del = 1;
-               }
-               if (del) {
-                       TAILQ_REMOVE(&hardware->h_rports, port, p_entries);
-                       lldpd_port_cleanup(cfg, port, 1);
-                       free(port);
-               }
-       }
-}
-
 void
 lldpd_hardware_cleanup(struct lldpd *cfg, struct lldpd_hardware *hardware)
 {
-       lldpd_port_cleanup(cfg, &hardware->h_lport, 1);
+       lldpd_port_cleanup(&hardware->h_lport, 1);
        if (hardware->h_ops->cleanup)
                hardware->h_ops->cleanup(cfg, hardware);
        levent_hardware_release(hardware);
@@ -341,10 +216,10 @@ lldpd_cleanup(struct lldpd *cfg)
                hardware_next = TAILQ_NEXT(hardware, h_entries);
                if (!hardware->h_flags) {
                        TAILQ_REMOVE(&cfg->g_hardware, hardware, h_entries);
-                       lldpd_remote_cleanup(cfg, hardware, 1);
+                       lldpd_remote_cleanup(hardware, 1);
                        lldpd_hardware_cleanup(cfg, hardware);
                } else
-                       lldpd_remote_cleanup(cfg, hardware, 0);
+                       lldpd_remote_cleanup(hardware, 0);
        }
 
        for (chassis = TAILQ_FIRST(&cfg->g_chassis); chassis;
@@ -495,7 +370,7 @@ lldpd_decode(struct lldpd *cfg, char *frame, int s,
        if (oport) {
                /* The port is known, remove it before adding it back */
                TAILQ_REMOVE(&hardware->h_rports, oport, p_entries);
-               lldpd_port_cleanup(cfg, oport, 1);
+               lldpd_port_cleanup(oport, 1);
                free(oport);
        }
        if (ochassis) {
@@ -974,7 +849,7 @@ lldpd_exit(struct lldpd *cfg)
        for (hardware = TAILQ_FIRST(&cfg->g_hardware); hardware != NULL;
             hardware = hardware_next) {
                hardware_next = TAILQ_NEXT(hardware, h_entries);
-               lldpd_remote_cleanup(cfg, hardware, 1);
+               lldpd_remote_cleanup(hardware, 1);
                lldpd_hardware_cleanup(cfg, hardware);
        }
 }
@@ -1246,12 +1121,12 @@ lldpd_main(int argc, char *argv[])
        TAILQ_INIT(&lchassis->c_mgmt);
 #ifdef ENABLE_LLDPMED
        if (lldpmed > 0) {
-               if (lldpmed == LLDPMED_CLASS_III)
+               if (lldpmed == LLDP_MED_CLASS_III)
                        lchassis->c_cap_available |= LLDP_CAP_TELEPHONE;
                lchassis->c_med_type = lldpmed;
-               lchassis->c_med_cap_available = LLDPMED_CAP_CAP |
-                   LLDPMED_CAP_IV | LLDPMED_CAP_LOCATION |
-                   LLDPMED_CAP_POLICY | LLDPMED_CAP_MDI_PSE | LLDPMED_CAP_MDI_PD;
+               lchassis->c_med_cap_available = LLDP_MED_CAP_CAP |
+                   LLDP_MED_CAP_IV | LLDP_MED_CAP_LOCATION |
+                   LLDP_MED_CAP_POLICY | LLDP_MED_CAP_MDI_PSE | LLDP_MED_CAP_MDI_PD;
                cfg->g_noinventory = noinventory;
        } else
                cfg->g_noinventory = 1;
diff --git a/src/daemon/lldpd.h b/src/daemon/lldpd.h
new file mode 100644 (file)
index 0000000..25135a5
--- /dev/null
@@ -0,0 +1,257 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _LLDPD_H
+#define _LLDPD_H
+
+#if HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#ifdef HAVE_VALGRIND_VALGRIND_H
+# include <valgrind/valgrind.h>
+#else
+# define RUNNING_ON_VALGRIND 0
+#endif
+
+#define _GNU_SOURCE 1
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <sys/queue.h>
+#ifdef HAVE_SYS_TYPES_H
+#  include <sys/types.h>
+#endif
+#ifndef INCLUDE_LINUX_IF_H
+#  include <net/if.h>
+#else
+#  include <arpa/inet.h>
+#  include <linux/if.h>
+#endif
+#if HAVE_GETIFADDRS
+#  include <ifaddrs.h>
+#endif
+#include <net/ethernet.h>
+#include <netinet/in.h>
+#include <linux/ethtool.h>
+#include <sys/un.h>
+
+#include "lldp-tlv.h"
+#if defined (ENABLE_CDP) || defined (ENABLE_FDP)
+#  include "cdp.h"
+#endif
+#ifdef ENABLE_SONMP
+#  include "sonmp.h"
+#endif
+#ifdef ENABLE_EDP
+#  include "edp.h"
+#endif
+
+#include "../compat/compat.h"
+#include "../marshal.h"
+#include "../log.h"
+#include "../ctl.h"
+#include "../lldpd-structs.h"
+
+/* We don't want to import event2/event.h. We only need those as
+   opaque structs. */
+struct event;
+struct event_base;
+
+#define SYSFS_CLASS_NET "/sys/class/net/"
+#define SYSFS_CLASS_DMI "/sys/class/dmi/id/"
+#define LLDPD_TTL              120
+#define LLDPD_TX_DELAY         30
+#define LLDPD_TX_MSGDELAY      1
+#define LLDPD_PID_FILE         "/var/run/lldpd.pid"
+
+#define USING_AGENTX_SUBAGENT_MODULE 1
+
+#define PROTO_SEND_SIG struct lldpd *, 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 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 */
+};
+
+/* Smart mode / Hide mode */
+#define SMART_INCOMING_FILTER          (1<<0) /* Incoming filtering enabled */
+#define SMART_INCOMING_ONE_PROTO       (1<<1) /* On reception, keep only one proto */
+#define SMART_INCOMING_ONE_NEIGH       (1<<2) /* On reception, keep only one neighbor */
+#define SMART_OUTGOING_FILTER          (1<<3) /* Outgoing filtering enabled */
+#define SMART_OUTGOING_ONE_PROTO       (1<<4) /* On emission, keep only one proto */
+#define SMART_OUTGOING_ONE_NEIGH       (1<<5) /* On emission, consider only one neighbor */
+#define SMART_INCOMING (SMART_INCOMING_FILTER |    \
+                        SMART_INCOMING_ONE_PROTO | \
+                        SMART_INCOMING_ONE_NEIGH)
+#define SMART_OUTGOING (SMART_OUTGOING_FILTER |                \
+                       SMART_OUTGOING_ONE_PROTO |      \
+                       SMART_OUTGOING_ONE_NEIGH)
+#define SMART_HIDDEN(port) (port->p_hidden_in)
+
+struct lldpd {
+       int                      g_sock;
+       int                      g_delay;
+
+       struct event_base       *g_base;
+#ifdef USE_SNMP
+#endif
+
+       struct protocol         *g_protocols;
+       time_t                   g_lastsent;
+       int                      g_lastrid;
+       int                      g_smart;
+       int                      g_receiveonly;
+       struct event            *g_main_loop;
+#ifdef USE_SNMP
+       int                      g_snmp;
+       struct event            *g_snmp_timeout;
+       void                    *g_snmp_fds;
+       char                    *g_snmp_agentx;
+#endif /* USE_SNMP */
+
+       /* Unix socket handling */
+       int                      g_ctl;
+       struct event            *g_ctl_event;
+
+       char                    *g_mgmt_pattern;
+       char                    *g_cid_pattern;
+       char                    *g_interfaces;
+
+       char                    *g_descr_override;
+       char                    *g_platform_override;
+       char                    *g_lsb_release;
+       int                      g_advertise_version;
+#ifdef ENABLE_LLDPMED
+       int                      g_noinventory;
+#endif
+
+#define LOCAL_CHASSIS(cfg) ((struct lldpd_chassis *)(TAILQ_FIRST(&cfg->g_chassis)))
+       TAILQ_HEAD(, lldpd_chassis) g_chassis;
+       TAILQ_HEAD(, lldpd_hardware) g_hardware;
+};
+
+typedef void(*lldpd_ifhandlers)(struct lldpd *, struct ifaddrs *);
+
+/* lldpd.c */
+struct lldpd_hardware  *lldpd_get_hardware(struct lldpd *,
+    char *, int, struct lldpd_ops *);
+struct lldpd_hardware  *lldpd_alloc_hardware(struct lldpd *, char *);
+void    lldpd_hardware_cleanup(struct lldpd*, struct lldpd_hardware *);
+struct lldpd_mgmt *lldpd_alloc_mgmt(int family, void *addr, size_t addrsize, u_int32_t iface);
+void    lldpd_recv(struct lldpd *, struct lldpd_hardware *, int);
+void    lldpd_loop(struct lldpd *);
+int     lldpd_main(int, char **);
+
+/* event.c */
+void    levent_loop(struct lldpd *);
+void    levent_hardware_init(struct lldpd_hardware *);
+void    levent_hardware_add_fd(struct lldpd_hardware *, int);
+void    levent_hardware_release(struct lldpd_hardware *);
+
+/* lldp.c */
+int     lldp_send(PROTO_SEND_SIG);
+int     lldp_decode(PROTO_DECODE_SIG);
+
+/* cdp.c */
+#ifdef ENABLE_CDP
+int     cdpv1_send(PROTO_SEND_SIG);
+int     cdpv2_send(PROTO_SEND_SIG);
+int     cdpv1_guess(PROTO_GUESS_SIG);
+int     cdpv2_guess(PROTO_GUESS_SIG);
+#endif
+#if defined (ENABLE_CDP) || defined (ENABLE_FDP)
+int     cdp_decode(PROTO_DECODE_SIG);
+#endif
+#ifdef ENABLE_FDP
+int     fdp_send(PROTO_SEND_SIG);
+#endif
+
+#ifdef ENABLE_SONMP
+/* sonmp.c */
+int     sonmp_send(PROTO_SEND_SIG);
+int     sonmp_decode(PROTO_DECODE_SIG);
+#endif
+
+#ifdef ENABLE_EDP
+/* edp.c */
+int     edp_send(PROTO_SEND_SIG);
+int     edp_decode(PROTO_DECODE_SIG);
+#endif
+
+/* interfaces.c */
+void    lldpd_ifh_whitelist(struct lldpd *, struct ifaddrs *);
+void    lldpd_ifh_bond(struct lldpd *, struct ifaddrs *);
+void    lldpd_ifh_eth(struct lldpd *, struct ifaddrs *);
+#ifdef ENABLE_DOT1
+void    lldpd_ifh_vlan(struct lldpd *, struct ifaddrs *);
+#endif
+void    lldpd_ifh_mgmt(struct lldpd *, struct ifaddrs *);
+void    lldpd_ifh_chassis(struct lldpd *, struct ifaddrs *);
+
+/* dmi.c */
+#ifdef ENABLE_LLDPMED
+#if __i386__ || __amd64__
+char   *dmi_hw(void);
+char   *dmi_fw(void);
+char   *dmi_sn(void);
+char   *dmi_manuf(void);
+char   *dmi_model(void);
+char   *dmi_asset(void);
+#endif
+#endif
+
+/* agent.c */
+void            agent_shutdown(void);
+void            agent_init(struct lldpd *, char *);
+
+/* agent_priv.c */
+void            agent_priv_register_domain(void);
+
+/* client.c */
+struct client_handle {
+       enum hmsg_type type;
+       int (*handle)(struct lldpd*, enum hmsg_type *,
+           void *, int, void **);
+};
+
+int     client_handle_client(struct lldpd *, int,
+    enum hmsg_type, void *, int);
+
+/* priv.c */
+void    priv_init(char*, int, uid_t, gid_t);
+void    priv_ctl_cleanup(void);
+char           *priv_gethostbyname(void);
+int             priv_open(char*);
+int             priv_ethtool(char*, struct ethtool_cmd*);
+int             priv_iface_init(const char *);
+int     priv_iface_multicast(const char *, u_int8_t *, int);
+int     priv_snmp_socket(struct sockaddr_un *);
+
+/* privsep_fdpass.c */
+int     receive_fd(int);
+void    send_fd(int, int);
+
+#endif /* _LLDPD_H */
similarity index 65%
rename from src/main.c
rename to src/daemon/main.c
index 5670e9fa1a511dcefd1c4103cf6f79f5a20feb7e..e23fe45f9c52d2b523c6de21b51f1e37df899d8d 100644 (file)
@@ -1,3 +1,4 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
 #include "lldpd.h"
 
 int
similarity index 99%
rename from src/priv.c
rename to src/daemon/priv.c
index bbd6ee008b9ce8e70bae8e702f58d7868a21a04a..000c5769232128059a6e774ba09ce685894b4165 100644 (file)
@@ -1,3 +1,4 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
 /*
  * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
  *
similarity index 98%
rename from src/privsep_fdpass.c
rename to src/daemon/privsep_fdpass.c
index 6b004806a9bb902fb89ac9170d70be919a4a50ea..5f19d6713ddad0f835b61c79581de9f23bcd7b7b 100644 (file)
@@ -1,3 +1,4 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
 /*
  * Copyright 2001 Niels Provos <provos@citi.umich.edu>
  * All rights reserved.
similarity index 99%
rename from src/sonmp.c
rename to src/daemon/sonmp.c
index ba905d9300bd9f5e047a241a1555122b100d183e..541c682cbb0eeca0e1cb82d0c83297b74c57e9c0 100644 (file)
@@ -1,3 +1,4 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
 /*
  * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
  *
@@ -397,7 +398,7 @@ sonmp_decode(struct lldpd *cfg, char *frame, int s,
 
 malformed:
        lldpd_chassis_cleanup(chassis, 1);
-       lldpd_port_cleanup(cfg, port, 1);
+       lldpd_port_cleanup(port, 1);
        free(port);
        return -1;
 }
similarity index 96%
rename from src/sonmp.h
rename to src/daemon/sonmp.h
index 68502dc776025008788f131ecca8e7e889c5cad4..d2b1541fe644e08cb845cc4762f08d8c027ad517 100644 (file)
@@ -1,3 +1,4 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
 /*
  * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
  *
diff --git a/src/display.c b/src/display.c
deleted file mode 100644 (file)
index 892ebed..0000000
+++ /dev/null
@@ -1,1015 +0,0 @@
-/*
- * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "lldpctl.h"
-
-#include <stdio.h>
-#include <unistd.h>
-#include <time.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <arpa/inet.h>
-
-TAILQ_HEAD(interfaces, lldpd_interface);
-
-#define ntohll(x) (((u_int64_t)(ntohl((int)((x << 32) >> 32))) << 32) |        \
-           (unsigned int)ntohl(((int)(x >> 32))))
-#define htonll(x) ntohll(x)
-
-struct value_string {
-       int value;
-       char *string;
-};
-
-static const struct value_string lldpd_protocol_map[] = {
-       { LLDPD_MODE_LLDP,      "LLDP" },
-       { LLDPD_MODE_CDPV1,     "CDPv1"},
-       { LLDPD_MODE_CDPV2,     "CDPv2"},
-       { LLDPD_MODE_EDP,       "EDP" },
-       { LLDPD_MODE_FDP,       "FDP"},
-       { LLDPD_MODE_SONMP,     "SONMP"},
-        { 0, NULL }
-};
-
-static const struct value_string chassis_id_subtype_map[] = {
-       { LLDP_CHASSISID_SUBTYPE_IFNAME,  "ifname"},
-       { LLDP_CHASSISID_SUBTYPE_IFALIAS, "ifalias" },
-       { LLDP_CHASSISID_SUBTYPE_LOCAL,   "local" },
-       { LLDP_CHASSISID_SUBTYPE_LLADDR,  "mac" },
-       { LLDP_CHASSISID_SUBTYPE_ADDR,    "ip" },
-       { LLDP_CHASSISID_SUBTYPE_PORT,    "unhandled" },
-       { LLDP_CHASSISID_SUBTYPE_CHASSIS, "unhandled" },
-       { 0, NULL},
-};
-
-static const struct value_string port_id_subtype_map[] = {
-       { LLDP_PORTID_SUBTYPE_IFNAME,   "ifname"},
-       { LLDP_PORTID_SUBTYPE_IFALIAS,  "ifalias" },
-       { LLDP_PORTID_SUBTYPE_LOCAL,    "local" },
-       { LLDP_PORTID_SUBTYPE_LLADDR,   "mac" },
-       { LLDP_PORTID_SUBTYPE_ADDR,     "ip" },
-       { LLDP_PORTID_SUBTYPE_PORT,     "unhandled" },
-       { LLDP_PORTID_SUBTYPE_AGENTCID, "unhandled" },
-       { 0, NULL},
-};
-
-#ifdef ENABLE_LLDPMED
-static const struct value_string chassis_med_type_map[] = {
-       { LLDPMED_CLASS_I,        "Generic Endpoint (Class I)" },
-       { LLDPMED_CLASS_II,       "Media Endpoint (Class II)" },
-       { LLDPMED_CLASS_III,      "Communication Device Endpoint (Class III)" },
-       { LLDPMED_NETWORK_DEVICE, "Network Connectivity Device" },
-       { 0, NULL },
-};
-
-static const struct value_string lldpmed_capabilit_map[] = {
-       {LLDPMED_CAP_CAP,       "Capabilities"},
-       {LLDPMED_CAP_POLICY,    "Policy"},
-       {LLDPMED_CAP_LOCATION,  "Location"},
-       {LLDPMED_CAP_MDI_PSE,   "MDI/PSE"},
-       {LLDPMED_CAP_MDI_PD,    "MDI/PD"},
-       {LLDPMED_CAP_IV,        "Inventory"},
-       { 0, NULL },
-};
-
-static const struct value_string port_med_policy_map[] = {
-       { LLDPMED_APPTYPE_VOICE ,           "Voice"},
-       { LLDPMED_APPTYPE_VOICESIGNAL,      "Voice Signaling"},
-       { LLDPMED_APPTYPE_GUESTVOICE,       "Guest Voice"},
-       { LLDPMED_APPTYPE_GUESTVOICESIGNAL, "Guest Voice Signaling"},
-       { LLDPMED_APPTYPE_SOFTPHONEVOICE,   "Softphone Voice"},
-       { LLDPMED_APPTYPE_VIDEOCONFERENCE,  "Video Conferencing"},
-       { LLDPMED_APPTYPE_VIDEOSTREAM,      "Streaming Video"},
-       { LLDPMED_APPTYPE_VIDEOSIGNAL,      "Video Signaling"},
-       { 0, NULL },
-};
-
-static const struct value_string civic_address_type_values[] = {
-        { 0,    "Language" },
-        { 1,    "National subdivisions" },
-        { 2,    "County, parish, district" },
-        { 3,    "City, township" },
-        { 4,    "City division, borough, ward" },
-        { 5,    "Neighborhood, block" },
-        { 6,    "Street" },
-        { 16,   "Leading street direction" },
-        { 17,   "Trailing street suffix" },
-        { 18,   "Street suffix" },
-        { 19,   "House number" },
-        { 20,   "House number suffix" },
-        { 21,   "Landmark or vanity address" },
-        { 22,   "Additional location info" },
-        { 23,   "Name" },
-        { 24,   "Postal/ZIP code" },
-        { 25,   "Building" },
-        { 26,   "Unit" },
-        { 27,   "Floor" },
-        { 28,   "Room number" },
-        { 29,   "Place type" },
-        { 128,  "Script" },
-        { 0, NULL }
-};
-
-static const struct value_string civic_address_type_tags[] = {
-        { 0,    "language" },
-        { 1,    "country-subdivision" },
-        { 2,    "county" },
-        { 3,    "city" },
-        { 4,    "city-division" },
-        { 5,    "block" },
-        { 6,    "street" },
-        { 16,   "direction" },
-        { 17,   "street-suffix" },
-        { 18,   "street-suffix" },
-        { 19,   "number" },
-        { 20,   "number-suffix" },
-        { 21,   "landmark" },
-        { 22,   "additional" },
-        { 23,   "name" },
-        { 24,   "zip" },
-        { 25,   "building" },
-        { 26,   "unit" },
-        { 27,   "floor" },
-        { 28,   "room" },
-        { 29,   "place-type" },
-        { 128,  "Script" },
-        { 0, NULL }
-};
-
-static const struct value_string port_med_geoid_map[] = {
-       { 1, "WGS84" },
-       { 2, "NAD83" },
-       { 3, "NAD83/MLLW" },
-       { 0, NULL },
-};
-
-static const struct value_string port_med_pow_devicetype_map[] = {
-       { LLDPMED_POW_TYPE_PSE, "PSE Device" },
-       { LLDPMED_POW_TYPE_PD,  "PD Device" },
-       { 0, NULL },
-};
-
-static const struct value_string port_med_pow_source_map[] = {
-       { LLDPMED_POW_SOURCE_PRIMARY, "Primary Power Source" },
-       { LLDPMED_POW_SOURCE_BACKUP,  "Backup Power Source / Power Conservation Mode" },
-       { LLDPMED_POW_SOURCE_PSE,     "PSE" },
-       { LLDPMED_POW_SOURCE_LOCAL,   "Local"},
-       { LLDPMED_POW_SOURCE_BOTH,    "PSE + Local"},
-       { 0, NULL },
-};
-
-static const struct value_string port_med_pow_priority_map[] = {
-       { LLDPMED_POW_PRIO_CRITICAL, "critical" },
-       { LLDPMED_POW_PRIO_HIGH,     "high" },
-       { LLDPMED_POW_PRIO_LOW,      "low" },
-       { 0, NULL },
-};
-#endif
-
-#ifdef ENABLE_DOT3
-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" },
-       { 41,   "10GigBaseCX4 - X copper over 8 pair 100-Ohm balanced cable" },
-       { 42,   "2BaseTL - Voice grade UTP copper, up to 2700m, optional PAF" },
-       { 43,   "10PassTS - Voice grade UTP copper, up to 750m, optional PAF" },
-       { 44,   "100BaseBX10D - One single-mode fiber OLT, long wavelength, 10km" },
-       { 45,   "100BaseBX10U - One single-mode fiber ONU, long wavelength, 10km" },
-       { 46,   "100BaseLX10 - Two single-mode fibers, long wavelength, 10km" },
-       { 47,   "1000BaseBX10D - One single-mode fiber OLT, long wavelength, 10km" },
-       { 48,   "1000BaseBX10U - One single-mode fiber ONU, long wavelength, 10km" },
-       { 49,   "1000BaseLX10 - Two sigle-mode fiber, long wavelength, 10km" },
-       { 50,   "1000BasePX10D - One single-mode fiber EPON OLT, 10km" },
-       { 51,   "1000BasePX10U - One single-mode fiber EPON ONU, 10km" },
-       { 52,   "1000BasePX20D - One single-mode fiber EPON OLT, 20km" },
-       { 53,   "1000BasePX20U - One single-mode fiber EPON ONU, 20km" },
-       { 0, NULL }
-};
-
-static const struct value_string port_dot3_power_devicetype_map[] = {
-       { LLDP_DOT3_POWER_PSE, "PSE" },
-       { LLDP_DOT3_POWER_PD,  "PD" },
-       { 0, NULL }
-};
-
-static const struct value_string port_dot3_power_pairs_map[] = {
-       { LLDP_DOT3_POWERPAIRS_SIGNAL, "signal" },
-       { LLDP_DOT3_POWERPAIRS_SPARE,  "spare" },
-       { 0, NULL }
-};
-
-static const struct value_string port_dot3_power_class_map[] = {
-       { 1, "class 0" },
-       { 2, "class 1" },
-       { 3, "class 2" },
-       { 4, "class 3" },
-       { 5, "class 4" },
-       { 0, NULL }
-};
-
-static const struct value_string port_dot3_power_pse_source_map[] = {
-       { LLDP_DOT3_POWER_SOURCE_BOTH, "PSE + Local" },
-       { LLDP_DOT3_POWER_SOURCE_PSE, "PSE" },
-       { 0, NULL }
-};
-
-static const struct value_string port_dot3_power_pd_source_map[] = {
-       { LLDP_DOT3_POWER_SOURCE_BACKUP, "Backup source" },
-       { LLDP_DOT3_POWER_SOURCE_PRIMARY, "Primary power source" },
-       { 0, NULL }
-};
-
-static const struct value_string port_dot3_power_priority_map[] = {
-       { LLDPMED_POW_PRIO_CRITICAL, "critical" },
-       { LLDPMED_POW_PRIO_HIGH,     "high" },
-       { LLDPMED_POW_PRIO_LOW,      "low" },
-       { 0, NULL },
-};
-#endif
-
-static const struct value_string chassis_capability_map[] = {
-       { LLDP_CAP_OTHER,    "Other" },
-       { LLDP_CAP_REPEATER, "Repeater"},
-       { LLDP_CAP_BRIDGE,   "Bridge"},
-       { LLDP_CAP_ROUTER,   "Router"},
-       { LLDP_CAP_WLAN,     "Wlan"},
-       { LLDP_CAP_TELEPHONE,"Telephone"},
-       { LLDP_CAP_DOCSIS,   "Docsis"},
-       { LLDP_CAP_STATION,  "Station"},
-       { 0, NULL},
-};
-
-
-static const char*
-map_lookup(const struct value_string * list, int n)
-{
-
-       unsigned int i;
-
-       for( i = 0; list[i].string != NULL; i ++ ) {
-               if( list[i].value == n ) {
-                       return list[i].string;
-               }
-       }
-
-       return "unknown";
-}
-
-static char*
-u2str(unsigned n)
-{
-       static char buf[21];
-       snprintf(buf, sizeof(buf), "%u", n);
-       return buf;
-}
-
-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;
-}
-
-static void
-display_cap(struct writer * w, struct lldpd_chassis *chassis, u_int8_t bit, char *symbol)
-{
-       if (chassis->c_cap_available & bit) {
-               tag_start(w, "capability", "Capability");
-               tag_attr (w, "type", "", symbol );
-               tag_attr (w, "enabled", "", (chassis->c_cap_enabled & bit)?"on":"off"); 
-               tag_end  (w);
-       }
-}
-
-#ifdef ENABLE_LLDPMED
-static int
-display_fixed_precision(u_int64_t value, int intpart, int floatpart, int displaysign, char ** res)
-{
-       static char buf[64]; 
-       u_int64_t tmp = value;
-       int negative = 0;
-       u_int32_t integer = 0;
-       if (value & (1ULL<<(intpart + floatpart - 1))) {
-               negative = 1;
-               tmp = ~value;
-               tmp += 1;
-       }
-       integer = (u_int32_t)((tmp &
-               (((1ULL << intpart)-1) << floatpart)) >> floatpart);
-       tmp = (tmp & ((1<< floatpart) - 1))*10000/(1ULL << floatpart);
-       snprintf(buf, sizeof(buf),"%s%u.%04llu", displaysign?(negative?"-":"+"):"",
-           integer, (unsigned long long int)tmp);
-
-       *res = buf;
-
-       return negative;
-}
-
-static void
-display_latitude_or_longitude(struct writer *w, int option, u_int64_t value)
-{
-       static char buf[70]; 
-       int negative;
-       char * str;
-
-       if ( option == 0 ) {
-               tag_start(w, "lat", "Latitude");
-       } else {
-               tag_start(w, "lon", "Longitude");
-       }
-       negative = display_fixed_precision(value, 9, 25, 0, &str);
-       if (option == 0)
-               snprintf(buf, sizeof(buf), "%s %s", str, negative?" S":" N");
-       else
-               snprintf(buf, sizeof(buf), "%s %s", str, negative?" W":" E");
-
-       tag_data(w, buf);
-       tag_end(w);
-}
-
-static void
-display_med_capability(struct writer *w, struct lldpd_chassis *chassis, int cap)
-{
-       if (chassis->c_med_cap_available & cap) {
-               tag_start(w, "capability", "Capability");
-               tag_attr(w, "type", "",
-                       map_lookup(lldpmed_capabilit_map, cap));
-               tag_end(w);
-       }
-}
-
-static void
-display_med(struct writer *w, struct lldpd_chassis *chassis, struct lldpd_port *port)
-{
-       int i;
-       char *value;
-
-       tag_start(w, "lldp-med", "LLDP-MED");
-
-       tag_datatag(w, "device-type", "Device Type",
-               map_lookup(chassis_med_type_map, chassis->c_med_type));
-
-       display_med_capability(w, chassis, LLDPMED_CAP_CAP);
-       display_med_capability(w, chassis, LLDPMED_CAP_POLICY);
-       display_med_capability(w, chassis, LLDPMED_CAP_LOCATION);
-       display_med_capability(w, chassis, LLDPMED_CAP_MDI_PSE);
-       display_med_capability(w, chassis, LLDPMED_CAP_MDI_PD);
-       display_med_capability(w, chassis, LLDPMED_CAP_IV);
-
-       for (i = 0; i < LLDPMED_APPTYPE_LAST; i++) {
-               if (i+1 == port->p_med_policy[i].type) {
-                       tag_start(w, "policy", "LLDP-MED Network Policy for");
-                       tag_attr(w, "apptype", "AppType",
-                                u2str(port->p_med_policy[i].type));
-                       tag_attr(w, "defined", "Defined",
-                                (port->p_med_policy[i].unknown)?"no":"yes");
-
-                       tag_datatag(w, "descr", "",
-                           map_lookup(port_med_policy_map, port->p_med_policy[i].type));
-
-                       if (port->p_med_policy[i].tagged) {
-                               tag_start(w, "vlan", "VLAN");
-                               if (port->p_med_policy[i].vid == 0) {
-                                       tag_attr(w, "vid", "", "priority");
-                               } else if (port->p_med_policy[i].vid == 4095) {
-                                       tag_attr(w, "vid", "", "reserved");
-                               } else {
-                                       tag_attr(w, "vid", "",
-                                                u2str(port->p_med_policy[i].vid));
-                               }
-                               tag_end(w);
-                       }
-
-                       tag_datatag(w, "priority", "Layer 2 Priority",
-                                   u2str(port->p_med_policy[i].priority));
-
-                       tag_datatag(w, "dscp", "DSCP Value",
-                                   u2str(port->p_med_policy[i].dscp));
-
-                       tag_end(w);
-               }
-       }
-       for (i = 0; i < LLDPMED_LOCFORMAT_LAST; i++) {
-               if (i+1 == port->p_med_location[i].format) {
-                       tag_start(w, "location", "LLDP-MED Location Identification");
-
-                       switch(port->p_med_location[i].format) {
-                       case LLDPMED_LOCFORMAT_COORD:
-                               tag_attr(w, "type", "Type", "coordinates");
-
-                               if (port->p_med_location[i].data_len != 16) {
-                                       tag_datatag(w, "error", "Error", "bad data length");
-                               } else {
-                                       u_int64_t l;
-                                       u_int8_t  v;
-                                       char *    s;
-
-                                       v = *(u_int8_t*)(port->p_med_location[i].data + 15);
-                                       tag_attr(w, "geoid", "Geoid",
-                                                map_lookup(port_med_geoid_map,v));
-
-                                       /* Latitude and longitude */
-                                       memcpy(&l, port->p_med_location[i].data,
-                                           sizeof(u_int64_t));
-                                       l = (ntohll(l) &
-                                           0x03FFFFFFFF000000ULL) >> 24;
-                                       display_latitude_or_longitude(w,0, l);
-                                       memcpy(&l, port->p_med_location[i].data + 5,
-                                           sizeof(u_int64_t));
-                                       l = (ntohll(l) &
-                                           0x03FFFFFFFF000000ULL) >> 24;
-                                       display_latitude_or_longitude(w,1, l);
-
-                                       /* Altitude */
-                                       memcpy(&l, port->p_med_location[i].data + 10,
-                                           sizeof(u_int64_t));
-                                       l = (ntohll(l) &
-                                           0x3FFFFFFF000000ULL) >> 24;
-                                       display_fixed_precision(l, 22, 8, 1, &s);
-
-                                       tag_start(w, "altitude", "Altitude");
-                                       switch ((*(u_int8_t*)(port->p_med_location[i].data +
-                                                   10)) & 0xf0) {
-                                       case (1 << 4):
-                                               tag_attr(w, "unit", "", "m");
-                                               break;
-                                       case (2 << 4):
-                                               tag_attr(w, "unit", "", "floor");
-                                               break;
-                                       default:
-                                               tag_attr(w, "unit", "", "unknown");
-                                       }
-                                       tag_data(w,s);
-                                       tag_end(w);
-
-                               }
-                               break;
-                       case LLDPMED_LOCFORMAT_CIVIC:
-                               tag_attr(w, "type", "Type", "address");
-
-                               if ((port->p_med_location[i].data_len < 3) ||
-                                   (port->p_med_location[i].data_len - 1 !=
-                                       *(u_int8_t*)port->p_med_location[i].data)) {
-                                       tag_datatag(w, "error", "Error", "bad data length");
-                               } else {
-                                       int l = 4, n, catype, calength; 
-                                       char country[3];
-                                       country[0] = ((char *)port->p_med_location[i].data)[2];
-                                       country[1] = ((char *)port->p_med_location[i].data)[3];
-                                       country[2] = 0;
-
-                                       tag_datatag(w, "country", "Country", country);
-
-                                       while ((n = (port->
-                                                   p_med_location[i].data_len - l)) >= 2) {
-                                               catype = *(u_int8_t*)(port->
-                                                   p_med_location[i].data + l);
-                                               calength = *(u_int8_t*)(port->
-                                                   p_med_location[i].data + l + 1);
-                                               if (n < 2 + calength) {
-                                                       tag_datatag(w, "error", "Error", "bad data length");
-                                                       break;
-                                               }
-
-                                               if ((value = strndup((char *)(port->
-                                                       p_med_location[i].data + l + 2),
-                                                           calength)) == NULL) {
-                                                       fatalx("not enough memory");
-                                                       break;
-                                               }
-                                               tag_datatag(w,
-                                                       map_lookup(civic_address_type_tags,catype),
-                                                       map_lookup(civic_address_type_values,catype),
-                                                       value);
-                                               free(value);
-                                               l += 2 + calength;
-                                       }
-                               }
-                               break;
-                       case LLDPMED_LOCFORMAT_ELIN:
-                               if ((value = strndup((char *)(port->
-                                               p_med_location[i].data),
-                                           port->p_med_location[i].data_len)) == NULL) {
-                                       fatalx( "not enough memory");
-                                       break;
-                               }
-                               tag_attr(w, "type", "Type", "elin");
-                               tag_datatag(w, "ecs", "ECS ELIN", value);
-                               free(value);
-                               break;
-                       default:
-                               tag_attr(w, "type", "", "unknown");
-                               tag_datatag(w, "unknown", "Data",
-                                       dump(port->p_med_location[i].data,
-                                            port->p_med_location[i].data_len, 20, ' '));
-                       }
-                       tag_end(w);
-               }
-       }
-       if (port->p_med_power.devicetype) {
-               tag_start(w, "poe", "Extended Power-over-Ethernet");
-
-               tag_start(w, "device-type", "Power Type & Source");
-               tag_data(w, map_lookup(port_med_pow_devicetype_map, port->p_med_power.devicetype));
-               tag_end(w);
-
-               tag_start(w, "source", "Power Source");
-               tag_data(w, map_lookup(port_med_pow_source_map, port->p_med_power.source));
-               tag_end(w);
-               
-               tag_start(w, "priority", "Power Priority");
-               tag_data(w, map_lookup(port_med_pow_priority_map, port->p_med_power.priority));
-               tag_end(w);
-
-               if(port->p_med_power.val < 1024) {
-                       tag_start(w, "power", "Power Value");
-                       tag_data(w, u2str(port->p_med_power.val * 100));
-                       tag_end(w);
-               }
-               tag_end(w);
-       }
-       if (chassis->c_med_hw ||
-           chassis->c_med_sw ||
-           chassis->c_med_fw ||
-           chassis->c_med_sn ||
-           chassis->c_med_manuf ||
-           chassis->c_med_model ||
-           chassis->c_med_asset) {
-               tag_start(w, "inventory", "Inventory");
-
-               if (chassis->c_med_hw)
-                       tag_datatag(w, "hardware", "Hardware Revision",
-                                       chassis->c_med_hw);
-               if (chassis->c_med_sw)
-                       tag_datatag(w, "software", "Software Revision",
-                                       chassis->c_med_sw);
-               if (chassis->c_med_fw)
-                       tag_datatag(w, "firmware", "Firmware Revision",
-                                       chassis->c_med_fw);
-               if (chassis->c_med_sn)
-                       tag_datatag(w, "serial", "Serial Number",
-                                       chassis->c_med_sn);
-               if (chassis->c_med_manuf)
-                       tag_datatag(w, "manufacturer", "Manufacturer",
-                                       chassis->c_med_manuf);
-               if (chassis->c_med_model)
-                       tag_datatag(w, "model", "Model",
-                                       chassis->c_med_model);
-               if (chassis->c_med_asset)
-                       tag_datatag(w, "asset", "Asset ID",
-                                       chassis->c_med_asset);
-
-               tag_end(w);
-       }
-
-       tag_end(w);
-}
-#endif
-
-static void
-display_chassis(struct writer * w, struct lldpd_chassis *chassis)
-{
-       char *cid;
-       struct in_addr ip;
-       struct lldpd_mgmt *mgmt;
-       char addrbuf[INET6_ADDRSTRLEN];
-
-       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;
-
-       tag_start(w, "chassis", "Chassis");
-       tag_start(w, "id", "ChassisID");
-       tag_attr (w, "type", "", map_lookup(chassis_id_subtype_map, chassis->c_id_subtype));
-
-       switch (chassis->c_id_subtype) {
-       case LLDP_CHASSISID_SUBTYPE_IFNAME:
-       case LLDP_CHASSISID_SUBTYPE_IFALIAS:
-       case LLDP_CHASSISID_SUBTYPE_LOCAL:
-               tag_data (w, cid);
-               break;
-       case LLDP_CHASSISID_SUBTYPE_LLADDR:
-               tag_data(w, dump(chassis->c_id, chassis->c_id_len, ETH_ALEN, ':'));
-               break;
-       case LLDP_CHASSISID_SUBTYPE_ADDR:
-               if (*(u_int8_t*)chassis->c_id == 1) {
-                       memcpy(&ip, chassis->c_id + 1, sizeof(struct in_addr));
-                       tag_data(w, inet_ntoa(ip));
-                       break;
-               }
-       case LLDP_CHASSISID_SUBTYPE_PORT:
-       case LLDP_CHASSISID_SUBTYPE_CHASSIS:
-       default:
-               tag_data(w, dump(chassis->c_id, chassis->c_id_len, 16, ' '));
-       }
-
-       tag_end(w);
-       
-       tag_datatag(w, "name", "SysName", chassis->c_name);
-       tag_datatag(w, "descr", "SysDescr", chassis->c_descr);
-
-       TAILQ_FOREACH(mgmt, &chassis->c_mgmt, m_entries) {
-               memset(addrbuf, 0, sizeof(addrbuf));
-               inet_ntop(lldpd_af(mgmt->m_family), &mgmt->m_addr, addrbuf, sizeof(addrbuf));
-               switch (mgmt->m_family) {
-               case LLDPD_AF_IPV4:
-                       tag_datatag(w, "mgmt-ip", "MgmtIP", addrbuf);
-                       break;
-               case LLDPD_AF_IPV6:
-                       tag_datatag(w, "mgmt-ip6", "MgmtIPv6", addrbuf);
-                       break;
-               }
-       }
-
-       display_cap(w, chassis, LLDP_CAP_OTHER, "Other");
-       display_cap(w, chassis, LLDP_CAP_REPEATER, "Repeater");
-       display_cap(w, chassis, LLDP_CAP_BRIDGE, "Bridge");
-       display_cap(w, chassis, LLDP_CAP_ROUTER, "Router");
-       display_cap(w, chassis, LLDP_CAP_WLAN, "Wlan");
-       display_cap(w, chassis, LLDP_CAP_TELEPHONE, "Tel");
-       display_cap(w, chassis, LLDP_CAP_DOCSIS, "Docsis");
-       display_cap(w, chassis, LLDP_CAP_STATION, "Station");
-
-       tag_end(w);
-}
-
-#ifdef ENABLE_DOT3
-static void
-display_autoneg(struct writer * w, struct lldpd_port *port, int bithd, int bitfd, char *desc)
-{
-       if (!((port->p_macphy.autoneg_advertised & bithd) ||
-               (port->p_macphy.autoneg_advertised & bitfd)))
-               return;
-
-       tag_start(w, "advertised", "Adv");
-       tag_attr(w, "type", "", desc);
-       tag_attr(w, "hd", "HD", (port->p_macphy.autoneg_advertised & bithd)?"yes":"no");
-       tag_attr(w, "fd", "FD", (port->p_macphy.autoneg_advertised)?"yes":"no");
-       tag_end (w);
-}
-#endif
-
-static void
-display_port(struct writer * w, struct lldpd_port *port)
-{
-       char *pid;
-       struct in_addr address;
-
-       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;
-
-       tag_start(w, "port", "Port");
-       tag_start(w, "id", "PortID");
-       tag_attr (w, "type", "", map_lookup(port_id_subtype_map, port->p_id_subtype));
-
-       switch (port->p_id_subtype) {
-       case LLDP_PORTID_SUBTYPE_IFNAME:
-       case LLDP_PORTID_SUBTYPE_IFALIAS:
-       case LLDP_PORTID_SUBTYPE_LOCAL:
-               tag_data (w, pid);
-               break;
-       case LLDP_PORTID_SUBTYPE_LLADDR:
-               tag_data(w, dump(port->p_id, port->p_id_len, ETH_ALEN, ':'));
-               break;
-       case LLDP_PORTID_SUBTYPE_ADDR:
-               if (*(u_int8_t*)port->p_id == 1) {
-                       memcpy(&address, port->p_id + 1,
-                           sizeof(struct in_addr));
-                       tag_data(w, inet_ntoa(address));
-                       break;
-               }
-       case LLDP_PORTID_SUBTYPE_PORT:
-       case LLDP_PORTID_SUBTYPE_AGENTCID:
-       default:
-               tag_data(w, dump(port->p_id, port->p_id_len, 16, ' '));
-       }
-
-       tag_end(w);
-
-       tag_datatag(w, "descr", "PortDescr", port->p_descr);
-
-#ifdef ENABLE_DOT3
-       if (port->p_mfs)
-               tag_datatag(w, "mfs", "MFS", u2str(port->p_mfs));
-
-       if (port->p_aggregid)
-               tag_datatag(w, "aggregation", " Port is aggregated. PortAggregID",
-                           u2str(port->p_aggregid));
-
-       if (port->p_macphy.autoneg_support || port->p_macphy.autoneg_enabled ||
-           port->p_macphy.mau_type) {
-               tag_start(w, "auto-negotiation", "PMD autoneg");
-               tag_attr (w, "supported", "supported",
-                   port->p_macphy.autoneg_support?"yes":"no");
-               tag_attr (w, "enabled", "enabled",
-                   port->p_macphy.autoneg_enabled?"yes":"no");
-
-               if (port->p_macphy.autoneg_enabled) {
-                       display_autoneg(w, port, LLDP_DOT3_LINK_AUTONEG_10BASE_T,
-                           LLDP_DOT3_LINK_AUTONEG_10BASET_FD,
-                           "10Base-T");
-                       display_autoneg(w, port, LLDP_DOT3_LINK_AUTONEG_100BASE_TX,
-                           LLDP_DOT3_LINK_AUTONEG_100BASE_TXFD,
-                           "100Base-T");
-                       display_autoneg(w, port, LLDP_DOT3_LINK_AUTONEG_100BASE_T2,
-                           LLDP_DOT3_LINK_AUTONEG_100BASE_T2FD,
-                           "100Base-T2");
-                       display_autoneg(w, port, LLDP_DOT3_LINK_AUTONEG_1000BASE_X,
-                           LLDP_DOT3_LINK_AUTONEG_1000BASE_XFD,
-                           "100Base-X");
-                       display_autoneg(w, port, LLDP_DOT3_LINK_AUTONEG_1000BASE_T,
-                           LLDP_DOT3_LINK_AUTONEG_1000BASE_TFD,
-                           "1000Base-T");
-               }
-               tag_datatag(w, "current", "MAU oper type",
-                       map_lookup(operational_mau_type_values, port->p_macphy.mau_type));
-               tag_end(w);
-       }
-       if (port->p_power.devicetype) {
-               tag_start(w, "power", "MDI Power");
-               tag_attr(w, "supported", "supported",
-                   port->p_power.supported?"yes":"no");
-               tag_attr(w, "enabled", "enabled",
-                   port->p_power.enabled?"yes":"no");
-               tag_attr(w, "paircontrol", "pair control",
-                   port->p_power.paircontrol?"yes":"no");
-               tag_start(w, "device-type", "Device type");
-               tag_data(w, map_lookup(port_dot3_power_devicetype_map,
-                       port->p_power.devicetype));
-               tag_end(w);
-               tag_start(w, "pairs", "Power pairs");
-               tag_data(w, map_lookup(port_dot3_power_pairs_map,
-                       port->p_power.pairs));
-               tag_end(w);
-               tag_start(w, "class", "Class");
-               tag_data(w, map_lookup(port_dot3_power_class_map,
-                       port->p_power.class));
-               tag_end(w);
-
-               /* 802.3at */
-               if (port->p_power.powertype != LLDP_DOT3_POWER_8023AT_OFF) {
-                       tag_start(w, "power-type", "Power type");
-                       tag_data(w, u2str(port->p_power.powertype));
-                       tag_end(w);
-
-                       tag_start(w, "source", "Power Source");
-                       tag_data(w, map_lookup(
-                                   (port->p_power.devicetype == LLDP_DOT3_POWER_PSE)?
-                                       port_dot3_power_pse_source_map:
-                                       port_dot3_power_pd_source_map,
-                                       port->p_power.source));
-                       tag_end(w);
-
-                       tag_start(w, "priority", "Power Priority");
-                       tag_data(w, map_lookup(port_dot3_power_priority_map,
-                               port->p_power.priority));
-                       tag_end(w);
-
-                       tag_start(w, "requested", "PD requested power Value");
-                       tag_data(w, u2str(port->p_power.requested * 100));
-                       tag_end(w);
-
-                       tag_start(w, "allocated", "PSE allocated power Value");
-                       tag_data(w, u2str(port->p_power.allocated * 100));
-                       tag_end(w);
-               }
-
-               tag_end(w);
-       }
-#endif
-       tag_end(w);
-}
-
-#ifdef ENABLE_DOT1
-static void
-display_vlans(struct writer *w, struct lldpd_port *port)
-{
-       int foundpvid = 0;
-       struct lldpd_vlan *vlan;
-       TAILQ_FOREACH(vlan, &port->p_vlans, v_entries) {
-               if (port->p_pvid == vlan->v_vid)
-                       foundpvid = 1;
-
-               tag_start(w, "vlan", "VLAN");
-               tag_attr(w, "vlan-id", "", u2str(vlan->v_vid));
-               if (port->p_pvid == vlan->v_vid)
-                       tag_attr(w, "pvid", "pvid", "yes");
-               tag_data(w, vlan->v_name);
-               tag_end(w);
-       }
-       if (!foundpvid && port->p_pvid) {
-               tag_start(w, "vlan", "VLAN");
-               tag_attr(w, "vlan-id", "", u2str(port->p_pvid));
-               tag_attr(w, "pvid", "pvid", "yes");
-               tag_end(w);
-       }
-}
-
-static void
-display_ppvids(struct writer *w, struct lldpd_port *port)
-{
-       struct lldpd_ppvid *ppvid;
-       TAILQ_FOREACH(ppvid, &port->p_ppvids, p_entries) {
-               tag_start(w, "ppvid", "PPVID");
-               if (ppvid->p_ppvid)
-                       tag_attr(w, "value", "", u2str(ppvid->p_ppvid));
-               tag_attr(w, "supported", "supported",
-                        (ppvid->p_cap_status & LLDPD_PPVID_CAP_SUPPORTED)?"yes":"no");
-               tag_attr(w, "enabled", "enabled",
-                        (ppvid->p_cap_status & LLDPD_PPVID_CAP_ENABLED)?"yes":"no");
-               tag_end(w);
-       }
-}
-
-static void
-display_pids(struct writer *w, struct lldpd_port *port)
-{
-       struct lldpd_pi *pi;
-       char *hex;
-       int i;
-       TAILQ_FOREACH(pi, &port->p_pids, p_entries) {
-               if (!pi->p_pi_len) continue;
-               tag_start(w, "pi", "PI");
-               /* Convert to hex for display */
-               if ((hex = malloc(pi->p_pi_len * 2 + 1)) == NULL)
-                       fatal(NULL);
-               for (i = 0; i < pi->p_pi_len; i++)
-                       snprintf(hex + 2*i, 3, "%02X", (unsigned char)pi->p_pi[i]);
-               tag_data(w, hex);
-               tag_end(w);
-               free(hex);
-       }
-}
-#endif
-
-static const char*
-display_age(struct lldpd_port *port)
-{
-       static char sage[30];
-       int age = (int)(time(NULL) - port->p_lastchange);
-       if (snprintf(sage, sizeof(sage),
-               "%d day%s, %02d:%02d:%02d",
-               age / (60*60*24),
-               (age / (60*60*24) > 1)?"s":"",
-               (age / (60*60)) % 24,
-               (age / 60) % 60,
-               age % 60) >= sizeof(sage))
-               return "too much";
-       else
-               return sage;
-}
-
-void
-display_interfaces(int s, const char * fmt, int hidden, int argc, char *argv[])
-{
-       int i;
-       struct writer * w;
-       char sep[80];
-       struct lldpd_interface *iff;
-       struct lldpd_interface_list *ifs;
-       struct lldpd_port *port;
-       struct lldpd_chassis *chassis;
-       struct lldpd_hardware *hardware;
-
-       if ( strcmp(fmt,"plain") == 0 ) {
-               w = txt_init( stdout );
-       } else if (strcmp(fmt, "keyvalue") == 0) {
-               w = kv_init( stdout );
-       }
-#ifdef USE_XML
-       else if ( strcmp(fmt,"xml") == 0 ) {
-               w = xml_init( stdout );
-       }
-#endif
-       else {
-               w = txt_init( stdout );
-       }
-
-       memset(sep, '-', 79);
-       sep[79] = 0;
-
-       ifs = get_interfaces(s);
-       tag_start(w, "lldp", "LLDP neighbors");
-       
-       TAILQ_FOREACH(iff, ifs, next) {
-               if (optind < argc) {
-                       for (i = optind; i < argc; i++)
-                               if (strncmp(argv[i], iff->name, IFNAMSIZ) == 0)
-                                       break;
-                       if (i == argc)
-                               continue;
-               }
-               
-               hardware = get_interface(s, iff->name);
-               if (TAILQ_EMPTY(&hardware->h_rports))
-                       continue;
-               TAILQ_FOREACH(port, &hardware->h_rports, p_entries) {
-                       if (!hidden && SMART_HIDDEN(port)) continue;
-                       chassis = port->p_chassis;
-
-                       tag_start(w, "interface", "Interface");
-                       tag_attr(w, "name", "", iff->name );
-                       tag_attr(w, "via" , "via", map_lookup(lldpd_protocol_map, port->p_protocol));
-                       tag_attr(w, "rid" , "RID", u2str(chassis->c_index));
-                       tag_attr(w, "age" , "Time", display_age(port));
-
-                       display_chassis(w,chassis);
-                       display_port(w, port);
-#ifdef ENABLE_DOT1
-                       if (!TAILQ_EMPTY(&port->p_vlans) || port->p_pvid) {
-                               display_vlans(w, port);
-                       }
-                       if (!TAILQ_EMPTY(&port->p_ppvids)) {
-                               display_ppvids(w, port);
-                       }
-                       if (!TAILQ_EMPTY(&port->p_pids)) {
-                               display_pids(w, port);
-                       }
-#endif
-#ifdef ENABLE_LLDPMED
-                       if (port->p_med_cap_enabled) {
-                               display_med(w, chassis, port);
-                       }
-#endif
-                       tag_end(w); /* interface */
-               }
-       }
-
-       tag_end(w);
-       w->finish(w);
-}
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
new file mode 100644 (file)
index 0000000..725c0e7
--- /dev/null
@@ -0,0 +1,12 @@
+lib_LTLIBRARIES = liblldpctl.la
+include_HEADERS = lldpctl.h
+
+liblldpctl_la_SOURCES = lldpctl.h private.h errors.c connection.c atom.c atom-private.c
+liblldpctl_la_LIBADD  = $(top_builddir)/src/libcommon-daemon-lib.la
+liblldpctl_la_LDFLAGS = -export-symbols-regex '^lldpctl_' -version-info 0:0:0
+
+# -version-info format is `current`:`revision`:`age`. For more details, see:
+#   http://sources.redhat.com/autobook/autobook/autobook_91.html#SEC91
+
+pkgconfigdir   = $(libdir)/pkgconfig
+pkgconfig_DATA = lldpctl.pc
diff --git a/src/lib/atom-private.c b/src/lib/atom-private.c
new file mode 100644 (file)
index 0000000..190512f
--- /dev/null
@@ -0,0 +1,2520 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <arpa/inet.h>
+
+#include "lldpctl.h"
+#include "../lldpd-structs.h"
+#include "../log.h"
+#include "private.h"
+
+#define ntohll(x)                                              \
+       (((u_int64_t)(ntohl((int)(((x) << 32) >> 32))) << 32) | \
+           (unsigned int)ntohl(((int)((x) >> 32))))
+
+/* Translation from constants to string */
+
+struct value_string {
+       int value;
+       char *string;
+};
+
+static const struct value_string lldpd_protocol_map[] = {
+       { LLDPD_MODE_LLDP,      "LLDP" },
+       { LLDPD_MODE_CDPV1,     "CDPv1"},
+       { LLDPD_MODE_CDPV2,     "CDPv2"},
+       { LLDPD_MODE_EDP,       "EDP" },
+       { LLDPD_MODE_FDP,       "FDP"},
+       { LLDPD_MODE_SONMP,     "SONMP"},
+        { 0, NULL }
+};
+
+static const struct value_string chassis_id_subtype_map[] = {
+       { LLDP_CHASSISID_SUBTYPE_IFNAME,  "ifname"},
+       { LLDP_CHASSISID_SUBTYPE_IFALIAS, "ifalias" },
+       { LLDP_CHASSISID_SUBTYPE_LOCAL,   "local" },
+       { LLDP_CHASSISID_SUBTYPE_LLADDR,  "mac" },
+       { LLDP_CHASSISID_SUBTYPE_ADDR,    "ip" },
+       { LLDP_CHASSISID_SUBTYPE_PORT,    "unhandled" },
+       { LLDP_CHASSISID_SUBTYPE_CHASSIS, "unhandled" },
+       { 0, NULL},
+};
+
+static const struct value_string port_id_subtype_map[] = {
+       { LLDP_PORTID_SUBTYPE_IFNAME,   "ifname"},
+       { LLDP_PORTID_SUBTYPE_IFALIAS,  "ifalias" },
+       { LLDP_PORTID_SUBTYPE_LOCAL,    "local" },
+       { LLDP_PORTID_SUBTYPE_LLADDR,   "mac" },
+       { LLDP_PORTID_SUBTYPE_ADDR,     "ip" },
+       { LLDP_PORTID_SUBTYPE_PORT,     "unhandled" },
+       { LLDP_PORTID_SUBTYPE_AGENTCID, "unhandled" },
+       { 0, NULL},
+};
+
+#ifdef ENABLE_DOT3
+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" },
+       { 41,   "10GigBaseCX4 - X copper over 8 pair 100-Ohm balanced cable" },
+       { 42,   "2BaseTL - Voice grade UTP copper, up to 2700m, optional PAF" },
+       { 43,   "10PassTS - Voice grade UTP copper, up to 750m, optional PAF" },
+       { 44,   "100BaseBX10D - One single-mode fiber OLT, long wavelength, 10km" },
+       { 45,   "100BaseBX10U - One single-mode fiber ONU, long wavelength, 10km" },
+       { 46,   "100BaseLX10 - Two single-mode fibers, long wavelength, 10km" },
+       { 47,   "1000BaseBX10D - One single-mode fiber OLT, long wavelength, 10km" },
+       { 48,   "1000BaseBX10U - One single-mode fiber ONU, long wavelength, 10km" },
+       { 49,   "1000BaseLX10 - Two sigle-mode fiber, long wavelength, 10km" },
+       { 50,   "1000BasePX10D - One single-mode fiber EPON OLT, 10km" },
+       { 51,   "1000BasePX10U - One single-mode fiber EPON ONU, 10km" },
+       { 52,   "1000BasePX20D - One single-mode fiber EPON OLT, 20km" },
+       { 53,   "1000BasePX20U - One single-mode fiber EPON ONU, 20km" },
+       { 0, NULL }
+};
+
+static const struct value_string port_dot3_power_devicetype_map[] = {
+       { LLDP_DOT3_POWER_PSE, "PSE" },
+       { LLDP_DOT3_POWER_PD,  "PD" },
+       { 0, NULL }
+};
+
+static const struct value_string port_dot3_power_pairs_map[] = {
+       { LLDP_DOT3_POWERPAIRS_SIGNAL, "signal" },
+       { LLDP_DOT3_POWERPAIRS_SPARE,  "spare" },
+       { 0, NULL }
+};
+
+static const struct value_string port_dot3_power_class_map[] = {
+       { 1, "class 0" },
+       { 2, "class 1" },
+       { 3, "class 2" },
+       { 4, "class 3" },
+       { 5, "class 4" },
+       { 0, NULL }
+};
+
+static const struct value_string port_dot3_power_pse_source_map[] = {
+       { LLDP_DOT3_POWER_SOURCE_BOTH, "PSE + Local" },
+       { LLDP_DOT3_POWER_SOURCE_PSE, "PSE" },
+       { 0, NULL }
+};
+
+static const struct value_string port_dot3_power_pd_source_map[] = {
+       { LLDP_DOT3_POWER_SOURCE_BACKUP, "Backup source" },
+       { LLDP_DOT3_POWER_SOURCE_PRIMARY, "Primary power source" },
+       { 0, NULL }
+};
+
+static const struct value_string port_dot3_power_priority_map[] = {
+       { 0,                          "unknown" },
+       { LLDP_MED_POW_PRIO_CRITICAL, "critical" },
+       { LLDP_MED_POW_PRIO_HIGH,     "high" },
+       { LLDP_MED_POW_PRIO_LOW,      "low" },
+       { 0, NULL },
+};
+#endif
+
+#ifdef ENABLE_LLDPMED
+static const struct value_string chassis_med_type_map[] = {
+       { LLDP_MED_CLASS_I,        "Generic Endpoint (Class I)" },
+       { LLDP_MED_CLASS_II,       "Media Endpoint (Class II)" },
+       { LLDP_MED_CLASS_III,      "Communication Device Endpoint (Class III)" },
+       { LLDP_MED_NETWORK_DEVICE, "Network Connectivity Device" },
+       { 0, NULL },
+};
+
+static const struct value_string port_med_policy_map[] = {
+       { LLDP_MED_APPTYPE_VOICE ,           "Voice"},
+       { LLDP_MED_APPTYPE_VOICESIGNAL,      "Voice Signaling"},
+       { LLDP_MED_APPTYPE_GUESTVOICE,       "Guest Voice"},
+       { LLDP_MED_APPTYPE_GUESTVOICESIGNAL, "Guest Voice Signaling"},
+       { LLDP_MED_APPTYPE_SOFTPHONEVOICE,   "Softphone Voice"},
+       { LLDP_MED_APPTYPE_VIDEOCONFERENCE,  "Video Conferencing"},
+       { LLDP_MED_APPTYPE_VIDEOSTREAM,      "Streaming Video"},
+       { LLDP_MED_APPTYPE_VIDEOSIGNAL,      "Video Signaling"},
+       { 0, NULL },
+};
+
+static const struct value_string port_med_location_map[] = {
+       { LLDP_MED_LOCFORMAT_COORD, "Coordinates" },
+       { LLDP_MED_LOCFORMAT_CIVIC, "Civic address" },
+       { LLDP_MED_LOCFORMAT_ELIN, "ELIN" },
+       { 0, NULL },
+};
+
+static const struct value_string civic_address_type_map[] = {
+        { 0,    "Language" },
+        { 1,    "Country subdivision" },
+        { 2,    "County" },
+        { 3,    "City" },
+        { 4,    "City division" },
+        { 5,    "Block" },
+        { 6,    "Street" },
+        { 16,   "Direction" },
+        { 17,   "Trailing street suffix" },
+        { 18,   "Street suffix" },
+        { 19,   "Number" },
+        { 20,   "Number suffix" },
+        { 21,   "Landmark" },
+        { 22,   "Additional" },
+        { 23,   "Name" },
+        { 24,   "ZIP" },
+        { 25,   "Building" },
+        { 26,   "Unit" },
+        { 27,   "Floor" },
+        { 28,   "Room" },
+        { 29,   "Place type" },
+        { 128,  "Script" },
+        { 0, NULL }
+};
+
+static const struct value_string port_med_geoid_map[] = {
+       { LLDP_MED_LOCATION_GEOID_WGS84, "WGS84" },
+       { LLDP_MED_LOCATION_GEOID_NAD83, "NAD83" },
+       { LLDP_MED_LOCATION_GEOID_NAD83_MLLW, "NAD83/MLLW" },
+       { 0, NULL },
+};
+
+static const struct value_string port_med_pow_devicetype_map[] = {
+       { LLDP_MED_POW_TYPE_PSE, "PSE" },
+       { LLDP_MED_POW_TYPE_PD,  "PD" },
+       { 0, NULL },
+};
+
+static const struct value_string port_med_pow_source_map[] = {
+       { LLDP_MED_POW_SOURCE_PRIMARY, "Primary Power Source" },
+       { LLDP_MED_POW_SOURCE_BACKUP,  "Backup Power Source / Power Conservation Mode" },
+       { LLDP_MED_POW_SOURCE_PSE,     "PSE" },
+       { LLDP_MED_POW_SOURCE_LOCAL,   "Local"},
+       { LLDP_MED_POW_SOURCE_BOTH,    "PSE + Local"},
+       { 0, NULL },
+};
+
+static const struct value_string port_med_pow_source_map2[] = {
+       { 0,                           "unknown" },
+       { LLDP_MED_POW_SOURCE_PRIMARY, "primary" },
+       { LLDP_MED_POW_SOURCE_BACKUP,  "backup" },
+       { LLDP_MED_POW_SOURCE_PSE,     "pse" },
+       { LLDP_MED_POW_SOURCE_LOCAL,   "local" },
+       { LLDP_MED_POW_SOURCE_BOTH,    "both" },
+       { 0, NULL }
+};
+
+static const struct value_string port_med_pow_priority_map[] = {
+       { 0,                          "unknown" },
+       { LLDP_MED_POW_PRIO_CRITICAL, "critical" },
+       { LLDP_MED_POW_PRIO_HIGH,     "high" },
+       { LLDP_MED_POW_PRIO_LOW,      "low" },
+       { 0, NULL },
+};
+#endif
+
+static const char*
+map_lookup(const struct value_string *list, int n)
+{
+
+       unsigned int i;
+
+       for (i = 0; list[i].string != NULL; i++) {
+               if (list[i].value == n) {
+                       return list[i].string;
+               }
+       }
+
+       return "unknown";
+}
+
+#if defined ENABLE_LLDPMED || defined ENABLE_DOT3
+static int
+map_reverse_lookup(const struct value_string *list, const char *string)
+{
+       unsigned int i;
+
+       for (i = 0; list[i].string != NULL; i++) {
+               if (!strcasecmp(list[i].string, string))
+                       return list[i].value;
+       }
+
+       return -1;
+}
+#endif
+
+/* Atom methods */
+
+static int
+_lldpctl_atom_new_interfaces_list(lldpctl_atom_t *atom, va_list ap)
+{
+       struct _lldpctl_atom_interfaces_list_t *iflist =
+           (struct _lldpctl_atom_interfaces_list_t *)atom;
+       iflist->ifs = va_arg(ap, struct lldpd_interface_list *);
+       return 1;
+}
+
+static void
+_lldpctl_atom_free_interfaces_list(lldpctl_atom_t *atom)
+{
+       struct _lldpctl_atom_interfaces_list_t *iflist =
+           (struct _lldpctl_atom_interfaces_list_t *)atom;
+       struct lldpd_interface *iface, *iface_next;
+       for (iface = TAILQ_FIRST(iflist->ifs);
+            iface != NULL;
+            iface = iface_next) {
+               /* Don't TAILQ_REMOVE, this is not a real list! */
+               iface_next = TAILQ_NEXT(iface, next);
+               free(iface->name);
+               free(iface);
+       }
+       free(iflist->ifs);
+}
+
+static lldpctl_atom_iter_t*
+_lldpctl_atom_iter_interfaces_list(lldpctl_atom_t *atom)
+{
+       struct _lldpctl_atom_interfaces_list_t *iflist =
+           (struct _lldpctl_atom_interfaces_list_t *)atom;
+       return (lldpctl_atom_iter_t*)TAILQ_FIRST(iflist->ifs);
+}
+
+static lldpctl_atom_iter_t*
+_lldpctl_atom_next_interfaces_list(lldpctl_atom_t *atom, lldpctl_atom_iter_t *iter)
+{
+       return (lldpctl_atom_iter_t*)TAILQ_NEXT((struct lldpd_interface *)iter, next);
+}
+
+static lldpctl_atom_t*
+_lldpctl_atom_value_interfaces_list(lldpctl_atom_t *atom, lldpctl_atom_iter_t *iter)
+{
+       struct lldpd_interface *iface = (struct lldpd_interface *)iter;
+       return _lldpctl_new_atom(atom->conn, atom_interface, iface->name);
+}
+
+static int
+_lldpctl_atom_new_interface(lldpctl_atom_t *atom, va_list ap)
+{
+       struct _lldpctl_atom_interface_t *port =
+           (struct _lldpctl_atom_interface_t *)atom;
+       port->name = strdup(va_arg(ap, char *));
+       return (port->name != NULL);
+}
+
+static void
+_lldpctl_atom_free_interface(lldpctl_atom_t *atom)
+{
+       struct _lldpctl_atom_interface_t *port =
+           (struct _lldpctl_atom_interface_t *)atom;
+       free(port->name);
+}
+
+static const char*
+_lldpctl_atom_get_str_interface(lldpctl_atom_t *atom, lldpctl_key_t key)
+{
+       struct _lldpctl_atom_interface_t *port =
+           (struct _lldpctl_atom_interface_t *)atom;
+       switch (key) {
+       case lldpctl_k_interface_name:
+               return port->name;
+       default:
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+}
+
+static int
+_lldpctl_atom_new_any_list(lldpctl_atom_t *atom, va_list ap)
+{
+       struct _lldpctl_atom_any_list_t *plist =
+           (struct _lldpctl_atom_any_list_t *)atom;
+       plist->parent = va_arg(ap, struct _lldpctl_atom_port_t *);
+       lldpctl_atom_inc_ref((lldpctl_atom_t *)plist->parent);
+       return 1;
+}
+
+static void
+_lldpctl_atom_free_any_list(lldpctl_atom_t *atom)
+{
+       struct _lldpctl_atom_any_list_t *plist =
+           (struct _lldpctl_atom_any_list_t *)atom;
+       lldpctl_atom_dec_ref((lldpctl_atom_t *)plist->parent);
+}
+
+static lldpctl_atom_iter_t*
+_lldpctl_atom_iter_ports_list(lldpctl_atom_t *atom)
+{
+       struct _lldpctl_atom_any_list_t *plist =
+           (struct _lldpctl_atom_any_list_t *)atom;
+       return (lldpctl_atom_iter_t*)TAILQ_FIRST(&plist->parent->hardware->h_rports);
+}
+
+static lldpctl_atom_iter_t*
+_lldpctl_atom_next_ports_list(lldpctl_atom_t *atom, lldpctl_atom_iter_t *iter)
+{
+       struct lldpd_port *port = (struct lldpd_port *)iter;
+       return (lldpctl_atom_iter_t*)TAILQ_NEXT(port, p_entries);
+}
+
+static lldpctl_atom_t*
+_lldpctl_atom_value_ports_list(lldpctl_atom_t *atom, lldpctl_atom_iter_t *iter)
+{
+       struct lldpd_port *port = (struct lldpd_port *)iter;
+       return _lldpctl_new_atom(atom->conn, atom_port, NULL, port,
+           ((struct _lldpctl_atom_any_list_t *)atom)->parent);
+}
+
+static int
+_lldpctl_atom_new_port(lldpctl_atom_t *atom, va_list ap)
+{
+       struct _lldpctl_atom_port_t *port =
+           (struct _lldpctl_atom_port_t *)atom;
+       port->hardware = va_arg(ap, struct lldpd_hardware*);
+       port->port = va_arg(ap, struct lldpd_port*);
+       port->parent = va_arg(ap, struct _lldpctl_atom_port_t*);
+       if (port->parent)
+               lldpctl_atom_inc_ref((lldpctl_atom_t*)port->parent);
+       return 1;
+}
+
+TAILQ_HEAD(chassis_list, lldpd_chassis);
+
+static void
+add_chassis(struct chassis_list *chassis_list,
+       struct lldpd_chassis *chassis)
+{
+       struct lldpd_chassis *one_chassis, *next_chassis;
+       /* This is an odd function because we want to add the chassis and the
+        * chained chassis which may not be referenced elsewhere. Serialization
+        * will bring us useless chassis in the mix. */
+       while (chassis) {
+               TAILQ_FOREACH(one_chassis, chassis_list, c_entries) {
+                       if (one_chassis == chassis) return;
+               }
+               next_chassis = TAILQ_NEXT(chassis, c_entries);
+               TAILQ_INSERT_TAIL(chassis_list,
+                   chassis, c_entries);
+               chassis = next_chassis;
+       }
+}
+
+static void
+_lldpctl_atom_free_port(lldpctl_atom_t *atom)
+{
+       struct _lldpctl_atom_port_t *port =
+           (struct _lldpctl_atom_port_t *)atom;
+       struct lldpd_hardware *hardware = port->hardware;
+       struct lldpd_chassis  *one_chassis, *one_chassis_next;
+       struct lldpd_port     *one_port;
+
+       /* We need to free the whole struct lldpd_hardware: local port, local
+        * chassis and remote ports... The same chassis may be present several
+        * times. We build a list of chassis (we don't use reference count). */
+       struct chassis_list chassis_list;
+       TAILQ_INIT(&chassis_list);
+
+       if (port->parent) lldpctl_atom_dec_ref((lldpctl_atom_t*)port->parent);
+       if (!hardware) return;
+
+       add_chassis(&chassis_list, port->port->p_chassis);
+       TAILQ_FOREACH(one_port, &hardware->h_rports, p_entries)
+               add_chassis(&chassis_list, one_port->p_chassis);
+
+       /* Free hardware port */
+       lldpd_remote_cleanup(hardware, 1);
+       lldpd_port_cleanup(port->port, 1);
+       free(port->hardware);
+
+       /* Free list of chassis */
+       for (one_chassis = TAILQ_FIRST(&chassis_list);
+            one_chassis != NULL;
+            one_chassis = one_chassis_next) {
+               one_chassis_next = TAILQ_NEXT(one_chassis, c_entries);
+               lldpd_chassis_cleanup(one_chassis, 1);
+       }
+}
+
+static lldpctl_atom_t*
+_lldpctl_atom_get_atom_port(lldpctl_atom_t *atom, lldpctl_key_t key)
+{
+       struct _lldpctl_atom_port_t *p =
+           (struct _lldpctl_atom_port_t *)atom;
+       struct lldpd_port     *port     = p->port;
+       struct lldpd_hardware *hardware = p->hardware;
+
+       /* Local port only */
+       if (hardware != NULL) {
+               switch (key) {
+               case lldpctl_k_port_neighbors:
+                       return _lldpctl_new_atom(atom->conn, atom_ports_list, p);
+               default: break;
+               }
+       }
+
+       /* Local and remote port */
+       switch (key) {
+       case lldpctl_k_chassis_mgmt:
+               return _lldpctl_new_atom(atom->conn, atom_mgmts_list,
+                   p, port->p_chassis);
+#ifdef ENABLE_DOT3
+       case lldpctl_k_port_dot3_power:
+               return _lldpctl_new_atom(atom->conn, atom_dot3_power,
+                   p);
+#endif
+#ifdef ENABLE_DOT1
+       case lldpctl_k_port_vlans:
+               return _lldpctl_new_atom(atom->conn, atom_vlans_list,
+                   p);
+       case lldpctl_k_port_ppvids:
+               return _lldpctl_new_atom(atom->conn, atom_ppvids_list,
+                   p);
+       case lldpctl_k_port_pis:
+               return _lldpctl_new_atom(atom->conn, atom_pis_list,
+                   p);
+#endif
+#ifdef ENABLE_LLDPMED
+       case lldpctl_k_port_med_policies:
+               return _lldpctl_new_atom(atom->conn, atom_med_policies_list,
+                   p);
+       case lldpctl_k_port_med_locations:
+               return _lldpctl_new_atom(atom->conn, atom_med_locations_list,
+                   p);
+       case lldpctl_k_port_med_power:
+               return _lldpctl_new_atom(atom->conn, atom_med_power, p);
+#endif
+       default:
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+}
+
+static lldpctl_atom_t*
+_lldpctl_atom_set_atom_port(lldpctl_atom_t *atom, lldpctl_key_t key, lldpctl_atom_t *value)
+{
+       struct _lldpctl_atom_port_t *p =
+           (struct _lldpctl_atom_port_t *)atom;
+       struct lldpd_hardware *hardware = p->hardware;
+       struct lldpd_port_set set;
+       int rc;
+
+#ifdef ENABLE_DOT3
+       struct _lldpctl_atom_dot3_power_t *dpow;
+#endif
+#ifdef ENABLE_LLDPMED
+       struct _lldpctl_atom_med_power_t *mpow;
+       struct _lldpctl_atom_med_policy_t *mpol;
+       struct _lldpctl_atom_med_location_t *mloc;
+#endif
+
+       /* Local port only */
+       if (hardware == NULL) {
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+
+       memset(&set, 0, sizeof(struct lldpd_port_set));
+
+       switch (key) {
+#ifdef ENABLE_DOT3
+       case lldpctl_k_port_dot3_power:
+               if (value->type != atom_dot3_power) {
+                       SET_ERROR(atom->conn, LLDPCTL_ERR_INCORRECT_ATOM_TYPE);
+                       return NULL;
+               }
+
+               dpow = (struct _lldpctl_atom_dot3_power_t *)value;
+               set.dot3_power = &dpow->parent->port->p_power;
+               break;
+#endif
+#ifdef ENABLE_LLDPMED
+       case lldpctl_k_port_med_power:
+               if (value->type != atom_med_power) {
+                       SET_ERROR(atom->conn, LLDPCTL_ERR_INCORRECT_ATOM_TYPE);
+                       return NULL;
+               }
+
+               mpow = (struct _lldpctl_atom_med_power_t *)value;
+               set.med_power = &mpow->parent->port->p_med_power;
+               break;
+       case lldpctl_k_port_med_policies:
+               if (value->type != atom_med_policy) {
+                       SET_ERROR(atom->conn, LLDPCTL_ERR_INCORRECT_ATOM_TYPE);
+                       return NULL;
+               }
+               mpol = (struct _lldpctl_atom_med_policy_t *)value;
+               set.med_policy = mpol->policy;
+               break;
+       case lldpctl_k_port_med_locations:
+               if (value->type != atom_med_location) {
+                       SET_ERROR(atom->conn, LLDPCTL_ERR_INCORRECT_ATOM_TYPE);
+                       return NULL;
+               }
+               mloc = (struct _lldpctl_atom_med_location_t *)value;
+               set.med_location = mloc->location;
+               break;
+#endif
+       default:
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+
+       set.ifname = hardware->h_ifname;
+       rc = _lldpctl_do_something(atom->conn,
+           CONN_STATE_SET_PORT_SEND, CONN_STATE_SET_PORT_RECV,
+           value,
+           SET_PORT, &set, &MARSHAL_INFO(lldpd_port_set),
+           NULL, NULL);
+       if (rc == 0) return atom;
+       return NULL;
+}
+
+static const char*
+_lldpctl_atom_get_str_port(lldpctl_atom_t *atom, lldpctl_key_t key)
+{
+       struct _lldpctl_atom_port_t *p =
+           (struct _lldpctl_atom_port_t *)atom;
+       struct lldpd_port     *port     = p->port;
+       struct lldpd_hardware *hardware = p->hardware;
+       struct lldpd_chassis  *chassis  = port->p_chassis;
+       char *ipaddress = NULL; size_t len;
+
+       /* Local port only */
+       if (hardware != NULL) {
+               switch (key) {
+               case lldpctl_k_port_name:
+                       return hardware->h_ifname;
+               default: break;
+               }
+       }
+
+       /* Local and remote port */
+       switch (key) {
+       case lldpctl_k_port_protocol:
+               return map_lookup(lldpd_protocol_map, port->p_protocol);
+       case lldpctl_k_port_id_subtype:
+               return map_lookup(port_id_subtype_map, port->p_id_subtype);
+       case lldpctl_k_port_id:
+               switch (port->p_id_subtype) {
+               case LLDP_PORTID_SUBTYPE_IFNAME:
+               case LLDP_PORTID_SUBTYPE_IFALIAS:
+               case LLDP_PORTID_SUBTYPE_LOCAL:
+                       return port->p_id;
+               case LLDP_PORTID_SUBTYPE_LLADDR:
+                       return _lldpctl_dump_in_atom(atom,
+                           (uint8_t*)port->p_id, port->p_id_len,
+                           ':', 0);
+               case LLDP_PORTID_SUBTYPE_ADDR:
+                       switch (port->p_id[0]) {
+                       case LLDP_MGMT_ADDR_IP4: len = INET_ADDRSTRLEN + 1; break;
+                       case LLDP_MGMT_ADDR_IP6: len = INET6_ADDRSTRLEN + 1; break;
+                       default: len = 0;
+                       }
+                       if (len > 0) {
+                               ipaddress = _lldpctl_alloc_in_atom(atom, len);
+                               if (!ipaddress) return NULL;
+                               if (inet_ntop((port->p_id[0] == LLDP_MGMT_ADDR_IP4)?
+                                       AF_INET:AF_INET6,
+                                       &port->p_id[1], ipaddress, len) == NULL)
+                                       break;
+                               return ipaddress;
+                       }
+                       break;
+               }
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       case lldpctl_k_port_descr:
+               return port->p_descr;
+
+#ifdef ENABLE_DOT3
+       case lldpctl_k_port_dot3_mautype:
+               return map_lookup(operational_mau_type_values,
+                   port->p_macphy.mau_type);
+#endif
+
+       case lldpctl_k_chassis_id_subtype:
+               return map_lookup(chassis_id_subtype_map, chassis->c_id_subtype);
+       case lldpctl_k_chassis_id:
+               switch (chassis->c_id_subtype) {
+               case LLDP_CHASSISID_SUBTYPE_IFNAME:
+               case LLDP_CHASSISID_SUBTYPE_IFALIAS:
+               case LLDP_CHASSISID_SUBTYPE_LOCAL:
+                       return chassis->c_id;
+               case LLDP_CHASSISID_SUBTYPE_LLADDR:
+                       return _lldpctl_dump_in_atom(atom,
+                           (uint8_t*)chassis->c_id, chassis->c_id_len,
+                           ':', 0);
+               case LLDP_CHASSISID_SUBTYPE_ADDR:
+                       switch (chassis->c_id[0]) {
+                       case LLDP_MGMT_ADDR_IP4: len = INET_ADDRSTRLEN + 1; break;
+                       case LLDP_MGMT_ADDR_IP6: len = INET6_ADDRSTRLEN + 1; break;
+                       default: len = 0;
+                       }
+                       if (len > 0) {
+                               ipaddress = _lldpctl_alloc_in_atom(atom, len);
+                               if (!ipaddress) return NULL;
+                               if (inet_ntop((chassis->c_id[0] == LLDP_MGMT_ADDR_IP4)?
+                                       AF_INET:AF_INET6,
+                                       &chassis->c_id[1], ipaddress, len) == NULL)
+                                       break;
+                               return ipaddress;
+                       }
+                       break;
+               }
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       case lldpctl_k_chassis_name: return chassis->c_name;
+       case lldpctl_k_chassis_descr: return chassis->c_descr;
+
+#ifdef ENABLE_LLDPMED
+       case lldpctl_k_chassis_med_type:
+               return map_lookup(chassis_med_type_map, chassis->c_med_type);
+       case lldpctl_k_chassis_med_inventory_hw:
+               return chassis->c_med_hw;
+       case lldpctl_k_chassis_med_inventory_sw:
+               return chassis->c_med_sw;
+       case lldpctl_k_chassis_med_inventory_fw:
+               return chassis->c_med_fw;
+       case lldpctl_k_chassis_med_inventory_sn:
+               return chassis->c_med_sn;
+       case lldpctl_k_chassis_med_inventory_manuf:
+               return chassis->c_med_manuf;
+       case lldpctl_k_chassis_med_inventory_model:
+               return chassis->c_med_model;
+       case lldpctl_k_chassis_med_inventory_asset:
+               return chassis->c_med_asset;
+#endif
+
+       default:
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+}
+
+static long int
+_lldpctl_atom_get_int_port(lldpctl_atom_t *atom, lldpctl_key_t key)
+{
+       struct _lldpctl_atom_port_t *p =
+           (struct _lldpctl_atom_port_t *)atom;
+       struct lldpd_port     *port     = p->port;
+       struct lldpd_hardware *hardware = p->hardware;
+       struct lldpd_chassis  *chassis  = port->p_chassis;
+
+       /* Local port only */
+       if (hardware != NULL) {
+               switch (key) {
+               case lldpctl_k_port_index:
+                       return hardware->h_ifindex;
+               default: break;
+               }
+       }
+
+       /* Local and remote port */
+       switch (key) {
+       case lldpctl_k_port_protocol:
+               return port->p_protocol;
+       case lldpctl_k_port_age:
+               return port->p_lastchange;
+       case lldpctl_k_port_id_subtype:
+               return port->p_id_subtype;
+       case lldpctl_k_port_hidden:
+               return port->p_hidden_in;
+#ifdef ENABLE_DOT3
+       case lldpctl_k_port_dot3_mfs:
+               if (port->p_mfs > 0)
+                       return port->p_mfs;
+               break;
+       case lldpctl_k_port_dot3_aggregid:
+               if (port->p_aggregid > 0)
+                       return port->p_aggregid;
+               break;
+       case lldpctl_k_port_dot3_autoneg_support:
+               return port->p_macphy.autoneg_support;
+       case lldpctl_k_port_dot3_autoneg_enabled:
+               return port->p_macphy.autoneg_enabled;
+       case lldpctl_k_port_dot3_autoneg_advertised:
+               return port->p_macphy.autoneg_advertised;
+       case lldpctl_k_port_dot3_mautype:
+               return port->p_macphy.mau_type;
+#endif
+#ifdef ENABLE_DOT1
+       case lldpctl_k_port_vlan_pvid:
+               return port->p_pvid;
+#endif
+       case lldpctl_k_chassis_index:
+               return chassis->c_index;
+       case lldpctl_k_chassis_id_subtype:
+               return chassis->c_id_subtype;
+       case lldpctl_k_chassis_cap_available:
+               return chassis->c_cap_available;
+       case lldpctl_k_chassis_cap_enabled:
+               return chassis->c_cap_enabled;
+#ifdef ENABLE_LLDPMED
+       case lldpctl_k_chassis_med_type:
+               return chassis->c_med_type;
+       case lldpctl_k_chassis_med_cap:
+               return chassis->c_med_cap_available;
+#endif
+       default:
+               return SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+       }
+       return SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+}
+
+static const uint8_t*
+_lldpctl_atom_get_buf_port(lldpctl_atom_t *atom, lldpctl_key_t key, size_t *n)
+{
+       struct _lldpctl_atom_port_t *p =
+           (struct _lldpctl_atom_port_t *)atom;
+       struct lldpd_port     *port     = p->port;
+       struct lldpd_chassis  *chassis  = port->p_chassis;
+
+       switch (key) {
+       case lldpctl_k_port_id:
+               *n = port->p_id_len;
+               return (uint8_t*)port->p_id;
+       case lldpctl_k_chassis_id:
+               *n = chassis->c_id_len;
+               return (uint8_t*)chassis->c_id;
+       default:
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+}
+
+static int
+_lldpctl_atom_new_mgmts_list(lldpctl_atom_t *atom, va_list ap)
+{
+       struct _lldpctl_atom_mgmts_list_t *plist =
+           (struct _lldpctl_atom_mgmts_list_t *)atom;
+       plist->parent = va_arg(ap, struct _lldpctl_atom_port_t *);
+       plist->chassis = va_arg(ap, struct lldpd_chassis *);
+       lldpctl_atom_inc_ref((lldpctl_atom_t *)plist->parent);
+       return 1;
+}
+
+static void
+_lldpctl_atom_free_mgmts_list(lldpctl_atom_t *atom)
+{
+       struct _lldpctl_atom_mgmts_list_t *plist =
+           (struct _lldpctl_atom_mgmts_list_t *)atom;
+       lldpctl_atom_dec_ref((lldpctl_atom_t *)plist->parent);
+}
+
+static lldpctl_atom_iter_t*
+_lldpctl_atom_iter_mgmts_list(lldpctl_atom_t *atom)
+{
+       struct _lldpctl_atom_mgmts_list_t *plist =
+           (struct _lldpctl_atom_mgmts_list_t *)atom;
+       return (lldpctl_atom_iter_t*)TAILQ_FIRST(&plist->chassis->c_mgmt);
+}
+
+static lldpctl_atom_iter_t*
+_lldpctl_atom_next_mgmts_list(lldpctl_atom_t *atom, lldpctl_atom_iter_t *iter)
+{
+       struct lldpd_mgmt *mgmt = (struct lldpd_mgmt *)iter;
+       return (lldpctl_atom_iter_t*)TAILQ_NEXT(mgmt, m_entries);
+}
+
+static lldpctl_atom_t*
+_lldpctl_atom_value_mgmts_list(lldpctl_atom_t *atom, lldpctl_atom_iter_t *iter)
+{
+       struct _lldpctl_atom_mgmts_list_t *plist =
+           (struct _lldpctl_atom_mgmts_list_t *)atom;
+       struct lldpd_mgmt *mgmt = (struct lldpd_mgmt *)iter;
+       return _lldpctl_new_atom(atom->conn, atom_mgmt, plist->parent, mgmt);
+}
+
+static int
+_lldpctl_atom_new_mgmt(lldpctl_atom_t *atom, va_list ap)
+{
+       struct _lldpctl_atom_mgmt_t *mgmt =
+           (struct _lldpctl_atom_mgmt_t *)atom;
+       mgmt->parent = va_arg(ap, struct _lldpctl_atom_port_t *);
+       mgmt->mgmt = va_arg(ap, struct lldpd_mgmt *);
+       lldpctl_atom_inc_ref((lldpctl_atom_t *)mgmt->parent);
+       return 1;
+}
+
+static void
+_lldpctl_atom_free_mgmt(lldpctl_atom_t *atom)
+{
+       struct _lldpctl_atom_mgmt_t *mgmt =
+           (struct _lldpctl_atom_mgmt_t *)atom;
+       lldpctl_atom_dec_ref((lldpctl_atom_t *)mgmt->parent);
+}
+
+static const char*
+_lldpctl_atom_get_str_mgmt(lldpctl_atom_t *atom, lldpctl_key_t key)
+{
+       char *ipaddress = NULL;
+       size_t len; int af;
+       struct _lldpctl_atom_mgmt_t *m =
+           (struct _lldpctl_atom_mgmt_t *)atom;
+
+       /* Local and remote port */
+       switch (key) {
+       case lldpctl_k_mgmt_ip:
+               switch (m->mgmt->m_family) {
+               case LLDPD_AF_IPV4:
+                       len = INET_ADDRSTRLEN + 1;
+                       af  = AF_INET;
+                       break;
+               case LLDPD_AF_IPV6:
+                       len = INET6_ADDRSTRLEN + 1;
+                       af = AF_INET6;
+                       break;
+               default:
+                       len = 0;
+               }
+               if (len == 0) break;
+               ipaddress = _lldpctl_alloc_in_atom(atom, len);
+               if (!ipaddress) return NULL;
+               if (inet_ntop(af, &m->mgmt->m_addr, ipaddress, len) == NULL)
+                       break;
+               return ipaddress;
+       default:
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+       SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+       return NULL;
+}
+
+#ifdef ENABLE_DOT3
+static int
+_lldpctl_atom_new_dot3_power(lldpctl_atom_t *atom, va_list ap)
+{
+       struct _lldpctl_atom_dot3_power_t *dpow =
+           (struct _lldpctl_atom_dot3_power_t *)atom;
+       dpow->parent = va_arg(ap, struct _lldpctl_atom_port_t *);
+       lldpctl_atom_inc_ref((lldpctl_atom_t *)dpow->parent);
+       return 1;
+}
+
+static void
+_lldpctl_atom_free_dot3_power(lldpctl_atom_t *atom)
+{
+       struct _lldpctl_atom_dot3_power_t *dpow =
+           (struct _lldpctl_atom_dot3_power_t *)atom;
+       lldpctl_atom_dec_ref((lldpctl_atom_t *)dpow->parent);
+}
+
+static const char*
+_lldpctl_atom_get_str_dot3_power(lldpctl_atom_t *atom, lldpctl_key_t key)
+{
+       struct _lldpctl_atom_dot3_power_t *dpow =
+           (struct _lldpctl_atom_dot3_power_t *)atom;
+       struct lldpd_port     *port     = dpow->parent->port;
+
+       /* Local and remote port */
+       switch (key) {
+       case lldpctl_k_dot3_power_devicetype:
+               return map_lookup(port_dot3_power_devicetype_map,
+                   port->p_power.devicetype);
+       case lldpctl_k_dot3_power_pairs:
+               return map_lookup(port_dot3_power_pairs_map,
+                   port->p_power.pairs);
+       case lldpctl_k_dot3_power_class:
+               return map_lookup(port_dot3_power_class_map,
+                   port->p_power.class);
+       case lldpctl_k_dot3_power_source:
+               return map_lookup((port->p_power.devicetype == LLDP_DOT3_POWER_PSE)?
+                   port_dot3_power_pse_source_map:
+                   port_dot3_power_pd_source_map,
+                   port->p_power.source);
+       case lldpctl_k_dot3_power_priority:
+               return map_lookup(port_dot3_power_priority_map,
+                   port->p_power.priority);
+       default:
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+}
+
+static long int
+_lldpctl_atom_get_int_dot3_power(lldpctl_atom_t *atom, lldpctl_key_t key)
+{
+       struct _lldpctl_atom_dot3_power_t *dpow =
+           (struct _lldpctl_atom_dot3_power_t *)atom;
+       struct lldpd_port     *port     = dpow->parent->port;
+
+       /* Local and remote port */
+       switch (key) {
+       case lldpctl_k_dot3_power_devicetype:
+               return port->p_power.devicetype;
+       case lldpctl_k_dot3_power_supported:
+               return port->p_power.supported;
+       case lldpctl_k_dot3_power_enabled:
+               return port->p_power.enabled;
+       case lldpctl_k_dot3_power_paircontrol:
+               return port->p_power.paircontrol;
+       case lldpctl_k_dot3_power_pairs:
+               return port->p_power.pairs;
+       case lldpctl_k_dot3_power_class:
+               return port->p_power.class;
+       case lldpctl_k_dot3_power_type:
+               return port->p_power.powertype;
+       case lldpctl_k_dot3_power_source:
+               return port->p_power.source;
+       case lldpctl_k_dot3_power_priority:
+               return port->p_power.priority;
+       case lldpctl_k_dot3_power_requested:
+               return port->p_power.requested * 100;
+       case lldpctl_k_dot3_power_allocated:
+               return port->p_power.allocated * 100;
+       default:
+               return SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+       }
+}
+
+static lldpctl_atom_t*
+_lldpctl_atom_set_int_dot3_power(lldpctl_atom_t *atom, lldpctl_key_t key,
+    long int value)
+{
+       struct _lldpctl_atom_dot3_power_t *dpow =
+           (struct _lldpctl_atom_dot3_power_t *)atom;
+       struct lldpd_port *port = dpow->parent->port;
+
+       /* Only local port can be modified */
+       if (dpow->parent->hardware == NULL) {
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+
+       switch (key) {
+       case lldpctl_k_dot3_power_devicetype:
+               switch (value) {
+               case 0:         /* Disabling */
+               case LLDP_DOT3_POWER_PSE:
+               case LLDP_DOT3_POWER_PD:
+                       port->p_power.devicetype = value;
+                       return atom;
+               default: goto bad;
+               }
+       case lldpctl_k_dot3_power_supported:
+               switch (value) {
+               case 0:
+               case 1:
+                       port->p_power.supported = value;
+                       return atom;
+               default: goto bad;
+               }
+       case lldpctl_k_dot3_power_enabled:
+               switch (value) {
+               case 0:
+               case 1:
+                       port->p_power.enabled = value;
+                       return atom;
+               default: goto bad;
+               }
+       case lldpctl_k_dot3_power_paircontrol:
+               switch (value) {
+               case 0:
+               case 1:
+                       port->p_power.paircontrol = value;
+                       return atom;
+               default: goto bad;
+               }
+       case lldpctl_k_dot3_power_pairs:
+               switch (value) {
+               case 1:
+               case 2:
+                       port->p_power.pairs = value;
+                       return atom;
+               default: goto bad;
+               }
+       case lldpctl_k_dot3_power_class:
+               if (value < 0 || value > 5)
+                       goto bad;
+               port->p_power.class = value;
+               return atom;
+       case lldpctl_k_dot3_power_type:
+               switch (value) {
+               case LLDP_DOT3_POWER_8023AT_TYPE1:
+               case LLDP_DOT3_POWER_8023AT_TYPE2:
+               case LLDP_DOT3_POWER_8023AT_OFF:
+                       port->p_power.powertype = value;
+                       return atom;
+               default: goto bad;
+               }
+       case lldpctl_k_dot3_power_source:
+               if (value < 0 || value > 3)
+                       goto bad;
+               port->p_power.source = value;
+               return atom;
+       case lldpctl_k_dot3_power_priority:
+               switch (value) {
+               case LLDP_DOT3_POWER_PRIO_UNKNOWN:
+               case LLDP_DOT3_POWER_PRIO_CRITICAL:
+               case LLDP_DOT3_POWER_PRIO_HIGH:
+               case LLDP_DOT3_POWER_PRIO_LOW:
+                       port->p_power.priority = value;
+                       return atom;
+               default: goto bad;
+               }
+       case lldpctl_k_dot3_power_allocated:
+               if (value < 0) goto bad;
+               port->p_power.allocated = value / 100;
+               return atom;
+       case lldpctl_k_dot3_power_requested:
+               if (value < 0) goto bad;
+               port->p_power.requested = value / 100;
+               return atom;
+       default:
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+
+       return atom;
+bad:
+       SET_ERROR(atom->conn, LLDPCTL_ERR_BAD_VALUE);
+       return NULL;
+}
+
+static lldpctl_atom_t*
+_lldpctl_atom_set_str_dot3_power(lldpctl_atom_t *atom, lldpctl_key_t key,
+    const char *value)
+{
+       switch (key) {
+       case lldpctl_k_dot3_power_devicetype:
+               return _lldpctl_atom_set_int_dot3_power(atom, key,
+                   map_reverse_lookup(port_dot3_power_devicetype_map, value));
+       case lldpctl_k_dot3_power_pairs:
+               return _lldpctl_atom_set_int_dot3_power(atom, key,
+                   map_reverse_lookup(port_dot3_power_pairs_map, value));
+       case lldpctl_k_dot3_power_priority:
+               return _lldpctl_atom_set_int_dot3_power(atom, key,
+                   map_reverse_lookup(port_dot3_power_priority_map, value));
+       default:
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+}
+#endif
+
+#ifdef ENABLE_DOT1
+static lldpctl_atom_iter_t*
+_lldpctl_atom_iter_vlans_list(lldpctl_atom_t *atom)
+{
+       struct _lldpctl_atom_any_list_t *vlist =
+           (struct _lldpctl_atom_any_list_t *)atom;
+       return (lldpctl_atom_iter_t*)TAILQ_FIRST(&vlist->parent->port->p_vlans);
+}
+
+static lldpctl_atom_iter_t*
+_lldpctl_atom_next_vlans_list(lldpctl_atom_t *atom, lldpctl_atom_iter_t *iter)
+{
+       struct lldpd_vlan *vlan = (struct lldpd_vlan *)iter;
+       return (lldpctl_atom_iter_t*)TAILQ_NEXT(vlan, v_entries);
+}
+
+static lldpctl_atom_t*
+_lldpctl_atom_value_vlans_list(lldpctl_atom_t *atom, lldpctl_atom_iter_t *iter)
+{
+       struct _lldpctl_atom_any_list_t *vlist =
+           (struct _lldpctl_atom_any_list_t *)atom;
+       struct lldpd_vlan *vlan = (struct lldpd_vlan *)iter;
+       return _lldpctl_new_atom(atom->conn, atom_vlan, vlist->parent, vlan);
+}
+
+static int
+_lldpctl_atom_new_vlan(lldpctl_atom_t *atom, va_list ap)
+{
+       struct _lldpctl_atom_vlan_t *vlan =
+           (struct _lldpctl_atom_vlan_t *)atom;
+       vlan->parent = va_arg(ap, struct _lldpctl_atom_port_t *);
+       vlan->vlan = va_arg(ap, struct lldpd_vlan *);
+       lldpctl_atom_inc_ref((lldpctl_atom_t *)vlan->parent);
+       return 1;
+}
+
+static void
+_lldpctl_atom_free_vlan(lldpctl_atom_t *atom)
+{
+       struct _lldpctl_atom_vlan_t *vlan =
+           (struct _lldpctl_atom_vlan_t *)atom;
+       lldpctl_atom_dec_ref((lldpctl_atom_t *)vlan->parent);
+}
+
+static const char*
+_lldpctl_atom_get_str_vlan(lldpctl_atom_t *atom, lldpctl_key_t key)
+{
+       struct _lldpctl_atom_vlan_t *m =
+           (struct _lldpctl_atom_vlan_t *)atom;
+
+       /* Local and remote port */
+       switch (key) {
+       case lldpctl_k_vlan_name:
+               return m->vlan->v_name;
+       default:
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+}
+
+static long int
+_lldpctl_atom_get_int_vlan(lldpctl_atom_t *atom, lldpctl_key_t key)
+{
+       struct _lldpctl_atom_vlan_t *m =
+           (struct _lldpctl_atom_vlan_t *)atom;
+
+       /* Local and remote port */
+       switch (key) {
+       case lldpctl_k_vlan_id:
+               return m->vlan->v_vid;
+       default:
+               return SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+       }
+}
+
+static lldpctl_atom_iter_t*
+_lldpctl_atom_iter_ppvids_list(lldpctl_atom_t *atom)
+{
+       struct _lldpctl_atom_any_list_t *vlist =
+           (struct _lldpctl_atom_any_list_t *)atom;
+       return (lldpctl_atom_iter_t*)TAILQ_FIRST(&vlist->parent->port->p_ppvids);
+}
+
+static lldpctl_atom_iter_t*
+_lldpctl_atom_next_ppvids_list(lldpctl_atom_t *atom, lldpctl_atom_iter_t *iter)
+{
+       struct lldpd_ppvid *ppvid = (struct lldpd_ppvid *)iter;
+       return (lldpctl_atom_iter_t*)TAILQ_NEXT(ppvid, p_entries);
+}
+
+static lldpctl_atom_t*
+_lldpctl_atom_value_ppvids_list(lldpctl_atom_t *atom, lldpctl_atom_iter_t *iter)
+{
+       struct _lldpctl_atom_any_list_t *vlist =
+           (struct _lldpctl_atom_any_list_t *)atom;
+       struct lldpd_ppvid *ppvid = (struct lldpd_ppvid *)iter;
+       return _lldpctl_new_atom(atom->conn, atom_ppvid, vlist->parent, ppvid);
+}
+
+static int
+_lldpctl_atom_new_ppvid(lldpctl_atom_t *atom, va_list ap)
+{
+       struct _lldpctl_atom_ppvid_t *ppvid =
+           (struct _lldpctl_atom_ppvid_t *)atom;
+       ppvid->parent = va_arg(ap, struct _lldpctl_atom_port_t *);
+       ppvid->ppvid = va_arg(ap, struct lldpd_ppvid *);
+       lldpctl_atom_inc_ref((lldpctl_atom_t *)ppvid->parent);
+       return 1;
+}
+
+static void
+_lldpctl_atom_free_ppvid(lldpctl_atom_t *atom)
+{
+       struct _lldpctl_atom_ppvid_t *ppvid =
+           (struct _lldpctl_atom_ppvid_t *)atom;
+       lldpctl_atom_dec_ref((lldpctl_atom_t *)ppvid->parent);
+}
+
+static long int
+_lldpctl_atom_get_int_ppvid(lldpctl_atom_t *atom, lldpctl_key_t key)
+{
+       struct _lldpctl_atom_ppvid_t *m =
+           (struct _lldpctl_atom_ppvid_t *)atom;
+
+       /* Local and remote port */
+       switch (key) {
+       case lldpctl_k_ppvid_id:
+               return m->ppvid->p_ppvid;
+       case lldpctl_k_ppvid_status:
+               return m->ppvid->p_cap_status;
+       default:
+               return SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+       }
+}
+
+static lldpctl_atom_iter_t*
+_lldpctl_atom_iter_pis_list(lldpctl_atom_t *atom)
+{
+       struct _lldpctl_atom_any_list_t *vlist =
+           (struct _lldpctl_atom_any_list_t *)atom;
+       return (lldpctl_atom_iter_t*)TAILQ_FIRST(&vlist->parent->port->p_pids);
+}
+
+static lldpctl_atom_iter_t*
+_lldpctl_atom_next_pis_list(lldpctl_atom_t *atom, lldpctl_atom_iter_t *iter)
+{
+       struct lldpd_pi *pi = (struct lldpd_pi *)iter;
+       return (lldpctl_atom_iter_t*)TAILQ_NEXT(pi, p_entries);
+}
+
+static lldpctl_atom_t*
+_lldpctl_atom_value_pis_list(lldpctl_atom_t *atom, lldpctl_atom_iter_t *iter)
+{
+       struct _lldpctl_atom_any_list_t *vlist =
+           (struct _lldpctl_atom_any_list_t *)atom;
+       struct lldpd_pi *pi = (struct lldpd_pi *)iter;
+       return _lldpctl_new_atom(atom->conn, atom_pi, vlist->parent, pi);
+}
+
+static int
+_lldpctl_atom_new_pi(lldpctl_atom_t *atom, va_list ap)
+{
+       struct _lldpctl_atom_pi_t *pi =
+           (struct _lldpctl_atom_pi_t *)atom;
+       pi->parent = va_arg(ap, struct _lldpctl_atom_port_t *);
+       pi->pi = va_arg(ap, struct lldpd_pi *);
+       lldpctl_atom_inc_ref((lldpctl_atom_t *)pi->parent);
+       return 1;
+}
+
+static void
+_lldpctl_atom_free_pi(lldpctl_atom_t *atom)
+{
+       struct _lldpctl_atom_pi_t *pi =
+           (struct _lldpctl_atom_pi_t *)atom;
+       lldpctl_atom_dec_ref((lldpctl_atom_t *)pi->parent);
+}
+
+static const uint8_t*
+_lldpctl_atom_get_buf_pi(lldpctl_atom_t *atom, lldpctl_key_t key, size_t *n)
+{
+       struct _lldpctl_atom_pi_t *m =
+           (struct _lldpctl_atom_pi_t *)atom;
+
+       /* Local and remote port */
+       switch (key) {
+       case lldpctl_k_pi_id:
+               *n = m->pi->p_pi_len;
+               return (const uint8_t*)m->pi->p_pi;
+       default:
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+}
+#endif
+
+#ifdef ENABLE_LLDPMED
+static lldpctl_atom_iter_t*
+_lldpctl_atom_iter_med_policies_list(lldpctl_atom_t *atom)
+{
+       int i;
+       struct _lldpctl_atom_any_list_t *vlist =
+           (struct _lldpctl_atom_any_list_t *)atom;
+       for (i = 0; i < LLDP_MED_APPTYPE_LAST; i++)
+               vlist->parent->port->p_med_policy[i].index = i;
+       return (lldpctl_atom_iter_t*)&vlist->parent->port->p_med_policy[0];
+}
+
+static lldpctl_atom_iter_t*
+_lldpctl_atom_next_med_policies_list(lldpctl_atom_t *atom, lldpctl_atom_iter_t *iter)
+{
+       struct lldpd_med_policy *policy = (struct lldpd_med_policy *)iter;
+       if (policy->index == LLDP_MED_APPTYPE_LAST - 1) return NULL;
+       return (lldpctl_atom_iter_t*)(++policy);
+}
+
+static lldpctl_atom_t*
+_lldpctl_atom_value_med_policies_list(lldpctl_atom_t *atom, lldpctl_atom_iter_t *iter)
+{
+       struct _lldpctl_atom_any_list_t *vlist =
+           (struct _lldpctl_atom_any_list_t *)atom;
+       struct lldpd_med_policy *policy = (struct lldpd_med_policy *)iter;
+       return _lldpctl_new_atom(atom->conn, atom_med_policy, vlist->parent, policy);
+}
+
+static int
+_lldpctl_atom_new_med_policy(lldpctl_atom_t *atom, va_list ap)
+{
+       struct _lldpctl_atom_med_policy_t *policy =
+           (struct _lldpctl_atom_med_policy_t *)atom;
+       policy->parent = va_arg(ap, struct _lldpctl_atom_port_t *);
+       policy->policy = va_arg(ap, struct lldpd_med_policy *);
+       lldpctl_atom_inc_ref((lldpctl_atom_t *)policy->parent);
+       return 1;
+}
+
+static void
+_lldpctl_atom_free_med_policy(lldpctl_atom_t *atom)
+{
+       struct _lldpctl_atom_med_policy_t *policy =
+           (struct _lldpctl_atom_med_policy_t *)atom;
+       lldpctl_atom_dec_ref((lldpctl_atom_t *)policy->parent);
+}
+
+static long int
+_lldpctl_atom_get_int_med_policy(lldpctl_atom_t *atom, lldpctl_key_t key)
+{
+       struct _lldpctl_atom_med_policy_t *m =
+           (struct _lldpctl_atom_med_policy_t *)atom;
+
+       /* Local and remote port */
+       switch (key) {
+       case lldpctl_k_med_policy_type:
+               return m->policy->type;
+       case lldpctl_k_med_policy_unknown:
+               return m->policy->unknown;
+       case lldpctl_k_med_policy_tagged:
+               return m->policy->tagged;
+       case lldpctl_k_med_policy_vid:
+               return m->policy->vid;
+       case lldpctl_k_med_policy_dscp:
+               return m->policy->dscp;
+       case lldpctl_k_med_policy_priority:
+               return m->policy->priority;
+       default:
+               return SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+       }
+}
+
+static lldpctl_atom_t*
+_lldpctl_atom_set_int_med_policy(lldpctl_atom_t *atom, lldpctl_key_t key,
+    long int value)
+{
+       struct _lldpctl_atom_med_policy_t *m =
+           (struct _lldpctl_atom_med_policy_t *)atom;
+
+       /* Only local port can be modified */
+       if (m->parent->hardware == NULL) {
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+
+       switch (key) {
+       case lldpctl_k_med_policy_type:
+               /* We let set any policy type, including one whose are not
+                * compatible with the index. If a policy type is set, the index
+                * will be ignored. If a policy type is 0, the index will be
+                * used to know which policy to "erase". */
+               if (value < 0 || value > LLDP_MED_APPTYPE_LAST) goto bad;
+               m->policy->type = value;
+               return atom;
+       case lldpctl_k_med_policy_unknown:
+               if (value != 0 && value != 1) goto bad;
+               m->policy->unknown = value;
+               return atom;
+       case lldpctl_k_med_policy_tagged:
+               if (value != 0 && value != 1) goto bad;
+               m->policy->tagged = value;
+               return atom;
+       case lldpctl_k_med_policy_vid:
+               if (value < 0 || value > 4094) goto bad;
+               m->policy->vid = value;
+               return atom;
+       case lldpctl_k_med_policy_dscp:
+               if (value < 0 || value > 63) goto bad;
+               m->policy->dscp = value;
+               return atom;
+       case lldpctl_k_med_policy_priority:
+               if (value < 0 || value > 7) goto bad;
+               m->policy->priority = value;
+               return atom;
+       default:
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+
+       return atom;
+bad:
+       SET_ERROR(atom->conn, LLDPCTL_ERR_BAD_VALUE);
+       return NULL;
+}
+
+static const char*
+_lldpctl_atom_get_str_med_policy(lldpctl_atom_t *atom, lldpctl_key_t key)
+{
+       struct _lldpctl_atom_med_policy_t *m =
+           (struct _lldpctl_atom_med_policy_t *)atom;
+
+       /* Local and remote port */
+       switch (key) {
+       case lldpctl_k_med_policy_type:
+               return map_lookup(port_med_policy_map, m->policy->type);
+       default:
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+}
+
+static lldpctl_atom_iter_t*
+_lldpctl_atom_iter_med_locations_list(lldpctl_atom_t *atom)
+{
+       int i;
+       struct _lldpctl_atom_any_list_t *vlist =
+           (struct _lldpctl_atom_any_list_t *)atom;
+       for (i = 0; i < LLDP_MED_LOCFORMAT_LAST; i++)
+               vlist->parent->port->p_med_location[i].index = i;
+       return (lldpctl_atom_iter_t*)&vlist->parent->port->p_med_location[0];
+}
+
+static lldpctl_atom_iter_t*
+_lldpctl_atom_next_med_locations_list(lldpctl_atom_t *atom, lldpctl_atom_iter_t *iter)
+{
+       struct lldpd_med_loc *location = (struct lldpd_med_loc *)iter;
+       if (location->index == LLDP_MED_LOCFORMAT_LAST - 1) return NULL;
+       return (lldpctl_atom_iter_t*)(++location);
+}
+
+static lldpctl_atom_t*
+_lldpctl_atom_value_med_locations_list(lldpctl_atom_t *atom, lldpctl_atom_iter_t *iter)
+{
+       struct _lldpctl_atom_any_list_t *vlist =
+           (struct _lldpctl_atom_any_list_t *)atom;
+       struct lldpd_med_loc *location = (struct lldpd_med_loc *)iter;
+       return _lldpctl_new_atom(atom->conn, atom_med_location, vlist->parent, location);
+}
+
+static int
+_lldpctl_atom_new_med_location(lldpctl_atom_t *atom, va_list ap)
+{
+       struct _lldpctl_atom_med_location_t *location =
+           (struct _lldpctl_atom_med_location_t *)atom;
+       location->parent = va_arg(ap, struct _lldpctl_atom_port_t *);
+       location->location = va_arg(ap, struct lldpd_med_loc *);
+       lldpctl_atom_inc_ref((lldpctl_atom_t *)location->parent);
+       return 1;
+}
+
+static void
+_lldpctl_atom_free_med_location(lldpctl_atom_t *atom)
+{
+       struct _lldpctl_atom_med_location_t *location =
+           (struct _lldpctl_atom_med_location_t *)atom;
+       lldpctl_atom_dec_ref((lldpctl_atom_t *)location->parent);
+}
+
+static long int
+_lldpctl_atom_get_int_med_location(lldpctl_atom_t *atom, lldpctl_key_t key)
+{
+       struct _lldpctl_atom_med_location_t *m =
+           (struct _lldpctl_atom_med_location_t *)atom;
+
+       /* Local and remote port */
+       switch (key) {
+       case lldpctl_k_med_location_format:
+               switch (m->location->format) {
+               case LLDP_MED_LOCFORMAT_COORD:
+                       if (m->location->data_len != 16) break;
+                       return LLDP_MED_LOCFORMAT_COORD;
+               case LLDP_MED_LOCFORMAT_CIVIC:
+                       if ((m->location->data_len < 3) ||
+                           (m->location->data_len - 1 !=
+                               m->location->data[0])) break;
+                       return LLDP_MED_LOCFORMAT_CIVIC;
+               case LLDP_MED_LOCFORMAT_ELIN:
+                       return LLDP_MED_LOCFORMAT_ELIN;
+               default:
+                       return 0;
+               }
+               return SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+       case lldpctl_k_med_location_geoid:
+               if (m->location->format != LLDP_MED_LOCFORMAT_COORD)
+                       return SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return m->location->data[15];
+       case lldpctl_k_med_location_altitude_unit:
+               if (m->location->format != LLDP_MED_LOCFORMAT_COORD)
+                       return SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return (m->location->data[10] & 0xf0) >> 4;
+       default:
+               return SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+       }
+}
+
+static lldpctl_atom_t*
+_lldpctl_atom_set_int_med_location(lldpctl_atom_t *atom, lldpctl_key_t key,
+    long int value)
+{
+       struct _lldpctl_atom_med_location_t *mloc =
+           (struct _lldpctl_atom_med_location_t *)atom;
+
+       /* Only local port can be modified */
+       if (mloc->parent->hardware == NULL) {
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+
+       switch (key) {
+       case lldpctl_k_med_location_format:
+               switch (value) {
+               case 0:         /* Disabling */
+               case LLDP_MED_LOCFORMAT_COORD:
+                       mloc->location->format = value;
+                       if (mloc->location->data) free(mloc->location->data);
+                       mloc->location->data = calloc(1, 16);
+                       if (mloc->location->data == NULL) {
+                               mloc->location->data_len = 0;
+                               SET_ERROR(atom->conn, LLDPCTL_ERR_NOMEM);
+                               return NULL;
+                       }
+                       mloc->location->data_len = 16;
+                       return atom;
+               case LLDP_MED_LOCFORMAT_CIVIC:
+                       mloc->location->format = value;
+                       if (mloc->location->data) free(mloc->location->data);
+                       mloc->location->data = calloc(1, 4);
+                       if (mloc->location->data == NULL) {
+                               mloc->location->data_len = 0;
+                               SET_ERROR(atom->conn, LLDPCTL_ERR_NOMEM);
+                               return NULL;
+                       }
+                       mloc->location->data_len = 4;
+                       mloc->location->data[0] = 3;
+                       mloc->location->data[1] = 2; /* Client */
+                       mloc->location->data[2] = 'U';
+                       mloc->location->data[3] = 'S';
+                       return atom;
+               case LLDP_MED_LOCFORMAT_ELIN:
+                       mloc->location->format = value;
+                       mloc->location->data = NULL;
+                       mloc->location->data_len = 0;
+                       return atom;
+               default: goto bad;
+               }
+       case lldpctl_k_med_location_geoid:
+               if (mloc->location->format != LLDP_MED_LOCFORMAT_COORD) goto bad;
+               if (mloc->location->data == NULL || mloc->location->data_len != 16) goto bad;
+               switch (value) {
+               case 0:
+               case LLDP_MED_LOCATION_GEOID_WGS84:
+               case LLDP_MED_LOCATION_GEOID_NAD83:
+               case LLDP_MED_LOCATION_GEOID_NAD83_MLLW:
+                       mloc->location->data[15] = value;
+                       return atom;
+               default: goto bad;
+               }
+       case lldpctl_k_med_location_altitude_unit:
+               if (mloc->location->format != LLDP_MED_LOCFORMAT_COORD) goto bad;
+               if (mloc->location->data == NULL || mloc->location->data_len != 16) goto bad;
+               switch (value) {
+               case 0:
+               case LLDP_MED_LOCATION_ALTITUDE_UNIT_METER:
+               case LLDP_MED_LOCATION_ALTITUDE_UNIT_FLOOR:
+                       mloc->location->data[10] = value << 4;
+                       return atom;
+               default: goto bad;
+               }
+       default:
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+
+       return atom;
+bad:
+       SET_ERROR(atom->conn, LLDPCTL_ERR_BAD_VALUE);
+       return NULL;
+
+}
+
+static const char*
+fixed_precision(lldpctl_atom_t *atom,
+    u_int64_t value, int intpart, int floatpart, int displaysign,
+    const char *negsuffix, const char *possuffix)
+{
+       char *buf;
+       u_int64_t tmp = value;
+       int negative = 0, n;
+       u_int32_t integer = 0;
+       if (value & (1ULL << (intpart + floatpart - 1))) {
+               negative = 1;
+               tmp = ~value;
+               tmp += 1;
+       }
+       integer = (u_int32_t)((tmp &
+               (((1ULL << intpart)-1) << floatpart)) >> floatpart);
+       tmp = (tmp & ((1<< floatpart) - 1))*10000/(1ULL << floatpart);
+
+       if ((buf = _lldpctl_alloc_in_atom(atom, 64)) == NULL)
+               return NULL;
+       n = snprintf(buf, 64, "%s%u.%04llu%s",
+           displaysign?(negative?"-":"+"):"",
+           integer, (unsigned long long int)tmp,
+           (negative && negsuffix)?negsuffix:
+           (!negative && possuffix)?possuffix:"");
+       if (n > -1 && n < 64)
+               return buf;
+       SET_ERROR(atom->conn, LLDPCTL_ERR_NOMEM);
+       return NULL;
+}
+
+static const char*
+_lldpctl_atom_get_str_med_location(lldpctl_atom_t *atom, lldpctl_key_t key)
+{
+       struct _lldpctl_atom_med_location_t *m =
+           (struct _lldpctl_atom_med_location_t *)atom;
+       char *value;
+       u_int64_t l;
+
+       /* Local and remote port */
+       switch (key) {
+       case lldpctl_k_med_location_format:
+               return map_lookup(port_med_location_map, m->location->format);
+       case lldpctl_k_med_location_geoid:
+               if (m->location->format != LLDP_MED_LOCFORMAT_COORD) break;
+               return map_lookup(port_med_geoid_map,
+                   m->location->data[15]);
+       case lldpctl_k_med_location_latitude:
+               if (m->location->format != LLDP_MED_LOCFORMAT_COORD) break;
+               memcpy(&l, m->location->data, sizeof(u_int64_t));
+               l = (ntohll(l) & 0x03FFFFFFFF000000ULL) >> 24;
+               return fixed_precision(atom, l, 9, 25, 0, " S", " N");
+       case lldpctl_k_med_location_longitude:
+               if (m->location->format != LLDP_MED_LOCFORMAT_COORD) break;
+               memcpy(&l, m->location->data + 5, sizeof(u_int64_t));
+               l = (ntohll(l) & 0x03FFFFFFFF000000ULL) >> 24;
+               return fixed_precision(atom, l, 9, 25, 0, " W", " E");
+       case lldpctl_k_med_location_altitude:
+               if (m->location->format != LLDP_MED_LOCFORMAT_COORD) break;
+               memset(&l, 0, sizeof(u_int64_t));
+               memcpy(&l, m->location->data + 10, 5);
+               l = (ntohll(l) & 0x3FFFFFFF000000ULL) >> 24;
+               return fixed_precision(atom, l, 22, 8, 1, NULL, NULL);
+       case lldpctl_k_med_location_altitude_unit:
+               if (m->location->format != LLDP_MED_LOCFORMAT_COORD) break;
+               switch (m->location->data[10] & 0xf0) {
+               case (LLDP_MED_LOCATION_ALTITUDE_UNIT_METER << 4):
+                       return "m";
+               case (LLDP_MED_LOCATION_ALTITUDE_UNIT_FLOOR << 4):
+                       return "floor";
+               }
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       case lldpctl_k_med_location_country:
+               if (m->location->format != LLDP_MED_LOCFORMAT_CIVIC) break;
+               value = _lldpctl_alloc_in_atom(atom, 3);
+               if (!value) return NULL;
+               memcpy(value, m->location->data + 2, 2);
+               return value;
+       case lldpctl_k_med_location_elin:
+               if (m->location->format != LLDP_MED_LOCFORMAT_ELIN) break;
+               value = _lldpctl_alloc_in_atom(atom, m->location->data_len + 1);
+               if (!value) return NULL;
+               memcpy(value, m->location->data, m->location->data_len);
+               return value;
+       default:
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+       SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+       return NULL;
+}
+
+static void
+write_fixed_precision(uint8_t *where, double l,
+    int precisionnb, int intnb, int floatnb)
+{
+       int intpart, floatpart, precision = 6;
+       if (l > 0) {
+               intpart = (int)l;
+               floatpart = (l - intpart) * (1 << floatnb);
+       } else {
+               intpart = -(int)l;
+               floatpart = (-(l + intpart)) * (1 << floatnb);
+               intpart = ~intpart; intpart += 1;
+               floatpart = ~floatpart; floatpart += 1;
+       }
+       if ((1 << precisionnb) - 1 < precision)
+               precision = (1 << precisionnb) - 1;
+       /* We need to write precision, int part and float part. */
+       do {
+               int obit, i, o;
+               unsigned int ints[3] = { precision, intpart, floatpart };
+               unsigned int bits[3] = { precisionnb, intnb, floatnb };
+               for (i = 0, obit = 8, o = 0; i < 3;) {
+                       if (obit > bits[i]) {
+                               where[o] = where[o] |
+                                   ((ints[i] & ((1 << bits[i]) - 1)) << (obit - bits[i]));
+                               obit -= bits[i];
+                               i++;
+                       } else {
+                               where[o] = where[o] |
+                                   ((ints[i] >> (bits[i] - obit)) & ((1 << obit) - 1));
+                               bits[i] -= obit;
+                               obit = 8;
+                               o++;
+                       }
+               }
+       } while(0);
+}
+
+static lldpctl_atom_t*
+_lldpctl_atom_set_str_med_location(lldpctl_atom_t *atom, lldpctl_key_t key,
+    const char *value)
+{
+       struct _lldpctl_atom_med_location_t *mloc =
+           (struct _lldpctl_atom_med_location_t *)atom;
+       double l;
+       char *end;
+
+       /* Only local port can be modified */
+       if (mloc->parent->hardware == NULL) {
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+
+       switch (key) {
+       case lldpctl_k_med_location_latitude:
+               if (mloc->location->format != LLDP_MED_LOCFORMAT_COORD) goto bad;
+               if (mloc->location->data == NULL || mloc->location->data_len != 16) goto bad;
+               l = strtod(value, &end);
+               if (!end) goto bad;
+               if (end && *end != '\0') {
+                       if (*(end+1) != '\0') goto bad;
+                       if (*end == 'S') l = -l;
+                       else if (*end != 'N') goto bad;
+               }
+               write_fixed_precision((uint8_t*)mloc->location->data, l, 6, 9, 25);
+               return atom;
+       case lldpctl_k_med_location_longitude:
+               if (mloc->location->format != LLDP_MED_LOCFORMAT_COORD) goto bad;
+               if (mloc->location->data == NULL || mloc->location->data_len != 16) goto bad;
+               l = strtod(value, &end);
+               if (!end) goto bad;
+               if (end && *end != '\0') {
+                       if (*(end+1) != '\0') goto bad;
+                       if (*end == 'W') l = -l;
+                       else if (*end != 'E') goto bad;
+               }
+               write_fixed_precision((uint8_t*)mloc->location->data + 5, l, 6, 9, 25);
+               return atom;
+       case lldpctl_k_med_location_altitude:
+               if (mloc->location->format != LLDP_MED_LOCFORMAT_COORD) goto bad;
+               if (mloc->location->data == NULL || mloc->location->data_len != 16) goto bad;
+               l = strtod(value, &end);
+               if (!end || *end != '\0') goto bad;
+               write_fixed_precision((uint8_t*)mloc->location->data + 11, l, 2, 22, 8);
+               return atom;
+       case lldpctl_k_med_location_altitude_unit:
+               if (mloc->location->format != LLDP_MED_LOCFORMAT_COORD) goto bad;
+               if (mloc->location->data == NULL || mloc->location->data_len != 16) goto bad;
+               if (!strcmp(value, "m"))
+                       return _lldpctl_atom_set_int_med_location(atom, key,
+                           LLDP_MED_LOCATION_ALTITUDE_UNIT_METER);
+               if (!strcmp(value, "f") ||
+                   (!strcmp(value, "floor")))
+                       return _lldpctl_atom_set_int_med_location(atom, key,
+                           LLDP_MED_LOCATION_ALTITUDE_UNIT_FLOOR);
+               goto bad;
+       case lldpctl_k_med_location_country:
+               if (mloc->location->format != LLDP_MED_LOCFORMAT_CIVIC) goto bad;
+               if (mloc->location->data == NULL || mloc->location->data_len < 3) goto bad;
+               if (strlen(value) != 2) goto bad;
+               memcpy(mloc->location->data + 2, value, 2);
+               return atom;
+       case lldpctl_k_med_location_elin:
+               if (mloc->location->format != LLDP_MED_LOCFORMAT_ELIN) goto bad;
+               if (mloc->location->data) free(mloc->location->data);
+               mloc->location->data = calloc(1, strlen(value));
+               if (mloc->location->data == NULL) {
+                       mloc->location->data_len = 0;
+                       SET_ERROR(atom->conn, LLDPCTL_ERR_NOMEM);
+                       return NULL;
+               }
+               mloc->location->data_len = strlen(value);
+               memcpy(mloc->location->data, value,
+                   mloc->location->data_len);
+               return atom;
+       default:
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+
+       return atom;
+bad:
+       SET_ERROR(atom->conn, LLDPCTL_ERR_BAD_VALUE);
+       return NULL;
+
+}
+
+static lldpctl_atom_t*
+_lldpctl_atom_get_atom_med_location(lldpctl_atom_t *atom, lldpctl_key_t key)
+{
+       struct _lldpctl_atom_med_location_t *m =
+           (struct _lldpctl_atom_med_location_t *)atom;
+
+       /* Local and remote port */
+       switch (key) {
+       case lldpctl_k_med_location_ca_elements:
+               if (m->location->format != LLDP_MED_LOCFORMAT_CIVIC) {
+                       SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+                       return NULL;
+               }
+               return _lldpctl_new_atom(atom->conn, atom_med_caelements_list, m);
+       default:
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+}
+
+static lldpctl_atom_t*
+_lldpctl_atom_set_atom_med_location(lldpctl_atom_t *atom, lldpctl_key_t key,
+    lldpctl_atom_t *value)
+{
+       struct _lldpctl_atom_med_location_t *m =
+           (struct _lldpctl_atom_med_location_t *)atom;
+       struct _lldpctl_atom_med_caelement_t *el;
+       uint8_t *new;
+
+       /* Only local port can be modified */
+       if (m->parent->hardware == NULL) {
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+
+       switch (key) {
+       case lldpctl_k_med_location_ca_elements:
+               if (value->type != atom_med_caelement) {
+                       SET_ERROR(atom->conn, LLDPCTL_ERR_INCORRECT_ATOM_TYPE);
+                       return NULL;
+               }
+               if (m->location->format != LLDP_MED_LOCFORMAT_CIVIC) goto bad;
+               if (m->location->data == NULL || m->location->data_len < 3) goto bad;
+
+               /* We append this element. */
+               el = (struct _lldpctl_atom_med_caelement_t *)value;
+               new = malloc(m->location->data_len + 2 + el->len);
+               if (new == NULL) {
+                       SET_ERROR(atom->conn, LLDPCTL_ERR_NOMEM);
+                       return NULL;
+               }
+               memcpy(new, m->location->data, m->location->data_len);
+               new[m->location->data_len] = el->type;
+               new[m->location->data_len + 1] = el->len;
+               memcpy(new + m->location->data_len + 2, el->value, el->len);
+               new[0] += 2 + el->len;
+               free(m->location->data);
+               m->location->data = (char*)new;
+               m->location->data_len += 2 + el->len;
+               return atom;
+       default:
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+bad:
+       SET_ERROR(atom->conn, LLDPCTL_ERR_BAD_VALUE);
+       return NULL;
+}
+
+struct ca_iter {
+       uint8_t *data;
+       size_t data_len;
+};
+
+static lldpctl_atom_iter_t*
+_lldpctl_atom_iter_med_caelements_list(lldpctl_atom_t *atom)
+{
+       struct _lldpctl_atom_med_caelements_list_t *plist =
+           (struct _lldpctl_atom_med_caelements_list_t *)atom;
+       struct ca_iter *iter = _lldpctl_alloc_in_atom(atom, sizeof(struct ca_iter));
+       if (!iter) return NULL;
+       iter->data = (uint8_t*)plist->parent->location->data + 4;
+       iter->data_len = plist->parent->location->data_len - 4;
+       return (lldpctl_atom_iter_t*)iter;
+}
+
+static lldpctl_atom_iter_t*
+_lldpctl_atom_next_med_caelements_list(lldpctl_atom_t *atom, lldpctl_atom_iter_t *iter)
+{
+       struct ca_iter *cai = (struct ca_iter *)iter;
+       int len;
+       if (cai->data_len < 2) return NULL;
+       len = *((uint8_t *)cai->data + 1);
+       if (cai->data_len < 2 + len) return NULL;
+       cai->data += 2 + len;
+       cai->data_len -= 2 + len;
+       return (lldpctl_atom_iter_t*)cai;
+}
+
+static lldpctl_atom_t*
+_lldpctl_atom_value_med_caelements_list(lldpctl_atom_t *atom, lldpctl_atom_iter_t *iter)
+{
+       struct _lldpctl_atom_med_caelements_list_t *plist =
+           (struct _lldpctl_atom_med_caelements_list_t *)atom;
+       struct ca_iter *cai = (struct ca_iter *)iter;
+       size_t len;
+       if (cai->data_len < 2) return NULL;
+       len = *((uint8_t *)cai->data + 1);
+       if (cai->data_len < 2 + len) return NULL;
+       return _lldpctl_new_atom(atom->conn, atom_med_caelement, plist->parent,
+           (int)*cai->data, cai->data + 2, len);
+}
+
+static lldpctl_atom_t*
+_lldpctl_atom_create_med_caelements_list(lldpctl_atom_t *atom)
+{
+       struct _lldpctl_atom_med_caelements_list_t *plist =
+           (struct _lldpctl_atom_med_caelements_list_t *)atom;
+       return _lldpctl_new_atom(atom->conn, atom_med_caelement, plist->parent,
+           -1, NULL, 0);
+}
+
+static int
+_lldpctl_atom_new_med_caelement(lldpctl_atom_t *atom, va_list ap)
+{
+       struct _lldpctl_atom_med_caelement_t *el =
+           (struct _lldpctl_atom_med_caelement_t *)atom;
+       el->parent = va_arg(ap, struct _lldpctl_atom_med_location_t *);
+       el->type   = va_arg(ap, int);
+       el->value  = va_arg(ap, uint8_t*);
+       el->len    = va_arg(ap, size_t);
+       lldpctl_atom_inc_ref((lldpctl_atom_t *)el->parent);
+       return 1;
+}
+
+static void
+_lldpctl_atom_free_med_caelement(lldpctl_atom_t *atom)
+{
+       struct _lldpctl_atom_med_caelement_t *el =
+           (struct _lldpctl_atom_med_caelement_t *)atom;
+       lldpctl_atom_dec_ref((lldpctl_atom_t *)el->parent);
+}
+
+static const char*
+_lldpctl_atom_get_str_med_caelement(lldpctl_atom_t *atom, lldpctl_key_t key)
+{
+       char *value = NULL;
+       struct _lldpctl_atom_med_caelement_t *m =
+           (struct _lldpctl_atom_med_caelement_t *)atom;
+
+       /* Local and remote port */
+       switch (key) {
+       case lldpctl_k_med_civicaddress_type:
+               return map_lookup(civic_address_type_map, m->type);
+       case lldpctl_k_med_civicaddress_value:
+               value = _lldpctl_alloc_in_atom(atom, m->len + 1);
+               if (!value) return NULL;
+               memcpy(value, m->value, m->len);
+               return value;
+       default:
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+}
+
+static lldpctl_atom_t*
+_lldpctl_atom_set_str_med_caelement(lldpctl_atom_t *atom, lldpctl_key_t key,
+    const char *value)
+{
+       struct _lldpctl_atom_med_caelement_t *el =
+           (struct _lldpctl_atom_med_caelement_t *)atom;
+
+       /* Only local port can be modified */
+       if (el->parent->parent->hardware == NULL) {
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+
+       switch (key) {
+       case lldpctl_k_med_civicaddress_value:
+               if (strlen(value) > 250) goto bad;
+               el->value = _lldpctl_alloc_in_atom(atom, strlen(value) + 1);
+               if (el->value == NULL) return NULL;
+               strcpy((char*)el->value, value);
+               el->len = strlen(value);
+               return atom;
+       default:
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+
+       return atom;
+bad:
+       SET_ERROR(atom->conn, LLDPCTL_ERR_BAD_VALUE);
+       return NULL;
+}
+
+static long int
+_lldpctl_atom_get_int_med_caelement(lldpctl_atom_t *atom, lldpctl_key_t key)
+{
+       struct _lldpctl_atom_med_caelement_t *m =
+           (struct _lldpctl_atom_med_caelement_t *)atom;
+
+       switch (key) {
+       case lldpctl_k_med_civicaddress_type:
+               return m->type;
+       default:
+               return SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+       }
+}
+
+static lldpctl_atom_t*
+_lldpctl_atom_set_int_med_caelement(lldpctl_atom_t *atom, lldpctl_key_t key,
+    long int value)
+{
+       struct _lldpctl_atom_med_caelement_t *el =
+           (struct _lldpctl_atom_med_caelement_t *)atom;
+
+       /* Only local port can be modified */
+       if (el->parent->parent->hardware == NULL) {
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+
+       switch (key) {
+       case lldpctl_k_med_civicaddress_type:
+               if (value <= 0 || value > 128) goto bad;
+               el->type = value;
+               return atom;
+       default:
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+
+       return atom;
+bad:
+       SET_ERROR(atom->conn, LLDPCTL_ERR_BAD_VALUE);
+       return NULL;
+}
+
+static int
+_lldpctl_atom_new_med_power(lldpctl_atom_t *atom, va_list ap)
+{
+       struct _lldpctl_atom_med_power_t *mpow =
+           (struct _lldpctl_atom_med_power_t *)atom;
+       mpow->parent = va_arg(ap, struct _lldpctl_atom_port_t *);
+       lldpctl_atom_inc_ref((lldpctl_atom_t *)mpow->parent);
+       return 1;
+}
+
+static void
+_lldpctl_atom_free_med_power(lldpctl_atom_t *atom)
+{
+       struct _lldpctl_atom_med_power_t *mpow =
+           (struct _lldpctl_atom_med_power_t *)atom;
+       lldpctl_atom_dec_ref((lldpctl_atom_t *)mpow->parent);
+}
+
+static const char*
+_lldpctl_atom_get_str_med_power(lldpctl_atom_t *atom, lldpctl_key_t key)
+{
+       struct _lldpctl_atom_med_power_t *mpow =
+           (struct _lldpctl_atom_med_power_t *)atom;
+       struct lldpd_port *port = mpow->parent->port;
+
+       /* Local and remote port */
+       switch (key) {
+       case lldpctl_k_med_power_type:
+               return map_lookup(port_med_pow_devicetype_map,
+                   port->p_med_power.devicetype);
+       case lldpctl_k_med_power_source:
+               return map_lookup(port_med_pow_source_map,
+                   port->p_med_power.source);
+       case lldpctl_k_med_power_priority:
+               return map_lookup(port_med_pow_priority_map,
+                   port->p_med_power.priority);
+       default:
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+}
+
+static long int
+_lldpctl_atom_get_int_med_power(lldpctl_atom_t *atom, lldpctl_key_t key)
+{
+       struct _lldpctl_atom_med_power_t *dpow =
+           (struct _lldpctl_atom_med_power_t *)atom;
+       struct lldpd_port     *port     = dpow->parent->port;
+
+       /* Local and remote port */
+       switch (key) {
+       case lldpctl_k_med_power_type:
+               return port->p_med_power.devicetype;
+       case lldpctl_k_med_power_source:
+               return port->p_med_power.source;
+       case lldpctl_k_med_power_priority:
+               return port->p_med_power.priority;
+       case lldpctl_k_med_power_val:
+               return port->p_med_power.val * 100;
+       default:
+               return SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+       }
+}
+
+static lldpctl_atom_t*
+_lldpctl_atom_set_int_med_power(lldpctl_atom_t *atom, lldpctl_key_t key,
+    long int value)
+{
+       struct _lldpctl_atom_med_power_t *dpow =
+           (struct _lldpctl_atom_med_power_t *)atom;
+       struct lldpd_port *port = dpow->parent->port;
+
+       /* Only local port can be modified */
+       if (dpow->parent->hardware == NULL) {
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+
+       switch (key) {
+       case lldpctl_k_med_power_type:
+               switch (value) {
+               case 0:
+               case LLDP_MED_POW_TYPE_PSE:
+               case LLDP_MED_POW_TYPE_PD:
+                       port->p_med_power.devicetype = value;
+                       return atom;
+               default: goto bad;
+               }
+       case lldpctl_k_med_power_source:
+               switch (value) {
+               case LLDP_MED_POW_SOURCE_PRIMARY:
+               case LLDP_MED_POW_SOURCE_BACKUP:
+                       if (port->p_med_power.devicetype != LLDP_MED_POW_TYPE_PSE)
+                               goto bad;
+                       port->p_med_power.source = value;
+                       return atom;
+               case LLDP_MED_POW_SOURCE_PSE:
+               case LLDP_MED_POW_SOURCE_LOCAL:
+               case LLDP_MED_POW_SOURCE_BOTH:
+                       if (port->p_med_power.devicetype != LLDP_MED_POW_TYPE_PD)
+                               goto bad;
+                       port->p_med_power.source = value;
+                       return atom;
+               case LLDP_MED_POW_SOURCE_UNKNOWN:
+                       port->p_med_power.source = value;
+                       return atom;
+               default: goto bad;
+               }
+       case lldpctl_k_med_power_priority:
+               if (value < 0 || value > 3) goto bad;
+               port->p_med_power.priority = value;
+               return atom;
+       case lldpctl_k_med_power_val:
+               if (value < 0) goto bad;
+               port->p_med_power.val = value / 100;
+               return atom;
+       default:
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+
+       return atom;
+bad:
+       SET_ERROR(atom->conn, LLDPCTL_ERR_BAD_VALUE);
+       return NULL;
+}
+
+static lldpctl_atom_t*
+_lldpctl_atom_set_str_med_power(lldpctl_atom_t *atom, lldpctl_key_t key,
+    const char *value)
+{
+       switch (key) {
+       case lldpctl_k_med_power_type:
+               return _lldpctl_atom_set_int_med_power(atom, key,
+                   map_reverse_lookup(port_med_pow_devicetype_map, value));
+       case lldpctl_k_med_power_source:
+               return _lldpctl_atom_set_int_med_power(atom, key,
+                   map_reverse_lookup(port_med_pow_source_map2, value));
+       case lldpctl_k_med_power_priority:
+               return _lldpctl_atom_set_int_med_power(atom, key,
+                   map_reverse_lookup(port_med_pow_priority_map, value));
+       default:
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+}
+#endif
+
+struct atom_builder {
+       atom_t type;    /* Atom type */
+       size_t size;    /* Size of structure to allocate */
+       int  (*init)(lldpctl_atom_t *, va_list); /* Optional additional init steps */
+       void (*free)(lldpctl_atom_t *); /* Optional deallocation steps */
+
+       lldpctl_atom_iter_t* (*iter)(lldpctl_atom_t *); /* Optional, return an iterator for this object */
+       lldpctl_atom_iter_t* (*next)(lldpctl_atom_t *,  lldpctl_atom_iter_t *); /* Return the next object for the provided iterator */
+       lldpctl_atom_t*      (*value)(lldpctl_atom_t *, lldpctl_atom_iter_t *); /* Return the current object for the provided iterator */
+
+       lldpctl_atom_t*      (*get)(lldpctl_atom_t *,        lldpctl_key_t);
+       const char*          (*get_str)(lldpctl_atom_t *,    lldpctl_key_t);
+       const u_int8_t*      (*get_buffer)(lldpctl_atom_t *, lldpctl_key_t, size_t *);
+       long int             (*get_int)(lldpctl_atom_t *,    lldpctl_key_t);
+
+       lldpctl_atom_t*      (*set)(lldpctl_atom_t *, lldpctl_key_t, lldpctl_atom_t *);
+       lldpctl_atom_t*      (*set_str)(lldpctl_atom_t *, lldpctl_key_t, const char *);
+       lldpctl_atom_t*      (*set_buffer)(lldpctl_atom_t *, lldpctl_key_t, const u_int8_t *, size_t);
+       lldpctl_atom_t*      (*set_int)(lldpctl_atom_t *, lldpctl_key_t, long int);
+       lldpctl_atom_t*      (*create)(lldpctl_atom_t *);
+};
+
+struct atom_builder builders[] = {
+       { atom_interfaces_list, sizeof(struct _lldpctl_atom_interfaces_list_t),
+         .init  = _lldpctl_atom_new_interfaces_list,
+         .free  = _lldpctl_atom_free_interfaces_list,
+         .iter  = _lldpctl_atom_iter_interfaces_list,
+         .next  = _lldpctl_atom_next_interfaces_list,
+         .value = _lldpctl_atom_value_interfaces_list },
+       { atom_interface, sizeof(struct _lldpctl_atom_interface_t),
+         .init = _lldpctl_atom_new_interface,
+         .free = _lldpctl_atom_free_interface,
+         .get_str = _lldpctl_atom_get_str_interface },
+       { atom_ports_list, sizeof(struct _lldpctl_atom_any_list_t),
+         .init = _lldpctl_atom_new_any_list,
+         .free = _lldpctl_atom_free_any_list,
+         .iter = _lldpctl_atom_iter_ports_list,
+         .next = _lldpctl_atom_next_ports_list,
+         .value = _lldpctl_atom_value_ports_list },
+       { atom_port, sizeof(struct _lldpctl_atom_port_t),
+         .init = _lldpctl_atom_new_port,
+         .free = _lldpctl_atom_free_port,
+         .get  = _lldpctl_atom_get_atom_port,
+         .set  = _lldpctl_atom_set_atom_port,
+         .get_str = _lldpctl_atom_get_str_port,
+         .get_int = _lldpctl_atom_get_int_port,
+         .get_buffer = _lldpctl_atom_get_buf_port },
+       { atom_mgmts_list, sizeof(struct _lldpctl_atom_mgmts_list_t),
+         .init = _lldpctl_atom_new_mgmts_list,
+         .free = _lldpctl_atom_free_mgmts_list,
+         .iter = _lldpctl_atom_iter_mgmts_list,
+         .next = _lldpctl_atom_next_mgmts_list,
+         .value = _lldpctl_atom_value_mgmts_list },
+       { atom_mgmt, sizeof(struct _lldpctl_atom_mgmt_t),
+         .init = _lldpctl_atom_new_mgmt,
+         .free = _lldpctl_atom_free_mgmt,
+         .get_str = _lldpctl_atom_get_str_mgmt },
+#ifdef ENABLE_DOT3
+       { atom_dot3_power, sizeof(struct _lldpctl_atom_dot3_power_t),
+         .init = _lldpctl_atom_new_dot3_power,
+         .free = _lldpctl_atom_free_dot3_power,
+         .get_int = _lldpctl_atom_get_int_dot3_power,
+         .set_int = _lldpctl_atom_set_int_dot3_power,
+         .get_str = _lldpctl_atom_get_str_dot3_power,
+         .set_str = _lldpctl_atom_set_str_dot3_power },
+#endif
+#ifdef ENABLE_DOT1
+       { atom_vlans_list, sizeof(struct _lldpctl_atom_any_list_t),
+         .init = _lldpctl_atom_new_any_list,
+         .free = _lldpctl_atom_free_any_list,
+         .iter = _lldpctl_atom_iter_vlans_list,
+         .next = _lldpctl_atom_next_vlans_list,
+         .value = _lldpctl_atom_value_vlans_list },
+       { atom_vlan, sizeof(struct _lldpctl_atom_vlan_t),
+         .init = _lldpctl_atom_new_vlan,
+         .free = _lldpctl_atom_free_vlan,
+         .get_str = _lldpctl_atom_get_str_vlan,
+         .get_int = _lldpctl_atom_get_int_vlan },
+       { atom_ppvids_list, sizeof(struct _lldpctl_atom_any_list_t),
+         .init = _lldpctl_atom_new_any_list,
+         .free = _lldpctl_atom_free_any_list,
+         .iter = _lldpctl_atom_iter_ppvids_list,
+         .next = _lldpctl_atom_next_ppvids_list,
+         .value = _lldpctl_atom_value_ppvids_list },
+       { atom_ppvid, sizeof(struct _lldpctl_atom_ppvid_t),
+         .init = _lldpctl_atom_new_ppvid,
+         .free = _lldpctl_atom_free_ppvid,
+         .get_int = _lldpctl_atom_get_int_ppvid },
+       { atom_pis_list, sizeof(struct _lldpctl_atom_any_list_t),
+         .init = _lldpctl_atom_new_any_list,
+         .free = _lldpctl_atom_free_any_list,
+         .iter = _lldpctl_atom_iter_pis_list,
+         .next = _lldpctl_atom_next_pis_list,
+         .value = _lldpctl_atom_value_pis_list },
+       { atom_pi, sizeof(struct _lldpctl_atom_pi_t),
+         .init = _lldpctl_atom_new_pi,
+         .free = _lldpctl_atom_free_pi,
+         .get_buffer = _lldpctl_atom_get_buf_pi },
+#endif
+#ifdef ENABLE_LLDPMED
+       { atom_med_policies_list, sizeof(struct _lldpctl_atom_any_list_t),
+         .init = _lldpctl_atom_new_any_list,
+         .free = _lldpctl_atom_free_any_list,
+         .iter = _lldpctl_atom_iter_med_policies_list,
+         .next = _lldpctl_atom_next_med_policies_list,
+         .value = _lldpctl_atom_value_med_policies_list },
+       { atom_med_policy, sizeof(struct _lldpctl_atom_med_policy_t),
+         .init = _lldpctl_atom_new_med_policy,
+         .free = _lldpctl_atom_free_med_policy,
+         .get_int = _lldpctl_atom_get_int_med_policy,
+         .set_int = _lldpctl_atom_set_int_med_policy,
+         .get_str = _lldpctl_atom_get_str_med_policy },
+       { atom_med_locations_list, sizeof(struct _lldpctl_atom_any_list_t),
+         .init = _lldpctl_atom_new_any_list,
+         .free = _lldpctl_atom_free_any_list,
+         .iter = _lldpctl_atom_iter_med_locations_list,
+         .next = _lldpctl_atom_next_med_locations_list,
+         .value = _lldpctl_atom_value_med_locations_list },
+       { atom_med_location, sizeof(struct _lldpctl_atom_med_location_t),
+         .init = _lldpctl_atom_new_med_location,
+         .free = _lldpctl_atom_free_med_location,
+         .get     = _lldpctl_atom_get_atom_med_location,
+         .set     = _lldpctl_atom_set_atom_med_location,
+         .get_int = _lldpctl_atom_get_int_med_location,
+         .set_int = _lldpctl_atom_set_int_med_location,
+         .get_str = _lldpctl_atom_get_str_med_location,
+         .set_str = _lldpctl_atom_set_str_med_location },
+       { atom_med_caelements_list, sizeof(struct _lldpctl_atom_med_caelements_list_t),
+         .init = _lldpctl_atom_new_any_list,
+         .free = _lldpctl_atom_free_any_list,
+         .iter = _lldpctl_atom_iter_med_caelements_list,
+         .next = _lldpctl_atom_next_med_caelements_list,
+         .value = _lldpctl_atom_value_med_caelements_list,
+         .create = _lldpctl_atom_create_med_caelements_list },
+       { atom_med_caelement, sizeof(struct _lldpctl_atom_med_caelement_t),
+         .init = _lldpctl_atom_new_med_caelement,
+         .free = _lldpctl_atom_free_med_caelement,
+         .get_int = _lldpctl_atom_get_int_med_caelement,
+         .set_int = _lldpctl_atom_set_int_med_caelement,
+         .get_str = _lldpctl_atom_get_str_med_caelement,
+         .set_str = _lldpctl_atom_set_str_med_caelement },
+       { atom_med_power, sizeof(struct _lldpctl_atom_med_power_t),
+         .init = _lldpctl_atom_new_med_power,
+         .free = _lldpctl_atom_free_med_power,
+         .get_int = _lldpctl_atom_get_int_med_power,
+         .set_int = _lldpctl_atom_set_int_med_power,
+         .get_str = _lldpctl_atom_get_str_med_power,
+         .set_str = _lldpctl_atom_set_str_med_power },
+#endif
+       { 0 }
+};
+
+lldpctl_atom_t*
+_lldpctl_new_atom(lldpctl_conn_t *conn, atom_t type, ...)
+{
+       struct atom_builder *builder;
+       struct lldpctl_atom_t *atom;
+       va_list(ap);
+       for (builder = builders; builder->size > 0; builder++) {
+               if (builder->type != type) continue;
+               atom = calloc(1, builder->size);
+               if (atom == NULL) {
+                       SET_ERROR(conn, LLDPCTL_ERR_NOMEM);
+                       return NULL;
+               }
+               atom->count = 1;
+               atom->type  = type;
+               atom->conn  = conn;
+               TAILQ_INIT(&atom->buffers);
+               atom->free  = builder->free;
+
+               atom->iter  = builder->iter;
+               atom->next  = builder->next;
+               atom->value = builder->value;
+
+               atom->get       = builder->get;
+               atom->get_str   = builder->get_str;
+               atom->get_buffer= builder->get_buffer;
+               atom->get_int   = builder->get_int;
+
+               atom->set       = builder->set;
+               atom->set_str   = builder->set_str;
+               atom->set_buffer= builder->set_buffer;
+               atom->set_int   = builder->set_int;
+               atom->create    = builder->create;
+
+               va_start(ap, type);
+               if (builder->init && builder->init(atom, ap) == 0) {
+                       free(atom);
+                       va_end(ap);
+                       /* Error to be set in init() */
+                       return NULL;
+               }
+               va_end(ap);
+               return atom;
+       }
+       LLOG_WARNX("unknown atom type: %d", type);
+       SET_ERROR(conn, LLDPCTL_ERR_FATAL);
+       return NULL;
+}
+
+/**
+ * Allocate a buffer inside an atom.
+ *
+ * It will be freed automatically when the atom is released. This buffer cannot
+ * be reallocated and should not be freed!
+ *
+ * @param atom Atom which will be used as a container.
+ * @param size Size of the allocated area.
+ * @return Pointer to the buffer or @c NULL if allocation fails.
+ */
+void*
+_lldpctl_alloc_in_atom(lldpctl_atom_t *atom, size_t size)
+{
+       struct atom_buffer *buffer;
+
+       if ((buffer = calloc(1, size + sizeof(struct atom_buffer))) == NULL) {
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOMEM);
+               return NULL;
+       }
+       TAILQ_INSERT_TAIL(&atom->buffers, buffer, next);
+       return &buffer->data[0];
+}
+
+/**
+ * Allocate a buffer inside an atom and dump another buffer in it.
+ *
+ * The dump is done in hexadecimal with the provided separator.
+ *
+ * @param atom   Atom which will be used as a container.
+ * @param input  Buffer we want to dump.
+ * @param size   Size of the buffer
+ * @param sep    Separator to use.
+ * @param max    Maximum number of bytes to dump. Can be 0 if no maximum.
+ * @return A string representing the dump of the buffer or @c NULL if error.
+ */
+const char*
+_lldpctl_dump_in_atom(lldpctl_atom_t *atom,
+    const uint8_t *input, size_t size,
+    char sep, size_t max)
+{
+       static const char truncation[] = "[...]";
+       size_t i, len;
+       char *buffer = NULL;
+
+       if (max > 0 && size > max)
+               len = max * 3 + sizeof(truncation) + 1;
+       else
+               len = size * 3 + 1;
+
+       if ((buffer = _lldpctl_alloc_in_atom(atom, len)) == NULL)
+               return NULL;
+
+       for (i = 0; (i < size) && (max == 0 || i < max); i++)
+               sprintf(buffer + i * 3, "%02x%c", *(u_int8_t*)(input + i), sep);
+       if (max > 0 && size > max)
+               sprintf(buffer + i * 3, "%s", truncation);
+       else
+               *(buffer + i*3 - 1) = 0;
+       return buffer;
+}
diff --git a/src/lib/atom.c b/src/lib/atom.c
new file mode 100644 (file)
index 0000000..6e008e2
--- /dev/null
@@ -0,0 +1,386 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "lldpctl.h"
+#include "private.h"
+#include "../marshal.h"
+#include "../ctl.h"
+#include "../lldpd-structs.h"
+
+lldpctl_conn_t*
+lldpctl_atom_get_connection(lldpctl_atom_t *atom)
+{
+       if (atom)
+               return atom->conn;
+       return NULL;
+}
+
+void
+lldpctl_atom_inc_ref(lldpctl_atom_t *atom)
+{
+       atom->count++;
+}
+
+void
+lldpctl_atom_dec_ref(lldpctl_atom_t *atom)
+{
+       struct atom_buffer *buffer, *buffer_next;
+       if (atom && (--atom->count == 0)) {
+               if (atom->free)
+                       atom->free(atom);
+
+               /* Remove special allocated buffers */
+               for (buffer = TAILQ_FIRST(&atom->buffers);
+                    buffer;
+                    buffer = buffer_next) {
+                       buffer_next = TAILQ_NEXT(buffer, next);
+                       free(buffer);
+               }
+
+               free(atom);
+       }
+}
+
+lldpctl_atom_t*
+lldpctl_atom_get(lldpctl_atom_t *atom, lldpctl_key_t key)
+{
+       if (atom == NULL) return NULL;
+       RESET_ERROR(atom->conn);
+
+       if (atom->get == NULL) {
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+       return atom->get(atom, key);
+}
+
+lldpctl_atom_t*
+lldpctl_atom_set(lldpctl_atom_t *atom, lldpctl_key_t key,
+    lldpctl_atom_t *value)
+{
+       if (atom == NULL) return NULL;
+       RESET_ERROR(atom->conn);
+
+       if (atom->set == NULL) {
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+       return atom->set(atom, key, value);
+}
+
+const char*
+lldpctl_atom_get_str(lldpctl_atom_t *atom, lldpctl_key_t key)
+{
+       char *strresult = NULL;
+       const uint8_t *bufresult = NULL;
+       long int intresult = -1;
+       int n1;
+       size_t n2;
+
+       if (atom == NULL) return NULL;
+       RESET_ERROR(atom->conn);
+
+       if (atom->get_str != NULL) {
+               strresult = (char *)atom->get_str(atom, key);
+               if (strresult) return strresult;
+               if (lldpctl_last_error(atom->conn) != LLDPCTL_ERR_NOT_EXIST)
+                       return NULL;
+       }
+
+       RESET_ERROR(atom->conn);
+       if (atom->get_int != NULL) {
+               intresult = atom->get_int(atom, key);
+               if (lldpctl_last_error(atom->conn) != LLDPCTL_ERR_NOT_EXIST) {
+                       strresult = _lldpctl_alloc_in_atom(atom, 21);
+                       if (!strresult) return NULL;
+                       n1 = snprintf(strresult, 21, "%ld", intresult);
+                       if (n1 > -1 && n1 < 21)
+                               return strresult;
+                       SET_ERROR(atom->conn, LLDPCTL_ERR_NOMEM); /* Not really true... */
+                       return NULL;
+               }
+       }
+
+       RESET_ERROR(atom->conn);
+       if (atom->get_buffer != NULL) {
+               bufresult = atom->get_buffer(atom, key, &n2);
+               if (bufresult)
+                       return _lldpctl_dump_in_atom(atom, bufresult, n2, ' ', 0);
+               if (lldpctl_last_error(atom->conn) != LLDPCTL_ERR_NOT_EXIST)
+                       return NULL;
+       }
+
+       SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+       return NULL;
+}
+
+lldpctl_atom_t*
+lldpctl_atom_set_str(lldpctl_atom_t *atom, lldpctl_key_t key,
+       const char *value)
+{
+       lldpctl_atom_t *result = NULL;
+       char *end;
+       long int converted;
+       int isint;
+       int bad = 0;
+
+       if (atom == NULL) return NULL;
+       RESET_ERROR(atom->conn);
+
+       if (atom->set_str != NULL) {
+               result = atom->set_str(atom, key, value);
+               if (result) return result;
+               if (lldpctl_last_error(atom->conn) != LLDPCTL_ERR_NOT_EXIST &&
+                   lldpctl_last_error(atom->conn) != LLDPCTL_ERR_BAD_VALUE)
+                       return NULL;
+               bad = bad || (lldpctl_last_error(atom->conn) == LLDPCTL_ERR_BAD_VALUE);
+       }
+
+       converted = strtol(value, &end, 0);
+       isint = (end != value && *end == '\0');
+
+       RESET_ERROR(atom->conn);
+       if (atom->set_int != NULL && isint) {
+               result = atom->set_int(atom, key, converted);
+               if (result) return result;
+               if (lldpctl_last_error(atom->conn) != LLDPCTL_ERR_NOT_EXIST &&
+                   lldpctl_last_error(atom->conn) != LLDPCTL_ERR_BAD_VALUE)
+                       return NULL;
+               bad = bad || (lldpctl_last_error(atom->conn) == LLDPCTL_ERR_BAD_VALUE);
+       }
+
+       RESET_ERROR(atom->conn);
+       if (atom->set_buffer != NULL) {
+               result = atom->set_buffer(atom, key, (u_int8_t*)value, strlen(value));
+               if (result) return result;
+               if (lldpctl_last_error(atom->conn) != LLDPCTL_ERR_NOT_EXIST &&
+                   lldpctl_last_error(atom->conn) != LLDPCTL_ERR_BAD_VALUE)
+                       return NULL;
+               bad = bad || (lldpctl_last_error(atom->conn) == LLDPCTL_ERR_BAD_VALUE);
+       }
+
+       SET_ERROR(atom->conn, bad?LLDPCTL_ERR_BAD_VALUE:LLDPCTL_ERR_NOT_EXIST);
+       return NULL;
+}
+
+const u_int8_t*
+lldpctl_atom_get_buffer(lldpctl_atom_t *atom, lldpctl_key_t key,
+    size_t *length)
+{
+       if (atom == NULL) return NULL;
+       RESET_ERROR(atom->conn);
+
+       if (atom->get_buffer == NULL) {
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+       return atom->get_buffer(atom, key, length);
+}
+
+lldpctl_atom_t*
+lldpctl_atom_set_buffer(lldpctl_atom_t *atom, lldpctl_key_t key,
+    const u_int8_t* value, size_t length)
+{
+       if (atom == NULL) return NULL;
+       RESET_ERROR(atom->conn);
+
+       if (atom->set_buffer == NULL) {
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+       return atom->set_buffer(atom, key, value, length);
+}
+
+long int
+lldpctl_atom_get_int(lldpctl_atom_t *atom, lldpctl_key_t key)
+{
+       if (atom == NULL) return LLDPCTL_ERR_NOT_EXIST;
+       RESET_ERROR(atom->conn);
+
+       if (atom->get_int == NULL)
+               return SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+       return atom->get_int(atom, key);
+}
+
+lldpctl_atom_t*
+lldpctl_atom_set_int(lldpctl_atom_t *atom, lldpctl_key_t key,
+    long int value)
+{
+       if (atom == NULL) return NULL;
+       RESET_ERROR(atom->conn);
+
+       if (atom->set_int == NULL) {
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+       return atom->set_int(atom, key, value);
+}
+
+lldpctl_atom_iter_t*
+lldpctl_atom_iter(lldpctl_atom_t *atom)
+{
+       if (atom == NULL) return NULL;
+       RESET_ERROR(atom->conn);
+
+       if (!atom->iter) {
+               SET_ERROR(atom->conn, LLDPCTL_ERR_CANNOT_ITERATE);
+               return NULL;
+       }
+       return atom->iter(atom);
+}
+
+lldpctl_atom_iter_t*
+lldpctl_atom_iter_next(lldpctl_atom_t *atom, lldpctl_atom_iter_t *iter)
+{
+       if (atom == NULL) return NULL;
+       RESET_ERROR(atom->conn);
+
+       if (!atom->next) {
+               SET_ERROR(atom->conn, LLDPCTL_ERR_CANNOT_ITERATE);
+               return NULL;
+       }
+       return atom->next(atom, iter);
+}
+
+lldpctl_atom_t*
+lldpctl_atom_iter_value(lldpctl_atom_t *atom,  lldpctl_atom_iter_t *iter)
+{
+       if (atom == NULL) return NULL;
+       RESET_ERROR(atom->conn);
+
+       if (!atom->value) {
+               SET_ERROR(atom->conn, LLDPCTL_ERR_CANNOT_ITERATE);
+               return NULL;
+       }
+       return atom->value(atom, iter);
+}
+
+lldpctl_atom_t*
+lldpctl_atom_create(lldpctl_atom_t *atom)
+{
+       if (atom == NULL) return NULL;
+       RESET_ERROR(atom->conn);
+
+       if (!atom->create) {
+               SET_ERROR(atom->conn, LLDPCTL_ERR_CANNOT_CREATE);
+               return NULL;
+       }
+       return atom->create(atom);
+}
+
+/**
+ * Get somethin with IO.
+ *
+ * @param conn       The connection to lldpd.
+ * @param state_send State to be when "sending"
+ * @param state_recv State to be when "receiving"
+ * @param type       Type of message to send (and receive)
+ * @param to_send    Data to send.
+ * @param mi_send    Marshalling info for data to send.
+ * @param to_recv    Data to receive.
+ * @param mi_recv    Marshalling info for data to recive.
+ * @return 0 in case of success, a negative integer in case of failure.
+ */
+int
+_lldpctl_do_something(lldpctl_conn_t *conn,
+    int state_send, int state_recv, void *state_data,
+    enum hmsg_type type,
+    void *to_send, struct marshal_info *mi_send,
+    void **to_recv, struct marshal_info *mi_recv)
+{
+       ssize_t rc;
+
+       if (conn->state == CONN_STATE_IDLE) {
+               /* We need to build the message to send, then send
+                * it. */
+               if (ctl_msg_send_unserialized(&conn->output_buffer, &conn->output_buffer_len,
+                       type, to_send, mi_send) != 0)
+                       return SET_ERROR(conn, LLDPCTL_ERR_SERIALIZATION);
+               conn->state = state_send;
+               conn->state_data = state_data;
+       }
+       if (conn->state == state_send && conn->state_data == state_data) {
+               /* We need to send the currently built message */
+               rc = lldpctl_send(conn);
+               if (rc < 0)
+                       return SET_ERROR(conn, rc);
+               conn->state = state_recv;
+       }
+       if (conn->state == state_recv && conn->state_data == state_data) {
+               /* We need to receive the answer */
+               while ((rc = ctl_msg_recv_unserialized(&conn->input_buffer, &conn->input_buffer_len,
+                           type,
+                           to_recv, mi_recv)) > 0) {
+                       /* We need more bytes */
+                       rc = _lldpctl_needs(conn, rc);
+                       if (rc < 0)
+                               return SET_ERROR(conn, rc);
+               }
+               if (rc < 0)
+                       return SET_ERROR(conn, LLDPCTL_ERR_SERIALIZATION);
+               /* rc == 0 */
+               conn->state = CONN_STATE_IDLE;
+               conn->state_data = NULL;
+               return 0;
+       } else
+               return SET_ERROR(conn, LLDPCTL_ERR_INVALID_STATE);
+}
+
+lldpctl_atom_t*
+lldpctl_get_interfaces(lldpctl_conn_t *conn)
+{
+       struct lldpd_interface_list *ifs;
+       int rc;
+
+       RESET_ERROR(conn);
+
+       rc = _lldpctl_do_something(conn,
+           CONN_STATE_GET_INTERFACES_SEND, CONN_STATE_GET_INTERFACES_RECV, NULL,
+           GET_INTERFACES,
+           NULL, NULL,
+           (void **)&ifs, &MARSHAL_INFO(lldpd_interface_list));
+       if (rc == 0)
+               return _lldpctl_new_atom(conn, atom_interfaces_list, ifs);
+       return NULL;
+}
+
+lldpctl_atom_t*
+lldpctl_get_port(lldpctl_atom_t *atom)
+{
+       int rc;
+       lldpctl_conn_t *conn = atom->conn;
+       struct lldpd_hardware *hardware;
+       struct _lldpctl_atom_interface_t *iface =
+           (struct _lldpctl_atom_interface_t *)atom;
+
+       RESET_ERROR(conn);
+
+       if (atom->type != atom_interface) {
+               SET_ERROR(conn, LLDPCTL_ERR_INCORRECT_ATOM_TYPE);
+               return NULL;
+       }
+       rc = _lldpctl_do_something(conn,
+           CONN_STATE_GET_PORT_SEND, CONN_STATE_GET_PORT_RECV, iface->name,
+           GET_INTERFACE, (void*)iface->name, &MARSHAL_INFO(string),
+           (void**)&hardware, &MARSHAL_INFO(lldpd_hardware));
+       if (rc == 0)
+               return _lldpctl_new_atom(conn, atom_port,
+                   hardware, &hardware->h_lport, NULL);
+       return NULL;
+}
diff --git a/src/lib/connection.c b/src/lib/connection.c
new file mode 100644 (file)
index 0000000..85a11ca
--- /dev/null
@@ -0,0 +1,215 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <unistd.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "lldpctl.h"
+#include "private.h"
+#include "../compat/compat.h"
+#include "../ctl.h"
+
+const char*
+lldpctl_get_default_transport(void)
+{
+       return LLDPD_CTL_SOCKET;
+}
+
+/* Connect to the remote end */
+static int
+sync_connect()
+{
+       return ctl_connect(LLDPD_CTL_SOCKET);
+}
+
+/* Synchronously send data to remote end. */
+static ssize_t
+sync_send(lldpctl_conn_t *lldpctl,
+    const uint8_t *data, size_t length, void *user_data)
+{
+       struct lldpctl_conn_sync_t *conn = user_data;
+       size_t nb;
+
+       if (conn->fd == -1 &&
+           ((conn->fd = sync_connect()) == -1)) {
+               return LLDPCTL_ERR_CANNOT_CONNECT;
+       }
+
+       while ((nb = write(conn->fd, data, length)) == -1) {
+               if (errno == EAGAIN || errno == EINTR) continue;
+               return LLDPCTL_ERR_CALLBACK_FAILURE;
+       }
+       return nb;
+}
+
+/* Statiscally receive data from remote end. */
+static ssize_t
+sync_recv(lldpctl_conn_t *lldpctl,
+    const uint8_t *data, size_t length, void *user_data)
+{
+       struct lldpctl_conn_sync_t *conn = user_data;
+       size_t nb;
+
+       if (conn->fd == -1 &&
+           ((conn->fd = sync_connect()) == -1)) {
+               lldpctl->error = LLDPCTL_ERR_CANNOT_CONNECT;
+               return LLDPCTL_ERR_CANNOT_CONNECT;
+       }
+
+       while ((nb = read(conn->fd, (void*)data, length)) == -1) {
+               if (errno == EAGAIN || errno == EINTR) continue;
+               return LLDPCTL_ERR_CALLBACK_FAILURE;
+       }
+       return nb;
+}
+
+
+
+lldpctl_conn_t*
+lldpctl_new(lldpctl_send_callback send, lldpctl_recv_callback recv, void *user_data)
+{
+       lldpctl_conn_t *conn = NULL;
+       struct lldpctl_conn_sync_t *data = NULL;
+
+       /* Both callbacks are mandatory or should be NULL. */
+       if (send && !recv) return NULL;
+       if (recv && !send) return NULL;
+
+       if ((conn = calloc(1, sizeof(lldpctl_conn_t))) == NULL)
+               return NULL;
+
+       if (!send && !recv) {
+               if ((data = malloc(sizeof(struct lldpctl_conn_sync_t))) == NULL) {
+                       free(conn);
+                       return NULL;
+               }
+               data->fd = -1;
+               conn->send = sync_send;
+               conn->recv = sync_recv;
+               conn->user_data = data;
+       } else {
+               conn->send = send;
+               conn->recv = recv;
+               conn->user_data = user_data;
+       }
+
+       return conn;
+}
+
+int
+lldpctl_release(lldpctl_conn_t *conn)
+{
+       if (conn == NULL) return 0;
+       if (conn->send == sync_send) {
+               struct lldpctl_conn_sync_t *data = conn->user_data;
+               if (data->fd != -1) close(data->fd);
+               free(conn->user_data);
+       }
+       free(conn->input_buffer);
+       free(conn->output_buffer);
+       free(conn);
+       return 0;
+}
+
+/**
+ * Request some bytes if they are not already here.
+ *
+ * @param conn   The connection to lldpd.
+ * @param length The number of requested bytes.
+ * @return A negative integer if we can't have the bytes or the number of bytes we got.
+ */
+ssize_t
+_lldpctl_needs(lldpctl_conn_t *conn, size_t length)
+{
+       uint8_t *buffer = NULL;
+       ssize_t  rc;
+       if (conn->input_buffer_len >= length) return 0;
+
+       if ((buffer = malloc(length)) == NULL)
+               return SET_ERROR(conn, LLDPCTL_ERR_NOMEM);
+       rc = conn->recv(conn, buffer, length, conn->user_data);
+       if (rc < 0) {
+               free(buffer);
+               return SET_ERROR(conn, rc);
+       }
+       if (rc == 0) {
+               free(buffer);
+               return SET_ERROR(conn, LLDPCTL_ERR_EOF);
+       }
+       rc = lldpctl_recv(conn, buffer, rc);
+       free(buffer);
+       if (rc < 0)
+               return SET_ERROR(conn, rc);
+       RESET_ERROR(conn);
+       return rc;
+}
+
+ssize_t
+lldpctl_recv(lldpctl_conn_t *conn, const uint8_t *data, size_t length)
+{
+
+       RESET_ERROR(conn);
+
+       if (length == 0) return 0;
+
+       /* Received data should be appended to the input buffer. */
+       if (conn->input_buffer == NULL) {
+               conn->input_buffer_len = 0;
+               if ((conn->input_buffer = malloc(length)) == NULL)
+                       return SET_ERROR(conn, LLDPCTL_ERR_NOMEM);
+       } else {
+               uint8_t *new = realloc(conn->input_buffer, conn->input_buffer_len + length);
+               if (new == NULL)
+                       return SET_ERROR(conn, LLDPCTL_ERR_NOMEM);
+               conn->input_buffer = new;
+       }
+       memcpy(conn->input_buffer + conn->input_buffer_len, data, length);
+       conn->input_buffer_len += length;
+       RESET_ERROR(conn);
+       return length;
+}
+
+ssize_t
+lldpctl_send(lldpctl_conn_t *conn)
+{
+       /* Send waiting data. */
+       ssize_t rc;
+
+       RESET_ERROR(conn);
+
+       if (!conn->output_buffer) return 0;
+       rc = conn->send(conn,
+           conn->output_buffer, conn->output_buffer_len,
+           conn->user_data);
+       if (rc < 0) return SET_ERROR(conn, rc);
+
+       /* "Shrink" the output buffer. */
+       if (rc == conn->output_buffer_len) {
+               free(conn->output_buffer);
+               conn->output_buffer = NULL;
+               conn->output_buffer_len = 0;
+               RESET_ERROR(conn);
+               return rc;
+       }
+       conn->output_buffer_len -= rc;
+       memmove(conn->output_buffer, conn->output_buffer + rc, conn->output_buffer_len);
+       /* We don't shrink the buffer. It will be either freed or shrinked later */
+       RESET_ERROR(conn);
+       return rc;
+}
diff --git a/src/lib/errors.c b/src/lib/errors.c
new file mode 100644 (file)
index 0000000..140630b
--- /dev/null
@@ -0,0 +1,55 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "lldpctl.h"
+#include "private.h"
+#include "../log.h"
+
+const char*
+lldpctl_strerror(lldpctl_error_t error)
+{
+       /* No default case to let the compiler warns us if we miss an error code. */
+       switch (error) {
+       case LLDPCTL_NO_ERROR: return "No error";
+       case LLDPCTL_ERR_WOULDBLOCK: return "Requested operation would block";
+       case LLDPCTL_ERR_EOF: return "End of file reached";
+       case LLDPCTL_ERR_NOT_EXIST: return "The requested information does not exist";
+       case LLDPCTL_ERR_CANNOT_CONNECT: return "Unable to connect to lldpd daemon";
+       case LLDPCTL_ERR_INCORRECT_ATOM_TYPE: return "Provided atom is of incorrect type";
+       case LLDPCTL_ERR_SERIALIZATION: return "Error while serializing or unserializing data";
+       case LLDPCTL_ERR_INVALID_STATE: return "Other input/output operation already in progress";
+       case LLDPCTL_ERR_CANNOT_ITERATE: return "Cannot iterate on this atom";
+       case LLDPCTL_ERR_CANNOT_CREATE: return "Cannot create a new element for this atom";
+       case LLDPCTL_ERR_BAD_VALUE: return "Provided value is invalid";
+       case LLDPCTL_ERR_FATAL: return "Unexpected fatal error";
+       case LLDPCTL_ERR_NOMEM: return "Not enough memory available";
+       case LLDPCTL_ERR_CALLBACK_FAILURE: return "A failure occured during callback processing";
+       }
+       return "Unknown error code";
+}
+
+lldpctl_error_t
+lldpctl_last_error(lldpctl_conn_t *lldpctl)
+{
+       return lldpctl->error;
+}
+
+void
+lldpctl_log_callback(void (*cb)(int severity, const char *msg))
+{
+       log_register(cb);
+}
diff --git a/src/lib/lldpctl.h b/src/lib/lldpctl.h
new file mode 100644 (file)
index 0000000..823a5fd
--- /dev/null
@@ -0,0 +1,720 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef LLDPCTL_H
+#define LLDPCTL_H
+
+/**
+ * @section liblldpctl Interfacing with lldpd
+ *
+ * Interfacing with a running instance of lldpd can be done through liblldpctl
+ * library.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+/**
+ * Get default transport name.
+ *
+ * Currently, this is the default location of the Unix socket.
+ */
+const char* lldpctl_get_default_transport(void);
+
+/**
+ * Setup log handlers.
+ *
+ * By default, liblldpctl will log to stderr. The following function will
+ * register another callback for this purpose. Messages logged through this
+ * callback may be cryptic. They are targeted for the developer. Message for end
+ * users should rely on return codes.
+ */
+void lldpctl_log_callback(void (*cb)(int severity, const char *msg));
+
+/*@{*/
+/**
+ * Structure referencing a connection with lldpd.
+ *
+ * This structure should be handled as opaque. It can be allocated
+ * with @c lldpctl_new() and the associated resources will be freed
+ * with @c lldpctl_release().
+ */
+typedef struct lldpctl_conn_t lldpctl_conn_t;
+
+/**
+ * Callback function invoked to send data to lldpd.
+ *
+ * @param lldpctl   Handle to the connection to lldpd.
+ * @param data      Bytes to be sent.
+ * @param length    Length of provided data.
+ * @param user_data Provided user data.
+ * @return The number of bytes really sent or either @c LLDPCTL_ERR_WOULDBLOCK
+ *         if no bytes can be sent without blocking or @c
+ *         LLDPCTL_ERR_CALLBACK_FAILURE for other errors.
+ */
+typedef ssize_t (*lldpctl_send_callback)(lldpctl_conn_t *lldpctl,
+    const uint8_t *data, size_t length, void *user_data);
+
+/**
+ * Callback function invoked to receive data from lldpd.
+ *
+ * @param lldpctl   Handle to the connection to lldpd.
+ * @param data      Buffer for receiving data
+ * @param length    Maximum bytes we can receive
+ * @param user_data Provided user data.
+ * @return The number of bytes really received or either @c
+ *         LLDPCTL_ERR_WOULDBLOCK if no bytes can be received without blocking,
+ *         @c LLDPCTL_ERR_CALLBACK_FAILURE for other errors or @c
+ *         LLDPCTL_ERR_EOF if end of file was reached.
+ */
+typedef ssize_t (*lldpctl_recv_callback)(lldpctl_conn_t *lldpctl,
+    const uint8_t *data, size_t length, void *user_data);
+
+/**
+ * Function invoked when additional data is available from lldpd.
+ *
+ * This function should be invoked in case of asynchronous IO when new data is
+ * available from lldpd (expected or unexpected).
+ *
+ * @param  lldpctl   Handle to the connection to lldpd.
+ * @param  data      Data received from lldpd.
+ * @param  length    Length of data received.
+ * @return The number of bytes processed or a negative integer if an error has
+ *         occurred.
+ */
+ssize_t lldpctl_recv(lldpctl_conn_t *lldpctl, const uint8_t *data, size_t length);
+
+/**
+ * Function invoked when there is an opportunity to send data to lldpd.
+ *
+ * This function should be invoked in case of asynchronous IO when new data can
+ * be written to lldpd.
+ *
+ * @param  lldpctl  Handle to the connection to lldpd.
+ * @return The number of bytes processed or a negative integer if an error has
+ *         occured.
+ */
+ssize_t lldpctl_send(lldpctl_conn_t *lldpctl);
+
+/**
+ * Allocate a new handler for connecting to lldpd.
+ *
+ * This library does not handle IO. They are delegated to a set of functions to
+ * allow a user to specify exactly how IO should be done. This open the
+ * possibility to query a remote lldpd through SSH for example. A user is
+ * expected to provide two functions: the first one is called when the library
+ * requests incoming data, the other one when it requests outgoing
+ * data. Moreover, the user is also expected to call the appropriate functions
+ * when data comes back or is effectively sent (in case of asynchronous IO).
+ *
+ * @param  send      Callback to be used when sending   new data is requested.
+ * @param  recv      Callback to be used when receiving new data is requested.
+ * @param  user_data Data to pass to callbacks.
+ * @return An handler to be used to connect to lldpd or @c NULL in
+ *         case of error. In the later case, the error is probable an
+ *         out of memory condition.
+ *
+ * The allocated handler can be released with @c lldpctl_release(). If the
+ * provided parameters are both @c NULL, default synchronous callbacks will be
+ * used.
+ */
+lldpctl_conn_t *lldpctl_new(lldpctl_send_callback send,
+    lldpctl_recv_callback recv, void *user_data);
+
+/**
+ * Release resources associated with a connection to lldpd.
+ *
+ * @param   lldpctl Previously allocated handler to a connection to lldpd.
+ * @return  0 on success or a negative integer
+ *
+ * @see lldpctl_new()
+ */
+int lldpctl_release(lldpctl_conn_t *lldpctl);
+/*@}*/
+
+/*@{*/
+/**
+ * Possible error codes for functions that return negative integers on
+ * this purpose or for @c lldpctl_last_error().
+ */
+typedef enum {
+       /**
+        * No error has happened (yet).
+        */
+       LLDPCTL_NO_ERROR = 0,
+       /**
+        * A IO related operation would block if performed.
+        */
+       LLDPCTL_ERR_WOULDBLOCK = -501,
+       /**
+        * A IO related operation has reached a end of file condition.
+        */
+       LLDPCTL_ERR_EOF = -502,
+       /**
+        * The requested information does not exist. For example, when
+        * requesting an inexistant information from an atom.
+        */
+       LLDPCTL_ERR_NOT_EXIST = -503,
+       /**
+        * Cannot connect to the lldpd daemon. This error only happens with
+        * default synchronous handlers.
+        */
+       LLDPCTL_ERR_CANNOT_CONNECT = -504,
+       /**
+        * Atom is of incorrect type for the requested operation.
+        */
+       LLDPCTL_ERR_INCORRECT_ATOM_TYPE = -505,
+       /**
+        * An error occurred during serialization of message.
+        */
+       LLDPCTL_ERR_SERIALIZATION =  -506,
+       /**
+        * The requested operation cannot be performed because we have another
+        * operation already running.
+        */
+       LLDPCTL_ERR_INVALID_STATE =  -507,
+       /**
+        * The provided atom cannot be iterated.
+        */
+       LLDPCTL_ERR_CANNOT_ITERATE =  -508,
+       /**
+        * The provided value is invalid.
+        */
+       LLDPCTL_ERR_BAD_VALUE =  -509,
+       /**
+        * No new element can be created for this element.
+        */
+       LLDPCTL_ERR_CANNOT_CREATE =  -510,
+       /**
+        * The library is under unexpected conditions and cannot process
+        * any further data reliably.
+        */
+       LLDPCTL_ERR_FATAL = -900,
+       /**
+        * Out of memory condition. Things may get havoc here but we
+        * should be able to recover.
+        */
+       LLDPCTL_ERR_NOMEM = -901,
+       /**
+        * An error occurred in a user provided callback.
+        */
+       LLDPCTL_ERR_CALLBACK_FAILURE = -902
+} lldpctl_error_t;
+
+/**
+ * Describe a provided error code.
+ *
+ * @param error Error code to be described.
+ * @return Statically allocated string describing the error.
+ */
+const char *lldpctl_strerror(lldpctl_error_t error);
+
+/**
+ * Get the last error associated to a connection to lldpd.
+ *
+ * @param  lldpctl Previously allocated handler to a connection to lldpd.
+ * @return 0 if no error is currently registered. A negative integer
+ *         otherwise.
+ *
+ * For functions returning int, this function will return the same
+ * error number. For functions returning something else, you can use
+ * this function to get the appropriate error number.
+ */
+lldpctl_error_t lldpctl_last_error(lldpctl_conn_t *lldpctl);
+
+/**
+ * Describe the last error associate to a connection.
+ *
+ * @param conn Previously allocated handler to a connection to lldpd.
+ * @return Statically allocated string describing the error
+ */
+#define lldpctl_last_strerror(conn) lldpctl_strerror(lldpctl_last_error(conn))
+/*@}*/
+
+/*@{*/
+/**
+ * Structure representing an element (chassis, port, VLAN, ...)
+ *
+ * This is an opaque structure that can be passed along some functions to
+ * transmit chassis, ports, VLAN and other information related to LLDP. Most
+ * information are extracted using @c lldpctl_atom_get(), @c
+ * lldpctl_atom_get_str(), @c lldpctl_atom_get_buffer() or @c
+ * lldpctl_atom_get_int(), unless some IO with lldpd is needed to retrieve the
+ * requested information. In this case, there exists an appropriate function to
+ * convert the "deferred" atom into a normal one (like @c lldpctl_get_port()).
+ *
+ * An atom is reference counted. Unless documented otherwise, a function
+ * returning an atom will return a new reference that should be decremented if
+ * not used anymore.
+ *
+ * @see lldpctl_atom_inc_ref(), lldpctl_atom_dec_ref().
+ */
+typedef struct lldpctl_atom_t lldpctl_atom_t;
+
+/**
+ * Return the reference to connection with lldpd.
+ *
+ * @param atom The atom we want reference from.
+ * @return The reference to the connection to lldpd.
+ *
+ * Each atom contains an internal reference to the corresponding connection to
+ * lldpd. Use this function to get it.
+ */
+lldpctl_conn_t *lldpctl_atom_get_connection(lldpctl_atom_t *atom);
+
+/**
+ * Increment reference count for an atom.
+ *
+ * @param atom Atom we which to increase reference count.
+ */
+void lldpctl_atom_inc_ref(lldpctl_atom_t *atom);
+
+/**
+ * Decrement reference count for an atom.
+ *
+ * @param atom Atom we want to decrease reference count. Can be @c NULL. In this
+ *             case, nothing happens.
+ *
+ * When the reference count becomes 0, the atom is freed. 
+ */
+void lldpctl_atom_dec_ref(lldpctl_atom_t *atom);
+
+/**
+ * Retrieve the list of available interfaces.
+ *
+ * @param lldpctl Previously allocated handler to a connection to lldpd.
+ * @return The list of available ports or @c NULL if an error happened.
+ *
+ * This function will make IO with the daemon to get the list of
+ * ports. Depending on the IO model, information may not be available right now
+ * and the function should be called again later. If @c NULL is returned, check
+ * what the last error is. If it is @c LLDPCTL_ERR_WOULDBLOCK, try again later
+ * (when more data is available).
+ */
+lldpctl_atom_t *lldpctl_get_interfaces(lldpctl_conn_t *lldpctl);
+
+/**
+ * Retrieve the information related to a given interface.
+ *
+ * @param port The port we want to retrieve information from. This port is an
+ *             atom retrieved from @c lldpctl_get_interfaces().
+ * @return Atom related to this port which may be used in subsequent functions.
+ *
+ * This functions may have to do IO to get the information related to the given
+ * port. Depending on the IO mode, information may not be available tight now
+ * and the function should be called again later. If @c NULL is returned, check
+ * what the last error is. If it is @c LLDPCTL_ERR_WOULDBLOCK, try again later
+ * (when more data is available).
+ */
+lldpctl_atom_t *lldpctl_get_port(lldpctl_atom_t *port);
+
+/**
+ * Piece of information that can be retrieved from/written to an atom.
+ *
+ * Each piece of information can potentially be retrieved as an atom (A), a
+ * string (S), a buffer (B) or an integer (I). Additionaly, when an information
+ * can be retrieved as an atom, it is usually iterable (L). When an atom can be
+ * retrieved as a string and as an additional type, the string is expected to be
+ * formatted. For example, the MAC address of a local port can be retrieved as a
+ * buffer and a string. As a string, you'll get something like
+ * "00:11:22:33:44:55". Also, all values that can be get as an integer or a
+ * buffer can be get as a string too. There is no special formatting in this
+ * case. "(BS)" means that the string get a special appropriate format.
+ *
+ * The name of a key is an indication on the type of atom that information can
+ * be extracted from. For example, @c lldpctl_k_med_policy_type can be extracted
+ * from an atom you got by iterating on @c lldpctl_k_port_med_policies. On the
+ * other hand, @c lldpctl_k_port_descr and @c lldpctl_k_chassis can be retrieved
+ * from an atom retrieved either by iterating @c lldpctl_k_port_neighbors or
+ * with @c lldpctl_get_port().
+ *
+ * Some values may be written. They are marked with (W). Such a change may or
+ * may not be transmitted immediatly. If they are not transmitted immediatly,
+ * this means that the resulting atom should be written to another atom. For
+ * example, when writting @c lldpctl_k_med_policy_tagged, you need to write the
+ * resulting atom to @c lldpctl_k_port_med_policies. If the change is
+ * transmitted immediatly, you need to check the error status of the connection
+ * to know if it has been transmitted correctly. Notably, if you get @c
+ * LLDPCTL_ERR_WOULDBLOCK, you need to try again later. Usually, changes are
+ * transmitted immediatly. The exception are changes that need to be grouped to
+ * be consistent, like a LLDP MED location. When a change is transmitted
+ * immediatly, it is marked with (O).
+ *
+ * Some values may also be created. They are flagged with (C). This only applies
+ * to elements that can be iterated (L) and written (W). The element created
+ * still needs to be appended to the list by being written to it. The creation
+ * is done with @c lldpctl_atom_create().
+ *
+ * An atom marked with (S) can be retrieved as a string only. It cannot be
+ * written. An atom marked with (IS) can be retrieved as an integer and features
+ * an appropriate representation as a string (usually, the name of a constant)
+ * which is more meaningful than just the integer. An atom marked as (I) can be
+ * retrieved and as a string. In the later case, this is just a string
+ * representation of the integer. An atom marked with (AL) can be retrieved as
+ * an atom only and can be iterated over. This is usually a list of things. An
+ * atom marked (I,W) can be read as an integer or a string and can be written as
+ * an integer. The change would not be commited until the atom is written to the
+ * nearest atom supporting (A,WO) operation (eventually with an indirection, i.e
+ * first write to a (A,W), then to a (A,WO)).
+ */
+typedef enum {
+       lldpctl_k_interface_name, /**< (S) The interface name. */
+
+       lldpctl_k_port_name,    /**< (S) The port name. Only works for a local port. */
+       lldpctl_k_port_index,   /**< (I) The port index. Only works for a local port. */
+       /**
+        * (AL) The list of known neighbors for this port.
+        *
+        * A neighbor is in fact a remote port.
+        */
+       lldpctl_k_port_neighbors,
+       lldpctl_k_port_protocol,   /**< (IS) The protocol that was used to retrieve this information. */
+       lldpctl_k_port_age,        /**< (I)  Age of information, seconds from epoch. */
+       lldpctl_k_port_id_subtype, /**< (IS) The subtype ID of this port.  */
+       lldpctl_k_port_id,         /**< (BS) The ID of this port. */
+       lldpctl_k_port_descr,      /**< (S) The description of this port. */
+       lldpctl_k_port_hidden,     /**< (I) Is this port hidden (or should it be displayed?)? */
+
+       lldpctl_k_port_dot3_mfs,           /**< (I) MFS */
+       lldpctl_k_port_dot3_aggregid,   /**< (I) Port aggregation ID */
+       lldpctl_k_port_dot3_autoneg_support, /**< (I) Autonegotiation support. */
+       lldpctl_k_port_dot3_autoneg_enabled, /**< (I) Autonegotiation enabled. */
+       lldpctl_k_port_dot3_autoneg_advertised, /**< (I) Advertised protocols. See LLDP_DOT3_LINK_AUTONEG_* */
+       lldpctl_k_port_dot3_mautype, /**< (IS) Current MAU type. See LLDP_DOT3_MAU_* */
+
+       lldpctl_k_port_dot3_power, /**< (A,WO) Dot3 power related stuff. */
+       lldpctl_k_dot3_power_devicetype, /**< (IS,W) Device type. See LLDP_DOT3_POWER_PSE/PD. */
+       lldpctl_k_dot3_power_supported, /**< (I,W) Is MDI power supported. */
+       lldpctl_k_dot3_power_enabled, /**< (I,W) Is MDI power enabled. */
+       lldpctl_k_dot3_power_paircontrol, /**< (I,W) Pair-control enabled? */
+       lldpctl_k_dot3_power_pairs, /**< (IS,W) See LLDP_DOT3_POWERPAIRS_ */
+       lldpctl_k_dot3_power_class, /**< (IS,W) Power class. */
+       lldpctl_k_dot3_power_type, /**< (I,W) 802.3AT power type */
+       lldpctl_k_dot3_power_source, /**< (IS,W) 802.3AT power source */
+       lldpctl_k_dot3_power_priority, /**< (IS,W) 802.3AT power priority */
+       lldpctl_k_dot3_power_allocated, /**< (I,W) 802.3AT power allocated */
+       lldpctl_k_dot3_power_requested, /**< (I,W) 802.3AT power requested */
+
+       lldpctl_k_port_vlan_pvid, /**< (I) Primary VLAN ID */
+       lldpctl_k_port_vlans, /**< (AL) List of VLAN */
+       lldpctl_k_vlan_id, /**< (I) VLAN ID */
+       lldpctl_k_vlan_name, /**< (S) VLAN name */
+
+       lldpctl_k_port_ppvids, /**< (AL) List of PPVIDs */
+       lldpctl_k_ppvid_status, /**< (I) Status of PPVID (see LLDP_PPVID_CAP_*) */
+       lldpctl_k_ppvid_id, /**< (I) ID of PPVID */
+
+       lldpctl_k_port_pis, /**< (AL) List of PIDs */
+       lldpctl_k_pi_id,    /**< (B) PID value */
+
+       lldpctl_k_chassis_index,   /**< (I) The chassis index. */
+       lldpctl_k_chassis_id_subtype, /**< (IS) The subtype ID of this chassis. */
+       lldpctl_k_chassis_id,         /**< (BS) The ID of this chassis. */
+       lldpctl_k_chassis_name,       /**< (S) The name of this chassis. */
+       lldpctl_k_chassis_descr,      /**< (S) The description of this chassis. */
+       lldpctl_k_chassis_cap_available, /**< (I) Available capabalities (see LLDP_CAP_*) */
+       lldpctl_k_chassis_cap_enabled,   /**< (I) Enabled capabilities (see LLDP_CAP_*) */
+       lldpctl_k_chassis_mgmt,          /**< (AL) List of management addresses */
+
+       lldpctl_k_chassis_med_type, /**< (IS) Chassis MED type. See LLDP_MED_CLASS_* */
+       lldpctl_k_chassis_med_cap,  /**< (I) Available MED capabilitied. See LLDP_MED_CAP_* */
+       lldpctl_k_chassis_med_inventory_hw, /**< (S) LLDP MED inventory "Hardware Revision" */
+       lldpctl_k_chassis_med_inventory_sw, /**< (S) LLDP MED inventory "Software Revision" */
+       lldpctl_k_chassis_med_inventory_fw, /**< (S) LLDP MED inventory "Firmware Revision" */
+       lldpctl_k_chassis_med_inventory_sn, /**< (S) LLDP MED inventory "Serial Number" */
+       lldpctl_k_chassis_med_inventory_manuf, /**< (S) LLDP MED inventory "Manufacturer" */
+       lldpctl_k_chassis_med_inventory_model, /**< (S) LLDP MED inventory "Model" */
+       lldpctl_k_chassis_med_inventory_asset, /**< (S) LLDP MED inventory "Asset ID" */
+
+       lldpctl_k_port_med_policies, /**< (AL,WO) MED policies attached to a port. */
+       lldpctl_k_med_policy_type, /**< (IS,W) MED policy app type. See LLDP_MED_APPTYPE_*. 0 if a policy is not defined. */
+       lldpctl_k_med_policy_unknown, /**< (I,W) Is MED policy defined? */
+       lldpctl_k_med_policy_tagged, /**< (I,W) MED policy tagging */
+       lldpctl_k_med_policy_vid,    /**< (I,W) MED policy VID */
+       lldpctl_k_med_policy_priority, /**< (I,W) MED policy priority */
+       lldpctl_k_med_policy_dscp,     /**< (I,W) MED policy DSCP */
+
+       lldpctl_k_port_med_locations, /**< (AL,WO) MED locations attached to a port. */
+       lldpctl_k_med_location_format, /**< (IS,W) MED location format. See
+                                       * LLDP_MED_LOCFORMAT_*. 0 if this
+                                       * location is not defined. When written,
+                                       * the following fields will be zeroed
+                                       * out. */
+       lldpctl_k_med_location_geoid, /**< (IS,W) MED geoid. See LLDP_MED_LOCATION_GEOID_*. Only if format is COORD. */
+       lldpctl_k_med_location_latitude,  /**< (S,W) MED latitude. Only if format is COORD. */
+       lldpctl_k_med_location_longitude, /**< (S,W) MED longitude. Only if format is COORD. */
+       lldpctl_k_med_location_altitude,  /**< (S,W) MED altitude. Only if format is COORD. */
+       lldpctl_k_med_location_altitude_unit, /**< (S,W) MED altitude unit. See LLDP_MED_LOCATION_ALTITUDE_UNIT_*.
+                                              * Only if format is COORD. */
+
+       lldpctl_k_med_location_country, /**< (S,W) MED country. Only if format is CIVIC. */
+       lldpctl_k_med_location_elin, /**< (S,W) MED ELIN. Only if format is ELIN. */
+
+       lldpctl_k_med_location_ca_elements, /**< (AL,WC) MED civic address elements. Only if format is CIVIC */
+       lldpctl_k_med_civicaddress_type, /**< (IS,W) MED civic address type. */
+       lldpctl_k_med_civicaddress_value, /**< (S,W) MED civic address value. */
+
+       lldpctl_k_port_med_power, /**< (A,WO) LLDP-MED power related stuff. */
+       lldpctl_k_med_power_type, /**< (IS,W) LLDP MED power device type. See LLDP_MED_POW_TYPE_* */
+       lldpctl_k_med_power_source, /**< (IS,W) LLDP MED power source. See LLDP_MED_POW_SOURCE_* */
+       lldpctl_k_med_power_priority, /**< (IS,W) LLDP MED power priority. See LLDP_MED_POW_PRIO_* */
+       lldpctl_k_med_power_val, /**< (I,W) LLDP MED power value */
+
+       lldpctl_k_mgmt_ip,      /**< (S) IP address */
+} lldpctl_key_t;
+
+/**
+ * Retrieve a bit of information as an atom.
+ *
+ * @param atom The atom we want to query.
+ * @param key  The information we want from the atom.
+ * @return The atom representing the requested information or @c NULL if the
+ *         information is not available.
+ *
+ * Not every value of @c info will be available as an atom. See the
+ * documentation of @c lldpctl_key_t for values accepting to be extracted as an
+ * atom. Usually, this is only iterable values or values representing a complex
+ * object.
+ *
+ * The provided atom is not a _borrowed_ reference. You need to decrement the
+ * reference count when you don't need it anymore.
+ *
+ * As a convenience, this function will return @c NULL if the first parameter is
+ * @c NULL and no error will be raised.
+ */
+lldpctl_atom_t *lldpctl_atom_get(lldpctl_atom_t *atom, lldpctl_key_t key);
+
+/**
+ * Set a bit of information with an atom.
+ *
+ * @param atom  The atom we want to write to.
+ * @param key   The key information we want to write.
+ * @param value The value of the information we want to write.
+ * @return The updated atom with the appropriate information.
+ *
+ * This function will return @c NULL in case of error. If the last error is @c
+ * LLDPCTL_ERR_WOULDBLOCK, the write should be retried later with the exact same
+ * parameters. LLDPCTL_ERR_BAD_VALUE is raised when the provided atom is not
+ * correct.
+ */
+lldpctl_atom_t *lldpctl_atom_set(lldpctl_atom_t *atom, lldpctl_key_t key,
+    lldpctl_atom_t *value);
+
+/**
+ * Retrieve a bit of information as a null-terminated string.
+ *
+ * @param atom The atom we want to query.
+ * @param key  The information we want from the atom.
+ * @return The requested string or @c NULL if the information is not available.
+ *
+ * Not every value of @c info will be available as a string. See the
+ * documentation of @c lldpctl_key_t for values accepting to be extracted as a
+ * string. Usually, only piece of information stored as string are available in
+ * this form but sometimes, you can get a nice formatted string instead of an
+ * integer with this function.
+ *
+ * As a convenience, this function will return @c NULL if the first parameter is
+ * @c NULL and no error will be raised.
+ *
+ * The provided string may live inside the atom providing it. If you need it
+ * longer, duplicate it.
+ */
+const char *lldpctl_atom_get_str(lldpctl_atom_t *atom, lldpctl_key_t key);
+
+/**
+ * Set a bit of information using a null-terminated string.
+ *
+ * @param atom  The atom we want to write to.
+ * @param key   The key information we want to write.
+ * @param value The value of the information we want to write.
+ * @return The updated atom with the appropriate information.
+ *
+ * This function will return @c NULL in case of error. If the last error is @c
+ * LLDPCTL_ERR_WOULDBLOCK, the write should be retried later with the exact same
+ * parameters. LLDPCTL_ERR_BAD_VALUE is raised when the provided atom is not
+ * correct.
+ */
+lldpctl_atom_t *lldpctl_atom_set_str(lldpctl_atom_t *atom, lldpctl_key_t key,
+    const char *value);
+
+/**
+ * Retrieve a bit of information as a buffer.
+ *
+ * @param atom        The atom we want to query.
+ * @param key         The information we want from the atom.
+ * @param length[out] The size of the returned buffer.
+ * @return The requested buffer or @c NULL if the information is not available.
+ *
+ * Not every value of @c info will be available as a buffer. See the
+ * documentation of @c lldpctl_key_t for values accepting to be extracted as a
+ * string. Usually, only piece of information stored as buffer are available in
+ * this form.
+ *
+ * As a convenience, this function will return @c NULL if the first parameter is
+ * @c NULL and no error will be raised. If this function returns @c NULL, the
+ * third parameter is set to 0.
+ *
+ * The provided buffer may live inside the atom providing it. If you need it
+ * longer, duplicate it.
+ */
+const u_int8_t *lldpctl_atom_get_buffer(lldpctl_atom_t *atom, lldpctl_key_t key,
+    size_t *length);
+
+/**
+ * Set a bit of information using a buffer
+ *
+ * @param atom   The atom we want to write to.
+ * @param key    The key information we want to write.
+ * @param value  The value of the information we want to write.
+ * @param length The length of the provided buffer.
+ * @return The updated atom with the appropriate information.
+ *
+ * This function will return @c NULL in case of error. If the last error is @c
+ * LLDPCTL_ERR_WOULDBLOCK, the write should be retried later with the exact same
+ * parameters. LLDPCTL_ERR_BAD_VALUE is raised when the provided atom is not
+ * correct.
+ */
+lldpctl_atom_t *lldpctl_atom_set_buffer(lldpctl_atom_t *atom, lldpctl_key_t key,
+    const u_int8_t *value, size_t length);
+
+/**
+ * Retrieve a bit of information as an integer.
+ *
+ * @param atom The atom we want to query.
+ * @param key  The information we want from the atom.
+ * @return The requested integer or -1 if the information is not available
+ *
+ * Not every value of @c info will be available as an integer. See the
+ * documentation of @c lldpctl_key_t for values accepting to be extracted as a
+ * string. Usually, only piece of information stored as an integer are available
+ * in this form.
+ *
+ * Only @c lldpctl_last_error() can tell if the returned value is an error or
+ * not. However, most values extracted from lldpd cannot be negative.
+ */
+long int lldpctl_atom_get_int(lldpctl_atom_t *atom, lldpctl_key_t key);
+
+/**
+ * Set a bit of information using an integer
+ *
+ * @param atom   The atom we want to write to.
+ * @param key    The key information we want to write.
+ * @param value  The value of the information we want to write.
+ * @return The updated atom with the appropriate information.
+ *
+ * This function will return @c NULL in case of error. If the last error is @c
+ * LLDPCTL_ERR_WOULDBLOCK, the write should be retried later with the exact same
+ * parameters. LLDPCTL_ERR_BAD_VALUE is raised when the provided atom is not
+ * correct.
+ */
+lldpctl_atom_t *lldpctl_atom_set_int(lldpctl_atom_t *atom, lldpctl_key_t key,
+    long int value);
+
+/*@}*/
+
+/*@{*/
+/**
+ * Iterator over an iterable atom (a list of ports, a list of VLAN, ...). When
+ * an atom is a list, it can be iterated over to extract the appropriate values.
+ *
+ * @see lldpctl_atom_iter(), lldpctl_atom_iter_next(), lldpctl_atom_iter_value()
+ */
+typedef struct lldpctl_atom_iter_t lldpctl_atom_iter_t;
+
+/**
+ * Return an iterator over a given atom.
+ *
+ * If an atom is iterable (if it is a list, like a list of ports, a list of
+ * VLAN, a list of neighbors), it is possible to iterate over it. First use this
+ * function to get an iterator then use @c lldpctl_atom_iter_next() to get the
+ * next item and @c lldpctl_atom_iter_value() to the actuel item.
+ *
+ * @param atom The atom we want to create an iterator from.
+ * @return The iterator or @c NULL if an error happened or if the atom is empty
+ *         (check with @c lldpctl_last_error()).
+ *
+ * As a convenience, if the provided atom is @c NULL, this function will return
+ * @c NULL and no error will be raised.
+ */
+lldpctl_atom_iter_t *lldpctl_atom_iter(lldpctl_atom_t *atom);
+
+/**
+ * Return the next element of an iterator.
+ *
+ * @param atom The atom we are currently iterating.
+ * @param iter The iterator we want the next element from.
+ * @return An iterator starting on the next element or @c NULL if we have no
+ *         more elements
+ *
+ * @see lldpctl_atom_iter(), lldpctl_atom_iter_value().
+ *
+ * As a convenience, if the provided atom is @c NULL, this function will return
+ * @c NULL and no error will be raised.
+ */
+lldpctl_atom_iter_t *lldpctl_atom_iter_next(lldpctl_atom_t *atom, lldpctl_atom_iter_t *iter);
+
+/**
+ * Return the value of an iterator.
+ *
+ * @param atom The atom we are currently iterating.
+ * @param iter The iterator we want the next element from.
+ * @return The atom currently associated with the iterator.
+ *
+ * @see lldpctl_atom_iter(), lldpctl_atom_iter_next().
+ */
+lldpctl_atom_t *lldpctl_atom_iter_value(lldpctl_atom_t *atom, lldpctl_atom_iter_t *iter);
+
+/**
+ * Convenience macro to iter over every value of an iterable object.
+ *
+ * @param atom  The atom you want to iterate on.
+ * @param value Atom that will be used to contain each value.
+ *
+ * This macro behaves as a for loop. Moreover, at the end of each iteration,
+ * value is deallocated. Don't use it outside of the loop!
+ */
+#define lldpctl_atom_foreach(atom, value)                              \
+       for (lldpctl_atom_iter_t *iter = lldpctl_atom_iter(atom);       \
+            iter && (value = lldpctl_atom_iter_value(atom, iter));     \
+            iter = lldpctl_atom_iter_next(atom, iter),                 \
+                lldpctl_atom_dec_ref(value))
+
+/**
+ * Create a new value for an iterable element.
+ *
+ * The value is meant to be appended using @c lldpctl_atom_set(). Currently,
+ * there is no way to delete an element from a list. It is also not advisable to
+ * use getters on a newly created object until it is fully initialized. If its
+ * internal representation is using a buffer, it may not be initialized until
+ * the first set.
+ *
+ * @param atom The atom we want to create a new element for.
+ * @return The new element.
+ */
+lldpctl_atom_t *lldpctl_atom_create(lldpctl_atom_t *atom);
+/*@}*/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/lib/lldpctl.pc.in b/src/lib/lldpctl.pc.in
new file mode 100644 (file)
index 0000000..c8500ec
--- /dev/null
@@ -0,0 +1,10 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=${prefix}/include
+
+Name: lldpctl
+Description: Library to interface with lldpd, a 802.1AB daemon
+Version: @VERSION@
+Libs: -L${libdir} -llldpctl
+Cflags: -I${includedir}
diff --git a/src/lib/private.h b/src/lib/private.h
new file mode 100644 (file)
index 0000000..755c870
--- /dev/null
@@ -0,0 +1,231 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/queue.h>
+#include "../compat/compat.h"
+#include "../marshal.h"
+#include "../ctl.h"
+
+/* connection.c */
+struct lldpctl_conn_t {
+       /* Callback handling */
+       lldpctl_recv_callback recv; /* Receive callback */
+       lldpctl_send_callback send; /* Send callback */
+       void *user_data;            /* Callback user data */
+
+       /* IO state handling. */
+       uint8_t *input_buffer;  /* Current input/output buffer */
+       uint8_t *output_buffer; /* Current input/output buffer */
+       size_t input_buffer_len;
+       size_t output_buffer_len;
+
+#define CONN_STATE_IDLE                        0
+#define CONN_STATE_GET_INTERFACES_SEND 1
+#define CONN_STATE_GET_INTERFACES_RECV 2
+#define CONN_STATE_GET_PORT_SEND       3
+#define CONN_STATE_GET_PORT_RECV       4
+#define CONN_STATE_SET_PORT_SEND       5
+#define CONN_STATE_SET_PORT_RECV       6
+       int state;              /* Current state */
+       void *state_data;       /* Data attached to the state. It is used to
+                                * check that we are using the same data as a
+                                * previous call until the state machine goes to
+                                * CONN_STATE_IDLE. */
+
+       /* Error handling */
+       lldpctl_error_t error;  /* Last error */
+};
+
+/* User data for synchronous callbacks. */
+struct lldpctl_conn_sync_t {
+       int fd;                 /* File descriptor to the socket. */
+};
+
+ssize_t _lldpctl_needs(lldpctl_conn_t *lldpctl, size_t length);
+int _lldpctl_do_something(lldpctl_conn_t *conn,
+    int state_send, int state_recv, void *state_data,
+    enum hmsg_type type,
+    void *to_send, struct marshal_info *mi_send,
+    void **to_recv, struct marshal_info *mi_recv);
+
+/* error.c */
+#define SET_ERROR(conn, x)    ((conn)->error = x)
+#define RESET_ERROR(conn)     SET_ERROR((conn), LLDPCTL_NO_ERROR)
+
+
+/* atom.c and atom-private.c */
+typedef enum {
+       atom_interfaces_list,
+       atom_interface,
+       atom_ports_list,
+       atom_port,
+       atom_mgmts_list,
+       atom_mgmt,
+#ifdef ENABLE_DOT3
+       atom_dot3_power,
+#endif
+#ifdef ENABLE_DOT1
+       atom_vlans_list,
+       atom_vlan,
+       atom_ppvids_list,
+       atom_ppvid,
+       atom_pis_list,
+       atom_pi,
+#endif
+#ifdef ENABLE_LLDPMED
+       atom_med_policies_list,
+       atom_med_policy,
+       atom_med_locations_list,
+       atom_med_location,
+       atom_med_caelements_list,
+       atom_med_caelement,
+       atom_med_power,
+#endif
+} atom_t;
+
+void *_lldpctl_alloc_in_atom(lldpctl_atom_t *, size_t);
+const char *_lldpctl_dump_in_atom(lldpctl_atom_t *, const uint8_t *, size_t, char, size_t);
+
+struct atom_buffer {
+       TAILQ_ENTRY(atom_buffer) next;
+       u_int8_t data[0];
+};
+
+struct lldpctl_atom_t {
+       int count;
+       atom_t type;
+       lldpctl_conn_t *conn;
+       TAILQ_HEAD(, atom_buffer) buffers; /* List of buffers */
+
+       /* Destructor */
+       void                 (*free)(lldpctl_atom_t *);
+
+       /* Iterators */
+       lldpctl_atom_iter_t *(*iter)(lldpctl_atom_t *);
+       lldpctl_atom_iter_t *(*next)(lldpctl_atom_t *, lldpctl_atom_iter_t *);
+       lldpctl_atom_t      *(*value)(lldpctl_atom_t *, lldpctl_atom_iter_t *);
+
+       /* Getters */
+       lldpctl_atom_t *(*get)(lldpctl_atom_t *, lldpctl_key_t);
+       const char     *(*get_str)(lldpctl_atom_t *, lldpctl_key_t);
+       const u_int8_t *(*get_buffer)(lldpctl_atom_t *, lldpctl_key_t, size_t *);
+       long int        (*get_int)(lldpctl_atom_t *, lldpctl_key_t);
+
+       /* Setters */
+       lldpctl_atom_t *(*set)(lldpctl_atom_t *, lldpctl_key_t, lldpctl_atom_t *);
+       lldpctl_atom_t *(*set_str)(lldpctl_atom_t *, lldpctl_key_t, const char *);
+       lldpctl_atom_t *(*set_buffer)(lldpctl_atom_t *, lldpctl_key_t, const u_int8_t *, size_t);
+       lldpctl_atom_t *(*set_int)(lldpctl_atom_t *, lldpctl_key_t, long int);
+       lldpctl_atom_t *(*create)(lldpctl_atom_t *);
+};
+
+struct _lldpctl_atom_interfaces_list_t {
+       lldpctl_atom_t base;
+       struct lldpd_interface_list *ifs;
+};
+
+struct _lldpctl_atom_interface_t {
+       lldpctl_atom_t base;
+       char *name;
+};
+
+struct _lldpctl_atom_port_t {
+       lldpctl_atom_t base;
+       struct lldpd_hardware *hardware; /* Local port only */
+       struct lldpd_port     *port;     /* Local and remote */
+       struct _lldpctl_atom_port_t *parent; /* Local port if we are a remote port */
+};
+
+/* Can represent any simple list holding just a reference to a port. */
+struct _lldpctl_atom_any_list_t {
+       lldpctl_atom_t base;
+       struct _lldpctl_atom_port_t *parent;
+};
+
+struct _lldpctl_atom_mgmts_list_t {
+       lldpctl_atom_t base;
+       struct _lldpctl_atom_port_t *parent;
+       struct lldpd_chassis *chassis; /* Chassis containing the list of IP addresses */
+};
+
+struct _lldpctl_atom_mgmt_t {
+       lldpctl_atom_t base;
+       struct _lldpctl_atom_port_t *parent;
+       struct lldpd_mgmt *mgmt;
+};
+
+#ifdef ENABLE_DOT3
+struct _lldpctl_atom_dot3_power_t {
+       lldpctl_atom_t base;
+       struct _lldpctl_atom_port_t *parent;
+};
+#endif
+
+#ifdef ENABLE_DOT1
+struct _lldpctl_atom_vlan_t {
+       lldpctl_atom_t base;
+       struct _lldpctl_atom_port_t *parent;
+       struct lldpd_vlan *vlan;
+};
+
+struct _lldpctl_atom_ppvid_t {
+       lldpctl_atom_t base;
+       struct _lldpctl_atom_port_t *parent;
+       struct lldpd_ppvid *ppvid;
+};
+
+struct _lldpctl_atom_pi_t {
+       lldpctl_atom_t base;
+       struct _lldpctl_atom_port_t *parent;
+       struct lldpd_pi *pi;
+};
+#endif
+
+#ifdef ENABLE_LLDPMED
+struct _lldpctl_atom_med_policy_t {
+       lldpctl_atom_t base;
+       struct _lldpctl_atom_port_t *parent;
+       struct lldpd_med_policy *policy;
+};
+
+struct _lldpctl_atom_med_location_t {
+       lldpctl_atom_t base;
+       struct _lldpctl_atom_port_t *parent;
+       struct lldpd_med_loc *location;
+};
+
+/* This list should have the same structure than _llpdctl_atom_any_list_t */
+struct _lldpctl_atom_med_caelements_list_t {
+       lldpctl_atom_t base;
+       struct _lldpctl_atom_med_location_t *parent;
+};
+
+struct _lldpctl_atom_med_caelement_t {
+       lldpctl_atom_t base;
+       struct _lldpctl_atom_med_location_t *parent;
+       int type;
+       uint8_t *value;
+       size_t   len;
+};
+
+struct _lldpctl_atom_med_power_t {
+       lldpctl_atom_t base;
+       struct _lldpctl_atom_port_t *parent;
+};
+#endif
+
+struct lldpctl_atom_t *_lldpctl_new_atom(lldpctl_conn_t *conn, atom_t type, ...);
diff --git a/src/lldp-const.h b/src/lldp-const.h
new file mode 100644 (file)
index 0000000..533948e
--- /dev/null
@@ -0,0 +1,218 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _LLDP_H
+#define _LLDP_H
+
+/* Definitions prefixed by `LLDP_` are constants from LLDP
+ * specifications. Definitions prefixed by `LLDPD_` are custom
+ * constants that are useful in the context of lldpd and its clients.
+ */
+
+/* Chassis ID subtype */
+#define LLDP_CHASSISID_SUBTYPE_CHASSIS 1
+#define LLDP_CHASSISID_SUBTYPE_IFALIAS 2
+#define LLDP_CHASSISID_SUBTYPE_PORT    3
+#define LLDP_CHASSISID_SUBTYPE_LLADDR  4
+#define LLDP_CHASSISID_SUBTYPE_ADDR    5
+#define LLDP_CHASSISID_SUBTYPE_IFNAME  6
+#define LLDP_CHASSISID_SUBTYPE_LOCAL   7
+
+/* Port ID subtype */
+#define LLDP_PORTID_SUBTYPE_IFALIAS    1
+#define LLDP_PORTID_SUBTYPE_PORT       2
+#define LLDP_PORTID_SUBTYPE_LLADDR     3
+#define LLDP_PORTID_SUBTYPE_ADDR       4
+#define LLDP_PORTID_SUBTYPE_IFNAME     5
+#define LLDP_PORTID_SUBTYPE_AGENTCID   6
+#define LLDP_PORTID_SUBTYPE_LOCAL      7
+
+/* 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
+
+/* Dot3 Power Devicetype */
+#define LLDP_DOT3_POWER_PSE    1
+#define LLDP_DOT3_POWER_PD     2
+
+/* Dot3 Power Pairs (RFC 3621) */
+#define LLDP_DOT3_POWERPAIRS_SIGNAL    1
+#define LLDP_DOT3_POWERPAIRS_SPARE     2
+
+/* Dot3 Power type (for 802.3at) */
+#define LLDP_DOT3_POWER_8023AT_OFF     0
+#define LLDP_DOT3_POWER_8023AT_TYPE1   1
+#define LLDP_DOT3_POWER_8023AT_TYPE2   2
+
+/* Dot3 power source */
+#define LLDP_DOT3_POWER_SOURCE_UNKNOWN 0
+#define LLDP_DOT3_POWER_SOURCE_PRIMARY 1
+#define LLDP_DOT3_POWER_SOURCE_PSE     1
+#define LLDP_DOT3_POWER_SOURCE_BACKUP  2
+#define LLDP_DOT3_POWER_SOURCE_BOTH    3
+
+/* Dot3 power priority */
+#define LLDP_DOT3_POWER_PRIO_UNKNOWN   0
+#define LLDP_DOT3_POWER_PRIO_CRITICAL  1
+#define LLDP_DOT3_POWER_PRIO_HIGH      2
+#define LLDP_DOT3_POWER_PRIO_LOW       3
+
+/* 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
+
+/* Capabilities */
+#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
+
+#define LLDP_PPVID_CAP_SUPPORTED       (1 << 1)
+#define LLDP_PPVID_CAP_ENABLED         (1 << 2)
+
+/* see http://www.iana.org/assignments/address-family-numbers */
+#define LLDP_MGMT_ADDR_NONE    0
+#define LLDP_MGMT_ADDR_IP4     1
+#define LLDP_MGMT_ADDR_IP6     2
+
+#define LLDP_MGMT_IFACE_UNKNOWN 1
+#define LLDP_MGMT_IFACE_IFINDEX 2
+#define LLDP_MGMT_IFACE_SYSPORT        3
+
+#define LLDP_MED_CLASS_I       1
+#define LLDP_MED_CLASS_II      2
+#define LLDP_MED_CLASS_III     3
+#define LLDP_MED_NETWORK_DEVICE        4
+
+/* LLDP MED application ttpes */
+#define LLDP_MED_APPTYPE_UNDEFINED             0
+#define LLDP_MED_APPTYPE_VOICE                 1
+#define LLDP_MED_APPTYPE_VOICESIGNAL           2
+#define LLDP_MED_APPTYPE_GUESTVOICE            3
+#define LLDP_MED_APPTYPE_GUESTVOICESIGNAL      4
+#define LLDP_MED_APPTYPE_SOFTPHONEVOICE                5
+#define LLDP_MED_APPTYPE_VIDEOCONFERENCE       6
+#define LLDP_MED_APPTYPE_VIDEOSTREAM           7
+#define LLDP_MED_APPTYPE_VIDEOSIGNAL           8
+#define LLDP_MED_APPTYPE_LAST                  LLDP_MED_APPTYPE_VIDEOSIGNAL
+
+/* LLDP MED location formats */
+#define LLDP_MED_LOCFORMAT_COORD       1
+#define LLDP_MED_LOCFORMAT_CIVIC       2
+#define LLDP_MED_LOCFORMAT_ELIN                3
+#define LLDP_MED_LOCFORMAT_LAST                LLDP_MED_LOCFORMAT_ELIN
+
+#define LLDP_MED_LOCATION_GEOID_WGS84          1
+#define LLDP_MED_LOCATION_GEOID_NAD83          2
+#define LLDP_MED_LOCATION_GEOID_NAD83_MLLW     3
+
+#define LLDP_MED_LOCATION_ALTITUDE_UNIT_METER  1
+#define LLDP_MED_LOCATION_ALTITUDE_UNIT_FLOOR  2
+
+/* LLDP MED power related constants */
+#define LLDP_MED_POW_TYPE_PSE          1
+#define LLDP_MED_POW_TYPE_PD           2
+#define LLDP_MED_POW_TYPE_RESERVED     3
+
+#define LLDP_MED_POW_SOURCE_UNKNOWN    1
+#define LLDP_MED_POW_SOURCE_PRIMARY    2
+#define LLDP_MED_POW_SOURCE_BACKUP     3
+#define LLDP_MED_POW_SOURCE_RESERVED   4
+#define LLDP_MED_POW_SOURCE_PSE                5
+#define LLDP_MED_POW_SOURCE_LOCAL      6
+#define LLDP_MED_POW_SOURCE_BOTH       7
+
+#define LLDP_MED_POW_PRIO_UNKNOWN      0
+#define LLDP_MED_POW_PRIO_CRITICAL     1
+#define LLDP_MED_POW_PRIO_HIGH         2
+#define LLDP_MED_POW_PRIO_LOW          3
+
+/* LLDP MED capabilities */
+#define LLDP_MED_CAP_CAP       0x01
+#define LLDP_MED_CAP_POLICY    0x02
+#define LLDP_MED_CAP_LOCATION  0x04
+#define LLDP_MED_CAP_MDI_PSE   0x08
+#define LLDP_MED_CAP_MDI_PD    0x10
+#define LLDP_MED_CAP_IV                0x20
+
+/* Protocol constants for multi-protocol lldpd */
+#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
+#define LLDPD_MODE_FDP         6
+#define LLDPD_MODE_MAX         LLDPD_MODE_FDP
+
+
+#endif /* _LLDP_H */
diff --git a/src/lldp.h b/src/lldp.h
deleted file mode 100644 (file)
index 738b4ce..0000000
+++ /dev/null
@@ -1,248 +0,0 @@
-/*
- * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#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                                      \
-}
-
-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}
-#define LLDP_TLV_ORG_MED {0x00, 0x12, 0xbb}
-
-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
-};
-
-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
-};
-
-/* 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
-
-/* Dot3 Power Devicetype */
-#define LLDP_DOT3_POWER_PSE 1
-#define LLDP_DOT3_POWER_PD 2
-
-/* Dot3 Power Pairs (RFC 3621) */
-#define LLDP_DOT3_POWERPAIRS_SIGNAL 1
-#define LLDP_DOT3_POWERPAIRS_SPARE 2
-
-/* Dot3 Power type (for 802.3at) */
-#define LLDP_DOT3_POWER_8023AT_OFF 0
-#define LLDP_DOT3_POWER_8023AT_TYPE1 1
-#define LLDP_DOT3_POWER_8023AT_TYPE2 2
-
-/* Dot3 power source */
-#define LLDP_DOT3_POWER_SOURCE_UNKNOWN 0
-#define LLDP_DOT3_POWER_SOURCE_PRIMARY 1
-#define LLDP_DOT3_POWER_SOURCE_PSE 1
-#define LLDP_DOT3_POWER_SOURCE_BACKUP 2
-#define LLDP_DOT3_POWER_SOURCE_BOTH 3
-
-/* Dot3 power priority */
-#define LLDP_DOT3_POWER_PRIO_UNKNOWN 0
-#define LLDP_DOT3_POWER_PRIO_CRITICAL 1
-#define LLDP_DOT3_POWER_PRIO_HIGH 2
-#define LLDP_DOT3_POWER_PRIO_LOW 3
-
-/* 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_NONE     = 0,
-       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
-};
-
-#ifdef ENABLE_LLDPMED
-enum {
-       LLDP_TLV_MED_CAP        = 1,
-       LLDP_TLV_MED_POLICY     = 2,
-       LLDP_TLV_MED_LOCATION   = 3,
-       LLDP_TLV_MED_MDI        = 4,
-       LLDP_TLV_MED_IV_HW      = 5,
-       LLDP_TLV_MED_IV_FW      = 6,
-       LLDP_TLV_MED_IV_SW      = 7,
-       LLDP_TLV_MED_IV_SN      = 8,
-       LLDP_TLV_MED_IV_MANUF   = 9,
-       LLDP_TLV_MED_IV_MODEL   = 10,
-       LLDP_TLV_MED_IV_ASSET   = 11
-};
-#endif
-
-#define LLDPMED_CLASS_I 1
-#define LLDPMED_CLASS_II 2
-#define LLDPMED_CLASS_III 3
-#define LLDPMED_NETWORK_DEVICE 4
-
-#define LLDPMED_APPTYPE_VOICE 1
-#define LLDPMED_APPTYPE_VOICESIGNAL 2
-#define LLDPMED_APPTYPE_GUESTVOICE 3
-#define LLDPMED_APPTYPE_GUESTVOICESIGNAL 4
-#define LLDPMED_APPTYPE_SOFTPHONEVOICE 5
-#define LLDPMED_APPTYPE_VIDEOCONFERENCE 6
-#define LLDPMED_APPTYPE_VIDEOSTREAM 7
-#define LLDPMED_APPTYPE_VIDEOSIGNAL 8
-#define LLDPMED_APPTYPE_LAST LLDPMED_APPTYPE_VIDEOSIGNAL
-
-#define LLDPMED_LOCFORMAT_COORD 1
-#define LLDPMED_LOCFORMAT_CIVIC 2
-#define LLDPMED_LOCFORMAT_ELIN 3
-#define LLDPMED_LOCFORMAT_LAST LLDPMED_LOCFORMAT_ELIN
-
-#define LLDPMED_POW_TYPE_PSE 1
-#define LLDPMED_POW_TYPE_PD 2
-#define LLDPMED_POW_TYPE_RESERVED 3
-
-#define LLDPMED_POW_SOURCE_UNKNOWN 1
-#define LLDPMED_POW_SOURCE_PRIMARY 2
-#define LLDPMED_POW_SOURCE_BACKUP 3
-#define LLDPMED_POW_SOURCE_RESERVED 4
-#define LLDPMED_POW_SOURCE_PSE 5
-#define LLDPMED_POW_SOURCE_LOCAL 6
-#define LLDPMED_POW_SOURCE_BOTH 7
-
-#define LLDPMED_POW_PRIO_UNKNOWN 0
-#define LLDPMED_POW_PRIO_CRITICAL 1
-#define LLDPMED_POW_PRIO_HIGH 2
-#define LLDPMED_POW_PRIO_LOW 3
-
-#define LLDPMED_CAP_CAP 0x01
-#define LLDPMED_CAP_POLICY 0x02
-#define LLDPMED_CAP_LOCATION 0x04
-#define LLDPMED_CAP_MDI_PSE 0x08
-#define LLDPMED_CAP_MDI_PD 0x10
-#define LLDPMED_CAP_IV 0x20
-
-#endif /* _LLDP_H */
diff --git a/src/lldpctl.c b/src/lldpctl.c
deleted file mode 100644 (file)
index 436ac2e..0000000
+++ /dev/null
@@ -1,773 +0,0 @@
-/*
- * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "lldpctl.h"
-
-#include <stdio.h>
-#include <unistd.h>
-#include <time.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <arpa/inet.h>
-
-static void             usage(void);
-
-#ifdef HAVE___PROGNAME
-extern const char      *__progname;
-#else
-# define __progname "lldpctl"
-#endif
-
-#define LLDPCTL_ARGS "hdvaf:L:P:O:o:"
-
-static void
-usage(void)
-{
-       fprintf(stderr, "Usage:   %s [OPTIONS ...] [INTERFACES ...]\n", __progname);
-       fprintf(stderr, "Version: %s\n", PACKAGE_STRING);
-
-       fprintf(stderr, "\n");
-
-       fprintf(stderr, "-d          Enable more debugging information.\n");
-       fprintf(stderr, "-a          Display all remote ports, including hidden ones.\n");
-       fprintf(stderr, "-f format   Choose output format (plain, keyvalue or xml).\n");
-#ifdef ENABLE_LLDPMED
-       fprintf(stderr, "-L location Enable the transmission of LLDP-MED location TLV for the\n");
-       fprintf(stderr, "            given interfaces. Can be repeated to enable the transmission\n");
-       fprintf(stderr, "            of the location in several formats.\n");
-       fprintf(stderr, "-P policy   Enable the transmission of LLDP-MED Network Policy TLVs\n");
-       fprintf(stderr, "            for the given interfaces. Can be repeated to specify\n");
-       fprintf(stderr, "            different policies.\n");
-       fprintf(stderr, "-O poe      Enable the transmission of LLDP-MED POE-MDI TLV\n");
-       fprintf(stderr, "            for the given interfaces.\n");
-#endif
-#ifdef ENABLE_DOT3
-       fprintf(stderr, "-o poe      Enable the transmission of Dot3 POE-MDI TLV\n");
-       fprintf(stderr, "            for the given interfaces.\n");
-#endif
-
-       fprintf(stderr, "\n");
-
-       fprintf(stderr, "see manual page lldpctl(8) for more information\n");
-       exit(1);
-}
-
-#ifdef ENABLE_LLDPMED
-static int
-lldpd_parse_location(struct lldpd_med_loc *medloc, const char *location)
-{
-       char *l, *e, *s, *data, *n;
-       double ll, altitude;
-       u_int32_t intpart, floatpart;
-       int type = 0, i;
-
-       memset(medloc, 0, sizeof(struct lldpd_med_loc));
-
-       if (strlen(location) == 0)
-               return 0;
-       if ((l = strdup(location)) == NULL)
-               fatal(NULL);
-       s = l;
-       if ((e = strchr(s, ':')) == NULL)
-               goto invalid_location;
-       *e = '\0';
-       type = atoi(s);
-       switch (type) {
-       case LLDPMED_LOCFORMAT_COORD:
-               /* Coordinates */
-               if ((medloc->data = (char *)malloc(16)) == NULL)
-                       fatal(NULL);
-               medloc->data_len = 16;
-               medloc->format = LLDPMED_LOCFORMAT_COORD;
-               data = medloc->data;
-
-               /* Latitude and longitude */
-               for (i = 0; i < 2; i++) {
-                       s = e+1;
-                       if ((e = strchr(s, ':')) == NULL)
-                               goto invalid_location;
-                       *e = '\0';
-                       ll = atof(s);
-                       s = e + 1;
-                       if ((e = strchr(s, ':')) == NULL)
-                               goto invalid_location;
-                       *e = '\0';
-                       intpart = (int)ll;
-                       floatpart = (ll - intpart) * (1 << 25);
-                       if (((i == 0) && (*s == 'S')) ||
-                           ((i == 1) && (*s == 'W'))) {
-                               intpart = ~intpart;
-                               intpart += 1;
-                               floatpart = ~floatpart;
-                               floatpart += 1;
-                       } else if (((i == 0) && (*s != 'N')) ||
-                           ((i == 1) && (*s != 'E'))) 
-                               goto invalid_location;
-                       *(u_int8_t *)data = (6 << 2) |         /* Precision */
-                           ((intpart & 0x180) >> 7);          /* Int part 2 bits */
-                       data++;
-                       *(u_int8_t *)data = (((intpart & 0x7f) << 1) | /* Int part 7 bits */
-                           ((floatpart & 0x1000000) >> 24));   /* Float part 1 bit */
-                       data++;
-                       *(u_int8_t *)data = (floatpart & 0xff0000) >> 16; /* 8 bits */
-                       data++;
-                       *(u_int8_t *)data = (floatpart & 0xff00) >> 8; /* 8 bits */
-                       data++;
-                       *(u_int8_t *)data = (floatpart & 0xff); /* 8 bits */
-                       data++;
-               }
-               
-               /* Altitude */
-               s = e+1;
-               if ((e = strchr(s, ':')) == NULL)
-                       goto invalid_location;
-               *e = '\0';
-               altitude = atof(s);
-               s = e+1;
-               if ((e = strchr(s, ':')) == NULL)
-                       goto invalid_location;
-               *e = '\0';
-               if (altitude < 0) {
-                       intpart = -(int)altitude;
-                       floatpart = (-(altitude + intpart)) * (1 << 8);
-                       intpart = ~intpart; intpart += 1;
-                       floatpart = ~floatpart; floatpart += 1;
-               } else {
-                       intpart = (int)altitude;
-                       floatpart = (altitude - intpart) * (1 << 8);
-               }
-               if ((*s != 'm') && (*s != 'f'))
-                       goto invalid_location;
-               *(u_int8_t *)data = ((((*s == 'm')?1:2) << 4) |        /* Type 4 bits */
-                   0);                                                /* Precision 4 bits */
-               data++;
-               *(u_int8_t *)data = ((6 << 6) |                        /* Precision 2 bits */
-                   ((intpart & 0x3f0000) >> 16));                     /* Int 6 bits */
-               data++;
-               *(u_int8_t *)data = (intpart & 0xff00) >> 8; /* Int 8 bits */
-               data++;
-               *(u_int8_t *)data = intpart & 0xff; /* Int 8 bits */
-               data++;
-               *(u_int8_t *)data = floatpart & 0xff; /* Float 8 bits */
-               data++;
-
-               /* Datum */
-               s = e + 1;
-               if (strchr(s, ':') != NULL)
-                       goto invalid_location;
-               *(u_int8_t *)data = atoi(s);
-               break;
-       case LLDPMED_LOCFORMAT_CIVIC:
-               /* Civic address */
-               medloc->data_len = 4;
-               s = e+1;
-               if ((s = strchr(s, ':')) == NULL)
-                       goto invalid_location;
-               s = s+1;
-               do {
-                       if ((s = strchr(s, ':')) == NULL)
-                               break;
-                       s = s+1;
-                       /* s is the beginning of the word */
-                       if ((n = strchr(s, ':')) == NULL)
-                               n = s + strlen(s);
-                       /* n is the end of the word */
-                       medloc->data_len += (n - s) + 2;
-                       if ((s = strchr(s, ':')) == NULL)
-                               break;
-                       s = s+1;
-               } while (1);
-               s = e+1;
-               if ((medloc->data =
-                    (char *)malloc(medloc->data_len)) == NULL)
-                       fatal(NULL);
-               medloc->format = LLDPMED_LOCFORMAT_CIVIC;
-               data = medloc->data;
-               *(u_int8_t *)data = medloc->data_len - 1;
-               data++;
-               *(u_int8_t *)data = 2; /* Client location */
-               data++;
-               if ((e = strchr(s, ':')) == NULL)
-                       goto invalid_location;
-               if ((e - s) != 2)
-                       goto invalid_location;
-               memcpy(data, s, 2); /* Country code */
-               data += 2;
-               while (*e != '\0') {
-                       s=e+1;
-                       if ((e = strchr(s, ':')) == NULL)
-                               goto invalid_location;
-                       *e = '\0';
-                       *(u_int8_t *)data = atoi(s);
-                       data++;
-                       s=e+1;
-                       if ((e = strchr(s, ':')) == NULL)
-                               e = s + strlen(s);
-                       *(u_int8_t *)data = e - s;
-                       data++;
-                       memcpy(data, s, e-s);
-                       data += e-s;
-               }
-               break;
-       case LLDPMED_LOCFORMAT_ELIN:
-               s = e+1;
-               medloc->data_len = strlen(s);
-               if ((medloc->data = (char *)malloc(strlen(s))) == NULL)
-                       fatal(NULL);
-               medloc->format = LLDPMED_LOCFORMAT_ELIN;
-               strcpy(medloc->data, s);
-               break;
-       default:
-               type = 0;
-               goto invalid_location;
-       }
-
-       return 0;
-invalid_location:
-       LLOG_WARNX("the format of the location is invalid (%s)",
-               location);
-       return -1;
-}
-
-static int
-lldpd_parse_policy(struct lldpd_med_policy *medpolicy, const char *policy)
-{
-       const char *e;
-       int app_type            = 0;
-       int unknown_policy_flag = 0;
-       int tagged_flag         = 0;
-       int vlan_id             = 0;
-       int l2_prio             = 0;
-       int dscp                = 0;
-
-       memset(medpolicy, 0, sizeof(struct lldpd_med_policy));
-
-       if (strlen(policy) == 0) {
-               return 0;
-       }
-
-       e = policy;
-
-       /* Application Type: */
-       app_type = atoi(e);
-       if (app_type < 1 || app_type > LLDPMED_APPTYPE_LAST) {
-               LLOG_WARNX("Application Type (%u) out of range.", app_type);
-               goto invalid_policy;
-       }
-
-       /* Unknown Policy Flag (U): */
-       if ((e = strchr(e, ':')) == NULL) {
-               LLOG_WARNX("Expected Unknown Policy Flag (U).");
-               goto invalid_policy;
-       }
-       e = e + 1;
-       unknown_policy_flag = atoi(e);
-       if (unknown_policy_flag < 0 || unknown_policy_flag > 1) {
-               LLOG_WARNX("Unknown Policy Flag (%u) out of range.", unknown_policy_flag);
-               goto invalid_policy;
-       }
-
-       /* Tagged Flag (T): */
-       if ((e = strchr(e, ':')) == NULL) {
-               LLOG_WARNX("Expected Tagged Flag (T).");
-               goto invalid_policy;
-       }
-       e = e + 1;
-       tagged_flag = atoi(e);
-       if (tagged_flag < 0 || tagged_flag > 1) {
-               LLOG_WARNX("Tagged Flag (%u) out of range.", tagged_flag);
-               goto invalid_policy;
-       }
-
-       /* VLAN-ID (VID): */
-       if ((e = strchr(e, ':')) == NULL) {
-               LLOG_WARNX("Expected VLAN ID (VID).");
-               goto invalid_policy;
-       }
-       e = e + 1;
-       vlan_id = atoi(e);
-       if (vlan_id < 0 || vlan_id > 4094) {
-               LLOG_WARNX("VLAN ID (%u) out of range.", vlan_id);
-               goto invalid_policy;
-       }
-
-       /* Layer 2 Priority: */
-       if ((e = strchr(e, ':')) == NULL) {
-               LLOG_WARNX("Expected Layer 2 Priority.");
-               goto invalid_policy;
-       }
-       e = e + 1;
-       l2_prio = atoi(e);
-       if (l2_prio < 0 || l2_prio > 7) {
-               LLOG_WARNX("Layer 2 Priority (%u) out of range.", l2_prio);
-               goto invalid_policy;
-       }
-
-       /* DSCP value: */
-       if ((e = strchr(e, ':')) == NULL) {
-               LLOG_WARNX("Expected DSCP value.");
-               goto invalid_policy;
-       }
-       e = e + 1;
-       dscp = atoi(e);
-       if (dscp < 0 || dscp > 63) {
-               LLOG_WARNX("DSCP value (%u) out of range.", dscp);
-               goto invalid_policy;
-       }
-
-       medpolicy->type     = (u_int8_t)  app_type;
-       medpolicy->unknown  = (u_int8_t)  unknown_policy_flag;
-       medpolicy->tagged   = (u_int8_t)  tagged_flag;
-       medpolicy->vid      = (u_int16_t) vlan_id;
-       medpolicy->priority = (u_int8_t)  l2_prio;
-       medpolicy->dscp     = (u_int8_t)  dscp;
-
-       return 0;
-
-invalid_policy:
-       LLOG_WARNX("The format of the policy is invalid (%s)",
-               policy);
-       return -1;
-}
-
-static int
-lldpd_parse_power(struct lldpd_med_power *medpower, const char *poe)
-{
-       const char *e;
-       int device_type = 0;
-       int source      = 0;
-       int priority    = 0;
-       int val         = 0;
-
-       memset(medpower, 0, sizeof(struct lldpd_med_power));
-
-       if (strlen(poe) == 0)
-               return 0;
-       e = poe;
-
-       /* Device type */
-       if (!strncmp(e, "PD", 2))
-               device_type = LLDPMED_POW_TYPE_PD;
-       else if (!strncmp(e, "PSE", 3))
-               device_type = LLDPMED_POW_TYPE_PSE;
-       else {
-               LLOG_WARNX("Device type should be either 'PD' or 'PSE'.");
-               goto invalid_poe;
-       }
-
-       /* Source */
-       if ((e = strchr(e, ':')) == NULL) {
-               LLOG_WARNX("Expected power source.");
-               goto invalid_poe;
-       }
-       source = atoi(++e);
-       if (source < 0 || source > 3) {
-               LLOG_WARNX("Power source out of range (%d).", source);
-               goto invalid_poe;
-       }
-
-       /* Priority */
-       if ((e = strchr(e, ':')) == NULL) {
-               LLOG_WARNX("Expected power priority.");
-               goto invalid_poe;
-       }
-       priority = atoi(++e);
-       if (priority < 0 || priority > 3) {
-               LLOG_WARNX("Power priority out of range (%d).", priority);
-               goto invalid_poe;
-       }
-
-       /* Value */
-       if ((e = strchr(e, ':')) == NULL) {
-               LLOG_WARNX("Expected power value.");
-               goto invalid_poe;
-       }
-       val = atoi(++e);
-       if (val < 0 || val > 1023) {
-               LLOG_WARNX("Power value out of range (%d).", val);
-               goto invalid_poe;
-       }
-
-       medpower->devicetype = device_type;
-       medpower->priority = priority;
-       medpower->val = val;
-
-       switch (device_type) {
-       case LLDPMED_POW_TYPE_PD:
-               switch (source) {
-               case 1:
-                       medpower->source = LLDPMED_POW_SOURCE_PSE;
-                       break;
-               case 2:
-                       medpower->source = LLDPMED_POW_SOURCE_LOCAL;
-                       break;
-               case 3:
-                       medpower->source = LLDPMED_POW_SOURCE_BOTH;
-                       break;
-               default:
-                       medpower->source = LLDPMED_POW_SOURCE_UNKNOWN;
-                       break;
-               }
-               break;
-       case LLDPMED_POW_TYPE_PSE:
-               switch (source) {
-               case 1:
-                       medpower->source = LLDPMED_POW_SOURCE_PRIMARY;
-                       break;
-               case 2:
-                       medpower->source = LLDPMED_POW_SOURCE_BACKUP;
-                       break;
-               default:
-                       medpower->source = LLDPMED_POW_SOURCE_UNKNOWN;
-                       break;
-               }
-               break;
-       }
-       return 0;
-
- invalid_poe:
-       LLOG_WARNX("The format POE-MDI is invalid (%s)", poe);
-       return -1;
-}
-#endif
-
-#ifdef ENABLE_DOT3
-static int
-lldpd_parse_dot3_power(struct lldpd_dot3_power *dot3power, const char *poe)
-{
-       const char *e;
-
-       memset(dot3power, 0, sizeof(struct lldpd_dot3_power));
-
-       if (strlen(poe) == 0)
-               return 0;
-       e = poe;
-
-       /* Device type */
-       if (!strncmp(e, "PD", 2))
-               dot3power->devicetype = LLDP_DOT3_POWER_PD;
-       else if (!strncmp(e, "PSE", 3))
-               dot3power->devicetype = LLDP_DOT3_POWER_PSE;
-       else {
-               LLOG_WARNX("Device type should be either 'PD' or 'PSE'.");
-               goto invalid_dot3_poe;
-       }
-
-       /* Supported */
-       if ((e = strchr(e, ':')) == NULL) {
-               LLOG_WARNX("Expected power support.");
-               goto invalid_dot3_poe;
-       }
-       dot3power->supported = atoi(++e);
-       if (dot3power->supported > 1) {
-               LLOG_WARNX("Power support should be 1 or 0, not %d", dot3power->supported);
-               goto invalid_dot3_poe;
-       }
-
-       /* Enabled */
-       if ((e = strchr(e, ':')) == NULL) {
-               LLOG_WARNX("Expected power ability.");
-               goto invalid_dot3_poe;
-       }
-       dot3power->enabled = atoi(++e);
-       if (dot3power->enabled > 1) {
-               LLOG_WARNX("Power ability should be 1 or 0, not %d", dot3power->enabled);
-               goto invalid_dot3_poe;
-       }
-
-       /* Pair control */
-       if ((e = strchr(e, ':')) == NULL) {
-               LLOG_WARNX("Expected power pair control ability.");
-               goto invalid_dot3_poe;
-       }
-       dot3power->paircontrol = atoi(++e);
-       if (dot3power->paircontrol > 1) {
-               LLOG_WARNX("Power pair control ability should be 1 or 0, not %d",
-                   dot3power->paircontrol);
-               goto invalid_dot3_poe;
-       }
-
-       /* Power pairs */
-       if ((e = strchr(e, ':')) == NULL) {
-               LLOG_WARNX("Expected power pairs.");
-               goto invalid_dot3_poe;
-       }
-       dot3power->pairs = atoi(++e);
-       if (dot3power->pairs < 1 || dot3power->pairs > 2) {
-               LLOG_WARNX("Power pairs should be 1 or 2, not %d.", dot3power->pairs);
-               goto invalid_dot3_poe;
-       }
-
-       /* Class */
-       if ((e = strchr(e, ':')) == NULL) {
-               LLOG_WARNX("Expected power class.");
-               goto invalid_dot3_poe;
-       }
-       dot3power->class = atoi(++e);
-       if (dot3power->class > 5) {
-               LLOG_WARNX("Power class out of range (%d).", dot3power->class);
-               goto invalid_dot3_poe;
-       }
-       /* 802.3at */
-       if ((e = strchr(e, ':')) == NULL) {
-               dot3power->powertype = LLDP_DOT3_POWER_8023AT_OFF;
-               return 0;
-       }
-       /* 802.3at: Power type */
-       dot3power->powertype = atoi(++e);
-       if ((dot3power->powertype != LLDP_DOT3_POWER_8023AT_TYPE1) &&
-           (dot3power->powertype != LLDP_DOT3_POWER_8023AT_TYPE2)) {
-               LLOG_WARNX("Incorrect power type (%d).", dot3power->powertype);
-               goto invalid_dot3_poe;
-       }
-       /* 802.3at: Source */
-       if ((e = strchr(e, ':')) == NULL) {
-               LLOG_WARNX("Expected power source.");
-               goto invalid_dot3_poe;
-       }
-       dot3power->source = atoi(++e);
-       if (dot3power->source > 3) {
-               LLOG_WARNX("Power source out of range (%d).", dot3power->source);
-               goto invalid_dot3_poe;
-       }
-       /* 802.3at: priority */
-       if ((e = strchr(e, ':')) == NULL) {
-               LLOG_WARNX("Expected power priority.");
-               goto invalid_dot3_poe;
-       }
-       dot3power->priority = atoi(++e);
-       if (dot3power->priority > 3) {
-               LLOG_WARNX("Power priority out of range (%d).", dot3power->priority);
-               goto invalid_dot3_poe;
-       }
-       /* 802.3at: requested */
-       if ((e = strchr(e, ':')) == NULL) {
-               LLOG_WARNX("Expected requested power value.");
-               goto invalid_dot3_poe;
-       }
-       dot3power->requested = atoi(++e);
-       /* 802.3at: allocated */
-       if ((e = strchr(e, ':')) == NULL) {
-               LLOG_WARNX("Expected allocated power value.");
-               goto invalid_dot3_poe;
-       }
-       dot3power->allocated = atoi(++e);
-       return 0;
-
- invalid_dot3_poe:
-       LLOG_WARNX("The format POE-MDI is invalid (%s)", poe);
-       return -1;
-}
-#endif
-
-#define ACTION_SET_LOCATION   (1 << 0)
-#define ACTION_SET_POLICY     (1 << 1)
-#define ACTION_SET_POWER      (1 << 2)
-#define ACTION_SET_DOT3_POWER (1 << 3)
-static void
-set_port(int s, int argc, char *argv[], int action)
-{
-       int ch;
-#ifdef ENABLE_LLDPMED
-       struct lldpd_med_loc    location;
-       struct lldpd_med_power  medpower;
-       struct lldpd_med_policy policy;
-#endif
-#ifdef ENABLE_DOT3
-       struct lldpd_dot3_power dot3power;
-#endif
-       int done;
-       int skip[4] = {0, 0, 0, 0};
-       int skip_[4];
-       struct lldpd_interface *iff;
-       struct lldpd_interface_list *ifs;
-       struct lldpd_port_set set;
-       int i;
-
- redo_set_port:
-       memcpy(skip_, skip, sizeof(skip));
-       done = 1;
-       optind = 1;
-       while ((ch = getopt(argc, argv, LLDPCTL_ARGS)) != -1) {
-               switch (ch) {
-#ifdef ENABLE_LLDPMED
-               case 'L':
-                       if (action & ACTION_SET_LOCATION) {
-                               if (skip_[0]--) break;
-                               if (lldpd_parse_location(&location, optarg) == -1)
-                                       fatalx("set_port: incorrect location");
-                               done = 0;
-                               skip[0]++;
-                       }
-                       break;
-               case 'P':
-                       if (action & ACTION_SET_POLICY) {
-                               if (skip_[1]--) break;
-                               if (lldpd_parse_policy(&policy, optarg) == -1)
-                                       fatalx("set_port: incorrect network policy.");
-                               done = 0;
-                               skip[1]++;
-                       }
-                       break;
-               case 'O':
-                       if (action & ACTION_SET_POWER) {
-                               if (skip_[2]--) break;
-                               if (lldpd_parse_power(&medpower, optarg) == -1)
-                                       fatalx("set_port: incorrect MED POE-MDI.");
-                               done = 0;
-                               skip[2]++;
-                       }
-                       break;
-#endif
-#ifdef ENABLE_DOT3
-               case 'o':
-                       if (action & ACTION_SET_DOT3_POWER) {
-                               if (skip_[3]--) break;
-                               if (lldpd_parse_dot3_power(&dot3power, optarg) == -1)
-                                       fatalx("set_port: incorrect DOT3 POE-MDI.");
-                               done = 0;
-                               skip[3]++;
-                       }
-                       break;
-#endif
-               }
-       }
-       if (done) return;
-
-       ifs = get_interfaces(s);
-       TAILQ_FOREACH(iff, ifs, next) {
-               if (optind < argc) {
-                       for (i = optind; i < argc; i++)
-                               if (strncmp(argv[i], iff->name, IFNAMSIZ) == 0)
-                                       break;
-                       if (i == argc)
-                               continue;
-               }
-
-               memset(&set, 0, sizeof(struct lldpd_port_set));
-               set.ifname = iff->name;
-#ifdef ENABLE_LLDPMED
-               if (action & ACTION_SET_LOCATION)  set.med_location = &location;
-               if (action & ACTION_SET_POLICY)    set.med_policy   = &policy;
-               if (action & ACTION_SET_POWER)     set.med_power    = &medpower;
-#endif
-#ifdef ENABLE_DOT3
-               if (action & ACTION_SET_DOT3_POWER)set.dot3_power   = &dot3power;
-#endif
-               if (ctl_msg_send_recv(s, SET_PORT,
-                                     &set, &MARSHAL_INFO(lldpd_port_set),
-                                     NULL, NULL) == -1)
-                       fatalx("set_port: unable to send new location information");
-               LLOG_INFO("configuration change for %s", iff->name);
-       }
-       goto redo_set_port;
-}
-
-struct lldpd_interface_list*
-get_interfaces(int s)
-{
-       struct lldpd_interface_list *ifs;
-       if (ctl_msg_send_recv(s, GET_INTERFACES, NULL, NULL, (void **)&ifs,
-               &MARSHAL_INFO(lldpd_interface_list)) == -1)
-               fatalx("get_interfaces: unable to get the list of interfaces");
-       return ifs;
-}
-
-struct lldpd_hardware*
-get_interface(int s, char *name)
-{
-       struct lldpd_hardware *h;
-       if (ctl_msg_send_recv(s, GET_INTERFACE,
-               name, &MARSHAL_INFO(string),
-               (void **)&h, &MARSHAL_INFO(lldpd_hardware)) == -1)
-               fatalx("get_interface: unable to get information for specified interface");
-       return h;
-}
-
-int
-main(int argc, char *argv[])
-{
-       int ch, s, debug = 1;
-       char * fmt = "plain";
-       int action = 0, hidden = 0;
-       
-       /*
-        * Get and parse command line options
-        */
-       while ((ch = getopt(argc, argv, LLDPCTL_ARGS)) != -1) {
-               switch (ch) {
-               case 'h':
-                       usage();
-                       break;
-               case 'd':
-                       debug++;
-                       break;
-               case 'v':
-                       fprintf(stdout, "%s\n", PACKAGE_VERSION);
-                       exit(0);
-                       break;
-               case 'a':
-                       hidden = 1;
-                       break;
-               case 'f':
-                       fmt = optarg;
-                       break;
-               case 'L':
-               case 'P':
-               case 'O':
-#ifdef ENABLE_LLDPMED
-                       switch (ch) {
-                       case 'L': action |= ACTION_SET_LOCATION; break;
-                       case 'P': action |= ACTION_SET_POLICY; break;
-                       case 'O': action |= ACTION_SET_POWER; break;
-                       }
-#else
-                       fprintf(stderr, "LLDP-MED support is not built-in\n");
-                       usage();
-#endif
-                       break;
-               case 'o':
-#ifdef ENABLE_DOT3
-                       action |= ACTION_SET_DOT3_POWER;
-#else
-                       fprintf(stderr, "Dot3 support is not built-in\n");
-                       usage();
-#endif
-                       break;
-               default:
-                       usage();
-               }
-       }
-
-       log_init(debug, __progname);
-
-       if ((action != 0) && (getuid() != 0)) {
-               fatalx("mere mortals may not do that, 'root' privileges are required.");
-       }
-       
-       if ((s = ctl_connect(LLDPD_CTL_SOCKET)) == -1)
-               fatalx("unable to connect to socket " LLDPD_CTL_SOCKET);
-
-       if (!action)
-               display_interfaces(s, fmt, hidden, argc, argv);
-       else
-               set_port(s, argc, argv, action);
-       
-       close(s);
-       return 0;
-}
diff --git a/src/lldpd-structs.c b/src/lldpd-structs.c
new file mode 100644 (file)
index 0000000..8e186c4
--- /dev/null
@@ -0,0 +1,149 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include "lldpd-structs.h"
+
+void
+lldpd_chassis_mgmt_cleanup(struct lldpd_chassis *chassis)
+{
+       struct lldpd_mgmt *mgmt, *mgmt_next;
+       for (mgmt = TAILQ_FIRST(&chassis->c_mgmt);
+            mgmt != NULL;
+            mgmt = mgmt_next) {
+               mgmt_next = TAILQ_NEXT(mgmt, m_entries);
+               free(mgmt);
+       }
+       TAILQ_INIT(&chassis->c_mgmt);
+}
+
+void
+lldpd_chassis_cleanup(struct lldpd_chassis *chassis, int all)
+{
+#ifdef ENABLE_LLDPMED
+       free(chassis->c_med_hw);
+       free(chassis->c_med_sw);
+       free(chassis->c_med_fw);
+       free(chassis->c_med_sn);
+       free(chassis->c_med_manuf);
+       free(chassis->c_med_model);
+       free(chassis->c_med_asset);
+#endif
+       free(chassis->c_id);
+       free(chassis->c_name);
+       free(chassis->c_descr);
+       lldpd_chassis_mgmt_cleanup(chassis);
+       if (all)
+               free(chassis);
+}
+
+#ifdef ENABLE_DOT1
+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);
+               free(vlan);
+       }
+       TAILQ_INIT(&port->p_vlans);
+}
+
+void
+lldpd_ppvid_cleanup(struct lldpd_port *port)
+{
+       struct lldpd_ppvid *ppvid, *ppvid_next;
+       for (ppvid = TAILQ_FIRST(&port->p_ppvids);
+           ppvid != NULL;
+           ppvid = ppvid_next) {
+               ppvid_next = TAILQ_NEXT(ppvid, p_entries);
+               free(ppvid);
+       }
+       TAILQ_INIT(&port->p_ppvids);
+}
+
+void
+lldpd_pi_cleanup(struct lldpd_port *port)
+{
+       struct lldpd_pi *pi, *pi_next;
+       for (pi = TAILQ_FIRST(&port->p_pids);
+           pi != NULL;
+           pi = pi_next) {
+               free(pi->p_pi);
+               pi_next = TAILQ_NEXT(pi, p_entries);
+               free(pi);
+       }
+       TAILQ_INIT(&port->p_pids);
+}
+#endif
+
+void
+lldpd_remote_cleanup(struct lldpd_hardware *hardware, int all)
+{
+       struct lldpd_port *port, *port_next;
+       int del;
+       for (port = TAILQ_FIRST(&hardware->h_rports);
+            port != NULL;
+            port = port_next) {
+               port_next = TAILQ_NEXT(port, p_entries);
+               del = all;
+               if (!all &&
+                   (time(NULL) - port->p_lastupdate > port->p_chassis->c_ttl)) {
+                       hardware->h_rx_ageout_cnt++;
+                       del = 1;
+               }
+               if (del) {
+                       if (!all)
+                               TAILQ_REMOVE(&hardware->h_rports, port, p_entries);
+                       lldpd_port_cleanup(port, 1);
+                       free(port);
+               }
+       }
+       if (all) TAILQ_INIT(&hardware->h_rports);
+}
+
+/* If `all' is true, clear all information, including information that
+   are not refreshed periodically. Port should be freed manually. */
+void
+lldpd_port_cleanup(struct lldpd_port *port, int all)
+{
+#ifdef ENABLE_LLDPMED
+       int i;
+       if (all)
+               for (i=0; i < LLDP_MED_LOCFORMAT_LAST; i++)
+                       free(port->p_med_location[i].data);
+#endif
+#ifdef ENABLE_DOT1
+       lldpd_vlan_cleanup(port);
+       lldpd_ppvid_cleanup(port);
+       lldpd_pi_cleanup(port);
+#endif
+       free(port->p_id);
+       free(port->p_descr);
+       if (all) {
+               free(port->p_lastframe);
+               if (port->p_chassis) { /* chassis may not have been attributed, yet */
+                       port->p_chassis->c_refcount--;
+                       port->p_chassis = NULL;
+               }
+       }
+}
similarity index 53%
rename from src/lldpd.h
rename to src/lldpd-structs.h
index 431001b8f2e1afa0814103c7320dc655dfa44c09..a0dfc6443de1874fc2edd6ddfa83b8e0de4a824f 100644 (file)
@@ -1,3 +1,4 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
 /*
  * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
  *
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#ifndef _LLDPD_H
-#define _LLDPD_H
+#ifndef _LLDPD_STRUCTS_H
+#define _LLDPD_STRUCTS_H
 
 #if HAVE_CONFIG_H
 #  include <config.h>
 #endif
 
-#ifdef HAVE_VALGRIND_VALGRIND_H
-# include <valgrind/valgrind.h>
-#else
-# define RUNNING_ON_VALGRIND 0
-#endif
-
-#define _GNU_SOURCE 1
-#include <stdlib.h>
-#include <stddef.h>
-#include <string.h>
-#include <sys/queue.h>
-#ifdef HAVE_SYS_TYPES_H
-#  include <sys/types.h>
-#endif
+#include <sys/types.h>
+#include <sys/socket.h>
 #ifndef INCLUDE_LINUX_IF_H
 #  include <net/if.h>
 #else
 #  include <arpa/inet.h>
 #  include <linux/if.h>
 #endif
-#if HAVE_GETIFADDRS
-#  include <ifaddrs.h>
-#endif
 #include <net/ethernet.h>
 #include <netinet/in.h>
-#include <linux/ethtool.h>
-#include <sys/un.h>
+#include <sys/queue.h>
 
-#include "compat.h"
-#include "lldp.h"
-#if defined (ENABLE_CDP) || defined (ENABLE_FDP)
-#  include "cdp.h"
-#endif
-#ifdef ENABLE_SONMP
-#  include "sonmp.h"
-#endif
-#ifdef ENABLE_EDP
-#  include "edp.h"
-#endif
+#include "compat/compat.h"
 #include "marshal.h"
-
-/* We don't want to import event2/event.h. We only need those as
-   opaque structs. */
-struct event;
-struct event_base;
-
-#define SYSFS_CLASS_NET "/sys/class/net/"
-#define SYSFS_CLASS_DMI "/sys/class/dmi/id/"
-#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
+#include "lldp-const.h"
 
 #ifdef ENABLE_DOT1
-#define LLDPD_PPVID_CAP_SUPPORTED              (1 << 1)
-#define LLDPD_PPVID_CAP_ENABLED                        (1 << 2)
-
 struct lldpd_ppvid {
        TAILQ_ENTRY(lldpd_ppvid) p_entries;
        u_int8_t                p_cap_status;
@@ -115,6 +71,7 @@ MARSHAL_END;
 
 #ifdef ENABLE_LLDPMED
 struct lldpd_med_policy {
+       u_int8_t                 index; /* Not used. */
        u_int8_t                 type;
        u_int8_t                 unknown;
        u_int8_t                 tagged;
@@ -125,6 +82,7 @@ struct lldpd_med_policy {
 MARSHAL(lldpd_med_policy);
 
 struct lldpd_med_loc {
+       u_int8_t                 index; /* Not used. */
        u_int8_t                 format;
        char                    *data;
        int                      data_len;
@@ -178,14 +136,10 @@ inline static int
 lldpd_af(int af)
 {
        switch (af) {
-       case LLDPD_AF_IPV4:
-               return AF_INET;
-       case LLDPD_AF_IPV6:
-               return AF_INET6;
-       case LLDPD_AF_LAST:
-               return AF_MAX;
-       default:
-               return AF_UNSPEC;
+       case LLDPD_AF_IPV4: return AF_INET;
+       case LLDPD_AF_IPV6: return AF_INET6;
+       case LLDPD_AF_LAST: return AF_MAX;
+       default: return AF_UNSPEC;
        }
 }
 
@@ -280,8 +234,8 @@ struct lldpd_port {
 
 #ifdef ENABLE_LLDPMED
        u_int16_t                p_med_cap_enabled;
-       struct lldpd_med_policy  p_med_policy[LLDPMED_APPTYPE_LAST];
-       struct lldpd_med_loc     p_med_location[LLDPMED_LOCFORMAT_LAST];
+       struct lldpd_med_policy  p_med_policy[LLDP_MED_APPTYPE_LAST];
+       struct lldpd_med_loc     p_med_location[LLDP_MED_LOCFORMAT_LAST];
        struct lldpd_med_power   p_med_power;
 #endif
 
@@ -403,222 +357,15 @@ MARSHAL_END;
 TAILQ_HEAD(lldpd_interface_list, lldpd_interface);
 MARSHAL_TQ(lldpd_interface_list, lldpd_interface);
 
-#define PROTO_SEND_SIG struct lldpd *, 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 protocol {
-#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
-#define LLDPD_MODE_FDP 6
-#define LLDPD_MODE_MAX LLDPD_MODE_FDP
-       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 */
-};
-
-/* Smart mode / Hide mode */
-#define SMART_INCOMING_FILTER          (1<<0) /* Incoming filtering enabled */
-#define SMART_INCOMING_ONE_PROTO       (1<<1) /* On reception, keep only one proto */
-#define SMART_INCOMING_ONE_NEIGH       (1<<2) /* On reception, keep only one neighbor */
-#define SMART_OUTGOING_FILTER          (1<<3) /* Outgoing filtering enabled */
-#define SMART_OUTGOING_ONE_PROTO       (1<<4) /* On emission, keep only one proto */
-#define SMART_OUTGOING_ONE_NEIGH       (1<<5) /* On emission, consider only one neighbor */
-#define SMART_INCOMING (SMART_INCOMING_FILTER |    \
-                        SMART_INCOMING_ONE_PROTO | \
-                        SMART_INCOMING_ONE_NEIGH)
-#define SMART_OUTGOING (SMART_OUTGOING_FILTER |                \
-                       SMART_OUTGOING_ONE_PROTO |      \
-                       SMART_OUTGOING_ONE_NEIGH)
-#define SMART_HIDDEN(port) (port->p_hidden_in)
-
-struct lldpd {
-       int                      g_sock;
-       int                      g_delay;
-
-       struct event_base       *g_base;
-#ifdef USE_SNMP
-#endif
-
-       struct protocol         *g_protocols;
-       time_t                   g_lastsent;
-       int                      g_lastrid;
-       int                      g_smart;
-       int                      g_receiveonly;
-       struct event            *g_main_loop;
-#ifdef USE_SNMP
-       int                      g_snmp;
-       struct event            *g_snmp_timeout;
-       void                    *g_snmp_fds;
-       char                    *g_snmp_agentx;
-#endif /* USE_SNMP */
-
-       /* Unix socket handling */
-       int                      g_ctl;
-       struct event            *g_ctl_event;
-
-       char                    *g_mgmt_pattern;
-       char                    *g_cid_pattern;
-       char                    *g_interfaces;
-
-       char                    *g_descr_override;
-       char                    *g_platform_override;
-       char                    *g_lsb_release;
-       int                      g_advertise_version;
-#ifdef ENABLE_LLDPMED
-       int                      g_noinventory;
-#endif
-
-#define LOCAL_CHASSIS(cfg) ((struct lldpd_chassis *)(TAILQ_FIRST(&cfg->g_chassis)))
-       TAILQ_HEAD(, lldpd_chassis) g_chassis;
-       TAILQ_HEAD(, lldpd_hardware) g_hardware;
-};
-
-typedef void(*lldpd_ifhandlers)(struct lldpd *, struct ifaddrs *);
-
-enum hmsg_type {
-       NONE,
-       GET_INTERFACES,         /* Get list of interfaces */
-       GET_INTERFACE,          /* Get all information related to an interface */
-       SET_PORT,               /* Set port-related information (location, power, policy) */
-};
-
-/* lldpd.c */
-struct lldpd_hardware  *lldpd_get_hardware(struct lldpd *,
-    char *, int, struct lldpd_ops *);
-struct lldpd_hardware  *lldpd_alloc_hardware(struct lldpd *, char *);
-void    lldpd_hardware_cleanup(struct lldpd*, struct lldpd_hardware *);
+/* Cleanup functions */
+void    lldpd_chassis_mgmt_cleanup(struct lldpd_chassis *);
+void    lldpd_chassis_cleanup(struct lldpd_chassis *, int);
+void    lldpd_remote_cleanup(struct lldpd_hardware *, int);
+void    lldpd_port_cleanup(struct lldpd_port *, int);
 #ifdef ENABLE_DOT1
 void    lldpd_ppvid_cleanup(struct lldpd_port *);
 void    lldpd_vlan_cleanup(struct lldpd_port *);
 void    lldpd_pi_cleanup(struct lldpd_port *);
 #endif
-void    lldpd_remote_cleanup(struct lldpd *, struct lldpd_hardware *, int);
-void    lldpd_port_cleanup(struct lldpd*, struct lldpd_port *, int);
-struct lldpd_mgmt *lldpd_alloc_mgmt(int family, void *addr, size_t addrsize, u_int32_t iface);
-void    lldpd_chassis_mgmt_cleanup(struct lldpd_chassis *);
-void    lldpd_chassis_cleanup(struct lldpd_chassis *, int);
-void    lldpd_recv(struct lldpd *, struct lldpd_hardware *, int);
-void    lldpd_loop(struct lldpd *);
-int     lldpd_main(int, char **);
-
-/* event.c */
-void    levent_loop(struct lldpd *);
-void    levent_hardware_init(struct lldpd_hardware *);
-void    levent_hardware_add_fd(struct lldpd_hardware *, int);
-void    levent_hardware_release(struct lldpd_hardware *);
-
-/* lldp.c */
-int     lldp_send(PROTO_SEND_SIG);
-int     lldp_decode(PROTO_DECODE_SIG);
-
-/* cdp.c */
-#ifdef ENABLE_CDP
-int     cdpv1_send(PROTO_SEND_SIG);
-int     cdpv2_send(PROTO_SEND_SIG);
-int     cdpv1_guess(PROTO_GUESS_SIG);
-int     cdpv2_guess(PROTO_GUESS_SIG);
-#endif
-#if defined (ENABLE_CDP) || defined (ENABLE_FDP)
-int     cdp_decode(PROTO_DECODE_SIG);
-#endif
-#ifdef ENABLE_FDP
-int     fdp_send(PROTO_SEND_SIG);
-#endif
-
-#ifdef ENABLE_SONMP
-/* sonmp.c */
-int     sonmp_send(PROTO_SEND_SIG);
-int     sonmp_decode(PROTO_DECODE_SIG);
-#endif
-
-#ifdef ENABLE_EDP
-/* edp.c */
-int     edp_send(PROTO_SEND_SIG);
-int     edp_decode(PROTO_DECODE_SIG);
-#endif
-
-/* ctl.c */
-int     ctl_create(char *);
-int     ctl_connect(char *);
-void    ctl_cleanup(char *);
-int     ctl_msg_send(int, enum hmsg_type, void *, size_t);
-int     ctl_msg_recv(int, enum hmsg_type *, void **);
-int     ctl_msg_send_recv(int, enum hmsg_type,
-    void *, struct marshal_info *, void **, struct marshal_info *);
-
-/* interfaces.c */
-void    lldpd_ifh_whitelist(struct lldpd *, struct ifaddrs *);
-void    lldpd_ifh_bond(struct lldpd *, struct ifaddrs *);
-void    lldpd_ifh_eth(struct lldpd *, struct ifaddrs *);
-#ifdef ENABLE_DOT1
-void    lldpd_ifh_vlan(struct lldpd *, struct ifaddrs *);
-#endif
-void    lldpd_ifh_mgmt(struct lldpd *, struct ifaddrs *);
-void    lldpd_ifh_chassis(struct lldpd *, struct ifaddrs *);
 
-/* dmi.c */
-#ifdef ENABLE_LLDPMED
-#if __i386__ || __amd64__
-char   *dmi_hw(void);
-char   *dmi_fw(void);
-char   *dmi_sn(void);
-char   *dmi_manuf(void);
-char   *dmi_model(void);
-char   *dmi_asset(void);
-#endif
 #endif
-
-/* log.c */
-void             log_init(int, const char *);
-void             log_warn(const char *, ...) __attribute__ ((format (printf, 1, 2)));
-#define LLOG_WARN(x,...) log_warn("%s: " x, __FUNCTION__ , ## __VA_ARGS__)
-void             log_warnx(const char *, ...) __attribute__ ((format (printf, 1, 2)));
-#define LLOG_WARNX(x,...) log_warnx("%s: " x,  __FUNCTION__ , ## __VA_ARGS__)
-void             log_info(const char *, ...) __attribute__ ((format (printf, 1, 2)));
-#define LLOG_INFO(x,...) log_info("%s: " x, __FUNCTION__ , ## __VA_ARGS__)
-void             log_debug(const char *, ...) __attribute__ ((format (printf, 1, 2)));
-#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);
-void            agent_init(struct lldpd *, char *);
-
-/* agent_priv.c */
-void            agent_priv_register_domain(void);
-
-/* client.c */
-struct client_handle {
-       enum hmsg_type type;
-       int (*handle)(struct lldpd*, enum hmsg_type *,
-           void *, int, void **);
-};
-
-int     client_handle_client(struct lldpd *, int,
-    enum hmsg_type, void *, int);
-
-/* priv.c */
-void    priv_init(char*, int, uid_t, gid_t);
-void    priv_ctl_cleanup(void);
-char           *priv_gethostbyname(void);
-int             priv_open(char*);
-int             priv_ethtool(char*, struct ethtool_cmd*);
-int             priv_iface_init(const char *);
-int     priv_iface_multicast(const char *, u_int8_t *, int);
-int     priv_snmp_socket(struct sockaddr_un *);
-
-/* privsep_fdpass.c */
-int     receive_fd(int);
-void    send_fd(int, int);
-
-#endif /* _LLDPD_H */
index 2b110f437e541f5632b4e0bae43d261dc05b6e32..02a094a45cbb8e528169af5ceb12b6c30be2084a 100644 (file)
--- a/src/log.c
+++ b/src/log.c
@@ -1,3 +1,4 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
 /*     $OpenBSD: log.c,v 1.11 2007/12/07 17:17:00 reyk Exp $   */
 
 /*
  * 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>
+#define _GNU_SOURCE
 #include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
 #include <syslog.h>
+#include <sys/types.h>
+#include <string.h>
+#include <errno.h>
 #include <time.h>
 
-static int      debug;
+/* By default, logging is done on stderr. */
+static int      debug = 1;
+
+/* Logging can be modified by providing an appropriate log handler. */
+static void (*logh)(int severity, const char *msg) = NULL;
 
 static void     vlog(int, const char *, va_list);
 static void     logit(int, const char *, ...);
@@ -42,6 +47,13 @@ log_init(int n_debug, const char *progname)
        tzset();
 }
 
+void
+log_register(void (*cb)(int, const char*))
+{
+       logh = cb;
+}
+
+
 static void
 logit(int pri, const char *fmt, ...)
 {
@@ -55,9 +67,16 @@ logit(int pri, const char *fmt, ...)
 static void
 vlog(int pri, const char *fmt, va_list ap)
 {
-       char    *nfmt;
-
-       if (debug) {
+       if (logh) {
+               char *result;
+               if (vasprintf(&result, fmt, ap) != -1) {
+                       logh(pri, result);
+                       return;
+               }
+               /* Otherwise, fallback to output on stderr. */
+       }
+       if (debug || logh) {
+               char *nfmt;
                /* best effort in out of mem situations */
                if (asprintf(&nfmt, "%s\n", fmt) == -1) {
                        vfprintf(stderr, fmt, ap);
@@ -121,7 +140,7 @@ log_debug(const char *emsg, ...)
 {
        va_list  ap;
 
-       if (debug > 1) {
+       if (debug > 1 || logh) {
                va_start(ap, emsg);
                vlog(LOG_DEBUG, emsg, ap);
                va_end(ap);
diff --git a/src/log.h b/src/log.h
new file mode 100644 (file)
index 0000000..957f534
--- /dev/null
+++ b/src/log.h
@@ -0,0 +1,36 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _LOG_H
+#define _LOG_H
+
+/* log.c */
+void             log_init(int, const char *);
+void             log_warn(const char *, ...) __attribute__ ((format (printf, 1, 2)));
+#define LLOG_WARN(x,...) log_warn("%s: " x, __FUNCTION__ , ## __VA_ARGS__)
+void             log_warnx(const char *, ...) __attribute__ ((format (printf, 1, 2)));
+#define LLOG_WARNX(x,...) log_warnx("%s: " x,  __FUNCTION__ , ## __VA_ARGS__)
+void             log_info(const char *, ...) __attribute__ ((format (printf, 1, 2)));
+#define LLOG_INFO(x,...) log_info("%s: " x, __FUNCTION__ , ## __VA_ARGS__)
+void             log_debug(const char *, ...) __attribute__ ((format (printf, 1, 2)));
+#define LLOG_DEBUG(x,...) log_debug("%s: " x, __FUNCTION__ , ## __VA_ARGS__)
+void             fatal(const char *);
+void             fatalx(const char *);
+
+void            log_register(void (*cb)(int, const char*));
+
+#endif
index 09ffa490bf309a1081fa29aac4fe8bb3dbac73dc..993d6543aabd2ed0dc07c674185482cda7082780 100644 (file)
@@ -1,3 +1,4 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
 /*
  * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx>
  *
  */
 
 #define MARSHAL_EXPORT
-#include "lldpd.h"
+#include "marshal.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <string.h>
+
+#include "compat/compat.h"
+#include "log.h"
+
+#include "lldpd-structs.h"
 
 /* A serialized object */
 struct marshal_serialized {
@@ -51,7 +63,7 @@ TAILQ_HEAD(ref_l, ref);
 size_t
 marshal_serialize_(struct marshal_info *mi, void *unserialized, void **input,
     int skip, void *_refs, int osize)
-{      
+{
        struct ref_l *refs = _refs;
        struct ref *cref;
        int size;
@@ -176,7 +188,7 @@ marshal_alloc(struct gc_l *pointers, size_t len, void *orig)
 {
        struct gc *gpointer = NULL;
 
-       void *result = malloc(len);
+       void *result = calloc(1, len);
        if (!result) return NULL;
        if ((gpointer = (struct gc *)calloc(1,
                    sizeof(struct gc))) == NULL) {
@@ -212,14 +224,14 @@ marshal_unserialize_(struct marshal_info *mi, void *buffer, size_t len, void **o
        int    total_len = sizeof(struct marshal_serialized) + (skip?0:mi->size);
        struct marshal_serialized *serialized = buffer;
        struct gc_l *pointers = _pointers;
-       int size, already;
+       int size, already, extra = 0;
        void *new;
        struct marshal_subinfo *current;
        struct gc *apointer;
 
        if (len < sizeof(struct marshal_serialized) || len < total_len) {
-               LLOG_WARNX("data to deserialize is too small for structure %s",
-                   mi->name);
+               LLOG_WARNX("data to deserialize is too small (%zu) for structure %s",
+                   len, mi->name);
                return 0;
        }
 
@@ -239,7 +251,8 @@ marshal_unserialize_(struct marshal_info *mi, void *buffer, size_t len, void **o
                switch (mi->name[0]) {
                case 'n': size = strnlen((char *)serialized->object,
                    len - sizeof(struct marshal_serialized)) + 1; break;
-               case 'f': size = osize; break;
+               case 'f': size = osize; extra=1; break; /* The extra byte is to ensure that
+                                                          the string is null terminated. */
                }
                if (size > len - sizeof(struct marshal_serialized)) {
                        LLOG_WARNX("data to deserialize contains a string too long");
@@ -251,7 +264,7 @@ marshal_unserialize_(struct marshal_info *mi, void *buffer, size_t len, void **o
 
        /* First, the main structure */
        if (!skip) {
-               if ((*output = marshal_alloc(pointers, size, serialized->orig)) == NULL) {
+               if ((*output = marshal_alloc(pointers, size + extra, serialized->orig)) == NULL) {
                        LLOG_WARNX("unable to allocate memory to unserialize structure %s",
                            mi->name);
                        total_len = 0;
index a72272f85e2de9ade60edc1a107dec2c0c1d8a31..4bdf39b4fe4d8cfac94cda3382c8cd4e5899c75b 100644 (file)
@@ -1,3 +1,4 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
 /*
  * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx>
  *
@@ -17,6 +18,9 @@
 #ifndef _MARSHAL_H
 #define _MARSHAL_H
 
+#include <stddef.h>
+#include <sys/types.h>
+
 struct marshal_info;
 enum marshal_subinfo_kind {
        pointer,
index 3f37cb555aee2cb76d3ba10665f8311689c2b374..2fb3565bff834686693160c8319d78ab5b9e1781 100644 (file)
@@ -6,33 +6,33 @@ check_marshal_SOURCES = check_marshal.c \
        $(top_srcdir)/src/marshal.h
 
 check_lldp_SOURCES = check_lldp.c \
-       $(top_srcdir)/src/lldpd.h \
+       $(top_srcdir)/src/daemon/lldpd.h \
        common.h common.c
 
 check_cdp_SOURCES = check_cdp.c \
-       $(top_srcdir)/src/lldpd.h \
+       $(top_srcdir)/src/daemon/lldpd.h \
        common.h common.c
 
 check_sonmp_SOURCES = check_sonmp.c \
-       $(top_srcdir)/src/lldpd.h \
+       $(top_srcdir)/src/daemon/lldpd.h \
        common.h common.c
 
 check_edp_SOURCES = check_edp.c \
-       $(top_srcdir)/src/lldpd.h \
+       $(top_srcdir)/src/daemon/lldpd.h \
        common.h common.c
 
 check_ifaddrs_SOURCES = check_ifaddrs.c \
-       $(top_srcdir)/src/lldpd.h \
-       $(top_srcdir)/src/getifaddrs.c
+       $(top_srcdir)/src/daemon/lldpd.h \
+       $(top_srcdir)/src/compat/getifaddrs.c
 
 AM_CFLAGS = @CHECK_CFLAGS@
-LDADD = $(top_builddir)/src/liblldpd.la @CHECK_LIBS@ @LIBEVENT_LDFLAGS@
+LDADD = $(top_builddir)/src/daemon/liblldpd.la @CHECK_LIBS@ @LIBEVENT_LDFLAGS@
 
 if USE_SNMP
 TESTS += check_snmp
 check_snmp_SOURCES = check_snmp.c \
-       $(top_srcdir)/src/lldpd.h \
-       $(top_srcdir)/src/agent.h
+       $(top_srcdir)/src/daemon/lldpd.h \
+       $(top_srcdir)/src/daemon/agent.h
 endif
 
 check_PROGRAMS = $(TESTS)
index 7ad844f089e9490b19704b139377aebfac89a3c6..a5b2509250d6c6bcdfb5d30ae9f3d9ee0a165de9 100644 (file)
@@ -4,7 +4,6 @@
 #include <arpa/inet.h>
 #include <netinet/in.h>
 #include <check.h>
-#include "../src/lldpd.h"
 #include "common.h"
 
 char filenameprefix[] = "cdp_send";
index 3a6b627f4481b796d531ad752e38cbdf2aa9e337..9dd7c7d2e87f422baac20fc325d7d6938d813b96 100644 (file)
@@ -4,7 +4,6 @@
 #include <arpa/inet.h>
 #include <netinet/in.h>
 #include <check.h>
-#include "../src/lldpd.h"
 #include "common.h"
 
 char filenameprefix[] = "edp_send";
index 47f6b841240208bd2b51cb445f9980186e3925f5..fba6275bc4278d92c28898c27e1dae4fabcccf73 100644 (file)
@@ -1,7 +1,7 @@
 #include <stdio.h>
 #include <arpa/inet.h>
 #include <check.h>
-#include "../src/lldpd.h"
+#include "common.h"
 
 #define DUMP "ifdump.txt"
 
index 87e6ba441f9d28aec7aeb7bb1b9890dda0f2b3b1..5cebdaa5e739a476fa65bacd6d8341186c12e7af 100644 (file)
@@ -4,7 +4,6 @@
 #include <arpa/inet.h>
 #include <netinet/in.h>
 #include <check.h>
-#include "../src/lldpd.h"
 #include "common.h"
 
 char filenameprefix[] = "lldp_send";
@@ -51,30 +50,30 @@ check_received_port_med(
        ck_assert_int_eq(rport->p_med_cap_enabled, sport->p_med_cap_enabled);
        ck_assert_int_eq(rport->p_med_cap_enabled, sport->p_med_cap_enabled);
        ck_assert_int_eq(
-               rport->p_med_location[LLDPMED_LOCFORMAT_CIVIC-1].format,
-               sport->p_med_location[LLDPMED_LOCFORMAT_CIVIC-1].format);
+               rport->p_med_location[LLDP_MED_LOCFORMAT_CIVIC-1].format,
+               sport->p_med_location[LLDP_MED_LOCFORMAT_CIVIC-1].format);
        ck_assert_int_eq(
-               rport->p_med_location[LLDPMED_LOCFORMAT_CIVIC-1].data_len,
-               sport->p_med_location[LLDPMED_LOCFORMAT_CIVIC-1].data_len);
+               rport->p_med_location[LLDP_MED_LOCFORMAT_CIVIC-1].data_len,
+               sport->p_med_location[LLDP_MED_LOCFORMAT_CIVIC-1].data_len);
        ck_assert_str_eq_n(
-               rport->p_med_location[LLDPMED_LOCFORMAT_CIVIC-1].data,
-               sport->p_med_location[LLDPMED_LOCFORMAT_CIVIC-1].data,
-               sport->p_med_location[LLDPMED_LOCFORMAT_CIVIC-1].data_len);
+               rport->p_med_location[LLDP_MED_LOCFORMAT_CIVIC-1].data,
+               sport->p_med_location[LLDP_MED_LOCFORMAT_CIVIC-1].data,
+               sport->p_med_location[LLDP_MED_LOCFORMAT_CIVIC-1].data_len);
        ck_assert_int_eq(
-               rport->p_med_policy[LLDPMED_APPTYPE_SOFTPHONEVOICE-1].type,
-               sport->p_med_policy[LLDPMED_APPTYPE_SOFTPHONEVOICE-1].type);
+               rport->p_med_policy[LLDP_MED_APPTYPE_SOFTPHONEVOICE-1].type,
+               sport->p_med_policy[LLDP_MED_APPTYPE_SOFTPHONEVOICE-1].type);
        ck_assert_int_eq(
-               rport->p_med_policy[LLDPMED_APPTYPE_SOFTPHONEVOICE-1].tagged,
-               sport->p_med_policy[LLDPMED_APPTYPE_SOFTPHONEVOICE-1].tagged);
+               rport->p_med_policy[LLDP_MED_APPTYPE_SOFTPHONEVOICE-1].tagged,
+               sport->p_med_policy[LLDP_MED_APPTYPE_SOFTPHONEVOICE-1].tagged);
        ck_assert_int_eq(
-               rport->p_med_policy[LLDPMED_APPTYPE_SOFTPHONEVOICE-1].vid,
-               sport->p_med_policy[LLDPMED_APPTYPE_SOFTPHONEVOICE-1].vid);
+               rport->p_med_policy[LLDP_MED_APPTYPE_SOFTPHONEVOICE-1].vid,
+               sport->p_med_policy[LLDP_MED_APPTYPE_SOFTPHONEVOICE-1].vid);
        ck_assert_int_eq(
-               rport->p_med_policy[LLDPMED_APPTYPE_SOFTPHONEVOICE-1].priority,
-               sport->p_med_policy[LLDPMED_APPTYPE_SOFTPHONEVOICE-1].priority);
+               rport->p_med_policy[LLDP_MED_APPTYPE_SOFTPHONEVOICE-1].priority,
+               sport->p_med_policy[LLDP_MED_APPTYPE_SOFTPHONEVOICE-1].priority);
        ck_assert_int_eq(
-               rport->p_med_policy[LLDPMED_APPTYPE_SOFTPHONEVOICE-1].dscp,
-               sport->p_med_policy[LLDPMED_APPTYPE_SOFTPHONEVOICE-1].dscp);
+               rport->p_med_policy[LLDP_MED_APPTYPE_SOFTPHONEVOICE-1].dscp,
+               sport->p_med_policy[LLDP_MED_APPTYPE_SOFTPHONEVOICE-1].dscp);
        ck_assert_int_eq(
                rport->p_med_power.devicetype, sport->p_med_power.devicetype);
        ck_assert_int_eq(rport->p_med_power.source, sport->p_med_power.source);
@@ -318,33 +317,33 @@ START_TEST (test_send_rcv_med)
        chassis.c_descr = "Chassis description";
        chassis.c_cap_available = LLDP_CAP_ROUTER | LLDP_CAP_BRIDGE;
        chassis.c_cap_enabled = LLDP_CAP_ROUTER;
-       chassis.c_med_cap_available = LLDPMED_CAP_CAP | LLDPMED_CAP_POLICY |
-               LLDPMED_CAP_LOCATION | LLDPMED_CAP_MDI_PSE |
-               LLDPMED_CAP_IV;
-       chassis.c_med_type = LLDPMED_CLASS_III;
+       chassis.c_med_cap_available = LLDP_MED_CAP_CAP | LLDP_MED_CAP_POLICY |
+               LLDP_MED_CAP_LOCATION | LLDP_MED_CAP_MDI_PSE |
+               LLDP_MED_CAP_IV;
+       chassis.c_med_type = LLDP_MED_CLASS_III;
        chassis.c_med_hw = "hardware rev 5";
        chassis.c_med_fw = "47b5";
        chassis.c_med_sw = "2.6.22b5";
        chassis.c_med_sn = "SN 47842";
        hardware.h_lport.p_med_cap_enabled = chassis.c_med_cap_available;
-       hardware.h_lport.p_med_location[LLDPMED_LOCFORMAT_CIVIC-1].format =
-               LLDPMED_LOCFORMAT_CIVIC;
-       hardware.h_lport.p_med_location[LLDPMED_LOCFORMAT_CIVIC-1].data = "Your favorite city";
-       hardware.h_lport.p_med_location[LLDPMED_LOCFORMAT_CIVIC-1].data_len = 
+       hardware.h_lport.p_med_location[LLDP_MED_LOCFORMAT_CIVIC-1].format =
+               LLDP_MED_LOCFORMAT_CIVIC;
+       hardware.h_lport.p_med_location[LLDP_MED_LOCFORMAT_CIVIC-1].data = "Your favorite city";
+       hardware.h_lport.p_med_location[LLDP_MED_LOCFORMAT_CIVIC-1].data_len = 
                sizeof("Your favorite city");
-       hardware.h_lport.p_med_policy[LLDPMED_APPTYPE_SOFTPHONEVOICE-1].type =
-               LLDPMED_APPTYPE_SOFTPHONEVOICE;
-       hardware.h_lport.p_med_policy[LLDPMED_APPTYPE_SOFTPHONEVOICE-1].tagged =
+       hardware.h_lport.p_med_policy[LLDP_MED_APPTYPE_SOFTPHONEVOICE-1].type =
+               LLDP_MED_APPTYPE_SOFTPHONEVOICE;
+       hardware.h_lport.p_med_policy[LLDP_MED_APPTYPE_SOFTPHONEVOICE-1].tagged =
                1;
-       hardware.h_lport.p_med_policy[LLDPMED_APPTYPE_SOFTPHONEVOICE-1].vid =
+       hardware.h_lport.p_med_policy[LLDP_MED_APPTYPE_SOFTPHONEVOICE-1].vid =
                51;
-       hardware.h_lport.p_med_policy[LLDPMED_APPTYPE_SOFTPHONEVOICE-1].priority =
+       hardware.h_lport.p_med_policy[LLDP_MED_APPTYPE_SOFTPHONEVOICE-1].priority =
                6;
-       hardware.h_lport.p_med_policy[LLDPMED_APPTYPE_SOFTPHONEVOICE-1].dscp =
+       hardware.h_lport.p_med_policy[LLDP_MED_APPTYPE_SOFTPHONEVOICE-1].dscp =
                46;
-       hardware.h_lport.p_med_power.devicetype = LLDPMED_POW_TYPE_PSE;
-       hardware.h_lport.p_med_power.source = LLDPMED_POW_SOURCE_PRIMARY;
-       hardware.h_lport.p_med_power.priority = LLDPMED_POW_PRIO_HIGH;
+       hardware.h_lport.p_med_power.devicetype = LLDP_MED_POW_TYPE_PSE;
+       hardware.h_lport.p_med_power.source = LLDP_MED_POW_SOURCE_PRIMARY;
+       hardware.h_lport.p_med_power.priority = LLDP_MED_POW_PRIO_HIGH;
        hardware.h_lport.p_med_power.val = 65;
 
        /* Build packet */
index 61afb51eecdbed47b257bcc319e5769904a8b7ea..f6d14dd820ab6b1dee5a21aac6d8146954214608 100644 (file)
@@ -5,12 +5,16 @@
 
 #define MARSHAL_EXPORT
 #include "../src/marshal.h"
+#include "../src/log.h"
 
 /* This suite can be run in valgrind for memory leaks:
    CK_FORK=no valgrind -v --leak-check=yes ./tests/check_marshal
 */
 
 
+/* Use this callback to avoid some logs */
+void donothing(int pri, const char *msg) {};
+
 struct struct_simple {
        int a1;
        long a2;
@@ -393,6 +397,8 @@ START_TEST(test_too_small_unmarshal) {
        size_t len, len2;
        int i, j;
 
+       log_register(donothing);
+
        len = marshal_serialize(struct_nestedpointers, &source, &buffer);
        fail_unless(len > 0, "Unable to serialize");
        memset(&source_simple1, 0, sizeof(struct struct_simple));
@@ -411,6 +417,8 @@ START_TEST(test_too_small_unmarshal) {
        fail_unless(len2 == len, "Deserialized too much");
        free(destination->c3);
        free(destination->c4); free(destination); free(buffer);
+
+       log_register(NULL);
 }
 END_TEST
 
@@ -634,7 +642,7 @@ START_TEST(test_fixed_string) {
        struct struct_fixedstring source = {
                .s1 = 44444,
                .s2 = "String 2 Bla",
-               .s2_len = 8,
+               .s2_len = 8,    /* Not 12! */
                .s3 = "String 3",
        };
        struct struct_fixedstring *destination;
@@ -656,6 +664,7 @@ START_TEST(test_fixed_string) {
        ck_assert_int_eq(destination->s2[5], 'g');
        ck_assert_int_eq(destination->s2[6], ' ');
        ck_assert_int_eq(destination->s2[7], '2');
+       ck_assert_int_eq(destination->s2[8], '\0'); /* fixed string are null-terminated too */
        ck_assert_str_eq(destination->s3, "String 3");
        free(destination->s2); free(destination->s3);
        free(destination);
index 8d26d10ea03c7913a544ae6182a6645b4a01add6..db145a46abf4bacaec20a4b17d3f8f57460457c8 100644 (file)
@@ -1,7 +1,7 @@
 #include <check.h>
 
-#include "../src/lldpd.h"
-#include "../src/agent.h"
+#include "../src/daemon/lldpd.h"
+#include "../src/daemon/agent.h"
 
 #include <net-snmp/net-snmp-config.h>
 #include <net-snmp/net-snmp-includes.h>
@@ -39,10 +39,10 @@ struct lldpd_chassis chassis1 = {
        .c_cap_enabled   = LLDP_CAP_ROUTER,
        .c_ttl           = 60,
 #ifdef ENABLE_LLDPMED
-       .c_med_cap_available = LLDPMED_CAP_CAP | LLDPMED_CAP_IV | \
-               LLDPMED_CAP_LOCATION |  LLDPMED_CAP_POLICY | \
-               LLDPMED_CAP_MDI_PSE | LLDPMED_CAP_MDI_PD,
-       .c_med_type   = LLDPMED_CLASS_II,
+       .c_med_cap_available = LLDP_MED_CAP_CAP | LLDP_MED_CAP_IV | \
+               LLDP_MED_CAP_LOCATION | LLDP_MED_CAP_POLICY | \
+               LLDP_MED_CAP_MDI_PSE | LLDP_MED_CAP_MDI_PD,
+       .c_med_type   = LLDP_MED_CLASS_II,
        .c_med_hw     = "Hardware 1",
        /* We skip c_med_fw */
        .c_med_sw     = "Software 1",
@@ -130,18 +130,18 @@ struct lldpd_hardware hardware1 = {
                },
 #endif
 #ifdef ENABLE_LLDPMED
-               .p_med_cap_enabled = LLDPMED_CAP_CAP | LLDPMED_CAP_IV | LLDPMED_CAP_MDI_PD |
-                       LLDPMED_CAP_POLICY | LLDPMED_CAP_LOCATION,
+               .p_med_cap_enabled = LLDP_MED_CAP_CAP | LLDP_MED_CAP_IV | LLDP_MED_CAP_MDI_PD |
+                       LLDP_MED_CAP_POLICY | LLDP_MED_CAP_LOCATION,
                .p_med_policy = {
                        { .type = 0 }, { .type = 0 }, {
-                               .type = LLDPMED_APPTYPE_GUESTVOICE,
+                               .type = LLDP_MED_APPTYPE_GUESTVOICE,
                                .unknown = 1,
                                .tagged = 1,
                                .vid = 475,
                                .priority = 3,
                                .dscp = 62
                        }, { .type = 0 }, { .type = 0 }, { .type = 0 }, {
-                               .type = LLDPMED_APPTYPE_VIDEOSTREAM,
+                               .type = LLDP_MED_APPTYPE_VIDEOSTREAM,
                                .unknown = 0,
                                .tagged = 1,
                                .vid = 472,
@@ -151,16 +151,16 @@ struct lldpd_hardware hardware1 = {
                },
                .p_med_location = {
                        { .format = 0 }, {
-                               .format = LLDPMED_LOCFORMAT_CIVIC,
+                               .format = LLDP_MED_LOCFORMAT_CIVIC,
                                /* 2:FR:6:Commercial Rd:19:4 */
                                .data = "\x15\x02FR\x06\x0dCommercial Rd\x13\x014",
                                .data_len = 22,
                        }, { .format = 0 }
                },
                .p_med_power = {
-                       .devicetype = LLDPMED_POW_TYPE_PD,
-                       .source = LLDPMED_POW_SOURCE_LOCAL,
-                       .priority = LLDPMED_POW_PRIO_HIGH,
+                       .devicetype = LLDP_MED_POW_TYPE_PD,
+                       .source = LLDP_MED_POW_SOURCE_LOCAL,
+                       .priority = LLDP_MED_POW_PRIO_HIGH,
                        .val = 100
                },
 #endif         
@@ -197,18 +197,18 @@ struct lldpd_hardware hardware2 = {
                },
 #endif
 #ifdef ENABLE_LLDPMED
-               .p_med_cap_enabled = LLDPMED_CAP_CAP | LLDPMED_CAP_IV | LLDPMED_CAP_MDI_PD |
-                       LLDPMED_CAP_MDI_PSE | LLDPMED_CAP_POLICY | LLDPMED_CAP_LOCATION,
+               .p_med_cap_enabled = LLDP_MED_CAP_CAP | LLDP_MED_CAP_IV | LLDP_MED_CAP_MDI_PD |
+                       LLDP_MED_CAP_MDI_PSE | LLDP_MED_CAP_POLICY | LLDP_MED_CAP_LOCATION,
                .p_med_policy = {
                        { .type = 0 }, { .type = 0 }, {
-                               .type = LLDPMED_APPTYPE_GUESTVOICE,
+                               .type = LLDP_MED_APPTYPE_GUESTVOICE,
                                .unknown = 1,
                                .tagged = 1,
                                .vid = 475,
                                .priority = 3,
                                .dscp = 62
                        }, { .type = 0 }, { .type = 0 }, {
-                               .type = LLDPMED_APPTYPE_VIDEOCONFERENCE,
+                               .type = LLDP_MED_APPTYPE_VIDEOCONFERENCE,
                                .unknown = 0,
                                .tagged = 0,
                                .vid = 1007,
@@ -218,7 +218,7 @@ struct lldpd_hardware hardware2 = {
                },
                .p_med_location = {
                        {
-                               .format = LLDPMED_LOCFORMAT_COORD,
+                               .format = LLDP_MED_LOCFORMAT_COORD,
                                .data = "Not interpreted",
                                .data_len = 15,
                        }, { .format = 0 }, { .format = 0 },
@@ -241,11 +241,11 @@ struct lldpd_vlan vlan1449 = {
        .v_vid = 1449,
 };
 struct lldpd_ppvid ppvid47 = {
-       .p_cap_status = LLDPD_PPVID_CAP_SUPPORTED | LLDPD_PPVID_CAP_ENABLED,
+       .p_cap_status = LLDP_PPVID_CAP_SUPPORTED | LLDP_PPVID_CAP_ENABLED,
        .p_ppvid = 47,
 };
 struct lldpd_ppvid ppvid118 = {
-       .p_cap_status = LLDPD_PPVID_CAP_SUPPORTED | LLDPD_PPVID_CAP_ENABLED,
+       .p_cap_status = LLDP_PPVID_CAP_SUPPORTED | LLDP_PPVID_CAP_ENABLED,
        .p_ppvid = 118,
 };
 struct lldpd_pi pi88cc = {
index 72ae31319eca8ae22f2867eebaefebd066903cbd..d82ae05e9bf6182679cb8b5d45b6f440c520ae43 100644 (file)
@@ -4,7 +4,6 @@
 #include <arpa/inet.h>
 #include <netinet/in.h>
 #include <check.h>
-#include "../src/lldpd.h"
 #include "common.h"
 
 char filenameprefix[] = "sonmp_send";
index 4ca7cb2a0456015f45b017f2f729b971190a40fe..f7e3d5454eabdbedf2bd4ae055d8b938ed66eac1 100644 (file)
@@ -7,7 +7,6 @@
 #include <time.h>
 #include <fcntl.h>
 #include <check.h>
-#include "../src/lldpd.h"
 #include "common.h"
 
 int dump = -1;
index 48cbf25119aa3c0190517c54132e54c9920c5f49..179aee6a7bb3faa588badc351c9f738574accc3f 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef _COMMON_H
 #define _COMMON_H
 
-#include "../src/lldpd.h"
+#include "../src/daemon/lldpd.h"
 
 /* See:
  * http://wiki.wireshark.org/Development/LibpcapFileFormat