From 4b292b556534baf26cf741626dff95a99781fe5f Mon Sep 17 00:00:00 2001 From: Vincent Bernat Date: Tue, 14 Aug 2012 17:06:23 +0200 Subject: [PATCH] Separate daemon and client code. Provide a client library. 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. --- .gitignore | 5 + Makefile.am | 9 +- NEWS | 7 + README.md | 42 +- configure.ac | 19 +- doxygen.am | 186 ++ doxygen.cfg | 305 ++++ m4/ax_prog_doxygen.m4 | 532 ++++++ man/lldpctl.8 | 72 +- src/Makefile.am | 42 +- src/agent.h | 28 - src/client/Makefile.am | 10 + src/client/actions.c | 374 +++++ src/{lldpctl.h => client/client.h} | 22 +- src/client/display.c | 589 +++++++ src/{ => client}/kv_writer.c | 3 +- src/client/lldpctl.c | 121 ++ src/{ => client}/text_writer.c | 3 +- src/{ => client}/writer.h | 3 +- src/{ => client}/xml_writer.c | 4 +- src/compat/Makefile.am | 4 + src/{ => compat}/compat.h | 8 + src/{ => compat}/getifaddrs.c | 3 +- src/{ => compat}/malloc.c | 1 + src/{ => compat}/realloc.c | 1 + src/{ => compat}/strlcpy.c | 1 + src/ctl.c | 242 ++- src/ctl.h | 47 + src/daemon/Makefile.am | 28 + src/{ => daemon}/agent.c | 49 +- src/daemon/agent.h | 29 + src/{ => daemon}/agent_priv.c | 1 + src/{ => daemon}/cdp.c | 3 +- src/{ => daemon}/cdp.h | 1 + src/{ => daemon}/client.c | 29 +- src/{ => daemon}/dmi.c | 1 + src/{ => daemon}/edp.c | 3 +- src/{ => daemon}/edp.h | 1 + src/{ => daemon}/event.c | 1 + src/{ => daemon}/frame.c | 1 + src/{ => daemon}/frame.h | 1 + src/{ => daemon}/interfaces.c | 5 +- src/daemon/lldp-tlv.h | 67 + src/{ => daemon}/lldp.c | 71 +- src/{ => daemon}/lldpd.c | 149 +- src/daemon/lldpd.h | 257 +++ src/{ => daemon}/main.c | 1 + src/{ => daemon}/priv.c | 1 + src/{ => daemon}/privsep_fdpass.c | 1 + src/{ => daemon}/sonmp.c | 3 +- src/{ => daemon}/sonmp.h | 1 + src/display.c | 1015 ----------- src/lib/Makefile.am | 12 + src/lib/atom-private.c | 2520 ++++++++++++++++++++++++++++ src/lib/atom.c | 386 +++++ src/lib/connection.c | 215 +++ src/lib/errors.c | 55 + src/lib/lldpctl.h | 720 ++++++++ src/lib/lldpctl.pc.in | 10 + src/lib/private.h | 231 +++ src/lldp-const.h | 218 +++ src/lldp.h | 248 --- src/lldpctl.c | 773 --------- src/lldpd-structs.c | 149 ++ src/{lldpd.h => lldpd-structs.h} | 295 +--- src/log.c | 41 +- src/log.h | 36 + src/marshal.c | 29 +- src/marshal.h | 4 + tests/Makefile.am | 18 +- tests/check_cdp.c | 1 - tests/check_edp.c | 1 - tests/check_ifaddrs.c | 2 +- tests/check_lldp.c | 69 +- tests/check_marshal.c | 11 +- tests/check_snmp.c | 42 +- tests/check_sonmp.c | 1 - tests/common.c | 1 - tests/common.h | 2 +- 79 files changed, 7713 insertions(+), 2779 deletions(-) create mode 100644 doxygen.am create mode 100644 doxygen.cfg create mode 100644 m4/ax_prog_doxygen.m4 delete mode 100644 src/agent.h create mode 100644 src/client/Makefile.am create mode 100644 src/client/actions.c rename src/{lldpctl.h => client/client.h} (69%) create mode 100644 src/client/display.c rename src/{ => client}/kv_writer.c (97%) create mode 100644 src/client/lldpctl.c rename src/{ => client}/text_writer.c (97%) rename src/{ => client}/writer.h (91%) rename src/{ => client}/xml_writer.c (97%) create mode 100644 src/compat/Makefile.am rename src/{ => compat}/compat.h (97%) rename src/{ => compat}/getifaddrs.c (99%) rename src/{ => compat}/malloc.c (85%) rename src/{ => compat}/realloc.c (88%) rename src/{ => compat}/strlcpy.c (97%) create mode 100644 src/ctl.h create mode 100644 src/daemon/Makefile.am rename src/{ => daemon}/agent.c (98%) create mode 100644 src/daemon/agent.h rename src/{ => daemon}/agent_priv.c (99%) rename src/{ => daemon}/cdp.c (99%) rename src/{ => daemon}/cdp.h (97%) rename src/{ => daemon}/client.c (87%) rename src/{ => daemon}/dmi.c (97%) rename src/{ => daemon}/edp.c (99%) rename src/{ => daemon}/edp.h (96%) rename src/{ => daemon}/event.c (99%) rename src/{ => daemon}/frame.c (96%) rename src/{ => daemon}/frame.h (98%) rename src/{ => daemon}/interfaces.c (99%) create mode 100644 src/daemon/lldp-tlv.h rename src/{ => daemon}/lldp.c (94%) rename src/{ => daemon}/lldpd.c (90%) create mode 100644 src/daemon/lldpd.h rename src/{ => daemon}/main.c (65%) rename src/{ => daemon}/priv.c (99%) rename src/{ => daemon}/privsep_fdpass.c (98%) rename src/{ => daemon}/sonmp.c (99%) rename src/{ => daemon}/sonmp.h (96%) delete mode 100644 src/display.c create mode 100644 src/lib/Makefile.am create mode 100644 src/lib/atom-private.c create mode 100644 src/lib/atom.c create mode 100644 src/lib/connection.c create mode 100644 src/lib/errors.c create mode 100644 src/lib/lldpctl.h create mode 100644 src/lib/lldpctl.pc.in create mode 100644 src/lib/private.h create mode 100644 src/lldp-const.h delete mode 100644 src/lldp.h delete mode 100644 src/lldpctl.c create mode 100644 src/lldpd-structs.c rename src/{lldpd.h => lldpd-structs.h} (53%) create mode 100644 src/log.h diff --git a/.gitignore b/.gitignore index ae8f0de4..3e5acad7 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,11 @@ # 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 diff --git a/Makefile.am b/Makefile.am index 09703913..7931805c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 e26b5a17..98eca5f4 100644 --- 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. diff --git a/README.md b/README.md index c203d300..152a47e3 100644 --- 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 diff --git a/configure.ac b/configure.ac index 138a9de9..1749ee8e 100644 --- a/configure.ac +++ b/configure.ac @@ -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 index 00000000..050ff6b0 --- /dev/null +++ b/doxygen.am @@ -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 index 00000000..56141266 --- /dev/null +++ b/doxygen.cfg @@ -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 index 00000000..44b22b00 --- /dev/null +++ b/m4/ax_prog_doxygen.m4 @@ -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 +# +# 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 +]) diff --git a/man/lldpctl.8 b/man/lldpctl.8 index 18191fea..941bdc3c 100644 --- a/man/lldpctl.8 +++ b/man/lldpctl.8 @@ -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 diff --git a/src/Makefile.am b/src/Makefile.am index 5107435a..c924341f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 index 8a9f1d24..00000000 --- a/src/agent.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2008 Vincent Bernat - * - * 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 -#include -#include -#include - -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 index 00000000..896dccd0 --- /dev/null +++ b/src/client/Makefile.am @@ -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 index 00000000..da903c59 --- /dev/null +++ b/src/client/actions.c @@ -0,0 +1,374 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2012 Vincent Bernat + * + * 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 +#include + +#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); +} diff --git a/src/lldpctl.h b/src/client/client.h similarity index 69% rename from src/lldpctl.h rename to src/client/client.h index c66a877f..61f55f30 100644 --- a/src/lldpctl.h +++ b/src/client/client.h @@ -1,3 +1,4 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ /* * Copyright (c) 2012 Vincent Bernat * @@ -14,18 +15,23 @@ * 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 +#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 index 00000000..524f57b4 --- /dev/null +++ b/src/client/display.c @@ -0,0 +1,589 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2008 Vincent Bernat + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); +} diff --git a/src/kv_writer.c b/src/client/kv_writer.c similarity index 97% rename from src/kv_writer.c rename to src/client/kv_writer.c index ab7a87cd..3897caaf 100644 --- a/src/kv_writer.c +++ b/src/client/kv_writer.c @@ -1,3 +1,4 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ /* * Copyright (c) 2010 Andreas Hofmeister * 2010 Vincent Bernat @@ -20,7 +21,7 @@ #include #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 index 00000000..ae7a4aa7 --- /dev/null +++ b/src/client/lldpctl.c @@ -0,0 +1,121 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2008 Vincent Bernat + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/src/text_writer.c b/src/client/text_writer.c similarity index 97% rename from src/text_writer.c rename to src/client/text_writer.c index b76cdcb5..017dca20 100644 --- a/src/text_writer.c +++ b/src/client/text_writer.c @@ -1,3 +1,4 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ /* * Copyright (c) 2010 Andreas Hofmeister * @@ -19,7 +20,7 @@ #include #include "writer.h" -#include "lldpd.h" +#include "../log.h" static char sep[] = "-------------------------------------------------------------------------------"; diff --git a/src/writer.h b/src/client/writer.h similarity index 91% rename from src/writer.h rename to src/client/writer.h index b2479c35..cedaee10 100644 --- a/src/writer.h +++ b/src/client/writer.h @@ -1,3 +1,4 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ /* * Copyright (c) 2010 Andreas Hofmeister * @@ -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 * ); diff --git a/src/xml_writer.c b/src/client/xml_writer.c similarity index 97% rename from src/xml_writer.c rename to src/client/xml_writer.c index de404fba..9192fd70 100644 --- a/src/xml_writer.c +++ b/src/client/xml_writer.c @@ -1,3 +1,4 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ /* * Copyright (c) 2010 Andreas Hofmeister * @@ -22,7 +23,7 @@ #include #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 index 00000000..6bf15b99 --- /dev/null +++ b/src/compat/Makefile.am @@ -0,0 +1,4 @@ +noinst_LTLIBRARIES = libcompat.la + +libcompat_la_SOURCES = compat.h +libcompat_la_LIBADD = @LTLIBOBJS@ diff --git a/src/compat.h b/src/compat/compat.h similarity index 97% rename from src/compat.h rename to src/compat/compat.h index 6da5615c..957f3992 100644 --- a/src/compat.h +++ b/src/compat/compat.h @@ -1,3 +1,4 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ /* * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. @@ -29,6 +30,11 @@ * @(#)queue.h 8.5 (Berkeley) 8/20/94 */ +#ifndef _COMPAT_H +#define _COMPAT_H + +#include + #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 diff --git a/src/getifaddrs.c b/src/compat/getifaddrs.c similarity index 99% rename from src/getifaddrs.c rename to src/compat/getifaddrs.c index 902078ec..b2fea1eb 100644 --- a/src/getifaddrs.c +++ b/src/compat/getifaddrs.c @@ -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 diff --git a/src/malloc.c b/src/compat/malloc.c similarity index 85% rename from src/malloc.c rename to src/compat/malloc.c index 8ff490aa..5b97294d 100644 --- a/src/malloc.c +++ b/src/compat/malloc.c @@ -1,3 +1,4 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ /* malloc replacement that can allocate 0 byte */ #undef malloc diff --git a/src/realloc.c b/src/compat/realloc.c similarity index 88% rename from src/realloc.c rename to src/compat/realloc.c index 8a89d4b3..c6aa351c 100644 --- a/src/realloc.c +++ b/src/compat/realloc.c @@ -1,3 +1,4 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ /* realloc replacement that can reallocate 0 byte or NULL pointers*/ #undef realloc diff --git a/src/strlcpy.c b/src/compat/strlcpy.c similarity index 97% rename from src/strlcpy.c rename to src/compat/strlcpy.c index c30566a7..f5f135af 100644 --- a/src/strlcpy.c +++ b/src/compat/strlcpy.c @@ -1,3 +1,4 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ /* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $ */ /* diff --git a/src/ctl.c b/src/ctl.c index f7f0ddae..c37f6d01 100644 --- a/src/ctl.c +++ b/src/ctl.c @@ -1,3 +1,4 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ /* * Copyright (c) 2008 Vincent Bernat * @@ -14,8 +15,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include "lldpd.h" - +#include #include #include #include @@ -23,6 +23,19 @@ #include #include +#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 index 00000000..6f8b398e --- /dev/null +++ b/src/ctl.h @@ -0,0 +1,47 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2012 Vincent Bernat + * + * 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 +#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 index 00000000..943297c6 --- /dev/null +++ b/src/daemon/Makefile.am @@ -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 diff --git a/src/agent.c b/src/daemon/agent.c similarity index 98% rename from src/agent.c rename to src/daemon/agent.c index ed325f57..7acbe699 100644 --- a/src/agent.c +++ b/src/daemon/agent.c @@ -1,3 +1,4 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ /* * Copyright (c) 2008 Vincent Bernat * @@ -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 index 00000000..74479a31 --- /dev/null +++ b/src/daemon/agent.h @@ -0,0 +1,29 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* +* Copyright (c) 2008 Vincent Bernat +* +* 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 +#include +#include +#include + +static oid lldp_oid[] = {1, 0, 8802, 1, 1, 2}; +size_t agent_lldp_vars_size(void); + +#endif diff --git a/src/agent_priv.c b/src/daemon/agent_priv.c similarity index 99% rename from src/agent_priv.c rename to src/daemon/agent_priv.c index ab6b065a..2b8cbc95 100644 --- a/src/agent_priv.c +++ b/src/daemon/agent_priv.c @@ -1,3 +1,4 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ /* * Copyright (c) 2008 Vincent Bernat * diff --git a/src/cdp.c b/src/daemon/cdp.c similarity index 99% rename from src/cdp.c rename to src/daemon/cdp.c index 8d168c39..a433c68c 100644 --- a/src/cdp.c +++ b/src/daemon/cdp.c @@ -1,3 +1,4 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ /* * Copyright (c) 2008 Vincent Bernat * @@ -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; } diff --git a/src/cdp.h b/src/daemon/cdp.h similarity index 97% rename from src/cdp.h rename to src/daemon/cdp.h index bae7f5e2..a84601ab 100644 --- a/src/cdp.h +++ b/src/daemon/cdp.h @@ -1,3 +1,4 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ /* * Copyright (c) 2008 Vincent Bernat * diff --git a/src/client.c b/src/daemon/client.c similarity index 87% rename from src/client.c rename to src/daemon/client.c index 9b602e63..909b3801 100644 --- a/src/client.c +++ b/src/daemon/client.c @@ -1,3 +1,4 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ /* * Copyright (c) 2008 Vincent Bernat * @@ -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); diff --git a/src/dmi.c b/src/daemon/dmi.c similarity index 97% rename from src/dmi.c rename to src/daemon/dmi.c index 14aa895b..ed364e0a 100644 --- a/src/dmi.c +++ b/src/daemon/dmi.c @@ -1,3 +1,4 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ /* * Copyright (c) 2009 Vincent Bernat * diff --git a/src/edp.c b/src/daemon/edp.c similarity index 99% rename from src/edp.c rename to src/daemon/edp.c index b1c8a98b..a3a23b25 100644 --- a/src/edp.c +++ b/src/daemon/edp.c @@ -1,3 +1,4 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ /* * Copyright (c) 2008 Vincent Bernat * @@ -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; } diff --git a/src/edp.h b/src/daemon/edp.h similarity index 96% rename from src/edp.h rename to src/daemon/edp.h index ffa92ddf..e3cb7382 100644 --- a/src/edp.h +++ b/src/daemon/edp.h @@ -1,3 +1,4 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ /* * Copyright (c) 2008 Vincent Bernat * diff --git a/src/event.c b/src/daemon/event.c similarity index 99% rename from src/event.c rename to src/daemon/event.c index 376b1e2a..9d0d8005 100644 --- a/src/event.c +++ b/src/daemon/event.c @@ -1,3 +1,4 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ /* * Copyright (c) 2012 Vincent Bernat * diff --git a/src/frame.c b/src/daemon/frame.c similarity index 96% rename from src/frame.c rename to src/daemon/frame.c index d091e596..e1093ad3 100644 --- a/src/frame.c +++ b/src/daemon/frame.c @@ -1,3 +1,4 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ /* * Copyright (c) 2009 Vincent Bernat * diff --git a/src/frame.h b/src/daemon/frame.h similarity index 98% rename from src/frame.h rename to src/daemon/frame.h index 81249cc3..7bb1ea6f 100644 --- a/src/frame.h +++ b/src/daemon/frame.h @@ -1,3 +1,4 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ /* * Copyright (c) 2009 Vincent Bernat * diff --git a/src/interfaces.c b/src/daemon/interfaces.c similarity index 99% rename from src/interfaces.c rename to src/daemon/interfaces.c index a99125dd..0a1c8333 100644 --- a/src/interfaces.c +++ b/src/daemon/interfaces.c @@ -1,3 +1,4 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ /* * Copyright (c) 2008 Vincent Bernat * @@ -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 index 00000000..ab24486f --- /dev/null +++ b/src/daemon/lldp-tlv.h @@ -0,0 +1,67 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2012 Vincent Bernat + * + * 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 diff --git a/src/lldp.c b/src/daemon/lldp.c similarity index 94% rename from src/lldp.c rename to src/daemon/lldp.c index c58f0d85..34c38541 100644 --- a/src/lldp.c +++ b/src/daemon/lldp.c @@ -1,3 +1,4 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ /* * Copyright (c) 2008 Vincent Bernat * @@ -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; } diff --git a/src/lldpd.c b/src/daemon/lldpd.c similarity index 90% rename from src/lldpd.c rename to src/daemon/lldpd.c index 1b75738a..8ce719c2 100644 --- a/src/lldpd.c +++ b/src/daemon/lldpd.c @@ -1,3 +1,4 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ /* * Copyright (c) 2008 Vincent Bernat * @@ -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 index 00000000..25135a52 --- /dev/null +++ b/src/daemon/lldpd.h @@ -0,0 +1,257 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2008 Vincent Bernat + * + * 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 +#endif + +#ifdef HAVE_VALGRIND_VALGRIND_H +# include +#else +# define RUNNING_ON_VALGRIND 0 +#endif + +#define _GNU_SOURCE 1 +#include +#include +#include +#include +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifndef INCLUDE_LINUX_IF_H +# include +#else +# include +# include +#endif +#if HAVE_GETIFADDRS +# include +#endif +#include +#include +#include +#include + +#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 */ diff --git a/src/main.c b/src/daemon/main.c similarity index 65% rename from src/main.c rename to src/daemon/main.c index 5670e9fa..e23fe45f 100644 --- a/src/main.c +++ b/src/daemon/main.c @@ -1,3 +1,4 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ #include "lldpd.h" int diff --git a/src/priv.c b/src/daemon/priv.c similarity index 99% rename from src/priv.c rename to src/daemon/priv.c index bbd6ee00..000c5769 100644 --- a/src/priv.c +++ b/src/daemon/priv.c @@ -1,3 +1,4 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ /* * Copyright (c) 2008 Vincent Bernat * diff --git a/src/privsep_fdpass.c b/src/daemon/privsep_fdpass.c similarity index 98% rename from src/privsep_fdpass.c rename to src/daemon/privsep_fdpass.c index 6b004806..5f19d671 100644 --- a/src/privsep_fdpass.c +++ b/src/daemon/privsep_fdpass.c @@ -1,3 +1,4 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ /* * Copyright 2001 Niels Provos * All rights reserved. diff --git a/src/sonmp.c b/src/daemon/sonmp.c similarity index 99% rename from src/sonmp.c rename to src/daemon/sonmp.c index ba905d93..541c682c 100644 --- a/src/sonmp.c +++ b/src/daemon/sonmp.c @@ -1,3 +1,4 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ /* * Copyright (c) 2008 Vincent Bernat * @@ -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; } diff --git a/src/sonmp.h b/src/daemon/sonmp.h similarity index 96% rename from src/sonmp.h rename to src/daemon/sonmp.h index 68502dc7..d2b1541f 100644 --- a/src/sonmp.h +++ b/src/daemon/sonmp.h @@ -1,3 +1,4 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ /* * Copyright (c) 2008 Vincent Bernat * diff --git a/src/display.c b/src/display.c deleted file mode 100644 index 892ebedb..00000000 --- a/src/display.c +++ /dev/null @@ -1,1015 +0,0 @@ -/* - * Copyright (c) 2008 Vincent Bernat - * - * 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 -#include -#include -#include -#include -#include -#include -#include - -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 index 00000000..725c0e7a --- /dev/null +++ b/src/lib/Makefile.am @@ -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 index 00000000..190512f9 --- /dev/null +++ b/src/lib/atom-private.c @@ -0,0 +1,2520 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2012 Vincent Bernat + * + * 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 +#include +#include +#include + +#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 index 00000000..6e008e23 --- /dev/null +++ b/src/lib/atom.c @@ -0,0 +1,386 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2012 Vincent Bernat + * + * 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 +#include +#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 index 00000000..85a11ca1 --- /dev/null +++ b/src/lib/connection.c @@ -0,0 +1,215 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2012 Vincent Bernat + * + * 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 +#include +#include +#include + +#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 index 00000000..140630b6 --- /dev/null +++ b/src/lib/errors.c @@ -0,0 +1,55 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2012 Vincent Bernat + * + * 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 index 00000000..823a5fdc --- /dev/null +++ b/src/lib/lldpctl.h @@ -0,0 +1,720 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2012 Vincent Bernat + * + * 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 +#include +#include + +/** + * 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 index 00000000..c8500ece --- /dev/null +++ b/src/lib/lldpctl.pc.in @@ -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 index 00000000..755c8705 --- /dev/null +++ b/src/lib/private.h @@ -0,0 +1,231 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2012 Vincent Bernat + * + * 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 +#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 index 00000000..533948ea --- /dev/null +++ b/src/lldp-const.h @@ -0,0 +1,218 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2008 Vincent Bernat + * + * 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 index 738b4ceb..00000000 --- a/src/lldp.h +++ /dev/null @@ -1,248 +0,0 @@ -/* - * Copyright (c) 2008 Vincent Bernat - * - * 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 index 436ac2e8..00000000 --- a/src/lldpctl.c +++ /dev/null @@ -1,773 +0,0 @@ -/* - * Copyright (c) 2008 Vincent Bernat - * - * 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 -#include -#include -#include -#include -#include -#include -#include - -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 index 00000000..8e186c43 --- /dev/null +++ b/src/lldpd-structs.c @@ -0,0 +1,149 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2008 Vincent Bernat + * + * 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 +#include +#include +#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; + } + } +} diff --git a/src/lldpd.h b/src/lldpd-structs.h similarity index 53% rename from src/lldpd.h rename to src/lldpd-structs.h index 431001b8..a0dfc644 100644 --- a/src/lldpd.h +++ b/src/lldpd-structs.h @@ -1,3 +1,4 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ /* * Copyright (c) 2008 Vincent Bernat * @@ -14,75 +15,30 @@ * 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 #endif -#ifdef HAVE_VALGRIND_VALGRIND_H -# include -#else -# define RUNNING_ON_VALGRIND 0 -#endif - -#define _GNU_SOURCE 1 -#include -#include -#include -#include -#ifdef HAVE_SYS_TYPES_H -# include -#endif +#include +#include #ifndef INCLUDE_LINUX_IF_H # include #else # include # include #endif -#if HAVE_GETIFADDRS -# include -#endif #include #include -#include -#include +#include -#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 */ diff --git a/src/log.c b/src/log.c index 2b110f43..02a094a4 100644 --- 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 $ */ /* @@ -16,17 +17,21 @@ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include "lldpd.h" - -#include - -#include -#include +#define _GNU_SOURCE #include +#include +#include #include +#include +#include +#include #include -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 index 00000000..957f5349 --- /dev/null +++ b/src/log.h @@ -0,0 +1,36 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2012 Vincent Bernat + * + * 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 diff --git a/src/marshal.c b/src/marshal.c index 09ffa490..993d6543 100644 --- a/src/marshal.c +++ b/src/marshal.c @@ -1,3 +1,4 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ /* * Copyright (c) 2012 Vincent Bernat * @@ -15,7 +16,18 @@ */ #define MARSHAL_EXPORT -#include "lldpd.h" +#include "marshal.h" + +#include +#include +#include +#include +#include + +#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; diff --git a/src/marshal.h b/src/marshal.h index a72272f8..4bdf39b4 100644 --- a/src/marshal.h +++ b/src/marshal.h @@ -1,3 +1,4 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ /* * Copyright (c) 2012 Vincent Bernat * @@ -17,6 +18,9 @@ #ifndef _MARSHAL_H #define _MARSHAL_H +#include +#include + struct marshal_info; enum marshal_subinfo_kind { pointer, diff --git a/tests/Makefile.am b/tests/Makefile.am index 3f37cb55..2fb3565b 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -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) diff --git a/tests/check_cdp.c b/tests/check_cdp.c index 7ad844f0..a5b25092 100644 --- a/tests/check_cdp.c +++ b/tests/check_cdp.c @@ -4,7 +4,6 @@ #include #include #include -#include "../src/lldpd.h" #include "common.h" char filenameprefix[] = "cdp_send"; diff --git a/tests/check_edp.c b/tests/check_edp.c index 3a6b627f..9dd7c7d2 100644 --- a/tests/check_edp.c +++ b/tests/check_edp.c @@ -4,7 +4,6 @@ #include #include #include -#include "../src/lldpd.h" #include "common.h" char filenameprefix[] = "edp_send"; diff --git a/tests/check_ifaddrs.c b/tests/check_ifaddrs.c index 47f6b841..fba6275b 100644 --- a/tests/check_ifaddrs.c +++ b/tests/check_ifaddrs.c @@ -1,7 +1,7 @@ #include #include #include -#include "../src/lldpd.h" +#include "common.h" #define DUMP "ifdump.txt" diff --git a/tests/check_lldp.c b/tests/check_lldp.c index 87e6ba44..5cebdaa5 100644 --- a/tests/check_lldp.c +++ b/tests/check_lldp.c @@ -4,7 +4,6 @@ #include #include #include -#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 */ diff --git a/tests/check_marshal.c b/tests/check_marshal.c index 61afb51e..f6d14dd8 100644 --- a/tests/check_marshal.c +++ b/tests/check_marshal.c @@ -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); diff --git a/tests/check_snmp.c b/tests/check_snmp.c index 8d26d10e..db145a46 100644 --- a/tests/check_snmp.c +++ b/tests/check_snmp.c @@ -1,7 +1,7 @@ #include -#include "../src/lldpd.h" -#include "../src/agent.h" +#include "../src/daemon/lldpd.h" +#include "../src/daemon/agent.h" #include #include @@ -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 = { diff --git a/tests/check_sonmp.c b/tests/check_sonmp.c index 72ae3131..d82ae05e 100644 --- a/tests/check_sonmp.c +++ b/tests/check_sonmp.c @@ -4,7 +4,6 @@ #include #include #include -#include "../src/lldpd.h" #include "common.h" char filenameprefix[] = "sonmp_send"; diff --git a/tests/common.c b/tests/common.c index 4ca7cb2a..f7e3d545 100644 --- a/tests/common.c +++ b/tests/common.c @@ -7,7 +7,6 @@ #include #include #include -#include "../src/lldpd.h" #include "common.h" int dump = -1; diff --git a/tests/common.h b/tests/common.h index 48cbf251..179aee6a 100644 --- a/tests/common.h +++ b/tests/common.h @@ -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 -- 2.39.2