From c225318685067166d127a6929b0304fa066a9133 Mon Sep 17 00:00:00 2001 From: ben Date: Thu, 24 Apr 2014 10:03:07 -0500 Subject: [PATCH 1/3] Merge remote-tracking branch 'olsr/release-0.6.6' into commotion Conflicts: CHANGELOG Makefile gui/win32/Inst/installer.nsi gui/win32/Main/Frontend.rc make/hash_source.sh --- CHANGELOG | 204 ++++++- Makefile | 50 +- Makefile.inc | 16 +- README-Olsr-Extensions | 42 +- contrib/netsimpcap/src/netsimpcap.c | 4 + doc/Makefile | 20 +- files/olsrd.conf.commotion | 19 +- files/olsrd.conf.default.full | 54 ++ files/olsrd.conf.default.lq | 54 ++ files/olsrd.conf.default.lq-fisheye | 54 ++ files/olsrd.ufw.profile | 4 + files/sgw_policy_routing_setup.sh | 177 ++++++ gui/linux-gtk/Makefile | 11 + gui/linux-gtk/src/main.c | 2 + gui/win32/Inst/installer.nsi | 2 +- gui/win32/Main/Frontend.rc | 2 +- lib/arprefresh/README_ARPREFRESH | 6 +- lib/arprefresh/src/olsrd_arprefresh.c | 51 +- lib/bmf/Makefile | 2 - lib/dot_draw/README_DOT_DRAW | 4 +- lib/dyn_gw/Makefile | 5 +- lib/dyn_gw/src/olsrd_dyn_gw.c | 3 - lib/httpinfo/src/olsrd_httpinfo.c | 3 +- lib/jsoninfo/src/olsrd_jsoninfo.c | 28 +- lib/mdns/Makefile | 18 +- lib/mdns/src/NetworkInterfaces.c | 4 +- lib/mdns/src/RouterElection.c | 1 - lib/mdns/src/container_of.h | 2 +- lib/mdns/src/mdns.c | 5 +- lib/p2pd/Makefile | 18 +- lib/p2pd/src/p2pd.c | 1 - lib/pud/doc/pud.odt | Bin 71360 -> 73850 bytes lib/pud/nmealib/Makefile | 2 +- lib/pud/nmealib/Makefile.inc | 15 +- lib/pud/nmealib/src/parse.c | 69 ++- lib/pud/nmealib/src/tok.c | 17 +- lib/pud/src/configTools.c | 9 +- lib/pud/src/configuration.c | 5 - lib/pud/src/netTools.h | 23 +- lib/pud/src/pud.c | 3 +- lib/pud/src/uplinkGateway.c | 14 +- lib/pud/wireformat-java/Makefile | 34 +- lib/pud/wireformat/Makefile | 14 +- lib/quagga/README_QUAGGA | 4 +- lib/quagga/src/common.h | 7 + lib/quagga/src/plugin.c | 7 +- lib/quagga/src/quagga.c | 4 + lib/txtinfo/README_TXTINFO | 1 + lib/txtinfo/src/olsrd_txtinfo.c | 1 + make/Makefile.win32 | 4 +- make/hash_source.sh | 10 +- src/bsd/dummy.c | 5 +- src/builddata.h | 48 ++ src/cfgparser/cfgfile_gen.c | 83 +++ src/cfgparser/olsrd_conf.c | 137 +++++ src/cfgparser/oparse.y | 132 +++++ src/cfgparser/oscan.lex | 35 ++ src/defs.h | 4 - src/gateway.c | 780 +++++++++++++++++++++++--- src/gateway.h | 12 +- src/gateway_default_handler.c | 208 +++---- src/gateway_list.c | 187 ++++++ src/gateway_list.h | 152 +++++ src/kernel_routes.h | 2 +- src/kernel_tunnel.h | 2 +- src/linux/kernel_routes_nl.c | 4 +- src/linux/kernel_tunnel.c | 22 +- src/lq_packet.c | 10 +- src/main.c | 29 +- src/neighbor_table.c | 9 +- src/olsr.c | 1 + src/olsr_cfg.h | 25 +- src/olsr_switch/main.c | 8 +- src/process_package.c | 20 +- src/scheduler.c | 2 +- src/win32/arpa/inet.h | 2 + src/win32/compat.c | 2 + src/win32/dummy.c | 5 +- 78 files changed, 2598 insertions(+), 437 deletions(-) create mode 100644 files/olsrd.ufw.profile create mode 100755 files/sgw_policy_routing_setup.sh create mode 100644 src/builddata.h create mode 100644 src/gateway_list.c create mode 100644 src/gateway_list.h diff --git a/CHANGELOG b/CHANGELOG index e1259f0b..59669e4c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,8 +1,162 @@ -0.6.5.4 ------------------------------------------------------------------- +0.6.6.1 ------------------------------------------------------------------- -Ferry Huberts (11): +Henning Rogge (2): + Update version after release of v0.6.6 + Release v0.6.6.1 + +Manuel Munz (1): + Fix jsoninfo again after a regression which was introduced with + +0.6.6 ------------------------------------------------------------------- + +Bastian Bittorf (3): + [hotfix] plugin: httpinfo: in case of an unrecoverable error ("could not + initialize HTTP socket") exit for returnvalue != 0 + plugin: dot_draw: readme: make it clear, that it only opens an + IPv4-socket, so a 'telnet ::1 2004' is not possible + plugin: dot_draw: readme: replaced a b0rken link + +Ferry Huberts (146): + Update version after branch of v0.6.5 + gateway: store current gateway(s) in a struct + gateway: lazily allocate the current gateway structs + gateway: add path_cost to 'set gateway' interface + gateway: add interface to get the gateway costs + gateway: simplify by using the new costs interface + gateway: add gateway_list.{c,h} + gateway: keep track of the active gateways in a list + Merge branch 'release-0.6.5' + pud: java make target is dependent on library make target + Merge branch 'release-0.6.5' + Merge branch 'release-0.6.5' + json_info: only output smart gateway info on Linux + kernel_route: olsr_os_inetgw_tunnel_route can now take the table + gateway: let the gateway code determine the tunnel name + gateway: remove the worst gateway before adding new one + gateway: add SmartGatewayUseCount configuration parameter + gateway: use SmartGatewayUseCount setting the the gateway lists + gateway: add SmartGatewayEgressInterfaces configuration parameter + gateway: add SmartGatewayMarkOffset{Egress,Tunnels} configuration + parameters + gateway: add SmartGatewayPolicyRoutingScript configuration parameter + gateway: initialise a set of fixed tunnel names in/for multi-gateway mode + gateway: initialise the egress interface names in/for multi-gateway mode + gateway: use fixed tunnel names in/for multi-gateway mode + gateway: setup and clear table specific default routes in/for + multi-gateway mode + gateway: setup/cleanup multi-gateway mode during startup/shutdown of olsrd + gateway: introduce and use MULTI_GW_MODE define + gateway: enable multi-gateway mode + gateway: convert some defines into inline functions + kernel_route: olsr_os_inetgw_tunnel_route now needs the table explicitly + Merge branch 'multigw' + gateway: remove 2 superfluous asserts + gateway: add an assert to prevent an 'unused' warning + gateway: also adjust the IPv6 gateway list in olsr_update_gateway_entry + gateway: fix policy script permissions checking + gateway: the gateway policy script really is a bash script + gateway: minor indentation fixes in the gateway script + gateway: fix copy/paste mistake in setting up the egress rules + gateway: remove policy rules before adding them + gateway: ignore errors in remove-before-add rule removals + Revert "linux: name is required to be non-null in + olsr_os_cleanup_iptunnel" + gateway: do not show (bogus) errors + gateway: cleanup the server tunnel later + cfgparser: add SmartGatewayAlwaysRemoveServerTunnel configuration + parameter + gateway: use SmartGatewayAlwaysRemoveServerTunnel configuration parameter + gateway: fix a revert problem + gateway: use tnl_(4|6) tunnel names instead of tnl(4|6)_ + gateway: properly set the egress interface names + json_info: do not loop forever on smart gateway egress interfaces + pud: the best uplink gateway is the same as the current (smart) gateway + pud: there is no hard dependency on smart gateway + pud: only forward downlinked messages when the smart gateway system is + enabled + pud: update documentation about smart gateway changes + gateway: fix 2 small typos in a comment + Revert "pud: the best uplink gateway is the same as the current (smart) + gateway" + pud: minor update resulting from the previous revert + pud: minor update to getBestUplinkGateway + gateway: minor improvement to get_unused_iptunnel_name + gateway: update some comments + gateway: remove 2 unneeded asserts + gateway: use standard macro to loop over the gateway tree in + olsr_cleanup_gateways + gateway: fix 'eligible' conditions in gw_default_choose_gateway + gateway: minor speed improvement in gw_default_choose_gateway + android: fix the path of the tunnel node in the /dev tree + pud: use the proper __ANDROID__ define + gateway: allow advertisement of zero bandwidth + gateway: add some comments + gateway: also set (update) the gateway when costs have changed + gateway: find_interfaceName only works in multi-gateway mode + gateway: simplify gw_default_calc_threshold + gateway: improve comments of gw_default_weigh_costs a bit + gateway: rename some variables to make it more readable + gateway: fix stable count dampening in gw_default_timer + gateway: do not use bitwise operators for logic conditions + gateway: reverse semantics of two variables + gateway: always select a new gateway when threshold != 0 + main: add -pidfile command line option + main: fix pidfile compilation on windows + Merge branch 'release-0.6.5' + make: enable 32/64 bit cross compilation + pud: pull in nmealib v1.0.4 + gateway: remove some 'unused' code from sgw script + make: fix typos in 'enable 32/64 bit cross compilation' commit + pud: enable 32/64 bit cross compilation for the java build + make: ifdef fixups + Merge branch 'release-0.6.5' + Merge branch 'release-0.6.5' + Merge branch 'release-0.6.5' + pud: detect the java include directory + dyn_gw: remove bogus include + make: fix a type in the Linux SUBDIRS assignment + make: add dyn_gw, mdns and p2pd to the Android SUBDIRS + pud: update nmealib 32/64 cross compilation + Revert "pud: update nmealib 32/64 cross compilation" + main: fix 2 minor warnings for win64 build on debian squeeze + make: only install sgw related files when sgw is supported + mdns: fix container_of pointer arith + mdns: don't use non-standard ushot type + mdns: fix three alignment warnings + mdns: supported on Android + p2pd: supported on Android + make: ensure win32 install respects DESTDIR + Merge branch 'release-0.6.5' + gateway: work around kernel IPIP module initialisation bug + gateway: fix overlap in success/fail IPIP tunnel init + doc: make the documentation generation commands a bit more verbose + pud: detect the java include directory + pud: better detection of java jdk + pud: better detection of java jdk + gateway: work around kernel IPIP module initialisation bug + Merge branch 'release-0.6.5' + PUD: nmealib: fix parsing of date in GPRMC + pud: nmealib: fixup the previous commit + main: use /dev/urandom by default + main: use /dev/urandom by default + pud: set local loopback for multicast tx + pud: set local loopback for multicast tx + Merge branch 'release-0.6.5' + arprefresh: fix some spelling + arprefresh: document the BPF program to make it readable + arprefresh: use the ARRAYSIZE macro + arprefresh: remove a comment + arprefresh: do not process IPv6 packets when in IPv4 mode + arprefresh: add a note about VLANs and IPv6 + remove pthread usage as much as possible + Branch v0.6.6 Remove mercurial ignore file; we use git build: ignore builddata.c when hashing sources + build: move build data into its own include file + gateway: always deal with 64bit scaled path costs + gateway: introduce and use removeGatewayFromList function + gateway: add SmartGatewayTakeDownPercentage configuration parameter + gateway: proactively take down 'expensive' gateways release: fix the list of generated files release: update some comments release: refactor the checkVersionIncrementing function @@ -12,26 +166,42 @@ Ferry Huberts (11): release: also check against the Makefile version when branching txtinfo: prevent buffer overflow pud: nmealib: _nmea_parse_time not parsing hsec when given "hhmmss" format + build: really _ignore_ builddata.c when hashing sources + build: only print the md5 hash in hash_source.sh + Makefile: fix builddata when building from tarball + pud: fix readIPAddress function + +Hans-Christoph Steiner (3): + GNU/Linux: add service profile for 'ufw' firewall package + update Commotion example olsrd.conf to represent current project state + dyn_gw/dyn_gw_plain require /proc, remove from default build on non-Linux + +Henning Rogge (7): + Fixed debug output of Scheduler polling interval + Fix multicast join for IPv6 + Merge branch 'master' of http://olsr.org/git/olsrd + Update version after release of v0.6.5.2 + Update version after release of v0.6.5.2 + Fix build break for MinGW compilation on Fedora 19. + Release v0.6.6 -Henning Rogge (2): - Update version after release of v0.6.5.3 - Release v0.6.5.4 +Ronald in 't Velt (1): + Fix setsockopt for setting Traffic Class in IPv6 -0.6.5.3 ------------------------------------------------------------------- +Saverio Proto (3): + main: fix the check on inet_pton return value because it returns 1 on + success. + mDNS: remove from Makefile PTHREAD useless dependency + txtinfo: update README file -Ferry Huberts (5): - pud: detect the java include directory - pud: better detection of java jdk - gateway: work around kernel IPIP module initialisation bug - main: use /dev/urandom by default - pud: set local loopback for multicast tx +Scott Kidder (1): + jsoninfo: ensure output is wrapped in curly braces. -Henning Rogge (2): - Update version after release of v0.6.5.2 - Release v0.6.5.3 +Vasilis Tsiligiannis (1): + olsrd-quagga: Fix memory leak when adding and deleting routes -Ronald in 't Velt (1): - Fix setsockopt for setting Traffic Class in IPv6 +equinox (1): + quagga plugin routes additional 0.6.5.2 ------------------------------------------------------------------- diff --git a/Makefile b/Makefile index 4432f59b..162bf9f9 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,7 @@ # Please also write a new version to: # gui/win32/Main/Frontend.rc (line 71, around "CAPTION [...]") # gui/win32/Inst/installer.nsi (line 57, around "MessageBox MB_YESNO [...]") -VERS = 0.6.5.4 +VERS = pre-0.6.6.2 TOPDIR = . INSTALLOVERWRITE ?= @@ -59,6 +59,15 @@ CFGDIR = src/cfgparser include $(CFGDIR)/local.mk TAG_SRCS = $(SRCS) $(HDRS) $(wildcard $(CFGDIR)/*.[ch] $(SWITCHDIR)/*.[ch]) +SGW_SUPPORT = 0 +ifeq ($(OS),linux) + SGW_SUPPORT = 1 +endif +ifeq ($(OS),android) + SGW_SUPPORT = 1 +endif + + .PHONY: default_target switch default_target: $(EXENAME) @@ -79,7 +88,7 @@ switch: src/builddata.c: $(MAKECMDPREFIX)$(RM) "$@" $(MAKECMDPREFIX)echo "#include \"defs.h\"" >> "$@" - $(MAKECMDPREFIX)echo "const char olsrd_version[] = \"olsr.org - $(VERS)-git_`git log -1 --pretty=%h`-hash_`./make/hash_source.sh`\";" >> "$@" + $(MAKECMDPREFIX)echo "const char olsrd_version[] = \"olsr.org - $(VERS)`./make/hash_source.sh`\";" >> "$@" $(MAKECMDPREFIX)date +"const char build_date[] = \"%Y-%m-%d %H:%M:%S\";" >> "$@" $(MAKECMDPREFIX)echo "const char build_host[] = \"$(shell hostname)\";" >> "$@" @@ -122,6 +131,14 @@ install_bin: mkdir -p $(SBINDIR) install -m 755 $(EXENAME) $(SBINDIR) $(STRIP) $(SBINDIR)/$(EXENAME) +ifeq ($(SGW_SUPPORT),1) + $(MAKECMDPREFIX)if [ -e $(SBINDIR)/$(SGW_POLICY_SCRIPT) ]; then \ + cp -f files/$(SGW_POLICY_SCRIPT) $(SBINDIR)/$(SGW_POLICY_SCRIPT).new; \ + echo "Policy routing script was saved as $(SBINDIR)/$(SGW_POLICY_SCRIPT).new"; \ + else \ + cp -f files/$(SGW_POLICY_SCRIPT) $(SBINDIR)/$(SGW_POLICY_SCRIPT); \ + fi +endif uninstall_bin: rm -f $(SBINDIR)/$(EXENAME) @@ -188,9 +205,9 @@ ifeq ($(OS),win32) SUBDIRS := dot_draw httpinfo jsoninfo mini pgraph secure txtinfo else ifeq ($(OS),android) -SUBDIRS := arprefresh bmf dot_draw dyn_gw_plain httpinfo jsoninfo mdp mini nameservice pgraph pud secure sgwdynspeed txtinfo watchdog +SUBDIRS := arprefresh bmf dot_draw dyn_gw dyn_gw_plain httpinfo jsoninfo mdns mini nameservice p2pd pgraph pud secure sgwdynspeed txtinfo watchdog else -SUBDIRS := dot_draw dyn_gw dyn_gw_plain httpinfo jsoninfo mini nameservice pgraph secure txtinfo watchdog +SUBDIRS := dot_draw httpinfo jsoninfo mini nameservice pgraph secure txtinfo watchdog endif endif endif @@ -252,18 +269,6 @@ bmf_install: bmf_uninstall: $(MAKECMDPREFIX)$(MAKECMD) -C lib/bmf DESTDIR=$(DESTDIR) uninstall -dnssd: - $(MAKECMDPREFIX)$(MAKECMD) -C lib/dnssd - -dnssd_clean: - $(MAKECMDPREFIX)$(MAKECMD) -C lib/dnssd DESTDIR=$(DESTDIR) clean - -dnssd_install: - $(MAKECMDPREFIX)$(MAKECMD) -C lib/dnssd DESTDIR=$(DESTDIR) install - -dnssd_uninstall: - $(MAKECMDPREFIX)$(MAKECMD) -C lib/dnssd DESTDIR=$(DESTDIR) uninstall - dot_draw: $(MAKECMDPREFIX)$(MAKECMD) -C lib/dot_draw @@ -343,19 +348,6 @@ mdns_uninstall: # nameserver uses regex, which was only recently added to Android. On # Android, $(REGEX_OBJS) will have all of the files needed, on all # other platforms, it'll be empty and therefore ignored. - -mdp: - $(MAKECMDPREFIX)$(MAKECMD) -C lib/mdp - -mdp_clean: - $(MAKECMDPREFIX)$(MAKECMD) -C lib/mdp DESTDIR=$(DESTDIR) clean - -mdp_install: - $(MAKECMDPREFIX)$(MAKECMD) -C lib/mdp DESTDIR=$(DESTDIR) install - -mdp_uninstall: - $(MAKECMDPREFIX)$(MAKECMD) -C lib/mdp DESTDIR=$(DESTDIR) uninstall - nameservice: $(MAKECMDPREFIX)$(MAKECMD) -C lib/nameservice clean $(MAKECMDPREFIX)$(MAKECMD) -C lib/nameservice diff --git a/Makefile.inc b/Makefile.inc index 9ef98818..52fea38e 100644 --- a/Makefile.inc +++ b/Makefile.inc @@ -68,6 +68,7 @@ MANDIR ?= $(SHAREDIR)/man EXENAME ?= olsrd CFGNAME ?= $(EXENAME).conf CFGFILE ?= $(ETCDIR)/$(CFGNAME) +SGW_POLICY_SCRIPT ?= sgw_policy_routing_setup.sh DOCDIR_OLSRD ?= $(DOCDIR)/$(EXENAME) @@ -150,8 +151,6 @@ CFLAGS += $(WARNINGS) CFLAGS += $(OPTIMIZE) CFLAGS += $(EXTRA_CFLAGS) -# Must be specified along with -lpthread on linux -CPPFLAGS += $(OS_CFLAG_PTHREAD) endif ifneq ($(MUDFLAP),0) CFLAGS += -fmudflapth @@ -184,11 +183,22 @@ LDFLAGS += -Wl,--warn-common ifneq ($(MUDFLAP),0) LIBS += -lmudflapth endif -LIBS += $(OS_LIB_PTHREAD) # extra options from the outside CPPFLAGS += $(EXTRA_CPPFLAGS) + +# 32/64 cross compilation +ifdef M32 +CFLAGS += -m32 +LDFLAGS += -m32 +endif +ifdef M64 +CFLAGS += -m64 +LDFLAGS += -m64 +endif + + ################################### # # options to save space on small systems diff --git a/README-Olsr-Extensions b/README-Olsr-Extensions index ac244cf6..51baf286 100644 --- a/README-Olsr-Extensions +++ b/README-Olsr-Extensions @@ -251,20 +251,49 @@ All other parameters will be ignored if SmartGateway is set to "no" 5.2) Client Side -1- SmartGatewayAllowNAT controls whether you want to allow the selection +1- SmartGatewayUseCount controls the maximum number of gateways that can be + in use at any given time. This setting is used to mitigate the effects of + breaking connections (due to the selection of a new gateway) on a dynamic + network. + The default setting is 1. +2- SmartGatewayTakeDownPercentage determines the take-down percentage for a + non-current smart gateway tunnel. If the cost of the current smart gateway + tunnel is less than this percentage of the cost of the non-current smart + gateway tunnel, then the non-current smart gateway tunnel is taken down + because it is then presumed to be 'too expensive'. + This setting is only relevant when SmartGatewayUseCount is larger than 1; + a value of 0 will result in the tunnels not being taken down proactively. + The default setting is 0. +3- SmartGatewayPolicyRoutingScript controles the policy routing script that is + executed during startup and shutdown of olsrd. The script is only executed + when SmartGatewayUseCount is set to a value larger than 1. The script must + setup policy routing rules such that multi-gateway mode works. A sample + script is included. + The default setting is 'not set'. +4- SmartGatewayEgressInterfaces determines the egress interfaces that are part + of the multi-gateway setup and therefore only relevant when + SmartGatewayUseCount is larger than 1 (in which case it must be explicitly + set). + The default setting is 'not set'. +5- SmartGatewayMarkOffsetEgress and SmartGatewayMarkOffsetTunnels determine + the ranges of policy routing rule markings that are used in a multi-gateway + setup. The ranges are not allowed to overlap. Both settings are only + relevant when a multi-gateway setup is used. + The default settings are 91 and 101 respectively. +6- SmartGatewayAllowNAT controls whether you want to allow the selection of an outgoing ipv4 gateway with NAT (Network Address Translation). The default setting is "yes". -2- SmartGatewayPeriod determines the period (in milliseconds) on which +7- SmartGatewayPeriod determines the period (in milliseconds) on which a new smart gateway selection is performed. The default setting is 10000 milliseconds. -3- SmartGatewayStableCount determines the number of times the link state +8- SmartGatewayStableCount determines the number of times the link state database must be stable before a new smart gateway is selected. The default setting is 6. -4- SmartGatewayThreshold controls whether you want to allow re-selection +9- SmartGatewayThreshold controls whether you want to allow re-selection of a new outgoing gateway if its routing cost is lower or equal to the configured percentage of the routing cost of the current gateway. The default setting is 0, which disables it. -5- SmartGatewayWeightExitLinkUp, SmartGatewayWeightExitLinkDown, +10-SmartGatewayWeightExitLinkUp, SmartGatewayWeightExitLinkDown, SmartGatewayWeightEtx and SmartGatewayDividerEtx control the weighing of gateway bandwidth and ETX costs. @@ -331,6 +360,9 @@ All other parameters will be ignored if SmartGateway is set to "no" local IPv6 address to use the IPv6 gateway without any kind of address translation. The maximum prefix length is 64 bits, the default is ::/0 (no prefix). +5- SmartGatewayAlwaysRemoveServerTunnel can be used to signal that the + server tunnel must always be removed on shutdown, irrespective of the + interface up/down state during startup. 5.4) Architecture & Notes diff --git a/contrib/netsimpcap/src/netsimpcap.c b/contrib/netsimpcap/src/netsimpcap.c index 8246d368..fae45b3b 100644 --- a/contrib/netsimpcap/src/netsimpcap.c +++ b/contrib/netsimpcap/src/netsimpcap.c @@ -70,6 +70,10 @@ u_int8_t mac_bc[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; static void signalHandler(int signo __attribute__ ((unused))) { + /* + * Normally errno must be saved here and restored before returning but since + * we do a simple assignment we don't need to do that in this signal handler. + */ running = 0; } diff --git a/doc/Makefile b/doc/Makefile index c6dec192..ced50a0b 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -56,10 +56,10 @@ clean: doc: $(DOX_FILE) @echo "[$@] doc" @echo " Cleaning up $(OS) outputs..." - @rm -fr "$(HTML_OUTPUT)" "$(LATEX_OUTPUT)" "$(PDF_OUTPUT)" - @mkdir -p "$(HTML_OUTPUT)" - @mkdir -p "$(LATEX_OUTPUT)" - @mkdir -p "$(PDF_DIR)" + @rm -frv "$(HTML_OUTPUT)" "$(LATEX_OUTPUT)" "$(PDF_OUTPUT)" + @mkdir -pv "$(HTML_OUTPUT)" + @mkdir -pv "$(LATEX_OUTPUT)" + @mkdir -pv "$(PDF_DIR)" @echo " Generating doxygen configuration..." @sed \ -e "s/__OLSRD_VER__/$(OLSRD_VER)/" \ @@ -72,11 +72,11 @@ doc: $(DOX_FILE) "$(DOX_FILE)" > "$(TMP_DOX_FILE)" @echo " Running doxygen to create HTML..." @doxygen "$(TMP_DOX_FILE)" - @rm -f "$(TMP_DOX_FILE)" + @rm -fv "$(TMP_DOX_FILE)" @echo " Running latex to create a PDF..." - @$(MAKE) -s -C $(LATEX_OUTPUT) all > /dev/null 2>&1 - @mkdir -p "$(PDF_DIR)" - @mv "$(LATEX_OUTPUT)/refman.pdf" "$(PDF_OUTPUT)" - @rm -fr "$(LATEX_OUTPUT)" - @rmdir --ignore-fail-on-non-empty "$(LATEX_DIR)" + @$(MAKE) -s -C $(LATEX_OUTPUT) all + @mkdir -pv "$(PDF_DIR)" + @mv -v "$(LATEX_OUTPUT)/refman.pdf" "$(PDF_OUTPUT)" + @rm -frv "$(LATEX_OUTPUT)" + @rmdir -v --ignore-fail-on-non-empty "$(LATEX_DIR)" @echo " Done" diff --git a/files/olsrd.conf.commotion b/files/olsrd.conf.commotion index 3709ed55..029e6008 100644 --- a/files/olsrd.conf.commotion +++ b/files/olsrd.conf.commotion @@ -23,14 +23,9 @@ LoadPlugin "olsrd_dyn_gw.so.0.5" { } -LoadPlugin "olsrd_httpinfo.so.0.1" -{ - PlParam "Net" "0.0.0.0 0.0.0.0" - PlParam "port" "1978" -} - LoadPlugin "olsrd_nameservice.so.0.3" { + # you should set this to your own node name PlParam "name" "commotion-7814921" PlParam "sighup-pid-file" "/var/run/dnsmasq.pid" PlParam "suffix" ".mesh" @@ -43,13 +38,21 @@ LoadPlugin "olsrd_p2pd.so.0.1.0" PlParam "UdpDestPort" "224.0.0.251 5353" } +LoadPlugin "olsrd_jsoninfo.so.0.0" +{ +# PlParam "accept" "0.0.0.0" +} + LoadPlugin "olsrd_txtinfo.so.0.1" { - PlParam "accept" "0.0.0.0" +# PlParam "accept" "0.0.0.0" } -Interface "wlan0" +InterfaceDefaults { + # if you using this on Mac OS X, then comment this out because Mac OS X + # does weird things with 255.255.255.255. By commenting this out, olsrd + # will use the broadcast that's assigned to the network interface Ip4Broadcast 255.255.255.255 } diff --git a/files/olsrd.conf.default.full b/files/olsrd.conf.default.full index 6b11c7d9..21fd505a 100644 --- a/files/olsrd.conf.default.full +++ b/files/olsrd.conf.default.full @@ -144,6 +144,60 @@ # SmartGateway no +# Signals that the server tunnel must always be removed on shutdown, +# irrespective of the interface up/down state during startup. +# (default is "no") + +# SmartGatewayAlwaysRemoveServerTunnel no + +# Determines the maximum number of gateways that can be in use at any given +# time. This setting is used to mitigate the effects of breaking connections +# (due to the selection of a new gateway) on a dynamic network. +# (default is 1) + +# SmartGatewayUseCount 1 + +# Determines the take-down percentage for a non-current smart gateway tunnel. +# If the cost of the current smart gateway tunnel is less than this percentage +# of the cost of the non-current smart gateway tunnel, then the non-current smart +# gateway tunnel is taken down because it is then presumed to be 'too expensive'. +# This setting is only relevant when SmartGatewayUseCount is larger than 1; +# a value of 0 will result in the tunnels not being taken down proactively. +# (default is 0) + +# SmartGatewayTakeDownPercentage 0 + +# Determines the policy routing script that is executed during startup and +# shutdown of olsrd. The script is only executed when SmartGatewayUseCount +# is set to a value larger than 1. The script must setup policy routing +# rules such that multi-gateway mode works. A sample script is included. +# (default is not set) + +# SmartGatewayPolicyRoutingScript "" + +# Determines the egress interfaces that are part of the multi-gateway setup and +# therefore only relevant when SmartGatewayUseCount is larger than 1 (in which +# case it must be explicitly set). +# (default is not set) + +# SmartGatewayEgressInterfaces "" + +# Determines the offset of the smart gateway egress interfaces mark that are +# used in the policy routing rules in a multi-gateway setup. Only relevant +# when a multi-gateway setup is used. +# (default is 91) + +# SmartGatewayMarkOffsetEgress 91 + +# Determines the offset of the smart gateway tunnel interfaces mark that are +# used in the policy routing rules in a multi-gateway setup. Only relevant +# when a multi-gateway setup is used. +# The ranges [egress offset, egress offset + egress count] and +# [tunnel offset, tunnel offset + use count] are not allowed to overlap. +# (default is 101) + +# SmartGatewayMarkOffsetTunnels 101 + # Allows the selection of a smartgateway with NAT (only for IPv4) # (default is "yes") diff --git a/files/olsrd.conf.default.lq b/files/olsrd.conf.default.lq index 4ed06500..078e2f95 100644 --- a/files/olsrd.conf.default.lq +++ b/files/olsrd.conf.default.lq @@ -81,6 +81,60 @@ # SmartGateway no +# Signals that the server tunnel must always be removed on shutdown, +# irrespective of the interface up/down state during startup. +# (default is "no") + +# SmartGatewayAlwaysRemoveServerTunnel no + +# Determines the maximum number of gateways that can be in use at any given +# time. This setting is used to mitigate the effects of breaking connections +# (due to the selection of a new gateway) on a dynamic network. +# (default is 1) + +# SmartGatewayUseCount 1 + +# Determines the take-down percentage for a non-current smart gateway tunnel. +# If the cost of the current smart gateway tunnel is less than this percentage +# of the cost of the non-current smart gateway tunnel, then the non-current smart +# gateway tunnel is taken down because it is then presumed to be 'too expensive'. +# This setting is only relevant when SmartGatewayUseCount is larger than 1; +# a value of 0 will result in the tunnels not being taken down proactively. +# (default is 0) + +# SmartGatewayTakeDownPercentage 0 + +# Determines the policy routing script that is executed during startup and +# shutdown of olsrd. The script is only executed when SmartGatewayUseCount +# is set to a value larger than 1. The script must setup policy routing +# rules such that multi-gateway mode works. A sample script is included. +# (default is not set) + +# SmartGatewayPolicyRoutingScript "" + +# Determines the egress interfaces that are part of the multi-gateway setup and +# therefore only relevant when SmartGatewayUseCount is larger than 1 (in which +# case it must be explicitly set). +# (default is not set) + +# SmartGatewayEgressInterfaces "" + +# Determines the offset of the smart gateway egress interfaces mark that are +# used in the policy routing rules in a multi-gateway setup. Only relevant +# when a multi-gateway setup is used. +# (default is 91) + +# SmartGatewayMarkOffsetEgress 91 + +# Determines the offset of the smart gateway tunnel interfaces mark that are +# used in the policy routing rules in a multi-gateway setup. Only relevant +# when a multi-gateway setup is used. +# The ranges [egress offset, egress offset + egress count] and +# [tunnel offset, tunnel offset + use count] are not allowed to overlap. +# (default is 101) + +# SmartGatewayMarkOffsetTunnels 101 + # Allows the selection of a smartgateway with NAT (only for IPv4) # (default is "yes") diff --git a/files/olsrd.conf.default.lq-fisheye b/files/olsrd.conf.default.lq-fisheye index 0437239a..64e30d83 100644 --- a/files/olsrd.conf.default.lq-fisheye +++ b/files/olsrd.conf.default.lq-fisheye @@ -81,6 +81,60 @@ # SmartGateway no +# Signals that the server tunnel must always be removed on shutdown, +# irrespective of the interface up/down state during startup. +# (default is "no") + +# SmartGatewayAlwaysRemoveServerTunnel no + +# Determines the maximum number of gateways that can be in use at any given +# time. This setting is used to mitigate the effects of breaking connections +# (due to the selection of a new gateway) on a dynamic network. +# (default is 1) + +# SmartGatewayUseCount 1 + +# Determines the take-down percentage for a non-current smart gateway tunnel. +# If the cost of the current smart gateway tunnel is less than this percentage +# of the cost of the non-current smart gateway tunnel, then the non-current smart +# gateway tunnel is taken down because it is then presumed to be 'too expensive'. +# This setting is only relevant when SmartGatewayUseCount is larger than 1; +# a value of 0 will result in the tunnels not being taken down proactively. +# (default is 0) + +# SmartGatewayTakeDownPercentage 0 + +# Determines the policy routing script that is executed during startup and +# shutdown of olsrd. The script is only executed when SmartGatewayUseCount +# is set to a value larger than 1. The script must setup policy routing +# rules such that multi-gateway mode works. A sample script is included. +# (default is not set) + +# SmartGatewayPolicyRoutingScript "" + +# Determines the egress interfaces that are part of the multi-gateway setup and +# therefore only relevant when SmartGatewayUseCount is larger than 1 (in which +# case it must be explicitly set). +# (default is not set) + +# SmartGatewayEgressInterfaces "" + +# Determines the offset of the smart gateway egress interfaces mark that are +# used in the policy routing rules in a multi-gateway setup. Only relevant +# when a multi-gateway setup is used. +# (default is 91) + +# SmartGatewayMarkOffsetEgress 91 + +# Determines the offset of the smart gateway tunnel interfaces mark that are +# used in the policy routing rules in a multi-gateway setup. Only relevant +# when a multi-gateway setup is used. +# The ranges [egress offset, egress offset + egress count] and +# [tunnel offset, tunnel offset + use count] are not allowed to overlap. +# (default is 101) + +# SmartGatewayMarkOffsetTunnels 101 + # Allows the selection of a smartgateway with NAT (only for IPv4) # (default is "yes") diff --git a/files/olsrd.ufw.profile b/files/olsrd.ufw.profile new file mode 100644 index 00000000..7b68a12f --- /dev/null +++ b/files/olsrd.ufw.profile @@ -0,0 +1,4 @@ +[olsrd] +title=olsrd +description=optimized link-state routing daemon for OLSR mesh networking +ports=698 diff --git a/files/sgw_policy_routing_setup.sh b/files/sgw_policy_routing_setup.sh new file mode 100755 index 00000000..7648fe13 --- /dev/null +++ b/files/sgw_policy_routing_setup.sh @@ -0,0 +1,177 @@ +#!/bin/bash + +set -e +set -u + +############################################################################### +# +# SETTINGS +# +############################################################################### + +declare IPVERSION_4="ipv4" +declare IPVERSION_6="ipv6" + +declare MODE_GENERIC="generic" +declare MODE_OLSRIF="olsrif" +declare MODE_SGWSRVTUN="sgwsrvtun" +declare MODE_EGRESSIF="egressif" +declare MODE_SGWTUN="sgwtun" + +declare ADDMODE_ADD="add" +declare ADDMODE_DEL="del" + + +############################################################################### +# +# HELPER FUNCTIONS +# +############################################################################### + +function usage() { + echo "" + echo "The script was called as:" + echo " ${script} ${arguments[@]:-}" + echo "" + echo "Usage:" + echo " ${script} ipVersion mode addMode [ifname [ifmark]]" + echo " - ipVersion: ${IPVERSION_4} or ${IPVERSION_6}" + echo " - mode : ${MODE_GENERIC}, ${MODE_OLSRIF}, ${MODE_SGWSRVTUN}, ${MODE_EGRESSIF} or ${MODE_SGWTUN}" + echo " - addMode : ${ADDMODE_ADD} or ${ADDMODE_DEL}" + echo " - ifname : an interface name, not relevant for generic mode" + echo " - ifmark : an interface marking (number), only relevant for ${MODE_EGRESSIF} and ${MODE_SGWTUN} modes" +} + +function error() { + local -i firstLine=1 + while [ ${#} -gt 0 ]; do + if [ ${firstLine} -eq 1 ]; then + echo "Error: ${1}" + else + echo " ${1}" + fi + firstLine=0 + shift 1 + done +} + + +############################################################################### +# +# MODE FUNCTIONS +# +############################################################################### + +function generic() { + "${IPTABLES}" ${IPTABLES_ARGS} -t mangle "${ADDMODE_IPTABLES}" OUTPUT -j CONNMARK --restore-mark +} + +function olsrif() { + "${IPTABLES}" ${IPTABLES_ARGS} -t mangle "${ADDMODE_IPTABLES}" PREROUTING -i "${1}" -j CONNMARK --restore-mark +} + +function sgwsrvtun() { + "${IPTABLES}" ${IPTABLES_ARGS} -t mangle "${ADDMODE_IPTABLES}" PREROUTING -i "${1}" -j CONNMARK --restore-mark +} + +function egressif() { + "${IPTABLES}" ${IPTABLES_ARGS} -t mangle "${ADDMODE_IPTABLES}" POSTROUTING -m conntrack --ctstate NEW -o "${1}" -j CONNMARK --set-mark "${2}" + "${IPTABLES}" ${IPTABLES_ARGS} -t mangle "${ADDMODE_IPTABLES}" INPUT -m conntrack --ctstate NEW -i "${1}" -j CONNMARK --set-mark "${2}" + "${IP}" ${IP_ARGS} rule "${ADDMODE_IP}" fwmark "${2}" table "${2}" pref "${2}" +} + +function sgwtun() { + "${IPTABLES}" ${IPTABLES_ARGS} -t mangle "${ADDMODE_IPTABLES}" POSTROUTING -m conntrack --ctstate NEW -o "${1}" -j CONNMARK --set-mark "${2}" + "${IP}" ${IP_ARGS} rule "${ADDMODE_IP}" fwmark "${2}" table "${2}" pref "${2}" +} + + +############################################################################### +# +# MAIN +# +############################################################################### + +declare script="${0}" +declare -a arguments=( ${@} ) +declare -i argc=${#} + +# we always need 3 arguments, check it +if [ ${argc} -lt 3 ]; then + error "Need at least 3 arguments" + usage + exit 1 +fi + +# get first 3 arguments +declare ipVersion=${1} +declare mode="${2}" +declare addMode="${3}" +shift 3 +argc=${#} + +# check IP version argument +if [ ! "${ipVersion}" == "${IPVERSION_4}" ] && \ + [ ! "${ipVersion}" == "${IPVERSION_6}" ]; then + error "Illegal IP version" + usage + exit 1 +fi + +# check mode argument +if [ ! "${mode}" == "${MODE_GENERIC}" ] && \ + [ ! "${mode}" == "${MODE_OLSRIF}" ] && \ + [ ! "${mode}" == "${MODE_SGWSRVTUN}" ] && \ + [ ! "${mode}" == "${MODE_EGRESSIF}" ] && \ + [ ! "${mode}" == "${MODE_SGWTUN}" ]; then + error "Illegal mode" + usage + exit 1 +fi + +# check addMode argument +if [ ! "${addMode}" == "${ADDMODE_ADD}" ] && \ + [ ! "${addMode}" == "${ADDMODE_DEL}" ]; then + error "Illegal addMode" + usage + exit 1 +fi + +# check argument count for all modes +if ([ "${mode}" == "${MODE_GENERIC}" ] && [ ${argc} -ne 0 ]) || \ + ([ "${mode}" == "${MODE_OLSRIF}" ] && [ ${argc} -ne 1 ]) || \ + ([ "${mode}" == "${MODE_SGWSRVTUN}" ] && [ ${argc} -ne 1 ]) || \ + ([ "${mode}" == "${MODE_EGRESSIF}" ] && [ ${argc} -ne 2 ]) || \ + ([ "${mode}" == "${MODE_SGWTUN}" ] && [ ${argc} -ne 2 ]); then + error "Not enough arguments or too many arguments" + usage + exit 1 +fi + +# process ipVersion argument +declare IPTABLES="iptables" +declare IPTABLES_ARGS="" +declare IP="ip" +declare IP_ARGS="-4" +if [ "${ipVersion}" == "${IPVERSION_6}" ]; then + IPTABLES="ip6tables" + IPTABLES_ARGS="" + IP="ip" + IP_ARGS="-6" +fi + +# process addMode argument +declare ADDMODE_IPTABLES="-D" +declare ADDMODE_IP="del" +if [ "${addMode}" == "${ADDMODE_ADD}" ]; then + # first call the delete mode to remove any left-over rules + set +e + "${mode}" "${@}" 2> /dev/null + set -e + + ADDMODE_IPTABLES="-I" + ADDMODE_IP="add" +fi + +# call the mode +"${mode}" "${@}" diff --git a/gui/linux-gtk/Makefile b/gui/linux-gtk/Makefile index 50e060a7..28183cd3 100644 --- a/gui/linux-gtk/Makefile +++ b/gui/linux-gtk/Makefile @@ -11,6 +11,17 @@ LDFLAGS = -pie LIBS := $(shell pkg-config --libs gtk+-2.0) -lm +# 32/64 cross compilation +ifdef M32 +CFLAGS += -m32 +LDFLAGS += -m32 +endif +ifdef M64 +CFLAGS += -m64 +LDFLAGS += -m64 +endif + + all: default_target default_target: olsrd-gui diff --git a/gui/linux-gtk/src/main.c b/gui/linux-gtk/src/main.c index 1be35063..652c7d20 100644 --- a/gui/linux-gtk/src/main.c +++ b/gui/linux-gtk/src/main.c @@ -129,12 +129,14 @@ __attribute__((noreturn)) void shutdown_(int sig) { + int errNr = errno; printf("Cleaning up...\n"); if (ipc_close() < 0) printf("Could not close socket!\n"); printf("BYE-BYE!\n"); + errno = errNr; exit(sig); } diff --git a/gui/win32/Inst/installer.nsi b/gui/win32/Inst/installer.nsi index 0539bba1..91fe05ca 100644 --- a/gui/win32/Inst/installer.nsi +++ b/gui/win32/Inst/installer.nsi @@ -54,7 +54,7 @@ UninstPage uninstConfirm UninstPage instfiles Function .onInit - MessageBox MB_YESNO "This will install olsr.org 0.6.5.4 on your computer. Continue?" IDYES NoAbort + MessageBox MB_YESNO "This will install olsr.org pre-0.6.6.2 on your computer. Continue?" IDYES NoAbort Abort NoAbort: FunctionEnd diff --git a/gui/win32/Main/Frontend.rc b/gui/win32/Main/Frontend.rc index 92e9a4de..3df92d6c 100644 --- a/gui/win32/Main/Frontend.rc +++ b/gui/win32/Main/Frontend.rc @@ -68,7 +68,7 @@ IDD_FRONTEND_DIALOG DIALOGEX 0, 0, 399, 289 STYLE DS_MODALFRAME | WS_MINIMIZEBOX | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU EXSTYLE WS_EX_APPWINDOW -CAPTION "olsr.org Switch 0.6.5.4" +CAPTION "olsr.org Switch pre-0.6.6.2" FONT 8, "MS Sans Serif", 0, 0, 0x1 BEGIN CONTROL "Tab1",IDC_TAB1,"SysTabControl32",0x0,7,7,383,256 diff --git a/lib/arprefresh/README_ARPREFRESH b/lib/arprefresh/README_ARPREFRESH index 8308d510..4c3b45f3 100644 --- a/lib/arprefresh/README_ARPREFRESH +++ b/lib/arprefresh/README_ARPREFRESH @@ -3,12 +3,12 @@ ARP REFRESH PLUGIN FOR OLSRD by Sven-Ola Tuecke --------------------------------------------------------------------- -This plugin listens to all recevice UDP packets and maintains an +This plugin listens to all received UDP packets and maintains an internal list of MAC addresses extracted from these. By hitchhiking the set-route-function, the kernel ARP cache will be refreshed from -this list if a direct-neighbour host route is configured. Result: +this list if a direct-neighbor host route is configured. Result: no more ARP lookups if you use a larger routing chain - e.g. fetch -a web site 8 olsr-hops away does not show the typcial 8-nodes-need +a web site 8 olsr-hops away does not show the typical 8-nodes-need to ARP first delay. --------------------------------------------------------------------- diff --git a/lib/arprefresh/src/olsrd_arprefresh.c b/lib/arprefresh/src/olsrd_arprefresh.c index 8282dcfb..8533b7c4 100644 --- a/lib/arprefresh/src/olsrd_arprefresh.c +++ b/lib/arprefresh/src/olsrd_arprefresh.c @@ -31,7 +31,9 @@ */ /* - * Plugin to refresh the local ARP cache from received OLSR broadcasts + * Plugin to refresh the local ARP cache from received OLSR broadcasts. + * + * Note: this code does not work with IPv6 and not with VLANs (on IPv4 or IPv6) */ #include @@ -112,14 +114,7 @@ olsr_arp_event(void *foo __attribute__ ((unused))) MSG_TRUNC, (struct sockaddr *)&from, &fromlen); - if (0 <= size && size >= (ssize_t) sizeof(buf) - - /*** && - ETH_P_IP == ntohs(buf.eth.h_proto) && - IPPROTO_UDP == buf.ip.protocol && - arprefresh_portnum == ntohs(buf.udp.source) && - arprefresh_portnum == ntohs(buf.udp.dest) ***/ - ) { + if (0 <= size && size >= (ssize_t) sizeof(buf)) { union { struct arpreq arp; struct sockaddr_in in_pa; @@ -166,25 +161,27 @@ olsrd_plugin_init(void) struct sock_fprog filter; struct sock_filter BPF_code[] = { /* tcpdump -s [sizeof(arprefresh_buf)] -ni lo udp and dst port [arprefresh_portnum] -dd */ - {0x28, 0, 0, 0x0000000c}, - {0x15, 0, 4, 0x000086dd}, - {0x30, 0, 0, 0x00000014}, - {0x15, 0, 11, 0x00000011}, - {0x28, 0, 0, 0x00000038}, - {0x15, 8, 9, arprefresh_portnum}, - {0x15, 0, 8, 0x00000800}, - {0x30, 0, 0, 0x00000017}, - {0x15, 0, 6, 0x00000011}, - {0x28, 0, 0, 0x00000014}, - {0x45, 4, 0, 0x00001fff}, - {0xb1, 0, 0, 0x0000000e}, - {0x48, 0, 0, 0x00000010}, - {0x15, 0, 1, arprefresh_portnum}, - {0x6, 0, 0, sizeof(arprefresh_buf)} - , - {0x6, 0, 0, 0x00000000} + + /* Note: This program assumes NON-VLAN */ + /* See also http://www.unix.com/man-page/FreeBSD/4/bpf/ */ + /* code jump-true jump-false generic-multiuse-field */ + {BPF_LD | BPF_H | BPF_ABS , 0, 0, 0x0000000c}, // 0x28: A <- P[k:2], get 2-bytes from offset 0x0c : get Ethernet type + +/* test4 */ {BPF_JMP | BPF_JEQ | BPF_K , 0, 8, 0x00000800}, // 0x15: pc += (A == k) ? ipv4 : ignore : IPv4? + +/* ipv4 */ {BPF_LD | BPF_B | BPF_ABS , 0, 0, 0x00000017}, // 0x30: A <- P[k:1], get 1-byte from offset 0x17 : get IPv4 protocol + {BPF_JMP | BPF_JEQ | BPF_K , 0, 6, 0x00000011}, // 0x15: pc += (A == k) ? udp4 : ignore : UDP? +/* udp4 */ {BPF_LD | BPF_H | BPF_ABS , 0, 0, 0x00000014}, // 0x28: A <- P[k:2], get 2-bytes from offset 0x14 : get fragment offset + {BPF_JMP | BPF_JSET | BPF_K , 4, 0, 0x00001fff}, // 0x45: pc += (A & k) ? ignore : nofrag : is this a fragment? +/* nofrag */ {BPF_LDX | BPF_B | BPF_MSH , 0, 0, 0x0000000e}, // 0xb1: X <- 4*(P[k:1]&0xf) : get the IP header length in bytes + {BPF_LD | BPF_H | BPF_IND , 0, 0, 0x00000010}, // 0x48: A <- P[X+k:2] : get UDP destination port + {BPF_JMP | BPF_JEQ | BPF_K , 0, 1, arprefresh_portnum}, // 0x15: pc += (A == k) ? ok : ignore : sent to port arprefresh_portnum? + +/* ok */ {BPF_RET | BPF_K , 0, 0, sizeof(arprefresh_buf)}, // : accept sizeof(arprefresh_buf) bytes (all headers) +/* ignore */ {BPF_RET | BPF_K , 0, 0, 0x00000000} // : accept 0 bytes + }; - filter.len = sizeof(BPF_code) / sizeof(BPF_code[0]); + filter.len = ARRAYSIZE(BPF_code); filter.filter = BPF_code; if (0 <= (arprefresh_sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP))) && 0 <= (flags = fcntl(arprefresh_sockfd, F_GETFL)) diff --git a/lib/bmf/Makefile b/lib/bmf/Makefile index cff7b70f..46047c91 100644 --- a/lib/bmf/Makefile +++ b/lib/bmf/Makefile @@ -39,8 +39,6 @@ PLUGIN_VER = 1.7.0 TOPDIR = ../.. include $(TOPDIR)/Makefile.inc -#LIBS += $(OS_LIB_PTHREAD) - ifeq ($(OS),$(filter $(OS),linux android)) default_target: $(PLUGIN_FULLNAME) diff --git a/lib/dot_draw/README_DOT_DRAW b/lib/dot_draw/README_DOT_DRAW index df70317a..01b85a4f 100644 --- a/lib/dot_draw/README_DOT_DRAW +++ b/lib/dot_draw/README_DOT_DRAW @@ -23,7 +23,7 @@ ABOUT Note that this code is a quick hack done at the Wizards Of Oz 3 -The plugin generates output (over a TCP socket) in the dot format. +The plugin generates output (over a TCP-IPv4 socket) in the dot format. The dot tool generates visual presentations of directed graphs. It can be downloaded as part of the GraphViz package from: http://www.graphviz.org/ @@ -38,6 +38,8 @@ make install olsr-topology-view.pl is a simple script to visualize the dotdraw data on your computer. You will find some more explanations about the script at +http://n0rg.org/node1448/OlsrTopologyVisualization/OlsrTopologyVisualization.htm +or http://br1.einfach.org/OlsrTopologyVisualization - Henning Rogge diff --git a/lib/dyn_gw/Makefile b/lib/dyn_gw/Makefile index 35dbe76f..dc22c334 100644 --- a/lib/dyn_gw/Makefile +++ b/lib/dyn_gw/Makefile @@ -43,7 +43,10 @@ PLUGIN_VER = 0.5 TOPDIR = ../.. include $(TOPDIR)/Makefile.inc -LIBS += $(OS_LIB_PTHREAD) +LIBS += $(OS_LIB_PTHREAD) + +# Must be specified along with -lpthread on linux +CPPFLAGS += $(OS_CFLAG_PTHREAD) default_target: $(PLUGIN_FULLNAME) diff --git a/lib/dyn_gw/src/olsrd_dyn_gw.c b/lib/dyn_gw/src/olsrd_dyn_gw.c index c006af11..027863c1 100644 --- a/lib/dyn_gw/src/olsrd_dyn_gw.c +++ b/lib/dyn_gw/src/olsrd_dyn_gw.c @@ -60,9 +60,6 @@ #include #include #include -#ifdef __linux__ -#include -#endif /* __linux__ */ #include #include #include diff --git a/lib/httpinfo/src/olsrd_httpinfo.c b/lib/httpinfo/src/olsrd_httpinfo.c index d6ea8310..277c3dda 100644 --- a/lib/httpinfo/src/olsrd_httpinfo.c +++ b/lib/httpinfo/src/olsrd_httpinfo.c @@ -55,6 +55,7 @@ #endif /* _WIN32 */ #include "olsr.h" +#include "builddata.h" #include "olsr_cfg.h" #include "interfaces.h" #include "olsr_protocol.h" @@ -313,7 +314,7 @@ olsrd_plugin_init(void) if (http_socket < 0) { fprintf(stderr, "(HTTPINFO) could not initialize HTTP socket\n"); - exit(0); + exit(1); } /* Register socket */ diff --git a/lib/jsoninfo/src/olsrd_jsoninfo.c b/lib/jsoninfo/src/olsrd_jsoninfo.c index 68f9be19..966292c5 100644 --- a/lib/jsoninfo/src/olsrd_jsoninfo.c +++ b/lib/jsoninfo/src/olsrd_jsoninfo.c @@ -73,6 +73,7 @@ #include "ipcalc.h" #include "olsr.h" +#include "builddata.h" #include "olsr_types.h" #include "neighbor_table.h" #include "two_hop_neighbor_table.h" @@ -983,8 +984,30 @@ ipc_print_config(struct autobuf *abuf) abuf_json_string(abuf, "lockFile", olsr_cnf->lock_file); abuf_json_boolean(abuf, "useNiit", olsr_cnf->use_niit); +#ifdef __linux__ abuf_json_boolean(abuf, "smartGateway", olsr_cnf->smart_gw_active); if (olsr_cnf->smart_gw_active) { + abuf_json_boolean(abuf, "smartGatewayAlwaysRemoveServerTunnel", olsr_cnf->smart_gw_always_remove_server_tunnel); + abuf_json_int(abuf, "smartGatewayUseCount", olsr_cnf->smart_gw_use_count); + abuf_json_string(abuf, "smartGatewayPolicyRoutingScript", olsr_cnf->smart_gw_policyrouting_script); + { + struct autobuf egressbuf; + struct sgw_egress_if * egressif = olsr_cnf->smart_gw_egress_interfaces; + + abuf_init(&egressbuf, (olsr_cnf->smart_gw_egress_interfaces_count * IFNAMSIZ) /* interface names */ + + (olsr_cnf->smart_gw_egress_interfaces_count - 1) /* commas */); + while (egressif) { + if (egressbuf.len) { + abuf_puts(&egressbuf, ","); + } + abuf_appendf(&egressbuf, "%s", egressif->name); + egressif = egressif->next; + } + abuf_json_string(abuf, "smartGatewayEgressInterfaces", egressbuf.buf); + abuf_free(&egressbuf); + } + abuf_json_int(abuf, "smartGatewayMarkOffsetEgress", olsr_cnf->smart_gw_mark_offset_egress); + abuf_json_int(abuf, "smartGatewayMarkOffsetTunnels", olsr_cnf->smart_gw_mark_offset_tunnels); abuf_json_boolean(abuf, "smartGatewayAllowNat", olsr_cnf->smart_gw_allow_nat); abuf_json_boolean(abuf, "smartGatewayUplinkNat", olsr_cnf->smart_gw_uplink_nat); abuf_json_int(abuf, "smartGatewayPeriod", olsr_cnf->smart_gw_period); @@ -997,6 +1020,7 @@ ipc_print_config(struct autobuf *abuf) olsr_ip_to_string(&mainaddrbuf, &olsr_cnf->smart_gw_prefix.prefix)); abuf_json_int(abuf, "smartGatewayPrefixLength", olsr_cnf->smart_gw_prefix.prefix_len); } +#endif /* __linux__ */ abuf_json_string(abuf, "mainIpAddress", olsr_ip_to_string(&mainaddrbuf, &olsr_cnf->main_addr)); @@ -1259,7 +1283,7 @@ send_info(unsigned int send_what, int the_socket) abuf_init(&abuf, 32768); // only add if outputing JSON - if (send_what & SIW_ALL) abuf_puts(&abuf, "{\n"); + if (send_what & SIW_ALL) abuf_puts(&abuf, "{"); if ((send_what & SIW_LINKS) == SIW_LINKS) ipc_print_links(&abuf); if ((send_what & SIW_NEIGHBORS) == SIW_NEIGHBORS) ipc_print_neighbors(&abuf); @@ -1281,7 +1305,7 @@ send_info(unsigned int send_what, int the_socket) abuf_json_int(&abuf, "timeSinceStartup", now_times); if(*uuid != 0) abuf_json_string(&abuf, "uuid", uuid); - abuf_puts(&abuf, "}\n"); + abuf_puts(&abuf, "}\n"); } /* this outputs the olsrd.conf text directly, not JSON */ diff --git a/lib/mdns/Makefile b/lib/mdns/Makefile index b27f007c..e158d8c1 100644 --- a/lib/mdns/Makefile +++ b/lib/mdns/Makefile @@ -6,15 +6,23 @@ PLUGIN_VER = 1.0.1 TOPDIR = ../.. include $(TOPDIR)/Makefile.inc -LIBS += $(OS_LIB_PTHREAD) +SUPPORTED = 0 +ifeq ($(OS),linux) +SUPPORTED = 1 +endif +ifeq ($(OS),android) +SUPPORTED = 1 +endif + + +ifeq ($(SUPPORTED),0) -# Must be specified along with -lpthread on linux -CPPFLAGS += $(OS_CFLAG_PTHREAD) +.PHONY: all default_target install clean -ifneq ($(OS),linux) +all: default_target default_target install clean: - @echo "*** mdns Plugin only supported on Linux, sorry!" + @echo "*** $(PLUGIN_NAME) plugin not supported on $(OS), sorry!" else diff --git a/lib/mdns/src/NetworkInterfaces.c b/lib/mdns/src/NetworkInterfaces.c index 44cd522f..831a004b 100644 --- a/lib/mdns/src/NetworkInterfaces.c +++ b/lib/mdns/src/NetworkInterfaces.c @@ -285,7 +285,7 @@ CreateRouterElectionSocket(const char *ifName) BmfPError("Could not get ipv4 address of %s interface", ifName); goto bail; } - ipv4_addr = ((struct sockaddr_in *)&req.ifr_addr)->sin_addr; + ipv4_addr = ((struct sockaddr_in *)(void *) &req.ifr_addr)->sin_addr; mc_settings.imr_interface = ipv4_addr; errno = 0; if (setsockopt(rxSocket, ipProtoSetting, ipAddMembershipSetting, @@ -380,7 +380,7 @@ static int CreateHelloSocket(const char *ifName) { BmfPError("Could not get ipv4 address of %s interface", ifName); goto bail; } - ipv4_addr = ((struct sockaddr_in *)&req.ifr_addr)->sin_addr; + ipv4_addr = ((struct sockaddr_in *)(void *) &req.ifr_addr)->sin_addr; address.in4.sin_addr = ipv4_addr; address.in4.sin_family = ipFamilySetting; address.in4.sin_port = ipPort; diff --git a/lib/mdns/src/RouterElection.c b/lib/mdns/src/RouterElection.c index cb150276..dba336aa 100644 --- a/lib/mdns/src/RouterElection.c +++ b/lib/mdns/src/RouterElection.c @@ -7,7 +7,6 @@ #include /* assert() */ #include /* ETH_P_IP */ #include /* struct sockaddr_ll, PACKET_MULTICAST */ -//#include /* pthread_t, pthread_create() */ #include /* sigset_t, sigfillset(), sigdelset(), SIGINT */ #include /* struct ip */ #include /* struct udphdr */ diff --git a/lib/mdns/src/container_of.h b/lib/mdns/src/container_of.h index ca3150c9..4fcaebd6 100644 --- a/lib/mdns/src/container_of.h +++ b/lib/mdns/src/container_of.h @@ -55,7 +55,7 @@ * @param member name of node inside struct * @return pointer to surrounding struct */ -#define container_of(ptr, type, member) ((type *)( (char *)(ptr) - offsetof(type,member) )) +#define container_of(ptr, type, member) ((type *) (((size_t) ptr) - offsetof(type, member) )) /** * Helper function for NULL safe container_of macro diff --git a/lib/mdns/src/mdns.c b/lib/mdns/src/mdns.c index 2d5e06a9..3eaba16c 100644 --- a/lib/mdns/src/mdns.c +++ b/lib/mdns/src/mdns.c @@ -50,7 +50,6 @@ #include /* assert() */ #include /* ETH_P_IP */ #include /* struct sockaddr_ll, PACKET_MULTICAST */ -//#include /* pthread_t, pthread_create() */ #include /* sigset_t, sigfillset(), sigdelset(), SIGINT */ #include /* struct ip */ #include /* struct udphdr */ @@ -91,8 +90,8 @@ static uint16_t ip_checksum(char* data, int len) else len = (len >> 1) + 1; while (len > 0) { - sum += *((ushort*)data); - data += sizeof(ushort); + sum += *((unsigned short int*)(void *)data); + data += sizeof(unsigned short int); len--; } sum = (sum >> 16) + (sum & 0xffff); diff --git a/lib/p2pd/Makefile b/lib/p2pd/Makefile index b6dc775a..c8a2f909 100644 --- a/lib/p2pd/Makefile +++ b/lib/p2pd/Makefile @@ -6,15 +6,23 @@ PLUGIN_VER = 0.1.0 TOPDIR = ../.. include $(TOPDIR)/Makefile.inc -LIBS += $(OS_LIB_PTHREAD) +SUPPORTED = 0 +ifeq ($(OS),linux) +SUPPORTED = 1 +endif +ifeq ($(OS),android) +SUPPORTED = 1 +endif + + +ifeq ($(SUPPORTED),0) -# Must be specified along with -lpthread on linux -CPPFLAGS += $(OS_CFLAG_PTHREAD) +.PHONY: all default_target install clean -ifneq ($(OS),linux) +all: default_target default_target install clean: - @echo "*** p2pd Plugin only supported on Linux, sorry!" + @echo "*** $(PLUGIN_NAME) plugin not supported on $(OS), sorry!" else diff --git a/lib/p2pd/src/p2pd.c b/lib/p2pd/src/p2pd.c index 73905a0d..b696c4fc 100644 --- a/lib/p2pd/src/p2pd.c +++ b/lib/p2pd/src/p2pd.c @@ -52,7 +52,6 @@ #include #include /* ETH_P_IP */ #include /* struct sockaddr_ll, PACKET_MULTICAST */ -//#include /* pthread_t, pthread_create() */ #include /* sigset_t, sigfillset(), sigdelset(), SIGINT */ #include /* struct ip */ #include /* struct udphdr */ diff --git a/lib/pud/doc/pud.odt b/lib/pud/doc/pud.odt index 9f3d31fe0c147c14056437dacc6111ec3889780d..52cd26d6baa2d41350ce56a9b97986f75b3288ec 100755 GIT binary patch delta 30418 zcmagG1#l(3k~L^%W@e@~Gc&W@W@cu#uNm6R)MmHYZZk78Gcz-@*WY{d{+;<_W2fR) z>Qw4TRZ>WKBT`CrSAxBEgCQu&fP$d`0YL!)8HF~JSyKP3m|99&XjX`MvacIETrB(B3 z%A;YeD(NlMrVICvNy$Q@aiZi<4nT^r+z&s-O&@EmQyRng&dv9W?^7$hMS;Me;=sal zvCuR()X6~tpb*^=zklwrgkKMaHni^jcl1l1no4KUzl48xT5C;Sn;wi!WNJ0t{w@Bx zkZ@|Z1<~jEg!X=`Wq$aLx^zwd)z388F97{Yc^S;P^ON#s zkoy(C0v5?4!x_i&S)UOrNjk=%2ZM1_$N~h zG{PZax$6U`4G%>?JSM*lF62mhjD18ZE}OVPVae$hyt8xvh6AwGD{0@@1a7@!LVIg^ zPJ1i%mzxb(0XXXkfqsw7>)-Mx7QO31&!OH%Xw?Tk>jdwN3fFhSnEi z8D6t4<$xIEagRV+$ox_u(SsJyMc)a6*frbJzK0 z4otTX`=3{T-$Z8G1CO;=>u)iqAmu; z@V7$c!m=&R*ufp{jWu4YudUoMTE0Yt2^fyTHkoYEn5x78=MKrDwl6NjeZd|Wll|54 z$X3RLaS-D>^72Odl&1L{e#1-n%)!YHt(w2BPFSvy!FeRcD{KuBn9 zcpJ}$Qj-M$IH*fsb3QVU^-k^WU$OorXG1rY*5)pF-Yxl=u(z2vbE+-uc=|0)`0m6> zcko%kqNlZIe#JhpVPQW@%MN z(-W7b2Q1j~wtZ_<+sZ?$R0sd=bUm#%PQ=W^TMoSpwkk5pI!k z*vEl0j$)!^hcEp>R7cT)5K=@KmUDFxrr*8Cp2qy$ASP(w zbn?1zREIr}LOT8RIlP~Ou&dfg>`u`#eOW4Ce2&4ib$V}Mf(IfDSok~Rm%kDF$rE9D zUW*39cJ@N{PU6Y1>>}$OacRT1++67Fo`+Wrm_Esfqs? zpd>H`1||#X&Fyx$ZHPE$aC3y$D&A4))&``jq^M^Llw?=Fq79nlypvmhYE@qT7kD(i z6$ryeu#MbO4*cTOz(03gFTFxTwShvsgE2ge}I%3}9mqH3{t+ zm%=2!PpNKOkjk+%rRXp$N(VG2Nx?*>b!5G?kg@15*xaRY&K-33V>AQo&HWfg#>uw< zpGA0S%OFoz7vodp=`CREIkxT>z=-XuYw0;M%+AowUG$I?jolyKJa^7gW(>ej?)(3v z{AbnwEWVWpimJW+`R_NT`oArN;*~6eyj@+*Pno8-!olXwGi@pf!Gghvv zA$1t@(fdvoy0-X=4D9hc`D~F#P9+Zl+Sl^Pata&_#0 z&~(o#gOUa8NwCZ2?5coA_1S@cwN5JgbwQWTURDMrl}G#6if{hb`l?3OT+;(UCdK0R zNE_pWBoAjAUtM1R+^V+rks;JBvUI-Kk5g;<4PV&*p{}G%a%4v??Y6|PG-k3N(LMIQ z-WVE&^5QuKgNO;qtH|5Vza5Y+3(3nEbm0$aMo)0vExQ0pke21s4azc$=wZMR`27Uf z^@lvfqB)~txiSQi8v4RpUpfqrU+=jsWTN9djcTr6jR3x9L* zjYTJyfhwc;*5$>iPg4_1*ZNxemuB_*b05&BCGQCG@8H_KACNXM{fBR$?BGUsr~YyQ z%kK`|MZ=0AS=~h*0wXF09BT(j0{e|iBM2>W{QFd8J*>DBE^01|fYD<8!a&F30BuG4 z8Fm$>tWet9RDv4o5eQo~+Yd#oX}SLG$9Xq%?7|Jbwx4*j6G__kXJbURY#HSa2=X4xlyp>2;Fob=q;TT9@!KIQSBi*!&LGx=XFb;5+K76znuwpiY z3Mus+e44X`T>A0;07x>au1yd`$?vi5A(#NSPk-%C#**s)t?u3UKWeRh?QP(HhCiaD za=be%FVR0!2UzNK)-C{&?0eSm63N&u(lZ8pkLUncGHS(E@FDG%QbKvABg|y7-(-FH z3H_ig@G~?c^QPM%Qa*8O-?E4S6jO2+5V8(|DfvB;n*~t>_Su^S6H3Rr!4o`4ntH$! zWOX4J;`%XL;2GjfPMIA7$To*8F@#4k?vl&5YU(lIS zTe^WYU7Wj>i_#Ed{s-G3pVP3kDR}NFf7GWRhbmiZX$l=C<3}v3Ku_$#N~wUN$B^&O zkI1xu0fqiM>i+#?Ah^nMnf|j_9WIg5MyxjoF=MekY%8F%_3K29WB& zdmW~08LQKgKcmlW3Oh}k7PR%YYD?XIiO_U*n$~Teu%<3H*);M@umR#)O?C5}r_tG| z10QQTP0j6%@p)$Zx%ofF5nZWBFH`#+Tfr~4*3t=S9K_duM2vaBu6H%D2+R{iH|wLA zzx>;jmbBtHer1cxg8pMi5g*LH%aOVH{F|w|`prxzd7*ds+MIk>H0Ip(lcKq>3wV2S zI}AGojMkBoNlC-m2YfuapN93$)_@a9ac$eu`FCW!=0UmG{5ShJ-xWju|C-0J*{yHx z|FMpW55EgY)=()ls~z1*Px$#Ni|JvbF4(+6L_FGXq1o*!{pAfBUikzI*tl?%zH9+c#OEG|SG&V{n>!0No*c?o(u{i}TO z`2n8^2_CsK|LVwHEEY@Oy2)hQ3rqq#0YtH=UzLE0IvKeiX+4<{A~$}id2%!CM>wY2 z*V;T{MUU}BSZOcNlVH%l?7IubZEvX5QpiZ9b z<$YqS1V9Y6LXu9B*2ooH?$aXu4b>LS&#FCjlJ(cctLs)4XcUdUVq)c^tx8d@4^Cl^ z^E<%!K|{ns@@>w3-ioUMXVFf&VPee=D7qCDfoJ4_D4f3G-N|!G%CA1<9ui*o9Oak4 zEzL-S!W8YU6B5+s$-fS1M2pFd`XCN7K*oR22A_cVGA@81ia$H)0(B;VTG2men~g+f zx^izx9@l$<+m%V3Jm}RSpIX0*70TTV_09m$61}yYBeuu@^Rvy1iO=T2Yt%oyz&+4}W@L zsx2wIXG@Bqv-CC0=iDI=;~2Gx{64ToWA=P1nl@!@VhQ07d>%C6qKJ~QPYA~k%KZgU zm1+Mm?rVJ_?r87d>e9%R19QJEuZ-9$q_ZU)f;Tf03{CLYCJfSEtAfGpniF`K>atgY zooU)$7|>pkR=tDje5otQ^+!*Nb5TSx)sD{-xjn@RQ=+<&4UEJ4BIO=xaZ#Cc4{EU~ z)IitoJVk|4#DZR6WxIpBPhqjdx>OfXF0qgj+nT~$@1^SdQeth%ST149KN!mB7 z5WB9&f9qu>S{2VTCV_FdAsLI8r$RFn*0^|??LLUR#M#jBIN2`S>UjOo;6~xs+UxcS z&!~2*YIa$}1Z5We(k~u*vUxXG0+Oh2y%#R$*P^HS=yO7We+4YM+5Y#6!uK7}X|x`k zxV_jw8lSU6qH)jdgftED5+Ik8!T`$t=mK*XE|GP5Ppt9jp`kfq*SnX$E6cGI=pA*O zbi9>pYfOR9o)Z>@hpi5DXe)4XR}kqB+IH;=MsFWaxapF6w|Z@cnuf0~uoE@gQ*J$O zL5^9OF#y9bmR%^wFN;Y>Pa+R!+;PV9pq`Nld4v0b{nm?%_=zeP*`$A}s(Gp$kps_~ zf(Ki@(@xEb?|JFMDU7|3e3*xzzXi%F_1Pto`x>eXs*Ol_9^+UtuN}LL-L^aYP_YF( zvvVYcsd0YseiQ+e+(OjH>2hX!4Em$N8Jb*MUW-hLr%`rJz97pelpz=Z&iRK-NL({Q zecSnzYB*pMq(t_jJW~VsLA_<@4Pm(1Nqp3zIGLAF^t-e%LqeYVa*YK7M+vrLI z$YX7%mqOOi1=g?U&j-KJ_V;`3M7v)n*iV!U1~^~-8+O94{KS*BsvTeK=5D44Ca3K5nw26NZz6z2)#QP1O}g)OA> z%c9H1Q=t&WUim05sv4MF}N6n4b%cs zXO^;|#@fgv!x8rHiQ_RR0X6cQTNU(GCO=+tK3VANd48^^t;j@*@8zL&LUTKL3T^?550!nu~x^j)%a>dBOWEOuVSKw3o}1 zl9Q#_6Y538Q{v_~x$>LLci|VP3GYqS`pb?gBMqMXkvsO`FMa)IdZgE%hpzzkJ!BYU zdeR^66FCSFi=g8B0|W@y!BMU3ZprNmXQ&4CRDz{+Ie_j>T%gA2LP|MVl_Ay0W;iQjBwp5mIa5Le%a$MpX7%%qwAoIQL6_3LtZVz_15Ov6@ zL3zcKZNSPhu@*irZkupvkh$?s3)gF#bFgHD>}dS_g`T<^mcmu&De=3w8scQcy>D62 zwEHbn_5YCtkpIY>zdQfnpa00zukC+u@jo(0;UD?uq04Mg{LJ8ku;L59aHQuoHI@eQ zOrO~cuL3|_>Qw!ScTn*Ep(Q69mDl*XNEAY619nezM`s2M8+{PA1qCcnQVy~@42c^& ztiT@s>S}hK7&?aZn>1s8keYCZ6tXpf%L9D>Gy1mcJ4*}41}J-DtvUaAo+%eC}(YK+om0O2MEZxJMCaUrLfR#OyOE{ zK8#LLx8nhxI8BmeNkA?-R=Y_WTh42GJr5_ViwiM$<#WFRE^vg%Z_EgSd3IlZ4-bur zXBG|&QX)|l=RA|Bbjr$-5{-g0wwZ6p-)K^$;7!&H6-(rF>aH z>^sO|fI8v(2Q%@q?+o_?V?$~;AbM`2_C#yUjX^V59&$;zK{`lh;Te!DlGdC>`*~t>6n*fb1 z@7n=vk)XxtH(x~>@3}&vo3Swk+cQ;!SiRNE+_Xd%aLZPa1_jP~^WA8Q*&cDc1o#g~ zo?;;^xB=Wl_?aG~-lIrFF+#un)C3u6sF5!=d7cw|f5yEu4j6S4wUUkJmSvPl4Z-D% zbe#FB32NpL;g+Hp>`YIcRWlb@eR;W7s0tZ1e4F zBmiDpHeuJ}DeAJQT!v6=u3a9B~{z1I7!pa+XEK4pv>{1E++p}!$#-#;%P z7^|J%5@@7pk-M{c#~Y)aj{ZahtIz8>&(Ww5SilW93bC2H4732=rZ=V^ z2u|{FLn=|&4g|aPT?niWl0JVz!(V%R6`xsrODMyx+KiL1?MP3XgP6Y&7Nf7QZ`)|!hf;N;U4Mb}WGYL15eTq=nI5%GC-su8JA z8K8`*PtilW;XB$G7*dc$IY-JfbxbJ8^r7UHL+xV;&1S-O4B2D3iExJ$Cfq=>FX};K z&;7x#;yV>d}pj)_UQn~0(pSDHHf0b za@nQ%9PS8PnkVNb%Z8e^>8pc?&27hmf#>Ztw2NS-2u1bwU?r zE-U_yc>-z3yx;E(`YZ*ICBRRI5fy#oj_OvTg&iz;sm?AP1J|1jxi*}~eS_(fB(yTv z>}~=MIFQA3hspC{>_6f$N<;ggR^-H~Yt;cG71|w#=S8#@ueZk|VBf%-siTvQ5JRC&9Q7vU~dq zJTQ`WK`LMdH>(!9vKIs*FJn6_4=(?vbG}zcOB~>Q)bs&%n+qGbr%VuI!W6{6XrVp!AGJDu7{pWzUk83z!>VXiZvuQyjO=^@Uk==3NrSM;0SI3GMX<7$a; zEM^?60(S2ES9Lt~kO{@dTKw<9>V9Yo-qa2JL6c)1nI#L#-yk*0ed`ARjYq@;klt+G zLd{n;QC|3OJb|8=3BxE~q-m0#R5Z$Bd`pl8kX2k+0}uZ4kY!hxl2~4L0n! zYY-)hcr=@G?9N4o*q~!iy75Clbfz73rVLIM8e98?uuNZ$L?o<6yG8|CT{r$>S-`yJ z?0`EqoGj@JBmzIZeZB(9S1;KCY7gStS8iJHmCS!fL_d(y)B^OLlf)yBko;snVqw=) zq-?2H4r<%a<}#6`u5KOi!zuh!$R*RKE9^{7p0SRw!1oY}5bzfT(v6|%a&md2E>Ooz z9th^m+Ey%^v^wlk3Lbn#QMKHAK*&9@?S;<91eruMYNEi$51;_7{+M+dL_ml+UjB?= z7!O?Ua>aXbK&V%zckk>1UceS0Qx5M;5R{Z833OrLFMo((bOcQaqd|}kB?zosf1tJc zdW;@{=Jl@|i!i2FjT1wp0%p6~1Iy?`z zGDBu39bQS7_9(-U2fEWbaI<-5Z>ixmqUImq6)x#nusQ%0_1lIDIWK(y_}1@g#Sc>z zC*iqj@3r|$YpshtjcCDJmZm9?_cYEW$Z$xji1ose2lM%edBp?HM+aQ$6Yc3~FGFiv zMn~aeMW6lzU*aOsW}lLT$CYM&swL6lFqjkK<;ga9op(YHWOtHQA*e3Y`=g+%s+6R$ zaC+*eLpA_znDH>aqoFPgas1#Z#@lVv5PvB4J_&Jww7`2mO~0lm}Cm2{0VOgF;tt(-{P`h73=%=c6ET#vaG5M*Wfoj+WH}(v)Gv1^h@-u zu2MA`^Xb73#$l|&WQo`Q)}YEC&gIx1ml{12P+g3QDo$`TX+G0RKBjmpBgR$xv`^}c z`9@Yfn;{YUS5y2zRCT^RxCPVqEz`>UIvxB4prJQSooVpud#%!v5>-*6SWw3dDNMy{ z65s$xA_O}}<>*yolE)!g13 zY$e&fk}OZakRn(`i7@UuCP3+w(x|R?D`kM0G9XBOTBYnk7{wJ~*cr%XseC=IUS*?f zv2GI&UI;O2hoK)>Hw7im2VSd$LUUBm$D1G!En@OmNK<=!c@!V9U0;92YR` z70QpQ?^9&+!h1WbXeKBVDmYTz1Ta~Vd_g4HOY1dCC{-$2U99uGC-FXg8y!kQP_a2x z4lQHWq<^@{Y}wgsZ(Z8SZB4ScO5Fo4^ay<0I?#Q!yGzc_*2;){z4_sl3Ol1|2_G;I zdxu=FNE31R=Tw9cD%l^(VcNlKfKLMqm2J@r(d@7^J&jA=3zKk%_HppkNRf2h zM(VOqs^cQmiQ&`0*+dMcW-`)LFf+{GE)K~0r@ixLRgMib^QIA+E~S7SiY(iJoW}>= z7U^<@h+*3mP9L&5yZ7z^t+}sk*WZyGVfUU#iM~q-SqJj^qZAO8pq&KGYnf3W&H*Vr zzjyKw>zwApBc+fTnTG{{AaMcW!)Xi8^=Fe06qx1)^H-?DWzc@46TP@FdAhGx4KH(v zh=gU3I1eeOFCv)%Paq@q`{w$%u6yf!`<&p(#XS& z(Y5gdhv(L1fkT|&5KDkJpQ993o7e6+QI!)ecx<-01I;}xEWG~odVivl2>BWwE6 zW|_EILa#UY7{}8Ls`3wFlS!?+sSP^2g^p7NN#H)62_sr&lPen9_!J?$CuHTSH6>Al z7yrU9qdfPJ{^A=;Z&e|zx6E6>9)Yz5EKXhRN)iIPXs>>r@M&5d%VAmLyOKFGBVlFD0xn=IaBr})2Y#!>~9-Qv?XxE3{JT2|C@};44#=CllMJ9I#@LWd!sz*3eRe8ce#EE z8+n!%bb$XPn*7F4rnlf{nR*Vl&5!OYmUl!|jJ&ww+v7*3TjuofvET>n84q>i7}oU; z!UC4yW(f+L$`B*Qd$7Uxx28+{a#1k?3RcVCOH$z%u|Tuupk9*I%3G&p(QZw1R_-hh zmFmR4AbBfMmh+h)q_i%Kxuo`{>e+6z7>;-BB%7ptp+?g0lVh0_DCw7881Tqv+ z2^Tj>eXmLSfwziD{CWnQxtW9`f zGw&=T5*ueq4zKa>E=dx2qRt71?)v4ly^MV!WwVL{XQ8gFimmm*PTSUM0dYYb)Nhez zL%m!Z;Hx`u*Dl_$I*rp>re3~KN63wHD=zuE;ww3QrNCidzL074@C)!5@Y$sO0qaxL zq4m77)@07)kOEYou2xyLm}lPbbIBl$!Uy=P^mE#d{_^7$u2^HiYe5{7LfN*y%&TD4 zLLE1&tnBB48#?!VF0hOC)x$H;gWFqJXlvmTAg4?YO!6S|3DNd{11+2Tw&J-UPi zdwB*--w9_)5eVFcRo$D2g#OH5@wW>>?KH=_vL;6`_-wZwUDzGlr!p-}oN4NM!)JE5 z%8zL-{h2xfdF;0HohL?HQ83{pA=YDm;^Pi%BiSYG>%A~}d$B3Gb8IA7n_2(K`$xnH z07s;N=a(OKp7FTnqex~=jHH0v<}+NwjLm16RTg{q+yyI53v=P^fLvjlUS;-rH|TGI zLnyk(2O3|+*V@OXkj~9k;b_8DC>Oj(wfX}8+<@r#GG3hvC^5ytoIw+AD+!P0w{RkZ z%Ab%~*scdre1q*osPnr1m&8h{i_&6vfGSZzy3D|-_dmByNJ;virmpTKzyB)kJiwvz zvtPc<-x&zH8J#&cu~FU6kARI^3|*&1i@KS~u*pbssMo=|o6=SC-q5wH4}h=a&@i{o zkXIiAk(y)ZchbeHZgH{RyU{)SX2()L%@lC1pE_!}pz2o3=A-6ZZHeN&(CMs10w#Tq zTk@q0(pS%+60?Q15SCr;r#Hi00x)IR-!yB=?jQV5d(VIugqJ|e#Qs96p8f63)BJ?9 zzv`Q(JU(HKM}>9sRKg1lBy6XC?ODqwY^QzgF`YsD)VUApLi^bEok6Vn<#(NT3`xl0 z+*GJ_DlWkkay!~XRCH*{h1IVL1Ym#+O8Yf;V1T5O)PVn5Jg&h9&*3YIg zBAreKV5vP#U`HhosD>DH;|%6)5p?kWOcX&M2RRbBxF&L-8QkbbnkazSX7bLQUcm|q z#E2=hWWBmJ8Ww;-B%?V;9f=n}4XLnCg#kAC#(buM3O=9)P1Wys9>&~!91*x{obIZk zUnK@zagK-+*h!I#g&P>wl*7whO#qg6(GJFX*Dyy0bV45+<7td5qO>Xk?O<^|C^3bz ziyu}+DIGxtj7-Z&E)9sM0uQGm&j&-o>}JSk4M#DU?zIn`4?{8Nzx;vrM=Z&oqTNIx z2}B7Z>H3$zKhZZ(2#yn28U}65RTFjwkJp?;V6M1vnnAGms+1TgdT?^P$KaYW&&3rlofFk=$G`fp%cdu~0eI;y+V*OY5&H@y^51#+ zer3Xlpj4*>fw=UDY?CE`>nf~Ck|q&cC@=ql*bW_9sMrOqkqbe%oVy14{7n-DH;s3r zcTj-4NrcDb^Q5~rkTfb0D0a*4c5$WLE{+ajY4taNayQKat(x=hGkh@nNwb2ZSFR30 zXNvT*-*n{K1>lg;cxg}s5{f!&!B3E*^pg<^(R<$|7yFN&|JR|EdJ>{#6!6rNuT}4R z)XnoXk~*zK0GkVyrN#7qzF-a&ZjiYyOc5k}2^|Q778Ap~2`3 zf24$Ts{jyt$Ad@{;i5|A>Sp_XdMFQm%BKhq_hd6O=cN_wJIP|%SzC&uoou0YN4Wc} zWgYddpQ0V`PrKp~CJ~i^%xGKnjc!PItk5nM>GOgfqM(u}#5CW!$Scy`PXhS11C9Ld#Thhe(W+hnf z-|VALpX^V6ObH?Uf+nLLDyy@O-u0nbj{G4AH`@o!0Ya1{JDm( zg}Ir>*DI$>sy=MVzUjQp%T$wcz^Smk69x3yd$>b@oAKeCp~ogmhQM;}%gp|}`}FMu zQ4??p_9IY}EFka*l+$h^XLm!iswqD(Wl?#ptL%GY%8Ko72+qANk}0`f29s{87n!A8nv?oZts<5o%CiLKH9Gp&YOhp` z?X?*-3W}=Eei{|E0P|}Q44k(f65RhAD z6BUfSu z#88*7tzNESwH_sNG{a;G#RJE=2h2mGB%f&>*j5Pn-$txfeZu24 zbXoBd$HdO{^}Ro9)X<#jg-R9$%{U38`ppPx%pfbdybbT-Y8=&bcFsru0Q1NQsDsPL zO4T8R7}Q2GK7!WK&q}5;g6!&B9e)}m^H;aru{x0pgfM~wIKZ!&<7<(_tVL{Hy;gsy zxtTri+~}Cf!kMe1zyj6d(Gp>s*0`yLAt${<=Zn)jCNQuv*B9zQ8|IPp9SVGYFAUUAja+1Dp-o&eM!`LCQmIeko$*g>(%cm+0>aEVu{CA~N3(8~p)) zcp99e5?Yet3wJMU?_nh3?HpHkuJ5&qp^%;E@p{-dG{UX-$?74y@$ZX76WHZJ@5t!b zkWBs1&pjJg0s0#0UJ*v;*xfvTBJ2b>cU)~%We=1l=NjRWugK_V$xChK`x8xcX*G_e!|rYM zEP|h1pFMlj$FoeE0ScaEm!&jVWwG5*Qgzi5vKZ)M$*MH)gMR1#B>3YRPjeeG;^yGh zU}dmT0}Izdt%SD(E1qe`6$)<=~%S) ztoNkU^s%GIFRicZGg>9TY-?%=kswDe118k<&H00_(mChyU`X@VO@4zAiaQ@C3?XOh z`G`JpzMT7=n2zpGbrwgIz_IS5HXgYCf<*1E>E3(PHQ@*FbEfFJUg!>?KsE_sMBAs7 zO=|(y3cy$QzBj5S_V)cUTkXhVBdixJdZveQqB-#vDCPkFC^28+bQg(7=s2TzkEuiL zTS*HH=v~jr_UQE`XBa}!{VdP6hSJHQy@;xhVM+7v37SDPmXq{Jo+Zq~th12Y$%lN; zBQNeNpDZ7Pj=0f+)H2gCXGZ0+L51abBy!NWS^$O5y|YP*GbX0#cAxxkBC7#1q4^c3 zLSn6Mn&?@XrXUHE5R_mjo8d4CC{&2q6CvPhN;?is;^Jz{ju3RWOYkIQrHm-@x@@w% z?(SvGwm86a4%T}GeRwA;v@V0S&g09f-Ea8dZ0TM+1_>x+GW7-Rdw356w#&3ELw z9Cp`A?Ln?bf6^Hhen1)19WVW@Sf=mSYMnAoH%(LAsUN0aya5SIa7Nf4WyR?I2GB=G zTx4I3aOu85@jJbr_jH?WQ0!-w2~V50l?_!{yGEOXpB@R%Nx}kX(Kd{p0R2lvWYVtYx$D zM+;54827&xO~F$JL0ISfyTFLr& zw2J&;#Ue1Ye>f%FNVkW8LvIbVCznw9ne{egXx>+bNx!$ADIv+q$HZP;U7eX3Je(Pi z@n3!|J^|P?O{VZ~V&968v4&KJ8VA)KD#|M#|H3!$GeIWLDd#xz;uenr;uqmwRJZ7I z+;R|e7SEcGy?c2{c2&37Tw|i~Ijn?Za7D(L-PW65u?$p(B4POz`w&2R{Y?Dss=-{# zfLQ;s>a;ue3@vC$PI$u2iJXya&zyi~?>rM(rHMWdZGEAt(mlmQPCXm$L(>nLZYtSE z8$kxsg%EmDkK=*oMUY1VCM18{nPahaCM^4&4boFY;pAd{AkdehX&=^WKb@cO5mbor zN+>Mr4Jj=D$`ND3%obyV%@Sja{7wgce`BeQqJ49vi6ajTweJZtNU?)0q!9Jg)Z!Mb z3PGIGPY3EL3`tk{uy3R>8z(Lv)ZYrxkQ1gCECi}*Q>T)H2vFT)F<1$r;H22>hY-4l zA@qXLf|w^Geht86M8O};6yh!*FlIBe7TLMbIiHw#ATG-#k4#D$Bhra%k9 z&g)1+hGFhmdB$MHT^{js?=Zw5tv`(}b7H@t!_~~gtP$SxtPRA%yg{uMW-$pdQLWNa zYt4Q5%Ev490!UI{U@QH~VAGNjb{a8TJQg0$IEAi-EN6FznIN ze|OC#BeU0XHY_{Wor^N zpGm5P>McSNT9$_GeGjD0fl{p#BZBk87T44dg%o#;2Q;-F4dl(v?=cYy`3xA>M~X8_ zuuWn@_!{R?%IYIZp2L)QkHJ1HQEAXu@QOw1rL+2bm`q=_1fJ3kniy#xXFB#V8;I|s zG%zNT9Ca>5i|K#W96{44y_Is6*3U|pk!wYkMQ<-O642K!A zKEH-zYd=Hs!UaJUl^VH;n7MB{cIxrJ!B%H#8v@IVGZduR)J(hUzMO>h4M3iEBuYIY z%?f#yERN%!r7nwg`L)Tgi0s^^Q9OOQtf?I^0#HO+Hq|3>8++&x?l?tHGYRb0czUdc zJGW6XrkO9Bk*Uk@bZuY4RND9TKs^~2G(f}fGoLG|3QH(6y9>u*ok z=FjIIO<-OxvOI>ffM`Dc6bfMtb`L56n8CqTy)EgH36&DuSZz*2>`M2m8*0SI2OJHulMiVn^4hRnt|~AYQ1oAwKLS z>X{_yk8v^y71PtF+e6Y@dEjyy8FrymW{k*RXcpINNN*P?K%s|h0O!6Cv+Do;ET^Zceo4o*D$5~(?km4 zeeZ%b)azh5Vr0358!OTndbS(@96#w!$p){kHG136;I`OiOWVH1+@f4H--DjE|2b*Y zpj>Yai76XR>nvV6{G)#~YBqrXDKW=#sQ^Kcoi!t{M-P#$Zm6(w#j#8W=g0EdTgM(%W%EP38bHG6`JfDS2A7rT} z^CTq5;xlc-%(M9-J6ny$-gVX%hlB0Jjx@aavGO!(%lv~eJK-Nq%>?)8KLCAosOo%d ze&*L6G!Lwls=G}#*{HrefU$jDsghu!)4R1(X5=Q&8Tc zr5##kH=)tN^Dv&|GIlrau;fcVy2f!jHFYKjFzQ{&4lop0JY29swRC$^*(;UoLDSr{_ruJUL`*Hg*>eQMvnJGu&F8~Bn? z3@V8x6)kX4M@d4V8@jlrq65g3IYd3W)?YS_Tty;>t+3yBi11;n*mwHRJs^B|Th6yp zdt(l2nCEaHh@2`ty-}mlER1EB>HyvTasPOcPK4H_g^6Ss!WA%X-5ZDY*B|vyQTwBk zQw`@PB!)L&Jb1U?`Kmgh-cM*uwJx99BaaxvfcZ7H-j<8@h8lZY3jzXZZxLeG$Zc+NK%)!qJuoele*U{gk!fdMtes{T32l0aCzQ+)A5QIT` zhl|$H&O7ok(C^6>aM4Y%J*;CHCoywUIIwS{nh)k;3L`C8OHkuwxv;&F${>sICh}__o|t zs0XFbsQzc}eTHBmxW@6eGoN*M>O#mSUmrkdWx#2$$AS21(0Igog0kT0AOx&X9em*W zSK$L)qdyJRO^|brncSUn@$7GLw_ppJjryYA#)}6w^v4f&)?}ik9n*wI{p+d@%|>4B zZD)72R#@FHU{YVL zT@=k<)cnzJ4GB2T5GK5v2DJ%ocfy3LNa8r!65h6K6!oO3G zLF^_AH}uo0=IR9!dNy2GoNlTJiQNJM#()IoIhu7~*0C!ya?SQ&O9F?lGI>*Cm8;jS z!OhwDP4Wfk6nqLusL;{( z;6!{NAkB#XJH-6IV(Y(?fAOF6YW<2e}Dl2{iqWOe+35Plw;va6#Df8go!QD=@;t{HZoaDJ68{V4Ni7_ zc2*);OCu*!1v4{CV^e(xCwnVXW0xN+Y>c){WWuhNHYW6mQ@;q1Sm+s<=$Y76nK-!_ zS-2Tl6F+`svapg6*_yf-ayuA08Jas8I#|#f+q>Gi@R6{xF#T7SyS&p4HsutW#6mU zjj4GSzYp}~`;&R!$=`Xt@iL-E!0pe1-2jj-d{!qj93DiUyLeA<>* zY~tlf&gM{?fSm(-B#1=_Rv{CVZ<(OAmuS}XnQ%kxT9Y1)7;O44RheomRT)TnSwS0p z>ki*mfN7TLPjz0DsisZgvqH6U`qZ(yg8}SO&q_aEU)?(QrdS9nvzd2|pDT0ry2QKX zq<6xNSZ5WEe*aHzUmaFe^X*R~Al;49ASEFJ5+Vo^N_UrlNOv4jI)wuq5NT-v>28r0 zkd*H3ZhrfKzM`++d++bL&vVZ|=j^p+uRZItKWk?8nmMyQ)Y@xY>0`xeP-Ge_N51G) zo>j&MzgeB(`i2!d^!^2FZ0eZHY{4E*{WiDRc+$c7*;h=LaY!9qw9^Q`OI$a3i2nLm zf8IL~p~_zDa82Bd#erTLCW#Q6$pItk-gM$rVBn5;%KI%gtWH8-h*s?jH-_i#RdsVW z9zNk(yb~S$W_XKTY_`tJG2IBY$!+(M#6Vl;k^=GpI%!DsrnXn z)QBFwU72|}j^-GZRTL!sN#-Ghw{3JZ``)|Y70|SU?bf%diVQ;Synr;aEZ;!x6{M+w zT?RrXB)_?+G4UJ@!K!V{g--GM$cslo^oHj5RtOmEGaLF$%%$y-ok;?&@M#yGa6&nzY9zM7{t#BbZKj;N^6ir} z|4b0b*U(q7ZP!bVho9lVg9szWaNv|z4>qTD(4cdc^)~aS*n8UI1T#13hiSH$`3R1g zg0XP56B*3NDCc#%$(?vasQb=bgdwLlJzmCqiJ?PM1v6PB3gpj5=`UjfM164arbQ@3_*+N zP>!2YnX@AMWYW$}q9=+d_}_azY=iko_!Hzc`iNxA`s{XRhbiw4%5Bv?@28?Dvz0)=# zE>9sdi#!cUG#zK1Rs+XB%adJu>_#Z880h5}>!su5fA*j!Gy)Crj6r#p;Z*`vnBTMzZ?bestM=sM{gX*yJ6ch50@*A`xb2;uGnT_IpES%@<}7qWjliO z@1AouGrGnhkoHW!UUp2Aowp6jycu9HSxn9yv=HKX*Nl5DnXQ4x1dqbT+|7XqQ-0m- z+}cUnQI|@LSYCBOZJrdi_#x6p&A_)8A%<^a=E{fgLUDAeWry9c-%5LLN`6ZzXdf)O z#~kiaUVFcP|9ncW{bZwk>~fnHiOt(w3vP%oFb}AI_ACG1q^6K391j-gv(6r5m%{*k z&>NtS`+aX6aPJ<)N1$i=hz8aLi-6EI=x>TaeuC~2j9HKU#h96zQbXkX`V*Y z`APoq{h;?eYo4jKihYUnsa!1|Tj1(5=pNjp2`G4hrT9p-g3i=G=uR-!U}Kh?*?c3H zy{tLMLgVPhPNna}luLTTM8(P6%aN14W?ue-QQO+MotlOBaRR0?GBVhccfJjSTwo;3 zc42)5C%ci!j$esVS}nq3xYs3N^8VilLH}o0`ul}Ie{)pwi=g1iYT-uB$@+94TRfMk zd?>|(Y3;F3dZH6G?rgQE+ht3f>Xi;_Nus{SU*8|~J{3|@Qo<)7_);@GG<3L_5?XUO z9hjM!=^+pq8R>q!+p`c!+hm;KeFPqVB!#ydFxsz0wQ+Cbsw5tmW3SK*|jg;mAbxBJTXVbFnd$PBZj(I#EYw3ednl{ayPHP~`)H>vQdVaDoH#euE?L5cXC9qQmk9kL{ z!fs={`q+84C5)WkflR>3>STZI-h^ABLuguY(ihvyR{8`@$1!^-u9X`#BW}g1 zsi~zC9w%sQRhzrLPiuF-NfnxOtxwd}&aV)0n)PL*$GbeKl*#V*{Tj!wQfn5MZ#g)% zFrZ=CucAZwE+HY0`g5{aU=*8}!2}4@^7a;q6}te-M{efzra;0x^ZR|Z(32RNuy=Ldlm zzIo~ejvM3kQhW^wyw(qKk7MHFb5Y{xiU=LoN6TFg);l^nA|miQPfvHc3JVME-!2lQ zx>p&sN0C}qalBN_;^DPHNPC))dn+?XmqtYgCP48s=n_~4i zP!rzUuXpR6ismUj4yRH=1&@K7XkXXO9W1E013Eu4L0<~KH{PvLgp-;mn3bKqmnD0z zLi&4!{mPi*L=L>qmrtMz4=uT3!w!q`rC1U4=^7Q@Aa?1`~E20dJMI`7$Yb`%u1CGP1i;<-F5yq}~`r%()wtsgRDu*{EWllw$jqt}k^UE!2D}8vNw$ zKK4S@&ez_qzKo%xd=NQ8`+J!eWPJWyAeHotp?)7r6vF2Wc#bayG#58DG)qOH=InjMlU!mDeOBcy;%C3jRm6U~=ntI?ME3D2H8Zo+iMC2&zM}zk`UGwuzaqkcWLM8&xa8wikSG#5$u1;{lFx2& zg7dB6pvZ|Zuy+2>t{~lg-+FttHIiAyVd-0HO-;?GPgXNuf=5a$V!6$gU0jYm;gQ?g z+et}Cz66s(Vq%B|PWrM{@>W+@&GV{xY>s=?jEsy_G_9K!ds1RSpM_R9(!Gki1fy6% zTJIo`^s_AYi&5LIF}q$FUrb7jmkG7!$1A<*Pbt}Tg@K-Gwzf-9CYHwnr~$xIz1iLL z!rdLf(iFTb>f&dATtM}0*yUiI{4{UO(4!)AZ`g!V$K%LobEC=D#wOlvTXGM(hRc4r z?`UTMlS1I|yGa82yrPm4i%t#b)Q9xt4T%4V<1N72a!N+VA`k#NMbE=RSj}z=2?@m{ zyfmxc|L$8e`Z$EFM=d2cHx~%5^lfC7BKE>ybU*jKWaM~l8zId@Zn5R>w zdbsgWQGE2BJ(XIaaj>j(J|}p8DQ&XF{h6VZ&vW0$=AAK+3hPbKG)vk{@L4Qy zJUiYCNJ&ggRC;sb`@GBjl-wqH*adIle>+wH{U44s{x$*t{cndlk{?1b1rHN}c-Wcm z1a9k82hiV|n3y!PD$B|B3=Ks`MLC=vZpo#H=jhZ53JKK~&xk3Snwlo1tIJ^yM1(MmI2O9bL*pugW#R#{nDe?%xu5Zdbl-P+#Th+}`260{k&wzhV5KFT*} z=Hfl;dsWl{G>0WG$Fc;ulHwI5Ei5eH61+K&*Geb&fH8uanwplDI}kSS-n|P73hL|Y z!=@Hr{a$DS1nw}li|NoOklwM4+1IH?U)+&%|6LA;)gfA`D1oidxIpwON1vS>0NuB% zq-1MH3@7)1kB<+P8F!LD5JoGywv%<9Sy@>Kf@mg+Iyyo??>DIJnqxkg3p(BMb*Nt- ztE88Cr%0O0TSQ%S*4x|r=Ke6Yzr_VYo)7`6W<~-4ny!KZ7(_=W9i3Q@*gJ)739V=R zU88`CfjnG#S?Sfn?$Jg}Au@m9uhkvp&~M9v|=6u!9s^i$|$p+fTQ z9#Juxw4foQ*SwpKH%itHw`ShxG_FyWQmenI)%mXnwgSI~y9*OOaxur{>M@qkl$fNL z?4#U=y08E9Zj|^i5II;_!a}@gd}u~1_j}P2E->2f!umPl|I5>XP;4X$8S)M7eey=HD0PAI1RKKNs`cNvUbs@R3H5mN5#&uD;PIV*dkkg6 zdDV}*4|QLt`XBe98k}ohUmpLlS!;d`BUtyR!%)Wf*WiFD&oIC`X+O4eg_gKN;UC)M z*WCLLfPiX#0RmJU%ACIw2rLZzRfw!Te5Pb8K2NuGZj>P`Vm_($Yel#{k3+x9*-)`o zYZGGF+efjt6{+sO`)*J}Q|k6`{V3`5Aqk9#C!RzYPVs=*CuG+fTqMCBNtLjRO@yJ@ zEiQFjD3WeMR=!{B8uFjMX4f9Y^l-Zvt|52Z+QwvSaicgsWTo;bX|Ohh;i4fm zx5h12Q5tg9P&1%_D~WQfY#_&uhQS8+7iGO`W)%DhLO!lpVSEgc-7*+mxAq!dWsDSVUN8-TrTQJqo=|vX0c@OiAmDB;Y-LJKhmubL*34;IG2Ak74v? zkkis|-W7-q>erCSRnc*=+=wZ4d{#%E$DU_Y5wP4Dxc8(R5gm?&;VmbHXQpTJ2BH%_`S%<;EI(ds(W$atU>#N;IYPj%BYn@mCR>&T!-=HaKAsEq2!DaNN04dD|H za*S!dQ7Q8sBp%^h1bDQ4bYzhIXel9WmiZBGUQwNP_Qk8}I*znk85;oz(O+oa+D9Mj zPVH=BBcDjP;=aCzu<>ZxxQ)q*q~V$H^A7jDvq|KtmfMe>K5-ID)K=+90KF|qus(J7 znXdI65VCsV?%ilSzFaSqwK@UY;7mGYTSxFguHd118-+03sHq(lMn@991~v&mulJ+2d(2wT-3^P(B?y!75>0VHsByn%y46Rq;GwC|Xlhb9G$} zu8|v~gOmpL&CQ_^go(Zv2Z0*e)Z!u|>cNlGWNoFJ$PbK+eCz6dpolO)(rMF}0Dx)`&ZhB+-9k>#`XxK1r+k&5t=Y--0 zDwy^K3@Ir=_++247rNHL0J?$2Mej=x>aARKV%QQe_x3Hye2AA1yWs+zXlr?ccqYz7eR6lZp8!dznANHc4)K*wZv=5s~U81E-G;FO9$u zFd#)kKWW#&ApTZ_mhrh7ldCFGyPv`Ng$I@#e_SLNTM&GQ{^Kb0?Ch{dPe^XLz#CDm zcx4(t_>Rb%;%dUJ_SzF2#DUA2FiMZ@dCHVZjNp^VS;c`O1e2OuD(kG_H7({t(CuX~ zJ_)`f%lN!0bR%GH22bCfRGMo31DaPG6R|AmZDTyCj-xuV^3{`15f6^;6wLIGXt~`{ z3*0n7|B~!TtZYr7rBh4(;=9`2avlfOO;C4+L|wsLuZzDy&CYjhR1g);r~53U>?v0G z1hb`mrS=3TrgT_$8-0`-Dm)KP@a37p$-Q)AIAT8&%chO@4`{aWRU*B7X)y=WBtNT6 z*zsQeVCcPkupMdpQc*;gCPOS88L#)l>|>u)!c3-HP0V;-c(RowWOf~14X71-d_|_m za{EkiRLx820gh5WaZ(3}H+tHW6qzNUn@`Woef2ZXNN_t?RH{iD)aYc9Zs^%d%goQg zIeIUjFX`F3qpl)sa(&`}hQyQ@gXd`=ai2Ca+!mthy>6tab*~13?Gj>11?3GZpRb*E zsO}m82hWc2#XYC*nxT06BrBV*5E66dCBjwt`sfHOK!{P|L$m=rs%vVpwD#6i_pOh% zEClCA1Oj*#8NV-7nvvl?HDvNIE#Pi9&|Lq@WeRRq z*-je%ljuBS{wIPN^qpPy!pZuJJHysZeg~fx(O3O_KURy3QTr66A`LKmj(DKwVPPJ- z;@!fMk$VhQ>L^eKX`T{^-3Vx|k<8cHqM&G$xX-s|tcfe&$HN2Hz#u_IWnfnL-O{&J zyK~aL!K}2HSpom-$S^1MjQayd=z})C*+UYeG4POV(n%iUX((|r=5w(yorMM7XeIBZ z)=BT;AQHcugv3q09ZWKnu+BA^oWq)D)3!I-*F??LEl%&+fNra`MN&6l-l?*Eny&z( z-h8CF$B!MK-m&5Y9w`t_p3;w5UuAN2c<1k$B2N0Gh~YK*KqL2q+52Z_b*#n&vxVrb ztRk~t6iS^rf&{>YCQh;$9W5kq@$qIz__Z!M$NlI89kF2<-QF*iUabUB;t?5@2B^#c zOVdj6*)-l%!+<6aE5+NS^Qw(gwlO%E;yNh|RL384f7}=ECu_E3$u=aH%}Y}-72hd> z5e)8^CP@y<;%M^9wKLO#+*=KlZ=+N4t$r`FiYfE1gDD|Jp{g_DxH2m;A}7#Oji0@F zRRcL%B)C?HZ{aZH?wIB+t9aO%Cf6qQ_7~4?vEYC7Eei$d2rp6-qs?HT9ITtVHo3iL zB`y`H$l;)5$sNwxHD%v!N>HSG}=9qy^%hWiTj}u?uDQT^|L5?>&srr=>xKU*Yt<03UuvA=^pc8XvB# z@klL98~O4Ul%2id(Uq0ap;v9zQ4GCpN4!e@Ut`RgBUP9(D!eJ>40DXE(hGukI&C#ZbcnWtcTt;v3Q8r`s_3R$7Giw?VtZL-zu=bYHNcY(u^G-L z!Pz8Q%eZNwQ?taGoEHjfuW6FXRBazr#k=&f%!-_(Hxax<^}03q%3-3#U%J@vAn16ZYbdzi%-% zVZEX80HuQ~ChF%`S=J|vMDvtkNZR3>Kb%^g;G6q0_4}DYUgHw$<_&)r3$| z-VT1^=PoVjF-p_JN;~VI_q;fKgym)q@t7!)I(6#pLtY8#bTq*)NAa7P2$VBBp#qCc zf!(*{Us~^<1!%o)!k)sI%3C!y46cym%|@_WJpu_q3`;N$tEKifckq0_r`ybr=Qmrl zTv&)m5+ILA)iKB-pcT(`t_QGYj8gXb=SXI^CyU(T(X6)#Dc6_~6$2&{(0gwnBh${1 z%vrKWvNiE-+YWd=Lct8O3_oM$ArT`xvWTezZi3}OnV{U+v&c#!UQW- z4k}31e>-hO*qP*0oTN~gS7Dp5uk;&zkmO>#X`3+lER{MMv-I7?lH8X<8#bBfr$#~L zzW71{i7AQ?)tDrh`HrOE*54nNx92E0TWn5k<)4ne;%BmVp1jMBzhVE`p)J(fOqfTn zE`AG*GSj;y^LtGk!~WcI@0(_e4+$)xNdCS|e6dK|jFXU}* z5XBAxl~uv`QsNzs;g>~6_R=UN?nm_O5tvvJ<9u9^@I%?A+3-UH0b6UawH|khpLwUg zb5a$uuJlnIK;E%-^T?am^z}4MBO#+xbd9Sz6BQ`qX~AN9|9JC?JTj(|+M+ zWIh-ch&0Weqzg0wEl8TPtCCeauMi9prL_f$Kc=x(?!8LVXj3d|e?86@Rcx4+Zj$yY z?Bt%W4ACdO?OW0mj(Xce>_=2|Pb=7+^`qUezxN>=%_%2xS&I+b`Jm5SrGnw6$gT|%{;%Ii*v4;hKpVlwEJ z{2U>*{`t9|L$GcE8*?C8aTEJ;G=x`7P{41yjOR6$zL*gk_| zMki*^=?-djH;e!kps8&+dyvO-Feu5USkG^#dF8PEelEc}n0U$hZA& zvn^?Olql2=^h|q#@WL|qDIZnkM&OCUOxUo~hp0>!9eEDt1LwIh1+YVG(Fu62R(aYz_We`=Gc3?cf$o*Nv|iJ0YK0kH$}I13t5!E}uAy z>8BA{oj1WWlQonUlx+pqM9<&M6N~A5|2Y=yq+q~9zqO!;J5MwI{xfNUq3m>5GhK&d z)qVea6srpkcUYMJ#jw+G>^)m=UGF{2~1<6VQm_sOXMAYv0d2$2lH z9iiV6&@{y3AjwH?+^3G9Ie2sb7URiR$hUEwhGGMX^b$<=dhnYxgrTE+< zs1$E8Z7PoEjX{;T-0fES9p~51I2*EgY(ee;Dw>_SWw&9cyPF%% zJeOw^7^F}pZ@OzZ_$?8M)}zKtL`9);O?*_*;8%Rb)TQLq1){t8jrN7B=*ICtXa9T0 zJJw~Y1@>i)E1CClvQq&g6m-MV&0+}Dz)X_hyY4iDC-`OZHr#x*sOV88s`(TTh+-v%8R=q$UF|sz+zI;$l!YO^b2u3MOqjzbL`~WP)+)f zSUhPx-Xg&owuhleEzHGmM=C7hOBEue4Wwi|I9_|_yuLkwU8e5N#R(g14DAaGCk%B0 ziJV7_OcwB#shorfm~f4gv@iU?ZX@zNvs`rpeDW8kAuR0lp)6v-Y&c|kLURITl!wHr zZt0G+4v6QeQeyK=1PO-L>YjKO1*yr5P3a8>Ig3m<;hi>8;p7*{&=r2q$P>HIfm9;X z*w19w-!f4^S`FhSkX8eCYe3U#iFsAQYX41c2?hp8tI6F2(rOfa&d?;2*K$FTBamde z02apmkye8ynNIqzA@(c@>^#3~7&H4FViy%Y zz5wRqEiED#`sBo_w#JR8p*<&?dLEqF9O<+SaTaKvphQ$^!i9}=R>SjN@t^dJJZEZDF^;jq; zWJl45A1yzL<9>(LJKjcfJ-bKjY7dd85a(2LpMpQF@74~suYoXa#^~qHDh^Q|f!_G& z!M#ktcdv!+Yr@)`ndssKy)7*sW;}eL__0o793~{7W=To6*43r6Zf0ET z)I&vb^bVt($&qAL@4Le^?xi;aCC@2~=pgB>apFExg{wi;ja>Way=9ou!^!S-TogB? zJwSE18$q&eoP0}fdVs<~vPcb!`{x_vX_=PMY3_A>1Z$%3X-l7UpHw$S9Q!b?!KVSG z*i+*vF)}B78*i=^o-%fT;GykzlxdzkHIM?AB7{Z@EF2!t?EW*!^X%luo6>wvu3oeT zCOstqubnVm%|DQyKKl8C1nhqLL+W`(0&Y6}F|D(8Dt=W2DpflBA@w{H1mgGV^=jbe zGdVTrOWdx%T@3mLwO=12pnliir}nd+KTTX;Wex=XwT~Xy`W)IG{kT-2pCg@ z{LY^?t}i4fy=DOh#!&}2feegwf%41HtqT%3RClNx`|h8Xz-EB5_!U&i!uhB2`Wo@y zFQe@R?z*7;b%|5J3W4vX74Dx_e%_dW)`tUK;pH60KNaTkkJN)DxDCZqV)NHBz~Axo z4~Bt(Y~jG-(fmZ_`ZB=Z%L;)bdtgbZpay@f2!w$l`sa$!AI=dFG+5Z%|CqZz$B6%1 zNsUD4WF+N3C9lsg0>r!a$xneq05&h#14+b%C4qa91F44x`jCs8u%uUFJvcyIa3uz& z(r!i}!J=N%>|ANWHg7_zQGU~K9mrI3(Ia+!UB7X1qb z*_VnxF<8Bn{*6D(&6n5w9fOxpAuZ$r^(WOX5y-hL3i%6zRdVK(ndM$on zV5D1MU~KK2Ul`f4I$6B<*@pZ)@A?@0cZ$9K4jrujqS*C;_wN*=7=w<`e^BfaufcI} zen0)MIJ|_FXaW%ai+9?W(!W5Pa8>*ddQDzR{{}7PBw(HlOm#+hEe`deLQ-J-h4UM` zk}k!6!*=pY^Elmi~u~c!TdjfW>w(; delta 27955 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 Cx?IEn diff --git a/lib/pud/nmealib/Makefile b/lib/pud/nmealib/Makefile index 0288a6e4..2cbe920b 100644 --- a/lib/pud/nmealib/Makefile +++ b/lib/pud/nmealib/Makefile @@ -47,7 +47,7 @@ lib/$(LIBNAME): $(OBJ) ifeq ($(VERBOSE),0) @echo "[LD] $@" endif - $(MAKECMDPREFIX)$(CC) -shared -Wl,-soname=$(LIBNAME) -o "$@" $(LIBRARIES) $(OBJ) + $(MAKECMDPREFIX)$(CC) $(LDFLAGS) -Wl,-soname=$(LIBNAME) -o "$@" $(LIBRARIES) $(OBJ) build/%.o: src/%.c Makefile Makefile.inc ifeq ($(VERBOSE),0) diff --git a/lib/pud/nmealib/Makefile.inc b/lib/pud/nmealib/Makefile.inc index cb169392..28d90cf0 100644 --- a/lib/pud/nmealib/Makefile.inc +++ b/lib/pud/nmealib/Makefile.inc @@ -31,7 +31,7 @@ endif # we expect the version to be like 'v0.5.3-27-g0c2727a' and then strip the 'v', # and the '-27-g0c2727a' parts -VERSION=1.0.3 +VERSION=1.0.4 # protect against no version number ifeq ($(strip $(VERSION)),) @@ -52,3 +52,16 @@ CFLAGS+= -O2 else CFLAGS+= -O0 endif + + +LDFLAGS = -shared -Wl,--warn-common -fPIC + +# 32/64 cross compilation +ifdef M32 +CFLAGS += -m32 +LDFLAGS += -m32 +endif +ifdef M64 +CFLAGS += -m64 +LDFLAGS += -m64 +endif diff --git a/lib/pud/nmealib/src/parse.c b/lib/pud/nmealib/src/parse.c index 4153df04..ff3065a0 100644 --- a/lib/pud/nmealib/src/parse.c +++ b/lib/pud/nmealib/src/parse.c @@ -76,6 +76,35 @@ static bool _nmea_parse_time(const char *s, const int len, nmeaTIME *t) { return false; } +/** + * Parse nmeaTIME (date only, no time) from a string. + * The month is adjusted -1 to comply with the nmeaTIME month range of [0, 11]. + * The year is adjusted +100 for years before 90 to comply with the nmeaTIME + * year range of [90, 189]. + * + * @param date the date + * @param t a pointer to the nmeaTIME structure in which to store the parsed date + * @return true on success, false otherwise + */ +static bool _nmea_parse_date(const int date, nmeaTIME *t) { + assert(t); + + if ((date < 0) || (date > 999999)) { + nmea_error("Parse error: invalid time format in %d", date); + return false; + } + + t->day = date / 10000; + t->mon = (date / 100) % 100; + t->mon--; + t->year = date % 100; + if (t->year < 90) { + t->year += 100; + } + + return true; +} + /** * Validate the time fields in an nmeaTIME structure. * Expects: @@ -618,6 +647,7 @@ int nmea_parse_GPGSV(const char *s, const int len, nmeaGPGSV *pack) { int nmea_parse_GPRMC(const char *s, const int len, nmeaGPRMC *pack) { int token_count; char time_buff[NMEA_TIMEPARSE_BUF]; + int date; size_t time_buff_len = 0; assert(s); @@ -629,6 +659,7 @@ int nmea_parse_GPRMC(const char *s, const int len, nmeaGPRMC *pack) { * Clear before parsing, to be able to detect absent fields */ time_buff[0] = '\0'; + date = -1; pack->present = 0; pack->utc.year = -1; pack->utc.mon = -1; @@ -649,31 +680,18 @@ int nmea_parse_GPRMC(const char *s, const int len, nmeaGPRMC *pack) { pack->mode = 0; /* parse */ - token_count = nmea_scanf(s, len, "$GPRMC,%s,%c,%f,%c,%f,%c,%f,%f,%2d%2d%2d,%f,%c,%c*", &time_buff[0], &pack->status, - &pack->lat, &pack->ns, &pack->lon, &pack->ew, &pack->speed, &pack->track, &pack->utc.day, &pack->utc.mon, - &pack->utc.year, &pack->magvar, &pack->magvar_ew, &pack->mode); + token_count = nmea_scanf(s, len, "$GPRMC,%s,%c,%f,%c,%f,%c,%f,%f,%d,%f,%c,%c*", &time_buff[0], &pack->status, + &pack->lat, &pack->ns, &pack->lon, &pack->ew, &pack->speed, &pack->track, &date, + &pack->magvar, &pack->magvar_ew, &pack->mode); /* see that we have enough tokens */ - if ((token_count != 13) && (token_count != 14)) { - nmea_error("GPRMC parse error: need 13 or 14 tokens, got %d in %s", token_count, s); + if ((token_count != 11) && (token_count != 12)) { + nmea_error("GPRMC parse error: need 11 or 12 tokens, got %d in %s", token_count, s); return 0; } /* determine which fields are present and validate them */ - if ((pack->utc.year != -1) && (pack->utc.mon != -1) && (pack->utc.day != -1)) { - if (pack->utc.year < 90) { - pack->utc.year += 100; - } - pack->utc.mon -= 1; - - if (!validateDate(&pack->utc)) { - return 0; - } - - nmea_INFO_set_present(&pack->present, UTCDATE); - } - time_buff_len = strlen(&time_buff[0]); if (time_buff_len) { if (!_nmea_parse_time(&time_buff[0], time_buff_len, &pack->utc)) { @@ -716,6 +734,19 @@ int nmea_parse_GPRMC(const char *s, const int len, nmeaGPRMC *pack) { if (!isnan(pack->track)) { nmea_INFO_set_present(&pack->present, TRACK); } + + if (date != -1) { + if (!_nmea_parse_date(date, &pack->utc)) { + return 0; + } + + if (!validateDate(&pack->utc)) { + return 0; + } + + nmea_INFO_set_present(&pack->present, UTCDATE); + } + if (!isnan(pack->magvar) && (pack->magvar_ew)) { if (!validateNSEW(&pack->magvar_ew, false)) { return 0; @@ -723,7 +754,7 @@ int nmea_parse_GPRMC(const char *s, const int len, nmeaGPRMC *pack) { nmea_INFO_set_present(&pack->present, MAGVAR); } - if (token_count == 13) { + if (token_count == 11) { pack->mode = 'A'; } else { if (!pack->mode) { diff --git a/lib/pud/nmealib/src/tok.c b/lib/pud/nmealib/src/tok.c index bfc6867f..ade2acef 100644 --- a/lib/pud/nmealib/src/tok.c +++ b/lib/pud/nmealib/src/tok.c @@ -35,20 +35,25 @@ #define NMEA_CONVSTR_BUF 64 /** - * Calculate crc control sum of a string + * Calculate crc control sum of a string. + * If the string starts with a '$' then that character is skipped as per + * the NMEA spec. * * @param s the string * @param len the length of the string * @return the crc */ int nmea_calc_crc(const char *s, const int len) { - int chksum = 0; - int it; + int chksum = 0; + int it = 0; - for (it = 0; it < len; it++) - chksum ^= (int) s[it]; + if (s[it] == '$') + it++; - return chksum; + for (; it < len; it++) + chksum ^= (int) s[it]; + + return chksum; } /** diff --git a/lib/pud/src/configTools.c b/lib/pud/src/configTools.c index 036623de..cb53b4e6 100644 --- a/lib/pud/src/configTools.c +++ b/lib/pud/src/configTools.c @@ -263,6 +263,7 @@ bool readULL(const char * parameterName, const char * str, unsigned long long * union olsr_sockaddr * dst, bool * dstSet) { union olsr_sockaddr ip; int conversion; + in_port_t port; assert(parameterName != NULL); assert(str != NULL); @@ -288,10 +289,14 @@ bool readULL(const char * parameterName, const char * str, unsigned long long * } if (!*dstSet) { - setOlsrSockaddrPort(&ip, htons(portDefault)); + port = htons(portDefault); + } else { + port = getOlsrSockaddrPort(dst, portDefault); } - *dst = ip; + dst->in.sa_family = ip.in.sa_family; + setOlsrSockaddrPort(dst, port); + setOlsrSockaddrAddr(dst, &ip); *dstSet = true; return true; } diff --git a/lib/pud/src/configuration.c b/lib/pud/src/configuration.c index 08c3e99b..e73d80a1 100644 --- a/lib/pud/src/configuration.c +++ b/lib/pud/src/configuration.c @@ -1421,11 +1421,6 @@ int setUseLoopback(const char *value, void *data __attribute__ ((unused)), unsigned int checkConfig(void) { int retval = true; - if (!olsr_cnf->smart_gw_active) { - pudError(false, "Smart Gateway must be active"); - retval = false; - } - if (rxNonOlsrInterfaceCount == 0) { pudError(false, "No receive non-OLSR interfaces configured"); retval = false; diff --git a/lib/pud/src/netTools.h b/lib/pud/src/netTools.h index 84a61680..473d2673 100644 --- a/lib/pud/src/netTools.h +++ b/lib/pud/src/netTools.h @@ -11,9 +11,10 @@ #include #include #include +#include -#ifdef __ANDROID_API__ +#ifdef __ANDROID__ typedef __in_port_t in_port_t; #endif /* __ANDROID_API__ */ @@ -59,6 +60,26 @@ static inline void setOlsrSockaddrPort(union olsr_sockaddr * addr, in_port_t por } } +/** + Set the IP address in an OLSR socket address + + @param addr + A pointer to OLSR socket address + @param addr + A pointer to the IP address (in network byte order) + */ +static inline void setOlsrSockaddrAddr(union olsr_sockaddr * addr, union olsr_sockaddr * ip) { + if (!addr) { + return; + } + + if (addr->in.sa_family == AF_INET) { + addr->in4.sin_addr = ip->in4.sin_addr; + } else { + addr->in6.sin6_addr = ip->in6.sin6_addr; + } +} + /** Determine whether an IP address (v4 or v6) is a multicast address. diff --git a/lib/pud/src/pud.c b/lib/pud/src/pud.c index 13bac7e8..66ea20ec 100644 --- a/lib/pud/src/pud.c +++ b/lib/pud/src/pud.c @@ -228,7 +228,8 @@ static void packetReceivedFromDownlink(int skfd, void *data __attribute__ ((unus /* we now have a position update (olsrMessage) of a certain length * (olsrMessageLength). this needs to be transmitted over OLSR and on the LAN */ - /* send out over OLSR interfaces */ + /* send out over OLSR interfaces (only when the smart gateway system is enabled) */ + if (olsr_cnf->smart_gw_active) { int r; struct interface *ifn; diff --git a/lib/pud/src/uplinkGateway.c b/lib/pud/src/uplinkGateway.c index a00ecd15..ecb2333e 100644 --- a/lib/pud/src/uplinkGateway.c +++ b/lib/pud/src/uplinkGateway.c @@ -28,10 +28,6 @@ static inline unsigned long long gw_speed(struct gateway_entry *gw) { * A gateway is better when the sum of its uplink and downlink are greater than * the previous best gateway. In case of a tie, the lowest IP address wins. * - * This code is copied from lib/txtinfo/src/olsrd_txtinfo.c, function ipc_print_gateway. - * It adjusted for best gateway selection but otherwise kept the same as much - * as possible. - * * @param bestGateway * a pointer to the variable in which to store the best gateway */ @@ -45,17 +41,9 @@ void getBestUplinkGateway(union olsr_ip_addr * bestGateway) { bool eval6 = false; struct tc_entry * tc = olsr_lookup_tc_entry(&gw->originator); - if (!tc) { + if (!tc || (tc->path_cost == ROUTE_COST_BROKEN) || (!gw->uplink || !gw->downlink)) { /* gateways should not exist without tc entry */ - continue; - } - - if (tc->path_cost == ROUTE_COST_BROKEN) { /* do not consider nodes with an infinite ETX */ - continue; - } - - if (!gw->uplink || !gw->downlink) { /* do not consider nodes without bandwidth or with a uni-directional link */ continue; } diff --git a/lib/pud/wireformat-java/Makefile b/lib/pud/wireformat-java/Makefile index c76b26fa..33139c08 100644 --- a/lib/pud/wireformat-java/Makefile +++ b/lib/pud/wireformat-java/Makefile @@ -9,6 +9,18 @@ OLSRD_PLUGIN = false TOPDIR = ../../.. include $(TOPDIR)/Makefile.inc +LDFLAGS = -shared -Wl,--warn-common -fPIC + +# 32/64 cross compilation +ifdef M32 +CFLAGS += -m32 +LDFLAGS += -m32 +endif +ifdef M64 +CFLAGS += -m64 +LDFLAGS += -m64 +endif + ###################### # # Settings @@ -31,8 +43,10 @@ SHAREJAVADIR ?= $(SHAREDIR)/java/$(EXENAME) MACHINE=$(shell uname -m) ARCH= +ARCH2=-i386 ifeq ($(strip $(MACHINE)),x86_64) ARCH=.$(MACHINE) + ARCH2=-amd64 endif @@ -46,11 +60,11 @@ INC_DIR_WIREFORMAT_FULL = $(INC_DIR_WIREFORMAT)/$(WIREFORMAT_PROJECT) ifneq ($(wildcard /usr/lib/jvm/java),) INC_DIR_JVM = /usr/lib/jvm/java/include else - ifneq ($(wildcard /usr/lib/jvm/default-java),) - INC_DIR_JVM = /usr/lib/jvm/default-java/include + ifneq ($(wildcard /usr/lib/jvm/java-1.7.0-openjdk$(ARCH)),) + INC_DIR_JVM = /usr/lib/jvm/java-1.7.0-openjdk$(ARCH)/include else - ifneq ($(wildcard /usr/lib/jvm/java-1.7.0-openjdk$(ARCH)),) - INC_DIR_JVM = /usr/lib/jvm/java-1.7.0-openjdk$(ARCH)/include + ifneq ($(wildcard /usr/lib/jvm/java-1.7.0-openjdk$(ARCH2)),) + INC_DIR_JVM = /usr/lib/jvm/java-1.7.0-openjdk$(ARCH2)/include else ifneq ($(wildcard /usr/lib/jvm/java-1.7.0-openjdk),) INC_DIR_JVM = /usr/lib/jvm/java-1.7.0-openjdk/include @@ -58,10 +72,14 @@ else ifneq ($(wildcard /usr/lib/jvm/java-1.6.0-openjdk$(ARCH)),) INC_DIR_JVM = /usr/lib/jvm/java-1.6.0-openjdk$(ARCH)/include else - ifneq ($(wildcard /usr/lib/jvm/java-1.6.0-openjdk),) - INC_DIR_JVM = /usr/lib/jvm/java-1.6.0-openjdk/include + ifneq ($(wildcard /usr/lib/jvm/java-1.6.0-openjdk$(ARCH2)),) + INC_DIR_JVM = /usr/lib/jvm/java-1.6.0-openjdk$(ARCH2)/include else - $(error No java detected) + ifneq ($(wildcard /usr/lib/jvm/java-1.6.0-openjdk),) + INC_DIR_JVM = /usr/lib/jvm/java-1.6.0-openjdk/include + else + $(error No java detected) + endif endif endif endif @@ -131,7 +149,7 @@ $(LIBDIR_BUILD)/$(SONAME): $(BUILD_DIR)/$(JAVA_PKG_UNDER)_UplinkMessage.o \ ifeq ($(VERBOSE),0) @echo "[LD] $@" endif - $(MAKECMDPREFIX)$(CC) -shared -Wl,-soname=$(SONAME) $(LIBRARIES) -o "$@" \ + $(MAKECMDPREFIX)$(CC) $(LDFLAGS) -Wl,-soname=$(SONAME) $(LIBRARIES) -o "$@" \ $(BUILD_DIR)/$(JAVA_PKG_UNDER)_UplinkMessage.o \ $(BUILD_DIR)/$(JAVA_PKG_UNDER)_ClusterLeader.o \ $(BUILD_DIR)/$(JAVA_PKG_UNDER)_PositionUpdate.o diff --git a/lib/pud/wireformat/Makefile b/lib/pud/wireformat/Makefile index c7c77522..0fc25e2f 100644 --- a/lib/pud/wireformat/Makefile +++ b/lib/pud/wireformat/Makefile @@ -6,6 +6,18 @@ OLSRD_PLUGIN = false TOPDIR = ../../.. include $(TOPDIR)/Makefile.inc +LDFLAGS = -shared -Wl,--warn-common -fPIC + +# 32/64 cross compilation +ifdef M32 +CFLAGS += -m32 +LDFLAGS += -m32 +endif +ifdef M64 +CFLAGS += -m64 +LDFLAGS += -m64 +endif + ###################### # # Settings @@ -48,7 +60,7 @@ $(LIBDIR_BUILD)/$(SONAME): $(OBJECTS_C) ifeq ($(VERBOSE),0) @echo "[LD] $@" endif - $(MAKECMDPREFIX)$(CC) -shared -Wl,-soname=$(SONAME) $(LIBRARIES) -o "$@" $(OBJECTS_C) + $(MAKECMDPREFIX)$(CC) $(LDFLAGS) -Wl,-soname=$(SONAME) $(LIBRARIES) -o "$@" $(OBJECTS_C) $(BUILD_DIR)/%.o: $(SRC_DIR_C)/%.c $(INC_DIR_C)/compiler.h ifeq ($(VERBOSE),0) diff --git a/lib/quagga/README_QUAGGA b/lib/quagga/README_QUAGGA index 32bbfc82..85fa0507 100644 --- a/lib/quagga/README_QUAGGA +++ b/lib/quagga/README_QUAGGA @@ -26,8 +26,8 @@ PlParam "Redistribute" "" isis, bgp, hsls May be used more then once -PlParam "ExportRoutes" "" - exports olsr-routes to quagga or to both, quagga and kernel +PlParam "ExportRoutes" "" + exports olsr-routes to quagga only, or to quagga and kernel no routes are exported to quagga (normal behaviour) if not set. PlParam "LocalPref" "" diff --git a/lib/quagga/src/common.h b/lib/quagga/src/common.h index b6a1a769..7696410a 100644 --- a/lib/quagga/src/common.h +++ b/lib/quagga/src/common.h @@ -18,11 +18,14 @@ * ------------------------------------------------------------------------- */ #define OPTION_EXPORT 1 +#define OPTION_ROUTE_ADDITIONAL 2 /* Zebra route types */ #define ZEBRA_ROUTE_OLSR 11 #define ZEBRA_ROUTE_MAX 14 +#include "process_routes.h" + struct zebra { unsigned char status; unsigned char options; @@ -33,6 +36,10 @@ struct zebra { char *sockpath; unsigned int port; unsigned char version; + export_route_function orig_addroute_function; + export_route_function orig_addroute6_function; + export_route_function orig_delroute_function; + export_route_function orig_delroute6_function; }; extern struct zebra zebra; diff --git a/lib/quagga/src/plugin.c b/lib/quagga/src/plugin.c index c7784c99..1c72c76f 100644 --- a/lib/quagga/src/plugin.c +++ b/lib/quagga/src/plugin.c @@ -65,7 +65,6 @@ zplugin_redistribute(const char *value, void *data __attribute__ ((unused)), set int zplugin_exportroutes(const char *value, void *data __attribute__ ((unused)), set_plugin_parameter_addon addon __attribute__ ((unused))) { - if (!strcmp(value, "only")) { olsr_addroute_function = zebra_addroute; olsr_delroute_function = zebra_delroute; @@ -73,11 +72,15 @@ zplugin_exportroutes(const char *value, void *data __attribute__ ((unused)), set olsr_delroute6_function = zebra_delroute; zebra.options |= OPTION_EXPORT; } else if (!strcmp(value, "additional")) { + zebra.orig_addroute_function = olsr_addroute_function; + zebra.orig_delroute_function = olsr_delroute_function; + zebra.orig_addroute6_function = olsr_addroute6_function; + zebra.orig_delroute6_function = olsr_delroute6_function; olsr_addroute_function = zebra_addroute; olsr_delroute_function = zebra_delroute; olsr_addroute6_function = zebra_addroute; olsr_delroute6_function = zebra_delroute; - zebra.options |= OPTION_EXPORT; + zebra.options |= OPTION_EXPORT | OPTION_ROUTE_ADDITIONAL; } return 0; diff --git a/lib/quagga/src/quagga.c b/lib/quagga/src/quagga.c index 62fdbe95..43d3d1d6 100644 --- a/lib/quagga/src/quagga.c +++ b/lib/quagga/src/quagga.c @@ -99,6 +99,8 @@ zebra_addroute(const struct rt_entry *r) } retval = zclient_write(zpacket_route(olsr_cnf->ip_version == AF_INET ? ZEBRA_IPV4_ROUTE_ADD : ZEBRA_IPV6_ROUTE_ADD, &route)); + if(!retval && zebra.options & OPTION_ROUTE_ADDITIONAL) + retval = olsr_cnf->ip_version == AF_INET ? zebra.orig_addroute_function(r) : zebra.orig_addroute6_function(r); free(route.ifindex); free(route.nexthop); @@ -152,6 +154,8 @@ zebra_delroute(const struct rt_entry *r) } retval = zclient_write(zpacket_route(olsr_cnf->ip_version == AF_INET ? ZEBRA_IPV4_ROUTE_DELETE : ZEBRA_IPV6_ROUTE_DELETE, &route)); + if(!retval && zebra.options & OPTION_ROUTE_ADDITIONAL) + retval = olsr_cnf->ip_version == AF_INET ? zebra.orig_delroute_function(r) : zebra.orig_delroute6_function(r); free(route.ifindex); free(route.nexthop); diff --git a/lib/txtinfo/README_TXTINFO b/lib/txtinfo/README_TXTINFO index f0c71bb1..c4e6b0c2 100644 --- a/lib/txtinfo/README_TXTINFO +++ b/lib/txtinfo/README_TXTINFO @@ -32,6 +32,7 @@ So what commands does the txtinfo plugin accept? * Routes: "/route" -> send_what=SIW_ROUTE * Topology: "/topo" -> send_what=SIW_TOPO * 2-hop neighbors: "/2hop" -> send_what=SIW_2HOP + * Version: "/ver" -> send_what=version of olsrd This is the same as the "/neigh" and "/link" commands combined: diff --git a/lib/txtinfo/src/olsrd_txtinfo.c b/lib/txtinfo/src/olsrd_txtinfo.c index d1ac379c..b56545de 100644 --- a/lib/txtinfo/src/olsrd_txtinfo.c +++ b/lib/txtinfo/src/olsrd_txtinfo.c @@ -68,6 +68,7 @@ #include "ipcalc.h" #include "olsr.h" +#include "builddata.h" #include "olsr_types.h" #include "neighbor_table.h" #include "two_hop_neighbor_table.h" diff --git a/make/Makefile.win32 b/make/Makefile.win32 index d7ad5ddb..217446ab 100644 --- a/make/Makefile.win32 +++ b/make/Makefile.win32 @@ -14,8 +14,8 @@ CPPFLAGS += -I$(TOPDIR)/src/win32 PLUGIN_SONAME ?= $(PLUGIN_NAME) PLUGIN_FULLNAME ?= $(PLUGIN_NAME).dll -INSTALL_LIB = cp $(PLUGIN_FULLNAME) ../.. -UNINSTALL_LIB = del /F ../../$(PLUGIN_FULLNAME) +INSTALL_LIB = mkdir -p $(LIBDIR);cp $(PLUGIN_FULLNAME) $(LIBDIR)/$(PLUGIN_FULLNAME) +UNINSTALL_LIB = del /F $(LIBDIR)/$(PLUGIN_FULLNAME) ifdef OLSRD_PLUGIN LDFLAGS += -L$(TOPDIR) diff --git a/make/hash_source.sh b/make/hash_source.sh index b60781e0..9b41fe65 100755 --- a/make/hash_source.sh +++ b/make/hash_source.sh @@ -1,7 +1,15 @@ #!/bin/sh + +sha="`git log -1 --pretty=%h 2> /dev/null`" +if [ -z "$sha" ]; then + sha="0000000" +fi +echo -n "-git_$sha" + md5cmd=md5sum os=$(uname) if [ "xDarwin" = "x$os" ] ; then md5cmd=md5 fi -cat `find . -name *.[ch] | grep -E '[/\\]?builddata.c$'`| $md5cmd +echo -n "-hash_" +cat `find . -name *.[ch] | grep -v -E '[/\\]?builddata.c$'`| $md5cmd | awk '{ print $1; }' diff --git a/src/bsd/dummy.c b/src/bsd/dummy.c index 114d19b3..d1aca7ea 100644 --- a/src/bsd/dummy.c +++ b/src/bsd/dummy.c @@ -23,7 +23,7 @@ void olsr_os_cleanup_iptunnel(const char * name __attribute__((unused))) { } struct olsr_iptunnel_entry *olsr_os_add_ipip_tunnel(union olsr_ip_addr *target __attribute__ ((unused)), - bool transportV4 __attribute__ ((unused))) { + bool transportV4 __attribute__ ((unused)), char *name) { return NULL; } @@ -49,7 +49,8 @@ void olsr_os_niit_6to4_route(const struct olsr_ip_prefix *dst_v6 __attribute__ ( } void olsr_os_inetgw_tunnel_route(uint32_t if_idx __attribute__ ((unused)), bool ipv4 __attribute__ ((unused)), - bool set __attribute__ ((unused))) { + bool set __attribute__ ((unused)), + uint8_t table __attribute__ ((unused))) { } int olsr_os_policy_rule(int family __attribute__ ((unused)), diff --git a/src/builddata.h b/src/builddata.h new file mode 100644 index 00000000..3ae62d68 --- /dev/null +++ b/src/builddata.h @@ -0,0 +1,48 @@ +/* + * The olsr.org Optimized Link-State Routing daemon(olsrd) + * Copyright (c) 2004, Andreas Tonnesen(andreto@olsr.org) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of olsr.org, olsrd nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Visit http://www.olsr.org for more information. + * + * If you find this software useful feel free to make a donation + * to the project. For more information see the website or contact + * the copyright holders. + * + */ + +#ifndef _OLSR_BUILDDATA_H +#define _OLSR_BUILDDATA_H + +extern const char olsrd_version[]; +extern const char build_date[]; +extern const char build_host[]; + +#endif /* _OLSR_BUILDDATA_H */ diff --git a/src/cfgparser/cfgfile_gen.c b/src/cfgparser/cfgfile_gen.c index 768bf8f2..b308bb26 100644 --- a/src/cfgparser/cfgfile_gen.c +++ b/src/cfgparser/cfgfile_gen.c @@ -39,6 +39,7 @@ */ #include "olsrd_conf.h" +#include "builddata.h" #include "../ipcalc.h" #include "../net_olsr.h" #include "../common/autobuf.h" @@ -436,6 +437,88 @@ void olsrd_write_cnf_autobuf(struct autobuf *out, struct olsrd_config *cnf) { abuf_appendf(out, "%sSmartGateway %s\n", cnf->smart_gw_active == DEF_SMART_GW ? "# " : "", cnf->smart_gw_active ? "yes" : "no"); + abuf_puts(out, + "\n" + "# Signals that the server tunnel must always be removed on shutdown,\n" + "# irrespective of the interface up/down state during startup.\n" + "# (default is \"no\")\n" + "\n"); + abuf_appendf(out, "%sSmartGatewayAlwaysRemoveServerTunnel %s\n", + cnf->smart_gw_always_remove_server_tunnel == DEF_SMART_GW_ALWAYS_REMOVE_SERVER_TUNNEL ? "# " : "", + cnf->smart_gw_always_remove_server_tunnel ? "yes" : "no"); + abuf_puts(out, + "\n" + "# Determines the maximum number of gateways that can be in use at any given\n" + "# time. This setting is used to mitigate the effects of breaking connections\n" + "# (due to the selection of a new gateway) on a dynamic network.\n" + "# (default is 1)\n" + "\n"); + abuf_appendf(out, "%sSmartGatewayUseCount %d\n", + cnf->smart_gw_use_count == DEF_GW_USE_COUNT ? "# " : "", + cnf->smart_gw_use_count); + abuf_puts(out, + "\n" + "# Determines the take-down percentage for a non-current smart gateway tunnel.\n" + "# If the cost of the current smart gateway tunnel is less than this percentage\n" + "# of the cost of the non-current smart gateway tunnel, then the non-current smart\n" + "# gateway tunnel is taken down because it is then presumed to be 'too expensive'.\n" + "# This setting is only relevant when SmartGatewayUseCount is larger than 1;\n" + "# a value of 0 will result in the tunnels not being taken down proactively.\n" + "# (default is 0)\n" + "\n"); + abuf_appendf(out, "%sSmartGatewayTakeDownPercentage %d\n", + cnf->smart_gw_takedown_percentage == DEF_GW_TAKEDOWN_PERCENTAGE ? "# " : "", + cnf->smart_gw_takedown_percentage); + abuf_puts(out, + "\n" + "# Determines the policy routing script that is executed during startup and\n" + "# shutdown of olsrd. The script is only executed when SmartGatewayUseCount\n" + "# is set to a value larger than 1. The script must setup policy routing\n" + "# rules such that multi-gateway mode works. A sample script is included.\n" + "# (default is not set)\n" + "\n"); + abuf_appendf(out, "%sSmartGatewayPolicyRoutingScript %s\n", + !cnf->smart_gw_policyrouting_script ? "# " : "", + !cnf->smart_gw_policyrouting_script ? "" : cnf->smart_gw_policyrouting_script); + abuf_puts(out, + "\n" + "# Determines the egress interfaces that are part of the multi-gateway setup and\n" + "# therefore only relevant when SmartGatewayUseCount is larger than 1 (in which\n" + "# case it must be explicitly set).\n" + "# (default is not set)\n" + "\n"); + abuf_appendf(out, "%sSmartGatewayEgressInterfaces", + !cnf->smart_gw_egress_interfaces ? "# " : ""); + { + struct sgw_egress_if * sgwegressif = olsr_cnf->smart_gw_egress_interfaces; + while (sgwegressif) { + abuf_appendf(out, " \"%s\"", sgwegressif->name); + sgwegressif = sgwegressif->next; + } + abuf_puts(out, "\n"); + } + abuf_puts(out, + "\n" + "# Determines the offset of the smart gateway egress interfaces mark that are\n" + "# used in the policy routing rules in a multi-gateway setup. Only relevant\n" + "# when a multi-gateway setup is used.\n" + "# (default is 91)\n" + "\n"); + abuf_appendf(out, "%sSmartGatewayMarkOffsetEgress %u\n", + cnf->smart_gw_mark_offset_egress == DEF_GW_MARK_OFFSET_EGRESS ? "# " : "", + cnf->smart_gw_mark_offset_egress); + abuf_puts(out, + "\n" + "# Determines the offset of the smart gateway tunnel interfaces mark that are\n" + "# used in the policy routing rules in a multi-gateway setup. Only relevant\n" + "# when a multi-gateway setup is used.\n" + "# The ranges [egress offset, egress offset + egress count] and\n" + "# [tunnel offset, tunnel offset + use count] are not allowed to overlap.\n" + "# (default is 101)\n" + "\n"); + abuf_appendf(out, "%sSmartGatewayMarkOffsetTunnels %u\n", + cnf->smart_gw_mark_offset_tunnels == DEF_GW_MARK_OFFSET_TUNNELS ? "# " : "", + cnf->smart_gw_mark_offset_tunnels); abuf_puts(out, "\n" "# Allows the selection of a smartgateway with NAT (only for IPv4)\n" diff --git a/src/cfgparser/olsrd_conf.c b/src/cfgparser/olsrd_conf.c index 7b6605b6..ca856f5a 100644 --- a/src/cfgparser/olsrd_conf.c +++ b/src/cfgparser/olsrd_conf.c @@ -59,6 +59,7 @@ #include #include #include +#include #endif /* __linux__ */ extern FILE *yyin; @@ -554,6 +555,112 @@ olsrd_sanity_check_cnf(struct olsrd_config *cnf) } #ifdef __linux__ + if ((cnf->smart_gw_use_count < MIN_SMARTGW_USE_COUNT_MIN) || (cnf->smart_gw_use_count > MAX_SMARTGW_USE_COUNT_MAX)) { + fprintf(stderr, "Error, bad gateway use count %d, outside of range [%d, %d]\n", + cnf->smart_gw_use_count, MIN_SMARTGW_USE_COUNT_MIN, MAX_SMARTGW_USE_COUNT_MAX); + return -1; + } + + if (cnf->smart_gw_use_count > 1) { + struct sgw_egress_if * sgwegressif = cnf->smart_gw_egress_interfaces; + + /* check that the sgw takedown percentage is in the range [0, 100] */ + if (/*(cnf->smart_gw_takedown_percentage < 0) ||*/ (cnf->smart_gw_takedown_percentage > 100)) { + fprintf(stderr, "Error, smart gateway takedown percentage (%u) is not in the range [0, 100]\n", + cnf->smart_gw_takedown_percentage); + return -1; + } + + if (!cnf->smart_gw_policyrouting_script) { + fprintf(stderr, "Error, no policy routing script configured in multi-gateway mode\n"); + return -1; + } + + { + struct stat statbuf; + + int r = stat(cnf->smart_gw_policyrouting_script, &statbuf); + if (r) { + fprintf(stderr, "Error, policy routing script not found: %s\n", strerror(errno)); + return -1; + } + + if (!S_ISREG(statbuf.st_mode)) { + fprintf(stderr, "Error, policy routing script not a regular file\n"); + return -1; + } + + if (statbuf.st_uid) { + fprintf(stderr, "Error, policy routing script must be owned by root\n"); + return -1; + } + + if (!(statbuf.st_mode & (S_IRUSR | S_IXUSR))) { + fprintf(stderr, "Error, policy routing script is not executable\n"); + return -1; + } + } + + /* egress interface(s) must be set */ + if (!sgwegressif) { + fprintf(stderr, "Error, no egress interfaces configured in multi-gateway mode\n"); + return -1; + } + + /* an egress interface must not be an OLSR interface */ + while (sgwegressif) { + struct olsr_if * olsrif = cnf->interfaces; + while (olsrif) { + if (!strcmp(olsrif->name, sgwegressif->name)) { + fprintf(stderr, "Error, egress interface %s already is an OLSR interface\n", sgwegressif->name); + return -1; + } + olsrif = olsrif->next; + } + cnf->smart_gw_egress_interfaces_count++; + sgwegressif = sgwegressif->next; + } + + if (cnf->smart_gw_egress_interfaces_count > MAX_SMARTGW_EGRESS_INTERFACE_COUNT_MAX) { + fprintf(stderr, "Error, egress interface count %u not in range [1, %u]\n", + cnf->smart_gw_egress_interfaces_count, MAX_SMARTGW_EGRESS_INTERFACE_COUNT_MAX); + return -1; + } + + { + uint8_t egressLow = cnf->smart_gw_mark_offset_egress; + uint8_t egressHigh = egressLow + cnf->smart_gw_egress_interfaces_count - 1; + uint8_t tunnelsLow = cnf->smart_gw_mark_offset_tunnels; + uint8_t tunnelsHigh = tunnelsLow + cnf->smart_gw_use_count - 1; + bool overlap = false; + + /* check that the egress interface marks range does not overflow */ + if (egressLow > (UINT8_MAX - cnf->smart_gw_egress_interfaces_count)) { + fprintf(stderr, "Error, egress interface mark offset %u together with egress interface count %u overflows range [0, %u]\n", + egressLow, cnf->smart_gw_egress_interfaces_count, UINT8_MAX); + return -1; + } + + /* check that the tunnel interface marks range does not overflow */ + if (tunnelsLow > (UINT8_MAX - cnf->smart_gw_use_count)) { + fprintf(stderr, "Error, tunnel interface mark offset %u together with use count %u overflows range [0, %u]\n", + tunnelsLow, cnf->smart_gw_use_count, UINT8_MAX); + return -1; + } + + /* check that the egress and tunnel marks ranges do not overlap */ + overlap = ((tunnelsLow <= egressLow) && (egressLow <= tunnelsHigh)); + overlap = overlap || ((tunnelsLow <= egressHigh) && (egressHigh <= tunnelsHigh)); + overlap = overlap || ((egressLow <= tunnelsLow) && (tunnelsLow <= egressHigh)); + overlap = overlap || ((egressLow <= tunnelsHigh) && (tunnelsHigh <= egressHigh)); + if (overlap) { + fprintf(stderr, "Error, egress interface mark range [%u, %u] overlaps with tunnel interface mark range [%u, %u]\n", + egressLow, egressHigh, tunnelsLow, tunnelsHigh); + return -1; + } + } + } + if (cnf->smart_gw_period < MIN_SMARTGW_PERIOD || cnf->smart_gw_period > MAX_SMARTGW_PERIOD) { fprintf(stderr, "Error, bad gateway period: %d msec (should be %d-%d)\n", cnf->smart_gw_period, MIN_SMARTGW_PERIOD, MAX_SMARTGW_PERIOD); @@ -788,6 +895,14 @@ set_default_cnf(struct olsrd_config *cnf) cnf->niit6to4_if_index = 0; cnf->smart_gw_active = DEF_SMART_GW; + cnf->smart_gw_always_remove_server_tunnel = DEF_SMART_GW_ALWAYS_REMOVE_SERVER_TUNNEL; + cnf->smart_gw_use_count = DEF_GW_USE_COUNT; + cnf->smart_gw_takedown_percentage = DEF_GW_TAKEDOWN_PERCENTAGE; + cnf->smart_gw_policyrouting_script = NULL; + cnf->smart_gw_egress_interfaces = NULL; + cnf->smart_gw_egress_interfaces_count = 0; + cnf->smart_gw_mark_offset_egress = DEF_GW_MARK_OFFSET_EGRESS; + cnf->smart_gw_mark_offset_tunnels = DEF_GW_MARK_OFFSET_TUNNELS; cnf->smart_gw_allow_nat = DEF_GW_ALLOW_NAT; cnf->smart_gw_period = DEF_GW_PERIOD; cnf->smart_gw_stablecount = DEF_GW_STABLE_COUNT; @@ -916,6 +1031,28 @@ olsrd_print_cnf(struct olsrd_config *cnf) printf("Smart Gateway : %s\n", cnf->smart_gw_active ? "yes" : "no"); + printf("SmGw. Del Srv Tun: %s\n", cnf->smart_gw_always_remove_server_tunnel ? "yes" : "no"); + + printf("SmGw. Use Count : %u\n", cnf->smart_gw_use_count); + + printf("SmGw. Takedown%% : %u\n", cnf->smart_gw_takedown_percentage); + + printf("SmGw. Pol. Script: %s\n", cnf->smart_gw_policyrouting_script); + + printf("SmGw. Egress I/Fs:"); + { + struct sgw_egress_if * sgwegressif = cnf->smart_gw_egress_interfaces; + while (sgwegressif) { + printf(" %s", sgwegressif->name); + sgwegressif = sgwegressif->next; + } + } + printf("\n"); + + printf("SmGw. Mark Egress: %u\n", cnf->smart_gw_mark_offset_egress); + + printf("SmGw. Mark Tunnel: %u\n", cnf->smart_gw_mark_offset_tunnels); + printf("SmGw. Allow NAT : %s\n", cnf->smart_gw_allow_nat ? "yes" : "no"); printf("SmGw. period : %d\n", cnf->smart_gw_period); diff --git a/src/cfgparser/oparse.y b/src/cfgparser/oparse.y index c123ae46..9b4b9e4f 100644 --- a/src/cfgparser/oparse.y +++ b/src/cfgparser/oparse.y @@ -56,6 +56,7 @@ #include #include #include +#include #define PARSER_DEBUG 1 @@ -215,6 +216,13 @@ static int add_ipv6_addr(YYSTYPE ipaddr_arg, YYSTYPE prefixlen_arg) %token TOK_LOCK_FILE %token TOK_USE_NIIT %token TOK_SMART_GW +%token TOK_SMART_GW_ALWAYS_REMOVE_SERVER_TUNNEL +%token TOK_SMART_GW_USE_COUNT +%token TOK_SMART_GW_TAKEDOWN_PERCENTAGE +%token TOK_SMART_GW_POLICYROUTING_SCRIPT +%token TOK_SMART_GW_EGRESS_IFS +%token TOK_SMART_GW_MARK_OFFSET_EGRESS +%token TOK_SMART_GW_MARK_OFFSET_TUNNELS %token TOK_SMART_GW_ALLOW_NAT %token TOK_SMART_GW_PERIOD %token TOK_SMART_GW_STABLECOUNT @@ -302,6 +310,12 @@ stmt: idebug | alock_file | suse_niit | bsmart_gw + | bsmart_gw_always_remove_server_tunnel + | ismart_gw_use_count + | ismart_gw_takedown_percentage + | ssmart_gw_policyrouting_script + | ismart_gw_mark_offset_egress + | ismart_gw_mark_offset_tunnels | bsmart_gw_allow_nat | ismart_gw_period | asmart_gw_stablecount @@ -317,6 +331,7 @@ stmt: idebug | bsrc_ip_routes | amain_ip | bset_ipforward + | ssgw_egress_ifs ; block: TOK_HNA4 hna4body @@ -1304,6 +1319,123 @@ bsmart_gw: TOK_SMART_GW TOK_BOOLEAN } ; +bsmart_gw_always_remove_server_tunnel: TOK_SMART_GW_ALWAYS_REMOVE_SERVER_TUNNEL TOK_BOOLEAN +{ + PARSER_DEBUG_PRINTF("Smart gateway always remove server tunnel: %s\n", $2->boolean ? "enabled" : "disabled"); + olsr_cnf->smart_gw_always_remove_server_tunnel = $2->boolean; + free($2); +} +; + +ismart_gw_use_count: TOK_SMART_GW_USE_COUNT TOK_INTEGER +{ + PARSER_DEBUG_PRINTF("Smart gateway use count: %d\n", $2->integer); + olsr_cnf->smart_gw_use_count = $2->integer; + free($2); +} +; + +ismart_gw_takedown_percentage: TOK_SMART_GW_TAKEDOWN_PERCENTAGE TOK_INTEGER +{ + PARSER_DEBUG_PRINTF("Smart gateway takedown percentage: %d\n", $2->integer); + olsr_cnf->smart_gw_takedown_percentage = $2->integer; + free($2); +} +; + +ssmart_gw_policyrouting_script: TOK_SMART_GW_POLICYROUTING_SCRIPT TOK_STRING +{ + PARSER_DEBUG_PRINTF("Smart gateway policy routing script: %s\n", $2->string); + olsr_cnf->smart_gw_policyrouting_script = $2->string; + free($2); +} +; + +ssgw_egress_ifs: TOK_SMART_GW_EGRESS_IFS sgw_egress_ifs +; + +sgw_egress_ifs: | sgw_egress_ifs sgw_egress_if +; + +sgw_egress_if: TOK_STRING +{ + struct sgw_egress_if *in, *last; + char * str = $1->string; + char *end; + + /* Trim leading space */ + while(isspace(*str)) { + str++; + } + + /* Trim trailing space */ + end = str + strlen(str) - 1; + while((end > str) && isspace(*end)) { + end--; + } + + /* Write new null terminator */ + *(end + 1) = '\0'; + + if(*str == 0) { + PARSER_DEBUG_PRINTF("Smart gateway egress interface: (skipped)\n"); + } else { + PARSER_DEBUG_PRINTF("Smart gateway egress interface: %s\n", str); + + in = olsr_cnf->smart_gw_egress_interfaces; + last = NULL; + while (in != NULL) { + if (strcmp(in->name, str) == 0) { + free ($1->string); + break; + } + last = in; + in = in->next; + } + + if (in != NULL) { + /* remove old interface from list to add it later at the beginning */ + if (last) { + last->next = in->next; + } + else { + olsr_cnf->smart_gw_egress_interfaces = in->next; + } + } + else { + in = malloc(sizeof(*in)); + if (in == NULL) { + fprintf(stderr, "Out of memory(ADD IF)\n"); + YYABORT; + } + memset(in, 0, sizeof(*in)); + + in->name = str; + } + /* Queue */ + in->next = olsr_cnf->smart_gw_egress_interfaces; + olsr_cnf->smart_gw_egress_interfaces = in; + free($1); + } +} +; + +ismart_gw_mark_offset_egress: TOK_SMART_GW_MARK_OFFSET_EGRESS TOK_INTEGER +{ + PARSER_DEBUG_PRINTF("Smart gateway mark offset egress interfaces: %d\n", $2->integer); + olsr_cnf->smart_gw_mark_offset_egress = $2->integer; + free($2); +} +; + +ismart_gw_mark_offset_tunnels: TOK_SMART_GW_MARK_OFFSET_TUNNELS TOK_INTEGER +{ + PARSER_DEBUG_PRINTF("Smart gateway mark offset tunnel interfaces: %d\n", $2->integer); + olsr_cnf->smart_gw_mark_offset_tunnels = $2->integer; + free($2); +} +; + bsmart_gw_allow_nat: TOK_SMART_GW_ALLOW_NAT TOK_BOOLEAN { PARSER_DEBUG_PRINTF("Smart gateway allow client nat: %s\n", $2->boolean ? "yes" : "no"); diff --git a/src/cfgparser/oscan.lex b/src/cfgparser/oscan.lex index 194c53a0..6321acd3 100644 --- a/src/cfgparser/oscan.lex +++ b/src/cfgparser/oscan.lex @@ -466,6 +466,41 @@ IPV6ADDR {IPV6PAT1}|{IPV6PAT2}|{IPV6PAT3}|{IPV6PAT4}|{IPV6PAT5}|{IPV6PAT6}|{IPV6 return TOK_SMART_GW; } +"SmartGatewayAlwaysRemoveServerTunnel" { + yylval = NULL; + return TOK_SMART_GW_ALWAYS_REMOVE_SERVER_TUNNEL; +} + +"SmartGatewayUseCount" { + yylval = NULL; + return TOK_SMART_GW_USE_COUNT; +} + +"SmartGatewayTakeDownPercentage" { + yylval = NULL; + return TOK_SMART_GW_TAKEDOWN_PERCENTAGE; +} + +"SmartGatewayPolicyRoutingScript" { + yylval = NULL; + return TOK_SMART_GW_POLICYROUTING_SCRIPT; +} + +"SmartGatewayEgressInterfaces" { + yylval = NULL; + return TOK_SMART_GW_EGRESS_IFS; +} + +"SmartGatewayMarkOffsetEgress" { + yylval = NULL; + return TOK_SMART_GW_MARK_OFFSET_EGRESS; +} + +"SmartGatewayMarkOffsetTunnels" { + yylval = NULL; + return TOK_SMART_GW_MARK_OFFSET_TUNNELS; +} + "SmartGatewayAllowNAT" { yylval = NULL; return TOK_SMART_GW_ALLOW_NAT; diff --git a/src/defs.h b/src/defs.h index 99ff32e5..efde9083 100644 --- a/src/defs.h +++ b/src/defs.h @@ -56,10 +56,6 @@ #include "olsr_protocol.h" #include "olsr_cfg.h" -extern const char olsrd_version[]; -extern const char build_date[]; -extern const char build_host[]; - #ifndef OLSRD_GLOBAL_CONF_FILE #define OLSRD_CONF_FILE_NAME "olsrd.conf" #define OLSRD_GLOBAL_CONF_FILE "/etc/" OLSRD_CONF_FILE_NAME diff --git a/src/gateway.c b/src/gateway.c index 15587a64..95036b4c 100644 --- a/src/gateway.c +++ b/src/gateway.c @@ -20,34 +20,67 @@ #include "duplicate_set.h" #include "log.h" #include "gateway_default_handler.h" +#include "gateway_list.h" #include "gateway.h" #include #include +/* + * Defines for the multi-gateway script + */ + +#define SCRIPT_MODE_GENERIC "generic" +#define SCRIPT_MODE_OLSRIF "olsrif" +#define SCRIPT_MODE_SGWSRVTUN "sgwsrvtun" +#define SCRIPT_MODE_EGRESSIF "egressif" +#define SCRIPT_MODE_SGWTUN "sgwtun" + +/** structure that holds an interface name, mark and a pointer to the gateway that uses it */ +struct interfaceName { + char name[IFNAMSIZ]; /**< interface name */ + uint8_t mark; /**< marking */ + struct gateway_entry *gw; /**< gateway that uses this interface name */ +}; + /** the gateway tree */ struct avl_tree gateway_tree; /** gateway cookie */ static struct olsr_cookie_info *gateway_entry_mem_cookie = NULL; +/** gateway container cookie */ +static struct olsr_cookie_info *gw_container_entry_mem_cookie = NULL; + /** the gateway netmask for the HNA */ static uint8_t smart_gateway_netmask[sizeof(union olsr_ip_addr)]; /** the gateway handler/plugin */ static struct olsr_gw_handler *gw_handler; -/** the current IPv4 gateway */ -static struct gateway_entry *current_ipv4_gw; +/** the IPv4 gateway list */ +static struct gw_list gw_list_ipv4; -/** the tunnel of the current IPv4 gateway */ -static struct olsr_iptunnel_entry *v4gw_tunnel; +/** the IPv6 gateway list */ +static struct gw_list gw_list_ipv6; + +/** the current IPv4 gateway */ +static struct gw_container_entry *current_ipv4_gw; /** the current IPv6 gateway */ -static struct gateway_entry *current_ipv6_gw; +static struct gw_container_entry *current_ipv6_gw; + +/** interface names for smart gateway egress interfaces */ +struct interfaceName * sgwEgressInterfaceNames; + +/** interface names for smart gateway tunnel interfaces, IPv4 */ +struct interfaceName * sgwTunnel4InterfaceNames; + +/** interface names for smart gateway tunnel interfaces, IPv6 */ +struct interfaceName * sgwTunnel6InterfaceNames; -/** the tunnel of the current IPv6 gateway */ -static struct olsr_iptunnel_entry *v6gw_tunnel; +/** the timer for proactive takedown */ +static struct timer_entry *gw_takedown_timer; /* * Forward Declarations @@ -59,9 +92,31 @@ static void olsr_delete_gateway_tree_entry(struct gateway_entry * gw, uint8_t pr * Helper Functions */ -#define TUNNEL_NAME (olsr_cnf->ip_version == AF_INET ? TUNNEL_ENDPOINT_IF : TUNNEL_ENDPOINT_IF6) +/** + * @return the gateway 'server' tunnel name to use + */ +static inline const char * server_tunnel_name(void) { + return (olsr_cnf->ip_version == AF_INET ? TUNNEL_ENDPOINT_IF : TUNNEL_ENDPOINT_IF6); +} + +/** + * Convert the netmask of the HNA (in the form of an IP address) to a HNA + * pointer. + * + * @param mask the netmask of the HNA (in the form of an IP address) + * @param prefixlen the prefix length + * @return a pointer to the HNA + */ +static inline uint8_t * hna_mask_to_hna_pointer(union olsr_ip_addr *mask, int prefixlen) { + return (((uint8_t *)mask) + ((prefixlen+7)/8)); +} -#define OLSR_IP_ADDR_2_HNA_PTR(mask, prefixlen) (((uint8_t *)mask) + ((prefixlen+7)/8)) +/** + * @return true if multi-gateway mode is enabled + */ +static inline bool multi_gateway_mode(void) { + return (olsr_cnf->smart_gw_use_count > 1); +} /** * Convert an encoded 1 byte transport value (5 bits mantissa, 3 bits exponent) @@ -114,6 +169,258 @@ static uint8_t serialize_gw_speed(uint32_t speed) { return ((speed - 1) << 3) | exp; } +/** + * Find an interfaceName struct corresponding to a certain gateway + * (when gw != NULL) or to an empty interfaceName struct (when gw == NULL). + * + * @param gw the gateway to find (when not NULL), or the empty struct to find (when NULL) + * @return a pointer to the struct, or NULL when not found + */ +static struct interfaceName * find_interfaceName(struct gateway_entry *gw) { + struct interfaceName * sgwTunnelInterfaceNames; + uint8_t i = 0; + + if (!multi_gateway_mode()) { + return NULL; + } + + assert(sgwTunnel4InterfaceNames); + assert(sgwTunnel6InterfaceNames); + + sgwTunnelInterfaceNames = (olsr_cnf->ip_version == AF_INET) ? sgwTunnel4InterfaceNames : sgwTunnel6InterfaceNames; + while (i < olsr_cnf->smart_gw_use_count) { + struct interfaceName * ifn = &sgwTunnelInterfaceNames[i]; + if (ifn->gw == gw) { + return ifn; + } + i++; + } + + return NULL; +} + +/** + * Get an unused olsr ipip tunnel name for a certain gateway and store it in name. + * + * @param gw pointer to the gateway + * @param name pointer to output buffer (length IFNAMSIZ) + * @param interfaceName a pointer to the location where to store a pointer to the interfaceName struct + */ +static void get_unused_iptunnel_name(struct gateway_entry *gw, char * name, struct interfaceName ** interfaceName) { + static uint32_t counter = 0; + + assert(gw); + assert(name); + assert(interfaceName); + + memset(name, 0, IFNAMSIZ); + + if (multi_gateway_mode()) { + struct interfaceName * ifn = find_interfaceName(NULL); + + if (ifn) { + strncpy(&name[0], &ifn->name[0], sizeof(ifn->name)); + *interfaceName = ifn; + ifn->gw = gw; + return; + } + + /* do not return, fall-through to classic naming as fallback */ + } + + snprintf(name, IFNAMSIZ, "tnl_%08x", (olsr_cnf->ip_version == AF_INET) ? gw->originator.v4.s_addr : ++counter); + *interfaceName = NULL; +} + +/** + * Set an olsr ipip tunnel name that is used by a certain gateway as unused + * + * @param gw pointer to the gateway + */ +static void set_unused_iptunnel_name(struct gateway_entry *gw) { + struct interfaceName * ifn; + + if (!multi_gateway_mode()) { + return; + } + + assert(gw); + + ifn = find_interfaceName(gw); + if (ifn) { + ifn->gw = NULL; + return; + } +} + +/** + * Run the multi-gateway script/ + * + * @param mode the mode (see SCRIPT_MODE_* defines) + * @param add true to add policy routing, false to remove it + * @param ifname the interface name (optional) + * @param ifmark the interface mark (optional + * @return true when successful + */ +static bool multiGwRunScript(const char * mode, bool add, const char * ifname, uint8_t * ifmark) { + struct autobuf buf; + int r; + + abuf_init(&buf, 1024); + + abuf_appendf(&buf, "\"%s\"", olsr_cnf->smart_gw_policyrouting_script); + + abuf_appendf(&buf, " \"%s\"", (olsr_cnf->ip_version == AF_INET) ? "ipv4" : "ipv6"); + + assert(!strcmp(mode, SCRIPT_MODE_GENERIC) || !strcmp(mode, SCRIPT_MODE_OLSRIF) || + !strcmp(mode, SCRIPT_MODE_SGWSRVTUN) || !strcmp(mode, SCRIPT_MODE_EGRESSIF) || + !strcmp(mode, SCRIPT_MODE_SGWTUN)); + abuf_appendf(&buf, " \"%s\"", mode); + + abuf_appendf(&buf, " \"%s\"", add ? "add" : "del"); + + if (ifname) { + assert(!strcmp(mode, SCRIPT_MODE_OLSRIF) || !strcmp(mode, SCRIPT_MODE_SGWSRVTUN) || + !strcmp(mode, SCRIPT_MODE_EGRESSIF) || !strcmp(mode, SCRIPT_MODE_SGWTUN)); + abuf_appendf(&buf, " \"%s\"", ifname); + } else { + assert(!strcmp(mode, SCRIPT_MODE_GENERIC)); + } + if (ifmark) { + assert(!strcmp(mode, SCRIPT_MODE_EGRESSIF) || !strcmp(mode, SCRIPT_MODE_SGWTUN)); + assert(ifname); + abuf_appendf(&buf, " \"%u\"", *ifmark); + } else { + assert(!strcmp(mode, SCRIPT_MODE_GENERIC) || !strcmp(mode, SCRIPT_MODE_OLSRIF) || + !strcmp(mode, SCRIPT_MODE_SGWSRVTUN)); + } + + r = system(buf.buf); + + abuf_free(&buf); + + return (r == 0); +} + +/** + * Setup generic multi-gateway iptables and ip rules + * + * - generic (on olsrd start/stop) + * iptablesExecutable -t mangle -A OUTPUT -j CONNMARK --restore-mark + * + * @param add true to add policy routing, false to remove it + * @return true when successful + */ +static bool multiGwRulesGeneric(bool add) { + return multiGwRunScript(SCRIPT_MODE_GENERIC, add, NULL, NULL); +} + +/** + * Setup multi-gateway iptables and ip rules for all OLSR interfaces. + * + * - olsr interfaces (on olsrd start/stop) + * iptablesExecutable -t mangle -A PREROUTING -i ${olsrInterface} -j CONNMARK --restore-mark + * + * @param add true to add policy routing, false to remove it + * @return true when successful + */ +static bool multiGwRulesOlsrInterfaces(bool add) { + bool ok = true; + struct interface * ifn; + + for (ifn = ifnet; ifn; ifn = ifn->int_next) { + if (!multiGwRunScript(SCRIPT_MODE_OLSRIF, add, ifn->int_name, NULL)) { + ok = false; + if (add) { + return ok; + } + } + } + + return ok; +} + +/** + * Setup multi-gateway iptables and ip rules for the smart gateway server tunnel. + * + * - sgw server tunnel interface (on olsrd start/stop) + * iptablesExecutable -t mangle -A PREROUTING -i tunl0 -j CONNMARK --restore-mark + * + * @param add true to add policy routing, false to remove it + * @return true when successful + */ +static bool multiGwRulesSgwServerTunnel(bool add) { + return multiGwRunScript(SCRIPT_MODE_SGWSRVTUN, add, server_tunnel_name(), NULL); +} + +/** + * Setup multi-gateway iptables and ip rules for all egress interfaces. + * + * - egress interfaces (on interface start/stop) + * iptablesExecutable -t mangle -A POSTROUTING -m conntrack --ctstate NEW -o ${egressInterface} -j CONNMARK --set-mark ${egressInterfaceMark} + * iptablesExecutable -t mangle -A INPUT -m conntrack --ctstate NEW -i ${egressInterface} -j CONNMARK --set-mark ${egressInterfaceMark} + * ip rule add fwmark ${egressInterfaceMark} table ${egressInterfaceMark} pref ${egressInterfaceMark} + * + * like table: + * ppp0 91 + * eth0 92 + * + * @param add true to add policy routing, false to remove it + * @return true when successful + */ +static bool multiGwRulesEgressInterfaces(bool add) { + bool ok = true; + unsigned int i = 0; + + for (i = 0; i < olsr_cnf->smart_gw_egress_interfaces_count; i++) { + struct interfaceName * ifn = &sgwEgressInterfaceNames[i]; + if (!multiGwRunScript(SCRIPT_MODE_EGRESSIF, add, ifn->name, &ifn->mark)) { + ok = false; + if (add) { + return ok; + } + } + } + + return ok; +} + +/** + * Setup multi-gateway iptables and ip rules for the smart gateway client tunnels. + * + * - sgw tunnels (on sgw tunnel start/stop) + * iptablesExecutable -t mangle -A POSTROUTING -m conntrack --ctstate NEW -o ${sgwTunnelInterface} -j CONNMARK --set-mark ${sgwTunnelInterfaceMark} + * ip rule add fwmark ${sgwTunnelInterfaceMark} table ${sgwTunnelInterfaceMark} pref ${sgwTunnelInterfaceMark} + * + * like table: + * tnl_101 101 + * tnl_102 102 + * tnl_103 103 + * tnl_104 104 + * tnl_105 105 + * tnl_106 106 + * tnl_107 107 + * tnl_108 108 + */ +static bool multiGwRulesSgwTunnels(bool add) { + bool ok = true; + unsigned int i = 0; + + while (i < olsr_cnf->smart_gw_use_count) { + struct interfaceName * ifn = (olsr_cnf->ip_version == AF_INET) ? &sgwTunnel4InterfaceNames[i] : &sgwTunnel6InterfaceNames[i]; + if (!multiGwRunScript(SCRIPT_MODE_SGWTUN, add, ifn->name, &ifn->mark)) { + ok = false; + if (add) { + return ok; + } + } + + i++; + } + + return ok; +} + /* * Callback Functions */ @@ -149,6 +456,88 @@ static void cleanup_gateway_handler(void *ptr) { olsr_cookie_free(gateway_entry_mem_cookie, gw); } +/** + * Remove a gateway from a gateway list. + * + * @param gw_list a pointer to the gateway list + * @param ipv4 true when dealing with an IPv4 gateway / gateway list + * @param gw a pointer to the gateway to remove from the list + */ +static void removeGatewayFromList(struct gw_list * gw_list, bool ipv4, struct gw_container_entry * gw) { + if (gw->tunnel) { + struct interfaceName * ifn = find_interfaceName(gw->gw); + if (ifn) { + olsr_os_inetgw_tunnel_route(gw->tunnel->if_index, ipv4, false, ifn->mark); + } + olsr_os_del_ipip_tunnel(gw->tunnel); + set_unused_iptunnel_name(gw->gw); + gw->tunnel = NULL; + } + gw->gw = NULL; + olsr_cookie_free(gw_container_entry_mem_cookie, olsr_gw_list_remove(gw_list, gw)); +} + +/** + * Remove expensive gateways from the gateway list. + * It uses the smart_gw_takedown_percentage configuration parameter + * + * @param gw_list a pointer to the gateway list + * @param ipv4 true when dealing with an IPv4 gateway / gateway list + * @param current_gw the current gateway + */ +static void takeDownExpensiveGateways(struct gw_list * gw_list, bool ipv4, struct gw_container_entry * current_gw) { + uint64_t current_gw_cost_boundary; + + /* + * exit immediately when takedown is disabled, there is no current gateway, or + * when there is only a single gateway + */ + if ((olsr_cnf->smart_gw_takedown_percentage == 0) || (current_gw == NULL ) || (gw_list->count <= 1)) { + return; + } + + /* get the cost boundary */ + + /* scale down because otherwise the percentage calculation can overflow */ + current_gw_cost_boundary = (current_gw->path_cost >> 2); + + if (olsr_cnf->smart_gw_takedown_percentage < 100) { + current_gw_cost_boundary = (current_gw_cost_boundary * 100) / olsr_cnf->smart_gw_takedown_percentage; + } + + /* loop while we still have gateways */ + while (gw_list->count > 1) { + /* get the worst gateway */ + struct gw_container_entry * worst_gw = olsr_gw_list_get_worst_entry(gw_list); + + /* exit when it's the current gateway */ + if (worst_gw == current_gw) { + return; + } + + /* + * exit when it (and further ones; the list is sorted on costs) has lower + * costs than the boundary costs + */ + if ((worst_gw->path_cost >> 2) < current_gw_cost_boundary) { + return; + } + + /* it's is too expensive: take it down */ + removeGatewayFromList(gw_list, ipv4, worst_gw); + } +} + +/** + * Timer callback for proactive gateway takedown + * + * @param unused unused + */ +static void gw_takedown_timer_callback(void *unused __attribute__ ((unused))) { + takeDownExpensiveGateways(&gw_list_ipv4, true, current_ipv4_gw); + takeDownExpensiveGateways(&gw_list_ipv6, false, current_ipv6_gw); +} + /* * Main Interface */ @@ -162,13 +551,56 @@ int olsr_init_gateways(void) { gateway_entry_mem_cookie = olsr_alloc_cookie("gateway_entry_mem_cookie", OLSR_COOKIE_TYPE_MEMORY); olsr_cookie_set_memory_size(gateway_entry_mem_cookie, sizeof(struct gateway_entry)); + gw_container_entry_mem_cookie = olsr_alloc_cookie("gw_container_entry_mem_cookie", OLSR_COOKIE_TYPE_MEMORY); + olsr_cookie_set_memory_size(gw_container_entry_mem_cookie, sizeof(struct gw_container_entry)); + avl_init(&gateway_tree, avl_comp_default); - current_ipv4_gw = NULL; - v4gw_tunnel = NULL; + olsr_gw_list_init(&gw_list_ipv4, olsr_cnf->smart_gw_use_count); + olsr_gw_list_init(&gw_list_ipv6, olsr_cnf->smart_gw_use_count); + if (!multi_gateway_mode()) { + sgwEgressInterfaceNames = NULL; + sgwTunnel4InterfaceNames = NULL; + sgwTunnel6InterfaceNames = NULL; + } else { + uint8_t i; + struct sgw_egress_if * egressif; + + /* setup the egress interface name/mark pairs */ + sgwEgressInterfaceNames = olsr_malloc(sizeof(struct interfaceName) * olsr_cnf->smart_gw_egress_interfaces_count, "sgwEgressInterfaceNames"); + i = 0; + egressif = olsr_cnf->smart_gw_egress_interfaces; + while (egressif) { + struct interfaceName * ifn = &sgwEgressInterfaceNames[i]; + ifn->gw = NULL; + ifn->mark = i + olsr_cnf->smart_gw_mark_offset_egress; + egressif->mark = ifn->mark; + snprintf(&ifn->name[0], sizeof(ifn->name), egressif->name, egressif->mark); + + egressif = egressif->next; + i++; + } + assert(i == olsr_cnf->smart_gw_egress_interfaces_count); + + /* setup the SGW tunnel name/mark pairs */ + sgwTunnel4InterfaceNames = olsr_malloc(sizeof(struct interfaceName) * olsr_cnf->smart_gw_use_count, "sgwTunnel4InterfaceNames"); + sgwTunnel6InterfaceNames = olsr_malloc(sizeof(struct interfaceName) * olsr_cnf->smart_gw_use_count, "sgwTunnel6InterfaceNames"); + for (i = 0; i < olsr_cnf->smart_gw_use_count; i++) { + struct interfaceName * ifn = &sgwTunnel4InterfaceNames[i]; + ifn->gw = NULL; + ifn->mark = i + olsr_cnf->smart_gw_mark_offset_tunnels; + snprintf(&ifn->name[0], sizeof(ifn->name), "tnl_4%03u", ifn->mark); + + ifn = &sgwTunnel6InterfaceNames[i]; + ifn->gw = NULL; + ifn->mark = i + olsr_cnf->smart_gw_mark_offset_tunnels; + snprintf(&ifn->name[0], sizeof(ifn->name), "tnl_6%03u", ifn->mark); + } + } + + current_ipv4_gw = NULL; current_ipv6_gw = NULL; - v6gw_tunnel = NULL; gw_handler = NULL; @@ -178,6 +610,7 @@ int olsr_init_gateways(void) { gw_handler = &gw_def_handler; gw_handler->init(); + /* * There appears to be a kernel bug in some kernels (at least in the 3.0 * Debian Squeeze kernel, but not in the Fedora 17 kernels) around @@ -185,7 +618,7 @@ int olsr_init_gateways(void) { * a few times before giving up */ while (retries-- > 0) { - if (!olsr_os_init_iptunnel(TUNNEL_NAME)) { + if (!olsr_os_init_iptunnel(server_tunnel_name())) { retries = 5; break; } @@ -201,37 +634,115 @@ int olsr_init_gateways(void) { return 0; } +/** + * Startup gateway system + */ +int olsr_startup_gateways(void) { + bool ok = true; + + if (!multi_gateway_mode()) { + return 0; + } + + ok = ok && multiGwRulesGeneric(true); + ok = ok && multiGwRulesSgwServerTunnel(true); + ok = ok && multiGwRulesOlsrInterfaces(true); + ok = ok && multiGwRulesEgressInterfaces(true); + ok = ok && multiGwRulesSgwTunnels(true); + if (!ok) { + olsr_printf(0, "Could not setup multi-gateway iptables and ip rules\n"); + olsr_shutdown_gateways(); + return 1; + } + + if (olsr_cnf->smart_gw_takedown_percentage > 0) { + /* start gateway takedown timer */ + olsr_set_timer(&gw_takedown_timer, olsr_cnf->smart_gw_period, 0, true, &gw_takedown_timer_callback, NULL, 0); + } + + return 0; +} + +/** + * Shutdown gateway tunnel system + */ +void olsr_shutdown_gateways(void) { + if (!multi_gateway_mode()) { + return; + } + + if (olsr_cnf->smart_gw_takedown_percentage > 0) { + /* stop gateway takedown timer */ + olsr_stop_timer(gw_takedown_timer); + gw_takedown_timer = NULL; + } + + (void)multiGwRulesSgwTunnels(false); + (void)multiGwRulesEgressInterfaces(false); + (void)multiGwRulesOlsrInterfaces(false); + (void)multiGwRulesSgwServerTunnel(false); + (void)multiGwRulesGeneric(false); +} + /** * Cleanup gateway tunnel system */ void olsr_cleanup_gateways(void) { - struct avl_node * avlnode = NULL; + struct gateway_entry * tree_gw; + struct gw_container_entry * gw; olsr_remove_ifchange_handler(smartgw_tunnel_monitor); - olsr_os_cleanup_iptunnel(TUNNEL_NAME); - /* remove all gateways in the gateway tree that are not the active gateway */ - while ((avlnode = avl_walk_first(&gateway_tree))) { - struct gateway_entry* tree_gw = node2gateway(avlnode); + OLSR_FOR_ALL_GATEWAY_ENTRIES(tree_gw) { if ((tree_gw != olsr_get_inet_gateway(false)) && (tree_gw != olsr_get_inet_gateway(true))) { olsr_delete_gateway_tree_entry(tree_gw, FORCE_DELETE_GW_ENTRY, true); } - } + } OLSR_FOR_ALL_GATEWAY_ENTRIES_END(gw) - /* remove the active IPv4 gateway */ - olsr_delete_gateway_tree_entry(olsr_get_inet_gateway(false), FORCE_DELETE_GW_ENTRY, true); + /* remove all active IPv4 gateways (should be at most 1 now) */ + OLSR_FOR_ALL_GWS(&gw_list_ipv4.head, gw) { + if (gw && gw->gw) { + olsr_delete_gateway_entry(&gw->gw->originator, FORCE_DELETE_GW_ENTRY, true); + } + } + OLSR_FOR_ALL_GWS_END(gw); - /* remove the active IPv6 gateway */ - olsr_delete_gateway_tree_entry(olsr_get_inet_gateway(true), FORCE_DELETE_GW_ENTRY, true); + /* remove all active IPv6 gateways (should be at most 1 now) */ + OLSR_FOR_ALL_GWS(&gw_list_ipv6.head, gw) { + if (gw && gw->gw) { + olsr_delete_gateway_entry(&gw->gw->originator, FORCE_DELETE_GW_ENTRY, true); + } + } + OLSR_FOR_ALL_GWS_END(gw); /* there should be no more gateways */ assert(!avl_walk_first(&gateway_tree)); + assert(!current_ipv4_gw); + assert(!current_ipv6_gw); + + olsr_os_cleanup_iptunnel(server_tunnel_name()); assert(gw_handler); gw_handler->cleanup(); gw_handler = NULL; + if (sgwEgressInterfaceNames) { + free(sgwEgressInterfaceNames); + sgwEgressInterfaceNames = NULL; + } + if (sgwTunnel4InterfaceNames) { + free(sgwTunnel4InterfaceNames); + sgwTunnel4InterfaceNames = NULL; + } + if (sgwTunnel6InterfaceNames) { + free(sgwTunnel6InterfaceNames); + sgwTunnel6InterfaceNames = NULL; + } + + olsr_gw_list_cleanup(&gw_list_ipv6); + olsr_gw_list_cleanup(&gw_list_ipv4); + olsr_free_cookie(gw_container_entry_mem_cookie); olsr_free_cookie(gateway_entry_mem_cookie); } @@ -283,8 +794,9 @@ void olsr_print_gateway_entries(void) { * @param prefixlen of the HNA */ void olsr_modifiy_inetgw_netmask(union olsr_ip_addr *mask, int prefixlen) { - uint8_t *ptr = OLSR_IP_ADDR_2_HNA_PTR(mask, prefixlen); + uint8_t *ptr = hna_mask_to_hna_pointer(mask, prefixlen); + /* copy the current settings for uplink/downlink into the mask */ memcpy(ptr, &smart_gateway_netmask, sizeof(smart_gateway_netmask) - prefixlen / 8); if (olsr_cnf->has_ipv4_gateway) { ptr[GW_HNA_FLAGS] |= GW_HNA_FLAG_IPV4; @@ -310,17 +822,17 @@ void olsr_modifiy_inetgw_netmask(union olsr_ip_addr *mask, int prefixlen) { */ void refresh_smartgw_netmask(void) { uint8_t *ip; + + /* clear the mask */ memset(&smart_gateway_netmask, 0, sizeof(smart_gateway_netmask)); if (olsr_cnf->smart_gw_active) { ip = (uint8_t *) &smart_gateway_netmask; - if (olsr_cnf->smart_gw_uplink > 0 && olsr_cnf->smart_gw_downlink > 0) { - /* the link is bi-directional with a non-zero bandwidth */ - ip[GW_HNA_FLAGS] |= GW_HNA_FLAG_LINKSPEED; - ip[GW_HNA_DOWNLINK] = serialize_gw_speed(olsr_cnf->smart_gw_downlink); - ip[GW_HNA_UPLINK] = serialize_gw_speed(olsr_cnf->smart_gw_uplink); - } + ip[GW_HNA_FLAGS] |= GW_HNA_FLAG_LINKSPEED; + ip[GW_HNA_DOWNLINK] = serialize_gw_speed(olsr_cnf->smart_gw_downlink); + ip[GW_HNA_UPLINK] = serialize_gw_speed(olsr_cnf->smart_gw_uplink); + if (olsr_cnf->ip_version == AF_INET6 && olsr_cnf->smart_gw_prefix.prefix_len > 0) { ip[GW_HNA_FLAGS] |= GW_HNA_FLAG_IPV6PREFIX; ip[GW_HNA_V6PREFIXLEN] = olsr_cnf->smart_gw_prefix.prefix_len; @@ -347,7 +859,7 @@ bool olsr_is_smart_gateway(struct olsr_ip_prefix *prefix, union olsr_ip_addr *ma return false; } - ptr = OLSR_IP_ADDR_2_HNA_PTR(mask, prefix->prefix_len); + ptr = hna_mask_to_hna_pointer(mask, prefix->prefix_len); return ptr[GW_HNA_PAD] == 0 && ptr[GW_HNA_FLAGS] != 0; } @@ -360,6 +872,7 @@ bool olsr_is_smart_gateway(struct olsr_ip_prefix *prefix, union olsr_ip_addr *ma * @param seqno the sequence number of the HNA */ void olsr_update_gateway_entry(union olsr_ip_addr *originator, union olsr_ip_addr *mask, int prefixlen, uint16_t seqno) { + struct gw_container_entry * new_gw_in_list; uint8_t *ptr; struct gateway_entry *gw = node2gateway(avl_find(&gateway_tree, originator)); @@ -377,7 +890,7 @@ void olsr_update_gateway_entry(union olsr_ip_addr *originator, union olsr_ip_add /* keep new HNA seqno */ gw->seqno = seqno; - ptr = OLSR_IP_ADDR_2_HNA_PTR(mask, prefixlen); + ptr = hna_mask_to_hna_pointer(mask, prefixlen); if ((ptr[GW_HNA_FLAGS] & GW_HNA_FLAG_LINKSPEED) != 0) { gw->uplink = deserialize_gw_speed(ptr[GW_HNA_UPLINK]); gw->downlink = deserialize_gw_speed(ptr[GW_HNA_DOWNLINK]); @@ -411,6 +924,21 @@ void olsr_update_gateway_entry(union olsr_ip_addr *originator, union olsr_ip_add gw->cleanup_timer = NULL; } + /* update the costs of the gateway when it is an active gateway */ + new_gw_in_list = olsr_gw_list_find(&gw_list_ipv4, gw); + if (new_gw_in_list) { + assert(gw_handler); + new_gw_in_list = olsr_gw_list_update(&gw_list_ipv4, new_gw_in_list, gw_handler->getcosts(new_gw_in_list->gw)); + assert(new_gw_in_list); + } + + new_gw_in_list = olsr_gw_list_find(&gw_list_ipv6, gw); + if (new_gw_in_list) { + assert(gw_handler); + new_gw_in_list = olsr_gw_list_update(&gw_list_ipv6, new_gw_in_list, gw_handler->getcosts(new_gw_in_list->gw)); + assert(new_gw_in_list); + } + /* call update handler */ assert(gw_handler); gw_handler->update(gw); @@ -452,7 +980,7 @@ static void olsr_delete_gateway_tree_entry(struct gateway_entry * gw, uint8_t pr } if (gw->cleanup_timer == NULL || gw->ipv4 || gw->ipv6) { - /* found a gw and it wasn't deleted yet */ + /* the gw is not scheduled for deletion */ if (olsr_cnf->ip_version == AF_INET && prefixlen == 0) { change = gw->ipv4; @@ -468,6 +996,8 @@ static void olsr_delete_gateway_tree_entry(struct gateway_entry * gw, uint8_t pr } if (prefixlen == FORCE_DELETE_GW_ENTRY || !(gw->ipv4 || gw->ipv6)) { + struct gw_container_entry * gw_in_list; + /* prevent this gateway from being chosen as the new gateway */ gw->ipv4 = false; gw->ipv4nat = false; @@ -478,23 +1008,48 @@ static void olsr_delete_gateway_tree_entry(struct gateway_entry * gw, uint8_t pr gw_handler->delete(gw); /* cleanup gateway if necessary */ - if (current_ipv4_gw == gw) { - if (v4gw_tunnel) { - olsr_os_inetgw_tunnel_route(v4gw_tunnel->if_index, true, false); - olsr_os_del_ipip_tunnel(v4gw_tunnel); - v4gw_tunnel = NULL; + gw_in_list = olsr_gw_list_find(&gw_list_ipv4, gw); + if (gw_in_list) { + if (current_ipv4_gw && current_ipv4_gw->gw == gw) { + olsr_os_inetgw_tunnel_route(current_ipv4_gw->tunnel->if_index, true, false, olsr_cnf->rt_table_tunnel); + current_ipv4_gw = NULL; + } + + if (gw_in_list->tunnel) { + struct interfaceName * ifn = find_interfaceName(gw_in_list->gw); + if (ifn) { + olsr_os_inetgw_tunnel_route(gw_in_list->tunnel->if_index, true, false, ifn->mark); + } + olsr_os_del_ipip_tunnel(gw_in_list->tunnel); + set_unused_iptunnel_name(gw_in_list->gw); + gw_in_list->tunnel = NULL; } - current_ipv4_gw = NULL; + gw_in_list->gw = NULL; + gw_in_list = olsr_gw_list_remove(&gw_list_ipv4, gw_in_list); + olsr_cookie_free(gw_container_entry_mem_cookie, gw_in_list); } - if (current_ipv6_gw == gw) { - if (v6gw_tunnel) { - olsr_os_inetgw_tunnel_route(v6gw_tunnel->if_index, false, false); - olsr_os_del_ipip_tunnel(v6gw_tunnel); - v6gw_tunnel = NULL; + + gw_in_list = olsr_gw_list_find(&gw_list_ipv6, gw); + if (gw_in_list) { + if (current_ipv6_gw && current_ipv6_gw->gw == gw) { + olsr_os_inetgw_tunnel_route(current_ipv6_gw->tunnel->if_index, false, false, olsr_cnf->rt_table_tunnel); + current_ipv6_gw = NULL; + } + + if (gw_in_list->tunnel) { + struct interfaceName * ifn = find_interfaceName(gw_in_list->gw); + if (ifn) { + olsr_os_inetgw_tunnel_route(gw_in_list->tunnel->if_index, false, false, ifn->mark); + } + olsr_os_del_ipip_tunnel(gw_in_list->tunnel); + set_unused_iptunnel_name(gw_in_list->gw); + gw_in_list->tunnel = NULL; } - current_ipv6_gw = NULL; + gw_in_list->gw = NULL; + gw_in_list = olsr_gw_list_remove(&gw_list_ipv6, gw_in_list); + olsr_cookie_free(gw_container_entry_mem_cookie, gw_in_list); } if (!immediate) { @@ -503,6 +1058,13 @@ static void olsr_delete_gateway_tree_entry(struct gateway_entry * gw, uint8_t pr } else { cleanup_gateway_handler(gw); } + + /* when the current gateway was deleted, then immediately choose a new gateway */ + if (!current_ipv4_gw || !current_ipv6_gw) { + assert(gw_handler); + gw_handler->choose(!current_ipv4_gw, !current_ipv6_gw); + } + } else if (change) { assert(gw_handler); gw_handler->update(gw); @@ -518,12 +1080,12 @@ void olsr_trigger_gatewayloss_check(void) { bool ipv4 = false; bool ipv6 = false; - if (current_ipv4_gw) { - struct tc_entry *tc = olsr_lookup_tc_entry(¤t_ipv4_gw->originator); + if (current_ipv4_gw && current_ipv4_gw->gw) { + struct tc_entry *tc = olsr_lookup_tc_entry(¤t_ipv4_gw->gw->originator); ipv4 = (tc == NULL || tc->path_cost == ROUTE_COST_BROKEN); } - if (current_ipv6_gw) { - struct tc_entry *tc = olsr_lookup_tc_entry(¤t_ipv6_gw->originator); + if (current_ipv6_gw && current_ipv6_gw->gw) { + struct tc_entry *tc = olsr_lookup_tc_entry(¤t_ipv6_gw->gw->originator); ipv6 = (tc == NULL || tc->path_cost == ROUTE_COST_BROKEN); } @@ -541,11 +1103,12 @@ void olsr_trigger_gatewayloss_check(void) { * Sets a new internet gateway. * * @param originator ip address of the node with the new gateway + * @param path_cost the path cost * @param ipv4 set ipv4 gateway * @param ipv6 set ipv6 gateway * @return true if an error happened, false otherwise */ -bool olsr_set_inet_gateway(union olsr_ip_addr *originator, bool ipv4, bool ipv6) { +bool olsr_set_inet_gateway(union olsr_ip_addr *originator, uint64_t path_cost, bool ipv4, bool ipv6) { struct gateway_entry *new_gw; ipv4 = ipv4 && (olsr_cnf->ip_version == AF_INET || olsr_cnf->use_niit); @@ -561,37 +1124,98 @@ bool olsr_set_inet_gateway(union olsr_ip_addr *originator, bool ipv4, bool ipv6) } /* handle IPv4 */ - if (ipv4 && new_gw->ipv4 && (!new_gw->ipv4nat || olsr_cnf->smart_gw_allow_nat) && current_ipv4_gw != new_gw) { - struct olsr_iptunnel_entry *new_v4gw_tunnel = olsr_os_add_ipip_tunnel(&new_gw->originator, true); - if (new_v4gw_tunnel) { - olsr_os_inetgw_tunnel_route(new_v4gw_tunnel->if_index, true, true); - if (v4gw_tunnel) { - olsr_os_del_ipip_tunnel(v4gw_tunnel); - v4gw_tunnel = NULL; - } - current_ipv4_gw = new_gw; - v4gw_tunnel = new_v4gw_tunnel; + if (ipv4 && + new_gw->ipv4 && + (!new_gw->ipv4nat || olsr_cnf->smart_gw_allow_nat) && + (!current_ipv4_gw || current_ipv4_gw->gw != new_gw || current_ipv4_gw->path_cost != path_cost)) { + /* new gw is different than the current gw, or costs have changed */ + + struct gw_container_entry * new_gw_in_list = olsr_gw_list_find(&gw_list_ipv4, new_gw); + if (new_gw_in_list) { + /* new gw is already in the gw list */ + assert(new_gw_in_list->tunnel); + olsr_os_inetgw_tunnel_route(new_gw_in_list->tunnel->if_index, true, true, olsr_cnf->rt_table_tunnel); + current_ipv4_gw = olsr_gw_list_update(&gw_list_ipv4, new_gw_in_list, path_cost); } else { - /* adding the tunnel failed, we try again in the next cycle */ - ipv4 = false; + /* new gw is not yet in the gw list */ + char name[IFNAMSIZ]; + struct olsr_iptunnel_entry *new_v4gw_tunnel; + struct interfaceName * interfaceName; + + if (olsr_gw_list_full(&gw_list_ipv4)) { + /* the list is full: remove the worst active gateway */ + struct gw_container_entry* worst = olsr_gw_list_get_worst_entry(&gw_list_ipv4); + assert(worst); + + removeGatewayFromList(&gw_list_ipv4, true, worst); + } + + get_unused_iptunnel_name(new_gw, name, &interfaceName); + new_v4gw_tunnel = olsr_os_add_ipip_tunnel(&new_gw->originator, true, name); + if (new_v4gw_tunnel) { + if (interfaceName) { + olsr_os_inetgw_tunnel_route(new_v4gw_tunnel->if_index, true, true, interfaceName->mark); + } + olsr_os_inetgw_tunnel_route(new_v4gw_tunnel->if_index, true, true, olsr_cnf->rt_table_tunnel); + + new_gw_in_list = olsr_cookie_malloc(gw_container_entry_mem_cookie); + new_gw_in_list->gw = new_gw; + new_gw_in_list->tunnel = new_v4gw_tunnel; + new_gw_in_list->path_cost = path_cost; + current_ipv4_gw = olsr_gw_list_add(&gw_list_ipv4, new_gw_in_list); + } else { + /* adding the tunnel failed, we try again in the next cycle */ + set_unused_iptunnel_name(new_gw); + ipv4 = false; + } } } /* handle IPv6 */ - if (ipv6 && new_gw->ipv6 && current_ipv6_gw != new_gw) { - struct olsr_iptunnel_entry *new_v6gw_tunnel = olsr_os_add_ipip_tunnel(&new_gw->originator, false); - if (new_v6gw_tunnel) { - olsr_os_inetgw_tunnel_route(new_v6gw_tunnel->if_index, false, true); - if (v6gw_tunnel) { - olsr_os_del_ipip_tunnel(v6gw_tunnel); - v6gw_tunnel = NULL; - } - current_ipv6_gw = new_gw; - v6gw_tunnel = new_v6gw_tunnel; - } else { - /* adding the tunnel failed, we try again in the next cycle */ - ipv6 = false; - } + if (ipv6 && + new_gw->ipv6 && + (!current_ipv6_gw || current_ipv6_gw->gw != new_gw || current_ipv6_gw->path_cost != path_cost)) { + /* new gw is different than the current gw, or costs have changed */ + + struct gw_container_entry * new_gw_in_list = olsr_gw_list_find(&gw_list_ipv6, new_gw); + if (new_gw_in_list) { + /* new gw is already in the gw list */ + assert(new_gw_in_list->tunnel); + olsr_os_inetgw_tunnel_route(new_gw_in_list->tunnel->if_index, true, true, olsr_cnf->rt_table_tunnel); + current_ipv6_gw = olsr_gw_list_update(&gw_list_ipv6, new_gw_in_list, path_cost); + } else { + /* new gw is not yet in the gw list */ + char name[IFNAMSIZ]; + struct olsr_iptunnel_entry *new_v6gw_tunnel; + struct interfaceName * interfaceName; + + if (olsr_gw_list_full(&gw_list_ipv6)) { + /* the list is full: remove the worst active gateway */ + struct gw_container_entry* worst = olsr_gw_list_get_worst_entry(&gw_list_ipv6); + assert(worst); + + removeGatewayFromList(&gw_list_ipv6, false, worst); + } + + get_unused_iptunnel_name(new_gw, name, &interfaceName); + new_v6gw_tunnel = olsr_os_add_ipip_tunnel(&new_gw->originator, false, name); + if (new_v6gw_tunnel) { + if (interfaceName) { + olsr_os_inetgw_tunnel_route(new_v6gw_tunnel->if_index, false, true, interfaceName->mark); + } + olsr_os_inetgw_tunnel_route(new_v6gw_tunnel->if_index, false, true, olsr_cnf->rt_table_tunnel); + + new_gw_in_list = olsr_cookie_malloc(gw_container_entry_mem_cookie); + new_gw_in_list->gw = new_gw; + new_gw_in_list->tunnel = new_v6gw_tunnel; + new_gw_in_list->path_cost = path_cost; + current_ipv6_gw = olsr_gw_list_add(&gw_list_ipv6, new_gw_in_list); + } else { + /* adding the tunnel failed, we try again in the next cycle */ + set_unused_iptunnel_name(new_gw); + ipv6 = false; + } + } } return !ipv4 && !ipv6; @@ -605,10 +1229,10 @@ bool olsr_set_inet_gateway(union olsr_ip_addr *originator, bool ipv4, bool ipv6) */ struct gateway_entry *olsr_get_inet_gateway(bool ipv6) { if (ipv6) { - return current_ipv6_gw; + return current_ipv6_gw ? current_ipv6_gw->gw : NULL; } - return current_ipv4_gw; + return current_ipv4_gw ? current_ipv4_gw->gw : NULL; } #endif /* __linux__ */ diff --git a/src/gateway.h b/src/gateway.h index 385a48e7..74812182 100644 --- a/src/gateway.h +++ b/src/gateway.h @@ -102,6 +102,14 @@ struct olsr_gw_handler { */ void (*startup)(void); + /** + * Called when the costs of a gateway must be determined. + * + * @param gw the gateway + * @return the costs + */ + uint64_t (*getcosts)(struct gateway_entry *gw); + /** * Called when a new gateway must be chosen. * @@ -130,6 +138,8 @@ struct olsr_gw_handler { */ int olsr_init_gateways(void); +int olsr_startup_gateways(void); +void olsr_shutdown_gateways(void); void olsr_cleanup_gateways(void); void olsr_trigger_inetgw_startup(void); #ifndef NODEBUG @@ -163,7 +173,7 @@ void olsr_trigger_gatewayloss_check(void); * Gateway Plugin Functions */ -bool olsr_set_inet_gateway(union olsr_ip_addr *originator, bool ipv4, bool ipv6); +bool olsr_set_inet_gateway(union olsr_ip_addr *originator, uint64_t path_cost, bool ipv4, bool ipv6); struct gateway_entry *olsr_get_inet_gateway(bool ipv6); #endif /* GATEWAY_H_ */ diff --git a/src/gateway_default_handler.c b/src/gateway_default_handler.c index ec331fc4..11bca377 100644 --- a/src/gateway_default_handler.c +++ b/src/gateway_default_handler.c @@ -14,14 +14,15 @@ static uint32_t gw_def_nodecount; static uint32_t gw_def_stablecount; -static bool gw_def_finished_ipv4; -static bool gw_def_finished_ipv6; +static bool gw_def_choose_new_ipv4_gw; +static bool gw_def_choose_new_ipv6_gw; static struct timer_entry *gw_def_timer; /* forward declarations */ static void gw_default_init(void); static void gw_default_cleanup(void); static void gw_default_startup_handler(void); +static uint64_t gw_default_getcosts(struct gateway_entry *gw); static void gw_default_choosegw_handler(bool ipv4, bool ipv6); static void gw_default_update_handler(struct gateway_entry *); static void gw_default_delete_handler(struct gateway_entry *); @@ -33,6 +34,7 @@ struct olsr_gw_handler gw_def_handler = { &gw_default_init, &gw_default_cleanup, &gw_default_startup_handler, + &gw_default_getcosts, &gw_default_choosegw_handler, &gw_default_update_handler, &gw_default_delete_handler @@ -49,15 +51,11 @@ struct olsr_gw_handler gw_def_handler = { * @return the threshold path cost */ static inline uint64_t gw_default_calc_threshold(uint64_t path_cost) { - uint64_t path_cost_times_threshold; - if (olsr_cnf->smart_gw_thresh == 0) { - path_cost_times_threshold = path_cost; - } else { - path_cost_times_threshold = (path_cost * (uint64_t) olsr_cnf->smart_gw_thresh + (uint64_t) 50) / (uint64_t) 100; + return path_cost; } - return path_cost_times_threshold; + return ((path_cost * (uint64_t) olsr_cnf->smart_gw_thresh) + (uint64_t) 50) / (uint64_t) 100; } /** @@ -74,7 +72,7 @@ static inline uint64_t gw_default_calc_threshold(uint64_t path_cost) { * @param exitDk the gateway exit link downlink bandwidth (in kbps) * @return the weighed path cost */ -static inline uint64_t gw_default_weigh_costs(uint64_t path_cost, uint32_t exitUk, uint32_t exitDk) { +static inline uint64_t gw_default_weigh_costs(uint32_t path_cost, uint32_t exitUk, uint32_t exitDk) { uint8_t WexitU = olsr_cnf->smart_gw_weight_exitlink_up; uint8_t WexitD = olsr_cnf->smart_gw_weight_exitlink_down; uint8_t Wetx = olsr_cnf->smart_gw_weight_etx; @@ -84,8 +82,8 @@ static inline uint64_t gw_default_weigh_costs(uint64_t path_cost, uint32_t exitU uint64_t costE; if (!Detx) { - /* only consider path costs (classic behaviour) */ - return path_cost; + /* only consider path costs (classic behaviour) (but scale to 64 bit) */ + return (uint64_t)path_cost << 32; } if (!exitUk || !exitDk) { @@ -103,9 +101,9 @@ static inline uint64_t gw_default_weigh_costs(uint64_t path_cost, uint32_t exitU * Wetx = the ETX path cost weight (configured) * Detx = the ETX path cost divider (configured) * - * WexitU WexitD Wetx - * path_cost_weighed = ------ + ------ + ---- * path_cost - * exitUm exitDm Detx + * WexitU WexitD Wetx + * path_cost_weight = ------ + ------ + ---- * path_cost + * exitUm exitDm Detx * * Since the gateway exit link bandwidths are in Kbps, the following formula * is used to convert them to the desired Mbps: @@ -114,30 +112,30 @@ static inline uint64_t gw_default_weigh_costs(uint64_t path_cost, uint32_t exitU * bwM = ---- bwK = bandwidth in Kbps * 1000 bwM = bandwidth in Mbps * - * exitUm = the gateway exit link uplink bandwidth, in Kbps - * exitDm = the gateway exit link downlink bandwidth, in Kbps + * exitUk = the gateway exit link uplink bandwidth, in Kbps + * exitDk = the gateway exit link downlink bandwidth, in Kbps * - * 1000 * WexitU 1000 * WexitD Wetx - * path_cost_weighed = ------------- + ------------- + ---- * path_cost - * exitUk exitDk Detx + * 1000 * WexitU 1000 * WexitD Wetx + * path_cost_weight = ------------- + ------------- + ---- * path_cost + * exitUk exitDk Detx * * * Analysis of the required bit width of the result: * - * exitUk = 29 bits = [1, 320,000,000] - * exitDk = 29 bits = [1, 320,000,000] - * WexitU = 8 bits = [1, 255] - * WexitD = 8 bits = [1, 255] - * Wetx = 8 bits = [1, 255] - * Detx = 8 bits = [1, 255] - * path_cost = 32 bits = [1, 4,294,967,295] + * exitUk = [1, 320,000,000] = 29 bits + * exitDk = [1, 320,000,000] = 29 bits + * WexitU = [1, 255] = 8 bits + * WexitD = [1, 255] = 8 bits + * Wetx = [1, 255] = 8 bits + * Detx = [1, 255] = 8 bits + * path_cost = [1, 4,294,967,295] = 32 bits * - * 1000 * 255 1000 * 255 255 - * path_cost_weighed(max) = ---------- + ---------- + --- * 4,294,967,295 - * 1 1 1 + * 1000 * 255 1000 * 255 255 + * path_cost_weight(max) = ---------- + ---------- + --- * 4,294,967,295 + * 1 1 1 * - * path_cost_weighed(max) = 0x3E418 + 0x3E418 + 0xFEFFFFFF01 - * path_cost_weighed(max) = 0xFF0007C731 + * path_cost_weight(max) = 0x3E418 + 0x3E418 + 0xFEFFFFFF01 + * path_cost_weight(max) = 0xFF0007C731 * * Because we can multiply 0xFF0007C731 by 2^24 without overflowing a * 64 bits number, we do this to increase accuracy. @@ -157,90 +155,78 @@ static inline uint64_t gw_default_weigh_costs(uint64_t path_cost, uint32_t exitU static void gw_default_choose_gateway(void) { uint64_t cost_ipv4_threshold = UINT64_MAX; uint64_t cost_ipv6_threshold = UINT64_MAX; - bool eval_cost_ipv4_threshold = false; - bool eval_cost_ipv6_threshold = false; - struct gateway_entry *inet_ipv4 = NULL; - struct gateway_entry *inet_ipv6 = NULL; - uint64_t cost_ipv4 = UINT64_MAX; - uint64_t cost_ipv6 = UINT64_MAX; + bool cost_ipv4_threshold_valid = false; + bool cost_ipv6_threshold_valid = false; + struct gateway_entry *chosen_gw_ipv4 = NULL; + struct gateway_entry *chosen_gw_ipv6 = NULL; + uint64_t chosen_gw_ipv4_costs = UINT64_MAX; + uint64_t chosen_gw_ipv6_costs = UINT64_MAX; struct gateway_entry *gw; - struct tc_entry *tc; - bool dual; + bool dual = false; if (olsr_cnf->smart_gw_thresh) { /* determine the path cost thresholds */ - gw = olsr_get_inet_gateway(false); - if (gw) { - tc = olsr_lookup_tc_entry(&gw->originator); - if (tc) { - uint64_t cost = gw_default_weigh_costs(tc->path_cost, gw->uplink, gw->downlink); - cost_ipv4_threshold = gw_default_calc_threshold(cost); - eval_cost_ipv4_threshold = true; - } + uint64_t cost = gw_default_getcosts(olsr_get_inet_gateway(false)); + if (cost != UINT64_MAX) { + cost_ipv4_threshold = gw_default_calc_threshold(cost); + cost_ipv4_threshold_valid = true; } - gw = olsr_get_inet_gateway(true); - if (gw) { - tc = olsr_lookup_tc_entry(&gw->originator); - if (tc) { - uint64_t cost = gw_default_weigh_costs(tc->path_cost, gw->uplink, gw->downlink); - cost_ipv6_threshold = gw_default_calc_threshold(cost); - eval_cost_ipv6_threshold = true; - } + + cost = gw_default_getcosts(olsr_get_inet_gateway(true)); + if (cost != UINT64_MAX) { + cost_ipv6_threshold = gw_default_calc_threshold(cost); + cost_ipv6_threshold_valid = true; } } OLSR_FOR_ALL_GATEWAY_ENTRIES(gw) { - uint64_t path_cost; - tc = olsr_lookup_tc_entry(&gw->originator); + uint64_t gw_cost = gw_default_getcosts(gw); - if (!tc) { - /* gateways should not exist without tc entry */ + if (gw_cost == UINT64_MAX) { + /* never select a node with infinite costs */ continue; } - if (tc->path_cost == ROUTE_COST_BROKEN) { - /* do not consider nodes with an infinite ETX */ - continue; - } - - if (!gw->uplink || !gw->downlink) { - /* do not consider nodes without bandwidth or with a uni-directional link */ - continue; + if (gw_def_choose_new_ipv4_gw) { + bool gw_eligible_v4 = gw->ipv4 + /* && (olsr_cnf->ip_version == AF_INET || olsr_cnf->use_niit) *//* contained in gw_def_choose_new_ipv4_gw */ + && (olsr_cnf->smart_gw_allow_nat || !gw->ipv4nat); + if (gw_eligible_v4 && gw_cost < chosen_gw_ipv4_costs + && (!cost_ipv4_threshold_valid || (gw_cost < cost_ipv4_threshold))) { + chosen_gw_ipv4 = gw; + chosen_gw_ipv4_costs = gw_cost; + } } - /* determine the path cost */ - path_cost = gw_default_weigh_costs(tc->path_cost, gw->uplink, gw->downlink); - - if (!gw_def_finished_ipv4 && gw->ipv4 && gw->ipv4nat == olsr_cnf->smart_gw_allow_nat && path_cost < cost_ipv4 - && (!eval_cost_ipv4_threshold || (path_cost < cost_ipv4_threshold))) { - inet_ipv4 = gw; - cost_ipv4 = path_cost; - } - if (!gw_def_finished_ipv6 && gw->ipv6 && path_cost < cost_ipv6 - && (!eval_cost_ipv6_threshold || (path_cost < cost_ipv6_threshold))) { - inet_ipv6 = gw; - cost_ipv6 = path_cost; + if (gw_def_choose_new_ipv6_gw) { + bool gw_eligible_v6 = gw->ipv6 + /* && olsr_cnf->ip_version == AF_INET6 *//* contained in gw_def_choose_new_ipv6_gw */; + if (gw_eligible_v6 && gw_cost < chosen_gw_ipv6_costs + && (!cost_ipv6_threshold_valid || (gw_cost < cost_ipv6_threshold))) { + chosen_gw_ipv6 = gw; + chosen_gw_ipv6_costs = gw_cost; + } } } OLSR_FOR_ALL_GATEWAY_ENTRIES_END(gw) - /* determine if we found an IPv4 and IPv6 gateway */ - gw_def_finished_ipv4 |= (inet_ipv4 != NULL); - gw_def_finished_ipv6 |= (inet_ipv6 != NULL); + /* determine if we should keep looking for IPv4 and/or IPv6 gateways */ + gw_def_choose_new_ipv4_gw = gw_def_choose_new_ipv4_gw && (chosen_gw_ipv4 == NULL); + gw_def_choose_new_ipv6_gw = gw_def_choose_new_ipv6_gw && (chosen_gw_ipv6 == NULL); /* determine if we are dealing with a dual stack gateway */ - dual = (inet_ipv4 == inet_ipv6) && (inet_ipv4 != NULL); + dual = chosen_gw_ipv4 && (chosen_gw_ipv4 == chosen_gw_ipv6); - if (inet_ipv4) { + if (chosen_gw_ipv4) { /* we are dealing with an IPv4 or dual stack gateway */ - olsr_set_inet_gateway(&inet_ipv4->originator, true, dual); + olsr_set_inet_gateway(&chosen_gw_ipv4->originator, chosen_gw_ipv4_costs, true, dual); } - if (inet_ipv6 && !dual) { + if (chosen_gw_ipv6 && !dual) { /* we are dealing with an IPv6-only gateway */ - olsr_set_inet_gateway(&inet_ipv6->originator, false, true); + olsr_set_inet_gateway(&chosen_gw_ipv6->originator, chosen_gw_ipv6_costs, false, true); } - if ((olsr_cnf->smart_gw_thresh == 0) && gw_def_finished_ipv4 && gw_def_finished_ipv6) { + if ((olsr_cnf->smart_gw_thresh == 0) && !gw_def_choose_new_ipv4_gw && !gw_def_choose_new_ipv6_gw) { /* stop looking for a better gateway */ olsr_stop_timer(gw_def_timer); gw_def_timer = NULL; @@ -285,14 +271,14 @@ static void gw_default_timer(void *unused __attribute__ ((unused))) { static void gw_default_lookup_gateway(bool ipv4, bool ipv6) { if (ipv4) { /* get a new IPv4 gateway if we use OLSRv4 or NIIT */ - gw_def_finished_ipv4 = !(olsr_cnf->ip_version == AF_INET || olsr_cnf->use_niit); + gw_def_choose_new_ipv4_gw = (olsr_cnf->ip_version == AF_INET) || olsr_cnf->use_niit; } if (ipv6) { /* get a new IPv6 gateway if we use OLSRv6 */ - gw_def_finished_ipv6 = !(olsr_cnf->ip_version == AF_INET6); + gw_def_choose_new_ipv6_gw = olsr_cnf->ip_version == AF_INET6; } - if (!(gw_def_finished_ipv4 && gw_def_finished_ipv6)) { + if (gw_def_choose_new_ipv4_gw || gw_def_choose_new_ipv6_gw) { gw_default_choose_gateway(); } } @@ -312,8 +298,8 @@ static void gw_default_init(void) { /* initialize values */ gw_def_nodecount = 0; gw_def_stablecount = 0; - gw_def_finished_ipv4 = false; - gw_def_finished_ipv6 = false; + gw_def_choose_new_ipv4_gw = true; + gw_def_choose_new_ipv6_gw = true; gw_def_timer = NULL; } @@ -332,19 +318,45 @@ static void gw_default_startup_handler(void) { gw_def_stablecount = 0; /* get a new IPv4 gateway if we use OLSRv4 or NIIT */ - gw_def_finished_ipv4 = !(olsr_cnf->ip_version == AF_INET || olsr_cnf->use_niit); + gw_def_choose_new_ipv4_gw = (olsr_cnf->ip_version == AF_INET) || olsr_cnf->use_niit; /* get a new IPv6 gateway if we use OLSRv6 */ - gw_def_finished_ipv6 = !(olsr_cnf->ip_version == AF_INET6); + gw_def_choose_new_ipv6_gw = olsr_cnf->ip_version == AF_INET6; /* keep in mind we might be a gateway ourself */ - gw_def_finished_ipv4 |= olsr_cnf->has_ipv4_gateway; - gw_def_finished_ipv6 |= olsr_cnf->has_ipv6_gateway; + gw_def_choose_new_ipv4_gw = gw_def_choose_new_ipv4_gw && !olsr_cnf->has_ipv4_gateway; + gw_def_choose_new_ipv6_gw = gw_def_choose_new_ipv6_gw && !olsr_cnf->has_ipv6_gateway; /* (re)start gateway lazy selection timer */ olsr_set_timer(&gw_def_timer, olsr_cnf->smart_gw_period, 0, true, &gw_default_timer, NULL, 0); } +/** + * Called when the costs of a gateway must be determined. + * + * @param gw the gateway + * @return the costs, or UINT64_MAX in case the gateway is null or has inifinite costs + */ +static uint64_t gw_default_getcosts(struct gateway_entry *gw) { + struct tc_entry* tc; + + if (!gw) { + return UINT64_MAX; + } + + tc = olsr_lookup_tc_entry(&gw->originator); + + if (!tc || (tc->path_cost == ROUTE_COST_BROKEN) || (!gw->uplink || !gw->downlink)) { + /* gateways should not exist without tc entry */ + /* do not consider nodes with an infinite ETX */ + /* do not consider nodes without bandwidth or with a uni-directional link */ + return UINT64_MAX; + } + + /* determine the path cost */ + return gw_default_weigh_costs(tc->path_cost, gw->uplink, gw->downlink); +} + /** * Choose a new gateway * @@ -354,7 +366,7 @@ static void gw_default_startup_handler(void) { static void gw_default_choosegw_handler(bool ipv4, bool ipv6) { gw_default_lookup_gateway(ipv4, ipv6); - if (!(gw_def_finished_ipv4 && gw_def_finished_ipv6)) { + if (gw_def_choose_new_ipv4_gw || gw_def_choose_new_ipv6_gw) { gw_default_startup_handler(); } } diff --git a/src/gateway_list.c b/src/gateway_list.c new file mode 100644 index 00000000..c84a7891 --- /dev/null +++ b/src/gateway_list.c @@ -0,0 +1,187 @@ +/* + * The olsr.org Optimized Link-State Routing daemon(olsrd) + * Copyright (c) 2004, Thomas Lopatic (thomas@lopatic.de) + * IPv4 performance optimization (c) 2006, sven-ola(gmx.de) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of olsr.org, olsrd nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Visit http://www.olsr.org for more information. + * + * If you find this software useful feel free to make a donation + * to the project. For more information see the website or contact + * the copyright holders. + * + */ + +#ifdef __linux__ + +#include "gateway_list.h" + +#include "gateway.h" +#include "common/list.h" + +#include +#include +#include + +/* + * Exported functions + */ + +/** + * Initialization + * + * @param list a pointer to the list + * @param count the maximum number of entries to be kept in the list. Must be + * larger than zero + */ +void olsr_gw_list_init(struct gw_list * list, uint8_t count) { + assert(list); + assert(count > 0); + + list_head_init(&list->head); + list->count_max = count; + list->count = 0; +} + +/** + * Cleanup + * + * @param list a pointer to the list + */ +void olsr_gw_list_cleanup(struct gw_list * list __attribute__((unused))) { + /* nothing to do */ +} + +/** + * Find an entry on the list + * + * @param list a pointer to the list + * @param entry a pointer to the entry to find + * @return a pointer to the entry, or NULL when not found + */ +struct gw_container_entry * olsr_gw_list_find(struct gw_list * list, struct gateway_entry * entry) { + struct gw_container_entry * gw; + + assert(list); + assert(entry); + + OLSR_FOR_ALL_GWS(&list->head, gw) { + if (gw && gw->gw && (gw->gw == entry)) { + return gw; + } + } + OLSR_FOR_ALL_GWS_END(gw); + + return NULL; +} + +/** + * Add an entry to the list. + * + * The list is ordered on costs with the lowest costs (best) first and the + * highest costs (worst) last. In case of equal costs, the entry is added + * _before_ the one(s) that is(are) already in the list. + * + * @param list a pointer to the list + * @param entry a pointer to the entry + * @return a pointer to the added entry + */ +struct gw_container_entry * olsr_gw_list_add(struct gw_list * list, struct gw_container_entry * entry) { + struct gw_container_entry * gw; + + assert(list); + assert(entry); + assert(!olsr_gw_list_full(list)); + + list_node_init(&entry->list_node); + + OLSR_FOR_ALL_GWS(&list->head, gw) { + if (gw && (entry->path_cost <= gw->path_cost)) { + /* add before the iterated list entry: the gateway to insert has lower + * costs or has equal costs but is newer (since we insert it) */ + list_add_before(&gw->list_node, &entry->list_node); + list->count++; + return entry; + } + } + OLSR_FOR_ALL_GWS_END(gw); + + /* add at the end */ + list_add_after(list->head.prev, &entry->list_node); + list->count++; + return entry; +} + +/** + * Update an entry on the list. + * + * @param list a pointer to the list + * @param entry a pointer to the entry + * @param path_cost the costs of the entry + * @return a pointer to the updated entry + */ +struct gw_container_entry * olsr_gw_list_update(struct gw_list * list, struct gw_container_entry * entry, + uint64_t path_cost) { + assert(list); + assert(entry); + assert(!olsr_gw_list_empty(list)); + + if (entry->path_cost == path_cost) { + return entry; + } + + /* don't touch gw */ + /* don't touch tunnel */ + entry->path_cost = path_cost; + /* don't touch list_node */ + + list_remove(&entry->list_node); + list->count--; + return olsr_gw_list_add(list, entry); +} + +/** + * Remove a gateway from the list (but do not free it's memory) + * + * @param list a pointer to the list + * @param entry a pointer to the gateway + * @return a pointer to the removed entry + */ +struct gw_container_entry * olsr_gw_list_remove(struct gw_list * list, struct gw_container_entry * entry) { + assert(list); + assert(entry); + assert(!olsr_gw_list_empty(list)); + + list_remove(&entry->list_node); + list->count--; + return entry; +} + +#endif /* __linux__ */ diff --git a/src/gateway_list.h b/src/gateway_list.h new file mode 100644 index 00000000..e947b16e --- /dev/null +++ b/src/gateway_list.h @@ -0,0 +1,152 @@ +/* + * The olsr.org Optimized Link-State Routing daemon(olsrd) + * Copyright (c) 2004, Thomas Lopatic (thomas@lopatic.de) + * IPv4 performance optimization (c) 2006, sven-ola(gmx.de) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of olsr.org, olsrd nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Visit http://www.olsr.org for more information. + * + * If you find this software useful feel free to make a donation + * to the project. For more information see the website or contact + * the copyright holders. + * + */ + +#ifndef _GW_LIST_H +#define _GW_LIST_H + +#ifdef __linux__ + +#include "gateway.h" +#include "common/list.h" +#include "kernel_tunnel.h" +#include +#include +#include + +/** Holds the list head and list administration */ +struct gw_list { + struct list_node head; /**< The (ordered) list of entries */ + uint8_t count_max; /**< The maximum number of entries in the list */ + uint8_t count; /**< The number of entries in the list */ +}; + +/** A container for a gateway and its tunnel */ +struct gw_container_entry { + struct gateway_entry * gw; /**< the gateway entry */ + struct olsr_iptunnel_entry * tunnel; /**< the gateway tunnel */ + uint64_t path_cost; /**< the gateway path costs */ + struct list_node list_node; /**< the list node */ +}; + +/** Cast from list_node to gw_container_entry */ +LISTNODE2STRUCT(olsr_gw_list_node2entry, struct gw_container_entry, list_node); + +/** Deletion safe macro for gateway list traversal (do not delete the previous or next node, current node is ok) */ +#define OLSR_FOR_ALL_GWS(head, gw) {\ + struct list_node * _list_node; \ + struct list_node * _next_list_node; \ + for (_list_node = (head)->next; _list_node != (head); _list_node = _next_list_node) { \ + _next_list_node = _list_node->next; \ + gw = olsr_gw_list_node2entry(_list_node); +#define OLSR_FOR_ALL_GWS_END(gw) }} + +/** + * @param list a pointer to the list + * @return true when multiple gateways mode is enabled + */ +static inline bool olsr_gw_list_isModeMulti(struct gw_list * list) { + assert(list); + return (list->count_max > 1); +} + +void olsr_gw_list_init(struct gw_list * list, uint8_t count); +void olsr_gw_list_cleanup(struct gw_list * list); + +/** + * @param list a pointer to the list + * @return true if the list is empty + */ +static inline bool olsr_gw_list_empty(struct gw_list * list) { + assert(list); + return (list->count == 0); +} + +/** + * @param list a pointer to the list + * @return true if the list is full + */ +static inline bool olsr_gw_list_full(struct gw_list * list) { + assert(list); + return (list->count >= list->count_max); +} + +/** + * Get the best entry that is on the list + * + * @param list a pointer to the list + * @return a pointer to the best entry, or NULL when the list is empty + */ +static inline struct gw_container_entry * olsr_gw_list_get_best_entry(struct gw_list * list) { + assert(list); + + if (olsr_gw_list_empty(list)) { + return NULL; + } + + /* get the best (first) entry of the list */ + return olsr_gw_list_node2entry(list->head.next); +} + +/** + * Get the worst entry that is on the list + * + * @param list a pointer to the list + * @return a pointer to the worst entry + */ +static inline struct gw_container_entry * olsr_gw_list_get_worst_entry(struct gw_list * list) { + assert(list); + + if (olsr_gw_list_empty(list)) { + return NULL; + } + + /* get the worst (last) entry of the list */ + return olsr_gw_list_node2entry(list->head.prev); +} + +struct gw_container_entry * olsr_gw_list_find(struct gw_list * list, struct gateway_entry * entry); +struct gw_container_entry * olsr_gw_list_add(struct gw_list * list, struct gw_container_entry * entry); +struct gw_container_entry * olsr_gw_list_update(struct gw_list * list, struct gw_container_entry * entry, + uint64_t gw_path_cost); +struct gw_container_entry * olsr_gw_list_remove(struct gw_list * list, struct gw_container_entry * entry); + +#endif /* __linux__ */ +#endif /* _GW_LIST_H */ diff --git a/src/kernel_routes.h b/src/kernel_routes.h index bb4caaec..10e7eca2 100644 --- a/src/kernel_routes.h +++ b/src/kernel_routes.h @@ -59,7 +59,7 @@ int rtnetlink_register_socket(int); void olsr_os_niit_4to6_route(const struct olsr_ip_prefix *dst_v4, bool set); void olsr_os_niit_6to4_route(const struct olsr_ip_prefix *dst_v6, bool set); -void olsr_os_inetgw_tunnel_route(uint32_t if_idx, bool ipv4, bool set); +void olsr_os_inetgw_tunnel_route(uint32_t if_idx, bool ipv4, bool set, uint8_t table); int olsr_os_policy_rule(int family, int rttable, uint32_t priority, const char *if_name, bool set); int olsr_os_localhost_if(union olsr_ip_addr *ip, bool create); diff --git a/src/kernel_tunnel.h b/src/kernel_tunnel.h index f644f9c3..1936db27 100644 --- a/src/kernel_tunnel.h +++ b/src/kernel_tunnel.h @@ -41,7 +41,7 @@ struct olsr_iptunnel_entry { int olsr_os_init_iptunnel(const char * name); void olsr_os_cleanup_iptunnel(const char * name); -struct olsr_iptunnel_entry *olsr_os_add_ipip_tunnel(union olsr_ip_addr *target, bool transportV4); +struct olsr_iptunnel_entry *olsr_os_add_ipip_tunnel(union olsr_ip_addr *target, bool transportV4, char *name); void olsr_os_del_ipip_tunnel(struct olsr_iptunnel_entry *); #endif /* KERNEL_TUNNEL_H_ */ diff --git a/src/linux/kernel_routes_nl.c b/src/linux/kernel_routes_nl.c index aac38a9b..4bf22102 100644 --- a/src/linux/kernel_routes_nl.c +++ b/src/linux/kernel_routes_nl.c @@ -466,14 +466,14 @@ void olsr_os_niit_4to6_route(const struct olsr_ip_prefix *dst_v4, bool set) { } } -void olsr_os_inetgw_tunnel_route(uint32_t if_idx, bool ipv4, bool set) { +void olsr_os_inetgw_tunnel_route(uint32_t if_idx, bool ipv4, bool set, uint8_t table) { const struct olsr_ip_prefix *dst; assert(olsr_cnf->ip_version == AF_INET6 || ipv4); dst = ipv4 ? &ipv4_internet_route : &ipv6_internet_route; - if (olsr_new_netlink_route(ipv4 ? AF_INET : AF_INET6, olsr_cnf->rt_table_tunnel, + if (olsr_new_netlink_route(ipv4 ? AF_INET : AF_INET6, table, if_idx, RT_METRIC_DEFAULT, olsr_cnf->rt_proto, NULL, NULL, dst, set, false)) { olsr_syslog(OLSR_LOG_ERR, ". error while %s inetgw tunnel route to %s for if %d", set ? "setting" : "removing", olsr_ip_prefix_to_string(dst), if_idx); diff --git a/src/linux/kernel_tunnel.c b/src/linux/kernel_tunnel.c index a0e32b85..983e00d2 100644 --- a/src/linux/kernel_tunnel.c +++ b/src/linux/kernel_tunnel.c @@ -108,7 +108,7 @@ void olsr_os_cleanup_iptunnel(const char * dev) { olsr_os_del_ipip_tunnel(t); } - if (!store_iptunnel_state) { + if (olsr_cnf->smart_gw_always_remove_server_tunnel || !store_iptunnel_state) { olsr_if_set_state(dev, false); } @@ -185,39 +185,23 @@ static int os_ip_tunnel(const char *name, void *target) { return target != NULL ? if_nametoindex(name) : 1; } -/** - * Dummy for generating an interface name for an olsr ipip tunnel - * @param target IP destination of the tunnel - * @param name pointer to output buffer (length IFNAMSIZ) - */ -static void generate_iptunnel_name(union olsr_ip_addr *target, char *name) { - static char PREFIX[] = "tnl_"; - static uint32_t counter = 0; - - snprintf(name, IFNAMSIZ, "%s%08x", PREFIX, - olsr_cnf->ip_version == AF_INET ? target->v4.s_addr : ++counter); -} - /** * demands an ipip tunnel to a certain target. If no tunnel exists it will be created * @param target ip address of the target * @param transportV4 true if IPv4 traffic is used, false for IPv6 traffic + * @param name pointer to name string buffer (length IFNAMSIZ) * @return NULL if an error happened, pointer to olsr_iptunnel_entry otherwise */ -struct olsr_iptunnel_entry *olsr_os_add_ipip_tunnel(union olsr_ip_addr *target, bool transportV4 __attribute__ ((unused))) { +struct olsr_iptunnel_entry *olsr_os_add_ipip_tunnel(union olsr_ip_addr *target, bool transportV4 __attribute__ ((unused)), char *name) { struct olsr_iptunnel_entry *t; assert(olsr_cnf->ip_version == AF_INET6 || transportV4); t = (struct olsr_iptunnel_entry *)avl_find(&tunnel_tree, target); if (t == NULL) { - char name[IFNAMSIZ]; int if_idx; struct ipaddr_str buf; - memset(name, 0, sizeof(name)); - generate_iptunnel_name(target, name); - if_idx = os_ip_tunnel(name, (olsr_cnf->ip_version == AF_INET) ? (void *) &target->v4.s_addr : (void *) &target->v6); if (if_idx == 0) { // cannot create tunnel diff --git a/src/lq_packet.c b/src/lq_packet.c index e20325cd..d0ca71cd 100644 --- a/src/lq_packet.c +++ b/src/lq_packet.c @@ -360,8 +360,8 @@ serialize_lq_hello(struct lq_hello_message *lq_hello, struct interface *outif) /* * Initially, we want to put the complete lq_hello into the message. * For this flush the output buffer (if there are some bytes in). - * This is a hack/fix, which prevents message fragementation resulting - * in instable links. The ugly lq/genmsg code should be reworked anyhow. + * This is a hack/fix, which prevents message fragmentation resulting + * in unstable links. The ugly lq/genmsg code should be reworked anyhow. */ if (0 < net_output_pending(outif)) { for (i = 0; i <= MAX_NEIGH; i++) { @@ -408,7 +408,7 @@ serialize_lq_hello(struct lq_hello_message *lq_hello, struct interface *outif) req = olsr_cnf->ipsize + olsr_sizeof_hello_lqdata(); // no, we also need space for an info header, as this is the - // first neighbor with the current neighor type and link type + // first neighbor with the current neighbor type and link type if (is_first) req += sizeof(struct lq_hello_info_header); @@ -552,8 +552,8 @@ serialize_lq_tc(struct lq_tc_message *lq_tc, struct interface *outif) /* * Initially, we want to put the complete lq_tc into the message. * For this flush the output buffer (if there are some bytes in). - * This is a hack/fix, which prevents message fragementation resulting - * in instable links. The ugly lq/genmsg code should be reworked anyhow. + * This is a hack/fix, which prevents message fragmentation resulting + * in unstable links. The ugly lq/genmsg code should be reworked anyhow. */ if (0 < net_output_pending(outif)) { for (neigh = lq_tc->neigh; neigh != NULL; neigh = neigh->next) { diff --git a/src/main.c b/src/main.c index aa7e214f..5bb26a23 100644 --- a/src/main.c +++ b/src/main.c @@ -46,6 +46,7 @@ #include "ipcalc.h" #include "defs.h" +#include "builddata.h" #include "olsr.h" #include "log.h" #include "scheduler.h" @@ -179,7 +180,7 @@ static int olsr_create_lock_file(bool noExitOnFail) { /* create file for lock */ lock_fd = open(lock_file_name, O_WRONLY | O_CREAT, S_IRWXU); - if (lock_fd == 0) { + if (lock_fd < 0) { if (noExitOnFail) { return -1; } @@ -234,11 +235,11 @@ static void writePidFile(void) { /* write the PID */ { pid_t pid = getpid(); - int chars = snprintf(buf, sizeof(buf), "%d", pid); + int chars = snprintf(buf, sizeof(buf), "%d", (int)pid); ssize_t chars_written = write(fd, buf, chars); if (chars_written != chars) { close(fd); - snprintf(buf, sizeof(buf), "Could not write the PID %d to the PID file %s", pid, olsr_cnf->pidfile); + snprintf(buf, sizeof(buf), "Could not write the PID %d to the PID file %s", (int)pid, olsr_cnf->pidfile); perror(buf); if (remove(olsr_cnf->pidfile) < 0) { snprintf(buf, sizeof(buf), "Could not remove the PID file %s", olsr_cnf->pidfile); @@ -591,6 +592,15 @@ int main(int argc, char *argv[]) { } } +#ifdef __linux__ + /* startup gateway system */ + if (olsr_cnf->smart_gw_active) { + if (olsr_startup_gateways()) { + olsr_exit("Cannot startup gateway tunnels", 1); + } + } +#endif /* __linux__ */ + olsr_do_startup_sleep(); /* Print heartbeat to stdout */ @@ -723,6 +733,9 @@ int main(int argc, char *argv[]) { *@param signo the signal that triggered this callback */ void olsr_reconfigure(int signo __attribute__ ((unused))) { +#ifndef _WIN32 + int errNr = errno; +#endif /* if we are started with -nofork, we do not want to go into the * background here. So we can simply stop on -HUP */ @@ -748,6 +761,9 @@ void olsr_reconfigure(int signo __attribute__ ((unused))) { olsr_syslog(OLSR_LOG_INFO, "RECONFIGURING!\n"); } } +#ifndef _WIN32 + errno = errNr; +#endif olsr_shutdown(0); } #endif /* _WIN32 */ @@ -785,6 +801,9 @@ SignalHandler(unsigned long signo) static void olsr_shutdown(int signo __attribute__ ((unused))) #endif /* _WIN32 */ { +#ifndef _WIN32 + int errNr = errno; +#endif struct interface *ifn; int exit_value; @@ -828,6 +847,7 @@ static void olsr_shutdown(int signo __attribute__ ((unused))) #ifdef __linux__ /* trigger gateway selection */ if (olsr_cnf->smart_gw_active) { + olsr_shutdown_gateways(); olsr_cleanup_gateways(); } @@ -906,6 +926,9 @@ static void olsr_shutdown(int signo __attribute__ ((unused))) exit_value = olsr_cnf->exit_value; olsrd_free_cnf(olsr_cnf); +#ifndef _WIN32 + errno = errNr; +#endif exit(exit_value); } diff --git a/src/neighbor_table.c b/src/neighbor_table.c index f7c4aae7..23a349f2 100644 --- a/src/neighbor_table.c +++ b/src/neighbor_table.c @@ -385,15 +385,10 @@ olsr_print_neighbor_table(void) /* The whole function doesn't do anything else. */ const int iplen = olsr_cnf->ip_version == AF_INET ? (INET_ADDRSTRLEN - 1) : (INET6_ADDRSTRLEN - 1); int idx; - struct tm * nowtm; - struct timeval now; - - (void)gettimeofday(&now, NULL); - nowtm = localtime((time_t *)&now.tv_sec); OLSR_PRINTF(1, - "\n--- %02d:%02d:%02d.%02d ------------------------------------------------ NEIGHBORS\n\n" - "%*s LQ NLQ SYM MPR MPRS will\n", nowtm->tm_hour, nowtm->tm_min, nowtm->tm_sec, (int)now.tv_usec / 10000, + "\n--- %s ------------------------------------------------ NEIGHBORS\n\n" + "%*s LQ NLQ SYM MPR MPRS will\n", olsr_wallclock_string(), iplen, "IP address"); for (idx = 0; idx < HASHSIZE; idx++) { diff --git a/src/olsr.c b/src/olsr.c index 10b4ee35..83612b0c 100644 --- a/src/olsr.c +++ b/src/olsr.c @@ -44,6 +44,7 @@ */ #include "defs.h" +#include "builddata.h" #include "olsr.h" #include "link_set.h" #include "two_hop_neighbor_table.h" diff --git a/src/olsr_cfg.h b/src/olsr_cfg.h index ec831183..bf409fd4 100644 --- a/src/olsr_cfg.h +++ b/src/olsr_cfg.h @@ -81,6 +81,11 @@ #define DEF_MIN_TC_VTIME 0.0 #define DEF_USE_NIIT true #define DEF_SMART_GW false +#define DEF_SMART_GW_ALWAYS_REMOVE_SERVER_TUNNEL false +#define DEF_GW_USE_COUNT 1 +#define DEF_GW_TAKEDOWN_PERCENTAGE 25 +#define DEF_GW_MARK_OFFSET_EGRESS 91 +#define DEF_GW_MARK_OFFSET_TUNNELS 101 #define DEF_GW_PERIOD 10*1000 #define DEF_GW_STABLE_COUNT 6 #define DEF_GW_ALLOW_NAT true @@ -122,6 +127,11 @@ #define MAX_LQ_AGING 1.0 #define MIN_LQ_AGING 0.01 +#define MIN_SMARTGW_USE_COUNT_MIN 1 +#define MAX_SMARTGW_USE_COUNT_MAX 64 + +#define MAX_SMARTGW_EGRESS_INTERFACE_COUNT_MAX 32 + #define MIN_SMARTGW_PERIOD 1*1000 #define MAX_SMARTGW_PERIOD 320000*1000 @@ -231,6 +241,12 @@ struct plugin_entry { struct plugin_entry *next; }; +struct sgw_egress_if { + char *name; + uint8_t mark; + struct sgw_egress_if *next; +}; + /* * The config struct */ @@ -276,7 +292,14 @@ struct olsrd_config { char *lock_file; bool use_niit; - bool smart_gw_active, smart_gw_allow_nat, smart_gw_uplink_nat; + bool smart_gw_active, smart_gw_always_remove_server_tunnel, smart_gw_allow_nat, smart_gw_uplink_nat; + uint8_t smart_gw_use_count; + uint8_t smart_gw_takedown_percentage; + char *smart_gw_policyrouting_script; + struct sgw_egress_if * smart_gw_egress_interfaces; + uint8_t smart_gw_egress_interfaces_count; + uint8_t smart_gw_mark_offset_egress; + uint8_t smart_gw_mark_offset_tunnels; uint32_t smart_gw_period; uint8_t smart_gw_stablecount; uint8_t smart_gw_thresh; diff --git a/src/olsr_switch/main.c b/src/olsr_switch/main.c index 893a9cd2..53fa4c2d 100644 --- a/src/olsr_switch/main.c +++ b/src/olsr_switch/main.c @@ -105,10 +105,14 @@ void ohs_close(int signo __attribute__ ((unused))) #endif /* _WIN32 */ { +#ifndef _WIN32 + int errNr = errno; +#endif printf("OHS: exit\n"); - close(srv_socket); - +#ifndef _WIN32 + errno = errNr; +#endif exit(0); } diff --git a/src/process_package.c b/src/process_package.c index 780efaba..b39b692b 100644 --- a/src/process_package.c +++ b/src/process_package.c @@ -441,21 +441,15 @@ olsr_hello_tap(struct hello_message *message, struct interface *in_if, const uni /* find the input interface in the list of neighbor interfaces */ for (walker = message->neighbors; walker != NULL; walker = walker->next) { - if (walker->link != UNSPEC_LINK - && ipequal(&walker->address, &in_if->ip_addr)) { + if (ipequal(&walker->address, &in_if->ip_addr)) { + /* + * memorize our neighbour's idea of the link quality, so that we + * know the link quality in both directions + */ + olsr_memorize_foreign_hello_lq(lnk, walker->link != UNSPEC_LINK ? walker : NULL); break; } } - - /* - * memorize our neighbour's idea of the link quality, so that we - * know the link quality in both directions - * - * walker is NULL if there the current interface was not included in - * the message (or was included as an UNSPEC_LINK) - */ - olsr_memorize_foreign_hello_lq(lnk, walker); - /* update packet loss for link quality calculation */ olsr_received_hello_handler(lnk); } @@ -493,7 +487,7 @@ olsr_hello_tap(struct hello_message *message, struct interface *in_if, const uni if (neighbor->willingness != WILL_NEVER) process_message_neighbors(neighbor, message); - /* Process changes immedeatly in case of MPR updates */ + /* Process changes immediately in case of MPR updates */ olsr_process_changes(); olsr_free_hello_packet(message); diff --git a/src/scheduler.c b/src/scheduler.c index 6b897326..b5812312 100644 --- a/src/scheduler.c +++ b/src/scheduler.c @@ -463,7 +463,7 @@ handle_fds(uint32_t next_interval) void __attribute__ ((noreturn)) olsr_scheduler(void) { - OLSR_PRINTF(1, "Scheduler started - polling every %f ms\n", (double)olsr_cnf->pollrate); + OLSR_PRINTF(1, "Scheduler started - polling every %d ms\n", (int)(olsr_cnf->pollrate*1000)); /* Main scheduler loop */ while (true) { diff --git a/src/win32/arpa/inet.h b/src/win32/arpa/inet.h index 126536df..92f8f1d7 100644 --- a/src/win32/arpa/inet.h +++ b/src/win32/arpa/inet.h @@ -50,9 +50,11 @@ #include #undef interface +#ifndef InetNtopA int inet_aton(const char *cp, struct in_addr *addr); int inet_pton(int af, const char *src, void *dst); char *inet_ntop(int af, const void *src, char *dst, int size); +#endif #endif /* !defined TL_ARPA_INET_H_INCLUDED */ diff --git a/src/win32/compat.c b/src/win32/compat.c index c837698a..f9a68750 100644 --- a/src/win32/compat.c +++ b/src/win32/compat.c @@ -214,6 +214,7 @@ dlerror(void) #define NS_IN6ADDRSZ 16 #define NS_INT16SZ 2 +#ifndef InetNtopA static int inet_pton4(const char *src, unsigned char *dst) { @@ -486,6 +487,7 @@ inet_ntop(int af, const void *src, char *dst, int size) return (NULL); } } +#endif int isatty(int fd) diff --git a/src/win32/dummy.c b/src/win32/dummy.c index 63d7890d..29e2675c 100644 --- a/src/win32/dummy.c +++ b/src/win32/dummy.c @@ -20,7 +20,7 @@ void olsr_os_cleanup_iptunnel(const char * name __attribute__((unused))) { } struct olsr_iptunnel_entry *olsr_os_add_ipip_tunnel(union olsr_ip_addr *target __attribute__ ((unused)), - bool transportV4 __attribute__ ((unused))) { + bool transportV4 __attribute__ ((unused)), char *name __attribute__ ((unused))) { return NULL; } @@ -45,7 +45,8 @@ void olsr_os_niit_6to4_route(const struct olsr_ip_prefix *dst_v6 __attribute__ ( } void olsr_os_inetgw_tunnel_route(uint32_t if_idx __attribute__ ((unused)), bool ipv4 __attribute__ ((unused)), - bool set __attribute__ ((unused))) { + bool set __attribute__ ((unused)), + uint8_t table __attribute__ ((unused))) { } int olsr_os_policy_rule(int family __attribute__ ((unused)), From 41b9ad3c1f019cf8bd8b8c336cf23e3a1dd0a41e Mon Sep 17 00:00:00 2001 From: ben Date: Thu, 24 Apr 2014 15:15:35 -0500 Subject: [PATCH 2/3] Upating Makefiles for commotion-specific customizations --- Makefile | 27 ++++++++++++++++++++++++++- openwrt/commotion/Makefile | 4 ++-- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 162bf9f9..6209a56e 100644 --- a/Makefile +++ b/Makefile @@ -205,7 +205,7 @@ ifeq ($(OS),win32) SUBDIRS := dot_draw httpinfo jsoninfo mini pgraph secure txtinfo else ifeq ($(OS),android) -SUBDIRS := arprefresh bmf dot_draw dyn_gw dyn_gw_plain httpinfo jsoninfo mdns mini nameservice p2pd pgraph pud secure sgwdynspeed txtinfo watchdog +SUBDIRS := arprefresh bmf dot_draw dyn_gw_plain httpinfo jsoninfo mdp mini nameservice pgraph pud secure sgwdynspeed txtinfo watchdog else SUBDIRS := dot_draw httpinfo jsoninfo mini nameservice pgraph secure txtinfo watchdog endif @@ -269,6 +269,18 @@ bmf_install: bmf_uninstall: $(MAKECMDPREFIX)$(MAKECMD) -C lib/bmf DESTDIR=$(DESTDIR) uninstall +dnssd: + $(MAKECMDPREFIX)$(MAKECMD) -C lib/dnssd + +dnssd_clean: + $(MAKECMDPREFIX)$(MAKECMD) -C lib/dnssd DESTDIR=$(DESTDIR) clean + +dnssd_install: + $(MAKECMDPREFIX)$(MAKECMD) -C lib/dnssd DESTDIR=$(DESTDIR) install + +dnssd_uninstall: + $(MAKECMDPREFIX)$(MAKECMD) -C lib/dnssd DESTDIR=$(DESTDIR) uninstall + dot_draw: $(MAKECMDPREFIX)$(MAKECMD) -C lib/dot_draw @@ -348,6 +360,19 @@ mdns_uninstall: # nameserver uses regex, which was only recently added to Android. On # Android, $(REGEX_OBJS) will have all of the files needed, on all # other platforms, it'll be empty and therefore ignored. + +mdp: + $(MAKECMDPREFIX)$(MAKECMD) -C lib/mdp + +mdp_clean: + $(MAKECMDPREFIX)$(MAKECMD) -C lib/mdp DESTDIR=$(DESTDIR) clean + +mdp_install: + $(MAKECMDPREFIX)$(MAKECMD) -C lib/mdp DESTDIR=$(DESTDIR) install + +mdp_uninstall: + $(MAKECMDPREFIX)$(MAKECMD) -C lib/mdp DESTDIR=$(DESTDIR) uninstall + nameservice: $(MAKECMDPREFIX)$(MAKECMD) -C lib/nameservice clean $(MAKECMDPREFIX)$(MAKECMD) -C lib/nameservice diff --git a/openwrt/commotion/Makefile b/openwrt/commotion/Makefile index 314a8a2b..7420a722 100644 --- a/openwrt/commotion/Makefile +++ b/openwrt/commotion/Makefile @@ -10,8 +10,8 @@ include $(TOPDIR)/rules.mk SERVAL_VERSION:=batphone-release-0.90 PKG_NAME:=olsrd -PKG_VERSION:=0.6.5 -PKG_RELEASE:=2 +PKG_VERSION:=0.6.6 +PKG_RELEASE:=1 PKG_SOURCE_URL:=https://github.com/opentechinstitute/olsrd/archive PKG_SOURCE_VERSION:=release-$(PKG_VERSION) From eba27d82a391d928fdb48a18c63090abe391c6cb Mon Sep 17 00:00:00 2001 From: ben Date: Thu, 24 Apr 2014 15:21:47 -0500 Subject: [PATCH 3/3] added mdns to Android target in Makefile --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 6209a56e..57b2917c 100644 --- a/Makefile +++ b/Makefile @@ -205,7 +205,7 @@ ifeq ($(OS),win32) SUBDIRS := dot_draw httpinfo jsoninfo mini pgraph secure txtinfo else ifeq ($(OS),android) -SUBDIRS := arprefresh bmf dot_draw dyn_gw_plain httpinfo jsoninfo mdp mini nameservice pgraph pud secure sgwdynspeed txtinfo watchdog +SUBDIRS := arprefresh bmf dot_draw dyn_gw_plain httpinfo jsoninfo mdns mdp mini nameservice pgraph pud secure sgwdynspeed txtinfo watchdog else SUBDIRS := dot_draw httpinfo jsoninfo mini nameservice pgraph secure txtinfo watchdog endif