Merge branch 'stable' into pud
authorFerry Huberts <f.huberts@mindef.nl>
Wed, 5 Oct 2011 14:42:34 +0000 (16:42 +0200)
committerFerry Huberts <f.huberts@mindef.nl>
Wed, 5 Oct 2011 14:42:34 +0000 (16:42 +0200)
* stable:
  Update version to pre-0.6.3
  Add changelog for 0.6.2
  Update version to 0.6.2
  Sanitize the kernel check function for linux
  3 != 2
  Fix Linux kernel version check for >= 3.x.x

41 files changed:
Makefile
lib/pud/.cproject [new file with mode: 0644]
lib/pud/.gitignore [new file with mode: 0644]
lib/pud/.project [new file with mode: 0644]
lib/pud/Makefile [new file with mode: 0644]
lib/pud/Makefile.inc [new file with mode: 0644]
lib/pud/README [new file with mode: 0644]
lib/pud/doc/.gitignore [new file with mode: 0644]
lib/pud/doc/Makefile [new file with mode: 0644]
lib/pud/doc/doxygen.conf [new file with mode: 0644]
lib/pud/doc/olsrd.conf.default.pud [new file with mode: 0644]
lib/pud/doc/pud.odt [new file with mode: 0755]
lib/pud/scripts/makeVersionH [new file with mode: 0755]
lib/pud/src/compiler.h [new file with mode: 0644]
lib/pud/src/configuration.c [new file with mode: 0644]
lib/pud/src/configuration.h [new file with mode: 0644]
lib/pud/src/dedup.c [new file with mode: 0644]
lib/pud/src/dedup.h [new file with mode: 0644]
lib/pud/src/dump.c [new file with mode: 0644]
lib/pud/src/dump.h [new file with mode: 0644]
lib/pud/src/gpsConversion.c [new file with mode: 0644]
lib/pud/src/gpsConversion.h [new file with mode: 0644]
lib/pud/src/netTools.c [new file with mode: 0644]
lib/pud/src/netTools.h [new file with mode: 0644]
lib/pud/src/networkInterfaces.c [new file with mode: 0644]
lib/pud/src/networkInterfaces.h [new file with mode: 0644]
lib/pud/src/nodeIdConversion.c [new file with mode: 0644]
lib/pud/src/nodeIdConversion.h [new file with mode: 0644]
lib/pud/src/posAvg.c [new file with mode: 0644]
lib/pud/src/posAvg.h [new file with mode: 0644]
lib/pud/src/pud.c [new file with mode: 0644]
lib/pud/src/pud.h [new file with mode: 0644]
lib/pud/src/pudOlsrdPlugin.c [new file with mode: 0644]
lib/pud/src/pudOlsrdPlugin.h [new file with mode: 0644]
lib/pud/src/receiver.c [new file with mode: 0644]
lib/pud/src/receiver.h [new file with mode: 0644]
lib/pud/src/timers.c [new file with mode: 0644]
lib/pud/src/timers.h [new file with mode: 0644]
lib/pud/src/wireFormat.c [new file with mode: 0644]
lib/pud/src/wireFormat.h [new file with mode: 0644]
lib/pud/version-script.txt [new file with mode: 0644]

index 6be4a2e..c3ffb17 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -325,6 +325,16 @@ tas_install:
 tas_uninstall:
                @$(MAKECMD) -C lib/tas DESTDIR=$(DESTDIR) uninstall
 
+pud:
+               $(MAKECMD) -C lib/pud clean
+               $(MAKECMD) -C lib/pud 
+
+pud_install:
+               $(MAKECMD) -C lib/pud DESTDIR=$(DESTDIR) install 
+
+pud_uninstall:
+               $(MAKECMD) -C lib/pud DESTDIR=$(DESTDIR) uninstall
+
 txtinfo:
                @$(MAKECMD) -C lib/txtinfo clean
                @$(MAKECMD) -C lib/txtinfo
