diff options
Diffstat (limited to '')
126 files changed, 39982 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..19644f3 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,16 @@ +AM_CFLAGS = -I $(top_srcdir)/include $(LLDP_CFLAGS) +AM_CPPFLAGS = $(LLDP_CPPFLAGS) -DSYSCONFDIR='"$(sysconfdir)"' +AM_LDFLAGS = $(LLDP_LDFLAGS) + +noinst_LTLIBRARIES = libcommon-daemon-lib.la libcommon-daemon-client.la +include_HEADERS = lldp-const.h + +libcommon_daemon_lib_la_SOURCES = \ + log.c log.h version.c \ + marshal.c marshal.h \ + ctl.c ctl.h \ + lldpd-structs.c lldpd-structs.h lldp-const.h +libcommon_daemon_lib_la_LIBADD = compat/libcompat.la + +libcommon_daemon_client_la_SOURCES = log.c log.h version.c lldp-const.h +libcommon_daemon_client_la_LIBADD = compat/libcompat.la diff --git a/src/Makefile.in b/src/Makefile.in new file mode 100644 index 0000000..c04901f --- /dev/null +++ b/src/Makefile.in @@ -0,0 +1,773 @@ +# 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@ +subdir = src +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 = +LTLIBRARIES = $(noinst_LTLIBRARIES) +libcommon_daemon_client_la_DEPENDENCIES = compat/libcompat.la +am_libcommon_daemon_client_la_OBJECTS = log.lo version.lo +libcommon_daemon_client_la_OBJECTS = \ + $(am_libcommon_daemon_client_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 = +libcommon_daemon_lib_la_DEPENDENCIES = compat/libcompat.la +am_libcommon_daemon_lib_la_OBJECTS = log.lo version.lo marshal.lo \ + ctl.lo lldpd-structs.lo +libcommon_daemon_lib_la_OBJECTS = \ + $(am_libcommon_daemon_lib_la_OBJECTS) +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)/ctl.Plo \ + ./$(DEPDIR)/lldpd-structs.Plo ./$(DEPDIR)/log.Plo \ + ./$(DEPDIR)/marshal.Plo ./$(DEPDIR)/version.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 = $(libcommon_daemon_client_la_SOURCES) \ + $(libcommon_daemon_lib_la_SOURCES) +DIST_SOURCES = $(libcommon_daemon_client_la_SOURCES) \ + $(libcommon_daemon_lib_la_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +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)$(includedir)" +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 +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) -DSYSCONFDIR='"$(sysconfdir)"' +AM_LDFLAGS = $(LLDP_LDFLAGS) +noinst_LTLIBRARIES = libcommon-daemon-lib.la libcommon-daemon-client.la +include_HEADERS = lldp-const.h +libcommon_daemon_lib_la_SOURCES = \ + log.c log.h version.c \ + marshal.c marshal.h \ + ctl.c ctl.h \ + lldpd-structs.c lldpd-structs.h lldp-const.h + +libcommon_daemon_lib_la_LIBADD = compat/libcompat.la +libcommon_daemon_client_la_SOURCES = log.c log.h version.c lldp-const.h +libcommon_daemon_client_la_LIBADD = compat/libcompat.la +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.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/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/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_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): + +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}; \ + } + +libcommon-daemon-client.la: $(libcommon_daemon_client_la_OBJECTS) $(libcommon_daemon_client_la_DEPENDENCIES) $(EXTRA_libcommon_daemon_client_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libcommon_daemon_client_la_OBJECTS) $(libcommon_daemon_client_la_LIBADD) $(LIBS) + +libcommon-daemon-lib.la: $(libcommon_daemon_lib_la_OBJECTS) $(libcommon_daemon_lib_la_DEPENDENCIES) $(EXTRA_libcommon_daemon_lib_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libcommon_daemon_lib_la_OBJECTS) $(libcommon_daemon_lib_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ctl.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lldpd-structs.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/log.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/marshal.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/version.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 +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) $(HEADERS) +installdirs: + for dir in "$(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: + +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) + +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-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/ctl.Plo + -rm -f ./$(DEPDIR)/lldpd-structs.Plo + -rm -f ./$(DEPDIR)/log.Plo + -rm -f ./$(DEPDIR)/marshal.Plo + -rm -f ./$(DEPDIR)/version.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-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +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)/ctl.Plo + -rm -f ./$(DEPDIR)/lldpd-structs.Plo + -rm -f ./$(DEPDIR)/log.Plo + -rm -f ./$(DEPDIR)/marshal.Plo + -rm -f ./$(DEPDIR)/version.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 + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-generic 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-man install-pdf install-pdf-am 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 + +.PRECIOUS: Makefile + + +# 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/client/Makefile.am b/src/client/Makefile.am new file mode 100644 index 0000000..23a3225 --- /dev/null +++ b/src/client/Makefile.am @@ -0,0 +1,49 @@ +AM_CFLAGS = -I $(top_srcdir)/include $(LLDP_CFLAGS) +AM_CPPFLAGS = $(LLDP_CPPFLAGS) +AM_LDFLAGS = $(LLDP_LDFLAGS) + +sbin_PROGRAMS = lldpcli +man_MANS = lldpcli.8 +dist_man_MANS = lldpctl.8 + +install-exec-local: lldpcli + cd $(DESTDIR)$(sbindir) && rm -f lldpctl + cd $(DESTDIR)$(sbindir) && $(LN_S) lldpcli lldpctl +uninstall-local: + cd $(DESTDIR)$(sbindir) && rm -f lldpctl + +lldpcli_SOURCES = client.h lldpcli.c display.c \ + conf.c conf-med.c conf-inv.c conf-dot3.c conf-power.c \ + conf-lldp.c conf-system.c \ + commands.c show.c \ + misc.c tokenizer.c \ + utf8.c \ + writer.h text_writer.c kv_writer.c json_writer.c +lldpcli_LDADD = \ + $(top_builddir)/src/libcommon-daemon-client.la \ + $(top_builddir)/src/lib/liblldpctl.la \ + @READLINE_LIBS@ +lldpcli_CFLAGS = $(AM_CFLAGS) +lldpcli_LDFLAGS = $(AM_LDFLAGS) $(LLDP_BIN_LDFLAGS) + +if USE_XML +lldpcli_SOURCES += xml_writer.c +lldpcli_CFLAGS += @libxml2_CFLAGS@ +lldpcli_LDADD += @libxml2_LIBS@ +endif + +# Completions +bashcompletiondir = $(datadir)/bash-completion/completions +dist_bashcompletion_DATA = completion/lldpcli +zshcompletiondir = $(datadir)/zsh/site-functions +dist_zshcompletion_DATA = completion/_lldpcli + +# Default configuration +lldpdconfdir = $(sysconfdir)/lldpd.d +dist_lldpdconf_DATA = README.conf + +TEMPLATES = lldpcli.8 +EXTRA_DIST = lldpcli.8.in +CLEANFILES = $(TEMPLATES) +lldpcli.8: lldpcli.8.in +include $(top_srcdir)/edit.am diff --git a/src/client/Makefile.in b/src/client/Makefile.in new file mode 100644 index 0000000..c179404 --- /dev/null +++ b/src/client/Makefile.in @@ -0,0 +1,1276 @@ +# 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@ +sbin_PROGRAMS = lldpcli$(EXEEXT) +@USE_XML_TRUE@am__append_1 = xml_writer.c +@USE_XML_TRUE@am__append_2 = @libxml2_CFLAGS@ +@USE_XML_TRUE@am__append_3 = @libxml2_LIBS@ +subdir = src/client +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 $(dist_bashcompletion_DATA) \ + $(dist_lldpdconf_DATA) $(dist_zshcompletion_DATA) \ + $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__installdirs = "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)" \ + "$(DESTDIR)$(bashcompletiondir)" "$(DESTDIR)$(lldpdconfdir)" \ + "$(DESTDIR)$(zshcompletiondir)" +PROGRAMS = $(sbin_PROGRAMS) +am__lldpcli_SOURCES_DIST = client.h lldpcli.c display.c conf.c \ + conf-med.c conf-inv.c conf-dot3.c conf-power.c conf-lldp.c \ + conf-system.c commands.c show.c misc.c tokenizer.c utf8.c \ + writer.h text_writer.c kv_writer.c json_writer.c xml_writer.c +@USE_XML_TRUE@am__objects_1 = lldpcli-xml_writer.$(OBJEXT) +am_lldpcli_OBJECTS = lldpcli-lldpcli.$(OBJEXT) \ + lldpcli-display.$(OBJEXT) lldpcli-conf.$(OBJEXT) \ + lldpcli-conf-med.$(OBJEXT) lldpcli-conf-inv.$(OBJEXT) \ + lldpcli-conf-dot3.$(OBJEXT) lldpcli-conf-power.$(OBJEXT) \ + lldpcli-conf-lldp.$(OBJEXT) lldpcli-conf-system.$(OBJEXT) \ + lldpcli-commands.$(OBJEXT) lldpcli-show.$(OBJEXT) \ + lldpcli-misc.$(OBJEXT) lldpcli-tokenizer.$(OBJEXT) \ + lldpcli-utf8.$(OBJEXT) lldpcli-text_writer.$(OBJEXT) \ + lldpcli-kv_writer.$(OBJEXT) lldpcli-json_writer.$(OBJEXT) \ + $(am__objects_1) +lldpcli_OBJECTS = $(am_lldpcli_OBJECTS) +am__DEPENDENCIES_1 = +lldpcli_DEPENDENCIES = $(top_builddir)/src/libcommon-daemon-client.la \ + $(top_builddir)/src/lib/liblldpctl.la $(am__DEPENDENCIES_1) +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 = +lldpcli_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(lldpcli_CFLAGS) \ + $(CFLAGS) $(lldpcli_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)/lldpcli-commands.Po \ + ./$(DEPDIR)/lldpcli-conf-dot3.Po \ + ./$(DEPDIR)/lldpcli-conf-inv.Po \ + ./$(DEPDIR)/lldpcli-conf-lldp.Po \ + ./$(DEPDIR)/lldpcli-conf-med.Po \ + ./$(DEPDIR)/lldpcli-conf-power.Po \ + ./$(DEPDIR)/lldpcli-conf-system.Po ./$(DEPDIR)/lldpcli-conf.Po \ + ./$(DEPDIR)/lldpcli-display.Po \ + ./$(DEPDIR)/lldpcli-json_writer.Po \ + ./$(DEPDIR)/lldpcli-kv_writer.Po \ + ./$(DEPDIR)/lldpcli-lldpcli.Po ./$(DEPDIR)/lldpcli-misc.Po \ + ./$(DEPDIR)/lldpcli-show.Po ./$(DEPDIR)/lldpcli-text_writer.Po \ + ./$(DEPDIR)/lldpcli-tokenizer.Po ./$(DEPDIR)/lldpcli-utf8.Po \ + ./$(DEPDIR)/lldpcli-xml_writer.Po +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 = $(lldpcli_SOURCES) +DIST_SOURCES = $(am__lldpcli_SOURCES_DIST) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +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; }; \ + } +man8dir = $(mandir)/man8 +NROFF = nroff +MANS = $(dist_man_MANS) $(man_MANS) +DATA = $(dist_bashcompletion_DATA) $(dist_lldpdconf_DATA) \ + $(dist_zshcompletion_DATA) +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 = $(dist_man_MANS) $(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) +man_MANS = lldpcli.8 +dist_man_MANS = lldpctl.8 +lldpcli_SOURCES = client.h lldpcli.c display.c conf.c conf-med.c \ + conf-inv.c conf-dot3.c conf-power.c conf-lldp.c conf-system.c \ + commands.c show.c misc.c tokenizer.c utf8.c writer.h \ + text_writer.c kv_writer.c json_writer.c $(am__append_1) +lldpcli_LDADD = $(top_builddir)/src/libcommon-daemon-client.la \ + $(top_builddir)/src/lib/liblldpctl.la @READLINE_LIBS@ \ + $(am__append_3) +lldpcli_CFLAGS = $(AM_CFLAGS) $(am__append_2) +lldpcli_LDFLAGS = $(AM_LDFLAGS) $(LLDP_BIN_LDFLAGS) + +# Completions +bashcompletiondir = $(datadir)/bash-completion/completions +dist_bashcompletion_DATA = completion/lldpcli +zshcompletiondir = $(datadir)/zsh/site-functions +dist_zshcompletion_DATA = completion/_lldpcli + +# Default configuration +lldpdconfdir = $(sysconfdir)/lldpd.d +dist_lldpdconf_DATA = README.conf +TEMPLATES = lldpcli.8 +EXTRA_DIST = lldpcli.8.in +CLEANFILES = $(TEMPLATES) +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/client/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/client/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-sbinPROGRAMS: $(sbin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-sbinPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(sbindir)" && rm -f $$files + +clean-sbinPROGRAMS: + @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +lldpcli$(EXEEXT): $(lldpcli_OBJECTS) $(lldpcli_DEPENDENCIES) $(EXTRA_lldpcli_DEPENDENCIES) + @rm -f lldpcli$(EXEEXT) + $(AM_V_CCLD)$(lldpcli_LINK) $(lldpcli_OBJECTS) $(lldpcli_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lldpcli-commands.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lldpcli-conf-dot3.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lldpcli-conf-inv.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lldpcli-conf-lldp.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lldpcli-conf-med.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lldpcli-conf-power.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lldpcli-conf-system.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lldpcli-conf.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lldpcli-display.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lldpcli-json_writer.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lldpcli-kv_writer.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lldpcli-lldpcli.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lldpcli-misc.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lldpcli-show.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lldpcli-text_writer.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lldpcli-tokenizer.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lldpcli-utf8.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lldpcli-xml_writer.Po@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 $@ $< + +lldpcli-lldpcli.o: lldpcli.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-lldpcli.o -MD -MP -MF $(DEPDIR)/lldpcli-lldpcli.Tpo -c -o lldpcli-lldpcli.o `test -f 'lldpcli.c' || echo '$(srcdir)/'`lldpcli.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-lldpcli.Tpo $(DEPDIR)/lldpcli-lldpcli.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='lldpcli.c' object='lldpcli-lldpcli.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-lldpcli.o `test -f 'lldpcli.c' || echo '$(srcdir)/'`lldpcli.c + +lldpcli-lldpcli.obj: lldpcli.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-lldpcli.obj -MD -MP -MF $(DEPDIR)/lldpcli-lldpcli.Tpo -c -o lldpcli-lldpcli.obj `if test -f 'lldpcli.c'; then $(CYGPATH_W) 'lldpcli.c'; else $(CYGPATH_W) '$(srcdir)/lldpcli.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-lldpcli.Tpo $(DEPDIR)/lldpcli-lldpcli.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='lldpcli.c' object='lldpcli-lldpcli.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-lldpcli.obj `if test -f 'lldpcli.c'; then $(CYGPATH_W) 'lldpcli.c'; else $(CYGPATH_W) '$(srcdir)/lldpcli.c'; fi` + +lldpcli-display.o: display.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-display.o -MD -MP -MF $(DEPDIR)/lldpcli-display.Tpo -c -o lldpcli-display.o `test -f 'display.c' || echo '$(srcdir)/'`display.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-display.Tpo $(DEPDIR)/lldpcli-display.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='display.c' object='lldpcli-display.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-display.o `test -f 'display.c' || echo '$(srcdir)/'`display.c + +lldpcli-display.obj: display.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-display.obj -MD -MP -MF $(DEPDIR)/lldpcli-display.Tpo -c -o lldpcli-display.obj `if test -f 'display.c'; then $(CYGPATH_W) 'display.c'; else $(CYGPATH_W) '$(srcdir)/display.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-display.Tpo $(DEPDIR)/lldpcli-display.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='display.c' object='lldpcli-display.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-display.obj `if test -f 'display.c'; then $(CYGPATH_W) 'display.c'; else $(CYGPATH_W) '$(srcdir)/display.c'; fi` + +lldpcli-conf.o: conf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-conf.o -MD -MP -MF $(DEPDIR)/lldpcli-conf.Tpo -c -o lldpcli-conf.o `test -f 'conf.c' || echo '$(srcdir)/'`conf.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-conf.Tpo $(DEPDIR)/lldpcli-conf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='conf.c' object='lldpcli-conf.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-conf.o `test -f 'conf.c' || echo '$(srcdir)/'`conf.c + +lldpcli-conf.obj: conf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-conf.obj -MD -MP -MF $(DEPDIR)/lldpcli-conf.Tpo -c -o lldpcli-conf.obj `if test -f 'conf.c'; then $(CYGPATH_W) 'conf.c'; else $(CYGPATH_W) '$(srcdir)/conf.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-conf.Tpo $(DEPDIR)/lldpcli-conf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='conf.c' object='lldpcli-conf.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-conf.obj `if test -f 'conf.c'; then $(CYGPATH_W) 'conf.c'; else $(CYGPATH_W) '$(srcdir)/conf.c'; fi` + +lldpcli-conf-med.o: conf-med.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-conf-med.o -MD -MP -MF $(DEPDIR)/lldpcli-conf-med.Tpo -c -o lldpcli-conf-med.o `test -f 'conf-med.c' || echo '$(srcdir)/'`conf-med.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-conf-med.Tpo $(DEPDIR)/lldpcli-conf-med.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='conf-med.c' object='lldpcli-conf-med.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-conf-med.o `test -f 'conf-med.c' || echo '$(srcdir)/'`conf-med.c + +lldpcli-conf-med.obj: conf-med.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-conf-med.obj -MD -MP -MF $(DEPDIR)/lldpcli-conf-med.Tpo -c -o lldpcli-conf-med.obj `if test -f 'conf-med.c'; then $(CYGPATH_W) 'conf-med.c'; else $(CYGPATH_W) '$(srcdir)/conf-med.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-conf-med.Tpo $(DEPDIR)/lldpcli-conf-med.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='conf-med.c' object='lldpcli-conf-med.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-conf-med.obj `if test -f 'conf-med.c'; then $(CYGPATH_W) 'conf-med.c'; else $(CYGPATH_W) '$(srcdir)/conf-med.c'; fi` + +lldpcli-conf-inv.o: conf-inv.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-conf-inv.o -MD -MP -MF $(DEPDIR)/lldpcli-conf-inv.Tpo -c -o lldpcli-conf-inv.o `test -f 'conf-inv.c' || echo '$(srcdir)/'`conf-inv.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-conf-inv.Tpo $(DEPDIR)/lldpcli-conf-inv.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='conf-inv.c' object='lldpcli-conf-inv.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-conf-inv.o `test -f 'conf-inv.c' || echo '$(srcdir)/'`conf-inv.c + +lldpcli-conf-inv.obj: conf-inv.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-conf-inv.obj -MD -MP -MF $(DEPDIR)/lldpcli-conf-inv.Tpo -c -o lldpcli-conf-inv.obj `if test -f 'conf-inv.c'; then $(CYGPATH_W) 'conf-inv.c'; else $(CYGPATH_W) '$(srcdir)/conf-inv.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-conf-inv.Tpo $(DEPDIR)/lldpcli-conf-inv.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='conf-inv.c' object='lldpcli-conf-inv.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-conf-inv.obj `if test -f 'conf-inv.c'; then $(CYGPATH_W) 'conf-inv.c'; else $(CYGPATH_W) '$(srcdir)/conf-inv.c'; fi` + +lldpcli-conf-dot3.o: conf-dot3.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-conf-dot3.o -MD -MP -MF $(DEPDIR)/lldpcli-conf-dot3.Tpo -c -o lldpcli-conf-dot3.o `test -f 'conf-dot3.c' || echo '$(srcdir)/'`conf-dot3.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-conf-dot3.Tpo $(DEPDIR)/lldpcli-conf-dot3.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='conf-dot3.c' object='lldpcli-conf-dot3.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-conf-dot3.o `test -f 'conf-dot3.c' || echo '$(srcdir)/'`conf-dot3.c + +lldpcli-conf-dot3.obj: conf-dot3.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-conf-dot3.obj -MD -MP -MF $(DEPDIR)/lldpcli-conf-dot3.Tpo -c -o lldpcli-conf-dot3.obj `if test -f 'conf-dot3.c'; then $(CYGPATH_W) 'conf-dot3.c'; else $(CYGPATH_W) '$(srcdir)/conf-dot3.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-conf-dot3.Tpo $(DEPDIR)/lldpcli-conf-dot3.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='conf-dot3.c' object='lldpcli-conf-dot3.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-conf-dot3.obj `if test -f 'conf-dot3.c'; then $(CYGPATH_W) 'conf-dot3.c'; else $(CYGPATH_W) '$(srcdir)/conf-dot3.c'; fi` + +lldpcli-conf-power.o: conf-power.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-conf-power.o -MD -MP -MF $(DEPDIR)/lldpcli-conf-power.Tpo -c -o lldpcli-conf-power.o `test -f 'conf-power.c' || echo '$(srcdir)/'`conf-power.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-conf-power.Tpo $(DEPDIR)/lldpcli-conf-power.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='conf-power.c' object='lldpcli-conf-power.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-conf-power.o `test -f 'conf-power.c' || echo '$(srcdir)/'`conf-power.c + +lldpcli-conf-power.obj: conf-power.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-conf-power.obj -MD -MP -MF $(DEPDIR)/lldpcli-conf-power.Tpo -c -o lldpcli-conf-power.obj `if test -f 'conf-power.c'; then $(CYGPATH_W) 'conf-power.c'; else $(CYGPATH_W) '$(srcdir)/conf-power.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-conf-power.Tpo $(DEPDIR)/lldpcli-conf-power.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='conf-power.c' object='lldpcli-conf-power.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-conf-power.obj `if test -f 'conf-power.c'; then $(CYGPATH_W) 'conf-power.c'; else $(CYGPATH_W) '$(srcdir)/conf-power.c'; fi` + +lldpcli-conf-lldp.o: conf-lldp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-conf-lldp.o -MD -MP -MF $(DEPDIR)/lldpcli-conf-lldp.Tpo -c -o lldpcli-conf-lldp.o `test -f 'conf-lldp.c' || echo '$(srcdir)/'`conf-lldp.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-conf-lldp.Tpo $(DEPDIR)/lldpcli-conf-lldp.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='conf-lldp.c' object='lldpcli-conf-lldp.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-conf-lldp.o `test -f 'conf-lldp.c' || echo '$(srcdir)/'`conf-lldp.c + +lldpcli-conf-lldp.obj: conf-lldp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-conf-lldp.obj -MD -MP -MF $(DEPDIR)/lldpcli-conf-lldp.Tpo -c -o lldpcli-conf-lldp.obj `if test -f 'conf-lldp.c'; then $(CYGPATH_W) 'conf-lldp.c'; else $(CYGPATH_W) '$(srcdir)/conf-lldp.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-conf-lldp.Tpo $(DEPDIR)/lldpcli-conf-lldp.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='conf-lldp.c' object='lldpcli-conf-lldp.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-conf-lldp.obj `if test -f 'conf-lldp.c'; then $(CYGPATH_W) 'conf-lldp.c'; else $(CYGPATH_W) '$(srcdir)/conf-lldp.c'; fi` + +lldpcli-conf-system.o: conf-system.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-conf-system.o -MD -MP -MF $(DEPDIR)/lldpcli-conf-system.Tpo -c -o lldpcli-conf-system.o `test -f 'conf-system.c' || echo '$(srcdir)/'`conf-system.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-conf-system.Tpo $(DEPDIR)/lldpcli-conf-system.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='conf-system.c' object='lldpcli-conf-system.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-conf-system.o `test -f 'conf-system.c' || echo '$(srcdir)/'`conf-system.c + +lldpcli-conf-system.obj: conf-system.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-conf-system.obj -MD -MP -MF $(DEPDIR)/lldpcli-conf-system.Tpo -c -o lldpcli-conf-system.obj `if test -f 'conf-system.c'; then $(CYGPATH_W) 'conf-system.c'; else $(CYGPATH_W) '$(srcdir)/conf-system.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-conf-system.Tpo $(DEPDIR)/lldpcli-conf-system.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='conf-system.c' object='lldpcli-conf-system.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-conf-system.obj `if test -f 'conf-system.c'; then $(CYGPATH_W) 'conf-system.c'; else $(CYGPATH_W) '$(srcdir)/conf-system.c'; fi` + +lldpcli-commands.o: commands.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-commands.o -MD -MP -MF $(DEPDIR)/lldpcli-commands.Tpo -c -o lldpcli-commands.o `test -f 'commands.c' || echo '$(srcdir)/'`commands.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-commands.Tpo $(DEPDIR)/lldpcli-commands.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='commands.c' object='lldpcli-commands.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-commands.o `test -f 'commands.c' || echo '$(srcdir)/'`commands.c + +lldpcli-commands.obj: commands.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-commands.obj -MD -MP -MF $(DEPDIR)/lldpcli-commands.Tpo -c -o lldpcli-commands.obj `if test -f 'commands.c'; then $(CYGPATH_W) 'commands.c'; else $(CYGPATH_W) '$(srcdir)/commands.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-commands.Tpo $(DEPDIR)/lldpcli-commands.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='commands.c' object='lldpcli-commands.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-commands.obj `if test -f 'commands.c'; then $(CYGPATH_W) 'commands.c'; else $(CYGPATH_W) '$(srcdir)/commands.c'; fi` + +lldpcli-show.o: show.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-show.o -MD -MP -MF $(DEPDIR)/lldpcli-show.Tpo -c -o lldpcli-show.o `test -f 'show.c' || echo '$(srcdir)/'`show.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-show.Tpo $(DEPDIR)/lldpcli-show.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='show.c' object='lldpcli-show.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-show.o `test -f 'show.c' || echo '$(srcdir)/'`show.c + +lldpcli-show.obj: show.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-show.obj -MD -MP -MF $(DEPDIR)/lldpcli-show.Tpo -c -o lldpcli-show.obj `if test -f 'show.c'; then $(CYGPATH_W) 'show.c'; else $(CYGPATH_W) '$(srcdir)/show.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-show.Tpo $(DEPDIR)/lldpcli-show.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='show.c' object='lldpcli-show.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-show.obj `if test -f 'show.c'; then $(CYGPATH_W) 'show.c'; else $(CYGPATH_W) '$(srcdir)/show.c'; fi` + +lldpcli-misc.o: misc.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-misc.o -MD -MP -MF $(DEPDIR)/lldpcli-misc.Tpo -c -o lldpcli-misc.o `test -f 'misc.c' || echo '$(srcdir)/'`misc.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-misc.Tpo $(DEPDIR)/lldpcli-misc.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='misc.c' object='lldpcli-misc.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-misc.o `test -f 'misc.c' || echo '$(srcdir)/'`misc.c + +lldpcli-misc.obj: misc.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-misc.obj -MD -MP -MF $(DEPDIR)/lldpcli-misc.Tpo -c -o lldpcli-misc.obj `if test -f 'misc.c'; then $(CYGPATH_W) 'misc.c'; else $(CYGPATH_W) '$(srcdir)/misc.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-misc.Tpo $(DEPDIR)/lldpcli-misc.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='misc.c' object='lldpcli-misc.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-misc.obj `if test -f 'misc.c'; then $(CYGPATH_W) 'misc.c'; else $(CYGPATH_W) '$(srcdir)/misc.c'; fi` + +lldpcli-tokenizer.o: tokenizer.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-tokenizer.o -MD -MP -MF $(DEPDIR)/lldpcli-tokenizer.Tpo -c -o lldpcli-tokenizer.o `test -f 'tokenizer.c' || echo '$(srcdir)/'`tokenizer.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-tokenizer.Tpo $(DEPDIR)/lldpcli-tokenizer.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tokenizer.c' object='lldpcli-tokenizer.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-tokenizer.o `test -f 'tokenizer.c' || echo '$(srcdir)/'`tokenizer.c + +lldpcli-tokenizer.obj: tokenizer.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-tokenizer.obj -MD -MP -MF $(DEPDIR)/lldpcli-tokenizer.Tpo -c -o lldpcli-tokenizer.obj `if test -f 'tokenizer.c'; then $(CYGPATH_W) 'tokenizer.c'; else $(CYGPATH_W) '$(srcdir)/tokenizer.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-tokenizer.Tpo $(DEPDIR)/lldpcli-tokenizer.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tokenizer.c' object='lldpcli-tokenizer.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-tokenizer.obj `if test -f 'tokenizer.c'; then $(CYGPATH_W) 'tokenizer.c'; else $(CYGPATH_W) '$(srcdir)/tokenizer.c'; fi` + +lldpcli-utf8.o: utf8.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-utf8.o -MD -MP -MF $(DEPDIR)/lldpcli-utf8.Tpo -c -o lldpcli-utf8.o `test -f 'utf8.c' || echo '$(srcdir)/'`utf8.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-utf8.Tpo $(DEPDIR)/lldpcli-utf8.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utf8.c' object='lldpcli-utf8.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-utf8.o `test -f 'utf8.c' || echo '$(srcdir)/'`utf8.c + +lldpcli-utf8.obj: utf8.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-utf8.obj -MD -MP -MF $(DEPDIR)/lldpcli-utf8.Tpo -c -o lldpcli-utf8.obj `if test -f 'utf8.c'; then $(CYGPATH_W) 'utf8.c'; else $(CYGPATH_W) '$(srcdir)/utf8.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-utf8.Tpo $(DEPDIR)/lldpcli-utf8.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utf8.c' object='lldpcli-utf8.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-utf8.obj `if test -f 'utf8.c'; then $(CYGPATH_W) 'utf8.c'; else $(CYGPATH_W) '$(srcdir)/utf8.c'; fi` + +lldpcli-text_writer.o: text_writer.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-text_writer.o -MD -MP -MF $(DEPDIR)/lldpcli-text_writer.Tpo -c -o lldpcli-text_writer.o `test -f 'text_writer.c' || echo '$(srcdir)/'`text_writer.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-text_writer.Tpo $(DEPDIR)/lldpcli-text_writer.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='text_writer.c' object='lldpcli-text_writer.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-text_writer.o `test -f 'text_writer.c' || echo '$(srcdir)/'`text_writer.c + +lldpcli-text_writer.obj: text_writer.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-text_writer.obj -MD -MP -MF $(DEPDIR)/lldpcli-text_writer.Tpo -c -o lldpcli-text_writer.obj `if test -f 'text_writer.c'; then $(CYGPATH_W) 'text_writer.c'; else $(CYGPATH_W) '$(srcdir)/text_writer.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-text_writer.Tpo $(DEPDIR)/lldpcli-text_writer.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='text_writer.c' object='lldpcli-text_writer.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-text_writer.obj `if test -f 'text_writer.c'; then $(CYGPATH_W) 'text_writer.c'; else $(CYGPATH_W) '$(srcdir)/text_writer.c'; fi` + +lldpcli-kv_writer.o: kv_writer.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-kv_writer.o -MD -MP -MF $(DEPDIR)/lldpcli-kv_writer.Tpo -c -o lldpcli-kv_writer.o `test -f 'kv_writer.c' || echo '$(srcdir)/'`kv_writer.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-kv_writer.Tpo $(DEPDIR)/lldpcli-kv_writer.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='kv_writer.c' object='lldpcli-kv_writer.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-kv_writer.o `test -f 'kv_writer.c' || echo '$(srcdir)/'`kv_writer.c + +lldpcli-kv_writer.obj: kv_writer.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-kv_writer.obj -MD -MP -MF $(DEPDIR)/lldpcli-kv_writer.Tpo -c -o lldpcli-kv_writer.obj `if test -f 'kv_writer.c'; then $(CYGPATH_W) 'kv_writer.c'; else $(CYGPATH_W) '$(srcdir)/kv_writer.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-kv_writer.Tpo $(DEPDIR)/lldpcli-kv_writer.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='kv_writer.c' object='lldpcli-kv_writer.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-kv_writer.obj `if test -f 'kv_writer.c'; then $(CYGPATH_W) 'kv_writer.c'; else $(CYGPATH_W) '$(srcdir)/kv_writer.c'; fi` + +lldpcli-json_writer.o: json_writer.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-json_writer.o -MD -MP -MF $(DEPDIR)/lldpcli-json_writer.Tpo -c -o lldpcli-json_writer.o `test -f 'json_writer.c' || echo '$(srcdir)/'`json_writer.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-json_writer.Tpo $(DEPDIR)/lldpcli-json_writer.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='json_writer.c' object='lldpcli-json_writer.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-json_writer.o `test -f 'json_writer.c' || echo '$(srcdir)/'`json_writer.c + +lldpcli-json_writer.obj: json_writer.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-json_writer.obj -MD -MP -MF $(DEPDIR)/lldpcli-json_writer.Tpo -c -o lldpcli-json_writer.obj `if test -f 'json_writer.c'; then $(CYGPATH_W) 'json_writer.c'; else $(CYGPATH_W) '$(srcdir)/json_writer.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-json_writer.Tpo $(DEPDIR)/lldpcli-json_writer.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='json_writer.c' object='lldpcli-json_writer.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-json_writer.obj `if test -f 'json_writer.c'; then $(CYGPATH_W) 'json_writer.c'; else $(CYGPATH_W) '$(srcdir)/json_writer.c'; fi` + +lldpcli-xml_writer.o: xml_writer.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-xml_writer.o -MD -MP -MF $(DEPDIR)/lldpcli-xml_writer.Tpo -c -o lldpcli-xml_writer.o `test -f 'xml_writer.c' || echo '$(srcdir)/'`xml_writer.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-xml_writer.Tpo $(DEPDIR)/lldpcli-xml_writer.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='xml_writer.c' object='lldpcli-xml_writer.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-xml_writer.o `test -f 'xml_writer.c' || echo '$(srcdir)/'`xml_writer.c + +lldpcli-xml_writer.obj: xml_writer.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-xml_writer.obj -MD -MP -MF $(DEPDIR)/lldpcli-xml_writer.Tpo -c -o lldpcli-xml_writer.obj `if test -f 'xml_writer.c'; then $(CYGPATH_W) 'xml_writer.c'; else $(CYGPATH_W) '$(srcdir)/xml_writer.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-xml_writer.Tpo $(DEPDIR)/lldpcli-xml_writer.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='xml_writer.c' object='lldpcli-xml_writer.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-xml_writer.obj `if test -f 'xml_writer.c'; then $(CYGPATH_W) 'xml_writer.c'; else $(CYGPATH_W) '$(srcdir)/xml_writer.c'; fi` + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-man8: $(dist_man_MANS) $(man_MANS) + @$(NORMAL_INSTALL) + @list1=''; \ + list2='$(dist_man_MANS) $(man_MANS)'; \ + test -n "$(man8dir)" \ + && test -n "`echo $$list1$$list2`" \ + || exit 0; \ + echo " $(MKDIR_P) '$(DESTDIR)$(man8dir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(man8dir)" || exit 1; \ + { for i in $$list1; do echo "$$i"; done; \ + if test -n "$$list2"; then \ + for i in $$list2; do echo "$$i"; done \ + | sed -n '/\.8[a-z]*$$/p'; \ + fi; \ + } | while read p; do \ + if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; echo "$$p"; \ + done | \ + sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \ + -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ + sed 'N;N;s,\n, ,g' | { \ + list=; while read file base inst; do \ + if test "$$base" = "$$inst"; then list="$$list $$file"; else \ + echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \ + $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst" || exit $$?; \ + fi; \ + done; \ + for i in $$list; do echo "$$i"; done | $(am__base_list) | \ + while read files; do \ + test -z "$$files" || { \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man8dir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(man8dir)" || exit $$?; }; \ + done; } + +uninstall-man8: + @$(NORMAL_UNINSTALL) + @list=''; test -n "$(man8dir)" || exit 0; \ + files=`{ for i in $$list; do echo "$$i"; done; \ + l2='$(dist_man_MANS) $(man_MANS)'; for i in $$l2; do echo "$$i"; done | \ + sed -n '/\.8[a-z]*$$/p'; \ + } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \ + -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ + dir='$(DESTDIR)$(man8dir)'; $(am__uninstall_files_from_dir) +install-dist_bashcompletionDATA: $(dist_bashcompletion_DATA) + @$(NORMAL_INSTALL) + @list='$(dist_bashcompletion_DATA)'; test -n "$(bashcompletiondir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(bashcompletiondir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(bashcompletiondir)" || 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)$(bashcompletiondir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(bashcompletiondir)" || exit $$?; \ + done + +uninstall-dist_bashcompletionDATA: + @$(NORMAL_UNINSTALL) + @list='$(dist_bashcompletion_DATA)'; test -n "$(bashcompletiondir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(bashcompletiondir)'; $(am__uninstall_files_from_dir) +install-dist_lldpdconfDATA: $(dist_lldpdconf_DATA) + @$(NORMAL_INSTALL) + @list='$(dist_lldpdconf_DATA)'; test -n "$(lldpdconfdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(lldpdconfdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(lldpdconfdir)" || 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)$(lldpdconfdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(lldpdconfdir)" || exit $$?; \ + done + +uninstall-dist_lldpdconfDATA: + @$(NORMAL_UNINSTALL) + @list='$(dist_lldpdconf_DATA)'; test -n "$(lldpdconfdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(lldpdconfdir)'; $(am__uninstall_files_from_dir) +install-dist_zshcompletionDATA: $(dist_zshcompletion_DATA) + @$(NORMAL_INSTALL) + @list='$(dist_zshcompletion_DATA)'; test -n "$(zshcompletiondir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(zshcompletiondir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(zshcompletiondir)" || 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)$(zshcompletiondir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(zshcompletiondir)" || exit $$?; \ + done + +uninstall-dist_zshcompletionDATA: + @$(NORMAL_UNINSTALL) + @list='$(dist_zshcompletion_DATA)'; test -n "$(zshcompletiondir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(zshcompletiondir)'; $(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 $(PROGRAMS) $(MANS) $(DATA) +installdirs: + for dir in "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)" "$(DESTDIR)$(bashcompletiondir)" "$(DESTDIR)$(lldpdconfdir)" "$(DESTDIR)$(zshcompletiondir)"; 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) + +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-libtool clean-sbinPROGRAMS \ + mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/lldpcli-commands.Po + -rm -f ./$(DEPDIR)/lldpcli-conf-dot3.Po + -rm -f ./$(DEPDIR)/lldpcli-conf-inv.Po + -rm -f ./$(DEPDIR)/lldpcli-conf-lldp.Po + -rm -f ./$(DEPDIR)/lldpcli-conf-med.Po + -rm -f ./$(DEPDIR)/lldpcli-conf-power.Po + -rm -f ./$(DEPDIR)/lldpcli-conf-system.Po + -rm -f ./$(DEPDIR)/lldpcli-conf.Po + -rm -f ./$(DEPDIR)/lldpcli-display.Po + -rm -f ./$(DEPDIR)/lldpcli-json_writer.Po + -rm -f ./$(DEPDIR)/lldpcli-kv_writer.Po + -rm -f ./$(DEPDIR)/lldpcli-lldpcli.Po + -rm -f ./$(DEPDIR)/lldpcli-misc.Po + -rm -f ./$(DEPDIR)/lldpcli-show.Po + -rm -f ./$(DEPDIR)/lldpcli-text_writer.Po + -rm -f ./$(DEPDIR)/lldpcli-tokenizer.Po + -rm -f ./$(DEPDIR)/lldpcli-utf8.Po + -rm -f ./$(DEPDIR)/lldpcli-xml_writer.Po + -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-dist_bashcompletionDATA \ + install-dist_lldpdconfDATA install-dist_zshcompletionDATA \ + install-man + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-exec-local install-sbinPROGRAMS + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: install-man8 + +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)/lldpcli-commands.Po + -rm -f ./$(DEPDIR)/lldpcli-conf-dot3.Po + -rm -f ./$(DEPDIR)/lldpcli-conf-inv.Po + -rm -f ./$(DEPDIR)/lldpcli-conf-lldp.Po + -rm -f ./$(DEPDIR)/lldpcli-conf-med.Po + -rm -f ./$(DEPDIR)/lldpcli-conf-power.Po + -rm -f ./$(DEPDIR)/lldpcli-conf-system.Po + -rm -f ./$(DEPDIR)/lldpcli-conf.Po + -rm -f ./$(DEPDIR)/lldpcli-display.Po + -rm -f ./$(DEPDIR)/lldpcli-json_writer.Po + -rm -f ./$(DEPDIR)/lldpcli-kv_writer.Po + -rm -f ./$(DEPDIR)/lldpcli-lldpcli.Po + -rm -f ./$(DEPDIR)/lldpcli-misc.Po + -rm -f ./$(DEPDIR)/lldpcli-show.Po + -rm -f ./$(DEPDIR)/lldpcli-text_writer.Po + -rm -f ./$(DEPDIR)/lldpcli-tokenizer.Po + -rm -f ./$(DEPDIR)/lldpcli-utf8.Po + -rm -f ./$(DEPDIR)/lldpcli-xml_writer.Po + -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-dist_bashcompletionDATA \ + uninstall-dist_lldpdconfDATA uninstall-dist_zshcompletionDATA \ + uninstall-local uninstall-man uninstall-sbinPROGRAMS + +uninstall-man: uninstall-man8 + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-generic clean-libtool clean-sbinPROGRAMS 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-dist_bashcompletionDATA \ + install-dist_lldpdconfDATA install-dist_zshcompletionDATA \ + install-dvi install-dvi-am install-exec install-exec-am \ + install-exec-local install-html install-html-am install-info \ + install-info-am install-man install-man8 install-pdf \ + install-pdf-am install-ps install-ps-am install-sbinPROGRAMS \ + 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-dist_bashcompletionDATA uninstall-dist_lldpdconfDATA \ + uninstall-dist_zshcompletionDATA uninstall-local uninstall-man \ + uninstall-man8 uninstall-sbinPROGRAMS + +.PRECIOUS: Makefile + + +install-exec-local: lldpcli + cd $(DESTDIR)$(sbindir) && rm -f lldpctl + cd $(DESTDIR)$(sbindir) && $(LN_S) lldpcli lldpctl +uninstall-local: + cd $(DESTDIR)$(sbindir) && rm -f lldpctl +lldpcli.8: lldpcli.8.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/client/README.conf b/src/client/README.conf new file mode 100644 index 0000000..4982e9e --- /dev/null +++ b/src/client/README.conf @@ -0,0 +1,8 @@ +# You can put lldpd configuration snippets into this directory. +# Upon start, lldpd will read each files in this directory and execute content +# as if they were passed as arguments to lldpcli +# +# Files should be suffixed by .conf and have content like: +# configure system description 'my little server' +# +# See lldpcli(8) for more details. diff --git a/src/client/client.h b/src/client/client.h new file mode 100644 index 0000000..04fca94 --- /dev/null +++ b/src/client/client.h @@ -0,0 +1,148 @@ +/* -*- 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 _CLIENT_H +#define _CLIENT_H + +#if HAVE_CONFIG_H +# include <config.h> +#endif + +#include "../lib/lldpctl.h" +#include "../lldp-const.h" +#include "../log.h" +#include "../ctl.h" +#include "../compat/compat.h" +#include "writer.h" + +#ifdef HAVE_ADDRESS_SANITIZER +# include <sanitizer/lsan_interface.h> +# define SUPPRESS_LEAK(x) __lsan_ignore_object(x) +#else +# define SUPPRESS_LEAK(x) +#endif + +/* Readline stuff */ +#ifdef HAVE_LIBREADLINE +# if defined(HAVE_READLINE_READLINE_H) +# include <readline/readline.h> +# elif defined(HAVE_READLINE_H) +# include <readline.h> +# else +extern char *readline(); +extern char *rl_line_buffer; +extern int rl_point; +extern int rl_insert_text(const char *); +extern void rl_forced_update_display(void); +extern int rl_bind_key(int, int (*f)(int, int)); +# endif +#endif +#ifdef HAVE_READLINE_HISTORY +# if defined(HAVE_READLINE_HISTORY_H) +# include <readline/history.h> +# elif defined(HAVE_HISTORY_H) +# include <history.h> +# else +extern void add_history(); +# endif +#endif +#undef NEWLINE + +extern const char *ctlname; + +/* commands.c */ +#define NEWLINE "<CR>" +struct cmd_node; +struct cmd_env; +struct cmd_node *commands_root(void); +struct cmd_node *commands_new(struct cmd_node *, const char *, const char *, + int (*validate)(struct cmd_env *, const void *), + int (*execute)(struct lldpctl_conn_t *, struct writer *, struct cmd_env *, + const void *), + const void *); +struct cmd_node *commands_privileged(struct cmd_node *); +struct cmd_node *commands_lock(struct cmd_node *); +struct cmd_node *commands_hidden(struct cmd_node *); +void commands_free(struct cmd_node *); +const char *cmdenv_arg(struct cmd_env *); +const char *cmdenv_get(struct cmd_env *, const char *); +int cmdenv_put(struct cmd_env *, const char *, const char *); +int cmdenv_pop(struct cmd_env *, int); +int commands_execute(struct lldpctl_conn_t *, struct writer *, struct cmd_node *, int, + const char **, int); +char *commands_complete(struct cmd_node *, int, const char **, int, int); +/* helpers */ +int cmd_check_no_env(struct cmd_env *, const void *); +int cmd_check_env(struct cmd_env *, const void *); +int cmd_store_env(struct lldpctl_conn_t *, struct writer *, struct cmd_env *, + const void *); +int cmd_store_env_and_pop(struct lldpctl_conn_t *, struct writer *, struct cmd_env *, + const void *); +int cmd_store_env_value(struct lldpctl_conn_t *, struct writer *, struct cmd_env *, + const void *); +int cmd_store_env_value_and_pop(struct lldpctl_conn_t *, struct writer *, + struct cmd_env *, const void *); +int cmd_store_env_value_and_pop2(struct lldpctl_conn_t *, struct writer *, + struct cmd_env *, const void *); +int cmd_store_env_value_and_pop3(struct lldpctl_conn_t *, struct writer *, + struct cmd_env *, const void *); +int cmd_store_something_env_value_and_pop2(const char *, struct cmd_env *, + const void *); +int cmd_store_something_env_value(const char *, struct cmd_env *, const void *); +lldpctl_atom_t *cmd_iterate_on_interfaces(struct lldpctl_conn_t *, struct cmd_env *); +lldpctl_atom_t *cmd_iterate_on_ports(struct lldpctl_conn_t *, struct cmd_env *, + const char **); +void cmd_restrict_ports(struct cmd_node *); +void cmd_restrict_protocol(struct cmd_node *); + +/* misc.c */ +int contains(const char *, const char *); +const char *totag(const char *); + +/* display.c */ +#define DISPLAY_BRIEF 1 +#define DISPLAY_NORMAL 2 +#define DISPLAY_DETAILS 3 +void display_interfaces(lldpctl_conn_t *, struct writer *, struct cmd_env *, int, int); +void display_interface(lldpctl_conn_t *, struct writer *, int, lldpctl_atom_t *, + lldpctl_atom_t *, int, int); +void display_local_chassis(lldpctl_conn_t *, struct writer *, struct cmd_env *, int); +void display_configuration(lldpctl_conn_t *, struct writer *); +void display_interfaces_stats(lldpctl_conn_t *, struct writer *, struct cmd_env *); +void display_interface_stats(lldpctl_conn_t *, struct writer *, lldpctl_atom_t *); +void display_local_interfaces(lldpctl_conn_t *, struct writer *, struct cmd_env *, int, + int); + +/* show.c */ +void register_commands_show(struct cmd_node *); +void register_commands_watch(struct cmd_node *); + +/* conf*.c */ +void register_commands_configure(struct cmd_node *); +void register_commands_configure_system(struct cmd_node *, struct cmd_node *); +void register_commands_configure_lldp(struct cmd_node *, struct cmd_node *); +void register_commands_configure_med(struct cmd_node *, struct cmd_node *); +void register_commands_configure_inventory(struct cmd_node *, struct cmd_node *); +void register_commands_configure_dot3(struct cmd_node *); +void register_commands_medpow(struct cmd_node *); +void register_commands_dot3pow(struct cmd_node *); + +/* tokenizer.c */ +int tokenize_line(const char *, int *, char ***); +void tokenize_free(int, char **); + +#endif diff --git a/src/client/commands.c b/src/client/commands.c new file mode 100644 index 0000000..804789e --- /dev/null +++ b/src/client/commands.c @@ -0,0 +1,855 @@ +/* -*- 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 "client.h" +#include <string.h> +#include <sys/queue.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <libgen.h> + +/** + * An element of the environment (a key and a value). + */ +struct cmd_env_el { + TAILQ_ENTRY(cmd_env_el) next; /**< Next environment element */ + const char *key; /**< Key for this element */ + const char *value; /**< Value for this element */ +}; + +/** + * A stack element. + */ +struct cmd_env_stack { + TAILQ_ENTRY(cmd_env_stack) next; /**< Next element, down the stack */ + struct cmd_node *el; /**< Stored element */ +}; + +/** + * Structure representing an environment for the current command. + * + * An environment is a list of values stored for use for the function executing + * as well as the current command, the current position in the command and a + * stack for cmd_node + */ +struct cmd_env { + TAILQ_HEAD(, cmd_env_el) elements; /**< List of environment variables */ + TAILQ_HEAD(, cmd_env_stack) stack; /**< Stack */ + int argc; /**< Number of argument in the command */ + int argp; /**< Current argument */ + const char **argv; /**< Arguments */ +}; + +/** + * Structure representing a command node. + * + * Such a node contains a token accepted to enter the node (or @c NULL if there + * is no token needed), a documentation string to present the user, a function + * to validate the user input (or @c NULL if no function is needed) and a + * function to execute when entering the node. Because we can enter a node just + * by completing, the execution part should have no other effect than modifying + * the environment, with the exception of execution on @c NEWLINE (which cannot + * happen when completing). + */ +struct cmd_node { + TAILQ_ENTRY(cmd_node) next; /**< Next sibling */ + + const char *token; /**< Token to enter this cnode */ + const char *doc; /**< Documentation string */ + int privileged; /**< Privileged command? */ + int lock; /**< Lock required for execution? */ + int hidden; /**< Hidden command? */ + + /** + * Function validating entry in this node. Can be @c NULL. + */ + int (*validate)(struct cmd_env *, const void *); + /** + * Function to execute when entering this node. May be @c NULL. + * + * This function can alter the environment + */ + int (*execute)(struct lldpctl_conn_t *, struct writer *, struct cmd_env *, + const void *); + const void *arg; /**< Magic argument for the previous two functions */ + + /* List of possible subentries */ + TAILQ_HEAD(, cmd_node) subentries; /* List of subnodes */ +}; + +/** + * Create a root node. + * + * @return the root node. + */ +struct cmd_node * +commands_root(void) +{ + struct cmd_node *new = calloc(1, sizeof(struct cmd_node)); + if (new == NULL) fatalx("lldpctl", "out of memory"); + TAILQ_INIT(&new->subentries); + return new; +} + +/** + * Make a node accessible only to privileged users. + * + * @param node node to change privileges + * @return the modified node + * + * The node is modified. It is returned to ease chaining. + */ +struct cmd_node * +commands_privileged(struct cmd_node *node) +{ + if (node) node->privileged = 1; + return node; +} + +/** + * Make a node accessible only with a lock. + * + * @param node node to use lock to execute + * @return the modified node + * + * The node is modified. It is returned to ease chaining. + */ +struct cmd_node * +commands_lock(struct cmd_node *node) +{ + if (node) node->lock = 1; + return node; +} + +/** + * Hide a node from help or completion. + * + * @param node node to hide + * @return the modified node + * + * The node is modified. It is returned to ease chaining. + */ +struct cmd_node * +commands_hidden(struct cmd_node *node) +{ + if (node) node->hidden = 1; + return node; +} + +/** + * Create a new node acessible by any user. + * + * @param root The node we want to attach this node. + * @param token Token to enter this node. Or @c NULL if no token is needed. + * @param doc Documentation for this node. + * @param validate Function that should return 1 if we can enter the node. + * @param execute Function that should return 1 on successful execution of this node. + * @param arg Magic argument for precedent functions. + * @return the newly created node + */ +struct cmd_node * +commands_new(struct cmd_node *root, const char *token, const char *doc, + int (*validate)(struct cmd_env *, const void *), + int (*execute)(struct lldpctl_conn_t *, struct writer *, struct cmd_env *, + const void *), + const void *arg) +{ + struct cmd_node *new = calloc(1, sizeof(struct cmd_node)); + if (new == NULL) fatalx("lldpctl", "out of memory"); + new->token = token; + new->doc = doc; + new->validate = validate; + new->execute = execute; + new->arg = arg; + TAILQ_INIT(&new->subentries); + TAILQ_INSERT_TAIL(&root->subentries, new, next); + return new; +} + +/** + * Free a command tree. + * + * @param root The node we want to free. + */ +void +commands_free(struct cmd_node *root) +{ + struct cmd_node *subcmd, *subcmd_next; + for (subcmd = TAILQ_FIRST(&root->subentries); subcmd != NULL; + subcmd = subcmd_next) { + subcmd_next = TAILQ_NEXT(subcmd, next); + TAILQ_REMOVE(&root->subentries, subcmd, next); + commands_free(subcmd); + } + free(root); +} + +/** + * Return the current argument in the environment. This can be @c NEWLINE or + * @c NULL. + * + * @param env The environment. + * @return current argument. + */ +const char * +cmdenv_arg(struct cmd_env *env) +{ + if (env->argp < env->argc) return env->argv[env->argp]; + if (env->argp == env->argc) return NEWLINE; + return NULL; +} + +/** + * Get a value from the environment. + * + * @param env The environment. + * @param key The key for the requested value. + * @return @c NULL if not found or the requested value otherwise. If no value is + * associated, return the key. + */ +const char * +cmdenv_get(struct cmd_env *env, const char *key) +{ + struct cmd_env_el *el; + TAILQ_FOREACH (el, &env->elements, next) + if (!strcmp(el->key, key)) return el->value ? el->value : el->key; + return NULL; +} + +/** + * Put a value in the environment. + * + * @param env The environment. + * @param key The key for the value. + * @param value The value. + * @return 0 on success, -1 otherwise. + */ +int +cmdenv_put(struct cmd_env *env, const char *key, const char *value) +{ + struct cmd_env_el *el = malloc(sizeof(struct cmd_env_el)); + if (el == NULL) { + log_warn("lldpctl", + "unable to allocate memory for new environment variable"); + return -1; + } + el->key = key; + el->value = value; + TAILQ_INSERT_TAIL(&env->elements, el, next); + return 0; +} + +/** + * Pop some node from the execution stack. + * + * This allows to resume parsing on a previous state. Useful to call after + * parsing optional arguments. + * + * @param env The environment. + * @param n How many element we want to pop. + * @return 0 on success, -1 otherwise. + */ +int +cmdenv_pop(struct cmd_env *env, int n) +{ + while (n-- > 0) { + if (TAILQ_EMPTY(&env->stack)) { + log_warnx("lldpctl", "environment stack is empty"); + return -1; + } + struct cmd_env_stack *first = TAILQ_FIRST(&env->stack); + TAILQ_REMOVE(&env->stack, first, next); + free(first); + } + return 0; +} + +/** + * Push some node on the execution stack. + * + * @param env The environment. + * @param node The node to push. + * @return 0 on success, -1 on error. + */ +static int +cmdenv_push(struct cmd_env *env, struct cmd_node *node) +{ + struct cmd_env_stack *el = malloc(sizeof(struct cmd_env_stack)); + if (el == NULL) { + log_warn("lldpctl", "not enough memory to allocate a stack element"); + return -1; + } + el->el = node; + TAILQ_INSERT_HEAD(&env->stack, el, next); + return 0; +} + +/** + * Return the top of the stack, without poping it. + * + * @param env The environment. + * @return the top element or @c NULL is the stack is empty. + */ +static struct cmd_node * +cmdenv_top(struct cmd_env *env) +{ + if (TAILQ_EMPTY(&env->stack)) return NULL; + return TAILQ_FIRST(&env->stack)->el; +} + +/** + * Free execution environment. + * + * @param env The environment. + */ +static void +cmdenv_free(struct cmd_env *env) +{ + while (!TAILQ_EMPTY(&env->stack)) + cmdenv_pop(env, 1); + + struct cmd_env_el *first; + while (!TAILQ_EMPTY(&env->elements)) { + first = TAILQ_FIRST(&env->elements); + TAILQ_REMOVE(&env->elements, first, next); + free(first); + } +} + +struct candidate_word { + TAILQ_ENTRY(candidate_word) next; + const char *word; + const char *doc; + int hidden; +}; + +/** + * Execute or complete a command from the given node. + * + * @param conn Connection to lldpd. + * @param w Writer for output. + * @param root Root node we want to start from. + * @param argc Number of arguments. + * @param argv Array of arguments. + * @param word Completed word. Or NULL when no completion is required. + * @param all When completing, display possible completions even if only one choice + * is possible. + * @param priv Is the current user privileged? + * @return 0 on success, -1 otherwise. + */ +static int +_commands_execute(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_node *root, + int argc, const char **argv, char **word, int all, int priv) +{ + int n, rc = 0, completion = (word != NULL); + int help = 0; /* Are we asking for help? */ + int complete = 0; /* Are we asking for possible completions? */ + int needlock = 0; /* Do we need a lock? */ + struct cmd_env env = { .elements = TAILQ_HEAD_INITIALIZER(env.elements), + .stack = TAILQ_HEAD_INITIALIZER(env.stack), + .argc = argc, + .argv = argv, + .argp = 0 }; + static int lockfd = -1; + static char *lockname = NULL; /* Name of lockfile */ + cmdenv_push(&env, root); + if (!completion) + for (n = 0; n < argc; n++) + log_debug("lldpctl", "argument %02d: `%s`", n, argv[n]); + if (completion) *word = NULL; + +#define CAN_EXECUTE(candidate) \ + ((!candidate->privileged || priv || complete) && \ + (!candidate->validate || candidate->validate(&env, candidate->arg) == 1)) + + /* When completion is in progress, we use the same algorithm than for + * execution until we reach the cursor position. */ + struct cmd_node *current = NULL; + while ((current = cmdenv_top(&env))) { + if (!completion) { + help = !!cmdenv_get(&env, "help"); /* Are we asking for help? */ + complete = !!cmdenv_get(&env, "complete"); /* Or completion? */ + } + + struct cmd_node *candidate, *best = NULL; + const char *token = (env.argp < env.argc) ? env.argv[env.argp] : + (env.argp == env.argc && !help && !complete) ? NEWLINE : + NULL; + if (token == NULL || (completion && env.argp == env.argc - 1)) goto end; + if (!completion) + log_debug("lldpctl", "process argument %02d: `%s`", env.argp, + token); + TAILQ_FOREACH (candidate, ¤t->subentries, next) { + if (candidate->token && + !strncmp(candidate->token, token, strlen(token)) && + CAN_EXECUTE(candidate)) { + if (candidate->token && + !strcmp(candidate->token, token)) { + /* Exact match */ + best = candidate; + needlock = needlock || candidate->lock; + break; + } + if (!best) + best = candidate; + else { + if (!completion) + log_warnx("lldpctl", + "ambiguous token: %s (%s or %s)", + token, candidate->token, + best->token); + rc = -1; + goto end; + } + } + } + if (!best) { + /* Take first that validate */ + TAILQ_FOREACH (candidate, ¤t->subentries, next) { + if (!candidate->token && CAN_EXECUTE(candidate)) { + best = candidate; + needlock = needlock || candidate->lock; + break; + } + } + } + if (!best && env.argp == env.argc) goto end; + if (!best) { + if (!completion) + log_warnx("lldpctl", + "unknown command from argument %d: `%s`", + env.argp + 1, token); + rc = -1; + goto end; + } + + /* Push and execute */ + cmdenv_push(&env, best); + if (best->execute) { + if (needlock) { + if (lockfd == -1) { + if (lockname == NULL && + asprintf(&lockname, "%s.lock", ctlname) == + -1) { + log_warnx("lldpctl", + "not enough memory to build lock filename"); + rc = -1; + goto end; + } + log_debug("lldpctl", "open %s for locking", + lockname); + if ((lockfd = open(lockname, O_RDWR)) == -1) { + log_warn("lldpctl", + "cannot open lock %s", lockname); + rc = -1; + goto end; + } + } + if (lockf(lockfd, F_LOCK, 0) == -1) { + log_warn("lldpctl", "cannot get lock on %s", + lockname); + rc = -1; + close(lockfd); + lockfd = -1; + goto end; + } + } + rc = best->execute(conn, w, &env, best->arg) != 1 ? -1 : rc; + if (needlock && lockf(lockfd, F_ULOCK, 0) == -1) { + log_warn("lldpctl", "cannot unlock %s", lockname); + close(lockfd); + lockfd = -1; + } + if (rc == -1) goto end; + } + env.argp++; + } +end: + if (!completion && !help && !complete) { + if (rc == 0 && env.argp != env.argc + 1) { + log_warnx("lldpctl", "incomplete command"); + rc = -1; + } + } else if (rc == 0 && (env.argp == env.argc - 1 || help || complete)) { + /* We need to complete. Let's build the list of candidate words. */ + struct cmd_node *candidate = NULL; + size_t maxl = 10; /* Max length of a word */ + TAILQ_HEAD(, candidate_word) words; /* List of subnodes */ + TAILQ_INIT(&words); + current = cmdenv_top(&env); + if (!TAILQ_EMPTY(¤t->subentries)) { + TAILQ_FOREACH (candidate, ¤t->subentries, next) { + if ((!candidate->token || help || complete || + !strncmp(env.argv[env.argc - 1], + candidate->token, + strlen(env.argv[env.argc - 1]))) && + CAN_EXECUTE(candidate)) { + struct candidate_word *cword = + malloc(sizeof(struct candidate_word)); + if (!cword) break; + cword->word = candidate->token; + cword->doc = candidate->doc; + cword->hidden = candidate->hidden; + if (cword->word && strlen(cword->word) > maxl) + maxl = strlen(cword->word); + TAILQ_INSERT_TAIL(&words, cword, next); + } + } + } + if (!TAILQ_EMPTY(&words)) { + /* Search if there is a common prefix, then return it. */ + char prefix[maxl + 2]; /* Extra space may be added at the end */ + struct candidate_word *cword, *cword_next; + memset(prefix, 0, maxl + 2); + for (size_t n = 0; n < maxl; n++) { + int c = 1; /* Set to 0 to exit outer loop */ + TAILQ_FOREACH (cword, &words, next) { + c = 0; + if (cword->hidden) continue; + if (cword->word == NULL) break; + if (!strcmp(cword->word, NEWLINE)) break; + if (strlen(cword->word) == n) break; + if (prefix[n] == '\0') + prefix[n] = cword->word[n]; + else if (prefix[n] != cword->word[n]) + break; + c = 1; + } + if (c == 0) { + prefix[n] = '\0'; + break; + } + } + /* If the prefix is complete, add a space, otherwise, + * just return it as is. */ + if (!all && !help && !complete && strcmp(prefix, NEWLINE) && + strlen(prefix) > 0 && + strlen(env.argv[env.argc - 1]) < strlen(prefix)) { + TAILQ_FOREACH (cword, &words, next) { + if (cword->word && + !strcmp(prefix, cword->word)) { + prefix[strlen(prefix)] = ' '; + break; + } + } + *word = strdup(prefix); + } else { + /* No common prefix, print possible completions */ + if (!complete) + fprintf(stderr, "\n-- \033[1;34m%s\033[0m\n", + current->doc ? current->doc : "Help"); + TAILQ_FOREACH (cword, &words, next) { + if (cword->hidden) continue; + + char fmt[100]; + if (!complete) { + snprintf(fmt, sizeof(fmt), + "%s%%%ds%s %%s\n", "\033[1m", + (int)maxl, "\033[0m"); + fprintf(stderr, fmt, + cword->word ? cword->word : "WORD", + cword->doc ? cword->doc : "..."); + } else { + if (!cword->word || + !strcmp(cword->word, NEWLINE)) + continue; + fprintf(stdout, "%s %s\n", + cword->word ? cword->word : "WORD", + cword->doc ? cword->doc : "..."); + } + } + } + for (cword = TAILQ_FIRST(&words); cword != NULL; + cword = cword_next) { + cword_next = TAILQ_NEXT(cword, next); + TAILQ_REMOVE(&words, cword, next); + free(cword); + } + } + } + cmdenv_free(&env); + return rc; +} + +/** + * Complete the given command. + */ +char * +commands_complete(struct cmd_node *root, int argc, const char **argv, int all, + int privileged) +{ + char *word = NULL; + if (_commands_execute(NULL, NULL, root, argc, argv, &word, all, privileged) == + 0) + return word; + return NULL; +} + +/** + * Execute the given commands. + */ +int +commands_execute(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_node *root, + int argc, const char **argv, int privileged) +{ + return _commands_execute(conn, w, root, argc, argv, NULL, 0, privileged); +} + +/** + * Check if the environment does not contain the given key. + * + * @param env The environment. + * @param key The key to search for. + * @return 1 if the environment does not contain the key. 0 otherwise. + */ +int +cmd_check_no_env(struct cmd_env *env, const void *key) +{ + return cmdenv_get(env, (const char *)key) == NULL; +} + +/** + * Check if the environment does contain the given key. + * + * @param env The environment. + * @param key The key to search for. Can be a comma separated list. + * @return 1 if the environment does contain the key. 0 otherwise. + */ +int +cmd_check_env(struct cmd_env *env, const void *key) +{ + struct cmd_env_el *el; + const char *list = key; + int count = 0; + TAILQ_FOREACH (el, &env->elements, next) { + if (contains(list, el->key)) count++; + } + while ((list = strchr(list, ','))) { + list++; + count--; + } + return (count == 1); +} + +/** + * Store the given key in the environment. + * + * @param conn The connection. + * @param w The writer (not used). + * @param env The environment. + * @param key The key to store. + * @return 1 if the key was stored + */ +int +cmd_store_env(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env, + const void *key) +{ + return cmdenv_put(env, key, NULL) != -1; +} + +/** + * Store the given key in the environment and pop one element from the stack. + * + * @param conn The connection. + * @param w The writer (not used). + * @param env The environment. + * @param key The key to store. + * @return 1 if the key was stored + */ +int +cmd_store_env_and_pop(struct lldpctl_conn_t *conn, struct writer *w, + struct cmd_env *env, const void *key) +{ + return (cmd_store_env(conn, w, env, key) != -1 && cmdenv_pop(env, 1) != -1); +} + +/** + * Store the given key with a value being the current keyword in the environment + * and pop X elements from the stack. + * + * @param conn The connection. + * @param w The writer (not used). + * @param env The environment. + * @param key The key to store. + * @return 1 if the key was stored + */ +int +cmd_store_env_value_and_pop(struct lldpctl_conn_t *conn, struct writer *w, + struct cmd_env *env, const void *key) +{ + return ( + cmdenv_put(env, key, cmdenv_arg(env)) != -1 && cmdenv_pop(env, 1) != -1); +} +int +cmd_store_env_value_and_pop2(struct lldpctl_conn_t *conn, struct writer *w, + struct cmd_env *env, const void *key) +{ + return ( + cmdenv_put(env, key, cmdenv_arg(env)) != -1 && cmdenv_pop(env, 2) != -1); +} +int +cmd_store_env_value(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env, + const void *key) +{ + return (cmdenv_put(env, key, cmdenv_arg(env)) != -1); +} +int +cmd_store_env_value_and_pop3(struct lldpctl_conn_t *conn, struct writer *w, + struct cmd_env *env, const void *key) +{ + return ( + cmdenv_put(env, key, cmdenv_arg(env)) != -1 && cmdenv_pop(env, 3) != -1); +} +int +cmd_store_something_env_value_and_pop2(const char *what, struct cmd_env *env, + const void *value) +{ + return (cmdenv_put(env, what, value) != -1 && cmdenv_pop(env, 2) != -1); +} +int +cmd_store_something_env_value(const char *what, struct cmd_env *env, const void *value) +{ + return (cmdenv_put(env, what, value) != -1); +} + +/** + * Provide an iterator on all interfaces contained in "ports". + * + * @warning This function is not reentrant. It uses static variables to keep + * track of ports that have already been provided. Moreover, to release all + * resources, the iterator should be used until its end. + * + * @param conn The connection. + * @param env The environment. + * @return The next interface in the set of ports (or in all ports if no `ports` + * variable is present in the environment) + */ +lldpctl_atom_t * +cmd_iterate_on_interfaces(struct lldpctl_conn_t *conn, struct cmd_env *env) +{ + static lldpctl_atom_iter_t *iter = NULL; + static lldpctl_atom_t *iface_list = NULL; + static lldpctl_atom_t *iface = NULL; + const char *interfaces = cmdenv_get(env, "ports"); + + do { + if (iter == NULL) { + iface_list = lldpctl_get_interfaces(conn); + if (!iface_list) { + log_warnx("lldpctl", + "not able to get the list of interfaces. %s", + lldpctl_last_strerror(conn)); + return NULL; + } + iter = lldpctl_atom_iter(iface_list); + if (!iter) return NULL; + } else { + iter = lldpctl_atom_iter_next(iface_list, iter); + if (iface) { + lldpctl_atom_dec_ref(iface); + iface = NULL; + } + if (!iter) { + lldpctl_atom_dec_ref(iface_list); + return NULL; + } + } + + iface = lldpctl_atom_iter_value(iface_list, iter); + } while (interfaces && + !contains(interfaces, + lldpctl_atom_get_str(iface, lldpctl_k_interface_name))); + + return iface; +} + +/** + * Provide an iterator on all ports contained in "ports", as well as the + * default port. + * + * @warning This function is not reentrant. It uses static variables to keep + * track of ports that have already been provided. Moreover, to release all + * resources, the iterator should be used until its end. + * + * @param conn The connection. + * @param env The environment. + * @param name Name of the interface (for logging purpose) + * @return The next interface in the set of ports (or in all ports if no `ports` + * variable is present in the environment), including the default port + * if no `ports` variable is present in the environment. + */ +lldpctl_atom_t * +cmd_iterate_on_ports(struct lldpctl_conn_t *conn, struct cmd_env *env, + const char **name) +{ + static int put_default = 0; + static lldpctl_atom_t *last_port = NULL; + const char *interfaces = cmdenv_get(env, "ports"); + + if (last_port) { + lldpctl_atom_dec_ref(last_port); + last_port = NULL; + } + if (!put_default) { + lldpctl_atom_t *iface = cmd_iterate_on_interfaces(conn, env); + if (iface) { + *name = lldpctl_atom_get_str(iface, lldpctl_k_interface_name); + last_port = lldpctl_get_port(iface); + return last_port; + } + if (!interfaces) { + put_default = 1; + *name = "(default)"; + last_port = lldpctl_get_default_port(conn); + return last_port; + } + return NULL; + } else { + put_default = 0; + return NULL; + } +} + +/** + * Restrict the command to some ports. + */ +void +cmd_restrict_ports(struct cmd_node *root) +{ + /* Restrict to some ports. */ + commands_new(commands_new(root, "ports", "Restrict configuration to some ports", + cmd_check_no_env, NULL, "ports"), + NULL, + "Restrict configuration to the specified ports (comma-separated list)", + NULL, cmd_store_env_value_and_pop2, "ports"); +} + +/** + * Restrict the command to specific protocol + */ +void +cmd_restrict_protocol(struct cmd_node *root) +{ + /* Restrict to some ports. */ + commands_new(commands_new(root, "protocol", "Restrict to specific protocol", + cmd_check_no_env, NULL, "protocol"), + NULL, "Restrict display to the specified protocol", NULL, + cmd_store_env_value_and_pop2, "protocol"); +} diff --git a/src/client/completion/_lldpcli b/src/client/completion/_lldpcli new file mode 100644 index 0000000..ff942f1 --- /dev/null +++ b/src/client/completion/_lldpcli @@ -0,0 +1,48 @@ +#compdef lldpcli +# +# zsh completion for lldpcli +# +# Copyright (c) 2014 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. + + +__lldpcli_command () { + local -a completions + completions=(${(f)"$(_call_program commands lldpcli complete ${words[1,$(($CURRENT-1))]})"}) + completions=(${completions:s/ /:/}) + _describe -t lldpcli-command "lldpcli completion" completions "$@" +} + +_lldpcli () { + local curcontext="$curcontext" state line + + _arguments -C \ + '*-d[print more debugging information]' \ + '(- *)-v[print version number and exit]' \ + '-u[use an alternate socket with lldpd]:UNIX socket:_files' \ + '-f[output format]:format:(plain xml json json0 keyvalue)' \ + '*-c[read a configuration file]:configuration file:_files' \ + '(-)*::lldpcli command:__lldpcli_command' +} + + +_lldpcli "$@" + +# Local Variables: +# mode: Shell-Script +# sh-indentation: 4 +# indent-tabs-mode: nil +# sh-basic-offset: 4 +# End: +# vim: ft=zsh sw=4 ts=4 et diff --git a/src/client/completion/lldpcli b/src/client/completion/lldpcli new file mode 100755 index 0000000..48476f7 --- /dev/null +++ b/src/client/completion/lldpcli @@ -0,0 +1,26 @@ +_lldpcli() +{ + COMPREPLY=() + COMP_WORDBREAKS=" " + local cur=${COMP_WORDS[COMP_CWORD]} + local cmd=(${COMP_WORDS[*]}) + + if [ "" != "$cur" ]; then + unset cmd[COMP_CWORD] + fi + + local choices=$(${cmd[0]} complete ${cmd[@]:1} | \ + cut -d " " -f 1) + COMPREPLY=($(compgen -W '${choices}' -- ${cur} )) + return 0 +} + +complete -F _lldpcli lldpcli + +# Local Variables: +# mode: Shell-Script +# sh-indentation: 4 +# indent-tabs-mode: nil +# sh-basic-offset: 4 +# End: +# vim: ft=zsh sw=4 ts=4 et diff --git a/src/client/conf-dot3.c b/src/client/conf-dot3.c new file mode 100644 index 0000000..e90b60d --- /dev/null +++ b/src/client/conf-dot3.c @@ -0,0 +1,35 @@ +/* -*- 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 <unistd.h> +#include <string.h> + +#include "client.h" +#include "../log.h" + +/** + * Commands to configure dot3 + */ +void +register_commands_configure_dot3(struct cmd_node *configure) +{ + if (lldpctl_key_get_map(lldpctl_k_dot3_power_class)[0].string == NULL) return; + + struct cmd_node *configure_dot3 = + commands_new(configure, "dot3", "Dot3 configuration", NULL, NULL, NULL); + register_commands_dot3pow(configure_dot3); +} diff --git a/src/client/conf-inv.c b/src/client/conf-inv.c new file mode 100644 index 0000000..b137a18 --- /dev/null +++ b/src/client/conf-inv.c @@ -0,0 +1,161 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * SPDX-FileCopyrightText: 2022 Koninklijke Philips N.V. + * SPDX-License-Identifier: ISC + */ + +#include <unistd.h> +#include <string.h> + +#include "client.h" +#include "../log.h" + +static int +cmd_inventory(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env, + const void *arg) +{ + log_debug("lldpctl", "configure inventory information"); + + lldpctl_atom_t *chassis = lldpctl_get_local_chassis(conn); + + if (chassis == NULL) { + log_warnx("lldpctl", "unable to get configuration from lldpd. %s", + lldpctl_last_strerror(conn)); + return 0; + } + + const char *action = arg; + if ((!strcmp(action, "hardware-revision") && + (lldpctl_atom_set_str(chassis, lldpctl_k_chassis_med_inventory_hw, + cmdenv_get(env, "hardware-revision")) == NULL)) || + (!strcmp(action, "software-revision") && + (lldpctl_atom_set_str(chassis, lldpctl_k_chassis_med_inventory_sw, + cmdenv_get(env, "software-revision")) == NULL)) || + (!strcmp(action, "firmware-revision") && + (lldpctl_atom_set_str(chassis, lldpctl_k_chassis_med_inventory_fw, + cmdenv_get(env, "firmware-revision")) == NULL)) || + (!strcmp(action, "serial-number") && + (lldpctl_atom_set_str(chassis, lldpctl_k_chassis_med_inventory_sn, + cmdenv_get(env, "serial-number")) == NULL)) || + (!strcmp(action, "manufacturer") && + (lldpctl_atom_set_str(chassis, lldpctl_k_chassis_med_inventory_manuf, + cmdenv_get(env, "manufacturer")) == NULL)) || + (!strcmp(action, "model") && + (lldpctl_atom_set_str(chassis, lldpctl_k_chassis_med_inventory_model, + cmdenv_get(env, "model")) == NULL)) || + (!strcmp(action, "asset") && + (lldpctl_atom_set_str(chassis, lldpctl_k_chassis_med_inventory_asset, + cmdenv_get(env, "asset")) == NULL))) { + log_warnx("lldpctl", "Unable to setup inventory. %s", + lldpctl_last_strerror(conn)); + lldpctl_atom_dec_ref(chassis); + return 0; + } + + log_info("lldpctl", "Configuration for inventory is applied"); + lldpctl_atom_dec_ref(chassis); + return 1; +} + +/** + * Register `configure inventory *` commands + * + */ +static void +register_commands_inv(struct cmd_node *configure_inv, struct cmd_node *unconfigure_inv) +{ + commands_new(commands_new(commands_new(configure_inv, "hardware-revision", + "Set hardware-revision string", NULL, NULL, NULL), + NULL, "Inventory hardware-revision string", NULL, + cmd_store_env_value, "hardware-revision"), + NEWLINE, "Set hardware-revision string", NULL, cmd_inventory, + "hardware-revision"); + + commands_new(commands_new(unconfigure_inv, "hardware-revision", + "Unset hardware-revision string", NULL, NULL, NULL), + NEWLINE, "Unset hardware-revision string", NULL, cmd_inventory, + "hardware-revision"); + + commands_new(commands_new(commands_new(configure_inv, "software-revision", + "Set software-revision string", NULL, NULL, NULL), + NULL, "Inventory software-revision string", NULL, + cmd_store_env_value, "software-revision"), + NEWLINE, "Set software-revision string", NULL, cmd_inventory, + "software-revision"); + + commands_new(commands_new(unconfigure_inv, "software-revision", + "Unset software-revision string", NULL, NULL, NULL), + NEWLINE, "Unset software-revision string", NULL, cmd_inventory, + "software-revision"); + + commands_new(commands_new(commands_new(configure_inv, "firmware-revision", + "Set firmware-revision string", NULL, NULL, NULL), + NULL, "Inventory firmware-revision string", NULL, + cmd_store_env_value, "firmware-revision"), + NEWLINE, "Set firmware-revision string", NULL, cmd_inventory, + "firmware-revision"); + + commands_new(commands_new(unconfigure_inv, "firmware-revision", + "Unset firmware-revision string", NULL, NULL, NULL), + NEWLINE, "Unset firmware-revision string", NULL, cmd_inventory, + "firmware-revision"); + + commands_new(commands_new(commands_new(configure_inv, "serial-number", + "Set serial-number string", NULL, NULL, NULL), + NULL, "Inventory serial-number string", NULL, + cmd_store_env_value, "serial-number"), + NEWLINE, "Set serial-number string", NULL, cmd_inventory, "serial-number"); + + commands_new(commands_new(unconfigure_inv, "serial-number", + "Unset serial-number string", NULL, NULL, NULL), + NEWLINE, "Unset serial-number string", NULL, cmd_inventory, + "serial-number"); + + commands_new(commands_new(commands_new(configure_inv, "manufacturer", + "Set manufacturer string", NULL, NULL, NULL), + NULL, "Inventory manufacturer string", NULL, + cmd_store_env_value, "manufacturer"), + NEWLINE, "Set manufacturer string", NULL, cmd_inventory, "manufacturer"); + + commands_new(commands_new(unconfigure_inv, "manufacturer", + "Unset manufacturer string", NULL, NULL, NULL), + NEWLINE, "Unset manufacturer string", NULL, cmd_inventory, "manufacturer"); + + commands_new(commands_new(commands_new(configure_inv, "model", + "Set model string", NULL, NULL, NULL), + NULL, "Inventory model string", NULL, cmd_store_env_value, + "model"), + NEWLINE, "Set model string", NULL, cmd_inventory, "model"); + + commands_new(commands_new(unconfigure_inv, "model", "Unset model string", NULL, + NULL, NULL), + NEWLINE, "Unset model string", NULL, cmd_inventory, "model"); + + commands_new(commands_new(commands_new(configure_inv, "asset", + "Set asset string", NULL, NULL, NULL), + NULL, "Inventory asset string", NULL, cmd_store_env_value, + "asset"), + NEWLINE, "Set asset string", NULL, cmd_inventory, "asset"); + + commands_new(commands_new(unconfigure_inv, "asset", "Unset asset string", NULL, + NULL, NULL), + NEWLINE, "Unset asset string", NULL, cmd_inventory, "asset"); +} + +/** + * Register `configure inventory *` + * + */ +void +register_commands_configure_inventory(struct cmd_node *configure, + struct cmd_node *unconfigure) +{ + if (lldpctl_key_get_map(lldpctl_k_med_policy_type)[0].value <= 0) return; + + struct cmd_node *configure_inv = commands_new(configure, "inventory", + "Inventory configuration", NULL, NULL, NULL); + struct cmd_node *unconfigure_inv = commands_new(unconfigure, "inventory", + "Inventory configuration", NULL, NULL, NULL); + + register_commands_inv(configure_inv, unconfigure_inv); +} diff --git a/src/client/conf-lldp.c b/src/client/conf-lldp.c new file mode 100644 index 0000000..0c5b985 --- /dev/null +++ b/src/client/conf-lldp.c @@ -0,0 +1,762 @@ +/* -*- 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 <unistd.h> +#include <string.h> +#include <limits.h> + +#include "client.h" +#include "../log.h" + +static int +cmd_txdelay(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env, + const void *arg) +{ + const char *interval; + char interval_ms[8]; /* less than 2.5 hours */ + lldpctl_key_t key; + int arglen; + + log_debug("lldpctl", "set transmit delay"); + + lldpctl_atom_t *config = lldpctl_get_configuration(conn); + if (config == NULL) { + log_warnx("lldpctl", "unable to get configuration from lldpd. %s", + lldpctl_last_strerror(conn)); + return 0; + } + interval = cmdenv_get(env, "tx-interval"); + key = lldpctl_k_config_tx_interval; + /* interval is either <number> for seconds or <number>ms for milliseconds */ + if (interval) { + arglen = strlen(interval); + /* room for "ms" in interval, room for interval in interval_ms */ + if (arglen >= 2 && arglen - 2 < sizeof(interval_ms) && + strcmp("ms", interval + arglen - 2) == 0) { + /* remove "ms" suffix */ + memcpy(interval_ms, interval, arglen - 2); + interval_ms[arglen - 2] = '\0'; + /* substitute key and value */ + key = lldpctl_k_config_tx_interval_ms; + interval = interval_ms; + } + } + if (lldpctl_atom_set_str(config, key, interval) == NULL) { + log_warnx("lldpctl", "unable to set transmit delay. %s", + lldpctl_last_strerror(conn)); + lldpctl_atom_dec_ref(config); + return 0; + } + log_info("lldpctl", "transmit delay set to new value"); + lldpctl_atom_dec_ref(config); + return 1; +} + +static int +cmd_txhold(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env, + const void *arg) +{ + log_debug("lldpctl", "set transmit hold"); + + lldpctl_atom_t *config = lldpctl_get_configuration(conn); + if (config == NULL) { + log_warnx("lldpctl", "unable to get configuration from lldpd. %s", + lldpctl_last_strerror(conn)); + return 0; + } + if (lldpctl_atom_set_str(config, lldpctl_k_config_tx_hold, + cmdenv_get(env, "tx-hold")) == NULL) { + log_warnx("lldpctl", "unable to set transmit hold. %s", + lldpctl_last_strerror(conn)); + lldpctl_atom_dec_ref(config); + return 0; + } + log_info("lldpctl", "transmit hold set to new value %s", + cmdenv_get(env, "tx-hold")); + lldpctl_atom_dec_ref(config); + return 1; +} + +static int +cmd_status(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env, + const void *arg) +{ + lldpctl_atom_t *port; + const char *name; + const char *status = cmdenv_get(env, "status"); + + log_debug("lldpctl", "lldp administrative port status set to '%s'", status); + + if (!status || !strlen(status)) { + log_warnx("lldpctl", "no status specified"); + return 0; + } + + while ((port = cmd_iterate_on_ports(conn, env, &name))) { + if (lldpctl_atom_set_str(port, lldpctl_k_port_status, status) == NULL) { + log_warnx("lldpctl", + "unable to set LLDP status for %s." + " %s", + name, lldpctl_last_strerror(conn)); + } + } + + return 1; +} + +static int +cmd_agent_type(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env, + const void *arg) +{ + const char *str = arg; + int value = -1; + + log_debug("lldpctl", "set agent type to '%s'", str); + + lldpctl_atom_t *config = lldpctl_get_configuration(conn); + if (config == NULL) { + log_warnx("lldpctl", "unable to get configuration from lldpd. %s", + lldpctl_last_strerror(conn)); + return 0; + } + + for (lldpctl_map_t *b_map = + lldpctl_key_get_map(lldpctl_k_config_lldp_agent_type); + b_map->string; b_map++) { + if (!strcmp(b_map->string, str)) { + value = b_map->value; + break; + } + } + + if (value == -1) { + log_warnx("lldpctl", "invalid value"); + lldpctl_atom_dec_ref(config); + return 0; + } + + if (lldpctl_atom_set_int(config, lldpctl_k_config_lldp_agent_type, value) == + NULL) { + log_warnx("lldpctl", + "unable to set LLDP agent type." + " %s", + lldpctl_last_strerror(conn)); + lldpctl_atom_dec_ref(config); + return 0; + } + + log_info("lldpctl", "agent type set to new value : %s", str); + lldpctl_atom_dec_ref(config); + + return 1; +} + +static int +cmd_portid_type_local(struct lldpctl_conn_t *conn, struct writer *w, + struct cmd_env *env, const void *arg) +{ + lldpctl_atom_t *port; + const char *name; + const char *id = cmdenv_get(env, "port-id"); + const char *descr = cmdenv_get(env, "port-descr"); + + log_debug("lldpctl", + "lldp PortID TLV Subtype Local port-id '%s' port-descr '%s'", id, descr); + + if (!id || !strlen(id)) { + log_warnx("lldpctl", "no id specified"); + return 0; + } + + while ((port = cmd_iterate_on_ports(conn, env, &name))) { + if (lldpctl_atom_set_str(port, lldpctl_k_port_id, id) == NULL) { + log_warnx("lldpctl", + "unable to set LLDP PortID for %s." + " %s", + name, lldpctl_last_strerror(conn)); + } + if (descr && + lldpctl_atom_set_str(port, lldpctl_k_port_descr, descr) == NULL) { + log_warnx("lldpctl", + "unable to set LLDP Port Description for %s." + " %s", + name, lldpctl_last_strerror(conn)); + } + } + + return 1; +} + +static int +cmd_port_descr(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env, + const void *arg) +{ + lldpctl_atom_t *port; + const char *name; + const char *descr = cmdenv_get(env, "port-descr"); + + log_debug("lldpctl", "lldp port-descr '%s'", descr); + + while ((port = cmd_iterate_on_ports(conn, env, &name))) { + if (descr && + lldpctl_atom_set_str(port, lldpctl_k_port_descr, descr) == NULL) { + log_warnx("lldpctl", + "unable to set LLDP Port Description for %s." + " %s", + name, lldpctl_last_strerror(conn)); + } + } + + return 1; +} + +static int +cmd_portid_type(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env, + const void *arg) +{ + const char *value_str = 0; + int value = -1; + + log_debug("lldpctl", "lldp PortID TLV Subtype"); + + lldpctl_atom_t *config = lldpctl_get_configuration(conn); + if (config == NULL) { + log_warnx("lldpctl", "unable to get configuration from lldpd. %s", + lldpctl_last_strerror(conn)); + return 0; + } + + value_str = arg; + for (lldpctl_map_t *b_map = + lldpctl_key_get_map(lldpctl_k_config_lldp_portid_type); + b_map->string; b_map++) { + if (!strcmp(b_map->string, value_str)) { + value = b_map->value; + break; + } + } + + if (value == -1) { + log_warnx("lldpctl", "invalid value"); + lldpctl_atom_dec_ref(config); + return 0; + } + + if (lldpctl_atom_set_int(config, lldpctl_k_config_lldp_portid_type, value) == + NULL) { + log_warnx("lldpctl", + "unable to set LLDP PortID type." + " %s", + lldpctl_last_strerror(conn)); + lldpctl_atom_dec_ref(config); + return 0; + } + + log_info("lldpctl", "LLDP PortID TLV type set to new value : %s", value_str); + lldpctl_atom_dec_ref(config); + + return 1; +} + +static int +cmd_chassis_cap_advertise(struct lldpctl_conn_t *conn, struct writer *w, + struct cmd_env *env, const void *arg) +{ + log_debug("lldpctl", "lldp capabilities-advertisements %s", + arg ? "enable" : "disable"); + + lldpctl_atom_t *config = lldpctl_get_configuration(conn); + if (config == NULL) { + log_warnx("lldpctl", "unable to get configuration from lldpd. %s", + lldpctl_last_strerror(conn)); + return 0; + } + if (lldpctl_atom_set_int(config, lldpctl_k_config_chassis_cap_advertise, + arg ? 1 : 0) == NULL) { + log_warnx("lldpctl", + "unable to %s chassis capabilities advertisement: %s", + arg ? "enable" : "disable", lldpctl_last_strerror(conn)); + lldpctl_atom_dec_ref(config); + return 0; + } + log_info("lldpctl", "chassis capabilities advertisement %s", + arg ? "enabled" : "disabled"); + lldpctl_atom_dec_ref(config); + return 1; +} + +/* FIXME: see about compressing this with other functions */ +static int +cmd_chassis_mgmt_advertise(struct lldpctl_conn_t *conn, struct writer *w, + struct cmd_env *env, const void *arg) +{ + log_debug("lldpctl", "lldp management-addresses-advertisements %s", + arg ? "enable" : "disable"); + + lldpctl_atom_t *config = lldpctl_get_configuration(conn); + if (config == NULL) { + log_warnx("lldpctl", "unable to get configuration from lldpd. %s", + lldpctl_last_strerror(conn)); + return 0; + } + if (lldpctl_atom_set_int(config, lldpctl_k_config_chassis_mgmt_advertise, + arg ? 1 : 0) == NULL) { + log_warnx("lldpctl", + "unable to %s management addresses advertisement: %s", + arg ? "enable" : "disable", lldpctl_last_strerror(conn)); + lldpctl_atom_dec_ref(config); + return 0; + } + log_info("lldpctl", "management addresses advertisement %s", + arg ? "enabled" : "disabled"); + lldpctl_atom_dec_ref(config); + return 1; +} + +static int +cmd_vlan_tx(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env, + const void *arg) +{ + lldpctl_atom_t *port; + const char *name; + const char *vlan_id = cmdenv_get(env, "vlan-tx-id"); + const char *vlan_prio = cmdenv_get(env, "vlan-tx-prio"); + const char *vlan_dei = cmdenv_get(env, "vlan-tx-dei"); + + /* Default values are used to disable VLAN */ + int vlan_id_int = -1; + int vlan_prio_int = -1; + int vlan_dei_int = -1; + int vlan_tag = -1; + + if (!arg) + log_debug("lldpctl", + "lldp disable VLAN tagging of transmitted LLDP frames"); + else + log_debug("lldpctl", + "lldp enable VLAN tagging of transmitted LLDP frames with VLAN ID: '%s', Priority: '%s', DEI: '%s'", + vlan_id, vlan_prio, vlan_dei); + + if (arg) { + if (!vlan_id || !strlen(vlan_id)) { + log_warnx("lldpctl", "no VLAN id for TX specified"); + return 0; + } else { + const char *errstr; + vlan_id_int = strtonum(vlan_id, 0, 4094, &errstr); + if (errstr != NULL) { + log_warnx("lldpctl", "invalid VLAN ID for TX `%s': %s", + vlan_id, errstr); + return 0; + } + } + + if (!vlan_prio || !strlen(vlan_prio)) { + log_warnx("lldpctl", + "no VLAN priority for TX specified, using default (0)"); + /* Use default priority */ + vlan_prio_int = 0; + } else { + const char *errstr; + vlan_prio_int = strtonum(vlan_prio, 0, 7, &errstr); + if (errstr != NULL) { + log_warnx("lldpctl", "invalid VLAN piority `%s': %s", + vlan_prio, errstr); + return 0; + } + } + + if (!vlan_dei || !strlen(vlan_dei)) { + log_warnx("lldpctl", + "no VLAN Drop eligible indicator (DEI) for TX specified, using default (0)"); + /* Use default priority */ + vlan_dei_int = 0; + } else { + const char *errstr; + vlan_dei_int = strtonum(vlan_dei, 0, 1, &errstr); + if (errstr != NULL) { + log_warnx("lldpctl", + "invalid VLAN Drop eligible indicator (DEI) `%s': %s", + vlan_dei, errstr); + return 0; + } + } + /* Priority(3bits) | DEI(1bit) | VID(12bits) */ + vlan_tag = ((vlan_prio_int & 0x7) << 13) | + ((vlan_dei_int & 0x1) << 12) | (vlan_id_int & 0xfff); + } + + while ((port = cmd_iterate_on_ports(conn, env, &name))) { + if (lldpctl_atom_set_int(port, lldpctl_k_port_vlan_tx, vlan_tag) == + NULL) { + log_warnx("lldpctl", + "unable to set VLAN TX config on %s." + " %s", + name, lldpctl_last_strerror(conn)); + } + } + + return 1; +} + +#ifdef ENABLE_CUSTOM +static int +cmd_custom_tlv_set(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env, + const void *arg) +{ + lldpctl_atom_t *port; + const char *s; + const char *name; + uint8_t oui[LLDP_TLV_ORG_OUI_LEN]; + uint8_t oui_info[LLDP_TLV_ORG_OUI_INFO_MAXLEN]; + int oui_info_len = 0; + uint16_t subtype = 0; + const char *op = "add"; + + if (!arg || !strcmp(arg, "remove")) op = "remove"; + + log_debug("lldpctl", "lldp custom-tlv(s) %s", op); + + if (!arg) goto set; + + s = cmdenv_get(env, "oui"); + if (!s || + (sscanf(s, "%02hhx,%02hhx,%02hhx", &oui[0], &oui[1], &oui[2]) != 3 && + sscanf(s, "%02hhX,%02hhX,%02hhX", &oui[0], &oui[1], &oui[2]) != 3)) { + log_warnx("lldpctl", "invalid OUI value '%s'", s); + return 0; + } + + s = cmdenv_get(env, "subtype"); + if (!s) { + log_warnx("lldpctl", "no subtype specified"); + return 0; + } else { + const char *errstr; + subtype = strtonum(s, 0, 255, &errstr); + if (errstr != NULL) { + log_warnx("lldpctl", "invalid subtype value `%s': %s", s, + errstr); + return 0; + } + } + + s = cmdenv_get(env, "oui-info"); + /* This info is optional */ + if (s) { + const char delim[] = ","; + char *s_copy = strdup(s); + char *token = strtok(s_copy, delim); + while (token != NULL) { + if (sscanf(token, "%02hhx", &oui_info[oui_info_len]) == 1 || + sscanf(token, "%02hhX", &oui_info[oui_info_len]) == 1) + oui_info_len++; + if (oui_info_len >= sizeof(oui_info)) break; + token = strtok(NULL, delim); + } + free(s_copy); + } + + s = cmdenv_get(env, "replace"); + /* This info is optional */ + if (s) op = "replace"; + +set: + while ((port = cmd_iterate_on_ports(conn, env, &name))) { + lldpctl_atom_t *custom_tlvs; + if (!arg) { + lldpctl_atom_set(port, lldpctl_k_custom_tlvs_clear, NULL); + } else if (!(custom_tlvs = + lldpctl_atom_get(port, lldpctl_k_custom_tlvs))) { + log_warnx("lldpctl", "unable to get custom TLVs for port %s", + name); + } else { + lldpctl_atom_t *tlv = lldpctl_atom_create(custom_tlvs); + if (!tlv) { + log_warnx("lldpctl", + "unable to create new custom TLV for port %s", + name); + } else { + /* Configure custom TLV */ + lldpctl_atom_set_buffer(tlv, lldpctl_k_custom_tlv_oui, + oui, sizeof(oui)); + lldpctl_atom_set_int(tlv, + lldpctl_k_custom_tlv_oui_subtype, subtype); + lldpctl_atom_set_buffer(tlv, + lldpctl_k_custom_tlv_oui_info_string, oui_info, + oui_info_len); + lldpctl_atom_set_str(tlv, lldpctl_k_custom_tlv_op, op); + + /* Assign it to port */ + lldpctl_atom_set(port, lldpctl_k_custom_tlv, tlv); + + lldpctl_atom_dec_ref(tlv); + } + lldpctl_atom_dec_ref(custom_tlvs); + } + } + + return 1; +} + +static int +cmd_check_no_add_env(struct cmd_env *env, const void *arg) +{ + const char *what = arg; + if (cmdenv_get(env, "add")) return 0; + if (cmdenv_get(env, what)) return 0; + return 1; +} + +static int +cmd_check_no_replace_env(struct cmd_env *env, const void *arg) +{ + const char *what = arg; + if (cmdenv_get(env, "replace")) return 0; + if (cmdenv_get(env, what)) return 0; + return 1; +} + +static void +register_commands_configure_lldp_custom_tlvs(struct cmd_node *configure_lldp, + struct cmd_node *unconfigure_lldp) +{ + struct cmd_node *configure_custom_tlvs; + struct cmd_node *unconfigure_custom_tlvs; + struct cmd_node *configure_custom_tlvs_basic; + struct cmd_node *unconfigure_custom_tlvs_basic; + + configure_custom_tlvs = commands_new(configure_lldp, "custom-tlv", + "Add custom TLV(s) to be broadcast on ports", NULL, NULL, NULL); + + unconfigure_custom_tlvs = commands_new(unconfigure_lldp, "custom-tlv", + "Remove ALL custom TLV(s)", NULL, NULL, NULL); + + commands_new(unconfigure_custom_tlvs, NEWLINE, "Remove ALL custom TLV", NULL, + cmd_custom_tlv_set, NULL); + + commands_new(configure_custom_tlvs, "add", "Add custom TLV", + cmd_check_no_replace_env, cmd_store_env_and_pop, "add"); + commands_new(configure_custom_tlvs, "replace", "Replace custom TLV", + cmd_check_no_add_env, cmd_store_env_and_pop, "replace"); + + /* Basic form: 'configure lldp custom-tlv oui 11,22,33 subtype 44' */ + configure_custom_tlvs_basic = commands_new( + commands_new(commands_new(commands_new(configure_custom_tlvs, "oui", + "Organizationally Unique Identifier", NULL, + NULL, NULL), + NULL, "Organizationally Unique Identifier", NULL, + cmd_store_env_value, "oui"), + "subtype", "Organizationally Defined Subtype", NULL, NULL, NULL), + NULL, "Organizationally Defined Subtype", NULL, cmd_store_env_value, + "subtype"); + + commands_new(configure_custom_tlvs_basic, NEWLINE, + "Add custom TLV(s) to be broadcast on ports", NULL, cmd_custom_tlv_set, + "enable"); + + /* Basic form: 'unconfigure lldp custom-tlv oui 11,22,33 subtype 44' */ + unconfigure_custom_tlvs_basic = commands_new( + commands_new(commands_new(commands_new(unconfigure_custom_tlvs, "oui", + "Organizationally Unique Identifier", NULL, + NULL, NULL), + NULL, "Organizationally Unique Identifier", NULL, + cmd_store_env_value, "oui"), + "subtype", "Organizationally Defined Subtype", NULL, NULL, NULL), + NULL, "Organizationally Defined Subtype", NULL, cmd_store_env_value, + "subtype"); + + commands_new(unconfigure_custom_tlvs_basic, NEWLINE, + "Remove specific custom TLV", NULL, cmd_custom_tlv_set, "remove"); + + /* Extended form: 'configure custom-tlv lldp oui 11,22,33 subtype 44 oui-info + * 55,66,77,...' */ + commands_new(commands_new(commands_new(configure_custom_tlvs_basic, "oui-info", + "Organizationally Unique Identifier", NULL, NULL, + NULL), + NULL, "OUI Info String", NULL, cmd_store_env_value, + "oui-info"), + NEWLINE, "Add custom TLV(s) to be broadcast on ports", NULL, + cmd_custom_tlv_set, "enable"); +} +#endif /* ENABLE_CUSTOM */ + +static int +cmd_store_status_env_value(struct lldpctl_conn_t *conn, struct writer *w, + struct cmd_env *env, const void *value) +{ + return cmd_store_something_env_value("status", env, value); +} + +/** + * Register `configure lldp` commands. + * + * Those are the commands that are related to the LLDP protocol but not + * Dot1/Dot3/MED. Commands not related to LLDP should go in system instead. + */ +void +register_commands_configure_lldp(struct cmd_node *configure, + struct cmd_node *unconfigure) +{ + struct cmd_node *configure_lldp = + commands_new(configure, "lldp", "LLDP configuration", NULL, NULL, NULL); + struct cmd_node *unconfigure_lldp = + commands_new(unconfigure, "lldp", "LLDP configuration", NULL, NULL, NULL); + + commands_new( + commands_new(commands_new(configure_lldp, "tx-interval", + "Set LLDP transmit delay", cmd_check_no_env, NULL, + "ports"), + NULL, "LLDP transmit <delay> in seconds or <delay>ms in milliseconds", + NULL, cmd_store_env_value, "tx-interval"), + NEWLINE, "Set LLDP transmit delay", NULL, cmd_txdelay, NULL); + + commands_new(commands_new(commands_new(configure_lldp, "tx-hold", + "Set LLDP transmit hold", cmd_check_no_env, NULL, + "ports"), + NULL, "LLDP transmit hold in seconds", NULL, + cmd_store_env_value, "tx-hold"), + NEWLINE, "Set LLDP transmit hold", NULL, cmd_txhold, NULL); + + struct cmd_node *status = commands_new(configure_lldp, "status", + "Set administrative status", NULL, NULL, NULL); + + for (lldpctl_map_t *status_map = lldpctl_key_get_map(lldpctl_k_port_status); + status_map->string; status_map++) { + const char *tag = strdup(totag(status_map->string)); + SUPPRESS_LEAK(tag); + commands_new(commands_new(status, tag, status_map->string, NULL, + cmd_store_status_env_value, status_map->string), + NEWLINE, "Set port administrative status", NULL, cmd_status, NULL); + } + + /* Configure the various agent type we can configure. */ + struct cmd_node *configure_lldp_agent_type = commands_new(configure_lldp, + "agent-type", "LLDP agent type", NULL, NULL, NULL); + for (lldpctl_map_t *b_map = + lldpctl_key_get_map(lldpctl_k_config_lldp_agent_type); + b_map->string; b_map++) { + const char *tag = strdup(totag(b_map->string)); + SUPPRESS_LEAK(tag); + commands_new(commands_new(configure_lldp_agent_type, tag, b_map->string, + NULL, NULL, NULL), + NEWLINE, "Set LLDP agent type", NULL, cmd_agent_type, + b_map->string); + } + + /* Now handle the various portid subtypes we can configure. */ + struct cmd_node *configure_lldp_portid_type = commands_new(configure_lldp, + "portidsubtype", "LLDP PortID TLV Subtype", NULL, NULL, NULL); + + for (lldpctl_map_t *b_map = + lldpctl_key_get_map(lldpctl_k_config_lldp_portid_type); + b_map->string; b_map++) { + if (!strcmp(b_map->string, "ifname")) { + commands_new(commands_new(configure_lldp_portid_type, + b_map->string, "Interface Name", + cmd_check_no_env, NULL, "ports"), + NEWLINE, NULL, NULL, cmd_portid_type, b_map->string); + } else if (!strcmp(b_map->string, "local")) { + struct cmd_node *port_id = + commands_new(commands_new(configure_lldp_portid_type, + b_map->string, "Local", NULL, NULL, NULL), + NULL, "Port ID", NULL, cmd_store_env_value, "port-id"); + commands_new(port_id, NEWLINE, "Set local port ID", NULL, + cmd_portid_type_local, b_map->string); + commands_new(commands_new(commands_new(port_id, "description", + "Also set port description", NULL, + NULL, NULL), + NULL, "Port description", NULL, + cmd_store_env_value, "port-descr"), + NEWLINE, "Set local port ID and description", NULL, + cmd_portid_type_local, NULL); + } else if (!strcmp(b_map->string, "macaddress")) { + commands_new(commands_new(configure_lldp_portid_type, + b_map->string, "MAC Address", cmd_check_no_env, + NULL, "ports"), + NEWLINE, NULL, NULL, cmd_portid_type, b_map->string); + } + } + + commands_new(commands_new(commands_new(configure_lldp, "portdescription", + "Port Description", NULL, NULL, NULL), + NULL, "Port description", NULL, cmd_store_env_value, + "port-descr"), + NEWLINE, "Set port description", NULL, cmd_port_descr, NULL); + + commands_new(commands_new(configure_lldp, "capabilities-advertisements", + "Enable chassis capabilities advertisement", cmd_check_no_env, + NULL, "ports"), + NEWLINE, "Enable chassis capabilities advertisement", NULL, + cmd_chassis_cap_advertise, "enable"); + commands_new(commands_new(unconfigure_lldp, "capabilities-advertisements", + "Don't enable chassis capabilities advertisement", + cmd_check_no_env, NULL, "ports"), + NEWLINE, "Don't enable chassis capabilities advertisement", NULL, + cmd_chassis_cap_advertise, NULL); + + commands_new(commands_new(configure_lldp, "management-addresses-advertisements", + "Enable management addresses advertisement", cmd_check_no_env, + NULL, "ports"), + NEWLINE, "Enable management addresses advertisement", NULL, + cmd_chassis_mgmt_advertise, "enable"); + commands_new(commands_new(unconfigure_lldp, + "management-addresses-advertisements", + "Don't enable management addresses advertisement", + cmd_check_no_env, NULL, "ports"), + NEWLINE, "Don't enable management addresses advertisement", NULL, + cmd_chassis_mgmt_advertise, NULL); + + struct cmd_node *vlan_tx = + commands_new(commands_new(configure_lldp, "vlan-tx", + "Send LLDP frames with a VLAN tag", NULL, NULL, NULL), + NULL, "VLAN ID (0-4094)", NULL, cmd_store_env_value, "vlan-tx-id"); + + struct cmd_node *vlan_tx_prio = + commands_new(commands_new(vlan_tx, "priority", + "Also set a priority in a VLAN tag (default 0)", NULL, + NULL, NULL), + NULL, "Priority to be included in a VLAN tag (0-7)", NULL, + cmd_store_env_value, "vlan-tx-prio"); + + commands_new(vlan_tx, NEWLINE, "Enable VLAN tagging of transmitted LLDP frames", + NULL, cmd_vlan_tx, "enable"); + + commands_new(vlan_tx_prio, NEWLINE, + "Set VLAN ID and priority for transmitted frames", NULL, cmd_vlan_tx, + "enable"); + + commands_new( + commands_new( + commands_new(vlan_tx_prio, "dei", + "Also set a Drop eligible indicator (DEI) in a VLAN tag (default 0)", + NULL, NULL, NULL), + NULL, + "Drop eligible indicator (DEI) in a VLAN tag (0-don't drop; 1-drop)", + NULL, cmd_store_env_value, "vlan-tx-dei"), + NEWLINE, "Set VLAN ID, priority and DEI for transmitted frames", NULL, + cmd_vlan_tx, "enable"); + + commands_new(commands_new(unconfigure_lldp, "vlan-tx", + "Send LLDP frames without VLAN tag", NULL, NULL, NULL), + NEWLINE, "Disable VLAN tagging of transmitted LLDP frames", NULL, + cmd_vlan_tx, NULL); + +#ifdef ENABLE_CUSTOM + register_commands_configure_lldp_custom_tlvs(configure_lldp, unconfigure_lldp); +#endif +} diff --git a/src/client/conf-med.c b/src/client/conf-med.c new file mode 100644 index 0000000..8c03f8e --- /dev/null +++ b/src/client/conf-med.c @@ -0,0 +1,499 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <unistd.h> +#include <string.h> + +#include "client.h" +#include "../log.h" + +static int +_cmd_medlocation(struct lldpctl_conn_t *conn, struct cmd_env *env, int format) +{ + lldpctl_atom_t *port; + const char *name; + while ((port = cmd_iterate_on_ports(conn, env, &name))) { + lldpctl_atom_t *med_location = NULL, *med_locations = NULL; + const char *what = NULL; + int ok = 0; + + med_locations = lldpctl_atom_get(port, lldpctl_k_port_med_locations); + if (med_locations == NULL) { + log_warnx("lldpctl", + "unable to set LLDP-MED location: support seems unavailable"); + continue; /* Iterator needs to be run until end */ + } + + med_location = lldpctl_atom_iter_value(med_locations, + lldpctl_atom_iter_next(med_locations, + lldpctl_atom_iter(med_locations))); + + switch (format) { + case LLDP_MED_LOCFORMAT_COORD: + if ((what = "format", + lldpctl_atom_set_int(med_location, + lldpctl_k_med_location_format, format)) == NULL || + (what = "latitude", + lldpctl_atom_set_str(med_location, + lldpctl_k_med_location_latitude, + cmdenv_get(env, "latitude"))) == NULL || + (what = "longitude", + lldpctl_atom_set_str(med_location, + lldpctl_k_med_location_longitude, + cmdenv_get(env, "longitude"))) == NULL || + (what = "altitude", + lldpctl_atom_set_str(med_location, + lldpctl_k_med_location_altitude, + cmdenv_get(env, "altitude"))) == NULL || + (what = "altitude unit", + lldpctl_atom_set_str(med_location, + lldpctl_k_med_location_altitude_unit, + cmdenv_get(env, "altitude-unit"))) == NULL || + (what = "datum", + lldpctl_atom_set_str(med_location, + lldpctl_k_med_location_geoid, + cmdenv_get(env, "datum"))) == NULL) + log_warnx("lldpctl", + "unable to set LLDP MED location value for %s on %s. %s.", + what, name, lldpctl_last_strerror(conn)); + else + ok = 1; + break; + case LLDP_MED_LOCFORMAT_CIVIC: + if ((what = "format", + lldpctl_atom_set_int(med_location, + lldpctl_k_med_location_format, format)) == NULL || + (what = "country", + lldpctl_atom_set_str(med_location, + lldpctl_k_med_location_country, + cmdenv_get(env, "country"))) == NULL) { + log_warnx("lldpctl", + "unable to set LLDP MED location value for %s on %s. %s.", + what, name, lldpctl_last_strerror(conn)); + break; + } + ok = 1; + for (lldpctl_map_t *addr_map = + lldpctl_key_get_map(lldpctl_k_med_civicaddress_type); + addr_map->string; addr_map++) { + lldpctl_atom_t *cael, *caels; + const char *value = cmdenv_get(env, addr_map->string); + if (!value) continue; + + caels = lldpctl_atom_get(med_location, + lldpctl_k_med_location_ca_elements); + cael = lldpctl_atom_create(caels); + + if (lldpctl_atom_set_str(cael, + lldpctl_k_med_civicaddress_type, + addr_map->string) == NULL || + lldpctl_atom_set_str(cael, + lldpctl_k_med_civicaddress_value, + value) == NULL || + lldpctl_atom_set(med_location, + lldpctl_k_med_location_ca_elements, + cael) == NULL) { + log_warnx("lldpctl", + "unable to add a civic address element `%s`. %s", + addr_map->string, + lldpctl_last_strerror(conn)); + ok = 0; + } + + lldpctl_atom_dec_ref(cael); + lldpctl_atom_dec_ref(caels); + if (!ok) break; + } + break; + case LLDP_MED_LOCFORMAT_ELIN: + if (lldpctl_atom_set_int(med_location, + lldpctl_k_med_location_format, format) == NULL || + lldpctl_atom_set_str(med_location, + lldpctl_k_med_location_elin, + cmdenv_get(env, "elin")) == NULL) + log_warnx("lldpctl", + "unable to set LLDP MED location on %s. %s", name, + lldpctl_last_strerror(conn)); + else + ok = 1; + break; + } + if (ok) { + if (lldpctl_atom_set(port, lldpctl_k_port_med_locations, + med_location) == NULL) { + log_warnx("lldpctl", + "unable to set LLDP MED location on %s. %s.", name, + lldpctl_last_strerror(conn)); + } else + log_info("lldpctl", + "LLDP-MED location has been set for port %s", name); + } + + lldpctl_atom_dec_ref(med_location); + lldpctl_atom_dec_ref(med_locations); + } + return 1; +} + +static int +cmd_medlocation_coordinate(struct lldpctl_conn_t *conn, struct writer *w, + struct cmd_env *env, const void *arg) +{ + log_debug("lldpctl", "set MED location coordinate"); + return _cmd_medlocation(conn, env, LLDP_MED_LOCFORMAT_COORD); +} + +static int +cmd_medlocation_address(struct lldpctl_conn_t *conn, struct writer *w, + struct cmd_env *env, const void *arg) +{ + log_debug("lldpctl", "set MED location address"); + return _cmd_medlocation(conn, env, LLDP_MED_LOCFORMAT_CIVIC); +} + +static int +cmd_medlocation_elin(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env, + const void *arg) +{ + log_debug("lldpctl", "set MED location ELIN"); + return _cmd_medlocation(conn, env, LLDP_MED_LOCFORMAT_ELIN); +} + +static int +cmd_medpolicy(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env, + const void *arg) +{ + log_debug("lldpctl", "set MED policy"); + lldpctl_atom_t *iface; + while ((iface = cmd_iterate_on_interfaces(conn, env))) { + const char *name = + lldpctl_atom_get_str(iface, lldpctl_k_interface_name); + lldpctl_atom_t *port = lldpctl_get_port(iface); + lldpctl_atom_t *med_policy = NULL, *med_policies = NULL; + const char *what = NULL; + + med_policies = lldpctl_atom_get(port, lldpctl_k_port_med_policies); + if (med_policies == NULL) { + log_warnx("lldpctl", + "unable to set LLDP-MED policies: support seems unavailable"); + goto end; + } + + med_policy = lldpctl_atom_iter_value(med_policies, + lldpctl_atom_iter_next(med_policies, + lldpctl_atom_iter(med_policies))); + + if ((what = "application", + lldpctl_atom_set_str(med_policy, lldpctl_k_med_policy_type, + cmdenv_get(env, "application"))) == NULL || + (what = "unknown flag", + lldpctl_atom_set_int(med_policy, lldpctl_k_med_policy_unknown, + cmdenv_get(env, "unknown") ? 1 : 0)) == NULL || + (what = "tagged flag", + lldpctl_atom_set_int(med_policy, lldpctl_k_med_policy_tagged, + cmdenv_get(env, "tagged") ? 1 : 0)) == NULL || + (what = "vlan", + cmdenv_get(env, "vlan") ? + lldpctl_atom_set_str(med_policy, lldpctl_k_med_policy_vid, + cmdenv_get(env, "vlan")) : + lldpctl_atom_set_int(med_policy, lldpctl_k_med_policy_vid, + 0)) == NULL || + (what = "priority", + cmdenv_get(env, "priority") ? + lldpctl_atom_set_str(med_policy, + lldpctl_k_med_policy_priority, + cmdenv_get(env, "priority")) : + lldpctl_atom_set_int(med_policy, + lldpctl_k_med_policy_priority, 0)) == NULL || + (what = "dscp", + cmdenv_get(env, "dscp") ? + lldpctl_atom_set_str(med_policy, lldpctl_k_med_policy_dscp, + cmdenv_get(env, "dscp")) : + lldpctl_atom_set_int(med_policy, lldpctl_k_med_policy_dscp, + 0)) == NULL) + log_warnx("lldpctl", + "unable to set LLDP MED policy value for %s on %s. %s.", + what, name, lldpctl_last_strerror(conn)); + else { + if (lldpctl_atom_set(port, lldpctl_k_port_med_policies, + med_policy) == NULL) { + log_warnx("lldpctl", + "unable to set LLDP MED policy on %s. %s.", name, + lldpctl_last_strerror(conn)); + } else + log_info("lldpctl", + "LLDP-MED policy has been set for port %s", name); + } + + end: + lldpctl_atom_dec_ref(med_policy); + lldpctl_atom_dec_ref(med_policies); + lldpctl_atom_dec_ref(port); + } + return 1; +} + +/** + * Register `configure med location coordinate` commands. + */ +static void +register_commands_medloc_coord(struct cmd_node *configure_medlocation) +{ + /* MED location coordinate (set) */ + struct cmd_node *configure_medloc_coord = commands_new(configure_medlocation, + "coordinate", "MED location coordinate configuration", NULL, NULL, NULL); + commands_new(configure_medloc_coord, NEWLINE, + "Configure MED location coordinates", cmd_check_env, + cmd_medlocation_coordinate, + "latitude,longitude,altitude,altitude-unit,datum"); + commands_new(commands_new(configure_medloc_coord, "latitude", + "Specify latitude", cmd_check_no_env, NULL, "latitude"), + NULL, "Latitude as xx.yyyyN or xx.yyyyS", NULL, + cmd_store_env_value_and_pop2, "latitude"); + commands_new(commands_new(configure_medloc_coord, "longitude", + "Specify longitude", cmd_check_no_env, NULL, "longitude"), + NULL, "Longitude as xx.yyyyE or xx.yyyyW", NULL, + cmd_store_env_value_and_pop2, "longitude"); + struct cmd_node *altitude = + commands_new(commands_new(configure_medloc_coord, "altitude", + "Specify altitude", cmd_check_no_env, NULL, "altitude"), + NULL, "Altitude", NULL, cmd_store_env_value, "altitude"); + commands_new(altitude, "m", "meters", NULL, cmd_store_env_value_and_pop3, + "altitude-unit"); + commands_new(altitude, "f", "floors", NULL, cmd_store_env_value_and_pop3, + "altitude-unit"); + + struct cmd_node *datum = commands_new(configure_medloc_coord, "datum", + "Specify datum", cmd_check_no_env, NULL, "datum"); + for (lldpctl_map_t *datum_map = + lldpctl_key_get_map(lldpctl_k_med_location_geoid); + datum_map->string; datum_map++) + commands_new(datum, datum_map->string, NULL, NULL, + cmd_store_env_value_and_pop2, "datum"); +} + +/** + * Register `configure med location address` commands. + */ +static void +register_commands_medloc_addr(struct cmd_node *configure_medlocation) +{ + /* MED location address (set) */ + struct cmd_node *configure_medloc_addr = commands_new(configure_medlocation, + "address", "MED location address configuration", NULL, NULL, NULL); + commands_new(configure_medloc_addr, NEWLINE, "Configure MED location address", + cmd_check_env, cmd_medlocation_address, "country"); + + /* Country */ + commands_new(commands_new(configure_medloc_addr, "country", + "Specify country (mandatory)", cmd_check_no_env, NULL, + "country"), + NULL, "Country as a two-letter code", NULL, cmd_store_env_value_and_pop2, + "country"); + + /* Other fields */ + for (lldpctl_map_t *addr_map = + lldpctl_key_get_map(lldpctl_k_med_civicaddress_type); + addr_map->string; addr_map++) { + const char *tag = strdup(totag(addr_map->string)); + SUPPRESS_LEAK(tag); + commands_new(commands_new(configure_medloc_addr, tag, addr_map->string, + cmd_check_no_env, NULL, addr_map->string), + NULL, addr_map->string, NULL, cmd_store_env_value_and_pop2, + addr_map->string); + } +} + +/** + * Register `configure med location elin` commands. + */ +static void +register_commands_medloc_elin(struct cmd_node *configure_medlocation) +{ + /* MED location elin (set) */ + commands_new(commands_new(commands_new(configure_medlocation, "elin", + "MED location ELIN configuration", NULL, NULL, + NULL), + NULL, "ELIN number", NULL, cmd_store_env_value, "elin"), + NEWLINE, "Set MED location ELIN number", NULL, cmd_medlocation_elin, NULL); +} + +/** + * Register `configure med location` commands. + */ +static void +register_commands_medloc(struct cmd_node *configure_med) +{ + struct cmd_node *configure_medlocation = commands_new(configure_med, "location", + "MED location configuration", NULL, NULL, NULL); + + register_commands_medloc_coord(configure_medlocation); + register_commands_medloc_addr(configure_medlocation); + register_commands_medloc_elin(configure_medlocation); +} + +static int +cmd_check_application_but_no(struct cmd_env *env, const void *arg) +{ + const char *what = arg; + if (!cmdenv_get(env, "application")) return 0; + if (cmdenv_get(env, what)) return 0; + return 1; +} +static int +cmd_store_app_env_value_and_pop2(struct lldpctl_conn_t *conn, struct writer *w, + struct cmd_env *env, const void *value) +{ + return cmd_store_something_env_value_and_pop2("application", env, value); +} +static int +cmd_store_prio_env_value_and_pop2(struct lldpctl_conn_t *conn, struct writer *w, + struct cmd_env *env, const void *value) +{ + return cmd_store_something_env_value_and_pop2("priority", env, value); +} + +/** + * Register `configure med policy` commands. + */ +static void +register_commands_medpol(struct cmd_node *configure_med) +{ + struct cmd_node *configure_medpolicy = commands_new(configure_med, "policy", + "MED policy configuration", NULL, NULL, NULL); + + commands_new(configure_medpolicy, NEWLINE, "Apply new MED policy", + cmd_check_env, cmd_medpolicy, "application"); + + /* Application */ + struct cmd_node *configure_application = + commands_new(configure_medpolicy, "application", "MED policy application", + cmd_check_no_env, NULL, "application"); + + for (lldpctl_map_t *pol_map = lldpctl_key_get_map(lldpctl_k_med_policy_type); + pol_map->string; pol_map++) { + char *tag = strdup(totag(pol_map->string)); + SUPPRESS_LEAK(tag); + commands_new(configure_application, tag, pol_map->string, NULL, + cmd_store_app_env_value_and_pop2, pol_map->string); + } + + /* Remaining keywords */ + commands_new(configure_medpolicy, "unknown", "Set unknown flag", + cmd_check_application_but_no, cmd_store_env_and_pop, "unknown"); + commands_new(configure_medpolicy, "tagged", "Set tagged flag", + cmd_check_application_but_no, cmd_store_env_and_pop, "tagged"); + commands_new(commands_new(configure_medpolicy, "vlan", "VLAN advertising", + cmd_check_application_but_no, NULL, "vlan"), + NULL, "VLAN ID to advertise", NULL, cmd_store_env_value_and_pop2, "vlan"); + commands_new(commands_new(configure_medpolicy, "dscp", "DiffServ advertising", + cmd_check_application_but_no, NULL, "dscp"), + NULL, "DSCP value to advertise (between 0 and 63)", NULL, + cmd_store_env_value_and_pop2, "dscp"); + struct cmd_node *priority = commands_new(configure_medpolicy, "priority", + "MED policy priority", cmd_check_application_but_no, NULL, "priority"); + for (lldpctl_map_t *prio_map = + lldpctl_key_get_map(lldpctl_k_med_policy_priority); + prio_map->string; prio_map++) { + char *tag = strdup(totag(prio_map->string)); + SUPPRESS_LEAK(tag); + commands_new(priority, tag, prio_map->string, NULL, + cmd_store_prio_env_value_and_pop2, prio_map->string); + } +} + +static int +cmd_faststart(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env, + const void *arg) +{ + log_debug("lldpctl", "configure fast interval support"); + + lldpctl_atom_t *config = lldpctl_get_configuration(conn); + if (config == NULL) { + log_warnx("lldpctl", "unable to get configuration from lldpd. %s", + lldpctl_last_strerror(conn)); + return 0; + } + + const char *action = arg; + if ((!strcmp(action, "enable") && + (lldpctl_atom_set_int(config, lldpctl_k_config_fast_start_enabled, 1) == + NULL)) || + (!strcmp(action, "disable") && + (lldpctl_atom_set_int(config, lldpctl_k_config_fast_start_enabled, 0) == + NULL)) || + (!strcmp(action, "delay") && + (lldpctl_atom_set_str(config, lldpctl_k_config_fast_start_interval, + cmdenv_get(env, "tx-interval")) == NULL))) { + log_warnx("lldpctl", "unable to setup fast start. %s", + lldpctl_last_strerror(conn)); + lldpctl_atom_dec_ref(config); + return 0; + } + log_info("lldpctl", "configruation for fast start applied"); + lldpctl_atom_dec_ref(config); + return 1; +} + +/** + * Register "configure med fast-start *" + */ +static void +register_commands_medfast(struct cmd_node *med, struct cmd_node *nomed) +{ + struct cmd_node *configure_fast = commands_new(med, "fast-start", + "Fast start configuration", cmd_check_no_env, NULL, "ports"); + struct cmd_node *unconfigure_fast = commands_new(nomed, "fast-start", + "Fast start configuration", cmd_check_no_env, NULL, "ports"); + + /* Enable */ + commands_new(commands_new(configure_fast, "enable", "Enable fast start", NULL, + NULL, NULL), + NEWLINE, "Enable fast start", NULL, cmd_faststart, "enable"); + + /* Set TX delay */ + commands_new(commands_new(commands_new(configure_fast, "tx-interval", + "Set LLDP fast transmit delay", NULL, NULL, NULL), + NULL, "LLDP fast transmit delay in seconds", NULL, + cmd_store_env_value, "tx-interval"), + NEWLINE, "Set LLDP fast transmit delay", NULL, cmd_faststart, "delay"); + + /* Disable */ + commands_new(commands_new(unconfigure_fast, NEWLINE, "Disable fast start", NULL, + cmd_faststart, "disable"), + NEWLINE, "Disable fast start", NULL, cmd_faststart, "disable"); +} + +/** + * Register "configure med *" + */ +void +register_commands_configure_med(struct cmd_node *configure, + struct cmd_node *unconfigure) +{ + if (lldpctl_key_get_map(lldpctl_k_med_policy_type)[0].string == NULL) return; + + struct cmd_node *configure_med = + commands_new(configure, "med", "MED configuration", NULL, NULL, NULL); + struct cmd_node *unconfigure_med = + commands_new(unconfigure, "med", "MED configuration", NULL, NULL, NULL); + + register_commands_medloc(configure_med); + register_commands_medpol(configure_med); + register_commands_medpow(configure_med); + register_commands_medfast(configure_med, unconfigure_med); +} diff --git a/src/client/conf-power.c b/src/client/conf-power.c new file mode 100644 index 0000000..7c33540 --- /dev/null +++ b/src/client/conf-power.c @@ -0,0 +1,389 @@ +/* -*- 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 <unistd.h> +#include <string.h> + +#include "client.h" +#include "../log.h" + +static int +cmd_medpower(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env, + const void *arg) +{ + log_debug("lldpctl", "set MED power"); + lldpctl_atom_t *port; + const char *name; + while ((port = cmd_iterate_on_ports(conn, env, &name))) { + lldpctl_atom_t *med_power; + const char *what = NULL; + + med_power = lldpctl_atom_get(port, lldpctl_k_port_med_power); + if (med_power == NULL) { + log_warnx("lldpctl", + "unable to set LLDP-MED power: support seems unavailable"); + continue; /* Need to finish the loop */ + } + + if ((what = "device type", + lldpctl_atom_set_str(med_power, lldpctl_k_med_power_type, + cmdenv_get(env, "device-type"))) == NULL || + (what = "power source", + lldpctl_atom_set_str(med_power, lldpctl_k_med_power_source, + cmdenv_get(env, "source"))) == NULL || + (what = "power priority", + lldpctl_atom_set_str(med_power, lldpctl_k_med_power_priority, + cmdenv_get(env, "priority"))) == NULL || + (what = "power value", + lldpctl_atom_set_str(med_power, lldpctl_k_med_power_val, + cmdenv_get(env, "value"))) == NULL) + log_warnx("lldpctl", + "unable to set LLDP MED power value for %s on %s. %s.", + what, name, lldpctl_last_strerror(conn)); + else { + if (lldpctl_atom_set(port, lldpctl_k_port_med_power, + med_power) == NULL) { + log_warnx("lldpctl", + "unable to set LLDP MED power on %s. %s.", name, + lldpctl_last_strerror(conn)); + } else + log_info("lldpctl", + "LLDP-MED power has been set for port %s", name); + } + + lldpctl_atom_dec_ref(med_power); + } + return 1; +} + +static int +cmd_store_powerpairs_env_value_and_pop2(struct lldpctl_conn_t *conn, struct writer *w, + struct cmd_env *env, const void *value) +{ + return cmd_store_something_env_value_and_pop2("powerpairs", env, value); +} +static int +cmd_store_class_env_value_and_pop2(struct lldpctl_conn_t *conn, struct writer *w, + struct cmd_env *env, const void *value) +{ + return cmd_store_something_env_value_and_pop2("class", env, value); +} +static int +cmd_store_prio_env_value_and_pop2(struct lldpctl_conn_t *conn, struct writer *w, + struct cmd_env *env, const void *value) +{ + return cmd_store_something_env_value_and_pop2("priority", env, value); +} + +static int +cmd_dot3power(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env, + const void *arg) +{ + log_debug("lldpctl", "set dot3 power"); + lldpctl_atom_t *port; + const char *name; + while ((port = cmd_iterate_on_ports(conn, env, &name))) { + lldpctl_atom_t *dot3_power; + const char *what = NULL; + int ok = 1; + + dot3_power = lldpctl_atom_get(port, lldpctl_k_port_dot3_power); + if (dot3_power == NULL) { + log_warnx("lldpctl", + "unable to set Dot3 power: support seems unavailable"); + continue; /* Need to finish the loop */ + } + + if ((what = "device type", + lldpctl_atom_set_str(dot3_power, + lldpctl_k_dot3_power_devicetype, + cmdenv_get(env, "device-type"))) == NULL || + /* Flags */ + (what = "supported flag", + lldpctl_atom_set_int(dot3_power, lldpctl_k_dot3_power_supported, + cmdenv_get(env, "supported") ? 1 : 0)) == NULL || + (what = "enabled flag", + lldpctl_atom_set_int(dot3_power, lldpctl_k_dot3_power_enabled, + cmdenv_get(env, "enabled") ? 1 : 0)) == NULL || + (what = "paircontrol flag", + lldpctl_atom_set_int(dot3_power, + lldpctl_k_dot3_power_paircontrol, + cmdenv_get(env, "paircontrol") ? 1 : 0)) == NULL || + /* Powerpairs */ + (what = "power pairs", + lldpctl_atom_set_str(dot3_power, lldpctl_k_dot3_power_pairs, + cmdenv_get(env, "powerpairs"))) == NULL || + /* Class */ + (what = "power class", + cmdenv_get(env, "class") ? + lldpctl_atom_set_str(dot3_power, lldpctl_k_dot3_power_class, + cmdenv_get(env, "class")) : + lldpctl_atom_set_int(dot3_power, lldpctl_k_dot3_power_class, + 0)) == NULL || + (what = "802.3at type", + lldpctl_atom_set_int(dot3_power, lldpctl_k_dot3_power_type, + 0)) == NULL) { + log_warnx("lldpctl", + "unable to set LLDP Dot3 power value for %s on %s. %s.", + what, name, lldpctl_last_strerror(conn)); + ok = 0; + } else if (cmdenv_get(env, "typeat")) { + int typeat = cmdenv_get(env, "typeat")[0] - '0'; + const char *source = cmdenv_get(env, "source"); + if ((what = "802.3at type", + lldpctl_atom_set_int(dot3_power, + lldpctl_k_dot3_power_type, typeat)) == NULL || + (what = "source", + lldpctl_atom_set_int(dot3_power, + lldpctl_k_dot3_power_source, + (!strcmp(source, "primary")) ? + LLDP_DOT3_POWER_SOURCE_PRIMARY : + (!strcmp(source, "backup")) ? + LLDP_DOT3_POWER_SOURCE_BACKUP : + (!strcmp(source, "pse")) ? + LLDP_DOT3_POWER_SOURCE_PSE : + (!strcmp(source, "local")) ? + LLDP_DOT3_POWER_SOURCE_LOCAL : + (!strcmp(source, "both")) ? + LLDP_DOT3_POWER_SOURCE_BOTH : + LLDP_DOT3_POWER_SOURCE_UNKNOWN)) == NULL || + (what = "priority", + lldpctl_atom_set_str(dot3_power, + lldpctl_k_dot3_power_priority, + cmdenv_get(env, "priority"))) == NULL || + (what = "requested power", + lldpctl_atom_set_str(dot3_power, + lldpctl_k_dot3_power_requested, + cmdenv_get(env, "requested"))) == NULL || + (what = "allocated power", + lldpctl_atom_set_str(dot3_power, + lldpctl_k_dot3_power_allocated, + cmdenv_get(env, "allocated"))) == NULL) { + log_warnx("lldpctl", + "unable to set LLDP Dot3 power value for %s on %s. %s.", + what, name, lldpctl_last_strerror(conn)); + ok = 0; + } + } + if (ok) { + if (lldpctl_atom_set(port, lldpctl_k_port_dot3_power, + dot3_power) == NULL) { + log_warnx("lldpctl", + "unable to set LLDP Dot3 power on %s. %s.", name, + lldpctl_last_strerror(conn)); + } else + log_info("lldpctl", + "LLDP Dot3 power has been set for port %s", name); + } + + lldpctl_atom_dec_ref(dot3_power); + } + return 1; +} + +static int +cmd_check_type_but_no(struct cmd_env *env, const void *arg) +{ + const char *what = arg; + if (!cmdenv_get(env, "device-type")) return 0; + if (cmdenv_get(env, what)) return 0; + return 1; +} +static int +cmd_check_typeat_but_no(struct cmd_env *env, const void *arg) +{ + const char *what = arg; + if (!cmdenv_get(env, "typeat")) return 0; + if (cmdenv_get(env, what)) return 0; + return 1; +} +static int +cmd_check_type(struct cmd_env *env, const char *type) +{ + const char *etype = cmdenv_get(env, "device-type"); + if (!etype) return 0; + return (!strcmp(type, etype)); +} +static int +cmd_check_pse(struct cmd_env *env, const void *arg) +{ + return cmd_check_type(env, "pse"); +} +static int +cmd_check_pd(struct cmd_env *env, const void *arg) +{ + return cmd_check_type(env, "pd"); +} + +static void +register_commands_pow_source(struct cmd_node *source) +{ + commands_new(source, "unknown", "Unknown power source", NULL, + cmd_store_env_value_and_pop2, "source"); + commands_new(source, "primary", "Primary power source", cmd_check_pse, + cmd_store_env_value_and_pop2, "source"); + commands_new(source, "backup", "Backup power source", cmd_check_pse, + cmd_store_env_value_and_pop2, "source"); + commands_new(source, "pse", "Power source is PSE", cmd_check_pd, + cmd_store_env_value_and_pop2, "source"); + commands_new(source, "local", "Local power source", cmd_check_pd, + cmd_store_env_value_and_pop2, "source"); + commands_new(source, "both", "Both PSE and local source available", + cmd_check_pd, cmd_store_env_value_and_pop2, "source"); +} + +static void +register_commands_pow_priority(struct cmd_node *priority, int key) +{ + for (lldpctl_map_t *prio_map = lldpctl_key_get_map(key); prio_map->string; + prio_map++) { + char *tag = strdup(totag(prio_map->string)); + SUPPRESS_LEAK(tag); + commands_new(priority, tag, prio_map->string, NULL, + cmd_store_prio_env_value_and_pop2, prio_map->string); + } +} + +/** + * Register `configure med power` commands. + */ +void +register_commands_medpow(struct cmd_node *configure_med) +{ + struct cmd_node *configure_medpower = commands_new(configure_med, "power", + "MED power configuration", NULL, NULL, NULL); + + commands_new(configure_medpower, NEWLINE, "Apply new MED power configuration", + cmd_check_env, cmd_medpower, "device-type,source,priority,value"); + + /* Type: PSE or PD */ + commands_new(configure_medpower, "pd", "MED power consumer", cmd_check_no_env, + cmd_store_env_value_and_pop, "device-type"); + commands_new(configure_medpower, "pse", "MED power provider", cmd_check_no_env, + cmd_store_env_value_and_pop, "device-type"); + + /* Source */ + struct cmd_node *source = commands_new(configure_medpower, "source", + "MED power source", cmd_check_type_but_no, NULL, "source"); + register_commands_pow_source(source); + + /* Priority */ + struct cmd_node *priority = commands_new(configure_medpower, "priority", + "MED power priority", cmd_check_type_but_no, NULL, "priority"); + register_commands_pow_priority(priority, lldpctl_k_med_power_priority); + + /* Value */ + commands_new(commands_new(configure_medpower, "value", "MED power value", + cmd_check_type_but_no, NULL, "value"), + NULL, "MED power value in milliwatts", NULL, cmd_store_env_value_and_pop2, + "value"); +} + +static int +cmd_check_env_power(struct cmd_env *env, const void *nothing) +{ + /* We need type and powerpair but if we have typeat, we also request + * source, priority, requested and allocated. */ + if (!cmdenv_get(env, "device-type")) return 0; + if (!cmdenv_get(env, "powerpairs")) return 0; + if (cmdenv_get(env, "typeat")) { + return (!!cmdenv_get(env, "source") && !!cmdenv_get(env, "priority") && + !!cmdenv_get(env, "requested") && !!cmdenv_get(env, "allocated")); + } + return 1; +} + +/** + * Register `configure med dot3` commands. + */ +void +register_commands_dot3pow(struct cmd_node *configure_dot3) +{ + struct cmd_node *configure_dot3power = commands_new(configure_dot3, "power", + "Dot3 power configuration", NULL, NULL, NULL); + + commands_new(configure_dot3power, NEWLINE, "Apply new Dot3 power configuration", + cmd_check_env_power, cmd_dot3power, NULL); + + /* Type: PSE or PD */ + commands_new(configure_dot3power, "pd", "Dot3 power consumer", cmd_check_no_env, + cmd_store_env_value_and_pop, "device-type"); + commands_new(configure_dot3power, "pse", "Dot3 power provider", + cmd_check_no_env, cmd_store_env_value_and_pop, "device-type"); + + /* Flags */ + commands_new(configure_dot3power, "supported", "MDI power support present", + cmd_check_type_but_no, cmd_store_env_and_pop, "supported"); + commands_new(configure_dot3power, "enabled", "MDI power support enabled", + cmd_check_type_but_no, cmd_store_env_and_pop, "enabled"); + commands_new(configure_dot3power, "paircontrol", + "MDI power pair can be selected", cmd_check_type_but_no, + cmd_store_env_and_pop, "paircontrol"); + + /* Power pairs */ + struct cmd_node *powerpairs = commands_new(configure_dot3power, "powerpairs", + "Which pairs are currently used for power (mandatory)", + cmd_check_type_but_no, NULL, "powerpairs"); + for (lldpctl_map_t *pp_map = lldpctl_key_get_map(lldpctl_k_dot3_power_pairs); + pp_map->string; pp_map++) { + commands_new(powerpairs, pp_map->string, pp_map->string, NULL, + cmd_store_powerpairs_env_value_and_pop2, pp_map->string); + } + + /* Class */ + struct cmd_node *class = commands_new(configure_dot3power, "class", + "Power class", cmd_check_type_but_no, NULL, "class"); + for (lldpctl_map_t *class_map = lldpctl_key_get_map(lldpctl_k_dot3_power_class); + class_map->string; class_map++) { + const char *tag = strdup(totag(class_map->string)); + SUPPRESS_LEAK(tag); + commands_new(class, tag, class_map->string, NULL, + cmd_store_class_env_value_and_pop2, class_map->string); + } + + /* 802.3at type */ + struct cmd_node *typeat = commands_new(configure_dot3power, "type", + "802.3at device type", cmd_check_type_but_no, NULL, "typeat"); + commands_new(typeat, "1", "802.3at type 1", NULL, cmd_store_env_value_and_pop2, + "typeat"); + commands_new(typeat, "2", "802.3at type 2", NULL, cmd_store_env_value_and_pop2, + "typeat"); + + /* Source */ + struct cmd_node *source = commands_new(configure_dot3power, "source", + "802.3at dot3 power source (mandatory)", cmd_check_typeat_but_no, NULL, + "source"); + register_commands_pow_source(source); + + /* Priority */ + struct cmd_node *priority = commands_new(configure_dot3power, "priority", + "802.3at dot3 power priority (mandatory)", cmd_check_typeat_but_no, NULL, + "priority"); + register_commands_pow_priority(priority, lldpctl_k_dot3_power_priority); + + /* Values */ + commands_new(commands_new(configure_dot3power, "requested", + "802.3at dot3 power value requested (mandatory)", + cmd_check_typeat_but_no, NULL, "requested"), + NULL, "802.3at power value requested in milliwatts", NULL, + cmd_store_env_value_and_pop2, "requested"); + commands_new(commands_new(configure_dot3power, "allocated", + "802.3at dot3 power value allocated (mandatory)", + cmd_check_typeat_but_no, NULL, "allocated"), + NULL, "802.3at power value allocated in milliwatts", NULL, + cmd_store_env_value_and_pop2, "allocated"); +} diff --git a/src/client/conf-system.c b/src/client/conf-system.c new file mode 100644 index 0000000..0700ec2 --- /dev/null +++ b/src/client/conf-system.c @@ -0,0 +1,602 @@ +/* -*- 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 <unistd.h> +#include <string.h> +#include <sys/utsname.h> + +#include "client.h" +#include "../log.h" + +static int +cmd_iface_pattern(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env, + const void *arg) +{ + log_debug("lldpctl", "set iface pattern"); + + lldpctl_atom_t *config = lldpctl_get_configuration(conn); + if (config == NULL) { + log_warnx("lldpctl", "unable to get configuration from lldpd. %s", + lldpctl_last_strerror(conn)); + return 0; + } + + const char *value = cmdenv_get(env, "iface-pattern"); + if (lldpctl_atom_set_str(config, lldpctl_k_config_iface_pattern, value) == + NULL) { + log_warnx("lldpctl", "unable to set iface-pattern. %s", + lldpctl_last_strerror(conn)); + lldpctl_atom_dec_ref(config); + return 0; + } + log_info("lldpctl", "iface-pattern set to new value %s", + value ? value : "(none)"); + lldpctl_atom_dec_ref(config); + return 1; +} + +static int +cmd_perm_iface_pattern(struct lldpctl_conn_t *conn, struct writer *w, + struct cmd_env *env, const void *arg) +{ + log_debug("lldpctl", "set permanent iface pattern"); + + lldpctl_atom_t *config = lldpctl_get_configuration(conn); + if (config == NULL) { + log_warnx("lldpctl", "unable to get configuration from lldpd. %s", + lldpctl_last_strerror(conn)); + return 0; + } + + const char *value = cmdenv_get(env, "iface-pattern"); + if (lldpctl_atom_set_str(config, lldpctl_k_config_perm_iface_pattern, value) == + NULL) { + log_warnx("lldpctl", "unable to set permanent iface pattern. %s", + lldpctl_last_strerror(conn)); + lldpctl_atom_dec_ref(config); + return 0; + } + log_info("lldpctl", "permanent iface pattern set to new value %s", + value ? value : "(none)"); + lldpctl_atom_dec_ref(config); + return 1; +} + +static int +cmd_iface_promisc(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env, + const void *arg) +{ + lldpctl_atom_t *config = lldpctl_get_configuration(conn); + if (config == NULL) { + log_warnx("lldpctl", "unable to get configuration from lldpd. %s", + lldpctl_last_strerror(conn)); + return 0; + } + if (lldpctl_atom_set_int(config, lldpctl_k_config_iface_promisc, arg ? 1 : 0) == + NULL) { + log_warnx("lldpctl", "unable to %s promiscuous mode: %s", + arg ? "enable" : "disable", lldpctl_last_strerror(conn)); + lldpctl_atom_dec_ref(config); + return 0; + } + log_info("lldpctl", "interface promiscuous mode %s", + arg ? "enabled" : "disabled"); + lldpctl_atom_dec_ref(config); + return 1; +} + +static int +cmd_system_description(struct lldpctl_conn_t *conn, struct writer *w, + struct cmd_env *env, const void *arg) +{ + int platform = 0; + const char *what = arg; + const char *value; + if (!strcmp(what, "system")) { + value = cmdenv_get(env, "description"); + } else { + value = cmdenv_get(env, "platform"); + platform = 1; + } + log_debug("lldpctl", "set %s description", what); + lldpctl_atom_t *config = lldpctl_get_configuration(conn); + if (config == NULL) { + log_warnx("lldpctl", "unable to get configuration from lldpd. %s", + lldpctl_last_strerror(conn)); + return 0; + } + if (lldpctl_atom_set_str(config, + platform ? lldpctl_k_config_platform : lldpctl_k_config_description, + value) == NULL) { + log_warnx("lldpctl", "unable to set description. %s", + lldpctl_last_strerror(conn)); + lldpctl_atom_dec_ref(config); + return 0; + } + log_info("lldpctl", "description set to new value %s", + value ? value : "(none)"); + lldpctl_atom_dec_ref(config); + return 1; +} + +static int +cmd_system_chassisid(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env, + const void *arg) +{ + const char *value; + value = cmdenv_get(env, "description"); + log_debug("lldpctl", "set chassis ID"); + lldpctl_atom_t *config = lldpctl_get_configuration(conn); + if (config == NULL) { + log_warnx("lldpctl", "unable to get configuration from lldpd. %s", + lldpctl_last_strerror(conn)); + return 0; + } + if (lldpctl_atom_set_str(config, lldpctl_k_config_cid_string, value) == NULL) { + log_warnx("lldpctl", "unable to set chassis ID. %s", + lldpctl_last_strerror(conn)); + lldpctl_atom_dec_ref(config); + return 0; + } + log_info("lldpctl", "chassis ID set to new value %s", value ? value : "(none)"); + lldpctl_atom_dec_ref(config); + return 1; +} + +static int +cmd_management(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env, + const void *arg) +{ + log_debug("lldpctl", "set management pattern"); + + lldpctl_atom_t *config = lldpctl_get_configuration(conn); + if (config == NULL) { + log_warnx("lldpctl", "unable to get configuration from lldpd. %s", + lldpctl_last_strerror(conn)); + return 0; + } + + const char *value = cmdenv_get(env, "management-pattern"); + if (lldpctl_atom_set_str(config, lldpctl_k_config_mgmt_pattern, value) == + NULL) { + log_warnx("lldpctl", "unable to set management pattern. %s", + lldpctl_last_strerror(conn)); + lldpctl_atom_dec_ref(config); + return 0; + } + log_info("lldpctl", "management pattern set to new value %s", + value ? value : "(none)"); + lldpctl_atom_dec_ref(config); + return 1; +} + +static int +cmd_hostname(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env, + const void *arg) +{ + struct utsname un; + log_debug("lldpctl", "set system name"); + + lldpctl_atom_t *config = lldpctl_get_configuration(conn); + if (config == NULL) { + log_warnx("lldpctl", "unable to get configuration from lldpd. %s", + lldpctl_last_strerror(conn)); + return 0; + } + + const char *value = cmdenv_get(env, "hostname"); + if (value && strlen(value) == 1 && value[0] == '.') { + if (uname(&un) < 0) { + log_warn("lldpctl", "cannot get node name"); + lldpctl_atom_dec_ref(config); + return 0; + } + char *c = strchr(un.nodename, '.'); + if (c) *c = 0; + value = un.nodename; + } + if (lldpctl_atom_set_str(config, lldpctl_k_config_hostname, value) == NULL) { + log_warnx("lldpctl", "unable to set system name. %s", + lldpctl_last_strerror(conn)); + lldpctl_atom_dec_ref(config); + return 0; + } + log_info("lldpctl", "system name set to new value %s", + value ? value : "(none)"); + lldpctl_atom_dec_ref(config); + return 1; +} + +static int +cmd_capability(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env, + const void *arg) +{ + log_debug("lldpctl", "set capabilities"); + + int ret = 0; + lldpctl_atom_t *config = lldpctl_get_configuration(conn); + lldpctl_atom_t *chassis = NULL; + if (config == NULL) { + log_warnx("lldpctl", "unable to get configuration from lldpd. %s", + lldpctl_last_strerror(conn)); + goto cmd_capability_end; + } + + if (!strcmp(arg, "configure")) { + + const char *s = cmdenv_get(env, "capabilities"); + if (s) { + chassis = lldpctl_get_local_chassis(conn); + if (chassis == NULL) { + log_warnx("lldpctl", + "unable to get local chassis from lldpd. %s", + lldpctl_last_strerror(conn)); + goto cmd_capability_end; + } + u_int16_t value = 0; + const char delim[] = ","; + char *s_copy = strdup(s); + char *token = strtok(s_copy, delim); + while (token != NULL) { + if (!strcmp(token, "other")) { + value |= LLDP_CAP_OTHER; + } else if (!strcmp(token, "repeater")) { + value |= LLDP_CAP_REPEATER; + } else if (!strcmp(token, "bridge")) { + value |= LLDP_CAP_BRIDGE; + } else if (!strcmp(token, "wlan")) { + value |= LLDP_CAP_WLAN; + } else if (!strcmp(token, "router")) { + value |= LLDP_CAP_ROUTER; + } else if (!strcmp(token, "telephone")) { + value |= LLDP_CAP_TELEPHONE; + } else if (!strcmp(token, "docsis")) { + value |= LLDP_CAP_DOCSIS; + } else if (!strcmp(token, "station")) { + value |= LLDP_CAP_STATION; + } else { + log_warnx("lldpctl", "capability %s not found", + token); + } + token = strtok(NULL, delim); + } + free(s_copy); + + if (lldpctl_atom_set_int(chassis, lldpctl_k_chassis_cap_enabled, + value) == NULL) { + log_warnx("lldpctl", + "unable to set system capabilities. %s", + lldpctl_last_strerror(conn)); + goto cmd_capability_end; + } + if (lldpctl_atom_set_int(config, + lldpctl_k_config_chassis_cap_override, 1) == NULL) { + log_warnx("lldpctl", + "unable to set system capabilities override. %s", + lldpctl_last_strerror(conn)); + goto cmd_capability_end; + } + log_debug("lldpctl", "system capabilities set to new value %d", + value); + } + } else { + if (lldpctl_atom_set_int(config, lldpctl_k_config_chassis_cap_override, + 0) == NULL) { + log_warnx("lldpctl", + "unable to set system capabilities to not override. %s", + lldpctl_last_strerror(conn)); + goto cmd_capability_end; + } + } + + ret = 1; +cmd_capability_end: + lldpctl_atom_dec_ref(chassis); + lldpctl_atom_dec_ref(config); + return ret; +} + +static int +cmd_update_descriptions(struct lldpctl_conn_t *conn, struct writer *w, + struct cmd_env *env, const void *arg) +{ + lldpctl_atom_t *config = lldpctl_get_configuration(conn); + if (config == NULL) { + log_warnx("lldpctl", "unable to get configuration from lldpd. %s", + lldpctl_last_strerror(conn)); + return 0; + } + if (lldpctl_atom_set_int(config, lldpctl_k_config_ifdescr_update, + arg ? 1 : 0) == NULL) { + log_warnx("lldpctl", "unable to %s interface description update: %s", + arg ? "enable" : "disable", lldpctl_last_strerror(conn)); + lldpctl_atom_dec_ref(config); + return 0; + } + log_info("lldpctl", "interface description update %s", + arg ? "enabled" : "disabled"); + lldpctl_atom_dec_ref(config); + return 1; +} + +static int +cmd_bondslave_srcmac_type(struct lldpctl_conn_t *conn, struct writer *w, + struct cmd_env *env, const void *arg) +{ + const char *value_str = 0; + int value = -1; + + log_debug("lldpctl", "bond slave src mac"); + + lldpctl_atom_t *config = lldpctl_get_configuration(conn); + if (config == NULL) { + log_warnx("lldpctl", "unable to get configuration from lldpd. %s", + lldpctl_last_strerror(conn)); + return 0; + } + + value_str = arg; + for (lldpctl_map_t *b_map = + lldpctl_key_get_map(lldpctl_k_config_bond_slave_src_mac_type); + b_map->string; b_map++) { + if (!strcmp(b_map->string, value_str)) { + value = b_map->value; + break; + } + } + + if (value == -1) { + log_warnx("lldpctl", "invalid value"); + lldpctl_atom_dec_ref(config); + return 0; + } + + if (lldpctl_atom_set_int(config, lldpctl_k_config_bond_slave_src_mac_type, + value) == NULL) { + log_warnx("lldpctl", + "unable to set bond slave src mac type." + " %s", + lldpctl_last_strerror(conn)); + lldpctl_atom_dec_ref(config); + return 0; + } + + log_info("lldpctl", "bond slave src mac set to new value: %s", value_str); + lldpctl_atom_dec_ref(config); + + return 1; +} + +static int +cmd_maxneighs(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env, + const void *arg) +{ + log_debug("lldpctl", "set maximum neighbors"); + + lldpctl_atom_t *config = lldpctl_get_configuration(conn); + if (config == NULL) { + log_warnx("lldpctl", "unable to get configuration from lldpd. %s", + lldpctl_last_strerror(conn)); + return 0; + } + if (lldpctl_atom_set_str(config, lldpctl_k_config_max_neighbors, + cmdenv_get(env, "max-neighbors")) == NULL) { + log_warnx("lldpctl", "unable to set maximum of neighbors. %s", + lldpctl_last_strerror(conn)); + lldpctl_atom_dec_ref(config); + return 0; + } + log_info("lldpctl", "maximum neighbors set to new value %s", + cmdenv_get(env, "max-neighbors")); + lldpctl_atom_dec_ref(config); + return 1; +} + +/** + * Register `configure system bond-slave-src-mac-type` + */ +static void +register_commands_srcmac_type(struct cmd_node *configure) +{ + struct cmd_node *bond_slave_src_mac_type = + commands_new(configure, "bond-slave-src-mac-type", + "Set LLDP bond slave source MAC type", NULL, NULL, NULL); + + for (lldpctl_map_t *b_map = + lldpctl_key_get_map(lldpctl_k_config_bond_slave_src_mac_type); + b_map->string; b_map++) { + if (!strcmp(b_map->string, "real")) { + commands_new(commands_new(bond_slave_src_mac_type, + b_map->string, "Real mac", NULL, NULL, NULL), + NEWLINE, NULL, NULL, cmd_bondslave_srcmac_type, + b_map->string); + } else if (!strcmp(b_map->string, "zero")) { + commands_new(commands_new(bond_slave_src_mac_type, + b_map->string, "All zero mac", NULL, NULL, + NULL), + NEWLINE, NULL, NULL, cmd_bondslave_srcmac_type, + b_map->string); + } else if (!strcmp(b_map->string, "fixed")) { + commands_new(commands_new(bond_slave_src_mac_type, + b_map->string, "Fixed value (3Com card)", NULL, + NULL, NULL), + NEWLINE, NULL, NULL, cmd_bondslave_srcmac_type, + b_map->string); + } else if (!strcmp(b_map->string, "local")) { + commands_new(commands_new(bond_slave_src_mac_type, + b_map->string, + "Real Mac with locally " + "administered bit set", + NULL, NULL, NULL), + NEWLINE, NULL, NULL, cmd_bondslave_srcmac_type, + b_map->string); + } + } +} + +static void +register_commands_capabilities(struct cmd_node *configure, struct cmd_node *unconfigure) +{ + struct cmd_node *configure_capability = commands_new(configure, "capabilities", + "Capabilities configuration", cmd_check_no_env, NULL, "ports"); + struct cmd_node *unconfigure_capability = + commands_new(unconfigure, "capabilities", "Capabilities configuration", + cmd_check_no_env, NULL, "ports"); + + /* Override */ + commands_new(commands_new(commands_new(configure_capability, "enabled", + "Override capabilities", NULL, NULL, NULL), + NULL, " Set of capabilities separated by commas", NULL, + cmd_store_env_value, "capabilities"), + NEWLINE, "Override capabilities", NULL, cmd_capability, "configure"); + + /* Do not override */ + commands_new(commands_new(unconfigure_capability, "enabled", + "Do not override capabilities", NULL, NULL, NULL), + NEWLINE, "Do not override capabilities", NULL, cmd_capability, + "unconfigure"); +} + +/** + * Register `configure system` commands. + * + * Those are the commands to configure protocol-independant stuff. + */ +void +register_commands_configure_system(struct cmd_node *configure, + struct cmd_node *unconfigure) +{ + struct cmd_node *configure_system = commands_new(configure, "system", + "System configuration", cmd_check_no_env, NULL, "ports"); + struct cmd_node *unconfigure_system = commands_new(unconfigure, "system", + "System configuration", cmd_check_no_env, NULL, "ports"); + struct cmd_node *configure_interface = commands_new(configure_system, + "interface", "Interface related items", NULL, NULL, NULL); + struct cmd_node *unconfigure_interface = commands_new(unconfigure_system, + "interface", "Interface related items", NULL, NULL, NULL); + + commands_new(commands_new(commands_new(configure_system, "description", + "Override chassis description", NULL, NULL, NULL), + NULL, "Chassis description", NULL, cmd_store_env_value, + "description"), + NEWLINE, "Override chassis description", NULL, cmd_system_description, + "system"); + commands_new(commands_new(unconfigure_system, "description", + "Don't override chassis description", NULL, NULL, NULL), + NEWLINE, "Don't override chassis description", NULL, cmd_system_description, + "system"); + + commands_new(commands_new(commands_new(configure_system, "chassisid", + "Override chassis ID", NULL, NULL, NULL), + NULL, "Chassis ID", NULL, cmd_store_env_value, "description"), + NEWLINE, "Override chassis ID", NULL, cmd_system_chassisid, "system"); + commands_new(commands_new(unconfigure_system, "chassisid", + "Don't override chassis ID", NULL, NULL, NULL), + NEWLINE, "Don't override chassis ID", NULL, cmd_system_chassisid, "system"); + + commands_new(commands_new(commands_new(configure_system, "platform", + "Override platform description", NULL, NULL, + NULL), + NULL, "Platform description (CDP)", NULL, cmd_store_env_value, + "platform"), + NEWLINE, "Override platform description", NULL, cmd_system_description, + "platform"); + commands_new(commands_new(unconfigure_system, "platform", + "Don't override platform description", NULL, NULL, NULL), + NEWLINE, "Don't override platform description", NULL, + cmd_system_description, "platform"); + + commands_new(commands_new(commands_new(configure_system, "hostname", + "Override system name", NULL, NULL, NULL), + NULL, "System name", NULL, cmd_store_env_value, "hostname"), + NEWLINE, "Override system name", NULL, cmd_hostname, NULL); + commands_new(commands_new(unconfigure_system, "hostname", + "Don't override system name", NULL, NULL, NULL), + NEWLINE, "Don't override system name", NULL, cmd_hostname, NULL); + + commands_new(commands_new(commands_new(configure_system, "max-neighbors", + "Set maximum number of neighbors per port", + cmd_check_no_env, NULL, "ports"), + NULL, "Maximum number of neighbors", NULL, cmd_store_env_value, + "max-neighbors"), + NEWLINE, "Set maximum number of neighbors per port", NULL, cmd_maxneighs, + NULL); + + commands_new( + commands_new(commands_new(commands_new(commands_new(configure_system, "ip", + "IP related options", NULL, NULL, + NULL), + "management", "IP management related options", + NULL, NULL, NULL), + "pattern", "Set IP management pattern", NULL, NULL, NULL), + NULL, "IP management pattern (comma-separated list of wildcards)", NULL, + cmd_store_env_value, "management-pattern"), + NEWLINE, "Set IP management pattern", NULL, cmd_management, NULL); + commands_new( + commands_new(commands_new(commands_new(unconfigure_system, "ip", + "IP related options", NULL, NULL, NULL), + "management", "IP management related options", NULL, NULL, + NULL), + "pattern", "Delete any IP management pattern", NULL, NULL, NULL), + NEWLINE, "Delete any IP management pattern", NULL, cmd_management, NULL); + + commands_new(commands_new(commands_new(configure_interface, "pattern", + "Set active interface pattern", NULL, NULL, NULL), + NULL, "Interface pattern (comma-separated list of wildcards)", + NULL, cmd_store_env_value, "iface-pattern"), + NEWLINE, "Set active interface pattern", NULL, cmd_iface_pattern, NULL); + commands_new(commands_new(unconfigure_interface, "pattern", + "Delete any interface pattern", NULL, NULL, NULL), + NEWLINE, "Clear interface pattern", NULL, cmd_iface_pattern, NULL); + + commands_new( + commands_new(commands_new(configure_interface, "permanent", + "Set permanent interface pattern", NULL, NULL, NULL), + NULL, "Permanent interface pattern (comma-separated list of wildcards)", + NULL, cmd_store_env_value, "iface-pattern"), + NEWLINE, "Set permanent interface pattern", NULL, cmd_perm_iface_pattern, + NULL); + commands_new(commands_new(unconfigure_interface, "permanent", + "Clear permanent interface pattern", NULL, NULL, NULL), + NEWLINE, "Delete any interface pattern", NULL, cmd_perm_iface_pattern, + NULL); + + commands_new(commands_new(configure_interface, "description", + "Update interface descriptions with neighbor name", NULL, NULL, + NULL), + NEWLINE, "Update interface descriptions with neighbor name", NULL, + cmd_update_descriptions, "enable"); + commands_new(commands_new(unconfigure_interface, "description", + "Don't update interface descriptions with neighbor name", NULL, + NULL, NULL), + NEWLINE, "Don't update interface descriptions with neighbor name", NULL, + cmd_update_descriptions, NULL); + + commands_new(commands_new(configure_interface, "promiscuous", + "Enable promiscuous mode on managed interfaces", NULL, NULL, + NULL), + NEWLINE, "Enable promiscuous mode on managed interfaces", NULL, + cmd_iface_promisc, "enable"); + commands_new(commands_new(unconfigure_interface, "promiscuous", + "Don't enable promiscuous mode on managed interfaces", NULL, + NULL, NULL), + NEWLINE, "Don't enable promiscuous mode on managed interfaces", NULL, + cmd_iface_promisc, NULL); + + register_commands_capabilities(configure_system, unconfigure_system); + register_commands_srcmac_type(configure_system); +} diff --git a/src/client/conf.c b/src/client/conf.c new file mode 100644 index 0000000..7bbb1f9 --- /dev/null +++ b/src/client/conf.c @@ -0,0 +1,44 @@ +/* -*- 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 <unistd.h> +#include <string.h> + +#include "client.h" +#include "../log.h" + +/** + * Register `configure` and `no configure` commands. + */ +void +register_commands_configure(struct cmd_node *root) +{ + struct cmd_node *configure = + commands_new(root, "configure", "Change system settings", NULL, NULL, NULL); + struct cmd_node *unconfigure = commands_new(root, "unconfigure", + "Unconfigure system settings", NULL, NULL, NULL); + commands_privileged(commands_lock(configure)); + commands_privileged(commands_lock(unconfigure)); + cmd_restrict_ports(configure); + cmd_restrict_ports(unconfigure); + + register_commands_configure_system(configure, unconfigure); + register_commands_configure_lldp(configure, unconfigure); + register_commands_configure_med(configure, unconfigure); + register_commands_configure_inventory(configure, unconfigure); + register_commands_configure_dot3(configure); +} diff --git a/src/client/display.c b/src/client/display.c new file mode 100644 index 0000000..6e3ec78 --- /dev/null +++ b/src/client/display.c @@ -0,0 +1,1039 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <ctype.h> +#include <time.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <arpa/inet.h> +#include <string.h> + +#include "../log.h" +#include "client.h" + +static void +display_cap(struct writer *w, lldpctl_atom_t *chassis, u_int8_t bit, const char *symbol) +{ + if (lldpctl_atom_get_int(chassis, lldpctl_k_chassis_cap_available) & bit) { + tag_start(w, "capability", "Capability"); + tag_attr(w, "type", "", symbol); + tag_attr(w, "enabled", "", + (lldpctl_atom_get_int(chassis, lldpctl_k_chassis_cap_enabled) & + bit) ? + "on" : + "off"); + tag_end(w); + } +} + +static void +display_med_capability(struct writer *w, long int available, int cap, + const char *symbol) +{ + if (available & cap) { + tag_start(w, "capability", "Capability"); + tag_attr(w, "type", "", symbol); + tag_attr(w, "available", "", "yes"); + tag_end(w); + } +} + +static void +display_med(struct writer *w, lldpctl_atom_t *port, lldpctl_atom_t *chassis) +{ + lldpctl_atom_t *medpolicies, *medpolicy; + lldpctl_atom_t *medlocations, *medlocation; + lldpctl_atom_t *caelements, *caelement; + lldpctl_atom_t *medpower; + long int cap = lldpctl_atom_get_int(chassis, lldpctl_k_chassis_med_cap); + const char *type; + + if (lldpctl_atom_get_int(chassis, lldpctl_k_chassis_med_type) <= 0) return; + + tag_start(w, "lldp-med", "LLDP-MED"); + + tag_datatag(w, "device-type", "Device Type", + lldpctl_atom_get_str(chassis, lldpctl_k_chassis_med_type)); + + display_med_capability(w, cap, LLDP_MED_CAP_CAP, "Capabilities"); + display_med_capability(w, cap, LLDP_MED_CAP_POLICY, "Policy"); + display_med_capability(w, cap, LLDP_MED_CAP_LOCATION, "Location"); + display_med_capability(w, cap, LLDP_MED_CAP_MDI_PSE, "MDI/PSE"); + display_med_capability(w, cap, LLDP_MED_CAP_MDI_PD, "MDI/PD"); + display_med_capability(w, cap, LLDP_MED_CAP_IV, "Inventory"); + + /* LLDP MED policies */ + medpolicies = lldpctl_atom_get(port, lldpctl_k_port_med_policies); + lldpctl_atom_foreach(medpolicies, medpolicy) + { + if (lldpctl_atom_get_int(medpolicy, lldpctl_k_med_policy_type) <= 0) + continue; + + tag_start(w, "policy", "LLDP-MED Network Policy for"); + tag_attr(w, "apptype", "", + lldpctl_atom_get_str(medpolicy, lldpctl_k_med_policy_type)); + tag_attr(w, "defined", "Defined", + (lldpctl_atom_get_int(medpolicy, lldpctl_k_med_policy_unknown) > + 0) ? + "no" : + "yes"); + + if (lldpctl_atom_get_int(medpolicy, lldpctl_k_med_policy_tagged) > 0) { + int vid = + lldpctl_atom_get_int(medpolicy, lldpctl_k_med_policy_vid); + tag_start(w, "vlan", "VLAN"); + if (vid == 0) { + tag_attr(w, "vid", "", "priority"); + } else if (vid == 4095) { + tag_attr(w, "vid", "", "reserved"); + } else { + tag_attr(w, "vid", "", + lldpctl_atom_get_str(medpolicy, + lldpctl_k_med_policy_vid)); + } + tag_end(w); + } + + tag_datatag(w, "priority", "Priority", + lldpctl_atom_get_str(medpolicy, lldpctl_k_med_policy_priority)); + /* Also give a numeric value */ + int pcp = + lldpctl_atom_get_int(medpolicy, lldpctl_k_med_policy_priority); + char spcp[2] = { pcp + '0', '\0' }; + tag_datatag(w, "pcp", "PCP", spcp); + tag_datatag(w, "dscp", "DSCP Value", + lldpctl_atom_get_str(medpolicy, lldpctl_k_med_policy_dscp)); + + tag_end(w); + } + lldpctl_atom_dec_ref(medpolicies); + + /* LLDP MED locations */ + medlocations = lldpctl_atom_get(port, lldpctl_k_port_med_locations); + lldpctl_atom_foreach(medlocations, medlocation) + { + int format = + lldpctl_atom_get_int(medlocation, lldpctl_k_med_location_format); + if (format <= 0) continue; + tag_start(w, "location", "LLDP-MED Location Identification"); + tag_attr(w, "type", "Type", + lldpctl_atom_get_str(medlocation, lldpctl_k_med_location_format)); + + switch (format) { + case LLDP_MED_LOCFORMAT_COORD: + tag_attr(w, "geoid", "Geoid", + lldpctl_atom_get_str(medlocation, + lldpctl_k_med_location_geoid)); + tag_datatag(w, "lat", "Latitude", + lldpctl_atom_get_str(medlocation, + lldpctl_k_med_location_latitude)); + tag_datatag(w, "lon", "Longitude", + lldpctl_atom_get_str(medlocation, + lldpctl_k_med_location_longitude)); + tag_start(w, "altitude", "Altitude"); + tag_attr(w, "unit", "", + lldpctl_atom_get_str(medlocation, + lldpctl_k_med_location_altitude_unit)); + tag_data(w, + lldpctl_atom_get_str(medlocation, + lldpctl_k_med_location_altitude)); + tag_end(w); + break; + case LLDP_MED_LOCFORMAT_CIVIC: + tag_datatag(w, "country", "Country", + lldpctl_atom_get_str(medlocation, + lldpctl_k_med_location_country)); + caelements = lldpctl_atom_get(medlocation, + lldpctl_k_med_location_ca_elements); + lldpctl_atom_foreach(caelements, caelement) + { + type = lldpctl_atom_get_str(caelement, + lldpctl_k_med_civicaddress_type); + tag_datatag(w, totag(type), type, + lldpctl_atom_get_str(caelement, + lldpctl_k_med_civicaddress_value)); + } + lldpctl_atom_dec_ref(caelements); + break; + case LLDP_MED_LOCFORMAT_ELIN: + tag_datatag(w, "ecs", "ECS ELIN", + lldpctl_atom_get_str(medlocation, + lldpctl_k_med_location_elin)); + break; + } + + tag_end(w); + } + lldpctl_atom_dec_ref(medlocations); + + /* LLDP MED power */ + medpower = lldpctl_atom_get(port, lldpctl_k_port_med_power); + if (lldpctl_atom_get_int(medpower, lldpctl_k_med_power_type) > 0) { + tag_start(w, "poe", "Extended Power-over-Ethernet"); + + tag_datatag(w, "device-type", "Power Type & Source", + lldpctl_atom_get_str(medpower, lldpctl_k_med_power_type)); + tag_datatag(w, "source", "Power Source", + lldpctl_atom_get_str(medpower, lldpctl_k_med_power_source)); + tag_datatag(w, "priority", "Power priority", + lldpctl_atom_get_str(medpower, lldpctl_k_med_power_priority)); + tag_datatag(w, "power", "Power Value", + lldpctl_atom_get_str(medpower, lldpctl_k_med_power_val)); + + tag_end(w); + } + lldpctl_atom_dec_ref(medpower); + + /* LLDP MED inventory */ + do { + const char *hw = + lldpctl_atom_get_str(chassis, lldpctl_k_chassis_med_inventory_hw); + const char *sw = + lldpctl_atom_get_str(chassis, lldpctl_k_chassis_med_inventory_sw); + const char *fw = + lldpctl_atom_get_str(chassis, lldpctl_k_chassis_med_inventory_fw); + const char *sn = + lldpctl_atom_get_str(chassis, lldpctl_k_chassis_med_inventory_sn); + const char *manuf = lldpctl_atom_get_str(chassis, + lldpctl_k_chassis_med_inventory_manuf); + const char *model = lldpctl_atom_get_str(chassis, + lldpctl_k_chassis_med_inventory_model); + const char *asset = lldpctl_atom_get_str(chassis, + lldpctl_k_chassis_med_inventory_asset); + if (!(hw || sw || fw || sn || manuf || model || asset)) break; + + tag_start(w, "inventory", "Inventory"); + tag_datatag(w, "hardware", "Hardware Revision", hw); + tag_datatag(w, "software", "Software Revision", sw); + tag_datatag(w, "firmware", "Firmware Revision", fw); + tag_datatag(w, "serial", "Serial Number", sn); + tag_datatag(w, "manufacturer", "Manufacturer", manuf); + tag_datatag(w, "model", "Model", model); + tag_datatag(w, "asset", "Asset ID", asset); + tag_end(w); + } while (0); + + tag_end(w); +} + +static void +display_chassis(struct writer *w, lldpctl_atom_t *chassis, int details) +{ + lldpctl_atom_t *mgmts, *mgmt; + + tag_start(w, "chassis", "Chassis"); + tag_start(w, "id", "ChassisID"); + tag_attr(w, "type", "", + lldpctl_atom_get_str(chassis, lldpctl_k_chassis_id_subtype)); + tag_data(w, lldpctl_atom_get_str(chassis, lldpctl_k_chassis_id)); + tag_end(w); + tag_datatag(w, "name", "SysName", + lldpctl_atom_get_str(chassis, lldpctl_k_chassis_name)); + if (details == DISPLAY_BRIEF) { + tag_end(w); + return; + } + tag_datatag(w, "descr", "SysDescr", + lldpctl_atom_get_str(chassis, lldpctl_k_chassis_descr)); + + /* Management addresses */ + mgmts = lldpctl_atom_get(chassis, lldpctl_k_chassis_mgmt); + lldpctl_atom_foreach(mgmts, mgmt) + { + tag_datatag(w, "mgmt-ip", "MgmtIP", + lldpctl_atom_get_str(mgmt, lldpctl_k_mgmt_ip)); + if (lldpctl_atom_get_int(mgmt, lldpctl_k_mgmt_iface_index)) + tag_datatag(w, "mgmt-iface", "MgmtIface", + lldpctl_atom_get_str(mgmt, lldpctl_k_mgmt_iface_index)); + } + lldpctl_atom_dec_ref(mgmts); + + /* Capabilities */ + display_cap(w, chassis, LLDP_CAP_OTHER, "Other"); + display_cap(w, chassis, LLDP_CAP_REPEATER, "Repeater"); + display_cap(w, chassis, LLDP_CAP_BRIDGE, "Bridge"); + display_cap(w, chassis, LLDP_CAP_ROUTER, "Router"); + display_cap(w, chassis, LLDP_CAP_WLAN, "Wlan"); + display_cap(w, chassis, LLDP_CAP_TELEPHONE, "Tel"); + display_cap(w, chassis, LLDP_CAP_DOCSIS, "Docsis"); + display_cap(w, chassis, LLDP_CAP_STATION, "Station"); + + tag_end(w); +} + +static void +display_custom_tlvs(struct writer *w, lldpctl_atom_t *neighbor) +{ + lldpctl_atom_t *custom_list, *custom; + int have_custom_tlvs = 0; + size_t i, len, slen; + const uint8_t *oui, *oui_info; + char buf[1600]; /* should be enough for printing */ + + custom_list = lldpctl_atom_get(neighbor, lldpctl_k_custom_tlvs); + lldpctl_atom_foreach(custom_list, custom) + { + /* This tag gets added only once, if there are any custom TLVs */ + if (!have_custom_tlvs) { + tag_start(w, "unknown-tlvs", "Unknown TLVs"); + have_custom_tlvs++; + } + len = 0; + oui = lldpctl_atom_get_buffer(custom, lldpctl_k_custom_tlv_oui, &len); + len = 0; + oui_info = lldpctl_atom_get_buffer(custom, + lldpctl_k_custom_tlv_oui_info_string, &len); + if (!oui) continue; + tag_start(w, "unknown-tlv", "TLV"); + + /* Add OUI as attribute */ + snprintf(buf, sizeof(buf), "%02X,%02X,%02X", oui[0], oui[1], oui[2]); + tag_attr(w, "oui", "OUI", buf); + snprintf(buf, sizeof(buf), "%d", + (int)lldpctl_atom_get_int(custom, + lldpctl_k_custom_tlv_oui_subtype)); + tag_attr(w, "subtype", "SubType", buf); + snprintf(buf, sizeof(buf), "%d", (int)len); + tag_attr(w, "len", "Len", buf); + if (len > 0) { + for (slen = 0, i = 0; i < len; ++i) + slen += snprintf(buf + slen, + sizeof(buf) > slen ? sizeof(buf) - slen : 0, + "%02X%s", oui_info[i], ((i < len - 1) ? "," : "")); + tag_data(w, buf); + } + tag_end(w); + } + lldpctl_atom_dec_ref(custom_list); + + if (have_custom_tlvs) tag_end(w); +} + +static void +display_autoneg(struct writer *w, int advertised, int bithd, int bitfd, + const char *desc) +{ + if (!((advertised & bithd) || (advertised & bitfd))) return; + + tag_start(w, "advertised", "Adv"); + tag_attr(w, "type", "", desc); + if (bitfd != bithd) { + tag_attr(w, "hd", "HD", (advertised & bithd) ? "yes" : "no"); + tag_attr(w, "fd", "FD", (advertised & bitfd) ? "yes" : "no"); + } + tag_end(w); +} + +static void +display_port(struct writer *w, lldpctl_atom_t *port, int details) +{ + int vlan_tx_tag; + char buf[5]; /* should be enough for printing */ + + tag_start(w, "port", "Port"); + tag_start(w, "id", "PortID"); + tag_attr(w, "type", "", lldpctl_atom_get_str(port, lldpctl_k_port_id_subtype)); + tag_data(w, lldpctl_atom_get_str(port, lldpctl_k_port_id)); + tag_end(w); + + tag_datatag(w, "descr", "PortDescr", + lldpctl_atom_get_str(port, lldpctl_k_port_descr)); + + if ((vlan_tx_tag = lldpctl_atom_get_int(port, lldpctl_k_port_vlan_tx)) != -1) { + tag_start(w, "vlanTX", "VlanTX"); + snprintf(buf, sizeof(buf), "%d", vlan_tx_tag & 0xfff); + tag_attr(w, "id", "VID", buf); + snprintf(buf, sizeof(buf), "%d", (vlan_tx_tag >> 13) & 0x7); + tag_attr(w, "prio", "Prio", buf); + snprintf(buf, sizeof(buf), "%d", (vlan_tx_tag >> 12) & 0x1); + tag_attr(w, "dei", "DEI", buf); + tag_end(w); + } + + if (details && lldpctl_atom_get_int(port, lldpctl_k_port_ttl) > 0) + tag_datatag(w, "ttl", "TTL", + lldpctl_atom_get_str(port, lldpctl_k_port_ttl)); + + /* Dot3 */ + if (details == DISPLAY_DETAILS) { + tag_datatag(w, "mfs", "MFS", + lldpctl_atom_get_str(port, lldpctl_k_port_dot3_mfs)); + tag_datatag(w, "aggregation", "Port is aggregated. PortAggregID", + lldpctl_atom_get_str(port, lldpctl_k_port_dot3_aggregid)); + + long int autoneg_support, autoneg_enabled, autoneg_advertised, mautype; + autoneg_support = + lldpctl_atom_get_int(port, lldpctl_k_port_dot3_autoneg_support); + autoneg_enabled = + lldpctl_atom_get_int(port, lldpctl_k_port_dot3_autoneg_enabled); + autoneg_advertised = + lldpctl_atom_get_int(port, lldpctl_k_port_dot3_autoneg_advertised); + mautype = lldpctl_atom_get_int(port, lldpctl_k_port_dot3_mautype); + if (autoneg_support > 0 || autoneg_enabled > 0 || mautype > 0) { + tag_start(w, "auto-negotiation", "PMD autoneg"); + tag_attr(w, "supported", "supported", + (autoneg_support > 0) ? "yes" : "no"); + tag_attr(w, "enabled", "enabled", + (autoneg_enabled > 0) ? "yes" : "no"); + + if (autoneg_enabled > 0) { + if (autoneg_advertised < 0) autoneg_advertised = 0; + display_autoneg(w, autoneg_advertised, + LLDP_DOT3_LINK_AUTONEG_10BASE_T, + LLDP_DOT3_LINK_AUTONEG_10BASET_FD, "10Base-T"); + display_autoneg(w, autoneg_advertised, + LLDP_DOT3_LINK_AUTONEG_100BASE_TX, + LLDP_DOT3_LINK_AUTONEG_100BASE_TXFD, "100Base-TX"); + display_autoneg(w, autoneg_advertised, + LLDP_DOT3_LINK_AUTONEG_100BASE_T2, + LLDP_DOT3_LINK_AUTONEG_100BASE_T2FD, "100Base-T2"); + display_autoneg(w, autoneg_advertised, + LLDP_DOT3_LINK_AUTONEG_100BASE_T4, + LLDP_DOT3_LINK_AUTONEG_100BASE_T4, "100Base-T4"); + display_autoneg(w, autoneg_advertised, + LLDP_DOT3_LINK_AUTONEG_1000BASE_X, + LLDP_DOT3_LINK_AUTONEG_1000BASE_XFD, "1000Base-X"); + display_autoneg(w, autoneg_advertised, + LLDP_DOT3_LINK_AUTONEG_1000BASE_T, + LLDP_DOT3_LINK_AUTONEG_1000BASE_TFD, "1000Base-T"); + } + tag_datatag(w, "current", "MAU oper type", + lldpctl_atom_get_str(port, lldpctl_k_port_dot3_mautype)); + tag_end(w); + } + + lldpctl_atom_t *dot3_power = + lldpctl_atom_get(port, lldpctl_k_port_dot3_power); + int devicetype = + lldpctl_atom_get_int(dot3_power, lldpctl_k_dot3_power_devicetype); + if (devicetype > 0) { + tag_start(w, "power", "MDI Power"); + tag_attr(w, "supported", "Supported", + (lldpctl_atom_get_int(dot3_power, + lldpctl_k_dot3_power_supported) > 0) ? + "yes" : + "no"); + tag_attr(w, "enabled", "Enabled", + (lldpctl_atom_get_int(dot3_power, + lldpctl_k_dot3_power_enabled) > 0) ? + "yes" : + "no"); + tag_attr(w, "paircontrol", "Pair control", + (lldpctl_atom_get_int(dot3_power, + lldpctl_k_dot3_power_paircontrol) > 0) ? + "yes" : + "no"); + tag_start(w, "device-type", "Device type"); + tag_data(w, + lldpctl_atom_get_str(dot3_power, + lldpctl_k_dot3_power_devicetype)); + ; + tag_end(w); + tag_start(w, "pairs", "Power pairs"); + tag_data(w, + lldpctl_atom_get_str(dot3_power, + lldpctl_k_dot3_power_pairs)); + tag_end(w); + tag_start(w, "class", "Class"); + tag_data(w, + lldpctl_atom_get_str(dot3_power, + lldpctl_k_dot3_power_class)); + tag_end(w); + + /* 802.3at */ + if (lldpctl_atom_get_int(dot3_power, + lldpctl_k_dot3_power_type) > + LLDP_DOT3_POWER_8023AT_OFF) { + tag_start(w, "power-type", "Power type"); + tag_data(w, + lldpctl_atom_get_str(dot3_power, + lldpctl_k_dot3_power_type)); + tag_end(w); + + tag_start(w, "source", "Power Source"); + tag_data(w, + lldpctl_atom_get_str(dot3_power, + lldpctl_k_dot3_power_source)); + tag_end(w); + + tag_start(w, "priority", "Power Priority"); + tag_data(w, + lldpctl_atom_get_str(dot3_power, + lldpctl_k_dot3_power_priority)); + tag_end(w); + + tag_start(w, "requested", "PD requested power Value"); + tag_data(w, + lldpctl_atom_get_str(dot3_power, + lldpctl_k_dot3_power_requested)); + tag_end(w); + + tag_start(w, "allocated", "PSE allocated power Value"); + tag_data(w, + lldpctl_atom_get_str(dot3_power, + lldpctl_k_dot3_power_allocated)); + tag_end(w); + } + + /* 802.3bt */ + if (lldpctl_atom_get_int(dot3_power, + lldpctl_k_dot3_power_type_ext) > + LLDP_DOT3_POWER_8023BT_OFF) { + tag_start(w, "requested-a", "Requested mode A"); + tag_data(w, + lldpctl_atom_get_str(dot3_power, + lldpctl_k_dot3_power_requested_a)); + tag_end(w); + tag_start(w, "requested-b", "Requested mode B"); + tag_data(w, + lldpctl_atom_get_str(dot3_power, + lldpctl_k_dot3_power_requested_b)); + tag_end(w); + tag_start(w, "allocated-a", "Allocated alternative A"); + tag_data(w, + lldpctl_atom_get_str(dot3_power, + lldpctl_k_dot3_power_allocated_a)); + tag_end(w); + tag_start(w, "allocated-b", "Allocated alternative B"); + tag_data(w, + lldpctl_atom_get_str(dot3_power, + lldpctl_k_dot3_power_allocated_b)); + tag_end(w); + tag_start(w, "pse-powering-status", + "PSE powering status"); + tag_data(w, + lldpctl_atom_get_str(dot3_power, + lldpctl_k_dot3_power_pse_status)); + tag_end(w); + tag_start(w, "pd-powering-status", + "PD powering status"); + tag_data(w, + lldpctl_atom_get_str(dot3_power, + lldpctl_k_dot3_power_pd_status)); + tag_end(w); + tag_start(w, "power-pairs-ext", "Power pairs extra"); + tag_data(w, + lldpctl_atom_get_str(dot3_power, + lldpctl_k_dot3_power_pse_pairs_ext)); + tag_end(w); + tag_start(w, "power-class-ext-a", "Class extra A"); + tag_data(w, + lldpctl_atom_get_str(dot3_power, + lldpctl_k_dot3_power_class_a)); + tag_end(w); + tag_start(w, "power-class-ext-b", "Class extra B"); + tag_data(w, + lldpctl_atom_get_str(dot3_power, + lldpctl_k_dot3_power_class_b)); + tag_end(w); + tag_start(w, "power-class-ext", "Class extra"); + tag_data(w, + lldpctl_atom_get_str(dot3_power, + lldpctl_k_dot3_power_class_ext)); + tag_end(w); + tag_start(w, "power-type-ext", "Power type extra"); + tag_data(w, + lldpctl_atom_get_str(dot3_power, + lldpctl_k_dot3_power_type_ext)); + tag_end(w); + tag_start(w, "pd-load", "PD load"); + tag_data(w, + lldpctl_atom_get_str(dot3_power, + lldpctl_k_dot3_power_pd_load)); + tag_end(w); + tag_start(w, "max-power", + "PSE maximum available power"); + tag_data(w, + lldpctl_atom_get_str(dot3_power, + lldpctl_k_dot3_power_pse_max)); + tag_end(w); + } + + tag_end(w); + } + lldpctl_atom_dec_ref(dot3_power); + } + + tag_end(w); +} + +static void +display_local_ttl(struct writer *w, lldpctl_conn_t *conn, int details) +{ + char *ttl; + long int tx_hold; + long int tx_interval; + + lldpctl_atom_t *configuration; + configuration = lldpctl_get_configuration(conn); + if (!configuration) { + log_warnx("lldpctl", "not able to get configuration. %s", + lldpctl_last_strerror(conn)); + return; + } + + tx_hold = lldpctl_atom_get_int(configuration, lldpctl_k_config_tx_hold); + tx_interval = + lldpctl_atom_get_int(configuration, lldpctl_k_config_tx_interval_ms); + + tx_interval = (tx_interval * tx_hold + 999) / 1000; + + if (asprintf(&ttl, "%lu", tx_interval) == -1) { + log_warnx("lldpctl", "not enough memory to build TTL."); + goto end; + } + + tag_start(w, "ttl", "TTL"); + tag_attr(w, "ttl", "", ttl); + tag_end(w); + free(ttl); +end: + lldpctl_atom_dec_ref(configuration); +} + +static void +display_vlans(struct writer *w, lldpctl_atom_t *port) +{ + lldpctl_atom_t *vlans, *vlan; + int foundpvid = 0; + int pvid, vid; + + pvid = lldpctl_atom_get_int(port, lldpctl_k_port_vlan_pvid); + + vlans = lldpctl_atom_get(port, lldpctl_k_port_vlans); + lldpctl_atom_foreach(vlans, vlan) + { + vid = lldpctl_atom_get_int(vlan, lldpctl_k_vlan_id); + + tag_start(w, "vlan", "VLAN"); + tag_attr(w, "vlan-id", "", + lldpctl_atom_get_str(vlan, lldpctl_k_vlan_id)); + if (pvid == vid) { + tag_attr(w, "pvid", "pvid", "yes"); + foundpvid = 1; + } else { + tag_attr(w, "pvid", "pvid", "no"); + } + tag_data(w, lldpctl_atom_get_str(vlan, lldpctl_k_vlan_name)); + tag_end(w); + } + lldpctl_atom_dec_ref(vlans); + + if (!foundpvid && pvid > 0) { + tag_start(w, "vlan", "VLAN"); + tag_attr(w, "vlan-id", "", + lldpctl_atom_get_str(port, lldpctl_k_port_vlan_pvid)); + tag_attr(w, "pvid", "pvid", "yes"); + tag_end(w); + } +} + +static void +display_ppvids(struct writer *w, lldpctl_atom_t *port) +{ + lldpctl_atom_t *ppvids, *ppvid; + ppvids = lldpctl_atom_get(port, lldpctl_k_port_ppvids); + lldpctl_atom_foreach(ppvids, ppvid) + { + int status = lldpctl_atom_get_int(ppvid, lldpctl_k_ppvid_status); + tag_start(w, "ppvid", "PPVID"); + if (lldpctl_atom_get_int(ppvid, lldpctl_k_ppvid_id) > 0) + tag_attr(w, "value", "", + lldpctl_atom_get_str(ppvid, lldpctl_k_ppvid_id)); + tag_attr(w, "supported", "supported", + (status & LLDP_PPVID_CAP_SUPPORTED) ? "yes" : "no"); + tag_attr(w, "enabled", "enabled", + (status & LLDP_PPVID_CAP_ENABLED) ? "yes" : "no"); + tag_end(w); + } + lldpctl_atom_dec_ref(ppvids); +} + +static void +display_pids(struct writer *w, lldpctl_atom_t *port) +{ + lldpctl_atom_t *pids, *pid; + pids = lldpctl_atom_get(port, lldpctl_k_port_pis); + lldpctl_atom_foreach(pids, pid) + { + const char *pi = lldpctl_atom_get_str(pid, lldpctl_k_pi_id); + if (pi && strlen(pi) > 0) tag_datatag(w, "pi", "PI", pi); + } + lldpctl_atom_dec_ref(pids); +} + +static const char * +display_age(time_t lastchange) +{ + static char sage[30]; + int age = (int)(time(NULL) - lastchange); + if (snprintf(sage, sizeof(sage), "%d day%s, %02d:%02d:%02d", + age / (60 * 60 * 24), (age / (60 * 60 * 24) > 1) ? "s" : "", + (age / (60 * 60)) % 24, (age / 60) % 60, age % 60) >= sizeof(sage)) + return "too much"; + else + return sage; +} + +void +display_local_chassis(lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env, + int details) +{ + tag_start(w, "local-chassis", "Local chassis"); + + lldpctl_atom_t *chassis = lldpctl_get_local_chassis(conn); + display_chassis(w, chassis, details); + if (details == DISPLAY_DETAILS) { + display_med(w, NULL, chassis); + } + lldpctl_atom_dec_ref(chassis); + + tag_end(w); +} + +void +display_interface(lldpctl_conn_t *conn, struct writer *w, int hidden, + lldpctl_atom_t *iface, lldpctl_atom_t *port, int details, int protocol) +{ + int local = 0; + + if (!hidden && lldpctl_atom_get_int(port, lldpctl_k_port_hidden)) return; + + /* user might have specified protocol to filter on display */ + if ((protocol != LLDPD_MODE_MAX) && + (protocol != lldpctl_atom_get_int(port, lldpctl_k_port_protocol))) + return; + + /* Infer local / remote port from the port index (remote == 0) */ + local = lldpctl_atom_get_int(port, lldpctl_k_port_index) > 0 ? 1 : 0; + + lldpctl_atom_t *chassis = lldpctl_atom_get(port, lldpctl_k_port_chassis); + + tag_start(w, "interface", "Interface"); + tag_attr(w, "name", "", lldpctl_atom_get_str(iface, lldpctl_k_interface_name)); + if (!local) { + tag_attr(w, "via", "via", + lldpctl_atom_get_str(port, lldpctl_k_port_protocol)); + if (details > DISPLAY_BRIEF) { + tag_attr(w, "rid", "RID", + lldpctl_atom_get_str(chassis, lldpctl_k_chassis_index)); + tag_attr(w, "age", "Time", + display_age( + lldpctl_atom_get_int(port, lldpctl_k_port_age))); + } + } else { + tag_datatag(w, "status", "Administrative status", + lldpctl_atom_get_str(port, lldpctl_k_port_status)); + } + + display_chassis(w, chassis, details); + display_port(w, port, details); + if (details && local && conn) display_local_ttl(w, conn, details); + if (details == DISPLAY_DETAILS) { + display_vlans(w, port); + display_ppvids(w, port); + display_pids(w, port); + display_med(w, port, chassis); + } + + lldpctl_atom_dec_ref(chassis); + + display_custom_tlvs(w, port); + + tag_end(w); +} + +/** + * Display information about interfaces. + * + * @param conn Connection to lldpd. + * @param w Writer. + * @param env Environment from which we may find the list of ports. + * @param hidden Whatever to show hidden ports. + * @param details Level of details we need (DISPLAY_*). + */ +void +display_interfaces(lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env, + int hidden, int details) +{ + lldpctl_atom_t *iface; + int protocol = LLDPD_MODE_MAX; + const char *proto_str; + + /* user might have specified protocol to filter display results */ + proto_str = cmdenv_get(env, "protocol"); + + if (proto_str) { + log_debug("display", "filter protocol: %s ", proto_str); + + protocol = 0; + for (lldpctl_map_t *protocol_map = + lldpctl_key_get_map(lldpctl_k_port_protocol); + protocol_map->string; protocol_map++) { + if (!strcasecmp(proto_str, protocol_map->string)) { + protocol = protocol_map->value; + break; + } + } + } + + tag_start(w, "lldp", "LLDP neighbors"); + while ((iface = cmd_iterate_on_interfaces(conn, env))) { + lldpctl_atom_t *port; + lldpctl_atom_t *neighbors; + lldpctl_atom_t *neighbor; + port = lldpctl_get_port(iface); + neighbors = lldpctl_atom_get(port, lldpctl_k_port_neighbors); + lldpctl_atom_foreach(neighbors, neighbor) + { + display_interface(conn, w, hidden, iface, neighbor, details, + protocol); + } + lldpctl_atom_dec_ref(neighbors); + lldpctl_atom_dec_ref(port); + } + tag_end(w); +} + +/** + * Display information about local interfaces. + * + * @param conn Connection to lldpd. + * @param w Writer. + * @param hidden Whatever to show hidden ports. + * @param env Environment from which we may find the list of ports. + * @param details Level of details we need (DISPLAY_*). + */ +void +display_local_interfaces(lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env, + int hidden, int details) +{ + lldpctl_atom_t *iface; + int protocol = LLDPD_MODE_MAX; + + tag_start(w, "lldp", "LLDP interfaces"); + while ((iface = cmd_iterate_on_interfaces(conn, env))) { + lldpctl_atom_t *port; + port = lldpctl_get_port(iface); + display_interface(conn, w, hidden, iface, port, details, protocol); + lldpctl_atom_dec_ref(port); + } + tag_end(w); +} + +static void +display_stat(struct writer *w, const char *tag, const char *descr, + long unsigned int cnt) +{ + char buf[20] = {}; + + tag_start(w, tag, descr); + snprintf(buf, sizeof(buf), "%lu", cnt); + tag_attr(w, tag, "", buf); + tag_end(w); +} + +void +display_interface_stats(lldpctl_conn_t *conn, struct writer *w, lldpctl_atom_t *port) +{ + tag_start(w, "interface", "Interface"); + tag_attr(w, "name", "", lldpctl_atom_get_str(port, lldpctl_k_port_name)); + + display_stat(w, "tx", "Transmitted", + lldpctl_atom_get_int(port, lldpctl_k_tx_cnt)); + display_stat(w, "rx", "Received", lldpctl_atom_get_int(port, lldpctl_k_rx_cnt)); + + display_stat(w, "rx_discarded_cnt", "Discarded", + lldpctl_atom_get_int(port, lldpctl_k_rx_discarded_cnt)); + + display_stat(w, "rx_unrecognized_cnt", "Unrecognized", + lldpctl_atom_get_int(port, lldpctl_k_rx_unrecognized_cnt)); + + display_stat(w, "ageout_cnt", "Ageout", + lldpctl_atom_get_int(port, lldpctl_k_ageout_cnt)); + + display_stat(w, "insert_cnt", "Inserted", + lldpctl_atom_get_int(port, lldpctl_k_insert_cnt)); + + display_stat(w, "delete_cnt", "Deleted", + lldpctl_atom_get_int(port, lldpctl_k_delete_cnt)); + + tag_end(w); +} + +/** + * Display interface stats + * + * @param conn Connection to lldpd. + * @param w Writer. + * @param env Environment from which we may find the list of ports. + */ +void +display_interfaces_stats(lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env) +{ + lldpctl_atom_t *iface; + int summary = 0; + u_int64_t h_tx_cnt = 0; + u_int64_t h_rx_cnt = 0; + u_int64_t h_rx_discarded_cnt = 0; + u_int64_t h_rx_unrecognized_cnt = 0; + u_int64_t h_ageout_cnt = 0; + u_int64_t h_insert_cnt = 0; + u_int64_t h_delete_cnt = 0; + + if (cmdenv_get(env, "summary")) summary = 1; + + tag_start(w, "lldp", (summary ? "LLDP Global statistics" : "LLDP statistics")); + while ((iface = cmd_iterate_on_interfaces(conn, env))) { + lldpctl_atom_t *port; + port = lldpctl_get_port(iface); + if (!summary) + display_interface_stats(conn, w, port); + else { + h_tx_cnt += lldpctl_atom_get_int(port, lldpctl_k_tx_cnt); + h_rx_cnt += lldpctl_atom_get_int(port, lldpctl_k_rx_cnt); + h_rx_discarded_cnt += + lldpctl_atom_get_int(port, lldpctl_k_rx_discarded_cnt); + h_rx_unrecognized_cnt += + lldpctl_atom_get_int(port, lldpctl_k_rx_unrecognized_cnt); + h_ageout_cnt += + lldpctl_atom_get_int(port, lldpctl_k_ageout_cnt); + h_insert_cnt += + lldpctl_atom_get_int(port, lldpctl_k_insert_cnt); + h_delete_cnt += + lldpctl_atom_get_int(port, lldpctl_k_delete_cnt); + } + lldpctl_atom_dec_ref(port); + } + + if (summary) { + tag_start(w, "summary", "Summary of stats"); + display_stat(w, "tx", "Transmitted", h_tx_cnt); + display_stat(w, "rx", "Received", h_rx_cnt); + display_stat(w, "rx_discarded_cnt", "Discarded", h_rx_discarded_cnt); + + display_stat(w, "rx_unrecognized_cnt", "Unrecognized", + h_rx_unrecognized_cnt); + + display_stat(w, "ageout_cnt", "Ageout", h_ageout_cnt); + + display_stat(w, "insert_cnt", "Inserted", h_insert_cnt); + + display_stat(w, "delete_cnt", "Deleted", h_delete_cnt); + tag_end(w); + } + tag_end(w); +} + +static const char * +N(const char *str) +{ + if (str == NULL || strlen(str) == 0) return "(none)"; + return str; +} + +void +display_configuration(lldpctl_conn_t *conn, struct writer *w) +{ + lldpctl_atom_t *configuration; + + configuration = lldpctl_get_configuration(conn); + if (!configuration) { + log_warnx("lldpctl", "not able to get configuration. %s", + lldpctl_last_strerror(conn)); + return; + } + + tag_start(w, "configuration", "Global configuration"); + tag_start(w, "config", "Configuration"); + + tag_datatag(w, "tx-delay", "Transmit delay", + lldpctl_atom_get_str(configuration, lldpctl_k_config_tx_interval)); + tag_datatag(w, "tx-delay-ms", "Transmit delay in milliseconds", + lldpctl_atom_get_str(configuration, lldpctl_k_config_tx_interval_ms)); + tag_datatag(w, "tx-hold", "Transmit hold", + lldpctl_atom_get_str(configuration, lldpctl_k_config_tx_hold)); + tag_datatag(w, "max-neighbors", "Maximum number of neighbors", + lldpctl_atom_get_str(configuration, lldpctl_k_config_max_neighbors)); + tag_datatag(w, "rx-only", "Receive mode", + lldpctl_atom_get_int(configuration, lldpctl_k_config_receiveonly) ? "yes" : + "no"); + tag_datatag(w, "mgmt-pattern", "Pattern for management addresses", + N(lldpctl_atom_get_str(configuration, lldpctl_k_config_mgmt_pattern))); + tag_datatag(w, "iface-pattern", "Interface pattern", + N(lldpctl_atom_get_str(configuration, lldpctl_k_config_iface_pattern))); + tag_datatag(w, "perm-iface-pattern", "Permanent interface pattern", + N(lldpctl_atom_get_str(configuration, + lldpctl_k_config_perm_iface_pattern))); + tag_datatag(w, "cid-pattern", "Interface pattern for chassis ID", + N(lldpctl_atom_get_str(configuration, lldpctl_k_config_cid_pattern))); + tag_datatag(w, "cid-string", "Override chassis ID with", + N(lldpctl_atom_get_str(configuration, lldpctl_k_config_cid_string))); + tag_datatag(w, "description", "Override description with", + N(lldpctl_atom_get_str(configuration, lldpctl_k_config_description))); + tag_datatag(w, "platform", "Override platform with", + N(lldpctl_atom_get_str(configuration, lldpctl_k_config_platform))); + tag_datatag(w, "hostname", "Override system name with", + N(lldpctl_atom_get_str(configuration, lldpctl_k_config_hostname))); + tag_datatag(w, "capabilities", "Override system capabilities", + lldpctl_atom_get_int(configuration, lldpctl_k_config_chassis_cap_override) ? + "yes" : + "no"); + tag_datatag(w, "advertise-version", "Advertise version", + lldpctl_atom_get_int(configuration, lldpctl_k_config_advertise_version) ? + "yes" : + "no"); + tag_datatag(w, "ifdescr-update", "Update interface descriptions", + lldpctl_atom_get_int(configuration, lldpctl_k_config_ifdescr_update) ? + "yes" : + "no"); + tag_datatag(w, "iface-promisc", "Promiscuous mode on managed interfaces", + lldpctl_atom_get_int(configuration, lldpctl_k_config_iface_promisc) ? + "yes" : + "no"); + tag_datatag(w, "lldpmed-no-inventory", "Disable LLDP-MED inventory", + (lldpctl_atom_get_int(configuration, + lldpctl_k_config_lldpmed_noinventory) == 0) ? + "no" : + "yes"); + tag_datatag(w, "lldpmed-faststart", "LLDP-MED fast start mechanism", + (lldpctl_atom_get_int(configuration, lldpctl_k_config_fast_start_enabled) == + 0) ? + "no" : + "yes"); + tag_datatag(w, "lldpmed-faststart-interval", "LLDP-MED fast start interval", + N(lldpctl_atom_get_str(configuration, + lldpctl_k_config_fast_start_interval))); + tag_datatag(w, "bond-slave-src-mac-type", + "Source MAC for LLDP frames on bond slaves", + lldpctl_atom_get_str(configuration, + lldpctl_k_config_bond_slave_src_mac_type)); + tag_datatag(w, "lldp-portid-type", "Port ID TLV subtype for LLDP frames", + lldpctl_atom_get_str(configuration, lldpctl_k_config_lldp_portid_type)); + tag_datatag(w, "lldp-agent-type", "Agent type", + lldpctl_atom_get_str(configuration, lldpctl_k_config_lldp_agent_type)); + + tag_end(w); + tag_end(w); + + lldpctl_atom_dec_ref(configuration); +} diff --git a/src/client/json_writer.c b/src/client/json_writer.c new file mode 100644 index 0000000..dc6665a --- /dev/null +++ b/src/client/json_writer.c @@ -0,0 +1,374 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2017 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 + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/queue.h> + +#include "writer.h" +#include "../compat/compat.h" +#include "../log.h" + +enum tag { STRING, BOOL, ARRAY, OBJECT }; + +struct element { + struct element *parent; /* Parent (if any) */ + TAILQ_ENTRY(element) next; /* Sibling (if any) */ + char *key; /* Key if parent is an object */ + enum tag tag; /* Kind of element */ + union { + char *string; /* STRING */ + int boolean; /* BOOL */ + TAILQ_HEAD(, element) children; /* ARRAY or OBJECT */ + }; +}; + +struct json_writer_private { + FILE *fh; + int variant; + struct element *root; + struct element *current; /* should always be an object */ +}; + +/* Create a new element. If a parent is provided, it will also be attached to + * the parent. */ +static struct element * +json_element_new(struct element *parent, const char *key, enum tag tag) +{ + struct element *child = malloc(sizeof(*child)); + if (child == NULL) fatal(NULL, NULL); + child->parent = parent; + child->key = key ? strdup(key) : NULL; + child->tag = tag; + TAILQ_INIT(&child->children); + if (parent) TAILQ_INSERT_TAIL(&parent->children, child, next); + return child; +} + +/* Free the element content (but not the element itself) */ +static void +json_element_free(struct element *current) +{ + struct element *el, *el_next; + switch (current->tag) { + case STRING: + free(current->string); + break; + case BOOL: + break; + case ARRAY: + case OBJECT: + for (el = TAILQ_FIRST(¤t->children); el != NULL; el = el_next) { + el_next = TAILQ_NEXT(el, next); + json_element_free(el); + TAILQ_REMOVE(¤t->children, el, next); + if (current->tag == OBJECT) free(el->key); + free(el); + } + break; + } +} + +static void +json_free(struct json_writer_private *p) +{ + json_element_free(p->root); + free(p->root); +} + +static void +json_string_dump(FILE *fh, const char *s) +{ + fprintf(fh, "\""); + while (*s != '\0') { + unsigned int c = *s; + size_t len; + switch (c) { + case '"': + fprintf(fh, "\\\""); + s++; + break; + case '\\': + fprintf(fh, "\\\\"); + s++; + break; + case '\b': + fprintf(fh, "\\b"); + s++; + break; + case '\f': + fprintf(fh, "\\f"); + s++; + break; + case '\n': + fprintf(fh, "\\n"); + s++; + break; + case '\r': + fprintf(fh, "\\r"); + s++; + break; + case '\t': + fprintf(fh, "\\t"); + s++; + break; + default: + len = utf8_validate_cz(s); + if (len == 0) { + /* Not a valid UTF-8 char, use a + * replacement character */ + fprintf(fh, "\\uFFFD"); + s++; + } else if (c < 0x1f) { + /* 7-bit ASCII character */ + fprintf(fh, "\\u%04X", c); + s++; + } else { + /* UTF-8, write as is */ + while (len--) + fprintf(fh, "%c", *s++); + } + break; + } + } + fprintf(fh, "\""); +} + +/* Dump an element to the specified file handle. */ +static void +json_element_dump(FILE *fh, struct element *current, int indent) +{ + static const char pairs[2][2] = { "{}", "[]" }; + struct element *el; + switch (current->tag) { + case STRING: + json_string_dump(fh, current->string); + break; + case BOOL: + fprintf(fh, current->boolean ? "true" : "false"); + break; + case ARRAY: + case OBJECT: + fprintf(fh, "%c\n%*s", pairs[(current->tag == ARRAY)][0], indent + 2, + ""); + TAILQ_FOREACH (el, ¤t->children, next) { + if (current->tag == OBJECT) fprintf(fh, "\"%s\": ", el->key); + json_element_dump(fh, el, indent + 2); + if (TAILQ_NEXT(el, next)) fprintf(fh, ",\n%*s", indent + 2, ""); + } + fprintf(fh, "\n%*c", indent + 1, pairs[(current->tag == ARRAY)][1]); + break; + } +} + +static void +json_dump(struct json_writer_private *p) +{ + json_element_dump(p->fh, p->root, 0); + fprintf(p->fh, "\n"); +} + +static void +json_start(struct writer *w, const char *tag, const char *descr) +{ + struct json_writer_private *p = w->priv; + struct element *child; + struct element *new; + + /* Look for the tag in the current object. */ + TAILQ_FOREACH (child, &p->current->children, next) { + if (!strcmp(child->key, tag)) break; + } + if (!child) child = json_element_new(p->current, tag, ARRAY); + + /* Queue the new element. */ + new = json_element_new(child, NULL, OBJECT); + p->current = new; +} + +static void +json_attr(struct writer *w, const char *tag, const char *descr, const char *value) +{ + struct json_writer_private *p = w->priv; + struct element *new = json_element_new(p->current, tag, STRING); + if (value && (!strcmp(value, "yes") || !strcmp(value, "on"))) { + new->tag = BOOL; + new->boolean = 1; + } else if (value && (!strcmp(value, "no") || !strcmp(value, "off"))) { + new->tag = BOOL; + new->boolean = 0; + } else { + new->string = strdup(value ? value : ""); + } +} + +static void +json_data(struct writer *w, const char *data) +{ + struct json_writer_private *p = w->priv; + struct element *new = json_element_new(p->current, "value", STRING); + new->string = strdup(data ? data : ""); +} + +/* When an array has only one member, just remove the array. When an object has + * `value` as the only key, remove the object. Moreover, for an object, move the + * `name` key outside (inside a new object). This is a recursive function. We + * think the depth will be limited. Also, the provided element can be + * destroyed. Don't use it after this function! + * + * During the cleaning process, we will generate array of 1-size objects that + * could be turned into an object. We don't do that since people may rely on + * this format. Another problem is the format is changing depending on the + * number of interfaces or the number of neighbors. + */ +static void +json_element_cleanup(struct element *el) +{ +#ifndef ENABLE_JSON0 + struct element *child, *child_next; + + /* If array with one element, steal the content. Object with only one + * value whose key is "value", steal the content. */ + if ((el->tag == ARRAY || el->tag == OBJECT) && + (child = TAILQ_FIRST(&el->children)) && !TAILQ_NEXT(child, next) && + (el->tag == ARRAY || !strcmp(child->key, "value"))) { + free(child->key); + child->key = el->key; + child->parent = el->parent; + TAILQ_INSERT_BEFORE(el, child, next); + TAILQ_REMOVE(&el->parent->children, el, next); + free(el); + json_element_cleanup(child); + return; + } + + /* Other kind of arrays, recursively clean */ + if (el->tag == ARRAY) { + for (child = TAILQ_FIRST(&el->children); child; child = child_next) { + child_next = TAILQ_NEXT(child, next); + json_element_cleanup(child); + } + return; + } + + /* Other kind of objects, recursively clean, but if one key is "name", + * use it's value as a key for a new object stealing the existing + * one. */ + if (el->tag == OBJECT) { + struct element *name_child = NULL; + for (child = TAILQ_FIRST(&el->children); child; child = child_next) { + child_next = TAILQ_NEXT(child, next); + json_element_cleanup(child); + } + /* Redo a check to find if we have a "name" key now */ + for (child = TAILQ_FIRST(&el->children); child; child = child_next) { + child_next = TAILQ_NEXT(child, next); + if (!strcmp(child->key, "name") && child->tag == STRING) { + name_child = child; + } + } + if (name_child) { + struct element *new_el = json_element_new(NULL, NULL, OBJECT); + /* Replace el by new_el in parent object/array */ + new_el->parent = el->parent; + TAILQ_INSERT_BEFORE(el, new_el, next); + TAILQ_REMOVE(&el->parent->children, el, next); + new_el->key = el->key; + + /* new_el is parent of el */ + el->parent = new_el; + el->key = name_child->string; /* stolen */ + TAILQ_INSERT_TAIL(&new_el->children, el, next); + + /* Remove "name" child */ + TAILQ_REMOVE(&el->children, name_child, next); + free(name_child->key); + free(name_child); + } + return; + } +#endif +} + +static void +json_cleanup(struct json_writer_private *p) +{ + if (p->variant != 0) json_element_cleanup(p->root); +} + +static void +json_end(struct writer *w) +{ + struct json_writer_private *p = w->priv; + while ((p->current = p->current->parent) != NULL && p->current->tag != OBJECT) + ; + if (p->current == NULL) { + fatalx("lldpctl", "unbalanced tags"); + return; + } + + /* Display current object if last one */ + if (p->current == p->root) { + json_cleanup(p); + json_dump(p); + json_free(p); + fprintf(p->fh, "\n"); + fflush(p->fh); + p->root = p->current = json_element_new(NULL, NULL, OBJECT); + } +} + +static void +json_finish(struct writer *w) +{ + struct json_writer_private *p = w->priv; + if (p->current != p->root) log_warnx("lldpctl", "unbalanced tags"); + json_free(p); + free(p); + free(w); +} + +struct writer * +json_init(FILE *fh, int variant) +{ + struct writer *result; + struct json_writer_private *priv; + + priv = malloc(sizeof(*priv)); + if (priv == NULL) fatal(NULL, NULL); + + priv->fh = fh; + priv->root = priv->current = json_element_new(NULL, NULL, OBJECT); + priv->variant = variant; + + result = malloc(sizeof(*result)); + if (result == NULL) fatal(NULL, NULL); + + result->priv = priv; + result->start = json_start; + result->attr = json_attr; + result->data = json_data; + result->end = json_end; + result->finish = json_finish; + + return result; +} diff --git a/src/client/kv_writer.c b/src/client/kv_writer.c new file mode 100644 index 0000000..56c9a43 --- /dev/null +++ b/src/client/kv_writer.c @@ -0,0 +1,133 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2010 Andreas Hofmeister <andi@collax.com> + * 2010 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 "writer.h" +#include "../log.h" + +#define SEP '.' + +struct kv_writer_private { + FILE *fh; + char *prefix; +}; + +static void +kv_start(struct writer *w, const char *tag, const char *descr) +{ + struct kv_writer_private *p = w->priv; + char *newprefix; + int s; + + s = strlen(p->prefix) + 1 + strlen(tag); + if ((newprefix = malloc(s + 1)) == NULL) fatal(NULL, NULL); + if (strlen(p->prefix) > 0) + snprintf(newprefix, s + 1, "%s\1%s", p->prefix, tag); + else + snprintf(newprefix, s + 1, "%s", tag); + free(p->prefix); + p->prefix = newprefix; +} + +static void +kv_data(struct writer *w, const char *data) +{ + struct kv_writer_private *p = w->priv; + char *key = strdup(p->prefix); + char *value = data ? strdup(data) : NULL; + char *dot, *nl; + if (!key) fatal(NULL, NULL); + while ((dot = strchr(key, '\1')) != NULL) + *dot = SEP; + if (value) { + nl = value; + while ((nl = strchr(nl, '\n'))) { + *nl = ' '; + nl++; + } + } + fprintf(p->fh, "%s=%s\n", key, value ? value : ""); + free(key); + free(value); +} + +static void +kv_end(struct writer *w) +{ + struct kv_writer_private *p = w->priv; + char *dot; + + if ((dot = strrchr(p->prefix, '\1')) == NULL) { + p->prefix[0] = '\0'; + fflush(p->fh); + } else + *dot = '\0'; +} + +static void +kv_attr(struct writer *w, const char *tag, const char *descr, const char *value) +{ + if (!strcmp(tag, "name") || !strcmp(tag, "type")) { + /* Special case for name, replace the last prefix */ + kv_end(w); + kv_start(w, value, NULL); + } else { + kv_start(w, tag, NULL); + kv_data(w, value); + kv_end(w); + } +} + +static void +kv_finish(struct writer *w) +{ + struct kv_writer_private *p = w->priv; + + free(p->prefix); + free(w->priv); + w->priv = NULL; + + free(w); +} + +struct writer * +kv_init(FILE *fh) +{ + + struct writer *result; + struct kv_writer_private *priv; + + if ((priv = malloc(sizeof(*priv))) == NULL) fatal(NULL, NULL); + + priv->fh = fh; + priv->prefix = strdup(""); + + if ((result = malloc(sizeof(struct writer))) == NULL) fatal(NULL, NULL); + + result->priv = priv; + result->start = kv_start; + result->attr = kv_attr; + result->data = kv_data; + result->end = kv_end; + result->finish = kv_finish; + + return result; +} diff --git a/src/client/lldpcli.8.in b/src/client/lldpcli.8.in new file mode 100644 index 0000000..3f85da3 --- /dev/null +++ b/src/client/lldpcli.8.in @@ -0,0 +1,1151 @@ +.\" Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org> +.\" Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx> +.\" +.\" Permission to use, copy, modify, and/or distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.Dd $Mdocdate: July 16 2008 $ +.Dt LLDPCLI 8 +.Os +.Sh NAME +.Nm lldpcli , +.Nm lldpctl +.Nd control LLDP daemon +.Sh SYNOPSIS +.Nm +.Op Fl dv +.Op Fl u Ar socket +.Op Fl f Ar format +.Op Fl c Ar file +.Op Ar command ... +.Nm lldpctl +.Op Fl dv +.Op Fl u Ar socket +.Op Fl f Ar format +.Op Ar interfaces ... +.Sh DESCRIPTION +The +.Nm +program controls +.Xr lldpd 8 +daemon. +.Pp +When no command is specified, +.Nm +will start an interactive shell which can be used to input arbitrary +commands as if they were specified on the command line. This +interactive shell should provide completion and history support. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl d +Enable more debugging information. This flag can be repeated. +.It Fl u Ar socket +Specify the Unix-domain socket used for communication with +.Xr lldpd 8 . +.It Fl v +Show +.Nm +version. When repeated, show more build information. +.It Fl f Ar format +Choose the output format. Currently +.Em plain , +.Em xml , +.Em json , +.Em json0 +and +.Em keyvalue +formats are available. The default is +.Em plain . +.Em json0 +is more verbose than +.Em json +but the structure of the JSON object is not affected by the number of +interfaces or the number of neighbors. It is therefore easier to +parse. +.It Fl c Ar file +Read the given configuration file. This option may be repeated several +times. If a directory is provided, each file contained in it will be +read if ending by +.Li .conf . +Order is alphabetical. +.El +.Pp +When invoked as +.Nm lldpctl , +.Nm +will display detailed information about each neighbors on the +specified interfaces or on all interfaces if none are specified. This +command is mostly kept for backward compatibility with older versions. +.Pp +The following commands are supported by +.Nm . +When there is no ambiguity, the keywords can be abbreviated. For +example, +.Cd show neighbors ports eth0 summary +and +.Cd sh neigh p eth0 sum +are the same command. +.Bd -ragged -offset XX +.Cd exit +.Bd -ragged -offset XXXXXX +Quit +.Nm . +.Ed + +.Cd help Op ... +.Bd -ragged -offset XXXXXX +Display general help or help about a command. Also, you can get help +using the completion or by pressing the +.Ic ? +key. However, completion and inline help may be unavailable if +.Nm +was compiled without readline support but +.Cd help +command is always available. +.Ed + +.Cd show neighbors +.Op ports Ar ethX Op ,... +.Op Cd details | summary +.Op Cd hidden +.Bd -ragged -offset XXXXXX +Display information about each neighbor known by +.Xr lldpd 8 +daemon. With +.Cd summary , +only the name and the port description of each remote host will be +displayed. On the other hand, with +.Cd details , +all available information will be displayed, giving a verbose +view. When using +.Cd hidden , +also display remote ports hidden by the smart filter. When specifying +one or several ports, the information displayed is limited to the +given list of ports. +.Ed + +.Cd show interfaces +.Op ports Ar ethX Op ,... +.Op Cd details | summary +.Op Cd hidden +.Bd -ragged -offset XXXXXX +Display information about each local interface known by +.Xr lldpd 8 +daemon. With +.Cd summary , +only the name and the port description of each local interface will be +displayed. On the other hand, with +.Cd details , +all available information will be displayed, giving a verbose +view. When using +.Cd hidden , +also display local ports hidden by the smart filter. When specifying +one or several ports, the information displayed is limited to the +given list of ports. +.Ed + +.Cd show chassis +.Op Cd details | summary +.Bd -ragged -offset XXXXXX +Display information about local chassis. With +.Cd summary , +most details are skipped. On the other hand, with +.Cd details , +all available information will be displayed, giving a verbose +view. +.Ed + +.Cd watch +.Op ports Ar ethX Op ,... +.Op Cd details | summary +.Op Cd hidden +.Op Cd limit Ar X +.Bd -ragged -offset XXXXXX +Watch for any neighbor changes and report them as soon as they +happen. When specifying ports, the changes are only reported when +happening on the given ports. +.Cd hidden , summary +and +.Cd details +have the same meaning than previously described. If +.Cd limit +is specified, +.Nm +will exit after receiving the specified number of events. +.Ed + +.Cd show configuration +.Bd -ragged -offset XXXXXX +Display global configuration of +.Xr lldpd 8 +daemon. +.Ed + +.Cd show statistics +.Op ports Ar ethX Op ,... +.Op Cd summary +.Bd -ragged -offset XXXXXX +Report LLDP-related statistics, like the number of LLDPDU transmitted, +received, discarded or unrecognized. When specifying ports, only the +statistics from the given port are reported. With +.Cd summary +the statistics of each port is summed. +.Ed + +.Cd update +.Bd -ragged -offset XXXXXX +Make +.Xr lldpd 8 +update its information and send new LLDP PDU on all interfaces. +.Ed + +.Cd configure +.Cd system hostname Ar name +.Bd -ragged -offset XXXXXX +Override system hostname with the provided value. By default, the +system name is the FQDN found from the resolved value of +.Ic uname -n . +As a special value, use "." (dot) to use the short hostname instead of +a FQDN. +.Ed + +.Cd unconfigure +.Cd system hostname +.Bd -ragged -offset XXXXXX +Do not override system hostname and restore the use of the node name. +.Ed + +.Cd configure +.Cd system description Ar description +.Bd -ragged -offset XXXXXX +Override chassis description with the provided value instead of using +kernel name, node name, kernel version, build date and architecture. +.Ed + +.Cd unconfigure +.Cd system description +.Bd -ragged -offset XXXXXX +Do not override chassis description and use a value computed from node +name, kernel name, kernel version, build date and architecture instead. +.Ed + +.Cd configure +.Cd system chassisid Ar description +.Bd -ragged -offset XXXXXX +Override chassis ID with the provided value instead of using MAC address +from one interface or host name. +.Ed + +.Cd unconfigure +.Cd system chassisid +.Bd -ragged -offset XXXXXX +Do not override chassis ID and use a value computed from one of the interface +MAC address (or host name if none is found). +.Ed + +.Cd configure +.Cd system platform Ar description +.Bd -ragged -offset XXXXXX +Override platform description with the provided value instead of using +kernel name. This value is currently only used for CDP. +.Ed + +.Cd unconfigure +.Cd system platform +.Bd -ragged -offset XXXXXX +Do not override platform description and use the kernel name. This +option undoes the previous one. +.Ed + +.Cd configure +.Cd system capabilities enabled Ar capabilities +.Bd -ragged -offset XXXXXX +Override system capabilities with the provided value instead of using +kernel information. Several capabilities can be specified separated by +commas. Only available capabilities can be enabled. Valid capabilities are: +.Bl -tag -width "XXX." -compact -offset XX +.It Sy other +.It Sy repeater +.It Sy bridge +.It Sy wlan +.It Sy router +.It Sy telephone +.It Sy docsis +.It Sy station +.El +Here is an example of use: +.D1 configure system capabilities enabled bridge,router +.Pp +.Ed + +.Cd unconfigure +.Cd system capabilities enabled +.Bd -ragged -offset XXXXXX +Do not override capabilities and use the kernel information. This option +undoes the previous one. +.Ed + +.Cd configure +.Cd system interface pattern Ar pattern +.Bd -ragged -offset XXXXXX +Specify which interface to listen and send LLDPDU to. Without this +option, +.Nm lldpd +will use all available physical interfaces. This option can use +wildcards. Several interfaces can be specified separated by commas. +It is also possible to remove an interface by prefixing it with an +exclamation mark. It is possible to allow an interface by +prefixing it with two exclamation marks. An allowed interface beats +a forbidden interfaces which beats a simple matched interface. For +example, with +.Em eth*,!eth1,!eth2 +.Nm lldpd +will only use interfaces starting by +.Em eth +with the exception of +.Em eth1 +and +.Em eth2 . +While with +.Em *,!eth*,!!eth1 +.Nm +will use all interfaces, except interfaces starting by +.Em eth +with the exception of +.Em eth1 . +When an exact match is found, it will circumvent some tests. For example, if +.Em eth0.12 +is specified, it will be accepted even if this is a VLAN interface. +.Ed + +.Cd unconfigure +.Cd system interface pattern +.Bd -ragged -offset XXXXXX +Remove any previously configured interface pattern and use all +physical interfaces. This option undoes the previous one. +.Ed + +.Cd configure +.Cd system interface permanent Ar pattern +.Bd -ragged -offset XXXXXX +Specify interfaces whose configuration is permanently kept by +.Nm lldpd . +By default, +.Nm lldpd +disregard any data about interfaces when they are removed from the +system (statistics, custom configuration). This option allows one to +specify a pattern similar to the interface pattern. If an interface +disappear but matches the pattern, its data is kept in memory and +reused if the interface reappear at some point. For example, on Linux, +one could use the pattern +.Em eth*,eno*,enp* , +which should match fixed interfaces on most systems. +.Ed + +.Cd unconfigure +.Cd system interface permanent +.Bd -ragged -offset XXXXXX +Remove any previously configured permanent interface pattern. Any +interface removed from the system will be forgotten. This option +undoes the previous one. +.Ed + +.Cd configure +.Cd system interface description +.Bd -ragged -offset XXXXXX +Some OS allows the user to set a description for an interface. Setting +this option will enable +.Nm lldpd +to override this description with the name of the peer neighbor if one +is found or with the number of neighbors found. +.Ed + +.Cd unconfigure +.Cd system interface description +.Bd -ragged -offset XXXXXX +Do not update interface description with the name of the peer +neighbor. This option undoes the previous one. +.Ed + +.Cd configure +.Cd system interface promiscuous +.Bd -ragged -offset XXXXXX +Enable promiscuous mode on managed interfaces. +.Pp +When the interface is not managed any more (or when quitting +.Nm lldpd ) , +the interface is left in promiscuous mode as it is difficult to know +if someone else also put the interface in promiscuous mode. +.Pp +This option is known to be useful when the remote switch is a Cisco +2960 and the local network card features VLAN hardware +acceleration. In this case, you may not receive LLDP frames from the +remote switch. The most plausible explanation for this is the frame is +tagged with some VLAN (usually VLAN 1) and your network card is +filtering VLAN. This is not the only available solution to work-around +this problem. If you are concerned about performance issues, you can +also tag the VLAN 1 on each interface instead. +.Pp +Currently, this option has no effect on anything else than Linux. On +other OS, either disable VLAN acceleration, tag VLAN 1 or enable +promiscuous mode manually on the interface. +.Ed + +.Cd unconfigure +.Cd system interface promiscuous +.Bd -ragged -offset XXXXXX +Do not set promiscuous mode on managed interfaces. This option does +not disable promiscuous mode on interfaces already using this mode. +.Ed + +.Cd configure +.Cd system ip management pattern Ar pattern +.Bd -ragged -offset XXXXXX +Specify the management addresses of this system. As for interfaces +(described above), this option can use wildcards and inversions. +Without this option, the first IPv4 and the first IPv6 are used. If an +exact IP address is provided, it is used as a management address +without any check. If only negative patterns are provided, only one +IPv4 and one IPv6 addresses are chosen. Otherwise, many of them can be +selected. If you want to remove IPv6 addresses, you can use +.Em !*:* . +If an interface name is matched, the first IPv4 address and the first +IPv6 address associated to this interface will be chosen. +.Ed + +.Cd unconfigure +.Cd system ip management pattern +.Bd -ragged -offset XXXXXX +Unset any specific pattern for matching management addresses. This +option undoes the previous one. +.Ed + +.Cd configure +.Cd system bond-slave-src-mac-type Ar value +.Bd -ragged -offset XXXXXX +Set the type of src mac in lldp frames sent on bond slaves + +Valid types are: +.Bl -tag -width "XXX." -compact -offset XX +.It Sy real +Slave real mac +.It Sy zero +All zero mac +.It Sy fixed +An arbitrary fixed value +.Li ( 00:60:08:69:97:ef ) +.It Sy local +Real mac with locally administered bit set. If the real mac already +has the locally administered bit set, fallback to the fixed value. +.El +.Pp +Default value for +.Nm bond-slave-src-mac-type +is +.Nm local . +Some switches may complain when using one of the two other possible +values (either because +.Li 00:00:00:00:00:00 +is not a valid MAC or because the MAC address is flapping from one +port to another). Using +.Sy local +might lead to a duplicate MAC address on the network (but this is +quite unlikely). +.Ed + +.Cd configure +.Cd system max-neighbors Ar neighbors +.Bd -ragged -offset XXXXXX +Change the maximum number of neighbors accepted (for each protocol) on +an interface. This is a global value. The default is 32. This setting +only applies to future neighbors. +.Ed + +.Cd configure +.Cd lldp agent-type +.Cd nearest-bridge | nearest-non-tpmr-bridge | nearest-customer-bridge +.Bd -ragged -offset XXXXXX +The destination MAC address used to send LLDPDU allows an agent to +control the propagation of LLDPDUs. By default, the +.Li 01:80:c2:00:00:0e +MAC address is used and limit the propagation of the LLDPDU to the +nearest bridge +.Cd ( nearest-bridge ) . +To instruct +.Nm lldpd +to use the +.Li 01:80:c2:00:00:03 +MAC address instead, use +.Cd nearest-nontpmr-bridge +instead. +To use the +.Li 01:80:c2:00:00:00 +MAC address instead, use +.Cd nearest-customer-bridge +instead. +.Ed + +.Cd configure +.Cd lldp capabilities-advertisements +.Pp +.Cd unconfigure +.Cd lldp capabilities-advertisements +.Bd -ragged -offset XXXXXX +Enable or disable advertisements of the chassis capabilities TLV. +.Ed + +.Cd configure +.Cd lldp management-addresses-advertisements +.Pp +.Cd unconfigure +.Cd lldp management-addresses-advertisements +.Bd -ragged -offset XXXXXX +Enable or disable advertisements of the management address TLV. +.Ed + +.Cd configure +.Cd lldp portidsubtype +.Cd ifname | macaddress +.Pp +.Cd configure +.Op ports Ar ethX Op ,... +.Cd lldp portidsubtype +.Cd local Ar value +.Bd -ragged -offset XXXXXX +Force port ID subtype. By default, +.Nm lldpd +will use the MAC address as port identifier and the interface name as +port description, unless the interface has an alias. In this case, the +interface name will be used as port identifier and the description +will be the interface alias. With this command, you can force the port +identifier to be the interface name (with +.Cd ifname ) , +the MAC address (with +.Cd macaddress ) +or a local value (with +.Cd value ) . +In the latest case, the local value should be provided. +.Ed + +.Cd configure +.Op ports Ar ethX Op ,... +.Cd lldp portdescription +.Cd Ar description +.Bd -ragged -offset XXXXXX +Force port description to the provided string. +.Ed + +.Cd configure +.Cd lldp tx-interval Ar interval +.Bd -ragged -offset XXXXXX +Change transmit delay to the specified value in seconds. The transmit +delay is the delay between two transmissions of LLDP PDU. The default +value is 30 seconds. Note: +.Nm lldpd +also starts another system based refresh timer on each port to detect +changes such as a hostname. This is the value of the tx-interval +multiplied by 20. +.Pp +You can specify an +.Cd interval +value in milliseconds by appending a "ms" suffix to the figure (e.g. +"configure lldp tx-interval 1500ms" is 1.5s, not 1500s). In this case +the TTL for received and sent LLDP frames is rounded up to the next +second. Note: the effective interval can be limited by the operating +system capabilities and CPU speed. +.Ed + +.Cd configure +.Cd lldp tx-hold Ar hold +.Bd -ragged -offset XXXXXX +Change transmit hold value to the specified value. This value is used +to compute the TTL of transmitted packets which is the product of this +value and of the transmit delay. The default value is 4 and therefore +the default TTL is 120 seconds. +.Ed + +.Cd configure +.Op ports Ar ethX Op ,... +.Cd lldp +.Cd status Ar rx-and-tx | rx-only | tx-only | disabled +.Bd -ragged -offset XXXXXX +Configure the administrative status of the given port. By default, all +ports are configured to be in +.Ar rx-and-tx +mode. This means they can receive and transmit LLDP frames (as well as +other protocols if needed). In +.Ar rx-only +mode, they won't emit any frames and in +.Ar tx-only +mode, they won't receive any frames. In +.Ar disabled +mode, no frame will be sent and any incoming frame will be +discarded. This setting does not override the operational mode of the +main daemon. If it is configured in receive-only mode (with the +.Fl r +flag), setting any transmit mode won't have any effect. +.Ed + +.Cd configure +.Op ports Ar ethX Op ,... +.Cd lldp +.Cd vlan-tx Ar vlan_id +.Op Cd prio Ar priority Op Cd dei Ar dei +.Bd -ragged -offset XXXXXX +Configure the given port to send LLDP frames over a specified VLAN. With VLAN Identifier (VID) as +.Ar vlan_id , +Priority Code Point (PCP) as +.Ar priority , +and Drop Eligible Indicator (DEI) as +.Ar dei . +.Nm lldpd +accepts LLDP frames on all VLANs. +.Ed + +.Cd configure +.Op ports Ar ethX Op ,... +.Cd lldp custom-tlv +.Op Cd add | replace +.Cd oui Ar oui +.Cd subtype Ar subtype +.Op Cd oui-info Ar content +.Bd -ragged -offset XXXXXX +Emit a custom TLV for OUI +.Ar oui , +with subtype +.Ar subtype +and optionally with the bytes specified in +.Ar content . +Both +.Ar oui +and +.Ar content +should be a comma-separated list of bytes in hex format. +.Ar oui +must be exactly 3-byte long. +If +.Ar add +is specified then the TLV will be added. This is the default action. +If +.Ar replace +is specified then all TLVs with the same +.Ar oui +and +.Ar subtype +will be replaced. + +.Ed + +.Cd unconfigure +.Op ports Ar ethX Op ,... +.Cd lldp custom-tlv +.Op Cd oui Ar oui +.Op Cd subtype Ar subtype +.Bd -ragged -offset XXXXXX +When no oui is specified, remove all previously configured custom TLV. +When OUI +.Ar oui +and subtype +.Ar subtype +is specified, remove specific instances of custom TLV. +.Ed + +.Cd configure med fast-start +.Cd enable | tx-interval Ar interval +.Bd -ragged -offset XXXXXX +Configure LLDP-MED fast start mechanism. When a new LLDP-MED-enabled +neighbor is detected, fast start allows +.Nm lldpd +to shorten the interval between two LLDPDU. +.Cd enable +should enable LLDP-MED fast start while +.Cd tx-interval +specifies the interval between two LLDPDU in seconds. The default +interval is 1 second. Once 4 LLDPDU have been sent, the fast start +mechanism is disabled until a new neighbor is detected. +.Ed + +.Cd unconfigure med fast-start +.Bd -ragged -offset XXXXXX +Disable LLDP-MED fast start mechanism. +.Ed + +.Cd configure +.Op ports Ar ethX Op ,... +.Cd med location coordinate +.Cd latitude Ar latitude +.Cd longitude Ar longitude +.Cd altitude Ar altitude Ar unit +.Cd datum Ar datum +.Bd -ragged -offset XXXXXX +Advertise a coordinate based location on the given ports (or on all +ports if no port is specified). The format of +.Ar latitude +is a decimal floating point number followed either by +.Em N +or +.Em S . +The format of +.Ar longitude +is a decimal floating point number followed either by +.Em E +or +.Em W . +.Ar altitude +is a decimal floating point number followed either by +.Em m +when expressed in meters or +.Em f +when expressed in floors. A space is expected between the floating +point number and the unit. +.Ar datum +is one of those values: +.Bl -bullet -compact -offset XXXXXXXX +.It +WGS84 +.It +NAD83 +.It +NAD83/MLLW +.El +.Pp +A valid use of this command is: +.D1 configure ports eth0 med location coordinate latitude 48.85667N longitude 2.2014E altitude 117.47 m datum WGS84 +.Ed + +.Cd configure +.Op ports Ar ethX Op ,... +.Cd med location address +.Cd country Ar country +.Cd Op Ar type value Op ... +.Bd -ragged -offset XXXXXX +Advertise a civic address on the given ports (or on all ports if no +port is specified). +.Ar country +is the two-letter code representing the country. The remaining +arguments should be paired to form the address. The first member of +each pair indicates the type of the second member which is a free-form +text. Here is the list of valid types: +.Bl -bullet -compact -offset XXXXXXXX +.It +language +.It +country-subdivision +.It +county +.It +city +.It +city-division +.It +block +.It +street +.It +direction +.It +trailing-street-suffix +.It +street-suffix +.It +number +.It +number-suffix +.It +landmark +.It +additional +.It +name +.It +zip +.It +building +.It +unit +.It +floor +.It +room +.It +place-type +.It +script +.El +.Pp +A valid use of this command is: +.D1 configure ports eth1 med location address country US street Qo Commercial Road Qc city Qo Roseville Qc +.Ed + +.Cd configure +.Op ports Ar ethX Op ,... +.Cd med location elin +.Ar number +.Bd -ragged -offset XXXXXX +Advertise the availability of an ELIN number. This is used for setting +up emergency call. If the provided number is too small, it will be +padded with 0. Here is an example of use: +.D1 configure ports eth2 med location elin 911 +.Ed + +.Cd configure +.Op ports Ar ethX Op ,... +.Cd med policy +.Cd application Ar application +.Op Cd unknown +.Op Cd tagged +.Op Cd vlan Ar vlan +.Op Cd priority Ar priority +.Op Cd dscp Ar dscp +.Bd -ragged -offset XXXXXX +Advertise a specific network policy for the given ports (or for all +ports if no port was provided). Only the application type is +mandatory. +.Ar application +should be one of the following values: +.Bl -bullet -compact -offset XXXXXXXX +.It +voice +.It +voice-signaling +.It +guest-voice +.It +guest-voice-signaling +.It +softphone-voice +.It +video-conferencing +.It +streaming-video +.It +video-signaling +.El +.Pp +The +.Cd unknown +flag tells that the network policy for the specified application type +is required by the device but is currently unknown. This is used by +Endpoint Devices, not by Network Connectivity Devices. If not +specified, the network policy for the given application type is +defined. +.Pp +When a VLAN is specified with +.Ar vlan +tells which 802.1q VLAN ID has to be advertised for the network +policy. A valid value is between 1 and 4094. +.Cd tagged +tells the VLAN should be tagged for the specified application type. +.Pp +.Ar priority +allows one to specify IEEE 802.1d / IEEE 802.1p Layer 2 Priority, also +known as Class of Service (CoS), to be used for the specified +application type. This field is usually ignored if no VLAN is +specified. The names match 802.1D-2004 standard (table G-2). Some more +recent standards may use different labels. Only the numeric values +should be relied upon. The accepted labels are: +.Bl -tag -width "X." -compact -offset XXXX +.It Sy 1 +background +.It Sy 0 +best-effort +.It Sy 2 +excellent-effort +.It Sy 3 +critical-applications +.It Sy 4 +video +.It Sy 5 +voice +.It Sy 6 +internetwork-control +.It Sy 7 +network-control +.El +.Pp +.Ar dscp +represents the DSCP value to be advertised for the given network +policy. DiffServ/Differentiated Services Code Point (DSCP) value as +defined in IETF RFC 2474 for the specified application type. Value: 0 +(default per RFC 2475) through 63. Note: The class selector DSCP +values are backwards compatible for devices that only support the old +IP precedence Type of Service (ToS) format. (See the RFCs for what +these values mean) +.Pp +A valid use of this command is: +.D1 configure med policy application voice vlan 500 priority voice dscp 46 +.Ed + +.Cd configure +.Cd inventory hardware-revision Ar value +.Bd -ragged -offset XXXXXX +Override hardware-revision with the provided value. By default, the +hardware-revision is fetched from /sys/class/dmi +.Ed + +.Cd unconfigure +.Cd inventory hardware-revision +.Bd -ragged -offset XXXXXX +Do not override hardware-revision and restore the use of the /sys/class/dmi value. +.Ed + +.Cd configure +.Cd inventory software-revision Ar value +.Bd -ragged -offset XXXXXX +Override software-revision with the provided value. By default, the +software-revision is fetched from uname +.Ed + +.Cd unconfigure +.Cd inventory software-revision +.Bd -ragged -offset XXXXXX +Do not override software-revision and restore the use of the uname value. +.Ed + +.Cd configure +.Cd inventory firmware-revision Ar value +.Bd -ragged -offset XXXXXX +Override firmware-revision with the provided value. By default, the +firmware-revision is fetched from /sys/class/dmi +.Ed + +.Cd unconfigure +.Cd inventory firmware-revision +.Bd -ragged -offset XXXXXX +Do not override firmware-revision and restore the use of the /sys/class/dmi value. +.Ed + +.Cd configure +.Cd inventory serial-number Ar value +.Bd -ragged -offset XXXXXX +Override serial-number with the provided value. By default, the +serial-number is fetched from /sys/class/dmi +.Ed + +.Cd unconfigure +.Cd inventory serial-number +.Bd -ragged -offset XXXXXX +Do not override serial-number and restore the use of the /sys/class/dmi value. +.Ed + +.Cd configure +.Cd inventory manufacturer Ar value +.Bd -ragged -offset XXXXXX +Override manufacturer with the provided value. By default, the +manufacturer is fetched from /sys/class/dmi +.Ed + +.Cd unconfigure +.Cd inventory manufacturer +.Bd -ragged -offset XXXXXX +Do not override manufacturer and restore the use of the /sys/class/dmi value. +.Ed + +.Cd configure +.Cd inventory model Ar value +.Bd -ragged -offset XXXXXX +Override model with the provided value. By default, the +model is fetched from /sys/class/dmi +.Ed + +.Cd unconfigure +.Cd inventory model +.Bd -ragged -offset XXXXXX +Do not override model and restore the use of the /sys/class/dmi value. +.Ed + +.Cd configure +.Cd inventory asset Ar value +.Bd -ragged -offset XXXXXX +Override asset with the provided value. By default, the +asset is fetched from /sys/class/dmi +.Ed + +.Cd unconfigure +.Cd inventory asset +.Bd -ragged -offset XXXXXX +Do not override asset and restore the use of the /sys/class/dmi value. +.Ed + +.Cd configure +.Op ports Ar ethX Op ,... +.Cd med power pse | pd +.Cd source Ar source +.Cd priority Ar priority +.Cd value Ar value +.Bd -ragged -offset XXXXXX +Advertise the LLDP-MED POE-MDI TLV for the given ports or for all +interfaces if no port is provided. One can act as a PD (power +consumer) or a PSE (power provider). No check is done on the validity +of the parameters while LLDP-MED requires some restrictions: +.Bl -bullet +.It +PD shall never request more power than physical 802.3af class. +.It +PD shall never draw more than the maximum power advertised by PSE. +.It +PSE shall not reduce power allocated to PD when this power is in use. +.It +PSE may request reduced power using conservation mode +.It +Being PSE or PD is a global parameter, not a per-port parameter. +.Nm +does not enforce this: a port can be set as PD or PSE. LLDP-MED also +requires for a PSE to only have one power source (primary or +backup). Again, +.Nm +does not enforce this. Each port can have its own power source. The +same applies for PD and power priority. LLDP-MED MIB does not allow +this kind of representation. +.El +.Pp +Valid types are: +.Bl -tag -width "XXX." -compact -offset XX +.It Sy pse +Power Sourcing Entity (power provider) +.It Sy pd +Power Device (power consumer) +.El +.Pp +Valid sources are: +.Bl -tag -width "XXXXXXX" -compact -offset XX +.It Sy unknown +Unknown +.It Sy primary +For PSE, the power source is the primary power source. +.It Sy backup +For PSE, the power source is the backup power source or a power +conservation mode is asked (the PSE may be running on UPS for +example). +.It Sy pse +For PD, the power source is the PSE. +.It Sy local +For PD, the power source is a local source. +.It Sy both +For PD, the power source is both the PSE and a local source. +.El +.Pp +Valid priorities are: +.Bl -tag -width "XXXXXXXXX" -compact -offset XX +.It Sy unknown +Unknown priority +.It Sy critical +Critical +.It Sy high +High +.It Sy low +Low +.El +.Pp +.Ar value +should be the total power in milliwatts required by the PD device or +available by the PSE device. +.Pp +Here is an example of use: +.D1 configure med power pd source pse priority high value 5000 +.Ed + +.Cd configure +.Op ports Ar ethX Op ,... +.Cd dot3 power pse | pd +.Op Cd supported +.Op Cd enabled +.Op Cd paircontrol +.Cd powerpairs Ar powerpairs +.Op Cd class Ar class +.Op Cd type Ar type Cd source Ar source Cd priority Ar priority Cd requested Ar requested Cd allocated Ar allocated +.Bd -ragged -offset XXXXXX +Advertise Dot3 POE-MDI TLV for the given port or for all ports if none +was provided. One can act as a PD (power consumer) or a PSE (power +provider). This configuration is distinct of the configuration of the +transmission of the LLDP-MED POE-MDI TLV but the user should ensure +the coherency of those two configurations if they are used together. +.Pp +.Ar supported +means that MDI power is supported on the given port while +.Ar enabled +means that MDI power is enabled. +.Ar paircontrol +is used to indicate if pair selection can be controlled. Valid values +for +.Ar powerpairs +are: +.Bl -tag -width "XXXXXX" -compact -offset XX +.It Sy signal +The signal pairs only are in use. +.It Sy spare +The spare pairs only are in use. +.El +.Pp +When specified, +.Ar class +is a number between 0 and 4. +.Pp +The remaining parameters are in conformance with 802.3at and are optional. +.Ar type +should be either 1 or 2, indicating which if the device conforms to +802.3at type 1 or 802.3at type 2. Values of +.Ar source +and +.Ar priority +are the same as for LLDP-MED POE-MDI TLV. +.Ar requested +and +.Ar allocated +are expressed in milliwats. +.Pp +Here are two valid uses of this command: +.D1 configure ports eth3 dot3 power pse supported enabled paircontrol powerpairs spare class class-3 +.D1 configure dot3 power pd supported enabled powerpairs spare class class-3 type 1 source pse priority low requested 10000 allocated 15000 +.Ed + +.Cd pause +.Bd -ragged -offset XXXXXX +Pause +.Nm lldpd +operations. +.Nm lldpd +will not send any more frames or receive ones. This can be undone with +.Cd resume +command. This only works interactively as lldpd asks lldpcli to +unpause after reading the configuration file. +.Ed + +.Cd resume +.Bd -ragged -offset XXXXXX +Resume +.Nm lldpd +operations. +.Nm lldpd +will start to send and receive frames. This command is issued +internally after processing configuration but can be used at any time +if a manual +.Cd pause +command is issued. +.Ed + +.Ed +.Sh FILES +.Bl -tag -width "@LLDPD_CTL_SOCKET@XX" -compact +.It @LLDPD_CTL_SOCKET@ +Unix-domain socket used for communication with +.Xr lldpd 8 . +.El +.Sh SEE ALSO +.Xr lldpd 8 +.Sh AUTHORS +.An -nosplit +The +.Nm +program was written by +.An Vincent Bernat Aq bernat@luffy.cx . diff --git a/src/client/lldpcli.c b/src/client/lldpcli.c new file mode 100644 index 0000000..8999ea7 --- /dev/null +++ b/src/client/lldpcli.c @@ -0,0 +1,611 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <time.h> +#include <errno.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <arpa/inet.h> +#include <libgen.h> +#include <dirent.h> +#include <signal.h> +#include <sys/queue.h> + +#include "client.h" + +#ifdef HAVE___PROGNAME +extern const char *__progname; +#else +# define __progname "lldpcli" +#endif + +/* Global for completion */ +static struct cmd_node *root = NULL; +const char *ctlname = NULL; + +static int +is_lldpctl(const char *name) +{ + static int last_result = -1; + if (last_result == -1 && name) { + char *basec = strdup(name); + if (!basec) return 0; + char *bname = basename(basec); + last_result = (!strcmp(bname, "lldpctl")); + free(basec); + } + return (last_result == -1) ? 0 : last_result; +} + +static void +usage() +{ + fprintf(stderr, "Usage: %s [OPTIONS ...] [COMMAND ...]\n", __progname); + fprintf(stderr, "Version: %s\n", PACKAGE_STRING); + + fprintf(stderr, "\n"); + + fprintf(stderr, "-d Enable more debugging information.\n"); + fprintf(stderr, + "-u socket Specify the Unix-domain socket used for communication with lldpd(8).\n"); + fprintf(stderr, + "-f format Choose output format (plain, keyvalue, json, json0" +#if defined USE_XML + ", xml" +#endif + ").\n"); + if (!is_lldpctl(NULL)) + fprintf(stderr, "-c conf Read the provided configuration file.\n"); + + fprintf(stderr, "\n"); + + fprintf(stderr, "See manual page lldpcli(8) for more information\n"); + exit(1); +} + +static int +is_privileged() +{ + /* Check we can access the control socket with read/write + * privileges. The `access()` function uses the real UID and real GID, + * therefore we don't have to mangle with our identity. */ + return (ctlname && access(ctlname, R_OK | W_OK) == 0); +} + +static const char * +prompt() +{ +#define CESC "\033" + int privileged = is_privileged(); + if (isatty(STDIN_FILENO)) { + if (privileged) return "[lldpcli] # "; + return "[lldpcli] $ "; + } + return ""; +} + +static int must_exit = 0; +/** + * Exit the interpreter. + */ +static int +cmd_exit(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env, + const void *arg) +{ + log_info("lldpctl", "quit lldpcli"); + must_exit = 1; + return 1; +} + +/** + * Send an "update" request. + */ +static int +cmd_update(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env, + const void *arg) +{ + log_info("lldpctl", "ask for global update"); + + lldpctl_atom_t *config = lldpctl_get_configuration(conn); + if (config == NULL) { + log_warnx("lldpctl", "unable to get configuration from lldpd. %s", + lldpctl_last_strerror(conn)); + return 0; + } + if (lldpctl_atom_set_int(config, lldpctl_k_config_tx_interval, -1) == NULL) { + log_warnx("lldpctl", + "unable to ask lldpd for immediate retransmission. %s", + lldpctl_last_strerror(conn)); + lldpctl_atom_dec_ref(config); + return 0; + } + log_info("lldpctl", "immediate retransmission requested successfully"); + lldpctl_atom_dec_ref(config); + return 1; +} + +/** + * Pause or resume execution of lldpd. + * + * @param conn The connection to lldpd. + * @param pause 1 if we want to pause lldpd, 0 otherwise + * @return 1 on success, 0 on error + */ +static int +cmd_pause_resume(lldpctl_conn_t *conn, int pause) +{ + lldpctl_atom_t *config = lldpctl_get_configuration(conn); + if (config == NULL) { + log_warnx("lldpctl", "unable to get configuration from lldpd. %s", + lldpctl_last_strerror(conn)); + return 0; + } + if (lldpctl_atom_get_int(config, lldpctl_k_config_paused) == pause) { + log_debug("lldpctl", "lldpd is already %s", + pause ? "paused" : "resumed"); + lldpctl_atom_dec_ref(config); + return 1; + } + if (lldpctl_atom_set_int(config, lldpctl_k_config_paused, pause) == NULL) { + log_warnx("lldpctl", "unable to ask lldpd to %s operations. %s", + pause ? "pause" : "resume", lldpctl_last_strerror(conn)); + lldpctl_atom_dec_ref(config); + return 0; + } + log_info("lldpctl", "lldpd should %s operations", pause ? "pause" : "resume"); + lldpctl_atom_dec_ref(config); + return 1; +} +static int +cmd_pause(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env, + const void *arg) +{ + (void)w; + (void)env; + return cmd_pause_resume(conn, 1); +} +static int +cmd_resume(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env, + const void *arg) +{ + (void)w; + (void)env; + return cmd_pause_resume(conn, 0); +} + +#ifdef HAVE_LIBREADLINE +static int +_cmd_complete(int all) +{ + char **argv = NULL; + int argc = 0; + int rc = 1; + size_t len = strlen(rl_line_buffer); + char *line = malloc(len + 2); + if (!line) return -1; + strlcpy(line, rl_line_buffer, len + 2); + line[rl_point] = 2; /* empty character, will force a word */ + line[rl_point + 1] = 0; + + if (tokenize_line(line, &argc, &argv) != 0) goto end; + + char *compl = + commands_complete(root, argc, (const char **)argv, all, is_privileged()); + if (compl &&strlen(argv[argc - 1]) < strlen(compl )) { + if (rl_insert_text(compl +strlen(argv[argc - 1])) < 0) { + free(compl ); + goto end; + } + free(compl ); + rc = 0; + goto end; + } + /* No completion or several completion available. */ + free(compl ); + fprintf(stderr, "\n"); + rl_forced_update_display(); + rc = 0; +end: + free(line); + tokenize_free(argc, argv); + return rc; +} + +static int +cmd_complete(int count, int ch) +{ + return _cmd_complete(0); +} + +static int +cmd_help(int count, int ch) +{ + return _cmd_complete(1); +} +#else +static char * +readline(const char *p) +{ + static char line[2048]; + fprintf(stderr, "%s", p); + fflush(stderr); + if (fgets(line, sizeof(line) - 2, stdin) == NULL) return NULL; + return strdup(line); +} +#endif + +/** + * Execute a tokenized command and display its output. + * + * @param conn The connection to lldpd. + * @param fmt Output format. + * @param argc Number of arguments. + * @param argv Array of arguments. + * @return 0 if an error occurred, 1 otherwise + */ +static int +cmd_exec(lldpctl_conn_t *conn, const char *fmt, int argc, const char **argv) +{ + /* Init output formatter */ + struct writer *w; + + if (strcmp(fmt, "plain") == 0) + w = txt_init(stdout); + else if (strcmp(fmt, "keyvalue") == 0) + w = kv_init(stdout); + else if (strcmp(fmt, "json") == 0) + w = json_init(stdout, 1); + else if (strcmp(fmt, "json0") == 0) + w = json_init(stdout, 0); +#ifdef USE_XML + else if (strcmp(fmt, "xml") == 0) + w = xml_init(stdout); +#endif + else { + log_warnx("lldpctl", "unknown output format \"%s\"", fmt); + w = txt_init(stdout); + } + + /* Execute command */ + int rc = commands_execute(conn, w, root, argc, argv, is_privileged()); + if (rc != 0) { + log_info("lldpctl", "an error occurred while executing last command"); + w->finish(w); + return 0; + } + w->finish(w); + return 1; +} + +/** + * Execute a command line and display its output. + * + * @param conn The connection to lldpd. + * @param fmt Output format. + * @param line Line to execute. + * @return -1 if an error occurred, 0 if nothing was executed. 1 otherwise. + */ +static int +parse_and_exec(lldpctl_conn_t *conn, const char *fmt, const char *line) +{ + int cargc = 0; + char **cargv = NULL; + int n; + log_debug("lldpctl", "tokenize command line"); + n = tokenize_line(line, &cargc, &cargv); + switch (n) { + case -1: + log_warnx("lldpctl", "internal error while tokenizing"); + return -1; + case 1: + log_warnx("lldpctl", "unmatched quotes"); + return -1; + } + if (cargc != 0) n = cmd_exec(conn, fmt, cargc, (const char **)cargv); + tokenize_free(cargc, cargv); + return (cargc == 0) ? 0 : (n == 0) ? -1 : 1; +} + +static struct cmd_node * +register_commands() +{ + root = commands_root(); + register_commands_show(root); + register_commands_watch(root); + commands_new(commands_privileged(commands_new(root, "update", + "Update information and send LLDPU on all ports", NULL, NULL, + NULL)), + NEWLINE, "Update information and send LLDPU on all ports", NULL, cmd_update, + NULL); + register_commands_configure(root); + commands_hidden(commands_new(root, "complete", + "Get possible completions from a given command", NULL, + cmd_store_env_and_pop, "complete")); + commands_new(root, "help", "Get help on a possible command", NULL, + cmd_store_env_and_pop, "help"); + commands_new(commands_new(root, "pause", "Pause lldpd operations", NULL, NULL, + NULL), + NEWLINE, "Pause lldpd operations", NULL, cmd_pause, NULL); + commands_new(commands_new(root, "resume", "Resume lldpd operations", NULL, NULL, + NULL), + NEWLINE, "Resume lldpd operations", NULL, cmd_resume, NULL); + commands_new(commands_new(root, "exit", "Exit interpreter", NULL, NULL, NULL), + NEWLINE, "Exit interpreter", NULL, cmd_exit, NULL); + return root; +} + +struct input { + TAILQ_ENTRY(input) next; + char *name; +}; +TAILQ_HEAD(inputs, input); +static int +filter(const struct dirent *dir) +{ + if (strlen(dir->d_name) < 5) return 0; + if (strcmp(dir->d_name + strlen(dir->d_name) - 5, ".conf")) return 0; + return 1; +} + +/** + * Append a new input file/directory to the list of inputs. + * + * @param arg Directory or file name to add. + * @param inputs List of inputs + * @param acceptdir 1 if we accept a directory, 0 otherwise + */ +static void +input_append(const char *arg, struct inputs *inputs, int acceptdir, int warn) +{ + struct stat statbuf; + if (stat(arg, &statbuf) == -1) { + if (warn) { + log_warn("lldpctl", + "cannot find configuration file/directory %s", arg); + } else { + log_debug("lldpctl", + "cannot find configuration file/directory %s", arg); + } + return; + } + + if (!S_ISDIR(statbuf.st_mode)) { + struct input *input = malloc(sizeof(struct input)); + if (!input) { + log_warn("lldpctl", "not enough memory to process %s", arg); + return; + } + log_debug("lldpctl", "input: %s", arg); + input->name = strdup(arg); + TAILQ_INSERT_TAIL(inputs, input, next); + return; + } + if (!acceptdir) { + log_debug("lldpctl", "skip directory %s", arg); + return; + } + + struct dirent **namelist = NULL; + int n = scandir(arg, &namelist, filter, alphasort); + if (n < 0) { + log_warnx("lldpctl", "unable to read directory %s", arg); + return; + } + for (int i = 0; i < n; i++) { + char *fullname; + if (asprintf(&fullname, "%s/%s", arg, namelist[i]->d_name) != -1) { + input_append(fullname, inputs, 0, 1); + free(fullname); + } + free(namelist[i]); + } + free(namelist); +} + +int +main(int argc, char *argv[]) +{ + int ch, debug = 0, use_syslog = 0, rc = EXIT_FAILURE; + const char *fmt = "plain"; + lldpctl_conn_t *conn = NULL; + const char *options = is_lldpctl(argv[0]) ? "hdvf:u:" : "hdsvf:c:C:u:"; + lldpctl_atom_t *configuration; + + int gotinputs = 0, version = 0; + struct inputs inputs; + TAILQ_INIT(&inputs); + + ctlname = lldpctl_get_default_transport(); + + signal(SIGHUP, SIG_IGN); + + /* Get and parse command line options */ + optind = 1; + while ((ch = getopt(argc, argv, options)) != -1) { + switch (ch) { + case 'd': + if (use_syslog) + use_syslog = 0; + else + debug++; + break; + case 's': + if (debug == 0) + use_syslog = 1; + else + debug--; + break; + case 'h': + usage(); + break; + case 'u': + ctlname = optarg; + break; + case 'v': + version++; + break; + case 'f': + fmt = optarg; + break; + case 'C': + case 'c': + if (!gotinputs) { + log_init(use_syslog, debug, __progname); + lldpctl_log_level(debug + 1); + gotinputs = 1; + } + input_append(optarg, &inputs, 1, ch == 'c'); + break; + default: + usage(); + } + } + + if (version) { + version_display(stdout, "lldpcli", version > 1); + exit(0); + } + + if (!gotinputs) { + log_init(use_syslog, debug, __progname); + lldpctl_log_level(debug + 1); + } + + /* Disable SIGPIPE */ + signal(SIGPIPE, SIG_IGN); + + /* Register commands */ + root = register_commands(); + + /* Make a connection */ + log_debug("lldpctl", "connect to lldpd"); + conn = lldpctl_new_name(ctlname, NULL, NULL, NULL); + if (conn == NULL) goto end; + + /* Check we have a working connection */ + if ((configuration = lldpctl_get_configuration(conn)) == NULL) { + /* ctl.c already outputs an error */ + goto end; + } + lldpctl_atom_dec_ref(configuration); + + /* Process file inputs */ + while (gotinputs && !TAILQ_EMPTY(&inputs)) { + /* coverity[use_after_free] + TAILQ_REMOVE does the right thing */ + struct input *first = TAILQ_FIRST(&inputs); + log_debug("lldpctl", "process: %s", first->name); + FILE *file = fopen(first->name, "r"); + if (file) { + size_t n; + ssize_t len; + char *line; + while (line = NULL, len = 0, + (len = getline(&line, &n, file)) > 0) { + if (line[len - 1] == '\n') { + line[len - 1] = '\0'; + parse_and_exec(conn, fmt, line); + } + free(line); + } + free(line); + fclose(file); + } else { + log_warn("lldpctl", "unable to open %s", first->name); + } + TAILQ_REMOVE(&inputs, first, next); + free(first->name); + free(first); + } + + /* Process additional arguments. First if we are lldpctl (interfaces) */ + if (is_lldpctl(NULL)) { + char *line = NULL; + for (int i = optind; i < argc; i++) { + char *prev = line; + if (asprintf(&line, "%s%s%s", prev ? prev : "show neigh ports ", + argv[i], (i == argc - 1) ? " details" : ",") == -1) { + log_warnx("lldpctl", + "not enough memory to build list of interfaces"); + free(prev); + goto end; + } + free(prev); + } + if (line == NULL && (line = strdup("show neigh details")) == NULL) { + log_warnx("lldpctl", "not enough memory to build command line"); + goto end; + } + log_debug("lldpctl", "execute %s", line); + if (parse_and_exec(conn, fmt, line) != -1) rc = EXIT_SUCCESS; + free(line); + goto end; + } + + /* Then, if we are regular lldpcli (command line) */ + if (optind < argc) { + const char **cargv; + int cargc; + cargv = &((const char **)argv)[optind]; + cargc = argc - optind; + if (cmd_exec(conn, fmt, cargc, cargv) == 1) rc = EXIT_SUCCESS; + goto end; + } + + if (gotinputs) { + rc = EXIT_SUCCESS; + goto end; + } + + /* Interactive session */ +#ifdef HAVE_LIBREADLINE + rl_bind_key('?', cmd_help); + rl_bind_key('\t', cmd_complete); +#endif + char *line = NULL; + do { + if ((line = readline(prompt()))) { + int n = parse_and_exec(conn, fmt, line); + if (n != 0) { +#ifdef HAVE_READLINE_HISTORY + add_history(line); +#endif + } + free(line); + } + } while (!must_exit && line != NULL); + rc = EXIT_SUCCESS; + +end: + while (!TAILQ_EMPTY(&inputs)) { + /* coverity[use_after_free] + TAILQ_REMOVE does the right thing */ + struct input *first = TAILQ_FIRST(&inputs); + TAILQ_REMOVE(&inputs, first, next); + free(first->name); + free(first); + } + if (conn) lldpctl_release(conn); + if (root) commands_free(root); + return rc; +} diff --git a/src/client/lldpctl.8 b/src/client/lldpctl.8 new file mode 100644 index 0000000..68a27a1 --- /dev/null +++ b/src/client/lldpctl.8 @@ -0,0 +1 @@ +.so man8/lldpcli.8 diff --git a/src/client/misc.c b/src/client/misc.c new file mode 100644 index 0000000..827bd90 --- /dev/null +++ b/src/client/misc.c @@ -0,0 +1,73 @@ +/* -*- 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 "client.h" +#include <string.h> +#include <ctype.h> + +/** + * Check if an element is present in a comma-separated list. + * + * @param list Comma-separated list of elements. + * @param element Element we want to check for. + * @return 0 if the element was not found, 1 otherwise. + */ +int +contains(const char *list, const char *element) +{ + int len; + if (element == NULL || list == NULL) return 0; + while (list) { + len = strlen(element); + if (!strncmp(list, element, len) && + (list[len] == '\0' || list[len] == ',')) + return 1; + list = strchr(list, ','); + if (list) list++; + } + return 0; +} + +/** + * Transform a string to a tag. This puts the string into lower space and + * replace spaces with '-'. The result is statically allocated. + * + * @param value String to transform to a tag. + * @return The tagged value or the string "none" if @c value is @c NULL + */ +const char * +totag(const char *value) +{ + int i; + static char *result = NULL; + free(result); + result = NULL; + if (!value) return "none"; + result = calloc(1, strlen(value) + 1); + if (!result) return "none"; + for (i = 0; i < strlen(value); i++) { + switch (value[i]) { + case ' ': + result[i] = '-'; + break; + default: + result[i] = tolower((int)value[i]); + break; + } + } + return result; +} diff --git a/src/client/show.c b/src/client/show.c new file mode 100644 index 0000000..f13ae57 --- /dev/null +++ b/src/client/show.c @@ -0,0 +1,364 @@ +/* -*- 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 "client.h" +#include <string.h> +#include <limits.h> + +/** + * Show neighbors. + * + * The environment will contain the following keys: + * - C{ports} list of ports we want to restrict showing. + * - C{hidden} if we should show hidden ports. + * - C{summary} if we want to show only a summary + * - C{detailed} for a detailed overview + */ +static int +cmd_show_neighbors(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env, + const void *arg) +{ + log_debug("lldpctl", "show neighbors data (%s) %s hidden neighbors", + cmdenv_get(env, "summary") ? "summary" : + cmdenv_get(env, "detailed") ? "detailed" : + "normal", + cmdenv_get(env, "hidden") ? "with" : "without"); + if (cmdenv_get(env, "ports")) + log_debug("lldpctl", "restrict to the following ports: %s", + cmdenv_get(env, "ports")); + + display_interfaces(conn, w, env, !!cmdenv_get(env, "hidden"), + cmdenv_get(env, "summary") ? DISPLAY_BRIEF : + cmdenv_get(env, "detailed") ? DISPLAY_DETAILS : + DISPLAY_NORMAL); + + return 1; +} + +/** + * Show interfaces. + * + * The environment will contain the following keys: + * - C{ports} list of ports we want to restrict showing. + * - C{hidden} if we should show hidden ports. + * - C{summary} if we want to show only a summary + * - C{detailed} for a detailed overview + */ +static int +cmd_show_interfaces(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env, + const void *arg) +{ + log_debug("lldpctl", "show interfaces data (%s) %s hidden interfaces", + cmdenv_get(env, "summary") ? "summary" : + cmdenv_get(env, "detailed") ? "detailed" : + "normal", + cmdenv_get(env, "hidden") ? "with" : "without"); + if (cmdenv_get(env, "ports")) + log_debug("lldpctl", "restrict to the following ports: %s", + cmdenv_get(env, "ports")); + + display_local_interfaces(conn, w, env, !!cmdenv_get(env, "hidden"), + cmdenv_get(env, "summary") ? DISPLAY_BRIEF : + cmdenv_get(env, "detailed") ? DISPLAY_DETAILS : + DISPLAY_NORMAL); + + return 1; +} + +/** + * Show chassis. + * + * The environment will contain the following keys: + * - C{summary} if we want to show only a summary + * - C{detailed} for a detailed overview + */ +static int +cmd_show_chassis(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env, + const void *arg) +{ + log_debug("lldpctl", "show chassis data (%s)", + cmdenv_get(env, "summary") ? "summary" : + cmdenv_get(env, "detailed") ? "detailed" : + "normal"); + + display_local_chassis(conn, w, env, + cmdenv_get(env, "summary") ? DISPLAY_BRIEF : + cmdenv_get(env, "detailed") ? DISPLAY_DETAILS : + DISPLAY_NORMAL); + + return 1; +} + +/** + * Show stats. + * + * The environment will contain the following keys: + * - C{ports} list of ports we want to restrict showing. + * - C{summary} summary of stats + */ +static int +cmd_show_interface_stats(struct lldpctl_conn_t *conn, struct writer *w, + struct cmd_env *env, const void *arg) +{ + log_debug("lldpctl", "show stats data"); + if (cmdenv_get(env, "ports")) + log_debug("lldpctl", "restrict to the following ports: %s", + cmdenv_get(env, "ports")); + if (cmdenv_get(env, "summary")) + log_debug("lldpctl", "show summary of stats across ports"); + + display_interfaces_stats(conn, w, env); + + return 1; +} + +static int +cmd_check_no_detailed_nor_summary(struct cmd_env *env, const void *arg) +{ + if (cmdenv_get(env, "detailed")) return 0; + if (cmdenv_get(env, "summary")) return 0; + return 1; +} + +/** + * Show running configuration. + */ +static int +cmd_show_configuration(struct lldpctl_conn_t *conn, struct writer *w, + struct cmd_env *env, const void *arg) +{ + log_debug("lldpctl", "show running configuration"); + display_configuration(conn, w); + return 1; +} + +struct watcharg { + struct cmd_env *env; + struct writer *w; + size_t nb; +}; + +/** + * Callback for the next function to display a new neighbor. + */ +static void +watchcb(lldpctl_change_t type, lldpctl_atom_t *interface, lldpctl_atom_t *neighbor, + void *data) +{ + struct watcharg *wa = data; + struct cmd_env *env = wa->env; + struct writer *w = wa->w; + const char *interfaces = cmdenv_get(env, "ports"); + const char *proto_str; + int protocol = LLDPD_MODE_MAX; + + if (interfaces && + !contains(interfaces, + lldpctl_atom_get_str(interface, lldpctl_k_interface_name))) + return; + + /* user might have specified protocol to filter display results */ + proto_str = cmdenv_get(env, "protocol"); + + if (proto_str) { + log_debug("display", "filter protocol: %s ", proto_str); + + protocol = 0; /* unsupported */ + for (lldpctl_map_t *protocol_map = + lldpctl_key_get_map(lldpctl_k_port_protocol); + protocol_map->string; protocol_map++) { + if (!strcasecmp(proto_str, protocol_map->string)) { + protocol = protocol_map->value; + break; + } + } + } + + switch (type) { + case lldpctl_c_deleted: + tag_start(w, "lldp-deleted", "LLDP neighbor deleted"); + break; + case lldpctl_c_updated: + tag_start(w, "lldp-updated", "LLDP neighbor updated"); + break; + case lldpctl_c_added: + tag_start(w, "lldp-added", "LLDP neighbor added"); + break; + default: + return; + } + display_interface(NULL, w, 1, interface, neighbor, + cmdenv_get(env, "summary") ? DISPLAY_BRIEF : + cmdenv_get(env, "detailed") ? DISPLAY_DETAILS : + DISPLAY_NORMAL, + protocol); + tag_end(w); + wa->nb++; +} + +/** + * Watch for neighbor changes. + */ +static int +cmd_watch_neighbors(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env, + const void *arg) +{ + struct watcharg wa = { .env = env, .w = w, .nb = 0 }; + const char *limit_str = cmdenv_get(env, "limit"); + size_t limit = 0; + + if (limit_str) { + const char *errstr; + limit = strtonum(limit_str, 1, LLONG_MAX, &errstr); + if (errstr != NULL) { + log_warnx("lldpctl", "specified limit (%s) is %s and ignored", + limit_str, errstr); + } + } + + log_debug("lldpctl", "watch for neighbor changes"); + if (lldpctl_watch_callback2(conn, watchcb, &wa) < 0) { + log_warnx("lldpctl", "unable to watch for neighbors. %s", + lldpctl_last_strerror(conn)); + return 0; + } + while (1) { + if (lldpctl_watch(conn) < 0) { + log_warnx("lldpctl", "unable to watch for neighbors. %s", + lldpctl_last_strerror(conn)); + return 0; + } + if (limit > 0 && wa.nb >= limit) return 1; + } + return 0; +} + +/** + * Register common subcommands for `watch` and `show neighbors` and `show chassis' + */ +static void +register_common_commands(struct cmd_node *root, int neighbor) +{ + /* With more details */ + commands_new(root, "details", "With more details", + cmd_check_no_detailed_nor_summary, cmd_store_env_and_pop, "detailed"); + + /* With less details */ + commands_new(root, "summary", "With less details", + cmd_check_no_detailed_nor_summary, cmd_store_env_and_pop, "summary"); + + if (!neighbor) return; + + /* With hidden neighbors */ + commands_new(root, "hidden", "Include hidden neighbors", cmd_check_no_env, + cmd_store_env_and_pop, "hidden"); + + /* Some specific port */ + cmd_restrict_ports(root); + + /* Specific protocol */ + cmd_restrict_protocol(root); +} + +/** + * Register sub command summary + */ +static void +register_summary_command(struct cmd_node *root) +{ + commands_new(root, "summary", "With less details", + cmd_check_no_detailed_nor_summary, cmd_store_env_and_pop, "summary"); +} + +/** + * Register subcommands to `show` + * + * @param root Root node + */ +void +register_commands_show(struct cmd_node *root) +{ + struct cmd_node *show = commands_new(root, "show", + "Show running system information", NULL, NULL, NULL); + struct cmd_node *neighbors = + commands_new(show, "neighbors", "Show neighbors data", NULL, NULL, NULL); + + struct cmd_node *interfaces = + commands_new(show, "interfaces", "Show interfaces data", NULL, NULL, NULL); + + struct cmd_node *chassis = + commands_new(show, "chassis", "Show local chassis data", NULL, NULL, NULL); + + struct cmd_node *stats = + commands_new(show, "statistics", "Show statistics", NULL, NULL, NULL); + + /* Neighbors data */ + commands_new(neighbors, NEWLINE, "Show neighbors data", NULL, + cmd_show_neighbors, NULL); + + register_common_commands(neighbors, 1); + + /* Interfaces data */ + commands_new(interfaces, NEWLINE, "Show interfaces data", NULL, + cmd_show_interfaces, NULL); + + cmd_restrict_ports(interfaces); + register_common_commands(interfaces, 0); + + /* Chassis data */ + commands_new(chassis, NEWLINE, "Show local chassis data", NULL, + cmd_show_chassis, NULL); + + register_common_commands(chassis, 0); + + /* Stats data */ + commands_new(stats, NEWLINE, "Show stats data", NULL, cmd_show_interface_stats, + NULL); + + cmd_restrict_ports(stats); + register_summary_command(stats); + + /* Register "show configuration" and "show running-configuration" */ + commands_new(commands_new(show, "configuration", "Show running configuration", + NULL, NULL, NULL), + NEWLINE, "Show running configuration", NULL, cmd_show_configuration, NULL); + commands_new(commands_new(show, "running-configuration", + "Show running configuration", NULL, NULL, NULL), + NEWLINE, "Show running configuration", NULL, cmd_show_configuration, NULL); +} + +/** + * Register subcommands to `watch` + * + * @param root Root node + */ +void +register_commands_watch(struct cmd_node *root) +{ + struct cmd_node *watch = + commands_new(root, "watch", "Monitor neighbor changes", NULL, NULL, NULL); + + commands_new(watch, NEWLINE, "Monitor neighbors change", NULL, + cmd_watch_neighbors, NULL); + + commands_new(commands_new(watch, "limit", "Don't show more than X events", + cmd_check_no_env, NULL, "limit"), + NULL, "Stop after getting X events", NULL, cmd_store_env_value_and_pop2, + "limit"); + + register_common_commands(watch, 1); +} diff --git a/src/client/text_writer.c b/src/client/text_writer.c new file mode 100644 index 0000000..95990e7 --- /dev/null +++ b/src/client/text_writer.c @@ -0,0 +1,183 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2010 Andreas Hofmeister <andi@collax.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 <stdlib.h> +#include <stdarg.h> +#include <string.h> + +#include "writer.h" +#include "../log.h" + +static char sep[] = + "-------------------------------------------------------------------------------"; + +struct txt_writer_private { + FILE *fh; + int level; + int attrs; + int lastnl; +}; + +static void +txt_fprintf(struct txt_writer_private *p, const char *fmt, ...) +{ + va_list ap; + + // Don't add a newline if we already had one. + while (p->lastnl && fmt[0] == '\n') { + fmt += 1; + } + if (fmt[0] == '\0') return; + + va_start(ap, fmt); + vfprintf(p->fh, fmt, ap); + va_end(ap); + + p->lastnl = (strlen(fmt) > 0 && fmt[strlen(fmt) - 1] == '\n'); +} + +static void +txt_start(struct writer *w, const char *tag, const char *descr) +{ + struct txt_writer_private *p = w->priv; + int i = 0; + char buf[128]; + + if (p->level == 0) { + txt_fprintf(p, "%s\n", sep); + } else if (!p->lastnl) { + txt_fprintf(p, "\n"); + } + + for (i = 1; i < p->level; i++) { + txt_fprintf(p, " "); + } + + snprintf(buf, sizeof(buf), "%s:", descr); + txt_fprintf(p, "%-13s", buf); + + if (p->level == 0) txt_fprintf(p, "\n%s", sep); + + p->level++; + p->attrs = 0; +} + +static void +txt_attr(struct writer *w, const char *tag, const char *descr, const char *value) +{ + struct txt_writer_private *p = w->priv; + + if (descr == NULL || strlen(descr) == 0) { + txt_fprintf(p, "%s%s", (p->attrs > 0 ? ", " : " "), + value ? value : "(none)"); + } else { + txt_fprintf(p, "%s%s: %s", (p->attrs > 0 ? ", " : " "), descr, + value ? value : "(none)"); + } + + p->attrs++; +} + +static void +txt_data(struct writer *w, const char *data) +{ + struct txt_writer_private *p = w->priv; + char *nl, *begin; + char *v = begin = data ? strdup(data) : NULL; + + if (v == NULL) { + txt_fprintf(p, " %s", data ? data : "(none)"); + return; + } + + txt_fprintf(p, " "); + while ((nl = strchr(v, '\n')) != NULL) { + *nl = '\0'; + txt_fprintf(p, "%s\n", v); + v = nl + 1; + + /* Indent */ + int i; + for (i = 1; i < p->level - 1; i++) { + txt_fprintf(p, " "); + } + txt_fprintf(p, "%-14s", " "); + } + txt_fprintf(p, "%s", v); + free(begin); +} + +static void +txt_end(struct writer *w) +{ + struct txt_writer_private *p = w->priv; + p->level--; + + if (p->level == 1) { + txt_fprintf(p, "\n%s", sep); + fflush(p->fh); + } +} + +static void +txt_finish(struct writer *w) +{ + struct txt_writer_private *p = w->priv; + + txt_fprintf(p, "\n"); + + free(w->priv); + w->priv = NULL; + + free(w); +} + +struct writer * +txt_init(FILE *fh) +{ + + struct writer *result; + struct txt_writer_private *priv; + + priv = malloc(sizeof(*priv)); + if (!priv) { + fatalx("lldpctl", "out of memory"); + return NULL; + } + + priv->fh = fh; + priv->level = 0; + priv->attrs = 0; + priv->lastnl = 1; + + result = malloc(sizeof(struct writer)); + if (!result) { + fatalx("lldpctl", "out of memory"); + free(priv); + return NULL; + } + + result->priv = priv; + result->start = txt_start; + result->attr = txt_attr; + result->data = txt_data; + result->end = txt_end; + result->finish = txt_finish; + + return result; +} diff --git a/src/client/tokenizer.c b/src/client/tokenizer.c new file mode 100644 index 0000000..c4cdda6 --- /dev/null +++ b/src/client/tokenizer.c @@ -0,0 +1,116 @@ +/* -*- 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 "client.h" + +#include <string.h> +/** + * Tokenize the given line. We support quoted strings and escaped characters + * with backslash. + * + * @param line Line to tokenize. + * @param argv Will get an array of arguments tokenized. + * @param argc Will get the number of tokenized arguments. + * @return 0 on success, -1 on internal error, 1 on unmatched quotes + */ +int +tokenize_line(const char *line, int *argc, char ***argv) +{ + int iargc = 0; + char **iargv = NULL; + const char ifs[] = " \n\t"; + const char quotes[] = "'\""; + const char escapes[] = "\\"; + char empty = 2; /* Empty character, will be removed from output + * but will mark a word. */ + + /* Escape handle. Also escape quoted characters. */ + int escaped = 0; + int ipos = 0; + char quote = 0; + char input[2 * strlen(line) + 3]; /* 3 = 2 for '\n ' and 1 for \0 */ + memset(input, 0, 2 * strlen(line) + 3); + for (int pos = 0; line[pos]; pos++) { + if (line[pos] == '#' && !escaped && !quote) break; + if (!escaped && strchr(escapes, line[pos])) + escaped = 1; + else if (!escaped && strchr(quotes, line[pos]) && !quote) { + input[ipos++] = empty; + input[ipos++] = '!'; + quote = line[pos]; + } else if (!escaped && quote == line[pos]) + quote = 0; + else { + input[ipos++] = line[pos]; + input[ipos++] = (escaped || quote) ? '!' : ' '; + escaped = 0; + } + } + if (escaped || quote) return 1; + /* Trick to not have to handle \0 in a special way */ + input[ipos++] = ifs[0]; + input[ipos++] = ' '; + + /* Tokenize, we don't have to handle quotes anymore */ + int wbegin = -1; /* Offset of the beginning of the current word */ + +#define CURRENT (input[2 * pos]) +#define ESCAPED (input[2 * pos + 1] != ' ') + for (int pos = 0; CURRENT; pos++) { + if (wbegin == -1) { + if (!ESCAPED && strchr(ifs, CURRENT)) + /* IFS while not in a word, continue. */ + continue; + /* Start a word. */ + wbegin = pos; + continue; + } + if (ESCAPED || !strchr(ifs, CURRENT)) /* Regular character in a word. */ + continue; + + /* End of word. */ + char *word = calloc(1, pos - wbegin + 1); + if (!word) goto error; + int i, j; + for (i = wbegin, j = 0; i != pos; i++) + if (input[2 * i] != empty) word[j++] = input[2 * i]; + char **nargv = realloc(iargv, sizeof(char *) * (iargc + 1)); + if (!nargv) { + free(word); + goto error; + } + nargv[iargc++] = word; + iargv = nargv; + wbegin = -1; + } + + *argc = iargc; + *argv = iargv; + return 0; + +error: + tokenize_free(iargc, iargv); + return -1; +} + +void +tokenize_free(int argc, char **argv) +{ + while (argc) + free(argv[--argc]); + free(argv); +} diff --git a/src/client/utf8.c b/src/client/utf8.c new file mode 100644 index 0000000..4dbf178 --- /dev/null +++ b/src/client/utf8.c @@ -0,0 +1,90 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + Copyright (c) 2011 Joseph A. Adams (joeyadams3.14159@gmail.com) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include <stddef.h> +#include "writer.h" + +/* + * Validate a single UTF-8 character starting at @s. + * The string must be null-terminated. + * + * If it's valid, return its length (1 thru 4). + * If it's invalid or clipped, return 0. + * + * This function implements the syntax given in RFC3629, which is + * the same as that given in The Unicode Standard, Version 6.0. + * + * It has the following properties: + * + * * All codepoints U+0000..U+10FFFF may be encoded, + * except for U+D800..U+DFFF, which are reserved + * for UTF-16 surrogate pair encoding. + * * UTF-8 byte sequences longer than 4 bytes are not permitted, + * as they exceed the range of Unicode. + * * The sixty-six Unicode "non-characters" are permitted + * (namely, U+FDD0..U+FDEF, U+xxFFFE, and U+xxFFFF). + */ +size_t +utf8_validate_cz(const char *s) +{ + unsigned char c = *s++; + + if (c <= 0x7F) { /* 00..7F */ + return 1; + } else if (c <= 0xC1) { /* 80..C1 */ + /* Disallow overlong 2-byte sequence. */ + return 0; + } else if (c <= 0xDF) { /* C2..DF */ + /* Make sure subsequent byte is in the range 0x80..0xBF. */ + if (((unsigned char)*s++ & 0xC0) != 0x80) return 0; + + return 2; + } else if (c <= 0xEF) { /* E0..EF */ + /* Disallow overlong 3-byte sequence. */ + if (c == 0xE0 && (unsigned char)*s < 0xA0) return 0; + + /* Disallow U+D800..U+DFFF. */ + if (c == 0xED && (unsigned char)*s > 0x9F) return 0; + + /* Make sure subsequent bytes are in the range 0x80..0xBF. */ + if (((unsigned char)*s++ & 0xC0) != 0x80) return 0; + if (((unsigned char)*s++ & 0xC0) != 0x80) return 0; + + return 3; + } else if (c <= 0xF4) { /* F0..F4 */ + /* Disallow overlong 4-byte sequence. */ + if (c == 0xF0 && (unsigned char)*s < 0x90) return 0; + + /* Disallow codepoints beyond U+10FFFF. */ + if (c == 0xF4 && (unsigned char)*s > 0x8F) return 0; + + /* Make sure subsequent bytes are in the range 0x80..0xBF. */ + if (((unsigned char)*s++ & 0xC0) != 0x80) return 0; + if (((unsigned char)*s++ & 0xC0) != 0x80) return 0; + if (((unsigned char)*s++ & 0xC0) != 0x80) return 0; + + return 4; + } else { /* F5..FF */ + return 0; + } +} diff --git a/src/client/writer.h b/src/client/writer.h new file mode 100644 index 0000000..c66f699 --- /dev/null +++ b/src/client/writer.h @@ -0,0 +1,57 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2010 Andreas Hofmeister <andi@collax.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. + */ + +#ifndef _WRITER_H +#define _WRITER_H + +#include <stdio.h> +#include <config.h> + +struct writer { + void *priv; + void (*start)(struct writer *, const char *tag, const char *descr); + void (*attr)(struct writer *, const char *tag, const char *descr, + const char *value); + void (*data)(struct writer *, const char *data); + void (*end)(struct writer *); + void (*finish)(struct writer *); +}; + +#define tag_start(w, ...) w->start(w, ##__VA_ARGS__) +#define tag_attr(w, ...) w->attr(w, ##__VA_ARGS__) +#define tag_data(w, ...) w->data(w, ##__VA_ARGS__) +#define tag_end(w, ...) w->end(w, ##__VA_ARGS__) +#define tag_datatag(w, t, d, v) \ + do { \ + if ((v) == NULL) break; \ + w->start(w, t, d); \ + w->data(w, v); \ + w->end(w); \ + } while (0); + +extern struct writer *txt_init(FILE *); +extern struct writer *kv_init(FILE *); +extern struct writer *json_init(FILE *, int); + +#ifdef USE_XML +extern struct writer *xml_init(FILE *); +#endif + +/* utf8.c */ +size_t utf8_validate_cz(const char *s); + +#endif /* _WRITER_H */ diff --git a/src/client/xml_writer.c b/src/client/xml_writer.c new file mode 100644 index 0000000..0c5efb5 --- /dev/null +++ b/src/client/xml_writer.c @@ -0,0 +1,158 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2010 Andreas Hofmeister <andi@collax.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 <stdlib.h> +#include <string.h> + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wdocumentation" +#endif +#include <libxml/encoding.h> +#include <libxml/xmlwriter.h> +#if defined(__clang__) +# pragma clang diagnostic pop +#endif + +#include "writer.h" +#include "../log.h" + +struct xml_writer_private { + FILE *fh; + ssize_t depth; + xmlTextWriterPtr xw; + xmlDocPtr doc; +}; + +static void +xml_new_writer(struct xml_writer_private *priv) +{ + priv->xw = xmlNewTextWriterDoc(&(priv->doc), 0); + if (!priv->xw) fatalx("lldpctl", "cannot create xml writer"); + + xmlTextWriterSetIndent(priv->xw, 4); + + if (xmlTextWriterStartDocument(priv->xw, NULL, "UTF-8", NULL) < 0) + fatalx("lldpctl", "cannot start xml document"); +} + +static void +xml_start(struct writer *w, const char *tag, const char *descr) +{ + struct xml_writer_private *p = w->priv; + + if (p->depth == 0) xml_new_writer(p); + + if (xmlTextWriterStartElement(p->xw, BAD_CAST tag) < 0) + log_warnx("lldpctl", "cannot start '%s' element", tag); + + if (descr && (strlen(descr) > 0)) { + if (xmlTextWriterWriteFormatAttribute(p->xw, BAD_CAST "label", "%s", + descr) < 0) + log_warnx("lldpctl", + "cannot add attribute 'label' to element %s", tag); + } + + p->depth++; +} + +static void +xml_attr(struct writer *w, const char *tag, const char *descr, const char *value) +{ + struct xml_writer_private *p = w->priv; + + if (xmlTextWriterWriteFormatAttribute(p->xw, BAD_CAST tag, "%s", + value ? value : "") < 0) + log_warnx("lldpctl", "cannot add attribute %s with value %s", tag, + value ? value : "(none)"); +} + +static void +xml_data(struct writer *w, const char *data) +{ + struct xml_writer_private *p = w->priv; + if (xmlTextWriterWriteString(p->xw, BAD_CAST(data ? data : "")) < 0) + log_warnx("lldpctl", "cannot add '%s' as data to element", + data ? data : "(none)"); +} + +static void +xml_end(struct writer *w) +{ + struct xml_writer_private *p = w->priv; + + if (xmlTextWriterEndElement(p->xw) < 0) + log_warnx("lldpctl", "cannot end element"); + + if (--p->depth == 0) { + int failed = 0; + + if (xmlTextWriterEndDocument(p->xw) < 0) { + log_warnx("lldpctl", "cannot finish document"); + failed = 1; + } + + xmlFreeTextWriter(p->xw); + if (!failed) { + xmlDocDump(p->fh, p->doc); + fflush(p->fh); + } + xmlFreeDoc(p->doc); + } +} + +static void +xml_finish(struct writer *w) +{ + struct xml_writer_private *p = w->priv; + if (p->depth != 0) { + log_warnx("lldpctl", "unbalanced tags"); + /* memory leak... */ + } + + free(p); + free(w); +} + +struct writer * +xml_init(FILE *fh) +{ + + struct writer *result; + struct xml_writer_private *priv; + + priv = malloc(sizeof(*priv)); + if (!priv) { + fatalx("lldpctl", "out of memory"); + return NULL; + } + priv->fh = fh; + priv->depth = 0; + + result = malloc(sizeof(struct writer)); + if (!result) fatalx("lldpctl", "out of memory"); + + result->priv = priv; + result->start = xml_start; + result->attr = xml_attr; + result->data = xml_data; + result->end = xml_end; + result->finish = xml_finish; + + return result; +} diff --git a/src/compat/Makefile.am b/src/compat/Makefile.am new file mode 100644 index 0000000..3717edd --- /dev/null +++ b/src/compat/Makefile.am @@ -0,0 +1,8 @@ +AM_CFLAGS = $(LLDP_CFLAGS) +AM_CPPFLAGS = $(LLDP_CPPFLAGS) +AM_LDFLAGS = $(LLDP_LDFLAGS) + +noinst_LTLIBRARIES = libcompat.la + +libcompat_la_SOURCES = compat.h empty.c +libcompat_la_LIBADD = @LTLIBOBJS@ diff --git a/src/compat/Makefile.in b/src/compat/Makefile.in new file mode 100644 index 0000000..55187e0 --- /dev/null +++ b/src/compat/Makefile.in @@ -0,0 +1,725 @@ +# 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@ +subdir = src/compat +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 $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) +libcompat_la_DEPENDENCIES = @LTLIBOBJS@ +am_libcompat_la_OBJECTS = empty.lo +libcompat_la_OBJECTS = $(am_libcompat_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_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)/asprintf.Plo $(DEPDIR)/daemon.Plo \ + $(DEPDIR)/getline.Plo $(DEPDIR)/malloc.Plo \ + $(DEPDIR)/realloc.Plo $(DEPDIR)/setproctitle.Plo \ + $(DEPDIR)/strlcpy.Plo $(DEPDIR)/strndup.Plo \ + $(DEPDIR)/strnlen.Plo $(DEPDIR)/strtonum.Plo \ + $(DEPDIR)/vsyslog.Plo ./$(DEPDIR)/empty.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 = $(libcompat_la_SOURCES) +DIST_SOURCES = $(libcompat_la_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +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 \ + asprintf.c daemon.c getline.c malloc.c realloc.c \ + setproctitle.c strlcpy.c strndup.c strnlen.c strtonum.c \ + vsyslog.c +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 = $(LLDP_CFLAGS) +AM_CPPFLAGS = $(LLDP_CPPFLAGS) +AM_LDFLAGS = $(LLDP_LDFLAGS) +noinst_LTLIBRARIES = libcompat.la +libcompat_la_SOURCES = compat.h empty.c +libcompat_la_LIBADD = @LTLIBOBJS@ +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.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/compat/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/compat/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_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): + +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}; \ + } + +libcompat.la: $(libcompat_la_OBJECTS) $(libcompat_la_DEPENDENCIES) $(EXTRA_libcompat_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libcompat_la_OBJECTS) $(libcompat_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/asprintf.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/daemon.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/getline.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/malloc.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/realloc.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/setproctitle.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/strlcpy.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/strndup.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/strnlen.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/strtonum.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/vsyslog.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/empty.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 + +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) +installdirs: +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: + +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) + +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-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -f $(DEPDIR)/asprintf.Plo + -rm -f $(DEPDIR)/daemon.Plo + -rm -f $(DEPDIR)/getline.Plo + -rm -f $(DEPDIR)/malloc.Plo + -rm -f $(DEPDIR)/realloc.Plo + -rm -f $(DEPDIR)/setproctitle.Plo + -rm -f $(DEPDIR)/strlcpy.Plo + -rm -f $(DEPDIR)/strndup.Plo + -rm -f $(DEPDIR)/strnlen.Plo + -rm -f $(DEPDIR)/strtonum.Plo + -rm -f $(DEPDIR)/vsyslog.Plo + -rm -f ./$(DEPDIR)/empty.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-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +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)/asprintf.Plo + -rm -f $(DEPDIR)/daemon.Plo + -rm -f $(DEPDIR)/getline.Plo + -rm -f $(DEPDIR)/malloc.Plo + -rm -f $(DEPDIR)/realloc.Plo + -rm -f $(DEPDIR)/setproctitle.Plo + -rm -f $(DEPDIR)/strlcpy.Plo + -rm -f $(DEPDIR)/strndup.Plo + -rm -f $(DEPDIR)/strnlen.Plo + -rm -f $(DEPDIR)/strtonum.Plo + -rm -f $(DEPDIR)/vsyslog.Plo + -rm -f ./$(DEPDIR)/empty.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: + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-generic 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-info install-info-am install-man install-pdf \ + install-pdf-am 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 + +.PRECIOUS: Makefile + + +# 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/compat/asprintf.c b/src/compat/asprintf.c new file mode 100644 index 0000000..58c5dae --- /dev/null +++ b/src/compat/asprintf.c @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2004 Darren Tucker. + * + * Based originally on asprintf.c from OpenBSD: + * Copyright (c) 1997 Todd C. Miller <Todd.Miller@courtesan.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <errno.h> +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <limits.h> +#include "compat.h" + +#define INIT_SZ 128 + +int +vasprintf(char **str, const char *fmt, va_list ap) +{ + int ret = -1; + va_list ap2; + char *string, *newstr; + size_t len; + + va_copy(ap2, ap); + if ((string = malloc(INIT_SZ)) == NULL) goto fail; + + ret = vsnprintf(string, INIT_SZ, fmt, ap2); + if (ret >= 0 && ret < INIT_SZ) { /* succeeded with initial alloc */ + *str = string; + } else if (ret == INT_MAX || ret < 0) { /* Bad length */ + free(string); + goto fail; + } else { /* bigger than initial, realloc allowing for nul */ + len = (size_t)ret + 1; + if ((newstr = realloc(string, len)) == NULL) { + free(string); + goto fail; + } else { + va_end(ap2); + va_copy(ap2, ap); + ret = vsnprintf(newstr, len, fmt, ap2); + if (ret >= 0 && (size_t)ret < len) { + *str = newstr; + } else { /* failed with realloc'ed string, give up */ + free(newstr); + goto fail; + } + } + } + va_end(ap2); + return (ret); + +fail: + *str = NULL; + errno = ENOMEM; + va_end(ap2); + return (-1); +} + +int +asprintf(char **str, const char *fmt, ...) +{ + va_list ap; + int ret; + + *str = NULL; + va_start(ap, fmt); + ret = vasprintf(str, fmt, ap); + va_end(ap); + + return ret; +} diff --git a/src/compat/compat.h b/src/compat/compat.h new file mode 100644 index 0000000..91ff4ad --- /dev/null +++ b/src/compat/compat.h @@ -0,0 +1,93 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + */ + +#ifndef _COMPAT_H +#define _COMPAT_H + +#if HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdio.h> +#include <stddef.h> +#include <stdarg.h> +#include <string.h> +#include <unistd.h> + +#undef getopt + +#if !HAVE_ASPRINTF +int vasprintf(char **, const char *, va_list) __attribute__((format(printf, 2, 0))); +int asprintf(char **, const char *, ...) __attribute__((format(printf, 2, 3))); +#endif + +#if !HAVE_VSYSLOG +void vsyslog(int, const char *, va_list) __attribute__((format(printf, 2, 0))); +#endif + +#if !HAVE_DAEMON +int daemon(int, int); +#endif + +#if !HAVE_STRLCPY +size_t strlcpy(char *, const char *, size_t); +#endif + +#if !HAVE_STRNLEN +size_t strnlen(const char *, size_t); +#endif + +#if !HAVE_STRNDUP +char *strndup(const char *, size_t); +#endif + +#if !HAVE_STRTONUM +long long strtonum(const char *, long long, long long, const char **); +#endif + +#if !HAVE_GETLINE +ssize_t getline(char **, size_t *, FILE *); +#endif + +#if !HAVE_SETPROCTITLE +void setproctitle(const char *fmt, ...) __attribute__((format(printf, 1, 2))); +#endif + +#if !HAVE_MALLOC +void *malloc(size_t size); +#endif + +#if !HAVE_REALLOC +void *realloc(void *ptr, size_t size); +#endif + +#endif diff --git a/src/compat/daemon.c b/src/compat/daemon.c new file mode 100644 index 0000000..5b7f980 --- /dev/null +++ b/src/compat/daemon.c @@ -0,0 +1,66 @@ +/* $OpenBSD: daemon.c,v 1.6 2005/08/08 08:05:33 espie Exp $ */ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* OPENBSD ORIGINAL: lib/libc/gen/daemon.c */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include "compat.h" + +int +daemon(int nochdir, int noclose) +{ + int fd; + + switch (fork()) { + case -1: + return (-1); + case 0: + break; + default: + _exit(0); + } + + if (setsid() == -1) return (-1); + + if (!nochdir) (void)chdir("/"); + + /* coverity[resource_leak] + fd may be leaked if < 2, it's expected */ + if (!noclose && (fd = open("/dev/null", O_RDWR, 0)) != -1) { + (void)dup2(fd, STDIN_FILENO); + (void)dup2(fd, STDOUT_FILENO); + (void)dup2(fd, STDERR_FILENO); + if (fd > 2) (void)close(fd); + } + return (0); +} diff --git a/src/compat/empty.c b/src/compat/empty.c new file mode 100644 index 0000000..85d5837 --- /dev/null +++ b/src/compat/empty.c @@ -0,0 +1,21 @@ +/* -*- 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. + */ + +/* Some versions of ar don't like to build a library from + * nothing. This happens on Mac OS X where we don't need any + * compatibility layer. So, we put a tiny variable. Just here. */ +int __lldpd_int_zero = 0; diff --git a/src/compat/getline.c b/src/compat/getline.c new file mode 100644 index 0000000..a3e6047 --- /dev/null +++ b/src/compat/getline.c @@ -0,0 +1,106 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2009 The NetBSD Foundation, Inc. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Roy Marples. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* Several modifications to make the code more portable (and less robust and far less + * efficient) */ + +#include <stdio.h> +#include <errno.h> +#include <limits.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "compat.h" + +#define MINBUF 128 + +static ssize_t +___getdelim(char **buf, size_t *buflen, int sep, FILE *fp) +{ + int p; + size_t len = 0, newlen; + char *newb; + + if (buf == NULL || buflen == NULL) { + errno = EINVAL; + return -1; + } + + /* If buf is NULL, we have to assume a size of zero */ + if (*buf == NULL) *buflen = 0; + + do { + p = fgetc(fp); + if (ferror(fp)) return -1; + if (p == EOF) break; + + /* Ensure we can handle it */ + if (len > SSIZE_MAX) { + errno = EOVERFLOW; + return -1; + } + newlen = len + 2; /* reserve space for NUL terminator */ + if (newlen > *buflen) { + if (newlen < MINBUF) newlen = MINBUF; +#define powerof2(x) ((((x)-1) & (x)) == 0) + if (!powerof2(newlen)) { + /* Grow the buffer to the next power of 2 */ + newlen--; + newlen |= newlen >> 1; + newlen |= newlen >> 2; + newlen |= newlen >> 4; + newlen |= newlen >> 8; + newlen |= newlen >> 16; +#if SIZE_MAX > 0xffffffffU + newlen |= newlen >> 32; +#endif + newlen++; + } + + newb = realloc(*buf, newlen); + if (newb == NULL) return -1; + *buf = newb; + *buflen = newlen; + } + + (*buf)[len++] = p; + } while (p != sep); + + /* POSIX demands we return -1 on EOF. */ + if (len == 0) return -1; + + if (*buf != NULL) (*buf)[len] = '\0'; + return (ssize_t)len; +} + +ssize_t +getline(char **buf, size_t *buflen, FILE *fp) +{ + return ___getdelim(buf, buflen, '\n', fp); +} diff --git a/src/compat/malloc.c b/src/compat/malloc.c new file mode 100644 index 0000000..0c8bb2c --- /dev/null +++ b/src/compat/malloc.c @@ -0,0 +1,16 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* malloc replacement that can allocate 0 byte */ + +#undef malloc +#include <stdlib.h> +#include <sys/types.h> +#include "compat.h" + +/* Allocate an N-byte block of memory from the heap. + If N is zero, allocate a 1-byte block. */ +void * +rpl_malloc(size_t n) +{ + if (n == 0) n = 1; + return malloc(n); +} diff --git a/src/compat/realloc.c b/src/compat/realloc.c new file mode 100644 index 0000000..4d74064 --- /dev/null +++ b/src/compat/realloc.c @@ -0,0 +1,17 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* realloc replacement that can reallocate 0 byte or NULL pointers*/ + +#undef realloc +#include <stdlib.h> +#include <sys/types.h> +#include "compat.h" + +/* Reallocate an N-byte block of memory from the heap. + If N is zero, allocate a 1-byte block. */ +void * +rpl_realloc(void *ptr, size_t n) +{ + if (!ptr) return malloc(n); + if (n == 0) n = 1; + return realloc(ptr, n); +} diff --git a/src/compat/setproctitle.c b/src/compat/setproctitle.c new file mode 100644 index 0000000..c5e40ec --- /dev/null +++ b/src/compat/setproctitle.c @@ -0,0 +1,9 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ + +#include "compat.h" + +void +setproctitle(const char *fmt, ...) +{ + /* Do nothing. */ +} diff --git a/src/compat/strlcpy.c b/src/compat/strlcpy.c new file mode 100644 index 0000000..dda982f --- /dev/null +++ b/src/compat/strlcpy.c @@ -0,0 +1,51 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $ */ + +/* + * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com> + * + * Permission to use, copy, modify, and/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/types.h> +#include <string.h> +#include "compat.h" + +/* + * Copy src to string dst of size siz. At most siz-1 characters + * will be copied. Always NUL terminates (unless siz == 0). + * Returns strlen(src); if retval >= siz, truncation occurred. + */ +size_t +strlcpy(char *dst, const char *src, size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + + /* Copy as many bytes as will fit */ + if (n != 0) { + while (--n != 0) { + if ((*d++ = *s++) == '\0') break; + } + } + + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) { + if (siz != 0) *d = '\0'; /* NUL-terminate dst */ + while (*s++) + ; + } + + return (s - src - 1); /* count does not include NUL */ +} diff --git a/src/compat/strndup.c b/src/compat/strndup.c new file mode 100644 index 0000000..1db7069 --- /dev/null +++ b/src/compat/strndup.c @@ -0,0 +1,24 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ + +#include <stdlib.h> +#include <string.h> +#include "compat.h" + +/* + * Similar to `strdup()` but copies at most n bytes. + */ +char * +strndup(const char *string, size_t maxlen) +{ + char *result; + /* We may use `strnlen()` but it may be unavailable. */ + const char *end = memchr(string, '\0', maxlen); + size_t len = end ? (size_t)(end - string) : maxlen; + + result = malloc(len + 1); + if (!result) return 0; + + memcpy(result, string, len); + result[len] = '\0'; + return result; +} diff --git a/src/compat/strnlen.c b/src/compat/strnlen.c new file mode 100644 index 0000000..07db4a4 --- /dev/null +++ b/src/compat/strnlen.c @@ -0,0 +1,15 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ + +#include <string.h> +#include "compat.h" + +/* + * Determine the length of a fixed-size string. This is really a + * wrapper around `memchr()`. + */ +size_t +strnlen(const char *string, size_t maxlen) +{ + const char *end = memchr(string, '\0', maxlen); + return end ? (size_t)(end - string) : maxlen; +} diff --git a/src/compat/strtonum.c b/src/compat/strtonum.c new file mode 100644 index 0000000..aed36b9 --- /dev/null +++ b/src/compat/strtonum.c @@ -0,0 +1,65 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ + +/* $OpenBSD: strtonum.c,v 1.8 2015/09/13 08:31:48 guenther Exp $ */ + +/* + * Copyright (c) 2004 Ted Unangst and Todd Miller + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <errno.h> +#include <limits.h> +#include <stdlib.h> +#include "compat.h" + +#define INVALID 1 +#define TOOSMALL 2 +#define TOOLARGE 3 + +long long +strtonum(const char *numstr, long long minval, long long maxval, const char **errstrp) +{ + long long ll = 0; + int error = 0; + char *ep; + struct errval { + const char *errstr; + int err; + } ev[4] = { + { NULL, 0 }, + { "invalid", EINVAL }, + { "too small", ERANGE }, + { "too large", ERANGE }, + }; + + ev[0].err = errno; + errno = 0; + if (minval > maxval) { + error = INVALID; + } else { + ll = strtoll(numstr, &ep, 10); + if (numstr == ep || *ep != '\0') + error = INVALID; + else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval) + error = TOOSMALL; + else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval) + error = TOOLARGE; + } + if (errstrp != NULL) *errstrp = ev[error].errstr; + errno = ev[error].err; + if (error) ll = 0; + + return (ll); +} diff --git a/src/compat/vsyslog.c b/src/compat/vsyslog.c new file mode 100644 index 0000000..a3209cb --- /dev/null +++ b/src/compat/vsyslog.c @@ -0,0 +1,17 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ + +#include <stdlib.h> +#include <syslog.h> +#include "compat.h" + +/* vsyslog() doesn't exist on HP-UX */ +void +vsyslog(int facility, const char *format, va_list ap) +{ + char *msg = NULL; + if (vasprintf(&msg, format, ap) == -1) { + return; + } + syslog(facility, "%s", msg); + free(msg); +} diff --git a/src/ctl.c b/src/ctl.c new file mode 100644 index 0000000..f9473a2 --- /dev/null +++ b/src/ctl.c @@ -0,0 +1,261 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <string.h> + +#include "ctl.h" +#include "marshal.h" +#include "log.h" +#include "compat/compat.h" + +/** + * Create a new listening Unix socket for control protocol. + * + * @param name The name of the Unix socket. + * @return The socket when successful, -1 otherwise. + */ +int +ctl_create(const char *name) +{ + int s; + struct sockaddr_un su; + int rc; + + log_debug("control", "create control socket %s", name); + + if ((s = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) return -1; + if (fcntl(s, F_SETFD, FD_CLOEXEC) == -1) { + close(s); + return -1; + } + su.sun_family = AF_UNIX; + strlcpy(su.sun_path, name, sizeof(su.sun_path)); + if (bind(s, (struct sockaddr *)&su, sizeof(struct sockaddr_un)) == -1) { + rc = errno; + close(s); + errno = rc; + return -1; + } + + log_debug("control", "listen to control socket %s", name); + if (listen(s, 5) == -1) { + rc = errno; + close(s); + errno = rc; + log_debug("control", "cannot listen to control socket %s", name); + return -1; + } + return s; +} + +/** + * Connect to the control Unix socket. + * + * @param name The name of the Unix socket. + * @return The socket when successful, -1 otherwise. + */ +int +ctl_connect(const char *name) +{ + int s; + struct sockaddr_un su; + int rc; + + log_debug("control", "connect to control socket %s", name); + + if ((s = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) return -1; + su.sun_family = AF_UNIX; + strlcpy(su.sun_path, name, sizeof(su.sun_path)); + if (connect(s, (struct sockaddr *)&su, sizeof(struct sockaddr_un)) == -1) { + rc = errno; + log_warn("control", "unable to connect to socket %s", name); + close(s); + errno = rc; + return -1; + } + return s; +} + +/** + * Remove the control Unix socket. + * + * @param name The name of the Unix socket. + */ +void +ctl_cleanup(const char *name) +{ + log_debug("control", "cleanup control socket"); + if (unlink(name) == -1) log_warn("control", "unable to unlink %s", name); +} + +/** + * Serialize and "send" a structure through the control protocol. + * + * This function does not really send the message but outputs it to a buffer. + * + * @param output_buffer A pointer to a buffer to which the message will be + * appended. Can be @c NULL. In this case, the buffer will + * be allocated. + * @param[in,out] output_len The length of the provided buffer. Will be updated + * with the new length + * @param type The type of message we want to send. + * @param t The structure to be serialized and sent. + * @param mi The appropriate marshal structure for serialization. + * @return -1 in case of failure, 0 in case of success. + * + * Make sure this function logic matches the server-side one: @c levent_ctl_recv(). + */ +int +ctl_msg_send_unserialized(uint8_t **output_buffer, size_t *output_len, + enum hmsg_type type, void *t, struct marshal_info *mi) +{ + ssize_t len = 0, newlen; + void *buffer = NULL; + + log_debug("control", "send a message through control socket"); + if (t) { + len = marshal_serialize_(mi, t, &buffer, 0, NULL, 0); + if (len <= 0) { + log_warnx("control", "unable to serialize data"); + return -1; + } + } + + newlen = len + sizeof(struct hmsg_header); + + if (*output_buffer == NULL) { + *output_len = 0; + if ((*output_buffer = malloc(newlen)) == NULL) { + log_warn("control", "no memory available"); + free(buffer); + return -1; + } + } else { + void *new = realloc(*output_buffer, *output_len + newlen); + if (new == NULL) { + log_warn("control", "no memory available"); + free(buffer); + return -1; + } + *output_buffer = new; + } + + struct hmsg_header hdr; + memset(&hdr, 0, sizeof(struct hmsg_header)); + hdr.type = type; + hdr.len = len; + memcpy(*output_buffer + *output_len, &hdr, sizeof(struct hmsg_header)); + if (t) + memcpy(*output_buffer + *output_len + sizeof(struct hmsg_header), + buffer, len); + *output_len += newlen; + free(buffer); + return 0; +} + +/** + * "Receive" and unserialize a structure through the control protocol. + * + * Like @c ctl_msg_send_unserialized(), this function uses buffer to receive the + * incoming message. + * + * @param[in,out] input_buffer The buffer with the incoming message. Will be + * updated once the message has been unserialized to + * point to the remaining of the message or will be + * freed if all the buffer has been consumed. Can be + * @c NULL. + * @param[in,out] input_len The length of the provided buffer. Will be updated + * to the length of remaining data once the message + * has been unserialized. + * @param expected_type The expected message type. + * @param[out] t Will contain a pointer to the unserialized structure. + * Can be @c NULL if we don't want to store the + * answer. + * @param mi The appropriate marshal structure for unserialization. + * + * @return -1 in case of error, 0 in case of success and the number of bytes we + * request to complete unserialization. + * + * When requesting a notification, the input buffer is left untouched if we + * don't get one and we fail silently. + */ +size_t +ctl_msg_recv_unserialized(uint8_t **input_buffer, size_t *input_len, + enum hmsg_type expected_type, void **t, struct marshal_info *mi) +{ + struct hmsg_header hdr; + int rc = -1; + + if (*input_buffer == NULL || *input_len < sizeof(struct hmsg_header)) { + /* Not enough data. */ + return sizeof(struct hmsg_header) - *input_len; + } + + log_debug("control", "receive a message through control socket"); + memcpy(&hdr, *input_buffer, sizeof(struct hmsg_header)); + if (hdr.len > HMSG_MAX_SIZE) { + log_warnx("control", "message received is too large"); + /* We discard the whole buffer */ + free(*input_buffer); + *input_buffer = NULL; + *input_len = 0; + return -1; + } + if (*input_len < sizeof(struct hmsg_header) + hdr.len) { + /* Not enough data. */ + return sizeof(struct hmsg_header) + hdr.len - *input_len; + } + if (hdr.type != expected_type) { + if (expected_type == NOTIFICATION) return -1; + log_warnx("control", + "incorrect received message type (expected: %d, received: %d)", + expected_type, hdr.type); + goto end; + } + + if (t && !hdr.len) { + log_warnx("control", "no payload available in answer"); + goto end; + } + if (t) { + /* We have data to unserialize. */ + if (marshal_unserialize_(mi, *input_buffer + sizeof(struct hmsg_header), + hdr.len, t, NULL, 0, 0) <= 0) { + log_warnx("control", "unable to deserialize received data"); + goto end; + } + } + + rc = 0; +end: + /* Discard input buffer */ + *input_len -= sizeof(struct hmsg_header) + hdr.len; + if (*input_len == 0) { + free(*input_buffer); + *input_buffer = NULL; + } else + memmove(*input_buffer, + *input_buffer + sizeof(struct hmsg_header) + hdr.len, *input_len); + return rc; +} diff --git a/src/ctl.h b/src/ctl.h new file mode 100644 index 0000000..7e5b076 --- /dev/null +++ b/src/ctl.h @@ -0,0 +1,64 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _CTL_H +#define _CTL_H + +#if HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdint.h> +#include "marshal.h" + +enum hmsg_type { + NONE, + GET_CONFIG, /* Get global configuration */ + SET_CONFIG, /* Change global configuration */ + GET_INTERFACES, /* Get list of interfaces */ + SET_CHASSIS, /* Set local chassis */ + GET_CHASSIS, /* Get local chassis */ + GET_INTERFACE, /* Get all information related to an interface */ + GET_DEFAULT_PORT, /* Get all information related to default port */ + SET_PORT, /* Set port-related information (location, power, policy) */ + SUBSCRIBE, /* Subscribe to neighbor changes */ + NOTIFICATION, /* Notification message (sent by lldpd!) */ +}; + +/** Header for the control protocol. + * + * The protocol is pretty simple. We send a single message containing the + * provided message type with the message length, followed by the message + * content. + */ +struct hmsg_header { + enum hmsg_type type; + size_t len; +}; +#define HMSG_MAX_SIZE (1 << 19) + +/* ctl.c */ +int ctl_create(const char *); +int ctl_connect(const char *); +void ctl_cleanup(const char *); + +int ctl_msg_send_unserialized(uint8_t **, size_t *, enum hmsg_type, void *, + struct marshal_info *); +size_t ctl_msg_recv_unserialized(uint8_t **, size_t *, enum hmsg_type, void **, + struct marshal_info *); + +#endif diff --git a/src/daemon/Makefile.am b/src/daemon/Makefile.am new file mode 100644 index 0000000..42240a4 --- /dev/null +++ b/src/daemon/Makefile.am @@ -0,0 +1,180 @@ +AM_CFLAGS = -I $(top_srcdir)/include $(LLDP_CFLAGS) +AM_CPPFLAGS = $(LLDP_CPPFLAGS) +AM_LDFLAGS = $(LLDP_LDFLAGS) +BUILT_SOURCES = +CLEANFILES = + +sbin_PROGRAMS = lldpd +man_MANS = lldpd.8 + +noinst_LTLIBRARIES = liblldpd.la + +## Convenience library for lldpd and tests +nodist_liblldpd_la_SOURCES = +liblldpd_la_SOURCES = \ + frame.h frame.c \ + lldp-tlv.h \ + client.c \ + priv.c \ + privsep.c privsep_io.c privsep_fd.c \ + interfaces.c \ + event.c lldpd.c \ + pattern.c \ + bitmap.c \ + probes.d trace.h \ + protocols/lldp.c \ + protocols/cdp.c \ + protocols/cdp.h \ + protocols/sonmp.c \ + protocols/sonmp.h \ + protocols/edp.c \ + protocols/edp.h +liblldpd_la_CFLAGS = $(AM_CFLAGS) @libevent_CFLAGS@ @libcap_CFLAGS@ +liblldpd_la_CPPFLAGS = $(AM_CPPFLAGS) -DSYSCONFDIR='"$(sysconfdir)"' -DLLDPCLI_PATH='"$(sbindir)/lldpcli"' +liblldpd_la_LIBADD = \ + $(top_builddir)/src/libcommon-daemon-client.la \ + $(top_builddir)/src/libcommon-daemon-lib.la @libevent_LIBS@ @libcap_LIBS@ + +## lldpd +lldpd_SOURCES = main.c +lldpd_LDFLAGS = $(AM_LDFLAGS) $(LLDP_BIN_LDFLAGS) +lldpd_LDADD = liblldpd.la @libevent_LDFLAGS@ + +if HOST_OS_LINUX +liblldpd_la_SOURCES += \ + forward-linux.c \ + interfaces-linux.c \ + netlink.c \ + dmi-linux.c \ + priv-linux.c +endif +if HOST_OS_DRAGONFLY +liblldpd_la_SOURCES += \ + forward-bsd.c \ + interfaces-bpf.c \ + interfaces-bsd.c \ + dmi-dummy.c \ + priv-bsd.c +endif +if HOST_OS_FREEBSD +liblldpd_la_SOURCES += \ + forward-bsd.c \ + interfaces-bpf.c \ + interfaces-bsd.c \ + dmi-freebsd.c \ + priv-bsd.c +endif +if HOST_OS_OPENBSD +liblldpd_la_SOURCES += \ + interfaces-bpf.c \ + forward-bsd.c \ + interfaces-bsd.c \ + dmi-openbsd.c \ + priv-bsd.c +endif +if HOST_OS_NETBSD +liblldpd_la_SOURCES += \ + forward-bsd.c \ + interfaces-bpf.c \ + interfaces-bsd.c \ + dmi-dummy.c \ + priv-bsd.c +endif +if HOST_OS_OSX +liblldpd_la_SOURCES += \ + forward-bsd.c \ + interfaces-bpf.c \ + interfaces-bsd.c \ + dmi-osx.c \ + priv-bsd.c +liblldpd_la_LDFLAGS = $(AM_LDFLAGS) +liblldpd_la_LDFLAGS += -framework Foundation +liblldpd_la_LDFLAGS += -framework CoreFoundation -framework IOKit +liblldpd_la_LDFLAGS += -framework IOKit +endif +if HOST_OS_SOLARIS +liblldpd_la_SOURCES += \ + forward-solaris.c \ + interfaces-bpf.c \ + interfaces-solaris.c \ + dmi-dummy.c \ + priv-bsd.c +endif + +# seccomp support +if USE_SECCOMP +BUILT_SOURCES += syscall-names.h +CLEANFILES += syscall-names.h syscall-names.h.tmp +syscall-names.h: + $(AM_V_GEN) + $(AM_V_at)echo "#include <sys/syscall.h>" | $(CPP) -dM - > $@.tmp ;\ + echo "static const char *syscall_names[] = {" > $@ ;\ + grep '^#define __NR_' $@.tmp | \ + LC_ALL=C sed -r -n -e 's/^\#define[ \t]+__NR_([a-z0-9_]+)[ \t]+([0-9]+)(.*)/ [\2] = "\1",/p' >> $@ ;\ + echo "};" >> $@ ;\ + rm $@.tmp +nodist_liblldpd_la_SOURCES += syscall-names.h +liblldpd_la_SOURCES += priv-seccomp.c +liblldpd_la_CFLAGS += @libseccomp_CFLAGS@ +liblldpd_la_LIBADD += @libseccomp_LIBS@ +endif + +# Add SNMP support if needed +if USE_SNMP +noinst_LTLIBRARIES += liblldpd-snmp.la +liblldpd_snmp_la_SOURCES = agent.c agent_priv.c agent.h +liblldpd_snmp_la_CFLAGS = $(liblldpd_la_CFLAGS) @NETSNMP_CFLAGS@ +liblldpd_snmp_la_CPPFLAGS = $(liblldpd_la_CPPFLAGS) +liblldpd_la_LIBADD += liblldpd-snmp.la +lldpd_LDADD += @NETSNMP_LIBS@ +endif + +## Systemtap/DTrace +EXTRA_DIST = dtrace2systemtap.awk +if ENABLE_SYSTEMTAP +BUILT_SOURCES += probes.h +CLEANFILES += probes.h lldpd.stp +probes.h: probes.d + $(AM_V_GEN) + $(AM_V_at)$(DTRACE) -C -h -s $< -o $@ +probes.o: probes.d + $(AM_V_GEN) + $(AM_V_at)$(DTRACE) -C -G -s $< -o $@ +lldpd_LDADD += probes.o + +lldpd.stp: probes.d $(srcdir)/dtrace2systemtap.awk $(top_builddir)/config.status + $(AM_V_GEN)$(AWK) -f $(srcdir)/dtrace2systemtap.awk -v sbindir=$(sbindir) $< > $@ || ( rm -f $@ ; exit 1 ) +tapsetdir = $(datadir)/systemtap/tapset +tapset_DATA = lldpd.stp +endif + +## libevent +if LIBEVENT_EMBEDDED +event.c: $(top_builddir)/libevent/libevent.la +$(top_builddir)/libevent/libevent.la: $(top_srcdir)/libevent/*.c $(top_srcdir)/libevent/*.h + (cd $(top_builddir)/libevent && $(MAKE)) +endif + +## systemd service file +if HAVE_SYSTEMDSYSTEMUNITDIR +systemdsystemunit_DATA = lldpd.service +endif + +if HAVE_SYSUSERSDIR +sysusers_DATA = lldpd.sysusers.conf +endif + +if HOST_OS_LINUX +if HAVE_APPARMORDIR +apparmor_DATA = usr.sbin.lldpd +endif +endif + +TEMPLATES = lldpd.8 lldpd.service lldpd.sysusers.conf usr.sbin.lldpd +EXTRA_DIST += lldpd.8.in lldpd.service.in lldpd.sysusers.conf.in usr.sbin.lldpd.in +CLEANFILES += $(TEMPLATES) +lldpd.8: lldpd.8.in +lldpd.service: lldpd.service.in +lldpd.sysusers.conf: lldpd.sysusers.conf.in +usr.sbin.lldpd: usr.sbin.lldpd.in +include $(top_srcdir)/edit.am diff --git a/src/daemon/Makefile.in b/src/daemon/Makefile.in new file mode 100644 index 0000000..397289e --- /dev/null +++ b/src/daemon/Makefile.in @@ -0,0 +1,1545 @@ +# 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@ +sbin_PROGRAMS = lldpd$(EXEEXT) +@HOST_OS_LINUX_TRUE@am__append_1 = \ +@HOST_OS_LINUX_TRUE@ forward-linux.c \ +@HOST_OS_LINUX_TRUE@ interfaces-linux.c \ +@HOST_OS_LINUX_TRUE@ netlink.c \ +@HOST_OS_LINUX_TRUE@ dmi-linux.c \ +@HOST_OS_LINUX_TRUE@ priv-linux.c + +@HOST_OS_DRAGONFLY_TRUE@am__append_2 = \ +@HOST_OS_DRAGONFLY_TRUE@ forward-bsd.c \ +@HOST_OS_DRAGONFLY_TRUE@ interfaces-bpf.c \ +@HOST_OS_DRAGONFLY_TRUE@ interfaces-bsd.c \ +@HOST_OS_DRAGONFLY_TRUE@ dmi-dummy.c \ +@HOST_OS_DRAGONFLY_TRUE@ priv-bsd.c + +@HOST_OS_FREEBSD_TRUE@am__append_3 = \ +@HOST_OS_FREEBSD_TRUE@ forward-bsd.c \ +@HOST_OS_FREEBSD_TRUE@ interfaces-bpf.c \ +@HOST_OS_FREEBSD_TRUE@ interfaces-bsd.c \ +@HOST_OS_FREEBSD_TRUE@ dmi-freebsd.c \ +@HOST_OS_FREEBSD_TRUE@ priv-bsd.c + +@HOST_OS_OPENBSD_TRUE@am__append_4 = \ +@HOST_OS_OPENBSD_TRUE@ interfaces-bpf.c \ +@HOST_OS_OPENBSD_TRUE@ forward-bsd.c \ +@HOST_OS_OPENBSD_TRUE@ interfaces-bsd.c \ +@HOST_OS_OPENBSD_TRUE@ dmi-openbsd.c \ +@HOST_OS_OPENBSD_TRUE@ priv-bsd.c + +@HOST_OS_NETBSD_TRUE@am__append_5 = \ +@HOST_OS_NETBSD_TRUE@ forward-bsd.c \ +@HOST_OS_NETBSD_TRUE@ interfaces-bpf.c \ +@HOST_OS_NETBSD_TRUE@ interfaces-bsd.c \ +@HOST_OS_NETBSD_TRUE@ dmi-dummy.c \ +@HOST_OS_NETBSD_TRUE@ priv-bsd.c + +@HOST_OS_OSX_TRUE@am__append_6 = \ +@HOST_OS_OSX_TRUE@ forward-bsd.c \ +@HOST_OS_OSX_TRUE@ interfaces-bpf.c \ +@HOST_OS_OSX_TRUE@ interfaces-bsd.c \ +@HOST_OS_OSX_TRUE@ dmi-osx.c \ +@HOST_OS_OSX_TRUE@ priv-bsd.c + +@HOST_OS_SOLARIS_TRUE@am__append_7 = \ +@HOST_OS_SOLARIS_TRUE@ forward-solaris.c \ +@HOST_OS_SOLARIS_TRUE@ interfaces-bpf.c \ +@HOST_OS_SOLARIS_TRUE@ interfaces-solaris.c \ +@HOST_OS_SOLARIS_TRUE@ dmi-dummy.c \ +@HOST_OS_SOLARIS_TRUE@ priv-bsd.c + + +# seccomp support +@USE_SECCOMP_TRUE@am__append_8 = syscall-names.h +@USE_SECCOMP_TRUE@am__append_9 = syscall-names.h syscall-names.h.tmp +@USE_SECCOMP_TRUE@am__append_10 = syscall-names.h +@USE_SECCOMP_TRUE@am__append_11 = priv-seccomp.c +@USE_SECCOMP_TRUE@am__append_12 = @libseccomp_CFLAGS@ +@USE_SECCOMP_TRUE@am__append_13 = @libseccomp_LIBS@ + +# Add SNMP support if needed +@USE_SNMP_TRUE@am__append_14 = liblldpd-snmp.la +@USE_SNMP_TRUE@am__append_15 = liblldpd-snmp.la +@USE_SNMP_TRUE@am__append_16 = @NETSNMP_LIBS@ +@ENABLE_SYSTEMTAP_TRUE@am__append_17 = probes.h +@ENABLE_SYSTEMTAP_TRUE@am__append_18 = probes.h lldpd.stp +@ENABLE_SYSTEMTAP_TRUE@am__append_19 = probes.o +subdir = src/daemon +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 $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__installdirs = "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)" \ + "$(DESTDIR)$(apparmordir)" "$(DESTDIR)$(systemdsystemunitdir)" \ + "$(DESTDIR)$(sysusersdir)" "$(DESTDIR)$(tapsetdir)" +PROGRAMS = $(sbin_PROGRAMS) +LTLIBRARIES = $(noinst_LTLIBRARIES) +liblldpd_snmp_la_LIBADD = +am__liblldpd_snmp_la_SOURCES_DIST = agent.c agent_priv.c agent.h +@USE_SNMP_TRUE@am_liblldpd_snmp_la_OBJECTS = \ +@USE_SNMP_TRUE@ liblldpd_snmp_la-agent.lo \ +@USE_SNMP_TRUE@ liblldpd_snmp_la-agent_priv.lo +liblldpd_snmp_la_OBJECTS = $(am_liblldpd_snmp_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 = +liblldpd_snmp_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(liblldpd_snmp_la_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +@USE_SNMP_TRUE@am_liblldpd_snmp_la_rpath = +am__DEPENDENCIES_1 = +liblldpd_la_DEPENDENCIES = \ + $(top_builddir)/src/libcommon-daemon-client.la \ + $(top_builddir)/src/libcommon-daemon-lib.la \ + $(am__DEPENDENCIES_1) $(am__append_15) +am__liblldpd_la_SOURCES_DIST = frame.h frame.c lldp-tlv.h client.c \ + priv.c privsep.c privsep_io.c privsep_fd.c interfaces.c \ + event.c lldpd.c pattern.c bitmap.c probes.d trace.h \ + protocols/lldp.c protocols/cdp.c protocols/cdp.h \ + protocols/sonmp.c protocols/sonmp.h protocols/edp.c \ + protocols/edp.h forward-linux.c interfaces-linux.c netlink.c \ + dmi-linux.c priv-linux.c forward-bsd.c interfaces-bpf.c \ + interfaces-bsd.c dmi-dummy.c priv-bsd.c dmi-freebsd.c \ + dmi-openbsd.c dmi-osx.c forward-solaris.c interfaces-solaris.c \ + priv-seccomp.c +am__dirstamp = $(am__leading_dot)dirstamp +@HOST_OS_LINUX_TRUE@am__objects_1 = liblldpd_la-forward-linux.lo \ +@HOST_OS_LINUX_TRUE@ liblldpd_la-interfaces-linux.lo \ +@HOST_OS_LINUX_TRUE@ liblldpd_la-netlink.lo \ +@HOST_OS_LINUX_TRUE@ liblldpd_la-dmi-linux.lo \ +@HOST_OS_LINUX_TRUE@ liblldpd_la-priv-linux.lo +@HOST_OS_DRAGONFLY_TRUE@am__objects_2 = liblldpd_la-forward-bsd.lo \ +@HOST_OS_DRAGONFLY_TRUE@ liblldpd_la-interfaces-bpf.lo \ +@HOST_OS_DRAGONFLY_TRUE@ liblldpd_la-interfaces-bsd.lo \ +@HOST_OS_DRAGONFLY_TRUE@ liblldpd_la-dmi-dummy.lo \ +@HOST_OS_DRAGONFLY_TRUE@ liblldpd_la-priv-bsd.lo +@HOST_OS_FREEBSD_TRUE@am__objects_3 = liblldpd_la-forward-bsd.lo \ +@HOST_OS_FREEBSD_TRUE@ liblldpd_la-interfaces-bpf.lo \ +@HOST_OS_FREEBSD_TRUE@ liblldpd_la-interfaces-bsd.lo \ +@HOST_OS_FREEBSD_TRUE@ liblldpd_la-dmi-freebsd.lo \ +@HOST_OS_FREEBSD_TRUE@ liblldpd_la-priv-bsd.lo +@HOST_OS_OPENBSD_TRUE@am__objects_4 = liblldpd_la-interfaces-bpf.lo \ +@HOST_OS_OPENBSD_TRUE@ liblldpd_la-forward-bsd.lo \ +@HOST_OS_OPENBSD_TRUE@ liblldpd_la-interfaces-bsd.lo \ +@HOST_OS_OPENBSD_TRUE@ liblldpd_la-dmi-openbsd.lo \ +@HOST_OS_OPENBSD_TRUE@ liblldpd_la-priv-bsd.lo +@HOST_OS_NETBSD_TRUE@am__objects_5 = liblldpd_la-forward-bsd.lo \ +@HOST_OS_NETBSD_TRUE@ liblldpd_la-interfaces-bpf.lo \ +@HOST_OS_NETBSD_TRUE@ liblldpd_la-interfaces-bsd.lo \ +@HOST_OS_NETBSD_TRUE@ liblldpd_la-dmi-dummy.lo \ +@HOST_OS_NETBSD_TRUE@ liblldpd_la-priv-bsd.lo +@HOST_OS_OSX_TRUE@am__objects_6 = liblldpd_la-forward-bsd.lo \ +@HOST_OS_OSX_TRUE@ liblldpd_la-interfaces-bpf.lo \ +@HOST_OS_OSX_TRUE@ liblldpd_la-interfaces-bsd.lo \ +@HOST_OS_OSX_TRUE@ liblldpd_la-dmi-osx.lo \ +@HOST_OS_OSX_TRUE@ liblldpd_la-priv-bsd.lo +@HOST_OS_SOLARIS_TRUE@am__objects_7 = liblldpd_la-forward-solaris.lo \ +@HOST_OS_SOLARIS_TRUE@ liblldpd_la-interfaces-bpf.lo \ +@HOST_OS_SOLARIS_TRUE@ liblldpd_la-interfaces-solaris.lo \ +@HOST_OS_SOLARIS_TRUE@ liblldpd_la-dmi-dummy.lo \ +@HOST_OS_SOLARIS_TRUE@ liblldpd_la-priv-bsd.lo +@USE_SECCOMP_TRUE@am__objects_8 = liblldpd_la-priv-seccomp.lo +am_liblldpd_la_OBJECTS = liblldpd_la-frame.lo liblldpd_la-client.lo \ + liblldpd_la-priv.lo liblldpd_la-privsep.lo \ + liblldpd_la-privsep_io.lo liblldpd_la-privsep_fd.lo \ + liblldpd_la-interfaces.lo liblldpd_la-event.lo \ + liblldpd_la-lldpd.lo liblldpd_la-pattern.lo \ + liblldpd_la-bitmap.lo protocols/liblldpd_la-lldp.lo \ + protocols/liblldpd_la-cdp.lo protocols/liblldpd_la-sonmp.lo \ + protocols/liblldpd_la-edp.lo $(am__objects_1) $(am__objects_2) \ + $(am__objects_3) $(am__objects_4) $(am__objects_5) \ + $(am__objects_6) $(am__objects_7) $(am__objects_8) +am__objects_9 = +nodist_liblldpd_la_OBJECTS = $(am__objects_9) +liblldpd_la_OBJECTS = $(am_liblldpd_la_OBJECTS) \ + $(nodist_liblldpd_la_OBJECTS) +liblldpd_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(liblldpd_la_CFLAGS) \ + $(CFLAGS) $(liblldpd_la_LDFLAGS) $(LDFLAGS) -o $@ +am_lldpd_OBJECTS = main.$(OBJEXT) +lldpd_OBJECTS = $(am_lldpd_OBJECTS) +lldpd_DEPENDENCIES = liblldpd.la $(am__DEPENDENCIES_1) \ + $(am__append_19) +lldpd_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(lldpd_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)/liblldpd_la-bitmap.Plo \ + ./$(DEPDIR)/liblldpd_la-client.Plo \ + ./$(DEPDIR)/liblldpd_la-dmi-dummy.Plo \ + ./$(DEPDIR)/liblldpd_la-dmi-freebsd.Plo \ + ./$(DEPDIR)/liblldpd_la-dmi-linux.Plo \ + ./$(DEPDIR)/liblldpd_la-dmi-openbsd.Plo \ + ./$(DEPDIR)/liblldpd_la-dmi-osx.Plo \ + ./$(DEPDIR)/liblldpd_la-event.Plo \ + ./$(DEPDIR)/liblldpd_la-forward-bsd.Plo \ + ./$(DEPDIR)/liblldpd_la-forward-linux.Plo \ + ./$(DEPDIR)/liblldpd_la-forward-solaris.Plo \ + ./$(DEPDIR)/liblldpd_la-frame.Plo \ + ./$(DEPDIR)/liblldpd_la-interfaces-bpf.Plo \ + ./$(DEPDIR)/liblldpd_la-interfaces-bsd.Plo \ + ./$(DEPDIR)/liblldpd_la-interfaces-linux.Plo \ + ./$(DEPDIR)/liblldpd_la-interfaces-solaris.Plo \ + ./$(DEPDIR)/liblldpd_la-interfaces.Plo \ + ./$(DEPDIR)/liblldpd_la-lldpd.Plo \ + ./$(DEPDIR)/liblldpd_la-netlink.Plo \ + ./$(DEPDIR)/liblldpd_la-pattern.Plo \ + ./$(DEPDIR)/liblldpd_la-priv-bsd.Plo \ + ./$(DEPDIR)/liblldpd_la-priv-linux.Plo \ + ./$(DEPDIR)/liblldpd_la-priv-seccomp.Plo \ + ./$(DEPDIR)/liblldpd_la-priv.Plo \ + ./$(DEPDIR)/liblldpd_la-privsep.Plo \ + ./$(DEPDIR)/liblldpd_la-privsep_fd.Plo \ + ./$(DEPDIR)/liblldpd_la-privsep_io.Plo \ + ./$(DEPDIR)/liblldpd_snmp_la-agent.Plo \ + ./$(DEPDIR)/liblldpd_snmp_la-agent_priv.Plo \ + ./$(DEPDIR)/main.Po protocols/$(DEPDIR)/liblldpd_la-cdp.Plo \ + protocols/$(DEPDIR)/liblldpd_la-edp.Plo \ + protocols/$(DEPDIR)/liblldpd_la-lldp.Plo \ + protocols/$(DEPDIR)/liblldpd_la-sonmp.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 = $(liblldpd_snmp_la_SOURCES) $(liblldpd_la_SOURCES) \ + $(nodist_liblldpd_la_SOURCES) $(lldpd_SOURCES) +DIST_SOURCES = $(am__liblldpd_snmp_la_SOURCES_DIST) \ + $(am__liblldpd_la_SOURCES_DIST) $(lldpd_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +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; }; \ + } +man8dir = $(mandir)/man8 +NROFF = nroff +MANS = $(man_MANS) +DATA = $(apparmor_DATA) $(systemdsystemunit_DATA) $(sysusers_DATA) \ + $(tapset_DATA) +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) +BUILT_SOURCES = $(am__append_8) $(am__append_17) +CLEANFILES = $(am__append_9) $(am__append_18) $(TEMPLATES) +man_MANS = lldpd.8 +noinst_LTLIBRARIES = liblldpd.la $(am__append_14) +nodist_liblldpd_la_SOURCES = $(am__append_10) +liblldpd_la_SOURCES = frame.h frame.c lldp-tlv.h client.c priv.c \ + privsep.c privsep_io.c privsep_fd.c interfaces.c event.c \ + lldpd.c pattern.c bitmap.c probes.d trace.h protocols/lldp.c \ + protocols/cdp.c protocols/cdp.h protocols/sonmp.c \ + protocols/sonmp.h protocols/edp.c protocols/edp.h \ + $(am__append_1) $(am__append_2) $(am__append_3) \ + $(am__append_4) $(am__append_5) $(am__append_6) \ + $(am__append_7) $(am__append_11) +liblldpd_la_CFLAGS = $(AM_CFLAGS) @libevent_CFLAGS@ @libcap_CFLAGS@ \ + $(am__append_12) +liblldpd_la_CPPFLAGS = $(AM_CPPFLAGS) -DSYSCONFDIR='"$(sysconfdir)"' -DLLDPCLI_PATH='"$(sbindir)/lldpcli"' +liblldpd_la_LIBADD = $(top_builddir)/src/libcommon-daemon-client.la \ + $(top_builddir)/src/libcommon-daemon-lib.la @libevent_LIBS@ \ + @libcap_LIBS@ $(am__append_13) $(am__append_15) +lldpd_SOURCES = main.c +lldpd_LDFLAGS = $(AM_LDFLAGS) $(LLDP_BIN_LDFLAGS) +lldpd_LDADD = liblldpd.la @libevent_LDFLAGS@ $(am__append_16) \ + $(am__append_19) +@HOST_OS_OSX_TRUE@liblldpd_la_LDFLAGS = $(AM_LDFLAGS) -framework \ +@HOST_OS_OSX_TRUE@ Foundation -framework CoreFoundation \ +@HOST_OS_OSX_TRUE@ -framework IOKit -framework IOKit +@USE_SNMP_TRUE@liblldpd_snmp_la_SOURCES = agent.c agent_priv.c agent.h +@USE_SNMP_TRUE@liblldpd_snmp_la_CFLAGS = $(liblldpd_la_CFLAGS) @NETSNMP_CFLAGS@ +@USE_SNMP_TRUE@liblldpd_snmp_la_CPPFLAGS = $(liblldpd_la_CPPFLAGS) +EXTRA_DIST = dtrace2systemtap.awk lldpd.8.in lldpd.service.in \ + lldpd.sysusers.conf.in usr.sbin.lldpd.in +@ENABLE_SYSTEMTAP_TRUE@tapsetdir = $(datadir)/systemtap/tapset +@ENABLE_SYSTEMTAP_TRUE@tapset_DATA = lldpd.stp +@HAVE_SYSTEMDSYSTEMUNITDIR_TRUE@systemdsystemunit_DATA = lldpd.service +@HAVE_SYSUSERSDIR_TRUE@sysusers_DATA = lldpd.sysusers.conf +@HAVE_APPARMORDIR_TRUE@@HOST_OS_LINUX_TRUE@apparmor_DATA = usr.sbin.lldpd +TEMPLATES = lldpd.8 lldpd.service lldpd.sysusers.conf usr.sbin.lldpd +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: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) 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/daemon/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/daemon/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-sbinPROGRAMS: $(sbin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-sbinPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(sbindir)" && rm -f $$files + +clean-sbinPROGRAMS: + @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +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}; \ + } + +liblldpd-snmp.la: $(liblldpd_snmp_la_OBJECTS) $(liblldpd_snmp_la_DEPENDENCIES) $(EXTRA_liblldpd_snmp_la_DEPENDENCIES) + $(AM_V_CCLD)$(liblldpd_snmp_la_LINK) $(am_liblldpd_snmp_la_rpath) $(liblldpd_snmp_la_OBJECTS) $(liblldpd_snmp_la_LIBADD) $(LIBS) +protocols/$(am__dirstamp): + @$(MKDIR_P) protocols + @: > protocols/$(am__dirstamp) +protocols/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) protocols/$(DEPDIR) + @: > protocols/$(DEPDIR)/$(am__dirstamp) +protocols/liblldpd_la-lldp.lo: protocols/$(am__dirstamp) \ + protocols/$(DEPDIR)/$(am__dirstamp) +protocols/liblldpd_la-cdp.lo: protocols/$(am__dirstamp) \ + protocols/$(DEPDIR)/$(am__dirstamp) +protocols/liblldpd_la-sonmp.lo: protocols/$(am__dirstamp) \ + protocols/$(DEPDIR)/$(am__dirstamp) +protocols/liblldpd_la-edp.lo: protocols/$(am__dirstamp) \ + protocols/$(DEPDIR)/$(am__dirstamp) + +liblldpd.la: $(liblldpd_la_OBJECTS) $(liblldpd_la_DEPENDENCIES) $(EXTRA_liblldpd_la_DEPENDENCIES) + $(AM_V_CCLD)$(liblldpd_la_LINK) $(liblldpd_la_OBJECTS) $(liblldpd_la_LIBADD) $(LIBS) + +lldpd$(EXEEXT): $(lldpd_OBJECTS) $(lldpd_DEPENDENCIES) $(EXTRA_lldpd_DEPENDENCIES) + @rm -f lldpd$(EXEEXT) + $(AM_V_CCLD)$(lldpd_LINK) $(lldpd_OBJECTS) $(lldpd_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + -rm -f protocols/*.$(OBJEXT) + -rm -f protocols/*.lo + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblldpd_la-bitmap.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblldpd_la-client.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblldpd_la-dmi-dummy.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblldpd_la-dmi-freebsd.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblldpd_la-dmi-linux.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblldpd_la-dmi-openbsd.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblldpd_la-dmi-osx.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblldpd_la-event.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblldpd_la-forward-bsd.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblldpd_la-forward-linux.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblldpd_la-forward-solaris.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblldpd_la-frame.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblldpd_la-interfaces-bpf.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblldpd_la-interfaces-bsd.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblldpd_la-interfaces-linux.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblldpd_la-interfaces-solaris.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblldpd_la-interfaces.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblldpd_la-lldpd.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblldpd_la-netlink.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblldpd_la-pattern.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblldpd_la-priv-bsd.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblldpd_la-priv-linux.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblldpd_la-priv-seccomp.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblldpd_la-priv.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblldpd_la-privsep.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblldpd_la-privsep_fd.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblldpd_la-privsep_io.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblldpd_snmp_la-agent.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblldpd_snmp_la-agent_priv.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@protocols/$(DEPDIR)/liblldpd_la-cdp.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@protocols/$(DEPDIR)/liblldpd_la-edp.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@protocols/$(DEPDIR)/liblldpd_la-lldp.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@protocols/$(DEPDIR)/liblldpd_la-sonmp.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 $@ $< + +liblldpd_snmp_la-agent.lo: agent.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_snmp_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_snmp_la_CFLAGS) $(CFLAGS) -MT liblldpd_snmp_la-agent.lo -MD -MP -MF $(DEPDIR)/liblldpd_snmp_la-agent.Tpo -c -o liblldpd_snmp_la-agent.lo `test -f 'agent.c' || echo '$(srcdir)/'`agent.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblldpd_snmp_la-agent.Tpo $(DEPDIR)/liblldpd_snmp_la-agent.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='agent.c' object='liblldpd_snmp_la-agent.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_snmp_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_snmp_la_CFLAGS) $(CFLAGS) -c -o liblldpd_snmp_la-agent.lo `test -f 'agent.c' || echo '$(srcdir)/'`agent.c + +liblldpd_snmp_la-agent_priv.lo: agent_priv.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_snmp_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_snmp_la_CFLAGS) $(CFLAGS) -MT liblldpd_snmp_la-agent_priv.lo -MD -MP -MF $(DEPDIR)/liblldpd_snmp_la-agent_priv.Tpo -c -o liblldpd_snmp_la-agent_priv.lo `test -f 'agent_priv.c' || echo '$(srcdir)/'`agent_priv.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblldpd_snmp_la-agent_priv.Tpo $(DEPDIR)/liblldpd_snmp_la-agent_priv.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='agent_priv.c' object='liblldpd_snmp_la-agent_priv.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_snmp_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_snmp_la_CFLAGS) $(CFLAGS) -c -o liblldpd_snmp_la-agent_priv.lo `test -f 'agent_priv.c' || echo '$(srcdir)/'`agent_priv.c + +liblldpd_la-frame.lo: frame.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -MT liblldpd_la-frame.lo -MD -MP -MF $(DEPDIR)/liblldpd_la-frame.Tpo -c -o liblldpd_la-frame.lo `test -f 'frame.c' || echo '$(srcdir)/'`frame.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblldpd_la-frame.Tpo $(DEPDIR)/liblldpd_la-frame.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='frame.c' object='liblldpd_la-frame.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -c -o liblldpd_la-frame.lo `test -f 'frame.c' || echo '$(srcdir)/'`frame.c + +liblldpd_la-client.lo: client.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -MT liblldpd_la-client.lo -MD -MP -MF $(DEPDIR)/liblldpd_la-client.Tpo -c -o liblldpd_la-client.lo `test -f 'client.c' || echo '$(srcdir)/'`client.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblldpd_la-client.Tpo $(DEPDIR)/liblldpd_la-client.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='client.c' object='liblldpd_la-client.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -c -o liblldpd_la-client.lo `test -f 'client.c' || echo '$(srcdir)/'`client.c + +liblldpd_la-priv.lo: priv.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -MT liblldpd_la-priv.lo -MD -MP -MF $(DEPDIR)/liblldpd_la-priv.Tpo -c -o liblldpd_la-priv.lo `test -f 'priv.c' || echo '$(srcdir)/'`priv.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblldpd_la-priv.Tpo $(DEPDIR)/liblldpd_la-priv.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='priv.c' object='liblldpd_la-priv.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -c -o liblldpd_la-priv.lo `test -f 'priv.c' || echo '$(srcdir)/'`priv.c + +liblldpd_la-privsep.lo: privsep.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -MT liblldpd_la-privsep.lo -MD -MP -MF $(DEPDIR)/liblldpd_la-privsep.Tpo -c -o liblldpd_la-privsep.lo `test -f 'privsep.c' || echo '$(srcdir)/'`privsep.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblldpd_la-privsep.Tpo $(DEPDIR)/liblldpd_la-privsep.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='privsep.c' object='liblldpd_la-privsep.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -c -o liblldpd_la-privsep.lo `test -f 'privsep.c' || echo '$(srcdir)/'`privsep.c + +liblldpd_la-privsep_io.lo: privsep_io.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -MT liblldpd_la-privsep_io.lo -MD -MP -MF $(DEPDIR)/liblldpd_la-privsep_io.Tpo -c -o liblldpd_la-privsep_io.lo `test -f 'privsep_io.c' || echo '$(srcdir)/'`privsep_io.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblldpd_la-privsep_io.Tpo $(DEPDIR)/liblldpd_la-privsep_io.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='privsep_io.c' object='liblldpd_la-privsep_io.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -c -o liblldpd_la-privsep_io.lo `test -f 'privsep_io.c' || echo '$(srcdir)/'`privsep_io.c + +liblldpd_la-privsep_fd.lo: privsep_fd.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -MT liblldpd_la-privsep_fd.lo -MD -MP -MF $(DEPDIR)/liblldpd_la-privsep_fd.Tpo -c -o liblldpd_la-privsep_fd.lo `test -f 'privsep_fd.c' || echo '$(srcdir)/'`privsep_fd.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblldpd_la-privsep_fd.Tpo $(DEPDIR)/liblldpd_la-privsep_fd.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='privsep_fd.c' object='liblldpd_la-privsep_fd.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -c -o liblldpd_la-privsep_fd.lo `test -f 'privsep_fd.c' || echo '$(srcdir)/'`privsep_fd.c + +liblldpd_la-interfaces.lo: interfaces.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -MT liblldpd_la-interfaces.lo -MD -MP -MF $(DEPDIR)/liblldpd_la-interfaces.Tpo -c -o liblldpd_la-interfaces.lo `test -f 'interfaces.c' || echo '$(srcdir)/'`interfaces.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblldpd_la-interfaces.Tpo $(DEPDIR)/liblldpd_la-interfaces.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='interfaces.c' object='liblldpd_la-interfaces.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -c -o liblldpd_la-interfaces.lo `test -f 'interfaces.c' || echo '$(srcdir)/'`interfaces.c + +liblldpd_la-event.lo: event.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -MT liblldpd_la-event.lo -MD -MP -MF $(DEPDIR)/liblldpd_la-event.Tpo -c -o liblldpd_la-event.lo `test -f 'event.c' || echo '$(srcdir)/'`event.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblldpd_la-event.Tpo $(DEPDIR)/liblldpd_la-event.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='event.c' object='liblldpd_la-event.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -c -o liblldpd_la-event.lo `test -f 'event.c' || echo '$(srcdir)/'`event.c + +liblldpd_la-lldpd.lo: lldpd.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -MT liblldpd_la-lldpd.lo -MD -MP -MF $(DEPDIR)/liblldpd_la-lldpd.Tpo -c -o liblldpd_la-lldpd.lo `test -f 'lldpd.c' || echo '$(srcdir)/'`lldpd.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblldpd_la-lldpd.Tpo $(DEPDIR)/liblldpd_la-lldpd.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='lldpd.c' object='liblldpd_la-lldpd.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -c -o liblldpd_la-lldpd.lo `test -f 'lldpd.c' || echo '$(srcdir)/'`lldpd.c + +liblldpd_la-pattern.lo: pattern.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -MT liblldpd_la-pattern.lo -MD -MP -MF $(DEPDIR)/liblldpd_la-pattern.Tpo -c -o liblldpd_la-pattern.lo `test -f 'pattern.c' || echo '$(srcdir)/'`pattern.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblldpd_la-pattern.Tpo $(DEPDIR)/liblldpd_la-pattern.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pattern.c' object='liblldpd_la-pattern.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -c -o liblldpd_la-pattern.lo `test -f 'pattern.c' || echo '$(srcdir)/'`pattern.c + +liblldpd_la-bitmap.lo: bitmap.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -MT liblldpd_la-bitmap.lo -MD -MP -MF $(DEPDIR)/liblldpd_la-bitmap.Tpo -c -o liblldpd_la-bitmap.lo `test -f 'bitmap.c' || echo '$(srcdir)/'`bitmap.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblldpd_la-bitmap.Tpo $(DEPDIR)/liblldpd_la-bitmap.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='bitmap.c' object='liblldpd_la-bitmap.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -c -o liblldpd_la-bitmap.lo `test -f 'bitmap.c' || echo '$(srcdir)/'`bitmap.c + +protocols/liblldpd_la-lldp.lo: protocols/lldp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -MT protocols/liblldpd_la-lldp.lo -MD -MP -MF protocols/$(DEPDIR)/liblldpd_la-lldp.Tpo -c -o protocols/liblldpd_la-lldp.lo `test -f 'protocols/lldp.c' || echo '$(srcdir)/'`protocols/lldp.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) protocols/$(DEPDIR)/liblldpd_la-lldp.Tpo protocols/$(DEPDIR)/liblldpd_la-lldp.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='protocols/lldp.c' object='protocols/liblldpd_la-lldp.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -c -o protocols/liblldpd_la-lldp.lo `test -f 'protocols/lldp.c' || echo '$(srcdir)/'`protocols/lldp.c + +protocols/liblldpd_la-cdp.lo: protocols/cdp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -MT protocols/liblldpd_la-cdp.lo -MD -MP -MF protocols/$(DEPDIR)/liblldpd_la-cdp.Tpo -c -o protocols/liblldpd_la-cdp.lo `test -f 'protocols/cdp.c' || echo '$(srcdir)/'`protocols/cdp.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) protocols/$(DEPDIR)/liblldpd_la-cdp.Tpo protocols/$(DEPDIR)/liblldpd_la-cdp.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='protocols/cdp.c' object='protocols/liblldpd_la-cdp.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -c -o protocols/liblldpd_la-cdp.lo `test -f 'protocols/cdp.c' || echo '$(srcdir)/'`protocols/cdp.c + +protocols/liblldpd_la-sonmp.lo: protocols/sonmp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -MT protocols/liblldpd_la-sonmp.lo -MD -MP -MF protocols/$(DEPDIR)/liblldpd_la-sonmp.Tpo -c -o protocols/liblldpd_la-sonmp.lo `test -f 'protocols/sonmp.c' || echo '$(srcdir)/'`protocols/sonmp.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) protocols/$(DEPDIR)/liblldpd_la-sonmp.Tpo protocols/$(DEPDIR)/liblldpd_la-sonmp.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='protocols/sonmp.c' object='protocols/liblldpd_la-sonmp.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -c -o protocols/liblldpd_la-sonmp.lo `test -f 'protocols/sonmp.c' || echo '$(srcdir)/'`protocols/sonmp.c + +protocols/liblldpd_la-edp.lo: protocols/edp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -MT protocols/liblldpd_la-edp.lo -MD -MP -MF protocols/$(DEPDIR)/liblldpd_la-edp.Tpo -c -o protocols/liblldpd_la-edp.lo `test -f 'protocols/edp.c' || echo '$(srcdir)/'`protocols/edp.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) protocols/$(DEPDIR)/liblldpd_la-edp.Tpo protocols/$(DEPDIR)/liblldpd_la-edp.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='protocols/edp.c' object='protocols/liblldpd_la-edp.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -c -o protocols/liblldpd_la-edp.lo `test -f 'protocols/edp.c' || echo '$(srcdir)/'`protocols/edp.c + +liblldpd_la-forward-linux.lo: forward-linux.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -MT liblldpd_la-forward-linux.lo -MD -MP -MF $(DEPDIR)/liblldpd_la-forward-linux.Tpo -c -o liblldpd_la-forward-linux.lo `test -f 'forward-linux.c' || echo '$(srcdir)/'`forward-linux.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblldpd_la-forward-linux.Tpo $(DEPDIR)/liblldpd_la-forward-linux.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='forward-linux.c' object='liblldpd_la-forward-linux.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -c -o liblldpd_la-forward-linux.lo `test -f 'forward-linux.c' || echo '$(srcdir)/'`forward-linux.c + +liblldpd_la-interfaces-linux.lo: interfaces-linux.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -MT liblldpd_la-interfaces-linux.lo -MD -MP -MF $(DEPDIR)/liblldpd_la-interfaces-linux.Tpo -c -o liblldpd_la-interfaces-linux.lo `test -f 'interfaces-linux.c' || echo '$(srcdir)/'`interfaces-linux.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblldpd_la-interfaces-linux.Tpo $(DEPDIR)/liblldpd_la-interfaces-linux.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='interfaces-linux.c' object='liblldpd_la-interfaces-linux.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -c -o liblldpd_la-interfaces-linux.lo `test -f 'interfaces-linux.c' || echo '$(srcdir)/'`interfaces-linux.c + +liblldpd_la-netlink.lo: netlink.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -MT liblldpd_la-netlink.lo -MD -MP -MF $(DEPDIR)/liblldpd_la-netlink.Tpo -c -o liblldpd_la-netlink.lo `test -f 'netlink.c' || echo '$(srcdir)/'`netlink.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblldpd_la-netlink.Tpo $(DEPDIR)/liblldpd_la-netlink.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='netlink.c' object='liblldpd_la-netlink.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -c -o liblldpd_la-netlink.lo `test -f 'netlink.c' || echo '$(srcdir)/'`netlink.c + +liblldpd_la-dmi-linux.lo: dmi-linux.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -MT liblldpd_la-dmi-linux.lo -MD -MP -MF $(DEPDIR)/liblldpd_la-dmi-linux.Tpo -c -o liblldpd_la-dmi-linux.lo `test -f 'dmi-linux.c' || echo '$(srcdir)/'`dmi-linux.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblldpd_la-dmi-linux.Tpo $(DEPDIR)/liblldpd_la-dmi-linux.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dmi-linux.c' object='liblldpd_la-dmi-linux.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -c -o liblldpd_la-dmi-linux.lo `test -f 'dmi-linux.c' || echo '$(srcdir)/'`dmi-linux.c + +liblldpd_la-priv-linux.lo: priv-linux.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -MT liblldpd_la-priv-linux.lo -MD -MP -MF $(DEPDIR)/liblldpd_la-priv-linux.Tpo -c -o liblldpd_la-priv-linux.lo `test -f 'priv-linux.c' || echo '$(srcdir)/'`priv-linux.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblldpd_la-priv-linux.Tpo $(DEPDIR)/liblldpd_la-priv-linux.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='priv-linux.c' object='liblldpd_la-priv-linux.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -c -o liblldpd_la-priv-linux.lo `test -f 'priv-linux.c' || echo '$(srcdir)/'`priv-linux.c + +liblldpd_la-forward-bsd.lo: forward-bsd.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -MT liblldpd_la-forward-bsd.lo -MD -MP -MF $(DEPDIR)/liblldpd_la-forward-bsd.Tpo -c -o liblldpd_la-forward-bsd.lo `test -f 'forward-bsd.c' || echo '$(srcdir)/'`forward-bsd.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblldpd_la-forward-bsd.Tpo $(DEPDIR)/liblldpd_la-forward-bsd.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='forward-bsd.c' object='liblldpd_la-forward-bsd.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -c -o liblldpd_la-forward-bsd.lo `test -f 'forward-bsd.c' || echo '$(srcdir)/'`forward-bsd.c + +liblldpd_la-interfaces-bpf.lo: interfaces-bpf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -MT liblldpd_la-interfaces-bpf.lo -MD -MP -MF $(DEPDIR)/liblldpd_la-interfaces-bpf.Tpo -c -o liblldpd_la-interfaces-bpf.lo `test -f 'interfaces-bpf.c' || echo '$(srcdir)/'`interfaces-bpf.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblldpd_la-interfaces-bpf.Tpo $(DEPDIR)/liblldpd_la-interfaces-bpf.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='interfaces-bpf.c' object='liblldpd_la-interfaces-bpf.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -c -o liblldpd_la-interfaces-bpf.lo `test -f 'interfaces-bpf.c' || echo '$(srcdir)/'`interfaces-bpf.c + +liblldpd_la-interfaces-bsd.lo: interfaces-bsd.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -MT liblldpd_la-interfaces-bsd.lo -MD -MP -MF $(DEPDIR)/liblldpd_la-interfaces-bsd.Tpo -c -o liblldpd_la-interfaces-bsd.lo `test -f 'interfaces-bsd.c' || echo '$(srcdir)/'`interfaces-bsd.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblldpd_la-interfaces-bsd.Tpo $(DEPDIR)/liblldpd_la-interfaces-bsd.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='interfaces-bsd.c' object='liblldpd_la-interfaces-bsd.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -c -o liblldpd_la-interfaces-bsd.lo `test -f 'interfaces-bsd.c' || echo '$(srcdir)/'`interfaces-bsd.c + +liblldpd_la-dmi-dummy.lo: dmi-dummy.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -MT liblldpd_la-dmi-dummy.lo -MD -MP -MF $(DEPDIR)/liblldpd_la-dmi-dummy.Tpo -c -o liblldpd_la-dmi-dummy.lo `test -f 'dmi-dummy.c' || echo '$(srcdir)/'`dmi-dummy.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblldpd_la-dmi-dummy.Tpo $(DEPDIR)/liblldpd_la-dmi-dummy.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dmi-dummy.c' object='liblldpd_la-dmi-dummy.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -c -o liblldpd_la-dmi-dummy.lo `test -f 'dmi-dummy.c' || echo '$(srcdir)/'`dmi-dummy.c + +liblldpd_la-priv-bsd.lo: priv-bsd.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -MT liblldpd_la-priv-bsd.lo -MD -MP -MF $(DEPDIR)/liblldpd_la-priv-bsd.Tpo -c -o liblldpd_la-priv-bsd.lo `test -f 'priv-bsd.c' || echo '$(srcdir)/'`priv-bsd.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblldpd_la-priv-bsd.Tpo $(DEPDIR)/liblldpd_la-priv-bsd.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='priv-bsd.c' object='liblldpd_la-priv-bsd.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -c -o liblldpd_la-priv-bsd.lo `test -f 'priv-bsd.c' || echo '$(srcdir)/'`priv-bsd.c + +liblldpd_la-dmi-freebsd.lo: dmi-freebsd.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -MT liblldpd_la-dmi-freebsd.lo -MD -MP -MF $(DEPDIR)/liblldpd_la-dmi-freebsd.Tpo -c -o liblldpd_la-dmi-freebsd.lo `test -f 'dmi-freebsd.c' || echo '$(srcdir)/'`dmi-freebsd.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblldpd_la-dmi-freebsd.Tpo $(DEPDIR)/liblldpd_la-dmi-freebsd.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dmi-freebsd.c' object='liblldpd_la-dmi-freebsd.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -c -o liblldpd_la-dmi-freebsd.lo `test -f 'dmi-freebsd.c' || echo '$(srcdir)/'`dmi-freebsd.c + +liblldpd_la-dmi-openbsd.lo: dmi-openbsd.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -MT liblldpd_la-dmi-openbsd.lo -MD -MP -MF $(DEPDIR)/liblldpd_la-dmi-openbsd.Tpo -c -o liblldpd_la-dmi-openbsd.lo `test -f 'dmi-openbsd.c' || echo '$(srcdir)/'`dmi-openbsd.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblldpd_la-dmi-openbsd.Tpo $(DEPDIR)/liblldpd_la-dmi-openbsd.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dmi-openbsd.c' object='liblldpd_la-dmi-openbsd.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -c -o liblldpd_la-dmi-openbsd.lo `test -f 'dmi-openbsd.c' || echo '$(srcdir)/'`dmi-openbsd.c + +liblldpd_la-dmi-osx.lo: dmi-osx.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -MT liblldpd_la-dmi-osx.lo -MD -MP -MF $(DEPDIR)/liblldpd_la-dmi-osx.Tpo -c -o liblldpd_la-dmi-osx.lo `test -f 'dmi-osx.c' || echo '$(srcdir)/'`dmi-osx.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblldpd_la-dmi-osx.Tpo $(DEPDIR)/liblldpd_la-dmi-osx.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dmi-osx.c' object='liblldpd_la-dmi-osx.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -c -o liblldpd_la-dmi-osx.lo `test -f 'dmi-osx.c' || echo '$(srcdir)/'`dmi-osx.c + +liblldpd_la-forward-solaris.lo: forward-solaris.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -MT liblldpd_la-forward-solaris.lo -MD -MP -MF $(DEPDIR)/liblldpd_la-forward-solaris.Tpo -c -o liblldpd_la-forward-solaris.lo `test -f 'forward-solaris.c' || echo '$(srcdir)/'`forward-solaris.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblldpd_la-forward-solaris.Tpo $(DEPDIR)/liblldpd_la-forward-solaris.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='forward-solaris.c' object='liblldpd_la-forward-solaris.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -c -o liblldpd_la-forward-solaris.lo `test -f 'forward-solaris.c' || echo '$(srcdir)/'`forward-solaris.c + +liblldpd_la-interfaces-solaris.lo: interfaces-solaris.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -MT liblldpd_la-interfaces-solaris.lo -MD -MP -MF $(DEPDIR)/liblldpd_la-interfaces-solaris.Tpo -c -o liblldpd_la-interfaces-solaris.lo `test -f 'interfaces-solaris.c' || echo '$(srcdir)/'`interfaces-solaris.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblldpd_la-interfaces-solaris.Tpo $(DEPDIR)/liblldpd_la-interfaces-solaris.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='interfaces-solaris.c' object='liblldpd_la-interfaces-solaris.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -c -o liblldpd_la-interfaces-solaris.lo `test -f 'interfaces-solaris.c' || echo '$(srcdir)/'`interfaces-solaris.c + +liblldpd_la-priv-seccomp.lo: priv-seccomp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -MT liblldpd_la-priv-seccomp.lo -MD -MP -MF $(DEPDIR)/liblldpd_la-priv-seccomp.Tpo -c -o liblldpd_la-priv-seccomp.lo `test -f 'priv-seccomp.c' || echo '$(srcdir)/'`priv-seccomp.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblldpd_la-priv-seccomp.Tpo $(DEPDIR)/liblldpd_la-priv-seccomp.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='priv-seccomp.c' object='liblldpd_la-priv-seccomp.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblldpd_la_CPPFLAGS) $(CPPFLAGS) $(liblldpd_la_CFLAGS) $(CFLAGS) -c -o liblldpd_la-priv-seccomp.lo `test -f 'priv-seccomp.c' || echo '$(srcdir)/'`priv-seccomp.c + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + -rm -rf protocols/.libs protocols/_libs +install-man8: $(man_MANS) + @$(NORMAL_INSTALL) + @list1=''; \ + list2='$(man_MANS)'; \ + test -n "$(man8dir)" \ + && test -n "`echo $$list1$$list2`" \ + || exit 0; \ + echo " $(MKDIR_P) '$(DESTDIR)$(man8dir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(man8dir)" || exit 1; \ + { for i in $$list1; do echo "$$i"; done; \ + if test -n "$$list2"; then \ + for i in $$list2; do echo "$$i"; done \ + | sed -n '/\.8[a-z]*$$/p'; \ + fi; \ + } | while read p; do \ + if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; echo "$$p"; \ + done | \ + sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \ + -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ + sed 'N;N;s,\n, ,g' | { \ + list=; while read file base inst; do \ + if test "$$base" = "$$inst"; then list="$$list $$file"; else \ + echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \ + $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst" || exit $$?; \ + fi; \ + done; \ + for i in $$list; do echo "$$i"; done | $(am__base_list) | \ + while read files; do \ + test -z "$$files" || { \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man8dir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(man8dir)" || exit $$?; }; \ + done; } + +uninstall-man8: + @$(NORMAL_UNINSTALL) + @list=''; test -n "$(man8dir)" || exit 0; \ + files=`{ for i in $$list; do echo "$$i"; done; \ + l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \ + sed -n '/\.8[a-z]*$$/p'; \ + } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \ + -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ + dir='$(DESTDIR)$(man8dir)'; $(am__uninstall_files_from_dir) +install-apparmorDATA: $(apparmor_DATA) + @$(NORMAL_INSTALL) + @list='$(apparmor_DATA)'; test -n "$(apparmordir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(apparmordir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(apparmordir)" || 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)$(apparmordir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(apparmordir)" || exit $$?; \ + done + +uninstall-apparmorDATA: + @$(NORMAL_UNINSTALL) + @list='$(apparmor_DATA)'; test -n "$(apparmordir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(apparmordir)'; $(am__uninstall_files_from_dir) +install-systemdsystemunitDATA: $(systemdsystemunit_DATA) + @$(NORMAL_INSTALL) + @list='$(systemdsystemunit_DATA)'; test -n "$(systemdsystemunitdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(systemdsystemunitdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(systemdsystemunitdir)" || 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)$(systemdsystemunitdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(systemdsystemunitdir)" || exit $$?; \ + done + +uninstall-systemdsystemunitDATA: + @$(NORMAL_UNINSTALL) + @list='$(systemdsystemunit_DATA)'; test -n "$(systemdsystemunitdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(systemdsystemunitdir)'; $(am__uninstall_files_from_dir) +install-sysusersDATA: $(sysusers_DATA) + @$(NORMAL_INSTALL) + @list='$(sysusers_DATA)'; test -n "$(sysusersdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(sysusersdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(sysusersdir)" || 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)$(sysusersdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(sysusersdir)" || exit $$?; \ + done + +uninstall-sysusersDATA: + @$(NORMAL_UNINSTALL) + @list='$(sysusers_DATA)'; test -n "$(sysusersdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(sysusersdir)'; $(am__uninstall_files_from_dir) +install-tapsetDATA: $(tapset_DATA) + @$(NORMAL_INSTALL) + @list='$(tapset_DATA)'; test -n "$(tapsetdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(tapsetdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(tapsetdir)" || 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)$(tapsetdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(tapsetdir)" || exit $$?; \ + done + +uninstall-tapsetDATA: + @$(NORMAL_UNINSTALL) + @list='$(tapset_DATA)'; test -n "$(tapsetdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(tapsetdir)'; $(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: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) check-am +all-am: Makefile $(PROGRAMS) $(LTLIBRARIES) $(MANS) $(DATA) +installdirs: + for dir in "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)" "$(DESTDIR)$(apparmordir)" "$(DESTDIR)$(systemdsystemunitdir)" "$(DESTDIR)$(sysusersdir)" "$(DESTDIR)$(tapsetdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) install-am +install-exec: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) 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 protocols/$(DEPDIR)/$(am__dirstamp) + -rm -f protocols/$(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." + -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + clean-sbinPROGRAMS mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/liblldpd_la-bitmap.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-client.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-dmi-dummy.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-dmi-freebsd.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-dmi-linux.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-dmi-openbsd.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-dmi-osx.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-event.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-forward-bsd.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-forward-linux.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-forward-solaris.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-frame.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-interfaces-bpf.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-interfaces-bsd.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-interfaces-linux.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-interfaces-solaris.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-interfaces.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-lldpd.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-netlink.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-pattern.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-priv-bsd.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-priv-linux.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-priv-seccomp.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-priv.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-privsep.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-privsep_fd.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-privsep_io.Plo + -rm -f ./$(DEPDIR)/liblldpd_snmp_la-agent.Plo + -rm -f ./$(DEPDIR)/liblldpd_snmp_la-agent_priv.Plo + -rm -f ./$(DEPDIR)/main.Po + -rm -f protocols/$(DEPDIR)/liblldpd_la-cdp.Plo + -rm -f protocols/$(DEPDIR)/liblldpd_la-edp.Plo + -rm -f protocols/$(DEPDIR)/liblldpd_la-lldp.Plo + -rm -f protocols/$(DEPDIR)/liblldpd_la-sonmp.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-apparmorDATA install-man \ + install-systemdsystemunitDATA install-sysusersDATA \ + install-tapsetDATA + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-sbinPROGRAMS + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: install-man8 + +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)/liblldpd_la-bitmap.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-client.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-dmi-dummy.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-dmi-freebsd.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-dmi-linux.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-dmi-openbsd.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-dmi-osx.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-event.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-forward-bsd.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-forward-linux.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-forward-solaris.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-frame.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-interfaces-bpf.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-interfaces-bsd.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-interfaces-linux.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-interfaces-solaris.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-interfaces.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-lldpd.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-netlink.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-pattern.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-priv-bsd.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-priv-linux.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-priv-seccomp.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-priv.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-privsep.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-privsep_fd.Plo + -rm -f ./$(DEPDIR)/liblldpd_la-privsep_io.Plo + -rm -f ./$(DEPDIR)/liblldpd_snmp_la-agent.Plo + -rm -f ./$(DEPDIR)/liblldpd_snmp_la-agent_priv.Plo + -rm -f ./$(DEPDIR)/main.Po + -rm -f protocols/$(DEPDIR)/liblldpd_la-cdp.Plo + -rm -f protocols/$(DEPDIR)/liblldpd_la-edp.Plo + -rm -f protocols/$(DEPDIR)/liblldpd_la-lldp.Plo + -rm -f protocols/$(DEPDIR)/liblldpd_la-sonmp.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-apparmorDATA uninstall-man \ + uninstall-sbinPROGRAMS uninstall-systemdsystemunitDATA \ + uninstall-sysusersDATA uninstall-tapsetDATA + +uninstall-man: uninstall-man8 + +.MAKE: all check install install-am install-exec install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-generic clean-libtool clean-noinstLTLIBRARIES \ + clean-sbinPROGRAMS 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-apparmorDATA install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-man8 install-pdf \ + install-pdf-am install-ps install-ps-am install-sbinPROGRAMS \ + install-strip install-systemdsystemunitDATA \ + install-sysusersDATA install-tapsetDATA 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-apparmorDATA \ + uninstall-man uninstall-man8 uninstall-sbinPROGRAMS \ + uninstall-systemdsystemunitDATA uninstall-sysusersDATA \ + uninstall-tapsetDATA + +.PRECIOUS: Makefile + +@USE_SECCOMP_TRUE@syscall-names.h: +@USE_SECCOMP_TRUE@ $(AM_V_GEN) +@USE_SECCOMP_TRUE@ $(AM_V_at)echo "#include <sys/syscall.h>" | $(CPP) -dM - > $@.tmp ;\ +@USE_SECCOMP_TRUE@ echo "static const char *syscall_names[] = {" > $@ ;\ +@USE_SECCOMP_TRUE@ grep '^#define __NR_' $@.tmp | \ +@USE_SECCOMP_TRUE@ LC_ALL=C sed -r -n -e 's/^\#define[ \t]+__NR_([a-z0-9_]+)[ \t]+([0-9]+)(.*)/ [\2] = "\1",/p' >> $@ ;\ +@USE_SECCOMP_TRUE@ echo "};" >> $@ ;\ +@USE_SECCOMP_TRUE@ rm $@.tmp +@ENABLE_SYSTEMTAP_TRUE@probes.h: probes.d +@ENABLE_SYSTEMTAP_TRUE@ $(AM_V_GEN) +@ENABLE_SYSTEMTAP_TRUE@ $(AM_V_at)$(DTRACE) -C -h -s $< -o $@ +@ENABLE_SYSTEMTAP_TRUE@probes.o: probes.d +@ENABLE_SYSTEMTAP_TRUE@ $(AM_V_GEN) +@ENABLE_SYSTEMTAP_TRUE@ $(AM_V_at)$(DTRACE) -C -G -s $< -o $@ + +@ENABLE_SYSTEMTAP_TRUE@lldpd.stp: probes.d $(srcdir)/dtrace2systemtap.awk $(top_builddir)/config.status +@ENABLE_SYSTEMTAP_TRUE@ $(AM_V_GEN)$(AWK) -f $(srcdir)/dtrace2systemtap.awk -v sbindir=$(sbindir) $< > $@ || ( rm -f $@ ; exit 1 ) + +@LIBEVENT_EMBEDDED_TRUE@event.c: $(top_builddir)/libevent/libevent.la +@LIBEVENT_EMBEDDED_TRUE@$(top_builddir)/libevent/libevent.la: $(top_srcdir)/libevent/*.c $(top_srcdir)/libevent/*.h +@LIBEVENT_EMBEDDED_TRUE@ (cd $(top_builddir)/libevent && $(MAKE)) +lldpd.8: lldpd.8.in +lldpd.service: lldpd.service.in +lldpd.sysusers.conf: lldpd.sysusers.conf.in +usr.sbin.lldpd: usr.sbin.lldpd.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/daemon/agent.c b/src/daemon/agent.c new file mode 100644 index 0000000..cd631a8 --- /dev/null +++ b/src/daemon/agent.c @@ -0,0 +1,1939 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "lldpd.h" + +#include <assert.h> + +#include "agent.h" + +#if HAVE_NET_SNMP_AGENT_UTIL_FUNCS_H +# include <net-snmp/agent/util_funcs.h> +#else +/* The above header may be buggy. We just need this function. */ +int header_generic(struct variable *, oid *, size_t *, int, size_t *, WriteMethod **); +#endif + +/* For net-snmp */ +extern int register_sysORTable(oid *, size_t, const char *); +extern int unregister_sysORTable(oid *, size_t); + +/* Global variable because no way to pass it as argument. Should not be used + * elsewhere. */ +#define scfg agent_scfg +struct lldpd *agent_scfg; + +static uint8_t +swap_bits(uint8_t n) +{ + n = ((n & 0xF0) >> 4) | ((n & 0x0F) << 4); + n = ((n & 0xCC) >> 2) | ((n & 0x33) << 2); + n = ((n & 0xAA) >> 1) | ((n & 0x55) << 1); + + return n; +}; + +extern struct timeval starttime; +static long int +lastchange(struct lldpd_port *port) +{ + if (port->p_lastchange > starttime.tv_sec) + return (port->p_lastchange - starttime.tv_sec) * 100; + return 0; +} + +/* ------------- + Helper functions to build header_*indexed_table() functions. + Those functions keep an internal state. They are not reentrant! +*/ +struct header_index { + struct variable *vp; + oid *name; /* Requested/returned OID */ + size_t *length; /* Length of above OID */ + int exact; + oid best[MAX_OID_LEN]; /* Best OID */ + size_t best_len; /* Best OID length */ + void *entity; /* Best entity */ +}; +static struct header_index header_idx; + +static int +header_index_init(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + /* If the requested OID name is less than OID prefix we + handle, adjust it to our prefix. */ + if ((snmp_oid_compare(name, *length, vp->name, vp->namelen)) < 0) { + memcpy(name, vp->name, sizeof(oid) * vp->namelen); + *length = vp->namelen; + } + /* Now, we can only handle OID matching our prefix. Those two + tests are not really necessary since NetSNMP won't give us + OID "above" our prefix. But this makes unit tests + easier. */ + if (*length < vp->namelen) return 0; + if (memcmp(name, vp->name, vp->namelen * sizeof(oid))) return 0; + + if (write_method != NULL) *write_method = 0; + *var_len = sizeof(long); + + /* Initialize our header index structure */ + header_idx.vp = vp; + header_idx.name = name; + header_idx.length = length; + header_idx.exact = exact; + header_idx.best_len = 0; + header_idx.entity = NULL; + return 1; +} + +static int +header_index_add(oid *index, size_t len, void *entity) +{ + int result; + oid *target; + size_t target_len; + + target = header_idx.name + header_idx.vp->namelen; + target_len = *header_idx.length - header_idx.vp->namelen; + if ((result = snmp_oid_compare(index, len, target, target_len)) < 0) + return 0; /* Too small. */ + if (result == 0) return header_idx.exact; + if (header_idx.best_len == 0 || + (snmp_oid_compare(index, len, header_idx.best, header_idx.best_len) < 0)) { + memcpy(header_idx.best, index, sizeof(oid) * len); + header_idx.best_len = len; + header_idx.entity = entity; + } + return 0; /* No best match yet. */ +} + +static void * +header_index_best() +{ + if (header_idx.entity == NULL) return NULL; + if (header_idx.exact) return NULL; + memcpy(header_idx.name + header_idx.vp->namelen, header_idx.best, + sizeof(oid) * header_idx.best_len); + *header_idx.length = header_idx.vp->namelen + header_idx.best_len; + return header_idx.entity; +} +/* ----------------------------- */ + +static struct lldpd_hardware * +header_portindexed_table(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_hardware *hardware; + + if (!header_index_init(vp, name, length, exact, var_len, write_method)) + return NULL; + TAILQ_FOREACH (hardware, &scfg->g_hardware, h_entries) { + oid index[1] = { hardware->h_ifindex }; + if (header_index_add(index, 1, hardware)) return hardware; + } + return header_index_best(); +} + +#ifdef ENABLE_LLDPMED +static struct lldpd_med_policy * +header_pmedindexed_policy_table(struct variable *vp, oid *name, size_t *length, + int exact, size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_hardware *hardware; + int i; + oid index[2]; + + if (!header_index_init(vp, name, length, exact, var_len, write_method)) + return NULL; + TAILQ_FOREACH (hardware, &scfg->g_hardware, h_entries) { + for (i = 0; i < LLDP_MED_APPTYPE_LAST; i++) { + if (hardware->h_lport.p_med_policy[i].type != i + 1) continue; + index[0] = hardware->h_ifindex; + index[1] = i + 1; + if (header_index_add(index, 2, + &hardware->h_lport.p_med_policy[i])) + return &hardware->h_lport.p_med_policy[i]; + } + } + return header_index_best(); +} + +static struct lldpd_med_loc * +header_pmedindexed_location_table(struct variable *vp, oid *name, size_t *length, + int exact, size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_hardware *hardware; + int i; + oid index[2]; + + if (!header_index_init(vp, name, length, exact, var_len, write_method)) + return NULL; + TAILQ_FOREACH (hardware, &scfg->g_hardware, h_entries) { + for (i = 0; i < LLDP_MED_LOCFORMAT_LAST; i++) { + if (hardware->h_lport.p_med_location[i].format != i + 1) + continue; + index[0] = hardware->h_ifindex; + index[1] = i + 2; + if (header_index_add(index, 2, + &hardware->h_lport.p_med_location[i])) + return &hardware->h_lport.p_med_location[i]; + } + } + return header_index_best(); +} +#endif + +static struct lldpd_port * +header_tprindexed_table(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method, int withmed) +{ + struct lldpd_hardware *hardware; + struct lldpd_port *port; + oid index[3]; + + if (!header_index_init(vp, name, length, exact, var_len, write_method)) + return NULL; + TAILQ_FOREACH (hardware, &scfg->g_hardware, h_entries) { + TAILQ_FOREACH (port, &hardware->h_rports, p_entries) { + if (SMART_HIDDEN(port)) continue; +#ifdef ENABLE_LLDPMED + if (withmed && !port->p_chassis->c_med_cap_available) continue; +#endif + index[0] = lastchange(port); + index[1] = hardware->h_ifindex; + index[2] = port->p_chassis->c_index; + if (header_index_add(index, 3, port)) return port; + } + } + return header_index_best(); +} + +static struct lldpd_mgmt * +header_ipindexed_table(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_chassis *chassis = LOCAL_CHASSIS(scfg); + struct lldpd_mgmt *mgmt; + oid index[2 + 16]; + + if (!header_index_init(vp, name, length, exact, var_len, write_method)) + return NULL; + TAILQ_FOREACH (mgmt, &chassis->c_mgmt, m_entries) { + int i; + switch (mgmt->m_family) { + case LLDPD_AF_IPV4: + index[0] = 1; + break; + case LLDPD_AF_IPV6: + index[0] = 2; + break; + default: + assert(0); + } + index[1] = mgmt->m_addrsize; + if (index[1] > sizeof(index) - 2) continue; /* Odd... */ + for (i = 0; i < index[1]; i++) + index[i + 2] = mgmt->m_addr.octets[i]; + if (header_index_add(index, 2 + index[1], mgmt)) return mgmt; + } + + return header_index_best(); +} + +static struct lldpd_mgmt * +header_tpripindexed_table(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_hardware *hardware; + struct lldpd_port *port; + struct lldpd_mgmt *mgmt; + oid index[5 + 16]; + + if (!header_index_init(vp, name, length, exact, var_len, write_method)) + return NULL; + TAILQ_FOREACH (hardware, &scfg->g_hardware, h_entries) { + TAILQ_FOREACH (port, &hardware->h_rports, p_entries) { + if (SMART_HIDDEN(port)) continue; + TAILQ_FOREACH (mgmt, &port->p_chassis->c_mgmt, m_entries) { + int i; + index[0] = lastchange(port); + index[1] = hardware->h_ifindex; + index[2] = port->p_chassis->c_index; + switch (mgmt->m_family) { + case LLDPD_AF_IPV4: + index[3] = 1; + break; + case LLDPD_AF_IPV6: + index[3] = 2; + break; + default: + assert(0); + } + index[4] = mgmt->m_addrsize; + if (index[4] > sizeof(index) - 5) continue; /* Odd... */ + for (i = 0; i < index[4]; i++) + index[i + 5] = mgmt->m_addr.octets[i]; + if (header_index_add(index, 5 + index[4], mgmt)) + return mgmt; + } + } + } + return header_index_best(); +} + +#ifdef ENABLE_CUSTOM +static struct lldpd_custom * +header_tprcustomindexed_table(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_hardware *hardware; + struct lldpd_port *port; + struct lldpd_custom *custom; + oid index[8]; + oid idx; + + if (!header_index_init(vp, name, length, exact, var_len, write_method)) + return NULL; + TAILQ_FOREACH (hardware, &scfg->g_hardware, h_entries) { + TAILQ_FOREACH (port, &hardware->h_rports, p_entries) { + if (SMART_HIDDEN(port)) continue; + idx = 1; + TAILQ_FOREACH (custom, &port->p_custom_list, next) { + index[0] = lastchange(port); + index[1] = hardware->h_ifindex; + index[2] = port->p_chassis->c_index; + index[3] = custom->oui[0]; + index[4] = custom->oui[1]; + index[5] = custom->oui[2]; + index[6] = custom->subtype; + index[7] = idx++; + if (header_index_add(index, 8, custom)) return custom; + } + } + } + return header_index_best(); +} +#endif + +#ifdef ENABLE_LLDPMED +# define TPR_VARIANT_MED_POLICY 2 +# define TPR_VARIANT_MED_LOCATION 3 +static void * +header_tprmedindexed_table(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method, int variant) +{ + struct lldpd_hardware *hardware; + struct lldpd_port *port; + int j; + oid index[4]; + + if (!header_index_init(vp, name, length, exact, var_len, write_method)) + return NULL; + TAILQ_FOREACH (hardware, &scfg->g_hardware, h_entries) { + TAILQ_FOREACH (port, &hardware->h_rports, p_entries) { + if (SMART_HIDDEN(port)) continue; + if (!port->p_chassis->c_med_cap_available) continue; + switch (variant) { + case TPR_VARIANT_MED_POLICY: + for (j = 0; j < LLDP_MED_APPTYPE_LAST; j++) { + if (port->p_med_policy[j].type != j + 1) + continue; + index[0] = lastchange(port); + index[1] = hardware->h_ifindex; + index[2] = port->p_chassis->c_index; + index[3] = j + 1; + if (header_index_add(index, 4, + &port->p_med_policy[j])) + return &port->p_med_policy[j]; + } + break; + case TPR_VARIANT_MED_LOCATION: + for (j = 0; j < LLDP_MED_LOCFORMAT_LAST; j++) { + if (port->p_med_location[j].format != j + 1) + continue; + index[0] = lastchange(port); + index[1] = hardware->h_ifindex; + index[2] = port->p_chassis->c_index; + index[3] = j + 2; + if (header_index_add(index, 4, + &port->p_med_location[j])) + return &port->p_med_location[j]; + } + break; + } + } + } + return header_index_best(); +} +#endif + +#ifdef ENABLE_DOT1 +static struct lldpd_vlan * +header_pvindexed_table(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_hardware *hardware; + struct lldpd_vlan *vlan; + + if (!header_index_init(vp, name, length, exact, var_len, write_method)) + return NULL; + TAILQ_FOREACH (hardware, &scfg->g_hardware, h_entries) { + TAILQ_FOREACH (vlan, &hardware->h_lport.p_vlans, v_entries) { + oid index[2] = { hardware->h_ifindex, vlan->v_vid }; + if (header_index_add(index, 2, vlan)) return vlan; + } + } + return header_index_best(); +} + +static struct lldpd_vlan * +header_tprvindexed_table(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_hardware *hardware; + struct lldpd_port *port; + struct lldpd_vlan *vlan; + + if (!header_index_init(vp, name, length, exact, var_len, write_method)) + return NULL; + TAILQ_FOREACH (hardware, &scfg->g_hardware, h_entries) { + TAILQ_FOREACH (port, &hardware->h_rports, p_entries) { + if (SMART_HIDDEN(port)) continue; + TAILQ_FOREACH (vlan, &port->p_vlans, v_entries) { + oid index[4] = { lastchange(port), hardware->h_ifindex, + port->p_chassis->c_index, vlan->v_vid }; + if (header_index_add(index, 4, vlan)) return vlan; + } + } + } + return header_index_best(); +} + +static struct lldpd_ppvid * +header_pppvidindexed_table(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_hardware *hardware; + struct lldpd_ppvid *ppvid; + + if (!header_index_init(vp, name, length, exact, var_len, write_method)) + return NULL; + TAILQ_FOREACH (hardware, &scfg->g_hardware, h_entries) { + TAILQ_FOREACH (ppvid, &hardware->h_lport.p_ppvids, p_entries) { + oid index[2] = { hardware->h_ifindex, ppvid->p_ppvid }; + if (header_index_add(index, 2, ppvid)) return ppvid; + } + } + return header_index_best(); +} + +static struct lldpd_ppvid * +header_tprppvidindexed_table(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_hardware *hardware; + struct lldpd_port *port; + struct lldpd_ppvid *ppvid; + + if (!header_index_init(vp, name, length, exact, var_len, write_method)) + return NULL; + TAILQ_FOREACH (hardware, &scfg->g_hardware, h_entries) { + TAILQ_FOREACH (port, &hardware->h_rports, p_entries) { + if (SMART_HIDDEN(port)) continue; + TAILQ_FOREACH (ppvid, &port->p_ppvids, p_entries) { + oid index[4] = { lastchange(port), hardware->h_ifindex, + port->p_chassis->c_index, ppvid->p_ppvid }; + if (header_index_add(index, 4, ppvid)) return ppvid; + } + } + } + return header_index_best(); +} + +static struct lldpd_pi * +header_ppiindexed_table(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_hardware *hardware; + struct lldpd_pi *pi; + + if (!header_index_init(vp, name, length, exact, var_len, write_method)) + return NULL; + TAILQ_FOREACH (hardware, &scfg->g_hardware, h_entries) { + TAILQ_FOREACH (pi, &hardware->h_lport.p_pids, p_entries) { + oid index[2] = { hardware->h_ifindex, + frame_checksum((const u_char *)pi->p_pi, pi->p_pi_len, + 0) }; + if (header_index_add(index, 2, pi)) return pi; + } + } + return header_index_best(); +} + +static struct lldpd_pi * +header_tprpiindexed_table(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_hardware *hardware; + struct lldpd_port *port; + struct lldpd_pi *pi; + + if (!header_index_init(vp, name, length, exact, var_len, write_method)) + return NULL; + TAILQ_FOREACH (hardware, &scfg->g_hardware, h_entries) { + TAILQ_FOREACH (port, &hardware->h_rports, p_entries) { + if (SMART_HIDDEN(port)) continue; + TAILQ_FOREACH (pi, &port->p_pids, p_entries) { + oid index[4] = { lastchange(port), hardware->h_ifindex, + port->p_chassis->c_index, + frame_checksum((const u_char *)pi->p_pi, + pi->p_pi_len, 0) }; + if (header_index_add(index, 4, pi)) return pi; + } + } + } + return header_index_best(); +} +#endif + +/* Scalars */ +#define LLDP_SNMP_TXINTERVAL 1 +#define LLDP_SNMP_TXMULTIPLIER 2 +#define LLDP_SNMP_REINITDELAY 3 +#define LLDP_SNMP_TXDELAY 4 +#define LLDP_SNMP_NOTIFICATION 5 +#define LLDP_SNMP_LASTUPDATE 6 +#define LLDP_SNMP_STATS_INSERTS 7 +#define LLDP_SNMP_STATS_DELETES 8 +#define LLDP_SNMP_STATS_DROPS 9 +#define LLDP_SNMP_STATS_AGEOUTS 10 +/* Chassis */ +#define LLDP_SNMP_CIDSUBTYPE 1 +#define LLDP_SNMP_CID 2 +#define LLDP_SNMP_SYSNAME 3 +#define LLDP_SNMP_SYSDESCR 4 +#define LLDP_SNMP_SYSCAP_SUP 5 +#define LLDP_SNMP_SYSCAP_ENA 6 +/* Stats */ +#define LLDP_SNMP_STATS_TX 2 +#define LLDP_SNMP_STATS_RX_DISCARDED 4 +#define LLDP_SNMP_STATS_RX_ERRORS 5 +#define LLDP_SNMP_STATS_RX 6 +#define LLDP_SNMP_STATS_RX_TLVDISCARDED 7 +#define LLDP_SNMP_STATS_RX_TLVUNRECOGNIZED 8 +#define LLDP_SNMP_STATS_RX_AGEOUTS 9 +/* Ports */ +#define LLDP_SNMP_PIDSUBTYPE 2 +#define LLDP_SNMP_PID 3 +#define LLDP_SNMP_PORTDESC 4 +#define LLDP_SNMP_DOT3_AUTONEG_SUPPORT 5 +#define LLDP_SNMP_DOT3_AUTONEG_ENABLED 6 +#define LLDP_SNMP_DOT3_AUTONEG_ADVERTISED 7 +#define LLDP_SNMP_DOT3_AUTONEG_MAU 8 +#define LLDP_SNMP_DOT3_AGG_STATUS 9 +#define LLDP_SNMP_DOT3_AGG_ID 10 +#define LLDP_SNMP_DOT3_MFS 11 +#define LLDP_SNMP_DOT3_POWER_DEVICETYPE 12 +#define LLDP_SNMP_DOT3_POWER_SUPPORT 13 +#define LLDP_SNMP_DOT3_POWER_ENABLED 14 +#define LLDP_SNMP_DOT3_POWER_PAIRCONTROL 15 +#define LLDP_SNMP_DOT3_POWER_PAIRS 16 +#define LLDP_SNMP_DOT3_POWER_CLASS 17 +#define LLDP_SNMP_DOT3_POWER_TYPE 18 +#define LLDP_SNMP_DOT3_POWER_SOURCE 19 +#define LLDP_SNMP_DOT3_POWER_PRIORITY 20 +#define LLDP_SNMP_DOT3_POWER_REQUESTED 21 +#define LLDP_SNMP_DOT3_POWER_ALLOCATED 22 +#define LLDP_SNMP_DOT1_PVID 23 +/* Vlans */ +#define LLDP_SNMP_DOT1_VLANNAME 1 +/* Protocol VLAN IDs */ +#define LLDP_SNMP_DOT1_PPVLAN_SUPPORTED 2 +#define LLDP_SNMP_DOT1_PPVLAN_ENABLED 3 +/* Protocol Identity */ +#define LLDP_SNMP_DOT1_PI 1 +/* Management address */ +#define LLDP_SNMP_ADDR_LEN 1 +#define LLDP_SNMP_ADDR_IFSUBTYPE 2 +#define LLDP_SNMP_ADDR_IFID 3 +#define LLDP_SNMP_ADDR_OID 4 +/* Custom TLVs */ +#define LLDP_SNMP_ORG_DEF_INFO 1 +/* LLDP-MED */ +#define LLDP_SNMP_MED_CAP_AVAILABLE 1 +#define LLDP_SNMP_MED_CAP_ENABLED 2 +#define LLDP_SNMP_MED_CLASS 3 +#define LLDP_SNMP_MED_HW 4 +#define LLDP_SNMP_MED_FW 5 +#define LLDP_SNMP_MED_SW 6 +#define LLDP_SNMP_MED_SN 7 +#define LLDP_SNMP_MED_MANUF 8 +#define LLDP_SNMP_MED_MODEL 9 +#define LLDP_SNMP_MED_ASSET 10 +#define LLDP_SNMP_MED_POLICY_VID 11 +#define LLDP_SNMP_MED_POLICY_PRIO 12 +#define LLDP_SNMP_MED_POLICY_DSCP 13 +#define LLDP_SNMP_MED_POLICY_UNKNOWN 14 +#define LLDP_SNMP_MED_POLICY_TAGGED 15 +#define LLDP_SNMP_MED_LOCATION 16 +#define LLDP_SNMP_MED_POE_DEVICETYPE 17 +#define LLDP_SNMP_MED_POE_PSE_POWERVAL 19 +#define LLDP_SNMP_MED_POE_PSE_POWERSOURCE 20 +#define LLDP_SNMP_MED_POE_PSE_POWERPRIORITY 21 +#define LLDP_SNMP_MED_POE_PD_POWERVAL 22 +#define LLDP_SNMP_MED_POE_PD_POWERSOURCE 23 +#define LLDP_SNMP_MED_POE_PD_POWERPRIORITY 24 + +/* The following macro should be used anytime where the selected OID + is finally not returned (for example, when the associated data is + not available). In this case, we retry the function with the next + OID. */ +#define TRYNEXT(X) \ + do { \ + if (!exact && (name[*length - 1] < MAX_SUBID)) \ + return X(vp, name, length, exact, var_len, write_method); \ + return NULL; \ + } while (0) + +static u_char * +agent_h_scalars(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + static unsigned long long_ret; + struct lldpd_hardware *hardware; + struct lldpd_port *port; + + if (header_generic(vp, name, length, exact, var_len, write_method)) return NULL; + + switch (vp->magic) { + case LLDP_SNMP_TXINTERVAL: + long_ret = (scfg->g_config.c_tx_interval + 999) / 1000; + return (u_char *)&long_ret; + case LLDP_SNMP_TXMULTIPLIER: + long_ret = scfg->g_config.c_tx_hold; + return (u_char *)&long_ret; + case LLDP_SNMP_REINITDELAY: + long_ret = 1; + return (u_char *)&long_ret; + case LLDP_SNMP_TXDELAY: + long_ret = LLDPD_TX_MSGDELAY; + return (u_char *)&long_ret; + case LLDP_SNMP_NOTIFICATION: + long_ret = 5; + return (u_char *)&long_ret; + case LLDP_SNMP_LASTUPDATE: + long_ret = 0; + TAILQ_FOREACH (hardware, &scfg->g_hardware, h_entries) { + /* Check if the last removal of a remote port on this local port + * was the last change. */ + if (hardware->h_lport.p_lastremove > long_ret) + long_ret = hardware->h_lport.p_lastremove; + /* Check if any change on the existing remote ports was the last + * change. */ + TAILQ_FOREACH (port, &hardware->h_rports, p_entries) { + if (SMART_HIDDEN(port)) continue; + if (port->p_lastchange > long_ret) + long_ret = port->p_lastchange; + } + } + if (long_ret) long_ret = (long_ret - starttime.tv_sec) * 100; + return (u_char *)&long_ret; + case LLDP_SNMP_STATS_INSERTS: + /* We assume this is equal to valid frames received on all ports */ + long_ret = 0; + TAILQ_FOREACH (hardware, &scfg->g_hardware, h_entries) + long_ret += hardware->h_insert_cnt; + return (u_char *)&long_ret; + case LLDP_SNMP_STATS_AGEOUTS: + long_ret = 0; + TAILQ_FOREACH (hardware, &scfg->g_hardware, h_entries) + long_ret += hardware->h_ageout_cnt; + return (u_char *)&long_ret; + case LLDP_SNMP_STATS_DELETES: + long_ret = 0; + TAILQ_FOREACH (hardware, &scfg->g_hardware, h_entries) + long_ret += hardware->h_delete_cnt; + return (u_char *)&long_ret; + case LLDP_SNMP_STATS_DROPS: + long_ret = 0; + TAILQ_FOREACH (hardware, &scfg->g_hardware, h_entries) + long_ret += hardware->h_drop_cnt; + return (u_char *)&long_ret; + default: + break; + } + return NULL; +} + +#ifdef ENABLE_LLDPMED +static u_char * +agent_v_med_power(struct variable *vp, size_t *var_len, struct lldpd_med_power *power) +{ + static unsigned long long_ret; + + switch (vp->magic) { + case LLDP_SNMP_MED_POE_DEVICETYPE: + switch (power->devicetype) { + case LLDP_MED_POW_TYPE_PSE: + long_ret = 2; + break; + case LLDP_MED_POW_TYPE_PD: + long_ret = 3; + break; + case 0: + long_ret = 4; + break; + default: + long_ret = 1; + } + return (u_char *)&long_ret; + case LLDP_SNMP_MED_POE_PSE_POWERVAL: + case LLDP_SNMP_MED_POE_PD_POWERVAL: + if (((vp->magic == LLDP_SNMP_MED_POE_PSE_POWERVAL) && + (power->devicetype == LLDP_MED_POW_TYPE_PSE)) || + ((vp->magic == LLDP_SNMP_MED_POE_PD_POWERVAL) && + (power->devicetype == LLDP_MED_POW_TYPE_PD))) { + long_ret = power->val; + return (u_char *)&long_ret; + } + break; + case LLDP_SNMP_MED_POE_PSE_POWERSOURCE: + if (power->devicetype == LLDP_MED_POW_TYPE_PSE) { + switch (power->source) { + case LLDP_MED_POW_SOURCE_PRIMARY: + long_ret = 2; + break; + case LLDP_MED_POW_SOURCE_BACKUP: + long_ret = 3; + break; + default: + long_ret = 1; + } + return (u_char *)&long_ret; + } + break; + case LLDP_SNMP_MED_POE_PD_POWERSOURCE: + if (power->devicetype == LLDP_MED_POW_TYPE_PD) { + switch (power->source) { + case LLDP_MED_POW_SOURCE_PSE: + long_ret = 2; + break; + case LLDP_MED_POW_SOURCE_LOCAL: + long_ret = 3; + break; + case LLDP_MED_POW_SOURCE_BOTH: + long_ret = 4; + break; + default: + long_ret = 1; + } + return (u_char *)&long_ret; + } + break; + case LLDP_SNMP_MED_POE_PSE_POWERPRIORITY: + case LLDP_SNMP_MED_POE_PD_POWERPRIORITY: + if (((vp->magic == LLDP_SNMP_MED_POE_PSE_POWERPRIORITY) && + (power->devicetype == LLDP_MED_POW_TYPE_PSE)) || + ((vp->magic == LLDP_SNMP_MED_POE_PD_POWERPRIORITY) && + (power->devicetype == LLDP_MED_POW_TYPE_PD))) { + switch (power->priority) { + case LLDP_MED_POW_PRIO_CRITICAL: + long_ret = 2; + break; + case LLDP_MED_POW_PRIO_HIGH: + long_ret = 3; + break; + case LLDP_MED_POW_PRIO_LOW: + long_ret = 4; + break; + default: + long_ret = 1; + } + return (u_char *)&long_ret; + } + break; + } + + return NULL; +} +static u_char * +agent_h_local_med_power(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_med_power *power = NULL; + struct lldpd_hardware *hardware; + int pse = 0; + + if (!LOCAL_CHASSIS(scfg)->c_med_cap_available) return NULL; + if (header_generic(vp, name, length, exact, var_len, write_method)) return NULL; + + /* LLDP-MED requires only one device type for all + ports. Moreover, a PSE can only have one power source. At + least, all PD values are global and not per-port. We try to + do our best. For device type, we decide on the number of + PD/PSE ports. */ + TAILQ_FOREACH (hardware, &scfg->g_hardware, h_entries) { + if (hardware->h_lport.p_med_power.devicetype == LLDP_MED_POW_TYPE_PSE) { + pse++; + if (pse == 1) /* Take this port as a reference */ + power = &hardware->h_lport.p_med_power; + } else if (hardware->h_lport.p_med_power.devicetype == + LLDP_MED_POW_TYPE_PD) { + pse--; + if (pse == -1) /* Take this one instead */ + power = &hardware->h_lport.p_med_power; + } + } + if (power) { + u_char *a; + if ((a = agent_v_med_power(vp, var_len, power)) != NULL) return a; + } + TRYNEXT(agent_h_local_med_power); +} +static u_char * +agent_h_remote_med_power(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_port *port; + u_char *a; + + if ((port = header_tprindexed_table(vp, name, length, exact, var_len, + write_method, 1)) == NULL) + return NULL; + + if ((a = agent_v_med_power(vp, var_len, &port->p_med_power)) != NULL) return a; + TRYNEXT(agent_h_remote_med_power); +} + +static u_char * +agent_v_med(struct variable *vp, size_t *var_len, struct lldpd_chassis *chassis, + struct lldpd_port *port) +{ + static unsigned long long_ret; + static uint8_t bit; + + switch (vp->magic) { + case LLDP_SNMP_MED_CLASS: + long_ret = chassis->c_med_type; + return (u_char *)&long_ret; + case LLDP_SNMP_MED_CAP_AVAILABLE: + *var_len = 1; + bit = swap_bits(chassis->c_med_cap_available); + return (u_char *)&bit; + case LLDP_SNMP_MED_CAP_ENABLED: + if (!port) break; + *var_len = 1; + bit = swap_bits(port->p_med_cap_enabled); + return (u_char *)&bit; + +# define LLDP_H_MED(magic, variable) \ + case magic: \ + if (chassis->variable) { \ + *var_len = strlen(chassis->variable); \ + return (u_char *)chassis->variable; \ + } \ + break + + LLDP_H_MED(LLDP_SNMP_MED_HW, c_med_hw); + LLDP_H_MED(LLDP_SNMP_MED_SW, c_med_sw); + LLDP_H_MED(LLDP_SNMP_MED_FW, c_med_fw); + LLDP_H_MED(LLDP_SNMP_MED_SN, c_med_sn); + LLDP_H_MED(LLDP_SNMP_MED_MANUF, c_med_manuf); + LLDP_H_MED(LLDP_SNMP_MED_MODEL, c_med_model); + LLDP_H_MED(LLDP_SNMP_MED_ASSET, c_med_asset); + } + return NULL; +} +static u_char * +agent_h_local_med(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + u_char *a; + + if (!LOCAL_CHASSIS(scfg)->c_med_cap_available) return NULL; + if (header_generic(vp, name, length, exact, var_len, write_method)) return NULL; + + if ((a = agent_v_med(vp, var_len, LOCAL_CHASSIS(scfg), NULL)) != NULL) return a; + TRYNEXT(agent_h_local_med); +} + +static u_char * +agent_h_remote_med(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_port *port; + u_char *a; + + if ((port = header_tprindexed_table(vp, name, length, exact, var_len, + write_method, 1)) == NULL) + return NULL; + + if ((a = agent_v_med(vp, var_len, port->p_chassis, port)) != NULL) return a; + TRYNEXT(agent_h_remote_med); +} + +static u_char * +agent_v_med_policy(struct variable *vp, size_t *var_len, + struct lldpd_med_policy *policy) +{ + static unsigned long long_ret; + + switch (vp->magic) { + case LLDP_SNMP_MED_POLICY_VID: + long_ret = policy->vid; + return (u_char *)&long_ret; + case LLDP_SNMP_MED_POLICY_PRIO: + long_ret = policy->priority; + return (u_char *)&long_ret; + case LLDP_SNMP_MED_POLICY_DSCP: + long_ret = policy->dscp; + return (u_char *)&long_ret; + case LLDP_SNMP_MED_POLICY_UNKNOWN: + long_ret = policy->unknown ? 1 : 2; + return (u_char *)&long_ret; + case LLDP_SNMP_MED_POLICY_TAGGED: + long_ret = policy->tagged ? 1 : 2; + return (u_char *)&long_ret; + default: + return NULL; + } +} +static u_char * +agent_h_remote_med_policy(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_med_policy *policy; + + if ((policy = (struct lldpd_med_policy *)header_tprmedindexed_table(vp, name, + length, exact, var_len, write_method, TPR_VARIANT_MED_POLICY)) == NULL) + return NULL; + + return agent_v_med_policy(vp, var_len, policy); +} +static u_char * +agent_h_local_med_policy(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_med_policy *policy; + + if ((policy = (struct lldpd_med_policy *)header_pmedindexed_policy_table(vp, + name, length, exact, var_len, write_method)) == NULL) + return NULL; + + return agent_v_med_policy(vp, var_len, policy); +} + +static u_char * +agent_v_med_location(struct variable *vp, size_t *var_len, + struct lldpd_med_loc *location) +{ + switch (vp->magic) { + case LLDP_SNMP_MED_LOCATION: + *var_len = location->data_len; + return (u_char *)location->data; + default: + return NULL; + } +} +static u_char * +agent_h_remote_med_location(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_med_loc *location; + + if ((location = (struct lldpd_med_loc *)header_tprmedindexed_table(vp, name, + length, exact, var_len, write_method, TPR_VARIANT_MED_LOCATION)) == + NULL) + return NULL; + + return agent_v_med_location(vp, var_len, location); +} +static u_char * +agent_h_local_med_location(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_med_loc *location; + + if ((location = (struct lldpd_med_loc *)header_pmedindexed_location_table(vp, + name, length, exact, var_len, write_method)) == NULL) + return NULL; + + return agent_v_med_location(vp, var_len, location); +} +#endif + +static u_char * +agent_v_chassis(struct variable *vp, size_t *var_len, struct lldpd_chassis *chassis) +{ + static uint8_t bit; + static unsigned long long_ret; + + switch (vp->magic) { + case LLDP_SNMP_CIDSUBTYPE: + long_ret = chassis->c_id_subtype; + return (u_char *)&long_ret; + case LLDP_SNMP_CID: + *var_len = chassis->c_id_len; + return (u_char *)chassis->c_id; + case LLDP_SNMP_SYSNAME: + if (!chassis->c_name || *chassis->c_name == '\0') break; + *var_len = strlen(chassis->c_name); + return (u_char *)chassis->c_name; + case LLDP_SNMP_SYSDESCR: + if (!chassis->c_descr || *chassis->c_descr == '\0') break; + *var_len = strlen(chassis->c_descr); + return (u_char *)chassis->c_descr; + case LLDP_SNMP_SYSCAP_SUP: + *var_len = 1; + bit = swap_bits(chassis->c_cap_available); + return (u_char *)&bit; + case LLDP_SNMP_SYSCAP_ENA: + *var_len = 1; + bit = swap_bits(chassis->c_cap_enabled); + return (u_char *)&bit; + default: + break; + } + return NULL; +} +static u_char * +agent_h_local_chassis(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + u_char *a; + + if (header_generic(vp, name, length, exact, var_len, write_method)) return NULL; + + if ((a = agent_v_chassis(vp, var_len, LOCAL_CHASSIS(scfg))) != NULL) return a; + TRYNEXT(agent_h_local_chassis); +} +static u_char * +agent_h_remote_chassis(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_port *port; + u_char *a; + + if ((port = header_tprindexed_table(vp, name, length, exact, var_len, + write_method, 0)) == NULL) + return NULL; + + if ((a = agent_v_chassis(vp, var_len, port->p_chassis)) != NULL) return a; + TRYNEXT(agent_h_remote_chassis); +} + +static u_char * +agent_h_stats(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + static unsigned long long_ret; + struct lldpd_hardware *hardware; + + if ((hardware = header_portindexed_table(vp, name, length, exact, var_len, + write_method)) == NULL) + return NULL; + + switch (vp->magic) { + case LLDP_SNMP_STATS_TX: + long_ret = hardware->h_tx_cnt; + return (u_char *)&long_ret; + case LLDP_SNMP_STATS_RX: + long_ret = hardware->h_rx_cnt; + return (u_char *)&long_ret; + case LLDP_SNMP_STATS_RX_DISCARDED: + case LLDP_SNMP_STATS_RX_ERRORS: + /* We discard only frame with errors. Therefore, the two values + * are equal */ + long_ret = hardware->h_rx_discarded_cnt; + return (u_char *)&long_ret; + case LLDP_SNMP_STATS_RX_TLVDISCARDED: + case LLDP_SNMP_STATS_RX_TLVUNRECOGNIZED: + /* We discard only unrecognized TLV. Malformed TLV + implies dropping the whole frame */ + long_ret = hardware->h_rx_unrecognized_cnt; + return (u_char *)&long_ret; + case LLDP_SNMP_STATS_RX_AGEOUTS: + long_ret = hardware->h_ageout_cnt; + return (u_char *)&long_ret; + default: + return NULL; + } +} + +#ifdef ENABLE_DOT1 +static u_char * +agent_v_vlan(struct variable *vp, size_t *var_len, struct lldpd_vlan *vlan) +{ + switch (vp->magic) { + case LLDP_SNMP_DOT1_VLANNAME: + *var_len = strlen(vlan->v_name); + return (u_char *)vlan->v_name; + default: + return NULL; + } +} +static u_char * +agent_h_local_vlan(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_vlan *vlan; + + if ((vlan = header_pvindexed_table(vp, name, length, exact, var_len, + write_method)) == NULL) + return NULL; + + return agent_v_vlan(vp, var_len, vlan); +} +static u_char * +agent_h_remote_vlan(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_vlan *vlan; + + if ((vlan = header_tprvindexed_table(vp, name, length, exact, var_len, + write_method)) == NULL) + return NULL; + + return agent_v_vlan(vp, var_len, vlan); +} + +static u_char * +agent_v_ppvid(struct variable *vp, size_t *var_len, struct lldpd_ppvid *ppvid) +{ + static unsigned long long_ret; + + switch (vp->magic) { + case LLDP_SNMP_DOT1_PPVLAN_SUPPORTED: + long_ret = (ppvid->p_cap_status & LLDP_PPVID_CAP_SUPPORTED) ? 1 : 2; + return (u_char *)&long_ret; + case LLDP_SNMP_DOT1_PPVLAN_ENABLED: + long_ret = (ppvid->p_cap_status & LLDP_PPVID_CAP_ENABLED) ? 1 : 2; + return (u_char *)&long_ret; + default: + return NULL; + } +} +static u_char * +agent_h_local_ppvid(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_ppvid *ppvid; + + if ((ppvid = header_pppvidindexed_table(vp, name, length, exact, var_len, + write_method)) == NULL) + return NULL; + + return agent_v_ppvid(vp, var_len, ppvid); +} + +static u_char * +agent_h_remote_ppvid(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_ppvid *ppvid; + + if ((ppvid = header_tprppvidindexed_table(vp, name, length, exact, var_len, + write_method)) == NULL) + return NULL; + + return agent_v_ppvid(vp, var_len, ppvid); +} + +static u_char * +agent_v_pi(struct variable *vp, size_t *var_len, struct lldpd_pi *pi) +{ + switch (vp->magic) { + case LLDP_SNMP_DOT1_PI: + *var_len = pi->p_pi_len; + return (u_char *)pi->p_pi; + default: + return NULL; + } +} +static u_char * +agent_h_local_pi(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_pi *pi; + + if ((pi = header_ppiindexed_table(vp, name, length, exact, var_len, + write_method)) == NULL) + return NULL; + + return agent_v_pi(vp, var_len, pi); +} +static u_char * +agent_h_remote_pi(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_pi *pi; + + if ((pi = header_tprpiindexed_table(vp, name, length, exact, var_len, + write_method)) == NULL) + return NULL; + + return agent_v_pi(vp, var_len, pi); +} +#endif + +static u_char * +agent_v_port(struct variable *vp, size_t *var_len, struct lldpd_port *port) +{ +#ifdef ENABLE_DOT3 + static uint16_t short_ret; + static uint8_t bit; +#endif + static unsigned long long_ret; + + switch (vp->magic) { + case LLDP_SNMP_PIDSUBTYPE: + long_ret = port->p_id_subtype; + return (u_char *)&long_ret; + case LLDP_SNMP_PID: + *var_len = port->p_id_len; + return (u_char *)port->p_id; + case LLDP_SNMP_PORTDESC: + if (!port->p_descr || *port->p_descr == '\0') break; + *var_len = strlen(port->p_descr); + return (u_char *)port->p_descr; +#ifdef ENABLE_DOT3 + case LLDP_SNMP_DOT3_AUTONEG_SUPPORT: + long_ret = 2 - port->p_macphy.autoneg_support; + return (u_char *)&long_ret; + case LLDP_SNMP_DOT3_AUTONEG_ENABLED: + long_ret = 2 - port->p_macphy.autoneg_enabled; + return (u_char *)&long_ret; + case LLDP_SNMP_DOT3_AUTONEG_ADVERTISED: + *var_len = 2; + short_ret = htons(port->p_macphy.autoneg_advertised); + return (u_char *)&short_ret; + case LLDP_SNMP_DOT3_AUTONEG_MAU: + long_ret = port->p_macphy.mau_type; + return (u_char *)&long_ret; + case LLDP_SNMP_DOT3_AGG_STATUS: + bit = swap_bits((port->p_aggregid > 0) ? 3 : 0); + *var_len = 1; + return (u_char *)&bit; + case LLDP_SNMP_DOT3_AGG_ID: + long_ret = port->p_aggregid; + return (u_char *)&long_ret; + case LLDP_SNMP_DOT3_MFS: + if (port->p_mfs) { + long_ret = port->p_mfs; + return (u_char *)&long_ret; + } + break; + case LLDP_SNMP_DOT3_POWER_DEVICETYPE: + if (port->p_power.devicetype) { + long_ret = + (port->p_power.devicetype == LLDP_DOT3_POWER_PSE) ? 1 : 2; + return (u_char *)&long_ret; + } + break; + case LLDP_SNMP_DOT3_POWER_SUPPORT: + if (port->p_power.devicetype) { + long_ret = (port->p_power.supported) ? 1 : 2; + return (u_char *)&long_ret; + } + break; + case LLDP_SNMP_DOT3_POWER_ENABLED: + if (port->p_power.devicetype) { + long_ret = (port->p_power.enabled) ? 1 : 2; + return (u_char *)&long_ret; + } + break; + case LLDP_SNMP_DOT3_POWER_PAIRCONTROL: + if (port->p_power.devicetype) { + long_ret = (port->p_power.paircontrol) ? 1 : 2; + return (u_char *)&long_ret; + } + break; + case LLDP_SNMP_DOT3_POWER_PAIRS: + if (port->p_power.devicetype) { + long_ret = port->p_power.pairs; + return (u_char *)&long_ret; + } + break; + case LLDP_SNMP_DOT3_POWER_CLASS: + if (port->p_power.devicetype && port->p_power.class) { + long_ret = port->p_power.class; + return (u_char *)&long_ret; + } + break; + case LLDP_SNMP_DOT3_POWER_TYPE: + if (port->p_power.devicetype && + port->p_power.powertype != LLDP_DOT3_POWER_8023AT_OFF) { + *var_len = 1; + bit = (((port->p_power.powertype == + LLDP_DOT3_POWER_8023AT_TYPE1) ? + 0 : + 1) + << 7) | + (((port->p_power.devicetype == LLDP_DOT3_POWER_PSE) ? 0 : 1) + << 6); + return (u_char *)&bit; + } + break; + case LLDP_SNMP_DOT3_POWER_SOURCE: + if (port->p_power.devicetype && + port->p_power.powertype != LLDP_DOT3_POWER_8023AT_OFF) { + *var_len = 1; + bit = swap_bits(port->p_power.source % (1 << 2)); + return (u_char *)&bit; + } + break; + case LLDP_SNMP_DOT3_POWER_PRIORITY: + if (port->p_power.devicetype && + port->p_power.powertype != LLDP_DOT3_POWER_8023AT_OFF) { + /* See 30.12.2.1.16. This seems defined in reverse order... */ + long_ret = 4 - port->p_power.priority; + return (u_char *)&long_ret; + } + break; + case LLDP_SNMP_DOT3_POWER_REQUESTED: + if (port->p_power.devicetype && + port->p_power.powertype != LLDP_DOT3_POWER_8023AT_OFF) { + long_ret = port->p_power.requested; + return (u_char *)&long_ret; + } + break; + case LLDP_SNMP_DOT3_POWER_ALLOCATED: + if (port->p_power.devicetype && + port->p_power.powertype != LLDP_DOT3_POWER_8023AT_OFF) { + long_ret = port->p_power.allocated; + return (u_char *)&long_ret; + } + break; +#endif +#ifdef ENABLE_DOT1 + case LLDP_SNMP_DOT1_PVID: + long_ret = port->p_pvid; + return (u_char *)&long_ret; +#endif + default: + break; + } + return NULL; +} +static u_char * +agent_h_remote_port(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_port *port; + u_char *a; + + if ((port = header_tprindexed_table(vp, name, length, exact, var_len, + write_method, 0)) == NULL) + return NULL; + + if ((a = agent_v_port(vp, var_len, port)) != NULL) return a; + TRYNEXT(agent_h_remote_port); +} +static u_char * +agent_h_local_port(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_hardware *hardware; + u_char *a; + + if ((hardware = header_portindexed_table(vp, name, length, exact, var_len, + write_method)) == NULL) + return NULL; + + if ((a = agent_v_port(vp, var_len, &hardware->h_lport)) != NULL) return a; + TRYNEXT(agent_h_local_port); +} + +static u_char * +agent_v_management(struct variable *vp, size_t *var_len, struct lldpd_mgmt *mgmt) +{ + static unsigned long int long_ret; + static oid zeroDotZero[2] = { 0, 0 }; + + switch (vp->magic) { + case LLDP_SNMP_ADDR_LEN: + long_ret = mgmt->m_addrsize + 1; + return (u_char *)&long_ret; + case LLDP_SNMP_ADDR_IFSUBTYPE: + if (mgmt->m_iface != 0) + long_ret = LLDP_MGMT_IFACE_IFINDEX; + else + long_ret = 1; + return (u_char *)&long_ret; + case LLDP_SNMP_ADDR_IFID: + long_ret = mgmt->m_iface; + return (u_char *)&long_ret; + case LLDP_SNMP_ADDR_OID: + *var_len = sizeof(zeroDotZero); + return (u_char *)zeroDotZero; + default: + return NULL; + } +} +static u_char * +agent_h_local_management(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + + struct lldpd_mgmt *mgmt; + + if ((mgmt = header_ipindexed_table(vp, name, length, exact, var_len, + write_method)) == NULL) + return NULL; + + return agent_v_management(vp, var_len, mgmt); +} +static u_char * +agent_h_remote_management(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_mgmt *mgmt; + + if ((mgmt = header_tpripindexed_table(vp, name, length, exact, var_len, + write_method)) == NULL) + return NULL; + + return agent_v_management(vp, var_len, mgmt); +} + +#ifdef ENABLE_CUSTOM +static u_char * +agent_v_custom(struct variable *vp, size_t *var_len, struct lldpd_custom *custom) +{ + switch (vp->magic) { + case LLDP_SNMP_ORG_DEF_INFO: + *var_len = custom->oui_info_len; + return (u_char *)custom->oui_info; + default: + return NULL; + } +} +static u_char * +agent_h_remote_custom(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_custom *custom; + + if ((custom = header_tprcustomindexed_table(vp, name, length, exact, var_len, + write_method)) == NULL) + return NULL; + + return agent_v_custom(vp, var_len, custom); +} +#endif + +/* + Here is how it works: a agent_h_*() function will handle incoming + requests. It will use an appropriate header_*indexed_table() + function to grab the appropriate structure that was queried (a port, + a chassis, ...). It will then delegate to a agent_v_*() function the + responsability to extract the appropriate answer. + + agent_h_*() functions and header_*indexed_table() are not shared + between remote and not remote version while agent_v_*() functions + are the same for both version. +*/ + +/* For testing purposes, keep this structure ordered by increasing OID! */ +struct variable8 agent_lldp_vars[] = { + /* Scalars */ + { LLDP_SNMP_TXINTERVAL, ASN_INTEGER, RONLY, agent_h_scalars, 3, { 1, 1, 1 } }, + { LLDP_SNMP_TXMULTIPLIER, ASN_INTEGER, RONLY, agent_h_scalars, 3, { 1, 1, 2 } }, + { LLDP_SNMP_REINITDELAY, ASN_INTEGER, RONLY, agent_h_scalars, 3, { 1, 1, 3 } }, + { LLDP_SNMP_TXDELAY, ASN_INTEGER, RONLY, agent_h_scalars, 3, { 1, 1, 4 } }, + { LLDP_SNMP_NOTIFICATION, ASN_INTEGER, RONLY, agent_h_scalars, 3, { 1, 1, 5 } }, + { LLDP_SNMP_LASTUPDATE, ASN_TIMETICKS, RONLY, agent_h_scalars, 3, { 1, 2, 1 } }, + { LLDP_SNMP_STATS_INSERTS, ASN_GAUGE, RONLY, agent_h_scalars, 3, { 1, 2, 2 } }, + { LLDP_SNMP_STATS_DELETES, ASN_GAUGE, RONLY, agent_h_scalars, 3, { 1, 2, 3 } }, + { LLDP_SNMP_STATS_DROPS, ASN_GAUGE, RONLY, agent_h_scalars, 3, { 1, 2, 4 } }, + { LLDP_SNMP_STATS_AGEOUTS, ASN_GAUGE, RONLY, agent_h_scalars, 3, { 1, 2, 5 } }, + /* Stats */ + { LLDP_SNMP_STATS_TX, ASN_COUNTER, RONLY, agent_h_stats, 5, { 1, 2, 6, 1, 2 } }, + { LLDP_SNMP_STATS_RX_DISCARDED, ASN_COUNTER, RONLY, agent_h_stats, 5, + { 1, 2, 7, 1, 2 } }, + { LLDP_SNMP_STATS_RX_ERRORS, ASN_COUNTER, RONLY, agent_h_stats, 5, + { 1, 2, 7, 1, 3 } }, + { LLDP_SNMP_STATS_RX, ASN_COUNTER, RONLY, agent_h_stats, 5, { 1, 2, 7, 1, 4 } }, + { LLDP_SNMP_STATS_RX_TLVDISCARDED, ASN_COUNTER, RONLY, agent_h_stats, 5, + { 1, 2, 7, 1, 5 } }, + { LLDP_SNMP_STATS_RX_TLVUNRECOGNIZED, ASN_COUNTER, RONLY, agent_h_stats, 5, + { 1, 2, 7, 1, 6 } }, + { LLDP_SNMP_STATS_RX_AGEOUTS, ASN_GAUGE, RONLY, agent_h_stats, 5, + { 1, 2, 7, 1, 7 } }, + /* Local chassis */ + { LLDP_SNMP_CIDSUBTYPE, ASN_INTEGER, RONLY, agent_h_local_chassis, 3, + { 1, 3, 1 } }, + { LLDP_SNMP_CID, ASN_OCTET_STR, RONLY, agent_h_local_chassis, 3, { 1, 3, 2 } }, + { LLDP_SNMP_SYSNAME, ASN_OCTET_STR, RONLY, agent_h_local_chassis, 3, + { 1, 3, 3 } }, + { LLDP_SNMP_SYSDESCR, ASN_OCTET_STR, RONLY, agent_h_local_chassis, 3, + { 1, 3, 4 } }, + { LLDP_SNMP_SYSCAP_SUP, ASN_OCTET_STR, RONLY, agent_h_local_chassis, 3, + { 1, 3, 5 } }, + { LLDP_SNMP_SYSCAP_ENA, ASN_OCTET_STR, RONLY, agent_h_local_chassis, 3, + { 1, 3, 6 } }, + /* Local ports */ + { LLDP_SNMP_PIDSUBTYPE, ASN_INTEGER, RONLY, agent_h_local_port, 5, + { 1, 3, 7, 1, 2 } }, + { LLDP_SNMP_PID, ASN_OCTET_STR, RONLY, agent_h_local_port, 5, + { 1, 3, 7, 1, 3 } }, + { LLDP_SNMP_PORTDESC, ASN_OCTET_STR, RONLY, agent_h_local_port, 5, + { 1, 3, 7, 1, 4 } }, + /* Local management address */ + { LLDP_SNMP_ADDR_LEN, ASN_INTEGER, RONLY, agent_h_local_management, 5, + { 1, 3, 8, 1, 3 } }, + { LLDP_SNMP_ADDR_IFSUBTYPE, ASN_INTEGER, RONLY, agent_h_local_management, 5, + { 1, 3, 8, 1, 4 } }, + { LLDP_SNMP_ADDR_IFID, ASN_INTEGER, RONLY, agent_h_local_management, 5, + { 1, 3, 8, 1, 5 } }, + { LLDP_SNMP_ADDR_OID, ASN_OBJECT_ID, RONLY, agent_h_local_management, 5, + { 1, 3, 8, 1, 6 } }, + /* Remote ports */ + { LLDP_SNMP_CIDSUBTYPE, ASN_INTEGER, RONLY, agent_h_remote_chassis, 5, + { 1, 4, 1, 1, 4 } }, + { LLDP_SNMP_CID, ASN_OCTET_STR, RONLY, agent_h_remote_chassis, 5, + { 1, 4, 1, 1, 5 } }, + { LLDP_SNMP_PIDSUBTYPE, ASN_INTEGER, RONLY, agent_h_remote_port, 5, + { 1, 4, 1, 1, 6 } }, + { LLDP_SNMP_PID, ASN_OCTET_STR, RONLY, agent_h_remote_port, 5, + { 1, 4, 1, 1, 7 } }, + { LLDP_SNMP_PORTDESC, ASN_OCTET_STR, RONLY, agent_h_remote_port, 5, + { 1, 4, 1, 1, 8 } }, + { LLDP_SNMP_SYSNAME, ASN_OCTET_STR, RONLY, agent_h_remote_chassis, 5, + { 1, 4, 1, 1, 9 } }, + { LLDP_SNMP_SYSDESCR, ASN_OCTET_STR, RONLY, agent_h_remote_chassis, 5, + { 1, 4, 1, 1, 10 } }, + { LLDP_SNMP_SYSCAP_SUP, ASN_OCTET_STR, RONLY, agent_h_remote_chassis, 5, + { 1, 4, 1, 1, 11 } }, + { LLDP_SNMP_SYSCAP_ENA, ASN_OCTET_STR, RONLY, agent_h_remote_chassis, 5, + { 1, 4, 1, 1, 12 } }, + /* Remote management address */ + { LLDP_SNMP_ADDR_IFSUBTYPE, ASN_INTEGER, RONLY, agent_h_remote_management, 5, + { 1, 4, 2, 1, 3 } }, + { LLDP_SNMP_ADDR_IFID, ASN_INTEGER, RONLY, agent_h_remote_management, 5, + { 1, 4, 2, 1, 4 } }, + { LLDP_SNMP_ADDR_OID, ASN_OBJECT_ID, RONLY, agent_h_remote_management, 5, + { 1, 4, 2, 1, 5 } }, +#ifdef ENABLE_CUSTOM + /* Custom TLVs */ + { LLDP_SNMP_ORG_DEF_INFO, ASN_OCTET_STR, RONLY, agent_h_remote_custom, 5, + { 1, 4, 4, 1, 4 } }, +#endif +#ifdef ENABLE_DOT3 + /* Dot3, local ports */ + { LLDP_SNMP_DOT3_AUTONEG_SUPPORT, ASN_INTEGER, RONLY, agent_h_local_port, 8, + { 1, 5, 4623, 1, 2, 1, 1, 1 } }, + { LLDP_SNMP_DOT3_AUTONEG_ENABLED, ASN_INTEGER, RONLY, agent_h_local_port, 8, + { 1, 5, 4623, 1, 2, 1, 1, 2 } }, + { LLDP_SNMP_DOT3_AUTONEG_ADVERTISED, ASN_OCTET_STR, RONLY, agent_h_local_port, + 8, { 1, 5, 4623, 1, 2, 1, 1, 3 } }, + { LLDP_SNMP_DOT3_AUTONEG_MAU, ASN_INTEGER, RONLY, agent_h_local_port, 8, + { 1, 5, 4623, 1, 2, 1, 1, 4 } }, + { LLDP_SNMP_DOT3_POWER_DEVICETYPE, ASN_INTEGER, RONLY, agent_h_local_port, 8, + { 1, 5, 4623, 1, 2, 2, 1, 1 } }, + { LLDP_SNMP_DOT3_POWER_SUPPORT, ASN_INTEGER, RONLY, agent_h_local_port, 8, + { 1, 5, 4623, 1, 2, 2, 1, 2 } }, + { LLDP_SNMP_DOT3_POWER_ENABLED, ASN_INTEGER, RONLY, agent_h_local_port, 8, + { 1, 5, 4623, 1, 2, 2, 1, 3 } }, + { LLDP_SNMP_DOT3_POWER_PAIRCONTROL, ASN_INTEGER, RONLY, agent_h_local_port, 8, + { 1, 5, 4623, 1, 2, 2, 1, 4 } }, + { LLDP_SNMP_DOT3_POWER_PAIRS, ASN_INTEGER, RONLY, agent_h_local_port, 8, + { 1, 5, 4623, 1, 2, 2, 1, 5 } }, + { LLDP_SNMP_DOT3_POWER_CLASS, ASN_INTEGER, RONLY, agent_h_local_port, 8, + { 1, 5, 4623, 1, 2, 2, 1, 6 } }, + { LLDP_SNMP_DOT3_POWER_TYPE, ASN_OCTET_STR, RONLY, agent_h_local_port, 8, + { 1, 5, 4623, 1, 2, 2, 1, 7 } }, + { LLDP_SNMP_DOT3_POWER_SOURCE, ASN_OCTET_STR, RONLY, agent_h_local_port, 8, + { 1, 5, 4623, 1, 2, 2, 1, 8 } }, + { LLDP_SNMP_DOT3_POWER_PRIORITY, ASN_INTEGER, RONLY, agent_h_local_port, 8, + { 1, 5, 4623, 1, 2, 2, 1, 9 } }, + { LLDP_SNMP_DOT3_POWER_REQUESTED, ASN_INTEGER, RONLY, agent_h_local_port, 8, + { 1, 5, 4623, 1, 2, 2, 1, 10 } }, + { LLDP_SNMP_DOT3_POWER_ALLOCATED, ASN_INTEGER, RONLY, agent_h_local_port, 8, + { 1, 5, 4623, 1, 2, 2, 1, 11 } }, + { LLDP_SNMP_DOT3_AGG_STATUS, ASN_OCTET_STR, RONLY, agent_h_local_port, 8, + { 1, 5, 4623, 1, 2, 3, 1, 1 } }, + { LLDP_SNMP_DOT3_AGG_ID, ASN_INTEGER, RONLY, agent_h_local_port, 8, + { 1, 5, 4623, 1, 2, 3, 1, 2 } }, + { LLDP_SNMP_DOT3_MFS, ASN_INTEGER, RONLY, agent_h_local_port, 8, + { 1, 5, 4623, 1, 2, 4, 1, 1 } }, +#endif +/* Dot3, remote ports */ +#ifdef ENABLE_DOT3 + { LLDP_SNMP_DOT3_AUTONEG_SUPPORT, ASN_INTEGER, RONLY, agent_h_remote_port, 8, + { 1, 5, 4623, 1, 3, 1, 1, 1 } }, + { LLDP_SNMP_DOT3_AUTONEG_ENABLED, ASN_INTEGER, RONLY, agent_h_remote_port, 8, + { 1, 5, 4623, 1, 3, 1, 1, 2 } }, + { LLDP_SNMP_DOT3_AUTONEG_ADVERTISED, ASN_OCTET_STR, RONLY, agent_h_remote_port, + 8, { 1, 5, 4623, 1, 3, 1, 1, 3 } }, + { LLDP_SNMP_DOT3_AUTONEG_MAU, ASN_INTEGER, RONLY, agent_h_remote_port, 8, + { 1, 5, 4623, 1, 3, 1, 1, 4 } }, + { LLDP_SNMP_DOT3_POWER_DEVICETYPE, ASN_INTEGER, RONLY, agent_h_remote_port, 8, + { 1, 5, 4623, 1, 3, 2, 1, 1 } }, + { LLDP_SNMP_DOT3_POWER_SUPPORT, ASN_INTEGER, RONLY, agent_h_remote_port, 8, + { 1, 5, 4623, 1, 3, 2, 1, 2 } }, + { LLDP_SNMP_DOT3_POWER_ENABLED, ASN_INTEGER, RONLY, agent_h_remote_port, 8, + { 1, 5, 4623, 1, 3, 2, 1, 3 } }, + { LLDP_SNMP_DOT3_POWER_PAIRCONTROL, ASN_INTEGER, RONLY, agent_h_remote_port, 8, + { 1, 5, 4623, 1, 3, 2, 1, 4 } }, + { LLDP_SNMP_DOT3_POWER_PAIRS, ASN_INTEGER, RONLY, agent_h_remote_port, 8, + { 1, 5, 4623, 1, 3, 2, 1, 5 } }, + { LLDP_SNMP_DOT3_POWER_CLASS, ASN_INTEGER, RONLY, agent_h_remote_port, 8, + { 1, 5, 4623, 1, 3, 2, 1, 6 } }, + { LLDP_SNMP_DOT3_POWER_TYPE, ASN_OCTET_STR, RONLY, agent_h_remote_port, 8, + { 1, 5, 4623, 1, 3, 2, 1, 7 } }, + { LLDP_SNMP_DOT3_POWER_SOURCE, ASN_OCTET_STR, RONLY, agent_h_remote_port, 8, + { 1, 5, 4623, 1, 3, 2, 1, 8 } }, + { LLDP_SNMP_DOT3_POWER_PRIORITY, ASN_INTEGER, RONLY, agent_h_remote_port, 8, + { 1, 5, 4623, 1, 3, 2, 1, 9 } }, + { LLDP_SNMP_DOT3_POWER_REQUESTED, ASN_INTEGER, RONLY, agent_h_remote_port, 8, + { 1, 5, 4623, 1, 3, 2, 1, 10 } }, + { LLDP_SNMP_DOT3_POWER_ALLOCATED, ASN_INTEGER, RONLY, agent_h_remote_port, 8, + { 1, 5, 4623, 1, 3, 2, 1, 11 } }, + { LLDP_SNMP_DOT3_AGG_STATUS, ASN_OCTET_STR, RONLY, agent_h_remote_port, 8, + { 1, 5, 4623, 1, 3, 3, 1, 1 } }, + { LLDP_SNMP_DOT3_AGG_ID, ASN_INTEGER, RONLY, agent_h_remote_port, 8, + { 1, 5, 4623, 1, 3, 3, 1, 2 } }, + { LLDP_SNMP_DOT3_MFS, ASN_INTEGER, RONLY, agent_h_remote_port, 8, + { 1, 5, 4623, 1, 3, 4, 1, 1 } }, +#endif +#ifdef ENABLE_LLDPMED + /* LLDP-MED local */ + { LLDP_SNMP_MED_CLASS, ASN_INTEGER, RONLY, agent_h_local_med, 6, + { 1, 5, 4795, 1, 1, 1 } }, + { LLDP_SNMP_MED_POLICY_VID, ASN_INTEGER, RONLY, agent_h_local_med_policy, 8, + { 1, 5, 4795, 1, 2, 1, 1, 2 } }, + { LLDP_SNMP_MED_POLICY_PRIO, ASN_INTEGER, RONLY, agent_h_local_med_policy, 8, + { 1, 5, 4795, 1, 2, 1, 1, 3 } }, + { LLDP_SNMP_MED_POLICY_DSCP, ASN_INTEGER, RONLY, agent_h_local_med_policy, 8, + { 1, 5, 4795, 1, 2, 1, 1, 4 } }, + { LLDP_SNMP_MED_POLICY_UNKNOWN, ASN_INTEGER, RONLY, agent_h_local_med_policy, 8, + { 1, 5, 4795, 1, 2, 1, 1, 5 } }, + { LLDP_SNMP_MED_POLICY_TAGGED, ASN_INTEGER, RONLY, agent_h_local_med_policy, 8, + { 1, 5, 4795, 1, 2, 1, 1, 6 } }, + { LLDP_SNMP_MED_HW, ASN_OCTET_STR, RONLY, agent_h_local_med, 6, + { 1, 5, 4795, 1, 2, 2 } }, + { LLDP_SNMP_MED_FW, ASN_OCTET_STR, RONLY, agent_h_local_med, 6, + { 1, 5, 4795, 1, 2, 3 } }, + { LLDP_SNMP_MED_SW, ASN_OCTET_STR, RONLY, agent_h_local_med, 6, + { 1, 5, 4795, 1, 2, 4 } }, + { LLDP_SNMP_MED_SN, ASN_OCTET_STR, RONLY, agent_h_local_med, 6, + { 1, 5, 4795, 1, 2, 5 } }, + { LLDP_SNMP_MED_MANUF, ASN_OCTET_STR, RONLY, agent_h_local_med, 6, + { 1, 5, 4795, 1, 2, 6 } }, + { LLDP_SNMP_MED_MODEL, ASN_OCTET_STR, RONLY, agent_h_local_med, 6, + { 1, 5, 4795, 1, 2, 7 } }, + { LLDP_SNMP_MED_ASSET, ASN_OCTET_STR, RONLY, agent_h_local_med, 6, + { 1, 5, 4795, 1, 2, 8 } }, + { LLDP_SNMP_MED_LOCATION, ASN_OCTET_STR, RONLY, agent_h_local_med_location, 8, + { 1, 5, 4795, 1, 2, 9, 1, 2 } }, + { LLDP_SNMP_MED_POE_DEVICETYPE, ASN_INTEGER, RONLY, agent_h_local_med_power, 6, + { 1, 5, 4795, 1, 2, 10 } }, + { LLDP_SNMP_MED_POE_PSE_POWERVAL, ASN_GAUGE, RONLY, agent_h_local_med_power, 8, + { 1, 5, 4795, 1, 2, 11, 1, 1 } }, + { LLDP_SNMP_MED_POE_PSE_POWERPRIORITY, ASN_INTEGER, RONLY, + agent_h_local_med_power, 8, { 1, 5, 4795, 1, 2, 11, 1, 2 } }, + { LLDP_SNMP_MED_POE_PSE_POWERSOURCE, ASN_INTEGER, RONLY, + agent_h_local_med_power, 6, { 1, 5, 4795, 1, 2, 12 } }, + { LLDP_SNMP_MED_POE_PD_POWERVAL, ASN_GAUGE, RONLY, agent_h_local_med_power, 6, + { 1, 5, 4795, 1, 2, 13 } }, + { LLDP_SNMP_MED_POE_PD_POWERSOURCE, ASN_INTEGER, RONLY, agent_h_local_med_power, + 6, { 1, 5, 4795, 1, 2, 14 } }, + { LLDP_SNMP_MED_POE_PD_POWERPRIORITY, ASN_INTEGER, RONLY, + agent_h_local_med_power, 6, { 1, 5, 4795, 1, 2, 15 } }, + /* LLDP-MED remote */ + { LLDP_SNMP_MED_CAP_AVAILABLE, ASN_OCTET_STR, RONLY, agent_h_remote_med, 8, + { 1, 5, 4795, 1, 3, 1, 1, 1 } }, + { LLDP_SNMP_MED_CAP_ENABLED, ASN_OCTET_STR, RONLY, agent_h_remote_med, 8, + { 1, 5, 4795, 1, 3, 1, 1, 2 } }, + { LLDP_SNMP_MED_CLASS, ASN_INTEGER, RONLY, agent_h_remote_med, 8, + { 1, 5, 4795, 1, 3, 1, 1, 3 } }, + { LLDP_SNMP_MED_POLICY_VID, ASN_INTEGER, RONLY, agent_h_remote_med_policy, 8, + { 1, 5, 4795, 1, 3, 2, 1, 2 } }, + { LLDP_SNMP_MED_POLICY_PRIO, ASN_INTEGER, RONLY, agent_h_remote_med_policy, 8, + { 1, 5, 4795, 1, 3, 2, 1, 3 } }, + { LLDP_SNMP_MED_POLICY_DSCP, ASN_INTEGER, RONLY, agent_h_remote_med_policy, 8, + { 1, 5, 4795, 1, 3, 2, 1, 4 } }, + { LLDP_SNMP_MED_POLICY_UNKNOWN, ASN_INTEGER, RONLY, agent_h_remote_med_policy, + 8, { 1, 5, 4795, 1, 3, 2, 1, 5 } }, + { LLDP_SNMP_MED_POLICY_TAGGED, ASN_INTEGER, RONLY, agent_h_remote_med_policy, 8, + { 1, 5, 4795, 1, 3, 2, 1, 6 } }, + { LLDP_SNMP_MED_HW, ASN_OCTET_STR, RONLY, agent_h_remote_med, 8, + { 1, 5, 4795, 1, 3, 3, 1, 1 } }, + { LLDP_SNMP_MED_FW, ASN_OCTET_STR, RONLY, agent_h_remote_med, 8, + { 1, 5, 4795, 1, 3, 3, 1, 2 } }, + { LLDP_SNMP_MED_SW, ASN_OCTET_STR, RONLY, agent_h_remote_med, 8, + { 1, 5, 4795, 1, 3, 3, 1, 3 } }, + { LLDP_SNMP_MED_SN, ASN_OCTET_STR, RONLY, agent_h_remote_med, 8, + { 1, 5, 4795, 1, 3, 3, 1, 4 } }, + { LLDP_SNMP_MED_MANUF, ASN_OCTET_STR, RONLY, agent_h_remote_med, 8, + { 1, 5, 4795, 1, 3, 3, 1, 5 } }, + { LLDP_SNMP_MED_MODEL, ASN_OCTET_STR, RONLY, agent_h_remote_med, 8, + { 1, 5, 4795, 1, 3, 3, 1, 6 } }, + { LLDP_SNMP_MED_ASSET, ASN_OCTET_STR, RONLY, agent_h_remote_med, 8, + { 1, 5, 4795, 1, 3, 3, 1, 7 } }, + { LLDP_SNMP_MED_LOCATION, ASN_OCTET_STR, RONLY, agent_h_remote_med_location, 8, + { 1, 5, 4795, 1, 3, 4, 1, 2 } }, + { LLDP_SNMP_MED_POE_DEVICETYPE, ASN_INTEGER, RONLY, agent_h_remote_med_power, 8, + { 1, 5, 4795, 1, 3, 5, 1, 1 } }, + { LLDP_SNMP_MED_POE_PSE_POWERVAL, ASN_GAUGE, RONLY, agent_h_remote_med_power, 8, + { 1, 5, 4795, 1, 3, 6, 1, 1 } }, + { LLDP_SNMP_MED_POE_PSE_POWERSOURCE, ASN_INTEGER, RONLY, + agent_h_remote_med_power, 8, { 1, 5, 4795, 1, 3, 6, 1, 2 } }, + { LLDP_SNMP_MED_POE_PSE_POWERPRIORITY, ASN_INTEGER, RONLY, + agent_h_remote_med_power, 8, { 1, 5, 4795, 1, 3, 6, 1, 3 } }, + { LLDP_SNMP_MED_POE_PD_POWERVAL, ASN_GAUGE, RONLY, agent_h_remote_med_power, 8, + { 1, 5, 4795, 1, 3, 7, 1, 1 } }, + { LLDP_SNMP_MED_POE_PD_POWERSOURCE, ASN_INTEGER, RONLY, + agent_h_remote_med_power, 8, { 1, 5, 4795, 1, 3, 7, 1, 2 } }, + { LLDP_SNMP_MED_POE_PD_POWERPRIORITY, ASN_INTEGER, RONLY, + agent_h_remote_med_power, 8, { 1, 5, 4795, 1, 3, 7, 1, 3 } }, +#endif +/* Dot1, local and remote ports */ +#ifdef ENABLE_DOT1 + { LLDP_SNMP_DOT1_PVID, ASN_INTEGER, RONLY, agent_h_local_port, 8, + { 1, 5, 32962, 1, 2, 1, 1, 1 } }, + { LLDP_SNMP_DOT1_PPVLAN_SUPPORTED, ASN_INTEGER, RONLY, agent_h_local_ppvid, 8, + { 1, 5, 32962, 1, 2, 2, 1, 2 } }, + { LLDP_SNMP_DOT1_PPVLAN_ENABLED, ASN_INTEGER, RONLY, agent_h_local_ppvid, 8, + { 1, 5, 32962, 1, 2, 2, 1, 3 } }, + { LLDP_SNMP_DOT1_VLANNAME, ASN_OCTET_STR, RONLY, agent_h_local_vlan, 8, + { 1, 5, 32962, 1, 2, 3, 1, 2 } }, + { LLDP_SNMP_DOT1_PI, ASN_OCTET_STR, RONLY, agent_h_local_pi, 8, + { 1, 5, 32962, 1, 2, 4, 1, 2 } }, +#endif +#ifdef ENABLE_DOT1 + { LLDP_SNMP_DOT1_PVID, ASN_INTEGER, RONLY, agent_h_remote_port, 8, + { 1, 5, 32962, 1, 3, 1, 1, 1 } }, + { LLDP_SNMP_DOT1_PPVLAN_SUPPORTED, ASN_INTEGER, RONLY, agent_h_remote_ppvid, 8, + { 1, 5, 32962, 1, 3, 2, 1, 2 } }, + { LLDP_SNMP_DOT1_PPVLAN_ENABLED, ASN_INTEGER, RONLY, agent_h_remote_ppvid, 8, + { 1, 5, 32962, 1, 3, 2, 1, 3 } }, + /* Remote vlans */ + { LLDP_SNMP_DOT1_VLANNAME, ASN_OCTET_STR, RONLY, agent_h_remote_vlan, 8, + { 1, 5, 32962, 1, 3, 3, 1, 2 } }, + /* Protocol identity */ + { LLDP_SNMP_DOT1_PI, ASN_OCTET_STR, RONLY, agent_h_remote_pi, 8, + { 1, 5, 32962, 1, 3, 4, 1, 2 } }, +#endif +}; +size_t +agent_lldp_vars_size(void) +{ + return sizeof(agent_lldp_vars) / sizeof(struct variable8); +} + +/** + * Send a notification about a change in one remote neighbor. + * + * @param hardware Interface on which the change has happened. + * @param type Type of change (add, delete, update) + * @param rport Changed remote port + */ +void +agent_notify(struct lldpd_hardware *hardware, int type, struct lldpd_port *rport) +{ + struct lldpd_hardware *h; + + /* OID of the notification */ + oid notification_oid[] = { LLDP_OID, 0, 0, 1 }; + size_t notification_oid_len = OID_LENGTH(notification_oid); + /* OID for snmpTrapOID.0 */ + oid objid_snmptrap[] = { SNMPTRAP_OID }; + size_t objid_snmptrap_len = OID_LENGTH(objid_snmptrap); + + /* Other OID */ + oid inserts_oid[] = { LLDP_OID, 1, 2, 2 }; + size_t inserts_oid_len = OID_LENGTH(inserts_oid); + unsigned long inserts = 0; + + oid deletes_oid[] = { LLDP_OID, 1, 2, 3 }; + size_t deletes_oid_len = OID_LENGTH(deletes_oid); + unsigned long deletes = 0; + + oid drops_oid[] = { LLDP_OID, 1, 2, 4 }; + size_t drops_oid_len = OID_LENGTH(drops_oid); + unsigned long drops = 0; + + oid ageouts_oid[] = { LLDP_OID, 1, 2, 5 }; + size_t ageouts_oid_len = OID_LENGTH(ageouts_oid); + unsigned long ageouts = 0; + + /* We also add some extra. Easy ones. */ + oid locport_oid[] = { LLDP_OID, 1, 3, 7, 1, 4, hardware->h_ifindex }; + size_t locport_oid_len = OID_LENGTH(locport_oid); + oid sysname_oid[] = { LLDP_OID, 1, 4, 1, 1, 9, lastchange(rport), + hardware->h_ifindex, rport->p_chassis->c_index }; + size_t sysname_oid_len = OID_LENGTH(sysname_oid); + oid portdescr_oid[] = { LLDP_OID, 1, 4, 1, 1, 8, lastchange(rport), + hardware->h_ifindex, rport->p_chassis->c_index }; + size_t portdescr_oid_len = OID_LENGTH(portdescr_oid); + + netsnmp_variable_list *notification_vars = NULL; + + if (!hardware->h_cfg->g_snmp) return; + + switch (type) { + case NEIGHBOR_CHANGE_DELETED: + log_debug("snmp", "send notification for neighbor deleted on %s", + hardware->h_ifname); + break; + case NEIGHBOR_CHANGE_UPDATED: + log_debug("snmp", "send notification for neighbor updated on %s", + hardware->h_ifname); + break; + case NEIGHBOR_CHANGE_ADDED: + log_debug("snmp", "send notification for neighbor added on %s", + hardware->h_ifname); + break; + } + + TAILQ_FOREACH (h, &hardware->h_cfg->g_hardware, h_entries) { + inserts += h->h_insert_cnt; + deletes += h->h_delete_cnt; + ageouts += h->h_ageout_cnt; + drops += h->h_drop_cnt; + } + + /* snmpTrapOID */ + snmp_varlist_add_variable(¬ification_vars, objid_snmptrap, + objid_snmptrap_len, ASN_OBJECT_ID, (u_char *)notification_oid, + notification_oid_len * sizeof(oid)); + + snmp_varlist_add_variable(¬ification_vars, inserts_oid, inserts_oid_len, + ASN_GAUGE, (u_char *)&inserts, sizeof(inserts)); + snmp_varlist_add_variable(¬ification_vars, deletes_oid, deletes_oid_len, + ASN_GAUGE, (u_char *)&deletes, sizeof(inserts)); + snmp_varlist_add_variable(¬ification_vars, drops_oid, drops_oid_len, + ASN_GAUGE, (u_char *)&drops, sizeof(drops)); + snmp_varlist_add_variable(¬ification_vars, ageouts_oid, ageouts_oid_len, + ASN_GAUGE, (u_char *)&ageouts, sizeof(ageouts)); + + if (type != NEIGHBOR_CHANGE_DELETED) { + snmp_varlist_add_variable(¬ification_vars, locport_oid, + locport_oid_len, ASN_OCTET_STR, (u_char *)hardware->h_ifname, + strnlen(hardware->h_ifname, IFNAMSIZ)); + if (rport->p_chassis->c_name && *rport->p_chassis->c_name != '\0') { + snmp_varlist_add_variable(¬ification_vars, sysname_oid, + sysname_oid_len, ASN_OCTET_STR, + (u_char *)rport->p_chassis->c_name, + strlen(rport->p_chassis->c_name)); + } + if (rport->p_descr) { + snmp_varlist_add_variable(¬ification_vars, portdescr_oid, + portdescr_oid_len, ASN_OCTET_STR, (u_char *)rport->p_descr, + strlen(rport->p_descr)); + } + } + + log_debug("snmp", "sending SNMP trap (%ld, %ld, %ld)", inserts, deletes, + ageouts); + send_v2trap(notification_vars); + snmp_free_varbind(notification_vars); +} + +/* Logging NetSNMP messages */ +static int +agent_log_callback(int major, int minor, void *serverarg, void *clientarg) +{ + struct snmp_log_message *slm = (struct snmp_log_message *)serverarg; + char *msg = strdup(slm->msg); + (void)major; + (void)minor; + (void)clientarg; + + if (msg && msg[strlen(msg) - 1] == '\n') msg[strlen(msg) - 1] = '\0'; + switch (slm->priority) { + case LOG_EMERG: + log_warnx("libsnmp", "%s", msg ? msg : slm->msg); + break; + case LOG_ALERT: + log_warnx("libsnmp", "%s", msg ? msg : slm->msg); + break; + case LOG_CRIT: + log_warnx("libsnmp", "%s", msg ? msg : slm->msg); + break; + case LOG_ERR: + log_warnx("libsnmp", "%s", msg ? msg : slm->msg); + break; + case LOG_WARNING: + log_warnx("libsnmp", "%s", msg ? msg : slm->msg); + break; + case LOG_NOTICE: + log_info("libsnmp", "%s", msg ? msg : slm->msg); + break; + case LOG_INFO: + log_info("libsnmp", "%s", msg ? msg : slm->msg); + break; + case LOG_DEBUG: + log_debug("libsnmp", "%s", msg ? msg : slm->msg); + break; + } + free(msg); + return SNMP_ERR_NOERROR; +} + +void +agent_init(struct lldpd *cfg, const char *agentx) +{ + int rc; + + log_info("snmp", "enable SNMP subagent"); + netsnmp_enable_subagent(); + + log_debug("snmp", "enable logging"); + snmp_disable_log(); + snmp_enable_calllog(); + snmp_register_callback(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_LOGGING, + agent_log_callback, NULL); + + scfg = cfg; + + /* We are chrooted, we don't want to handle persistent states */ + netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DONT_PERSIST_STATE, + TRUE); + /* Do not load any MIB */ + setenv("MIBS", "", 1); + setenv("MIBDIRS", "/dev/null", 1); + +#ifdef ENABLE_PRIVSEP + /* We provide our UNIX domain transport */ + log_debug("snmp", "register UNIX domain transport"); + agent_priv_register_domain(); +#endif + + if (agentx) + netsnmp_ds_set_string(NETSNMP_DS_APPLICATION_ID, + NETSNMP_DS_AGENT_X_SOCKET, agentx); + init_agent("lldpAgent"); + REGISTER_MIB("lldp", agent_lldp_vars, variable8, lldp_oid); + init_snmp("lldpAgent"); + + log_debug("snmp", "register to sysORTable"); + if ((rc = register_sysORTable(lldp_oid, OID_LENGTH(lldp_oid), + "lldpMIB implementation by lldpd")) != 0) + log_warnx("snmp", "unable to register to sysORTable (%d)", rc); +} + +void +agent_shutdown() +{ + log_debug("snmp", "agent shutdown"); + unregister_sysORTable(lldp_oid, OID_LENGTH(lldp_oid)); + snmp_shutdown("lldpAgent"); +} diff --git a/src/daemon/agent.h b/src/daemon/agent.h new file mode 100644 index 0000000..b498aeb --- /dev/null +++ b/src/daemon/agent.h @@ -0,0 +1,35 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _AGENT_H +#define _AGENT_H + +#include <net-snmp/net-snmp-config.h> +#include <net-snmp/net-snmp-includes.h> +#include <net-snmp/agent/net-snmp-agent-includes.h> +#include <net-snmp/agent/snmp_vars.h> + +#ifndef RONLY +# define RONLY NETSNMP_OLDAPI_RONLY +#endif + +#define LLDP_OID 1, 0, 8802, 1, 1, 2 +#define SNMPTRAP_OID 1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0 +static oid lldp_oid[] = { LLDP_OID }; +size_t agent_lldp_vars_size(void); + +#endif diff --git a/src/daemon/agent_priv.c b/src/daemon/agent_priv.c new file mode 100644 index 0000000..b5cf72a --- /dev/null +++ b/src/daemon/agent_priv.c @@ -0,0 +1,243 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* Some of the code here (agent_priv_unix_*) has been adapted from code from + * Net-SNMP project (snmplib/snmpUnixDomain.c). Net-SNMP project is licensed + * using BSD and BSD-like licenses. I don't know the exact license of the file + * snmplib/snmpUnixDomain.c. */ + +#include "lldpd.h" + +#include <unistd.h> +#include <errno.h> +#include <poll.h> + +#ifdef ENABLE_PRIVSEP +# include <net-snmp/net-snmp-config.h> +# include <net-snmp/net-snmp-includes.h> +# include <net-snmp/agent/net-snmp-agent-includes.h> +# include <net-snmp/agent/snmp_vars.h> +# include <net-snmp/library/snmpUnixDomain.h> + +# ifdef ASN_PRIV_STOP +/* NetSNMP 5.8+ */ +# define F_SEND_SIGNATURE \ + netsnmp_transport *t, const void *buf, int size, void **opaque, int *olength +# define F_FMTADDR_SIGNATURE netsnmp_transport *t, const void *data, int len +# define F_FROM_OSTRING_SIGNATURE const void *o, size_t o_len, int local +# else +# define F_SEND_SIGNATURE \ + netsnmp_transport *t, void *buf, int size, void **opaque, int *olength +# define F_FMTADDR_SIGNATURE netsnmp_transport *t, void *data, int len +# define F_FROM_OSTRING_SIGNATURE const u_char *o, size_t o_len, int local +# endif + +static oid netsnmp_unix[] = { TRANSPORT_DOMAIN_LOCAL }; +static netsnmp_tdomain unixDomain; + +static char * +agent_priv_unix_fmtaddr(F_FMTADDR_SIGNATURE) +{ + /* We don't bother to implement the full function */ + return strdup("Local Unix socket with privilege separation: unknown"); +} + +static int +agent_priv_unix_recv(netsnmp_transport *t, void *buf, int size, void **opaque, + int *olength) +{ + int rc = -1; + socklen_t tolen = sizeof(struct sockaddr_un); + struct sockaddr *to = NULL; + + if (t == NULL || t->sock < 0) goto recv_error; + to = (struct sockaddr *)calloc(1, sizeof(struct sockaddr_un)); + if (to == NULL) goto recv_error; + if (getsockname(t->sock, to, &tolen) != 0) goto recv_error; + while (rc < 0) { + rc = recv(t->sock, buf, size, 0); + /* TODO: handle the (unlikely) case where we get EAGAIN or EWOULDBLOCK + */ + if (rc < 0 && errno != EINTR) { + log_warn("snmp", "unable to receive from fd %d", t->sock); + goto recv_error; + } + } + *opaque = (void *)to; + *olength = sizeof(struct sockaddr_un); + return rc; + +recv_error: + free(to); + *opaque = NULL; + *olength = 0; + return -1; +} + +# define AGENT_WRITE_TIMEOUT 2000 +static int +agent_priv_unix_send(F_SEND_SIGNATURE) +{ + int rc = -1; + + if (t != NULL && t->sock >= 0) { + struct pollfd sagentx = { .fd = t->sock, + .events = POLLOUT | POLLERR | POLLHUP }; + while (rc < 0) { + rc = poll(&sagentx, 1, AGENT_WRITE_TIMEOUT); + if (rc == 0) { + log_warnx("snmp", + "timeout while communicating with the master agent"); + rc = -1; + break; + } + if (rc > 0) { + /* We can either write or have an error somewhere */ + rc = send(t->sock, buf, size, 0); + if (rc < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK || + errno == EINTR) + /* Let's retry */ + continue; + log_warn("snmp", + "error while sending to master agent"); + break; + } + } else { + if (errno != EINTR) { + log_warn("snmp", + "error while attempting to send to master agent"); + break; + } + continue; + } + } + } + return rc; +} + +static int +agent_priv_unix_close(netsnmp_transport *t) +{ + int rc = 0; + + if (t->sock >= 0) { + rc = close(t->sock); + t->sock = -1; + return rc; + } + return -1; +} + +static int +agent_priv_unix_accept(netsnmp_transport *t) +{ + log_warnx("snmp", "should not have been called"); + return -1; +} + +static netsnmp_transport * +agent_priv_unix_transport(const char *string, int len, int local) +{ + struct sockaddr_un addr = { .sun_family = AF_UNIX }; + netsnmp_transport *t = NULL; + + if (local) { + log_warnx("snmp", "should not have been called for local transport"); + return NULL; + } + if (!string) return NULL; + if (len >= sizeof(addr.sun_path) || + strlcpy(addr.sun_path, string, sizeof(addr.sun_path)) >= + sizeof(addr.sun_path)) { + log_warnx("snmp", "path too long for Unix domain transport"); + return NULL; + } + + if ((t = (netsnmp_transport *)calloc(1, sizeof(netsnmp_transport))) == NULL) + return NULL; + + t->domain = netsnmp_unix; + t->domain_length = sizeof(netsnmp_unix) / sizeof(netsnmp_unix[0]); + + if ((t->sock = priv_snmp_socket(&addr)) < 0) { + netsnmp_transport_free(t); + return NULL; + } + + t->flags = NETSNMP_TRANSPORT_FLAG_STREAM; + + if ((t->remote = (u_char *)calloc(1, strlen(addr.sun_path) + 1)) == NULL) { + agent_priv_unix_close(t); + netsnmp_transport_free(t); + return NULL; + } + memcpy(t->remote, addr.sun_path, strlen(addr.sun_path)); + t->remote_length = strlen(addr.sun_path); + + t->msgMaxSize = 0x7fffffff; + t->f_recv = agent_priv_unix_recv; + t->f_send = agent_priv_unix_send; + t->f_close = agent_priv_unix_close; + t->f_accept = agent_priv_unix_accept; + t->f_fmtaddr = agent_priv_unix_fmtaddr; + + return t; +} + +# if HAVE_NETSNMP_TDOMAIN_F_CREATE_FROM_TSTRING_NEW +static netsnmp_transport * +agent_priv_unix_create_tstring_new(const char *string, int local, + const char *default_target) +{ + if ((!string || *string == '\0') && default_target && *default_target != '\0') { + string = default_target; + } + if (!string) return NULL; + return agent_priv_unix_transport(string, strlen(string), local); +} +# else +static netsnmp_transport * +agent_priv_unix_create_tstring(const char *string, int local) +{ + if (!string) return NULL; + return agent_priv_unix_transport(string, strlen(string), local); +} +# endif + +static netsnmp_transport * +agent_priv_unix_create_ostring(F_FROM_OSTRING_SIGNATURE) +{ + return agent_priv_unix_transport((char *)o, o_len, local); +} + +void +agent_priv_register_domain() +{ + unixDomain.name = netsnmp_unix; + unixDomain.name_length = sizeof(netsnmp_unix) / sizeof(oid); + unixDomain.prefix = (const char **)calloc(2, sizeof(char *)); + unixDomain.prefix[0] = "unix"; +# if HAVE_NETSNMP_TDOMAIN_F_CREATE_FROM_TSTRING_NEW + unixDomain.f_create_from_tstring_new = agent_priv_unix_create_tstring_new; +# else + unixDomain.f_create_from_tstring = agent_priv_unix_create_tstring; +# endif + unixDomain.f_create_from_ostring = agent_priv_unix_create_ostring; + netsnmp_tdomain_register(&unixDomain); +} +#endif diff --git a/src/daemon/bitmap.c b/src/daemon/bitmap.c new file mode 100644 index 0000000..c7a6330 --- /dev/null +++ b/src/daemon/bitmap.c @@ -0,0 +1,63 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2020 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. + */ + +/* Helpers around bitmaps */ + +#include "lldpd.h" + +/* + * Set vlan id in the bitmap + */ +void +bitmap_set(uint32_t *bmap, uint16_t vlan_id) +{ + if (vlan_id < MAX_VLAN) bmap[vlan_id / 32] |= (((uint32_t)1) << (vlan_id % 32)); +} + +/* + * Checks if the bitmap is empty + */ +int +bitmap_isempty(uint32_t *bmap) +{ + int i; + + for (i = 0; i < VLAN_BITMAP_LEN; i++) { + if (bmap[i] != 0) return 0; + } + + return 1; +} + +/* + * Calculate the number of bits set in the bitmap to get total + * number of VLANs + */ +unsigned int +bitmap_numbits(uint32_t *bmap) +{ + unsigned int num = 0; + + for (int i = 0; (i < VLAN_BITMAP_LEN); i++) { + uint32_t v = bmap[i]; + v = v - ((v >> 1) & 0x55555555); + v = (v & 0x33333333) + ((v >> 2) & 0x33333333); + num += (((v + (v >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24; + } + + return num; +} diff --git a/src/daemon/client.c b/src/daemon/client.c new file mode 100644 index 0000000..d9d907f --- /dev/null +++ b/src/daemon/client.c @@ -0,0 +1,700 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "lldpd.h" +#include "trace.h" + +#include <sys/utsname.h> + +static ssize_t +client_handle_none(struct lldpd *cfg, enum hmsg_type *type, void *input, int input_len, + void **output, int *subscribed) +{ + log_info("rpc", "received noop request from client"); + *type = NONE; + return 0; +} + +/* Return the global configuration */ +static ssize_t +client_handle_get_configuration(struct lldpd *cfg, enum hmsg_type *type, void *input, + int input_len, void **output, int *subscribed) +{ + ssize_t output_len; + log_debug("rpc", "client requested configuration"); + output_len = lldpd_config_serialize(&cfg->g_config, output); + if (output_len <= 0) { + output_len = 0; + *type = NONE; + } + return output_len; +} + +static char * +xstrdup(const char *str) +{ + if (!str) return NULL; + return strdup(str); +} + +/* Change the global configuration */ +static ssize_t +client_handle_set_configuration(struct lldpd *cfg, enum hmsg_type *type, void *input, + int input_len, void **output, int *subscribed) +{ + struct lldpd_config *config; + + log_debug("rpc", "client request a change in configuration"); + /* Get the proposed configuration. */ + if (lldpd_config_unserialize(input, input_len, &config) <= 0) { + *type = NONE; + return 0; + } + +#define CHANGED(w) (config->w != cfg->g_config.w) +#define CHANGED_STR(w) \ + (!(config->w == cfg->g_config.w || \ + (config->w && cfg->g_config.w && !strcmp(config->w, cfg->g_config.w)))) + + /* What needs to be done? Transmit delay? */ + if (CHANGED(c_tx_interval) && config->c_tx_interval != 0) { + if (config->c_tx_interval < 0) { + log_debug("rpc", "client asked for immediate retransmission"); + } else { + log_debug("rpc", "client change transmit interval to %d ms", + config->c_tx_interval); + cfg->g_config.c_tx_interval = config->c_tx_interval; + cfg->g_config.c_ttl = + cfg->g_config.c_tx_interval * cfg->g_config.c_tx_hold; + cfg->g_config.c_ttl = (cfg->g_config.c_ttl + 999) / 1000; + } + levent_send_now(cfg); + } + if (CHANGED(c_tx_hold) && config->c_tx_hold > 0) { + log_debug("rpc", "client change transmit hold to %d", + config->c_tx_hold); + cfg->g_config.c_tx_hold = config->c_tx_hold; + cfg->g_config.c_ttl = + cfg->g_config.c_tx_interval * cfg->g_config.c_tx_hold; + cfg->g_config.c_ttl = (cfg->g_config.c_ttl + 999) / 1000; + } + if (CHANGED(c_max_neighbors) && config->c_max_neighbors > 0) { + log_debug("rpc", "client change maximum neighbors to %d", + config->c_max_neighbors); + cfg->g_config.c_max_neighbors = config->c_max_neighbors; + } + if (CHANGED(c_lldp_portid_type) && + config->c_lldp_portid_type > LLDP_PORTID_SUBTYPE_UNKNOWN && + config->c_lldp_portid_type <= LLDP_PORTID_SUBTYPE_MAX) { + log_debug("rpc", "change lldp portid tlv subtype to %d", + config->c_lldp_portid_type); + cfg->g_config.c_lldp_portid_type = config->c_lldp_portid_type; + levent_update_now(cfg); + } + if (CHANGED(c_lldp_agent_type) && + config->c_lldp_agent_type > LLDP_AGENT_TYPE_UNKNOWN && + config->c_lldp_agent_type <= LLDP_AGENT_TYPE_MAX) { + log_debug("rpc", "change lldp agent type to %d", + config->c_lldp_agent_type); + cfg->g_config.c_lldp_agent_type = config->c_lldp_agent_type; + levent_update_now(cfg); + } + /* Pause/resume */ + if (CHANGED(c_paused)) { + log_debug("rpc", "client asked to %s lldpd", + config->c_paused ? "pause" : "resume"); + cfg->g_config.c_paused = config->c_paused; + levent_send_now(cfg); + } + +#ifdef ENABLE_LLDPMED + if (CHANGED(c_enable_fast_start)) { + cfg->g_config.c_enable_fast_start = config->c_enable_fast_start; + log_debug("rpc", "client asked to %s fast start", + cfg->g_config.c_enable_fast_start ? "enable" : "disable"); + } + if (CHANGED(c_tx_fast_interval) && config->c_tx_fast_interval > 0) { + log_debug("rpc", "change fast interval to %d", + config->c_tx_fast_interval); + cfg->g_config.c_tx_fast_interval = config->c_tx_fast_interval; + } +#endif + if (CHANGED_STR(c_iface_pattern)) { + log_debug("rpc", "change interface pattern to %s", + config->c_iface_pattern ? config->c_iface_pattern : "(NULL)"); + free(cfg->g_config.c_iface_pattern); + cfg->g_config.c_iface_pattern = xstrdup(config->c_iface_pattern); + levent_update_now(cfg); + } + if (CHANGED_STR(c_perm_ifaces)) { + log_debug("rpc", "change permanent interface pattern to %s", + config->c_perm_ifaces ? config->c_perm_ifaces : "(NULL)"); + free(cfg->g_config.c_perm_ifaces); + cfg->g_config.c_perm_ifaces = xstrdup(config->c_perm_ifaces); + levent_update_now(cfg); + } + if (CHANGED_STR(c_mgmt_pattern)) { + log_debug("rpc", "change management pattern to %s", + config->c_mgmt_pattern ? config->c_mgmt_pattern : "(NULL)"); + free(cfg->g_config.c_mgmt_pattern); + cfg->g_config.c_mgmt_pattern = xstrdup(config->c_mgmt_pattern); + levent_update_now(cfg); + } + if (CHANGED_STR(c_cid_string)) { + log_debug("rpc", "change chassis ID string to %s", + config->c_cid_string ? config->c_cid_string : "(NULL)"); + free(cfg->g_config.c_cid_string); + cfg->g_config.c_cid_string = xstrdup(config->c_cid_string); + free(LOCAL_CHASSIS(cfg)->c_id); + LOCAL_CHASSIS(cfg)->c_id = NULL; + lldpd_update_localchassis(cfg); + levent_update_now(cfg); + } + if (CHANGED_STR(c_description)) { + log_debug("rpc", "change chassis description to %s", + config->c_description ? config->c_description : "(NULL)"); + free(cfg->g_config.c_description); + cfg->g_config.c_description = xstrdup(config->c_description); + lldpd_update_localchassis(cfg); + levent_update_now(cfg); + } + if (CHANGED_STR(c_platform)) { + log_debug("rpc", "change platform description to %s", + config->c_platform ? config->c_platform : "(NULL)"); + free(cfg->g_config.c_platform); + cfg->g_config.c_platform = xstrdup(config->c_platform); + lldpd_update_localchassis(cfg); + levent_update_now(cfg); + } + if (CHANGED_STR(c_hostname)) { + log_debug("rpc", "change system name to %s", + config->c_hostname ? config->c_hostname : "(NULL)"); + free(cfg->g_config.c_hostname); + cfg->g_config.c_hostname = xstrdup(config->c_hostname); + lldpd_update_localchassis(cfg); + levent_update_now(cfg); + } + if (CHANGED(c_set_ifdescr)) { + log_debug("rpc", + "%s setting of interface description based on discovered neighbors", + config->c_set_ifdescr ? "enable" : "disable"); + cfg->g_config.c_set_ifdescr = config->c_set_ifdescr; + levent_update_now(cfg); + } + if (CHANGED(c_promisc)) { + log_debug("rpc", "%s promiscuous mode on managed interfaces", + config->c_promisc ? "enable" : "disable"); + cfg->g_config.c_promisc = config->c_promisc; + levent_update_now(cfg); + } + if (CHANGED(c_cap_advertise)) { + log_debug("rpc", "%s chassis capabilities advertisement", + config->c_cap_advertise ? "enable" : "disable"); + cfg->g_config.c_cap_advertise = config->c_cap_advertise; + levent_update_now(cfg); + } + if (CHANGED(c_cap_override)) { + log_debug("rpc", "%s chassis capabilities override", + config->c_cap_override ? "enable" : "disable"); + cfg->g_config.c_cap_override = config->c_cap_override; + levent_update_now(cfg); + } + if (CHANGED(c_mgmt_advertise)) { + log_debug("rpc", "%s management addresses advertisement", + config->c_mgmt_advertise ? "enable" : "disable"); + cfg->g_config.c_mgmt_advertise = config->c_mgmt_advertise; + levent_update_now(cfg); + } + if (CHANGED(c_bond_slave_src_mac_type)) { + if (config->c_bond_slave_src_mac_type > + LLDP_BOND_SLAVE_SRC_MAC_TYPE_UNKNOWN && + config->c_bond_slave_src_mac_type <= + LLDP_BOND_SLAVE_SRC_MAC_TYPE_MAX) { + log_debug("rpc", "change bond src mac type to %d", + config->c_bond_slave_src_mac_type); + cfg->g_config.c_bond_slave_src_mac_type = + config->c_bond_slave_src_mac_type; + } else { + log_info("rpc", "Invalid bond slave src mac type: %d\n", + config->c_bond_slave_src_mac_type); + } + } + + lldpd_config_cleanup(config); + free(config); + + return 0; +} + +/* Return the list of interfaces. + Input: nothing. + Output: list of interface names (lldpd_interface_list) +*/ +static ssize_t +client_handle_get_interfaces(struct lldpd *cfg, enum hmsg_type *type, void *input, + int input_len, void **output, int *subscribed) +{ + struct lldpd_interface *iff, *iff_next; + struct lldpd_hardware *hardware; + ssize_t output_len; + + /* Build the list of interfaces */ + struct lldpd_interface_list ifs; + + log_debug("rpc", "client request the list of interfaces"); + TAILQ_INIT(&ifs); + TAILQ_FOREACH (hardware, &cfg->g_hardware, h_entries) { + if ((iff = (struct lldpd_interface *)malloc( + sizeof(struct lldpd_interface))) == NULL) + fatal("rpc", NULL); + iff->name = hardware->h_ifname; + TAILQ_INSERT_TAIL(&ifs, iff, next); + } + + output_len = lldpd_interface_list_serialize(&ifs, output); + if (output_len <= 0) { + output_len = 0; + *type = NONE; + } + + /* Free the temporary list */ + for (iff = TAILQ_FIRST(&ifs); iff != NULL; iff = iff_next) { + iff_next = TAILQ_NEXT(iff, next); + TAILQ_REMOVE(&ifs, iff, next); + free(iff); + } + + return output_len; +} + +/** + * Set local chassis info + * Input: chassis object + * Output: updated chassis object + */ +static ssize_t +client_handle_set_local_chassis(struct lldpd *cfg, enum hmsg_type *type, void *input, + int input_len, void **output, int *subscribed) +{ + struct lldpd_chassis *chassis = NULL; + struct lldpd_chassis *local_chassis = NULL; +#ifdef ENABLE_LLDPMED + struct utsname un; +#endif + + log_debug("rpc", "client request a change in chassis configuration"); + if (lldpd_chassis_unserialize(input, input_len, &chassis) <= 0) { + *type = NONE; + return 0; + } + + local_chassis = LOCAL_CHASSIS(cfg); + +#ifdef ENABLE_LLDPMED + free(local_chassis->c_med_hw); + local_chassis->c_med_hw = + (!chassis->c_med_hw) ? dmi_hw() : strdup(chassis->c_med_hw); + + // Follows lldpd.c - only set sw if advertising is enabled + if (cfg->g_config.c_advertise_version) { + free(local_chassis->c_med_sw); + + if (!chassis->c_med_sw) { + if (uname(&un) < 0) { + log_warn("rpc", + "Could not get default uname. Will continue anyway."); + local_chassis->c_med_sw = NULL; + } else { + local_chassis->c_med_sw = strdup(un.release); + } + } else { + local_chassis->c_med_sw = strdup(chassis->c_med_sw); + } + } + + free(local_chassis->c_med_fw); + local_chassis->c_med_fw = + (!chassis->c_med_fw) ? dmi_fw() : strdup(chassis->c_med_fw); + + free(local_chassis->c_med_sn); + local_chassis->c_med_sn = + (!chassis->c_med_sn) ? dmi_sn() : strdup(chassis->c_med_sn); + + free(local_chassis->c_med_manuf); + local_chassis->c_med_manuf = + (!chassis->c_med_manuf) ? dmi_manuf() : strdup(chassis->c_med_manuf); + + free(local_chassis->c_med_model); + local_chassis->c_med_model = + (!chassis->c_med_model) ? dmi_model() : strdup(chassis->c_med_model); + + free(local_chassis->c_med_asset); + local_chassis->c_med_asset = + (!chassis->c_med_asset) ? dmi_asset() : strdup(chassis->c_med_asset); +#endif + + if (chassis->c_cap_enabled != local_chassis->c_cap_enabled) { + local_chassis->c_cap_enabled = chassis->c_cap_enabled; + log_debug("rpc", "change capabilities enabled to: %d", + local_chassis->c_cap_enabled); + } + +#ifdef ENABLE_LLDPMED + log_debug("rpc", "change hardware-revision to: %s", local_chassis->c_med_hw); + log_debug("rpc", "change software-revision to: %s", local_chassis->c_med_sw); + log_debug("rpc", "change firmware-revision to: %s", local_chassis->c_med_fw); + log_debug("rpc", "change serial-number to: %s", local_chassis->c_med_sn); + log_debug("rpc", "change manufacturer to: %s", local_chassis->c_med_manuf); + log_debug("rpc", "change model to: %s", local_chassis->c_med_model); + log_debug("rpc", "change asset to: %s", local_chassis->c_med_asset); +#endif + + lldpd_chassis_cleanup(chassis, 1); + + ssize_t output_len = lldpd_chassis_serialize(local_chassis, output); + if (output_len <= 0) { + *type = NONE; + return 0; + } + + return output_len; +} + +/* Return the local chassis. + Input: nothing. + Output: local chassis (lldpd_chassis) +*/ +static ssize_t +client_handle_get_local_chassis(struct lldpd *cfg, enum hmsg_type *type, void *input, + int input_len, void **output, int *subscribed) +{ + struct lldpd_chassis *chassis = LOCAL_CHASSIS(cfg); + ssize_t output_len; + + log_debug("rpc", "client request the local chassis"); + output_len = lldpd_chassis_serialize(chassis, output); + if (output_len <= 0) { + output_len = 0; + *type = NONE; + } + + return output_len; +} + +/* Return all available information related to an interface + Input: name of the interface (serialized) + Output: Information about the interface (lldpd_hardware) +*/ +static ssize_t +client_handle_get_interface(struct lldpd *cfg, enum hmsg_type *type, void *input, + int input_len, void **output, int *subscribed) +{ + char *name; + struct lldpd_hardware *hardware; + void *p; + + /* Get name of the interface */ + if (marshal_unserialize(string, input, input_len, &p) <= 0) { + *type = NONE; + return 0; + } + name = p; + + /* Search appropriate hardware */ + log_debug("rpc", "client request interface %s", name); + TAILQ_FOREACH (hardware, &cfg->g_hardware, h_entries) + if (!strcmp(hardware->h_ifname, name)) { + ssize_t output_len = lldpd_hardware_serialize(hardware, output); + free(name); + if (output_len <= 0) { + *type = NONE; + return 0; + } + return output_len; + } + + log_warnx("rpc", "no interface %s found", name); + free(name); + *type = NONE; + return 0; +} + +/* Return all available information related to an interface + Input: name of the interface (serialized) + Output: Information about the interface (lldpd_hardware) +*/ +static ssize_t +client_handle_get_default_port(struct lldpd *cfg, enum hmsg_type *type, void *input, + int input_len, void **output, int *subscribed) +{ + log_debug("rpc", "client request the default local port"); + ssize_t output_len = lldpd_port_serialize(cfg->g_default_local_port, output); + if (output_len <= 0) { + *type = NONE; + return 0; + } + return output_len; +} + +static int +_client_handle_set_port(struct lldpd *cfg, struct lldpd_port *port, + struct lldpd_port_set *set) +{ +#ifdef ENABLE_LLDPMED + struct lldpd_med_loc *loc = NULL; +#endif + if (set->local_id) { + log_debug("rpc", "requested change to Port ID"); + free(port->p_id); + port->p_id = strdup(set->local_id); + port->p_id_len = strlen(set->local_id); + port->p_id_subtype = LLDP_PORTID_SUBTYPE_LOCAL; + port->p_descr_force = 0; + } + if (set->local_descr) { + log_debug("rpc", "requested change to Port Description"); + free(port->p_descr); + port->p_descr = strdup(set->local_descr); + port->p_descr_force = 1; + } + switch (set->rxtx) { + case LLDPD_RXTX_TXONLY: + log_debug("rpc", "requested TX only mode"); + port->p_disable_rx = 1; + port->p_disable_tx = 0; + break; + case LLDPD_RXTX_RXONLY: + log_debug("rpc", "requested RX only mode"); + port->p_disable_rx = 0; + port->p_disable_tx = 1; + break; + case LLDPD_RXTX_BOTH: + log_debug("rpc", "requested RX/TX mode"); + port->p_disable_rx = port->p_disable_tx = 0; + break; + case LLDPD_RXTX_DISABLED: + log_debug("rpc", "requested disabled mode"); + port->p_disable_rx = port->p_disable_tx = 1; + break; + } + if (set->vlan_tx_enabled > -1) { + port->p_vlan_tx_enabled = set->vlan_tx_enabled; + port->p_vlan_tx_tag = set->vlan_tx_tag; + } +#ifdef ENABLE_LLDPMED + if (set->med_policy && set->med_policy->type > 0) { + log_debug("rpc", "requested change to MED policy"); + if (set->med_policy->type > LLDP_MED_APPTYPE_LAST) { + log_warnx("rpc", "invalid policy provided: %d", + set->med_policy->type); + return -1; + } + memcpy(&port->p_med_policy[set->med_policy->type - 1], set->med_policy, + sizeof(struct lldpd_med_policy)); + port->p_med_cap_enabled |= LLDP_MED_CAP_POLICY; + } + if (set->med_location && set->med_location->format > 0) { + char *newdata = NULL; + log_debug("rpc", "requested change to MED location"); + if (set->med_location->format > LLDP_MED_LOCFORMAT_LAST) { + log_warnx("rpc", "invalid location format provided: %d", + set->med_location->format); + return -1; + } + loc = &port->p_med_location[set->med_location->format - 1]; + free(loc->data); + memcpy(loc, set->med_location, sizeof(struct lldpd_med_loc)); + if (!loc->data || !(newdata = malloc(loc->data_len))) loc->data_len = 0; + if (newdata) memcpy(newdata, loc->data, loc->data_len); + loc->data = newdata; + port->p_med_cap_enabled |= LLDP_MED_CAP_LOCATION; + } + if (set->med_power) { + log_debug("rpc", "requested change to MED power"); + memcpy(&port->p_med_power, set->med_power, + sizeof(struct lldpd_med_power)); + switch (set->med_power->devicetype) { + case LLDP_MED_POW_TYPE_PD: + port->p_med_cap_enabled |= LLDP_MED_CAP_MDI_PD; + port->p_med_cap_enabled &= ~LLDP_MED_CAP_MDI_PSE; + break; + case LLDP_MED_POW_TYPE_PSE: + port->p_med_cap_enabled |= LLDP_MED_CAP_MDI_PSE; + port->p_med_cap_enabled &= ~LLDP_MED_CAP_MDI_PD; + break; + } + } +#endif +#ifdef ENABLE_DOT3 + if (set->dot3_power) { + log_debug("rpc", "requested change to Dot3 power"); + memcpy(&port->p_power, set->dot3_power, + sizeof(struct lldpd_dot3_power)); + } +#endif +#ifdef ENABLE_CUSTOM + if (set->custom_list_clear) { + log_debug("rpc", "requested custom TLVs clear"); + lldpd_custom_list_cleanup(port); + } else { + if (set->custom) { + log_info("rpc", + "custom TLV op %s oui %02x:%02x:%02x subtype %x", + (set->custom_tlv_op == CUSTOM_TLV_REMOVE) ? "remove" : + (set->custom_tlv_op == CUSTOM_TLV_ADD) ? "add" : + "replace", + set->custom->oui[0], set->custom->oui[1], + set->custom->oui[2], set->custom->subtype); + switch (set->custom_tlv_op) { + case CUSTOM_TLV_REMOVE: + lldpd_custom_tlv_cleanup(port, set->custom); + break; + case CUSTOM_TLV_ADD: + lldpd_custom_tlv_add(port, set->custom); + break; + case CUSTOM_TLV_REPLACE: + default: + lldpd_custom_tlv_cleanup(port, set->custom); + lldpd_custom_tlv_add(port, set->custom); + break; + } + } + } +#endif + return 0; +} + +/* Set some port related settings (policy, location, power) + Input: name of the interface, policy/location/power setting to be modified + Output: nothing +*/ +static ssize_t +client_handle_set_port(struct lldpd *cfg, enum hmsg_type *type, void *input, + int input_len, void **output, int *subscribed) +{ + int ret = 0; + struct lldpd_port_set *set = NULL; + struct lldpd_hardware *hardware = NULL; + + if (lldpd_port_set_unserialize(input, input_len, &set) <= 0) { + *type = NONE; + return 0; + } + if (!set->ifname) { + log_warnx("rpc", "no interface provided"); + goto set_port_finished; + } + + /* Search the appropriate hardware */ + if (strlen(set->ifname) == 0) { + log_debug("rpc", "client request change to default port"); + if (_client_handle_set_port(cfg, cfg->g_default_local_port, set) == -1) + goto set_port_finished; + ret = 1; + } else { + log_debug("rpc", "client request change to port %s", set->ifname); + TAILQ_FOREACH (hardware, &cfg->g_hardware, h_entries) { + if (!strcmp(hardware->h_ifname, set->ifname)) { + struct lldpd_port *port = &hardware->h_lport; + if (_client_handle_set_port(cfg, port, set) == -1) + goto set_port_finished; + ret = 1; + break; + } + } + } + + if (ret == 0) + log_warn("rpc", "no interface %s found", set->ifname); + else + levent_update_now(cfg); + +set_port_finished: + if (!ret) *type = NONE; + free(set->ifname); + free(set->local_id); + free(set->local_descr); +#ifdef ENABLE_LLDPMED + free(set->med_policy); + if (set->med_location) free(set->med_location->data); + free(set->med_location); + free(set->med_power); +#endif +#ifdef ENABLE_DOT3 + free(set->dot3_power); +#endif +#ifdef ENABLE_CUSTOM + if (set->custom) { + free(set->custom->oui_info); + free(set->custom); + } +#endif + free(set); + return 0; +} + +/* Register subscribtion to neighbor changes */ +static ssize_t +client_handle_subscribe(struct lldpd *cfg, enum hmsg_type *type, void *input, + int input_len, void **output, int *subscribed) +{ + log_debug("rpc", "client subscribe to changes"); + *subscribed = 1; + return 0; +} + +struct client_handle { + enum hmsg_type type; + const char *name; + ssize_t ( + *handle)(struct lldpd *, enum hmsg_type *, void *, int, void **, int *); +}; + +static struct client_handle client_handles[] = { { NONE, "None", client_handle_none }, + { GET_CONFIG, "Get configuration", client_handle_get_configuration }, + { SET_CONFIG, "Set configuration", client_handle_set_configuration }, + { GET_INTERFACES, "Get interfaces", client_handle_get_interfaces }, + { GET_INTERFACE, "Get interface", client_handle_get_interface }, + { GET_DEFAULT_PORT, "Get default port", client_handle_get_default_port }, + { SET_CHASSIS, "Set local chassis", client_handle_set_local_chassis }, + { GET_CHASSIS, "Get local chassis", client_handle_get_local_chassis }, + { SET_PORT, "Set port", client_handle_set_port }, + { SUBSCRIBE, "Subscribe", client_handle_subscribe }, { 0, NULL } }; + +int +client_handle_client(struct lldpd *cfg, ssize_t (*send)(void *, int, void *, size_t), + void *out, enum hmsg_type type, void *buffer, size_t n, int *subscribed) +{ + struct client_handle *ch; + void *answer; + ssize_t len, sent; + + log_debug("rpc", "handle client request"); + for (ch = client_handles; ch->handle != NULL; ch++) { + if (ch->type == type) { + TRACE(LLDPD_CLIENT_REQUEST(ch->name)); + answer = NULL; + len = ch->handle(cfg, &type, buffer, n, &answer, subscribed); + sent = send(out, type, answer, len); + free(answer); + return sent; + } + } + + log_warnx("rpc", "unknown message request (%d) received", type); + return -1; +} diff --git a/src/daemon/dmi-dummy.c b/src/daemon/dmi-dummy.c new file mode 100644 index 0000000..2954d50 --- /dev/null +++ b/src/daemon/dmi-dummy.c @@ -0,0 +1,57 @@ +/* -*- 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 "lldpd.h" + +#ifdef ENABLE_LLDPMED + +char * +dmi_hw() +{ + return NULL; +} + +char * +dmi_fw() +{ + return NULL; +} + +char * +dmi_sn() +{ + return NULL; +} + +char * +dmi_manuf() +{ + return NULL; +} + +char * +dmi_model() +{ + return NULL; +} + +char * +dmi_asset() +{ + return NULL; +} +#endif diff --git a/src/daemon/dmi-freebsd.c b/src/daemon/dmi-freebsd.c new file mode 100644 index 0000000..f20b71b --- /dev/null +++ b/src/daemon/dmi-freebsd.c @@ -0,0 +1,82 @@ +/* -*- 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 "lldpd.h" +#include <unistd.h> +#include <kenv.h> + +#ifdef ENABLE_LLDPMED +/* Fill in inventory stuff: + - hardware version: smbios.system.version + - firmware version: smbios.bios.version + - software version: `uname -r` + - serial number: smbios.system.serial + - manufacturer: smbios.system.maker + - model: smbios.system.product + - asset: smbios.chassis.tag +*/ + +static char * +dmi_get(char *file) +{ + char buffer[100] = {}; + + log_debug("localchassis", "DMI request for %s", file); + if (kenv(KENV_GET, file, buffer, sizeof(buffer) - 1) == -1) { + log_debug("localchassis", "cannot get %s", file); + return NULL; + } + if (strlen(buffer)) return strdup(buffer); + return NULL; +} + +char * +dmi_hw() +{ + return dmi_get("smbios.system.version"); +} + +char * +dmi_fw() +{ + return dmi_get("smbios.bios.version"); +} + +char * +dmi_sn() +{ + return dmi_get("smbios.system.serial"); +} + +char * +dmi_manuf() +{ + return dmi_get("smbios.system.maker"); +} + +char * +dmi_model() +{ + return dmi_get("smibios.system.product"); +} + +char * +dmi_asset() +{ + return dmi_get("smibios.chassis.tag"); +} +#endif diff --git a/src/daemon/dmi-linux.c b/src/daemon/dmi-linux.c new file mode 100644 index 0000000..47a6d1b --- /dev/null +++ b/src/daemon/dmi-linux.c @@ -0,0 +1,90 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2009 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 "lldpd.h" +#include <unistd.h> + +#ifdef ENABLE_LLDPMED +/* Fill in inventory stuff: + - hardware version: /sys/class/dmi/id/product_version + - firmware version: /sys/class/dmi/id/bios_version + - software version: `uname -r` + - serial number: /sys/class/dmi/id/product_serial + - manufacturer: /sys/class/dmi/id/sys_vendor + - model: /sys/class/dmi/id/product_name + - asset: /sys/class/dmi/id/chassis_asset_tag +*/ + +static char * +dmi_get(const char *file) +{ + int dmi, s; + char buffer[100] = {}; + + log_debug("localchassis", "DMI request for file %s", file); + if ((dmi = priv_open(file)) < 0) { + log_debug("localchassis", "cannot get DMI file %s", file); + return NULL; + } + if ((s = read(dmi, buffer, sizeof(buffer))) == -1) { + log_debug("localchassis", "cannot read DMI file %s", file); + close(dmi); + return NULL; + } + close(dmi); + buffer[sizeof(buffer) - 1] = '\0'; + if ((s > 0) && (buffer[s - 1] == '\n')) buffer[s - 1] = '\0'; + if (strlen(buffer)) return strdup(buffer); + return NULL; +} + +char * +dmi_hw() +{ + return dmi_get(SYSFS_CLASS_DMI "product_version"); +} + +char * +dmi_fw() +{ + return dmi_get(SYSFS_CLASS_DMI "bios_version"); +} + +char * +dmi_sn() +{ + return dmi_get(SYSFS_CLASS_DMI "product_serial"); +} + +char * +dmi_manuf() +{ + return dmi_get(SYSFS_CLASS_DMI "sys_vendor"); +} + +char * +dmi_model() +{ + return dmi_get(SYSFS_CLASS_DMI "product_name"); +} + +char * +dmi_asset() +{ + return dmi_get(SYSFS_CLASS_DMI "chassis_asset_tag"); +} +#endif diff --git a/src/daemon/dmi-openbsd.c b/src/daemon/dmi-openbsd.c new file mode 100644 index 0000000..a226829 --- /dev/null +++ b/src/daemon/dmi-openbsd.c @@ -0,0 +1,73 @@ +/* -*- 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 "lldpd.h" +#include <sys/param.h> +#include <sys/sysctl.h> + +#ifdef ENABLE_LLDPMED + +static char * +dmi_get(int what, const char *descr) +{ + char result[100] = {}; + size_t len = sizeof(result) - 1; + int mib[2] = { CTL_HW, what }; + if (sysctl(mib, 2, result, &len, NULL, 0) == -1) { + log_debug("localchassis", "cannot get %s", descr); + return NULL; + } + log_debug("localchassis", "got `%s` for %s", result, descr); + return strdup(result); +} + +char * +dmi_hw() +{ + return dmi_get(HW_VERSION, "hardware revision"); +} + +char * +dmi_fw() +{ + return NULL; +} + +char * +dmi_sn() +{ + return dmi_get(HW_SERIALNO, "serial number"); +} + +char * +dmi_manuf() +{ + return dmi_get(HW_VENDOR, "hardware vendor"); +} + +char * +dmi_model() +{ + return dmi_get(HW_PRODUCT, "hardware product"); +} + +char * +dmi_asset() +{ + return dmi_get(HW_UUID, "hardware UUID"); +} +#endif diff --git a/src/daemon/dmi-osx.c b/src/daemon/dmi-osx.c new file mode 100644 index 0000000..cde8676 --- /dev/null +++ b/src/daemon/dmi-osx.c @@ -0,0 +1,109 @@ +/* -*- 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 "lldpd.h" + +#include <CoreFoundation/CoreFoundation.h> +#include <IOKit/IOKitLib.h> + +#ifdef ENABLE_LLDPMED +static char * +dmi_get(const char *classname, CFStringRef property) +{ + char *result = NULL; + CFMutableDictionaryRef matching = NULL; + CFTypeRef cfres = NULL; + io_service_t service = 0; + matching = IOServiceMatching(classname); + if (!matching) { + log_debug("localchassis", "cannot get %s class from registry", + classname); + goto end; + } + service = IOServiceGetMatchingService(kIOMasterPortDefault, matching); + if (!service) { + log_warnx("localchassis", "cannot get matching %s class from registry", + classname); + goto end; + } + cfres = IORegistryEntryCreateCFProperty(service, property, kCFAllocatorDefault, + kNilOptions); + if (!cfres) { + log_debug("localchassis", + "cannot find property %s in class %s in registry", + CFStringGetCStringPtr(property, kCFStringEncodingMacRoman), + classname); + goto end; + } + + if (CFGetTypeID(cfres) == CFStringGetTypeID()) + result = strdup(CFStringGetCStringPtr((CFStringRef)cfres, + kCFStringEncodingMacRoman)); + else if (CFGetTypeID(cfres) == CFDataGetTypeID()) { + /* OK, we know this is a string. */ + result = calloc(1, CFDataGetLength((CFDataRef)cfres) + 1); + if (!result) goto end; + memcpy(result, CFDataGetBytePtr((CFDataRef)cfres), + CFDataGetLength((CFDataRef)cfres)); + } else + log_debug("localchassis", "unknown type for property %s in class %s", + CFStringGetCStringPtr(property, kCFStringEncodingMacRoman), + classname); + +end: + if (cfres) CFRelease(cfres); + if (service) IOObjectRelease(service); + return result; +} + +char * +dmi_hw() +{ + return dmi_get("IOPlatformExpertDevice", CFSTR("version")); +} + +char * +dmi_fw() +{ + /* Dunno where it is. Maybe in SMC? */ + return NULL; +} + +char * +dmi_sn() +{ + return dmi_get("IOPlatformExpertDevice", CFSTR("IOPlatformSerialNumber")); +} + +char * +dmi_manuf() +{ + return dmi_get("IOPlatformExpertDevice", CFSTR("manufacturer")); +} + +char * +dmi_model() +{ + return dmi_get("IOPlatformExpertDevice", CFSTR("model")); +} + +char * +dmi_asset() +{ + return dmi_get("IOPlatformExpertDevice", CFSTR("board-id")); +} +#endif diff --git a/src/daemon/dtrace2systemtap.awk b/src/daemon/dtrace2systemtap.awk new file mode 100644 index 0000000..3761008 --- /dev/null +++ b/src/daemon/dtrace2systemtap.awk @@ -0,0 +1,25 @@ +#!/usr/bin/awk -f + +# Convert a simple dtrace probe files into a tapset. Heavily inspired +# by dtrace2systemtap.pl from libvirt + +($1 == "provider") { + provider = $2 +} + +($1 == "probe") { + name = substr($2, 0, index($2, "(") - 1) + split(substr($0, index($0, "(") + 1, index($0, ")") - index($0, "(") - 1), + args, /, /) + printf "probe %s.%s = process(\"%s/%s\").provider(\"%s\").mark(\"%s\") {\n", provider, name, sbindir, provider, provider, name + for (arg in args) { + match(args[arg], /^(.+[^a-z_])([a-z_]+)$/, aarg) + type = aarg[1] + argname = aarg[2] + if (type == "char *") + printf " %s = user_string($arg%d);\n", argname, arg + else + printf " %s = $arg%d;\n", argname, arg + } + printf "}\n\n" +} diff --git a/src/daemon/event.c b/src/daemon/event.c new file mode 100644 index 0000000..971500f --- /dev/null +++ b/src/daemon/event.c @@ -0,0 +1,911 @@ +/* -*- 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 "lldpd.h" +#include "trace.h" + +#include <unistd.h> +#include <signal.h> +#include <errno.h> +#include <time.h> +#include <fcntl.h> +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wdocumentation" +#endif +#include <event2/event.h> +#include <event2/bufferevent.h> +#include <event2/buffer.h> +#if defined(__clang__) +# pragma clang diagnostic pop +#endif + +#define EVENT_BUFFER 1024 + +static void +levent_log_cb(int severity, const char *msg) +{ + switch (severity) { + case _EVENT_LOG_DEBUG: + log_debug("libevent", "%s", msg); + break; + case _EVENT_LOG_MSG: + log_info("libevent", "%s", msg); + break; + case _EVENT_LOG_WARN: + log_warnx("libevent", "%s", msg); + break; + case _EVENT_LOG_ERR: + log_warnx("libevent", "%s", msg); + break; + } +} + +struct lldpd_events { + TAILQ_ENTRY(lldpd_events) next; + struct event *ev; +}; +TAILQ_HEAD(ev_l, lldpd_events); + +#define levent_snmp_fds(cfg) ((struct ev_l *)(cfg)->g_snmp_fds) +#define levent_hardware_fds(hardware) ((struct ev_l *)(hardware)->h_recv) + +#ifdef USE_SNMP +# include <net-snmp/net-snmp-config.h> +# include <net-snmp/net-snmp-includes.h> +# include <net-snmp/agent/net-snmp-agent-includes.h> +# include <net-snmp/agent/snmp_vars.h> + +/* Compatibility with older versions of NetSNMP */ +# ifndef HAVE_SNMP_SELECT_INFO2 +# define netsnmp_large_fd_set fd_set +# define snmp_read2 snmp_read +# define snmp_select_info2 snmp_select_info +# define netsnmp_large_fd_set_init(...) +# define netsnmp_large_fd_set_cleanup(...) +# define NETSNMP_LARGE_FD_SET FD_SET +# define NETSNMP_LARGE_FD_CLR FD_CLR +# define NETSNMP_LARGE_FD_ZERO FD_ZERO +# define NETSNMP_LARGE_FD_ISSET FD_ISSET +# else +# include <net-snmp/library/large_fd_set.h> +# endif + +static void levent_snmp_update(struct lldpd *); + +/* + * Callback function when we have something to read from SNMP. + * + * This function is called because we have a read event on one SNMP + * file descriptor. When need to call snmp_read() on it. + */ +static void +levent_snmp_read(evutil_socket_t fd, short what, void *arg) +{ + struct lldpd *cfg = arg; + netsnmp_large_fd_set fdset; + (void)what; + netsnmp_large_fd_set_init(&fdset, FD_SETSIZE); + NETSNMP_LARGE_FD_ZERO(&fdset); + NETSNMP_LARGE_FD_SET(fd, &fdset); + snmp_read2(&fdset); + levent_snmp_update(cfg); +} + +/* + * Callback function for a SNMP timeout. + * + * A SNMP timeout has occurred. Call `snmp_timeout()` to handle it. + */ +static void +levent_snmp_timeout(evutil_socket_t fd, short what, void *arg) +{ + struct lldpd *cfg = arg; + (void)what; + (void)fd; + snmp_timeout(); + run_alarms(); + levent_snmp_update(cfg); +} + +/* + * Watch a new SNMP FD. + * + * @param base The libevent base we are working on. + * @param fd The file descriptor we want to watch. + * + * The file descriptor is appended to the list of file descriptors we + * want to watch. + */ +static void +levent_snmp_add_fd(struct lldpd *cfg, int fd) +{ + struct event_base *base = cfg->g_base; + struct lldpd_events *snmpfd = calloc(1, sizeof(struct lldpd_events)); + if (!snmpfd) { + log_warn("event", "unable to allocate memory for new SNMP event"); + return; + } + levent_make_socket_nonblocking(fd); + if ((snmpfd->ev = event_new(base, fd, EV_READ | EV_PERSIST, levent_snmp_read, + cfg)) == NULL) { + log_warnx("event", "unable to allocate a new SNMP event for FD %d", fd); + free(snmpfd); + return; + } + if (event_add(snmpfd->ev, NULL) == -1) { + log_warnx("event", "unable to schedule new SNMP event for FD %d", fd); + event_free(snmpfd->ev); + free(snmpfd); + return; + } + TAILQ_INSERT_TAIL(levent_snmp_fds(cfg), snmpfd, next); +} + +/* + * Update SNMP event loop. + * + * New events are added and some other are removed. This function + * should be called every time a SNMP event happens: either when + * handling a SNMP packet, a SNMP timeout or when sending a SNMP + * packet. This function will keep libevent in sync with NetSNMP. + * + * @param base The libevent base we are working on. + */ +static void +levent_snmp_update(struct lldpd *cfg) +{ + int maxfd = 0; + int block = 1; + struct timeval timeout; + static int howmany = 0; + int added = 0, removed = 0, current = 0; + struct lldpd_events *snmpfd, *snmpfd_next; + + /* snmp_select_info() can be tricky to understand. We set `block` to + 1 to means that we don't request a timeout. snmp_select_info() + will reset `block` to 0 if it wants us to setup a timeout. In + this timeout, `snmp_timeout()` should be invoked. + + Each FD in `fdset` will need to be watched for reading. If one of + them become active, `snmp_read()` should be called on it. + */ + + netsnmp_large_fd_set fdset; + netsnmp_large_fd_set_init(&fdset, FD_SETSIZE); + NETSNMP_LARGE_FD_ZERO(&fdset); + snmp_select_info2(&maxfd, &fdset, &timeout, &block); + + /* We need to untrack any event whose FD is not in `fdset` + anymore */ + for (snmpfd = TAILQ_FIRST(levent_snmp_fds(cfg)); snmpfd; snmpfd = snmpfd_next) { + snmpfd_next = TAILQ_NEXT(snmpfd, next); + if (event_get_fd(snmpfd->ev) >= maxfd || + (!NETSNMP_LARGE_FD_ISSET(event_get_fd(snmpfd->ev), &fdset))) { + event_free(snmpfd->ev); + TAILQ_REMOVE(levent_snmp_fds(cfg), snmpfd, next); + free(snmpfd); + removed++; + } else { + NETSNMP_LARGE_FD_CLR(event_get_fd(snmpfd->ev), &fdset); + current++; + } + } + + /* Invariant: FD in `fdset` are not in list of FD */ + for (int fd = 0; fd < maxfd; fd++) { + if (NETSNMP_LARGE_FD_ISSET(fd, &fdset)) { + levent_snmp_add_fd(cfg, fd); + added++; + } + } + current += added; + if (howmany != current) { + log_debug("event", + "added %d events, removed %d events, total of %d events", added, + removed, current); + howmany = current; + } + + /* If needed, handle timeout */ + if (evtimer_add(cfg->g_snmp_timeout, block ? NULL : &timeout) == -1) + log_warnx("event", "unable to schedule timeout function for SNMP"); + + netsnmp_large_fd_set_cleanup(&fdset); +} +#endif /* USE_SNMP */ + +struct lldpd_one_client { + TAILQ_ENTRY(lldpd_one_client) next; + struct lldpd *cfg; + struct bufferevent *bev; + int subscribed; /* Is this client subscribed to changes? */ +}; +TAILQ_HEAD(, lldpd_one_client) lldpd_clients; + +static void +levent_ctl_free_client(struct lldpd_one_client *client) +{ + if (client && client->bev) bufferevent_free(client->bev); + if (client) { + TAILQ_REMOVE(&lldpd_clients, client, next); + free(client); + } +} + +static void +levent_ctl_close_clients() +{ + struct lldpd_one_client *client, *client_next; + for (client = TAILQ_FIRST(&lldpd_clients); client; client = client_next) { + client_next = TAILQ_NEXT(client, next); + levent_ctl_free_client(client); + } +} + +static ssize_t +levent_ctl_send(struct lldpd_one_client *client, int type, void *data, size_t len) +{ + struct bufferevent *bev = client->bev; + struct hmsg_header hdr = { .len = len, .type = type }; + bufferevent_disable(bev, EV_WRITE); + if (bufferevent_write(bev, &hdr, sizeof(struct hmsg_header)) == -1 || + (len > 0 && bufferevent_write(bev, data, len) == -1)) { + log_warnx("event", "unable to create answer to client"); + levent_ctl_free_client(client); + return -1; + } + bufferevent_enable(bev, EV_WRITE); + return len; +} + +void +levent_ctl_notify(char *ifname, int state, struct lldpd_port *neighbor) +{ + struct lldpd_one_client *client, *client_next; + struct lldpd_neighbor_change neigh = { .ifname = ifname, + .state = state, + .neighbor = neighbor }; + void *output = NULL; + ssize_t output_len = 0; + + /* Don't use TAILQ_FOREACH, the client may be deleted in case of errors. */ + log_debug("control", "notify clients of neighbor changes"); + for (client = TAILQ_FIRST(&lldpd_clients); client; client = client_next) { + client_next = TAILQ_NEXT(client, next); + if (!client->subscribed) continue; + + if (output == NULL) { + /* Ugly hack: we don't want to transmit a list of + * ports. We patch the port to avoid this. */ + TAILQ_ENTRY(lldpd_port) backup_p_entries; + memcpy(&backup_p_entries, &neighbor->p_entries, + sizeof(backup_p_entries)); + memset(&neighbor->p_entries, 0, sizeof(backup_p_entries)); + output_len = lldpd_neighbor_change_serialize(&neigh, &output); + memcpy(&neighbor->p_entries, &backup_p_entries, + sizeof(backup_p_entries)); + + if (output_len <= 0) { + log_warnx("event", + "unable to serialize changed neighbor"); + return; + } + } + + levent_ctl_send(client, NOTIFICATION, output, output_len); + } + + free(output); +} + +static ssize_t +levent_ctl_send_cb(void *out, int type, void *data, size_t len) +{ + struct lldpd_one_client *client = out; + return levent_ctl_send(client, type, data, len); +} + +static void +levent_ctl_recv(struct bufferevent *bev, void *ptr) +{ + struct lldpd_one_client *client = ptr; + struct evbuffer *buffer = bufferevent_get_input(bev); + size_t buffer_len = evbuffer_get_length(buffer); + struct hmsg_header hdr; + void *data = NULL; + + log_debug("control", "receive data on Unix socket"); + if (buffer_len < sizeof(struct hmsg_header)) return; /* Not enough data yet */ + if (evbuffer_copyout(buffer, &hdr, sizeof(struct hmsg_header)) != + sizeof(struct hmsg_header)) { + log_warnx("event", "not able to read header"); + return; + } + if (hdr.len > HMSG_MAX_SIZE) { + log_warnx("event", "message received is too large"); + goto recv_error; + } + + if (buffer_len < hdr.len + sizeof(struct hmsg_header)) + return; /* Not enough data yet */ + if (hdr.len > 0 && (data = malloc(hdr.len)) == NULL) { + log_warnx("event", "not enough memory"); + goto recv_error; + } + evbuffer_drain(buffer, sizeof(struct hmsg_header)); + if (hdr.len > 0) evbuffer_remove(buffer, data, hdr.len); + + /* Currently, we should not receive notification acknowledgment. But if + * we receive one, we can discard it. */ + if (hdr.len == 0 && hdr.type == NOTIFICATION) return; + if (client_handle_client(client->cfg, levent_ctl_send_cb, client, hdr.type, + data, hdr.len, &client->subscribed) == -1) + goto recv_error; + free(data); + return; + +recv_error: + free(data); + levent_ctl_free_client(client); +} + +static void +levent_ctl_event(struct bufferevent *bev, short events, void *ptr) +{ + struct lldpd_one_client *client = ptr; + if (events & BEV_EVENT_ERROR) { + log_warnx("event", "an error occurred with client: %s", + evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR())); + levent_ctl_free_client(client); + } else if (events & BEV_EVENT_EOF) { + log_debug("event", "client has been disconnected"); + levent_ctl_free_client(client); + } +} + +static void +levent_ctl_accept(evutil_socket_t fd, short what, void *arg) +{ + struct lldpd *cfg = arg; + struct lldpd_one_client *client = NULL; + int s; + (void)what; + + log_debug("control", "accept a new connection"); + if ((s = accept(fd, NULL, NULL)) == -1) { + log_warn("event", "unable to accept connection from socket"); + return; + } + client = calloc(1, sizeof(struct lldpd_one_client)); + if (!client) { + log_warnx("event", "unable to allocate memory for new client"); + close(s); + goto accept_failed; + } + client->cfg = cfg; + levent_make_socket_nonblocking(s); + TAILQ_INSERT_TAIL(&lldpd_clients, client, next); + if ((client->bev = bufferevent_socket_new(cfg->g_base, s, + BEV_OPT_CLOSE_ON_FREE)) == NULL) { + log_warnx("event", + "unable to allocate a new buffer event for new client"); + close(s); + goto accept_failed; + } + bufferevent_setcb(client->bev, levent_ctl_recv, NULL, levent_ctl_event, client); + bufferevent_enable(client->bev, EV_READ | EV_WRITE); + log_debug("event", "new client accepted"); + /* coverity[leaked_handle] + s has been saved by bufferevent_socket_new */ + return; +accept_failed: + levent_ctl_free_client(client); +} + +static void +levent_priv(evutil_socket_t fd, short what, void *arg) +{ + struct event_base *base = arg; + ssize_t n; + int err; + char one; + (void)what; + /* Check if we have some data available. We need to pass the socket in + * non-blocking mode to be able to run the check without disruption. */ + levent_make_socket_nonblocking(fd); + n = read(fd, &one, 1); + err = errno; + levent_make_socket_blocking(fd); + + switch (n) { + case -1: + if (err == EAGAIN || err == EWOULDBLOCK) /* No data, all good */ + return; + log_warnx("event", "unable to poll monitor process, exit"); + break; + case 0: + log_warnx("event", "monitor process has terminated, exit"); + break; + default: + /* This is a bit unsafe as we are now out-of-sync with the + * monitor. It would be safer to request 0 byte, but some OS + * (illumos) seem to take the shortcut that by asking 0 byte, + * we can just return 0 byte. */ + log_warnx("event", + "received unexpected data from monitor process, exit"); + break; + } + event_base_loopbreak(base); +} + +static void +levent_dump(evutil_socket_t fd, short what, void *arg) +{ + struct event_base *base = arg; + (void)fd; + (void)what; + log_debug("event", "dumping all events"); + event_base_dump_events(base, stderr); +} +static void +levent_stop(evutil_socket_t fd, short what, void *arg) +{ + struct event_base *base = arg; + (void)fd; + (void)what; + event_base_loopbreak(base); +} + +static void +levent_update_and_send(evutil_socket_t fd, short what, void *arg) +{ + struct lldpd *cfg = arg; + struct timeval tv; + long interval_ms = cfg->g_config.c_tx_interval; + + (void)fd; + (void)what; + lldpd_loop(cfg); + if (cfg->g_iface_event != NULL) interval_ms *= 20; + if (interval_ms < 30000) interval_ms = 30000; + tv.tv_sec = interval_ms / 1000; + tv.tv_usec = (interval_ms % 1000) * 1000; + event_add(cfg->g_main_loop, &tv); +} + +void +levent_update_now(struct lldpd *cfg) +{ + if (cfg->g_main_loop) event_active(cfg->g_main_loop, EV_TIMEOUT, 1); +} + +void +levent_send_now(struct lldpd *cfg) +{ + struct lldpd_hardware *hardware; + TAILQ_FOREACH (hardware, &cfg->g_hardware, h_entries) { + if (hardware->h_timer) + event_active(hardware->h_timer, EV_TIMEOUT, 1); + else + log_warnx("event", "BUG: no timer present for interface %s", + hardware->h_ifname); + } +} + +static void +levent_init(struct lldpd *cfg) +{ + /* Setup libevent */ + log_debug("event", "initialize libevent"); + event_set_log_callback(levent_log_cb); + if (!(cfg->g_base = event_base_new())) + fatalx("event", "unable to create a new libevent base"); + log_info("event", "libevent %s initialized with %s method", event_get_version(), + event_base_get_method(cfg->g_base)); + + /* Setup SNMP */ +#ifdef USE_SNMP + if (cfg->g_snmp) { + agent_init(cfg, cfg->g_snmp_agentx); + cfg->g_snmp_timeout = + evtimer_new(cfg->g_base, levent_snmp_timeout, cfg); + if (!cfg->g_snmp_timeout) + fatalx("event", "unable to setup timeout function for SNMP"); + if ((cfg->g_snmp_fds = malloc(sizeof(struct ev_l))) == NULL) + fatalx("event", "unable to allocate memory for SNMP events"); + TAILQ_INIT(levent_snmp_fds(cfg)); + } +#endif + + /* Setup loop that will run every X seconds. */ + log_debug("event", "register loop timer"); + if (!(cfg->g_main_loop = + event_new(cfg->g_base, -1, 0, levent_update_and_send, cfg))) + fatalx("event", "unable to setup main timer"); + event_active(cfg->g_main_loop, EV_TIMEOUT, 1); + + /* Setup unix socket */ + struct event *ctl_event; + log_debug("event", "register Unix socket"); + TAILQ_INIT(&lldpd_clients); + levent_make_socket_nonblocking(cfg->g_ctl); + if ((ctl_event = event_new(cfg->g_base, cfg->g_ctl, EV_READ | EV_PERSIST, + levent_ctl_accept, cfg)) == NULL) + fatalx("event", "unable to setup control socket event"); + event_add(ctl_event, NULL); + + /* Somehow monitor the monitor process */ + struct event *monitor_event; + log_debug("event", "monitor the monitor process"); + if ((monitor_event = event_new(cfg->g_base, priv_fd(PRIV_UNPRIVILEGED), + EV_READ | EV_PERSIST, levent_priv, cfg->g_base)) == NULL) + fatalx("event", "unable to monitor monitor process"); + event_add(monitor_event, NULL); + + /* Signals */ + log_debug("event", "register signals"); + evsignal_add(evsignal_new(cfg->g_base, SIGUSR1, levent_dump, cfg->g_base), + NULL); + evsignal_add(evsignal_new(cfg->g_base, SIGINT, levent_stop, cfg->g_base), NULL); + evsignal_add(evsignal_new(cfg->g_base, SIGTERM, levent_stop, cfg->g_base), + NULL); +} + +/* Initialize libevent and start the event loop */ +void +levent_loop(struct lldpd *cfg) +{ + levent_init(cfg); + lldpd_loop(cfg); +#ifdef USE_SNMP + if (cfg->g_snmp) levent_snmp_update(cfg); +#endif + + /* libevent loop */ + do { + TRACE(LLDPD_EVENT_LOOP()); + if (event_base_got_break(cfg->g_base) || + event_base_got_exit(cfg->g_base)) + break; + } while (event_base_loop(cfg->g_base, EVLOOP_ONCE) == 0); + + if (cfg->g_iface_timer_event != NULL) event_free(cfg->g_iface_timer_event); + +#ifdef USE_SNMP + if (cfg->g_snmp) agent_shutdown(); +#endif /* USE_SNMP */ + + levent_ctl_close_clients(); +} + +/* Release libevent resources */ +void +levent_shutdown(struct lldpd *cfg) +{ + if (cfg->g_iface_event) event_free(cfg->g_iface_event); + if (cfg->g_cleanup_timer) event_free(cfg->g_cleanup_timer); + event_base_free(cfg->g_base); +} + +static void +levent_hardware_recv(evutil_socket_t fd, short what, void *arg) +{ + struct lldpd_hardware *hardware = arg; + struct lldpd *cfg = hardware->h_cfg; + (void)what; + log_debug("event", "received something for %s", hardware->h_ifname); + lldpd_recv(cfg, hardware, fd); + levent_schedule_cleanup(cfg); +} + +void +levent_hardware_init(struct lldpd_hardware *hardware) +{ + log_debug("event", "initialize events for %s", hardware->h_ifname); + if ((hardware->h_recv = malloc(sizeof(struct ev_l))) == NULL) { + log_warnx("event", "unable to allocate memory for %s", + hardware->h_ifname); + return; + } + TAILQ_INIT(levent_hardware_fds(hardware)); +} + +void +levent_hardware_add_fd(struct lldpd_hardware *hardware, int fd) +{ + struct lldpd_events *hfd = NULL; + if (!hardware->h_recv) return; + + hfd = calloc(1, sizeof(struct lldpd_events)); + if (!hfd) { + log_warnx("event", "unable to allocate new event for %s", + hardware->h_ifname); + return; + } + levent_make_socket_nonblocking(fd); + if ((hfd->ev = event_new(hardware->h_cfg->g_base, fd, EV_READ | EV_PERSIST, + levent_hardware_recv, hardware)) == NULL) { + log_warnx("event", "unable to allocate a new event for %s", + hardware->h_ifname); + free(hfd); + return; + } + if (event_add(hfd->ev, NULL) == -1) { + log_warnx("event", "unable to schedule new event for %s", + hardware->h_ifname); + event_free(hfd->ev); + free(hfd); + return; + } + TAILQ_INSERT_TAIL(levent_hardware_fds(hardware), hfd, next); +} + +void +levent_hardware_release(struct lldpd_hardware *hardware) +{ + struct lldpd_events *ev, *ev_next; + if (hardware->h_timer) { + event_free(hardware->h_timer); + hardware->h_timer = NULL; + } + if (!hardware->h_recv) return; + + log_debug("event", "release events for %s", hardware->h_ifname); + for (ev = TAILQ_FIRST(levent_hardware_fds(hardware)); ev; ev = ev_next) { + ev_next = TAILQ_NEXT(ev, next); + /* We may close several time the same FD. This is harmless. */ + close(event_get_fd(ev->ev)); + event_free(ev->ev); + TAILQ_REMOVE(levent_hardware_fds(hardware), ev, next); + free(ev); + } + free(levent_hardware_fds(hardware)); +} + +static void +levent_iface_trigger(evutil_socket_t fd, short what, void *arg) +{ + struct lldpd *cfg = arg; + log_debug("event", "triggering update of all interfaces"); + lldpd_update_localports(cfg); +} + +static void +levent_iface_recv(evutil_socket_t fd, short what, void *arg) +{ + struct lldpd *cfg = arg; + char buffer[EVENT_BUFFER]; + int n; + + if (cfg->g_iface_cb == NULL) { + /* Discard the message */ + while (1) { + n = read(fd, buffer, sizeof(buffer)); + if (n == -1 && (errno == EWOULDBLOCK || errno == EAGAIN)) break; + if (n == -1) { + log_warn("event", + "unable to receive interface change notification message"); + return; + } + if (n == 0) { + log_warnx("event", + "end of file reached while getting interface change notification message"); + return; + } + } + } else { + cfg->g_iface_cb(cfg); + } + + /* Schedule local port update. We don't run it right away because we may + * receive a batch of events like this. */ + struct timeval one_sec = { 1, 0 }; + TRACE(LLDPD_INTERFACES_NOTIFICATION()); + log_debug("event", + "received notification change, schedule an update of all interfaces in one second"); + if (cfg->g_iface_timer_event == NULL) { + if ((cfg->g_iface_timer_event = evtimer_new(cfg->g_base, + levent_iface_trigger, cfg)) == NULL) { + log_warnx("event", + "unable to create a new event to trigger interface update"); + return; + } + } + if (evtimer_add(cfg->g_iface_timer_event, &one_sec) == -1) { + log_warnx("event", "unable to schedule interface updates"); + return; + } +} + +int +levent_iface_subscribe(struct lldpd *cfg, int socket) +{ + log_debug("event", "subscribe to interface changes from socket %d", socket); + levent_make_socket_nonblocking(socket); + cfg->g_iface_event = event_new(cfg->g_base, socket, EV_READ | EV_PERSIST, + levent_iface_recv, cfg); + if (cfg->g_iface_event == NULL) { + log_warnx("event", + "unable to allocate a new event for interface changes"); + return -1; + } + if (event_add(cfg->g_iface_event, NULL) == -1) { + log_warnx("event", "unable to schedule new interface changes event"); + event_free(cfg->g_iface_event); + cfg->g_iface_event = NULL; + return -1; + } + return 0; +} + +static void +levent_trigger_cleanup(evutil_socket_t fd, short what, void *arg) +{ + struct lldpd *cfg = arg; + lldpd_cleanup(cfg); +} + +void +levent_schedule_cleanup(struct lldpd *cfg) +{ + log_debug("event", "schedule next cleanup"); + if (cfg->g_cleanup_timer != NULL) { + event_free(cfg->g_cleanup_timer); + } + cfg->g_cleanup_timer = evtimer_new(cfg->g_base, levent_trigger_cleanup, cfg); + if (cfg->g_cleanup_timer == NULL) { + log_warnx("event", "unable to allocate a new event for cleanup tasks"); + return; + } + + /* Compute the next TTL event */ + struct timeval tv = { cfg->g_config.c_ttl, 0 }; + time_t now = time(NULL); + time_t next; + struct lldpd_hardware *hardware; + struct lldpd_port *port; + TAILQ_FOREACH (hardware, &cfg->g_hardware, h_entries) { + TAILQ_FOREACH (port, &hardware->h_rports, p_entries) { + if (now >= port->p_lastupdate + port->p_ttl) { + tv.tv_sec = 0; + log_debug("event", + "immediate cleanup on port %s (%lld, %d, %lld)", + hardware->h_ifname, (long long)now, port->p_ttl, + (long long)port->p_lastupdate); + break; + } + next = port->p_ttl - (now - port->p_lastupdate); + if (next < tv.tv_sec) tv.tv_sec = next; + } + } + + log_debug("event", "next cleanup in %ld seconds", (long)tv.tv_sec); + if (event_add(cfg->g_cleanup_timer, &tv) == -1) { + log_warnx("event", "unable to schedule cleanup task"); + event_free(cfg->g_cleanup_timer); + cfg->g_cleanup_timer = NULL; + return; + } +} + +static void +levent_send_pdu(evutil_socket_t fd, short what, void *arg) +{ + struct lldpd_hardware *hardware = arg; + int tx_interval = hardware->h_cfg->g_config.c_tx_interval; + + log_debug("event", "trigger sending PDU for port %s", hardware->h_ifname); + lldpd_send(hardware); + +#ifdef ENABLE_LLDPMED + if (hardware->h_tx_fast > 0) hardware->h_tx_fast--; + + if (hardware->h_tx_fast > 0) + tx_interval = hardware->h_cfg->g_config.c_tx_fast_interval * 1000; +#endif + + struct timeval tv; + tv.tv_sec = tx_interval / 1000; + tv.tv_usec = (tx_interval % 1000) * 1000; + if (event_add(hardware->h_timer, &tv) == -1) { + log_warnx("event", "unable to re-register timer event for port %s", + hardware->h_ifname); + event_free(hardware->h_timer); + hardware->h_timer = NULL; + return; + } +} + +void +levent_schedule_pdu(struct lldpd_hardware *hardware) +{ + log_debug("event", "schedule sending PDU on %s", hardware->h_ifname); + if (hardware->h_timer == NULL) { + hardware->h_timer = + evtimer_new(hardware->h_cfg->g_base, levent_send_pdu, hardware); + if (hardware->h_timer == NULL) { + log_warnx("event", "unable to schedule PDU sending for port %s", + hardware->h_ifname); + return; + } + } + + struct timeval tv = { 0, 0 }; + if (event_add(hardware->h_timer, &tv) == -1) { + log_warnx("event", "unable to register timer event for port %s", + hardware->h_ifname); + event_free(hardware->h_timer); + hardware->h_timer = NULL; + return; + } +} + +int +levent_make_socket_nonblocking(int fd) +{ + int flags; + if ((flags = fcntl(fd, F_GETFL, NULL)) < 0) { + log_warn("event", "fcntl(%d, F_GETFL)", fd); + return -1; + } + if (flags & O_NONBLOCK) return 0; + if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) { + log_warn("event", "fcntl(%d, F_SETFL)", fd); + return -1; + } + return 0; +} + +int +levent_make_socket_blocking(int fd) +{ + int flags; + if ((flags = fcntl(fd, F_GETFL, NULL)) < 0) { + log_warn("event", "fcntl(%d, F_GETFL)", fd); + return -1; + } + if (!(flags & O_NONBLOCK)) return 0; + if (fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) == -1) { + log_warn("event", "fcntl(%d, F_SETFL)", fd); + return -1; + } + return 0; +} + +#ifdef HOST_OS_LINUX +/* Receive and log error from a socket when there is suspicion of an error. */ +void +levent_recv_error(int fd, const char *source) +{ + do { + ssize_t n; + char buf[1024] = {}; + struct msghdr msg = { .msg_control = buf, + .msg_controllen = sizeof(buf) }; + if ((n = recvmsg(fd, &msg, MSG_ERRQUEUE | MSG_DONTWAIT)) <= 0) { + return; + } + struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); + if (cmsg == NULL) + log_warnx("event", "received unknown error on %s", source); + else + log_warnx("event", "received error (level=%d/type=%d) on %s", + cmsg->cmsg_level, cmsg->cmsg_type, source); + } while (1); +} +#endif diff --git a/src/daemon/forward-bsd.c b/src/daemon/forward-bsd.c new file mode 100644 index 0000000..1f930de --- /dev/null +++ b/src/daemon/forward-bsd.c @@ -0,0 +1,31 @@ +/* -*- 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 "lldpd.h" + +#include <sys/param.h> +#include <sys/sysctl.h> + +int +interfaces_routing_enabled(struct lldpd *cfg) +{ + (void)cfg; + int n, mib[4] = { CTL_NET, PF_INET, IPPROTO_IP, IPCTL_FORWARDING }; + size_t len = sizeof(int); + if (sysctl(mib, 4, &n, &len, NULL, 0) != -1) return (n == 1); + return -1; +} diff --git a/src/daemon/forward-linux.c b/src/daemon/forward-linux.c new file mode 100644 index 0000000..6308445 --- /dev/null +++ b/src/daemon/forward-linux.c @@ -0,0 +1,59 @@ +/* -*- 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 "lldpd.h" + +#include <unistd.h> + +static int +ip_forwarding_enabled(int af) +{ + int fd, rc = -1; + const char *fname; + char status; + + if (af == LLDPD_AF_IPV4) + fname = PROCFS_SYS_NET "ipv4/ip_forward"; + else if (af == LLDPD_AF_IPV6) + fname = PROCFS_SYS_NET "ipv6/conf/all/forwarding"; + else + return -1; + + if ((fd = priv_open(fname)) < 0) return -1; + + if (read(fd, &status, 1) == 1) rc = (status == '1'); + + close(fd); + return rc; +} + +int +interfaces_routing_enabled(struct lldpd *cfg) +{ + (void)cfg; + int rc; + + rc = ip_forwarding_enabled(LLDPD_AF_IPV4); + /* + * Report being a router if IPv4 forwarding is enabled. + * In case of error also stop the execution right away. + * If IPv4 forwarding is disabled we'll check the IPv6 status. + */ + if (rc != 0) return rc; + + return ip_forwarding_enabled(LLDPD_AF_IPV6); +} diff --git a/src/daemon/forward-solaris.c b/src/daemon/forward-solaris.c new file mode 100644 index 0000000..b44a110 --- /dev/null +++ b/src/daemon/forward-solaris.c @@ -0,0 +1,27 @@ +/* -*- 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 "lldpd.h" + +int +interfaces_routing_enabled(struct lldpd *cfg) +{ + /* Dunno how to get this for Solaris. See the commit introducing Solaris + support (maybe c3e340b6be8add4eb3a41882847a96e66793e82c) for a + solution which does not work in a chroot. */ + return 0; +} diff --git a/src/daemon/frame.c b/src/daemon/frame.c new file mode 100644 index 0000000..adc4629 --- /dev/null +++ b/src/daemon/frame.c @@ -0,0 +1,66 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2009 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 "lldpd.h" + +/** + * Compute the checksum as 16-bit word. + */ +u_int16_t +frame_checksum(const u_char *cp, int len, int cisco) +{ + unsigned int sum = 0, v = 0; + int oddbyte = 0; + + while ((len -= 2) >= 0) { + sum += *cp++ << 8; + sum += *cp++; + } + if ((oddbyte = len & 1) != 0) v = *cp; + + /* The remaining byte seems to be handled oddly by Cisco. From function + * dissect_cdp() in wireshark. 2014/6/14,zhengy@yealink.com: + * + * CDP doesn't adhere to RFC 1071 section 2. (B). It incorrectly assumes + * checksums are calculated on a big endian platform, therefore i.s.o. + * padding odd sized data with a zero byte _at the end_ it sets the last + * big endian _word_ to contain the last network _octet_. This byteswap + * has to be done on the last octet of network data before feeding it to + * the Internet checksum routine. + * CDP checksumming code has a bug in the addition of this last _word_ + * as a signed number into the long word intermediate checksum. When + * reducing this long to word size checksum an off-by-one error can be + * made. This off-by-one error is compensated for in the last _word_ of + * the network data. + */ + if (oddbyte) { + if (cisco) { + if (v & 0x80) { + sum += 0xff << 8; + sum += v - 1; + } else { + sum += v; + } + } else { + sum += v << 8; + } + } + + sum = (sum >> 16) + (sum & 0xffff); + sum += sum >> 16; + return (0xffff & ~sum); +} diff --git a/src/daemon/frame.h b/src/daemon/frame.h new file mode 100644 index 0000000..714de09 --- /dev/null +++ b/src/daemon/frame.h @@ -0,0 +1,102 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2009 Vincent Bernat <bernat@luffy.cx> + * Copyright (c) 2014 Michael Chapman + * + * 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 _FRAME_H +#define _FRAME_H + +static union { + uint8_t f_uint8; + uint16_t f_uint16; + uint32_t f_uint32; +} types; + +/* This set of macro are used to build packets. The current position in buffer + * is `pos'. The length of the remaining space in buffer is `length'. `type' + * should be a member of `types'. + * + * This was stolen from ladvd which was adapted from Net::CDP. The original + * author of those macros, Michael Chapman, has relicensed those macros under + * the ISC license. */ + +#define POKE(value, type, func) \ + ((length >= sizeof(type)) && \ + (type = func(value), memcpy(pos, &type, sizeof(type)), length -= sizeof(type), \ + pos += sizeof(type), 1)) +#define POKE_UINT8(value) POKE(value, types.f_uint8, ) +#define POKE_UINT16(value) POKE(value, types.f_uint16, htons) +#define POKE_UINT32(value) POKE(value, types.f_uint32, htonl) +#define POKE_BYTES(value, bytes) \ + ((length >= (bytes)) && \ + (memcpy(pos, value, bytes), length -= (bytes), pos += (bytes), 1)) +#define POKE_SAVE(where) (where = pos, 1) +#define POKE_RESTORE(where) \ + do { \ + if ((where) > pos) \ + length -= ((where)-pos); \ + else \ + length += (pos - (where)); \ + pos = (where); \ + } while (0) + +/* This set of macro are used to parse packets. The same variable as for POKE_* + * are used. There is no check on boundaries. */ + +#define PEEK(type, func) \ + (memcpy(&type, pos, sizeof(type)), length -= sizeof(type), pos += sizeof(type), \ + func(type)) +#define PEEK_UINT8 PEEK(types.f_uint8, ) +#define PEEK_UINT16 PEEK(types.f_uint16, ntohs) +#define PEEK_UINT32 PEEK(types.f_uint32, ntohl) +#define PEEK_BYTES(value, bytes) \ + do { \ + memcpy(value, pos, bytes); \ + length -= (bytes); \ + pos += (bytes); \ + } while (0) +#define PEEK_DISCARD(bytes) \ + do { \ + length -= (bytes); \ + pos += (bytes); \ + } while (0) +#define PEEK_DISCARD_UINT8 PEEK_DISCARD(1) +#define PEEK_DISCARD_UINT16 PEEK_DISCARD(2) +#define PEEK_DISCARD_UINT32 PEEK_DISCARD(4) +#define PEEK_CMP(value, bytes) \ + (length -= (bytes), pos += (bytes), memcmp(pos - bytes, value, bytes)) +#define PEEK_SAVE POKE_SAVE +#define PEEK_RESTORE POKE_RESTORE + +/* LLDP specific. We need a `tlv' pointer. */ +#define POKE_START_LLDP_TLV(type) (tlv = pos, POKE_UINT16(type << 9)) +#define POKE_END_LLDP_TLV \ + (memcpy(&types.f_uint16, tlv, sizeof(uint16_t)), \ + types.f_uint16 |= htons((pos - (tlv + 2)) & 0x01ff), \ + memcpy(tlv, &types.f_uint16, sizeof(uint16_t)), 1) + +/* Same for CDP */ +#define POKE_START_CDP_TLV(type) ((void)POKE_UINT16(type), tlv = pos, POKE_UINT16(0)) +#define POKE_END_CDP_TLV \ + (types.f_uint16 = htons(pos - tlv + 2), \ + memcpy(tlv, &types.f_uint16, sizeof(uint16_t)), 1) + +/* Same for EDP */ +#define POKE_START_EDP_TLV(type) \ + ((void)POKE_UINT8(EDP_TLV_MARKER), (void)POKE_UINT8(type), tlv = pos, POKE_UINT16(0)) +#define POKE_END_EDP_TLV POKE_END_CDP_TLV + +#endif /* _FRAME_H */ diff --git a/src/daemon/interfaces-bpf.c b/src/daemon/interfaces-bpf.c new file mode 100644 index 0000000..2091eaf --- /dev/null +++ b/src/daemon/interfaces-bpf.c @@ -0,0 +1,117 @@ +/* -*- 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 "lldpd.h" +#include <unistd.h> +#include <errno.h> +#include <net/bpf.h> + +struct bpf_buffer { + size_t len; /* Total length of the buffer */ + struct bpf_hdr data[0]; +}; + +int +ifbpf_phys_init(struct lldpd *cfg, struct lldpd_hardware *hardware) +{ + struct bpf_buffer *buffer = NULL; + int fd = -1; + + log_debug("interfaces", "initialize ethernet device %s", hardware->h_ifname); + if ((fd = priv_iface_init(hardware->h_ifindex, hardware->h_ifname)) == -1) + return -1; + + /* Allocate receive buffer */ + hardware->h_data = buffer = malloc(ETHER_MAX_LEN + + BPF_WORDALIGN(sizeof(struct bpf_hdr)) + sizeof(struct bpf_buffer)); + if (buffer == NULL) { + log_warn("interfaces", "unable to allocate buffer space for BPF on %s", + hardware->h_ifname); + goto end; + } + buffer->len = ETHER_MAX_LEN + BPF_WORDALIGN(sizeof(struct bpf_hdr)); + + /* Setup multicast */ + interfaces_setup_multicast(cfg, hardware->h_ifname, 0); + + hardware->h_sendfd = fd; /* Send */ + + levent_hardware_add_fd(hardware, fd); /* Receive */ + log_debug("interfaces", "interface %s initialized (fd=%d)", hardware->h_ifname, + fd); + return 0; + +end: + if (fd >= 0) close(fd); + free(buffer); + hardware->h_data = NULL; + return -1; +} + +/* Ethernet send/receive through BPF */ +static int +ifbpf_eth_send(struct lldpd *cfg, struct lldpd_hardware *hardware, char *buffer, + size_t size) +{ + log_debug("interfaces", "send PDU to ethernet device %s (fd=%d)", + hardware->h_ifname, hardware->h_sendfd); + return write(hardware->h_sendfd, buffer, size); +} + +static int +ifbpf_eth_recv(struct lldpd *cfg, struct lldpd_hardware *hardware, int fd, char *buffer, + size_t size) +{ + struct bpf_buffer *bpfbuf = hardware->h_data; + struct bpf_hdr *bh; + log_debug("interfaces", "receive PDU from ethernet device %s", + hardware->h_ifname); + + /* We assume we have only receive one packet (unbuffered mode). Dunno if + * this is correct. */ + if (read(fd, bpfbuf->data, bpfbuf->len) == -1) { + if (errno == ENETDOWN) { + log_debug("interfaces", + "error while receiving frame on %s (network down)", + hardware->h_ifname); + } else { + log_warn("interfaces", "error while receiving frame on %s", + hardware->h_ifname); + hardware->h_rx_discarded_cnt++; + } + return -1; + } + bh = (struct bpf_hdr *)bpfbuf->data; + if (bh->bh_caplen < size) size = bh->bh_caplen; + memcpy(buffer, (char *)bpfbuf->data + bh->bh_hdrlen, size); + + return size; +} + +static int +ifbpf_eth_close(struct lldpd *cfg, struct lldpd_hardware *hardware) +{ + log_debug("interfaces", "close ethernet device %s", hardware->h_ifname); + interfaces_setup_multicast(cfg, hardware->h_ifname, 1); + return 0; +} + +struct lldpd_ops bpf_ops = { + .send = ifbpf_eth_send, + .recv = ifbpf_eth_recv, + .cleanup = ifbpf_eth_close, +}; diff --git a/src/daemon/interfaces-bsd.c b/src/daemon/interfaces-bsd.c new file mode 100644 index 0000000..a8248fe --- /dev/null +++ b/src/daemon/interfaces-bsd.c @@ -0,0 +1,641 @@ +/* -*- 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 "lldpd.h" + +#include <unistd.h> +#include <ifaddrs.h> +#include <errno.h> +#include <ctype.h> +#include <sys/param.h> +#include <sys/sysctl.h> +#include <sys/ioctl.h> +#include <net/bpf.h> +#include <net/if_types.h> +#include <net/if_media.h> +#include <net/if_dl.h> +#if defined HOST_OS_FREEBSD +# include <net/if_vlan_var.h> +# include <net/if_bridgevar.h> +# include <net/if_lagg.h> +#elif defined HOST_OS_DRAGONFLY +# include <net/vlan/if_vlan_var.h> +# include <net/bridge/if_bridgevar.h> +#elif defined HOST_OS_OPENBSD +# include <net/if_vlan_var.h> +# include <net/if_bridge.h> +# include <net/if_trunk.h> +#elif defined HOST_OS_NETBSD +# include <net/if_vlanvar.h> +# include <net/if_bridgevar.h> +# include <net/agr/if_agrioctl.h> +#elif defined HOST_OS_OSX +# include <osx/if_vlan_var.h> +# include <osx/if_bridgevar.h> +# include <osx/if_bond_var.h> +#endif + +#ifndef IFDESCRSIZE +# define IFDESCRSIZE 64 +#endif + +static int +ifbsd_check_wireless(struct lldpd *cfg, struct ifaddrs *ifaddr, + struct interfaces_device *iface) +{ + struct ifmediareq ifmr = {}; + strlcpy(ifmr.ifm_name, iface->name, sizeof(ifmr.ifm_name)); + if (ioctl(cfg->g_sock, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0 || + IFM_TYPE(ifmr.ifm_current) != IFM_IEEE80211) + return 0; /* Not wireless either */ + iface->type |= IFACE_WIRELESS_T | IFACE_PHYSICAL_T; + return 0; +} + +static void +ifbsd_check_bridge(struct lldpd *cfg, struct interfaces_device_list *interfaces, + struct interfaces_device *master) +{ + static size_t ifbic_len = 64; + struct ifbreq *req = NULL; + struct ifbifconf bifc = {}; + +retry_alloc: + if ((req = realloc(req, ifbic_len)) == NULL) { + log_warn("interfaces", "unable to allocate memory to query bridge %s", + master->name); + free(bifc.ifbic_req); + return; + } + bifc.ifbic_len = ifbic_len; + bifc.ifbic_req = req; + +#if defined HOST_OS_FREEBSD || defined HOST_OS_NETBSD || defined HOST_OS_OSX || \ + defined HOST_OS_DRAGONFLY + struct ifdrv ifd = { .ifd_cmd = BRDGGIFS, + .ifd_len = sizeof(bifc), + .ifd_data = &bifc }; + + strlcpy(ifd.ifd_name, master->name, sizeof(ifd.ifd_name)); + if (ioctl(cfg->g_sock, SIOCGDRVSPEC, (caddr_t)&ifd) < 0) { + log_debug("interfaces", "%s is not a bridge", master->name); + return; + } +#elif defined HOST_OS_OPENBSD + strlcpy(bifc.ifbic_name, master->name, sizeof(bifc.ifbic_name)); + if (ioctl(cfg->g_sock, SIOCBRDGIFS, (caddr_t)&bifc) < 0) { + log_debug("interfaces", "%s is not a bridge", master->name); + return; + } +#else +# error Unsupported OS +#endif + if (bifc.ifbic_len >= ifbic_len) { + ifbic_len = bifc.ifbic_len + 1; + goto retry_alloc; + } + for (int i = 0; i < bifc.ifbic_len / sizeof(*req); i++) { + struct interfaces_device *slave = + interfaces_nametointerface(interfaces, req[i].ifbr_ifsname); + if (slave == NULL) { + log_warnx("interfaces", + "%s should be bridged to %s but we don't know %s", + req[i].ifbr_ifsname, master->name, req[i].ifbr_ifsname); + continue; + } + log_debug("interfaces", "%s is bridged to %s", slave->name, + master->name); + slave->upper = master; + } + master->type |= IFACE_BRIDGE_T; +} + +static void +ifbsd_check_bond(struct lldpd *cfg, struct interfaces_device_list *interfaces, + struct interfaces_device *master) +{ +#if defined HOST_OS_OPENBSD +/* OpenBSD is the same as FreeBSD, just lagg->trunk */ +# define lagg_reqport trunk_reqport +# define lagg_reqall trunk_reqall +# define SIOCGLAGG SIOCGTRUNK +# define LAGG_MAX_PORTS TRUNK_MAX_PORTS +#endif +#if defined HOST_OS_OPENBSD || defined HOST_OS_FREEBSD + struct lagg_reqport rpbuf[LAGG_MAX_PORTS]; + struct lagg_reqall ra = { .ra_size = sizeof(rpbuf), .ra_port = rpbuf }; + strlcpy(ra.ra_ifname, master->name, IFNAMSIZ); + if (ioctl(cfg->g_sock, SIOCGLAGG, (caddr_t)&ra) < 0) { + log_debug("interfaces", "%s is not a bond", master->name); + return; + } + + for (int i = 0; i < ra.ra_ports; i++) { + struct interfaces_device *slave; + slave = interfaces_nametointerface(interfaces, rpbuf[i].rp_portname); + if (slave == NULL) { + log_warnx("interfaces", + "%s should be enslaved to %s but we don't know %s", + rpbuf[i].rp_portname, master->name, rpbuf[i].rp_portname); + continue; + } + log_debug("interfaces", "%s is enslaved to bond %s", slave->name, + master->name); + slave->upper = master; + } + master->type |= IFACE_BOND_T; +#elif defined HOST_OS_NETBSD + /* No max, we consider a maximum of 24 ports */ + char buf[sizeof(struct agrportinfo) * 24] = {}; + size_t buflen = sizeof(buf); + struct agrreq ar = { .ar_version = AGRREQ_VERSION, + .ar_cmd = AGRCMD_PORTLIST, + .ar_buf = buf, + .ar_buflen = buflen }; + struct ifreq ifr = { .ifr_data = &ar }; + struct agrportlist *apl = (void *)buf; + struct agrportinfo *api = (void *)(apl + 1); + strlcpy(ifr.ifr_name, master->name, sizeof(ifr.ifr_name)); + if (ioctl(cfg->g_sock, SIOCGETAGR, &ifr) == -1) { + if (errno == E2BIG) { + log_warnx("interfaces", + "%s is a too big aggregate. Please, report the problem", + master->name); + } else { + log_debug("interfaces", "%s is not an aggregate", master->name); + } + return; + } + for (int i = 0; i < apl->apl_nports; i++, api++) { + struct interfaces_device *slave; + slave = interfaces_nametointerface(interfaces, api->api_ifname); + if (slave == NULL) { + log_warnx("interfaces", + "%s should be enslaved to %s but we don't know %s", + api->api_ifname, master->name, api->api_ifname); + continue; + } + log_debug("interfaces", "%s is enslaved to bond %s", slave->name, + master->name); + slave->upper = master; + } + master->type |= IFACE_BOND_T; +#elif defined HOST_OS_OSX + struct if_bond_req ibr = { .ibr_op = IF_BOND_OP_GET_STATUS, + .ibr_ibru = { + .ibru_status = { .ibsr_version = IF_BOND_STATUS_REQ_VERSION } } }; + struct ifreq ifr = { .ifr_data = (caddr_t)&ibr }; + strlcpy(ifr.ifr_name, master->name, sizeof(ifr.ifr_name)); + if (ioctl(cfg->g_sock, SIOCGIFBOND, (caddr_t)&ifr) < 0) { + log_debug("interfaces", "%s is not an aggregate", master->name); + return; + } + master->type |= IFACE_BOND_T; + if (ibr.ibr_ibru.ibru_status.ibsr_total == 0) { + log_debug("interfaces", "no members for bond %s", master->name); + return; + } + + struct if_bond_status_req *ibsr_p = &ibr.ibr_ibru.ibru_status; + ibsr_p->ibsr_buffer = + malloc(sizeof(struct if_bond_status) * ibsr_p->ibsr_total); + if (ibsr_p->ibsr_buffer == NULL) { + log_warnx("interfaces", "not enough memory to check bond members"); + return; + } + ibsr_p->ibsr_count = ibsr_p->ibsr_total; + if (ioctl(cfg->g_sock, SIOCGIFBOND, (caddr_t)&ifr) < 0) { + log_warn("interfaces", "unable to get members for bond %s", + master->name); + goto end; + } + + struct if_bond_status *ibs_p = (struct if_bond_status *)ibsr_p->ibsr_buffer; + for (int i = 0; i < ibsr_p->ibsr_total; i++, ibs_p++) { + struct interfaces_device *slave; + slave = interfaces_nametointerface(interfaces, ibs_p->ibs_if_name); + if (slave == NULL) { + log_warnx("interfaces", + "%s should be enslaved to %s but we don't know %s", + ibs_p->ibs_if_name, master->name, ibs_p->ibs_if_name); + continue; + } + log_debug("interfaces", "%s is enslaved to bond %s", slave->name, + master->name); + slave->upper = master; + } +end: + free(ibsr_p->ibsr_buffer); +#elif defined HOST_OS_DRAGONFLY + log_debug("interfaces", "DragonFly BSD does not support link aggregation"); +#else +# error Unsupported OS +#endif +} + +static void +ifbsd_check_vlan(struct lldpd *cfg, struct interfaces_device_list *interfaces, + struct interfaces_device *vlan) +{ + struct interfaces_device *lower; + struct vlanreq vreq = {}; + struct ifreq ifr = { .ifr_data = (caddr_t)&vreq }; + strlcpy(ifr.ifr_name, vlan->name, sizeof(ifr.ifr_name)); + if (ioctl(cfg->g_sock, SIOCGETVLAN, (caddr_t)&ifr) < 0) { + log_debug("interfaces", "%s is not a VLAN", vlan->name); + return; + } + if (strlen(vreq.vlr_parent) == 0) { + log_debug("interfaces", "%s is a VLAN but has no lower interface", + vlan->name); + vlan->lower = NULL; + vlan->type |= IFACE_VLAN_T; + return; + } + lower = interfaces_nametointerface(interfaces, vreq.vlr_parent); + if (lower == NULL) { + log_warnx("interfaces", + "%s should be a VLAN of %s but %s does not exist", vlan->name, + vreq.vlr_parent, vreq.vlr_parent); + return; + } + log_debug("interfaces", "%s is VLAN %d of %s", vlan->name, vreq.vlr_tag, + lower->name); + vlan->lower = lower; + bitmap_set(vlan->vlan_bmap, vreq.vlr_tag); + vlan->type |= IFACE_VLAN_T; +} + +static void +ifbsd_check_physical(struct lldpd *cfg, struct interfaces_device_list *interfaces, + struct interfaces_device *iface) +{ + if (iface->type & + (IFACE_VLAN_T | IFACE_BOND_T | IFACE_BRIDGE_T | IFACE_PHYSICAL_T)) + return; + + if (!(iface->flags & (IFF_MULTICAST | IFF_BROADCAST))) { + log_debug("interfaces", + "skip %s: not able to do multicast nor broadcast", iface->name); + return; + } + log_debug("interfaces", "%s is a physical interface", iface->name); + iface->type |= IFACE_PHYSICAL_T; +} + +/* Remove any dangerous interface. Currently, only p2p0 is removed as it + * triggers some AirDrop functionality when we send something on it. + * See: https://github.com/lldpd/lldpd/issues/61 + */ +static void +ifbsd_denylist(struct lldpd *cfg, struct interfaces_device_list *interfaces) +{ +#ifdef HOST_OS_OSX + struct interfaces_device *iface = NULL; + TAILQ_FOREACH (iface, interfaces, next) { + int i; + if (strncmp(iface->name, "p2p", 3)) continue; + if (strlen(iface->name) < 4) continue; + for (i = 3; + iface->name[i] != '\0' && isdigit((unsigned char)(iface->name[i])); + i++) + ; + if (iface->name[i] == '\0') { + log_debug("interfaces", "skip %s: AirDrop interface", + iface->name); + iface->ignore = 1; + } + } +#endif +} + +static struct interfaces_device * +ifbsd_extract_device(struct lldpd *cfg, struct ifaddrs *ifaddr) +{ + struct interfaces_device *iface = NULL; + struct sockaddr_dl *saddrdl = + ALIGNED_CAST(struct sockaddr_dl *, ifaddr->ifa_addr); + if ((saddrdl->sdl_type != IFT_BRIDGE) && (saddrdl->sdl_type != IFT_L2VLAN) && + (saddrdl->sdl_type != IFT_ETHER)) { + log_debug("interfaces", "skip %s: not an ethernet device (%d)", + ifaddr->ifa_name, saddrdl->sdl_type); + return NULL; + } + if ((iface = calloc(1, sizeof(struct interfaces_device))) == NULL) { + log_warn("interfaces", "unable to allocate memory for %s", + ifaddr->ifa_name); + return NULL; + } + + iface->index = saddrdl->sdl_index; + iface->name = strdup(ifaddr->ifa_name); + iface->flags = ifaddr->ifa_flags; + + /* MAC address */ + iface->address = malloc(ETHER_ADDR_LEN); + if (iface->address) memcpy(iface->address, LLADDR(saddrdl), ETHER_ADDR_LEN); + + /* Grab description */ +#ifdef SIOCGIFDESCR +# if defined HOST_OS_FREEBSD || defined HOST_OS_OPENBSD + iface->alias = malloc(IFDESCRSIZE); + if (iface->alias) { +# if defined HOST_OS_FREEBSD + struct ifreq ifr = { .ifr_buffer = { .buffer = iface->alias, + .length = IFDESCRSIZE } }; +# else + struct ifreq ifr = { .ifr_data = (caddr_t)iface->alias }; +# endif + strlcpy(ifr.ifr_name, ifaddr->ifa_name, sizeof(ifr.ifr_name)); + if (ioctl(cfg->g_sock, SIOCGIFDESCR, (caddr_t)&ifr) < 0) { + free(iface->alias); + iface->alias = NULL; + } + } +# endif +#endif /* SIOCGIFDESCR */ + + if (ifbsd_check_wireless(cfg, ifaddr, iface) == -1) { + interfaces_free_device(iface); + return NULL; + } + + return iface; +} + +static void +ifbsd_extract(struct lldpd *cfg, struct interfaces_device_list *interfaces, + struct interfaces_address_list *addresses, struct ifaddrs *ifaddr) +{ + struct interfaces_address *address = NULL; + struct interfaces_device *device = NULL; + if (!ifaddr->ifa_name) return; + if (!ifaddr->ifa_addr) return; + switch (ifaddr->ifa_addr->sa_family) { + case AF_LINK: + log_debug("interfaces", "grabbing information on interface %s", + ifaddr->ifa_name); + device = ifbsd_extract_device(cfg, ifaddr); + if (device) { +#if defined HOST_OS_OPENBSD + /* On OpenBSD, the interface can have IFF_RUNNING but be down. + */ + struct if_data *ifdata; + ifdata = ifaddr->ifa_data; + if (!LINK_STATE_IS_UP(ifdata->ifi_link_state)) + device->flags &= ~IFF_RUNNING; +#endif + TAILQ_INSERT_TAIL(interfaces, device, next); + } + break; + case AF_INET: + case AF_INET6: + log_debug("interfaces", "got an IP address on %s", ifaddr->ifa_name); + address = malloc(sizeof(struct interfaces_address)); + if (address == NULL) { + log_warn("interfaces", + "not enough memory for a new IP address on %s", + ifaddr->ifa_name); + return; + } + address->flags = ifaddr->ifa_flags; + address->index = if_nametoindex(ifaddr->ifa_name); + memcpy(&address->address, ifaddr->ifa_addr, + (ifaddr->ifa_addr->sa_family == AF_INET) ? + sizeof(struct sockaddr_in) : + sizeof(struct sockaddr_in6)); + TAILQ_INSERT_TAIL(addresses, address, next); + break; + default: + log_debug("interfaces", "unhandled family %d for interface %s", + ifaddr->ifa_addr->sa_family, ifaddr->ifa_name); + } +} + +static void +ifbsd_macphy(struct lldpd *cfg, struct lldpd_hardware *hardware) +{ +#ifdef ENABLE_DOT3 + struct ifmediareq ifmr = {}; +# ifdef HAVE_TYPEOF + typeof(ifmr.ifm_ulist[0]) media_list[32] = {}; +# else + int media_list[32] = {}; +# endif + ifmr.ifm_ulist = media_list; + ifmr.ifm_count = 32; + struct lldpd_port *port = &hardware->h_lport; + unsigned int duplex; + unsigned int media; + int advertised_ifmedia_to_rfc3636[][3] = { + { IFM_10_T, LLDP_DOT3_LINK_AUTONEG_10BASE_T, + LLDP_DOT3_LINK_AUTONEG_10BASET_FD }, + { IFM_10_STP, LLDP_DOT3_LINK_AUTONEG_10BASE_T, + LLDP_DOT3_LINK_AUTONEG_10BASET_FD }, + { IFM_100_TX, LLDP_DOT3_LINK_AUTONEG_100BASE_TX, + LLDP_DOT3_LINK_AUTONEG_100BASE_TXFD }, + { IFM_100_T4, LLDP_DOT3_LINK_AUTONEG_100BASE_T4, + LLDP_DOT3_LINK_AUTONEG_100BASE_T4 }, + { IFM_100_T2, LLDP_DOT3_LINK_AUTONEG_100BASE_T2, + LLDP_DOT3_LINK_AUTONEG_100BASE_T2FD }, + { IFM_1000_SX, LLDP_DOT3_LINK_AUTONEG_1000BASE_X, + LLDP_DOT3_LINK_AUTONEG_1000BASE_XFD }, + { IFM_1000_LX, LLDP_DOT3_LINK_AUTONEG_1000BASE_X, + LLDP_DOT3_LINK_AUTONEG_1000BASE_XFD }, + { IFM_1000_CX, LLDP_DOT3_LINK_AUTONEG_1000BASE_X, + LLDP_DOT3_LINK_AUTONEG_1000BASE_XFD }, + { IFM_1000_T, LLDP_DOT3_LINK_AUTONEG_1000BASE_T, + LLDP_DOT3_LINK_AUTONEG_1000BASE_TFD }, + { 0, 0, 0 } + }; + int current_ifmedia_to_rfc3636[][3] = { { IFM_10_T, LLDP_DOT3_MAU_10BASETHD, + LLDP_DOT3_MAU_10BASETFD }, + { IFM_10_STP, LLDP_DOT3_MAU_10BASETHD, LLDP_DOT3_MAU_10BASETFD }, + { IFM_10_2, LLDP_DOT3_MAU_10BASE2, LLDP_DOT3_MAU_10BASE2 }, + { IFM_10_5, LLDP_DOT3_MAU_10BASE5, LLDP_DOT3_MAU_10BASE5 }, + { IFM_100_TX, LLDP_DOT3_MAU_100BASETXHD, LLDP_DOT3_MAU_100BASETXFD }, + { IFM_100_FX, LLDP_DOT3_MAU_100BASEFXHD, LLDP_DOT3_MAU_100BASEFXFD }, + { IFM_100_T2, LLDP_DOT3_MAU_100BASET2HD, LLDP_DOT3_MAU_100BASET2FD }, + { IFM_1000_SX, LLDP_DOT3_MAU_1000BASESXHD, LLDP_DOT3_MAU_1000BASESXFD }, + { IFM_10_FL, LLDP_DOT3_MAU_10BASEFLHD, LLDP_DOT3_MAU_10BASEFLFD }, + { IFM_1000_LX, LLDP_DOT3_MAU_1000BASELXHD, LLDP_DOT3_MAU_1000BASELXFD }, + { IFM_1000_CX, LLDP_DOT3_MAU_1000BASECXHD, LLDP_DOT3_MAU_1000BASECXFD }, + { IFM_1000_T, LLDP_DOT3_MAU_1000BASETHD, LLDP_DOT3_MAU_1000BASETFD }, + { IFM_10G_LR, LLDP_DOT3_MAU_10GIGBASELR, LLDP_DOT3_MAU_10GIGBASELR }, + { IFM_10G_SR, LLDP_DOT3_MAU_10GIGBASESR, LLDP_DOT3_MAU_10GIGBASESR }, + { IFM_10G_CX4, LLDP_DOT3_MAU_10GIGBASELX4, LLDP_DOT3_MAU_10GIGBASELX4 }, +# ifdef IFM_10G_T + { IFM_10G_T, LLDP_DOT3_MAU_10GIGBASECX4, LLDP_DOT3_MAU_10GIGBASECX4 }, +# endif +# ifdef IFM_10G_TWINAX + { IFM_10G_TWINAX, LLDP_DOT3_MAU_10GIGBASECX4, + LLDP_DOT3_MAU_10GIGBASECX4 }, +# endif +# ifdef IFM_10G_TWINAX_LONG + { IFM_10G_TWINAX_LONG, LLDP_DOT3_MAU_10GIGBASECX4, + LLDP_DOT3_MAU_10GIGBASECX4 }, +# endif +# ifdef IFM_10G_LRM + { IFM_10G_LRM, LLDP_DOT3_MAU_10GIGBASELR, LLDP_DOT3_MAU_10GIGBASELR }, +# endif +# ifdef IFM_10G_SFP_CU + { IFM_10G_SFP_CU, LLDP_DOT3_MAU_10GIGBASECX4, + LLDP_DOT3_MAU_10GIGBASECX4 }, +# endif + { 0, 0, 0 } }; + + log_debug("interfaces", "get MAC/phy for %s", hardware->h_ifname); + strlcpy(ifmr.ifm_name, hardware->h_ifname, sizeof(ifmr.ifm_name)); + if (ioctl(cfg->g_sock, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) { + log_debug("interfaces", "unable to get media information from %s", + hardware->h_ifname); + return; + } + if (IFM_TYPE(ifmr.ifm_current) != IFM_ETHER) { + log_warnx("interfaces", + "cannot get media information from %s: not an ethernet device", + hardware->h_ifname); + return; + } + if ((ifmr.ifm_status & IFM_ACTIVE) == 0) { + log_debug("interfaces", "interface %s is now down, skip", + hardware->h_ifname); + return; + } + if (ifmr.ifm_count == 0) { + log_warnx("interfaces", "no media information available on %s", + hardware->h_ifname); + return; + } + port->p_macphy.autoneg_support = port->p_macphy.autoneg_enabled = 0; + for (int m = 0; m < ifmr.ifm_count; m++) { + media = IFM_SUBTYPE(ifmr.ifm_ulist[m]); + duplex = !!(IFM_OPTIONS(ifmr.ifm_ulist[m]) & IFM_FDX); + if (media == IFM_AUTO) { + port->p_macphy.autoneg_support = 1; + port->p_macphy.autoneg_enabled = + (IFM_SUBTYPE(ifmr.ifm_current) == IFM_AUTO); + continue; + } + + int found = 0; + for (int j = 0; advertised_ifmedia_to_rfc3636[j][0]; j++) { + if (advertised_ifmedia_to_rfc3636[j][0] == media) { + port->p_macphy.autoneg_advertised |= + advertised_ifmedia_to_rfc3636[j][1 + duplex]; + found = 1; + break; + } + } + if (!found) + port->p_macphy.autoneg_advertised |= + LLDP_DOT3_LINK_AUTONEG_OTHER; + } + + port->p_macphy.mau_type = 0; + media = IFM_SUBTYPE(ifmr.ifm_active); + duplex = !!(IFM_OPTIONS(ifmr.ifm_active) & IFM_FDX); + for (int j = 0; current_ifmedia_to_rfc3636[j][0]; j++) { + if (current_ifmedia_to_rfc3636[j][0] == media) { + port->p_macphy.mau_type = + current_ifmedia_to_rfc3636[j][1 + duplex]; + break; + } + } +#endif +} + +extern struct lldpd_ops bpf_ops; +void +interfaces_update(struct lldpd *cfg) +{ + struct lldpd_hardware *hardware; + struct interfaces_device *iface; + struct interfaces_device_list *interfaces; + struct interfaces_address_list *addresses; + struct ifaddrs *ifaddrs = NULL, *ifaddr; + + interfaces = malloc(sizeof(struct interfaces_device_list)); + addresses = malloc(sizeof(struct interfaces_address_list)); + if (interfaces == NULL || addresses == NULL) { + log_warnx("interfaces", "unable to allocate memory"); + goto end; + } + TAILQ_INIT(interfaces); + TAILQ_INIT(addresses); + if (getifaddrs(&ifaddrs) < 0) { + log_warnx("interfaces", "unable to get list of interfaces"); + goto end; + } + + for (ifaddr = ifaddrs; ifaddr != NULL; ifaddr = ifaddr->ifa_next) { + ifbsd_extract(cfg, interfaces, addresses, ifaddr); + } + /* Link interfaces together if needed */ + TAILQ_FOREACH (iface, interfaces, next) { + ifbsd_check_bridge(cfg, interfaces, iface); + ifbsd_check_bond(cfg, interfaces, iface); + ifbsd_check_vlan(cfg, interfaces, iface); + ifbsd_check_physical(cfg, interfaces, iface); + } + + ifbsd_denylist(cfg, interfaces); + interfaces_helper_allowlist(cfg, interfaces); + interfaces_helper_physical(cfg, interfaces, &bpf_ops, ifbpf_phys_init); +#ifdef ENABLE_DOT1 + interfaces_helper_vlan(cfg, interfaces); +#endif + interfaces_helper_mgmt(cfg, addresses, interfaces); + interfaces_helper_chassis(cfg, interfaces); + + /* Mac/PHY */ + TAILQ_FOREACH (hardware, &cfg->g_hardware, h_entries) { + if (!hardware->h_flags) continue; + ifbsd_macphy(cfg, hardware); + interfaces_helper_promisc(cfg, hardware); + } + + if (cfg->g_iface_event == NULL) { + int s; + log_debug("interfaces", "subscribe to route socket notifications"); + if ((s = socket(PF_ROUTE, SOCK_RAW, 0)) < 0) { + log_warn("interfaces", "unable to open route socket"); + goto end; + } + +#ifdef ROUTE_MSGFILTER + unsigned int rtfilter; + rtfilter = ROUTE_FILTER(RTM_IFINFO); + if (setsockopt(s, PF_ROUTE, ROUTE_MSGFILTER, &rtfilter, + sizeof(rtfilter)) == -1) + log_warn("interfaces", + "unable to set filter for interface updates"); +#endif + + if (levent_iface_subscribe(cfg, s) == -1) close(s); + } + +end: + interfaces_free_devices(interfaces); + interfaces_free_addresses(addresses); + if (ifaddrs) freeifaddrs(ifaddrs); +} + +void +interfaces_cleanup(struct lldpd *cfg) +{ +} diff --git a/src/daemon/interfaces-linux.c b/src/daemon/interfaces-linux.c new file mode 100644 index 0000000..e764943 --- /dev/null +++ b/src/daemon/interfaces-linux.c @@ -0,0 +1,1046 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "lldpd.h" + +#include <stdio.h> +#include <unistd.h> +#include <inttypes.h> +#include <errno.h> +#include <sys/ioctl.h> +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wdocumentation" +#endif +#include <netinet/in.h> +#include <linux/if_vlan.h> +#include <linux/if_bonding.h> +#include <linux/if_bridge.h> +#include <linux/wireless.h> +#include <linux/sockios.h> +#include <linux/if_packet.h> +#include <linux/ethtool.h> +#if defined(__clang__) +# pragma clang diagnostic pop +#endif + +#define SYSFS_PATH_MAX 256 +#define MAX_PORTS 1024 +#define MAX_BRIDGES 1024 + +static int +iflinux_eth_init(struct lldpd *cfg, struct lldpd_hardware *hardware) +{ + int fd; + + log_debug("interfaces", "initialize ethernet device %s", hardware->h_ifname); + if ((fd = priv_iface_init(hardware->h_ifindex, hardware->h_ifname)) == -1) + return -1; + hardware->h_sendfd = fd; /* Send */ + + interfaces_setup_multicast(cfg, hardware->h_ifname, 0); + + levent_hardware_add_fd(hardware, fd); /* Receive */ + log_debug("interfaces", "interface %s initialized (fd=%d)", hardware->h_ifname, + fd); + return 0; +} + +/* Generic ethernet send/receive */ +static int +iflinux_eth_send(struct lldpd *cfg, struct lldpd_hardware *hardware, char *buffer, + size_t size) +{ + log_debug("interfaces", "send PDU to ethernet device %s (fd=%d)", + hardware->h_ifname, hardware->h_sendfd); + return write(hardware->h_sendfd, buffer, size); +} + +static int +iflinux_generic_recv(struct lldpd_hardware *hardware, int fd, char *buffer, size_t size, + struct sockaddr_ll *from) +{ + int n, retry = 0; + socklen_t fromlen; + +retry: + fromlen = sizeof(*from); + memset(from, 0, fromlen); + if ((n = recvfrom(fd, buffer, size, 0, (struct sockaddr *)from, &fromlen)) == + -1) { + if (errno == EAGAIN && retry == 0) { + /* There may be an error queued in the socket. Clear it and + * retry. */ + levent_recv_error(fd, hardware->h_ifname); + retry++; + goto retry; + } + if (errno == ENETDOWN) { + log_debug("interfaces", + "error while receiving frame on %s (network down)", + hardware->h_ifname); + } else { + log_warn("interfaces", + "error while receiving frame on %s (retry: %d)", + hardware->h_ifname, retry); + hardware->h_rx_discarded_cnt++; + } + return -1; + } + if (from->sll_pkttype == PACKET_OUTGOING) return -1; + return n; +} + +static int +iflinux_eth_recv(struct lldpd *cfg, struct lldpd_hardware *hardware, int fd, + char *buffer, size_t size) +{ + int n; + struct sockaddr_ll from; + + log_debug("interfaces", "receive PDU from ethernet device %s", + hardware->h_ifname); + if ((n = iflinux_generic_recv(hardware, fd, buffer, size, &from)) == -1) + return -1; + return n; +} + +static int +iflinux_eth_close(struct lldpd *cfg, struct lldpd_hardware *hardware) +{ + log_debug("interfaces", "close ethernet device %s", hardware->h_ifname); + interfaces_setup_multicast(cfg, hardware->h_ifname, 1); + return 0; +} + +static struct lldpd_ops eth_ops = { + .send = iflinux_eth_send, + .recv = iflinux_eth_recv, + .cleanup = iflinux_eth_close, +}; + +static int +iflinux_is_bridge(struct lldpd *cfg, struct interfaces_device_list *interfaces, + struct interfaces_device *iface) +{ +#ifdef ENABLE_OLDIES + struct interfaces_device *port; + char path[SYSFS_PATH_MAX]; + int f; + + if ((snprintf(path, SYSFS_PATH_MAX, SYSFS_CLASS_NET "%s/" SYSFS_BRIDGE_FDB, + iface->name)) >= SYSFS_PATH_MAX) + log_warnx("interfaces", "path truncated"); + if ((f = priv_open(path)) < 0) return 0; + close(f); + + /* Also grab all ports */ + TAILQ_FOREACH (port, interfaces, next) { + if (port->upper) continue; + if (snprintf(path, SYSFS_PATH_MAX, + SYSFS_CLASS_NET "%s/" SYSFS_BRIDGE_PORT_SUBDIR "/%s/port_no", + iface->name, port->name) >= SYSFS_PATH_MAX) + log_warnx("interfaces", "path truncated"); + if ((f = priv_open(path)) < 0) continue; + log_debug("interfaces", "port %s is bridged to %s", port->name, + iface->name); + port->upper = iface; + close(f); + } + + return 1; +#else + return 0; +#endif +} + +static int +iflinux_is_vlan(struct lldpd *cfg, struct interfaces_device_list *interfaces, + struct interfaces_device *iface) +{ +#ifdef ENABLE_OLDIES + struct vlan_ioctl_args ifv = {}; + ifv.cmd = GET_VLAN_REALDEV_NAME_CMD; + strlcpy(ifv.device1, iface->name, sizeof(ifv.device1)); + if (ioctl(cfg->g_sock, SIOCGIFVLAN, &ifv) >= 0) { + /* This is a VLAN, get the lower interface and the VID */ + struct interfaces_device *lower = + interfaces_nametointerface(interfaces, ifv.u.device2); + if (!lower) { + log_debug("interfaces", + "unable to find lower interface for VLAN %s", iface->name); + return 0; + } + + memset(&ifv, 0, sizeof(ifv)); + ifv.cmd = GET_VLAN_VID_CMD; + strlcpy(ifv.device1, iface->name, sizeof(ifv.device1)); + if (ioctl(cfg->g_sock, SIOCGIFVLAN, &ifv) < 0) { + log_debug("interfaces", "unable to find VID for VLAN %s", + iface->name); + return 0; + } + + iface->lower = lower; + bitmap_set(iface->vlan_bmap, ifv.u.VID); + return 1; + } +#endif + return 0; +} + +static int +iflinux_is_bond(struct lldpd *cfg, struct interfaces_device_list *interfaces, + struct interfaces_device *master) +{ +#ifdef ENABLE_OLDIES + /* Shortcut if we detect the new team driver. Upper and lower links + * should already be set with netlink in this case. */ + if (master->driver && !strcmp(master->driver, "team")) { + return 1; + } + + struct ifreq ifr = {}; + struct ifbond ifb = {}; + strlcpy(ifr.ifr_name, master->name, sizeof(ifr.ifr_name)); + ifr.ifr_data = (char *)&ifb; + if (ioctl(cfg->g_sock, SIOCBONDINFOQUERY, &ifr) >= 0) { + while (ifb.num_slaves--) { + struct ifslave ifs; + memset(&ifr, 0, sizeof(ifr)); + memset(&ifs, 0, sizeof(ifs)); + strlcpy(ifr.ifr_name, master->name, sizeof(ifr.ifr_name)); + ifr.ifr_data = (char *)&ifs; + ifs.slave_id = ifb.num_slaves; + if (ioctl(cfg->g_sock, SIOCBONDSLAVEINFOQUERY, &ifr) >= 0) { + struct interfaces_device *slave = + interfaces_nametointerface(interfaces, + ifs.slave_name); + if (slave == NULL) continue; + if (slave->upper) continue; + log_debug("interfaces", + "interface %s is enslaved to %s", slave->name, + master->name); + slave->upper = master; + } + } + return 1; + } +#endif + return 0; +} + +/** + * Get permanent MAC from ethtool. + * + * Return 0 on success, -1 on error. + */ +static int +iflinux_get_permanent_mac_ethtool(struct lldpd *cfg, + struct interfaces_device_list *interfaces, struct interfaces_device *iface) +{ + int ret = -1; + struct ifreq ifr = {}; + struct ethtool_perm_addr *epaddr = + calloc(sizeof(struct ethtool_perm_addr) + ETHER_ADDR_LEN, 1); + if (epaddr == NULL) goto end; + + strlcpy(ifr.ifr_name, iface->name, sizeof(ifr.ifr_name)); + epaddr->cmd = ETHTOOL_GPERMADDR; + epaddr->size = ETHER_ADDR_LEN; + ifr.ifr_data = (caddr_t)epaddr; + if (ioctl(cfg->g_sock, SIOCETHTOOL, &ifr) == -1) { + static int once = 0; + if (errno == EPERM && !once) { + log_warnx("interfaces", + "no permission to get permanent MAC address for %s (requires 2.6.19+)", + iface->name); + once = 1; + goto end; + } + if (errno != EPERM) + log_warn("interfaces", + "cannot get permanent MAC address for %s", iface->name); + goto end; + } + if (epaddr->data[0] != 0 || epaddr->data[1] != 0 || epaddr->data[2] != 0 || + epaddr->data[3] != 0 || epaddr->data[4] != 0 || epaddr->data[5] != 0) { + memcpy(iface->address, epaddr->data, ETHER_ADDR_LEN); + ret = 0; + goto end; + } + log_debug("interfaces", "cannot get permanent MAC for %s (all 0)", iface->name); +end: + free(epaddr); + return ret; +} + +/** + * Get permanent MAC address for a bond device. + */ +static void +iflinux_get_permanent_mac_bond(struct lldpd *cfg, + struct interfaces_device_list *interfaces, struct interfaces_device *iface) +{ + struct interfaces_device *master = iface->upper; + int f, state = 0; + FILE *netbond; + const char *slaveif = "Slave Interface: "; + const char *hwaddr = "Permanent HW addr: "; + u_int8_t mac[ETHER_ADDR_LEN]; + char path[SYSFS_PATH_MAX]; + char line[100]; + + /* We have a bond, we need to query it to get real MAC addresses */ + if (snprintf(path, SYSFS_PATH_MAX, "/proc/net/bonding/%s", master->name) >= + SYSFS_PATH_MAX) { + log_warnx("interfaces", "path truncated"); + return; + } + if ((f = priv_open(path)) < 0) { + if (snprintf(path, SYSFS_PATH_MAX, "/proc/self/net/bonding/%s", + master->name) >= SYSFS_PATH_MAX) { + log_warnx("interfaces", "path truncated"); + return; + } + f = priv_open(path); + } + if (f < 0) { + log_warnx("interfaces", "unable to get permanent MAC address for %s", + iface->name); + return; + } + if ((netbond = fdopen(f, "r")) == NULL) { + log_warn("interfaces", "unable to read stream from %s", path); + close(f); + return; + } + /* State 0: + We parse the file to search "Slave Interface: ". If found, go to + state 1. + State 1: + We parse the file to search "Permanent HW addr: ". If found, we get + the mac. + */ + while (fgets(line, sizeof(line), netbond)) { + switch (state) { + case 0: + if (strncmp(line, slaveif, strlen(slaveif)) == 0) { + if (line[strlen(line) - 1] == '\n') + line[strlen(line) - 1] = '\0'; + if (strcmp(iface->name, line + strlen(slaveif)) == 0) + state++; + } + break; + case 1: + if (strncmp(line, hwaddr, strlen(hwaddr)) == 0) { + if (line[strlen(line) - 1] == '\n') + line[strlen(line) - 1] = '\0'; + if (sscanf(line + strlen(hwaddr), + "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", + &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], + &mac[5]) != ETHER_ADDR_LEN) { + log_warn("interfaces", "unable to parse %s", + line + strlen(hwaddr)); + fclose(netbond); + return; + } + memcpy(iface->address, mac, ETHER_ADDR_LEN); + fclose(netbond); + return; + } + break; + } + } + log_warnx("interfaces", "unable to find real MAC address for enslaved %s", + iface->name); + fclose(netbond); +} + +/** + * Get permanent MAC. + */ +static void +iflinux_get_permanent_mac(struct lldpd *cfg, struct interfaces_device_list *interfaces, + struct interfaces_device *iface) +{ + struct interfaces_device *master = iface->upper; + + if (master == NULL || master->type != IFACE_BOND_T) return; + if (iflinux_get_permanent_mac_ethtool(cfg, interfaces, iface) == -1 && + (master->driver == NULL || !strcmp(master->driver, "bonding"))) + /* Fallback to old method for a bond */ + iflinux_get_permanent_mac_bond(cfg, interfaces, iface); +} + +#ifdef ENABLE_DOT3 +# define ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32 (SCHAR_MAX) +# define ETHTOOL_DECLARE_LINK_MODE_MASK(name) \ + uint32_t name[ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32] + +struct ethtool_link_usettings { + struct ethtool_link_settings base; + struct { + ETHTOOL_DECLARE_LINK_MODE_MASK(supported); + ETHTOOL_DECLARE_LINK_MODE_MASK(advertising); + ETHTOOL_DECLARE_LINK_MODE_MASK(lp_advertising); + } link_modes; +}; + +static int +iflinux_ethtool_link_mode_test_bit(unsigned int nr, const uint32_t *mask) +{ + if (nr >= 32 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32) return 0; + return !!(mask[nr / 32] & (1 << (nr % 32))); +} +static void +iflinux_ethtool_link_mode_unset_bit(unsigned int nr, uint32_t *mask) +{ + if (nr >= 32 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32) return; + mask[nr / 32] &= ~(1 << (nr % 32)); +} +static int +iflinux_ethtool_link_mode_is_empty(const uint32_t *mask) +{ + for (unsigned int i = 0; i < ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32; ++i) { + if (mask[i] != 0) return 0; + } + + return 1; +} + +static int +iflinux_ethtool_glink(struct lldpd *cfg, const char *ifname, + struct ethtool_link_usettings *uset) +{ + int rc; + + /* Try with ETHTOOL_GLINKSETTINGS first */ + struct { + struct ethtool_link_settings req; + uint32_t link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32]; + } ecmd; + static int8_t nwords = 0; + struct ifreq ifr = {}; + strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + + if (nwords == 0) { + /* Do a handshake first. We assume that this is device-independant. */ + memset(&ecmd, 0, sizeof(ecmd)); + ecmd.req.cmd = ETHTOOL_GLINKSETTINGS; + ifr.ifr_data = (caddr_t)&ecmd; + rc = ioctl(cfg->g_sock, SIOCETHTOOL, &ifr); + if (rc == 0) { + nwords = -ecmd.req.link_mode_masks_nwords; + log_debug("interfaces", "glinksettings nwords is %" PRId8, + nwords); + } else { + static int once = 0; + if (errno == EPERM && !once) { + log_warnx("interfaces", + "cannot get ethtool link information " + "with GLINKSETTINGS (requires 4.9+). " + "25G+ speeds may be missing in MAC/PHY TLVs"); + once = 1; + } + nwords = -1; + } + } + if (nwords > 0) { + memset(&ecmd, 0, sizeof(ecmd)); + ecmd.req.cmd = ETHTOOL_GLINKSETTINGS; + ecmd.req.link_mode_masks_nwords = nwords; + ifr.ifr_data = (caddr_t)&ecmd; + rc = ioctl(cfg->g_sock, SIOCETHTOOL, &ifr); + if (rc == 0) { + log_debug("interfaces", + "got ethtool results for %s with GLINKSETTINGS", ifname); + memcpy(&uset->base, &ecmd.req, sizeof(uset->base)); + unsigned int u32_offs = 0; + memcpy(uset->link_modes.supported, + &ecmd.link_mode_data[u32_offs], + 4 * ecmd.req.link_mode_masks_nwords); + u32_offs += ecmd.req.link_mode_masks_nwords; + memcpy(uset->link_modes.advertising, + &ecmd.link_mode_data[u32_offs], + 4 * ecmd.req.link_mode_masks_nwords); + u32_offs += ecmd.req.link_mode_masks_nwords; + memcpy(uset->link_modes.lp_advertising, + &ecmd.link_mode_data[u32_offs], + 4 * ecmd.req.link_mode_masks_nwords); + goto end; + } + } + + /* Try with ETHTOOL_GSET */ + struct ethtool_cmd ethc; + memset(ðc, 0, sizeof(ethc)); + ethc.cmd = ETHTOOL_GSET; + ifr.ifr_data = (caddr_t)ðc; + rc = ioctl(cfg->g_sock, SIOCETHTOOL, &ifr); + if (rc == 0) { + /* Do a partial copy (only what we need) */ + log_debug("interfaces", "got ethtool results for %s with GSET", ifname); + memset(uset, 0, sizeof(*uset)); + uset->base.cmd = ETHTOOL_GSET; + uset->base.link_mode_masks_nwords = 1; + uset->link_modes.supported[0] = ethc.supported; + uset->link_modes.advertising[0] = ethc.advertising; + uset->link_modes.lp_advertising[0] = ethc.lp_advertising; + uset->base.speed = (ethc.speed_hi << 16) | ethc.speed; + uset->base.duplex = ethc.duplex; + uset->base.port = ethc.port; + uset->base.autoneg = ethc.autoneg; + } else { + static int once = 0; + if (errno == EPERM && !once) { + log_warnx("interfaces", + "cannot get ethtool link information " + "with GSET (requires 2.6.19+). " + "MAC/PHY TLV will be unavailable"); + once = 1; + } + } +end: + return rc; +} + +/* Fill up MAC/PHY for a given hardware port */ +static void +iflinux_macphy(struct lldpd *cfg, struct lldpd_hardware *hardware) +{ + struct ethtool_link_usettings uset = {}; + struct lldpd_port *port = &hardware->h_lport; + int j; + int advertised_ethtool_to_rfc3636[][2] = { + { ETHTOOL_LINK_MODE_10baseT_Half_BIT, LLDP_DOT3_LINK_AUTONEG_10BASE_T }, + { ETHTOOL_LINK_MODE_10baseT_Full_BIT, + LLDP_DOT3_LINK_AUTONEG_10BASET_FD }, + { ETHTOOL_LINK_MODE_100baseT_Half_BIT, + LLDP_DOT3_LINK_AUTONEG_100BASE_TX }, + { ETHTOOL_LINK_MODE_100baseT_Full_BIT, + LLDP_DOT3_LINK_AUTONEG_100BASE_TXFD }, + { ETHTOOL_LINK_MODE_1000baseT_Half_BIT, + LLDP_DOT3_LINK_AUTONEG_1000BASE_T }, + { ETHTOOL_LINK_MODE_1000baseT_Full_BIT, + LLDP_DOT3_LINK_AUTONEG_1000BASE_TFD }, + { ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, + LLDP_DOT3_LINK_AUTONEG_1000BASE_XFD }, + { ETHTOOL_LINK_MODE_Pause_BIT, LLDP_DOT3_LINK_AUTONEG_FDX_PAUSE }, + { ETHTOOL_LINK_MODE_Asym_Pause_BIT, LLDP_DOT3_LINK_AUTONEG_FDX_APAUSE }, + { -1, 0 } + }; + + log_debug("interfaces", "ask ethtool for the appropriate MAC/PHY for %s", + hardware->h_ifname); + if (iflinux_ethtool_glink(cfg, hardware->h_ifname, &uset) == 0) { + port->p_macphy.autoneg_support = + iflinux_ethtool_link_mode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, + uset.link_modes.supported); + port->p_macphy.autoneg_enabled = + (uset.base.autoneg == AUTONEG_DISABLE) ? 0 : 1; + for (j = 0; advertised_ethtool_to_rfc3636[j][0] >= 0; j++) { + if (iflinux_ethtool_link_mode_test_bit( + advertised_ethtool_to_rfc3636[j][0], + uset.link_modes.advertising)) { + port->p_macphy.autoneg_advertised |= + advertised_ethtool_to_rfc3636[j][1]; + iflinux_ethtool_link_mode_unset_bit( + advertised_ethtool_to_rfc3636[j][0], + uset.link_modes.advertising); + } + } + iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, + uset.link_modes.advertising); + iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_TP_BIT, + uset.link_modes.advertising); + iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_AUI_BIT, + uset.link_modes.advertising); + iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_MII_BIT, + uset.link_modes.advertising); + iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, + uset.link_modes.advertising); + iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_BNC_BIT, + uset.link_modes.advertising); + iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_Pause_BIT, + uset.link_modes.advertising); + iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, + uset.link_modes.advertising); + iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_Backplane_BIT, + uset.link_modes.advertising); + if (!iflinux_ethtool_link_mode_is_empty(uset.link_modes.advertising)) { + port->p_macphy.autoneg_advertised |= + LLDP_DOT3_LINK_AUTONEG_OTHER; + } + switch (uset.base.speed) { + case SPEED_10: + port->p_macphy.mau_type = (uset.base.duplex == DUPLEX_FULL) ? + LLDP_DOT3_MAU_10BASETFD : + LLDP_DOT3_MAU_10BASETHD; + if (uset.base.port == PORT_BNC) + port->p_macphy.mau_type = LLDP_DOT3_MAU_10BASE2; + if (uset.base.port == PORT_FIBRE) + port->p_macphy.mau_type = + (uset.base.duplex == DUPLEX_FULL) ? + LLDP_DOT3_MAU_10BASEFLFD : + LLDP_DOT3_MAU_10BASEFLHD; + break; + case SPEED_100: + port->p_macphy.mau_type = (uset.base.duplex == DUPLEX_FULL) ? + LLDP_DOT3_MAU_100BASETXFD : + LLDP_DOT3_MAU_100BASETXHD; + if (uset.base.port == PORT_BNC) + port->p_macphy.mau_type = + (uset.base.duplex == DUPLEX_FULL) ? + LLDP_DOT3_MAU_100BASET2FD : + LLDP_DOT3_MAU_100BASET2HD; + if (uset.base.port == PORT_FIBRE) + port->p_macphy.mau_type = + (uset.base.duplex == DUPLEX_FULL) ? + LLDP_DOT3_MAU_100BASEFXFD : + LLDP_DOT3_MAU_100BASEFXHD; + break; + case SPEED_1000: + port->p_macphy.mau_type = (uset.base.duplex == DUPLEX_FULL) ? + LLDP_DOT3_MAU_1000BASETFD : + LLDP_DOT3_MAU_1000BASETHD; + if (uset.base.port == PORT_FIBRE) + port->p_macphy.mau_type = + (uset.base.duplex == DUPLEX_FULL) ? + LLDP_DOT3_MAU_1000BASEXFD : + LLDP_DOT3_MAU_1000BASEXHD; + break; + case SPEED_2500: + port->p_macphy.mau_type = LLDP_DOT3_MAU_2P5GIGT; + break; + case SPEED_5000: + port->p_macphy.mau_type = LLDP_DOT3_MAU_5GIGT; + break; + case SPEED_10000: + // Distinguish between RJ45 BaseT, DAC BaseCX4, or Fibre BaseLR + if (uset.base.port == PORT_TP) { + port->p_macphy.mau_type = LLDP_DOT3_MAU_10GBASET; + } else if (uset.base.port == PORT_FIBRE) { + port->p_macphy.mau_type = LLDP_DOT3_MAU_10GIGBASELR; + } else if (uset.base.port == PORT_DA) { + port->p_macphy.mau_type = LLDP_DOT3_MAU_10GIGBASECX4; + } + break; + case SPEED_25000: + // Distinguish between RJ45 BaseT, DAC BaseCR, or Fibre BaseLR + if (uset.base.port == PORT_TP) { + port->p_macphy.mau_type = LLDP_DOT3_MAU_25GBASET; + } else if (uset.base.port == PORT_FIBRE) { + port->p_macphy.mau_type = LLDP_DOT3_MAU_25GBASELR; + } else if (uset.base.port == PORT_DA) { + port->p_macphy.mau_type = LLDP_DOT3_MAU_25GBASECR; + } + break; + case SPEED_40000: + // Same kind of approximation. + port->p_macphy.mau_type = (uset.base.port == PORT_FIBRE) ? + LLDP_DOT3_MAU_40GBASELR4 : + LLDP_DOT3_MAU_40GBASECR4; + break; + case SPEED_50000: + // Same kind of approximation. + port->p_macphy.mau_type = (uset.base.port == PORT_FIBRE) ? + LLDP_DOT3_MAU_50GBASELR : + LLDP_DOT3_MAU_50GBASECR; + break; + case SPEED_100000: + // Ditto + port->p_macphy.mau_type = (uset.base.port == PORT_FIBRE) ? + LLDP_DOT3_MAU_100GBASELR4 : + LLDP_DOT3_MAU_100GBASECR4; + break; + } + if (uset.base.port == PORT_AUI) + port->p_macphy.mau_type = LLDP_DOT3_MAU_AUI; + } +} +#else /* ENABLE_DOT3 */ +static void +iflinux_macphy(struct lldpd *cfg, struct lldpd_hardware *hardware) +{ +} +#endif /* ENABLE_DOT3 */ + +#ifdef ENABLE_OLDIES +struct bond_master { + char name[IFNAMSIZ]; + int index; +}; + +static int +iface_bond_init(struct lldpd *cfg, struct lldpd_hardware *hardware) +{ + struct bond_master *master = hardware->h_data; + int fd; + int un = 1; + + if (!master) return -1; + + log_debug("interfaces", "initialize enslaved device %s", hardware->h_ifname); + + /* First, we get a socket to the raw physical interface */ + if ((fd = priv_iface_init(hardware->h_ifindex, hardware->h_ifname)) == -1) + return -1; + hardware->h_sendfd = fd; + interfaces_setup_multicast(cfg, hardware->h_ifname, 0); + + /* Then, we open a raw interface for the master */ + log_debug("interfaces", "enslaved device %s has master %s(%d)", + hardware->h_ifname, master->name, master->index); + if ((fd = priv_iface_init(master->index, master->name)) == -1) { + close(hardware->h_sendfd); + return -1; + } + /* With bonding and older kernels (< 2.6.27) we need to listen + * to bond device. We use setsockopt() PACKET_ORIGDEV to get + * physical device instead of bond device (works with >= + * 2.6.24). */ + if (setsockopt(fd, SOL_PACKET, PACKET_ORIGDEV, &un, sizeof(un)) == -1) { + log_info("interfaces", + "unable to setsockopt for master bonding device of %s. " + "You will get inaccurate results", + hardware->h_ifname); + } + interfaces_setup_multicast(cfg, master->name, 0); + + levent_hardware_add_fd(hardware, hardware->h_sendfd); + levent_hardware_add_fd(hardware, fd); + log_debug("interfaces", "interface %s initialized (fd=%d,master=%s[%d])", + hardware->h_ifname, hardware->h_sendfd, master->name, fd); + return 0; +} + +static int +iface_bond_recv(struct lldpd *cfg, struct lldpd_hardware *hardware, int fd, + char *buffer, size_t size) +{ + int n; + struct sockaddr_ll from; + struct bond_master *master = hardware->h_data; + + log_debug("interfaces", "receive PDU from enslaved device %s", + hardware->h_ifname); + if ((n = iflinux_generic_recv(hardware, fd, buffer, size, &from)) == -1) + return -1; + if (fd == hardware->h_sendfd) /* We received this on the physical interface. */ + return n; + /* We received this on the bonding interface. Is it really for us? */ + if (from.sll_ifindex == hardware->h_ifindex) /* This is for us */ + return n; + if (from.sll_ifindex == master->index) + /* We don't know from which physical interface it comes (kernel + * < 2.6.24). In doubt, this is for us. */ + return n; + return -1; /* Not for us */ +} + +static int +iface_bond_close(struct lldpd *cfg, struct lldpd_hardware *hardware) +{ + struct bond_master *master = hardware->h_data; + log_debug("interfaces", "closing enslaved device %s", hardware->h_ifname); + interfaces_setup_multicast(cfg, hardware->h_ifname, 1); + interfaces_setup_multicast(cfg, master->name, 1); + free(hardware->h_data); + hardware->h_data = NULL; + return 0; +} + +struct lldpd_ops bond_ops = { + .send = iflinux_eth_send, + .recv = iface_bond_recv, + .cleanup = iface_bond_close, +}; + +static void +iflinux_handle_bond(struct lldpd *cfg, struct interfaces_device_list *interfaces) +{ + struct interfaces_device *iface; + struct interfaces_device *master; + struct lldpd_hardware *hardware; + struct bond_master *bmaster; + int created; + TAILQ_FOREACH (iface, interfaces, next) { + if (!(iface->type & IFACE_PHYSICAL_T)) continue; + if (iface->ignore) continue; + if (!iface->upper || !(iface->upper->type & IFACE_BOND_T)) continue; + + master = iface->upper; + log_debug("interfaces", + "%s is an acceptable enslaved device (master=%s)", iface->name, + master->name); + created = 0; + if ((hardware = lldpd_get_hardware(cfg, iface->name, iface->index)) == + NULL) { + if ((hardware = lldpd_alloc_hardware(cfg, iface->name, + iface->index)) == NULL) { + log_warnx("interfaces", + "Unable to allocate space for %s", iface->name); + continue; + } + created = 1; + } + if (hardware->h_flags) continue; + if (hardware->h_ops != &bond_ops || hardware->h_ifindex_changed) { + if (!created) { + log_debug("interfaces", + "bond %s is converted from another type of interface", + hardware->h_ifname); + if (hardware->h_ops && hardware->h_ops->cleanup) + hardware->h_ops->cleanup(cfg, hardware); + levent_hardware_release(hardware); + levent_hardware_init(hardware); + } + bmaster = hardware->h_data = + calloc(1, sizeof(struct bond_master)); + if (!bmaster) { + log_warn("interfaces", "not enough memory"); + lldpd_hardware_cleanup(cfg, hardware); + continue; + } + } else + bmaster = hardware->h_data; + bmaster->index = master->index; + strlcpy(bmaster->name, master->name, IFNAMSIZ); + if (hardware->h_ops != &bond_ops || hardware->h_ifindex_changed) { + if (iface_bond_init(cfg, hardware) != 0) { + log_warn("interfaces", "unable to initialize %s", + hardware->h_ifname); + lldpd_hardware_cleanup(cfg, hardware); + continue; + } + hardware->h_ops = &bond_ops; + hardware->h_mangle = 1; + } + if (created) + interfaces_helper_add_hardware(cfg, hardware); + else + lldpd_port_cleanup(&hardware->h_lport, 0); + + hardware->h_flags = iface->flags; + iface->ignore = 1; + + /* Get local address */ + memcpy(&hardware->h_lladdr, iface->address, ETHER_ADDR_LEN); + + /* Fill information about port */ + interfaces_helper_port_name_desc(cfg, hardware, iface); + + /* Fill additional info */ +# ifdef ENABLE_DOT3 + hardware->h_lport.p_aggregid = master->index; +# endif + hardware->h_mtu = iface->mtu ? iface->mtu : 1500; + } +} +#endif + +/* Query each interface to get the appropriate driver */ +static void +iflinux_add_driver(struct lldpd *cfg, struct interfaces_device_list *interfaces) +{ + struct interfaces_device *iface; + TAILQ_FOREACH (iface, interfaces, next) { + struct ethtool_drvinfo ethc = { .cmd = ETHTOOL_GDRVINFO }; + struct ifreq ifr = { .ifr_data = (caddr_t)ðc }; + if (iface->driver) continue; + + strlcpy(ifr.ifr_name, iface->name, IFNAMSIZ); + if (ioctl(cfg->g_sock, SIOCETHTOOL, &ifr) == 0) { + iface->driver = strdup(ethc.driver); + log_debug("interfaces", "driver for %s is `%s`", iface->name, + iface->driver); + } + } +} + +/* Query each interface to see if it is a wireless one */ +static void +iflinux_add_wireless(struct lldpd *cfg, struct interfaces_device_list *interfaces) +{ + struct interfaces_device *iface; + TAILQ_FOREACH (iface, interfaces, next) { + struct iwreq iwr = {}; + strlcpy(iwr.ifr_name, iface->name, IFNAMSIZ); + if (ioctl(cfg->g_sock, SIOCGIWNAME, &iwr) >= 0) { + log_debug("interfaces", "%s is wireless", iface->name); + iface->type |= IFACE_WIRELESS_T | IFACE_PHYSICAL_T; + } + } +} + +/* Query each interface to see if it is a bridge */ +static void +iflinux_add_bridge(struct lldpd *cfg, struct interfaces_device_list *interfaces) +{ + struct interfaces_device *iface; + + TAILQ_FOREACH (iface, interfaces, next) { + if (iface->type & + (IFACE_PHYSICAL_T | IFACE_VLAN_T | IFACE_BOND_T | IFACE_BRIDGE_T)) + continue; + if (iflinux_is_bridge(cfg, interfaces, iface)) { + log_debug("interfaces", "interface %s is a bridge", + iface->name); + iface->type |= IFACE_BRIDGE_T; + } + } +} + +/* Query each interface to see if it is a bond */ +static void +iflinux_add_bond(struct lldpd *cfg, struct interfaces_device_list *interfaces) +{ + struct interfaces_device *iface; + + TAILQ_FOREACH (iface, interfaces, next) { + if (iface->type & + (IFACE_PHYSICAL_T | IFACE_VLAN_T | IFACE_BOND_T | IFACE_BRIDGE_T)) + continue; + if (iflinux_is_bond(cfg, interfaces, iface)) { + log_debug("interfaces", "interface %s is a bond", iface->name); + iface->type |= IFACE_BOND_T; + } + } +} + +/* Query each interface to see if it is a vlan */ +static void +iflinux_add_vlan(struct lldpd *cfg, struct interfaces_device_list *interfaces) +{ + struct interfaces_device *iface; + + TAILQ_FOREACH (iface, interfaces, next) { + if (iface->type & + (IFACE_PHYSICAL_T | IFACE_VLAN_T | IFACE_BOND_T | IFACE_BRIDGE_T)) + continue; + if (iflinux_is_vlan(cfg, interfaces, iface)) { + log_debug("interfaces", "interface %s is a VLAN", iface->name); + iface->type |= IFACE_VLAN_T; + } + } +} + +static void +iflinux_add_physical(struct lldpd *cfg, struct interfaces_device_list *interfaces) +{ + struct interfaces_device *iface; + /* Deny some drivers */ + const char *const *rif; + const char *const denied_drivers[] = { "cdc_mbim", "vxlan", NULL }; + + TAILQ_FOREACH (iface, interfaces, next) { + if (iface->type & (IFACE_VLAN_T | IFACE_BOND_T | IFACE_BRIDGE_T)) + continue; + + iface->type &= ~IFACE_PHYSICAL_T; + + /* We request that the interface is able to do either multicast + * or broadcast to be able to send discovery frames. */ + if (!(iface->flags & (IFF_MULTICAST | IFF_BROADCAST))) { + log_debug("interfaces", + "skip %s: not able to do multicast nor broadcast", + iface->name); + continue; + } + + /* Check if the driver is not denied */ + if (iface->driver) { + int skip = 0; + for (rif = denied_drivers; *rif; rif++) { + if (strcmp(iface->driver, *rif) == 0) { + log_debug("interfaces", + "skip %s: denied driver", iface->name); + skip = 1; + break; + } + } + if (skip) continue; + } + + /* If the interface is linked to another one, skip it too. */ + if (iface->lower && + (!iface->driver || + (strcmp(iface->driver, "veth") && + strcmp(iface->driver, "dsa")))) { + log_debug("interfaces", + "skip %s: there is a lower interface (%s)", iface->name, + iface->lower->name); + continue; + } + + /* Get the real MAC address (for example, if the interface is enslaved) + */ + iflinux_get_permanent_mac(cfg, interfaces, iface); + + log_debug("interfaces", "%s is a physical interface", iface->name); + iface->type |= IFACE_PHYSICAL_T; + } +} + +void +interfaces_update(struct lldpd *cfg) +{ + struct lldpd_hardware *hardware; + struct interfaces_device_list *interfaces; + struct interfaces_address_list *addresses; + interfaces = netlink_get_interfaces(cfg); + addresses = netlink_get_addresses(cfg); + if (interfaces == NULL || addresses == NULL) { + log_warnx("interfaces", "cannot update the list of local interfaces"); + return; + } + + /* Add missing bits to list of interfaces */ + iflinux_add_driver(cfg, interfaces); + if (LOCAL_CHASSIS(cfg)->c_cap_available & LLDP_CAP_WLAN) + iflinux_add_wireless(cfg, interfaces); + if (LOCAL_CHASSIS(cfg)->c_cap_available & LLDP_CAP_BRIDGE) + iflinux_add_bridge(cfg, interfaces); + iflinux_add_bond(cfg, interfaces); + iflinux_add_vlan(cfg, interfaces); + iflinux_add_physical(cfg, interfaces); + + interfaces_helper_allowlist(cfg, interfaces); +#ifdef ENABLE_OLDIES + iflinux_handle_bond(cfg, interfaces); +#endif + interfaces_helper_physical(cfg, interfaces, ð_ops, iflinux_eth_init); +#ifdef ENABLE_DOT1 + interfaces_helper_vlan(cfg, interfaces); +#endif + interfaces_helper_mgmt(cfg, addresses, interfaces); + interfaces_helper_chassis(cfg, interfaces); + + /* Mac/PHY */ + TAILQ_FOREACH (hardware, &cfg->g_hardware, h_entries) { + if (!hardware->h_flags) continue; + iflinux_macphy(cfg, hardware); + interfaces_helper_promisc(cfg, hardware); + } +} + +void +interfaces_cleanup(struct lldpd *cfg) +{ + netlink_cleanup(cfg); +} diff --git a/src/daemon/interfaces-solaris.c b/src/daemon/interfaces-solaris.c new file mode 100644 index 0000000..5e3ae3c --- /dev/null +++ b/src/daemon/interfaces-solaris.c @@ -0,0 +1,174 @@ +/* -*- 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 "lldpd.h" + +#include <unistd.h> +#include <sys/sockio.h> +#include <net/if_types.h> + +/* Solaris comes with libdladm which seems to be handy to get all the necessary + * information. Unfortunately, this library needs a special device file and a + * Unix socket to a daemon. This is a bit difficult to use it in a + * privilege-separated daemon. Therefore, we keep using ioctl(). This should + * also improve compatibility with older versions of Solaris. + */ + +static void +ifsolaris_extract(struct lldpd *cfg, struct interfaces_device_list *interfaces, + struct interfaces_address_list *addresses, struct lifreq *lifr) +{ + int flags = 0; + int index = 0; + struct interfaces_address *address = NULL; + struct interfaces_device *device = NULL; + + sa_family_t lifr_af = lifr->lifr_addr.ss_family; + struct lifreq lifrl = { .lifr_name = {} }; + strlcpy(lifrl.lifr_name, lifr->lifr_name, sizeof(lifrl.lifr_name)); + + /* Flags */ + if (ioctl(cfg->g_sock, SIOCGLIFFLAGS, (caddr_t)&lifrl) < 0) { + log_warn("interfaces", "unable to get flags for %s", lifrl.lifr_name); + return; + } + flags = lifrl.lifr_flags; + + /* Index */ + if (ioctl(cfg->g_sock, SIOCGLIFINDEX, (caddr_t)&lifrl) < 0) { + log_warn("interfaces", "unable to get index for %s", lifrl.lifr_name); + return; + } + index = lifrl.lifr_index; + + /* Record the address */ + if ((address = malloc(sizeof(struct interfaces_address))) == NULL) { + log_warn("interfaces", "not enough memory for a new IP address on %s", + lifrl.lifr_name); + return; + } + address->flags = flags; + address->index = index; + memcpy(&address->address, &lifr->lifr_addr, + (lifr_af == AF_INET) ? sizeof(struct sockaddr_in) : + sizeof(struct sockaddr_in6)); + TAILQ_INSERT_TAIL(addresses, address, next); + + /* Hardware address */ + if (ioctl(cfg->g_sock, SIOCGLIFHWADDR, (caddr_t)&lifrl) < 0) { + log_debug("interfaces", "unable to get hardware address for %s", + lifrl.lifr_name); + return; + } + struct sockaddr_dl *saddrdl = (struct sockaddr_dl *)&lifrl.lifr_addr; + if (saddrdl->sdl_type != 4) { + log_debug("interfaces", "skip %s: not an ethernet device (%d)", + lifrl.lifr_name, saddrdl->sdl_type); + return; + } + + /* Handle the interface */ + if ((device = calloc(1, sizeof(struct interfaces_device))) == NULL) { + log_warn("interfaces", "unable to allocate memory for %s", + lifrl.lifr_name); + return; + } + + device->name = strdup(lifrl.lifr_name); + device->flags = flags; + device->index = index; + device->type = IFACE_PHYSICAL_T; + device->address = malloc(ETHER_ADDR_LEN); + if (device->address) memcpy(device->address, LLADDR(saddrdl), ETHER_ADDR_LEN); + + /* MTU */ + if (ioctl(cfg->g_sock, SIOCGLIFMTU, (caddr_t)&lifrl) < 0) { + log_debug("interfaces", "unable to get MTU for %s", lifrl.lifr_name); + } else + device->mtu = lifrl.lifr_mtu; + + TAILQ_INSERT_TAIL(interfaces, device, next); +} + +extern struct lldpd_ops bpf_ops; +void +interfaces_update(struct lldpd *cfg) +{ + struct lldpd_hardware *hardware; + caddr_t buffer = NULL; + struct interfaces_device_list *interfaces; + struct interfaces_address_list *addresses; + interfaces = malloc(sizeof(struct interfaces_device_list)); + addresses = malloc(sizeof(struct interfaces_address_list)); + if (interfaces == NULL || addresses == NULL) { + log_warnx("interfaces", "unable to allocate memory"); + goto end; + } + TAILQ_INIT(interfaces); + TAILQ_INIT(addresses); + + struct lifnum lifn = { .lifn_family = AF_UNSPEC, .lifn_flags = LIFC_ENABLED }; + if (ioctl(cfg->g_sock, SIOCGLIFNUM, &lifn) < 0) { + log_warn("interfaces", "unable to get the number of interfaces"); + goto end; + } + + size_t bufsize = lifn.lifn_count * sizeof(struct lifreq); + if ((buffer = malloc(bufsize)) == NULL) { + log_warn("interfaces", "unable to allocate buffer to get interfaces"); + goto end; + } + + struct lifconf lifc = { .lifc_family = AF_UNSPEC, + .lifc_flags = LIFC_ENABLED, + .lifc_len = bufsize, + .lifc_buf = buffer }; + if (ioctl(cfg->g_sock, SIOCGLIFCONF, (char *)&lifc) < 0) { + log_warn("interfaces", "unable to get the network interfaces"); + goto end; + } + + int num = lifc.lifc_len / sizeof(struct lifreq); + if (num > lifn.lifn_count) num = lifn.lifn_count; + log_debug("interfaces", "got %d interfaces", num); + + struct lifreq *lifrp = (struct lifreq *)buffer; + for (int n = 0; n < num; n++, lifrp++) + ifsolaris_extract(cfg, interfaces, addresses, lifrp); + + interfaces_helper_allowlist(cfg, interfaces); + interfaces_helper_physical(cfg, interfaces, &bpf_ops, ifbpf_phys_init); + interfaces_helper_mgmt(cfg, addresses, interfaces); + interfaces_helper_chassis(cfg, interfaces); + + /* Mac/PHY */ + TAILQ_FOREACH (hardware, &cfg->g_hardware, h_entries) { + if (!hardware->h_flags) continue; + /* TODO: mac/phy for Solaris */ + interfaces_helper_promisc(cfg, hardware); + } + +end: + free(buffer); + interfaces_free_devices(interfaces); + interfaces_free_addresses(addresses); +} + +void +interfaces_cleanup(struct lldpd *cfg) +{ +} diff --git a/src/daemon/interfaces.c b/src/daemon/interfaces.c new file mode 100644 index 0000000..c1179d9 --- /dev/null +++ b/src/daemon/interfaces.c @@ -0,0 +1,764 @@ +/* -*- 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 "lldpd.h" +#include "trace.h" + +#include <stddef.h> +#include <unistd.h> +#include <errno.h> +#include <assert.h> +#include <arpa/inet.h> + +static int +lldpd_af(int af) +{ + switch (af) { + case LLDPD_AF_IPV4: + return AF_INET; + case LLDPD_AF_IPV6: + return AF_INET6; + case LLDPD_AF_LAST: + return AF_MAX; + default: + return AF_UNSPEC; + } +} + +/* Generic ethernet interface initialization */ +/** + * Enable multicast on the given interface. + */ +void +interfaces_setup_multicast(struct lldpd *cfg, const char *name, int remove) +{ + int rc; + size_t i, j; + const u_int8_t *mac; + const u_int8_t zero[ETHER_ADDR_LEN] = {}; + + for (i = 0; cfg->g_protocols[i].mode != 0; i++) { + if (!cfg->g_protocols[i].enabled) continue; + for (j = 0; j < sizeof(cfg->g_protocols[0].mac) / + sizeof(cfg->g_protocols[0].mac[0]); + j++) { + mac = cfg->g_protocols[i].mac[j]; + if (memcmp(mac, zero, ETHER_ADDR_LEN) == 0) break; + if ((rc = priv_iface_multicast(name, mac, !remove)) != 0) { + errno = rc; + if (errno != ENOENT) + log_debug("interfaces", + "unable to %s %s address to multicast filter for %s (%s)", + (remove) ? "delete" : "add", + cfg->g_protocols[i].name, name, + strerror(rc)); + } + } + } +} + +/** + * Free an interface. + * + * @param iff interface to be freed + */ +void +interfaces_free_device(struct interfaces_device *iff) +{ + if (!iff) return; + free(iff->name); + free(iff->alias); + free(iff->address); + free(iff->driver); + free(iff); +} + +/** + * Free a list of interfaces. + * + * @param ifs list of interfaces to be freed + */ +void +interfaces_free_devices(struct interfaces_device_list *ifs) +{ + struct interfaces_device *iff, *iff_next; + if (!ifs) return; + for (iff = TAILQ_FIRST(ifs); iff != NULL; iff = iff_next) { + iff_next = TAILQ_NEXT(iff, next); + interfaces_free_device(iff); + } + free(ifs); +} + +/** + * Free one address + * + * @param ifaddr Address to be freed + */ +void +interfaces_free_address(struct interfaces_address *ifaddr) +{ + free(ifaddr); +} + +/** + * Free a list of addresses. + * + * @param ifaddrs list of addresses + */ +void +interfaces_free_addresses(struct interfaces_address_list *ifaddrs) +{ + struct interfaces_address *ifa, *ifa_next; + if (!ifaddrs) return; + for (ifa = TAILQ_FIRST(ifaddrs); ifa != NULL; ifa = ifa_next) { + ifa_next = TAILQ_NEXT(ifa, next); + interfaces_free_address(ifa); + } + free(ifaddrs); +} + +/** + * Find the appropriate interface from the name. + * + * @param interfaces List of available interfaces + * @param device Name of the device we search for + * @return The interface or NULL if not found + */ +struct interfaces_device * +interfaces_nametointerface(struct interfaces_device_list *interfaces, + const char *device) +{ + struct interfaces_device *iface; + TAILQ_FOREACH (iface, interfaces, next) { + if (!strncmp(iface->name, device, IFNAMSIZ)) return iface; + } + log_debug("interfaces", "cannot get interface for index %s", device); + return NULL; +} + +/** + * Find the appropriate interface from the index. + * + * @param interfaces List of available interfaces + * @param index Index of the device we search for + * @return The interface or NULL if not found + */ +struct interfaces_device * +interfaces_indextointerface(struct interfaces_device_list *interfaces, int index) +{ + struct interfaces_device *iface; + TAILQ_FOREACH (iface, interfaces, next) { + if (iface->index == index) return iface; + } + log_debug("interfaces", "cannot get interface for index %d", index); + return NULL; +} + +void +interfaces_helper_allowlist(struct lldpd *cfg, + struct interfaces_device_list *interfaces) +{ + struct interfaces_device *iface; + + if (!cfg->g_config.c_iface_pattern) return; + + TAILQ_FOREACH (iface, interfaces, next) { + int m = pattern_match(iface->name, cfg->g_config.c_iface_pattern, 0); + switch (m) { + case PATTERN_MATCH_DENIED: + log_debug("interfaces", "deny %s", iface->name); + iface->ignore = 1; + continue; + case PATTERN_MATCH_ALLOWED_EXACT: + log_debug("interfaces", + "allow %s (consider it as a physical interface)", + iface->name); + iface->type |= IFACE_PHYSICAL_T; + continue; + } + } +} + +#ifdef ENABLE_DOT1 +static void +iface_append_vlan(struct lldpd *cfg, struct interfaces_device *vlan, + struct interfaces_device *lower) +{ + struct lldpd_hardware *hardware = + lldpd_get_hardware(cfg, lower->name, lower->index); + struct lldpd_port *port; + struct lldpd_vlan *v; + char *name = NULL; + uint16_t vlan_id; + + if (hardware == NULL) { + log_debug("interfaces", "cannot find real interface %s for VLAN %s", + lower->name, vlan->name); + return; + } + port = &hardware->h_lport; + + for (int i = 0; (i < VLAN_BITMAP_LEN); i++) { + if (vlan->vlan_bmap[i] == 0) continue; + for (unsigned bit = 0; bit < 32; bit++) { + uint32_t mask = 1L << bit; + if (!(vlan->vlan_bmap[i] & mask)) continue; + vlan_id = (i * 32) + bit; + if (asprintf(&name, "vlan%d", vlan_id) == -1) return; + + /* Check if the VLAN is already here. */ + TAILQ_FOREACH (v, &port->p_vlans, v_entries) + if (strncmp(name, v->v_name, IFNAMSIZ) == 0) { + free(name); + return; + } + + if ((v = (struct lldpd_vlan *)calloc(1, + sizeof(struct lldpd_vlan))) == NULL) { + free(name); + return; + } + v->v_name = name; + v->v_vid = vlan_id; + if (vlan->pvid) port->p_pvid = vlan->pvid; + log_debug("interfaces", "append VLAN %s for %s", v->v_name, + hardware->h_ifname); + TAILQ_INSERT_TAIL(&port->p_vlans, v, v_entries); + } + } +} + +/** + * Append VLAN to the lowest possible interface. + * + * @param vlan The VLAN interface (used to get VLAN ID). + * @param upper The upper interface we are currently examining. + * @param depth Depth of the stack (avoid infinite recursion) + * + * Initially, upper == vlan. This function will be called recursively. + */ +static void +iface_append_vlan_to_lower(struct lldpd *cfg, struct interfaces_device_list *interfaces, + struct interfaces_device *vlan, struct interfaces_device *upper, int depth) +{ + if (depth > 5) { + log_warnx("interfaces", + "BUG: maximum depth reached when applying VLAN %s (loop?)", + vlan->name); + return; + } + depth++; + struct interfaces_device *lower; + log_debug("interfaces", + "looking to apply VLAN %s to physical interface behind %s", vlan->name, + upper->name); + + /* Some bridges managed VLAN internally, skip them. */ + if (upper->type & IFACE_BRIDGE_VLAN_T) { + log_debug("interfaces", + "VLAN %s ignored for VLAN-aware bridge interface %s", vlan->name, + upper->name); + return; + } + + /* Easy: check if we have a lower interface. */ + if (upper->lower) { + log_debug("interfaces", "VLAN %s on lower interface %s", vlan->name, + upper->name); + iface_append_vlan_to_lower(cfg, interfaces, vlan, upper->lower, depth); + return; + } + + /* Other easy case, we have a physical interface. */ + if (upper->type & IFACE_PHYSICAL_T) { + log_debug("interfaces", "VLAN %s on physical interface %s", vlan->name, + upper->name); + iface_append_vlan(cfg, vlan, upper); + return; + } + + /* We can now search for interfaces that have our interface as an upper + * interface. */ + TAILQ_FOREACH (lower, interfaces, next) { + if (lower->upper != upper) continue; + log_debug("interfaces", "VLAN %s on lower interface %s", vlan->name, + upper->name); + iface_append_vlan_to_lower(cfg, interfaces, vlan, lower, depth); + } +} + +void +interfaces_helper_vlan(struct lldpd *cfg, struct interfaces_device_list *interfaces) +{ + struct interfaces_device *iface; + + TAILQ_FOREACH (iface, interfaces, next) { + if (!(iface->type & IFACE_VLAN_T) && bitmap_isempty(iface->vlan_bmap)) + continue; + + /* We need to find the physical interfaces of this + vlan, through bonds and bridges. */ + log_debug("interfaces", + "search physical interface for VLAN interface %s", iface->name); + iface_append_vlan_to_lower(cfg, interfaces, iface, iface, 0); + } +} +#endif + +/* Fill out chassis ID if not already done. Only physical interfaces are + * considered. */ +void +interfaces_helper_chassis(struct lldpd *cfg, struct interfaces_device_list *interfaces) +{ + struct interfaces_device *iface; + struct lldpd_hardware *hardware; + char *name = NULL; + static u_int8_t zero_mac[] = { 0, 0, 0, 0, 0, 0 }; + + if (!cfg->g_config.c_cap_override) { + LOCAL_CHASSIS(cfg)->c_cap_enabled &= + ~(LLDP_CAP_BRIDGE | LLDP_CAP_WLAN | LLDP_CAP_STATION); + TAILQ_FOREACH (iface, interfaces, next) { + if (iface->type & IFACE_BRIDGE_T) + LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_BRIDGE; + if (iface->type & IFACE_WIRELESS_T) + LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_WLAN; + } + if ((LOCAL_CHASSIS(cfg)->c_cap_available & LLDP_CAP_STATION) && + (LOCAL_CHASSIS(cfg)->c_cap_enabled == 0)) + LOCAL_CHASSIS(cfg)->c_cap_enabled = LLDP_CAP_STATION; + } + + /* Do not modify the chassis if it's already set to a MAC address or if + * it's set to a local address equal to the user-provided + * configuration. */ + if ((LOCAL_CHASSIS(cfg)->c_id != NULL && + LOCAL_CHASSIS(cfg)->c_id_subtype == LLDP_CHASSISID_SUBTYPE_LLADDR) || + cfg->g_config.c_cid_string != NULL) + return; /* We already have one */ + + TAILQ_FOREACH (iface, interfaces, next) { + if (!(iface->type & IFACE_PHYSICAL_T)) continue; + if (cfg->g_config.c_cid_pattern && + !pattern_match(iface->name, cfg->g_config.c_cid_pattern, 0)) + continue; + + if ((hardware = lldpd_get_hardware(cfg, iface->name, iface->index)) == + NULL) + /* That's odd. Let's skip. */ + continue; + if (memcmp(hardware->h_lladdr, zero_mac, ETHER_ADDR_LEN) == 0) + /* All-zero MAC address */ + continue; + + name = malloc(ETHER_ADDR_LEN); + if (!name) { + log_warn("interfaces", "not enough memory for chassis ID"); + return; + } + free(LOCAL_CHASSIS(cfg)->c_id); + memcpy(name, hardware->h_lladdr, ETHER_ADDR_LEN); + LOCAL_CHASSIS(cfg)->c_id = name; + LOCAL_CHASSIS(cfg)->c_id_len = ETHER_ADDR_LEN; + LOCAL_CHASSIS(cfg)->c_id_subtype = LLDP_CHASSISID_SUBTYPE_LLADDR; + return; + } +} + +#undef IN_IS_ADDR_LOOPBACK +#define IN_IS_ADDR_LOOPBACK(a) ((a)->s_addr == htonl(INADDR_LOOPBACK)) +#undef IN_IS_ADDR_ANY +#define IN_IS_ADDR_ANY(a) ((a)->s_addr == htonl(INADDR_ANY)) +#undef IN_IS_ADDR_LINKLOCAL +#define IN_IS_ADDR_LINKLOCAL(a) (((a)->s_addr & htonl(0xffff0000)) == htonl(0xa9fe0000)) +#undef IN_IS_ADDR_GLOBAL +#define IN_IS_ADDR_GLOBAL(a) \ + (!IN_IS_ADDR_LOOPBACK(a) && !IN_IS_ADDR_ANY(a) && !IN_IS_ADDR_LINKLOCAL(a)) +#undef IN6_IS_ADDR_GLOBAL +#define IN6_IS_ADDR_GLOBAL(a) (!IN6_IS_ADDR_LOOPBACK(a) && !IN6_IS_ADDR_LINKLOCAL(a)) + +/* Add management addresses for the given family. We only take one of each + address family, unless a pattern is provided and is not all negative. For + example !*:*,!10.* will only deny addresses. We will pick the first IPv4 + address not matching 10.*. +*/ +static int +interfaces_helper_mgmt_for_af(struct lldpd *cfg, int af, + struct interfaces_address_list *addrs, struct interfaces_device_list *interfaces, + int global, int allnegative) +{ + struct interfaces_address *addr; + struct interfaces_device *device; + struct lldpd_mgmt *mgmt; + char addrstrbuf[INET6_ADDRSTRLEN]; + int found = 0; + union lldpd_address in_addr; + size_t in_addr_size; + + TAILQ_FOREACH (addr, addrs, next) { + if (addr->address.ss_family != lldpd_af(af)) continue; + + switch (af) { + case LLDPD_AF_IPV4: + in_addr_size = sizeof(struct in_addr); + memcpy(&in_addr, + &((struct sockaddr_in *)&addr->address)->sin_addr, + in_addr_size); + if (global) { + if (!IN_IS_ADDR_GLOBAL(&in_addr.inet)) continue; + } else { + if (!IN_IS_ADDR_LINKLOCAL(&in_addr.inet)) continue; + } + break; + case LLDPD_AF_IPV6: + in_addr_size = sizeof(struct in6_addr); + memcpy(&in_addr, + &((struct sockaddr_in6 *)&addr->address)->sin6_addr, + in_addr_size); + if (global) { + if (!IN6_IS_ADDR_GLOBAL(&in_addr.inet6)) continue; + } else { + if (!IN6_IS_ADDR_LINKLOCAL(&in_addr.inet6)) continue; + } + break; + default: + assert(0); + continue; + } + if (inet_ntop(lldpd_af(af), &in_addr, addrstrbuf, sizeof(addrstrbuf)) == + NULL) { + log_warn("interfaces", + "unable to convert IP address to a string"); + continue; + } + if (cfg->g_config.c_mgmt_pattern == NULL || + /* Match on IP address */ + pattern_match(addrstrbuf, cfg->g_config.c_mgmt_pattern, + allnegative) || + /* Match on interface name */ + ((device = interfaces_indextointerface(interfaces, addr->index)) && + pattern_match(device->name, cfg->g_config.c_mgmt_pattern, + allnegative))) { + mgmt = + lldpd_alloc_mgmt(af, &in_addr, in_addr_size, addr->index); + if (mgmt == NULL) { + assert(errno == ENOMEM); /* anything else is a bug */ + log_warn("interfaces", "out of memory error"); + return found; + } + log_debug("interfaces", "add management address %s", + addrstrbuf); + TAILQ_INSERT_TAIL(&LOCAL_CHASSIS(cfg)->c_mgmt, mgmt, m_entries); + found = 1; + + /* Don't take additional address if the pattern is all negative. + */ + if (allnegative) break; + } + } + return found; +} + +/* Find a management address in all available interfaces, even those that were + already handled. This is a special interface handler because it does not + really handle interface related information (management address is attached + to the local chassis). */ +void +interfaces_helper_mgmt(struct lldpd *cfg, struct interfaces_address_list *addrs, + struct interfaces_device_list *interfaces) +{ + int allnegative = 0; + int af; + const char *pattern = cfg->g_config.c_mgmt_pattern; + + lldpd_chassis_mgmt_cleanup(LOCAL_CHASSIS(cfg)); + if (!cfg->g_config.c_mgmt_advertise) return; + + /* Is the pattern provided an actual IP address? */ + if (pattern && strpbrk(pattern, "!,*?") == NULL) { + unsigned char addr[sizeof(struct in6_addr)]; + size_t addr_size; + struct lldpd_mgmt *mgmt; + struct interfaces_address *ifaddr; + + for (af = LLDPD_AF_UNSPEC + 1; af != LLDPD_AF_LAST; af++) { + switch (af) { + case LLDPD_AF_IPV4: + addr_size = sizeof(struct in_addr); + break; + case LLDPD_AF_IPV6: + addr_size = sizeof(struct in6_addr); + break; + default: + assert(0); + } + if (inet_pton(lldpd_af(af), pattern, addr) == 1) break; + } + if (af != LLDPD_AF_LAST) { + /* Try to get the index if possible. */ + TAILQ_FOREACH (ifaddr, addrs, next) { + if (ifaddr->address.ss_family != lldpd_af(af)) continue; + if (LLDPD_AF_IPV4 == af) { + struct sockaddr_in *sa_sin; + sa_sin = (struct sockaddr_in *)&ifaddr->address; + if (0 == + memcmp(addr, &(sa_sin->sin_addr), + addr_size)) + break; + } else if (LLDPD_AF_IPV6 == af) { + if (0 == + memcmp(addr, + &((struct sockaddr_in6 *)&ifaddr + ->address) + ->sin6_addr, + addr_size)) + break; + } + } + + mgmt = lldpd_alloc_mgmt(af, addr, addr_size, + ifaddr ? ifaddr->index : 0); + if (mgmt == NULL) { + log_warn("interfaces", "out of memory error"); + return; + } + log_debug("interfaces", "add exact management address %s", + pattern); + TAILQ_INSERT_TAIL(&LOCAL_CHASSIS(cfg)->c_mgmt, mgmt, m_entries); + return; + } + /* else: could be an interface name */ + } + + /* Is the pattern provided all negative? */ + if (pattern == NULL) + allnegative = 1; + else if (pattern[0] == '!') { + /* If each comma is followed by '!', its an all + negative pattern */ + const char *sep = pattern; + while ((sep = strchr(sep, ',')) && (*(++sep) == '!')) + ; + if (sep == NULL) allnegative = 1; + } + + /* Find management addresses */ + for (af = LLDPD_AF_UNSPEC + 1; af != LLDPD_AF_LAST; af++) { + (void)(interfaces_helper_mgmt_for_af(cfg, af, addrs, interfaces, 1, + allnegative) || + interfaces_helper_mgmt_for_af(cfg, af, addrs, interfaces, 0, + allnegative)); + } +} + +/* Fill up port name and description */ +void +interfaces_helper_port_name_desc(struct lldpd *cfg, struct lldpd_hardware *hardware, + struct interfaces_device *iface) +{ + struct lldpd_port *port = &hardware->h_lport; + + /* We need to set the portid to what the client configured. + This can be done from the CLI. + */ + int has_alias = (iface->alias != NULL && strlen(iface->alias) != 0 && + strncmp("lldpd: ", iface->alias, 7)); + int portid_type = cfg->g_config.c_lldp_portid_type; + if (portid_type == LLDP_PORTID_SUBTYPE_IFNAME || + (portid_type == LLDP_PORTID_SUBTYPE_UNKNOWN && has_alias) || + (port->p_id_subtype == LLDP_PORTID_SUBTYPE_LOCAL && has_alias)) { + if (port->p_id_subtype != LLDP_PORTID_SUBTYPE_LOCAL) { + log_debug("interfaces", "use ifname for %s", + hardware->h_ifname); + port->p_id_subtype = LLDP_PORTID_SUBTYPE_IFNAME; + port->p_id_len = strlen(hardware->h_ifname); + free(port->p_id); + if ((port->p_id = calloc(1, port->p_id_len)) == NULL) + fatal("interfaces", NULL); + memcpy(port->p_id, hardware->h_ifname, port->p_id_len); + } + + if (port->p_descr_force == 0) { + /* use the actual alias in the port description */ + log_debug("interfaces", "using alias in description for %s", + hardware->h_ifname); + free(port->p_descr); + if (has_alias) { + port->p_descr = strdup(iface->alias); + } else { + /* We don't have anything else to put here and for CDP + * with need something non-NULL */ + port->p_descr = strdup(hardware->h_ifname); + } + } + } else { + if (port->p_id_subtype != LLDP_PORTID_SUBTYPE_LOCAL) { + log_debug("interfaces", "use MAC address for %s", + hardware->h_ifname); + port->p_id_subtype = LLDP_PORTID_SUBTYPE_LLADDR; + free(port->p_id); + if ((port->p_id = calloc(1, ETHER_ADDR_LEN)) == NULL) + fatal("interfaces", NULL); + memcpy(port->p_id, hardware->h_lladdr, ETHER_ADDR_LEN); + port->p_id_len = ETHER_ADDR_LEN; + } + + if (port->p_descr_force == 0) { + /* use the ifname in the port description until alias is set */ + log_debug("interfaces", "using ifname in description for %s", + hardware->h_ifname); + free(port->p_descr); + port->p_descr = strdup(hardware->h_ifname); + } + } +} + +void +interfaces_helper_add_hardware(struct lldpd *cfg, struct lldpd_hardware *hardware) +{ + TRACE(LLDPD_INTERFACES_NEW(hardware->h_ifname)); + TAILQ_INSERT_TAIL(&cfg->g_hardware, hardware, h_entries); +} + +void +interfaces_helper_physical(struct lldpd *cfg, struct interfaces_device_list *interfaces, + struct lldpd_ops *ops, int (*init)(struct lldpd *, struct lldpd_hardware *)) +{ + struct interfaces_device *iface; + struct lldpd_hardware *hardware; + int created; + + TAILQ_FOREACH (iface, interfaces, next) { + if (!(iface->type & IFACE_PHYSICAL_T)) continue; + if (iface->ignore) continue; + + log_debug("interfaces", "%s is an acceptable ethernet device", + iface->name); + created = 0; + if ((hardware = lldpd_get_hardware(cfg, iface->name, iface->index)) == + NULL) { + if ((hardware = lldpd_alloc_hardware(cfg, iface->name, + iface->index)) == NULL) { + log_warnx("interfaces", + "Unable to allocate space for %s", iface->name); + continue; + } + created = 1; + } + if (hardware->h_flags) continue; + if (hardware->h_ops != ops || hardware->h_ifindex_changed) { + if (!created) { + log_debug("interfaces", + "interface %s is converted from another type of interface", + hardware->h_ifname); + if (hardware->h_ops && hardware->h_ops->cleanup) { + hardware->h_ops->cleanup(cfg, hardware); + levent_hardware_release(hardware); + levent_hardware_init(hardware); + } + } + if (init(cfg, hardware) != 0) { + log_warnx("interfaces", "unable to initialize %s", + hardware->h_ifname); + lldpd_hardware_cleanup(cfg, hardware); + continue; + } + hardware->h_ops = ops; + hardware->h_mangle = + (iface->upper && iface->upper->type & IFACE_BOND_T); + } + if (created) + interfaces_helper_add_hardware(cfg, hardware); + else + lldpd_port_cleanup(&hardware->h_lport, 0); + + hardware->h_flags = iface->flags; /* Should be non-zero */ + iface->ignore = 1; /* Future handlers + don't have to + care about this + interface. */ + + /* Get local address */ + memcpy(&hardware->h_lladdr, iface->address, ETHER_ADDR_LEN); + + /* Fill information about port */ + interfaces_helper_port_name_desc(cfg, hardware, iface); + + /* Fill additional info */ + hardware->h_mtu = iface->mtu ? iface->mtu : 1500; + +#ifdef ENABLE_DOT3 + if (iface->upper && iface->upper->type & IFACE_BOND_T) + hardware->h_lport.p_aggregid = iface->upper->index; + else + hardware->h_lport.p_aggregid = 0; +#endif + } +} + +void +interfaces_helper_promisc(struct lldpd *cfg, struct lldpd_hardware *hardware) +{ + if (!cfg->g_config.c_promisc) return; + if (priv_iface_promisc(hardware->h_ifname) != 0) { + log_warnx("interfaces", "unable to enable promiscuous mode for %s", + hardware->h_ifname); + } +} + +/** + * Send the packet using the hardware function. Optionnaly mangle the MAC address. + * + * With bonds, we have duplicate MAC address on different physical + * interfaces. We need to alter the source MAC address when we send on an + * inactive slave. The `h_mangle` flah is used to know if we need to do + * something like that. + */ +int +interfaces_send_helper(struct lldpd *cfg, struct lldpd_hardware *hardware, char *buffer, + size_t size) +{ + if (size < 2 * ETHER_ADDR_LEN) { + log_warnx("interfaces", "packet to send on %s is too small!", + hardware->h_ifname); + return 0; + } + if (hardware->h_mangle) { +#define MAC_UL_ADMINISTERED_BIT_MASK 0x02 + char *src_mac = buffer + ETHER_ADDR_LEN; + char arbitrary[] = { 0x00, 0x60, 0x08, 0x69, 0x97, 0xef }; + + switch (cfg->g_config.c_bond_slave_src_mac_type) { + case LLDP_BOND_SLAVE_SRC_MAC_TYPE_LOCALLY_ADMINISTERED: + if (!(*src_mac & MAC_UL_ADMINISTERED_BIT_MASK)) { + *src_mac |= MAC_UL_ADMINISTERED_BIT_MASK; + break; + } + /* Fallback to fixed value */ + memcpy(src_mac, arbitrary, ETHER_ADDR_LEN); + break; + case LLDP_BOND_SLAVE_SRC_MAC_TYPE_FIXED: + memcpy(src_mac, arbitrary, ETHER_ADDR_LEN); + break; + case LLDP_BOND_SLAVE_SRC_MAC_TYPE_ZERO: + memset(src_mac, 0, ETHER_ADDR_LEN); + break; + } + } + return hardware->h_ops->send(cfg, hardware, buffer, size); +} diff --git a/src/daemon/lldp-tlv.h b/src/daemon/lldp-tlv.h new file mode 100644 index 0000000..a9bf7cf --- /dev/null +++ b/src/daemon/lldp-tlv.h @@ -0,0 +1,83 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _LLDP_TLV_H +#define _LLDP_TLV_H + +#define LLDP_ADDR_NEAREST_BRIDGE \ + { \ + 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e \ + } +#define LLDP_ADDR_NEAREST_NONTPMR_BRIDGE \ + { \ + 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 \ + } +#define LLDP_ADDR_NEAREST_CUSTOMER_BRIDGE \ + { \ + 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 \ + } + +#define LLDP_TLV_END 0 +#define LLDP_TLV_CHASSIS_ID 1 +#define LLDP_TLV_PORT_ID 2 +#define LLDP_TLV_TTL 3 +#define LLDP_TLV_PORT_DESCR 4 +#define LLDP_TLV_SYSTEM_NAME 5 +#define LLDP_TLV_SYSTEM_DESCR 6 +#define LLDP_TLV_SYSTEM_CAP 7 +#define LLDP_TLV_MGMT_ADDR 8 + +#define LLDP_TLV_ORG_DOT1 \ + { \ + 0x00, 0x80, 0xc2 \ + } +#define LLDP_TLV_ORG_DOT3 \ + { \ + 0x00, 0x12, 0x0f \ + } +#define LLDP_TLV_ORG_MED \ + { \ + 0x00, 0x12, 0xbb \ + } +#define LLDP_TLV_ORG_DCBX \ + { \ + 0x00, 0x1b, 0x21 \ + } + +#define LLDP_TLV_DOT1_PVID 1 +#define LLDP_TLV_DOT1_PPVID 2 +#define LLDP_TLV_DOT1_VLANNAME 3 +#define LLDP_TLV_DOT1_PI 4 + +#define LLDP_TLV_DOT3_MAC 1 +#define LLDP_TLV_DOT3_POWER 2 +#define LLDP_TLV_DOT3_LA 3 +#define LLDP_TLV_DOT3_MFS 4 + +#define LLDP_TLV_MED_CAP 1 +#define LLDP_TLV_MED_POLICY 2 +#define LLDP_TLV_MED_LOCATION 3 +#define LLDP_TLV_MED_MDI 4 +#define LLDP_TLV_MED_IV_HW 5 +#define LLDP_TLV_MED_IV_FW 6 +#define LLDP_TLV_MED_IV_SW 7 +#define LLDP_TLV_MED_IV_SN 8 +#define LLDP_TLV_MED_IV_MANUF 9 +#define LLDP_TLV_MED_IV_MODEL 10 +#define LLDP_TLV_MED_IV_ASSET 11 + +#endif diff --git a/src/daemon/lldpd.8.in b/src/daemon/lldpd.8.in new file mode 100644 index 0000000..32d57a6 --- /dev/null +++ b/src/daemon/lldpd.8.in @@ -0,0 +1,424 @@ +.\" Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org> +.\" Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx> +.\" +.\" Permission to use, copy, modify, and/or distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.Dd $Mdocdate: August 21 2008 $ +.Dt LLDPD 8 +.Os +.Sh NAME +.Nm lldpd +.Nd LLDP daemon +.Sh SYNOPSIS +.Nm +.Op Fl dxcseiklrv +.Op Fl D Ar debug +.Op Fl p Ar pidfile +.Op Fl S Ar description +.Op Fl P Ar platform +.Op Fl X Ar socket +.Op Fl m Ar management +.Op Fl u Ar file +.Op Fl I Ar interfaces +.Op Fl C Ar interfaces +.Op Fl M Ar class +.Op Fl H Ar hide +.Op Fl L Ar lldpcli +.Op Fl O Ar configfile +.Sh DESCRIPTION +.Nm +is a daemon able to receive and send +.Em LLDP +frames. The Link Layer Discovery Protocol is a vendor-neutral Layer 2 +protocol that allows a network device to advertise its identity and +capabilities on the local network. +.Pp +.Nm +also implements an SNMP subagent using AgentX protocol to interface to +a regular SNMP agent like Net-SNMP. To enable this subagent, you need +something like that in your +.Xr snmpd.conf 5 : +.Bd -literal -offset indent +master agentx +.Ed +.Pp +This daemon implements both reception and sending. It will collect +various information to send LLDP frames to all Ethernet interfaces, +including management address, speed and VLAN names. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl d +Do not daemonize. +If this option is specified, +.Nm +will run in the foreground. When specified one more time, +.Nm +will not log to syslog but only to stderr. Then, this option can be +specified many times to increase verbosity. When specified four times, +debug logs will be enabled. They can be filtered with +.Fl D +flag. +.It Fl D Ar debug +This option allows the user to filter out debugging information by +specifying allowed tokens. This option can be repeated several times +to allow several tokens. This option must be combined with the +.Fl d +flag to have some effect. Only debugging logs can be filtered. Here is +a list of allowed tokens with their description: +.Bl -tag -width "XXXXXXXXXX" -offset "XXXX" -compact +.It Sy main +Main daemon. +.It Sy interfaces +Discovery of local interfaces. +.It Sy lldp +LLDP PDU encoding/decoding. +.It Sy edp +EDP PDU encoding/decoding. +.It Sy cdp +CDP/FDP PDU encoding/decoding. +.It Sy sonmp +SONMP PDU encoding/decoding. +.It Sy event +Events management. +.It Sy libevent +Events management but for logs generated by libevent. +.It Sy privsep +Privilege separation. +.It Sy localchassis +Retrieval of information related to the local chassis. +.It Sy rpc +Client communication. +.It Sy control +Management of the Unix control socket. +.It Sy snmp +SNMP subagent. +.It Sy libsnmp +SNMP subagent but for logs generated by NetSNMP. +.It Sy decode +Generic PDU decoding. +.It Sy marshal +Low-level serialization mechanisms. +.It Sy alloc +Low-level allocation mechanisms. +.It Sy send +Sending PDU to some interface. +.It Sy receive +Receiving PDU from some interface. +.It Sy loop +Main loop. +.It Sy smartfilter +Smart filtering of different protocols on the same port. +.It Sy netlink +Netlink subsystem. +.El +.It Fl p Ar pidfile +Use the provided PID file to record +.Nm +PID instead of @LLDPD_PID_FILE@. +.It Fl k +Disable advertising of kernel release, version and machine. Kernel name +(ie: Linux) will still be shared, and Inventory software version will be set +to 'Unknown'. +.It Fl S Ar description +Override system description with the provided description. The default +description is the kernel name, the node name, the kernel version, the +build date and the architecture (except if you use the +.Fl k +flag described above). +.It Fl P Ar platform +Override the CDP platform name with the provided value. The default +description is the kernel name (Linux). +.It Fl x +Enable SNMP subagent. +With this option, +.Nm +will enable an SNMP subagent using AgentX protocol. This allows you to +get information about local system and remote systems through SNMP. +.It Fl X Ar socket +Enable SNMP subagent using the specified socket. +.Nm +will enable an SNMP subagent using AgentX protocol for the given +socket. This option implies the previous one. The default socket is +usually +.Em /var/agentx/master . +You can specify a socket like +.Em tcp:127.0.0.1:705 +for example. Since the process that will open this socket is enclosed +in a chroot, you need to specify an IP address (not a hostname) when +using a TCP or UDP socket. +.It Fl c +Enable the support of CDP protocol to deal with Cisco routers that do +not speak LLDP. If repeated, CDPv1 packets will be sent even when +there is no CDP peer detected. If repeated once again, CDPv2 packets +will be sent even when there is no CDP peer detected. If repeated once +again (i.e. +.Fl cccc ) , +CDPv1 will be disabled and CDPv2 will be enabled. If repeated once +again (i.e. +.Fl ccccc ) , +CDPv1 will be disabled and CDPv2 will be forced. +.It Fl f +Enable the support of FDP protocol to deal with Foundry routers that do +not speak LLDP. If repeated, FDP packets will be sent even when there +is no FDP peer detected. +.It Fl s +Enable the support of SONMP protocol to deal with Nortel routers and +switches that do not speak LLDP. If repeated, SONMP packets will be +sent even when there is no SONMP peer detected. +.It Fl e +Enable the support of EDP protocol to deal with Extreme routers and +switches that do not speak LLDP. If repeated, EDP packets will be sent +even when there is no EDP peer detected. +.It Fl l +Force to send LLDP packets even when there is no LLDP peer detected +but there is a peer speaking another protocol detected. By default, +LLDP packets are sent when there is a peer speaking LLDP detected or +when there is no peer at all. If repeated, LLDP is disabled. +.It Fl r +Receive-only mode. With this switch, +.Nm +will not send any frame. It will only listen to neighbors. +.It Fl m Ar management +Specify the management addresses of this system. As for interfaces +(described below), this option can use wildcards and inversions. +Without this option, the first IPv4 and the first IPv6 are used. If an +exact IP address is provided, it is used as a management address +without any check. If only negative patterns are provided, only one +IPv4 and one IPv6 addresses are chosen. Otherwise, many of them can be +selected. If you want to remove IPv6 addresses, you can use +.Em !*:* . +If an interface name is matched, the first IPv4 address and the first +IPv6 address associated to this interface will be chosen. +.It Fl u Ar file +Specify the Unix-domain socket used for communication with +.Xr lldpctl 8 . +.It Fl I Ar interfaces +Specify which interface to listen and send LLDPDU to. Without this +option, +.Nm +will use all available physical interfaces. This option can use +wildcards. Several interfaces can be specified separated by commas. +It is also possible to remove an interface by prefixing it with an +exclamation mark. It is possible to allow an interface by +prefixing it with two exclamation marks. An allowed interface beats +a forbidden interface which beats a simple matched interface. For +example, with +.Em eth*,!eth1,!eth2 +.Nm +will only use interfaces starting by +.Em eth +with the exception of +.Em eth1 +and +.Em eth2 . +While with +.Em *,!eth*,!!eth1 +.Nm +will use all interfaces, except interfaces starting by +.Em eth +with the exception of +.Em eth1 . +When an exact match is found, it will circumvent some tests. For example, if +.Em eth0.12 +is specified, it will be accepted even if this is a VLAN interface. +.It Fl C Ar interfaces +Specify which interfaces to use for computing chassis ID. Without this +option, all interfaces are considered. +.Nm +will take the first MAC address from all the considered interfaces +to compute the chassis ID. The logic of this option is the same as for +.Fl I +flag: you can exclude interfaces with an exclamation mark and use +globbing to specify several interfaces. If all interfaces are +removed (with +.Em !* ) , +the system name is used as a chassis ID instead. +.It Fl M Ar class +Enable emission of LLDP-MED frame. Depending on the selected class, +the standard defines which set of TLV should be transmitted. See +section 10.2.1. Some devices may be strict about this aspect. The +class should be one of the following value: +.Bl -tag -width "0:XX" -compact +.It Sy 1 +Generic Endpoint (Class I) +.It Sy 2 +Media Endpoint (Class II). In this case, the standard requires to +define at least one network policy through +.Nm lldpcli . +.It Sy 3 +Communication Device Endpoints (Class III). In this case, the standard +requires to define at least one network policy through +.Nm lldpcli . +.It Sy 4 +Network Connectivity Device +.El +.It Fl i +Disable LLDP-MED inventory TLV transmission. +.Nm +will still receive (and publish using SNMP if enabled) those LLDP-MED +TLV but will not send them. Use this option if you don't want to +transmit sensible information like serial numbers. +.It Fl H Ar hide +Filter neighbors. See section +.Sx FILTERING NEIGHBORS +for details. +.It Fl L Ar lldpcli +Provide an alternative path to +.Nm lldpcli +for configuration. If empty, does not use +.Nm lldpcli +for configuration. +.It Fl O Ar configfile +Override default configuration locations processed by +.Nm lldpcli +at start. If a directory is provided, each file contained in it will be read if ending by +.Sy .conf. +Order is alphabetical. +.It Fl v +Show +.Nm +version. When repeated, show more build information. +.El +.Sh FILTERING NEIGHBORS +In a heterogeneous network, you may see several different hosts on the +same port, even if there is only one physically plugged to this +port. For example, if you have a Nortel switch running LLDP which is +plugged to a Cisco switch running CDP and your host is plugged to the +Cisco switch, you will see the Nortel switch as well because LLDP +frames are forwarded by the Cisco switch. This may not be what you +want. The +.Fl H Ar hide +parameter will allow you to tell +.Nm +to discard some frames that it receives and to avoid to send some +other frames. +.Pp +Incoming filtering and outgoing filtering are +unrelated. Incoming filtering will hide some remote ports to get you a +chance to know exactly what equipment is on the other side of the +network cable. Outgoing filtering will avoid to use some protocols to +avoid flooding your network with a protocol that is not handled by the +nearest equipment. Keep in mind that even without filtering, +.Nm +will speak protocols for which at least one frame has been received +and LLDP otherwise (there are other options to change this behaviour, +for example +.Fl cc , ss , ee , ll +and +.Fl ff +). +.Pp +When enabling incoming filtering, +.Nm +will try to select one protocol and filter out neighbors using other +protocols. To select this protocol, the rule is to take the less used +protocol. If on one port, you get 12 CDP neighbors and 1 LLDP +neighbor, this mean that the remote switch speaks LLDP and does not +filter CDP. Therefore, we select LLDP. When enabling outgoing +filtering, +.Nm +will also try to select one protocol and only speaks this +protocol. The filtering is done per port. Each port may select a +different protocol. +.Pp +There are two additional criteria when enabling filtering: allowing +one or several protocols to be selected (in case of a tie) and +allowing one or several neighbors to be selected. Even when allowing +several protocols, the rule of selecting the protocols with the less +neighbors still apply. If +.Nm +selects LLDP and CDP, this means they have the same number of +neighbors. The selection of the neighbor is random. Incoming filtering +will select a set of neighbors to be displayed while outgoing +filtering will use the selected set of neighbors to decide which +protocols to use: if a selected neighbor speaks LLDP and another one +CDP, +.Nm +will speak both CDP and LLDP on this port. +.Pp +There are some corner cases. A typical example is a switch speaking +two protocols (CDP and LLDP for example). You want to get the +information from the best protocol but you want to speak both +protocols because some tools use the CDP table and some other the LLDP +table. +.Pp +The table below summarize all accepted values for the +.Fl H Ar hide +parameter. The default value is +.Em 15 +which corresponds to the corner case described above. The +.Em filter +column means that filtering is enabled. The +.Em 1proto +column tells that only one protocol will be kept. The +.Em 1neigh +column tells that only one neighbor will be kept. +.Pp +.Bl -column -compact -offset indent "HXXX" "filterX" "1protoX" "1neighX" "filterX" "1protoX" "1neighX" +.It Ta Ta incoming Ta Ta outgoing Ta +.It Ta Em filter Ta Em 1proto Ta Em 1neigh Ta Em filter Ta Em 1proto Ta Em 1neigh +.It Em 0 Ta Ta Ta Ta Ta Ta +.It Em 1 Ta x Ta x Ta Ta x Ta x Ta +.It Em 2 Ta x Ta x Ta Ta Ta Ta +.It Em 3 Ta Ta Ta Ta x Ta x Ta +.It Em 4 Ta x Ta Ta Ta x Ta Ta +.It Em 5 Ta x Ta Ta Ta Ta Ta +.It Em 6 Ta Ta Ta Ta x Ta Ta +.It Em 7 Ta x Ta x Ta x Ta x Ta x Ta +.It Em 8 Ta x Ta x Ta x Ta Ta Ta +.It Em 9 Ta x Ta Ta x Ta x Ta x Ta +.It Em 10 Ta Ta Ta Ta x Ta Ta x +.It Em 11 Ta x Ta Ta x Ta Ta Ta +.It Em 12 Ta x Ta Ta x Ta x Ta Ta x +.It Em 13 Ta x Ta Ta x Ta x Ta Ta +.It Em 14 Ta x Ta x Ta Ta x Ta Ta x +.It Em 15 Ta x Ta x Ta Ta x Ta Ta +.It Em 16 Ta x Ta x Ta x Ta x Ta Ta x +.It Em 17 Ta x Ta x Ta x Ta x Ta Ta +.It Em 18 Ta x Ta Ta Ta x Ta Ta x +.It Em 19 Ta x Ta Ta Ta x Ta x Ta +.El +.Sh FILES +.Bl -tag -width "@LLDPD_CTL_SOCKET@XX" -compact +.It @LLDPD_CTL_SOCKET@ +Unix-domain socket used for communication with +.Xr lldpctl 8 . +.It @sysconfdir@/lldpd.conf +Configuration file for +.Nm . +Commands in this files are executed by +.Xr lldpcli 8 +at start. +.It @sysconfdir@/lldpd.d +Directory containing configuration files whose commands are executed +by +.Xr lldpcli 8 +at start. +.El +.Sh SEE ALSO +.Xr lldpctl 8 , +.Xr lldpcli 8 , +.Xr snmpd 8 +.Sh HISTORY +The +.Nm +program is inspired from a preliminary work of Reyk Floeter. +.Sh AUTHORS +.An -nosplit +The +.Nm +program was written by +.An Pierre-Yves Ritschard Aq pyr@openbsd.org , +and +.An Vincent Bernat Aq bernat@luffy.cx . diff --git a/src/daemon/lldpd.c b/src/daemon/lldpd.c new file mode 100644 index 0000000..4859fb8 --- /dev/null +++ b/src/daemon/lldpd.c @@ -0,0 +1,2020 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "lldpd.h" +#include "trace.h" + +#include <stdio.h> +#include <unistd.h> +#include <errno.h> +#include <limits.h> +#include <signal.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <time.h> +#include <libgen.h> +#include <assert.h> +#include <sys/utsname.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/socket.h> +#include <sys/select.h> +#include <sys/time.h> +#include <sys/ioctl.h> +#include <arpa/inet.h> +#include <netinet/if_ether.h> +#include <pwd.h> +#include <grp.h> + +#if HAVE_VFORK_H +# include <vfork.h> +#endif +#if HAVE_WORKING_FORK +# define vfork fork +#endif + +static void usage(void); + +static struct protocol protos[] = { + { LLDPD_MODE_LLDP, 1, "LLDP", 'l', lldp_send, lldp_decode, NULL, + { LLDP_ADDR_NEAREST_BRIDGE, LLDP_ADDR_NEAREST_NONTPMR_BRIDGE, + LLDP_ADDR_NEAREST_CUSTOMER_BRIDGE } }, +#ifdef ENABLE_CDP + { LLDPD_MODE_CDPV1, 0, "CDPv1", 'c', cdpv1_send, cdp_decode, cdpv1_guess, + { CDP_MULTICAST_ADDR } }, + { LLDPD_MODE_CDPV2, 0, "CDPv2", 'c', cdpv2_send, cdp_decode, cdpv2_guess, + { CDP_MULTICAST_ADDR } }, +#endif +#ifdef ENABLE_SONMP + { LLDPD_MODE_SONMP, 0, "SONMP", 's', sonmp_send, sonmp_decode, NULL, + { SONMP_MULTICAST_ADDR } }, +#endif +#ifdef ENABLE_EDP + { LLDPD_MODE_EDP, 0, "EDP", 'e', edp_send, edp_decode, NULL, + { EDP_MULTICAST_ADDR } }, +#endif +#ifdef ENABLE_FDP + { LLDPD_MODE_FDP, 0, "FDP", 'f', fdp_send, cdp_decode, NULL, + { FDP_MULTICAST_ADDR } }, +#endif + { 0, 0, "any", ' ', NULL, NULL, NULL, { { 0, 0, 0, 0, 0, 0 } } } +}; + +static char **saved_argv; +#ifdef HAVE___PROGNAME +extern const char *__progname; +#else +# define __progname "lldpd" +#endif + +static void +usage(void) +{ + fprintf(stderr, "Usage: %s [OPTIONS ...]\n", __progname); + fprintf(stderr, "Version: %s\n", PACKAGE_STRING); + + fprintf(stderr, "\n"); + + fprintf(stderr, "-d Do not daemonize.\n"); + fprintf(stderr, "-r Receive-only mode\n"); + fprintf(stderr, "-i Disable LLDP-MED inventory TLV transmission.\n"); + fprintf(stderr, + "-k Disable advertising of kernel release, version, machine.\n"); + fprintf(stderr, "-S descr Override the default system description.\n"); + fprintf(stderr, "-P name Override the default hardware platform.\n"); + fprintf(stderr, + "-m IP Specify the IP management addresses of this system.\n"); + fprintf(stderr, + "-u file Specify the Unix-domain socket used for communication with lldpctl(8).\n"); + fprintf(stderr, + "-H mode Specify the behaviour when detecting multiple neighbors.\n"); + fprintf(stderr, "-I iface Limit interfaces to use.\n"); + fprintf(stderr, "-C iface Limit interfaces to use for computing chassis ID.\n"); + fprintf(stderr, "-L path Override path for lldpcli command.\n"); + fprintf(stderr, + "-O file Override default configuration locations processed by lldpcli(8) at start.\n"); +#ifdef ENABLE_LLDPMED + fprintf(stderr, + "-M class Enable emission of LLDP-MED frame. 'class' should be one of:\n"); + fprintf(stderr, " 1 Generic Endpoint (Class I)\n"); + fprintf(stderr, " 2 Media Endpoint (Class II)\n"); + fprintf(stderr, " 3 Communication Device Endpoints (Class III)\n"); + fprintf(stderr, " 4 Network Connectivity Device\n"); +#endif +#ifdef USE_SNMP + fprintf(stderr, "-x Enable SNMP subagent.\n"); + fprintf(stderr, "-X sock Specify the SNMP subagent socket.\n"); +#endif + fprintf(stderr, "\n"); + +#if defined ENABLE_CDP || defined ENABLE_EDP || defined ENABLE_FDP || \ + defined ENABLE_SONMP + fprintf(stderr, "Additional protocol support.\n"); +# ifdef ENABLE_CDP + fprintf(stderr, "-c Enable the support of CDP protocol. (Cisco)\n"); +# endif +# ifdef ENABLE_EDP + fprintf(stderr, "-e Enable the support of EDP protocol. (Extreme)\n"); +# endif +# ifdef ENABLE_FDP + fprintf(stderr, "-f Enable the support of FDP protocol. (Foundry)\n"); +# endif +# ifdef ENABLE_SONMP + fprintf(stderr, "-s Enable the support of SONMP protocol. (Nortel)\n"); +# endif + + fprintf(stderr, "\n"); +#endif + + fprintf(stderr, "See manual page lldpd(8) for more information\n"); + exit(1); +} + +struct lldpd_hardware * +lldpd_get_hardware(struct lldpd *cfg, char *name, int index) +{ + struct lldpd_hardware *hardware; + TAILQ_FOREACH (hardware, &cfg->g_hardware, h_entries) { + if (strcmp(hardware->h_ifname, name) == 0) { + if (hardware->h_flags == 0) { + if (hardware->h_ifindex != 0 && + hardware->h_ifindex != index) { + log_debug("interfaces", + "%s changed index: from %d to %d", + hardware->h_ifname, hardware->h_ifindex, + index); + hardware->h_ifindex_changed = 1; + } + hardware->h_ifindex = index; + break; + } + if (hardware->h_ifindex == index) break; + } + } + return hardware; +} + +/** + * Allocate the default local port. This port will be cloned each time we need a + * new local port. + */ +static void +lldpd_alloc_default_local_port(struct lldpd *cfg) +{ + struct lldpd_port *port; + + if ((port = (struct lldpd_port *)calloc(1, sizeof(struct lldpd_port))) == NULL) + fatal("main", NULL); + +#ifdef ENABLE_DOT1 + TAILQ_INIT(&port->p_vlans); + TAILQ_INIT(&port->p_ppvids); + TAILQ_INIT(&port->p_pids); +#endif +#ifdef ENABLE_CUSTOM + TAILQ_INIT(&port->p_custom_list); +#endif + cfg->g_default_local_port = port; +} + +/** + * Clone a given port. The destination needs to be already allocated. + */ +static int +lldpd_clone_port(struct lldpd_port *destination, struct lldpd_port *source) +{ + + u_int8_t *output = NULL; + ssize_t output_len; + struct lldpd_port *cloned = NULL; + output_len = lldpd_port_serialize(source, (void **)&output); + if (output_len == -1 || + lldpd_port_unserialize(output, output_len, &cloned) <= 0) { + log_warnx("alloc", "unable to clone default port"); + free(output); + return -1; + } + memcpy(destination, cloned, sizeof(struct lldpd_port)); + free(cloned); + free(output); +#ifdef ENABLE_DOT1 + marshal_repair_tailq(lldpd_vlan, &destination->p_vlans, v_entries); + marshal_repair_tailq(lldpd_ppvid, &destination->p_ppvids, p_entries); + marshal_repair_tailq(lldpd_pi, &destination->p_pids, p_entries); +#endif +#ifdef ENABLE_CUSTOM + marshal_repair_tailq(lldpd_custom, &destination->p_custom_list, next); +#endif + return 0; +} + +struct lldpd_hardware * +lldpd_alloc_hardware(struct lldpd *cfg, char *name, int index) +{ + struct lldpd_hardware *hardware; + + log_debug("alloc", "allocate a new local port (%s)", name); + + if ((hardware = (struct lldpd_hardware *)calloc(1, + sizeof(struct lldpd_hardware))) == NULL) + return NULL; + + /* Clone default local port */ + if (lldpd_clone_port(&hardware->h_lport, cfg->g_default_local_port) == -1) { + log_warnx("alloc", "unable to clone default port"); + free(hardware); + return NULL; + } + + hardware->h_cfg = cfg; + strlcpy(hardware->h_ifname, name, sizeof(hardware->h_ifname)); + hardware->h_ifindex = index; + hardware->h_lport.p_chassis = LOCAL_CHASSIS(cfg); + hardware->h_lport.p_chassis->c_refcount++; + TAILQ_INIT(&hardware->h_rports); + +#ifdef ENABLE_LLDPMED + if (LOCAL_CHASSIS(cfg)->c_med_cap_available) { + hardware->h_lport.p_med_cap_enabled = LLDP_MED_CAP_CAP; + if (!cfg->g_config.c_noinventory) + hardware->h_lport.p_med_cap_enabled |= LLDP_MED_CAP_IV; + } +#endif + + levent_hardware_init(hardware); + return hardware; +} + +struct lldpd_mgmt * +lldpd_alloc_mgmt(int family, void *addrptr, size_t addrsize, u_int32_t iface) +{ + struct lldpd_mgmt *mgmt; + + log_debug("alloc", "allocate a new management address (family: %d)", family); + + if (family <= LLDPD_AF_UNSPEC || family >= LLDPD_AF_LAST) { + errno = EAFNOSUPPORT; + return NULL; + } + if (addrsize > LLDPD_MGMT_MAXADDRSIZE) { + errno = EOVERFLOW; + return NULL; + } + mgmt = calloc(1, sizeof(struct lldpd_mgmt)); + if (mgmt == NULL) { + errno = ENOMEM; + return NULL; + } + mgmt->m_family = family; + memcpy(&mgmt->m_addr, addrptr, addrsize); + mgmt->m_addrsize = addrsize; + mgmt->m_iface = iface; + return mgmt; +} + +void +lldpd_hardware_cleanup(struct lldpd *cfg, struct lldpd_hardware *hardware) +{ + log_debug("alloc", "cleanup hardware port %s", hardware->h_ifname); + + free(hardware->h_lport_previous); + free(hardware->h_lchassis_previous_id); + free(hardware->h_lport_previous_id); + free(hardware->h_ifdescr_previous); + lldpd_port_cleanup(&hardware->h_lport, 1); + if (hardware->h_ops && hardware->h_ops->cleanup) + hardware->h_ops->cleanup(cfg, hardware); + levent_hardware_release(hardware); + free(hardware); +} + +static void +lldpd_ifdescr_neighbors(struct lldpd *cfg) +{ + if (!cfg->g_config.c_set_ifdescr) return; + struct lldpd_hardware *hardware; + TAILQ_FOREACH (hardware, &cfg->g_hardware, h_entries) { + struct lldpd_port *port; + char *description; + const char *neighbor = NULL; + unsigned neighbors = 0; + TAILQ_FOREACH (port, &hardware->h_rports, p_entries) { + if (SMART_HIDDEN(port)) continue; + neighbors++; + neighbor = port->p_chassis->c_name; + } + if (neighbors == 0) + description = strdup(""); + else if (neighbors == 1 && neighbor && *neighbor != '\0') { + if (asprintf(&description, "%s", neighbor) == -1) { + continue; + } + } else { + if (asprintf(&description, "%d neighbor%s", neighbors, + (neighbors > 1) ? "s" : "") == -1) { + continue; + } + } + if (hardware->h_ifdescr_previous == NULL || + strcmp(hardware->h_ifdescr_previous, description)) { + priv_iface_description(hardware->h_ifname, description); + free(hardware->h_ifdescr_previous); + hardware->h_ifdescr_previous = description; + } else + free(description); + } +} + +static void +lldpd_count_neighbors(struct lldpd *cfg) +{ +#if HAVE_SETPROCTITLE + struct lldpd_chassis *chassis; + const char *neighbor; + unsigned neighbors = 0; + TAILQ_FOREACH (chassis, &cfg->g_chassis, c_entries) { + neighbors++; + neighbor = chassis->c_name; + } + neighbors--; + if (neighbors == 0) + setproctitle("no neighbor."); + else if (neighbors == 1 && neighbor && *neighbor != '\0') + setproctitle("connected to %s.", neighbor); + else + setproctitle("%d neighbor%s.", neighbors, (neighbors > 1) ? "s" : ""); +#endif + lldpd_ifdescr_neighbors(cfg); +} + +static void +notify_clients_deletion(struct lldpd_hardware *hardware, struct lldpd_port *rport) +{ + TRACE(LLDPD_NEIGHBOR_DELETE(hardware->h_ifname, rport->p_chassis->c_name, + rport->p_descr)); + levent_ctl_notify(hardware->h_ifname, NEIGHBOR_CHANGE_DELETED, rport); +#ifdef USE_SNMP + agent_notify(hardware, NEIGHBOR_CHANGE_DELETED, rport); +#endif +} + +static void +lldpd_reset_timer(struct lldpd *cfg) +{ + /* Reset timer for ports that have been changed. */ + struct lldpd_hardware *hardware; + TAILQ_FOREACH (hardware, &cfg->g_hardware, h_entries) { + /* We keep a flat copy of the local port to see if there is any + * change. To do this, we zero out fields that are not + * significant, marshal the port, then restore. */ + struct lldpd_port *port = &hardware->h_lport; + /* Take the current flags into account to detect a change. */ + port->_p_hardware_flags = hardware->h_flags; + u_int8_t *output = NULL; + ssize_t output_len; + char save[LLDPD_PORT_START_MARKER]; + memcpy(save, port, sizeof(save)); + /* coverity[sizeof_mismatch] + We intentionally partially memset port */ + memset(port, 0, sizeof(save)); + output_len = lldpd_port_serialize(port, (void **)&output); + memcpy(port, save, sizeof(save)); + if (output_len == -1) { + log_warnx("localchassis", + "unable to serialize local port %s to check for differences", + hardware->h_ifname); + continue; + } + + /* Compare with the previous value */ + if (!hardware->h_ifindex_changed && hardware->h_lport_previous && + output_len == hardware->h_lport_previous_len && + !memcmp(output, hardware->h_lport_previous, output_len)) { + log_debug("localchassis", "no change detected for port %s", + hardware->h_ifname); + } else { + log_debug("localchassis", + "change detected for port %s, resetting its timer", + hardware->h_ifname); + hardware->h_ifindex_changed = 0; + levent_schedule_pdu(hardware); + } + + /* Update the value */ + free(hardware->h_lport_previous); + hardware->h_lport_previous = output; + hardware->h_lport_previous_len = output_len; + } +} + +static void +lldpd_all_chassis_cleanup(struct lldpd *cfg) +{ + struct lldpd_chassis *chassis, *chassis_next; + log_debug("localchassis", "cleanup all chassis"); + + for (chassis = TAILQ_FIRST(&cfg->g_chassis); chassis; chassis = chassis_next) { + chassis_next = TAILQ_NEXT(chassis, c_entries); + if (chassis->c_refcount == 0) { + TAILQ_REMOVE(&cfg->g_chassis, chassis, c_entries); + lldpd_chassis_cleanup(chassis, 1); + } + } +} + +void +lldpd_cleanup(struct lldpd *cfg) +{ + struct lldpd_hardware *hardware, *hardware_next; + + log_debug("localchassis", "cleanup all ports"); + + for (hardware = TAILQ_FIRST(&cfg->g_hardware); hardware != NULL; + hardware = hardware_next) { + hardware_next = TAILQ_NEXT(hardware, h_entries); + if (!hardware->h_flags) { + int m = cfg->g_config.c_perm_ifaces ? + pattern_match(hardware->h_ifname, + cfg->g_config.c_perm_ifaces, 0) : + 0; + switch (m) { + case PATTERN_MATCH_DENIED: + log_debug("localchassis", + "delete non-permanent interface %s", + hardware->h_ifname); + TRACE(LLDPD_INTERFACES_DELETE(hardware->h_ifname)); + TAILQ_REMOVE(&cfg->g_hardware, hardware, h_entries); + lldpd_remote_cleanup(hardware, notify_clients_deletion, + 1); + lldpd_hardware_cleanup(cfg, hardware); + break; + case PATTERN_MATCH_ALLOWED: + case PATTERN_MATCH_ALLOWED_EXACT: + log_debug("localchassis", "do not delete %s, permanent", + hardware->h_ifname); + lldpd_remote_cleanup(hardware, notify_clients_deletion, + 1); + break; + } + } else { + lldpd_remote_cleanup(hardware, notify_clients_deletion, + !(hardware->h_flags & IFF_RUNNING)); + } + } + + levent_schedule_cleanup(cfg); + lldpd_all_chassis_cleanup(cfg); + lldpd_count_neighbors(cfg); +} + +/* Update chassis `ochassis' with values from `chassis'. The later one is not + expected to be part of a list! It will also be wiped from memory. */ +static void +lldpd_move_chassis(struct lldpd_chassis *ochassis, struct lldpd_chassis *chassis) +{ + struct lldpd_mgmt *mgmt, *mgmt_next; + + /* We want to keep refcount, index and list stuff from the current + * chassis */ + TAILQ_ENTRY(lldpd_chassis) entries; + int refcount = ochassis->c_refcount; + int index = ochassis->c_index; + memcpy(&entries, &ochassis->c_entries, sizeof(entries)); + lldpd_chassis_cleanup(ochassis, 0); + + /* Make the copy. */ + /* WARNING: this is a kludgy hack, we need in-place copy and cannot use + * marshaling. */ + memcpy(ochassis, chassis, sizeof(struct lldpd_chassis)); + TAILQ_INIT(&ochassis->c_mgmt); + + /* Copy of management addresses */ + for (mgmt = TAILQ_FIRST(&chassis->c_mgmt); mgmt != NULL; mgmt = mgmt_next) { + mgmt_next = TAILQ_NEXT(mgmt, m_entries); + TAILQ_REMOVE(&chassis->c_mgmt, mgmt, m_entries); + TAILQ_INSERT_TAIL(&ochassis->c_mgmt, mgmt, m_entries); + } + + /* Restore saved values */ + ochassis->c_refcount = refcount; + ochassis->c_index = index; + memcpy(&ochassis->c_entries, &entries, sizeof(entries)); + + /* Get rid of the new chassis */ + free(chassis); +} + +static int +lldpd_guess_type(struct lldpd *cfg, char *frame, int s) +{ + size_t i, j; + if (s < ETHER_ADDR_LEN) return -1; + for (i = 0; cfg->g_protocols[i].mode != 0; i++) { + if (!cfg->g_protocols[i].enabled) continue; + if (cfg->g_protocols[i].guess == NULL) { + for (j = 0; j < sizeof(cfg->g_protocols[0].mac) / + sizeof(cfg->g_protocols[0].mac[0]); + j++) { + if (memcmp(frame, cfg->g_protocols[i].mac[j], + ETHER_ADDR_LEN) == 0) { + log_debug("decode", + "guessed protocol is %s (from MAC address)", + cfg->g_protocols[i].name); + return cfg->g_protocols[i].mode; + } + } + } else { + if (cfg->g_protocols[i].guess(frame, s)) { + log_debug("decode", + "guessed protocol is %s (from detector function)", + cfg->g_protocols[i].name); + return cfg->g_protocols[i].mode; + } + } + } + return -1; +} + +static void +lldpd_decode(struct lldpd *cfg, char *frame, int s, struct lldpd_hardware *hardware) +{ + int i; + struct lldpd_chassis *chassis, *ochassis = NULL; + struct lldpd_port *port, *oport = NULL, *aport; + int guess = LLDPD_MODE_LLDP; + + log_debug("decode", "decode a received frame on %s", hardware->h_ifname); + + if (s < sizeof(struct ether_header) + 4) { + /* Too short, just discard it */ + hardware->h_rx_discarded_cnt++; + return; + } + + /* Decapsulate VLAN frames */ + struct ether_header eheader; + memcpy(&eheader, frame, sizeof(struct ether_header)); + if (eheader.ether_type == htons(ETHERTYPE_VLAN)) { + /* VLAN decapsulation means to shift 4 bytes left the frame from + * offset 2*ETHER_ADDR_LEN */ + memmove(frame + 2 * ETHER_ADDR_LEN, frame + 2 * ETHER_ADDR_LEN + 4, + s - 2 * ETHER_ADDR_LEN); + s -= 4; + } + + TAILQ_FOREACH (oport, &hardware->h_rports, p_entries) { + if ((oport->p_lastframe != NULL) && (oport->p_lastframe->size == s) && + (memcmp(oport->p_lastframe->frame, frame, s) == 0)) { + /* Already received the same frame */ + log_debug("decode", "duplicate frame, no need to decode"); + oport->p_lastupdate = time(NULL); + return; + } + } + + guess = lldpd_guess_type(cfg, frame, s); + for (i = 0; cfg->g_protocols[i].mode != 0; i++) { + if (!cfg->g_protocols[i].enabled) continue; + if (cfg->g_protocols[i].mode == guess) { + log_debug("decode", "using decode function for %s protocol", + cfg->g_protocols[i].name); + if (cfg->g_protocols[i].decode(cfg, frame, s, hardware, + &chassis, &port) == -1) { + log_debug("decode", + "function for %s protocol did not decode this frame", + cfg->g_protocols[i].name); + hardware->h_rx_discarded_cnt++; + return; + } + chassis->c_protocol = port->p_protocol = + cfg->g_protocols[i].mode; + break; + } + } + if (cfg->g_protocols[i].mode == 0) { + log_debug("decode", "unable to guess frame type on %s", + hardware->h_ifname); + return; + } + TRACE(LLDPD_FRAME_DECODED(hardware->h_ifname, cfg->g_protocols[i].name, + chassis->c_name, port->p_descr)); + + /* Do we already have the same MSAP somewhere? */ + int count = 0; + log_debug("decode", "search for the same MSAP"); + TAILQ_FOREACH (oport, &hardware->h_rports, p_entries) { + if (port->p_protocol == oport->p_protocol) { + count++; + if ((port->p_id_subtype == oport->p_id_subtype) && + (port->p_id_len == oport->p_id_len) && + (memcmp(port->p_id, oport->p_id, port->p_id_len) == 0) && + (chassis->c_id_subtype == oport->p_chassis->c_id_subtype) && + (chassis->c_id_len == oport->p_chassis->c_id_len) && + (memcmp(chassis->c_id, oport->p_chassis->c_id, + chassis->c_id_len) == 0)) { + ochassis = oport->p_chassis; + log_debug("decode", "MSAP is already known"); + break; + } + } + } + /* Do we have room for a new MSAP? */ + if (!oport && cfg->g_config.c_max_neighbors) { + if (count == (cfg->g_config.c_max_neighbors - 1)) { + log_debug("decode", + "max neighbors %d reached for port %s, " + "dropping any new ones silently", + cfg->g_config.c_max_neighbors, hardware->h_ifname); + } else if (count > cfg->g_config.c_max_neighbors - 1) { + log_debug("decode", + "too many neighbors for port %s, drop this new one", + hardware->h_ifname); + lldpd_port_cleanup(port, 1); + lldpd_chassis_cleanup(chassis, 1); + free(port); + return; + } + } + /* No, but do we already know the system? */ + if (!oport) { + log_debug("decode", "MSAP is unknown, search for the chassis"); + TAILQ_FOREACH (ochassis, &cfg->g_chassis, c_entries) { + if ((chassis->c_protocol == ochassis->c_protocol) && + (chassis->c_id_subtype == ochassis->c_id_subtype) && + (chassis->c_id_len == ochassis->c_id_len) && + (memcmp(chassis->c_id, ochassis->c_id, chassis->c_id_len) == + 0)) + break; + } + } + + if (oport) { + /* The port is known, remove it before adding it back */ + TAILQ_REMOVE(&hardware->h_rports, oport, p_entries); + lldpd_port_cleanup(oport, 1); + free(oport); + } + if (ochassis) { + if (port->p_ttl == 0) { + /* Shutdown LLDPDU is special. We do not want to replace + * the chassis. Free the new chassis (which is mostly empty) */ + log_debug("decode", "received a shutdown LLDPDU"); + lldpd_chassis_cleanup(chassis, 1); + } else { + lldpd_move_chassis(ochassis, chassis); + } + chassis = ochassis; + } else { + /* Chassis not known, add it */ + log_debug("decode", "unknown chassis, add it to the list"); + chassis->c_index = ++cfg->g_lastrid; + chassis->c_refcount = 0; + TAILQ_INSERT_TAIL(&cfg->g_chassis, chassis, c_entries); + i = 0; + TAILQ_FOREACH (ochassis, &cfg->g_chassis, c_entries) + i++; + log_debug("decode", "%d different systems are known", i); + } + /* Add port */ + port->p_lastchange = port->p_lastupdate = time(NULL); + if ((port->p_lastframe = (struct lldpd_frame *)malloc( + s + sizeof(struct lldpd_frame))) != NULL) { + port->p_lastframe->size = s; + memcpy(port->p_lastframe->frame, frame, s); + } + TAILQ_INSERT_TAIL(&hardware->h_rports, port, p_entries); + port->p_chassis = chassis; + port->p_chassis->c_refcount++; + /* Several cases are possible : + 1. chassis is new, its refcount was 0. It is now attached + to this port, its refcount is 1. + 2. chassis already exists and was attached to another + port, we increase its refcount accordingly. + 3. chassis already exists and was attached to the same + port, its refcount was decreased with + lldpd_port_cleanup() and is now increased again. + + In all cases, if the port already existed, it has been + freed with lldpd_port_cleanup() and therefore, the refcount + of the chassis that was attached to it is decreased. + */ + i = 0; + /* coverity[use_after_free] + TAILQ_REMOVE does the right thing */ + TAILQ_FOREACH (aport, &hardware->h_rports, p_entries) + i++; + log_debug("decode", "%d neighbors for %s", i, hardware->h_ifname); + + if (!oport) hardware->h_insert_cnt++; + + /* Notify */ + log_debug("decode", "send notifications for changes on %s", hardware->h_ifname); + if (oport) { + TRACE(LLDPD_NEIGHBOR_UPDATE(hardware->h_ifname, chassis->c_name, + port->p_descr, i)); + levent_ctl_notify(hardware->h_ifname, NEIGHBOR_CHANGE_UPDATED, port); +#ifdef USE_SNMP + agent_notify(hardware, NEIGHBOR_CHANGE_UPDATED, port); +#endif + } else { + TRACE(LLDPD_NEIGHBOR_NEW(hardware->h_ifname, chassis->c_name, + port->p_descr, i)); + levent_ctl_notify(hardware->h_ifname, NEIGHBOR_CHANGE_ADDED, port); +#ifdef USE_SNMP + agent_notify(hardware, NEIGHBOR_CHANGE_ADDED, port); +#endif + } + +#ifdef ENABLE_LLDPMED + if (!oport && port->p_chassis->c_med_type) { + /* New neighbor, fast start */ + if (hardware->h_cfg->g_config.c_enable_fast_start && + !hardware->h_tx_fast) { + log_debug("decode", + "%s: entering fast start due to " + "new neighbor", + hardware->h_ifname); + hardware->h_tx_fast = hardware->h_cfg->g_config.c_tx_fast_init; + } + + levent_schedule_pdu(hardware); + } +#endif + + return; +} + +/* Get the output of lsb_release -s -d. This is a slow function. It should be + called once. It return NULL if any problem happens. Otherwise, this is a + statically allocated buffer. The result includes the trailing \n */ +static char * +lldpd_get_lsb_release() +{ + static char release[1024]; + char cmd[][12] = { "lsb_release", "-s", "-d" }; + char *const command[] = { cmd[0], cmd[1], cmd[2], NULL }; + int pid, status, devnull, count; + int pipefd[2]; + + log_debug("localchassis", "grab LSB release"); + + if (pipe(pipefd)) { + log_warn("localchassis", "unable to get a pair of pipes"); + return NULL; + } + + pid = vfork(); + switch (pid) { + case -1: + log_warn("localchassis", "unable to fork"); + return NULL; + case 0: + /* Child, exec lsb_release */ + close(pipefd[0]); + if ((devnull = open("/dev/null", O_RDWR, 0)) != -1) { + dup2(devnull, STDIN_FILENO); + dup2(devnull, STDERR_FILENO); + dup2(pipefd[1], STDOUT_FILENO); + if (devnull > 2) close(devnull); + if (pipefd[1] > 2) close(pipefd[1]); + execvp("lsb_release", command); + } + _exit(127); + break; + default: + /* Father, read the output from the children */ + close(pipefd[1]); + count = 0; + do { + status = + read(pipefd[0], release + count, sizeof(release) - count); + if ((status == -1) && (errno == EINTR)) continue; + if (status > 0) count += status; + } while (count < sizeof(release) && (status > 0)); + if (status < 0) { + log_info("localchassis", "unable to read from lsb_release"); + close(pipefd[0]); + waitpid(pid, &status, 0); + return NULL; + } + close(pipefd[0]); + if (count >= sizeof(release)) { + log_info("localchassis", "output of lsb_release is too large"); + waitpid(pid, &status, 0); + return NULL; + } + status = -1; + if (waitpid(pid, &status, 0) != pid) return NULL; + if (!WIFEXITED(status) || (WEXITSTATUS(status) != 0)) { + log_info("localchassis", + "lsb_release information not available"); + return NULL; + } + if (!count) { + log_info("localchassis", + "lsb_release returned an empty string"); + return NULL; + } + release[count] = '\0'; + return release; + } + /* Should not be here */ + return NULL; +} + +/* Same like lldpd_get_lsb_release but reads /etc/os-release for PRETTY_NAME=. */ +static char * +lldpd_get_os_release() +{ + static char release[1024]; + char line[1024]; + char *key, *val; + char *ptr1 = release; + + log_debug("localchassis", "grab OS release"); + FILE *fp = fopen("/etc/os-release", "r"); + if (!fp) { + log_debug("localchassis", "could not open /etc/os-release"); + fp = fopen("/usr/lib/os-release", "r"); + } + if (!fp) { + log_info("localchassis", + "could not open either /etc/os-release or /usr/lib/os-release"); + return NULL; + } + + while ((fgets(line, sizeof(line), fp) != NULL)) { + key = strtok(line, "="); + val = strtok(NULL, "="); + + if (strncmp(key, "PRETTY_NAME", sizeof(line)) == 0) { + strlcpy(release, val, sizeof(line)); + break; + } + } + fclose(fp); + + /* Remove trailing newline and all " in the string. */ + ptr1 = release + strlen(release) - 1; + while (ptr1 != release && ((*ptr1 == '"') || (*ptr1 == '\n'))) { + *ptr1 = '\0'; + ptr1--; + } + if (release[0] == '"') return release + 1; + return release; +} + +static void +lldpd_hide_ports(struct lldpd *cfg, struct lldpd_hardware *hardware, int mask) +{ + struct lldpd_port *port; + int protocols[LLDPD_MODE_MAX + 1]; + char buffer[256]; + int i, j, k, found; + unsigned int min; + + log_debug("smartfilter", "apply smart filter for port %s", hardware->h_ifname); + + /* Compute the number of occurrences of each protocol */ + for (i = 0; i <= LLDPD_MODE_MAX; i++) + protocols[i] = 0; + TAILQ_FOREACH (port, &hardware->h_rports, p_entries) + protocols[port->p_protocol]++; + + /* Turn the protocols[] array into an array of + enabled/disabled protocols. 1 means enabled, 0 + means disabled. */ + min = (unsigned int)-1; + for (i = 0; i <= LLDPD_MODE_MAX; i++) + if (protocols[i] && (protocols[i] < min)) min = protocols[i]; + found = 0; + for (i = 0; i <= LLDPD_MODE_MAX; i++) + if ((protocols[i] == min) && !found) { + /* If we need a tie breaker, we take + the first protocol only */ + if (cfg->g_config.c_smart & mask & + (SMART_OUTGOING_ONE_PROTO | SMART_INCOMING_ONE_PROTO)) + found = 1; + protocols[i] = 1; + } else + protocols[i] = 0; + + /* We set the p_hidden flag to 1 if the protocol is disabled */ + TAILQ_FOREACH (port, &hardware->h_rports, p_entries) { + if (mask == SMART_OUTGOING) + port->p_hidden_out = protocols[port->p_protocol] ? 0 : 1; + else + port->p_hidden_in = protocols[port->p_protocol] ? 0 : 1; + } + + /* If we want only one neighbor, we take the first one */ + if (cfg->g_config.c_smart & mask & + (SMART_OUTGOING_ONE_NEIGH | SMART_INCOMING_ONE_NEIGH)) { + found = 0; + TAILQ_FOREACH (port, &hardware->h_rports, p_entries) { + if (mask == SMART_OUTGOING) { + if (found) port->p_hidden_out = 1; + if (!port->p_hidden_out) found = 1; + } + if (mask == SMART_INCOMING) { + if (found) port->p_hidden_in = 1; + if (!port->p_hidden_in) found = 1; + } + } + } + + /* Print a debug message summarizing the operation */ + for (i = 0; i <= LLDPD_MODE_MAX; i++) + protocols[i] = 0; + k = j = 0; + TAILQ_FOREACH (port, &hardware->h_rports, p_entries) { + if (!(((mask == SMART_OUTGOING) && port->p_hidden_out) || + ((mask == SMART_INCOMING) && port->p_hidden_in))) { + k++; + protocols[port->p_protocol] = 1; + } + j++; + } + buffer[0] = '\0'; + for (i = 0; cfg->g_protocols[i].mode != 0; i++) { + if (cfg->g_protocols[i].enabled && + protocols[cfg->g_protocols[i].mode]) { + if (strlen(buffer) + strlen(cfg->g_protocols[i].name) + 3 > + sizeof(buffer)) { + /* Unlikely, our buffer is too small */ + memcpy(buffer + sizeof(buffer) - 4, "...", 4); + break; + } + if (buffer[0]) + strncat(buffer, ", ", + sizeof(buffer) - strlen(buffer) - 1); + strncat(buffer, cfg->g_protocols[i].name, + sizeof(buffer) - strlen(buffer) - 1); + } + } + log_debug("smartfilter", "%s: %s: %d visible neighbors (out of %d)", + hardware->h_ifname, (mask == SMART_OUTGOING) ? "out filter" : "in filter", + k, j); + log_debug("smartfilter", "%s: protocols: %s", hardware->h_ifname, + buffer[0] ? buffer : "(none)"); +} + +/* Hide unwanted ports depending on smart mode set by the user */ +static void +lldpd_hide_all(struct lldpd *cfg) +{ + struct lldpd_hardware *hardware; + + if (!cfg->g_config.c_smart) return; + log_debug("smartfilter", "apply smart filter results on all ports"); + TAILQ_FOREACH (hardware, &cfg->g_hardware, h_entries) { + if (cfg->g_config.c_smart & SMART_INCOMING_FILTER) + lldpd_hide_ports(cfg, hardware, SMART_INCOMING); + if (cfg->g_config.c_smart & SMART_OUTGOING_FILTER) + lldpd_hide_ports(cfg, hardware, SMART_OUTGOING); + } +} + +/* If PD device and PSE allocated power, echo back this change. If we have + * several LLDP neighbors, we use the latest updated. */ +static void +lldpd_dot3_power_pd_pse(struct lldpd_hardware *hardware) +{ +#ifdef ENABLE_DOT3 + struct lldpd_port *port, *selected_port = NULL; + /* Are we a PD device? */ + if (hardware->h_lport.p_power.devicetype != LLDP_DOT3_POWER_PD) return; + TAILQ_FOREACH (port, &hardware->h_rports, p_entries) { + if (port->p_hidden_in) continue; + + if (port->p_protocol != LLDPD_MODE_LLDP && + port->p_protocol != LLDPD_MODE_CDPV2) + continue; + + if (port->p_power.devicetype != LLDP_DOT3_POWER_PSE) continue; + if (!selected_port || port->p_lastupdate > selected_port->p_lastupdate) + selected_port = port; + } + if (selected_port && + selected_port->p_power.allocated != hardware->h_lport.p_power.allocated) { + log_info("receive", + "for %s, PSE told us allocated is now %d instead of %d", + hardware->h_ifname, selected_port->p_power.allocated, + hardware->h_lport.p_power.allocated); + hardware->h_lport.p_power.allocated = selected_port->p_power.allocated; + levent_schedule_pdu(hardware); + } + +# ifdef ENABLE_CDP + if (selected_port && + selected_port->p_cdp_power.management_id != + hardware->h_lport.p_cdp_power.management_id) { + hardware->h_lport.p_cdp_power.management_id = + selected_port->p_cdp_power.management_id; + } +# endif + +#endif +} + +void +lldpd_recv(struct lldpd *cfg, struct lldpd_hardware *hardware, int fd) +{ + char *buffer = NULL; + int n; + log_debug("receive", "receive a frame on %s", hardware->h_ifname); + if ((buffer = (char *)malloc(hardware->h_mtu)) == NULL) { + log_warn("receive", "failed to alloc reception buffer"); + return; + } + if ((n = hardware->h_ops->recv(cfg, hardware, fd, buffer, hardware->h_mtu)) == + -1) { + log_debug("receive", "discard frame received on %s", + hardware->h_ifname); + free(buffer); + return; + } + if (hardware->h_lport.p_disable_rx) { + log_debug("receive", "RX disabled, ignore the frame on %s", + hardware->h_ifname); + free(buffer); + return; + } + if (cfg->g_config.c_paused) { + log_debug("receive", "paused, ignore the frame on %s", + hardware->h_ifname); + free(buffer); + return; + } + hardware->h_rx_cnt++; + log_debug("receive", "decode received frame on %s", hardware->h_ifname); + TRACE(LLDPD_FRAME_RECEIVED(hardware->h_ifname, buffer, (size_t)n)); + lldpd_decode(cfg, buffer, n, hardware); + lldpd_hide_all(cfg); /* Immediatly hide */ + lldpd_dot3_power_pd_pse(hardware); + lldpd_count_neighbors(cfg); + free(buffer); +} + +static void +lldpd_send_shutdown(struct lldpd_hardware *hardware) +{ + struct lldpd *cfg = hardware->h_cfg; + if (cfg->g_config.c_receiveonly || cfg->g_config.c_paused) return; + if (hardware->h_lport.p_disable_tx) return; + if ((hardware->h_flags & IFF_RUNNING) == 0) return; + + /* It's safe to call `lldp_send_shutdown()` because shutdown LLDPU will + * only be emitted if LLDP was sent on that port. */ + if (lldp_send_shutdown(hardware->h_cfg, hardware) != 0) + log_warnx("send", "unable to send shutdown LLDPDU on %s", + hardware->h_ifname); +} + +void +lldpd_send(struct lldpd_hardware *hardware) +{ + struct lldpd *cfg = hardware->h_cfg; + struct lldpd_port *port; + int i, sent; + + if (cfg->g_config.c_receiveonly || cfg->g_config.c_paused) return; + if (hardware->h_lport.p_disable_tx) return; + if ((hardware->h_flags & IFF_RUNNING) == 0) return; + + log_debug("send", "send PDU on %s", hardware->h_ifname); + sent = 0; + for (i = 0; cfg->g_protocols[i].mode != 0; i++) { + if (!cfg->g_protocols[i].enabled) continue; + /* We send only if we have at least one remote system + * speaking this protocol or if the protocol is forced */ + if (cfg->g_protocols[i].enabled > 1) { + cfg->g_protocols[i].send(cfg, hardware); + sent++; + continue; + } + TAILQ_FOREACH (port, &hardware->h_rports, p_entries) { + /* If this remote port is disabled, we don't + * consider it */ + if (port->p_hidden_out) continue; + if (port->p_protocol == cfg->g_protocols[i].mode) { + TRACE(LLDPD_FRAME_SEND(hardware->h_ifname, + cfg->g_protocols[i].name)); + log_debug("send", "send PDU on %s with protocol %s", + hardware->h_ifname, cfg->g_protocols[i].name); + cfg->g_protocols[i].send(cfg, hardware); + hardware->h_lport.p_protocol = cfg->g_protocols[i].mode; + sent++; + break; + } + } + } + + if (!sent) { + /* Nothing was sent for this port, let's speak the first + * available protocol. */ + for (i = 0; cfg->g_protocols[i].mode != 0; i++) { + if (!cfg->g_protocols[i].enabled) continue; + TRACE(LLDPD_FRAME_SEND(hardware->h_ifname, + cfg->g_protocols[i].name)); + log_debug("send", "fallback to protocol %s for %s", + cfg->g_protocols[i].name, hardware->h_ifname); + cfg->g_protocols[i].send(cfg, hardware); + break; + } + if (cfg->g_protocols[i].mode == 0) + log_warnx("send", "no protocol enabled, dunno what to send"); + } +} + +#ifdef ENABLE_LLDPMED +static void +lldpd_med(struct lldpd *cfg, struct utsname *un) +{ + static short int once = 0; + if (!once && cfg) { + LOCAL_CHASSIS(cfg)->c_med_hw = dmi_hw(); + LOCAL_CHASSIS(cfg)->c_med_fw = dmi_fw(); + LOCAL_CHASSIS(cfg)->c_med_sn = dmi_sn(); + LOCAL_CHASSIS(cfg)->c_med_manuf = dmi_manuf(); + LOCAL_CHASSIS(cfg)->c_med_model = dmi_model(); + LOCAL_CHASSIS(cfg)->c_med_asset = dmi_asset(); + if (un) { + if (LOCAL_CHASSIS(cfg)->c_med_sw) + free(LOCAL_CHASSIS(cfg)->c_med_sw); + + if (cfg->g_config.c_advertise_version) + LOCAL_CHASSIS(cfg)->c_med_sw = strdup(un->release); + else + LOCAL_CHASSIS(cfg)->c_med_sw = strdup("Unknown"); + } + once = 1; + } +} +#endif + +static int +lldpd_routing_enabled(struct lldpd *cfg) +{ + int routing; + + if ((LOCAL_CHASSIS(cfg)->c_cap_available & LLDP_CAP_ROUTER) == 0) return 0; + + if ((routing = interfaces_routing_enabled(cfg)) == -1) { + log_debug("localchassis", "unable to check if routing is enabled"); + return 0; + } + return routing; +} + +void +lldpd_update_localchassis(struct lldpd *cfg) +{ + struct utsname un; + char *hp; + + log_debug("localchassis", "update information for local chassis"); + assert(LOCAL_CHASSIS(cfg) != NULL); + + /* Set system name and description */ + if (uname(&un) < 0) fatal("localchassis", "failed to get system information"); + if (cfg->g_config.c_hostname) { + log_debug("localchassis", "use overridden system name `%s`", + cfg->g_config.c_hostname); + hp = cfg->g_config.c_hostname; + } else { + if ((hp = priv_gethostname()) == NULL) + fatal("localchassis", "failed to get system name"); + } + free(LOCAL_CHASSIS(cfg)->c_name); + free(LOCAL_CHASSIS(cfg)->c_descr); + if ((LOCAL_CHASSIS(cfg)->c_name = strdup(hp)) == NULL) + fatal("localchassis", NULL); + if (cfg->g_config.c_description) { + log_debug("localchassis", "use overridden description `%s`", + cfg->g_config.c_description); + if (asprintf(&LOCAL_CHASSIS(cfg)->c_descr, "%s", + cfg->g_config.c_description) == -1) + fatal("localchassis", "failed to set full system description"); + } else { + if (cfg->g_config.c_advertise_version) { + log_debug("localchassis", "advertise system version"); + if (asprintf(&LOCAL_CHASSIS(cfg)->c_descr, "%s %s %s %s %s", + cfg->g_lsb_release ? cfg->g_lsb_release : "", + un.sysname, un.release, un.version, un.machine) == -1) + fatal("localchassis", + "failed to set full system description"); + } else { + log_debug("localchassis", "do not advertise system version"); + if (asprintf(&LOCAL_CHASSIS(cfg)->c_descr, "%s", + cfg->g_lsb_release ? cfg->g_lsb_release : un.sysname) == + -1) + fatal("localchassis", + "failed to set minimal system description"); + } + } + if (cfg->g_config.c_platform == NULL) + cfg->g_config.c_platform = strdup(un.sysname); + + if (!cfg->g_config.c_cap_override) { + /* Check routing */ + if (lldpd_routing_enabled(cfg)) { + log_debug("localchassis", + "routing is enabled, enable router capability"); + LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_ROUTER; + } else + LOCAL_CHASSIS(cfg)->c_cap_enabled &= ~LLDP_CAP_ROUTER; + +#ifdef ENABLE_LLDPMED + if (LOCAL_CHASSIS(cfg)->c_cap_available & LLDP_CAP_TELEPHONE) + LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_TELEPHONE; + lldpd_med(cfg, &un); +#endif + if ((LOCAL_CHASSIS(cfg)->c_cap_available & LLDP_CAP_STATION) && + (LOCAL_CHASSIS(cfg)->c_cap_enabled == 0)) + LOCAL_CHASSIS(cfg)->c_cap_enabled = LLDP_CAP_STATION; + else if (LOCAL_CHASSIS(cfg)->c_cap_enabled != LLDP_CAP_STATION) + LOCAL_CHASSIS(cfg)->c_cap_enabled &= ~LLDP_CAP_STATION; + } + + /* Set chassis ID if needed. This is only done if chassis ID + has not been set previously (with the MAC address of an + interface for example) + */ + if (cfg->g_config.c_cid_string != NULL) { + log_debug("localchassis", "use specified chassis ID string"); + free(LOCAL_CHASSIS(cfg)->c_id); + if (!(LOCAL_CHASSIS(cfg)->c_id = strdup(cfg->g_config.c_cid_string))) + fatal("localchassis", NULL); + LOCAL_CHASSIS(cfg)->c_id_len = strlen(cfg->g_config.c_cid_string); + LOCAL_CHASSIS(cfg)->c_id_subtype = LLDP_CHASSISID_SUBTYPE_LOCAL; + } + if (LOCAL_CHASSIS(cfg)->c_id == NULL) { + log_debug("localchassis", + "no chassis ID is currently set, use chassis name"); + if (!(LOCAL_CHASSIS(cfg)->c_id = strdup(LOCAL_CHASSIS(cfg)->c_name))) + fatal("localchassis", NULL); + LOCAL_CHASSIS(cfg)->c_id_len = strlen(LOCAL_CHASSIS(cfg)->c_name); + LOCAL_CHASSIS(cfg)->c_id_subtype = LLDP_CHASSISID_SUBTYPE_LOCAL; + } +} + +void +lldpd_update_localports(struct lldpd *cfg) +{ + struct lldpd_hardware *hardware; + + log_debug("localchassis", "update information for local ports"); + + /* h_flags is set to 0 for each port. If the port is updated, h_flags + * will be set to a non-zero value. This will allow us to clean up any + * non up-to-date port */ + TAILQ_FOREACH (hardware, &cfg->g_hardware, h_entries) + hardware->h_flags = 0; + + TRACE(LLDPD_INTERFACES_UPDATE()); + interfaces_update(cfg); + lldpd_cleanup(cfg); + lldpd_reset_timer(cfg); +} + +void +lldpd_loop(struct lldpd *cfg) +{ + /* Main loop. + 1. Update local ports information + 2. Update local chassis information + */ + log_debug("loop", "start new loop"); + if (!cfg->g_config.c_cap_override) LOCAL_CHASSIS(cfg)->c_cap_enabled = 0; + /* Information for local ports is triggered even when it is possible to + * update them on some other event because we want to refresh them if we + * missed something. */ + log_debug("loop", "update information for local ports"); + lldpd_update_localports(cfg); + log_debug("loop", "update information for local chassis"); + lldpd_update_localchassis(cfg); + lldpd_count_neighbors(cfg); +} + +static void +lldpd_exit(struct lldpd *cfg) +{ + char *lockname = NULL; + struct lldpd_hardware *hardware, *hardware_next; + log_debug("main", "exit lldpd"); + + TAILQ_FOREACH (hardware, &cfg->g_hardware, h_entries) + lldpd_send_shutdown(hardware); + + if (asprintf(&lockname, "%s.lock", cfg->g_ctlname) != -1) { + priv_ctl_cleanup(lockname); + free(lockname); + } + close(cfg->g_ctl); + priv_ctl_cleanup(cfg->g_ctlname); + log_debug("main", "cleanup hardware information"); + for (hardware = TAILQ_FIRST(&cfg->g_hardware); hardware != NULL; + hardware = hardware_next) { + hardware_next = TAILQ_NEXT(hardware, h_entries); + log_debug("main", "cleanup interface %s", hardware->h_ifname); + lldpd_remote_cleanup(hardware, NULL, 1); + lldpd_hardware_cleanup(cfg, hardware); + } + interfaces_cleanup(cfg); + lldpd_port_cleanup(cfg->g_default_local_port, 1); + lldpd_all_chassis_cleanup(cfg); + free(cfg->g_default_local_port); + free(cfg->g_config.c_platform); + levent_shutdown(cfg); +} + +/** + * Run lldpcli to configure lldpd. + * + * @return PID of running lldpcli or -1 if error. + */ +static pid_t +lldpd_configure(int use_syslog, int debug, const char *path, const char *ctlname, + const char *config_path) +{ + pid_t lldpcli = vfork(); + int devnull; + + char sdebug[debug + 4]; + if (use_syslog) + strlcpy(sdebug, "-s", 3); + else { + /* debug = 0 -> -sd */ + /* debug = 1 -> -sdd */ + /* debug = 2 -> -sddd */ + memset(sdebug, 'd', sizeof(sdebug)); + sdebug[debug + 3] = '\0'; + sdebug[0] = '-'; + sdebug[1] = 's'; + } + log_debug("main", "invoke %s %s", path, sdebug); + + switch (lldpcli) { + case -1: + log_warn("main", "unable to fork"); + return -1; + case 0: + /* Child, exec lldpcli */ + if ((devnull = open("/dev/null", O_RDWR, 0)) != -1) { + dup2(devnull, STDIN_FILENO); + dup2(devnull, STDOUT_FILENO); + if (devnull > 2) close(devnull); + + if (config_path) { + execl(path, "lldpcli", sdebug, "-u", ctlname, "-C", + config_path, "resume", (char *)NULL); + } else { + execl(path, "lldpcli", sdebug, "-u", ctlname, "-C", + SYSCONFDIR "/lldpd.conf", "-C", + SYSCONFDIR "/lldpd.d", "resume", (char *)NULL); + } + + log_warn("main", "unable to execute %s", path); + log_warnx("main", + "configuration is incomplete, lldpd needs to be unpaused"); + } + _exit(127); + break; + default: + /* Father, don't do anything stupid */ + return lldpcli; + } + /* Should not be here */ + return -1; +} + +struct intint { + int a; + int b; +}; +static const struct intint filters[] = { { 0, 0 }, + { 1, + SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO | SMART_OUTGOING_FILTER | + SMART_OUTGOING_ONE_PROTO }, + { 2, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO }, + { 3, SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_PROTO }, + { 4, SMART_INCOMING_FILTER | SMART_OUTGOING_FILTER }, + { 5, SMART_INCOMING_FILTER }, { 6, SMART_OUTGOING_FILTER }, + { 7, + SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO | + SMART_INCOMING_ONE_NEIGH | SMART_OUTGOING_FILTER | + SMART_OUTGOING_ONE_PROTO }, + { 8, + SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO | + SMART_INCOMING_ONE_NEIGH }, + { 9, + SMART_INCOMING_FILTER | SMART_INCOMING_ONE_NEIGH | SMART_OUTGOING_FILTER | + SMART_OUTGOING_ONE_PROTO }, + { 10, SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_NEIGH }, + { 11, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_NEIGH }, + { 12, + SMART_INCOMING_FILTER | SMART_INCOMING_ONE_NEIGH | SMART_OUTGOING_FILTER | + SMART_OUTGOING_ONE_NEIGH }, + { 13, + SMART_INCOMING_FILTER | SMART_INCOMING_ONE_NEIGH | SMART_OUTGOING_FILTER }, + { 14, + SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO | SMART_OUTGOING_FILTER | + SMART_OUTGOING_ONE_NEIGH }, + { 15, + SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO | SMART_OUTGOING_FILTER }, + { 16, + SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO | + SMART_INCOMING_ONE_NEIGH | SMART_OUTGOING_FILTER | + SMART_OUTGOING_ONE_NEIGH }, + { 17, + SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO | + SMART_INCOMING_ONE_NEIGH | SMART_OUTGOING_FILTER }, + { 18, + SMART_INCOMING_FILTER | SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_NEIGH }, + { 19, + SMART_INCOMING_FILTER | SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_PROTO }, + { -1, 0 } }; + +#ifndef HOST_OS_OSX +/** + * Tell if we have been started by systemd. + */ +static int +lldpd_started_by_systemd() +{ +# ifdef HOST_OS_LINUX + int fd = -1; + const char *notifysocket = getenv("NOTIFY_SOCKET"); + if (!notifysocket || !strchr("@/", notifysocket[0]) || strlen(notifysocket) < 2) + return 0; + + log_debug("main", "running with systemd, don't fork but signal ready"); + if ((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) { + log_warn("main", "unable to open systemd notification socket %s", + notifysocket); + return 0; + } + + struct sockaddr_un su = { .sun_family = AF_UNIX }; + strlcpy(su.sun_path, notifysocket, sizeof(su.sun_path)); + if (notifysocket[0] == '@') su.sun_path[0] = 0; + + char ready[] = "READY=1"; + struct iovec iov = { .iov_base = ready, .iov_len = sizeof ready - 1 }; + struct msghdr hdr = { .msg_name = &su, + .msg_namelen = + offsetof(struct sockaddr_un, sun_path) + strlen(notifysocket), + .msg_iov = &iov, + .msg_iovlen = 1 }; + unsetenv("NOTIFY_SOCKET"); + if (sendmsg(fd, &hdr, MSG_NOSIGNAL) < 0) { + log_warn("main", "unable to send notification to systemd"); + close(fd); + return 0; + } + close(fd); + return 1; +# else + return 0; +# endif +} +#endif + +#ifdef HOST_OS_LINUX +static void +version_convert(const char *sversion, unsigned iversion[], size_t n) +{ + const char *p = sversion; + char *end; + for (size_t i = 0; i < n; i++) { + iversion[i] = strtol(p, &end, 10); + if (*end != '.') break; + p = end + 1; + } +} + +static void +version_check(void) +{ + struct utsname uts; + if (uname(&uts) == -1) return; + unsigned version_min[3] = {}; + unsigned version_cur[3] = {}; + version_convert(uts.release, version_cur, 3); + version_convert(MIN_LINUX_KERNEL_VERSION, version_min, 3); + if (version_min[0] > version_cur[0] || + (version_min[0] == version_cur[0] && version_min[1] > version_cur[1]) || + (version_min[0] == version_cur[0] && version_min[1] == version_cur[1] && + version_min[2] > version_cur[2])) { + log_warnx("lldpd", "minimal kernel version required is %s, got %s", + MIN_LINUX_KERNEL_VERSION, uts.release); + log_warnx("lldpd", + "lldpd may be unable to detect bonds and bridges correctly"); +# ifndef ENABLE_OLDIES + log_warnx("lldpd", "consider recompiling with --enable-oldies option"); +# endif + } +} +#else +static void +version_check(void) +{ +} +#endif + +int +lldpd_main(int argc, char *argv[], char *envp[]) +{ + struct lldpd *cfg; + struct lldpd_chassis *lchassis; + int ch, debug = 0, use_syslog = 1, daemonize = 1; + const char *errstr; +#ifdef USE_SNMP + int snmp = 0; + const char *agentx = NULL; /* AgentX socket */ +#endif + const char *ctlname = NULL; + char *mgmtp = NULL; + char *cidp = NULL; + char *interfaces = NULL; + /* We do not want more options here. Please add them in lldpcli instead + * unless there is a very good reason. Most command-line options will + * get deprecated at some point. */ + char *popt, + opts[] = "H:vhkrdD:p:xX:m:u:4:6:I:C:p:M:P:S:iL:O:@ "; + int i, found, advertise_version = 1; +#ifdef ENABLE_LLDPMED + int lldpmed = 0, noinventory = 0; + int enable_fast_start = 1; +#endif + char *descr_override = NULL; + char *platform_override = NULL; + char *lsb_release = NULL; + const char *lldpcli = LLDPCLI_PATH; + const char *pidfile = LLDPD_PID_FILE; + int smart = 15; + int receiveonly = 0, version = 0; + int ctl; + const char *config_file = NULL; + +#ifdef ENABLE_PRIVSEP + /* Non privileged user */ + struct passwd *user; + struct group *group; + uid_t uid; + gid_t gid; +#endif + + saved_argv = argv; + +#if HAVE_SETPROCTITLE_INIT + setproctitle_init(argc, argv, envp); +#endif + + /* + * Get and parse command line options + */ + if ((popt = strchr(opts, '@')) != NULL) { + for (i = 0; protos[i].mode != 0 && *popt != '\0'; i++) + *(popt++) = protos[i].arg; + *popt = '\0'; + } + while ((ch = getopt(argc, argv, opts)) != -1) { + switch (ch) { + case 'h': + usage(); + break; + case 'v': + version++; + break; + case 'd': + if (daemonize) + daemonize = 0; + else if (use_syslog) + use_syslog = 0; + else + debug++; + break; + case 'D': + log_accept(optarg); + break; + case 'p': + pidfile = optarg; + break; + case 'r': + receiveonly = 1; + break; + case 'm': + if (mgmtp) { + fprintf(stderr, "-m can only be used once\n"); + usage(); + } + mgmtp = strdup(optarg); + break; + case 'u': + if (ctlname) { + fprintf(stderr, "-u can only be used once\n"); + usage(); + } + ctlname = optarg; + break; + case 'I': + if (interfaces) { + fprintf(stderr, "-I can only be used once\n"); + usage(); + } + interfaces = strdup(optarg); + break; + case 'C': + if (cidp) { + fprintf(stderr, "-C can only be used once\n"); + usage(); + } + cidp = strdup(optarg); + break; + case 'L': + if (strlen(optarg)) + lldpcli = optarg; + else + lldpcli = NULL; + break; + case 'k': + advertise_version = 0; + break; +#ifdef ENABLE_LLDPMED + case 'M': + lldpmed = strtonum(optarg, 1, 4, &errstr); + if (errstr) { + fprintf(stderr, + "-M requires an argument between 1 and 4\n"); + usage(); + } + break; + case 'i': + noinventory = 1; + break; +#else + case 'M': + case 'i': + fprintf(stderr, "LLDP-MED support is not built-in\n"); + usage(); + break; +#endif +#ifdef USE_SNMP + case 'x': + snmp = 1; + break; + case 'X': + if (agentx) { + fprintf(stderr, "-X can only be used once\n"); + usage(); + } + snmp = 1; + agentx = optarg; + break; +#else + case 'x': + case 'X': + fprintf(stderr, "SNMP support is not built-in\n"); + usage(); +#endif + break; + case 'S': + if (descr_override) { + fprintf(stderr, "-S can only be used once\n"); + usage(); + } + descr_override = strdup(optarg); + break; + case 'P': + if (platform_override) { + fprintf(stderr, "-P can only be used once\n"); + usage(); + } + platform_override = strdup(optarg); + break; + case 'H': + smart = strtonum(optarg, 0, + sizeof(filters) / sizeof(filters[0]), &errstr); + if (errstr) { + fprintf(stderr, + "-H requires an int between 0 and %zu\n", + sizeof(filters) / sizeof(filters[0])); + usage(); + } + break; + case 'O': + if (config_file) { + fprintf(stderr, "-O can only be used once\n"); + usage(); + } + config_file = optarg; + break; + default: + found = 0; + for (i = 0; protos[i].mode != 0; i++) { + if (ch == protos[i].arg) { + found = 1; + protos[i].enabled++; + } + } + if (!found) usage(); + } + } + + if (version) { + version_display(stdout, "lldpd", version > 1); + exit(0); + } + + if (ctlname == NULL) ctlname = LLDPD_CTL_SOCKET; + + /* Set correct smart mode */ + for (i = 0; (filters[i].a != -1) && (filters[i].a != smart); i++) + ; + if (filters[i].a == -1) { + fprintf(stderr, "Incorrect mode for -H\n"); + usage(); + } + smart = filters[i].b; + + log_init(use_syslog, debug, __progname); + tzset(); /* Get timezone info before chroot */ + if (use_syslog && daemonize) { + /* So, we use syslog and we daemonize (or we are started by + * systemd). No need to continue writing to stdout. */ + int fd; + /* coverity[resource_leak] + fd may be leaked if < 2, it's expected */ + if ((fd = open("/dev/null", O_RDWR, 0)) != -1) { + dup2(fd, STDIN_FILENO); + dup2(fd, STDOUT_FILENO); + dup2(fd, STDERR_FILENO); + if (fd > 2) close(fd); + } + } + log_debug("main", "lldpd " PACKAGE_VERSION " starting..."); + version_check(); +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + fatalx("main", "fuzzing enabled, unsafe for production"); +#endif + + /* Grab uid and gid to use for priv sep */ +#ifdef ENABLE_PRIVSEP + if ((user = getpwnam(PRIVSEP_USER)) == NULL) + fatalx("main", + "no " PRIVSEP_USER + " user for privilege separation, please create it"); + uid = user->pw_uid; + if ((group = getgrnam(PRIVSEP_GROUP)) == NULL) + fatalx("main", + "no " PRIVSEP_GROUP + " group for privilege separation, please create it"); + gid = group->gr_gid; +#endif + + /* Create and setup socket */ + int retry = 1; + log_debug("main", "creating control socket"); + while ((ctl = ctl_create(ctlname)) == -1) { + if (retry-- && errno == EADDRINUSE) { + /* Check if a daemon is really listening */ + int tfd; + log_info("main", + "unable to create control socket because it already exists"); + log_info("main", "check if another instance is running"); + if ((tfd = ctl_connect(ctlname)) != -1) { + /* Another instance is running */ + close(tfd); + log_warnx("main", + "another instance is running, please stop it"); + fatalx("main", "giving up"); + } else if (errno == ECONNREFUSED) { + /* Nobody is listening */ + log_info("main", + "old control socket is present, clean it"); + ctl_cleanup(ctlname); + continue; + } + log_warn("main", + "cannot determine if another daemon is already running"); + fatalx("main", "giving up"); + } + log_warn("main", "unable to create control socket at %s", ctlname); + fatalx("main", "giving up"); + } +#ifdef ENABLE_PRIVSEP + if (chown(ctlname, uid, gid) == -1) + log_warn("main", "unable to chown control socket"); + if (chmod(ctlname, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP) == + -1) + log_warn("main", "unable to chmod control socket"); +#endif + + /* Create associated advisory lock file */ + char *lockname = NULL; + int fd; + if (asprintf(&lockname, "%s.lock", ctlname) == -1) + fatal("main", "cannot build lock name"); + if ((fd = open(lockname, O_CREAT | O_RDWR, 0000)) == -1) + fatal("main", "cannot create lock file for control socket"); + close(fd); +#ifdef ENABLE_PRIVSEP + if (chown(lockname, uid, gid) == -1) + log_warn("main", "unable to chown control socket lock"); + if (chmod(lockname, + S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP) == -1) + log_warn("main", "unable to chmod control socket lock"); +#endif + free(lockname); + + /* Disable SIGPIPE */ + signal(SIGPIPE, SIG_IGN); + + /* Disable SIGHUP, until handlers are installed */ + signal(SIGHUP, SIG_IGN); + + /* Daemonization, unless started by systemd or launchd or debug */ +#ifndef HOST_OS_OSX + if (!lldpd_started_by_systemd() && daemonize) { + int pid; + char *spid; + log_debug("main", "going into background"); + if (daemon(0, 1) != 0) fatal("main", "failed to detach daemon"); + if ((pid = open(pidfile, O_TRUNC | O_CREAT | O_WRONLY, 0666)) == -1) + fatal("main", + "unable to open pid file " LLDPD_PID_FILE + " (or the specified one)"); + if (asprintf(&spid, "%d\n", getpid()) == -1) + fatal("main", + "unable to create pid file " LLDPD_PID_FILE + " (or the specified one)"); + if (write(pid, spid, strlen(spid)) == -1) + fatal("main", + "unable to write pid file " LLDPD_PID_FILE + " (or the specified one)"); + free(spid); + close(pid); + } +#endif + + /* Configuration with lldpcli */ + if (lldpcli) { + if (!config_file) { + log_debug("main", + "invoking lldpcli for default configuration locations"); + } else { + log_debug("main", + "invoking lldpcli for user supplied configuration location"); + } + if (lldpd_configure(use_syslog, debug, lldpcli, ctlname, config_file) == + -1) + fatal("main", "unable to spawn lldpcli"); + } + + /* Try to read system information from /etc/os-release if possible. + Fall back to lsb_release for compatibility. */ + log_debug("main", "get OS/LSB release information"); + lsb_release = lldpd_get_os_release(); + if (!lsb_release) { + lsb_release = lldpd_get_lsb_release(); + } + + log_debug("main", "initialize privilege separation"); +#ifdef ENABLE_PRIVSEP + priv_init(PRIVSEP_CHROOT, ctl, uid, gid); +#else + priv_init(); +#endif + + /* Initialization of global configuration */ + if ((cfg = (struct lldpd *)calloc(1, sizeof(struct lldpd))) == NULL) + fatal("main", NULL); + + lldpd_alloc_default_local_port(cfg); + cfg->g_ctlname = ctlname; + cfg->g_ctl = ctl; + cfg->g_config.c_mgmt_pattern = mgmtp; + cfg->g_config.c_cid_pattern = cidp; + cfg->g_config.c_iface_pattern = interfaces; + cfg->g_config.c_smart = smart; + if (lldpcli) cfg->g_config.c_paused = 1; + cfg->g_config.c_receiveonly = receiveonly; + cfg->g_config.c_tx_interval = LLDPD_TX_INTERVAL * 1000; + cfg->g_config.c_tx_hold = LLDPD_TX_HOLD; + cfg->g_config.c_ttl = cfg->g_config.c_tx_interval * cfg->g_config.c_tx_hold; + cfg->g_config.c_ttl = (cfg->g_config.c_ttl + 999) / 1000; + cfg->g_config.c_max_neighbors = LLDPD_MAX_NEIGHBORS; +#ifdef ENABLE_LLDPMED + cfg->g_config.c_enable_fast_start = enable_fast_start; + cfg->g_config.c_tx_fast_init = LLDPD_FAST_INIT; + cfg->g_config.c_tx_fast_interval = LLDPD_FAST_TX_INTERVAL; +#endif +#ifdef USE_SNMP + cfg->g_snmp = snmp; + cfg->g_snmp_agentx = agentx; +#endif /* USE_SNMP */ + cfg->g_config.c_bond_slave_src_mac_type = + LLDP_BOND_SLAVE_SRC_MAC_TYPE_LOCALLY_ADMINISTERED; + + /* Get ioctl socket */ + log_debug("main", "get an ioctl socket"); + if ((cfg->g_sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) + fatal("main", "failed to get ioctl socket"); + + /* Description */ + if (!(cfg->g_config.c_advertise_version = advertise_version) && lsb_release && + lsb_release[strlen(lsb_release) - 1] == '\n') + lsb_release[strlen(lsb_release) - 1] = '\0'; + cfg->g_lsb_release = lsb_release; + if (descr_override) cfg->g_config.c_description = descr_override; + + if (platform_override) cfg->g_config.c_platform = platform_override; + + /* Set system capabilities */ + log_debug("main", "set system capabilities"); + if ((lchassis = (struct lldpd_chassis *)calloc(1, + sizeof(struct lldpd_chassis))) == NULL) + fatal("localchassis", NULL); + cfg->g_config.c_cap_advertise = 1; + cfg->g_config.c_cap_override = 0; + lchassis->c_cap_available = + LLDP_CAP_BRIDGE | LLDP_CAP_WLAN | LLDP_CAP_ROUTER | LLDP_CAP_STATION; + cfg->g_config.c_mgmt_advertise = 1; + TAILQ_INIT(&lchassis->c_mgmt); +#ifdef ENABLE_LLDPMED + if (lldpmed > 0) { + if (lldpmed == LLDP_MED_CLASS_III) + lchassis->c_cap_available |= LLDP_CAP_TELEPHONE; + lchassis->c_med_type = lldpmed; + lchassis->c_med_cap_available = LLDP_MED_CAP_CAP | LLDP_MED_CAP_IV | + LLDP_MED_CAP_LOCATION | LLDP_MED_CAP_POLICY | LLDP_MED_CAP_MDI_PSE | + LLDP_MED_CAP_MDI_PD; + cfg->g_config.c_noinventory = noinventory; + } else + cfg->g_config.c_noinventory = 1; +#endif + + log_debug("main", "initialize protocols"); + cfg->g_protocols = protos; + for (i = 0; protos[i].mode != 0; i++) { + + /* With -ll, disable LLDP */ + if (protos[i].mode == LLDPD_MODE_LLDP) protos[i].enabled %= 3; + /* With -ccc force CDPV2, enable CDPV1 */ + if (protos[i].mode == LLDPD_MODE_CDPV1 && protos[i].enabled == 3) { + protos[i].enabled = 1; + } + /* With -cc force CDPV1, enable CDPV2 */ + if (protos[i].mode == LLDPD_MODE_CDPV2 && protos[i].enabled == 2) { + protos[i].enabled = 1; + } + + /* With -cccc disable CDPV1, enable CDPV2 */ + if (protos[i].mode == LLDPD_MODE_CDPV1 && protos[i].enabled >= 4) { + protos[i].enabled = 0; + } + + /* With -cccc disable CDPV1, enable CDPV2; -ccccc will force CDPv2 */ + if (protos[i].mode == LLDPD_MODE_CDPV2 && protos[i].enabled == 4) { + protos[i].enabled = 1; + } + + if (protos[i].enabled > 1) + log_info("main", "protocol %s enabled and forced", + protos[i].name); + else if (protos[i].enabled) + log_info("main", "protocol %s enabled", protos[i].name); + else + log_info("main", "protocol %s disabled", protos[i].name); + } + + TAILQ_INIT(&cfg->g_hardware); + TAILQ_INIT(&cfg->g_chassis); + TAILQ_INSERT_TAIL(&cfg->g_chassis, lchassis, c_entries); + lchassis->c_refcount++; /* We should always keep a reference to local chassis */ + + /* Main loop */ + log_debug("main", "start main loop"); + levent_loop(cfg); + lchassis->c_refcount--; + lldpd_exit(cfg); + free(cfg); + + return (0); +} diff --git a/src/daemon/lldpd.h b/src/daemon/lldpd.h new file mode 100644 index 0000000..2fc381b --- /dev/null +++ b/src/daemon/lldpd.h @@ -0,0 +1,429 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _LLDPD_H +#define _LLDPD_H + +#if HAVE_CONFIG_H +# include <config.h> +#endif + +#ifdef HAVE_VALGRIND_VALGRIND_H +# include <valgrind/valgrind.h> +#else +# define RUNNING_ON_VALGRIND 0 +#endif + +#include <stdlib.h> +#include <stddef.h> +#include <string.h> +#include <sys/queue.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <net/if_arp.h> +#include <netinet/if_ether.h> +#include <sys/un.h> + +#include "lldp-tlv.h" +#if defined ENABLE_CDP || defined ENABLE_FDP +# include "protocols/cdp.h" +#endif +#ifdef ENABLE_SONMP +# include "protocols/sonmp.h" +#endif +#ifdef ENABLE_EDP +# include "protocols/edp.h" +#endif + +#include "../compat/compat.h" +#include "../marshal.h" +#include "../log.h" +#include "../ctl.h" +#include "../lldpd-structs.h" + +/* We don't want to import event2/event.h. We only need those as + opaque structs. */ +struct event; +struct event_base; + +#define PROCFS_SYS_NET "/proc/sys/net/" +#define SYSFS_CLASS_NET "/sys/class/net/" +#define SYSFS_CLASS_DMI "/sys/class/dmi/id/" +#define LLDPD_TX_INTERVAL 30 +#define LLDPD_TX_HOLD 4 +#define LLDPD_TTL LLDPD_TX_INTERVAL *LLDPD_TX_HOLD +#define LLDPD_TX_MSGDELAY 1 +#define LLDPD_MAX_NEIGHBORS 32 +#define LLDPD_FAST_TX_INTERVAL 1 +#define LLDPD_FAST_INIT 4 + +#define USING_AGENTX_SUBAGENT_MODULE 1 + +#define PROTO_SEND_SIG struct lldpd *, struct lldpd_hardware * +#define PROTO_DECODE_SIG \ + struct lldpd *, char *, int, struct lldpd_hardware *, struct lldpd_chassis **, \ + struct lldpd_port ** +#define PROTO_GUESS_SIG char *, int + +#define ALIGNED_CAST(TYPE, ATTR) ((TYPE)(void *)(ATTR)) + +struct protocol { + int mode; /* > 0 mode identifier (unique per protocol) */ + int enabled; /* Is this protocol enabled? */ + const char *name; /* Name of protocol */ + char arg; /* Argument to enable this protocol */ + int (*send)(PROTO_SEND_SIG); /* How to send a frame */ + int (*decode)(PROTO_DECODE_SIG); /* How to decode a frame */ + int (*guess)(PROTO_GUESS_SIG); /* Can be NULL, use MAC address in this case */ + u_int8_t mac[3][ETHER_ADDR_LEN]; /* Destination MAC addresses used by this + protocol */ +}; + +#define SMART_HIDDEN(port) (port->p_hidden_in) + +struct lldpd; + +/* lldpd.c */ +struct lldpd_hardware *lldpd_get_hardware(struct lldpd *, char *, int); +struct lldpd_hardware *lldpd_alloc_hardware(struct lldpd *, char *, int); +void lldpd_hardware_cleanup(struct lldpd *, struct lldpd_hardware *); +struct lldpd_mgmt *lldpd_alloc_mgmt(int family, void *addr, size_t addrsize, + u_int32_t iface); +void lldpd_recv(struct lldpd *, struct lldpd_hardware *, int); +void lldpd_send(struct lldpd_hardware *); +void lldpd_loop(struct lldpd *); +int lldpd_main(int, char **, char **); +void lldpd_update_localports(struct lldpd *); +void lldpd_update_localchassis(struct lldpd *); +void lldpd_cleanup(struct lldpd *); + +/* frame.c */ +u_int16_t frame_checksum(const u_int8_t *, int, int); + +/* event.c */ +void levent_loop(struct lldpd *); +void levent_shutdown(struct lldpd *); +void levent_hardware_init(struct lldpd_hardware *); +void levent_hardware_add_fd(struct lldpd_hardware *, int); +void levent_hardware_release(struct lldpd_hardware *); +void levent_ctl_notify(char *, int, struct lldpd_port *); +void levent_send_now(struct lldpd *); +void levent_update_now(struct lldpd *); +int levent_iface_subscribe(struct lldpd *, int); +void levent_schedule_pdu(struct lldpd_hardware *); +void levent_schedule_cleanup(struct lldpd *); +int levent_make_socket_nonblocking(int); +int levent_make_socket_blocking(int); +#ifdef HOST_OS_LINUX +void levent_recv_error(int, const char *); +#endif + +/* lldp.c */ +int lldp_send_shutdown(PROTO_SEND_SIG); +int lldp_send(PROTO_SEND_SIG); +int lldp_decode(PROTO_DECODE_SIG); + +/* cdp.c */ +#ifdef ENABLE_CDP +int cdpv1_send(PROTO_SEND_SIG); +int cdpv2_send(PROTO_SEND_SIG); +int cdpv1_guess(PROTO_GUESS_SIG); +int cdpv2_guess(PROTO_GUESS_SIG); +#endif +#if defined ENABLE_CDP || defined ENABLE_FDP +int cdp_decode(PROTO_DECODE_SIG); +#endif +#ifdef ENABLE_FDP +int fdp_send(PROTO_SEND_SIG); +#endif + +#ifdef ENABLE_SONMP +/* sonmp.c */ +int sonmp_send(PROTO_SEND_SIG); +int sonmp_decode(PROTO_DECODE_SIG); +#endif + +#ifdef ENABLE_EDP +/* edp.c */ +int edp_send(PROTO_SEND_SIG); +int edp_decode(PROTO_DECODE_SIG); +#endif + +/* dmi.c */ +#ifdef ENABLE_LLDPMED +char *dmi_hw(void); +char *dmi_fw(void); +char *dmi_sn(void); +char *dmi_manuf(void); +char *dmi_model(void); +char *dmi_asset(void); +#endif + +#ifdef USE_SNMP +/* agent.c */ +void agent_shutdown(void); +void agent_init(struct lldpd *, const char *); +void agent_notify(struct lldpd_hardware *, int, struct lldpd_port *); +#endif + +#ifdef ENABLE_PRIVSEP +/* agent_priv.c */ +void agent_priv_register_domain(void); +#endif + +/* client.c */ +int client_handle_client(struct lldpd *cfg, + ssize_t (*send)(void *, int, void *, size_t), void *, enum hmsg_type type, + void *buffer, size_t n, int *); + +/* priv.c */ +#ifdef ENABLE_PRIVSEP +void priv_init(const char *, int, uid_t, gid_t); +#else +void priv_init(void); +#endif +void priv_wait(void); +void priv_ctl_cleanup(const char *ctlname); +char *priv_gethostname(void); +#ifdef HOST_OS_LINUX +int priv_open(const char *); +void asroot_open(void); +#endif +int priv_iface_init(int, char *); +int asroot_iface_init_os(int, char *, int *); +int priv_iface_multicast(const char *, const u_int8_t *, int); +int priv_iface_description(const char *, const char *); +int asroot_iface_description_os(const char *, const char *); +int priv_iface_promisc(const char *); +int asroot_iface_promisc_os(const char *); +int priv_snmp_socket(struct sockaddr_un *); + +enum priv_cmd { + PRIV_PING, + PRIV_DELETE_CTL_SOCKET, + PRIV_GET_HOSTNAME, + PRIV_OPEN, + PRIV_IFACE_INIT, + PRIV_IFACE_MULTICAST, + PRIV_IFACE_DESCRIPTION, + PRIV_IFACE_PROMISC, + PRIV_SNMP_SOCKET, +}; + +/* priv-seccomp.c */ +#if defined USE_SECCOMP && defined ENABLE_PRIVSEP +int priv_seccomp_init(int, int); +#endif + +/* privsep_io.c */ +enum priv_context { PRIV_PRIVILEGED, PRIV_UNPRIVILEGED }; +int may_read(enum priv_context, void *, size_t); +void must_read(enum priv_context, void *, size_t); +void must_write(enum priv_context, const void *, size_t); +void priv_privileged_fd(int); +void priv_unprivileged_fd(int); +int priv_fd(enum priv_context); +int receive_fd(enum priv_context); +void send_fd(enum priv_context, int); + +/* interfaces-*.c */ + +/* BPF filter to get revelant information from interfaces */ +/* LLDP: "ether proto 0x88cc and ether dst 01:80:c2:00:00:0e" */ +/* FDP: "ether dst 01:e0:52:cc:cc:cc" */ +/* CDP: "ether dst 01:00:0c:cc:cc:cc" */ +/* SONMP: "ether dst 01:00:81:00:01:00" */ +/* EDP: "ether dst 00:e0:2b:00:00:00" */ +/* For optimization purpose, we first check if the first bit of the + first byte is 1. if not, this can only be an EDP packet: + + tcpdump -dd "(ether[0] & 1 = 1 and + ((ether proto 0x88cc and (ether dst 01:80:c2:00:00:0e or + ether dst 01:80:c2:00:00:03 or + ether dst 01:80:c2:00:00:00)) or + (ether dst 01:e0:52:cc:cc:cc) or + (ether dst 01:00:0c:cc:cc:cc) or + (ether dst 01:00:81:00:01:00))) or + (ether dst 00:e0:2b:00:00:00)" +*/ + +#ifndef ETH_P_LLDP +# define ETH_P_LLDP 0x88cc +#endif +#define LLDPD_FILTER_F \ + { 0x30, 0, 0, 0x00000000 }, { 0x54, 0, 0, 0x00000001 }, { 0x15, 0, 16, 0x00000001 }, \ + { 0x28, 0, 0, 0x0000000c }, { 0x15, 0, 6, ETH_P_LLDP }, \ + { 0x20, 0, 0, 0x00000002 }, { 0x15, 2, 0, 0xc200000e }, \ + { 0x15, 1, 0, 0xc2000003 }, { 0x15, 0, 2, 0xc2000000 }, \ + { 0x28, 0, 0, 0x00000000 }, { 0x15, 12, 13, 0x00000180 }, \ + { 0x20, 0, 0, 0x00000002 }, { 0x15, 0, 2, 0x52cccccc }, \ + { 0x28, 0, 0, 0x00000000 }, { 0x15, 8, 9, 0x000001e0 }, \ + { 0x15, 1, 0, 0x0ccccccc }, { 0x15, 0, 2, 0x81000100 }, \ + { 0x28, 0, 0, 0x00000000 }, { 0x15, 4, 5, 0x00000100 }, \ + { 0x20, 0, 0, 0x00000002 }, { 0x15, 0, 3, 0x2b000000 }, \ + { 0x28, 0, 0, 0x00000000 }, { 0x15, 0, 1, 0x000000e0 }, \ + { 0x6, 0, 0, 0x00040000 }, \ + { \ + 0x6, 0, 0, 0x00000000 \ + } + +/* This function is responsible to refresh information about interfaces. It is + * OS specific but should be present for each OS. It can use the functions in + * `interfaces.c` as helper by providing a list of OS-independent interface + * devices. */ +void interfaces_update(struct lldpd *); + +/* interfaces.c */ +/* An interface cannot be both physical and (bridge or bond or vlan) */ +#define IFACE_PHYSICAL_T (1 << 0) /* Physical interface */ +#define IFACE_BRIDGE_T (1 << 1) /* Bridge interface */ +#define IFACE_BOND_T (1 << 2) /* Bond interface */ +#define IFACE_VLAN_T (1 << 3) /* VLAN interface */ +#define IFACE_WIRELESS_T (1 << 4) /* Wireless interface */ +#define IFACE_BRIDGE_VLAN_T (1 << 5) /* Bridge-aware VLAN interface */ + +#define MAX_VLAN 4096 +#define VLAN_BITMAP_LEN (MAX_VLAN / 32) +struct interfaces_device { + TAILQ_ENTRY(interfaces_device) next; + int ignore; /* Ignore this interface */ + int index; /* Index */ + char *name; /* Name */ + char *alias; /* Alias */ + char *address; /* MAC address */ + char *driver; /* Driver */ + int flags; /* Flags (IFF_*) */ + int mtu; /* MTU */ + int type; /* Type (see IFACE_*_T) */ + uint32_t vlan_bmap[VLAN_BITMAP_LEN]; /* If a VLAN, what are the VLAN ID? */ + int pvid; /* If a VLAN, what is the default VLAN? */ + struct interfaces_device *lower; /* Lower interface (for a VLAN for example) */ + struct interfaces_device *upper; /* Upper interface (for a bridge or a bond) */ + + /* The following are OS specific. Should be static (no free function) */ +#ifdef HOST_OS_LINUX + int lower_idx; /* Index to lower interface */ + int upper_idx; /* Index to upper interface */ +#endif +}; +struct interfaces_address { + TAILQ_ENTRY(interfaces_address) next; + int index; /* Index */ + int flags; /* Flags */ + struct sockaddr_storage address; /* Address */ + + /* The following are OS specific. */ + /* Nothing yet. */ +}; +TAILQ_HEAD(interfaces_device_list, interfaces_device); +TAILQ_HEAD(interfaces_address_list, interfaces_address); +void interfaces_free_device(struct interfaces_device *); +void interfaces_free_address(struct interfaces_address *); +void interfaces_free_devices(struct interfaces_device_list *); +void interfaces_free_addresses(struct interfaces_address_list *); +struct interfaces_device *interfaces_indextointerface(struct interfaces_device_list *, + int); +struct interfaces_device *interfaces_nametointerface(struct interfaces_device_list *, + const char *); + +void interfaces_helper_promisc(struct lldpd *, struct lldpd_hardware *); +void interfaces_helper_allowlist(struct lldpd *, struct interfaces_device_list *); +void interfaces_helper_chassis(struct lldpd *, struct interfaces_device_list *); +void interfaces_helper_add_hardware(struct lldpd *, struct lldpd_hardware *); +void interfaces_helper_physical(struct lldpd *, struct interfaces_device_list *, + struct lldpd_ops *, int (*init)(struct lldpd *, struct lldpd_hardware *)); +void interfaces_helper_port_name_desc(struct lldpd *, struct lldpd_hardware *, + struct interfaces_device *); +void interfaces_helper_mgmt(struct lldpd *, struct interfaces_address_list *, + struct interfaces_device_list *); +#ifdef ENABLE_DOT1 +void interfaces_helper_vlan(struct lldpd *, struct interfaces_device_list *); +#endif +int interfaces_send_helper(struct lldpd *, struct lldpd_hardware *, char *, size_t); + +void interfaces_setup_multicast(struct lldpd *, const char *, int); +int interfaces_routing_enabled(struct lldpd *); +void interfaces_cleanup(struct lldpd *); + +#ifdef HOST_OS_LINUX +/* netlink.c */ +struct interfaces_device_list *netlink_get_interfaces(struct lldpd *); +struct interfaces_address_list *netlink_get_addresses(struct lldpd *); +void netlink_cleanup(struct lldpd *); +struct lldpd_netlink; +#endif + +#ifndef HOST_OS_LINUX +/* interfaces-bpf.c */ +int ifbpf_phys_init(struct lldpd *, struct lldpd_hardware *); +#endif + +/* pattern.c */ +enum pattern_match_result { + PATTERN_MATCH_DENIED, + PATTERN_MATCH_ALLOWED, + PATTERN_MATCH_ALLOWED_EXACT +}; +enum pattern_match_result pattern_match(char *, char *, int); + +/* bitmap.c */ +void bitmap_set(uint32_t *bmap, uint16_t vlan_id); +int bitmap_isempty(uint32_t *bmap); +unsigned int bitmap_numbits(uint32_t *bmap); + +struct lldpd { + int g_sock; + struct event_base *g_base; +#ifdef USE_SNMP +#endif + + struct lldpd_config g_config; + + struct protocol *g_protocols; + int g_lastrid; + struct event *g_main_loop; + struct event *g_cleanup_timer; +#ifdef USE_SNMP + int g_snmp; + struct event *g_snmp_timeout; + void *g_snmp_fds; + const char *g_snmp_agentx; +#endif /* USE_SNMP */ + + /* Unix socket handling */ + const char *g_ctlname; + int g_ctl; + struct event *g_iface_event; /* Triggered when there is an interface change */ + struct event + *g_iface_timer_event; /* Triggered one second after last interface change */ + void (*g_iface_cb)( + struct lldpd *); /* Called when there is an interface change */ + + char *g_lsb_release; + +#ifdef HOST_OS_LINUX + struct lldpd_netlink *g_netlink; +#endif + + struct lldpd_port *g_default_local_port; +#define LOCAL_CHASSIS(cfg) ((struct lldpd_chassis *)(TAILQ_FIRST(&cfg->g_chassis))) + TAILQ_HEAD(, lldpd_chassis) g_chassis; + TAILQ_HEAD(, lldpd_hardware) g_hardware; +}; + +#endif /* _LLDPD_H */ diff --git a/src/daemon/lldpd.service.in b/src/daemon/lldpd.service.in new file mode 100644 index 0000000..80288a5 --- /dev/null +++ b/src/daemon/lldpd.service.in @@ -0,0 +1,22 @@ +[Unit] +Description=LLDP daemon +Documentation=man:lldpd(8) +After=network.target +RequiresMountsFor=@PRIVSEP_CHROOT@ + +[Service] +Type=notify +NotifyAccess=main +EnvironmentFile=-/etc/default/lldpd +EnvironmentFile=-/etc/sysconfig/lldpd +ExecStart=@sbindir@/lldpd $DAEMON_ARGS $LLDPD_OPTIONS +Restart=on-failure +PrivateTmp=yes +ProtectHome=yes +ProtectKernelTunables=no +ProtectControlGroups=yes +ProtectKernelModules=yes +#ProtectSystem=full + +[Install] +WantedBy=multi-user.target diff --git a/src/daemon/lldpd.sysusers.conf.in b/src/daemon/lldpd.sysusers.conf.in new file mode 100644 index 0000000..7cbf50a --- /dev/null +++ b/src/daemon/lldpd.sysusers.conf.in @@ -0,0 +1,6 @@ +# System user and group for lldpd +# @PRIVSEP_USER@:@PRIVSEP_GROUP@ + +# Type Name ID GECOS Home +u @PRIVSEP_USER@ - "lldpd user" @PRIVSEP_CHROOT@ +m @PRIVSEP_USER@ @PRIVSEP_GROUP@ diff --git a/src/daemon/main.c b/src/daemon/main.c new file mode 100644 index 0000000..c2d9297 --- /dev/null +++ b/src/daemon/main.c @@ -0,0 +1,17 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +#include "lldpd.h" + +/** + * @mainpage + * + * lldpd is an implementation of 802.1AB (aka LLDP). It provides an interface + * for third party clients to interact with it: querying neighbors, setting some + * TLV. This interface is included into a library whose API can be found in @ref + * liblldpctl + */ + +int +main(int argc, char **argv, char **envp) +{ + return lldpd_main(argc, argv, envp); +} diff --git a/src/daemon/netlink.c b/src/daemon/netlink.c new file mode 100644 index 0000000..32fee76 --- /dev/null +++ b/src/daemon/netlink.c @@ -0,0 +1,993 @@ +/* -*- 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. + */ + +/* Grabbing interfaces information with netlink only. */ + +#include "lldpd.h" + +#include <errno.h> +#include <sys/socket.h> +#include <netdb.h> +#include <net/if_arp.h> +#include <linux/netlink.h> +#include <linux/rtnetlink.h> +#include <linux/if_bridge.h> + +#define NETLINK_BUFFER 4096 + +struct netlink_req { + struct nlmsghdr hdr; + struct ifinfomsg ifm; + /* attribute has to be NLMSG aligned */ + struct rtattr ext_req __attribute__((aligned(NLMSG_ALIGNTO))); + __u32 ext_filter_mask; +}; + +struct lldpd_netlink { + int nl_socket_queries; + int nl_socket_changes; + int nl_socket_recv_size; + /* Cache */ + struct interfaces_device_list *devices; + struct interfaces_address_list *addresses; +}; + +/** + * Set netlink socket buffer size. + * + * This returns the effective size on success. If the provided value is 0, this + * returns the current size instead. It returns -1 on system errors and -2 if + * the size was not changed appropriately (when reaching the max). + */ +static int +netlink_socket_set_buffer_size(int s, int optname, const char *optname_str, int bufsize) +{ + socklen_t size = sizeof(int); + int got = 0; + + if (bufsize > 0 && + setsockopt(s, SOL_SOCKET, optname, &bufsize, sizeof(bufsize)) < 0) { + log_warn("netlink", "unable to set %s to '%d'", optname_str, bufsize); + return -1; + } + + /* Now read them back from kernel. + * SO_SNDBUF & SO_RCVBUF are cap-ed at sysctl `net.core.rmem_max` & + * `net.core.wmem_max`. This it the easiest [probably sanest too] + * to validate that our socket buffers were set properly. + */ + if (getsockopt(s, SOL_SOCKET, optname, &got, &size) < 0) { + log_warn("netlink", "unable to get %s", optname_str); + return -1; + } + if (bufsize > 0 && got < bufsize) { + log_warnx("netlink", + "tried to set %s to '%d' " + "but got '%d'", + optname_str, bufsize, got); + return -2; + } + + return got; +} + +/** + * Connect to netlink. + * + * Open a Netlink socket and connect to it. + * + * @param groups Which groups we want to subscribe to + * @return 0 on success, -1 otherwise + */ +static int +netlink_connect(struct lldpd *cfg, unsigned groups) +{ + int s1 = -1, s2 = -1; + struct sockaddr_nl local = { .nl_family = AF_NETLINK, + .nl_pid = 0, + .nl_groups = groups }; + + /* Open Netlink socket for subscriptions */ + log_debug("netlink", "opening netlink sockets"); + s1 = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (s1 == -1) { + log_warn("netlink", "unable to open netlink socket for changes"); + goto error; + } + if (NETLINK_SEND_BUFSIZE && + netlink_socket_set_buffer_size(s1, SO_SNDBUF, "SO_SNDBUF", + NETLINK_SEND_BUFSIZE) == -1) { + log_warn("netlink", "unable to set send buffer size"); + goto error; + } + + int rc = netlink_socket_set_buffer_size(s1, SO_RCVBUF, "SO_RCVBUF", + NETLINK_RECEIVE_BUFSIZE); + switch (rc) { + case -1: + log_warn("netlink", "unable to set receiver buffer size"); + goto error; + case -2: + /* Cannot set size */ + cfg->g_netlink->nl_socket_recv_size = 0; + break; + default: + cfg->g_netlink->nl_socket_recv_size = rc; + break; + } + if (groups && + bind(s1, (struct sockaddr *)&local, sizeof(struct sockaddr_nl)) < 0) { + log_warn("netlink", "unable to bind netlink socket"); + goto error; + } + + /* Opening Netlink socket to for queries */ + s2 = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (s2 == -1) { + log_warn("netlink", "unable to open netlink socket for queries"); + goto error; + } + cfg->g_netlink->nl_socket_changes = s1; + cfg->g_netlink->nl_socket_queries = s2; + return 0; +error: + if (s1 != -1) close(s1); + if (s2 != -1) close(s2); + return -1; +} + +/** + * Send a netlink message. + * + * The type of the message can be chosen as well the route family. The + * mesage will always be NLM_F_REQUEST | NLM_F_DUMP. + * + * @param s the netlink socket + * @param type the request type (eg RTM_GETLINK) + * @param family the rt family (eg AF_PACKET) + * @return 0 on success, -1 otherwise + */ +static int +netlink_send(int s, int type, int family, int seq) +{ + struct netlink_req req = { .hdr = { .nlmsg_len = + NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .nlmsg_type = type, + .nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP, + .nlmsg_seq = seq, + .nlmsg_pid = getpid() }, + .ifm = { .ifi_family = family } }; + struct iovec iov = { .iov_base = &req, .iov_len = req.hdr.nlmsg_len }; + struct sockaddr_nl peer = { .nl_family = AF_NETLINK }; + struct msghdr rtnl_msg = { .msg_iov = &iov, + .msg_iovlen = 1, + .msg_name = &peer, + .msg_namelen = sizeof(struct sockaddr_nl) }; + + if (family == AF_BRIDGE) { + unsigned int len = RTA_LENGTH(sizeof(__u32)); + /* request bridge vlan attributes */ + req.ext_req.rta_type = IFLA_EXT_MASK; + req.ext_req.rta_len = len; + req.ext_filter_mask = RTEXT_FILTER_BRVLAN; + req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + RTA_ALIGN(len); + iov.iov_len = req.hdr.nlmsg_len; + } + + /* Send netlink message. This is synchronous but we are guaranteed + * to not block. */ + log_debug("netlink", "sending netlink message"); + if (sendmsg(s, (struct msghdr *)&rtnl_msg, 0) == -1) { + log_warn("netlink", "unable to send netlink message"); + return -1; + } + + return 0; +} + +static void +netlink_parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len) +{ + while (RTA_OK(rta, len)) { + if ((rta->rta_type <= max) && (!tb[rta->rta_type])) + tb[rta->rta_type] = rta; + rta = RTA_NEXT(rta, len); + } +} + +/** + * Parse a `linkinfo` attributes. + * + * @param iff where to put the result + * @param rta linkinfo attribute + * @param len length of attributes + */ +static void +netlink_parse_linkinfo(struct interfaces_device *iff, struct rtattr *rta, int len) +{ + struct rtattr *link_info_attrs[IFLA_INFO_MAX + 1] = {}; + char *kind = NULL; + uint16_t vlan_id; + + netlink_parse_rtattr(link_info_attrs, IFLA_INFO_MAX, rta, len); + + if (link_info_attrs[IFLA_INFO_KIND]) { + kind = strdup(RTA_DATA(link_info_attrs[IFLA_INFO_KIND])); + if (kind) { + if (!strcmp(kind, "vlan")) { + log_debug("netlink", "interface %s is a VLAN", + iff->name); + iff->type |= IFACE_VLAN_T; + } else if (!strcmp(kind, "bridge")) { + log_debug("netlink", "interface %s is a bridge", + iff->name); + iff->type |= IFACE_BRIDGE_T; + } else if (!strcmp(kind, "bond")) { + log_debug("netlink", "interface %s is a bond", + iff->name); + iff->type |= IFACE_BOND_T; + } else if (!strcmp(kind, "team")) { + log_debug("netlink", "interface %s is a team", + iff->name); + iff->type |= IFACE_BOND_T; + } + } + } + + if (kind && !strcmp(kind, "vlan") && link_info_attrs[IFLA_INFO_DATA]) { + struct rtattr *vlan_link_info_data_attrs[IFLA_VLAN_MAX + 1] = {}; + netlink_parse_rtattr(vlan_link_info_data_attrs, IFLA_VLAN_MAX, + RTA_DATA(link_info_attrs[IFLA_INFO_DATA]), + RTA_PAYLOAD(link_info_attrs[IFLA_INFO_DATA])); + + if (vlan_link_info_data_attrs[IFLA_VLAN_ID]) { + vlan_id = *(uint16_t *)RTA_DATA( + vlan_link_info_data_attrs[IFLA_VLAN_ID]); + bitmap_set(iff->vlan_bmap, vlan_id); + log_debug("netlink", "VLAN ID for interface %s is %d", + iff->name, vlan_id); + } + } + + if (kind && !strcmp(kind, "bridge") && link_info_attrs[IFLA_INFO_DATA]) { + struct rtattr *bridge_link_info_data_attrs[IFLA_BR_MAX + 1] = {}; + netlink_parse_rtattr(bridge_link_info_data_attrs, IFLA_BR_MAX, + RTA_DATA(link_info_attrs[IFLA_INFO_DATA]), + RTA_PAYLOAD(link_info_attrs[IFLA_INFO_DATA])); + + if (bridge_link_info_data_attrs[IFLA_BR_VLAN_FILTERING] && + *(uint8_t *)RTA_DATA( + bridge_link_info_data_attrs[IFLA_BR_VLAN_FILTERING]) > 0) { + iff->type |= IFACE_BRIDGE_VLAN_T; + } + } + + free(kind); +} + +/** + * Parse a `afspec` attributes. + * + * @param iff where to put the result + * @param rta afspec attribute + * @param len length of attributes + */ +static void +netlink_parse_afspec(struct interfaces_device *iff, struct rtattr *rta, int len) +{ + while (RTA_OK(rta, len)) { + struct bridge_vlan_info *vinfo; + switch (rta->rta_type) { + case IFLA_BRIDGE_VLAN_INFO: + vinfo = RTA_DATA(rta); + log_debug("netlink", "found VLAN %d on interface %s", + vinfo->vid, iff->name ? iff->name : "(unknown)"); + + bitmap_set(iff->vlan_bmap, vinfo->vid); + if (vinfo->flags & + (BRIDGE_VLAN_INFO_PVID | BRIDGE_VLAN_INFO_UNTAGGED)) + iff->pvid = vinfo->vid; + break; + default: + log_debug("netlink", + "unknown afspec attribute type %d for iface %s", + rta->rta_type, iff->name ? iff->name : "(unknown)"); + break; + } + rta = RTA_NEXT(rta, len); + } + /* All enbridged interfaces will have VLAN 1 by default, ignore it */ + if (iff->vlan_bmap[0] == 2 && (bitmap_numbits(iff->vlan_bmap) == 1) && + iff->pvid == 1) { + log_debug("netlink", + "found only default VLAN 1 on interface %s, removing", + iff->name ? iff->name : "(unknown)"); + iff->vlan_bmap[0] = iff->pvid = 0; + } +} + +/** + * Parse a `link` netlink message. + * + * @param msg message to be parsed + * @param iff where to put the result + * return 0 if the interface is worth it, -1 otherwise + */ +static int +netlink_parse_link(struct nlmsghdr *msg, struct interfaces_device *iff) +{ + struct ifinfomsg *ifi; + struct rtattr *attribute; + int len; + ifi = NLMSG_DATA(msg); + len = msg->nlmsg_len - NLMSG_LENGTH(sizeof(struct ifinfomsg)); + + if (ifi->ifi_type != ARPHRD_ETHER) { + log_debug("netlink", "skip non Ethernet interface at index %d", + ifi->ifi_index); + return -1; + } + + iff->index = ifi->ifi_index; + iff->flags = ifi->ifi_flags; + iff->lower_idx = -1; + iff->upper_idx = -1; + + for (attribute = IFLA_RTA(ifi); RTA_OK(attribute, len); + attribute = RTA_NEXT(attribute, len)) { + switch (attribute->rta_type) { + case IFLA_IFNAME: + /* Interface name */ + iff->name = strdup(RTA_DATA(attribute)); + break; + case IFLA_IFALIAS: + /* Interface alias */ + iff->alias = strdup(RTA_DATA(attribute)); + break; + case IFLA_ADDRESS: + /* Interface MAC address */ + iff->address = malloc(RTA_PAYLOAD(attribute)); + if (iff->address) + memcpy(iff->address, RTA_DATA(attribute), + RTA_PAYLOAD(attribute)); + break; + case IFLA_LINK: + /* Index of "lower" interface */ + if (iff->lower_idx == -1) { + iff->lower_idx = *(int *)RTA_DATA(attribute); + log_debug("netlink", "attribute IFLA_LINK for %s: %d", + iff->name ? iff->name : "(unknown)", + iff->lower_idx); + } else { + log_debug("netlink", + "attribute IFLA_LINK for %s: %d (ignored)", + iff->name ? iff->name : "(unknown)", + iff->lower_idx); + } + break; + case IFLA_LINK_NETNSID: + /* Is the lower interface into another namesapce? */ + iff->lower_idx = -2; + log_debug("netlink", + "attribute IFLA_LINK_NETNSID received for %s", + iff->name ? iff->name : "(unknown)"); + break; + case IFLA_MASTER: + /* Index of master interface */ + iff->upper_idx = *(int *)RTA_DATA(attribute); + break; + case IFLA_MTU: + /* Maximum Transmission Unit */ + iff->mtu = *(int *)RTA_DATA(attribute); + break; + case IFLA_LINKINFO: + netlink_parse_linkinfo(iff, RTA_DATA(attribute), + RTA_PAYLOAD(attribute)); + break; + case IFLA_AF_SPEC: + if (ifi->ifi_family != AF_BRIDGE) break; + netlink_parse_afspec(iff, RTA_DATA(attribute), + RTA_PAYLOAD(attribute)); + break; + default: + log_debug("netlink", + "unhandled link attribute type %d for iface %s", + attribute->rta_type, iff->name ? iff->name : "(unknown)"); + break; + } + } + if (!iff->name || !iff->address) { + log_debug("netlink", + "interface %d does not have a name or an address, skip", + iff->index); + return -1; + } + if (iff->upper_idx == -1) { + /* No upper interface, we cannot be enslaved. We need to clear + * the flag because the appropriate information may come later + * and we don't want to miss it. */ + iff->flags &= ~IFF_SLAVE; + } + if (iff->lower_idx == -2) iff->lower_idx = -1; + + if (ifi->ifi_family == AF_BRIDGE && msg->nlmsg_type == RTM_DELLINK && + iff->upper_idx != -1) { + log_debug("netlink", "removal of %s from bridge %d", iff->name, + iff->upper_idx); + msg->nlmsg_type = RTM_NEWLINK; + iff->upper_idx = -1; + } + + log_debug("netlink", "parsed link %d (%s, flags: %d)", iff->index, iff->name, + iff->flags); + return 0; +} + +/** + * Parse a `address` netlink message. + * + * @param msg message to be parsed + * @param ifa where to put the result + * return 0 if the address is worth it, -1 otherwise + */ +static int +netlink_parse_address(struct nlmsghdr *msg, struct interfaces_address *ifa) +{ + struct ifaddrmsg *ifi; + struct rtattr *attribute; + int len; + ifi = NLMSG_DATA(msg); + len = msg->nlmsg_len - NLMSG_LENGTH(sizeof(struct ifaddrmsg)); + + ifa->index = ifi->ifa_index; + ifa->flags = ifi->ifa_flags; + switch (ifi->ifa_family) { + case AF_INET: + case AF_INET6: + break; + default: + log_debug("netlink", "got a non IP address on if %d (family: %d)", + ifa->index, ifi->ifa_family); + return -1; + } + + for (attribute = IFA_RTA(ifi); RTA_OK(attribute, len); + attribute = RTA_NEXT(attribute, len)) { + switch (attribute->rta_type) { + case IFA_ADDRESS: + /* Address */ + if (ifi->ifa_family == AF_INET) { + struct sockaddr_in ip; + memset(&ip, 0, sizeof(struct sockaddr_in)); + ip.sin_family = AF_INET; + memcpy(&ip.sin_addr, RTA_DATA(attribute), + sizeof(struct in_addr)); + memcpy(&ifa->address, &ip, sizeof(struct sockaddr_in)); + } else { + struct sockaddr_in6 ip6; + memset(&ip6, 0, sizeof(struct sockaddr_in6)); + ip6.sin6_family = AF_INET6; + memcpy(&ip6.sin6_addr, RTA_DATA(attribute), + sizeof(struct in6_addr)); + memcpy(&ifa->address, &ip6, + sizeof(struct sockaddr_in6)); + } + break; + default: + log_debug("netlink", + "unhandled address attribute type %d for iface %d", + attribute->rta_type, ifa->index); + break; + } + } + if (ifa->address.ss_family == AF_UNSPEC) { + log_debug("netlink", "no IP for interface %d", ifa->index); + return -1; + } + return 0; +} + +/** + * Merge an old interface with a new one. + * + * Some properties may be absent in the new interface that should be copied over + * from the old one. + */ +static void +netlink_merge(struct interfaces_device *old, struct interfaces_device *new) +{ + if (new->alias == NULL) { + new->alias = old->alias; + old->alias = NULL; + } + if (new->address == NULL) { + new->address = old->address; + old->address = NULL; + } + if (new->mtu == 0) new->mtu = old->mtu; + if (new->type == 0) new->type = old->type; + + if (bitmap_isempty(new->vlan_bmap) && new->type == IFACE_VLAN_T) + memcpy((void *)new->vlan_bmap, (void *)old->vlan_bmap, + sizeof(uint32_t) * VLAN_BITMAP_LEN); + + /* It's not possible for lower link to change */ + new->lower_idx = old->lower_idx; +} + +/** + * Receive netlink answer from the kernel. + * + * @param ifs list to store interface list or NULL if we don't + * @param ifas list to store address list or NULL if we don't + * @return 0 on success, -1 on error + */ +static int +netlink_recv(struct lldpd *cfg, int s, struct interfaces_device_list *ifs, + struct interfaces_address_list *ifas) +{ + int end = 0, ret = 0, flags, retry = 0; + struct iovec iov; + int link_update = 0; + + struct interfaces_device *ifdold; + struct interfaces_device *ifdnew; + struct interfaces_address *ifaold; + struct interfaces_address *ifanew; + char addr[INET6_ADDRSTRLEN + 1]; + + iov.iov_len = NETLINK_BUFFER; + iov.iov_base = malloc(iov.iov_len); + if (!iov.iov_base) { + log_warn("netlink", "not enough memory"); + return -1; + } + + while (!end) { + ssize_t len; + struct nlmsghdr *msg; + struct sockaddr_nl peer = { .nl_family = AF_NETLINK }; + struct msghdr rtnl_reply = { .msg_iov = &iov, + .msg_iovlen = 1, + .msg_name = &peer, + .msg_namelen = sizeof(struct sockaddr_nl) }; + flags = MSG_PEEK | MSG_TRUNC; + retry: + len = recvmsg(s, &rtnl_reply, flags); + if (len == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + if (retry++ == 0) { + levent_recv_error(s, "netlink socket"); + goto retry; + } + log_warnx("netlink", + "should have received something, but didn't"); + ret = 0; + goto out; + } + int rsize = cfg->g_netlink->nl_socket_recv_size; + if (errno == ENOBUFS && rsize > 0 && + rsize < NETLINK_MAX_RECEIVE_BUFSIZE && + s == cfg->g_netlink->nl_socket_changes) { + /* Try to increase buffer size, only for the + * socket used to receive changes */ + rsize *= 2; + if (rsize > NETLINK_MAX_RECEIVE_BUFSIZE) { + rsize = NETLINK_MAX_RECEIVE_BUFSIZE; + } + int rc = netlink_socket_set_buffer_size(s, SO_RCVBUF, + "SO_RCVBUF", rsize); + if (rc < 0) + cfg->g_netlink->nl_socket_recv_size = 0; + else + cfg->g_netlink->nl_socket_recv_size = rsize; + if (rc > 0 || rc == -2) { + log_info("netlink", + "netlink receive buffer too small, retry with larger one (%d)", + rsize); + flags = 0; + goto retry; + } + } + log_warn("netlink", "unable to receive netlink answer"); + ret = -1; + goto out; + } + if (!len) { + ret = 0; + goto out; + } + + if (iov.iov_len < len || (rtnl_reply.msg_flags & MSG_TRUNC)) { + void *tmp; + + /* Provided buffer is not large enough, enlarge it + * to size of len (which should be total length of the message) + * and try again. */ + iov.iov_len = len; + tmp = realloc(iov.iov_base, iov.iov_len); + if (!tmp) { + log_warn("netlink", "not enough memory"); + ret = -1; + goto out; + } + log_debug("netlink", "enlarge message size to %zu bytes", len); + iov.iov_base = tmp; + flags = 0; + goto retry; + } + + if (flags != 0) { + /* Buffer is big enough, do the actual reading */ + flags = 0; + goto retry; + } + + for (msg = (struct nlmsghdr *)(void *)(iov.iov_base); + NLMSG_OK(msg, len); msg = NLMSG_NEXT(msg, len)) { + if (!(msg->nlmsg_flags & NLM_F_MULTI)) end = 1; + switch (msg->nlmsg_type) { + case NLMSG_DONE: + log_debug("netlink", "received done message"); + end = 1; + break; + case RTM_NEWLINK: + case RTM_DELLINK: + if (!ifs) break; + log_debug("netlink", "received link information"); + ifdnew = calloc(1, sizeof(struct interfaces_device)); + if (ifdnew == NULL) { + log_warn("netlink", + "not enough memory for another interface, give up what we have"); + goto end; + } + if (netlink_parse_link(msg, ifdnew) == 0) { + /* We need to find if we already have this + * interface */ + TAILQ_FOREACH (ifdold, ifs, next) { + if (ifdold->index == ifdnew->index) + break; + } + + if (msg->nlmsg_type == RTM_NEWLINK) { + if (ifdold == NULL) { + log_debug("netlink", + "interface %s is new", + ifdnew->name); + TAILQ_INSERT_TAIL(ifs, ifdnew, + next); + } else { + log_debug("netlink", + "interface %s/%s is updated", + ifdold->name, ifdnew->name); + netlink_merge(ifdold, ifdnew); + TAILQ_INSERT_AFTER(ifs, ifdold, + ifdnew, next); + TAILQ_REMOVE(ifs, ifdold, next); + interfaces_free_device(ifdold); + } + } else { + if (ifdold == NULL) { + log_warnx("netlink", + "removal request for %s, but no knowledge of it", + ifdnew->name); + } else { + log_debug("netlink", + "interface %s is to be removed", + ifdold->name); + TAILQ_REMOVE(ifs, ifdold, next); + interfaces_free_device(ifdold); + } + interfaces_free_device(ifdnew); + } + link_update = 1; + } else { + interfaces_free_device(ifdnew); + } + break; + case RTM_NEWADDR: + case RTM_DELADDR: + if (!ifas) break; + log_debug("netlink", "received address information"); + ifanew = calloc(1, sizeof(struct interfaces_address)); + if (ifanew == NULL) { + log_warn("netlink", + "not enough memory for another address, give what we have"); + goto end; + } + if (netlink_parse_address(msg, ifanew) == 0) { + if (ifanew->address.ss_family == AF_INET6 && + ifanew->flags & IFA_F_TEMPORARY) { + interfaces_free_address(ifanew); + break; + } + TAILQ_FOREACH (ifaold, ifas, next) { + if ((ifaold->index == ifanew->index) && + !memcmp(&ifaold->address, + &ifanew->address, + sizeof(ifaold->address))) + break; + } + if (getnameinfo( + (struct sockaddr *)&ifanew->address, + sizeof(ifanew->address), addr, + sizeof(addr), NULL, 0, + NI_NUMERICHOST) != 0) { + strlcpy(addr, "(unknown)", + sizeof(addr)); + } + + if (msg->nlmsg_type == RTM_NEWADDR) { + if (ifaold == NULL) { + log_debug("netlink", + "new address %s%%%d", addr, + ifanew->index); + TAILQ_INSERT_TAIL(ifas, ifanew, + next); + } else { + log_debug("netlink", + "updated address %s%%%d", + addr, ifaold->index); + TAILQ_INSERT_AFTER(ifas, ifaold, + ifanew, next); + TAILQ_REMOVE(ifas, ifaold, + next); + interfaces_free_address(ifaold); + } + } else { + if (ifaold == NULL) { + log_info("netlink", + "removal request for address of %s%%%d, but no knowledge of it", + addr, ifanew->index); + } else { + log_debug("netlink", + "address %s%%%d is to be removed", + addr, ifaold->index); + TAILQ_REMOVE(ifas, ifaold, + next); + interfaces_free_address(ifaold); + } + interfaces_free_address(ifanew); + } + } else { + interfaces_free_address(ifanew); + } + break; + default: + log_debug("netlink", + "received unhandled message type %d (len: %d)", + msg->nlmsg_type, msg->nlmsg_len); + } + } + } +end: + if (link_update) { + /* Fill out lower/upper */ + struct interfaces_device *iface1, *iface2; + TAILQ_FOREACH (iface1, ifs, next) { + if (iface1->upper_idx != -1 && + iface1->upper_idx != iface1->index) { + TAILQ_FOREACH (iface2, ifs, next) { + if (iface1->upper_idx == iface2->index) { + log_debug("netlink", + "upper interface for %s is %s", + iface1->name, iface2->name); + iface1->upper = iface2; + break; + } + } + if (iface2 == NULL) iface1->upper = NULL; + } else { + iface1->upper = NULL; + } + if (iface1->lower_idx != -1 && + iface1->lower_idx != iface1->index) { + TAILQ_FOREACH (iface2, ifs, next) { + if (iface1->lower_idx == iface2->index) { + /* Workaround a bug introduced + * in Linux 4.1: a pair of veth + * will be lower interface of + * each other. Do not modify + * index as if one of them is + * updated, we will loose the + * information about the + * loop. */ + if (iface2->lower_idx == + iface1->index) { + iface1->lower = NULL; + log_debug("netlink", + "link loop detected between %s(%d) and %s(%d)", + iface1->name, iface1->index, + iface2->name, + iface2->index); + } else { + log_debug("netlink", + "lower interface for %s is %s", + iface1->name, iface2->name); + iface1->lower = iface2; + } + break; + } + } + } else { + iface1->lower = NULL; + } + } + } + +out: + free(iov.iov_base); + return ret; +} + +static int +netlink_group_mask(int group) +{ + return group ? (1 << (group - 1)) : 0; +} + +/** + * Subscribe to link changes. + * + * @return 0 on success, -1 otherwise + */ +static int +netlink_subscribe_changes(struct lldpd *cfg) +{ + unsigned int groups; + + log_debug("netlink", "listening on interface changes"); + + groups = netlink_group_mask(RTNLGRP_LINK) | + netlink_group_mask(RTNLGRP_IPV4_IFADDR) | + netlink_group_mask(RTNLGRP_IPV6_IFADDR); + + return netlink_connect(cfg, groups); +} + +/** + * Receive changes from netlink */ +static void +netlink_change_cb(struct lldpd *cfg) +{ + if (cfg->g_netlink == NULL) return; + netlink_recv(cfg, cfg->g_netlink->nl_socket_changes, cfg->g_netlink->devices, + cfg->g_netlink->addresses); +} + +/** + * Initialize netlink subsystem. + * + * This can be called several times but will have effect only the first time. + * + * @return 0 on success, -1 otherwise + */ +static int +netlink_initialize(struct lldpd *cfg) +{ +#ifdef ENABLE_DOT1 + struct interfaces_device *iff; +#endif + + if (cfg->g_netlink) return 0; + + log_debug("netlink", "initialize netlink subsystem"); + if ((cfg->g_netlink = calloc(sizeof(struct lldpd_netlink), 1)) == NULL) { + log_warn("netlink", "unable to allocate memory for netlink subsystem"); + goto end; + } + + /* Connect to netlink (by requesting to get notified on updates) and + * request updated information right now */ + if (netlink_subscribe_changes(cfg) == -1) goto end; + + struct interfaces_address_list *ifaddrs = cfg->g_netlink->addresses = + malloc(sizeof(struct interfaces_address_list)); + if (ifaddrs == NULL) { + log_warn("netlink", "not enough memory for address list"); + goto end; + } + TAILQ_INIT(ifaddrs); + + struct interfaces_device_list *ifs = cfg->g_netlink->devices = + malloc(sizeof(struct interfaces_device_list)); + if (ifs == NULL) { + log_warn("netlink", "not enough memory for interface list"); + goto end; + } + TAILQ_INIT(ifs); + + if (netlink_send(cfg->g_netlink->nl_socket_queries, RTM_GETADDR, AF_UNSPEC, + 1) == -1) + goto end; + netlink_recv(cfg, cfg->g_netlink->nl_socket_queries, NULL, ifaddrs); + if (netlink_send(cfg->g_netlink->nl_socket_queries, RTM_GETLINK, AF_PACKET, + 2) == -1) + goto end; + netlink_recv(cfg, cfg->g_netlink->nl_socket_queries, ifs, NULL); +#ifdef ENABLE_DOT1 + /* If we have a bridge, search for VLAN-aware bridges */ + TAILQ_FOREACH (iff, ifs, next) { + if (iff->type & IFACE_BRIDGE_T) { + log_debug("netlink", + "interface %s is a bridge, check for VLANs", iff->name); + if (netlink_send(cfg->g_netlink->nl_socket_queries, RTM_GETLINK, + AF_BRIDGE, 3) == -1) + goto end; + netlink_recv(cfg, cfg->g_netlink->nl_socket_queries, ifs, NULL); + break; + } + } +#endif + + /* Listen to any future change */ + cfg->g_iface_cb = netlink_change_cb; + if (levent_iface_subscribe(cfg, cfg->g_netlink->nl_socket_changes) == -1) { + goto end; + } + + return 0; +end: + netlink_cleanup(cfg); + return -1; +} + +/** + * Cleanup netlink subsystem. + */ +void +netlink_cleanup(struct lldpd *cfg) +{ + if (cfg->g_netlink == NULL) return; + if (cfg->g_netlink->nl_socket_changes != -1) + close(cfg->g_netlink->nl_socket_changes); + if (cfg->g_netlink->nl_socket_queries != -1) + close(cfg->g_netlink->nl_socket_queries); + interfaces_free_devices(cfg->g_netlink->devices); + interfaces_free_addresses(cfg->g_netlink->addresses); + + free(cfg->g_netlink); + cfg->g_netlink = NULL; +} + +/** + * Receive the list of interfaces. + * + * @return a list of interfaces. + */ +struct interfaces_device_list * +netlink_get_interfaces(struct lldpd *cfg) +{ + if (netlink_initialize(cfg) == -1) return NULL; + struct interfaces_device *ifd; + TAILQ_FOREACH (ifd, cfg->g_netlink->devices, next) { + ifd->ignore = 0; + } + return cfg->g_netlink->devices; +} + +/** + * Receive the list of addresses. + * + * @return a list of addresses. + */ +struct interfaces_address_list * +netlink_get_addresses(struct lldpd *cfg) +{ + if (netlink_initialize(cfg) == -1) return NULL; + return cfg->g_netlink->addresses; +} diff --git a/src/daemon/pattern.c b/src/daemon/pattern.c new file mode 100644 index 0000000..0f9885d --- /dev/null +++ b/src/daemon/pattern.c @@ -0,0 +1,80 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "lldpd.h" + +#include <string.h> +#include <fnmatch.h> + +/** + * Match a list of patterns. + * + * @param string String to match against the list of patterns + * @param patterns List of comma separated patterns. A pattern may + * begin by `!` to negate it. In this case, it is + * denied. A pattern may begin with `!!`. In this + * case, it is allowed back. Each pattern will then be + * matched against `fnmatch()` function. + * @param found Value to return if the pattern isn't found. Should be either + * PATTERN_MATCH_DENIED or PATTERN_MACTH_DENIED. + * + * If a pattern is found matching and denied at the same time, it + * will be denied. If it is both allowed and denied, it + * will be allowed. + * + * @return PATTERN_MATCH_DENIED if the string matches a denied pattern which is not + * allowed or if the pattern wasn't found and `found` was set to + * PATTERN_MATCH_DENIED. Otherwise, return PATTERN_MATCH_ALLOWED unless the + * interface match is exact, in this case return PATTERN_MATCH_ALLOWED_EXACT. + */ +enum pattern_match_result +pattern_match(char *string, char *patterns, int found) +{ + char *pattern; + int denied = 0; + found = found ? PATTERN_MATCH_ALLOWED : PATTERN_MATCH_DENIED; + + if ((patterns = strdup(patterns)) == NULL) { + log_warnx("interfaces", "unable to allocate memory"); + return PATTERN_MATCH_DENIED; + } + + for (pattern = strtok(patterns, ","); pattern != NULL; + pattern = strtok(NULL, ",")) { + if ((pattern[0] == '!') && (pattern[1] == '!') && + (fnmatch(pattern + 2, string, 0) == 0)) { + /* Allowed. No need to search further. */ + found = (strcmp(pattern + 2, string)) ? + PATTERN_MATCH_ALLOWED : + PATTERN_MATCH_ALLOWED_EXACT; + break; + } + if ((pattern[0] == '!') && (fnmatch(pattern + 1, string, 0) == 0)) { + denied = 1; + found = PATTERN_MATCH_DENIED; + } else if (!denied && fnmatch(pattern, string, 0) == 0) { + if (!strcmp(pattern, string)) { + found = PATTERN_MATCH_ALLOWED_EXACT; + } else if (found < 2) { + found = PATTERN_MATCH_ALLOWED; + } + } + } + + free(patterns); + return found; +} diff --git a/src/daemon/priv-bsd.c b/src/daemon/priv-bsd.c new file mode 100644 index 0000000..61f332a --- /dev/null +++ b/src/daemon/priv-bsd.c @@ -0,0 +1,202 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "lldpd.h" + +#include <unistd.h> +#include <net/bpf.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> + +int +asroot_iface_init_os(int ifindex, char *name, int *fd) +{ + int enable, required, rc; + struct bpf_insn filter[] = { LLDPD_FILTER_F }; + struct ifreq ifr = { .ifr_name = {} }; + struct bpf_program fprog = { .bf_insns = filter, + .bf_len = sizeof(filter) / sizeof(struct bpf_insn) }; + +#ifndef HOST_OS_SOLARIS + int n = 0; + char dev[20]; + do { + snprintf(dev, sizeof(dev), "/dev/bpf%d", n++); + *fd = open(dev, O_RDWR); + } while (*fd < 0 && errno == EBUSY); +#else + *fd = open("/dev/bpf", O_RDWR); +#endif + if (*fd < 0) { + rc = errno; + log_warn("privsep", "unable to find a free BPF"); + return rc; + } + + /* Set buffer size */ + required = ETHER_MAX_LEN + BPF_WORDALIGN(sizeof(struct bpf_hdr)); + if (ioctl(*fd, BIOCSBLEN, (caddr_t)&required) < 0) { + rc = errno; + log_warn("privsep", "unable to set receive buffer size for BPF on %s", + name); + return rc; + } + + /* Bind the interface to BPF device */ + strlcpy(ifr.ifr_name, name, IFNAMSIZ); + if (ioctl(*fd, BIOCSETIF, (caddr_t)&ifr) < 0) { + rc = errno; + log_warn("privsep", "failed to bind interface %s to BPF", name); + return rc; + } + + /* Disable buffering */ + enable = 1; + if (ioctl(*fd, BIOCIMMEDIATE, (caddr_t)&enable) < 0) { + rc = errno; + log_warn("privsep", "unable to disable buffering for %s", name); + return rc; + } + + /* Let us write the MAC address (raw packet mode) */ + enable = 1; + if (ioctl(*fd, BIOCSHDRCMPLT, (caddr_t)&enable) < 0) { + rc = errno; + log_warn("privsep", "unable to set the `header complete` flag for %s", + name); + return rc; + } + + /* Don't see sent packets */ +#ifdef HOST_OS_OPENBSD + enable = BPF_DIRECTION_OUT; + if (ioctl(*fd, BIOCSDIRFILT, (caddr_t)&enable) < 0) +#else + enable = 0; + if (ioctl(*fd, BIOCSSEESENT, (caddr_t)&enable) < 0) +#endif + { + rc = errno; + log_warn("privsep", + "unable to set packet direction for BPF filter on %s", name); + return rc; + } + + /* Install read filter */ + if (ioctl(*fd, BIOCSETF, (caddr_t)&fprog) < 0) { + rc = errno; + log_warn("privsep", "unable to setup BPF filter for %s", name); + return rc; + } +#ifdef BIOCSETWF + /* Install write filter (optional) */ + if (ioctl(*fd, BIOCSETWF, (caddr_t)&fprog) < 0) { + rc = errno; + log_info("privsep", "unable to setup write BPF filter for %s", name); + return rc; + } +#endif + +#ifdef BIOCLOCK + /* Lock interface, but first make it non blocking since we cannot do + * this later */ + levent_make_socket_nonblocking(*fd); + if (ioctl(*fd, BIOCLOCK, (caddr_t)&enable) < 0) { + rc = errno; + log_info("privsep", "unable to lock BPF interface %s", name); + return rc; + } +#endif + return 0; +} + +int +asroot_iface_description_os(const char *name, const char *description) +{ +#ifdef IFDESCRSIZE +# if defined HOST_OS_FREEBSD || defined HOST_OS_OPENBSD + char descr[IFDESCRSIZE]; + int rc, sock = -1; +# if defined HOST_OS_FREEBSD + struct ifreq ifr = { .ifr_buffer = { .buffer = descr, .length = IFDESCRSIZE } }; +# else + struct ifreq ifr = { .ifr_data = (caddr_t)descr }; +# endif + strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == 1) { + rc = errno; + log_warnx("privsep", "unable to open inet socket"); + return rc; + } + if (strlen(description) == 0) { + /* No neighbor, try to append "was" to the current description */ + if (ioctl(sock, SIOCGIFDESCR, (caddr_t)&ifr) < 0) { + rc = errno; + log_warnx("privsep", "unable to get description of %s", name); + close(sock); + return rc; + } + if (strncmp(descr, "lldpd: ", 7) == 0) { + if (strncmp(descr + 7, "was ", 4) == 0) { + /* Already has an old neighbor */ + close(sock); + return 0; + } else { + /* Append was */ + memmove(descr + 11, descr + 7, sizeof(descr) - 11); + memcpy(descr, "lldpd: was ", 11); + } + } else { + /* No description, no neighbor */ + strlcpy(descr, "lldpd: no neighbor", sizeof(descr)); + } + } else + snprintf(descr, sizeof(descr), "lldpd: connected to %s", description); +# if defined HOST_OS_FREEBSD + ift.ifr_buffer.length = strlen(descr); +# endif + if (ioctl(sock, SIOCSIFDESCR, (caddr_t)&ifr) < 0) { + rc = errno; + log_warnx("privsep", "unable to set description of %s", name); + close(sock); + return rc; + } + close(sock); + return 0; +# endif +#endif /* IFDESCRSIZE */ + static int once = 0; + if (!once) { + log_warnx("privsep", "cannot set interface description for this OS"); + once = 1; + } + return 0; +} + +int +asroot_iface_promisc_os(const char *name) +{ + /* The promiscuous mode can be set when setting BPF + (BIOCPROMISC). Unfortunately, the interface is locked down and we + cannot change that without reopening a new socket. Let's do nothing + for now. */ + return 0; +} diff --git a/src/daemon/priv-linux.c b/src/daemon/priv-linux.c new file mode 100644 index 0000000..9a6f90a --- /dev/null +++ b/src/daemon/priv-linux.c @@ -0,0 +1,337 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "lldpd.h" + +#include <unistd.h> +#include <inttypes.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <limits.h> +#include <fcntl.h> +#include <errno.h> +#include <regex.h> +#include <sys/ioctl.h> +#include <netpacket/packet.h> /* For sockaddr_ll */ +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wdocumentation" +#endif +#include <linux/filter.h> /* For BPF filtering */ +#include <linux/sockios.h> +#include <linux/if_ether.h> +#include <linux/ethtool.h> +#if defined(__clang__) +# pragma clang diagnostic pop +#endif + +/* Defined in linux/pkt_sched.h */ +#define TC_PRIO_CONTROL 7 +/* Defined in sysfs/libsysfs.h */ +#define SYSFS_PATH_MAX 256 + +/* Proxy for open */ +int +priv_open(const char *file) +{ + int len, rc; + enum priv_cmd cmd = PRIV_OPEN; + must_write(PRIV_UNPRIVILEGED, &cmd, sizeof(enum priv_cmd)); + len = strlen(file); + must_write(PRIV_UNPRIVILEGED, &len, sizeof(int)); + must_write(PRIV_UNPRIVILEGED, file, len); + priv_wait(); + must_read(PRIV_UNPRIVILEGED, &rc, sizeof(int)); + if (rc == -1) return rc; + return receive_fd(PRIV_UNPRIVILEGED); +} + +void +asroot_open() +{ + const char *authorized[] = { PROCFS_SYS_NET "ipv4/ip_forward", + PROCFS_SYS_NET "ipv6/conf/all/forwarding", + "/proc/net/bonding/[^.][^/]*", "/proc/self/net/bonding/[^.][^/]*", +#ifdef ENABLE_OLDIES + SYSFS_CLASS_NET "[^.][^/]*/brforward", + SYSFS_CLASS_NET "[^.][^/]*/brport", + SYSFS_CLASS_NET "[^.][^/]*/brif/[^.][^/]*/port_no", +#endif + SYSFS_CLASS_DMI "product_version", SYSFS_CLASS_DMI "product_serial", + SYSFS_CLASS_DMI "product_name", SYSFS_CLASS_DMI "bios_version", + SYSFS_CLASS_DMI "sys_vendor", SYSFS_CLASS_DMI "chassis_asset_tag", + NULL }; + const char **f; + char *file; + int fd, len, rc; + regex_t preg; + + must_read(PRIV_PRIVILEGED, &len, sizeof(len)); + if (len < 0 || len > PATH_MAX) fatalx("privsep", "too large value requested"); + if ((file = (char *)malloc(len + 1)) == NULL) fatal("privsep", NULL); + must_read(PRIV_PRIVILEGED, file, len); + file[len] = '\0'; + + for (f = authorized; *f != NULL; f++) { + if (regcomp(&preg, *f, REG_NOSUB) != 0) /* Should not happen */ + fatal("privsep", "unable to compile a regex"); + if (regexec(&preg, file, 0, NULL, 0) == 0) { + regfree(&preg); + break; + } + regfree(&preg); + } + if (*f == NULL) { + log_warnx("privsep", "not authorized to open %s", file); + rc = -1; + must_write(PRIV_PRIVILEGED, &rc, sizeof(int)); + free(file); + return; + } + if ((fd = open(file, O_RDONLY)) == -1) { + rc = -1; + must_write(PRIV_PRIVILEGED, &rc, sizeof(int)); + free(file); + return; + } + free(file); + must_write(PRIV_PRIVILEGED, &fd, sizeof(int)); + send_fd(PRIV_PRIVILEGED, fd); + close(fd); +} + +/* Quirks needed by some additional interfaces. Currently, this is limited to + * disabling LLDP firmware for i40e. */ +static void +asroot_iface_init_quirks(int ifindex, char *name) +{ + int s = -1; + int fd = -1; + + /* Check driver. */ + struct ethtool_drvinfo ethc = { .cmd = ETHTOOL_GDRVINFO }; + struct ifreq ifr = { .ifr_data = (caddr_t)ðc }; + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { + log_warn("privsep", "unable to open a socket"); + goto end; + } + strlcpy(ifr.ifr_name, name, IFNAMSIZ); + if (ioctl(s, SIOCETHTOOL, &ifr) != 0 || + strncmp("i40e", ethc.driver, sizeof(ethc.driver))) { + /* Not i40e */ + goto end; + } + log_info("interfaces", + "i40e driver detected for %s, disabling LLDP in firmware", name); + + /* We assume debugfs is mounted. Otherwise, we would need to check if it + * is mounted, then unshare a new mount namespace, mount it, issues the + * command, leave the namespace. Let's see if there is such a need. */ + + /* Alternative is to use ethtool (ethtool --set-priv-flags ens5f0 + * disable-fw-lldp on). However, this requires a recent firmware (from + * i40e_ethtool.c): + * + * If the driver detected FW LLDP was disabled on init, this flag could + * be set, however we do not support _changing_ the flag: + * - on XL710 if NPAR is enabled or FW API version < 1.7 + * - on X722 with FW API version < 1.6 + */ + + char command[] = "lldp stop"; + char sysfs_path[SYSFS_PATH_MAX + 1]; + if (snprintf(sysfs_path, SYSFS_PATH_MAX, "/sys/kernel/debug/i40e/%.*s/command", + (int)sizeof(ethc.bus_info), ethc.bus_info) >= SYSFS_PATH_MAX) { + log_warnx("interfaces", "path truncated"); + goto end; + } + if ((fd = open(sysfs_path, O_WRONLY)) == -1) { + if (errno == ENOENT) { + log_info("interfaces", + "%s does not exist, " + "cannot disable LLDP in firmware for %s", + sysfs_path, name); + goto end; + } + log_warn("interfaces", + "cannot open %s to disable LLDP in firmware for %s", sysfs_path, + name); + goto end; + } + if (write(fd, command, sizeof(command) - 1) == -1) { + log_warn("interfaces", "cannot disable LLDP in firmware for %s", name); + goto end; + } +end: + if (s != -1) close(s); + if (fd != -1) close(fd); +} + +int +asroot_iface_init_os(int ifindex, char *name, int *fd) +{ + int rc; + /* Open listening socket to receive/send frames */ + if ((*fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) < 0) { + rc = errno; + return rc; + } + + struct sockaddr_ll sa = { .sll_family = AF_PACKET, .sll_ifindex = ifindex }; + if (bind(*fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) { + rc = errno; + log_warn("privsep", "unable to bind to raw socket for interface %s", + name); + return rc; + } + + /* Set filter */ + log_debug("privsep", "set BPF filter for %s", name); + static struct sock_filter lldpd_filter_f[] = { LLDPD_FILTER_F }; + struct sock_fprog prog = { .filter = lldpd_filter_f, + .len = sizeof(lldpd_filter_f) / sizeof(struct sock_filter) }; + if (setsockopt(*fd, SOL_SOCKET, SO_ATTACH_FILTER, &prog, sizeof(prog)) < 0) { + rc = errno; + log_warn("privsep", "unable to change filter for %s", name); + return rc; + } + + /* Set priority to TC_PRIO_CONTROL for ice Intel cards. See #444. */ + int prio = TC_PRIO_CONTROL; + if (setsockopt(*fd, SOL_SOCKET, SO_PRIORITY, &prio, sizeof(prio)) < 0) { + rc = errno; + log_warn("privsep", + "unable to set priority \"control\" to socket for interface %s", + name); + return rc; + } + +#ifdef SO_LOCK_FILTER + int lock = 1; + if (setsockopt(*fd, SOL_SOCKET, SO_LOCK_FILTER, &lock, sizeof(lock)) < 0) { + if (errno != ENOPROTOOPT) { + rc = errno; + log_warn("privsep", "unable to lock filter for %s", name); + return rc; + } + } +#endif +#ifdef PACKET_IGNORE_OUTGOING + int ignore = 1; + if (setsockopt(*fd, SOL_PACKET, PACKET_IGNORE_OUTGOING, &ignore, + sizeof(ignore)) < 0) { + if (errno != ENOPROTOOPT) { + rc = errno; + log_warn("privsep", + "unable to set packet direction for BPF filter on %s", + name); + return rc; + } + } +#endif + + asroot_iface_init_quirks(ifindex, name); + return 0; +} + +int +asroot_iface_description_os(const char *name, const char *description) +{ + /* We could use netlink but this is a lot to do in a privileged + * process. Just write to /sys/class/net/XXXX/ifalias. */ + char *file; + char descr[IFALIASZ]; + FILE *fp; + int rc; + if (name[0] == '\0' || name[0] == '.') { + log_warnx("privsep", "odd interface name %s", name); + return -1; + } + if (asprintf(&file, SYSFS_CLASS_NET "%s/ifalias", name) == -1) { + log_warn("privsep", + "unable to allocate memory for setting description"); + return -1; + } + if ((fp = fopen(file, "r+")) == NULL) { + rc = errno; + log_debug("privsep", "cannot open interface description for %s: %s", + name, strerror(errno)); + free(file); + return rc; + } + free(file); + if (strlen(description) == 0 && fgets(descr, sizeof(descr), fp) != NULL) { + if (strncmp(descr, "lldpd: ", 7) == 0) { + if (strncmp(descr + 7, "was ", 4) == 0) { + /* Already has an old neighbor */ + fclose(fp); + return 0; + } else { + /* Append was */ + memmove(descr + 11, descr + 7, sizeof(descr) - 11); + memcpy(descr, "lldpd: was ", 11); + } + } else { + /* No description, no neighbor */ + strlcpy(descr, "lldpd: no neighbor", sizeof(descr)); + } + } else + snprintf(descr, sizeof(descr), "lldpd: connected to %s", description); + if (fputs(descr, fp) == EOF) { + log_debug("privsep", "cannot set interface description for %s", name); + fclose(fp); + return -1; + } + fclose(fp); + return 0; +} + +int +asroot_iface_promisc_os(const char *name) +{ + int s, rc; + if ((s = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) < 0) { + rc = errno; + log_warn("privsep", "unable to open raw socket"); + return rc; + } + + struct ifreq ifr = {}; + strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + + if (ioctl(s, SIOCGIFFLAGS, &ifr) == -1) { + rc = errno; + log_warn("privsep", "unable to get interface flags for %s", name); + close(s); + return rc; + } + + if (ifr.ifr_flags & IFF_PROMISC) { + close(s); + return 0; + } + ifr.ifr_flags |= IFF_PROMISC; + if (ioctl(s, SIOCSIFFLAGS, &ifr) == -1) { + rc = errno; + log_warn("privsep", "unable to set promisc mode for %s", name); + close(s); + return rc; + } + log_info("privsep", "promiscuous mode enabled for %s", name); + close(s); + return 0; +} diff --git a/src/daemon/priv-seccomp.c b/src/daemon/priv-seccomp.c new file mode 100644 index 0000000..8322cb2 --- /dev/null +++ b/src/daemon/priv-seccomp.c @@ -0,0 +1,210 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "lldpd.h" +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <signal.h> + +#include "syscall-names.h" +#include <seccomp.h> + +#ifndef SYS_SECCOMP +# define SYS_SECCOMP 1 +#endif + +#if defined(__i386__) +# define REG_SYSCALL REG_EAX +# define ARCH_NR AUDIT_ARCH_I386 +#elif defined(__x86_64__) +# define REG_SYSCALL REG_RAX +# define ARCH_NR AUDIT_ARCH_X86_64 +#else +# error "Platform does not support seccomp filter yet" +# define REG_SYSCALL 0 +# define ARCH_NR 0 +#endif + +/* If there is no privilege separation, seccomp is currently useless */ +#ifdef ENABLE_PRIVSEP +static int monitored = -1; +static int trapped = 0; +/** + * SIGSYS signal handler + * @param nr the signal number + * @param info siginfo_t pointer + * @param void_context handler context + * + * Simple signal handler for SIGSYS displaying the error, killing the child and + * exiting. + * + */ +static void +priv_seccomp_trap_handler(int signal, siginfo_t *info, void *vctx) +{ + ucontext_t *ctx = (ucontext_t *)(vctx); + unsigned int syscall; + + if (trapped) _exit(161); /* Avoid loops */ + + /* Get details */ + if (info->si_code != SYS_SECCOMP) return; + if (!ctx) _exit(161); + syscall = ctx->uc_mcontext.gregs[REG_SYSCALL]; + trapped = 1; + + /* Log them. Technically, `log_warnx()` is not signal safe, but we are + * unlikely to reenter here. */ + log_warnx("seccomp", "invalid syscall attempted: %s(%d)", + (syscall < sizeof(syscall_names)) ? syscall_names[syscall] : "unknown", + syscall); + + /* Kill children and exit */ + kill(monitored, SIGTERM); + fatalx("seccomp", "invalid syscall not allowed: stop here"); + _exit(161); +} + +/** + * Install a TRAP action signal handler + * + * This function installs the TRAP action signal handler and is based on + * examples from Will Drewry and Kees Cook. Returns zero on success, negative + * values on failure. + * + */ +static int +priv_seccomp_trap_install() +{ + struct sigaction signal_handler = {}; + sigset_t signal_mask; + + sigemptyset(&signal_mask); + sigaddset(&signal_mask, SIGSYS); + + signal_handler.sa_sigaction = &priv_seccomp_trap_handler; + signal_handler.sa_flags = SA_SIGINFO; + if (sigaction(SIGSYS, &signal_handler, NULL) < 0) return -errno; + if (sigprocmask(SIG_UNBLOCK, &signal_mask, NULL)) return -errno; + + return 0; +} + +/** + * Initialize seccomp. + * + * @param remote file descriptor to talk with the unprivileged process + * @param monitored monitored child + * @return negative on failures or 0 if everything was setup + */ +int +priv_seccomp_init(int remote, int child) +{ + int rc = -1; + scmp_filter_ctx ctx = NULL; + + log_debug("seccomp", "initialize libseccomp filter"); + monitored = child; + if (priv_seccomp_trap_install() < 0) { + log_warn("seccomp", "unable to install SIGSYS handler"); + goto failure_scmp; + } + + if ((ctx = seccomp_init(SCMP_ACT_TRAP)) == NULL) { + log_warnx("seccomp", "unable to initialize libseccomp subsystem"); + goto failure_scmp; + } + + if ((rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 1, + SCMP_CMP(0, SCMP_CMP_EQ, remote))) < 0 || + (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 1, + SCMP_CMP(0, SCMP_CMP_EQ, remote))) < 0) { + errno = -rc; + log_warn("seccomp", "unable to allow read/write on remote socket"); + goto failure_scmp; + } + + /* We are far more generic from here. */ + if ((rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0)) < 0 || + (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0)) < + 0 || /* write needed for */ + (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 0)) < 0 || + (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(openat), 0)) < 0 || + (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(lseek), 0)) < 0 || + (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fcntl), 0)) < 0 || + (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(kill), 0)) < 0 || + (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), 0)) < 0 || + (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(bind), 0)) < 0 || + (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt), 0)) < 0 || + (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getsockname), 0)) < + 0 || + (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(uname), 0)) < 0 || + (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(unlink), 0)) < 0 || + (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 0)) < 0 || + (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(sendmsg), 0)) < 0 || + (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(sendmmsg), 0)) < 0 || + (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(wait4), 0)) < 0 || + (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(stat), 0)) < 0 || + (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(brk), 0)) < + 0 || /* brk needed for newer libc */ + (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getpid), 0)) < 0 || + (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigreturn), 0)) < + 0 || + (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(close), 0)) < 0 || + (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(sendto), 0)) < 0 || + (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(poll), 0)) < 0 || + (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(recvmsg), 0)) < 0 || + (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(recvfrom), 0)) < 0 || + (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(readv), 0)) < 0 || + (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mprotect), 0)) < 0 || + (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(sendmmsg), 0)) < 0 || + (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(clock_gettime), 0)) < + 0 || + (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(gettimeofday), 0)) < + 0 || + (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(newfstatat), 0)) < 0 || + (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(pread64), 0)) < 0 || + (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(access), 0)) < 0 || + (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigprocmask), 0)) < + 0 || + (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getsockopt), 0)) < 0 || + (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ppoll), 0)) < 0 || + /* The following are for resolving addresses */ + (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap), 0)) < 0 || + (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(munmap), 0)) < 0 || + (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fstat), 0)) < 0 || + (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(connect), 0)) < 0 || + (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(futex), 0)) < 0 || + + (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0)) < 0) { + errno = -rc; + log_warn("seccomp", "unable to build seccomp rules"); + goto failure_scmp; + } + + if ((rc = seccomp_load(ctx)) < 0) { + errno = -rc; + log_warn("seccomp", "unable to load libseccomp filter"); + goto failure_scmp; + } + +failure_scmp: + seccomp_release(ctx); + return rc; +} +#endif diff --git a/src/daemon/priv.c b/src/daemon/priv.c new file mode 100644 index 0000000..d642328 --- /dev/null +++ b/src/daemon/priv.c @@ -0,0 +1,761 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* This file contains code for privilege separation. When an error arises in + * monitor (which is running as root), it just stops instead of trying to + * recover. This module also contains proxies to privileged operations. In this + * case, error can be non fatal. */ + +#include "lldpd.h" +#include "trace.h" + +#include <stdio.h> +#include <unistd.h> +#include <signal.h> +#include <errno.h> +#include <limits.h> +#include <sys/wait.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <fcntl.h> +#include <grp.h> +#include <sys/utsname.h> +#include <sys/ioctl.h> +#include <netinet/if_ether.h> + +#ifdef HAVE_LINUX_CAPABILITIES +# include <sys/capability.h> +# include <sys/prctl.h> +#endif + +#if defined HOST_OS_FREEBSD || defined HOST_OS_OSX || defined HOST_OS_DRAGONFLY +# include <net/if_dl.h> +#endif +#if defined HOST_OS_SOLARIS +# include <sys/sockio.h> +#endif + +/* Use resolv.h */ +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif +#ifdef HAVE_NETINET_IN_H +# include <netinet/in.h> +#endif +#ifdef HAVE_ARPA_NAMESER_H +# include <arpa/nameser.h> /* DNS HEADER struct */ +#endif +#ifdef HAVE_NETDB_H +# include <netdb.h> +#endif +#ifdef HAVE_RESOLV_H +# include <resolv.h> +#endif + +/* Bionic has res_init() but it's not in any header */ +#if defined HAVE_RES_INIT && defined __BIONIC__ +int res_init(void); +#endif + +#ifdef ENABLE_PRIVSEP +static int monitored = -1; /* Child */ +#endif + +/* Proxies */ +static void +priv_ping() +{ + int rc; + enum priv_cmd cmd = PRIV_PING; + must_write(PRIV_UNPRIVILEGED, &cmd, sizeof(enum priv_cmd)); + priv_wait(); + must_read(PRIV_UNPRIVILEGED, &rc, sizeof(int)); + log_debug("privsep", "monitor ready"); +} + +/* Proxy for ctl_cleanup */ +void +priv_ctl_cleanup(const char *ctlname) +{ + int rc, len = strlen(ctlname); + enum priv_cmd cmd = PRIV_DELETE_CTL_SOCKET; + must_write(PRIV_UNPRIVILEGED, &cmd, sizeof(enum priv_cmd)); + must_write(PRIV_UNPRIVILEGED, &len, sizeof(int)); + must_write(PRIV_UNPRIVILEGED, ctlname, len); + priv_wait(); + must_read(PRIV_UNPRIVILEGED, &rc, sizeof(int)); +} + +/* Proxy for gethostname */ +char * +priv_gethostname() +{ + static char *buf = NULL; + int len; + enum priv_cmd cmd = PRIV_GET_HOSTNAME; + must_write(PRIV_UNPRIVILEGED, &cmd, sizeof(enum priv_cmd)); + priv_wait(); + must_read(PRIV_UNPRIVILEGED, &len, sizeof(int)); + if (len < 0 || len > 255) fatalx("privsep", "too large value requested"); + if ((buf = (char *)realloc(buf, len + 1)) == NULL) fatal("privsep", NULL); + must_read(PRIV_UNPRIVILEGED, buf, len); + buf[len] = '\0'; + return buf; +} + +int +priv_iface_init(int index, char *iface) +{ + int rc; + char dev[IFNAMSIZ] = {}; + enum priv_cmd cmd = PRIV_IFACE_INIT; + must_write(PRIV_UNPRIVILEGED, &cmd, sizeof(enum priv_cmd)); + must_write(PRIV_UNPRIVILEGED, &index, sizeof(int)); + strlcpy(dev, iface, IFNAMSIZ); + must_write(PRIV_UNPRIVILEGED, dev, IFNAMSIZ); + priv_wait(); + must_read(PRIV_UNPRIVILEGED, &rc, sizeof(int)); + if (rc != 0) return -1; + return receive_fd(PRIV_UNPRIVILEGED); +} + +int +priv_iface_multicast(const char *name, const u_int8_t *mac, int add) +{ + int rc; + enum priv_cmd cmd = PRIV_IFACE_MULTICAST; + must_write(PRIV_UNPRIVILEGED, &cmd, sizeof(enum priv_cmd)); + must_write(PRIV_UNPRIVILEGED, name, IFNAMSIZ); + must_write(PRIV_UNPRIVILEGED, mac, ETHER_ADDR_LEN); + must_write(PRIV_UNPRIVILEGED, &add, sizeof(int)); + priv_wait(); + must_read(PRIV_UNPRIVILEGED, &rc, sizeof(int)); + return rc; +} + +int +priv_iface_description(const char *name, const char *description) +{ + int rc, len = strlen(description); + enum priv_cmd cmd = PRIV_IFACE_DESCRIPTION; + must_write(PRIV_UNPRIVILEGED, &cmd, sizeof(enum priv_cmd)); + must_write(PRIV_UNPRIVILEGED, name, IFNAMSIZ); + must_write(PRIV_UNPRIVILEGED, &len, sizeof(int)); + must_write(PRIV_UNPRIVILEGED, description, len); + priv_wait(); + must_read(PRIV_UNPRIVILEGED, &rc, sizeof(int)); + return rc; +} + +/* Proxy to set interface in promiscuous mode */ +int +priv_iface_promisc(const char *ifname) +{ + int rc; + enum priv_cmd cmd = PRIV_IFACE_PROMISC; + must_write(PRIV_UNPRIVILEGED, &cmd, sizeof(enum priv_cmd)); + must_write(PRIV_UNPRIVILEGED, ifname, IFNAMSIZ); + priv_wait(); + must_read(PRIV_UNPRIVILEGED, &rc, sizeof(int)); + return rc; +} + +int +priv_snmp_socket(struct sockaddr_un *addr) +{ + int rc; + enum priv_cmd cmd = PRIV_SNMP_SOCKET; + must_write(PRIV_UNPRIVILEGED, &cmd, sizeof(enum priv_cmd)); + must_write(PRIV_UNPRIVILEGED, addr, sizeof(struct sockaddr_un)); + priv_wait(); + must_read(PRIV_UNPRIVILEGED, &rc, sizeof(int)); + if (rc < 0) return rc; + return receive_fd(PRIV_UNPRIVILEGED); +} + +static void +asroot_ping() +{ + int rc = 1; + must_write(PRIV_PRIVILEGED, &rc, sizeof(int)); +} + +static void +asroot_ctl_cleanup() +{ + int len; + char *ctlname; + int rc = 0; + + must_read(PRIV_PRIVILEGED, &len, sizeof(int)); + if (len < 0 || len > PATH_MAX) fatalx("privsep", "too large value requested"); + if ((ctlname = (char *)malloc(len + 1)) == NULL) fatal("privsep", NULL); + + must_read(PRIV_PRIVILEGED, ctlname, len); + ctlname[len] = 0; + + ctl_cleanup(ctlname); + free(ctlname); + + /* Ack */ + must_write(PRIV_PRIVILEGED, &rc, sizeof(int)); +} + +static void +asroot_gethostname() +{ + struct utsname un; + struct addrinfo hints = { .ai_flags = AI_CANONNAME }; + struct addrinfo *res; + int len; + if (uname(&un) < 0) fatal("privsep", "failed to get system information"); + if (getaddrinfo(un.nodename, NULL, &hints, &res) != 0) { + log_info("privsep", "unable to get system name"); +#ifdef HAVE_RES_INIT + res_init(); +#endif + len = strlen(un.nodename); + must_write(PRIV_PRIVILEGED, &len, sizeof(int)); + must_write(PRIV_PRIVILEGED, un.nodename, len); + } else { + len = strlen(res->ai_canonname); + must_write(PRIV_PRIVILEGED, &len, sizeof(int)); + must_write(PRIV_PRIVILEGED, res->ai_canonname, len); + freeaddrinfo(res); + } +} + +static void +asroot_iface_init() +{ + int rc = -1, fd = -1; + int ifindex; + char name[IFNAMSIZ]; + must_read(PRIV_PRIVILEGED, &ifindex, sizeof(ifindex)); + must_read(PRIV_PRIVILEGED, &name, sizeof(name)); + name[sizeof(name) - 1] = '\0'; + + TRACE(LLDPD_PRIV_INTERFACE_INIT(name)); + rc = asroot_iface_init_os(ifindex, name, &fd); + must_write(PRIV_PRIVILEGED, &rc, sizeof(rc)); + if (rc == 0 && fd >= 0) send_fd(PRIV_PRIVILEGED, fd); + if (fd >= 0) close(fd); +} + +static void +asroot_iface_multicast() +{ + int sock = -1, add, rc = 0; + struct ifreq ifr = { .ifr_name = {} }; + must_read(PRIV_PRIVILEGED, ifr.ifr_name, IFNAMSIZ); +#if defined HOST_OS_LINUX + must_read(PRIV_PRIVILEGED, ifr.ifr_hwaddr.sa_data, ETHER_ADDR_LEN); +#elif defined HOST_OS_FREEBSD || defined HOST_OS_OSX || defined HOST_OS_DRAGONFLY + /* Black magic from mtest.c */ + struct sockaddr_dl *dlp = ALIGNED_CAST(struct sockaddr_dl *, &ifr.ifr_addr); + dlp->sdl_len = sizeof(struct sockaddr_dl); + dlp->sdl_family = AF_LINK; + dlp->sdl_index = 0; + dlp->sdl_nlen = 0; + dlp->sdl_alen = ETHER_ADDR_LEN; + dlp->sdl_slen = 0; + must_read(PRIV_PRIVILEGED, LLADDR(dlp), ETHER_ADDR_LEN); +#elif defined HOST_OS_OPENBSD || defined HOST_OS_NETBSD || defined HOST_OS_SOLARIS + struct sockaddr *sap = (struct sockaddr *)&ifr.ifr_addr; +# if !defined HOST_OS_SOLARIS + sap->sa_len = sizeof(struct sockaddr); +# endif + sap->sa_family = AF_UNSPEC; + must_read(PRIV_PRIVILEGED, sap->sa_data, ETHER_ADDR_LEN); +#else +# error Unsupported OS +#endif + + must_read(PRIV_PRIVILEGED, &add, sizeof(int)); + if (((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) || + ((ioctl(sock, (add) ? SIOCADDMULTI : SIOCDELMULTI, &ifr) < 0) && + (errno != EADDRINUSE))) + rc = errno; + + if (sock != -1) close(sock); + must_write(PRIV_PRIVILEGED, &rc, sizeof(rc)); +} + +static void +asroot_iface_description() +{ + char name[IFNAMSIZ]; + char *description; + int len, rc; + must_read(PRIV_PRIVILEGED, &name, sizeof(name)); + name[sizeof(name) - 1] = '\0'; + must_read(PRIV_PRIVILEGED, &len, sizeof(int)); + if (len < 0 || len > PATH_MAX) fatalx("privsep", "too large value requested"); + if ((description = (char *)malloc(len + 1)) == NULL) fatal("privsep", NULL); + + must_read(PRIV_PRIVILEGED, description, len); + description[len] = 0; + TRACE(LLDPD_PRIV_INTERFACE_DESCRIPTION(name, description)); + rc = asroot_iface_description_os(name, description); + must_write(PRIV_PRIVILEGED, &rc, sizeof(rc)); + free(description); +} + +static void +asroot_iface_promisc() +{ + char name[IFNAMSIZ]; + int rc; + must_read(PRIV_PRIVILEGED, &name, sizeof(name)); + name[sizeof(name) - 1] = '\0'; + rc = asroot_iface_promisc_os(name); + must_write(PRIV_PRIVILEGED, &rc, sizeof(rc)); +} + +static void +asroot_snmp_socket() +{ + int sock, rc; + static struct sockaddr_un *addr = NULL; + struct sockaddr_un bogus; + + if (!addr) { + addr = (struct sockaddr_un *)malloc(sizeof(struct sockaddr_un)); + if (!addr) fatal("privsep", NULL); + must_read(PRIV_PRIVILEGED, addr, sizeof(struct sockaddr_un)); + } else + /* We have already been asked to connect to a socket. We will + * connect to the same socket. */ + must_read(PRIV_PRIVILEGED, &bogus, sizeof(struct sockaddr_un)); + if (addr->sun_family != AF_UNIX) + fatal("privsep", "someone is trying to trick me"); + addr->sun_path[sizeof(addr->sun_path) - 1] = '\0'; + + if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { + log_warn("privsep", "cannot open socket"); + must_write(PRIV_PRIVILEGED, &sock, sizeof(int)); + return; + } + if ((rc = connect(sock, (struct sockaddr *)addr, sizeof(struct sockaddr_un))) != + 0) { + log_info("privsep", "cannot connect to %s: %s", addr->sun_path, + strerror(errno)); + close(sock); + rc = -1; + must_write(PRIV_PRIVILEGED, &rc, sizeof(int)); + return; + } + + int flags; + if ((flags = fcntl(sock, F_GETFL, NULL)) < 0 || + fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) { + log_warn("privsep", "cannot set sock %s to non-block : %s", + addr->sun_path, strerror(errno)); + + close(sock); + rc = -1; + must_write(PRIV_PRIVILEGED, &rc, sizeof(int)); + return; + } + + must_write(PRIV_PRIVILEGED, &rc, sizeof(int)); + send_fd(PRIV_PRIVILEGED, sock); + close(sock); +} + +struct dispatch_actions { + enum priv_cmd msg; + void (*function)(void); +}; + +static struct dispatch_actions actions[] = { { PRIV_PING, asroot_ping }, + { PRIV_DELETE_CTL_SOCKET, asroot_ctl_cleanup }, + { PRIV_GET_HOSTNAME, asroot_gethostname }, +#ifdef HOST_OS_LINUX + { PRIV_OPEN, asroot_open }, +#endif + { PRIV_IFACE_INIT, asroot_iface_init }, + { PRIV_IFACE_MULTICAST, asroot_iface_multicast }, + { PRIV_IFACE_DESCRIPTION, asroot_iface_description }, + { PRIV_IFACE_PROMISC, asroot_iface_promisc }, + { PRIV_SNMP_SOCKET, asroot_snmp_socket }, { -1, NULL } }; + +/* Main loop, run as root */ +static void +priv_loop(int privileged, int once) +{ + enum priv_cmd cmd; + struct dispatch_actions *a; + +#ifdef ENABLE_PRIVSEP + setproctitle("monitor."); +# ifdef USE_SECCOMP + if (priv_seccomp_init(privileged, monitored) != 0) + fatal("privsep", "cannot continue without seccomp setup"); +# endif +#endif + while (!may_read(PRIV_PRIVILEGED, &cmd, sizeof(enum priv_cmd))) { + log_debug("privsep", "received command %d", cmd); + for (a = actions; a->function != NULL; a++) { + if (cmd == a->msg) { + a->function(); + break; + } + } + if (a->function == NULL) fatalx("privsep", "bogus message received"); + if (once) break; + } +} + +/* This function is a NOOP when privilege separation is enabled. In + * the other case, it should be called when we wait an action from the + * privileged side. */ +void +priv_wait() +{ +#ifndef ENABLE_PRIVSEP + /* We have no remote process on the other side. Let's emulate it. */ + priv_loop(0, 1); +#endif +} + +#ifdef ENABLE_PRIVSEP +static void +priv_exit_rc_status(int rc, int status) +{ + switch (rc) { + case 0: + /* kill child */ + kill(monitored, SIGTERM); + /* we will receive a sigchld in the future */ + return; + case -1: + /* child doesn't exist anymore, we consider this is an error to + * be here */ + _exit(1); + break; + default: + /* Monitored child has terminated */ + /* Mimic the exit state of the child */ + if (WIFEXITED(status)) { + /* Normal exit */ + _exit(WEXITSTATUS(status)); + } + if (WIFSIGNALED(status)) { + /* Terminated with signal */ + signal(WTERMSIG(status), SIG_DFL); + raise(WTERMSIG(status)); + _exit(1); /* We consider that not being killed is an error. */ + } + /* Other cases, consider this as an error. */ + _exit(1); + break; + } +} + +static void +priv_exit() +{ + int status; + int rc; + rc = waitpid(monitored, &status, WNOHANG); + priv_exit_rc_status(rc, status); +} + +/* If priv parent gets a TERM or HUP, pass it through to child instead */ +static void +sig_pass_to_chld(int sig) +{ + int oerrno = errno; + if (monitored != -1) kill(monitored, sig); + errno = oerrno; +} + +/* If priv parent gets a SIGCHLD, it will exit if this is the monitored + * process. Other processes (including lldpcli)) are just reaped without + * consequences. */ +static void +sig_chld(int sig) +{ + int status; + int rc = waitpid(monitored, &status, WNOHANG); + if (rc == 0) { + while ((rc = waitpid(-1, &status, WNOHANG)) > 0) { + if (rc == monitored) priv_exit_rc_status(rc, status); + } + return; + } + priv_exit_rc_status(rc, status); +} + +/* Create a subdirectory and check if it's here. */ +static int +_mkdir(const char *pathname, mode_t mode) +{ + int save_errno; + if (mkdir(pathname, mode) == 0 || errno == EEXIST) { + errno = 0; + return 0; + } + + /* We can get EROFS on some platforms. Let's check if the directory exists. */ + save_errno = errno; + if (chdir(pathname) == -1) { + errno = save_errno; + return -1; + } + + /* We should restore current directory, but in the context we are + * running, we do not care. */ + return 0; +} + +/* Create a directory recursively. */ +static int +mkdir_p(const char *pathname, mode_t mode) +{ + char path[PATH_MAX + 1]; + char *current; + + if (strlcpy(path, pathname, sizeof(path)) >= sizeof(path)) { + errno = ENAMETOOLONG; + return -1; + } + + /* Use strtok which will provides non-empty tokens only. */ + for (current = path + 1; *current; current++) { + if (*current != '/') continue; + *current = '\0'; + if (_mkdir(path, mode) != 0) return -1; + *current = '/'; + } + if (_mkdir(path, mode) != 0) return -1; + + return 0; +} + +/* Initialization */ +# define LOCALTIME "/etc/localtime" +static void +priv_setup_chroot(const char *chrootdir) +{ + /* Create chroot if it does not exist */ + if (mkdir_p(chrootdir, 0755) == -1) { + fatal("privsep", "unable to create chroot directory"); + } + + /* Check if /etc/localtime exists in chroot or outside chroot */ + char path[1024]; + int source = -1, destination = -1; + if (snprintf(path, sizeof(path), "%s" LOCALTIME, chrootdir) >= sizeof(path)) + return; + if ((source = open(LOCALTIME, O_RDONLY)) == -1) { + if (errno == ENOENT) return; + log_warn("privsep", "cannot read " LOCALTIME); + return; + } + + /* Prepare copy of /etc/localtime */ + path[strlen(chrootdir) + 4] = '\0'; + if (mkdir(path, 0755) == -1) { + if (errno != EEXIST) { + log_warn("privsep", "unable to create %s directory", path); + close(source); + return; + } + } + path[strlen(chrootdir) + 4] = '/'; + + /* Do copy */ + char buffer[1024]; + ssize_t n; + mode_t old = umask(S_IWGRP | S_IWOTH); + if ((destination = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0666)) == + -1) { + if (errno != EEXIST) log_warn("privsep", "cannot create %s", path); + close(source); + umask(old); + return; + } + umask(old); + while ((n = read(source, buffer, sizeof(buffer))) > 0) { + ssize_t nw, left = n; + char *p = buffer; + while (left > 0) { + if ((nw = write(destination, p, left)) == -1) { + if (errno == EINTR) continue; + log_warn("privsep", "cannot write to %s", path); + close(source); + close(destination); + unlink(path); + return; + } + left -= nw; + p += nw; + } + } + if (n == -1) { + log_warn("privsep", "cannot read " LOCALTIME); + unlink(path); + } else { + log_info("privsep", LOCALTIME " copied to chroot"); + } + close(source); + close(destination); +} +#else /* !ENABLE_PRIVSEP */ + +/* Reap any children. It should only be lldpcli since there is not monitored + * process. */ +static void +sig_chld(int sig) +{ + int status = 0; + while (waitpid(-1, &status, WNOHANG) > 0) + ; +} + +#endif + +#ifdef ENABLE_PRIVSEP +static void +priv_drop(uid_t uid, gid_t gid) +{ + gid_t gidset[1]; + gidset[0] = gid; + log_debug("privsep", "dropping privileges"); +# ifdef HAVE_SETRESGID + if (setresgid(gid, gid, gid) == -1) fatal("privsep", "setresgid() failed"); +# else + if (setregid(gid, gid) == -1) fatal("privsep", "setregid() failed"); +# endif + if (setgroups(1, gidset) == -1) fatal("privsep", "setgroups() failed"); +# ifdef HAVE_SETRESUID + if (setresuid(uid, uid, uid) == -1) fatal("privsep", "setresuid() failed"); +# else + if (setreuid(uid, uid) == -1) fatal("privsep", "setreuid() failed"); +# endif +} + +static void +priv_caps(uid_t uid, gid_t gid) +{ +# ifdef HAVE_LINUX_CAPABILITIES + cap_t caps; + const char *caps_strings[2] = { + "cap_dac_override,cap_net_raw,cap_net_admin,cap_setuid,cap_setgid=pe", + "cap_dac_override,cap_net_raw,cap_net_admin=pe" + }; + log_debug("privsep", + "getting CAP_NET_RAW/ADMIN and CAP_DAC_OVERRIDE privilege"); + if (!(caps = cap_from_text(caps_strings[0]))) + fatal("privsep", "unable to convert caps"); + if (cap_set_proc(caps) == -1) { + log_warn("privsep", + "unable to drop privileges, monitor running as root"); + cap_free(caps); + return; + } + cap_free(caps); + + if (prctl(PR_SET_KEEPCAPS, 1L, 0L, 0L, 0L) == -1) + fatal("privsep", "cannot keep capabilities"); + priv_drop(uid, gid); + + log_debug("privsep", "dropping extra capabilities"); + if (!(caps = cap_from_text(caps_strings[1]))) + fatal("privsep", "unable to convert caps"); + if (cap_set_proc(caps) == -1) + fatal("privsep", "unable to drop extra privileges"); + cap_free(caps); +# else + log_info("privsep", "no libcap support, running monitor as root"); +# endif +} +#endif + +void +#ifdef ENABLE_PRIVSEP +priv_init(const char *chrootdir, int ctl, uid_t uid, gid_t gid) +#else +priv_init(void) +#endif +{ + + int pair[2]; + + /* Create socket pair */ + if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) < 0) { + fatal("privsep", + "unable to create socket pair for privilege separation"); + } + + priv_unprivileged_fd(pair[0]); + priv_privileged_fd(pair[1]); + +#ifdef ENABLE_PRIVSEP + /* Spawn off monitor */ + if ((monitored = fork()) < 0) fatal("privsep", "unable to fork monitor"); + switch (monitored) { + case 0: + /* We are in the children, drop privileges */ + if (RUNNING_ON_VALGRIND) + log_warnx("privsep", "running on valgrind, keep privileges"); + else { + priv_setup_chroot(chrootdir); + if (chroot(chrootdir) == -1) + fatal("privsep", "unable to chroot"); + if (chdir("/") != 0) fatal("privsep", "unable to chdir"); + priv_drop(uid, gid); + } + close(pair[1]); + priv_ping(); + break; + default: + /* We are in the monitor */ + if (ctl != -1) close(ctl); + close(pair[0]); + if (atexit(priv_exit) != 0) + fatal("privsep", "unable to set exit function"); + + priv_caps(uid, gid); + + /* Install signal handlers */ + const struct sigaction pass_to_child = { .sa_handler = sig_pass_to_chld, + .sa_flags = SA_RESTART }; + sigaction(SIGALRM, &pass_to_child, NULL); + sigaction(SIGTERM, &pass_to_child, NULL); + sigaction(SIGHUP, &pass_to_child, NULL); + sigaction(SIGINT, &pass_to_child, NULL); + sigaction(SIGQUIT, &pass_to_child, NULL); + const struct sigaction child = { .sa_handler = sig_chld, + .sa_flags = SA_RESTART }; + sigaction(SIGCHLD, &child, NULL); + sig_chld(0); /* Reap already dead children */ + priv_loop(pair[1], 0); + exit(0); + } +#else + const struct sigaction child = { .sa_handler = sig_chld, + .sa_flags = SA_RESTART }; + sigaction(SIGCHLD, &child, NULL); + sig_chld(0); /* Reap already dead children */ + log_warnx("priv", "no privilege separation available"); + priv_ping(); +#endif +} diff --git a/src/daemon/privsep.c b/src/daemon/privsep.c new file mode 100644 index 0000000..04c49a9 --- /dev/null +++ b/src/daemon/privsep.c @@ -0,0 +1,26 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ + +#include "lldpd.h" + +static int privileged, unprivileged; +void +priv_privileged_fd(int fd) +{ + privileged = fd; +} +void +priv_unprivileged_fd(int fd) +{ + unprivileged = fd; +} +int +priv_fd(enum priv_context ctx) +{ + switch (ctx) { + case PRIV_PRIVILEGED: + return privileged; + case PRIV_UNPRIVILEGED: + return unprivileged; + } + return -1; /* Not possible */ +} diff --git a/src/daemon/privsep_fd.c b/src/daemon/privsep_fd.c new file mode 100644 index 0000000..fa98663 --- /dev/null +++ b/src/daemon/privsep_fd.c @@ -0,0 +1,129 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ + +#include "lldpd.h" + +#include <sys/param.h> +#include <sys/uio.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +/* + * Copyright 2001 Niels Provos <provos@citi.umich.edu> + * All rights reserved. + * + * Copyright (c) 2002 Matthieu Herrb + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +void +send_fd(enum priv_context ctx, int fd) +{ + struct msghdr msg; + union { + struct cmsghdr hdr; + char buf[CMSG_SPACE(sizeof(int))]; + } cmsgbuf; + struct cmsghdr *cmsg; + struct iovec vec; + int result = 0; + ssize_t n; + + memset(&msg, 0, sizeof(msg)); + memset(&cmsgbuf.buf, 0, sizeof(cmsgbuf.buf)); + + if (fd >= 0) { + msg.msg_control = (caddr_t)&cmsgbuf.buf; + msg.msg_controllen = sizeof(cmsgbuf.buf); + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + memcpy(CMSG_DATA(cmsg), &fd, sizeof(int)); + } else { + result = errno; + } + + vec.iov_base = &result; + vec.iov_len = sizeof(int); + msg.msg_iov = &vec; + msg.msg_iovlen = 1; + + if ((n = sendmsg(priv_fd(ctx), &msg, 0)) == -1) + log_warn("privsep", "sendmsg(%d)", priv_fd(ctx)); + if (n != sizeof(int)) + log_warnx("privsep", "sendmsg: expected sent 1 got %ld", (long)n); +} + +int +receive_fd(enum priv_context ctx) +{ + struct msghdr msg; + union { + struct cmsghdr hdr; + char buf[CMSG_SPACE(sizeof(int))]; + } cmsgbuf; + struct cmsghdr *cmsg; + struct iovec vec; + ssize_t n; + int result; + int fd; + + memset(&msg, 0, sizeof(msg)); + vec.iov_base = &result; + vec.iov_len = sizeof(int); + msg.msg_iov = &vec; + msg.msg_iovlen = 1; + msg.msg_control = &cmsgbuf.buf; + msg.msg_controllen = sizeof(cmsgbuf.buf); + + if ((n = recvmsg(priv_fd(ctx), &msg, 0)) == -1) log_warn("privsep", "recvmsg"); + if (n != sizeof(int)) + log_warnx("privsep", "recvmsg: expected received 1 got %ld", (long)n); + if (result == 0) { + cmsg = CMSG_FIRSTHDR(&msg); + if (cmsg == NULL) { + log_warnx("privsep", "no message header"); + return -1; + } + if (cmsg->cmsg_type != SCM_RIGHTS) + log_warnx("privsep", "expected type %d got %d", SCM_RIGHTS, + cmsg->cmsg_type); + memcpy(&fd, CMSG_DATA(cmsg), sizeof(int)); + return fd; + } else { + errno = result; + return -1; + } +} diff --git a/src/daemon/privsep_io.c b/src/daemon/privsep_io.c new file mode 100644 index 0000000..9d0923a --- /dev/null +++ b/src/daemon/privsep_io.c @@ -0,0 +1,100 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ + +#include "lldpd.h" + +#include <sys/param.h> +#include <sys/uio.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +/* Stolen from sbin/pflogd/privsep.c from OpenBSD */ +/* + * Copyright (c) 2003 Can Erkin Acar + * Copyright (c) 2003 Anil Madhavapeddy <anil@recoil.org> + * + * 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. + */ + +/* Read all data or return 1 for error. */ +int +may_read(enum priv_context ctx, void *buf, size_t n) +{ + char *s = buf; + ssize_t res, pos = 0; + + while (n > pos) { + res = read(priv_fd(ctx), s + pos, n - pos); + switch (res) { + case -1: + if (errno == EINTR || errno == EAGAIN) continue; + return 1; + case 0: + return 1; + default: + pos += res; + } + } + return (0); +} + +/* Read data with the assertion that it all must come through, or + * else abort the process. Based on atomicio() from openssh. */ +void +must_read(enum priv_context ctx, void *buf, size_t n) +{ + char *s = buf; + ssize_t res, pos = 0; + + while (n > pos) { + res = read(priv_fd(ctx), s + pos, n - pos); + switch (res) { + case -1: + if (errno == EINTR || errno == EAGAIN) continue; + _exit(0); + case 0: + _exit(0); + default: + pos += res; + } + } +} + +/* Write data with the assertion that it all has to be written, or + * else abort the process. Based on atomicio() from openssh. */ +void +must_write(enum priv_context ctx, const void *buf, size_t n) +{ + const char *s = buf; + ssize_t res, pos = 0; + + while (n > pos) { + res = write(priv_fd(ctx), s + pos, n - pos); + switch (res) { + case -1: + if (errno == EINTR || errno == EAGAIN) continue; + _exit(0); + case 0: + _exit(0); + default: + pos += res; + } + } +} diff --git a/src/daemon/probes.d b/src/daemon/probes.d new file mode 100644 index 0000000..a4d7785 --- /dev/null +++ b/src/daemon/probes.d @@ -0,0 +1,115 @@ +/* -*- 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. + */ +provider lldpd { + + /** + * Fired when a frame is received, before it is decoded. + * @param ifname the name of the interface + * @param frame the received frame + * @param len the len of the received frame + */ + probe frame_received(char *ifname, void *frame, size_t len); + + /** + * Fired when a frame is decoded. + * @param ifname the name of the interface + * @param protocol the name of the protocol + * @param chassis_name the name of chassis (may be NULL) + * @param port_descr the description of the port (may be NULL) + */ + probe frame_decoded(char *ifname, char *protocol, char *chassis_name, char *port_descr); + + /** + * Fired when a frame is sent. + * @param ifname the name of the interface + * @param protocol the name of the protocol + */ + probe frame_send(char *ifname, char *protocol); + + /** + * Fired when a neighbor is added. + * @param ifname the name of the interface where the neighbor appeared + * @param chassis_name the name of chassis (may be NULL) + * @param port_descr the description of the port (may be NULL) + * @param count the total number of neighbors known + */ + probe neighbor_new(char *ifname, char *chassis_name, char *port_descr, int count); + + /** + * Fired when a neighbor is updated. + * @param ifname the name of the interface where the neighbor updated + * @param chassis_name the name of chassis (may be NULL) + * @param port_descr the description of the port (may be NULL) + * @param count the total number of neighbors known + */ + probe neighbor_update(char *ifname, char *chassis_name, char *port_descr, int count); + + /** + * Fired when a neighbor is deleted. + * @param ifname the name of the interface where the neighbor deleted + * @param chassis_name the name of chassis (may be NULL) + * @param port_descr the description of the port (may be NULL) + * @param count the total number of neighbors known + */ + probe neighbor_delete(char *ifname, char *chassis_name, char *port_descr); + + /** + * Fired before handling a client request. + * @param name the name of the request + */ + probe client_request(char *name); + + /** + * Fired for each iteration of the event loop. + */ + probe event_loop(); + + /** + * Fired when initializing a new interface in privileged mode. + * @param name the name of the interface + */ + probe priv_interface_init(char *name); + + /** + * Fired when setting description of an interface. + * @param name the name of the interface + * @param desc the description of the interface + */ + probe priv_interface_description(char *name, char *description); + + /** + * Fired when doing an interface updates. + */ + probe interfaces_update(); + + /** + * Fired when receiving an interface update notification. + */ + probe interfaces_notification(); + + /** + * Fired when an interface is removed. + * @param name the name of the interface + */ + probe interfaces_delete(char *name); + + /** + * Fired when an interface is added. + * @param name the name of the interface + */ + probe interfaces_new(char *name); +}; diff --git a/src/daemon/protocols/cdp.c b/src/daemon/protocols/cdp.c new file mode 100644 index 0000000..620455b --- /dev/null +++ b/src/daemon/protocols/cdp.c @@ -0,0 +1,711 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* We also supports FDP which is very similar to CDPv1 */ +#include "../lldpd.h" +#include "../frame.h" + +/* + * CDP Requests Power at the switch output and therefore has to take into + * account the loss in the PoE cable. This is done by the switch automatically + * if lldp is used as the protocol. + */ +#define CDP_CLASS_3_MAX_PSE_POE 154 /* 15.4W Max PoE at PSE class 3 */ +#define CDP_SWTICH_DEFAULT_POE_PD 130 /* 13.W default PoE at PD */ +#define CDP_SWTICH_DEFAULT_POE_PSE 154 /* 15.4W default PoE at PSE */ +#define CDP_SWITCH_POE_CLASS_4_OFFSET 45 /* 4.5W max loss from cable */ +#define CDP_SWITCH_POE_CLASS_3_OFFSET 24 /* 2.4W max loss from cable */ + +#if defined ENABLE_CDP || defined ENABLE_FDP + +# include <stdio.h> +# include <unistd.h> +# include <errno.h> +# include <arpa/inet.h> + +static int +cdp_send(struct lldpd *global, struct lldpd_hardware *hardware, int version) +{ + const char *platform = "Unknown"; + struct lldpd_chassis *chassis; + struct lldpd_mgmt *mgmt; + struct lldpd_port *port; + u_int8_t mcastaddr[] = CDP_MULTICAST_ADDR; + u_int8_t llcorg[] = LLC_ORG_CISCO; +# ifdef ENABLE_FDP + const char *capstr; +# endif + u_int16_t checksum; + int length, i; + u_int32_t cap; + u_int8_t *packet; + u_int8_t *pos, *pos_len_eh, *pos_llc, *pos_cdp, *pos_checksum, *tlv, *end; + + log_debug("cdp", "send CDP frame on %s", hardware->h_ifname); + + port = &(hardware->h_lport); + chassis = port->p_chassis; + +# ifdef ENABLE_FDP + if (version == 0) { + /* With FDP, change multicast address and LLC PID */ + const u_int8_t fdpmcastaddr[] = FDP_MULTICAST_ADDR; + const u_int8_t fdpllcorg[] = LLC_ORG_FOUNDRY; + memcpy(mcastaddr, fdpmcastaddr, sizeof(mcastaddr)); + memcpy(llcorg, fdpllcorg, sizeof(llcorg)); + } +# endif + + length = hardware->h_mtu; + if ((packet = (u_int8_t *)calloc(1, length)) == NULL) return ENOMEM; + pos = packet; + + /* Ethernet header */ + if (!(POKE_BYTES(mcastaddr, sizeof(mcastaddr)) && + POKE_BYTES(&hardware->h_lladdr, ETHER_ADDR_LEN) && + POKE_SAVE(pos_len_eh) && /* We compute the len later */ + POKE_UINT16(0))) + goto toobig; + + /* LLC */ + if (!(POKE_SAVE(pos_llc) && POKE_UINT8(0xaa) && /* SSAP */ + POKE_UINT8(0xaa) && /* DSAP */ + POKE_UINT8(0x03) && /* Control field */ + POKE_BYTES(llcorg, sizeof(llcorg)) && POKE_UINT16(LLC_PID_CDP))) + goto toobig; + + /* CDP header */ + if (!(POKE_SAVE(pos_cdp) && POKE_UINT8((version == 0) ? 1 : version) && + POKE_UINT8(global ? global->g_config.c_ttl : 180) && + POKE_SAVE(pos_checksum) && /* Save checksum position */ + POKE_UINT16(0))) + goto toobig; + + /* Chassis ID */ + const char *chassis_name = chassis->c_name ? chassis->c_name : ""; + if (!(POKE_START_CDP_TLV(CDP_TLV_CHASSIS) && + POKE_BYTES(chassis_name, strlen(chassis_name)) && POKE_END_CDP_TLV)) + goto toobig; + + /* Adresses */ + /* See: + * http://www.cisco.com/univercd/cc/td/doc/product/lan/trsrb/frames.htm#xtocid12 + * + * It seems that Cisco implies that CDP supports IPv6 using + * 802.2 address format with 0xAAAA03 0x000000 0x0800, but + * 0x0800 is the Ethernet protocol type for IPv4. Therefore, + * we support only IPv4. */ + i = 0; + TAILQ_FOREACH (mgmt, &chassis->c_mgmt, m_entries) + if (mgmt->m_family == LLDPD_AF_IPV4) i++; + if (i > 0) { + if (!(POKE_START_CDP_TLV(CDP_TLV_ADDRESSES) && POKE_UINT32(i))) + goto toobig; + TAILQ_FOREACH (mgmt, &chassis->c_mgmt, m_entries) { + switch (mgmt->m_family) { + case LLDPD_AF_IPV4: + if (!(POKE_UINT8(1) && /* Type: NLPID */ + POKE_UINT8(1) && /* Length: 1 */ + POKE_UINT8(CDP_ADDRESS_PROTO_IP) && /* IP */ + POKE_UINT16(sizeof( + struct in_addr)) && /* Address length */ + POKE_BYTES(&mgmt->m_addr, + sizeof(struct in_addr)))) + goto toobig; + break; + } + } + if (!(POKE_END_CDP_TLV)) goto toobig; + } + + /* Port ID */ + const char *port_descr = + hardware->h_lport.p_descr ? hardware->h_lport.p_descr : ""; + if (!(POKE_START_CDP_TLV(CDP_TLV_PORT) && + POKE_BYTES(port_descr, strlen(port_descr)) && POKE_END_CDP_TLV)) + goto toobig; + + /* Capabilities */ + if (version != 0) { + cap = 0; + if (chassis->c_cap_enabled & LLDP_CAP_ROUTER) cap |= CDP_CAP_ROUTER; + if (chassis->c_cap_enabled & LLDP_CAP_BRIDGE) cap |= CDP_CAP_SWITCH; + cap |= CDP_CAP_HOST; + if (!(POKE_START_CDP_TLV(CDP_TLV_CAPABILITIES) && POKE_UINT32(cap) && + POKE_END_CDP_TLV)) + goto toobig; +# ifdef ENABLE_FDP + } else { + /* With FDP, it seems that a string is used in place of an int */ + if (chassis->c_cap_enabled & LLDP_CAP_ROUTER) + capstr = "Router"; + else if (chassis->c_cap_enabled & LLDP_CAP_BRIDGE) + capstr = "Switch"; + else if (chassis->c_cap_enabled & LLDP_CAP_REPEATER) + capstr = "Bridge"; + else + capstr = "Host"; + if (!(POKE_START_CDP_TLV(CDP_TLV_CAPABILITIES) && + POKE_BYTES(capstr, strlen(capstr)) && POKE_END_CDP_TLV)) + goto toobig; +# endif + } + + /* Native VLAN */ +# ifdef ENABLE_DOT1 + if (version >= 2 && hardware->h_lport.p_pvid != 0) { + if (!(POKE_START_CDP_TLV(CDP_TLV_NATIVEVLAN) && + POKE_UINT16(hardware->h_lport.p_pvid) && POKE_END_CDP_TLV)) + goto toobig; + } +# endif + + /* Software version */ + const char *chassis_descr = chassis->c_descr ? chassis->c_descr : ""; + if (!(POKE_START_CDP_TLV(CDP_TLV_SOFTWARE) && + POKE_BYTES(chassis_descr, strlen(chassis_descr)) && POKE_END_CDP_TLV)) + goto toobig; + + /* Platform */ + if (global && global->g_config.c_platform) + platform = global->g_config.c_platform; + + if (!(POKE_START_CDP_TLV(CDP_TLV_PLATFORM) && + POKE_BYTES(platform, strlen(platform)) && POKE_END_CDP_TLV)) + goto toobig; + +# ifdef ENABLE_DOT3 + if ((version >= 2) && (port->p_power.powertype != LLDP_DOT3_POWER_8023AT_OFF) && + (port->p_power.devicetype == LLDP_DOT3_POWER_PD) && + (port->p_power.requested > 0) && (port->p_power.requested <= 655)) { + u_int16_t requested; + u_int16_t consumption; + + if (port->p_power.requested != port->p_power.allocated) { + port->p_cdp_power.request_id++; + log_debug("cdp", "requested: %d, allocated:%d", + port->p_power.requested, port->p_power.allocated); + } + consumption = port->p_power.allocated ? port->p_power.allocated : + CDP_SWTICH_DEFAULT_POE_PD; + if (consumption > 130) { + consumption += CDP_SWITCH_POE_CLASS_4_OFFSET; + } else { + consumption += CDP_SWITCH_POE_CLASS_3_OFFSET; + } + if (port->p_power.requested > 130) { /* Class 4 */ + requested = + port->p_power.requested + CDP_SWITCH_POE_CLASS_4_OFFSET; + } else { /* Class 3 */ + requested = + port->p_power.requested + CDP_SWITCH_POE_CLASS_3_OFFSET; + } + if (!(POKE_START_CDP_TLV(CDP_TLV_POWER_CONSUMPTION) && + POKE_UINT16(consumption * 100) && POKE_END_CDP_TLV)) + goto toobig; + /* Avoid request id 0 from overflow */ + if (!port->p_cdp_power.request_id) { + port->p_cdp_power.request_id = 1; + } + if (!port->p_cdp_power.management_id) { + port->p_cdp_power.management_id = 1; + } + if (!(POKE_START_CDP_TLV(CDP_TLV_POWER_REQUESTED) && + POKE_UINT16(port->p_cdp_power.request_id) && + POKE_UINT16(port->p_cdp_power.management_id) && + POKE_UINT32(requested * 100) && POKE_END_CDP_TLV)) + goto toobig; + } +# elif defined ENABLE_LLDPMED + /* Power use */ + if ((version >= 2) && port->p_med_cap_enabled && + (port->p_med_power.source != LLDP_MED_POW_SOURCE_LOCAL) && + (port->p_med_power.val > 0) && (port->p_med_power.val <= 655)) { + if (!(POKE_START_CDP_TLV(CDP_TLV_POWER_CONSUMPTION) && + POKE_UINT16(port->p_med_power.val * 100) && POKE_END_CDP_TLV)) + goto toobig; + } +# endif + + (void)POKE_SAVE(end); + + /* Compute len and checksum */ + POKE_RESTORE(pos_len_eh); + if (!(POKE_UINT16(end - pos_llc))) goto toobig; + checksum = frame_checksum(pos_cdp, end - pos_cdp, (version != 0) ? 1 : 0); + POKE_RESTORE(pos_checksum); + if (!(POKE_UINT16(checksum))) goto toobig; + + if (interfaces_send_helper(global, hardware, (char *)packet, end - packet) == + -1) { + log_warn("cdp", "unable to send packet on real device for %s", + hardware->h_ifname); + free(packet); + return ENETDOWN; + } + + hardware->h_tx_cnt++; + + free(packet); + return 0; +toobig: + free(packet); + return -1; +} + +# define CHECK_TLV_SIZE(x, name) \ + do { \ + if (tlv_len < (x)) { \ + log_warnx("cdp", name " CDP/FDP TLV too short received on %s", \ + hardware->h_ifname); \ + goto malformed; \ + } \ + } while (0) +/* cdp_decode also decodes FDP */ +int +cdp_decode(struct lldpd *cfg, char *frame, int s, struct lldpd_hardware *hardware, + struct lldpd_chassis **newchassis, struct lldpd_port **newport) +{ + struct lldpd_chassis *chassis; + struct lldpd_port *port; + struct lldpd_mgmt *mgmt; + struct in_addr addr; +# if 0 + u_int16_t cksum; +# endif + u_int8_t *software = NULL, *platform = NULL; + int software_len = 0, platform_len = 0, proto, version, nb, caps; + const unsigned char cdpaddr[] = CDP_MULTICAST_ADDR; +# ifdef ENABLE_FDP + const unsigned char fdpaddr[] = CDP_MULTICAST_ADDR; + int fdp = 0; +# endif + u_int8_t *pos, *tlv, *pos_address, *pos_next_address; + int length, len_eth, tlv_type, tlv_len, addresses_len, address_len; +# ifdef ENABLE_DOT1 + struct lldpd_vlan *vlan; +# endif + + log_debug("cdp", "decode CDP frame received on %s", hardware->h_ifname); + + if ((chassis = calloc(1, sizeof(struct lldpd_chassis))) == NULL) { + log_warn("cdp", "failed to allocate remote chassis"); + return -1; + } + TAILQ_INIT(&chassis->c_mgmt); + if ((port = calloc(1, sizeof(struct lldpd_port))) == NULL) { + log_warn("cdp", "failed to allocate remote port"); + free(chassis); + return -1; + } +# ifdef ENABLE_DOT1 + TAILQ_INIT(&port->p_vlans); +# endif + + length = s; + pos = (u_int8_t *)frame; + + if (length < 2 * ETHER_ADDR_LEN + sizeof(u_int16_t) /* Ethernet */ + + 8 /* LLC */ + 4 /* CDP header */) { + log_warn("cdp", "too short CDP/FDP frame received on %s", + hardware->h_ifname); + goto malformed; + } + + if (PEEK_CMP(cdpaddr, sizeof(cdpaddr)) != 0) { +# ifdef ENABLE_FDP + PEEK_RESTORE((u_int8_t *)frame); + if (PEEK_CMP(fdpaddr, sizeof(fdpaddr)) != 0) + fdp = 1; + else { +# endif + log_info("cdp", + "frame not targeted at CDP/FDP multicast address received on %s", + hardware->h_ifname); + goto malformed; +# ifdef ENABLE_FDP + } +# endif + } + PEEK_DISCARD(ETHER_ADDR_LEN); /* Don't care of source address */ + len_eth = PEEK_UINT16; + if (len_eth > length) { + log_warnx("cdp", "incorrect 802.3 frame size reported on %s", + hardware->h_ifname); + goto malformed; + } + + /* This is the correct length of the CDP + LLC packets */ + length = len_eth; + + PEEK_DISCARD(6); /* Skip beginning of LLC */ + proto = PEEK_UINT16; + if (proto != LLC_PID_CDP) { + if ((proto != LLC_PID_DRIP) && (proto != LLC_PID_PAGP) && + (proto != LLC_PID_PVSTP) && (proto != LLC_PID_UDLD) && + (proto != LLC_PID_VTP) && (proto != LLC_PID_DTP) && + (proto != LLC_PID_STP)) + log_debug("cdp", "incorrect LLC protocol ID received on %s", + hardware->h_ifname); + goto malformed; + } + +# if 0 + /* Check checksum */ + cksum = frame_checksum(pos, len_eth - 8, +# ifdef ENABLE_FDP + !fdp /* fdp = 0 -> cisco checksum */ +# else + 1 /* cisco checksum */ +# endif + ); + if (cksum != 0) { + log_info("cdp", "incorrect CDP/FDP checksum for frame received on %s (%d)", + hardware->h_ifname, cksum); + goto malformed; + } +# endif + + /* Check version */ + version = PEEK_UINT8; + if ((version != 1) && (version != 2)) { + log_warnx("cdp", + "incorrect CDP/FDP version (%d) for frame received on %s", version, + hardware->h_ifname); + goto malformed; + } + port->p_ttl = PEEK_UINT8; /* TTL */ + PEEK_DISCARD_UINT16; /* Checksum, already checked */ + + while (length) { + if (length < 4) { + log_warnx("cdp", + "CDP/FDP TLV header is too large for " + "frame received on %s", + hardware->h_ifname); + goto malformed; + } + tlv_type = PEEK_UINT16; + tlv_len = PEEK_UINT16 - 4; + + (void)PEEK_SAVE(tlv); + if ((tlv_len < 0) || (length < tlv_len)) { + log_warnx("cdp", + "incorrect size in CDP/FDP TLV header for frame " + "received on %s", + hardware->h_ifname); + goto malformed; + } + switch (tlv_type) { + case CDP_TLV_CHASSIS: + free(chassis->c_name); + if ((chassis->c_name = (char *)calloc(1, tlv_len + 1)) == + NULL) { + log_warn("cdp", + "unable to allocate memory for chassis name"); + goto malformed; + } + PEEK_BYTES(chassis->c_name, tlv_len); + chassis->c_id_subtype = LLDP_CHASSISID_SUBTYPE_LOCAL; + free(chassis->c_id); + if ((chassis->c_id = (char *)malloc(tlv_len)) == NULL) { + log_warn("cdp", + "unable to allocate memory for chassis ID"); + goto malformed; + } + memcpy(chassis->c_id, chassis->c_name, tlv_len); + chassis->c_id_len = tlv_len; + break; + case CDP_TLV_ADDRESSES: + CHECK_TLV_SIZE(4, "Address"); + addresses_len = tlv_len - 4; + for (nb = PEEK_UINT32; nb > 0; nb--) { + (void)PEEK_SAVE(pos_address); + /* We first try to get the real length of the packet */ + if (addresses_len < 2) { + log_warn("cdp", + "too short address subframe " + "received on %s", + hardware->h_ifname); + goto malformed; + } + PEEK_DISCARD_UINT8; + addresses_len--; + address_len = PEEK_UINT8; + addresses_len--; + if (addresses_len < address_len + 2) { + log_warn("cdp", + "too short address subframe " + "received on %s", + hardware->h_ifname); + goto malformed; + } + PEEK_DISCARD(address_len); + addresses_len -= address_len; + address_len = PEEK_UINT16; + addresses_len -= 2; + if (addresses_len < address_len) { + log_warn("cdp", + "too short address subframe " + "received on %s", + hardware->h_ifname); + goto malformed; + } + PEEK_DISCARD(address_len); + addresses_len -= address_len; + (void)PEEK_SAVE(pos_next_address); + /* Next, we go back and try to extract + IPv4 address */ + PEEK_RESTORE(pos_address); + if ((PEEK_UINT8 == 1) && (PEEK_UINT8 == 1) && + (PEEK_UINT8 == CDP_ADDRESS_PROTO_IP) && + (PEEK_UINT16 == sizeof(struct in_addr))) { + PEEK_BYTES(&addr, sizeof(struct in_addr)); + mgmt = lldpd_alloc_mgmt(LLDPD_AF_IPV4, &addr, + sizeof(struct in_addr), 0); + if (mgmt == NULL) { + if (errno == ENOMEM) + log_warn("cdp", + "unable to allocate memory for management address"); + else + log_warn("cdp", + "too large management address received on %s", + hardware->h_ifname); + goto malformed; + } + TAILQ_INSERT_TAIL(&chassis->c_mgmt, mgmt, + m_entries); + } + /* Go to the end of the address */ + PEEK_RESTORE(pos_next_address); + } + break; + case CDP_TLV_PORT: + if (tlv_len == 0) { + log_warn("cdp", "too short port description received"); + goto malformed; + } + free(port->p_descr); + if ((port->p_descr = (char *)calloc(1, tlv_len + 1)) == NULL) { + log_warn("cdp", + "unable to allocate memory for port description"); + goto malformed; + } + PEEK_BYTES(port->p_descr, tlv_len); + port->p_id_subtype = LLDP_PORTID_SUBTYPE_IFNAME; + free(port->p_id); + if ((port->p_id = (char *)calloc(1, tlv_len)) == NULL) { + log_warn("cdp", + "unable to allocate memory for port ID"); + goto malformed; + } + memcpy(port->p_id, port->p_descr, tlv_len); + port->p_id_len = tlv_len; + break; + case CDP_TLV_CAPABILITIES: +# ifdef ENABLE_FDP + if (fdp) { + /* Capabilities are string with FDP */ + if (!strncmp("Router", (char *)pos, tlv_len)) + chassis->c_cap_enabled = LLDP_CAP_ROUTER; + else if (!strncmp("Switch", (char *)pos, tlv_len)) + chassis->c_cap_enabled = LLDP_CAP_BRIDGE; + else if (!strncmp("Bridge", (char *)pos, tlv_len)) + chassis->c_cap_enabled = LLDP_CAP_REPEATER; + else + chassis->c_cap_enabled = LLDP_CAP_STATION; + chassis->c_cap_available = chassis->c_cap_enabled; + break; + } +# endif + CHECK_TLV_SIZE(4, "Capabilities"); + caps = PEEK_UINT32; + if (caps & CDP_CAP_ROUTER) + chassis->c_cap_enabled |= LLDP_CAP_ROUTER; + if (caps & 0x0e) chassis->c_cap_enabled |= LLDP_CAP_BRIDGE; + if (chassis->c_cap_enabled == 0) + chassis->c_cap_enabled = LLDP_CAP_STATION; + chassis->c_cap_available = chassis->c_cap_enabled; + break; + case CDP_TLV_SOFTWARE: + software_len = tlv_len; + (void)PEEK_SAVE(software); + break; + case CDP_TLV_PLATFORM: + platform_len = tlv_len; + (void)PEEK_SAVE(platform); + break; +# ifdef ENABLE_DOT1 + case CDP_TLV_NATIVEVLAN: + CHECK_TLV_SIZE(2, "Native VLAN"); + if ((vlan = (struct lldpd_vlan *)calloc(1, + sizeof(struct lldpd_vlan))) == NULL) { + log_warn("cdp", + "unable to alloc vlan " + "structure for " + "tlv received on %s", + hardware->h_ifname); + goto malformed; + } + vlan->v_vid = port->p_pvid = PEEK_UINT16; + if (asprintf(&vlan->v_name, "VLAN #%d", vlan->v_vid) == -1) { + log_warn("cdp", + "unable to alloc VLAN name for " + "TLV received on %s", + hardware->h_ifname); + free(vlan); + goto malformed; + } + TAILQ_INSERT_TAIL(&port->p_vlans, vlan, v_entries); + break; +# endif +# ifdef ENABLE_DOT3 + case CDP_TLV_POWER_AVAILABLE: + CHECK_TLV_SIZE(12, "Power Available"); + /* check if it is a respone to a request id */ + if (PEEK_UINT16 > 0) { + port->p_cdp_power.management_id = PEEK_UINT16; + port->p_power.allocated = PEEK_UINT32; + port->p_power.allocated /= 100; + port->p_power.supported = 1; + port->p_power.enabled = 1; + port->p_power.devicetype = LLDP_DOT3_POWER_PSE; + port->p_power.powertype = LLDP_DOT3_POWER_8023AT_TYPE2; + log_debug("cdp", "Allocated power %d00", + port->p_power.allocated); + if (port->p_power.allocated > CDP_CLASS_3_MAX_PSE_POE) { + port->p_power.allocated -= + CDP_SWITCH_POE_CLASS_4_OFFSET; + } else if (port->p_power.allocated > + CDP_SWITCH_POE_CLASS_3_OFFSET) { + port->p_power.allocated -= + CDP_SWITCH_POE_CLASS_3_OFFSET; + } else { + port->p_power.allocated = 0; + } + port->p_power.requested = + hardware->h_lport.p_power.requested; + } + break; +# endif + default: + log_debug("cdp", "unknown CDP/FDP TLV type (%d) received on %s", + ntohs(tlv_type), hardware->h_ifname); + hardware->h_rx_unrecognized_cnt++; + } + PEEK_DISCARD(tlv + tlv_len - pos); + } + if (!software && platform) { + if ((chassis->c_descr = (char *)calloc(1, platform_len + 1)) == NULL) { + log_warn("cdp", + "unable to allocate memory for chassis description"); + goto malformed; + } + memcpy(chassis->c_descr, platform, platform_len); + } else if (software && !platform) { + if ((chassis->c_descr = (char *)calloc(1, software_len + 1)) == NULL) { + log_warn("cdp", + "unable to allocate memory for chassis description"); + goto malformed; + } + memcpy(chassis->c_descr, software, software_len); + } else if (software && platform) { +# define CONCAT_PLATFORM " running on\n" + if ((chassis->c_descr = (char *)calloc(1, + software_len + platform_len + strlen(CONCAT_PLATFORM) + 1)) == + NULL) { + log_warn("cdp", + "unable to allocate memory for chassis description"); + goto malformed; + } + memcpy(chassis->c_descr, platform, platform_len); + memcpy(chassis->c_descr + platform_len, CONCAT_PLATFORM, + strlen(CONCAT_PLATFORM)); + memcpy(chassis->c_descr + platform_len + strlen(CONCAT_PLATFORM), + software, software_len); + } + if ((chassis->c_id == NULL) || (port->p_id == NULL) || + (chassis->c_name == NULL) || (chassis->c_descr == NULL) || + (port->p_descr == NULL) || (port->p_ttl == 0) || + (chassis->c_cap_enabled == 0)) { + log_warnx("cdp", + "some mandatory CDP/FDP tlv are missing for frame received on %s", + hardware->h_ifname); + goto malformed; + } + *newchassis = chassis; + *newport = port; + return 1; + +malformed: + lldpd_chassis_cleanup(chassis, 1); + lldpd_port_cleanup(port, 1); + free(port); + return -1; +} + +# ifdef ENABLE_CDP +int +cdpv1_send(struct lldpd *global, struct lldpd_hardware *hardware) +{ + return cdp_send(global, hardware, 1); +} + +int +cdpv2_send(struct lldpd *global, struct lldpd_hardware *hardware) +{ + return cdp_send(global, hardware, 2); +} +# endif + +# ifdef ENABLE_FDP +int +fdp_send(struct lldpd *global, struct lldpd_hardware *hardware) +{ + return cdp_send(global, hardware, 0); +} +# endif + +# ifdef ENABLE_CDP +static int +cdp_guess(char *pos, int length, int version) +{ + const u_int8_t mcastaddr[] = CDP_MULTICAST_ADDR; + if (length < 2 * ETHER_ADDR_LEN + sizeof(u_int16_t) /* Ethernet */ + + 8 /* LLC */ + 4 /* CDP header */) + return 0; + if (PEEK_CMP(mcastaddr, ETHER_ADDR_LEN) != 0) return 0; + PEEK_DISCARD(ETHER_ADDR_LEN); + PEEK_DISCARD_UINT16; /* Ethernet */ + PEEK_DISCARD(8); /* LLC */ + return (PEEK_UINT8 == version); +} + +int +cdpv1_guess(char *frame, int len) +{ + return cdp_guess(frame, len, 1); +} + +int +cdpv2_guess(char *frame, int len) +{ + return cdp_guess(frame, len, 2); +} +# endif + +#endif /* defined (ENABLE_CDP) || defined (ENABLE_FDP) */ diff --git a/src/daemon/protocols/cdp.h b/src/daemon/protocols/cdp.h new file mode 100644 index 0000000..f83ffef --- /dev/null +++ b/src/daemon/protocols/cdp.h @@ -0,0 +1,70 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _CDP_H +#define _CDP_H + +#define CDP_MULTICAST_ADDR \ + { \ + 0x01, 0x00, 0x0c, 0xcc, 0xcc, 0xcc \ + } +#define FDP_MULTICAST_ADDR \ + { \ + 0x01, 0xe0, 0x52, 0xcc, 0xcc, 0xcc, \ + } +#define LLC_ORG_CISCO \ + { \ + 0x00, 0x00, 0x0c \ + } +#define LLC_ORG_FOUNDRY \ + { \ + 0x00, 0xe0, 0x52 \ + } +#define LLC_PID_CDP 0x2000 +/* Other protocols */ +#define LLC_PID_DRIP 0x102 +#define LLC_PID_PAGP 0x104 +#define LLC_PID_PVSTP 0x10b +#define LLC_PID_UDLD 0x111 +#define LLC_PID_VTP 0x2003 +#define LLC_PID_DTP 0x2004 +#define LLC_PID_STP 0x200a + +enum { + CDP_TLV_CHASSIS = 1, + CDP_TLV_ADDRESSES = 2, + CDP_TLV_PORT = 3, + CDP_TLV_CAPABILITIES = 4, + CDP_TLV_SOFTWARE = 5, + CDP_TLV_PLATFORM = 6, + CDP_TLV_NATIVEVLAN = 10, + CDP_TLV_POWER_CONSUMPTION = 0x10, + CDP_TLV_POWER_REQUESTED = 0x19, + CDP_TLV_POWER_AVAILABLE = 0x1A +}; + +#define CDP_ADDRESS_PROTO_IP 0xcc + +#define CDP_CAP_ROUTER 0x01 +#define CDP_CAP_TRANSPARENT_BRIDGE 0x02 +#define CDP_CAP_SOURCE_BRIDGE 0x04 +#define CDP_CAP_SWITCH 0x08 +#define CDP_CAP_HOST 0x10 +#define CDP_CAP_IGMP 0x20 +#define CDP_CAP_REPEATER 0x40 + +#endif /* _CDP_H */ diff --git a/src/daemon/protocols/edp.c b/src/daemon/protocols/edp.c new file mode 100644 index 0000000..b55130b --- /dev/null +++ b/src/daemon/protocols/edp.c @@ -0,0 +1,514 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "../lldpd.h" +#include "../frame.h" + +#ifdef ENABLE_EDP + +# include <stdio.h> +# include <unistd.h> +# include <errno.h> +# include <arpa/inet.h> +# include <fnmatch.h> + +static int seq = 0; + +int +edp_send(struct lldpd *global, struct lldpd_hardware *hardware) +{ + const u_int8_t mcastaddr[] = EDP_MULTICAST_ADDR; + const u_int8_t llcorg[] = LLC_ORG_EXTREME; + struct lldpd_chassis *chassis; + int length, i, v; + u_int8_t *packet, *pos, *pos_llc, *pos_len_eh, *pos_len_edp, *pos_edp, *tlv, + *end; + u_int16_t checksum; +# ifdef ENABLE_DOT1 + struct lldpd_vlan *vlan; + unsigned int state = 0; +# endif + u_int8_t edp_fakeversion[] = { 7, 6, 4, 99 }; + /* Subsequent XXX can be replaced by other values. We place + them here to ensure the position of "" to be a bit + invariant with version changes. */ + const char *deviceslot[] = { "eth", "veth", "XXX", "XXX", "XXX", "XXX", "XXX", + "XXX", "", NULL }; + + log_debug("edp", "send EDP frame on port %s", hardware->h_ifname); + + chassis = hardware->h_lport.p_chassis; +# ifdef ENABLE_DOT1 + while (state != 2) { +# endif + length = hardware->h_mtu; + if ((packet = (u_int8_t *)calloc(1, length)) == NULL) return ENOMEM; + pos = packet; + v = 0; + + /* Ethernet header */ + if (!(POKE_BYTES(mcastaddr, sizeof(mcastaddr)) && + POKE_BYTES(&hardware->h_lladdr, ETHER_ADDR_LEN) && + POKE_SAVE(pos_len_eh) && /* We compute the len later */ + POKE_UINT16(0))) + goto toobig; + + /* LLC */ + if (!(POKE_SAVE(pos_llc) && /* We need to save our + current position to + compute ethernet len */ + /* SSAP and DSAP */ + POKE_UINT8(0xaa) && POKE_UINT8(0xaa) && + /* Control field */ + POKE_UINT8(0x03) && + /* ORG */ + POKE_BYTES(llcorg, sizeof(llcorg)) && POKE_UINT16(LLC_PID_EDP))) + goto toobig; + + /* EDP header */ + if ((chassis->c_id_len != ETHER_ADDR_LEN) || + (chassis->c_id_subtype != LLDP_CHASSISID_SUBTYPE_LLADDR)) { + log_warnx("edp", + "local chassis does not use MAC address as chassis ID!?"); + free(packet); + return EINVAL; + } + if (!(POKE_SAVE(pos_edp) && /* Save the start of EDP frame */ + POKE_UINT8(1) && POKE_UINT8(0) && + POKE_SAVE(pos_len_edp) && /* We compute the len + and the checksum + later */ + POKE_UINT32(0) && /* Len + Checksum */ + POKE_UINT16(seq) && POKE_UINT16(0) && + POKE_BYTES(chassis->c_id, ETHER_ADDR_LEN))) + goto toobig; + seq++; + +# ifdef ENABLE_DOT1 + switch (state) { + case 0: +# endif + /* Display TLV */ + if (!(POKE_START_EDP_TLV(EDP_TLV_DISPLAY) && + POKE_BYTES(chassis->c_name, strlen(chassis->c_name)) && + POKE_UINT8(0) && /* Add a NULL character + for better + compatibility */ + POKE_END_EDP_TLV)) + goto toobig; + + /* Info TLV */ + if (!(POKE_START_EDP_TLV(EDP_TLV_INFO))) goto toobig; + /* We try to emulate the slot thing */ + for (i = 0; deviceslot[i] != NULL; i++) { + if (strncmp(hardware->h_ifname, deviceslot[i], + strlen(deviceslot[i])) == 0) { + if (!(POKE_UINT16(i) && + POKE_UINT16(atoi(hardware->h_ifname + + strlen(deviceslot[i]))))) + goto toobig; + break; + } + } + /* If we don't find a "slot", we say that the + interface is in slot 8 */ + if (deviceslot[i] == NULL) { + if (!(POKE_UINT16(8) && + POKE_UINT16(hardware->h_ifindex))) + goto toobig; + } + if (!(POKE_UINT16(0) && /* vchassis */ + POKE_UINT32(0) && POKE_UINT16(0) && /* Reserved */ + /* Version */ + POKE_BYTES(edp_fakeversion, sizeof(edp_fakeversion)) && + /* Connections, we say that we won't + have more interfaces than this + mask. */ + POKE_UINT32(0xffffffff) && POKE_UINT32(0) && + POKE_UINT32(0) && POKE_UINT32(0) && POKE_END_EDP_TLV)) + goto toobig; + +# ifdef ENABLE_DOT1 + break; + case 1: + TAILQ_FOREACH (vlan, &hardware->h_lport.p_vlans, v_entries) { + v++; + if (!(POKE_START_EDP_TLV(EDP_TLV_VLAN) && + POKE_UINT8(0) && /* Flags: no IP address */ + POKE_UINT8(0) && /* Reserved */ + POKE_UINT16(vlan->v_vid) && + POKE_UINT32(0) && /* Reserved */ + POKE_UINT32(0) && /* IP address */ + /* VLAN name */ + POKE_BYTES(vlan->v_name, + strlen(vlan->v_name)) && + POKE_UINT8(0) && POKE_END_EDP_TLV)) + goto toobig; + } + break; + } + + if ((state == 1) && (v == 0)) { + /* No VLAN, no need to send another TLV */ + free(packet); + break; + } +# endif + + /* Null TLV */ + if (!(POKE_START_EDP_TLV(EDP_TLV_NULL) && POKE_END_EDP_TLV && + POKE_SAVE(end))) + goto toobig; + + /* Compute len and checksum */ + i = end - pos_llc; /* Ethernet length */ + v = end - pos_edp; /* EDP length */ + POKE_RESTORE(pos_len_eh); + if (!(POKE_UINT16(i))) goto toobig; + POKE_RESTORE(pos_len_edp); + if (!(POKE_UINT16(v))) goto toobig; + checksum = frame_checksum(pos_edp, v, 0); + if (!(POKE_UINT16(checksum))) goto toobig; + + if (interfaces_send_helper(global, hardware, (char *)packet, + end - packet) == -1) { + log_warn("edp", "unable to send packet on real device for %s", + hardware->h_ifname); + free(packet); + return ENETDOWN; + } + free(packet); + +# ifdef ENABLE_DOT1 + state++; + } +# endif + + hardware->h_tx_cnt++; + return 0; +toobig: + free(packet); + return E2BIG; +} + +# define CHECK_TLV_SIZE(x, name) \ + do { \ + if (tlv_len < (x)) { \ + log_warnx("edp", name " EDP TLV too short received on %s", \ + hardware->h_ifname); \ + goto malformed; \ + } \ + } while (0) + +int +edp_decode(struct lldpd *cfg, char *frame, int s, struct lldpd_hardware *hardware, + struct lldpd_chassis **newchassis, struct lldpd_port **newport) +{ + struct lldpd_chassis *chassis; + struct lldpd_port *port; +# ifdef ENABLE_DOT1 + struct lldpd_mgmt *mgmt, *mgmt_next, *m; + struct lldpd_vlan *lvlan = NULL, *lvlan_next; +# endif + const unsigned char edpaddr[] = EDP_MULTICAST_ADDR; + int length, gotend = 0, gotvlans = 0, edp_len, tlv_len, tlv_type; + int edp_port, edp_slot; + u_int8_t *pos, *pos_edp, *tlv; + u_int8_t version[4]; +# ifdef ENABLE_DOT1 + struct in_addr address; + struct lldpd_port *oport; +# endif + + log_debug("edp", "decode EDP frame on port %s", hardware->h_ifname); + + if ((chassis = calloc(1, sizeof(struct lldpd_chassis))) == NULL) { + log_warn("edp", "failed to allocate remote chassis"); + return -1; + } + TAILQ_INIT(&chassis->c_mgmt); + if ((port = calloc(1, sizeof(struct lldpd_port))) == NULL) { + log_warn("edp", "failed to allocate remote port"); + free(chassis); + return -1; + } +# ifdef ENABLE_DOT1 + TAILQ_INIT(&port->p_vlans); +# endif + + length = s; + pos = (u_int8_t *)frame; + + if (length < 2 * ETHER_ADDR_LEN + sizeof(u_int16_t) + 8 /* LLC */ + 10 + + ETHER_ADDR_LEN /* EDP header */) { + log_warnx("edp", "too short EDP frame received on %s", + hardware->h_ifname); + goto malformed; + } + + if (PEEK_CMP(edpaddr, sizeof(edpaddr)) != 0) { + log_info("edp", + "frame not targeted at EDP multicast address received on %s", + hardware->h_ifname); + goto malformed; + } + PEEK_DISCARD(ETHER_ADDR_LEN); + PEEK_DISCARD_UINT16; + PEEK_DISCARD(6); /* LLC: DSAP + SSAP + control + org */ + if (PEEK_UINT16 != LLC_PID_EDP) { + log_debug("edp", "incorrect LLC protocol ID received on %s", + hardware->h_ifname); + goto malformed; + } + + (void)PEEK_SAVE(pos_edp); /* Save the start of EDP packet */ + if (PEEK_UINT8 != 1) { + log_warnx("edp", "incorrect EDP version for frame received on %s", + hardware->h_ifname); + goto malformed; + } + PEEK_DISCARD_UINT8; /* Reserved */ + edp_len = PEEK_UINT16; + PEEK_DISCARD_UINT16; /* Checksum */ + PEEK_DISCARD_UINT16; /* Sequence */ + if (PEEK_UINT16 != 0) { /* ID Type = 0 = MAC */ + log_warnx("edp", "incorrect device id type for frame received on %s", + hardware->h_ifname); + goto malformed; + } + if (edp_len > length + 10) { + log_warnx("edp", "incorrect size for EDP frame received on %s", + hardware->h_ifname); + goto malformed; + } + port->p_ttl = cfg ? cfg->g_config.c_tx_interval * cfg->g_config.c_tx_hold : 0; + port->p_ttl = (port->p_ttl + 999) / 1000; + chassis->c_id_subtype = LLDP_CHASSISID_SUBTYPE_LLADDR; + chassis->c_id_len = ETHER_ADDR_LEN; + if ((chassis->c_id = (char *)malloc(ETHER_ADDR_LEN)) == NULL) { + log_warn("edp", "unable to allocate memory for chassis ID"); + goto malformed; + } + PEEK_BYTES(chassis->c_id, ETHER_ADDR_LEN); + +# ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + /* Let's check checksum */ + if (frame_checksum(pos_edp, edp_len, 0) != 0) { + log_warnx("edp", "incorrect EDP checksum for frame received on %s", + hardware->h_ifname); + goto malformed; + } +# endif + + while (length && !gotend) { + if (length < 4) { + log_warnx("edp", + "EDP TLV header is too large for " + "frame received on %s", + hardware->h_ifname); + goto malformed; + } + if (PEEK_UINT8 != EDP_TLV_MARKER) { + log_warnx("edp", + "incorrect marker starting EDP TLV header for frame " + "received on %s", + hardware->h_ifname); + goto malformed; + } + tlv_type = PEEK_UINT8; + tlv_len = PEEK_UINT16 - 4; + (void)PEEK_SAVE(tlv); + if ((tlv_len < 0) || (tlv_len > length)) { + log_debug("edp", + "incorrect size in EDP TLV header for frame " + "received on %s", + hardware->h_ifname); + /* Some poor old Extreme Summit are quite bogus */ + gotend = 1; + break; + } + switch (tlv_type) { + case EDP_TLV_INFO: + CHECK_TLV_SIZE(32, "Info"); + port->p_id_subtype = LLDP_PORTID_SUBTYPE_IFNAME; + edp_slot = PEEK_UINT16; + edp_port = PEEK_UINT16; + free(port->p_id); + port->p_id_len = + asprintf(&port->p_id, "%d/%d", edp_slot + 1, edp_port + 1); + if (port->p_id_len == -1) { + log_warn("edp", + "unable to allocate memory for " + "port ID"); + goto malformed; + } + free(port->p_descr); + if (asprintf(&port->p_descr, "Slot %d / Port %d", edp_slot + 1, + edp_port + 1) == -1) { + log_warn("edp", + "unable to allocate memory for " + "port description"); + goto malformed; + } + PEEK_DISCARD_UINT16; /* vchassis */ + PEEK_DISCARD(6); /* Reserved */ + PEEK_BYTES(version, 4); + free(chassis->c_descr); + if (asprintf(&chassis->c_descr, + "EDP enabled device, version %d.%d.%d.%d", version[0], + version[1], version[2], version[3]) == -1) { + log_warn("edp", + "unable to allocate memory for " + "chassis description"); + goto malformed; + } + break; + case EDP_TLV_DISPLAY: + free(chassis->c_name); + if ((chassis->c_name = (char *)calloc(1, tlv_len + 1)) == + NULL) { + log_warn("edp", + "unable to allocate memory for chassis " + "name"); + goto malformed; + } + /* TLV display contains a lot of garbage */ + PEEK_BYTES(chassis->c_name, tlv_len); + break; + case EDP_TLV_NULL: + if (tlv_len != 0) { + log_warnx("edp", + "null tlv with incorrect size in frame " + "received on %s", + hardware->h_ifname); + goto malformed; + } + if (length) + log_debug("edp", "extra data after edp frame on %s", + hardware->h_ifname); + gotend = 1; + break; + case EDP_TLV_VLAN: +# ifdef ENABLE_DOT1 + CHECK_TLV_SIZE(12, "VLAN"); + if ((lvlan = (struct lldpd_vlan *)calloc(1, + sizeof(struct lldpd_vlan))) == NULL) { + log_warn("edp", "unable to allocate vlan"); + goto malformed; + } + PEEK_DISCARD_UINT16; /* Flags + reserved */ + lvlan->v_vid = PEEK_UINT16; /* VID */ + PEEK_DISCARD(4); /* Reserved */ + PEEK_BYTES(&address, sizeof(address)); + + if (address.s_addr != INADDR_ANY) { + mgmt = lldpd_alloc_mgmt(LLDPD_AF_IPV4, &address, + sizeof(struct in_addr), 0); + if (mgmt == NULL) { + log_warn("edp", "Out of memory"); + goto malformed; + } + TAILQ_INSERT_TAIL(&chassis->c_mgmt, mgmt, m_entries); + } + + if ((lvlan->v_name = (char *)calloc(1, tlv_len + 1 - 12)) == + NULL) { + log_warn("edp", "unable to allocate vlan name"); + goto malformed; + } + PEEK_BYTES(lvlan->v_name, tlv_len - 12); + + TAILQ_INSERT_TAIL(&port->p_vlans, lvlan, v_entries); + lvlan = NULL; +# endif + gotvlans = 1; + break; + default: + log_debug("edp", "unknown EDP TLV type (%d) received on %s", + tlv_type, hardware->h_ifname); + hardware->h_rx_unrecognized_cnt++; + } + PEEK_DISCARD(tlv + tlv_len - pos); + } + if ((chassis->c_id == NULL) || (port->p_id == NULL) || + (chassis->c_name == NULL) || (chassis->c_descr == NULL) || + (port->p_descr == NULL) || (gotend == 0)) { +# ifdef ENABLE_DOT1 + if (gotvlans && gotend) { + /* VLAN can be sent in a separate frames. We need to add + * those vlans to an existing port */ + TAILQ_FOREACH (oport, &hardware->h_rports, p_entries) { + if (!((oport->p_protocol == LLDPD_MODE_EDP) && + (oport->p_chassis->c_id_subtype == + chassis->c_id_subtype) && + (oport->p_chassis->c_id_len == + chassis->c_id_len) && + (memcmp(oport->p_chassis->c_id, chassis->c_id, + chassis->c_id_len) == 0))) + continue; + /* We attach the VLANs to the found port */ + lldpd_vlan_cleanup(oport); + for (lvlan = TAILQ_FIRST(&port->p_vlans); lvlan != NULL; + lvlan = lvlan_next) { + lvlan_next = TAILQ_NEXT(lvlan, v_entries); + TAILQ_REMOVE(&port->p_vlans, lvlan, v_entries); + TAILQ_INSERT_TAIL(&oport->p_vlans, lvlan, + v_entries); + } + /* And the IP addresses */ + for (mgmt = TAILQ_FIRST(&chassis->c_mgmt); mgmt != NULL; + mgmt = mgmt_next) { + mgmt_next = TAILQ_NEXT(mgmt, m_entries); + TAILQ_REMOVE(&chassis->c_mgmt, mgmt, m_entries); + /* Don't add an address that already exists! */ + TAILQ_FOREACH (m, &chassis->c_mgmt, m_entries) + if (m->m_family == mgmt->m_family && + !memcmp(&m->m_addr, &mgmt->m_addr, + sizeof(m->m_addr))) + break; + if (m == NULL) + TAILQ_INSERT_TAIL( + &oport->p_chassis->c_mgmt, mgmt, + m_entries); + } + } + /* We discard the remaining frame */ + goto malformed; + } +# else + if (gotvlans) goto malformed; +# endif + log_warnx("edp", + "some mandatory tlv are missing for frame received on %s", + hardware->h_ifname); + goto malformed; + } + *newchassis = chassis; + *newport = port; + return 1; + +malformed: +# ifdef ENABLE_DOT1 + free(lvlan); +# endif + lldpd_chassis_cleanup(chassis, 1); + lldpd_port_cleanup(port, 1); + free(port); + return -1; +} + +#endif /* ENABLE_EDP */ diff --git a/src/daemon/protocols/edp.h b/src/daemon/protocols/edp.h new file mode 100644 index 0000000..f4ee183 --- /dev/null +++ b/src/daemon/protocols/edp.h @@ -0,0 +1,41 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _EDP_H +#define _EDP_H + +#define EDP_MULTICAST_ADDR \ + { \ + 0x00, 0xe0, 0x2b, 0x00, 0x00, 0x00 \ + } +#define LLC_ORG_EXTREME \ + { \ + 0x00, 0xe0, 0x2b \ + } +#define LLC_PID_EDP 0x00bb + +#define EDP_TLV_MARKER 0x99 + +enum { + EDP_TLV_NULL = 0, + EDP_TLV_DISPLAY = 1, + EDP_TLV_INFO = 2, + EDP_TLV_VLAN = 5, + EDP_TLV_ESRP = 8, +}; + +#endif /* _EDP_H */ diff --git a/src/daemon/protocols/lldp.c b/src/daemon/protocols/lldp.c new file mode 100644 index 0000000..6e73237 --- /dev/null +++ b/src/daemon/protocols/lldp.c @@ -0,0 +1,1315 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "../lldpd.h" +#include "../frame.h" + +#include <unistd.h> +#include <errno.h> +#include <time.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ioctl.h> + +static int +lldpd_af_to_lldp_proto(int af) +{ + switch (af) { + case LLDPD_AF_IPV4: + return LLDP_MGMT_ADDR_IP4; + case LLDPD_AF_IPV6: + return LLDP_MGMT_ADDR_IP6; + default: + return LLDP_MGMT_ADDR_NONE; + } +} + +static int +lldpd_af_from_lldp_proto(int proto) +{ + switch (proto) { + case LLDP_MGMT_ADDR_IP4: + return LLDPD_AF_IPV4; + case LLDP_MGMT_ADDR_IP6: + return LLDPD_AF_IPV6; + default: + return LLDPD_AF_UNSPEC; + } +} + +static int +_lldp_send(struct lldpd *global, struct lldpd_hardware *hardware, u_int8_t c_id_subtype, + char *c_id, int c_id_len, u_int8_t p_id_subtype, char *p_id, int p_id_len, + int shutdown, int without_vlans) +{ + struct lldpd_port *port; + struct lldpd_chassis *chassis; + struct lldpd_frame *frame; + int length; + u_int8_t *packet, *pos, *tlv; + struct lldpd_mgmt *mgmt; + int proto; + int vlans = 0; + + u_int8_t mcastaddr_regular[] = LLDP_ADDR_NEAREST_BRIDGE; + u_int8_t mcastaddr_nontpmr[] = LLDP_ADDR_NEAREST_NONTPMR_BRIDGE; + u_int8_t mcastaddr_customer[] = LLDP_ADDR_NEAREST_CUSTOMER_BRIDGE; + u_int8_t *mcastaddr; +#ifdef ENABLE_DOT1 + const u_int8_t dot1[] = LLDP_TLV_ORG_DOT1; + struct lldpd_vlan *vlan; + struct lldpd_ppvid *ppvid; + struct lldpd_pi *pi; +#endif +#ifdef ENABLE_DOT3 + const u_int8_t dot3[] = LLDP_TLV_ORG_DOT3; +#endif +#ifdef ENABLE_LLDPMED + int i; + const u_int8_t med[] = LLDP_TLV_ORG_MED; +#endif +#ifdef ENABLE_CUSTOM + struct lldpd_custom *custom; +#endif + port = &hardware->h_lport; + chassis = port->p_chassis; + length = hardware->h_mtu; + if ((packet = (u_int8_t *)calloc(1, length)) == NULL) return ENOMEM; + pos = packet; + + /* Ethernet header */ + switch (global->g_config.c_lldp_agent_type) { + case LLDP_AGENT_TYPE_NEAREST_NONTPMR_BRIDGE: + mcastaddr = mcastaddr_nontpmr; + break; + case LLDP_AGENT_TYPE_NEAREST_CUSTOMER_BRIDGE: + mcastaddr = mcastaddr_customer; + break; + case LLDP_AGENT_TYPE_NEAREST_BRIDGE: + default: + mcastaddr = mcastaddr_regular; + break; + } + if (!( + /* LLDP multicast address */ + POKE_BYTES(mcastaddr, ETHER_ADDR_LEN) && + /* Source MAC address */ + POKE_BYTES(&hardware->h_lladdr, ETHER_ADDR_LEN))) + goto toobig; + + /* Insert VLAN tag if needed */ + if (port->p_vlan_tx_enabled) { + if (!( + /* VLAN ethertype */ + POKE_UINT16(ETHERTYPE_VLAN) && + /* VLAN Tag Control Information (TCI) */ + /* Priority(3bits) | DEI(1bit) | VID(12bit) */ + POKE_UINT16(port->p_vlan_tx_tag))) + goto toobig; + } + + if (!( + /* LLDP frame */ + POKE_UINT16(ETH_P_LLDP))) + goto toobig; + + /* Chassis ID */ + if (!(POKE_START_LLDP_TLV(LLDP_TLV_CHASSIS_ID) && POKE_UINT8(c_id_subtype) && + POKE_BYTES(c_id, c_id_len) && POKE_END_LLDP_TLV)) + goto toobig; + + /* Port ID */ + if (!(POKE_START_LLDP_TLV(LLDP_TLV_PORT_ID) && POKE_UINT8(p_id_subtype) && + POKE_BYTES(p_id, p_id_len) && POKE_END_LLDP_TLV)) + goto toobig; + + /* Time to live */ + if (!(POKE_START_LLDP_TLV(LLDP_TLV_TTL) && + POKE_UINT16(shutdown ? 0 : (global ? global->g_config.c_ttl : 180)) && + POKE_END_LLDP_TLV)) + goto toobig; + + if (shutdown) goto end; + + /* System name */ + if (chassis->c_name && *chassis->c_name != '\0') { + if (!(POKE_START_LLDP_TLV(LLDP_TLV_SYSTEM_NAME) && + POKE_BYTES(chassis->c_name, strlen(chassis->c_name)) && + POKE_END_LLDP_TLV)) + goto toobig; + } + + /* System description (skip it if empty) */ + if (chassis->c_descr && *chassis->c_descr != '\0') { + if (!(POKE_START_LLDP_TLV(LLDP_TLV_SYSTEM_DESCR) && + POKE_BYTES(chassis->c_descr, strlen(chassis->c_descr)) && + POKE_END_LLDP_TLV)) + goto toobig; + } + + /* System capabilities */ + if (global->g_config.c_cap_advertise && chassis->c_cap_available) { + if (!(POKE_START_LLDP_TLV(LLDP_TLV_SYSTEM_CAP) && + POKE_UINT16(chassis->c_cap_available) && + POKE_UINT16(chassis->c_cap_enabled) && POKE_END_LLDP_TLV)) + goto toobig; + } + + /* Management addresses */ + TAILQ_FOREACH (mgmt, &chassis->c_mgmt, m_entries) { + proto = lldpd_af_to_lldp_proto(mgmt->m_family); + if (proto == LLDP_MGMT_ADDR_NONE) continue; + if (!(POKE_START_LLDP_TLV(LLDP_TLV_MGMT_ADDR) && + /* Size of the address, including its type */ + POKE_UINT8(mgmt->m_addrsize + 1) && POKE_UINT8(proto) && + POKE_BYTES(&mgmt->m_addr, mgmt->m_addrsize))) + goto toobig; + + /* Interface port type, OID */ + if (mgmt->m_iface == 0) { + if (!( + /* We don't know the management interface */ + POKE_UINT8(LLDP_MGMT_IFACE_UNKNOWN) && POKE_UINT32(0))) + goto toobig; + } else { + if (!( + /* We have the index of the management interface */ + POKE_UINT8(LLDP_MGMT_IFACE_IFINDEX) && + POKE_UINT32(mgmt->m_iface))) + goto toobig; + } + if (!( + /* We don't provide an OID for management */ + POKE_UINT8(0) && POKE_END_LLDP_TLV)) + goto toobig; + } + + /* Port description */ + if (port->p_descr && *port->p_descr != '\0') { + if (!(POKE_START_LLDP_TLV(LLDP_TLV_PORT_DESCR) && + POKE_BYTES(port->p_descr, strlen(port->p_descr)) && + POKE_END_LLDP_TLV)) + goto toobig; + } + +#ifdef ENABLE_DOT1 + /* Port VLAN ID */ + if (port->p_pvid != 0) { + if (!(POKE_START_LLDP_TLV(LLDP_TLV_ORG) && + POKE_BYTES(dot1, sizeof(dot1)) && + POKE_UINT8(LLDP_TLV_DOT1_PVID) && POKE_UINT16(port->p_pvid) && + POKE_END_LLDP_TLV)) { + goto toobig; + } + } + /* Port and Protocol VLAN IDs */ + TAILQ_FOREACH (ppvid, &port->p_ppvids, p_entries) { + if (!(POKE_START_LLDP_TLV(LLDP_TLV_ORG) && + POKE_BYTES(dot1, sizeof(dot1)) && + POKE_UINT8(LLDP_TLV_DOT1_PPVID) && + POKE_UINT8(ppvid->p_cap_status) && + POKE_UINT16(ppvid->p_ppvid) && POKE_END_LLDP_TLV)) { + goto toobig; + } + } + /* VLANs */ + if (!without_vlans) { + TAILQ_FOREACH (vlan, &port->p_vlans, v_entries) { + vlans++; + if (!(POKE_START_LLDP_TLV(LLDP_TLV_ORG) && + POKE_BYTES(dot1, sizeof(dot1)) && + POKE_UINT8(LLDP_TLV_DOT1_VLANNAME) && + POKE_UINT16(vlan->v_vid) && + POKE_UINT8(strlen(vlan->v_name)) && + POKE_BYTES(vlan->v_name, strlen(vlan->v_name)) && + POKE_END_LLDP_TLV)) + goto toobig; + } + } + /* Protocol Identities */ + TAILQ_FOREACH (pi, &port->p_pids, p_entries) { + if (!(POKE_START_LLDP_TLV(LLDP_TLV_ORG) && + POKE_BYTES(dot1, sizeof(dot1)) && + POKE_UINT8(LLDP_TLV_DOT1_PI) && POKE_UINT8(pi->p_pi_len) && + POKE_BYTES(pi->p_pi, pi->p_pi_len) && POKE_END_LLDP_TLV)) + goto toobig; + } +#endif + +#ifdef ENABLE_DOT3 + /* Aggregation status */ + if (!(POKE_START_LLDP_TLV(LLDP_TLV_ORG) && POKE_BYTES(dot3, sizeof(dot3)) && + POKE_UINT8(LLDP_TLV_DOT3_LA) && + /* Bit 0 = capability ; Bit 1 = status */ + POKE_UINT8((port->p_aggregid) ? 3 : 1) && + POKE_UINT32(port->p_aggregid) && POKE_END_LLDP_TLV)) + goto toobig; + + /* MAC/PHY */ + if (!(POKE_START_LLDP_TLV(LLDP_TLV_ORG) && POKE_BYTES(dot3, sizeof(dot3)) && + POKE_UINT8(LLDP_TLV_DOT3_MAC) && + POKE_UINT8(port->p_macphy.autoneg_support | + (port->p_macphy.autoneg_enabled << 1)) && + POKE_UINT16(port->p_macphy.autoneg_advertised) && + POKE_UINT16(port->p_macphy.mau_type) && POKE_END_LLDP_TLV)) + goto toobig; + + /* MFS */ + if (port->p_mfs) { + if (!(POKE_START_LLDP_TLV(LLDP_TLV_ORG) && + POKE_BYTES(dot3, sizeof(dot3)) && + POKE_UINT8(LLDP_TLV_DOT3_MFS) && POKE_UINT16(port->p_mfs) && + POKE_END_LLDP_TLV)) + goto toobig; + } + /* Power */ + if (port->p_power.devicetype) { + if (!((POKE_START_LLDP_TLV(LLDP_TLV_ORG)) && + POKE_BYTES(dot3, sizeof(dot3)) && + POKE_UINT8(LLDP_TLV_DOT3_POWER) && + POKE_UINT8(((((2 - port->p_power.devicetype) % (1 << 1)) << 0) | + ((port->p_power.supported % (1 << 1)) << 1) | + ((port->p_power.enabled % (1 << 1)) << 2) | + ((port->p_power.paircontrol % (1 << 1)) << 3))) && + POKE_UINT8(port->p_power.pairs) && + POKE_UINT8(port->p_power.class))) + goto toobig; + /* 802.3at */ + if (port->p_power.powertype != LLDP_DOT3_POWER_8023AT_OFF) { + if (!(POKE_UINT8(((((port->p_power.powertype == + LLDP_DOT3_POWER_8023AT_TYPE1) ? + 1 : + 0) + << 7) | + (((port->p_power.devicetype == LLDP_DOT3_POWER_PSE) ? + 0 : + 1) + << 6) | + ((port->p_power.source % (1 << 2)) << 4) | + ((port->p_power.priority % (1 << 2)) << 0))) && + POKE_UINT16(port->p_power.requested) && + POKE_UINT16(port->p_power.allocated))) + goto toobig; + } + if (!(POKE_END_LLDP_TLV)) goto toobig; + } +#endif + +#ifdef ENABLE_LLDPMED + if (port->p_med_cap_enabled) { + /* LLDP-MED cap */ + if (port->p_med_cap_enabled & LLDP_MED_CAP_CAP) { + if (!(POKE_START_LLDP_TLV(LLDP_TLV_ORG) && + POKE_BYTES(med, sizeof(med)) && + POKE_UINT8(LLDP_TLV_MED_CAP) && + POKE_UINT16(chassis->c_med_cap_available) && + POKE_UINT8(chassis->c_med_type) && POKE_END_LLDP_TLV)) + goto toobig; + } + + /* LLDP-MED inventory */ +# define LLDP_INVENTORY(value, subtype) \ + if (value) { \ + if (!(POKE_START_LLDP_TLV(LLDP_TLV_ORG) && POKE_BYTES(med, sizeof(med)) && \ + POKE_UINT8(subtype) && \ + POKE_BYTES(value, (strlen(value) > 32) ? 32 : strlen(value)) && \ + POKE_END_LLDP_TLV)) \ + goto toobig; \ + } + + if (port->p_med_cap_enabled & LLDP_MED_CAP_IV) { + LLDP_INVENTORY(chassis->c_med_hw, LLDP_TLV_MED_IV_HW); + LLDP_INVENTORY(chassis->c_med_fw, LLDP_TLV_MED_IV_FW); + LLDP_INVENTORY(chassis->c_med_sw, LLDP_TLV_MED_IV_SW); + LLDP_INVENTORY(chassis->c_med_sn, LLDP_TLV_MED_IV_SN); + LLDP_INVENTORY(chassis->c_med_manuf, LLDP_TLV_MED_IV_MANUF); + LLDP_INVENTORY(chassis->c_med_model, LLDP_TLV_MED_IV_MODEL); + LLDP_INVENTORY(chassis->c_med_asset, LLDP_TLV_MED_IV_ASSET); + } + + /* LLDP-MED location */ + for (i = 0; i < LLDP_MED_LOCFORMAT_LAST; i++) { + if (port->p_med_location[i].format == i + 1) { + if (!(POKE_START_LLDP_TLV(LLDP_TLV_ORG) && + POKE_BYTES(med, sizeof(med)) && + POKE_UINT8(LLDP_TLV_MED_LOCATION) && + POKE_UINT8(port->p_med_location[i].format) && + POKE_BYTES(port->p_med_location[i].data, + port->p_med_location[i].data_len) && + POKE_END_LLDP_TLV)) + goto toobig; + } + } + + /* LLDP-MED network policy */ + for (i = 0; i < LLDP_MED_APPTYPE_LAST; i++) { + if (port->p_med_policy[i].type == i + 1) { + if (!(POKE_START_LLDP_TLV(LLDP_TLV_ORG) && + POKE_BYTES(med, sizeof(med)) && + POKE_UINT8(LLDP_TLV_MED_POLICY) && + POKE_UINT32(( + ((port->p_med_policy[i].type % (1 << 8)) + << 24) | + ((port->p_med_policy[i].unknown % (1 << 1)) + << 23) | + ((port->p_med_policy[i].tagged % (1 << 1)) + << 22) | + /*((0 %(1<< + 1))<<21) |*/ + ((port->p_med_policy[i].vid % (1 << 12)) + << 9) | + ((port->p_med_policy[i].priority % (1 << 3)) + << 6) | + ((port->p_med_policy[i].dscp % (1 << 6)) + << 0))) && + POKE_END_LLDP_TLV)) + goto toobig; + } + } + + /* LLDP-MED POE-MDI */ + if ((port->p_med_power.devicetype == LLDP_MED_POW_TYPE_PSE) || + (port->p_med_power.devicetype == LLDP_MED_POW_TYPE_PD)) { + int devicetype = 0, source = 0; + if (!(POKE_START_LLDP_TLV(LLDP_TLV_ORG) && + POKE_BYTES(med, sizeof(med)) && + POKE_UINT8(LLDP_TLV_MED_MDI))) + goto toobig; + switch (port->p_med_power.devicetype) { + case LLDP_MED_POW_TYPE_PSE: + devicetype = 0; + switch (port->p_med_power.source) { + case LLDP_MED_POW_SOURCE_PRIMARY: + source = 1; + break; + case LLDP_MED_POW_SOURCE_BACKUP: + source = 2; + break; + case LLDP_MED_POW_SOURCE_RESERVED: + source = 3; + break; + default: + source = 0; + break; + } + break; + case LLDP_MED_POW_TYPE_PD: + devicetype = 1; + switch (port->p_med_power.source) { + case LLDP_MED_POW_SOURCE_PSE: + source = 1; + break; + case LLDP_MED_POW_SOURCE_LOCAL: + source = 2; + break; + case LLDP_MED_POW_SOURCE_BOTH: + source = 3; + break; + default: + source = 0; + break; + } + break; + } + if (!(POKE_UINT8((((devicetype % (1 << 2)) << 6) | + ((source % (1 << 2)) << 4) | + ((port->p_med_power.priority % (1 << 4)) << 0))) && + POKE_UINT16(port->p_med_power.val) && + POKE_END_LLDP_TLV)) + goto toobig; + } + } +#endif + +#ifdef ENABLE_CUSTOM + TAILQ_FOREACH (custom, &port->p_custom_list, next) { + if (!(POKE_START_LLDP_TLV(LLDP_TLV_ORG) && + POKE_BYTES(custom->oui, sizeof(custom->oui)) && + POKE_UINT8(custom->subtype) && + POKE_BYTES(custom->oui_info, custom->oui_info_len) && + POKE_END_LLDP_TLV)) + goto toobig; + } +#endif + +end: + /* END */ + if (!(POKE_START_LLDP_TLV(LLDP_TLV_END) && POKE_END_LLDP_TLV)) goto toobig; + + if (interfaces_send_helper(global, hardware, (char *)packet, pos - packet) == + -1) { + log_warn("lldp", "unable to send packet on real device for %s", + hardware->h_ifname); + free(packet); + return ENETDOWN; + } + + hardware->h_tx_cnt++; + + /* We assume that LLDP frame is the reference */ + if (!shutdown && + (frame = (struct lldpd_frame *)malloc(sizeof(int) + pos - packet)) != + NULL) { + frame->size = pos - packet; + memcpy(&frame->frame, packet, frame->size); + if ((hardware->h_lport.p_lastframe == NULL) || + (hardware->h_lport.p_lastframe->size != frame->size) || + (memcmp(hardware->h_lport.p_lastframe->frame, frame->frame, + frame->size) != 0)) { + free(hardware->h_lport.p_lastframe); + hardware->h_lport.p_lastframe = frame; + hardware->h_lport.p_lastchange = time(NULL); + } else + free(frame); + } + + free(packet); + return 0; + +toobig: + free(packet); + if (vlans > 0 && !without_vlans) { + /* Retry without VLANs */ + return _lldp_send(global, hardware, c_id_subtype, c_id, c_id_len, + p_id_subtype, p_id, p_id_len, shutdown, 1); + } + log_info("lldp", "Cannot send LLDP packet for %s, too big message", + hardware->h_ifname); + return E2BIG; +} + +/* Send a shutdown LLDPDU. */ +int +lldp_send_shutdown(struct lldpd *global, struct lldpd_hardware *hardware) +{ + if (hardware->h_lchassis_previous_id == NULL || + hardware->h_lport_previous_id == NULL) + return 0; + return _lldp_send(global, hardware, hardware->h_lchassis_previous_id_subtype, + hardware->h_lchassis_previous_id, hardware->h_lchassis_previous_id_len, + hardware->h_lport_previous_id_subtype, hardware->h_lport_previous_id, + hardware->h_lport_previous_id_len, 1, 0); +} + +int +lldp_send(struct lldpd *global, struct lldpd_hardware *hardware) +{ + struct lldpd_port *port = &hardware->h_lport; + struct lldpd_chassis *chassis = port->p_chassis; + int ret; + + /* Check if we have a change. */ + if (hardware->h_lchassis_previous_id != NULL && + hardware->h_lport_previous_id != NULL && + (hardware->h_lchassis_previous_id_subtype != chassis->c_id_subtype || + hardware->h_lchassis_previous_id_len != chassis->c_id_len || + hardware->h_lport_previous_id_subtype != port->p_id_subtype || + hardware->h_lport_previous_id_len != port->p_id_len || + memcmp(hardware->h_lchassis_previous_id, chassis->c_id, + chassis->c_id_len) || + memcmp(hardware->h_lport_previous_id, port->p_id, port->p_id_len))) { + log_info("lldp", + "MSAP has changed for port %s, sending a shutdown LLDPDU", + hardware->h_ifname); + if ((ret = lldp_send_shutdown(global, hardware)) != 0) return ret; + } + + log_debug("lldp", "send LLDP PDU to %s", hardware->h_ifname); + + if ((ret = _lldp_send(global, hardware, chassis->c_id_subtype, chassis->c_id, + chassis->c_id_len, port->p_id_subtype, port->p_id, port->p_id_len, 0, + 0)) != 0) + return ret; + + /* Record current chassis and port ID */ + free(hardware->h_lchassis_previous_id); + hardware->h_lchassis_previous_id_subtype = chassis->c_id_subtype; + hardware->h_lchassis_previous_id_len = chassis->c_id_len; + if ((hardware->h_lchassis_previous_id = malloc(chassis->c_id_len)) != NULL) + memcpy(hardware->h_lchassis_previous_id, chassis->c_id, + chassis->c_id_len); + free(hardware->h_lport_previous_id); + hardware->h_lport_previous_id_subtype = port->p_id_subtype; + hardware->h_lport_previous_id_len = port->p_id_len; + if ((hardware->h_lport_previous_id = malloc(port->p_id_len)) != NULL) + memcpy(hardware->h_lport_previous_id, port->p_id, port->p_id_len); + + return 0; +} + +#define CHECK_TLV_SIZE(x, name) \ + do { \ + if (tlv_size < (x)) { \ + log_warnx("lldp", name " TLV too short received on %s", hardware->h_ifname); \ + goto malformed; \ + } \ + } while (0) +#define CHECK_TLV_MAX_SIZE(x, name) \ + do { \ + if (tlv_size > (x)) { \ + log_warnx("lldp", name " TLV too large received on %s", hardware->h_ifname); \ + goto malformed; \ + } \ + } while (0) + +int +lldp_decode(struct lldpd *cfg, char *frame, int s, struct lldpd_hardware *hardware, + struct lldpd_chassis **newchassis, struct lldpd_port **newport) +{ + struct lldpd_chassis *chassis; + struct lldpd_port *port; + char lldpaddr[ETHER_ADDR_LEN]; + const char dot1[] = LLDP_TLV_ORG_DOT1; + const char dot3[] = LLDP_TLV_ORG_DOT3; + const char med[] = LLDP_TLV_ORG_MED; + const char dcbx[] = LLDP_TLV_ORG_DCBX; + unsigned char orgid[3]; + int length, gotend = 0, ttl_received = 0; + int tlv_size, tlv_type, tlv_subtype, tlv_count = 0; + u_int8_t *pos, *tlv; + char *b; +#ifdef ENABLE_DOT1 + struct lldpd_vlan *vlan = NULL; + int vlan_len; + struct lldpd_ppvid *ppvid; + struct lldpd_pi *pi = NULL; +#endif + struct lldpd_mgmt *mgmt; + int af; + u_int8_t addr_str_length, addr_str_buffer[32] = { 0 }; + u_int8_t addr_family, addr_length, *addr_ptr, iface_subtype; + u_int32_t iface_number, iface; + int unrecognized; +#ifdef ENABLE_CUSTOM + struct lldpd_custom *custom = NULL; +#endif + + log_debug("lldp", "receive LLDP PDU on %s", hardware->h_ifname); + + if ((chassis = calloc(1, sizeof(struct lldpd_chassis))) == NULL) { + log_warn("lldp", "failed to allocate remote chassis"); + return -1; + } + TAILQ_INIT(&chassis->c_mgmt); + if ((port = calloc(1, sizeof(struct lldpd_port))) == NULL) { + log_warn("lldp", "failed to allocate remote port"); + free(chassis); + return -1; + } +#ifdef ENABLE_DOT1 + TAILQ_INIT(&port->p_vlans); + TAILQ_INIT(&port->p_ppvids); + TAILQ_INIT(&port->p_pids); +#endif +#ifdef ENABLE_CUSTOM + TAILQ_INIT(&port->p_custom_list); +#endif + + length = s; + pos = (u_int8_t *)frame; + + if (length < 2 * ETHER_ADDR_LEN + sizeof(u_int16_t)) { + log_warnx("lldp", "too short frame received on %s", hardware->h_ifname); + goto malformed; + } + PEEK_BYTES(lldpaddr, ETHER_ADDR_LEN); + if (memcmp(lldpaddr, (const char[])LLDP_ADDR_NEAREST_BRIDGE, ETHER_ADDR_LEN) && + memcmp(lldpaddr, (const char[])LLDP_ADDR_NEAREST_NONTPMR_BRIDGE, + ETHER_ADDR_LEN) && + memcmp(lldpaddr, (const char[])LLDP_ADDR_NEAREST_CUSTOMER_BRIDGE, + ETHER_ADDR_LEN)) { + log_info("lldp", + "frame not targeted at LLDP multicast address received on %s", + hardware->h_ifname); + goto malformed; + } + PEEK_DISCARD(ETHER_ADDR_LEN); /* Skip source address */ + if (PEEK_UINT16 != ETH_P_LLDP) { + log_info("lldp", "non LLDP frame received on %s", hardware->h_ifname); + goto malformed; + } + + while (length && (!gotend)) { + if (length < 2) { + log_warnx("lldp", "tlv header too short received on %s", + hardware->h_ifname); + goto malformed; + } + tlv_size = PEEK_UINT16; + tlv_type = tlv_size >> 9; + tlv_size = tlv_size & 0x1ff; + (void)PEEK_SAVE(tlv); + if (length < tlv_size) { + log_warnx("lldp", "frame too short for tlv received on %s", + hardware->h_ifname); + goto malformed; + } + /* Check order for mandatory TLVs */ + tlv_count++; + switch (tlv_type) { + case LLDP_TLV_CHASSIS_ID: + if (tlv_count != 1) { + log_warnx("lldp", + "Chassis ID TLV should be first on %s, but it is on position %d", + hardware->h_ifname, tlv_count); + goto malformed; + } + break; + case LLDP_TLV_PORT_ID: + if (tlv_count != 2) { + log_warnx("lldp", + "Port ID TLV should be second on %s, but it is on position %d", + hardware->h_ifname, tlv_count); + goto malformed; + } + break; + case LLDP_TLV_TTL: + if (tlv_count != 3) { + log_warnx("lldp", + "TTL TLV should be third on %s, but it is on position %d", + hardware->h_ifname, tlv_count); + goto malformed; + } + break; + } + + switch (tlv_type) { + case LLDP_TLV_END: + if (tlv_size != 0) { + log_warnx("lldp", + "lldp end received with size not null on %s", + hardware->h_ifname); + goto malformed; + } + if (length) + log_debug("lldp", "extra data after lldp end on %s", + hardware->h_ifname); + gotend = 1; + break; + case LLDP_TLV_CHASSIS_ID: + case LLDP_TLV_PORT_ID: + CHECK_TLV_SIZE(2, "Port/Chassis Id"); + CHECK_TLV_MAX_SIZE(256, "Port/Chassis Id"); + tlv_subtype = PEEK_UINT8; + if ((tlv_subtype == 0) || (tlv_subtype > 7)) { + log_warnx("lldp", + "unknown subtype for tlv id received on %s", + hardware->h_ifname); + goto malformed; + } + if ((b = (char *)calloc(1, tlv_size - 1)) == NULL) { + log_warn("lldp", + "unable to allocate memory for id tlv " + "received on %s", + hardware->h_ifname); + goto malformed; + } + PEEK_BYTES(b, tlv_size - 1); + if (tlv_type == LLDP_TLV_PORT_ID) { + if (port->p_id != NULL) { + log_warnx("lldp", + "Port ID TLV received twice on %s", + hardware->h_ifname); + free(b); + goto malformed; + } + port->p_id_subtype = tlv_subtype; + port->p_id = b; + port->p_id_len = tlv_size - 1; + } else { + if (chassis->c_id != NULL) { + log_warnx("lldp", + "Chassis ID TLV received twice on %s", + hardware->h_ifname); + free(b); + goto malformed; + } + chassis->c_id_subtype = tlv_subtype; + chassis->c_id = b; + chassis->c_id_len = tlv_size - 1; + } + break; + case LLDP_TLV_TTL: + if (ttl_received) { + log_warnx("lldp", "TTL TLV received twice on %s", + hardware->h_ifname); + goto malformed; + } + CHECK_TLV_SIZE(2, "TTL"); + port->p_ttl = PEEK_UINT16; + ttl_received = 1; + break; + case LLDP_TLV_PORT_DESCR: + case LLDP_TLV_SYSTEM_NAME: + case LLDP_TLV_SYSTEM_DESCR: + if (tlv_size < 1) { + log_debug("lldp", "empty tlv received on %s", + hardware->h_ifname); + break; + } + if ((b = (char *)calloc(1, tlv_size + 1)) == NULL) { + log_warn("lldp", + "unable to allocate memory for string tlv " + "received on %s", + hardware->h_ifname); + goto malformed; + } + PEEK_BYTES(b, tlv_size); + switch (tlv_type) { + case LLDP_TLV_PORT_DESCR: + free(port->p_descr); + port->p_descr = b; + break; + case LLDP_TLV_SYSTEM_NAME: + free(chassis->c_name); + chassis->c_name = b; + break; + case LLDP_TLV_SYSTEM_DESCR: + free(chassis->c_descr); + chassis->c_descr = b; + break; + default: + /* unreachable */ + free(b); + break; + } + break; + case LLDP_TLV_SYSTEM_CAP: + CHECK_TLV_SIZE(4, "System capabilities"); + chassis->c_cap_available = PEEK_UINT16; + chassis->c_cap_enabled = PEEK_UINT16; + break; + case LLDP_TLV_MGMT_ADDR: + CHECK_TLV_SIZE(1, "Management address"); + addr_str_length = PEEK_UINT8; + if (addr_str_length > sizeof(addr_str_buffer)) { + log_warnx("lldp", "too large management address on %s", + hardware->h_ifname); + goto malformed; + } + CHECK_TLV_SIZE(1 + addr_str_length, "Management address"); + PEEK_BYTES(addr_str_buffer, addr_str_length); + addr_length = addr_str_length - 1; + addr_family = addr_str_buffer[0]; + addr_ptr = &addr_str_buffer[1]; + CHECK_TLV_SIZE(1 + addr_str_length + 5, "Management address"); + iface_subtype = PEEK_UINT8; + iface_number = PEEK_UINT32; + + af = lldpd_af_from_lldp_proto(addr_family); + if (af == LLDPD_AF_UNSPEC) break; + if (iface_subtype == LLDP_MGMT_IFACE_IFINDEX) + iface = iface_number; + else + iface = 0; + mgmt = lldpd_alloc_mgmt(af, addr_ptr, addr_length, iface); + if (mgmt == NULL) { + if (errno == ENOMEM) + log_warn("lldp", + "unable to allocate memory " + "for management address"); + else + log_warn("lldp", + "too large management address " + "received on %s", + hardware->h_ifname); + goto malformed; + } + TAILQ_INSERT_TAIL(&chassis->c_mgmt, mgmt, m_entries); + break; + case LLDP_TLV_ORG: + CHECK_TLV_SIZE(1 + (int)sizeof(orgid), "Organisational"); + PEEK_BYTES(orgid, sizeof(orgid)); + unrecognized = 0; + tlv_subtype = PEEK_UINT8; + if (memcmp(dot1, orgid, sizeof(orgid)) == 0) { +#ifndef ENABLE_DOT1 + unrecognized = 1; +#else + /* Dot1 */ + switch (tlv_subtype) { + case LLDP_TLV_DOT1_VLANNAME: + CHECK_TLV_SIZE(7, "VLAN"); + if ((vlan = (struct lldpd_vlan *)calloc(1, + sizeof(struct lldpd_vlan))) == NULL) { + log_warn("lldp", + "unable to alloc vlan " + "structure for " + "tlv received on %s", + hardware->h_ifname); + goto malformed; + } + vlan->v_vid = PEEK_UINT16; + vlan_len = PEEK_UINT8; + CHECK_TLV_SIZE(7 + vlan_len, "VLAN"); + if ((vlan->v_name = (char *)calloc(1, + vlan_len + 1)) == NULL) { + log_warn("lldp", + "unable to alloc vlan name for " + "tlv received on %s", + hardware->h_ifname); + goto malformed; + } + PEEK_BYTES(vlan->v_name, vlan_len); + TAILQ_INSERT_TAIL(&port->p_vlans, vlan, + v_entries); + vlan = NULL; + break; + case LLDP_TLV_DOT1_PVID: + CHECK_TLV_SIZE(6, "PVID"); + port->p_pvid = PEEK_UINT16; + break; + case LLDP_TLV_DOT1_PPVID: + CHECK_TLV_SIZE(7, "PPVID"); + /* validation needed */ + /* PPVID has to be unique if more than + one PPVID TLVs are received - + discard if duplicate */ + /* if support bit is not set and + enabled bit is set - PPVID TLV is + considered error and discarded */ + /* if PPVID > 4096 - bad and discard */ + if ((ppvid = (struct lldpd_ppvid *)calloc(1, + sizeof(struct lldpd_ppvid))) == NULL) { + log_warn("lldp", + "unable to alloc ppvid " + "structure for " + "tlv received on %s", + hardware->h_ifname); + goto malformed; + } + ppvid->p_cap_status = PEEK_UINT8; + ppvid->p_ppvid = PEEK_UINT16; + TAILQ_INSERT_TAIL(&port->p_ppvids, ppvid, + p_entries); + break; + case LLDP_TLV_DOT1_PI: + /* validation needed */ + /* PI has to be unique if more than + one PI TLVs are received - discard + if duplicate ?? */ + CHECK_TLV_SIZE(5, "PI"); + if ((pi = (struct lldpd_pi *)calloc(1, + sizeof(struct lldpd_pi))) == NULL) { + log_warn("lldp", + "unable to alloc PI " + "structure for " + "tlv received on %s", + hardware->h_ifname); + goto malformed; + } + pi->p_pi_len = PEEK_UINT8; + CHECK_TLV_SIZE(5 + pi->p_pi_len, "PI"); + if ((pi->p_pi = (char *)calloc(1, + pi->p_pi_len)) == NULL) { + log_warn("lldp", + "unable to alloc pid name for " + "tlv received on %s", + hardware->h_ifname); + goto malformed; + } + PEEK_BYTES(pi->p_pi, pi->p_pi_len); + TAILQ_INSERT_TAIL(&port->p_pids, pi, p_entries); + pi = NULL; + break; + default: + /* Unknown Dot1 TLV, ignore it */ + unrecognized = 1; + } +#endif + } else if (memcmp(dot3, orgid, sizeof(orgid)) == 0) { +#ifndef ENABLE_DOT3 + unrecognized = 1; +#else + /* Dot3 */ + switch (tlv_subtype) { + case LLDP_TLV_DOT3_MAC: + CHECK_TLV_SIZE(9, "MAC/PHY"); + port->p_macphy.autoneg_support = PEEK_UINT8; + port->p_macphy.autoneg_enabled = + (port->p_macphy.autoneg_support & 0x2) >> 1; + port->p_macphy.autoneg_support = + port->p_macphy.autoneg_support & 0x1; + port->p_macphy.autoneg_advertised = PEEK_UINT16; + port->p_macphy.mau_type = PEEK_UINT16; + break; + case LLDP_TLV_DOT3_LA: + CHECK_TLV_SIZE(9, "Link aggregation"); + PEEK_DISCARD_UINT8; + port->p_aggregid = PEEK_UINT32; + break; + case LLDP_TLV_DOT3_MFS: + CHECK_TLV_SIZE(6, "MFS"); + port->p_mfs = PEEK_UINT16; + break; + case LLDP_TLV_DOT3_POWER: + CHECK_TLV_SIZE(7, "Power"); + port->p_power.devicetype = PEEK_UINT8; + port->p_power.supported = + (port->p_power.devicetype & 0x2) >> 1; + port->p_power.enabled = + (port->p_power.devicetype & 0x4) >> 2; + port->p_power.paircontrol = + (port->p_power.devicetype & 0x8) >> 3; + port->p_power.devicetype = + (port->p_power.devicetype & 0x1) ? + LLDP_DOT3_POWER_PSE : + LLDP_DOT3_POWER_PD; + port->p_power.pairs = PEEK_UINT8; + port->p_power.class = PEEK_UINT8; + /* 802.3at? */ + if (tlv_size >= 12) { + port->p_power.powertype = PEEK_UINT8; + port->p_power.source = + (port->p_power.powertype & + (1 << 5 | 1 << 4)) >> + 4; + port->p_power.priority = + (port->p_power.powertype & + (1 << 1 | 1 << 0)); + port->p_power.powertype = + (port->p_power.powertype & + (1 << 7)) ? + LLDP_DOT3_POWER_8023AT_TYPE1 : + LLDP_DOT3_POWER_8023AT_TYPE2; + port->p_power.requested = PEEK_UINT16; + port->p_power.allocated = PEEK_UINT16; + } else + port->p_power.powertype = + LLDP_DOT3_POWER_8023AT_OFF; + /* 802.3bt? */ + if (tlv_size >= 29) { + port->p_power.requested_a = PEEK_UINT16; + port->p_power.requested_b = PEEK_UINT16; + port->p_power.allocated_a = PEEK_UINT16; + port->p_power.allocated_b = PEEK_UINT16; + port->p_power.pse_status = PEEK_UINT16; + port->p_power.pd_status = + (port->p_power.pse_status & + (1 << 13 | 1 << 12)) >> + 12; + port->p_power.pse_pairs_ext = + (port->p_power.pse_status & + (1 << 11 | 1 << 10)) >> + 10; + port->p_power.class_a = + (port->p_power.pse_status & + (1 << 9 | 1 << 8 | 1 << 7)) >> + 7; + port->p_power.class_b = + (port->p_power.pse_status & + (1 << 6 | 1 << 5 | 1 << 4)) >> + 4; + port->p_power.class_ext = + (port->p_power.pse_status & 0xf); + port->p_power.pse_status = + (port->p_power.pse_status & + (1 << 15 | 1 << 14)) >> + 14; + port->p_power.type_ext = PEEK_UINT8; + port->p_power.pd_load = + (port->p_power.type_ext & 0x1); + port->p_power.type_ext = + ((port->p_power.type_ext & + (1 << 3 | 1 << 2 | 1 << 1)) + + 1); + port->p_power.pse_max = PEEK_UINT16; + } else { + port->p_power.type_ext = + LLDP_DOT3_POWER_8023BT_OFF; + } + break; + default: + /* Unknown Dot3 TLV, ignore it */ + unrecognized = 1; + } +#endif + } else if (memcmp(med, orgid, sizeof(orgid)) == 0) { + /* LLDP-MED */ +#ifndef ENABLE_LLDPMED + unrecognized = 1; +#else + u_int32_t policy; + unsigned loctype; + unsigned power; + + switch (tlv_subtype) { + case LLDP_TLV_MED_CAP: + CHECK_TLV_SIZE(7, "LLDP-MED capabilities"); + chassis->c_med_cap_available = PEEK_UINT16; + chassis->c_med_type = PEEK_UINT8; + port->p_med_cap_enabled |= LLDP_MED_CAP_CAP; + break; + case LLDP_TLV_MED_POLICY: + CHECK_TLV_SIZE(8, "LLDP-MED policy"); + policy = PEEK_UINT32; + if (((policy >> 24) < 1) || + ((policy >> 24) > LLDP_MED_APPTYPE_LAST)) { + log_info("lldp", + "unknown policy field %d " + "received on %s", + policy, hardware->h_ifname); + break; + } + port->p_med_policy[(policy >> 24) - 1].type = + (policy >> 24); + port->p_med_policy[(policy >> 24) - 1].unknown = + ((policy & 0x800000) != 0); + port->p_med_policy[(policy >> 24) - 1].tagged = + ((policy & 0x400000) != 0); + port->p_med_policy[(policy >> 24) - 1].vid = + (policy & 0x001FFE00) >> 9; + port->p_med_policy[(policy >> 24) - 1] + .priority = (policy & 0x1C0) >> 6; + port->p_med_policy[(policy >> 24) - 1].dscp = + policy & 0x3F; + port->p_med_cap_enabled |= LLDP_MED_CAP_POLICY; + break; + case LLDP_TLV_MED_LOCATION: + CHECK_TLV_SIZE(5, "LLDP-MED Location"); + loctype = PEEK_UINT8; + if ((loctype < 1) || + (loctype > LLDP_MED_LOCFORMAT_LAST)) { + log_info("lldp", + "unknown location type " + "received on %s", + hardware->h_ifname); + break; + } + free(port->p_med_location[loctype - 1].data); + if ((port->p_med_location[loctype - 1].data = + (char *)malloc(tlv_size - 5)) == + NULL) { + log_warn("lldp", + "unable to allocate memory " + "for LLDP-MED location for " + "frame received on %s", + hardware->h_ifname); + goto malformed; + } + PEEK_BYTES( + port->p_med_location[loctype - 1].data, + tlv_size - 5); + port->p_med_location[loctype - 1].data_len = + tlv_size - 5; + port->p_med_location[loctype - 1].format = + loctype; + port->p_med_cap_enabled |= + LLDP_MED_CAP_LOCATION; + break; + case LLDP_TLV_MED_MDI: + CHECK_TLV_SIZE(7, "LLDP-MED PoE-MDI"); + power = PEEK_UINT8; + switch (power & 0xC0) { + case 0x0: + port->p_med_power.devicetype = + LLDP_MED_POW_TYPE_PSE; + port->p_med_cap_enabled |= + LLDP_MED_CAP_MDI_PSE; + switch (power & 0x30) { + case 0x0: + port->p_med_power.source = + LLDP_MED_POW_SOURCE_UNKNOWN; + break; + case 0x10: + port->p_med_power.source = + LLDP_MED_POW_SOURCE_PRIMARY; + break; + case 0x20: + port->p_med_power.source = + LLDP_MED_POW_SOURCE_BACKUP; + break; + default: + port->p_med_power.source = + LLDP_MED_POW_SOURCE_RESERVED; + } + break; + case 0x40: + port->p_med_power.devicetype = + LLDP_MED_POW_TYPE_PD; + port->p_med_cap_enabled |= + LLDP_MED_CAP_MDI_PD; + switch (power & 0x30) { + case 0x0: + port->p_med_power.source = + LLDP_MED_POW_SOURCE_UNKNOWN; + break; + case 0x10: + port->p_med_power.source = + LLDP_MED_POW_SOURCE_PSE; + break; + case 0x20: + port->p_med_power.source = + LLDP_MED_POW_SOURCE_LOCAL; + break; + default: + port->p_med_power.source = + LLDP_MED_POW_SOURCE_BOTH; + } + break; + default: + port->p_med_power.devicetype = + LLDP_MED_POW_TYPE_RESERVED; + } + if ((power & 0x0F) > LLDP_MED_POW_PRIO_LOW) + port->p_med_power.priority = + LLDP_MED_POW_PRIO_UNKNOWN; + else + port->p_med_power.priority = + power & 0x0F; + port->p_med_power.val = PEEK_UINT16; + break; + case LLDP_TLV_MED_IV_HW: + case LLDP_TLV_MED_IV_SW: + case LLDP_TLV_MED_IV_FW: + case LLDP_TLV_MED_IV_SN: + case LLDP_TLV_MED_IV_MANUF: + case LLDP_TLV_MED_IV_MODEL: + case LLDP_TLV_MED_IV_ASSET: + if (tlv_size <= 4) + b = NULL; + else { + if ((b = (char *)malloc( + tlv_size - 3)) == NULL) { + log_warn("lldp", + "unable to allocate " + "memory for LLDP-MED " + "inventory for frame " + "received on %s", + hardware->h_ifname); + goto malformed; + } + PEEK_BYTES(b, tlv_size - 4); + b[tlv_size - 4] = '\0'; + } + switch (tlv_subtype) { + case LLDP_TLV_MED_IV_HW: + free(chassis->c_med_hw); + chassis->c_med_hw = b; + break; + case LLDP_TLV_MED_IV_FW: + free(chassis->c_med_fw); + chassis->c_med_fw = b; + break; + case LLDP_TLV_MED_IV_SW: + free(chassis->c_med_sw); + chassis->c_med_sw = b; + break; + case LLDP_TLV_MED_IV_SN: + free(chassis->c_med_sn); + chassis->c_med_sn = b; + break; + case LLDP_TLV_MED_IV_MANUF: + free(chassis->c_med_manuf); + chassis->c_med_manuf = b; + break; + case LLDP_TLV_MED_IV_MODEL: + free(chassis->c_med_model); + chassis->c_med_model = b; + break; + case LLDP_TLV_MED_IV_ASSET: + free(chassis->c_med_asset); + chassis->c_med_asset = b; + break; + default: + /* unreachable */ + free(b); + break; + } + port->p_med_cap_enabled |= LLDP_MED_CAP_IV; + break; + default: + /* Unknown LLDP MED, ignore it */ + hardware->h_rx_unrecognized_cnt++; + } +#endif /* ENABLE_LLDPMED */ + } else if (memcmp(dcbx, orgid, sizeof(orgid)) == 0) { + log_debug("lldp", + "unsupported DCBX tlv received on %s - ignore", + hardware->h_ifname); + unrecognized = 1; + } else { + log_debug("lldp", + "unknown org tlv [%02x:%02x:%02x] received on %s", + orgid[0], orgid[1], orgid[2], hardware->h_ifname); + unrecognized = 1; + } + if (unrecognized) { + hardware->h_rx_unrecognized_cnt++; +#ifdef ENABLE_CUSTOM + custom = (struct lldpd_custom *)calloc(1, + sizeof(struct lldpd_custom)); + if (!custom) { + log_warn("lldp", + "unable to allocate memory for custom TLV"); + goto malformed; + } + custom->oui_info_len = tlv_size > 4 ? tlv_size - 4 : 0; + memcpy(custom->oui, orgid, sizeof(custom->oui)); + custom->subtype = tlv_subtype; + if (custom->oui_info_len > 0) { + custom->oui_info = malloc(custom->oui_info_len); + if (!custom->oui_info) { + log_warn("lldp", + "unable to allocate memory for custom TLV data"); + goto malformed; + } + PEEK_BYTES(custom->oui_info, + custom->oui_info_len); + } + TAILQ_INSERT_TAIL(&port->p_custom_list, custom, next); + custom = NULL; +#endif + } + break; + default: + log_warnx("lldp", "unknown tlv (%d) received on %s", tlv_type, + hardware->h_ifname); + hardware->h_rx_unrecognized_cnt++; + break; + } + if (pos > tlv + tlv_size) { + log_warnx("lldp", "BUG: already past TLV!"); + goto malformed; + } + PEEK_DISCARD(tlv + tlv_size - pos); + } + + /* Some random check */ + if ((chassis->c_id == NULL) || (port->p_id == NULL) || (!ttl_received) || + (gotend == 0)) { + log_warnx("lldp", + "some mandatory tlv are missing for frame received on %s", + hardware->h_ifname); + goto malformed; + } + *newchassis = chassis; + *newport = port; + return 1; +malformed: +#ifdef ENABLE_CUSTOM + free(custom); +#endif +#ifdef ENABLE_DOT1 + free(vlan); + free(pi); +#endif + lldpd_chassis_cleanup(chassis, 1); + lldpd_port_cleanup(port, 1); + free(port); + return -1; +} diff --git a/src/daemon/protocols/sonmp.c b/src/daemon/protocols/sonmp.c new file mode 100644 index 0000000..34ebcd7 --- /dev/null +++ b/src/daemon/protocols/sonmp.c @@ -0,0 +1,410 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "../lldpd.h" +#include "../frame.h" + +#ifdef ENABLE_SONMP + +# include <stdio.h> +# include <unistd.h> +# include <errno.h> +# include <arpa/inet.h> + +static struct sonmp_chassis sonmp_chassis_types[] = { + { 1, "unknown (via SONMP)" }, + { 2, "Nortel 3000" }, + { 3, "Nortel 3030" }, + { 4, "Nortel 2310" }, + { 5, "Nortel 2810" }, + { 6, "Nortel 2912" }, + { 7, "Nortel 2914" }, + { 8, "Nortel 271x" }, + { 9, "Nortel 2813" }, + { 10, "Nortel 2814" }, + { 11, "Nortel 2915" }, + { 12, "Nortel 5000" }, + { 13, "Nortel 2813SA" }, + { 14, "Nortel 2814SA" }, + { 15, "Nortel 810M" }, + { 16, "Nortel EtherCell" }, + { 17, "Nortel 5005" }, + { 18, "Alcatel Ethernet workgroup conc." }, + { 20, "Nortel 2715SA" }, + { 21, "Nortel 2486" }, + { 22, "Nortel 28000 series" }, + { 23, "Nortel 23000 series" }, + { 24, "Nortel 5DN00x series" }, + { 25, "BayStack Ethernet" }, + { 26, "Nortel 23100 series" }, + { 27, "Nortel 100Base-T Hub" }, + { 28, "Nortel 3000 Fast Ethernet" }, + { 29, "Nortel Orion switch" }, + { 30, "unknown" }, + { 31, "Nortel DDS " }, + { 32, "Nortel Centillion" }, + { 33, "Nortel Centillion" }, + { 34, "Nortel Centillion" }, + { 35, "BayStack 301" }, + { 36, "BayStack TokenRing Hub" }, + { 37, "Nortel FVC Multimedia Switch" }, + { 38, "Nortel Switch Node" }, + { 39, "BayStack 302 Switch" }, + { 40, "BayStack 350 Switch" }, + { 41, "BayStack 150 Ethernet Hub" }, + { 42, "Nortel Centillion 50N switch" }, + { 43, "Nortel Centillion 50T switch" }, + { 44, "BayStack 303 and 304 Switches" }, + { 45, "BayStack 200 Ethernet Hub" }, + { 46, "BayStack 250 10/100 Ethernet Hub" }, + { 48, "BayStack 450 10/100/1000 Switches" }, + { 49, "BayStack 410 10/100 Switches" }, + { 50, "Nortel Ethernet Routing 1200 L3 Switch" }, + { 51, "Nortel Ethernet Routing 1250 L3 Switch" }, + { 52, "Nortel Ethernet Routing 1100 L3 Switch" }, + { 53, "Nortel Ethernet Routing 1150 L3 Switch" }, + { 54, "Nortel Ethernet Routing 1050 L3 Switch" }, + { 55, "Nortel Ethernet Routing 1051 L3 Switch" }, + { 56, "Nortel Ethernet Routing 8610 L3 Switch" }, + { 57, "Nortel Ethernet Routing 8606 L3 Switch" }, + { 58, "Nortel Ethernet Routing Switch 8010" }, + { 59, "Nortel Ethernet Routing Switch 8006" }, + { 60, "BayStack 670 wireless access point" }, + { 61, "Nortel Ethernet Routing Switch 740 " }, + { 62, "Nortel Ethernet Routing Switch 750 " }, + { 63, "Nortel Ethernet Routing Switch 790" }, + { 64, "Nortel Business Policy Switch 2000 10/100 Switches" }, + { 65, "Nortel Ethernet Routing 8110 L2 Switch" }, + { 66, "Nortel Ethernet Routing 8106 L2 Switch" }, + { 67, "BayStack 3580 Gig Switch" }, + { 68, "BayStack 10 Power Supply Unit" }, + { 69, "BayStack 420 10/100 Switch" }, + { 70, "OPTera Metro 1200 Ethernet Service Module" }, + { 71, "Nortel Ethernet Routing Switch 8010co" }, + { 72, "Nortel Ethernet Routing 8610co L3 switch" }, + { 73, "Nortel Ethernet Routing 8110co L2 switch" }, + { 74, "Nortel Ethernet Routing 8003" }, + { 75, "Nortel Ethernet Routing 8603 L3 switch" }, + { 76, "Nortel Ethernet Routing 8103 L2 switch" }, + { 77, "BayStack 380 10/100/1000 Switch" }, + { 78, "Nortel Ethernet Switch 470-48T" }, + { 79, "OPTera Metro 1450 Ethernet Service Module" }, + { 80, "OPTera Metro 1400 Ethernet Service Module" }, + { 81, "Alteon Switch Family" }, + { 82, "Ethernet Switch 460-24T-PWR" }, + { 83, "OPTera Metro 8010 OPM L2 Switch" }, + { 84, "OPTera Metro 8010co OPM L2 Switch" }, + { 85, "OPTera Metro 8006 OPM L2 Switch" }, + { 86, "OPTera Metro 8003 OPM L2 Switch" }, + { 87, "Alteon 180e" }, + { 88, "Alteon AD3" }, + { 89, "Alteon 184" }, + { 90, "Alteon AD4" }, + { 91, "Nortel Ethernet Routing 1424 L3 switch" }, + { 92, "Nortel Ethernet Routing 1648 L3 switch" }, + { 93, "Nortel Ethernet Routing 1612 L3 switch" }, + { 94, "Nortel Ethernet Routing 1624 L3 switch " }, + { 95, "BayStack 380-24F Fiber 1000 Switch" }, + { 96, "Nortel Ethernet Routing Switch 5510-24T" }, + { 97, "Nortel Ethernet Routing Switch 5510-48T" }, + { 98, "Nortel Ethernet Switch 470-24T" }, + { 99, "Nortel Networks Wireless LAN Access Point 2220" }, + { 100, "Ethernet Routing RBS 2402 L3 switch" }, + { 101, "Alteon Application Switch 2424 " }, + { 102, "Alteon Application Switch 2224 " }, + { 103, "Alteon Application Switch 2208 " }, + { 104, "Alteon Application Switch 2216" }, + { 105, "Alteon Application Switch 3408" }, + { 106, "Alteon Application Switch 3416" }, + { 107, "Nortel Networks Wireless LAN SecuritySwitch 2250" }, + { 108, "Ethernet Switch 425-48T" }, + { 109, "Ethernet Switch 425-24T" }, + { 110, "Nortel Networks Wireless LAN Access Point 2221" }, + { 111, "Nortel Metro Ethernet Service Unit 24-T SPF switch" }, + { 112, "Nortel Metro Ethernet Service Unit 24-T LX DC switch" }, + { 113, "Nortel Ethernet Routing Switch 8300 10-slot chassis" }, + { 114, "Nortel Ethernet Routing Switch 8300 6-slot chassis" }, + { 115, "Nortel Ethernet Routing Switch 5520-24T-PWR" }, + { 116, "Nortel Ethernet Routing Switch 5520-48T-PWR" }, + { 117, "Nortel Networks VPN Gateway 3050" }, + { 118, "Alteon SSL 310 10/100" }, + { 119, "Alteon SSL 310 10/100 Fiber" }, + { 120, "Alteon SSL 310 10/100 FIPS" }, + { 121, "Alteon SSL 410 10/100/1000" }, + { 122, "Alteon SSL 410 10/100/1000 Fiber" }, + { 123, "Alteon Application Switch 2424-SSL" }, + { 124, "Nortel Ethernet Switch 325-24T" }, + { 125, "Nortel Ethernet Switch 325-24G" }, + { 126, "Nortel Networks Wireless LAN Access Point 2225" }, + { 127, "Nortel Networks Wireless LAN SecuritySwitch 2270" }, + { 128, "Nortel 24-port Ethernet Switch 470-24T-PWR" }, + { 129, "Nortel 48-port Ethernet Switch 470-48T-PWR" }, + { 130, "Nortel Ethernet Routing Switch 5530-24TFD" }, + { 131, "Nortel Ethernet Switch 3510-24T" }, + { 132, "Nortel Metro Ethernet Service Unit 12G AC L3 switch" }, + { 133, "Nortel Metro Ethernet Service Unit 12G DC L3 switch" }, + { 134, "Nortel Secure Access Switch" }, + { 135, "Networks VPN Gateway 3070" }, + { 136, "OPTera Metro 3500" }, + { 137, "SMB BES 1010 24T" }, + { 138, "SMB BES 1010 48T" }, + { 139, "SMB BES 1020 24T PWR" }, + { 140, "SMB BES 1020 48T PWR" }, + { 141, "SMB BES 2010 24T" }, + { 142, "SMB BES 2010 48T" }, + { 143, "SMB BES 2020 24T PWR" }, + { 144, "SMB BES 2020 48T PWR" }, + { 145, "SMB BES 110 24T" }, + { 146, "SMB BES 110 48T" }, + { 147, "SMB BES 120 24T PWR" }, + { 148, "SMB BES 120 48T PWR" }, + { 149, "SMB BES 210 24T" }, + { 150, "SMB BES 210 48T" }, + { 151, "SMB BES 220 24T PWR" }, + { 152, "SMB BES 220 48T PWR" }, + { 153, "OME 6500" }, + { 0, "unknown (via SONMP)" }, +}; + +int +sonmp_send(struct lldpd *global, struct lldpd_hardware *hardware) +{ + const u_int8_t mcastaddr[] = SONMP_MULTICAST_ADDR; + const u_int8_t llcorg[] = LLC_ORG_NORTEL; + struct lldpd_chassis *chassis; + struct lldpd_mgmt *mgmt; + u_int8_t *packet, *pos, *pos_pid, *end; + int length; + struct in_addr address; + + log_debug("sonmp", "send SONMP PDU to %s", hardware->h_ifname); + + chassis = hardware->h_lport.p_chassis; + length = hardware->h_mtu; + if ((packet = (u_int8_t *)calloc(1, length)) == NULL) return ENOMEM; + pos = packet; + + /* Ethernet header */ + if (!( + /* SONMP multicast address as target */ + POKE_BYTES(mcastaddr, sizeof(mcastaddr)) && + /* Source MAC addresss */ + POKE_BYTES(&hardware->h_lladdr, ETHER_ADDR_LEN) && + /* SONMP frame is of fixed size */ + POKE_UINT16(SONMP_SIZE))) + goto toobig; + + /* LLC header */ + if (!( + /* DSAP and SSAP */ + POKE_UINT8(0xaa) && POKE_UINT8(0xaa) && + /* Control field */ + POKE_UINT8(0x03) && + /* ORG */ + POKE_BYTES(llcorg, sizeof(llcorg)) && + POKE_SAVE(pos_pid) && /* We will modify PID later to + create a new frame */ + POKE_UINT16(LLC_PID_SONMP_HELLO))) + goto toobig; + + address.s_addr = htonl(INADDR_ANY); + TAILQ_FOREACH (mgmt, &chassis->c_mgmt, m_entries) { + if (mgmt->m_family == LLDPD_AF_IPV4) { + address.s_addr = mgmt->m_addr.inet.s_addr; + } + break; + } + + /* SONMP */ + if (!( + /* Our IP address */ + POKE_BYTES(&address, sizeof(struct in_addr)) && + /* Segment on three bytes, we don't have slots, so we + skip the first two bytes */ + POKE_UINT16(0) && POKE_UINT8(hardware->h_ifindex) && + POKE_UINT8(1) && /* Chassis: Other */ + POKE_UINT8(12) && /* Back: Ethernet, Fast Ethernet and Gigabit */ + POKE_UINT8(SONMP_TOPOLOGY_NEW) && /* Should work. We have no state */ + POKE_UINT8(1) && /* Links: Dunno what it is */ + POKE_SAVE(end))) + goto toobig; + + if (interfaces_send_helper(global, hardware, (char *)packet, end - packet) == + -1) { + log_warn("sonmp", "unable to send packet on real device for %s", + hardware->h_ifname); + free(packet); + return ENETDOWN; + } + + POKE_RESTORE(pos_pid); /* Modify LLC PID */ + (void)POKE_UINT16(LLC_PID_SONMP_FLATNET); + POKE_RESTORE(packet); /* Go to the beginning */ + PEEK_DISCARD(ETHER_ADDR_LEN - 1); /* Modify the last byte of the MAC address */ + (void)POKE_UINT8(1); + + if (interfaces_send_helper(global, hardware, (char *)packet, end - packet) == + -1) { + log_warn("sonmp", + "unable to send second SONMP packet on real device for %s", + hardware->h_ifname); + free(packet); + return ENETDOWN; + } + + free(packet); + hardware->h_tx_cnt++; + return 0; +toobig: + free(packet); + return -1; +} + +int +sonmp_decode(struct lldpd *cfg, char *frame, int s, struct lldpd_hardware *hardware, + struct lldpd_chassis **newchassis, struct lldpd_port **newport) +{ + const u_int8_t mcastaddr[] = SONMP_MULTICAST_ADDR; + struct lldpd_chassis *chassis; + struct lldpd_port *port; + struct lldpd_mgmt *mgmt; + int length, i; + u_int8_t *pos; + u_int8_t seg[3], rchassis; + struct in_addr address; + + log_debug("sonmp", "decode SONMP PDU from %s", hardware->h_ifname); + + if ((chassis = calloc(1, sizeof(struct lldpd_chassis))) == NULL) { + log_warn("sonmp", "failed to allocate remote chassis"); + return -1; + } + TAILQ_INIT(&chassis->c_mgmt); + if ((port = calloc(1, sizeof(struct lldpd_port))) == NULL) { + log_warn("sonmp", "failed to allocate remote port"); + free(chassis); + return -1; + } +# ifdef ENABLE_DOT1 + TAILQ_INIT(&port->p_vlans); +# endif + + length = s; + pos = (u_int8_t *)frame; + if (length < SONMP_SIZE + 2 * ETHER_ADDR_LEN + sizeof(u_int16_t)) { + log_warnx("sonmp", "too short SONMP frame received on %s", + hardware->h_ifname); + goto malformed; + } + if (PEEK_CMP(mcastaddr, sizeof(mcastaddr)) != 0) + /* There is two multicast address. We just handle only one of + * them. */ + goto malformed; + /* We skip to LLC PID */ + PEEK_DISCARD(ETHER_ADDR_LEN); + PEEK_DISCARD_UINT16; + PEEK_DISCARD(6); + if (PEEK_UINT16 != LLC_PID_SONMP_HELLO) { + log_debug("sonmp", "incorrect LLC protocol ID received for SONMP on %s", + hardware->h_ifname); + goto malformed; + } + + chassis->c_id_subtype = LLDP_CHASSISID_SUBTYPE_ADDR; + if ((chassis->c_id = calloc(1, sizeof(struct in_addr) + 1)) == NULL) { + log_warn("sonmp", "unable to allocate memory for chassis id on %s", + hardware->h_ifname); + goto malformed; + } + chassis->c_id_len = sizeof(struct in_addr) + 1; + chassis->c_id[0] = 1; + PEEK_BYTES(&address, sizeof(struct in_addr)); + memcpy(chassis->c_id + 1, &address, sizeof(struct in_addr)); + if (asprintf(&chassis->c_name, "%s", inet_ntoa(address)) == -1) { + log_warnx("sonmp", "unable to write chassis name for %s", + hardware->h_ifname); + goto malformed; + } + PEEK_BYTES(seg, sizeof(seg)); + rchassis = PEEK_UINT8; + for (i = 0; sonmp_chassis_types[i].type != 0; i++) { + if (sonmp_chassis_types[i].type == rchassis) break; + } + if (asprintf(&chassis->c_descr, "%s", sonmp_chassis_types[i].description) == + -1) { + log_warnx("sonmp", "unable to write chassis description for %s", + hardware->h_ifname); + goto malformed; + } + mgmt = lldpd_alloc_mgmt(LLDPD_AF_IPV4, &address, sizeof(struct in_addr), 0); + if (mgmt == NULL) { + if (errno == ENOMEM) + log_warn("sonmp", + "unable to allocate memory for management address"); + else + log_warn("sonmp", "too large management address received on %s", + hardware->h_ifname); + goto malformed; + } + TAILQ_INSERT_TAIL(&chassis->c_mgmt, mgmt, m_entries); + port->p_ttl = + cfg ? (cfg->g_config.c_tx_interval * cfg->g_config.c_tx_hold) : LLDPD_TTL; + port->p_ttl = (port->p_ttl + 999) / 1000; + + port->p_id_subtype = LLDP_PORTID_SUBTYPE_LOCAL; + + port->p_id_len = + asprintf(&port->p_id, "%02x-%02x-%02x", seg[0], seg[1], seg[2]); + if (port->p_id_len == -1) { + log_warn("sonmp", "unable to allocate memory for port id on %s", + hardware->h_ifname); + goto malformed; + } + + /* Port description depend on the number of segments */ + if ((seg[0] == 0) && (seg[1] == 0)) { + if (asprintf(&port->p_descr, "port %d", seg[2]) == -1) { + log_warnx("sonmp", "unable to write port description for %s", + hardware->h_ifname); + goto malformed; + } + } else if (seg[0] == 0) { + if (asprintf(&port->p_descr, "port %d/%d", seg[1], seg[2]) == -1) { + log_warnx("sonmp", "unable to write port description for %s", + hardware->h_ifname); + goto malformed; + } + } else { + if (asprintf(&port->p_descr, "port %x:%x:%x", seg[0], seg[1], seg[2]) == + -1) { + log_warnx("sonmp", "unable to write port description for %s", + hardware->h_ifname); + goto malformed; + } + } + *newchassis = chassis; + *newport = port; + return 1; + +malformed: + lldpd_chassis_cleanup(chassis, 1); + lldpd_port_cleanup(port, 1); + free(port); + return -1; +} + +#endif /* ENABLE_SONMP */ diff --git a/src/daemon/protocols/sonmp.h b/src/daemon/protocols/sonmp.h new file mode 100644 index 0000000..513c4bb --- /dev/null +++ b/src/daemon/protocols/sonmp.h @@ -0,0 +1,42 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _SONMP_H +#define _SONMP_H + +#define SONMP_MULTICAST_ADDR \ + { \ + 0x01, 0x00, 0x81, 0x00, 0x01, 0x00 \ + } +#define LLC_ORG_NORTEL \ + { \ + 0x00, 0x00, 0x81 \ + } +#define LLC_PID_SONMP_HELLO 0x01a2 +#define LLC_PID_SONMP_FLATNET 0x01a1 +#define SONMP_SIZE 19 + +struct sonmp_chassis { + int type; + const char *description; +}; + +#define SONMP_TOPOLOGY_CHANGED 1 +#define SONMP_TOPOLOGY_UNCHANGED 2 +#define SONMP_TOPOLOGY_NEW 3 + +#endif /* _SONMP_H */ diff --git a/src/daemon/trace.h b/src/daemon/trace.h new file mode 100644 index 0000000..2e7c482 --- /dev/null +++ b/src/daemon/trace.h @@ -0,0 +1,8 @@ +#ifdef ENABLE_DTRACE +# include "probes.h" +# define TRACE(probe) probe +# define TRACE_ENABLED(probe) probe##_ENABLED() +#else +# define TRACE(probe) +# define TRACE_ENABLED(probe) (0) +#endif diff --git a/src/daemon/usr.sbin.lldpd.in b/src/daemon/usr.sbin.lldpd.in new file mode 100644 index 0000000..d459cd4 --- /dev/null +++ b/src/daemon/usr.sbin.lldpd.in @@ -0,0 +1,65 @@ +#include <tunables/global> + +@sbindir@/lldpd { + #include <abstractions/base> + #include <abstractions/nameservice> + + capability chown, + capability dac_override, + capability fowner, + capability fsetid, + capability kill, + capability net_admin, + capability net_raw, + capability setgid, + capability setuid, + capability sys_chroot, + capability sys_module, + + # Need to receive/send raw packets + network packet raw, + + @sbindir@/lldpd mr, + /run/systemd/notify w, + + # Ability to run lldpcli for self-configuration + @sbindir@/lldpcli rix, + @sysconfdir@/lldpd.d/ r, + @sysconfdir@/lldpd.d/* r, + @sysconfdir@/lldpd.conf r, + + # PID file and socket + @LLDPD_PID_FILE@ rw, + @LLDPD_CTL_SOCKET@ rw, + + # Chroot setup + @PRIVSEP_CHROOT@ w, + @PRIVSEP_CHROOT@/etc/ rw, + @PRIVSEP_CHROOT@/etc/localtime rw, + + # Gather system description + /etc/os-release r, + /usr/lib/os-release r, + /usr/bin/lsb_release Cxr -> lsb_release, + profile lsb_release { + #include <abstractions/base> + #include <abstractions/python> + /usr/bin/lsb_release r, + /bin/dash ixr, + /usr/bin/dpkg-query ixr, + /usr/include/python2.[4567]/pyconfig.h r, + /etc/lsb-release r, + /etc/debian_version r, + /var/lib/dpkg/** r, + /usr/local/lib/python3.[0-5]/dist-packages/ r, + /usr/bin/ r, + /usr/bin/python3.[0-5] r, + } + + # Gather network information + @{PROC}/sys/net/ipv4/ip_forward r, + @{PROC}/net/bonding/* r, + @{PROC}/self/net/bonding/* r, + /sys/devices/virtual/dmi/** r, + /sys/devices/pci**/net/*/ifalias r, +} 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@ diff --git a/src/lldp-const.h b/src/lldp-const.h new file mode 100644 index 0000000..454424d --- /dev/null +++ b/src/lldp-const.h @@ -0,0 +1,352 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _LLDP_H +#define _LLDP_H + +/* Definitions prefixed by `LLDP_` are constants from LLDP + * specifications. Definitions prefixed by `LLDPD_` are custom + * constants that are useful in the context of lldpd and its clients. + */ + +#define LLDP_TLV_ORG 127 +#define LLDP_TLV_ORG_OUI_LEN 3 +#define LLDP_TLV_ORG_OUI_INFO_MAXLEN 507 + +/* Chassis ID subtype */ +#define LLDP_CHASSISID_SUBTYPE_CHASSIS 1 +#define LLDP_CHASSISID_SUBTYPE_IFALIAS 2 +#define LLDP_CHASSISID_SUBTYPE_PORT 3 +#define LLDP_CHASSISID_SUBTYPE_LLADDR 4 +#define LLDP_CHASSISID_SUBTYPE_ADDR 5 +#define LLDP_CHASSISID_SUBTYPE_IFNAME 6 +#define LLDP_CHASSISID_SUBTYPE_LOCAL 7 + +/* Port ID subtype */ +#define LLDP_PORTID_SUBTYPE_UNKNOWN 0 +#define LLDP_PORTID_SUBTYPE_IFALIAS 1 +#define LLDP_PORTID_SUBTYPE_PORT 2 +#define LLDP_PORTID_SUBTYPE_LLADDR 3 +#define LLDP_PORTID_SUBTYPE_ADDR 4 +#define LLDP_PORTID_SUBTYPE_IFNAME 5 +#define LLDP_PORTID_SUBTYPE_AGENTCID 6 +#define LLDP_PORTID_SUBTYPE_LOCAL 7 +#define LLDP_PORTID_SUBTYPE_MAX LLDP_PORTID_SUBTYPE_LOCAL + +/* Operational MAU Type field. See: + * https://www.iana.org/assignments/ianamau-mib/ianamau-mib */ +#define LLDP_DOT3_MAU_AUI 1 +#define LLDP_DOT3_MAU_10BASE5 2 +#define LLDP_DOT3_MAU_FOIRL 3 +#define LLDP_DOT3_MAU_10BASE2 4 +#define LLDP_DOT3_MAU_10BASET 5 +#define LLDP_DOT3_MAU_10BASEFP 6 +#define LLDP_DOT3_MAU_10BASEFB 7 +#define LLDP_DOT3_MAU_10BASEFL 8 +#define LLDP_DOT3_MAU_10BROAD36 9 +#define LLDP_DOT3_MAU_10BASETHD 10 +#define LLDP_DOT3_MAU_10BASETFD 11 +#define LLDP_DOT3_MAU_10BASEFLHD 12 +#define LLDP_DOT3_MAU_10BASEFLFD 13 +#define LLDP_DOT3_MAU_100BASET4 14 +#define LLDP_DOT3_MAU_100BASETXHD 15 +#define LLDP_DOT3_MAU_100BASETXFD 16 +#define LLDP_DOT3_MAU_100BASEFXHD 17 +#define LLDP_DOT3_MAU_100BASEFXFD 18 +#define LLDP_DOT3_MAU_100BASET2HD 19 +#define LLDP_DOT3_MAU_100BASET2FD 20 +#define LLDP_DOT3_MAU_1000BASEXHD 21 +#define LLDP_DOT3_MAU_1000BASEXFD 22 +#define LLDP_DOT3_MAU_1000BASELXHD 23 +#define LLDP_DOT3_MAU_1000BASELXFD 24 +#define LLDP_DOT3_MAU_1000BASESXHD 25 +#define LLDP_DOT3_MAU_1000BASESXFD 26 +#define LLDP_DOT3_MAU_1000BASECXHD 27 +#define LLDP_DOT3_MAU_1000BASECXFD 28 +#define LLDP_DOT3_MAU_1000BASETHD 29 +#define LLDP_DOT3_MAU_1000BASETFD 30 +#define LLDP_DOT3_MAU_10GIGBASEX 31 +#define LLDP_DOT3_MAU_10GIGBASELX4 32 +#define LLDP_DOT3_MAU_10GIGBASER 33 +#define LLDP_DOT3_MAU_10GIGBASEER 34 +#define LLDP_DOT3_MAU_10GIGBASELR 35 +#define LLDP_DOT3_MAU_10GIGBASESR 36 +#define LLDP_DOT3_MAU_10GIGBASEW 37 +#define LLDP_DOT3_MAU_10GIGBASEEW 38 +#define LLDP_DOT3_MAU_10GIGBASELW 39 +#define LLDP_DOT3_MAU_10GIGBASESW 40 +#define LLDP_DOT3_MAU_10GIGBASECX4 41 +#define LLDP_DOT3_MAU_2BASETL 42 +#define LLDP_DOT3_MAU_10PASSTS 43 +#define LLDP_DOT3_MAU_100BASEBX10D 44 +#define LLDP_DOT3_MAU_100BASEBX10U 45 +#define LLDP_DOT3_MAU_100BASELX10 46 +#define LLDP_DOT3_MAU_1000BASEBX10D 47 +#define LLDP_DOT3_MAU_1000BASEBX10U 48 +#define LLDP_DOT3_MAU_1000BASELX10 49 +#define LLDP_DOT3_MAU_1000BASEPX10D 50 +#define LLDP_DOT3_MAU_1000BASEPX10U 51 +#define LLDP_DOT3_MAU_1000BASEPX20D 52 +#define LLDP_DOT3_MAU_1000BASEPX20U 53 +#define LLDP_DOT3_MAU_10GBASET 54 +#define LLDP_DOT3_MAU_10GBASELRM 55 +#define LLDP_DOT3_MAU_1000BASEKX 56 +#define LLDP_DOT3_MAU_10GBASEKX4 57 +#define LLDP_DOT3_MAU_10GBASEKR 58 +#define LLDP_DOT3_MAU_10G1GBASEPRXD1 59 +#define LLDP_DOT3_MAU_10G1GBASEPRXD2 60 +#define LLDP_DOT3_MAU_10G1GBASEPRXD3 61 +#define LLDP_DOT3_MAU_10G1GBASEPRXU1 62 +#define LLDP_DOT3_MAU_10G1GBASEPRXU2 63 +#define LLDP_DOT3_MAU_10G1GBASEPRXU3 64 +#define LLDP_DOT3_MAU_10GBASEPRD1 65 +#define LLDP_DOT3_MAU_10GBASEPRD2 66 +#define LLDP_DOT3_MAU_10GBASEPRD3 67 +#define LLDP_DOT3_MAU_10GBASEPRU1 68 +#define LLDP_DOT3_MAU_10GBASEPRU3 69 +#define LLDP_DOT3_MAU_40GBASEKR4 70 +#define LLDP_DOT3_MAU_40GBASECR4 71 +#define LLDP_DOT3_MAU_40GBASESR4 72 +#define LLDP_DOT3_MAU_40GBASEFR 73 +#define LLDP_DOT3_MAU_40GBASELR4 74 +#define LLDP_DOT3_MAU_100GBASECR10 75 +#define LLDP_DOT3_MAU_100GBASESR10 76 +#define LLDP_DOT3_MAU_100GBASELR4 77 +#define LLDP_DOT3_MAU_100GBASEER4 78 +#define LLDP_DOT3_MAU_1000BASET1 79 +#define LLDP_DOT3_MAU_1000BASEPX30D 80 +#define LLDP_DOT3_MAU_1000BASEPX30U 81 +#define LLDP_DOT3_MAU_1000BASEPX40D 82 +#define LLDP_DOT3_MAU_1000BASEPX40U 83 +#define LLDP_DOT3_MAU_10G1GBASEPRXD4 84 +#define LLDP_DOT3_MAU_10G1GBASEPRXU4 85 +#define LLDP_DOT3_MAU_10GBASEPRD4 86 +#define LLDP_DOT3_MAU_10GBASEPRU4 87 +#define LLDP_DOT3_MAU_25GBASECR 88 +#define LLDP_DOT3_MAU_25GBASECRS 89 +#define LLDP_DOT3_MAU_25GBASEKR 90 +#define LLDP_DOT3_MAU_25GBASEKRS 91 +#define LLDP_DOT3_MAU_25GBASER 92 +#define LLDP_DOT3_MAU_25GBASESR 93 +#define LLDP_DOT3_MAU_25GBASET 94 +#define LLDP_DOT3_MAU_40GBASEER4 95 +#define LLDP_DOT3_MAU_40GBASER 96 +#define LLDP_DOT3_MAU_40GBASET 97 +#define LLDP_DOT3_MAU_100GBASECR4 98 +#define LLDP_DOT3_MAU_100GBASEKR4 99 +#define LLDP_DOT3_MAU_100GBASEKP4 100 +#define LLDP_DOT3_MAU_100GBASER 101 +#define LLDP_DOT3_MAU_100GBASESR4 102 +#define LLDP_DOT3_MAU_2P5GIGT 103 +#define LLDP_DOT3_MAU_5GIGT 104 +#define LLDP_DOT3_MAU_100BASET1 105 +#define LLDP_DOT3_MAU_1000BASERHA 106 +#define LLDP_DOT3_MAU_1000BASERHB 107 +#define LLDP_DOT3_MAU_1000BASERHC 108 +#define LLDP_DOT3_MAU_2P5GBASEKX 109 +#define LLDP_DOT3_MAU_2P5GBASEX 110 +#define LLDP_DOT3_MAU_5GBASEKR 111 +#define LLDP_DOT3_MAU_5GBASER 112 +#define LLDP_DOT3_MAU_10GPASSXR 113 +#define LLDP_DOT3_MAU_25GBASELR 114 +#define LLDP_DOT3_MAU_25GBASEER 115 +#define LLDP_DOT3_MAU_50GBASER 116 +#define LLDP_DOT3_MAU_50GBASECR 117 +#define LLDP_DOT3_MAU_50GBASEKR 118 +#define LLDP_DOT3_MAU_50GBASESR 119 +#define LLDP_DOT3_MAU_50GBASEFR 120 +#define LLDP_DOT3_MAU_50GBASELR 121 +#define LLDP_DOT3_MAU_50GBASEER 122 +#define LLDP_DOT3_MAU_100GBASECR2 123 +#define LLDP_DOT3_MAU_100GBASEKR2 124 +#define LLDP_DOT3_MAU_100GBASESR2 125 +#define LLDP_DOT3_MAU_100GBASEDR 126 +#define LLDP_DOT3_MAU_200GBASER 127 +#define LLDP_DOT3_MAU_200GBASEDR4 128 +#define LLDP_DOT3_MAU_200GBASEFR4 129 +#define LLDP_DOT3_MAU_200GBASELR4 130 +#define LLDP_DOT3_MAU_200GBASECR4 131 +#define LLDP_DOT3_MAU_200GBASEKR4 132 +#define LLDP_DOT3_MAU_200GBASESR4 133 +#define LLDP_DOT3_MAU_200GBASEER4 134 +#define LLDP_DOT3_MAU_400GBASER 135 +#define LLDP_DOT3_MAU_400GBASESR16 136 +#define LLDP_DOT3_MAU_400GBASEDR4 137 +#define LLDP_DOT3_MAU_400GBASEFR8 138 +#define LLDP_DOT3_MAU_400GBASELR8 139 +#define LLDP_DOT3_MAU_400GBASEER8 140 +#define LLDP_DOT3_MAU_10BASET1L 141 +#define LLDP_DOT3_MAU_10BASET1SHD 142 +#define LLDP_DOT3_MAU_10BASET1SMD 143 +#define LLDP_DOT3_MAU_10BASET1SFD 144 + +/* Dot3 Power Devicetype */ +#define LLDP_DOT3_POWER_PSE 1 +#define LLDP_DOT3_POWER_PD 2 + +/* Dot3 Power Pairs (RFC 3621) */ +#define LLDP_DOT3_POWERPAIRS_SIGNAL 1 +#define LLDP_DOT3_POWERPAIRS_SPARE 2 + +/* Dot3 Power type (for 802.3at) */ +#define LLDP_DOT3_POWER_8023AT_OFF 0 +#define LLDP_DOT3_POWER_8023AT_TYPE1 1 +#define LLDP_DOT3_POWER_8023AT_TYPE2 2 + +/* 802.3bt additions */ +#define LLDP_DOT3_POWER_8023BT_OFF 0 +#define LLDP_DOT3_POWER_8023BT_TYPE3 1 +#define LLDP_DOT3_POWER_8023BT_TYPE4 2 + +/* Dot3 power source */ +#define LLDP_DOT3_POWER_SOURCE_UNKNOWN 0 +#define LLDP_DOT3_POWER_SOURCE_PRIMARY 1 +#define LLDP_DOT3_POWER_SOURCE_PSE 1 +#define LLDP_DOT3_POWER_SOURCE_BACKUP 2 +#define LLDP_DOT3_POWER_SOURCE_LOCAL 2 +#define LLDP_DOT3_POWER_SOURCE_BOTH 3 + +/* Dot3 power priority */ +#define LLDP_DOT3_POWER_PRIO_UNKNOWN 0 +#define LLDP_DOT3_POWER_PRIO_CRITICAL 1 +#define LLDP_DOT3_POWER_PRIO_HIGH 2 +#define LLDP_DOT3_POWER_PRIO_LOW 3 + +/* PMD Auto-Negotiation Advertised Capability field, from RFC 3636 (see + * IANAifMauAutoNegCapBits). Unfortunately, we are limited to two bytes, so + * higher speed capabilities will map to "other". */ +#define LLDP_DOT3_LINK_AUTONEG_OTHER 0x8000 +#define LLDP_DOT3_LINK_AUTONEG_10BASE_T 0x4000 +#define LLDP_DOT3_LINK_AUTONEG_10BASET_FD 0x2000 +#define LLDP_DOT3_LINK_AUTONEG_100BASE_T4 0x1000 +#define LLDP_DOT3_LINK_AUTONEG_100BASE_TX 0x0800 +#define LLDP_DOT3_LINK_AUTONEG_100BASE_TXFD 0x0400 +#define LLDP_DOT3_LINK_AUTONEG_100BASE_T2 0x0200 +#define LLDP_DOT3_LINK_AUTONEG_100BASE_T2FD 0x0100 +#define LLDP_DOT3_LINK_AUTONEG_FDX_PAUSE 0x0080 +#define LLDP_DOT3_LINK_AUTONEG_FDX_APAUSE 0x0040 +#define LLDP_DOT3_LINK_AUTONEG_FDX_SPAUSE 0x0020 +#define LLDP_DOT3_LINK_AUTONEG_FDX_BPAUSE 0x0010 +#define LLDP_DOT3_LINK_AUTONEG_1000BASE_X 0x0008 +#define LLDP_DOT3_LINK_AUTONEG_1000BASE_XFD 0x0004 +#define LLDP_DOT3_LINK_AUTONEG_1000BASE_T 0x0002 +#define LLDP_DOT3_LINK_AUTONEG_1000BASE_TFD 0x0001 + +/* Capabilities */ +#define LLDP_CAP_OTHER 0x01 +#define LLDP_CAP_REPEATER 0x02 +#define LLDP_CAP_BRIDGE 0x04 +#define LLDP_CAP_WLAN 0x08 +#define LLDP_CAP_ROUTER 0x10 +#define LLDP_CAP_TELEPHONE 0x20 +#define LLDP_CAP_DOCSIS 0x40 +#define LLDP_CAP_STATION 0x80 + +#define LLDP_PPVID_CAP_SUPPORTED (1 << 1) +#define LLDP_PPVID_CAP_ENABLED (1 << 2) + +/* see http://www.iana.org/assignments/address-family-numbers */ +#define LLDP_MGMT_ADDR_NONE 0 +#define LLDP_MGMT_ADDR_IP4 1 +#define LLDP_MGMT_ADDR_IP6 2 + +#define LLDP_MGMT_IFACE_UNKNOWN 1 +#define LLDP_MGMT_IFACE_IFINDEX 2 +#define LLDP_MGMT_IFACE_SYSPORT 3 + +#define LLDP_MED_CLASS_I 1 +#define LLDP_MED_CLASS_II 2 +#define LLDP_MED_CLASS_III 3 +#define LLDP_MED_NETWORK_DEVICE 4 + +/* LLDP MED application ttpes */ +#define LLDP_MED_APPTYPE_UNDEFINED 0 +#define LLDP_MED_APPTYPE_VOICE 1 +#define LLDP_MED_APPTYPE_VOICESIGNAL 2 +#define LLDP_MED_APPTYPE_GUESTVOICE 3 +#define LLDP_MED_APPTYPE_GUESTVOICESIGNAL 4 +#define LLDP_MED_APPTYPE_SOFTPHONEVOICE 5 +#define LLDP_MED_APPTYPE_VIDEOCONFERENCE 6 +#define LLDP_MED_APPTYPE_VIDEOSTREAM 7 +#define LLDP_MED_APPTYPE_VIDEOSIGNAL 8 +#define LLDP_MED_APPTYPE_LAST LLDP_MED_APPTYPE_VIDEOSIGNAL + +/* LLDP MED location formats */ +#define LLDP_MED_LOCFORMAT_COORD 1 +#define LLDP_MED_LOCFORMAT_CIVIC 2 +#define LLDP_MED_LOCFORMAT_ELIN 3 +#define LLDP_MED_LOCFORMAT_LAST LLDP_MED_LOCFORMAT_ELIN + +#define LLDP_MED_LOCATION_GEOID_WGS84 1 +#define LLDP_MED_LOCATION_GEOID_NAD83 2 +#define LLDP_MED_LOCATION_GEOID_NAD83_MLLW 3 + +#define LLDP_MED_LOCATION_ALTITUDE_UNIT_METER 1 +#define LLDP_MED_LOCATION_ALTITUDE_UNIT_FLOOR 2 + +/* LLDP MED power related constants */ +#define LLDP_MED_POW_TYPE_PSE 1 +#define LLDP_MED_POW_TYPE_PD 2 +#define LLDP_MED_POW_TYPE_RESERVED 3 + +#define LLDP_MED_POW_SOURCE_UNKNOWN 1 +#define LLDP_MED_POW_SOURCE_PRIMARY 2 +#define LLDP_MED_POW_SOURCE_BACKUP 3 +#define LLDP_MED_POW_SOURCE_RESERVED 4 +#define LLDP_MED_POW_SOURCE_PSE 5 +#define LLDP_MED_POW_SOURCE_LOCAL 6 +#define LLDP_MED_POW_SOURCE_BOTH 7 + +#define LLDP_MED_POW_PRIO_UNKNOWN 0 +#define LLDP_MED_POW_PRIO_CRITICAL 1 +#define LLDP_MED_POW_PRIO_HIGH 2 +#define LLDP_MED_POW_PRIO_LOW 3 + +/* LLDP MED capabilities */ +#define LLDP_MED_CAP_CAP 0x01 +#define LLDP_MED_CAP_POLICY 0x02 +#define LLDP_MED_CAP_LOCATION 0x04 +#define LLDP_MED_CAP_MDI_PSE 0x08 +#define LLDP_MED_CAP_MDI_PD 0x10 +#define LLDP_MED_CAP_IV 0x20 + +/* Protocol constants for multi-protocol lldpd */ +#define LLDPD_MODE_LLDP 1 +#define LLDPD_MODE_CDPV1 2 +#define LLDPD_MODE_CDPV2 3 +#define LLDPD_MODE_SONMP 4 +#define LLDPD_MODE_EDP 5 +#define LLDPD_MODE_FDP 6 +#define LLDPD_MODE_MAX LLDPD_MODE_FDP + +/* Bond slave src mac type constants */ +#define LLDP_BOND_SLAVE_SRC_MAC_TYPE_UNKNOWN 0 +#define LLDP_BOND_SLAVE_SRC_MAC_TYPE_REAL 1 +#define LLDP_BOND_SLAVE_SRC_MAC_TYPE_ZERO 2 +#define LLDP_BOND_SLAVE_SRC_MAC_TYPE_FIXED 3 +#define LLDP_BOND_SLAVE_SRC_MAC_TYPE_LOCALLY_ADMINISTERED 4 +#define LLDP_BOND_SLAVE_SRC_MAC_TYPE_MAX \ + LLDP_BOND_SLAVE_SRC_MAC_TYPE_LOCALLY_ADMINISTERED + +/* Agent types */ +#define LLDP_AGENT_TYPE_UNKNOWN 0 +#define LLDP_AGENT_TYPE_NEAREST_BRIDGE 1 +#define LLDP_AGENT_TYPE_NEAREST_NONTPMR_BRIDGE 2 +#define LLDP_AGENT_TYPE_NEAREST_CUSTOMER_BRIDGE 3 +#define LLDP_AGENT_TYPE_MAX LLDP_AGENT_TYPE_NEAREST_CUSTOMER_BRIDGE + +#endif /* _LLDP_H */ diff --git a/src/lldpd-structs.c b/src/lldpd-structs.c new file mode 100644 index 0000000..6b42713 --- /dev/null +++ b/src/lldpd-structs.c @@ -0,0 +1,232 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdlib.h> +#include <unistd.h> +#include <time.h> +#include "lldpd-structs.h" +#include "log.h" + +void +lldpd_chassis_mgmt_cleanup(struct lldpd_chassis *chassis) +{ + struct lldpd_mgmt *mgmt, *mgmt_next; + + log_debug("alloc", "cleanup management addresses for chassis %s", + chassis->c_name ? chassis->c_name : "(unknown)"); + + for (mgmt = TAILQ_FIRST(&chassis->c_mgmt); mgmt != NULL; mgmt = mgmt_next) { + mgmt_next = TAILQ_NEXT(mgmt, m_entries); + free(mgmt); + } + TAILQ_INIT(&chassis->c_mgmt); +} + +void +lldpd_chassis_cleanup(struct lldpd_chassis *chassis, int all) +{ + lldpd_chassis_mgmt_cleanup(chassis); + log_debug("alloc", "cleanup chassis %s", + chassis->c_name ? chassis->c_name : "(unknown)"); +#ifdef ENABLE_LLDPMED + free(chassis->c_med_hw); + free(chassis->c_med_sw); + free(chassis->c_med_fw); + free(chassis->c_med_sn); + free(chassis->c_med_manuf); + free(chassis->c_med_model); + free(chassis->c_med_asset); +#endif + free(chassis->c_id); + free(chassis->c_name); + free(chassis->c_descr); + if (all) free(chassis); +} + +#ifdef ENABLE_DOT1 +void +lldpd_vlan_cleanup(struct lldpd_port *port) +{ + struct lldpd_vlan *vlan, *vlan_next; + for (vlan = TAILQ_FIRST(&port->p_vlans); vlan != NULL; vlan = vlan_next) { + free(vlan->v_name); + vlan_next = TAILQ_NEXT(vlan, v_entries); + free(vlan); + } + TAILQ_INIT(&port->p_vlans); + port->p_pvid = 0; +} + +void +lldpd_ppvid_cleanup(struct lldpd_port *port) +{ + struct lldpd_ppvid *ppvid, *ppvid_next; + for (ppvid = TAILQ_FIRST(&port->p_ppvids); ppvid != NULL; ppvid = ppvid_next) { + ppvid_next = TAILQ_NEXT(ppvid, p_entries); + free(ppvid); + } + TAILQ_INIT(&port->p_ppvids); +} + +void +lldpd_pi_cleanup(struct lldpd_port *port) +{ + struct lldpd_pi *pi, *pi_next; + for (pi = TAILQ_FIRST(&port->p_pids); pi != NULL; pi = pi_next) { + free(pi->p_pi); + pi_next = TAILQ_NEXT(pi, p_entries); + free(pi); + } + TAILQ_INIT(&port->p_pids); +} +#endif + +#ifdef ENABLE_CUSTOM +void +lldpd_custom_tlv_add(struct lldpd_port *port, struct lldpd_custom *curr) +{ + struct lldpd_custom *custom; + + if ((custom = malloc(sizeof(struct lldpd_custom)))) { + memcpy(custom, curr, sizeof(struct lldpd_custom)); + if ((custom->oui_info = malloc(custom->oui_info_len))) { + memcpy(custom->oui_info, curr->oui_info, custom->oui_info_len); + TAILQ_INSERT_TAIL(&port->p_custom_list, custom, next); + } else { + free(custom); + log_warn("rpc", + "could not allocate memory for custom TLV info"); + } + } +} + +void +lldpd_custom_tlv_cleanup(struct lldpd_port *port, struct lldpd_custom *curr) +{ + struct lldpd_custom *custom, *custom_next; + for (custom = TAILQ_FIRST(&port->p_custom_list); custom != NULL; + custom = custom_next) { + custom_next = TAILQ_NEXT(custom, next); + if (!memcmp(curr->oui, custom->oui, sizeof(curr->oui)) && + curr->subtype == custom->subtype) { + TAILQ_REMOVE(&port->p_custom_list, custom, next); + free(custom->oui_info); + free(custom); + } + } +} + +void +lldpd_custom_list_cleanup(struct lldpd_port *port) +{ + struct lldpd_custom *custom, *custom_next; + for (custom = TAILQ_FIRST(&port->p_custom_list); custom != NULL; + custom = custom_next) { + custom_next = TAILQ_NEXT(custom, next); + free(custom->oui_info); + free(custom); + } + TAILQ_INIT(&port->p_custom_list); +} +#endif + +/* Cleanup a remote port. The before last argument, `expire` is a function that + * should be called when a remote port is removed. If the last argument is 1, + * all remote ports are removed. + */ +void +lldpd_remote_cleanup(struct lldpd_hardware *hardware, + void (*expire)(struct lldpd_hardware *, struct lldpd_port *), int all) +{ + struct lldpd_port *port, *port_next; + int del; + time_t now = time(NULL); + + log_debug("alloc", "cleanup remote port on %s", hardware->h_ifname); + for (port = TAILQ_FIRST(&hardware->h_rports); port != NULL; port = port_next) { + port_next = TAILQ_NEXT(port, p_entries); + del = all; + if (!all && expire && (now >= port->p_lastupdate + port->p_ttl)) { + if (port->p_ttl > 0) hardware->h_ageout_cnt++; + del = 1; + } + if (del) { + if (expire) expire(hardware, port); + /* This TAILQ_REMOVE is dangerous. It should not be + * called while in liblldpctl because we don't have a + * real list. It is only needed to be called when we + * don't delete the entire list. */ + if (!all) TAILQ_REMOVE(&hardware->h_rports, port, p_entries); + + hardware->h_delete_cnt++; + /* Register last removal to be able to report + * lldpStatsRemTablesLastChangeTime */ + hardware->h_lport.p_lastremove = time(NULL); + lldpd_port_cleanup(port, 1); + free(port); + } + } + if (all) TAILQ_INIT(&hardware->h_rports); +} + +/* If `all' is true, clear all information, including information that + are not refreshed periodically. Port should be freed manually. */ +void +lldpd_port_cleanup(struct lldpd_port *port, int all) +{ +#ifdef ENABLE_LLDPMED + int i; + if (all) + for (i = 0; i < LLDP_MED_LOCFORMAT_LAST; i++) + free(port->p_med_location[i].data); +#endif +#ifdef ENABLE_DOT1 + lldpd_vlan_cleanup(port); + lldpd_ppvid_cleanup(port); + lldpd_pi_cleanup(port); +#endif + /* will set these to NULL so we don't free wrong memory */ + + if (all) { + free(port->p_id); + port->p_id = NULL; + free(port->p_descr); + port->p_descr = NULL; + free(port->p_lastframe); + if (port->p_chassis) { /* chassis may not have been attributed, yet */ + port->p_chassis->c_refcount--; + port->p_chassis = NULL; + } +#ifdef ENABLE_CUSTOM + lldpd_custom_list_cleanup(port); +#endif + } +} + +void +lldpd_config_cleanup(struct lldpd_config *config) +{ + log_debug("alloc", "general configuration cleanup"); + free(config->c_mgmt_pattern); + free(config->c_cid_pattern); + free(config->c_cid_string); + free(config->c_iface_pattern); + free(config->c_perm_ifaces); + free(config->c_hostname); + free(config->c_platform); + free(config->c_description); +} diff --git a/src/lldpd-structs.h b/src/lldpd-structs.h new file mode 100644 index 0000000..8d19282 --- /dev/null +++ b/src/lldpd-structs.h @@ -0,0 +1,568 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _LLDPD_STRUCTS_H +#define _LLDPD_STRUCTS_H + +#if HAVE_CONFIG_H +# include <config.h> +#endif + +#include <sys/types.h> +#include <sys/socket.h> + +/* This is not very convenient, but we need net/if.h for IFNAMSIZ and others but + * we may also need linux/if.h in some modules. And they conflict each others. + */ +#ifdef HOST_OS_LINUX +# include <linux/if.h> +#else +# include <net/if.h> +#endif + +#include <netinet/in.h> +#include <netinet/if_ether.h> +#include <sys/queue.h> + +#include "compat/compat.h" +#include "marshal.h" +#include "lldp-const.h" + +#ifdef ENABLE_DOT1 +struct lldpd_ppvid { + TAILQ_ENTRY(lldpd_ppvid) p_entries; + u_int8_t p_cap_status; + u_int16_t p_ppvid; +}; +MARSHAL_BEGIN(lldpd_ppvid) +MARSHAL_TQE(lldpd_ppvid, p_entries) +MARSHAL_END(lldpd_ppvid); + +struct lldpd_vlan { + TAILQ_ENTRY(lldpd_vlan) v_entries; + char *v_name; + u_int16_t v_vid; +}; +MARSHAL_BEGIN(lldpd_vlan) +MARSHAL_TQE(lldpd_vlan, v_entries) +MARSHAL_STR(lldpd_vlan, v_name) +MARSHAL_END(lldpd_vlan); + +struct lldpd_pi { + TAILQ_ENTRY(lldpd_pi) p_entries; + char *p_pi; + int p_pi_len; +}; +MARSHAL_BEGIN(lldpd_pi) +MARSHAL_TQE(lldpd_pi, p_entries) +MARSHAL_FSTR(lldpd_pi, p_pi, p_pi_len) +MARSHAL_END(lldpd_pi); +#endif + +#ifdef ENABLE_LLDPMED +struct lldpd_med_policy { + u_int8_t index; /* Not used. */ + u_int8_t type; + u_int8_t unknown; + u_int8_t tagged; + u_int16_t vid; + u_int8_t priority; + u_int8_t dscp; +}; +MARSHAL(lldpd_med_policy); + +struct lldpd_med_loc { + u_int8_t index; /* Not used. */ + u_int8_t format; + char *data; + int data_len; +}; +MARSHAL_BEGIN(lldpd_med_loc) +MARSHAL_FSTR(lldpd_med_loc, data, data_len) +MARSHAL_END(lldpd_med_loc); + +struct lldpd_med_power { + u_int8_t devicetype; /* PD or PSE */ + u_int8_t source; + u_int8_t priority; + u_int16_t val; +}; +MARSHAL(lldpd_med_power); +#endif + +#ifdef ENABLE_DOT3 +struct lldpd_dot3_macphy { + u_int8_t autoneg_support; + u_int8_t autoneg_enabled; + u_int16_t autoneg_advertised; + u_int16_t mau_type; +}; + +struct lldpd_dot3_power { + u_int8_t devicetype; + u_int8_t supported; + u_int8_t enabled; + u_int8_t paircontrol; + u_int8_t pairs; + u_int8_t class; + u_int8_t powertype; /* If set to LLDP_DOT3_POWER_8023AT_OFF, + following fields have no meaning */ + u_int8_t source; + u_int8_t priority; + u_int16_t requested; + u_int16_t allocated; + + /* For 802.3BT */ + u_int8_t pd_4pid; + u_int16_t requested_a; + u_int16_t requested_b; + u_int16_t allocated_a; + u_int16_t allocated_b; + u_int16_t pse_status; + u_int8_t pd_status; + u_int8_t pse_pairs_ext; + u_int8_t class_a; + u_int8_t class_b; + u_int8_t class_ext; + u_int8_t type_ext; + u_int8_t pd_load; + u_int16_t pse_max; +}; +MARSHAL(lldpd_dot3_power); +#endif + +#if defined ENABLE_CDP || defined ENABLE_FDP +struct cdpv2_power { + u_int16_t request_id; + u_int16_t management_id; +}; +#endif + +enum { LLDPD_AF_UNSPEC = 0, LLDPD_AF_IPV4, LLDPD_AF_IPV6, LLDPD_AF_LAST }; + +#define LLDPD_MGMT_MAXADDRSIZE 16 /* sizeof(struct in6_addr) */ +union lldpd_address { + struct in_addr inet; + struct in6_addr inet6; + u_int8_t octets[LLDPD_MGMT_MAXADDRSIZE]; /* network byte order! */ +}; +struct lldpd_mgmt { + TAILQ_ENTRY(lldpd_mgmt) m_entries; + int m_family; + union lldpd_address m_addr; + size_t m_addrsize; + u_int32_t m_iface; +}; +MARSHAL_BEGIN(lldpd_mgmt) +MARSHAL_TQE(lldpd_mgmt, m_entries) +MARSHAL_END(lldpd_mgmt); + +struct lldpd_chassis { + TAILQ_ENTRY(lldpd_chassis) c_entries; + u_int16_t c_refcount; /* Reference count by ports */ + u_int16_t c_index; /* Monotonic index */ + u_int8_t c_protocol; /* Protocol used to get this chassis */ + u_int8_t c_id_subtype; + char *c_id; + int c_id_len; + char *c_name; + char *c_descr; + + u_int16_t c_cap_available; + u_int16_t c_cap_enabled; + + TAILQ_HEAD(, lldpd_mgmt) c_mgmt; + +#ifdef ENABLE_LLDPMED + u_int16_t c_med_cap_available; + u_int8_t c_med_type; + char *c_med_hw; + char *c_med_fw; + char *c_med_sw; + char *c_med_sn; + char *c_med_manuf; + char *c_med_model; + char *c_med_asset; +#endif +}; +/* WARNING: any change to this structure should also be reflected into + `lldpd_copy_chassis()` which is not using marshaling. */ +MARSHAL_BEGIN(lldpd_chassis) +MARSHAL_IGNORE(lldpd_chassis, c_entries.tqe_next) +MARSHAL_IGNORE(lldpd_chassis, c_entries.tqe_prev) +MARSHAL_FSTR(lldpd_chassis, c_id, c_id_len) +MARSHAL_STR(lldpd_chassis, c_name) +MARSHAL_STR(lldpd_chassis, c_descr) +MARSHAL_SUBTQ(lldpd_chassis, lldpd_mgmt, c_mgmt) +#ifdef ENABLE_LLDPMED +MARSHAL_STR(lldpd_chassis, c_med_hw) +MARSHAL_STR(lldpd_chassis, c_med_fw) +MARSHAL_STR(lldpd_chassis, c_med_sw) +MARSHAL_STR(lldpd_chassis, c_med_sn) +MARSHAL_STR(lldpd_chassis, c_med_manuf) +MARSHAL_STR(lldpd_chassis, c_med_model) +MARSHAL_STR(lldpd_chassis, c_med_asset) +#endif +MARSHAL_END(lldpd_chassis); + +#ifdef ENABLE_CUSTOM + +# define CUSTOM_TLV_ADD 1 +# define CUSTOM_TLV_REPLACE 2 +# define CUSTOM_TLV_REMOVE 3 + +/* Custom TLV struct as defined on page 35 of IEEE 802.1AB-2005 */ +struct lldpd_custom { + TAILQ_ENTRY(lldpd_custom) next; /* Pointer to next custom TLV */ + + /* Organizationally Unique Identifier */ + u_int8_t oui[LLDP_TLV_ORG_OUI_LEN]; + /* Organizationally Defined Subtype */ + u_int8_t subtype; + /* Organizationally Defined Information String */ + u_int8_t *oui_info; + /* Organizationally Defined Information String length */ + int oui_info_len; +}; +MARSHAL_BEGIN(lldpd_custom) +MARSHAL_TQE(lldpd_custom, next) +MARSHAL_FSTR(lldpd_custom, oui_info, oui_info_len) +MARSHAL_END(lldpd_custom); +#endif + +struct lldpd_port { + TAILQ_ENTRY(lldpd_port) p_entries; + struct lldpd_chassis *p_chassis; /* Attached chassis */ + time_t p_lastchange; /* Time of last change of values */ + time_t p_lastupdate; /* Time of last update received */ + time_t + p_lastremove; /* Time of last removal of a remote port. Used for local ports + * only Used for deciding lldpStatsRemTablesLastChangeTime */ + struct lldpd_frame *p_lastframe; /* Frame received during last update */ + u_int8_t p_protocol; /* Protocol used to get this port */ + u_int8_t p_hidden_in : 1; /* Considered as hidden for reception */ + u_int8_t p_hidden_out : 1; /* Considered as hidden for emission */ + u_int8_t p_disable_rx : 1; /* Should RX be disabled for this port? */ + u_int8_t p_disable_tx : 1; /* Should TX be disabled for this port? */ + /* Important: all fields that should be ignored to check if a port has + * been changed should be before this mark. */ +#define LLDPD_PORT_START_MARKER (offsetof(struct lldpd_port, _p_hardware_flags)) + int _p_hardware_flags; /* This is a copy of hardware flags. Do not use it! */ + u_int8_t p_id_subtype; + char *p_id; + int p_id_len; + char *p_descr; + int p_descr_force; /* Description has been forced by user */ + u_int16_t p_mfs; + u_int16_t p_ttl; /* TTL for remote port */ + int p_vlan_tx_tag; + int p_vlan_tx_enabled; + +#ifdef ENABLE_DOT3 + /* Dot3 stuff */ + u_int32_t p_aggregid; + struct lldpd_dot3_macphy p_macphy; + struct lldpd_dot3_power p_power; +#endif + +#ifdef ENABLE_LLDPMED + u_int16_t p_med_cap_enabled; + struct lldpd_med_policy p_med_policy[LLDP_MED_APPTYPE_LAST]; + struct lldpd_med_loc p_med_location[LLDP_MED_LOCFORMAT_LAST]; + struct lldpd_med_power p_med_power; +#endif + +#if defined ENABLE_CDP || defined ENABLE_FDP + struct cdpv2_power p_cdp_power; +#endif + +#ifdef ENABLE_DOT1 + u_int16_t p_pvid; + TAILQ_HEAD(, lldpd_vlan) p_vlans; + TAILQ_HEAD(, lldpd_ppvid) p_ppvids; + TAILQ_HEAD(, lldpd_pi) p_pids; +#endif +#ifdef ENABLE_CUSTOM + TAILQ_HEAD(, lldpd_custom) p_custom_list; +#endif +}; +MARSHAL_BEGIN(lldpd_port) +MARSHAL_TQE(lldpd_port, p_entries) +MARSHAL_POINTER(lldpd_port, lldpd_chassis, p_chassis) +MARSHAL_IGNORE(lldpd_port, p_lastframe) +MARSHAL_FSTR(lldpd_port, p_id, p_id_len) +MARSHAL_STR(lldpd_port, p_descr) +#ifdef ENABLE_LLDPMED +MARSHAL_SUBSTRUCT(lldpd_port, lldpd_med_loc, p_med_location[0]) +MARSHAL_SUBSTRUCT(lldpd_port, lldpd_med_loc, p_med_location[1]) +MARSHAL_SUBSTRUCT(lldpd_port, lldpd_med_loc, p_med_location[2]) +#endif +#ifdef ENABLE_DOT1 +MARSHAL_SUBTQ(lldpd_port, lldpd_vlan, p_vlans) +MARSHAL_SUBTQ(lldpd_port, lldpd_ppvid, p_ppvids) +MARSHAL_SUBTQ(lldpd_port, lldpd_pi, p_pids) +#endif +#ifdef ENABLE_CUSTOM +MARSHAL_SUBTQ(lldpd_port, lldpd_custom, p_custom_list) +#endif +MARSHAL_END(lldpd_port); + +/* Used to modify some port related settings */ +#define LLDPD_RXTX_UNCHANGED 0 +#define LLDPD_RXTX_TXONLY 1 +#define LLDPD_RXTX_RXONLY 2 +#define LLDPD_RXTX_DISABLED 3 +#define LLDPD_RXTX_BOTH 4 +#define LLDPD_RXTX_FROM_PORT(p) \ + (((p)->p_disable_rx && (p)->p_disable_tx) ? LLDPD_RXTX_DISABLED : \ + ((p)->p_disable_rx && !(p)->p_disable_tx) ? LLDPD_RXTX_TXONLY : \ + (!(p)->p_disable_rx && (p)->p_disable_tx) ? LLDPD_RXTX_RXONLY : \ + LLDPD_RXTX_BOTH) +#define LLDPD_RXTX_RXENABLED(v) ((v) == LLDPD_RXTX_RXONLY || (v) == LLDPD_RXTX_BOTH) +#define LLDPD_RXTX_TXENABLED(v) ((v) == LLDPD_RXTX_TXONLY || (v) == LLDPD_RXTX_BOTH) +struct lldpd_port_set { + char *ifname; + char *local_id; + char *local_descr; + int rxtx; + int vlan_tx_tag; + int vlan_tx_enabled; +#ifdef ENABLE_LLDPMED + struct lldpd_med_policy *med_policy; + struct lldpd_med_loc *med_location; + struct lldpd_med_power *med_power; +#endif +#ifdef ENABLE_DOT3 + struct lldpd_dot3_power *dot3_power; +#endif +#ifdef ENABLE_CUSTOM + struct lldpd_custom *custom; + int custom_list_clear; + int custom_tlv_op; +#endif +}; +MARSHAL_BEGIN(lldpd_port_set) +MARSHAL_STR(lldpd_port_set, ifname) +MARSHAL_STR(lldpd_port_set, local_id) +MARSHAL_STR(lldpd_port_set, local_descr) +#ifdef ENABLE_LLDPMED +MARSHAL_POINTER(lldpd_port_set, lldpd_med_policy, med_policy) +MARSHAL_POINTER(lldpd_port_set, lldpd_med_loc, med_location) +MARSHAL_POINTER(lldpd_port_set, lldpd_med_power, med_power) +#endif +#ifdef ENABLE_DOT3 +MARSHAL_POINTER(lldpd_port_set, lldpd_dot3_power, dot3_power) +#endif +#ifdef ENABLE_CUSTOM +MARSHAL_POINTER(lldpd_port_set, lldpd_custom, custom) +#endif +MARSHAL_END(lldpd_port_set); + +/* Smart mode / Hide mode */ +#define SMART_INCOMING_FILTER (1 << 0) /* Incoming filtering enabled */ +#define SMART_INCOMING_ONE_PROTO (1 << 1) /* On reception, keep only one proto */ +#define SMART_INCOMING_ONE_NEIGH (1 << 2) /* On reception, keep only one neighbor */ +#define SMART_OUTGOING_FILTER (1 << 3) /* Outgoing filtering enabled */ +#define SMART_OUTGOING_ONE_PROTO (1 << 4) /* On emission, keep only one proto */ +#define SMART_OUTGOING_ONE_NEIGH (1 << 5) /* On emission, only one neighbor */ +#define SMART_INCOMING \ + (SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO | SMART_INCOMING_ONE_NEIGH) +#define SMART_OUTGOING \ + (SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_PROTO | SMART_OUTGOING_ONE_NEIGH) + +struct lldpd_config { + int c_paused; /* lldpd is paused */ + int c_tx_interval; /* Transmit interval (in ms) */ + int c_ttl; /* TTL */ + int c_smart; /* Bitmask for smart configuration (see SMART_*) */ + int c_receiveonly; /* Receive only mode */ + int c_max_neighbors; /* Maximum number of neighbors (per protocol) */ + + char *c_mgmt_pattern; /* Pattern to match a management address */ + char *c_cid_pattern; /* Pattern to match interfaces to use for chassis ID */ + char *c_cid_string; /* User defined string for chassis ID */ + char *c_iface_pattern; /* Pattern to match interfaces to use */ + char *c_perm_ifaces; /* Pattern to match interfaces to keep */ + + char *c_platform; /* Override platform description (for CDP) */ + char *c_description; /* Override chassis description */ + char *c_hostname; /* Override system name */ + int c_advertise_version; /* Should the precise version be advertised? */ + int c_set_ifdescr; /* Set interface description */ + int c_promisc; /* Interfaces should be in promiscuous mode */ + int c_cap_advertise; /* Chassis capabilities advertisement */ + int c_cap_override; /* Override chassis capabilities enabled */ + int c_mgmt_advertise; /* Management addresses advertisement */ + +#ifdef ENABLE_LLDPMED + int c_noinventory; /* Don't send inventory with LLDP-MED */ + int c_enable_fast_start; /* enable fast start */ + int c_tx_fast_init; /* Num of lldpd lldppdu's for fast start */ + int c_tx_fast_interval; /* Time intr between sends during fast start */ +#endif + int c_tx_hold; /* Transmit hold */ + int c_bond_slave_src_mac_type; /* Src mac type in lldp frames over bond + slaves */ + int c_lldp_portid_type; /* The PortID type */ + int c_lldp_agent_type; /* The agent type */ +}; +MARSHAL_BEGIN(lldpd_config) +MARSHAL_STR(lldpd_config, c_mgmt_pattern) +MARSHAL_STR(lldpd_config, c_cid_pattern) +MARSHAL_STR(lldpd_config, c_cid_string) +MARSHAL_STR(lldpd_config, c_iface_pattern) +MARSHAL_STR(lldpd_config, c_perm_ifaces) +MARSHAL_STR(lldpd_config, c_hostname) +MARSHAL_STR(lldpd_config, c_platform) +MARSHAL_STR(lldpd_config, c_description) +MARSHAL_END(lldpd_config); + +struct lldpd_frame { + int size; + unsigned char frame[1]; +}; + +struct lldpd_hardware; +struct lldpd; +struct lldpd_ops { + int (*send)(struct lldpd *, struct lldpd_hardware *, char *, + size_t); /* Function to send a frame */ + int (*recv)(struct lldpd *, struct lldpd_hardware *, int, char *, + size_t); /* Function to receive a frame */ + int (*cleanup)(struct lldpd *, struct lldpd_hardware *); /* Cleanup function. */ +}; + +/* An interface is uniquely identified by h_ifindex, h_ifname and h_ops. This + * means if an interface becomes enslaved, it will be considered as a new + * interface. The same applies for renaming and we include the index in case of + * renaming to an existing interface. */ +struct lldpd_hardware { + TAILQ_ENTRY(lldpd_hardware) h_entries; + + struct lldpd *h_cfg; /* Pointer to main configuration */ + void *h_recv; /* FD for reception */ + int h_sendfd; /* FD for sending, only used by h_ops */ + int h_mangle; /* 1 if we have to mangle the MAC address */ + struct lldpd_ops *h_ops; /* Hardware-dependent functions */ + void *h_data; /* Hardware-dependent data */ + void *h_timer; /* Timer for this port */ + + int h_mtu; + int h_flags; /* Packets will be sent only + if IFF_RUNNING. Will be + removed if this is left + to 0. */ + int h_ifindex; /* Interface index, used by SNMP */ + int h_ifindex_changed; /* Interface index has changed */ + char h_ifname[IFNAMSIZ]; /* Should be unique */ + u_int8_t h_lladdr[ETHER_ADDR_LEN]; + + u_int64_t h_tx_cnt; + u_int64_t h_rx_cnt; + u_int64_t h_rx_discarded_cnt; + u_int64_t h_rx_unrecognized_cnt; + u_int64_t h_ageout_cnt; + u_int64_t h_insert_cnt; + u_int64_t h_delete_cnt; + u_int64_t h_drop_cnt; + + /* Previous values of different stuff. */ + /* Backup of the previous local port. Used to check if there was a + * change to send an immediate update. All those are not marshalled to + * the client. */ + void *h_lport_previous; + ssize_t h_lport_previous_len; + /* Backup of the previous chassis ID. Used to check if there was a + * change and send an LLDP shutdown. */ + u_int8_t h_lchassis_previous_id_subtype; + char *h_lchassis_previous_id; + int h_lchassis_previous_id_len; + /* Backup of the previous port ID. Used to check if there was a change + * and send an LLDP shutdown. */ + u_int8_t h_lport_previous_id_subtype; + char *h_lport_previous_id; + int h_lport_previous_id_len; + char *h_ifdescr_previous; + + struct lldpd_port h_lport; /* Port attached to this hardware port */ + TAILQ_HEAD(, lldpd_port) h_rports; /* Remote ports */ + +#ifdef ENABLE_LLDPMED + int h_tx_fast; /* current tx fast start count */ +#endif +}; +MARSHAL_BEGIN(lldpd_hardware) +MARSHAL_IGNORE(lldpd_hardware, h_entries.tqe_next) +MARSHAL_IGNORE(lldpd_hardware, h_entries.tqe_prev) +MARSHAL_IGNORE(lldpd_hardware, h_ops) +MARSHAL_IGNORE(lldpd_hardware, h_data) +MARSHAL_IGNORE(lldpd_hardware, h_cfg) +MARSHAL_IGNORE(lldpd_hardware, h_lport_previous) +MARSHAL_IGNORE(lldpd_hardware, h_lport_previous_len) +MARSHAL_IGNORE(lldpd_hardware, h_lchassis_previous_id_subtype) +MARSHAL_IGNORE(lldpd_hardware, h_lchassis_previous_id) +MARSHAL_IGNORE(lldpd_hardware, h_lchassis_previous_id_len) +MARSHAL_IGNORE(lldpd_hardware, h_lport_previous_id_subtype) +MARSHAL_IGNORE(lldpd_hardware, h_lport_previous_id) +MARSHAL_IGNORE(lldpd_hardware, h_lport_previous_id_len) +MARSHAL_SUBSTRUCT(lldpd_hardware, lldpd_port, h_lport) +MARSHAL_SUBTQ(lldpd_hardware, lldpd_port, h_rports) +MARSHAL_END(lldpd_hardware); + +struct lldpd_interface { + TAILQ_ENTRY(lldpd_interface) next; + char *name; +}; +MARSHAL_BEGIN(lldpd_interface) +MARSHAL_TQE(lldpd_interface, next) +MARSHAL_STR(lldpd_interface, name) +MARSHAL_END(lldpd_interface); +TAILQ_HEAD(lldpd_interface_list, lldpd_interface); +MARSHAL_TQ(lldpd_interface_list, lldpd_interface); + +struct lldpd_neighbor_change { + char *ifname; +#define NEIGHBOR_CHANGE_DELETED -1 +#define NEIGHBOR_CHANGE_ADDED 1 +#define NEIGHBOR_CHANGE_UPDATED 0 + int state; + struct lldpd_port *neighbor; +}; +MARSHAL_BEGIN(lldpd_neighbor_change) +MARSHAL_STR(lldpd_neighbor_change, ifname) +MARSHAL_POINTER(lldpd_neighbor_change, lldpd_port, neighbor) +MARSHAL_END(lldpd_neighbor_change); + +/* Cleanup functions */ +void lldpd_chassis_mgmt_cleanup(struct lldpd_chassis *); +void lldpd_chassis_cleanup(struct lldpd_chassis *, int); +void lldpd_remote_cleanup(struct lldpd_hardware *, + void (*expire)(struct lldpd_hardware *, struct lldpd_port *), int); +void lldpd_port_cleanup(struct lldpd_port *, int); +void lldpd_config_cleanup(struct lldpd_config *); +#ifdef ENABLE_DOT1 +void lldpd_ppvid_cleanup(struct lldpd_port *); +void lldpd_vlan_cleanup(struct lldpd_port *); +void lldpd_pi_cleanup(struct lldpd_port *); +#endif +#ifdef ENABLE_CUSTOM +void lldpd_custom_tlv_cleanup(struct lldpd_port *, struct lldpd_custom *); +void lldpd_custom_tlv_add(struct lldpd_port *, struct lldpd_custom *); +void lldpd_custom_list_cleanup(struct lldpd_port *); +#endif + +#endif diff --git a/src/log.c b/src/log.c new file mode 100644 index 0000000..ddfc14a --- /dev/null +++ b/src/log.c @@ -0,0 +1,279 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* $OpenBSD: log.c,v 1.11 2007/12/07 17:17:00 reyk Exp $ */ + +/* + * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> + * + * Permission to use, copy, modify, and/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 MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <syslog.h> +#include <sys/types.h> +#include <string.h> +#include <errno.h> +#include <time.h> +#include "log.h" + +/* By default, logging is done on stderr. */ +static int use_syslog = 0; +/* Default debug level */ +static int debug = 0; + +/* Logging can be modified by providing an appropriate log handler. */ +static void (*logh)(int severity, const char *msg) = NULL; + +static void vlog(int, const char *, const char *, va_list); +static void logit(int, const char *, const char *, ...); + +#define MAX_DBG_TOKENS 40 +static const char *tokens[MAX_DBG_TOKENS + 1] = { NULL }; + +void +log_init(int n_syslog, int n_debug, const char *progname) +{ + use_syslog = n_syslog; + debug = n_debug; + + if (use_syslog) openlog(progname, LOG_PID | LOG_NDELAY, LOG_DAEMON); + + tzset(); +} + +void +log_level(int n_debug) +{ + if (n_debug >= 0) debug = n_debug; +} + +void +log_register(void (*cb)(int, const char *)) +{ + logh = cb; +} + +void +log_accept(const char *token) +{ + int i; + for (i = 0; i < MAX_DBG_TOKENS; i++) { + if (tokens[i] == NULL) { + tokens[i + 1] = NULL; + tokens[i] = token; + return; + } + } +} + +static void +logit(int pri, const char *token, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vlog(pri, token, fmt, ap); + va_end(ap); +} + +static char * +date() +{ + /* Return the current date as incomplete ISO 8601 (2012-12-12T16:13:30) */ + static char date[] = "2012-12-12T16:13:30"; + time_t t = time(NULL); + struct tm *tmp = localtime(&t); + strftime(date, sizeof(date), "%Y-%m-%dT%H:%M:%S", tmp); + return date; +} + +static const char * +translate(int fd, int priority) +{ + /* Translate a syslog priority to a string. With colors if the output is a + * terminal. */ + int tty = isatty(fd); + switch (tty) { + case 1: + switch (priority) { + case LOG_EMERG: + return "\033[1;37;41m[EMRG"; + case LOG_ALERT: + return "\033[1;37;41m[ALRT"; + case LOG_CRIT: + return "\033[1;37;41m[CRIT"; + case LOG_ERR: + return "\033[1;31m[ ERR"; + case LOG_WARNING: + return "\033[1;33m[WARN"; + case LOG_NOTICE: + return "\033[1;34m[NOTI"; + case LOG_INFO: + return "\033[1;34m[INFO"; + case LOG_DEBUG: + return "\033[36m[ DBG"; + } + break; + default: + switch (priority) { + case LOG_EMERG: + return "[EMRG"; + case LOG_ALERT: + return "[ALRT"; + case LOG_CRIT: + return "[CRIT"; + case LOG_ERR: + return "[ ERR"; + case LOG_WARNING: + return "[WARN"; + case LOG_NOTICE: + return "[NOTI"; + case LOG_INFO: + return "[INFO"; + case LOG_DEBUG: + return "[ DBG"; + } + } + return "[UNKN]"; +} + +static void +vlog(int pri, const char *token, const char *fmt, va_list ap) +{ + if (logh) { + char *result = NULL; + if (vasprintf(&result, fmt, ap) != -1) { + logh(pri, result); + free(result); + return; + } + /* Otherwise, abort. We don't know if "ap" is still OK. We could + * have made a copy, but this is too much overhead for a + * situation that shouldn't happen. */ + return; + } + + /* Log to syslog if requested */ + if (use_syslog) { + va_list ap2; + va_copy(ap2, ap); + vsyslog(pri, fmt, ap2); + va_end(ap2); + } + + /* Log to standard error in all cases */ + char *nfmt; + /* best effort in out of mem situations */ + if (asprintf(&nfmt, "%s %s%s%s]%s %s\n", date(), translate(STDERR_FILENO, pri), + token ? "/" : "", token ? token : "", + isatty(STDERR_FILENO) ? "\033[0m" : "", fmt) == -1) { + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + } else { + vfprintf(stderr, nfmt, ap); + free(nfmt); + } + fflush(stderr); +} + +void +log_warn(const char *token, const char *emsg, ...) +{ + char *nfmt = NULL; + va_list ap; + + /* best effort to even work in out of memory situations */ + if (emsg == NULL) + logit(LOG_WARNING, "%s", strerror(errno)); + else { + va_start(ap, emsg); + + if (asprintf(&nfmt, "%s: %s", emsg, strerror(errno)) == -1) { + /* we tried it... */ + vlog(LOG_WARNING, token, emsg, ap); + logit(LOG_WARNING, "%s", strerror(errno)); + } else { + vlog(LOG_WARNING, token, nfmt, ap); + free(nfmt); + } + va_end(ap); + } +} + +void +log_warnx(const char *token, const char *emsg, ...) +{ + va_list ap; + + va_start(ap, emsg); + vlog(LOG_WARNING, token, emsg, ap); + va_end(ap); +} + +void +log_info(const char *token, const char *emsg, ...) +{ + va_list ap; + + if (use_syslog || debug > 0 || logh) { + va_start(ap, emsg); + vlog(LOG_INFO, token, emsg, ap); + va_end(ap); + } +} + +static int +log_debug_accept_token(const char *token) +{ + int i; + if (tokens[0] == NULL) return 1; + for (i = 0; (i < MAX_DBG_TOKENS) && (tokens[i] != NULL); i++) { + if (!strcmp(tokens[i], token)) return 1; + } + return 0; +} + +void +log_debug(const char *token, const char *emsg, ...) +{ + va_list ap; + + if ((debug > 1 && log_debug_accept_token(token)) || logh) { + va_start(ap, emsg); + vlog(LOG_DEBUG, token, emsg, ap); + va_end(ap); + } +} + +void +fatal(const char *token, const char *emsg) +{ + if (emsg == NULL) + logit(LOG_CRIT, token ? token : "fatal", "%s", strerror(errno)); + else if (errno) + logit(LOG_CRIT, token ? token : "fatal", "%s: %s", emsg, + strerror(errno)); + else + logit(LOG_CRIT, token ? token : "fatal", "%s", emsg); + + exit(1); +} + +void +fatalx(const char *token, const char *emsg) +{ + errno = 0; + fatal(token, emsg); +} diff --git a/src/log.h b/src/log.h new file mode 100644 index 0000000..a85ccbf --- /dev/null +++ b/src/log.h @@ -0,0 +1,39 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _LOG_H +#define _LOG_H + +#include <stdio.h> + +/* log.c */ +void log_init(int, int, const char *); +void log_warn(const char *, const char *, ...) __attribute__((format(printf, 2, 3))); +void log_warnx(const char *, const char *, ...) __attribute__((format(printf, 2, 3))); +void log_info(const char *, const char *, ...) __attribute__((format(printf, 2, 3))); +void log_debug(const char *, const char *, ...) __attribute__((format(printf, 2, 3))); +void fatal(const char *, const char *) __attribute__((__noreturn__)); +void fatalx(const char *, const char *) __attribute__((__noreturn__)); + +void log_register(void (*cb)(int, const char *)); +void log_accept(const char *); +void log_level(int); + +/* version.c */ +void version_display(FILE *, const char *, int); + +#endif diff --git a/src/marshal.c b/src/marshal.c new file mode 100644 index 0000000..c6a25f7 --- /dev/null +++ b/src/marshal.c @@ -0,0 +1,380 @@ +/* -*- 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. + */ + +#define MARSHAL_EXPORT +#include "marshal.h" + +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/queue.h> +#include <string.h> + +#include "compat/compat.h" +#include "log.h" + +#include "lldpd-structs.h" + +/* Stolen from CCAN */ +#if HAVE_ALIGNOF +# define ALIGNOF(t) (__alignof__(t)) +#else +# define ALIGNOF(t) \ + ((sizeof(t) > 1) ? \ + ((char *)(&((struct { \ + char c; \ + t _h; \ + } *)0) \ + ->_h) - \ + (char *)0) : \ + 1) +#endif + +/* A serialized object */ +struct marshal_serialized { + void *orig; /* Original reference. Also enforce alignment. */ + size_t size; + unsigned char object[0]; +}; + +struct marshal_info marshal_info_string = { + .name = "null string", + .size = 0, + .pointers = { MARSHAL_SUBINFO_NULL }, +}; +struct marshal_info marshal_info_fstring = { + .name = "fixed string", + .size = 0, + .pointers = { MARSHAL_SUBINFO_NULL }, +}; +struct marshal_info marshal_info_ignore = { + .name = "ignored", + .size = 0, + .pointers = { MARSHAL_SUBINFO_NULL }, +}; + +/* List of already seen pointers */ +struct ref { + TAILQ_ENTRY(ref) next; + void *pointer; + uintptr_t dummy; /* To renumerate pointers */ +}; +TAILQ_HEAD(ref_l, ref); + +/* Serialize the given object. */ +ssize_t +marshal_serialize_(struct marshal_info *mi, void *unserialized, void **input, int skip, + void *_refs, int osize) +{ + struct ref_l *refs = _refs; + struct ref *cref; + int size; + size_t len; + struct marshal_subinfo *current; + struct marshal_serialized *new = NULL, *serialized = NULL; + uintptr_t dummy = 1; + + log_debug("marshal", "start serialization of %s", mi->name); + + /* Check if we have already serialized this one. */ + if (!refs) { + refs = calloc(1, sizeof(struct ref_l)); + if (!refs) { + log_warnx("marshal", + "unable to allocate memory for list of references"); + return -1; + } + TAILQ_INIT(refs); + } + TAILQ_FOREACH (cref, refs, next) { + if (unserialized == cref->pointer) return 0; + /* dummy should be higher than any existing dummy */ + if (cref->dummy >= dummy) dummy = cref->dummy + 1; + } + + /* Handle special cases. */ + size = mi->size; + if (!strcmp(mi->name, "null string")) /* We know we can't be called with NULL */ + size = strlen((char *)unserialized) + 1; + else if (!strcmp(mi->name, "fixed string")) + size = osize; + + /* Allocate serialized structure */ + len = sizeof(struct marshal_serialized) + (skip ? 0 : size); + serialized = calloc(1, len); + if (!serialized) { + log_warnx("marshal", + "unable to allocate memory to serialize structure %s", mi->name); + len = -1; + goto marshal_error; + } + /* We don't use the original pointer but a dummy one. */ + serialized->orig = (unsigned char *)dummy; + + /* Append the new reference */ + if (!(cref = calloc(1, sizeof(struct ref)))) { + log_warnx("marshal", + "unable to allocate memory for list of references"); + free(serialized); + len = -1; + goto marshal_error; + } + cref->pointer = unserialized; + cref->dummy = dummy; + TAILQ_INSERT_TAIL(refs, cref, next); + + /* First, serialize the main structure */ + if (!skip) memcpy(serialized->object, unserialized, size); + + /* Then, serialize inner structures */ + for (current = mi->pointers; current->mi; current++) { + size_t sublen; + size_t padlen; + void *source; + void *target = NULL; + if (current->kind == ignore) continue; + if (current->kind == pointer) { + memcpy(&source, (unsigned char *)unserialized + current->offset, + sizeof(void *)); + if (source == NULL) continue; + } else + source = + (void *)((unsigned char *)unserialized + current->offset); + if (current->offset2) + memcpy(&osize, (unsigned char *)unserialized + current->offset2, + sizeof(int)); + target = NULL; + sublen = marshal_serialize_(current->mi, source, &target, + current->kind == substruct, refs, osize); + if (sublen == -1) { + log_warnx("marshal", + "unable to serialize substructure %s for %s", + current->mi->name, mi->name); + free(serialized); + return -1; + } + /* We want to put the renumerated pointer instead of the real one. */ + if (current->kind == pointer && !skip) { + TAILQ_FOREACH (cref, refs, next) { + if (source == cref->pointer) { + void *fakepointer = + (unsigned char *)cref->dummy; + memcpy((unsigned char *)serialized->object + + current->offset, + &fakepointer, sizeof(void *)); + break; + } + } + } + if (sublen == 0) continue; /* This was already serialized */ + /* Append the result, force alignment to be able to unserialize it */ + padlen = ALIGNOF(struct marshal_serialized); + padlen = (padlen - (len % padlen)) % padlen; + new = realloc(serialized, len + padlen + sublen); + if (!new) { + log_warnx("marshal", + "unable to allocate more memory to serialize structure %s", + mi->name); + free(serialized); + free(target); + len = -1; + goto marshal_error; + } + memset((unsigned char *)new + len, 0, padlen); + memcpy((unsigned char *)new + len + padlen, target, sublen); + free(target); + len += sublen + padlen; + serialized = (struct marshal_serialized *)new; + } + + serialized->size = len; + *input = serialized; +marshal_error: + if (refs && !_refs) { + struct ref *cref, *cref_next; + for (cref = TAILQ_FIRST(refs); cref != NULL; cref = cref_next) { + cref_next = TAILQ_NEXT(cref, next); + TAILQ_REMOVE(refs, cref, next); + free(cref); + } + free(refs); + } + return len; +} + +/* This structure is used to track memory allocation when serializing */ +struct gc { + TAILQ_ENTRY(gc) next; + void *pointer; + void *orig; /* Original reference (not valid anymore !) */ +}; +TAILQ_HEAD(gc_l, gc); + +static void * +marshal_alloc(struct gc_l *pointers, size_t len, void *orig) +{ + struct gc *gpointer = NULL; + + void *result = calloc(1, len); + if (!result) return NULL; + if ((gpointer = (struct gc *)calloc(1, sizeof(struct gc))) == NULL) { + free(result); + return NULL; + } + gpointer->pointer = result; + gpointer->orig = orig; + TAILQ_INSERT_TAIL(pointers, gpointer, next); + return result; +} +static void +marshal_free(struct gc_l *pointers, int gconly) +{ + struct gc *pointer, *pointer_next; + for (pointer = TAILQ_FIRST(pointers); pointer != NULL; pointer = pointer_next) { + pointer_next = TAILQ_NEXT(pointer, next); + TAILQ_REMOVE(pointers, pointer, next); + if (!gconly) free(pointer->pointer); + free(pointer); + } +} + +/* Unserialize the given object. */ +size_t +marshal_unserialize_(struct marshal_info *mi, void *buffer, size_t len, void **output, + void *_pointers, int skip, int osize) +{ + int total_len = sizeof(struct marshal_serialized) + (skip ? 0 : mi->size); + struct marshal_serialized *serialized = buffer; + struct gc_l *pointers = _pointers; + int size, already, extra = 0; + void *new; + struct marshal_subinfo *current; + struct gc *apointer; + + log_debug("marshal", "start unserialization of %s", mi->name); + + if (len < sizeof(struct marshal_serialized) || len < total_len) { + log_warnx("marshal", + "data to deserialize is too small (%zu) for structure %s", len, + mi->name); + return 0; + } + + /* Initialize garbage collection */ + if (!pointers) { + pointers = calloc(1, sizeof(struct gc_l)); + if (!pointers) { + log_warnx("marshal", + "unable to allocate memory for garbage collection"); + return 0; + } + TAILQ_INIT(pointers); + } + + /* Special cases */ + size = mi->size; + if (!strcmp(mi->name, "null string") || !strcmp(mi->name, "fixed string")) { + switch (mi->name[0]) { + case 'n': + size = strnlen((char *)serialized->object, + len - sizeof(struct marshal_serialized)) + + 1; + break; + case 'f': + size = osize; + extra = 1; + break; /* The extra byte is to ensure that + the string is null terminated. */ + } + if (size > len - sizeof(struct marshal_serialized)) { + log_warnx("marshal", + "data to deserialize contains a string too long"); + total_len = 0; + goto unmarshal_error; + } + total_len += size; + } + + /* First, the main structure */ + if (!skip) { + if ((*output = marshal_alloc(pointers, size + extra, + serialized->orig)) == NULL) { + log_warnx("marshal", + "unable to allocate memory to unserialize structure %s", + mi->name); + total_len = 0; + goto unmarshal_error; + } + memcpy(*output, serialized->object, size); + } + + /* Then, each substructure */ + for (current = mi->pointers; current->mi; current++) { + size_t sublen; + size_t padlen; + new = (unsigned char *)*output + current->offset; + if (current->kind == ignore) { + memset((unsigned char *)*output + current->offset, 0, + sizeof(void *)); + continue; + } + if (current->kind == pointer) { + if (*(void **)new == NULL) continue; + + /* Did we already see this reference? */ + already = 0; + TAILQ_FOREACH (apointer, pointers, next) + if (apointer->orig == *(void **)new) { + memcpy((unsigned char *)*output + + current->offset, + &apointer->pointer, sizeof(void *)); + already = 1; + break; + } + if (already) continue; + } + /* Deserialize */ + if (current->offset2) + memcpy(&osize, (unsigned char *)*output + current->offset2, + sizeof(int)); + padlen = ALIGNOF(struct marshal_serialized); + padlen = (padlen - (total_len % padlen)) % padlen; + if (len < total_len + padlen || + ((sublen = marshal_unserialize_(current->mi, + (unsigned char *)buffer + total_len + padlen, + len - total_len - padlen, &new, pointers, + current->kind == substruct, osize)) == 0)) { + log_warnx("marshal", + "unable to serialize substructure %s for %s", + current->mi->name, mi->name); + total_len = 0; + goto unmarshal_error; + } + /* Link the result */ + if (current->kind == pointer) + memcpy((unsigned char *)*output + current->offset, &new, + sizeof(void *)); + total_len += sublen + padlen; + } + +unmarshal_error: + if (pointers && !_pointers) { + marshal_free(pointers, (total_len > 0)); + free(pointers); + } + return total_len; +} diff --git a/src/marshal.h b/src/marshal.h new file mode 100644 index 0000000..e28a011 --- /dev/null +++ b/src/marshal.h @@ -0,0 +1,158 @@ +/* -*- 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 _MARSHAL_H +#define _MARSHAL_H + +#include <stddef.h> +#include <stdint.h> +#include <sys/types.h> + +struct marshal_info; +enum marshal_subinfo_kind { + pointer, + substruct, + ignore, +}; +#define MARSHAL_INFO_POINTER 1 +#define MARSHAL_INFO_SUB 2 +struct marshal_subinfo { + size_t offset; /* Offset compared to parent structure */ + size_t offset2; /* Ancillary offset (for related data) */ + enum marshal_subinfo_kind kind; /* Kind of substructure */ + struct marshal_info *mi; +}; +#define MARSHAL_SUBINFO_NULL \ + { \ + .offset = 0, .offset2 = 0, .kind = ignore, .mi = NULL \ + } +struct marshal_info { + const char *name; /* Name of structure */ + size_t size; /* Size of the structure */ +#if defined __GNUC__ && __GNUC__ < 3 + /* With gcc 2.96, flexible arrays are not supported, even with + * -std=gnu99. And with gcc 3.x, zero-sized arrays cannot be statically + * initialized (with more than one element). */ + struct marshal_subinfo pointers[0]; /* Pointer to other structures */ +#else + struct marshal_subinfo pointers[]; /* Pointer to other structures */ +#endif +}; +/* Special case for strings */ +extern struct marshal_info marshal_info_string; +extern struct marshal_info marshal_info_fstring; +extern struct marshal_info marshal_info_ignore; + +/* Declare a new marshal_info struct named after the type we want to + marshal. The marshalled type has to be a structure. */ +#define MARSHAL_INFO(type) marshal_info_##type +#ifdef MARSHAL_EXPORT +# define MARSHAL_HELPER_FUNCTIONS(type, ttype) \ + ssize_t type##_serialize(ttype *source, void *buffer); \ + ssize_t type##_serialize(ttype *source, void *buffer) \ + { \ + return marshal_serialize(type, source, buffer); \ + } \ + size_t type##_unserialize(void *buffer, size_t len, ttype **destination); \ + size_t type##_unserialize(void *buffer, size_t len, ttype **destination) \ + { \ + void *p; \ + size_t rc; \ + rc = marshal_unserialize(type, buffer, len, &p); \ + if (rc <= 0) return rc; \ + *destination = p; \ + return rc; \ + } +# define MARSHAL_BEGIN(type) \ + struct marshal_info MARSHAL_INFO( \ + type) = { .name = #type, .size = sizeof(struct type), .pointers = { +# define MARSHAL_ADD(_kind, type, subtype, member) \ + { .offset = offsetof(struct type, member), \ + .offset2 = 0, \ + .kind = _kind, \ + .mi = &MARSHAL_INFO(subtype) }, +# define MARSHAL_FSTR(type, member, len) \ + { .offset = offsetof(struct type, member), \ + .offset2 = offsetof(struct type, len), \ + .kind = pointer, \ + .mi = &marshal_info_fstring }, +# define MARSHAL_END(type) \ + MARSHAL_SUBINFO_NULL \ + } \ + } \ + ; \ + MARSHAL_HELPER_FUNCTIONS(type, struct type) +#else +# define MARSHAL_HELPER_FUNCTIONS(type, ttype) \ + ssize_t type##_serialize(ttype *, void *); \ + size_t type##_unserialize(void *, size_t, ttype **); +# define MARSHAL_BEGIN(type) extern struct marshal_info MARSHAL_INFO(type); +# define MARSHAL_ADD(...) +# define MARSHAL_FSTR(...) +# define MARSHAL_END(type) MARSHAL_HELPER_FUNCTIONS(type, struct type) +#endif +/* Shortcuts */ +#define MARSHAL_POINTER(...) MARSHAL_ADD(pointer, ##__VA_ARGS__) +#define MARSHAL_SUBSTRUCT(...) MARSHAL_ADD(substruct, ##__VA_ARGS__) +#define MARSHAL_STR(type, member) MARSHAL_ADD(pointer, type, string, member) +#define MARSHAL_IGNORE(type, member) MARSHAL_ADD(ignore, type, ignore, member) +#define MARSHAL_TQE(type, field) \ + MARSHAL_POINTER(type, type, field.tqe_next) \ + MARSHAL_IGNORE(type, field.tqe_prev) +/* Support for TAILQ list is partial. Access to last and previous + elements is not available. Some operations are therefore not + possible. However, TAILQ_FOREACH is still + available. */ +#define MARSHAL_TQH(type, subtype) \ + MARSHAL_POINTER(type, subtype, tqh_first) \ + MARSHAL_IGNORE(type, tqh_last) +#define MARSHAL_SUBTQ(type, subtype, field) \ + MARSHAL_POINTER(type, subtype, field.tqh_first) \ + MARSHAL_IGNORE(type, field.tqh_last) +#define MARSHAL(type) \ + MARSHAL_BEGIN(type) \ + MARSHAL_END(type) +#define MARSHAL_TQ(type, subtype) \ + MARSHAL_BEGIN(type) \ + MARSHAL_TQH(type, subtype) \ + MARSHAL_END(type) + +/* Serialization */ +ssize_t marshal_serialize_(struct marshal_info *, void *, void **, int, void *, int) + __attribute__((nonnull(1, 2, 3))); +#define marshal_serialize(type, o, output) \ + marshal_serialize_(&MARSHAL_INFO(type), o, output, 0, NULL, 0) + +/* Unserialization */ +size_t marshal_unserialize_(struct marshal_info *, void *, size_t, void **, void *, int, + int) __attribute__((nonnull(1, 2, 4))); +#define marshal_unserialize(type, o, l, input) \ + marshal_unserialize_(&MARSHAL_INFO(type), o, l, input, NULL, 0, 0) + +#define marshal_repair_tailq(type, head, field) \ + do { \ + struct type *__item, *__item_next; \ + (head)->tqh_last = &(head)->tqh_first; \ + for (__item = TAILQ_FIRST(head); __item != NULL; __item = __item_next) { \ + __item_next = TAILQ_NEXT(__item, field); \ + __item->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = __item; \ + (head)->tqh_last = &__item->field.tqe_next; \ + } \ + } while (0) + +#endif diff --git a/src/version.c b/src/version.c new file mode 100644 index 0000000..c921564 --- /dev/null +++ b/src/version.c @@ -0,0 +1,138 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2016 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 + +#include <stdio.h> +#include "compat/compat.h" +#include "log.h" + +static void +version_display_array(FILE *destination, const char *prefix, const char *const *items) +{ + fprintf(destination, "%s", prefix); + size_t count = 0; + for (const char *const *p = items; *p; p++, count++) + fprintf(destination, "%s%s", count ? ", " : "", *p); + if (count == 0) + fprintf(destination, "(none)\n"); + else + fprintf(destination, "\n"); +} + +void +version_display(FILE *destination, const char *progname, int verbose) +{ + if (!verbose) { + fprintf(destination, "%s\n", PACKAGE_VERSION); + return; + } + + const char *const lldp_features[] = { +#ifdef ENABLE_LLDPMED + "LLDP-MED", +#endif +#ifdef ENABLE_DOT1 + "Dot1", +#endif +#ifdef ENABLE_DOT3 + "Dot3", +#endif +#ifdef ENABLE_CUSTOM + "Custom TLV", +#endif + NULL + }; + const char *const protocols[] = { +#ifdef ENABLE_CDP + "CDP", +#endif +#ifdef ENABLE_FDP + "FDP", +#endif +#ifdef ENABLE_EDP + "EDP", +#endif +#ifdef ENABLE_SONMP + "SONMP", +#endif + NULL + }; + const char *const output_formats[] = { "TEXT", "KV", "JSON", +#ifdef USE_XML + "XML", +#endif + NULL }; + + fprintf(destination, "%s %s\n", progname, PACKAGE_VERSION); + fprintf(destination, " Built on " BUILD_DATE "\n"); + fprintf(destination, "\n"); + + /* Features */ + if (!strcmp(progname, "lldpd")) { + version_display_array(destination, + "Additional LLDP features: ", lldp_features); + version_display_array(destination, + "Additional protocols: ", protocols); + fprintf(destination, + "SNMP support: " +#ifdef USE_SNMP + "yes\n" +#else + "no\n" +#endif + ); +#ifdef HOST_OS_LINUX + fprintf(destination, + "Old kernel support: " +# ifdef ENABLE_OLDIES + "yes" +# else + "no" +# endif + " (Linux " MIN_LINUX_KERNEL_VERSION "+)\n"); +#endif +#ifdef ENABLE_PRIVSEP + fprintf(destination, + "Privilege separation: " + "enabled\n"); + fprintf(destination, "Privilege separation user: " PRIVSEP_USER "\n"); + fprintf(destination, + "Privilege separation group: " PRIVSEP_GROUP "\n"); + fprintf(destination, + "Privilege separation chroot: " PRIVSEP_CHROOT "\n"); +#else + fprintf(destination, + "Privilege separation: " + "disabled\n"); +#endif + fprintf(destination, "Configuration directory: " SYSCONFDIR "\n"); + } + + if (!strcmp(progname, "lldpcli")) { + version_display_array(destination, + "Additional output formats: ", output_formats); + } + + fprintf(destination, "\n"); + + /* Build */ + fprintf(destination, "C compiler command: %s\n", LLDP_CC); + fprintf(destination, "Linker command: %s\n", LLDP_LD); +} |