diff options
Diffstat (limited to 'src/lib')
-rw-r--r-- | src/lib/Makefile.am | 75 | ||||
-rw-r--r-- | src/lib/Makefile.in | 978 | ||||
-rw-r--r-- | src/lib/atom.c | 655 | ||||
-rw-r--r-- | src/lib/atom.h | 344 | ||||
-rw-r--r-- | src/lib/atoms/chassis.c | 326 | ||||
-rw-r--r-- | src/lib/atoms/config.c | 338 | ||||
-rw-r--r-- | src/lib/atoms/custom.c | 226 | ||||
-rw-r--r-- | src/lib/atoms/dot1.c | 250 | ||||
-rw-r--r-- | src/lib/atoms/dot3.c | 474 | ||||
-rw-r--r-- | src/lib/atoms/interface.c | 118 | ||||
-rw-r--r-- | src/lib/atoms/med.c | 1123 | ||||
-rw-r--r-- | src/lib/atoms/mgmt.c | 149 | ||||
-rw-r--r-- | src/lib/atoms/port.c | 862 | ||||
-rw-r--r-- | src/lib/connection.c | 306 | ||||
-rw-r--r-- | src/lib/errors.c | 75 | ||||
-rw-r--r-- | src/lib/fixedpoint.c | 255 | ||||
-rw-r--r-- | src/lib/fixedpoint.h | 42 | ||||
-rw-r--r-- | src/lib/helpers.c | 78 | ||||
-rw-r--r-- | src/lib/helpers.h | 24 | ||||
-rw-r--r-- | src/lib/lldpctl.h | 1186 | ||||
-rw-r--r-- | src/lib/lldpctl.map | 53 | ||||
-rw-r--r-- | src/lib/lldpctl.pc.in | 6 |
22 files changed, 7943 insertions, 0 deletions
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am new file mode 100644 index 0000000..c1a6d81 --- /dev/null +++ b/src/lib/Makefile.am @@ -0,0 +1,75 @@ +AM_CFLAGS = -I $(top_srcdir)/include $(LLDP_CFLAGS) +AM_CPPFLAGS = $(LLDP_CPPFLAGS) +AM_LDFLAGS = $(LLDP_LDFLAGS) + +lib_LTLIBRARIES = liblldpctl.la +include_HEADERS = lldpctl.h + +noinst_LTLIBRARIES = libfixedpoint.la +libfixedpoint_la_SOURCES = fixedpoint.h fixedpoint.c + +ATOM_FILES = \ + atoms/config.c atoms/dot1.c atoms/dot3.c \ + atoms/interface.c atoms/med.c atoms/mgmt.c atoms/port.c \ + atoms/custom.c atoms/chassis.c +liblldpctl_la_SOURCES = \ + lldpctl.h atom.h helpers.h \ + errors.c connection.c atom.c helpers.c \ + $(ATOM_FILES) +nodist_liblldpctl_la_SOURCES = atom-glue.c +liblldpctl_la_LIBADD = $(top_builddir)/src/libcommon-daemon-lib.la libfixedpoint.la + +atom-glue.c: $(ATOM_FILES) Makefile + $(AM_V_GEN)(for f in $(ATOM_FILES:%=$(srcdir)/%); do \ + $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) $$f; done | \ + $(SED) -n 's+^void init_atom_builder_\([^(]*\)(void).*, \([0-9]*\)).*+\2 \1+p' | \ + sort | \ + $(AWK) '{ atoms[$$2] = 1 } \ + END { for (atom in atoms) { print "void init_atom_builder_"atom"(void);" } \ + print "void init_atom_builder(void);"; \ + print "void init_atom_builder(void) {"; \ + print " static int init = 0; if (init) return; init++;"; \ + for (atom in atoms) { print " init_atom_builder_"atom"();" } \ + print "}"; }' && \ + for f in $(ATOM_FILES:%=$(srcdir)/%); do \ + $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) $$f; done | \ + $(SED) -n 's+^void init_atom_map_\([^(]*\)(void).*, \([0-9]*\)).*+\2 \1+p' | \ + sort -n | \ + $(AWK) '{ atoms[$$2] = 1 } \ + END { for (atom in atoms) { print "void init_atom_map_"atom"(void);" } \ + print "void init_atom_map(void);"; \ + print "void init_atom_map(void) {"; \ + print " static int init = 0; if (init) return; init++;"; \ + for (atom in atoms) { print " init_atom_map_"atom"();" } \ + print "}"; }' ) \ + > $@.tmp + $(AM_V_at)$(GREP) -q init_atom_builder_ $@.tmp + $(AM_V_at)$(GREP) -q init_atom_map_ $@.tmp + $(AM_V_at)mv $@.tmp $@ +CLEANFILES = atom-glue.c + +# -version-info format is `current`:`revision`:`age`. For more details, see: +# https://www.sourceware.org/autobook/autobook/autobook_61.html#Library-Versioning +# +# -version-number could be computed from -version-info, mostly major +# is `current` - `age`, minor is `age` and revision is `revision' and +# major.minor should be used when updating lldpctl.map. +liblldpctl_la_LDFLAGS = $(AM_LDFLAGS) -version-info 13:1:9 +liblldpctl_la_DEPENDENCIES = libfixedpoint.la + +if HAVE_LD_VERSION_SCRIPT +liblldpctl_la_DEPENDENCIES += lldpctl.map +liblldpctl_la_LDFLAGS += -Wl,--version-script=$(srcdir)/lldpctl.map +else +liblldpctl_la_LDFLAGS += -export-symbols-regex '^lldpctl_' +endif + +pkgconfig_DATA = lldpctl.pc + +TEMPLATES = lldpctl.pc +EXTRA_DIST = lldpctl.pc.in lldpctl.map +CLEANFILES += $(TEMPLATES) +lldpctl.pc: lldpctl.pc.in +include $(top_srcdir)/edit.am diff --git a/src/lib/Makefile.in b/src/lib/Makefile.in new file mode 100644 index 0000000..9306493 --- /dev/null +++ b/src/lib/Makefile.in @@ -0,0 +1,978 @@ +# Makefile.in generated by automake 1.16.5 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2021 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +@HAVE_LD_VERSION_SCRIPT_TRUE@am__append_1 = lldpctl.map +@HAVE_LD_VERSION_SCRIPT_TRUE@am__append_2 = -Wl,--version-script=$(srcdir)/lldpctl.map +@HAVE_LD_VERSION_SCRIPT_FALSE@am__append_3 = -export-symbols-regex '^lldpctl_' +subdir = src/lib +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/alignof.m4 \ + $(top_srcdir)/m4/args.m4 \ + $(top_srcdir)/m4/ax_build_date_epoch.m4 \ + $(top_srcdir)/m4/ax_cflags_gcc_option.m4 \ + $(top_srcdir)/m4/ax_ld_check_flag.m4 \ + $(top_srcdir)/m4/ax_lib_readline.m4 \ + $(top_srcdir)/m4/ax_prog_doxygen.m4 \ + $(top_srcdir)/m4/config_subdirs.m4 \ + $(top_srcdir)/m4/ld-version-script.m4 \ + $(top_srcdir)/m4/libcap.m4 $(top_srcdir)/m4/libevent.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/os.m4 \ + $(top_srcdir)/m4/progname.m4 $(top_srcdir)/m4/seccomp.m4 \ + $(top_srcdir)/m4/snmp.m4 $(top_srcdir)/m4/stdint.m4 \ + $(top_srcdir)/m4/systemtap.m4 $(top_srcdir)/m4/xml2.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(include_HEADERS) \ + $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkgconfigdir)" \ + "$(DESTDIR)$(includedir)" +LTLIBRARIES = $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES) +libfixedpoint_la_LIBADD = +am_libfixedpoint_la_OBJECTS = fixedpoint.lo +libfixedpoint_la_OBJECTS = $(am_libfixedpoint_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +am__dirstamp = $(am__leading_dot)dirstamp +am__objects_1 = atoms/config.lo atoms/dot1.lo atoms/dot3.lo \ + atoms/interface.lo atoms/med.lo atoms/mgmt.lo atoms/port.lo \ + atoms/custom.lo atoms/chassis.lo +am_liblldpctl_la_OBJECTS = errors.lo connection.lo atom.lo helpers.lo \ + $(am__objects_1) +nodist_liblldpctl_la_OBJECTS = atom-glue.lo +liblldpctl_la_OBJECTS = $(am_liblldpctl_la_OBJECTS) \ + $(nodist_liblldpctl_la_OBJECTS) +liblldpctl_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(liblldpctl_la_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/atom-glue.Plo ./$(DEPDIR)/atom.Plo \ + ./$(DEPDIR)/connection.Plo ./$(DEPDIR)/errors.Plo \ + ./$(DEPDIR)/fixedpoint.Plo ./$(DEPDIR)/helpers.Plo \ + atoms/$(DEPDIR)/chassis.Plo atoms/$(DEPDIR)/config.Plo \ + atoms/$(DEPDIR)/custom.Plo atoms/$(DEPDIR)/dot1.Plo \ + atoms/$(DEPDIR)/dot3.Plo atoms/$(DEPDIR)/interface.Plo \ + atoms/$(DEPDIR)/med.Plo atoms/$(DEPDIR)/mgmt.Plo \ + atoms/$(DEPDIR)/port.Plo +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libfixedpoint_la_SOURCES) $(liblldpctl_la_SOURCES) \ + $(nodist_liblldpctl_la_SOURCES) +DIST_SOURCES = $(libfixedpoint_la_SOURCES) $(liblldpctl_la_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +DATA = $(pkgconfig_DATA) +HEADERS = $(include_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp \ + $(top_srcdir)/edit.am +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APPARMORDIR = @APPARMORDIR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CONFIGURE_ARGS = @CONFIGURE_ARGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DOXYGEN_PAPER_SIZE = @DOXYGEN_PAPER_SIZE@ +DSYMUTIL = @DSYMUTIL@ +DTRACE = @DTRACE@ +DUMPBIN = @DUMPBIN@ +DX_CONFIG = @DX_CONFIG@ +DX_DOCDIR = @DX_DOCDIR@ +DX_DOT = @DX_DOT@ +DX_DOXYGEN = @DX_DOXYGEN@ +DX_DVIPS = @DX_DVIPS@ +DX_EGREP = @DX_EGREP@ +DX_ENV = @DX_ENV@ +DX_FLAG_chi = @DX_FLAG_chi@ +DX_FLAG_chm = @DX_FLAG_chm@ +DX_FLAG_doc = @DX_FLAG_doc@ +DX_FLAG_dot = @DX_FLAG_dot@ +DX_FLAG_html = @DX_FLAG_html@ +DX_FLAG_man = @DX_FLAG_man@ +DX_FLAG_pdf = @DX_FLAG_pdf@ +DX_FLAG_ps = @DX_FLAG_ps@ +DX_FLAG_rtf = @DX_FLAG_rtf@ +DX_FLAG_xml = @DX_FLAG_xml@ +DX_HHC = @DX_HHC@ +DX_LATEX = @DX_LATEX@ +DX_MAKEINDEX = @DX_MAKEINDEX@ +DX_PDFLATEX = @DX_PDFLATEX@ +DX_PERL = @DX_PERL@ +DX_PROJECT = @DX_PROJECT@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ETAGS = @ETAGS@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FILECMD = @FILECMD@ +FUZZ_DECODE_ENGINE = @FUZZ_DECODE_ENGINE@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LAUNCHDDAEMONSDIR = @LAUNCHDDAEMONSDIR@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LLDPD_CTL_SOCKET = @LLDPD_CTL_SOCKET@ +LLDPD_PID_FILE = @LLDPD_PID_FILE@ +LLDP_BIN_LDFLAGS = @LLDP_BIN_LDFLAGS@ +LLDP_CFLAGS = @LLDP_CFLAGS@ +LLDP_CPPFLAGS = @LLDP_CPPFLAGS@ +LLDP_LDFLAGS = @LLDP_LDFLAGS@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NETLINK_MAX_RECEIVE_BUFSIZE = @NETLINK_MAX_RECEIVE_BUFSIZE@ +NETLINK_RECEIVE_BUFSIZE = @NETLINK_RECEIVE_BUFSIZE@ +NETLINK_SEND_BUFSIZE = @NETLINK_SEND_BUFSIZE@ +NETSNMP_CFLAGS = @NETSNMP_CFLAGS@ +NETSNMP_CONFIG = @NETSNMP_CONFIG@ +NETSNMP_LIBS = @NETSNMP_LIBS@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PRIVSEP_CHROOT = @PRIVSEP_CHROOT@ +PRIVSEP_GROUP = @PRIVSEP_GROUP@ +PRIVSEP_USER = @PRIVSEP_USER@ +RANLIB = @RANLIB@ +READLINE_LIBS = @READLINE_LIBS@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +SYSTEMDSYSTEMUNITDIR = @SYSTEMDSYSTEMUNITDIR@ +SYSUSERSDIR = @SYSUSERSDIR@ +VERSION = @VERSION@ +XML2_CONFIG = @XML2_CONFIG@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +apparmordir = @apparmordir@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +check_CFLAGS = @check_CFLAGS@ +check_LIBS = @check_LIBS@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +launchddaemonsdir = @launchddaemonsdir@ +libbsd_CFLAGS = @libbsd_CFLAGS@ +libbsd_LIBS = @libbsd_LIBS@ +libcap_CFLAGS = @libcap_CFLAGS@ +libcap_LIBS = @libcap_LIBS@ +libdir = @libdir@ +libevent_CFLAGS = @libevent_CFLAGS@ +libevent_LDFLAGS = @libevent_LDFLAGS@ +libevent_LIBS = @libevent_LIBS@ +libexecdir = @libexecdir@ +libseccomp_CFLAGS = @libseccomp_CFLAGS@ +libseccomp_LIBS = @libseccomp_LIBS@ +libxml2_CFLAGS = @libxml2_CFLAGS@ +libxml2_LIBS = @libxml2_LIBS@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgconfigdir = @pkgconfigdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +systemdsystemunitdir = @systemdsystemunitdir@ +sysusersdir = @sysusersdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +AM_CFLAGS = -I $(top_srcdir)/include $(LLDP_CFLAGS) +AM_CPPFLAGS = $(LLDP_CPPFLAGS) +AM_LDFLAGS = $(LLDP_LDFLAGS) +lib_LTLIBRARIES = liblldpctl.la +include_HEADERS = lldpctl.h +noinst_LTLIBRARIES = libfixedpoint.la +libfixedpoint_la_SOURCES = fixedpoint.h fixedpoint.c +ATOM_FILES = \ + atoms/config.c atoms/dot1.c atoms/dot3.c \ + atoms/interface.c atoms/med.c atoms/mgmt.c atoms/port.c \ + atoms/custom.c atoms/chassis.c + +liblldpctl_la_SOURCES = \ + lldpctl.h atom.h helpers.h \ + errors.c connection.c atom.c helpers.c \ + $(ATOM_FILES) + +nodist_liblldpctl_la_SOURCES = atom-glue.c +liblldpctl_la_LIBADD = $(top_builddir)/src/libcommon-daemon-lib.la libfixedpoint.la +CLEANFILES = atom-glue.c $(TEMPLATES) + +# -version-info format is `current`:`revision`:`age`. For more details, see: +# https://www.sourceware.org/autobook/autobook/autobook_61.html#Library-Versioning +# +# -version-number could be computed from -version-info, mostly major +# is `current` - `age`, minor is `age` and revision is `revision' and +# major.minor should be used when updating lldpctl.map. +liblldpctl_la_LDFLAGS = $(AM_LDFLAGS) -version-info 13:1:9 \ + $(am__append_2) $(am__append_3) +liblldpctl_la_DEPENDENCIES = libfixedpoint.la $(am__append_1) +pkgconfig_DATA = lldpctl.pc +TEMPLATES = lldpctl.pc +EXTRA_DIST = lldpctl.pc.in lldpctl.map +edit = $(SED) \ + -e 's|@bindir[@]|$(bindir)|g' \ + -e 's|@sbindir[@]|$(sbindir)|g' \ + -e 's|@sysconfdir[@]|$(sysconfdir)|g' \ + -e 's|@pkgdatadir[@]|$(pkgdatadir)|g' \ + -e 's|@libdir[@]|$(libdir)|g' \ + -e 's|@srcdir[@]|$(srcdir)|g' \ + -e 's|@top_builddir[@]|$(top_builddir)|g' \ + -e 's|@includedir[@]|$(includedir)|g' \ + -e 's|@exec_prefix[@]|$(exec_prefix)|g' \ + -e 's|@prefix[@]|$(prefix)|g' \ + -e 's|@VERSION[@]|$(VERSION)|g' \ + -e 's|@PACKAGE[@]|$(PACKAGE)|g' \ + -e 's|@PACKAGE_NAME[@]|$(PACKAGE_NAME)|g' \ + -e 's|@PACKAGE_URL[@]|$(PACKAGE_URL)|g' \ + -e 's|@PRIVSEP_USER[@]|$(PRIVSEP_USER)|g' \ + -e 's|@PRIVSEP_GROUP[@]|$(PRIVSEP_GROUP)|g' \ + -e 's|@PRIVSEP_CHROOT[@]|$(PRIVSEP_CHROOT)|g' \ + -e 's|@LLDPD_PID_FILE[@]|$(LLDPD_PID_FILE)|g' \ + -e 's|@LLDPD_CTL_SOCKET[@]|$(LLDPD_CTL_SOCKET)|g' \ + -e 's|@PRIVSEP_CHROOT[@]|$(PRIVSEP_CHROOT)|g' + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/edit.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; +$(top_srcdir)/edit.am $(am__empty): + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ + } + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libfixedpoint.la: $(libfixedpoint_la_OBJECTS) $(libfixedpoint_la_DEPENDENCIES) $(EXTRA_libfixedpoint_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libfixedpoint_la_OBJECTS) $(libfixedpoint_la_LIBADD) $(LIBS) +atoms/$(am__dirstamp): + @$(MKDIR_P) atoms + @: > atoms/$(am__dirstamp) +atoms/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) atoms/$(DEPDIR) + @: > atoms/$(DEPDIR)/$(am__dirstamp) +atoms/config.lo: atoms/$(am__dirstamp) atoms/$(DEPDIR)/$(am__dirstamp) +atoms/dot1.lo: atoms/$(am__dirstamp) atoms/$(DEPDIR)/$(am__dirstamp) +atoms/dot3.lo: atoms/$(am__dirstamp) atoms/$(DEPDIR)/$(am__dirstamp) +atoms/interface.lo: atoms/$(am__dirstamp) \ + atoms/$(DEPDIR)/$(am__dirstamp) +atoms/med.lo: atoms/$(am__dirstamp) atoms/$(DEPDIR)/$(am__dirstamp) +atoms/mgmt.lo: atoms/$(am__dirstamp) atoms/$(DEPDIR)/$(am__dirstamp) +atoms/port.lo: atoms/$(am__dirstamp) atoms/$(DEPDIR)/$(am__dirstamp) +atoms/custom.lo: atoms/$(am__dirstamp) atoms/$(DEPDIR)/$(am__dirstamp) +atoms/chassis.lo: atoms/$(am__dirstamp) \ + atoms/$(DEPDIR)/$(am__dirstamp) + +liblldpctl.la: $(liblldpctl_la_OBJECTS) $(liblldpctl_la_DEPENDENCIES) $(EXTRA_liblldpctl_la_DEPENDENCIES) + $(AM_V_CCLD)$(liblldpctl_la_LINK) -rpath $(libdir) $(liblldpctl_la_OBJECTS) $(liblldpctl_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + -rm -f atoms/*.$(OBJEXT) + -rm -f atoms/*.lo + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/atom-glue.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/atom.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/connection.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/errors.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fixedpoint.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/helpers.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@atoms/$(DEPDIR)/chassis.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@atoms/$(DEPDIR)/config.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@atoms/$(DEPDIR)/custom.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@atoms/$(DEPDIR)/dot1.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@atoms/$(DEPDIR)/dot3.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@atoms/$(DEPDIR)/interface.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@atoms/$(DEPDIR)/med.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@atoms/$(DEPDIR)/mgmt.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@atoms/$(DEPDIR)/port.Plo@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + -rm -rf atoms/.libs atoms/_libs +install-pkgconfigDATA: $(pkgconfig_DATA) + @$(NORMAL_INSTALL) + @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkgconfigdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkgconfigdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgconfigdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgconfigdir)" || exit $$?; \ + done + +uninstall-pkgconfigDATA: + @$(NORMAL_UNINSTALL) + @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(pkgconfigdir)'; $(am__uninstall_files_from_dir) +install-includeHEADERS: $(include_HEADERS) + @$(NORMAL_INSTALL) + @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(includedir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(includedir)" || exit $$?; \ + done + +uninstall-includeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir) + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) $(DATA) $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkgconfigdir)" "$(DESTDIR)$(includedir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + -rm -f atoms/$(DEPDIR)/$(am__dirstamp) + -rm -f atoms/$(am__dirstamp) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ + clean-noinstLTLIBRARIES mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/atom-glue.Plo + -rm -f ./$(DEPDIR)/atom.Plo + -rm -f ./$(DEPDIR)/connection.Plo + -rm -f ./$(DEPDIR)/errors.Plo + -rm -f ./$(DEPDIR)/fixedpoint.Plo + -rm -f ./$(DEPDIR)/helpers.Plo + -rm -f atoms/$(DEPDIR)/chassis.Plo + -rm -f atoms/$(DEPDIR)/config.Plo + -rm -f atoms/$(DEPDIR)/custom.Plo + -rm -f atoms/$(DEPDIR)/dot1.Plo + -rm -f atoms/$(DEPDIR)/dot3.Plo + -rm -f atoms/$(DEPDIR)/interface.Plo + -rm -f atoms/$(DEPDIR)/med.Plo + -rm -f atoms/$(DEPDIR)/mgmt.Plo + -rm -f atoms/$(DEPDIR)/port.Plo + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-includeHEADERS install-pkgconfigDATA + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-libLTLIBRARIES + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f ./$(DEPDIR)/atom-glue.Plo + -rm -f ./$(DEPDIR)/atom.Plo + -rm -f ./$(DEPDIR)/connection.Plo + -rm -f ./$(DEPDIR)/errors.Plo + -rm -f ./$(DEPDIR)/fixedpoint.Plo + -rm -f ./$(DEPDIR)/helpers.Plo + -rm -f atoms/$(DEPDIR)/chassis.Plo + -rm -f atoms/$(DEPDIR)/config.Plo + -rm -f atoms/$(DEPDIR)/custom.Plo + -rm -f atoms/$(DEPDIR)/dot1.Plo + -rm -f atoms/$(DEPDIR)/dot3.Plo + -rm -f atoms/$(DEPDIR)/interface.Plo + -rm -f atoms/$(DEPDIR)/med.Plo + -rm -f atoms/$(DEPDIR)/mgmt.Plo + -rm -f atoms/$(DEPDIR)/port.Plo + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-includeHEADERS uninstall-libLTLIBRARIES \ + uninstall-pkgconfigDATA + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-generic clean-libLTLIBRARIES clean-libtool \ + clean-noinstLTLIBRARIES cscopelist-am ctags ctags-am distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-includeHEADERS install-info \ + install-info-am install-libLTLIBRARIES install-man install-pdf \ + install-pdf-am install-pkgconfigDATA install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ + uninstall-includeHEADERS uninstall-libLTLIBRARIES \ + uninstall-pkgconfigDATA + +.PRECIOUS: Makefile + + +atom-glue.c: $(ATOM_FILES) Makefile + $(AM_V_GEN)(for f in $(ATOM_FILES:%=$(srcdir)/%); do \ + $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) $$f; done | \ + $(SED) -n 's+^void init_atom_builder_\([^(]*\)(void).*, \([0-9]*\)).*+\2 \1+p' | \ + sort | \ + $(AWK) '{ atoms[$$2] = 1 } \ + END { for (atom in atoms) { print "void init_atom_builder_"atom"(void);" } \ + print "void init_atom_builder(void);"; \ + print "void init_atom_builder(void) {"; \ + print " static int init = 0; if (init) return; init++;"; \ + for (atom in atoms) { print " init_atom_builder_"atom"();" } \ + print "}"; }' && \ + for f in $(ATOM_FILES:%=$(srcdir)/%); do \ + $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) $$f; done | \ + $(SED) -n 's+^void init_atom_map_\([^(]*\)(void).*, \([0-9]*\)).*+\2 \1+p' | \ + sort -n | \ + $(AWK) '{ atoms[$$2] = 1 } \ + END { for (atom in atoms) { print "void init_atom_map_"atom"(void);" } \ + print "void init_atom_map(void);"; \ + print "void init_atom_map(void) {"; \ + print " static int init = 0; if (init) return; init++;"; \ + for (atom in atoms) { print " init_atom_map_"atom"();" } \ + print "}"; }' ) \ + > $@.tmp + $(AM_V_at)$(GREP) -q init_atom_builder_ $@.tmp + $(AM_V_at)$(GREP) -q init_atom_map_ $@.tmp + $(AM_V_at)mv $@.tmp $@ +lldpctl.pc: lldpctl.pc.in + +$(TEMPLATES): Makefile + $(AM_V_GEN)$(MKDIR_P) $(@D) && $(edit) $(srcdir)/$@.in > $@.tmp && mv $@.tmp $@ + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/lib/atom.c b/src/lib/atom.c new file mode 100644 index 0000000..04011d4 --- /dev/null +++ b/src/lib/atom.c @@ -0,0 +1,655 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdio.h> +#include <string.h> +#include <limits.h> +#include "lldpctl.h" +#include "atom.h" +#include "../log.h" +#include "../marshal.h" +#include "../ctl.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) +{ + if (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; + const char *errstr; + long long converted = 0; + int isint = 0; + 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); + } + + if (value) { + converted = strtonum(value, LLONG_MIN, LLONG_MAX, &errstr); + isint = (errstr == NULL); + } + + 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, + value ? strlen(value) : 0); + 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 state_data Ancillary data for state handling + * @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. + * + * The current state must match one of @c CONN_STATE_IDLE, @c state_send or @c + * state_recv and in the two later cases, the provided @c state_data must match. + */ +int +_lldpctl_do_something(lldpctl_conn_t *conn, int state_send, int state_recv, + const char *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_WATCHING) + /* The connection cannot be used anymore. */ + return SET_ERROR(conn, LLDPCTL_ERR_INVALID_STATE); + + 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; + if (state_data) + strlcpy(conn->state_data, state_data, sizeof(conn->state_data)); + else + conn->state_data[0] = 0; + } + if (conn->state == state_send && + (state_data == NULL || + !strncmp(conn->state_data, state_data, sizeof(conn->state_data) - 1))) { + /* 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 && + (state_data == NULL || + !strncmp(conn->state_data, state_data, sizeof(conn->state_data) - 1))) { + /* 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[0] = 0; + return 0; + } else + return SET_ERROR(conn, LLDPCTL_ERR_INVALID_STATE); +} + +int +lldpctl_watch_callback(lldpctl_conn_t *conn, lldpctl_change_callback cb, void *data) +{ + int rc; + + RESET_ERROR(conn); + + rc = _lldpctl_do_something(conn, CONN_STATE_SET_WATCH_SEND, + CONN_STATE_SET_WATCH_RECV, NULL, SUBSCRIBE, NULL, NULL, NULL, NULL); + if (rc == 0) { + conn->watch_cb = cb; + conn->watch_data = data; + conn->state = CONN_STATE_WATCHING; + } + return rc; +} + +int +lldpctl_watch_callback2(lldpctl_conn_t *conn, lldpctl_change_callback2 cb, void *data) +{ + int rc; + + RESET_ERROR(conn); + + rc = _lldpctl_do_something(conn, CONN_STATE_SET_WATCH_SEND, + CONN_STATE_SET_WATCH_RECV, NULL, SUBSCRIBE, NULL, NULL, NULL, NULL); + if (rc == 0) { + conn->watch_cb2 = cb; + conn->watch_data = data; + conn->state = CONN_STATE_WATCHING; + } + return rc; +} + +int +lldpctl_watch(lldpctl_conn_t *conn) +{ + int rc = 0; + + RESET_ERROR(conn); + + if (conn->state != CONN_STATE_WATCHING) + return SET_ERROR(conn, LLDPCTL_ERR_INVALID_STATE); + + conn->watch_triggered = 0; + while (!conn->watch_triggered) { + rc = _lldpctl_needs(conn, 1); + if (rc < 0) return SET_ERROR(conn, rc); + } + + RESET_ERROR(conn); + return 0; +} + +lldpctl_atom_t * +lldpctl_get_configuration(lldpctl_conn_t *conn) +{ + int rc; + struct lldpd_config *config; + void *p; + + RESET_ERROR(conn); + + rc = _lldpctl_do_something(conn, CONN_STATE_GET_CONFIG_SEND, + CONN_STATE_GET_CONFIG_RECV, NULL, GET_CONFIG, NULL, NULL, &p, + &MARSHAL_INFO(lldpd_config)); + if (rc == 0) { + config = p; + return _lldpctl_new_atom(conn, atom_config, config); + } + return NULL; +} + +lldpctl_atom_t * +lldpctl_get_interfaces(lldpctl_conn_t *conn) +{ + struct lldpd_interface_list *ifs; + void *p; + 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, &p, + &MARSHAL_INFO(lldpd_interface_list)); + if (rc == 0) { + ifs = p; + return _lldpctl_new_atom(conn, atom_interfaces_list, ifs); + } + return NULL; +} + +lldpctl_atom_t * +lldpctl_get_local_chassis(lldpctl_conn_t *conn) +{ + struct lldpd_chassis *chassis; + void *p; + int rc; + + RESET_ERROR(conn); + + rc = _lldpctl_do_something(conn, CONN_STATE_GET_CHASSIS_SEND, + CONN_STATE_GET_CHASSIS_RECV, NULL, GET_CHASSIS, NULL, NULL, &p, + &MARSHAL_INFO(lldpd_chassis)); + if (rc == 0) { + chassis = p; + return _lldpctl_new_atom(conn, atom_chassis, chassis, NULL, 0); + } + return NULL; +} + +lldpctl_atom_t * +lldpctl_get_port(lldpctl_atom_t *atom) +{ + int rc; + lldpctl_conn_t *conn = atom->conn; + struct lldpd_hardware *hardware; + void *p; + 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), &p, &MARSHAL_INFO(lldpd_hardware)); + if (rc == 0) { + hardware = p; + return _lldpctl_new_atom(conn, atom_port, 1, hardware, + &hardware->h_lport, NULL); + } + return NULL; +} + +lldpctl_atom_t * +lldpctl_get_default_port(lldpctl_conn_t *conn) +{ + struct lldpd_port *port; + void *p; + int rc; + + RESET_ERROR(conn); + + rc = _lldpctl_do_something(conn, CONN_STATE_GET_DEFAULT_PORT_SEND, + CONN_STATE_GET_DEFAULT_PORT_RECV, "", GET_DEFAULT_PORT, NULL, NULL, &p, + &MARSHAL_INFO(lldpd_port)); + if (rc == 0) { + port = p; + return _lldpctl_new_atom(conn, atom_port, 1, NULL, port, NULL); + } + return NULL; +} + +static lldpctl_map_t empty_map[] = { { 0, NULL } }; + +static struct atom_map atom_map_list = { .next = NULL }; + +lldpctl_map_t * +lldpctl_key_get_map(lldpctl_key_t key) +{ + init_atom_map(); + struct atom_map *map; + for (map = atom_map_list.next; map; map = map->next) { + if (map->key == key) return map->map; + } + return empty_map; +} + +void +atom_map_register(struct atom_map *map, int prio) +{ + (void)prio; + struct atom_map *iter = &atom_map_list; + + while (iter->next) + iter = iter->next; + + iter->next = map; +} + +static struct atom_builder atom_builder_list = { .nextb = NULL }; + +void +atom_builder_register(struct atom_builder *builder, int prio) +{ + (void)prio; + struct atom_builder *iter = &atom_builder_list; + + while (iter->nextb) + iter = iter->nextb; + + iter->nextb = builder; +} + +lldpctl_atom_t * +_lldpctl_new_atom(lldpctl_conn_t *conn, atom_t type, ...) +{ + init_atom_builder(); + struct atom_builder *builder; + struct lldpctl_atom_t *atom; + va_list(ap); + for (builder = atom_builder_list.nextb; builder; builder = builder->nextb) { + 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; + } + log_warnx("rpc", "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++) + snprintf(buffer + i * 3, 4, "%02x%c", *(u_int8_t *)(input + i), sep); + if (max > 0 && size > max) + snprintf(buffer + i * 3, sizeof(truncation) + 1, "%s", truncation); + else + *(buffer + i * 3 - 1) = 0; + return buffer; +} diff --git a/src/lib/atom.h b/src/lib/atom.h new file mode 100644 index 0000000..0dfc85b --- /dev/null +++ b/src/lib/atom.h @@ -0,0 +1,344 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/queue.h> +#include "../lldpd-structs.h" +#include "../compat/compat.h" +#include "../marshal.h" +#include "../ctl.h" + +/* connection.c */ +struct lldpctl_conn_t { + /* the Unix-domain socket to connect to lldpd */ + char *ctlname; + + /* 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 +#define CONN_STATE_SET_WATCH_SEND 7 +#define CONN_STATE_SET_WATCH_RECV 8 +#define CONN_STATE_GET_CONFIG_SEND 9 +#define CONN_STATE_GET_CONFIG_RECV 10 +#define CONN_STATE_SET_CONFIG_SEND 11 +#define CONN_STATE_SET_CONFIG_RECV 12 +#define CONN_STATE_GET_CHASSIS_SEND 13 +#define CONN_STATE_GET_CHASSIS_RECV 14 +#define CONN_STATE_GET_DEFAULT_PORT_SEND 15 +#define CONN_STATE_GET_DEFAULT_PORT_RECV 16 +#define CONN_STATE_WATCHING 17 +#define CONN_STATE_SET_CHASSIS_SEND 18 +#define CONN_STATE_SET_CHASSIS_RECV 19 + + int state; /* Current state */ + /* 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. */ + char state_data[IFNAMSIZ + 64]; + /* Error handling */ + lldpctl_error_t error; /* Last error */ + + /* Handling notifications */ + lldpctl_change_callback watch_cb; + lldpctl_change_callback2 watch_cb2; + void *watch_data; + int watch_triggered; +}; + +/* 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, + const char *state_data, enum hmsg_type type, void *to_send, + struct marshal_info *mi_send, void **to_recv, struct marshal_info *mi_recv); + +/* errors.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_config, + 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 +#ifdef ENABLE_CUSTOM + atom_custom_list, + atom_custom, +#endif + atom_chassis, +} 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_config_t { + lldpctl_atom_t base; + struct lldpd_config *config; +}; + +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_chassis_t { + lldpctl_atom_t base; + struct lldpd_chassis *chassis; + struct _lldpctl_atom_port_t + *parent; /* Optional: parent of this atom (owning our reference) */ + int embedded; /* This atom is "embedded" (not refcounted) */ +}; + +struct _lldpctl_atom_port_t { + lldpctl_atom_t base; + int local; /* Local or remote port? */ + struct lldpd_hardware *hardware; /* Local port only (but optional) */ + struct lldpd_port *port; /* Local and remote */ + struct _lldpctl_atom_port_t *parent; /* Local port if we are a remote port */ + lldpctl_atom_t *chassis; /* Internal atom for chassis */ +}; + +/* 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; + lldpctl_atom_t *parent; + struct lldpd_chassis *chassis; /* Chassis containing the list of IP addresses */ +}; + +struct _lldpctl_atom_mgmt_t { + lldpctl_atom_t base; + lldpctl_atom_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 + +#ifdef ENABLE_CUSTOM +struct _lldpctl_atom_custom_list_t { + lldpctl_atom_t base; + struct _lldpctl_atom_port_t *parent; + struct lldpd_custom_list *list; +}; + +struct _lldpctl_atom_custom_t { + lldpctl_atom_t base; + struct _lldpctl_atom_port_t *parent; + int op; + struct lldpd_custom *tlv; +}; +#endif + +struct lldpctl_atom_t *_lldpctl_new_atom(lldpctl_conn_t *conn, atom_t type, ...); + +struct atom_map { + int key; + struct atom_map *next; + lldpctl_map_t map[]; +}; + +void atom_map_register(struct atom_map *map, int); +void init_atom_map(void); + +#define ATOM_MAP_REGISTER(NAME, PRIO) \ + void init_atom_map_##NAME(void); \ + void init_atom_map_##NAME(void) \ + { \ + atom_map_register(&NAME, PRIO); \ + } + +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 *nextb; +}; + +void atom_builder_register(struct atom_builder *builder, int); +void init_atom_builder(void); + +#define ATOM_BUILDER_REGISTER(NAME, PRIO) \ + void init_atom_builder_##NAME(void); \ + void init_atom_builder_##NAME(void) \ + { \ + atom_builder_register(&NAME, PRIO); \ + } diff --git a/src/lib/atoms/chassis.c b/src/lib/atoms/chassis.c new file mode 100644 index 0000000..4d195f7 --- /dev/null +++ b/src/lib/atoms/chassis.c @@ -0,0 +1,326 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2015 Vincent Bernat <vincent@bernat.im> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <arpa/inet.h> + +#include "../lldpctl.h" +#include "../../log.h" +#include "../atom.h" +#include "../helpers.h" + +static lldpctl_map_t 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 }, +}; + +#ifdef ENABLE_LLDPMED + +static lldpctl_map_t 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 }, +}; + +#endif + +static int +_lldpctl_atom_new_chassis(lldpctl_atom_t *atom, va_list ap) +{ + struct _lldpctl_atom_chassis_t *p = (struct _lldpctl_atom_chassis_t *)atom; + p->chassis = va_arg(ap, struct lldpd_chassis *); + p->parent = va_arg(ap, struct _lldpctl_atom_port_t *); + p->embedded = va_arg(ap, int); + if (p->parent && !p->embedded) + lldpctl_atom_inc_ref((lldpctl_atom_t *)p->parent); + return 1; +} + +static void +_lldpctl_atom_free_chassis(lldpctl_atom_t *atom) +{ + struct _lldpctl_atom_chassis_t *p = (struct _lldpctl_atom_chassis_t *)atom; + /* When we have a parent, the chassis structure is in fact part of the + * parent, just decrement the reference count of the parent. Otherwise, + * we need to free the whole chassis. When embedded, we don't alter the + * reference count of the parent. Therefore, it's important to also not + * increase the reference count of this atom. See + * `_lldpctl_atom_get_atom_chassis' for how to handle that. */ + if (p->parent) { + if (!p->embedded) lldpctl_atom_dec_ref((lldpctl_atom_t *)p->parent); + } else + lldpd_chassis_cleanup(p->chassis, 1); +} + +static lldpctl_atom_t * +_lldpctl_atom_get_atom_chassis(lldpctl_atom_t *atom, lldpctl_key_t key) +{ + struct _lldpctl_atom_chassis_t *p = (struct _lldpctl_atom_chassis_t *)atom; + struct lldpd_chassis *chassis = p->chassis; + + switch (key) { + case lldpctl_k_chassis_mgmt: + return _lldpctl_new_atom(atom->conn, atom_mgmts_list, + (p->parent && p->embedded) ? (lldpctl_atom_t *)p->parent : + (lldpctl_atom_t *)p, + chassis); + default: + SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST); + return NULL; + } +} + +#ifdef ENABLE_LLDPMED +static lldpctl_atom_t * +_lldpctl_atom_set_str_chassis(lldpctl_atom_t *atom, lldpctl_key_t key, + const char *value) +{ + struct _lldpctl_atom_chassis_t *p = (struct _lldpctl_atom_chassis_t *)atom; + struct lldpd_chassis *chassis = p->chassis; + + char *canary = NULL; + + int rc; + + switch (key) { + case lldpctl_k_chassis_med_inventory_hw: + free(chassis->c_med_hw); + chassis->c_med_hw = xstrdup(value); + break; + case lldpctl_k_chassis_med_inventory_sw: + free(chassis->c_med_sw); + chassis->c_med_sw = xstrdup(value); + break; + case lldpctl_k_chassis_med_inventory_fw: + free(chassis->c_med_fw); + chassis->c_med_fw = xstrdup(value); + break; + case lldpctl_k_chassis_med_inventory_sn: + free(chassis->c_med_sn); + chassis->c_med_sn = xstrdup(value); + break; + case lldpctl_k_chassis_med_inventory_manuf: + free(chassis->c_med_manuf); + chassis->c_med_manuf = xstrdup(value); + break; + case lldpctl_k_chassis_med_inventory_model: + free(chassis->c_med_model); + chassis->c_med_model = xstrdup(value); + break; + case lldpctl_k_chassis_med_inventory_asset: + free(chassis->c_med_asset); + chassis->c_med_asset = xstrdup(value); + break; + default: + SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST); + return NULL; + } + + if (asprintf(&canary, "%d%s", key, value ? value : "(NULL)") == -1) { + SET_ERROR(atom->conn, LLDPCTL_ERR_NOMEM); + return NULL; + } + + rc = _lldpctl_do_something(atom->conn, CONN_STATE_SET_CHASSIS_SEND, + CONN_STATE_SET_CHASSIS_RECV, canary, SET_CHASSIS, chassis, + &MARSHAL_INFO(lldpd_chassis), NULL, NULL); + + free(canary); + if (rc == 0) return atom; + return NULL; +} +#endif /* ENABLE_LLDPMED */ + +static const char * +_lldpctl_atom_get_str_chassis(lldpctl_atom_t *atom, lldpctl_key_t key) +{ + struct _lldpctl_atom_chassis_t *p = (struct _lldpctl_atom_chassis_t *)atom; + struct lldpd_chassis *chassis = p->chassis; + char *ipaddress = NULL; + size_t len; + + /* Local and remote port */ + switch (key) { + + 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 lldpctl_atom_t * +_lldpctl_atom_set_int_chassis(lldpctl_atom_t *atom, lldpctl_key_t key, long int value) +{ + int rc; + char *canary = NULL; + struct _lldpctl_atom_chassis_t *c = (struct _lldpctl_atom_chassis_t *)atom; + struct lldpd_chassis chassis; + memcpy(&chassis, c->chassis, sizeof(struct lldpd_chassis)); + + switch (key) { + case lldpctl_k_chassis_cap_enabled: + chassis.c_cap_enabled = c->chassis->c_cap_enabled = + chassis.c_cap_available & value; + break; + default: + SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST); + return NULL; + } + + if (asprintf(&canary, "%d%ld", key, value) == -1) { + SET_ERROR(atom->conn, LLDPCTL_ERR_NOMEM); + return NULL; + } + + rc = _lldpctl_do_something(atom->conn, CONN_STATE_SET_CHASSIS_SEND, + CONN_STATE_SET_CHASSIS_RECV, canary, SET_CHASSIS, &chassis, + &MARSHAL_INFO(lldpd_chassis), NULL, NULL); + + free(canary); + if (rc == 0) return atom; + return NULL; +} + +static long int +_lldpctl_atom_get_int_chassis(lldpctl_atom_t *atom, lldpctl_key_t key) +{ + struct _lldpctl_atom_chassis_t *p = (struct _lldpctl_atom_chassis_t *)atom; + struct lldpd_chassis *chassis = p->chassis; + + /* Local and remote port */ + switch (key) { + 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); + } +} + +static const uint8_t * +_lldpctl_atom_get_buf_chassis(lldpctl_atom_t *atom, lldpctl_key_t key, size_t *n) +{ + struct _lldpctl_atom_chassis_t *p = (struct _lldpctl_atom_chassis_t *)atom; + struct lldpd_chassis *chassis = p->chassis; + + switch (key) { + 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 struct atom_builder chassis = { + atom_chassis, + sizeof(struct _lldpctl_atom_chassis_t), + .init = _lldpctl_atom_new_chassis, + .free = _lldpctl_atom_free_chassis, + .get = _lldpctl_atom_get_atom_chassis, + .get_str = _lldpctl_atom_get_str_chassis, + .get_int = _lldpctl_atom_get_int_chassis, + .set_int = _lldpctl_atom_set_int_chassis, + .get_buffer = _lldpctl_atom_get_buf_chassis, +#ifdef ENABLE_LLDPMED + .set_str = _lldpctl_atom_set_str_chassis, +#endif +}; + +ATOM_BUILDER_REGISTER(chassis, 3); diff --git a/src/lib/atoms/config.c b/src/lib/atoms/config.c new file mode 100644 index 0000000..8a4af2e --- /dev/null +++ b/src/lib/atoms/config.c @@ -0,0 +1,338 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2015 Vincent Bernat <vincent@bernat.im> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <arpa/inet.h> + +#include "../lldpctl.h" +#include "../../log.h" +#include "../atom.h" +#include "../helpers.h" + +static struct atom_map bond_slave_src_mac_map = { + .key = lldpctl_k_config_bond_slave_src_mac_type, + .map = { + { LLDP_BOND_SLAVE_SRC_MAC_TYPE_REAL, "real"}, + { LLDP_BOND_SLAVE_SRC_MAC_TYPE_ZERO, "zero"}, + { LLDP_BOND_SLAVE_SRC_MAC_TYPE_FIXED, "fixed"}, + { LLDP_BOND_SLAVE_SRC_MAC_TYPE_LOCALLY_ADMINISTERED, "local" }, + { LLDP_BOND_SLAVE_SRC_MAC_TYPE_UNKNOWN, NULL}, + }, +}; + +static struct atom_map lldp_portid_map = { + .key = lldpctl_k_config_lldp_portid_type, + .map = { + { LLDP_PORTID_SUBTYPE_IFNAME, "ifname"}, + { LLDP_PORTID_SUBTYPE_LLADDR, "macaddress"}, + { LLDP_PORTID_SUBTYPE_LOCAL, "local"}, + { LLDP_PORTID_SUBTYPE_UNKNOWN, NULL}, + }, +}; + +static struct atom_map lldp_agent_map = { + .key = lldpctl_k_config_lldp_agent_type, + .map = { + { LLDP_AGENT_TYPE_NEAREST_BRIDGE, "nearest bridge"}, + { LLDP_AGENT_TYPE_NEAREST_NONTPMR_BRIDGE, "nearest non-TPMR bridge"}, + { LLDP_AGENT_TYPE_NEAREST_CUSTOMER_BRIDGE, "nearest customer bridge"}, + { LLDP_AGENT_TYPE_UNKNOWN, NULL}, + }, +}; + +ATOM_MAP_REGISTER(bond_slave_src_mac_map, 1); +ATOM_MAP_REGISTER(lldp_portid_map, 2); +ATOM_MAP_REGISTER(lldp_agent_map, 3); + +static int +_lldpctl_atom_new_config(lldpctl_atom_t *atom, va_list ap) +{ + struct _lldpctl_atom_config_t *c = (struct _lldpctl_atom_config_t *)atom; + c->config = va_arg(ap, struct lldpd_config *); + return 1; +} + +static void +_lldpctl_atom_free_config(lldpctl_atom_t *atom) +{ + struct _lldpctl_atom_config_t *c = (struct _lldpctl_atom_config_t *)atom; + lldpd_config_cleanup(c->config); + free(c->config); +} + +static const char * +_lldpctl_atom_get_str_config(lldpctl_atom_t *atom, lldpctl_key_t key) +{ + char *res = NULL; + struct _lldpctl_atom_config_t *c = (struct _lldpctl_atom_config_t *)atom; + switch (key) { + case lldpctl_k_config_mgmt_pattern: + res = c->config->c_mgmt_pattern; + break; + case lldpctl_k_config_iface_pattern: + res = c->config->c_iface_pattern; + break; + case lldpctl_k_config_perm_iface_pattern: + res = c->config->c_perm_ifaces; + break; + case lldpctl_k_config_cid_pattern: + res = c->config->c_cid_pattern; + break; + case lldpctl_k_config_cid_string: + res = c->config->c_cid_string; + break; + case lldpctl_k_config_description: + res = c->config->c_description; + break; + case lldpctl_k_config_platform: + res = c->config->c_platform; + break; + case lldpctl_k_config_hostname: + res = c->config->c_hostname; + break; + case lldpctl_k_config_bond_slave_src_mac_type: + return map_lookup(bond_slave_src_mac_map.map, + c->config->c_bond_slave_src_mac_type); + case lldpctl_k_config_lldp_portid_type: + return map_lookup(lldp_portid_map.map, c->config->c_lldp_portid_type); + case lldpctl_k_config_lldp_agent_type: + return map_lookup(lldp_agent_map.map, c->config->c_lldp_agent_type); + default: + SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST); + return NULL; + } + return res ? res : ""; +} + +static struct _lldpctl_atom_config_t * +__lldpctl_atom_set_str_config(struct _lldpctl_atom_config_t *c, char **local, + char **global, const char *value) +{ + if (value) { + char *aval = NULL; + size_t len = strlen(value) + 1; + aval = _lldpctl_alloc_in_atom((lldpctl_atom_t *)c, len); + if (!aval) return NULL; + memcpy(aval, value, len); + *local = aval; + free(*global); + *global = strdup(aval); + } else { + free(*global); + *local = *global = NULL; + } + return c; +} + +static lldpctl_atom_t * +_lldpctl_atom_set_str_config(lldpctl_atom_t *atom, lldpctl_key_t key, const char *value) +{ + struct _lldpctl_atom_config_t *c = (struct _lldpctl_atom_config_t *)atom; + struct lldpd_config config; + memcpy(&config, c->config, sizeof(struct lldpd_config)); + char *canary = NULL; + int rc; + + switch (key) { + case lldpctl_k_config_perm_iface_pattern: + if (!__lldpctl_atom_set_str_config(c, &config.c_perm_ifaces, + &c->config->c_perm_ifaces, value)) + return NULL; + break; + case lldpctl_k_config_iface_pattern: + if (!__lldpctl_atom_set_str_config(c, &config.c_iface_pattern, + &c->config->c_iface_pattern, value)) + return NULL; + break; + case lldpctl_k_config_mgmt_pattern: + if (!__lldpctl_atom_set_str_config(c, &config.c_mgmt_pattern, + &c->config->c_mgmt_pattern, value)) + return NULL; + break; + case lldpctl_k_config_cid_string: + if (!__lldpctl_atom_set_str_config(c, &config.c_cid_string, + &c->config->c_cid_string, value)) + return NULL; + break; + case lldpctl_k_config_description: + if (!__lldpctl_atom_set_str_config(c, &config.c_description, + &c->config->c_description, value)) + return NULL; + break; + case lldpctl_k_config_platform: + if (!__lldpctl_atom_set_str_config(c, &config.c_platform, + &c->config->c_platform, value)) + return NULL; + break; + case lldpctl_k_config_hostname: + if (!__lldpctl_atom_set_str_config(c, &config.c_hostname, + &c->config->c_hostname, value)) + return NULL; + break; + default: + SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST); + return NULL; + } + + if (asprintf(&canary, "%d%s", key, value ? value : "(NULL)") == -1) { + SET_ERROR(atom->conn, LLDPCTL_ERR_NOMEM); + return NULL; + } + rc = _lldpctl_do_something(atom->conn, CONN_STATE_SET_CONFIG_SEND, + CONN_STATE_SET_CONFIG_RECV, canary, SET_CONFIG, &config, + &MARSHAL_INFO(lldpd_config), NULL, NULL); + free(canary); + if (rc == 0) return atom; + +#undef SET_STR + + return NULL; +} + +static long int +_lldpctl_atom_get_int_config(lldpctl_atom_t *atom, lldpctl_key_t key) +{ + struct _lldpctl_atom_config_t *c = (struct _lldpctl_atom_config_t *)atom; + switch (key) { + case lldpctl_k_config_paused: + return c->config->c_paused; + case lldpctl_k_config_tx_interval: + return (c->config->c_tx_interval + 999) / 1000; /* s units */ + case lldpctl_k_config_tx_interval_ms: + return c->config->c_tx_interval; /* ms units */ + case lldpctl_k_config_receiveonly: + return c->config->c_receiveonly; + case lldpctl_k_config_advertise_version: + return c->config->c_advertise_version; + case lldpctl_k_config_ifdescr_update: + return c->config->c_set_ifdescr; + case lldpctl_k_config_iface_promisc: + return c->config->c_promisc; + case lldpctl_k_config_chassis_cap_advertise: + return c->config->c_cap_advertise; + case lldpctl_k_config_chassis_cap_override: + return c->config->c_cap_override; + case lldpctl_k_config_chassis_mgmt_advertise: + return c->config->c_mgmt_advertise; +#ifdef ENABLE_LLDPMED + case lldpctl_k_config_lldpmed_noinventory: + return c->config->c_noinventory; + case lldpctl_k_config_fast_start_enabled: + return c->config->c_enable_fast_start; + case lldpctl_k_config_fast_start_interval: + return c->config->c_tx_fast_interval; +#endif + case lldpctl_k_config_tx_hold: + return c->config->c_tx_hold; + case lldpctl_k_config_max_neighbors: + return c->config->c_max_neighbors; + default: + return SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST); + } +} + +static lldpctl_atom_t * +_lldpctl_atom_set_int_config(lldpctl_atom_t *atom, lldpctl_key_t key, long int value) +{ + int rc; + char *canary = NULL; + struct _lldpctl_atom_config_t *c = (struct _lldpctl_atom_config_t *)atom; + struct lldpd_config config; + memcpy(&config, c->config, sizeof(struct lldpd_config)); + + switch (key) { + case lldpctl_k_config_paused: + config.c_paused = c->config->c_paused = value; + break; + case lldpctl_k_config_tx_interval: + config.c_tx_interval = value * 1000; + if (value > 0) c->config->c_tx_interval = value * 1000; + break; + case lldpctl_k_config_tx_interval_ms: + config.c_tx_interval = value; + if (value > 0) c->config->c_tx_interval = value; + break; + case lldpctl_k_config_ifdescr_update: + config.c_set_ifdescr = c->config->c_set_ifdescr = value; + break; + case lldpctl_k_config_iface_promisc: + config.c_promisc = c->config->c_promisc = value; + break; + case lldpctl_k_config_chassis_cap_advertise: + config.c_cap_advertise = c->config->c_cap_advertise = value; + break; + case lldpctl_k_config_chassis_cap_override: + config.c_cap_override = c->config->c_cap_override = value; + break; + case lldpctl_k_config_chassis_mgmt_advertise: + config.c_mgmt_advertise = c->config->c_mgmt_advertise = value; + break; +#ifdef ENABLE_LLDPMED + case lldpctl_k_config_fast_start_enabled: + config.c_enable_fast_start = c->config->c_enable_fast_start = value; + break; + case lldpctl_k_config_fast_start_interval: + config.c_tx_fast_interval = c->config->c_tx_fast_interval = value; + break; +#endif + case lldpctl_k_config_tx_hold: + config.c_tx_hold = value; + if (value > 0) c->config->c_tx_hold = value; + break; + case lldpctl_k_config_max_neighbors: + config.c_max_neighbors = value; + if (value > 0) c->config->c_max_neighbors = value; + break; + case lldpctl_k_config_bond_slave_src_mac_type: + config.c_bond_slave_src_mac_type = value; + c->config->c_bond_slave_src_mac_type = value; + break; + case lldpctl_k_config_lldp_portid_type: + config.c_lldp_portid_type = value; + c->config->c_lldp_portid_type = value; + break; + case lldpctl_k_config_lldp_agent_type: + config.c_lldp_agent_type = value; + c->config->c_lldp_agent_type = value; + break; + default: + SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST); + return NULL; + } + + if (asprintf(&canary, "%d%ld", key, value) == -1) { + SET_ERROR(atom->conn, LLDPCTL_ERR_NOMEM); + return NULL; + } + rc = _lldpctl_do_something(atom->conn, CONN_STATE_SET_CONFIG_SEND, + CONN_STATE_SET_CONFIG_RECV, canary, SET_CONFIG, &config, + &MARSHAL_INFO(lldpd_config), NULL, NULL); + free(canary); + if (rc == 0) return atom; + return NULL; +} + +static struct atom_builder config = { atom_config, + sizeof(struct _lldpctl_atom_config_t), .init = _lldpctl_atom_new_config, + .free = _lldpctl_atom_free_config, .get_str = _lldpctl_atom_get_str_config, + .set_str = _lldpctl_atom_set_str_config, + .get_int = _lldpctl_atom_get_int_config, + .set_int = _lldpctl_atom_set_int_config }; + +ATOM_BUILDER_REGISTER(config, 1); diff --git a/src/lib/atoms/custom.c b/src/lib/atoms/custom.c new file mode 100644 index 0000000..ffa5b03 --- /dev/null +++ b/src/lib/atoms/custom.c @@ -0,0 +1,226 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2015 Vincent Bernat <vincent@bernat.im> + * Copyright (c) 2015 Alexandru Ardelean <ardeleanalex@gmail.com> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <arpa/inet.h> + +#include "../lldpctl.h" +#include "../../log.h" +#include "../atom.h" +#include "../helpers.h" + +#ifdef ENABLE_CUSTOM + +# define min(x, y) ((x > y) ? y : x) + +static lldpctl_atom_iter_t * +_lldpctl_atom_iter_custom_list(lldpctl_atom_t *atom) +{ + struct _lldpctl_atom_custom_list_t *custom = + (struct _lldpctl_atom_custom_list_t *)atom; + return (lldpctl_atom_iter_t *)TAILQ_FIRST(&custom->parent->port->p_custom_list); +} + +static lldpctl_atom_iter_t * +_lldpctl_atom_next_custom_list(lldpctl_atom_t *atom, lldpctl_atom_iter_t *iter) +{ + return (lldpctl_atom_iter_t *)TAILQ_NEXT((struct lldpd_custom *)iter, next); +} + +static lldpctl_atom_t * +_lldpctl_atom_value_custom_list(lldpctl_atom_t *atom, lldpctl_atom_iter_t *iter) +{ + struct _lldpctl_atom_custom_list_t *custom = + (struct _lldpctl_atom_custom_list_t *)atom; + struct lldpd_custom *tlv = (struct lldpd_custom *)iter; + return _lldpctl_new_atom(atom->conn, atom_custom, custom->parent, tlv); +} + +static lldpctl_atom_t * +_lldpctl_atom_create_custom_list(lldpctl_atom_t *atom) +{ + struct _lldpctl_atom_custom_list_t *custom = + (struct _lldpctl_atom_custom_list_t *)atom; + struct lldpd_custom *tlv; + + tlv = _lldpctl_alloc_in_atom(atom, sizeof(struct lldpd_custom)); + if (!tlv) return NULL; + return _lldpctl_new_atom(atom->conn, atom_custom, custom->parent, tlv); +} + +static int +_lldpctl_atom_new_custom(lldpctl_atom_t *atom, va_list ap) +{ + struct _lldpctl_atom_custom_t *custom = (struct _lldpctl_atom_custom_t *)atom; + + custom->parent = va_arg(ap, struct _lldpctl_atom_port_t *); + custom->tlv = va_arg(ap, struct lldpd_custom *); + lldpctl_atom_inc_ref((lldpctl_atom_t *)custom->parent); + return 1; +} + +static void +_lldpctl_atom_free_custom(lldpctl_atom_t *atom) +{ + struct _lldpctl_atom_custom_t *custom = (struct _lldpctl_atom_custom_t *)atom; + lldpctl_atom_dec_ref((lldpctl_atom_t *)custom->parent); +} + +static long int +_lldpctl_atom_get_int_custom(lldpctl_atom_t *atom, lldpctl_key_t key) +{ + struct _lldpctl_atom_custom_t *custom = (struct _lldpctl_atom_custom_t *)atom; + + switch (key) { + case lldpctl_k_custom_tlv_oui_subtype: + return custom->tlv->subtype; + default: + return SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST); + } +} + +static lldpctl_atom_t * +_lldpctl_atom_set_str_custom(lldpctl_atom_t *atom, lldpctl_key_t key, const char *value) +{ + struct _lldpctl_atom_custom_t *custom = (struct _lldpctl_atom_custom_t *)atom; + + if (!value || !strlen(value)) return NULL; + + /* Only local port can be modified */ + if (!custom->parent->local) { + SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST); + return NULL; + } + + switch (key) { + case lldpctl_k_custom_tlv_op: + if (!strcmp(value, "replace")) + custom->op = CUSTOM_TLV_REPLACE; + else if (!strcmp(value, "remove")) + custom->op = CUSTOM_TLV_REMOVE; + else + custom->op = CUSTOM_TLV_ADD; + return atom; + default: + SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST); + return NULL; + } + + SET_ERROR(atom->conn, LLDPCTL_ERR_BAD_VALUE); + return NULL; +} + +static lldpctl_atom_t * +_lldpctl_atom_set_int_custom(lldpctl_atom_t *atom, lldpctl_key_t key, long int value) +{ + struct _lldpctl_atom_custom_t *custom = (struct _lldpctl_atom_custom_t *)atom; + + /* Only local port can be modified */ + if (!custom->parent->local) { + SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST); + return NULL; + } + + switch (key) { + case lldpctl_k_custom_tlv_oui_subtype: + if (value < 0 || value > 255) goto bad; + custom->tlv->subtype = value; + return atom; + default: + SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST); + return NULL; + } +bad: + SET_ERROR(atom->conn, LLDPCTL_ERR_BAD_VALUE); + return NULL; +} + +static const uint8_t * +_lldpctl_atom_get_buffer_custom(lldpctl_atom_t *atom, lldpctl_key_t key, size_t *n) +{ + struct _lldpctl_atom_custom_t *custom = (struct _lldpctl_atom_custom_t *)atom; + + switch (key) { + case lldpctl_k_custom_tlv_oui: + *n = sizeof(custom->tlv->oui); + return (const uint8_t *)&custom->tlv->oui; + case lldpctl_k_custom_tlv_oui_info_string: + *n = custom->tlv->oui_info_len; + return (const uint8_t *)custom->tlv->oui_info; + default: + SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST); + return NULL; + } +} + +static lldpctl_atom_t * +_lldpctl_atom_set_buffer_custom(lldpctl_atom_t *atom, lldpctl_key_t key, + const u_int8_t *buf, size_t n) +{ + struct _lldpctl_atom_custom_t *custom = (struct _lldpctl_atom_custom_t *)atom; + + /* Only local port can be modified */ + if (!custom->parent->local) { + SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST); + return NULL; + } + + switch (key) { + case lldpctl_k_custom_tlv_oui: + memcpy(&custom->tlv->oui, buf, min(n, sizeof(custom->tlv->oui))); + return atom; + case lldpctl_k_custom_tlv_oui_info_string: + if (n == 0 || n > LLDP_TLV_ORG_OUI_INFO_MAXLEN) { + SET_ERROR(atom->conn, LLDPCTL_ERR_BAD_VALUE); + return NULL; + } + custom->tlv->oui_info_len = n; + if (!(custom->tlv->oui_info = _lldpctl_alloc_in_atom(atom, n))) { + custom->tlv->oui_info_len = 0; + SET_ERROR(atom->conn, LLDPCTL_ERR_NOMEM); + return NULL; + } + memcpy(custom->tlv->oui_info, buf, n); + return atom; + default: + SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST); + return NULL; + } +} + +static struct atom_builder custom_list = { atom_custom_list, + sizeof(struct _lldpctl_atom_any_list_t), .init = _lldpctl_atom_new_any_list, + .free = _lldpctl_atom_free_any_list, .iter = _lldpctl_atom_iter_custom_list, + .next = _lldpctl_atom_next_custom_list, + .value = _lldpctl_atom_value_custom_list, + .create = _lldpctl_atom_create_custom_list }; + +static struct atom_builder custom = { atom_custom, + sizeof(struct _lldpctl_atom_custom_t), .init = _lldpctl_atom_new_custom, + .free = _lldpctl_atom_free_custom, .get_int = _lldpctl_atom_get_int_custom, + .set_int = _lldpctl_atom_set_int_custom, + .set_str = _lldpctl_atom_set_str_custom, + .get_buffer = _lldpctl_atom_get_buffer_custom, + .set_buffer = _lldpctl_atom_set_buffer_custom }; + +ATOM_BUILDER_REGISTER(custom_list, 22); +ATOM_BUILDER_REGISTER(custom, 23); + +#endif /* ENABLE_CUSTOM */ diff --git a/src/lib/atoms/dot1.c b/src/lib/atoms/dot1.c new file mode 100644 index 0000000..c2feda0 --- /dev/null +++ b/src/lib/atoms/dot1.c @@ -0,0 +1,250 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2015 Vincent Bernat <vincent@bernat.im> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <arpa/inet.h> + +#include "../lldpctl.h" +#include "../../log.h" +#include "../atom.h" +#include "../helpers.h" + +#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; + } +} + +static struct atom_builder vlans_list = { 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 }; + +static struct atom_builder vlan = { 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 }; + +static struct atom_builder ppvids_list = { 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 }; + +static struct atom_builder ppvid = { 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 }; + +static struct atom_builder pis_list = { 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 }; + +static struct atom_builder pi = { 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 }; + +ATOM_BUILDER_REGISTER(vlans_list, 9); +ATOM_BUILDER_REGISTER(vlan, 10); +ATOM_BUILDER_REGISTER(ppvids_list, 11); +ATOM_BUILDER_REGISTER(ppvid, 12); +ATOM_BUILDER_REGISTER(pis_list, 13); +ATOM_BUILDER_REGISTER(pi, 14); + +#endif diff --git a/src/lib/atoms/dot3.c b/src/lib/atoms/dot3.c new file mode 100644 index 0000000..f3f490a --- /dev/null +++ b/src/lib/atoms/dot3.c @@ -0,0 +1,474 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2015 Vincent Bernat <vincent@bernat.im> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <arpa/inet.h> + +#include "../lldpctl.h" +#include "../../log.h" +#include "../atom.h" +#include "../helpers.h" + +#ifdef ENABLE_DOT3 + +static lldpctl_map_t port_dot3_power_devicetype_map[] = { + { LLDP_DOT3_POWER_PSE, "PSE" }, { LLDP_DOT3_POWER_PD, "PD" }, { 0, NULL } +}; + +static lldpctl_map_t port_dot3_power_pse_source_map[] = { { LLDP_DOT3_POWER_SOURCE_BOTH, + "PSE + Local" }, + { LLDP_DOT3_POWER_SOURCE_PSE, "PSE" }, { 0, NULL } }; + +static lldpctl_map_t port_dot3_power_pd_source_map[] = { + { LLDP_DOT3_POWER_SOURCE_BACKUP, "Backup source" }, + { LLDP_DOT3_POWER_SOURCE_PRIMARY, "Primary power source" }, { 0, NULL } +}; + +static struct atom_map port_dot3_power_pairs_map = { + .key = lldpctl_k_dot3_power_pairs, + .map = { { LLDP_DOT3_POWERPAIRS_SIGNAL, "signal" }, + { LLDP_DOT3_POWERPAIRS_SPARE, "spare" }, { 0, NULL } }, +}; + +static struct atom_map port_dot3_power_class_map = { + .key = lldpctl_k_dot3_power_class, + .map = { { 1, "class 0" }, { 2, "class 1" }, { 3, "class 2" }, { 4, "class 3" }, + { 5, "class 4" }, { 0, NULL } }, +}; + +static struct atom_map port_dot3_power_priority_map = { + .key = lldpctl_k_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 }, + }, +}; + +static struct atom_map port_dot3_power_pd_4pid_map = { + .key = lldpctl_k_dot3_power_pd_4pid, + .map = { + { 0, "PD does not support powering both modes" }, + { 1, "PD supports powering both modes" }, + { 0, NULL}, + }, +}; + +static struct atom_map port_dot3_power_pse_status_map = { + .key = lldpctl_k_dot3_power_pse_status, + .map = { + { 0, "unknown" }, + { 1, "2-pair powering" }, + { 2, "4-pair powering dual-signature PD" }, + { 3, "4-pair powering single-signature PD" }, + { 0, NULL }, + }, +}; + +static struct atom_map port_dot3_power_pd_status_map = { + .key = lldpctl_k_dot3_power_pd_status, + .map = { + { 0, "unknown" }, + { 1, "2-pair powered PD" }, + { 2, "4-pair powered dual-signature PD" }, + { 3, "4-pair powered single-signature PD" }, + { 0, NULL }, + }, +}; + +static struct atom_map port_dot3_power_pse_pairs_ext_map = { + .key = lldpctl_k_dot3_power_pse_pairs_ext, + .map = { + { 0, "unknown" }, + { 1, "alternative A" }, + { 2, "alternative B" }, + { 3, "both alternatives" }, + { 0, NULL }, + }, +}; + +static struct atom_map port_dot3_power_class_a_map = { + .key = lldpctl_k_dot3_power_class_a, + .map = { + { 0, "unknown" }, + { 1, "class 1" }, + { 2, "class 2" }, + { 3, "class 3" }, + { 4, "class 4" }, + { 5, "class 5" }, + { 6, "unknown" }, + { 7, "single-signature PD or 2-pair only PSE" }, + { 0, NULL }, + }, +}; + +static struct atom_map port_dot3_power_class_b_map = { + .key = lldpctl_k_dot3_power_class_b, + .map = { + { 0, "unknown" }, + { 1, "class 1" }, + { 2, "class 2" }, + { 3, "class 3" }, + { 4, "class 4" }, + { 5, "class 5" }, + { 6, "unknown" }, + { 7, "single-signature PD or 2-pair only PSE" }, + { 0, NULL }, + }, +}; + +static struct atom_map port_dot3_power_class_ext_map = { + .key = lldpctl_k_dot3_power_class_ext, + .map = { + { 0, "unknown" }, + { 1, "class 1" }, + { 2, "class 2" }, + { 3, "class 3" }, + { 4, "class 4" }, + { 5, "class 5" }, + { 6, "class 6" }, + { 7, "class 7" }, + { 8, "class 8" }, + { 9, "unknown" }, + { 10, "unknown" }, + { 11, "unknown" }, + { 12, "unknown" }, + { 13, "unknown" }, + { 14, "unknown" }, + { 15, "dual-signature PD" }, + { 0, NULL }, + }, +}; + +static struct atom_map port_dot3_power_type_ext_map = { + .key = lldpctl_k_dot3_power_type_ext, + .map = { + { LLDP_DOT3_POWER_8023BT_OFF, "802.3bt off" }, + { 1, "type 3 PSE" }, + { 2, "type 4 PSE" }, + { 3, "type 3 single-signature PD" }, + { 4, "type 3 dual-signature PD" }, + { 5, "type 4 single-signature PD" }, + { 6, "type 4 dual-signature PD" }, + { 7, "unknown" }, + { 8, "unknown" }, + { 0, NULL }, + }, +}; + +static struct atom_map port_dot3_power_pd_load_map = { + .key = lldpctl_k_dot3_power_pd_load, + .map = { + { 0, "PD is single- or dual-signature and power is not " + "electrically isolated" }, + { 1, "PD is dual-signature and power is electrically " + "isolated" }, + { 0, NULL }, + }, +}; + +ATOM_MAP_REGISTER(port_dot3_power_pairs_map, 4); +ATOM_MAP_REGISTER(port_dot3_power_class_map, 5); +ATOM_MAP_REGISTER(port_dot3_power_priority_map, 6); + +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.map, port->p_power.pairs); + case lldpctl_k_dot3_power_class: + return map_lookup(port_dot3_power_class_map.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.map, + port->p_power.priority); + case lldpctl_k_dot3_power_pd_4pid: + return map_lookup(port_dot3_power_pd_4pid_map.map, + port->p_power.pd_4pid); + case lldpctl_k_dot3_power_pse_status: + return map_lookup(port_dot3_power_pse_status_map.map, + port->p_power.pse_status); + case lldpctl_k_dot3_power_pd_status: + return map_lookup(port_dot3_power_pd_status_map.map, + port->p_power.pd_status); + case lldpctl_k_dot3_power_pse_pairs_ext: + return map_lookup(port_dot3_power_pse_pairs_ext_map.map, + port->p_power.pse_pairs_ext); + case lldpctl_k_dot3_power_class_a: + return map_lookup(port_dot3_power_class_a_map.map, + port->p_power.class_a); + case lldpctl_k_dot3_power_class_b: + return map_lookup(port_dot3_power_class_b_map.map, + port->p_power.class_b); + case lldpctl_k_dot3_power_class_ext: + return map_lookup(port_dot3_power_class_ext_map.map, + port->p_power.class_ext); + case lldpctl_k_dot3_power_type_ext: + return map_lookup(port_dot3_power_type_ext_map.map, + port->p_power.type_ext); + case lldpctl_k_dot3_power_pd_load: + return map_lookup(port_dot3_power_pd_load_map.map, + port->p_power.pd_load); + 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; + /* 802.3bt additions */ + case lldpctl_k_dot3_power_pd_4pid: + return port->p_power.pd_4pid; + case lldpctl_k_dot3_power_requested_a: + return port->p_power.requested_a * 100; + case lldpctl_k_dot3_power_requested_b: + return port->p_power.requested_b * 100; + case lldpctl_k_dot3_power_allocated_a: + return port->p_power.allocated_a * 100; + case lldpctl_k_dot3_power_allocated_b: + return port->p_power.allocated_b * 100; + case lldpctl_k_dot3_power_pse_status: + return port->p_power.pse_status; + case lldpctl_k_dot3_power_pd_status: + return port->p_power.pd_status; + case lldpctl_k_dot3_power_pse_pairs_ext: + return port->p_power.pse_pairs_ext; + case lldpctl_k_dot3_power_class_a: + return port->p_power.class_a; + case lldpctl_k_dot3_power_class_b: + return port->p_power.class_b; + case lldpctl_k_dot3_power_class_ext: + return port->p_power.class_ext; + case lldpctl_k_dot3_power_type_ext: + return port->p_power.type_ext; + case lldpctl_k_dot3_power_pd_load: + return port->p_power.pd_load; + case lldpctl_k_dot3_power_pse_max: + return port->p_power.pse_max * 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->local) { + 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.map, value)); + case lldpctl_k_dot3_power_class: + return _lldpctl_atom_set_int_dot3_power(atom, key, + map_reverse_lookup(port_dot3_power_class_map.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.map, value)); + default: + SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST); + return NULL; + } +} + +static struct atom_builder dot3_power = { 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 }; + +ATOM_BUILDER_REGISTER(dot3_power, 8); + +#endif diff --git a/src/lib/atoms/interface.c b/src/lib/atoms/interface.c new file mode 100644 index 0000000..95cae3a --- /dev/null +++ b/src/lib/atoms/interface.c @@ -0,0 +1,118 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2015 Vincent Bernat <vincent@bernat.im> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <arpa/inet.h> + +#include "../lldpctl.h" +#include "../../log.h" +#include "../atom.h" +#include "../helpers.h" + +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 struct atom_builder interfaces_list = { 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 }; + +static struct atom_builder interface = { 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_BUILDER_REGISTER(interfaces_list, 2); +ATOM_BUILDER_REGISTER(interface, 3); diff --git a/src/lib/atoms/med.c b/src/lib/atoms/med.c new file mode 100644 index 0000000..c1e3234 --- /dev/null +++ b/src/lib/atoms/med.c @@ -0,0 +1,1123 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2015 Vincent Bernat <vincent@bernat.im> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <arpa/inet.h> + +#include "../lldpctl.h" +#include "../../log.h" +#include "../atom.h" +#include "../helpers.h" +#include "../fixedpoint.h" + +#ifdef ENABLE_LLDPMED + +static lldpctl_map_t port_med_location_map[] = { + { LLDP_MED_LOCFORMAT_COORD, "Coordinates" }, + { LLDP_MED_LOCFORMAT_CIVIC, "Civic address" }, + { LLDP_MED_LOCFORMAT_ELIN, "ELIN" }, + { 0, NULL }, +}; + +static lldpctl_map_t port_med_pow_devicetype_map[] = { + { LLDP_MED_POW_TYPE_PSE, "PSE" }, + { LLDP_MED_POW_TYPE_PD, "PD" }, + { 0, NULL }, +}; + +static lldpctl_map_t 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 lldpctl_map_t 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 struct atom_map port_med_geoid_map = { + .key = lldpctl_k_med_location_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 struct atom_map civic_address_type_map = { + .key = lldpctl_k_med_civicaddress_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 struct atom_map port_med_policy_map = { .key = lldpctl_k_med_policy_type, + .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 struct atom_map port_med_policy_prio_map = { + .key = lldpctl_k_med_policy_priority, + .map = { + { 1, "Background" }, + { 0, "Best effort" }, + { 2, "Excellent effort" }, + { 3, "Critical applications" }, + { 4, "Video" }, + { 5, "Voice" }, + { 6, "Internetwork control" }, + { 7, "Network control" }, + { 0, NULL }, + }, +}; + +static struct atom_map port_med_pow_priority_map = { + .key = lldpctl_k_med_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 }, + }, +}; + +ATOM_MAP_REGISTER(port_med_geoid_map, 7); +ATOM_MAP_REGISTER(civic_address_type_map, 8); +ATOM_MAP_REGISTER(port_med_policy_map, 9); +ATOM_MAP_REGISTER(port_med_policy_prio_map, 10); +ATOM_MAP_REGISTER(port_med_pow_priority_map, 11); + +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->local) { + 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.map, m->policy->type); + case lldpctl_k_med_policy_priority: + return map_lookup(port_med_policy_prio_map.map, m->policy->priority); + default: + SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST); + return NULL; + } +} + +static lldpctl_atom_t * +_lldpctl_atom_set_str_med_policy(lldpctl_atom_t *atom, lldpctl_key_t key, + const char *value) +{ + /* Local and remote port */ + switch (key) { + case lldpctl_k_med_policy_type: + return _lldpctl_atom_set_int_med_policy(atom, key, + map_reverse_lookup(port_med_policy_map.map, value)); + case lldpctl_k_med_policy_priority: + return _lldpctl_atom_set_int_med_policy(atom, key, + map_reverse_lookup(port_med_policy_prio_map.map, value)); + 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->local) { + 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; + 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; + 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; + free(mloc->location->data); + 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] &= 0x0f; + 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 * +read_fixed_precision(lldpctl_atom_t *atom, char *buffer, unsigned shift, + unsigned intbits, unsigned fltbits, const char *suffix) +{ + struct fp_number fp = + fp_buftofp((unsigned char *)buffer, intbits, fltbits, shift); + char *result = fp_fptostr(fp, suffix); + if (result == NULL) { + SET_ERROR(atom->conn, LLDPCTL_ERR_NOMEM); + return NULL; + } + + size_t len = strlen(result) + 1; + char *stored = _lldpctl_alloc_in_atom(atom, len); + if (stored == NULL) { + free(result); + return NULL; + } + strlcpy(stored, result, len); + free(result); + return stored; +} + +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; + + /* 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.map, m->location->data[15]); + case lldpctl_k_med_location_latitude: + if (m->location->format != LLDP_MED_LOCFORMAT_COORD) break; + return read_fixed_precision(atom, m->location->data, 0, 9, 25, "NS"); + case lldpctl_k_med_location_longitude: + if (m->location->format != LLDP_MED_LOCFORMAT_COORD) break; + return read_fixed_precision(atom, m->location->data, 40, 9, 25, "EW"); + case lldpctl_k_med_location_altitude: + if (m->location->format != LLDP_MED_LOCFORMAT_COORD) break; + return read_fixed_precision(atom, m->location->data, 84, 22, 8, 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; + if (m->location->data_len < 4) return NULL; + 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 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; + struct fp_number fp; + char *end = NULL; + + /* Only local port can be modified */ + if (!mloc->parent->local) { + 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; + if (value) fp = fp_strtofp(value, &end, 9, 25); + if (!end) goto bad; + if (end && *end != '\0') { + if (*(end + 1) != '\0') goto bad; + if (*end == 'S') + fp = fp_negate(fp); + else if (*end != 'N') + goto bad; + } + fp_fptobuf(fp, (unsigned char *)mloc->location->data, 0); + 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; + if (value) fp = fp_strtofp(value, &end, 9, 25); + if (!end) goto bad; + if (end && *end != '\0') { + if (*(end + 1) != '\0') goto bad; + if (*end == 'W') + fp = fp_negate(fp); + else if (*end != 'E') + goto bad; + } + fp_fptobuf(fp, (unsigned char *)mloc->location->data, 40); + 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; + if (value) fp = fp_strtofp(value, &end, 22, 8); + if (!end || *end != '\0') goto bad; + fp_fptobuf(fp, (unsigned char *)mloc->location->data, 84); + return atom; + case lldpctl_k_med_location_altitude_unit: + if (!value) goto bad; + 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; + break; + case lldpctl_k_med_location_geoid: + return _lldpctl_atom_set_int_med_location(atom, key, + map_reverse_lookup(port_med_geoid_map.map, value)); + 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 (!value || strlen(value) != 2) goto bad; + memcpy(mloc->location->data + 2, value, 2); + return atom; + case lldpctl_k_med_location_elin: + if (!value) goto bad; + if (mloc->location->format != LLDP_MED_LOCFORMAT_ELIN) goto bad; + 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->local) { + 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; + if (plist->parent->location->data_len < 4 || + *(uint8_t *)plist->parent->location->data < 3 || + !(iter = _lldpctl_alloc_in_atom(atom, sizeof(struct ca_iter)))) + return NULL; + iter->data = (uint8_t *)plist->parent->location->data + 4; + iter->data_len = *(uint8_t *)plist->parent->location->data - 3; + 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 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->local) { + 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 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.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; + size_t len; + + /* Only local port can be modified */ + if (!el->parent->parent->local) { + SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST); + return NULL; + } + + switch (key) { + case lldpctl_k_med_civicaddress_value: + if (!value) goto bad; + len = strlen(value) + 1; + if (len > 251) goto bad; + el->value = _lldpctl_alloc_in_atom(atom, len); + if (el->value == NULL) return NULL; + strlcpy((char *)el->value, value, len); + el->len = strlen(value); + return atom; + case lldpctl_k_med_civicaddress_type: + return _lldpctl_atom_set_int_med_caelement(atom, key, + map_reverse_lookup(civic_address_type_map.map, value)); + 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.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->local) { + 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.map, value)); + default: + SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST); + return NULL; + } +} + +static struct atom_builder med_policies_list = { 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 }; + +static struct atom_builder med_policy = { 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, + .set_str = _lldpctl_atom_set_str_med_policy }; + +static struct atom_builder med_locations_list = { 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 }; + +static struct atom_builder med_location = { 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 }; + +static struct atom_builder med_caelements_list = { 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 }; + +static struct atom_builder med_caelement = { 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 }; + +static struct atom_builder med_power = { 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 }; + +ATOM_BUILDER_REGISTER(med_policies_list, 15); +ATOM_BUILDER_REGISTER(med_policy, 16); +ATOM_BUILDER_REGISTER(med_locations_list, 17); +ATOM_BUILDER_REGISTER(med_location, 18); +ATOM_BUILDER_REGISTER(med_caelements_list, 19); +ATOM_BUILDER_REGISTER(med_caelement, 20); +ATOM_BUILDER_REGISTER(med_power, 21); + +#endif diff --git a/src/lib/atoms/mgmt.c b/src/lib/atoms/mgmt.c new file mode 100644 index 0000000..095de76 --- /dev/null +++ b/src/lib/atoms/mgmt.c @@ -0,0 +1,149 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2015 Vincent Bernat <vincent@bernat.im> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <arpa/inet.h> + +#include "../lldpctl.h" +#include "../../log.h" +#include "../atom.h" +#include "../helpers.h" + +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, lldpctl_atom_t *); + plist->chassis = va_arg(ap, struct lldpd_chassis *); + lldpctl_atom_inc_ref(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(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, lldpctl_atom_t *); + mgmt->mgmt = va_arg(ap, struct lldpd_mgmt *); + lldpctl_atom_inc_ref(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(mgmt->parent); +} + +static long int +_lldpctl_atom_get_int_mgmt(lldpctl_atom_t *atom, lldpctl_key_t key) +{ + struct _lldpctl_atom_mgmt_t *m = (struct _lldpctl_atom_mgmt_t *)atom; + + /* Local and remote port */ + switch (key) { + case lldpctl_k_mgmt_iface_index: + return m->mgmt->m_iface; + default: + return SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST); + } +} + +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; +} + +static struct atom_builder mgmts_list = { 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 }; + +static struct atom_builder mgmt = { atom_mgmt, sizeof(struct _lldpctl_atom_mgmt_t), + .init = _lldpctl_atom_new_mgmt, .free = _lldpctl_atom_free_mgmt, + .get_int = _lldpctl_atom_get_int_mgmt, .get_str = _lldpctl_atom_get_str_mgmt }; + +ATOM_BUILDER_REGISTER(mgmts_list, 6); +ATOM_BUILDER_REGISTER(mgmt, 7); diff --git a/src/lib/atoms/port.c b/src/lib/atoms/port.c new file mode 100644 index 0000000..f99e18b --- /dev/null +++ b/src/lib/atoms/port.c @@ -0,0 +1,862 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2015 Vincent Bernat <vincent@bernat.im> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <arpa/inet.h> + +#include "../lldpctl.h" +#include "../../log.h" +#include "../atom.h" +#include "../helpers.h" + +static struct atom_map lldpd_protocol_map = { .key = lldpctl_k_port_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 }, + } }; + +ATOM_MAP_REGISTER(lldpd_protocol_map, 3); + +static lldpctl_map_t 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 }, +}; + +static struct atom_map port_status_map = { .key = lldpctl_k_port_status, + .map = { + { LLDPD_RXTX_TXONLY, "TX only" }, + { LLDPD_RXTX_RXONLY, "RX only" }, + { LLDPD_RXTX_DISABLED, "disabled" }, + { LLDPD_RXTX_BOTH, "RX and TX" }, + { 0, NULL }, + } }; + +ATOM_MAP_REGISTER(port_status_map, 3); + +#ifdef ENABLE_DOT3 +static lldpctl_map_t operational_mau_type_values[] = { + { LLDP_DOT3_MAU_AUI, "AUI - No internal MAU, view from AUI" }, + { LLDP_DOT3_MAU_10BASE5, "10Base5 - Thick coax MAU" }, + { LLDP_DOT3_MAU_FOIRL, "Foirl - FOIRL MAU" }, + { LLDP_DOT3_MAU_10BASE2, "10Base2 - Thin coax MAU" }, + { LLDP_DOT3_MAU_10BASET, "10BaseT - UTP MAU" }, + { LLDP_DOT3_MAU_10BASEFP, "10BaseFP - Passive fiber MAU" }, + { LLDP_DOT3_MAU_10BASEFB, "10BaseFB - Sync fiber MAU" }, + { LLDP_DOT3_MAU_10BASEFL, "10BaseFL - Async fiber MAU" }, + { LLDP_DOT3_MAU_10BROAD36, "10Broad36 - Broadband DTE MAU" }, + { LLDP_DOT3_MAU_10BASETHD, "10BaseTHD - UTP MAU, half duplex mode" }, + { LLDP_DOT3_MAU_10BASETFD, "10BaseTFD - UTP MAU, full duplex mode" }, + { LLDP_DOT3_MAU_10BASEFLHD, "10BaseFLHD - Async fiber MAU, half duplex mode" }, + { LLDP_DOT3_MAU_10BASEFLFD, "10BaseFLDF - Async fiber MAU, full duplex mode" }, + { LLDP_DOT3_MAU_100BASET4, "100BaseT4 - 4 pair category 3 UTP" }, + { LLDP_DOT3_MAU_100BASETXHD, + "100BaseTXHD - 2 pair category 5 UTP, half duplex mode" }, + { LLDP_DOT3_MAU_100BASETXFD, + "100BaseTXFD - 2 pair category 5 UTP, full duplex mode" }, + { LLDP_DOT3_MAU_100BASEFXHD, + "100BaseFXHD - X fiber over PMT, half duplex mode" }, + { LLDP_DOT3_MAU_100BASEFXFD, + "100BaseFXFD - X fiber over PMT, full duplex mode" }, + { LLDP_DOT3_MAU_100BASET2HD, + "100BaseT2HD - 2 pair category 3 UTP, half duplex mode" }, + { LLDP_DOT3_MAU_100BASET2FD, + "100BaseT2FD - 2 pair category 3 UTP, full duplex mode" }, + { LLDP_DOT3_MAU_1000BASEXHD, + "1000BaseXHD - PCS/PMA, unknown PMD, half duplex mode" }, + { LLDP_DOT3_MAU_1000BASEXFD, + "1000BaseXFD - PCS/PMA, unknown PMD, full duplex mode" }, + { LLDP_DOT3_MAU_1000BASELXHD, + "1000BaseLXHD - Fiber over long-wavelength laser, half duplex mode" }, + { LLDP_DOT3_MAU_1000BASELXFD, + "1000BaseLXFD - Fiber over long-wavelength laser, full duplex mode" }, + { LLDP_DOT3_MAU_1000BASESXHD, + "1000BaseSXHD - Fiber over short-wavelength laser, half duplex mode" }, + { LLDP_DOT3_MAU_1000BASESXFD, + "1000BaseSXFD - Fiber over short-wavelength laser, full duplex mode" }, + { LLDP_DOT3_MAU_1000BASECXHD, + "1000BaseCXHD - Copper over 150-Ohm balanced cable, half duplex mode" }, + { LLDP_DOT3_MAU_1000BASECXFD, + "1000BaseCXFD - Copper over 150-Ohm balanced cable, full duplex mode" }, + { LLDP_DOT3_MAU_1000BASETHD, + "1000BaseTHD - Four-pair Category 5 UTP, half duplex mode" }, + { LLDP_DOT3_MAU_1000BASETFD, + "1000BaseTFD - Four-pair Category 5 UTP, full duplex mode" }, + { LLDP_DOT3_MAU_10GIGBASEX, "10GigBaseX - X PCS/PMA, unknown PMD." }, + { LLDP_DOT3_MAU_10GIGBASELX4, "10GigBaseLX4 - X fiber over WWDM optics" }, + { LLDP_DOT3_MAU_10GIGBASER, "10GigBaseR - R PCS/PMA, unknown PMD." }, + { LLDP_DOT3_MAU_10GIGBASEER, "10GigBaseER - R fiber over 1550 nm optics" }, + { LLDP_DOT3_MAU_10GIGBASELR, "10GigBaseLR - R fiber over 1310 nm optics" }, + { LLDP_DOT3_MAU_10GIGBASESR, "10GigBaseSR - R fiber over 850 nm optics" }, + { LLDP_DOT3_MAU_10GIGBASEW, "10GigBaseW - W PCS/PMA, unknown PMD." }, + { LLDP_DOT3_MAU_10GIGBASEEW, "10GigBaseEW - W fiber over 1550 nm optics" }, + { LLDP_DOT3_MAU_10GIGBASELW, "10GigBaseLW - W fiber over 1310 nm optics" }, + { LLDP_DOT3_MAU_10GIGBASESW, "10GigBaseSW - W fiber over 850 nm optics" }, + { LLDP_DOT3_MAU_10GIGBASECX4, + "10GigBaseCX4 - X copper over 8 pair 100-Ohm balanced cable" }, + { LLDP_DOT3_MAU_2BASETL, + "2BaseTL - Voice grade UTP copper, up to 2700m, optional PAF" }, + { LLDP_DOT3_MAU_10PASSTS, + "10PassTS - Voice grade UTP copper, up to 750m, optional PAF" }, + { LLDP_DOT3_MAU_100BASEBX10D, + "100BaseBX10D - One single-mode fiber OLT, long wavelength, 10km" }, + { LLDP_DOT3_MAU_100BASEBX10U, + "100BaseBX10U - One single-mode fiber ONU, long wavelength, 10km" }, + { LLDP_DOT3_MAU_100BASELX10, + "100BaseLX10 - Two single-mode fibers, long wavelength, 10km" }, + { LLDP_DOT3_MAU_1000BASEBX10D, + "1000BaseBX10D - One single-mode fiber OLT, long wavelength, 10km" }, + { LLDP_DOT3_MAU_1000BASEBX10U, + "1000BaseBX10U - One single-mode fiber ONU, long wavelength, 10km" }, + { LLDP_DOT3_MAU_1000BASELX10, + "1000BaseLX10 - Two sigle-mode fiber, long wavelength, 10km" }, + { LLDP_DOT3_MAU_1000BASEPX10D, + "1000BasePX10D - One single-mode fiber EPON OLT, 10km" }, + { LLDP_DOT3_MAU_1000BASEPX10U, + "1000BasePX10U - One single-mode fiber EPON ONU, 10km" }, + { LLDP_DOT3_MAU_1000BASEPX20D, + "1000BasePX20D - One single-mode fiber EPON OLT, 20km" }, + { LLDP_DOT3_MAU_1000BASEPX20U, + "1000BasePX20U - One single-mode fiber EPON ONU, 20km" }, + { LLDP_DOT3_MAU_10GBASET, + "10GbaseT - Four-pair Category 6A or better, full duplex mode only" }, + { LLDP_DOT3_MAU_10GBASELRM, + "10GbaseLRM - R multimode fiber over 1310 nm optics" }, + { LLDP_DOT3_MAU_1000BASEKX, "1000baseKX - X backplane, full duplex mode only" }, + { LLDP_DOT3_MAU_10GBASEKX4, + "10GbaseKX4 - 4 lane X backplane, full duplex mode only" }, + { LLDP_DOT3_MAU_10GBASEKR, "10GbaseKR - R backplane, full duplex mode only" }, + { LLDP_DOT3_MAU_10G1GBASEPRXD1, + "10G1GbasePRXD1 - One single-mode fiber asymmetric-rate EPON OLT, low power budget (PRX10)" }, + { LLDP_DOT3_MAU_10G1GBASEPRXD2, + "10G1GbasePRXD2 - One single-mode fiber asymmetric-rate EPON OLT, medium power budget (PRX20)" }, + { LLDP_DOT3_MAU_10G1GBASEPRXD3, + "10G1GbasePRXD3 - One single-mode fiber asymmetric-rate EPON OLT, high power budget (PRX30)" }, + { LLDP_DOT3_MAU_10G1GBASEPRXU1, + "10G1GbasePRXU1 - One single-mode fiber asymmetric-rate EPON ONU, low power budget (PRX10)" }, + { LLDP_DOT3_MAU_10G1GBASEPRXU2, + "10G1GbasePRXU2 - One single-mode fiber asymmetric-rate EPON ONU, medium power budget (PRX20)" }, + { LLDP_DOT3_MAU_10G1GBASEPRXU3, + "10G1GbasePRXU3 - One single-mode fiber asymmetric-rate EPON ONU, high power budget (PRX30)" }, + { LLDP_DOT3_MAU_10GBASEPRD1, + "10GbasePRD1 - One single-mode fiber symmetric-rate EPON OLT, low power budget (PR10)" }, + { LLDP_DOT3_MAU_10GBASEPRD2, + "10GbasePRD2 - One single-mode fiber symmetric-rate EPON OLT, medium power budget (PR20)" }, + { LLDP_DOT3_MAU_10GBASEPRD3, + "10GbasePRD3 - One single-mode fiber symmetric-rate EPON OLT, high power budget (PR30)" }, + { LLDP_DOT3_MAU_10GBASEPRU1, + "10GbasePRU1 - One single-mode fiber symmetric-rate EPON ONU, low and medium power budget" }, + { LLDP_DOT3_MAU_10GBASEPRU3, + "10GbasePRU3 - One single-mode fiber symmetric-rate EPON ONU, high power budget (PR30)" }, + { LLDP_DOT3_MAU_40GBASEKR4, + "40GbaseKR4 - 40GBASE-R PCS/PMA over an electrical backplane" }, + { LLDP_DOT3_MAU_40GBASECR4, + "40GbaseCR4 - 40GBASE-R PCS/PMA over 4 lane shielded copper balanced cable" }, + { LLDP_DOT3_MAU_40GBASESR4, + "40GbaseSR4 - 40GBASE-R PCS/PMA over 4 lane multimode fiber" }, + { LLDP_DOT3_MAU_40GBASEFR, + "40GbaseFR - 40GBASE-R PCS/PMA over single mode fiber" }, + { LLDP_DOT3_MAU_40GBASELR4, + "40GbaseLR4 - 40GBASE-R PCS/PMA over 4 WDM lane single mode fiber" }, + { LLDP_DOT3_MAU_100GBASECR10, + "100GbaseCR10 - 100GBASE-R PCS/PMA over 10 lane shielded copper balanced cable" }, + { LLDP_DOT3_MAU_100GBASESR10, + "100GbaseSR10 - 100GBASE-R PCS/PMA over 10 lane multimode fiber" }, + { LLDP_DOT3_MAU_100GBASELR4, + "100GbaseLR4 - 100GBASE-R PCS/PMA over 4 WDM lane single mode fiber, long reach" }, + { LLDP_DOT3_MAU_100GBASEER4, + "100GbaseER4 - 100GBASE-R PCS/PMA over 4 WDM lane single mode fiber PMD, extended reach" }, + { LLDP_DOT3_MAU_1000BASET1, + "1000baseT1 - 1000BASE-T1 single balanced twisted-pair copper cabling PHY" }, + { LLDP_DOT3_MAU_1000BASEPX30D, + "1000basePX30D - One single-mode fiber EPON OLT, 20km, 1:32 split ratio" }, + { LLDP_DOT3_MAU_1000BASEPX30U, + "1000basePX30U - One single-mode fiber EPON ONU, 20km, 1:32 split ratio" }, + { LLDP_DOT3_MAU_1000BASEPX40D, + "1000basePX40D - One single-mode fiber EPON OLT, 20km, 1:64 split ratio" }, + { LLDP_DOT3_MAU_1000BASEPX40U, + "1000basePX40U - One single-mode fiber EPON ONU, 20km, 1:64 split ratio" }, + { LLDP_DOT3_MAU_10G1GBASEPRXD4, + "10G1GbasePRXD4 - One single-mode fiber asymmetric-rate EPON OLT, supporting extended power budget (PRX40)" }, + { LLDP_DOT3_MAU_10G1GBASEPRXU4, + "10G1GbasePRXU4 - One single-mode fiber asymmetric-rate EPON ONU, supporting extended power budget (PRX40)" }, + { LLDP_DOT3_MAU_10GBASEPRD4, + "10GbasePRD4 - One single-mode fiber symmetric-rate EPON OLT, supporting extended power budget (PR40)" }, + { LLDP_DOT3_MAU_10GBASEPRU4, + "10GbasePRU4 - One single-mode fiber symmetric-rate EPON ONU, supporting extended power budget (PR40)" }, + { LLDP_DOT3_MAU_25GBASECR, + "25GbaseCR - 25GBASE-R PCS/PMA over shielded balanced copper cable" }, + { LLDP_DOT3_MAU_25GBASECRS, + "25GbaseCRS - 25GBASE-R PCS/PMA over shielded balanced copper cable without RS-FEC" }, + { LLDP_DOT3_MAU_25GBASEKR, + "25GbaseKR - 25GBASE-R PCS/PMA over an electrical backplane" }, + { LLDP_DOT3_MAU_25GBASEKRS, + "25GbaseKRS - 25GBASE-R PCS/PMA over an electrical backplane without RS-FEC" }, + { LLDP_DOT3_MAU_25GBASER, "25GbaseR - 25GBASE-R PCS/PMA over undefined PMD" }, + { LLDP_DOT3_MAU_25GBASESR, + "25GbaseSR - 25GBASE-R PCS/PMA over multimode fiber" }, + { LLDP_DOT3_MAU_25GBASET, + "25GbaseT - Four-pair twisted-pair balanced copper cabling" }, + { LLDP_DOT3_MAU_40GBASEER4, + "40GbaseER4 - 40GBASE-R PCS/PMA over 4 WDM lane single mode fiber" }, + { LLDP_DOT3_MAU_40GBASER, + "40GbaseR - 40GBASE-R PCS as over undefined PMA/PMD" }, + { LLDP_DOT3_MAU_40GBASET, + "40GbaseT - Four-pair twisted-pair balanced copper cabling" }, + { LLDP_DOT3_MAU_100GBASECR4, + "100GbaseCR4 - 100GBASE-R PCS/PMA over 4 lane shielded copper balanced cable" }, + { LLDP_DOT3_MAU_100GBASEKR4, + "100GbaseKR4 - 100GBASE-R PCS/PMA over an electrical backplane" }, + { LLDP_DOT3_MAU_100GBASEKP4, + "100GbaseKP4 - 100GBASE-P PCS/PMA over an electrical backplane PMD" }, + { LLDP_DOT3_MAU_100GBASER, + "100GbaseR - 100GBASE-R Multi-lane PCS over undefined PMA/PMD" }, + { LLDP_DOT3_MAU_100GBASESR4, + "100GbaseSR4 - 100GBASE-R PCS/PMA over 4 lane multimode fiber" }, + { LLDP_DOT3_MAU_2P5GIGT, + "2p5GigT - 2.5GBASE-T Four-pair twisted-pair balanced copper cabling PHY" }, + { LLDP_DOT3_MAU_5GIGT, + "5GigT - 5GBASE-T Four-pair twisted-pair balanced copper cabling PHY" }, + { LLDP_DOT3_MAU_100BASET1, + "100baseT1 - 100BASE-T1 Single balanced twisted-pair copper cabling PHY" }, + { LLDP_DOT3_MAU_1000BASERHA, + "1000baseRHA - 1000BASE-RHA Plastic optical fiber PHY" }, + { LLDP_DOT3_MAU_1000BASERHB, + "1000baseRHB - 1000BASE-RHB Plastic optical fiber PHY" }, + { LLDP_DOT3_MAU_1000BASERHC, + "1000baseRHC - 1000BASE-RHC Plastic optical fiber PHY" }, + { LLDP_DOT3_MAU_2P5GBASEKX, + "2p5GbaseKX - 2.5GBASE-X PMD over an electrical backplane" }, + { LLDP_DOT3_MAU_2P5GBASEX, + "2p5GbaseX - 2.5GBASE-X PCS/PMA over undefined PMD" }, + { LLDP_DOT3_MAU_5GBASEKR, + "5GbaseKR - 5GBASE-KR PMD over an electrical backplane" }, + { LLDP_DOT3_MAU_5GBASER, "5GbaseR - 5GBASE-R PCS/PMA over undefined PMD" }, + { LLDP_DOT3_MAU_10GPASSXR, + "10GpassXR - Coax cable distribution network PHY continuous downstream/burst mode upstream PHY" }, + { LLDP_DOT3_MAU_25GBASELR, + "25GbaseLR - 25GBASE-R PCS/PMA over single-mode fiber PMD, with long reach" }, + { LLDP_DOT3_MAU_25GBASEER, + "25GbaseER - 25GBASE-R PCS/PMA over single-mode fiber PMD, with extended reach" }, + { LLDP_DOT3_MAU_50GBASER, + "50GbaseR - 50GBASE-R Multi-lane PCS over undefined PMA/PMD" }, + { LLDP_DOT3_MAU_50GBASECR, + "50GbaseCR - 50GBASE-R PCS/PMA over shielded copper balanced cable PMD" }, + { LLDP_DOT3_MAU_50GBASEKR, + "50GbaseKR - 50GBASE-R PCS/PMA over an electrical backplane PMD" }, + { LLDP_DOT3_MAU_50GBASESR, + "50GbaseSR - 50GBASE-R PCS/PMA over multimode fiber PMD" }, + { LLDP_DOT3_MAU_50GBASEFR, + "50GbaseFR - 50GBASE-R PCS/PMA over single mode fiber PMD with reach up to at least 2 km" }, + { LLDP_DOT3_MAU_50GBASELR, + "50GbaseLR - 50GBASE-R PCS/PMA over single mode fiber PMD with reach up to at least 10 km" }, + { LLDP_DOT3_MAU_50GBASEER, + "50GbaseER - 50GBASE-R PCS/PMA over single-mode fiber PMD with reach up to at least 40 km" }, + { LLDP_DOT3_MAU_100GBASECR2, + "100GbaseCR2 - 100GBASE-R PCS/PMA over 2 lane shielded copper balanced cable PMD" }, + { LLDP_DOT3_MAU_100GBASEKR2, + "100GbaseKR2 - 100GBASE-R PCS/PMA over an electrical backplane PMD" }, + { LLDP_DOT3_MAU_100GBASESR2, + "100GbaseSR2 - 100GBASE-R PCS/PMA over 2 lane multimode fiber PMD" }, + { LLDP_DOT3_MAU_100GBASEDR, + "100GbaseDR - 100GBASE-R PCS/PMA over single mode fiber PMD" }, + { LLDP_DOT3_MAU_200GBASER, + "200GbaseR - 200GBASE-R Multi-lane PCS over undefined PMA/PMD" }, + { LLDP_DOT3_MAU_200GBASEDR4, + "200GbaseDR4 - 200GBASE-R PCS/PMA over 4-lane single-mode fiber PMD" }, + { LLDP_DOT3_MAU_200GBASEFR4, + "200GbaseFR4 - 200GBASE-R PCS/PMA over 4 WDM lane single-mode fiber PMD with reach up to at least 2 km" }, + { LLDP_DOT3_MAU_200GBASELR4, + "200GbaseLR4 - 200GBASE-R PCS/PMA over 4 WDM lane single-mode fiber PMD with reach up to at least 10 km" }, + { LLDP_DOT3_MAU_200GBASECR4, + "200GbaseCR4 - 200GBASE-R PCS/PMA over 4 lane shielded copper balanced cable PMD" }, + { LLDP_DOT3_MAU_200GBASEKR4, + "200GbaseKR4 - 200GBASE-R PCS/PMA over an electrical backplane PMD" }, + { LLDP_DOT3_MAU_200GBASESR4, + "200GbaseSR4 - 200GBASE-R PCS/PMA over 4 lane multimode fiber PMD" }, + { LLDP_DOT3_MAU_200GBASEER4, + "200GbaseER4 - 200GBASE-R PCS/PMA over 4 WDM lane single-mode fiber PMD with reach up to at least 40 km" }, + { LLDP_DOT3_MAU_400GBASER, + "400GbaseR - 400GBASE-R Multi-lane PCS over undefined PMA/PMD" }, + { LLDP_DOT3_MAU_400GBASESR16, + "400GbaseSR16 - 400GBASE-R PCS/PMA over 16-lane multimode fiber PMD" }, + { LLDP_DOT3_MAU_400GBASEDR4, + "400GbaseDR4 - 400GBASE-R PCS/PMA over 4-lane single-mode fiber PMD" }, + { LLDP_DOT3_MAU_400GBASEFR8, + "400GbaseFR8 - 400GBASE-R PCS/PMA over 8 WDM lane single-mode fiber PMD with reach up to at least 2 km" }, + { LLDP_DOT3_MAU_400GBASELR8, + "400GbaseLR8 - 400GBASE-R PCS/PMA over 8 WDM lane single-mode fiber PMD with reach up to at least 10 km" }, + { LLDP_DOT3_MAU_400GBASEER8, + "400GbaseER8 - 400GBASE-R PCS/PMA over 8 WDM lane single-mode fiber PMD with reach up to at least 40 km" }, + { LLDP_DOT3_MAU_10BASET1L, "10baseT1L - 10BASE-T1L Single balanced pair PHY" }, + { LLDP_DOT3_MAU_10BASET1SHD, + "10baseT1SHD - 10BASE-T1S Single balanced pair PHY, half duplex mode" }, + { LLDP_DOT3_MAU_10BASET1SMD, + "10baseT1SMD - 10BASE-T1S Single balanced pair PHY, multidrop mode" }, + { LLDP_DOT3_MAU_10BASET1SFD, + "10baseT1SFD - 10BASE-T1S Single balanced pair PHY, full duplex mode" }, + { 0, NULL } +}; +#endif + +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, 0, 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->local = va_arg(ap, int); + 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); + + if (port->port) { + /* Internal atom. We are the parent, but our reference count is + * not incremented. */ + port->chassis = _lldpctl_new_atom(atom->conn, atom_chassis, + port->port->p_chassis, port, 1); + } + 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; + TAILQ_FOREACH (one_chassis, chassis_list, c_entries) { + if (one_chassis == chassis) return; + } + TAILQ_INSERT_TAIL(chassis_list, chassis, c_entries); +} + +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; + + /* Free internal chassis atom. Should be freed immediately since we + * should have the only reference. */ + lldpctl_atom_dec_ref((lldpctl_atom_t *)port->chassis); + + /* 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); + else if (!hardware && port->port) { + /* No parent, no hardware, we assume a single neighbor: one + * port, one chassis. */ + if (port->port->p_chassis) { + lldpd_chassis_cleanup(port->port->p_chassis, 1); + port->port->p_chassis = NULL; + } + lldpd_port_cleanup(port->port, 1); + free(port->port); + } + 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, NULL, 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_port_chassis: + if (port->p_chassis) { + return _lldpctl_new_atom(atom->conn, atom_chassis, + port->p_chassis, p, 0); + } + SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST); + return NULL; +#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 +#ifdef ENABLE_CUSTOM + case lldpctl_k_custom_tlvs: + return _lldpctl_new_atom(atom->conn, atom_custom_list, p); +#endif + default: + /* Compatibility: query the associated chassis too */ + if (port->p_chassis) return lldpctl_atom_get(p->chassis, key); + 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; + char *canary = NULL; + +#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 +#ifdef ENABLE_CUSTOM + struct _lldpctl_atom_custom_t *custom; +#endif + + /* Local and default port only */ + if (!p->local) { + SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST); + return NULL; + } + + set.vlan_tx_enabled = -1; + + switch (key) { + case lldpctl_k_port_id: + set.local_id = p->port->p_id; + break; + case lldpctl_k_port_descr: + set.local_descr = p->port->p_descr; + break; + case lldpctl_k_port_status: + set.rxtx = LLDPD_RXTX_FROM_PORT(p->port); + break; + case lldpctl_k_port_vlan_tx: + set.vlan_tx_tag = p->port->p_vlan_tx_tag; + set.vlan_tx_enabled = p->port->p_vlan_tx_enabled; + break; +#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 +#ifdef ENABLE_CUSTOM + case lldpctl_k_custom_tlvs_clear: + set.custom_list_clear = 1; + break; + case lldpctl_k_custom_tlv: + if (value->type != atom_custom) { + SET_ERROR(atom->conn, LLDPCTL_ERR_INCORRECT_ATOM_TYPE); + return NULL; + } + custom = (struct _lldpctl_atom_custom_t *)value; + set.custom = custom->tlv; + set.custom_tlv_op = custom->op; + break; +#endif + default: + SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST); + return NULL; + } + + char empty_str[] = ""; + set.ifname = hardware ? hardware->h_ifname : empty_str; + + if (asprintf(&canary, "%d%p%s", key, value, set.ifname) == -1) { + SET_ERROR(atom->conn, LLDPCTL_ERR_NOMEM); + return NULL; + } + rc = _lldpctl_do_something(atom->conn, CONN_STATE_SET_PORT_SEND, + CONN_STATE_SET_PORT_RECV, canary, SET_PORT, &set, + &MARSHAL_INFO(lldpd_port_set), NULL, NULL); + free(canary); + 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; + char *ipaddress = NULL; + size_t len; + + /* Local port only */ + switch (key) { + case lldpctl_k_port_name: + if (hardware != NULL) return hardware->h_ifname; + break; + case lldpctl_k_port_status: + if (p->local) + return map_lookup(port_status_map.map, + LLDPD_RXTX_FROM_PORT(port)); + break; + default: + break; + } + + if (!port) return NULL; + + /* Local and remote port */ + switch (key) { + case lldpctl_k_port_protocol: + return map_lookup(lldpd_protocol_map.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 + + default: + /* Compatibility: query the associated chassis too */ + return lldpctl_atom_get_str(p->chassis, key); + } +} + +static lldpctl_atom_t * +_lldpctl_atom_set_int_port(lldpctl_atom_t *atom, lldpctl_key_t key, long int value) +{ + struct _lldpctl_atom_port_t *p = (struct _lldpctl_atom_port_t *)atom; + struct lldpd_port *port = p->port; + + if (p->local) { + switch (key) { + case lldpctl_k_port_status: + port->p_disable_rx = !LLDPD_RXTX_RXENABLED(value); + port->p_disable_tx = !LLDPD_RXTX_TXENABLED(value); + break; + case lldpctl_k_port_vlan_tx: + if (value > -1) { + port->p_vlan_tx_tag = value; + port->p_vlan_tx_enabled = 1; + } else + port->p_vlan_tx_enabled = 0; + break; + default: + SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST); + return NULL; + } + } else { + SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST); + return NULL; + } + + return _lldpctl_atom_set_atom_port(atom, key, NULL); +} + +static lldpctl_atom_t * +_lldpctl_atom_set_str_port(lldpctl_atom_t *atom, lldpctl_key_t key, const char *value) +{ + struct _lldpctl_atom_port_t *p = (struct _lldpctl_atom_port_t *)atom; + struct lldpd_port *port = p->port; + + if (!value || !strlen(value)) return NULL; + + if (p->local) { + switch (key) { + case lldpctl_k_port_status: + return _lldpctl_atom_set_int_port(atom, key, + map_reverse_lookup(port_status_map.map, value)); + default: + break; + } + } + + switch (key) { + case lldpctl_k_port_id: + free(port->p_id); + port->p_id = strdup(value); + port->p_id_len = strlen(value); + break; + case lldpctl_k_port_descr: + free(port->p_descr); + port->p_descr = strdup(value); + break; + default: + SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST); + return NULL; + } + + return _lldpctl_atom_set_atom_port(atom, key, 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; + + /* Local port only */ + if (hardware != NULL) { + switch (key) { + case lldpctl_k_port_index: + return hardware->h_ifindex; + case lldpctl_k_tx_cnt: + return hardware->h_tx_cnt; + case lldpctl_k_rx_cnt: + return hardware->h_rx_cnt; + case lldpctl_k_rx_discarded_cnt: + return hardware->h_rx_discarded_cnt; + case lldpctl_k_rx_unrecognized_cnt: + return hardware->h_rx_unrecognized_cnt; + case lldpctl_k_ageout_cnt: + return hardware->h_ageout_cnt; + case lldpctl_k_insert_cnt: + return hardware->h_insert_cnt; + case lldpctl_k_delete_cnt: + return hardware->h_delete_cnt; + default: + break; + } + } + if (p->local) { + switch (key) { + case lldpctl_k_port_status: + return LLDPD_RXTX_FROM_PORT(port); + default: + break; + } + } + if (!port) return SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST); + + /* 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_ttl: + return port->p_ttl; + case lldpctl_k_port_id_subtype: + return port->p_id_subtype; + case lldpctl_k_port_hidden: + return port->p_hidden_in; + case lldpctl_k_port_vlan_tx: + return port->p_vlan_tx_enabled ? port->p_vlan_tx_tag : -1; +#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 + default: + /* Compatibility: query the associated chassis too */ + return lldpctl_atom_get_int(p->chassis, key); + } + 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; + + switch (key) { + case lldpctl_k_port_id: + *n = port->p_id_len; + return (uint8_t *)port->p_id; + default: + /* Compatibility: query the associated chassis too */ + return lldpctl_atom_get_buffer(p->chassis, key, n); + } +} + +static struct atom_builder ports_list = { 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 }; + +static struct atom_builder port = { 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, .set_str = _lldpctl_atom_set_str_port, + .get_int = _lldpctl_atom_get_int_port, .set_int = _lldpctl_atom_set_int_port, + .get_buffer = _lldpctl_atom_get_buf_port }; + +ATOM_BUILDER_REGISTER(ports_list, 4); +ATOM_BUILDER_REGISTER(port, 5); diff --git a/src/lib/connection.c b/src/lib/connection.c new file mode 100644 index 0000000..43f46cb --- /dev/null +++ b/src/lib/connection.c @@ -0,0 +1,306 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/un.h> + +#include "lldpctl.h" +#include "atom.h" +#include "../compat/compat.h" +#include "../ctl.h" +#include "../log.h" + +const char * +lldpctl_get_default_transport(void) +{ + return LLDPD_CTL_SOCKET; +} + +/* Connect to the remote end */ +static int +sync_connect(lldpctl_conn_t *lldpctl) +{ + return ctl_connect(lldpctl->ctlname); +} + +/* 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; + ssize_t nb; + + if (conn->fd == -1 && ((conn->fd = sync_connect(lldpctl)) == -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; +} + +/* Statically 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; + ssize_t nb; + size_t remain, offset = 0; + + if (conn->fd == -1 && ((conn->fd = sync_connect(lldpctl)) == -1)) { + lldpctl->error = LLDPCTL_ERR_CANNOT_CONNECT; + return LLDPCTL_ERR_CANNOT_CONNECT; + } + + remain = length; + do { + if ((nb = read(conn->fd, (unsigned char *)data + offset, remain)) == + -1) { + if (errno == EAGAIN || errno == EINTR) continue; + return LLDPCTL_ERR_CALLBACK_FAILURE; + } + remain -= nb; + offset += nb; + } while (remain > 0 && nb != 0); + return offset; +} + +lldpctl_conn_t * +lldpctl_new(lldpctl_send_callback send, lldpctl_recv_callback recv, void *user_data) +{ + return lldpctl_new_name(lldpctl_get_default_transport(), send, recv, user_data); +} + +lldpctl_conn_t * +lldpctl_new_name(const char *ctlname, 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; + + conn->ctlname = strdup(ctlname); + if (conn->ctlname == NULL) { + free(conn); + return NULL; + } + if (!send && !recv) { + if ((data = malloc(sizeof(struct lldpctl_conn_sync_t))) == NULL) { + free(conn->ctlname); + 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; + free(conn->ctlname); + 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; + ssize_t rc; + + if ((buffer = calloc(1, 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; +} + +static int +check_for_notification(lldpctl_conn_t *conn) +{ + struct lldpd_neighbor_change *change; + void *p; + int rc; + lldpctl_change_t type; + lldpctl_atom_t *interface = NULL, *neighbor = NULL; + rc = ctl_msg_recv_unserialized(&conn->input_buffer, &conn->input_buffer_len, + NOTIFICATION, &p, &MARSHAL_INFO(lldpd_neighbor_change)); + if (rc != 0) return rc; + change = p; + + /* We have a notification, call the callback */ + if (conn->watch_cb || conn->watch_cb2) { + switch (change->state) { + case NEIGHBOR_CHANGE_DELETED: + type = lldpctl_c_deleted; + break; + case NEIGHBOR_CHANGE_ADDED: + type = lldpctl_c_added; + break; + case NEIGHBOR_CHANGE_UPDATED: + type = lldpctl_c_updated; + break; + default: + log_warnx("control", "unknown notification type (%d)", + change->state); + goto end; + } + interface = _lldpctl_new_atom(conn, atom_interface, change->ifname); + if (interface == NULL) goto end; + neighbor = + _lldpctl_new_atom(conn, atom_port, 0, NULL, change->neighbor, NULL); + if (neighbor == NULL) goto end; + if (conn->watch_cb) + conn->watch_cb(conn, type, interface, neighbor, + conn->watch_data); + else + conn->watch_cb2(type, interface, neighbor, conn->watch_data); + conn->watch_triggered = 1; + goto end; + } + +end: + if (interface) lldpctl_atom_dec_ref(interface); + if (neighbor) + lldpctl_atom_dec_ref(neighbor); + else { + lldpd_chassis_cleanup(change->neighbor->p_chassis, 1); + lldpd_port_cleanup(change->neighbor, 1); + free(change->neighbor); + } + free(change->ifname); + free(change); + + /* Indicate if more data remains in the buffer for processing */ + 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; + + /* Read all notifications */ + while (!check_for_notification(conn)) + ; + + RESET_ERROR(conn); + + return conn->input_buffer_len; +} + +int +lldpctl_process_conn_buffer(lldpctl_conn_t *conn) +{ + int rc; + + rc = check_for_notification(conn); + + RESET_ERROR(conn); + + return rc; +} + +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 0000000..899ca6f --- /dev/null +++ b/src/lib/errors.c @@ -0,0 +1,75 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "lldpctl.h" +#include "atom.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 occurred 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); +} + +void +lldpctl_log_level(int level) +{ + if (level >= 1) log_level(level - 1); +} diff --git a/src/lib/fixedpoint.c b/src/lib/fixedpoint.c new file mode 100644 index 0000000..4df0b50 --- /dev/null +++ b/src/lib/fixedpoint.c @@ -0,0 +1,255 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2013 Vincent Bernat <bernat@luffy.cx> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <arpa/inet.h> +#include "fixedpoint.h" + +/* This is not a general purpose fixed point library. First, there is no + * arithmetic. Second, some functions assume that the total precision does not + * exceed 64 bits. + */ + +#ifdef ENABLE_LLDPMED + +# ifndef ntohll +# define ntohll(x) \ + (((u_int64_t)(ntohl((int)(((x) << 32) >> 32))) << 32) | \ + (unsigned int)ntohl(((int)((x) >> 32)))) +# endif + +/** + * Convert a string to fixed point number. + * + * @param repr String to convert. + * @param end If not NULL, will contain a pointer to the character after the + * last character used in the conversion. + * @param intbits Number of bits to represent the integer part. + * @param fltbits Number of bits to represent the float part. + * @return A fixed point number. + * + * If there is an overflow, there will be a truncation. Moreover, the fraction + * part will be rounded to the nearest possible power of two representation. The + * point will depend on the number of decimal provided with the fraction + * part. + */ +struct fp_number +fp_strtofp(const char *repr, char **end, unsigned intbits, unsigned fltbits) +{ + char *endptr = NULL, *e2; + struct fp_number result = { .integer = { 0, intbits }, + .fraction = { 0, fltbits, 0 } }; + result.integer.value = strtoll(repr, &endptr, 10); + if (result.integer.value >= (1LL << (intbits - 1))) + result.integer.value = (1LL << (intbits - 1)) - 1; + else if (result.integer.value < ~(1LL << (intbits - 1)) + 1) + result.integer.value = ~(1LL << (intbits - 1)) + 1; + if (*endptr == '.') { + long long precision = 1; + e2 = endptr + 1; + result.fraction.value = strtoll(e2, &endptr, 10); + /* Convert to a representation in power of two. Get the + * precision from the number of digits provided. This is NOT the + * value of the higher bits in the binary representation: we + * consider that if the user inputs, 0.9375, it means to + * represent anything between 0 and 0.9999 with the same + * precision. Therefore, we don't have only 4 bits of precision + * but 14. */ + while (e2++ != endptr) + precision *= 10; + result.fraction.value <<= fltbits; + result.fraction.value /= precision; + result.fraction.precision = (precision == 1) ? + 1 : + (sizeof(precision) * 8 - __builtin_clzll(precision - 1)); + if (result.fraction.precision > fltbits) + result.fraction.precision = fltbits; + } + if (end) *end = endptr; + return result; +} + +/** + * Get a string representation of a fixed point number. + * + * @param fp Fixed point number. + * @param suffix If not NULL, use the first character when positive and the + * second one when negative instead of prefixing by `-`. + * @return the string representation + * + * Since we convert from binary to decimal, we are as precise as the binary + * representation. + */ +char * +fp_fptostr(struct fp_number fp, const char *suffix) +{ + char *result = NULL; + char *frac = NULL; + int negative = (fp.integer.value < 0); + if (fp.fraction.value == 0) + frac = strdup(""); + else { + long long decimal = fp.fraction.value; + long long precision = 1; + int len = 0; + while ((1LL << fp.fraction.precision) > precision) { + precision *= 10; + len += 1; + } + /* We did round-up, when converting from decimal. We round-down + * to have some coherency. */ + precision /= 10; + len -= 1; + if (precision == 0) precision = 1; + decimal *= precision; + decimal >>= fp.fraction.bits; + if (asprintf(&frac, ".%0*llu", len, decimal) == -1) return NULL; + } + if (asprintf(&result, "%s%llu%s%c", (suffix == NULL && negative) ? "-" : "", + (negative) ? (-fp.integer.value) : fp.integer.value, frac, + (suffix && !negative) ? suffix[0] : + (suffix && negative) ? suffix[1] : + ' ') == -1) { + free(frac); + return NULL; + } + free(frac); + if (!suffix) result[strlen(result) - 1] = '\0'; + return result; +} + +/** + * Turn a fixed point number into its representation in a buffer. + * + * @param fp Fixed point number. + * @param buf Output buffer. + * @param shift Number of bits to skip at the beginning of the buffer. + * + * The representation of a fixed point number is the precision (always 6 bits + * because we assume that int part + frac part does not exceed 64 bits), the + * integer part and the fractional part. + */ +void +fp_fptobuf(struct fp_number fp, unsigned char *buf, unsigned shift) +{ + unsigned long long value = (fp.integer.value >= 0) ? + ((fp.integer.value << fp.fraction.bits) + fp.fraction.value) : + (~(((unsigned long long)(-fp.integer.value) << fp.fraction.bits) + + fp.fraction.value) + + 1); + unsigned long long ints[] = { fp.integer.bits + fp.fraction.precision, value }; + unsigned int bits[] = { 6, fp.integer.bits + fp.fraction.bits }; + + unsigned i, obit, o; + for (i = 0, obit = 8 - (shift % 8), o = shift / 8; i < 2;) { + if (obit > bits[i]) { + /* We need to clear bits that will be overwritten but do not + * touch other bits */ + if (bits[i] != 0) { + buf[o] = buf[o] & + (~((1 << obit) - 1) | + ((1 << (obit - bits[i])) - 1)); + buf[o] = buf[o] | + ((ints[i] & ((1 << bits[i]) - 1)) + << (obit - bits[i])); + obit -= bits[i]; + } + i++; + } else { + /* As in the other branch... */ + buf[o] = buf[o] & (~((1 << obit) - 1)); + buf[o] = buf[o] | + ((ints[i] >> (bits[i] - obit)) & ((1 << obit) - 1)); + bits[i] -= obit; + obit = 8; + o++; + } + } +} + +/** + * Parse a fixed point number from a buffer. + * + * @param buf Input buffer + * @param intbits Number of bits used for integer part. + * @param fltbits Number of bits used for fractional part. + * @param shift Number of bits to skip at the beginning of the buffer. + * + * @return the parsed fixed point number. + * + * The representation is the same as for @c fp_fptobuf(). + */ +struct fp_number +fp_buftofp(const unsigned char *buf, unsigned intbits, unsigned fltbits, unsigned shift) +{ + unsigned long long value = 0, precision = 0; + unsigned long long *ints[] = { &precision, &value }; + unsigned int bits[] = { 6, intbits + fltbits }; + + unsigned o, ibit, i; + for (o = 0, ibit = 8 - (shift % 8), i = shift / 8; o < 2;) { + if (ibit > bits[o]) { + if (bits[o] > 0) { + *ints[o] = *ints[o] | + ((buf[i] >> (ibit - bits[o])) & + ((1ULL << bits[o]) - 1)); + ibit -= bits[o]; + } + o++; + } else { + *ints[o] = *ints[o] | + ((buf[i] & ((1ULL << ibit) - 1)) << (bits[o] - ibit)); + bits[o] -= ibit; + ibit = 8; + i++; + } + } + + /* Don't handle too low precision */ + if (precision > intbits) + precision -= intbits; + else + precision = intbits; + + int negative = !!(value & (1ULL << (intbits + fltbits - 1))); + if (negative) value = (~value + 1) & ((1ULL << (intbits + fltbits - 1)) - 1); + struct fp_number result = { .integer = { value >> fltbits, intbits }, + .fraction = { value & ((1ULL << fltbits) - 1), fltbits, precision } }; + if (negative) result.integer.value = -result.integer.value; + + return result; +} + +/** + * Negate a fixed point number. + */ +struct fp_number +fp_negate(struct fp_number fp) +{ + unsigned intbits = fp.integer.bits; + struct fp_number result = fp; + result.integer.value = -result.integer.value; + if (result.integer.value >= (1LL << (intbits - 1))) + result.integer.value = (1LL << (intbits - 1)) - 1; + else if (result.integer.value < ~(1LL << (intbits - 1)) + 1) + result.integer.value = ~(1LL << (intbits - 1)) + 1; + return result; +} + +#endif diff --git a/src/lib/fixedpoint.h b/src/lib/fixedpoint.h new file mode 100644 index 0000000..8b4216c --- /dev/null +++ b/src/lib/fixedpoint.h @@ -0,0 +1,42 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#if HAVE_CONFIG_H +# include <config.h> +#endif + +#if !defined FIXEDPOINT_H && defined ENABLE_LLDPMED +# define FIXEDPOINT_H + +struct fp_number { + struct { + long long value; + unsigned bits; + } integer; + struct { + long long value; + unsigned bits; + unsigned precision; + } fraction; +}; +struct fp_number fp_strtofp(const char *, char **, unsigned, unsigned); +struct fp_number fp_buftofp(const unsigned char *, unsigned, unsigned, unsigned); +struct fp_number fp_negate(struct fp_number); +char *fp_fptostr(struct fp_number, const char *); +void fp_fptobuf(struct fp_number, unsigned char *, unsigned); + +#endif diff --git a/src/lib/helpers.c b/src/lib/helpers.c new file mode 100644 index 0000000..973f8f4 --- /dev/null +++ b/src/lib/helpers.c @@ -0,0 +1,78 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2015 Vincent Bernat <vincent@bernat.im> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <arpa/inet.h> + +#include "lldpctl.h" +#include "../log.h" +#include "atom.h" +#include "helpers.h" + +const char * +map_lookup(lldpctl_map_t *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"; +} + +int +map_reverse_lookup(lldpctl_map_t *list, const char *string) +{ + if (!string) return -1; + + for (unsigned int i = 0; list[i].string != NULL; i++) { + if (!strcasecmp(list[i].string, string)) return list[i].value; + } + + return -1; +} + +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; +} + +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); +} + +char * +xstrdup(const char *str) +{ + if (!str) return NULL; + return strdup(str); +} diff --git a/src/lib/helpers.h b/src/lib/helpers.h new file mode 100644 index 0000000..6f7ae0b --- /dev/null +++ b/src/lib/helpers.h @@ -0,0 +1,24 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2015 Vincent Bernat <vincent@bernat.im> + * + * 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. + */ + +const char *map_lookup(lldpctl_map_t *list, int n); +int map_reverse_lookup(lldpctl_map_t *list, const char *string); + +int _lldpctl_atom_new_any_list(lldpctl_atom_t *atom, va_list ap); +void _lldpctl_atom_free_any_list(lldpctl_atom_t *atom); + +char *xstrdup(const char *); diff --git a/src/lib/lldpctl.h b/src/lib/lldpctl.h new file mode 100644 index 0000000..5d2668f --- /dev/null +++ b/src/lib/lldpctl.h @@ -0,0 +1,1186 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef LLDPCTL_H +#define LLDPCTL_H + +/** + * @defgroup liblldpctl liblldpctl: library to interface with lldpd + * + * `liblldpctl` allows any program to convenienty query and modify the behaviour + * of a running lldpd daemon. + * + * To use this library, use `pkg-config` to get the appropriate options: + * * `pkg-config --libs lldpctl` for `LIBS` or `LDFLAGS` + * * `pkg-config --cflags lldpctl` for `CFLAGS` + * + * @warning This library is tightly coupled with lldpd. The library to use + * should be the one shipped with lldpd. Clients of the library are then tied + * by the classic API/ABI rules and may be compiled separatly. + * + * There are two important structures in this library: @c lldpctl_conn_t which + * represents a connection and @c lldpctl_atom_t which represents a piece of + * information. Those types are opaque. No direct access to them should be done. + * + * The library is expected to be reentrant and therefore thread-safe. It is + * however not expected that a connection to be used in several thread + * simultaneously. This also applies to the different pieces of information + * gathered through this connection. Several connection to lldpd can be used + * simultaneously. + * + * The first step is to establish a connection. See @ref lldpctl_connection for + * more information about this. The next step is to query the lldpd daemon. See + * @ref lldpctl_atoms on how to do this. + * + * `liblldpctl` tries to handle errors in a coherent way. Any function returning + * a pointer will return @c NULL on error and the last error can be retrieved + * through @ref lldpctl_last_error() function. Most functions returning integers + * will return a negative integer representing the error if something goes + * wrong. The use of @ref lldpctl_last_error() allows one to check if this is a + * real error if there is a doubt. See @ref lldpctl_errors_logs for more about + * this. + * + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdlib.h> +#include <stdint.h> +#include <sys/types.h> + +/** + * @defgroup lldpctl_connection Managing connection to lldpd + * + * Connection with 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. 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 + * (@ref lldpctl_recv()) or needs to be sent (@ref lldpctl_send()). + * + * Because the most common case is synchronous IO, `liblldpctl` will use classic + * synchronous IO with the Unix socket if no IO functions are provided by the + * user. For all other cases, the user must provide the appropriate functions. + * + * A connection should be allocated by using @ref lldpctl_new(). It needs to be + * released with @ref lldpctl_release(). + * + * @{ + */ + +/** + * Get default transport name. + * + * Currently, this is the default location of the Unix socket. + */ +const char *lldpctl_get_default_transport(void); + +/** + * 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 conn 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 *conn, const uint8_t *data, + size_t length, void *user_data); + +/** + * Callback function invoked to receive data from lldpd. + * + * @param conn 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 *conn, 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 conn Handle to the connection to lldpd. + * @param data Data received from lldpd. + * @param length Length of data received. + * @return The number of bytes available or a negative integer if an error has + * occurred. 0 is not an error. It usually means that a notification has + * been processed. + */ +ssize_t lldpctl_recv(lldpctl_conn_t *conn, 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 conn Handle to the connection to lldpd. + * @return The number of bytes processed or a negative integer if an error has + * occurred. + */ +ssize_t lldpctl_send(lldpctl_conn_t *conn); + +/** + * Function invoked to see if there's more data to be processed in the buffer. + * + * This function should be invoked to check for notifications in the data that + * has already been read. Its used typically for asynchronous connections. + * + * @param conn Handle to the connection to lldpd. + * @return 0 to indicate maybe more data is available for processing + * !0 to indicate no data or insufficient data for processing + */ +int lldpctl_process_conn_buffer(lldpctl_conn_t *conn); + +/** + * Allocate a new handler for connecting to lldpd. + * + * @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); + +/** + * Allocate a new handler for connecting to lldpd. + * + * @param ctlname the Unix-domain socket to connect to lldpd. + * @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_name(const char *ctlname, lldpctl_send_callback send, + lldpctl_recv_callback recv, void *user_data); + +/** + * Release resources associated with a connection to lldpd. + * + * @param conn 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 *conn); +/**@}*/ + +/** + * @defgroup lldpctl_errors_logs Errors and logs handling + * + * Error codes and logs handling. + * + * When a function returns a pointer, it may return @c NULL to indicate an error + * condition. In this case, it is possible to use @ref lldpctl_last_error() to + * get the related error code which is one of the values in @ref lldpctl_error_t + * enumeration. For display purpose @ref lldpctl_strerror() may be used to + * translate this error code. + * + * When a function returns an integer, it may return a negative value. It + * usually means this is an error but some functions may return a legitimate + * negative value (for example @ref lldpctl_atom_get_int()). When there is a + * doubt, @ref lldpctl_last_error() should be checked. + * + * An error is attached to a connection. If there is no connection, no error + * handling is available. Most functions use a connection or an atom as first + * argument and therefore are attached to a connection. To get the connection + * related to an atom, use @ref lldpctl_atom_get_connection(). + * + * Also have a look at @ref lldpctl_log_callback() function if you want a custom + * log handling. + * + * @{ + */ + +/** + * 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)); + +/** + * Setup log level. + * + * By default, liblldpctl will only log warnings. The following function allows + * to increase verbosity. This function has no effect if callbacks are + * registered with the previous function. + * + * @param level Level of verbosity (1 = warnings, 2 = info, 3 = debug). + */ +void lldpctl_log_level(int level); + +/** + * 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 conn 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 *conn); + +/** + * 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)) +/**@}*/ + +/** + * @defgroup lldpctl_atoms Extracting information: atoms + * + * Information retrieved from lldpd is represented as an atom. + * + * 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()). + * + * For some information, setters are also available: @c lldpctl_atom_set(), @c + * lldpctl_atom_set_str(), @c lldpctl_atom_set_buffer() or @c + * lldpctl_atom_set_int(). Unlike getters, some of those may require IO to + * achieve their goal. + * + * An atom is reference counted. The semantics are quite similar to Python and + * you must be careful of the ownership of a reference. It is possible to own a + * reference by calling @c lldpctl_atom_inc_ref(). Once the atom is not needed + * any more, you can abandon ownership with @c lldpctl_atom_dec_ref(). Unless + * documented otherwise, a function returning an atom will return a new + * reference (the ownership is assigned to the caller, no need to call @c + * lldpctl_atom_inc_ref()). Unless documented otherwise, when providing an atom + * to a function, the atom is usually borrowed (no change in reference + * counting). Currently, no function will steal ownership. + * + * It is quite important to use the reference counting functions + * correctly. Segfaults or memory leaks may occur otherwise. Once the reference + * count reaches 0, the atom is immediately freed. Reusing it will likely lead + * to memory corruption. + * + * @{ + */ + +/** + * Structure representing an element (chassis, port, VLAN, ...) + * + * @see lldpctl_atom_inc_ref(), lldpctl_atom_dec_ref(). + */ +typedef struct lldpctl_atom_t lldpctl_atom_t; + +/** + * Structure representing a map from an integer to a character string. + * + * @see lldpctl_key_get_map(). + */ +typedef const struct { + int value; + const char *string; +} lldpctl_map_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); + +/** + * Possible events for a change (notification). + * + * @see lldpctl_watch_callback2 + */ +typedef enum { + lldpctl_c_deleted, /**< The neighbor has been deleted */ + lldpctl_c_updated, /**< The neighbor has been updated */ + lldpctl_c_added, /**< This is a new neighbor */ +} lldpctl_change_t; + +/** + * Callback function invoked when a change is detected. + * + * @param conn Connection with lldpd. Should not be used. + * @param type Type of change detected. + * @param interface Physical interface on which the change has happened. + * @param neighbor Changed neighbor. + * @param data Data provided when registering the callback. + * + * The provided interface and neighbor atoms are stolen by the callback: their + * reference count are decremented when the callback ends. If you want to keep a + * reference to it, be sure to increment the reference count in the callback. + * + * @warning The provided connection should not be used at all. Do not use @c + * lldpctl_atom_set_*() functions on @c interface or @c neighbor either. If you + * do, you will get a @c LLDPCTL_ERR_INVALID_STATE error. + * + * @see lldpctl_watch_callback + */ +typedef void (*lldpctl_change_callback)(lldpctl_conn_t *conn, lldpctl_change_t type, + lldpctl_atom_t *interface, lldpctl_atom_t *neighbor, void *data); + +/** + * Callback function invoked when a change is detected. + * + * @param type Type of change detected. + * @param interface Physical interface on which the change has happened. + * @param neighbor Changed neighbor. + * @param data Data provided when registering the callback. + * + * The provided interface and neighbor atoms are stolen by the callback: their + * reference count are decremented when the callback ends. If you want to keep a + * reference to it, be sure to increment the reference count in the callback. + * + * @see lldpctl_watch_callback2 + */ +typedef void (*lldpctl_change_callback2)(lldpctl_change_t type, + lldpctl_atom_t *interface, lldpctl_atom_t *neighbor, void *data); + +/** + * Register a callback to be called on changes. + * + * @param conn Connection with lldpd. + * @param cb Replace the current callback with the provided one. + * @param data Data that will be passed to the callback. + * @return 0 in case of success or -1 in case of errors. + * + * This function will register the necessity to push neighbor changes to lldpd + * and therefore will issue IO operations. The error code could then be @c + * LLDPCTL_ERR_WOULDBLOCK. + * + * @warning Once a callback is registered, the connection shouldn't be used for + * anything else than receiving notifications. If you do, you will get a @c + * LLDPCTL_ERR_INVALID_STATE error. + * + * @deprecated This function is deprecated and lldpctl_watch_callback2 should be + * used instead. + */ +int lldpctl_watch_callback(lldpctl_conn_t *conn, lldpctl_change_callback cb, void *data) + __attribute__((deprecated)); + +/** + * Register a callback to be called on changes. + * + * @param conn Connection with lldpd. + * @param cb Replace the current callback with the provided one. + * @param data Data that will be passed to the callback. + * @return 0 in case of success or -1 in case of errors. + * + * This function will register the necessity to push neighbor changes to lldpd + * and therefore will issue IO operations. The error code could then be @c + * LLDPCTL_ERR_WOULDBLOCK. + * + * @warning Once a callback is registered, the connection shouldn't be used for + * anything else than receiving notifications. If you do, you will get a @c + * LLDPCTL_ERR_INVALID_STATE error. + */ +int lldpctl_watch_callback2(lldpctl_conn_t *conn, lldpctl_change_callback2 cb, + void *data); + +/** + * Wait for the next change. + * + * @param conn Connection with lldpd. + * @return 0 on success or a negative integer in case of error. + * + * This function will return once a change has been detected. It is only useful + * as a main loop when using the builtin blocking IO mechanism. + */ +int lldpctl_watch(lldpctl_conn_t *conn); + +/** + * @defgroup liblldpctl_atom_get_special Retrieving atoms from lldpd + * + * Special access functions. + * + * Most information can be retrieved through @ref lldpctl_atom_get(), @ref + * lldpctl_atom_get_int(), @ref lldpctl_atom_get_str() or @ref + * lldpctl_atom_get_buffer() but some information can only be retrieved through + * special functions because IO operation is needed (and also, for some of them, + * because we don't have an atom yet). + * + * @{ + */ + +/** + * Retrieve global configuration of lldpd daemon. + * + * @param conn Connection with lldpd. + * @return The global configuration or @c NULL if an error happened. + * + * This function will make IO with the daemon to get the + * configuration. 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 the last error. If it is @c LLDPCTL_ERR_WOULDBLOCK, try again + * later. + */ +lldpctl_atom_t *lldpctl_get_configuration(lldpctl_conn_t *conn); + +/** + * Retrieve the list of available interfaces. + * + * @param conn 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). + * + * The list of available ports can be iterated with @ref lldpctl_atom_foreach(). + */ +lldpctl_atom_t *lldpctl_get_interfaces(lldpctl_conn_t *conn); + +/** + * Retrieve the information related to the local chassis. + * + * @param conn Previously allocated handler to a connection to lldpd. + * @return Atom related to the local chassis which may be used in subsequent functions. + * + * This function may have to do IO to get the information related to the local + * chassis. Depending on the IO mode, 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_local_chassis(lldpctl_conn_t *conn); + +/** + * 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 an interation on @c lldpctl_get_interfaces(). + * @return Atom related to this port which may be used in subsequent functions. + * + * This function may have to do IO to get the information related to the given + * port. Depending on the IO mode, 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_port(lldpctl_atom_t *port); + +/** + * Retrieve the default port information. + * + * This port contains default settings whenever a new port needs to be created. + * + * @param conn Previously allocated handler to a connection to lldpd. + * @return Atom of the default port which may be used in subsequent functions. + * + * This function may have to do IO to get the information related to the given + * port. Depending on the IO mode, 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_default_port(lldpctl_conn_t *conn); + +/**@}*/ + +/** + * 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). @c lldpctl_atom_set_str() may accept a @c + * NULL value. This case is marked with (N) and usually reset the item to the + * default value or no value. + * + * 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 as an integer 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_config_tx_interval, /**< `(I,WO)` Transmit interval. When set to -1, + it is meant to transmit now. */ + lldpctl_k_config_receiveonly, /**< `(I)` Receive only mode */ + lldpctl_k_config_mgmt_pattern, /**< `(S,WON)` Pattern to choose the management + address */ + lldpctl_k_config_iface_pattern, /**< `(S,WON)` Pattern of enabled interfaces */ + lldpctl_k_config_cid_pattern, /**< `(S)` Interface pattern to choose the chassis + ID */ + lldpctl_k_config_description, /**< `(S,WON)` Chassis description overridden */ + lldpctl_k_config_platform, /**< `(S,WON)` Platform description overridden (CDP) + */ + lldpctl_k_config_hostname, /**< `(S,WON)` System name overridden */ + lldpctl_k_config_advertise_version, /**< `(I)` Advertise version */ + lldpctl_k_config_lldpmed_noinventory, /**< `(I)` Disable LLDP-MED inventory */ + lldpctl_k_config_paused, /**< `(I,WO)` lldpd is paused */ + lldpctl_k_config_fast_start_enabled, /**< `(I,WO)` Is fast start enabled */ + lldpctl_k_config_fast_start_interval, /**< `(I,WO)` Start fast transmit interval + */ + lldpctl_k_config_ifdescr_update, /**< `(I,WO)` Enable or disable setting + interface description */ + lldpctl_k_config_iface_promisc, /**< `(I,WO)` Enable or disable promiscuous mode + on interfaces */ + lldpctl_k_config_chassis_cap_advertise, /**< `(I,WO)` Enable or disable chassis + capabilities advertisement */ + lldpctl_k_config_chassis_mgmt_advertise, /**< `(I,WO)` Enable or disable + management addresses advertisement + */ + lldpctl_k_config_cid_string, /**< `(S,WON)` User defined string for the chassis + ID */ + lldpctl_k_config_perm_iface_pattern, /**< `(S,WON)` Pattern of permanent + interfaces */ + lldpctl_k_config_tx_interval_ms, /**< `(I,WO)` Transmit interval in + milliseconds. Set to -1 to transmit now. */ + lldpctl_k_config_chassis_cap_override, /**< `(I,WO)` Override chassis + capabilities */ + + lldpctl_k_interface_name = 1000, /**< `(S)` The interface name. */ + + lldpctl_k_port_name = + 1100, /**< `(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 = 1200, + 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,WO)` The ID of this port. */ + lldpctl_k_port_descr, /**< `(S,WO)` The description of this port. */ + lldpctl_k_port_hidden, /**< `(I)` Is this port hidden (or should it be + displayed?)? */ + lldpctl_k_port_status, /**< `(IS,WO)` Operational status of this (local) port */ + lldpctl_k_port_chassis, /**< `(A)` Chassis associated to the port */ + lldpctl_k_port_ttl, /**< `(I)` TTL for port, 0 if info is attached to chassis */ + lldpctl_k_port_vlan_tx, /**< `(I,W)` VLAN tag for TX on port, -1 VLAN disabled + */ + + lldpctl_k_port_dot3_mfs = 1300, /**< `(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 = 1400, /**< `(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 */ + + /* 802.3bt additions */ + lldpctl_k_dot3_power_pd_4pid, /**< `(IS)` 802.3BT both modes supported? */ + lldpctl_k_dot3_power_requested_a, /**< `(I)` 802.3BT power value requested for + A */ + lldpctl_k_dot3_power_requested_b, /**< `(I)` 802.3BT power value requested for + B */ + lldpctl_k_dot3_power_allocated_a, /**< `(I)` 802.3BT power value allocated for + A */ + lldpctl_k_dot3_power_allocated_b, /**< `(I)` 802.3BT power value allocated for + B */ + lldpctl_k_dot3_power_pse_status, /**< `(IS)` 802.3BT PSE powering status */ + lldpctl_k_dot3_power_pd_status, /**< `(IS)` 802.3BT PD powering status */ + lldpctl_k_dot3_power_pse_pairs_ext, /**< `(IS)` 802.3BT PSE power pairs */ + lldpctl_k_dot3_power_class_a, /**< `(IS)` 802.3BT power class for A */ + lldpctl_k_dot3_power_class_b, /**< `(IS)` 802.3BT power class for B */ + lldpctl_k_dot3_power_class_ext, /**< `(IS)` 802.3BT power class */ + lldpctl_k_dot3_power_type_ext, /**< `(IS)` 802.3BT power type */ + lldpctl_k_dot3_power_pd_load, /**< `(IS)` 802.3BT dualsig isolated? */ + lldpctl_k_dot3_power_pse_max, /**< `(I)` 802.3BT maximum available power */ + + lldpctl_k_port_vlan_pvid = 1500, /**< `(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 = 1600, /**< `(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 = 1700, /**< `(AL)` List of PIDs */ + lldpctl_k_pi_id, /**< `(B)` PID value */ + + lldpctl_k_chassis_index = 1800, /**< `(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 capabilities (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_ttl, /**< Deprecated */ + + lldpctl_k_chassis_med_type = + 1900, /**< `(IS)` Chassis MED type. See `LLDP_MED_CLASS_*` */ + lldpctl_k_chassis_med_cap, /**< `(I)` Available MED capabilities. See + `LLDP_MED_CAP_*` */ + lldpctl_k_chassis_med_inventory_hw, /**< `(S,W)` LLDP MED inventory "Hardware + Revision" */ + lldpctl_k_chassis_med_inventory_sw, /**< `(S,W)` LLDP MED inventory "Software + Revision" */ + lldpctl_k_chassis_med_inventory_fw, /**< `(S,W)` LLDP MED inventory "Firmware + Revision" */ + lldpctl_k_chassis_med_inventory_sn, /**< `(S,W)` LLDP MED inventory "Serial + Number" */ + lldpctl_k_chassis_med_inventory_manuf, /**< `(S,W)` LLDP MED inventory + "Manufacturer" */ + lldpctl_k_chassis_med_inventory_model, /**< `(S,W)` LLDP MED inventory "Model" + */ + lldpctl_k_chassis_med_inventory_asset, /**< `(S,W)` LLDP MED inventory "Asset + ID" */ + + lldpctl_k_port_med_policies = + 2000, /**< `(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 = + 2100, /**< `(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 = + 2200, /**< `(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 = + 2300, /**< `(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 = 2400, /**< `(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 = 3000, /**< `(S)` IP address */ + lldpctl_k_mgmt_iface_index = 30001, /**< `(I)` Interface index */ + + lldpctl_k_tx_cnt = 4000, /**< `(I)` tx cnt. Only works for a local port. */ + lldpctl_k_rx_cnt, /**< `(I)` rx cnt. Only works for a local port. */ + lldpctl_k_rx_discarded_cnt, /**< `(I)` discarded cnt. Only works for a local + port. */ + lldpctl_k_rx_unrecognized_cnt, /**< `(I)` unrecognized cnt. Only works for a + local port. */ + lldpctl_k_ageout_cnt, /**< `(I)` ageout cnt. Only works for a local port. */ + lldpctl_k_insert_cnt, /**< `(I)` insert cnt. Only works for a local port. */ + lldpctl_k_delete_cnt, /**< `(I)` delete cnt. Only works for a local port. */ + lldpctl_k_config_tx_hold, /**< `(I,WO)` Transmit hold interval. */ + lldpctl_k_config_bond_slave_src_mac_type, /**< `(I,WO)` bond slave src mac type. + */ + lldpctl_k_config_lldp_portid_type, /**< `(I,WO)` LLDP PortID TLV Subtype */ + lldpctl_k_config_lldp_agent_type, /**< `(I,WO)` LLDP agent type */ + lldpctl_k_config_max_neighbors, /**< `(I,WO)`Maximum number of neighbors per + port. */ + + lldpctl_k_custom_tlvs = 5000, /**< `(AL)` custom TLVs */ + lldpctl_k_custom_tlvs_clear, /**< `(WO)` clear list of custom TLVs */ + lldpctl_k_custom_tlv, /**< `(AL,WO)` custom TLV **/ + lldpctl_k_custom_tlv_oui, /**< `(B,W)` custom TLV Organizationally Unique + Identifier. Default is 0 (3 bytes) */ + lldpctl_k_custom_tlv_oui_subtype, /**< `(I,W)` custom TLV subtype. Default is 0 + (1 byte) */ + lldpctl_k_custom_tlv_oui_info_string, /**< `(BS,W)` custom TLV Organizationally + Unique Identifier Information String + (up to 507 bytes) */ + lldpctl_k_custom_tlv_op, /**< `(S,W)` custom TLV operation */ + +} lldpctl_key_t; + +/** + * Get a map related to a key. + * + * Many keys expect to be written with a discrete number of values. Take for + * example @c lldpctl_k_med_civicaddress_type, it can take any integer between 1 + * and 128. However, each integer can be named. It can be useful for an + * application to get a translation between the integer that can be provided and + * a more human-readable name. This function allows to retrieve the + * corresponding map. + * + * @param key The piece of information we want a map from. + * @return The map or @c NULL if no map is available. + * + * The returned map has its last element set to 0. It is also expected that the + * string value can be used with a set operation. It will be translated to the + * integer value. + */ +lldpctl_map_t *lldpctl_key_get_map(lldpctl_key_t key); + +/** + * 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[out] length 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 uint8_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 uint8_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); + +/** + * @defgroup liblldpctl_atom_iter Iterating over atoms + * + * Iterate over atoms (lists). + * + * @{ + */ +/** + * 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 name that will be used to contain each value. + * + * This macro behaves as a for loop. Moreover, at the end of each iteration, the + * reference count of the provided value is decremented. If you need to use it + * outside of the loop, you need to increment it. + */ +#define lldpctl_atom_foreach(atom, value) \ + for (lldpctl_atom_iter_t *iter##_LINE_ = lldpctl_atom_iter(atom); \ + iter##_LINE_ && (value = lldpctl_atom_iter_value(atom, iter##_LINE_)); \ + iter##_LINE_ = lldpctl_atom_iter_next(atom, iter##_LINE_), \ + 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.map b/src/lib/lldpctl.map new file mode 100644 index 0000000..c602b64 --- /dev/null +++ b/src/lib/lldpctl.map @@ -0,0 +1,53 @@ +LIBLLDPCTL_4.9 { + global: + lldpctl_watch_callback2; +}; + +LIBLLDPCTL_4.8 { + global: + lldpctl_get_default_port; +}; + +LIBLLDPCTL_4.7 { + global: + lldpctl_get_local_chassis; +}; + +LIBLLDPCTL_4.6 { + global: + lldpctl_atom_create; + lldpctl_atom_dec_ref; + lldpctl_atom_get; + lldpctl_atom_get_buffer; + lldpctl_atom_get_connection; + lldpctl_atom_get_int; + lldpctl_atom_get_str; + lldpctl_atom_inc_ref; + lldpctl_atom_iter; + lldpctl_atom_iter_next; + lldpctl_atom_iter_value; + lldpctl_atom_set; + lldpctl_atom_set_buffer; + lldpctl_atom_set_int; + lldpctl_atom_set_str; + lldpctl_get_configuration; + lldpctl_get_default_transport; + lldpctl_get_interfaces; + lldpctl_get_port; + lldpctl_key_get_map; + lldpctl_last_error; + lldpctl_log_callback; + lldpctl_log_level; + lldpctl_new; + lldpctl_new_name; + lldpctl_process_conn_buffer; + lldpctl_recv; + lldpctl_release; + lldpctl_send; + lldpctl_strerror; + lldpctl_watch; + lldpctl_watch_callback; + + local: + *; +}; diff --git a/src/lib/lldpctl.pc.in b/src/lib/lldpctl.pc.in new file mode 100644 index 0000000..65ce1ef --- /dev/null +++ b/src/lib/lldpctl.pc.in @@ -0,0 +1,6 @@ +Name: lldpctl +Description: Library to interface with lldpd, a 802.1AB daemon +Version: @VERSION@ +URL: @PACKAGE_URL@ +Libs: -L@libdir@ -llldpctl +Cflags: -I@includedir@ |