diff --git a/lib/pud/.cproject b/lib/pud/.cproject
new file mode 100644 (file)
index 0000000..613593f
--- /dev/null
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<?fileVersion 4.0.0?>
+
+<cproject storage_type_id="org.eclipse.cdt.core.XmlProjectDescriptionStorage">
+       <storageModule moduleId="org.eclipse.cdt.core.settings">
+               <cconfiguration id="cdt.managedbuild.toolchain.gnu.base.1351149791">
+                       <storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="cdt.managedbuild.toolchain.gnu.base.1351149791" moduleId="org.eclipse.cdt.core.settings" name="Default">
+                               <externalSettings/>
+                               <extensions>
+                                       <extension id="org.eclipse.cdt.core.ELF" point="org.eclipse.cdt.core.BinaryParser"/>
+                                       <extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+                                       <extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/>
+                                       <extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+                                       <extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+                                       <extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+                               </extensions>
+                       </storageModule>
+                       <storageModule moduleId="cdtBuildSystem" version="4.0.0">
+                               <configuration buildProperties="" description="" id="cdt.managedbuild.toolchain.gnu.base.1351149791" name="Default" parent="org.eclipse.cdt.build.core.emptycfg">
+                                       <folderInfo id="cdt.managedbuild.toolchain.gnu.base.1351149791.194568260" name="/" resourcePath="">
+                                               <toolChain id="cdt.managedbuild.toolchain.gnu.base.1857599947" name="cdt.managedbuild.toolchain.gnu.base" superClass="cdt.managedbuild.toolchain.gnu.base">
+                                                       <targetPlatform archList="all" binaryParser="org.eclipse.cdt.core.ELF" id="cdt.managedbuild.target.gnu.platform.base.96293551" name="Debug Platform" osList="linux,hpux,aix,qnx" superClass="cdt.managedbuild.target.gnu.platform.base"/>
+                                                       <builder id="cdt.managedbuild.target.gnu.builder.base.110180720" keepEnvironmentInBuildfile="false" managedBuildOn="false" name="Gnu Make Builder" superClass="cdt.managedbuild.target.gnu.builder.base"/>
+                                                       <tool id="cdt.managedbuild.tool.gnu.archiver.base.1748744861" name="GCC Archiver" superClass="cdt.managedbuild.tool.gnu.archiver.base"/>
+                                                       <tool id="cdt.managedbuild.tool.gnu.cpp.compiler.base.1915084422" name="GCC C++ Compiler" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.base"/>
+                                                       <tool id="cdt.managedbuild.tool.gnu.c.compiler.base.1191190745" name="GCC C Compiler" superClass="cdt.managedbuild.tool.gnu.c.compiler.base">
+                                                               <option id="gnu.c.compiler.option.include.paths.1918367451" superClass="gnu.c.compiler.option.include.paths" valueType="includePath">
+                                                                       <listOptionValue builtIn="false" value="&quot;${workspace_loc:/olsrd/src}&quot;"/>
+                                                               </option>
+                                                               <inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.543924301" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
+                                                       </tool>
+                                                       <tool id="cdt.managedbuild.tool.gnu.c.linker.base.1962353518" name="GCC C Linker" superClass="cdt.managedbuild.tool.gnu.c.linker.base">
+                                                               <inputType id="cdt.managedbuild.tool.gnu.c.linker.input.1776642444" superClass="cdt.managedbuild.tool.gnu.c.linker.input">
+                                                                       <additionalInput kind="additionalinputdependency" paths="$(USER_OBJS)"/>
+                                                                       <additionalInput kind="additionalinput" paths="$(LIBS)"/>
+                                                               </inputType>
+                                                       </tool>
+                                                       <tool id="cdt.managedbuild.tool.gnu.cpp.linker.base.461751874" name="GCC C++ Linker" superClass="cdt.managedbuild.tool.gnu.cpp.linker.base"/>
+                                                       <tool id="cdt.managedbuild.tool.gnu.assembler.base.2086764709" name="GCC Assembler" superClass="cdt.managedbuild.tool.gnu.assembler.base">
+                                                               <inputType id="cdt.managedbuild.tool.gnu.assembler.input.361450686" superClass="cdt.managedbuild.tool.gnu.assembler.input"/>
+                                                       </tool>
+                                               </toolChain>
+                                       </folderInfo>
+                               </configuration>
+                       </storageModule>
+                       <storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
+               </cconfiguration>
+       </storageModule>
+       <storageModule moduleId="cdtBuildSystem" version="4.0.0">
+               <project id="pud.null.1418229864" name="pud"/>
+       </storageModule>
+       <storageModule moduleId="refreshScope" versionNumber="1">
+               <resource resourceType="PROJECT" workspacePath="/olsrdPud"/>
+       </storageModule>
+       <storageModule moduleId="org.eclipse.cdt.make.core.buildtargets"/>
+       <storageModule moduleId="scannerConfiguration">
+               <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
+               <scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.base.1351149791;cdt.managedbuild.toolchain.gnu.base.1351149791.194568260;cdt.managedbuild.tool.gnu.c.compiler.base.1191190745;cdt.managedbuild.tool.gnu.c.compiler.input.543924301">
+                       <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC"/>
+               </scannerConfigBuildInfo>
+       </storageModule>
+</cproject>
diff --git a/lib/pud/.gitignore b/lib/pud/.gitignore
new file mode 100644 (file)
index 0000000..b4d46a8
--- /dev/null
@@ -0,0 +1,2 @@
+/olsrd.lock
+/src/version.h
diff --git a/lib/pud/.project b/lib/pud/.project
new file mode 100644 (file)
index 0000000..735c206
--- /dev/null
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>olsrdPud</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <buildCommand>
+                       <name>org.eclipse.cdt.managedbuilder.core.genmakebuilder</name>
+                       <triggers>clean,full,incremental,</triggers>
+                       <arguments>
+                               <dictionary>
+                                       <key>?name?</key>
+                                       <value></value>
+                               </dictionary>
+                               <dictionary>
+                                       <key>org.eclipse.cdt.make.core.append_environment</key>
+                                       <value>true</value>
+                               </dictionary>
+                               <dictionary>
+                                       <key>org.eclipse.cdt.make.core.autoBuildTarget</key>
+                                       <value>all</value>
+                               </dictionary>
+                               <dictionary>
+                                       <key>org.eclipse.cdt.make.core.buildArguments</key>
+                                       <value></value>
+                               </dictionary>
+                               <dictionary>
+                                       <key>org.eclipse.cdt.make.core.buildCommand</key>
+                                       <value>make</value>
+                               </dictionary>
+                               <dictionary>
+                                       <key>org.eclipse.cdt.make.core.cleanBuildTarget</key>
+                                       <value>clean</value>
+                               </dictionary>
+                               <dictionary>
+                                       <key>org.eclipse.cdt.make.core.contents</key>
+                                       <value>org.eclipse.cdt.make.core.activeConfigSettings</value>
+                               </dictionary>
+                               <dictionary>
+                                       <key>org.eclipse.cdt.make.core.enableAutoBuild</key>
+                                       <value>false</value>
+                               </dictionary>
+                               <dictionary>
+                                       <key>org.eclipse.cdt.make.core.enableCleanBuild</key>
+                                       <value>true</value>
+                               </dictionary>
+                               <dictionary>
+                                       <key>org.eclipse.cdt.make.core.enableFullBuild</key>
+                                       <value>true</value>
+                               </dictionary>
+                               <dictionary>
+                                       <key>org.eclipse.cdt.make.core.fullBuildTarget</key>
+                                       <value>all</value>
+                               </dictionary>
+                               <dictionary>
+                                       <key>org.eclipse.cdt.make.core.stopOnError</key>
+                                       <value>true</value>
+                               </dictionary>
+                               <dictionary>
+                                       <key>org.eclipse.cdt.make.core.useDefaultBuildCmd</key>
+                                       <value>true</value>
+                               </dictionary>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder</name>
+                       <triggers>full,incremental,</triggers>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+       </buildSpec>
+       <natures>
+               <nature>org.eclipse.cdt.core.cnature</nature>
+               <nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature>
+               <nature>org.eclipse.cdt.managedbuilder.core.ScannerConfigNature</nature>
+       </natures>
+</projectDescription>
diff --git a/lib/pud/Makefile b/lib/pud/Makefile
new file mode 100644 (file)
index 0000000..43bfcbb
--- /dev/null
@@ -0,0 +1,68 @@
+include Makefile.inc
+
+TOPDIR = ../..
+include $(TOPDIR)/Makefile.inc
+
+
+#CFLAGS += -DPUD_DUMP_GPS_PACKETS_RX_NON_OLSR
+#CFLAGS += -DPUD_DUMP_GPS_PACKETS_TX_OLSR
+
+#CFLAGS += -DPUD_DUMP_GPS_PACKETS_RX_OLSR
+#CFLAGS += -DPUD_DUMP_GPS_PACKETS_TX_NON_OLSR
+
+#CFLAGS += -DPUD_DUMP_AVERAGING
+#CFLAGS += -DPUD_DUMP_DEDUP
+
+
+CFLAGS += -Werror -D_GNU_SOURCE
+
+
+VERSION_FILE = ./src/version.h
+
+LIBS += -lnmea -lm
+
+
+ifneq ($(OS),linux)
+
+default_target install clean:
+       @echo "*** $(PLUGIN_NAME) plugin only supported on Linux, sorry!"
+
+else
+
+all: default_target
+
+default_target: $(VERSION_FILE) $(PLUGIN_FULLNAME)
+
+$(PLUGIN_FULLNAME): $(OBJS) version-script.txt
+       @echo "[LD] $@"
+       @$(CC) $(LDFLAGS) -o $(PLUGIN_FULLNAME) $(OBJS) $(LIBS)
+
+$(VERSION_FILE): Makefile scripts/makeVersionH
+       @echo "[$@]"
+       @./scripts/makeVersionH "$@" "$(PLUGIN_VER)"
+
+
+.PHONY:        install uninstall clean dist distclean doc doc-clean
+
+install: $(PLUGIN_FULLNAME)
+       $(INSTALL_LIB)
+       $(STRIP) "$(LIBDIR)/$(PLUGIN_FULLNAME)"
+
+uninstall:
+       rm -f "$(LIBDIR)/lib$(PLUGIN_NAME).so" "$(LIBDIR)/$(PLUGIN_NAME)"
+       $(UNINSTALL_LIB)
+
+clean: doc-clean
+       @echo "[$@]"
+       @rm -f $(OBJS) $(SRCS:%.c=%.d) "$(PLUGIN_FULLNAME)" "$(VERSION_FILE)"
+
+dist:                          distclean
+distclean:                     clean
+
+doc:
+       @$(MAKE) -C doc all
+
+doc-clean:
+       @$(MAKE) -C doc clean
+
+endif
diff --git a/lib/pud/Makefile.inc b/lib/pud/Makefile.inc
new file mode 100644 (file)
index 0000000..4cfe12a
--- /dev/null
@@ -0,0 +1,3 @@
+OLSRD_PLUGIN = true
+PLUGIN_NAME  = olsrd_pud
+PLUGIN_VER   = 1.0.0
diff --git a/lib/pud/README b/lib/pud/README
new file mode 100644 (file)
index 0000000..916e2c0
--- /dev/null
@@ -0,0 +1,3 @@
+This plugin depends on the NMEA parsing library that can be found at:
+
+       https://github.com/fhuberts/nmealib
diff --git a/lib/pud/doc/.gitignore b/lib/pud/doc/.gitignore
new file mode 100644 (file)
index 0000000..cebfb10
--- /dev/null
@@ -0,0 +1,5 @@
+/html/
+/man/
+/latex/
+/doxygen.conf.temp
+/olsrd_pud.pdf
diff --git a/lib/pud/doc/Makefile b/lib/pud/doc/Makefile
new file mode 100644 (file)
index 0000000..054abcc
--- /dev/null
@@ -0,0 +1,34 @@
+include ../Makefile.inc
+
+HTML_DIR = html
+MAN_DIR = man
+LATEX_DIR = latex
+PDF_NAME = $(PLUGIN_NAME).pdf
+DOX_FILE = doxygen.conf
+TMP_DOX_FILE = $(DOX_FILE).temp
+
+.PHONY: .gitignore all clean
+
+.gitignore:
+       @echo "[$@] doc"
+       @echo "/$(HTML_DIR)/" > $@
+       @echo "/$(MAN_DIR)/" >> $@
+       @echo "/$(LATEX_DIR)/" >> $@
+       @echo "/$(TMP_DOX_FILE)" >> $@
+       @echo "/$(PDF_NAME)" >> $@
+
+clean:
+       @echo "[$@] doc"
+       @rm -fr "$(HTML_DIR)" "$(LATEX_DIR)" "$(MAN_DIR)" "$(PDF_NAME)"
+
+all: clean $(DOX_FILE)
+       @echo "[$@] doc"
+       @sed \
+         -e "s/__PLUGIN_VER__/$(PLUGIN_VER)/" \
+         -e "s/__LIBNAME__/$(PLUGIN_NAME)/" \
+         "$(DOX_FILE)" > "$(TMP_DOX_FILE)"
+       @doxygen "$(TMP_DOX_FILE)"
+       @rm "$(TMP_DOX_FILE)"
+       @$(MAKE) -s -C $(LATEX_DIR) all > /dev/null 2>&1
+       @mv "$(LATEX_DIR)/refman.pdf" "$(PDF_NAME)"
+       @rm -fr "$(LATEX_DIR)"
diff --git a/lib/pud/doc/doxygen.conf b/lib/pud/doc/doxygen.conf
new file mode 100644 (file)
index 0000000..f3ecc07
--- /dev/null
@@ -0,0 +1,1636 @@
+# Doxyfile 1.7.1
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+#       TAG = value [value, ...]
+# For lists items can also be appended using:
+#       TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING      = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME           = "__LIBNAME__"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER         = "__PLUGIN_VER__"
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       =
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS         = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE        = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF       =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES        = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH        =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH    =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF      = YES
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF           = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS           = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES  = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE               = 2
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES                =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C  = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN   = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL   = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this
+# tag. The format is ext=language, where ext is a file extension, and language
+# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C,
+# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make
+# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
+# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions
+# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
+
+EXTENSION_MAPPING      =
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT    = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT        = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT            = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen to replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT   = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING            = YES
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT   = NO
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penality.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will rougly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols
+
+SYMBOL_CACHE_SIZE      = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL            = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE        = YES
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC         = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS  = YES
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespace are hidden.
+
+EXTRACT_ANON_NSPACES   = YES
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES       = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES   = NO
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS       = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS        = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
+# will sort the (brief and detailed) documentation of class members so that
+# constructors and destructors are listed first. If set to NO (the default)
+# the constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
+# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
+# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES       = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME     = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS       =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES        = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES       = NO
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES             = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES        = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER    =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. The create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option.
+# You can optionally specify a file name after the option, if omitted
+# DoxygenLayout.xml will be used as the name of the layout file.
+
+LAYOUT_FILE            =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET                  = YES
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS               = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR      = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC       = YES
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE           =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT                  = ../src
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING         = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
+
+FILE_PATTERNS          = *.c *.h
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE              = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE                =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix filesystem feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS       =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS        =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH           =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS       =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH             =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER           =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
+# is applied to all files.
+
+FILTER_PATTERNS        =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES    = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER         = YES
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS    = NO
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION    = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.
+# Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS              = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS       = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX     = YES
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX    = 2
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX          =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER            =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER            =
+
+# If the HTML_TIMESTAMP tag is set to YES then the generated HTML
+# documentation will contain the timesstamp.
+
+HTML_TIMESTAMP         = NO
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET        =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
+# Doxygen will adjust the colors in the stylesheet and background images
+# according to this color. Hue is specified as an angle on a colorwheel,
+# see http://en.wikipedia.org/wiki/Hue for more information.
+# For instance the value 0 represents red, 60 is yellow, 120 is green,
+# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
+# The allowed range is 0 to 359.
+
+HTML_COLORSTYLE_HUE    = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
+# the colors in the HTML output. For a value of 0 the output will use
+# grayscales only. A value of 255 will produce the most vivid colors.
+
+HTML_COLORSTYLE_SAT    = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
+# the luminance component of the colors in the HTML output. Values below
+# 100 gradually make the output lighter, whereas values above 100 make
+# the output darker. The value divided by 100 is the actual gamma applied,
+# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
+# and 100 does not change the gamma.
+
+HTML_COLORSTYLE_GAMMA  = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
+# this to NO can help when comparing the output of multiple runs.
+
+HTML_TIMESTAMP         = YES
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS     = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS  = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+
+GENERATE_DOCSET        = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+
+# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+
+DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
+
+# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
+
+DOCSET_PUBLISHER_NAME  = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP      = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE               = __LIBNAME__.chm
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION           =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI           = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING     =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND             = YES
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
+# that can be used as input for Qt's qhelpgenerator to generate a
+# Qt Compressed Help (.qch) of the generated HTML documentation.
+
+GENERATE_QHP           = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE               =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE          = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER     = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
+# add. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME   =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
+# Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS  =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
+# Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS  =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION           =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+#  will be generated, which together with the HTML files, form an Eclipse help
+# plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
+# the help appears.
+
+GENERATE_ECLIPSEHELP   = NO
+
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
+# this name.
+
+ECLIPSE_DOC_ID         = org.doxygen.Project
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX          = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW      = YES
+
+# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list.
+
+USE_INLINE_TREES       = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH         = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
+# links to external symbols imported via tag files in a separate window.
+
+EXT_LINKS_IN_WINDOW    = NO
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE       = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are
+# not supported properly for IE 6.0, but are supported on all modern browsers.
+# Note that when changing this option you need to delete any form_*.png files
+# in the HTML output before the changes have effect.
+
+FORMULA_TRANSPARENT    = YES
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using
+# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
+# (GENERATE_DOCSET) there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+
+SEARCHENGINE           = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a PHP enabled web server instead of at the web client
+# using Javascript. Doxygen will generate the search PHP script and index
+# file to put on the web server. The advantage of the server
+# based approach is that it scales better to large projects and allows
+# full text search. The disadvances is that it is more difficult to setup
+# and does not have live searching capabilities.
+
+SERVER_BASED_SEARCH    = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX         = YES
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
+# Makefile that is written to the output directory.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE             = a4
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES         =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER           =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS         = YES
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX           = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE        = YES
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES     = NO
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include
+# source code with syntax highlighting in the LaTeX output.
+# Note that which sources are shown also depends on other settings
+# such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE      = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS         = YES
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE    =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE    =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN           = YES
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION          = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS              = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT             = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA             =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD                =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING     = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION        = YES
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF     = YES
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH           =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS  =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED             =
+PREDEFINED            += "__attribute__(x)="
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED      =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line, have an all uppercase name, and do not end with a semicolon. Such
+# function macros are typically used for boiler-plate code, and will confuse
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+#
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES               =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE       =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS        = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH              = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option is superseded by the HAVE_DOT option below. This is only a
+# fallback. It is recommended to install and use dot, since it yields more
+# powerful graphs.
+
+CLASS_DIAGRAMS         = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH            =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT               = YES
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
+# allowed to run in parallel. When set to 0 (the default) doxygen will
+# base this on the number of processors available in the system. You can set it
+# explicitly to a value larger than 0 to get control over the balance
+# between CPU load and processing speed.
+
+DOT_NUM_THREADS        = 0
+
+# By default doxygen will write a font called FreeSans.ttf to the output
+# directory and reference it in all dot files that doxygen generates. This
+# font does not include all possible unicode characters however, so when you need
+# these (or just want a differently looking font) you can specify the font name
+# using DOT_FONTNAME. You need need to make sure dot is able to find the font,
+# which can be done by putting it in a standard location or by setting the
+# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
+# containing the font.
+
+DOT_FONTNAME           = FreeSans.ttf
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE           = 10
+
+# By default doxygen will tell dot to use the output directory to look for the
+# FreeSans.ttf font (which doxygen will put there itself). If you specify a
+# different font using DOT_FONTNAME you can set the path where dot
+# can find it using this tag.
+
+DOT_FONTPATH           =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH    = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS           = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK               = YES
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS     = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH          = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH             = YES
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH           = YES
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH        = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT       = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH               =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS           =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES    = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT        = YES
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS      = YES
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP            = YES
diff --git a/lib/pud/doc/olsrd.conf.default.pud b/lib/pud/doc/olsrd.conf.default.pud
new file mode 100644 (file)
index 0000000..ec9e44d
--- /dev/null
@@ -0,0 +1,789 @@
+#
+# OLSR.org routing daemon config file
+# This file contains ALL available options and explanations about them
+#
+# Lines starting with a # are discarded
+#
+
+#### ATTENTION for IPv6 users ####
+# Because of limitations in the parser IPv6 addresses must NOT
+# begin with a ":", so please add a "0" as a prefix.
+
+###########################
+### Basic configuration ###
+###########################
+# keep this settings at the beginning of your first configuration file
+
+# Debug level (0-9)
+# If set to 0 the daemon runs in the background, unless "NoFork" is set to true
+# (Default is 1)
+
+DebugLevel  0
+
+# IP version to use (4 or 6)
+# (Default is 4)
+
+# IpVersion 4
+
+#################################
+### OLSRd agent configuration ###
+#################################
+# this parameters control the settings of the routing agent which are not
+# related to the OLSR protocol and it's extensions
+
+# Clear the screen each time the internal state changes
+# (Default is yes)
+
+# ClearScreen     yes
+
+# Should olsrd keep on running even if there are
+# no interfaces available? This is a good idea
+# for a PCMCIA/USB hotswap environment.
+# (Default is yes)
+
+# AllowNoInt  yes
+
+# LockFile
+# The lockfile is used to prevent multiple OLSR instances running at the same
+# time.
+# (Linux/BSD default is "/var/run/olsrd-ipv(4/6).lock")
+# (Win32 default is "<configfile>-ipv(4/6).lock")
+
+LockFile "/tmp/olsrd.lock"
+
+# Polling rate for OLSR sockets in seconds (float). 
+# (Default is 0.05)
+
+# Pollrate  0.05
+
+# Interval to poll network interfaces for configuration changes (in seconds).
+# Linux systems can detect interface statechange via netlink sockets.
+# (Defaults is 2.5)
+
+# NicChgsPollInt  2.5
+
+# TOS(type of service) value for the IP header of control traffic.
+# (Default is 16)
+
+# TosValue 16
+
+# FIBMetric controls the metric value of the host-routes OLSRd sets.
+# - "flat" means that the metric value is always 2. This is the preferred value
+#   because it helps the linux kernel routing to clean up older routes
+# - "correct" use the hopcount as the metric value.
+# - "approx" use the hopcount as the metric value too, but does only update the
+#   hopcount if the nexthop changes too
+# (Default is "flat")
+
+# FIBMetric "flat"
+
+#######################################
+### Linux specific OLSRd extensions ###
+#######################################
+# these parameters are only working on linux at the moment, but might become
+# useful on BSD in the future
+
+# SrcIpRoutes tells OLSRd to set the Src flag of host routes to the originator-ip
+# of the node. In addition to this an additional localhost device is created
+# to make sure the returning traffic can be received.
+# (Default is "no")
+
+# SrcIpRoutes no
+
+# Specify the proto tag to be used for routes olsr inserts into kernel
+# currently only implemented for linux
+# valid values under linux are 1 .. 254
+# 1 gets remapped by olsrd to 0 UNSPECIFIED (1 is reserved for ICMP redirects)
+# 2 KERNEL routes (not very wise to use)
+# 3 BOOT (should in fact not be used by routing daemons)
+# 4 STATIC 
+# 8 .. 15 various routing daemons (gated, zebra, bird, & co)
+# (defaults to 0 which gets replaced by an OS-specific default value
+# under linux 3 (BOOT) (for backward compatibility)
+
+# RtProto 0
+
+# Specifies the routing Table olsr uses
+# RtTable is for host routes, RtTableDefault for the route to the default
+# internet gateway (2 in case of IPv6+NIIT) and RtTableTunnel is for
+# routes to the ipip tunnels, valid values are 1 to 254
+# There is a special parameter "auto" (choose default below)
+# (with smartgw: default is 254/223/224)
+# (without smartgw: default is 254/254/254, linux main table)
+
+# RtTable auto
+# RtTableDefault auto
+# RtTableTunnel auto
+
+# Specifies the policy rule priorities for the three routing tables and
+# a special rule for smartgateway routing (see README-Olsr-Extensions)
+# Priorities can only be set if three different routing tables are set.
+# if set the values must obey to condition
+# RtTablePriority < RtTableDefaultOlsrPriority
+# < RtTableTunnelPriority < RtTableDefaultPriority.
+# There are two special parameters, "auto" (choose fitting to SmartGW
+# mode) and "none" (do not set policy rule)
+# (with smartgw: default is none/32776/32776/32796)
+# (without smartgw: default is none/none/none/none)
+
+# RtTablePriority auto
+# RtTableDefaultOlsrPriority auto
+# RtTableTunnelPriority auto
+# RtTableDefaultPriority auto
+
+# Activates (in IPv6 mode) the automatic use of NIIT
+# (see README-Olsr-Extensions)
+# (default is "yes")
+
+# UseNiit yes
+
+# Activates the smartgateway ipip tunnel feature.
+# See README-Olsr-Extensions for a description of smartgateways.
+# (default is "yes")
+
+# SmartGateway yes
+
+# Allows the selection of a smartgateway with NAT (only for IPv4)
+# (default is "yes")
+
+# SmartGatewayAllowNAT yes
+
+# Defines what kind of Uplink this node will publish as a
+# smartgateway. The existence of the uplink is detected by
+# a route to 0.0.0.0/0, ::ffff:0:0/96 and/or 2000::/3.
+# possible values are "none", "ipv4", "ipv6", "both"
+# (default is "both")
+
+# SmartGatewayUplink "both"
+
+# Specifies if the local ipv4 uplink use NAT
+# (default is "yes")
+
+# SmartGatewayUplinkNAT yes
+
+# Specifies the speed of the uplink in kilobit/s.
+# First parameter is upstream, second parameter is downstream
+# (default is 128/1024)
+
+# SmartGatewaySpeed 128 1024
+
+# Specifies the EXTERNAL ipv6 prefix of the uplink. A prefix
+# length of more than 64 is not allowed.
+# (default is 0::/0
+
+# SmartGatewayPrefix 0::/0
+
+##############################
+### OLSR protocol settings ###
+##############################
+
+# For testing purposes it may be nice to use another port for olsrd
+# for using another port than the IANA assigned one 
+# for a production network, there should be a good reason!!
+# valid values are integers >1, please be careful with using reserved
+# port numbers
+# (default is 698, the IANA assigned olsr-port)
+
+# OlsrPort 698
+
+# Sets the main IP (originator ip) of the router. This IP will NEVER
+# change during the uptime of olsrd.
+# (default is 0.0.0.0, which triggers usage of the IP of the first interface)
+
+# MainIp 10.0.0.1
+
+# The fixed willingness to use (0-7)
+# If not set willingness will be calculated
+# dynamically based on battery/power status
+# (default is 3)
+
+# Willingness     3
+
+# HNA (Host network association) allows the OLSR to announce
+# additional IPs or IP subnets to the net that are reachable
+# through this node.
+# Syntax for HNA4 is "network-address    network-mask"
+# Syntax for HNA6 is "network-address    prefix-length"
+# (default is no HNA)
+Hna4
+{
+# Internet gateway
+# 0.0.0.0   0.0.0.0
+# specific small networks reachable through this node
+# 15.15.0.0 255.255.255.0
+}
+Hna6
+{
+# Internet gateway
+#   0::                     0
+# specific small networks reachable through this node
+#   fec0:2200:106:0:0:0:0:0 48
+}
+
+
+# Hysteresis for link sensing (only for hopcount metric)
+# Hysteresis adds more robustness to the link sensing
+# but delays neighbor registration.
+# (defaults to yes)
+
+# UseHysteresis yes
+
+# Hysteresis parameters (only for hopcount metric)
+# Do not alter these unless you know what you are doing!
+# Set to auto by default. Allowed values are floating point
+# values in the interval 0,1
+# THR_LOW must always be lower than THR_HIGH!!
+# (default is 0.5/0.8/0.3)
+
+# HystScaling  0.50
+# HystThrHigh  0.80
+# HystThrLow 0.30
+
+# TC redundancy
+# Specifies how much neighbor info should be sent in
+# TC messages. Because of a design problem in the 0.5.x
+# dijkstra implementation this value must be set to 2.
+# 2 - send all neighbors
+# (default is 2)
+
+# TcRedundancy  2
+
+# MPR coverage specifies how many MPRs a node should
+# try select to reach every 2 hop neighbor. Because of
+# a design problem in the 0.5.x lq mpr selection algorithm
+# this value should be set to 7.
+# (default is 7)
+
+# MprCoverage 7
+
+################################
+### OLSR protocol extensions ###
+################################
+
+# Link quality level switch between hopcount and 
+# cost-based (mostly ETX) routing. Because of
+# a design problem in the 0.5.x dijkstra algorithm this
+# value should not be set to 1.
+# 0 = do not use link quality
+# 2 = use link quality for MPR selection and routing
+# (default is 2)
+
+# LinkQualityLevel 2
+
+# Link quality algorithm (only for lq level 2)
+# (see README-Olsr-Extensions)
+# - "etx_float", a floating point  ETX with exponential aging
+# - "etx_fpm", same as ext_float, but with integer arithmetic
+# - "etx_ff" (ETX freifunk), an etx variant which use all OLSR
+#   traffic (instead of only hellos) for ETX calculation
+# - "etx_ffeth", an incompatible variant of etx_ff that allows
+#   ethernet links with ETX 0.1.
+# (defaults to "etx_ff")
+
+# LinkQualityAlgorithm    "etx_ff"
+
+# Link quality aging factor (only for lq level 2)
+# Tuning parameter for etx_float and etx_fpm, smaller values
+# mean slower changes of ETX value. (allowed values are
+# between 0.01 and 1.0)
+# (default is 0.05)
+
+# LinkQualityAging 0.05
+
+# Fisheye mechanism for TCs (0 meansoff, 1 means on)
+# (default is 1)
+
+# LinkQualityFishEye  1
+
+#
+# NatThreshold 
+#
+# (currently this is only in the freifunk firmware)
+# If the NAT-Endpoint (the preferred 0/0 HNA emitting node)
+# is to be changed, the ETX value of the current 0/0 is 
+# multiplied with the NatThreshold value before being
+# compared to the new one.
+# The parameter can be a value between 0.1 and 1.0, but
+# should be close to 1.0 if changed.
+# WARNING: This parameter should not be used together with
+# the etx_ffeth metric !!
+# (defaults to 1.0)
+
+# NatThreshold  1.0
+
+#############################################################
+### Configuration of the IPC to the windows GUI interface ###
+#############################################################
+
+IpcConnect
+{
+     # Determines how many simultaneously
+     # IPC connections that will be allowed
+     # Setting this to 0 disables IPC
+
+     # MaxConnections  0
+
+     # By default only 127.0.0.1 is allowed
+     # to connect. Here allowed hosts and networks can
+     # be added
+
+     # Host            127.0.0.1
+     # Host            10.0.0.5
+     # Net             192.168.1.0 255.255.255.0     
+}
+
+#####################################
+### Example plugin configurations ###
+#####################################
+# Olsrd plugins to load
+# This must be the absolute path to the file
+# or the loader will use the following scheme:
+# - Try the paths in the LD_LIBRARY_PATH 
+#   environment variable.
+# - The list of libraries cached in /etc/ld.so.cache
+# - /lib, followed by /usr/lib
+#
+# the examples in this list are for linux, so check if the plugin is
+# available if you use windows/BSD.
+# each plugin should have a README file in it's lib subfolder
+
+# LoadPlugin "olsrd_httpinfo.dll"
+LoadPlugin "./lib/httpinfo/olsrd_httpinfo.so.0.1"
+{
+    # port number the httpinfo plugin will be listening, default 1978
+#   PlParam     "port"   "8000"
+    
+    # ip address that can access the plugin, use "0.0.0.0"
+    # to allow everyone
+    PlParam     "Host"   "127.0.0.1"
+#   PlParam     "Host"   "80.23.53.22"
+    
+    # networks that can access the plugin (ip/netmask)
+    # careful with 0.0.0.0/0, makes (ddos) attacks poss.
+    PlParam     "Net"    "127.0.0.1 255.0.0.0"
+#    PlParam     "Net"    "0.0.0.0 0.0.0.0"
+#    PlParam     "Net"    "104.0.0.0 255.255.0.0"
+#    PlParam     "Net"    "192.168.0.0 255.255.0.0"
+}
+
+# LoadPlugin "olsrd_txtinfo.dll"
+LoadPlugin "./lib/txtinfo/olsrd_txtinfo.so.0.1"
+{
+    # port number the txtinfo plugin will be listening, default 2006
+#   PlParam     "port"   "81"
+    # ip address that can access the plugin, use "0.0.0.0"
+    # to allow everyone
+    PlParam     "Accept"   "127.0.0.1"
+}
+
+#LoadPlugin "olsrd_secure.so.0.3"
+#{
+    # define source of keyfile for secure plugin
+    # see lib/secure/README_SECURE for more information
+    
+#   PlParam     "Keyfile"   "/etc/olsr-keyfile.txt"
+#}
+
+#LoadPlugin "olsrd_dyn_gw.so.0.5"
+#{
+    # The plugin check interval can be set here in milliseconds.
+    # The default is 1000 ms (1 second).
+#    PlParam     "CheckInterval"  "5000"
+    
+    # The ping check interval in case there is any pinged host specified.
+    # The default is 5 seconds.
+#    PlParam     "PingInterval"   "40"
+    
+    # If one or more IPv4 addresses are given, do a ping on these in
+    # descending order to validate that there is not only an entry in
+    # routing table, but also a real network connection. If any of
+    # these addresses could be pinged successfully, the test was
+    # succesful, i.e. if the ping on the 1st address was successful,the
+    # 2nd won't be pinged.
+    #
+    # The Ping list applies to the group of HNAs specified above or to the 
+    # default internet gateway when no HNA is specified.
+    #
+    # Running the plugin without parameters acts as the 'old' dyn_gw_plain.
+    
+    #   The following ping entries for the internet gateway
+#    PlParam "Ping"   "141.1.1.1"
+#    PlParam "Ping"   "194.25.2.129"
+    
+    #   First group of HNAs with related ping host
+#    PlParam    "HNA"    "192.168.80.0 255.255.255.0"
+#    PlParam    "HNA"    "192.168.81.0 255.255.255.0"
+#    PlParam    "Ping"   "192.168.81.12"
+    
+    #   Second HNA group with multiple related ping hosts.
+    #   Specifying multiple ping hosts provides redundancy.
+#    PlParam "HNA"    "192.168.100.0 255.255.255.0"
+#    PlParam "HNA"    "192.168.101.0 255.255.255.0"
+#    PlParam "HNA"    "192.168.102.0 255.255.255.0"
+#    PlParam "Ping"   "192.168.100.10"
+#    PlParam "Ping"   "192.168.101.10"
+    
+    #   Third HNA group without ping check
+#    PlParam "HNA"    "192.168.200.0 255.255.255.0"
+#    PlParam "HNA"    "192.168.201.0 255.255.255.0"
+#    PlParam "HNA"    "192.168.202.0 255.255.255.0"
+#}
+
+#LoadPlugin "olsrd_dyn_gw_plain.so.0.4"
+#{
+#}
+
+LoadPlugin "./lib/pud/olsrd_pud.so.1.0.0"
+{
+    # nodeIdType is used to indicate the type of the nodeId field and is a
+    #            number in the range 0-255, with the following meaning:
+    #
+    #        0 : MAC address of sending interface
+    #            (nodeId is not relevant)
+    #        1 : an MSISDN number with 15 digits
+    #        2 : a Tetra number with 17 digits
+    #        3 : a DNS name
+    #        4 : IPv4 address (OLSR main address) of the sending node
+    #            (nodeId is not relevant)
+    #        6 : IPv6 address (OLSR main address) of the sending node
+    #            (nodeId is not relevant)
+    #      192 : a 7 digit number conforming to 'Nationaal Nummerplan
+    #            Brandweer Nederland'
+    #      193 : a 6 digit number conforming to 'Nationaal Nummerplan
+    #            Ambulancezorg Nederland'
+    #      194 : a 4 digit number in the range [1, 8191]
+    #
+    #      other numbers are reserved 
+    #
+    # Note: setting 4 or 6 is the same for olsrd: both mean 'use the main
+    #       IP address, which is either an IPv4 or an IPv6 address, depending
+    #       on the IP version under which olsrd is run.
+    #
+    # Default: 4
+    #
+    #PlParam     "nodeIdType"                   "4"
+
+    # nodeId is the node identification with the meaning as indicated by the
+    #        nodeIdType parameter. When not set AND the nodeIdType is 3 (DNS
+    #        name) then the hostname is used.
+    #
+    # Note: Both the nodeIdType and nodeId fields are transported over
+    #       OLSR, so care must be taken to keep the size of the nodeId
+    #       parameter down.
+    #
+    # Default: none
+    #
+    #PlParam     "nodeId"                       "host.example.com"
+
+
+    #
+    # RX Parameters
+    #
+
+    # rxNonOlsrIf is a network interface on the host on which the plugin will
+    #             listen for GPS multicasts. Multiple such interfaces can be
+    #             specified by specifying the parameter multiple times.
+    #
+    # Default: none
+    #
+    PlParam     "rxNonOlsrIf"                  "lo"
+
+    # rxAllowedSourceIpAddress is an IP address from which the plugin is
+    #                          allowed to process/parse GPS sentences. When
+    #                          this parameter is not configured then GPS
+    #                          sentences from ALL IP addresses are processed.
+    #                          Multiple IP addresses can be specified by
+    #                          specifying the parameter multiple times.
+    #
+    # Default: none
+    #
+    PlParam     "rxAllowedSourceIpAddress"     "127.0.0.1"
+
+    # rxMcAddr is the multicast address on which the plugin will listen for GPS
+    #          multicasts.
+    #
+    # Default: 224.0.0.224 (IPv4) or FF02:0:0:0:0:0:0:1 (IPv6)
+    #
+    #PlParam     "rxMcAddr"                     "224.0.0.224"
+
+    # rxMcPort is the UDP port on which the plugin will listen for GPS
+    #          multicasts.
+    #
+    # Default: 2240
+    #
+    #PlParam     "rxMcPort"                     "2240"
+
+
+    #
+    # TX Parameters
+    #
+
+    # txNonOlsrIf is a network interface on the host on which the plugin will
+    #             transmit GPS multicasts that were received through the OLSR
+    #             network. Multiple such interfaces can be specified by
+    #             specifying the parameter multiple times.
+    #
+    # Default: none
+    #
+    PlParam     "txNonOlsrIf"                  "lo"
+
+    # txMcAddr is the multicast address on which the plugin will transmit GPS
+    #          multicasts that were received through the OLSR network.
+    #
+    # Default: 224.0.0.224 (IPv4) or FF02:0:0:0:0:0:0:1 (IPv6)
+    #
+    #PlParam     "txMcAddr"                     "224.0.0.224"
+
+    # txMcPort is the UDP port on which the plugin will transmit GPS multicasts
+    #          that were received through the OLSR network.
+    #
+    # Default: 2240
+    #
+    #PlParam     "txMcPort"                     "2240"
+
+    # txTtl is the TTL that is used when transmitting GPS multicasts that were
+    #       received through the OLSR network
+    #
+    # Default: 1
+    #
+    #PlParam     "txTtl"                        "1"
+
+    # txNmeaMessagePrefix is the NMEA message prefix of GPS multicasts that the
+    #                     plugin transmits. It must be exactly 4 characters
+    #                     long.
+    #
+    # Default: NBSX
+    #
+    #PlParam     "txNmeaMessagePrefix"          "NBSX"
+
+
+    #
+    # OLSR Parameters
+    #
+
+    # olsrTtl is the TTL that is used when sending messages over the OLSR
+    #         networks
+    #
+    # Default: 64
+    #
+    #PlParam     "olsrTtl"                      "64"
+
+
+    #
+    # Update Parameters
+    #
+
+    # updateIntervalStationary is the interval (in seconds) between position
+    #                          updates sent over the OLSR network when the
+    #                          node is stationary
+    #
+    # Default: 60
+    #
+    #PlParam     "updateIntervalStationary"     "60"
+
+    # updateIntervalMoving is the interval (in seconds) between position
+    #                      updates sent over the OLSR network when the
+    #                      node is moving
+    #
+    # Default: 5
+    #
+    #PlParam     "updateIntervalMoving"         "5"
+
+    # movingSpeedThreshold is the speed from which we consider the node is
+    #                      moving
+    #
+    # Default: 5
+    #
+    #PlParam     "movingSpeedThreshold"         "5"
+
+    # movingDistanceThreshold is the distance from the previous position from
+    #                         which we consider the node is moving
+    #
+    # Default: 50
+    #
+    #PlParam     "movingDistanceThreshold"      "50"
+
+    # dopMultiplier One of the situations that is seen as movement is when the
+    #               current position with its uncertainty circle no longer
+    #               overlaps the last transmitted position with its uncertainty
+    #               circle. This parameter is used to adjust the sizes of these
+    #               uncertainty circles: setting it to a value less than 1.0
+    #               will make both uncertainty circles smaller by this factor,
+    #               resulting in earlier movement detection. Setting it to a
+    #               value larger than 1.0 will detect movement later.
+    #
+    # Default: 1.0
+    #
+    #PlParam     "dopMultiplier"                "1.0"
+
+    # defaultHdop is the default value that is taken for HDOP (in meters) in
+    #             determining whether we are moving when there is a position
+    #             available but no HDOP.
+    #
+    # Default: 50
+    #
+    #PlParam     "defaultHdop"                  "50"
+
+    # defaultVdop is the default value that is taken for VDOP (in meters) in
+    #             determining whether we are moving when there is a position
+    #             available but no VDOP.
+    #
+    # Default: 50
+    #
+    #PlParam     "defaultVdop"                  "50"
+
+    # averageDepth is the depth of the position average list, or the number
+    #              of positions that are averaged to obtain the average
+    #              position
+    #
+    # Default: 5
+    #
+    #PlParam     "averageDepth"                 "5"
+
+    # hysteresisCountToStationary is the number of position updates that
+    #                             effectuate a state transition from moving to
+    #                             stationary that must be received before the
+    #                             actual transition is taken
+    #
+    # Default: 17
+    #
+    #PlParam     "hysteresisCountToStationary"  "17"
+
+    # hysteresisCountToMoving is the number of position updates that effectuate
+    #                         a state transition from stationary to moving that
+    #                         must be received before the actual transition is
+    #                         taken
+    #
+    # Default: 5
+    #
+    #PlParam     "hysteresisCountToMoving"      "5"
+
+
+    #
+    # Other Plugin Parameters
+    #
+
+    # useDeDup determines whether duplicate message detection is to be
+    #          performed. When 0 then no such detection is performed, when 1
+    #          then the detection is performed 
+    #
+    # Default: 1
+    #
+    #PlParam     "useDeDup"                     "1"
+
+    # deDupDepth the number of messages that are tracked to detect duplucates
+    #            messages received from the OLSR network
+    #
+    # Default: 56
+    #
+    #PlParam     "deDupDepth"                   "256"
+
+    # useLoopback determines whether the message that is sent over the OLSR
+    #             network should be immediately looped back, thus pretending
+    #             that the message (that is sent by this node) is received from
+    #             the OLSR network. When 0 then no loopback is performed, when
+    #             1 then the loopback is performed
+    #
+    # Default: 0
+    #
+    PlParam     "useLoopback"                  "1"
+}
+
+#############################################
+### OLSRD default interface configuration ###
+#############################################
+# the default interface section can have the same values as the following
+# interface configuration. It will allow you so set common options for all
+# interfaces.
+
+InterfaceDefaults {
+    # Ip4Broadcast      255.255.255.255
+}
+
+######################################
+### OLSRd Interfaces configuration ###
+######################################
+# multiple interfaces can be specified for a single configuration block
+# multiple configuration blocks can be specified
+
+# WARNING, don't forget to insert your interface names here !
+Interface "br0"
+{
+    # Interface Mode is used to prevent unnecessary
+    # packet forwarding on switched ethernet interfaces
+    # valid Modes are "mesh" and "ether"
+    # (default is "mesh")
+
+    # Mode "mesh"
+
+    # IPv4 broadcast address for outgoing OLSR packets.
+    # One usefull example would be 255.255.255.255
+    # The second useful value would be to
+    # specify the peer adress of an ptp-tunnel.
+    # another name of this parameter is "IPv4Multicast"
+    # (default is 0.0.0.0, which triggers the usage of the
+    # interface broadcast IP)
+    
+    # Ip4Broadcast      0.0.0.0
+
+    # IPv6 multicast address
+    # (default is FF02::6D, the manet-router linklocal multicast)
+
+    # IPv6Multicast FF02::6D
+
+    # IPv4 src address for outgoing OLSR packages
+    # (default is 0.0.0.0, which triggers usage of the interface IP)
+
+    # IPv4Src 0.0.0.0
+
+    # IPv6 src prefix. OLSRd will choose one of the interface IPs
+    # which matches the prefix of this parameter.
+    # (default is 0::/0, which triggers the usage
+    # of a not-linklocal interface IP)
+
+    # IPv6Src 0::/0
+    
+    # Emission intervals in seconds.
+    # If not defined, Freifunk network defaults are used
+    # (default is 2.0/20.0 for Hello and 5.0/300.0 for Tc/Mid/Hna)
+
+    # HelloInterval       2.0
+    # HelloValidityTime  20.0
+    # TcInterval          5.0
+    # TcValidityTime    300.0
+    # MidInterval         5.0
+    # MidValidityTime   300.0
+    # HnaInterval         5.0
+    # HnaValidityTime   300.0
+    
+    # When multiple links exist between hosts
+    # the weight of interface is used to determine
+    # the link to use. Normally the weight is
+    # automatically calculated by olsrd based
+    # on the characteristics of the interface,
+    # but here you can specify a fixed value.
+    # Olsrd will choose links with the lowest value.
+    # Note:
+    # Interface weight is used only when LinkQualityLevel is set to 0.
+    # For any other value of LinkQualityLevel, the interface ETX
+    # value is used instead.
+    # Weight 0
+
+    # If a certain route should be preferred 
+    # or ignored by the mesh, the Link Quality 
+    # value of a node can be multiplied with a factor 
+    # entered here. In the example the route 
+    # using 192.168.0.1 would rather be ignored.
+    # A multiplier of 0.5 will result in a small
+    # (bad) LinkQuality value and a high (bad)
+    # ETX value.
+    # Note:
+    # Link quality multiplier is used only when
+    # LinkQualityLevel is > 0.
+
+    # example 1: reduce LQ to 192.168.0.1 by half
+    # LinkQualityMult 192.168.0.1 0.5
+
+    # example 2: reduce LQ to all nodes on this interface by 20%
+    # LinkQualityMult default 0.8
+}
diff --git a/lib/pud/doc/pud.odt b/lib/pud/doc/pud.odt
new file mode 100755 (executable)
index 0000000..782ca80
Binary files /dev/null and b/lib/pud/doc/pud.odt differ
diff --git a/lib/pud/scripts/makeVersionH b/lib/pud/scripts/makeVersionH
new file mode 100755 (executable)
index 0000000..70ee4ef
--- /dev/null
@@ -0,0 +1,30 @@
+#!/bin/bash
+
+set -e
+set -u
+
+outfile="${1}"
+version="${2}"
+
+set +e
+sha="$(git describe 2> /dev/null)"
+set -e
+
+
+cat > "${outfile}" << EOF
+#ifndef _PUD_VERSION_H_
+#define _PUD_VERSION_H_
+
+#define PLUGIN_VER "${version}"
+EOF
+
+if [[ -n "${sha:-}" ]]; then
+  cat >> "${outfile}" << EOF
+#define GIT_SHA    "${sha}"
+EOF
+fi
+
+cat >> "${outfile}" << EOF
+
+#endif /* _PUD_VERSION_H_ */
+EOF
diff --git a/lib/pud/src/compiler.h b/lib/pud/src/compiler.h
new file mode 100644 (file)
index 0000000..1b426be
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef _PUD_COMPILER_H_
+#define _PUD_COMPILER_H_
+
+/** Compiler hint to expect x */
+#ifndef likely
+# if defined(__GNUC__)
+#  define likely(x)                                    __builtin_expect((x),1)
+# else
+#  define likely(x)                                            (x)
+# endif
+#endif
+
+/** Compiler hint to not expect x */
+#ifndef unlikely
+# if defined(__GNUC__)
+#  define unlikely(x)                                  __builtin_expect((x),0)
+# else
+#  define unlikely(x)                                  (x)
+# endif
+#endif
+
+#endif /* _PUD_COMPILER_H_ */
diff --git a/lib/pud/src/configuration.c b/lib/pud/src/configuration.c
new file mode 100644 (file)
index 0000000..931683d
--- /dev/null
@@ -0,0 +1,1877 @@
+#include "configuration.h"
+
+/* Plugin includes */
+#include "pud.h"
+#include "netTools.h"
+#include "networkInterfaces.h"
+
+/* OLSR includes */
+#include "olsr_types.h"
+#include "olsr_cfg.h"
+
+/* System includes */
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <nmea/util.h>
+
+/*
+ * Utility functions
+ */
+
+/**
+ Determine the address of the port in an OLSR socket address
+
+ @param ipVersion
+ The IP version (AF_INET or AF_INET6)
+ @param addr
+ A pointer to OLSR socket address
+ @return
+ A pointer to the port
+ */
+static in_port_t * getOlsrSockaddrPort(int ipVersion, union olsr_sockaddr * addr) {
+       if (ipVersion == AF_INET) {
+               return &addr->in4.sin_port;
+       } else {
+               return &addr->in6.sin6_port;
+       }
+}
+
+/**
+ Get pointer to the IP address and port in an OLSR socket address
+ @param ipVersion
+ The IP version (AF_INET or AF_INET6)
+ @param addr
+ A pointer to OLSR socket address
+ @param ipAddress
+ A pointer to the location where the pointer to the IP address will be stored
+ @param port
+ A pointer to the location where the pointer to the port will be stored
+ */
+static void getOlsrSockAddrAndPort(int ipVersion, union olsr_sockaddr * addr,
+               void ** ipAddress, in_port_t ** port) {
+       if (ipVersion == AF_INET) {
+               *ipAddress = (void *) &addr->in4.sin_addr;
+               *port = (void *) &addr->in4.sin_port;
+       } else {
+               *ipAddress = (void *) &addr->in6.sin6_addr;
+               *port = (void *) &addr->in6.sin6_port;
+       }
+}
+
+/**
+ Read an unsigned long long number from a value string
+
+ @param valueName
+ the name of the value
+ @param value
+ the string to convert to a number
+ @param valueNumber
+ a pointer to the location where to store the number upon successful conversion
+
+ @return
+ - true on success
+ - false otherwise
+ */
+static bool readULL(const char * valueName, const char * value,
+               unsigned long long * valueNumber) {
+       char * endPtr = NULL;
+       unsigned long long valueNew;
+
+       errno = 0;
+       valueNew = strtoull(value, &endPtr, 10);
+
+       if (!((endPtr != value) && (*value != '\0') && (*endPtr == '\0'))) {
+               /* invalid conversion */
+               pudError(true, "Configured %s (%s) could not be converted to a number",
+                               valueName, value);
+               return false;
+       }
+
+       *valueNumber = valueNew;
+
+       return true;
+}
+
+/**
+ Read a double number from a value string
+
+ @param valueName
+ the name of the value
+ @param value
+ the string to convert to a number
+ @param valueNumber
+ a pointer to the location where to store the number upon successful conversion
+
+ @return
+ - true on success
+ - false otherwise
+ */
+static bool readDouble(const char * valueName, const char * value,
+               double * valueNumber) {
+       char * endPtr = NULL;
+       double valueNew;
+
+       errno = 0;
+       valueNew = strtod(value, &endPtr);
+
+       if (!((endPtr != value) && (*value != '\0') && (*endPtr == '\0'))) {
+               /* invalid conversion */
+               pudError(true, "Configured %s (%s) could not be converted to a number",
+                               valueName, value);
+               return false;
+       }
+
+       *valueNumber = valueNew;
+
+       return true;
+}
+
+/*
+ * nodeIdType
+ */
+
+/** The nodeIdType */
+static NodeIdType nodeIdType = PUD_NODE_ID_TYPE_DEFAULT;
+
+/**
+ @return
+ The node ID type
+ */
+NodeIdType getNodeIdTypeNumber(void) {
+       return nodeIdType;
+}
+
+/**
+ Set the node ID type.
+
+ @param value
+ The value of the node ID type to set (a number in string representation)
+ @param data
+ Unused
+ @param addon
+ Unused
+
+ @return
+ - true when an error is detected
+ - false otherwise
+ */
+int setNodeIdType(const char *value, void *data __attribute__ ((unused)),
+               set_plugin_parameter_addon addon __attribute__ ((unused))) {
+       static const char * valueName = PUD_NODE_ID_TYPE_NAME;
+       unsigned long long nodeIdTypeNew;
+
+       assert (value != NULL);
+
+       if (!readULL(valueName, value, &nodeIdTypeNew)) {
+               return true;
+       }
+
+       if (nodeIdTypeNew > PUD_NODE_ID_TYPE_MAX) {
+               pudError(false, "Configured %s (%llu) is out of range 0-%u", valueName,
+                               nodeIdTypeNew, PUD_NODE_ID_TYPE_MAX);
+               return true;
+       }
+
+       switch (nodeIdTypeNew) {
+               case PUD_NODEIDTYPE_MAC:
+               case PUD_NODEIDTYPE_MSISDN:
+               case PUD_NODEIDTYPE_TETRA:
+               case PUD_NODEIDTYPE_DNS:
+               case PUD_NODEIDTYPE_IPV4:
+               case PUD_NODEIDTYPE_IPV6:
+               case PUD_NODEIDTYPE_192:
+               case PUD_NODEIDTYPE_193:
+               case PUD_NODEIDTYPE_194:
+                       break;
+
+               default:
+                       pudError(false, "Configured %s (%llu) is reserved", valueName,
+                                       nodeIdTypeNew);
+                       return true;
+       }
+
+       nodeIdType = nodeIdTypeNew;
+
+       return false;
+}
+
+/*
+ * nodeId
+ */
+
+/** The maximum length of a nodeId */
+#define PUD_NODEIDMAXLENGTH 255
+
+/** The nodeId buffer */
+static unsigned char nodeId[PUD_NODEIDMAXLENGTH + 1];
+
+/** The length of the string in the nodeId buffer */
+static size_t nodeIdLength = 0;
+
+/** True when the nodeId is set */
+static bool nodeIdSet = false;
+
+/** The nodeId as a nuber */
+static unsigned long long nodeIdNumber = 0;
+
+/** True when the nodeIdNumber is set */
+static bool nodeIdNumberSet = false;
+
+/**
+ @return
+ The node ID
+ */
+unsigned char * getNodeId(void) {
+       return getNodeIdWithLength(NULL);
+}
+
+/**
+ @param value
+ A pointer to the node ID number
+ @return
+ - true on success
+ - false otherwise
+ */
+bool getNodeIdAsNumber(unsigned long long * value) {
+       if (!nodeIdNumberSet) {
+               if (!readULL(PUD_NODE_ID_NAME, (char *) &nodeId[0], &nodeIdNumber)) {
+                       return false;
+               }
+               nodeIdNumberSet = true;
+       }
+       *value = nodeIdNumber;
+       return true;
+}
+
+/**
+ Get the nodeId and its length
+
+ @param length
+ a pointer to the variable in which to store the nodeId length (allowed to be
+ NULL, in which case the length is not stored)
+
+ @return
+ The node ID
+ */
+unsigned char * getNodeIdWithLength(size_t *length) {
+       if (!nodeIdSet) {
+               setNodeId("", NULL, (set_plugin_parameter_addon) {.pc = NULL});
+       }
+
+       if (length != NULL) {
+               *length = nodeIdLength;
+       }
+
+       return &nodeId[0];
+}
+
+/**
+ Set the node ID.
+
+ @param value
+ The value of the node ID to set (in string representation)
+ @param data
+ Unused
+ @param addon
+ Unused
+
+ @return
+ - true when an error is detected
+ - false otherwise
+ */
+int setNodeId(const char *value, void *data __attribute__ ((unused)), set_plugin_parameter_addon addon __attribute__ ((unused))) {
+       static const char * valueName = PUD_NODE_ID_NAME;
+       size_t valueLength;
+
+       assert (value != NULL);
+
+       valueLength = strlen(value);
+       if (valueLength > PUD_NODEIDMAXLENGTH) {
+               pudError(false, "Configured %s is too long, maximum length is"
+                       " %u, current length is %lu", valueName, PUD_NODEIDMAXLENGTH,
+                               (unsigned long) valueLength);
+               return true;
+       }
+
+       strcpy((char *) &nodeId[0], value);
+       nodeIdLength = valueLength;
+       nodeIdSet = true;
+
+       return false;
+}
+
+/*
+ * nodeId Cache
+ */
+
+/** The size of the cached nodeId buffer */
+#define PUD_CACHED_NODEID_BUFFER_SIZE 16
+
+/** The cached nodeId buffer: contains a pre-processed version of the nodeId
+ in order to improve performance. It is currently used for nodeIdTypes
+ PUD_NODEIDTYPE_MSISDN, PUD_NODEIDTYPE_TETRA, PUD_NODEIDTYPE_192,
+ PUD_NODEIDTYPE_193 (so basically for numbers that will not change) */
+static unsigned char cachedNodeIdBuffer[PUD_CACHED_NODEID_BUFFER_SIZE];
+
+/** The number of bytes stored in cachedNodeIdBuffer */
+static unsigned int cachedNodeIdBufferLength = 0;
+
+/**
+ @param buffer
+ A pointer to the location in which to store a pointer to the nodeId cache
+ buffer
+ @param length
+ A pointer to the location in which to store the number of bytes in the buffer
+ */
+void getNodeIdNumberForOlsrCache(unsigned char ** buffer, unsigned int * length) {
+       *buffer = &cachedNodeIdBuffer[0];
+       *length = cachedNodeIdBufferLength;
+}
+
+/**
+ Check a nodeId number for validity and if valid set it up in the
+ cachedNodeIdBuffer. The valid range for the number is [min, max].
+
+ @param min
+ The lower bound for a valid number
+ @param max
+ The upper bound for a valid number
+ @param bytes
+ The number of bytes used by the number in the wire format
+
+ @return
+ - true when the number is valid
+ - false otherwise
+ */
+static bool setupNodeIdNumberForOlsrCache(unsigned long long min,
+               unsigned long long max, unsigned int bytes) {
+       unsigned long long val;
+
+       assert (bytes <= PUD_CACHED_NODEID_BUFFER_SIZE);
+
+       if (!getNodeIdAsNumber(&val)) {
+               return false;
+       }
+
+       if ((val >= min) && (val <= max)) {
+               int i = bytes - 1;
+               while (i >= 0) {
+                       cachedNodeIdBuffer[i] = val & 0xff;
+                       val >>= 8;
+                       i--;
+               }
+
+               assert(val == 0);
+
+               cachedNodeIdBufferLength = bytes;
+               return true;
+       }
+
+       pudError(false, "%s value %llu is out of range [%llu,%llu]",
+                       PUD_NODE_ID_NAME, val, min, max);
+       return false;
+}
+
+/*
+ * nodeId Validation
+ */
+
+/**
+ Validate whether the configured nodeId is valid w.r.t. the configured
+ nodeIdType
+
+ @return
+ - true when ok
+ - false on failure
+ */
+static bool setupNodeIdNumberForOlsrCacheAndValidate(NodeIdType nodeIdTypeNumber) {
+       switch (nodeIdTypeNumber) {
+               case PUD_NODEIDTYPE_IPV4: /* IPv4 address */
+               case PUD_NODEIDTYPE_IPV6: /* IPv6 address */
+               case PUD_NODEIDTYPE_MAC: /* hardware address */
+                       /* explicit return: configured nodeId is not relevant */
+                       return true;
+
+               case PUD_NODEIDTYPE_MSISDN: /* an MSISDN number */
+                       return setupNodeIdNumberForOlsrCache(0LL, 999999999999999LL,
+                                       PUD_NODEIDTYPE_MSISDN_BYTES);
+
+               case PUD_NODEIDTYPE_TETRA: /* a Tetra number */
+                       return setupNodeIdNumberForOlsrCache(0LL, 99999999999999999LL,
+                                       PUD_NODEIDTYPE_TETRA_BYTES);
+
+               case PUD_NODEIDTYPE_DNS: /* DNS name */
+               {
+                       bool invalidChars;
+                       char report[256];
+
+                       invalidChars = nmea_string_has_invalid_chars((char *) getNodeId(),
+                                       PUD_NODE_ID_NAME, &report[0], sizeof(report));
+                       if (invalidChars) {
+                               pudError(false, &report[0]);
+                       }
+                       return !invalidChars;
+               }
+
+               case PUD_NODEIDTYPE_192:
+                       return setupNodeIdNumberForOlsrCache(0LL, 9999999LL,
+                                       PUD_NODEIDTYPE_192_BYTES);
+
+               case PUD_NODEIDTYPE_193:
+                       return setupNodeIdNumberForOlsrCache(0LL, 999999LL,
+                                       PUD_NODEIDTYPE_193_BYTES);
+
+               case PUD_NODEIDTYPE_194:
+                       return setupNodeIdNumberForOlsrCache(1LL, 8191LL,
+                                       PUD_NODEIDTYPE_194_BYTES);
+
+               default: /* unsupported */
+                       /* explicit return: configured nodeId is not relevant, will
+                        * fallback to IP addresses */
+                       return true;
+       }
+
+       return false;
+}
+
+/*
+ * rxNonOlsrIf
+ */
+
+/** The maximum number of RX non-OLSR interfaces */
+#define PUD_RX_NON_OLSR_IF_MAX 32
+
+/** Array with RX non-OLSR interface names */
+static unsigned char rxNonOlsrInterfaceNames[PUD_RX_NON_OLSR_IF_MAX][IFNAMSIZ + 1];
+
+/** The number of RX non-OLSR interface names in the array */
+static unsigned int rxNonOlsrInterfaceCount = 0;
+
+/**
+ Determine whether a give interface name is configured as a receive non-OLSR
+ interface.
+
+ @param ifName
+ The interface name to check
+
+ @return
+ - true when the given interface name is configured as a receive non-OLSR
+ interface
+ - false otherwise
+ */
+bool isRxNonOlsrInterface(const char *ifName) {
+       unsigned int i;
+
+       assert (ifName != NULL);
+
+       for (i = 0; i < rxNonOlsrInterfaceCount; i++) {
+               if (strncmp((char *) &rxNonOlsrInterfaceNames[i][0], ifName, IFNAMSIZ
+                               + 1) == 0) {
+                       return true;
+               }
+       }
+
+       return false;
+}
+
+/**
+ Add a receive non-OLSR interface
+
+ @param value
+ The name of the non-OLSR interface to add
+ @param data
+ Unused
+ @param addon
+ Unused
+
+ @return
+ - true when an error is detected
+ - false otherwise
+ */
+int addRxNonOlsrInterface(const char *value, void *data __attribute__ ((unused)),
+               set_plugin_parameter_addon addon __attribute__ ((unused))) {
+       unsigned long valueLength;
+
+       assert (value != NULL);
+
+       valueLength = strlen(value);
+       if (valueLength > IFNAMSIZ) {
+               pudError(false, "Configured %s (%s) is too long,"
+                       " maximum length is %u, current length is %lu",
+                               PUD_RX_NON_OLSR_IF_NAME, value, IFNAMSIZ, valueLength);
+               return true;
+       }
+
+       if (!isRxNonOlsrInterface(value)) {
+               if (rxNonOlsrInterfaceCount >= PUD_RX_NON_OLSR_IF_MAX) {
+                       pudError(false, "Can't configure more than %u receive interfaces",
+                                       PUD_RX_NON_OLSR_IF_MAX);
+                       return true;
+               }
+
+               strcpy((char *) &rxNonOlsrInterfaceNames[rxNonOlsrInterfaceCount][0],
+                               value);
+               rxNonOlsrInterfaceCount++;
+       }
+
+       return false;
+}
+
+/*
+ * rxAllowedSourceIpAddress
+ */
+
+/** The maximum number of RX allowed source IP addresses */
+#define PUD_RX_ALLOWED_SOURCE_IP_MAX 32
+
+/** Array with RX allowed source IP addresses */
+static struct sockaddr rxAllowedSourceIpAddresses[PUD_RX_ALLOWED_SOURCE_IP_MAX];
+
+/** The number of RX allowed source IP addresses in the array */
+static unsigned int rxAllowedSourceIpAddressesCount = 0;
+
+/**
+ Determine whether a give IP address is configured as an allowed source IP
+ address.
+
+ @param sender
+ The IP address to check
+
+ @return
+ - true when the given IP address is configured as an allowed source IP
+ address
+ - false otherwise
+ */
+bool isRxAllowedSourceIpAddress(struct sockaddr * sender) {
+       void * addr;
+       unsigned int addrSize;
+       unsigned int i;
+
+       if (rxAllowedSourceIpAddressesCount == 0) {
+               return true;
+       }
+
+       if (sender == NULL) {
+               return false;
+       }
+
+       if (sender->sa_family == AF_INET) {
+               addr = (void *) (&((struct sockaddr_in *) sender)->sin_addr);
+               addrSize = sizeof(struct in_addr);
+       } else {
+               addr = (void *) (&((struct sockaddr_in6 *) sender)->sin6_addr);
+               addrSize = sizeof(struct in6_addr);
+       }
+
+       for (i = 0; i < rxAllowedSourceIpAddressesCount; i++) {
+               if ((rxAllowedSourceIpAddresses[i].sa_family == sender->sa_family)
+                               && (memcmp(&rxAllowedSourceIpAddresses[i].sa_data, addr,
+                                               addrSize) == 0)) {
+                       return true;
+               }
+       }
+
+       return false;
+}
+
+/**
+ Set the RX allowed source IP addresses.
+
+ @param value
+ The RX allowed source IP address (in string representation)
+ @param data
+ Unused
+ @param addon
+ Unused
+
+ @return
+ - true when an error is detected
+ - false otherwise
+ */
+int addRxAllowedSourceIpAddress(const char *value, void *data __attribute__ ((unused)),
+               set_plugin_parameter_addon addon __attribute__ ((unused))) {
+       static const char * valueName = PUD_RX_ALLOWED_SOURCE_IP_NAME;
+       const char * valueInternal = value;
+       int conversion;
+       struct sockaddr addr;
+
+       assert (value != NULL);
+
+       memset(&addr, 0, sizeof(addr));
+
+       addr.sa_family = olsr_cnf->ip_version;
+       conversion = inet_pton(olsr_cnf->ip_version, valueInternal, &addr.sa_data);
+       if (conversion != 1) {
+               pudError((conversion == -1) ? true : false,
+                               "Configured %s (%s) is not an IP address", valueName,
+                               valueInternal);
+               return true;
+       }
+
+       if ((rxAllowedSourceIpAddressesCount == 0) || !isRxAllowedSourceIpAddress(&addr)) {
+               if (rxAllowedSourceIpAddressesCount >= PUD_RX_ALLOWED_SOURCE_IP_MAX) {
+                       pudError(false, "Can't configure more than %u allowed source IP"
+                               " addresses", PUD_RX_ALLOWED_SOURCE_IP_MAX);
+                       return true;
+               }
+
+               memcpy(&rxAllowedSourceIpAddresses[rxAllowedSourceIpAddressesCount],
+                               &addr, sizeof(addr));
+               rxAllowedSourceIpAddressesCount++;
+       }
+
+       return false;
+}
+
+/*
+ * rxMcAddr
+ */
+
+/** The rx multicast address */
+static union olsr_sockaddr rxMcAddr;
+
+/** True when the rx multicast address is set */
+static bool rxMcAddrSet = false;
+
+/**
+ @return
+ The receive multicast address (in network byte order). Sets both the address
+ and the port to their default values when the address was not yet set.
+ */
+union olsr_sockaddr * getRxMcAddr(void) {
+       if (!rxMcAddrSet) {
+               setRxMcAddr(NULL, NULL, ((set_plugin_parameter_addon) {.pc = NULL}));
+       }
+       return &rxMcAddr;
+}
+
+/**
+ Set the receive multicast address. Sets the address to its default value when
+ the value is NULL. Also sets the port to its default value when the address
+ was not yet set.
+
+ @param value
+ The receive multicast address (in string representation)
+ @param data
+ Unused
+ @param addon
+ Unused
+
+ @return
+ - true when an error is detected
+ - false otherwise
+ */
+int setRxMcAddr(const char *value, void *data __attribute__ ((unused)), set_plugin_parameter_addon addon __attribute__ ((unused))) {
+       static const char * valueName = PUD_RX_MC_ADDR_NAME;
+       void * ipAddress;
+       in_port_t * port;
+       const char * valueInternal = value;
+       int conversion;
+
+       getOlsrSockAddrAndPort(olsr_cnf->ip_version, &rxMcAddr, &ipAddress, &port);
+       if (olsr_cnf->ip_version == AF_INET) {
+               rxMcAddr.in4.sin_family = olsr_cnf->ip_version;
+               if (valueInternal == NULL) {
+                       valueInternal = PUD_RX_MC_ADDR_4_DEFAULT;
+               }
+       } else {
+               rxMcAddr.in6.sin6_family = olsr_cnf->ip_version;
+               if (valueInternal == NULL) {
+                       valueInternal = PUD_RX_MC_ADDR_6_DEFAULT;
+               }
+       }
+
+       if (!rxMcAddrSet) {
+               *port = htons(PUD_RX_MC_PORT_DEFAULT);
+       }
+
+       conversion = inet_pton(olsr_cnf->ip_version, valueInternal, ipAddress);
+       if (conversion != 1) {
+               pudError((conversion == -1) ? true : false,
+                               "Configured %s (%s) is not an IP address", valueName,
+                               valueInternal);
+               return true;
+       }
+
+       if (!isMulticast(olsr_cnf->ip_version, &rxMcAddr)) {
+               pudError(false, "Configured %s (%s) is not a multicast address",
+                               valueName, valueInternal);
+               return true;
+       }
+
+       rxMcAddrSet = true;
+       return false;
+}
+
+/*
+ * rxMcPort
+ */
+
+/**
+ @return
+ The receive multicast port (in network byte order)
+ */
+unsigned short getRxMcPort(void) {
+       union olsr_sockaddr * addr = getRxMcAddr();
+       return *getOlsrSockaddrPort(olsr_cnf->ip_version, addr);
+}
+
+/**
+ Set the receive multicast port
+
+ @param value
+ The receive multicast port (a number in string representation)
+ @param data
+ Unused
+ @param addon
+ Unused
+
+ @return
+ - true when an error is detected
+ - false otherwise
+ */
+int setRxMcPort(const char *value, void *data __attribute__ ((unused)), set_plugin_parameter_addon addon __attribute__ ((unused))) {
+       static const char * valueName = PUD_RX_MC_PORT_NAME;
+       unsigned long long rxMcPortNew;
+       in_port_t * port;
+       union olsr_sockaddr * addr = getRxMcAddr();
+
+       assert (value != NULL);
+
+       if (!readULL(valueName, value, &rxMcPortNew)) {
+               return true;
+       }
+
+       if ((rxMcPortNew < 1) || (rxMcPortNew > 65535)) {
+               pudError(false, "Configured %s (%llu) is outside of"
+                       " valid range 1-65535", valueName, rxMcPortNew);
+               return true;
+       }
+
+       port = getOlsrSockaddrPort(olsr_cnf->ip_version, addr);
+       *port = htons((uint16_t) rxMcPortNew);
+
+       return false;
+}
+
+/*
+ * txNonOlsrIf
+ */
+
+/** The maximum number of rx non-olsr interfaces */
+#define PUD_TX_NON_OLSR_IF_MAX 32
+
+/** Array with tx non-olsr interface names */
+static unsigned char txNonOlsrInterfaceNames[PUD_TX_NON_OLSR_IF_MAX][IFNAMSIZ + 1];
+
+/** The number of tx interface names in the array */
+static unsigned int txNonOlsrInterfaceCount = 0;
+
+/**
+ Determine whether a give interface name is configured as a transmit non-OLSR
+ interface.
+
+ @param ifName
+ The interface to check
+
+ @return
+ - true when the given interface name is configured as a transmit non-OLSR
+ interface
+ - false otherwise
+ */
+bool isTxNonOlsrInterface(const char *ifName) {
+       unsigned int i;
+
+       assert (ifName != NULL);
+
+       for (i = 0; i < txNonOlsrInterfaceCount; i++) {
+               if (strncmp((char *) &txNonOlsrInterfaceNames[i][0], ifName, IFNAMSIZ
+                               + 1) == 0) {
+                       return true;
+               }
+       }
+
+       return false;
+}
+
+/**
+ Add a transmit non-OLSR interface
+
+ @param value
+ The name of the non-OLSR interface to add
+ @param data
+ Unused
+ @param addon
+ Unused
+
+ @return
+ - true when an error is detected
+ - false otherwise
+ */
+int addTxNonOlsrInterface(const char *value, void *data __attribute__ ((unused)),
+               set_plugin_parameter_addon addon __attribute__ ((unused))) {
+       unsigned long valueLength;
+
+       assert (value != NULL);
+
+       valueLength = strlen(value);
+       if (valueLength > IFNAMSIZ) {
+               pudError(false, "Configured %s (%s) is too long,"
+                       " maximum length is %u, current length is %lu",
+                               PUD_TX_NON_OLSR_IF_NAME, value, IFNAMSIZ, valueLength);
+               return true;
+       }
+
+       if (!isTxNonOlsrInterface(value)) {
+               if (txNonOlsrInterfaceCount >= PUD_TX_NON_OLSR_IF_MAX) {
+                       pudError(false, "Can not configure more than %u transmit"
+                               " interfaces", PUD_TX_NON_OLSR_IF_MAX);
+                       return true;
+               }
+
+               strcpy((char *) &txNonOlsrInterfaceNames[txNonOlsrInterfaceCount][0],
+                               value);
+               txNonOlsrInterfaceCount++;
+       }
+
+       return false;
+}
+
+/*
+ * txMcAddr
+ */
+
+/** The tx multicast address */
+static union olsr_sockaddr txMcAddr;
+
+/** True when the tx multicast address is set */
+static bool txMcAddrSet = false;
+
+/**
+ @return
+ The transmit multicast address (in network byte order). Sets both the address
+ and the port to their default values when the address was not yet set.
+ */
+union olsr_sockaddr * getTxMcAddr(void) {
+       if (!txMcAddrSet) {
+               setTxMcAddr(NULL, NULL, ((set_plugin_parameter_addon) {.pc = NULL}));
+       }
+       return &txMcAddr;
+}
+
+/**
+ Set the transmit multicast address. Sets the address to its default value when
+ the value is NULL. Also sets the port to its default value when the address
+ was not yet set.
+
+ @param value
+ The transmit multicast address (in string representation)
+ @param data
+ Unused
+ @param addon
+ Unused
+
+ @return
+ - true when an error is detected
+ - false otherwise
+ */
+int setTxMcAddr(const char *value, void *data __attribute__ ((unused)), set_plugin_parameter_addon addon __attribute__ ((unused))) {
+       static const char * valueName = PUD_TX_MC_ADDR_NAME;
+       void * ipAddress;
+       in_port_t * port;
+       const char * valueInternal = value;
+       int conversion;
+
+       getOlsrSockAddrAndPort(olsr_cnf->ip_version, &txMcAddr, &ipAddress, &port);
+       if (olsr_cnf->ip_version == AF_INET) {
+               txMcAddr.in4.sin_family = olsr_cnf->ip_version;
+               if (valueInternal == NULL) {
+                       valueInternal = PUD_TX_MC_ADDR_4_DEFAULT;
+               }
+       } else {
+               txMcAddr.in6.sin6_family = olsr_cnf->ip_version;
+               if (valueInternal == NULL) {
+                       valueInternal = PUD_TX_MC_ADDR_6_DEFAULT;
+               }
+       }
+
+       if (!txMcAddrSet) {
+               *port = htons(PUD_TX_MC_PORT_DEFAULT);
+       }
+
+       conversion = inet_pton(olsr_cnf->ip_version, valueInternal, ipAddress);
+       if (conversion != 1) {
+               pudError((conversion == -1) ? true : false,
+                               "Configured %s (%s) is not an IP address", valueName,
+                               valueInternal);
+               return true;
+       }
+
+       if (!isMulticast(olsr_cnf->ip_version, &txMcAddr)) {
+               pudError(false, "Configured %s (%s) is not a multicast address",
+                               valueName, valueInternal);
+               return true;
+       }
+
+       txMcAddrSet = true;
+       return false;
+}
+
+/*
+ * txMcPort
+ */
+
+/**
+ @return
+ The transmit multicast port (in network byte order)
+ */
+unsigned short getTxMcPort(void) {
+       union olsr_sockaddr * addr = getTxMcAddr();
+       return *getOlsrSockaddrPort(olsr_cnf->ip_version, addr);
+}
+
+/**
+ Set the transmit multicast port
+
+ @param value
+ The transmit multicast port (a number in string representation)
+ @param data
+ Unused
+ @param addon
+ Unused
+
+ @return
+ - true when an error is detected
+ - false otherwise
+ */
+int setTxMcPort(const char *value, void *data __attribute__ ((unused)), set_plugin_parameter_addon addon __attribute__ ((unused))) {
+       static const char * valueName = PUD_TX_MC_PORT_NAME;
+       unsigned long long txMcPortNew;
+       in_port_t * port;
+       union olsr_sockaddr * addr = getTxMcAddr();
+
+       assert (value != NULL);
+
+       if (!readULL(valueName, value, &txMcPortNew)) {
+               return true;
+       }
+
+       if ((txMcPortNew < 1) || (txMcPortNew > 65535)) {
+               pudError(false, "Configured %s (%llu) is outside of"
+                       " valid range 1-65535", valueName, txMcPortNew);
+               return true;
+       }
+
+       port = getOlsrSockaddrPort(olsr_cnf->ip_version, addr);
+       *port = htons((uint16_t) txMcPortNew);
+
+       return false;
+}
+
+/*
+ * txTtl
+ */
+
+/** The tx TTL */
+static unsigned char txTtl = PUD_TX_TTL_DEFAULT;
+
+/**
+ @return
+ The transmit multicast IP packet time-to-live
+ */
+unsigned char getTxTtl(void) {
+       return txTtl;
+}
+
+/**
+ Set the transmit multicast IP packet time-to-live
+
+ @param value
+ The transmit multicast IP packet time-to-live (a number in string representation)
+ @param data
+ Unused
+ @param addon
+ Unused
+
+ @return
+ - true when an error is detected
+ - false otherwise
+ */
+int setTxTtl(const char *value, void *data __attribute__ ((unused)), set_plugin_parameter_addon addon __attribute__ ((unused))) {
+       static const char * valueName = PUD_TX_TTL_NAME;
+       unsigned long long txTtlNew;
+
+       assert (value != NULL);
+
+       if (!readULL(valueName, value, &txTtlNew)) {
+               return true;
+       }
+
+       if ((txTtlNew < 1) || (txTtlNew > MAX_TTL)) {
+               pudError(false, "Configured %s (%llu) is outside of"
+                       " valid range 1-%u", valueName, txTtlNew, MAX_TTL);
+               return true;
+       }
+
+       txTtl = txTtlNew;
+
+       return false;
+}
+
+/*
+ * txNmeaMessagePrefix
+ */
+
+/** The exact length of the tx NMEA message prefix */
+#define PUD_TXNMEAMESSAGEPREFIXLENGTH 4
+
+/** The tx NMEA message prefix buffer */
+static unsigned char txNmeaMessagePrefix[PUD_TXNMEAMESSAGEPREFIXLENGTH + 1];
+
+/** True when the tx NMEA message prefix is set */
+static bool txNmeaMessagePrefixSet = false;
+
+/**
+ @return
+ The transmit multicast NMEA message prefix
+ */
+unsigned char * getTxNmeaMessagePrefix(void) {
+       if (!txNmeaMessagePrefixSet) {
+               setTxNmeaMessagePrefix(PUD_TX_NMEAMESSAGEPREFIX_DEFAULT, NULL,
+                               (set_plugin_parameter_addon) {.pc = NULL});
+       }
+       return &txNmeaMessagePrefix[0];
+}
+
+/**
+ Set the transmit multicast NMEA message prefix
+
+ @param value
+ The transmit multicast NMEA message prefix (in string representation)
+ @param data
+ Unused
+ @param addon
+ Unused
+
+ @return
+ - true when an error is detected
+ - false otherwise
+ */
+int setTxNmeaMessagePrefix(const char *value, void *data __attribute__ ((unused)),
+               set_plugin_parameter_addon addon __attribute__ ((unused))) {
+       static const char * valueName = PUD_TX_NMEAMESSAGEPREFIX_NAME;
+       size_t valueLength;
+       bool invalidChars;
+       char report[256];
+
+       assert (value != NULL);
+
+       valueLength = strlen(value);
+       if (valueLength != PUD_TXNMEAMESSAGEPREFIXLENGTH) {
+               pudError(false, "Configured %s (%s) must be %u exactly characters",
+                               valueName, value, PUD_TXNMEAMESSAGEPREFIXLENGTH);
+               return true;
+       }
+
+       invalidChars = nmea_string_has_invalid_chars(value, valueName, &report[0],
+                       sizeof(report));
+       if (invalidChars) {
+               pudError(false, &report[0]);
+               return true;
+       }
+
+       if ((strchr(value, ' ') != NULL) || (strchr(value, '\t') != NULL)) {
+               pudError(false, "Configured %s (%s) can not contain whitespace",
+                               valueName, value);
+               return true;
+       }
+
+       strcpy((char *) &txNmeaMessagePrefix[0], value);
+       txNmeaMessagePrefixSet = true;
+       return false;
+}
+
+/*
+ * olsrTtl
+ */
+
+/** The OLSR TTL */
+static unsigned char olsrTtl = PUD_OLSR_TTL_DEFAULT;
+
+/**
+ @return
+ The OLSR multicast IP packet time-to-live
+ */
+unsigned char getOlsrTtl(void) {
+       return olsrTtl;
+}
+
+/**
+ Set the OLSR multicast IP packet time-to-live
+
+ @param value
+ The OLSR multicast IP packet time-to-live (a number in string representation)
+ @param data
+ Unused
+ @param addon
+ Unused
+
+ @return
+ - true when an error is detected
+ - false otherwise
+ */
+int setOlsrTtl(const char *value, void *data __attribute__ ((unused)), set_plugin_parameter_addon addon __attribute__ ((unused))) {
+       static const char * valueName = PUD_OLSR_TTL_NAME;
+       unsigned long long olsrTtlNew;
+
+       assert (value != NULL);
+
+       if (!readULL(valueName, value, &olsrTtlNew)) {
+               return true;
+       }
+
+       if ((olsrTtlNew < 1) || (olsrTtlNew > MAX_TTL)) {
+               pudError(false, "Configured %s (%llu) is outside of valid range 1-%u",
+                               valueName, olsrTtlNew, MAX_TTL);
+               return true;
+       }
+
+       olsrTtl = olsrTtlNew;
+
+       return false;
+}
+
+/*
+ * updateIntervalStationary
+ */
+
+/** The stationary interval update plugin parameter (in seconds) */
+static unsigned long long updateIntervalStationary = PUD_UPDATE_INTERVAL_STATIONARY_DEFAULT;
+
+/**
+ @return
+ The stationary interval update plugin parameter (in seconds)
+ */
+unsigned long long getUpdateIntervalStationary(void) {
+       return updateIntervalStationary;
+}
+
+/**
+ Set stationary interval update plugin parameter
+
+ @param value
+ The stationary interval update plugin parameter (in seconds)
+ @param data
+ Unused
+ @param addon
+ Unused
+
+ @return
+ - true when an error is detected
+ - false otherwise
+ */
+int setUpdateIntervalStationary(const char *value, void *data __attribute__ ((unused)),
+               set_plugin_parameter_addon addon __attribute__ ((unused))) {
+       static const char * valueName = PUD_UPDATE_INTERVAL_STATIONARY_NAME;
+       unsigned long long updateIntervalStationaryNew;
+
+       assert (value != NULL);
+
+       if (!readULL(valueName, value, &updateIntervalStationaryNew)) {
+               return true;
+       }
+
+       if (updateIntervalStationaryNew < 1) {
+               pudError(false, "Configured %s must be at least 1", valueName);
+               return true;
+       }
+
+       updateIntervalStationary = updateIntervalStationaryNew;
+
+       return false;
+}
+
+/*
+ * updateIntervalMoving
+ */
+
+/** The moving interval update plugin parameter (in seconds) */
+static unsigned long long updateIntervalMoving = PUD_UPDATE_INTERVAL_MOVING_DEFAULT;
+
+/**
+ @return
+ The moving interval update plugin parameter (in seconds)
+ */
+unsigned long long getUpdateIntervalMoving(void) {
+       return updateIntervalMoving;
+}
+
+/**
+ Set moving interval update plugin parameter
+
+ @param value
+ The moving interval update plugin parameter (in seconds)
+ @param data
+ Unused
+ @param addon
+ Unused
+
+ @return
+ - true when an error is detected
+ - false otherwise
+ */
+int setUpdateIntervalMoving(const char *value, void *data __attribute__ ((unused)),
+               set_plugin_parameter_addon addon __attribute__ ((unused))) {
+       static const char * valueName = PUD_UPDATE_INTERVAL_MOVING_NAME;
+       unsigned long long updateIntervalMovingNew;
+
+       assert (value != NULL);
+
+       if (!readULL(valueName, value, &updateIntervalMovingNew)) {
+               return true;
+       }
+
+       if (updateIntervalMovingNew < 1) {
+               pudError(false, "Configured %s must be at least 1", valueName);
+               return true;
+       }
+
+       updateIntervalMoving = updateIntervalMovingNew;
+
+       return false;
+}
+
+/*
+ * movingSpeedThreshold
+ */
+
+/** The moving speed threshold plugin parameter (in kph) */
+static unsigned long long movingSpeedThreshold = PUD_MOVING_SPEED_THRESHOLD_DEFAULT;
+
+/**
+ @return
+ The moving speed threshold plugin parameter (in kph)
+ */
+unsigned long long getMovingSpeedThreshold(void) {
+       return movingSpeedThreshold;
+}
+
+/**
+ Set moving speed threshold plugin parameter
+
+ @param value
+ The moving speed threshold plugin parameter (in kph)
+ @param data
+ Unused
+ @param addon
+ Unused
+
+ @return
+ - true when an error is detected
+ - false otherwise
+ */
+int setMovingSpeedThreshold(const char *value, void *data __attribute__ ((unused)),
+               set_plugin_parameter_addon addon __attribute__ ((unused))) {
+       static const char * valueName = PUD_MOVING_SPEED_THRESHOLD_NAME;
+       unsigned long long movingSpeedThresholdNew;
+
+       assert (value != NULL);
+
+       if (!readULL(valueName, value, &movingSpeedThresholdNew)) {
+               return true;
+       }
+
+       movingSpeedThreshold = movingSpeedThresholdNew;
+
+       return false;
+}
+
+/*
+ * movingDistanceThreshold
+ */
+
+/** The moving distance threshold plugin parameter (in meters) */
+static unsigned long long movingDistanceThreshold = PUD_MOVING_DISTANCE_THRESHOLD_DEFAULT;
+
+/**
+ @return
+ The moving distance threshold plugin parameter (in meters)
+ */
+unsigned long long getMovingDistanceThreshold(void) {
+       return movingDistanceThreshold;
+}
+
+/**
+ Set moving distance threshold plugin parameter
+
+ @param value
+ The moving distance threshold plugin parameter (in meter)
+ @param data
+ Unused
+ @param addon
+ Unused
+
+ @return
+ - true when an error is detected
+ - false otherwise
+ */
+int setMovingDistanceThreshold(const char *value, void *data __attribute__ ((unused)),
+               set_plugin_parameter_addon addon __attribute__ ((unused))) {
+       static const char * valueName = PUD_MOVING_DISTANCE_THRESHOLD_NAME;
+       unsigned long long movingDistanceThresholdNew;
+
+       assert (value != NULL);
+
+       if (!readULL(valueName, value, &movingDistanceThresholdNew)) {
+               return true;
+       }
+
+       movingDistanceThreshold = movingDistanceThresholdNew;
+
+       return false;
+}
+
+/*
+ * dopMultiplier
+ */
+
+/* The DOP multiplier plugin parameter */
+static double dopMultiplier = PUD_DOP_MULTIPLIER_DEFAULT;
+
+/**
+ @return
+ The DOP multiplier plugin parameter
+ */
+double getDopMultiplier(void) {
+       return dopMultiplier;
+}
+
+/**
+ Set DOP multiplier plugin parameter
+
+ @param value
+ The DOP multiplier plugin parameter
+ @param data
+ Unused
+ @param addon
+ Unused
+
+ @return
+ - true when an error is detected
+ - false otherwise
+ */
+int setDopMultiplier(const char *value, void *data __attribute__ ((unused)),
+               set_plugin_parameter_addon addon __attribute__ ((unused))) {
+       static const char * valueName = PUD_DOP_MULTIPLIER_NAME;
+       double dopMultiplierNew;
+
+       assert (value != NULL);
+
+       if (!readDouble(valueName, value, &dopMultiplierNew)) {
+               return true;
+       }
+
+       dopMultiplier = dopMultiplierNew;
+
+       return false;
+}
+
+/*
+ * defaultHdop
+ */
+
+/** The default HDOP plugin parameter (in meters) */
+static unsigned long long defaultHdop = PUD_DEFAULT_HDOP_DEFAULT;
+
+/**
+ @return
+ The default HDOP plugin parameter (in meters)
+ */
+unsigned long long getDefaultHdop(void) {
+       return defaultHdop;
+}
+
+/**
+ Set default HDOP plugin parameter
+
+ @param value
+ The default HDOP plugin parameter (in meters)
+ @param data
+ Unused
+ @param addon
+ Unused
+
+ @return
+ - true when an error is detected
+ - false otherwise
+ */
+int setDefaultHdop(const char *value, void *data __attribute__ ((unused)),
+               set_plugin_parameter_addon addon __attribute__ ((unused))) {
+       static const char * valueName = PUD_MOVING_DISTANCE_THRESHOLD_NAME;
+       unsigned long long defaultHdopNew;
+
+       assert (value != NULL);
+
+       if (!readULL(valueName, value, &defaultHdopNew)) {
+               return true;
+       }
+
+       defaultHdop = defaultHdopNew;
+
+       return false;
+}
+
+/*
+ * defaultVdop
+ */
+
+/** The default VDOP plugin parameter (in meters) */
+static unsigned long long defaultVdop = PUD_DEFAULT_VDOP_DEFAULT;
+
+/**
+ @return
+ The default VDOP plugin parameter (in meters)
+ */
+unsigned long long getDefaultVdop(void) {
+       return defaultVdop;
+}
+
+/**
+ Set default VDOP plugin parameter
+
+ @param value
+ The default VDOP plugin parameter (in meters)
+ @param data
+ Unused
+ @param addon
+ Unused
+
+ @return
+ - true when an error is detected
+ - false otherwise
+ */
+int setDefaultVdop(const char *value, void *data __attribute__ ((unused)),
+               set_plugin_parameter_addon addon __attribute__ ((unused))) {
+       static const char * valueName = PUD_MOVING_DISTANCE_THRESHOLD_NAME;
+       unsigned long long defaultVdopNew;
+
+       assert (value != NULL);
+
+       if (!readULL(valueName, value, &defaultVdopNew)) {
+               return true;
+       }
+
+       defaultVdop = defaultVdopNew;
+
+       return false;
+}
+
+/*
+ * averageDepth
+ */
+
+/** The depth of the average list */
+static unsigned long long averageDepth = PUD_AVERAGE_DEPTH_DEFAULT;
+
+/**
+ @return
+ The depth of the average list
+ */
+unsigned long long getAverageDepth(void) {
+       return averageDepth;
+}
+
+/**
+ Set average depth plugin parameter
+
+ @param value
+ The average depth plugin parameter
+ @param data
+ Unused
+ @param addon
+ Unused
+
+ @return
+ - true when an error is detected
+ - false otherwise
+ */
+int setAverageDepth(const char *value, void *data __attribute__ ((unused)),
+               set_plugin_parameter_addon addon __attribute__ ((unused))) {
+       static const char * valueName = PUD_AVERAGE_DEPTH_NAME;
+       unsigned long long averageDepthNew;
+
+       assert (value != NULL);
+
+       if (!readULL(valueName, value, &averageDepthNew)) {
+               return true;
+       }
+
+       if (averageDepthNew < 1) {
+               pudError(false, "Configured %s must be at least 1", valueName);
+               return true;
+       }
+
+       averageDepth = averageDepthNew;
+
+       return false;
+}
+
+/*
+ * hysteresisCountToStationary
+ */
+
+/** The hysteresis count for changing state from moving to stationary */
+static unsigned long long hysteresisCountToStationary = PUD_HYSTERESIS_COUNT_2STAT_DEFAULT;
+
+/**
+ @return
+ The hysteresis count for changing state from moving to stationary
+ */
+unsigned long long getHysteresisCountToStationary(void) {
+       return hysteresisCountToStationary;
+}
+
+/**
+ Set hysteresis count plugin parameter
+
+ @param value
+ The hysteresis count plugin parameter
+ @param data
+ Unused
+ @param addon
+ Unused
+
+ @return
+ - true when an error is detected
+ - false otherwise
+ */
+int setHysteresisCountToStationary(const char *value, void *data __attribute__ ((unused)),
+               set_plugin_parameter_addon addon __attribute__ ((unused))) {
+       static const char * valueName = PUD_HYSTERESIS_COUNT_2STAT_NAME;
+       unsigned long long hysteresisCountNew;
+
+       assert (value != NULL);
+
+       if (!readULL(valueName, value, &hysteresisCountNew)) {
+               return true;
+       }
+
+       hysteresisCountToStationary = hysteresisCountNew;
+
+       return false;
+}
+
+/*
+ * hysteresisCountToMoving
+ */
+
+/** The hysteresis count for changing state from stationary to moving */
+static unsigned long long hysteresisCountToMoving = PUD_HYSTERESIS_COUNT_2MOV_DEFAULT;
+
+/**
+ @return
+ The hysteresis count for changing state from stationary to moving
+ */
+unsigned long long getHysteresisCountToMoving(void) {
+       return hysteresisCountToMoving;
+}
+
+/**
+ Set hysteresis count plugin parameter
+
+ @param value
+ The hysteresis count plugin parameter
+ @param data
+ Unused
+ @param addon
+ Unused
+
+ @return
+ - true when an error is detected
+ - false otherwise
+ */
+int setHysteresisCountToMoving(const char *value, void *data __attribute__ ((unused)),
+               set_plugin_parameter_addon addon __attribute__ ((unused))) {
+       static const char * valueName = PUD_HYSTERESIS_COUNT_2MOV_NAME;
+       unsigned long long hysteresisCountNew;
+
+       assert (value != NULL);
+
+       if (!readULL(valueName, value, &hysteresisCountNew)) {
+               return true;
+       }
+
+       hysteresisCountToMoving = hysteresisCountNew;
+
+       return false;
+}
+
+/*
+ * useDeDup
+ */
+
+/* when true then duplicate message detection is performed */
+static bool useDeDup = PUD_USE_DEDUP_DEFAULT;
+
+/**
+ @return
+ The duplicate message detection setting
+ */
+bool getUseDeDup(void) {
+       return useDeDup;
+}
+
+/**
+ Set duplicate message detection setting plugin parameter
+
+ @param value
+ The duplicate message detection setting plugin parameter
+ @param data
+ Unused
+ @param addon
+ Unused
+
+ @return
+ - true when an error is detected
+ - false otherwise
+ */
+int setUseDeDup(const char *value, void *data __attribute__ ((unused)),
+               set_plugin_parameter_addon addon __attribute__ ((unused))) {
+       static const char * valueName = PUD_USE_DEDUP_NAME;
+       unsigned long long useDeDupNew;
+
+       assert (value != NULL);
+
+       if (!readULL(valueName, value, &useDeDupNew)) {
+               return true;
+       }
+
+       if ((useDeDupNew != 0) && (useDeDupNew != 1)) {
+               pudError(false, "Configured %s must be 0 (false) or 1 (true)",
+                               valueName);
+               return true;
+       }
+
+       useDeDup = (useDeDupNew == 1);
+
+       return false;
+}
+
+/*
+ * deDupDepth
+ */
+
+/** The hysteresis count for changing state from stationary to moving */
+static unsigned long long deDupDepth = PUD_DEDUP_DEPTH_DEFAULT;
+
+/**
+ @return
+ The hysteresis count for changing state from stationary to moving
+ */
+unsigned long long getDeDupDepth(void) {
+       return deDupDepth;
+}
+
+/**
+ Set de-duplication depth plugin parameter
+
+ @param value
+ The de-duplication depth plugin parameter
+ @param data
+ Unused
+ @param addon
+ Unused
+
+ @return
+ - true when an error is detected
+ - false otherwise
+ */
+int setDeDupDepth(const char *value, void *data __attribute__ ((unused)),
+               set_plugin_parameter_addon addon __attribute__ ((unused))) {
+       static const char * valueName = PUD_DEDUP_DEPTH_NAME;
+       unsigned long long deDupDepthNew;
+
+       assert (value != NULL);
+
+       if (!readULL(valueName, value, &deDupDepthNew)) {
+               return true;
+       }
+
+       deDupDepth = deDupDepthNew;
+
+       return false;
+}
+
+/*
+ * useLoopback
+ */
+
+/* when true then loopback is performed */
+static bool useLoopback = PUD_USE_LOOPBACK_DEFAULT;
+
+/**
+ @return
+ The loopback usage setting
+ */
+bool getUseLoopback(void) {
+       return useLoopback;
+}
+
+/**
+ Set loopback usage plugin parameter
+
+ @param value
+ The loopback usage plugin parameter
+ @param data
+ Unused
+ @param addon
+ Unused
+
+ @return
+ - true when an error is detected
+ - false otherwise
+ */
+int setUseLoopback(const char *value, void *data __attribute__ ((unused)),
+               set_plugin_parameter_addon addon __attribute__ ((unused))) {
+       static const char * valueName = PUD_USE_LOOPBACK_NAME;
+       unsigned long long useLoopbackNew;
+
+       assert (value != NULL);
+
+       if (!readULL(valueName, value, &useLoopbackNew)) {
+               return true;
+       }
+
+       if ((useLoopbackNew != 0) && (useLoopbackNew != 1)) {
+               pudError(false, "Configured %s must be 0 (false) or 1 (true)",
+                               valueName);
+               return true;
+       }
+
+       useLoopback = (useLoopbackNew == 1);
+
+       return false;
+}
+
+/*
+ * Check Functions
+ */
+
+/**
+ Check the configuration for consistency and validity.
+
+ @return
+ - true when the configuration is consistent and valid
+ - false otherwise
+ */
+unsigned int checkConfig(void) {
+       int retval = true;
+
+       if (rxNonOlsrInterfaceCount == 0) {
+               pudError(false, "No receive non-OLSR interfaces configured");
+               retval = false;
+       }
+
+       if (txNonOlsrInterfaceCount == 0) {
+               pudError(false, "No transmit non-OLSR interfaces configured");
+               retval = false;
+       }
+
+       if (!nodeIdSet) {
+               if (nodeIdType == PUD_NODEIDTYPE_DNS) {
+                       char name[PUD_NODEIDMAXLENGTH + 1];
+
+                       errno = 0;
+                       if (gethostname(&name[0], sizeof(name)) < 0) {
+                               pudError(true, "Could not get the host name");
+                               retval = false;
+                       } else {
+                               setNodeId(&name[0], NULL,
+                                               (set_plugin_parameter_addon) {.pc = NULL});
+                       }
+               } else if ((nodeIdType != PUD_NODEIDTYPE_MAC) && (nodeIdType
+                               != PUD_NODEIDTYPE_IPV4) && (nodeIdType != PUD_NODEIDTYPE_IPV6)) {
+                       pudError(false, "No node ID set while one is required for"
+                               " node type %u", nodeIdType);
+                       retval = false;
+               }
+       }
+
+       if (!setupNodeIdNumberForOlsrCacheAndValidate(nodeIdType)) {
+               retval = false;
+       }
+
+       if (updateIntervalMoving > updateIntervalStationary) {
+               pudError(false,"The update interval for moving situations must not be"
+               " larger than that for stationary situations");
+               retval = false;
+       }
+
+       return retval;
+}
+
+/**
+ Check the configuration for consistency and validity after everything has been
+ setup.
+
+ @return
+ - true when the configuration is consistent and valid
+ - false otherwise
+ */
+unsigned int checkRunSetup(void) {
+       int retval = true;
+       unsigned int i;
+
+       /* any receive interface name that is configured but is not the name of an
+        * actual receive interface is not a valid interface name */
+       for (i = 0; i < rxNonOlsrInterfaceCount; i++) {
+               unsigned char * nonOlsrInterfaceName = &rxNonOlsrInterfaceNames[i][0];
+
+               TRxTxNetworkInterface * interfaceObject = getRxNetworkInterfaces();
+               bool found = false;
+               while (interfaceObject != NULL) {
+                       if (strncmp((char *) nonOlsrInterfaceName,
+                                       (char *) &interfaceObject->name[0], IFNAMSIZ + 1) == 0) {
+                               found = true;
+                               break;
+                       }
+                       interfaceObject = interfaceObject->next;
+               }
+               if (!found) {
+                       pudError(false, "Configured receive non-OLSR interface %s is not"
+                               " a known interface name", nonOlsrInterfaceName);
+                       retval = false;
+               }
+       }
+
+       /* any transmit interface name that is configured but is not the name of an
+        * actual transmit interface is not a valid interface name */
+       for (i = 0; i < txNonOlsrInterfaceCount; i++) {
+               unsigned char * nonOlsrInterfaceName = &txNonOlsrInterfaceNames[i][0];
+
+               TRxTxNetworkInterface * interfaceObject = getTxNetworkInterfaces();
+               bool found = false;
+               while (interfaceObject != NULL) {
+                       if (strncmp((char *) nonOlsrInterfaceName,
+                                       (char *) &interfaceObject->name[0], IFNAMSIZ + 1) == 0) {
+                               found = true;
+                               break;
+                       }
+                       interfaceObject = interfaceObject->next;
+               }
+               if (!found) {
+                       pudError(false, "Configured transmit non-OLSR interface %s is not"
+                               " a known interface name", nonOlsrInterfaceName);
+                       retval = false;
+               }
+       }
+
+       return retval;
+}
diff --git a/lib/pud/src/configuration.h b/lib/pud/src/configuration.h
new file mode 100644 (file)
index 0000000..65ce115
--- /dev/null
@@ -0,0 +1,294 @@
+#ifndef _PUD_CONFIGURATION_H_
+#define _PUD_CONFIGURATION_H_
+
+/* Plugin includes */
+#include "wireFormat.h"
+
+/* OLSR includes */
+#include "olsrd_plugin.h"
+
+/* System includes */
+#include <stdbool.h>
+#include <sys/socket.h>
+
+/*
+ * Global Parameters
+ */
+
+/** The name of the nodeIdType plugin parameter */
+#define PUD_NODE_ID_TYPE_NAME                                  "nodeIdType"
+
+/** The default value of the nodeIdType plugin parameter */
+#define PUD_NODE_ID_TYPE_DEFAULT                               PUD_NODEIDTYPE_IPV4
+
+/** The maximum value of the nodeIdType plugin parameter */
+#define PUD_NODE_ID_TYPE_MAX                                   254
+
+NodeIdType getNodeIdTypeNumber(void);
+int setNodeIdType(const char *value, void *data,
+               set_plugin_parameter_addon addon);
+
+/** The name of the nodeId plugin parameter */
+#define PUD_NODE_ID_NAME                                               "nodeId"
+
+unsigned char * getNodeId(void);
+bool getNodeIdAsNumber(unsigned long long * value);
+unsigned char * getNodeIdWithLength(size_t *length);
+int setNodeId(const char *value, void *data, set_plugin_parameter_addon addon);
+
+void getNodeIdNumberForOlsrCache(unsigned char ** buffer, unsigned int * length);
+
+/*
+ * RX Parameters
+ */
+
+/** The name of the receive non-OLSR interfaces plugin parameter */
+#define PUD_RX_NON_OLSR_IF_NAME                                        "rxNonOlsrIf"
+
+bool isRxNonOlsrInterface(const char *ifName);
+int addRxNonOlsrInterface(const char *value, void *data,
+               set_plugin_parameter_addon addon);
+
+/** The name of the allowed source IP address plugin parameter */
+#define PUD_RX_ALLOWED_SOURCE_IP_NAME                  "rxAllowedSourceIpAddress"
+
+bool isRxAllowedSourceIpAddress(struct sockaddr * sender);
+int addRxAllowedSourceIpAddress(const char *value, void *data,
+               set_plugin_parameter_addon addon);
+
+/** The name of the receive multicast address plugin parameter */
+#define PUD_RX_MC_ADDR_NAME                                            "rxMcAddr"
+
+/** The default value of the receive multicast address plugin parameter for IPv4 */
+#define PUD_RX_MC_ADDR_4_DEFAULT                               "224.0.0.224"
+
+/** The default value of the receive multicast address plugin parameter for IPv6 */
+#define PUD_RX_MC_ADDR_6_DEFAULT                               "FF02:0:0:0:0:0:0:1"
+
+union olsr_sockaddr * getRxMcAddr(void);
+int
+setRxMcAddr(const char *value, void *data, set_plugin_parameter_addon addon);
+
+/** The name of the receive multicast port plugin parameter */
+#define PUD_RX_MC_PORT_NAME                                            "rxMcPort"
+
+/** The default value of the receive multicast port plugin parameter */
+#define PUD_RX_MC_PORT_DEFAULT                                 2240
+
+unsigned short getRxMcPort(void);
+int
+setRxMcPort(const char *value, void *data, set_plugin_parameter_addon addon);
+
+/*
+ * TX Parameters
+ */
+
+/** The name of the transmit non-OLSR interfaces plugin parameter */
+#define PUD_TX_NON_OLSR_IF_NAME                                        "txNonOlsrIf"
+
+bool isTxNonOlsrInterface(const char *ifName);
+int addTxNonOlsrInterface(const char *value, void *data,
+               set_plugin_parameter_addon addon);
+
+/** The name of the transmit multicast address plugin parameter */
+#define PUD_TX_MC_ADDR_NAME                                            "txMcAddr"
+
+/** The default value of the transmit multicast address plugin parameter fro IPv4*/
+#define PUD_TX_MC_ADDR_4_DEFAULT                               "224.0.0.224"
+
+/** The default value of the transmit multicast address plugin parameter for IPv6 */
+#define PUD_TX_MC_ADDR_6_DEFAULT                               "FF02:0:0:0:0:0:0:1"
+
+union olsr_sockaddr * getTxMcAddr(void);
+int
+setTxMcAddr(const char *value, void *data, set_plugin_parameter_addon addon);
+
+/** The name of the transmit multicast port plugin parameter */
+#define PUD_TX_MC_PORT_NAME                            "txMcPort"
+
+/** The default value of the transmit multicast port plugin parameter */
+#define PUD_TX_MC_PORT_DEFAULT                         2240
+
+unsigned short getTxMcPort(void);
+int
+setTxMcPort(const char *value, void *data, set_plugin_parameter_addon addon);
+
+/** The name of the transmit multicast time-to-live plugin parameter */
+#define PUD_TX_TTL_NAME                                                        "txTtl"
+
+/** The default value of the transmit multicast time-to-live plugin parameter */
+#define PUD_TX_TTL_DEFAULT                                             1
+
+unsigned char getTxTtl(void);
+int setTxTtl(const char *value, void *data, set_plugin_parameter_addon addon);
+
+/** The name of the transmit multicast NMEA message prefix plugin parameter */
+#define PUD_TX_NMEAMESSAGEPREFIX_NAME                  "txNmeaMessagePrefix"
+
+/** The default value of the transmit multicast NMEA message prefix plugin parameter */
+#define PUD_TX_NMEAMESSAGEPREFIX_DEFAULT               "NBSX"
+
+unsigned char * getTxNmeaMessagePrefix(void);
+int setTxNmeaMessagePrefix(const char *value, void *data,
+               set_plugin_parameter_addon addon);
+
+/*
+ * OLSR Parameters
+ */
+
+/** The name of the OLSR multicast time-to-live plugin parameter */
+#define PUD_OLSR_TTL_NAME                                              "olsrTtl"
+
+/** The default value of the OLSR multicast time-to-live plugin parameter */
+#define PUD_OLSR_TTL_DEFAULT                                   64
+
+unsigned char getOlsrTtl(void);
+int setOlsrTtl(const char *value, void *data, set_plugin_parameter_addon addon);
+
+/*
+ * Update Parameters
+ */
+
+/** The name of the stationary update interval plugin parameter */
+#define PUD_UPDATE_INTERVAL_STATIONARY_NAME            "updateIntervalStationary"
+
+/** The default value of the stationary update interval plugin parameter */
+#define PUD_UPDATE_INTERVAL_STATIONARY_DEFAULT 60
+
+unsigned long long getUpdateIntervalStationary(void);
+int setUpdateIntervalStationary(const char *value, void *data,
+               set_plugin_parameter_addon addon);
+
+/** The name of the moving update interval plugin parameter */
+#define PUD_UPDATE_INTERVAL_MOVING_NAME                        "updateIntervalMoving"
+
+/** The default value of the moving update interval plugin parameter */
+#define PUD_UPDATE_INTERVAL_MOVING_DEFAULT             5
+
+unsigned long long getUpdateIntervalMoving(void);
+int setUpdateIntervalMoving(const char *value, void *data,
+               set_plugin_parameter_addon addon);
+
+/** The name of the moving speed threshold plugin parameter */
+#define PUD_MOVING_SPEED_THRESHOLD_NAME                        "movingSpeedThreshold"
+
+/** The default value of the moving speed threshold plugin parameter */
+#define PUD_MOVING_SPEED_THRESHOLD_DEFAULT             5
+
+unsigned long long getMovingSpeedThreshold(void);
+int setMovingSpeedThreshold(const char *value, void *data,
+               set_plugin_parameter_addon addon);
+
+/** The name of the moving distance threshold plugin parameter */
+#define PUD_MOVING_DISTANCE_THRESHOLD_NAME             "movingDistanceThreshold"
+
+/** The default value of the moving distance threshold plugin parameter */
+#define PUD_MOVING_DISTANCE_THRESHOLD_DEFAULT  50
+
+unsigned long long getMovingDistanceThreshold(void);
+int setMovingDistanceThreshold(const char *value, void *data,
+               set_plugin_parameter_addon addon);
+
+/** The name of the DOP multiplier plugin parameter */
+#define PUD_DOP_MULTIPLIER_NAME                "dopMultiplier"
+
+/** The default value of the DOP multiplier plugin parameter */
+#define PUD_DOP_MULTIPLIER_DEFAULT     1.0
+
+double getDopMultiplier(void);
+int setDopMultiplier(const char *value, void *data,
+               set_plugin_parameter_addon addon);
+
+/** The name of the default HDOP plugin parameter */
+#define PUD_DEFAULT_HDOP_NAME          "defaultHdop"
+
+/** The default value of the default HDOP plugin parameter */
+#define PUD_DEFAULT_HDOP_DEFAULT       50
+
+unsigned long long getDefaultHdop(void);
+int setDefaultHdop(const char *value, void *data,
+               set_plugin_parameter_addon addon);
+
+/** The name of the default VDOP plugin parameter */
+#define PUD_DEFAULT_VDOP_NAME          "defaultVdop"
+
+/** The default value of the default VDOP plugin parameter */
+#define PUD_DEFAULT_VDOP_DEFAULT       50
+
+unsigned long long getDefaultVdop(void);
+int setDefaultVdop(const char *value, void *data,
+               set_plugin_parameter_addon addon);
+
+/** The name of the average depth plugin parameter */
+#define PUD_AVERAGE_DEPTH_NAME                                 "averageDepth"
+
+/** The default value of the average depth plugin parameter */
+#define PUD_AVERAGE_DEPTH_DEFAULT                              5
+
+unsigned long long getAverageDepth(void);
+int setAverageDepth(const char *value, void *data,
+               set_plugin_parameter_addon addon);
+
+/** The name of the hysteresis count to stationary plugin parameter */
+#define PUD_HYSTERESIS_COUNT_2STAT_NAME                        "hysteresisCountToStationary"
+
+/** The default value of the hysteresis count to stationary plugin parameter */
+#define PUD_HYSTERESIS_COUNT_2STAT_DEFAULT             17
+
+unsigned long long getHysteresisCountToStationary(void);
+int setHysteresisCountToStationary(const char *value, void *data,
+               set_plugin_parameter_addon addon);
+
+/** The name of the hysteresis count to moving plugin parameter */
+#define PUD_HYSTERESIS_COUNT_2MOV_NAME                 "hysteresisCountToMoving"
+
+/** The default value of the hysteresis count to moving plugin parameter */
+#define PUD_HYSTERESIS_COUNT_2MOV_DEFAULT              5
+
+unsigned long long getHysteresisCountToMoving(void);
+int setHysteresisCountToMoving(const char *value, void *data,
+               set_plugin_parameter_addon addon);
+
+/*
+ * Other Plugin Settings
+ */
+
+/** The name of the deduplication usage plugin parameter */
+#define PUD_USE_DEDUP_NAME                                             "useDeDup"
+
+/** The default value of the deduplication usage plugin parameter */
+#define PUD_USE_DEDUP_DEFAULT                                  true
+
+bool getUseDeDup(void);
+int
+setUseDeDup(const char *value, void *data, set_plugin_parameter_addon addon);
+
+/** The name of the deduplication depth plugin parameter */
+#define PUD_DEDUP_DEPTH_NAME                                   "deDupDepth"
+
+/** The default value of the deduplication depth plugin parameter */
+#define PUD_DEDUP_DEPTH_DEFAULT                                        256
+
+unsigned long long getDeDupDepth(void);
+int
+setDeDupDepth(const char *value, void *data, set_plugin_parameter_addon addon);
+
+/** The name of the loopback usage plugin parameter */
+#define PUD_USE_LOOPBACK_NAME                                  "useLoopback"
+
+/** The default value of the loopback usage plugin parameter */
+#define PUD_USE_LOOPBACK_DEFAULT                               false
+
+bool getUseLoopback(void);
+int
+setUseLoopback(const char *value, void *data, set_plugin_parameter_addon addon);
+
+/*
+ * Check Functions
+ */
+
+unsigned int checkConfig(void);
+
+unsigned int checkRunSetup(void);
+
+#endif /* _PUD_CONFIGURATION_H_ */
diff --git a/lib/pud/src/dedup.c b/lib/pud/src/dedup.c
new file mode 100644 (file)
index 0000000..38cb3b6
--- /dev/null
@@ -0,0 +1,228 @@
+#include "dedup.h"
+
+/* Plugin includes */
+
+/* OLSR includes */
+#include "olsr.h"
+
+/* System includes */
+#include <assert.h>
+
+#ifdef PUD_DUMP_DEDUP
+#include <arpa/inet.h>
+#endif
+
+/* Defines */
+
+#define LISTSIZE(x)                    ((x)->entriesMaxCount) /* always valid */
+#define NEWESTINDEX(x)         ((x)->newestEntryIndex) /* always valid */
+#define WRAPINDEX(x, i)                ((i) % LISTSIZE(x)) /* always valid for i>=0 */
+#define INCOMINGINDEX(x)       WRAPINDEX(x, (NEWESTINDEX(x) + LISTSIZE(x) - 1)) /* always valid */
+
+/**
+ Initialise the de-duplication list: allocate memory for the entries and
+ reset fields.
+
+ @param deDupList
+ The de-duplication list
+ @param maxEntries
+ The maximum number of entries in the list (the number of messages that should
+ be tracked)
+
+ @return
+ - false on failure
+ - true otherwise
+ */
+bool initDeDupList(DeDupList * deDupList, unsigned long long maxEntries) {
+       void * p;
+
+       if (deDupList == NULL) {
+               return false;
+       }
+       if (maxEntries < 1) {
+               return false;
+       }
+
+       p = olsr_malloc(maxEntries * sizeof(DeDupEntry),
+                       "DeDupEntry entries for DeDupList (PUD)");
+       if (p == NULL) {
+               return false;
+       }
+
+       deDupList->entriesMaxCount = maxEntries;
+       deDupList->entries = p;
+
+       deDupList->entriesCount = 0;
+       deDupList->newestEntryIndex = 0;
+
+       return true;
+}
+
+/**
+ Clean up the de-duplication list: free memory and reset fields.
+
+ @param deDupList
+ The de-duplication list
+ */
+void destroyDeDupList(DeDupList * deDupList) {
+       assert (deDupList != NULL);
+
+       if (deDupList->entries != NULL) {
+               free(deDupList->entries);
+               deDupList->entries = NULL;
+       }
+
+       deDupList->entriesMaxCount = 0;
+
+       deDupList->entriesCount = 0;
+       deDupList->newestEntryIndex = 0;
+}
+
+/**
+ Add a new (incoming) message to the de-duplication list
+
+ @param deDupList
+ The de-duplication list
+ @param olsrMessage
+ The message
+ */
+void addToDeDup(DeDupList * deDupList, union olsr_message *olsrMessage) {
+       unsigned long long incomingIndex;
+       DeDupEntry * newEntry;
+
+       assert (deDupList != NULL);
+
+       incomingIndex = INCOMINGINDEX(deDupList);
+       newEntry = &deDupList->entries[incomingIndex];
+
+#ifdef PUD_DUMP_DEDUP
+       olsr_printf(0, "addToDeDup: entriesCount=%llu, newestEntryIndex=%llu,"
+                       " incomingIndex=%llu (before)\n", deDupList->entriesCount,
+                       deDupList->newestEntryIndex, INCOMINGINDEX(deDupList));
+#endif
+
+       memset(newEntry, 0, sizeof(DeDupEntry));
+       if (olsr_cnf->ip_version == AF_INET) {
+               newEntry->seqno = olsrMessage->v4.seqno;
+               memcpy(&newEntry->originator.v4, &olsrMessage->v4.originator,
+                               sizeof(newEntry->originator.v4));
+       } else {
+               newEntry->seqno = olsrMessage->v6.seqno;
+               memcpy(&newEntry->originator.v6, &olsrMessage->v6.originator,
+                               sizeof(newEntry->originator.v6));
+       }
+
+       deDupList->newestEntryIndex = incomingIndex;
+       if (deDupList->entriesCount < deDupList->entriesMaxCount) {
+               deDupList ->entriesCount++;
+       }
+
+#ifdef PUD_DUMP_DEDUP
+       {
+               char addr[64];
+               olsr_printf(0, "addToDeDup: added seqno %u from %s\n", newEntry->seqno,
+                               inet_ntop(olsr_cnf->ip_version,
+                                               &newEntry->originator,
+                                               &addr[0],sizeof(addr)));
+               olsr_printf(0, "addToDeDup: entriesCount=%llu, newestEntryIndex=%llu,"
+                               " incomingIndex=%llu (after)\n\n", deDupList->entriesCount,
+                               deDupList->newestEntryIndex, INCOMINGINDEX(deDupList));
+       }
+#endif
+}
+
+/**
+ Determines whether a new (incoming) message is already in the de-duplication
+ list
+
+ @param deDupList
+ The de-duplication list
+ @param olsrMessage
+ The message
+
+ @return
+ - true when the message is already in the list
+ - false otherwise
+ */
+bool isInDeDupList(DeDupList * deDupList, union olsr_message *olsrMessage) {
+       bool retval = false;
+       unsigned long long iteratedIndex = NEWESTINDEX(deDupList);
+       unsigned long long count = deDupList->entriesCount;
+
+#ifdef PUD_DUMP_DEDUP
+       olsr_printf(0, "isInDeDupList: count=%llu, iteratedIndex=%llu"
+                       " maxCount=%llu (iteration start)\n", count, iteratedIndex,
+                       deDupList->entriesMaxCount);
+#endif
+
+       /* we iterate from newest until oldest: we have a higher probability to
+        * match on the newest entries */
+
+       while (count > 0) {
+               DeDupEntry * iteratedEntry = &deDupList->entries[iteratedIndex];
+
+#ifdef PUD_DUMP_DEDUP
+               olsr_printf(0, "isInDeDupList: count=%llu, iteratedIndex=%llu"
+                               " (iteration)\n", count, iteratedIndex);
+#endif
+
+               if (olsr_cnf->ip_version == AF_INET) {
+#ifdef PUD_DUMP_DEDUP
+                       {
+                               char iteratedAddr[64];
+                               char olsrMessageAddr[64];
+
+                               olsr_printf(0, "isInDeDupList: iterated.seqno %u ==?"
+                                               " olsrMessage.seqno %u\n", iteratedEntry->seqno,
+                                               olsrMessage->v4.seqno);
+                               olsr_printf(0, "isInDeDupList: iterated.addr %s ==?"
+                                               " olsrMessage.addr %s\n", inet_ntop(olsr_cnf->ip_version,
+                                                               &iteratedEntry->originator.v4,
+                                                               &iteratedAddr[0],sizeof(iteratedAddr)),
+                                                               inet_ntop(olsr_cnf->ip_version,
+                                                               &olsrMessage->v4.originator,
+                                                               &olsrMessageAddr[0],sizeof(olsrMessageAddr)));
+                       }
+#endif
+                       if ((iteratedEntry->seqno == olsrMessage->v4.seqno) && (memcmp(
+                                       &iteratedEntry->originator.v4, &olsrMessage->v4.originator,
+                                       sizeof(iteratedEntry->originator.v4))) == 0) {
+                               retval = true;
+                               break;
+                       }
+               } else {
+#ifdef PUD_DUMP_DEDUP
+                       {
+                               char iteratedAddr[64];
+                               char olsrMessageAddr[64];
+
+                               olsr_printf(0, "isInDeDupList: iterated.seqno %u ==?"
+                                               " olsrMessage.seqno %u\n", iteratedEntry->seqno,
+                                               olsrMessage->v6.seqno);
+                               olsr_printf(0, "isInDeDupList: iterated.addr %s ==?"
+                                               " olsrMessage.addr %s\n", inet_ntop(olsr_cnf->ip_version,
+                                                               &iteratedEntry->originator.v6,
+                                                               &iteratedAddr[0],sizeof(iteratedAddr)),
+                                                               inet_ntop(olsr_cnf->ip_version,
+                                                               &olsrMessage->v6.originator,
+                                                               &olsrMessageAddr[0],sizeof(olsrMessageAddr)));
+                       }
+#endif
+                       if ((iteratedEntry->seqno == olsrMessage->v6.seqno) && (memcmp(
+                                       &iteratedEntry->originator.v6, &olsrMessage->v6.originator,
+                                       sizeof(iteratedEntry->originator.v6)) == 0)) {
+                               retval = true;
+                               break;
+                       }
+               }
+
+               iteratedIndex = WRAPINDEX(deDupList, iteratedIndex + 1); /* go the the next older entry */
+               count--;
+       }
+
+#ifdef PUD_DUMP_DEDUP
+       olsr_printf(0,"isInDeDupList: result = %s\n\n", retval ? "true" : "false");
+#endif
+
+       return retval;
+}
diff --git a/lib/pud/src/dedup.h b/lib/pud/src/dedup.h
new file mode 100644 (file)
index 0000000..b37ede1
--- /dev/null
@@ -0,0 +1,41 @@
+#ifndef _PUD_DEDUP_H_
+#define _PUD_DEDUP_H_
+
+/* Plugin includes */
+
+/* OLSR includes */
+#include "olsr_types.h"
+#include "olsr_protocol.h"
+
+/* System includes */
+#include <stdint.h>
+#include <stdbool.h>
+
+/* A de-duplication entry holding the information to compare, 18 bytes */
+typedef struct _DeDupEntry {
+               uint16_t seqno;
+               union olsr_ip_addr originator;
+} DeDupEntry;
+
+/**
+ A list of de-duplication entries that are used to determine whether a received
+ OLSR message was already seen.
+
+ The list is a circular list.
+ */
+typedef struct _DeDupList {
+               unsigned long long entriesMaxCount; /**< the maximum number of entries in the list */
+               DeDupEntry * entries; /**< the list entries */
+
+               unsigned long long entriesCount; /**< the number of entries in the list */
+               unsigned long long newestEntryIndex; /**< index of the newest entry in the list (zero-based) */
+} DeDupList;
+
+bool initDeDupList(DeDupList * deDupList, unsigned long long maxEntries);
+void destroyDeDupList(DeDupList * deDupList);
+
+void addToDeDup(DeDupList * deDupList, union olsr_message *olsrMessage);
+
+bool isInDeDupList(DeDupList * deDupList, union olsr_message *olsrMessage);
+
+#endif /* _PUD_DEDUP_H_ */
diff --git a/lib/pud/src/dump.c b/lib/pud/src/dump.c
new file mode 100644 (file)
index 0000000..1724145
--- /dev/null
@@ -0,0 +1,108 @@
+#include "dump.h"
+
+#ifdef PUD_DUMP_GPS_PACKETS
+
+/* Plugin includes */
+#include "pud.h"
+
+/* OLSRD includes */
+#include "olsr.h"
+
+/* System includes */
+#include <string.h>
+
+/** the number of bytes/characters per line */
+#define PUD_DUMP_GPS_PACKETS_CHARSPERLINE 16
+
+/**
+ Prints a packet in hex/ascii.
+
+ @param packet
+ a pointer to the packet
+ @param length
+ the number of bytes in the packet
+ */
+void dump_packet(unsigned char* packet, unsigned int length) {
+       unsigned int packetPos;
+       unsigned int linePos;
+       unsigned char line[PUD_DUMP_GPS_PACKETS_CHARSPERLINE + 1];
+
+       for (packetPos = 0; packetPos < length; packetPos
+                       += PUD_DUMP_GPS_PACKETS_CHARSPERLINE) {
+               unsigned int copyLength = PUD_DUMP_GPS_PACKETS_CHARSPERLINE;
+               if ((packetPos + copyLength) > length) {
+                       copyLength = length - packetPos;
+               }
+
+               memcpy(&line[0], &packet[packetPos], copyLength);
+               line[copyLength] = '\0';
+
+               olsr_printf(0, "%s: ", PUD_PLUGIN_ABBR);
+               for (linePos = 0; linePos < PUD_DUMP_GPS_PACKETS_CHARSPERLINE; linePos++) {
+                       if (linePos >= copyLength) {
+                               olsr_printf(0, "   ");
+                       } else {
+                               olsr_printf(0, "%2.2X ", packet[packetPos + linePos]);
+                               if ((line[linePos] < 32) || (line[linePos] > 126)) {
+                                       line[linePos] = '.';
+                               }
+                       }
+               }
+               olsr_printf(0, " %s\n", line);
+       }
+}
+
+#endif /* PUD_DUMP_GPS_PACKETS */
+
+#ifdef PUD_DUMP_NMEA
+
+/* Plugin includes */
+
+/* OLSRD includes */
+#include "olsr.h"
+
+/* System includes */
+#include <nmea/info.h>
+#include <nmea/sentence.h>
+
+void dump_nmeaInfo(nmeaINFO * nmeaInfo, const char * prefix) {
+       olsr_printf(0,
+                       "%s (%p)\n" /* prefix */
+                       "  smask = %02x%s%s%s%s%s\n"
+                       "  utc   = %04u%02u%02u %02u:%02u:%02u.%02u\n"
+                       "  sig   = %s (%u)\n"
+                       "  fix   = %s (%u)\n"
+                       "  PDOP  = %fm\n"
+                       "  HDOP  = %fm\n"
+                       "  VDOP  = %fm\n"
+                       "  lat   = %f\n"
+                       "  lon   = %f\n"
+                       "  alt   = %f\n"
+                       "  speed = %f\n"
+                       "  track = %f\n"
+                       "  decl  = %f\n"
+                       "\n",
+                       prefix, nmeaInfo,
+                       nmeaInfo->smask,
+                       ((nmeaInfo->smask & GPGGA) != 0) ? " GPGGA" : "",
+                       ((nmeaInfo->smask & GPGSA) != 0) ? " GPGSA" : "",
+                       ((nmeaInfo->smask & GPGSV) != 0) ? " GPGSV" : "",
+                       ((nmeaInfo->smask & GPRMC) != 0) ? " GPRMC" : "",
+                       ((nmeaInfo->smask & GPVTG) != 0) ? " GPVTG" : "",
+                       (nmeaInfo->utc.year + 1900), nmeaInfo->utc.mon, nmeaInfo->utc.day,
+                       nmeaInfo->utc.hour, nmeaInfo->utc.min, nmeaInfo->utc.sec, nmeaInfo->utc.hsec,
+                       (nmeaInfo->sig == NMEA_SIG_BAD) ? "BAD" : (nmeaInfo->sig == NMEA_SIG_LOW) ? "LOW" : (nmeaInfo->sig == NMEA_SIG_MID) ? "MID" : (nmeaInfo->sig == NMEA_SIG_HIGH) ? "HIGH" : "UNKNOWN", nmeaInfo->sig,
+                       (nmeaInfo->fix == NMEA_FIX_BAD) ? "BAD" : (nmeaInfo->fix == NMEA_FIX_2D) ? "2D" : (nmeaInfo->fix == NMEA_FIX_3D) ? "3D" : "UNKNOWN", nmeaInfo->fix,
+                       nmeaInfo->PDOP,
+                       nmeaInfo->HDOP,
+                       nmeaInfo->VDOP,
+                       nmeaInfo->lat,
+                       nmeaInfo->lon,
+                       nmeaInfo->elv,
+                       nmeaInfo->speed,
+                       nmeaInfo->direction,
+                       nmeaInfo->declination
+       );
+}
+
+#endif /* PUD_DUMP_NMEA */
diff --git a/lib/pud/src/dump.h b/lib/pud/src/dump.h
new file mode 100644 (file)
index 0000000..126959b
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef _PUD_DUMP_H_
+#define _PUD_DUMP_H_
+
+#if defined(PUD_DUMP_GPS_PACKETS_RX_NON_OLSR) || \
+       defined(PUD_DUMP_GPS_PACKETS_RX_OLSR) || \
+       defined(PUD_DUMP_GPS_PACKETS_TX_OLSR) || \
+       defined(PUD_DUMP_GPS_PACKETS_TX_NON_OLSR) || \
+       defined(PUD_DUMP_GPS_PACKETS)
+
+#ifndef PUD_DUMP_GPS_PACKETS
+#define PUD_DUMP_GPS_PACKETS
+#endif
+
+void dump_packet(unsigned char* packet, unsigned int length);
+
+#endif /* PUD_DUMP_GPS_PACKETS_* */
+
+
+#if defined(PUD_DUMP_AVERAGING) || \
+       defined(PUD_DUMP_NMEA)
+
+#ifndef PUD_DUMP_NMEA
+#define PUD_DUMP_NMEA
+#endif
+
+#include <nmea/info.h>
+
+void dump_nmeaInfo(nmeaINFO * nmeaInfo, const char * prefix);
+
+#endif /* PUD_DUMP_AVERAGING */
+
+#endif /* _PUD_DUMP_H_ */
diff --git a/lib/pud/src/gpsConversion.c b/lib/pud/src/gpsConversion.c
new file mode 100644 (file)
index 0000000..c2c57cd
--- /dev/null
@@ -0,0 +1,427 @@
+#include "gpsConversion.h"
+
+/* Plugin includes */
+#include "wireFormat.h"
+#include "pud.h"
+#include "nodeIdConversion.h"
+#include "configuration.h"
+#include "compiler.h"
+
+/* OLSR includes */
+#include "olsr.h"
+
+/* System includes */
+#include <nmea/gmath.h>
+#include <nmea/tok.h>
+#include <nmea/info.h>
+#include <netinet/in.h>
+#include <stdio.h>
+
+/* ************************************************************************
+ * OLSR --> External
+ * ************************************************************************ */
+
+/**
+ Convert an OLSR message into a string to multicast on the LAN
+
+ @param olsrMessage
+ A pointer to the OLSR message
+ @param txGpsBuffer
+ A pointer to the buffer in which the transmit string can be written
+ @param txGpsBufferSize
+ The size of the txGpsBuffer
+
+ @return
+ - the length of the transmit string placed in the txGpsBuffer
+ - 0 (zero) in case of an error
+ */
+unsigned int gpsFromOlsr(union olsr_message *olsrMessage,
+               unsigned char * txGpsBuffer, unsigned int txGpsBufferSize) {
+       unsigned long validityTime;
+
+       struct tm timeStruct;
+       char latitudeString[PUD_TX_LATITUDE_DIGITS + 1];
+       const char * latitudeHemisphere;
+       char longitudeString[PUD_TX_LONGITUDE_DIGITS + 1];
+       const char * longitudeHemisphere;
+       char altitudeString[PUD_TX_ALTITUDE_DIGITS + 1];
+       char speedString[PUD_TX_SPEED_DIGITS + 1];
+       char trackString[PUD_TX_TRACK_DIGITS + 1];
+       char hdopString[PUD_TX_HDOP_DIGITS + 1];
+
+       char nodeIdTypeString[PUD_TX_NODEIDTYPE_DIGITS + 1];
+       char nodeIdString[PUD_TX_NODEID_BUFFERSIZE + 1];
+       const char * nodeId;
+
+       unsigned int transmitStringLength;
+
+       GpsInfo* gpsMessage;
+       PudOlsrWireFormat * olsrGpsMessage =
+                       getOlsrMessagePayload(olsr_cnf->ip_version, olsrMessage);
+
+       if (unlikely(olsrGpsMessage->version != PUD_WIRE_FORMAT_VERSION)) {
+               /* currently we can only handle our own version */
+               pudError(false, "Can not handle version %u OLSR PUD messages"
+                       " (only version %u): message ignored", olsrGpsMessage->version,
+                               PUD_WIRE_FORMAT_VERSION);
+               return 0;
+       }
+
+       validityTime = getValidityTimeFromOlsr(olsrGpsMessage->validityTime);
+
+       gpsMessage = &olsrGpsMessage->gpsInfo;
+
+       /* time is ALWAYS present so we can just use it */
+       getTimeFromOlsr(gpsMessage->time, &timeStruct);
+
+       if (likely(nmea_INFO_has_field(olsrGpsMessage->smask, LAT))) {
+               int chars;
+               double latitude = getLatitudeFromOlsr(gpsMessage->lat);
+
+               if (latitude >= 0) {
+                       latitudeHemisphere = "N";
+               } else {
+                       latitudeHemisphere = "S";
+                       latitude = -latitude;
+               }
+               latitude = nmea_degree2ndeg(latitude);
+
+               chars = snprintf(&latitudeString[0], PUD_TX_LATITUDE_DIGITS,
+                               "%." PUD_TX_LATITUDE_DECIMALS "f", latitude);
+               if (likely(chars < PUD_TX_LATITUDE_DIGITS)) {
+                       latitudeString[chars] = '\0';
+               } else {
+                       latitudeString[PUD_TX_LATITUDE_DIGITS] = '\0';
+               }
+       } else {
+               latitudeHemisphere = "";
+               latitudeString[0] = '\0';
+       }
+
+       if (likely(nmea_INFO_has_field(olsrGpsMessage->smask, LON))) {
+               int chars;
+               double longitude = getLongitudeFromOlsr(gpsMessage->lon);
+
+               if (longitude >= 0) {
+                       longitudeHemisphere = "E";
+               } else {
+                       longitudeHemisphere = "W";
+                       longitude = -longitude;
+               }
+               longitude = nmea_degree2ndeg(longitude);
+
+               chars = snprintf(&longitudeString[0], PUD_TX_LONGITUDE_DIGITS,
+                               "%." PUD_TX_LONGITUDE_DECIMALS "f", longitude);
+               if (likely(chars < PUD_TX_LONGITUDE_DIGITS)) {
+                       longitudeString[chars] = '\0';
+               } else {
+                       longitudeString[PUD_TX_LONGITUDE_DIGITS] = '\0';
+               }
+       } else {
+               longitudeHemisphere = "";
+               longitudeString[0] = '\0';
+       }
+
+       if (likely(nmea_INFO_has_field(olsrGpsMessage->smask, ELV))) {
+               int chars = snprintf(&altitudeString[0], PUD_TX_ALTITUDE_DIGITS, "%ld",
+                               getAltitudeFromOlsr(gpsMessage->alt));
+               if (likely(chars < PUD_TX_ALTITUDE_DIGITS)) {
+                       altitudeString[chars] = '\0';
+               } else {
+                       altitudeString[PUD_TX_ALTITUDE_DIGITS] = '\0';
+               }
+       } else {
+               altitudeString[0] = '\0';
+       }
+
+       if (likely(nmea_INFO_has_field(olsrGpsMessage->smask, SPEED))) {
+               int chars = snprintf(&speedString[0], PUD_TX_SPEED_DIGITS, "%lu",
+                               getSpeedFromOlsr(gpsMessage->speed));
+               if (likely(chars < PUD_TX_SPEED_DIGITS)) {
+                       speedString[chars] = '\0';
+               } else {
+                       speedString[PUD_TX_SPEED_DIGITS] = '\0';
+               }
+       } else {
+               speedString[0] = '\0';
+       }
+
+       if (likely(nmea_INFO_has_field(olsrGpsMessage->smask, DIRECTION))) {
+               int chars = snprintf(&trackString[0], PUD_TX_TRACK_DIGITS, "%lu",
+                               getTrackFromOlsr(gpsMessage->track));
+               if (likely(chars < PUD_TX_TRACK_DIGITS)) {
+                       trackString[chars] = '\0';
+               } else {
+                       trackString[PUD_TX_TRACK_DIGITS] = '\0';
+               }
+       } else {
+               trackString[0] = '\0';
+       }
+
+       if (likely(nmea_INFO_has_field(olsrGpsMessage->smask, HDOP))) {
+               int chars = snprintf(&hdopString[0], PUD_TX_HDOP_DIGITS,
+                               "%." PUD_TX_HDOP_DECIMALS "f", nmea_meters2dop(getHdopFromOlsr(
+                                               gpsMessage->hdop)));
+               if (likely(chars < PUD_TX_HDOP_DIGITS)) {
+                       hdopString[chars] = '\0';
+               } else {
+                       hdopString[PUD_TX_HDOP_DIGITS] = '\0';
+               }
+       } else {
+               hdopString[0] = '\0';
+       }
+
+       getNodeTypeStringFromOlsr(olsr_cnf->ip_version, olsrMessage,
+                       &nodeIdTypeString[0], sizeof(nodeIdTypeString));
+       getNodeIdStringFromOlsr(olsr_cnf->ip_version, olsrMessage, &nodeId,
+                       &nodeIdString[0], sizeof(nodeIdString));
+
+       transmitStringLength = nmea_printf((char *) txGpsBuffer, txGpsBufferSize
+                       - 1, "$P%s," /* prefix (always) */
+               "%u," /* sentence version (always) */
+               "%s,%s," /* nodeIdType/nodeId (always) */
+               "%02u%02u%02u," /* date (always) */
+               "%02u%02u%02u," /* time (always) */
+               "%lu," /* validity time (always) */
+               "%s,%s," /* latitude (optional) */
+               "%s,%s," /* longitude (optional) */
+               "%s," /* altitude (optional) */
+               "%s," /* speed (optional) */
+               "%s," /* track (optional) */
+               "%s" /* hdop (optional) */
+       , getTxNmeaMessagePrefix(), PUD_TX_SENTENCE_VERSION, &nodeIdTypeString[0],
+                       nodeId, timeStruct.tm_mday, timeStruct.tm_mon, (timeStruct.tm_year
+                                       % 100), timeStruct.tm_hour, timeStruct.tm_min,
+                       timeStruct.tm_sec, validityTime, &latitudeString[0],
+                       latitudeHemisphere, &longitudeString[0], longitudeHemisphere,
+                       &altitudeString[0], &speedString[0], &trackString[0],
+                       &hdopString[0]);
+
+       if (unlikely(transmitStringLength > (txGpsBufferSize - 1))) {
+               pudError(false, "String to transmit on non-OLSR is too large, need"
+                       " at least %u bytes, skipped", transmitStringLength);
+               return 0;
+       }
+
+       if (unlikely(transmitStringLength == (txGpsBufferSize - 1))) {
+               txGpsBuffer[txGpsBufferSize - 1] = '\0';
+       } else {
+               txGpsBuffer[transmitStringLength] = '\0';
+       }
+
+       return transmitStringLength;
+}
+
+/* ************************************************************************
+ * External --> OLSR
+ * ************************************************************************ */
+
+/**
+ Convert the node information to the node information for an OLSR message and
+ put it in the PUD message in the OLSR message. Also updates the PUD message
+ smask.
+
+ @param olsrGpsMessage
+ A pointer to the PUD message in the OLSR message
+ @param olsrMessageSize
+ The maximum number of bytes available for the olsrMessage
+ @param nodeIdType
+ The nodeIdType
+
+ @return
+ The number of bytes written in the PUD message in the OLSR message (for ALL
+ the node information)
+ */
+static size_t setupNodeInfoForOlsr(PudOlsrWireFormat * olsrGpsMessage,
+               unsigned int olsrMessageSize, NodeIdType nodeIdType) {
+       unsigned char * buffer;
+       unsigned int length = 0;
+
+       olsrGpsMessage->nodeInfo.nodeIdType = nodeIdType;
+       switch (nodeIdType) {
+               case PUD_NODEIDTYPE_MAC: /* hardware address */
+                       /* handled when the message is actually sent into OLSR, in the
+                        * pre-transmit hook */
+                       length = PUD_NODEIDTYPE_MAC_BYTES;
+                       break;
+
+               case PUD_NODEIDTYPE_MSISDN: /* an MSISDN number */
+               case PUD_NODEIDTYPE_TETRA: /* a Tetra number */
+               case PUD_NODEIDTYPE_192:
+               case PUD_NODEIDTYPE_193:
+               case PUD_NODEIDTYPE_194:
+                       getNodeIdNumberForOlsrCache(&buffer, &length);
+                       memcpy(&olsrGpsMessage->nodeInfo.nodeId, buffer, length);
+                       break;
+
+               case PUD_NODEIDTYPE_DNS: /* DNS name */
+               {
+                       size_t nodeIdLength;
+                       unsigned char * nodeId = getNodeIdWithLength(&nodeIdLength);
+                       long charsAvailable = olsrMessageSize - (PUD_OLSRWIREFORMATSIZE
+                                       + sizeof(NodeInfo)
+                                       - sizeof(olsrGpsMessage->nodeInfo.nodeId)) - 1;
+
+                       length = nodeIdLength + 1;
+                       if (unlikely((long) length > charsAvailable)) {
+                               length = charsAvailable;
+                       }
+
+                       memcpy(&olsrGpsMessage->nodeInfo.nodeId, nodeId, length);
+                       (&olsrGpsMessage->nodeInfo.nodeId)[length] = '\0';
+               }
+                       break;
+
+               case PUD_NODEIDTYPE_IPV4: /* IPv4 address */
+               case PUD_NODEIDTYPE_IPV6: /* IPv6 address */
+                       /* explicit return: no nodeId information in message */
+                       return 0;
+
+               default: /* unsupported */
+                       /* fallback to IP address */
+                       olsrGpsMessage->nodeInfo.nodeIdType = (olsr_cnf->ip_version
+                                       == AF_INET) ? PUD_NODEIDTYPE_IPV4 : PUD_NODEIDTYPE_IPV6;
+
+                       /* explicit return: no nodeId information in message */
+                       return 0;
+       }
+
+       olsrGpsMessage->smask |= PUD_FLAGS_ID;
+       return ((sizeof(NodeInfo)
+                       - (sizeof(olsrGpsMessage->nodeInfo.nodeId) /* nodeId placeholder */))
+                       + length);
+}
+
+/**
+ Convert a nmeaINFO structure into an OLSR message.
+
+ @param nmeaInfo
+ A pointer to a nmeaINFO structure
+ @param olsrMessage
+ A pointer to an OLSR message in which to place the converted information
+ @param olsrMessageSize
+ The maximum number of bytes available for the olsrMessage
+ @param validityTime
+ the validity time of the message
+
+ @return
+ - the aligned size of the converted information
+ - 0 (zero) in case of an error
+ */
+unsigned int gpsToOlsr(nmeaINFO *nmeaInfo, union olsr_message *olsrMessage,
+               unsigned int olsrMessageSize, unsigned long long validityTime) {
+       unsigned int aligned_size;
+       unsigned int aligned_size_remainder;
+       size_t nodeLength;
+       PudOlsrWireFormat * olsrGpsMessage =
+                       getOlsrMessagePayload(olsr_cnf->ip_version, olsrMessage);
+
+       /*
+        * Compose message contents
+        */
+
+       olsrGpsMessage->version = PUD_WIRE_FORMAT_VERSION;
+       olsrGpsMessage->validityTime = getValidityTimeForOlsr(validityTime);
+       olsrGpsMessage->smask = nmeaInfo->smask;
+
+       /* utc is always present, we make sure of that, so just use it */
+       olsrGpsMessage->gpsInfo.time = getTimeForOlsr(nmeaInfo->utc.hour,
+                       nmeaInfo->utc.min, nmeaInfo->utc.sec);
+
+       if (likely(nmea_INFO_has_field(nmeaInfo->smask, LAT))) {
+               olsrGpsMessage->gpsInfo.lat = getLatitudeForOlsr(nmeaInfo->lat);
+       } else {
+               olsrGpsMessage->gpsInfo.lat = (1 << (PUD_LATITUDE_BITS - 1));
+       }
+
+       if (likely(nmea_INFO_has_field(nmeaInfo->smask, LON))) {
+               olsrGpsMessage->gpsInfo.lon = getLongitudeForOlsr(nmeaInfo->lon);
+       } else {
+               olsrGpsMessage->gpsInfo.lon = (1 << (PUD_LONGITUDE_BITS - 1));
+       }
+
+       if (likely(nmea_INFO_has_field(nmeaInfo->smask, ELV))) {
+               olsrGpsMessage->gpsInfo.alt = getAltitudeForOlsr(nmeaInfo->elv);
+       } else {
+               olsrGpsMessage->gpsInfo.alt = -PUD_ALTITUDE_MIN;
+       }
+
+       if (likely(nmea_INFO_has_field(nmeaInfo->smask, SPEED))) {
+               olsrGpsMessage->gpsInfo.speed = getSpeedForOlsr(nmeaInfo->speed);
+       } else {
+               olsrGpsMessage->gpsInfo.speed = 0;
+       }
+
+       if (likely(nmea_INFO_has_field(nmeaInfo->smask, DIRECTION))) {
+               olsrGpsMessage->gpsInfo.track = getTrackForOlsr(nmeaInfo->direction);
+       } else {
+               olsrGpsMessage->gpsInfo.track = 0;
+       }
+
+       if (likely(nmea_INFO_has_field(nmeaInfo->smask, HDOP))) {
+               olsrGpsMessage->gpsInfo.hdop = getHdopForOlsr(nmeaInfo->HDOP);
+       } else {
+               olsrGpsMessage->gpsInfo.hdop = PUD_HDOP_MAX;
+       }
+
+       nodeLength = setupNodeInfoForOlsr(olsrGpsMessage, olsrMessageSize,
+                       getNodeIdTypeNumber());
+
+       /*
+        * Messages in OLSR are 4-byte aligned: align
+        */
+
+       /* size = type, string, string terminator */
+       aligned_size = PUD_OLSRWIREFORMATSIZE + nodeLength;
+       aligned_size_remainder = (aligned_size % 4);
+       if (aligned_size_remainder != 0) {
+               aligned_size += (4 - aligned_size_remainder);
+       }
+
+       /*
+        * Fill message headers (fill ALL fields, except message)
+        * Note: olsr_vtime is currently unused, we use it for our validity time.
+        */
+
+       if (olsr_cnf->ip_version == AF_INET) {
+               /* IPv4 */
+
+               olsrMessage->v4.olsr_msgtype = PUD_OLSR_MSG_TYPE;
+               olsrMessage->v4.olsr_vtime = reltime_to_me(validityTime * 1000);
+               /* message->v4.olsr_msgsize at the end */
+               memcpy(&olsrMessage->v4.originator, &olsr_cnf->main_addr,
+                               olsr_cnf->ipsize);
+               olsrMessage->v4.ttl = getOlsrTtl();
+               olsrMessage->v4.hopcnt = 0;
+               olsrMessage->v4.seqno = htons(get_msg_seqno());
+
+               /* add length of message->v4 fields */
+               aligned_size += (sizeof(olsrMessage->v4)
+                               - sizeof(olsrMessage->v4.message));
+               olsrMessage->v4.olsr_msgsize = htons(aligned_size);
+       } else {
+               /* IPv6 */
+
+               olsrMessage->v6.olsr_msgtype = PUD_OLSR_MSG_TYPE;
+               olsrMessage->v6.olsr_vtime = reltime_to_me(validityTime * 1000);
+               /* message->v6.olsr_msgsize at the end */
+               memcpy(&olsrMessage->v6.originator, &olsr_cnf->main_addr,
+                               olsr_cnf->ipsize);
+               olsrMessage->v6.ttl = getOlsrTtl();
+               olsrMessage->v6.hopcnt = 0;
+               olsrMessage->v6.seqno = htons(get_msg_seqno());
+
+               /* add length of message->v6 fields */
+               aligned_size += (sizeof(olsrMessage->v6)
+                               - sizeof(olsrMessage->v6.message));
+               olsrMessage->v6.olsr_msgsize = htons(aligned_size);
+       }
+
+       /* pad with zeroes */
+       if (aligned_size_remainder != 0) {
+               memset(&(((char *) &olsrGpsMessage->nodeInfo.nodeIdType)[nodeLength]),
+                               0, (4 - aligned_size_remainder));
+       }
+
+       return aligned_size;
+}
diff --git a/lib/pud/src/gpsConversion.h b/lib/pud/src/gpsConversion.h
new file mode 100644 (file)
index 0000000..fd8046d
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef _PUD_GPSCONVERSION_H_
+#define _PUD_GPSCONVERSION_H_
+
+/* Plugin includes */
+
+/* OLSR includes */
+#include "olsr_protocol.h"
+
+/* System includes */
+#include <nmea/info.h>
+
+/*
+ * Version
+ */
+
+/** The version of the transmit sentence */
+#define PUD_TX_SENTENCE_VERSION                0
+
+/** The OLSRD message type FIXME get an assigned one */
+#define PUD_OLSR_MSG_TYPE                      171
+
+/*
+ * Functions
+ */
+
+unsigned int gpsToOlsr(nmeaINFO *nmeaInfo, union olsr_message *olsrMessage,
+               unsigned int olsrMessageSize, unsigned long long validityTime);
+
+unsigned int gpsFromOlsr(union olsr_message *olsrMessage,
+               unsigned char * txGpsBuffer, unsigned int txGpsBufferSize);
+
+#endif /* _PUD_GPSCONVERSION_H_ */
diff --git a/lib/pud/src/netTools.c b/lib/pud/src/netTools.c
new file mode 100644 (file)
index 0000000..793a1f0
--- /dev/null
@@ -0,0 +1,81 @@
+#include "netTools.h"
+
+/* Plugin includes */
+#include "pud.h"
+
+/* OLSR includes */
+
+/* System includes */
+#include <assert.h>
+#include <stddef.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <unistd.h>
+
+/**
+ Determine whether an IP address (v4 or v6) is a multicast address.
+
+ @param addressFamily
+ The address family (AF_INET or AF_INET6)
+ @param addr
+ An IP address (v4 or v6)
+
+ @return
+ - true when the address is a multicast address
+ - false otherwise
+ */
+bool isMulticast(int addressFamily, union olsr_sockaddr *addr) {
+       assert(addr != NULL);
+       assert((addressFamily == AF_INET) || (addressFamily == AF_INET6));
+
+       if (addressFamily == AF_INET) {
+               return IN_MULTICAST(ntohl(addr->in4.sin_addr.s_addr));
+       }
+
+       return IN6_IS_ADDR_MULTICAST(&addr->in6.sin6_addr);
+}
+
+/**
+ Get the hardware address (MAC) of an interface
+
+ @param ifName
+ the name of the interface
+ @param family
+ the protocol family (AF_INET or AF_INET6)
+ @param ifr
+ the buffer in which to write the hardware address (MAC)
+
+ @return
+ - the pointer to the hardware address (inside ifr)
+ - NULL on failure
+ */
+unsigned char * getHardwareAddress(const char * ifName, int family,
+               struct ifreq *ifr) {
+       int fd;
+       int cpySize;
+
+       assert(ifName != NULL);
+       assert(strlen(ifName) <= IFNAMSIZ);
+       assert((family == AF_INET) || (family == AF_INET6));
+       assert(ifr != NULL);
+
+       fd = socket(family, SOCK_DGRAM, 0);
+
+       ifr->ifr_addr.sa_family = family;
+       memset(ifr->ifr_name, 0, sizeof(ifr->ifr_name));
+       cpySize = (strlen(ifName) < sizeof(ifr->ifr_name)) ? strlen(ifName)
+                       : sizeof(ifr->ifr_name);
+       strncpy(ifr->ifr_name, ifName, cpySize);
+
+       errno = 0;
+       if (ioctl(fd, SIOCGIFHWADDR, ifr) < 0) {
+               pudError(true, "%s@%u: ioctl(SIOCGIFHWADDR) error", __FILE__, __LINE__);
+               close(fd);
+               return NULL;
+       }
+
+       close(fd);
+
+       return (unsigned char *) &ifr->ifr_hwaddr.sa_data[0];
+}
diff --git a/lib/pud/src/netTools.h b/lib/pud/src/netTools.h
new file mode 100644 (file)
index 0000000..a6c5658
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef _PUD_NETTOOLS_H_
+#define _PUD_NETTOOLS_H_
+
+/* Plugin includes */
+
+/* OLSR includes */
+#include "olsr_types.h"
+
+/* System includes */
+#include <stdbool.h>
+#include <net/if.h>
+
+bool isMulticast(int addressFamily, union olsr_sockaddr *addr);
+
+unsigned char * getHardwareAddress(const char * ifName, int family,
+               struct ifreq *ifr);
+
+#endif /* _PUD_NETTOOLS_H_ */
diff --git a/lib/pud/src/networkInterfaces.c b/lib/pud/src/networkInterfaces.c
new file mode 100644 (file)
index 0000000..e55bd21
--- /dev/null
@@ -0,0 +1,659 @@
+#include "networkInterfaces.h"
+
+/* Plugin includes */
+#include "pud.h"
+#include "configuration.h"
+#include "netTools.h"
+
+/* OLSRD includes */
+#include "olsr_cfg.h"
+#include "olsr.h"
+
+/* System includes */
+#include <assert.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <ifaddrs.h>
+
+/*
+ * RX interfaces
+ */
+
+/** The list of network interface objects, receiving GPS NMEA sentences */
+static TRxTxNetworkInterface *rxNetworkInterfacesListHead = NULL;
+
+/** Pointer to the last network interface object, receiving GPS NMEA sentences */
+static TRxTxNetworkInterface *lastRxNetworkInterface = NULL;
+
+/**
+ @return
+ The list of network interface objects, receiving GPS NMEA sentences
+ */
+TRxTxNetworkInterface *getRxNetworkInterfaces(void) {
+       return rxNetworkInterfacesListHead;
+}
+
+/**
+ Create a receive socket for a network interface
+
+ @param networkInterface
+ The network interface object. This function expects it to be filled with all
+ information, except for the socket descriptor.
+ @param rxSocketHandlerFunction
+ The function that handles reception of data on the network interface
+
+ @return
+ - the socket descriptor (>= 0)
+ - -1 if an error occurred
+ */
+static int createRxSocket(TRxTxNetworkInterface * networkInterface,
+               socket_handler_func rxSocketHandlerFunction) {
+       int ipFamilySetting;
+       int ipProtoSetting;
+       int ipMcLoopSetting;
+       int ipAddMembershipSetting;
+       int socketReuseFlagValue = 1;
+       int mcLoopValue = 1;
+       union olsr_sockaddr address;
+       int rxSocket = -1;
+
+       assert(networkInterface != NULL);
+       assert(rxSocketHandlerFunction != NULL);
+       assert(strncmp((char *) &networkInterface->name[0], "",
+                                       sizeof(networkInterface->name)) != 0);
+
+       memset(&address, 0, sizeof(address));
+       if (olsr_cnf->ip_version == AF_INET) {
+               assert(networkInterface->ipAddress.in4.sin_addr.s_addr != INADDR_ANY);
+
+               ipFamilySetting = AF_INET;
+               ipProtoSetting = IPPROTO_IP;
+               ipMcLoopSetting = IP_MULTICAST_LOOP;
+               ipAddMembershipSetting = IP_ADD_MEMBERSHIP;
+
+               address.in4.sin_family = ipFamilySetting;
+               address.in4.sin_addr.s_addr = INADDR_ANY;
+               address.in4.sin_port = getRxMcPort();
+       } else {
+               assert(networkInterface->ipAddress.in6.sin6_addr.s6_addr != in6addr_any.s6_addr);
+
+               ipFamilySetting = AF_INET6;
+               ipProtoSetting = IPPROTO_IPV6;
+               ipMcLoopSetting = IPV6_MULTICAST_LOOP;
+               ipAddMembershipSetting = IPV6_ADD_MEMBERSHIP;
+
+               address.in6.sin6_family = ipFamilySetting;
+               address.in6.sin6_addr = in6addr_any;
+               address.in6.sin6_port = getRxMcPort();
+       }
+
+       /* Create a datagram socket on which to receive. */
+       errno = 0;
+       rxSocket = socket(ipFamilySetting, SOCK_DGRAM, 0);
+       if (rxSocket < 0) {
+               pudError(true, "Could not create a receive socket for interface %s",
+                               networkInterface->name);
+               goto bail;
+       }
+
+       /* Enable SO_REUSEADDR to allow multiple applications to receive the same
+        * multicast messages */
+       errno = 0;
+       if (setsockopt(rxSocket, SOL_SOCKET, SO_REUSEADDR, &socketReuseFlagValue,
+                       sizeof(socketReuseFlagValue)) < 0) {
+               pudError(true, "Could not set the reuse flag on the receive socket for"
+                       " interface %s", networkInterface->name);
+               goto bail;
+       }
+
+       /* Bind to the proper port number with the IP address INADDR_ANY
+        * (INADDR_ANY is really required here, do not change it) */
+       errno = 0;
+       if (bind(rxSocket, (struct sockaddr *) &address, sizeof(address)) < 0) {
+               pudError(true, "Could not bind the receive socket for interface"
+                       " %s to port %u", networkInterface->name, ntohs(getRxMcPort()));
+               goto bail;
+       }
+
+       /* Enable multicast local loopback */
+       errno = 0;
+       if (setsockopt(rxSocket, ipProtoSetting, ipMcLoopSetting, &mcLoopValue,
+                       sizeof(mcLoopValue)) < 0) {
+               pudError(true, "Could not %s multicast loopback on the"
+                       " receive socket for interface %s", mcLoopValue ? "enable"
+                               : "disable", networkInterface->name);
+               goto bail;
+       }
+
+       /* Join the multicast group on the local interface. Note that this
+        * ADD_MEMBERSHIP option must be called for each local interface over
+        * which the multicast datagrams are to be received. */
+       if (ipFamilySetting == AF_INET) {
+               struct ip_mreq mc_settings;
+               (void) memset(&mc_settings, 0, sizeof(mc_settings));
+               mc_settings.imr_multiaddr = getRxMcAddr()->in4.sin_addr;
+               mc_settings.imr_interface = networkInterface->ipAddress.in4.sin_addr;
+               errno = 0;
+               if (setsockopt(rxSocket, ipProtoSetting, ipAddMembershipSetting,
+                               &mc_settings, sizeof(mc_settings)) < 0) {
+                       pudError(true, "Could not subscribe interface %s to the configured"
+                               " multicast group", networkInterface->name);
+                       goto bail;
+               }
+       } else {
+               struct ipv6_mreq mc6_settings;
+               (void) memset(&mc6_settings, 0, sizeof(mc6_settings));
+               mc6_settings.ipv6mr_multiaddr = getRxMcAddr()->in6.sin6_addr;
+               mc6_settings.ipv6mr_interface = 0;
+               errno = 0;
+               if (setsockopt(rxSocket, ipProtoSetting, ipAddMembershipSetting,
+                               &mc6_settings, sizeof(mc6_settings)) < 0) {
+                       pudError(true, "Could not subscribe interface %s to the configured"
+                               " multicast group", networkInterface->name);
+                       goto bail;
+               }
+       }
+
+       add_olsr_socket(rxSocket, rxSocketHandlerFunction, NULL, networkInterface,
+                       SP_PR_READ);
+
+       return rxSocket;
+
+       bail: if (rxSocket >= 0) {
+               close(rxSocket);
+       }
+       return -1;
+
+}
+
+/**
+ Create a receive interface and add it to the list of receive network interface
+ objects
+
+ @param ifName
+ the network interface name
+ @param ipAddr
+ the IP address of the interface
+ @param rxSocketHandlerFunction
+ the function that handles reception of data on the network interface
+
+ @return
+ - true on success
+ - false on failure
+ */
+static bool createRxInterface(const char * ifName, union olsr_sockaddr ipAddr,
+               socket_handler_func rxSocketHandlerFunction) {
+       unsigned char * hwAddr;
+       int socketFd = -1;
+       TRxTxNetworkInterface * networkInterface = NULL;
+       struct ifreq ifReqHwAddr;
+
+       if (ifName == NULL) {
+               goto bail;
+       }
+
+       hwAddr = getHardwareAddress(ifName, olsr_cnf->ip_version, &ifReqHwAddr);
+       if (hwAddr == NULL) {
+               goto bail;
+       }
+
+       networkInterface = olsr_malloc(sizeof(TRxTxNetworkInterface),
+                       "TRxTxNetworkInterface (PUD)");
+       if (networkInterface == NULL) {
+               goto bail;
+       }
+
+       memcpy(networkInterface->name, ifName, sizeof(networkInterface->name));
+       networkInterface->name[IFNAMSIZ] = '\0';
+       networkInterface->ipAddress = ipAddr;
+       networkInterface->handler = NULL;
+       memcpy(&networkInterface->hwAddress[0], hwAddr,
+                       sizeof(networkInterface->hwAddress));
+       networkInterface->next = NULL;
+
+       /* networkInterface needs to be filled in when calling createRxSocket */
+       socketFd = createRxSocket(networkInterface, rxSocketHandlerFunction);
+       if (socketFd < 0) {
+               goto bail;
+       }
+       networkInterface->socketFd = socketFd;
+       networkInterface->handler = rxSocketHandlerFunction;
+
+       /* Add new object to the end of the global list. */
+       if (rxNetworkInterfacesListHead == NULL) {
+               rxNetworkInterfacesListHead = networkInterface;
+               lastRxNetworkInterface = networkInterface;
+       } else {
+               lastRxNetworkInterface->next = networkInterface;
+               lastRxNetworkInterface = networkInterface;
+       }
+
+       return true;
+
+       bail: if (networkInterface != NULL) {
+               free(networkInterface);
+       }
+       return false;
+
+}
+
+/*
+ * TX interfaces
+ */
+
+/** The list of network interface objects, sending our NMEA sentences */
+static TRxTxNetworkInterface *txNetworkInterfacesListHead = NULL;
+
+/** Pointer to the last network interface object, sending our NMEA sentences */
+static TRxTxNetworkInterface *lastTxNetworkInterface = NULL;
+
+/**
+ @return
+ The list of network interface objects, sending our NMEA sentences
+ */
+TRxTxNetworkInterface *getTxNetworkInterfaces(void) {
+       return txNetworkInterfacesListHead;
+}
+
+/**
+ Create a transmit socket for a network interface
+
+ @param networkInterface
+ The network interface object. This function expects it to be filled with all
+ information, except for the socket descriptor.
+
+ @return
+ - the socket descriptor (>= 0)
+ - -1 if an error occurred
+ */
+static int createTxSocket(TRxTxNetworkInterface * networkInterface) {
+       int ipFamilySetting;
+       int ipProtoSetting;
+       int ipMcLoopSetting;
+       int ipMcIfSetting;
+       int mcLoopValue = 0;
+       unsigned char txTtl = getTxTtl();
+       union olsr_sockaddr address;
+       int txSocket = -1;
+
+       assert(networkInterface != NULL);
+       assert(strncmp((char *) &networkInterface->name[0], "",
+                                       sizeof(networkInterface->name)) != 0);
+
+       memset(&address, 0, sizeof(address));
+       if (olsr_cnf->ip_version == AF_INET) {
+               assert(networkInterface->ipAddress.in4.sin_addr.s_addr != INADDR_ANY);
+
+               ipFamilySetting = AF_INET;
+               ipProtoSetting = IPPROTO_IP;
+               ipMcLoopSetting = IP_MULTICAST_LOOP;
+               ipMcIfSetting = IP_MULTICAST_IF;
+
+               address.in4.sin_family = ipFamilySetting;
+               address.in4.sin_addr = networkInterface->ipAddress.in4.sin_addr;
+               address.in4.sin_port = getTxMcPort();
+       } else {
+               assert(networkInterface->ipAddress.in6.sin6_addr.s6_addr != in6addr_any.s6_addr);
+
+               ipFamilySetting = AF_INET6;
+               ipProtoSetting = IPPROTO_IPV6;
+               ipMcLoopSetting = IPV6_MULTICAST_LOOP;
+               ipMcIfSetting = IPV6_MULTICAST_IF;
+
+               address.in6.sin6_family = ipFamilySetting;
+               address.in6.sin6_addr = networkInterface->ipAddress.in6.sin6_addr;
+               address.in6.sin6_port = getTxMcPort();
+       }
+
+       /*  Create a datagram socket on which to transmit */
+       errno = 0;
+       txSocket = socket(ipFamilySetting, SOCK_DGRAM, 0);
+       if (txSocket < 0) {
+               pudError(true, "Could not create a transmit socket for interface %s",
+                               networkInterface->name);
+               goto bail;
+       }
+
+       /* Bind the socket to the desired interface and port */
+       errno = 0;
+       if (setsockopt(txSocket, ipProtoSetting, ipMcIfSetting, &address,
+                       sizeof(address)) < 0) {
+               pudError(true, "Could not set the multicast interface on the"
+                       " transmit socket to interface %s", networkInterface->name);
+               goto bail;
+       }
+
+       /* Disable multicast local loopback */
+       errno = 0;
+       if (setsockopt(txSocket, ipProtoSetting, ipMcLoopSetting, &mcLoopValue,
+                       sizeof(mcLoopValue)) < 0) {
+               pudError(true, "Could not %s multicast loopback on the"
+                       " transmit socket for interface %s", mcLoopValue ? "enable"
+                               : "disable", networkInterface->name);
+               goto bail;
+       }
+
+       /* Set the TTL on the socket */
+       errno = 0;
+       if (setsockopt(txSocket, ipProtoSetting, IP_MULTICAST_TTL, &txTtl,
+                       sizeof(txTtl)) < 0) {
+               pudError(true, "Could not set TTL on the transmit socket"
+                       " for interface %s", networkInterface->name);
+               goto bail;
+       }
+
+       /* Set the no delay option on the socket */
+       errno = 0;
+       if (fcntl(txSocket, F_SETFL, O_NDELAY) < 0) {
+               pudError(true, "Could not set the no delay option on the"
+                       " transmit socket for interface %s", networkInterface->name);
+               goto bail;
+       }
+
+       return txSocket;
+
+       bail: if (txSocket >= 0) {
+               close(txSocket);
+       }
+       return -1;
+}
+
+/**
+ Create a transmit interface and add it to the list of transmit network
+ interface objects
+
+ @param ifName
+ the network interface name
+ @param ipAddr
+ the IP address of the interface
+
+ @return
+ - true on success
+ - false on failure
+ */
+static bool createTxInterface(const char * ifName, union olsr_sockaddr ipAddr) {
+       unsigned char * hwAddr;
+       int socketFd = -1;
+       TRxTxNetworkInterface * networkInterface = NULL;
+       struct ifreq ifReqHwAddr;
+
+       if (ifName == NULL) {
+               goto bail;
+       }
+
+       hwAddr = getHardwareAddress(ifName, olsr_cnf->ip_version, &ifReqHwAddr);
+       if (hwAddr == NULL) {
+               goto bail;
+       }
+
+       networkInterface = olsr_malloc(sizeof(TRxTxNetworkInterface),
+                       "TRxTxNetworkInterface (PUD)");
+       if (networkInterface == NULL) {
+               goto bail;
+       }
+
+       memcpy(networkInterface->name, ifName, sizeof(networkInterface->name));
+       networkInterface->name[IFNAMSIZ] = '\0';
+       networkInterface->ipAddress = ipAddr;
+       networkInterface->handler = NULL;
+       memcpy(&networkInterface->hwAddress[0], hwAddr,
+                       sizeof(networkInterface->hwAddress));
+       networkInterface->next = NULL;
+
+       /* networkInterface needs to be filled in when calling createTxSocket */
+       socketFd = createTxSocket(networkInterface);
+       if (socketFd < 0) {
+               goto bail;
+       }
+       networkInterface->socketFd = socketFd;
+
+       /* Add new object to the end of the global list. */
+       if (txNetworkInterfacesListHead == NULL) {
+               txNetworkInterfacesListHead = networkInterface;
+               lastTxNetworkInterface = networkInterface;
+       } else {
+               lastTxNetworkInterface->next = networkInterface;
+               lastTxNetworkInterface = networkInterface;
+       }
+
+       return true;
+
+       bail: if (networkInterface != NULL) {
+               free(networkInterface);
+       }
+       return false;
+}
+
+/*
+ * OLSR interfaces
+ */
+
+/** The list of OLSR network interface objects */
+static TOLSRNetworkInterface *olsrNetworkInterfacesListHead = NULL;
+
+/** Pointer to the last OLSR network interface object */
+static TOLSRNetworkInterface *lastOlsrNetworkInterface = NULL;
+
+/**
+ Get the OLSR interface structure for a certain OLSR interface. Note that
+ pointer comparison is performed to compare the OLSR interfaces.
+
+ @param olsrIntf
+ a pointer to an OLSR interface
+
+ @return
+ - a pointer to the OLSR interface structure
+ - NULL when not found
+ */
+TOLSRNetworkInterface * getOlsrNetworkInterface(struct interface *olsrIntf) {
+       TOLSRNetworkInterface * retval = olsrNetworkInterfacesListHead;
+
+       while ((retval->olsrIntf != olsrIntf) && (retval != NULL)) {
+               retval = retval->next;
+       }
+
+       return retval;
+}
+
+/**
+ Create an OLSR interface and add it to the list of OLSR network interface
+ objects
+
+ @param olsrIntf
+ a pointer to the OLSR interface
+
+ @return
+ - true on success
+ - false on failure
+ */
+static int createOlsrInterface(struct interface *olsrIntf) {
+       unsigned char * hwAddr;
+       struct ifreq ifReqHwAddr;
+       TOLSRNetworkInterface * networkInterface = NULL;
+
+       hwAddr = getHardwareAddress(olsrIntf->int_name, olsr_cnf->ip_version,
+                       &ifReqHwAddr);
+       if (hwAddr == NULL) {
+               goto bail;
+       }
+
+       networkInterface = olsr_malloc(sizeof(TOLSRNetworkInterface),
+                       "TOLSRNetworkInterface (PUD)");
+       if (networkInterface == NULL) {
+               goto bail;
+       }
+
+       networkInterface->olsrIntf = olsrIntf;
+       memcpy(&networkInterface->hwAddress[0], hwAddr,
+                       sizeof(networkInterface->hwAddress));
+       networkInterface->next = NULL;
+
+       /* Add new object to the end of the global list. */
+       if (olsrNetworkInterfacesListHead == NULL) {
+               olsrNetworkInterfacesListHead = networkInterface;
+               lastOlsrNetworkInterface = networkInterface;
+       } else {
+               lastOlsrNetworkInterface->next = networkInterface;
+               lastOlsrNetworkInterface = networkInterface;
+       }
+
+       return true;
+
+       bail: if (networkInterface != NULL) {
+               free(networkInterface);
+       }
+       return false;
+}
+
+/*
+ * Interface Functions
+ */
+
+/**
+ Creates receive and transmit sockets and register the receive sockets with
+ the OLSR stack
+
+ @param rxSocketHandlerFunction
+ The function to call upon reception of data on a receive socket
+
+ @return
+ - true on success
+ - false on failure
+ */
+bool createNetworkInterfaces(socket_handler_func rxSocketHandlerFunction) {
+       int retval = false;
+       struct ifaddrs *ifAddrs = NULL;
+       struct ifaddrs *ifAddr = NULL;
+
+       errno = 0;
+       if (getifaddrs(&ifAddrs) != 0) {
+               pudError(true, "Could not get list of interfaces and their addresses");
+               return retval;
+       }
+
+       /* loop over all interfaces */
+       for (ifAddr = ifAddrs; ifAddr != NULL; ifAddr = ifAddr->ifa_next) {
+               struct sockaddr * addr = ifAddr->ifa_addr;
+               if (addr != NULL) {
+                       int addrFamily = addr->sa_family;
+                       if (addrFamily == olsr_cnf->ip_version) {
+                               char * ifName = ifAddr->ifa_name;
+                               union olsr_sockaddr ipAddr;
+
+                               /* determine whether the iterated interface is an OLSR
+                                * interface: returns NULL when the interface is not an
+                                * OLSR interface */
+                               struct interface *olsrIntf = if_ifwithname(ifName);
+                               bool isOlsrIf = (olsrIntf != NULL);
+
+                               /* determine whether the iterated interface is configured as a
+                                * non-OLSR interface in the plugin parameter list */
+                               bool isRxNonOlsrIf = isRxNonOlsrInterface(ifName);
+                               bool isTxNonOlsrIf = isTxNonOlsrInterface(ifName);
+                               bool isNonOlsrIf = isRxNonOlsrIf || isTxNonOlsrIf;
+
+                               if (!isOlsrIf && !isNonOlsrIf) {
+                                       /* Interface is not an OLSR interface AND interface is not
+                                        * configured as non-OLSR interface: skip */
+                                       continue;
+                               }
+
+                               if (isOlsrIf && !createOlsrInterface(olsrIntf)) {
+                                       /* creating an OLSR interface failed */
+                                       goto end;
+                               }
+
+                               if (!isNonOlsrIf) {
+                                       /* interface is not configured as non-OLSR interface: skip */
+                                       continue;
+                               }
+
+                               if (addrFamily == AF_INET) {
+                                       memcpy(&ipAddr.in4, addr, sizeof(struct sockaddr_in));
+                               } else {
+                                       memcpy(&ipAddr.in6, addr, sizeof(struct sockaddr_in6));
+                               }
+
+                               if (isRxNonOlsrIf && !createRxInterface(ifName, ipAddr,
+                                               rxSocketHandlerFunction)) {
+                                       /* creating a receive interface failed */
+                                       goto end;
+                               }
+
+                               if (isTxNonOlsrIf && !createTxInterface(ifName, ipAddr)) {
+                                       /* creating a transmit interface failed */
+                                       goto end;
+                               }
+                       }
+               }
+       }
+
+       retval = true;
+
+       end: freeifaddrs(ifAddrs);
+       return retval;
+}
+
+/**
+ Cleanup the OLSR network interfaces in the given list
+
+ @param networkInterface
+ the list of network interface to close and clean up
+ */
+static void cleanupOlsrInterfaces(TOLSRNetworkInterface * networkInterface) {
+       TOLSRNetworkInterface * nextNetworkInterface = networkInterface;
+       while (nextNetworkInterface != NULL) {
+               TOLSRNetworkInterface * iteratedNetworkInterface = nextNetworkInterface;
+               nextNetworkInterface = iteratedNetworkInterface->next;
+               iteratedNetworkInterface->next = NULL;
+               free(iteratedNetworkInterface);
+       }
+}
+
+/**
+ Close and cleanup the network interfaces in the given list
+
+ @param networkInterface
+ the list of network interface to close and clean up
+ */
+static void closeInterfaces(TRxTxNetworkInterface * networkInterface) {
+       TRxTxNetworkInterface * nextNetworkInterface = networkInterface;
+       while (nextNetworkInterface != NULL) {
+               TRxTxNetworkInterface * iteratedNetworkInterface = nextNetworkInterface;
+               if (iteratedNetworkInterface->socketFd >= 0) {
+                       if (iteratedNetworkInterface->handler) {
+                               remove_olsr_socket(iteratedNetworkInterface->socketFd,
+                                               iteratedNetworkInterface->handler, NULL);
+                       }
+                       close(iteratedNetworkInterface->socketFd);
+                       iteratedNetworkInterface->socketFd = -1;
+               }
+               nextNetworkInterface = iteratedNetworkInterface->next;
+               iteratedNetworkInterface->next = NULL;
+               free(iteratedNetworkInterface);
+       }
+}
+
+/**
+ Close and cleanup all receive and transmit network interfaces
+ */
+void closeNetworkInterfaces(void) {
+       if (rxNetworkInterfacesListHead != NULL) {
+               closeInterfaces(rxNetworkInterfacesListHead);
+               rxNetworkInterfacesListHead = NULL;
+       }
+
+       if (txNetworkInterfacesListHead != NULL) {
+               closeInterfaces(txNetworkInterfacesListHead);
+               txNetworkInterfacesListHead = NULL;
+       }
+
+       if (olsrNetworkInterfacesListHead != NULL) {
+               cleanupOlsrInterfaces(olsrNetworkInterfacesListHead);
+               olsrNetworkInterfacesListHead = NULL;
+       }
+}
diff --git a/lib/pud/src/networkInterfaces.h b/lib/pud/src/networkInterfaces.h
new file mode 100644 (file)
index 0000000..d7a024d
--- /dev/null
@@ -0,0 +1,55 @@
+#ifndef _PUD_NETWORKINTERFACES_H
+#define _PUD_NETWORKINTERFACES_H
+
+/* Plugin includes */
+
+/* OLSR includes */
+#include "olsr_types.h"
+#include "interfaces.h"
+#include "scheduler.h"
+
+/* System includes */
+#include <stdbool.h>
+#include <net/if.h>
+
+/** A list of TRxTxNetworkInterface objects, used for non-OLSR interfaces */
+typedef struct _TRxTxNetworkInterface {
+               /** The socket file descriptor for the non-OLSR interface*/
+               int socketFd;
+
+               /** The name of the interface */
+               unsigned char name[IFNAMSIZ + 1];
+
+               /** The IP address of the interface */
+               union olsr_sockaddr ipAddress;
+
+               /** the socket handler function */
+               socket_handler_func handler;
+
+               /** The hardware address of the interface */
+               unsigned char hwAddress[IFHWADDRLEN];
+
+               /** The next TRxTxNetworkInterface in the list */
+               struct _TRxTxNetworkInterface * next;
+} TRxTxNetworkInterface;
+
+/** A list of TOLSRNetworkInterface objects, used for OLSR interfaces */
+typedef struct _TOLSRNetworkInterface {
+               /** A pointer to the OLSR interface */
+               struct interface * olsrIntf;
+
+               /** The hardware address of the interface */
+               unsigned char hwAddress[IFHWADDRLEN];
+
+               /** The next TOLSRNetworkInterface in the list */
+               struct _TOLSRNetworkInterface * next;
+} TOLSRNetworkInterface;
+
+bool createNetworkInterfaces(socket_handler_func rxSocketHandlerFunction);
+void closeNetworkInterfaces(void);
+
+TRxTxNetworkInterface * getRxNetworkInterfaces(void);
+TRxTxNetworkInterface * getTxNetworkInterfaces(void);
+TOLSRNetworkInterface * getOlsrNetworkInterface(struct interface * olsrIntf);
+
+#endif /* _PUD_NETWORKINTERFACES_H */
diff --git a/lib/pud/src/nodeIdConversion.c b/lib/pud/src/nodeIdConversion.c
new file mode 100644 (file)
index 0000000..372a280
--- /dev/null
@@ -0,0 +1,181 @@
+#include "nodeIdConversion.h"
+
+/* Plugin includes */
+#include "compiler.h"
+
+/* OLSR includes */
+
+/* System includes */
+#include <assert.h>
+#include <arpa/inet.h>
+#include <nmea/util.h>
+#include <net/if.h>
+
+/* ************************************************************************
+ * Node Information
+ * ************************************************************************ */
+
+/**
+ Convert the nodeIdType of an OLSR message into a string.
+
+ @param ipVersion
+ The ip version, either AF_INET or AF_INET6
+ @param olsrMessage
+ A pointer to the OLSR message. Used to be able to retrieve the IP address of
+ the sender.
+ @param nodeIdTypeBuffer
+ A pointer to the buffer in which the nodeIdType string representation is
+ written (the buffer needs to be at least PUD_TX_NODEIDTYPE_DIGITS + 1 bytes).
+ When NULL then the nodeIdType string is not written.
+ @param nodeIdTypeBufferSize
+ The size of the nodeIdTypeBuffer
+ */
+void getNodeTypeStringFromOlsr(int ipVersion, union olsr_message * olsrMessage,
+               char * nodeIdTypeBuffer, int nodeIdTypeBufferSize) {
+       int chars;
+
+       if (unlikely(!nodeIdTypeBuffer || (nodeIdTypeBufferSize == 0))) {
+               return;
+       }
+
+       assert(nodeIdTypeBufferSize >= (PUD_TX_NODEIDTYPE_DIGITS + 1));
+
+       /* message has NO nodeId information */
+       chars = snprintf(&nodeIdTypeBuffer[0], nodeIdTypeBufferSize, "%u",
+                       getNodeIdType(ipVersion, olsrMessage));
+       if (likely(chars < nodeIdTypeBufferSize)) {
+               nodeIdTypeBuffer[chars] = '\0';
+       } else {
+               nodeIdTypeBuffer[nodeIdTypeBufferSize] = '\0';
+       }
+
+       return;
+}
+
+/**
+ Get a nodeId number (in string representation), using a certain number of
+ bytes, from the message of an OLSR message.
+
+ @param buffer
+ A pointer to the buffer that holds the nodeId
+ @param bufferSize
+ The number of bytes used by the number in the buffer
+ @param nodeIdBuffer
+ The buffer in which to place the nodeId number in string representation
+ @param nodeIdBufferSize
+ The size of the nodeIdbuffer
+
+ @return
+ A pointer to the nodeId string representation (&nodeIdBuffer[0])
+ */
+static char *getNodeIdNumberFromOlsr(unsigned char * buffer,
+               unsigned int bufferSize, char *nodeIdBuffer, socklen_t nodeIdBufferSize) {
+       unsigned long long val = 0;
+       unsigned int i = 0;
+       int chars;
+
+       while (i < bufferSize) {
+               val <<= 8;
+               val += buffer[i];
+               i++;
+       }
+
+       chars = snprintf(nodeIdBuffer, nodeIdBufferSize, "%llu", val);
+       if (likely(chars < (int) nodeIdBufferSize)) {
+               nodeIdBuffer[chars] = '\0';
+       } else {
+               nodeIdBuffer[nodeIdBufferSize] = '\0';
+       }
+       return &nodeIdBuffer[0];
+}
+
+/**
+ Convert the nodeId of an OLSR message into a string.
+
+ @param ipVersion
+ The ip version, either AF_INET or AF_INET6
+ @param olsrMessage
+ A pointer to the OLSR message. Used to be able to retrieve the IP address of
+ the sender.
+ @param nodeIdStr
+ A pointer to a variable in which to store the pointer to the buffer in which
+ the nodeId string representation is written (the buffer needs to be at least
+ PUD_TX_NODEIDTYPE_DIGITS + 1 bytes). Not written to when nodeIdStrBuffer or
+ nodeIdStr is NULL or when nodeIdStrBufferSize is zero. Can point to
+ nodeIdStrBuffer or straight into the olsrMessage
+ @param nodeIdStrBuffer
+ A pointer to the buffer in which the nodeId string representation can be
+ written. Not written to when nodeIdStrBuffer or nodeIdStr is NULL or when
+ nodeIdStrBufferSize is zero.
+ @param nodeIdStrBufferSize
+ The size of the nodeIdStrBuffer. When zero then nodeIdStrBuffer and nodeIdStr
+ are not written to.
+ */
+void getNodeIdStringFromOlsr(int ipVersion, union olsr_message *olsrMessage,
+               const char **nodeIdStr, char *nodeIdStrBuffer,
+               unsigned int nodeIdStrBufferSize) {
+       PudOlsrWireFormat * olsrGpsMessage;
+       unsigned char * buffer;
+       unsigned int bufferSize;
+
+       if (unlikely(!nodeIdStrBuffer || (nodeIdStrBufferSize == 0) || !nodeIdStr)) {
+               return;
+       }
+
+       assert(nodeIdStrBufferSize >= (PUD_TX_NODEID_BUFFERSIZE + 1));
+
+       olsrGpsMessage = getOlsrMessagePayload(ipVersion, olsrMessage);
+
+       getNodeIdPointers(ipVersion, olsrMessage, &buffer, &bufferSize);
+
+       if (olsrGpsMessage->smask & PUD_FLAGS_ID) {
+               switch (olsrGpsMessage->nodeInfo.nodeIdType) {
+                       case PUD_NODEIDTYPE_MAC: /* hardware address */
+                       {
+                               int chars;
+
+                               assert(bufferSize == 6);
+
+                               chars = snprintf(nodeIdStrBuffer, nodeIdStrBufferSize,
+                                               "%02x:%02x:%02x:%02x:%02x:%02x", buffer[0], buffer[1],
+                                               buffer[2], buffer[3], buffer[4], buffer[5]);
+                               if (likely(chars < (int) nodeIdStrBufferSize)) {
+                                       nodeIdStrBuffer[chars] = '\0';
+                               } else {
+                                       nodeIdStrBuffer[nodeIdStrBufferSize - 1] = '\0';
+                               }
+                               *nodeIdStr = &nodeIdStrBuffer[0];
+                       }
+                               break;
+
+                       case PUD_NODEIDTYPE_DNS: /* DNS name */
+                               *nodeIdStr = (char *) &olsrGpsMessage->nodeInfo.nodeId;
+                               break;
+
+                       case PUD_NODEIDTYPE_MSISDN: /* an MSISDN number */
+                       case PUD_NODEIDTYPE_TETRA: /* a Tetra number */
+                       case PUD_NODEIDTYPE_192:
+                       case PUD_NODEIDTYPE_193:
+                       case PUD_NODEIDTYPE_194:
+                               *nodeIdStr = getNodeIdNumberFromOlsr(buffer, bufferSize,
+                                               nodeIdStrBuffer, nodeIdStrBufferSize);
+                               break;
+
+                       case PUD_NODEIDTYPE_IPV4: /* IPv4 address */
+                       case PUD_NODEIDTYPE_IPV6: /* IPv6 address */
+                       default: /* unsupported */
+                               goto noId;
+               }
+
+               return;
+       }
+
+       /* message has NO nodeId information */
+       noId: {
+               void * addr = getOlsrMessageOriginator(ipVersion, olsrMessage);
+               *nodeIdStr = inet_ntop(ipVersion, addr, nodeIdStrBuffer,
+                               nodeIdStrBufferSize);
+       }
+
+       return;
+}
diff --git a/lib/pud/src/nodeIdConversion.h b/lib/pud/src/nodeIdConversion.h
new file mode 100644 (file)
index 0000000..36f97de
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef _PUD_NODEIDCONVERSION_H_
+#define _PUD_NODEIDCONVERSION_H_
+
+/* Plugin includes */
+#include "wireFormat.h"
+
+/* OLSR includes */
+#include "olsr_protocol.h"
+
+/* System includes */
+#include <stddef.h>
+
+void getNodeTypeStringFromOlsr(int ipVersion, union olsr_message * olsrMessage,
+               char * nodeIdTypeBuffer, int nodeIdTypeBufferSize);
+
+void getNodeIdStringFromOlsr(int ipVersion, union olsr_message *olsrMessage,
+               const char **nodeIdStr, char *nodeIdStrBuffer,
+               unsigned int nodeIdStrBufferSize);
+
+#endif /* _PUD_NODEIDCONVERSION_H_ */
diff --git a/lib/pud/src/posAvg.c b/lib/pud/src/posAvg.c
new file mode 100644 (file)
index 0000000..276af69
--- /dev/null
@@ -0,0 +1,554 @@
+#include "posAvg.h"
+
+/* Plugin includes */
+#include "dump.h"
+
+/* OLSR includes */
+#include "olsr.h"
+
+/* System includes */
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+#include <nmea/sentence.h>
+#include <nmea/info.h>
+
+/* Defines */
+
+#define LISTSIZE(x)                    (((x)->entriesMaxCount) + 1) /* always valid */
+#define NEWESTINDEX(x)         ((x)->newestEntryIndex) /* always valid */
+#define WRAPINDEX(x, i)                ((i) % LISTSIZE(x)) /* always valid for i>=0 */
+#define INCOMINGINDEX(x)       WRAPINDEX(x, (NEWESTINDEX(x) + 1)) /* always valid */
+#define OLDESTINDEX(x)         (((x)->entriesCount > 1) ? WRAPINDEX(x, (INCOMINGINDEX(x) + LISTSIZE(x) - (x)->entriesCount)) : NEWESTINDEX(x)) /* always valid */
+
+/**
+ Flush/empty the position average list
+
+ @param positionAverageList
+ The position average list
+ */
+void flushPositionAverageList(PositionAverageList * positionAverageList) {
+       assert (positionAverageList != NULL);
+
+       (void) pthread_mutex_lock(&positionAverageList->mutex);
+
+       positionAverageList->entriesCount = 0;
+       memset(&positionAverageList->counters, 0,
+                       sizeof(positionAverageList->counters));
+
+       nmea_zero_INFO(&positionAverageList->positionAverageCumulative.nmeaInfo);
+       nmea_zero_INFO(&positionAverageList->positionAverage.nmeaInfo);
+
+       (void) pthread_mutex_unlock(&positionAverageList->mutex);
+
+#if defined(PUD_DUMP_AVERAGING)
+       olsr_printf(0, "flushPositionAverageList: Flushed the averaging list\n");
+#endif /* PUD_DUMP_AVERAGING */
+}
+
+/**
+ Initialise the position average list: allocate memory for the entries and
+ reset fields.
+
+ @param positionAverageList
+ The position average list
+ @param maxEntries
+ The maximum number of entries in the list (the number of entries that should
+ be averaged)
+
+ @return
+ - false on failure
+ - true otherwise
+ */
+bool initPositionAverageList(PositionAverageList * positionAverageList,
+               unsigned long long maxEntries) {
+       pthread_mutexattr_t attr;
+       void * p;
+
+       if (positionAverageList == NULL) {
+               return false;
+       }
+       if (maxEntries < 2) {
+               return false;
+       }
+
+       if (pthread_mutexattr_init(&attr)) {
+               return false;
+       }
+       if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP)) {
+               return false;
+       }
+       if (pthread_mutex_init(&positionAverageList->mutex, &attr)) {
+               return false;
+       }
+
+       p = olsr_malloc((maxEntries + 1) * sizeof(PositionUpdateEntry),
+                       "PositionAverageEntry entries for PositionAverageList (PUD)");
+       if (p == NULL) {
+               return false;
+       }
+
+       positionAverageList->entriesMaxCount = maxEntries;
+       positionAverageList->entries = p;
+       positionAverageList->newestEntryIndex = 0;
+
+       flushPositionAverageList(positionAverageList);
+
+       return true;
+}
+
+/**
+ Clean up the position average list: free memory and reset fields.
+
+ @param positionAverageList
+ The position average list
+ */
+void destroyPositionAverageList(PositionAverageList * positionAverageList) {
+       assert (positionAverageList != NULL);
+
+       (void) pthread_mutex_lock(&positionAverageList->mutex);
+
+       flushPositionAverageList(positionAverageList);
+
+       if (positionAverageList->entries != NULL) {
+               free(positionAverageList->entries);
+               positionAverageList->entries = NULL;
+       }
+
+       positionAverageList->entriesMaxCount = 0;
+       positionAverageList->newestEntryIndex = 0;
+
+       (void) pthread_mutex_unlock(&positionAverageList->mutex);
+
+       pthread_mutex_destroy(&positionAverageList->mutex);
+}
+
+/**
+ Get the entry for a certain type of position update.
+
+ @param positionAvgList
+ The position average list
+ @param positionType
+ The type of the position of the average entry
+
+ @return
+ A pointer to the requested position update entry
+ */
+PositionUpdateEntry * getPositionAverageEntry(
+               PositionAverageList * positionAvgList,
+               AverageEntryPositionType positionType) {
+       PositionUpdateEntry * r = NULL;
+
+       (void) pthread_mutex_lock(&positionAvgList->mutex);
+       switch (positionType) {
+               case OLDEST:
+                       assert(positionAvgList->entriesCount >= positionAvgList->entriesMaxCount);
+                       r = &positionAvgList->entries[OLDESTINDEX(positionAvgList)];
+                       break;
+
+               case INCOMING:
+                       r = &positionAvgList->entries[INCOMINGINDEX(positionAvgList)];
+                       break;
+
+               case NEWEST:
+                       r = &positionAvgList->entries[NEWESTINDEX(positionAvgList)];
+                       break;
+
+               case AVERAGECUMULATIVE:
+                       r = &positionAvgList->positionAverageCumulative;
+                       break;
+
+               case AVERAGE:
+                       r = &positionAvgList->positionAverage;
+                       break;
+
+               default:
+                       r = NULL;
+                       break;
+       }
+       (void) pthread_mutex_unlock(&positionAvgList->mutex);
+
+       return r;
+}
+
+/**
+ Update position average mask and fix counters for a new entry or for an entry
+ that is/will be removed. Update the respective counters when the smask of the
+ entry has the corresponding flag set. The fix counters count the fix values
+ separately.
+
+ @param positionAverageList
+ The position average list
+ @param entry
+ The entry to update the counters from
+ @param add
+ True when updating the counters for a new entry, false for an entry that
+ is/will be removed
+ */
+static void updateCounters(PositionAverageList * positionAverageList,
+               PositionUpdateEntry * entry, bool add) {
+       PositionUpdateCounters * counters = &positionAverageList->counters;
+#ifndef NDEBUG
+       unsigned long long maxCount = positionAverageList->entriesMaxCount;
+#endif
+       int amount = (add ? 1 : -1);
+
+       /* smask */
+       if ((entry->nmeaInfo.smask & GPGGA) != 0) {
+               assert(add ? (counters->gpgga < maxCount):(counters->gpgga > 0));
+               counters->gpgga += amount;
+       }
+       if ((entry->nmeaInfo.smask & GPGSA) != 0) {
+               assert(add ? (counters->gpgsa < maxCount):(counters->gpgsa > 0));
+               counters->gpgsa += amount;
+       }
+       if ((entry->nmeaInfo.smask & GPGSV) != 0) {
+               assert(add ? (counters->gpgsv < maxCount):(counters->gpgsv > 0));
+               counters->gpgsv += amount;
+       }
+       if ((entry->nmeaInfo.smask & GPRMC) != 0) {
+               assert(add ? (counters->gprmc < maxCount):(counters->gprmc > 0));
+               counters->gprmc += amount;
+       }
+       if ((entry->nmeaInfo.smask & GPVTG) != 0) {
+               assert(add ? (counters->gpvtg < maxCount):(counters->gpvtg > 0));
+               counters->gpvtg += amount;
+       }
+
+       /* sig */
+       if (nmea_INFO_has_field(entry->nmeaInfo.smask, SIG)) {
+               if (entry->nmeaInfo.sig == NMEA_SIG_HIGH) {
+                       assert(add ? (counters->sigHigh < maxCount):(counters->sigHigh > 0));
+                       counters->sigHigh += amount;
+               } else if (entry->nmeaInfo.sig == NMEA_SIG_MID) {
+                       assert(add ? (counters->sigMid < maxCount):(counters->sigMid > 0));
+                       counters->sigMid += amount;
+               } else if (entry->nmeaInfo.sig == NMEA_SIG_LOW) {
+                       assert(add ? (counters->sigLow < maxCount):(counters->sigLow > 0));
+                       counters->sigLow += amount;
+               } else {
+                       assert(add ? (counters->sigBad < maxCount):(counters->sigBad > 0));
+                       counters->sigBad += amount;
+               }
+       }
+
+       /* fix */
+       if (nmea_INFO_has_field(entry->nmeaInfo.smask, FIX)) {
+               if (entry->nmeaInfo.fix == NMEA_FIX_3D) {
+                       assert(add ? (counters->fix3d < maxCount):(counters->fix3d > 0));
+                       counters->fix3d += amount;
+               } else if (entry->nmeaInfo.fix == NMEA_FIX_2D) {
+                       assert(add ? (counters->fix2d < maxCount):(counters->fix2d > 0));
+                       counters->fix2d += amount;
+               } else {
+                       assert(add ? (counters->fixBad < maxCount):(counters->fixBad > 0));
+                       counters->fixBad += amount;
+               }
+       }
+}
+
+/**
+ Determine the new smask, sig and fix of the average position based on the
+ counters. The relevant smask bits (like GPGGA) are only set when all entries
+ in the average list have that bit set. The sig and fix will be set to the
+ lowest/worst value of all entries and will only be set to the highest/best
+ value when all entries in the average list are set to the highest/best value.
+
+ @param positionAverageList
+ The position average list
+ */
+static void determineCumulativeSmaskSigFix(
+               PositionAverageList * positionAverageList) {
+       PositionUpdateEntry * cumulative =
+                       &positionAverageList->positionAverageCumulative;
+       PositionUpdateCounters * counters = &positionAverageList->counters;
+       unsigned long long count = positionAverageList->entriesCount;
+
+       /* smask */
+       cumulative->nmeaInfo.smask = 0;
+
+       if (counters->gpgga >= count) {
+               cumulative->nmeaInfo.smask |= GPGGA;
+       }
+
+       if (counters->gpgsa >= count) {
+               cumulative->nmeaInfo.smask |= GPGSA;
+       }
+
+       if (counters->gpgsv >= count) {
+               cumulative->nmeaInfo.smask |= GPGSV;
+       }
+
+       if (counters->gprmc >= count) {
+               cumulative->nmeaInfo.smask |= GPRMC;
+       }
+
+       if (counters->gpvtg >= count) {
+               cumulative->nmeaInfo.smask |= GPVTG;
+       }
+
+       /* sig */
+       cumulative->nmeaInfo.sig = NMEA_SIG_BAD;
+       if (nmea_INFO_has_field(cumulative->nmeaInfo.smask, SIG)) {
+               if (counters->sigBad == 0) {
+                       if (counters->sigHigh >= count) {
+                               cumulative->nmeaInfo.sig = NMEA_SIG_HIGH;
+                       } else if (counters->sigMid > 0) {
+                               cumulative->nmeaInfo.sig = NMEA_SIG_MID;
+                       } else if (counters->sigLow > 0) {
+                               cumulative->nmeaInfo.sig = NMEA_SIG_LOW;
+                       }
+               }
+       }
+
+       /* fix */
+       cumulative->nmeaInfo.fix = NMEA_FIX_BAD;
+       if (nmea_INFO_has_field(cumulative->nmeaInfo.smask, FIX)) {
+               if (counters->fixBad == 0) {
+                       if (counters->fix3d >= count) {
+                               cumulative->nmeaInfo.fix = NMEA_FIX_3D;
+                       } else if (counters->fix2d > 0) {
+                               cumulative->nmeaInfo.fix = NMEA_FIX_2D;
+                       }
+               }
+       }
+}
+
+/**
+ Add/remove a position update entry to/from the average position list, updates
+ the counters, adjusts the entriesCount and redetermines the cumulative
+ smask, sig and fix.
+
+ @param positionAverageList
+ The position average list
+ @param entry
+ The entry to add/remove
+ @param add
+ True when the entry must be added to the list, false when it must be removed
+ */
+static void addOrRemoveEntryToFromCumulativeAverage(
+               PositionAverageList * positionAverageList, PositionUpdateEntry * entry,
+               bool add) {
+       PositionUpdateEntry * cumulative =
+                       &positionAverageList->positionAverageCumulative;
+
+#if defined(PUD_DUMP_AVERAGING)
+       dump_nmeaInfo(&entry->nmeaInfo, "addOrRemoveEntryToFromCumulativeAverage: entry");
+       dump_nmeaInfo(&cumulative->nmeaInfo, "addOrRemoveEntryToFromCumulativeAverage: positionAverageList->positionAverageCumulative (before)");
+       olsr_printf(0,
+                       "addOrRemoveEntryToFromCumulativeAverage: positionAverageList->counters (before)\n"
+                       "  gpgga   = %llu\n"
+                       "  gpgsa   = %llu\n"
+                       "  gpgsv   = %llu\n"
+                       "  gprmc   = %llu\n"
+                       "  gpvtg   = %llu\n"
+                       "  sigBad  = %llu\n"
+                       "  sigLow  = %llu\n"
+                       "  sigMid  = %llu\n"
+                       "  sigHigh = %llu\n"
+                       "  fixBad  = %llu\n"
+                       "  fix2d   = %llu\n"
+                       "  fix3d   = %llu\n"
+                       "\n",
+                       positionAverageList->counters.gpgga,
+                       positionAverageList->counters.gpgsa,
+                       positionAverageList->counters.gpgsv,
+                       positionAverageList->counters.gprmc,
+                       positionAverageList->counters.gpvtg,
+                       positionAverageList->counters.sigBad,
+                       positionAverageList->counters.sigLow,
+                       positionAverageList->counters.sigMid,
+                       positionAverageList->counters.sigHigh,
+                       positionAverageList->counters.fixBad,
+                       positionAverageList->counters.fix2d,
+                       positionAverageList->counters.fix3d
+       );
+#endif /* PUD_DUMP_AVERAGING */
+
+       if (!add) {
+               assert(positionAverageList->entriesCount >= positionAverageList->entriesMaxCount);
+               assert(entry == getPositionAverageEntry(positionAverageList, OLDEST));
+
+               /* do not touch smask */
+
+               /* do not touch utc */
+
+               /* do not touch sig */
+               /* do not touch fix */
+
+               /* do not touch satinfo */
+       } else {
+               assert(positionAverageList->entriesCount < positionAverageList->entriesMaxCount);
+               assert(entry == getPositionAverageEntry(positionAverageList, INCOMING));
+
+               /* smask at the end */
+
+               /* use the latest utc */
+               cumulative->nmeaInfo.utc = entry->nmeaInfo.utc;
+
+               /* sig at the end */
+               /* fix at the end */
+
+               /* use the latest satinfo */
+               cumulative->nmeaInfo.satinfo = entry->nmeaInfo.satinfo;
+       }
+
+       /* PDOP, HDOP, VDOP */
+       cumulative->nmeaInfo.PDOP += add ? entry->nmeaInfo.PDOP
+                       : -entry->nmeaInfo.PDOP;
+       cumulative->nmeaInfo.HDOP += add ? entry->nmeaInfo.HDOP
+                       : -entry->nmeaInfo.HDOP;
+       cumulative->nmeaInfo.VDOP += add ? entry->nmeaInfo.VDOP
+                       : -entry->nmeaInfo.VDOP;
+
+       /* lat, lon */
+       cumulative->nmeaInfo.lat += add ? entry->nmeaInfo.lat
+                       : -entry->nmeaInfo.lat;
+       cumulative->nmeaInfo.lon += add ? entry->nmeaInfo.lon
+                       : -entry->nmeaInfo.lon;
+
+       /* elv, speed, direction, declination */
+       cumulative->nmeaInfo.elv += add ? entry->nmeaInfo.elv
+                       : -entry->nmeaInfo.elv;
+       cumulative->nmeaInfo.speed += add ? entry->nmeaInfo.speed
+                       : -entry->nmeaInfo.speed;
+       cumulative->nmeaInfo.direction += add ? entry->nmeaInfo.direction
+                       : -entry->nmeaInfo.direction;
+       cumulative->nmeaInfo.declination += add ? entry->nmeaInfo.declination
+                       : -entry->nmeaInfo.declination;
+
+       positionAverageList->entriesCount += (add ? 1 : -1);
+
+       updateCounters(positionAverageList, entry, add);
+       determineCumulativeSmaskSigFix(positionAverageList);
+
+#if defined(PUD_DUMP_AVERAGING)
+       dump_nmeaInfo(&cumulative->nmeaInfo, "addOrRemoveEntryToFromCumulativeAverage: positionAverageList->positionAverageCumulative (after)");
+       olsr_printf(0,
+                       "addOrRemoveEntryToFromCumulativeAverage: positionAverageList->counters (before)\n"
+                       "  gpgga   = %llu\n"
+                       "  gpgsa   = %llu\n"
+                       "  gpgsv   = %llu\n"
+                       "  gprmc   = %llu\n"
+                       "  gpvtg   = %llu\n"
+                       "  sigBad  = %llu\n"
+                       "  sigLow  = %llu\n"
+                       "  sigMid  = %llu\n"
+                       "  sigHigh = %llu\n"
+                       "  fixBad  = %llu\n"
+                       "  fix2d   = %llu\n"
+                       "  fix3d   = %llu\n"
+                       "\n",
+                       positionAverageList->counters.gpgga,
+                       positionAverageList->counters.gpgsa,
+                       positionAverageList->counters.gpgsv,
+                       positionAverageList->counters.gprmc,
+                       positionAverageList->counters.gpvtg,
+                       positionAverageList->counters.sigBad,
+                       positionAverageList->counters.sigLow,
+                       positionAverageList->counters.sigMid,
+                       positionAverageList->counters.sigHigh,
+                       positionAverageList->counters.fixBad,
+                       positionAverageList->counters.fix2d,
+                       positionAverageList->counters.fix3d
+       );
+#endif /* PUD_DUMP_AVERAGING */
+}
+
+/**
+ Update the average position from the cumulative average position. Basically
+ divide all relevant cumulative values by the number of entries in the list.
+
+ @param positionAverageList
+ The position average list
+ */
+static void updatePositionAverageFromCumulative(
+               PositionAverageList * positionAverageList) {
+       double divider = positionAverageList->entriesCount;
+
+#if defined(PUD_DUMP_AVERAGING)
+       dump_nmeaInfo(&positionAverageList->positionAverage.nmeaInfo, "updatePositionAverageFromCumulative: positionAverageList->positionAverage (before)");
+#endif /* PUD_DUMP_AVERAGING */
+
+       memcpy(&positionAverageList->positionAverage,
+                       &positionAverageList->positionAverageCumulative,
+                       sizeof(positionAverageList->positionAverage));
+
+       /* smask: use from cumulative average */
+
+       /* utc: use from cumulative average */
+
+       /* sig: use from cumulative average */
+       /* fix: use from cumulative average */
+
+       if (divider > 1.0) {
+               positionAverageList->positionAverage.nmeaInfo.PDOP /= divider;
+               positionAverageList->positionAverage.nmeaInfo.HDOP /= divider;
+               positionAverageList->positionAverage.nmeaInfo.VDOP /= divider;
+
+               positionAverageList->positionAverage.nmeaInfo.lat /= divider;
+               positionAverageList->positionAverage.nmeaInfo.lon /= divider;
+
+               positionAverageList->positionAverage.nmeaInfo.elv /= divider;
+               positionAverageList->positionAverage.nmeaInfo.speed /= divider;
+               positionAverageList->positionAverage.nmeaInfo.direction /= divider;
+               positionAverageList->positionAverage.nmeaInfo.declination /= divider;
+       }
+
+       /* satinfo: use from average */
+
+#if defined(PUD_DUMP_AVERAGING)
+       dump_nmeaInfo(&positionAverageList->positionAverage.nmeaInfo, "updatePositionAverageFromCumulative: positionAverageList->positionAverage (after)");
+#endif /* PUD_DUMP_AVERAGING */
+}
+
+/**
+ Add a new (incoming) position update to the position average list
+
+ @param positionAverageList
+ The position average list
+ @param newEntry
+ The new (incoming) position update (must be the same as the one returned from
+ the function getPositionAverageEntryForIncoming:INCOMING)
+ */
+void addNewPositionToAverage(PositionAverageList * positionAverageList,
+               PositionUpdateEntry * newEntry) {
+       assert (positionAverageList != NULL);
+       assert (newEntry == getPositionAverageEntry(positionAverageList, INCOMING));
+
+#if defined(PUD_DUMP_AVERAGING)
+       dump_nmeaInfo(&newEntry->nmeaInfo, "addNewPositionToAverage: newEntry");
+       olsr_printf(0, "addNewPositionToAverage: positionAverageList->newestEntryIndex = %llu (before)\n", NEWESTINDEX(positionAverageList));
+       dump_nmeaInfo(&positionAverageList->positionAverageCumulative.nmeaInfo, "addNewPositionToAverage: positionAverageList->positionAverageCumulative.nmeaInfo (before)");
+#endif /* PUD_DUMP_AVERAGING */
+
+       (void) pthread_mutex_lock(&positionAverageList->mutex);
+
+       if (positionAverageList->entriesCount
+                       >= positionAverageList->entriesMaxCount) {
+               /* list is full, so first remove the oldest from the average */
+               addOrRemoveEntryToFromCumulativeAverage(positionAverageList,
+                               getPositionAverageEntry(positionAverageList, OLDEST), false);
+       }
+
+       /* now just add the new position */
+       addOrRemoveEntryToFromCumulativeAverage(positionAverageList, newEntry, true);
+
+       /* update the place where the new entry is stored */
+       positionAverageList->newestEntryIndex
+                       = WRAPINDEX(positionAverageList, NEWESTINDEX(positionAverageList) + 1);
+
+#if defined(PUD_DUMP_AVERAGING)
+       olsr_printf(0, "addNewPositionToAverage: positionAverageList->newestEntryIndex = %llu (after)\n", NEWESTINDEX(positionAverageList));
+       dump_nmeaInfo(&positionAverageList->positionAverageCumulative.nmeaInfo, "addNewPositionToAverage: positionAverageList->positionAverageCumulative.nmeaInfo (before)");
+#endif /* PUD_DUMP_AVERAGING */
+
+       /* update average position */
+       updatePositionAverageFromCumulative(positionAverageList);
+
+#if defined(PUD_DUMP_AVERAGING)
+       dump_nmeaInfo(&positionAverageList->positionAverage.nmeaInfo, "addNewPositionToAverage: positionAverageList->positionAverage.nmeaInfo (after)");
+#endif /* PUD_DUMP_AVERAGING */
+
+       (void) pthread_mutex_unlock(&positionAverageList->mutex);
+}
diff --git a/lib/pud/src/posAvg.h b/lib/pud/src/posAvg.h
new file mode 100644 (file)
index 0000000..488288f
--- /dev/null
@@ -0,0 +1,94 @@
+#ifndef _PUD_POSAVG_H_
+#define _PUD_POSAVG_H_
+
+/* Plugin includes */
+
+/* OLSR includes */
+
+/* System includes */
+#include <nmea/info.h>
+#include <pthread.h>
+#include <stdbool.h>
+
+/** Stores an nmeaINFO entry, used in the averaging */
+typedef struct _PositionUpdateEntry {
+               nmeaINFO nmeaInfo; /**< the position information */
+} PositionUpdateEntry;
+
+/**
+ Counts the number of GPxxx based entries. Some parameters in nmeaINFO are
+ dependent on different GPxxx NMEA sentences. These counters are used to
+ determine which information would be valid for the average position.
+ Also counts the fix values.
+ */
+typedef struct _PositionUpdateCounters {
+               /* smask */
+               unsigned long long gpgga; /**< the number of GPGGA based entries */
+               unsigned long long gpgsa; /**< the number of GPGSA based entries */
+               unsigned long long gpgsv; /**< the number of GPGSV based entries */
+               unsigned long long gprmc; /**< the number of GPRMC based entries */
+               unsigned long long gpvtg; /**< the number of GPVTG based entries */
+
+               /* sig */
+               unsigned long long sigBad; /**< the number of entries with a bad sig */
+               unsigned long long sigLow; /**< the number of entries with a low sig */
+               unsigned long long sigMid; /**< the number of entries with a mid sig */
+               unsigned long long sigHigh; /**< the number of entries with a high sig */
+
+               /* fix */
+               unsigned long long fixBad; /**< the number of entries with a bad fix */
+               unsigned long long fix2d; /**< the number of entries with a 2D fix */
+               unsigned long long fix3d; /**< the number of entries with a 3D fix */
+} PositionUpdateCounters;
+
+/**
+ A list of position updates that are used to determine the average position.
+
+ The list uses 1 extra entry: when 5 entries have to be averaged then the list
+ will have 6 entries. The 6th entry is used for the incoming entry, so that
+ it is already in the list (together with the old entry that will be removed
+ from the average) and does not need to be copied into the list. This is for
+ better performance.
+
+ The list is a circular list.
+ This means that there is a gap/unused entry in the list between the
+ newest entry and the oldest entry, which is the 'incoming entry'.
+
+ Note that 'positionAverageCumulative' stores cumulative values for parameters
+ for which an average is calculated. The reason is to minimise the number of
+ calculations to be performed.
+ */
+typedef struct _PositionAverageList {
+               pthread_mutex_t mutex; /**< access mutex */
+
+               unsigned long long entriesMaxCount; /**< the maximum number of entries in the list */
+               PositionUpdateEntry * entries; /**< the list entries */
+
+               unsigned long long entriesCount; /**< the number of entries in the list */
+               unsigned long long newestEntryIndex; /**< index of the newest entry in the list (zero-based) */
+               PositionUpdateCounters counters; /**< the counters */
+
+               PositionUpdateEntry positionAverageCumulative; /**< the average position with cumulative values */
+               PositionUpdateEntry positionAverage; /**< the average position */
+} PositionAverageList;
+
+/**
+ Enumeration describing the type of an entry position in the average list
+ */
+typedef enum _AverageEntryPositionType {
+       OLDEST, NEWEST, INCOMING, AVERAGECUMULATIVE, AVERAGE
+} AverageEntryPositionType;
+
+bool initPositionAverageList(PositionAverageList * positionAverageList,
+               unsigned long long maxEntries);
+void flushPositionAverageList(PositionAverageList * positionAverageList);
+void destroyPositionAverageList(PositionAverageList * positionAverageList);
+
+PositionUpdateEntry * getPositionAverageEntry(
+               PositionAverageList * positionAverageList,
+               AverageEntryPositionType positionType);
+
+void addNewPositionToAverage(PositionAverageList * positionAverageList,
+               PositionUpdateEntry * newEntry);
+
+#endif /* _PUD_POSAVG_H_ */
diff --git a/lib/pud/src/pud.c b/lib/pud/src/pud.c
new file mode 100644 (file)
index 0000000..88e6d0a
--- /dev/null
@@ -0,0 +1,297 @@
+#include "pud.h"
+
+/* Plugin includes */
+#include "configuration.h"
+#include "networkInterfaces.h"
+#include "dump.h"
+#include "gpsConversion.h"
+#include "receiver.h"
+#include "dedup.h"
+#include "compiler.h"
+
+/* OLSRD includes */
+#include "ipcalc.h"
+#include "parser.h"
+#include "olsr.h"
+
+/* System includes */
+#include <assert.h>
+
+/** The size of the buffer in which the received NMEA string is stored */
+#define BUFFER_SIZE_FOR_OLSR   2048
+
+/** The size of the buffer in which the transmit NMEA string is assembled */
+#define BUFFER_SIZE_FROM_OLSR  512
+
+/** The transmit socket address */
+static union olsr_sockaddr * txAddress;
+
+/** The de-duplication list */
+static DeDupList deDupList;
+
+/**
+ Report a plugin error.
+
+ @param useErrno
+ when true then errno is used in the error message; the error reason is also
+ reported.
+ @param format
+ a pointer to the format string
+ @param ...
+ arguments to the format string
+ */
+void pudError(bool useErrno, const char *format, ...) {
+       char strDesc[256];
+       char *stringErr = NULL;
+
+       if (useErrno) {
+               stringErr = strerror(errno);
+       }
+
+       if ((format == NULL) || (*format == '\0')) {
+               if (useErrno) {
+                       olsr_printf(0, "%s: %s\n", PUD_PLUGIN_ABBR, stringErr);
+               } else {
+                       olsr_printf(0, "%s: Unknown error\n", PUD_PLUGIN_ABBR);
+               }
+       } else {
+               va_list arglist;
+
+               va_start(arglist, format);
+               vsnprintf(strDesc, sizeof(strDesc), format, arglist);
+               va_end(arglist);
+
+               strDesc[sizeof(strDesc) - 1] = '\0'; /* Ensures null termination */
+
+               if (useErrno) {
+                       olsr_printf(0, "%s: %s: %s\n", PUD_PLUGIN_ABBR, strDesc, stringErr);
+               } else {
+                       olsr_printf(0, "%s: %s\n", PUD_PLUGIN_ABBR, strDesc);
+               }
+       }
+}
+
+/**
+ Sends a buffer out on all transmit interfaces
+
+ @param buffer
+ the buffer
+ @param bufferLength
+ the number of bytes in the buffer
+ */
+static void sendToAllTxInterfaces(unsigned char *buffer,
+               unsigned int bufferLength) {
+       TRxTxNetworkInterface *txNetworkInterfaces = getTxNetworkInterfaces();
+       while (txNetworkInterfaces != NULL) {
+               TRxTxNetworkInterface *networkInterface = txNetworkInterfaces;
+
+#ifdef PUD_DUMP_GPS_PACKETS_TX_NON_OLSR
+               olsr_printf(0, "%s: packet sent to non-OLSR interface %s (%u bytes)\n",
+                               PUD_PLUGIN_ABBR, &networkInterface->name[0], bufferLength);
+               dump_packet(&buffer[0], bufferLength);
+#endif
+
+               errno = 0;
+               if (sendto(networkInterface->socketFd, buffer, bufferLength, 0,
+                               (struct sockaddr *) &txAddress->in, sizeof(txAddress->in)) < 0) {
+                       pudError(true, "Transmit error on interface %s",
+                                       (char *) &networkInterface->name);
+               }
+               txNetworkInterfaces = networkInterface->next;
+       }
+}
+
+/**
+ Called by OLSR core when a packet for the plugin is received from the OLSR
+ network. It converts the packet into an NMEA string and transmits it over all
+ transmit non-OLSR network interfaces.
+
+ @param olsrMessage
+ a pointer to the received OLSR message
+ @param in_if
+ a pointer to the OLSR network interface on which the packet was received
+ @param ipaddr
+ a pointer to the IP address of the sender
+
+ @return
+ - true when the packet was processed
+ - false otherwise
+ */
+bool packetReceivedFromOlsr(union olsr_message *olsrMessage,
+               struct interface *in_if __attribute__ ((unused)), union olsr_ip_addr *ipaddr __attribute__ ((unused))) {
+       const union olsr_ip_addr * originator = getOlsrMessageOriginator(
+                       olsr_cnf->ip_version, olsrMessage);
+       unsigned int transmitStringLength;
+       unsigned char buffer[BUFFER_SIZE_FROM_OLSR];
+
+#ifdef PUD_DUMP_GPS_PACKETS_RX_OLSR
+       unsigned short olsrMessageSize =
+                       getOlsrMessageSize(olsr_cnf->ip_version, olsrMessage);
+#endif
+
+       /* when we do not loopback then check if the message originated from this
+        * node: back off */
+       if (!getUseLoopback() && ipequal(originator, &olsr_cnf->main_addr)) {
+               return false;
+       }
+
+       /* do deduplication: when we have already seen this message from the same
+        * originator then just back off */
+       if (likely(getUseDeDup())) {
+               if (isInDeDupList(&deDupList, olsrMessage)) {
+                       return false;
+               }
+
+               addToDeDup(&deDupList, olsrMessage);
+       }
+
+#ifdef PUD_DUMP_GPS_PACKETS_RX_OLSR
+       olsr_printf(0, "\n%s: packet received from OLSR interface %s (%u bytes)\n",
+                       PUD_PLUGIN_ABBR, in_if->int_name, olsrMessageSize);
+       dump_packet((unsigned char *) olsrMessage, olsrMessageSize);
+#endif
+
+       transmitStringLength = gpsFromOlsr(olsrMessage, &buffer[0], sizeof(buffer));
+       if (unlikely(transmitStringLength == 0)) {
+               return false;
+       }
+
+       sendToAllTxInterfaces(&buffer[0], transmitStringLength);
+
+       return true;
+}
+
+/**
+ Called by OLSR core when a packet for the plugin is received from the non-OLSR
+ network. It converts the packet into the internal OLSR wire format for a
+ position update and transmits it over all OLSR network interfaces.
+
+ @param skfd
+ the socket file descriptor on which the packet is received
+ @param data
+ a pointer to the network interface structure on which the packet was received
+ @param flags
+ unused
+ */
+#ifdef PUD_DUMP_GPS_PACKETS_RX_NON_OLSR
+static void packetReceivedForOlsr(int skfd, void *data, unsigned int flags __attribute__ ((unused))) {
+#else
+static void packetReceivedForOlsr(int skfd, void *data __attribute__ ((unused)), unsigned int flags __attribute__ ((unused))) {
+#endif
+       if (skfd >= 0) {
+               unsigned char rxBuffer[BUFFER_SIZE_FOR_OLSR];
+               ssize_t rxCount;
+               struct sockaddr sender;
+               socklen_t senderSize = sizeof(sender);
+
+               assert(data != NULL);
+
+               /* Receive the captured Ethernet frame */
+               memset(&sender, 0, senderSize);
+               errno = 0;
+               rxCount = recvfrom(skfd, &rxBuffer[0], (sizeof(rxBuffer) - 1), 0,
+                               &sender, &senderSize);
+               if (rxCount < 0) {
+                       pudError(true, "Receive error in %s, ignoring message.", __func__);
+                       return;
+               }
+
+               /* make sure the string is null-terminated */
+               rxBuffer[rxCount] = '\0';
+
+               /* only accept messages from configured IP addresses */
+               if (!isRxAllowedSourceIpAddress(&sender)) {
+                       return;
+               }
+
+#ifdef PUD_DUMP_GPS_PACKETS_RX_NON_OLSR
+               {
+                       TRxTxNetworkInterface * networkInterface = data;
+                       void * src;
+                       in_port_t port;
+                       char fromAddr[64];
+
+                       if (olsr_cnf->ip_version == AF_INET) {
+                               src = &((struct sockaddr_in*) &sender)->sin_addr;
+                               port = ntohs(((struct sockaddr_in*) &sender)->sin_port);
+                       } else {
+                               src = &((struct sockaddr_in6*) &sender)->sin6_addr;
+                               port = ntohs(((struct sockaddr_in6*) &sender)->sin6_port);
+                       }
+
+                       inet_ntop(olsr_cnf->ip_version, src, &fromAddr[0], sizeof(fromAddr));
+                       olsr_printf(0, "\n%s: packet received from %s, port %u on non-OLSR"
+                                       " interface %s (%lu bytes)\n", PUD_PLUGIN_ABBR, &fromAddr[0],
+                                       port, &networkInterface->name[0], (size_t) rxCount);
+
+                       dump_packet(&rxBuffer[0], (size_t)rxCount);
+               }
+#endif
+
+               /* we have the received string in the rxBuffer now */
+
+               /* hand the NMEA information to the receiver */
+               (void) receiverUpdateGpsInformation(&rxBuffer[0], rxCount);
+       }
+}
+
+/**
+ Initialise the plugin: check the configuration, initialise the NMEA parser,
+ create network interface sockets, hookup the plugin to OLSR and setup data
+ that can be setup in advance.
+
+ @return
+ - false upon failure
+ - true otherwise
+ */
+bool initPud(void) {
+       if (!checkConfig()) {
+               pudError(false, "Invalid configuration");
+               goto error;
+       }
+
+       initDeDupList(&deDupList, getDeDupDepth());
+
+       /* set global transmit socket config */
+       txAddress = getTxMcAddr();
+
+       if (!startReceiver()) {
+               pudError(false, "Could not start receiver");
+               goto error;
+       }
+
+       /*
+        * Creates receive and transmit sockets and register the receive sockets
+        * with the OLSR stack
+        */
+       if (!createNetworkInterfaces(&packetReceivedForOlsr)) {
+               pudError(false, "Could not create require network interfaces");
+               goto error;
+       }
+
+       if (!checkRunSetup()) {
+               pudError(false, "Invalid configuration");
+               goto error;
+       }
+
+       /*
+        * Tell OLSR to launch olsr_parser when the packets for this plugin
+        * arrive from the OLSR network
+        */
+       olsr_parser_add_function(&packetReceivedFromOlsr, PUD_OLSR_MSG_TYPE);
+
+       return true;
+
+       error: closePud();
+       return false;
+}
+
+/**
+ Stop the plugin: shut down all created network interface sockets and destroy
+ the NMEA parser.
+ */
+void closePud(void) {
+       closeNetworkInterfaces();
+       stopReceiver();
+       destroyDeDupList(&deDupList);
+}
diff --git a/lib/pud/src/pud.h b/lib/pud/src/pud.h
new file mode 100644 (file)
index 0000000..f234be8
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef _PUD_PUD_H
+#define _PUD_PUD_H
+
+/* Plugin includes */
+
+/* OLSR includes */
+#include "olsr_protocol.h"
+#include "interfaces.h"
+#include "olsr_types.h"
+
+/* System includes */
+#include <stdbool.h>
+
+/*
+ * Global
+ */
+
+/** The long plugin name */
+#define PUD_PLUGIN_NAME_LONG                   "OLSRD Position Update Distribution (PUD) plugin"
+
+/** The short plugin name / abbreviation */
+#define PUD_PLUGIN_ABBR                                        "PUD"
+
+/*
+ *  Interface
+ */
+
+bool initPud(void);
+
+void closePud(void);
+
+void pudError(bool useErrno, const char *format, ...) __attribute__ ((format(printf, 2, 3)));
+
+bool packetReceivedFromOlsr(union olsr_message *olsrMessage,
+               struct interface *in_if __attribute__ ((unused)),
+               union olsr_ip_addr *ipaddr __attribute__ ((unused)));
+
+#endif /* _PUD_PUD_H */
diff --git a/lib/pud/src/pudOlsrdPlugin.c b/lib/pud/src/pudOlsrdPlugin.c
new file mode 100644 (file)
index 0000000..a06f16e
--- /dev/null
@@ -0,0 +1,89 @@
+#include "pudOlsrdPlugin.h"
+
+/* Plugin includes */
+#include "pud.h"
+#include "version.h"
+
+/* OLSRD includes */
+#include "defs.h"
+#include "olsr.h"
+
+/* System includes */
+#include <stdbool.h>
+
+/*
+ * OLSR Entrypoints
+ */
+
+/**
+ OLSR entrypoint to initialise the plugin.
+
+ @return
+ - 0 on fail
+ - 1 on success
+ */
+int olsrd_plugin_init(void) {
+       bool retval = initPud();
+       if (retval) {
+               olsr_printf(0, "%s\n", PUD_PLUGIN_NAME_LONG
+#ifdef GIT_SHA
+                               " (" GIT_SHA ")"
+#endif
+               );
+       }
+       return (retval ? 1 : 0);
+}
+
+/**
+ OLSR entrypoint to retrieve the interface version supported by the plugin.
+
+ @return
+ the supported interface version
+ */
+int olsrd_plugin_interface_version(void) {
+       return PUD_PLUGIN_INTERFACE_VERSION;
+}
+
+/**
+ OLSR entrypoint to retrieve the plugin parameter configuration.
+
+ @param params
+ a pointer to a variable in which the function stores a pointer to the
+ plugin parameter configuration
+ @param size
+ a pointer to a variable in which the function stores the number of rows of the
+ plugin parameter configuration
+ */
+void olsrd_get_plugin_parameters(const struct olsrd_plugin_parameters **params,
+               int *size) {
+       *params = &plugin_parameters[0];
+       *size = ARRAYSIZE(plugin_parameters);
+}
+
+/*
+ * Shared Library Entrypoints
+ */
+
+/**
+ Shared library entrypoint declaration for initialisation
+ */
+static void __attribute__ ((constructor)) pud_init(void);
+
+/**
+ Shared library entrypoint declaration for destruction
+ */
+static void __attribute__ ((destructor)) pud_fini(void);
+
+
+/**
+ Shared library entrypoint for initialisation
+ */
+static void pud_init(void) {
+}
+
+/**
+ Shared library entrypoint for destruction
+ */
+static void pud_fini(void) {
+       closePud();
+}
diff --git a/lib/pud/src/pudOlsrdPlugin.h b/lib/pud/src/pudOlsrdPlugin.h
new file mode 100644 (file)
index 0000000..4d781de
--- /dev/null
@@ -0,0 +1,49 @@
+#ifndef _PUD_OLSRD_PLUGIN_H_
+#define _PUD_OLSRD_PLUGIN_H_
+
+/* Plugin includes */
+#include "configuration.h"
+
+/* OLSRD includes */
+#include "olsrd_plugin.h"
+
+/* System includes */
+#include <stddef.h>
+
+/** The interface version supported by the plugin */
+#define PUD_PLUGIN_INTERFACE_VERSION   5
+
+/**
+ The plugin parameter configuration, containing the parameter names, pointers
+ to their setters, and an optional data pointer that is given to the setter
+ when it is called.
+ */
+static const struct olsrd_plugin_parameters plugin_parameters[] = {
+       {       .name = PUD_NODE_ID_TYPE_NAME, .set_plugin_parameter = &setNodeIdType, .data = NULL},
+       {       .name = PUD_NODE_ID_NAME, .set_plugin_parameter = &setNodeId, .data = NULL},
+       {       .name = PUD_RX_NON_OLSR_IF_NAME, .set_plugin_parameter = &addRxNonOlsrInterface, .data = NULL},
+       {       .name = PUD_RX_ALLOWED_SOURCE_IP_NAME, .set_plugin_parameter = &addRxAllowedSourceIpAddress, .data = NULL},
+       {       .name = PUD_RX_MC_ADDR_NAME, .set_plugin_parameter = &setRxMcAddr, .data = NULL},
+       {       .name = PUD_RX_MC_PORT_NAME, .set_plugin_parameter = &setRxMcPort, .data = NULL},
+       {       .name = PUD_TX_NON_OLSR_IF_NAME, .set_plugin_parameter = &addTxNonOlsrInterface, .data = NULL},
+       {       .name = PUD_TX_MC_ADDR_NAME, .set_plugin_parameter = &setTxMcAddr, .data = NULL},
+       {       .name = PUD_TX_MC_PORT_NAME, .set_plugin_parameter = &setTxMcPort, .data = NULL},
+       {       .name = PUD_TX_TTL_NAME, .set_plugin_parameter = &setTxTtl, .data = NULL},
+       {       .name = PUD_TX_NMEAMESSAGEPREFIX_NAME, .set_plugin_parameter = &setTxNmeaMessagePrefix, .data = NULL},
+       {       .name = PUD_OLSR_TTL_NAME, .set_plugin_parameter = &setOlsrTtl, .data = NULL},
+       {       .name = PUD_UPDATE_INTERVAL_STATIONARY_NAME, .set_plugin_parameter = &setUpdateIntervalStationary, .data = NULL},
+       {       .name = PUD_UPDATE_INTERVAL_MOVING_NAME, .set_plugin_parameter = &setUpdateIntervalMoving, .data = NULL},
+       {       .name = PUD_MOVING_SPEED_THRESHOLD_NAME, .set_plugin_parameter = &setMovingSpeedThreshold, .data = NULL},
+       {       .name = PUD_MOVING_DISTANCE_THRESHOLD_NAME, .set_plugin_parameter = &setMovingDistanceThreshold, .data = NULL},
+       {       .name = PUD_DOP_MULTIPLIER_NAME, .set_plugin_parameter = &setDopMultiplier, .data = NULL},
+       {       .name = PUD_DEFAULT_HDOP_NAME, .set_plugin_parameter = &setDefaultHdop, .data = NULL},
+       {       .name = PUD_DEFAULT_VDOP_NAME, .set_plugin_parameter = &setDefaultVdop, .data = NULL},
+       {       .name = PUD_AVERAGE_DEPTH_NAME, .set_plugin_parameter = &setAverageDepth, .data = NULL},
+       {       .name = PUD_HYSTERESIS_COUNT_2STAT_NAME, .set_plugin_parameter = &setHysteresisCountToStationary, .data = NULL},
+       {       .name = PUD_HYSTERESIS_COUNT_2MOV_NAME, .set_plugin_parameter = &setHysteresisCountToMoving, .data = NULL},
+       {       .name = PUD_USE_DEDUP_NAME, .set_plugin_parameter = &setUseDeDup, .data = NULL},
+       {       .name = PUD_DEDUP_DEPTH_NAME, .set_plugin_parameter = &setDeDupDepth, .data = NULL},
+       {       .name = PUD_USE_LOOPBACK_NAME, .set_plugin_parameter = &setUseLoopback, .data = NULL}
+};
+
+#endif /* _PUD_OLSRD_PLUGIN_H_ */
diff --git a/lib/pud/src/receiver.c b/lib/pud/src/receiver.c
new file mode 100644 (file)
index 0000000..bfc1381
--- /dev/null
@@ -0,0 +1,808 @@
+#include "receiver.h"
+
+/* Plugin includes */
+#include "pud.h"
+#include "gpsConversion.h"
+#include "configuration.h"
+#include "dump.h"
+#include "timers.h"
+#include "posAvg.h"
+#include "networkInterfaces.h"
+#include "compiler.h"
+
+/* OLSRD includes */
+#include "net_olsr.h"
+
+/* System includes */
+#include <stddef.h>
+#include <nmea/parser.h>
+#include <nmea/info.h>
+#include <pthread.h>
+#include <nmea/info.h>
+#include <string.h>
+#include <nmea/gmath.h>
+#include <nmea/sentence.h>
+#include <math.h>
+#include <net/if.h>
+#include <assert.h>
+
+/* Debug includes */
+#if defined(PUD_DUMP_GPS_PACKETS_TX_OLSR) || \
+       defined(PUD_DUMP_AVERAGING)
+#include "olsr.h"
+#endif
+
+/*
+ * NMEA parser
+ */
+
+/** The NMEA string parser */
+static nmeaPARSER nmeaParser;
+
+/*
+ * State
+ */
+
+/** Type describing a tri-state boolean */
+typedef enum _TristateBoolean {
+       UNKNOWN = 0,
+       UNSET = 1,
+       SET = 2
+} TristateBoolean;
+
+#define TristateBooleanToString(s)     ((s == SET) ? "set" : \
+                                                                        (s == UNSET) ? "unset" : \
+                                                                        "unknown")
+
+/** Type describing movement state */
+typedef enum _MovementState {
+       STATIONARY = 0,
+       MOVING = 1
+} MovementState;
+
+#define MovementStateToString(s)       ((s == MOVING) ? "moving" : \
+                                                                        "stationary")
+
+/** Type describing state */
+typedef struct _StateType {
+       MovementState internalState; /**< the internal movement state */
+       MovementState externalState; /**< the externally visible movement state */
+       unsigned long long hysteresisCounter; /**< the hysteresis counter external state changes */
+} StateType;
+
+/** The state */
+static StateType state = { .internalState = MOVING, .externalState = MOVING, .hysteresisCounter = 0 };
+
+/** Type describing movement calculations */
+typedef struct _MovementType {
+       TristateBoolean moving; /**< SET: we are moving */
+
+       TristateBoolean overThresholds; /**< SET: at least 1 threshold state is set */
+       TristateBoolean speedOverThreshold; /**< SET: speed is over threshold */
+       TristateBoolean hDistanceOverThreshold; /**< SET: horizontal distance is outside threshold */
+       TristateBoolean vDistanceOverThreshold; /**< SET: vertical distance is outside threshold */
+
+       TristateBoolean outside; /**< SET: at least 1 outside state is SET */
+       TristateBoolean outsideHdop; /**< SET: avg is outside lastTx HDOP */
+       TristateBoolean outsideVdop; /**< SET: avg is outside lastTx VDOP */
+
+       TristateBoolean inside; /**< SET: all inside states are SET */
+       TristateBoolean insideHdop; /**< SET: avg is inside lastTx HDOP */
+       TristateBoolean insideVdop; /**< SET: avg is inside lastTx VDOP */
+} MovementType;
+
+/*
+ * Averaging
+ */
+
+/** The average position with its administration */
+static PositionAverageList positionAverageList;
+
+/*
+ * TX to OLSR
+ */
+
+typedef enum _TimedTxInterface {
+       OLSR = 1
+} TimedTxInterface;
+
+/** Structure of the latest GPS information that is transmitted */
+typedef struct _TransmitGpsInformation {
+       pthread_mutex_t mutex; /**< access mutex */
+       bool updated; /**< true when the information was updated */
+       PositionUpdateEntry txPosition; /**< The last transmitted position */
+} TransmitGpsInformation;
+
+/** The latest position information that is transmitted */
+static TransmitGpsInformation transmitGpsInformation;
+
+/** The last transmitted position.
+ * The same as transmitGpsInformation.txPosition.
+ * We keep this because then we can access the information without locking
+ * mutexes. */
+static PositionUpdateEntry txPosition;
+
+/** The size of the buffer in which the OLSR message is assembled */
+#define TX_BUFFER_SIZE_FOR_OLSR 512
+
+/*
+ * Functions
+ */
+
+/**
+ This function is called every time before a message is sent on a specific
+ interface. It can manipulate the outgoing message.
+ Note that a change to the outgoing messages is carried over to the message
+ that goes out on the next interface when the message is _not_ reset
+ before it is sent out on the next interface.
+
+ @param olsrMessage
+ A pointer to the outgoing message
+ @param ifn
+ A pointer to the OLSR interface structure
+ */
+static void nodeIdPreTransmitHook(union olsr_message *olsrMessage,
+               struct interface *ifn) {
+       /* set the MAC address in the message when needed */
+       if (unlikely(getNodeIdTypeNumber() == PUD_NODEIDTYPE_MAC)) {
+               TOLSRNetworkInterface * olsrIf = getOlsrNetworkInterface(ifn);
+               PudOlsrWireFormat * olsrGpsMessage =
+                               getOlsrMessagePayload(olsr_cnf->ip_version, olsrMessage);
+
+               if (likely(olsrIf != NULL)) {
+                       memcpy(&olsrGpsMessage->nodeInfo.nodeId, &olsrIf->hwAddress[0],
+                                       PUD_NODEIDTYPE_MAC_BYTES);
+               } else {
+                       pudError(false, "Could not find OLSR interface %s, cleared its"
+                               " MAC address in the OLSR message\n", ifn->int_name);
+                       memset(&olsrGpsMessage->nodeInfo.nodeId, 0, PUD_NODEIDTYPE_MAC_BYTES);
+               }
+       }
+}
+
+/**
+ Determine whether s position is valid.
+
+ @param position
+ a pointer to a position
+
+ @return
+ - true when valid
+ - false otherwise
+ */
+static bool positionValid(PositionUpdateEntry * position){
+       return (nmea_INFO_has_field(position->nmeaInfo.smask, FIX)
+                       && (position->nmeaInfo.fix != NMEA_FIX_BAD));
+}
+
+/** Send the transmit buffer out over all designated interfaces, called as a
+ * timer callback and also immediately on an external state change.
+
+ @param interfaces
+ a bitmap defining which interfaces to send over
+ */
+static void txToAllOlsrInterfaces(TimedTxInterface interfaces) {
+       unsigned char txBuffer[TX_BUFFER_SIZE_FOR_OLSR];
+       unsigned int aligned_size = 0;
+
+       (void) pthread_mutex_lock(&transmitGpsInformation.mutex);
+
+       if (!transmitGpsInformation.updated
+                       && positionValid(&transmitGpsInformation.txPosition)) {
+               nmea_time_now(&transmitGpsInformation.txPosition.nmeaInfo.utc);
+       }
+       aligned_size = gpsToOlsr(&transmitGpsInformation.txPosition.nmeaInfo,
+                       (union olsr_message *) &txBuffer[0], sizeof(txBuffer),
+                       ((state.externalState == MOVING) ? getUpdateIntervalMoving()
+                                       : getUpdateIntervalStationary()));
+       transmitGpsInformation.updated = false;
+       (void) pthread_mutex_unlock(&transmitGpsInformation.mutex);
+
+       if (aligned_size > 0) {
+               /* push out to all OLSR interfaces */
+               if ((interfaces & OLSR) != 0) {
+                       int r;
+                       struct interface *ifn;
+                       for (ifn = ifnet; ifn; ifn = ifn->int_next) {
+                               nodeIdPreTransmitHook((union olsr_message *) txBuffer, ifn);
+                               r = net_outbuffer_push(ifn, &txBuffer[0], aligned_size);
+                               if (r != (int) aligned_size) {
+                                       pudError(
+                                                       false,
+                                                       "Could not send to OLSR interface %s: %s"
+                                                               " (aligned_size=%u, r=%d)",
+                                                       ifn->int_name,
+                                                       ((r == -1) ? "no buffer was found"
+                                                                       : (r == 0) ? "there was not enough room in the buffer"
+                                                                                       : "unknown reason"), aligned_size, r);
+                               }
+#ifdef PUD_DUMP_GPS_PACKETS_TX_OLSR
+                               else {
+                                       olsr_printf(0, "%s: packet sent to OLSR interface %s (%d bytes)\n",
+                                                       PUD_PLUGIN_ABBR, ifn->int_name, aligned_size);
+                                       dump_packet(&txBuffer[0], aligned_size);
+                               }
+#endif
+                       }
+               }
+
+               /* loopback to tx interface when so configured */
+               if (getUseLoopback()) {
+                       (void) packetReceivedFromOlsr(
+                                       (union olsr_message *) &txBuffer[0], NULL, NULL);
+               }
+       }
+}
+
+/*
+ * Timer Callbacks
+ */
+
+/**
+ The OLSR tx timer callback
+
+ @param context
+ unused
+ */
+static void pud_olsr_tx_timer_callback(void *context __attribute__ ((unused))) {
+       txToAllOlsrInterfaces(OLSR);
+}
+
+/**
+ Detemine whether we are moving by comparing fields from the average
+ position against those of the last transmitted position.
+
+ MUST be called which the position average list locked.
+
+ @param avg
+ the average position
+ @param lastTx
+ the last transmitted position
+ @param result
+ the results of all movement criteria
+ */
+static void detemineMoving(PositionUpdateEntry * avg,
+               PositionUpdateEntry * lastTx, MovementType * result) {
+       /* avg field presence booleans */
+       bool avgHasSpeed;
+       bool avgHasPos;
+       bool avgHasHdop;
+       bool avgHasElv;
+       bool avgHasVdop;
+
+       /* lastTx field presence booleans */bool lastTxHasPos;
+       bool lastTxHasHdop;
+       bool lastTxHasElv;
+       bool lastTxHasVdop;
+
+       /* these have defaults */
+       double dopMultiplier;
+       double avgHdop;
+       double lastTxHdop;
+       double avgVdop;
+       double lastTxVdop;
+
+       /* calculated values and their validity booleans */
+       double hDistance;
+       double vDistance;
+       double hdopDistanceForOutside;
+       double hdopDistanceForInside;
+       double vdopDistanceForOutside;
+       double vdopDistanceForInside;
+       bool hDistanceValid;
+       bool hdopDistanceValid;
+       bool vDistanceValid;
+       bool vdopDistanceValid;
+
+       /* clear outputs */
+       memset(result, UNKNOWN, sizeof(MovementType));
+
+       /*
+        * Validity
+        *
+        * avg  last  movingNow
+        *  0     0   UNKNOWN : can't determine whether we're moving
+        *  0     1   UNKNOWN : can't determine whether we're moving
+        *  1     0   MOVING  : always seen as movement
+        *  1     1   determine via other parameters
+        */
+
+       if (!positionValid(avg)) {
+               /* everything is unknown */
+               return;
+       }
+
+       /* avg is valid here */
+
+       if (!positionValid(lastTx)) {
+               result->moving = SET;
+               /* the rest is unknown */
+               return;
+       }
+
+       /* both avg and lastTx are valid here */
+
+       /* avg field presence booleans */
+       avgHasSpeed = nmea_INFO_has_field(avg->nmeaInfo.smask, SPEED);
+       avgHasPos = nmea_INFO_has_field(avg->nmeaInfo.smask, LAT)
+                       && nmea_INFO_has_field(avg->nmeaInfo.smask, LON);
+       avgHasHdop = nmea_INFO_has_field(avg->nmeaInfo.smask, HDOP);
+       avgHasElv = nmea_INFO_has_field(avg->nmeaInfo.smask, ELV);
+       avgHasVdop = nmea_INFO_has_field(avg->nmeaInfo.smask, VDOP);
+
+       /* lastTx field presence booleans */
+       lastTxHasPos = nmea_INFO_has_field(lastTx->nmeaInfo.smask, LAT)
+                       && nmea_INFO_has_field(lastTx->nmeaInfo.smask, LON);
+       lastTxHasHdop = nmea_INFO_has_field(lastTx->nmeaInfo.smask, HDOP);
+       lastTxHasElv = nmea_INFO_has_field(lastTx->nmeaInfo.smask, ELV);
+       lastTxHasVdop = nmea_INFO_has_field(lastTx->nmeaInfo.smask, VDOP);
+
+       /* fill in some values _or_ defaults */
+       dopMultiplier = getDopMultiplier();
+       avgHdop = avgHasHdop ? avg->nmeaInfo.HDOP : getDefaultHdop();
+       lastTxHdop = lastTxHasHdop ? lastTx->nmeaInfo.HDOP : getDefaultHdop();
+       avgVdop = avgHasVdop ? avg->nmeaInfo.VDOP : getDefaultVdop();
+       lastTxVdop = lastTxHasVdop ? lastTx->nmeaInfo.VDOP : getDefaultVdop();
+
+       /*
+        * Calculations
+        */
+
+       /* hDistance */
+       if (avgHasPos && lastTxHasPos) {
+               nmeaPOS avgPos;
+               nmeaPOS lastTxPos;
+
+               avgPos.lat = nmea_degree2radian(avg->nmeaInfo.lat);
+               avgPos.lon = nmea_degree2radian(avg->nmeaInfo.lon);
+
+               lastTxPos.lat = nmea_degree2radian(lastTx->nmeaInfo.lat);
+               lastTxPos.lon = nmea_degree2radian(lastTx->nmeaInfo.lon);
+
+               hDistance = nmea_distance_ellipsoid(&avgPos, &lastTxPos, NULL, NULL);
+               hDistanceValid = true;
+       } else {
+               hDistanceValid = false;
+       }
+
+       /* hdopDistance */
+       if (avgHasHdop || lastTxHasHdop) {
+               hdopDistanceForOutside = dopMultiplier * (lastTxHdop + avgHdop);
+               hdopDistanceForInside = dopMultiplier * (lastTxHdop - avgHdop);
+               hdopDistanceValid = true;
+       } else {
+               hdopDistanceValid = false;
+       }
+
+       /* vDistance */
+       if (avgHasElv && lastTxHasElv) {
+               vDistance = fabs(lastTx->nmeaInfo.elv - avg->nmeaInfo.elv);
+               vDistanceValid = true;
+       } else {
+               vDistanceValid = false;
+       }
+
+       /* vdopDistance */
+       if (avgHasVdop || lastTxHasVdop) {
+               vdopDistanceForOutside = dopMultiplier * (lastTxVdop + avgVdop);
+               vdopDistanceForInside = dopMultiplier * (lastTxVdop - avgVdop);
+               vdopDistanceValid = true;
+       } else {
+               vdopDistanceValid = false;
+       }
+
+       /*
+        * Moving Criteria Evaluation Start
+        * We compare the average position against the last transmitted position.
+        */
+
+       /* Speed */
+       if (avgHasSpeed) {
+               if (avg->nmeaInfo.speed >= getMovingSpeedThreshold()) {
+                       result->speedOverThreshold = SET;
+               } else {
+                       result->speedOverThreshold = UNSET;
+               }
+       }
+
+       /*
+        * Position
+        *
+        * avg  last  hDistanceMoving
+        *  0     0   determine via other parameters
+        *  0     1   determine via other parameters
+        *  1     0   MOVING
+        *  1     1   determine via distance threshold and HDOP
+        */
+       if (avgHasPos && !lastTxHasPos) {
+               result->hDistanceOverThreshold = SET;
+       } else if (hDistanceValid) {
+               if (hDistance >= getMovingDistanceThreshold()) {
+                       result->hDistanceOverThreshold = SET;
+               } else {
+                       result->hDistanceOverThreshold = UNSET;
+               }
+
+               /*
+                * Position with HDOP
+                *
+                * avg  last  movingNow
+                *  0     0   determine via other parameters
+                *  0     1   determine via position with HDOP (avg has default HDOP)
+                *  1     0   determine via position with HDOP (lastTx has default HDOP)
+                *  1     1   determine via position with HDOP
+                */
+               if (hdopDistanceValid) {
+                       /* we are outside the HDOP when the HDOPs no longer overlap */
+                       if (hDistance > hdopDistanceForOutside) {
+                               result->outsideHdop = SET;
+                       } else {
+                               result->outsideHdop = UNSET;
+                       }
+
+                       /* we are inside the HDOP when the HDOPs fully overlap */
+                       if (hDistance <= hdopDistanceForInside) {
+                               result->insideHdop = SET;
+                       } else {
+                               result->insideHdop = UNSET;
+                       }
+               }
+       }
+
+       /*
+        * Elevation
+        *
+        * avg  last  movingNow
+        *  0     0   determine via other parameters
+        *  0     1   determine via other parameters
+        *  1     0   MOVING
+        *  1     1   determine via distance threshold and VDOP
+        */
+       if (avgHasElv && !lastTxHasElv) {
+               result->vDistanceOverThreshold = SET;
+       } else if (vDistanceValid) {
+               if (vDistance >= getMovingDistanceThreshold()) {
+                       result->vDistanceOverThreshold = SET;
+               } else {
+                       result->vDistanceOverThreshold = UNSET;
+               }
+
+               /*
+                * Elevation with VDOP
+                *
+                * avg  last  movingNow
+                *  0     0   determine via other parameters
+                *  0     1   determine via elevation with VDOP (avg has default VDOP)
+                *  1     0   determine via elevation with VDOP (lastTx has default VDOP)
+                *  1     1   determine via elevation with VDOP
+                */
+               if (vdopDistanceValid) {
+                       /* we are outside the VDOP when the VDOPs no longer overlap */
+                       if (vDistance > vdopDistanceForOutside) {
+                               result->outsideVdop = SET;
+                       } else {
+                               result->outsideVdop = UNSET;
+                       }
+
+                       /* we are inside the VDOP when the VDOPs fully overlap */
+                       if (vDistance <= vdopDistanceForInside) {
+                               result->insideVdop = SET;
+                       } else {
+                               result->insideVdop = UNSET;
+                       }
+               }
+       }
+
+       /*
+        * Moving Criteria Evaluation End
+        */
+
+       /* accumulate inside criteria */
+       if ((result->insideHdop == SET) && (result->insideVdop == SET)) {
+               result->inside = SET;
+       } else if ((result->insideHdop == UNSET) || (result->insideVdop == UNSET)) {
+               result->inside = UNSET;
+       }
+
+       /* accumulate outside criteria */
+       if ((result->outsideHdop == SET) || (result->outsideVdop == SET)) {
+               result->outside = SET;
+       } else if ((result->outsideHdop == UNSET)
+                       || (result->outsideVdop == UNSET)) {
+               result->outside = UNSET;
+       }
+
+       /* accumulate threshold criteria */
+       if ((result->speedOverThreshold == SET)
+                       || (result->hDistanceOverThreshold == SET)
+                       || (result->vDistanceOverThreshold == SET)) {
+               result->overThresholds = SET;
+       } else if ((result->speedOverThreshold == UNSET)
+                       || (result->hDistanceOverThreshold == UNSET)
+                       || (result->vDistanceOverThreshold == UNSET)) {
+               result->overThresholds = UNSET;
+       }
+
+       /* accumulate moving criteria */
+       if ((result->overThresholds == SET) || (result->outside == SET)) {
+               result->moving = SET;
+       } else if ((result->overThresholds == UNSET)
+                       && (result->outside == UNSET)) {
+               result->moving = UNSET;
+       }
+
+       return;
+}
+
+/**
+ Update the latest GPS information. This function is called when a packet is
+ received from a rxNonOlsr interface, containing one or more NMEA strings with
+ GPS information.
+
+ @param rxBuffer
+ the receive buffer with the received NMEA string(s)
+ @param rxCount
+ the number of bytes in the receive buffer
+
+ @return
+ - false on failure
+ - true otherwise
+ */
+bool receiverUpdateGpsInformation(unsigned char * rxBuffer, size_t rxCount) {
+       static const char * rxBufferPrefix = "$GP";
+       static const size_t rxBufferPrefixLength = 3;
+
+       bool retval = false;
+       PositionUpdateEntry * incomingEntry;
+       MovementState newState = MOVING;
+       PositionUpdateEntry * posAvgEntry;
+       MovementType movementResult;
+       TristateBoolean movingNow;
+       bool internalStateChange = false;
+       bool externalStateChange = false;
+       bool updateTransmitGpsInformation = false;
+
+       /* do not process when the message does not start with $GP */
+       if ((rxCount < rxBufferPrefixLength) || (strncmp((char *) rxBuffer,
+                       rxBufferPrefix, rxBufferPrefixLength) != 0)) {
+               return true;
+       }
+
+       (void) pthread_mutex_lock(&positionAverageList.mutex);
+
+       /* parse all NMEA strings in the rxBuffer into the incoming entry */
+       incomingEntry = getPositionAverageEntry(&positionAverageList, INCOMING);
+       nmea_zero_INFO(&incomingEntry->nmeaInfo);
+       nmea_parse(&nmeaParser, (char *) rxBuffer, rxCount,
+                       &incomingEntry->nmeaInfo);
+
+#if defined(PUD_DUMP_AVERAGING)
+       dump_nmeaInfo(&incomingEntry->nmeaInfo,
+                       "receiverUpdateGpsInformation: incoming entry");
+#endif /* PUD_DUMP_AVERAGING */
+
+       /* ignore when no useful information */
+       if (incomingEntry->nmeaInfo.smask == GPNON) {
+               retval = true;
+               goto end;
+       }
+
+       nmea_INFO_sanitise(&incomingEntry->nmeaInfo);
+
+#if defined(PUD_DUMP_AVERAGING)
+       dump_nmeaInfo(&incomingEntry->nmeaInfo,
+                       "receiverUpdateGpsInformation: incoming entry after sanitise");
+#endif /* PUD_DUMP_AVERAGING */
+
+       /* we always work with latitude, longitude in degrees and DOPs in meters */
+       nmea_INFO_unit_conversion(&incomingEntry->nmeaInfo);
+
+#if defined(PUD_DUMP_AVERAGING)
+       dump_nmeaInfo(&incomingEntry->nmeaInfo,
+                       "receiverUpdateGpsInformation: incoming entry after unit conversion");
+#endif /* PUD_DUMP_AVERAGING */
+
+       /*
+        * Averageing
+        */
+
+       if (state.internalState == MOVING) {
+               /* flush average: keep only the incoming entry */
+               flushPositionAverageList(&positionAverageList);
+       }
+       addNewPositionToAverage(&positionAverageList, incomingEntry);
+       posAvgEntry = getPositionAverageEntry(&positionAverageList, AVERAGE);
+
+       /*
+        * Movement detection
+        */
+
+       detemineMoving(posAvgEntry, &txPosition, &movementResult);
+       movingNow = movementResult.moving;
+
+#if defined(PUD_DUMP_AVERAGING)
+       olsr_printf(0, "receiverUpdateGpsInformation: internalState = %s\n",
+                       MovementStateToString(state.internalState));
+       olsr_printf(0, "receiverUpdateGpsInformation: movingNow     = %s\n",
+                       TristateBooleanToString(movingNow));
+#endif /* PUD_DUMP_AVERAGING */
+
+       /*
+        * Internal State
+        */
+
+       if (movingNow == SET) {
+               newState = MOVING;
+       } else if (movingNow == UNSET) {
+               newState = STATIONARY;
+       }
+       internalStateChange = (state.internalState != newState);
+       state.internalState = newState;
+
+       /*
+        * External State (+ hysteresis)
+        */
+
+       if (internalStateChange) {
+               /* restart hysteresis for external state change when we have an internal
+                * state change */
+               state.hysteresisCounter = 0;
+       }
+
+       /* when internal state and external state are not the same we need to
+        * perform hysteresis before we can propagate the internal state to the
+        * external state */
+       newState = state.externalState;
+       if (state.internalState != state.externalState) {
+               switch (state.internalState) {
+                       case STATIONARY:
+                               /* external state is MOVING */
+
+                               /* delay going to stationary a bit */
+                               state.hysteresisCounter++;
+
+                               if (state.hysteresisCounter
+                                               >= getHysteresisCountToStationary()) {
+                                       /* outside the hysteresis range, go to stationary */
+                                       newState = STATIONARY;
+                               }
+                               break;
+
+                       case MOVING:
+                               /* external state is STATIONARY */
+
+                               /* delay going to moving a bit */
+                               state.hysteresisCounter++;
+
+                               if (state.hysteresisCounter >= getHysteresisCountToMoving()) {
+                                       /* outside the hysteresis range, go to moving */
+                                       newState = MOVING;
+                               }
+                               break;
+
+                       default:
+                               /* when unknown do just as if we transition into moving */
+                               newState = MOVING;
+                               break;
+               }
+       }
+       externalStateChange = (state.externalState != newState);
+       state.externalState = newState;
+
+#if defined(PUD_DUMP_AVERAGING)
+       olsr_printf(0, "receiverUpdateGpsInformation: newState = %s\n",
+                       MovementStateToString(newState));
+       dump_nmeaInfo(&posAvgEntry->nmeaInfo,
+                       "receiverUpdateGpsInformation: posAvgEntry");
+#endif /* PUD_DUMP_AVERAGING */
+
+       /*
+        * Update transmitGpsInformation
+        */
+
+       updateTransmitGpsInformation = externalStateChange
+                       || (positionValid(posAvgEntry) && !positionValid(&txPosition))
+                       || (movementResult.inside == SET);
+
+       if ((state.externalState == MOVING) || updateTransmitGpsInformation) {
+               memcpy(&txPosition.nmeaInfo, &posAvgEntry->nmeaInfo, sizeof(nmeaINFO));
+               (void) pthread_mutex_lock(&transmitGpsInformation.mutex);
+               memcpy(&transmitGpsInformation.txPosition.nmeaInfo,
+                               &posAvgEntry->nmeaInfo, sizeof(nmeaINFO));
+               transmitGpsInformation.updated = true;
+
+#if defined(PUD_DUMP_AVERAGING)
+               dump_nmeaInfo(&transmitGpsInformation.txPosition.nmeaInfo,
+                       "receiverUpdateGpsInformation: transmitGpsInformation");
+#endif /* PUD_DUMP_AVERAGING */
+
+               (void) pthread_mutex_unlock(&transmitGpsInformation.mutex);
+       }
+
+       if (updateTransmitGpsInformation) {
+               TimedTxInterface interfaces = OLSR; /* always send over olsr */
+               if (!restartOlsrTxTimer(
+                               (state.externalState == STATIONARY) ? getUpdateIntervalStationary()
+                               : getUpdateIntervalMoving(), &pud_olsr_tx_timer_callback)) {
+                       pudError(0, "Could not restart OLSR tx timer, no periodic"
+                                       " position updates will be sent to the OLSR network");
+               }
+
+               /* do an immediate transmit */
+               txToAllOlsrInterfaces(interfaces);
+       }
+
+       retval = true;
+
+       end: (void) pthread_mutex_unlock(&positionAverageList.mutex);
+       return retval;
+}
+
+/*
+ * Receiver start/stop
+ */
+
+/**
+ Start the receiver
+
+ @return
+ - false on failure
+ - true otherwise
+ */
+bool startReceiver(void) {
+       pthread_mutexattr_t attr;
+       if (pthread_mutexattr_init(&attr)) {
+               return false;
+       }
+       if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP)) {
+               return false;
+       }
+       if (pthread_mutex_init(&transmitGpsInformation.mutex, &attr)) {
+               return false;
+       }
+
+       if (!nmea_parser_init(&nmeaParser)) {
+               pudError(false, "Could not initialise NMEA parser");
+               return false;
+       }
+
+       nmea_zero_INFO(&transmitGpsInformation.txPosition.nmeaInfo);
+       transmitGpsInformation.updated = false;
+
+       nmea_zero_INFO(&txPosition.nmeaInfo);
+
+       state.internalState = MOVING;
+       state.externalState = MOVING;
+       state.hysteresisCounter = 0;
+
+       initPositionAverageList(&positionAverageList, getAverageDepth());
+
+       if (!initOlsrTxTimer()) {
+               stopReceiver();
+               return false;
+       }
+
+       return true;
+}
+
+/**
+ Stop the receiver
+ */
+void stopReceiver(void) {
+       destroyOlsrTxTimer();
+
+       destroyPositionAverageList(&positionAverageList);
+
+       state.hysteresisCounter = 0;
+       state.externalState = MOVING;
+       state.internalState = MOVING;
+
+       nmea_zero_INFO(&txPosition.nmeaInfo);
+
+       transmitGpsInformation.updated = false;
+       nmea_zero_INFO(&transmitGpsInformation.txPosition.nmeaInfo);
+
+       nmea_parser_destroy(&nmeaParser);
+
+       (void) pthread_mutex_destroy(&transmitGpsInformation.mutex);
+}
diff --git a/lib/pud/src/receiver.h b/lib/pud/src/receiver.h
new file mode 100644 (file)
index 0000000..62f27d7
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef _PUD_RECEIVER_H_
+#define _PUD_RECEIVER_H_
+
+/* Plugin includes */
+
+/* OLSRD includes */
+
+/* System includes */
+#include <stddef.h>
+#include <stdbool.h>
+
+bool startReceiver(void);
+void stopReceiver(void);
+
+bool receiverUpdateGpsInformation(unsigned char * rxBuffer, size_t rxCount);
+
+#endif /* _PUD_RECEIVER_H_ */
diff --git a/lib/pud/src/timers.c b/lib/pud/src/timers.c
new file mode 100644 (file)
index 0000000..e30949c
--- /dev/null
@@ -0,0 +1,102 @@
+#include "timers.h"
+
+/* Plugin includes */
+#include "pud.h"
+
+/* OLSRD includes */
+#include "olsr_cookie.h"
+
+/* System includes */
+#include <stddef.h>
+#include <stdbool.h>
+
+/*
+ * OLSR Tx Timer
+ */
+
+/** The timer cookie, used to trace back the originator in debug */
+static struct olsr_cookie_info *pud_olsr_tx_timer_cookie = NULL;
+
+/** The timer */
+static struct timer_entry * pud_olsr_tx_timer = NULL;
+
+/**
+ Start the OLSR tx timer. Does nothing when the timer is already running.
+
+ @param interval
+ The interval in seconds
+ @param cb_func
+ The callback function to call when the timer expires
+
+ @return
+ - false on failure
+ - true otherwise
+ */
+static int startOlsrTxTimer(unsigned long long interval, timer_cb_func cb_func) {
+       if (pud_olsr_tx_timer == NULL) {
+               pud_olsr_tx_timer = olsr_start_timer(interval * MSEC_PER_SEC, 0,
+                               OLSR_TIMER_PERIODIC, cb_func, NULL,
+                               pud_olsr_tx_timer_cookie);
+               if (pud_olsr_tx_timer == NULL) {
+                       return false;
+               }
+       }
+
+       return true;
+}
+
+/**
+ Stop the OLSR tx timer
+ */
+static void stopOlsrTxTimer(void) {
+       if (pud_olsr_tx_timer != NULL) {
+             &